pax_global_header00006660000000000000000000000064152067546050014524gustar00rootroot0000000000000052 comment=dcff6d7673ba61e6f95acb314a282be0e672888b simutrans-124.5/000077500000000000000000000000001520675460500135645ustar00rootroot00000000000000simutrans-124.5/.editorconfig000066400000000000000000000007471520675460500162510ustar00rootroot00000000000000# EditorConfig (see https://EditorConfig.org) root = true [*] charset = utf-8 indent_style = tab insert_final_newline = true trim_trailing_whitespace = true [*.{txt,tab}] trim_trailing_whitespace = false [**/CMakeLists.txt] trim_trailing_whitespace = true [*.rc] indent_style = space indent_size = 1 [*.{nsi,nsh}] indent_style = space indent_size = 2 [Makefile,*.mk] indent_style = space indent_size = 2 [{*.vcxitems,*.vcxproj}] insert_final_newline = false indent_style = space simutrans-124.5/.github/000077500000000000000000000000001520675460500151245ustar00rootroot00000000000000simutrans-124.5/.github/build32.sh000066400000000000000000000011101520675460500167150ustar00rootroot00000000000000#!/bin/sh # builds 32bit GDI for windows # libbrotli static is broken in MinGW for freetype2 for f in libbrotlidec libbrotlienc libbrotlicommon; do mv "/mingw32/lib/$f.dll.a" "/mingw32/lib/$f.dll._" cp "/mingw32/lib/$f-static.a" "/mingw32/lib/$f.a" done # normal build autoconf make clean sh ./configure echo "FLAGS = -DREVISION=$(svn info --show-item revision svn://servers.simutrans.org/simutrans) " >>config.default echo "STATIC = 1" >>config.default %echo "VERBOSE = 1" >>config.default cat config.default >2 make sh tools/distribute.sh mv simu*.zip simuwin-gdi32-nightly.zip simutrans-124.5/.github/build64-SDL2.sh000077500000000000000000000016111520675460500174350ustar00rootroot00000000000000#!/bin/sh # build SDL2 simutrans nightly # libbrotli static is broken in MinGW for freetype2 for f in libbrotlidec libbrotlienc libbrotlicommon; do mv "/mingw64/lib/$f.dll.a" "/mingw64/lib/$f.dll._" cp "/mingw64/lib/$f-static.a" "/mingw64/lib/$f.a" done # normal build echo "BACKEND = sdl2" >config.default echo "OSTYPE = mingw" >>config.default echo "DEBUG = 0" >>config.default echo "MSG_LEVEL = 3" >>config.default echo "OPTIMISE = 1" >>config.default echo "MULTI_THREAD = 1" >>config.default echo "USE_ZSTD = 1" >>config.default echo "WITH_REVISION = 0" >>config.default echo "FLAGS = -DREVISION=$(svn info --show-item revision svn://servers.simutrans.org/simutrans) " >>config.default echo "STATIC = 1" >>config.default echo "VERBOSE = 1" >>config.default echo "LDFLAGS += $(pkg-config --libs --static SDL2) " >>config.default make sh tools/distribute.sh mv simu*.zip simuwin64-SDL2-nightly.zipsimutrans-124.5/.github/prepare-nsis.ps1000066400000000000000000000030301520675460500201550ustar00rootroot00000000000000# downloads and unpacks the unicode plugin needed for NSIS installer of paksetdownloader Write-Output "Downloading and installing NSIS" Invoke-WebRequest -UserAgent "Wget" -Uri "https://sourceforge.net/projects/nsis/files/NSIS%203/3.09/nsis-3.09.zip/download" -OutFile "nsis.zip" expand-archive -path "nsis.zip" -DestinationPath "." -Force Invoke-WebRequest -Uri "https://nsis.sourceforge.io/mediawiki/images/5/5a/NSISunzU.zip" -OutFile "NSISunzU.zip" Invoke-WebRequest -Uri "https://nsis.sourceforge.io/mediawiki/images/c/c9/Inetc.zip" -OutFile "Inetc.zip" Invoke-WebRequest -Uri "https://nsis.sourceforge.io/mediawiki/images/c/c7/CabX.zip" -OutFile "CabX.zip" Invoke-WebRequest -Uri "https://nsis.sourceforge.io/mediawiki/images/9/9d/Untgz.zip" -OutFile "Untgz.zip" Invoke-WebRequest -Uri "https://nsis.sourceforge.io/mediawiki/images/6/6c/Shelllink.zip" -OutFile "Shelllink.zip" expand-archive -path "CabX.zip" -DestinationPath "nsis-3.09" -Force expand-archive -path "Inetc.zip" -DestinationPath "nsis-3.09" -Force expand-archive -path "NSISunzU.zip" -DestinationPath "dummy" -Force move ".\dummy\NSISunzU\Plugin unicode\nsisunz.dll" .\nsis-3.09\plugins\x86-unicode\ expand-archive -path "Untgz.zip" -DestinationPath "dummy" -Force move .\dummy\Untgz\unicode\untgz.dll .\nsis-3.09\plugins\x86-unicode\ expand-archive -path "Shelllink.zip" -DestinationPath "dummy" -Force move .\dummy\Unicode\Plugins\ShellLink.dll .\nsis-3.09\plugins\x86-unicode\ Remove-Item 'dummy' -Recurse del *.zip ./nsis-3.09/makensis.exe ./src/Windows/nsis/onlineupgrade.nsi simutrans-124.5/.github/workflows/000077500000000000000000000000001520675460500171615ustar00rootroot00000000000000simutrans-124.5/.github/workflows/makeobj.yml000066400000000000000000000061111520675460500213130ustar00rootroot00000000000000name: Nightly build makeobj on: [push] jobs: makeobj_windows-nightly: name: makeobj Windows runs-on: windows-latest steps: - name: Setup Mingw uses: msys2/setup-msys2@v2 with: msystem: MINGW32 update: false install: base-devel mingw-w64-i686-toolchain mingw-w64-i686-gcc mingw-w64-i686-libpng - name: Checkout code uses: actions/checkout@v6 - name: build shell: msys2 {0} working-directory: ${{env.GITHUB_WORKSPACE}} run: | printf 'BACKEND := posix\nOSTYPE := mingw\nOPTIMISE := 1\nSTATIC := 1\n' | cat >config.default cd src/makeobj make cd ../../build/default/makeobj strip makeobj.exe - name: zip result shell: pwsh run: Compress-Archive build\default\makeobj\makeobj.exe build\default\makeobj\makeobj-windows-nightly.zip - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: build\default\makeobj\makeobj-windows-nightly.zip asset_name: makeobj-win-nightly.zip tag: Nightly overwrite: true makeobj_linux-x64-nightly: name: makeobj Linux x64 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: install_dependencies run: | sudo apt-get -y update sudo apt-get -ym install autoconf sudo apt-get -ym install libpng-dev sudo apt-get -ym install libbz2-dev - name: setup run: | autoconf ./configure echo "STATIC = 1" >>config.default cat config.default >>/dev/stderr - name: make makeobj run: | cd src/makeobj make - name: zip result run: | cd src/makeobj zip -r -9 makeobj_linux-x64-nightly.zip makeobj - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: src/makeobj/makeobj_linux-x64-nightly.zip asset_name: makeobj_linux-x64-nightly.zip tag: Nightly overwrite: true makeobj-mac: name: makeobj macOS arm runs-on: macos-latest steps: - uses: actions/checkout@v6 - name: install_dependencies run: | brew reinstall cmake pkg-config libpng - name: build run: | printf "#define REVISION %i\n" $(tools/get_revision.sh) > src/simutrans/revision.h cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build/src/makeobj - name: distribute run: | cd build/src/makeobj zip -r makeobj-arm-nightly.zip makeobj - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: build/src/makeobj/makeobj-arm-nightly.zip asset_name: makeobj-arm-nightly.zip tag: Nightly overwrite: true simutrans-124.5/.github/workflows/nightly-android.yml000066400000000000000000000164051520675460500230060ustar00rootroot00000000000000name: Nightly build Android on: push: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 path: 'gh-clone/simutrans' - name: Install dependencies run: | sudo dpkg --add-architecture i386 sudo apt-get -yqq update sudo apt-get -ym install curl expect git libc6:i386 libgcc1:i386 libncurses6:i386 libstdc++6:i386 zlib1g:i386 openjdk-21-jdk wget unzip vim make subversion zip sudo apt remove cmake - name: Install dependencies1 working-directory: /usr/local/lib/android/sdk run: | echo "$(find .. -maxdepth 3 -type d)" mv cmdline-tools/latest cmdline-tools/existing yes | cmdline-tools/existing/bin/sdkmanager --licenses sudo yes | cmdline-tools/existing/bin/sdkmanager --install "cmdline-tools;latest" echo "/usr/local/lib/android/sdk/cmdline-tools/latest/bin" >> $GITHUB_PATH sudo yes | cmdline-tools/latest/bin/sdkmanager --install "platform-tools" echo "/usr/local/lib/android/sdk/platform-tools" >> $GITHUB_PATH sudo yes | cmdline-tools/latest/bin/sdkmanager --install "build-tools;35.0.0" echo "/usr/local/lib/android/sdk/build-tools/35.0.0" >> $GITHUB_PATH sudo yes | cmdline-tools/latest/bin/sdkmanager --install "cmake;3.31.6" echo "/usr/local/lib/android/sdk/cmake/3.31.6/bin" >> $GITHUB_PATH sudo yes | cmdline-tools/latest/bin/sdkmanager --install "ndk;29.0.14206865" echo "/usr/local/lib/android/sdk/ndk/29.0.14206865" >> $GITHUB_PATH echo "ANDROID_NDK=/usr/local/lib/android/sdk/ndk/29.0.14206865" >> $GITHUB_ENV echo "($pwd)/ndk/29.0.14206865/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_PATH ln -s llvm-objdump ${ANDROID_HOME}/ndk/29.0.14206865/toolchains/llvm/prebuilt/linux-x86_64/bin/objdump echo "Github path ($PATH)" - name: Setup Android SDK environment variables run: | echo "ANDROID_HOME=/usr/local/lib/android/sdk" >> $GITHUB_ENV echo "ANDROID_SDK_HOME=/usr/local/lib/android/sdk" >> $GITHUB_ENV echo "ANDROID_SDK_ROOT=/usr/local/lib/android/sdk" >> $GITHUB_ENV echo "ANDROID_SDK=/usr/local/lib/android/sdk" >> $GITHUB_ENV echo "JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64" >> $GITHUB_ENV - name: Install keystore for Android APK signing run: | mkdir /home/runner/.android/ keytool -genkey -v -keystore /home/runner/.android/debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 -keypass android -storepass android -dname "cn=example.com,ou=exampleou,dc=example,dc=com" - name: Create working directory for next steps run: | sudo mkdir /android-sdl sudo chown -R runner /android-sdl sudo chgrp -R docker /android-sdl - name: Set up android project working-directory: /android-sdl run: | git clone --depth 1 https://github.com/simutrans/simutrans-android-project cd simutrans-android-project git submodule init git submodule update cd simutrans/jni/SDL git revert 53dbe18 --no-commit cd ../../.. cp -r $GITHUB_WORKSPACE/gh-clone/simutrans simutrans/jni/simutrans cp -r simutrans/jni/SDL/android-project/app/src/main/java simutrans/src/main cd simutrans/jni ./simutrans/src/android/AndroidPreBuild.sh cp -rf simutrans/simutrans/. ../src/main/assets - name: Build libraries working-directory: /android-sdl/simutrans-android-project/simutrans/jni run: | ./build_libraries.sh # Fluidsynth is a PITA to build; using the prebuilt release instead wget https://github.com/FluidSynth/fluidsynth/releases/download/v2.5.1/fluidsynth-2.5.1-android24.zip unzip fluidsynth-*.zip -d fluidsynth - name: Build Simutrans env: SIGNING_KEYSTORE: /android-sdl/simutrans-android-project/simutrans/jni/simutrans/src/android/Simutrans-upload.keystore SIGNING_KEY_ALIAS: ${{ secrets.ANDROID_UPLOAD_KEYSTORE_ALIAS }} SIGNING_KEY_PASSWORD: ${{ secrets.ANDROID_UPLOAD_KEYSTORE_PASS }} SIGNING_STORE_PASSWORD: ${{ secrets.ANDROID_UPLOAD_KEYSTORE_PASS }} working-directory: gh-clone/simutrans run: | echo "action_state=yellow" >> $GITHUB_ENV sed -i "s/versionCode [0-9]\+/versionCode $(./tools/get_revision.sh)/" /android-sdl/simutrans-android-project/simutrans/build.gradle VERSION=`sed -n 's/#define SIM_VERSION_MAJOR *\([0-9]*\)$/\1/ p' whatsNewDirectory/whatsnew-en-US - name: Update binaries of Nightly Release aab uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: /android-sdl/simutrans-android-project/simutrans/build/outputs/bundle/release/simutrans-release.aab asset_name: simuandroid-nightly.aab tag: Nightly overwrite: true - name: Update binaries of Nightly Release apk uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: /android-sdl/simutrans-android-project/simutrans/build/outputs/bundle/release/simutrans-nightly.apk asset_name: simuandroid-nightly.apk tag: Nightly overwrite: true - name: Release Beta on Google Play Store uses: r0adkll/upload-google-play@v1 if: github.repository == 'simutrans/simutrans' with: serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }} packageName: com.simutrans releaseFiles: /android-sdl/simutrans-android-project/simutrans/build/outputs/bundle/release/simutrans-release.aab track: beta status: completed whatsNewDirectory: /android-sdl/simutrans-android-project/simutrans/build/outputs/bundle/release/whatsNewDirectory simutrans-124.5/.github/workflows/nightly-macos-arm.yml000066400000000000000000000014331520675460500232400ustar00rootroot00000000000000name: Nightly build MacOS ARM on: [push] jobs: build: runs-on: macos-latest steps: - uses: actions/checkout@v6 - name: install_dependencies run: | brew install pkg-config libpng freetype zstd sdl2 fluidsynth - name: build run: | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DSIMUTRANS_UPDATE_LANGFILES=ON .. cmake --build build --target install - name: distribute run: | cd build zip -r simumac-nightly.zip simutrans - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: build/simumac-nightly.zip asset_name: simumac-arm-nightly.zip tag: Nightly overwrite: true simutrans-124.5/.github/workflows/nightly-macos-intel.yml000066400000000000000000000014301520675460500235710ustar00rootroot00000000000000name: Nightly build MacOS Intel on: [push] jobs: build: runs-on: macos-14 steps: - uses: actions/checkout@v6 - name: install_dependencies run: | brew install pkg-config libpng freetype zstd sdl2 fluidsynth - name: build run: | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DSIMUTRANS_UPDATE_LANGFILES=ON cmake --build build --target install - name: distribute run: | cd build zip -r simumac-nightly.zip simutrans - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: build/simumac-nightly.zip asset_name: simumac-intel-nightly.zip tag: Nightly overwrite: true simutrans-124.5/.github/workflows/nightly-ubuntu.yml000066400000000000000000000035661520675460500227140ustar00rootroot00000000000000name: Nightly build Ubuntu on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: install_dependencies run: | sudo apt-get -y update sudo apt-get -ym install libsdl2-dev sudo apt-get -ym install libfreetype6-dev sudo apt-get -ym install libfontconfig-dev sudo apt-get -ym install libminiupnpc-dev sudo apt-get -ym install libzstd-dev # We build fluidsynth with the minimum set of options we need to keep dependencies low sudo apt-get -ym install libsndfile1-dev wget https://github.com/FluidSynth/fluidsynth/archive/v2.5.3.tar.gz tar xf v*.tar.gz cd fluidsynth* cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib -Denable-aufile=0 -Denable-dbus=0 -Denable-ipv6=0 -Denable-jack=0 -Denable-ladspa=0 -Denable-midishare=0 -Denable-opensles=0 -Denable-oboe=0 -Denable-oss=0 -Denable-readline=0 -Denable-winmidi=0 -Denable-waveout=0 -Denable-network=0 -Denable-pulseaudio=1 -Denable-dsound=0 -Denable-alsa=0 -Denable-libinstpatch=0 -Denable-portaudio=0 -Denable-wasapi=0 -Denable-openmp=1 -Denable-libsndfile=1 -Denable-sdl3=0 cmake --build build -j $(nproc) sudo cmake --install build - name: build run: | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DOPTION_BUNDLE_LIBRARIES=ON -DSIMUTRANS_UPDATE_LANGFILES=ON cmake --build build -j$(nproc) --target install - name: distribute run: | cd build zip -r simulinux-x64-nightly.zip simutrans - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: build/simulinux-x64-nightly.zip asset_name: simulinux-x64-nightly.zip tag: Nightly overwrite: true simutrans-124.5/.github/workflows/nightly-windows-ms.yml000066400000000000000000000026471520675460500235000ustar00rootroot00000000000000# This workflow sets up and runs MSBuild and VSTest # to build and test a Visual Studio solution. name: Nightly Build Windows32 GDI on: [push] jobs: winGDI-32: runs-on: windows-latest steps: - name: Setup MSBuild and add to PATH uses: microsoft/setup-msbuild@v2 - name: Checkout code uses: actions/checkout@v6 id: checkout_code - name: Make NSIS shell: pwsh run: ./.github/prepare-nsis.ps1 - name: Run MSBuild shell: pwsh working-directory: ${{env.GITHUB_WORKSPACE}} run: | git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.bat ./vcpkg integrate install cd .. msbuild /m .\Simutrans-GDI.vcxproj -p:Configuration=Release /p:platform=Win32 - name: Build zipfile shell: pwsh run: | move-item "build/GDI/Simutrans GDI Nightly.exe" "simutrans/simutrans.exe" move-item "src\Windows\nsis\download-paksets.exe" "simutrans/download-pakset.exe" tools/get_lang_files.ps1 Compress-Archive -Path simutrans -DestinationPath "simuwin-gdi32-nightly.zip" dir - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ./simuwin-gdi32-nightly.zip asset_name: simuwin-gdi32-nightly.zip tag: Nightly overwrite: true simutrans-124.5/.github/workflows/nightly-windows.broken000066400000000000000000000023151520675460500235320ustar00rootroot00000000000000name: Nightly Build Windows on: [push] jobs: wingdi32: runs-on: windows-latest steps: - name: Setup Mingw uses: msys2/setup-msys2@v2 with: msystem: MINGW32 update: true install: git zip unzip svn base-devel mingw-w64-i686-toolchain autoconf mingw-w64-i686-gcc mingw-w64-i686-freetype mingw-w64-i686-zstd mingw-w64-i686-libpng mingw-w64-i686-brotli mingw-w64-i686-pkg-config - name: Checkout code uses: actions/checkout@v3 - name: Prepares NSIS plugins run: ./.github/prepare-nsis.ps1 shell: powershell - name: Create nsis installer uses: joncloud/makensis-action@v4 with: arguments: "/V3" additional-plugin-paths: NSIS_Plugins/Plugins script-file: src/Windows/nsis/onlineupgrade.nsi - name: CI-Build run: sh ./.github/build32.sh shell: msys2 {0} - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ./simuwin-gdi32-nightly.zip asset_name: simuwin-gdi32-nightly.zip tag: Nightly overwrite: true simutrans-124.5/.github/workflows/nightly-windows64-SDL2-ms.yml000066400000000000000000000024651520675460500243520ustar00rootroot00000000000000# This workflow sets up and runs MSBuild and VSTest # to build and test a Visual Studio solution. name: Nightly Built Windows64 SDL2 on: [push] jobs: winSDL2-64: runs-on: windows-latest steps: - name: Setup MSBuild and add to PATH uses: microsoft/setup-msbuild@v2 - name: Checkout code uses: actions/checkout@v6 id: checkout_code - name: Make NSIS shell: pwsh run: ./.github/prepare-nsis.ps1 - name: Run MSBuild shell: pwsh working-directory: ${{env.GITHUB_WORKSPACE}} run: | vcpkg integrate install msbuild /m .\Simutrans-SDL2.vcxproj -p:Configuration=Release /p:platform=x64 - name: Build zipfile shell: pwsh run: | move-item "build/SDL2/Simutrans SDL2 Nightly.exe" "simutrans/simutrans.exe" move-item "src\Windows\nsis\download-paksets.exe" "simutrans/download-pakset.exe" tools/get_lang_files.ps1 Compress-Archive -Path simutrans -DestinationPath "simuwin64-SDL2-nightly.zip" dir - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ./simuwin64-SDL2-nightly.zip asset_name: simuwin64-SDL2-nightly.zip tag: Nightly overwrite: true simutrans-124.5/.github/workflows/nightly-windows64-SDL2.broken000066400000000000000000000023721520675460500244110ustar00rootroot00000000000000name: Nightly Built Windows on: [push] jobs: winSDL2-64: runs-on: windows-latest steps: - name: Setup Mingw uses: msys2/setup-msys2@v2 with: msystem: MINGW64 update: true install: git zip unzip svn base-devel mingw-w64-x86_64-toolchain autoconf mingw-w64-x86_64-gcc mingw-w64-x86_64-freetype mingw-w64-x86_64-zstd mingw-w64-x86_64-libpng mingw-w64-x86_64-brotli mingw-w64-x86_64-SDL2 mingw-w64-x86_64-pkg-config - name: Checkout code uses: actions/checkout@v3 - name: Prepares NSIS plugins run: ./.github/prepare-nsis.ps1 shell: powershell - name: Create nsis installer uses: joncloud/makensis-action@v4 with: arguments: "/V3" additional-plugin-paths: NSIS_Plugins/Plugins script-file: src/Windows/nsis/onlineupgrade.nsi - name: CI-Build run: sh ./.github/build64-sdl2.sh shell: msys2 {0} - name: Update binaries of Nightly Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ./simuwin64-SDL2-nightly.zip asset_name: simuwin64-SDL2-nightly.zip tag: Nightly overwrite: true simutrans-124.5/.github/workflows/run-tests.yml000066400000000000000000000053161520675460500216550ustar00rootroot00000000000000# # CI for running tests # on: [push] name: Automated Tests jobs: run-tests-linux: runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v6 - name: Install dependencies run: | sudo apt-get -y update sudo apt-get -ym install libbz2-dev zlib1g-dev libpng-dev autoconf clang-18 ccache moreutils - name: Cache uses: actions/cache@v4 with: path: ~/.ccache key: ccache:${{ github.job }}:${{ github.ref }}:${{ github.sha }} restore-keys: | ccache:${{ github.job }}:${{ github.ref }} ccache:${{ github.job }} - name: Configure Simutrans Build run: | autoconf CC="ccache clang" CXX="ccache clang++" ./configure echo "FLAGS += -Wno-cast-align" >> config.default echo "FLAGS += -fsanitize=address,undefined -fno-sanitize-recover=all -fno-sanitize=shift,function" >> config.default echo "LDFLAGS += -fsanitize=address,undefined" >> config.default echo "STATIC := 0" >> config.default - name: Build Simutrans run: | CC="ccache clang" CXX="ccache clang++" make -j$(nproc) - name: Install pak64 run: | pushd simutrans ../tools/get_pak.sh pak64 popd - name: Install language files run: tools/get_lang_files.sh - name: Link tests as scenario run: | mkdir -p ~/simutrans/addons/pak/scenario ln -sT $GITHUB_WORKSPACE/tests ~/simutrans/addons/pak/scenario/automated-tests - name: Create simuconf run: | mkdir -p ~/simutrans/ echo "frames_per_second = 100" >> ~/simutrans/simuconf.tab echo "fast_forward_frames_per_second = 100" >> ~/simutrans/simuconf.tab - name: Run tests run: | export ASAN_OPTIONS="print_stacktrace=1 abort_on_error=1 detect_leaks=0" export UBSAN_OPTIONS="print_stacktrace=1 abort_on_error=1" cp tools/run-automated-tests.sh . chmod +x run-automated-tests.sh ulimit -St 600 # 10 minutes ought to be enough for anybody. ./run-automated-tests.sh - name: Run NWC protocol e2e suite # Black-box wire-protocol tests: spawn a headless server, send # one hand-rolled packet, parse the reply. Pins NWC_AUTH_PLAYER # / NWC_TOOL / NWC_SERVICE request and reply shapes and acts as # a regression tripwire for the network security class. Same # ASAN/UBSAN config as the scenario suite — a sanitizer hit # fails the job loudly. run: | export ASAN_OPTIONS="print_stacktrace=1 abort_on_error=1 detect_leaks=0" export UBSAN_OPTIONS="print_stacktrace=1 abort_on_error=1" python3 -m unittest discover -v -s tools/nwc_protocol_test -t . simutrans-124.5/.github/workflows/update-nightly-release.yml000066400000000000000000000013231520675460500242570ustar00rootroot00000000000000name: Update tag on: [push] jobs: update-nightly: runs-on: ubuntu-latest permissions: write-all steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: detect revision run: | echo "revision=`tools/get_revision.sh`" >> $GITHUB_ENV - name: set current head to nightly uses: richardsimko/update-tag@v1 with: tag_name: Nightly env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: rename the release to include revision uses: meeDamian/github-release@2.0 with: token: ${{ secrets.GITHUB_TOKEN }} tag: Nightly name: Nightly build r${{ env.revision }} allow_override: true simutrans-124.5/.gitignore000066400000000000000000000007131520675460500155550ustar00rootroot00000000000000*.ncb *.suo *.vcproj.* /out /Debug /Release build /vcpkg* /config.* /makeobj/makeobj /makeobj/makeobj.exe *sim /sim.exe *.*~ *~ /.build *.orig *.rej /simutrans/*pak* /simutrans/pak* /simutrans/PAK* /simutrans/text *.bdf *.zip *.vcxproj.* /ipch/* *.*sdf *.diff *.patch *.log /doxygen .vs/ revision.h .idea *.d *.res /autom4te.cache *.exe *.kdev4 vcpkg_installed/ UpgradeLog.htm src/linux/com.simutrans.Simutrans.metainfo.xml src/android/AndroidAppSettings.cfg simutrans-124.5/CMakeLists.txt000066400000000000000000000332741520675460500163350ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # cmake_minimum_required(VERSION 3.16) # Disable in-source builds if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message(FATAL_ERROR "Building Simutrans in-source is not supported. " "Please delete ${CMAKE_SOURCE_DIR}/CMakeCache.txt and configure in a different " "(preferrably empty) directory.") endif (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) set(CMAKE_WARN_DEPRECATED ON) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimutransVcpkgTriplet.cmake) project(simutrans LANGUAGES C CXX) # Force C++14 everywhere if(SIMUTRANS_STEAM_BUILT) set(CMAKE_CXX_STANDARD 17) else() set(CMAKE_CXX_STANDARD 14) endif() set(CMAKE_CXX_STANDARD_REQUIRED YES) set(CMAKE_CXX_EXTENSIONS OFF) include(TestBigEndian) TEST_BIG_ENDIAN(SIMUTRANS_BIG_ENDIAN) if (MINGW) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ON) set(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++") endif () if (APPLE) list(APPEND CMAKE_PREFIX_PATH "/usr/local/opt/readline") link_directories(/usr/local/lib /opt/homebrew/lib) set(CMAKE_EXE_LINKER_FLAGS "-framework Cocoa") endif () # # Dependencies # include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimutransFindDependencies.cmake) # # Configuration options # include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimutransBackend.cmake) # Select backend (SDL2 / GDI) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimutransCompileOptions.cmake) # CMake does not fill in CMAKE_BUILD_TYPE automatically. if (NOT MSVC) if (NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build type. Valid values are Debug Release MinSizeRel RelWithDebInfo" FORCE) endif () set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release MinSizeRel RelWithDebInfo) endif (NOT MSVC) # # sources # if(ANDROID) add_library(simutrans SHARED) target_link_libraries(simutrans PRIVATE android log) target_sources(simutrans PRIVATE src/android/android.cc) target_link_options(simutrans PRIVATE "-Wl,-z,max-page-size=16384") else () add_executable(simutrans MACOSX_BUNDLE) endif () if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") set_target_properties(simutrans PROPERTIES WIN32_EXECUTABLE 1) endif () include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimutransSourceList.cmake) # # output directory # file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/simutrans) set_target_properties( simutrans PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/simutrans/$<0:> ) # # Generate revision on compile time (so only if needed) if no revision is given # if (${SIMUTRANS_USE_REVISION}) file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/src/simutrans/revision.h "#define REVISION ${SIMUTRANS_USE_REVISION}\n") message(STATUS "Compiling Simutrans with manually specified revision ${SIMUTRANS_USE_REVISION}") set(SIMUTRANS_WC_REVISION ${SIMUTRANS_USE_REVISION}) set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimutransGenerateMetainfo.cmake) else () string(FIND ${CMAKE_GENERATOR} "Visual Studio" VS ) if (${VS} EQUAL 0) # using a script for MSVC project files file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/src/simutrans/revision.h "#define REVISION \n") add_custom_command(TARGET simutrans PRE_BUILD WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND cscript.exe ARGS "/Nologo tools/revision.jse" COMMENT "Find SVN revision" ) else () # using custom target svnhear that is always built to update the revision.h file using cmake script add_custom_target(svnheader ALL DEPENDS svn_header) add_custom_command(OUTPUT svn_header COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimutransRevision.cmake) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/simutrans/revision.h PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) add_dependencies(simutrans svnheader) endif () endif () # # Documentation # find_package(Doxygen) if (DOXYGEN_FOUND) if (DOXYGEN_HAVE_DOT) # DOXYGEN_DOT_PATH is deprecated, see # https://cmake.org/cmake/help/latest/module/FindDoxygen.html#variable:DOXYGEN_DOT_PATH get_target_property(graphviz_dot_executable doxygen::dot IMPORTED_LOCATION) get_filename_component(graphviz_dot_temp_dir ${GRAPHVIZ_DOT_EXE} DIRECTORY) file(TO_NATIVE_PATH "${graphviz_dot_temp_dir}" graphviz_dot_dir) endif () configure_file( documentation/Doxyfile.in documentation/Doxyfile @ONLY ) doxygen_add_docs(docs_simutrans CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/documentation/Doxyfile ) add_custom_target(docs DEPENDS docs_simutrans) # The sqapi doxygen file depends on .sh shell scripts to filter files if (LINUX OR MINGW) configure_file( documentation/DoxyfileSQAPI.in documentation/DoxyfileSQAPI @ONLY ) doxygen_add_docs(docs_sqapi CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/documentation/DoxyfileSQAPI WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/simutrans/script/api/ ) add_dependencies(docs docs_sqapi) endif () endif () # # Libraries # target_link_libraries(simutrans PRIVATE PNG::PNG) if (WIN32) target_sources(simutrans PRIVATE src/Windows/simres.rc) target_sources(simutrans PRIVATE src/simutrans/sys/clipboard_w32.cc) target_link_libraries(simutrans PRIVATE ws2_32 winmm) # Backup sound and music routines if the selected backend does not provide any routines set(BACKUP_SOUND src/simutrans/sound/win32_sound_xa.cc) set(BACKUP_MUSIC src/simutrans/music/w32_midi.cc) else () if (SIMUTRANS_BACKEND STREQUAL "sdl2") target_sources(simutrans PRIVATE src/simutrans/sys/clipboard_s2.cc) else () target_sources(simutrans PRIVATE src/simutrans/sys/clipboard_internal.cc) endif () if (APPLE) #Darwin kernel version is used to identify macOS version (16->10.12/Sierra) if (CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 16) #AVF set(BACKUP_SOUND src/simutrans/sound/AVF_core-audio_sound.mm) #AVF MIDI currently crash Simutrans #set(BACKUP_MUSIC src/simutrans/music/AVF_core-audio_midi.mm) set(BACKUP_MUSIC src/simutrans/music/no_midi.cc) else () #QTKit set(BACKUP_SOUND src/simutrans/sound/core-audio_sound.mm) set(BACKUP_MUSIC src/simutrans/music/core-audio_midi.mm) endif () else () set(BACKUP_SOUND src/simutrans/sound/no_sound.cc) set(BACKUP_MUSIC src/simutrans/music/no_midi.cc) endif () endif () if (SIMUTRANS_MULTI_THREAD) target_compile_definitions(simutrans PRIVATE MULTI_THREAD=1) target_link_libraries(simutrans PRIVATE Threads::Threads) endif (SIMUTRANS_MULTI_THREAD) if (SIMUTRANS_BACKEND STREQUAL "sdl2") target_sources(simutrans PRIVATE src/simutrans/display/simgraph16.cc src/simutrans/sys/simsys_s2.cc src/simutrans/sound/sdl2_sound.cc) target_include_directories(simutrans PRIVATE ${SDL2_INCLUDE_DIRS}) if (MINGW) target_link_libraries(simutrans PRIVATE ${SDL2_STATIC_LIBRARIES}) elseif (APPLE) target_link_libraries(simutrans PRIVATE ${SDL2_LIBRARIES}) elseif (MSVC) target_link_libraries(simutrans PRIVATE $ $,SDL2::SDL2,SDL2::SDL2-static>) elseif (ANDROID) target_link_libraries(simutrans PRIVATE SDL2 SDL2main) else () target_link_libraries(simutrans PRIVATE PkgConfig::SDL2) endif () target_compile_definitions(simutrans PRIVATE COLOUR_DEPTH=16) elseif (SIMUTRANS_BACKEND STREQUAL "gdi") target_sources(simutrans PRIVATE src/simutrans/display/simgraph16.cc src/simutrans/sys/simsys_w.cc ${BACKUP_SOUND} ) target_link_libraries(simutrans PRIVATE imm32 xaudio2_8) target_compile_definitions(simutrans PRIVATE COLOUR_DEPTH=16) else () if (NOT SIMUTRANS_BACKEND STREQUAL "none") message(WARNING "Unknown backend '${SIMUTRANS_BACKEND}', falling back to headless compilation") endif () target_sources(simutrans PRIVATE src/simutrans/display/simgraph0.cc src/simutrans/sys/simsys_posix.cc src/simutrans/sound/no_sound.cc src/simutrans/music/no_midi.cc) target_compile_definitions(simutrans PRIVATE COLOUR_DEPTH=0) endif () # # Link dependencies # target_link_libraries(simutrans PRIVATE ZLIB::ZLIB) target_link_libraries(simutrans PRIVATE BZip2::BZip2) # Freetype is mandatory for graphical builds if (SIMUTRANS_BACKEND STREQUAL "sdl2" OR SIMUTRANS_BACKEND STREQUAL "gdi") target_include_directories(simutrans PRIVATE ${Freetype_INCLUDE_DIRS}) if (MINGW) target_link_libraries(simutrans PRIVATE ${Freetype_STATIC_LIBRARIES}) elseif (APPLE) target_link_libraries(simutrans PRIVATE ${Freetype_LIBRARIES}) elseif (MSVC OR ANDROID) target_link_libraries(simutrans PRIVATE Freetype::Freetype) else () target_link_libraries(simutrans PRIVATE PkgConfig::Freetype) endif () if (SIMUTRANS_USE_FONTCONFIG) target_compile_definitions(simutrans PRIVATE USE_FONTCONFIG=1) target_link_libraries(simutrans PRIVATE Fontconfig::Fontconfig) endif () endif () if (SIMUTRANS_USE_UPNP) target_compile_definitions(simutrans PRIVATE USE_UPNP=1) target_link_libraries(simutrans PRIVATE MiniUPNP::MiniUPNP) endif (SIMUTRANS_USE_UPNP) if (SIMUTRANS_USE_ZSTD) target_sources(simutrans PRIVATE src/simutrans/io/rdwr/zstd_file_rdwr_stream.cc) target_include_directories(simutrans PRIVATE ${ZSTD_INCLUDE_DIRS}) target_compile_definitions(simutrans PRIVATE USE_ZSTD=1) if (MSVC) target_link_libraries(simutrans PRIVATE $,zstd::libzstd_shared,zstd::libzstd_static>) elseif (MINGW) # only 64 bit cmake, 32 bit is broken target_link_libraries(simutrans PRIVATE -lzstd) elseif (ANDROID) target_link_libraries(simutrans PRIVATE ZSTD::ZSTD) else() target_link_libraries(simutrans PRIVATE PkgConfig::ZSTD) endif () endif (SIMUTRANS_USE_ZSTD) # only consider FluidSynth for SDL2 and GDI backends if ((SIMUTRANS_BACKEND STREQUAL "sdl2") OR (SIMUTRANS_BACKEND STREQUAL "gdi")) if (SIMUTRANS_USE_FLUIDSYNTH_MIDI) target_sources(simutrans PRIVATE src/simutrans/music/fluidsynth.cc src/simutrans/gui/loadsoundfont_frame.cc) target_compile_definitions(simutrans PRIVATE USE_FLUIDSYNTH_MIDI=1) if (MINGW) # since fluidsynth 2.2.2 fluidsynth.pc include static libraries, but some are still missing target_link_libraries(simutrans PRIVATE ${FluidSynth_STATIC_LIBRARIES} -liconv -lgomp) elseif (MSVC) target_link_libraries(simutrans PRIVATE FluidSynth::libfluidsynth) elseif (ANDROID) target_link_libraries(simutrans PRIVATE fluidsynth) else () target_link_libraries(simutrans PRIVATE PkgConfig::FluidSynth) endif (MINGW) else () target_sources( simutrans PRIVATE ${BACKUP_MUSIC} ) endif (SIMUTRANS_USE_FLUIDSYNTH_MIDI) endif ((SIMUTRANS_BACKEND STREQUAL "sdl2") OR (SIMUTRANS_BACKEND STREQUAL "gdi")) if (SIMUTRANS_STEAM_BUILT AND NOT ANDROID) target_compile_definitions(simutrans PRIVATE STEAM_BUILT=1) target_sources(simutrans PRIVATE src/steam/steam.cc src/steam/workshop_item.cc src/steam/achievements.cc ) add_library(steam SHARED IMPORTED) if(APPLE) set_target_properties(steam PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/osx/libsteam_api.dylib) elseif(UNIX) set_target_properties(steam PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/linux64/libsteam_api.so) elseif(MSVC AND CMAKE_GENERATOR_PLATFORM MATCHES "x64") set_target_properties(steam PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/win64/steam_api64.dll) set_target_properties(steam PROPERTIES IMPORTED_IMPLIB ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/win64/steam_api64.lib) else(MSVC AND CMAKE_GENERATOR_PLATFORM MATCHES "Win32") set_target_properties(steam PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/steam_api.dll) set_target_properties(steam PROPERTIES IMPORTED_IMPLIB ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/steam_api.lib) endif() target_include_directories(simutrans PRIVATE ${CMAKE_SOURCE_DIR}/sdk/public/steam) target_link_libraries(simutrans PRIVATE steam) endif () # # compile options # if (MSVC) target_link_options(simutrans PRIVATE /INCREMENTAL:NO /NODEFAULTLIB:LIBCMTD /MANIFEST:NO /LARGEADDRESSAWARE) endif () if (SIMUTRANS_BIG_ENDIAN) target_compile_definitions(simutrans PRIVATE SIM_BIG_ENDIAN=1) endif () if (SIMUTRANS_VALGRIND_SUPPORT) add_definitions(-DUSE_VALGRIND_MEMCHECK=1) endif () if (SIMUTRANS_ENABLE_PROFILING) add_definitions(-DPROFILE=1) endif () target_compile_options(simutrans PRIVATE ${SIMUTRANS_COMMON_COMPILE_OPTIONS}) target_compile_definitions(simutrans PRIVATE $,DEBUG=1,>) target_compile_definitions(simutrans PRIVATE $,NDEBUG=1,>) target_compile_definitions(simutrans PRIVATE MSG_LEVEL=${SIMUTRANS_MSG_LEVEL}) if (SIMUTRANS_AUTOJOIN_PUBLIC) target_compile_definitions(simutrans PRIVATE AUTOJOIN_PUBLIC=1) endif () if (SIMUTRANS_ENABLE_WATERWAY_SIGNS) target_compile_definitions(simutrans PRIVATE ENABLE_WATERWAY_SIGNS=1) endif () if (SIMUTRANS_USE_SYSLOG) target_compile_definitions(simutrans PRIVATE SYSLOG=1) endif () if (SIMUTRANS_DEBUG_FLUSH_BUFFER) target_compile_definitions(simutrans PRIVATE DEBUG_FLUSH_BUFFER=1) endif () if (SIMUTRANS_USE_IP4_ONLY) target_compile_definitions(simutrans PRIVATE USE_IP4_ONLY=1) endif () if (SIMUTRANS_USE_OWN_PAKINSTALL) target_compile_definitions(simutrans PRIVATE USE_OWN_PAKINSTALL=1) endif () if (APPLE) target_compile_definitions(simutrans PRIVATE ALT_SDL_DIR=1) endif (APPLE) # # Package # if (APPLE) set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") include(MacBundle) endif () # # Tests # add_custom_target(test $ -set_workdir ${CMAKE_CURRENT_SOURCE_DIR}/simutrans -objects pak -scenario automated-tests -debug 2 -lang en -fps 100 DEPENDS simutrans ) # # Installation # include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/SimutransInstall.cmake) # # Nettool/Makeobj # if (NOT ANDROID) add_subdirectory(src/makeobj EXCLUDE_FROM_ALL) add_subdirectory(src/nettool EXCLUDE_FROM_ALL) endif () simutrans-124.5/LICENSE.txt000066400000000000000000000134001520675460500154050ustar00rootroot00000000000000This is the simutrans source code. (c) 1997-2004 Hj. Malthaner (c) since 2005 The Simutrans Team Simutrans source code is provided by the current simutrans team (team64@simutrans.com) as copyright holder under the artistic license. This includes the translations from: https://translator.simutrans.com/ the documentation, images and .dat files included in the source distribution, the midi files and the fonts with the exception of the m+10r.bdf, which is taken from the M+ font project: https://mplus-fonts.osdn.jp/ ----------------------------------------------------------------------------- The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: * "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. * "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. * "Copyright Holder" is whoever is named in the copyright or copyrights for the package. * "You" is you, if you're thinking about copying or distributing this Package. * "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) * "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End --------------------------------------------------------------------------- This means, translated to ebay terms: You may not charge for the program, if you charge shipping fees. simutrans-124.5/Makefile000066400000000000000000000703651520675460500152370ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # Define variables here to force them as simple flavor. -> Faster parallel builds. FLAGS := CFLAGS ?= LDFLAGS ?= LIBS := SOURCES := STATIC := 0 DYNAMICSTART = DYNAMICEND = CFG ?= default -include config.$(CFG) HOSTCC ?=$(CC) HOSTCXX ?=$(CXX) SDL2_CONFIG ?= pkg-config sdl2 #SDL2_CONFIG ?= sdl2-config FREETYPE_CONFIG ?= pkg-config freetype2 # FREETYPE_CONFIG ?= freetype-config FONTCONFIG_CONFIG ?= pkg-config fontconfig BACKENDS := gdi sdl2 mixer_sdl2 posix OSTYPES := amiga freebsd haiku linux mac mingw openbsd ifeq ($(findstring $(BACKEND), $(BACKENDS)),) $(error Unkown BACKEND "$(BACKEND)", must be one of "$(BACKENDS)") endif ifeq ($(findstring $(OSTYPE), $(OSTYPES)),) $(error Unkown OSTYPE "$(OSTYPE)", must be one of "$(OSTYPES)") endif ifeq ($(BACKEND),posix) COLOUR_DEPTH := 0 else COLOUR_DEPTH := 16 endif # since threads are needed to be linked dynamically on most systems ifdef MULTI_THREAD ifeq ($(shell expr $(MULTI_THREAD) \>= 1), 1) CFLAGS += -DMULTI_THREAD ifneq ($(OSTYPE),mingw) ifneq ($(OSTYPE),haiku) # mingw has already added pthread statically ifneq ($(OSTYPE),mingw) LDFLAGS += -lpthread endif endif endif else ifneq ($(OSTYPE),mingw) ifeq ($(BACKEND),sdl2) LDFLAGS += -lpthread endif ifeq ($(BACKEND),mixer_sdl2) LDFLAGS += -lpthread endif endif endif endif ifeq ($(OSTYPE),amiga) STD_LIBS ?= -lunix -lSDL_mixer -lsmpeg -lvorbisfile -lvorbis -logg CFLAGS += -mcrt=newlib -DSIM_BIG_ENDIAN -gstabs+ LDFLAGS += -Bstatic -non_shared else ifeq ($(OSTYPE),beos) # BeOS (obsolete) LIBS += -lnet else ifeq ($(OSTYPE),freebsd) CFLAGS += -I/usr/local/include else ifeq ($(OSTYPE),haiku) # Haiku (needs to activate the GCC 4x) LIBS += -lnetwork -lbe else ifeq ($(OSTYPE),mingw) CFLAGS += -DPNG_STATIC -DZLIB_STATIC ifeq ($(shell expr $(STATIC) \>= 1), 1) CFLAGS += -static LDFLAGS += -static-libgcc -static-libstdc++ -static endif ifeq ($(MINGW_PACKAGE_PREFIX),mingw-w64-i686) LDFLAGS += -Wl,--large-address-aware endif LDFLAGS += -pthread CFLAGS += -Wno-deprecated-copy -DNOMINMAX -DWIN32_LEAN_AND_MEAN -DWINVER=0x0501 -D_WIN32_IE=0x0500 LIBS += -lmingw32 -lgdi32 -lwinmm -lws2_32 -limm32 # Disable the console on Windows unless WIN32_CONSOLE is set or graphics are disabled ifdef WIN32_CONSOLE ifeq ($(shell expr $(WIN32_CONSOLE) \>= 1), 1) LDFLAGS += -mconsole endif else ifeq ($(BACKEND),posix) LDFLAGS += -mconsole else LDFLAGS += -mwindows endif else ifeq ($(OSTYPE),linux) ifeq ($(shell expr $(STATIC) \>= 1), 1) LDFLAGS += -Wl,-Bstatic -lm DYNAMICSTART = -Wl,-Bdynamic DYNAMICEND = -Wl,-Bstatic endif else ifeq ($(OSTYPE),mac) LDFLAGS += -framework Cocoa endif ifeq ($(BACKEND),sdl2) SOURCES += src/simutrans/sys/clipboard_s2.cc else ifeq ($(OSTYPE),mingw) SOURCES += src/simutrans/sys/clipboard_w32.cc else SOURCES += src/simutrans/sys/clipboard_internal.cc endif LIBS += -lbz2 -lz -lpng ifdef OPTIMISE ifeq ($(shell expr $(OPTIMISE) \>= 1), 1) CFLAGS += -O3 ifeq ($(findstring amiga, $(OSTYPE)),) ifneq ($(findstring clang, $(CXX)),) CFLAGS += -minline-all-stringops endif endif endif else CFLAGS += -O1 endif ifneq ($(LTO),) CFLAGS += -flto LDFLAGS += -flto endif ifdef DEBUG MSG_LEVEL ?= 3 PROFILE ?= 0 ifeq ($(shell expr $(DEBUG) \>= 1), 1) CFLAGS += -g -DDEBUG endif ifeq ($(shell expr $(DEBUG) \>= 2), 1) ifeq ($(shell expr $(PROFILE) \>= 2), 1) CFLAGS += -fno-inline endif endif ifeq ($(shell expr $(DEBUG) \>= 3), 1) ifeq ($(shell expr $(PROFILE) \>= 2), 1) CFLAGS += -O0 endif endif else CFLAGS += -DNDEBUG endif ifdef MSG_LEVEL CFLAGS += -DMSG_LEVEL=$(MSG_LEVEL) endif ifneq ($(BACKEND),posix) ifneq ($(FREETYPE_CONFIG),) CFLAGS += $(shell $(FREETYPE_CONFIG) --cflags) ifeq ($(shell expr $(STATIC) \>= 1), 1) # since static is not supported by slightly old freetype versions FTF := $(shell $(FREETYPE_CONFIG) --libs --static) ifneq ($(FTF),) LDFLAGS += $(subst -lm ,,$(FTF)) else LDFLAGS += $(shell $(FREETYPE_CONFIG) --libs) endif else LDFLAGS += $(shell $(FREETYPE_CONFIG) --libs) endif else LDFLAGS += -lfreetype ifeq ($(OSTYPE),mingw) LDFLAGS += -lpng -lharfbuzz endif endif ifeq ($(OSTYPE),mingw) LDFLAGS += -lfreetype endif ifdef USE_FONTCONFIG ifeq ($(shell expr $(USE_FONTCONFIG) \>= 1), 1) sdfsfsd := $(shell echo error) CFLAGS += -DUSE_FONTCONFIG CFLAGS += $(shell $(FONTCONFIG_CONFIG) --cflags) LDFLAGS += $(shell $(FONTCONFIG_CONFIG) --libs) endif endif endif ifdef USE_UPNP ifeq ($(shell expr $(USE_UPNP) \>= 1), 1) CFLAGS += -DUSE_UPNP LDFLAGS += -lminiupnpc ifeq ($(OSTYPE),mingw) ifeq ($(shell expr $(STATIC) \>= 1), 1) LDFLAGS += -Wl,-Bdynamic endif LDFLAGS += -liphlpapi ifeq ($(shell expr $(STATIC) \>= 1), 1) LDFLAGS += -Wl,-Bstatic endif endif endif endif ifdef USE_ZSTD ifeq ($(shell expr $(USE_ZSTD) \>= 1), 1) CFLAGS += -DUSE_ZSTD LDFLAGS += -lzstd SOURCES += src/simutrans/io/rdwr/zstd_file_rdwr_stream.cc endif endif ifdef USE_FLUIDSYNTH_MIDI ifeq ($(shell expr $(USE_FLUIDSYNTH_MIDI) \>= 1), 1) CFLAGS += -DUSE_FLUIDSYNTH_MIDI SOURCES += src/simutrans/music/fluidsynth.cc SOURCES += src/simutrans/gui/loadsoundfont_frame.cc LDFLAGS += $(DYNAMICSTART)-lfluidsynth $(DYNAMICEND) ifeq ($(OSTYPE),mingw) # fluidsynth.pc doesn't properly list dependant libraries, unable to use pkg-config. Manually listed below. Only valid for fluidsynth built with options: "-DBUILD_SHARED_LIBS=0 -Denable-aufile=0 -Denable-dbus=0 -Denable-ipv6=0 -Denable-jack=0 -Denable-ladspa=0 -Denable-midishare=0 -Denable-opensles=0 -Denable-oboe=0 -Denable-oss=0 -Denable-readline=0 -Denable-winmidi=0 -Denable-waveout=0 -Denable-libsndfile=0 -Denable-network=0 -Denable-pulseaudio=0 Denable-dsound=1 -Denable-sdl2=0" LDFLAGS += -lglib-2.0 -lintl -liconv -ldsound -lole32 else endif endif else USE_FLUIDSYNTH_MIDI := 0 endif ifdef PROFILE ifeq ($(shell expr $(PROFILE) \>= 1), 1) CFLAGS += -pg -DPROFILE ifeq ($(shell expr $(PROFILE) \>= 2), 1) CFLAGS += -fno-inline ifeq ($(findstring clang, $(CXX)),) CFLAGS += -fno-schedule-insns endif endif LDFLAGS += -pg endif endif ifdef WITH_REVISION ifeq ($(shell expr $(WITH_REVISION) \>= 1), 1) ifeq ($(shell expr $(WITH_REVISION) \>= 2), 1) REV := $(WITH_REVISION) else REV := $(shell tools/get_revision.sh) $(info Revision is $(REV)) endif endif endif ifneq ($(REV),) CFLAGS += -DREVISION=$(REV) DUMMY := $(shell rm -f src/simutrans/revision.h) else ifeq ("$(wildcard src/simutrans/revision.h)","") DUMMY := $(shell printf '#define REVISION' > src/simutrans/revision.h) endif endif ifneq ($(GIT_HASH),) GIT_HASH := $(shell git rev-parse --short=7 HEAD) ifeq ($(.SHELLSTATUS),0) $(info Git hash is 0x$(GIT_HASH)) CFLAGS += -DGIT_HASH=0x$(GIT_HASH) endif endif CFLAGS += -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align $(FLAGS) CCFLAGS += -ansi -Wstrict-prototypes -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 # sources (in alphabetic order) SOURCES += src/simutrans/builder/brueckenbauer.cc SOURCES += src/simutrans/builder/fabrikbauer.cc SOURCES += src/simutrans/builder/goods_manager.cc SOURCES += src/simutrans/builder/hausbauer.cc SOURCES += src/simutrans/builder/tree_builder.cc SOURCES += src/simutrans/builder/tunnelbauer.cc SOURCES += src/simutrans/builder/vehikelbauer.cc SOURCES += src/simutrans/builder/wegbauer.cc SOURCES += src/simutrans/dataobj/crossing_logic.cc SOURCES += src/simutrans/dataobj/environment.cc SOURCES += src/simutrans/dataobj/freelist.cc SOURCES += src/simutrans/dataobj/gameinfo.cc SOURCES += src/simutrans/dataobj/height_map_loader.cc SOURCES += src/simutrans/dataobj/koord.cc SOURCES += src/simutrans/dataobj/koord3d.cc SOURCES += src/simutrans/dataobj/loadsave.cc SOURCES += src/simutrans/dataobj/marker.cc SOURCES += src/simutrans/dataobj/objlist.cc SOURCES += src/simutrans/dataobj/pakset_manager.cc SOURCES += src/simutrans/dataobj/pakset_downloader.cc SOURCES += src/simutrans/dataobj/powernet.cc SOURCES += src/simutrans/dataobj/records.cc SOURCES += src/simutrans/dataobj/rect.cc SOURCES += src/simutrans/dataobj/ribi.cc SOURCES += src/simutrans/dataobj/route.cc SOURCES += src/simutrans/dataobj/scenario.cc SOURCES += src/simutrans/dataobj/schedule.cc SOURCES += src/simutrans/dataobj/settings.cc SOURCES += src/simutrans/dataobj/sve_cache.cc SOURCES += src/simutrans/dataobj/tabfile.cc SOURCES += src/simutrans/dataobj/translator.cc SOURCES += src/simutrans/descriptor/bridge_desc.cc SOURCES += src/simutrans/descriptor/building_desc.cc SOURCES += src/simutrans/descriptor/factory_desc.cc SOURCES += src/simutrans/descriptor/goods_desc.cc SOURCES += src/simutrans/descriptor/ground_desc.cc SOURCES += src/simutrans/descriptor/image.cc SOURCES += src/simutrans/descriptor/obj_base_desc.cc SOURCES += src/simutrans/descriptor/reader/bridge_reader.cc SOURCES += src/simutrans/descriptor/reader/building_reader.cc SOURCES += src/simutrans/descriptor/reader/citycar_reader.cc SOURCES += src/simutrans/descriptor/reader/crossing_reader.cc SOURCES += src/simutrans/descriptor/reader/factory_reader.cc SOURCES += src/simutrans/descriptor/reader/good_reader.cc SOURCES += src/simutrans/descriptor/reader/ground_reader.cc SOURCES += src/simutrans/descriptor/reader/groundobj_reader.cc SOURCES += src/simutrans/descriptor/reader/image_reader.cc SOURCES += src/simutrans/descriptor/reader/imagelist2d_reader.cc SOURCES += src/simutrans/descriptor/reader/imagelist_reader.cc SOURCES += src/simutrans/descriptor/reader/obj_reader.cc SOURCES += src/simutrans/descriptor/reader/pedestrian_reader.cc SOURCES += src/simutrans/descriptor/reader/roadsign_reader.cc SOURCES += src/simutrans/descriptor/reader/root_reader.cc SOURCES += src/simutrans/descriptor/reader/sim_reader.cc SOURCES += src/simutrans/descriptor/reader/skin_reader.cc SOURCES += src/simutrans/descriptor/reader/sound_reader.cc SOURCES += src/simutrans/descriptor/reader/text_reader.cc SOURCES += src/simutrans/descriptor/reader/tree_reader.cc SOURCES += src/simutrans/descriptor/reader/tunnel_reader.cc SOURCES += src/simutrans/descriptor/reader/vehicle_reader.cc SOURCES += src/simutrans/descriptor/reader/way_obj_reader.cc SOURCES += src/simutrans/descriptor/reader/way_reader.cc SOURCES += src/simutrans/descriptor/reader/xref_reader.cc SOURCES += src/simutrans/descriptor/sound_desc.cc SOURCES += src/simutrans/descriptor/tunnel_desc.cc SOURCES += src/simutrans/descriptor/vehicle_desc.cc SOURCES += src/simutrans/descriptor/way_desc.cc SOURCES += src/simutrans/display/font.cc SOURCES += src/simutrans/display/simgraph.cc SOURCES += src/simutrans/display/simgraph$(COLOUR_DEPTH).cc SOURCES += src/simutrans/display/simview.cc SOURCES += src/simutrans/display/viewport.cc SOURCES += src/simutrans/freight_list_sorter.cc SOURCES += src/simutrans/ground/boden.cc SOURCES += src/simutrans/ground/brueckenboden.cc SOURCES += src/simutrans/ground/fundament.cc SOURCES += src/simutrans/ground/grund.cc SOURCES += src/simutrans/ground/monorailboden.cc SOURCES += src/simutrans/ground/tunnelboden.cc SOURCES += src/simutrans/ground/wasser.cc SOURCES += src/simutrans/gui/components/gui_aligned_container.cc SOURCES += src/simutrans/gui/components/gui_building.cc SOURCES += src/simutrans/gui/components/gui_button.cc SOURCES += src/simutrans/gui/components/gui_button_to_chart.cc SOURCES += src/simutrans/gui/components/gui_chart.cc SOURCES += src/simutrans/gui/components/gui_colorbox.cc SOURCES += src/simutrans/gui/components/gui_combobox.cc SOURCES += src/simutrans/gui/components/gui_component.cc SOURCES += src/simutrans/gui/components/gui_container.cc SOURCES += src/simutrans/gui/components/gui_convoiinfo.cc SOURCES += src/simutrans/gui/components/gui_divider.cc SOURCES += src/simutrans/gui/components/gui_fixedwidth_textarea.cc SOURCES += src/simutrans/gui/components/gui_flowtext.cc SOURCES += src/simutrans/gui/components/gui_image.cc SOURCES += src/simutrans/gui/components/gui_image_list.cc SOURCES += src/simutrans/gui/components/gui_label.cc SOURCES += src/simutrans/gui/components/gui_map_preview.cc SOURCES += src/simutrans/gui/components/gui_numberinput.cc SOURCES += src/simutrans/gui/components/gui_obj_view.cc SOURCES += src/simutrans/gui/components/gui_schedule.cc SOURCES += src/simutrans/gui/components/gui_scrollbar.cc SOURCES += src/simutrans/gui/components/gui_scrolled_list.cc SOURCES += src/simutrans/gui/components/gui_scrollpane.cc SOURCES += src/simutrans/gui/components/gui_speedbar.cc SOURCES += src/simutrans/gui/components/gui_tab_panel.cc SOURCES += src/simutrans/gui/components/gui_textarea.cc SOURCES += src/simutrans/gui/components/gui_textinput.cc SOURCES += src/simutrans/gui/components/gui_timeinput.cc SOURCES += src/simutrans/gui/components/gui_waytype_tab_panel.cc SOURCES += src/simutrans/gui/components/gui_world_view.cc SOURCES += src/simutrans/gui/ai_option.cc SOURCES += src/simutrans/gui/ai_selector.cc SOURCES += src/simutrans/gui/banner.cc SOURCES += src/simutrans/gui/base_info.cc SOURCES += src/simutrans/gui/baum_edit.cc SOURCES += src/simutrans/gui/chat_frame.cc SOURCES += src/simutrans/gui/city_info.cc SOURCES += src/simutrans/gui/citybuilding_edit.cc SOURCES += src/simutrans/gui/citylist_frame.cc SOURCES += src/simutrans/gui/citylist_stats.cc SOURCES += src/simutrans/gui/climates.cc SOURCES += src/simutrans/gui/convoi_detail.cc SOURCES += src/simutrans/gui/convoi_filter_frame.cc SOURCES += src/simutrans/gui/convoi_frame.cc SOURCES += src/simutrans/gui/convoi_info.cc SOURCES += src/simutrans/gui/convoy_item.cc SOURCES += src/simutrans/gui/curiosity_edit.cc SOURCES += src/simutrans/gui/curiositylist_frame.cc SOURCES += src/simutrans/gui/curiositylist_stats.cc SOURCES += src/simutrans/gui/depot_frame.cc SOURCES += src/simutrans/gui/depotlist_frame.cc SOURCES += src/simutrans/gui/display_settings.cc SOURCES += src/simutrans/gui/enlarge_map_frame.cc SOURCES += src/simutrans/gui/extend_edit.cc SOURCES += src/simutrans/gui/fabrik_info.cc SOURCES += src/simutrans/gui/factory_chart.cc SOURCES += src/simutrans/gui/factory_edit.cc SOURCES += src/simutrans/gui/factorylist_frame.cc SOURCES += src/simutrans/gui/factorylist_stats.cc SOURCES += src/simutrans/gui/goods_frame.cc SOURCES += src/simutrans/gui/goods_stats.cc SOURCES += src/simutrans/gui/ground_info.cc SOURCES += src/simutrans/gui/groundobj_edit.cc SOURCES += src/simutrans/gui/gui_frame.cc SOURCES += src/simutrans/gui/gui_theme.cc SOURCES += src/simutrans/gui/halt_info.cc SOURCES += src/simutrans/gui/halt_list_filter_frame.cc SOURCES += src/simutrans/gui/halt_list_frame.cc SOURCES += src/simutrans/gui/halt_list_stats.cc SOURCES += src/simutrans/gui/headquarter_info.cc SOURCES += src/simutrans/gui/help_frame.cc SOURCES += src/simutrans/gui/jump_frame.cc SOURCES += src/simutrans/gui/kennfarbe.cc SOURCES += src/simutrans/gui/label_info.cc SOURCES += src/simutrans/gui/labellist_frame.cc SOURCES += src/simutrans/gui/labellist_stats.cc SOURCES += src/simutrans/gui/line_item.cc SOURCES += src/simutrans/gui/line_management_gui.cc SOURCES += src/simutrans/gui/load_relief_frame.cc SOURCES += src/simutrans/gui/loadfont_frame.cc SOURCES += src/simutrans/gui/loadsave_frame.cc SOURCES += src/simutrans/gui/map_frame.cc SOURCES += src/simutrans/gui/message_frame.cc SOURCES += src/simutrans/gui/message_option.cc SOURCES += src/simutrans/gui/message_stats.cc SOURCES += src/simutrans/gui/messagebox.cc SOURCES += src/simutrans/gui/minimap.cc SOURCES += src/simutrans/gui/money_frame.cc SOURCES += src/simutrans/gui/obj_info.cc SOURCES += src/simutrans/gui/optionen.cc SOURCES += src/simutrans/gui/pakinstaller.cc SOURCES += src/simutrans/gui/pakselector.cc SOURCES += src/simutrans/gui/password_frame.cc SOURCES += src/simutrans/gui/player_frame.cc SOURCES += src/simutrans/gui/player_ranking_frame.cc SOURCES += src/simutrans/gui/privatesign_info.cc SOURCES += src/simutrans/gui/savegame_frame.cc SOURCES += src/simutrans/gui/scenario_frame.cc SOURCES += src/simutrans/gui/scenario_info.cc SOURCES += src/simutrans/gui/schedule_list.cc SOURCES += src/simutrans/gui/script_generator_frame.cc SOURCES += src/simutrans/gui/script_tool_frame.cc SOURCES += src/simutrans/gui/server_frame.cc SOURCES += src/simutrans/gui/settings_frame.cc SOURCES += src/simutrans/gui/settings_stats.cc SOURCES += src/simutrans/gui/signal_info.cc SOURCES += src/simutrans/gui/signal_spacing.cc SOURCES += src/simutrans/gui/simwin.cc SOURCES += src/simutrans/gui/sound_frame.cc SOURCES += src/simutrans/gui/sprachen.cc SOURCES += src/simutrans/gui/station_building_select.cc SOURCES += src/simutrans/gui/themeselector.cc SOURCES += src/simutrans/gui/tool_selector.cc SOURCES += src/simutrans/gui/trafficlight_info.cc SOURCES += src/simutrans/gui/vehiclelist_frame.cc SOURCES += src/simutrans/gui/welt.cc SOURCES += src/simutrans/io/classify_file.cc SOURCES += src/simutrans/io/raw_image.cc SOURCES += src/simutrans/io/raw_image_bmp.cc SOURCES += src/simutrans/io/raw_image_png.cc SOURCES += src/simutrans/io/raw_image_ppm.cc SOURCES += src/simutrans/io/rdwr/adler32_stream.cc SOURCES += src/simutrans/io/rdwr/bzip2_file_rdwr_stream.cc SOURCES += src/simutrans/io/rdwr/compare_file_rd_stream.cc SOURCES += src/simutrans/io/rdwr/raw_file_rdwr_stream.cc SOURCES += src/simutrans/io/rdwr/rdwr_stream.cc SOURCES += src/simutrans/io/rdwr/zlib_file_rdwr_stream.cc SOURCES += src/simutrans/network/checksum.cc SOURCES += src/simutrans/network/memory_rw.cc SOURCES += src/simutrans/network/network.cc SOURCES += src/simutrans/network/network_address.cc SOURCES += src/simutrans/network/network_cmd.cc SOURCES += src/simutrans/network/network_cmd_ingame.cc SOURCES += src/simutrans/network/network_cmd_scenario.cc SOURCES += src/simutrans/network/network_cmp_pakset.cc SOURCES += src/simutrans/network/network_file_transfer.cc SOURCES += src/simutrans/network/network_packet.cc SOURCES += src/simutrans/network/network_socket_list.cc SOURCES += src/simutrans/network/pakset_info.cc SOURCES += src/simutrans/obj/baum.cc SOURCES += src/simutrans/obj/bruecke.cc SOURCES += src/simutrans/obj/crossing.cc SOURCES += src/simutrans/obj/depot.cc SOURCES += src/simutrans/obj/field.cc SOURCES += src/simutrans/obj/gebaeude.cc SOURCES += src/simutrans/obj/groundobj.cc SOURCES += src/simutrans/obj/label.cc SOURCES += src/simutrans/obj/leitung2.cc SOURCES += src/simutrans/obj/pillar.cc SOURCES += src/simutrans/obj/roadsign.cc SOURCES += src/simutrans/obj/signal.cc SOURCES += src/simutrans/obj/simobj.cc SOURCES += src/simutrans/obj/tunnel.cc SOURCES += src/simutrans/obj/way/kanal.cc SOURCES += src/simutrans/obj/way/maglev.cc SOURCES += src/simutrans/obj/way/monorail.cc SOURCES += src/simutrans/obj/way/narrowgauge.cc SOURCES += src/simutrans/obj/way/runway.cc SOURCES += src/simutrans/obj/way/schiene.cc SOURCES += src/simutrans/obj/way/strasse.cc SOURCES += src/simutrans/obj/way/weg.cc SOURCES += src/simutrans/obj/wayobj.cc SOURCES += src/simutrans/obj/wolke.cc SOURCES += src/simutrans/obj/zeiger.cc SOURCES += src/simutrans/old_blockmanager.cc SOURCES += src/simutrans/player/ai.cc SOURCES += src/simutrans/player/ai_goods.cc SOURCES += src/simutrans/player/ai_passenger.cc SOURCES += src/simutrans/player/ai_scripted.cc SOURCES += src/simutrans/player/finance.cc SOURCES += src/simutrans/player/simplay.cc SOURCES += src/simutrans/script/api/api_city.cc SOURCES += src/simutrans/script/api/api_command.cc SOURCES += src/simutrans/script/api/api_const.cc SOURCES += src/simutrans/script/api/api_control.cc SOURCES += src/simutrans/script/api/api_convoy.cc SOURCES += src/simutrans/script/api/api_factory.cc SOURCES += src/simutrans/script/api/api_gui.cc SOURCES += src/simutrans/script/api/api_halt.cc SOURCES += src/simutrans/script/api/api_include.cc SOURCES += src/simutrans/script/api/api_line.cc SOURCES += src/simutrans/script/api/api_map_objects.cc SOURCES += src/simutrans/script/api/api_obj_desc.cc SOURCES += src/simutrans/script/api/api_obj_desc_base.cc SOURCES += src/simutrans/script/api/api_pathfinding.cc SOURCES += src/simutrans/script/api/api_player.cc SOURCES += src/simutrans/script/api/api_scenario.cc SOURCES += src/simutrans/script/api/api_schedule.cc SOURCES += src/simutrans/script/api/api_settings.cc SOURCES += src/simutrans/script/api/api_simple.cc SOURCES += src/simutrans/script/api/api_tiles.cc SOURCES += src/simutrans/script/api/api_world.cc SOURCES += src/simutrans/script/api/export_desc.cc SOURCES += src/simutrans/script/api/get_next.cc SOURCES += src/simutrans/script/api_class.cc SOURCES += src/simutrans/script/api_function.cc SOURCES += src/simutrans/script/api_param.cc SOURCES += src/simutrans/script/dynamic_string.cc SOURCES += src/simutrans/script/export_objs.cc SOURCES += src/simutrans/script/script.cc SOURCES += src/simutrans/script/script_loader.cc SOURCES += src/simutrans/script/script_tool_manager.cc SOURCES += src/simutrans/simachievements.cc SOURCES += src/simutrans/simconvoi.cc SOURCES += src/simutrans/simdebug.cc SOURCES += src/simutrans/simevent.cc SOURCES += src/simutrans/simfab.cc SOURCES += src/simutrans/simhalt.cc SOURCES += src/simutrans/siminteraction.cc SOURCES += src/simutrans/simintr.cc SOURCES += src/simutrans/simio.cc SOURCES += src/simutrans/simline.cc SOURCES += src/simutrans/simlinemgmt.cc SOURCES += src/simutrans/simloadingscreen.cc SOURCES += src/simutrans/simmain.cc SOURCES += src/simutrans/simmem.cc SOURCES += src/simutrans/simmesg.cc SOURCES += src/simutrans/simskin.cc SOURCES += src/simutrans/simsound.cc SOURCES += src/simutrans/simticker.cc SOURCES += src/simutrans/simware.cc SOURCES += src/simutrans/sys/simsys.cc SOURCES += src/simutrans/tool/simmenu.cc SOURCES += src/simutrans/tool/simtool-script-generator.cc SOURCES += src/simutrans/tool/simtool-scripted.cc SOURCES += src/simutrans/tool/simtool.cc SOURCES += src/simutrans/utils/cbuffer.cc SOURCES += src/simutrans/utils/checklist.cc SOURCES += src/simutrans/utils/csv.cc SOURCES += src/simutrans/utils/log.cc SOURCES += src/simutrans/utils/searchfolder.cc SOURCES += src/simutrans/utils/sha1.cc SOURCES += src/simutrans/utils/sha1_hash.cc SOURCES += src/simutrans/utils/simrandom.cc SOURCES += src/simutrans/utils/simstring.cc SOURCES += src/simutrans/utils/simthread.cc SOURCES += src/simutrans/utils/unicode.cc SOURCES += src/simutrans/vehicle/air_vehicle.cc SOURCES += src/simutrans/vehicle/movingobj.cc SOURCES += src/simutrans/vehicle/pedestrian.cc SOURCES += src/simutrans/vehicle/rail_vehicle.cc SOURCES += src/simutrans/vehicle/road_vehicle.cc SOURCES += src/simutrans/vehicle/simroadtraffic.cc SOURCES += src/simutrans/vehicle/vehicle.cc SOURCES += src/simutrans/vehicle/vehicle_base.cc SOURCES += src/simutrans/vehicle/water_vehicle.cc SOURCES += src/simutrans/world/placefinder.cc SOURCES += src/simutrans/world/simcity.cc SOURCES += src/simutrans/world/simplan.cc SOURCES += src/simutrans/world/simworld.cc SOURCES += src/simutrans/world/surface.cc SOURCES += src/simutrans/world/terraformer.cc SOURCES += src/squirrel/sq_extensions.cc SOURCES += src/squirrel/sqstdlib/sqstdaux.cc SOURCES += src/squirrel/sqstdlib/sqstdblob.cc SOURCES += src/squirrel/sqstdlib/sqstdio.cc SOURCES += src/squirrel/sqstdlib/sqstdmath.cc SOURCES += src/squirrel/sqstdlib/sqstdrex.cc SOURCES += src/squirrel/sqstdlib/sqstdstream.cc SOURCES += src/squirrel/sqstdlib/sqstdstring.cc SOURCES += src/squirrel/sqstdlib/sqstdsystem.cc SOURCES += src/squirrel/squirrel/sqapi.cc SOURCES += src/squirrel/squirrel/sqbaselib.cc SOURCES += src/squirrel/squirrel/sqclass.cc SOURCES += src/squirrel/squirrel/sqcompiler.cc SOURCES += src/squirrel/squirrel/sqdebug.cc SOURCES += src/squirrel/squirrel/sqfuncstate.cc SOURCES += src/squirrel/squirrel/sqlexer.cc SOURCES += src/squirrel/squirrel/sqmem.cc SOURCES += src/squirrel/squirrel/sqobject.cc SOURCES += src/squirrel/squirrel/sqstate.cc SOURCES += src/squirrel/squirrel/sqtable.cc SOURCES += src/squirrel/squirrel/sqvm.cc ifeq ($(BACKEND),gdi) SOURCES += src/simutrans/sys/simsys_w.cc SOURCES += src/simutrans/sound/win32_sound_xa.cc LDFLAGS += -lxaudio2_8 ifneq ($(shell expr $(USE_FLUIDSYNTH_MIDI) \>= 1), 1) SOURCES += src/simutrans/music/w32_midi.cc endif endif ifeq ($(BACKEND),sdl2) SOURCES += src/simutrans/sys/simsys_s2.cc ifeq ($(OSTYPE),mac) AV_FOUNDATION ?= 0 ifeq ($(shell expr $(AV_FOUNDATION) \>= 1), 1) # Core Audio (AVFoundation) base sound system routines SOURCES += src/simutrans/sound/AVF_core-audio_sound.mm LIBS += -framework Foundation -framework AVFoundation ifneq ($(shell expr $(USE_FLUIDSYNTH_MIDI) \>= 1), 1) SOURCES += src/simutrans/music/AVF_core-audio_midi.mm endif else # Core Audio (Quicktime) base sound system routines SOURCES += src/simutrans/sound/core-audio_sound.mm LIBS += -framework Foundation -framework QTKit ifneq ($(shell expr $(USE_FLUIDSYNTH_MIDI) \>= 1), 1) SOURCES += src/simutrans/music/core-audio_midi.mm endif endif else SOURCES += src/simutrans/sound/sdl2_sound.cc ifneq ($(shell expr $(USE_FLUIDSYNTH_MIDI) \>= 1), 1) ifneq ($(OSTYPE),mingw) SOURCES += src/simutrans/music/no_midi.cc else SOURCES += src/simutrans/music/w32_midi.cc endif endif endif ifeq ($(SDL2_CONFIG),) ifeq ($(OSTYPE),mac) SDL_CFLAGS := -F /Library/Frameworks -I/Library/Frameworks/SDL2.framework/Headers SDL_LDFLAGS := -framework SDL2 -F /Library/Frameworks -I /Library/Frameworks/SDL2.framework/Headers else SDL_CFLAGS := -I$(MINGDIR)/include/SDL2 -Dmain=SDL_main SDL_LDFLAGS := -lSDL2main -lSDL2 endif else SDL_CFLAGS := $(shell $(SDL2_CONFIG) --cflags) SDL_LDFLAGS := $(DYNAMICSTART) $(shell $(SDL2_CONFIG) --libs) $(DYNAMICEND) ifeq ($(shell expr $(STATIC) \>= 1), 1) ifeq ($(OSTYPE),mingw) SDL_LDFLAGS = $(shell $(SDL2_CONFIG) --static --libs) endif endif endif CFLAGS += $(SDL_CFLAGS) LIBS += $(SDL_LDFLAGS) endif ifeq ($(BACKEND),mixer_sdl2) SOURCES += src/simutrans/sys/simsys_s2.cc ifeq ($(SDL2_CONFIG),) ifeq ($(OSTYPE),mac) SDL_CFLAGS := -F /Library/Frameworks -I/Library/Frameworks/SDL2.framework/Headers SDL_LDFLAGS := -framework SDL2 -F /Library/Frameworks -I /Library/Frameworks/SDL2.framework/Headers else SDL_CFLAGS := -I$(MINGDIR)/include/SDL2 -Dmain=SDL_main SDL_LDFLAGS := -lSDL2main -lSDL2 endif else SOURCES += src/simutrans/sound/sdl_mixer_sound.cc SOURCES += src/simutrans/music/sdl_midi.cc SDL_CFLAGS := $(shell $(SDL2_CONFIG) --cflags) SDL_LDFLAGS := $(DYMANICSTART) $(shell $(SDL2_CONFIG) --libs) -lSDL2_mixer $(DYNAMICEND) ifeq ($(shell expr $(STATIC) \>= 1), 1) ifeq ($(OSTYPE),mingw) SDL_LDFLAGS = $(shell $(SDL2_CONFIG) --static --libs) -lSDL2_mixer endif endif endif CFLAGS += $(SDL_CFLAGS) LIBS += $(SDL_LDFLAGS) endif ifeq ($(BACKEND),posix) SOURCES += src/simutrans/sys/simsys_posix.cc SOURCES += src/simutrans/music/no_midi.cc SOURCES += src/simutrans/sound/no_sound.cc endif CFLAGS += -DCOLOUR_DEPTH=$(COLOUR_DEPTH) ifeq ($(OSTYPE),mingw) SOURCES += src/Windows/simres.rc ifneq ($(REV),) WINDRES ?= windres -DREVISION=$(REV) else WINDRES ?= windres -DREVISION endif endif CCFLAGS += $(CFLAGS) CXXFLAGS += $(CFLAGS) # static linking everything but SDL2 works is the best one can do on Linux ... ifeq ($(OSTYPE),linux) ifeq ($(shell expr $(STATIC) \>= 1), 1) LIBS += -static-libgcc -static-libstdc++ ifeq ($(BACKEND),posix) LIBS += -static endif endif endif BUILDDIR ?= build/$(CFG) PROGDIR ?= $(BUILDDIR) PROG ?= sim .DEFAULT_GOAL := simutrans .PHONY: simutrans makeobj nettool include common.mk ifeq ($(OSTYPE),mac) include OSX/osx.mk endif all: simutrans makeobj nettool makeobj: @echo "Building makeobj" $(Q)$(MAKE) -e -C src/makeobj FLAGS="$(FLAGS)" nettool: @echo "Building nettool" $(Q)$(MAKE) -e -C src/nettool FLAGS="$(FLAGS)" test: simutrans $(PROGDIR)/$(PROG) -set_basedir $(shell pwd)/simutrans -objects pak -scenario automated-tests -debug 2 -lang en -fps 100 clean: @echo "===> Cleaning up" $(Q)rm -f $(OBJS) $(Q)rm -f $(DEPS) $(Q)rm -f $(PROGDIR)/$(PROG) $(Q)rm -fr $(PROGDIR)/$(PROG).app $(Q)$(MAKE) -e -C src/makeobj clean $(Q)$(MAKE) -e -C src/nettool clean simutrans-124.5/README.html000066400000000000000000000277071520675460500154240ustar00rootroot00000000000000 About the Simutrans Code

Simutrans

1) About

Simutrans is a freeware and open-source transportation simulator. Your goal is to establish a successful transport company. Transport passengers, mail and goods by rail, road, ship, and even air. Interconnect districts, cities, public buildings, industries and tourist attractions by building a transport network you always dreamed of.

1.1) Download Simutrans

You can download Simutrans from:

There is a "nightly" version available, but most most people should use the last "stable" release.

Main sites

Help

Additional downloads

2) Compiling Simutrans

This is a short guide on compiling simutrans. If you want more detailed information, read the Compiling Simutrans wiki page.

If you are on Windows, download either Microsoft Visual Studio or MSYS2. MSVC is easy for debugging, MSYS2 is easy to set up (but it has to be done on the command line).

2.1) Getting the Source Code

You can download the latest version with a SVN client:

svn checkout svn://servers.simutrans.org/simutrans/trunk

If you prefer to use git, there is a mirror of the svn repository available at github:

git clone http://github.com/simutrans/simutrans.git

Note that the svn repository is the main repository, and the git repository is just a mirror. If you use git instead of svn, you will need to set the game version manually to join a network game.

2.2) Getting the libraries

This is a list of libraries used by Simutrans. Not all of them are necessary, some are optional, so pick them according to your needs. Read below about how to install them.

Library Website Necessary? Notes
zlib https://zlib.net/ Necessary Basic compression support
bzip2 https://www.bzip.org/downloads.html Necessary Alternative compression. You can pick this or zstd
libpng http://www.libpng.org/pub/png/ Necessary Image manipulation
libSDL2 http://www.libsdl.org/ Necessary* *On Linux & Mac. Optional but recommended for Windows. Graphics back-end
libzstd https://github.com/facebook/zstd Optional Alternative compression (larger save files than bzip2, but faster)
libfreetype http://www.freetype.org/ Necessary TrueType font support
libminiupnpc http://miniupnp.free.fr/ Optional Easy Server option
libfluidsynth https://www.fluidsynth.org/ Optional MIDI playback recommended on Linux & temporarily on Mac
libSDL2_mixer http://www.libsdl.org/ Optional Alternative MIDI playback and sound system
libfontconfig https://www.fontconfig.org/ Optional Font autodetection (Linux/Mac)

You will also need pkgconfig (Unix) or vcpkg (Microsoft Visual C++)

  • MSVC: Should compile the needed libraries automatically, if VCPKG is installed. Alternative, copy install-building-libs-.bat to the vcpkg folder and run it.
  • MSYS2: Run [setup-mingw.sh] (tools/setup-mingw.sh) to get the libraries and set up the environment.
  • Ubuntu/Debian: Run [setup-debian.sh] (tools/setup-debian.sh) to get the libraries and set up the environment.
  • Linux: Use pkgs.org to search for development libraries available in your package manager.
  • Mac: Install libraries via [Homebrew] (https://brew.sh/). Some guidance can be found in the github directory.

2.3) Compiling

Go to the source code directory of simutrans (simutrans/trunk if you downloaded from svn). You have three build systems to choose from: make, MSVC, and CMake. We recommend make or MSVC for debug builds, cmake for MacOS and Android.

Compiling will give you only the executable, you still need a Simutrans installation to run the program. You can start simutrans with -use_workdir to point it to an existing installation.

2.3.1) Compiling with make

The executable will be built in build/default.

(Linux) autoconf
(MacOS) autoreconf -ivf
./configure
make -j 4
(MacOS) make OSX/getversion

2.3.2) Compiling with Microsoft Visual Studio

Simutrans solution is a single solution file simutrans.sln with 4 projects:

  • Simutrans-Main: The project that holds the shared, non back-end specific, files. All the followings use Main to build the specific back-end executables.
  • Simutrans SDL2: Preferred back-end for Simutrans.
  • Simutrans GDI: Windows-only back-end.
  • Simutrans Server: Server back-end with no graphical interface.

Select on of them as startup project and in the configuration manager compile it.

2.3.3) Compiling with CMake

The executable will be built in build/simutrans.

Commandline on Linux/MinGW/MacOS ...
cmake -G "Insert Correct Makefiles Generator" -B build .
cmake --build build -j 4

See here for a list of generators.

MSVC
mkdir build && cd build
cmake.exe .. -G "Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE=[vcpkg-root]/scripts/buildsystems/vcpkg.cmake
cmake --build . --config Release

2.4) Cross-Compiling

If you want to cross-compile Simutrans from Linux for Windows, see the Cross-Compiling Simutrans wiki page.

3) Contribute

You cand find general information about contributing to Simutrans in the Development Index of the wiki.

3.1) Coding

  • If you want to contribute, read the coding guidelines in simutrans/documentation/coding_styles.txt
  • You definitely should check out the Technical Documentation Sub-Forum as well.
  • Do not open Pull Requests in GitHub. Use the Patches & Projects Sub-Forum instead.

3.2) Translating

Simutrans is constantly updating and adding texts so we are always in need for translators:

3.3) Painting

Simutrans is always looking for artists! If you want to paint graphics for Simutrans, check:

3.4) Reporting bugs

For bug reports use the Bug Reports Sub-Forum.

4) License

Simutrans is licensed under the Artistic License version 1.0. The Artistic License 1.0 is an OSI-approved license which allows for use, distribution, modification, and distribution of modified versions, under the terms of the Artistic License 1.0. For the complete license text see LICENSE.txt.

Simutrans paksets (which are necessary to run the game) have their own license, but no one is included alongside this code.

5) Credits

Simutrans was originally written by Hansjörg Malthaner "Hajo" from 1997 until he retired from development around 2004. Since then a team of contributors (The Simutrans Team) lead by Markus Pristovsek "Prissi" is developing Simutrans.

A list of early contributors can be found in simutrans/thanks.txt

simutrans-124.5/README.md000066400000000000000000000232451520675460500150510ustar00rootroot00000000000000# Simutrans - 1.0) [About](#1-about) - 1.1) [Download Simutrans](#11-download-simutrans) - 1.2) [Helpful links](#12-helpful-links) - 2.0) [Compiling Simutrans](#2-compiling-simutrans) - 2.1) [Getting the Source Code](#21-getting-the-source-code) - 2.2) [Getting the libraries](#22-getting-the-libraries) - 2.3) [Compiling](#23-compiling) - 2.3.1) [Compiling with make](#232-compiling-with-make) - 2.3.2) [Compiling with MSVC](#233-compiling-with-microsoft-visual-studio) - 2.3.3) [Compiling with CMake](#231-compiling-with-cmake) - 2.4) [Cross-Compiling](#24-Cross-Compiling) - 3.0) [Contribute](#3-contribute) - 3.1) [Coding](#31-coding) - 3.2) [Getting the libraries](#32-translating) - 3.3) [Painting](#33-painting) - 3.4) [Reporting-bugs](#34-reporting-bugs) - 4.0) [License](#4-license) - 5.0) [Credits](#5-credits) ## 1) About Simutrans is a freeware and open-source transportation simulator. Your goal is to establish a successful transport company. Transport passengers, mail and goods by rail, road, ship, and even air. Interconnect districts, cities, public buildings, industries and tourist attractions by building a transport network you always dreamed of. ## 1.1) Download Simutrans You can download Simutrans from: - [Simutrans Official Website](https://www.simutrans.com/en/) - [Steam](https://store.steampowered.com/app/434520/Simutrans/) - [Linux package managers](https://pkgs.org/search/?q=simutrans) There is a "nightly" version available, but most most people should use the last "stable" release. ## 1.2) Helpful links ### Main sites - [Main website](https://www.simutrans.com/) - [International Forum](https://forum.simutrans.com/) - [German Forumurl](https://www.simutrans-forum.de/) - [Japanese Forum](https://forum.japanese.simutrans.com/) ### Help - [Simutrans Wiki](https://wiki.simutrans.com) - [Starter Guide (PDF)](http://simutrans.igoreliezer.com/docs/Simutrans%20Starter%20Guide.pdf) - [Help Center (International Forum)](https://forum.simutrans.com/index.php/board,7.0.html) ### Additional downloads - [Paksets](https://www.simutrans.com/en/paksets/) - [Addons](https://simutrans-germany.com/wiki/wiki/en_Addons) ## 2) Compiling Simutrans This is a short guide on compiling simutrans. If you want more detailed information, read the [Compiling Simutrans](https://simutrans-germany.com/wiki/wiki/en_CompilingSimutrans) wiki page. If you are on Windows, download either [Microsoft Visual Studio](https://visualstudio.microsoft.com/) or [MSYS2](https://www.msys2.org/). MSVC is easy for debugging, MSYS2 is easy to set up (but it has to be done on the command line). ### 2.1) Getting the Source Code You can download the latest version with a SVN client: ``` svn checkout svn://servers.simutrans.org/simutrans/trunk ``` If you prefer to use git, there is a mirror of the svn repository available at github: ``` git clone http://github.com/simutrans/simutrans.git ``` Note that the svn repository is the main repository, and the git repository is just a mirror. If you use git instead of svn, you will need to set the game version manually to join a network game. ### 2.2) Getting the libraries This is a list of libraries used by Simutrans. Not all of them are necessary, some are optional, so pick them according to your needs. Read below about how to install them. | Library | Website | Necessary? | Notes | |---------------|-------------------------------------|------------|---------------------------------------------------------------------------| | zlib | https://zlib.net/ | Necessary | Basic compression support | | bzip2 | https://www.bzip.org/downloads.html | Necessary | Alternative compression. You can pick this or zstd | | libpng | http://www.libpng.org/pub/png/ | Necessary | Image manipulation | | libSDL2 | http://www.libsdl.org/ | Necessary* | *On Linux & Mac. Optional but recommended for Windows. Graphics back-end | | libzstd | https://github.com/facebook/zstd | Optional | Alternative compression (larger save files than bzip2, but faster) | | libfreetype | http://www.freetype.org/ | Necessary | TrueType font support | | libminiupnpc | http://miniupnp.free.fr/ | Optional | Easy Server option | | libfluidsynth | https://www.fluidsynth.org/ | Optional | MIDI playback recommended on Linux & temporarily on Mac | | libSDL2_mixer | http://www.libsdl.org/ | Optional | Alternative MIDI playback and sound system | | libfontconfig | https://www.fontconfig.org/ | Optional | Font autodetection (Linux/Mac) | You will also need pkgconfig (Unix) or [vcpkg](https://github.com/Microsoft/vcpkg) (Microsoft Visual C++) - MSVC: Should compile the needed libraries automatically, if VCPKG is installed. Alternative, copy install-building-libs-{architecture}.bat to the vcpkg folder and run it. - MSYS2: Run [setup-mingw.sh] (tools/setup-mingw.sh) to get the libraries and set up the environment. - Ubuntu/Debian: Run [setup-debian.sh] (tools/setup-debian.sh) to get the libraries and set up the environment. - Linux: Use [pkgs.org](https://pkgs.org/) to search for development libraries available in your package manager. - Mac: Install libraries via [Homebrew] (https://brew.sh/). Some guidance can be found in the github directory. ### 2.3) Compiling Go to the source code directory of simutrans (simutrans/trunk if you downloaded from svn). You have three build systems to choose from: make, MSVC, and CMake. We recommend make or MSVC for debug builds, cmake for MacOS and Android. Compiling will give you only the executable, you still need a Simutrans installation to run the program. You can start simutrans with `-use_workdir` to point it to an existing installation. #### 2.3.1) Compiling with make The executable will be built in build/default. ``` (Linux) autoconf (MacOS) autoreconf -ivf ./configure make -j 4 (MacOS) make OSX/getversion ``` #### 2.3.2) Compiling with Microsoft Visual Studio Simutrans solution is a single solution file [simutrans.sln](./simutrans.sln) with 4 projects: - Simutrans-Main: The project that holds the shared, non back-end specific, files. All the followings use Main to build the specific back-end executables. - Simutrans SDL2: Preferred back-end for Simutrans. - Simutrans GDI: Windows-only back-end. - Simutrans Server: Server back-end with no graphical interface. Select on of them as startup project and in the configuration manager compile it. #### 2.3.3) Compiling with CMake The executable will be built in build/simutrans. ##### Commandline on Linux/MinGW/MacOS ... ``` cmake -G "Insert Correct Makefiles Generator" -B build . cmake --build build -j 4 ``` See [here](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) for a list of generators. ##### MSVC ``` mkdir build && cd build cmake.exe .. -G "Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE=[vcpkg-root]/scripts/buildsystems/vcpkg.cmake cmake --build . --config Release ``` ### 2.4) Cross-Compiling If you want to cross-compile Simutrans from Linux for Windows, see the [Cross-Compiling Simutrans](https://simutrans-germany.com/wiki/wiki/en_Cross-Compiling_Simutrans) wiki page. ## 3) Contribute You cand find general information about contributing to Simutrans in the [Development Index](https://simutrans-germany.com/wiki/wiki/en_Devel_Index?structure=en_Devel_Index) of the wiki. ### 3.1) Coding - If you want to contribute, read the coding guidelines in simutrans/documentation/coding_styles.txt - You definitely should check out the [Technical Documentation Sub-Forum](https://forum.simutrans.com/index.php/board,112.0.html) as well. - **Do not open Pull Requests** in GitHub. Use the [Patches & Projects](https://forum.simutrans.com/index.php/board,33.0.html) Sub-Forum instead. ### 3.2) Translating Simutrans is constantly updating and adding texts so we are always in need for translators: - To help with translation use the [SimuTranslator](https://translator.simutrans.com/) web tool. - To request a translator account use the [Translation Sub-Forum](https://forum.simutrans.com/index.php/board,47.0.html). ### 3.3) Painting Simutrans is always looking for artists! If you want to paint graphics for Simutrans, check: - The "Creating images" section of the [Development Index](https://simutrans-germany.com/wiki/wiki/en_Devel_Index). - The [General Resources and Tools](https://forum.simutrans.com/index.php/board,108.0.html) Sub-Forum. ### 3.4) Reporting bugs For bug reports use the [Bug Reports](https://forum.simutrans.com/index.php/board,8.0.html) Sub-Forum. ## 4) License Simutrans is licensed under the Artistic License version 1.0. The Artistic License 1.0 is an OSI-approved license which allows for use, distribution, modification, and distribution of modified versions, under the terms of the Artistic License 1.0. For the complete license text see [LICENSE.txt](./LICENSE.txt). Simutrans paksets (which are necessary to run the game) have their own license, but no one is included alongside this code. ## 5) Credits Simutrans was originally written by Hansjörg Malthaner "Hajo" from 1997 until he retired from development around 2004. Since then a team of contributors (The Simutrans Team) lead by Markus Pristovsek "Prissi" is developing Simutrans. A list of early contributors can be found in [simutrans/thanks.txt](./simutrans/thanks.txt) simutrans-124.5/Simutrans-GDI.vcxproj000066400000000000000000000240321520675460500175700ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 Stable Win32 Stable x64 {0621B295-BEB7-4767-82F1-F27995610323} Simutrans Simutrans GDI x86-windows-static x64-windows-static Application $(DefaultPlatformToolset) true true true <_ProjectFileVersion>10.0.30319.1 $(SolutionDir)build\GDI\ $(SolutionDir)build\GDI\$(Configuration)\ $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) $(ProjectName) Debug false $(ProjectName) Nightly false false true true true true x64-windows-static true x64-windows-static true x64-windows-static true Level3 true true 4250;4373;4800;4996;26812;26451 true false Windows true ProgramDatabase Disabled MSG_LEVEL=4;USE_ZSTD;USE_UPNP;COLOUR_DEPTH=16;MULTI_THREAD=1;MSG_LEVEL=3;DEBUG=3;REVISION_FROM_FILE;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP MultiThreaded EnableFastChecks kernel32.lib;user32.lib;gdi32.lib;shell32.lib;winmm.lib;advapi32.lib;ws2_32.lib;imm32.lib;Xaudio2.lib;iphlpapi.lib true false false true USE_ZSTD;USE_UPNP;COLOUR_DEPTH=16;MULTI_THREAD=1;MSG_LEVEL=3;NDEBUG;REVISION_FROM_FILE;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP USE_ZSTD;USE_UPNP;COLOUR_DEPTH=16;MULTI_THREAD=1;MSG_LEVEL=3;NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP tools/get_revision.bat Obtain SVN revision number true true true ProgramDatabase MaxSpeed true Speed MultiThreaded true true true true kernel32.lib;user32.lib;gdi32.lib;shell32.lib;winmm.lib;advapi32.lib;ws2_32.lib;imm32.lib;Xaudio2.lib;iphlpapi.lib false true true true simutrans-124.5/Simutrans-GDI.vcxproj.filters000066400000000000000000003214401520675460500212420ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Resource Files simutrans-124.5/Simutrans-Main.vcxitems000066400000000000000000002060261520675460500202250ustar00rootroot00000000000000 $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true {5a9777dd-ce40-45ae-8cb4-0ff2db4abd27} %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)src\simutrans\ simutrans-124.5/Simutrans-Main.vcxitems.filters000066400000000000000000003203521520675460500216730ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Resource Files simutrans-124.5/Simutrans-SDL2.vcxproj000066400000000000000000000236521520675460500177000ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 Stable Win32 Stable x64 {E74757E8-C2FD-44AD-87BD-3D55F4709484} Simutrans Simutrans SDL2 x86-windows-static x64-windows-static Application $(DefaultPlatformToolset) true true true <_ProjectFileVersion>10.0.30319.1 $(SolutionDir)build\SDL2\ $(SolutionDir)build\SDL2\$(Configuration)\ $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) $(ProjectName) Debug false $(ProjectName) Nightly false false true true true true Level3 true true 4250;4373;4800;4996;26812;26451 true false Windows true ProgramDatabase Disabled USE_ZSTD;USE_UPNP;COLOUR_DEPTH=16;MULTI_THREAD=1;MSG_LEVEL=3;DEBUG=3;REVISION_FROM_FILE;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP MultiThreaded EnableFastChecks kernel32.lib;user32.lib;shell32.lib;winmm.lib;advapi32.lib;ws2_32.lib;imm32.lib;iphlpapi.lib;OleAut32.lib;GDI32.lib;version.lib;Ole32.lib;SetupAPI.lib;urlmon.lib true false false true USE_ZSTD;USE_UPNP;COLOUR_DEPTH=16;MULTI_THREAD=1;MSG_LEVEL=3;NDEBUG;REVISION_FROM_FILE;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP USE_ZSTD;USE_UPNP;COLOUR_DEPTH=16;MULTI_THREAD=1;MSG_LEVEL=3;NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP tools/get_revision.bat Obtain SVN revision number true true false true true MaxSpeed true Speed MultiThreaded true true true true false false true true kernel32.lib;user32.lib;shell32.lib;winmm.lib;advapi32.lib;ws2_32.lib;imm32.lib;iphlpapi.lib;OleAut32.lib;GDI32.lib;version.lib;Ole32.lib;SetupAPI.lib;urlmon.lib false true true true true true simutrans-124.5/Simutrans-SDL2.vcxproj.filters000066400000000000000000000015041520675460500213370ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms simutrans-124.5/Simutrans-Server.vcxproj000066400000000000000000000226121520675460500204350ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 Stable Win32 Stable x64 {0EE7EB1C-43C6-4548-9C60-B79F4D781B4C} Simutrans Simutrans Server x86-windows-static x64-windows-static Application $(DefaultPlatformToolset) true <_ProjectFileVersion>10.0.30319.1 $(SolutionDir)build\Server\ $(SolutionDir)build\Server\$(Configuration)\ $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) $(ProjectName) Debug false $(ProjectName) Nightly false false true true true true true true true Level3 true true 4250;4373;4800;4996;26812;26451 true false Console MachineX86 true ProgramDatabase Disabled USE_ZSTD;COLOUR_DEPTH=0;MULTI_THREAD=1;MSG_LEVEL=3;DEBUG=3;REVISION_FROM_FILE;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP MultiThreadedDebugDLL EnableFastChecks kernel32.lib;user32.lib;shell32.lib;winmm.lib;advapi32.lib;ws2_32.lib;imm32.lib true false false true USE_ZSTD;COLOUR_DEPTH=0;MULTI_THREAD=1;MSG_LEVEL=3;NDEBUG;REVISION_FROM_FILE;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP USE_ZSTD;COLOUR_DEPTH=0;MULTI_THREAD=1;MSG_LEVEL=3;NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;WINVER=_WIN32_WINNT_WINXP tools/get_revision.bat Obtain SVN revision number true true true MaxSpeed true Speed MultiThreadedDLL true true true true kernel32.lib;user32.lib;shell32.lib;winmm.lib;advapi32.lib;ws2_32.lib;imm32.lib false true true true simutrans-124.5/Simutrans-Server.vcxproj.filters000066400000000000000000000015041520675460500221010ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms simutrans-124.5/Simutrans.sln000066400000000000000000000071661520675460500163010ustar00rootroot00000000000000Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.33424.131 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Simutrans-Main", "Simutrans-Main.vcxitems", "{5A9777DD-CE40-45AE-8CB4-0FF2DB4ABD27}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Simutrans GDI", "Simutrans-GDI.vcxproj", "{0621B295-BEB7-4767-82F1-F27995610323}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Simutrans SDL2", "Simutrans-SDL2.vcxproj", "{E74757E8-C2FD-44AD-87BD-3D55F4709484}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Simutrans Server", "Simutrans-Server.vcxproj", "{0EE7EB1C-43C6-4548-9C60-B79F4D781B4C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 Stable|x64 = Stable|x64 Stable|x86 = Stable|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0621B295-BEB7-4767-82F1-F27995610323}.Debug|x64.ActiveCfg = Debug|x64 {0621B295-BEB7-4767-82F1-F27995610323}.Debug|x64.Build.0 = Debug|x64 {0621B295-BEB7-4767-82F1-F27995610323}.Debug|x86.ActiveCfg = Debug|Win32 {0621B295-BEB7-4767-82F1-F27995610323}.Debug|x86.Build.0 = Debug|Win32 {0621B295-BEB7-4767-82F1-F27995610323}.Release|x64.ActiveCfg = Release|x64 {0621B295-BEB7-4767-82F1-F27995610323}.Release|x86.ActiveCfg = Release|Win32 {0621B295-BEB7-4767-82F1-F27995610323}.Release|x86.Build.0 = Release|Win32 {0621B295-BEB7-4767-82F1-F27995610323}.Stable|x64.ActiveCfg = Stable|x64 {0621B295-BEB7-4767-82F1-F27995610323}.Stable|x64.Build.0 = Stable|x64 {0621B295-BEB7-4767-82F1-F27995610323}.Stable|x86.ActiveCfg = Stable|Win32 {0621B295-BEB7-4767-82F1-F27995610323}.Stable|x86.Build.0 = Stable|Win32 {E74757E8-C2FD-44AD-87BD-3D55F4709484}.Debug|x64.ActiveCfg = Debug|x64 {E74757E8-C2FD-44AD-87BD-3D55F4709484}.Debug|x86.ActiveCfg = Debug|Win32 {E74757E8-C2FD-44AD-87BD-3D55F4709484}.Release|x64.ActiveCfg = Release|x64 {E74757E8-C2FD-44AD-87BD-3D55F4709484}.Release|x64.Build.0 = Release|x64 {E74757E8-C2FD-44AD-87BD-3D55F4709484}.Release|x86.ActiveCfg = Release|Win32 {E74757E8-C2FD-44AD-87BD-3D55F4709484}.Stable|x64.ActiveCfg = Stable|x64 {E74757E8-C2FD-44AD-87BD-3D55F4709484}.Stable|x86.ActiveCfg = Stable|Win32 {E74757E8-C2FD-44AD-87BD-3D55F4709484}.Stable|x86.Build.0 = Stable|Win32 {0EE7EB1C-43C6-4548-9C60-B79F4D781B4C}.Debug|x64.ActiveCfg = Debug|Win32 {0EE7EB1C-43C6-4548-9C60-B79F4D781B4C}.Debug|x86.ActiveCfg = Debug|Win32 {0EE7EB1C-43C6-4548-9C60-B79F4D781B4C}.Release|x64.ActiveCfg = Release|x64 {0EE7EB1C-43C6-4548-9C60-B79F4D781B4C}.Release|x86.ActiveCfg = Release|Win32 {0EE7EB1C-43C6-4548-9C60-B79F4D781B4C}.Stable|x64.ActiveCfg = Stable|x64 {0EE7EB1C-43C6-4548-9C60-B79F4D781B4C}.Stable|x86.ActiveCfg = Stable|Win32 {0EE7EB1C-43C6-4548-9C60-B79F4D781B4C}.Stable|x86.Build.0 = Stable|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {49739D12-4369-4D44-B614-A4C97AF7D06A} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution Simutrans-Main.vcxitems*{0621b295-beb7-4767-82f1-f27995610323}*SharedItemsImports = 4 Simutrans-Main.vcxitems*{0ee7eb1c-43c6-4548-9c60-b79f4d781b4c}*SharedItemsImports = 4 Simutrans-Main.vcxitems*{5a9777dd-ce40-45ae-8cb4-0ff2db4abd27}*SharedItemsImports = 9 Simutrans-Main.vcxitems*{e74757e8-c2fd-44ad-87bd-3d55f4709484}*SharedItemsImports = 4 EndGlobalSection EndGlobal simutrans-124.5/cmake/000077500000000000000000000000001520675460500146445ustar00rootroot00000000000000simutrans-124.5/cmake/FindCCache.cmake000066400000000000000000000016621520675460500175620ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # # - Find CCache compiler cache executable. # # This module defines the following variables: # CCache_FOUND - true if CCache was found. # CCache_EXECUTABLE - Path to CCache executable. # CCache_VERSION - Version string of CCache executable. # include(FindPackageHandleStandardArgs) find_program(CCache_EXECUTABLE ccache) if (CCache_EXECUTABLE) execute_process(COMMAND "${CCache_EXECUTABLE}" --version OUTPUT_VARIABLE CCache_VERSION_OUTPUT ) if (CCache_VERSION_OUTPUT MATCHES "version ([0-9]+\\.[0-9]+\\.[0-9]+)") set(CCache_VERSION "${CMAKE_MATCH_1}") endif () endif (CCache_EXECUTABLE) find_package_handle_standard_args(CCache FOUND_VAR CCache_FOUND REQUIRED_VARS CCache_EXECUTABLE VERSION_VAR CCache_VERSION ) if (CCache_FOUND OR NOT CCache_FIND_REQUIRED) mark_as_advanced(CCache_EXECUTABLE) endif () simutrans-124.5/cmake/FindMiniUPNP.cmake000066400000000000000000000043411520675460500200500ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # # Locate MiniUPNP library. # This module defines # MiniUPNP_FOUND, if miniupnp library and headers have been found # MiniUPNP_LIBRARY, the miniupnp variant # MiniUPNP_INCLUDE_DIR, where to find miniupnpc.h and family) # MiniUPNP_VERSION, the API version of MiniUPNP # if (MiniUPNP_INCLUDE_DIR AND MiniUPNP_LIBRARY) # Already in cache, be silent set(MiniUPNP_FIND_QUIETLY TRUE) endif () find_path(MiniUPNP_INCLUDE_DIR miniupnpc.h HINTS $ENV{MINIUPNP_INCLUDE_DIR} PATH_SUFFIXES miniupnpc ) find_library(MiniUPNP_LIBRARY miniupnpc HINTS $ENV{MINIUPNP_LIBRARY} ) find_library(MiniUPNP_STATIC_LIBRARY libminiupnpc.a HINTS $ENV{MINIUPNP_STATIC_LIBRARY} ) set(MiniUPNP_INCLUDE_DIRS ${MiniUPNP_INCLUDE_DIR}) set(MiniUPNP_LIBRARIES ${MiniUPNP_LIBRARY}) set(MiniUPNP_STATIC_LIBRARIES ${MiniUPNP_STATIC_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( MiniUPNP DEFAULT_MSG MiniUPNP_LIBRARY MiniUPNP_INCLUDE_DIR ) if (MiniUPNP_FOUND) file(STRINGS "${MiniUPNP_INCLUDE_DIR}/miniupnpc.h" MiniUPNP_API_VERSION_STR REGEX "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+[0-9]+") if (MiniUPNP_API_VERSION_STR MATCHES "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+([0-9]+)") set(MiniUPNP_API_VERSION "${CMAKE_MATCH_1}") endif() if (MiniUPNP_API_VERSION GREATER_EQUAL 10 AND NOT MiniUPNP_FIND_QUIETLY) message(STATUS "Found MiniUPNP API version ${MiniUPNP_API_VERSION}") endif() endif() mark_as_advanced(MiniUPNP_INCLUDE_DIR MiniUPNP_LIBRARY MiniUPNP_STATIC_LIBRARY) if (MiniUPNP_FOUND) if (NOT TARGET MiniUPNP::MiniUPNP) add_library(MiniUPNP::MiniUPNP UNKNOWN IMPORTED) endif (NOT TARGET MiniUPNP::MiniUPNP) set_target_properties(MiniUPNP::MiniUPNP PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${MiniUPNP_INCLUDE_DIRS}") set_target_properties(MiniUPNP::MiniUPNP PROPERTIES IMPORTED_LOCATION "${MiniUPNP_LIBRARIES}") if (MINGW) # MinGW needs an additional link flags set_property(TARGET MiniUPNP::MiniUPNP PROPERTY INTERFACE_LINK_LIBRARIES "-liphlpapi -lws2_32") endif (MINGW) if (MSVC) set_property(TARGET MiniUPNP::MiniUPNP PROPERTY INTERFACE_LINK_LIBRARIES "iphlpapi.lib") endif (MSVC) endif (MiniUPNP_FOUND) simutrans-124.5/cmake/MacBundle.cmake000066400000000000000000000025331520675460500175030ustar00rootroot00000000000000# Set copyright text string(TIMESTAMP YEAR "%Y") set(COPYRIGHT "Copyright 1997-${YEAR} by the Simutrans Team") # Get version number file(READ ${SOURCE_DIR}/src/simutrans/simversion.h VERSION_FILE) string(REGEX MATCH "VERSION_MAJOR ([0-9]*)" _ ${VERSION_FILE}) set(VERSION ${CMAKE_MATCH_1}) # Copy the icon file set(ICON ${SOURCE_DIR}/src/OSX/simutrans.icns) target_sources(simutrans PRIVATE ${ICON}) set_source_files_properties(${ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") # Bundle information set_target_properties(simutrans PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME simutrans MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION} MACOSX_BUNDLE_COPYRIGHT ${COPYRIGHT} MACOSX_BUNDLE_GUI_IDENTIFIER org.simutrans.simutrans MACOSX_BUNDLE_ICON_FILE simutrans MACOSX_BUNDLE_INFO_STRING "Simutrans ${VERSION}, ${COPYRIGHT}" MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION} ) # Change the install directory from /usr/local to the build directory, so it's easier to package. set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}) if (SIMUTRANS_STEAM_BUILT) install(FILES ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/osx/libsteam_api.dylib DESTINATION ${CMAKE_BINARY_DIR}/simutrans/simutrans.app/Contents/MacOS) endif () install(CODE " include(BundleUtilities) fixup_bundle(\"${CMAKE_BINARY_DIR}/simutrans/simutrans.app\" \"\" \"\") ") simutrans-124.5/cmake/SimutransBackend.cmake000066400000000000000000000010511520675460500211000ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # if (SDL2_FOUND AND Freetype_FOUND) list(APPEND AVAILABLE_BACKENDS "sdl2") mark_as_advanced(SDL2_DIR) endif () if (WIN32 AND Freetype_FOUND) list(APPEND AVAILABLE_BACKENDS "gdi") endif () list(APPEND AVAILABLE_BACKENDS "none") string(REGEX MATCH "^[^;][^;]*" FIRST_BACKEND "${AVAILABLE_BACKENDS}") set(SIMUTRANS_BACKEND "${FIRST_BACKEND}" CACHE STRING "Graphics backend") set_property(CACHE SIMUTRANS_BACKEND PROPERTY STRINGS ${AVAILABLE_BACKENDS}) simutrans-124.5/cmake/SimutransCompileOptions.cmake000066400000000000000000000161351520675460500225260ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # if (CCache_FOUND) option(SIMUTRANS_USE_CCACHE "Use CCache compiler cache to improve recompilation speed" ON) if (SIMUTRANS_USE_CCACHE) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCache_EXECUTABLE}") set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCache_EXECUTABLE}") endif (SIMUTRANS_USE_CCACHE) endif (CCache_FOUND) option(SIMUTRANS_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF) option(SIMUTRANS_SANITIZE_ADDRESS "Build with Address Santitizer support (-fsanitize=address)" OFF) if (NOT MSVC) option(SIMUTRANS_SANITIZE_UNDEFINED "Build with Undefined Behaviour Sanitizer support (-fsanitize=undefined)" OFF) option(SIMUTRANS_SANITIZE_THREAD "Build with Thread Sanitizer support (-fsanitize=thread)" OFF) endif () option(SIMUTRANS_VALGRIND_SUPPORT "Add support for valgrind \"memcheck\" tool" OFF) if (CMAKE_USE_PTHREADS_INIT) option(SIMUTRANS_MULTI_THREAD "Use multiple threads for drawing" ON) else (CMAKE_USE_PTHREADS_INIT) set(SIMUTRANS_MULTI_THREAD OFF) endif (CMAKE_USE_PTHREADS_INIT) if (NOT CMAKE_SIZEOF_VOID_P EQUAL 4) option(SIMUTRANS_BUILD_32BIT "Build 32 or 64 bit executable" OFF) endif () if (MiniUPNP_FOUND) option(SIMUTRANS_USE_UPNP "Use MiniUPNP for easier server setup" ON) endif (MiniUPNP_FOUND) if (ZSTD_FOUND) option(SIMUTRANS_USE_ZSTD "Enable support for zstd save file compression (larger save files than bzip2, but faster)" ON) endif (ZSTD_FOUND) if (FluidSynth_FOUND AND NOT WIN32) option(SIMUTRANS_USE_FLUIDSYNTH_MIDI "Enable FluidSynth for MIDI playback" ON) endif (FluidSynth_FOUND AND NOT WIN32) if(Fontconfig_FOUND) option(SIMUTRANS_USE_FONTCONFIG "Use Fontconfig for font autodetection" ON) endif() option(SIMUTRANS_INSTALL_PAK64 "Download pak64 on install" OFF) option(SIMUTRANS_UPDATE_LANGFILES "Update language files from the translator on install" OFF) option(SIMUTRANS_ENABLE_PROFILING "Enable profiling code" OFF) option(SIMUTRANS_USE_SYSLOG "Enable logging to syslog" OFF) option(SIMUTRANS_USE_IP4_ONLY "Use only IPv4" OFF) option(SIMUTRANS_STEAM_BUILT "Compile a Steam build" OFF) option(DEBUG_FLUSH_BUFFER "Highlite areas changes since last redraw" OFF) option(ENABLE_WATERWAY_SIGNS "Allow private signs on watersways" OFF) option(AUTOJOIN_PUBLIC "Join when making things public" OFF) option(SIMUTRANS_USE_REVISION "Use the given revision number" OFF) option(SIMUTRANS_USE_OWN_PAKINSTALL "Use built-in pakset installer instead of scripted" OFF) if(NOT SIMUTRANS_DEBUG_LEVEL) set(SIMUTRANS_DEBUG_LEVEL $) endif () if(NOT SIMUTRANS_MSG_LEVEL) set(SIMUTRANS_MSG_LEVEL 3 CACHE STRING "Message verbosity level") endif () set_property(CACHE SIMUTRANS_MSG_LEVEL PROPERTY STRINGS 0 1 2 3 4) if(OPTION_BUNDLE_LIBRARIES AND UNIX AND NOT APPLE) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) set(CMAKE_INSTALL_RPATH "\$ORIGIN/lib") # otherwise RUNPATH will be set instead of RPATH, which can lead to issues add_link_options("-Wl,--disable-new-dtags") endif() include(CheckCXXCompilerFlag) # # This function adds all supported compiler flags to result_list # Example: SIMUTRANS_CHECK_CXX_COMPILER_FLAGS(COMMON_COMPILE_OPTIONS -Wall -Wextra -Werror) # will add -Wall -Wextra -Werror to COMMON_COMPILE_OPTIONS. # function(SIMUTRANS_CHECK_CXX_COMPILER_FLAGS result_list) set(temp_list "") foreach (flag ${ARGN}) # We cannot check for -Wno-* or -fno-* as this won't throw a warning so we must check for -W* or -f* directly string(REGEX REPLACE "^(-[Wf])no-" "\\1" sanitizedFlag ${flag}) set(cachedVarName ${sanitizedFlag}) string(REPLACE "+" "X" cachedVarName ${cachedVarName}) string(REGEX REPLACE "[-=]" "_" cachedVarName ${cachedVarName}) if (NOT ${CMAKE_CXX_COMPILER_ID}_${cachedVarName}_CHECKED) check_cxx_compiler_flag(${sanitizedFlag} CXX_FLAG_${cachedVarName}_SUPPORTED) set(${CMAKE_CXX_COMPILER_ID}_${cachedVarName}_CHECKED YES CACHE INTERNAL "") endif() if (CXX_FLAG_${cachedVarName}_SUPPORTED) list(APPEND temp_list ${flag}) endif (CXX_FLAG_${cachedVarName}_SUPPORTED) unset(cachedVarName) unset(sanitizedFlag) endforeach () if (NOT ${result_list}) set(${result_list} ${temp_list} PARENT_SCOPE) elseif (temp_list) set(${result_list} "${${result_list}};${temp_list}" PARENT_SCOPE) endif () endfunction() if (MSVC) SIMUTRANS_CHECK_CXX_COMPILER_FLAGS(SIMUTRANS_COMMON_COMPILE_OPTIONS /W3 4250;4373;4800;4996;26812;26451 /wd4244 # C4244: 'conversion_type': conversion from 'type1' to 'type2', possible loss of data /wd4267 # C4267: '=': conversion from 'type1' to 'type2', possible loss of data /wd4068 # C4068: unknown pragma /wd4250 # C4250: same name in derived class belong to derived class (silly) /wd26812 # Prefer 'enum class' over 'enum' (silly) /wd26451 # Arithmetic overflow: Using operator 'operator' on a size-a byte value and then casting the result to a size-b byte value. (widely used in finances) /wdD9025 # /MT # static multithreded libaries /MP # parallel builds ) set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach() add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_SCL_SECURE_NO_WARNINGS) add_definitions(-DNOMINMAX) add_definitions(-DWIN32_LEAN_AND_MEAN) if (SIMUTRANS_WARNINGS_AS_ERRORS) add_compile_options(/WX) endif () if (CMAKE_SIZEOF_VOID_P EQUAL 4) add_link_options(/LARGEADDRESSAWARE) endif () if (SIMUTRANS_SANITIZE_ADDRESS) add_compile_options(/fsanitize=address) add_link_options(/fsanitize=address) endif () else (MSVC) # Assume GCC/Clang SIMUTRANS_CHECK_CXX_COMPILER_FLAGS(SIMUTRANS_COMMON_COMPILE_OPTIONS -Wall -Wextra -Wformat=2 -Wundef -Wmissing-include-dirs -Wcast-qual -Wpointer-arith -Wcast-align -Wduplicated-cond ) SIMUTRANS_CHECK_CXX_COMPILER_FLAGS(SIMUTRANS_COMMON_COMPILE_OPTIONS -Wno-format-nonliteral # Mostly for translator -Wno-overloaded-virtual # For makeobj -Wno-deprecated-declarations # auto_ptr for squirrel -Wno-deprecated-copy # for squirrel -Wno-cast-align # for squirrel -Wno-return-std-move # for squirrel ) if (SIMUTRANS_WARNINGS_AS_ERRORS) add_compile_options(-Werror) endif () # Only add for MinGW 32 bit buids if (WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4) add_link_options(-Wl,--large-address-aware) endif () if (SIMUTRANS_SANITIZE_ADDRESS) add_compile_options( -fsanitize=address -fno-sanitize-recover=all ) add_link_options(-fsanitize=address) endif () if (SIMUTRANS_SANITIZE_UNDEFINED) SIMUTRANS_CHECK_CXX_COMPILER_FLAGS(SIMUTRANS_COMMON_COMPILE_OPTIONS -fsanitize=undefined -fno-sanitize-recover=all -fno-sanitize=shift -fno-sanitize=function # not supported by GCC ) add_link_options(-fsanitize=undefined) endif () if (SIMUTRANS_SANITIZE_THREAD) add_compile_options(-fsanitize=thread -fno-sanitize-recover=all) add_link_options(-fsanitize=thread) endif () if (SIMUTRANS_PROFILE) SIMUTRANS_CHECK_CXX_COMPILER_FLAGS(SIMUTRANS_COMMON_COMPILE_OPTIONS -pg -fno-inline -fno-schedule-insns ) endif (SIMUTRANS_PROFILE) endif (MSVC) simutrans-124.5/cmake/SimutransFindDependencies.cmake000066400000000000000000000156751520675460500227610ustar00rootroot00000000000000if (NOT ANDROID) find_package(CCache) find_package(ZLIB REQUIRED) find_package(BZip2 REQUIRED) find_package(PNG REQUIRED) find_package(MiniUPNP) find_package(Fontconfig) if (MSVC) find_package(ZSTD) find_package(SDL2) find_package(Freetype) find_package(FluidSynth) else () find_package(PkgConfig MODULE REQUIRED) pkg_check_modules(ZSTD REQUIRED IMPORTED_TARGET libzstd) pkg_check_modules(SDL2 IMPORTED_TARGET sdl2) pkg_check_modules(Freetype IMPORTED_TARGET freetype2) pkg_check_modules(FluidSynth IMPORTED_TARGET fluidsynth>=2.1.0) endif () set(CMAKE_THREAD_PREFER_PTHREAD ON) find_package(Threads) else () if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(PREBUILT_DIR "debug") else () set(PREBUILT_DIR "release") endif () include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../zlib/) add_library(ZLIB::ZLIB SHARED IMPORTED) set(SHARED_LIBRARY_SO ${CMAKE_CURRENT_SOURCE_DIR}/../zlib/prebuilt/${PREBUILT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libz.so) set_target_properties(ZLIB::ZLIB PROPERTIES IMPORTED_LOCATION ${SHARED_LIBRARY_SO}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../bzip2/) add_library(BZip2::BZip2 SHARED IMPORTED) set(SHARED_LIBRARY_SO ${CMAKE_CURRENT_SOURCE_DIR}/../bzip2/prebuilt/${PREBUILT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libbz2.so) set_target_properties(BZip2::BZip2 PROPERTIES IMPORTED_LOCATION ${SHARED_LIBRARY_SO}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../libpng/) add_library(PNG::PNG SHARED IMPORTED) if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(SHARED_LIBRARY_SO ${CMAKE_CURRENT_SOURCE_DIR}/../libpng/prebuilt/${PREBUILT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libpng16d.so) else () set(SHARED_LIBRARY_SO ${CMAKE_CURRENT_SOURCE_DIR}/../libpng/prebuilt/release/${CMAKE_ANDROID_ARCH_ABI}/libpng16.so) endif () set_target_properties(PNG::PNG PROPERTIES IMPORTED_LOCATION ${SHARED_LIBRARY_SO}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../zstd/lib/) add_library(ZSTD::ZSTD SHARED IMPORTED) set(SHARED_LIBRARY_SO ${CMAKE_CURRENT_SOURCE_DIR}/../zstd/prebuilt/${PREBUILT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libzstd.so) set_target_properties(ZSTD::ZSTD PROPERTIES IMPORTED_LOCATION ${SHARED_LIBRARY_SO}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../freetype/include/) add_library(Freetype::Freetype SHARED IMPORTED) if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(SHARED_LIBRARY_SO ${CMAKE_CURRENT_SOURCE_DIR}/../freetype/prebuilt/${PREBUILT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libfreetyped.so) else () set(SHARED_LIBRARY_SO ${CMAKE_CURRENT_SOURCE_DIR}/../freetype/prebuilt/${PREBUILT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libfreetype.so) endif () set_target_properties(Freetype::Freetype PROPERTIES IMPORTED_LOCATION ${SHARED_LIBRARY_SO}) find_library(SDL2 SDL2) # Fluidsynth dependencies are a big family # We may be able to reduce the family numbers by compiling Fluidsynth ourselves # But such process is long and tedious (see: https://github.com/FluidSynth/fluidsynth/blob/master/.azure/azure-pipelines-android.yml ) # For now we use the release libraries include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/include/) find_package(OpenMP REQUIRED) add_library(libfluidsynth SHARED IMPORTED) set(SHARED_LIBRARY_SO ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libfluidsynth.so) set_target_properties(libfluidsynth PROPERTIES IMPORTED_LOCATION ${SHARED_LIBRARY_SO}) add_library(libFLAC SHARED IMPORTED) set_target_properties(libFLAC PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libFLAC.so) add_library(libfluidsynth-assetloader SHARED IMPORTED) set_target_properties(libfluidsynth-assetloader PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libfluidsynth-assetloader.so) add_library(libgio-2.0 SHARED IMPORTED) set_target_properties(libgio-2.0 PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libgio-2.0.so) add_library(libglib-2.0 SHARED IMPORTED) set_target_properties(libglib-2.0 PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libglib-2.0.so) add_library(libgmodule-2.0 SHARED IMPORTED) set_target_properties(libgmodule-2.0 PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libgmodule-2.0.so) add_library(libgobject-2.0 SHARED IMPORTED) set_target_properties(libgobject-2.0 PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libgobject-2.0.so) add_library(libgthread-2.0 SHARED IMPORTED) set_target_properties(libgthread-2.0 PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libgthread-2.0.so) add_library(libinstpatch-1.0 SHARED IMPORTED) set_target_properties(libinstpatch-1.0 PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libinstpatch-1.0.so) add_library(liboboe SHARED IMPORTED) set_target_properties(liboboe PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/liboboe.so) add_library(libogg SHARED IMPORTED) set_target_properties(libogg PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libogg.so) add_library(libopus SHARED IMPORTED) set_target_properties(libopus PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libopus.so) add_library(libpcre SHARED IMPORTED) set_target_properties(libpcre PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libpcre.so) add_library(libpcreposix SHARED IMPORTED) set_target_properties(libpcreposix PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libpcreposix.so) add_library(libsndfile SHARED IMPORTED) set_target_properties(libsndfile PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libsndfile.so) add_library(libvorbis SHARED IMPORTED) set_target_properties(libvorbis PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libvorbis.so) add_library(libvorbisenc SHARED IMPORTED) set_target_properties(libvorbisenc PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libvorbisenc.so) add_library(libvorbisfile SHARED IMPORTED) set_target_properties(libvorbisfile PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../fluidsynth/lib/${CMAKE_ANDROID_ARCH_ABI}/libvorbisfile.so) add_library(fluidsynth INTERFACE) target_link_libraries(fluidsynth INTERFACE libFLAC libfluidsynth-assetloader libgio-2.0 libglib-2.0 libgmodule-2.0 libgobject-2.0 libgthread-2.0 libinstpatch-1.0 liboboe libogg libopus libpcre libpcreposix libsndfile libvorbis libvorbisenc libvorbisfile libfluidsynth OpenMP::OpenMP_CXX ) endif () simutrans-124.5/cmake/SimutransGenerateMetainfo.cmake000066400000000000000000000023171520675460500227740ustar00rootroot00000000000000# gather informations to update flatpak and Android creation files STRING(STRIP ${SIMUTRANS_WC_REVISION} SIMUTRANS_WC_REVISION) file(READ "${SOURCE_DIR}/src/simutrans/simversion.h" FILE_CONTENT) string(REGEX MATCH "#define SIM_VERSION_MAJOR[ ]+([0-9]*)" _ ${FILE_CONTENT}) set(ver_major ${CMAKE_MATCH_1}) string(REGEX MATCH "#define SIM_VERSION_MINOR[ ]+([0-9]*)" _ ${FILE_CONTENT}) set(ver_minor ${CMAKE_MATCH_1}) string(REGEX MATCH "#define SIM_VERSION_PATCH[ ]+([0-9]*)" _ ${FILE_CONTENT}) set(ver_patch ${CMAKE_MATCH_1}) string(REGEX MATCH "#define[ ]+SIM_VERSION_BUILD[ ]+SIM_BUILD_RELEASE[\r\n]+" RELEASE_FLAG ${FILE_CONTENT}) string(TIMESTAMP TODAY "%Y-%m-%d") # finally, update flatpak xml if (RELEASE_FLAG) set(nightly_suffix "") else () set(nightly_suffix " nightly") endif () configure_file( "${SOURCE_DIR}/src/linux/com.simutrans.Simutrans.metainfo.xml.in" "${SOURCE_DIR}/src/linux/com.simutrans.Simutrans.metainfo.xml" @ONLY ) # and Android files if (RELEASE_FLAG) set(nightly_suffix "-Release") else () set(nightly_suffix "-Nightly") endif () configure_file( "${SOURCE_DIR}/src/android/AndroidAppSettings.cfg.in" "${SOURCE_DIR}/src/android/AndroidAppSettings.cfg" @ONLY )simutrans-124.5/cmake/SimutransInstall.cmake000066400000000000000000000132721520675460500211670ustar00rootroot00000000000000if (APPLE) # Self-contained bundle set(SIMUTRANS_BASE_DIR "${CMAKE_BINARY_DIR}/simutrans/simutrans.app/Contents/Resources/simutrans") set(SIMUTRANS_BIN_DIR "${CMAKE_BINARY_DIR}/simutrans") set(SIMUTRANS_OUTPUT_DIR "") elseif (UNIX AND NOT OPTION_BUNDLE_LIBRARIES AND NOT SINGLE_INSTALL) # System Installation (Linux only) include(GNUInstallDirs) if (USE_GAMES_DATADIR) set(SIMUTRANS_BASE_DIR "${CMAKE_INSTALL_DATADIR}/games/simutrans") else () set(SIMUTRANS_BASE_DIR "${CMAKE_INSTALL_DATADIR}/simutrans") endif () set(SIMUTRANS_BIN_DIR "${CMAKE_INSTALL_BINDIR}") set(SIMUTRANS_OUTPUT_DIR "${CMAKE_INSTALL_PREFIX}") install(FILES ${CMAKE_SOURCE_DIR}/src/simutrans/simutrans.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) install(FILES ${CMAKE_SOURCE_DIR}/src/linux/simutrans.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) install(FILES ${CMAKE_SOURCE_DIR}/src/linux/com.simutrans.Simutrans.metainfo.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) else () # Portable installation set(SIMUTRANS_BASE_DIR "${CMAKE_BINARY_DIR}/simutrans") set(SIMUTRANS_BIN_DIR "${CMAKE_BINARY_DIR}/simutrans") set(SIMUTRANS_OUTPUT_DIR "") endif () install(TARGETS simutrans RUNTIME DESTINATION "${SIMUTRANS_BIN_DIR}" BUNDLE DESTINATION "${SIMUTRANS_BIN_DIR}") install(DIRECTORY "${CMAKE_SOURCE_DIR}/simutrans/" DESTINATION ${SIMUTRANS_BASE_DIR} REGEX "get_pak.sh" EXCLUDE) # # Download language files # if (SIMUTRANS_UPDATE_LANGFILES) if (MSVC) # MSVC has no variable on the install target path at execution time, which is why we expand the directories at creation time! install(CODE "execute_process(COMMAND powershell -ExecutionPolicy Bypass -File ${CMAKE_SOURCE_DIR}/tools/get_lang_files.ps1 WORKING_DIRECTORY ${SIMUTRANS_OUTPUT_DIR}/${SIMUTRANS_BASE_DIR}/..)") else () install(CODE "execute_process(COMMAND sh ${CMAKE_SOURCE_DIR}/tools/get_lang_files.sh WORKING_DIRECTORY ${SIMUTRANS_OUTPUT_DIR}/${SIMUTRANS_BASE_DIR}/.. )") endif () endif () # # Pak installer # if (NOT WIN32) install(FILES "${CMAKE_SOURCE_DIR}/tools/get_pak.sh" DESTINATION "${SIMUTRANS_BASE_DIR}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) else () # NSIS must be installed manually in the path with the right addons if(MINGW) install(CODE "execute_process(COMMAND makensis onlineupgrade.nsi WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/Windows/nsis)") else () install(CODE "execute_process(COMMAND cmd /k \"$ENV{ProgramFiles\(x86\)}/NSIS/makensis.exe\" onlineupgrade.nsi WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/Windows/nsis)") endif () install(FILES "${CMAKE_SOURCE_DIR}/src/Windows/nsis/download-paksets.exe" DESTINATION "${SIMUTRANS_BASE_DIR}") endif () # # Install pak64 if requested or needed # if (SIMUTRANS_INSTALL_PAK64) if (MSVC) install(CODE "if(NOT EXISTS ${SIMUTRANS_BASE_DIR}/pak) execute_process(COMMAND powershell -Command \"Remove-Item \'${SIMUTRANS_BASE_DIR}/pak\' -Recurse\" WORKING_DIRECTORY ${SIMUTRANS_OUTPUT_DIR}/${SIMUTRANS_BASE_DIR}) file(STRINGS ${CMAKE_SOURCE_DIR}/src/simutrans/paksetinfo.h URLpak64 REGEX \"/pak64/\") string( REGEX REPLACE \"^.[\\t ]*\\\"\" \"\" URLpak64 \${URLpak64}) string( REGEX REPLACE \"\\\", .*\$\" \"\" URLpak64 \${URLpak64}) message(\"install pak to \" ${SIMUTRANS_BASE_DIR}) execute_process(COMMAND powershell -ExecutionPolicy Bypass -File ${CMAKE_SOURCE_DIR}/tools/get_pak.ps1 \${URLpak64} WORKING_DIRECTORY ${SIMUTRANS_OUTPUT_DIR}/${SIMUTRANS_BASE_DIR}) endif () ") else () # install pak64 with the bundle install(CODE "file(STRINGS ${CMAKE_SOURCE_DIR}/src/simutrans/paksetinfo.h URLpak64 REGEX \"/pak64/\") string( REGEX REPLACE \"^.[\\t ]*\\\"\" \"\" URLpak64 \${URLpak64}) string( REGEX REPLACE \"\\\", .*\$\" \"\" URLpak64 \${URLpak64}) execute_process(COMMAND sh ${CMAKE_SOURCE_DIR}/tools/get_pak.sh \${URLpak64} WORKING_DIRECTORY ${SIMUTRANS_OUTPUT_DIR}/${SIMUTRANS_BASE_DIR}) ") endif () endif () # # Bundle libraries on linux if requested # if (OPTION_BUNDLE_LIBRARIES AND UNIX AND NOT APPLE) # Steam Runtime already includes some of our libraries if (SIMUTRANS_STEAM_BUILT) install(CODE [[ file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR DEPENDENCIES EXECUTABLES "${CMAKE_BINARY_DIR}/simutrans/simutrans" PRE_EXCLUDE_REGEXES "libSDL2*|libz.so*|libfreetype*|libpng*|libglib*|libogg*|libpcre*|libvorbis*|libfontconfig*|libpulse*|libsteam_api.so*" POST_EXCLUDE_REGEXES "ld-linux|libc.so|libdl.so|libm.so|libgcc_s.so|libpthread.so|libstdc...so|libgomp.so") ]]) else () install(CODE [[ file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR DEPENDENCIES EXECUTABLES "${CMAKE_BINARY_DIR}/simutrans/simutrans" PRE_EXCLUDE_REGEXES "libSDL2*|libz.so*|libfontconfig*|libpulse*" POST_EXCLUDE_REGEXES "ld-linux|libc.so|libdl.so|libm.so|libgcc_s.so|libpthread.so|libstdc...so|libgomp.so") ]]) endif () install(CODE [[ file(INSTALL DESTINATION "${CMAKE_BINARY_DIR}/simutrans/lib" FILES ${DEPENDENCIES} FOLLOW_SYMLINK_CHAIN) ]]) endif () # # Include steam library (for some reason it is not done automagically as others) # if (SIMUTRANS_STEAM_BUILT) if(MSVC AND CMAKE_GENERATOR_PLATFORM MATCHES "Win32") install(FILES ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/steam_api.dll DESTINATION ${CMAKE_BINARY_DIR}/simutrans) elseif(MSVC AND CMAKE_GENERATOR_PLATFORM MATCHES "x64") install(FILES ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/win64/steam_api64.dll DESTINATION ${CMAKE_BINARY_DIR}/simutrans) elseif(UNIX AND NOT APPLE) # For Apple it was already done in MacBundle.cmake install(FILES ${CMAKE_SOURCE_DIR}/sdk/redistributable_bin/linux64/libsteam_api.so DESTINATION ${CMAKE_BINARY_DIR}/simutrans/lib) endif() endif()simutrans-124.5/cmake/SimutransRevision.cmake000066400000000000000000000061671520675460500213640ustar00rootroot00000000000000# # This file is part of the Simutrans project under the artistic licence. # (see licence.txt) # find_package(Subversion QUIET) find_package(Git QUIET) # assume this is from simutrans/simutrans! if (Git_FOUND) execute_process(WORKING_DIRECTORY "${SOURCE_DIR}" COMMAND "${GIT_EXECUTABLE}" log -1 RESULT_VARIABLE res_var OUTPUT_VARIABLE SIMUTRANS_RAW_WC_REVISION ) if (res_var EQUAL 0) message( "git log -1 ok:" ${SIMUTRANS_RAW_WC_REVISION}) string( REGEX REPLACE "[\t\r\n]" " " TEMP1 ${SIMUTRANS_RAW_WC_REVISION}) string( REGEX REPLACE "^.*trunk\@" "" TEMP2 ${TEMP1}) string( REGEX REPLACE " .*$" "" SIMUTRANS_WC_REVISION ${TEMP2}) if(SIMUTRANS_WC_REVISION MATCHES "commit") unset(SIMUTRANS_WC_REVISION) else () execute_process(WORKING_DIRECTORY "${SOURCE_DIR}" COMMAND "${GIT_EXECUTABLE}" rev-parse --short=7 HEAD RESULT_VARIABLE res_var OUTPUT_VARIABLE SIMUTRANS_WC_HASH ) endif() endif() endif () if (NOT SIMUTRANS_WC_REVISION AND Subversion_FOUND) execute_process(WORKING_DIRECTORY "${SOURCE_DIR}" COMMAND svn info --show-item revision RESULT_VARIABLE res_var OUTPUT_VARIABLE SIMUTRANS_WC_REVISION ERROR_VARIABLE dummy ) if (res_var) # SVN not found => no result unset(SIMUTRANS_WC_REVISION) endif () endif () # Fallback for git commits not submitted to svn (e.g. when using git-svn) if (NOT SIMUTRANS_WC_REVISION AND Git_FOUND) execute_process(WORKING_DIRECTORY "${SOURCE_DIR}" COMMAND "${GIT_EXECUTABLE}" rev-list --count --first-parent HEAD RESULT_VARIABLE res_var OUTPUT_VARIABLE SIMUTRANS_WC_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE ) if (res_var) # not a git repository unset(SIMUTRANS_WC_REVISION) else () # the number of commit +328 equals the revision ... MATH(EXPR res_var "${res_var}+328") execute_process(WORKING_DIRECTORY "${SOURCE_DIR}" COMMAND "${GIT_EXECUTABLE}" rev-parse --short=7 HEAD RESULT_VARIABLE res_var OUTPUT_VARIABLE SIMUTRANS_WC_HASH ) endif () endif () if (SIMUTRANS_WC_REVISION) # write a file with the SVNVERSION define if (NOT SIMUTRANS_WC_HASH) file(WRITE revision.h.txt "#define REVISION ${SIMUTRANS_WC_REVISION}\n") message(STATUS "Compiling Simutrans revision ${SIMUTRANS_WC_REVISION} without hash ...") else () file(WRITE revision.h.txt "#define REVISION ${SIMUTRANS_WC_REVISION}\n#define GIT_HASH 0x${SIMUTRANS_WC_HASH}\n") message(STATUS "Compiling Simutrans revision ${SIMUTRANS_WC_REVISION} with hash ${SIMUTRANS_WC_HASH} ...") endif () # copy the file to the final header only if the version changes # reduces needless rebuilds execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different revision.h.txt "${SOURCE_DIR}/src/simutrans/revision.h") include(${SOURCE_DIR}/cmake/SimutransGenerateMetainfo.cmake) else () message(WARNING "Could not find revision information because this repository " "is neither a Subversion nor a Git repository. Revision information " "will be unavailable. You can set the SIMUTRANS_USE_REVISION option " "to manually specify a revision number") if (NOT EXISTS "${CMAKE_SOURCE_DIR}/revision.h") file(WRITE "${CMAKE_SOURCE_DIR}/revision.h" "#define REVISION \n") endif () endif () simutrans-124.5/cmake/SimutransSourceList.cmake000066400000000000000000000325651520675460500216630ustar00rootroot00000000000000target_sources(simutrans PRIVATE src/simutrans/builder/brueckenbauer.cc src/simutrans/builder/fabrikbauer.cc src/simutrans/builder/goods_manager.cc src/simutrans/builder/hausbauer.cc src/simutrans/builder/tree_builder.cc src/simutrans/builder/tunnelbauer.cc src/simutrans/builder/vehikelbauer.cc src/simutrans/builder/wegbauer.cc src/simutrans/dataobj/crossing_logic.cc src/simutrans/dataobj/environment.cc src/simutrans/dataobj/freelist.cc src/simutrans/dataobj/gameinfo.cc src/simutrans/dataobj/height_map_loader.cc src/simutrans/dataobj/koord.cc src/simutrans/dataobj/koord3d.cc src/simutrans/dataobj/loadsave.cc src/simutrans/dataobj/marker.cc src/simutrans/dataobj/objlist.cc src/simutrans/dataobj/pakset_manager.cc src/simutrans/dataobj/pakset_downloader.cc src/simutrans/dataobj/powernet.cc src/simutrans/dataobj/records.cc src/simutrans/dataobj/rect.cc src/simutrans/dataobj/ribi.cc src/simutrans/dataobj/route.cc src/simutrans/dataobj/scenario.cc src/simutrans/dataobj/schedule.cc src/simutrans/dataobj/settings.cc src/simutrans/dataobj/sve_cache.cc src/simutrans/dataobj/tabfile.cc src/simutrans/dataobj/translator.cc src/simutrans/descriptor/bridge_desc.cc src/simutrans/descriptor/building_desc.cc src/simutrans/descriptor/factory_desc.cc src/simutrans/descriptor/goods_desc.cc src/simutrans/descriptor/ground_desc.cc src/simutrans/descriptor/image.cc src/simutrans/descriptor/obj_base_desc.cc src/simutrans/descriptor/reader/bridge_reader.cc src/simutrans/descriptor/reader/building_reader.cc src/simutrans/descriptor/reader/citycar_reader.cc src/simutrans/descriptor/reader/crossing_reader.cc src/simutrans/descriptor/reader/factory_reader.cc src/simutrans/descriptor/reader/good_reader.cc src/simutrans/descriptor/reader/ground_reader.cc src/simutrans/descriptor/reader/groundobj_reader.cc src/simutrans/descriptor/reader/image_reader.cc src/simutrans/descriptor/reader/imagelist2d_reader.cc src/simutrans/descriptor/reader/imagelist_reader.cc src/simutrans/descriptor/reader/obj_reader.cc src/simutrans/descriptor/reader/pedestrian_reader.cc src/simutrans/descriptor/reader/roadsign_reader.cc src/simutrans/descriptor/reader/root_reader.cc src/simutrans/descriptor/reader/sim_reader.cc src/simutrans/descriptor/reader/skin_reader.cc src/simutrans/descriptor/reader/sound_reader.cc src/simutrans/descriptor/reader/text_reader.cc src/simutrans/descriptor/reader/tree_reader.cc src/simutrans/descriptor/reader/tunnel_reader.cc src/simutrans/descriptor/reader/vehicle_reader.cc src/simutrans/descriptor/reader/way_obj_reader.cc src/simutrans/descriptor/reader/way_reader.cc src/simutrans/descriptor/reader/xref_reader.cc src/simutrans/descriptor/sound_desc.cc src/simutrans/descriptor/tunnel_desc.cc src/simutrans/descriptor/vehicle_desc.cc src/simutrans/descriptor/way_desc.cc src/simutrans/display/font.cc src/simutrans/display/simview.cc src/simutrans/display/simgraph.cc src/simutrans/display/viewport.cc src/simutrans/freight_list_sorter.cc src/simutrans/ground/boden.cc src/simutrans/ground/brueckenboden.cc src/simutrans/ground/fundament.cc src/simutrans/ground/grund.cc src/simutrans/ground/monorailboden.cc src/simutrans/ground/tunnelboden.cc src/simutrans/ground/wasser.cc src/simutrans/gui/components/gui_aligned_container.cc src/simutrans/gui/components/gui_building.cc src/simutrans/gui/components/gui_button.cc src/simutrans/gui/components/gui_button_to_chart.cc src/simutrans/gui/components/gui_chart.cc src/simutrans/gui/components/gui_colorbox.cc src/simutrans/gui/components/gui_combobox.cc src/simutrans/gui/components/gui_component.cc src/simutrans/gui/components/gui_container.cc src/simutrans/gui/components/gui_convoiinfo.cc src/simutrans/gui/components/gui_divider.cc src/simutrans/gui/components/gui_fixedwidth_textarea.cc src/simutrans/gui/components/gui_flowtext.cc src/simutrans/gui/components/gui_image.cc src/simutrans/gui/components/gui_image_list.cc src/simutrans/gui/components/gui_label.cc src/simutrans/gui/components/gui_map_preview.cc src/simutrans/gui/components/gui_numberinput.cc src/simutrans/gui/components/gui_obj_view.cc src/simutrans/gui/components/gui_schedule.cc src/simutrans/gui/components/gui_scrollbar.cc src/simutrans/gui/components/gui_scrolled_list.cc src/simutrans/gui/components/gui_scrollpane.cc src/simutrans/gui/components/gui_speedbar.cc src/simutrans/gui/components/gui_tab_panel.cc src/simutrans/gui/components/gui_textarea.cc src/simutrans/gui/components/gui_textinput.cc src/simutrans/gui/components/gui_timeinput.cc src/simutrans/gui/components/gui_waytype_tab_panel.cc src/simutrans/gui/components/gui_world_view.cc src/simutrans/gui/ai_option.cc src/simutrans/gui/ai_selector.cc src/simutrans/gui/banner.cc src/simutrans/gui/base_info.cc src/simutrans/gui/baum_edit.cc src/simutrans/gui/city_info.cc src/simutrans/gui/citybuilding_edit.cc src/simutrans/gui/citylist_frame.cc src/simutrans/gui/citylist_stats.cc src/simutrans/gui/climates.cc src/simutrans/gui/chat_frame.cc src/simutrans/gui/convoi_detail.cc src/simutrans/gui/convoi_filter_frame.cc src/simutrans/gui/convoi_frame.cc src/simutrans/gui/convoi_info.cc src/simutrans/gui/convoy_item.cc src/simutrans/gui/curiosity_edit.cc src/simutrans/gui/curiositylist_frame.cc src/simutrans/gui/curiositylist_stats.cc src/simutrans/gui/depot_frame.cc src/simutrans/gui/depotlist_frame.cc src/simutrans/gui/display_settings.cc src/simutrans/gui/enlarge_map_frame.cc src/simutrans/gui/extend_edit.cc src/simutrans/gui/fabrik_info.cc src/simutrans/gui/factory_chart.cc src/simutrans/gui/factory_edit.cc src/simutrans/gui/factorylist_frame.cc src/simutrans/gui/factorylist_stats.cc src/simutrans/gui/goods_frame.cc src/simutrans/gui/goods_stats.cc src/simutrans/gui/ground_info.cc src/simutrans/gui/groundobj_edit.cc src/simutrans/gui/gui_frame.cc src/simutrans/gui/gui_theme.cc src/simutrans/gui/halt_info.cc src/simutrans/gui/halt_list_filter_frame.cc src/simutrans/gui/halt_list_frame.cc src/simutrans/gui/halt_list_stats.cc src/simutrans/gui/headquarter_info.cc src/simutrans/gui/help_frame.cc src/simutrans/gui/jump_frame.cc src/simutrans/gui/kennfarbe.cc src/simutrans/gui/label_info.cc src/simutrans/gui/labellist_frame.cc src/simutrans/gui/labellist_stats.cc src/simutrans/gui/line_item.cc src/simutrans/gui/line_management_gui.cc src/simutrans/gui/load_relief_frame.cc src/simutrans/gui/loadfont_frame.cc src/simutrans/gui/loadsave_frame.cc src/simutrans/gui/map_frame.cc src/simutrans/gui/message_frame.cc src/simutrans/gui/message_option.cc src/simutrans/gui/message_stats.cc src/simutrans/gui/messagebox.cc src/simutrans/gui/minimap.cc src/simutrans/gui/money_frame.cc src/simutrans/gui/obj_info.cc src/simutrans/gui/optionen.cc src/simutrans/gui/pakinstaller.cc src/simutrans/gui/pakselector.cc src/simutrans/gui/password_frame.cc src/simutrans/gui/player_frame.cc src/simutrans/gui/player_ranking_frame.cc src/simutrans/gui/privatesign_info.cc src/simutrans/gui/savegame_frame.cc src/simutrans/gui/scenario_frame.cc src/simutrans/gui/scenario_info.cc src/simutrans/gui/schedule_list.cc src/simutrans/gui/script_generator_frame.cc src/simutrans/gui/script_tool_frame.cc src/simutrans/gui/server_frame.cc src/simutrans/gui/settings_frame.cc src/simutrans/gui/settings_stats.cc src/simutrans/gui/signal_info.cc src/simutrans/gui/signal_spacing.cc src/simutrans/gui/simwin.cc src/simutrans/gui/sound_frame.cc src/simutrans/gui/sprachen.cc src/simutrans/gui/station_building_select.cc src/simutrans/gui/themeselector.cc src/simutrans/gui/tool_selector.cc src/simutrans/gui/trafficlight_info.cc src/simutrans/gui/vehiclelist_frame.cc src/simutrans/gui/welt.cc src/simutrans/io/classify_file.cc src/simutrans/io/raw_image.cc src/simutrans/io/raw_image_bmp.cc src/simutrans/io/raw_image_png.cc src/simutrans/io/raw_image_ppm.cc src/simutrans/io/rdwr/adler32_stream.cc src/simutrans/io/rdwr/bzip2_file_rdwr_stream.cc src/simutrans/io/rdwr/compare_file_rd_stream.cc src/simutrans/io/rdwr/raw_file_rdwr_stream.cc src/simutrans/io/rdwr/rdwr_stream.cc src/simutrans/io/rdwr/zlib_file_rdwr_stream.cc src/simutrans/network/checksum.cc src/simutrans/network/memory_rw.cc src/simutrans/network/network.cc src/simutrans/network/network_address.cc src/simutrans/network/network_cmd.cc src/simutrans/network/network_cmd_ingame.cc src/simutrans/network/network_cmd_scenario.cc src/simutrans/network/network_cmp_pakset.cc src/simutrans/network/network_file_transfer.cc src/simutrans/network/network_packet.cc src/simutrans/network/network_socket_list.cc src/simutrans/network/pakset_info.cc src/simutrans/obj/baum.cc src/simutrans/obj/bruecke.cc src/simutrans/obj/crossing.cc src/simutrans/obj/depot.cc src/simutrans/obj/field.cc src/simutrans/obj/gebaeude.cc src/simutrans/obj/groundobj.cc src/simutrans/obj/label.cc src/simutrans/obj/leitung2.cc src/simutrans/obj/pillar.cc src/simutrans/obj/roadsign.cc src/simutrans/obj/signal.cc src/simutrans/obj/simobj.cc src/simutrans/obj/tunnel.cc src/simutrans/obj/way/kanal.cc src/simutrans/obj/way/maglev.cc src/simutrans/obj/way/monorail.cc src/simutrans/obj/way/narrowgauge.cc src/simutrans/obj/way/runway.cc src/simutrans/obj/way/schiene.cc src/simutrans/obj/way/strasse.cc src/simutrans/obj/way/weg.cc src/simutrans/obj/wayobj.cc src/simutrans/obj/wolke.cc src/simutrans/obj/zeiger.cc src/simutrans/old_blockmanager.cc src/simutrans/player/ai.cc src/simutrans/player/ai_goods.cc src/simutrans/player/ai_passenger.cc src/simutrans/player/ai_scripted.cc src/simutrans/player/finance.cc src/simutrans/player/simplay.cc src/simutrans/revision.h src/simutrans/script/api/api_city.cc src/simutrans/script/api/api_command.cc src/simutrans/script/api/api_const.cc src/simutrans/script/api/api_control.cc src/simutrans/script/api/api_convoy.cc src/simutrans/script/api/api_factory.cc src/simutrans/script/api/api_gui.cc src/simutrans/script/api/api_halt.cc src/simutrans/script/api/api_include.cc src/simutrans/script/api/api_line.cc src/simutrans/script/api/api_map_objects.cc src/simutrans/script/api/api_obj_desc.cc src/simutrans/script/api/api_obj_desc_base.cc src/simutrans/script/api/api_pathfinding.cc src/simutrans/script/api/api_player.cc src/simutrans/script/api/api_scenario.cc src/simutrans/script/api/api_schedule.cc src/simutrans/script/api/api_settings.cc src/simutrans/script/api/api_simple.cc src/simutrans/script/api/api_tiles.cc src/simutrans/script/api/api_world.cc src/simutrans/script/api/export_desc.cc src/simutrans/script/api/get_next.cc src/simutrans/script/api_class.cc src/simutrans/script/api_function.cc src/simutrans/script/api_param.cc src/simutrans/script/dynamic_string.cc src/simutrans/script/export_objs.cc src/simutrans/script/script.cc src/simutrans/script/script_loader.cc src/simutrans/script/script_tool_manager.cc src/simutrans/simachievements.cc src/simutrans/simconvoi.cc src/simutrans/simdebug.cc src/simutrans/simevent.cc src/simutrans/simfab.cc src/simutrans/simhalt.cc src/simutrans/siminteraction.cc src/simutrans/simintr.cc src/simutrans/simio.cc src/simutrans/simline.cc src/simutrans/simlinemgmt.cc src/simutrans/simloadingscreen.cc src/simutrans/simmain.cc src/simutrans/simmem.cc src/simutrans/simmesg.cc src/simutrans/simskin.cc src/simutrans/simsound.cc src/simutrans/simticker.cc src/simutrans/simware.cc src/simutrans/sys/simsys.cc src/simutrans/tool/simmenu.cc src/simutrans/tool/simtool-script-generator.cc src/simutrans/tool/simtool-scripted.cc src/simutrans/tool/simtool.cc src/simutrans/utils/cbuffer.cc src/simutrans/utils/checklist.cc src/simutrans/utils/csv.cc src/simutrans/utils/log.cc src/simutrans/utils/searchfolder.cc src/simutrans/utils/sha1.cc src/simutrans/utils/sha1_hash.cc src/simutrans/utils/simrandom.cc src/simutrans/utils/simstring.cc src/simutrans/utils/simthread.cc src/simutrans/utils/unicode.cc src/simutrans/vehicle/air_vehicle.cc src/simutrans/vehicle/movingobj.cc src/simutrans/vehicle/pedestrian.cc src/simutrans/vehicle/rail_vehicle.cc src/simutrans/vehicle/road_vehicle.cc src/simutrans/vehicle/simroadtraffic.cc src/simutrans/vehicle/vehicle.cc src/simutrans/vehicle/vehicle_base.cc src/simutrans/vehicle/water_vehicle.cc src/simutrans/world/placefinder.cc src/simutrans/world/simcity.cc src/simutrans/world/simplan.cc src/simutrans/world/simworld.cc src/simutrans/world/surface.cc src/simutrans/world/terraformer.cc src/squirrel/sq_extensions.cc src/squirrel/sqstdlib/sqstdaux.cc src/squirrel/sqstdlib/sqstdblob.cc src/squirrel/sqstdlib/sqstdio.cc src/squirrel/sqstdlib/sqstdmath.cc src/squirrel/sqstdlib/sqstdrex.cc src/squirrel/sqstdlib/sqstdstream.cc src/squirrel/sqstdlib/sqstdstring.cc src/squirrel/sqstdlib/sqstdsystem.cc src/squirrel/squirrel/sqapi.cc src/squirrel/squirrel/sqbaselib.cc src/squirrel/squirrel/sqclass.cc src/squirrel/squirrel/sqcompiler.cc src/squirrel/squirrel/sqdebug.cc src/squirrel/squirrel/sqfuncstate.cc src/squirrel/squirrel/sqlexer.cc src/squirrel/squirrel/sqmem.cc src/squirrel/squirrel/sqobject.cc src/squirrel/squirrel/sqstate.cc src/squirrel/squirrel/sqtable.cc src/squirrel/squirrel/sqvm.cc ) simutrans-124.5/cmake/SimutransVcpkgTriplet.cmake000066400000000000000000000055051520675460500221770ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # set the correct triplet when compiling on MSVC using VCPKG # # MSVC variable is not defined until the first call to project if (CMAKE_GENERATOR MATCHES "Visual Studio.*" OR CMAKE_GENERATOR MATCHES "Ninja" AND WIN32) if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "CMAKE_TOOLCHAIN_FILE") elseif (NOT DEFINED CMAKE_TOOLCHAIN_FILE) set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/build/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "CMAKE_TOOLCHAIN_FILE") endif () endif () # to built static libaries on vcpkg (exists on Linux too), we need to modify the VCPKG triplet (only if vcpkg) STRING(TOLOWER "${CMAKE_TOOLCHAIN_FILE}" CMAKE_TOOLCHAIN_FILE_LC) if (CMAKE_TOOLCHAIN_FILE_LC MATCHES ".*vcpkg.cmake") if (CMAKE_GENERATOR MATCHES "Visual Studio.*" AND "${CMAKE_GENERATOR_PLATFORM}" STREQUAL "") # default is 32bit for now message( WARNING "No architecture givem => assume x86 build") set(CMAKE_GENERATOR_PLATFORM "Win32" CACHE STRING "" FORCE) set(VCPKG_TARGET_TRIPLET "x86-windows-static" CACHE STRING "Default target is 32bit static builds" FORCE) endif () if (DEFINED ENV{VCPKG_DEFAULT_TRIPLET} AND NOT DEFINED VCPKG_TARGET_TRIPLET) set(VCPKG_TARGET_TRIPLET "$ENV{VCPKG_DEFAULT_TRIPLET}" CACHE STRING "") endif () # if not defined, then guess if (NOT DEFINED VCPKG_TARGET_TRIPLET) if (DEFINED CMAKE_INSTALL_PREFIX) if (CMAKE_INSTALL_PREFIX MATCHES ".*x86-.*") set(VCPKG_TARGET_TRIPLET "x86-windows-static" CACHE STRING "Default target is 32bit static builds") elseif (CMAKE_INSTALL_PREFIX MATCHES ".*arm64-.*") set(VCPKG_TARGET_TRIPLET "arm64-windows-static" CACHE STRING "Default target is 64bit arm static builds") else () set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "Default target is 64bit static builds") endif () elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*") if (CMAKE_GENERATOR_PLATFORM MATCHES "Win32") set(VCPKG_TARGET_TRIPLET "x86-windows-static" CACHE STRING "Default target is 32bit static builds") elseif (CMAKE_GENERATOR_PLATFORM MATCHES "ARM64") set(VCPKG_TARGET_TRIPLET "arm64-windows-static" CACHE STRING "Default target is 64bit arm static builds") else () set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "Default target is 64bit static builds") endif () else () message(FATAL_ERROR "Please specify VCPKG triplet!") endif () endif () message( "-- VCPKG: triplet=" ${VCPKG_TARGET_TRIPLET} " platform=" ${CMAKE_GENERATOR_PLATFORM}) else () if (CMAKE_GENERATOR MATCHES "Visual Studio.*" OR CMAKE_GENERATOR MATCHES "Ninja" AND WIN32) message(WARNING "CMake will fail without setting CMAKE_TOOLCHAIN_FILE!") endif () endif () simutrans-124.5/common.mk000066400000000000000000000025411520675460500154070ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # OBJS := $(patsubst %, $(BUILDDIR)/%.o, $(basename $(SOURCES))) DEPS := $(OBJS:%.o=%.d) DIRS := $(sort $(dir $(OBJS))) # Make build directories DUMMY := $(shell mkdir -p $(DIRS)) BUILDCONFIG_FILES := common.mk Makefile config.$(CFG) .PHONY: all clean .SUFFIXES: .rc ifeq ($(VERBOSE),) Q := @ else Q := endif simutrans: $(PROGDIR)/$(PROG) $(PROGDIR)/$(PROG): $(OBJS) @echo "===> LD $@" $(Q)$(HOSTCXX) $(OBJS) $(LDFLAGS) $(LIBS) -o $(PROGDIR)/$(PROG) -include $(DEPS) # Silence stale header dependency errors %.h: @true $(BUILDDIR)/%.o: %.mm $(BUILDCONFIG_FILES) @echo "===> Obj-c OSX $<" $(Q)$(HOSTCXX) $(CXXFLAGS) $(OBJCFLAGS) -c -MMD -o $@ $< $(BUILDDIR)/%.o: %.m $(BUILDCONFIG_FILES) @echo "===> Obj-c OSX $<" $(Q)$(HOSTCXX) $(CXXFLAGS) $(OBJCFLAGS) -c -MMD -o $@ $< $(BUILDDIR)/%.o: %.c $(BUILDCONFIG_FILES) @echo "===> HOSTCC $<" $(Q)$(HOSTCC) $(CCFLAGS) -c -MMD -o $@ $< $(BUILDDIR)/%.o: %.cc $(BUILDCONFIG_FILES) @echo "===> HOSTCXX $<" $(Q)$(HOSTCXX) $(CXXFLAGS) -c -MMD -o $@ $< $(BUILDDIR)/%.o: %.rc $(BUILDCONFIG_FILES) @echo "===> RES $<" # $(Q)$(WINDRES) --preprocessor "$(HOSTCXX) -E -xc -DRC_INVOKED -MMD -MF $(@:%.o=%.d) -MT $@" -O COFF $< $@ $(Q)$(WINDRES) --preprocessor "$(HOSTCXX) -E -xc -DRC_INVOKED -MMD -MT $@" -O COFF $< $@ simutrans-124.5/config.default.in000066400000000000000000000071531520675460500170120ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # this is config.default.in #BACKEND := gdi #BACKEND := sdl2 #BACKEND := posix BACKEND = @backend@ #OSTYPE := amiga #OSTYPE := beos #OSTYPE := freebsd #OSTYPE := haiku #OSTYPE := linux #OSTYPE := mac #OSTYPE := mingw #OSTYPE := openbsd OSTYPE = @os_type@ #DEBUG := 1 # Level 1-3, higher number means more debug-friendly but slower, see Makefile MSG_LEVEL := 3 # Level 1-4, more runtime debug messages (without only warnings and errors) OPTIMISE := 1 # Add umpteen optimisation flags #PROFILE := 1 # Enable profiling #PROFILE := 2 # Enable profiling with optimisation flags, can be used with `OPTIMISE = 1' STATIC := 1 # Enable static linkage (to be at least somewhat portable), Freetype and SDL only for non-MinGW OSTYPE. AV_FOUNDATION := @av_foundation@ # Use AVFoundation instead of QTKit. If you are building on macOS 10.12 or later, this must be enabled. WITH_REVISION := @svn@ # adds the revision from svn; required for networkgames # if you do not use SVN, add -DREVISION="1234" to the FLAGS below #WIN32_CONSOLE := 1 # adds a console for windows debugging MULTI_THREAD := @multithread@ # Enable multithreading, highly recommended BUNDLE_PTHREADGC2 := @bundlepthread@ # using UPnP for easy server hosting behind routers USE_UPNP := @upnp@ # using fontconfig for linux/mac TTF selection USE_FONTCONFIG := @fontconfig@ # using zstd compression USE_ZSTD := @zstd@ # using FluidSynth for MIDI playback (SDL2 backend needed for Linux/MacOS, SDL2 or GDI for MinGW) USE_FLUIDSYNTH_MIDI := @fluidsynth@ # enable link time optimizations #LTO := 1 # Define these as empty strings, if you don't have the respective config program #ALLEGRO_CONFIG := #PNG_CONFIG := #SDL_CONFIG := #SDL2_CONFIG := #FREETYPE_CONFIG := # Or define as the config program if you do #ALLEGRO_CONFIG := allegro-config #PNG_CONFIG := pkg-config libpng #SDL_CONFIG := sdl-config #SDL2_CONFIG := sdl2-config #FREETYPE_CONFIG := freetype-config #VERBOSE := 1 # The following useful conditional compilation flags exist # # Needed to compile: # SIM_BIG_ENDIAN: MUST be set for PPC/Motorola byte order! (old mac, amiga) # NO_INTPTR_T: must be set if intptr_t is not available # # Changing appearance: # USE_SOFTPOINTER: emulate mouse pointer (set automatically in Makefile) # # Useful for debugging: # DEBUG_ROUTES: show routing calculation information in minimap # SHOW_FORE_GRUND: show which objects are drawn as foreground # DEBUG_FLUSH_BUFFER: show the dirty areas on the screen # USE_VALGRIND_MEMCHECK: make valgrind-memcheck aware of the memory allocation stuff in dataobj/freelist # SYSLOG: send debug output to syslog # # Following flags alter game engine (and are off for standard builds) # OTTD_LIKE: Enables half height tiles and crossconnects all industries # AUTOMATIC_BRIDGES and AUTOMATIC_TUNNELS: will be built also for player # AUTOJOIN_PUBLIC: stations next to a public stop will be joined to it # MAX_CHOOSE_BLOCK_TILES=xxx: maximum distance between choose signal and a target (undefined means no limit) # DESTINATION_CITYCARS: Citycars can have a destination (not recommended) # # In order to use the flags, add a line like this: (-Dxxx) # FLAGS := -DREVISION="1234" FLAGS := @endian@ # Output directories: # # use this put objects file in same directory, where the sources are (not recommended): # ... otherwise defaults to 'build/default') # # BUILDDIR := $(shell pwd) # # use this to specify the target directory for the executable: # .. otherwise defaults to BUILDDIR # MAKEOBJ_PROGDIR := $(shell pwd) NETTOOL_PROGDIR := $(shell pwd) PROGDIR := $(shell pwd) simutrans-124.5/config.template000066400000000000000000000071241520675460500165720ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # # to compile: # copy this file to config.default and adjust the settings # #BACKEND := gdi #BACKEND := sdl2 #BACKEND := posix #OSTYPE := amiga #OSTYPE := freebsd #OSTYPE := haiku #OSTYPE := linux #OSTYPE := mac #OSTYPE := mingw #OSTYPE := openbsd #DEBUG := 1 # Level 1-3, higher number means more debug-friendly but slower, see Makefile #MSG_LEVEL := 3 # Level 1-4, more runtime debug messages (without only warnings and errors) #OPTIMISE := 1 # Add umpteen optimisation flags #PROFILE := 1 # Enable profiling #PROFILE := 2 # Enable profiling with optimisation flags, can be used with `OPTIMISE = 1' #STATIC := 1 # Enable static linkage, but not SDL2 for non-MinGW OSTYPE. #AV_FOUNDATION := 1 # Use AVFoundation instead of QTKit. If you are building on macOS 10.12 or later, this must be enabled. #WITH_REVISION := 1 # adds the revision from svn; required for networkgames # if you do not use SVN, add -DREVISION="1234" to the FLAGS below #WIN32_CONSOLE := 1 # adds a console for windows debugging #MULTI_THREAD := 1 # Enable multithreading # using freetype for Truetype font support #USE_FREETYPE := 1 # using UPnP for easy server hosting behind routers #USE_UPNP := 1 # Enable support for zstd save file compression (larger save files than bzip2, but faster) #USE_ZSTD := 1 # use fontconfig to find default font (does not work for windows) #USE_FONTCONFIG := 0 # enable link time optimizations #LTO := 1 # using FluidSynth for MIDI playback (SDL2 backend needed for Linux/MacOS, SDL2 or GDI for MinGW) #USE_FLUIDSYNTH_MIDI := 1 # Define these as empty strings, if you don't have the respective config program #ALLEGRO_CONFIG := #PNG_CONFIG := #SDL_CONFIG := #SDL2_CONFIG := #FREETYPE_CONFIG := # Or define as the config program if you do #ALLEGRO_CONFIG := allegro-config #PNG_CONFIG := pkg-config libpng #SDL_CONFIG := sdl-config #SDL2_CONFIG := sdl2-config #FREETYPE_CONFIG := freetype-config #VERBOSE := 1 # The following useful conditional compilation flags exist # # Needed to compile: # SIM_BIG_ENDIAN: MUST be set for PPC/Motorola byte order! (old mac, amiga) # NO_INTPTR_T: must be set if intptr_t is not available # # Changing appearance: # USE_SOFTPOINTER: emulate mouse pointer (set automatically in Makefile) # # Useful for debugging: # DEBUG_ROUTES: show routing calculation information in minimap # SHOW_FORE_GRUND: show which objects are drawn as foreground # DEBUG_FLUSH_BUFFER: show the dirty areas on the screen # USE_VALGRIND_MEMCHECK: make valgrind-memcheck aware of the memory allocation stuff in dataobj/freelist # SYSLOG: send debug output to syslog # # Following flags alter game engine (and are off for standard builds) # OTTD_LIKE: Enables half height tiles and crossconnects all industries # AUTOMATIC_BRIDGES and AUTOMATIC_TUNNELS: will be built also for player # AUTOJOIN_PUBLIC: stations next to a public stop will be joined to it # MAX_CHOOSE_BLOCK_TILES=xxx: maximum distance between choose signal and a target (undefined means no limit) # DESTINATION_CITYCARS: Citycars can have a destination (not recommended) # # In order to use the flags, add a line like this: (-Dxxx) # FLAGS := -DREVISION="1234" # Output directories: # # use this put objects file in same directory, where the sources are (not recommended): # ... otherwise defaults to 'build/default') # # BUILDDIR := $(shell pwd) # # use this to specify the target directory for the executable: # .. otherwise defaults to BUILDDIR # # MAKEOBJ_PROGDIR := $(shell pwd) # NETTOOL_PROGDIR := $(shell pwd) # PROGDIR := $(shell pwd) simutrans-124.5/configure.ac000066400000000000000000000073771520675460500160700ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # simutrans.ac AC_INIT([simutrans],[version-0.2]) AC_PROG_CC AC_LANG(C++) # architecture AC_C_BIGENDIAN([AC_SUBST(endian, '-DSIM_BIG_ENDIAN')], [AC_SUBST(endian, '')], [AC_SUBST(endian, '')], [AC_SUBST(endian, '')]) # missing libs AC_CHECK_LIB(png, png_read_image, [], [AC_MSG_WARN([libpng is missing! Makeobj will not compile!])] ) AC_CHECK_LIB(bz2, BZ2_bzReadOpen, [], [AC_MSG_WARN([libbz2 is missing! Only zlib compressed saves will be available.])] ) # optional (but highly recommended) multithreading AC_SEARCH_LIBS(pthread_mutex_destroy, pthread, [AC_SUBST(multithread, 1)], [AC_SUBST(multithread, 0)] ) AC_CHECK_LIB(pthreadGC2, pthread_mutex_destroy, [AC_SUBST(bundlepthread, 1)], [AC_SUBST(bundlepthread, 0)] ) # find OS and backend by libs ... AC_CHECK_LIB(SDL2, SDL_GetRenderDriverInfo) AC_CHECK_HEADERS(windows.h) AC_CHECK_HEADERS(LocaleRoster.h) # optional zstd AC_SEARCH_LIBS(ZSTD_CStreamInSize, zstd, [AC_SUBST(zstd, 1)], [AC_SUBST(zstd, 0)] ) # hackish detection of OS ... if test "$ac_cv_header_windows_h" == yes then AC_SUBST(os_type, mingw) AC_SUBST(av_foundation, 0) # optional upnp AC_SEARCH_LIBS(upnpDiscover, miniupnpc, [AC_SUBST(upnp, 1)], [AC_SUBST(upnp, 0)], -liphlpapi -lws2_32 ) # optional fluidsynth AC_SEARCH_LIBS(new_fluid_settings, fluidsynth, [AC_SUBST(fluidsynth, 1)], [AC_SUBST(fluidsynth, 0)], -lglib-2.0 -lintl -liconv -ldsound -lole32) else # optional upnp AC_SEARCH_LIBS(upnpDiscover, miniupnpc, [AC_SUBST(upnp, 1)], [AC_SUBST(upnp, 0)] ) # optional fontconfig AC_SEARCH_LIBS(FcInitLoadConfigAndFonts, fontconfig, [AC_SUBST(fontconfig, 1)], [AC_SUBST(fontconfig, 0)], -lfontconfig ) # optional fluidsynth AC_SEARCH_LIBS(new_fluid_settings, fluidsynth, [AC_SUBST(fluidsynth, 1)], [AC_SUBST(fluidsynth, 0)] ) # optional fluidsynth AC_SEARCH_LIBS(FcInit, fontconfig, [AC_SUBST(fontconfig, 1)], [AC_SUBST(fontconfig, 0)] ) if uname | grep "Darwin" then AC_LANG_PUSH(Objective C++) AC_CHECK_HEADERS(AVFoundation/AVFoundation.h) AC_CHECK_HEADERS(QTKit/QTMovie.h) AC_LANG_POP(Objective C++) if test "$ac_cv_header_AVFoundation_AVFoundation_h" == yes then AC_SUBST(av_foundation, 1) else AC_SUBST(av_foundation, 0) if test "$ac_cv_header_QTKit_QTMovie_h" != yes then AC_MSG_ERROR([Neither AVFoundation nor QTKit are available as a sound driver. Simutrans will not compile!]) fi fi AC_SUBST(os_type, mac) else # Mac stuff AC_SUBST(av_foundation, 0) if test "$ac_cv_header_LocaleRoster_h" == yes then AC_SUBST(os_type, haiku) elif uname | grep "Linux" then AC_SUBST(os_type, linux) elif uname | grep "BSD" then AC_SUBST(os_type, freebsd) elif uname | grep "miga" then AC_SUBST(os_type, amiga) else AC_MSG_ERROR([Unknow OS!]) fi fi fi # and backend ... AC_ARG_ENABLE([server], [AS_HELP_STRING( [--enable-server], [Builds a server without graphics])], [], [enable_server=no]) # first test if forced as server if test "x$enable_server" != "xno" then AC_SUBST(backend, posix) elif test "$ac_cv_header_windows_h" == yes then AC_SUBST(backend, gdi) AC_MSG_WARN([Using GDI backend!]) AC_SEARCH_LIBS(FT_Init_FreeType, freetype, [], [AC_MSG_ERROR([FreeType Library is needed for graphical interface!])]) elif test "$ac_cv_lib_SDL2_SDL_GetRenderDriverInfo" == yes then AC_SUBST(backend, sdl2) AC_MSG_WARN([Using SDL2 backend!]) AC_SEARCH_LIBS(FT_Init_FreeType, freetype, [], [AC_MSG_ERROR([FreeType Library is needed for graphical interface!])]) else AC_SUBST(backend, posix) AC_MSG_WARN([No backend found, using server (posix)!]) fi # are we in a svn? if svn info then AC_SUBST(svn, 1) else AC_SUBST(svn, 0) fi AC_CONFIG_FILES([config.default]) AC_OUTPUT simutrans-124.5/documentation/000077500000000000000000000000001520675460500164355ustar00rootroot00000000000000simutrans-124.5/documentation/Doxyfile.in000066400000000000000000000137031520675460500205540ustar00rootroot00000000000000# Doxyfile 1.12.0 # This file describes the settings to be used by the documentation system # Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). # # Note: # # Use Doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] # Use Doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = "Simutrans" PROJECT_ICON = "@CMAKE_CURRENT_SOURCE_DIR@/src/simutrans/simutrans.svg" OUTPUT_DIRECTORY = "@CMAKE_CURRENT_BINARY_DIR@/documentation/simutrans/" OUTPUT_LANGUAGE = English ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the OPTIMIZE_OUTPUT_FOR_C = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = YES EXTRACT_LOCAL_METHODS = YES SORT_GROUP_NAMES = YES SORT_BY_SCOPE_NAME = YES #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/src/" \ "@CMAKE_CURRENT_SOURCE_DIR@/documentation/simutrans_doc.h" FILE_PATTERNS = *.c \ *.cc \ *.h \ *.mm RECURSIVE = YES # Simutrans Squirrel API has its own documentation definition EXCLUDE = "@CMAKE_CURRENT_SOURCE_DIR@/src/simutrans/script/" \ "@CMAKE_CURRENT_SOURCE_DIR@/src/external/" #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = YES #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to Sqlite3 output #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Configuration options related to diagram generator tools #--------------------------------------------------------------------------- HAVE_DOT = @DOXYGEN_HAVE_DOT@ DOT_PATH = "@graphviz_dot_dir@" simutrans-124.5/documentation/DoxyfileSQAPI.in000066400000000000000000000024541520675460500213530ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # # Doxygen file to generate documentation for Squirrel API # Assumes Doxygen is started in src/simutrans/script/api/ # PROJECT_NAME = Simutrans-Squirrel-API PROJECT_ICON = @CMAKE_CURRENT_SOURCE_DIR@/src/simutrans/simutrans.svg OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/documentation/sqapi/ EXTRACT_PRIVATE = YES INPUT = api_factory.cc \ api_base.h \ api_doc.h \ changelog.h \ api_skeleton.h \ api_skeleton.cc \ api_city.cc \ api_command.cc \ api_const.cc \ api_control.cc \ api_convoy.cc \ api_gui.cc \ api_halt.cc \ api_include.cc \ api_line.cc \ api_map_objects.cc \ api_obj_desc.cc \ api_pathfinding.cc \ api_player.cc \ api_schedule.cc \ api_settings.cc \ api_scenario.cc \ api_simple.cc \ api_world.cc \ api_tiles.cc INPUT_FILTER = ./filter_cpp.sh FILTER_PATTERNS = *.cc #FILTER_SOURCE_FILES = YES #FILTER_SOURCE_PATTERNS = ./filter_sq.sh SOURCE_BROWSER = YES ENABLE_PREPROCESSING = YES PREDEFINED = SQAPI_DOC EXAMPLE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/simutrans/pak/scenario/ EXAMPLE_PATTERNS = *.nut = simutrans-124.5/documentation/NeuePalette.png000066400000000000000000000041431520675460500213600ustar00rootroot00000000000000PNG  IHDR! ̟5PLTE$Kg9^|Lq`tӜXXXkkk}}}7G%V0f:uEOZ{Xoƴ V n(09AJR["; ,P5e?zM\%j,y4VNlbvIJJz_t%5EUeu;)S7kETbqˀW+o E\-s>Jy0 ,JDc-]|>vO`q6R,,nE:_HyVœesǂ dt!,)J9hL`+.D2U]Fnv[oۮ? Z&u:*N7bCvP\i D!^86xQLjbxɜ䵤@`@@``@@^^@^j@ak+U4@ P0`@p Th, @ @Tl,8(H8\LlX@`px @H `0@ @@``@`Pl `x0p8@DP` 000@@@PPP```))6<-FK>l_Mqixƿ!SWeoMMMkkkȀIDATxYws7%Y҂dEe_} e (FQhA5Jژle_˜n-75|5w?|f89/IsU2ZX^#,ye>a_X ˂zaYHXEBP*T UBP*TY()HF(AX7 ˛aY\X%6aYJXeeYP*T UBP*T Uh eJF*4R -' ²$,+ *²]XVw;]²[X#P*T UBP*T BLHF*4B5uMaYKXue]aYOX+,²l(, P*T UBP*T Uh eJF*4R m" f²l!,-e+aZX>$,ˇe[al', UBP*T UBPfPThB#º(, '²|JX>-,; .a|NXv UBP*T UBPfPThB#nº!,{ ^²#, ~ya_X a9XX P*T UBP*T BLHF*4B/ !%a9TXÅr),G r+, rTBP*T UBP*4 2B#N֯Ir",_ ˩r.,gׅLa9KXoH*T UBP*T UBP(S*4R Pz+, ˷|a@X-, "aX^~DX+,/wE*T UBP*T UBBR ThB r\.,? rHX~,,? U3aTBP*T UBP*4 2B#~!aVX~%,__', {aAX P*T UBP*T BLHF*4B? 럄aQXnUXn[VaMXn ߅P*T UBP*T Uh eJF*4R )w r+, <(,aayDXDŽqP*T UBP*T Uh eJFli<%, ˿?<#,>YaY@X+, ²TBP*T UBP*4 2B#$, 8g_XY^SLaɹ,s',P*T UBP*T Uh eJF*45IENDB`simutrans-124.5/documentation/ReservedColors.png000066400000000000000000000036671520675460500221200ustar00rootroot00000000000000PNG  IHDRe~IDATx1E=4@&Vb#B ^(B*Q3j4P$&TXy4ZPI+bI4 \۷;ݻ~?wogwٽѝ8t@8ĭda㖗N>2짮{_k}廮|o+qGcgfu`ߋ|񧿌+|X)yeGzq{ҋoiw8+4҆XY#@%v.}3!I?x/@e|'YD筤wOdx#%UVuh//]WWڭZT msgڋ['cN@!#C? ɟ9L_r!5tяw:oS|$w/S>FU4IċCP]l`g|9Pa,#Y&л/B8"qD8qc~'[_#kx?_k7댵^~+_ֶ=+Ok+덵y^☁_r\>w^M}nDItя; b>1 J%.1ߏ|`~\c~?GAgq*fC3!Ǘi!C8"qD8q#G!C8"qD8q#G!C'.ߓ~иq&:8L{踕]!ъ Aӂo zϬKօ9+䳀`ORqI6U G_! يJˬה#~{%{PsOu"xEknRhJRǍKX|PLKh02`չcs`>1P8"qD8չb%M׬?v ۷kݽvsZ+gwSgk>ޕ {_r壇k8}•i~nZ>w \yêEc[n2Ʒ^8E|qQ~07W^`Bq"][LC;pD @K?hU[ƌ Aޱxfŕ.-</tx…+*! W'h n&@{8q#G!C8"Can??Bxfߚi5Ε/]5ZܽÕ^Z4ڱ{ah4}jkXORǵ~j<^ȧƻZrKt,XbqjaFfN\:AInbr4ōŴTkq{[et8q(\G_ZxrU~YEb.ګ1vW }BʴEC#G!C8"qD8q#G!C8"qD8q#G!C8"qD8q#G!Cvo|vqOt3gt+KKZ;\Z[vr7nkۺuR8ոOZm o8K cqCwMJFs KQG=f{atySϼ|{ߪ$:w <_%VJuA-f(x?ԓx"ݛ:,wx8fuCZY(rT%O뙳+*]o0B=}(?EWTw=qki'(x~\/It!+kMW56+m+h:P-U 4 C3?$81X7G?3~=sv%[Ͽpq T C8"qDG1iIENDB`simutrans-124.5/documentation/about-the-code.txt000066400000000000000000000063331520675460500220030ustar00rootroot00000000000000 Short Description of the Simutrans code ======================================= Simutrans is actually quite hierarchical, as it was a learning project for OOP once. It starts all with karte_t, which holds an array of planquadrat_t (a 2D coordinate), which holds arrays of grund_t (the actual ground, or foundations, tunnel, bridges, highways, water). Each ground holds an objlist, which contains moving objects such as cars, trains and airplanes as well as other objects including buildings and trees. The objects are derived from obj_t and are located in the obj/ folder. Each stuff can have three periodic calls: for very regularly and fast actions which require exclusive access on the map (moving cars, collision avoidance) there is sync_step, called before every frame. Slower actions like route_finding, way building etc. is done in step(), where the map can change in between slightly. Some objects (most notable trees) require seasonal changes, which are covered by the relatively new action method of check_season(); The program was developed by a single person most of the time, so sometimes the documentation might not be as good as it could be. So if you find something out about a section of code please document it. But other than that, most of Simutrans is quite compartmentalised and separate from other modules. Therefore it is, in my humble opinion, best to just work locally on one issue, and submit a patch. You will get some critical reception in the forum at https://forum.simutrans.com. How the UI works ---------------- The UI is almost fully separated from the main code. All dialogues are nowadays derived from gui_frame_t and fully object oriented. If you want to add a new dialogue, derive it from the gui_frame class. Then add components out of the gui/components folder. You will find almost all components there you could need. Scroll bars are best used as scroll panes: the containing component will be clipped and can be drawn as normal. After initializing a component you have to add it to your dialogue using add_component(). Failing to do so will just not show it. Certain active things like buttons and many more can give a feedback. Use action_listener(this) for them, or query them on closing; you do not need to handle events for them yourself. Comboboxes are more tricky, but you can ignore this for now. When there is an action (like a mouse click, test input finished by return etc.) and you have called action_listener(this) before, then action_triggered() will be called by the element. It has two parameters, one is a pointer to the element (to find also out which called) and the second the returned value. For buttons there are two variants of types. The one with _state are the recommended one, as they will keep their latest state. If you use only components from gui/components, it may be enough to init your dialogue and query them when the dialogue is closed. You do not need to define drawing or action routines. It can be as easy as that. The extended settings are an example for this. Certain elements have certain spacings. Please use the spacings provided by gui_themes.h which will scale nicely even with different button or font sizes. Avoid fixed numbers without good reasons. Written Nov. 2011/Feb 2014 by prissi simutrans-124.5/documentation/coding_styles.txt000066400000000000000000000330421520675460500220460ustar00rootroot00000000000000All this rules are written as the code should be. Simutrans does not yet conform 100% to this rules, but it should. 1. General coding rules: ----------------------- - Use always the simplest working solution. -> The solution must work, and should be safe and robust. Try to use the simplest (not shortest!) piece of code which gives the needed result. -> So called hacks and tricks most often break this rule. - Use always the same name for the same thing (i.e. give variables with same semantics the same name) -> This rule tries to avoid confusion and reduces the amount of things one has to remember while coding - Use meaningful names. Method names should describe actions (i.e. use "scrollbar_moved" instead of "scrollbar_callback"). Class names should be nouns, method names should be verbs or include action descriptions. -> A method named "holy_hand_grenade()" may be useful, but what does it do? Try to give the reader as much information as possible about what's going on there. -> Class names should be nouns because classes are static elements -> Method names should be verbs because methods are dynamic elements There are some standard prefixes used throughout the program: get_ returns a value set_ will set a value is_ (ist_) will test a conditions For new functions, the first (English) form should be used. Function names should never start with uppercase letters. - Use const wherever possible -> This helps the reader of the program (he sees which values are intended not to be changed) and the compiler (to optimize the code) - Keep as much class members as possible private -> This reduces dependencies between classes. Therefore the program can be changed easier and the probability of errors reduces. - Always initialize all member variables in all constructors -> Even if it seems easy, often it is forgotten - particularly if a class has several constructors - It is preferred to have class and member variables private and public get_/set_ methods instead of public variables. -> This allows adding actions to value changes and retrievals. -> Read the section about extern variables for more benefits of using getter/setter methods. There are strong advantages! - Avoid public member variables. Never use "protected" variables, rather make the variable private and use public or protected gib_xxx() and setze_xxx() methods. -> protected variables can be changed in subclasses. The super class doesn't see this changes and can't react to them. This is likely to produce errors and makes the code harder to maintain. -> sometimes it's necessary to bind actions to value assignments to variables later on in development. Public variables don't support this change of protocol and impose much work on the programmer if such a change is needed later. -> (prissi:) However, do not hide too much. Especially with virtual functions the use of protected members is also advisable. - Avoid extern variables. Global variables make programs harder to change and make some bugs harder to find. Sometimes it gets necessary to bind actions to variable access. Use getter/setter procedures and static variables instead. -> this rule reduces direct coupling and thus enhances the chance for reusing a component. -> using getter/setter methods allows to bind actions to read/write access later on -> using getter/setter methods allows checking for valid values in assignments -> using only one of getter/setter allows to make a variable read only or write only for other parts of the program. Read only is not the same as constant, and can't be replaced with a const variable. Write only is also not expressible with language features themselves. - References are preferred instead of pointers. -> References can be NULL and add safety and robustness to a program. Many problems with pointers don't occur when references are used. Unfortunately references can't be used always instead of pointers. -> Don't complicate code. Use pointers if the use of references would complicate things. -> Also reference can hide the fact, that a variable is changed. Thus again, use const when possible. - Avoid casts if possible. -> casts can lead to errors which are very hard to find, especially if pointers to connect are casted. -> try to use dynamic_cast(expression) if you need to cast (even though this eats performance) - Don't cast const'ness away. Use mutable copies of the objects or think about another design if you run into problems with const. -> Program maintainers and compiler rely on things declared const to be immutable. Both run into trouble if you cast const away. - If you need to cast use dynamic_cast() to get type checking support from the compiler. On static pointers, also a static_cast() can do the trick. - Avoid void * if possible. This is especially dangerous when used for pointers to objects, because the compiler can't call the destructor of the object through a void pointer. Also all type checks are impossible on void *. Use of void* is tolerated in C code, but not in C++ code. -> try using templates instead of void pointers. - Keep code readable. Spaces and formatting help the reader. I.e. space between methods shows clearly the end of one method and the beginning of another. On the other hand spaces can destroy readability if used wrong. -> Use spaces where the visual separation of things reflects separate entities in the code. -> Unreadable code is hard to maintain. Time is a valuable resource. Spaces and newlines are cheap. Use the cheap resource to save the valuable. - Avoid includes in header files. Use forward declarations "class xyz_t" where possible. -> This rule tries to reduce coupling of code and compile time. - use inline methods just for very small methods (1 to 3 lines max.) -> This rule tries to reduce coupling of code and compile time. - use the size defined type like sint8, sint16, sint32, sint64 and uint8, uint16, uint32 and image_id (for image numbers) Different compilers/platforms use different rules for values. If your code depends on signedness, write this explicitly down. Note, that this errors don't show up on your platform/with your compiler but perhaps only on your teammates systems. Be very careful about this because you can't test for this problems in your own development environment. i.e. Linux/PPC treats chars as unsigned, whereas Linux/Intel treats chars as signed values! -> This rule tries to increase robustness of the code There is one exception to this rule: If you really ever need to cast a pointer to an int, cast it to "long". All C compiler will guarantee, that a pointer fits into a long. - take care of boundary violations. Simutrans offers bounds checked templates (arrays, vectors, lists). Use them where possible. If you have to use plain arrays, always double check the values. Never trust a parameter! The caller might be erroneous, or a dangling pointer trashed some memory. I feel urged to write: ever and always check index values before accessing a plain array. -> This rule tries to increase robustness of the code - keep class interfaces minimal Classes should have only the minimal set of methods that are needed to cover all requirements. Convenience methods should go into other modules or subclasses. -> Small interfaces enhance the readability and understandability of a class. -> Small interfaces also enhance the re-usability. -> Separating convenience from core methods helps to understand the structure of the design. Try to comment all classes and methods in header files. Don't hesitate to write comments for variables, too. -> Class and method comments are found in the header files rather than the code files. Code files carry comments on algorithms and operations. Class comments should include: 1.) Purpose of the class. 2.) Relations of this class to others. 3.) What are the duties of this class (what are not?) 4.) Creator and creation date (if modified by someone else add a note too!) Method comments should answer the following questions: 1.) What does this method do? 2.) How does it do it? 3.) Why is it done that way? 4.) What is valid input (what not?) 5.) What are the results for valid input 6.) How does it react on invalid input? 7.) In which context should/may/can't this method be used? (If appropriate) 8.) Creator and creation data (if modified by someone else add a note too!) Class/Instance variable comments should state: 1.) Purpose of this variable 2.) Range of valid values, maybe a list of 'special' values if there are any 3.) Creator and creation data (if modified by someone else add a note too!) I know, many existing things in Simutrans are not commented in full detail, but new things should be! Rather add comments on old parts than leave out comments on new parts, if you want to have consistency. - Header comments: Include files which define classes do not need header comments, because the class comment can serve as a header comment. Other files should carry header comments like the example shown below: /* boden.cc <- name of the file * * Natur-Untergrund fr Simutrans. <- Short description of file contents * Erstellt am ???? <- Creation date (if known) * berarbeitet Januar 2001 <- Additional history information * von Hj. Malthaner <- author/creator of the file */ If you found an outdated version (very likely) it would be great if you update it. 2. Coding styles ---------------- Always use braces. Even is there is only one line in that block! An example: if(condition) { // do something here } else { // do something else here } Or K&R if you really like this more if(condition) { ... } else { ... } No other styles of using braces are allowed. This rule applies to loops and other compound statements, too. Do not forget brackets along comparisons and use double spaces around logical operators. Avoid spaces before the comparison operator: if((i&3)==1 && ptr==NULL) { You may also use double spaces after/before the brackets: if( (i&3)==1 && ptr==NULL ) { No space between if/while/for and bracket is allowed. Use spaces in for loops like in ifs: for( int i=0; i<10; i++ ) { Use spaces in parameters list. For example: call(x, y, count); instead of call(x,y,count); If a function accepts no parameters, on its delaration, and using C++ notation we'll use: int function() This C-style declaration is *not* conformant to our standards: int function(void) Also a space between type and pointer mark is preferred: use char *bla_t::get_text() instead char* bla_t::get_text() For function definitions, put the return type in the previous line: const slist_tpl * stadt_t::get_all_buildings() const { ... Do not use macros; if possible use inlines. If you use macros, then list all parameters like #define bla(s,t) ((s)+(t)) and don't forget the brackets around each parameter! (Again, better use inline) 3. Tables and detailed information ---------------------------------- According to the rule "Use always the same name for the same thing" we need a list of common names to avoid confusion. Unfortunately the program was started with many german terms in use, so many of the names are german names: Preferred variable names: Loop counters (int): i, j, n Indices (int): i, index, n Coordinates: pos (position), k (arbitrary coordinate) Screen positions: x, y (short form), xpos, ypos (long form, preferred) Convoi: cnv Vehicles: v Rail signals: sig Rail blocks: bs (obsolete) Grounds: gr (often also bd (from boden)) Roads: str Railroads: sch Generic way: weg Buildings: gb Factories: fab UI components: comp, c (temporary component variables) seldom directly used UI windows: ??? (win is preferred) UI events: ev Button: button Button lists/vectors: buttons Scrollable list: scl Temporary int value: t Temporary char value: c Temp. general value: tmp Temporary result: r, res, result ('result' is preferred) Variable pre-/suffixes: Offsets to a base value: xxx_off Method pre-/suffixes: Retrieving a member variable value: [obsolete: gib_xxx()] use get_xxx() Setting a member variable value: [obsolete: setze_xxx()] use set_xxx() Adding a listener to a component: add_listener(xxx_listener_t *) Remove a listener from a component: remove_listener(xxx_listener_t *) Class per-/suffixes: Standard class names get the suffix "_t" (for "Type") Template classes get the suffix "_tpl" (for "template") GUI (windows) classes carry a "_gui_t" suffix Classes of the new, component-oriented UI carry a gui_ prefix instead of the _gui_t suffix. Written by Hj. Malthaner Initial version: November 2000 MFC-coding styles: For being able to compile Simutrans with MSVC v6.00, some additional rules are necessary: (these are more suggestions, since it does not compile with VC6.0) 1. Change the following construct from: class myclass { ... static const int MY_CONST = 77; ... }; to: class myclass { ... enum { MY_CONST = 77 }; ... }; Reason: VC cannot compile the first. Both constructs achieve the same result. 2. Do not use: any_type *x = new any_type[0]; Reason: VC code crashes when deleting a zero sized array. Written by prissi Initial version: June 2006 simutrans-124.5/documentation/header.txt000066400000000000000000000003251520675460500204260ustar00rootroot00000000000000/* * filename * * Copyright (c) 1997 - 2011 Hansjrg Malthaner and * the simutrans development team * * This file is part of the Simutrans project under the artistic license. * (see license.txt) */ simutrans-124.5/documentation/simutrans-palette.pal000066400000000000000000000050461520675460500226210ustar00rootroot00000000000000JASC-PAL 0100 256 36 75 103 57 94 124 76 113 145 96 132 167 116 151 189 136 171 211 156 190 233 176 210 255 88 88 88 107 107 107 125 125 125 144 144 144 162 162 162 181 181 181 200 200 200 219 219 219 17 55 133 27 71 150 37 86 167 48 102 185 58 117 202 69 133 220 79 149 237 90 165 255 123 88 3 142 111 4 161 134 5 180 157 7 198 180 8 217 203 10 236 226 11 255 249 13 86 32 14 110 40 16 134 48 18 158 57 20 182 65 22 206 74 24 230 82 26 255 91 28 34 59 10 44 80 14 53 101 18 63 122 22 77 143 29 92 164 37 106 185 44 121 207 52 0 86 78 0 108 98 0 130 118 0 152 138 0 174 158 0 196 178 0 218 198 0 241 219 74 7 122 95 21 139 116 37 156 138 53 173 160 69 191 181 85 208 203 101 225 225 117 243 59 41 0 83 55 0 107 69 0 131 84 0 155 98 0 179 113 0 203 128 0 227 143 0 87 0 43 111 11 69 135 28 92 159 45 115 183 62 138 230 74 174 245 121 194 255 156 209 20 48 10 44 74 28 68 99 45 93 124 62 118 149 79 143 174 96 168 199 113 193 225 130 54 19 29 82 44 44 110 69 58 139 95 72 168 121 86 197 147 101 226 173 115 255 199 130 8 11 100 14 22 116 20 33 139 26 44 162 41 74 185 57 104 208 76 132 231 96 160 255 43 30 46 68 50 85 93 70 110 118 91 130 143 111 170 168 132 190 193 153 210 219 174 230 63 18 12 90 38 30 117 58 42 145 78 55 172 98 67 200 118 80 227 138 92 255 159 105 11 68 30 33 94 56 54 120 81 76 147 106 98 174 131 120 201 156 142 228 181 164 255 207 64 0 0 96 0 0 128 0 0 192 0 0 255 0 0 255 64 64 255 96 96 255 128 128 0 128 0 0 196 0 0 225 0 0 240 0 0 255 0 64 255 64 94 255 94 128 255 128 0 0 128 0 0 192 0 0 224 0 0 255 0 64 255 0 94 255 0 106 255 0 128 255 128 64 0 193 97 0 215 107 0 255 128 0 255 128 0 255 149 43 255 170 85 255 193 132 8 52 0 16 64 0 32 80 4 48 96 4 64 112 12 84 132 20 104 148 28 128 168 44 164 164 0 193 193 0 215 215 0 255 255 0 255 255 32 255 255 64 255 255 128 255 255 172 32 4 0 64 20 8 84 28 16 108 44 28 128 56 40 148 72 56 168 92 76 184 108 88 64 0 0 96 8 0 112 16 0 120 32 8 138 64 16 156 72 32 174 96 48 192 128 64 32 32 0 64 64 0 96 96 0 128 128 0 144 144 0 172 172 0 192 192 0 224 224 0 64 96 8 80 108 32 96 120 48 112 144 56 128 172 64 150 210 68 172 238 80 192 255 96 32 32 32 48 48 48 64 64 64 80 80 80 96 96 96 172 172 172 236 236 236 255 255 255 41 41 54 60 45 70 75 62 108 95 77 136 113 105 150 135 120 176 165 145 218 198 191 232 1 221 1 255 33 29 255 255 83 127 155 241 193 177 209 87 101 111 227 227 255 77 77 77 255 1 127 1 1 255 0 0 0 107 107 107 155 155 155 179 179 179 201 201 201 223 223 223 0 0 0 128 0 0 0 128 0 128 128 0 0 0 128 128 0 128 0 128 128 192 192 192 128 128 128 255 0 0 0 255 0 255 255 0 0 0 255 255 0 255 0 255 255 255 255 255 simutrans-124.5/documentation/simutrans_doc.h000066400000000000000000000025051520675460500214620ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /** @file simutrans_doc.h Just contains pages of exciting documentation */ /** * @mainpage Simutrans Code Documentation * * * License * ======= * * Simutrans is licensed under the Artistic License version 1.0. The Artistic License 1.0 is an OSI-approved license which allows for use, distribution, modification, and distribution of modified versions, under the terms of the Artistic License 1.0. For the complete license text see LICENSE.txt. * * Simutrans paksets (which are necessary to run the game) have their own license, but no one is included alongside this code. * * * * * @section s_squirrel squirrel Scripting language * ------------------ * * The scripts have to be written in squirrel. The manual can be found at Squirrel main page. * As squirrels like to crack nuts, understandably the script files get the extension '.nut'. * * * For more information see Simutrans-Squirrel-API Documentation. * */ /** * @defgroup squirrel-toolkit-api Squirrel toolkit interface * * The following methods create macro scripted tools. * * * */ simutrans-124.5/documentation/utf8-support.txt000066400000000000000000000015211520675460500215750ustar00rootroot00000000000000UTF8 support works very simple: If the language file is UTF8, then all input/output is handled as UTF8. However, this is only recommended for real multibyte codepages, as SDL just returns keys from Windows/Latin codepages (0...256) and not real UTF8, as far as I could find out. Also, if a font has less than 256 characters, then the font encoding will be ignored at the moment. Otherwise the font encoding must be ISO10626-1 (or what ever Unicode is). UTF8 files are beginning with "" in UTF8 as very first letter. The line endings must be "\xA" (see the japanese files) The only exception so far is the citylist, which must be in the same encoding as the language file. But this will also change someday. If an UTF8 file is encountered in a non UTF8 language, it will be translated to 8Bit Latin (by just ignoring all bits but the lowest 8). simutrans-124.5/readme.txt000066400000000000000000000100311520675460500155550ustar00rootroot00000000000000About ===== Simutrans is a freeware and open-source transportation simulator. Your goal is to establish a successful transport company. Transport passengers, mail and goods by rail, road, ship, and even air. Interconnect districts, cities, public buildings, industries and tourist attractions by building a transport network you always dreamed of. You can download Simutrans from: - Simutrans Website: https://www.simutrans.com/en/ - Steam: https://store.steampowered.com/app/434520/Simutrans/ Check the forum or wiki if you need help: - International Forum: https://forum.simutrans.com/ - Simutrans Wiki: https://wiki.simutrans.com/ Compiling Simutrans =================== This is a short guide on compiling simutrans. If you want more detailed information, read the Compiling Simutrans forum thread https://simutrans-germany.com/wiki/wiki/en_CompilingSimutrans If you are on Windows, download either MSVC or MinGW. MSVC is easy for debugging, MSYS2 is easy to set up (but it has to be done on the command line). - MSVC: https://visualstudio.microsoft.com/ - MSYS2: https://www.msys2.org/ Getting the libraries ------------------------ You will need pkgconfig (Unix) or vcpkg (Microsoft Visual C++) https://github.com/Microsoft/vcpkg - Needed (All): libpng2 libbzip2 zlib libfreetype - Needed (Linux/Mac): libSDL2 libfluidsynth (for midi music) - Optional but recommended: libzstd (faster compression) miniupnpc (for easy server setup) - MSVC: Copy install-building-libs-{architecture}.bat to the vcpkg folder and run it. - MSYS2/Ubuntu/Debian: Run setup-development.sh to get the libraries and set up the environment. - Linux: Use https://pkgs.org/ to search for development libraries available in your package manager. - Mac: Install libraries via Homebrew: https://brew.sh/ Compiling --------- Go to the source code directory of simutrans (simutrans/trunk if you downloaded from svn). You have three build systems to choose from: make, MSVC, and CMake. We recommend make or MSVC for debug builds. Compiling will give you only the executable, you still need a Simutrans installation to run the program. You can start simutrans with "-use_workdir" to point it to an existing installation. 1) make (MacOS) autoreconf -ivf (Linux) autoconf ./configure make -j 4 (MacOS) make OSX/getversion 2) Microsoft Visual C++ Open the simutrans.sln, select the target (default is GDI) and build. 3) CMake (Unix) cmake -G "Insert Correct Makefiles Generator" -B build . (Unix) cmake --build build -j 4 (MSVC) cmake.exe -B build -G "Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE=[vcpkg-root]/scripts/buildsystems/vcpkg.cmake (MSVC) cmake.exe --build build --config Release See here for a list of generators: https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html Cross-Compiling =============== If you want to cross-compile Simutrans from Linux for Windows, see the Cross-Compiling Simutrans wiki page https://simutrans-germany.com/wiki/wiki/en_Cross-Compiling_Simutrans Contribute ========== You cand find general information about contributing to Simutrans in the Development Index of the wiki: https://simutrans-germany.com/wiki/wiki/en_Devel_Index?structure=en_Devel_Index Reporting bugs ============== For bug reports use the Bug Reports Sub-Forum: https://forum.simutrans.com/index.php/board,8.0.html License ======= Simutrans is licensed under the Artistic License version 1.0. The Artistic License 1.0 is an OSI-approved license which allows for use, distribution, modification, and distribution of modified versions, under the terms of the Artistic License 1.0. For the complete license text see LICENSE.txt. Simutrans paksets (which are necessary to run the game) have their own license, but no one is included alongside this code. Credits ======= Simutrans was originally written by Hansjörg Malthaner "Hajo" from 1997 until he retired from development around 2004. Since then a team of contributors (The Simutrans Team) lead by Markus Pristovsek "Prissi" develop Simutrans. A list of early contributors can be found in simutrans/thanks.txt simutrans-124.5/reorganise-code-r10444.sh000077500000000000000000000212151520675460500200260ustar00rootroot00000000000000#!/bin/bash if [[ -n "$(ls | grep -e '.editorconfig')" ]]; then echo "Wrong directory!" >&2 exit 1 fi rm -rf revision.h mkdir -p src/simutrans mkdir -p src/simutrans/tool mkdir -p src/Windows svn add src declare -a PATHREPLACE=("s|\.\./squirrel/|\.\./\.\./squirrel/|") sed -i 's|#include "\.\./|#include "\.\./simutrans/|' squirrel/*.{h,cc} svn mv squirrel src/ sed -i 's|#include "\.\.|#include "\.\./simutrans|' makeobj/*.{h,cc} svn mv makeobj src/ svn mv gui src/simutrans svn mv obj src/simutrans svn mv dataobj descriptor display io music network player script sound sys tpl utils vehicle world src/simutrans/ PATHREPLACE+=("s|boden/wege/|obj/way/|") sed -i -E 's|#include \"\.\./([^/]+\")|#include \"\.\./\.\./ground/\1|' boden/wege/*.{h,cc} svn mv boden/wege/ src/simutrans/obj/way PATHREPLACE+=("s|bauer/|builder/|") svn mv bauer src/simutrans/builder PATHREPLACE+=("s|boden/|ground/|") sed -i 's|wege/|../obj/way/|' boden/*.{h,cc} svn mv boden src/simutrans/ground PATHREPLACE+=("s|nettools/|nettool/|") sed -i 's|#include "..|#include "../simutrans|' nettools/*.{h,cc} svn mv nettools src/nettool # Additional reorganizations PATHREPLACE+=("s|unicode\.cc|utils/unicode\.cc|" "s|unicode\.h|utils/unicode\.h|") sed -i 's|#include "|#include "../|' unicode.{h,cc} sed -i 's|#include "../utils/|#include "|' unicode.{h,cc} svn mv unicode.{h,cc} src/simutrans/utils PATHREPLACE+=("s|simdepot\.cc|obj/depot\.cc|" "s|simdepot\.h|obj/depot\.h|") sed -i 's|#include "|#include "../|' simdepot.{h,cc} sed -i 's|#include "../obj/|#include "|' simdepot.{h,cc} svn mv simdepot.cc src/simutrans/obj/depot.cc svn mv simdepot.h src/simutrans/obj/depot.h PATHREPLACE+=("s|simmenu\.cc|tool/simmenu\.cc|" "s|simmenu\.h|tool/simmenu\.h|") sed -i 's|#include "|#include "../|' simmenu.{h,cc} svn mv simmenu.{h,cc} src/simutrans/tool/ PATHREPLACE+=("s|simtool|tool/simtool|") sed -i 's|#include "|#include "../|' simtool* svn mv simtool* src/simutrans/tool/ PATHREPLACE+=("s|simworld\.cc|world/simworld\.cc|" "s|simworld\.h|world/simworld\.h|") sed -i 's|#include "|#include "../|' simworld.{h,cc} svn mv simworld.{h,cc} src/simutrans/world/ PATHREPLACE+=("s|simplan\.cc|world/simplan\.cc|" "s|simplan\.h|world/simplan\.h|") sed -i 's|#include "|#include "../|' simplan.{h,cc} svn mv simplan.{h,cc} src/simutrans/world/ PATHREPLACE+=("s|simcity\.cc|world/simcity\.cc|" "s|simcity\.h|world/simcity\.h|") sed -i 's|#include "|#include "../|' simcity.{h,cc} svn mv simcity.{h,cc} src/simutrans/world/ find . -maxdepth 1 -type f -name "*.cc" -exec svn mv {} src/simutrans \; find . -maxdepth 1 -type f -name "*.h" -exec svn mv {} src/simutrans \; PATHREPLACE+=("s|finder/|world/|") svn mv finder/*.{h,cc} src/simutrans/world svn rm --force finder PATHREPLACE+=("s|ifc/simtestdriver\.h|vehicle/simtestdriver\.h|" "s|ifc/sync_steppable\.h|obj/sync_steppable\.h|") svn mv ifc/simtestdriver.h src/simutrans/vehicle/ svn mv ifc/sync_steppable.h src/simutrans/obj/ svn rm --force ifc svn mv simres.rc src/Windows svn mv Simutrans.manifest src/Windows svn mv nsis src/Windows svn mv OSX src svn mv android src svn mv scripts tools svn mv distribute.sh tools svn mv findversion.sh tools svn mv get_lang_files.* tools svn mv get_pak.* tools svn mv get_revision.* tools svn mv install-building-libs* tools svn mv revision.jse tools svn mv ../base.tab src svn mv ../zipsrc.sh tools svn mv ../patches src/ svn mv *.ico src/Windows/ svn mv simutrans.svg src/simutrans/ svn mv .desktop src/simutrans/ find . -name '*_t.cc' -exec bash -c 'svn mv $0 ${0/_t\.cc/\.cc}' {} \; find . -name '*_t.h' -exec bash -c 'svn mv $0 ${0/_t\.h/\.h}' {} \; echo "Remove _t suffix from files" # remove _t also in the include files sed -i 's|_t\.|\.|' cmake/SimutransSourceList.cmake sed -i 's|_t\.|\.|' Simutrans-Main.vcxitems find . -type f -name "Makefile" -exec sed -E -i 's|_t\.|\.|' {} \; includereplace="-e s|_t\.h\\\"|\.h\\\"|" # process accumulated path and name changes in include files for pattern in "${PATHREPLACE[@]}" do echo "Replacing $pattern" if ! [[ $pattern == *".cc|"* ]]; then includereplace="${includereplace} -e ${pattern} " fi sed -i "$pattern" cmake/SimutransSourceList.cmake frontpattern=`echo $pattern | sed -E 's-(s\|.+\|).+\|-\1-'` backpattern=`echo $pattern | sed -E 's-s\|.+\|(.+\|)-\1-' | sed 's|/|\\\\\\\\|g'` mspattern=`echo $pattern | sed 's|/|\\\\\\\\|g'` sed -i "$mspattern" Simutrans-Main.vcxitems find . -type f -name "Makefile" -exec sed -E -i "$pattern" {} \; done #set -x echo "Now replace in include files" find . -type f "(" -name "*.cc" -o -name "*.h" -o -name "*mm" ")" -exec sed -i ${includereplace} {} \; sed -i 's|../world/||' src/simutrans/world/*.{h,cc} echo "Final cleaning up build system" sed -i -E 's|^\t\t([^\\\$]+)|\t\tsrc/simutrans/\1|' cmake/SimutransSourceList.cmake sed -i 's|makeboj/|add_subdirectory(src/makeobj EXCLUDE_FROM_ALL)|' cmake/SimutransSourceList.cmake sed -i 's|add_subdirectory(makeobj EXCLUDE_FROM_ALL)|add_subdirectory(src/makeobj EXCLUDE_FROM_ALL)|' CMakeLists.txt sed -i 's|add_subdirectory(nettools EXCLUDE_FROM_ALL)|add_subdirectory(src/nettool EXCLUDE_FROM_ALL)|' CMakeLists.txt sed -i 's|Directory)|Directory)src\\simutrans\\|' Simutrans-Main.vcxitems sed -i 's|ClCompile Include=\"|ClCompile Include=\"src\\simutrans\\|' Simutrans*.vcxproj sed -i 's|cscript.exe //Nologo revision.jse|cscript.exe //Nologo tools\\revision.jse|' Simutrans*.vcxproj sed -i 's|revision\.h\"|src\\\\simutrans\\\\revision\.h\"|' tools/revision.jse sed -i 's|SOURCES *+= |SOURCES += src/simutrans/|' Makefile sed -i 's|./get_revision.sh|tools/get_revision.sh|' Makefile sed -i 's| sys/| src/simutrans/sys/|' CMakeLists.txt sed -i 's| display/| src/simutrans/display/|' CMakeLists.txt sed -i 's| music/| src/simutrans/music/|' CMakeLists.txt sed -i 's| sound/| src/simutrans/sound/|' CMakeLists.txt sed -i 's| io/| src/simutrans/io/|' CMakeLists.txt sed -i 's| gui/| src/simutrans/gui/|' CMakeLists.txt sed -i 's|${SOURCE_DIR}/revision.h|${SOURCE_DIR}/src/simutrans/revision.h|' CMakeLists.txt sed -i 's|${SOURCE_DIR}/revision.h|${SOURCE_DIR}/src/simutrans/revision.h|' cmake/SimutransRevision.cmake sed -i 's|/nsis|/src/Windows/nsis|' CMakeLists.txt sed -i 's|get_pak.sh|${CMAKE_SOURCE_DIR}/tools/get_pak.sh|' CMakeLists.txt sed -i 's|get_pak.ps1|tools/get_pak.ps1|' CMakeLists.txt sed -i 's|get_lang_files.ps1|tools/get_lang_files.ps1|' CMakeLists.txt sed -i 's|../get_lang_files.sh|${CMAKE_SOURCE_DIR}/tools/get_lang_files.sh|' CMakeLists.txt sed -i 's|paksetinfo.h|src/simutrans/paksetinfo.h|' CMakeLists.txt sed -i 's|#include \"simversion.h\"|#include \"../simutrans/simversion.h\"|' src/Windows/simres.rc sed -i 's| simres.rc| src/Windows/simres.rc|' CMakeLists.txt sed -i 's|simutrans\\simres.rc|Windows\\simres.rc|' Simutrans-Main.vcxitems sed -i 's|src/simutrans/simres.rc|src/Windows/simres.rc|' Makefile # since squirrel moved up sed -i 's|simutrans/squirrel|squirrel|' cmake/SimutransSourceList.cmake sed -i 's|src\\simutrans\\squirrel|src\\squirrel|' Simutrans-Main.vcxitems find . -type f -name "Makefile" -exec sed -E -i 's|simutrans/squirrel/|squirrel/|' {} \; # Fix makeobj/nettool CMakeLists.txt sed -i 's|../|../simutrans/|' src/makeobj/CMakeLists.txt sed -i 's|../|../simutrans/|' src/nettool/CMakeLists.txt # Fix makeobj/nettool Makefile build sed -i 's|\$(MAKE) -e -C makeobj|$(MAKE) -e -C src/makeobj|' Makefile sed -i 's|\$(MAKE) -e -C nettools|$(MAKE) -e -C src/nettool|' Makefile sed -i 's|-include |-include ../|' src/{makeobj,nettool}/Makefile sed -i 's|\.\./uncommon\.mk|../../uncommon.mk|' src/{makeobj,nettool}/Makefile uncommon.mk sed -i 's|+= \.\./|+= ../simutrans/|' src/{makeobj,nettool}/Makefile sed -i 's|_PROGDIR := \.\./|_PROGDIR := ../../|' src/{makeobj,nettool}/Makefile sed -i 's|BUILDDIR := \.\./|BUILDDIR := ../../|' src/{makeobj,nettool}/Makefile sed -i 's|\.\./config\.|../../config.|' uncommon.mk sed -i 's|OBJS := $(patsubst %, $(BUILDDIR)/%-$(TOOL).o, $(basename $(patsubst ../%, %,$(filter ../%,$(SOURCES)))))|OBJS := $(patsubst %, $(BUILDDIR)/%-$(TOOL).o, $(basename $(patsubst ../simutrans/%, %,$(filter ../simutrans/%,$(SOURCES)))))|' uncommon.mk sed -i 's|OBJS += $(patsubst %, $(BUILDDIR)/$(TOOL)/%-$(TOOL).o, $(basename $(filter-out ../%,$(SOURCES))))|OBJS += $(patsubst %, $(BUILDDIR)/$(TOOL)/%-$(TOOL).o, $(basename $(filter-out ../simutrans/%,$(SOURCES))))|' uncommon.mk sed -i 's|\.\./%\.c \$(BUILDCONFIG_FILES)|../simutrans/%.cc $(BUILDCONFIG_FILES)|' uncommon.mk sed -i 's|\.\./%\.cc \$(BUILDCONFIG_FILES)|../simutrans/%.cc $(BUILDCONFIG_FILES)|' uncommon.mk # TODO Update location of paksetinfo.h in get_pak.sh # TODO fix distribution and github actions # prissi prefernces # TODO add file prefixes to some files (like obj_ for objects) # TODO rename xxx_vehicle.cc to vehicle_xxx.cc and so on simutrans-124.5/setup-development.sh000077500000000000000000000010551520675460500176040ustar00rootroot00000000000000 if grep "Ubuntu" /etc/*-release then echo "Setup for Ubuntu, please be patient!" sudo sh tools/setup-debian.sh elif grep -q MSYS2 /etc/*-release then echo "Setup for Mingw, please be patient!" tools/setup-mingw.sh elif grep "Debian" /etc/*-release then echo "Setup for Debian, please be patient!" su -c "sh tools/setup-debian.sh" fi read -p "Confirm compile simutrans? (Yy) " reply if [ $reply = "y" -o $reply = "Y" ]; then echo "Compiling using make" autoconf ./configure make -j $(nproc) echo "Update translations" tools/distribute.sh fi simutrans-124.5/simutrans/000077500000000000000000000000001520675460500156115ustar00rootroot00000000000000simutrans-124.5/simutrans/ai/000077500000000000000000000000001520675460500162025ustar00rootroot00000000000000simutrans-124.5/simutrans/ai/readme.txt000066400000000000000000000001431520675460500201760ustar00rootroot00000000000000Put AI scripts here: - Create directory for the ai script - Put the *.nut files in this directory simutrans-124.5/simutrans/ai/sqai/000077500000000000000000000000001520675460500171375ustar00rootroot00000000000000simutrans-124.5/simutrans/ai/sqai/ai.nut000066400000000000000000000121721520675460500202630ustar00rootroot00000000000000/** * Main file of the AI player. */ // TODO obey construction speed setting // TODO check allowed transport types // some meta data - unused ai <- {} ai.short_description <- "Test AI player implementation" ai.author <-"dwachs" ai.version <- "0.1" // includes include("basic") // .. definition of basic node classes include("astar") // .. route search for way building etc include("save") // .. routines to save class instances include("factorysearcher") // .. checks factories for available connections include("industry_connection_planner") // .. plans connection between 2 factories include("combined_connections") // .. plans connections using water + land transport include("industry_manager") // .. manages existing connection (buys, sells, upgrades convoys) include("placefinder") // .. utility functions to find places for stations near factories etc include("prototyper") // .. plans convoy-type for a connection include("road_connector") // .. builds road connection include("ship_connector") // .. creates ship connection include("station_manager") // .. keeps information about freight station include("vehicle_constructor") // .. constructs convoy, assign to line, start // basic functions sum <- @(a,b) a+b function abs(x) { return x>0 ? x : -x } // global variables our_player_nr <- -1 our_player <- null // player_x instance // the AI is organized as a tree, // all the work is done in the nodes of the tree tree <- {} // nodes with particular jobs factorysearcher <- null industry_manager <- null station_manager <- null // stepping info s <- {} s._step <- 0 s._next_construction_step <- 0 // the table 'persistent' will be saved in the savegame persistent.s <- s // 2.. 14 = 13 names possible_names <- ["Petersil Cars", "Teumer Alp Dream Trucks", "Runk & Strunk Transports", "A. Nach B. Transports", "Interflug Fourwheelers", "Pfarnest International", "Suboptimal Transports", "Conveyor Belts & Braces", "Bucket Brigade Inc.", "Maggikraut AG", "Bugs & Eggs Unlimited", "S. Claus & R. Deer Worldwide", "Leiterwagn & Sons" ] /** * Start-routine. Will be called when AI is initialized. * Parameter: the number of the AI player. */ function start(pl_nr) { init() our_player_nr = pl_nr if (our_player_nr > 1 && our_player_nr-2 < possible_names.len()) { player_x(our_player_nr).set_name( possible_names[our_player_nr-2]); } our_player = player_x(our_player_nr) print("Act as player no " + our_player_nr + " under the name " + our_player.get_name()) init_tree() } /** * Initialize the tree with basic nodes. */ function init_tree() { if (factorysearcher == null) { factorysearcher = factorysearcher_t() } if (industry_manager == null) { industry_manager = industry_manager_t() } if (!("tree" in persistent)) { tree = manager_t() tree.append_child(factorysearcher) tree.append_child(industry_manager) persistent.tree <- tree } else { if (persistent.tree.getclass() != manager_t) { // upgrade tree = manager_t() foreach(i in ["nodes", "next_to_step"]) { tree[i] = persistent.tree[i] } } else { tree = persistent.tree } } if (!("station_manager" in persistent)) { persistent.station_manager <- freight_station_manager_t(1) } } /** * Called after savegame is loaded. */ function resume_game(pl_nr) { init() our_player_nr = pl_nr our_player = player_x(our_player_nr) init_tree() s = persistent.s } function init() { annotate_classes() // sets class name as attribute for all known classes (save.nut) } /** * The heart beat of the player. * If the routine will take too much time, execution is suspended and later resumed. * This should be completely transparent to the script. * Then the main program is still responsive. */ function step() { tree.step() s._step++ // connector node may fail, but may offer alternative connector node local report = tree.get_report() if (report) { factorysearcher.append_report(report) } if (s._step > s._next_construction_step) { local r = factorysearcher.get_report() if (r && r.action) { print("New report: expected construction cost: " + (r.cost_fix / 100.0)) tree.append_child(r.action) } s._next_construction_step += 1 + (s._step % 3) } } /** * Helper routine: translate 3d-coordinate to string. * This can be used as key in tables. */ function coord3d_to_key(c) { return ("coord3d_" + c.x + "_" + c.y + "_" + c.z).toalnum(); } function coord_to_key(c) { return ("coord_" + c.x + "_" + c.y).toalnum(); } function is_cash_available(cost /* in 1/100 cr */) { return 2*cost + 2*our_player.get_current_maintenance() < our_player.get_current_net_wealth() } /** * Called to save into savegame. * Returns string that will be saved. * Here: we turn the persistent table into a string using recursive_save (from script/script_base.nut). */ function save() { local str = "" local tic = get_ops_total() local rem = get_ops_remaining() str = "persistent = " + recursive_save(persistent, "\t", [ persistent ] ) local toc = get_ops_total() print("save used " + (toc-tic) + " ops, remaining = " + rem) return str } simutrans-124.5/simutrans/ai/sqai/astar.nut000066400000000000000000000215371520675460500210110ustar00rootroot00000000000000/** * Classes to help with route-searching. * Based on the A* algorithm. */ /** * Nodes for A* */ class astar_node extends coord3d { previous = null // previous node cost = -1 // cost to reach this node dist = -1 // distance to target constructor(c, p, co, d) { x = c.x y = c.y z = c.z previous = p cost = co dist = d } function is_straight_move(d) { return d == dir || (previous && previous.dir == d) } } /** * Class to perform A* searches. * * Derived classes have to implement: * process_node(node): add nodes to open list reachable by node * * To use this: * 1) call prepare_search * 2) add tiles to target array * 3) call compute_bounding_box * 4) add start tiles to open list * 5) call search() * 6) use route */ class astar { closed_list = null // table nodes = null // array of astar_node heap = null // binary heap targets = null // array of coord3d's boundingbox = null // box containing all the targets route = null // route, reversed: target to start // statistics calls_open = 0 calls_closed = 0 calls_pop = 0 // costs - can be fine-tuned cost_straight = 10 cost_curve = 14 constructor() { closed_list = {} heap = simple_heap_x() targets = [] } function prepare_search() { closed_list = {} nodes = [] route = [] heap.clear() targets = [] calls_open = 0 calls_closed = 0 calls_pop = 0 } // adds node c to closed list function add_to_close(c) { closed_list[ coord3d_to_key(c) ] <- 1 calls_closed++ } function test_and_close(c) { local key = coord3d_to_key(c) if (key in closed_list) { return false } else { closed_list[ key ] <- 1 calls_closed++ return true } } function is_closed(c) { local key = coord3d_to_key(c) return (key in closed_list) } // add node c to open list with give weight function add_to_open(c, weight) { local i = nodes.len() nodes.append(c) heap.insert(weight, i) calls_open++ } function search() { // compute bounding box of targets compute_bounding_box() local current_node = null while (!heap.is_empty()) { calls_pop++ local wi = heap.pop() current_node = nodes[wi.value] local dist = current_node.dist // target reached if (dist == 0) break; // already visited previously if (!test_and_close(current_node)) { current_node = null continue; } // investigate neighbours and put them into open list process_node(current_node) current_node = null } route = [] if (current_node) { // found route route.append(current_node) while (current_node.previous) { current_node = current_node.previous route.append(current_node) } } print("Calls: pop = " + calls_pop + ", open = " + calls_open + ", close = " + calls_closed) } /** * Computes bounding box of all targets to speed up distance computation. */ function compute_bounding_box() { if (targets.len()>0) { local first = targets[0] boundingbox = { xmin = first.x, xmax = first.x, ymin = first.y, ymax = first.y } for(local i=1; i < targets.len(); i++) { local t = targets[i] if (boundingbox.xmin > t.x) boundingbox.xmin = t.x; if (boundingbox.xmax < t.x) boundingbox.xmax = t.x; if (boundingbox.ymin > t.y) boundingbox.ymin = t.y; if (boundingbox.ymax < t.y) boundingbox.ymax = t.y; } } } /** * Estimates distance to target. * Returns zero if and only if c is a target tile. */ function estimate_distance(c) { local d = 0 local curved = 0 // distance to bounding box local dx = boundingbox.xmin - c.x if (dx <= 0) dx = c.x - boundingbox.xmax; if (dx > 0) d += dx; else dx = 0; local dy = boundingbox.ymin - c.y if (dy <= 0) dy = c.y - boundingbox.ymax if (dy > 0) d += dy; else dy = 0; if (d > 0) { // cost to bounding box return cost_straight * d + (dx*dy > 0 ? cost_curve - cost_straight : 0); } else { local t = targets[0] d = abs(t.x-c.x) + abs(t.y-c.y) // inside bounding box for(local i=1; i < targets.len(); i++) { local t = targets[i] local dx = abs(t.x-c.x) local dy = abs(t.y-c.y) d = min(d, cost_straight * (dx+dy) + (dx*dy > 0 ? cost_curve - cost_straight : 0)) } } return d } } class ab_node extends ::astar_node { dir = 0 // direction to reach this node flag = 0 // flag internal to the route searcher constructor(c, p, co, d, di, fl=0) { base.constructor(c, p, co, d) dir = di flag = fl } } /** * Helper class to find bridges and spots to place them. */ class pontifex { player = null bridge = null constructor(pl, way) { player = pl local list = bridge_desc_x.get_available_bridges(way.get_waytype()) local len = list.len() local way_speed = way.get_topspeed() if (len>0) { bridge = list[0] for(local i=1; i bridge.get_topspeed()) { bridge = b } } else { if (way_speed < b.get_topspeed() && b.get_topspeed() < bridge.get_topspeed()) { bridge = b } } } } } function find_end(pos, dir, min_length) { return bridge_planner_x.find_end(player, pos, dir, bridge, min_length) } } /** * Class to search a route and to build a connection (i.e. roads). * Builds bridges. But not tunnels (not implemented). */ class astar_builder extends astar { builder = null bridger = null way = null function process_node(cnode) { local from = tile_x(cnode.x, cnode.y, cnode.z) local back = dir.backward(cnode.dir) for(local d = 1; d<16; d*=2) { // do not go backwards if (d == back) { continue } // continue straight after a bridge if (cnode.flag == 1 && d != cnode.dir) { continue } local to = from.get_neighbour(wt_all, d) if (to) { if (builder.is_allowed_step(from, to) && !is_closed(to)) { // estimate moving cost local move = cnode.is_straight_move(d) ? cost_straight : cost_curve local dist = estimate_distance(to) // is there already a road? if (!to.has_way(wt_road)) { move += 8 } local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, d) add_to_open(node, weight) } // try bridges else if (bridger && d == cnode.dir && cnode.flag != 1) { local len = 1 local max_len = bridger.bridge.get_max_length() do { local to = bridger.find_end(from, d, len) if (to.x < 0 || is_closed(to)) { break } local bridge_len = abs(from.x-to.x) + abs(from.y-to.y) local move = bridge_len * cost_straight * 3 /*extra bridge penalty */; // set distance to 1 if at a target tile local dist = max(estimate_distance(to), 1) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, d, 1 /*bridge*/) add_to_open(node, weight) len = bridge_len + 1 } while (len <= max_len) } } } } function search_route(start, end) { prepare_search() foreach (e in end) { targets.append(e); } compute_bounding_box() foreach (s in start) { local dist = estimate_distance(s) add_to_open(ab_node(s, null, 1, dist+1, 0, 0), dist+1) } search() if (route.len() > 0) { remove_field( route[0] ) // do not try to build in tunnels local is_tunnel_0 = tile_x(route[0].x, route[0].y, route[0].z).find_object(mo_tunnel) local is_tunnel_1 = is_tunnel_0 for (local i = 1; i= nodes.len()) { next_to_step = 0; return r_t(RT_DONE_NOTHING); } local ret = nodes[next_to_step].step() // take nodes report if (ret.report) { append_report(ret.report) } // delete child if ready if (ret.can_be_deleted()) { nodes.remove(next_to_step); } else if (ret.is_ready()) { next_to_step ++ } // successor node if (ret.node) { append_child(ret.node) } // our return code local rc = RT_PARTIAL_SUCCESS // want next call, too if (ret.has_failed()) { rc = RT_ERROR // propagate error } else if (next_to_step > nodes.len()) { rc = RT_SUCCESS // done with stepping all nodes } return r_t(rc) } } /** * Base class to report financial aspects of plans. */ class report_t { // tree that will be executed action = null // costs cost_fix = 0 cost_monthly = 0 // expected gain per month gain_per_m = 0 function merge_report(r) { action.append_child( r.action ) cost_fix += r.cost_fix cost_monthly += r.cost_monthly gain_per_m += r.gain_per_m } function _save() { return ::saveinstance("report_t", this) } } /** * Manager class. * Steps its child nodes, otherwise does some work(), generates reports. */ class manager_t extends node_seq_t { reports = null constructor(n = "manager_t") { base.constructor(n) reports = [] } function step() { dbgprint("stepping a child") local r = base.step() if (r.code == RT_DONE_NOTHING || r.code == RT_SUCCESS) { // all nodes were stepped dbgprint("doing some work") return work() } dbgprint("stepped a child") return r } function work() { } function append_report(r) { reports.append(r); } /** * Returns the best report available. */ function get_report() { if (reports.len() == 0) return null // find report that maximizes gain_per_m / cost_fix local i = -1 local best = null for(local j=0; j jps // 0x10 -> find flat place // 0x20 -> flat place (mark next step with 0x40) // 0x40 -> cannot go into water if (cnode.flag == 0x10) { process_node_to_land(cnode, from) return } local test_dir = cnode.previous ? (back ^ 0x0f) & (cnode.flag & 0x0f) : 0x0f for(local d = 1; d<16; d*=2) { // do not go backwards if (( d &test_dir) ==0 ) { continue } local to = from.get_neighbour(wt_all, d) if (to && !is_closed(to)) { local to_water = ::finder._tile_water_way(to) // water -> water // land -> land if (from_water == to_water) { // can we build way here? if (!from_water) { // empty space for station near a halt with harbour local halt = to.get_halt() if (halt && halt.get_owner().nr == our_player_nr && from.is_empty()) { // try to connect to this halt and to use its harbours process_node_to_land_halt(cnode, halt) } // can we build way here? if (!builder.is_allowed_step(from, to)) { // could be a halt on the other tile that has harbour that could be used continue; } } // estimate moving cost local move = cnode.is_straight_move(d) ? cost_straight : cost_curve local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist // use jump-point search (see dataobj/route.cc) local jps = to_water && (cnode.previous) ? (water_dir ^ 0x0f) | d | cnode.dir | from.get_canal_ribi() : 0x0f if (cnode.flag & 0x20) jps = jps | 0x40; local node = ab_node(to, cnode, cost, dist, d, jps) add_to_open(node, weight) } else if (from_water) { // water -> land if (from.is_water() && to.get_halt()) { // check for existing harbour local halt = ship_connector_t.get_harbour_halt(to) if (halt) { // create node as end node of water route local harbour_node = ab_node(to, cnode, cnode.cost, cnode.dist, 0x0f, 0x0f) // now look for empty spots to connect to this halt process_node_to_harbour(harbour_node, halt) } } // we have to build ourselves if (!from.is_water() || !can_place_harbour(to, d) ) { continue } local move = cost_harbour local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, d, 0x10) add_to_open(node, weight) } else { // land -> water if (!to.is_water() || !can_place_harbour(from, dir.backward(d)) || (cnode.flag & 0x60) ) { continue } if (cnode.previous) { local fromfrom = tile_x(cnode.previous.x, cnode.previous.y, cnode.previous.z) // want to build station here with one connecting road if (fromfrom == null || !fromfrom.is_empty() || fromfrom.get_slope()!=0 || cnode.previous.previous == null) { continue } } local move = cost_harbour local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, d, 0x0f) add_to_open(node, weight) } } } } function can_place_harbour(tile, d_water_to_land) { return ship_connector_t.get_harbour_type_for_tile(tile, planned_harbour, planned_harbour_flat, d_water_to_land) > 0 } function process_node_to_land(cnode, from) { local pos = coord(cnode.x, cnode.y) for(local d = 1; d<16; d++) { // test all 8 neighbouring tiles if (!dir.is_threeway(d)) { local c = pos + dir.to_coord(d) try { local to = square_x(c.x, c.y).get_ground_tile() if (to && to.is_empty() && to.get_slope()==0) { // can place station here local move = cost_road_stop local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, 0x0f, 0x0f) add_to_open(node, weight) } } catch(ev) {} } } } function process_node_to_harbour(cnode, halt) { local visited = {} // process all tiles of this halt foreach(tile in halt.get_tile_list()) { // check all neighbouring tiles for empty spots for(local d = 1; d<16; d*=2) { local c = coord(tile.x, tile.y) + dir.to_coord(d) try { local to = square_x(c.x, c.y).get_ground_tile() if (to && to.is_empty() && to.get_slope()==0 && !(coord3d_to_key(to) in visited) ) { // can place station here local move = cost_road_stop local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, 0x0f, 0x0f) add_to_open(node, weight) // mark as visited visited[ coord3d_to_key(to) ] <- 1 } } catch(ev) {} } } } /** * Does steps land-tile -> halt -> harbour -> water * Last route tile is cnode, check if station can be placed. * Check for harbour tiles, put corresponding nodes to open list. */ function process_node_to_land_halt(cnode, halt) { // process all tiles of this halt foreach(tile in halt.get_tile_list()) { if (!tile.is_water() && ship_connector_t.get_harbour_halt(tile)!=null) { local harbour_node = ab_node(tile, cnode, cnode.cost, cnode.dist, 0x0f, 0x0f) local d = 0 if (tile.get_slope()) { // get direction from slope d = dir.backward(slope.to_dir(tile.get_slope())) } else { // flat dock, check all four neighbour tiles for water + harbour d = 1 } do { local to = coord3d(tile.x, tile.y, tile.z) + dir.to_coord(d) if (world.is_coord_valid(to)) { local move = cost_road_stop local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, harbour_node, cost, dist, d, 0x0f) add_to_open(node, weight) } d = d*2 } while (tile.get_slope()==0 && d<16) } } } function search_route(fsrc, fdest) { print("Search amphibious connection") // find station places local s = [] local e = [] c_harbour_tiles = {} s.append(::finder.find_station_place(fsrc, fdest) ) e.append(::finder.find_station_place(fdest, fsrc) ) s.append(::ship_connector_t.find_anchorage(fsrc, planned_harbour, planned_harbour_flat, c_harbour_tiles) ) e.append(::ship_connector_t.find_anchorage(fdest, planned_harbour, planned_harbour_flat, c_harbour_tiles) ) // init search prepare_search() for(local i=0; i<2; i++) { foreach (e in e[i]) { targets.append(e); } } if (targets.len() == 0) { route = [] return } compute_bounding_box() for(local i=0; i<2; i++) { foreach (s in s[i]) { local dist = estimate_distance(s) add_to_open(ab_node(s, null, 1, dist+1, dist, 0), dist+1) } } search() print("End amphibious route search") } } simutrans-124.5/simutrans/ai/sqai/factorysearcher.nut000066400000000000000000000320171520675460500230560ustar00rootroot00000000000000/** * Class that searches for usefull unbuild factory connections. * Depending on player number uses two different methods: * 1) Method that tries to complete a factory tree (taken from the c++ implementation of the freight AI in player/ai_goods.cc * 2) A demand-driven method. */ class factorysearcher_t extends manager_t { froot = null // factory_x, complete this tree method = -1 factory_iterator = null constructor() { base.constructor("factorysearcher_t") debug = false ::factorysearcher = this } function get_next_end_consumer() { // iterate the factory_iterator, which is a generator if (factory_iterator == null || typeof(factory_iterator) != "generator") { // this is a generator factory_iterator = factory_iteration() } if (factory_iterator.getstatus() != "dead") { return resume factory_iterator } factory_iterator = null return null } function factory_iteration() { local list = factory_list_x() foreach(factory in list) { if (factory.output.len() == 0) { yield factory } } } function work() { if (method < 0) { method = our_player_nr % 2; froot = null } if (method == 0) { // traditional method, taken from C++ implementation // of Freight AI // root still has missing links? if (froot && count_missing_factories(froot) <= 0) { froot = null } // determine new root if (froot == null) { // find factory with incomplete connected tree local min_mfc = 10000; local fab while(fab = get_next_end_consumer()) { local n = count_missing_factories(fab) if ((n > 0) && (n < min_mfc)) { // TODO add some random here min_mfc = n froot = fab } } if (froot) { local fab = froot dbgprint("Choose consumer " + fab.get_name() + " at " + fab.x + "," + fab.y + ", which has " + min_mfc + " missing links") } } // nothing found?? if (froot==null) return r_t(RT_DONE_NOTHING); dbgprint("Connect " + froot.get_name() + " at " + froot.x + "," + froot.y) // find link to connect if (!plan_missing_link(froot)) { dbgprint(".. no missing link") // no missing link found - reset froot froot = null return r_t(RT_SUCCESS) } return r_t(RT_PARTIAL_SUCCESS) } else { // demand-driven method // plan root tree if (froot && plan_increase_consumption(froot) <= 0) { froot = null } // determine new root if (froot == null) { local fab if (fab = get_next_end_consumer()) { local n = plan_increase_consumption(fab) return r_t( n>0 ? RT_PARTIAL_SUCCESS : RT_SUCCESS) } } } return r_t(RT_SUCCESS) } /** * Creates the planner node */ static function plan_connection(fsrc, fdest, freight) { if (industry_manager.get_link_state(fsrc, fdest, freight) != industry_link_t.st_free) { dbgprint("Link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y + " state is " + industry_manager.get_link_state(fsrc, fdest, freight) ); return false } dbgprint("Close link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y) industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_planned); local icp = industry_connection_planner_t(fsrc, fdest, freight); append_child(icp) return true } /** * @returns -1 if factory tree is incomplete, otherwise number of missing connections */ // TODO cache the results per factory static function count_missing_factories(fab, indent = "") { // source of raw material? if (fab.input.len() == 0) return 0; local end_consumer = fab.output.len() == 0 // build list of supplying factories local suppliers = []; foreach(c in fab.get_suppliers()) { suppliers.append( factory_x(c.x, c.y) ); } local count = 0; local g_atleastone = false; // iterate all input goods and search for supply foreach(good, islot in fab.input) { // test for in-storage or in-transit goods local st = islot.get_storage() local it = islot.get_in_transit() if (st[0] + st[1] + it[0] + it[1] > 0) { // something stored/in-transit in last and current month // no need to search for more supply g_atleastone = true continue } // there is a complete tree to produce this good local g_complete = false; // minimum of missing links for one input good local g_count = 10000; foreach(s in suppliers) { if (good in s.output) { // check state of connection local state = industry_manager.get_link_state(s, fab, good); if (state == industry_link_t.st_failed) { continue // foreach } if (state != industry_link_t.st_free) { // planned / built -> nothing missing g_complete = true g_count = 0 continue } local n = count_missing_factories(s, indent + " "); if ( n<0) { // incomplete tree } else { // complete tree g_complete = true; g_count = min(g_count, n+1) } } } if (!g_complete && !end_consumer) { dbgprint(indent + "No supply of " + good + " for " + fab.get_name()) // no suppliers for this good return -1 } g_atleastone = g_atleastone || g_complete if (!end_consumer) { count += g_count // sum missing links } else { if (g_count > 0 && (count == 0 || g_count < count)) { count = g_count; } } dbgprint(indent + "Supply of " + good + " for " + fab.get_name() + " has " + g_count + " missing links") } if (end_consumer && !g_atleastone) { dbgprint(indent + "No supply for " + fab.get_name()) count = -1 } dbgprint(indent + "Factory " + fab.get_name() + " at " + fab.x + "," + fab.y + " has " + count + " missing links") return count } /** * find link to connect in tree of factory @p fab. * sets fsrc, fdest, lgood if true was returned * @returns true if link is found */ function plan_missing_link(fab, indent = "") { dbgprint(indent + "Missing link for factory " + fab.get_name() + " at " + fab.x + "," + fab.y) // source of raw material? if (fab.input.len() == 0) return false; // build list of supplying factories local suppliers = []; foreach(c in fab.get_suppliers()) { suppliers.append( factory_x(c.x, c.y) ); } local count = 0; // iterate all input goods and search for supply foreach(good, islot in fab.input) { // check for current supply if ( 4*(islot.get_storage()[0] + islot.get_in_transit()[0]) > islot.max_storage) { dbgprint(indent + ".. enough supply of " + good) continue } // find suitable supplier foreach(s in suppliers) { if ( !(good in s.output)) continue; // connection forbidden? planned? built? local state = industry_manager.get_link_state(s, fab, good) if (state != industry_link_t.st_free) { if (state == industry_link_t.st_built || state == industry_link_t.st_planned) { dbgprint(indent + ".. connection for " + good + " from " + s.get_name() + " to " + fab.get_name() + " already " + (state == industry_link_t.st_built ? "built" : "planned") ) break } continue // if connection state is 'failed' } local oslot = s.output[good] dbgprint(indent + ".. Factory " + s.get_name() + " at " + s.x + "," + s.y + " supplies " + good) if (8*oslot.get_storage()[0] > oslot.max_storage || !plan_missing_link(s, indent + " ")) { // this is our link dbgprint(indent + ".. plan this connection") plan_connection(s, fab, good) } return true } } return false // all links are connected } /** * Estimates additional possible consumption at this end-consumer factory */ function plan_increase_consumption(fab, indent = "") { // initialize search if (fab.output.len() > 0) { return 0 } local planned = 0; foreach(good, islot in fab.input) { local tree = estimate_consumption(fab, good) // now do some greedy selection: for each producer select enough suppliers planned += plan_consumption_connection(tree, fab, good) } return planned } function plan_consumption_connection(tree, fdest, freight, indent = "") { local planned = 0; local needed = tree.increase while (needed > 0) { local best = null local best_supply = 0 foreach(supplier in tree.suppliers) { local supply = supplier.supply dbgprint(indent + "Needed " + needed + " Provided " + supply ) if (supply > needed ? (best_supply == 0 || supply < best_supply) : supply > best_supply) { best = supplier best_supply = supply } } // go down in tree foreach(good, supplier_slot in best.inputs) { planned += plan_consumption_connection(supplier_slot, best.supplier, good, indent + " ") dbgprint(indent + "Planned for " + best_supply + " (total = " + planned + ")") } // plan this connection if (planned==0) { if (plan_connection(best.supplier, fdest, freight)) { dbgprint(indent + "Planned to consumer ") planned++ } } // disable this tree needed -= best_supply best.supply = 0 } return planned } /** * Estimates additional possible consumption of good at this factory * * Returns tree: * tree.increase - Potential local consumption * tree.supply - Potential supply * tree.basec - Base consumption * tree.suppliers - Array of supplier nodes * [].supplier - Factory * [].supply - Potential production of supplier } - produced by estimate_production * [].inputs - Table Good -> Consumer tree } * * @returns estimated consumption increase */ static function estimate_consumption(fab, prod = null, indent = "") { dbgprint(indent + "Estimates for consumption of " + prod + " at factory " + fab.get_name() + " at " + fab.x + "," + fab.y) // estimate max consumption local islot = fab.input[prod] local max_c = islot.get_base_consumption() // estimate actual consumpion local est_c = estimate_actual_consumption(islot) local increase = max_c - est_c dbgprint(indent + " potential increase of consumption of " + prod + " is " + increase) // iterate suppliers: // calculate potential additional supply (and build it) local tree = { suppliers = [], basec = max_c } // search for supply local supply = 0 foreach(c in fab.get_suppliers()) { local s = factory_x(c.x, c.y) if (prod in s.output) { local state = industry_manager.get_link_state(s, fab, prod) if (state == industry_link_t.st_planned || state == industry_link_t.st_failed) { // failed / planned -> no improvement possible dbgprint(indent + " transport link for " + prod + " is failed/planned" ) continue } local s_tree = estimate_production(s, prod, state == industry_link_t.st_built, indent + " ") s_tree.supplier <- s local more = s_tree.supply supply += more tree.suppliers.append(s_tree) dbgprint(indent + " potential increase of " + prod + " is " + more + " (state =" + state + ")") } } dbgprint(indent + " total additional supply of " + prod + " is " + supply) tree.increase <- min(increase, supply) tree.supply <- supply return tree } /** * Estimates additional possible production of good at this factory * @returns tree, see estimate_consumption */ static function estimate_production(fab, prod, exists, indent = "") { dbgprint(indent + "Estimates for production of " + prod + " at factory " + fab.get_name() + " at " + fab.x + "," + fab.y) local oslot = fab.output[prod] local fac = oslot.get_production_factor() local est_p = estimate_actual_production(oslot, exists) local increase = oslot.get_base_production() - est_p dbgprint(indent + " potential increase of production of " + prod + " is " + increase) // build tree for later planning local tree = { inputs = {}, supply = 0 } if (increase <= 0) { return tree } if (fab.input.len() == 0) { // producer of raw materials tree.supply = ( increase*fac)/100; return tree } // iterate all input goods and search for supply foreach(good, islot in fab.input) { local c_tree = estimate_consumption(fab, good, indent + " ") tree.inputs[good] <- c_tree local con = c_tree.increase local est = (con * fac)/ islot.get_consumption_factor() increase = min(increase, est) } tree.supply = increase return tree } static function estimate_actual_consumption(islot) { local con = islot.get_consumed() local isnew = (con.reduce(sum) - con[0]) == 0; if (!isnew) { // established connection: report max return con.reduce(max) } else { if (con[0] == 0) { // non-existing connection return 0 } else { // new connection: report base return islot.get_base_consumption() } } } static function estimate_actual_production(oslot, exists) { local pro = oslot.get_produced() local isnew = (pro.reduce(sum) - pro[0]) == 0; if (!isnew) { // established connection: report max return pro.reduce(max) } else { if (pro[0] == 0 && !exists) { // non-existing connection return 0 } else { // new connection: report base return oslot.get_base_consumption() } } } } simutrans-124.5/simutrans/ai/sqai/industry_connection_planner.nut000066400000000000000000000203771520675460500255170ustar00rootroot00000000000000/** * Plans connections suggested by factory-searcher. */ // helper class to simulate ways on open water openwater <- { function get_cost() { return 0; } function get_maintenance() { return 0; } function get_name() { return "open water"} } function get_max_convoi_length(wt) { switch(wt) { case wt_road: return settings.get_max_road_convoi_length(); case wt_water: return settings.get_max_ship_convoi_length(); } return 4; } /** * Planer class. */ class industry_connection_planner_t extends manager_t { // Source factory fsrc = null // factory_x // Destination factory fdest = null // factory_x // Freight to be transported freight = null // string prod = -1 // integer constructor(s,d,f) { base.constructor("industry_connection_planner_t"); fsrc = s; fdest = d; freight = f; } /** * Evaluates transport by road and ships. * Returns the corresponding reports. * In addition, returns amphibious_connection_planner_t. * This planner will start to work if the connection by road or ship did not succeed. */ function step() { debug = true local tic = get_ops_total(); print("Plan link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y) // TODO check if factories are still existing // TODO check if connection is plannable // compute monthly production if (prod < 0) { prod = calc_production() } dbgprint("production = " + prod); // road local rprt = plan_simple_connection(wt_road, null, null) if (rprt) { append_report(rprt) } // water rprt = plan_simple_connection(wt_water, null, null) if (rprt) { append_report(rprt) } if (reports.len() == 0) { dbgprint("Set link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y + " to MISSING") industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) return r_t(RT_TOTAL_FAIL) } // deliver it local r = r_t(RT_READY) r.report = get_report() // append a chain of alternative connector nodes local rchain = r.report while (reports.len()>0) { local rep = get_report() if (rep == null) { // may happen if no nice reports are available break; } rchain.action.reports.append( rep ) rchain = rep } local r_amph = report_t() r_amph.action = amphibious_connection_planner_t(fsrc, fdest, freight) if (rchain) { rchain.action.reports.append( r_amph ) } else { r.report = r_amph } local toc = get_ops_total(); print("industry_connection_planner wasted " + (toc-tic) + " ops") return r } /** * Plans a connection using one mode of transport * * If start or target are null then use fsrc/fdest. */ function plan_simple_connection(wt, start, target, distance = 0) { // compute correct distance if (distance == 0) { foreach(i in ["x", "y"]) { distance += abs( (start ? start[i] : fsrc[i]) - (target ? target[i] : fdest[i])) } } if (distance == 0) { // still zero? avoid division by zero in the prototyper distance = 1 } // plan convoy prototype local prototyper = prototyper_t(wt, freight) prototyper.min_speed = 1 prototyper.max_vehicles = get_max_convoi_length(wt) prototyper.max_length = 1 if (wt == wt_water) { prototyper.max_length = 4 } local cnv_valuator = valuator_simple_t() cnv_valuator.wt = wt cnv_valuator.freight = freight cnv_valuator.volume = prod cnv_valuator.max_cnvs = 200 cnv_valuator.distance = distance local bound_valuator = valuator_simple_t.valuate_monthly_transport.bindenv(cnv_valuator) prototyper.valuate = bound_valuator if (prototyper.step().has_failed()) { return null } local planned_convoy = prototyper.best print("best " + planned_convoy.min_top_speed + " / " + planned_convoy.max_speed) // fill in report when best way is found local r = report_t() // plan way local planned_way = null if (wt == wt_water) { planned_way = openwater } else { local way_list = way_desc_x.get_available_ways(wt, st_flat) local best_way = null local best = null foreach(way in way_list) { cnv_valuator.way_maintenance = way.get_maintenance() cnv_valuator.way_max_speed = way.get_topspeed() local test = cnv_valuator.valuate_monthly_transport(planned_convoy) if (best == null || test > best) { best = test best_way = way } } dbgprint("Best value = " + best + " way = " + best_way.get_name()) // valuate again with best way cnv_valuator.way_maintenance = 0 cnv_valuator.way_max_speed = best_way.get_topspeed() planned_way = best_way } // valuate again with best way r.gain_per_m = cnv_valuator.valuate_monthly_transport(planned_convoy) // plan station local planned_station = null if (wt != wt_water) { local station_list = building_desc_x.get_available_stations(building_desc_x.station, wt, good_desc_x(freight)) planned_station = select_station(station_list, planned_convoy.length, planned_convoy.capacity) } else { local station_list = building_desc_x.get_available_stations(building_desc_x.harbour, wt, good_desc_x(freight)) planned_station = select_station(station_list, 1, planned_convoy.capacity) } // plan depot local planned_depot = null { local depot_list = building_desc_x.get_available_stations(building_desc_x.depot, wt, {}) if (depot_list.len()) { planned_depot = depot_list[0] } } if (planned_convoy == null || planned_way == null || planned_station == null || planned_depot == null) { return null } // successfull - complete report r.cost_fix = cnv_valuator.distance * planned_way.get_cost() + 2*planned_station.get_cost() + planned_depot.get_cost() r.cost_monthly = cnv_valuator.distance * planned_way.get_maintenance() + 2*planned_station.get_maintenance() + planned_depot.get_maintenance() r.gain_per_m -= r.cost_monthly // create action node local cn = null switch(wt) { case wt_road: cn = road_connector_t(); break case wt_water: cn = ship_connector_t(); break } cn.fsrc = fsrc cn.fdest = fdest cn.freight = freight cn.planned_way = planned_way cn.planned_station = planned_station cn.planned_depot = planned_depot cn.planned_convoy = planned_convoy if (start) { cn.c_start = [start] print("Connector from " + coord_to_string(start)) } if (target) { cn.c_end = [target] print("Connector to " + coord_to_string(target)) } r.action = cn dbgprint("Plan: way = " + planned_way.get_name() + ", station = " + planned_station.get_name() + ", depot = " + planned_depot.get_name()); dbgprint("Report: gain_per_m = " + r.gain_per_m + ", nr_convoys = " + planned_convoy.nr_convoys + ", cost_fix = " + r.cost_fix + ", cost_monthly = " + r.cost_monthly) dbgprint("Report: dist = " + cnv_valuator.distance+ " way_cost = " + planned_way.get_cost()) dbgprint("Report: station = " + planned_station.get_cost()+ " depot = " + planned_depot.get_cost()) return r } function calc_production() { local src_prod = fsrc.output[freight].get_base_production(); local dest_con = fdest.input[freight].get_base_consumption(); // TODO implement production boost factors dbgprint("production = " + src_prod + " / " + dest_con); return min(src_prod,dest_con) } static function select_station(list, length, capacity) { local station_length = (length + CARUNITS_PER_TILE - 1) / CARUNITS_PER_TILE local capacity = capacity local station_capacity = 0 local station_is_terminus = false local best_station = null foreach(station in list) { local ok = (best_station == null) local s_capacity = station_length * station.get_capacity() if (!ok && station_length == 1) { // prefer terminus ok = station.is_terminus() && !station_is_terminus if (!ok) { // then prefer stations with enough capacity ok = station_capacity < capacity ? station_capacity < s_capacity : capacity < s_capacity && s_capacity < station_capacity } } if (station_length > 1) { // force non-terminus ok = !station_is_terminus } if (ok) { best_station = station station_capacity = station.get_capacity() station_is_terminus = station.is_terminus() } } return best_station } } simutrans-124.5/simutrans/ai/sqai/industry_manager.nut000066400000000000000000000252641520675460500232530ustar00rootroot00000000000000/** * Classes to manage industry connections. */ /** * A link is a connection between two factories. * Save its state here. */ class industry_link_t { f_src = null // factory_x f_dest = null // factory_x freight = null // good_desc_x state = 0 lines = null // array // next check needed if ticks > next_check // state == st_missing: check availability again // state == st_build: check for possible upgrades next_check = 0 // only set for st_missing, next time availability has to be checked static st_free = 0 /// not registered static st_planned = 1 /// link is planned static st_failed = 2 /// construction failed, do not try again static st_built = 3 /// connection successfully built static st_missing = 4 /// missing infrastructure, try again later constructor(s,d,f) { f_src = s f_dest = d freight = good_desc_x(f) lines = [] } function append_line(l) { lines.append(l) } function _save() { return ::saveinstance("industry_link_t", this) } } /** * Manage the links operated by us. */ class industry_manager_t extends manager_t { link_list = null link_iterator = null constructor() { base.constructor("industry_manager_t") link_list = {} ::industry_manager = this } /// Generate unique key from link data static function key(src, des, fre) { return ("freight-" + fre + "-from-" + coord_to_key(src) + "-to-" + coord_to_key(des) ).toalnum() } function set_link_state(src, des, fre, state) { local k = key(src, des, fre) try { link_list[k].state = state } catch(ev) { // not existing - create entry local l = industry_link_t(src, des, fre) l.state = state link_list[k] <- l } if (state == industry_link_t.st_built) { link_list[k].next_check = world.get_time().next_month_ticks local text = "" text = "Transport " + translate(fre) + " from " text += coord(src.x, src.y).href(src.get_name()) + " to " text += coord(des.x, des.y).href(des.get_name()) + "
" } if (state == industry_link_t.st_missing) { link_list[k].next_check = world.get_time().next_month_ticks } } function get_link_state(src, des, fre) { local link = access_link(src, des, fre) return link ? link.state : industry_link_t.st_free } function access_link(src, des, fre) { local k = key(src, des, fre) local res try { res = link_list[k] } catch(ev) { res = null } return res } /** * Loop through all links. */ function work() { // iterate the link_iterator, which is a generator if (link_iterator == null) { // this is a generator link_iterator = link_iteration() } if (link_iterator.getstatus() != "dead") { resume link_iterator } else { link_iterator = null return r_t(RT_SUCCESS); } return r_t(RT_PARTIAL_SUCCESS); } function link_iteration() { foreach(link in link_list) { check_link(link) yield link } } /** * Check link: * - if state is st_missing set state to st_free after some time * - for working links see after their lines */ function check_link(link) { switch(link.state) { case industry_link_t.st_free: case industry_link_t.st_planned: case industry_link_t.st_failed: return case industry_link_t.st_built: if (link.lines.len()==0) return break case industry_link_t.st_missing: if (link.next_check >= world.get_time().ticks) return // try to plan again link.state = industry_link_t.st_free break } // iterate through all lines foreach(line in link.lines) { check_link_line(link, line) } } /** * Manages convoys of one line: withdraw if there are too many, build new ones, upgrade to newer vehicles */ function check_link_line(link, line) { dbgprint("Check line " + line.get_name()) // find convoy local cnv = null local cnv_count = 0 { local list = line.get_convoy_list() cnv_count = list.get_count() if (cnv_count == 0) { return } cnv = list[0] } if (cnv.is_withdrawn()) { // come back later return } // try to upgrade if (cnv.has_obsolete_vehicles() && link.next_check < world.get_time().ticks) { link.next_check = world.get_time().next_month_ticks if (upgrade_link_line(link, line)) { // update successful return } } local lf = link.freight // capacity of convoy local capacity = 0 { local lf = link.freight foreach(v in cnv.get_vehicles()) { local f = v.get_freight() if (lf.is_interchangeable(f)) { capacity += v.get_capacity() } } } dbgprint("Capacity of convoy " + cnv.get_name() + " = " + capacity) dbgprint("Speed of convoy " + cnv.get_name() + " = " + cnv.get_speed()) // iterate through schedule, check for available freight local freight_available = false { local entries = cnv.get_schedule().entries local i = 0; while(i < entries.len() && !freight_available) { local entry = entries[i] // stations on schedule local halt = entry.get_halt(our_player) if (halt == null) continue // next station on schedule local nexthalt = null i++ while(i < entries.len()) { if (nexthalt = entries[i].get_halt(our_player)) break i++ } if (nexthalt == null) { nexthalt = entries[0].get_halt(our_player) } // freight available ? local freight_on_schedule = halt.get_freight_to_halt(lf, nexthalt) local capacity_halt = halt.get_capacity(lf) dbgprint("Freight from " + halt.get_name() + " to " + nexthalt.get_name() + " = " + freight_on_schedule) // either start is 2/3 full or more good available as one cnv can transport freight_available = (3*freight_on_schedule > 2*capacity_halt) || (freight_on_schedule > capacity); } } // calc gain per month of one convoy local gain_per_m = 0 { local p = line.get_profit() gain_per_m = p.reduce(sum) / (p.len() * cnv_count) dbgprint("Gain pm = " + gain_per_m) } // check state if convoys (loading level, stopped, new) local cc_load = 0 local cc_stop = 0 local cc_new = 0 local cc_empty = 0 local cnv_empty_stopped = null { local list = line.get_convoy_list() foreach(c in list) { // convoy empty? local is_empty = c.get_loading_level() == 0 // convoy new? less than 2 months old, and not much transported local d = c.get_traveled_distance() local is_new = (d[0] + d[1] == c.get_distance_traveled_total()) if (is_new) { local t = c.get_transported_goods(); if (t.reduce(sum) >= 2*capacity) { is_new = false } } if (is_new) { cc_new ++ is_empty = false } // new convoys do not count as empty if (is_empty) { cc_empty++ } // convoy stopped? but not for loading local is_stopped = false if (c.get_speed() == 0) { if (c.get_loading_limit() > 0) { // loading cc_load ++ } else { cc_stop ++ is_stopped = true; } } if (is_empty && is_stopped && cnv_empty_stopped==null) { cnv_empty_stopped = c } } } dbgprint("Line: loading = " + cc_load + ", stopped = " + cc_stop + ", new = " + cc_new + ", empty = " + cc_empty) dbgprint("") if (freight_available && cc_new == 0 && cc_stop < 2) { if (gain_per_m > 0) { // directly append // TODO put into report local proto = cnv_proto_t.from_convoy(cnv, lf) local depot = cnv.get_home_depot() local c = vehicle_constructor_t() c.p_depot = depot_x(depot.x, depot.y, depot.z) c.p_line = line c.p_convoy = proto c.p_count = 1 append_child(c) dbgprint("==> build additional convoy") } } if (!freight_available && cnv_count>1 && 2*cc_empty >= cnv_count && cnv_empty_stopped) { // freight, lots of empty and of stopped vehicles // -> something is blocked, maybe we block our own supply? // delete one convoy cnv_empty_stopped.destroy(our_player) dbgprint("==> destroy empty convoy") } dbgprint("") } /** * Upgrade: plan a new convoy type with the prototyper, then * sell existing convoys, create new ones. */ function upgrade_link_line(link, line) { // find convoy local cnv = null { local list = line.get_convoy_list() if (list.get_count() == 0) { // no convois - strange return false } cnv = list[0] } local wt = cnv.get_waytype() // estimate transport volume local transported = line.get_transported_goods().reduce(max) // plan convoy prototype local prototyper = prototyper_t(wt, link.freight.get_name()) // iterate through schedule to estimate distance local dist = 0 { local entries = cnv.get_schedule().entries local halts = [] for(local i=0; i < entries.len(); i++) { if (entries[i].get_halt(our_player)) { halts.append( entries[i] ) } } if (halts.len() < 2) { // not enough halts?? return false } dist = abs(halts.top().x - halts[0].x) + abs(halts.top().y - halts[0].y) for(local i=1; i < halts.len(); i++) { dist = max(dist, abs(halts[i].x - halts[i-1].x) + abs(halts[i].y - halts[i-1].y) ) } } local wt = wt // TODO do something smarter prototyper.min_speed = 1 prototyper.max_vehicles = get_max_convoi_length(wt) prototyper.max_length = 1 if (wt == wt_water) { prototyper.max_length = 4 } local cnv_valuator = valuator_simple_t() cnv_valuator.wt = wt cnv_valuator.freight = link.freight.get_name() cnv_valuator.volume = transported cnv_valuator.max_cnvs = 200 cnv_valuator.distance = dist local bound_valuator = valuator_simple_t.valuate_monthly_transport.bindenv(cnv_valuator) prototyper.valuate = bound_valuator if (prototyper.step().has_failed()) { return false } local planned_convoy = prototyper.best // check whether different from current convoy local cnv_veh = cnv.get_vehicles() local pro_veh = planned_convoy.veh local different = cnv_veh.len() != pro_veh.len() for(local i=0; i=0 && y>=0) area.append( (x << 16) + y ); } } } // sort sleep() area.sort() return area } static function find_empty_place(area, target) { // find place closest to target local tx = target.x local ty = target.y local best = null local dist = 10000 // check for flat and empty ground for(local i = 0; i0 && h == area[i-1]) continue; local x = h >> 16 local y = h & 0xffff if (world.is_coord_valid({x=x,y=y})) { local tile = square_x(x, y).get_ground_tile() if (tile.is_empty() && tile.get_slope()==0) { local d = abs(x - tx) + abs(y - ty) if (d < dist) { dist = d best = tile } } } } return best } static function _find_places(area, test /* function */) { local list = [] // check for flat and empty ground for(local i = 0; i0 && h == area[i-1]) continue; local x = h >> 16 local y = h & 0xffff if (world.is_coord_valid({x=x,y=y})) { local tile = square_x(x, y).get_ground_tile() if (test(tile)) { list.append(tile) } } } return list.len() > 0 ? list : [] } static function find_empty_places(area) { return _find_places(area, _tile_empty) } static function _tile_empty(tile) { return tile.is_empty() && tile.get_slope()==0 } static function _tile_empty_or_field(tile) { return tile.get_slope()==0 && (tile.is_empty() || tile.find_object(mo_field)) } static function find_water_places(area) { return _find_places(area, _tile_water) } static function _tile_water(tile) { return tile.is_water() && (tile.find_object(mo_building)==null) && (tile.find_object(mo_depot_water)==null) } static function _tile_water_way(tile) { if (tile.is_water()) { return true // (tile.find_object(mo_building)==null) && (tile.find_object(mo_depot_water)==null) } else { foreach(obj in tile.get_objects()) { try { if (obj.get_type() != mo_way) continue; if (obj.get_waytype() == wt_water) { return obj.get_desc().get_topspeed() > 5 } } catch(ev) { // object gone, most likely vehicle that moved on } } } return false } static function find_station_place(factory, target, unload = false) { if (unload) { // try unload station from station manager local res = ::station_manager.access_freight_station(factory).road_unload if (res) { return [res] } } local can_delete_fields = factory.get_field_count() > factory.get_min_field_count() local area = get_tiles_near_factory(factory) if (can_delete_fields) { return _find_places(area, _tile_empty_or_field); } else { return find_empty_places(area) } } /** * Can harbour of length @p len placed at @p pos (land tile!) in direction @p d. */ static function check_harbour_place(pos, len, d /* direction */) { local from = pos for(local i = 0; i0 ? wt_water : wt_all, d) if (to && _tile_water(to) && to.can_remove_all_objects(our_player)==null) { from = to } else { return false } } return true } } simutrans-124.5/simutrans/ai/sqai/prototyper.nut000066400000000000000000000164271520675460500221300ustar00rootroot00000000000000/** * Classes to plan a convoy prototype given some constraints. */ /** * Prototype of a convoy. */ class cnv_proto_t { // properties weight = 0 // weight (fully loaded) power = 0 // total power min_top_speed = 0 // top speed when fully loaded max_speed = -1 // speed limit length = 0 // length of convoy in internal units (one tile = 16 units) capacity = 0 // capacity of convoy for desired freight maintenance = 0 // maintenance cost per month running_cost = 0 // running cost per tile price = 0 // price missing_freight = true // convoy misses vehicles to transport desired freight veh = null // the vehicles of the convoy (array) // set by valuator nr_convoys = 0 // number of convoys that should be built constructor() { veh = [] } /** * Append vehicle to this prototype. * Desired freight is given as parameter freight. */ function append(newveh, freight) { local cnv = getclass().instance() cnv.constructor() cnv.veh.extend(veh) cnv.veh.append(newveh) cnv.weight = weight + freight.get_weight_per_unit() * newveh.get_capacity() + newveh.get_weight() cnv.power = power + newveh.get_power() if (max_speed >= 0) { cnv.max_speed = min(max_speed, newveh.get_topspeed()) } else { cnv.max_speed = newveh.get_topspeed() } cnv.length = length + newveh.get_length() local fits = newveh.get_freight().is_interchangeable(freight) cnv.missing_freight = missing_freight && (newveh.get_capacity()==0 || !fits) cnv.min_top_speed = convoy_x.calc_max_speed(cnv.power, cnv.weight, cnv.max_speed) cnv.capacity = capacity + (fits ? newveh.get_capacity() : 0) cnv.maintenance = maintenance + newveh.get_maintenance() cnv.running_cost = running_cost + newveh.get_running_cost() cnv.price = price + newveh.get_cost() return cnv } static function from_convoy(cnv, freight) { local p = cnv_proto_t() local list = cnv.get_vehicles() foreach(v in list) { p = p.append(v, freight) } return p } function _save() { return ::saveinstance("cnv_proto_t", this) } } /** * Class to find the best prototype. * * Iterates through all possible combinations. * This can take a very long time for e.g. passenger trains. * Some means to shorten the computation are required. * * If a prototype is valid, it is passed to the function valuate. * The prototype with largest score wins (or the first one if valuate == null). */ class prototyper_t extends node_t { wt = 0 // way-type freight = 0 // freight to be transported max_vehicles = 0 // maximum of number of vehicles in this prototype max_length = 0 // maximum lenght of convoy in number of tiles min_speed = 0 // minimum speed valuate = null // valuate function, takes prototype, returns number (null -> first valid prototype is returned) best = null // the best prototype up to now best_value = 0 // and its score constructor(w, /*string*/f) { base.constructor("prototyper"); wt = w freight = good_desc_x(f) } /** * Depth-first search. * Takes constraints into account. Calls valuate. */ function step() { // list of all vehicles local list = vehicle_desc_x.get_available_vehicles(wt) // vehicles that can be put first local list_first = [] // other vehicles: powered, no capacity (tenders), matching freight local list_other = [] // preprocess foreach(veh in list) { local first = veh.can_be_first() local fits = veh.get_freight().is_interchangeable(freight) local pwer = veh.get_power()>0 local none = veh.get_freight().get_name()=="None" || veh.get_capacity()==0 // use vehicles that can carry freight // or that are powered and have no freight capacity if (fits || (pwer && none) ) { if (first) { list_first.append(veh) } list_other.append(veh) } } // array of lists we try to iterate local it_lists = []; it_lists.resize(max_vehicles+1) local it_ind = []; it_ind.resize(max_vehicles+1) // current convoy candidate - array of desc local cnv = []; cnv.resize(max_vehicles+1) // initialize cnv[0] = cnv_proto_t() it_ind[1] = -1 it_lists[1] = list_first // iterating ind-th position in convoy local ind = 1 while(true) { it_ind[ind] ++ // done with iterating? if (it_ind[ind] >= it_lists[ind].len() ) { if (ind>1) { ind-- continue // iterating position ind-1 } else { break // end of the iteration } } local test = it_lists[ind][ it_ind[ind] ] // check couplings if ( ind==1 ? !test.can_be_first() : !vehicle_desc_x.is_coupling_allowed(cnv[ind-1].veh.top(), test) ) { continue; } // append cnv[ind] = cnv[ind-1].append(test, freight) local c = cnv[ind] // check constraints // .. length local l = (ind > 1 ? cnv[ind-1].length : 0) + max( CARUNITS_PER_TILE/2, test.get_length()); if (l > CARUNITS_PER_TILE*max_length) { continue; } // check if convoy finished if (test.can_be_last() && !c.missing_freight && c.min_top_speed >= min_speed) { // evaluate this candidate if (valuate) { local value = valuate.call(getroottable(), c) if (best==null || value > best_value) { best = c best_value = value } } else { // no valuator function -> take first valid convoy and return best = c; break } } // move on to next position if (ind >= max_vehicles) { continue; } ind++ local list_succ = test.get_successors() it_lists[ind] = list_succ.len()==0 ? list_other : list_succ it_ind[ind] = -1 } if (best) { foreach(ind, test in best.veh) { print("Best[" + ind + "] = " + test.get_name()) } return r_t(RT_SUCCESS) } return r_t(RT_ERROR) } } /** * A simple valuate function. */ class valuator_simple_t { wt = 0 // way-type freight = null // freight to be transported volume = 0 // monthly transport volume max_cnvs = 0 // maximum number of convoys distance = 0 way_max_speed = -1 // speed limit of planned way way_maintenance = 0 // maintenance of planned way /** * Estimates gain per month */ function valuate_monthly_transport(cnv) { local speed = way_max_speed > 0 ? min(way_max_speed, cnv.min_top_speed) : cnv.min_top_speed local frev = good_desc_x(freight).calc_revenue(wt, speed) local capacity = cnv.capacity // tiles per month of one convoy local tpm = convoy_x.speed_to_tiles_per_month(speed) / 2 + 1 // needed convoys to transport everything local n1 = max(1, volume * 2 * distance / (tpm * cnv.capacity)) // realistic number of convoys local ncnv = min(n1, min(max_cnvs, max(distance / 8, 3) ) ) cnv.nr_convoys = ncnv if (way_max_speed > 0) { // correction factor to prefer faster ways: // factor = 0 .. if 2*distance < ncnv // factor = 1 .. if distance/3 > ncnv // linear interpolated in between // without scaling almost always the cheapest way is chosen ... local factor = max(0, min(10*distance, 6*(2*distance - ncnv) ) ); // rescale tiles per month tpm = (tpm*factor) / (10*distance); } // monthly costs and revenue local value = ncnv*( (frev*cnv.capacity+1500)/3000*tpm - cnv.running_cost*tpm - cnv.maintenance) - distance * way_maintenance return value } function _save() { return ::saveinstance("valuator_simple_t", this) } } simutrans-124.5/simutrans/ai/sqai/road_connector.nut000066400000000000000000000156271520675460500227010ustar00rootroot00000000000000class road_connector_t extends manager_t { // input data fsrc = null fdest = null freight = null planned_way = null planned_station = null planned_depot = null planned_convoy = null finalize = true // step-by-step construct the connection phase = 0 // can be provided optionally c_start = null // array c_end = null // array // generated data c_depot = null c_sched = null c_line = null c_cnv = null constructor() { base.constructor("road_connector_t") debug = true } function work() { // TODO check if child does the right thing local pl = our_player local tic = get_ops_total(); switch(phase) { case 0: // find places for stations if (c_start == null) { c_start = ::finder.find_station_place(fsrc, fdest) } if (c_start && c_end == null) { c_end = ::finder.find_station_place(fdest, c_start, finalize) } if (c_start.len()>0 && c_end.len()>0) { phase ++ } else { print("No station places found") return error_handler() } case 1: // build way { sleep() local d = pl.get_current_cash(); local err = construct_road(pl, c_start, c_end, planned_way ) print("Way construction cost: " + (d-pl.get_current_cash()) ) if (err) { print("Failed to build way from " + coord_to_string(c_start[0])+ " to " + coord_to_string(c_end[0])) return error_handler() } phase ++ } case 2: // build station { local err = command_x.build_station(pl, c_start, planned_station ) if (err) { print("Failed to build station at " + coord_to_string(c_start)) gui.add_message_at(pl, "Failed to build road station at " + coord_to_string(c_start) + "\n" + err, c_start) return error_handler() } local err = command_x.build_station(pl, c_end, planned_station ) if (err) { gui.add_message_at(pl, "Failed to build road station at " + coord_to_string(c_end) + "\n" + err, c_end) print("Failed to build station at " + coord_to_string(c_end)) return error_handler() } if (finalize) { // store place of unload station for future use local fs = ::station_manager.access_freight_station(fdest) if (fs.road_unload == null) { fs.road_unload = c_end print( recursive_save({unload = c_end}, "\t\t\t", []) ) } } phase ++ } case 3: // find depot place { local err = construct_road_to_depot(pl, c_start, planned_way) if (err) { print("Failed to build depot access from " + coord_to_string(c_start)) return error_handler() } phase += 2 } case 5: // build depot { // depot already existing ? if (c_depot.find_object(mo_depot_road) == null) { // no: build local err = command_x.build_depot(pl, c_depot, planned_depot ) if (err) { print("Failed to build depot at " + coord_to_string(c_depot)) return error_handler() } if (finalize) { // store depot location local fs = ::station_manager.access_freight_station(fsrc) if (fs.road_depot == null) { fs.road_depot = c_depot } } } phase ++ } case 6: // create schedule { local sched = schedule_x(wt_road, []) sched.entries.append( schedule_entry_x(c_start, 100, 0) ); sched.entries.append( schedule_entry_x(c_end, 0, 0) ); c_sched = sched phase ++ } case 7: // create line and set schedule { pl.create_line(wt_road) // find the line - it is a line without schedule and convoys local list = pl.get_line_list() foreach(line in list) { if (line.get_waytype() == wt_road && line.get_schedule().entries.len()==0) { // right type, no schedule -> take this. c_line = line break } } // set schedule c_line.change_schedule(pl, c_sched); phase ++ } case 8: // append vehicle_constructor { local c = vehicle_constructor_t() c.p_depot = depot_x(c_depot.x, c_depot.y, c_depot.z) c.p_line = c_line c.p_convoy = planned_convoy c.p_count = min(planned_convoy.nr_convoys, 3) append_child(c) local toc = get_ops_total(); print("road_connector wasted " + (toc-tic) + " ops") phase ++ return r_t(RT_PARTIAL_SUCCESS) } case 9: // build station extension } if (finalize) { industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_built) } industry_manager.access_link(fsrc, fdest, freight).append_line(c_line) return r_t(RT_TOTAL_SUCCESS) } function error_handler() { local r = r_t(RT_TOTAL_FAIL) // TODO rollback if (reports.len()>0) { // there are alternatives print("Delivering alternative connector") r.report = reports.pop() if (r.report.action && r.report.action.getclass() == amphibious_connection_planner_t) { print("Delivering amphibious_connection_planner_t") r.node = r.report.action r.report = null } } else { industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_failed); } return r } function construct_road(pl, starts, ends, way) { local as = astar_builder() as.builder = way_planner_x(pl) as.way = way as.builder.set_build_types(way) as.bridger = pontifex(pl, way) if (as.bridger.bridge == null) { as.bridger = null } local res = as.search_route(starts, ends) if ("err" in res) { return res.err } c_start = res.start c_end = res.end } function construct_road_to_depot(pl, start, way) { local as = depot_pathfinder() as.builder = way_planner_x(pl) as.way = way as.builder.set_build_types(way) local res = as.search_route(start) if ("err" in res) { return res.err } local d = res.end c_depot = tile_x(d.x, d.y, d.z) } } class depot_pathfinder extends astar_builder { function estimate_distance(c) { local t = tile_x(c.x, c.y, c.z) if (t.is_empty() && t.get_slope()==0) { return 0 } local depot = t.find_object(mo_depot_road) if (depot && depot.get_owner().nr == our_player_nr) { return 0 } return 10 } function add_to_open(c, weight) { if (c.dist == 0) { // test for depot local t = tile_x(c.x, c.y, c.z) if (t.is_empty()) { // depot not existing, we must build, increase weight weight += 25 * cost_straight } } base.add_to_open(c, weight) } function search_route(start) { prepare_search() local dist = estimate_distance(start) add_to_open(ab_node(start, null, 1, dist+1, dist, 0), dist+1) search() if (route.len() > 0) { for (local i = 1; i0 && c_end.len()>0) { phase ++ } else { print("No station places found") return error_handler() } case 2: // find path between both factories { local err = find_route() if (err) { print("No way from " + coord_to_string(c_start[0])+ " to " + coord_to_string(c_end[0])) return error_handler() } phase ++ } case 3: // build harbour { local key local err = null { key = coord3d_to_key(c_start[0]) if (key in c_harbour_tiles) { err = build_harbour(c_harbour_tiles[key], c_start) } } if (err == null) { key = coord3d_to_key(c_end[0]) if (key in c_harbour_tiles) { err = build_harbour(c_harbour_tiles[key], c_end) } } if (err) { print("Failed to build harbour at " + key + " / " + err) return error_handler() } c_harbour_tiles = null phase ++ } case 4: // find route again after harbour was built { if (c_start.len()>1 || c_end.len()>1) { local err = find_route() if (err) { print("No way2 from " + coord_to_string(c_start[0])+ " to " + coord_to_string(c_end[0])) return error_handler() } } phase ++ } case 5: // build depot { // depot already existing ? if (c_depot.find_object(mo_depot_water) == null) { // no: build local err = command_x.build_depot(pl, c_depot, planned_depot ) if (err) { print("Failed to build depot at " + coord_to_string(c_depot)) return error_handler() } if (finalize) { // store depot location local fs = ::station_manager.access_freight_station(fsrc) if (fs.ship_depot == null) { fs.ship_depot = c_depot } } } phase ++ } case 6: // create schedule { local sched = schedule_x(wt_water, []) sched.entries.append( schedule_entry_x(c_start[0], 100, 0) ); sched.entries.append( schedule_entry_x(c_end[0], 0, 0) ); c_sched = sched phase ++ } case 7: // create line and set schedule { pl.create_line(wt_water) // find the line - it is a line without schedule and convoys local list = pl.get_line_list() foreach(line in list) { if (line.get_waytype() == wt_water && line.get_schedule().entries.len()==0) { // right type, no schedule -> take this. c_line = line break } } // set schedule c_line.change_schedule(pl, c_sched); phase ++ } case 8: // append vehicle_constructor { local c = vehicle_constructor_t() c.p_depot = depot_x(c_depot.x, c_depot.y, c_depot.z) c.p_line = c_line c.p_convoy = planned_convoy c.p_count = min(planned_convoy.nr_convoys, 3) append_child(c) local toc = get_ops_total(); print("ship_connector wasted " + (toc-tic) + " ops") phase ++ return r_t(RT_PARTIAL_SUCCESS) } case 9: // build station extension } if (finalize) { industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_built) } industry_manager.access_link(fsrc, fdest, freight).append_line(c_line) return r_t(RT_TOTAL_SUCCESS) } function error_handler() { local r = r_t(RT_TOTAL_FAIL) // TODO rollback if (reports.len()>0) { // there are alternatives print("Delivering alternative connector") r.report = reports.pop() if (r.report.action && r.report.action.getclass() == amphibious_connection_planner_t) { print("Delivering amphibious_connection_planner_t") r.node = r.report.action r.report = null } } else { industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_failed); } return r } static function find_anchorage(factory, planned_station, planned_harbour_flat, c_harbour_tiles) { // try to find tiles already covered by some harbours local tile_list = ::finder.find_water_places( ::finder.get_tiles_near_factory(factory) ) local halt_list = factory.get_halt_list() local anch = [] if (tile_list.len()>0 && halt_list.len()>0) { foreach(tile in tile_list) { local h = halt_x.get_halt(tile, our_player) if (h) { foreach(halt in halt_list) { if ( (h <=> halt) == 0) { anch.append(tile) } } } } } if (anch.len()>0) { return anch } // find places to build harbour if (tile_list.len()>0) { foreach(tile in tile_list) { for(local d = 1; d<16; d*=2) { local to = tile.get_neighbour(wt_all, d) if (to && get_harbour_type_for_tile(to, planned_station, planned_harbour_flat, d) > 0) { anch.append(tile) c_harbour_tiles[coord3d_to_key(tile)] <- to break } } } } return anch } /** * Checks whether we can build harbour: empty tile, slopes, enough space on water * returns type of harbour that can be placed here: 0 - nothing, 1 - harbour, 2 - flat harbour */ static function get_harbour_type_for_tile(tile, planned_harbour, planned_harbour_flat, d_water_to_land /*direction*/) { local type = 0 // 1 - harbour, 2 - flat harbour if (!tile.is_empty()) { return 0 } // first slope if (planned_harbour) { if (tile.get_slope() != 0) { // can place on slopes, target tile is sloped and empty -> we can terraform type = 1 } else if (planned_harbour_flat == null && command_x.can_set_slope(our_player, tile, dir.to_slope(d_water_to_land))==null) { // no slope, no flat dock, but terraforming possible type = 1 } } if (planned_harbour_flat) { if (tile.get_slope() == 0) { // flat place type = 2 } else if (planned_harbour == null && command_x.can_set_slope(our_player, tile, 0)==null) { // not flat, not harbour for slopes, can flatten type = 2 } } if (type == 0) { return 0 } // then space local size = type == 1 ? planned_harbour.get_size(0) : planned_harbour_flat.get_size(0) return finder.check_harbour_place(tile, size.x*size.y, dir.backward(d_water_to_land)) ? type : 0 } /** * Checks whether there is already a harbour on this tile. * @returns corresponding halt_x object (or null) */ static function get_harbour_halt(tile) { local halt = tile.get_halt() if (halt && halt.get_owner().nr == our_player_nr) { // our halt local harb = tile.find_object(mo_building) if (harb && (harb.get_desc().get_type()==building_desc_x.harbour || harb.get_desc().get_type()==building_desc_x.flat_harbour) ) { return halt } } return null } /** * Build harbour at @p tile, * replace water with an array containing all water tiles next to the harbour */ function build_harbour(tile, water_arr) { local water = water_arr[0] local err = null local len = 0 local dif = { x=tile.x-water.x, y=tile.y-water.y} print("Place harbour at " + coord3d_to_string(tile) + " to access " + coord3d_to_string(water) ) if (get_harbour_halt(tile)) { // already there } else { local d_water_to_land = coord_to_dir(dif) local type = get_harbour_type_for_tile(tile, planned_station, planned_harbour_flat, d_water_to_land) switch(type) { case 0: { err = "Cannot place harbour here" gui.add_message_at(our_player, "Cannot place any harbour at " + coord_to_string(tile), tile) break } case 1: { // harbour on slope local slope = dir.to_slope(d_water_to_land) // terraform ?? if (tile.get_slope() != slope && tile.get_slope() != 2*slope) { err = command_x.set_slope(our_player, tile, slope ) if (err) gui.add_message_at(our_player, "Failed to change slope at " + coord_to_string(tile) +"\n" + err, tile) } if (err == null) { err = command_x.build_station(our_player, tile, planned_station) if (err) gui.add_message_at(our_player, "Failed to harbour at " + coord_to_string(tile) +"\n" + err, tile) } local size = planned_station.get_size(0) len = size.x*size.y break } case 2: { // flat dock // flatten slope if (tile.get_slope() != 0) { err = command_x.set_slope(our_player, tile, 0 ) if (err) gui.add_message_at(our_player, "Failed to flatten slope at " + coord_to_string(tile) +"\n" + err, tile) } if (err == null) { local rotation = 0 switch (dir.backward(d_water_to_land)) { case 2: rotation = 1 break case 1: rotation = 2 break case 8: rotation = 3 default: } err = command_x.build_station(our_player, tile, planned_harbour_flat, rotation) if (err) gui.add_message_at(our_player, "Failed to build flat harbour at " + coord_to_string(tile) +"\n" + err, tile) } local size = planned_harbour_flat.get_size(0) len = size.x*size.y break } } } if (err) { return err; } // halt at this harbour local harbour_halt = halt_x.get_halt(tile, our_player) water_arr.clear() // all water tiles near harbour for(local l=0; lharbour_halt) != 0) ) { // we do not want to use this halt continue } water_arr.append(to) } } catch(ev) {/* ignore */} } } if (water_arr.len()==0) { // should not happen print("No non-harbour water tiles found near " + coord_to_string(water)) water_arr.append(water) } return null } function find_route() { local as = route_finder_water() local res = as.search_route(c_start, c_end) if ("err" in res) { return res.err } c_start = [res.start ] c_end = [res.end ] local asd = route_finder_water_depot() res = asd.search_route(as.route) if ("err" in res) { return res.err } local d = res.depot c_depot = tile_x(d.x, d.y, d.z) } function repair_keys() { local cht = {} foreach(key,val in c_harbour_tiles) { local rkey = repair_key(key) cht[rkey] <- val } c_harbour_tiles = cht } static function repair_key(key) { // replace ``dd:dd:dd'' by ``coord3d_dd_dd_dd'' local a = split(key, ":-") if (a.len() == 1) { return key } local rkey = "coord3d_" + a[0] for(local i=1; i 0) { return { start = route[ route.len()-1], end = route[0] } } print("No water depot route found") return { err = "No route" } } } class route_finder_water_depot extends route_finder_water { function estimate_distance(c) { local t = tile_x(c.x, c.y, c.z) if (t.is_water() && t.get_objects().get_count()==0) { return 0 } local depot = t.find_object(mo_depot_water) if (depot && depot.get_owner().nr == our_player_nr) { return 0 } return 10 } function add_to_open(c, weight) { if (c.dist == 0) { // test for depot local t = tile_x(c.x, c.y, c.z) if (t.get_objects().get_count()==0) { // depot not existing, we must build, increase weight weight += 25 * cost_straight } } base.add_to_open(c, weight) } function search_route(watertiles) { prepare_search() // do not place depot on route between harbours foreach(w in watertiles) { add_to_close(w) } // add neighboring tiles of route to open list for(local i=0; i 0) { return { depot = route[0] } } print("No water depot route found") return { err = "No route" } } } simutrans-124.5/simutrans/ai/sqai/station_manager.nut000066400000000000000000000024641520675460500230500ustar00rootroot00000000000000class freight_station_t { factory = null // factory_x road_depot = null // tile_x road_unload = null // tile_x ship_depot = null // tile_x constructor(f) { factory = f } function _save() { return ::saveinstance("freight_station_t", this) } } class freight_station_manager_t extends node_t { freight_station_list = null use_raw_names = 0 /// repair old savegames: use untranslated factory name as key constructor(urn) { base.constructor("freight_station_manager_t") freight_station_list = {} ::station_manager = this use_raw_names = urn } /// Generate unique key from link data static function key(factory) { return (factory.get_raw_name() + coord_to_string(factory)).toalnum() } /** * Access freight_station_t data, create node if not existent. */ function access_freight_station(factory) { local k = key(factory) local res try { res = freight_station_list[k] } catch(ev) { local fs = freight_station_t(factory) freight_station_list[k] <- fs res = fs } return res } /// Repair list: use untranslated factory names function repair_keys() { if (use_raw_names == 1) { return } local fsl = {} foreach(val in freight_station_list) { local rkey = key(val.factory) fsl[rkey] <- val } freight_station_list = fsl use_raw_names = 1 } } simutrans-124.5/simutrans/ai/sqai/vehicle_constructor.nut000066400000000000000000000045101520675460500237530ustar00rootroot00000000000000class vehicle_constructor_t extends node_t { // input data p_depot = null // depot_x p_line = null // line_x p_convoy = null // prototyper_t p_count = 0 p_withdraw = false // generated data c_cnv = null c_wt = 0 // step-by-step construct convoys phase = 0 constructor() { base.constructor("vehicle_constructor_t") debug = true } function step() { local pl = player_x(our_player) c_wt = p_convoy.veh[0].get_waytype() switch(phase) { case 0: // create the convoy (and the first vehicles) { p_depot.append_vehicle(pl, convoy_x(0), p_convoy.veh[0]) // find the newly created convoy // it should be the last in the list local cnv_list = p_depot.get_convoy_list() local trythis = cnv_list[cnv_list.len()-1] if (check_convoy(trythis)) { c_cnv = trythis } if (c_cnv == null) { foreach(cnv in cnv_list) { if (check_convoy(cnv)) { c_cnv = cnv break } } } phase ++ } case 1: // complete the convoy { local vlist = c_cnv.get_vehicles() while (vlist.len() < p_convoy.veh.len()) { p_depot.append_vehicle(pl, c_cnv, p_convoy.veh[ vlist.len() ]) vlist = c_cnv.get_vehicles() } phase ++ } case 2: // set line { c_cnv.set_line(pl, p_line) phase ++ } case 3: // withdraw old vehicles { if (p_withdraw) { local cnv_list = p_line.get_convoy_list() foreach(o_cnv in cnv_list) { if (o_cnv.id != c_cnv.id && !o_cnv.is_withdrawn()) { o_cnv.toggle_withdraw(pl) } } p_withdraw = false } phase ++ } case 4: // start { p_depot.start_convoy(pl, c_cnv) p_count -- if (p_count > 0) { phase = 0 return r_t(RT_PARTIAL_SUCCESS) } else { phase ++ } } } return r_t(RT_TOTAL_SUCCESS) } function check_convoy(cnv) { // check whether this convoy is for our purpose if (cnv.get_line() == null && cnv.get_waytype() == c_wt) { // now test for equal vehicles local vlist = cnv.get_vehicles() local len = vlist.len() if (len <= p_convoy.veh.len()) { local equal = true; for (local i=0; equal && i0 ? x : -x } // global variables our_player_nr <- -1 our_player <- null // player_x instance // for single run functions in month month_count <- false month_count_ticks <- world.get_time().next_month_ticks // build check for new lines build_check_month <- world.get_time().ticks // set factory strategie - 0 = traditional method, taken from C++ implementation factory_strategie <- 0 /* * different ticks per month for bits_per_month * * bits_per_month = 16 -> world.get_time().ticks_per_month = 65.536 * bits_per_month = 17 -> world.get_time().ticks_per_month = 131.072 * bits_per_month = 18 -> world.get_time().ticks_per_month = 262.144 * bits_per_month = 19 -> world.get_time().ticks_per_month = 524.288 * bits_per_month = 20 -> world.get_time().ticks_per_month = 1.048.576 * bits_per_month = 21 -> world.get_time().ticks_per_month = 2.097.152 * bits_per_month = 22 -> world.get_time().ticks_per_month = 4.194.304 * bits_per_month = 23 -> world.get_time().ticks_per_month = 8.388.608 * bits_per_month = 24 -> world.get_time().ticks_per_month = 16.777.216 * * set parameter for road lines check * */ road_line_check <- 500000 // convoy and citycar count convoy_count <- null road_convoy_count <- 0 citycar_count <- 0 // the AI is organized as a tree, // all the work is done in the nodes of the tree tree <- {} // nodes with particular jobs factorysearcher <- null industry_manager <- null station_manager <- null // stepping info s <- {} s._step <- 0 s._next_construction_step <- 0 // the table 'persistent' will be saved in the savegame persistent.s <- s // 2.. 14 = 13 names possible_names <- ["Petersil Cars", "Teumer Alp Dream Trucks", "Runk & Strunk Transports", "A. Nach B. Transports", "Interflug Fourwheelers", "Pfarnest International", "Suboptimal Transports", "Conveyor Belts & Braces", "Bucket Brigade Inc.", "Maggikraut AG", "Bugs & Eggs Unlimited", "S. Claus & R. Deer Worldwide", "Leiterwagn & Sons" ] /** * Start-routine. Will be called when AI is initialized. * Parameter: the number of the AI player. */ function start(pl_nr) { init(pl_nr) // set a funny name if (our_player_nr > 1 && our_player_nr-2 < possible_names.len()) { player_x(our_player_nr).set_name( possible_names[our_player_nr-2]); } gui.add_message_at(our_player, "### script version " + ai.version, world.get_time()) print("Act as player no " + our_player_nr + " under the name " + our_player.get_name()) local player_sai_count = 0 for ( local i = 2; i < 16; i++ ) { if ( player_x(i).is_valid() ) { if ( player_x(i).get_type() == 4 ) { player_sai_count++ } } } //build_check_month += abs(player_count / our_player_nr) build_check_month += (abs(player_sai_count / 2) * world.get_time().ticks_per_month) if ( ( player_sai_count % 2 ) == 0 ) { factory_strategie = 1 build_check_month -= world.get_time().ticks_per_month } else { factory_strategie = 0 } gui.add_message_at(our_player, "### player_sai_count " + player_sai_count + " # build_check_month " + build_check_month + " # factory_strategie " + factory_strategie, world.get_time()) } /** * Initialize the tree with basic nodes. */ function init_tree() { if (factorysearcher == null) { factorysearcher = factorysearcher_t() } if (industry_manager == null) { industry_manager = industry_manager_t() } if (!("tree" in persistent)) { tree = manager_t() tree.append_child(factorysearcher) tree.append_child(industry_manager) persistent.tree <- tree } else { if (persistent.tree.getclass() != manager_t) { // upgrade tree = manager_t() foreach(i in ["nodes", "next_to_step"]) { tree[i] = persistent.tree[i] } } else { tree = persistent.tree } } if (!("station_manager" in persistent)) { persistent.station_manager <- freight_station_manager_t(1) } } /** * Called after savegame is loaded. */ function resume_game(pl_nr) { init(pl_nr) s = persistent.s } function init(pl_nr) { debug.set_pause_on_error(true) our_player_nr = pl_nr our_player = player_x(our_player_nr) srand(our_player_nr * 4711) annotate_classes() // sets class name as attribute for all known classes (save.nut) init_tree() } /** * The heart beat of the player. * If the routine will take too much time, execution is suspended and later resumed. * This should be completely transparent to the script. * Then the main program is still responsive. */ function step() { if ( build_check_month <= world.get_time().ticks ) { tree.step() s._step++ // connector node may fail, but may offer alternative connector node local report = tree.get_report() if (report) { factorysearcher.append_report(report) } if (s._step > s._next_construction_step) { local r = factorysearcher.get_report() if (r && r.action) { /* gui.add_message_at(our_player, "####### r.retire_obj " + r.retire_obj, world.get_time()) gui.add_message_at(our_player, "####### world.get_time().raw " + world.get_time().raw, world.get_time()) */ if ( r.retire_time != null && r.retire_time < world.get_time().ticks ) { //gui.add_message_at(our_player, "####### report out of time ", world.get_time()) //gui.add_message_at(our_player, "####### r.retire_time " + r.retire_time, world.get_time()) //gui.add_message_at(our_player, "####### world.get_time().ticks " + world.get_time().ticks, world.get_time()) return } else if ( r.retire_obj != null && r.retire_obj <= world.get_time().raw ) { //gui.add_message_at(our_player, "####### object out of time ", world.get_time()) return } else { print("New report: expected construction cost: " + (r.cost_fix / 100.0)) //gui.add_message_at(our_player, "New report: expected construction cost: " + (r.cost_fix / 100.0), world.get_time()) tree.append_child(r.action) } } s._next_construction_step += 1 + (s._step % 3) } } else { //gui.add_message_at(our_player, " ai step() break : build_check_month = " + build_check_month, world.get_time()) month_check_message() sleep() } } /** * Helper routine: translate 3d-coordinate to string. * This can be used as key in tables. */ function coord3d_to_key(c) { return ("coord3d_" + c.x + "_" + c.y + "_" + c.z).toalnum(); } function coord_to_key(c) { return ("coord_" + c.x + "_" + c.y).toalnum(); } function equal_coord3d(a,b) { return a.x == b.x && a.y == b.y && a.z == b.z } /** * check cash <-> build cost * * @param[in] cost build cost in 1/100 cr */ function is_cash_available(cost) { //gui.add_message_at(our_player, " ***** cash : " + our_player.get_current_cash(), world.get_time()) //gui.add_message_at(our_player, " ***** cost : " + cost, world.get_time()) return cost + 2*our_player.get_current_maintenance() < our_player.get_current_cash()*100 } /** * Called to save into savegame. * Returns string that will be saved. * Here: we turn the persistent table into a string using recursive_save (from script/script_base.nut). */ function save() { local str = "" local tic = get_ops_total() local rem = get_ops_remaining() str = "persistent = " + recursive_save(persistent, "\t", [ persistent ] ) local toc = get_ops_total() print("save used " + (toc-tic) + " ops, remaining = " + rem) return str } /** * Returns random number rand with 0 <= rand < upper */ function myrand(upper) { if (upper <= 1) { return upper-1 } local rem = (RAND_MAX % upper) + 1 local r do { r = rand() } while (r > RAND_MAX - rem) return r % upper } /** * @return ticks for today + @p m months */ function today_plus_months(m) { local time = world.get_time() return time.ticks + m * time.ticks_per_month } /** * This function read full string from ground.outside.pak. * The string split by spaces and the first string (name pakset) return as lower case string. * * @return pakset name (lower case) or unknown */ function get_set_name() { local pakset = get_pakset_name() //* full string from ground.outside.pak if ( pakset != null ) { local s = pakset.find(" ") pakset = pakset.slice(0, s) pakset = pakset.tolower() } else { pakset = "unknown" } return pakset } /** * set global variables citicar and convoy counts * * @param output 0 -> no return : 1 -> return road_car_rate * @return none or calculate road car rate */ function set_map_vehicles_counts(output = 0) { sleep() local citycar_array = world.get_year_citycars() local convoy_array = world.get_convoys() convoy_count = convoy_array[0] citycar_count = citycar_array[0] road_convoy_count = 0 local list = world.get_convoy_list() foreach(cnv in list) { if ( cnv.is_valid() && cnv.get_waytype() == wt_road ) { road_convoy_count++ } } /* gui.add_message_at(our_player, "####### citycars " + citycar_count, world.get_time()) gui.add_message_at(our_player, "####### convoys " + convoy_count, world.get_time()) gui.add_message_at(our_player, "####### road convoys " + road_convoy_count, world.get_time()) */ if ( output == 1 ) { // citycars and road-cnv on map - rate per tile //local citycar_count = world.get_year_citycars() local map_size_tiles = world.get_size().x * world.get_size().y local map_citizens = world.get_citizens() local road_car_rate = (map_citizens[0]/10)/ max((citycar_count + road_convoy_count), 1) return road_car_rate } } simutrans-124.5/simutrans/ai/sqai_rail/astar.nut000066400000000000000000007732601520675460500220270ustar00rootroot00000000000000/** * @file astar.nut * Classes to help with route-searching. * Based on the A* algorithm. */ /** * Nodes for A* */ class astar_node extends coord3d { previous = null // previous node cost = -1 // cost to reach this node dist = -1 // distance to target constructor(c, p, co, d) { x = c.x y = c.y z = c.z previous = p cost = co dist = d } function is_straight_move(d) { return d == dir || (previous && previous.dir == d) } } /** * Class to perform A* searches. * * Derived classes have to implement: * process_node(node): add nodes to open list reachable by node * * To use this: * 1) call prepare_search * 2) add tiles to target array * 3) call compute_bounding_box * 4) add start tiles to open list * 5) call search() * 6) use route */ class astar { closed_list = null // table nodes = null // array of astar_node heap = null // binary heap targets = null // array of coord3d's boundingbox = null // box containing all the targets route = null // route, reversed: target to start // statistics calls_open = 0 calls_closed = 0 calls_pop = 0 // costs - can be fine-tuned cost_straight = 10 cost_curve = 14 constructor() { closed_list = {} heap = simple_heap_x() targets = [] } function prepare_search() { closed_list = {} nodes = [] route = [] heap.clear() targets = [] calls_open = 0 calls_closed = 0 calls_pop = 0 } // adds node c to closed list function add_to_close(c) { closed_list[ coord3d_to_key(c) ] <- 1 calls_closed++ } function test_and_close(c) { local key = coord3d_to_key(c) if (key in closed_list) { return false } else { closed_list[ key ] <- 1 calls_closed++ return true } } function is_closed(c) { local key = coord3d_to_key(c) return (key in closed_list) } // add node c to open list with give weight function add_to_open(c, weight) { local i = nodes.len() nodes.append(c) heap.insert(weight, i) calls_open++ } function search() { // compute bounding box of targets compute_bounding_box() local current_node = null while (!heap.is_empty()) { calls_pop++ local wi = heap.pop() current_node = nodes[wi.value] local dist = current_node.dist // target reached if (dist == 0) break; // already visited previously if (!test_and_close(current_node)) { current_node = null continue; } // investigate neighbours and put them into open list process_node(current_node) current_node = null } route = [] if (current_node) { // found route route.append(current_node) while (current_node.previous) { current_node = current_node.previous route.append(current_node) } } print("Calls: pop = " + calls_pop + ", open = " + calls_open + ", close = " + calls_closed) } /** * Computes bounding box of all targets to speed up distance computation. */ function compute_bounding_box() { if (targets.len()>0) { local first = targets[0] boundingbox = { xmin = first.x, xmax = first.x, ymin = first.y, ymax = first.y } for(local i=1; i < targets.len(); i++) { local t = targets[i] if (boundingbox.xmin > t.x) boundingbox.xmin = t.x; if (boundingbox.xmax < t.x) boundingbox.xmax = t.x; if (boundingbox.ymin > t.y) boundingbox.ymin = t.y; if (boundingbox.ymax < t.y) boundingbox.ymax = t.y; } } } /** * Estimates distance to target. * Returns zero if and only if c is a target tile. */ function estimate_distance(c) { local d = 0 local curved = 0 // distance to bounding box local dx = boundingbox.xmin - c.x if (dx <= 0) dx = c.x - boundingbox.xmax; if (dx > 0) d += dx; else dx = 0; local dy = boundingbox.ymin - c.y if (dy <= 0) dy = c.y - boundingbox.ymax if (dy > 0) d += dy; else dy = 0; if (d > 0) { // cost to bounding box return cost_straight * d + (dx*dy > 0 ? cost_curve - cost_straight : 0); } else { local t = targets[0] d = abs(t.x-c.x) + abs(t.y-c.y) // inside bounding box for(local i=1; i < targets.len(); i++) { local t = targets[i] local dx = abs(t.x-c.x) local dy = abs(t.y-c.y) d = min(d, cost_straight * (dx+dy) + (dx*dy > 0 ? cost_curve - cost_straight : 0)) } } return d } } /** * Class to search a route along existing ways. */ class astar_route_finder extends astar { wt = wt_all constructor(wt_) { base.constructor() wt = wt_ if ( [wt_all, wt_invalid, wt_water, wt_air].find(wt) ) { throw("Using this waytype is going to be inefficient. Use at own risk.") } cost_curve = cost_straight } function process_node(cnode) { local from = tile_x(cnode.x, cnode.y, cnode.z) local back = dir.backward(cnode.dir) // allowed directions local dirs = from.get_way_dirs_masked(wt) for(local d = 1; d<16; d*=2) { // do not go backwards, only along existing ways if ( d == back || ( (dirs & d) == 0) ) { continue } local to = from.get_neighbour(wt, d) if (to) { if (!is_closed(to)) { // estimate moving cost local move = cnode.is_straight_move(d) ? cost_straight : cost_curve local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost //+ dist local node = ab_node(to, cnode, cost, dist, d) add_to_open(node, weight) } } } } // start and end have to be arrays of objects with 3d-coordinates function search_route(start, end) { prepare_search() foreach (e in end) { targets.append(e); } compute_bounding_box() foreach (s in start) { local dist = estimate_distance(s) add_to_open(ab_node(s, null, 1, dist+1, 0, 0), dist+1) } search() if (route.len() > 0) { return { start = route[route.len()-1], end = route[0], routes = route } } print("No route found") return { err = "No route" } } } class ab_node extends ::astar_node { dir = 0 // direction to reach this node flag = 0 // flag internal to the route searcher constructor(c, p, co, d, di, fl=0) { base.constructor(c, p, co, d) dir = di flag = fl } } /** * Helper class to find bridges and spots to place them. */ class pontifex { player = null bridge = null constructor(pl, way) { // print messages box // 1 = erreg // 2 = list bridges local print_message_box = 0 local wt_name = ["", "road", "rail", "water"] if ( print_message_box > 1 ) { gui.add_message_at(pl, "____________ Search bridge ___________", world.get_time()) } player = pl local list = bridge_desc_x.get_available_bridges(way.get_waytype()) local len = list.len() local way_speed = way.get_topspeed() local bridge_min_len = 5 if (len>0) { bridge = list[0] if ( print_message_box == 2 ) { gui.add_message_at(pl, " ***** way : " + wt_name[way.get_waytype()], world.get_time()) gui.add_message_at(pl, " ***** bridge : " + bridge.get_name(), world.get_time()) gui.add_message_at(pl, " ***** get_max_length : " + bridge.get_max_length(), world.get_time()) } for(local i=1; i bridge_min_len || b.get_max_length() == 0 ) { if (bridge.get_topspeed() < way_speed) { if (b.get_topspeed() > bridge.get_topspeed()) { bridge = b } } else { if (way_speed < b.get_topspeed() && b.get_topspeed() < bridge.get_topspeed()) { bridge = b } } } } } if ( print_message_box > 1 ) { gui.add_message_at(pl, " *** bridge found : " + bridge.get_name() + " way : " + wt_name[way.get_waytype()], world.get_time()) gui.add_message_at(our_player, "--------- Search bridge end ----------", world.get_time()) } } function find_end(pos, dir, min_length) { return bridge_planner_x.find_end(player, pos, dir, bridge, min_length) } } /** * Class to search a route and to build a connection (i.e. roads). * Builds bridges. But not tunnels (not implemented). */ class astar_builder extends astar { builder = null bridger = null way = null function process_node(cnode) { local from = tile_x(cnode.x, cnode.y, cnode.z) local back = dir.backward(cnode.dir) for(local d = 1; d<16; d*=2) { // do not go backwards if (d == back) { continue } // continue straight after a bridge if (cnode.flag == 1 && d != cnode.dir) { continue } local to = from.get_neighbour(wt_all, d) if (to) { if (builder.is_allowed_step(from, to) && !is_closed(to)) { // estimate moving cost local move = cnode.is_straight_move(d) ? cost_straight : cost_curve local dist = estimate_distance(to) // is there already a road? if (!to.has_way(wt_road)) { move += 8 } local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, d) add_to_open(node, weight) } // try bridges else if (bridger && d == cnode.dir && cnode.flag != 1) { local len = 1 local max_len = bridger.bridge.get_max_length() do { local to = bridger.find_end(from, d, len) if (to.x < 0 || is_closed(to)) { break } local bridge_len = abs(from.x-to.x) + abs(from.y-to.y) // long bridges bad local bridge_factor = 3 if ( bridge_len > 20 ) { bridge_factor = 4 }/* else if ( bridge_len > 8 ) { bridge_factor = 4 }*/ local move = bridge_len * cost_straight * bridge_factor /*extra bridge penalty */; // set distance to 1 if at a target tile local dist = max(estimate_distance(to), 1) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, d, 1 /*bridge*/) add_to_open(node, weight) len = bridge_len + 1 } while (len <= max_len) } } } } function search_route(start, end, build_route = 1) { if ( start.len() == 0 || end.len() == 0 ) { if ( print_message_box > 0 ) { gui.add_message_at(our_player, " *** invalid tile : start or end ", world.get_time()) } return { err = "No route" } } prepare_search() foreach (e in end) { targets.append(e); } compute_bounding_box() foreach (s in start) { local dist = estimate_distance(s) add_to_open(ab_node(s, null, 1, dist+1, 0, 0), dist+1) } search() local bridge_tiles = 0 local count_tree = 0 if (route.len() > 0) { remove_field( route[0] ) // do not try to build in tunnels local is_tunnel_0 = tile_x(route[0].x, route[0].y, route[0].z).find_object(mo_tunnel) local is_tunnel_1 = is_tunnel_0 local last_treeway_tile = null for (local i = 1; i 1 && i < (route.len()-1) ) { local tx_0 = tile_x(route[i-1].x, route[i-1].y, route[i-1].z) local tx_1 = tile_x(route[i+1].x, route[i+1].y, route[i+1].z) if ( tx_0.find_object(mo_way) != null && tx_1.find_object(mo_way) != null ) { //gui.add_message_at(our_player, " check tx_0 and tx_1 ", t) if ( test_exists_way == null ) { local ty = route[i] local cnv_count = tx_0.find_object(mo_way).get_convoys_passed()[0] + tx_0.find_object(mo_way).get_convoys_passed()[1] if ( last_treeway_tile != null && cnv_count == 0 ) { ty = route[last_treeway_tile] } err = test_select_way(tx_1, tx_0, ty, way.get_waytype()) //gui.add_message_at(our_player, " check tx_0 and tx_1 : test_select_way " + err, t) if ( err ) { check_build_tile = false } err = null } } else if ( test_exists_way != null && test_exists_way.get_waytype() == way.get_waytype() ) { check_build_tile = false } if ( tx_0.find_object(mo_signal) != null ) { check_build_tile = false } } if ( test_exists_way != null && test_exists_way.get_owner() != our_player.nr ) { //&& last_treeway_tile != null //gui.add_message_at(our_player, "test_exists_way " + test_exists_way + " last_treeway_tile " + last_treeway_tile + " test_exists_way.get_waytype() " + test_exists_way.get_waytype() + " !t.is_bridge() " + !t.is_bridge() + " t.get_slope() " + t.get_slope(), t) test_exists_way = null } if ( t.is_bridge() ) { //gui.add_message_at(our_player, " t.is_bridge() " + t.is_bridge(), t) last_treeway_tile = null } if ( i > 2 && test_exists_way != null && last_treeway_tile != null && test_exists_way.get_waytype() == wt_rail && t.get_slope() == 0 ) { //gui.add_message_at(our_player, " (624) ", t) err = test_select_way(route[i], route[last_treeway_tile], route[i-1], way.get_waytype()) if ( err ) { last_treeway_tile = null } else { test_exists_way = null last_treeway_tile = null } err = null } /*if ( way.get_waytype() == wt_rail && !t.is_bridge() && t.get_slope == 0 ) { t = tile_x(route[i-1].x, route[i-1].y, route[i-1].z) d = t.get_way_dirs(way.get_waytype()) if ( dir.is_threeway(d) ) { last_treeway_tile = i - 1 } else { last_treeway_tile = null test_exists_way = null } }*/ if ( test_exists_way != null && ( i < 2 || test_exists_way.get_waytype() == wt_road ) ) { test_exists_way = null } local build_tile = false if ( settings.get_pay_for_total_distance_mode == 2 && test_exists_way == null && check_build_tile ) { err = command_x.build_way(our_player, route[i-1], route[i], way, true) build_tile = true } else if ( test_exists_way == null && check_build_tile ) { err = command_x.build_way(our_player, route[i-1], route[i], way, false) build_tile = true } if (err) { //gui.add_message_at(our_player, "Failed to build " + way.get_name() + " from " + coord_to_string(route[i-1]) + " to " + coord_to_string(route[i]) +"\n" + err, route[i]) // remove way // route[0] to route[i] //err = command_x.remove_way(our_player, route[0], route[i]) remove_wayline(route, (i - 1), way.get_waytype()) } else { t = tile_x(route[i-1].x, route[i-1].y, route[i-1].z) d = t.get_way_dirs(way.get_waytype()) //gui.add_message_at(our_player, " (666) dir.is_threeway(d) " + dir.is_threeway(d), t) if ( dir.is_threeway(d) && way.get_waytype() == wt_rail && build_tile ) { last_treeway_tile = i - 1 } } } else if ( build_route == 0 ) { if ( tile_x(route[i].x, route[i].y, route[i].z).find_object(mo_tree) != null ) { count_tree++ } } //} } else if (route[i-1].flag == 1) { // plan build bridge local b_tiles = 0 // if ( route[i-1].x == route[i].x ) { if ( route[i-1].y > route[i].y ) { b_tiles = (route[i-1].y - route[i].y + 1) bridge_tiles += b_tiles } else { b_tiles = (route[i].y - route[i-1].y + 1) bridge_tiles += b_tiles } } else if ( route[i-1].y == route[i].y ) { if ( route[i-1].x > route[i].x ) { b_tiles = (route[i-1].x - route[i].x + 1) bridge_tiles += b_tiles } else { b_tiles = (route[i].x - route[i-1].x + 1) bridge_tiles += b_tiles } } if ( build_route == 1 ) { // check ground under bridge // check_ground() return true build bridge // check_ground() return false no build bridge local build_bridge = true // check whether the ground can be adjusted and no bridge is necessary // bridge len <= 4 tiles if ( b_tiles < 8 ) { build_bridge = check_ground(tile_x(route[i-1].x, route[i-1].y, route[i-1].z), tile_x(route[i].x, route[i].y, route[i].z), way) //gui.add_message_at(our_player, "check_ground(pos_s, pos_e) --- " + build_bridge, route[i-1]) } if ( build_bridge ) { err = command_x.build_bridge(our_player, route[i-1], route[i], bridger.bridge) if (err) { // check whether bridge exists sleep() local arf = astar_route_finder(wt_road) local res_bridge = arf.search_route([route[i-1]], [route[i]]) if ("routes" in res_bridge && res_bridge.routes.len() == abs(route[i-1].x-route[i].x)+abs(route[i-1].y-route[i].y)+1) { // there is a bridge, continue err = null gui.add_message_at(our_player, "Failed to build bridge from " + coord_to_string(route[i-1]) + " to " + coord_to_string(route[i]) +"\n" + err, route[i]) } else { remove_wayline(route, (i - 1), way.get_waytype()) // remove bridge tiles build by not build bridge } } } } else if ( build_route == 0 ) { } } if (err) { return { err = err } } } return { start = route.top(), end = route[0], routes = route, bridge_lens = bridge_tiles, bridge_obj = bridger.bridge, tiles_tree = count_tree } } print("No route found") return { err = "No route" } } } /* * * */ function test_select_way(start, end, t_end, wt) { //gui.add_message_at(our_player, "start " + coord3d_to_string(start) + " end " + coord3d_to_string(end) + " t_end " + coord3d_to_string(t_end), start) local asf = astar_route_finder(wt_rail) local wayline = asf.search_route([start], [end]) if ( "err" in wayline ) { //gui.add_message_at(our_player, "no route from " + coord3d_to_string(start) + " to " + coord3d_to_string(end) , start) } else { //gui.add_message_at(our_player, "exists route from " + coord3d_to_string(start) + " to " + coord3d_to_string(end) , start) local toolr = command_x(tool_remove_way) toolr.work(our_player, t_end, end, "" + wt) return true } return false } /* * check ground under bridges for terraform and remove bridge * * bridges ramp - ramp * * tiles = array -> way tile - bridge tiles - way tile */ function replace_bridge_to_land(tiles) { // test_tile_is_empty(tile) if ( (tiles[1].is_bridge() && tiles[1].y == tiles[0].y ) ) { if ( tiles[1].x > tiles[0].x ) { // 31-1 : 3-13 local tiles_ground = [] for ( local i = 2; i < tiles.len()-2; i++ ) { tiles_ground = square_x(t_tile[i].x, t_tile[i].y).get_ground_tile() } local terraform_grid_tiles = [] local terraform_action = null // 0 = down ; 1 = up if ( tiles_ground[0].get_slope() == 31 && tiles_ground[1].get_slope() == 1 ) { local tile_a1 = square_x(tiles_ground[0].x, tiles_ground[0].y-1).get_ground_tile() local tile_a1 = square_x(tiles_ground[1].x, tiles_ground[1].y-1).get_ground_tile() local tile_b1 = square_x(tiles_ground[0].x, tiles_ground[0].y+1).get_ground_tile() local tile_b2 = square_x(tiles_ground[1].x, tiles_ground[1].y+1).get_ground_tile() } } else { // 13-3 : 1-31 } } } /* * check ground under bridges * * */ function check_ground(pos_s, pos_e, way) { // 0 = off // 1 = terraform // 2 = ground bridge // 3 = local print_message_box = 0 //gui.add_message_at(our_player, "check_ground(pos_s, pos_e) --- " + coord_to_string(pos_s) + " - " + coord_to_string(pos_e), pos_s) local check_x = 0 local check_y = 0 local f_count = 0 local t_tile = [] if ( pos_s.x == pos_e.x ) { check_x = 0 check_y = 1 if ( pos_s.y > pos_e.y ) { f_count = pos_s.y - pos_e.y - 1 local t = tile_x(pos_e.x + check_x, pos_e.y + check_y, pos_e.z) t_tile.append(t) } else { f_count = pos_e.y - pos_s.y - 1 local t = tile_x(pos_s.x + check_x, pos_s.y + check_y, pos_s.z) t_tile.append(t) } //gui.add_message_at(our_player, "check_ground(pos_s, pos_e) --- pos_s.x == pos_e.x -> " + coord_to_string(t_tile[0]), t_tile[0]) } else if ( pos_s.y == pos_e.y ) { check_y = 0 check_x = 1 if ( pos_s.x > pos_e.x ) { f_count = pos_s.x - pos_e.x - 1 local t = tile_x(pos_e.x + check_x, pos_e.y + check_y, pos_e.z) t_tile.append(t) } else { f_count = pos_e.x - pos_s.x - 1 local t = tile_x(pos_s.x + check_x, pos_s.y + check_y, pos_s.z) t_tile.append(t) } //gui.add_message_at(our_player, "check_ground(pos_s, pos_e) --- pos_s.y == pos_e.y -> " + coord_to_string(t_tile[0]), t_tile[0]) } for ( local i = 1; i < f_count; i++ ) { local t = tile_x(t_tile[i-1].x + check_x, t_tile[i-1].y + check_y, t_tile[i-1].z) t_tile.append(t) } local start_end_slope = 0 if ( tile_x(pos_s.x, pos_s.y, pos_s.z).get_slope() == 0 && tile_x(pos_e.x, pos_e.y, pos_e.z).get_slope() == 0 ) { start_end_slope = 1 } else if ( tile_x(pos_s.x, pos_s.y, pos_s.z).get_slope() > 0 && tile_x(pos_e.x, pos_e.y, pos_e.z).get_slope() == 0 ) { start_end_slope = 1 } else if ( tile_x(pos_s.x, pos_s.y, pos_s.z).get_slope() == 0 && tile_x(pos_e.x, pos_e.y, pos_e.z).get_slope() > 0 ) { start_end_slope = 1 } if (print_message_box == 2) gui.add_message_at(our_player, "(800) start_end_slope " + start_end_slope, world.get_time()) local z = null local z1 = null local terraform_tiles = [] local terraform_grid_tiles = [] local grid_coord = [] local err = null if ( start_end_slope == 1 && pos_s.z == pos_e.z && f_count > 1 ) { for ( local i = 0; i < f_count; i++ ) { // find z coord z = square_x(t_tile[i].x, t_tile[i].y).get_ground_tile() if ( (i + 1) == f_count ) { z1 = square_x(t_tile[i-1].x, t_tile[i-1].y).get_ground_tile() } else { z1 = square_x(t_tile[i+1].x, t_tile[i+1].y).get_ground_tile() } if (print_message_box == 2) gui.add_message_at(our_player, "check_ground bridge - tile_tree = " + tile_tree + " || tile_groundobj = " + tile_groundobj + " tile_moving_object = " + tile_moving_object, z) if ( !z.is_ground() ) { // tile is water return true } else if ( !test_tile_is_empty(z) ) { // tiles not free -> build bridge //gui.add_message_at(our_player, "check_ground bridge - !z.is_empty() = " + !z.is_empty() + " || !z.is_ground() = " + !z.is_ground() + " tile = " + coord_to_string(z), z) return true } else if ( ((pos_s.z-2) == z.z || (pos_s.z-1) == z.z) && z.get_slope() > 0 ) { // tiles free no build bridges -> terraform terraform_tiles.append(z) if (print_message_box == 2) gui.add_message_at(our_player, "(832) check_ground bridge - terraform_tiles.append(z) " + coord3d_to_string(z), z) //gui.add_message_at(our_player, "(832) z.get_slope() " + z.get_slope(), z) // EW slope 37-39 : 13-31 : 31-1 : 3-13 // NS slope 13-39 : 31-37 local z_slope_nr = z.get_slope() local z1_slope_nr = z1.get_slope() local grid_tile = null if ( ((z_slope_nr == 39 || z_slope_nr == 31) && z1_slope_nr == 37) || (z_slope_nr == 37 && (z1_slope_nr == 39 || z1_slope_nr == 31)) ) { if ( z1_slope_nr == 39 ) { grid_tile = tile_x(z1.x, z1.y+1, z1.z) } else if ( z_slope_nr == 39 ) { grid_tile = tile_x(z.x, z.y+1, z.z) } if ( z1_slope_nr == 31 ) { grid_tile = tile_x(z1.x+1, z1.y, z1.z) } else if ( z_slope_nr == 31 ) { grid_tile = tile_x(z.x+1, z.y, z.z) } } if ( (z_slope_nr == 13 && (z1_slope_nr == 31 || z1_slope_nr == 39)) || ((z_slope_nr == 31 || z_slope_nr == 39) && z1_slope_nr == 13) ) { if ( z1_slope_nr == 13 ) { grid_tile = tile_x(z1.x, z1.y, z1.z) } else { grid_tile = tile_x(z.x, z.y, z.z) } } if ( (z_slope_nr == 1 && z1_slope_nr == 31) || (z_slope_nr == 31 && z1_slope_nr == 1) ) { if ( z1_slope_nr == 1 ) { grid_tile = tile_x(z1.x, z1.y, z1.z) } else { grid_tile = tile_x(z.x, z.y, z.z) } } if (print_message_box == 3) gui.add_message_at(our_player, "(862) grid_coord.find(coord3d_to_string(grid_tile)) " + grid_coord.find(coord3d_to_string(grid_tile)), grid_tile) if ( grid_tile != null && grid_coord.find(coord3d_to_string(grid_tile)) == null ) { terraform_grid_tiles.append(grid_tile) grid_coord.append(coord3d_to_string(grid_tile)) if (print_message_box == 3) gui.add_message_at(our_player, "(862) check_ground bridge - terraform_grid_tiles.append(grid_tile) " + coord3d_to_string(grid_tile), grid_tile) } /* if ( (z_slope_nr == 3 && z1_slope_nr == 9) || (z_slope_nr == 9 && z1_slope_nr == 3) ) { if ( z1_slope_nr == 9 ) { terraform_grid_tiles.append(tile_x(z1.x+1, z1.y, z1.z)) } else { terraform_grid_tiles.append(tile_x(z.x+1, z.y, z.z)) } } */ } } local terraform_action = 1 // 0 = down ; 1 = up if ( terraform_tiles.len() > 0 && terraform_tiles.len() < 7 ) { local terraform_field = false if ( terraform_grid_tiles.len() > 0 ) { if (print_message_box == 3) gui.add_message_at(our_player, "(873) terraform_grid_tiles.len() " + terraform_grid_tiles.len(), world.get_time()) for ( local i = 0; i < terraform_grid_tiles.len(); i++ ) { if ( pos_s.z > terraform_grid_tiles[i].z ) { err = command_x.grid_raise(our_player, coord3d(terraform_grid_tiles[i].x, terraform_grid_tiles[i].y, terraform_grid_tiles[i].z)) } else { err = command_x.grid_lower(our_player, coord3d(terraform_grid_tiles[i].x, terraform_grid_tiles[i].y, terraform_grid_tiles[i].z)) } if ( err != null ) { terraform_field = true } } } if ( terraform_field ) { for ( local i = 0; i < terraform_tiles.len(); i++ ) { local f = square_x(terraform_tiles[i].x, terraform_tiles[i].y).get_ground_tile() if ( f.z == pos_s.z ) { continue } do { f = square_x(terraform_tiles[i].x, terraform_tiles[i].y).get_ground_tile() err = command_x.set_slope(our_player, f, 82) if ( err != null ) { return false } f = square_x(terraform_tiles[i].x, terraform_tiles[i].y).get_ground_tile() } while(f.z < pos_s.z ) } } err = command_x.build_way(our_player, tile_x(pos_s.x, pos_s.y, pos_s.z), tile_x(pos_e.x, pos_e.y, pos_e.z), way, true) if ( err != null ) { return true } return false } else { return true } } else if ( start_end_slope == 1 && ((pos_s.z-1) == pos_e.z || (pos_s.z+1) == pos_e.z) && f_count < 5 ) { for ( local i = 0; i < f_count; i++ ) { // find z coord z = square_x(t_tile[i].x, t_tile[i].y).get_ground_tile() if (print_message_box == 2) gui.add_message_at(our_player, "(990) check_ground bridge - " + coord_to_string(z), z) if ( !z.is_ground() ) { // tile is water return true } else if ( !test_tile_is_empty(z) ) { // tiles not free -> build bridge if (print_message_box == 2) gui.add_message_at(our_player, "(997) check_ground bridge - !z.is_empty() = " + !z.is_empty() + " || !z.is_ground() = " + !z.is_ground() + " tile = " + coord_to_string(z), z) return true } else if ( ((pos_s.z+1) == z.z || (pos_s.z-1) == z.z || (pos_s.z) == z.z) && z.get_slope() > 0 ) { // tiles free no build bridges -> terraform terraform_tiles.append(z) if (print_message_box == 2) gui.add_message_at(our_player, "(1002) check_ground bridge - terraform_tiles.append(z) " + coord_to_string(z), z) } } if (print_message_box == 1) { local f = null for ( local i = 0; i < terraform_tiles.len(); i++ ) { gui.add_message_at(our_player, "(1009) ---=> terraform_tiles[" + i + "] tile : " + coord3d_to_string(terraform_tiles[i]), world.get_time()) } } /* slope up 83 slope down 82 slope ramp double high up high 1 n 4 s 36 e 28 w 12 slope ramp double high up high 2 n 8 s 72 e 56 w 24 */ if ( ((pos_s.z-1) == pos_e.z || (pos_s.z+1) == pos_e.z) && terraform_tiles.len() > 0 ) { if ( pos_s.is_bridge() ) { err = remove_tile_to_empty(pos_s, way.get_waytype(), 0) } if ( terraform_tiles[0].z == pos_s.z && pos_s.z > pos_e.z && terraform_tiles.len() > 1 ) { terraform_tiles.reverse() } else if ( terraform_tiles[0].z < pos_s.z && pos_s.z > pos_e.z && terraform_tiles.len() > 1 ) { terraform_tiles.reverse() } else if ( terraform_tiles[0].z == pos_s.z && pos_s.z < pos_e.z && pos_s.x > pos_e.x && terraform_tiles.len() > 1 ) { terraform_tiles.reverse() } local slope_id = 0 if ( pos_s.x == pos_e.x && pos_s.y < terraform_tiles[0].y ) { if (print_message_box == 1) gui.add_message_at(our_player, " ---=> (897) tile : " + coord3d_to_string(terraform_tiles[0]), world.get_time()) if ( terraform_tiles[0].z == pos_s.z ) { slope_id = 4 } else { slope_id = 36 } } else if ( pos_s.x == pos_e.x && pos_s.y > terraform_tiles[0].y ) { if (print_message_box == 1) gui.add_message_at(our_player, " ---=> (900) tile : " + coord3d_to_string(terraform_tiles[0]), world.get_time()) if ( terraform_tiles[0].z == pos_s.z ) { slope_id = 36 } else { slope_id = 4 } } else if ( pos_s.y == pos_e.y && pos_s.x < terraform_tiles[0].x ) { if (print_message_box == 1) gui.add_message_at(our_player, " ---=> (903) tile : " + coord3d_to_string(terraform_tiles[0]), world.get_time()) if ( terraform_tiles[0].z == pos_s.z ) { slope_id = 12 } else { slope_id = 28 } } else if ( pos_s.y == pos_e.y && pos_s.x > terraform_tiles[0].x ) { if (print_message_box == 1) gui.add_message_at(our_player, " ---=> (906) tile : " + coord3d_to_string(terraform_tiles[0]), world.get_time()) if ( terraform_tiles[0].z == pos_s.z ) { slope_id = 28 } else { slope_id = 12 } } if ( slope_id > 0 ) { err = command_x.set_slope(our_player, terraform_tiles[0], slope_id) if ( terraform_tiles.len() > 1 ) { err = command_x.set_slope(our_player, terraform_tiles[1], 82) } } err = command_x.build_way(our_player, tile_x(pos_s.x, pos_s.y, pos_s.z), tile_x(pos_e.x, pos_e.y, pos_e.z), way, true) if ( err == null ) { return false } //} else if ( (pos_s.z+1) == pos_e.z && terraform_tiles.len() > 0 ) { } return true }else { return true } } /** * @fn check_tile_end_of_station(direction, count, s_tile) * check tile end of station * * @param direction = 1, 2, 4, 8 * @param count * @param s_tile = station tile : tile_x * * @return tile_x or null */ function check_tile_end_of_station(direction, count, s_tile) { local tx = 0 local ty = 0 if ( direction == 1 || direction == 4 || direction == 5 ) { ty = count } else if ( direction == 2 || direction == 8 || direction == 10 ) { tx = count } local t_square = null switch(direction) { case 1: t_square = square_x(s_tile.x, s_tile.y + ty) break case 2: t_square = square_x(s_tile.x - tx, s_tile.y) break case 4: t_square = square_x(s_tile.x, s_tile.y - ty) break case 8: t_square = square_x(s_tile.x + tx, s_tile.y) break case 5: t_square = square_x(s_tile.x, s_tile.y - ty) break case 10: t_square = square_x(s_tile.x + tx, s_tile.y) break } local tile_coord = coord3d_to_string(s_tile) // debug local t_ground = null // tile out of map if ( !world.is_coord_valid(t_square) ) { return null } t_ground = t_square.get_ground_tile() if ( test_tile_is_empty(t_ground) && t_ground.z > (s_tile.z - 2) && t_ground.z < (s_tile.z + 2) ) { //t_ground.z > s_tile.z return t_ground } return null } /** * terraform tile * * tile = tile_x * ref_hight = target height * */ function terraform_tile(tile, ref_hight) { // messages set 2 local print_message_box = 0 local pl = our_player if ( print_message_box > 0 ) { if ( debug ) ::debug.set_pause_on_error(true) gui.add_message_at(our_player, " ---=> terraform_tile(tile, ref_hight) tile : " + coord3d_to_string(tile) + " target hight : " + ref_hight, world.get_time()) } if ( test_tile_is_empty(tile) && ( tile.get_slope() > 0 || tile.z != ref_hight ) ) { local err = null if ( print_message_box == 2 ) { gui.add_message_at(our_player, " ---=> terraform", world.get_time()) gui.add_message_at(our_player, " ---=> tile z " + tile.z + " to ref_hight " + ref_hight, world.get_time()) } if ( tile.z < ref_hight && tile.z >= (ref_hight - 2) ) { // terraform up if ( print_message_box == 2 ) { gui.add_message_at(our_player, " ---=> tile up to flat ", world.get_time()) } do { err = command_x.set_slope(our_player, tile, 82 ) if ( err != null ) { break } //z = square_x(tile.x, tile.y).get_ground_tile() } while(tile.z < ref_hight ) } else if ( tile.z >= ref_hight || tile.z <= (ref_hight + 1) ) { // terraform down if ( print_message_box == 2 ) { gui.add_message_at(our_player, " ---=> tile down to flat ", world.get_time()) } do { err = command_x.set_slope(pl, tile, 83 ) if ( err != null ) { break } //z = square_x(fields[i].x, fields[i].y).get_ground_tile() } while(tile.z > ref_hight ) // replace water to land if ( tile.is_water() ) { command_x.change_climate_at(our_player, tile, cl_temperate) } } if ( err ) { return false } return true } else if ( test_tile_is_empty(tile) && ( tile.get_slope() == 0 || tile.z == ref_hight ) ) { return true } return false } /** * * */ function set_marker(pos) { local m_tile = null if (type(pos) == "array") { m_tile = pos[0] } else { m_tile = pos } local tile = square_x(m_tile.x, m_tile.y).get_ground_tile() local tool = command_x(tool_set_marker) tool.work(our_player, tile) } /** * Helper class to remove a field at a factory. * Used if no empty spot is available to place a station. */ function remove_field(pos) { local tile = square_x(pos.x, pos.y).get_ground_tile() local tool = command_x(tool_remover) while(tile.find_object(mo_field)) { tool.work(our_player, pos) } } /** * remove way line * route = way line * pos = last field to build * wt = waytype */ function remove_wayline(route, pos, wt, st_len = null) { local tool = command_x(tool_remover) local toolr = command_x(tool_remove_way) //while(tile.find_object(mo_field)) { // tool.work(our_player, pos) //} local l = 0 if ( st_len == null ) { st_len = 6 } if ( debug ) ::debug.set_pause_on_error(true) local new_route_s = null local new_route_e = null local i = pos local test = 0 local tile_treeway = false local way_cnv_count = 0 for ( i; i >= 0; i-- ) { l++ local tile = square_x(route[i].x, route[i].y).get_ground_tile() local next_tile = null if ( i > 0 ) { next_tile = square_x(route[i-1].x, route[i-1].y).get_ground_tile() } local no_bridge = false if ( i > 0 && ( abs(tile.x-next_tile.x) > 1 || abs(tile.y-next_tile.y) > 1 )) { if ( tile.find_object(mo_bridge) == null ) { no_bridge = true gui.add_message_at(our_player, "#1304# remove way by not build bridge " + coord3d_to_string(tile), next_tile) } } local t_field = tile.find_object(mo_way) if ( t_field == null ) { continue } local cnv_count = t_field.get_convoys_passed()[0] + t_field.get_convoys_passed()[1] // test field has way if ( t_field != null && t_field.get_waytype() == wt ) { // test direction next tile // break by direction 7, 11, 13, 14, 15 if ( i > 0 ) { if (dir.is_threeway(next_tile.get_way_dirs(wt)) || t_field.get_owner().nr == 1) { test = 1 tile_treeway = dir.is_threeway(next_tile.get_way_dirs(wt)) gui.add_message_at(our_player, "(1320) dir.is_threeway(next_tile.get_way_dirs(wt) " + dir.is_threeway(next_tile.get_way_dirs(wt)), next_tile) //::debug.pause() } } if ( tile.find_object(mo_building) != null ) { // no remove station if ( l < st_len ) { continue } test = 1 } else if ( wt == wt_road ) { local test_way = tile.find_object(mo_way) //.get_desc() //local tile_coord = coord3d_to_string(tile) if ( test_way.get_owner().nr == our_player_nr ) { // remove player road from tile // not remove public player road from tile if ( (t_field.get_convoys_passed()[0] + t_field.get_convoys_passed()[1]) == 0 ) { tool.work(our_player, tile) } } else { // break public way ( road ) test = 1 } } else { // if ( test == 0 ) { if ( l < st_len ) { way_cnv_count = t_field.get_convoys_passed()[0] + t_field.get_convoys_passed()[1] } // remove way from tile if ( cnv_count == way_cnv_count ) { //::debug.pause() tool.work(our_player, tile) } } /*} else if ( wt == wt_rail && dir.is_threeway(tile.get_way_dirs(wt)) ) { gui.add_message_at(our_player, "dir.is_threeway(next_tile.get_way_dirs(wt) " + dir.is_threeway(next_tile.get_way_dirs(wt)), next_tile) test = 1 */ // no remove tile } else if ( tile.find_object(mo_crossing) != null ) { // test crossing and remove local t_field = tile.get_way(wt) way_cnv_count = t_field.get_convoys_passed()[0] + t_field.get_convoys_passed()[1] //::debug.pause() if ( way_cnv_count == 0 ) { toolr.work(our_player, tile, tile, "" + wt) } //tool.work(our_player, tile) } // break by direction 7, 11, 13, 14, 15 or owner public player next tile if ( test == 1 ) { //::debug.pause() new_route_s = i - 1 i-- break } } if ( test == 1 ) { //test = 0 for ( local j = 0; j < i; j++ ) { test = 1 local tile = square_x(route[j].x, route[j].y).get_ground_tile() local next_tile = null if ( j < i ) { next_tile = square_x(route[j+1].x, route[j+1].y).get_ground_tile() } local t_field = tile.find_object(mo_way) // test field has way if ( t_field != null && t_field.get_waytype() == wt ) { // test direction: if in [7, 11, 13, 14, 15] then dir.is_threeway will find this //local tile_treeway = dir.is_threeway(next_tile.get_way_dirs(wt)) if ( tile.find_object(mo_building) != null ) { // no remove station if ( j < st_len ) { continue } test += 1 } else if ( wt == wt_road ) { //local tile_coord = coord3d_to_string(tile) if ( t_field.get_owner().nr == our_player_nr ) { // remove player road from tile // not remove public player road from tile if ( (t_field.get_convoys_passed()[0] + t_field.get_convoys_passed()[1]) == 0 ) { toolr.work(our_player, tile, tile, "" + wt_road) } } else { // break public way ( road ) test += 1 } /*} else if ( wt == wt_rail && dir.is_threeway(tile.get_way_dirs(wt)) ) { test += 1 ::debug.pause()*/ // no remove tile } else { // remove way from tile local cnv_count = t_field.get_convoys_passed()[0] + t_field.get_convoys_passed()[1] if ( cnv_count == way_cnv_count && !tile_treeway ) { toolr.work(our_player, tile, tile, "" + wt_rail) } else { } } } if ( tile.is_crossing() ) { //::debug.pause() // test crossing and remove local t_field = tile.get_way(wt) way_cnv_count = t_field.get_convoys_passed()[0] + t_field.get_convoys_passed()[1] //gui.add_message_at(our_player, "(1133) crossing way_cnv_count " + way_cnv_count, tile) //::debug.pause() if ( way_cnv_count == 0 ) { toolr.work(our_player, tile, tile, "" + wt) } //tool.work(our_player, tile) } // break by direction 7, 11, 13, 14, 15 if ( test >= 2 ) { //::debug.pause() new_route_e = j + 1 break } } } // check tile pos to empty local tile = square_x(route[pos].x, route[pos].y).get_ground_tile() if ( tile.find_object(mo_way) != null && wt == wt_rail && test == 0 ) { tool.work(our_player, tile) } local route_status = "" local new_route = [] if ( new_route_s != null && new_route_e != null ) { new_route = route.slice(new_route_e, new_route_s) route_status = coord_to_string(new_route[0]) + " to " + coord_to_string(new_route[new_route.len()-1]) } if ( test == 0 ) { //gui.add_message_at(our_player, "removed way from " + coord_to_string(route[pos]) + " to " + coord_to_string(route[0]), route[0]) } else { //gui.add_message_at(our_player, "removed way not all " + route_status, route[0]) //optimize_way_line(new_route, wt) } } /** * remove station / tiles remove until it is empty * fields = field list * wt = waytype */ function remove_tile_to_empty(tiles, wt, t_array = 1) { local tool = command_x(tool_remover) local toolr = command_x(tool_remove_way) if ( t_array == 1 ) { // remove array tiles for ( local i = 0; i < tiles.len(); i++ ) { local tile_remove = 1 local tiles_r = square_x(tiles[i].x, tiles[i].y).get_ground_tile() local test_way = tiles_r.find_object(mo_way) //.get_desc() local tile_coord = coord3d_to_string(tiles_r) if ( test_way != null ) { if ( test_way.get_owner().nr != our_player_nr ) { tile_remove = 0 } } if ( tile_remove == 1 ) { //gui.add_message_at(our_player, "remove tile " + coord3d_to_string(tiles[i]), tiles[i]) if ( tiles_r.is_crossing() ) { //::debug.pause() // test crossing and remove toolr.work(our_player, tiles_r, tiles_r, "" + wt) //tool.work(our_player, tile) } else { while(true){ tool.work(our_player, tiles_r) if (tiles_r.is_empty()) break } //} } } } } else if ( t_array == 0 ) { // remove one tile local tile_remove = 1 local tiles_r = tile_x(tiles.x, tiles.y, tiles.z) local test_way = tiles_r.has_way(wt) //.get_desc() //gui.add_message_at(our_player, "test way tile " + tiles_r.has_way(wt), tiles) //gui.add_message_at(our_player, "crossing_tile " + tiles_r.is_crossing(), tiles) //gui.add_message_at(our_player, "tiles_r.find_object(mo_way).get_owner().nr " + tiles_r.find_object(mo_way).get_owner().nr, tiles) if ( test_way && tiles_r.find_object(mo_way).get_owner().nr != our_player_nr && !tiles_r.is_crossing() ) { tile_remove = 0 } if ( tile_remove == 1 ) { //gui.add_message_at(our_player, "crossing_tile " + tiles_r.is_crossing(), tiles) if ( tiles_r.is_crossing() ) { //::debug.pause() // test crossing and remove toolr.work(our_player, tiles_r, tiles_r, "" + wt) //tool.work(our_player, tile) } else { while(true){ tool.work(our_player, tiles_r) if (tiles_r.is_empty()) break } } return true } else { return false } } } /** * function for check station lenght * * @param pl = player * @param starts_field = tile station from plan_simple_connection * @param st_lenght = stations fields count * @param wt = waytype * @param select_station = station object * @param build = 0 -> test ; 1 -> build * @param combined_halt = true -> yes ; false -> no * * returns false (something failed) or array of station tiles (success) * in case of success, the value of starts_field maybe changed * */ function check_station(pl, starts_field, t_route, st_lenght, wt, select_station, build = 1, combined_halt = false) { // print messages box // 1 // 2 local print_message_box = 0 //if ( build == 1 ) { print_message_box = 2 } if ( print_message_box == 2 ) { gui.add_message_at(pl, " --- start field : " + coord3d_to_string(starts_field) + " # station lenght : " + st_lenght, world.get_time()) } // save tiles from route local tiles_st = t_route.slice(0, st_lenght) if ( print_message_box == 2 ) gui.add_message_at(pl, "tiles_st.len() = " + tiles_st.len(), tiles_st[0]) if ( starts_field == t_route[t_route.len()-1] ) { tiles_st.clear() tiles_st = t_route.slice((t_route.len()-st_lenght), t_route.len()) tiles_st.reverse() if ( print_message_box == 2 ) gui.add_message_at(pl, "replace tiles_st.len() = " + tiles_st.len(), tiles_st[0]) } local st_build = false local err = null local t = tile_x(starts_field.x, starts_field.y, starts_field.z) local d = t.get_way_dirs(wt) d = dir.backward(d) // now d points outside of the built route // otherwise stations on very short tracks overlap if ( print_message_box == 2 ) { gui.add_message_at(pl, " --- field test : " + coord3d_to_string(starts_field), world.get_time()) gui.add_message_at(pl, " ------ get_way_dirs : " + d, world.get_time()) } // test directions local d_quer = (d == dir.east || d == dir.west) ? dir.north : dir.east; local test_dirs = [d, d_quer, dir.backward(d_quer) ] // first check direction d and backwards // then both orthogonal directions for(local i = 0; i test : " + coord3d_to_string(b1_tile), b1_tile) gui.add_message_at(pl, " ---=> dir.double(d) : " + dir.double(current_d), world.get_time()) } if ( test_field(pl, b1_tile, wt, dir.double(current_d), starts_field.z) && test_tile_is_empty(b1_tile)) { if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> add tile : " + coord3d_to_string(b1_tile), world.get_time()) } b_tile.append(b1_tile) // increase step: if positive add +1, if negative add -1 step = step>0 ? step+1 : step-1 // also increase steps to end of station if (step>0) { step_end ++; } } else { if (step < 0) { // not enough space, fail break } if (i>0) { // do not search for backward directions if orthogonal to d break } // search in forward direction failed, now go backward step = -1 } } // check route tiles for station build if ( print_message_box == 2 && b_tile.len() > 0 ) { gui.add_message_at(pl, " coord3d_to_string(b_tile[0]) " + coord3d_to_string(b_tile[0]) + " - coord3d_to_string(starts_field) " + coord3d_to_string(starts_field), starts_field) } if ( b_tile.len() > 0 && ( coord3d_to_string(b_tile[0]) != coord3d_to_string(starts_field) || b_tile.len() < st_lenght ) ) { if ( print_message_box == 2 ) { gui.add_message_at(pl, " check station tiles to route tiles ", starts_field) } local rc = 1 if (starts_field.x == tiles_st[0].x ) { for ( local i = 1; i < st_lenght; i++ ) { if ( tile_x(tiles_st[i].x, tiles_st[i].y, tiles_st[i].z).has_ways() ) { local t = tile_x(tiles_st[i].x, tiles_st[i].y, tiles_st[i].z) local d = t.get_way_dirs(wt) if ( print_message_box == 2 ) gui.add_message_at(pl, " dir.double(d) " + dir.double(d), tiles_st[i]) if ( tiles_st[0].x == tiles_st[i].x && dir.double(d) && t.get_slope() == 0 ) { rc++ } } } } if (starts_field.y == tiles_st[0].y ) { for ( local i = 1; i < st_lenght; i++ ) { if ( tile_x(tiles_st[i].x, tiles_st[i].y, tiles_st[i].z).has_ways() ) { local t = tile_x(tiles_st[i].x, tiles_st[i].y, tiles_st[i].z) local d = t.get_way_dirs(wt) if ( print_message_box == 2 ) gui.add_message_at(pl, " dir.double(d) " + dir.double(d), tiles_st[i]) if ( tiles_st[0].y == tiles_st[i].y && dir.double(d) && t.get_slope() == 0 ) { rc++ } } } } if ( print_message_box == 2 ) { gui.add_message_at(pl, " rc " + rc + " - st_lenght " + st_lenght, starts_field) } if ( rc == st_lenght ) { // replace stations fields b_tile.clear() b_tile = tiles_st if ( print_message_box == 2 ) { gui.add_message_at(pl, " set station tiles to route tiles ", starts_field) } step_end = 0 } } // build station if ( b_tile.len() == st_lenght && build == 1) { // this will build station, missing ways, do terraform st_build = expand_station(pl, b_tile, wt, select_station, starts_field, combined_halt) } else if ( b_tile.len() == st_lenght && build == 0 ) { st_build = true } // correct first tile of station // (this will correct c_start/c_end if these are used in the call to this method) if (st_build && step_end > 0 && build == 1) { starts_field.x += step_end*dc.x starts_field.y += step_end*dc.y if ( print_message_box == 2 ) gui.add_message_at(pl, " ---> first tile of station reset : " + coord3d_to_string(starts_field), starts_field) } if (st_build) { break // leave for loop to test directions } } if ( st_build == false ) { // move station if ( print_message_box > 0 ) { gui.add_message_at(pl, " *#* ERROR => expand station failed", world.get_time()) gui.add_message_at(pl, " --- field test : " + coord3d_to_string(starts_field), starts_field) gui.add_message_at(pl, " ------ get_way_dirs : " + d, world.get_time()) } //::debug.pause() } return st_build } /** * test fields station * pl = player * t_tile = field to test * wt = waytype * rotate = northsouth ( 5 ) or eastwest ( 10 ) * ref_hight = z from start field * way_exists = 0 -> field expand new station * 1 -> field has way for expand exists station */ function test_field(pl, t_tile, wt, rotate, ref_hight, way_exists = 0) { local print_message_box = 0 local err = null local z = null // tile out of map if ( !world.is_coord_valid(t_tile) ) { return false } // find z coord z = square_x(t_tile.x, t_tile.y).get_ground_tile() if ( test_tile_is_empty(t_tile) && t_tile.get_slope() == 0 && ref_hight == z.z && way_exists == 0 ) { // tile is empty and is flat if ( t_tile.is_ground() ) { if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> tile is empty and is flat " + coord3d_to_string(t_tile), t_tile) } return true } } else if ( t_tile.has_way(wt) && !t_tile.has_two_ways() && dir.double(t_tile.get_way_dirs(wt)) == rotate && t_tile.get_slope() == 0 && !t_tile.is_bridge() ) { // tile has single way and is flat - no bridge ramp if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> tile has single way and is flat " + coord3d_to_string(t_tile), t_tile) gui.add_message_at(pl, " ---=> dir.double(t_tile.get_way_dirs(wt)) == rotate : " + dir.double(t_tile.get_way_dirs(wt)) + " == " + rotate, t_tile) } return true } else if ( t_tile.has_way(wt) && !t_tile.has_two_ways() && dir.double(t_tile.get_way_dirs(wt)) == rotate && t_tile.get_slope() > 0 && t_tile.is_bridge() ) { // tile has single way and has bridge start if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> tile has single way and is bridge " + coord3d_to_string(t_tile), t_tile) } return true } else if ( test_tile_is_empty(t_tile) && ( t_tile.get_slope() > 0 || ref_hight != z.z ) && way_exists == 0 ) { // terraform // return true and terraform befor build station if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> tile terraform befor build station " + coord3d_to_string(t_tile), t_tile) } return true } return false } /** * test tile is empty * removed objects for empty tiles: tree, ground_object, moving_object * */ function test_tile_is_empty(t_tile) { local tile = tile_x(t_tile.x, t_tile.y, t_tile.z) local tile_tree = tile.find_object(mo_tree) local tile_groundobj = tile.find_object(mo_groundobj) local tile_moving_object = tile.find_object(mo_moving_object) //gui.add_message_at(our_player, " ---=> test_tile " + coord3d_to_string(tile) + " | is_empty " + tile.is_empty() + " | tile_tree " + tile_tree + " | tile_groundobj " + tile_groundobj + " | tile_moving_object " + tile_moving_object, tile) if ( tile.is_empty() ) { return true } else if ( tile_tree != null || tile_groundobj != null || tile_moving_object != null ) { return true } return false } /** * function expand station() * * @param pl = player_x * @param fields = array fields * @param wt = waytype * @param select_station = station object * @param start_fld = c_start or c_end * @param combined_halt = true -> yes ; false -> no */ function expand_station(pl, fields, wt, select_station, start_fld, combined_halt) { local start_field = tile_x(start_fld.x, start_fld.y, start_fld.z) // 0 = off // 1 = extension build // 2 = messages // 3 = check dock/harbour local print_message_box = 0 local ref_hight = start_field.z local err = null local combined_station = false // extension field for connect station to factory/dock local r = tile_x(start_field.x, start_field.y, start_field.z) local d = r.get_way_dirs(wt) local extension_tile = null switch(d) { case 1: extension_tile = square_x(start_field.x, start_field.y + 1) break case 2: extension_tile = square_x(start_field.x - 1, start_field.y) break case 4: extension_tile = square_x(start_field.x, start_field.y - 1) break case 8: extension_tile = square_x(start_field.x + 1, start_field.y) break } //gui.add_message_at(pl, "(1496) ---=> extension_tile " + extension_tile, world.get_time()) if ( extension_tile != null && tile_x(extension_tile.x, extension_tile.y, extension_tile.get_ground_tile().z).is_water() ) { extension_tile = null } local t = fields.len() if ( t > 0 ) { // terrafom local i = 1 if ( fields[0].x != start_field.x || fields[0].y != start_field.y ) { i = 0 } for ( i; i < t; i++ ) { local z = square_x(fields[i].x, fields[i].y).get_ground_tile() local f = tile_x(fields[i].x, fields[i].y, z.z) if ( test_tile_is_empty(f) && ( f.get_slope() > 0 || ref_hight != z.z ) ) { if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> terraform", world.get_time()) gui.add_message_at(pl, " ---=> tile z " + z.z + " start tile z " + ref_hight, world.get_time()) } if ( z.z < ref_hight && z.z >= (ref_hight - 2) ) { // terraform up if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> tile up to flat ", world.get_time()) } do { err = command_x.set_slope(pl, tile_x(f.x, f.y, z.z), 82 ) if ( err != null ) { break } z = square_x(fields[i].x, fields[i].y).get_ground_tile() } while(z.z < ref_hight ) } else if ( z.z >= ref_hight || z.z <= (ref_hight + 1) ) { // terraform down if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> tile down to flat ", world.get_time()) } do { err = command_x.set_slope(pl, tile_x(f.x, f.y, z.z), 83 ) if ( err != null ) { break } z = square_x(fields[i].x, fields[i].y).get_ground_tile() } while(z.z > ref_hight ) // replace water to land if ( z.is_water() ) { command_x.change_climate_at(our_player, z, cl_temperate) } } if ( err ) { return false } } } // build way to tiles for ( local i = 1; i < t; i++ ) { if ( test_tile_is_empty(fields[i]) && fields[i].get_slope() == 0 ) { err = null // empty then build way err = command_x.build_way(pl, fields[i-1], fields[i], planned_way, true) if ( err != null ) { gui.add_message_at(pl, " ---=> not build way tile " + coord3d_to_string(fields[i-1]) + " to " + coord3d_to_string(fields[i]) + " - err :" + err, fields[i]) remove_tile_to_empty(fields, wt) return false } } } // check harbour/dock local st_dock = search_station(start_field, wt, 1) if ( !equal_coord3d( fields[0], start_field) ) { local extension = find_extension(wt) if ( extension ) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, "-*---> selectet extension: " + extension.get_name(), world.get_time()) } if ( st_dock && extension_tile != null ) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, "-*---> dock/harbour found at : " + coord3d_to_string(st_dock[0]), st_dock[0]) } //gui.add_message_at(pl, "(1596) ---=> abs(fields[0].x - st_dock[0].x) " + abs(fields[0].x - st_dock[0].x), fields[0]) //gui.add_message_at(pl, "(1596) ---=> abs(fields[0].y - st_dock[0].y) " + abs(fields[0].y - st_dock[0].y), fields[0]) if ( abs(fields[0].x - st_dock[0].x) > 1 || abs(fields[0].y - st_dock[0].y) > 1 ) { local tile = tile_x(extension_tile.x, extension_tile.y, extension_tile.get_ground_tile().z) if ( test_tile_is_empty(tile) ) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, "-*---> build extension at : " + coord3d_to_string(tile), world.get_time()) } err = command_x.build_station(pl, tile, extension) } } } else { err = command_x.build_station(pl, start_field, select_station) } } } if ( st_dock ) { //&& st_dock[0].find_object(mo_building).get_waytype() == wt_water local st_dock_wt = st_dock[0].find_object(mo_building).get_desc().get_waytype() if ( print_message_box == 3 ) { gui.add_message_at(pl, "(1593) ---=> st_dock " + st_dock, st_dock[0]) local b = tile_x(st_dock[0].x, st_dock[0].y, st_dock[0].z).find_object(mo_building) gui.add_message_at(pl, "(1593) ---=> b " + b, st_dock[0]) gui.add_message_at(pl, "(1593) ---=> b.get_desc().get_waytype() " + b.get_desc().get_waytype(), st_dock[0]) gui.add_message_at(pl, "(1593) ---=> b.get_desc().get_name() " + b.get_desc().get_name(), st_dock[0]) ::debug.pause() } if ( st_dock_wt == wt_water ) { combined_station = true } } // station not build to start_field /* local build_connection = 0 if ( combined_station && !equal_coord3d( fields[0], start_field) ) { err = command_x.build_station(pl, start_field, select_station) build_connection = 1 // build connect tile to dock }*/ if ( err == null ) { // build station to tile for ( local i = 0; i < t; i++ ) { local z = square_x(fields[i].x, fields[i].y).get_ground_tile() local f = tile_x(fields[i].x, fields[i].y, z.z) if ( tile_x(fields[i].x, fields[i].y, fields[i].z).is_bridge() && f.get_slope() > 0 ) { // bridge start field -> build to ground fields[i].z -= 1 } // set the rotation for build local rotation = 0 /* switch (fields[i].get_way_dirs(wt)) { case 2: rotation = 1 break case 1: rotation = 2 break case 8: rotation = 3 break default: }*/ local d = tile_x(fields[i].x, fields[i].y, fields[i].z).get_way_dirs(wt) if ( d == 2 || d == 8 || d == 10 ) { rotation = 1 } else if ( d == 1 || d == 4 || d == 5 ) { rotation = 0 } if (debug) gui.add_message_at(pl, "(1952) ---=> set the rotation for build " + rotation, fields[i]) err = command_x.build_station(pl, fields[i], select_station, rotation) if ( err ) { if ( print_message_box > 0 ) { gui.add_message_at(pl, "(1615) ---=> not build station tile at " + coord3d_to_string(fields[i]), fields[i]) } if ( equal_coord3d( fields[0], start_field) ) { fields.remove(0) } remove_tile_to_empty(fields, wt_rail) return false } if ( print_message_box == 2 ) { gui.add_message_at(pl, " ---=> build station tile at " + coord3d_to_string(fields[i]), fields[i]) } } } // station not build to start_field if ( !equal_coord3d( fields[0], start_field) ) { if ( start_field.find_object(mo_building) ) { if ( print_message_box == 2 ) { gui.add_message_at(pl, "(1756) ---=> combined_station " + combined_station, fields[0]) } // remove connect tile to dock local tool = command_x(tool_remover) tool.work(our_player, start_field) } // connect way to station err = command_x.build_way(pl, start_field, fields[0], planned_way, true) } // check station connect factory local st = halt_x.get_halt(fields[0], pl) local s_tiles = [] /* gui.add_message_at(our_player, "(1927) -*---> halt_x.get_halt(fields[0], pl) : " + st, fields[0]) gui.add_message_at(our_player, "(1927) -*---> combined_station : " + combined_station, fields[0]) gui.add_message_at(our_player, "(1927) -*---> combined_halt : " + combined_halt, fields[0]) gui.add_message_at(our_player, "(1927) -*---> extension_tile : " + extension_tile, fields[0]) gui.add_message_at(our_player, "(1927) -*---> st.get_factory_list().len() : " + st.get_factory_list().len(), fields[0]) */ if ( st != null && st.get_factory_list().len() == 0 && combined_halt == false ) { local fl_st = st.get_factory_list() if ( combined_station == false && fl_st.len() == 0 && extension_tile != null ) { local tile = tile_x(extension_tile.x, extension_tile.y, extension_tile.get_ground_tile().z) if ( print_message_box == 1 ) { gui.add_message_at(our_player, "-*---> build extension at : " + coord3d_to_string(tile), tile) } local extension = find_extension(wt) local new_tile = 0 if ( test_tile_is_empty(tile) ) { new_tile = 1 } else if ( tile.has_way(wt_road) && !tile.has_two_ways() ) { local tiles = [1, 2, 4, 5, 8, 10] local test = 0 // test direction from road for ( local i = 0; i < tiles.len(); i++ ) { if ( tile.get_way_dirs(wt_road) == tiles[i] ) { test++ } } if ( test == 1 ) { local stations_list = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x("Passagiere")) err = command_x.build_station(pl, tile, stations_list[0]) if ( err ) { gui.add_message_at(pl, " -##-=> WARNING not connect factory: " + coord3d_to_string(start_field), start_field) new_tile = 1 } } else { new_tile = 1 } } else { new_tile = 1 } if ( new_tile == 1 ) { // to do connection factory other rotations if ( print_message_box == 1 ) { gui.add_message_at(pl, "check connect factory : build extension", start_field) } //::debug.pause() s_tiles.append(fields[0]) s_tiles.append(start_field) local tool = command_x(tool_remover) local test_tiles = [] local factory_connect = 0 if ( start_field.get_way_dirs(wt_rail) == 3 ) { if ( start_field.x == fields[0].x ) { s_tiles.append(square_x(start_field.x+1, start_field.y).get_ground_tile()) test_tiles.append(square_x(start_field.x-1, start_field.y).get_ground_tile()) test_tiles.append(square_x(start_field.x-1, start_field.y+1).get_ground_tile()) test_tiles.append(square_x(start_field.x, start_field.y+1).get_ground_tile()) factory_connect = build_extensions_connect_factory(pl, start_field, fields[0], test_tiles, extension) } else if ( start_field.y == fields[0].y ) { s_tiles.append(square_x(start_field.x, start_field.y-1).get_ground_tile()) test_tiles.append(square_x(start_field.x, start_field.y+1).get_ground_tile()) test_tiles.append(square_x(start_field.x-1, start_field.y+1).get_ground_tile()) test_tiles.append(square_x(start_field.x-1, start_field.y).get_ground_tile()) factory_connect = build_extensions_connect_factory(pl, start_field, fields[0], test_tiles, extension) } } else if ( start_field.get_way_dirs(wt_rail) == 6 ) { if ( start_field.x == fields[0].x ) { s_tiles.append(square_x(start_field.x-1, start_field.y).get_ground_tile()) test_tiles.append(square_x(start_field.x-1, start_field.y).get_ground_tile()) test_tiles.append(square_x(start_field.x-1, start_field.y-1).get_ground_tile()) test_tiles.append(square_x(start_field.x, start_field.y-1).get_ground_tile()) factory_connect = build_extensions_connect_factory(pl, start_field, fields[0], test_tiles, extension) } else if ( start_field.y == fields[0].y ) { s_tiles.append(square_x(start_field.x, start_field.y+1).get_ground_tile()) test_tiles.append(square_x(start_field.x, start_field.y-1).get_ground_tile()) test_tiles.append(square_x(start_field.x-1, start_field.y-1).get_ground_tile()) test_tiles.append(square_x(start_field.x-1, start_field.y).get_ground_tile()) factory_connect = build_extensions_connect_factory(pl, start_field, fields[0], test_tiles, extension) } } else if ( start_field.get_way_dirs(wt_rail) == 9 ) { if ( start_field.x == fields[0].x ) { s_tiles.append(square_x(start_field.x-1, start_field.y).get_ground_tile()) test_tiles.append(square_x(start_field.x+1, start_field.y).get_ground_tile()) test_tiles.append(square_x(start_field.x+1, start_field.y+1).get_ground_tile()) test_tiles.append(square_x(start_field.x, start_field.y+1).get_ground_tile()) factory_connect = build_extensions_connect_factory(pl, start_field, fields[0], test_tiles, extension) } else if ( start_field.y == fields[0].y ) { s_tiles.append(square_x(start_field.x, start_field.y-1).get_ground_tile()) test_tiles.append(square_x(start_field.x, start_field.y+1).get_ground_tile()) test_tiles.append(square_x(start_field.x+1, start_field.y+1).get_ground_tile()) test_tiles.append(square_x(start_field.x+1, start_field.y).get_ground_tile()) factory_connect = build_extensions_connect_factory(pl, start_field, fields[0], test_tiles, extension) } } else if ( start_field.get_way_dirs(wt_rail) == 12 ) { if ( start_field.x == fields[0].x ) { s_tiles.append(square_x(start_field.x-1, start_field.y).get_ground_tile()) test_tiles.append(square_x(start_field.x+1, start_field.y).get_ground_tile()) test_tiles.append(square_x(start_field.x+1, start_field.y-1).get_ground_tile()) test_tiles.append(square_x(start_field.x, start_field.y-1).get_ground_tile()) factory_connect = build_extensions_connect_factory(pl, start_field, fields[0], test_tiles, extension) } else if ( start_field.y == fields[0].y ) { s_tiles.append(square_x(start_field.x, start_field.y+1).get_ground_tile()) test_tiles.append(square_x(start_field.x, start_field.y-1).get_ground_tile()) test_tiles.append(square_x(start_field.x+1, start_field.y-1).get_ground_tile()) test_tiles.append(square_x(start_field.x+1, start_field.y).get_ground_tile()) factory_connect = build_extensions_connect_factory(pl, start_field, fields[0], test_tiles, extension) } } /* if ( tiles.len() == 3 ) { for ( local x = 0; x < 3; x++ ) { if ( halt_x.get_halt(tiles[x], pl) ) { fields.append(tiles[x]) } } }*/ /*if ( start_field.x < fields[0].x && start_field.y < fields[0].y ) { tile = square_x(start_field.x-1, start_field.y).get_ground_tile() if ( tile.is_empty ) { gui.add_message_at(pl, " --=> new extensions field: " + coord3d_to_string(tile), tile) s_tiles.append(fields[0]) s_tiles.append(start_field) if ( start_field.get_way_dirs(wt_rail) == 3 ) { s_tiles.append(square_x(start_field.x, start_field.y-1).get_ground_tile()) } else if ( start_field.get_way_dirs(wt_rail) == 6 ) { s_tiles.append(square_x(start_field.x, start_field.y+1).get_ground_tile()) } rebuild = 1 } fields.append(tile) } else if ( start_field.x > fields[0].x && start_field.y > fields[0].y ) { tile = square_x(start_field.x+1, start_field.y).get_ground_tile() if ( tile.is_empty ) { gui.add_message_at(pl, " --=> new extensions field: " + coord3d_to_string(tile), tile) s_tiles.append(fields[0]) s_tiles.append(start_field) if ( start_field.get_way_dirs(wt_rail) == 9 ) { s_tiles.append(square_x(start_field.x, start_field.y-1).get_ground_tile()) } else if ( start_field.get_way_dirs(wt_rail) == 12 ) { s_tiles.append(square_x(start_field.x+1, start_field.y).get_ground_tile()) } rebuild = 1 } fields.append(tile) } else {*/ if ( factory_connect == 0 ) { local tile = square_x(fields[0].x, fields[0].y).get_ground_tile() if ( tile.find_object(mo_building) != null ) { local waytypes = test_halt_waytypes(tile) if ( waytypes > 1 || start_field.find_object(mo_building) != null ) { if ( print_message_box > 0 ) { gui.add_message_at(pl, " -#-=> combined station " + coord3d_to_string(start_field), start_field) } } else { gui.add_message_at(pl, " -###-=> WARNING not connect factory: " + coord3d_to_string(start_field), start_field) } } } } } } else if ( combined_halt ) { // build combined station } if ( start_field.is_empty() ) { // rebuild way err = command_x.build_way(pl, s_tiles[0], s_tiles[1], planned_way, true) err = command_x.build_way(pl, s_tiles[1], s_tiles[2], planned_way, true) } return fields } } /** * function build_extensions_connect_factory() * * @param pl = player * @param st_field = start field * @param hlt_field = halt field * @param tiles[] = test tiles for extensions to connect factory * @param extension = extensions object * * @return * 1 = connect factory * 0 = not connect factory */ function build_extensions_connect_factory(pl, st_field, hlt_field, tiles, extension) { local factory_connect = 0 // check station connect factory local st = halt_x.get_halt(hlt_field, pl) local fl_st = null local err = null local tool = command_x(tool_remover) //gui.add_message_at(pl, " -##-=> " + coord3d_to_string(tiles[0]) + " tiles[0].is_empty() " + tiles[0].is_empty() + " - tiles[0].is_water() " + tiles[0].is_water() + " - tiles[0].has_ways() " + tiles[0].has_ways() + " - tiles[0].has_way(wt_water) " + tiles[0].has_way(wt_water), tiles[0]) //gui.add_message_at(pl, " -##-=> " + coord3d_to_string(tiles[1]) + " tiles[1].is_empty() " + tiles[1].is_empty() + " - tiles[1].is_water() " + tiles[1].is_water(), tiles[1]) //gui.add_message_at(pl, " -##-=> " + coord3d_to_string(tiles[2]) + " tiles[2].is_empty() " + tiles[2].is_empty() + " - tiles[2].is_water() " + tiles[2].is_water(), tiles[2]) //gui.add_message_at(pl, " -##-=> " + coord3d_to_string(tiles[0]) + " tiles[0].is_empty() " + tiles[0].is_empty() + " - tiles[0].is_water() " + tiles[0].is_water() + " - tiles[0].has_ways() " + tiles[0].has_ways() + " - tiles[0].has_way(wt_water) " + tiles[0].has_way(wt_water), tiles[0]) //gui.add_message_at(pl, " -##-=> " + coord3d_to_string(tiles[1]) + " tiles[1].is_empty() " + tiles[1].is_empty() + " - tiles[1].is_water() " + tiles[1].is_water() + " - tiles[1].has_ways() " + tiles[1].has_ways(), tiles[1]) //gui.add_message_at(pl, " -##-=> " + coord3d_to_string(tiles[2]) + " tiles[2].is_empty() " + tiles[2].is_empty() + " - tiles[2].is_water() " + tiles[2].is_water() + " - tiles[2].has_ways() " + tiles[2].has_ways(), tiles[2]) // test 1 -> rebuild 0 for (local i = 0; i < tiles.len(); i++ ) { if ( test_tile_is_empty(tiles[i]) && tiles[i].is_ground() ) { if ( i > 0 ) { tool.work(pl, st_field) command_x.build_station(pl, st_field, extension) } err = command_x.build_station(pl, tiles[i], extension) if ( err != null ) { gui.add_message_at(pl, " -##-=> build err tile " + i + " " + err, tiles[i]) } fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { factory_connect = 1 if ( i > 0 ) { tool.work(pl, st_field) } break } } else if ( tiles[i].has_way(wt_water) ) { //gui.add_message_at(pl, " -##-=> tiles["+i+"].get_way_dirs(wt_water) " + tiles[i].get_way_dirs(wt_water) + " - tiles["+i+"].find_object(mo_way).get_owner().nr " + tiles[i].find_object(mo_way).get_owner().nr, tiles[i]) if ( (tiles[i].get_way_dirs(wt_water) == 5 || tiles[i].get_way_dirs(wt_water) == 10 ) && tiles[i].find_object(mo_way).get_owner().nr == 16 ) { local station = find_station(wt_water) if ( station != false ) { err = command_x.build_station(pl, tiles[i], station) if ( err != null ) { gui.add_message_at(pl, " -##-=> build err tile " + i + " " + err, tiles[i]) } fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { factory_connect = 1 break } } } } else if ( (tiles[i].get_way_dirs(wt_road) == 5 || tiles[i].get_way_dirs(wt_road) == 10 ) && (tiles[i].find_object(mo_way).get_owner().nr == our_player.nr || tiles[i].find_object(mo_way).get_owner().nr == 1) ) { //gui.add_message_at(pl, " -##-=> build road station tile " + coord3d_to_string(tiles[i]), tiles[i]) //gui.add_message_at(pl, " -##-=> halt_x.get_halt(st_field, pl) " + halt_x.get_halt(st_field, pl), tiles[i]) local restor_way = 0 local station = find_station(wt_road) if ( station != false ) { if ( halt_x.get_halt(st_field, pl) == null ) { tool.work(pl, st_field) command_x.build_station(pl, st_field, extension) restor_way = 1 } err = command_x.build_station(pl, tiles[i], station) if ( err != null ) { gui.add_message_at(pl, " -##-=> build err tile " + i + " " + err, tiles[i]) } fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { factory_connect = 1 if (restor_way == 1) { tool.work(pl, st_field) } break } } } } return factory_connect } /* * tiles = field array * station_obj = building_desc_x.station */ function build_station(tiles, station_obj) { } /** * find signal tool * * @param sig_type = signal type (is_signal, is_presignal ... ) * @param wt = waytype */ function find_signal(sig_type, wt) { local list = sign_desc_x.get_available_signs(wt) local obj_sign = null foreach(o in list) { if (o.is_signal() && sig_type == "is_signal") { obj_sign = o } if ( o.is_traffic_light() && sig_type == "traffic_light" ) { obj_sign = o } } return obj_sign } /* * * */ function find_station(wt) { local list = building_desc_x.get_available_stations(building_desc_x.station, wt, good_desc_x(freight)) if ( list.len() > 0 ) { return list[0] } else { return false } } /** * find object tool * * obj = object type ( bridge, tunnel, way, catenary ) * wt = waytype * speed = speed */ function find_object(obj, wt, speed) { local list = [] switch(obj) { case "bridge": list = bridge_desc_x.get_available_bridges(wt) break case "tunnel": list = tunnel_desc_x.get_available_tunnels(wt) break case "way": list = way_desc_x.get_available_ways(wt, st_flat) break case "catenary": local li = wayobj_desc_x.get_available_wayobjs(wt) for (local j=0; j0) { obj_desc = list[0] //gui.add_message_at(our_player,"0 obj_desc " + obj_desc.get_name(), world.get_time()) for(local i=1; i obj_desc.get_topspeed() && b.get_topspeed() <= speed ) { obj_desc = b if ( obj_desc.get_topspeed() == speed ) { break } } else { if ( obj_desc.get_topspeed() >= speed ) { break } obj_desc = b //gui.add_message_at(our_player, i + " break obj_desc " + obj_desc.get_name(), world.get_time()) break } } } } } return obj_desc } /** * find extension building tool * * */ function find_extension(wt, tile_size = 1) { local print_message_box = 0 local select_extension = null // extension building from waytype for selected good local extension_list = building_desc_x.get_available_stations(building_desc_x.station_extension, wt, good_desc_x(freight)) if ( print_message_box == 2 ) { gui.add_message_at(our_player, "extension_list " + extension_list.len(), world.get_time()) } if ( extension_list.len() > 0 ) { foreach(extension in extension_list) { local ok = (select_extension == null) if ( print_message_box == 2 ) { gui.add_message_at(our_player, "extension " + extension.get_name() + " size " + extension.get_size(0), world.get_time()) } if ( !ok && coord_to_string(extension.get_size(0)) == "1,1" ) { if ( select_extension.get_capacity() > extension.get_capacity() ) { select_extension = extension } } else if ( coord_to_string(extension.get_size(0)) == "1,1" ) { select_extension = extension } } } if ( select_extension == null ) { extension_list.clear() // not find extension from waytype for selected good // search post extension from all waytypes extension_list = building_desc_x.get_available_stations(building_desc_x.station_extension, wt_all, good_desc_x(freight)) if ( print_message_box == 2 ) { gui.add_message_at(our_player, "extension_list " + extension_list.len(), world.get_time()) } if ( extension_list.len() > 0 ) { foreach(extension in extension_list) { local ok = (select_extension == null) if ( print_message_box == 2 ) { gui.add_message_at(our_player, "extension " + extension.get_name(), world.get_time()) } if ( !ok && coord_to_string(extension.get_size(0)) == "1,1" ) { if ( select_extension.get_capacity() > extension.get_capacity() ) { select_extension = extension } } else if ( coord_to_string(extension.get_size(0)) == "1,1" ) { select_extension = extension } } } } if ( select_extension == null ) { extension_list.clear() // not find extension from waytype for selected good // search post extension from all waytypes extension_list = building_desc_x.get_available_stations(building_desc_x.station_extension, wt_all, good_desc_x("post")) if ( print_message_box == 2 ) { gui.add_message_at(our_player, "extension_list " + extension_list.len(), world.get_time()) } if ( extension_list.len() > 0 ) { foreach(extension in extension_list) { local ok = (select_extension == null) if ( print_message_box == 2 ) { gui.add_message_at(our_player, "extension " + extension.get_name(), world.get_time()) } if ( !ok && coord_to_string(extension.get_size(0)) == "1,1" ) { if ( select_extension.get_capacity() > extension.get_capacity() ) { select_extension = extension } } else if ( coord_to_string(extension.get_size(0)) == "1,1" ) { select_extension = extension } } } } if ( print_message_box == 2 ) { gui.add_message_at(our_player, "select_extension " + select_extension, world.get_time()) } return select_extension } /** * search existing depot on range to station * * @param field_pos = start field * @param wt = waytype * @param range = search range * */ function search_depot(field_pos, wt, range = 10) { local list_exists_depot = depot_x.get_depot_list(our_player, wt) if ( list_exists_depot ) { local tile_min = [field_pos.x - range, field_pos.y - range] local tile_max = [field_pos.x + range, field_pos.y + range] local depot_found = null foreach(key in list_exists_depot) { if ( key.x >= tile_min[0] && key.y >= tile_min[1] && key.x <= tile_max[0] && key.y <= tile_max[1] ) { depot_found = tile_x(key.x, key.y, key.z) return depot_found } } } return false } /** * search existing station on range to field * * @param field_pos = start field * @param wt = waytype * @param range = search range * */ function search_station(field_pos, wt, range) { if ( field_pos == null || type(field_pos) == "array" ) { return false } local tile_min = [field_pos.x - range, field_pos.y - range] local tile_max = [field_pos.x + range, field_pos.y + range] local station_found = false foreach(halt in halt_list_x()) { if (halt.get_owner().nr == our_player_nr) { // && halt.get_type == wt local tile = halt.get_tile_list() if ( tile[0].x >= tile_min[0] && tile[0].y >= tile_min[1] && tile[0].x <= tile_max[0] && tile[0].y <= tile_max[1] ) { station_found = tile break } } } return station_found } /** * build double way for more convoys to line * * */ function build_double_track(start_field, wt, station_len) { // 1 // 2 - terraform // 21 - terraform grid // 3 - double track diagonal local print_message_box = 0 if ( print_message_box > 0 ) { gui.add_message_at(our_player, " ### build_double_track ### " + coord3d_to_string(start_field), start_field) gui.add_message_at(our_player, " ### station_len ### " + station_len, start_field) } //local way_list = way_desc_x.get_available_ways(wt, st_flat) local way_obj = start_field.find_object(mo_way).get_desc() //way_list[0] if ( !way_obj.is_available(world.get_time()) ) { way_obj = find_object("way", wt, way_obj.get_topspeed()) } local b_player = our_player //way_obj.get_owner() local d = start_field.get_way_dirs(wt) local tiles = [] local tiles_build_l = [] local tiles_build_r = [] local t = 0 local way_len = station_len if ( way_len < 8 ) { way_len = 8 } local diagonal_st = 0 local way_len_d = 0 if ( d == 6 || d == 9 ) { way_len++ way_len_d = 1 diagonal_st = d } else if ( d == 3 || d == 12 ) { way_len++ way_len_d = 1 diagonal_st = d } local diagonal_x = 0 local diagonal_y = 0 local signal = [] // check way // 8 fields straight // 11 fields diagonal for ( local i = 0; i < (way_len + way_len_d); i++ ) { if ( d == 5 ) { // build from n to s // ns - r local ref_ground = square_x(start_field.x, start_field.y + i).get_ground_tile() local t = tile_x(start_field.x + 1, start_field.y + i, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(start_field.x, start_field.y + i).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(start_field.x + 1, start_field.y + i, ref_ground.z)) } // ns - l t = tile_x(start_field.x - 1, start_field.y + i, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(start_field.x, start_field.y + i).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(start_field.x - 1, start_field.y + i, ref_ground.z)) } tiles.append(ref_ground) } else if ( d == 10 ) { //gui.add_message_at(our_player, "tile r " + coord3d_to_string(tile_x(start_field.x + i, start_field.y + 1, start_field.z)) + " empty " + tile_x(start_field.x + i, start_field.y + 1, start_field.z).is_empty(), start_field) //gui.add_message_at(our_player, " -- tile l " + coord3d_to_string(tile_x(start_field.x + i, start_field.y - 1, start_field.z)) + " to empty " + tile_x(start_field.x - i, start_field.y + 1, start_field.z).is_empty(), start_field) // ew - r // build from w to e local ref_ground = square_x(start_field.x + i, start_field.y).get_ground_tile() local t = tile_x(start_field.x + i, start_field.y + 1, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(start_field.x + i, start_field.y).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(start_field.x + i, start_field.y + 1, ref_ground.z)) } // ew - l t = tile_x(start_field.x + i, start_field.y - 1, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(start_field.x + i, start_field.y).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(start_field.x + i, start_field.y - 1, ref_ground.z)) } tiles.append(ref_ground) } else if ( d == 6 || d == 9 ) { // build from ne to sw // nw - r if ( i > 0 ) { if ( tiles[i-1].get_way_dirs(wt) == 6 ) { diagonal_y++ } if ( tiles[i-1].get_way_dirs(wt) == 9 ) { diagonal_x++ } } local ref_ground = square_x(start_field.x - diagonal_x, start_field.y + diagonal_y).get_ground_tile() if ( print_message_box == 2 ) { gui.add_message_at(b_player, "6/9 ref_ground " + coord3d_to_string(ref_ground), ref_ground) } local t = null if ( tiles_build_l.len() == 0 && ref_ground.get_way_dirs(wt) == 9 ) { t = tile_x(ref_ground.x, ref_ground.y + 1, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x, ref_ground.y + 1).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(ref_ground.x, ref_ground.y + 1, ref_ground.z)) } t = tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x, ref_ground.y + 2).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z)) } } else if ( tiles_build_l.len() > way_len-2 && tiles_build_l.len() <= way_len ) { // no build tile } else if ( tiles_build_l.len() < way_len-2 ) { t = tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x, ref_ground.y + 2).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z)) } } if ( tiles_build_r.len() == 0 && ref_ground.get_way_dirs(wt) == 6 ) { t = tile_x(ref_ground.x - 1, ref_ground.y, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x - 1, ref_ground.y).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(ref_ground.x - 1, ref_ground.y, ref_ground.z)) } t = tile_x(ref_ground.x - 2, ref_ground.y, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x - 2, ref_ground.y).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(ref_ground.x - 2, ref_ground.y, ref_ground.z)) } }else if ( tiles_build_r.len() > way_len-2 && i <= way_len ) { // no build tile } else if ( tiles_build_r.len() < way_len-2 ) { t = tile_x(ref_ground.x - 2, ref_ground.y, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x - 2, ref_ground.y).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(ref_ground.x - 2, ref_ground.y, ref_ground.z)) } } tiles.append(ref_ground) if ( print_message_box == 30 && i < way_len-2 ) { gui.add_message_at(b_player, "tiles_build_r[" + i + "] " + coord3d_to_string(tiles_build_r[i]), world.get_time()) gui.add_message_at(b_player, "tiles_build_l[" + i + "] " + coord3d_to_string(tiles_build_l[i]), world.get_time()) } } else if ( d == 3 || d == 12 ) { // build from nw to se // nw - r if ( i > 0 ) { if ( tiles[i-1].get_way_dirs(wt) == 3 ) { diagonal_x++ } if ( tiles[i-1].get_way_dirs(wt) == 12 ) { diagonal_y++ } } local ref_ground = square_x(start_field.x + diagonal_x, start_field.y + diagonal_y).get_ground_tile() if ( print_message_box == 2 ) { gui.add_message_at(b_player, "3/12 ref_ground " + coord3d_to_string(ref_ground), world.get_time()) gui.add_message_at(b_player, "tiles_build_l.len() " + tiles_build_l.len() + " ref_ground.get_way_dirs(wt)" + ref_ground.get_way_dirs(wt), world.get_time()) } local t = null if ( tiles_build_l.len() == 0 && ref_ground.get_way_dirs(wt) == 3 ) { //gui.add_message_at(b_player, "test tile " + coord3d_to_string(tile_x(ref_ground.x, ref_ground.y + 1, ref_ground.z)), world.get_time()) t = tile_x(ref_ground.x, ref_ground.y + 1, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x, ref_ground.y + 1).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(ref_ground.x, ref_ground.y + 1, ref_ground.z)) //gui.add_message_at(b_player, "add tile " + coord3d_to_string(tile_x(ref_ground.x, ref_ground.y + 1, ref_ground.z)), world.get_time()) } //gui.add_message_at(b_player, "test tile " + coord3d_to_string(tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z)), world.get_time()) t = tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x, ref_ground.y + 2).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z)) //gui.add_message_at(b_player, "add tile " + coord3d_to_string(tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z)), world.get_time()) } } else if ( tiles_build_l.len() > way_len-2 && tiles_build_l.len() <= way_len ) { // no build tile } else if ( tiles_build_l.len() < way_len-2 ) { //gui.add_message_at(b_player, "test tile " + coord3d_to_string(tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z)), world.get_time()) t = tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x, ref_ground.y + 2).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z)) //gui.add_message_at(b_player, "add tile " + coord3d_to_string(tile_x(ref_ground.x, ref_ground.y + 2, ref_ground.z)), world.get_time()) } } if ( tiles_build_r.len() == 0 && ref_ground.get_way_dirs(wt) == 12 ) { t = tile_x(ref_ground.x + 1, ref_ground.y, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x + 1, ref_ground.y).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(ref_ground.x + 1, ref_ground.y, ref_ground.z)) } t = tile_x(ref_ground.x + 2, ref_ground.y, ref_ground.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x + 2, ref_ground.y).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(ref_ground.x + 2, ref_ground.y, ref_ground.z)) } }else if ( tiles_build_r.len() > way_len-2 && i <= way_len ) { // no build tile } else if ( tiles_build_r.len() < way_len-2 ) { t = tile_x(ref_ground.x + 2, ref_ground.y, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(ref_ground.x + 2, ref_ground.y).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(ref_ground.x + 2, ref_ground.y, ref_ground.z)) } } tiles.append(ref_ground) if ( print_message_box == 4 && i < way_len-2 ) { gui.add_message_at(b_player, " 12 tiles_build_r[" + i + "] " + coord3d_to_string(tiles_build_r[i]), world.get_time()) gui.add_message_at(b_player, " 3 tiles_build_l[" + i + "] " + coord3d_to_string(tiles_build_l[i]), world.get_time()) } } } if ( print_message_box == 1 ) { gui.add_message_at(b_player, " tiles_build_r " + tiles_build_r.len(), tiles_build_r[0]) gui.add_message_at(b_player, " tiles_build_l " + tiles_build_l.len(), world.get_time()) } // test fields flat local tl = 0 local tr = 0 if ( ( ( tiles_build_r.len() == way_len || tiles_build_l.len() == way_len ) && diagonal_st == 0 ) || ( ( tiles_build_r.len() == (way_len-2) || tiles_build_l.len() == (way_len-2) ) && diagonal_st > 0 ) ) { if ( diagonal_st == 0 ) { // test straight way //gui.add_message_at(b_player, " -- slope test straight way ", world.get_time()) if ( tiles_build_r.len() == way_len ) { for ( local i = 0; i < way_len; i++ ) { if ( tiles_build_r[i].get_slope() == 0 ) { tr++ } } } if ( tiles_build_l.len() == way_len ) { for ( local i = 0; i < way_len; i++ ) { if ( tiles_build_l[i].get_slope() == 0 ) { tl++ } } } } else if ( diagonal_st == 6 || diagonal_st == 9 || diagonal_st == 3 || diagonal_st == 12 ) { // test diagonal way //gui.add_message_at(b_player, " -- slope test diagonal way ", world.get_time()) if ( tiles_build_r.len() == way_len - 2 ) { for ( local i = 0; i < way_len - 2; i++ ) { //gui.add_message_at(b_player, " -- tiles_build_r[" + i + "] " + coord3d_to_string(tiles_build_r[i]), world.get_time()) if ( tiles_build_r[i].get_slope() == 0 ) { tr++ } } } if ( tiles_build_l.len() == way_len - 2 ) { for ( local i = 0; i < way_len - 2; i++ ) { //gui.add_message_at(b_player, " -- tiles_build_l[" + i + "] " + coord3d_to_string(tiles_build_l[i]), world.get_time()) if ( tiles_build_l[i].get_slope() == 0 ) { tl++ } } } } //gui.add_message_at(our_player, " tiles r get_slope() = 0 " + tr, tiles_build_r[0]) //gui.add_message_at(our_player, " tiles l get_slope() = 0 " + tl, tiles_build_l[0]) //gui.add_message_at(our_player, " way_len " + way_len, world.get_time()) local tiles_build = null local err = null local terraform = 0 if ( ( tr == way_len && diagonal_st == 0 ) || ( tr == way_len - 2 && diagonal_st > 0 ) ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "build flat right ", start_field) } tiles_build = tiles_build_r tr = way_len tl = 0 } else if ( ( tl == way_len && diagonal_st == 0 ) || ( tl == way_len - 2 && diagonal_st > 0 ) ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "build flat left ", start_field) } tiles_build = tiles_build_l tl = way_len tr = 0 } else if ( (( tr < tiles_build_r.len() && tiles_build_r.len() == way_len && diagonal_st == 0 ) || ( tr < tiles_build_r.len() && tiles_build_r.len() == way_len - 2 && diagonal_st > 0 )) && tr >= tl ) { // check terraform tr if ( print_message_box == 1 ) { gui.add_message_at(b_player, "build right check terraform", world.get_time()) } tiles_build = tiles_build_r tr = way_len tl = 0 terraform = 1 } else if ( ( tl < tiles_build_l.len() && tiles_build_l.len() == way_len && diagonal_st == 0 ) || ( tl < tiles_build_l.len() && tiles_build_l.len() == way_len - 2 && diagonal_st > 0 ) ) { // check terraform tl if ( print_message_box == 1 ) { gui.add_message_at(b_player, "build left check terraform", world.get_time()) } tiles_build = tiles_build_l tl = way_len tr = 0 terraform = 1 } if ( terraform == 1 ) { // change terraform local g = terraform_way(tiles, tiles_build, tr, tl, way_len, d) if ( g != false ) { tr = g[0] tl = g[1] if ( way_len > g[2] ) { tiles_build.remove(tiles_build.len()-1) tiles.remove(tiles.len()-1) way_len = g[2] } } else { gui.add_message_at(b_player, "Error terraform", world.get_time()) return false } } // set build left or right /*if ( tr == way_len && tl == way_len ) { } else */ if ( tr == way_len ) { tiles_build = tiles_build_r } else if ( tl == way_len ) { tiles_build = tiles_build_l } // set diagonal start direction correct if ( diagonal_st > 0 ) { // tr = ribi 6 / // tl = ribi 9 / if ( ( tiles[0].get_way_dirs(wt) == 9 && tr == way_len ) || ( tiles[0].get_way_dirs(wt) == 6 && tl == way_len ) || ( tiles[0].get_way_dirs(wt) == 3 && tr == way_len ) || ( tiles[0].get_way_dirs(wt) == 12 && tl == way_len ) ) { //gui.add_message_at(b_player, "remove first tile from tiles[] ", world.get_time()) local n = tiles.slice(1) tiles.clear() tiles = n } else if ( tiles.len() > way_len ) { //gui.add_message_at(b_player, "remove last tile from tiles[] ", world.get_time()) local n = tiles.slice(0, way_len) tiles.clear() tiles = n } diagonal_st = tiles[0].get_way_dirs(wt) } if ( tr == way_len ) { if ( print_message_box == 3 ) { gui.add_message_at(b_player, "build flat right ", start_field) } if ( settings.get_drive_on_left() ) { if ( d == 10 ) { signal = [{coor=coord3d(tiles_build[1].x, tiles_build[1].y, tiles_build[1].z), ribi=8}, {coor=coord3d(tiles[tiles.len()-2].x, tiles[tiles.len()-2].y, tiles[tiles.len()-2].z), ribi=2}] //gui.add_message_at(b_player, "settings.get_drive_on_left() signals 10 tr " + coord3d_to_string(tiles_build[1]) + " & " + coord3d_to_string(tiles[6]), world.get_time()) } else if ( d == 5 ) { signal = [{coor=coord3d(tiles_build[tiles.len()-2].x, tiles_build[tiles.len()-2].y, tiles_build[tiles.len()-2].z), ribi=4}, {coor=coord3d(tiles[1].x, tiles[1].y, tiles[1].z), ribi=1}] //gui.add_message_at(b_player, "settings.get_drive_on_left() signals 5 tr " + coord3d_to_string(tiles_build[6]) + " & " + coord3d_to_string(tiles[1]), world.get_time()) } else if ( diagonal_st == 6 ) { // ribi 6 to 6 signal = [{coor=coord3d(tiles_build[0].x, tiles_build[0].y, tiles_build[0].z), ribi=2}, {coor=coord3d(tiles[way_len - 3].x, tiles[way_len - 3].y, tiles[way_len - 3].z), ribi=8}] //gui.add_message_at(b_player, "settings.get_drive_on_left() signals diagonal tr " + coord3d_to_string(tiles_build[0]) + " & " + coord3d_to_string(tiles[way_len - 2]), world.get_time()) } else if ( diagonal_st == 12 ) { signal = [{coor=coord3d(tiles_build[way_len - 3].x, tiles_build[way_len - 3].y, tiles_build[way_len - 3].z), ribi=4}, {coor=coord3d(tiles[1].x, tiles[1].y, tiles[1].z), ribi=1}] //gui.add_message_at(b_player, "settings.get_drive_on_left() signals diagonal tr " + coord3d_to_string(tiles_build[way_len - 3]) + " & " + coord3d_to_string(tiles[1]), world.get_time()) } } else { if ( d == 10 ) { signal = [{coor=coord3d(tiles_build[way_len - 2].x, tiles_build[way_len - 2].y, tiles_build[way_len - 2].z), ribi=2}, {coor=coord3d(tiles[1].x, tiles[1].y, tiles[1].z), ribi=8}] //gui.add_message_at(b_player, "signals 10 tr " + coord3d_to_string(tiles_build[6]) + " & " + coord3d_to_string(tiles[1]), world.get_time()) } else if ( d == 5 ) { signal = [{coor=coord3d(tiles_build[1].x, tiles_build[1].y, tiles_build[1].z), ribi=1}, {coor=coord3d(tiles[way_len - 2].x, tiles[way_len - 2].y, tiles[way_len - 2].z), ribi=4}] //gui.add_message_at(b_player, "signals 5 tr " + coord3d_to_string(tiles_build[1]) + " & " + coord3d_to_string(tiles[6]), world.get_time()) } else if ( diagonal_st == 6 ) { // ribi 6 to 6 signal = [{coor=coord3d(tiles_build[way_len - 3].x, tiles_build[way_len - 3].y, tiles_build[way_len - 3].z), ribi=4}, {coor=coord3d(tiles[1].x, tiles[1].y, tiles[1].z), ribi=1}] //gui.add_message_at(b_player, "signals diagonal tr " + coord3d_to_string(tiles_build[way_len - 3]) + " & " + coord3d_to_string(tiles[1]), world.get_time()) } else if ( diagonal_st == 12 ) { // ribi 12 to 12 signal = [{coor=coord3d(tiles_build[0].x, tiles_build[0].y, tiles_build[0].z), ribi=8}, {coor=coord3d(tiles[way_len - 2].x, tiles[way_len - 2].y, tiles[way_len - 2].z), ribi=2}] //gui.add_message_at(b_player, "signals diagonal tr " + coord3d_to_string(tiles_build[0]) + " & " + coord3d_to_string(tiles[way_len - 2]), world.get_time()) } } } else if ( tl == way_len ) { if ( print_message_box == 3 ) { gui.add_message_at(b_player, "build flat left ", start_field) } if ( settings.get_drive_on_left() ) { if ( d == 10 ) { signal = [{coor=coord3d(tiles_build[way_len - 2].x, tiles_build[way_len - 2].y, tiles_build[way_len - 2].z), ribi=2}, {coor=coord3d(tiles[1].x, tiles[1].y, tiles[1].z), ribi=8}] //gui.add_message_at(b_player, "settings.get_drive_on_left() signals 10 tl " + coord3d_to_string(tiles_build[6]) + " & " + coord3d_to_string(tiles[1]), world.get_time()) } else if ( d == 5 ) { signal = [{coor=coord3d(tiles_build[1].x, tiles_build[1].y, tiles_build[1].z), ribi=1}, {coor=coord3d(tiles[way_len - 2].x, tiles[way_len - 2].y, tiles[way_len - 2].z), ribi=4}] //gui.add_message_at(b_player, "settings.get_drive_on_left() signals 5 tl " + coord3d_to_string(tiles_build[1]) + " & " + coord3d_to_string(tiles[6]), world.get_time()) } else if ( diagonal_st == 9 ) { // ribi 9 to 9 signal = [{coor=coord3d(tiles_build[way_len - 3].x, tiles_build[way_len - 3].y, tiles_build[way_len - 3].z), ribi=8}, {coor=coord3d(tiles[1].x, tiles[1].y, tiles[1].z), ribi=2}] //gui.add_message_at(b_player, "settings.get_drive_on_left() signals diagonal tl " + coord3d_to_string(tiles_build[way_len - 3]) + " & " + coord3d_to_string(tiles[2]), world.get_time()) } else if ( diagonal_st == 3 ) { // ribi 3 to 3 signal = [{coor=coord3d(tiles_build[0].x, tiles_build[0].y, tiles_build[0].z), ribi=1}, {coor=coord3d(tiles[way_len - 2].x, tiles[way_len - 2].y, tiles[way_len - 2].z), ribi=4}] //gui.add_message_at(b_player, "settings.get_drive_on_left() signals diagonal tl " + coord3d_to_string(tiles_build[0]) + " & " + coord3d_to_string(tiles[way_len - 2]), world.get_time()) } } else { if ( d == 10 ) { signal = [{coor=coord3d(tiles_build[1].x, tiles_build[1].y, tiles_build[1].z), ribi=8}, {coor=coord3d(tiles[6].x, tiles[6].y, tiles[6].z), ribi=2}] //gui.add_message_at(b_player, "signals 10 tl " + coord3d_to_string(tiles_build[1]) + " & " + coord3d_to_string(tiles[6]), world.get_time()) } else if ( d == 5 ) { signal = [{coor=coord3d(tiles_build[way_len - 1].x, tiles_build[way_len - 1].y, tiles_build[way_len - 1].z), ribi=4}, {coor=coord3d(tiles[1].x, tiles[1].y, tiles[1].z), ribi=1}] //gui.add_message_at(b_player, "signals 5 tl " + coord3d_to_string(tiles_build[6]) + " & " + coord3d_to_string(tiles[1]), world.get_time()) } else if ( diagonal_st == 9 ) { // ribi 9 to 9 signal = [{coor=coord3d(tiles_build[0].x, tiles_build[0].y, tiles_build[0].z), ribi=1}, {coor=coord3d(tiles[way_len - 2].x, tiles[way_len - 2].y, tiles[way_len - 2].z), ribi=4}] //gui.add_message_at(b_player, "signals diagonal tl " + coord3d_to_string(tiles_build[0]) + " & " + coord3d_to_string(tiles[way_len - 1]), world.get_time()) } else if ( diagonal_st == 3 ) { // ribi 3 to 3 signal = [{coor=coord3d(tiles_build[way_len - 3].x, tiles_build[way_len - 3].y, tiles_build[way_len - 3].z), ribi=2}, {coor=coord3d(tiles[1].x, tiles[1].y, tiles[1].z), ribi=8}] //gui.add_message_at(b_player, "signals diagonal tl " + coord3d_to_string(tiles_build[way_len - 3]) + " & " + coord3d_to_string(tiles[1]), world.get_time()) } } } if ( tiles_build == null ) { //gui.add_message_at(b_player, " ERROR no double way found " + coord3d_to_string(tiles[0]), start_field) return false } // build way local err = null local sig_field = 0 local sig_tile_new = null if ( start_field.get_way_dirs(wt) == 5 || start_field.get_way_dirs(wt) == 10 ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "5/10 build tiles[0] " + coord3d_to_string(tiles[0]) + " to tiles_build[0] " + coord3d_to_string(tiles_build[0]), start_field) } // connect diagonal way local b_tile = null local tile_a = null local tile_b = null local tile_check = 0 if ( tl == way_len ) { if ( start_field.get_way_dirs(wt) == 5 ) { tile_a = tile_x(tiles[0].x, tiles[0].y-1, tiles[0].z) tile_b = tile_x(tiles[0].x-1, tiles[0].y-1, tiles[0].z) if ( tile_a.get_way_dirs(wt) == 12 && (tile_b.get_way_dirs(wt) == 3 || (tile_b.get_way_dirs(wt) == 10 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b } tile_check = 1 //gui.add_message_at(b_player, "(2765) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( start_field.get_way_dirs(wt) == 10 ) { tile_a = tile_x(tiles[0].x-1, tiles[0].y, tiles[0].z) tile_b = tile_x(tiles[0].x-1, tiles[0].y-1, tiles[0].z) if ( (tile_a.get_way_dirs(wt) == 12 || tile_a.get_way_dirs(wt) == 3) && (tile_b.get_way_dirs(wt) == 3 || tile_b.get_way_dirs(wt) == 12 || (tile_b.get_way_dirs(wt) == 5 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b } tile_check = 2 //gui.add_message_at(b_player, "(2773) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } } else if ( tr == way_len ) { if ( start_field.get_way_dirs(wt) == 5 ) { tile_a = tile_x(tiles[0].x, tiles[0].y-1, tiles[0].z) tile_b = tile_x(tiles[0].x+1, tiles[0].y-1, tiles[0].z) if ( tile_a.get_way_dirs(wt) == 6 && (tile_b.get_way_dirs(wt) == 9 || (tile_b.get_way_dirs(wt) == 10 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b sig_tile_new = "tr5" } tile_check = 3 //gui.add_message_at(b_player, "(2785) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( start_field.get_way_dirs(wt) == 10 ) { tile_a = tile_x(tiles[0].x-1, tiles[0].y, tiles[0].z) tile_b = tile_x(tiles[0].x-1, tiles[0].y+1, tiles[0].z) if ( tile_a.get_way_dirs(wt) == 6 && (tile_b.get_way_dirs(wt) == 9 || (tile_b.get_way_dirs(wt) == 5 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b sig_tile_new = "tr10t" } tile_check = 4 //gui.add_message_at(b_player, "(2795) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } } if ( b_tile == null ) { err = command_x.build_way(b_player, tiles[0], tiles_build[0], way_obj, true) } else { err = command_x.build_way(b_player, b_tile, tiles_build[0], way_obj, true) } if ( err == null ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[0] " + coord3d_to_string(tiles_build[0]) + " to tiles_build[" + (way_len - 1) + "] " + coord3d_to_string(tiles_build[way_len - 1]), start_field) } err = command_x.build_way(b_player, tiles_build[0], tiles_build[way_len - 1], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles[0]) + " to tile " + coord3d_to_string(tiles_build[way_len - 1]), tiles[0]) err = null } if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[" + (way_len - 1) + "] " + coord3d_to_string(tiles_build[way_len - 1]) + " to tiles[" + (way_len - 1) + "] " + coord3d_to_string(tiles[way_len - 1]), start_field) } // connect diagonal way b_tile = null if ( tile_check == 1 ) { tile_a = tile_x(tiles[way_len - 1].x, tiles[way_len - 1].y+1, tiles[way_len - 1].z) tile_b = tile_x(tiles[way_len - 1].x-1, tiles[way_len - 1].y+1, tiles[way_len - 1].z) if ( tile_a.get_way_dirs(wt) == 9 && (tile_b.get_way_dirs(wt) == 6 || tile_b.get_way_dirs(wt) == 10 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0) ) { b_tile = tile_b sig_tile_new = "tl5" } //gui.add_message_at(b_player, "(2827) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( tile_check == 2 ) { tile_a = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y, tiles[way_len - 1].z) tile_b = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y-1, tiles[way_len - 1].z) if ( tile_a.get_way_dirs(wt) == 6 && (tile_b.get_way_dirs(wt) == 9 || tile_b.get_way_dirs(wt) == 10 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0) ) { b_tile = tile_b sig_tile_new = "tl5" } //gui.add_message_at(b_player, "(2836) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( tile_check == 3 ) { tile_a = tile_x(tiles[way_len - 1].x, tiles[way_len - 1].y+1, tiles[way_len - 1].z) tile_b = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y+1, tiles[way_len - 1].z) if ( tile_a.get_way_dirs(wt) == 3 && (tile_b.get_way_dirs(wt) == 12 || (tile_b.get_way_dirs(wt) == 5 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b sig_tile_new = "tr5t" } //gui.add_message_at(b_player, "(2844) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( tile_check == 4 ) { tile_a = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y, tiles[way_len - 1].z) tile_b = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y+1, tiles[way_len - 1].z) if ( tile_a.get_way_dirs(wt) == 12 && (tile_b.get_way_dirs(wt) == 3 || (tile_b.get_way_dirs(wt) == 5 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b sig_tile_new = "tr10" } //gui.add_message_at(b_player, "(2853) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } if ( b_tile == null ) { err = command_x.build_way(b_player, tiles_build[way_len - 1], tiles[way_len - 1], way_obj, true) /*if ( settings.get_drive_on_left() ) { local st = tile_x(signal[0].coor.x, signal[0].coor.y, signal[0].coor.z).get_way_dirs(wt) if ( st == 11 || st == 13 || st == 7 || st == 14 ) { sig_field = 1 } } else {*/ local st = tile_x(signal[1].coor.x, signal[1].coor.y, signal[1].coor.z).get_way_dirs(wt) if ( st == 11 || st == 13 || st == 7 || st == 14 ) { sig_field = 1 } //} } else { err = command_x.build_way(b_player, tiles_build[way_len - 1], b_tile, way_obj, true) } if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[way_len - 1]) + " to tile " + coord3d_to_string(tiles[way_len - 1]), tiles[0]) err = null // remove last tile local tool = command_x(tool_remover) tool.work(our_player, tiles_build[way_len - 1]) // build err = command_x.build_way(b_player, tiles_build[way_len - 2], tiles[way_len - 2], way_obj, true) /*if ( settings.get_drive_on_left() ) { local st = tile_x(signal[0].coor.x, signal[0].coor.y, signal[0].coor.z).get_way_dirs(wt) if ( st == 11 || st == 13 || st == 7 || st == 14 ) { sig_field = 1 } } else {*/ local st = tile_x(signal[1].coor.x, signal[1].coor.y, signal[1].coor.z).get_way_dirs(wt) if ( st == 11 || st == 13 || st == 7 || st == 14 ) { sig_field = 1 } /*if ( signal[0].coor.x == tiles_build[way_len - 2].x && signal[0].coor.y == tiles_build[way_len - 2].y && signal[0].coor.z == tiles_build[way_len - 2].z ) { // 4 signal[0].ribi = 8 }*/ //} } } else { return false } } else if ( tiles[0].get_way_dirs(wt) == 6 ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "6 build tiles[0] " + coord3d_to_string(tiles[0]) + " to tiles_build[1] " + coord3d_to_string(tiles_build[1]), start_field) } err = command_x.build_way(b_player, tiles[0], tiles_build[1], way_obj, true) if ( err == null ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[1] " + coord3d_to_string(tiles_build[1]) + " to tiles_build[" + (way_len - 3) + "] " + coord3d_to_string(tiles_build[way_len - 3]), start_field) } err = command_x.build_way(b_player, tiles_build[1], tiles_build[way_len - 3], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[1]) + " to tile " + coord3d_to_string(tiles_build[way_len - 3]), tiles[0]) err = null } if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[" + (way_len - 3) + "] " + coord3d_to_string(tiles_build[way_len - 3]) + " to tiles[" + (way_len - 1) + "] " + coord3d_to_string(tiles[way_len - 1]), start_field) } err = command_x.build_way(b_player, tiles_build[way_len - 3], tiles[way_len - 1], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[way_len - 3]) + " to tile " + coord3d_to_string(tiles[way_len]), tiles[0]) err = null } } else { return false } } else if ( tiles[0].get_way_dirs(wt) == 9 ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "9 build tiles[0] " + coord3d_to_string(tiles[0]) + " to tiles_build[1] " + coord3d_to_string(tiles_build[1]), start_field) } err = command_x.build_way(b_player, tiles[0], tiles_build[1], way_obj, true) if ( err == null ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[1] " + coord3d_to_string(tiles_build[1]) + " to tiles_build[" + (way_len - 4) + "] " + coord3d_to_string(tiles_build[way_len - 4]), start_field) } err = command_x.build_way(b_player, tiles_build[1], tiles_build[way_len - 4], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[1]) + " to tile " + coord3d_to_string(tiles_build[way_len - 4]), tiles[0]) err = null } if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[" + (way_len - 4) + "] " + coord3d_to_string(tiles_build[way_len - 4]) + " to tiles[" + (way_len - 1) + "] " + coord3d_to_string(tiles[way_len - 1]), start_field) } err = command_x.build_way(b_player, tiles_build[way_len - 4], tiles[way_len - 1], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[way_len - 4]) + " to tile " + coord3d_to_string(tiles[way_len - 1]), tiles[0]) err = null } } } else if ( tiles[0].get_way_dirs(wt) == 3 ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "3 build tiles[0] " + coord3d_to_string(tiles[0]) + " to tiles_build[1] " + coord3d_to_string(tiles_build[1]), start_field) } err = command_x.build_way(b_player, tiles[0], tiles_build[1], way_obj, true) if ( err == null ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[1] " + coord3d_to_string(tiles_build[1]) + " to tiles_build[" + (way_len - 4) + "] " + coord3d_to_string(tiles_build[way_len - 4]), start_field) } err = command_x.build_way(b_player, tiles_build[1], tiles_build[way_len - 4], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[1]) + " to tile " + coord3d_to_string(tiles_build[way_len - 4]), tiles[0]) err = null } if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[" + (way_len - 4) + "] " + coord3d_to_string(tiles_build[way_len - 4]) + " to tiles[" + (way_len - 1) + "] " + coord3d_to_string(tiles[way_len - 1]), start_field) } err = command_x.build_way(b_player, tiles_build[way_len - 4], tiles[way_len - 1], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[way_len - 4]) + " to tile " + coord3d_to_string(tiles[way_len - 1]), tiles[0]) err = null } } } else if ( tiles[0].get_way_dirs(wt) == 12 ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "12 build tiles[0] " + coord3d_to_string(tiles[0]) + " to tiles_build[1] " + coord3d_to_string(tiles_build[1]), start_field) } err = command_x.build_way(b_player, tiles[0], tiles_build[1], way_obj, true) if ( err == null ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[1] " + coord3d_to_string(tiles_build[1]) + " to tiles_build[" + (way_len - 3) + "] " + coord3d_to_string(tiles_build[way_len - 3]), start_field) } err = command_x.build_way(b_player, tiles_build[1], tiles_build[way_len - 3], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[1]) + " to tile " + coord3d_to_string(tiles_build[way_len - 3]), tiles[0]) err = null } local t = 1 if ( tiles[way_len - t].get_way_dirs(wt) == 3 ) { t = 0 } if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[" + (way_len - 3) + "] " + coord3d_to_string(tiles_build[way_len - 3]) + " to tiles[" + (way_len - 1) + "] " + coord3d_to_string(tiles[way_len - 1]), start_field) } err = command_x.build_way(b_player, tiles_build[way_len - 3], tiles[way_len - 1], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles_build[way_len - 3]) + " to tile " + coord3d_to_string(tiles[way_len - 1]), tiles[0]) err = null } } } // build signals if ( diagonal_st == 0 ) { // build signals // select signal tool local obj_sign = find_signal("is_signal", wt) local sig_1 = tile_x(signal[0].coor.x, signal[0].coor.y, signal[0].coor.z) local sig_2 = tile_x(signal[1].coor.x, signal[1].coor.y, signal[1].coor.z) if ( sig_1.find_object(mo_way) != null && sig_2.find_object(mo_way) != null ) { local test = 0 /*local tiles = [3, 5, 6, 7, 9, 10, 11, 12] for ( local i = 0; i < tiles.len(); i++ ) { if ( sig_1.get_way_dirs(wt) == tiles[i] ) { test++ } if ( sig_2.get_way_dirs(wt) == tiles[i] ) { test++ } } if ( test == 2 ) {*/ for ( local j=0; j < signal.len(); j++ ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "signal to tile " + coord3d_to_string(tile_x(signal[j].coor.x, signal[j].coor.y, signal[j].coor.z)), start_field) } local fx = 0 local fy = 0 if ( j == 1 && d == 5 ) { //&& signal[j].coor.y == tiles[tiles.len()-2].y fy = sig_field } else if ( j == 1 && d == 10 ) { //&& signal[j].coor.x == tiles[tiles.len()-2].x fx = sig_field } local s_ribi = signal[j].ribi test = tile_x(signal[j].coor.x-fx, signal[j].coor.y-fy, signal[j].coor.z).get_way_dirs(wt) //gui.add_message_at(b_player, "signal set to ribi " + s_ribi, world.get_time()) if ( print_message_box == 3 ) { gui.add_message_at(b_player, "signal to tile " + coord3d_to_string(tile_x(signal[j].coor.x, signal[j].coor.y, signal[j].coor.z)), world.get_time()) } local signal_build_tile = tile_x(signal[j].coor.x-fx, signal[j].coor.y-fy, signal[j].coor.z) if ( settings.get_drive_on_left() ) { } else { // set s_ribi new by double track lenght 7 if ( test == 9 && s_ribi == 2 ) { s_ribi = 1 } if ( test == 12 && s_ribi == 1 ) { s_ribi = 8 } if ( test == 3 && s_ribi == 4 ) { s_ribi = 2 } if ( test == 6 && s_ribi == 8 ) { s_ribi = 4 } if ( print_message_box == 1 && signal[j].ribi != s_ribi ) { gui.add_message_at(b_player, coord3d_to_string(tile_x(signal[j].coor.x-fx, signal[j].coor.y-fy, signal[j].coor.z)) + " signal set to ribi new " + s_ribi, world.get_time()) } //gui.add_message_at(b_player, "sig_tile_new " + sig_tile_new, world.get_time()) if ( (sig_tile_new == "tr10" && j == 0) || (sig_tile_new == "tl5" && j == 0) ) { signal_build_tile = tiles_build[way_len - 1] //} else if ( sig_tile_new == "tr5s" ) { } else if ( (sig_tile_new == "tl10" && j == 0) || (sig_tile_new == "tr5" && j == 0) ) { signal_build_tile = tiles_build[0] } else if ( (sig_tile_new == "tr10t" && j == 1) || (sig_tile_new == "tr5t" && j == 1) ) { signal_build_tile = tiles[0] } } // check ribi test = signal_build_tile.get_way_dirs(wt) if ( test == 3 && s_ribi == 4 ) { s_ribi = 2 } while(true){ local err = command_x.build_sign_at(b_player, signal_build_tile, obj_sign) local ribi = signal_build_tile.get_way_dirs_masked(wt) if (ribi == s_ribi) break } } //} } if ( sig_tile_new != null ) { //::debug.pause() } } else if ( diagonal_st == 6 || diagonal_st == 9 || diagonal_st == 3 || diagonal_st == 12 ) { // build signals local list = sign_desc_x.get_available_signs(wt) local obj_sign = null foreach(o in list) { if ( print_message_box == 2 ) { gui.add_message_at(b_player, "signals " + o.get_name(), start_field) } if (o.is_signal()) { obj_sign = o break } } local sig_1 = tile_x(signal[0].coor.x, signal[0].coor.y, signal[0].coor.z) local sig_2 = tile_x(signal[1].coor.x, signal[1].coor.y, signal[1].coor.z) if ( sig_1.find_object(mo_way) != null && sig_2.find_object(mo_way) != null ) { local tiles = [3, 5, 6, 9, 10, 12] local test = 0 for ( local i = 0; i < tiles.len(); i++ ) { if ( sig_1.get_way_dirs(wt) == tiles[i] ) { test++ } if ( sig_2.get_way_dirs(wt) == tiles[i] ) { test++ } } if ( test == 2 ) { for ( local j=0; j < signal.len(); j++ ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "signal to tile " + coord3d_to_string(tile_x(signal[j].coor.x, signal[j].coor.y, signal[j].coor.z)), world.get_time()) } while(true){ local err = command_x.build_sign_at(b_player, tile_x(signal[j].coor.x, signal[j].coor.y, signal[j].coor.z), obj_sign) local ribi = tile_x(signal[j].coor.x, signal[j].coor.y, signal[j].coor.z).get_way_dirs_masked(wt) if (ribi == signal[j].ribi) break } } } } } print_message_box = 0 return true } } /// @publicsection /** * * check the route for build double ways * * @param start = start field line * @param end = end field line * @param wt = waytype * @param l = stations distance * @param c = count of double ways * @param c=0 -> no build double ways - return route array * * @retur tile_x array start tiles for double ways */ function check_way_line(start, end, wt, l, c, r_line) { /* * 1 = * 2 = straight * 3 = diagonal * 4 = */ if ( debug ) ::debug.set_pause_on_error(true) //debug.pause local print_message_box = 0 local print_message = 0 local message_text = [] if ( print_message_box > 0 ) { gui.add_message_at(our_player, " ### check_way_line ### " + coord3d_to_string(start), start) } local nexttile = [] //[tile_x(start.x, start.y, start.z)] local station_len = 0 //if (wt == wt_rail) { local asf = astar_route_finder(wt) local result = asf.search_route([start], [end]) // result is contains routes-array or error message // route is backward from end to start if ("err" in result) { //gui.add_message_at(our_player, " ### no route found: " + result.err, start) return nexttile } else { //gui.add_message_at(our_player, " ### route found: length = " + result.routes.len(), start) // route found, mark tiles local marked = {} local a = 0 foreach(node in result.routes) { local tile = tile_x(node.x, node.y, node.z) nexttile.append(tile) // count station len start/end if ( tile.find_object(mo_building) != null && a == 0 ) { station_len++ } else { if ( a == 0 ) { station_len += 2 } a = 1 } /* try { tile.mark(); marked.append(tile) } catch(ev) {}*/ } //::debug.pause() sleep() // unmark if game is unpaused again /* foreach(tile in marked) { tile.unmark(); } sleep()*/ } //} // optimize way line befor build double ways //optimize_way_line(nexttile, wt) /* gui.add_message_at(our_player, "end line " + coord3d_to_string(end), end) gui.add_message_at(our_player, "length " + l, world.get_time()) gui.add_message_at(our_player, "count " + c, world.get_time()) */ nexttile.reverse() if ( c == 0 ) { // return way line if ( l == 0 ) { // return way line len return nexttile.len() } else { // return way array return nexttile } } local d = 0 // count double ways local start_fields = [] local start_fields_id = [] local as = 0 local fc = 0 local dfcl = 0 local dfcr = 0 local dc = 0 local di = 0 local r = 0 local s = [] local l_split = 25 if ( station_len < 8 ) { station_len = 8 } else if ( station_len > 8 ) { l_split = station_len + 1 } if ( c > 0 ) { // distance double ways //local as = (l / (c + 1)).tointeger() as = (l / l_split).tointeger() //as = as - ( c * 16 ) if ( print_message_box > 0 ) { gui.add_message_at(our_player, c + " double way search", world.get_time()) message_text.append("l_split " + l_split + " as " + as + " l " + l + " c " + c) } for (local i = 0; i < as; i++ ) { if ( i == 0 ) { //if ( c == 1 ) { // s.append(15) //} else { s.append(l_split - (l_split * 0.3).tointeger() ) //} } else { s.append(s[i-1]+(l_split * 0.7).tointeger()) } message_text.append(" s[" + (i) + "] " + s[i]) } } else { s.append(1) } local sign = 0 local reset = 0 local stl = 0 local str = 0 local dst = 0 local i = 8 while ( i < (nexttile.len() - 1 - station_len) ) { i++ // check to signal local sig = nexttile[i-1].find_object(mo_signal) //gui.add_message_at(our_player, "find_object(mo_signal) " + sig, nexttile[i-1]) if ( sig != null && c > 0 ) { sign++ if ( sign == c ) { //gui.add_message_at(our_player, c + " double way found, no build ", world.get_time()) return true } print_message = 0 //gui.add_message_at(our_player, "signals " + sign, world.get_time()) } di = dc dc = d /*if ( i < station_len ) { st_tile = nexttile[i-1].find_object(mo_building) }*/ // len from double track local way_len = 8 if ( station_len > 8 ) { way_len = station_len if ( d == 6 || d == 9 || d == 3 || d == 12) { way_len = station_len + 1 } } else { if ( d == 6 || d == 9 || d == 3 || d == 12) { way_len = station_len + 1 } } d = nexttile[i].get_way_dirs(wt) // diagonal start ribi if ( ( dc == 5 && d == 6 ) || ( dc == 5 && d == 9 ) ) { dst = 5 } else if ( ( dc == 10 && d == 6 ) || ( dc == 10 && d == 9 ) ) { dst = 10 } else if ( ( dc == 5 && d == 3 ) || ( dc == 5 && d == 12 ) ) { dst = 5 } else if ( ( dc == 10 && d == 3 ) || ( dc == 10 && d == 12 ) ) { dst = 10 } else if ( d == 5 || d == 10 ) { dst = 0 } local t = nexttile[i] local st = 0 // st == 1 no field for double way if ( dst == 0 && fc == 0 && ( t.get_slope() > 0 || t.is_bridge() || t.has_two_ways() ) ) { // check slope, bridge and crossing to start field for double way if ( i >= s[r] && print_message_box == 2 ) { gui.add_message_at(our_player, " ### check first tile " + coord3d_to_string(t), t) } st = 1 } else if ( t.is_bridge() || t.has_two_ways() ) { // check bridge and crossing if ( i >= s[r] && print_message_box == 2 ) { gui.add_message_at(our_player, " ### check bridge " + coord3d_to_string(t), t) } st = 1 } else if ( t.get_way_dirs(wt) == 10 && t.find_object(mo_building) == null ) { local check_tile_str = tile_x(t.x, t.y + 1, t.z) local check_tile_stl = tile_x(t.x, t.y - 1, t.z) // check left & right ground and empty if ( world.is_coord_valid(check_tile_str) ) { // field right empty and ground if ( tile_x(t.x, t.y + 1, t.z).is_ground() && tile_x(t.x, t.y + 1, t.z).is_empty() ) { str++ } else { str = 0 } } else { str = 0 } if ( world.is_coord_valid(check_tile_stl) ) { // field left empty and ground if ( tile_x(t.x, t.y - 1, t.z).is_ground() && tile_x(t.x, t.y - 1, t.z).is_empty() ) { stl++ } else { stl = 0 } } else { stl = 0 } // end diagonal way dst = 0 dfcl = 0 dfcr = 0 } else if ( t.get_way_dirs(wt) == 5 && t.find_object(mo_building) == null ) { // check left & right out of map and ground and empty local check_tile_str = tile_x(t.x + 1, t.y, t.z) local check_tile_stl = tile_x(t.x - 1, t.y, t.z) if ( world.is_coord_valid(check_tile_str) ) { // field right empty and ground if ( check_tile_str.is_ground() && check_tile_str.is_empty() ) { str++ } else { str = 0 } } else { str = 0 } if ( world.is_coord_valid(check_tile_stl) ) { // field left empty and ground if ( check_tile_stl.is_ground() && check_tile_stl.is_empty() ) { stl++ } else { stl = 0 } } else { stl = 0 } // end diagonal way dst = 0 dfcl = 0 dfcr = 0 } else if ( dst > 0 && t.get_way_dirs(wt) == 6 || t.get_way_dirs(wt) == 9 ) { if ( print_message_box == 3 && i >= s[0] && i < (s[0] + way_len) && str == 0 && stl == 0 ) { gui.add_message_at(our_player, " # test 6/9 " + coord3d_to_string(t), t) gui.add_message_at(our_player, " diagonal start dst " + dst, world.get_time()) gui.add_message_at(our_player, " dc " + dc + " dfcl " + dfcl + " dfcr " + dfcr + " - stl " + stl + " - str " + str, world.get_time()) } if ( dst == 5 || dst == 10 ) { local check_tile_strs = null local check_tile_str = null local check_tile_stls = null local check_tile_stl = null if ( nexttile[i-2].x < nexttile[i-1].x || nexttile[i-2].y > nexttile[i-1].y ) { check_tile_strs = tile_x(t.x, t.y - 1, t.z) check_tile_str = tile_x(t.x, t.y - 2, t.z) check_tile_stls = tile_x(t.x + 1, t.y, t.z) check_tile_stl = tile_x(t.x + 2, t.y, t.z) //gui.add_message_at(our_player, " diagonal test A " + " i " + i + " - tile check_tile_str " + coord3d_to_string(check_tile_str), world.get_time()) } else { check_tile_strs = tile_x(t.x - 1, t.y, t.z) check_tile_str = tile_x(t.x - 2, t.y, t.z) check_tile_stls = tile_x(t.x, t.y + 1, t.z) check_tile_stl = tile_x(t.x, t.y + 2, t.z) //gui.add_message_at(our_player, " diagonal test B " + " i " + i + " - tile check_tile_str " + coord3d_to_string(check_tile_str), world.get_time()) } local tile_on_map_r = 0 local tile_on_map_l = 0 if ( world.is_coord_valid(check_tile_strs) && world.is_coord_valid(check_tile_str) ) { // tile is in range map tile_on_map_r = 1 } else { // tile is out of range map str = 0 } if ( world.is_coord_valid(check_tile_stls) && world.is_coord_valid(check_tile_stl) ) { // tile is in range map tile_on_map_l = 1 } else { // tile is out of range map stl = 0 } if ( ( dc == 6 && t.get_way_dirs(wt) == 9 ) || ( dc == 9 && t.get_way_dirs(wt) == 6 ) ) { if ( t.get_way_dirs(wt) == 6 && str == 0 ) { if ( print_message_box == 3 && i >= s[0] && i < (s[0] + way_len) ) { gui.add_message_at(our_player, " - check start ribi 6 " + coord3d_to_string(t), t) } if ( tile_on_map_r == 1 ) { if ( check_tile_strs.is_ground() && check_tile_strs.is_empty() ) { //&& tile_x(t.x, t.y - 2, t.z).is_ground() && tile_x(t.x, t.y - 2, t.z).is_empty() str++ } else { str = 0 } } else { //gui.add_message_at(our_player, " ERROR test tile out of map " + coord3d_to_string(check_tile_strs) + " / " + coord3d_to_string(check_tile_str), t) } } else if ( t.get_way_dirs(wt) == 9 && stl == 0 ) { if ( print_message_box == 3 && i >= s[0] && i < (s[0] + way_len) ) { gui.add_message_at(our_player, " - check start ribi 9 " + coord3d_to_string(t), t) } if ( tile_on_map_l == 1 ) { if ( check_tile_stls.is_ground() && check_tile_stls.is_empty() ) { //&& tile_x(t.x, t.y + 2, t.z).is_ground() && tile_x(t.x, t.y + 2, t.z).is_empty() stl++ } else { stl = 0 } } else { //gui.add_message_at(our_player, " ERROR test tile out of map " + coord3d_to_string(check_tile_stls) + " / " + coord3d_to_string(check_tile_stl), t) } } if ( t.get_way_dirs(wt) == 6 || t.get_way_dirs(wt) == 9 ) { if ( print_message_box == 3 && i >= s[0] && i < (s[0] + way_len) ) { gui.add_message_at(our_player, " - check ribi 6/9 l/r " + coord3d_to_string(t) + " d " + t.get_way_dirs(wt), t) } if ( tile_on_map_r == 1 ) { if ( str > 0 && str <= way_len - 3 ) { if ( check_tile_str.is_ground() && check_tile_str.is_empty() ) { //gui.add_message_at(player_x(1), " + str " + str, world.get_time()) str++ } else { str = 0 } } else if ( str > way_len - 3 && str <= way_len ) { //gui.add_message_at(player_x(1), " ++ str " + str, world.get_time()) str++ } } else { //gui.add_message_at(our_player, "(3320) ERROR test tile out of map ", t) } if ( tile_on_map_l == 1 ) { if ( stl > 0 && stl <= way_len - 3 ) { if ( check_tile_stl.is_ground() && check_tile_stl.is_empty() ) { //gui.add_message_at(player_x(1), " + stl " + stl, world.get_time()) stl++ } else { stl = 0 } } else if ( stl > way_len - 3 && stl <= way_len ) { //gui.add_message_at(player_x(1), " ++ stl " + stl, world.get_time()) stl++ } } else { //gui.add_message_at(our_player, "(3336) ERROR test tile out of map ", t) } } if ( print_message_box == 2 && i >= s[0] && i < (s[0] + way_len + 1) ) { gui.add_message_at(our_player, " -- stl " + stl + " - str " + str + " way_len " + way_len, world.get_time()) } } } // end straight way fc = 0 } else if ( dst > 0 && t.get_way_dirs(wt) == 3 || t.get_way_dirs(wt) == 12 ) { if ( print_message_box == 4 && i >= s[0] && i < (s[0] + way_len) && str == 0 && stl == 0 ) { // gui.add_message_at(our_player, " # test 3/12 " + coord3d_to_string(t), t) gui.add_message_at(our_player, " diagonal start dst " + dst, world.get_time()) gui.add_message_at(our_player, " dc " + dc + " dfcl " + dfcl + " dfcr " + dfcr + " - stl " + stl + " - str " + str, world.get_time()) } if ( dst == 5 || dst == 10 ) { local check_tile_strs = null local check_tile_str = null local check_tile_stls = null local check_tile_stl = null if ( nexttile[i-2].x > nexttile[i-1].x || nexttile[i-2].y > nexttile[i-1].y ) { check_tile_strs = tile_x(t.x - 1, t.y, t.z) check_tile_str = tile_x(t.x - 2, t.y, t.z) check_tile_stls = tile_x(t.x, t.y + 1, t.z) check_tile_stl = tile_x(t.x, t.y + 2, t.z) } else { check_tile_strs = tile_x(t.x, t.y - 1, t.z) check_tile_str = tile_x(t.x, t.y - 2, t.z) check_tile_stls = tile_x(t.x + 1, t.y, t.z) check_tile_stl = tile_x(t.x + 2, t.y, t.z) } local tile_on_map_r = 0 local tile_on_map_l = 0 if ( world.is_coord_valid(check_tile_strs) && world.is_coord_valid(check_tile_str) ) { // tile is in range map tile_on_map_r = 1 } else { // tile is out of range map str = 0 } if ( world.is_coord_valid(check_tile_stls) && world.is_coord_valid(check_tile_stl) ) { // tile is in range map tile_on_map_l = 1 } else { // tile is out of range map stl = 0 } if ( ( dc == 3 && t.get_way_dirs(wt) == 12 ) || ( dc == 12 && t.get_way_dirs(wt) == 3 ) ) { if ( t.get_way_dirs(wt) == 3 && str == 0 ) { if ( print_message_box == 4 ) { //&& i >= s[0] && i < (s[0] + way_len ) gui.add_message_at(our_player, " - check start ribi 3 " + coord3d_to_string(t), t) } if ( tile_on_map_r == 1 ) { if ( check_tile_strs.is_ground() && check_tile_strs.is_empty() ) { str++ } else { str = 0 } } else { //gui.add_message_at(our_player, "(3400) ERROR test tile out of map ", t) } } else if ( t.get_way_dirs(wt) == 12 && stl == 0 ) { if ( print_message_box == 4 && i >= s[0] && i < (s[0] + way_len) ) { // gui.add_message_at(our_player, " - check start ribi 12 " + coord3d_to_string(t), t) } if ( tile_on_map_l == 1 ) { if ( check_tile_stls.is_ground() && check_tile_stls.is_empty() ) { stl++ } else { stl = 0 } } else { //gui.add_message_at(our_player, "(3414) ERROR test tile out of map ", t) } } if ( t.get_way_dirs(wt) == 3 || t.get_way_dirs(wt) == 12 ) { if ( print_message_box == 4 && i >= s[0] && i < (s[0] + way_len) ) { gui.add_message_at(our_player, " - check ribi 3/12 l/r " + coord3d_to_string(t) + " d " + t.get_way_dirs(wt), t) } if ( tile_on_map_r == 1 ) { if ( str > 0 && str <= way_len - 3 ) { if ( check_tile_str.is_ground() && check_tile_str.is_empty() ) { //gui.add_message_at(player_x(1), " + str " + str, world.get_time()) str++ } else { str = 0 } } else if ( str > way_len - 3 && str <= way_len ) { //gui.add_message_at(player_x(1), " ++ str " + str, world.get_time()) str++ } } else { //gui.add_message_at(our_player, "(3436) ERROR test tile out of map ", t) } if ( tile_on_map_l == 1 ) { if ( stl > 0 && stl <= way_len - 3 ) { if ( check_tile_stl.is_ground() && check_tile_stl.is_empty() ) { //gui.add_message_at(player_x(1), " + stl " + stl, world.get_time()) stl++ } else { stl = 0 } } else if ( stl > way_len - 3 && stl <= way_len ) { //gui.add_message_at(player_x(1), " ++ stl " + stl, world.get_time()) stl++ } } else { //gui.add_message_at(our_player, "(3453) ERROR test tile out of map ", t) } } if ( print_message_box == 4 && i >= s[0] && i < (s[0] + way_len + 1) ) { gui.add_message_at(our_player, " -- stl " + stl + " - str " + str + " way_len " + way_len, world.get_time()) } } } // end straight way fc = 0 } if ( i >= s[r] && print_message_box == 2 ) { gui.add_message_at(our_player, "* st " + st + " dst " + dst + " stl " + stl + " str " + str + " fc " + fc + " * " + coord3d_to_string(t), t) } if ( t.get_slope() > 0 && nexttile[i-1].get_slope() > 0 ) { if ( i >= s[r] && print_message_box == 2 ) { gui.add_message_at(our_player, " last 2 tiles have slope - reset " + coord3d_to_string(t), t) ::debug.pause() } stl = 0 str = 0 fc = 0 } if ( dst == 0 && dc == d && i >= s[r] && st == 0 ) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, " stl " + stl + " str " + str + " fc " + fc + " * " + coord3d_to_string(t), t) } if ( stl == fc + 1 || str == fc + 1 ) { fc++ dfcr = 0 dfcl = 0 } else { fc = 0 stl = 0 str = 0 } } else if ( dst > 0 && i >= s[r] && st == 0 ) { if ( print_message_box == 3 ) { gui.add_message_at(player_x(1), " stl " + stl + " str " + str + " fc " + fc + " * " + coord3d_to_string(t), t) gui.add_message_at(player_x(1), " dfcl " + dfcl + " dfcr " + dfcr, world.get_time()) } if ( stl == dfcl + 2 ) { dfcl++ fc = 0 } else { dfcl = 0 stl = 0 } if ( str == dfcr + 2 ) { dfcr++ fc = 0 } else { dfcr = 0 str = 0 } } else { fc = 0 stl = 0 str = 0 } if ( print_message_box == 4 ) { gui.add_message_at(player_x(0), " fc " + fc + " s[" + r + "] " + s[r] + " i " + i + " - " + l + " dc " + dc + " di " + di + " d " + d + " * " + coord3d_to_string(t), world.get_time()) } if ( i == s[r] ) { message_text.append(" s[" + r + "] " + s[r] + " i " + i + " is tile " + coord3d_to_string(t)) } if ( i >= s[r] && ( fc >= way_len || dfcl >= way_len || dfcr >= way_len ) ) { // && start_fields.len() < c if ( ( nexttile[i-1].x > nexttile[i].x && fc > 0 && nexttile[i-1].y == nexttile[i].y ) || ( nexttile[i-1].y > nexttile[i].y && fc > 0 ) || ( nexttile[i-1].y > nexttile[i].y && fc == 0 ) || ( nexttile[i-1].x < nexttile[i].x && fc == 0 && nexttile[i-2].y > nexttile[i].y ) ) { /* * * */ if ( nexttile[i].get_slope() == 0 ) { if ( print_message == 1 ) { gui.add_message_at(our_player, " (1) add nexttile[i] id = " + i + " " + coord3d_to_string(t), t) } //start_fields.append(nexttile[i]) start_fields_id.append(i) stl = 0 str = 0 fc = 0 } else if ( nexttile[i+1].get_slope() == 0 ) { // plan start tile has slope then next tile if ( print_message == 1 ) { gui.add_message_at(our_player, " (2) add nexttile[i+1] id = " + (i+1) + " " + coord3d_to_string(nexttile[i+1]), nexttile[i+1]) } //start_fields.append(nexttile[i+1]) start_fields_id.append(i-1) stl = 0 str = 0 fc = 0 } } else if ( nexttile[i-1].x > nexttile[i].x && fc == 0 && nexttile[i-2].y > nexttile[i].y ) { /* * * */ if ( nexttile[i].get_slope() == 0 ) { if ( print_message == 1 ) { gui.add_message_at(our_player, " (3) add nexttile[i] id = " + i + " " + coord3d_to_string(t), t) } //start_fields.append(nexttile[i]) start_fields_id.append(i) stl = 0 str = 0 fc = 0 } } else if ( nexttile[i-1].x < nexttile[i].x && fc > 0 && nexttile[i-2].y == nexttile[i].y ) { if ( nexttile[i-way_len].get_slope() == 0 && !nexttile[i-way_len].is_bridge() && nexttile[i-way_len].find_object(mo_crossing) == null ) { //start_fields.append(nexttile[i-way_len]) start_fields_id.append(i-way_len) if ( print_message == 1 ) { gui.add_message_at(our_player, " (4) add nexttile[i-way_len] id = " + (i-way_len) + " " + coord3d_to_string(t), t) } } else { //start_fields.append(nexttile[i-way_len+1]) start_fields_id.append(i-way_len+1) if ( print_message == 1 ) { gui.add_message_at(our_player, " (5) add nexttile[i-way_len+1] id = " + (i-way_len) + " " + coord3d_to_string(t), t) } } stl = 0 str = 0 } else { if ( ( nexttile[i-way_len].get_way_dirs(wt) == 3 || nexttile[i-way_len].get_way_dirs(wt) == 12 ) && nexttile[i-way_len].get_slope() == 0 && fc == 0 ) { //start_fields.append(nexttile[i-way_len]) start_fields_id.append(i-way_len) stl = 0 str = 0 if ( print_message == 1 ) { gui.add_message_at(our_player, " (6) add nexttile[i-way_len] id = " + (i-way_len) + " " + coord3d_to_string(t), t) } } else if ( nexttile[i-way_len-1].get_slope() == 0 ) { //start_fields.append(nexttile[i-way_len+1]) start_fields_id.append(i-way_len+1) stl = 0 str = 0 fc = 0 if ( print_message == 1 ) { gui.add_message_at(our_player, " (7) add nexttile[i-way_len+1] id = " + (i-way_len+1) + " " + coord3d_to_string(t), t) } } } if ( print_message_box == 2 ) { gui.add_message_at(our_player, " tile s[" + r + "] " + coord3d_to_string(start_fields[r]), start_fields[r]) gui.add_message_at(our_player, " i " + i + " r " + r + " s[" + r + "] " + s[r], world.get_time()) gui.add_message_at(our_player, "* " + coord3d_to_string(t), world.get_time()) } local dw_abs = 10 if ( station_len > 8 ) { dw_abs = 1 } if ( r < as ) { if ( r < s.len() - 1 ) { if ( i > s[r] ) { s[r] = i + dw_abs } else { s[r] = i } } else { if ( i > s[r] ) { s.append(i + dw_abs) } else { s.append(i) } } r++ } } //gui.add_message_at(our_player, " tile d " + d + " * " + coord3d_to_string(t), world.get_time()) } if ( print_message == 1 && print_message_box > 0 ) { for ( local j = 0; j < message_text.len(); j++ ) { gui.add_message_at(our_player, message_text[j], world.get_time()) } } if ( sign == c - 1 && c > 2 ) { //gui.add_message_at(our_player, (c-1) + " double way found, no build " + c, world.get_time()) return true } if ( start_fields_id.len() <= c ) { for ( local i = 0; i < start_fields_id.len(); i++ ) { start_fields.append(nexttile[start_fields_id[i]]) } } else { // start_fields selected if ( c == 2 ) { if ( start_fields_id.len() >= 5 ) { start_fields.append(nexttile[start_fields_id[1]]) start_fields.append(nexttile[start_fields_id[start_fields_id.len()-2]]) } else { start_fields.append(nexttile[start_fields_id[0]]) start_fields.append(nexttile[start_fields_id[start_fields_id.len()-1]]) } } else { local a = (l / (c + 1)).tointeger() //gui.add_message_at(our_player, " a " + a, world.get_time()) local l1 = 0 local l2 = 0 for ( local i = 0; i < c; i++ ) { //gui.add_message_at(our_player, " (a * (i+1)) " + (a * (i+1)), world.get_time()) for ( local j = 1; j < start_fields_id.len(); j++ ) { if ( start_fields_id[j-1] < (a * (i+1)) ) { //l1 = (a * c) - start_fields_id[j-1] if ( start_fields_id.len() < i && start_fields_id[j] < (a * (i+1)) && start_fields_id[j+1] > (a * (i+1)) ) { //l1 = j if ( ((a * (i+1)) - start_fields_id[j+1]) < (start_fields_id[j] - (a * (i+1))) ) { l1 = j+1 } else { l1 = j } } else { if ( ((a * (i+1)) - start_fields_id[j-1]) < (start_fields_id[j] - (a * (i+1))) ) { l1 = j-1 } else { l1 = j //break } // } }/* else { l1 = j-1 break }*/ if ( l1 > 0 && start_fields_id.len() > j && (l1 - l2) < (a-10) ) { } else if ( l1 > 0 ) { break } } //if ( l1 > l2 ) { //} else { start_fields.append(nexttile[start_fields_id[l1]]) //gui.add_message_at(our_player, " start_fields.append() " + start_fields_id[l1], nexttile[start_fields_id[l1]]) l2 = l1 l1 = 0 //} } } //gui.add_message_at(our_player, " start_fields.len() " + start_fields.len(), world.get_time()) } return start_fields } /** * check the route and optimized this * * @param route tiles array from route * @param wt waytype from route * @param int_run count the run this function for selcted line * @param o_line line_x object * */ function optimize_way_line(route, wt, int_run, o_line) { // 0 = off // 1 = bridges // 2 = tunnel // 3 = crossing // 4 = terraform local print_message_box = 0 if ( print_message_box > 5 ) { //wt == wt_road && wt == wt_rail && gui.add_message_at(our_player, " optimize_way_line(route, wt) ", tile_x(route[0].x, route[0].y, route[0].z)) } //::debug.pause() local speed = tile_x(route[0].x, route[0].y, route[0].z).find_object(mo_way).get_desc().get_topspeed() local bridge_obj = find_object("bridge", wt, speed) local tunnel_obj = find_object("tunnel", wt, speed) local count_build = 0 local stations_awst = null local stations_awst_2 = [] // count station len start local station_len = 0 for ( local c = 0; c < 50; c++ ) { if ( route[c].find_object(mo_building) != null ) { station_len++ } else { break } } if ( station_len == 1 ) { station_len = 3 } //gui.add_message_at(our_player, " found bridge " + bridge_obj.get_name() + " tunnel " + tunnel_obj.get_name(), world.get_time()) for ( local i = 1; i < (route.len() - station_len); i++ ) { local tile_1 = tile_x(route[i-1].x, route[i-1].y, route[i-1].z) local tile_2 = tile_x(route[i].x, route[i].y, route[i].z) local tile_3 = tile_x(route[i+1].x, route[i+1].y, route[i+1].z) local tile_4 = null local tile_1_d = tile_1.get_way_dirs(wt) local tile_2_d = tile_2.get_way_dirs(wt) local tile_3_d = tile_3.get_way_dirs(wt) local tile_4_d = 0 local tile_2_speed = 0 // START :: remove diagonal double ways without spacing if ( dir.is_threeway(tile_1_d) && dir.is_threeway(tile_3_d) ) { tile_2_speed = tile_2.find_object(mo_way).get_desc().get_topspeed() local remove_tile = null switch(tile_2_d) { case 3: tile_4 = tile_x(tile_2.x+1, tile_2.y-1, tile_2.z) if ( tile_4.find_object(mo_way) != null ) { tile_4_d = tile_4.get_way_dirs(wt) if ( tile_2_speed >= tile_4.find_object(mo_way).get_desc().get_topspeed() && tile_4_d == 12 ) { remove_tile = tile_4 } else if ( tile_4_d == 12 ) { remove_tile = tile_2 } } break case 6: tile_4 = tile_x(tile_2.x+1, tile_2.y+1, tile_2.z) if ( tile_4.find_object(mo_way) != null ) { tile_4_d = tile_4.get_way_dirs(wt) if ( tile_2_speed >= tile_4.find_object(mo_way).get_desc().get_topspeed() && tile_4_d == 9 ) { remove_tile = tile_4 } else if ( tile_4_d == 9 ) { remove_tile = tile_2 } } break case 9: tile_4 = tile_x(tile_2.x-1, tile_2.y-1, tile_2.z) if ( tile_4.find_object(mo_way) != null ) { tile_4_d = tile_4.get_way_dirs(wt) if ( tile_2_speed >= tile_4.find_object(mo_way).get_desc().get_topspeed() && tile_4_d == 6 ) { remove_tile = tile_4 } else if ( tile_4_d == 6 ) { remove_tile = tile_2 } } break case 12: tile_4 = tile_x(tile_2.x-1, tile_2.y+1, tile_2.z) if ( tile_4.find_object(mo_way) != null ) { tile_4_d = tile_4.get_way_dirs(wt) if ( tile_2_speed >= tile_4.find_object(mo_way).get_desc().get_topspeed() && tile_4_d == 3 ) { remove_tile = tile_4 } else if ( tile_4_d == 3 ) { remove_tile = tile_2 } } break } if ( remove_tile != null ) { remove_tile_to_empty(remove_tile, wt, 0) } } // END :: remove diagonal double ways without spacing // START :: check station in line if ( tile_2.find_object(mo_building) != null && i > station_len && i < (route.len() - station_len) && wt == wt_rail && stations_awst == null ) { // station in line // set line.way_line_count o_line.way_line_count = 2 local li = tile_2.get_halt().get_line_list() // todo line_x -> my_line_t local s_line = select_linkline(li[0]) s_line.way_line_count = 2 /* local entries = r_line.get_schedule().entries if ( entries.len() == 2 ) { local sched = schedule_x(wt_rail, []) sched.entries.append( schedule_entry_x(entries[0], 100, 0) ); sched.entries.append( schedule_entry_x(t, 0, 0) ); sched.entries.append( schedule_entry_x(entries[entries.len()-1], 0, 0) ); r_line.change_schedule(our_player, sched); }*/ local t = null local r = null for (local j = 1; j < station_len; j++ ) { t = route[i+j] //gui.add_message_at(our_player, "st_awst ", t) if (t.find_object(mo_building) == null) { r = j break } } if ( (t.x == tile_2.x && t.y < tile_2.y) || (t.y == tile_2.y && t.x < tile_2.x) ) { stations_awst = t r = i + 9 } else if ( (t.x == tile_2.x && t.y > tile_2.y) || (t.y == tile_2.y && t.x > tile_2.x) ) { stations_awst = tile_1 r = i - 9 } for (local j = 1; j < 9; j++ ) { if ( r < 0 ) { break } stations_awst_2.append(route[r+j]) } } // END :: check station in line // START :: build bridges and tunnel - 2 to 4 tiles local build_bridge = 0 local build_tunnel = 0 local build_tile = null tile_4 = tile_x(route[i+2].x, route[i+2].y, route[i+2].z) tile_4_d = tile_4.get_way_dirs(wt) // tile 1 - 3 direction 5 or 10 -> way_d = 1 // tile 1 - 4 direction 5 or 10 -> way_d = 2 local way_d = 0 if ( tile_1_d == 5 || tile_1_d == 10 ) { if ( tile_1_d == tile_2_d && tile_2_d == tile_3_d && tile_3_d == tile_4_d && tile_1.z == tile_4.z && tile_1.get_slope() > 0 && tile_4.get_slope() > 0 ) { if ( our_player.get_current_cash() > 1000000 ) { if ( tile_1.z == tile_2.z && tile_2.z == tile_3.z && tile_1.is_bridge() != true ) { build_bridge = 2 build_tile = tile_4 } else if ( tile_1.z < tile_2.z && tile_2.z == tile_3.z ) { build_tunnel = 3 build_tile = tile_4 } } } else if ( tile_1_d == tile_2_d && tile_2_d == tile_3_d && tile_1.z == tile_3.z && tile_1.get_slope() > 0 && tile_3.get_slope() > 0 ) { if ( our_player.get_current_cash() > 500000 ) { if ( tile_1.z == tile_2.z && tile_2.z == tile_3.z && tile_1.is_bridge() != true ) { build_bridge = 2 build_tile = tile_3 } else if ( tile_1.z < tile_2.z ) { build_tunnel = 3 build_tile = tile_3 } } } else if ( tile_1_d == tile_2_d && tile_1.z == tile_2.z && tile_1.get_slope() > 0 && tile_2.get_slope() > 0 && tile_1.is_bridge() != true && tile_2.is_bridge() != true ) { // slope down - slope up -> bridge // slope up - slope down -> terraform down ( build_tunnel = 1 ) if ( (tile_1.get_slope() == 4 && tile_2.get_slope() == 36 && tile_1.y < tile_2.y) || (tile_1.get_slope() == 36 && tile_2.get_slope() == 4 && tile_1.y > tile_2.y) ) { build_tunnel = 1 } else if ( (tile_1.get_slope() == 12 && tile_2.get_slope() == 28 && tile_1.x < tile_2.x) || (tile_1.get_slope() == 28 && tile_2.get_slope() == 12 && tile_1.x > tile_2.x) ) { build_tunnel = 1 } else if ( (tile_1.get_slope() == 4 && tile_2.get_slope() == 36 && tile_1.y > tile_2.y) || (tile_1.get_slope() == 36 && tile_2.get_slope() == 4 && tile_1.y < tile_2.y) ) { build_bridge = 1 } else if ( (tile_1.get_slope() == 12 && tile_2.get_slope() == 28 && tile_1.x > tile_2.x) || (tile_1.get_slope() == 28 && tile_2.get_slope() == 12 && tile_1.x < tile_2.x) ) { build_bridge = 1 } build_tile = tile_2 } // check is way our player local tile_1_pl = tile_1.find_object(mo_way).get_owner().nr //.get_desc() local tile_2_coord = coord3d_to_string(tile_2) // not use for debug local tile_2_pl = tile_2.find_object(mo_way).get_owner().nr //.get_desc() if ( tile_1_pl != our_player_nr || tile_2_pl != our_player_nr ) { continue } if ( (way_d > 0 || build_bridge > 0) && print_message_box > 0 ) { // //gui.add_message_at(our_player, " optimize way way_d " + way_d, tile_1) gui.add_message_at(our_player, " optimize way build_bridge " + build_bridge, tile_1) } } if ( build_bridge > 0 && print_message_box == 1 ) { gui.add_message_at(our_player, " optimize way build_bridge " + build_bridge, tile_1) //::debug.pause() } if ( build_tunnel > 0 && print_message_box == 2 ) { gui.add_message_at(our_player, " optimize way build_tunnel " + build_tunnel, tile_1) //::debug.pause() } // way local way_obj = route[0].find_object(mo_way).get_desc() //way_list[0] if ( !way_obj.is_available(world.get_time()) ) { way_obj = find_object("way", wt, way_obj.get_topspeed()) } // catenary local catenary_obj = null if ( tile_1.find_object(mo_wayobj) != null ) { catenary_obj = tile_1.find_object(mo_wayobj).get_desc() if ( catenary_obj != null && !catenary_obj.is_available(world.get_time()) ) { catenary_obj = find_object("catenary", wt, catenary_obj.get_topspeed()) } } // slope up - slope down -> tunnel if ( build_tunnel == 1 ) { local step_ok = true local err = null // not build tunnel -> set slope down local tile_4 = tile_x(route[i-2].x, route[i-2].y, route[i-2].z) tile_4_d = tile_4.get_way_dirs(wt) /*if ( tile_4.find_object(mo_building) != null || tile_4.find_object(mo_bridge) != null ) { //dir.is_single(tile_4_d) local tool = command_x(tool_remover) err = tool.work(our_player, tile_3) if ( err == null ) { err = tool.work(our_player, tile_2) } } else { local tool = command_x(tool_remove_way) local err = tool.work(our_player, tile_3, tile_4, "" + wt) }*/ if ( tile_3.find_object(mo_bridge) == null && tile_4.find_object(mo_bridge) == null && tile_4.find_object(mo_building) == null && tile_3.find_object(mo_building) == null ) { local tool = command_x(tool_remove_way) err = tool.work(our_player, tile_4, tile_3, "" + wt) } else { remove_tile_to_empty(tile_1, wt, 0) remove_tile_to_empty(tile_2, wt, 0) } local way_obj = tile_4.find_object(mo_way).get_desc() if ( !way_obj.is_available(world.get_time()) ) { way_obj = find_object("way", wt, speed) } if ( err != null ) { // tile not remove -> restore way err = command_x.build_way(our_player, tile_4, tile_3, way_obj, true) step_ok = false } if ( step_ok ) { err = null // terraform down /*local t = [] t.append(tile_1) t.append(tile_2)*/ if ( check_tiles_for_terraform([tile_1, tile_2]) ) { // tiles left and right free -> terraform grid local cx = 0 local cy = 0 local terraform_tile = null if ( tile_1.y == tile_2.y ) { cy = 1 if ( tile_1.x > tile_2.x ) { terraform_tile = tile_1 } else { terraform_tile = tile_2 } } else if ( tile_1.x == tile_2.x ) { cx = 1 if ( tile_1.y > tile_2.y ) { terraform_tile = tile_1 } else { terraform_tile = tile_2 } } err = command_x.grid_lower(our_player, coord3d(terraform_tile.x, terraform_tile.y, terraform_tile.z)) if ( print_message_box == 4 ) { gui.add_message_at(our_player, " terraform terraform_tile " + coord3d_to_string(terraform_tile) + " : " + err, world.get_time()) } err = null err = command_x.grid_lower(our_player, coord3d(terraform_tile.x+cx, terraform_tile.y+cy, terraform_tile.z)) if ( print_message_box == 4 ) { gui.add_message_at(our_player, " terraform terraform_tile " + coord3d_to_string(tile_x(terraform_tile.x+cx, terraform_tile.y+cy, terraform_tile.z)) + " : " + err, world.get_time()) } } else { // tiles left and right not free -> terraform tile err = command_x.set_slope(our_player, tile_1, 83) err = command_x.set_slope(our_player, tile_2, 83) } // check ground is water after terraform if ( tile_1.is_water() ) { command_x.change_climate_at(our_player, tile_1, cl_temperate) remove_tile_to_empty(tile_2, wt, 0) } if ( tile_2.is_water() ) { command_x.change_climate_at(our_player, tile_2, cl_temperate) remove_tile_to_empty(tile_1, wt, 0) command_x.change_climate_at(our_player, tile_1, cl_temperate) } err = null err = command_x.build_way(our_player, tile_4, tile_3, way_obj, true) if (err != null ) { gui.add_message_at(our_player, " build tunnel " + coord3d_to_string(tile_4) + " - " + coord3d_to_string(tile_3) + ": " + err, world.get_time()) } else { count_build++ } } // restor exist catenary if ( catenary_obj != null ) { command_x.build_wayobj(our_player, tile_4, tile_3, catenary_obj) } } else if ( build_tunnel == 2 ) { err = null // build tunnel - not work tunnel tool script ai //local tile_4 = tile_x(route[i-2].x, route[i-2].y, route[i-2].z) local txt = coord3d_to_string(tile_1) local tool = command_x(tool_remove_way) local err = tool.work(our_player, tile_1, build_tile, "" + wt) err = command_x.build_tunnel_at(our_player, tile_1, tunnel_obj) if (err != null ) { gui.add_message_at(our_player, " build tunnel: " + err, world.get_time()) } else { count_build++ } // restor exist catenary if ( catenary_obj != null ) { command_x.build_wayobj(our_player, tile_1, build_tile, catenary_obj) } } else if ( build_tunnel == 3 ) { // build tunnel - not work tunnel tool script ai /*local tile_4 = tile_x(route[i-2].x, route[i-2].y, route[i-2].z) local txt = coord3d_to_string(tile_1) remove_tile_to_empty(tile_2, wt, 0) local err = command_x.build_tunnel_at(our_player, tile_1, tunnel_obj) if (err != null ) { gui.add_message_at(our_player, " build tunnel: " + err, world.get_time()) } else { count_build++ }*/ } // slope down - slope up -> bridge if ( build_bridge == 1 ) { local err = null local tile_4 = tile_x(route[i-2].x, route[i-2].y, route[i-2].z) //local err = remove_tile_to_empty(tile_2, wt, 0) if ( tile_3.find_object(mo_bridge) == null && tile_4.find_object(mo_bridge) == null && tile_4.find_object(mo_building) == null && tile_3.find_object(mo_building) == null ) { local tool = command_x(tool_remove_way) err = tool.work(our_player, tile_4, tile_3, "" + wt) } else { err = remove_tile_to_empty(tile_1, wt, 0) if ( err ) { err = remove_tile_to_empty(tile_2, wt, 0) } if ( err ) { err = null } else { continue } } if ( print_message_box == 4 && err != null ) { gui.add_message_at(our_player, "#4376# remove tile_1 " + coord3d_to_string(tile_1) + " : " + err, world.get_time()) gui.add_message_at(our_player, "#4376# remove tile_2 " + coord3d_to_string(tile_2) + " : " + err, world.get_time()) //::debug.pause() } if (err == null) { //err = null /*local t = [] t.append(tile_1) t.append(tile_2)*/ if ( check_tiles_for_terraform([tile_1, tile_2]) ) { // tiles left and right free -> terraform grid local cx = 0 local cy = 0 local terraform_tile = null if ( tile_1.y == tile_2.y ) { cy = 1 if ( tile_1.x > tile_2.x ) { terraform_tile = tile_1 } else { terraform_tile = tile_2 } } else if ( tile_1.x == tile_2.x ) { cx = 1 if ( tile_1.y > tile_2.y ) { terraform_tile = tile_1 } else { terraform_tile = tile_2 } } err = command_x.grid_raise(our_player, coord3d(terraform_tile.x, terraform_tile.y, terraform_tile.z)) //::debug.pause() if (err != null ) { if ( print_message_box == 4 ) { gui.add_message_at(our_player, "#4390# terraform fail -> build bridge : " + err, tile_1) ::debug.pause() } err = command_x.build_bridge(our_player, tile_1, build_tile, bridge_obj) } else { //gui.add_message_at(our_player, "#4344# terraform_tile : " + coord3d_to_string(terraform_tile), terraform_tile) err = command_x.grid_raise(our_player, coord3d(terraform_tile.x+cx, terraform_tile.y+cy, terraform_tile.z)) if ( err == null ) { err = command_x.build_way(our_player, tile_4, tile_3, way_obj, true) if (err != null ) { gui.add_message_at(our_player, "#4350# build way " + coord3d_to_string(tile_4) + " - " + coord3d_to_string(tile_3) + ": " + err, world.get_time()) } else { count_build++ } } else { gui.add_message_at(our_player, "#4403# err : " + err, tile_1) } } } else { // tiles left and right not free -> build bridge err = command_x.build_bridge(our_player, tile_1, build_tile, bridge_obj) } } // restor exist catenary if ( catenary_obj != null ) { command_x.build_wayobj(our_player, tile_1, build_tile, catenary_obj) } } else if ( build_bridge == 2 ) { local tool = command_x(tool_remove_way) local err = tool.work(our_player, tile_1, build_tile, "" + wt) //gui.add_message_at(our_player, " remove way: " + err, tile_1) if (err == null) { err = command_x.build_bridge(our_player, tile_1, build_tile, bridge_obj) if (err != null ) { gui.add_message_at(our_player, " build bridge: " + err, tile_1) // restore way err = command_x.build_way(our_player, tile_1, build_tile, way_obj, true) } else { count_build++ } } // restor exist catenary if ( catenary_obj != null ) { command_x.build_wayobj(our_player, tile_1, build_tile, catenary_obj) } } // END :: build bridges and tunnel - 2 to 4 tiles // START :: remove obsolete bridges if ( tile_2.is_bridge() && tile_1.z == tile_2.z && tile_2.get_slope() == 0 ) { local tile_way = tile_2.find_object(mo_bridge) local pl_check = tile_way.get_owner().nr local remove_bridge = 0 local bridge_len = 0 if ( tile_2_d == 5 && pl_check == our_player_nr ) { local t_tile = tile_2 if ( tile_2.y > tile_1.y ) { local check_gr = tile_x(t_tile.x, t_tile.y+1, t_tile.z) for ( local x = 1; route[i].is_bridge(); x++ ) { if ( check_gr.is_ground() && check_gr.is_empty() ) { remove_bridge++ } bridge_len = x i++ check_gr = square_x(route[i].x, route[i].y).get_ground_tile() } } else { local check_gr = tile_x(t_tile.x, t_tile.y-1, t_tile.z) for ( local x = 1; route[i].is_bridge(); x++ ) { if ( check_gr.is_ground() && check_gr.is_empty() ) { remove_bridge++ } bridge_len = x i++ check_gr = square_x(route[i].x, route[i].y).get_ground_tile() } } } else if ( tile_2_d == 10 && pl_check == our_player_nr ) { local t_tile = tile_2 if ( tile_2.x > tile_1.x ) { local check_gr = tile_x(t_tile.x+1, t_tile.y, t_tile.z) for ( local x = 1; route[i].is_bridge(); x++ ) { if ( check_gr.is_ground() && check_gr.is_empty() ) { remove_bridge++ } bridge_len = x i++ check_gr = square_x(route[i].x, route[i].y).get_ground_tile() } } else { local check_gr = tile_x(t_tile.x-1, t_tile.y, t_tile.z) for ( local x = 1; route[i].is_bridge(); x++ ) { if ( check_gr.is_ground() && check_gr.is_empty() ) { remove_bridge++ } bridge_len = x i++ check_gr = square_x(route[i].x, route[i].y).get_ground_tile() } } } if ( bridge_len == 4 && print_message_box == 1 ) { gui.add_message_at(our_player, " test bridge : bridge_len = " + bridge_len + " : remove_bridge = " + remove_bridge, tile_1) gui.add_message_at(our_player, " tile_4 " + coord3d_to_string(tile_4), world.get_time()) gui.add_message_at(our_player, " build_tile " + coord3d_to_string(tile_x(route[i-1].x, route[i-1].y, route[i-1].z)), world.get_time()) ::debug.pause() //sleep() } if ( remove_bridge > 0 && ( (remove_bridge+1) == bridge_len ) && bridge_len > 2 && bridge_len < 5 && pl_check == our_player_nr ) { build_tile = tile_x(route[i-1].x, route[i-1].y, route[i-1].z) if ( tile_2.z == build_tile.z ) { local err = remove_tile_to_empty(tile_2, wt, 0) if (err != null) { gui.add_message_at(our_player, " remove bridge: " + err, tile_1) } if (err) { err = check_ground(tile_2, build_tile, way_obj) build_tile = tile_x(route[i].x, route[i].y, route[i].z) err = command_x.build_way(our_player, tile_1, build_tile, way_obj, true) if (err != null ) { gui.add_message_at(our_player, " not build way: " + err, tile_1) // restore bridge err = command_x.build_bridge(our_player, tile_2, build_tile, bridge_obj) } else { count_build++ } } } else { local err = check_ground(tile_2, build_tile, way_obj) if (!err) { err = command_x.build_way(our_player, tile_1, tile_2, way_obj, true) } else { err = command_x.build_bridge(our_player, tile_2, build_tile, bridge_obj) } } } } // END :: remove obsolete bridges // START :: crossing // replace crossing to road bridge local check_crossing = tile_2.find_object(mo_crossing) if ( check_crossing != null ) { local tile_way = [tile_1.find_object(mo_way), tile_3.find_object(mo_way)] local pl_check = [tile_way[0].get_owner().nr, tile_way[1].get_owner().nr] local build_check = 0 if ( (pl_check[0] == our_player_nr || pl_check[0] == 1) && (pl_check[1] == our_player_nr || pl_check[1] == 1) ) { // no bridge by bridge and tunnel if ( !tile_1.is_bridge() && !tile_3.is_bridge() && !tile_1.is_tunnel() && !tile_3.is_tunnel() ) { build_check = 1 } } if ( wt == wt_road && build_check == 1 && world.get_time().year >= 1935 ) { if ( (tile_1_d == 5 && tile_3_d == 5) || (tile_1_d == 10 && tile_3_d == 10) ) { if ( print_message_box == 3 ) { gui.add_message_at(our_player, " test crossing = " + check_crossing, tile_2) gui.add_message_at(our_player, " find way = " + tile_2.find_object(mo_way), tile_2) gui.add_message_at(our_player, " way name = " + tile_2.find_object(mo_way).get_name() + " cnv count : " + tile_2.find_object(mo_way).get_convoys_passed()[1], tile_2) //gui.add_message_at(our_player, " crossing way = " + check_crossing., tile_2) //::debug.pause() } local cnv_count = tile_2.find_object(mo_way).get_convoys_passed()[1] if ( cnv_count > 100 ) { local tool = command_x(tool_remove_way) local err = tool.work(our_player, tile_1, tile_3, "" + wt) //gui.add_message_at(our_player, " remove way: " + err, tile_1) if (err == null) { err = command_x.build_bridge(our_player, tile_1, tile_3, bridge_obj) if (err != null ) { gui.add_message_at(our_player, " build bridge: " + err, tile_1) } else { count_build++ } } } } } } // END :: crossing } // build stations awst if ( stations_awst != null ) { station_aw(stations_awst, wt, stations_awst_2) } if (count_build > 0 ) { local cs = tile_x(route[route.len()-1].x, route[route.len()-1].y, route[route.len()-1].z)//route[route.len()] local ce = tile_x(route[0].x, route[0].y, route[0].z)//route[0] local cs_coord = coord_to_string(cs) local ce_coord = coord_to_string(ce) local msgtext = format(translate("%s optimize way line from %s (%s) to %s (%s)"), our_player.get_name(), cs.get_halt().get_name(), coord_to_string(cs), ce.get_halt().get_name(), coord_to_string(ce)) gui.add_message_at(our_player, msgtext, cs) return true } else { return false } } /** * check tiles free for terraform * * @param tiles array tiles for check tiles left and right is free */ function check_tiles_for_terraform(tiles) { local tiles_free = true local cx = 0 local cy = 0 if ( tiles[0].x == tiles[1].x ) { cx = 1 } else if ( tiles[0].y == tiles[1].y ) { cy = 1 } for ( local x = 0; x < tiles.len(); x++ ) { //gui.add_message_at(our_player, "test_tile_is_empty tiles[x]+ " + test_tile_is_empty(tile_x(tiles[x].x+cx, tiles[x].y+cy, tiles[x].z)), tiles[x]) //gui.add_message_at(our_player, "test_tile_is_empty tiles[x]- " + test_tile_is_empty(tile_x(tiles[x].x-cx, tiles[x].y-cy, tiles[x].z)), world.get_time()) if ( !test_tile_is_empty(tile_x(tiles[x].x+cx, tiles[x].y+cy, tiles[x].z)) || !test_tile_is_empty(tile_x(tiles[x].x-cx, tiles[x].y-cy, tiles[x].z)) ) { tiles_free = false } } return tiles_free } /** * check double ways in new line * waytype: wt_rail * * @param route tiles array from route * @param wt waytype from route */ function check_doubleway_in_line(route, wt) { //gui.add_message_at(our_player, " check_doubleway_in_line(route, wt) ", world.get_time()) local treeway_tiles = [] local nexttile = [] local t = 0 // count treeway tile to signal tile foreach(node in route.routes) { local tile = tile_x(node.x, node.y, node.z) nexttile.append(tile) // check route to treeways if ( t > 0 ) { t++ } // treeway if ( dir.is_threeway(tile.get_way_dirs(wt)) ) { treeway_tiles.append(tile) t++ if ( t <= 6 ) { t = 1 } } // test signals by rail if ( tile.find_object(mo_signal) != null && t < 6 ) { // connect in double way gui.add_message_at(our_player, " ## WARNING ## connect in double way ", tile) } else if ( t >= 6 ) { t = 0 } } //::debug.pause() //local asf = astar_route_finder(wt) //wayline = asf.search_route([start_l], [end_l]) } /* * destroy line complete * - destroy convoy * - remove line * check shedule/station connections befor * - destroy stations * - destroy depot by rail/ship * - destroy way line */ function destroy_line(line_obj, good, link_obj) { if ( debug ) ::debug.set_pause_on_error(true) // 1 = messages // 2 = debug.pause() // 3 = line check local print_message_box = 0 if ( print_message_box > 0 ) { gui.add_message_at(our_player, "+ destroy_line(line_obj, good, link) start line " + line_obj.get_name(), world.get_time()) } local line_name = line_obj.get_name() local cnv_list = line_obj.get_convoy_list() local cnv_count = cnv_list.get_count() local wt = line_obj.get_waytype() local cnv = null local depot = null local depot_t = null local start_l = null local end_l = null local start_h = null local end_h = null local nexttile = [] { local entries = line_obj.get_schedule().entries local entries_count = entries.len() if ( wt == wt_water ) { if ( entries.len() >= 2 ) { start_h = entries[0].get_halt(our_player) end_h = entries[entries.len()-1].get_halt(our_player) local t = start_h.get_tile_list() start_l = tile_x(t[0].x, t[0].y, t[0].z) t = end_h.get_tile_list() end_l = tile_x(t[0].x, t[0].y, t[0].z) } } else { if ( entries.len() >= 2 ) { start_l = tile_x(entries[0].x, entries[0].y, entries[0].z) end_l = tile_x(entries[entries.len()-1].x, entries[entries.len()-1].y, entries[entries.len()-1].z) start_h = start_l.get_halt() end_h = end_l.get_halt() } } } if ( print_message_box == 1 ) { gui.add_message_at(our_player, " " + line_obj.get_name(), world.get_time()) } local start_line_count = start_h.get_line_list().get_count() local end_line_count = end_h.get_line_list().get_count() local combined_s = test_halt_waytypes(start_l) local combined_e = test_halt_waytypes(end_l) if ( start_line_count >= 2 && end_line_count >= 2 && combined_s > 1 && combined_e > 1 ) { return false } local start_f = null local end_f = null //if ( wt != wt_water ) { start_f = start_h.get_factory_list() end_f = end_h.get_factory_list() //} if ( start_f.len() > 0 && (print_message_box == 1 || print_message_box == 3) ) { gui.add_message_at(our_player, " factory start " + start_f[0].get_name(), start_l) } else if ( print_message_box == 1 || print_message_box == 3 ) { gui.add_message_at(our_player, " not connect factory start ", world.get_time()) } if ( end_f.len() > 0 && (print_message_box == 1 || print_message_box == 3) ) { gui.add_message_at(our_player, " factory end " + end_f[0].get_name(), end_l) } else if ( print_message_box == 1 || print_message_box == 3 ) { gui.add_message_at(our_player, " not connect factory end ", world.get_time()) } local chk_link = 0 if ( start_f.len() > 0 && end_f.len() > 0 ) { chk_link = check_factory_links(start_f[0], end_f[0], good.get_name()) } //gui.add_message_at(our_player, "### check_factory_links(start_f[0], end_f[0], good.get_name()) " + check_factory_links(start_f[0], end_f[0], good.get_name()), world.get_time()) // check links if ( combined_s == 1 && combined_e == 1 && chk_link == 1 ) { local good_list_in = []; local g_count_in = 0 foreach(good, islot in start_f[0].input) { // test for in-storage or in-transit goods local st = islot.get_storage() local it = islot.get_in_transit() //gui.add_message_at(our_player, "### " + fab.get_name() + " ## " + good + " ## get_storage() " + st[0] + " get_in_transit() " + it[0], world.get_time()) if (st[0] + st[1] + it[0] + it[1] > 0) { // something stored/in-transit in last and current month // no need to search for more supply good_list_in.append({ g = good, t = 1 }) g_count_in++ } else { good_list_in.append({ g = good, t = 0 }) } } if (good_list_in.len() == g_count_in ) { if ( print_message_box == 1 || print_message_box == 3 ) { gui.add_message_at(our_player, "### last line connect factorys - stored/in-transit all goods > 0 factory start", world.get_time()) } if ( start_f[0].get_suppliers().len() == 0 && end_f[0].get_consumers().len() == 0 ) { if ( print_message_box == 1 || print_message_box == 3 ) { gui.add_message_at(our_player, "### factory start generator && factory end end-consumers", world.get_time()) } } else { // 12 month no output goods -> line remove local output_count = 0 local list_halts_dest = end_f[0].get_halt_list() // check conected halts if ( list_halts_dest.len() == 1 ) { // one halt remove link if ( print_message_box == 1 || print_message_box == 3 ) { gui.add_message_at(our_player, "### factory end : count halts = " + list_halts_dest.len(), world.get_time()) } //return true } else { /*foreach(good, islot in end_f[0].output) { // test for in-storage or in-transit goods local st = end_f[0].get_desc().get_outputs() foreach(t in st) { gui.add_message_at(our_player, "### foreach(t in st) - t.keys() " + t.keys(), world.get_time()) local tt = t.keys() for (local i = 0; i < tt.len(); i++) { gui.add_message_at(our_player, "**** keys() - tt["+i+"] " + tt[i], world.get_time()) } tt.clear() tt = t.values() for (local i = 0; i < tt.len(); i++) { if ( i == 2 ) { gui.add_message_at(our_player, "**** values() - tt["+i+"].get_name() " + tt[i].get_name(), world.get_time()) } else { gui.add_message_at(our_player, "**** values() - tt["+i+"] " + tt[i], world.get_time()) } } //output_count += t } }*/ if ( print_message_box == 1 || print_message_box == 3 ) { gui.add_message_at(our_player, "### factory end : count outputs good = " + output_count, world.get_time()) } return false } } } } if (cnv_list.get_count() > 0 ) { foreach(cnv in cnv_list) { if ( depot == null ) { depot = tile_x(cnv.get_home_depot().x, cnv.get_home_depot().y, cnv.get_home_depot().z) if ( print_message_box == 1 ) { gui.add_message_at(our_player, "depot " + coord3d_to_string(depot), world.get_time()) } } // mark convoy for destroying local cnv_trav_month = cnv.get_traveled_distance() local cnv_trav_month_count = 0 for (local i = 0; i < cnv_trav_month.len(); i++ ) { cnv_trav_month_count += cnv_trav_month[i] } if ( print_message_box == 3 ) { gui.add_message_at(our_player, "cnv_trav_month_count " + cnv_trav_month_count, world.get_time()) gui.add_message_at(our_player, "cnv.get_distance_traveled_total() " + cnv.get_distance_traveled_total(), world.get_time()) } if ( cnv.get_distance_traveled_total() < 3 && cnv_trav_month_count == cnv.get_distance_traveled_total() && !cnv.is_loading() && (link_obj != null && check_fsrc_input(link_obj.f_src)) ) { if ( print_message_box == 3 ) { gui.add_message_at(our_player, "return cnv/line new", world.get_time()) } return } if ( wt != wt_water && combined_s > 1 && start_line_count > 1 ) { if ( print_message_box == 1 ) { gui.add_message_at(our_player, "return start combined station, more lines ", world.get_time()) } return false } if ( wt == wt_water && combined_e > 1 && end_line_count > 2 ) { if ( print_message_box == 1 ) { gui.add_message_at(our_player, "return end combined station, more lines ", world.get_time()) } return false } cnv.destroy(our_player) //::debug.pause() // Prfen warum next_vehicle_check nicht vorhanden bei Schiffslinien // line_x statt link.line //if ( wt != wt_water ) { line_obj.next_vehicle_check = world.get_time().ticks + (world.get_time().ticks_per_month * 1) //} } // sleep - convoys are destroyed when simulation continues sleep() } // check convoy count cnv_list = line_obj.get_convoy_list() if ( cnv_list.get_count() == 0 ) { //::debug.pause() // destroy line line_obj.destroy(our_player) //industry_manager.access_link(fsrc, fdest, freight).remove_line(c_line) } else { gui.add_message_at(our_player, " --> ERROR not all convoys delete from line ", world.get_time()) return false } sleep() start_line_count = start_h.get_line_list().get_count() end_line_count = end_h.get_line_list().get_count() if ( print_message_box == 1 ) { gui.add_message_at(our_player, " combined station s waytypes = " + combined_s, start_l) gui.add_message_at(our_player, " combined station e waytypes = " + combined_e, end_l) } // check line way local wayline = null local treeways = 0 local double_ways = 0 // way station to next treeway by road local treeway_tile_s = [null, null] local treeway_tile_e = [null, null] // local double_way_tiles = [] if ( wt != wt_water && wt != wt_air ) { //gui.add_message_at(our_player, " search way line ", world.get_time()) local asf = astar_route_finder(wt) wayline = asf.search_route([start_l], [end_l]) local i = 0 // remove depot if ( depot == null ) { depot = search_depot(start_l, wt_road) if ( !depot ) { depot = search_depot(end_l, wt_road) } } if ( depot != false && check_home_depot(depot, wt) ) { // todo check vehicles in depot remove_tile_to_empty(depot, wt, 0) } //depot = search_depot(start_l, wt, 15) if ("err" in wayline) { gui.add_message_at(our_player, "ERROR search way line for remove", world.get_time()) } else { local sig_tile = 0 local t_t = 0 local treeway_tiles = [] foreach(node in wayline.routes) { local tile = tile_x(node.x, node.y, node.z) nexttile.append(tile) // check route to treeways // treeway tile other player, set next tile for remove if ( t_t == 1 ) { treeway_tile_s[1] = tile t_t = 0 } // one treeway ( depot ) then no split line way if ( dir.is_threeway(tile.get_way_dirs(wt)) ) { if ( treeways == 0 ) { treeway_tile_e[0] = tile treeway_tile_s[0] = tile } else { // if ( !(depot == false) && !(depot == null) ) { if ( tile.x == depot.x || tile.y == depot.y ) { depot_t = tile } else { treeway_tile_s[0] = tile } } else { treeway_tile_s[0] = tile } } treeways++ //gui.add_message_at(our_player, " ## treeway_tiles " + coord3d_to_string(tile), tile) treeway_tiles.append(tile) t_t = 1 // test signals by rail if ( sig_tile == 1 && wt == wt_rail ) { double_way_tiles.append(treeway_tiles.top()) sig_tile = 0 } } // treeway tile other player, set next tile for remove if ( treeway_tile_e[0] == null ) { treeway_tile_e[1] = tile } // test signals by rail if ( tile.find_object(mo_signal) != null && wt == wt_rail ) { //double_way_tiles.append(treeway_tile_s[0]) double_way_tiles.append(treeway_tiles.top()) sig_tile = 1 double_ways++ //gui.add_message_at(our_player, " ## treeway_tiles.top() " + coord3d_to_string(treeway_tiles.top()), tile) } /*else if ( dir.is_threeway(tile.get_way_dirs(wt)) && sig_tile == 1 && wt == wt_rail ) { double_way_tiles.append(tile) sig_tile = 0 }*/ } if ( wt == wt_rail && print_message_box > 0 ) { gui.add_message_at(our_player, " double_ways " + double_ways, world.get_time()) gui.add_message_at(our_player, " double_way_tiles.get_count() " + double_way_tiles.len(), world.get_time()) } } } // world.get_convoy_list() if ( wt == wt_rail ) { local remove_all = 0 if ( print_message_box == 1 ) { gui.add_message_at(our_player, " remove wt_rail line ", world.get_time()) } // remove combined station ( rail - water ) on start local tool = command_x(tool_remove_way) if ( start_line_count < 2 ) { // remove combined waytype halt water - not rail if ( combined_s > 1 ) { local t = start_h.get_tile_list() for ( local i = 0; i < t.len(); i++ ) { local k = t[i].find_object(mo_building).get_desc().get_waytype() if ( print_message_box == 1 ) { gui.add_message_at(our_player, " station tile " + i + " waytype " + k, world.get_time()) } if ( k == wt_water ) { local tool = command_x(tool_remover) //tool.work(our_player, t[i]) // to do ship line destroy combined_s = 1 start_line_count = 0 break } } } if ( treeways == 0 && combined_s == 1 && combined_e == 1 && start_line_count == 0 && end_line_count == 0 ) { // remove all remove_all = 1 } /* // remove depot if ( check_home_depot(depot, wt) ) { remove_tile_to_empty(depot, wt, 0) } */ if ( print_message_box > 0 ) { gui.add_message_at(our_player, " treeways " + treeways + " combined_s " + combined_s + " combined_e " + combined_e, world.get_time()) gui.add_message_at(our_player, " start_line_count " + start_line_count + " end_line_count " + end_line_count + " remove_all " + remove_all, world.get_time()) gui.add_message_at(our_player, " double_ways " + double_ways, world.get_time()) //::debug.pause() } local cnv_count_start = start_l.get_way(wt_rail).get_convoys_passed()[0] + start_l.get_way(wt_rail).get_convoys_passed()[1] local cnv_count_end = end_l.get_way(wt_rail).get_convoys_passed()[0] + end_l.get_way(wt_rail).get_convoys_passed()[1] // remove way from start to first treeway if ( treeways > 1 && combined_s == 1 && combined_e == 1 && start_line_count == 0 && remove_all == 0 ) { // remove station and way to next treeway if ( print_message_box == 1 ) { gui.add_message_at(our_player, "treeway_tile_s[0] " + coord3d_to_string(treeway_tile_s[0]), treeway_tile_s[0]) } local err = tool.work(our_player, start_l, treeway_tile_s[0], "" + wt_rail) if ( err != null ) { gui.add_message_at(our_player, " ## ERROR remove way start_l" + coord3d_to_string(start_l), start_l) } //remove_tile_to_empty(depot, wt, 0) //remove_tile_to_empty(depot_t, wt, 0) } // remove way from end to first treeway if ( treeways > 1 && combined_s == 1 && combined_e == 1 && end_line_count == 0 && remove_all == 0 ) { // remove station and way to next treeway if ( print_message_box == 1 ) { gui.add_message_at(our_player, "treeway_tile_e[0] " + coord3d_to_string(treeway_tile_e[0]), treeway_tile_e[0]) } local err = tool.work(our_player, end_l, treeway_tile_e[0], "" + wt_rail) if ( err != null ) { gui.add_message_at(our_player, " ## ERROR remove way end_l" + coord3d_to_string(end_l), start_l) } } // remove double ways by rail if ( double_ways > 0 ) { local j = 0; local c = 0 if ( double_ways > 4 ) { c = 1 } for ( local i = 0; i < double_ways; i++ ) { // remove double way // local cnv_count = t_field.get_convoys_passed()[0] + t_field.get_convoys_passed()[1] local cnv_count_0 = double_way_tiles[j].get_way(wt_rail).get_convoys_passed()[0] + double_way_tiles[j].get_way(wt_rail).get_convoys_passed()[1] local cnv_count_1 = double_way_tiles[j+1].get_way(wt_rail).get_convoys_passed()[0] + double_way_tiles[j+1].get_way(wt_rail).get_convoys_passed()[1] if ( print_message_box > 0 ) { gui.add_message_at(our_player, " double_way_tiles[j] " + coord3d_to_string(double_way_tiles[j]) + " double_way_tiles[j+1] " + coord3d_to_string(double_way_tiles[j+1]), double_way_tiles[j]) gui.add_message_at(our_player, " j " + j, double_way_tiles[j+1]) gui.add_message_at(our_player, " cnv_count_0 " + cnv_count_0 + " cnv_count_1 " + cnv_count_1 + " cnv_count_start " + cnv_count_start + " cnv_count_end " + cnv_count_end, double_way_tiles[i]) //::debug.pause() } if ( cnv_count_0 == cnv_count_1 && (cnv_count_0 <= (cnv_count_start + c) || cnv_count_0 <= (cnv_count_end + c)) ) { tool.work(our_player, double_way_tiles[j], double_way_tiles[j+1], "" + wt_rail) tool.work(our_player, double_way_tiles[j+1], double_way_tiles[j], "" + wt_rail) if ( i < (double_ways-1) ) { // remove single way to next double way tool.work(our_player, double_way_tiles[j+1], double_way_tiles[j+2], "" + wt_rail) //::debug.pause() } } else if ( !dir.is_threeway(double_way_tiles[j].get_way_dirs(wt)) && !dir.is_threeway(double_way_tiles[j+1].get_way_dirs(wt)) ) { tool.work(our_player, double_way_tiles[j], double_way_tiles[j+1], "" + wt_rail) tool.work(our_player, double_way_tiles[j+1], double_way_tiles[j], "" + wt_rail) } j += 2; } } } if ( start_f.len() > 0 && combined_s > 1 && start_line_count > 0 ) { // check lines befor remove start local lines = start_h.get_line_list() local i = 1 foreach(line in lines) { if ( line.get_waytype() == wt_rail ) { i++ } } remove_all = i } if ( end_f.len() > 0 && combined_e > 1 && end_line_count > 0 ) { // check lines befor remove end local lines = end_h.get_line_list() local i = 1 foreach(line in lines) { if ( line.get_waytype() == wt_rail ) { i++ } } remove_all = i } // remove rail line way by single halt and no more treeways if ( remove_all == 1 ) { // remove line way if ( print_message_box == 1 ) { gui.add_message_at(our_player, " remove_all == 1 ", world.get_time()) } local tool = command_x(tool_remove_way) if ( start_l.is_empty() || end_l.is_empty() ) { local c1 = 0 local c2 = 0 for ( local i = 0; i > 10; i++ ) { if ( !nexttile[nexttile.len-i].is_empty() ) { start_l = nexttile[nexttile.len-i] c1++ } if ( !nexttile[i].is_empty() ) { end_l = nexttile[i] c2++ } if ( (c1+c2) == 2 ) { break } } } local t = start_l.get_halt().get_tile_list() tool.work(our_player, start_l, end_l, "" + wt_rail) // remove all tiles from halt start for ( local i = 0; i < t.len(); i++ ) { if ( t[i].find_object(mo_building) ) { t[i].remove_object(our_player, mo_building) } } } //::debug.pause() } // remove road line if ( wt == wt_road ) { // remove depot not other road stations in range // to do check other stations /*if ( check_home_depot(depot, wt) ) { remove_tile_to_empty(depot, wt, 0) }*/ // remove way line and station by 0 lines connected start_line_count = start_h.get_line_list().get_count() end_line_count = end_h.get_line_list().get_count() if ( print_message_box == 1 ) { gui.add_message_at(our_player, " treeway_tile_s " + treeway_tile_s[0] + " " + treeway_tile_s[1], world.get_time()) gui.add_message_at(our_player, " treeway_tile_e " + treeway_tile_e[0] + " " + treeway_tile_e[1], world.get_time()) } local tool = command_x(tool_remove_way) if ( start_line_count == 0 ) { // remove combined waytype halt water - not road if ( combined_s > 1 ) { local t = start_h.get_tile_list() for ( local i = 0; i < t.len(); i++ ) { local k = t[i].find_object(mo_building).get_desc().get_waytype() if ( k == wt_water ) { local tool = command_x(tool_remover) tool.work(our_player, t[i]) break } } } // remove station and way to next treeway // treeway tile check player if ( treeway_tile_s[0] != null && treeway_tile_s[0].find_object(mo_way).get_owner() == our_player ) { tool.work(our_player, start_l, treeway_tile_s[0], "" + wt_road) } else if ( treeway_tile_s[1] != null && treeways > 0 ) { tool.work(our_player, start_l, treeway_tile_s[1], "" + wt_road) remove_tile_to_empty(treeway_tile_s[1], wt_road, 0) } } if ( end_line_count == 0 ) { // remove combined waytype halt water - not road if ( combined_e > 1 ) { local t = end_h.get_tile_list() for ( local i = 0; i < t.len(); i++ ) { local k = t[i].find_object(mo_building).get_desc().get_waytype() if ( k == wt_water ) { local tool = command_x(tool_remover) tool.work(our_player, t[i]) //break } } } // remove station and way to next treeway // treeway tile check player if ( treeway_tile_e[0] != null && treeway_tile_e[0].find_object(mo_way).get_owner() == our_player ) { tool.work(our_player, end_l, treeway_tile_e[0], "" + wt_road) } else if ( treeway_tile_e[1] != null && treeways > 0 ) { tool.work(our_player, end_l, treeway_tile_e[1], "" + wt_road) remove_tile_to_empty(treeway_tile_e[1], wt_road, 0) } } if ( treeways == 0 ) { tool.work(our_player, end_l, start_l, "" + wt_road) } } // remove water line if ( wt == wt_water ) { local tool = command_x(tool_remover) // remove depot if ( check_home_depot(depot, wt) ) { local err = depot.remove_object(our_player, mo_depot_rail) if ( err != null ) { gui.add_message_at(our_player, "## ERROR depot remove " + depot + ": " + err, home_depot) } //tool.work(our_player, depot) } // remove way line and station by 0 lines connected start_line_count = start_h.get_line_list().get_count() end_line_count = end_h.get_line_list().get_count() if ( start_line_count <= 1 ) { // remove combined waytype halt water - not road/rail if ( combined_s >= 1 && combined_e == 1 ) { local t = start_h.get_tile_list() for ( local i = 0; i < t.len(); i++ ) { if ( t[i].find_object(mo_building) != null ) { local k = t[i].find_object(mo_building).get_desc().get_waytype() if ( k == wt_water ) { tool.work(our_player, t[i]) //break } /*else if ( k == wt_rail ) { tool.work(our_player, t[i]) }*/ } } //t = start_h.get_tile_list() //tool.work(our_player, t[0]) } } if ( end_line_count <= 1 ) { // remove combined waytype halt water - not road/rail if ( combined_e <= 2 ) { local t = end_h.get_tile_list() for ( local i = 0; i < t.len(); i++ ) { if ( t[i].find_object(mo_building) != null ) { local k = t[i].find_object(mo_building).get_desc().get_waytype() if ( k == wt_water ) { tool.work(our_player, t[i]) /* break } else if ( k == wt_rail ) { tool.work(our_player, t[i])*/ } } } } } } if ( print_message_box > 0 ) { gui.add_message_at(our_player, "+ destroy_line(line_obj, good, link) finish line " + line_name, world.get_time()) //::debug.pause() } local msgtext = format(translate("%s removes line: %s"), our_player.get_name(), line_name) gui.add_message_at(our_player, msgtext, world.get_time()) //link_obj.status = 4 //link_obj.next_check = today_plus_months(36) check_stations_connections() return true } /// @publicsection /* * check waytypes from halt * tile = one tile from halt * return count waytypes */ function test_halt_waytypes(tile) { if ( tile.is_water() && tile.find_object(mo_building) == null ) { //gui.add_message_at(our_player, "halt is water tile " + coord3d_to_string(tile), tile) return 0 } local halt_tiles = tile.get_halt().get_tile_list() //gui.add_message_at(our_player, "halt has tiles " + halt_tiles.len(), tile) local test_rail = 0 local test_road = 0 local test_water = 0 for ( local i = 0; i < halt_tiles.len(); i++ ) { local building = halt_tiles[i].find_object(mo_building).get_desc() if ( building ) { switch (building.get_waytype()) { case wt_rail: test_rail++ break case wt_road: test_road++ break case wt_water: test_water++ break } } } local test_way = 0 if ( test_rail > 0 ) { test_way++ } if ( test_road > 0 ) { test_way++ } if ( test_water > 0 ) { test_way++ } return test_way } /// @publicsection /* * check depot as home for other vehicles * tile = depot coord * wt = waytype * * return: true = no vehicles; false = other vehicles */ function check_home_depot(tile, wt) { // load vehicle list local list = world.get_convoy_list() foreach(cnv in list) { if ( tile.x == cnv.get_home_depot().x && tile.y == cnv.get_home_depot().y && tile.z == cnv.get_home_depot().z ) { return false } } return true } /* * check stations to valid link * * * * */ function check_stations_connections() { //gui.add_message_at(our_player, "####### check_stations_connections()" , world.get_time()) if ( debug ) ::debug.set_pause_on_error(true) local haltlist = [] // list player halts foreach(halt in halt_list_x()) { if (halt.get_owner().nr == our_player_nr) { // && halt.get_type == wt haltlist.append(halt) } } // halt no lines and convoys local halt_unused = [] // test factory connection foreach(halt in haltlist) { local s = halt.get_name() local halt_factory_connect = halt.get_factory_list().len() local halt_lines = halt.get_line_list() local halt_line = null local halt_cnv = halt.get_convoy_list() //if ( halt_lines.get_count() == 1 ) { gui.add_message_at(our_player, "####### halt_lines[0]: " + halt_lines[0].get_name(), world.get_time()) } //local tool = command_x(tool_remover) if ( halt_lines.get_count() == 0 && halt_cnv.get_count() == 0 ) { //gui.add_message_at(our_player, "####### halt name: " + halt.get_name(), world.get_time()) // halt no lines and convoys - remove local t = halt.get_tile_list() for ( local i = 0; i < t.len(); i++ ) { if ( t[i].has_way(wt_rail) ) { // remove way by wt_rail from halt tiles gui.add_message_at(our_player, "####### remove rail halt : " + halt.get_name(), t[i]) remove_tile_to_empty(t, wt_rail, 1) } else { //gui.add_message_at(our_player, "####### remove building halt name: " + halt.get_name(), t[i]) t[i].remove_object(our_player, mo_building) } } } else if ( halt_factory_connect == 0 && halt_lines.get_count() < 2 ) { local line_list = player_x(our_player.nr).get_line_list() foreach(line in line_list) { //gui.add_message_at(player_x(our_player.nr), "####### line check " + line.get_name(), world.get_time()) if ( halt_lines[0].id == line.id ) { halt_line = line break } } //gui.add_message_at(our_player, "####### halt name: " + halt.get_name(), world.get_time()) local home_depot = null local wt = null local good = null // combined station not finish if ( halt_cnv.get_count() > 0 ) { // remove convoys from halt local j = halt_cnv.get_count() home_depot = halt_cnv[0].get_home_depot() wt = halt_cnv[0].get_waytype() for ( local i = 0; i < j; i++ ) { halt_cnv[i].destroy(our_player) } } else if ( halt.get_line_list().get_count() == 1 ) { local link_list = industry_manager.link_list local search_line = null foreach(link in link_list) { // start_f[0], end_f[0], good.get_name() foreach(index, line in link.lines) { //gui.add_message_at(player_x(our_player.nr), "####### line check " + line.get_name(), world.get_time()) if ( line.is_valid() && line.get_owner().nr == our_player.nr && halt_line.get_name() == line.get_name() ) { search_line = line } } } if ( search_line != null ) { destroy_line(search_line, good, null) //halt_line } break } } } } function check_combined_station(halt) { local lines = halt.get_line_list() local factorys = halt.get_factory_list() local halt_tiles = halt.get_tile_list() local wts = test_halt_waytypes(halt_tiles[0]) if ( wts > 1 && lines.get_count() == 1 && factorys.len() > 0 ) { gui.add_message_at(our_player, "####### station more wt and one line - factory connect ", world.get_time()) local wt = null foreach(line in lines) { wt = line.get_waytype() } local replace_tiles = [] for ( local i = 0; i < halt_tiles.len(); i++ ) { local building = halt_tiles[i].find_object(mo_building).get_desc() if ( building ) { if ( building.get_waytype() != wt ) { replace_tiles.append(halt_tiles[i]) } } } local extension = find_extension(wt_road) if ( extension != null ) { for ( local i = 0; i < replace_tiles.len()-1; i++ ) { remove_tile_to_empty(replace_tiles[i], wt_road, 0) command_x.build_station(our_player, replace_tiles[i], extension) } } } } function station_aw(start_field, wt, awst_array) { local print_message_box = 0 local b_player = our_player //gui.add_message_at(b_player, "station_aw ", start_field) local way_obj = start_field.find_object(mo_way).get_desc() //way_list[0] if ( !way_obj.is_available(world.get_time()) ) { way_obj = find_object("way", wt, way_obj.get_topspeed()) } local way_len = 8 local d = start_field.get_way_dirs(wt) local tiles = [] local tiles_build_l = [] local tiles_build_r = [] for (local i = 0; i < way_len; i++) { if ( d == 5 ) { // build from n to s // ns - r local ref_ground = square_x(start_field.x, start_field.y + i).get_ground_tile() local t = tile_x(start_field.x + 1, start_field.y + i, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(start_field.x, start_field.y + i).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(start_field.x + 1, start_field.y + i, ref_ground.z)) } // ns - l t = tile_x(start_field.x - 1, start_field.y + i, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(start_field.x, start_field.y + i).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(start_field.x - 1, start_field.y + i, ref_ground.z)) } tiles.append(ref_ground) } else if ( d == 10 ) { //gui.add_message_at(our_player, "tile r " + coord3d_to_string(tile_x(start_field.x + i, start_field.y + 1, start_field.z)) + " empty " + tile_x(start_field.x + i, start_field.y + 1, start_field.z).is_empty(), start_field) //gui.add_message_at(our_player, " -- tile l " + coord3d_to_string(tile_x(start_field.x + i, start_field.y - 1, start_field.z)) + " to empty " + tile_x(start_field.x - i, start_field.y + 1, start_field.z).is_empty(), start_field) // ew - r // build from w to e local ref_ground = square_x(start_field.x + i, start_field.y).get_ground_tile() local t = tile_x(start_field.x + i, start_field.y + 1, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(start_field.x + i, start_field.y).get_ground_tile().z == ref_ground.z tiles_build_r.append(tile_x(start_field.x + i, start_field.y + 1, ref_ground.z)) } // ew - l t = tile_x(start_field.x + i, start_field.y - 1, start_field.z) if ( t.is_valid() && test_tile_is_empty(t) ) { //&& square_x(start_field.x + i, start_field.y).get_ground_tile().z == ref_ground.z tiles_build_l.append(tile_x(start_field.x + i, start_field.y - 1, ref_ground.z)) } tiles.append(ref_ground) } } local tl = 0 local tr = 0 local tiles_build = null //way_len -= 1 if ( tiles_build_r.len() == way_len || tiles_build_l.len() == way_len ) { if ( tiles_build_r.len() == way_len ) { for ( local i = 0; i < way_len; i++ ) { if ( tiles_build_r[i].get_slope() == 0 ) { tr++ } } } if ( tiles_build_l.len() == way_len ) { for ( local i = 0; i < way_len; i++ ) { if ( tiles_build_l[i].get_slope() == 0 ) { tl++ } } } local err = null local terraform = 0 if ( tr == way_len ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "build flat right ", start_field) } tiles_build = tiles_build_r tr = way_len tl = 0 } else if ( tl == way_len ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "build flat left ", start_field) } tiles_build = tiles_build_l tl = way_len tr = 0 } else if ( ( tr < tiles_build_r.len() && tiles_build_r.len() == way_len ) && tr >= tl ) { // check terraform tr if ( print_message_box == 1 ) { gui.add_message_at(b_player, "build right check terraform", world.get_time()) } tiles_build = tiles_build_r tr = way_len tl = 0 terraform = 1 } else if ( tl < tiles_build_l.len() && tiles_build_l.len() == way_len ) { // check terraform tl if ( print_message_box == 1 ) { gui.add_message_at(b_player, "build left check terraform", world.get_time()) } tiles_build = tiles_build_l tl = way_len tr = 0 terraform = 1 } if ( terraform == 1 ) { // change terraform local g = terraform_way(tiles, tiles_build, tr, tl, way_len, d) tr = g[0] tl = g[1] if ( way_len > g[2] ) { tiles_build.remove(tiles_build.len()-1) tiles.remove(tiles.len()-1) } /* if ( tr == way_len ) { tr-- } if ( tl == way_len ) { tl-- } way_len-- */ } // set build left or right /*if ( tr == way_len && tl == way_len ) { } else */ if ( tr == way_len ) { tiles_build = tiles_build_r } else if ( tl == way_len ) { tiles_build = tiles_build_l } if ( tiles_build == null ) { //gui.add_message_at(b_player, " ERROR no double way found " + coord3d_to_string(tiles[0]), start_field) return false } // build way local err = null if ( start_field.get_way_dirs(wt) == 5 || start_field.get_way_dirs(wt) == 10 ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, "5/10 build tiles[0] " + coord3d_to_string(tiles[0]) + " to tiles_build[0] " + coord3d_to_string(tiles_build[0]), start_field) } // connect diagonal way local b_tile = null local tile_a = null local tile_b = null local tile_check = 0 if ( tl == way_len ) { if ( start_field.get_way_dirs(wt) == 5 ) { tile_a = tile_x(tiles[0].x, tiles[0].y-1, tiles[0].z) tile_b = tile_x(tiles[0].x-1, tiles[0].y-1, tiles[0].z) if ( tile_a.get_way_dirs(wt) == 12 && (tile_b.get_way_dirs(wt) == 3 || (tile_b.get_way_dirs(wt) == 10 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b } tile_check = 1 //gui.add_message_at(b_player, "(2765) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( start_field.get_way_dirs(wt) == 10 ) { tile_a = tile_x(tiles[0].x-1, tiles[0].y, tiles[0].z) tile_b = tile_x(tiles[0].x-1, tiles[0].y-1, tiles[0].z) if ( (tile_a.get_way_dirs(wt) == 12 || tile_a.get_way_dirs(wt) == 3) && (tile_b.get_way_dirs(wt) == 3 || tile_b.get_way_dirs(wt) == 12 || (tile_b.get_way_dirs(wt) == 5 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b } tile_check = 2 //gui.add_message_at(b_player, "(2773) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } } else if ( tr == way_len ) { if ( start_field.get_way_dirs(wt) == 5 ) { tile_a = tile_x(tiles[0].x, tiles[0].y-1, tiles[0].z) tile_b = tile_x(tiles[0].x+1, tiles[0].y-1, tiles[0].z) if ( tile_a.get_way_dirs(wt) == 6 && (tile_b.get_way_dirs(wt) == 9 || (tile_b.get_way_dirs(wt) == 10 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b } tile_check = 3 //gui.add_message_at(b_player, "(2785) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( start_field.get_way_dirs(wt) == 10 ) { tile_a = tile_x(tiles[0].x-1, tiles[0].y, tiles[0].z) tile_b = tile_x(tiles[0].x-1, tiles[0].y+1, tiles[0].z) if ( tile_a.get_way_dirs(wt) == 6 && (tile_b.get_way_dirs(wt) == 9 || (tile_b.get_way_dirs(wt) == 5 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b } tile_check = 4 //gui.add_message_at(b_player, "(2795) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } } if ( b_tile == null ) { err = command_x.build_way(b_player, tiles[0], tiles_build[0], way_obj, true) } else { err = command_x.build_way(b_player, b_tile, tiles_build[0], way_obj, true) } if ( err == null ) { if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[0] " + coord3d_to_string(tiles_build[0]) + " to tiles_build[" + (way_len - 1) + "] " + coord3d_to_string(tiles_build[way_len - 1]), start_field) } err = command_x.build_way(b_player, tiles_build[0], tiles_build[way_len - 1], way_obj, true) if ( err != null ) { gui.add_message_at(b_player, " ERROR => build tile " + coord3d_to_string(tiles[0]) + " to tile " + coord3d_to_string(tiles_build[way_len - 1]), tiles[0]) err = null } if ( print_message_box == 1 ) { gui.add_message_at(b_player, " build tiles_build[" + (way_len - 1) + "] " + coord3d_to_string(tiles_build[way_len - 1]) + " to tiles[" + (way_len - 1) + "] " + coord3d_to_string(tiles[way_len - 1]), start_field) } // connect diagonal way b_tile = null if ( tile_check == 1 ) { tile_a = tile_x(tiles[way_len - 1].x, tiles[way_len - 1].y+1, tiles[way_len - 1].z) tile_b = tile_x(tiles[way_len - 1].x-1, tiles[way_len - 1].y+1, tiles[way_len - 1].z) if ( tile_a.get_way_dirs(wt) == 9 && (tile_b.get_way_dirs(wt) == 6 || tile_b.get_way_dirs(wt) == 10 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0) ) { b_tile = tile_b } //gui.add_message_at(b_player, "(2827) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( tile_check == 2 ) { tile_a = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y, tiles[way_len - 1].z) tile_b = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y-1, tiles[way_len - 1].z) if ( tile_a.get_way_dirs(wt) == 6 && (tile_b.get_way_dirs(wt) == 9 || tile_b.get_way_dirs(wt) == 10 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0) ) { b_tile = tile_b } //gui.add_message_at(b_player, "(2836) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( tile_check == 3 ) { tile_a = tile_x(tiles[way_len - 1].x, tiles[way_len - 1].y+1, tiles[way_len - 1].z) tile_b = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y+1, tiles[way_len - 1].z) if ( tile_a.get_way_dirs(wt) == 3 && (tile_b.get_way_dirs(wt) == 12 || (tile_b.get_way_dirs(wt) == 5 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b } //gui.add_message_at(b_player, "(2844) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } else if ( tile_check == 4 ) { tile_a = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y, tiles[way_len - 1].z) tile_b = tile_x(tiles[way_len - 1].x+1, tiles[way_len - 1].y+1, tiles[way_len - 1].z) if ( tile_a.get_way_dirs(wt) == 12 && (tile_b.get_way_dirs(wt) == 3 || (tile_b.get_way_dirs(wt) == 5 && !tile_b.is_bridge() && !tile_b.is_tunnel() && tile_b.get_slope() == 0)) ) { b_tile = tile_b } //gui.add_message_at(b_player, "(2853) tile_a " + coord3d_to_string(tile_a) + " tile_b " + coord3d_to_string(tile_b), start_field) } if ( b_tile == null ) { err = command_x.build_way(b_player, tiles_build[way_len - 1], tiles[way_len - 1], way_obj, true) } else { err = command_x.build_way(b_player, tiles_build[way_len - 1], b_tile, way_obj, true) } local staw = [] for ( local j = 0; j < tiles.len(); j++ ) { if ( tiles[j].find_object(mo_building) == null && ( tiles[j].get_way_dirs(wt) == 5 || tiles[j].get_way_dirs(wt) == 10 ) ) { staw.append(tiles[j]) } } remove_tile_to_empty(staw, wt, 1) } } } /* if ( tiles[0].x == tiles[1].x && tiles[0].y < tiles[1].y ) { // N -> S } else if ( tiles[0].x < tiles[1].x && tiles[0].y == tiles[1].y ) { // W -> E } */ //err = command_x.build_way(b_player, tiles_build[way_len - 1], tiles[way_len - 1], way_obj, true) //tiles_build[way_len - 1] // awst_array //gui.add_message_at(b_player, "(6663) tr " + tr + " tl " + tl, start_field) // Ausweichstelle nach Station if ( tl == way_len ) { //gui.add_message_at(b_player, "(6664) ", awst_array[0]) if ( awst_array[0].x == awst_array[awst_array.len()-1].x || awst_array[0].y == awst_array[awst_array.len()-1].y ) { local tile_c = tile_x(awst_array[0].x, awst_array[0].y-1, awst_array[0].z) //gui.add_message_at(b_player, "(6667) ", tile_c) local err = command_x.build_way(b_player, awst_array[0], tile_c, way_obj, true) gui.add_message_at(b_player, "(6088) err " + err, awst_array[0]) if ( err != null ) { remove_tile_to_empty(awst_array, wt_rail, 1) } else { err = command_x.build_way(b_player, tile_c, tiles_build[0], way_obj, true) gui.add_message_at(b_player, "(6093) err " + err, tiles_build[0]) if ( err != null ) { remove_tile_to_empty(awst_array, wt_rail, 1) } else { local obj_sign = find_signal("is_signal", wt) local signal = null local s_tile = tile_x(awst_array[1].x,awst_array[1].y-1, awst_array[1].z) if ( s_tile.get_way_dirs(wt) == 10 ) { // ew signal = [{coor=coord3d(awst_array[1].x,awst_array[1].y-1, awst_array[1].z), ribi=8}, {coor=coord3d(awst_array[awst_array.len()-2].x, awst_array[awst_array.len()-2].y, awst_array[awst_array.len()-2].z), ribi=2}] } else if ( s_tile.get_way_dirs(wt) == 5 ) { // ns signal = [{coor=coord3d(awst_array[1].x,awst_array[1].y-1, awst_array[1].z), ribi=1}, {coor=coord3d(awst_array[awst_array.len()-2].x, awst_array[awst_array.len()-2].y, awst_array[awst_array.len()-2].z), ribi=4}] } for ( local j=0; j < 2; j++ ) { local s_ribi = signal[j].ribi local signal_build_tile = tile_x(signal[j].coor.x, signal[j].coor.y, signal[j].coor.z) gui.add_message_at(b_player, "(6103) signal_build_tile " + coord3d_to_string(signal_build_tile), signal_build_tile) while(true){ err = command_x.build_sign_at(b_player, signal_build_tile, obj_sign) if ( err != null ) { break } local ribi = signal_build_tile.get_way_dirs_masked(wt) if (ribi == s_ribi) break } } if ( err != null ) { remove_tile_to_empty(awst_array, wt_rail, 1) } } } } } } function terraform_way(tiles, tiles_build, tr, tl, way_len, d) { local err = null local b_player = our_player // 21 message slope local print_message_box = 0 if ( print_message_box > 0 ) { gui.add_message_at(b_player, " ---=> terraform_way() ", world.get_time()) } for ( local i = 0; i < tiles_build.len(); i++ ) { //local r = square_x(tiles_build[i].x, tiles_build[i].y) local z = 0 //local f = tile_x(tiles_build[i].x, tiles_build[i].y, z.z) local build_hight = square_x(tiles_build[i].x, tiles_build[i].y).get_ground_tile() local ref_hight = square_x(tiles[i].x, tiles[i].y).get_ground_tile() local straight_slope = false if ( ref_hight.get_slope() == 4 || ref_hight.get_slope() == 12 || ref_hight.get_slope() == 28 || ref_hight.get_slope() == 36 ) { // single hight && double hight 1 straight_slope = true } else if ( ref_hight.get_slope() == 8 || ref_hight.get_slope() == 24 || ref_hight.get_slope() == 56 || ref_hight.get_slope() == 72 ) { // double hight 2 straight_slope = true } if ( print_message_box == 21 ) { gui.add_message_at(b_player, " ---=> tiles[i] ground " + coord3d_to_string(ref_hight), ref_hight) gui.add_message_at(b_player, " ---=> tiles_build[i] ground " + coord3d_to_string(build_hight), build_hight) gui.add_message_at(b_player, " ---=> ref_hight.get_slope() " + ref_hight.get_slope(), world.get_time()) gui.add_message_at(b_player, " ---=> straight_slope " + straight_slope, world.get_time()) } if ( build_hight.get_slope() == ref_hight.get_slope() ) { // slope ref_tile == slope build_tile -> not terraform if ( print_message_box == 21 ) { gui.add_message_at(b_player, " slope ref_tile == slope build_tile -> not terraform ", build_hight) } } else if ( build_hight.is_empty() && ( build_hight.get_slope() > 0 || ref_hight.z != build_hight.z ) ) { if ( print_message_box == 21 ) { gui.add_message_at(b_player, " ---=> terraform", world.get_time()) gui.add_message_at(b_player, " ---=> tiles_build.z " + build_hight.z + " tiles.z " + ref_hight.z, world.get_time()) } if ( (build_hight.z < ref_hight.z && build_hight.z >= (ref_hight.z - 2)) ) { //|| straight_slope == true // terraform up if ( print_message_box == 21 ) { gui.add_message_at(b_player, " ---=> terraform up ", world.get_time()) } local terraform_tile = 1 // terraform grid up if ( print_message_box == 21 ) { gui.add_message_at(b_player, " build_hight.get_slope() " + build_hight.get_slope(), build_hight) } local tile_build_slope = [37, 9, 31, 1, 3] if ( tile_build_slope.find(build_hight.get_slope()) != null ) { local cx = 0 local cy = 0 local build_side = 0 if ( d == 10 ) { cx = 1 if ( tiles_build[i].y > tiles[i].y ) { build_side = 1 } } else if ( d == 5 ) { cy = 1 if ( tiles_build[i].x > tiles[i].x ) { build_side = 1 } } local tile_a = square_x(tiles_build[i].x+cx, tiles_build[i].y+cy).get_ground_tile() local tile_a1 = null local tile_b1 = null if ( build_side == 1 ) { tile_a1 = square_x(tiles_build[i].x+cy, tiles_build[i].y+cx).get_ground_tile() tile_b1 = square_x(tile_a.x+cy, tile_a.y+cx).get_ground_tile() } else { tile_a1 = square_x(tiles_build[i].x-cy, tiles_build[i].y-cx).get_ground_tile() tile_b1 = square_x(tile_a.x-cy, tile_a.y-cx).get_ground_tile() } if ( print_message_box == 21 ) { gui.add_message_at(b_player, " tile_a.get_slope() " + tile_a.get_slope(), tile_a) gui.add_message_at(b_player, " tile_a1.get_slope() " + tile_a1.get_slope(), tile_a1) gui.add_message_at(b_player, " tile_b1.get_slope() " + tile_b1.get_slope(), tile_b1) } local tile_a_slope = [27, 36, 31, 1] if ( build_hight.get_slope() == 37 && tile_a_slope.find(tile_a.get_slope()) != null && test_tile_is_empty(tile_a1) && test_tile_is_empty(tile_b1) ) { err = command_x.grid_raise(our_player, coord3d(tile_b1.x, tile_b1.y, tile_b1.z)) if ( err == null ) { terraform_tile = 0 } } else if ( (build_hight.get_slope() == 31 || build_hight.get_slope() == 3) && (tile_a.get_slope() == 4 || tile_a.get_slope() == 13 || tile_a.get_slope() == 1) ) { local tile_a1 = null local tile_b1 = null if ( build_side == 1 ) { tile_a1 = square_x(tiles_build[i].x+cy, tiles_build[i].y+cx).get_ground_tile() tile_b1 = square_x(tile_a.x+cy, tile_a.y+cx).get_ground_tile() } else { tile_a1 = square_x(tiles_build[i].x-cy, tiles_build[i].y-cx).get_ground_tile() tile_b1 = square_x(tile_a.x-cy, tile_a.y-cx).get_ground_tile() } //tile_a1 = square_x(tiles_build[i].x, tiles_build[i].y-1).get_ground_tile() //tile_b1 = square_x(tile_a.x, tile_a.y-1).get_ground_tile() if ( test_tile_is_empty(tile_a1) && test_tile_is_empty(tile_b1) ) { err = command_x.grid_raise(our_player, coord3d(tile_a.x, tile_a.y, tile_a.z)) terraform_tile = 0 } } } // terraform tile if ( terraform_tile == 1 ) { do { err = command_x.set_slope(b_player, build_hight, 82 ) if ( err != null ) { break } z = square_x(tiles_build[i].x, tiles_build[i].y).get_ground_tile() } while(z.z < ref_hight.z ) } } else if ( build_hight.z >= ref_hight.z || build_hight.z <= (ref_hight.z + 1) ) { // terraform down if ( print_message_box == 21 ) { gui.add_message_at(b_player, " ---=> terraform down ", world.get_time()) } if ( (i == 0 && tiles[0].get_slope() > 0 ) || ( i == (way_len-1) && tiles[way_len-1].get_slope() > 0 ) ) { } else { local terraform_tile = 1 // terraform grid down if ( print_message_box == 21 ) { gui.add_message_at(b_player, " build_hight.get_slope() " + build_hight.get_slope(), build_hight) } local tile_build_slope = [3, 1, 9, 37, 39] if ( tile_build_slope.find(build_hight.get_slope()) != null ) { local cx = 0 local cy = 0 local build_side = 0 if ( d == 10 ) { cx = 1 if ( tiles_build[i].y > tiles[i].y ) { build_side = 1 } } else if ( d == 5 ) { cy = 1 if ( tiles_build[i].x > tiles[i].x ) { build_side = 1 } } local tile_a = square_x(tiles_build[i].x+cx, tiles_build[i].y+cy).get_ground_tile() local tile_a1 = null local tile_b1 = null local tile_a_slope = null local tile_a_slope_WE = null local tile_a_slope_NS = null if ( build_side == 1 ) { // x/y build way > exists way tile_a1 = square_x(tiles_build[i].x+cy, tiles_build[i].y+cx).get_ground_tile() tile_b1 = square_x(tile_a.x+cy, tile_a.y+cx).get_ground_tile() tile_a_slope = [12, 13, 1, 4] tile_a_slope_WE = [39, 27, 36, 31] tile_a_slope_NS = [9] } else { // x/y build way < exists way tile_a1 = square_x(tiles_build[i].x-cy, tiles_build[i].y-cx).get_ground_tile() tile_b1 = square_x(tile_a.x-cy, tile_a.y-cx).get_ground_tile() tile_a_slope = [9, 12, 13, 1, 4, 39] tile_a_slope_WE = [39, 27, 36] tile_a_slope_NS = [31] } if ( print_message_box == 21 ) { gui.add_message_at(b_player, " tile_a.get_slope() " + tile_a.get_slope(), tile_a) gui.add_message_at(b_player, " tile_a1.get_slope() " + tile_a1.get_slope(), tile_a1) gui.add_message_at(b_player, " tile_b1.get_slope() " + tile_b1.get_slope(), tile_b1) gui.add_message_at(b_player, " build_side " + build_side, tiles_build[i]) } if ( tile_build_slope.find(build_hight.get_slope()) != null && tile_a_slope.find(tile_a.get_slope()) != null && test_tile_is_empty(tile_a1) && test_tile_is_empty(tile_b1) ) { //gui.add_message_at(b_player, " #6414# --", world.get_time()) if ( straight_slope == true && build_side == 0 ) { err = command_x.grid_raise(our_player, coord3d(tile_a.x, tile_a.y, tile_a.z)) } else if ( straight_slope == true && build_side == 1 ) { err = command_x.grid_raise(our_player, coord3d(tile_a1.x, tile_a1.y, tile_a1.z)) } else { err = command_x.grid_lower(our_player, coord3d(tile_b1.x, tile_b1.y, tile_b1.z)) } if ( err != null ) { gui.add_message_at(b_player, " #3047# err " + err, world.get_time()) } else { terraform_tile = 0 } } else if ( tile_build_slope.find(build_hight.get_slope()) != null && tile_a_slope_NS.find(tile_a.get_slope()) != null ) { //gui.add_message_at(b_player, " #6429# ", world.get_time()) if ( test_tile_is_empty(tile_a1) && test_tile_is_empty(tile_b1) ) { //gui.add_message_at(b_player, " #3065# straight_slope " + straight_slope + " -- build_side " + build_side, world.get_time()) if ( straight_slope == true && build_side == 0 ) { err = command_x.grid_lower(our_player, coord3d(tile_a.x, tile_a.y, tile_a.z)) } else if ( straight_slope == true && build_side == 1 ) { gui.add_message_at(b_player, " #3069# " + coord3d_to_string(tile_b1), world.get_time()) err = command_x.grid_lower(our_player, coord3d(tile_b1.x, tile_b1.y, tile_b1.z)) } else { err = command_x.grid_lower(our_player, coord3d(build_hight.x, build_hight.y, build_hight.z)) } if ( err != null ) { gui.add_message_at(b_player, " #3076# err " + err, world.get_time()) } else { terraform_tile = 0 } } } else if ( tile_build_slope.find(build_hight.get_slope()) != null && tile_a_slope_WE.find(tile_a.get_slope()) != null ) { //gui.add_message_at(b_player, " #6447# ", world.get_time()) if ( test_tile_is_empty(tile_a1) && test_tile_is_empty(tile_b1) ) { if ( straight_slope == true || build_side == 0 ) { err = command_x.grid_lower(our_player, coord3d(tile_a.x, tile_a.y, tile_a.z)) } else if ( straight_slope == true || build_side == 1 ) { err = command_x.grid_raise(our_player, coord3d(tile_b1.x, tile_b1.y, tile_b1.z)) } else { err = command_x.grid_lower(our_player, coord3d(tile_b1.x, tile_b1.y, tile_b1.z)) } if ( err != null ) { gui.add_message_at(b_player, " #3070# err " + err, world.get_time()) } else { terraform_tile = 0 } } } else if ( tile_build_slope.find(build_hight.get_slope()) != null && tile_a_slope_WE.find(tile_a1.get_slope()) != null ) { //gui.add_message_at(b_player, " #6463# ", world.get_time()) if ( test_tile_is_empty(tile_a1) && test_tile_is_empty(build_hight) ) { if ( straight_slope == false || build_side == 1 ) { err = command_x.grid_lower(our_player, coord3d(tile_a1.x, tile_a1.y, tile_a1.z)) }/* else if ( straight_slope == true || build_side == 1 ) { err = command_x.grid_raise(our_player, coord3d(tile_b1.x, tile_b1.y, tile_b1.z)) } else { err = command_x.grid_lower(our_player, coord3d(tile_b1.x, tile_b1.y, tile_b1.z)) } if ( err == null ) { terraform_tile = 0 } else { gui.add_message_at(b_player, " #3070# err " + err, world.get_time()) }*/ if ( err != null ) { //gui.add_message_at(b_player, " #6475# err " + err, world.get_time()) } else { terraform_tile = 0 } } } } // double hight 8, 24, 56, 72 // terraform tile //gui.add_message_at(b_player, " terraform_tile " + terraform_tile + " - !straight_slope " + !straight_slope + " - build_hight.get_slope() " + build_hight.get_slope(), build_hight) if ( terraform_tile == 1 || (!straight_slope && build_hight.get_slope() > 0) ) { //&& ref_hight.z < build_hight.z if ( print_message_box == 21 ) { gui.add_message_at(b_player, "#6492# terraform down ", build_hight) } do { err = command_x.set_slope(b_player, build_hight, 83 ) if ( err != null ) { gui.add_message_at(b_player, "#6498# Error terraform down: " + err, build_hight) break } z = square_x(tiles_build[i].x, tiles_build[i].y).get_ground_tile() } while(z.z > ref_hight.z ) // replace water to land z = square_x(tiles_build[i].x, tiles_build[i].y).get_ground_tile() if ( z.get_slope() == 0 && z.is_water() ) { command_x.change_climate_at(our_player, z, cl_temperate) } } } } if ( err != null ) { //gui.add_message_at(b_player, "#6510# Error terraform : " + err, build_hight) if ( i == (tiles_build.len()-1) ) { if ( tr == way_len ) { tr-- } if ( tl == way_len ) { tl-- } way_len-- } //return false } else { build_hight = square_x(tiles_build[i].x, tiles_build[i].y).get_ground_tile() } } if ( ref_hight.z == build_hight.z && ref_hight.get_slope() == build_hight.get_slope() ) { if ( print_message_box == 21 ) { gui.add_message_at(b_player, "#3135# ---=> slope tiles == tiles_build * tiles.z == tiles_build.z ", world.get_time()) } } else if ( build_hight.is_empty() && straight_slope == true ) { // set slope ramp if ( i < (tiles_build.len()-1) ) { if ( print_message_box == 21 ) { gui.add_message_at(b_player, " ---=> terraform slope ramp", world.get_time()) gui.add_message_at(b_player, " ---=> tiles_build.z " + build_hight.z + " tiles.z " + ref_hight.z, world.get_time()) } err = command_x.set_slope(b_player, build_hight, ref_hight.get_slope()) if ( err != null ) { gui.add_message_at(b_player, " ERROR " + err, world.get_time()) err = null } } else { if ( print_message_box == 21 ) { gui.add_message_at(b_player, " ---=> last tile slope ramp", world.get_time()) } //tiles_build.remove(tiles_build.len()-1) //tiles.remove(tiles.len()-1) if ( tr == way_len ) { tr-- } if ( tl == way_len ) { tl-- } way_len-- } } } if ( print_message_box == 21 ) { gui.add_message_at(b_player, "output: tr " + tr + " - tl " + tl + " - way_len " + way_len, world.get_time()) } local output = [] output.append(tr) output.append(tl) output.append(way_len) return output } /* * search my_line_t from line_x * * */ function select_linkline(s_line) { foreach(link in link_list) { foreach(index, line in link.lines) { if ( line.is_valid() ) { //local s_line_name = s_line.get_name() //local line_name = line.get_name() if ( s_line.get_name() == line.get_name() ) { return line } } } } } simutrans-124.5/simutrans/ai/sqai_rail/basic.nut000066400000000000000000000253111520675460500217610ustar00rootroot00000000000000/** * Classes for basic nodes in the tree, which represents our brain :) */ // return codes from step // enum return_code { const RT_DONE_NOTHING = 1 // Done nothing. const RT_PARTIAL_SUCCESS = 2 // Done something, want to continue next const RT_SUCCESS = 4 // Done something. const RT_ERROR = 8 // Some error occured. const RT_KILL_ME = 16 // Can be destroyed by parent. const RT_TOTAL_SUCCESS = 20 // RT_SUCCESS | RT_KILL_ME const RT_TOTAL_FAIL = 24 // RT_ERROR | RT_KILL_ME const RT_READY = 29 // RT_DONE_NOTHING | RT_SUCCESS | RT_ERROR | RT_KILL_ME // } /** * Class use as return value from the function step() * Signals success, contains additional data. */ class r_t { // return code composed of return_code constants above code = 0 // reports have to be handled report = null // run node immediately node = null constructor(c, r=null, n=null) { code = c; report = r; node = n } function is_ready() { return (code & RT_READY);} function can_be_deleted() { return (code & RT_KILL_ME); } function has_failed() { return (code & RT_ERROR);} } /** * Basic node class. */ class node_t { name = null debug = false /** * Main method of these nodes. Called from the global ::step() method. */ function step() { return r_t(RT_TOTAL_FAIL)} constructor(n = "node_t") { name = n } function dbgprint(t) { if (debug) print(name + ": " + t) } function _save() { return ::saveinstance(name, this) } } /** * Has array of nodes that will be executed / stepped sequentially. */ class node_seq_t extends node_t { nodes = null next_to_step = 0 constructor(n = "node_seq_t") { base.constructor(n) nodes = [] } function append_child(c) { nodes.append(c) } /// @returns value of type r_t function step() { if (nodes.len() == 0) return r_t(RT_SUCCESS); if (next_to_step >= nodes.len()) { next_to_step = 0; return r_t(RT_DONE_NOTHING); } local ret = nodes[next_to_step].step() if ( ret == null ) { gui.add_message_at(our_player, "####### nodes[next_to_step].step() is null; nodes.len() = " + nodes.len() + " # next_to_step " + next_to_step, world.get_time()) return r_t(RT_ERROR); } // take nodes report if (ret.report) { append_report(ret.report) } // delete child if ready if (ret.can_be_deleted()) { nodes.remove(next_to_step); } else if (ret.is_ready()) { next_to_step ++ } // successor node if (ret.node) { append_child(ret.node) } // our return code local rc = RT_PARTIAL_SUCCESS // want next call, too if (ret.has_failed()) { rc = RT_ERROR // propagate error } else if (next_to_step > nodes.len()) { rc = RT_SUCCESS // done with stepping all nodes build_check_month = world.get_time() + world.get_time().ticks_per_month } return r_t(rc) } } /** * Base class to report financial aspects of plans. */ class report_t { // tree that will be executed action = null // costs cost_fix = 0 cost_monthly = 0 // expected gain per month gain_per_m = 0 // distance = 0 points = 0 // retire plan retire_time = null // retire objects retire_obj = null function merge_report(r) { if (r.action) { action.append_child( r.action ) } cost_fix += r.cost_fix cost_monthly += r.cost_monthly gain_per_m += r.gain_per_m distance += r.distance points += r.points } function _save() { return ::saveinstance("report_t", this) } } /** * Manager class. * Steps its child nodes, otherwise does some work(), generates reports. */ class manager_t extends node_seq_t { reports = null constructor(n = "manager_t") { base.constructor(n) reports = [] } function step() { month_check_message() if ( build_check_month > world.get_time().ticks ) { // not plan link //if (debug) gui.add_message_at(our_player, " not plan link : build_check_month = " + build_check_month, world.get_time()) //if (debug) gui.add_message_at(our_player, " for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y, world.get_time()) //return RT_DONE_NOTHING } dbgprint("stepping a child") local r = base.step() if (r.has_failed()) { if ("error_handler" in this) { return error_handler() } } if (r.code == RT_DONE_NOTHING || r.code == RT_SUCCESS) { // all nodes were stepped dbgprint("doing some work") return work() } dbgprint("stepped a child") return r } function work() { /*if ( month_count ) { month_count = false month_count_ticks = world.get_time().next_month_ticks if ( our_player.is_valid() && our_player.get_type() == 4 ) { local operating_profit = player_x(our_player.nr).get_operating_profit() local net_wealth = player_x(our_player.nr).get_net_wealth() local message_text = format(translate("%s - last month: operating profit %d net wealth %d"), our_player.get_name(), operating_profit[1], net_wealth[1]) gui.add_message_at(our_player, message_text, world.get_time()) if ( operating_profit[1] < 0 ) { build_check_month = world.get_time().month + 1 if ( build_check_month > 11 ) { build_check_month = build_check_month - 11 } } local yt = world.get_time().year.tostring() // check all 5 years ( year xxx0 and xxx5 ) if ( world.get_time().month == 3 ) { #(yt.slice(-1) == "0" || yt.slice(-1) == "5") && // in april industry_manager.check_pl_lines() } // check all 5 years ( year xxx2 and xxx7 ) if ( (yt.slice(-1) == "2" || yt.slice(-1) == "7") && world.get_time().month == 4 ) { // in may // check unused halts check_stations_connections() } } } else if ( world.get_time().ticks > month_count_ticks ) { month_count = true } set_map_vehicles_counts()*/ month_check_message() } function append_report(r) { reports.append(r); } /** * Returns the best report available. */ function get_report() { if (reports.len() == 0) return null // find report that maximizes gain_per_m / cost_fix local i = -1 local best = null // [0] = cost_fix / 13 // [1] = gain_per_m * ( cost_fix / 13 ) * points local p_test = [0, 0] local p_best = [0, 0] for(local j=0; j month_count_ticks ) { month_count = true } set_map_vehicles_counts() } /* * * */ function build_check_time(build_cost) { local operating_profit = player_x(our_player.nr).get_operating_profit() local net_wealth = player_x(our_player.nr).get_current_net_wealth()/100 local cash = player_x(our_player.nr).get_current_cash() local maintenance = player_x(our_player.nr).get_current_maintenance()/100 local k = build_cost if ( cash > 0 ) { k -= cash + (net_wealth / 2) } //gui.add_message_at(our_player, "### k -= cash " + k, world.get_time()) local t = 0 if ( operating_profit[1] > 0 ) { t = abs(k / operating_profit[1]).tointeger() } else { t = 12 } //gui.add_message_at(our_player, "### build_cost " + build_cost, world.get_time()) //gui.add_message_at(our_player, "### operating_profit[1] " + operating_profit[1], world.get_time()) //gui.add_message_at(our_player, "### build_check_time(build_cost) " + t, world.get_time()) if ( t > 24 ) { t = 24 } return t } simutrans-124.5/simutrans/ai/sqai_rail/combined_connections.nut000066400000000000000000000461111520675460500250630ustar00rootroot00000000000000class amphibious_connection_planner_t extends industry_connection_planner_t { constructor(s,d,f) { base.constructor(s,d,f); name = "amphibious_connection_planner_t" } // print messages box // 1 = // 2 = // 3 = print_message_box_x = 0 function step() { local wt = null print("amphibious_connection_planner_t " + this) if ( print_message_box_x > 1 ) { gui.add_message_at(our_player, " ** amphibious_connection_planner_t " + this, world.get_time()) } // compute monthly production if (prod < 0) { prod = calc_production() } dbgprint("production = " + prod); // set link build cost 0 industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 0) // road - initial local rprt_road = plan_simple_connection(wt_road, null, null) if (rprt_road == null) { //return failed() } // rail - initial local rprt_rail = plan_simple_connection(wt_rail, null, null) if (rprt_rail == null) { //return failed() } // water - initial local rprt_water = plan_simple_connection(wt_water, null, null) if (rprt_water == null && ( rprt_road == null || rprt_rail == null )) { return failed() } if ( print_message_box_x == 1 ) { gui.add_message_at(our_player, " **** rprt_water - gain_per_m : " + rprt_water.gain_per_m + " # distance : " + rprt_water.distance, world.get_time()) gui.add_message_at(our_player, " **** rprt_rail - gain_per_m : " + rprt_rail.gain_per_m + " # distance : " + rprt_rail.distance, world.get_time()) gui.add_message_at(our_player, " **** rprt_road - gain_per_m : " + rprt_road.gain_per_m + " # distance : " + rprt_road.distance, world.get_time()) } if (rprt_water == null ) { return failed() //rprt_water = report_t() } // find flat harbour building local station_list = building_desc_x.get_available_stations(building_desc_x.flat_harbour, wt_water, good_desc_x(freight)) rprt_water.action.planned_harbour_flat = industry_connection_planner_t.select_station(station_list, 1, rprt_water.action.planned_convoy.capacity) local build_cost_lines = industry_manager.get_link_build_cost(fsrc, fdest, freight, 0) // find amphibious path local marine = null if ( rprt_rail != null && ( rprt_road==null || rprt_rail.gain_per_m > rprt_road.gain_per_m ) ) { marine = amphibious_pathfinder_t(rprt_rail.action.planned_way, rprt_water.action.planned_station, rprt_water.action.planned_harbour_flat) build_cost_lines += rprt_rail.cost_fix industry_manager.set_link_build_cost(fsrc, fdest, freight, build_cost_lines, 0) local a = industry_manager.get_combined_link(fsrc, fdest, freight) + 1 industry_manager.set_combined_link(fsrc, fdest, freight, a) wt = wt_rail if ( print_message_box_x == 1 ) { gui.add_message_at(our_player, " ---> rprt_rail ", world.get_time()) } } else if ( rprt_road != null ) { marine = amphibious_pathfinder_t(rprt_road.action.planned_way, rprt_water.action.planned_station, rprt_water.action.planned_harbour_flat) build_cost_lines += rprt_road.cost_fix industry_manager.set_link_build_cost(fsrc, fdest, freight, build_cost_lines, 0) local a = industry_manager.get_combined_link(fsrc, fdest, freight) + 1 industry_manager.set_combined_link(fsrc, fdest, freight, a) wt = wt_road if ( print_message_box_x == 1 ) { gui.add_message_at(our_player, " ---> rprt_road ", world.get_time()) } } if (marine == null) { industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 0) industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 1) industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 2) industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 3) industry_manager.set_combined_link(fsrc, fdest, freight, 0) return r_t(RT_TOTAL_FAIL) } else { local a = industry_manager.get_combined_link(fsrc, fdest, freight) + 1 industry_manager.set_combined_link(fsrc, fdest, freight, a) } // check build cost /*if ( wt == wt_rail && rprt_rail.cost_fix > our_player.get_current_cash() ) { return r_t(RT_TOTAL_FAIL) } else if ( wt == wt_road && rprt_road.cost_fix > our_player.get_current_cash() ) { return r_t(RT_TOTAL_FAIL) }*/ if ( wt == wt_rail ) { } else if ( wt == wt_road ) { } //gui.add_message_at(our_player, " ---> link " + fsrc + " " + fsrc.get_name() + " - " + fdest.get_name(), world.get_time()) //gui.add_message_at(our_player, " ---> build cost lines = " + build_cost_lines + " # industry_manager.get_combined_link(fsrc, fdest, freight) = " + industry_manager.get_combined_link(fsrc, fdest, freight), world.get_time()) local build_cash = player_x(our_player.nr).get_current_cash() + (((player_x(our_player.nr).get_current_net_wealth()/100) - player_x(our_player.nr).get_current_cash())/2) if ( build_cost_lines > build_cash ) { //industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 0) //industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 1) //industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 2) //industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 3) //industry_manager.set_combined_link(fsrc, fdest, freight, 0) build_check_month = world.get_time().ticks + (build_check_time(build_cost_lines) * world.get_time().ticks_per_month) return r_t(RT_TOTAL_FAIL) } marine.search_route(fsrc,fdest) local route = marine.route if (route.len() == 0) { return r_t(RT_TOTAL_FAIL) } // generate report local report = report_t() report.action = node_seq_t() if ( print_message_box_x == 1 ) { gui.add_message_at(our_player, " ---> marine.route.len(fsrc,fdest) " + route.len(), world.get_time()) gui.add_message_at(our_player, " ---> route[0] " + coord3d_to_string(route[0]) + " - route[route.len()-1] " + coord3d_to_string(route[route.len()-1]), world.get_time()) } // now loop through route backwards local i = route.len()-1; local from = tile_x(route[i].x, route[i].y, route[i].z) local on_water = ::finder._tile_water_way(from) local from_i = on_water ? i : i+1; i-- for(; i>=0; i--) { local to = tile_x(route[i].x, route[i].y, route[i].z) local to_water = ::finder._tile_water_way(to) local change = to_water != on_water if (change || i==0) { print("-- Plan arc from " + coord_to_string(from) + " to " + coord_to_string(route[i])) if ( print_message_box_x == 1 ) { gui.add_message_at(our_player, " **-- Plan arc from " + coord_to_string(from) + " to " + coord_to_string(route[i]), world.get_time()) } // change between land and sea local r = null if (on_water) { // from_i = first water tile // i = first land tile (or i==0) // plan between water tiles r = plan_simple_connection(wt_water, from_i < route.len()-1 ? from : null, change ? route[i+1] : null, from_i - (i+1)) // set harbour positions if (r) { local shipc = r.action if (from_i < route.len()-1) { local c = route[from_i+1] shipc.c_harbour_tiles[ coord3d_to_key(from) ] <- tile_x(c.x, c.y, c.z) } if (change) { shipc.c_harbour_tiles[ coord3d_to_key(route[i+1]) ] <- to } } } else { // from_i = first land tile, which is harbour slope // i = first water tile print("Try to catch index error") if (i+2 < route.len()) { r = plan_simple_connection(wt, route[from_i-1], change ? route[i+2] : null, from_i-1 - (i+2)) } else { // else: first tile of route but second already in water - no need to plan road r = report_t() } } if (r) { if (r.action) { r.action.finalize = !change } report.merge_report(r) } else { return failed() } from = to from_i = i on_water = to_water } } return r_t(RT_TOTAL_SUCCESS, report) } function failed() { //industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) return r_t(RT_TOTAL_FAIL) } } class amphibious_pathfinder_t extends astar { way = 0 builder = null planned_harbour = null planned_harbour_flat = null planned_harbour_len = 0 planned_harbour_flat_len = 0 cost_harbour = 333 cost_road_stop = 17 c_harbour_tiles = null // print messages box // 1 print_message_box_x = 0 constructor(way_, harbour_, harbour_flat_) { base.constructor() way = way_ builder = way_planner_x(our_player) builder.set_build_types(way) planned_harbour = harbour_ planned_harbour_flat = harbour_flat_ local size = planned_harbour.get_size(0) planned_harbour_len = size.x*size.y if (planned_harbour_flat) { local size = planned_harbour_flat.get_size(0) planned_harbour_flat_len = size.x*size.y } } function process_node(cnode) { local from = tile_x(cnode.x, cnode.y, cnode.z) local back = dir.backward(cnode.dir) local from_water = ::finder._tile_water_way(from) local water_dir = from.get_way_dirs(wt_water) // local message = [0, 0, 0] // flags // 0x0f -> jps // 0x10 -> find flat place // 0x20 -> flat place (mark next step with 0x40) // 0x40 -> cannot go into water if (cnode.flag == 0x10) { process_node_to_land(cnode, from) return } local test_dir = cnode.previous ? (back ^ 0x0f) & (cnode.flag & 0x0f) : 0x0f for(local d = 1; d<16; d*=2) { // do not go backwards if (( d &test_dir) == 0 ) { continue } local to = from.get_neighbour(wt_all, d) if (to && !is_closed(to)) { local to_water = ::finder._tile_water_way(to) // water -> water // land -> land if (from_water == to_water) { // can we build way here? if (!from_water) { // empty space for station near a halt with harbour local halt = to.get_halt() if (halt && halt.get_owner().nr == our_player_nr && from.is_empty()) { // try to connect to this halt and to use its harbours process_node_to_land_halt(cnode, halt) } // can we build way here? if (!builder.is_allowed_step(from, to)) { // could be a halt on the other tile that has harbour that could be used continue; } } // estimate moving cost local move = cnode.is_straight_move(d) ? cost_straight : cost_curve local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist // use jump-point search (see dataobj/route.cc) local jps = to_water && (cnode.previous) ? (water_dir ^ 0x0f) | d | cnode.dir | from.get_canal_ribi() : 0x0f if (cnode.flag & 0x20) jps = jps | 0x40; local node = ab_node(to, cnode, cost, dist, d, jps) add_to_open(node, weight) message[0] = 1 } else if (from_water) { // water -> land if (from.is_water() && to.get_halt()) { // check for existing harbour local halt = ship_connector_t.get_harbour_halt(to) if (halt) { // create node as end node of water route local harbour_node = ab_node(to, cnode, cnode.cost, cnode.dist, 0x0f, 0x0f) // now look for empty spots to connect to this halt process_node_to_harbour(harbour_node, halt) } } // we have to build ourselves if (!from.is_water() || !can_place_harbour(to, d) ) { continue } local move = cost_harbour local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, d, 0x10) add_to_open(node, weight) message[1] = 1 } else { // land -> water if (!to.is_water() || !can_place_harbour(from, dir.backward(d)) || (cnode.flag & 0x60) ) { continue } if (cnode.previous) { local fromfrom = tile_x(cnode.previous.x, cnode.previous.y, cnode.previous.z) // want to build station here with one connecting road if (fromfrom == null || !fromfrom.is_empty() || fromfrom.get_slope()!=0 || cnode.previous.previous == null) { continue } } local move = cost_harbour local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, d, 0x0f) add_to_open(node, weight) message[2] = 1 } } } /* if ( print_message_box_x == 1 ) { if ( message[0] == 1 ) { gui.add_message_at(our_player, "---- water -> water", world.get_time()) gui.add_message_at(our_player, "---- found", world.get_time()) } if ( message[1] == 1 ) { gui.add_message_at(our_player, "---- water -> land", world.get_time()) gui.add_message_at(our_player, "---- found", world.get_time()) } if ( message[2] == 1 ) { gui.add_message_at(our_player, "---- land -> water", world.get_time()) gui.add_message_at(our_player, "---- found", world.get_time()) } } */ } function can_place_harbour(tile, d_water_to_land) { return ship_connector_t.get_harbour_type_for_tile(tile, planned_harbour, planned_harbour_flat, d_water_to_land) > 0 } function can_place_harbour(tile, d_water_to_land) { return ship_connector_t.get_harbour_type_for_tile(tile, planned_harbour, planned_harbour_flat, d_water_to_land) > 0 } function process_node_to_land(cnode, from) { local pos = coord(cnode.x, cnode.y) for(local d = 1; d<16; d++) { // test all 8 neighbouring tiles if (!dir.is_threeway(d)) { local c = pos + dir.to_coord(d) try { local to = square_x(c.x, c.y).get_ground_tile() if (to && to.is_empty() && to.get_slope()==0) { // can place station here local move = cost_road_stop local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, 0x0f, 0x0f) add_to_open(node, weight) } } catch(ev) {} } } } function process_node_to_harbour(cnode, halt) { local visited = {} // process all tiles of this halt foreach(tile in halt.get_tile_list()) { // check all neighbouring tiles for empty spots for(local d = 1; d<16; d*=2) { local c = coord(tile.x, tile.y) + dir.to_coord(d) try { local to = square_x(c.x, c.y).get_ground_tile() if (to && to.is_empty() && to.get_slope()==0 && !(coord3d_to_key(to) in visited) ) { // can place station here local move = cost_road_stop local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, cnode, cost, dist, 0x0f, 0x0f) add_to_open(node, weight) // mark as visited visited[ coord3d_to_key(to) ] <- 1 } } catch(ev) {} } } } /** * Does steps land-tile -> halt -> harbour -> water * Last route tile is cnode, check if station can be placed. * Check for harbour tiles, put corresponding nodes to open list. */ function process_node_to_land_halt(cnode, halt) { // process all tiles of this halt foreach(tile in halt.get_tile_list()) { if (!tile.is_water() && ship_connector_t.get_harbour_halt(tile)!=null) { local harbour_node = ab_node(tile, cnode, cnode.cost, cnode.dist, 0x0f, 0x0f) local d = 0 if (tile.get_slope()) { // get direction from slope d = dir.backward(slope.to_dir(tile.get_slope())) } else { // flat dock, check all four neighbour tiles for water + harbour d = 1 } do { local to = coord3d(tile.x, tile.y, tile.z) + dir.to_coord(d) if (world.is_coord_valid(to)) { local move = cost_road_stop local dist = estimate_distance(to) local cost = cnode.cost + move local weight = cost + dist local node = ab_node(to, harbour_node, cost, dist, d, 0x0f) add_to_open(node, weight) } d = d*2 } while (tile.get_slope()==0 && d<16) } } } function search_route(fsrc, fdest) { print("Search amphibious connection") if ( print_message_box_x > 0 ) { local fs = fsrc.get_tile_list() local fd = fdest.get_tile_list() gui.add_message_at(our_player, "____________ Search amphibious connection ___________", world.get_time()) gui.add_message_at(our_player, " line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ")", world.get_time()) } // find station places local s = [] local e = [] c_harbour_tiles = {} s.append(::finder.find_station_place(fsrc, fdest) ) e.append(::finder.find_station_place(fdest, fsrc) ) s.append(::ship_connector_t.find_anchorage(fsrc, planned_harbour, planned_harbour_flat, c_harbour_tiles) ) e.append(::ship_connector_t.find_anchorage(fdest, planned_harbour, planned_harbour_flat, c_harbour_tiles) ) // init search prepare_search() for(local i=0; i<2; i++) { foreach (e in e[i]) { targets.append(e); } } if (targets.len() == 0) { route = [] return } compute_bounding_box() for(local i=0; i<2; i++) { foreach (s in s[i]) { local dist = estimate_distance(s) add_to_open(ab_node(s, null, 1, dist+1, dist, 0), dist+1) } } search() print("End amphibious route search") if ( print_message_box_x > 0 ) { gui.add_message_at(our_player, "------------ End amphibious route search -------------", world.get_time()) } } } simutrans-124.5/simutrans/ai/sqai_rail/factorysearcher.nut000066400000000000000000000626471520675460500241010ustar00rootroot00000000000000/** * Class that searches for usefull unbuild factory connections. * Depending on player number uses two different methods: * 1) Method that tries to complete a factory tree (taken from the c++ implementation of the freight AI in player/ai_goods.cc * 2) A demand-driven method. */ class factorysearcher_t extends manager_t { froot = null // factory_x, complete this tree method = -1 factory_iterator = null factory_list = null constructor() { base.constructor("factorysearcher_t") debug = false ::factorysearcher = this } function get_next_end_consumer() { // iterate the factory_iterator, which is a generator if (factory_iterator == null || typeof(factory_iterator) != "generator") { // this is a generator factory_iterator = factory_iteration() } if (factory_iterator.getstatus() != "dead") { return resume factory_iterator } factory_iterator = null return null } function factory_iteration() { factory_list = [] // copy list of end-consumers foreach(factory in factory_list_x()) { if (factory.output.len() == 0) { factory_list.append(factory) } } // shuffle for(local i=0; i 0) && (n < min_mfc)) { // TODO add some random here min_mfc = n froot = fab } } if (froot) { local fab = froot dbgprint("Choose consumer " + fab.get_name() + " at " + fab.x + "," + fab.y + ", which has " + min_mfc + " missing links") } } // nothing found?? if (froot==null) return r_t(RT_DONE_NOTHING); dbgprint("Connect " + froot.get_name() + " at " + froot.x + "," + froot.y) // find link to connect if (!plan_missing_link(froot)) { dbgprint(".. no missing link") // no missing link found - reset froot froot = null return r_t(RT_SUCCESS) } return r_t(RT_PARTIAL_SUCCESS) } else { // demand-driven method if (froot == null) { froot = get_next_end_consumer() } if (froot) { local n = plan_increase_consumption(froot) if (n==0 && count_missing_factories(froot) <= 0) { froot = null } return r_t( n>0 ? RT_PARTIAL_SUCCESS : RT_SUCCESS) } } return r_t(RT_SUCCESS) } /** * Creates the planner node */ static function plan_connection(fsrc, fdest, freight) { if (industry_manager.get_link_state(fsrc, fdest, freight) != industry_link_t.st_free) { dbgprint("Link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y + " state is " + industry_manager.get_link_state(fsrc, fdest, freight) ); return false } dbgprint("Close link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y) industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_planned); local icp = industry_connection_planner_t(fsrc, fdest, freight); append_child(icp) return true } /** * @returns -1 if factory tree is incomplete, otherwise number of missing connections */ // TODO cache the results per factory static function count_missing_factories(fab, indent = "") { // source of raw material? if (fab.input.len() == 0) return 0; local end_consumer = fab.output.len() == 0 // build list of supplying factories local suppliers = []; foreach(c in fab.get_suppliers()) { suppliers.append( factory_x(c.x, c.y) ); } local count = 0; local g_atleastone = false; // iterate all input goods and search for supply foreach(good, islot in fab.input) { // test for in-storage or in-transit goods local st = islot.get_storage() local it = islot.get_in_transit() //gui.add_message_at(our_player, "### " + fab.get_name() + " ## " + good + " ## get_storage() " + st[0] + " get_in_transit() " + it[0], world.get_time()) if (st[0] + st[1] + it[0] + it[1] > 0) { // something stored/in-transit in last and current month // no need to search for more supply g_atleastone = true continue } // there is a complete tree to produce this good local g_complete = false; // minimum of missing links for one input good local g_count = 10000; foreach(s in suppliers) { if (good in s.output) { // check state of connection local state = industry_manager.get_link_state(s, fab, good); if (state == industry_link_t.st_failed) { continue // foreach } if (state != industry_link_t.st_free) { // planned / built -> nothing missing g_complete = true g_count = 0 continue } local n = count_missing_factories(s, indent + " "); if ( n<0) { // incomplete tree } else { // complete tree g_complete = true; g_count = min(g_count, n+1) } } } if (!g_complete && !end_consumer) { dbgprint(indent + "No supply of " + good + " for " + fab.get_name()) //gui.add_message_at(our_player, " No supply of " + good + " for " + fab.get_name(), world.get_time()) // no suppliers for this good return -1 } g_atleastone = g_atleastone || g_complete if (!end_consumer) { count += g_count // sum missing links } else { if (g_count > 0 && (count == 0 || g_count < count)) { count = g_count; } } dbgprint(indent + "Supply of " + good + " for " + fab.get_name() + " has " + g_count + " missing links") if ( g_count > 0 ) { //gui.add_message_at(our_player, " Supply of " + good + " for " + fab.get_name() + " has " + g_count + " missing links", world.get_time()) } } if (end_consumer && !g_atleastone) { dbgprint(indent + "No supply for " + fab.get_name()) count = -1 } dbgprint(indent + "Factory " + fab.get_name() + " at " + fab.x + "," + fab.y + " has " + count + " missing links") return count } /** * find link to connect in tree of factory @p fab. * sets fsrc, fdest, lgood if true was returned * @returns true if link is found */ function plan_missing_link(fab, indent = "") { dbgprint(indent + "Missing link for factory " + fab.get_name() + " at " + fab.x + "," + fab.y) // source of raw material? if (fab.input.len() == 0) return false; // build list of supplying factories local suppliers = []; foreach(c in fab.get_suppliers()) { suppliers.append( factory_x(c.x, c.y) ); } local count = 0; // iterate all input goods and search for supply foreach(good, islot in fab.input) { // check for current supply if ( 4*(islot.get_storage()[0] + islot.get_in_transit()[0]) > islot.max_storage) { dbgprint(indent + ".. enough supply of " + good) continue } // find suitable supplier foreach(s in suppliers) { if ( !(good in s.output)) continue; // connection forbidden? planned? built? local state = industry_manager.get_link_state(s, fab, good) if (state != industry_link_t.st_free) { if (state == industry_link_t.st_built || state == industry_link_t.st_planned) { dbgprint(indent + ".. connection for " + good + " from " + s.get_name() + " to " + fab.get_name() + " already " + (state == industry_link_t.st_built ? "built" : "planned") ) break } continue // if connection state is 'failed' } local oslot = s.output[good] dbgprint(indent + ".. Factory " + s.get_name() + " at " + s.x + "," + s.y + " supplies " + good) if (8*oslot.get_storage()[0] > oslot.max_storage || !plan_missing_link(s, indent + " ")) { // this is our link dbgprint(indent + ".. plan this connection") plan_connection(s, fab, good) } return true } } return false // all links are connected } /** * Estimates additional possible consumption at this end-consumer factory */ function plan_increase_consumption(fab, indent = "") { // initialize search if (fab.output.len() > 0) { return 0 } local planned = 0; foreach(good, islot in fab.input) { local tree = estimate_consumption(fab, good) // now do some greedy selection: for each producer select enough suppliers planned += plan_consumption_connection(tree, fab, good) } return planned } function plan_consumption_connection(tree, fdest, freight, indent = "") { local planned = 0; local needed = tree.increase while (needed > 0) { local best = null local best_supply = 0 foreach(supplier in tree.suppliers) { local supply = supplier.supply dbgprint(indent + "Needed " + needed + " Provided " + supply ) if (supply > needed ? (best_supply == 0 || supply < best_supply) : supply > best_supply) { best = supplier best_supply = supply } } // go down in tree foreach(good, supplier_slot in best.inputs) { planned += plan_consumption_connection(supplier_slot, best.supplier, good, indent + " ") dbgprint(indent + "Planned for " + best_supply + " (total = " + planned + ")") } // plan this connection if (planned==0) { if (plan_connection(best.supplier, fdest, freight)) { dbgprint(indent + "Planned to consumer ") planned++ } } // disable this tree needed -= best_supply best.supply = 0 } return planned } /** * Estimates additional possible consumption of good at this factory * * Returns tree: * tree.increase - Potential local consumption * tree.supply - Potential supply * tree.basec - Base consumption * tree.suppliers - Array of supplier nodes * [].supplier - Factory * [].supply - Potential production of supplier } - produced by estimate_production * [].inputs - Table Good -> Consumer tree } * * @returns estimated consumption increase */ static function estimate_consumption(fab, prod = null, indent = "") { dbgprint(indent + "Estimates for consumption of " + prod + " at factory " + fab.get_name() + " at " + fab.x + "," + fab.y) // estimate max consumption local islot = fab.input[prod] local max_c = islot.get_base_consumption() // estimate actual consumpion local est_c = estimate_actual_consumption(islot) local increase = max_c - est_c dbgprint(indent + " potential increase of consumption of " + prod + " is " + increase) // iterate suppliers: // calculate potential additional supply (and build it) local tree = { suppliers = [], basec = max_c } // search for supply local supply = 0 foreach(c in fab.get_suppliers()) { local s = factory_x(c.x, c.y) if (prod in s.output) { local state = industry_manager.get_link_state(s, fab, prod) if (state == industry_link_t.st_planned || state == industry_link_t.st_failed) { // failed / planned -> no improvement possible dbgprint(indent + " transport link for " + prod + " is failed/planned" ) continue } local s_tree = estimate_production(s, prod, state == industry_link_t.st_built, indent + " ") s_tree.supplier <- s local more = s_tree.supply supply += more tree.suppliers.append(s_tree) dbgprint(indent + " potential increase of " + prod + " is " + more + " (state =" + state + ")") } } dbgprint(indent + " total additional supply of " + prod + " is " + supply) tree.increase <- min(increase, supply) tree.supply <- supply return tree } /** * Estimates additional possible production of good at this factory * @returns tree, see estimate_consumption */ static function estimate_production(fab, prod, exists, indent = "") { dbgprint(indent + "Estimates for production of " + prod + " at factory " + fab.get_name() + " at " + fab.x + "," + fab.y) local oslot = fab.output[prod] local fac = oslot.get_production_factor() local est_p = estimate_actual_production(oslot, exists) local increase = oslot.get_base_production() - est_p dbgprint(indent + " potential increase of production of " + prod + " is " + increase) // build tree for later planning local tree = { inputs = {}, supply = 0 } if (increase <= 0) { return tree } if (fab.input.len() == 0) { // producer of raw materials tree.supply = ( increase*fac)/100; return tree } // iterate all input goods and search for supply foreach(good, islot in fab.input) { local c_tree = estimate_consumption(fab, good, indent + " ") tree.inputs[good] <- c_tree local con = c_tree.increase local est = (con * fac)/ islot.get_consumption_factor() increase = min(increase, est) } tree.supply = increase return tree } static function estimate_actual_consumption(islot) { local con = islot.get_consumed() local isnew = (con.reduce(sum) - con[0]) == 0; if (!isnew) { // established connection: report max return con.reduce(max) } else { if (con[0] == 0) { // non-existing connection return 0 } else { // new connection: report base return islot.get_base_consumption() } } } static function estimate_actual_production(oslot, exists) { local pro = oslot.get_produced() local isnew = (pro.reduce(sum) - pro[0]) == 0; if (!isnew) { // established connection: report max return pro.reduce(max) } else { if (pro[0] == 0 && !exists) { // non-existing connection return 0 } else { // new connection: report base return oslot.get_base_consumption() } } } } /* * check factory chain befor build link * * return false = no build link * return true = build link */ function check_factory_link_line(f_src, f_dest, t_good) { local print_message_box = 0 local print_status = 0 local good_list_in = []; local g_count_in = 0 foreach(good, islot in f_dest.input) { // test for in-storage or in-transit goods local st = islot.get_storage() local it = islot.get_in_transit() //gui.add_message_at(our_player, "### " + fab.get_name() + " ## " + good + " ## get_storage() " + st[0] + " get_in_transit() " + it[0], world.get_time()) if (st[0] + st[1] + it[0] + it[1] > 0) { // something stored/in-transit in last and current month // no need to search for more supply good_list_in.append({ g = good, t = 1 }) g_count_in++ } else { good_list_in.append({ g = good, t = 0 }) } } local o = true for ( local i = 0; i < good_list_in.len(); i++ ) { //gui.add_message_at(our_player, " good in " + good_list_in[i].g + " connect " + good_list_in[i].t, world.get_time()) if ( good_list_in[i].g == t_good && good_list_in[i].t == 1 && good_list_in.len() >= g_count_in ) { // check all goods connect yes o = false } } // check consumers if ( !o ) { if ( f_dest.output.len() > 0 ) { // test connect next consumer fab local consumers = []; foreach(c in f_dest.get_consumers()) { consumers.append( factory_x(c.x, c.y) ); } // list output goods local good_list_out = []; foreach(good, islot in f_dest.output) { good_list_out.append(good) } /* // 1 consumer if ( consumers.len() == 1 ) { for ( local j = 0; j < good_list_out.len(); j++ ) { if ( check_factory_links(f_dest, consumers[0], good_list_out[j]) == 0 ) { o = false } } }*/ if ( consumers.len() > 0 ) { for ( local j = 0; j < good_list_out.len(); j++ ) { local consumers_links = 0 for ( local i = 0; i < consumers.len(); i++ ) { if ( check_factory_links(f_dest, consumers[i], good_list_out[j]) == 0 ) { if ( print_message_box >= 1 ) { gui.add_message_at(our_player, " link check consumers " + consumers[i].get_name() + " good " + good_list_out[j], world.get_time()) } // test to other supliers for this good g_count_in = 0 foreach(good, islot in consumers[i].input) { //if ( good == t_good ) { if ( good == good_list_out[j] ) { if ( print_message_box == 1 ) { gui.add_message_at(our_player, " consumers " + consumers[i].get_name() + " good " + good, world.get_time()) } // test for in-storage or in-transit goods local st = islot.get_storage() local it = islot.get_in_transit() //gui.add_message_at(our_player, "### " + fab.get_name() + " ## " + good + " ## get_storage() " + st[0] + " get_in_transit() " + it[0], world.get_time()) if (st[0] + st[1] + it[0] + it[1] > 0 && good_list_out[j] == good) { if ( print_message_box == 1 ) { gui.add_message_at(our_player, " good_list_out[j] " + good_list_out[j] + " good " + good, world.get_time()) } // something stored/in-transit in last and current month // no need to search for more supply g_count_in++ } local suppliers = []; foreach(c in consumers[i].get_suppliers()) { suppliers.append( factory_x(c.x, c.y) ); } if ( print_message_box == 1 ) { gui.add_message_at(our_player, " suppliers " + consumers[i].get_name() + " count " + suppliers.len(), world.get_time()) } for ( local k = 0; k < suppliers.len(); k++ ) { if ( check_factory_links(consumers[i], suppliers[k], good_list_out[j]) > 0 ) { consumers_links++ } foreach(good, islot in suppliers[k].input) { //if ( good_list_out[j] == good ) { if ( print_message_box >= 1 ) { gui.add_message_at(our_player, " supplier " + suppliers[k].get_name() + " good " + good, world.get_time()) } // test for in-storage or in-transit goods local st = islot.get_storage() local it = islot.get_in_transit() //gui.add_message_at(our_player, "### " + suppliers[k].get_name() + " ## " + good + " ## get_storage() " + st[0] + " get_in_transit() " + it[0], world.get_time()) if (st[0] + st[1] + it[0] + it[1] > 0 ) { if ( print_message_box == 1 ) { gui.add_message_at(our_player, "### " + suppliers[k].get_name() + " ## " + good + " ## get_storage() st[0] " + st[0] + " get_in_transit() it[0] " + it[0], world.get_time()) } // something stored/in-transit in last and current month // no need to search for more supply g_count_in++ } //} } } //} if ( print_message_box == 1 ) { gui.add_message_at(our_player, " g_count_in " + g_count_in + " consumers_links " + consumers_links, world.get_time()) } if ( g_count_in > 0 && consumers_links == 0 ) { o = false //::debug.pause() } } } } else { // all supplier and consumer for planned good connected // build second link supplier for this good o = true } } } } /* if ( g_count_in > 0 ) { o = false }*/ } else { o = true } } if ( print_message_box == 1 || print_status == 1 ) { local fs = f_src.get_tile_list() local fd = f_dest.get_tile_list() gui.add_message_at(our_player, "--> factory: " + f_src.get_name() + " (" + coord_to_string(fs[0]) + ") to factory " + f_dest.get_name() + " (" + coord_to_string(fd[0]) + ") --> good: " + t_good, world.get_time()) gui.add_message_at(our_player, "--> check_factory_link_line() return " + o, world.get_time()) } // check input f_src if ( o && f_src.input.len() > 0 ) { o = check_fsrc_input(f_src) } return o //get_delivered() //get_consumers() //get_suppliers() } /* * check factory link other goods of the same category * * */ function check_link_catg_goods(f_src, f_dest, t_good) { //gui.add_message_at(our_player, "--> check catg conect start (" + t_good + ")", world.get_time()) local line_catg = 0 local good_list_in = []; foreach(good, islot in f_dest.input) { if ( good != t_good && good_desc_x(good).get_catg_index() == good_desc_x(t_good).get_catg_index() ) { good_list_in.append(good) //gui.add_message_at(our_player, "-- input good " + good, world.get_time()) } } local good_list_out = []; foreach(good, islot in f_src.output) { if ( good != t_good && good_desc_x(good).get_catg_index() == good_desc_x(t_good).get_catg_index() ) { good_list_out.append(good) //gui.add_message_at(our_player, "-- output good " + good, world.get_time()) } } if ( good_list_out.len() > 0 && good_list_in.len() > 0 ) { //gui.add_message_at(our_player, "-- good_list_in.len() : " + good_list_in.len(), world.get_time()) //gui.add_message_at(our_player, "-- good_list_out.len() : " + good_list_out.len(), world.get_time()) for(local i = 0; i < good_list_out.len(); i++ ) { //gui.add_message_at(our_player, "-- good_list_in.find(good_list_out[i]) : " + good_list_in.find(good_list_out[i]), world.get_time()) if ( good_list_in.find(good_list_out[i]) != null ) { //gui.add_message_at(our_player, "-- conect good " + good_list_out[i], world.get_time()) line_catg += check_factory_links(fsrc, fdest, good_list_out[i]) //gui.add_message_at(our_player, "-- check pl link : " + industry_manager.get_link_state(fsrc, fdest, good_list_out[i]), world.get_time()) if ( industry_manager.get_link_state(fsrc, fdest, good_list_out[i]) == 3 ) { // no build - exists link from player return false } } } } //gui.add_message_at(our_player, "-- catg conect : " + line_catg, world.get_time()) if ( line_catg >= 2 ) { // no build - exists links from other player return false } //gui.add_message_at(our_player, "<-- check catg conect end", world.get_time()) return true } /* * check factory input storage * */ function check_fsrc_input(f_src) { // check input f_src if ( f_src.input.len() > 0 ) { local src_count_in = [] local j = 0 foreach(good, islot in f_src.input) { // test for in-storage or in-transit goods local st = islot.get_storage() local it = islot.get_in_transit() local count = 0 for ( local i = 0; i < 12; i++ ) { count += st[i] count += it[i] } src_count_in.append(count) local fs = f_src.get_tile_list() //gui.add_message_at(our_player, "### " + f_src.get_name() + " - " + coord_to_string(fs[0]) + " # " + good + " # src_count_in " + src_count_in[j], fs[0]) j++ } if ( src_count_in.find(0) != null && f_src.get_halt_list().len() > 0 ) { //local fs = f_src.get_tile_list() //gui.add_message_at(our_player, "### return false", fs[0]) return false } } return true } simutrans-124.5/simutrans/ai/sqai_rail/industry_connection_planner.nut000066400000000000000000001126561520675460500265300ustar00rootroot00000000000000/** * Plans connections suggested by factory-searcher. */ // helper class to simulate ways on open water openwater <- { function get_cost() { return 0; } function get_maintenance() { return 0; } function get_name() { return "open water"} } function get_max_convoi_length(wt) { switch(wt) { case wt_rail: return settings.get_max_rail_convoi_length(); case wt_road: return settings.get_max_road_convoi_length(); case wt_water: return settings.get_max_ship_convoi_length(); } return 4; } /** * Planer class. */ class industry_connection_planner_t extends manager_t { // Source factory fsrc = null // factory_x // Destination factory fdest = null // factory_x // Freight to be transported freight = null // string prod = -1 // integer // print messages box // 1 = vehicles // 2 = stations // 3 = depots // 4 = reports // 5 = factorys print_message_box = 0 wt_name = ["", "road", "rail", "water"] constructor(s,d,f) { base.constructor("industry_connection_planner_t"); fsrc = s; fdest = d; freight = f; debug = false } /** * Evaluates transport by road, rail and ships. * Returns the corresponding reports. * In addition, returns amphibious_connection_planner_t. * This planner will start to work if the connection by road, rail or ship did not succeed. */ function step() { local tic = get_ops_total(); // limit links to transport good local exists_links = check_factory_links(fsrc, fdest, freight, 0) local freight_input = fdest.input[freight].get_base_consumption() local freight_output = fsrc.output[freight].get_base_production() local fs = fsrc.get_tile_list() local fs_halt = fs[0].get_halt() if ( (fs_halt != null ) && exists_links == 1 ) { //|| fs_halt != false if (debug) gui.add_message_at(our_player, "fs_halt - " + fs_halt, fs[0]) // water factory - no more links than one if (debug) gui.add_message_at(our_player, " water factory - no more links than one ", fs[0]) return r_t(RT_TOTAL_FAIL) } if ( (freight_input < 700 || freight_output < 550) && exists_links >= 2 ) { return r_t(RT_TOTAL_FAIL) } else if ( (freight_input < 1500 || freight_output < 1250) && exists_links >= 3 ) { return r_t(RT_TOTAL_FAIL) } else if ( (freight_input > 1500 || freight_output > 1250) && exists_links >= 4 ) { return r_t(RT_TOTAL_FAIL) } else if ( exists_links > 4 ) { return r_t(RT_TOTAL_FAIL) } if ( build_check_month > world.get_time().ticks ) { // not plan link if (debug) gui.add_message_at(our_player, " not plan link : build_check_month = " + build_check_month, world.get_time()) if (debug) gui.add_message_at(our_player, " for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y, world.get_time()) return r_t(RT_TOTAL_FAIL) } print("Plan link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y) // TODO check if factories are still existing // TODO check if connection is plannable // compute monthly production if (prod < 0) { prod = calc_production() } dbgprint("production = " + prod); // rail local rprt = plan_simple_connection(wt_rail, null, null) if (rprt) { append_report(rprt) } // road rprt = plan_simple_connection(wt_road, null, null) if (rprt) { append_report(rprt) } // water rprt = plan_simple_connection(wt_water, null, null) if (rprt) { append_report(rprt) } if (reports.len() == 0) { dbgprint("Set link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y + " to MISSING") industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) return r_t(RT_TOTAL_FAIL) } // deliver it local r = r_t(RT_READY) r.report = get_report() // append a chain of alternative connector nodes local rchain = r.report while (reports.len()>0) { local rep = get_report() if (rep == null) { // may happen if no nice reports are available break; } rchain.action.reports.append( rep ) rchain = rep } local r_amph = report_t() r_amph.action = amphibious_connection_planner_t(fsrc, fdest, freight) if (rchain) { rchain.action.reports.append( r_amph ) } else { r.report = r_amph } local toc = get_ops_total(); print("industry_connection_planner wasted " + (toc-tic) + " ops") return r } /** * Plans a connection using one mode of transport * * If start or target are null then use fsrc/fdest. */ function plan_simple_connection(wt, start, target, distance = 0) { // set line build cost 0 industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 1) industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 2) industry_manager.set_link_build_cost(fsrc, fdest, freight, 0, 3) // compute correct distance if (distance == 0) { foreach(i in ["x", "y"]) { distance += abs( (start ? start[i] : fsrc[i]) - (target ? target[i] : fdest[i])) } } if (distance == 0) { // still zero? avoid division by zero in the prototyper // distance factorys distance = abs(fsrc.x - fdest.x) + abs(fsrc.y - fdest.y) // add 10% from distance distance + (distance / 100 * 10) } // plan convoy prototype local prototyper = prototyper_t(wt, freight) prototyper.min_speed = 1 prototyper.max_vehicles = get_max_convoi_length(wt) prototyper.max_length = prototyper.max_vehicles * 8 local freight_input = fdest.input[freight].get_base_consumption() local freight_output = fsrc.output[freight].get_base_production() prototyper.volume = min(freight_input, freight_output) if ( print_message_box == 5 ) { gui.add_message_at(our_player, "freight_input " + freight_input + " freight_output " + freight_output, world.get_time()) gui.add_message_at(our_player, "min(freight_input, freight_output) " + min(freight_input, freight_output), world.get_time()) } local cnv_valuator = valuator_simple_t() cnv_valuator.wt = wt cnv_valuator.freight = freight cnv_valuator.volume = prod cnv_valuator.max_cnvs = 200 // no signals and double tracks - limit 1 convoy for rail if (wt == wt_rail) { cnv_valuator.max_cnvs = 1 } cnv_valuator.distance = distance if ( print_message_box > 0 ) { gui.add_message_at(our_player, "___________________________ Start plan_simple_connection __________________________", world.get_time()) //gui.add_message_at(our_player, "plan way ", world.get_time()) local t = tile_x(fsrc.x, fsrc.y, 0) gui.add_message_at(our_player, "Plan " + wt_name[wt] + " link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y, t) } if ( debug ) { local t = tile_x(fsrc.x, fsrc.y, 0) gui.add_message_at(our_player, "Plan " + wt_name[wt] + " link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y, t) } local bound_valuator = valuator_simple_t.valuate_monthly_transport.bindenv(cnv_valuator) prototyper.valuate = bound_valuator if (prototyper.step().has_failed()) { if (debug) gui.add_message_at(our_player, "ERROR # no " + wt_name[wt] + " vehicle found for freight " + freight, world.get_time()) return null } local planned_convoy = prototyper.best print("best " + planned_convoy.min_top_speed + " / " + planned_convoy.max_speed) // fill in report when best way is found local r = report_t() // plan way local planned_way = null if (wt == wt_water) { planned_way = openwater } else { local way_list = way_desc_x.get_available_ways(wt, st_flat) local best_way = null local best = null foreach(way in way_list) { if ( !way.is_retired(world.get_time()) ) { cnv_valuator.way_maintenance = way.get_maintenance() cnv_valuator.way_max_speed = way.get_topspeed() local test = cnv_valuator.valuate_monthly_transport(planned_convoy) if (best == null || test > best) { best = test // max track speed 160 if (cnv_valuator.way_max_speed < 161 && wt == wt_rail ) { best_way = way } else { best_way = way } } } } dbgprint("Best value = " + best + " way = " + best_way.get_name()) // valuate again with best way cnv_valuator.way_maintenance = 0 cnv_valuator.way_max_speed = best_way.get_topspeed() planned_way = best_way } local planned_bridge = { cost = 0, montly_cost = 0, tiles = 0 } local tree_cost = 0 // cost remove tree local calc_route = null local p_start = null local p_end = null if (wt != wt_water && wt != wt_air) { /* plan build route */ p_start = ::finder.find_station_place(fsrc, fdest) if ( p_start.len() == 0 ) { calc_route = "No route" } else { p_end = ::finder.find_station_place(fdest, p_start, true) if ( p_end.len() == 0 ) { calc_route = "No route" } else { calc_route = test_route(our_player, p_start, p_end, planned_way) } } //gui.add_message_at(our_player, "calc_route: way tiles = " + calc_route.routes.len() + " bridge tiles = " + calc_route.bridge_lens, world.get_time()) //gui.add_message_at(our_player, "distance " + distance, world.get_time()) if ( calc_route == "No route" ) { // } else { cnv_valuator.distance = calc_route.routes.len() + calc_route.bridge_lens planned_bridge.cost = calc_route.bridge_lens * calc_route.bridge_obj.get_cost() planned_bridge.montly_cost = calc_route.bridge_lens * calc_route.bridge_obj.get_maintenance() planned_bridge.tiles = calc_route.bridge_lens // tree_desc_x.get_price() -> Simutrans r9528+ tree_cost = calc_route.tiles_tree * tree_desc_x.get_price() } } // valuate again with best way r.gain_per_m = cnv_valuator.valuate_monthly_transport(planned_convoy) if ( print_message_box == 1 ) { gui.add_message_at(our_player, "*** ", world.get_time()) gui.add_message_at(our_player, "plan station ", world.get_time()) } // plan station local planned_station = null local planned_harbour_flat = null if ( wt == wt_rail ) { //planned_convoy.length = 12 } if ( print_message_box == 1 ) { gui.add_message_at(our_player, "wt " + wt_name[wt], world.get_time()) gui.add_message_at(our_player, "planned_convoy.length " + planned_convoy.length, world.get_time()) } if (wt != wt_water) { local station_list = building_desc_x.get_available_stations(building_desc_x.station, wt, good_desc_x(freight)) if ( wt == wt_rail ) { planned_station = select_station(station_list, 8, planned_convoy.capacity) } else { planned_station = select_station(station_list, planned_convoy.length, planned_convoy.capacity) } } else { // find harbour building local station_list = building_desc_x.get_available_stations(building_desc_x.harbour, wt, good_desc_x(freight)) planned_station = select_station(station_list, 1, planned_convoy.capacity) // find flat harbour building station_list = building_desc_x.get_available_stations(building_desc_x.flat_harbour, wt_water, good_desc_x(freight)) planned_harbour_flat = select_station(station_list, 1, planned_convoy.capacity) } // plan depot local planned_depot = null { local depot_list = building_desc_x.get_available_stations(building_desc_x.depot, wt, {}) if (depot_list.len()) { planned_depot = depot_list[0] } } if (planned_convoy == null || planned_way == null || planned_station == null || planned_depot == null) { return null } // create action node local cn = null switch(wt) { case wt_rail: cn = rail_connector_t(); break case wt_road: cn = road_connector_t(); break case wt_water: cn = ship_connector_t(); break } cn.fsrc = fsrc cn.fdest = fdest cn.freight = freight cn.planned_way = planned_way cn.planned_station = planned_station cn.planned_depot = planned_depot cn.planned_convoy = planned_convoy if (start) { cn.c_start = [start] print("Connector from " + coord_to_string(start)) // mark tile for station //set_marker(cn.c_start) } if (target) { cn.c_end = [target] print("Connector to " + coord_to_string(target)) // mark tile for station //set_marker(cn.c_end) } r.distance = cnv_valuator.distance // stations lenght local a = planned_convoy.length local count = 0 do { a -= 16 count += 1 } while(a > 0) // check planned way speed - planned convoy speed if ( wt == wt_air && wt != wt_water && planned_way.get_topspeed() < planned_convoy.min_top_speed ) { //gui.add_message_at(our_player, "-- planned way (" + planned_way.get_topspeed() + ") to low speed for convoy (" + planned_convoy.min_top_speed + ")", world.get_time()) planned_way = find_object("way", wt, planned_convoy.min_top_speed) } // build cost for way, stations and depot local build_cost = ((r.distance * planned_way.get_cost()) + ((count*2)*planned_station.get_cost()) + planned_depot.get_cost() + planned_bridge.cost)/100 + (tree_cost/100*2) // build cost / 13 months //build_cost = build_cost / 13 local rail_station = null if ( wt == wt_rail ) { // terraform cost local terraform_cost = 0 try { terraform_cost = command_x.slope_get_price(82)/100 } catch(ev) { // hat nicht funktioniert terraform_cost = 7500 } build_cost += count*terraform_cost // check build station rail if ( calc_route != "No route" ) { rail_station = check_station(our_player, calc_route.routes[calc_route.routes.len()-1], calc_route.routes, count, wt_rail, planned_station, 0, true) if (rail_station) { rail_station = check_station(our_player, calc_route.routes[0], calc_route.routes, count, wt_rail, planned_station, 0, true) //if (debug) gui.add_message_at(our_player, "-check build station rail- rail_station " + rail_station, calc_route.routes[0]) } //if (debug && !rail_station) gui.add_message_at(our_player, "-check build station rail- rail_station " + rail_station, calc_route.routes[0]) //if (debug) gui.add_message_at(our_player, "-check build station rail- calc_route " + calc_route, world.get_time()) } } // save build cost to link if ( wt == wt_rail ) { industry_manager.set_link_build_cost(fsrc, fdest, freight, build_cost, 1) } else if ( wt == wt_road ) { industry_manager.set_link_build_cost(fsrc, fdest, freight, build_cost, 2) } else if ( wt == wt_water ) { industry_manager.set_link_build_cost(fsrc, fdest, freight, build_cost, 3) } local conv_capacity = planned_convoy.capacity local input_convoy = freight_input/conv_capacity local output_convoy = freight_output/conv_capacity /** * Assessment for the different connections * points calculated in function get_report() (basic.nut) * */ r.points = 100 // to many convoys if ( output_convoy > 200 ) { r.points -= 25 } // low freight volume if ( freight_input < 700 || freight_output < 550 ) { switch (wt) { case wt_rail: r.points -= 15 break case wt_road: r.points += 12 break case wt_water: r.points += 0 break } } // high freight volume if ( freight_input > 2250 || freight_output > 1700 ) { switch (wt) { case wt_rail: r.points += 22 break case wt_road: r.points -= 15 break case wt_water: r.points += 0 break } } // factory distance direct local f_dist = abs(fsrc.x - fdest.x) + abs(fsrc.y - fdest.y) // + 22% for long distance local f_dist_long = f_dist + (f_dist / 100 * 22) // + 35% for short distance local f_dist_short = f_dist + (f_dist / 100 * 35) // cash buffer up to first ingestion - % local cash_buffer = 0 //gui.add_message_at(our_player, "-- world.get_time().year " + world.get_time().year, world.get_time()) // lower share of bridges year ago local bridge_year_factor = 1 if ( world.get_time().year < 1920 ) { if ( wt == wt_road ) { bridge_year_factor = 4 } } else if ( world.get_time().year < 1935 ) { if ( wt == wt_road ) { bridge_year_factor = 3 } } // higt distance if ( r.distance > 350 ) { switch (wt) { case wt_rail: if ( f_dist_long < r.distance ) { r.points += 10 cash_buffer = 20 } else { r.points += 20 cash_buffer = 10 } if ( world.get_time().year < 1935 && planned_bridge.tiles > 18 ) { //gui.add_message_at(our_player, "wt_road: world.get_time().year < 1935 && planned_bridge.tiles > 5", world.get_time()) r.points -= (34*bridge_year_factor) } else if ( planned_bridge.tiles > 40 ) { r.points -= (25*bridge_year_factor) } if ( planned_convoy.max_speed < 50 ) { //gui.add_message_at(our_player, "wt_rail planned_convoy.max_speed " + planned_convoy.max_speed, world.get_time()) r.points -= 20 } break case wt_road: if ( f_dist_long < r.distance ) { r.points -= 25 cash_buffer = 20 } else { r.points -= 10 cash_buffer = 20 } if ( planned_bridge.tiles > 15 ) { r.points -= (32*bridge_year_factor) } if ( planned_convoy.max_speed < 40 ) { //gui.add_message_at(our_player, "wt_road planned_convoy.max_speed " + planned_convoy.max_speed, world.get_time()) r.points -= 20 } break case wt_water: if ( f_dist_long < r.distance ) { r.points -= 20 cash_buffer = 20 } else { r.points += 0 cash_buffer = 20 } break } if ( r.distance > 450 ) { r.points -= 10 } else if ( r.distance > 550 ) { r.points -= 20 } } // low distance if ( r.distance < 120 ) { switch (wt) { case wt_rail: if ( f_dist_short < r.distance ) { r.points -= 20 cash_buffer = 5 } else { r.points -= 10 cash_buffer = 10 } if ( world.get_time().year < 1935 && planned_bridge.tiles > 8 ) { //gui.add_message_at(our_player, "wt_road: world.get_time().year < 1935 && planned_bridge.tiles > 5", world.get_time()) r.points -= (38*bridge_year_factor) } else if ( planned_bridge.tiles > 15 ) { r.points -= (25*bridge_year_factor) } break case wt_road: if ( f_dist_short < r.distance ) { r.points += 25 cash_buffer = 5 } else { r.points += 10 cash_buffer = 10 } if ( world.get_time().year < 1935 && planned_bridge.tiles > 5 ) { //gui.add_message_at(our_player, "wt_road: world.get_time().year < 1935 && planned_bridge.tiles > 5", world.get_time()) r.points -= (38*bridge_year_factor) } else if ( planned_bridge.tiles > 15 ) { r.points -= (32*bridge_year_factor) } break case wt_water: if ( f_dist_short < r.distance ) { r.points -= 20 cash_buffer = 5 } else { r.points -= 10 cash_buffer = 10 } break } } // middle distance if ( r.distance > 120 && r.distance < 350 ) { switch (wt) { case wt_rail: if ( world.get_time().year < 1935 && planned_bridge.tiles > 12 ) { //gui.add_message_at(our_player, "wt_rail: world.get_time().year < 1935 && planned_bridge.tiles > 5", world.get_time()) r.points -= (22*bridge_year_factor) } else if ( planned_bridge.tiles > 28 ) { r.points -= (15*bridge_year_factor) } break case wt_road: if ( world.get_time().year < 1935 && planned_bridge.tiles > 5 ) { //gui.add_message_at(our_player, "wt_road: world.get_time().year < 1935 && planned_bridge.tiles > 5", world.get_time()) r.points -= (22*bridge_year_factor) } else if ( planned_bridge.tiles > 15 ) { r.points -= (15*bridge_year_factor) } break } } // route <-> factory distance local dist_route_check = 0 if ( get_set_name() == "pak64.german" ) { //gui.add_message_at(our_player, "factorys: f_dist " + f_dist + " - f_dist * 2) " + (f_dist * 2) + " - route len " + r.distance, world.get_time()) dist_route_check = f_dist + (f_dist / 3 * 4) } else { //gui.add_message_at(our_player, "factorys: f_dist " + f_dist + " - f_dist + (f_dist / 3 * 2) " + (f_dist + (f_dist / 3 * 2)) + " - route len " + r.distance, world.get_time()) dist_route_check = f_dist + (f_dist / 3 * 2) } if ( dist_route_check < r.distance ) { switch (wt) { case wt_rail: r.points -= 40 break case wt_road: r.points -= 60 break } } // build rail station if ( rail_station == false ) { r.points -= 200 } // freight weight local g = good_desc_x(freight).get_weight_per_unit() g = (g * 50) / 1000 // weight hight if ( g > 32 ) { switch (wt) { case wt_rail: r.points += 22 break case wt_road: r.points -= 8 break case wt_water: break } } // weight low if ( g < 20 ) { switch (wt) { case wt_rail: r.points -= 12 break case wt_road: r.points += 14 break case wt_water: break } } local road_car_rate = set_map_vehicles_counts(1) if ( print_message_box > 0 && road_car_rate < 10 ) { // gui.add_message_at(our_player, "citycar_count " + citycar_count + " - road_convoy_count " + road_convoy_count + " - citycar_rate " + road_car_rate, world.get_time()) //gui.add_message_at(our_player, "world.get_time().ticks_per_month " + world.get_time().ticks_per_month, world.get_time()) } if ( road_car_rate < 10 ) { switch (wt) { case wt_rail: r.points += 25 break case wt_road: r.points -= 25 break case wt_water: break } } // Adjustments to the pakset //gui.add_message_at(our_player, "get_set_name() " + get_set_name(), world.get_time()) //::debug.pause() if ( get_set_name() == "pak128.german" ) { switch (wt) { case wt_rail: r.points += 20 break case wt_road: r.points -= 15 break case wt_water: break } } if ( get_set_name() == "pak128" ) { switch (wt) { case wt_rail: r.points -= 200 break case wt_road: //r.points -= 15 break case wt_water: break } } // successfull - complete report r.cost_fix = build_cost r.cost_monthly = (r.distance * planned_way.get_maintenance()) + ((count*2)*planned_station.get_maintenance()) + planned_depot.get_maintenance() + planned_bridge.montly_cost // fixed cost convoy local cnv_veh = planned_convoy.veh local cnv_fixed_cost = 0 for (local x = 0; x < cnv_veh.len(); x++ ) { cnv_fixed_cost += cnv_veh[x].get_maintenance() } r.cost_monthly += cnv_fixed_cost //gui.add_message_at(our_player, "cnv_fixed_cost " + cnv_fixed_cost, world.get_time()) r.gain_per_m -= r.cost_monthly sleep() // capital check local cash = our_player.get_current_cash() - r.cost_fix if ( calc_route != null && calc_route != "No route" ) { // if combined station from ship local t = calc_route.routes[calc_route.routes.len()-1] local st_dock = search_station(t, wt_water, 1) if ( st_dock ) { local st = halt_x.get_halt(st_dock[0], our_player) if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() == 0 ) { cash = our_player.get_current_net_wealth() - r.cost_fix //gui.add_message_at(our_player, "combined station -> get_current_net_wealth() " + get_current_net_wealth(), world.get_time()) } else { } } } } local m = r.cost_fix/100*cash_buffer if ( r.distance > 350 ) { //gui.add_message_at(our_player, "connection planner r.distance > 350 -> " + r.distance, world.get_time()) m = r.cost_fix/40*cash_buffer } else if ( r.distance > 300 ) { //gui.add_message_at(our_player, "connection planner r.distance > 300 -> " + r.distance, world.get_time()) m = r.cost_fix/60*cash_buffer } else if ( r.distance > 250 ) { //gui.add_message_at(our_player, "connection planner r.distance > 250 -> " + r.distance, world.get_time()) m = r.cost_fix/80*cash_buffer } if ( (cash-m) < 0 ) { r.points -= 50 } // set retire time for report r.retire_time = world.get_time().next_month_ticks + world.get_time().ticks_per_month - 1 /* local txt_message = "Retire: Station " + planned_station.get_retire_date().month + "." + planned_station.get_retire_date().year if ( wt != wt_water && wt != wt_air ) { txt_message += ", Way " + planned_way.get_retire_date().month + "." + planned_way.get_retire_date().year } txt_message += ", Depot " + planned_depot.get_retire_date().month + "." + planned_depot.get_retire_date().year gui.add_message_at(our_player, txt_message, world.get_time()) txt_message = "Convoy " for ( local i = 0; i < planned_convoy.veh.len(); i++ ) { txt_message += " - " + planned_convoy.veh[i].get_retire_date().month + "." + planned_convoy.veh[i].get_retire_date().year } gui.add_message_at(our_player, txt_message, world.get_time()) */ // retire station local min_retire = planned_station.get_retire_date().raw // retire way if ( ( wt != wt_water && wt != wt_air ) && planned_way.get_retire_date().raw < min_retire ) { min_retire = planned_way.get_retire_date().raw } // retire depot if ( planned_depot.get_retire_date().raw < min_retire ) { min_retire = planned_depot.get_retire_date().raw } // retire vehicle convoy for ( local i = 0; i < planned_convoy.veh.len(); i++ ) { if ( planned_convoy.veh[i].get_retire_date().raw < min_retire ) { min_retire = planned_convoy.veh[i].get_retire_date().raw } } //gui.add_message_at(our_player, "retire " + min_retire_month + "." + min_retire_year, world.get_time()) r.retire_obj = min_retire /* gui.add_message_at(our_player, "Plan " + wt_name[wt] + " link for " + freight + " from " + fsrc.get_name() + " at " + fsrc.x + "," + fsrc.y + " to "+ fdest.get_name() + " at " + fdest.x + "," + fdest.y, world.get_time()) if ( calc_route != null && calc_route != "No route" ) { gui.add_message_at(our_player, "calc_route: way tiles = " + calc_route.routes.len() + " bridge tiles = " + calc_route.bridge_lens + " tree tiles = " + calc_route.tiles_tree, world.get_time()) } gui.add_message_at(our_player, " * Report: link points = " + r.points, world.get_time()) */ // successfull - complete report r.action = cn dbgprint("Plan: way = " + planned_way.get_name() + ", station = " + planned_station.get_name() + ", depot = " + planned_depot.get_name()); dbgprint("Report: gain_per_m = " + r.gain_per_m + ", nr_convoys = " + planned_convoy.nr_convoys + ", cost_fix = " + r.cost_fix + ", cost_monthly = " + r.cost_monthly) dbgprint("Report: dist = " + cnv_valuator.distance + " way_cost = " + planned_way.get_cost()) dbgprint("Report: station = " + planned_station.get_cost()+ " depot = " + planned_depot.get_cost()) if ( print_message_box == 4 || debug) { gui.add_message_at(our_player, "----- ", world.get_time()) gui.add_message_at(our_player, "Plan: way = " + planned_way.get_name() + ", station = " + planned_station.get_name() + ", depot = " + planned_depot.get_name(), world.get_time()) gui.add_message_at(our_player, "Report: gain_per_m = " + r.gain_per_m + ", nr_convoys = " + planned_convoy.nr_convoys + ", cost_build = " + r.cost_fix + ", cost_monthly = " + r.cost_monthly, world.get_time()) gui.add_message_at(our_player, "Report: dist = " + r.distance + " way_cost = " + planned_way.get_cost(), world.get_time()) gui.add_message_at(our_player, "Report: station = " + planned_station.get_cost() + " depot = " + planned_depot.get_cost(), world.get_time()) gui.add_message_at(our_player, "Report: bridge tiles = " + planned_bridge.tiles, world.get_time()) gui.add_message_at(our_player, " * Report: freight = " + freight + " convoy capacity = " + conv_capacity, world.get_time()) gui.add_message_at(our_player, " * Report: input amount = " + freight_input + " input amount / convoy capacity = " + input_convoy, world.get_time()) gui.add_message_at(our_player, " * Report: output = " + freight_output + " output / convoy capacity = " + output_convoy, world.get_time()) gui.add_message_at(our_player, " * Report: link points = " + r.points, world.get_time()) } if ( print_message_box > 0 ) { gui.add_message_at(our_player, "___________________________ End plan_simple_connection __________________________", world.get_time()) } return r } function calc_production() { local src_prod = fsrc.output[freight].get_base_production(); local dest_con = fdest.input[freight].get_base_consumption(); // TODO implement production boost factors local src_prod_faktor = fsrc.output[freight].get_production_factor(); local dest_con_faktor = fdest.input[freight].get_consumption_factor(); if ( print_message_box == 5 ) { gui.add_message_at(our_player, "----- factory info -----", world.get_time()) gui.add_message_at(our_player, " fsrc : " + fsrc.get_name(), world.get_time()) gui.add_message_at(our_player, "freight = " + freight + ", output = " + dest_con + ", factor = " + dest_con_faktor, world.get_time()) gui.add_message_at(our_player, "-----", world.get_time()) gui.add_message_at(our_player, " fdest : " + fdest.get_name(), world.get_time()) gui.add_message_at(our_player, "freight = " + freight + ", input = " + src_prod + ", factor = " + src_prod_faktor, world.get_time()) gui.add_message_at(our_player, "--- factory info end ---", world.get_time()) } dbgprint("production = " + src_prod + " / " + dest_con); return min(src_prod,dest_con) } static function select_station(list, length, capacity) { local station_length = (length + CARUNITS_PER_TILE - 1) / CARUNITS_PER_TILE local capacity = capacity local station_capacity = 0 local station_is_terminus = false local best_station = null if ( print_message_box == 2 ) { gui.add_message_at(our_player, "---- station list ----", world.get_time()) } foreach(station in list) { local ok = (best_station == null) local s_capacity = station_length * station.get_capacity() if ( print_message_box == 2 ) { gui.add_message_at(our_player, "station " + station.get_name() + ", type = " + station.get_type(), world.get_time()) } if (!ok && station_length == 1) { // prefer terminus ok = station.is_terminus() && !station_is_terminus if (!ok) { // then prefer stations with enough capacity ok = station_capacity < capacity ? station_capacity < s_capacity : capacity < s_capacity && s_capacity < station_capacity } } if (station_length > 1) { // force non-terminus ok = !station_is_terminus } if (ok) { best_station = station station_capacity = station.get_capacity() station_is_terminus = station.is_terminus() } } if ( print_message_box == 2 ) { gui.add_message_at(our_player, "----> selectet station: " + best_station.get_name(), world.get_time()) } return best_station } } /* * * */ function test_route(pl, starts, ends, way) { local as = astar_builder() as.builder = way_planner_x(pl) as.way = way as.builder.set_build_types(way) as.bridger = pontifex(pl, way) if (as.bridger.bridge == null) { as.bridger = null } local res = as.search_route(starts, ends, 0) if ("err" in res) { return res.err } return res } /** * check links factory src to factory dest * */ function check_factory_links(f_src, f_dest, good, show_msg = 0) { local print_message_box = 0 local print_status = 0 if ( show_msg == 1 ) { print_status = 1 } local fs = f_src.get_tile_list() local fd = f_dest.get_tile_list() if ( print_message_box == 1 || print_status == 1 ) { gui.add_message_at(our_player, "--> factory: " + f_src.get_name() + " (" + coord_to_string(fs[0]) + ") to factory " + f_dest.get_name() + " (" + coord_to_string(fd[0]) + ") --> good: " + good, world.get_time()) //gui.add_message_at(player_x(1), "--> good: " + good, world.get_time()) } local f_water = 0 local f_water_name = null local stations_fsrc = null local fs_tile = square_x(fs[0].x, fs[0].y).get_ground_tile() if ( fs_tile.is_water && fs_tile.get_halt() ) { stations_fsrc = fs_tile.get_halt().get_connections(good_desc_x(good)) f_water_name = fs_tile.get_halt().get_name() f_water = 1 if ( print_message_box == 2 ) { gui.add_message_at(player_x(1), "--> factory: " + f_src.get_name() + " to factory " + f_dest.get_name(), world.get_time()) gui.add_message_at(player_x(1), "**--> factory on water " + coord_to_string(fs[0]), world.get_time()) print_status = 1 gui.add_message_at(player_x(1), "**--> f_water_name " + f_water_name, world.get_time()) gui.add_message_at(player_x(1), "**--> stations_fsrc.len() " + stations_fsrc.len(), world.get_time()) } } else { // stations from factory src stations_fsrc = f_src.get_halt_list() } // stations from factory dest local stations_fdest = f_dest.get_halt_list() local link_count = 0 if ( stations_fsrc.len() > 0 && stations_fdest.len() > 0 ) { foreach(station in stations_fsrc) { local s = station.get_connections(good_desc_x(good)) if ( s.len() > 0 ) { local i = 0 if ( f_water = 1 && s[0].get_name() == f_water_name && s.len() > 1 ) { i = 1 } if ( print_message_box == 1 || print_status == 1 ) { gui.add_message_at(our_player, station.get_name() + " *--> to station " + s[i].get_name(), world.get_time()) } local f = s[i].get_factory_list() if ( f.len() == 1 || ( f_water == 1 && f.len() == 2 ) ) { local fd_t = f_dest.get_tile_list() local fs_t = f[0].get_tile_list() if ( fd_t[0].x == fs_t[0].x && fd_t[0].y == fs_t[0].y ) { link_count += 1 } } // to do check link by more combined station // test_halt_waytypes(tile) return count waytypes if ( f.len() == 0 ) { local c_station = s[i].get_connections(good_desc_x(good)) } } } } if ( print_message_box == 1 || print_status == 1 ) { gui.add_message_at(player_x(1), "check_factory_links() --> link count factory " + f_src.get_name() + " (" + coord_to_string(fs[0]) + ") to factory " + f_dest.get_name() + " (" + coord_to_string(fd[0]) + "): " + link_count + " # plan Player " + our_player.get_name(), f_src.get_tile_list()[0]) } return link_count } simutrans-124.5/simutrans/ai/sqai_rail/industry_manager.nut000066400000000000000000002771711520675460500242700ustar00rootroot00000000000000/** * Classes to manage industry connections. */ /** * Class to store lines plus extra data. * Derived from line_x, so it inherits all the methods from line_x. */ class my_line_t extends line_x { //dings_bums = 0 // extra data, you can put more such variables here. double_ways_count = 0 // count of double way build double_ways_build = 0 // double way build: 0 = no ; 1 = yes optimize_way_line = 0 // is way line optimize: 0 = no ; 1 = run 1 ; 2 = run 2 .... destroy_line_month = world.get_time().month // test month save line_way_speed = 0 // save way speed for line build_line = world.get_time() // create line next_vehicle_check = world.get_time().ticks + world.get_time().ticks_per_month // save ticks for next vehicle check next_check = world.get_time().ticks stuck_check = 0 // check stuck vehicles halt_length = 0 // tiles from halts add_convoy_time = world.get_time().ticks + world.get_time().ticks_per_month // save ticks for next add convoy - destroy convoy then add time line_bridges_count = 0 // count non player bridges from line line_bridges = [] // array bridge start/end from not player bridges way_line_count = 1 // more lines on way > 1 constructor(line /* line_x */) { base.constructor(line.id) // never change id ! } function _save() { return ::saveinstance("my_line_t", this) } } /** * A link is a connection between two factories. * Save its state here. */ class industry_link_t { f_src = null // factory_x f_dest = null // factory_x freight = null // good_desc_x state = 0 lines = null // array build_cost_link = 0 build_cost_rail = 0 build_cost_road = 0 build_cost_ship = 0 // 1 - link has combined lines combined_link = 0 /* double_ways_count = 0 // count of double way build double_ways_build = 0 // double way build: 0 = no ; 1 = yes optimize_way_line = 0 // is way line optimize: 0 = no ; 1 = yes destroy_line_month = null // test month save line_way_speed = 0 // save way speed for line build_line = 0 // create line next_vehicle_check = 0 // save ticks for next vehicle check */ // next check needed if ticks > next_check // state == st_missing: check availability again // state == st_build: check for possible upgrades next_check = 0 // if world.get_time().ticks > next_check then state is set to free (for state == failed, missing) static st_free = 0 /// not registered static st_planned = 1 /// link is planned static st_failed = 2 /// construction failed, do not try again static st_built = 3 /// connection successfully built static st_missing = 4 /// missing infrastructure, try again later constructor(s,d,f) { f_src = s f_dest = d freight = good_desc_x(f) lines = [] } function append_line(l) { lines.append(my_line_t(l)) } function remove_line(l) { foreach(idx, line in lines) { if (line.id == l.id) { lines.remove(idx) break } } } function _save() { return ::saveinstance("industry_link_t", this) } // upgrade elements in lines array from line_x to my_line_t function update_lines() { if (lines.len() > 0 && lines[0].getclass() == line_x) { local map_to_my_line_t = function(line) { return my_line_t(line); } lines.apply(map_to_my_line_t) } } } /** * Manage the links operated by us. */ class industry_manager_t extends manager_t { link_list = null link_iterator = null // print messages box // 1 = vehicles // 2 = // 3 = print_message_box = 0 constructor() { base.constructor("industry_manager_t") link_list = {} ::industry_manager = this debug = false } /// Generate unique key from link data static function key(src, des, fre) { return ("freight-" + fre + "-from-" + coord_to_key(src) + "-to-" + coord_to_key(des) ).toalnum() } function set_link_state(src, des, fre, state) { local k = key(src, des, fre) try { link_list[k].state = state } catch(ev) { // not existing - create entry local l = industry_link_t(src, des, fre) l.state = state link_list[k] <- l } switch(state) { case industry_link_t.st_built: link_list[k].next_check = today_plus_months(1) local text = "" text = "Transport " + translate(fre) + " from " text += coord(src.x, src.y).href(src.get_name()) + " to " text += coord(des.x, des.y).href(des.get_name()) + "
" if ( des.output.len() == 0 ) { build_check_month = world.get_time().ticks + (3 * world.get_time().ticks_per_month) //gui.add_message_at(our_player, "### " + des.get_name() + " ## end consumer set build_check_month = " + build_check_month, world.get_time()) } else { build_check_month = world.get_time().ticks + world.get_time().ticks_per_month //gui.add_message_at(our_player, "### " + des.get_name() + " ## set build_check_month = " + build_check_month, world.get_time()) } break case industry_link_t.st_missing: link_list[k].next_check = today_plus_months(3) //gui.add_message_at(our_player, "### link " + k + " ## state = " + state, world.get_time()) break case industry_link_t.st_failed: link_list[k].next_check = today_plus_months(12) link_list[k].state = 4 //gui.add_message_at(our_player, "### link " + k + " ## state = " + state, world.get_time()) } } function get_link_state(src, des, fre) { local link = access_link(src, des, fre) return link ? link.state : industry_link_t.st_free } /* * select * 0 - build_cost_link * 1 - build_cost_rail * 2 - build_cost_road * 3 - build_cost_ship * */ function set_link_build_cost(src, des, fre, cost, select) { local k = key(src, des, fre) local l = industry_link_t(src, des, fre) local i = 0 switch(select) { case 0: try { link_list[k].build_cost_link = cost } catch(ev) { // not existing - create entry l.build_cost_link = cost i = 1 } break case 1: try { link_list[k].build_cost_rail = cost } catch(ev) { // not existing - create entry l.build_cost_rail = cost i = 1 } break case 2: try { link_list[k].build_cost_road = cost } catch(ev) { // not existing - create entry l.build_cost_road = cost i = 1 } break case 3: try { link_list[k].build_cost_ship = cost } catch(ev) { // not existing - create entry l.build_cost_ship = cost i = 1 } break } if ( i == 1 ) { link_list[k] <- l } } function get_link_build_cost(src, des, fre, select) { local link = access_link(src, des, fre) switch(select) { case 0: return link.build_cost_link break case 1: return link.build_cost_rail break case 2: return link.build_cost_road break case 3: return link.build_cost_ship break } } function set_combined_link(src, des, fre, state) { local k = key(src, des, fre) try { link_list[k].combined_link = state } catch(ev) { // not existing - create entry local l = industry_link_t(src, des, fre) l.combined_link = state link_list[k] <- l } } function get_combined_link(src, des, fre) { local link = access_link(src, des, fre) return link.combined_link } function access_link(src, des, fre) { local k = key(src, des, fre) local res try { res = link_list[k] } catch(ev) { res = null } return res } /** * Loop through all links. */ function work() { month_check_message() set_map_vehicles_counts() // iterate the link_iterator, which is a generator if (link_iterator == null) { // this is a generator link_iterator = link_iteration() } if (link_iterator.getstatus() != "dead") { resume link_iterator } else { link_iterator = null return r_t(RT_SUCCESS); } return r_t(RT_PARTIAL_SUCCESS); } function link_iteration() { foreach(link in link_list) { if (!check_link(link)) { continue } yield link } } /** * Check link: * - if state is st_missing set state to st_free after some time * - for working links see after their lines * - @returns true if some work was done */ function check_link(link) { //gui.add_message_at(our_player, "#######" + link.f_src + " - " + link.f_src.get_name() + " - link.state " + link.state, world.get_time()) switch(link.state) { case industry_link_t.st_free: break case industry_link_t.st_planned: return false case industry_link_t.st_built: if (link.lines.len()==0) return false break case industry_link_t.st_failed: break case industry_link_t.st_missing: if (link.next_check >= world.get_time().ticks) return false // try to plan again link.state = industry_link_t.st_free link.next_check = 0 return false } // iterate through all lines foreach(index, line in link.lines) { if ( line.is_valid() ) { //gui.add_message_at(our_player, "####### valid line " + line.get_name(), world.get_time()) if ( line.next_vehicle_check < world.get_time().ticks ) { check_link_line(link, line) } } else { //gui.add_message_at(our_player, "####### invalid line " + line, world.get_time()) link.lines.remove(index) } } return true } /** * * * */ function check_pl_lines() { local line_list = player_x(our_player.nr).get_line_list() /* f_src = s f_dest = d freight = good_desc_x(f) */ local print_message_box = 0 local pl_lines = [] local line_ai_count = 0 foreach(link in link_list) { //gui.add_message_at(player_x(our_player.nr), " ####### link check: f_src " + link.f_src.get_name() + " f_dest " + link.f_dest.get_name() + " freight " + link.freight.get_name(), world.get_time()) foreach(index, line in link.lines) { //gui.add_message_at(player_x(our_player.nr), "####### line check " + line.get_name(), world.get_time()) if ( line.is_valid() && line.get_owner().nr == our_player.nr ) { line_ai_count++ pl_lines.append(line) } } } local ai_lines_missing = [] if ( pl_lines.len() > 0 ) { foreach(line in line_list) { local c = 0 for ( local i = 0; i < pl_lines.len(); i++ ) { if ( line.get_name() == pl_lines[i].get_name() ) { c = 1 break } } if ( c == 0 ) { ai_lines_missing.append(line) } } } //::debug.pause() //gui.add_message_at(player_x(our_player.nr), " ####### line check: line_list.get_count() " + line_list.get_count() + " ## line_ai_count " + line_ai_count, world.get_time()) // if ( line_list.get_count() > line_ai_count ) { for ( local i = 0; i < ai_lines_missing.len(); i++ ) { // rename line local line_name = ai_lines_missing[i].get_name() local str_search = ") " + translate("Line") local st_names = ai_lines_missing[i].get_schedule().entries local wt_name = ["", "road", "Train", "Ship"] local f_src = st_names[0].get_halt(our_player).get_factory_list() local f_dest = st_names[st_names.len()-1].get_halt(our_player).get_factory_list() local freight = "" if ( f_src.len() == 1 && f_dest.len() == 1 ) { // list output goods local good_list_out = [] foreach(good, islot in f_src[0].output) { good_list_out.append(good) } if ( good_list_out.len() == 1 ) { // one good output freight = good_list_out[0] } else { // more output goods - check good to f_dest //local good_list_in = [] foreach(good, islot in f_dest[0].input) { //good_list_in.append(good) for ( local i = 0; i < good_list_out.len(); i++ ) { if ( good_list_out[i] == good && freight == "" ) { freight = good } } } // todo more goods check } } else if ( f_src.len() == 0 || f_dest.len() == 0 ) { // combined line if ( f_src.len() == 0 && f_dest.len() == 1 ) { local connect_lines = st_names[0].get_halt(our_player).get_line_list() local cline_list = [] foreach(line in connect_lines) { if ( line.get_name() != line_name ) { cline_list.append(line) } } if ( cline_list.len() == 1 ) { local cline_st_names = cline_list[0].get_schedule().entries f_src = cline_st_names[0].get_halt(our_player).get_factory_list() if ( f_src.len() == 1 ) { local good_list_out = [] foreach(good, islot in f_src[0].output) { good_list_out.append(good) } if ( good_list_out.len() == 1 ) { // one good output freight = good_list_out[0] } else { // more output goods - check good to f_dest //local good_list_in = [] foreach(good, islot in f_dest[0].input) { ///good_list_in.append(good) for ( local i = 0; i < good_list_out.len(); i++ ) { if ( good_list_out[i] == good && freight == "" ) { freight = good } } } // todo more goods check } } } } }// todo check by more factorys // rename standard line name '(x) Line' if ( line_name.find(str_search) != null && freight != "" ) { local new_name = translate(wt_name[ai_lines_missing[i].get_waytype()]) + " " + translate(freight) + " " + st_names[0].get_halt(our_player).get_name() + " - " + st_names[st_names.len()-1].get_halt(our_player).get_name() //gui.add_message_at(player_x(our_player.nr), our_player.get_name() + " ####### line rename: " + line_name, world.get_time()) //gui.add_message_at(player_x(our_player.nr), our_player.get_name() + " ####### new name: " + new_name, world.get_time()) ai_lines_missing[i].set_name(new_name) } if ( freight != "" ) { //missing line add ai line list industry_manager.set_link_state(f_src[0], f_dest[0], freight, industry_link_t.st_built) industry_manager.access_link(f_src[0], f_dest[0], freight).append_line(ai_lines_missing[i]) } } } if ( line_list.get_count() > line_ai_count ) { if ( print_message_box == 1 ) { gui.add_message_at(player_x(our_player.nr), our_player.get_name() + " ####### line check: not all listet in ai lines ", world.get_time()) gui.add_message_at(player_x(our_player.nr), " ####### line check: line_list.get_count() " + line_list.get_count() + " ## line_ai_count " + line_ai_count, world.get_time()) } for ( local i = 0; i < ai_lines_missing.len(); i++ ) { gui.add_message_at(player_x(our_player.nr), "####### line missing " + ai_lines_missing[i].get_name(), world.get_time()) } } } /** * Manages convoys of one line: withdraw if there are too many, build new ones, upgrade to newer vehicles */ function check_link_line(link, line) { if ( debug ) ::debug.set_pause_on_error(true) // set first run as line build time /*if ( line.build_line == 0 ) { line.build_line = world.get_time() }*/ // set next check line.next_check = world.get_time().ticks + world.get_time().ticks_per_month // 0 = off // 1 // 2 = station expand // 3 // 4 = cnv status retired / all to old // 5 = electrified // 6 and pl 4 local print_message_box = 0 local wt = line.get_waytype() local passed_cnv_max = 0 // find route local nexttile = [] if (wt != wt_water && wt != wt_air) { local entries = line.get_schedule().entries local start = null local end = null if ( entries.len() >= 2 ) { start = tile_x(entries[0].x, entries[0].y, entries[0].z) end = tile_x(entries[entries.len()-1].x, entries[entries.len()-1].y, entries[entries.len()-1].z) } local asf = astar_route_finder(wt) local result = asf.search_route([start], [end]) // result is contains routes-array or error message // route is backward from end to start if ("err" in result) { gui.add_message_at(our_player, " ### no route found: " + result.err, start) gui.add_message_at(our_player, " ### line: " + line.get_name(), world.get_time()) gui.add_message_at(our_player, " ### start: " + coord3d_to_string(start) + " ### end: " + coord_to_string(end), start) return //nexttile } else { //gui.add_message_at(our_player, " ### route found: length = " + result.routes.len(), start) // route found, mark tiles foreach(node in result.routes) { local tile = tile_x(node.x, node.y, node.z) nexttile.append(tile) if ( wt == wt_road ) { local way = tile.get_way(wt) local passed_cnv = way.get_convoys_passed() if ( passed_cnv_max < passed_cnv[1] ) { passed_cnv_max = passed_cnv[1] } } } sleep() } } dbgprint("Check line " + line.get_name()) if ( our_player.nr == 6 && print_message_box == 6 ) { gui.add_message_at(our_player, "Check line " + line.get_name(), world.get_time()) } local line_new = 0 local bilanz_year = 0 if ( line.get_owner().nr == our_player.nr ) { // non profit in 5 months then destroy line //cnv.get_distance_traveled_total() > 3 && local profit_count = line.get_profit() //if ( cnv.get_distance_traveled_total() < 3 ) { return } local month_check = 0 for ( local i = 0; i < 12; i++ ) { bilanz_year += profit_count[i] if ( our_player.nr == 3 && print_message_box == 6 ) { //gui.add_message_at(our_player, "bilanz_year += profit_count["+i+"] " + bilanz_year, world.get_time()) } if ( i >= 4 ) { month_check += profit_count[i] } } if ( bilanz_year < 0 ) { /* gui.add_message_at(our_player, "Check line " + line.get_name(), world.get_time()) gui.add_message_at(our_player, "bilanz_year " + bilanz_year, world.get_time()) gui.add_message_at(our_player, "month_check " + month_check, world.get_time()) //::debug.pause()*/ } //profit_count[4] <= 0 && if ( profit_count[3] <= 0 && profit_count[2] <= 0 && profit_count[1] <= 0 && profit_count[0] <= 0 && month_check != 0) { //line.get_traveled_distance() > 1 && line.get_traveled_distance() < 25 && line.get_loading_level() == 0 && local chk_f_link = check_factory_links(link.f_src, link.f_dest, link.freight.get_name()) if ( bilanz_year <= 0 && print_message_box > 0 ) { gui.add_message_at(our_player, "(488) : Check line " + line.get_name(), world.get_time()) gui.add_message_at(our_player, "(488) : chk_f_link " + chk_f_link, world.get_time()) gui.add_message_at(our_player, "(488) : line.destroy_line_month " + line.destroy_line_month + " -#- " + (world.get_time().month+1), world.get_time()) //::debug.pause() } if ( line.destroy_line_month != (world.get_time().month+1) && (chk_f_link > 1 || (chk_f_link == 1 && link.f_src.get_suppliers().len() == 0 && link.f_dest.get_consumers().len() == 0) || (bilanz_year <= 0 && chk_f_link > 1)) ) { local erreg = destroy_line(line, link.freight, link) //gui.add_message_at(our_player, "(495) : destroy_line(line, link.freight, link) = " + erreg, world.get_time()) if ( erreg == false ) { line.destroy_line_month = world.get_time().month } else if ( erreg == true ) { link.state = 4 return } else if ( erreg == null ) { link.state = 4 return } // line not complete local entries = line.get_schedule().entries local start_l = null local end_l = null if ( wt == wt_water ) { if ( entries.len() >= 2 ) { local start_h = entries[0].get_halt(our_player) local end_h = entries[entries.len()-1].get_halt(our_player) local t = start_h.get_tile_list() start_l = tile_x(t[0].x, t[0].y, t[0].z) t = end_h.get_tile_list() end_l = tile_x(t[0].x, t[0].y, t[0].z) } } else { if ( entries.len() >= 2 ) { start_l = tile_x(entries[0].x, entries[0].y, entries[0].z) end_l = tile_x(entries[entries.len()-1].x, entries[entries.len()-1].y, entries[entries.len()-1].z) } } if ( test_halt_waytypes(end_l) == 1 && end_l.get_halt().get_factory_list().len() == 0 ) { local erreg = destroy_line(line, link.freight, link) /*if ( bilanz_year < 0 ) { gui.add_message_at(our_player, "line 490 : erreg = destroy_line " + erreg, world.get_time()) }*/ if ( erreg == false ) { line.destroy_line_month = world.get_time().month } else if ( erreg == true ) { link.state = 4 return } } } else { //gui.add_message_at(our_player, "return cnv/line new " + line.get_name(), world.get_time()) } if ( month_check == 0 ) { line_new = 1 } } } // find convoy local cnv = null local cnv_count = 0 local cnv_max_speed = 0 local cnv_retired = [] local new_cnv_add_line = false local stucked_cnv = [] { local list = [] // remove withdrawn convois foreach(cnv in line.get_convoy_list()) { if (cnv.is_valid() && !cnv.is_withdrawn()) { list.append(cnv); } } cnv_count = list.len() if (cnv_count == 0 || ( bilanz_year == 0 && cnv_count == 1 )) { // 0 convoy destroy line // 1 convoy and profit year 0 if ( line.get_owner().nr == our_player.nr ) { destroy_line(line, link.freight, link) sleep() } /*if ( bilanz_year < 0 ) { gui.add_message_at(our_player, "line 536 : destroy_line ", world.get_time()) }*/ return } local entries = line.get_schedule().entries local start_h = entries[0].get_halt(our_player) local end_l = tile_x(entries[entries.len()-1].x, entries[entries.len()-1].y, entries[entries.len()-1].z) local start_l = tile_x(entries[0].x, entries[0].y, entries[0].z) // remove stucked convoy // cnv_count > 10 then remove cnv_count/2 local cnv_remove_count = cnv_count if ( cnv_count > 10 ) { cnv_remove_count = abs(cnv_count / 2) //gui.add_message_at(our_player, "(738) #### cnv_remove_count " + cnv_remove_count, world.get_time()) } local loading_cnv_stucked = 0 for ( local i = 0; i < cnv_count; i++ ) { if ( !list[i].is_withdrawn() && cnv == null ) { cnv = list[i] } // stucked convoy destroy local d = list[i].get_traveled_distance() if ( list[i].get_distance_traveled_total() > 0 && d[0] == 0 && d[1] == 0 && list[i].is_loading() == false && cnv_count > 1 && list[i].get_loading_level() == 0 && stucked_cnv.len() <= cnv_remove_count ) { //gui.add_message_at(our_player, "####### destroy stucked road vehicles " + cnv_count, world.get_time())list[i].get_waytype() == wt_road && stucked_cnv.append(list[i]) //remove_cnv++ } // destroy convoys in depots if ( list[i].is_in_depot() ) { stucked_cnv.append(list[i]) } // destroy no waiting goods if ( start_h != null ) { local d = start_h.get_waiting() if ( d[0] == 0 && list[i].is_loading() == false && stucked_cnv.len() <= cnv_remove_count && list[i].get_loading_level() == 0 ) { //gui.add_message_at(our_player, "(605) ####### " + start_h.get_name() + " - destroy waiting road vehicles ", world.get_time()) stucked_cnv.append(list[i]) //remove_cnv++ } } // stucked loaded convoy if ( d[0] == 0 && d[1] == 0 && list[i].get_loading_level() > 0 && loading_cnv_stucked == 0 && line.get_waytype() == wt_road ) { loading_cnv_stucked = 1 //gui.add_message_at(our_player, "(768) #### loading convoy stucked ", world.get_time()) local traffic_obj = find_signal("traffic_light", line.get_waytype()) if ( traffic_obj != null ) { // end_l local asf = astar_route_finder(wt) local result = asf.search_route([list[i].get_pos()], [end_l]) local route_tile = [] if ("err" in result) { } else { foreach(node in result.routes) { local tile = tile_x(node.x, node.y, node.z) route_tile.append(tile) } sleep() } local traffic_build = 0 for ( local s = 0; s < route_tile.len(); s++ ) { local test_way = route_tile[s].find_object(mo_way) local test_traffic_light = route_tile[s].find_object(mo_roadsign) if ( test_traffic_light == null && dir.is_threeway(route_tile[s].get_way_dirs(wt_road)) && (test_way.get_owner().nr == our_player_nr || test_way.get_owner().nr == 1) ) { local err = command_x.build_sign_at(our_player, route_tile[s], traffic_obj) traffic_build++ } else if ( test_traffic_light != null ) { traffic_build++ } if ( traffic_build >= 2 ) { continue } } } } else if ( wt == wt_road ) { local cnv_tile = list[i].get_pos() // + [1] local way = tile_x(cnv_tile.x, cnv_tile.y, cnv_tile.z).get_way(wt) local passed_cnv = way.get_convoys_passed() /*if ( passed_cnv_max < passed_cnv[1] ) { passed_cnv_max = passed_cnv[1] }*/ // end_l local asf = astar_route_finder(wt) local result = asf.search_route([list[i].get_pos()], [start_l]) if ("err" in result) { } else { if ( result.routes.len() < 5 && passed_cnv[1] > 50 && list[i].get_loading_level() == 0 ) { //gui.add_message_at(our_player, "(768) #### cnv tille " + coord3d_to_string(cnv_tile) + " - ", cnv_tile) //gui.add_message_at(our_player, "(768) #### cnv tille cnv passed " + passed_cnv[1], cnv_tile) stucked_cnv.append(list[i]) } } } } //gui.add_message_at(our_player, "(835) #### stucked_cnv.len() " + stucked_cnv.len(), world.get_time()) // stucked vehicle remove, not last vehicle if ( stucked_cnv.len() > 0 ) { local j = stucked_cnv.len() if ( cnv_count == j ) { j-- } for ( local i = 0; i < j; i++ ) { if ( stucked_cnv[i].is_valid() ) { stucked_cnv[i].destroy(our_player) } } sleep() local road_car_rate = set_map_vehicles_counts(1) local next_time = road_line_check if ( road_car_rate < 10 ) { next_time = road_line_check / 2 } if ( line.get_waytype() == wt_road ) { line.next_vehicle_check = world.get_time().ticks + next_time if ( cnv_remove_count < cnv_count ) { line.next_vehicle_check = world.get_time().ticks + (next_time * 4) } local a = 2 if ( line.stuck_check == 1 ) { // small check intervall local time_c = abs(next_time / 4) if ( passed_cnv_max > 100 ) { time_c = abs(next_time / 8) a = 4 } else if ( passed_cnv_max > 50 ) { time_c = abs(next_time / 6) a = 3 } //gui.add_message_at(our_player, "**** line 870 - stuck_check = 1 : " + time_c, world.get_time()) line.next_vehicle_check = world.get_time().ticks + (time_c) } // set longer time for add convoy line.add_convoy_time = line.next_vehicle_check + (world.get_time().ticks_per_month * a) //gui.add_message_at(our_player, "#656# line.add_convoy_time : " + line.add_convoy_time + " -- a : " + a, world.get_time()) } else { line.next_vehicle_check = world.get_time().ticks + (world.get_time().ticks_per_month * 2) // set longer time for add convoy by remove convoy in last 6 month local cnv_line = line.get_convoy_count() local a = 0 for ( local x = 1; x < 8; x++ ) { if ( cnv_line[x] <= cnv_line[0] ) { a++ } } line.add_convoy_time = line.next_vehicle_check + (world.get_time().ticks_per_month * a) //gui.add_message_at(our_player, "#656# line.add_convoy_time : " + line.add_convoy_time + " -- a : " + a, world.get_time()) } local msgtext = format(translate("%s removes convoys from line: %s"), our_player.get_name(), line.get_name()) gui.add_message_at(our_player, msgtext, world.get_time()) return } if ( line_new == 1 ) { return } // check speed from convoys local a = convoy_max_speed(list[0]) cnv_max_speed = a // check cnv is retired first cnv if ( list[0].has_obsolete_vehicles() && !list[0].is_withdrawn() ) { if ( print_message_box == 4 ) { gui.add_message_at(our_player, " retired convoy " + list[0].get_name(), world.get_time()) } cnv_retired.append(list[0]) } local veh_count_line = line.get_convoy_count() if ( veh_count_line[0] > veh_count_line[1] ) { // not remove cnv from line by add cnv this month new_cnv_add_line = true //gui.add_message_at(our_player, "####### cnv new this month ", world.get_time()) } //&& !new_cnv_add_line local speed_count = 1 for ( local i = 1; i < cnv_count; i++ ) { local s = convoy_max_speed(list[i]) if ( a == s ) { speed_count++ } else if ( a < s ) { cnv_max_speed = s } // check cnv is retired all cnv if ( list[i].has_obsolete_vehicles() && !list[i].is_withdrawn() ) { if ( print_message_box == 4 ) { gui.add_message_at(our_player, " retired convoy " + list[i].get_name(), world.get_time()) } cnv_retired.append(list[i]) } } //gui.add_message_at(our_player, " max speed " + cnv_max_speed, world.get_time()) if ( speed_count < cnv_count && wt != wt_rail ) { // update convoys - retire slower convoys local k = 0 for ( local i = 0; i < cnv_count; i++ ) { if ( convoy_max_speed(list[i]) < cnv_max_speed && !list[i].is_withdrawn() ) { list[i].toggle_withdraw(our_player) k++ } } if ( k > 0 ) { local msgtext = format(translate("vehicles of the line %s were retired"), line.get_name()) gui.add_message_at(our_player, msgtext, world.get_time()) return } } else if ( cnv_count == cnv_retired.len() ) { // all cnv retired if ( print_message_box == 4 ) { gui.add_message_at(our_player, " all convoys to old -> upgrade_link_line", world.get_time()) } upgrade_link_line(link, line, cnv_max_speed, nexttile) return } if ( cnv_retired.len() > 0 && cnv_retired.len() < cnv_count ) { // update convoys - retire retired convoys local k = 0 for ( local i = 0; i < cnv_retired.len(); i++ ) { if ( !cnv_retired[i].is_withdrawn() ) { cnv_retired[i].toggle_withdraw(our_player) k++ } } if ( k > 0 ) { local msgtext = format(translate("vehicles of the line %s were retired"), line.get_name()) gui.add_message_at(our_player, msgtext, world.get_time()) return } } else if ( cnv_retired.len() == 1 && cnv_retired.len() == cnv_count ) { //gui.add_message_at(our_player, "*** cnv_upgrade = 1 -> line " + line.get_name(), world.get_time()) // create new convoy before retire retired convoy link.next_check = today_plus_months(1) if ( upgrade_link_line(link, line, cnv_max_speed, nexttile) ) { // update successful return } } } sleep() if ( cnv == null || !cnv.is_valid() ) { return } // find route //local nexttile = [] local nexttile_r = [] if (wt != wt_water && wt != wt_air) { local entries = cnv.get_schedule().entries local start = null local end = null if ( entries.len() >= 2 ) { start = tile_x(entries[0].x, entries[0].y, entries[0].z) end = tile_x(entries[entries.len()-1].x, entries[entries.len()-1].y, entries[entries.len()-1].z) } local asf = astar_route_finder(wt) if ( wt == wt_rail && line.double_ways_build > 0 ) { // return way for upgrade double ways local result = asf.search_route([end], [start]) if ("err" in result) { } else { foreach(node in result.routes) { local tile = tile_x(node.x, node.y, node.z) nexttile_r.append(tile) } sleep() } } } // check bridges other player in route if ( line.line_bridges.len() > 0 ) { local g = line.line_bridges for ( local x = 0; 0 < g.len(); x++ ) { local gt = g[x] if ( gt[0].is_bridge == false && gt[1].is_bridge == false ) { local bridge_obj = find_object("bridge", wt, line.line_way_speed) command_x.build_bridge(our_player, gt[0], gt[1], bridge_obj) if ( debug ) gui.add_message_at(our_player, "**** line 800 - build missing bridge in route ## ", gt[0]) } } //if ( debug ) gui.add_message_at(our_player, "**** line 816 - line_bridges_count = " + line.line_bridges_count + " ## ", g[0]) } if ( wt == wt_road && line.stuck_check == 0 ) { local way_count = nexttile.len() for ( local x = 1; x < 5; x++ ) { local t = nexttile[way_count-x]; local d = t.get_way_dirs(wt) if ( dir.is_threeway(d) ) { line.stuck_check = 1 gui.add_message_at(our_player, "**** line 1009 - set line.stuck_check = 1 ", t) break } } } // count bridges from other player if ( wt == wt_road && line.line_bridges_count == 0 ) { local tiles = nexttile local bridge_start = null local bridge_end = null for ( local x = 0; x < tiles.len(); x++ ) { if ( tiles[x].is_bridge() && bridge_start != null ) { local test_bridge = tiles[x].find_object(mo_bridge) //.get_desc() if ( test_bridge.get_owner().nr != our_player_nr && bridge_start == null ) { bridge_start = tiles[x] } } else if ( bridge_start != null && tiles[x].is_bridge() == false ) { bridge_end = tiles[x-1] line.line_bridges.append([bridge_start, bridge_end]) line.line_bridges_count++ bridge_start = null bridge_end = null } } if ( line.line_bridges.len() > 0 ) { local g = line.line_bridges[0] if ( debug ) gui.add_message_at(our_player, "**** line 828 - line_bridges_count = " + line.line_bridges_count + " ## ", g[0]) } } if (our_player.get_current_cash() > 50000 && wt != wt_water && wt != wt_air) { if ( line.optimize_way_line == 0 ) { // optimize way line befor build double ways local err = optimize_way_line(nexttile, wt, line.optimize_way_line, line) if ( err ) { line.optimize_way_line = 1 } } else if ( line.optimize_way_line == 1 && our_player.get_current_cash() > 500000 ) { local err = optimize_way_line(nexttile, wt, line.optimize_way_line, line) if ( err ) { line.optimize_way_line = 2 } } else if ( line.optimize_way_line == 2 && our_player.get_current_cash() > 1000000 ) { local err = optimize_way_line(nexttile, wt, line.optimize_way_line, line) if ( err ) { line.optimize_way_line = 3 } } else if ( line.optimize_way_line == 3 && our_player.get_current_cash() > 1000000 && world.get_time().year >= 1950 ) { local err = optimize_way_line(nexttile, wt, line.optimize_way_line, line) if ( err ) { line.optimize_way_line = 4 } } } if ( bilanz_year < 0 ) { //gui.add_message_at(our_player, " line 756 - bilanz_year < 0", world.get_time()) //::debug.pause() } // way speed //gui.add_message_at(our_player, " -##- " + link.line_way_speed + " old speed save line " + line.get_name(), world.get_time()) //gui.add_message_at(our_player, " cnv max speed " + cnv_max_speed, world.get_time()) if ( cnv_max_speed > line.line_way_speed && nexttile.len() > 3 ) { local way_speed = 500 local upgrade_tiles = 0 for ( local i = 0; i < nexttile.len(); i++ ) { local tile = tile_x(nexttile[i].x, nexttile[i].y, nexttile[i].z) local tile_way = tile.find_object(mo_way) if ( tile_way != null && (tile_way.get_owner().nr == our_player_nr || tile_way.get_owner().nr == 1) && ( !tile.has_two_ways() && !tile.is_bridge() ) ) { upgrade_tiles++ if ( tile_way.get_desc().get_topspeed() < way_speed ) { way_speed = tile_way.get_desc().get_topspeed() } if ( tile_way.get_desc().get_topspeed() == 45 && print_message_box == 1 ) { gui.add_message_at(our_player, way_speed + " way speed tile " + coord3d_to_string(tile_x(nexttile[i].x, nexttile[i].y, nexttile[i].z)), tile_x(nexttile[i].x, nexttile[i].y, nexttile[i].z)) } } } // check double ways by rail if ( wt == wt_rail && line.double_ways_build > 0 ) { for ( local i = 0; i < nexttile_r.len(); i++ ) { local tile = tile_x(nexttile_r[i].x, nexttile_r[i].y, nexttile_r[i].z) local tile_way = tile.find_object(mo_way) if ( tile_way.get_owner().nr == our_player_nr ) { //upgrade_tiles++ if ( tile_way.get_desc().get_topspeed() < way_speed && ( !tile.has_two_ways() && !tile.is_bridge() ) ) { way_speed = tile_way.get_desc().get_topspeed() } } } } line.line_way_speed = way_speed //gui.add_message_at(our_player, way_speed + " way speed line " + line.get_name(), world.get_time()) //gui.add_message_at(our_player, upgrade_tiles + " possible tiles for upgrading ", world.get_time()) //gui.add_message_at(our_player, " cnv max speed " + cnv_max_speed, world.get_time()) // upgrade way local way_obj = find_object("way", wt, cnv_max_speed) //gui.add_message_at(our_player, " way max speed new " + way_obj.get_topspeed(), world.get_time()) if ( cnv_max_speed >= way_speed && upgrade_tiles > 2 ) { local costs = (upgrade_tiles*(way_obj.get_cost()/100)) local count_build = 0 if ( wt == wt_rail && line.double_ways_build > 0 ) { costs += line.double_ways_count*8*(way_obj.get_cost()/100) } if ( our_player.get_current_cash() > costs ) { for ( local i = 1; i < nexttile.len(); i++ ) { local tile_way_1 = tile_x(nexttile[i-1].x, nexttile[i-1].y, nexttile[i-1].z) local tile_way_2 = tile_x(nexttile[i].x, nexttile[i].y, nexttile[i].z) if ( tile_way_2.find_object(mo_way) != null && (tile_way_2.find_object(mo_way).get_owner().nr == our_player_nr || tile_way_2.find_object(mo_way).get_owner().nr == 1) && tile_way_2.find_object(mo_way).get_desc().get_topspeed() < way_obj.get_topspeed() ) { command_x.build_way(our_player, tile_way_1, tile_way_2, way_obj, true) count_build++ } } // build double ways by rail if ( wt == wt_rail && line.double_ways_build > 0 ) { for ( local i = 1; i < nexttile_r.len(); i++ ) { local tile_way_1 = tile_x(nexttile_r[i-1].x, nexttile_r[i-1].y, nexttile_r[i-1].z) local tile_way_2 = tile_x(nexttile_r[i].x, nexttile_r[i].y, nexttile_r[i].z) if ( tile_way_2.find_object(mo_way) != null && (tile_way_2.find_object(mo_way).get_owner().nr == our_player_nr || tile_way_2.find_object(mo_way).get_owner().nr == 1) && tile_way_2.find_object(mo_way).get_desc().get_topspeed() < way_obj.get_topspeed() ) { command_x.build_way(our_player, tile_way_1, tile_way_2, way_obj, true) count_build++ } } } } local start_l = nexttile[nexttile.len()-1] local end_l = nexttile[0] if (count_build > 0 ) { local msgtext = format(translate("%s extends the route from %s (%s) to %s (%s)"), our_player.get_name(), start_l.get_halt().get_name(), coord_to_string(start_l), end_l.get_halt().get_name(), coord_to_string(end_l)) gui.add_message_at(our_player, msgtext, start_l) //gui.add_message_at(our_player, " upgrade way ", world.get_time()) } line.line_way_speed = way_obj.get_topspeed() } else { // set new way speed by not upgrade way line.line_way_speed = way_obj.get_topspeed() } } /*if ( bilanz_year < 0 ) { gui.add_message_at(our_player, " line 849 ", world.get_time()) //::debug.pause() }*/ if (cnv.is_valid() && cnv.is_withdrawn()) { // come back later if ( print_message_box > 0 ) { gui.add_message_at(our_player, "cnv.is_valid() && cnv.is_withdrawn() ", world.get_time()) } return } // try to upgrade /* if (cnv.has_obsolete_vehicles() && link.next_check < world.get_time().ticks) { link.next_check = today_plus_months(1) if (upgrade_link_line(link, line)) { // update successful gui.add_message_at(our_player, "*** upgrade_link_line -> line " + line.get_name(), world.get_time()) return } } */ local lf = link.freight // capacity of convoy local capacity = 0 { //local lf = link.freight foreach(v in cnv.get_vehicles()) { local f = v.get_freight() if (lf.is_interchangeable(f)) { capacity += v.get_capacity() } } } dbgprint("Capacity of convoy " + cnv.get_name() + " = " + capacity) dbgprint("Speed of convoy " + cnv.get_name() + " = " + cnv.get_speed()) // iterate through schedule, check for available freight local freight_available = false local start_l = null local end_l = null { local entries = line.get_schedule().entries if ( entries.len() >= 2 ) { start_l = tile_x(entries[0].x, entries[0].y, entries[0].z) end_l = tile_x(entries[entries.len()-1].x, entries[entries.len()-1].y, entries[entries.len()-1].z) } local i = 0; while(i < entries.len() && !freight_available) { local entry = entries[i] // stations on schedule local halt = entry.get_halt(our_player) if (halt == null) continue // next station on schedule local nexthalt = null i++ while(i < entries.len()) { if (nexthalt = entries[i].get_halt(our_player)) break i++ } if (nexthalt == null) { nexthalt = entries[0].get_halt(our_player) } // freight available ? local freight_on_schedule = halt.get_freight_to_halt(lf, nexthalt) local capacity_halt = halt.get_capacity(lf) dbgprint("Freight from " + halt.get_name() + " to " + nexthalt.get_name() + " = " + freight_on_schedule) // either start is 2/3 full or more good available as one cnv can transport freight_available = (3*freight_on_schedule > 2*capacity_halt) || (freight_on_schedule > capacity); } } /*if ( bilanz_year < 0 ) { gui.add_message_at(our_player, " line 927 ", world.get_time()) //::debug.pause() }*/ // calc gain per month of one convoy local gain_per_m = 0 { local p = line.get_profit() gain_per_m = p.reduce(sum) / (p.len() * cnv_count) dbgprint("Gain pm = " + gain_per_m) } // check state if convoys (loading level, stopped, new) local cc_load = 0 local cc_stop = 0 local cc_new = 0 local cc_empty = 0 local cnv_empty_stopped = null //local remove_cnv = 0 { local list = line.get_convoy_list() //local halt_len = line.halt_length local first_cnv = null local message_show = 0 foreach(c in list) { if (!c.is_valid() || c.is_withdrawn()) { // will be withdrawn, ignore it continue } if ( first_cnv == null ) { first_cnv = c } // convoy empty? local is_empty = c.get_loading_level() == 0 // convoy new? less than 2 months old, and not much transported local d = c.get_traveled_distance() local is_new = (d[0] + d[1] == c.get_distance_traveled_total()) if (is_new) { local t = c.get_transported_goods(); if (t.reduce(sum) >= 2*capacity) { is_new = false } } if (is_new) { cc_new ++ is_empty = false } // new convoys do not count as empty if (is_empty) { cc_empty++ } // convoy stopped? but not for loading local is_stopped = false if (c.get_speed() == 0) { if (c.get_loading_limit() > 0) { // loading cc_load ++ } else { cc_stop ++ is_stopped = true; } } if (is_empty && is_stopped && cnv_empty_stopped==null) { //gui.add_message_at(our_player, "### list.len() " + list.get_count() + " ### first_cnv.get_tile_length() " + first_cnv.get_tile_length() + " ### c.get_tile_length() " + c.get_tile_length(), world.get_time()) if ( list.get_count() == 2 && first_cnv.get_tile_length() < c.get_tile_length() ) { first_cnv.toggle_withdraw(our_player) } else { cnv_empty_stopped = c } } /* // stucked road vehicles destroy if ( c.get_distance_traveled_total() > 0 && d[0] == 0 && d[1] == 0 && c.is_loading() == false && c.get_waytype() == wt_road && cnv_count > 1) { //gui.add_message_at(our_player, "####### destroy stucked road vehicles " + cnv_count, world.get_time()) c.destroy(our_player) cnv_count-- message_show++ //remove_cnv++ }*/ } /*if ( message_show > 0 ) { local msgtext = format(translate("%s removes convoys from line: %s"), our_player.get_name(), line.get_name()) gui.add_message_at(our_player, msgtext, world.get_time()) return true }*/ } dbgprint("Line: loading = " + cc_load + ", stopped = " + cc_stop + ", new = " + cc_new + ", empty = " + cc_empty) dbgprint("") if ( print_message_box == 1 ) { gui.add_message_at(our_player, "freight_available " + freight_available + " cc_new " + cc_new + " cc_stop " + cc_stop + " cnv.is_valid() " + cnv.is_valid(), world.get_time()) } if ( (freight_available && cc_new == 0 && cc_stop < 2 && cnv.is_valid()) || bilanz_year < 0 ) { if ( bilanz_year < 0 ) { //gui.add_message_at(our_player, " line 1029 - bilanz_year < 0", world.get_time()) //::debug.pause() } // stations distance local l = 0 if ( wt == wt_water || wt == wt_air ) { l = abs(start_l.x - end_l.x) + abs(start_l.y - end_l.y) } else { l = check_way_line(start_l, end_l, wt, 0, 0, line) } local c = 0 local t = l % 80 //gui.add_message_at(our_player, "#### way len " + l + " % 80 = " + (l % 80), world.get_time()) //gui.add_message_at(our_player, "#### way len " + l + " / 80 = " + (l / 80), world.get_time()) if ( l > 30 && l <= 90 ) { c = 1 } else if ( l > 70 && l <= 140 ) { c = 2 } else if ( l > 140 && l <= 200 ) { c = 3 } else if ( l > 200 && l <= 330 ) { c = 4 } else if ( l > 330 && l < 460 ) { c = 5 } else if ( l >= 460 ) { c = (l / 80) } // no signals and double tracks - limit 1 convoy for rail if (wt == wt_rail && cnv_count == 1 && c > 0 && line.double_ways_build == 0 ) { if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### cnv.get_waytype() " + cnv.get_waytype() + " cnv.name " + cnv.get_name(), world.get_time()) gui.add_message_at(our_player, "####### lenght " + l + " double ways " + c, world.get_time()) } // // check way for find fields for double track if ( line.optimize_way_line == 0 ) { local err = optimize_way_line(nexttile, wt, line.optimize_way_line, line) if ( err ) { line.optimize_way_line = 1 } } local s_fields = check_way_line(start_l, end_l, wt, l, c, line) local cc = 1 //gui.add_message_at(our_player, "####### type(s_fields) " + type(s_fields), world.get_time()) if ( type(s_fields) == "array" ) { //gui.add_message_at(our_player, "####### s_fields.len() " + s_fields.len(), world.get_time()) if ( s_fields.len() == 0 ) { s_fields = check_way_line(end_l, start_l, wt, l, c, line) } } /* * calculate build cost * */ local build_double_ways = false if ( s_fields != true && c > 0 && s_fields.len() > 0 ) { local obj_sign = find_signal("is_signal", wt) local way_obj = s_fields[0].find_object(mo_way).get_desc() local build_cost = s_fields.len()*(obj_sign.get_cost()*2) build_cost += s_fields.len()*(way_obj.get_cost()*8) // terraform 4 fields build_cost = build_cost+(command_x.slope_get_price(82)*4) // count double ways build_cost = build_cost*c if ( build_cost/100 < (our_player.get_current_cash()+(our_player.get_current_maintenance()/100*5)) ) { build_double_ways = true } else { //gui.add_message_at(our_player, "calculate build cost double ways " + build_cost/100 + " < " + (our_player.get_current_cash()+(our_player.get_current_maintenance()/100*5)), world.get_time()) } } //gui.add_message_at(our_player, "####### s_fields " + s_fields, world.get_time()) if ( s_fields == true ) { cc += c } else if ( build_double_ways == true ) { //gui.add_message_at(our_player, "####### s_fields.len() " + s_fields.len(), world.get_time()) local count_build = 0 local build = false if ( s_fields.len() == c || s_fields.len() == c - 1 ) { if ( s_fields.len() == c - 1 ) { c-- } // count station len start local station_len = 0 for ( local h = 0; h < 50; h++ ) { if ( nexttile[h].find_object(mo_building) != null ) { station_len++ } else { break } } station_len += 3 if ( station_len < 8 ) { station_len = 8 } for ( local i = 0; i < c; i++ ) { build = build_double_track(s_fields[i], wt_rail, station_len) if ( build ) { cc++ count_build++ build = false } } } if (count_build > 0 ) { local msgtext = format(translate("%s extends the route from %s (%s) to %s (%s)"), our_player.get_name(), start_l.get_halt().get_name(), coord_to_string(start_l), end_l.get_halt().get_name(), coord_to_string(end_l)) gui.add_message_at(our_player, msgtext, start_l) } } if ( cc > 1 ) { line.double_ways_count = cc line.double_ways_build = 1 } else { return } } else if (wt == wt_rail && (cnv_count > c || c == 0) ) { return null } //gui.add_message_at(our_player, " line 1384 " + line.get_name() + " - line.way_line_count : " + line.way_line_count + " - line.double_ways_count : " + line.double_ways_count, world.get_time()) if ( wt == wt_rail ) { if ( line.way_line_count == 1 ) { cnv_count = line.double_ways_count + 1 } else if ( line.way_line_count > 1 ) { if ( line.double_ways_count > 2 ) { cnv_count = line.double_ways_count - 1 } else { cnv_count = line.double_ways_count } } //gui.add_message_at(our_player, " line set cnv_count " + cnv_count, world.get_time()) } if ( bilanz_year < 0 ) { //gui.add_message_at(our_player, " line 1149 ", world.get_time()) //gui.add_message_at(our_player, " gain_per_m " + gain_per_m, world.get_time()) //::debug.pause() } // pak192.comic fix // line profit < 0 then not add cnv count > 3 // wt_water local line_profit = line.get_profit() //local line_cnv_count = line.get_convoy_count() //if ( wt == wt_water ) gui.add_message_at(our_player, "#1390# test (line_profit[1] + line_profit[2]) " + (line_profit[1] + line_profit[2]), world.get_time()) //if ( wt == wt_water ) gui.add_message_at(our_player, "#1390# test line.get_convoy_count() " + line_cnv_count[0], world.get_time()) //if ( wt == wt_water ) gui.add_message_at(our_player, "#1390# test cnv_count " + cnv_count, world.get_time()) if ( (line_profit[1] + line_profit[2]) < 0 && cnv_count >= 3 && wt == wt_water ) { //gui.add_message_at(our_player, "#1390# break add cnv ", world.get_time()) line.next_vehicle_check = line.next_vehicle_check + (world.get_time().ticks_per_month * 2) return true } if ( (gain_per_m > 0 || bilanz_year < 0 ) && line.next_vehicle_check < world.get_time().ticks && line.add_convoy_time < world.get_time().ticks ) { // directly append // TODO put into report if ( bilanz_year < 0 ) { // gui.add_message_at(our_player, " line 1161 ", world.get_time()) //::debug.pause() } local proto = cnv_proto_t.from_convoy(cnv, lf) if ( print_message_box == 1 ) { gui.add_message_at(our_player, "###---- check convoys line : " + line.get_name(), world.get_time()) } // plan convoy prototype local freight = lf.get_name() local prototyper = prototyper_t(wt, freight) local proto_speed = cnv_max_speed - 30 if ( proto_speed >= 100 ) { proto_speed = 60 } if ( proto_speed < 1 ) { prototyper.min_speed = 1 } else { prototyper.min_speed = proto_speed } prototyper.max_vehicles = get_max_convoi_length(wt) local expand_station = [] //local station_count = 0 local station_exist = 0 local expand_station_end_src = [] local expand_station_end_dest = [] if ( wt == wt_rail ) { for ( local i = 0; i < 6; i++ ) { //gui.add_message_at(our_player, "###---- nexttile[" + i + "].find_object(mo_building) : " + nexttile[i].find_object(mo_building), world.get_time()) if ( nexttile[i].find_object(mo_building) != null ) { station_exist++ } } expand_station = check_expand_station(nexttile, line) if ( print_message_box == 2 && expand_station.len() > 0 ) { gui.add_message_at(our_player, "#1262##---- station_exist : " + station_exist + " ## expand_station.len() : " + expand_station.len(), world.get_time()) } } if ( expand_station.len() > 0 ) { prototyper.max_length = CARUNITS_PER_TILE * (station_exist + (expand_station.len()/2)) } else if ( wt != wt_road && wt != wt_air && wt != wt_water ) { prototyper.max_length = CARUNITS_PER_TILE * station_exist } else { prototyper.max_length = CARUNITS_PER_TILE //prototyper.max_vehicles * 8 } if ( print_message_box == 2 ) { //gui.add_message_at(our_player, "###---- station_exist : " + station_exist, world.get_time()) //gui.add_message_at(our_player, "###---- prototyper.max_length : " + prototyper.max_length, world.get_time()) } if ( wt == wt_road && check_good_quantity(start_l, end_l, lf, line) ) { line.next_vehicle_check = world.get_time().ticks + (road_line_check * 2) return true } local cnv_valuator = valuator_simple_t() cnv_valuator.wt = wt cnv_valuator.freight = freight local fd = link.f_dest local fs = link.f_src local freight_input = fd.input[freight].get_base_consumption() local freight_output = fs.output[freight].get_base_production() prototyper.volume = min(freight_input, freight_output) //cnv_valuator.volume = line.get_transported_goods().reduce(max) cnv_valuator.max_cnvs = 200 // no signals and double tracks - limit 1 convoy for rail if (wt == wt_rail) { cnv_valuator.max_cnvs = cnv_count } local dist = 0 if ( wt == wt_water || wt == wt_air ) { // through schedule to estimate distance local entries = cnv.get_schedule().entries dist = abs(entries[0].x - entries[1].x) + abs(entries[0].y - entries[1].y) } else { // distance real route dist = nexttile.len() } if (wt == wt_road ) { if ( dist <= 50) { cnv_valuator.max_cnvs = dist/3 } else if ( dist > 50 && dist <= 250 ) { cnv_valuator.max_cnvs = dist/2 } if ( passed_cnv_max > 100 ) { cnv_valuator.max_cnvs = dist/4 } //else if ( dist > 250 ) { cnv_valuator.max_cnvs = dist - 50 } if ( print_message_box == 1 ) { gui.add_message_at(our_player, "### line : " + line.get_name() + " dist: " + dist + " cnv max: " + cnv_valuator.max_cnvs, world.get_time()) } } // add 10% from distance //dist += dist / 100 * 10 if ( wt == wt_rail ) { prototyper.electrified = check_catenary(line, dist, start_l, cnv_max_speed) } cnv_valuator.distance = dist local bound_valuator = valuator_simple_t.valuate_monthly_transport.bindenv(cnv_valuator) prototyper.valuate = bound_valuator if (prototyper.step().has_failed()) { if ( print_message_box == 1 ) { gui.add_message_at(our_player, " ----> prototyper.step().has_failed() ", world.get_time()) } return null } local proto = prototyper.best if ( print_message_box == 1 ) { gui.add_message_at(our_player, " ----> proto : " + proto, world.get_time()) } // build convoy local stations_list = line.get_schedule().entries local depot = null //cnv.get_home_depot() for (local i=0; i station " + coord_to_string(c), c) gui.add_message_at(our_player, "####--> depot " + depot, world.get_time()) } break } if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####--> not depot found", world.get_time()) } } if ( depot == null || depot == false ) { //depot = cnv.get_home_depot() local c = cnv.get_home_depot() depot = tile_x(c.x, c.y, c.z) if ( depot.get_depot() == null ) { local way_tile = [null, null, null, null] way_tile[0] = tile_x(depot.x-1, depot.y, depot.z) way_tile[1] = tile_x(depot.x+1, depot.y, depot.z) way_tile[2] = tile_x(depot.x, depot.y-1, depot.z) way_tile[3] = tile_x(depot.x, depot.y+1, depot.z) for ( local i = 0; i < 4; i++ ) { if ( way_tile[i].find_object(mo_way) != null ) { local way_obj = find_object("way", wt, cnv.get_speed()) local depot_obj = building_desc_x.get_available_stations(building_desc_x.depot, wt, {}) local err = command_x.build_way(our_player, depot, way_tile[i], way_obj, true) if ( err == null ) { err = command_x.build_depot(our_player, depot, depot_obj[0] ) } else { gui.add_message_at(our_player, "##-ERROR-##--> not depot found", depot) return false } break } } } } local c = vehicle_constructor_t() c.p_depot = depot_x(depot.x, depot.y, depot.z) c.p_line = line c.p_convoy = proto local cnv_payload = c.p_convoy.capacity if ( wt == wt_road && cnv_payload < 30 ) { // by road add 3 vehicles c.p_count = 3 } else { c.p_count = 1 /*if ( int_li == cnv_count ) { c.p_count = 0 }*/ } local int_li = 0 if ( wt == wt_rail ) { local li = line.get_convoy_list() foreach(cnv in li) { if ( cnv.is_valid() ) { int_li++ } } } // input storage f_src = 0 or waiting good = 0 then not add convoys local entries = line.get_schedule().entries local start_h = entries[0].get_halt(our_player) local d = start_h.get_waiting() sleep() if ( (check_fsrc_input(link.f_src) || d[0] > 0) && int_li < cnv_count ) { append_child(c) if ( cnv_retired.len() == 1 && cnv_retired.len() == cnv_count ) { cnv_retired[0].toggle_withdraw(our_player) } dbgprint("==> build additional convoy") if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### cnv_count " + cnv_count, world.get_time()) gui.add_message_at(our_player, "Line: " + line.get_name() + " ==> build additional convoy", world.get_time()) } //if ( wt == wt_rail ) gui.add_message_at(our_player, "####### expand_station.len() " + expand_station.len(), world.get_time()) if ( wt == wt_rail && expand_station.len() > 0 ) { // tiles for convoy local a = c.p_convoy.length local st_lenght = 0 do { a -= 16 st_lenght += 1 } while(a > 0) // expand station local ret = build_expand_station(nexttile, expand_station, st_lenght, link.freight.get_name(), line) if ( ret ) { line.halt_length = st_lenght // set new line entries local entries = line.get_schedule().entries if ( entries.len() >= 2 ) { start_l = tile_x(entries[0].x, entries[0].y, entries[0].z) end_l = tile_x(entries[entries.len()-1].x, entries[entries.len()-1].y, entries[entries.len()-1].z) } } } if ( wt == wt_rail && proto.veh[0].needs_electrification() ) { build_catenary(start_l, end_l, depot, line) } local msgtext = format(translate("%s build additional convoy to line: %s"), our_player.get_name(), line.get_name()) gui.add_message_at(our_player, msgtext, world.get_time()) // save ticks for next check local road_car_rate = set_map_vehicles_counts(1) local next_time = road_line_check * 4 if ( road_car_rate < 10 ) { next_time = road_line_check * 2 } if ( wt == wt_road ) { line.next_vehicle_check = world.get_time().ticks + next_time } else { line.next_vehicle_check = world.get_time().ticks + (world.get_time().ticks_per_month * 2) } return true } else { line.next_vehicle_check = world.get_time().ticks + (world.get_time().ticks_per_month * 2) } } } /* local veh_count_line = line.get_convoy_count() local new_cnv_add_line = false if ( veh_count_line[0] > veh_count_line[1] ) { // not remove cnv from line by add cnv this month new_cnv_add_line = true //gui.add_message_at(our_player, "####### cnv new this month ", world.get_time()) } //!new_cnv_add_line && */ if ( !freight_available && cnv_count>1 && 2*cc_empty >= cnv_count && cnv_empty_stopped && line.next_vehicle_check < world.get_time().ticks && !new_cnv_add_line ) { // freight, lots of empty and of stopped vehicles // -> something is blocked, maybe we block our own supply? // delete one convoy if ( cnv_empty_stopped.is_valid() ) { cnv_empty_stopped.destroy(our_player) dbgprint("==> destroy empty convoy") if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### cnv_count " + cnv_count, world.get_time()) gui.add_message_at(our_player, "Line: " + line.get_name() + " ==> destroy empty convoy", world.get_time()) } local msgtext = format(translate("%s removes convoys from line: %s"), our_player.get_name(), line.get_name()) gui.add_message_at(our_player, msgtext, world.get_time()) // save ticks for next check local road_car_rate = set_map_vehicles_counts(1) local next_time = road_line_check if ( road_car_rate < 10 ) { next_time = road_line_check / 2 } if ( wt == wt_road ) { line.next_vehicle_check = world.get_time().ticks + next_time } else { line.next_vehicle_check = world.get_time().ticks + (world.get_time().ticks_per_month * 2) // set longer time for add convoy by remove convoy in last 6 month local cnv_line = line.get_convoy_count() local a = 0 for ( local x = 1; x < 8; x++ ) { if ( cnv_line[x] <= cnv_line[0] ) { a++ } } line.add_convoy_time = line.next_vehicle_check + (world.get_time().ticks_per_month * a) //gui.add_message_at(our_player, "#1511# line.add_convoy_time : " + line.add_convoy_time + " -- a : " + a, world.get_time()) } } } dbgprint("") if ( wt == wt_road ) { line.next_vehicle_check = world.get_time().ticks + road_line_check } else { line.next_vehicle_check = world.get_time().ticks + world.get_time().ticks_per_month } return true } /** * Upgrade: plan a new convoy type with the prototyper, then * sell existing convoys, create new ones. */ function upgrade_link_line(link, line, cnv_speed, route) { local print_message_box = 0 if ( print_message_box > 0 ) { gui.add_message_at(our_player, "### upgrade_link_line " + line.get_name(), world.get_time()) } //gui.add_message_at(our_player, "### cnv_speed " + cnv_speed, world.get_time()) // find convoy local cnv = null { local list = line.get_convoy_list() if (list.get_count() == 0) { // no convois - strange return false } cnv = list[0] } // estimate transport volume local transported = line.get_transported_goods().reduce(max) // plan convoy prototype local wt = line.get_waytype() local prototyper = prototyper_t(wt, link.freight.get_name()) // iterate through schedule to estimate distance local dist = 0 /*if ( wt != wt_water && wt != wt_air ) { dist = route.len() } else */ if ( route.len == 0 ) { local entries = line.get_schedule().entries local start_h = entries[0].get_halt(our_player) local end_h = entries[entries.len()-1].get_halt(our_player) local t = start_h.get_tile_list() start_l = tile_x(t[0].x, t[0].y, t[0].z) t = end_h.get_tile_list() end_l = tile_x(t[0].x, t[0].y, t[0].z) dist = abs(start_l.x - end_l.x) + abs(start_l.y - end_l.y) } else { dist = route.len() //dist = abs(route[0].x - route[route.len()-1].x) + abs(route[0].y - route[route.len()-1].y) } //local wt = wt // TODO do something smarter local proto_speed = cnv_speed - 25 if ( proto_speed >= 100 ) { proto_speed = 60 } if ( proto_speed < 1 ) { prototyper.min_speed = 1 } else { prototyper.min_speed = proto_speed } prototyper.max_vehicles = get_max_convoi_length(wt) prototyper.max_length = 1 local expand_station = [] if ( wt == wt_rail ) { expand_station = check_expand_station(route, line) if ( expand_station.len() > 0 ) { prototyper.max_length = CARUNITS_PER_TILE * (line.halt_length + (expand_station.len()/2)) } else { prototyper.max_length = CARUNITS_PER_TILE * line.halt_length } if ( print_message_box > 0 ) { gui.add_message_at(our_player, "### line.halt_length = " + line.halt_length + " ## expand_station.len() : " + expand_station.len(), world.get_time()) gui.add_message_at(our_player, "### prototyper.max_length = " + prototyper.max_length, world.get_time()) } prototyper.electrified = check_catenary(line, dist, route[route.len()-1], cnv_speed) } if (wt == wt_water) { prototyper.max_length = 4 } local cnv_valuator = valuator_simple_t() cnv_valuator.wt = wt cnv_valuator.freight = link.freight.get_name() cnv_valuator.volume = transported cnv_valuator.max_cnvs = 200 // no signals and double tracks - limit 1 convoy for rail if (wt == wt_rail) { cnv_valuator.max_cnvs = line.double_ways_count + 1 } cnv_valuator.distance = dist local bound_valuator = valuator_simple_t.valuate_monthly_transport.bindenv(cnv_valuator) prototyper.valuate = bound_valuator if (prototyper.step().has_failed()) { return false } local planned_convoy = prototyper.best // check whether different from current convoy local cnv_veh = cnv.get_vehicles() local pro_veh = planned_convoy.veh local different = cnv_veh.len() != pro_veh.len() for(local i=0; i 0 ) { // tiles for convoy local a = c.p_convoy.length local st_lenght = 0 do { a -= 16 st_lenght += 1 } while(a > 0) // expand station local ret = build_expand_station(route, expand_station, st_lenght, link.freight.get_name(), line) if ( ret ) { line.halt_length = st_lenght } } if ( wt == wt_rail && c.p_convoy.veh[0].needs_electrification() ) { build_catenary(route[0], route[route.len()-1], depot, line) } c.p_withdraw = true append_child(c) // save ticks for next check local road_car_rate = set_map_vehicles_counts(1) local next_time = road_line_check * 4 if ( road_car_rate < 10 ) { next_time = road_line_check * 2 } if ( wt == wt_road ) { line.next_vehicle_check = world.get_time().ticks + next_time } else { line.next_vehicle_check = world.get_time().ticks + (world.get_time().ticks_per_month * 2) } return true } // keys were broken for rotated maps, regenerate keys for all entries function repair_keys() { link_iterator = null local save_list = link_list link_list = {} foreach(link in save_list) { link_list[ key(link.f_src, link.f_dest, link.freight.get_name()) ] <- link } } function _save() { link_iterator = null // wont save return ::saveinstance("industry_manager_t", this) } function check_expand_station(nexttile, line) { // 0 = off // 1 // 2 = station expand // 3 local print_message_box = 0 local wt = line.get_waytype() local expand_station = [] local station_count = 0 local station_exist = 0 local expand_station_end_src = [] local expand_station_end_dest = [] // count exist station lenght for ( local i = 0; i < 6; i++ ) { if ( nexttile[i].find_object(mo_building) != null ) { station_count++ } } if ( line.halt_length == 0 ) { line.halt_length = station_count } // check expand station -> tiles direction end from way local tiles_end_src = [] local tiles_end_dest = [] local t = null // line end dest for ( local i = 1; i < 4; i++ ) { t = check_tile_end_of_station(nexttile[0].get_way_dirs(wt), i, nexttile[0]) if ( t != null ) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- check tile expand_station_end_dest : " + coord3d_to_string(t), t) } expand_station_end_dest.append(t) } else { break } } // line end src for ( local i = 1; i < 4; i++ ) { t = check_tile_end_of_station(nexttile[nexttile.len()-1].get_way_dirs(wt), i, nexttile[nexttile.len()-1]) if ( t != null ) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- check tile expand_station_end_src : " + coord3d_to_string(t), t) } expand_station_end_src.append(t) } else { break } } //prototyper.max_length = station_count line.halt_length = station_count station_exist = station_count local add_tile_src = 0 local add_tile_dest = 0 if ( station_count < 6 ) { // check expand station -> direction of the existing route/way // built cnv to new length end expand station befor create cnv //station_count++ for ( station_count; station_count < 6; station_count++ ) { //gui.add_message_at(our_player, "###---- nexttile[station_count-1] : " + coord3d_to_string(nexttile[station_count-1]) + " - " + nexttile[station_count-1].get_way_dirs(wt), nexttile[0]) //gui.add_message_at(our_player, "###---- nexttile[station_count] : " + coord3d_to_string(nexttile[station_count]) + " - " + nexttile[station_count].get_way_dirs(wt), nexttile[0]) //gui.add_message_at(our_player, "###---- nexttile[nexttile.len()-station_count-2] : " + coord3d_to_string(nexttile[nexttile.len()-station_count-1]) + " - " + nexttile[nexttile.len()-station_count-1].get_way_dirs(wt), nexttile[0]) //gui.add_message_at(our_player, "###---- nexttile[nexttile.len()-station_count-1] : " + coord3d_to_string(nexttile[nexttile.len()-station_count]) + " - " + nexttile[nexttile.len()-station_count].get_way_dirs(wt), nexttile[0]) /*if ( nexttile[station_count-1].get_way_dirs(wt) != nexttile[station_count].get_way_dirs(wt) || nexttile[nexttile.len()-station_count-1].get_way_dirs(wt) != nexttile[nexttile.len()-station_count].get_way_dirs(wt) || (nexttile[station_count].is_bridge() && nexttile[station_count].z == square_x(nexttile[station_count].x, nexttile[station_count].y).get_ground_tile().z) || (nexttile[nexttile.len()-station_count-1].is_bridge() && nexttile[nexttile.len()-station_count-1].z == square_x(nexttile[station_count].x, nexttile[nexttile.len()-station_count-1].y).get_ground_tile().z) ) { */ //if ( !test_field(our_player, nexttile[station_count], wt, nexttile[station_count].get_way_dirs(wt), square_x(nexttile[station_count].x, nexttile[station_count].y).get_ground_tile().z, 1) ) { local t1 = nexttile[station_count] local t2 = nexttile[(nexttile.len()-1)-station_count] local check_tile = 0 local check_src = 0 local check_dest = 0 if ( t1.get_way_dirs(wt) == 5 || t1.get_way_dirs(wt) == 10 ) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- check tile direction : " + coord3d_to_string(t1), t1) } if ( (t1.z == nexttile[station_exist-1].z && !t1.is_bridge() && !t1.is_tunnel() && test_tile_is_empty(t1) && !t1.has_two_ways()) || (t1.z < nexttile[station_exist-1].z && t1.is_bridge()) ) { //t1.get_slope() == 0 // dest if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- check tile t1 z : " + t1.z, t1) gui.add_message_at(our_player, "###---- check tile nexttile[station_exist-1].z (" + coord3d_to_string(nexttile[station_exist-1]) + ") : " + nexttile[station_exist-1].z, nexttile[station_exist-1]) } check_tile++ check_src++ } else { check_dest++ } } if ( t2.get_way_dirs(wt) == 5 || t2.get_way_dirs(wt) == 10 ) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- check tile direction : " + coord3d_to_string(t2), t2) } if ( (t2.z == nexttile[(nexttile.len()-1)-(station_exist-1)].z && !t2.is_bridge() && !t2.is_tunnel() && test_tile_is_empty(t2) && !t2.has_two_ways()) || (t2.z < nexttile[(nexttile.len()-1)-(station_exist-1)].z && t2.is_bridge()) ) { //t2.get_slope() == 0 // src if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- check tile t2 z : " + t2.z, t2) gui.add_message_at(our_player, "###---- check tile nexttile[(nexttile.len()-1)-(station_exist-1)].z (" + coord3d_to_string(nexttile[(nexttile.len()-1)-(station_exist-1)]) + ") : " + nexttile[(nexttile.len()-1)-(station_exist-1)].z, nexttile[(nexttile.len()-1)-(station_exist-1)]) } check_tile++ check_dest++ } else { check_src++ } } if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- station_count-station_exist : " + (station_count-station_exist), world.get_time()) gui.add_message_at(our_player, "###---- check_tile: " + check_tile + " check_dest: " + check_dest + " check_src: " + check_src, world.get_time()) gui.add_message_at(our_player, "###---- add_tile_dest: " + add_tile_dest + " add_tile_src: " + add_tile_src, world.get_time()) } if (check_tile == 2) { if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- add tile nexttile[station_count]: " + coord3d_to_string(nexttile[station_count]), nexttile[station_count]) gui.add_message_at(our_player, "###---- add tile nexttile[nexttile.len()-(station_count+1)]: " + coord3d_to_string(nexttile[nexttile.len()-(station_count+1)]), nexttile[nexttile.len()-(station_count+1)]) } expand_station.append(nexttile[station_count]) expand_station.append(nexttile[nexttile.len()-(station_count+1)]) } else if (check_tile == 1 && check_dest >= 1 && expand_station_end_dest.len()-1 >= add_tile_dest) { // dest if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- add tile expand_station_end_dest[add_tile_dest]: " + coord3d_to_string(expand_station_end_dest[add_tile_dest]), expand_station_end_dest[add_tile_dest]) gui.add_message_at(our_player, "###---- add tile nexttile[nexttile.len()-(station_count+1)]: " + coord3d_to_string(nexttile[nexttile.len()-(station_count+1)]), nexttile[nexttile.len()-(station_count+1)]) } expand_station.append(expand_station_end_dest[add_tile_dest]) add_tile_dest++ expand_station.append(nexttile[nexttile.len()-(station_count+1)]) } else if (check_tile == 1 && check_src >= 1 && expand_station_end_src.len()-1 >= add_tile_src) { // src if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- add tile nexttile[station_count]: " + coord3d_to_string(nexttile[station_count]), nexttile[station_count]) gui.add_message_at(our_player, "###---- add tile expand_station_end_src[add_tile_src]: " + coord3d_to_string(expand_station_end_src[add_tile_src]), expand_station_end_src[add_tile_src]) } expand_station.append(nexttile[station_count]) expand_station.append(expand_station_end_src[add_tile_src]) add_tile_src++ } else if (check_tile == 0 && check_src <= 1 && check_dest <= 1 && expand_station_end_dest.len()-1 >= add_tile_dest && expand_station_end_src.len()-1 >= add_tile_src) { // src & dest if ( print_message_box == 2 ) { gui.add_message_at(our_player, "###---- add tile expand_station_end_dest[add_tile_dest]: " + coord3d_to_string(expand_station_end_dest[add_tile_dest]), expand_station_end_dest[add_tile_dest]) gui.add_message_at(our_player, "###---- add tile expand_station_end_src[add_tile_src]: " + coord3d_to_string(expand_station_end_src[add_tile_src]), expand_station_end_src[add_tile_src]) } expand_station.append(expand_station_end_dest[add_tile_dest]) add_tile_dest++ expand_station.append(expand_station_end_src[add_tile_src]) add_tile_src++ } else { station_count-- break } /* } else { station_count-- break }*/ } if ( station_exist < station_count && print_message_box == 2 ) { gui.add_message_at(our_player, "###---- check stations field : " + station_exist, nexttile[0]) gui.add_message_at(our_player, "###---- check stations field expand -> direction exist way : " + station_count, nexttile[0]) } } return expand_station } function build_expand_station(nexttile, expand_station, st_lenght, freight, line) { // 0 = off // 1 = station expand // 2 // 3 local print_message_box = 0 local wt = line.get_waytype() local start_l = nexttile[nexttile.len()-1] local end_l = nexttile[0] local station_exist = 0 // count exist station lenght for ( local i = 0; i < 6; i++ ) { if ( nexttile[i].find_object(mo_building) != null ) { station_exist++ } } local station_list = building_desc_x.get_available_stations(building_desc_x.station, wt, good_desc_x(freight)) local station_s_obj = nexttile[nexttile.len()-1].find_object(mo_building).get_desc() if ( !station_s_obj.is_available(world.get_time()) ) { local capacity = station_s_obj.get_capacity() station_s_obj = null foreach(station in station_list) { if ( station_s_obj == null ) { station_s_obj = station } if ( station.get_capacity() >= capacity && station.get_capacity() < station_s_obj.get_capacity() ) { station_s_obj = station } } } local station_e_obj = nexttile[0].find_object(mo_building).get_desc() if ( !station_e_obj.is_available(world.get_time()) ) { station_e_obj = null foreach(station in station_list) { if ( station_e_obj == null ) { station_e_obj = station } if ( station.cost < station_e_obj.cost ) { station_e_obj = station } } } local way_obj = start_l.find_object(mo_way).get_desc() //way_list[0] if ( !way_obj.is_available(world.get_time()) ) { way_obj = find_object("way", wt, way_obj.get_topspeed()) } local catenary_obj = null //find_object("catenary", wt, 100) local build_catenary = false if ( start_l.find_object(mo_wayobj) != null ) { catenary_obj = start_l.find_object(mo_wayobj).get_desc() if ( catenary_obj != null && !catenary_obj.is_available(world.get_time()) && !catenary_obj.is_overhead_line() ) { catenary_obj = find_object("catenary", wt, way_obj.get_topspeed()) } } else { //catenary_obj = find_object("catenary", wt, way_obj.get_topspeed()) } if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### st_lenght > station_count : " + st_lenght + " > " + station_exist, expand_station[0]) } if ( st_lenght > station_exist ) { // expand station local sched = schedule_x(wt_rail, []) local sched_start = start_l local sched_end = end_l local b_count = st_lenght - station_exist switch(b_count) { case 2: b_count++ break case 3: b_count += 2 break case 4: b_count += 3 break case 5: b_count += 4 break } for ( local i = 0; i <= b_count; i++ ) { local station_obj = (i % 2) ? station_s_obj : station_e_obj if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### [i] " + i, expand_station[i]) } if ( expand_station[i].find_object(mo_way) == null ) { local build_tile = (i % 2) ? start_l : end_l if ( terraform_tile(expand_station[i], build_tile.z) ) { command_x.build_way(our_player, build_tile, expand_station[i], way_obj, true) command_x.build_station(our_player, expand_station[i], station_obj) if ( catenary_obj != null ) { command_x.build_wayobj(our_player, build_tile, expand_station[i], catenary_obj) } local entries = line.get_schedule().entries if ( build_tile == start_l ) { sched_start = expand_station[i] if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### expand stations tile schedule start_l ", expand_station[i]) } } else { sched_end = expand_station[i] if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### expand stations tile schedule end_l ", expand_station[i]) } } //::debug.pause() } //::debug.pause() } else { command_x.build_station(our_player, expand_station[i], station_obj) } if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### expand stations tile " + coord3d_to_string(expand_station[i]), expand_station[i]) } } sched.entries.append( schedule_entry_x(sched_start, 100, 0) ); sched.entries.append( schedule_entry_x(sched_end, 0, 0) ); line.change_schedule(our_player, sched) //line.halt_length = st_lenght if ( print_message_box == 1 ) { gui.add_message_at(our_player, "####### expand stations ", expand_station[0]) } local message_text = format(translate("%s expand the station %s (%s) and station %s (%s)"), our_player.get_name(), start_l.get_halt().get_name(), coord_to_string(start_l), end_l.get_halt().get_name(), coord_to_string(end_l)) gui.add_message_at(our_player, message_text, start_l) //::debug.pause() return true } return false } function check_catenary(line, dist, start_l, cnv_max_speed) { local catenary_obj = null //find_object("catenary", wt, 100) local catenary_cost = 0 local int_return = 0 local wt = line.get_waytype() local print_message_box = 0 if ( start_l.find_object(mo_wayobj) != null ) { catenary_obj = start_l.find_object(mo_wayobj).get_desc() if ( catenary_obj != null && !catenary_obj.is_available(world.get_time()) && !catenary_obj.is_overhead_line() ) { catenary_obj = find_object("catenary", wt, cnv_max_speed) } if ( print_message_box == 1 ) { gui.add_message_at(our_player, "###---- set electrified convoys " + line.get_name(), world.get_time()) gui.add_message_at(our_player, "###---- line has catenary ", world.get_time()) } int_return = 1 } else { catenary_obj = find_object("catenary", wt, cnv_max_speed) if ( catenary_obj != null ) { // build cost catenary_cost = (catenary_obj.get_cost() * (dist + (8 * line.double_ways_count))) + catenary_obj.get_cost() // + maintenance for 5 months catenary_cost += ((catenary_obj.get_maintenance() * (dist + (8 * line.double_ways_count))) + catenary_obj.get_maintenance()) * 5 } else { catenary_cost = 0 } // set electrified for vehicle by way len > xx tiles if ( dist > 80 && world.get_time().year >= 1935 && our_player.get_current_cash() > catenary_cost ) { if ( print_message_box == 1 ) { gui.add_message_at(our_player, "###---- set electrified convoys " + line.get_name(), world.get_time()) gui.add_message_at(our_player, "###---- line len " + nexttile.len(), world.get_time()) } int_return = 1 } } return int_return } function build_catenary(start_l, end_l, depot, line) { local print_message_box = 0 local wt = line.get_waytype() local way_obj = start_l.find_object(mo_way).get_desc() //way_list[0] if ( !way_obj.is_available(world.get_time()) ) { way_obj = find_object("way", wt, way_obj.get_topspeed()) } local catenary_obj = null //find_object("catenary", wt, 100) local build_catenary = false if ( start_l.find_object(mo_wayobj) != null ) { catenary_obj = start_l.find_object(mo_wayobj).get_desc() if ( catenary_obj != null && !catenary_obj.is_available(world.get_time()) && !catenary_obj.is_overhead_line() ) { catenary_obj = find_object("catenary", wt, way_obj.get_topspeed()) build_catenary = true } } else { catenary_obj = find_object("catenary", wt, way_obj.get_topspeed()) build_catenary = true } if ( build_catenary ) { command_x.build_wayobj(our_player, start_l, end_l, catenary_obj) command_x.build_wayobj(our_player, end_l, start_l, catenary_obj) command_x.build_wayobj(our_player, depot, start_l, catenary_obj) local msgtext = format(translate("%s electrified the line %s"), our_player.get_name(), line.get_name()) gui.add_message_at(our_player, msgtext, world.get_time()) } if ( print_message_box == 1 ) { //gui.add_message_at(our_player, "####### proto.veh[0].needs_electrification() : " + proto.veh[0].needs_electrification(), world.get_time()) gui.add_message_at(our_player, "####### cnv needs electrified " + catenary_obj.get_name(), depot) } return build_catenary } } function convoy_max_speed(cnv) { // convoy_x::calc_max_speed ( integer power, integer weight, integer speed_limit ) local pwr = 0 local weight = 0 local m_speed = 500 local veh_list = cnv.get_vehicles() for (local i = 0; i < veh_list.len(); i++) { pwr += veh_list[i].get_power() weight += veh_list[i].get_weight() + (veh_list[i].get_capacity()*veh_list[i].get_freight().get_weight_per_unit()) if ( m_speed > veh_list[i].get_topspeed() ) { m_speed = veh_list[i].get_topspeed() } } local a = cnv.calc_max_speed(pwr, weight, m_speed) return a } function convoy_is_retired(cnv) { local veh_list = cnv.get_vehicles() for (local i = 0; i < veh_list.len(); i++) { if ( veh_list[i].is_retired(world.get_time()) ) { return true } } } /* * check factory input + src station good quantity > max storage factory * * */ function check_good_quantity(start_l, end_l, good, line) { local f_dest = end_l.get_halt().get_factory_list() if ( f_dest.len() == 1 ) { local freight = good.get_name() foreach(freight, islot in f_dest[0].input) { if ( good.get_name() == islot.good ) { local st = islot.get_storage() local it = islot.get_in_transit() local good_src = start_l.get_halt().get_freight_to_halt(good, end_l.get_halt()) local max_storage = islot.max_storage //gui.add_message_at(our_player, "*** good halt src " + good_src + " " + line.get_name(), world.get_time()) //gui.add_message_at(our_player, "*** islot.good " + translate(islot.good) + " factory " + f_dest[0].get_name() + " input storage [" + max_storage + "]", world.get_time()) if ( st[0] < (max_storage/10) ) { return false } else if ( (st[0] + it[0] > max_storage && (max_storage/5) < st[0]) || it[0] > max_storage ) { // //gui.add_message_at(our_player, "*** good quantity [" + (st[0] + it[0]) + "] > factory " + f_dest[0].get_name() + " input storage [" + max_storage + "] " + line.get_name(), world.get_time()) return true } } } // todo more goods check } return false } simutrans-124.5/simutrans/ai/sqai_rail/info.txt000066400000000000000000000001331520675460500216370ustar00rootroot00000000000000Repo for deveoplement https://github.com/Andarix/simutrans-scenarios/tree/master/ai/sqaisimutrans-124.5/simutrans/ai/sqai_rail/placefinder.nut000066400000000000000000000113351520675460500231550ustar00rootroot00000000000000/** * Helper static functions to find places for stations, depots, etc. */ class finder { static function get_tiles_near_factory(factory) { local cov = settings.get_station_coverage() local area = [] // generate a list of tiles that will reach the factory local ftiles = factory.get_tile_list() foreach (c in ftiles) { for(local dx = -cov; dx <= cov; dx++) { for(local dy = -cov; dy <= cov; dy++) { if (dx==0 && dy==0) continue; local x = c.x+dx local y = c.y+dy if (x>=0 && y>=0) area.append( (x << 16) + y ); } } } // sort sleep() area.sort() return area } static function get_tiles_near_halt(halt) { local cov = 1 local area = [] // generate a list of tiles that will reach the halt for combined local ftiles = halt.get_tile_list() foreach (c in ftiles) { for(local dx = -cov; dx <= cov; dx++) { for(local dy = -cov; dy <= cov; dy++) { if (dx==0 && dy==0) continue; local x = c.x+dx local y = c.y+dy if (x>=0 && y>=0) area.append( (x << 16) + y ); } } } // sort sleep() area.sort() return area } static function find_empty_place(area, target) { // find place closest to target local tx = target.x local ty = target.y local best = null local dist = 10000 // check for flat and empty ground for(local i = 0; i0 && h == area[i-1]) continue; local x = h >> 16 local y = h & 0xffff if (world.is_coord_valid({x=x,y=y})) { local tile = square_x(x, y).get_ground_tile() if (tile.is_empty() && tile.get_slope()==0) { local d = abs(x - tx) + abs(y - ty) if (d < dist) { dist = d best = tile } } } } return best } static function _find_places(area, test /* function */) { local list = [] // check for flat and empty ground for(local i = 0; i0 && h == area[i-1]) continue; local x = h >> 16 local y = h & 0xffff if (world.is_coord_valid({x=x,y=y})) { local tile = square_x(x, y).get_ground_tile() if (test(tile)) { list.append(tile) } } } return list.len() > 0 ? list : [] } static function find_empty_places(area) { return _find_places(area, _tile_empty) } static function _tile_empty(tile) { return tile.is_empty() && tile.get_slope()==0 } static function _tile_empty_or_field(tile) { return tile.get_slope()==0 && (tile.is_empty() || tile.find_object(mo_field)) } static function find_water_places(area) { return _find_places(area, _tile_water) } static function _tile_water(tile) { return tile.is_water() && (tile.find_object(mo_building)==null) && (tile.find_object(mo_depot_water)==null) } static function _tile_water_way(tile) { if (tile.is_water()) { return true // (tile.find_object(mo_building)==null) && (tile.find_object(mo_depot_water)==null) } else { foreach(obj in tile.get_objects()) { try { if (obj.get_type() != mo_way) continue; if (obj.get_waytype() == wt_water) { return obj.get_desc().get_topspeed() > 5 } } catch(ev) { // object gone, most likely vehicle that moved on } } } return false } static function find_station_place(factory, target, unload = false) { if (unload) { // try unload station from station manager local res = ::station_manager.access_freight_station(factory).road_unload if (res) { return [res] } } local can_delete_fields = factory.get_field_count() > factory.get_min_field_count() local area = get_tiles_near_factory(factory) if (can_delete_fields) { return _find_places(area, _tile_empty_or_field); } else { return find_empty_places(area) } } /** * Can harbour of length @p len placed at @p pos (land tile!) in direction @p d. */ static function check_harbour_place(pos, len, d /* direction */) { local from = pos for(local i = 0; i0 ? wt_water : wt_all, d) if (to && _tile_water(to) && to.can_remove_all_objects(our_player)==null) { from = to } else { return false } } return true } } simutrans-124.5/simutrans/ai/sqai_rail/prototyper.nut000066400000000000000000000335671520675460500231430ustar00rootroot00000000000000/** * Classes to plan a convoy prototype given some constraints. */ /** * Prototype of a convoy. */ class cnv_proto_t { // properties weight = 0 // weight (fully loaded) power = 0 // total power min_top_speed = 0 // top speed when fully loaded max_speed = -1 // speed limit length = 0 // length of convoy in internal units (one tile = 16 units) capacity = 0 // capacity of convoy for desired freight maintenance = 0 // maintenance cost per month running_cost = 0 // running cost per tile price = 0 // price missing_freight = true // convoy misses vehicles to transport desired freight veh = null // the vehicles of the convoy (array) // set by valuator nr_convoys = 0 // number of convoys that should be built constructor() { veh = [] debug = false } /** * Append vehicle to this prototype. * Desired freight is given as parameter freight. */ function append(newveh, freight) { local cnv = getclass().instance() cnv.constructor() cnv.veh.extend(veh) cnv.veh.append(newveh) cnv.weight = weight + freight.get_weight_per_unit() * newveh.get_capacity() + newveh.get_weight() cnv.power = power + newveh.get_power() if (max_speed >= 0) { cnv.max_speed = min(max_speed, newveh.get_topspeed()) } else { cnv.max_speed = newveh.get_topspeed() } cnv.length = length + newveh.get_length() local fits = newveh.get_freight().is_interchangeable(freight) cnv.missing_freight = missing_freight && (newveh.get_capacity()==0 || !fits) cnv.min_top_speed = convoy_x.calc_max_speed(cnv.power, cnv.weight, cnv.max_speed) cnv.capacity = capacity + (fits ? newveh.get_capacity() : 0) cnv.maintenance = maintenance + newveh.get_maintenance() cnv.running_cost = running_cost + newveh.get_running_cost() cnv.price = price + newveh.get_cost() return cnv } static function from_convoy(cnv, freight) { local p = cnv_proto_t() local list = cnv.get_vehicles() foreach(v in list) { p = p.append(v, freight) } return p } function _save() { return ::saveinstance("cnv_proto_t", this) } } /** * Class to find the best prototype. * * Iterates through all possible combinations. * This can take a very long time for e.g. passenger trains. * Some means to shorten the computation are required. * * If a prototype is valid, it is passed to the function valuate. * The prototype with largest score wins (or the first one if valuate == null). */ class prototyper_t extends node_t { wt = 0 // way-type freight = 0 // freight to be transported max_vehicles = 0 // maximum of number of vehicles in this prototype max_length = 0 // maximum lenght of convoy in number of tiles min_speed = 0 // minimum speed electrified = 0 // 0 - not electrified vehicle; 1 - electrified vehicle volume = 0 // monthly transport volume valuate = null // valuate function, takes prototype, returns number (null -> first valid prototype is returned) best = null // the best prototype up to now best_value = 0 // and its score // print messages box // 1 = vehicle create data // 2 = vehicle found // 3 = convoy check lenght and wt_rail print_message_box = 0 wt_name = ["", "road", "rail", "water"] constructor(w, /*string*/f) { base.constructor("prototyper"); wt = w freight = good_desc_x(f) } /** * Depth-first search. * Takes constraints into account. Calls valuate. */ function step() { if ( print_message_box == 1 ) { local units = get_max_convoi_length(wt) gui.add_message_at(our_player, "**** ", world.get_time()) gui.add_message_at(our_player, "create convoy ", world.get_time()) gui.add_message_at(our_player, "wt " + wt_name[wt], world.get_time()) gui.add_message_at(our_player, "units: " + units, world.get_time()) gui.add_message_at(our_player, "CARUNITS_PER_TILE: " + CARUNITS_PER_TILE, world.get_time()) gui.add_message_at(our_player, "max_length: " + max_length, world.get_time()) gui.add_message_at(our_player, "electrified: " + electrified, world.get_time()) } //if ( wt == wt_rail ) gui.add_message_at(our_player, "max_length: " + max_length, world.get_time()) local date = world.get_time() // list of all vehicles local list = vehicle_desc_x.get_available_vehicles(wt) // vehicles that can be put first local list_first = [] // other vehicles: powered, no capacity (tenders), matching freight local list_other = [] local t = 0 // preprocess foreach(veh in list) { local first = veh.can_be_first() local fits = veh.get_freight().is_interchangeable(freight) local pwer = veh.get_power()>0 local power = veh.get_power() / 64 local speed = veh.get_topspeed() local none = veh.get_freight().get_name()=="None" || veh.get_capacity()==0 local timeline = !veh.is_retired(world.get_time()) local frev = good_desc_x(freight.get_name()).calc_revenue(wt, speed)/3 local t = frev*100 - veh.get_running_cost() // no build catenary -> no electrified vehicles //local electrified = !veh.needs_electrification() local electro = true if ( electrified == 0 && veh.needs_electrification() ) { electro = false } //gui.add_message_at(our_player, "timeline: " + veh.get_name() + " - " + timeline, world.get_time()) // use vehicles that can carry freight // or that are powered and have no freight capacity if ( (fits || (pwer && none)) && timeline && electro ) { if (first && (pwer || wt == wt_water) ) { /** * speed < 161 - max speed 160 for rail lines factory goods */ if ( speed < 161 && wt == wt_rail ) { list_first.append(veh) } else if ( wt != wt_rail ) { list_first.append(veh) } } else { list_other.append(veh) } if ( print_message_box == 2 && wt == wt_rail && pwer ) { gui.add_message_at(our_player, "* vehicle found: " + veh.get_name() + " power " + power + " speed " + speed + " ## " + t, world.get_time()) } } } // array of lists we try to iterate local it_lists = []; it_lists.resize(max_vehicles+1) local it_ind = []; it_ind.resize(max_vehicles+1) // current convoy candidate - array of desc local cnv = []; cnv.resize(max_vehicles+1) // initialize cnv[0] = cnv_proto_t() it_ind[1] = -1 it_lists[1] = list_first // iterating ind-th position in convoy local ind = 1 local count_cnv_length = false local show_mwssage = false //max_vehicles local a = 0 if ( wt == wt_water ) { a = CARUNITS_PER_TILE * 4 } else if ( wt == wt_rail ) { a = CARUNITS_PER_TILE * 3 if ( volume > 1200 && volume < 2200 ) { a = CARUNITS_PER_TILE * 4 } else if ( volume > 2200 ) { a = CARUNITS_PER_TILE * 5 } if ( show_mwssage ) { gui.add_message_at(our_player, "#prototyper 259# tiles_length: " + a + " - max_length: " + max_length, world.get_time()) show_mwssage = false } if ( get_set_name() == "pak64.german" ) { } else if ( a < max_length ) { a = max_length } } else { a = CARUNITS_PER_TILE } while(true) { it_ind[ind] ++ // done with iterating? if (it_ind[ind] >= it_lists[ind].len() ) { if (ind>1) { ind-- continue // iterating position ind-1 } else { break // end of the iteration } } local test = it_lists[ind][ it_ind[ind] ] // check couplings if ( ind==1 ? !test.can_be_first() : !vehicle_desc_x.is_coupling_allowed(cnv[ind-1].veh.top(), test) ) { continue; } // append cnv[ind] = cnv[ind-1].append(test, freight) local c = cnv[ind] // check constraints // .. length local l = (ind > 1 ? cnv[ind-1].length : 0) + max( CARUNITS_PER_TILE/2, test.get_length()); if ( print_message_box == 3 && wt == wt_rail ) { gui.add_message_at(our_player, "convoy l: " + l, world.get_time()) gui.add_message_at(our_player, "convoy length max: " + max( CARUNITS_PER_TILE/2, test.get_length()), world.get_time()) //show_mwssage = true } //max_vehicles /* local a = 0 if ( wt == wt_water ) { a = CARUNITS_PER_TILE * 4 } else if ( wt == wt_rail ) { a = CARUNITS_PER_TILE * 3 if ( volume > 1200 && volume < 2200 ) { a = CARUNITS_PER_TILE * 4 } else if ( volume > 2200 ) { a = CARUNITS_PER_TILE * 5 } if ( show_mwssage ) { gui.add_message_at(our_player, "#prototyper 259# tiles_length: " + a + " - max_length: " + max_length, world.get_time()) show_mwssage = false } if ( get_set_name() != "pak64.german" ) { max_length = 96 } if ( a < max_length ) { a = max_length } } else { a = CARUNITS_PER_TILE }*/ // no more by max length // no more by speed < max speed convoy if ((l + test.get_length()) > a || c["min_top_speed"] < c["max_speed"]) { //) { max_length CARUNITS_PER_TILE //gui.add_message_at(our_player, "c['min_top_speed']: " + c["min_top_speed"], world.get_time()) //gui.add_message_at(our_player, "c['max_speed']: " + c["max_speed"], world.get_time()) if ( (c["max_speed"]-c["min_top_speed"]) < 10 ) { count_cnv_length = true } else if ( (l + test.get_length()) > a ) { //gui.add_message_at(our_player, "#prototyper 277# tiles_length: " + a + " - (l + test.get_length()): " + (l + test.get_length()), world.get_time()) count_cnv_length = true } else { continue; } } // check if convoy finished if (test.can_be_last() && !c.missing_freight && c.min_top_speed >= min_speed) { // evaluate this candidate //gui.add_message_at(our_player, "valuate: " + valuate, world.get_time()) if (valuate) { local value = valuate.call(getroottable(), c) //if ( print_message_box == 2 ) { // gui.add_message_at(our_player, "evaluate this candidate: " + value, world.get_time()) //} if (best==null || value > best_value) { best = c best_value = value } } else { // no valuator function -> take first valid convoy and return best = c; break } } // move on to next position if (ind >= max_vehicles || count_cnv_length) { count_cnv_length = false continue; } ind++ local list_succ = test.get_successors() if (list_succ.len()==0) { it_lists[ind] = list_other } else{ it_lists[ind] = [] foreach(v in list_succ) { if (v.is_available(date)) { it_lists[ind].append(v) } } } it_ind[ind] = -1 } if (best) { foreach(ind, test in best.veh) { print("Best[" + ind + "] = " + test.get_name()) //if ( wt == wt_rail ) gui.add_message_at(our_player, "Best[" + ind + "] = " + test.get_name(), world.get_time()) } return r_t(RT_SUCCESS) } return r_t(RT_ERROR) } } /** * A simple valuate function. */ class valuator_simple_t { wt = 0 // way-type freight = null // freight to be transported volume = 0 // monthly transport volume max_cnvs = 0 // maximum number of convoys distance = 0 way_max_speed = -1 // speed limit of planned way way_maintenance = 0 // maintenance of planned way /** * Estimates gain per month */ function valuate_monthly_transport(cnv) { local speed = way_max_speed > 0 ? min(way_max_speed, cnv.min_top_speed) : cnv.min_top_speed local frev = good_desc_x(freight).calc_revenue(wt, speed)/2 local capacity = cnv.capacity // tiles per month of one convoy local tpm = convoy_x.speed_to_tiles_per_month(speed) / 2 + 1 // needed convoys to transport everything local n1 = max(1, volume * 2 * distance / (tpm * cnv.capacity)) // realistic number of convoys local ncnv = min(n1, min(max_cnvs, max(distance / 8, 3) ) ) cnv.nr_convoys = ncnv // double track and signals missing if (wt == wt_rail) { cnv.nr_convoys = 1 } if (way_max_speed > 0) { // correction factor to prefer faster ways: // factor = 0 .. if 2*distance < ncnv // factor = 1 .. if distance/3 > ncnv // linear interpolated in between // without scaling almost always the cheapest way is chosen ... local factor = max(0, min(10*distance, 6*(2*distance - ncnv) ) ); // rescale tiles per month tpm = (tpm*factor) / (10*distance); } // monthly costs and revenue local value = ncnv*( (frev*cnv.capacity+1500)/3000*tpm - cnv.running_cost*tpm - cnv.maintenance) - distance * way_maintenance /* // gain per field // station_maintenance missing local t = (frev*cnv.capacity) - cnv.running_cost - cnv.maintenance local i = 30000 // maintenance stations + bridges if ( wt == wt_rail ) { i += 50000 } if ( t <= way_maintenance*2 + i ) { return 0 } */ return value } function _save() { return ::saveinstance("valuator_simple_t", this) } } simutrans-124.5/simutrans/ai/sqai_rail/rail_connector.nut000066400000000000000000001057601520675460500237100ustar00rootroot00000000000000/** * @brief rail_connector.nut create rail lines * */ class rail_connector_t extends manager_t { // input data fsrc = null fdest = null freight = null planned_way = null planned_station = null planned_depot = null planned_convoy = null finalize = true // step-by-step construct the connection phase = 0 // can be provided optionally c_start = null // array c_end = null // array c_route = null // array // generated data c_depot = null c_sched = null c_line = null c_cnv = null t_start = [] t_end = [] st_lenght = 0 pl = null // print messages box // 1 = way // 2 = stations // 3 = depot print_message_box = 0 constructor() { base.constructor("rail_connector_t") debug = false } function work() { // TODO check if child does the right thing pl = our_player local tic = get_ops_total(); local fs = fsrc.get_tile_list() local fd = fdest.get_tile_list() local s_src = null local s_dest = null if ( phase == 0 ) { if ( !check_link_catg_goods(fsrc, fdest, freight) ) { //gui.add_message_at(pl, "no build line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ") exists catg links", world.get_time()) return r_t(RT_TOTAL_FAIL) // } else if ( check_factory_links(fsrc, fdest, freight) >= 2 ) { //gui.add_message_at(pl, "no build line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ") to many links", world.get_time()) return r_t(RT_TOTAL_FAIL) // } else { //gui.add_message_at(pl, "check line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ") " + freight, world.get_time()) local st_dock = search_station(c_start, wt_water, 1) if ( !check_factory_link_line(fsrc, fdest, freight) && !st_dock ) { //gui.add_message_at(pl, " st_dock " + st_dock, world.get_time()) return r_t(RT_TOTAL_FAIL) } } local build_cash = player_x(our_player.nr).get_current_cash() + (((player_x(our_player.nr).get_current_net_wealth()/100) - player_x(our_player.nr).get_current_cash())/2) local build_cost = industry_manager.get_link_build_cost(fsrc, fdest, freight, 1) if ( industry_manager.get_link_build_cost(fsrc, fdest, freight, 0) > 0 ) { build_cost = industry_manager.get_link_build_cost(fsrc, fdest, freight, 0) } if ( (build_check_month > world.get_time().ticks || build_cost > build_cash) && industry_manager.get_combined_link(fsrc, fdest, freight) == 0 ) { // not build link if ( debug ) gui.add_message_at(our_player, "#rail_conn# not build line : build_check_month = " + build_check_month + " or build cost link > cash : build cost line " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 1) + " | build cost link " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 0), world.get_time()) if ( debug ) gui.add_message_at(our_player, " ---> link " + fsrc + " " + fsrc.get_name() + " - " + fdest.get_name(), world.get_time()) //if ( debug ) gui.add_message_at(our_player, " ---> phase " + phase, world.get_time()) industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) return r_t(RT_TOTAL_FAIL) } } local err = null switch(phase) { case 0: // find places for stations if ( print_message_box > 0 ) { gui.add_message_at(pl, "______________________ build rail ______________________", world.get_time()) gui.add_message_at(pl, " line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ")", world.get_time()) } if (c_start == null) { c_start = ::finder.find_station_place(fsrc, fdest) } if (c_start && c_end == null) { c_end = ::finder.find_station_place(fdest, c_start, finalize) } if (c_start.len()>0 && c_end.len()>0) { phase ++ } else { print("No station places found") return error_handler() } case 1: // build way { sleep() local d = pl.get_current_cash() local err = null local line_start = null if ( (abs(c_start[0].x - c_end[0].x) + abs(c_start[0].y - c_end[0].y)) < 13 && industry_manager.get_combined_link(fsrc, fdest, freight) > 0) { //gui.add_message_at(pl, "c_start " + coord_to_string(c_start[0]) + " c_end " + coord_to_string(c_end[0]), c_end[0]) local d = settings.get_station_coverage() local f = 0 for ( local i = 0; i < fd.len(); i++ ) { f = abs(c_start[0].x - fd[i].x) + abs(c_start[0].y - fd[i].y) if ( f <= d ) { local extension = find_extension(wt_rail) if ( extension != null ) { local err = command_x.build_station(our_player, c_start[0], extension) if ( err == null ) { return r_t(RT_TOTAL_SUCCESS) } } } } if ( debug ) { gui.add_message_at(pl, "c_start " + coord_to_string(c_start[0]) + " c_end " + coord_to_string(c_end[0]), c_end[0]) ::debug.pause } } // test route for calculate cost local calc_route = null local r1 = test_route(our_player, c_start, c_end, planned_way) local r2 = test_route(our_player, c_end, c_start, planned_way) if ( r1 == "No route" && r2 == "No route" ) { return r_t(RT_TOTAL_FAIL) } else if ( r1 == "No route" && r2 != "No route" ) { calc_route = r2 } else if ( r1 != "No route" && r2 == "No route" ) { calc_route = r1 } else if ( r1.routes.len() > r2.routes.len() ) { calc_route = r2 r1 = null } else { calc_route = r1 } local build_status = null if ( calc_route.routes.len() < 13 ) { //gui.add_message_at(pl, "route len < 13 tiles ", c_end) //gui.add_message_at(pl, "c_start " + coord_to_string(c_start) + " c_end " + coord_to_string(c_end), c_end) local extension = find_extension(wt_rail) //local start_h = tile_x(c_start.x, c_start.y, c_start.z).get_halt().get_name() //local end_h = tile_x(c_end.x, c_end.y, c_end.z).get_halt().get_name() return error_handler() } else { build_status = check_build_station(calc_route) //gui.add_message_at(pl, "(129) build_status : " + build_status, world.get_time()) //::debug.pause() if ( build_status != true ) { calc_route = test_route(our_player, c_end, c_start, planned_way) if ( calc_route == "No route" ) { return r_t(RT_TOTAL_FAIL) } build_status = check_build_station(calc_route) if ( build_status == true ) { local c = c_start c_start = c_end c_end = c line_start = t_end[0] } else { return r_t(RT_TOTAL_FAIL) } } else { if ( build_status == true ) { line_start = t_start[0] } else { return r_t(RT_TOTAL_FAIL) } } } //gui.add_message_at(pl, "(146) build_status : " + build_status, world.get_time()) sleep() local build_cost = (calc_route.routes.len() * planned_way.get_cost()) + ((st_lenght*2)*planned_station.get_cost()) + planned_depot.get_cost() + (calc_route.bridge_lens * calc_route.bridge_obj.get_cost()) local cost_monthly = (calc_route.routes.len() * planned_way.get_maintenance()) + ((st_lenght*2)*planned_station.get_maintenance()) + planned_depot.get_maintenance() + (calc_route.bridge_lens * calc_route.bridge_obj.get_maintenance()) build_cost = build_cost/100 //build_cost + build_cost += (calc_route.tiles_tree * ((tree_desc_x.get_price()/100)*2)) //gui.add_message_at(pl, "tree remove cost: " + tree_desc_x.get_price(), world.get_time()) //gui.add_message_at(pl, "terraform cost: " + command_x.slope_get_price(82), world.get_time()) // terraform cost local terraform_cost = command_x.slope_get_price(82)/100 build_cost += st_lenght*terraform_cost //gui.add_message_at(pl, "cash: " + pl.get_current_cash() + " build cost: " + build_cost + " montly cost: " + (cost_monthly/100), world.get_time()) cost_monthly = (cost_monthly/100)+(pl.get_current_maintenance()/100) //gui.add_message_at(pl, "cash: " + pl.get_current_cash() + " current_maintenance(): " + pl.get_current_maintenance() + " get_maintenance()[0]: " + (-pl.get_maintenance()[0]), world.get_time()) //gui.add_message_at(pl, " montly cost new: " + cost_monthly, world.get_time()) sleep() // if combined station from ship local cash = pl.get_current_cash() local st_dock = search_station(line_start, wt_water, 1) //gui.add_message_at(our_player, "search_station(t_start[0], wt_water, 1) " + st_dock + " t_start[0] " + coord3d_to_string(t_start[0]), world.get_time()) if ( st_dock ) { local st = halt_x.get_halt(st_dock[0], our_player) if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() == 0 ) { cash = our_player.get_current_net_wealth() //gui.add_message_at(our_player, "rail: combined station -> get_current_net_wealth() " + (our_player.get_current_net_wealth()/100), world.get_time()) } else { } } } if ( (cash-build_cost) < (cost_monthly*4) ) { //remove_tile_to_empty(t_start, wt_rail, 1) //remove_tile_to_empty(t_end, wt_rail, 1) industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) gui.add_message_at(pl, "Rail: Way construction cost to height: cash: " + cash + " build cost: " + build_cost, world.get_time()) build_check_month = world.get_time().ticks + (build_check_time(build_cost) * world.get_time().ticks_per_month) return error_handler() } /* if ( calc_route.routes.len() > 150 && (cash-build_cost-(build_cost/2)) < (cost_monthly*10) ) { //remove_tile_to_empty(t_start, wt_rail, 1) //remove_tile_to_empty(t_end, wt_rail, 1) industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) gui.add_message_at(pl, "Way to long for rentabel build.", world.get_time()) return error_handler() } */ //gui.add_message_at(pl, "c_start.len() " + c_start.len() + " - c_end.len() " + c_end.len(), world.get_time()) //err = construct_rail(pl, c_start, c_end, planned_way ) if ( r1 == null ) { err = construct_rail(pl, c_end, c_start, planned_way ) local c = c_start c_start = c_end c_end = c } else { err = construct_rail(pl, c_start, c_end, planned_way ) } print("Way construction cost: " + (d-pl.get_current_cash()) ) if (err) { // fail, c_start, c_end still arrays //print("Failed to build way from " + coord_to_string(c_start[0]) + " to " + coord_to_string(c_end[0])) gui.add_message_at(pl, "Failed to build way from " + coord_to_string(c_start[0]) + " to " + coord_to_string(c_end[0]), world.get_time()) return error_handler() } else { // success, now c_start, c_end contain the start/end points where something was built if ( print_message_box == 1 ) { gui.add_message_at(pl, "Build rail from " + coord_to_string(c_start) + " to " + coord_to_string(c_end), world.get_time()) } // test connect in double way local asf = astar_route_finder(wt_rail) // check start -> end local wayline = asf.search_route([c_start], [c_end]) if ( "err" in wayline ) { // no route found } else { check_doubleway_in_line(wayline, wt_rail) // check end -> start wayline.clear() wayline = asf.search_route([c_end], [c_start]) check_doubleway_in_line(wayline, wt_rail) } } if ( line_start == t_end[0] ) { local c = c_start c_start = c_end c_end = c c_route.reverse() } phase ++ } case 2: // build station and extensions { /* local err = command_x.build_station(pl, c_start, planned_station ) if (err) { print("Failed to build station at " + coord_to_string(c_start)) if ( print_message_box == 2 ) { gui.add_message_at(pl, "Failed to build rail station at " + coord_to_string(c_start) + " error " + err, world.get_time()) } return error_handler() } */ if ( print_message_box == 2 ) { gui.add_message_at(pl, " planned_convoy.length " + planned_convoy.length, world.get_time()) } // stations lenght local a = planned_convoy.length local count = 0 do { a -= 16 count += 1 } while(a > 0) if ( print_message_box == 1 ) { gui.add_message_at(our_player, " rail_connector stations lenght: " + count, world.get_time()) } // check combined station or connect factory src local tiles_y = abs(fs[0].y - c_start.y) local tiles_x = abs(fs[0].x - c_start.x) // local fsrc_building = fs[0].find_object(mo_building).get_desc() local size = fsrc_building.get_size(0) //local size_x = size.slice(0, size.find(",")) //local size_y = size.slice(size.find(",")) local tiles_c = 0//(fs.len() / 2) + settings.get_station_coverage() + 2 if ( size.x == size.y ) { tiles_c = size.x + 2 } else if ( size.x > size.y ) { tiles_c = size.x + 2 } else if ( size.x < size.y ) { tiles_c = size.y + 2 } else { tiles_c = (fs.len() / 2) + settings.get_station_coverage() + 2 } //gui.add_message_at(pl, "fsrc tiles_c " + tiles_c, world.get_time()) local combined_halt = false if (tiles_x > tiles_c || tiles_y > tiles_c) { //gui.add_message_at(pl, "tiles_x = " + tiles_x + " - tiles_y = " + tiles_y + " - tiles_c = " + tiles_c, world.get_time()) combined_halt = true } // // check place and build station to c_start s_src = check_station(pl, c_start, c_route, count, wt_rail, planned_station, 1, combined_halt) if ( s_src == false ) { print("Failed to build station at " + coord_to_string(c_start)) if ( print_message_box > 0 ) { gui.add_message_at(pl, "Failed to build rail station at s_src " + coord3d_to_string(c_start), world.get_time()) } remove_wayline(c_route, c_route.len()-1, wt_rail) return error_handler() } // low cost station for line end local station_list = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x(freight)) local x = 0 local station_select = planned_station foreach(station in station_list) { if ( station.cost < station_select.cost ) { station_select = station } } // check combined station or connect factory dest tiles_y = abs(fd[0].y - c_end.y) tiles_x = abs(fd[0].x - c_end.x) // local fdest_building = fd[0].find_object(mo_building).get_desc() size = fdest_building.get_size(0) //size_x = size.slice(0, size.find(",")) //size_y = size.slice(size.find(",")) tiles_c = 0//(fs.len() / 2) + settings.get_station_coverage() + 2 if ( size.x == size.y ) { tiles_c = size.x + 2 } else if ( size.x > size.y ) { tiles_c = size.x + 2 } else if ( size.x < size.y ) { tiles_c = size.y + 2 } else { tiles_c = (fd.len() / 2) + settings.get_station_coverage() + 2 } //gui.add_message_at(pl, "fdest tiles_c " + tiles_c, world.get_time()) combined_halt = false if (tiles_x > tiles_c || tiles_y > tiles_c) { //gui.add_message_at(pl, "tiles_x = " + tiles_x + " - tiles_y = " + tiles_y + " - tiles_c = " + tiles_c, world.get_time()) combined_halt = true } // check place and build station to c_end s_dest = check_station(pl, c_end, c_route, count, wt_rail, station_select, 1, combined_halt) if ( s_dest == false ) { print("Failed to build station at " + coord_to_string(c_end)) if ( print_message_box > 0 ) { gui.add_message_at(pl, "Failed to build rail station at s_dest " + coord_to_string(c_end), world.get_time()) } build_check_month = world.get_time().ticks + world.get_time().ticks_per_month remove_wayline(c_route, c_route.len()-1, wt_rail, s_src.len()) remove_tile_to_empty(s_src, wt_rail) remove_tile_to_empty(c_start, wt_rail, 0) remove_tile_to_empty(c_end, wt_rail, 0) return error_handler() } //local if ( finalize ) { // store place of unload station for future use local fs = ::station_manager.access_freight_station(fdest) if (fs.rail_unload == null) { fs.rail_unload = c_end print( recursive_save({unload = c_end}, "\t\t\t", []) ) } } if ( print_message_box == 2 ) { //gui.add_message_at(our_player, " ... rotate " + rotate, world.get_time()) gui.add_message_at(pl, "Build station on " + coord3d_to_string(c_start) + " and " + coord3d_to_string(c_end), world.get_time()) } phase += 3 } case 5: // build depot { if ( print_message_box == 3 ) { gui.add_message_at(pl, "___________ exists depots rail ___________", world.get_time()) gui.add_message_at(pl," c_start pos: " + coord_to_string(c_start) + " : c_end pos: " + coord_to_string(c_end), world.get_time()) } // search depot to range start and end station local depot_found = search_depot(c_start, wt_rail) local starts_field = c_start if ( !depot_found ) { depot_found = search_depot(c_end, wt_rail) if ( !depot_found ) { } else { starts_field = c_end } } if ( !depot_found && print_message_box == 3 ) { gui.add_message_at(pl," *** depot not found *** ", world.get_time()) } else if ( print_message_box == 3 ) { gui.add_message_at(pl," ---> depot found : " + depot_found.get_pos(), coord_to_string(depot_found)) } // build rail to depot if ( depot_found ) { c_depot = depot_found local err = command_x.build_road(pl, starts_field, c_depot, planned_way, false, true) } else { local i = 0 if ( c_route.len() > 10 ) { i = c_route.len() - 5 - s_src.len() } local err = construct_rail_to_depot(pl, c_route[i], planned_way) //c_start if (err) { print("Failed to build depot access from " + coord_to_string(c_start)) return error_handler() } // depot already existing ? if (c_depot.find_object(mo_depot_rail) == null) { // no: build local err = command_x.build_depot(pl, c_depot, planned_depot ) if (err) { print("Failed to build depot at " + coord_to_string(c_depot)) return error_handler() } if (finalize) { // store depot location local fs = ::station_manager.access_freight_station(fsrc) if (fs.rail_depot == null) { fs.rail_depot = c_depot } } } if ( print_message_box == 1 ) { gui.add_message_at(pl, "Build depot on " + coord_to_string(c_depot), world.get_time()) } } phase ++ } case 6: // create schedule { //gui.add_message_at(pl, "c_start " + coord_to_string(c_start) + " - c_end " + coord_to_string(c_end), world.get_time()) local sched = schedule_x(wt_rail, []) sched.entries.append( schedule_entry_x(c_start, 100, 0) ); sched.entries.append( schedule_entry_x(c_end, 0, 0) ); c_sched = sched phase ++ } case 7: // create line and set schedule { pl.create_line(wt_rail) // find the line - it is a line without schedule and convoys local list = pl.get_line_list() foreach(line in list) { if (line.get_waytype() == wt_rail && line.get_schedule().entries.len()==0) { // right type, no schedule -> take this. c_line = line break } } // set schedule c_line.change_schedule(pl, c_sched); phase ++ } case 8: // append vehicle_constructor and set line { local c = vehicle_constructor_t() c.p_depot = depot_x(c_depot.x, c_depot.y, c_depot.z) c.p_line = c_line c.p_convoy = planned_convoy c.p_count = min(planned_convoy.nr_convoys, 1) append_child(c) local toc = get_ops_total(); print("rail_connector wasted " + (toc-tic) + " ops") // rename line local line_name = c_line.get_name() local str_search = ") " + translate("Line") local st_names = c_line.get_schedule().entries if ( line_name.find(str_search) != null ) { local new_name = translate("Train") + " " + translate(freight) + " " + st_names[0].get_halt(pl).get_name() + " - " + st_names[1].get_halt(pl).get_name() c_line.set_name(new_name) } phase ++ return r_t(RT_PARTIAL_SUCCESS) } case 9: // currently not used { } } //gui.add_message_at(pl, "finalize industry_manager.set_link_state ", world.get_time()) if (finalize) { industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_built) if ( industry_manager.get_combined_link(fsrc, fdest, freight) > 0 ) { local a = industry_manager.get_combined_link(fsrc, fdest, freight) - 1 industry_manager.set_combined_link(fsrc, fdest, freight, a) } } industry_manager.access_link(fsrc, fdest, freight).append_line(c_line) //gui.add_message_at(pl, "Build cost link " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 1), world.get_time()) local cs = tile_x(c_start.x, c_start.y, c_start.z) local ce = tile_x(c_end.x, c_end.y, c_end.z) /*if (c_start.len()>0 && c_end.len()>0) { local cs = c_start[0] local ce = c_end[0] }*/ local st = halt_x.get_halt(cs, pl) local f_name = ["", ""] if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { f_name[0] = fl_st[0].get_name() } else { f_name[0] = st.get_name() } } st = halt_x.get_halt(ce, pl) if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { f_name[1] = fl_st[0].get_name() } else { f_name[1] = st.get_name() } } local msgtext = format(translate("%s build rail line from %s (%s) to %s (%s)"), pl.get_name(), f_name[0], coord_to_string(cs), f_name[1], coord_to_string(ce)) //gui.add_message_at(pl, pl.get_name() + " build rail line from " + f_name[0] + " (" + coord_to_string(cs) + ") to " + f_name[1] + " (" + coord_to_string(ce) + ")", c_start) gui.add_message_at(pl, msgtext, c_start) return r_t(RT_TOTAL_SUCCESS) } function error_handler() { local r = r_t(RT_TOTAL_FAIL) // TODO rollback if (reports.len()>0) { // there are alternatives print("Delivering alternative connector") r.report = reports.pop() if (r.report.action && r.report.action.getclass() == amphibious_connection_planner_t) { print("Delivering amphibious_connection_planner_t") r.node = r.report.action r.report = null } } else { print("Error during setup of rail connection.") industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_failed); } return r } function construct_rail(pl, starts, ends, way) { local as = astar_builder() as.builder = way_planner_x(pl) as.way = way as.builder.set_build_types(way) as.bridger = pontifex(pl, way) if (as.bridger.bridge == null) { as.bridger = null } local res = as.search_route(starts, ends) if ("err" in res) { return res.err } c_start = res.start c_end = res.end c_route = res.routes } function construct_rail_to_depot(pl, start, way) { local as = depot_pathfinder() as.builder = way_planner_x(pl) as.way = way as.builder.set_build_types(way) local res = as.search_route(start) if ("err" in res) { return res.err } local d = res.end c_depot = tile_x(d.x, d.y, d.z) } function check_build_station(calc_route) { local routes = calc_route.routes local err = null // count tiles test for station local nr_tile_test = 3 if ( routes.len() > 150 ) { //gui.add_message_at(our_player, "distance " + distance, world.get_time()) //gui.add_message_at(our_player, "calc route " + coord3d_to_string(c_start[0]) + " to " + coord3d_to_string(c_end[0]) + ": way tiles = " + calc_route.routes.len() + " bridge tiles = " + calc_route.bridge_lens + " tree tiles = " + calc_route.tiles_tree, world.get_time()) } local s = routes.len()-nr_tile_test t_start = routes.slice(s) t_start.reverse() //t_start.append(tile_x(c_start[0].x, c_start[0].y, c_start[0].z)) t_end = routes.slice(0, nr_tile_test) //t_end.append(tile_x(c_end[0].x, c_end[0].y, c_end[0].z)) // stations lenght local a = planned_convoy.length do { a -= 16 st_lenght += 1 } while(a > 0) if ( !planned_way.is_available(world.get_time()) ) { planned_way = find_object("way", wt_rail, planned_way.get_topspeed()) } // check exist way local check_way = [] for ( local j = 0; j < (nr_tile_test*2); j++ ) { check_way.append(0) } for ( local i = 0; i < (nr_tile_test*2); i++ ) { if ( i < nr_tile_test ) { // start if ( tile_x(t_start[i].x, t_start[i].y, t_start[i].z).has_way(wt_rail) ) { //find_object(mo_way) check_way[i] = 1 } } else { // end if ( tile_x(t_end[i-nr_tile_test].x, t_end[i-nr_tile_test].y, t_end[i-nr_tile_test].z).has_way(wt_rail) ) { //find_object(mo_way) check_way[i] = 1 } } } for ( local j = 0; j < t_start.len()-1; j++ ) { err = command_x.build_way(pl, t_start[j], t_start[j+1], planned_way, true) if ( err != null ) { //gui.add_message_at(pl, "check_station build_way " + err, t_start[0]) } } //err = command_x.build_way(pl, t_start[0], t_start[1], planned_way, true) //if ( err != null ) { gui.add_message_at(pl, "check_station build_way " + err, t_start[0]) } //err = command_x.build_way(pl, t_start[1], t_start[2], planned_way, true) //if ( err != null ) { gui.add_message_at(pl, "check_station build_way " + err, t_start[0]) } //::debug.pause() if ( err == null ) { err = check_station(pl, t_start[0], routes, st_lenght, wt_rail, planned_station, 0) if ( err == false ) { gui.add_message_at(pl, "check_station " + err, t_start[0]) } if ( err == true ) { // station start ok for ( local j = 0; j < t_end.len()-1; j++ ) { err = command_x.build_way(pl, t_end[j], t_end[j+1], planned_way, true) if ( err != null ) { gui.add_message_at(pl, "check_station build_way " + err, t_end[0]) } } //::debug.pause() //err = command_x.build_way(pl, t_end[0], t_end[1], planned_way, true) //if ( err != null ) { gui.add_message_at(pl, "check_station build_way t_end " + err, t_end[0]) } //err = command_x.build_way(pl, t_end[1], t_end[2], planned_way, true) //if ( err != null ) { gui.add_message_at(pl, "check_station build_way t_end " + err, t_end[0]) } //local tool = command_x(tool_remove_way) if ( err == null ) { err = check_station(pl, t_end[0], routes, st_lenght, wt_rail, planned_station, 0) if ( err == true ) { // station end ok // remove track -> error by build /*for ( local i = 0; i < (nr_tile_test*2); i++ ) { if ( i < nr_tile_test && check_way[i] == 0 ) { remove_tile_to_empty(t_start[i], wt_rail, 0) } else if ( i > (nr_tile_test-1) && check_way[i] == 0 ) { remove_tile_to_empty(t_end[i-nr_tile_test], wt_rail, 0) } }*/ remove_test_way(check_way, nr_tile_test, t_start, t_end, 1) //tool.work(our_player, t_start[0], t_start[2], "" + wt_rail) //tool.work(our_player, t_end[0], t_end[2], "" + wt_rail) } else { // failed station place end /*for ( local i = 0; i < (nr_tile_test*2); i++ ) { if ( check_way[i] == 0 && i < nr_tile_test ) { remove_tile_to_empty(t_start[i], wt_rail, 0) //gui.add_message_at(pl, "remove " + coord3d_to_string(t_start[i]), t_start[i]) } else if ( check_way[i] == 0 && i > (nr_tile_test-1) ) { remove_tile_to_empty(t_end[i-nr_tile_test], wt_rail, 0) //gui.add_message_at(pl, "remove " + coord3d_to_string(t_end[i-nr_tile_test]), t_end[i-nr_tile_test]) } }*/ remove_test_way(check_way, nr_tile_test, t_start, t_end, 1) return error_handler() } } else { // remove start and end /*for ( local i = 0; i < (nr_tile_test*2); i++ ) { if ( check_way[i] == 0 && i < nr_tile_test ) { remove_tile_to_empty(t_start[i], wt_rail, 0) } else if ( check_way[i] == 0 && i > (nr_tile_test-1) ) { remove_tile_to_empty(t_end[i-nr_tile_test], wt_rail, 0) } }*/ remove_test_way(check_way, nr_tile_test, t_start, t_end, 1) } } else { // failed station place start // remove start //::debug.pause() /*for ( local i = 0; i < nr_tile_test; i++ ) { if ( check_way[i] == 0 && i < nr_tile_test ) { remove_tile_to_empty(t_start[i], wt_rail, 0) } }*/ remove_test_way(check_way, nr_tile_test, t_start, t_end, 0) return error_handler() } } else { // no built way start -> remove start /*for ( local i = 0; i < nr_tile_test; i++ ) { if ( check_way[i] == 0 && i < nr_tile_test ) { remove_tile_to_empty(t_start[i], wt_rail, 0) } }*/ remove_test_way(check_way, nr_tile_test, t_start, t_end, 0) } if ( print_message_box > 0 ) { gui.add_message_at(pl, "plan station start " + t_start[0] + " - plan station end " + t_end[0], t_start[2]) } return true } function remove_test_way(check_way, nr_tile_test, t_start, t_end, s) { for ( local i = 0; i < (nr_tile_test*2); i++ ) { if ( check_way[i] == 0 && i < nr_tile_test ) { remove_tile_to_empty(t_start[i], wt_rail, 0) } else if ( s == 1 && check_way[i] == 0 && i > (nr_tile_test-1) ) { remove_tile_to_empty(t_end[i-nr_tile_test], wt_rail, 0) } } } } class depot_pathfinder extends astar_builder { function estimate_distance(c) { local t = tile_x(c.x, c.y, c.z) if (t.is_empty() && t.get_slope()==0) { return 0 } local depot = t.find_object(mo_depot_rail) if (depot && depot.get_owner().nr == our_player_nr) { return 0 } return 10 } function add_to_open(c, weight) { if (c.dist == 0) { // test for depot local t = tile_x(c.x, c.y, c.z) if (t.is_empty()) { // depot not existing, we must build, increase weight weight += 25 * cost_straight } } base.add_to_open(c, weight) } function search_route(start) { prepare_search() local dist = estimate_distance(start) add_to_open(ab_node(start, null, 1, dist+1, dist, 0), dist+1) search() if (route.len() > 0) { for (local i = 1; i= 2 ) { //gui.add_message_at(pl, "no build line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ") to many links", world.get_time()) return r_t(RT_TOTAL_FAIL) // } else { //gui.add_message_at(pl, "check line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ") " + freight, world.get_time()) local st_dock = search_station(c_start, wt_water, 1) if ( !check_factory_link_line(fsrc, fdest, freight) && !st_dock ) { return r_t(RT_TOTAL_FAIL) } } local build_cash = player_x(our_player.nr).get_current_cash() + (((player_x(our_player.nr).get_current_net_wealth()/100) - player_x(our_player.nr).get_current_cash())/2) local build_cost = industry_manager.get_link_build_cost(fsrc, fdest, freight, 2) if ( industry_manager.get_link_build_cost(fsrc, fdest, freight, 0) > 0 ) { build_cost = industry_manager.get_link_build_cost(fsrc, fdest, freight, 0) } if ( (build_check_month > world.get_time().ticks || build_cost > build_cash) && industry_manager.get_combined_link(fsrc, fdest, freight) == 0 ) { // not build link if ( debug ) gui.add_message_at(our_player, "#road_conn# not build line : build_check_month = " + build_check_month + " or build cost link > cash : build cost line " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 2) + " | build cost link " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 0), world.get_time()) if ( debug ) gui.add_message_at(our_player, " ---> link " + fsrc + " " + fsrc.get_name() + " - " + fdest.get_name(), world.get_time()) industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) return r_t(RT_TOTAL_FAIL) } } switch(phase) { case 0: // station places if ( print_message_box > 0 && print_message_box != 4 ) { gui.add_message_at(our_player, "______________________ build road ______________________", world.get_time()) gui.add_message_at(pl, " line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ")", world.get_time()) } // count trials, and fail if necessary if (c_trial_route > 3) { print("Route building failed " + c_trial_route + " times.") if (debug) gui.add_message_at(pl, "Failed to complete route from " + coord_to_string(fsrc) + " to " + coord_to_string(fdest) + " after " + c_trial_route + " attempts", fsrc) return error_handler() } c_trial_route++ // make arrays if necessary if (type(c_start) == "instance") { c_start = [c_start] } if (type(c_end) == "instance") { c_end = [c_end] } // find places for stations if (c_generate_start) { c_start = ::finder.find_station_place(fsrc, fdest) } if (c_generate_end) { c_end = ::finder.find_station_place(fdest, c_start, finalize) } if (c_start.len()>0 && c_end.len()>0) { phase ++ } else { print("No station places found") return error_handler() } case 1: // build way { sleep() local d = pl.get_current_cash(); // test route for calculate cost local calc_route = test_route(our_player, c_start, c_end, planned_way) //gui.add_message_at(our_player, "plan road from " + coord_to_string(c_start[0]) + " to " + coord_to_string(c_end[0]), world.get_time()) if ( print_message_box == 1 && calc_route != "No route" ) { gui.add_message_at(our_player, "distance " + (calc_route.routes.len() + calc_route.bridge_lens), world.get_time()) } if ( calc_route == "No route" ) { return r_t(RT_TOTAL_FAIL) } //gui.add_message_at(our_player, "calc route " + coord3d_to_string(c_start[0]) + " to " + coord3d_to_string(c_end[0]) + ": way tiles = " + calc_route.routes.len() + " bridge tiles = " + calc_route.bridge_lens + " tree tiles = " + calc_route.tiles_tree, world.get_time()) sleep() local build_cost = (calc_route.routes.len() * planned_way.get_cost()) + (planned_station.get_cost()*2) + planned_depot.get_cost() + (calc_route.bridge_lens * calc_route.bridge_obj.get_cost()) local cost_monthly = (calc_route.routes.len() * planned_way.get_maintenance()) + (planned_station.get_maintenance()*2) + planned_depot.get_maintenance() + (calc_route.bridge_lens * calc_route.bridge_obj.get_maintenance()) build_cost = build_cost/100 build_cost = build_cost + (calc_route.tiles_tree * (tree_desc_x.get_price()/100)) //gui.add_message_at(pl, "tree remove cost: " + tree_desc_x.get_price(), world.get_time()) //gui.add_message_at(pl, "cash: " + pl.get_current_cash() + " build cost: " + build_cost + " montly cost: " + (cost_monthly/100), world.get_time()) cost_monthly = (cost_monthly/100)+(pl.get_current_maintenance()/100) //gui.add_message_at(pl, "cash: " + pl.get_current_cash() + " current maintenance: " + pl.get_current_maintenance(), world.get_time()) //gui.add_message_at(pl, " montly cost new: " + cost_monthly, world.get_time()) sleep() // if combined station from ship local cash = pl.get_current_cash() local st_dock = search_station(calc_route.routes[calc_route.routes.len()-1], wt_water, 1) if ( st_dock ) { local st = halt_x.get_halt(st_dock[0], our_player) if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() == 0 ) { cash = our_player.get_current_net_wealth() if ( print_message_box == 4 ) { gui.add_message_at(our_player, "road: combined station -> get_current_net_wealth() " + (our_player.get_current_net_wealth()/100), world.get_time()) } } else { } } } if ( (cash-build_cost) < (cost_monthly*4) ) { //gui.add_message_at(pl, "Way construction cost to height: cash: " + pl.get_current_cash() + " build cost: " + build_cost, world.get_time()) industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) build_check_month = world.get_time().ticks + (build_check_time(build_cost) + world.get_time().ticks_per_month) return error_handler() } if ( !planned_way.is_available(world.get_time()) ) { planned_way = find_object("way", wt_road, planned_way.get_topspeed()) } local err = construct_road(pl, c_start, c_end, planned_way ) print("Way construction cost: " + (d-pl.get_current_cash()) ) if (err) { if ( c_start.len()>0 && c_end.len()>0) { print("Failed to build way from " + coord_to_string(c_start[0]) + " to " + coord_to_string(c_end[0])) } else if (err) { print("Failed to build way from " + coord_to_string(c_start) + " to " + coord_to_string(c_end)) } if (err == "No route") { print("No route found from " + coord_to_string(c_start[0])+ " to " + coord_to_string(c_end[0])) // no point to try again return error_handler() } // try again return restart_with_phase0() } if ( print_message_box == 1 ) { gui.add_message_at(our_player, "Build road from " + coord_to_string(c_start) + " to " + coord_to_string(c_end), world.get_time()) } phase ++ } case 2: // build station and extensions { local err = null local obj_building = tile_x(c_start.x, c_start.y, c_start.z).find_object(mo_building) if ( obj_building != null && obj_building.get_owner() != our_player ) { if (debug) gui.add_message_at(pl, " --- tile to build station not free", c_start) build_check_month = world.get_time().ticks + world.get_time().ticks_per_month return restart_with_phase0() //return error_handler() } else if ( c_generate_start || obj_building == null) { err = build_road_station(c_start, planned_station) //err = command_x.build_station(pl, c_start, planned_station ) } if (err) { if (debug) gui.add_message_at(pl, "Failed to build road station at " + coord_to_string(c_start) + " [" + err + "]", c_start)// try again if ( print_message_box == 2 ) { gui.add_message_at(pl, "Failed to build road station at " + coord_to_string(c_start) + " error " + err, world.get_time()) } return restart_with_phase0() } else { c_generate_start = false // station build, do not search for another place } err = build_road_station(c_end, planned_station) //err = command_x.build_station(pl, c_end, planned_station ) if (err) { if (debug) gui.add_message_at(pl, "Failed to build road station at " + coord_to_string(c_end) + " [" + err + "]", c_end) // try again return restart_with_phase0() } else { c_generate_end = false // station build, do not search for another place } if (finalize) { // store place of unload station for future use local fs = ::station_manager.access_freight_station(fdest) if (fs.road_unload == null) { fs.road_unload = c_end } } if (debug && c_trial_route>1) { gui.add_message_at(pl, "Completed route from " + coord_to_string(c_start) + " to " + coord_to_string(c_end) + " after " + c_trial_route + " attempts", c_end) } if ( print_message_box == 2 ) { gui.add_message_at(our_player, "Build station on " + coord_to_string(c_start) + " and " + coord_to_string(c_end), world.get_time()) } // local asf = astar_route_finder(wt_road) local result = asf.search_route([c_start], [c_end]) if ( "routes" in result ) { } else { if ( debug ) ::debug.pause() sleep() return r_t(RT_TOTAL_FAIL) } if ( result.routes.len() <= 3 ) { //gui.add_message_at(pl, "route len < 3 tiles ", c_end) local extension = find_extension(wt_road) local start_h = tile_x(c_start.x, c_start.y, c_start.z).get_halt().get_name() local end_h = tile_x(c_end.x, c_end.y, c_end.z).get_halt().get_name() if ( extension != null && start_h == end_h ) { remove_tile_to_empty(c_start, wt_road, 0) command_x.build_station(our_player, c_start, extension) remove_tile_to_empty(c_end, wt_road, 0) command_x.build_station(our_player, c_end, extension) if ( result.routes.len() == 3 ) { foreach(node in result.routes) { local tile = tile_x(node.x, node.y, node.z) if ( tile.find_object(mo_building) == null ) { remove_tile_to_empty(tile, wt_road, 0) } } } } return r_t(RT_TOTAL_SUCCESS) } phase += 3 } case 5: // build depot { if ( print_message_box == 3 ) { gui.add_message_at(our_player, "___________ exists depots road ___________", world.get_time()) gui.add_message_at(our_player," c_start pos: " + coord_to_string(c_start) + " : c_end pos: " + coord_to_string(c_end), world.get_time()) } // search depot to range start and end station local depot_found = search_depot(c_start, wt_road) local starts_field = c_start if ( !depot_found ) { depot_found = search_depot(c_end, wt_road) starts_field = c_end } if ( !depot_found && print_message_box == 3 ) { gui.add_message_at(pl," *** depot not found *** ", world.get_time()) } else if ( print_message_box == 3 ) { gui.add_message_at(pl," ---> depot found : " + depot_found.get_pos(), coord_to_string(depot_found)) } local err = null // build road to depot if ( depot_found ) { c_depot = depot_found err = command_x.build_road(pl, starts_field, c_depot, planned_way, false, true) //err = construct_road(our_player, station_to_depot, c_depot, planned_way) } else { local i = c_route.len()-1 if ( i > 8 ) { i -= 8 } local trial = 0 local err = null do { // try 3x to find road to suitable depot spot err = construct_road_to_depot(pl, c_route[i], planned_way) trial ++ } while (err != null && err != "No route" && trial < 3) if (err) { // we do not like to fail at this phase, try to find another spot phase = 3 return r_t(RT_PARTIAL_SUCCESS) } // depot already existing ? if (c_depot.find_object(mo_depot_road) == null) { // no: build local err = command_x.build_depot(pl, c_depot, planned_depot ) if (err) { print("Failed to build depot at " + coord_to_string(c_depot)) return error_handler() } if (finalize) { // store depot location local fs = ::station_manager.access_freight_station(fsrc) if (fs.road_depot == null) { fs.road_depot = c_depot } } } if ( print_message_box == 3 ) { gui.add_message_at(our_player, "Build depot on " + coord_to_string(c_depot), world.get_time()) } } phase ++ } case 6: // create schedule { local sched = schedule_x(wt_road, []) sched.entries.append( schedule_entry_x(c_start, 100, 0) ); sched.entries.append( schedule_entry_x(c_end, 0, 0) ); c_sched = sched phase ++ } case 7: // create line and set schedule { pl.create_line(wt_road) // find the line - it is a line without schedule and convoys local list = pl.get_line_list() foreach(line in list) { if (line.get_waytype() == wt_road && line.get_schedule().entries.len()==0) { // right type, no schedule -> take this. c_line = line break } } // set schedule c_line.change_schedule(pl, c_sched); phase ++ } case 8: // append vehicle_constructor and set line { local c = vehicle_constructor_t() c.p_depot = depot_x(c_depot.x, c_depot.y, c_depot.z) c.p_line = c_line c.p_convoy = planned_convoy if ( world.get_time().year < 1935 && get_set_name() == "pak64.german" ) { c.p_count = min(planned_convoy.nr_convoys, 6) } else { c.p_count = min(planned_convoy.nr_convoys, 3) } append_child(c) local toc = get_ops_total(); print("road_connector wasted " + (toc-tic) + " ops") c_generate_start = c_start == null c_generate_end = c_end == null // rename line local line_name = c_line.get_name() local str_search = ") " + translate("Line") local st_names = c_line.get_schedule().entries if ( line_name.find(str_search) != null ) { local new_name = translate("road") + " " + translate(freight) + " " + st_names[0].get_halt(pl).get_name() + " - " + st_names[1].get_halt(pl).get_name() c_line.set_name(new_name) } phase ++ return r_t(RT_PARTIAL_SUCCESS) } case 9: // currently not used { } } if (finalize) { industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_built) if ( industry_manager.get_combined_link(fsrc, fdest, freight) > 0 ) { local a = industry_manager.get_combined_link(fsrc, fdest, freight) - 1 industry_manager.set_combined_link(fsrc, fdest, freight, a) } } industry_manager.access_link(fsrc, fdest, freight).append_line(c_line) //gui.add_message_at(pl, "Build cost link " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 2), world.get_time()) local cs = c_start local ce = c_end /* if (c_start.len()>0 && c_end.len()>0) { local cs = c_start[0] local ce = c_end[0] }*/ local st = halt_x.get_halt(cs, pl) local f_name = ["", ""] if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { f_name[0] = fl_st[0].get_name() } else { f_name[0] = st.get_name() } } st = halt_x.get_halt(ce, pl) if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { f_name[1] = fl_st[0].get_name() } else { f_name[1] = st.get_name() } } local msgtext = format(translate("%s build road line from %s (%s) to %s (%s)"), pl.get_name(), f_name[0], coord_to_string(cs), f_name[1], coord_to_string(ce)) //gui.add_message_at(pl, pl.get_name() + " build road line from " + f_name[0] + " (" + coord_to_string(cs) + ") to " + f_name[1] + " (" + coord_to_string(ce) + ")", c_start) gui.add_message_at(pl, msgtext, c_start) return r_t(RT_TOTAL_SUCCESS) } function restart_with_phase0() { if (c_generate_start) { c_start = null } if (c_generate_end ) { c_end = null } phase = 0 return r_t(RT_PARTIAL_SUCCESS) } function error_handler() { local r = r_t(RT_TOTAL_FAIL) // TODO rollback if (reports.len()>0) { // there are alternatives print("Delivering alternative connector") r.report = reports.pop() if (r.report.action && r.report.action.getclass() == amphibious_connection_planner_t) { print("Delivering amphibious_connection_planner_t") r.node = r.report.action r.report = null } } else { print("Error during setup of road connection.") industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_failed); } return r } function construct_road(pl, starts, ends, way) { local as = astar_builder() as.builder = way_planner_x(pl) as.way = way as.builder.set_build_types(way) as.bridger = pontifex(pl, way) if (as.bridger.bridge == null) { as.bridger = null } local res = as.search_route(starts, ends) if ("err" in res) { return res.err } c_start = res.start c_end = res.end c_route = res.routes } function construct_road_to_depot(pl, start, way) { local as = depot_pathfinder() as.builder = way_planner_x(pl) as.way = way as.builder.set_build_types(way) local res = as.search_route(start) if ("err" in res) { return res.err } local d = res.end c_depot = tile_x(d.x, d.y, d.z) } function build_road_station(tile, station) { local err = null local d = tile_x(tile.x, tile.y, tile.z).get_way_dirs(wt_road) local rotation = 0 switch (d) { case 2: rotation = 1 break case 1: rotation = 2 break case 8: rotation = 3 break case 10: rotation = 1 break default: } err = command_x.build_station(our_player, tile, station, rotation ) return err } } class depot_pathfinder extends astar_builder { function estimate_distance(c) { local t = tile_x(c.x, c.y, c.z) if (t.is_empty() && t.get_slope()==0) { return 0 } local depot = t.find_object(mo_depot_road) if (depot && depot.get_owner().nr == our_player_nr) { return 0 } return 10 } function add_to_open(c, weight) { if (c.dist == 0) { // test for depot local t = tile_x(c.x, c.y, c.z) if (t.is_empty()) { // depot not existing, we must build, increase weight weight += 25 * cost_straight } } base.add_to_open(c, weight) } function search_route(start) { prepare_search() local dist = estimate_distance(start) add_to_open(ab_node(start, null, 1, dist+1, dist, 0), dist+1) search() if (route.len() > 0) { for (local i = 1; i= 1 ) { //gui.add_message_at(pl, "no build line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ") to many links", world.get_time()) return r_t(RT_TOTAL_FAIL) } local build_cash = player_x(our_player.nr).get_current_cash() + (((player_x(our_player.nr).get_current_net_wealth()/100) - player_x(our_player.nr).get_current_cash())/2) local build_cost = industry_manager.get_link_build_cost(fsrc, fdest, freight, 3) if ( industry_manager.get_link_build_cost(fsrc, fdest, freight, 0) > 0 ) { build_cost = industry_manager.get_link_build_cost(fsrc, fdest, freight, 0) } if ( (build_check_month > world.get_time().ticks || build_cost > build_cash) && industry_manager.get_combined_link(fsrc, fdest, freight) == 0 ) { // not build link if ( debug ) gui.add_message_at(our_player, "#ship_conn# not build line : build_check_month = " + build_check_month + " or build cost link > cash : build cost line " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 3) + " | build cost link " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 0), world.get_time()) if ( debug ) gui.add_message_at(our_player, " ---> link " + fsrc + " " + fsrc.get_name() + " - " + fdest.get_name(), world.get_time()) industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_missing) return r_t(RT_TOTAL_FAIL) } } switch(phase) { case 0: { if ( print_message_box > 0 ) { gui.add_message_at(pl, "______________________ build ship ______________________", world.get_time()) gui.add_message_at(pl, " line from " + fsrc.get_name() + " (" + coord_to_string(fs[0]) + ") to " + fdest.get_name() + " (" + coord_to_string(fd[0]) + ")", world.get_time()) //::debug.pause() } // find flat harbour building if (planned_harbour_flat == null) { local station_list = building_desc_x.get_available_stations(building_desc_x.flat_harbour, wt_water, good_desc_x(freight)) planned_harbour_flat = industry_connection_planner_t.select_station(station_list, 1, planned_convoy.capacity) } if ( print_message_box == 2 ) { local flat = "----" if ( planned_harbour_flat ) { flat = planned_harbour_flat.get_name() } gui.add_message_at(pl," ... build station: " + planned_station.get_name() + " / " + flat, world.get_time()) } phase ++ } case 1: // find empty water tiles if (c_start == null) { c_start = find_anchorage(fsrc, planned_station, planned_harbour_flat, c_harbour_tiles) } if (c_end == null) { c_end = find_anchorage(fdest, planned_station, planned_harbour_flat, c_harbour_tiles) } if (c_start.len()>0 && c_end.len()>0) { if ( print_message_box == 2 ) { gui.add_message_at(pl, "Water way from " + coord_to_string(c_start[0]) + " to " + coord_to_string(c_end[0]), world.get_time()) } phase ++ } else { if ( print_message_box == 2 ) { gui.add_message_at(pl, " ... ERROR No station places found ", world.get_time()) gui.add_message_at(pl, "---------------------- ship end -----------------------", world.get_time()) } print("No station places found") return error_handler() } case 2: // find path between both factories { local err = find_route() if (err) { print("No way from " + coord_to_string(c_start[0])+ " to " + coord_to_string(c_end[0])) if ( print_message_box == 2 ) { gui.add_message_at(pl, "No way from " + coord_to_string(c_start[0]) + " to " + coord_to_string(c_end[0]), world.get_time()) } return error_handler() } phase ++ } case 3: // build harbour and extensions { local key local err = null { //if ( tile_x(c_start.x, c_start.y, c_start.z).is_empty() ) { key = coord3d_to_key(c_start[0]) if (key in c_harbour_tiles) { if ( c_harbour_tiles[key].get_halt() && c_harbour_tiles[key].get_halt().get_owner().nr == our_player_nr && c_harbour_tiles[key].find_object(mo_building) && c_harbour_tiles[key].find_object(mo_building).get_desc().get_type()==building_desc_x.station ) { // station from player exists - combined station if ( print_message_box == 2 ) { gui.add_message_at(our_player, "Cannot place any harbour at " + coord_to_string(c_harbour_tiles[key]) + " station exists :: search new place", c_harbour_tiles[key]) } // to do search new field c_start.clear() c_start = find_new_place(c_harbour_tiles[key].get_halt(), planned_station, planned_harbour_flat, c_harbour_tiles) key = coord3d_to_key(c_start[0]) if ( c_harbour_tiles[key].get_halt() && c_harbour_tiles[key].get_halt().get_owner().nr == our_player_nr && c_harbour_tiles[key].find_object(mo_building) && c_harbour_tiles[key].find_object(mo_building).get_desc().get_type()==building_desc_x.station ) { gui.add_message_at(our_player, "Cannot place any harbour at " + coord_to_string(c_harbour_tiles[key]), c_harbour_tiles[key]) return r_t(RT_TOTAL_FAIL) } else { err = build_harbour(c_harbour_tiles[key], c_start) } } else { err = build_harbour(c_harbour_tiles[key], c_start) if (err) { if ( c_start.len() > 1 ) { c_start.clear() c_start = find_anchorage(fsrc, planned_station, planned_harbour_flat, c_harbour_tiles) key = coord3d_to_key(c_start[0]) if ( c_start.len() > 0 ) { err = null err = build_harbour(c_harbour_tiles[key], c_start) } } } } } if (err) { print("Failed to build harbour at " + key + " / " + err) if ( print_message_box == 2 ) { gui.add_message_at(pl, " --- Failed to build harbour at " + key + " / " + err, world.get_time()) gui.add_message_at(pl, " --- c_start " + coord_to_string(c_start[0]), world.get_time()) } return r_t(RT_TOTAL_FAIL) } } // check station connection to factory or combined station if (err == null) { //remove_field(c_end[0]) key = coord3d_to_key(c_end[0]) if (key in c_harbour_tiles) { if ( c_harbour_tiles[key].find_object(mo_building) != null ) { if (debug) gui.add_message_at(pl, " --- tile to build harbour not free", world.get_time()) return r_t(RT_TOTAL_FAIL) } err = build_harbour(c_harbour_tiles[key], c_end) } } if (err) { print("Failed to build harbour at " + key + " / " + err) if ( print_message_box == 5 ) { gui.add_message_at(pl, " --- Failed to build harbour at " + key + " / " + err, world.get_time()) gui.add_message_at(pl, " --- c_end " + coord_to_string(c_end[0]), world.get_time()) } return error_handler() } c_harbour_tiles = null if ( print_message_box == 2 ) { gui.add_message_at(pl, "Build harbour on " + coord_to_string(c_start[0]) + " and " + coord_to_string(c_end[0]), world.get_time()) } phase ++ } case 4: // find route again after harbour was built { if (c_start.len()>1 || c_end.len()>1) { local err = find_route() if (err) { print("No way2 from " + coord_to_string(c_start[0])+ " to " + coord_to_string(c_end[0])) return error_handler() } } phase ++ } case 5: // build depot { // search depot to range start and end station local depot_found = search_depot(c_start[0], wt_water) local starts_field = c_start[0] if ( !depot_found ) { depot_found = search_depot(c_end[0], wt_water) starts_field = c_end[0] } if ( !depot_found && print_message_box == 3 ) { gui.add_message_at(pl," *** depot not found *** ", world.get_time()) } else if ( print_message_box == 3 ) { gui.add_message_at(pl," ---> depot found : " + depot_found.get_pos(), coord_to_string(depot_found)) } // build ship to depot if ( depot_found ) { c_depot = depot_found //local err = command_x.build_road(pl, starts_field, c_depot, planned_way, false, true) } else { // depot already existing ? local depot_tiles = [] local tile_range = 4 depot_tiles.append(tile_x(c_start[0].x-tile_range, c_start[0].y-tile_range, c_start[0].z)) depot_tiles.append(tile_x(c_start[0].x+tile_range, c_start[0].y-tile_range, c_start[0].z)) depot_tiles.append(tile_x(c_start[0].x-tile_range, c_start[0].y+tile_range, c_start[0].z)) depot_tiles.append(tile_x(c_start[0].x+tile_range, c_start[0].y+tile_range, c_start[0].z)) for ( local i = 0; i < depot_tiles.len(); i++ ) { //gui.add_message_at(pl, "depot_tiles[i].is_water() " + coord_to_string(depot_tiles[i]) + " " + depot_tiles[i].is_water(), depot_tiles[i]) //gui.add_message_at(pl, "depot_tiles[i].is_empty() " + coord_to_string(depot_tiles[i]) + " " + depot_tiles[i].is_empty(), depot_tiles[i]) local tile_halt = ::halt_x.get_halt(depot_tiles[i], player_x(1)) if ( tile_halt != null ) { //gui.add_message_at(pl, "tile_halt.get_halt() " + coord_to_string(depot_tiles[i]) + " " + tile_halt.get_halt(player_x(1)), depot_tiles[i]) } if ( depot_tiles[i].is_water() && tile_halt == null && depot_tiles[i].get_objects().get_count()==0 ) { // c_depot = depot_tiles[i] //gui.add_message_at(pl, "depot tile " + coord_to_string(c_depot), c_depot) break } } if (c_depot.find_object(mo_depot_water) == null) { // no: build local err = command_x.build_depot(pl, c_depot, planned_depot ) if (err) { print("Failed to build depot at " + coord_to_string(c_depot)) return error_handler() } if (finalize) { // store depot location local fs = ::station_manager.access_freight_station(fsrc) if (fs.ship_depot == null) { fs.ship_depot = c_depot } } } if ( print_message_box == 3 ) { gui.add_message_at(pl, "Build depot on " + coord_to_string(c_depot), world.get_time()) } } phase ++ } case 6: // create schedule { local sched = schedule_x(wt_water, []) sched.entries.append( schedule_entry_x(c_start[0], 100, 0) ); sched.entries.append( schedule_entry_x(c_end[0], 0, 0) ); if ( sched.entries[0].get_halt(pl).get_name() == sched.entries[1].get_halt(pl).get_name() ) { // set new start //gui.add_message_at(pl," ... start name (" + sched.entries[0].get_halt(pl).get_name() + ") == end name (" + sched.entries[1].get_halt(pl).get_name() + ")", world.get_time()) //gui.add_message_at(pl," ... c_start len = " + c_start.len(), world.get_time()) c_start.clear() c_start = find_anchorage(fsrc, planned_station, planned_harbour_flat, c_harbour_tiles) sched.entries[0] = schedule_entry_x(c_start[0], 100, 0); //gui.add_message_at(pl," ... new start name (" + sched.entries[0].get_halt(pl).get_name() + ")", world.get_time()) //::debug.pause() } c_sched = sched phase ++ } case 7: // create line and set schedule { pl.create_line(wt_water) // find the line - it is a line without schedule and convoys local list = pl.get_line_list() foreach(line in list) { if (line.get_waytype() == wt_water && line.get_schedule().entries.len()==0) { // right type, no schedule -> take this. c_line = line break } } // set schedule c_line.change_schedule(pl, c_sched); phase ++ } case 8: // append vehicle_constructor ans set line { local c = vehicle_constructor_t() c.p_depot = depot_x(c_depot.x, c_depot.y, c_depot.z) c.p_line = c_line c.p_convoy = planned_convoy c.p_count = min(planned_convoy.nr_convoys, 1) // 1 ship to begin append_child(c) local toc = get_ops_total(); print("ship_connector wasted " + (toc-tic) + " ops") // rename line local line_name = c_line.get_name() local str_search = ") " + translate("Line") local st_names = c_line.get_schedule().entries if ( line_name.find(str_search) != null ) { local new_name = translate("Ship") + " " + translate(freight) + " " + st_names[0].get_halt(pl).get_name() + " - " + st_names[1].get_halt(pl).get_name() c_line.set_name(new_name) } phase ++ return r_t(RT_PARTIAL_SUCCESS) } case 9: // currently not used { } } if (finalize) { industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_built) if ( industry_manager.get_combined_link(fsrc, fdest, freight) > 0 ) { local a = industry_manager.get_combined_link(fsrc, fdest, freight) - 1 industry_manager.set_combined_link(fsrc, fdest, freight, a) } } industry_manager.access_link(fsrc, fdest, freight).append_line(c_line) //gui.add_message_at(pl, "Build cost link " + industry_manager.get_link_build_cost(fsrc, fdest, freight, 3), world.get_time()) /* local cs = [] local ce = [] if (c_start.len()>0 && c_end.len()>0) { cs.append(tile_x(c_start[0].x, c_start[0].y, c_start[0].z)) ce.append(tile_x(c_end[0].x, c_end[0].y, c_end[0].z)) } else { cs.append(c_start[0]) ce.append(c_end[0]) } gui.add_message_at(pl, pl.get_name() + " build ship line from " + f_name[0] + " (" + coord_to_string(square_x(cs[0].x, cs[0].y)) + ") to " + f_name[1] + " (" + coord_to_string(square_x(ce[0].x, ce[0].y)) + ")", c_start) */ if (c_start.len()>0 && c_end.len()>0) { local st = halt_x.get_halt(c_start[0], pl) local f_name = ["", ""] if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { f_name[0] = fl_st[0].get_name() } else { f_name[0] = st.get_name() } } st = halt_x.get_halt(c_end[0], pl) if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { f_name[1] = fl_st[0].get_name() } else { f_name[1] = st.get_name() } } local msgtext = format(translate("%s build ship line from %s (%s) to %s (%s)"), pl.get_name(), f_name[0], coord_to_string(c_start[0]), f_name[1], coord_to_string(c_end[0])) //gui.add_message_at(pl, pl.get_name() + " build ship line from " + f_name[0] + " (" + coord_to_string(c_start[0]) + ") to " + f_name[1] + " (" + coord_to_string(c_end[0]) + ")", c_start[0]) gui.add_message_at(pl, msgtext, c_start[0]) } else { local st = halt_x.get_halt(c_start, pl) local f_name = ["", ""] if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { f_name[0] = fl_st[0].get_name() } else { f_name[0] = st.get_name() } } st = halt_x.get_halt(c_end, pl) if ( st ) { local fl_st = st.get_factory_list() if ( fl_st.len() > 0 ) { f_name[1] = fl_st[0].get_name() } else { f_name[1] = st.get_name() } } local msgtext = format(translate("%s build ship line from %s (%s) to %s (%s)"), pl.get_name(), f_name[0], coord_to_string(c_start), f_name[1], coord_to_string(c_end)) //gui.add_message_at(pl, pl.get_name() + " build ship line from " + f_name[0] + " (" + coord_to_string(c_start) + ") to " + f_name[1] + " (" + coord_to_string(c_end) + ")", c_start) gui.add_message_at(pl, msgtext, c_start) } return r_t(RT_TOTAL_SUCCESS) } function error_handler() { local r = r_t(RT_TOTAL_FAIL) // TODO rollback if (reports.len()>0) { // there are alternatives print("Delivering alternative connector") r.report = reports.pop() if (r.report.action && r.report.action.getclass() == amphibious_connection_planner_t) { print("Delivering amphibious_connection_planner_t") r.node = r.report.action r.report = null } } else { print("Error during setup of ship connection.") industry_manager.set_link_state(fsrc, fdest, freight, industry_link_t.st_failed); } return r } static function find_anchorage(factory, planned_station, planned_harbour_flat, c_harbour_tiles) { // try to find tiles already covered by some harbours local tile_list = ::finder.find_water_places( ::finder.get_tiles_near_factory(factory) ) local halt_list = factory.get_halt_list() local anch = [] if (tile_list.len()>0 && halt_list.len()>0) { foreach(tile in tile_list) { local h = halt_x.get_halt(tile, our_player) if (h) { foreach(halt in halt_list) { if ( (h <=> halt) == 0) { anch.append(tile) } } } } } if (anch.len()>0) { return anch } // find places to build harbour if (tile_list.len()>0) { foreach(tile in tile_list) { for(local d = 1; d<16; d*=2) { local to = tile.get_neighbour(wt_all, d) if ( print_message_box == 2 && to != null && to.is_empty() ) { gui.add_message_at(our_player, " ... place finder: to.is_empty() " + coord3d_to_string(to) + " = " + to.is_empty(), world.get_time()) } if (to && get_harbour_type_for_tile(to, planned_station, planned_harbour_flat, d) > 0) { anch.append(tile) c_harbour_tiles[coord3d_to_key(tile)] <- to break } /*if (to && get_harbour_type_for_tile(to, planned_station, planned_harbour_flat, d) > 0) { anch.append(tile) c_harbour_tiles[coord3d_to_key(tile)] <- to break }*/ } } } return anch } static function find_new_place(halt, planned_station, planned_harbour_flat, c_harbour_tiles) { local tile_list = ::finder.find_water_places( ::finder.get_tiles_near_halt(halt) ) local anch = [] // find places to build harbour if (tile_list.len()>0) { foreach(tile in tile_list) { for(local d = 1; d<16; d*=2) { local to = tile.get_neighbour(wt_all, d) if ( print_message_box == 2 && to.is_empty() ) { gui.add_message_at(our_player, " ... place finder: to.is_empty() " + coord3d_to_string(to) + " = " + to.is_empty(), world.get_time()) } if (to && get_harbour_type_for_tile(to, planned_station, planned_harbour_flat, d) > 0) { anch.append(tile) c_harbour_tiles[coord3d_to_key(tile)] <- to break } /*if (to && get_harbour_type_for_tile(to, planned_station, planned_harbour_flat, d) > 0) { anch.append(tile) c_harbour_tiles[coord3d_to_key(tile)] <- to break }*/ } } } return anch } /** * Checks whether we can build harbour: empty tile, slopes, enough space on water * returns type of harbour that can be placed here: 0 - nothing, 1 - harbour, 2 - flat harbour */ static function get_harbour_type_for_tile(tile, planned_harbour, planned_harbour_flat, d_water_to_land /*direction*/) { local type = 0 // 1 - harbour, 2 - flat harbour if (!tile.is_empty()) { return 0 } // first slope if (planned_harbour) { if (tile.get_slope() != 0) { // can place on slopes, target tile is sloped and empty -> we can terraform type = 1 } else if (planned_harbour_flat == null && command_x.can_set_slope(our_player, tile, dir.to_slope(d_water_to_land))==null) { // no slope, no flat dock, but terraforming possible type = 1 } } if (planned_harbour_flat) { if (tile.get_slope() == 0) { // flat place type = 2 } else if (planned_harbour == null && command_x.can_set_slope(our_player, tile, 0)==null) { // not flat, not harbour for slopes, can flatten type = 2 } } if (type == 0) { return 0 } // then space local size = type == 1 ? planned_harbour.get_size(0) : planned_harbour_flat.get_size(0) return finder.check_harbour_place(tile, size.x*size.y, dir.backward(d_water_to_land)) ? type : 0 } /** * Checks whether there is already a harbour on this tile. * @returns corresponding halt_x object (or null) */ static function get_harbour_halt(tile) { local halt = tile.get_halt() if (halt && halt.get_owner().nr == our_player_nr) { // our halt local harb = tile.find_object(mo_building) if (harb && (harb.get_desc().get_type()==building_desc_x.harbour || harb.get_desc().get_type()==building_desc_x.flat_harbour) ) { return halt } } return null } /** * Build harbour at @p tile, * replace water with an array containing all water tiles next to the harbour */ function build_harbour(tile, water_arr) { local water = water_arr[0] local err = null local len = 0 local dif = { x=tile.x-water.x, y=tile.y-water.y} if ( print_message_box == 2 ) { gui.add_message_at(our_player, " --- Place harbour at " + coord3d_to_string(tile) + " to access " + coord3d_to_string(water), world.get_time()) } print("Place harbour at " + coord3d_to_string(tile) + " to access " + coord3d_to_string(water) ) if ( tile.get_halt() && tile.get_halt().get_owner().nr == our_player_nr && tile.find_object(mo_building) && tile.find_object(mo_building).get_desc().get_type()==building_desc_x.station ) { // gui.add_message_at(our_player, "Cannot place any harbour at " + coord_to_string(tile) + " station exists", tile) // to do search new field } if (get_harbour_halt(tile)) { // already there } else { local d_water_to_land = coord_to_dir(dif) local type = get_harbour_type_for_tile(tile, planned_station, planned_harbour_flat, d_water_to_land) switch(type) { case 0: { err = "Cannot place harbour here" gui.add_message_at(our_player, "Cannot place any harbour at " + coord_to_string(tile), tile) // no build dock on station tile destroy exists way line // to do break } case 1: { // harbour on slope local slope = dir.to_slope(d_water_to_land) // terraform ?? if (tile.get_slope() != slope && tile.get_slope() != 2*slope) { err = command_x.set_slope(our_player, tile, slope ) if (err) gui.add_message_at(our_player, "Failed to change slope at " + coord_to_string(tile) +"\n" + err, tile) } if (err == null) { err = command_x.build_station(our_player, tile, planned_station) if (err) gui.add_message_at(our_player, "Failed to harbour at " + coord_to_string(tile) +"\n" + err, tile) } local size = planned_station.get_size(0) len = size.x*size.y break } case 2: { // flat dock // flatten slope if (tile.get_slope() != 0) { err = command_x.set_slope(our_player, tile, 0 ) if (err) gui.add_message_at(our_player, "Failed to flatten slope at " + coord_to_string(tile) +"\n" + err, tile) } if (err == null) { local rotation = 0 switch (dir.backward(d_water_to_land)) { case 2: rotation = 1 break case 1: rotation = 2 break case 8: rotation = 3 default: } err = command_x.build_station(our_player, tile, planned_harbour_flat, rotation) if (err) gui.add_message_at(our_player, "Failed to build flat harbour at " + coord_to_string(tile) +"\n" + err, tile) } local size = planned_harbour_flat.get_size(0) len = size.x*size.y break } } } if (err) { return err; } // halt at this harbour local harbour_halt = halt_x.get_halt(tile, our_player) water_arr.clear() // all water tiles near harbour for(local l=0; lharbour_halt) != 0) ) { // we do not want to use this halt continue } water_arr.append(to) } } catch(ev) {/* ignore */} } } if (water_arr.len()==0) { // should not happen print("No non-harbour water tiles found near " + coord_to_string(water)) water_arr.append(water) } return null } function find_route() { local as = route_finder_water() local res = as.search_route(c_start, c_end) if ("err" in res) { return res.err } c_start = [res.start ] c_end = [res.end ] local asd = route_finder_water_depot() res = asd.search_route(as.route) if ("err" in res) { return res.err } local d = res.depot c_depot = tile_x(d.x, d.y, d.z) } function repair_keys() { local cht = {} foreach(key,val in c_harbour_tiles) { local rkey = repair_key(key) cht[rkey] <- val } c_harbour_tiles = cht } static function repair_key(key) { // replace ``dd:dd:dd'' by ``coord3d_dd_dd_dd'' local a = split(key, ":-") if (a.len() == 1) { return key } local rkey = "coord3d_" + a[0] for(local i=1; i 0) { return { start = route.top(), end = route[0] } } print("No water depot route found") return { err = "No route" } } } class route_finder_water_depot extends route_finder_water { function estimate_distance(c) { local t = tile_x(c.x, c.y, c.z) if (t.is_water() && t.get_objects().get_count()==0) { return 0 } local depot = t.find_object(mo_depot_water) if (depot && depot.get_owner().nr == our_player_nr) { return 0 } return 10 } function add_to_open(c, weight) { if (c.dist == 0) { // test for depot local t = tile_x(c.x, c.y, c.z) if (t.get_objects().get_count()==0) { // depot not existing, we must build, increase weight weight += 25 * cost_straight } } base.add_to_open(c, weight) } function search_route(watertiles) { prepare_search() // do not place depot on route between harbours foreach(w in watertiles) { add_to_close(w) } // add neighboring tiles of route to open list for(local i=0; i 0) { return { depot = route[0] } } print("No water depot route found") return { err = "No route" } } } simutrans-124.5/simutrans/ai/sqai_rail/station_manager.nut000066400000000000000000000026721520675460500240600ustar00rootroot00000000000000class freight_station_t { factory = null // factory_x road_depot = null // tile_x road_unload = null // tile_x ship_depot = null // tile_x rail_depot = null // tile_x rail_unload = null // tile_x constructor(f) { factory = f } function _save() { return ::saveinstance("freight_station_t", this) } } class freight_station_manager_t extends node_t { freight_station_list = null use_raw_names = 0 /// repair old savegames: use untranslated factory name as key constructor(urn) { base.constructor("freight_station_manager_t") freight_station_list = {} ::station_manager = this use_raw_names = urn } /// Generate unique key from link data static function key(factory) { return (factory.get_raw_name() + coord_to_string(factory)).toalnum() } /** * Access freight_station_t data, create node if not existent. */ function access_freight_station(factory) { local k = key(factory) local res try { res = freight_station_list[k] } catch(ev) { local fs = freight_station_t(factory) freight_station_list[k] <- fs res = fs } return res } /// Repair list: use untranslated factory names function repair_keys() { if (use_raw_names == 1) { return } local fsl = {} foreach(val in freight_station_list) { local rkey = key(val.factory) fsl[rkey] <- val } freight_station_list = fsl use_raw_names = 1 } } simutrans-124.5/simutrans/ai/sqai_rail/vehicle_constructor.nut000066400000000000000000000063321520675460500247660ustar00rootroot00000000000000class vehicle_constructor_t extends node_t { // input data p_depot = null // depot_x p_line = null // line_x p_convoy = null // prototyper_t p_count = 0 p_withdraw = false // generated data c_cnv = null c_wt = 0 // step-by-step construct convoys phase = 0 constructor() { base.constructor("vehicle_constructor_t") debug = false } function step() { local pl = our_player c_wt = p_convoy.veh[0].get_waytype() //local wt_name = ["", "road", "rail", "water"] //gui.add_message_at(our_player, "convoy " + p_convoy.veh[0].get_name() + " - " + wt_name[c_wt], world.get_time()) switch(phase) { case 0: // create the convoy (and the first vehicles) { p_depot.append_vehicle(pl, convoy_x(0), p_convoy.veh[0]) // find the newly created convoy // it should be the last in the list local cnv_list = p_depot.get_convoy_list() local trythis = cnv_list.top() if (check_convoy(trythis)) { c_cnv = trythis } if (c_cnv == null) { foreach(cnv in cnv_list) { if (check_convoy(cnv)) { c_cnv = cnv break } } } phase ++ } case 1: // complete the convoy { local vlist = c_cnv.get_vehicles() while (vlist.len() < p_convoy.veh.len()) { local old_length = vlist.len() local res = p_depot.append_vehicle(pl, c_cnv, p_convoy.veh[ old_length ]) vlist = c_cnv.get_vehicles() if (old_length == vlist.len()) { print("Appending vehicle failed.") // appending failed return r_t(RT_TOTAL_FAIL) } } phase ++ } case 2: // set line { c_cnv.set_line(pl, p_line) phase ++ } case 3: // withdraw old vehicles { if (p_withdraw) { local cnv_list = p_line.get_convoy_list() foreach(o_cnv in cnv_list) { if (o_cnv.id != c_cnv.id && !o_cnv.is_withdrawn()) { o_cnv.toggle_withdraw(pl) } } p_withdraw = false } phase ++ } case 4: // start { p_depot.start_convoy(pl, c_cnv) p_count -- if (p_count > 0) { phase = 0 return r_t(RT_PARTIAL_SUCCESS) } else { phase ++ } } } return r_t(RT_TOTAL_SUCCESS) } function check_convoy(cnv) { // check whether this convoy is for our purpose if (cnv.get_line() == null && cnv.get_waytype() == c_wt) { // now test for equal vehicles local vlist = cnv.get_vehicles() local len = vlist.len() if (len <= p_convoy.veh.len()) { local equal = true; for (local i=0; equal && i we set meaningful defaults here. # # load/save the files in the users or the program directory directory? (default: 0 = user directory) # ATTENTION! # will be only used if this file is located in the program directory at config/simuconf.tab! # #singleuser_install = 0 # #################################program stuff################################## # # # Do not delete these comment line! (Needed for installer) # Default pak file path - which graphics set you want to play with? # Nothing means automatic selection # # ATTENTION! # This value will only be used if this file is located in the data directory at config/simuconf.tab # and will be overwritten by the settings from simutrans/simuconf.tab in the user directory. # #pak_file_path = pak/ #pak_file_path = pak.german/ #pak_file_path = pak128/ #pak_file_path = pak.japan/ #pak_file_path = pak.winter/ #pak_file_path = pak.ttd/ # #################################routing stuff################################## # maximum number of position tested during a way search # 100000 should be ok even for large maps with ships # 10000 is ok for everything else (consumes 16*x Bytes main memory, no further harm) max_route_steps = 1000000 # # How many tiles to check before giving up on finding a free bay at a stop? (200 default) max_choose_route_steps = 250 # size of catchment area of a station (default 2) # older game size was 3 # savegames with another catch area will give strange results station_coverage = 2 # Max number of steps in goods pathfinding # This should be equal or greater than the biggest group # of interconnected stations in your game. # # If you set it too low, some goods might not find a route # if the route is too complex. If you set it too high, the # search will take a lot of CPU power, particularly if searches # often fail because there is no route. # # Depending on your CPU power, you might want to limit the search # depth. # # prissi: On a 512x512 map with more than 150000 people daily, the saturation # value for "no route" was higher, around 8000. Using 300 instead almost doubled # the value of "no route" # max_hops = 2000 # Passengers and goods will change vehicles at most "max_transfer" # times to reach their destination. # # It seems that in good networks, the number of transfers is below # 5. I think 6 is a good compromise that allows complex networks but # cuts off the more extreme cases # # You can set this lower to save a little bit of CPU time, but at some # point this means you'll get less passengers to transport # # This value is less critical than "max_hops" from above. # # T. Kubes: I would say 8 for now, but this definitely should be difficulty # dependent setting. # max_transfers = 9 # way builder internal weights (defaults) # a higher weight make it more unlikely # make the curves negative, and the waybuilder will built strange tracks ... # way_straight=1 way_no_way=3 way_curve=5 way_double_curve=10 way_90_curve=30 way_slope=20 way_tunnel=16 way_leaving_road=50 way_avoid_crossings=8 way_max_bridge_len=15 way_count_maximum=2000 # if defined, the default tool will try to calculate straight ways (like OpenTTD) straight_way_without_control = 0 # stop_halt_as_scheduled=1, trains stop at the scheduled tile (or advance until they fits) # stop_halt_as_scheduled=0 (default) trains will advance always to the end of a platform #stop_halt_as_scheduled=0 ###############################passenger stuff############################## # also pak dependent # town growth multiplier factors (electricity is not used at the moment) # A higher factor mean faster growth # passenger_multiplier = 40 mail_multiplier = 20 goods_multiplier = 20 electricity_multiplier = 0 # town growth is size dependent. There are three different sizes (<1000, 1000-10000, >10000) # the idea is, that area increase by square but growth is linear growthfactor_villages = 400 growthfactor_cities = 200 growthfactor_capitals = 100 # passenger generation (default is 16) Smaller values means less passengers passenger_factor=16 # amount of passengers that travel from/to factories or attractions # the remaining percentage is intercity and intracity travel factory_worker_percentage = 33 tourist_percentage = 16 # how much local destinations are preferred over time # 1 means almost local passengers/mail only, 10000 spreads them over the map # You can have up to ten entries, first is the year, next is the factor. # The increase between the year will increase first slow then fast towards # the final years locality_factor[0]=1835,1 locality_factor[1]=2050,2500 # A factory will be added as worker's destination to a town, if it has either # is within factory_worker_radius # or has less than factory_worker_minimum_towns next cities added # and has not more than factory_worker_maximum_towns added # The default settings connect to maximum four cities with 77 tiles radius; or at least to the closest one # Setting factory_worker_maximum_towns to zero will not connect factories to towns factory_worker_radius = 77 factory_worker_minimum_towns = 1 factory_worker_maximum_towns = 4 # if enabled (default = 0 off) stops may have different capacities for passengers, mail, and freight separate_halt_capacities = 0 # three modes (default = 0) # 1: the payment is only relative to the distance to next interchange, 2 to the trips destination (default 0 is distance since last stop) pay_for_total_distance = 0 # things to overcrowded destinations won't load if active (default off) avoid_overcrowding = 0 # do not create goods/passenger/mail when the only route is over an overcrowded stop no_routing_over_overcrowded = 0 # in beginner mode, all good prices are multiplied by a factor (default 1500=1.5) beginner_price_factor = 1500 # Construction speed of new AIs, the higher the faster (default 8000) #ai_construction_speed = 8000 ##################################factory stuff################################# # when a city reaches 2^n times of this number # then a factory is extended, or a new factory chain is spawned #industry_increase_every = 2000 # smallest distance between two adjacent factories min_factory_spacing = 6 # max distance for connected factories # if percentage>0, it will be in percent of the largest map dimension # percentage also overrides the absolute value max_factory_spacing_percentage = 25 #max_factory_spacing = 40 # allow all possible supplier to connect to your factories? # best to leave it in default position. (only on for simuTTD) crossconnect_factories = 0 # how big is the chance for crossconnections in percent # (100% will give nearly the same results as crossconnect_factories=1) crossconnect_factories_percentage = 33 # how much is the total electric power available (in relation to total production) in parts per thousand electric_promille = 330 # true if transformers are allowed underground (default) allow_underground_transformers = 1 # with this switch on overcrowded factories will not receive goods any more # There are three settings 0=off, 1=old_style (default), 2=new_style just_in_time = 1 # how much amount in transport is sent before further distribution stops # This is only enabled when "just_in_time=1" is enabled # The limit is given in percent of factory storage (0=off) # It should be not too low to allow big convoys fully loaded at more than one # factory (for medium sized maps 500 seems a reasonable number) maximum_intransit_percentage = 0 # use beginner mode for new maps (can be switched anyway on the new map dialog) first_beginner = 0 # number of periods for averaging the amount of arrived pax/mail at factories for boost calculation # one period represents a fixed interval of 2^18 ms in-game time # value can range from 1 to 16, inclusive; 1 means no averaging; default is 4 factory_arrival_periods = 4 # whether factory's pax/mail demands are enforced or not; default is on factory_enforce_demand = 1 ##################################display stuff################################# # You can use a system font. BUT you must specify the whole path to it! # This can be only set in the user defined simuconf.tab #fontname=C:\Windows\fonts\arial.ttf # if run on a mobile device, setting hide_keyboard=1 will only show the keyboard # when there is text to input in a dialog. Other than that textinput will not # be possible (no keyboard short cuts). # Currently only supported with SDL2 hide_keyboard = 0 # player color can be fixed for several players or set totally random # in the latter case each player will get unique but random coloring # (default 0) random_player_colors = 0 # when set here, the player will always get these player colors, even # if random colors were enabled. # color values range from 0 to 27 for first and second player color # and there are 0...15 player in the game player_color[1] = 1,4 # Should either account (default) or net wealth be shown below the screen? player_finance_display_account = 1 # remove companies without convoys after x month (0=off, 6=default) remove_dummy_player_months = 6 # remove password of abandoned companies (without any building activity) after x month (0=off default) unprotect_abandoned_player_months = 0 # (=1) drive on the left side of the road drive_left = 0 # (=1) signals on the left side signals_on_left = 0 # Do you want to have random pedestrians in town? Look nice but needs some # CPU time to process them. (1=on, 0=off) # Impact on frame time: ~10% (16 cities on a new standard map) random_pedestrians = 1 # Do you want to have random pedestrians after pax are reaching this # destination? May generate quite a lot. (1=on, 0=off) stop_pedestrians = 1 # there are some other grounds (like rocky, lakes etc. ) # which could be added to the map (default 10) # show random objects to break uniformity (every n suited tiles) random_grounds_probability = 10 # show random moving animals (n = every n suited tiles, default 1000) random_wildlife_probability = 1000 # animate the water each interval (if images available) # costs some time for the additional redraw (~1-3%) water_animation_ms = 250 # Show info windows for private cars and pedestrians? # (1=on, 0=off) pedes_and_car_info = 0 # How much citycars will be generated citycar_level = 5 # After how many month a citycar breaks (and will be forever gone) ... # default is ten years (at bits_per_month setting of 20) default_citycar_life = 36 # Show info on trees? # (1=on, 0=off) tree_info = 1 # Show info also on bare ground? # (1=on, 0=off) ground_info = 0 # Show passenger level of townhalls? # (1=on, 0=off) townhall_info = 0 # do not show the button to add an inverted schedule for rail based convoys # (1=hide, 0=show anyway) hide_rail_return_ticket = 1 # always open only a single info window for the ground, # even if there are more objects on this tile #only_single_info = 1 # show a tooltip on convoys at several conditions # 0 no messages # 1 (default) only no_route and stuck # loading and waiting at signals too #show_vehicle_states = 1 # If a convoi which you are following enters a tunnel, what to do # 0=do not change view, 1=switch to underground mode and back, 2=switch to sliced underground mode #follow_convoi_underground = 2 # show (default) tiles with a halt when editing a schedule #visualize_schedule = 1 # Should stations get numbered names? (1=yes, 0=no) #numbered_stations = 0 # Show name signs and statistic? # 0 = don't show anything # 1 = station names # 2 = statistics # 3 = names and statistics # The visual style is added to this number: # 0 = black name in color box # 4 = name in player color with outline # 8 = box left of name in yellow outline show_names = 3 # Draw the earth slope to mark the border (default 1 on) draw_earth_border = 1 # Tile the outside with the ground.ouside.pak (default 0 off) draw_outside_tile = 0 # Color of background outside the map, default: COL_GREY2 (210) #background_color = 210 # If and where to display a compass # 0=off (default for main screen) # 1=top, 2=vertical centre, 3=bottom, 4=left, 8=horizontal centre, 12=right # default for minimap is 1+12=13 compass_screen_position = 0 compass_map_position = 13 # if set to 1 (default) the numpad will always move the map. # if set to 0, it will always produce numbers numpad_always_moves_map = 1 # drag the minimap with the left mouse too instead dragging the main map position (1=default) #leftdrag_in_minimap=1 # Position of the main menu bar (0=left, 1=top[default], 2=right, 3=bottom) #menubar_position = 0 # 1=selecting a tool again will close it (0=will be topped/reinit) #reselect_closes_tool = 1 # 1=upon opening, all toolbars will be closed, toolbar will be top centered #single_toolbar = 0 # 1=upon opening, toolbars will be stacked below each other #stack_toolbars = 1 # display scaling factor in percent or -1 for auto # dpi_scaling = 100 ###################################money stuff################################## # # These values are usually set in the pak files # You can adjust all the cost in the game, that are not inside some pak file # # show graphs old style (right to left, default) or rather left to right left_to_right_graphs = 0 # Starting money of the player. Given in Credit cents (1/100 Cr) #starting_money = 20000000 # New system of year dependent starting money. Up to ten triplets are # possible. The entries are of the form: # startingmoney[i]=year,money(in 1/100 credits),interpolate (1) or step(0) # starting_money[0]=1930,20000000,1 # starting_money[1]=2030,35000000,1 # allow buying obsolete vehicles (=1) in depot allow_buying_obsolete_vehicles = 1 # disable companies from making ways public with the appropiate tool # even when disabled companies can still make stops public # does not affect public service provider player disable_make_way_public = 0 # vehicle can loose a part of their value, when the are once used # the loss is given in 1/1000th, i.e 300 mean the value will be 70% used_vehicle_reduction = 0 # convois can have a maximum number of trailing vehicles (up to 250) # These are the dafult values. Pakset with airplanes with dual cargo holds # may need to increase the air counter! max_rail_convoi_length = 24 max_road_convoi_length = 4 max_ship_convoi_length = 4 max_air_convoi_length = 1 # lowest possible income with speedbonus (1000=1) default 125 (=1/8th) bonus_basefactor = 125 # if a convoy runs on a way that belongs to another player, toll may # be charged. The number given is the percentage of the running cost # of the convoy or the way cost (include electrification if needed). # (default 0) toll_runningcost_percentage = 0 toll_waycost_percentage = 0 # Maintenance costs of buildings #maintenance_building = 2000 # first stops: the actual cost is (cost*level*width*height) #cost_multiply_dock=500 #cost_multiply_station=600 #cost_multiply_roadstop=400 #cost_multiply_airterminal=3000 #cost_multiply_post=300 #cost_multiply_headquarter=1000 # cost for depots #cost_depot_air=5000 #cost_depot_rail=1000 #cost_depot_road=1300 #cost_depot_ship=2500 # maximum distance for merging non-adjacent stations allow_merge_distant_halt=2 # the cost to merge stations: the actual cost is (cost*2^distance) #cost_multiply_merge_halt=700 # other way related stuff #cost_signal=500 #cost_tunnel=10000 #cost_third_rail=80 # other construction/destruction stuff #cost_buy_land=100 #cost_alter_land=1000 #cost_set_slope=2500 #cost_alter_climate=5000 #cost_found_city=5000000 #cost_multiply_found_industry=20000 #cost_remove_tree=100 #cost_multiply_remove_haus=1000 #cost_multiply_remove_field=5000 #cost_kw_per_credit=512 #cost_transformer=2500 #cost_maintain_transformer=20 # how many months of maintainance the make public tool costs #cost_make_public_months=60 ##################################pak set stuff################################# # # how long is a diagonal (512: factor 2=1024/512, old way, 724: sqrt(2)=1024/724 # THIS WILL BE ONLY USED, WHEN THIS FILE IS IN THE pakxyz/config! #diagonal_multiplier = 724 # new height for old slopes when old single height savegame loaded with double height pak (default 1=single height, 2=double height) # THIS WILL BE ONLY USED, WHEN THIS FILE IS IN THE pakxyz/config! #height_conversion_factor = 1 # how height is a tile in z-direction (default 16, TTD 8) # THIS WILL BE ONLY USED, WHEN THIS FILE IS IN THE pakxyz/config! #tile_height = 16 # minimum/maximum allowed height level for the map (too high might cause display errors) # Also those number must no be the same #world_minimum_height = -12 #world_maximum_height = 32 # minimum distance between two townhalls #minimum_city_distance = 16 # Minimum distance of a city attraction to other special buildings #special_building_distance = 3 # Minimum clearance under bridges is 1 or 2 levels? (default = conversion factor) #way_height_clearance = 1 # Max. length of initial intercity road connections # If you want to speed up map creation, lower this value. # If you want more initial intercity roads, raise this value. # If the value is too large then very long bridges might be created. # 200 seems to be a good compromise between speed and road number # note: this will slow down map creation dramatically! # #intercity_road_length = 200 # Type of intercity roads - must be available as PAK file. # Intercityroad with timeline: intercity_road[number]=name,intro-year,retire-year # .. number = 0..9 - up to ten different intercity_roads possible # .. name = name of an existing pak file #intercity_road[0] = asphalt_road,0,1990 # default: city_road # (old, 102.2.2 and before) intercity_road_type = asphalt_road # Type of city roads - must be available as PAK file. # Cityroad with timeline: city_road[number]=name,intro-year,retire-year # .. number = 0..9 - up to ten different city_roads possible # .. name = name of an existing pak file #city_road[0] = dirt_road,1920,1940 # default: asphalt_road # (old, 102.2.2 and before) city_road_type = city_road # city road speed limit over time # A comma seperated list of year of change and new limit # without a timeline, the value will be 50 #cityroad_speeds=1,25,1910,30,1930,40,1950,50 # now river stuff # first river type (should be defined in pak dependent file) # The highest number is the smallest. A river with max_speed==0 is not navigable by ships. #river_type[0] = river #river_type[1] = small_river #river_type[2] = just_the source # river number (16 gives a few nicely branched rivers) #river_number = 16 # min length #river_min_length = 16 # max length #river_max_length = 320 ################################# forest stuff ################################# # please be careful in changing them, I spent lot of time finding optimals. # those values have impact on no. of spawned trees -> memory consumption # # Number of trees on square 2 - minimal usable, 3 good, 4 very nice looking max_no_of_trees_on_square = 3 # All following tree options are only active during map generation/extension. # Base forest size - minimal size of forest - map independent forest_base_size = 36 # Map size divisor - smaller it is the larger are individual forests forest_map_size_divisor = 38 # Forest count divisor - smaller it is, the more forest are generated forest_count_divisor = 16 # Determines how dense are spare trees going to be planted (works inversely) forest_inverse_spare_tree_density = 400 # climate with trees entirely (1: water, 2:desert, 4:tropic, 8:mediterranean, 16:temperate, 32:tundra, 64:rocky, 128:arctic) # zero (default) means no climate with at least one tree per tile tree_climates = 4 # climates with no trees at all (desert and arctic at the moment) no_tree_climates = 130 # 0: no trees will be created at all (save about 30% memory and # the season change will be much smoother on small machines) # 1: normal elliptic forest tree distribution # 2: if humidity climate generation is enabled, distribute trees based on humidity (else use 1) #tree_distribution = 2 ################################# landscape stuff ################################# # default groundwater (sea level) on new maps # groundwater = -2 # max_mountain_height = 160 # map_roughness = 4 # winter_snowline = 7 # climate height = min, max # desert_height = -3,0 # tropic_height = -3,0 # mediterran_height = -2,1 # temperate_height = 0,8 # tundra_height = 7,9 # rocky_height = 8,10 # arctic_height = 10,13 # and the ones for climates be humidity #climate_temperature_borders[0]=20 #climate_temperature_borders[1]=15 #climate_temperature_borders[2]=8 #climate_temperature_borders[3]=0 #climate_temperature_borders[4]=-10 #desert_humidity = 65 #tropic_humidity = 75 #moisture_change_land = 1 #moisture_change_water = 1 # which climate generator preferred (only heights and humidity at the moment) #climate_generator = humidity # relative size of a climate patch (best not to touch) #patch_size_percentage = 10 # maximum height level for lake creation (-128 or below ground water: no lakes) #lake_height = 8 ###################################time stuff################################### # Enforce vehicle introduction dates? # 0 = all vehicles available from start of the game # 1 = use introduction dates # 2 = (default) use settings during game creation, new games off # 3 = use settings during game creation, new games on # use_timeline = 3 # Starting year of the game: # Setting it below 1850 is not recommended for pak64 set! # You will have problems with missing vehicles, do not complain if you do so! # Setting it above 2050 will render game bit boring - no new vehicles. # # other recommended value for pak64 is 1956 # starting_year = 1930 # Starting month of the game for people who want to start in summer (default 1=January) starting_month = 1 # Should month be shown in date? # 0=no, 1=yes # 2>=show day in japan format=2, us format=3, german=4 # 5>=show no season but everything else in japan format=5, us format=5, german=6 # This is most useful, if you use longer months than the default length (see below) #show_month = 1 # Global time multiplier (will be save with new games) # 2^bits_per_month = duration of a game month in milliseconds real time # default before 99.x was 18. For example, 21 will make the month 2^3=8 times longer in real time # production and maintenance cost will be adjusted accordingly. # bits_per_month = 20 #################################system stuff################################# # Set this for playing MIDI music with your preferred soundfont. # Need Fluidsynth support. # A recommended lightweight (30 MB) soundfont is PCLite: # http://www.personalcopy.com/sfarkfonts1.htm # https://src.fedoraproject.org/repo/pkgs/PersonalCopy-Lite-soundfont/PCLite.sf2/629732b7552c12a8fae5b046d306273a/ # But there are many more, including greater quality ones. # Set either the full path or the name of the .sf2 soundfont saved into the "music" directory soundfont_filename = PCLite.sf2 # File format for saved games # Uncompressed formats: # - binary Uncompressed binary data # - xml Uncompressed XML data (better error detection/correction but significantly larger files) # Compressed formats: # - zipped, xml_zipped Compressed with gzip # - bzip2, xml_bzip2 Compressed with bzip2 (smaller files but slower save/reload) # - zstd, xml_zstd Compressed with zstd (larger files than bzip2 but faster reload) # Not always available saveformat = bzip2 # Alternate format for faster autosaves autosaveformat = zipped # zip and zstd allow for finetuning their packaging versus speed with an additional # compression level parameter. # Zip form 1(fastest) to 9(smallest) with 6 a good compromise # zstd goes form -10 to 30 or so. Meaningful are mostly single digit values save_level = 6 autosave_level = 1 # autosave every x months (0=off) autosave = 0 # save the current game when quitting and reload it upon reopening #reload_and_save_on_quit = 1 # display (screen/window) width # also see readme.txt, -screensize option #display_width = 704 # display (screen/window) height # also see readme.txt, -screensize option #display_height = 560 # show full screen # 0: disable fullscreen # 1: enable fullscreen # 2: enable borderless fullscreen emulation (frameless window, screen size cannot be altered) #fullscreen = 0 # How many frames per second to use? Display may look pretty until 10 or so # (depends very much on computer, game complexity and graphics driver) frames_per_second = 25 # Same as frames_per_second, but for fast forward (default 10) #fast_forward_frames_per_second = 10 # during zooming out simutrans may get slow due to the very high number # of tiles visible. If the tiles become equal or smaller than the tile size # below, a simpler clipping algorithm will be used, which will give some # clipping errors, but is faster. (default size = 4) # However, during normal gaming this will be determined automatically, so you # usually you do not need to set this manually. simple_drawing_tile_size = 4 # you can force fast redraw for fast forward by this (default off) simple_drawing_fast_forward = 1 # How much faster should the game proceed with fast forward (limited by your computer and size of the map) fast_forward = 50 # How many threads to use (default 4) #threads = 4 ###################################network stuff############################## # # Synchronized networking is always a trade off between fast response and safe # connections. A more relaxed timing will cause delay of commands but is more # likely to compensate for clients running slightly faster than the rest. # # Sets the local addresses Simutrans should listen on and use for making outgoing connections # By default it will use all local IPv4 and IPv6 addresses # This setting has no effect if Simutrans has been compiled with the USE_IP4_ONLY flag set! # The addresses listed will be tried in the order specified # A DNS name may be specified, this will be resolved and Simutrans will attempt to listen # on all of the addresses returned. #listen = ::,0.0.0.0 # How much delay before commands are executed on the clients. # A larger number will catch even clients running slightly ahead but cause delay. # This is set by the server side. #server_frames_ahead = 4 # How much extra delay in command execution on the client side, on top of server_frames_ahead. # A larger number can compensate for larger fluctuations in communication latency to smooth out play. # This is set by the client side. #additional_client_frames_behind = 4 # In network mode, there will be a fixed number of screen updates before a step. # Reasonable values should result in 2-5 steps per second. #server_frames_per_step = 4 # The server sends after a fixed number of steps some information to the clients. # Large values here means: reduced server communication (if that is of importance...) # Small values should improve the timing of the clients. #server_frames_between_checks = 24 # Automatically announce server on the central server directory (http://servers.simutrans.org/) # 0 (default) = off, 1 = on #server_announce = 0 # Interval of server announcement (if enabled) # Value is number of seconds between server announcements, default is 900 (15 minutes) # Minimum value is 60 (1 minute), for accurate listing it is recommended not to increase # this value to greater than 3600 (1 hour) # To disable announcements set server_announce to 0 #server_announce_interval = 900 # Fully Qualified Domain Name (FQDN) or IP address of your server (IPv6 or IPv4) #server_dns = 127.0.0.1 # alternative server name (an or IP6) Use only if server_dns is set! #server_altdns = fe80::1 # Port address (usually set via command line) #server_port = 13353 # Name of server in server listing #server_name = My Simutrans Server # Additional information about your server (for display on the list server) #server_comments = Comments about my server # Email address of server maintainer (for display on the list server) #server_email = maintainer@your.server # Pakset download URL (for display on the list server) #server_pakurl = http://your.domain/pakset.zip # Server info URL (for display on the list server) #server_infurl = http://your.domain/server-info.html # Pause server when no clients are connected #pause_server_no_clients = 1 # Server saves savegame when being killed (default=0 off) #server_save_game_on_quit = 0 # Nickname when joining network games #nickname = John Doe # Chat window transparency (0=off, 25, 50 75 are possible) chat_transparency = 75 # Here you can add a message about your server (It will read this file on each joining anew) #server_motd_filename = simutrans-124.5/simutrans/copyright.txt000066400000000000000000000013701520675460500203630ustar00rootroot00000000000000Simutrans is copyright (C) 1997-2004 by Hj. Malthaner. (c) 2005-2012 Simutrans Team >>>>>>>>>>> NO WARRANTIES, EXPRESS OR IMPLIED. <<<<<<<<<<< Simutrans may not be sold or modified in any way without written permission by the author. Have lots of fun playing Simutrans! The version is built by Markus Pristovsek. All complains, or any other comments please report at: https://forum.simutrans.com The source code from the program is available under the Artistic License. This is a written permission as demanded for above for every right granted by the Artistic License. Note, however, that individual pak sets may come with their own license. Download the source from https://sourceforge.net/projects/simutrans or find out about our svn in the forum. simutrans-124.5/simutrans/font/000077500000000000000000000000001520675460500165575ustar00rootroot00000000000000simutrans-124.5/simutrans/font/Prop-Latin1.bdf000066400000000000000000000430261520675460500213070ustar00rootroot00000000000000STARTFONT 2.1 FONT -simutrans-prop-medium-r-normal--12-120-77-75-P-80-latin1-0 SIZE 12 77 75 FONTBOUNDINGBOX 8 11 0 -2 STARTPROPERTIES 9 POINT_SIZE 100 PIXEL_SIZE 10 RESOLUTION_X 77 RESOLUTION_Y 75 FONT_ASCENT 9 FONT_DESCENT 2 AVERAGE_WIDTH 80 SPACING "P" ENDPROPERTIES CHARS 174 STARTCHAR char10 ENCODING 10 SWIDTH 389 0 DWIDTH 5 BBX 5 1 0 0 BITMAP 00 ENDCHAR STARTCHAR char29 ENCODING 29 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP ba 44 aa 92 92 aa 44 ba ENDCHAR STARTCHAR char30 ENCODING 30 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 38 44 aa 82 aa 92 44 38 ENDCHAR STARTCHAR char31 ENCODING 31 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 38 44 aa 82 92 aa 44 38 ENDCHAR STARTCHAR char33 ENCODING 33 SWIDTH 312 0 DWIDTH 4 BBX 4 9 0 -1 BITMAP 60 60 60 60 60 60 00 60 60 ENDCHAR STARTCHAR char34 ENCODING 34 SWIDTH 545 0 DWIDTH 7 BBX 7 3 0 5 BITMAP 6c 6c d8 ENDCHAR STARTCHAR char35 ENCODING 35 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 6c 6c fe 6c 6c fe 6c 6c ENDCHAR STARTCHAR char36 ENCODING 36 SWIDTH 545 0 DWIDTH 7 BBX 7 9 0 -1 BITMAP 10 78 d4 d0 d0 d0 d4 78 10 ENDCHAR STARTCHAR char37 ENCODING 37 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 72 b6 ac d8 36 6a da 9c ENDCHAR STARTCHAR char38 ENCODING 38 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 70 d0 d0 e0 74 fc d8 ec ENDCHAR STARTCHAR char39 ENCODING 39 SWIDTH 312 0 DWIDTH 4 BBX 4 3 0 5 BITMAP 60 60 c0 ENDCHAR STARTCHAR char40 ENCODING 40 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 30 60 c0 c0 c0 c0 60 30 ENDCHAR STARTCHAR char41 ENCODING 41 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP c0 60 30 30 30 30 60 c0 ENDCHAR STARTCHAR char42 ENCODING 42 SWIDTH 623 0 DWIDTH 8 BBX 8 5 0 1 BITMAP 24 18 7e 18 24 ENDCHAR STARTCHAR char43 ENCODING 43 SWIDTH 545 0 DWIDTH 7 BBX 7 5 0 1 BITMAP 30 30 fc 30 30 ENDCHAR STARTCHAR char44 ENCODING 44 SWIDTH 312 0 DWIDTH 4 BBX 4 3 0 -1 BITMAP 60 60 c0 ENDCHAR STARTCHAR char45 ENCODING 45 SWIDTH 467 0 DWIDTH 6 BBX 6 1 0 3 BITMAP f8 ENDCHAR STARTCHAR char46 ENCODING 46 SWIDTH 234 0 DWIDTH 3 BBX 3 2 0 0 BITMAP c0 c0 ENDCHAR STARTCHAR char47 ENCODING 47 SWIDTH 545 0 DWIDTH 7 BBX 7 7 0 0 BITMAP 04 0c 18 30 60 c0 80 ENDCHAR STARTCHAR char48 ENCODING 48 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc cc cc cc cc 78 ENDCHAR STARTCHAR char49 ENCODING 49 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 30 70 30 30 30 30 30 fc ENDCHAR STARTCHAR char50 ENCODING 50 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc 0c 18 30 60 c0 fc ENDCHAR STARTCHAR char51 ENCODING 51 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc 0c 38 0c 0c cc 78 ENDCHAR STARTCHAR char52 ENCODING 52 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 60 6c cc cc fc 0c 0c ENDCHAR STARTCHAR char53 ENCODING 53 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 c0 c0 f8 0c 0c cc 78 ENDCHAR STARTCHAR char54 ENCODING 54 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 c0 c0 f8 cc cc cc 78 ENDCHAR STARTCHAR char55 ENCODING 55 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP fc 0c 18 18 18 30 30 30 ENDCHAR STARTCHAR char56 ENCODING 56 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc 78 cc cc cc 78 ENDCHAR STARTCHAR char57 ENCODING 57 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc cc cc 7c 0c 78 ENDCHAR STARTCHAR char58 ENCODING 58 SWIDTH 312 0 DWIDTH 4 BBX 4 6 0 0 BITMAP 60 60 00 00 60 60 ENDCHAR STARTCHAR char59 ENCODING 59 SWIDTH 312 0 DWIDTH 4 BBX 4 7 0 -1 BITMAP 60 60 00 00 60 60 c0 ENDCHAR STARTCHAR char60 ENCODING 60 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 0c 38 e0 e0 38 0c ENDCHAR STARTCHAR char61 ENCODING 61 SWIDTH 467 0 DWIDTH 6 BBX 6 4 0 1 BITMAP f8 00 00 f8 ENDCHAR STARTCHAR char62 ENCODING 62 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP c0 70 1c 1c 70 c0 ENDCHAR STARTCHAR char63 ENCODING 63 SWIDTH 545 0 DWIDTH 7 BBX 7 9 0 -1 BITMAP 78 cc 0c 18 30 30 00 30 30 ENDCHAR STARTCHAR char64 ENCODING 64 SWIDTH 701 0 DWIDTH 9 BBX 8 8 0 0 BITMAP 7e e3 cf db db cf e0 7e ENDCHAR STARTCHAR char65 ENCODING 65 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc fc cc cc cc cc ENDCHAR STARTCHAR char66 ENCODING 66 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 cc cc f8 cc cc cc f8 ENDCHAR STARTCHAR char67 ENCODING 67 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc c0 c0 c0 c0 cc 78 ENDCHAR STARTCHAR char68 ENCODING 68 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 cc cc cc cc cc cc f8 ENDCHAR STARTCHAR char69 ENCODING 69 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP f8 c0 c0 f0 c0 c0 c0 f8 ENDCHAR STARTCHAR char70 ENCODING 70 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP f8 c0 c0 f0 c0 c0 c0 c0 ENDCHAR STARTCHAR char71 ENCODING 71 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc c0 c0 dc cc cc 74 ENDCHAR STARTCHAR char72 ENCODING 72 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc cc fc cc cc cc cc ENDCHAR STARTCHAR char73 ENCODING 73 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP f0 60 60 60 60 60 60 f0 ENDCHAR STARTCHAR char74 ENCODING 74 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 78 30 30 30 30 30 70 e0 ENDCHAR STARTCHAR char75 ENCODING 75 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP c4 cc d8 f0 f0 d8 cc c4 ENDCHAR STARTCHAR char76 ENCODING 76 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP c0 c0 c0 c0 c0 c0 c0 f8 ENDCHAR STARTCHAR char77 ENCODING 77 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 82 c6 ee fe d6 c6 c6 c6 ENDCHAR STARTCHAR char78 ENCODING 78 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc ec ec dc dc cc cc ENDCHAR STARTCHAR char79 ENCODING 79 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc cc cc cc cc 78 ENDCHAR STARTCHAR char80 ENCODING 80 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 cc cc cc cc f8 c0 c0 ENDCHAR STARTCHAR char81 ENCODING 81 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc cc cc d4 d8 6c ENDCHAR STARTCHAR char82 ENCODING 82 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 cc cc cc f8 f0 d8 cc ENDCHAR STARTCHAR char83 ENCODING 83 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc c0 78 0c 0c cc 78 ENDCHAR STARTCHAR char84 ENCODING 84 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP fc 30 30 30 30 30 30 30 ENDCHAR STARTCHAR char85 ENCODING 85 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc cc cc cc cc cc 78 ENDCHAR STARTCHAR char86 ENCODING 86 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc cc 78 78 78 30 30 ENDCHAR STARTCHAR char87 ENCODING 87 SWIDTH 701 0 DWIDTH 9 BBX 8 8 0 0 BITMAP c3 db db 5a 7e 7e 34 34 ENDCHAR STARTCHAR char88 ENCODING 88 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc 78 30 30 78 cc cc ENDCHAR STARTCHAR char89 ENCODING 89 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc cc 78 38 30 70 e0 ENDCHAR STARTCHAR char90 ENCODING 90 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP fc 0c 18 30 60 c0 c0 fc ENDCHAR STARTCHAR char91 ENCODING 91 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP f0 c0 c0 c0 c0 c0 c0 f0 ENDCHAR STARTCHAR char92 ENCODING 92 SWIDTH 545 0 DWIDTH 7 BBX 7 7 0 0 BITMAP 80 c0 60 30 18 0c 04 ENDCHAR STARTCHAR char93 ENCODING 93 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP f0 30 30 30 30 30 30 f0 ENDCHAR STARTCHAR char94 ENCODING 94 SWIDTH 545 0 DWIDTH 7 BBX 7 3 0 5 BITMAP 30 78 cc ENDCHAR STARTCHAR char95 ENCODING 95 SWIDTH 467 0 DWIDTH 6 BBX 6 1 0 -1 BITMAP f8 ENDCHAR STARTCHAR char96 ENCODING 96 SWIDTH 312 0 DWIDTH 4 BBX 4 3 0 5 BITMAP c0 c0 60 ENDCHAR STARTCHAR char97 ENCODING 97 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 0c 7c cc cc 7a ENDCHAR STARTCHAR char98 ENCODING 98 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP c0 c0 d8 ec cc cc ec d8 ENDCHAR STARTCHAR char99 ENCODING 99 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 cc c0 c0 cc 78 ENDCHAR STARTCHAR char100 ENCODING 100 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 0c 0c 6c dc cc cc dc 6c ENDCHAR STARTCHAR char101 ENCODING 101 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 cc fc c0 cc 78 ENDCHAR STARTCHAR char102 ENCODING 102 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 70 c0 f0 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char103 ENCODING 103 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 6c dc cc cc dc 6c 0c 78 ENDCHAR STARTCHAR char104 ENCODING 104 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP c0 c0 d8 ec cc cc cc cc ENDCHAR STARTCHAR char105 ENCODING 105 SWIDTH 234 0 DWIDTH 3 BBX 3 8 0 0 BITMAP c0 00 c0 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char106 ENCODING 106 SWIDTH 389 0 DWIDTH 5 BBX 5 10 0 -2 BITMAP 30 00 30 30 30 30 30 30 30 e0 ENDCHAR STARTCHAR char107 ENCODING 107 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP c0 c0 c8 d8 f0 f0 d8 c8 ENDCHAR STARTCHAR char108 ENCODING 108 SWIDTH 234 0 DWIDTH 3 BBX 3 8 0 0 BITMAP c0 c0 c0 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char109 ENCODING 109 SWIDTH 701 0 DWIDTH 9 BBX 8 6 0 0 BITMAP b6 db db db db db ENDCHAR STARTCHAR char110 ENCODING 110 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP d8 ec cc cc cc cc ENDCHAR STARTCHAR char111 ENCODING 111 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 cc cc cc cc 78 ENDCHAR STARTCHAR char112 ENCODING 112 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP d8 ec cc cc ec d8 c0 c0 ENDCHAR STARTCHAR char113 ENCODING 113 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 6c dc cc cc dc 6c 0c 0c ENDCHAR STARTCHAR char114 ENCODING 114 SWIDTH 312 0 DWIDTH 4 BBX 4 6 0 0 BITMAP b0 e0 c0 c0 c0 c0 ENDCHAR STARTCHAR char115 ENCODING 115 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 c4 78 0c cc 78 ENDCHAR STARTCHAR char116 ENCODING 116 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP c0 c0 f0 c0 c0 c0 c0 70 ENDCHAR STARTCHAR char117 ENCODING 117 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP cc cc cc cc dc 6c ENDCHAR STARTCHAR char118 ENCODING 118 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP cc cc cc 78 78 30 ENDCHAR STARTCHAR char119 ENCODING 119 SWIDTH 701 0 DWIDTH 9 BBX 8 6 0 0 BITMAP 99 db db 7e 7e 34 ENDCHAR STARTCHAR char120 ENCODING 120 SWIDTH 467 0 DWIDTH 6 BBX 6 6 0 0 BITMAP 88 d8 70 70 d8 88 ENDCHAR STARTCHAR char121 ENCODING 121 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP cc cc cc 6c 78 38 30 60 ENDCHAR STARTCHAR char122 ENCODING 122 SWIDTH 467 0 DWIDTH 6 BBX 6 6 0 0 BITMAP f8 18 30 60 c0 f8 ENDCHAR STARTCHAR char123 ENCODING 123 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 30 60 60 c0 60 60 60 30 ENDCHAR STARTCHAR char124 ENCODING 124 SWIDTH 234 0 DWIDTH 3 BBX 3 10 0 -2 BITMAP c0 c0 c0 c0 c0 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char125 ENCODING 125 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP c0 60 60 30 60 60 60 c0 ENDCHAR STARTCHAR char126 ENCODING 126 SWIDTH 545 0 DWIDTH 7 BBX 7 3 0 5 BITMAP 64 fc 98 ENDCHAR STARTCHAR char127 ENCODING 127 SWIDTH 389 0 DWIDTH 5 BBX 5 10 0 -2 BITMAP 88 18 30 60 c0 88 18 30 60 c0 ENDCHAR STARTCHAR char161 ENCODING 161 SWIDTH 312 0 DWIDTH 4 BBX 4 9 0 -1 BITMAP 60 60 00 60 60 60 60 60 60 ENDCHAR STARTCHAR char167 ENCODING 167 SWIDTH 623 0 DWIDTH 8 BBX 8 10 0 -2 BITMAP 1c 36 30 78 66 66 36 0c 6c 38 ENDCHAR STARTCHAR char171 ENCODING 171 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 24 6c d8 d8 6c 24 ENDCHAR STARTCHAR char176 ENCODING 176 SWIDTH 467 0 DWIDTH 6 BBX 6 4 0 2 BITMAP 70 d8 d8 70 ENDCHAR STARTCHAR char177 ENCODING 177 SWIDTH 389 0 DWIDTH 5 BBX 5 5 0 0 BITMAP 20 70 20 00 70 ENDCHAR STARTCHAR char178 ENCODING 178 SWIDTH 467 0 DWIDTH 6 BBX 6 5 0 3 BITMAP 70 d8 30 60 f8 ENDCHAR STARTCHAR char179 ENCODING 179 SWIDTH 467 0 DWIDTH 6 BBX 6 5 0 3 BITMAP 70 98 30 98 70 ENDCHAR STARTCHAR char181 ENCODING 181 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP cc cc cc cc dc f4 c0 c0 ENDCHAR STARTCHAR char183 ENCODING 183 SWIDTH 312 0 DWIDTH 4 BBX 4 3 0 3 BITMAP e0 e0 e0 ENDCHAR STARTCHAR char187 ENCODING 187 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 90 d8 6c 6c d8 90 ENDCHAR STARTCHAR char191 ENCODING 191 SWIDTH 545 0 DWIDTH 7 BBX 7 9 0 -1 BITMAP 30 30 00 30 30 60 c0 cc 78 ENDCHAR STARTCHAR char192 ENCODING 192 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 30 78 cc cc fc cc cc ENDCHAR STARTCHAR char193 ENCODING 193 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc cc fc cc cc ENDCHAR STARTCHAR char194 ENCODING 194 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 30 78 84 78 cc fc cc cc ENDCHAR STARTCHAR char195 ENCODING 195 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc 98 78 cc fc cc cc ENDCHAR STARTCHAR char196 ENCODING 196 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 78 cc cc fc cc cc ENDCHAR STARTCHAR char197 ENCODING 197 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc 78 78 cc fc cc cc ENDCHAR STARTCHAR char198 ENCODING 198 SWIDTH 701 0 DWIDTH 9 BBX 8 8 0 0 BITMAP 3f 6c 6c ff cc cc cc cf ENDCHAR STARTCHAR char199 ENCODING 199 SWIDTH 545 0 DWIDTH 7 BBX 7 10 0 -2 BITMAP 78 cc c0 c0 c0 c0 cc 78 30 60 ENDCHAR STARTCHAR char200 ENCODING 200 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 60 30 f8 c0 f0 c0 c0 f8 ENDCHAR STARTCHAR char201 ENCODING 201 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 18 30 f8 c0 f0 c0 c0 f8 ENDCHAR STARTCHAR char202 ENCODING 202 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 30 78 84 7c 60 78 60 7c ENDCHAR STARTCHAR char203 ENCODING 203 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP d8 00 f8 c0 f0 c0 c0 f8 ENDCHAR STARTCHAR char204 ENCODING 204 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP c0 60 00 f0 60 60 60 f0 ENDCHAR STARTCHAR char205 ENCODING 205 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 30 60 00 f0 60 60 60 f0 ENDCHAR STARTCHAR char206 ENCODING 206 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 20 70 88 f0 60 60 60 f0 ENDCHAR STARTCHAR char207 ENCODING 207 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 90 00 f0 60 60 60 60 f0 ENDCHAR STARTCHAR char208 ENCODING 208 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 7c 66 66 f6 66 66 66 7c ENDCHAR STARTCHAR char209 ENCODING 209 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc 98 ec fc fc dc cc ENDCHAR STARTCHAR char210 ENCODING 210 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 30 78 cc cc cc cc 78 ENDCHAR STARTCHAR char211 ENCODING 211 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc cc cc cc 78 ENDCHAR STARTCHAR char212 ENCODING 212 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 10 38 44 78 cc cc cc 78 ENDCHAR STARTCHAR char213 ENCODING 213 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc b8 cc cc cc cc 78 ENDCHAR STARTCHAR char214 ENCODING 214 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 78 cc cc cc cc cc 78 ENDCHAR STARTCHAR char215 ENCODING 215 SWIDTH 545 0 DWIDTH 7 BBX 7 5 0 0 BITMAP 44 28 10 28 44 ENDCHAR STARTCHAR char216 ENCODING 216 SWIDTH 545 0 DWIDTH 7 BBX 7 9 0 -1 BITMAP 04 78 cc dc cc ec cc 78 80 ENDCHAR STARTCHAR char217 ENCODING 217 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 30 cc cc cc cc cc 78 ENDCHAR STARTCHAR char218 ENCODING 218 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 cc cc cc cc cc 78 ENDCHAR STARTCHAR char219 ENCODING 219 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 30 78 84 cc cc cc cc 78 ENDCHAR STARTCHAR char220 ENCODING 220 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 cc cc cc cc cc 78 ENDCHAR STARTCHAR char221 ENCODING 221 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 cc cc 78 30 30 30 ENDCHAR STARTCHAR char222 ENCODING 222 SWIDTH 545 0 DWIDTH 7 BBX 7 9 0 -1 BITMAP c0 c0 f8 cc cc cc f8 c0 c0 ENDCHAR STARTCHAR char223 ENCODING 223 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 78 cc d8 cc cc d8 c0 c0 ENDCHAR STARTCHAR char224 ENCODING 224 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 30 78 0c 7c cc cc 7a ENDCHAR STARTCHAR char225 ENCODING 225 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 0c 7c cc cc 7a ENDCHAR STARTCHAR char226 ENCODING 226 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 10 38 44 78 0c 7c cc 7a ENDCHAR STARTCHAR char227 ENCODING 227 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc 98 78 0c 7c cc 7a ENDCHAR STARTCHAR char228 ENCODING 228 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 78 0c 7c cc cc 7a ENDCHAR STARTCHAR char229 ENCODING 229 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 38 6c 38 78 0c 7c cc 7a ENDCHAR STARTCHAR char230 ENCODING 230 SWIDTH 701 0 DWIDTH 9 BBX 8 6 0 0 BITMAP 76 1b 7f d8 db 76 ENDCHAR STARTCHAR char231 ENCODING 231 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 78 cc c0 c0 cc 78 30 60 ENDCHAR STARTCHAR char232 ENCODING 232 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 30 78 cc fc c0 cc 78 ENDCHAR STARTCHAR char233 ENCODING 233 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc fc c0 cc 78 ENDCHAR STARTCHAR char234 ENCODING 234 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 10 38 44 78 cc fc c0 78 ENDCHAR STARTCHAR char235 ENCODING 235 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 78 cc fc c0 cc 78 ENDCHAR STARTCHAR char236 ENCODING 236 SWIDTH 312 0 DWIDTH 4 BBX 4 8 0 0 BITMAP c0 60 00 60 60 60 60 60 ENDCHAR STARTCHAR char237 ENCODING 237 SWIDTH 312 0 DWIDTH 4 BBX 4 8 0 0 BITMAP 60 c0 00 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char238 ENCODING 238 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 20 70 88 60 60 60 60 60 ENDCHAR STARTCHAR char239 ENCODING 239 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 90 00 60 60 60 60 60 60 ENDCHAR STARTCHAR char240 ENCODING 240 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP ec 38 38 0c 7c cc cc 78 ENDCHAR STARTCHAR char241 ENCODING 241 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc 98 d8 ec cc cc cc ENDCHAR STARTCHAR char242 ENCODING 242 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 30 78 cc cc cc cc 78 ENDCHAR STARTCHAR char243 ENCODING 243 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc cc cc cc 78 ENDCHAR STARTCHAR char244 ENCODING 244 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 10 38 44 78 cc cc cc 78 ENDCHAR STARTCHAR char245 ENCODING 245 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc 98 78 cc cc cc 78 ENDCHAR STARTCHAR char246 ENCODING 246 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 78 cc cc cc cc 78 ENDCHAR STARTCHAR char247 ENCODING 247 SWIDTH 545 0 DWIDTH 7 BBX 7 5 0 0 BITMAP 10 00 7c 00 10 ENDCHAR STARTCHAR char248 ENCODING 248 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -1 BITMAP 04 78 cc dc ec cc 78 80 ENDCHAR STARTCHAR char249 ENCODING 249 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 30 cc cc cc cc dc 6c ENDCHAR STARTCHAR char250 ENCODING 250 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 cc cc cc cc dc 6c ENDCHAR STARTCHAR char251 ENCODING 251 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 30 78 84 cc cc cc dc 6c ENDCHAR STARTCHAR char252 ENCODING 252 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 cc cc cc cc dc 6c ENDCHAR STARTCHAR char253 ENCODING 253 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 18 30 cc cc 78 30 60 c0 ENDCHAR STARTCHAR char254 ENCODING 254 SWIDTH 467 0 DWIDTH 6 BBX 6 10 0 -2 BITMAP c0 c0 c0 f0 d8 d8 f0 c0 c0 c0 ENDCHAR STARTCHAR char255 ENCODING 255 SWIDTH 545 0 DWIDTH 7 BBX 7 10 0 -2 BITMAP cc 00 cc cc cc cc 7c 0c cc 78 ENDCHAR ENDFONT simutrans-124.5/simutrans/font/Prop-Latin2.bdf000066400000000000000000000455671520675460500213240ustar00rootroot00000000000000STARTFONT 2.1 FONT -simutrans-prop-medium-r-normal--12-120-77-75-P-80-latin2-0 SIZE 12 77 75 FONTBOUNDINGBOX 8 11 0 -2 STARTPROPERTIES 9 POINT_SIZE 100 PIXEL_SIZE 10 RESOLUTION_X 77 RESOLUTION_Y 75 FONT_ASCENT 9 FONT_DESCENT 3 AVERAGE_WIDTH 80 SPACING "P" ENDPROPERTIES CHARS 187 STARTCHAR char10 ENCODING 10 SWIDTH 389 0 DWIDTH 5 BBX 5 1 0 0 BITMAP 00 ENDCHAR STARTCHAR char29 ENCODING 29 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP ba 44 aa 92 92 aa 44 ba ENDCHAR STARTCHAR char30 ENCODING 30 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 38 44 aa 82 aa 92 44 38 ENDCHAR STARTCHAR char31 ENCODING 31 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 38 44 aa 82 92 aa 44 38 ENDCHAR STARTCHAR char33 ENCODING 33 SWIDTH 312 0 DWIDTH 4 BBX 4 9 0 -1 BITMAP 60 60 60 60 60 60 00 60 60 ENDCHAR STARTCHAR char34 ENCODING 34 SWIDTH 545 0 DWIDTH 7 BBX 7 3 0 5 BITMAP 6c 6c d8 ENDCHAR STARTCHAR char35 ENCODING 35 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 6c 6c fe 6c 6c fe 6c 6c ENDCHAR STARTCHAR char36 ENCODING 36 SWIDTH 545 0 DWIDTH 7 BBX 7 9 0 -1 BITMAP 10 78 d4 d0 d0 d0 d4 78 10 ENDCHAR STARTCHAR char37 ENCODING 37 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 72 b6 ac d8 36 6a da 9c ENDCHAR STARTCHAR char38 ENCODING 38 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 70 d0 d0 e0 74 fc d8 ec ENDCHAR STARTCHAR char39 ENCODING 39 SWIDTH 312 0 DWIDTH 4 BBX 4 3 0 5 BITMAP 60 60 c0 ENDCHAR STARTCHAR char40 ENCODING 40 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 30 60 c0 c0 c0 c0 60 30 ENDCHAR STARTCHAR char41 ENCODING 41 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP c0 60 30 30 30 30 60 c0 ENDCHAR STARTCHAR char42 ENCODING 42 SWIDTH 623 0 DWIDTH 8 BBX 8 5 0 1 BITMAP 24 18 7e 18 24 ENDCHAR STARTCHAR char43 ENCODING 43 SWIDTH 545 0 DWIDTH 7 BBX 7 5 0 1 BITMAP 30 30 fc 30 30 ENDCHAR STARTCHAR char44 ENCODING 44 SWIDTH 312 0 DWIDTH 4 BBX 4 3 0 -1 BITMAP 60 60 c0 ENDCHAR STARTCHAR char45 ENCODING 45 SWIDTH 467 0 DWIDTH 6 BBX 6 1 0 3 BITMAP f8 ENDCHAR STARTCHAR char46 ENCODING 46 SWIDTH 234 0 DWIDTH 3 BBX 3 2 0 0 BITMAP c0 c0 ENDCHAR STARTCHAR char47 ENCODING 47 SWIDTH 545 0 DWIDTH 7 BBX 7 7 0 0 BITMAP 04 0c 18 30 60 c0 80 ENDCHAR STARTCHAR char48 ENCODING 48 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc cc cc cc cc 78 ENDCHAR STARTCHAR char49 ENCODING 49 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP e0 60 60 60 60 60 60 f0 ENDCHAR STARTCHAR char50 ENCODING 50 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc 0c 18 30 60 c0 fc ENDCHAR STARTCHAR char51 ENCODING 51 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc 0c 38 0c 0c cc 78 ENDCHAR STARTCHAR char52 ENCODING 52 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 60 6c cc cc fc 0c 0c ENDCHAR STARTCHAR char53 ENCODING 53 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 c0 c0 f8 0c 0c cc 78 ENDCHAR STARTCHAR char54 ENCODING 54 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 c0 c0 f8 cc cc cc 78 ENDCHAR STARTCHAR char55 ENCODING 55 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP fc 0c 18 18 18 30 30 30 ENDCHAR STARTCHAR char56 ENCODING 56 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc 78 cc cc cc 78 ENDCHAR STARTCHAR char57 ENCODING 57 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc cc cc 7c 0c 78 ENDCHAR STARTCHAR char58 ENCODING 58 SWIDTH 312 0 DWIDTH 4 BBX 4 6 0 0 BITMAP 60 60 00 00 60 60 ENDCHAR STARTCHAR char59 ENCODING 59 SWIDTH 312 0 DWIDTH 4 BBX 4 7 0 -1 BITMAP 60 60 00 00 60 60 c0 ENDCHAR STARTCHAR char60 ENCODING 60 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 0c 38 e0 e0 38 0c ENDCHAR STARTCHAR char61 ENCODING 61 SWIDTH 467 0 DWIDTH 6 BBX 6 4 0 1 BITMAP f8 00 00 f8 ENDCHAR STARTCHAR char62 ENCODING 62 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP c0 70 1c 1c 70 c0 ENDCHAR STARTCHAR char63 ENCODING 63 SWIDTH 545 0 DWIDTH 7 BBX 7 9 0 -1 BITMAP 78 cc 0c 18 30 30 00 30 30 ENDCHAR STARTCHAR char64 ENCODING 64 SWIDTH 701 0 DWIDTH 9 BBX 8 8 0 0 BITMAP 7e e3 cf db db cf e0 7e ENDCHAR STARTCHAR char65 ENCODING 65 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc fc cc cc cc cc ENDCHAR STARTCHAR char66 ENCODING 66 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 cc cc f8 cc cc cc f8 ENDCHAR STARTCHAR char67 ENCODING 67 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc c0 c0 c0 c0 cc 78 ENDCHAR STARTCHAR char68 ENCODING 68 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 cc cc cc cc cc cc f8 ENDCHAR STARTCHAR char69 ENCODING 69 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP f8 c0 c0 f0 c0 c0 c0 f8 ENDCHAR STARTCHAR char70 ENCODING 70 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP f8 c0 c0 f0 c0 c0 c0 c0 ENDCHAR STARTCHAR char71 ENCODING 71 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc c0 c0 dc cc cc 74 ENDCHAR STARTCHAR char72 ENCODING 72 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc cc fc cc cc cc cc ENDCHAR STARTCHAR char73 ENCODING 73 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP f0 60 60 60 60 60 60 f0 ENDCHAR STARTCHAR char74 ENCODING 74 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 78 30 30 30 30 30 70 e0 ENDCHAR STARTCHAR char75 ENCODING 75 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP c4 cc d8 f0 f0 d8 cc c4 ENDCHAR STARTCHAR char76 ENCODING 76 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP c0 c0 c0 c0 c0 c0 c0 f8 ENDCHAR STARTCHAR char77 ENCODING 77 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 82 c6 ee fe d6 c6 c6 c6 ENDCHAR STARTCHAR char78 ENCODING 78 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc ec ec dc dc cc cc ENDCHAR STARTCHAR char79 ENCODING 79 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc cc cc cc cc 78 ENDCHAR STARTCHAR char80 ENCODING 80 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 cc cc cc cc f8 c0 c0 ENDCHAR STARTCHAR char81 ENCODING 81 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc cc cc cc d4 d8 6c ENDCHAR STARTCHAR char82 ENCODING 82 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP f8 cc cc cc f8 f0 d8 cc ENDCHAR STARTCHAR char83 ENCODING 83 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 78 cc c0 78 0c 0c cc 78 ENDCHAR STARTCHAR char84 ENCODING 84 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP fc 30 30 30 30 30 30 30 ENDCHAR STARTCHAR char85 ENCODING 85 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc cc cc cc cc cc 78 ENDCHAR STARTCHAR char86 ENCODING 86 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc cc 78 78 78 30 30 ENDCHAR STARTCHAR char87 ENCODING 87 SWIDTH 701 0 DWIDTH 9 BBX 8 8 0 0 BITMAP c3 db db 5a 7e 7e 34 34 ENDCHAR STARTCHAR char88 ENCODING 88 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc 78 30 30 78 cc cc ENDCHAR STARTCHAR char89 ENCODING 89 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc cc 78 38 30 70 e0 ENDCHAR STARTCHAR char90 ENCODING 90 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP fc 0c 18 30 60 c0 c0 fc ENDCHAR STARTCHAR char91 ENCODING 91 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP f0 c0 c0 c0 c0 c0 c0 f0 ENDCHAR STARTCHAR char92 ENCODING 92 SWIDTH 545 0 DWIDTH 7 BBX 7 7 0 0 BITMAP 80 c0 60 30 18 0c 04 ENDCHAR STARTCHAR char93 ENCODING 93 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP f0 30 30 30 30 30 30 f0 ENDCHAR STARTCHAR char94 ENCODING 94 SWIDTH 545 0 DWIDTH 7 BBX 7 3 0 5 BITMAP 30 78 cc ENDCHAR STARTCHAR char95 ENCODING 95 SWIDTH 467 0 DWIDTH 6 BBX 6 1 0 -1 BITMAP f8 ENDCHAR STARTCHAR char96 ENCODING 96 SWIDTH 312 0 DWIDTH 4 BBX 4 3 0 5 BITMAP c0 c0 60 ENDCHAR STARTCHAR char97 ENCODING 97 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 0c 7c cc cc 7a ENDCHAR STARTCHAR char98 ENCODING 98 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP c0 c0 d8 ec cc cc ec d8 ENDCHAR STARTCHAR char99 ENCODING 99 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 cc c0 c0 cc 78 ENDCHAR STARTCHAR char100 ENCODING 100 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 0c 0c 6c dc cc cc dc 6c ENDCHAR STARTCHAR char101 ENCODING 101 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 cc fc c0 cc 78 ENDCHAR STARTCHAR char102 ENCODING 102 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 70 c0 f0 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char103 ENCODING 103 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 6c dc cc cc dc 6c 0c 78 ENDCHAR STARTCHAR char104 ENCODING 104 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP c0 c0 d8 ec cc cc cc cc ENDCHAR STARTCHAR char105 ENCODING 105 SWIDTH 234 0 DWIDTH 3 BBX 3 8 0 0 BITMAP c0 00 c0 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char106 ENCODING 106 SWIDTH 389 0 DWIDTH 5 BBX 5 10 0 -2 BITMAP 30 00 30 30 30 30 30 30 30 e0 ENDCHAR STARTCHAR char107 ENCODING 107 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP c0 c0 c8 d8 f0 f0 d8 c8 ENDCHAR STARTCHAR char108 ENCODING 108 SWIDTH 234 0 DWIDTH 3 BBX 3 8 0 0 BITMAP c0 c0 c0 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char109 ENCODING 109 SWIDTH 701 0 DWIDTH 9 BBX 8 6 0 0 BITMAP b6 db db db db db ENDCHAR STARTCHAR char110 ENCODING 110 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP d8 ec cc cc cc cc ENDCHAR STARTCHAR char111 ENCODING 111 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 cc cc cc cc 78 ENDCHAR STARTCHAR char112 ENCODING 112 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP d8 ec cc cc ec d8 c0 c0 ENDCHAR STARTCHAR char113 ENCODING 113 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 6c dc cc cc dc 6c 0c 0c ENDCHAR STARTCHAR char114 ENCODING 114 SWIDTH 312 0 DWIDTH 4 BBX 4 6 0 0 BITMAP b0 e0 c0 c0 c0 c0 ENDCHAR STARTCHAR char115 ENCODING 115 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP 78 c4 78 0c cc 78 ENDCHAR STARTCHAR char116 ENCODING 116 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP c0 c0 f0 c0 c0 c0 c0 70 ENDCHAR STARTCHAR char117 ENCODING 117 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP cc cc cc cc dc 6c ENDCHAR STARTCHAR char118 ENCODING 118 SWIDTH 545 0 DWIDTH 7 BBX 7 6 0 0 BITMAP cc cc cc 78 78 30 ENDCHAR STARTCHAR char119 ENCODING 119 SWIDTH 701 0 DWIDTH 9 BBX 8 6 0 0 BITMAP 99 db db 7e 7e 34 ENDCHAR STARTCHAR char120 ENCODING 120 SWIDTH 467 0 DWIDTH 6 BBX 6 6 0 0 BITMAP 88 d8 70 70 d8 88 ENDCHAR STARTCHAR char121 ENCODING 121 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP cc cc cc 6c 78 38 30 60 ENDCHAR STARTCHAR char122 ENCODING 122 SWIDTH 467 0 DWIDTH 6 BBX 6 6 0 0 BITMAP f8 18 30 60 c0 f8 ENDCHAR STARTCHAR char123 ENCODING 123 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 30 60 60 c0 60 60 60 30 ENDCHAR STARTCHAR char124 ENCODING 124 SWIDTH 234 0 DWIDTH 3 BBX 3 10 0 -2 BITMAP c0 c0 c0 c0 c0 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char125 ENCODING 125 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP c0 60 60 30 60 60 60 c0 ENDCHAR STARTCHAR char126 ENCODING 126 SWIDTH 545 0 DWIDTH 7 BBX 7 3 0 5 BITMAP 64 fc 98 ENDCHAR STARTCHAR char127 ENCODING 127 SWIDTH 389 0 DWIDTH 5 BBX 5 10 0 -2 BITMAP 88 18 30 60 c0 88 18 30 60 c0 ENDCHAR STARTCHAR char161 ENCODING 161 SWIDTH 545 0 DWIDTH 7 BBX 7 10 0 -2 BITMAP 78 cc cc fc cc cc cc cc 18 0c ENDCHAR STARTCHAR char162 ENCODING 162 SWIDTH 545 0 DWIDTH 7 BBX 7 3 0 5 BITMAP 24 3c 18 ENDCHAR STARTCHAR char163 ENCODING 163 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 60 68 78 70 e0 e0 60 7c ENDCHAR STARTCHAR char164 ENCODING 164 SWIDTH 467 0 DWIDTH 6 BBX 6 6 0 1 BITMAP 48 30 48 48 30 48 ENDCHAR STARTCHAR char165 ENCODING 165 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc d8 c0 c0 c0 c0 f8 ENDCHAR STARTCHAR char166 ENCODING 166 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 c4 78 0c cc 78 ENDCHAR STARTCHAR char169 ENCODING 169 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 78 c4 78 0c cc 78 ENDCHAR STARTCHAR char170 ENCODING 170 SWIDTH 545 0 DWIDTH 7 BBX 7 10 0 -2 BITMAP 78 cc c0 78 0c 0c cc 78 0c 38 ENDCHAR STARTCHAR char171 ENCODING 171 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 fc 30 30 30 30 30 ENDCHAR STARTCHAR char172 ENCODING 172 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 fc 0c 18 70 c0 fc ENDCHAR STARTCHAR char174 ENCODING 174 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 fc 0c 18 70 c0 fc ENDCHAR STARTCHAR char175 ENCODING 175 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 30 00 fc 0c 18 70 c0 fc ENDCHAR STARTCHAR char176 ENCODING 176 SWIDTH 467 0 DWIDTH 6 BBX 6 4 0 2 BITMAP 70 d8 d8 70 ENDCHAR STARTCHAR char177 ENCODING 177 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 -2 BITMAP 78 0c 7c cc cc 7a 18 0c ENDCHAR STARTCHAR char178 ENCODING 178 SWIDTH 545 0 DWIDTH 7 BBX 7 2 0 -2 BITMAP 18 0c ENDCHAR STARTCHAR char179 ENCODING 179 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 60 68 78 70 e0 e0 60 60 ENDCHAR STARTCHAR char180 ENCODING 180 SWIDTH 467 0 DWIDTH 6 BBX 6 2 0 6 BITMAP 18 30 ENDCHAR STARTCHAR char181 ENCODING 181 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc cc d8 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char182 ENCODING 182 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 c4 78 0c cc 78 ENDCHAR STARTCHAR char185 ENCODING 185 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 78 c4 78 0c cc 78 ENDCHAR STARTCHAR char186 ENCODING 186 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 78 c4 78 0c cc 78 0c 38 ENDCHAR STARTCHAR char187 ENCODING 187 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP d2 cc f0 c0 c0 c0 c0 70 ENDCHAR STARTCHAR char188 ENCODING 188 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 18 30 f8 18 30 60 c0 f8 ENDCHAR STARTCHAR char190 ENCODING 190 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 48 30 f8 18 30 60 c0 f8 ENDCHAR STARTCHAR char191 ENCODING 191 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 30 00 f8 18 30 60 c0 f8 ENDCHAR STARTCHAR char192 ENCODING 192 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 f8 cc f8 f0 d8 cc ENDCHAR STARTCHAR char193 ENCODING 193 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc cc fc cc cc ENDCHAR STARTCHAR char194 ENCODING 194 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 30 78 84 78 cc fc cc cc ENDCHAR STARTCHAR char195 ENCODING 195 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc 98 78 cc fc cc cc ENDCHAR STARTCHAR char196 ENCODING 196 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 78 cc cc fc cc cc ENDCHAR STARTCHAR char197 ENCODING 197 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 18 30 c0 c0 c0 c0 c0 f8 ENDCHAR STARTCHAR char198 ENCODING 198 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc c0 c0 cc 78 ENDCHAR STARTCHAR char199 ENCODING 199 SWIDTH 545 0 DWIDTH 7 BBX 7 10 0 -2 BITMAP 78 cc c0 c0 c0 c0 cc 78 30 60 ENDCHAR STARTCHAR char200 ENCODING 200 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 78 cc c0 c0 cc 78 ENDCHAR STARTCHAR char201 ENCODING 201 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 18 30 f8 c0 f0 c0 c0 f8 ENDCHAR STARTCHAR char202 ENCODING 202 SWIDTH 467 0 DWIDTH 6 BBX 6 10 0 -2 BITMAP f8 c0 c0 f0 c0 c0 c0 f8 30 18 ENDCHAR STARTCHAR char203 ENCODING 203 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP d8 00 f8 c0 f0 c0 c0 f8 ENDCHAR STARTCHAR char204 ENCODING 204 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 50 20 f8 c0 f0 c0 c0 f8 ENDCHAR STARTCHAR char205 ENCODING 205 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 30 60 00 f0 60 60 60 f0 ENDCHAR STARTCHAR char206 ENCODING 206 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 26 70 88 f0 60 60 60 f0 ENDCHAR STARTCHAR char207 ENCODING 207 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 68 30 f8 cc cc cc cc f8 ENDCHAR STARTCHAR char208 ENCODING 208 SWIDTH 623 0 DWIDTH 8 BBX 8 8 0 0 BITMAP 7c 66 66 f6 66 66 66 7c ENDCHAR STARTCHAR char209 ENCODING 209 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 cc ec ec dc dc cc ENDCHAR STARTCHAR char210 ENCODING 210 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 cc ec ec dc dc cc ENDCHAR STARTCHAR char211 ENCODING 211 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc cc cc cc 78 ENDCHAR STARTCHAR char212 ENCODING 212 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 10 38 44 78 cc cc cc 78 ENDCHAR STARTCHAR char213 ENCODING 213 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc b8 cc cc cc cc 78 ENDCHAR STARTCHAR char214 ENCODING 214 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 78 cc cc cc cc cc 78 ENDCHAR STARTCHAR char216 ENCODING 216 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 f8 cc f8 f0 d8 cc ENDCHAR STARTCHAR char217 ENCODING 217 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 20 50 ac cc cc cc cc 78 ENDCHAR STARTCHAR char218 ENCODING 218 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 cc cc cc cc cc 78 ENDCHAR STARTCHAR char219 ENCODING 219 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 2c 58 84 cc cc cc cc 78 ENDCHAR STARTCHAR char220 ENCODING 220 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 cc cc cc cc cc 78 ENDCHAR STARTCHAR char221 ENCODING 221 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 cc cc 78 30 30 30 ENDCHAR STARTCHAR char222 ENCODING 222 SWIDTH 545 0 DWIDTH 7 BBX 7 9 0 -1 BITMAP c0 c0 f8 cc cc cc f8 c0 c0 ENDCHAR STARTCHAR char223 ENCODING 223 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 78 cc d8 cc cc d8 c0 c0 ENDCHAR STARTCHAR char224 ENCODING 224 SWIDTH 312 0 DWIDTH 4 BBX 4 8 0 0 BITMAP 30 60 b0 e0 c0 c0 c0 c0 ENDCHAR STARTCHAR char225 ENCODING 225 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 0c 7c cc cc 7a ENDCHAR STARTCHAR char226 ENCODING 226 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 10 38 44 78 0c 7c cc 7a ENDCHAR STARTCHAR char227 ENCODING 227 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc 98 78 0c 7c cc 7a ENDCHAR STARTCHAR char228 ENCODING 228 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 78 0c 7c cc cc 7a ENDCHAR STARTCHAR char229 ENCODING 229 SWIDTH 234 0 DWIDTH 3 BBX 3 8 0 0 BITMAP 60 c0 00 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char230 ENCODING 230 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc c0 c0 cc 78 ENDCHAR STARTCHAR char231 ENCODING 231 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 78 cc c0 c0 cc 78 30 60 ENDCHAR STARTCHAR char232 ENCODING 232 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 78 cc c0 c0 cc 78 ENDCHAR STARTCHAR char233 ENCODING 233 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc fc c0 cc 78 ENDCHAR STARTCHAR char234 ENCODING 234 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 -2 BITMAP 78 cc fc c0 cc 78 30 18 ENDCHAR STARTCHAR char235 ENCODING 235 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 78 cc fc c0 cc 78 ENDCHAR STARTCHAR char236 ENCODING 236 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 78 cc fc c0 cc 78 ENDCHAR STARTCHAR char237 ENCODING 237 SWIDTH 312 0 DWIDTH 4 BBX 4 8 0 0 BITMAP 60 c0 00 c0 c0 c0 c0 c0 ENDCHAR STARTCHAR char238 ENCODING 238 SWIDTH 467 0 DWIDTH 6 BBX 6 8 0 0 BITMAP 20 70 88 60 60 60 60 60 ENDCHAR STARTCHAR char239 ENCODING 239 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 90 6c 0c 6c dc cc dc 6c ENDCHAR STARTCHAR char240 ENCODING 240 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP ec 38 38 0c 7c cc cc 78 ENDCHAR STARTCHAR char241 ENCODING 241 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 d8 ec cc cc cc cc ENDCHAR STARTCHAR char242 ENCODING 242 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 48 30 d8 ec cc cc cc cc ENDCHAR STARTCHAR char243 ENCODING 243 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 78 cc cc cc cc 78 ENDCHAR STARTCHAR char244 ENCODING 244 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 10 38 44 78 cc cc cc 78 ENDCHAR STARTCHAR char245 ENCODING 245 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 64 fc 98 78 cc cc cc 78 ENDCHAR STARTCHAR char246 ENCODING 246 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 78 cc cc cc cc 78 ENDCHAR STARTCHAR char247 ENCODING 247 SWIDTH 389 0 DWIDTH 5 BBX 5 5 0 0 BITMAP 60 00 f0 00 60 ENDCHAR STARTCHAR char248 ENCODING 248 SWIDTH 389 0 DWIDTH 5 BBX 5 8 0 0 BITMAP 90 60 b0 e0 c0 c0 c0 c0 ENDCHAR STARTCHAR char249 ENCODING 249 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 20 50 ac cc cc cc dc 6c ENDCHAR STARTCHAR char250 ENCODING 250 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 18 30 cc cc cc cc dc 6c ENDCHAR STARTCHAR char251 ENCODING 251 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP 2c 58 84 cc cc cc dc 6c ENDCHAR STARTCHAR char252 ENCODING 252 SWIDTH 545 0 DWIDTH 7 BBX 7 8 0 0 BITMAP cc 00 cc cc cc cc dc 6c ENDCHAR STARTCHAR char253 ENCODING 253 SWIDTH 545 0 DWIDTH 7 BBX 7 10 0 -2 BITMAP 18 30 cc cc cc 6c 78 38 30 60 ENDCHAR STARTCHAR char254 ENCODING 254 SWIDTH 467 0 DWIDTH 6 BBX 6 10 0 -2 BITMAP c0 c0 c0 f0 d8 d8 f0 c0 c0 c0 ENDCHAR STARTCHAR char255 ENCODING 255 SWIDTH 545 0 DWIDTH 7 BBX 7 10 0 -2 BITMAP cc 00 cc cc cc cc 7c 0c cc 78 ENDCHAR ENDFONT simutrans-124.5/simutrans/font/cyr.bdf000066400000000000000000013536341520675460500200500ustar00rootroot00000000000000STARTFONT 2.1 FONT -simutrans-prop-medium-r-normal--12-120-77-75-P-80-ISO10646-1 SIZE 12 77 75 FONTBOUNDINGBOX 10 11 0 -2 STARTPROPERTIES 17 POINT_SIZE 100 PIXEL_SIZE 10 RESOLUTION_X 77 RESOLUTION_Y 75 FONT_ASCENT 9 FONT_DESCENT 2 AVERAGE_WIDTH 70 SPACING "P" FOUNDRY "simutrans" FAMILY_NAME "prop" WEIGHT_NAME "medium" SLANT "r" SETWIDTH_NAME "normal" ADD_STYLE_NAME "" CHARSET_REGISTRY "ISO10646" CHARSET_ENCODING "1" _GBDFED_INFO "Edited with gbdfed 1.5." ENDPROPERTIES CHARS 2744 STARTCHAR char0 ENCODING 0 SWIDTH 389 0 DWIDTH 8 0 BBX 6 8 0 0 BITMAP FC 84 84 84 84 84 84 FC ENDCHAR STARTCHAR char29 ENCODING 29 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP BA 44 AA 92 92 AA 44 BA ENDCHAR STARTCHAR char30 ENCODING 30 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 38 44 AA 82 AA 92 44 38 ENDCHAR STARTCHAR char31 ENCODING 31 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 38 44 AA 82 92 AA 44 38 ENDCHAR STARTCHAR char32 ENCODING 32 SWIDTH 389 0 DWIDTH 5 0 BBX 5 0 0 0 BITMAP ENDCHAR STARTCHAR char33 ENCODING 33 SWIDTH 311 0 DWIDTH 4 0 BBX 4 9 0 -1 BITMAP 60 60 60 60 60 60 00 60 60 ENDCHAR STARTCHAR char34 ENCODING 34 SWIDTH 545 0 DWIDTH 7 0 BBX 7 3 0 5 BITMAP 6C 6C D8 ENDCHAR STARTCHAR char35 ENCODING 35 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 6C 6C FE 6C 6C FE 6C 6C ENDCHAR STARTCHAR char36 ENCODING 36 SWIDTH 545 0 DWIDTH 7 0 BBX 7 9 0 -1 BITMAP 10 78 D4 D0 D0 D0 D4 78 10 ENDCHAR STARTCHAR char37 ENCODING 37 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 72 B6 AC D8 36 6A DA 9C ENDCHAR STARTCHAR char38 ENCODING 38 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 70 D0 D0 E0 74 FC D8 EC ENDCHAR STARTCHAR char39 ENCODING 39 SWIDTH 311 0 DWIDTH 4 0 BBX 4 3 0 5 BITMAP 60 60 C0 ENDCHAR STARTCHAR char40 ENCODING 40 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP 30 60 C0 C0 C0 C0 60 30 ENDCHAR STARTCHAR char41 ENCODING 41 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP C0 60 30 30 30 30 60 C0 ENDCHAR STARTCHAR char42 ENCODING 42 SWIDTH 623 0 DWIDTH 8 0 BBX 8 5 0 1 BITMAP 24 18 7E 18 24 ENDCHAR STARTCHAR char43 ENCODING 43 SWIDTH 545 0 DWIDTH 7 0 BBX 7 5 0 1 BITMAP 30 30 FC 30 30 ENDCHAR STARTCHAR char44 ENCODING 44 SWIDTH 311 0 DWIDTH 4 0 BBX 4 3 0 -1 BITMAP 60 60 C0 ENDCHAR STARTCHAR char45 ENCODING 45 SWIDTH 467 0 DWIDTH 6 0 BBX 6 1 0 3 BITMAP F8 ENDCHAR STARTCHAR char46 ENCODING 46 SWIDTH 233 0 DWIDTH 3 0 BBX 3 2 0 0 BITMAP C0 C0 ENDCHAR STARTCHAR char47 ENCODING 47 SWIDTH 545 0 DWIDTH 7 0 BBX 7 7 0 0 BITMAP 04 0C 18 30 60 C0 80 ENDCHAR STARTCHAR char48 ENCODING 48 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC CC CC CC CC CC 78 ENDCHAR STARTCHAR char49 ENCODING 49 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 30 70 30 30 30 30 30 FC ENDCHAR STARTCHAR char50 ENCODING 50 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC 0C 18 30 60 C0 FC ENDCHAR STARTCHAR char51 ENCODING 51 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC 0C 38 0C 0C CC 78 ENDCHAR STARTCHAR char52 ENCODING 52 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 60 60 6C CC CC FC 0C 0C ENDCHAR STARTCHAR char53 ENCODING 53 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP F8 C0 C0 F8 0C 0C CC 78 ENDCHAR STARTCHAR char54 ENCODING 54 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 C0 C0 F8 CC CC CC 78 ENDCHAR STARTCHAR char55 ENCODING 55 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP FC 0C 18 18 18 30 30 30 ENDCHAR STARTCHAR char56 ENCODING 56 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC CC 78 CC CC CC 78 ENDCHAR STARTCHAR char57 ENCODING 57 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC CC CC CC 7C 0C 78 ENDCHAR STARTCHAR char58 ENCODING 58 SWIDTH 311 0 DWIDTH 4 0 BBX 4 6 0 0 BITMAP 60 60 00 00 60 60 ENDCHAR STARTCHAR char59 ENCODING 59 SWIDTH 311 0 DWIDTH 4 0 BBX 4 7 0 -1 BITMAP 60 60 00 00 60 60 C0 ENDCHAR STARTCHAR char60 ENCODING 60 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP 0C 38 E0 E0 38 0C ENDCHAR STARTCHAR char61 ENCODING 61 SWIDTH 467 0 DWIDTH 6 0 BBX 6 4 0 1 BITMAP F8 00 00 F8 ENDCHAR STARTCHAR char62 ENCODING 62 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP C0 70 1C 1C 70 C0 ENDCHAR STARTCHAR char63 ENCODING 63 SWIDTH 545 0 DWIDTH 7 0 BBX 7 9 0 -1 BITMAP 78 CC 0C 18 30 30 00 30 30 ENDCHAR STARTCHAR char64 ENCODING 64 SWIDTH 701 0 DWIDTH 9 0 BBX 8 8 0 0 BITMAP 7E E3 CF DB DB CF E0 7E ENDCHAR STARTCHAR char65 ENCODING 65 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC CC FC CC CC CC CC ENDCHAR STARTCHAR char66 ENCODING 66 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP F8 CC CC F8 CC CC CC F8 ENDCHAR STARTCHAR char67 ENCODING 67 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC C0 C0 C0 C0 CC 78 ENDCHAR STARTCHAR char68 ENCODING 68 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP F8 CC CC CC CC CC CC F8 ENDCHAR STARTCHAR char69 ENCODING 69 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP F8 C0 C0 F0 C0 C0 C0 F8 ENDCHAR STARTCHAR char70 ENCODING 70 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP F8 C0 C0 F0 C0 C0 C0 C0 ENDCHAR STARTCHAR char71 ENCODING 71 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC C0 C0 DC CC CC 74 ENDCHAR STARTCHAR char72 ENCODING 72 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC CC CC FC CC CC CC CC ENDCHAR STARTCHAR char73 ENCODING 73 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP F0 60 60 60 60 60 60 F0 ENDCHAR STARTCHAR char74 ENCODING 74 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 78 30 30 30 30 30 70 E0 ENDCHAR STARTCHAR char75 ENCODING 75 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP C4 CC D8 F0 F0 D8 CC C4 ENDCHAR STARTCHAR char76 ENCODING 76 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP C0 C0 C0 C0 C0 C0 C0 F8 ENDCHAR STARTCHAR char77 ENCODING 77 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 82 C6 EE FE D6 C6 C6 C6 ENDCHAR STARTCHAR char78 ENCODING 78 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC CC EC EC DC DC CC CC ENDCHAR STARTCHAR char79 ENCODING 79 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC CC CC CC CC CC 78 ENDCHAR STARTCHAR char80 ENCODING 80 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP F8 CC CC CC CC F8 C0 C0 ENDCHAR STARTCHAR char81 ENCODING 81 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC CC CC CC D4 D8 6C ENDCHAR STARTCHAR char82 ENCODING 82 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP F8 CC CC CC F8 F0 D8 CC ENDCHAR STARTCHAR char83 ENCODING 83 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC C0 78 0C 0C CC 78 ENDCHAR STARTCHAR char84 ENCODING 84 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP FC 30 30 30 30 30 30 30 ENDCHAR STARTCHAR char85 ENCODING 85 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC CC CC CC CC CC CC 78 ENDCHAR STARTCHAR char86 ENCODING 86 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC CC CC 78 78 78 30 30 ENDCHAR STARTCHAR char87 ENCODING 87 SWIDTH 701 0 DWIDTH 9 0 BBX 8 8 0 0 BITMAP C3 DB DB 5A 7E 7E 34 34 ENDCHAR STARTCHAR char88 ENCODING 88 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC CC 78 30 30 78 CC CC ENDCHAR STARTCHAR char89 ENCODING 89 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC CC CC 78 38 30 70 E0 ENDCHAR STARTCHAR char90 ENCODING 90 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP FC 0C 18 30 60 C0 C0 FC ENDCHAR STARTCHAR char91 ENCODING 91 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP F0 C0 C0 C0 C0 C0 C0 F0 ENDCHAR STARTCHAR char92 ENCODING 92 SWIDTH 545 0 DWIDTH 7 0 BBX 7 7 0 0 BITMAP 80 C0 60 30 18 0C 04 ENDCHAR STARTCHAR char93 ENCODING 93 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP F0 30 30 30 30 30 30 F0 ENDCHAR STARTCHAR char94 ENCODING 94 SWIDTH 545 0 DWIDTH 7 0 BBX 7 3 0 5 BITMAP 30 78 CC ENDCHAR STARTCHAR char95 ENCODING 95 SWIDTH 467 0 DWIDTH 6 0 BBX 6 1 0 -1 BITMAP F8 ENDCHAR STARTCHAR char96 ENCODING 96 SWIDTH 311 0 DWIDTH 4 0 BBX 4 3 0 5 BITMAP C0 C0 60 ENDCHAR STARTCHAR char97 ENCODING 97 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP 78 0C 7C CC CC 7A ENDCHAR STARTCHAR char98 ENCODING 98 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP C0 C0 D8 EC CC CC EC D8 ENDCHAR STARTCHAR char99 ENCODING 99 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP 78 CC C0 C0 CC 78 ENDCHAR STARTCHAR char100 ENCODING 100 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 0C 0C 6C DC CC CC DC 6C ENDCHAR STARTCHAR char101 ENCODING 101 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP 78 CC FC C0 CC 78 ENDCHAR STARTCHAR char102 ENCODING 102 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP 70 C0 F0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR char103 ENCODING 103 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP 6C DC CC CC DC 6C 0C 78 ENDCHAR STARTCHAR char104 ENCODING 104 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP C0 C0 D8 EC CC CC CC CC ENDCHAR STARTCHAR char105 ENCODING 105 SWIDTH 233 0 DWIDTH 3 0 BBX 3 8 0 0 BITMAP C0 00 C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR char106 ENCODING 106 SWIDTH 389 0 DWIDTH 5 0 BBX 5 10 0 -2 BITMAP 30 00 30 30 30 30 30 30 30 E0 ENDCHAR STARTCHAR char107 ENCODING 107 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP C0 C0 C8 D8 F0 F0 D8 C8 ENDCHAR STARTCHAR char108 ENCODING 108 SWIDTH 233 0 DWIDTH 3 0 BBX 3 8 0 0 BITMAP C0 C0 C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR char109 ENCODING 109 SWIDTH 701 0 DWIDTH 9 0 BBX 8 6 0 0 BITMAP B6 DB DB DB DB DB ENDCHAR STARTCHAR char110 ENCODING 110 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP D8 EC CC CC CC CC ENDCHAR STARTCHAR char111 ENCODING 111 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP 78 CC CC CC CC 78 ENDCHAR STARTCHAR char112 ENCODING 112 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP D8 EC CC CC EC D8 C0 C0 ENDCHAR STARTCHAR char113 ENCODING 113 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP 6C DC CC CC DC 6C 0C 0C ENDCHAR STARTCHAR char114 ENCODING 114 SWIDTH 311 0 DWIDTH 4 0 BBX 4 6 0 0 BITMAP B0 E0 C0 C0 C0 C0 ENDCHAR STARTCHAR char115 ENCODING 115 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP 78 C4 78 0C CC 78 ENDCHAR STARTCHAR char116 ENCODING 116 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP C0 C0 F0 C0 C0 C0 C0 70 ENDCHAR STARTCHAR char117 ENCODING 117 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP CC CC CC CC DC 6C ENDCHAR STARTCHAR char118 ENCODING 118 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP CC CC CC 78 78 30 ENDCHAR STARTCHAR char119 ENCODING 119 SWIDTH 701 0 DWIDTH 9 0 BBX 8 6 0 0 BITMAP 99 DB DB 7E 7E 34 ENDCHAR STARTCHAR char120 ENCODING 120 SWIDTH 467 0 DWIDTH 6 0 BBX 6 6 0 0 BITMAP 88 D8 70 70 D8 88 ENDCHAR STARTCHAR char121 ENCODING 121 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP CC CC CC 6C 78 38 30 60 ENDCHAR STARTCHAR char122 ENCODING 122 SWIDTH 467 0 DWIDTH 6 0 BBX 6 6 0 0 BITMAP F8 18 30 60 C0 F8 ENDCHAR STARTCHAR char123 ENCODING 123 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP 30 60 60 C0 60 60 60 30 ENDCHAR STARTCHAR char124 ENCODING 124 SWIDTH 233 0 DWIDTH 3 0 BBX 3 10 0 -2 BITMAP C0 C0 C0 C0 C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR char125 ENCODING 125 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP C0 60 60 30 60 60 60 C0 ENDCHAR STARTCHAR char126 ENCODING 126 SWIDTH 545 0 DWIDTH 7 0 BBX 7 3 0 5 BITMAP 64 FC 98 ENDCHAR STARTCHAR char127 ENCODING 127 SWIDTH 389 0 DWIDTH 5 0 BBX 5 10 0 -2 BITMAP 88 18 30 60 C0 88 18 30 60 C0 ENDCHAR STARTCHAR char160 ENCODING 160 SWIDTH 389 0 DWIDTH 5 0 BBX 5 0 0 0 BITMAP ENDCHAR STARTCHAR char161 ENCODING 161 SWIDTH 311 0 DWIDTH 4 0 BBX 4 9 0 -1 BITMAP 60 60 00 60 60 60 60 60 60 ENDCHAR STARTCHAR char167 ENCODING 167 SWIDTH 623 0 DWIDTH 8 0 BBX 8 10 0 -2 BITMAP 1C 36 30 78 66 66 36 0C 6C 38 ENDCHAR STARTCHAR char171 ENCODING 171 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP 24 6C D8 D8 6C 24 ENDCHAR STARTCHAR char176 ENCODING 176 SWIDTH 467 0 DWIDTH 6 0 BBX 6 4 0 2 BITMAP 70 D8 D8 70 ENDCHAR STARTCHAR char177 ENCODING 177 SWIDTH 389 0 DWIDTH 5 0 BBX 5 5 0 0 BITMAP 20 70 20 00 70 ENDCHAR STARTCHAR char178 ENCODING 178 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 3 BITMAP 70 D8 30 60 F8 ENDCHAR STARTCHAR char179 ENCODING 179 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 3 BITMAP 70 98 30 98 70 ENDCHAR STARTCHAR char181 ENCODING 181 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP CC CC CC CC DC F4 C0 C0 ENDCHAR STARTCHAR char183 ENCODING 183 SWIDTH 311 0 DWIDTH 4 0 BBX 4 3 0 3 BITMAP E0 E0 E0 ENDCHAR STARTCHAR char187 ENCODING 187 SWIDTH 545 0 DWIDTH 7 0 BBX 7 6 0 0 BITMAP 90 D8 6C 6C D8 90 ENDCHAR STARTCHAR char191 ENCODING 191 SWIDTH 545 0 DWIDTH 7 0 BBX 7 9 0 -1 BITMAP 30 30 00 30 30 60 C0 CC 78 ENDCHAR STARTCHAR char192 ENCODING 192 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 60 30 78 CC CC FC CC CC ENDCHAR STARTCHAR char193 ENCODING 193 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 18 30 78 CC CC FC CC CC ENDCHAR STARTCHAR char194 ENCODING 194 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 30 78 84 78 CC FC CC CC ENDCHAR STARTCHAR char195 ENCODING 195 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 64 FC 98 78 CC FC CC CC ENDCHAR STARTCHAR char196 ENCODING 196 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC 00 78 CC CC FC CC CC ENDCHAR STARTCHAR char197 ENCODING 197 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 78 CC 78 78 CC FC CC CC ENDCHAR STARTCHAR char198 ENCODING 198 SWIDTH 701 0 DWIDTH 9 0 BBX 8 8 0 0 BITMAP 3F 6C 6C FF CC CC CC CF ENDCHAR STARTCHAR char199 ENCODING 199 SWIDTH 545 0 DWIDTH 7 0 BBX 7 10 0 -2 BITMAP 78 CC C0 C0 C0 C0 CC 78 30 60 ENDCHAR STARTCHAR char200 ENCODING 200 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 60 30 F8 C0 F0 C0 C0 F8 ENDCHAR STARTCHAR char201 ENCODING 201 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 18 30 F8 C0 F0 C0 C0 F8 ENDCHAR STARTCHAR char202 ENCODING 202 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 30 78 84 7C 60 78 60 7C ENDCHAR STARTCHAR char203 ENCODING 203 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP D8 00 F8 C0 F0 C0 C0 F8 ENDCHAR STARTCHAR char204 ENCODING 204 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP C0 60 00 F0 60 60 60 F0 ENDCHAR STARTCHAR char205 ENCODING 205 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP 30 60 00 F0 60 60 60 F0 ENDCHAR STARTCHAR char206 ENCODING 206 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 20 70 88 F0 60 60 60 F0 ENDCHAR STARTCHAR char207 ENCODING 207 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP 90 00 F0 60 60 60 60 F0 ENDCHAR STARTCHAR char208 ENCODING 208 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 7C 66 66 F6 66 66 66 7C ENDCHAR STARTCHAR char209 ENCODING 209 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 64 FC 98 EC FC FC DC CC ENDCHAR STARTCHAR char210 ENCODING 210 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 60 30 78 CC CC CC CC 78 ENDCHAR STARTCHAR char211 ENCODING 211 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 18 30 78 CC CC CC CC 78 ENDCHAR STARTCHAR char212 ENCODING 212 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 10 38 44 78 CC CC CC 78 ENDCHAR STARTCHAR char213 ENCODING 213 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 64 FC B8 CC CC CC CC 78 ENDCHAR STARTCHAR char214 ENCODING 214 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC 78 CC CC CC CC CC 78 ENDCHAR STARTCHAR char215 ENCODING 215 SWIDTH 545 0 DWIDTH 7 0 BBX 7 5 0 0 BITMAP 44 28 10 28 44 ENDCHAR STARTCHAR char216 ENCODING 216 SWIDTH 545 0 DWIDTH 7 0 BBX 7 9 0 -1 BITMAP 04 78 CC DC CC EC CC 78 80 ENDCHAR STARTCHAR char217 ENCODING 217 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 60 30 CC CC CC CC CC 78 ENDCHAR STARTCHAR char218 ENCODING 218 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 18 30 CC CC CC CC CC 78 ENDCHAR STARTCHAR char219 ENCODING 219 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 30 78 84 CC CC CC CC 78 ENDCHAR STARTCHAR char220 ENCODING 220 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC 00 CC CC CC CC CC 78 ENDCHAR STARTCHAR char221 ENCODING 221 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 18 30 CC CC 78 30 30 30 ENDCHAR STARTCHAR char222 ENCODING 222 SWIDTH 545 0 DWIDTH 7 0 BBX 7 9 0 -1 BITMAP C0 C0 F8 CC CC CC F8 C0 C0 ENDCHAR STARTCHAR char223 ENCODING 223 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP 78 CC D8 CC CC D8 C0 C0 ENDCHAR STARTCHAR char224 ENCODING 224 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 60 30 78 0C 7C CC CC 7A ENDCHAR STARTCHAR char225 ENCODING 225 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 18 30 78 0C 7C CC CC 7A ENDCHAR STARTCHAR char226 ENCODING 226 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 10 38 44 78 0C 7C CC 7A ENDCHAR STARTCHAR char227 ENCODING 227 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 64 FC 98 78 0C 7C CC 7A ENDCHAR STARTCHAR char228 ENCODING 228 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC 00 78 0C 7C CC CC 7A ENDCHAR STARTCHAR char229 ENCODING 229 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 38 6C 38 78 0C 7C CC 7A ENDCHAR STARTCHAR char230 ENCODING 230 SWIDTH 701 0 DWIDTH 9 0 BBX 8 6 0 0 BITMAP 76 1B 7F D8 DB 76 ENDCHAR STARTCHAR char231 ENCODING 231 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP 78 CC C0 C0 CC 78 30 60 ENDCHAR STARTCHAR char232 ENCODING 232 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 60 30 78 CC FC C0 CC 78 ENDCHAR STARTCHAR char233 ENCODING 233 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 18 30 78 CC FC C0 CC 78 ENDCHAR STARTCHAR char234 ENCODING 234 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 10 38 44 78 CC FC C0 78 ENDCHAR STARTCHAR char235 ENCODING 235 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC 00 78 CC FC C0 CC 78 ENDCHAR STARTCHAR char236 ENCODING 236 SWIDTH 311 0 DWIDTH 4 0 BBX 4 8 0 0 BITMAP C0 60 00 60 60 60 60 60 ENDCHAR STARTCHAR char237 ENCODING 237 SWIDTH 311 0 DWIDTH 4 0 BBX 4 8 0 0 BITMAP 60 C0 00 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR char238 ENCODING 238 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 20 70 88 60 60 60 60 60 ENDCHAR STARTCHAR char239 ENCODING 239 SWIDTH 389 0 DWIDTH 5 0 BBX 5 8 0 0 BITMAP 90 00 60 60 60 60 60 60 ENDCHAR STARTCHAR char240 ENCODING 240 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP EC 38 38 0C 7C CC CC 78 ENDCHAR STARTCHAR char241 ENCODING 241 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 64 FC 98 D8 EC CC CC CC ENDCHAR STARTCHAR char242 ENCODING 242 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 60 30 78 CC CC CC CC 78 ENDCHAR STARTCHAR char243 ENCODING 243 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 18 30 78 CC CC CC CC 78 ENDCHAR STARTCHAR char244 ENCODING 244 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 10 38 44 78 CC CC CC 78 ENDCHAR STARTCHAR char245 ENCODING 245 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 64 FC 98 78 CC CC CC 78 ENDCHAR STARTCHAR char246 ENCODING 246 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC 00 78 CC CC CC CC 78 ENDCHAR STARTCHAR char247 ENCODING 247 SWIDTH 545 0 DWIDTH 7 0 BBX 7 5 0 0 BITMAP 10 00 7C 00 10 ENDCHAR STARTCHAR char248 ENCODING 248 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -1 BITMAP 04 78 CC DC EC CC 78 80 ENDCHAR STARTCHAR char249 ENCODING 249 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 60 30 CC CC CC CC DC 6C ENDCHAR STARTCHAR char250 ENCODING 250 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 18 30 CC CC CC CC DC 6C ENDCHAR STARTCHAR char251 ENCODING 251 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 30 78 84 CC CC CC DC 6C ENDCHAR STARTCHAR char252 ENCODING 252 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP CC 00 CC CC CC CC DC 6C ENDCHAR STARTCHAR char253 ENCODING 253 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP 18 30 CC CC 78 30 60 C0 ENDCHAR STARTCHAR char254 ENCODING 254 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 -2 BITMAP C0 C0 C0 F0 D8 D8 F0 C0 C0 C0 ENDCHAR STARTCHAR char255 ENCODING 255 SWIDTH 545 0 DWIDTH 7 0 BBX 7 10 0 -2 BITMAP CC 00 CC CC CC CC 7C 0C CC 78 ENDCHAR STARTCHAR Abreve ENCODING 258 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 64 FC 98 78 CC FC CC CC ENDCHAR STARTCHAR abreve ENCODING 259 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 0 BITMAP 64 FC 98 78 0C 7C CC 7A ENDCHAR STARTCHAR Aogonek ENCODING 260 SWIDTH 545 0 DWIDTH 7 0 BBX 6 10 0 -2 BITMAP 78 CC CC FC CC CC CC CC 18 0C ENDCHAR STARTCHAR aogonek ENCODING 261 SWIDTH 545 0 DWIDTH 7 0 BBX 7 8 0 -2 BITMAP 78 0C 7C CC CC 7E 18 0C ENDCHAR STARTCHAR Cacute ENCODING 262 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 18 30 78 CC C0 C0 CC 78 ENDCHAR STARTCHAR cacute ENCODING 263 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 18 30 78 CC C0 C0 CC 78 ENDCHAR STARTCHAR Ccaron ENCODING 268 SWIDTH 701 0 DWIDTH 7 0 BBX 6 9 0 0 BITMAP 48 30 78 CC C0 C0 C0 CC 78 ENDCHAR STARTCHAR ccaron ENCODING 269 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 48 30 78 CC C0 C0 CC 78 ENDCHAR STARTCHAR Dcaron ENCODING 270 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 68 30 F8 CC CC CC CC F8 ENDCHAR STARTCHAR dcaron ENCODING 271 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 90 6C 0C 6C DC CC DC 6C ENDCHAR STARTCHAR Dcroat ENCODING 272 SWIDTH 623 0 DWIDTH 8 0 BBX 7 8 0 0 BITMAP 7C 66 66 F6 66 66 66 7C ENDCHAR STARTCHAR dcroat ENCODING 273 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP EC 38 38 0C 7C CC CC 78 ENDCHAR STARTCHAR Edotaccent ENCODING 278 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 0 0 BITMAP 40 00 F8 C0 F0 C0 C0 F8 ENDCHAR STARTCHAR edotaccent ENCODING 279 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 20 00 78 CC FC C0 CC 78 ENDCHAR STARTCHAR Eogonek ENCODING 280 SWIDTH 467 0 DWIDTH 6 0 BBX 5 10 0 -2 BITMAP F8 C0 C0 F0 C0 C0 C0 F8 30 18 ENDCHAR STARTCHAR eogonek ENCODING 281 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 -2 BITMAP 78 CC FC C0 CC 78 30 18 ENDCHAR STARTCHAR Ecaron ENCODING 282 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 0 0 BITMAP 50 20 F8 C0 F0 C0 C0 F8 ENDCHAR STARTCHAR ecaron ENCODING 283 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 48 30 78 CC FC C0 CC 78 ENDCHAR STARTCHAR Gbreve ENCODING 286 SWIDTH 545 0 DWIDTH 7 0 BBX 6 10 1 0 BITMAP 48 30 00 78 CC C0 DC CC CC 74 ENDCHAR STARTCHAR gbreve ENCODING 287 SWIDTH 545 0 DWIDTH 7 0 BBX 6 10 1 -1 BITMAP 48 30 00 6C DC CC DC 6C 0C 78 ENDCHAR STARTCHAR Iogonek ENCODING 302 SWIDTH 389 0 DWIDTH 5 0 BBX 4 10 0 -2 BITMAP F0 60 60 60 60 60 60 F0 20 10 ENDCHAR STARTCHAR iogonek ENCODING 303 SWIDTH 233 0 DWIDTH 3 0 BBX 3 10 0 -2 BITMAP C0 00 C0 C0 C0 C0 C0 C0 40 20 ENDCHAR STARTCHAR Idotaccent ENCODING 304 SWIDTH 467 0 DWIDTH 6 0 BBX 4 10 2 0 BITMAP 60 00 F0 60 60 60 60 60 60 F0 ENDCHAR STARTCHAR dotlessi ENCODING 305 SWIDTH 233 0 DWIDTH 3 0 BBX 2 6 1 0 BITMAP C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR Lacute ENCODING 313 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 0 0 BITMAP 18 30 C0 C0 C0 C0 C0 F8 ENDCHAR STARTCHAR lacute ENCODING 314 SWIDTH 233 0 DWIDTH 3 0 BBX 3 8 0 0 BITMAP 60 C0 00 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR Lcaron ENCODING 317 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP CC CC D8 C0 C0 C0 C0 F8 ENDCHAR STARTCHAR lcaron ENCODING 318 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP CC CC D8 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR Lslash ENCODING 321 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 60 68 78 70 E0 E0 60 7C ENDCHAR STARTCHAR lslash ENCODING 322 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 0 0 BITMAP 60 68 78 70 E0 E0 60 60 ENDCHAR STARTCHAR Nacute ENCODING 323 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 18 30 CC EC EC DC DC CC ENDCHAR STARTCHAR nacute ENCODING 324 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 18 30 D8 EC CC CC CC CC ENDCHAR STARTCHAR Ncaron ENCODING 327 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 48 30 CC EC EC DC DC CC ENDCHAR STARTCHAR ncaron ENCODING 328 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 48 30 D8 EC CC CC CC CC ENDCHAR STARTCHAR Ohungarumlaut ENCODING 336 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 64 FC B8 CC CC CC CC 78 ENDCHAR STARTCHAR ohungarumlaut ENCODING 337 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 64 FC 98 78 CC CC CC 78 ENDCHAR STARTCHAR Racute ENCODING 340 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 18 30 F8 CC F8 F0 D8 CC ENDCHAR STARTCHAR racute ENCODING 341 SWIDTH 311 0 DWIDTH 4 0 BBX 4 8 0 0 BITMAP 30 60 B0 E0 C0 C0 C0 C0 ENDCHAR STARTCHAR Rcaron ENCODING 344 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 48 30 F8 CC F8 F0 D8 CC ENDCHAR STARTCHAR rcaron ENCODING 345 SWIDTH 389 0 DWIDTH 5 0 BBX 4 8 0 0 BITMAP 90 60 B0 E0 C0 C0 C0 C0 ENDCHAR STARTCHAR Sacute ENCODING 346 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 18 30 78 C4 78 0C CC 78 ENDCHAR STARTCHAR sacute ENCODING 347 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 18 30 78 C4 78 0C CC 78 ENDCHAR STARTCHAR Scedilla ENCODING 350 SWIDTH 545 0 DWIDTH 7 0 BBX 6 10 1 -2 BITMAP 78 CC C0 78 0C 0C CC 78 30 E0 ENDCHAR STARTCHAR scedilla ENCODING 351 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 -2 BITMAP 78 C4 60 18 CC 78 30 60 ENDCHAR STARTCHAR Scaron ENCODING 352 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 48 30 78 C4 78 0C CC 78 ENDCHAR STARTCHAR scaron ENCODING 353 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 48 30 78 C4 78 0C CC 78 ENDCHAR STARTCHAR Tcaron ENCODING 356 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 48 30 FC 30 30 30 30 30 ENDCHAR STARTCHAR tcaron ENCODING 357 SWIDTH 623 0 DWIDTH 8 0 BBX 7 8 0 0 BITMAP D2 CC F0 C0 C0 C0 C0 70 ENDCHAR STARTCHAR Utilde ENCODING 360 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 64 FC 98 00 CC CC CC CC CC 78 ENDCHAR STARTCHAR utilde ENCODING 361 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 64 FC 98 00 CC CC CC CC DC 6C ENDCHAR STARTCHAR Umacron ENCODING 362 SWIDTH 545 0 DWIDTH 7 0 BBX 6 9 0 0 BITMAP 78 00 CC CC CC CC CC CC 78 ENDCHAR STARTCHAR umacron ENCODING 363 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 78 00 CC CC CC CC DC 6C ENDCHAR STARTCHAR Uring ENCODING 366 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 20 50 AC CC CC CC CC 78 ENDCHAR STARTCHAR uring ENCODING 367 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 20 50 AC CC CC CC DC 6C ENDCHAR STARTCHAR Uhungarumlaut ENCODING 368 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 2C 58 84 CC CC CC CC 78 ENDCHAR STARTCHAR uhungarumlaut ENCODING 369 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 2C 58 84 CC CC CC DC 6C ENDCHAR STARTCHAR Uogonek ENCODING 370 SWIDTH 545 0 DWIDTH 7 0 BBX 6 10 0 -2 BITMAP CC CC CC CC CC CC CC 78 10 0C ENDCHAR STARTCHAR uogonek ENCODING 371 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 -2 BITMAP CC CC CC CC DC 6C 08 04 ENDCHAR STARTCHAR Zacute ENCODING 377 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 18 30 FC 0C 18 70 C0 FC ENDCHAR STARTCHAR zacute ENCODING 378 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 0 0 BITMAP 18 30 F8 18 30 60 C0 F8 ENDCHAR STARTCHAR Zdotaccent ENCODING 379 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 0 0 BITMAP 30 00 FC 0C 18 70 C0 FC ENDCHAR STARTCHAR zdotaccent ENCODING 380 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 0 0 BITMAP 30 00 F8 18 30 60 C0 F8 ENDCHAR STARTCHAR Zcaron ENCODING 381 SWIDTH 545 0 DWIDTH 7 0 BBX 6 9 0 0 BITMAP 48 30 FC 0C 18 30 60 C0 FC ENDCHAR STARTCHAR zcaron ENCODING 382 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 0 0 BITMAP 48 30 F8 18 30 60 C0 F8 ENDCHAR STARTCHAR breve ENCODING 728 SWIDTH 545 0 DWIDTH 7 0 BBX 4 3 2 5 BITMAP 90 F0 60 ENDCHAR STARTCHAR ogonek ENCODING 731 SWIDTH 545 0 DWIDTH 7 0 BBX 3 2 3 -2 BITMAP C0 60 ENDCHAR STARTCHAR tonos ENCODING 900 SWIDTH 467 0 DWIDTH 6 0 BBX 3 2 2 7 BITMAP 60 C0 ENDCHAR STARTCHAR dieresistonos ENCODING 901 SWIDTH 467 0 DWIDTH 6 0 BBX 4 4 1 6 BITMAP 30 60 00 D0 ENDCHAR STARTCHAR Alphatonos ENCODING 902 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 18 30 00 78 CC CC FC CC CC CC ENDCHAR STARTCHAR anoteleia ENCODING 903 SWIDTH 467 0 DWIDTH 6 0 BBX 3 2 2 3 BITMAP E0 E0 ENDCHAR STARTCHAR Epsilontonos ENCODING 904 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 18 30 00 FC C0 C0 F8 C0 C0 FC ENDCHAR STARTCHAR Etatonos ENCODING 905 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 18 30 00 CC CC CC FC CC CC CC ENDCHAR STARTCHAR Iotatonos ENCODING 906 SWIDTH 467 0 DWIDTH 6 0 BBX 4 10 1 0 BITMAP 30 60 00 F0 60 60 60 60 60 F0 ENDCHAR STARTCHAR Omicrontonos ENCODING 908 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 18 30 00 78 CC CC CC CC CC 78 ENDCHAR STARTCHAR Upsilontonos ENCODING 910 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 18 30 00 CC CC 68 30 30 30 30 ENDCHAR STARTCHAR Omegatonos ENCODING 911 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 18 30 00 78 CC CC CC CC 68 EC ENDCHAR STARTCHAR iotadieresistonos ENCODING 912 SWIDTH 467 0 DWIDTH 6 0 BBX 5 10 1 0 BITMAP 30 60 00 D0 00 60 60 60 68 30 ENDCHAR STARTCHAR Alpha ENCODING 913 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 78 CC CC FC CC CC CC ENDCHAR STARTCHAR Beta ENCODING 914 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP F8 CC CC F8 CC CC F8 ENDCHAR STARTCHAR Gamma ENCODING 915 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP FC C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR Delta ENCODING 916 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 30 30 68 68 CC CC FC ENDCHAR STARTCHAR Epsilon ENCODING 917 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP FC C0 C0 F8 C0 C0 FC ENDCHAR STARTCHAR Zeta ENCODING 918 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP FC 0C 18 30 60 C0 FC ENDCHAR STARTCHAR Eta ENCODING 919 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP CC CC CC FC CC CC CC ENDCHAR STARTCHAR Theta ENCODING 920 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 78 CC CC FC CC CC 78 ENDCHAR STARTCHAR Iota ENCODING 921 SWIDTH 467 0 DWIDTH 6 0 BBX 4 7 1 0 BITMAP F0 60 60 60 60 60 F0 ENDCHAR STARTCHAR Kappa ENCODING 922 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP CC D8 D0 E0 D0 D8 CC ENDCHAR STARTCHAR Lambda ENCODING 923 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 30 30 68 68 CC CC CC ENDCHAR STARTCHAR Mu ENCODING 924 SWIDTH 623 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 82 C6 EE FE D6 C6 C6 C6 ENDCHAR STARTCHAR Nu ENCODING 925 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP CC CC EC EC DC DC CC CC ENDCHAR STARTCHAR Xi ENCODING 926 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP FC 00 00 78 00 00 FC ENDCHAR STARTCHAR Omicron ENCODING 927 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 78 CC CC CC CC CC 78 ENDCHAR STARTCHAR Pi ENCODING 928 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP FC CC CC CC CC CC CC ENDCHAR STARTCHAR Rho ENCODING 929 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP F8 CC CC F8 C0 C0 C0 ENDCHAR STARTCHAR Sigma ENCODING 931 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP FC 60 30 18 30 60 FC ENDCHAR STARTCHAR Tau ENCODING 932 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP FC 30 30 30 30 30 30 ENDCHAR STARTCHAR Upsilon ENCODING 933 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP CC CC 68 30 30 30 30 ENDCHAR STARTCHAR Phi ENCODING 934 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 30 78 D4 D4 D4 78 30 ENDCHAR STARTCHAR Chi ENCODING 935 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP CC CC 68 30 68 CC CC ENDCHAR STARTCHAR Psi ENCODING 936 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP D4 D4 D4 D4 78 30 30 ENDCHAR STARTCHAR Omega ENCODING 937 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 78 CC CC CC CC 68 EC ENDCHAR STARTCHAR Iotadieresis ENCODING 938 SWIDTH 467 0 DWIDTH 6 0 BBX 4 9 1 0 BITMAP D0 00 F0 60 60 60 60 60 F0 ENDCHAR STARTCHAR Upsilondieresis ENCODING 939 SWIDTH 467 0 DWIDTH 6 0 BBX 6 9 0 0 BITMAP 68 00 CC CC 68 30 30 30 30 ENDCHAR STARTCHAR alphatonos ENCODING 940 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 18 30 00 74 DC CC DC 74 ENDCHAR STARTCHAR epsilontonos ENCODING 941 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 18 30 00 78 CC 70 CC 78 ENDCHAR STARTCHAR etatonos ENCODING 942 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 -2 BITMAP 18 30 00 D8 EC CC CC CC 0C 0C ENDCHAR STARTCHAR iotatonos ENCODING 943 SWIDTH 467 0 DWIDTH 6 0 BBX 4 8 1 0 BITMAP 60 C0 00 C0 C0 C0 D0 60 ENDCHAR STARTCHAR upsilondieresistonos ENCODING 944 SWIDTH 467 0 DWIDTH 6 0 BBX 6 10 0 0 BITMAP 18 30 00 68 00 CC CC CC CC 78 ENDCHAR STARTCHAR alpha ENCODING 945 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP 74 DC CC DC 74 ENDCHAR STARTCHAR beta ENCODING 946 SWIDTH 467 0 DWIDTH 6 0 BBX 6 9 0 -2 BITMAP 70 D8 D8 F8 CC CC F8 C0 C0 ENDCHAR STARTCHAR gamma ENCODING 947 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 -2 BITMAP CC CC 68 68 30 30 30 ENDCHAR STARTCHAR delta ENCODING 948 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 78 C0 78 CC CC CC 78 ENDCHAR STARTCHAR epsilon ENCODING 949 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP 78 CC 70 CC 78 ENDCHAR STARTCHAR zeta ENCODING 950 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 -1 BITMAP FC 30 60 60 60 38 0C 38 ENDCHAR STARTCHAR eta ENCODING 951 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 -2 BITMAP D8 EC CC CC CC 0C 0C ENDCHAR STARTCHAR theta ENCODING 952 SWIDTH 467 0 DWIDTH 6 0 BBX 5 7 1 0 BITMAP 70 D8 D8 F8 D8 D8 70 ENDCHAR STARTCHAR iota ENCODING 953 SWIDTH 467 0 DWIDTH 6 0 BBX 4 5 1 0 BITMAP C0 C0 C0 D0 60 ENDCHAR STARTCHAR kappa ENCODING 954 SWIDTH 467 0 DWIDTH 6 0 BBX 5 5 1 0 BITMAP D8 D0 E0 D0 D8 ENDCHAR STARTCHAR lambda ENCODING 955 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 78 CC 0C 74 DC CC CC ENDCHAR STARTCHAR mu ENCODING 956 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 -2 BITMAP CC CC CC DC F4 C0 C0 ENDCHAR STARTCHAR nu ENCODING 957 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP CC CC 68 68 30 ENDCHAR STARTCHAR xi ENCODING 958 SWIDTH 467 0 DWIDTH 6 0 BBX 6 9 0 -2 BITMAP FC 30 60 78 C0 C0 78 0C 18 ENDCHAR STARTCHAR omicron ENCODING 959 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP 78 CC CC CC 78 ENDCHAR STARTCHAR pi ENCODING 960 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP FC 68 68 68 68 ENDCHAR STARTCHAR rho ENCODING 961 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 -2 BITMAP 78 CC CC EC D8 C0 C0 ENDCHAR STARTCHAR sigma1 ENCODING 962 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 -2 BITMAP 78 CC C0 C0 78 0C 38 ENDCHAR STARTCHAR sigma ENCODING 963 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP 7C D8 CC CC 78 ENDCHAR STARTCHAR tau ENCODING 964 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP FC 30 30 34 18 ENDCHAR STARTCHAR upsilon ENCODING 965 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP CC CC CC CC 78 ENDCHAR STARTCHAR phi ENCODING 966 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 -2 BITMAP 18 D4 D4 D4 78 30 30 ENDCHAR STARTCHAR chi ENCODING 967 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 -2 BITMAP CC CC 68 30 68 CC CC ENDCHAR STARTCHAR psi ENCODING 968 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 -2 BITMAP D4 D4 D4 D4 78 30 30 ENDCHAR STARTCHAR omega ENCODING 969 SWIDTH 467 0 DWIDTH 6 0 BBX 6 5 0 0 BITMAP 68 CC D4 D4 68 ENDCHAR STARTCHAR iotadieresis ENCODING 970 SWIDTH 467 0 DWIDTH 6 0 BBX 5 7 1 0 BITMAP D0 00 60 60 60 68 30 ENDCHAR STARTCHAR upsilondieresis ENCODING 971 SWIDTH 467 0 DWIDTH 6 0 BBX 6 7 0 0 BITMAP 68 00 CC CC CC CC 78 ENDCHAR STARTCHAR omicrontonos ENCODING 972 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 18 30 00 78 CC CC CC 78 ENDCHAR STARTCHAR upsilontonos ENCODING 973 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 18 30 00 CC CC CC CC 78 ENDCHAR STARTCHAR omegatonos ENCODING 974 SWIDTH 467 0 DWIDTH 6 0 BBX 6 8 0 0 BITMAP 18 30 00 68 CC D4 D4 68 ENDCHAR STARTCHAR U+0401 ENCODING 1025 SWIDTH 545 0 DWIDTH 7 0 BBX 6 9 1 0 BITMAP CC 00 FC C0 C0 F0 C0 C0 FC ENDCHAR STARTCHAR U+0404 ENCODING 1028 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 78 CC C0 F0 C0 C0 CC 78 ENDCHAR STARTCHAR U+0406 ENCODING 1030 SWIDTH 233 0 DWIDTH 3 0 BBX 2 8 1 0 BITMAP C0 C0 C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR U+0407 ENCODING 1031 SWIDTH 389 0 DWIDTH 5 0 BBX 4 9 1 0 BITMAP 90 00 60 60 60 60 60 60 60 ENDCHAR STARTCHAR U+040E ENCODING 1038 SWIDTH 623 0 DWIDTH 8 0 BBX 7 9 1 0 BITMAP 24 5A 46 2C 2C 18 18 D0 60 ENDCHAR STARTCHAR U+0410 ENCODING 1040 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 3C 6C CC CC CC FC CC CC ENDCHAR STARTCHAR U+0411 ENCODING 1041 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP FC C0 C0 F8 CC CC CC F8 ENDCHAR STARTCHAR U+0412 ENCODING 1042 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP F8 CC CC F8 CC CC CC F8 ENDCHAR STARTCHAR U+0413 ENCODING 1043 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP FC C0 C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR U+0414 ENCODING 1044 SWIDTH 701 0 DWIDTH 9 0 BBX 8 9 1 -1 BITMAP 1E 36 66 66 66 66 66 FF 81 ENDCHAR STARTCHAR U+0415 ENCODING 1045 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP FC C0 C0 F0 C0 C0 C0 FC ENDCHAR STARTCHAR U+0416 ENCODING 1046 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP B4 B4 78 30 30 78 B4 B4 ENDCHAR STARTCHAR U+0417 ENCODING 1047 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 78 CC 0C 18 0C 0C CC 78 ENDCHAR STARTCHAR U+0418 ENCODING 1048 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP CC CC CC CC DC EC CC CC ENDCHAR STARTCHAR U+0419 ENCODING 1049 SWIDTH 545 0 DWIDTH 7 0 BBX 6 9 1 0 BITMAP 48 B4 CC CC CC DC EC CC CC ENDCHAR STARTCHAR U+041A ENCODING 1050 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 1 0 BITMAP C8 D8 F0 E0 E0 F0 D8 C8 ENDCHAR STARTCHAR U+041B ENCODING 1051 SWIDTH 623 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 3E 66 66 66 66 66 66 C6 ENDCHAR STARTCHAR U+041C ENCODING 1052 SWIDTH 623 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP C6 EE FE D6 C6 C6 C6 C6 ENDCHAR STARTCHAR U+041D ENCODING 1053 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP CC CC CC FC CC CC CC CC ENDCHAR STARTCHAR U+041E ENCODING 1054 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 78 CC CC CC CC CC CC 78 ENDCHAR STARTCHAR U+041F ENCODING 1055 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP FC CC CC CC CC CC CC CC ENDCHAR STARTCHAR U+0420 ENCODING 1056 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP F8 CC CC CC F8 C0 C0 C0 ENDCHAR STARTCHAR U+0421 ENCODING 1057 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 78 CC C0 C0 C0 C0 CC 78 ENDCHAR STARTCHAR U+0422 ENCODING 1058 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP FC 30 30 30 30 30 30 30 ENDCHAR STARTCHAR U+0423 ENCODING 1059 SWIDTH 623 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 46 46 2C 2C 18 18 D0 60 ENDCHAR STARTCHAR U+0424 ENCODING 1060 SWIDTH 701 0 DWIDTH 9 0 BBX 8 8 1 0 BITMAP 18 7E DB DB DB 7E 18 18 ENDCHAR STARTCHAR U+0425 ENCODING 1061 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP CC 48 78 30 30 78 48 CC ENDCHAR STARTCHAR U+0426 ENCODING 1062 SWIDTH 623 0 DWIDTH 8 0 BBX 7 9 1 -1 BITMAP CC CC CC CC CC CC CC FE 02 ENDCHAR STARTCHAR U+0427 ENCODING 1063 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP CC CC CC CC 7C 0C 0C 0C ENDCHAR STARTCHAR U+0428 ENCODING 1064 SWIDTH 701 0 DWIDTH 9 0 BBX 8 8 1 0 BITMAP C3 C3 DB DB DB DB DB FF ENDCHAR STARTCHAR U+0429 ENCODING 1065 SWIDTH 779 0 DWIDTH 10 0 BBX 9 9 1 -1 BITMAP C300 C300 DB00 DB00 DB00 DB00 DB00 FF80 0080 ENDCHAR STARTCHAR U+042A ENCODING 1066 SWIDTH 623 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP E0 60 60 7C 66 66 66 7C ENDCHAR STARTCHAR U+042B ENCODING 1067 SWIDTH 701 0 DWIDTH 9 0 BBX 8 8 1 0 BITMAP C3 C3 C3 FB CF CF CF FB ENDCHAR STARTCHAR U+042C ENCODING 1068 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP C0 C0 C0 F8 CC CC CC F8 ENDCHAR STARTCHAR U+042D ENCODING 1069 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 78 CC 0C 3C 0C 0C CC 78 ENDCHAR STARTCHAR U+042E ENCODING 1070 SWIDTH 701 0 DWIDTH 9 0 BBX 8 8 1 0 BITMAP CE DB DB FB DB DB DB CE ENDCHAR STARTCHAR U+042F ENCODING 1071 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 7C CC CC CC 7C CC CC CC ENDCHAR STARTCHAR U+0430 ENCODING 1072 SWIDTH 623 0 DWIDTH 8 0 BBX 7 6 1 0 BITMAP 78 0C 7C CC CC 7A ENDCHAR STARTCHAR U+0431 ENCODING 1073 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 04 78 C0 F8 CC CC CC 78 ENDCHAR STARTCHAR U+0432 ENCODING 1074 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 70 D8 D8 F0 F8 CC CC 78 ENDCHAR STARTCHAR U+0433 ENCODING 1075 SWIDTH 467 0 DWIDTH 6 0 BBX 5 6 1 0 BITMAP F8 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR U+0434 ENCODING 1076 SWIDTH 701 0 DWIDTH 9 0 BBX 8 7 1 -1 BITMAP 3E 66 66 66 66 FF 81 ENDCHAR STARTCHAR U+0435 ENCODING 1077 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP 78 CC FC C0 CC 78 ENDCHAR STARTCHAR U+0436 ENCODING 1078 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP B4 FC 30 78 FC B4 ENDCHAR STARTCHAR U+0437 ENCODING 1079 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP 78 CC 18 0C CC 78 ENDCHAR STARTCHAR U+0438 ENCODING 1080 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP CC CC CC CC CC 74 ENDCHAR STARTCHAR U+0439 ENCODING 1081 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 48 30 CC CC CC CC CC 74 ENDCHAR STARTCHAR U+043A ENCODING 1082 SWIDTH 467 0 DWIDTH 6 0 BBX 5 6 1 0 BITMAP D8 D8 E0 F0 D8 D8 ENDCHAR STARTCHAR U+043B ENCODING 1083 SWIDTH 623 0 DWIDTH 8 0 BBX 7 6 1 0 BITMAP 3E 66 66 66 66 C6 ENDCHAR STARTCHAR U+043C ENCODING 1084 SWIDTH 623 0 DWIDTH 8 0 BBX 7 6 1 0 BITMAP C6 EE D6 C6 C6 C6 ENDCHAR STARTCHAR U+043D ENCODING 1085 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP CC CC FC CC CC CC ENDCHAR STARTCHAR U+043E ENCODING 1086 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP 78 CC CC CC CC 78 ENDCHAR STARTCHAR U+043F ENCODING 1087 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP F8 CC CC CC CC CC ENDCHAR STARTCHAR U+0440 ENCODING 1088 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP 78 CC CC F8 C0 C0 ENDCHAR STARTCHAR U+0441 ENCODING 1089 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP 78 CC C0 C0 CC 78 ENDCHAR STARTCHAR U+0442 ENCODING 1090 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP FC 30 30 30 30 30 ENDCHAR STARTCHAR U+0443 ENCODING 1091 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 -2 BITMAP CC CC CC CC 7C 0C CC 78 ENDCHAR STARTCHAR U+0444 ENCODING 1092 SWIDTH 701 0 DWIDTH 9 0 BBX 8 6 1 0 BITMAP 18 7E DB DB 7E 18 ENDCHAR STARTCHAR U+0445 ENCODING 1093 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP CC 78 30 30 78 CC ENDCHAR STARTCHAR U+0446 ENCODING 1094 SWIDTH 623 0 DWIDTH 8 0 BBX 7 7 1 -1 BITMAP CC CC CC CC CC 7E 02 ENDCHAR STARTCHAR U+0447 ENCODING 1095 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP CC CC CC 7C 0C 0C ENDCHAR STARTCHAR U+0448 ENCODING 1096 SWIDTH 701 0 DWIDTH 9 0 BBX 8 6 1 0 BITMAP DB DB DB DB DB 7F ENDCHAR STARTCHAR U+0449 ENCODING 1097 SWIDTH 701 0 DWIDTH 9 0 BBX 9 7 1 -1 BITMAP DB00 DB00 DB00 DB00 DB00 7F80 0080 ENDCHAR STARTCHAR U+044A ENCODING 1098 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP E0 60 78 6C 6C 38 ENDCHAR STARTCHAR U+044B ENCODING 1099 SWIDTH 623 0 DWIDTH 8 0 BBX 7 6 1 0 BITMAP C6 C6 F6 DE DE 76 ENDCHAR STARTCHAR U+044C ENCODING 1100 SWIDTH 467 0 DWIDTH 6 0 BBX 5 6 1 0 BITMAP C0 C0 F0 D8 D8 70 ENDCHAR STARTCHAR U+044D ENCODING 1101 SWIDTH 467 0 DWIDTH 6 0 BBX 5 6 1 0 BITMAP 70 D8 18 38 98 70 ENDCHAR STARTCHAR U+044E ENCODING 1102 SWIDTH 701 0 DWIDTH 9 0 BBX 8 6 1 0 BITMAP CE DB DB FB DB CE ENDCHAR STARTCHAR U+044F ENCODING 1103 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP 78 CC CC 7C CC CC ENDCHAR STARTCHAR U+0451 ENCODING 1105 SWIDTH 545 0 DWIDTH 7 0 BBX 6 8 1 0 BITMAP 48 00 78 CC FC C0 CC 78 ENDCHAR STARTCHAR U+0454 ENCODING 1108 SWIDTH 545 0 DWIDTH 7 0 BBX 6 6 1 0 BITMAP 78 CC C0 F0 C4 78 ENDCHAR STARTCHAR U+0456 ENCODING 1110 SWIDTH 233 0 DWIDTH 3 0 BBX 2 8 1 0 BITMAP C0 00 C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR U+0457 ENCODING 1111 SWIDTH 389 0 DWIDTH 5 0 BBX 4 8 1 0 BITMAP 90 00 60 60 60 60 60 60 ENDCHAR STARTCHAR U+045E ENCODING 1118 SWIDTH 545 0 DWIDTH 7 0 BBX 6 10 1 -2 BITMAP 48 30 CC CC CC CC 7C 0C CC 78 ENDCHAR STARTCHAR U+0490 ENCODING 1168 SWIDTH 545 0 DWIDTH 7 0 BBX 6 9 1 0 BITMAP 04 FC C0 C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR U+0491 ENCODING 1169 SWIDTH 467 0 DWIDTH 6 0 BBX 5 8 1 0 BITMAP 08 F8 C0 C0 C0 C0 C0 C0 ENDCHAR STARTCHAR U+0E01 ENCODING 3585 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 88 48 88 88 88 88 88 ENDCHAR STARTCHAR U+0E02 ENCODING 3586 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 1 0 BITMAP 48 A8 68 28 28 28 28 38 ENDCHAR STARTCHAR U+0E03 ENCODING 3587 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 8 1 0 BITMAP 54 AC 54 14 14 14 14 1C ENDCHAR STARTCHAR U+0E04 ENCODING 3588 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 8 1 0 BITMAP 78 84 94 AC 54 44 44 44 ENDCHAR STARTCHAR U+0E05 ENCODING 3589 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 8 1 0 BITMAP 58 A4 94 AC 54 44 44 44 ENDCHAR STARTCHAR U+0E06 ENCODING 3590 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 51 A9 51 11 11 1D 2B 11 ENDCHAR STARTCHAR U+0E07 ENCODING 3591 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 10 28 18 08 88 48 28 18 ENDCHAR STARTCHAR U+0E08 ENCODING 3592 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 88 08 28 58 28 08 08 ENDCHAR STARTCHAR U+0E09 ENCODING 3593 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 78 84 04 44 A4 66 3D 22 ENDCHAR STARTCHAR U+0E0A ENCODING 3594 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 0 BITMAP 02 04 48 B0 68 28 28 28 28 38 ENDCHAR STARTCHAR U+0E0B ENCODING 3595 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 10 0 0 BITMAP 01 02 54 A8 54 14 14 14 14 1C ENDCHAR STARTCHAR U+0E0C ENCODING 3596 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 72 8A 4A 8A 8A CA B6 4A ENDCHAR STARTCHAR U+0E0D ENCODING 3597 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 11 1 -2 BITMAP 64 94 54 94 94 D4 B4 5C 00 10 1C ENDCHAR STARTCHAR U+0E0E ENCODING 3598 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 11 1 -2 BITMAP 1C 22 12 22 22 62 A2 42 2A 52 2E ENDCHAR STARTCHAR U+0E0F ENCODING 3599 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 11 1 -2 BITMAP 1C 22 12 22 22 62 A2 42 52 AA 56 ENDCHAR STARTCHAR U+0E10 ENCODING 3600 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 11 1 -2 BITMAP 1C 22 18 04 12 2A 16 02 52 AA 56 ENDCHAR STARTCHAR U+0E11 ENCODING 3601 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 56 AD 55 19 19 11 11 11 ENDCHAR STARTCHAR U+0E12 ENCODING 3602 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 29 55 95 AD 95 AD 57 49 ENDCHAR STARTCHAR U+0E13 ENCODING 3603 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 64 94 54 94 94 D6 BD 52 ENDCHAR STARTCHAR U+0E14 ENCODING 3604 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 8 1 0 BITMAP 38 44 94 AC 94 A4 44 44 ENDCHAR STARTCHAR U+0E15 ENCODING 3605 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 8 1 0 BITMAP 28 54 94 AC 94 A4 44 44 ENDCHAR STARTCHAR U+0E16 ENCODING 3606 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 88 48 88 88 C8 A8 48 ENDCHAR STARTCHAR U+0E17 ENCODING 3607 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 44 AA 6A 32 32 22 22 22 ENDCHAR STARTCHAR U+0E18 ENCODING 3608 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 88 E0 18 88 88 88 F8 ENDCHAR STARTCHAR U+0E19 ENCODING 3609 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 44 A4 64 24 24 2E 35 22 ENDCHAR STARTCHAR U+0E1A ENCODING 3610 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 42 A2 62 22 22 22 22 3E ENDCHAR STARTCHAR U+0E1B ENCODING 3611 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 0 BITMAP 02 02 42 A2 62 22 22 22 22 3E ENDCHAR STARTCHAR U+0E1C ENCODING 3612 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 48 A8 C8 88 88 A8 D8 88 ENDCHAR STARTCHAR U+0E1D ENCODING 3613 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 10 3 0 BITMAP 08 08 48 A8 C8 88 88 A8 D8 88 ENDCHAR STARTCHAR U+0E1E ENCODING 3614 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 42 A2 62 22 22 2A 36 22 ENDCHAR STARTCHAR U+0E1F ENCODING 3615 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 0 BITMAP 02 02 42 A2 62 22 22 2A 36 22 ENDCHAR STARTCHAR U+0E20 ENCODING 3616 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 1C 22 12 22 22 62 A2 42 ENDCHAR STARTCHAR U+0E21 ENCODING 3617 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 42 A2 62 22 22 72 AA 46 ENDCHAR STARTCHAR U+0E22 ENCODING 3618 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 48 A8 C8 88 48 88 88 F8 ENDCHAR STARTCHAR U+0E23 ENCODING 3619 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 C8 20 10 10 30 50 20 ENDCHAR STARTCHAR U+0E24 ENCODING 3620 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 10 2 -2 BITMAP 70 88 48 88 88 C8 A8 48 08 08 ENDCHAR STARTCHAR U+0E25 ENCODING 3621 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 88 08 68 98 C8 A8 48 ENDCHAR STARTCHAR U+0E26 ENCODING 3622 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 -2 BITMAP 1C 22 12 22 22 62 A2 42 02 02 ENDCHAR STARTCHAR U+0E27 ENCODING 3623 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 88 08 08 08 18 28 10 ENDCHAR STARTCHAR U+0E28 ENCODING 3624 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 0 BITMAP 02 04 78 84 94 AC 54 44 44 44 ENDCHAR STARTCHAR U+0E29 ENCODING 3625 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 42 A2 6B 36 2A 22 22 3E ENDCHAR STARTCHAR U+0E2A ENCODING 3626 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 10 1 0 BITMAP 04 08 70 88 08 68 98 C8 A8 48 ENDCHAR STARTCHAR U+0E2B ENCODING 3627 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 42 A5 6E 32 22 22 22 22 ENDCHAR STARTCHAR U+0E2C ENCODING 3628 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 0 BITMAP 0A 14 4A A2 62 22 22 2A 36 22 ENDCHAR STARTCHAR U+0E2D ENCODING 3629 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 88 08 48 A8 C8 88 F8 ENDCHAR STARTCHAR U+0E2E ENCODING 3630 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 10 1 0 BITMAP 04 08 70 88 08 48 A8 C8 88 F8 ENDCHAR STARTCHAR U+0E2F ENCODING 3631 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 7 1 0 BITMAP 58 A8 48 08 08 08 08 ENDCHAR STARTCHAR U+0E30 ENCODING 3632 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 6 2 1 BITMAP 40 A8 70 40 A8 70 ENDCHAR STARTCHAR U+0E31 ENCODING 3633 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 3 2 9 BITMAP 40 A8 70 ENDCHAR STARTCHAR U+0E32 ENCODING 3634 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 8 2 0 BITMAP 70 88 08 08 08 08 08 08 ENDCHAR STARTCHAR U+0E33 ENCODING 3635 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 11 0 0 BITMAP 40 A0 40 1C 22 02 02 02 02 02 02 ENDCHAR STARTCHAR U+0E34 ENCODING 3636 SWIDTH 1000 0 DWIDTH 0 0 BBX 4 1 3 10 BITMAP F0 ENDCHAR STARTCHAR U+0E35 ENCODING 3637 SWIDTH 1000 0 DWIDTH 0 0 BBX 4 2 3 10 BITMAP 10 F0 ENDCHAR STARTCHAR U+0E36 ENCODING 3638 SWIDTH 1000 0 DWIDTH 0 0 BBX 5 3 2 10 BITMAP 10 28 F0 ENDCHAR STARTCHAR U+0E37 ENCODING 3639 SWIDTH 1000 0 DWIDTH 0 0 BBX 5 2 2 10 BITMAP 28 F8 ENDCHAR STARTCHAR U+0E38 ENCODING 3640 SWIDTH 1000 0 DWIDTH 0 0 BBX 2 2 4 -2 BITMAP C0 40 ENDCHAR STARTCHAR U+0E39 ENCODING 3641 SWIDTH 1000 0 DWIDTH 0 0 BBX 4 2 3 -2 BITMAP D0 70 ENDCHAR STARTCHAR U+0E3A ENCODING 3642 SWIDTH 1000 0 DWIDTH 0 0 BBX 2 2 3 -2 BITMAP C0 C0 ENDCHAR STARTCHAR U+0E3F ENCODING 3647 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 12 2 0 BITMAP 20 20 F0 A8 A8 F0 A8 A8 A8 F0 20 20 ENDCHAR STARTCHAR U+0E40 ENCODING 3648 SWIDTH 1000 0 DWIDTH 8 0 BBX 3 8 3 0 BITMAP 80 80 80 80 80 C0 A0 40 ENDCHAR STARTCHAR U+0E41 ENCODING 3649 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 8 1 0 BITMAP 90 90 90 90 90 D8 B4 48 ENDCHAR STARTCHAR U+0E42 ENCODING 3650 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 11 1 0 BITMAP 78 C0 20 20 20 20 20 20 30 28 10 ENDCHAR STARTCHAR U+0E43 ENCODING 3651 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 11 1 0 BITMAP 60 90 50 10 10 10 10 10 18 14 08 ENDCHAR STARTCHAR U+0E44 ENCODING 3652 SWIDTH 1000 0 DWIDTH 8 0 BBX 6 11 1 0 BITMAP B0 50 10 10 10 10 10 10 18 14 08 ENDCHAR STARTCHAR U+0E45 ENCODING 3653 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 10 2 -2 BITMAP 70 88 08 08 08 08 08 08 08 08 ENDCHAR STARTCHAR U+0E46 ENCODING 3654 SWIDTH 1000 0 DWIDTH 8 0 BBX 5 10 2 -2 BITMAP 50 A8 C8 A8 48 08 08 08 10 60 ENDCHAR STARTCHAR U+0E47 ENCODING 3655 SWIDTH 1000 0 DWIDTH 0 0 BBX 5 4 2 6 BITMAP 08 70 A8 50 ENDCHAR STARTCHAR U+0E48 ENCODING 3656 SWIDTH 1000 0 DWIDTH 0 0 BBX 1 3 6 6 BITMAP 80 80 80 ENDCHAR STARTCHAR U+0E49 ENCODING 3657 SWIDTH 1000 0 DWIDTH 0 0 BBX 4 3 3 6 BITMAP 90 50 E0 ENDCHAR STARTCHAR U+0E4A ENCODING 3658 SWIDTH 1000 0 DWIDTH 0 0 BBX 6 4 1 6 BITMAP 04 74 A8 50 ENDCHAR STARTCHAR U+0E4B ENCODING 3659 SWIDTH 1000 0 DWIDTH 0 0 BBX 3 3 3 6 BITMAP 40 E0 40 ENDCHAR STARTCHAR U+0E4C ENCODING 3660 SWIDTH 1000 0 DWIDTH 0 0 BBX 4 4 2 6 BITMAP 30 40 A0 40 ENDCHAR STARTCHAR U+0E4D ENCODING 3661 SWIDTH 1000 0 DWIDTH 0 0 BBX 3 3 3 6 BITMAP 40 A0 40 ENDCHAR STARTCHAR U+0E4E ENCODING 3662 SWIDTH 1000 0 DWIDTH 0 0 BBX 5 5 2 6 BITMAP 38 40 60 80 C0 ENDCHAR STARTCHAR U+0E4F ENCODING 3663 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 38 44 92 BA BA 92 44 38 ENDCHAR STARTCHAR U+0E50 ENCODING 3664 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 38 44 8A 9A B2 A2 44 38 ENDCHAR STARTCHAR U+0E51 ENCODING 3665 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 7 1 0 BITMAP 38 44 4A 4A 32 84 78 ENDCHAR STARTCHAR U+0E52 ENCODING 3666 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 8A 55 59 55 29 21 21 3F ENDCHAR STARTCHAR U+0E53 ENCODING 3667 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 8 1 0 BITMAP 6C 92 82 82 82 C2 A2 42 ENDCHAR STARTCHAR U+0E54 ENCODING 3668 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 0 BITMAP 02 04 38 40 88 94 88 88 44 3E ENDCHAR STARTCHAR U+0E55 ENCODING 3669 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 11 1 0 BITMAP 0A 14 08 30 40 88 94 88 88 44 3E ENDCHAR STARTCHAR U+0E56 ENCODING 3670 SWIDTH 1000 0 DWIDTH 8 0 BBX 8 9 0 0 BITMAP 80 5C 22 01 11 29 31 22 1C ENDCHAR STARTCHAR U+0E57 ENCODING 3671 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 0 BITMAP 08 04 52 AA 8A 8A 8A CA AC 48 ENDCHAR STARTCHAR U+0E58 ENCODING 3672 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 10 1 0 BITMAP 02 04 38 40 80 80 90 94 BA 4C ENDCHAR STARTCHAR U+0E59 ENCODING 3673 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 9 1 0 BITMAP 02 0A 54 A0 90 88 C4 A0 40 ENDCHAR STARTCHAR U+0E5A ENCODING 3674 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 7 1 0 BITMAP 5A AA 4A 0A 0A 0A 0A ENDCHAR STARTCHAR U+0E5B ENCODING 3675 SWIDTH 1000 0 DWIDTH 8 0 BBX 7 7 1 1 BITMAP 40 80 A8 AE B8 A0 40 ENDCHAR STARTCHAR U+203B ENCODING 8251 SWIDTH 960 0 DWIDTH 10 0 BBX 10 11 0 -2 BITMAP 0000 0800 4100 2200 1400 8880 1400 2200 4100 0800 0000 ENDCHAR STARTCHAR U+2639 ENCODING 9785 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 38 44 AA 82 92 AA 44 38 ENDCHAR STARTCHAR U+263A ENCODING 9786 SWIDTH 623 0 DWIDTH 8 0 BBX 8 8 0 0 BITMAP 38 44 AA 82 AA 92 44 38 ENDCHAR STARTCHAR U+300C ENCODING 12300 SWIDTH 960 0 DWIDTH 10 0 BBX 10 11 0 -2 BITMAP 0F00 0800 0800 0800 0800 0800 0800 0000 0000 0000 0000 ENDCHAR STARTCHAR U+300D ENCODING 12301 SWIDTH 960 0 DWIDTH 10 0 BBX 10 11 0 -2 BITMAP 0000 0000 0000 0000 0800 0800 0800 0800 0800 0800 7800 ENDCHAR STARTCHAR U+AC00 ENCODING 44032 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0270 0440 0440 0840 7040 0040 0040 ENDCHAR STARTCHAR U+AC01 ENCODING 44033 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AC04 ENCODING 44036 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AC07 ENCODING 44039 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+AC08 ENCODING 44040 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0470 1840 6040 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AC09 ENCODING 44041 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0470 1840 6040 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+AC0A ENCODING 44042 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0470 1840 6040 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+AC10 ENCODING 44048 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+AC11 ENCODING 44049 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+AC12 ENCODING 44050 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 2480 3C80 2540 1E20 ENDCHAR STARTCHAR U+AC13 ENCODING 44051 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+AC14 ENCODING 44052 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AC15 ENCODING 44053 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AC16 ENCODING 44054 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+AC17 ENCODING 44055 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0470 1840 6040 0040 0100 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+AC19 ENCODING 44057 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0470 1840 6040 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AC1A ENCODING 44058 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0440 1840 6040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+AC1B ENCODING 44059 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0470 1840 6040 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+AC1C ENCODING 44060 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 0520 0520 0520 09E0 0920 1120 6120 0120 0120 ENDCHAR STARTCHAR U+AC1D ENCODING 44061 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 0A00 0B80 0A00 3200 C200 0000 3F80 ENDCHAR STARTCHAR U+AC20 ENCODING 44064 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 0520 1920 6120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+AC24 ENCODING 44068 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 1920 6120 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+AC2C ENCODING 44076 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 0520 1920 6120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+AC2D ENCODING 44077 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 0520 1920 6120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+AC2F ENCODING 44079 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 0520 1920 6120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+AC30 ENCODING 44080 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 0520 1920 6120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+AC31 ENCODING 44081 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 0520 1920 6120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+AC38 ENCODING 44088 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 0240 0240 0440 0470 0840 7040 0040 0040 ENDCHAR STARTCHAR U+AC39 ENCODING 44089 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 0240 0470 1840 6040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AC3C ENCODING 44092 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 0240 0470 1840 6040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AC40 ENCODING 44096 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 0440 1870 6040 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AC4B ENCODING 44107 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 0240 0470 1840 6040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+AC4D ENCODING 44109 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 0240 0470 1840 6040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AC54 ENCODING 44116 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 0520 0520 0920 09E0 1120 6120 0120 0120 ENDCHAR STARTCHAR U+AC58 ENCODING 44120 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 0520 1920 6120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+AC5C ENCODING 44124 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 05E0 1920 6120 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+AC70 ENCODING 44144 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 03C0 0440 0440 0840 7040 0040 0040 ENDCHAR STARTCHAR U+AC71 ENCODING 44145 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AC74 ENCODING 44148 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AC77 ENCODING 44151 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+AC78 ENCODING 44152 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 05C0 1840 6040 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AC7A ENCODING 44154 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 05C0 1840 6040 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+AC80 ENCODING 44160 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+AC81 ENCODING 44161 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+AC83 ENCODING 44163 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+AC84 ENCODING 44164 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AC85 ENCODING 44165 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AC86 ENCODING 44166 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+AC89 ENCODING 44169 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 05C0 1840 6040 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AC8A ENCODING 44170 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0440 1840 6040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+AC8B ENCODING 44171 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 05C0 1840 6040 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+AC8C ENCODING 44172 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 09 09 09 0F 11 11 21 C1 01 01 ENDCHAR STARTCHAR U+AC90 ENCODING 44176 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 07A0 04A0 18A0 60A0 0000 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+AC94 ENCODING 44180 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 05A0 18A0 60A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+AC9C ENCODING 44188 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 07A0 04A0 18A0 60A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+AC9D ENCODING 44189 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 07A0 04A0 18A0 60A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+AC9F ENCODING 44191 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 07A0 04A0 18A0 60A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+ACA0 ENCODING 44192 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 07A0 04A0 18A0 60A0 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+ACA1 ENCODING 44193 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 07A0 04A0 18A0 60A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+ACA8 ENCODING 44200 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 0240 0240 05C0 0440 0840 7040 0040 0040 ENDCHAR STARTCHAR U+ACA9 ENCODING 44201 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+ACAA ENCODING 44202 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+ACAC ENCODING 44204 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+ACAF ENCODING 44207 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+ACB0 ENCODING 44208 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0440 19C0 6040 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+ACB8 ENCODING 44216 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+ACB9 ENCODING 44217 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+ACBB ENCODING 44219 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+ACBC ENCODING 44220 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+ACBD ENCODING 44221 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0240 05C0 1840 6040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+ACC1 ENCODING 44225 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 0440 19C0 6040 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+ACC4 ENCODING 44228 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 09 0F 09 09 17 11 21 C1 01 01 ENDCHAR STARTCHAR U+ACC8 ENCODING 44232 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 07A0 04A0 07A0 18A0 60A0 0000 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+ACCC ENCODING 44236 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 05A0 04A0 19A0 60A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+ACD5 ENCODING 44245 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 07A0 04A0 07A0 18A0 60A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+ACD7 ENCODING 44247 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 07A0 04A0 07A0 18A0 60A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+ACE0 ENCODING 44256 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 0040 0040 0840 0840 0840 0800 FFC0 ENDCHAR STARTCHAR U+ACE1 ENCODING 44257 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0840 0840 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+ACE4 ENCODING 44260 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0440 FFE0 0000 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+ACE7 ENCODING 44263 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0840 0840 FFE0 0000 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+ACE8 ENCODING 44264 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0840 0840 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+ACEA ENCODING 44266 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0840 0840 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+ACEC ENCODING 44268 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0840 0840 FFE0 0000 7900 0900 7A80 4440 3820 ENDCHAR STARTCHAR U+ACEF ENCODING 44271 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0840 0840 FFE0 0000 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+ACF0 ENCODING 44272 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0840 0840 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+ACF1 ENCODING 44273 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0840 0840 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+ACF3 ENCODING 44275 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0840 0840 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+ACF5 ENCODING 44277 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0840 0840 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+ACF6 ENCODING 44278 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0840 0840 FFE0 0000 7FC0 0400 0A00 71C0 ENDCHAR STARTCHAR U+ACFC ENCODING 44284 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0140 0140 0140 1170 1140 1140 1040 7F40 0040 0040 ENDCHAR STARTCHAR U+ACFD ENCODING 44285 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1270 1240 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AD00 ENCODING 44288 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0270 0A40 7F40 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AD04 ENCODING 44292 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 1240 1270 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AD06 ENCODING 44294 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 1240 1270 7FC0 0000 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+AD0C ENCODING 44300 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1270 1240 7F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+AD0D ENCODING 44301 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1270 1240 7F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+AD0F ENCODING 44303 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1270 1240 7F40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+AD11 ENCODING 44305 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1270 1240 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AD18 ENCODING 44312 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 02A0 02A0 02A0 22E0 22A0 22A0 20A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+AD1C ENCODING 44316 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 04E0 04A0 10A0 7EA0 00A0 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+AD20 ENCODING 44320 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 24E0 20A0 7EA0 0000 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+AD29 ENCODING 44329 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 04E0 24A0 20A0 7EA0 00A0 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+AD2C ENCODING 44332 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 04E0 24A0 20A0 7EA0 00A0 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AD2D ENCODING 44333 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 04E0 24A0 20A0 7EA0 00A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+AD34 ENCODING 44340 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0140 0140 0140 1140 1140 1140 1040 7F40 0040 0040 ENDCHAR STARTCHAR U+AD35 ENCODING 44341 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1240 1240 7F40 0040 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AD38 ENCODING 44344 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0A40 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AD3C ENCODING 44348 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 1240 1240 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AD44 ENCODING 44356 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1240 1240 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+AD45 ENCODING 44357 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1240 1240 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+AD47 ENCODING 44359 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1240 1240 7F40 0040 0200 0500 0880 3060 ENDCHAR STARTCHAR U+AD49 ENCODING 44361 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 1240 1240 7F40 0040 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AD50 ENCODING 44368 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 0040 2240 2240 2240 2240 2200 FFC0 ENDCHAR STARTCHAR U+AD54 ENCODING 44372 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 1140 FFE0 0000 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+AD58 ENCODING 44376 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 2240 2240 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+AD61 ENCODING 44385 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 2240 2240 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+AD63 ENCODING 44387 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 2240 2240 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+AD6C ENCODING 44396 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 0040 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+AD6D ENCODING 44397 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AD70 ENCODING 44400 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 0400 4400 4000 4000 3FC0 ENDCHAR STARTCHAR U+AD73 ENCODING 44403 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 0400 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+AD74 ENCODING 44404 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+AD75 ENCODING 44405 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 FFE0 0400 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+AD76 ENCODING 44406 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 FFE0 0400 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+AD7B ENCODING 44411 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 FFE0 0400 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+AD7C ENCODING 44412 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+AD7D ENCODING 44413 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+AD7F ENCODING 44415 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+AD81 ENCODING 44417 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+AD82 ENCODING 44418 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 0400 7FC0 0400 0A00 71C0 ENDCHAR STARTCHAR U+AD88 ENCODING 44424 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0240 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+AD89 ENCODING 44425 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 02C0 0240 0040 7F40 0840 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AD8C ENCODING 44428 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 02C0 0240 0040 7F40 0840 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+AD90 ENCODING 44432 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 02C0 0240 7F40 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AD9C ENCODING 44444 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 02C0 0240 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AD9D ENCODING 44445 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 02C0 0240 0040 7F40 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+ADA4 ENCODING 44452 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP FD 05 05 05 05 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+ADB7 ENCODING 44471 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 05A0 04A0 00A0 7EA0 10A0 0100 0100 0680 3860 ENDCHAR STARTCHAR U+ADC0 ENCODING 44480 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0240 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+ADC1 ENCODING 44481 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0040 7FC0 0840 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+ADC4 ENCODING 44484 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0040 7FC0 0840 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+ADC8 ENCODING 44488 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+ADD0 ENCODING 44496 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+ADD1 ENCODING 44497 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+ADD3 ENCODING 44499 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0040 7FC0 0840 0200 0500 0880 3060 ENDCHAR STARTCHAR U+ADDC ENCODING 44508 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 0040 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+ADE0 ENCODING 44512 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0000 FFE0 1100 5100 4000 4000 3FC0 ENDCHAR STARTCHAR U+ADE4 ENCODING 44516 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+ADF8 ENCODING 44536 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 0040 0040 0040 0040 0040 0000 FFC0 ENDCHAR STARTCHAR U+ADF9 ENCODING 44537 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+ADFC ENCODING 44540 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 FFE0 0000 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+ADFF ENCODING 44543 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 FFE0 0000 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+AE00 ENCODING 44544 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+AE01 ENCODING 44545 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 FFE0 0000 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+AE08 ENCODING 44552 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+AE09 ENCODING 44553 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+AE0B ENCODING 44555 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+AE0D ENCODING 44557 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 0040 0040 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+AE14 ENCODING 44564 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0140 0140 0140 0140 0140 0140 0040 7FC0 0040 0040 ENDCHAR STARTCHAR U+AE30 ENCODING 44592 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0240 0240 0440 0440 0840 7040 0040 0040 ENDCHAR STARTCHAR U+AE31 ENCODING 44593 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AE34 ENCODING 44596 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AE37 ENCODING 44599 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+AE38 ENCODING 44600 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0440 1840 6040 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AE3A ENCODING 44602 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0440 1840 6040 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+AE40 ENCODING 44608 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+AE41 ENCODING 44609 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+AE43 ENCODING 44611 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+AE45 ENCODING 44613 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AE46 ENCODING 44614 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+AE4A ENCODING 44618 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 0440 1840 6040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+AE4C ENCODING 44620 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 1140 1140 1170 1140 2140 2240 4240 0440 0040 ENDCHAR STARTCHAR U+AE4D ENCODING 44621 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 1240 2240 4440 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AE4E ENCODING 44622 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 1240 2240 4440 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+AE50 ENCODING 44624 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 1270 2240 4440 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AE54 ENCODING 44628 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 2240 4440 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AE56 ENCODING 44630 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 2240 4440 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+AE5C ENCODING 44636 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 1240 2240 4440 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+AE5D ENCODING 44637 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 1240 2240 4440 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+AE5F ENCODING 44639 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 1240 2240 4440 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+AE60 ENCODING 44640 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 1240 2240 4440 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AE61 ENCODING 44641 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 1240 2240 4440 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AE65 ENCODING 44645 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1270 2240 4440 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AE68 ENCODING 44648 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FD20 2520 2520 2520 2520 25E0 4520 4920 8920 1120 0120 ENDCHAR STARTCHAR U+AE69 ENCODING 44649 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FE00 2600 2780 4A00 9200 2200 0000 3F80 ENDCHAR STARTCHAR U+AE6C ENCODING 44652 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 1320 13E0 2520 4920 1120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+AE70 ENCODING 44656 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 1320 13E0 2520 4920 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+AE78 ENCODING 44664 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 1320 13E0 2520 4920 1120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+AE79 ENCODING 44665 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 1320 13E0 2520 4920 1120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+AE7B ENCODING 44667 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 1320 13E0 2520 4920 1120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+AE7C ENCODING 44668 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 1320 13E0 2520 4920 1120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+AE7D ENCODING 44669 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 1320 13E0 2520 4920 1120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+AE84 ENCODING 44676 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 1170 1140 1140 1140 2170 2240 4240 0440 0040 ENDCHAR STARTCHAR U+AE85 ENCODING 44677 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1270 1240 1270 2240 4440 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AE8C ENCODING 44684 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1270 1240 2270 4440 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AEBC ENCODING 44732 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 1140 11C0 1140 1140 2140 2240 4240 0440 0040 ENDCHAR STARTCHAR U+AEBD ENCODING 44733 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 1240 2240 4440 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AEBE ENCODING 44734 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 1240 2240 4440 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+AEC0 ENCODING 44736 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 1240 2240 4440 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AEC4 ENCODING 44740 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 2240 4440 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AECC ENCODING 44748 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 1240 2240 4440 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+AECD ENCODING 44749 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 1240 2240 4440 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+AECF ENCODING 44751 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 1240 2240 4440 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+AED0 ENCODING 44752 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 1240 2240 4440 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AED1 ENCODING 44753 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 13C0 1240 2240 4440 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AED8 ENCODING 44760 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FCA0 24A0 24A0 24A0 27A0 24A0 44A0 48A0 88A0 10A0 00A0 ENDCHAR STARTCHAR U+AED9 ENCODING 44761 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FD00 2500 2700 4900 9100 2100 0000 3F80 ENDCHAR STARTCHAR U+AEDC ENCODING 44764 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 12A0 13A0 24A0 48A0 10A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+AEE8 ENCODING 44776 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 12A0 13A0 24A0 48A0 10A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+AEEB ENCODING 44779 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 12A0 13A0 24A0 48A0 10A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+AEED ENCODING 44781 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 12A0 13A0 24A0 48A0 10A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+AEF4 ENCODING 44788 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 11C0 1140 1140 11C0 2140 2240 4240 0440 0040 ENDCHAR STARTCHAR U+AEF8 ENCODING 44792 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 13C0 1240 13C0 2240 4440 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+AEFC ENCODING 44796 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 13C0 1240 23C0 4440 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AF07 ENCODING 44807 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 13C0 1240 13C0 2240 4440 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+AF08 ENCODING 44808 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 13C0 1240 13C0 2240 4440 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AF0D ENCODING 44813 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 13C0 1240 23C0 4440 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AF10 ENCODING 44816 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FCA0 24A0 27A0 24A0 24A0 27A0 44A0 48A0 88A0 10A0 00A0 ENDCHAR STARTCHAR U+AF2C ENCODING 44844 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7BC0 0840 0840 0840 0840 1040 2040 4480 0400 FFC0 ENDCHAR STARTCHAR U+AF2D ENCODING 44845 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2440 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AF30 ENCODING 44848 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2040 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+AF32 ENCODING 44850 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2440 FFE0 0100 47E0 4240 4240 3980 ENDCHAR STARTCHAR U+AF34 ENCODING 44852 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 1040 2440 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+AF3C ENCODING 44860 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2440 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+AF3D ENCODING 44861 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2440 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+AF3F ENCODING 44863 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2440 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+AF41 ENCODING 44865 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2440 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+AF42 ENCODING 44866 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2440 FFE0 0000 7FC0 0400 0A00 71C0 ENDCHAR STARTCHAR U+AF43 ENCODING 44867 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 1040 2440 FFE0 0000 0200 7FC0 0400 1B00 60C0 ENDCHAR STARTCHAR U+AF48 ENCODING 44872 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 1140 1140 2170 4140 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+AF49 ENCODING 44873 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 4170 0840 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AF50 ENCODING 44880 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 0870 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AF5C ENCODING 44892 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 4170 0840 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AF5D ENCODING 44893 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 4170 0840 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AF64 ENCODING 44900 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 12A0 12A0 12A0 22E0 42A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+AF65 ENCODING 44901 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FD00 2500 4580 8500 2100 FD00 0100 3F80 ENDCHAR STARTCHAR U+AF79 ENCODING 44921 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 12A0 22E0 42A0 10A0 7EA0 00A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+AF80 ENCODING 44928 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 1140 1140 2140 4140 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+AF84 ENCODING 44932 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 4140 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+AF88 ENCODING 44936 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AF90 ENCODING 44944 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 4140 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+AF91 ENCODING 44945 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 4140 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+AF95 ENCODING 44949 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 2140 4140 0840 7F40 0040 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AF9C ENCODING 44956 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7BC0 0840 0840 0840 0840 1040 2040 5180 1100 FFC0 ENDCHAR STARTCHAR U+AFB8 ENCODING 44984 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0840 0840 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+AFB9 ENCODING 44985 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+AFBC ENCODING 44988 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+AFC0 ENCODING 44992 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+AFC7 ENCODING 44999 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 FFE0 0400 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+AFC8 ENCODING 45000 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+AFC9 ENCODING 45001 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+AFCB ENCODING 45003 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+AFCD ENCODING 45005 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+AFCE ENCODING 45006 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0000 FFE0 0400 7FC0 0400 0A00 71C0 ENDCHAR STARTCHAR U+AFD4 ENCODING 45012 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 1140 1140 1140 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+AFDC ENCODING 45020 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 12C0 0040 7F40 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+AFE8 ENCODING 45032 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 12C0 0040 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+AFE9 ENCODING 45033 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 12C0 0040 0040 7F40 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+AFF0 ENCODING 45040 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP FD 25 25 25 25 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+AFF1 ENCODING 45041 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP F900 2900 2B00 0100 0100 FD00 2100 1F80 ENDCHAR STARTCHAR U+AFF4 ENCODING 45044 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 14A0 15A0 00A0 00A0 7EA0 08A0 2800 2000 2000 1FE0 ENDCHAR STARTCHAR U+AFF8 ENCODING 45048 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 12A0 11A0 00A0 00A0 7EA0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B000 ENCODING 45056 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 14A0 15A0 00A0 00A0 7EA0 10A0 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B001 ENCODING 45057 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 14A0 15A0 00A0 00A0 7EA0 10A0 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B004 ENCODING 45060 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 14A0 15A0 00A0 00A0 7EA0 10A0 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B00C ENCODING 45068 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 1140 1140 1140 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+B010 ENCODING 45072 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 0040 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+B014 ENCODING 45076 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B01C ENCODING 45084 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 0040 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B01D ENCODING 45085 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 0040 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B028 ENCODING 45096 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 0840 0840 0840 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+B044 ENCODING 45124 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7BC0 0840 0840 0840 0840 1040 2040 4080 0000 FFC0 ENDCHAR STARTCHAR U+B045 ENCODING 45125 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2040 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B048 ENCODING 45128 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2040 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B04A ENCODING 45130 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2040 FFE0 0100 47E0 4240 4240 3980 ENDCHAR STARTCHAR U+B04C ENCODING 45132 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 1040 2040 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B04E ENCODING 45134 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 1040 2040 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+B053 ENCODING 45139 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 1040 2040 FFE0 0000 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+B054 ENCODING 45140 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2040 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B055 ENCODING 45141 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2040 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B057 ENCODING 45143 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2040 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B059 ENCODING 45145 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 0840 1040 2040 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B05D ENCODING 45149 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 0840 1040 2040 FFE0 0000 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+B07C ENCODING 45180 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 1140 1140 1140 1140 1140 2140 2240 4240 0440 0040 ENDCHAR STARTCHAR U+B07D ENCODING 45181 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 1240 2240 4440 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B080 ENCODING 45184 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 1240 2240 4440 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B084 ENCODING 45188 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 2240 4440 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B08C ENCODING 45196 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 1240 2240 4440 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B08D ENCODING 45197 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 1240 2240 4440 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B08F ENCODING 45199 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 1240 2240 4440 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B091 ENCODING 45201 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1240 1240 1240 2240 4440 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B098 ENCODING 45208 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 4070 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B099 ENCODING 45209 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B09A ENCODING 45210 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+B09C ENCODING 45212 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4070 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B09F ENCODING 45215 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+B0A0 ENCODING 45216 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B0A1 ENCODING 45217 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+B0A2 ENCODING 45218 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+B0A8 ENCODING 45224 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B0A9 ENCODING 45225 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B0AB ENCODING 45227 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B0AC ENCODING 45228 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B0AD ENCODING 45229 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B0AE ENCODING 45230 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+B0AF ENCODING 45231 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0100 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+B0B1 ENCODING 45233 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B0B3 ENCODING 45235 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 3F40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+B0B4 ENCODING 45236 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4120 4120 4120 4120 4120 41E0 4120 3D20 0120 0120 0120 ENDCHAR STARTCHAR U+B0B5 ENCODING 45237 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 8200 8200 8380 8200 7E00 0200 0000 3F80 ENDCHAR STARTCHAR U+B0B8 ENCODING 45240 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4120 4120 41E0 4120 3F20 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B0BC ENCODING 45244 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4120 4120 41E0 4120 3F20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B0C4 ENCODING 45252 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4120 4120 41E0 4120 3F20 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B0C5 ENCODING 45253 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4120 4120 41E0 4120 3F20 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B0C7 ENCODING 45255 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4120 4120 41E0 4120 3F20 0120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B0C8 ENCODING 45256 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4120 4120 41E0 4120 3F20 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+B0C9 ENCODING 45257 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4120 4120 41E0 4120 3F20 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B0D0 ENCODING 45264 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4070 4040 4040 4040 4070 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B0D1 ENCODING 45265 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4070 4040 4070 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B0D4 ENCODING 45268 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4070 4040 4070 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B0D8 ENCODING 45272 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4070 4040 4070 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B0E0 ENCODING 45280 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4070 4040 4070 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B0E5 ENCODING 45285 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4070 4040 4070 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B108 ENCODING 45320 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 41C0 4040 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B109 ENCODING 45321 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B10B ENCODING 45323 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0000 3C80 0480 0540 0220 ENDCHAR STARTCHAR U+B10C ENCODING 45324 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B110 ENCODING 45328 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B112 ENCODING 45330 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+B113 ENCODING 45331 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 3D40 0540 3DC0 2140 1FC0 ENDCHAR STARTCHAR U+B118 ENCODING 45336 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B119 ENCODING 45337 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B11B ENCODING 45339 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+B11C ENCODING 45340 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B11D ENCODING 45341 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B123 ENCODING 45347 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 3F40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+B124 ENCODING 45348 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 81 81 81 81 87 81 81 79 01 01 01 ENDCHAR STARTCHAR U+B125 ENCODING 45349 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 8100 8100 8700 8100 7D00 0100 0000 3F80 ENDCHAR STARTCHAR U+B128 ENCODING 45352 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 40A0 40A0 43A0 40A0 3EA0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B12C ENCODING 45356 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 40A0 40A0 43A0 40A0 3EA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B134 ENCODING 45364 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 40A0 40A0 43A0 40A0 3EA0 00A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B135 ENCODING 45365 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 40A0 40A0 43A0 40A0 3EA0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B137 ENCODING 45367 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 40A0 40A0 43A0 40A0 3EA0 00A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B138 ENCODING 45368 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 40A0 40A0 43A0 40A0 3EA0 00A0 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+B139 ENCODING 45369 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 40A0 40A0 43A0 40A0 3EA0 00A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B140 ENCODING 45376 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 41C0 4040 4040 41C0 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B141 ENCODING 45377 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 41C0 4040 41C0 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B144 ENCODING 45380 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 41C0 4040 41C0 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B148 ENCODING 45384 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 41C0 4040 41C0 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B150 ENCODING 45392 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 41C0 4040 41C0 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B151 ENCODING 45393 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 41C0 4040 41C0 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B154 ENCODING 45396 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 41C0 4040 41C0 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B155 ENCODING 45397 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 41C0 4040 41C0 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B158 ENCODING 45400 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 41C0 4040 41C0 3F40 0040 0000 3FC0 0040 3FC0 0040 ENDCHAR STARTCHAR U+B15C ENCODING 45404 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 81 81 87 81 81 87 81 79 01 01 01 ENDCHAR STARTCHAR U+B160 ENCODING 45408 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 40A0 43A0 40A0 43A0 3EA0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B178 ENCODING 45432 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4000 4000 4000 4000 4000 3FC0 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+B179 ENCODING 45433 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B17C ENCODING 45436 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B180 ENCODING 45440 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B182 ENCODING 45442 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 0400 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+B188 ENCODING 45448 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B189 ENCODING 45449 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B18B ENCODING 45451 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B18D ENCODING 45453 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B192 ENCODING 45458 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0400 FFE0 0000 7FC0 1100 1100 7FC0 ENDCHAR STARTCHAR U+B193 ENCODING 45459 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 0400 FFE0 0000 0E00 0000 7FC0 2080 1F00 ENDCHAR STARTCHAR U+B194 ENCODING 45460 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3E70 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+B198 ENCODING 45464 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3E70 0840 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B19C ENCODING 45468 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 3E40 0870 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B1A8 ENCODING 45480 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3E70 0840 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B1CC ENCODING 45516 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3E40 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+B1D0 ENCODING 45520 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3E40 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+B1D4 ENCODING 45524 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 3E40 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B1DC ENCODING 45532 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3E40 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B1DD ENCODING 45533 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3E40 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B1DF ENCODING 45535 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3E40 0840 7F40 0040 0200 0500 0880 3060 ENDCHAR STARTCHAR U+B1E8 ENCODING 45544 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4000 4000 4000 4000 4000 3FC0 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+B1E9 ENCODING 45545 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 1100 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B1EC ENCODING 45548 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 1100 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B1F0 ENCODING 45552 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 1100 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B1F9 ENCODING 45561 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 1100 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B1FB ENCODING 45563 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 1100 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B1FD ENCODING 45565 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 1100 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B204 ENCODING 45572 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+B205 ENCODING 45573 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B208 ENCODING 45576 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+B20B ENCODING 45579 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0400 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+B20C ENCODING 45580 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B214 ENCODING 45588 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B215 ENCODING 45589 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B217 ENCODING 45591 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B219 ENCODING 45593 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B220 ENCODING 45600 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3F40 0040 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+B234 ENCODING 45620 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 40C0 3C40 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B23C ENCODING 45628 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 81 81 81 79 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+B258 ENCODING 45656 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3F40 0040 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+B25C ENCODING 45660 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3C40 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+B260 ENCODING 45664 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 3C40 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B268 ENCODING 45672 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3C40 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B269 ENCODING 45673 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3C40 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B274 ENCODING 45684 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+B275 ENCODING 45685 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 1100 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B27C ENCODING 45692 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 0000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B284 ENCODING 45700 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B285 ENCODING 45701 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 1100 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B289 ENCODING 45705 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 1100 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B290 ENCODING 45712 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4000 4000 4000 4000 4000 3FC0 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+B291 ENCODING 45713 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B294 ENCODING 45716 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B298 ENCODING 45720 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B299 ENCODING 45721 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 0000 FFE0 0000 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+B29A ENCODING 45722 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 3FC0 0000 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+B2A0 ENCODING 45728 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B2A1 ENCODING 45729 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B2A3 ENCODING 45731 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B2A5 ENCODING 45733 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B2A6 ENCODING 45734 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0000 7FC0 0400 0A00 71C0 ENDCHAR STARTCHAR U+B2AA ENCODING 45738 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4000 4000 4000 3FC0 0000 FFE0 0000 7FC0 1100 1100 7FC0 ENDCHAR STARTCHAR U+B2AC ENCODING 45740 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3E40 0040 0040 0040 7FC0 0040 0040 ENDCHAR STARTCHAR U+B2B0 ENCODING 45744 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 3E40 0040 7FC0 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B2B4 ENCODING 45748 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 3E40 0040 7FC0 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B2C8 ENCODING 45768 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 4040 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B2C9 ENCODING 45769 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B2CC ENCODING 45772 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B2D0 ENCODING 45776 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B2D2 ENCODING 45778 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+B2D8 ENCODING 45784 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B2D9 ENCODING 45785 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B2DB ENCODING 45787 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B2DD ENCODING 45789 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B2E2 ENCODING 45794 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 4040 4040 4040 3F40 0040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+B2E4 ENCODING 45796 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 4070 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B2E5 ENCODING 45797 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B2E6 ENCODING 45798 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+B2E8 ENCODING 45800 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4070 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B2EB ENCODING 45803 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+B2EC ENCODING 45804 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B2ED ENCODING 45805 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+B2EE ENCODING 45806 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+B2EF ENCODING 45807 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 3A40 0A40 3BC0 2240 1FC0 ENDCHAR STARTCHAR U+B2F3 ENCODING 45811 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+B2F4 ENCODING 45812 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B2F5 ENCODING 45813 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B2F7 ENCODING 45815 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B2F8 ENCODING 45816 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B2F9 ENCODING 45817 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B2FA ENCODING 45818 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+B2FB ENCODING 45819 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0100 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+B2FF ENCODING 45823 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 3F40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+B300 ENCODING 45824 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7920 4120 4120 4120 4120 41E0 4120 3D20 0120 0120 0120 ENDCHAR STARTCHAR U+B301 ENCODING 45825 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 8200 8380 8200 7E00 0200 0000 3F80 ENDCHAR STARTCHAR U+B304 ENCODING 45828 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 41E0 4120 3F20 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B308 ENCODING 45832 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 41E0 4120 3F20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B310 ENCODING 45840 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 41E0 4120 3F20 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B311 ENCODING 45841 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 41E0 4120 3F20 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B313 ENCODING 45843 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 41E0 4120 3F20 0120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B314 ENCODING 45844 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 41E0 4120 3F20 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+B315 ENCODING 45845 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 41E0 4120 3F20 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B31C ENCODING 45852 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 4040 4040 4040 4070 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B354 ENCODING 45908 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 41C0 4040 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B355 ENCODING 45909 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B356 ENCODING 45910 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+B358 ENCODING 45912 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B35B ENCODING 45915 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+B35C ENCODING 45916 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B35E ENCODING 45918 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+B35F ENCODING 45919 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 3D40 0540 3DC0 2140 1FC0 ENDCHAR STARTCHAR U+B364 ENCODING 45924 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B365 ENCODING 45925 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B367 ENCODING 45927 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+B369 ENCODING 45929 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B36B ENCODING 45931 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0100 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+B36E ENCODING 45934 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 3F40 0040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+B370 ENCODING 45936 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F1 81 81 81 87 81 81 79 01 01 01 ENDCHAR STARTCHAR U+B371 ENCODING 45937 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP F900 8100 8700 8100 7D00 0100 0000 3F80 ENDCHAR STARTCHAR U+B374 ENCODING 45940 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 43A0 40A0 3EA0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B378 ENCODING 45944 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 43A0 40A0 3EA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B380 ENCODING 45952 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 43A0 40A0 3EA0 00A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B381 ENCODING 45953 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 43A0 40A0 3EA0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B383 ENCODING 45955 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 43A0 40A0 3EA0 00A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B384 ENCODING 45956 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 43A0 40A0 3EA0 00A0 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+B385 ENCODING 45957 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 43A0 40A0 3EA0 00A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B38C ENCODING 45964 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 4040 4040 41C0 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B390 ENCODING 45968 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 41C0 4040 41C0 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B394 ENCODING 45972 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 41C0 4040 41C0 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B3A0 ENCODING 45984 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 41C0 4040 41C0 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B3A1 ENCODING 45985 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 41C0 4040 41C0 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B3A8 ENCODING 45992 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F1 81 87 81 81 87 81 79 01 01 01 ENDCHAR STARTCHAR U+B3AC ENCODING 45996 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 43A0 40A0 43A0 3EA0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B3C4 ENCODING 46020 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4000 4000 4000 4000 3FC0 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+B3C5 ENCODING 46021 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B3C8 ENCODING 46024 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B3CB ENCODING 46027 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0400 FFE0 0000 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+B3CC ENCODING 46028 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B3CE ENCODING 46030 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0400 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+B3D0 ENCODING 46032 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0400 FFE0 0000 7900 0900 7A80 4440 3820 ENDCHAR STARTCHAR U+B3D4 ENCODING 46036 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B3D5 ENCODING 46037 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B3D7 ENCODING 46039 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B3D9 ENCODING 46041 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B3DB ENCODING 46043 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0400 FFE0 0000 0200 7FC0 0400 1B00 60C0 ENDCHAR STARTCHAR U+B3DD ENCODING 46045 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0400 FFE0 0000 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+B3E0 ENCODING 46048 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3E70 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+B3E4 ENCODING 46052 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 3E70 0840 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B3E8 ENCODING 46056 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 3E40 0870 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B3FC ENCODING 46076 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 40A0 40A0 40A0 3EE0 00A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+B410 ENCODING 46096 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 40E0 3CA0 10A0 7EA0 00A0 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B418 ENCODING 46104 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3E40 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+B41C ENCODING 46108 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 3E40 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+B420 ENCODING 46112 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 3E40 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B428 ENCODING 46120 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 3E40 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B429 ENCODING 46121 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 3E40 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B42B ENCODING 46123 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 3E40 0840 7F40 0040 0200 0500 0880 3060 ENDCHAR STARTCHAR U+B434 ENCODING 46132 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4000 4000 4000 4000 3FC0 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+B450 ENCODING 46160 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 4000 3FC0 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+B451 ENCODING 46161 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B454 ENCODING 46164 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+B458 ENCODING 46168 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B460 ENCODING 46176 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B461 ENCODING 46177 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B463 ENCODING 46179 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B465 ENCODING 46181 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B46C ENCODING 46188 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3E40 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+B480 ENCODING 46208 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 40C0 3C40 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B488 ENCODING 46216 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 81 81 79 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+B49D ENCODING 46237 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 41A0 3CA0 00A0 7EA0 10A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B4A4 ENCODING 46244 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3E40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+B4A8 ENCODING 46248 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 4040 3C40 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+B4AC ENCODING 46252 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 3C40 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B4B5 ENCODING 46261 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 4040 3C40 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B4B7 ENCODING 46263 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 4040 3C40 0040 7FC0 0840 0200 0500 0880 3060 ENDCHAR STARTCHAR U+B4B9 ENCODING 46265 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 4040 3C40 0040 7FC0 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B4C0 ENCODING 46272 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 4000 3FC0 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+B4C4 ENCODING 46276 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 0000 FFE0 5100 5100 4000 3FC0 ENDCHAR STARTCHAR U+B4C8 ENCODING 46280 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B4D0 ENCODING 46288 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B4D5 ENCODING 46293 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 1100 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B4DC ENCODING 46300 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4000 4000 4000 4000 3FC0 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+B4DD ENCODING 46301 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B4E0 ENCODING 46304 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B4E3 ENCODING 46307 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0000 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+B4E4 ENCODING 46308 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B4E6 ENCODING 46310 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 3FC0 0000 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+B4EC ENCODING 46316 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B4ED ENCODING 46317 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B4EF ENCODING 46319 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B4F1 ENCODING 46321 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 4000 3FC0 0000 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B4F8 ENCODING 46328 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3E40 0040 0040 0040 7FC0 0040 0040 ENDCHAR STARTCHAR U+B514 ENCODING 46356 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 4040 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B515 ENCODING 46357 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B518 ENCODING 46360 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B51B ENCODING 46363 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+B51C ENCODING 46364 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B524 ENCODING 46372 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B525 ENCODING 46373 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B527 ENCODING 46375 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B528 ENCODING 46376 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B529 ENCODING 46377 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B52A ENCODING 46378 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 4040 3F40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+B530 ENCODING 46384 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 4870 4840 4840 4840 3F40 0040 0040 ENDCHAR STARTCHAR U+B531 ENCODING 46385 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4870 4840 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B534 ENCODING 46388 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4870 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B538 ENCODING 46392 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4870 4840 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B540 ENCODING 46400 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4870 4840 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B541 ENCODING 46401 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4870 4840 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B543 ENCODING 46403 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4870 4840 3F40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B544 ENCODING 46404 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4870 4840 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B545 ENCODING 46405 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4870 4840 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B54B ENCODING 46411 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4870 4840 3F40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+B54C ENCODING 46412 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 5120 5120 5120 5120 51E0 5120 5120 3D20 0120 0120 ENDCHAR STARTCHAR U+B54D ENCODING 46413 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FE00 9200 9380 9200 7E00 0200 0000 3F80 ENDCHAR STARTCHAR U+B550 ENCODING 46416 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 4920 49E0 4920 3F20 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B554 ENCODING 46420 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 4920 49E0 4920 3F20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B55C ENCODING 46428 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 4920 49E0 4920 3F20 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B55D ENCODING 46429 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 4920 49E0 4920 3F20 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B55F ENCODING 46431 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 4920 49E0 4920 3F20 0120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B560 ENCODING 46432 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 4920 49E0 4920 3F20 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+B561 ENCODING 46433 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F20 4920 49E0 4920 3F20 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B5A0 ENCODING 46496 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 49C0 4840 4840 4840 4840 3F40 0040 0040 ENDCHAR STARTCHAR U+B5A1 ENCODING 46497 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B5A4 ENCODING 46500 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B5A8 ENCODING 46504 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B5AA ENCODING 46506 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+B5AB ENCODING 46507 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 3D40 0540 3DC0 2140 1FC0 ENDCHAR STARTCHAR U+B5B0 ENCODING 46512 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B5B1 ENCODING 46513 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B5B3 ENCODING 46515 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+B5B4 ENCODING 46516 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B5B5 ENCODING 46517 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B5BB ENCODING 46523 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 3F40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+B5BC ENCODING 46524 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 A1 A1 A1 A7 A1 A1 A1 79 01 01 ENDCHAR STARTCHAR U+B5BD ENCODING 46525 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FD00 9100 9700 9100 7D00 0100 0000 3F80 ENDCHAR STARTCHAR U+B5C0 ENCODING 46528 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 48A0 4BA0 48A0 3EA0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B5C4 ENCODING 46532 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 48A0 4BA0 48A0 3EA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B5CC ENCODING 46540 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 48A0 4BA0 48A0 3EA0 00A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B5CD ENCODING 46541 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 48A0 4BA0 48A0 3EA0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B5CF ENCODING 46543 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 48A0 4BA0 48A0 3EA0 00A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B5D0 ENCODING 46544 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 48A0 4BA0 48A0 3EA0 00A0 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+B5D1 ENCODING 46545 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 48A0 4BA0 48A0 3EA0 00A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B5D8 ENCODING 46552 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 49C0 4840 4840 49C0 4840 4840 3F40 0040 0040 ENDCHAR STARTCHAR U+B5EC ENCODING 46572 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 49C0 4840 49C0 3F40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B610 ENCODING 46608 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7BC0 4200 4200 4200 4200 39C0 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+B611 ENCODING 46609 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 3BC0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B614 ENCODING 46612 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B618 ENCODING 46616 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 3BC0 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B625 ENCODING 46629 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 3BC0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B62C ENCODING 46636 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F70 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+B634 ENCODING 46644 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 3E40 0870 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B648 ENCODING 46664 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 48A0 48A0 48A0 3EE0 00A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+B664 ENCODING 46692 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+B668 ENCODING 46696 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 4840 3E40 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+B69C ENCODING 46748 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 4200 39C0 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+B69D ENCODING 46749 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B6A0 ENCODING 46752 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+B6A4 ENCODING 46756 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 39C0 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B6AB ENCODING 46763 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 39C0 0000 FFE0 0400 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+B6AC ENCODING 46764 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B6B1 ENCODING 46769 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+B6D4 ENCODING 46804 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP FD 91 91 7D 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+B6F0 ENCODING 46832 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+B6F4 ENCODING 46836 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 4840 3E40 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+B6F8 ENCODING 46840 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 3E40 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B700 ENCODING 46848 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 4840 3E40 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B701 ENCODING 46849 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 4840 3E40 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B705 ENCODING 46853 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 4840 3E40 0040 7FC0 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B728 ENCODING 46888 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7BC0 4200 4200 4200 4200 39C0 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+B729 ENCODING 46889 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B72C ENCODING 46892 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B72F ENCODING 46895 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 FFE0 0000 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+B730 ENCODING 46896 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 39C0 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B738 ENCODING 46904 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+B739 ENCODING 46905 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B73B ENCODING 46907 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 4200 4200 39C0 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+B744 ENCODING 46916 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 0040 0040 7FC0 0040 0040 ENDCHAR STARTCHAR U+B748 ENCODING 46920 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 4840 3E40 0040 7FC0 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B74C ENCODING 46924 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 3E40 0040 7FC0 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B754 ENCODING 46932 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 4840 3E40 0040 7FC0 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B755 ENCODING 46933 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4840 4840 3E40 0040 7FC0 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B760 ENCODING 46944 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 4840 4840 4840 4840 3F40 0040 0040 ENDCHAR STARTCHAR U+B764 ENCODING 46948 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B768 ENCODING 46952 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B770 ENCODING 46960 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B771 ENCODING 46961 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B773 ENCODING 46963 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B775 ENCODING 46965 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 4840 4840 4840 3F40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B77C ENCODING 46972 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 7E40 4070 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B77D ENCODING 46973 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 4040 3F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B780 ENCODING 46976 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4070 4040 3F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+B784 ENCODING 46980 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B78C ENCODING 46988 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 4040 3F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B78D ENCODING 46989 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 4040 3F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B78F ENCODING 46991 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 4040 3F40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B790 ENCODING 46992 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 4040 3F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B791 ENCODING 46993 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B792 ENCODING 46994 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 4040 3F40 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+B796 ENCODING 46998 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 4040 3F40 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+B797 ENCODING 46999 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 4040 3F40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+B798 ENCODING 47000 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 0520 7D20 4120 41E0 4120 3D20 0120 0120 0120 ENDCHAR STARTCHAR U+B799 ENCODING 47001 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 0A00 FB80 8200 8200 7A00 0000 3F80 ENDCHAR STARTCHAR U+B79C ENCODING 47004 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 4120 4120 3D20 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B7A0 ENCODING 47008 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 4120 3D20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B7A8 ENCODING 47016 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 4120 4120 3D20 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B7A9 ENCODING 47017 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 4120 4120 3D20 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B7AB ENCODING 47019 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 4120 4120 3D20 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B7AC ENCODING 47020 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 4120 4120 3D20 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+B7AD ENCODING 47021 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 4120 4120 3D20 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B7B4 ENCODING 47028 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 7E40 4040 4040 4070 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B7B5 ENCODING 47029 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 7E40 4070 4040 3F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B7B8 ENCODING 47032 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 7E40 4070 4040 3F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B7C7 ENCODING 47047 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 7E40 4070 4040 3F40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B7C9 ENCODING 47049 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 7E40 4070 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B7EC ENCODING 47084 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 7FC0 4040 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B7ED ENCODING 47085 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 4040 3F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B7F0 ENCODING 47088 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 4040 3F40 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B7F4 ENCODING 47092 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B7FC ENCODING 47100 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 4040 3F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B7FD ENCODING 47101 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 4040 3F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B7FF ENCODING 47103 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 4040 3F40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+B800 ENCODING 47104 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 4040 3F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B801 ENCODING 47105 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B807 ENCODING 47111 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 4040 3F40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+B808 ENCODING 47112 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 09 09 F9 87 81 81 79 01 01 01 ENDCHAR STARTCHAR U+B809 ENCODING 47113 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP F900 0900 FF00 8100 8100 7900 0000 3F80 ENDCHAR STARTCHAR U+B80C ENCODING 47116 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 40A0 40A0 3CA0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B810 ENCODING 47120 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 40A0 3CA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B818 ENCODING 47128 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 40A0 40A0 3CA0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B819 ENCODING 47129 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 40A0 40A0 3CA0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B81B ENCODING 47131 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 40A0 40A0 3CA0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B81D ENCODING 47133 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 40A0 40A0 3CA0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B824 ENCODING 47140 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 7E40 4040 41C0 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B825 ENCODING 47141 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 41C0 4040 3F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B828 ENCODING 47144 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 41C0 4040 3F40 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B82C ENCODING 47148 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 41C0 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B834 ENCODING 47156 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 41C0 4040 3F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B835 ENCODING 47157 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 41C0 4040 3F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B837 ENCODING 47159 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 41C0 4040 3F40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+B838 ENCODING 47160 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 41C0 4040 3F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+B839 ENCODING 47161 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 41C0 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B840 ENCODING 47168 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 09 0F F9 81 87 81 79 01 01 01 ENDCHAR STARTCHAR U+B844 ENCODING 47172 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 07A0 7CA0 43A0 40A0 3CA0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B851 ENCODING 47185 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 07A0 7CA0 43A0 40A0 3CA0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B853 ENCODING 47187 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 07A0 7CA0 43A0 40A0 3CA0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B85C ENCODING 47196 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 7FC0 4000 4000 3FC0 0400 0400 FFC0 ENDCHAR STARTCHAR U+B85D ENCODING 47197 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0400 FFE0 0000 7FC0 0040 0040 ENDCHAR STARTCHAR U+B860 ENCODING 47200 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0400 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B864 ENCODING 47204 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0400 FFE0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B86C ENCODING 47212 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0400 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B86D ENCODING 47213 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0400 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B86F ENCODING 47215 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0400 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+B871 ENCODING 47217 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0400 FFE0 0000 3F80 4040 3F80 ENDCHAR STARTCHAR U+B878 ENCODING 47224 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 4070 3F40 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+B87C ENCODING 47228 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4070 3E40 0840 7F40 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+B88D ENCODING 47245 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4070 3E40 0840 7F40 0000 1F80 2040 1F80 ENDCHAR STARTCHAR U+B8A8 ENCODING 47272 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7CE0 40A0 3CA0 10A0 7EA0 0000 0880 1540 2220 ENDCHAR STARTCHAR U+B8B0 ENCODING 47280 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 4040 3F40 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+B8B4 ENCODING 47284 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3E40 0840 7F40 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+B8B8 ENCODING 47288 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3E40 0840 7FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B8C0 ENCODING 47296 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3E40 0840 7F40 0000 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B8C1 ENCODING 47297 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3E40 0840 7F40 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B8C3 ENCODING 47299 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3E40 0840 7F40 0000 0100 0680 3860 ENDCHAR STARTCHAR U+B8C5 ENCODING 47301 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3E40 0840 7F40 0000 1F80 2040 1F80 ENDCHAR STARTCHAR U+B8CC ENCODING 47308 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 7FC0 4000 4000 3FC0 1100 1100 FFC0 ENDCHAR STARTCHAR U+B8D0 ENCODING 47312 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 1100 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B8D4 ENCODING 47316 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 1100 FFE0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B8DD ENCODING 47325 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 1100 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B8DF ENCODING 47327 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 1100 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+B8E1 ENCODING 47329 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 1100 FFE0 0000 3F80 4040 3F80 ENDCHAR STARTCHAR U+B8E8 ENCODING 47336 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+B8E9 ENCODING 47337 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0400 7FC0 0040 0040 ENDCHAR STARTCHAR U+B8EC ENCODING 47340 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0400 4400 4000 3FC0 ENDCHAR STARTCHAR U+B8F0 ENCODING 47344 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B8F8 ENCODING 47352 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0400 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B8F9 ENCODING 47353 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 4440 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B8FB ENCODING 47355 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0400 0200 0D00 70C0 ENDCHAR STARTCHAR U+B8FD ENCODING 47357 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0400 3F80 4040 3F80 ENDCHAR STARTCHAR U+B904 ENCODING 47364 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3E40 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+B918 ENCODING 47384 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7CC0 4040 3C40 0040 7F40 0840 0880 1540 2220 ENDCHAR STARTCHAR U+B920 ENCODING 47392 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 09 F9 81 79 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+B93C ENCODING 47420 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3E40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+B93D ENCODING 47421 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 4040 3C40 0040 7FC0 0840 3FC0 0040 0040 ENDCHAR STARTCHAR U+B940 ENCODING 47424 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 4040 3C40 0040 7FC0 0840 2840 2000 1FC0 ENDCHAR STARTCHAR U+B944 ENCODING 47428 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 4040 3FC0 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B94C ENCODING 47436 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 4040 3C40 0040 7FC0 0840 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B94F ENCODING 47439 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 4040 3C40 0040 7FC0 0840 0100 0680 3860 ENDCHAR STARTCHAR U+B951 ENCODING 47441 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 4040 3C40 0040 7FC0 0840 1F80 2040 1F80 ENDCHAR STARTCHAR U+B958 ENCODING 47448 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+B959 ENCODING 47449 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 1100 7FC0 0040 0040 ENDCHAR STARTCHAR U+B95C ENCODING 47452 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 1100 5100 4000 3FC0 ENDCHAR STARTCHAR U+B960 ENCODING 47456 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B968 ENCODING 47464 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 1100 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B969 ENCODING 47465 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 5140 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B96B ENCODING 47467 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 1100 0200 0D00 70C0 ENDCHAR STARTCHAR U+B96D ENCODING 47469 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 1100 3F80 4040 3F80 ENDCHAR STARTCHAR U+B974 ENCODING 47476 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 7FC0 4000 4000 3FC0 0000 0000 FFC0 ENDCHAR STARTCHAR U+B975 ENCODING 47477 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0000 7FC0 0040 0040 ENDCHAR STARTCHAR U+B978 ENCODING 47480 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+B97C ENCODING 47484 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+B984 ENCODING 47492 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B985 ENCODING 47493 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+B987 ENCODING 47495 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+B989 ENCODING 47497 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0000 3F80 4040 3F80 ENDCHAR STARTCHAR U+B98A ENCODING 47498 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0000 7FC0 0A00 71C0 ENDCHAR STARTCHAR U+B98D ENCODING 47501 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 FFE0 0000 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+B98E ENCODING 47502 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 4000 3FC0 0000 FFE0 0000 7FC0 1100 7FC0 ENDCHAR STARTCHAR U+B9AC ENCODING 47532 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 7E40 4040 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+B9AD ENCODING 47533 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 4040 3F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B9B0 ENCODING 47536 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 4040 3F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B9B4 ENCODING 47540 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B9BC ENCODING 47548 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 4040 3F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B9BD ENCODING 47549 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 4040 3F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B9BF ENCODING 47551 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 4040 3F40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B9C1 ENCODING 47553 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 4040 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B9C8 ENCODING 47560 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 4270 4240 4240 4240 3E40 0040 0040 ENDCHAR STARTCHAR U+B9C9 ENCODING 47561 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+B9CC ENCODING 47564 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4270 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+B9CE ENCODING 47566 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0100 27E0 2240 2240 1D80 ENDCHAR STARTCHAR U+B9CF ENCODING 47567 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+B9D0 ENCODING 47568 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B9D1 ENCODING 47569 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+B9D2 ENCODING 47570 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+B9D8 ENCODING 47576 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+B9D9 ENCODING 47577 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+B9DB ENCODING 47579 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+B9DD ENCODING 47581 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+B9DE ENCODING 47582 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+B9E1 ENCODING 47585 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+B9E3 ENCODING 47587 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 3E40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+B9E4 ENCODING 47588 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 4520 4520 4520 45E0 4520 4520 3D20 0120 0120 ENDCHAR STARTCHAR U+B9E5 ENCODING 47589 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 8A00 8B80 8A00 7A00 0200 0000 3F80 ENDCHAR STARTCHAR U+B9E8 ENCODING 47592 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 45E0 4520 3D20 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+B9EC ENCODING 47596 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 45E0 4520 3D20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+B9F4 ENCODING 47604 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 45E0 4520 3D20 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+B9F5 ENCODING 47605 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 45E0 4520 3D20 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+B9F7 ENCODING 47607 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 45E0 4520 3D20 0120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+B9F8 ENCODING 47608 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 45E0 4520 3D20 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+B9F9 ENCODING 47609 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 45E0 4520 3D20 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+B9FA ENCODING 47610 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4520 45E0 4520 3D20 0120 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+BA00 ENCODING 47616 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4270 4240 4240 4240 4270 4240 3E40 0040 0040 ENDCHAR STARTCHAR U+BA01 ENCODING 47617 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4270 4240 4270 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BA08 ENCODING 47624 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4270 4240 4270 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BA15 ENCODING 47637 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4270 4240 4270 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BA38 ENCODING 47672 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 43C0 4240 4240 4240 4240 3E40 0040 0040 ENDCHAR STARTCHAR U+BA39 ENCODING 47673 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BA3C ENCODING 47676 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BA40 ENCODING 47680 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BA42 ENCODING 47682 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+BA48 ENCODING 47688 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BA49 ENCODING 47689 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BA4B ENCODING 47691 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+BA4D ENCODING 47693 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BA4E ENCODING 47694 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+BA53 ENCODING 47699 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 3E40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+BA54 ENCODING 47700 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 89 89 89 8F 89 89 89 79 01 01 ENDCHAR STARTCHAR U+BA55 ENCODING 47701 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP F900 8900 8F00 8900 7900 0100 0000 3F80 ENDCHAR STARTCHAR U+BA58 ENCODING 47704 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 44A0 47A0 44A0 3CA0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+BA5C ENCODING 47708 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 44A0 47A0 44A0 3CA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+BA64 ENCODING 47716 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 44A0 47A0 44A0 3CA0 00A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+BA65 ENCODING 47717 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 44A0 47A0 44A0 3CA0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+BA67 ENCODING 47719 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 44A0 47A0 44A0 3CA0 00A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+BA68 ENCODING 47720 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 44A0 47A0 44A0 3CA0 00A0 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+BA69 ENCODING 47721 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 44A0 47A0 44A0 3CA0 00A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+BA70 ENCODING 47728 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 43C0 4240 4240 43C0 4240 4240 3E40 0040 0040 ENDCHAR STARTCHAR U+BA71 ENCODING 47729 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 43C0 4240 43C0 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BA74 ENCODING 47732 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 43C0 4240 43C0 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BA78 ENCODING 47736 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 43C0 4240 43C0 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BA83 ENCODING 47747 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 43C0 4240 43C0 3E40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+BA84 ENCODING 47748 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 43C0 4240 43C0 3E40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BA85 ENCODING 47749 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 43C0 4240 43C0 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BA87 ENCODING 47751 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 43C0 4240 43C0 3E40 0040 0100 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+BA8C ENCODING 47756 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 89 8F 89 89 8F 89 89 79 01 01 ENDCHAR STARTCHAR U+BAA8 ENCODING 47784 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4040 4040 4040 4040 3FC0 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+BAA9 ENCODING 47785 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BAAB ENCODING 47787 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0400 FFE0 0000 7880 0880 0940 0A20 ENDCHAR STARTCHAR U+BAAC ENCODING 47788 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+BAB0 ENCODING 47792 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BAB2 ENCODING 47794 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0400 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+BAB8 ENCODING 47800 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BAB9 ENCODING 47801 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+BABB ENCODING 47803 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BABD ENCODING 47805 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+BAC4 ENCODING 47812 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E70 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+BAC8 ENCODING 47816 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 3E70 0840 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BAD8 ENCODING 47832 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 3E70 0840 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BAD9 ENCODING 47833 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 3E70 0840 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BAFC ENCODING 47868 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+BB00 ENCODING 47872 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 3E40 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+BB04 ENCODING 47876 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 3E40 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BB0D ENCODING 47885 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 3E40 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BB0F ENCODING 47887 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 3E40 0840 7F40 0040 0200 0500 0880 3060 ENDCHAR STARTCHAR U+BB11 ENCODING 47889 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 3E40 0840 7F40 0040 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BB18 ENCODING 47896 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4040 4040 4040 4040 3FC0 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+BB1C ENCODING 47900 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 1100 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+BB20 ENCODING 47904 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 1100 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BB29 ENCODING 47913 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 1100 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+BB2B ENCODING 47915 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 1100 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BB34 ENCODING 47924 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 4040 3FC0 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+BB35 ENCODING 47925 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BB36 ENCODING 47926 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0400 7FC0 0440 0440 0440 ENDCHAR STARTCHAR U+BB38 ENCODING 47928 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+BB3B ENCODING 47931 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0400 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+BB3C ENCODING 47932 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BB3D ENCODING 47933 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0000 FFE0 0400 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+BB3E ENCODING 47934 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0000 FFE0 0400 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+BB44 ENCODING 47940 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BB45 ENCODING 47941 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+BB47 ENCODING 47943 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BB49 ENCODING 47945 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+BB4D ENCODING 47949 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0000 FFE0 0400 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+BB4F ENCODING 47951 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0000 FFE0 0400 0E00 0000 FFE0 2080 1F00 ENDCHAR STARTCHAR U+BB50 ENCODING 47952 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+BB54 ENCODING 47956 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4440 44C0 3C40 0040 7F40 0840 2800 2000 2000 1FC0 ENDCHAR STARTCHAR U+BB58 ENCODING 47960 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4440 3CC0 0040 7F40 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BB61 ENCODING 47969 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4440 44C0 3C40 0040 7F40 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BB63 ENCODING 47971 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4440 44C0 3C40 0040 7F40 0840 0200 0500 0880 3060 ENDCHAR STARTCHAR U+BB6C ENCODING 47980 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 89 89 79 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+BB88 ENCODING 48008 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+BB8C ENCODING 48012 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4440 4440 3C40 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+BB90 ENCODING 48016 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4440 3C40 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BBA4 ENCODING 48036 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 4040 3FC0 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+BBA8 ENCODING 48040 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 0000 FFE0 5100 5100 4000 3FC0 ENDCHAR STARTCHAR U+BBAC ENCODING 48044 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BBB4 ENCODING 48052 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BBB7 ENCODING 48055 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 1100 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BBC0 ENCODING 48064 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4040 4040 4040 4040 3FC0 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+BBC4 ENCODING 48068 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+BBC8 ENCODING 48072 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 3FC0 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BBD0 ENCODING 48080 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BBD3 ENCODING 48083 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4040 4040 3FC0 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BBF8 ENCODING 48120 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 4240 4240 4240 4240 3E40 0040 0040 ENDCHAR STARTCHAR U+BBF9 ENCODING 48121 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BBFC ENCODING 48124 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BBFF ENCODING 48127 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+BC00 ENCODING 48128 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BC02 ENCODING 48130 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+BC08 ENCODING 48136 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BC09 ENCODING 48137 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BC0B ENCODING 48139 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+BC0C ENCODING 48140 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BC0D ENCODING 48141 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BC0F ENCODING 48143 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 0100 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+BC11 ENCODING 48145 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4240 4240 4240 3E40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BC14 ENCODING 48148 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 4240 7E40 4270 4240 4240 3E40 0040 0040 0040 ENDCHAR STARTCHAR U+BC15 ENCODING 48149 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BC16 ENCODING 48150 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+BC17 ENCODING 48151 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 0000 3C80 0480 0540 0220 ENDCHAR STARTCHAR U+BC18 ENCODING 48152 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4270 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BC1B ENCODING 48155 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+BC1C ENCODING 48156 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BC1D ENCODING 48157 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+BC1E ENCODING 48158 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+BC1F ENCODING 48159 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 3A40 0A40 3BC0 2240 1FC0 ENDCHAR STARTCHAR U+BC24 ENCODING 48164 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BC25 ENCODING 48165 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BC27 ENCODING 48167 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+BC29 ENCODING 48169 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BC2D ENCODING 48173 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E70 4240 3E40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BC30 ENCODING 48176 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 4520 7D20 4520 45E0 4520 3D20 0120 0120 0120 ENDCHAR STARTCHAR U+BC31 ENCODING 48177 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 8A00 8A00 FB80 8A00 7A00 0200 0000 3F80 ENDCHAR STARTCHAR U+BC34 ENCODING 48180 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 7DE0 4520 3D20 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+BC38 ENCODING 48184 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 7DE0 4520 3D20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+BC40 ENCODING 48192 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 7DE0 4520 3D20 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+BC41 ENCODING 48193 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 7DE0 4520 3D20 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+BC43 ENCODING 48195 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 7DE0 4520 3D20 0120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+BC44 ENCODING 48196 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 7DE0 4520 3D20 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+BC45 ENCODING 48197 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 7DE0 4520 3D20 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+BC49 ENCODING 48201 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4520 4520 7DE0 4520 3D20 0120 3FE0 2000 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+BC4C ENCODING 48204 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 4270 7E40 4240 4240 4270 3E40 0040 0040 0040 ENDCHAR STARTCHAR U+BC4D ENCODING 48205 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4270 7E40 4270 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BC50 ENCODING 48208 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4270 7E40 4270 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BC5D ENCODING 48221 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4270 7E40 4270 3E40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BC84 ENCODING 48260 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 4240 7FC0 4240 4240 4240 3E40 0040 0040 0040 ENDCHAR STARTCHAR U+BC85 ENCODING 48261 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BC88 ENCODING 48264 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BC8B ENCODING 48267 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+BC8C ENCODING 48268 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BC8E ENCODING 48270 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+BC94 ENCODING 48276 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BC95 ENCODING 48277 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BC97 ENCODING 48279 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+BC99 ENCODING 48281 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BC9A ENCODING 48282 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7FC0 4240 3E40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+BCA0 ENCODING 48288 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 89 89 89 F9 8F 89 89 79 01 01 01 ENDCHAR STARTCHAR U+BCA1 ENCODING 48289 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 8900 8900 FF00 8900 7900 0100 0000 3F80 ENDCHAR STARTCHAR U+BCA4 ENCODING 48292 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 44A0 7FA0 44A0 3CA0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+BCA7 ENCODING 48295 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 44A0 7FA0 44A0 3CA0 00A0 0000 3FE0 2000 2000 1FE0 ENDCHAR STARTCHAR U+BCA8 ENCODING 48296 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 44A0 7FA0 44A0 3CA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+BCB0 ENCODING 48304 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 44A0 7FA0 44A0 3CA0 00A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+BCB1 ENCODING 48305 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 44A0 7FA0 44A0 3CA0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+BCB3 ENCODING 48307 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 44A0 7FA0 44A0 3CA0 00A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+BCB4 ENCODING 48308 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 44A0 7FA0 44A0 3CA0 00A0 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+BCB5 ENCODING 48309 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 44A0 7FA0 44A0 3CA0 00A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+BCBC ENCODING 48316 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 43C0 7E40 4240 43C0 4240 3E40 0040 0040 0040 ENDCHAR STARTCHAR U+BCBD ENCODING 48317 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 43C0 7E40 43C0 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BCC0 ENCODING 48320 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 43C0 7E40 43C0 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BCC4 ENCODING 48324 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 43C0 7E40 43C0 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BCCD ENCODING 48333 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 43C0 7E40 43C0 3E40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BCCF ENCODING 48335 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 43C0 7E40 43C0 3E40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+BCD0 ENCODING 48336 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 43C0 7E40 43C0 3E40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BCD1 ENCODING 48337 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 43C0 7E40 43C0 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BCD5 ENCODING 48341 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 43C0 7E40 43C0 3E40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BCD8 ENCODING 48344 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 89 89 8F F9 89 8F 89 79 01 01 01 ENDCHAR STARTCHAR U+BCDC ENCODING 48348 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 47A0 7CA0 47A0 3CA0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+BCF4 ENCODING 48372 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4040 4040 7FC0 4040 4040 3FC0 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+BCF5 ENCODING 48373 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BCF6 ENCODING 48374 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0400 FFE0 0000 7FC0 0440 0440 0440 ENDCHAR STARTCHAR U+BCF8 ENCODING 48376 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+BCFC ENCODING 48380 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 3F80 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BD04 ENCODING 48388 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BD05 ENCODING 48389 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+BD07 ENCODING 48391 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BD09 ENCODING 48393 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+BD10 ENCODING 48400 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E70 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+BD14 ENCODING 48404 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 4240 3E70 0840 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BD24 ENCODING 48420 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 4240 3E70 0840 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BD2C ENCODING 48428 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 42A0 42A0 7EA0 42A0 3EE0 00A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+BD40 ENCODING 48448 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 44A0 7CA0 44E0 3CA0 10A0 7EA0 00A0 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BD48 ENCODING 48456 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+BD49 ENCODING 48457 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 4240 3E40 0840 7F40 0040 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BD4C ENCODING 48460 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 4240 3E40 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+BD50 ENCODING 48464 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 3C40 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BD58 ENCODING 48472 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 4240 3E40 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BD59 ENCODING 48473 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 4240 3E40 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BD64 ENCODING 48484 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4040 4040 7FC0 4040 4040 3FC0 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+BD68 ENCODING 48488 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 1100 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+BD80 ENCODING 48512 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 4040 3FC0 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+BD81 ENCODING 48513 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BD84 ENCODING 48516 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+BD87 ENCODING 48519 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0400 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+BD88 ENCODING 48520 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3F80 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BD89 ENCODING 48521 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3F80 FFE0 0400 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+BD8A ENCODING 48522 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3F80 FFE0 0400 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+BD90 ENCODING 48528 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BD91 ENCODING 48529 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+BD93 ENCODING 48531 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BD95 ENCODING 48533 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+BD99 ENCODING 48537 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3F80 FFE0 0400 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+BD9A ENCODING 48538 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0400 7FC0 1100 1100 7FC0 ENDCHAR STARTCHAR U+BD9C ENCODING 48540 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 4240 4240 3E40 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+BDA4 ENCODING 48548 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4440 7C40 44C0 3840 7F40 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BDB0 ENCODING 48560 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4440 7C40 44C0 3C40 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BDB8 ENCODING 48568 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 89 F9 89 79 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+BDD4 ENCODING 48596 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 7E40 4240 4240 3E40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+BDD5 ENCODING 48597 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4440 7C40 4440 3C40 0040 7FC0 0840 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BDD8 ENCODING 48600 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4440 7C40 4440 3C40 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+BDDC ENCODING 48604 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4440 7C40 4440 3840 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BDE9 ENCODING 48617 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4440 7C40 4440 3C40 0040 7FC0 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BDF0 ENCODING 48624 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 4040 3FC0 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+BDF4 ENCODING 48628 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 0000 FFE0 5100 5100 4000 3FC0 ENDCHAR STARTCHAR U+BDF8 ENCODING 48632 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3F80 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BE00 ENCODING 48640 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BE03 ENCODING 48643 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 1100 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BE05 ENCODING 48645 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 1100 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+BE0C ENCODING 48652 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4040 4040 7FC0 4040 4040 3FC0 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+BE0D ENCODING 48653 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BE10 ENCODING 48656 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+BE14 ENCODING 48660 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 3F80 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BE1C ENCODING 48668 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BE1D ENCODING 48669 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+BE1F ENCODING 48671 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4040 7FC0 4040 3FC0 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BE44 ENCODING 48708 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 4240 7E40 4240 4240 4240 3E40 0040 0040 0040 ENDCHAR STARTCHAR U+BE45 ENCODING 48709 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BE48 ENCODING 48712 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BE4C ENCODING 48716 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BE4E ENCODING 48718 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+BE54 ENCODING 48724 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BE55 ENCODING 48725 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BE57 ENCODING 48727 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+BE59 ENCODING 48729 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BE5A ENCODING 48730 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+BE5B ENCODING 48731 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4240 4240 7E40 4240 3E40 0040 0100 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+BE60 ENCODING 48736 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5570 5540 5540 3340 0040 0040 0040 ENDCHAR STARTCHAR U+BE61 ENCODING 48737 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 5540 3340 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BE64 ENCODING 48740 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5570 5540 3340 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BE68 ENCODING 48744 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 3340 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BE6A ENCODING 48746 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 3340 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+BE70 ENCODING 48752 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 5540 3340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BE71 ENCODING 48753 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 5540 3340 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BE73 ENCODING 48755 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 5540 3340 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+BE74 ENCODING 48756 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 5540 3340 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BE75 ENCODING 48757 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 5540 3340 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BE7B ENCODING 48763 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 3340 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+BE7C ENCODING 48764 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP A920 A920 F920 A920 A920 A9E0 A920 F920 0120 0120 0120 ENDCHAR STARTCHAR U+BE7D ENCODING 48765 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP AA00 AA00 FB80 AA00 7A00 0200 0000 3F80 ENDCHAR STARTCHAR U+BE80 ENCODING 48768 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5520 5520 7DE0 5520 3D20 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+BE84 ENCODING 48772 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5520 5520 7DE0 5520 3D20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+BE8C ENCODING 48780 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5520 5520 7DE0 5520 3D20 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+BE8D ENCODING 48781 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5520 5520 7DE0 5520 3D20 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+BE8F ENCODING 48783 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5520 5520 7DE0 5520 3D20 0120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+BE90 ENCODING 48784 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5520 5520 7DE0 5520 3D20 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+BE91 ENCODING 48785 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5520 5520 7DE0 5520 3D20 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+BE98 ENCODING 48792 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7770 5540 5540 5540 5570 3340 0040 0040 0040 ENDCHAR STARTCHAR U+BE99 ENCODING 48793 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5570 7740 5570 5540 3340 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BEA8 ENCODING 48808 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5570 7740 5570 5540 3340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BED0 ENCODING 48848 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 55C0 5540 5540 5540 3340 0040 0040 0040 ENDCHAR STARTCHAR U+BED1 ENCODING 48849 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 5540 3340 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BED4 ENCODING 48852 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 5540 3340 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+BED7 ENCODING 48855 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 5540 3340 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+BED8 ENCODING 48856 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 3340 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+BEE0 ENCODING 48864 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 5540 3340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BEE3 ENCODING 48867 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 5540 3340 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+BEE4 ENCODING 48868 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 5540 3340 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BEE5 ENCODING 48869 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 5540 3340 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BEEC ENCODING 48876 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP A8A0 A8A0 F8A0 A8A0 ABA0 A8A0 A8A0 F8A0 00A0 00A0 00A0 ENDCHAR STARTCHAR U+BF01 ENCODING 48897 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 54A0 54A0 7FA0 54A0 3CA0 00A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+BF08 ENCODING 48904 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 77C0 5540 5540 55C0 5540 3340 0040 0040 0040 ENDCHAR STARTCHAR U+BF09 ENCODING 48905 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 55C0 7740 55C0 5540 3340 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BF18 ENCODING 48920 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 55C0 7740 55C0 5540 3340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+BF19 ENCODING 48921 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 55C0 7740 55C0 5540 3340 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+BF1B ENCODING 48923 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 55C0 7740 55C0 5540 3340 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+BF1C ENCODING 48924 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 55C0 7740 55C0 5540 3340 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+BF1D ENCODING 48925 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 55C0 7740 55C0 5540 3340 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+BF40 ENCODING 48960 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4A40 4A40 7BC0 4A40 4A40 39C0 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+BF41 ENCODING 48961 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 3BC0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BF44 ENCODING 48964 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+BF48 ENCODING 48968 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 3180 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BF50 ENCODING 48976 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 3BC0 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BF51 ENCODING 48977 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 3BC0 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+BF55 ENCODING 48981 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 3BC0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+BF94 ENCODING 49044 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5540 3740 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+BFB0 ENCODING 49072 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4A40 4A40 7BC0 4A40 4A40 39C0 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+BFC5 ENCODING 49093 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 1100 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+BFCC ENCODING 49100 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 4A40 39C0 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+BFCD ENCODING 49101 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+BFD0 ENCODING 49104 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+BFD4 ENCODING 49108 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 3180 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+BFDC ENCODING 49116 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+BFDF ENCODING 49119 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+BFE1 ENCODING 49121 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C03C ENCODING 49212 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 4A40 39C0 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+C051 ENCODING 49233 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 FFE0 1100 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C058 ENCODING 49240 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 4A40 4A40 7BC0 4A40 4A40 39C0 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+C05C ENCODING 49244 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C060 ENCODING 49248 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 3180 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C068 ENCODING 49256 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C069 ENCODING 49257 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 4A40 7BC0 4A40 39C0 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C090 ENCODING 49296 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5540 5540 5540 3340 0040 0040 0040 ENDCHAR STARTCHAR U+C091 ENCODING 49297 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5540 3340 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C094 ENCODING 49300 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5540 3340 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C098 ENCODING 49304 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 3340 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C0A0 ENCODING 49312 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5540 3340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C0A1 ENCODING 49313 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5540 3340 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C0A3 ENCODING 49315 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5540 3340 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C0A5 ENCODING 49317 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 5540 5540 7740 5540 5540 3340 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C0AC ENCODING 49324 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 0840 1470 1440 2240 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C0AD ENCODING 49325 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 1440 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C0AF ENCODING 49327 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 1440 2240 4140 0000 3C80 0480 0540 0220 ENDCHAR STARTCHAR U+C0B0 ENCODING 49328 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1470 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C0B3 ENCODING 49331 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 1440 2240 4140 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+C0B4 ENCODING 49332 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1470 2240 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C0B5 ENCODING 49333 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1470 2240 4140 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+C0B6 ENCODING 49334 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1470 2240 4140 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C0BC ENCODING 49340 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 1440 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C0BD ENCODING 49341 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 1440 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C0BF ENCODING 49343 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 1440 2240 4140 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C0C0 ENCODING 49344 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 1440 2240 4140 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C0C1 ENCODING 49345 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 1440 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C0C5 ENCODING 49349 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1470 2240 4140 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C0C8 ENCODING 49352 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 1120 1120 2920 29E0 4520 4520 8320 0120 0120 ENDCHAR STARTCHAR U+C0C9 ENCODING 49353 SWIDTH 935 0 DWIDTH 12 0 BBX 10 8 0 1 BITMAP 1100 1100 19C0 2500 4300 8100 0000 1FC0 ENDCHAR STARTCHAR U+C0CC ENCODING 49356 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C0D0 ENCODING 49360 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0920 0920 15E0 2320 4120 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C0D8 ENCODING 49368 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C0D9 ENCODING 49369 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C0DB ENCODING 49371 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+C0DC ENCODING 49372 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+C0DD ENCODING 49373 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C0E4 ENCODING 49380 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0870 0840 1440 1440 2270 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C0E5 ENCODING 49381 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0870 0840 1470 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C0E8 ENCODING 49384 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0870 0840 1470 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C0EC ENCODING 49388 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0870 1440 2270 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C0F4 ENCODING 49396 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0870 0840 1470 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C0F5 ENCODING 49397 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0870 0840 1470 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C0F7 ENCODING 49399 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0870 0840 1470 2240 4140 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C0F9 ENCODING 49401 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0870 0840 1470 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C100 ENCODING 49408 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 11E0 1120 2920 2920 45E0 4520 8320 0120 0120 ENDCHAR STARTCHAR U+C104 ENCODING 49412 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C108 ENCODING 49416 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0920 0920 15E0 2320 4120 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C110 ENCODING 49424 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C115 ENCODING 49429 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 1120 19E0 2520 4320 8120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C11C ENCODING 49436 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 09C0 1440 1440 2240 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C11D ENCODING 49437 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C11E ENCODING 49438 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+C11F ENCODING 49439 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 3C80 0480 0540 0220 ENDCHAR STARTCHAR U+C120 ENCODING 49440 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C123 ENCODING 49443 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+C124 ENCODING 49444 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 15C0 2240 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C126 ENCODING 49446 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 15C0 2240 4140 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C127 ENCODING 49447 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 15C0 2240 4140 0040 3D40 0540 3DC0 2140 1FC0 ENDCHAR STARTCHAR U+C12C ENCODING 49452 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C12D ENCODING 49453 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C12F ENCODING 49455 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C130 ENCODING 49456 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C131 ENCODING 49457 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C136 ENCODING 49462 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 1440 2240 4140 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+C138 ENCODING 49464 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 10A0 10A0 2BA0 28A0 44A0 44A0 82A0 00A0 00A0 ENDCHAR STARTCHAR U+C139 ENCODING 49465 SWIDTH 935 0 DWIDTH 12 0 BBX 10 8 0 1 BITMAP 1080 1080 1B80 2480 4280 8080 0000 1FC0 ENDCHAR STARTCHAR U+C13C ENCODING 49468 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 1BA0 24A0 42A0 80A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C140 ENCODING 49472 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 08A0 08A0 17A0 22A0 42A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C148 ENCODING 49480 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 1BA0 24A0 42A0 80A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C149 ENCODING 49481 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 1BA0 24A0 42A0 80A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C14B ENCODING 49483 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 1BA0 24A0 42A0 80A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+C14C ENCODING 49484 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 1BA0 24A0 42A0 80A0 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+C14D ENCODING 49485 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 1BA0 24A0 42A0 80A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C154 ENCODING 49492 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 09C0 0840 1440 15C0 2240 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C155 ENCODING 49493 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 09C0 0840 15C0 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C158 ENCODING 49496 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 09C0 0840 15C0 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C15C ENCODING 49500 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 09C0 1440 23C0 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C164 ENCODING 49508 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 09C0 0840 15C0 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C165 ENCODING 49509 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 09C0 0840 15C0 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C167 ENCODING 49511 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 09C0 0840 15C0 2240 4140 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C168 ENCODING 49512 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 09C0 0840 15C0 2240 4140 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C169 ENCODING 49513 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 09C0 0840 15C0 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C170 ENCODING 49520 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 13A0 10A0 28A0 2BA0 44A0 44A0 82A0 00A0 00A0 ENDCHAR STARTCHAR U+C174 ENCODING 49524 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 13A0 18A0 27A0 42A0 80A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C178 ENCODING 49528 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 08A0 0BA0 14A0 23A0 42A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C185 ENCODING 49541 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 13A0 18A0 27A0 42A0 80A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C18C ENCODING 49548 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 0400 0400 0400 0A00 3180 C040 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+C18D ENCODING 49549 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C18E ENCODING 49550 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0400 FFE0 0000 7FC0 0440 0440 0440 ENDCHAR STARTCHAR U+C190 ENCODING 49552 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C194 ENCODING 49556 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C196 ENCODING 49558 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0400 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+C19C ENCODING 49564 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C19D ENCODING 49565 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C19F ENCODING 49567 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C1A1 ENCODING 49569 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C1A5 ENCODING 49573 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0400 FFE0 0000 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+C1A8 ENCODING 49576 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1440 1440 2270 4140 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+C1A9 ENCODING 49577 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 2240 4170 0840 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C1AC ENCODING 49580 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 2240 4170 0840 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C1B0 ENCODING 49584 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 6340 0870 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C1BD ENCODING 49597 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 2240 4170 0840 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C1C4 ENCODING 49604 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 08A0 08A0 08A0 14A0 24E0 42A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+C1C8 ENCODING 49608 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 28E0 44A0 10A0 7EA0 00A0 2020 2000 2000 1FE0 ENDCHAR STARTCHAR U+C1CC ENCODING 49612 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 28A0 44E0 10A0 7EA0 0000 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C1D4 ENCODING 49620 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 28E0 44A0 10A0 7EA0 00A0 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C1D7 ENCODING 49623 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 28E0 44A0 10A0 7EA0 00A0 0100 0100 0680 3860 ENDCHAR STARTCHAR U+C1D8 ENCODING 49624 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 28E0 44A0 10A0 7EA0 00A0 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C1E0 ENCODING 49632 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1440 1440 2240 4140 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+C1E4 ENCODING 49636 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 2240 4140 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+C1E8 ENCODING 49640 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 6340 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C1F0 ENCODING 49648 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 2240 4140 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C1F1 ENCODING 49649 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 2240 4140 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C1F3 ENCODING 49651 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 1440 2240 4140 0840 7F40 0040 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C1FC ENCODING 49660 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 0400 0400 0400 0A00 3180 C040 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+C1FD ENCODING 49661 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 1100 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C200 ENCODING 49664 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0000 1100 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C204 ENCODING 49668 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 1100 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C20C ENCODING 49676 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 1100 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C20D ENCODING 49677 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 1100 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C20F ENCODING 49679 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 1100 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C211 ENCODING 49681 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 1100 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C218 ENCODING 49688 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 2080 C060 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+C219 ENCODING 49689 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C21C ENCODING 49692 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+C21F ENCODING 49695 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 0400 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+C220 ENCODING 49696 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C228 ENCODING 49704 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C229 ENCODING 49705 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C22B ENCODING 49707 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C22D ENCODING 49709 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C22F ENCODING 49711 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0000 FFE0 0400 0200 7FC0 0400 1B00 60C0 ENDCHAR STARTCHAR U+C231 ENCODING 49713 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0000 FFE0 0400 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+C232 ENCODING 49714 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 0400 7FC0 1100 1100 7FC0 ENDCHAR STARTCHAR U+C234 ENCODING 49716 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1440 6340 0040 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+C248 ENCODING 49736 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 1040 28C0 4440 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C250 ENCODING 49744 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 21 21 51 89 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+C251 ENCODING 49745 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 2100 2100 5300 8900 0100 FD00 2100 1F80 ENDCHAR STARTCHAR U+C254 ENCODING 49748 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 29A0 44A0 00A0 7EA0 08A0 2800 2000 2000 1FE0 ENDCHAR STARTCHAR U+C258 ENCODING 49752 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 28A0 45A0 00A0 00A0 7EA0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C260 ENCODING 49760 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 29A0 44A0 00A0 7EA0 10A0 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C265 ENCODING 49765 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 10A0 29A0 44A0 00A0 7EA0 10A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C26C ENCODING 49772 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1440 6340 0040 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+C26D ENCODING 49773 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 1040 2840 4440 0040 7FC0 0840 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C270 ENCODING 49776 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 1040 2840 4440 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+C274 ENCODING 49780 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 2840 4440 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C27C ENCODING 49788 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 1040 2840 4440 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C27D ENCODING 49789 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 1040 2840 4440 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C27F ENCODING 49791 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 1040 2840 4440 0040 7FC0 0840 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C281 ENCODING 49793 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 1040 2840 4440 0040 7FC0 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C288 ENCODING 49800 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 2080 C060 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+C289 ENCODING 49801 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 1100 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C290 ENCODING 49808 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C298 ENCODING 49816 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C29B ENCODING 49819 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 1100 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C29D ENCODING 49821 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0400 1B00 60C0 0000 FFE0 1100 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C2A4 ENCODING 49828 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 0400 0400 0400 0A00 3180 C040 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+C2A5 ENCODING 49829 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C2A8 ENCODING 49832 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C2AC ENCODING 49836 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C2AD ENCODING 49837 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 1B00 60C0 0000 FFE0 0000 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+C2B4 ENCODING 49844 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C2B5 ENCODING 49845 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C2B7 ENCODING 49847 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C2B9 ENCODING 49849 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 0A00 1100 60C0 0000 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C2DC ENCODING 49884 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 0840 1440 1440 2240 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C2DD ENCODING 49885 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1440 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C2E0 ENCODING 49888 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1440 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C2E3 ENCODING 49891 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1440 2240 4140 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+C2E4 ENCODING 49892 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1440 2240 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C2EB ENCODING 49899 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 1440 2240 4140 0040 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+C2EC ENCODING 49900 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1440 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C2ED ENCODING 49901 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1440 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C2EF ENCODING 49903 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1440 2240 4140 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C2F1 ENCODING 49905 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1440 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C2F6 ENCODING 49910 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 0840 0840 1440 2240 4140 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+C2F8 ENCODING 49912 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 2240 2270 5540 5540 5540 88C0 0040 0040 ENDCHAR STARTCHAR U+C2F9 ENCODING 49913 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2270 5540 5540 8940 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C2FB ENCODING 49915 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2270 5540 5540 8940 0000 3C80 0480 0540 0220 ENDCHAR STARTCHAR U+C2FC ENCODING 49916 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5570 5540 8940 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C300 ENCODING 49920 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 5570 5540 8940 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C308 ENCODING 49928 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2270 5540 5540 8940 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C309 ENCODING 49929 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2270 5540 5540 8940 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C30C ENCODING 49932 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2270 5540 5540 8940 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C30D ENCODING 49933 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2270 5540 5540 8940 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C313 ENCODING 49939 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 5570 5540 8940 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+C314 ENCODING 49940 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2920 2920 2920 2920 2920 55E0 5520 9520 2320 0120 0120 ENDCHAR STARTCHAR U+C315 ENCODING 49941 SWIDTH 935 0 DWIDTH 12 0 BBX 10 8 0 1 BITMAP 2500 2500 25C0 5B00 9100 0100 0000 1FC0 ENDCHAR STARTCHAR U+C318 ENCODING 49944 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2520 2520 25E0 5B20 9120 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C31C ENCODING 49948 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2520 2520 25E0 5B20 9120 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C324 ENCODING 49956 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2520 2520 25E0 5B20 9120 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C325 ENCODING 49957 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2520 2520 25E0 5B20 9120 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C328 ENCODING 49960 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2520 2520 25E0 5B20 9120 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+C329 ENCODING 49961 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2520 2520 25E0 5B20 9120 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C345 ENCODING 49989 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2270 2240 5570 5540 8940 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C368 ENCODING 50024 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 23C0 2240 5540 5540 5540 88C0 0040 0040 ENDCHAR STARTCHAR U+C369 ENCODING 50025 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 23C0 5540 5540 8940 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C36C ENCODING 50028 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 23C0 5540 5540 8940 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C370 ENCODING 50032 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 55C0 5540 8940 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C372 ENCODING 50034 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 55C0 5540 8940 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C378 ENCODING 50040 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 23C0 5540 5540 8940 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C379 ENCODING 50041 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 23C0 5540 5540 8940 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C37C ENCODING 50044 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 23C0 5540 5540 8940 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C37D ENCODING 50045 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 23C0 5540 5540 8940 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C384 ENCODING 50052 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 28A0 28A0 28A0 28A0 2BA0 54A0 54A0 94A0 22A0 00A0 00A0 ENDCHAR STARTCHAR U+C388 ENCODING 50056 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 24A0 24A0 27A0 5AA0 92A0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C38C ENCODING 50060 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 24A0 24A0 27A0 5AA0 92A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C3C0 ENCODING 50112 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 24A0 27A0 24A0 5BA0 92A0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C3D8 ENCODING 50136 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1080 1080 1080 3180 4A40 8400 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+C3D9 ENCODING 50137 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C3DC ENCODING 50140 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C3DF ENCODING 50143 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0400 FFE0 0000 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+C3E0 ENCODING 50144 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 5140 8A20 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C3E2 ENCODING 50146 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 5140 8A20 0400 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+C3E8 ENCODING 50152 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C3E9 ENCODING 50153 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C3ED ENCODING 50157 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C3F4 ENCODING 50164 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5570 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+C3F5 ENCODING 50165 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 1240 2D40 4970 0840 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C3F8 ENCODING 50168 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 1240 2D40 4970 0840 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C408 ENCODING 50184 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 1240 2D40 4970 0840 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C410 ENCODING 50192 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 14A0 14A0 14A0 2AA0 52E0 00A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+C424 ENCODING 50212 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 14A0 14A0 2CE0 52A0 10A0 7EA0 00A0 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C42C ENCODING 50220 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+C430 ENCODING 50224 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 1240 2D40 4940 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+C434 ENCODING 50228 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 3640 4940 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C43C ENCODING 50236 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 1240 2D40 4940 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C43D ENCODING 50237 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 1240 2D40 4940 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C448 ENCODING 50248 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1080 1080 1080 3180 4A40 8400 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+C464 ENCODING 50276 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 2080 5140 8A20 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+C465 ENCODING 50277 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C468 ENCODING 50280 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+C46C ENCODING 50284 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 5140 8A20 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C474 ENCODING 50292 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C475 ENCODING 50293 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C479 ENCODING 50297 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C480 ENCODING 50304 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+C494 ENCODING 50324 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2840 2840 54C0 5440 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C49C ENCODING 50332 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 51 51 A9 A9 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+C4B8 ENCODING 50360 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+C4BC ENCODING 50364 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2840 2840 5440 5440 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+C4E9 ENCODING 50409 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 FFE0 1100 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C4F0 ENCODING 50416 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1080 1080 1080 3180 4A40 8400 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+C4F1 ENCODING 50417 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C4F4 ENCODING 50420 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C4F8 ENCODING 50424 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 5140 8A20 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C4FA ENCODING 50426 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 5140 8A20 0000 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+C4FF ENCODING 50431 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 5140 8A20 0000 FFE0 0000 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+C500 ENCODING 50432 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C501 ENCODING 50433 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2080 2080 5140 8A20 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C50C ENCODING 50444 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 0040 0040 0040 7FC0 0040 0040 ENDCHAR STARTCHAR U+C510 ENCODING 50448 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 1240 2D40 4940 0040 7FC0 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C514 ENCODING 50452 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 3640 4940 0040 7FC0 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C51C ENCODING 50460 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1240 1240 2D40 4940 0040 7FC0 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C528 ENCODING 50472 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 2240 2240 5540 5540 5540 88C0 0040 0040 ENDCHAR STARTCHAR U+C529 ENCODING 50473 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 8940 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C52C ENCODING 50476 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 8940 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C530 ENCODING 50480 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 5540 5540 8940 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C538 ENCODING 50488 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 8940 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C539 ENCODING 50489 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 8940 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C53B ENCODING 50491 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 8940 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C53D ENCODING 50493 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 2240 2240 2240 5540 5540 8940 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C544 ENCODING 50500 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 4270 4240 4240 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+C545 ENCODING 50501 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C548 ENCODING 50504 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4270 3C40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C549 ENCODING 50505 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0000 27C0 2100 2280 1C60 ENDCHAR STARTCHAR U+C54A ENCODING 50506 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0100 27E0 2240 2240 1D80 ENDCHAR STARTCHAR U+C54C ENCODING 50508 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C54D ENCODING 50509 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+C54E ENCODING 50510 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C553 ENCODING 50515 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+C554 ENCODING 50516 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C555 ENCODING 50517 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C557 ENCODING 50519 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C558 ENCODING 50520 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C559 ENCODING 50521 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C55D ENCODING 50525 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C55E ENCODING 50526 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 3C40 0040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+C560 ENCODING 50528 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 4520 4520 4520 45E0 4520 4520 3920 0120 0120 ENDCHAR STARTCHAR U+C561 ENCODING 50529 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 7200 8A00 8B80 8A00 7200 0200 0000 3F80 ENDCHAR STARTCHAR U+C564 ENCODING 50532 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C568 ENCODING 50536 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C570 ENCODING 50544 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C571 ENCODING 50545 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C573 ENCODING 50547 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+C574 ENCODING 50548 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+C575 ENCODING 50549 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C57C ENCODING 50556 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4270 4240 4240 4240 4270 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+C57D ENCODING 50557 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C580 ENCODING 50560 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C584 ENCODING 50564 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C587 ENCODING 50567 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 3A40 0A40 3BC0 2240 1FC0 ENDCHAR STARTCHAR U+C58C ENCODING 50572 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C58D ENCODING 50573 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C58F ENCODING 50575 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C591 ENCODING 50577 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C595 ENCODING 50581 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C597 ENCODING 50583 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4270 4240 4270 3C40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+C598 ENCODING 50584 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 4520 4520 45E0 4520 3920 0120 0120 ENDCHAR STARTCHAR U+C59C ENCODING 50588 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C5A0 ENCODING 50592 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C5A9 ENCODING 50601 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 4520 45E0 4520 3920 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C5B4 ENCODING 50612 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 43C0 4240 4240 4240 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+C5B5 ENCODING 50613 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C5B8 ENCODING 50616 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C5B9 ENCODING 50617 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 27C0 2100 2280 1C60 ENDCHAR STARTCHAR U+C5BB ENCODING 50619 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+C5BC ENCODING 50620 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C5BD ENCODING 50621 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+C5BE ENCODING 50622 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C5C4 ENCODING 50628 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C5C5 ENCODING 50629 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C5C6 ENCODING 50630 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 2480 3C80 2540 1E20 ENDCHAR STARTCHAR U+C5C7 ENCODING 50631 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C5C8 ENCODING 50632 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C5C9 ENCODING 50633 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C5CA ENCODING 50634 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+C5CC ENCODING 50636 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 3FC0 0040 3FC0 0040 ENDCHAR STARTCHAR U+C5CE ENCODING 50638 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 3C40 0040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+C5D0 ENCODING 50640 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 71 89 89 89 8F 89 89 89 71 01 01 ENDCHAR STARTCHAR U+C5D1 ENCODING 50641 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 7100 8900 8F00 8900 7100 0100 0000 3F80 ENDCHAR STARTCHAR U+C5D4 ENCODING 50644 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 47A0 44A0 38A0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C5D8 ENCODING 50648 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 47A0 44A0 38A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C5E0 ENCODING 50656 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 47A0 44A0 38A0 00A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C5E1 ENCODING 50657 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 47A0 44A0 38A0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C5E3 ENCODING 50659 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 47A0 44A0 38A0 00A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+C5E5 ENCODING 50661 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 47A0 44A0 38A0 00A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C5EC ENCODING 50668 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 43C0 4240 4240 43C0 4240 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+C5ED ENCODING 50669 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C5EE ENCODING 50670 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+C5F0 ENCODING 50672 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C5F4 ENCODING 50676 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C5F6 ENCODING 50678 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C5F7 ENCODING 50679 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 3D40 0540 3DC0 2140 1FC0 ENDCHAR STARTCHAR U+C5FC ENCODING 50684 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C5FD ENCODING 50685 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C5FE ENCODING 50686 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 2480 3C80 2540 1E20 ENDCHAR STARTCHAR U+C5FF ENCODING 50687 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C600 ENCODING 50688 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C601 ENCODING 50689 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C605 ENCODING 50693 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C606 ENCODING 50694 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+C607 ENCODING 50695 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 43C0 4240 43C0 3C40 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+C608 ENCODING 50696 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 71 89 8F 89 89 8F 89 89 71 01 01 ENDCHAR STARTCHAR U+C60C ENCODING 50700 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 47A0 44A0 47A0 38A0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C610 ENCODING 50704 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 47A0 44A0 47A0 38A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C618 ENCODING 50712 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 47A0 44A0 47A0 38A0 00A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C619 ENCODING 50713 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 47A0 44A0 47A0 38A0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C61B ENCODING 50715 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 47A0 44A0 47A0 38A0 00A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+C61C ENCODING 50716 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 47A0 44A0 47A0 38A0 00A0 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+C624 ENCODING 50724 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 2080 4040 4040 2080 1F00 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+C625 ENCODING 50725 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C628 ENCODING 50728 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C62C ENCODING 50732 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C62D ENCODING 50733 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0400 FFE0 0000 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+C62E ENCODING 50734 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0400 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+C630 ENCODING 50736 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0400 FFE0 0000 7900 0900 7A80 4440 3820 ENDCHAR STARTCHAR U+C633 ENCODING 50739 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0400 FFE0 0000 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+C634 ENCODING 50740 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C635 ENCODING 50741 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C637 ENCODING 50743 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C639 ENCODING 50745 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C63B ENCODING 50747 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0400 FFE0 0000 0200 7FC0 0400 1B00 60C0 ENDCHAR STARTCHAR U+C640 ENCODING 50752 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C70 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+C641 ENCODING 50753 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C70 0840 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C644 ENCODING 50756 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C70 0840 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C648 ENCODING 50760 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 3C40 0870 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C650 ENCODING 50768 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C70 0840 7F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C651 ENCODING 50769 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C70 0840 7F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C653 ENCODING 50771 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C70 0840 7F40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C654 ENCODING 50772 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C70 0840 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C655 ENCODING 50773 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C70 0840 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C65C ENCODING 50780 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3CA0 42A0 42A0 42A0 3CE0 00A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+C65D ENCODING 50781 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 7100 8900 8980 7100 2100 FD00 0100 3F80 ENDCHAR STARTCHAR U+C660 ENCODING 50784 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 44E0 38A0 10A0 7EA0 00A0 2020 2000 2000 1FE0 ENDCHAR STARTCHAR U+C66C ENCODING 50796 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 44E0 38A0 10A0 7EA0 00A0 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C66F ENCODING 50799 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 44E0 38A0 10A0 7EA0 00A0 0100 0100 0680 3860 ENDCHAR STARTCHAR U+C671 ENCODING 50801 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 44E0 38A0 10A0 7EA0 00A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C678 ENCODING 50808 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+C679 ENCODING 50809 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0840 7F40 0040 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C67C ENCODING 50812 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+C680 ENCODING 50816 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 3C40 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C688 ENCODING 50824 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C689 ENCODING 50825 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C68B ENCODING 50827 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0840 7F40 0040 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C68D ENCODING 50829 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0840 7F40 0040 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C694 ENCODING 50836 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 2080 4040 4040 2080 1F00 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+C695 ENCODING 50837 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 1100 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C698 ENCODING 50840 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 1100 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C69C ENCODING 50844 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 1100 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C6A4 ENCODING 50852 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 1100 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C6A5 ENCODING 50853 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 1100 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C6A7 ENCODING 50855 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 1100 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C6A9 ENCODING 50857 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 1100 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C6B0 ENCODING 50864 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+C6B1 ENCODING 50865 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C6B4 ENCODING 50868 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+C6B8 ENCODING 50872 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C6B9 ENCODING 50873 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 0400 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+C6BA ENCODING 50874 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 0400 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+C6C0 ENCODING 50880 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C6C1 ENCODING 50881 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C6C3 ENCODING 50883 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C6C5 ENCODING 50885 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C6CC ENCODING 50892 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+C6CD ENCODING 50893 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 44C0 3840 0040 7F40 0840 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C6D0 ENCODING 50896 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 44C0 3840 0040 7F40 0840 2800 2000 2000 1FC0 ENDCHAR STARTCHAR U+C6D4 ENCODING 50900 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 38C0 0040 7F40 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C6DC ENCODING 50908 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 44C0 3840 0040 7F40 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C6DD ENCODING 50909 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 44C0 3840 0040 7F40 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C6E0 ENCODING 50912 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 44C0 3840 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C6E1 ENCODING 50913 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 44C0 3840 0040 7F40 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C6E8 ENCODING 50920 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 71 89 89 71 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+C6E9 ENCODING 50921 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 7100 8900 8B00 7100 0100 FD00 2100 1F80 ENDCHAR STARTCHAR U+C6EC ENCODING 50924 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 45A0 38A0 00A0 7EA0 08A0 2800 2000 2000 1FE0 ENDCHAR STARTCHAR U+C6F0 ENCODING 50928 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 39A0 00A0 00A0 7EA0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C6F8 ENCODING 50936 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 45A0 38A0 00A0 7EA0 10A0 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C6F9 ENCODING 50937 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 45A0 38A0 00A0 7EA0 10A0 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C6FD ENCODING 50941 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 44A0 45A0 38A0 00A0 7EA0 10A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C704 ENCODING 50948 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+C705 ENCODING 50949 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 4440 3840 0040 7FC0 0840 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C708 ENCODING 50952 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 4440 3840 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+C70C ENCODING 50956 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 3840 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C714 ENCODING 50964 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 4440 3840 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C715 ENCODING 50965 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 4440 3840 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C717 ENCODING 50967 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 4440 3840 0040 7FC0 0840 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C719 ENCODING 50969 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3840 4440 4440 3840 0040 7FC0 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C720 ENCODING 50976 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+C721 ENCODING 50977 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 1100 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C724 ENCODING 50980 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 0000 FFE0 5100 5100 4000 3FC0 ENDCHAR STARTCHAR U+C728 ENCODING 50984 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C730 ENCODING 50992 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C731 ENCODING 50993 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 1100 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C733 ENCODING 50995 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 1100 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C735 ENCODING 50997 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 1100 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C737 ENCODING 50999 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 1100 0200 7FC0 0400 1B00 60C0 ENDCHAR STARTCHAR U+C73C ENCODING 51004 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 2080 4040 4040 2080 1F00 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+C73D ENCODING 51005 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C740 ENCODING 51008 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C744 ENCODING 51012 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C74A ENCODING 51018 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 0000 77C0 1280 7280 4280 3FC0 ENDCHAR STARTCHAR U+C74C ENCODING 51020 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C74D ENCODING 51021 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C74F ENCODING 51023 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C751 ENCODING 51025 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C752 ENCODING 51026 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0000 7FC0 0400 0A00 71C0 ENDCHAR STARTCHAR U+C753 ENCODING 51027 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 0000 0200 7FC0 0400 1B00 60C0 ENDCHAR STARTCHAR U+C754 ENCODING 51028 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0000 7FC0 0040 7FC0 0040 ENDCHAR STARTCHAR U+C755 ENCODING 51029 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 0000 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+C756 ENCODING 51030 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 4040 3F80 0000 FFE0 0000 7FC0 1100 1100 7FC0 ENDCHAR STARTCHAR U+C757 ENCODING 51031 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3F80 4040 3F80 0000 FFE0 0000 0E00 0000 7FC0 2080 1F00 ENDCHAR STARTCHAR U+C758 ENCODING 51032 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0040 0040 7FC0 0040 0040 ENDCHAR STARTCHAR U+C75C ENCODING 51036 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0040 7FC0 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C760 ENCODING 51040 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 3C40 0040 7FC0 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C768 ENCODING 51048 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0040 7FC0 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C76B ENCODING 51051 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 3C40 0040 7FC0 0040 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C774 ENCODING 51060 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 4240 4240 4240 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+C775 ENCODING 51061 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C778 ENCODING 51064 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C77C ENCODING 51068 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C77D ENCODING 51069 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+C77E ENCODING 51070 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C783 ENCODING 51075 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 7880 0FE0 7A40 4240 3980 ENDCHAR STARTCHAR U+C784 ENCODING 51076 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C785 ENCODING 51077 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C787 ENCODING 51079 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C788 ENCODING 51080 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C789 ENCODING 51081 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C78A ENCODING 51082 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+C78E ENCODING 51086 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 4240 4240 4240 3C40 0040 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+C790 ENCODING 51088 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 0840 0870 1440 1440 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C791 ENCODING 51089 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C794 ENCODING 51092 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1470 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C796 ENCODING 51094 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0100 27E0 2240 2240 1D80 ENDCHAR STARTCHAR U+C797 ENCODING 51095 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+C798 ENCODING 51096 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 1470 2240 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C79A ENCODING 51098 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 1470 2240 4140 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C7A0 ENCODING 51104 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C7A1 ENCODING 51105 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C7A3 ENCODING 51107 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C7A4 ENCODING 51108 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C7A5 ENCODING 51109 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C7A6 ENCODING 51110 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 1440 2240 4140 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+C7AC ENCODING 51116 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 1120 1120 1120 29E0 2920 4520 4520 0120 0120 ENDCHAR STARTCHAR U+C7AD ENCODING 51117 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 2200 2380 5200 5200 8A00 0000 3F80 ENDCHAR STARTCHAR U+C7B0 ENCODING 51120 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 11E0 2920 2920 4520 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C7B4 ENCODING 51124 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 29E0 2920 4520 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C7BC ENCODING 51132 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 11E0 2920 2920 4520 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C7BD ENCODING 51133 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 11E0 2920 2920 4520 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C7BF ENCODING 51135 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 11E0 2920 2920 4520 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+C7C0 ENCODING 51136 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 11E0 2920 2920 4520 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+C7C1 ENCODING 51137 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 11E0 2920 2920 4520 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C7C8 ENCODING 51144 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0870 0840 0840 1440 1470 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C7C9 ENCODING 51145 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0870 0840 1470 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C7CC ENCODING 51148 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0870 0840 1470 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C7CE ENCODING 51150 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0870 0840 1470 2240 4140 0100 27E0 2240 2240 1D80 ENDCHAR STARTCHAR U+C7D0 ENCODING 51152 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0870 1440 2270 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C7D8 ENCODING 51160 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0870 0840 1470 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C7DD ENCODING 51165 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0870 0840 1470 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C7E4 ENCODING 51172 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 11E0 1120 1120 2920 29E0 4520 4520 0120 0120 ENDCHAR STARTCHAR U+C7E8 ENCODING 51176 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 11E0 2920 2920 4520 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C7EC ENCODING 51180 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 1120 29E0 2920 4520 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C800 ENCODING 51200 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 09C0 0840 1440 1440 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C801 ENCODING 51201 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 09C0 1440 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C804 ENCODING 51204 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 09C0 1440 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C808 ENCODING 51208 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 15C0 2240 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C80A ENCODING 51210 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 15C0 2240 4140 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C810 ENCODING 51216 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 09C0 1440 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C811 ENCODING 51217 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 09C0 1440 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C813 ENCODING 51219 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 09C0 1440 2240 4140 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C815 ENCODING 51221 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 09C0 1440 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C816 ENCODING 51222 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 09C0 1440 2240 4140 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+C81C ENCODING 51228 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 21 21 21 27 51 51 89 89 01 01 ENDCHAR STARTCHAR U+C81D ENCODING 51229 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP F900 2100 2700 5100 5100 8900 0000 3F80 ENDCHAR STARTCHAR U+C820 ENCODING 51232 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 10A0 13A0 28A0 28A0 44A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+C824 ENCODING 51236 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 10A0 2BA0 28A0 44A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+C82C ENCODING 51244 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 10A0 13A0 28A0 28A0 44A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+C82D ENCODING 51245 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 10A0 13A0 28A0 28A0 44A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+C82F ENCODING 51247 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 10A0 13A0 28A0 28A0 44A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+C831 ENCODING 51249 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 10A0 13A0 28A0 28A0 44A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C838 ENCODING 51256 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 09C0 0840 0840 15C0 1440 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C83C ENCODING 51260 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 09C0 0840 15C0 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C840 ENCODING 51264 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 09C0 1440 23C0 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C848 ENCODING 51272 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 09C0 0840 15C0 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C849 ENCODING 51273 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 09C0 0840 15C0 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C84C ENCODING 51276 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 09C0 0840 15C0 2240 4140 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C84D ENCODING 51277 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 09C0 0840 15C0 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C854 ENCODING 51284 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 21 27 21 21 57 51 89 89 01 01 ENDCHAR STARTCHAR U+C870 ENCODING 51312 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0400 0400 0A00 1100 60C0 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+C871 ENCODING 51313 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C874 ENCODING 51316 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C878 ENCODING 51320 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 60C0 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C87A ENCODING 51322 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 60C0 0400 FFE0 0000 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+C880 ENCODING 51328 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C881 ENCODING 51329 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C883 ENCODING 51331 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C885 ENCODING 51333 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C886 ENCODING 51334 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0400 FFE0 0000 7FC0 0400 0A00 71C0 ENDCHAR STARTCHAR U+C887 ENCODING 51335 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 60C0 0400 FFE0 0000 0200 7FC0 0400 1B00 60C0 ENDCHAR STARTCHAR U+C88B ENCODING 51339 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 60C0 0400 FFE0 0000 0E00 0000 7FC0 2080 1F00 ENDCHAR STARTCHAR U+C88C ENCODING 51340 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1840 2440 2440 4270 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+C88D ENCODING 51341 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4170 0840 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C894 ENCODING 51348 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 2240 5540 0870 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C89D ENCODING 51357 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4170 0840 7F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C89F ENCODING 51359 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4170 0840 7F40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C8A1 ENCODING 51361 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4170 0840 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C8A8 ENCODING 51368 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 18A0 24A0 24A0 42E0 00A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+C8BC ENCODING 51388 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 28A0 44E0 44A0 10A0 7EA0 00A0 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C8BD ENCODING 51389 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 28A0 44E0 44A0 10A0 7EA0 00A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+C8C4 ENCODING 51396 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 1840 2440 2440 4240 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+C8C8 ENCODING 51400 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4140 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+C8CC ENCODING 51404 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 2240 5540 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C8D4 ENCODING 51412 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4140 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C8D5 ENCODING 51413 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4140 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C8D7 ENCODING 51415 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4140 0840 7F40 0040 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C8D9 ENCODING 51417 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 1440 2240 4140 0840 7F40 0040 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C8E0 ENCODING 51424 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0400 0400 0A00 1100 60C0 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+C8E1 ENCODING 51425 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 1100 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C8E4 ENCODING 51428 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0000 1100 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C8F5 ENCODING 51445 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 1100 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C8FC ENCODING 51452 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 1100 60C0 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+C8FD ENCODING 51453 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 71C0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C900 ENCODING 51456 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 71C0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+C904 ENCODING 51460 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 71C0 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C905 ENCODING 51461 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 71C0 0000 FFE0 0400 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+C906 ENCODING 51462 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 71C0 0000 FFE0 0400 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+C90C ENCODING 51468 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 71C0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C90D ENCODING 51469 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 71C0 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C90F ENCODING 51471 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 71C0 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C911 ENCODING 51473 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 71C0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C918 ENCODING 51480 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 1440 2240 4140 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+C92C ENCODING 51500 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 44C0 4440 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C934 ENCODING 51508 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 21 51 89 01 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+C950 ENCODING 51536 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 1440 2240 4140 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+C951 ENCODING 51537 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 4440 4440 0040 7FC0 0840 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C954 ENCODING 51540 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 4440 4440 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+C958 ENCODING 51544 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 4440 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C960 ENCODING 51552 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 4440 4440 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C961 ENCODING 51553 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 4440 4440 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C963 ENCODING 51555 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 4440 4440 0040 7FC0 0840 0200 0500 0880 3060 ENDCHAR STARTCHAR U+C96C ENCODING 51564 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 1100 60C0 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+C970 ENCODING 51568 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 71C0 0000 0000 FFE0 5100 5100 4000 3FC0 ENDCHAR STARTCHAR U+C974 ENCODING 51572 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 71C0 0000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C97C ENCODING 51580 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0400 0A00 71C0 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C988 ENCODING 51592 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0400 0400 0A00 1100 60C0 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+C989 ENCODING 51593 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C98C ENCODING 51596 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+C990 ENCODING 51600 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 60C0 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+C998 ENCODING 51608 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+C999 ENCODING 51609 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+C99B ENCODING 51611 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+C99D ENCODING 51613 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0A00 1100 60C0 0000 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+C9C0 ENCODING 51648 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 0840 0840 1440 1440 2240 4140 0040 0040 ENDCHAR STARTCHAR U+C9C1 ENCODING 51649 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C9C4 ENCODING 51652 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C9C7 ENCODING 51655 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+C9C8 ENCODING 51656 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 1440 2240 4140 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C9CA ENCODING 51658 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 1440 2240 4140 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+C9D0 ENCODING 51664 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C9D1 ENCODING 51665 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C9D3 ENCODING 51667 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C9D5 ENCODING 51669 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C9D6 ENCODING 51670 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+C9D9 ENCODING 51673 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 1440 2240 4140 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C9DA ENCODING 51674 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0840 0840 1440 2240 4140 0000 3FE0 0880 0880 3FE0 ENDCHAR STARTCHAR U+C9DC ENCODING 51676 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2240 2270 5540 5540 5540 88C0 0040 0040 ENDCHAR STARTCHAR U+C9DD ENCODING 51677 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 2240 5540 8940 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+C9E0 ENCODING 51680 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2270 5540 8940 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+C9E2 ENCODING 51682 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 2240 5540 8940 0100 27E0 2240 2240 1D80 ENDCHAR STARTCHAR U+C9E4 ENCODING 51684 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 5540 8940 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+C9E7 ENCODING 51687 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 5540 8940 0040 3A40 0A40 3BC0 2240 1FC0 ENDCHAR STARTCHAR U+C9EC ENCODING 51692 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 2240 5540 8940 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+C9ED ENCODING 51693 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 2240 5540 8940 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+C9EF ENCODING 51695 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 2240 5540 8940 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+C9F0 ENCODING 51696 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 2240 5540 8940 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+C9F1 ENCODING 51697 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 2240 5540 8940 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+C9F8 ENCODING 51704 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 2920 2920 2920 2920 55E0 5520 9520 2320 0120 0120 ENDCHAR STARTCHAR U+C9F9 ENCODING 51705 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 5200 5380 5200 AA00 AA00 0000 3F80 ENDCHAR STARTCHAR U+C9FC ENCODING 51708 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 2920 29E0 2920 5520 5520 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+CA00 ENCODING 51712 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 2920 29E0 5520 5520 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+CA08 ENCODING 51720 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 2920 29E0 2920 5520 5520 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+CA09 ENCODING 51721 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 2920 29E0 2920 5520 5520 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+CA0B ENCODING 51723 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 2920 29E0 2920 5520 5520 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+CA0C ENCODING 51724 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 2920 29E0 2920 5520 5520 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+CA0D ENCODING 51725 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 2920 29E0 2920 5520 5520 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+CA14 ENCODING 51732 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2270 2240 2240 5540 5570 5540 88C0 0040 0040 ENDCHAR STARTCHAR U+CA18 ENCODING 51736 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2270 2240 2270 5540 8940 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CA29 ENCODING 51753 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2270 2240 2270 5540 8940 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CA4C ENCODING 51788 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 23C0 2240 5540 5540 5540 88C0 0040 0040 ENDCHAR STARTCHAR U+CA4D ENCODING 51789 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 2240 5540 8940 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CA50 ENCODING 51792 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 2240 5540 8940 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CA54 ENCODING 51796 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 5540 8940 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CA5C ENCODING 51804 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 2240 5540 8940 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CA5D ENCODING 51805 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 2240 5540 8940 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CA5F ENCODING 51807 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 2240 5540 8940 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+CA60 ENCODING 51808 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 2240 5540 8940 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CA61 ENCODING 51809 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 2240 5540 8940 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CA68 ENCODING 51816 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 28A0 28A0 28A0 2BA0 54A0 54A0 94A0 22A0 00A0 00A0 ENDCHAR STARTCHAR U+CA7D ENCODING 51837 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 28A0 2BA0 28A0 54A0 54A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+CA84 ENCODING 51844 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 23C0 2240 2240 55C0 5540 5540 88C0 0040 0040 ENDCHAR STARTCHAR U+CA98 ENCODING 51864 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 23C0 2240 23C0 5540 8940 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CABC ENCODING 51900 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP FBC0 2080 2080 2080 5140 8A00 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+CABD ENCODING 51901 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CAC0 ENCODING 51904 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+CAC4 ENCODING 51908 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 2A80 4440 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+CACC ENCODING 51916 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+CACD ENCODING 51917 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CACF ENCODING 51919 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+CAD1 ENCODING 51921 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+CAD3 ENCODING 51923 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 2A80 4440 0400 FFE0 0000 0200 7FC0 0400 1B00 60C0 ENDCHAR STARTCHAR U+CAD8 ENCODING 51928 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 5540 8970 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+CAD9 ENCODING 51929 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 5540 4970 0840 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CAE0 ENCODING 51936 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 3640 4940 0870 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CAEC ENCODING 51948 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 5540 4970 0840 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CAF4 ENCODING 51956 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 24A0 24A0 5AA0 52E0 00A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+CB08 ENCODING 51976 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 28A0 54E0 54A0 10A0 7EA0 00A0 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CB10 ENCODING 51984 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 5540 8940 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+CB14 ENCODING 51988 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 5540 4940 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+CB18 ENCODING 51992 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 3640 4940 0840 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CB20 ENCODING 52000 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 5540 4940 0840 7F40 0040 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CB21 ENCODING 52001 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 5540 4940 0840 7F40 0040 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CB41 ENCODING 52033 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 1100 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+CB48 ENCODING 52040 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 2080 2080 5140 8A20 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+CB49 ENCODING 52041 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CB4C ENCODING 52044 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+CB50 ENCODING 52048 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 5140 8A20 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+CB58 ENCODING 52056 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+CB59 ENCODING 52057 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CB5D ENCODING 52061 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+CB64 ENCODING 52068 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 5540 5540 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+CB78 ENCODING 52088 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 54C0 5440 0040 7F40 0840 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CB79 ENCODING 52089 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 54C0 5440 0040 7F40 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CB9C ENCODING 52124 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 5540 5540 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+CBB8 ENCODING 52152 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7BC0 2080 2080 5140 8A20 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+CBD4 ENCODING 52180 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP FBC0 2080 2080 2080 5140 8A00 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+CBE4 ENCODING 52196 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+CBE7 ENCODING 52199 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+CBE9 ENCODING 52201 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP FBE0 2080 5140 8A20 0000 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+CC0C ENCODING 52236 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2240 2240 5540 5540 5540 88C0 0040 0040 ENDCHAR STARTCHAR U+CC0D ENCODING 52237 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2240 5540 8940 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CC10 ENCODING 52240 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2240 5540 8940 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CC14 ENCODING 52244 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 5540 8940 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CC1C ENCODING 52252 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2240 5540 8940 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CC1D ENCODING 52253 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2240 5540 8940 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CC21 ENCODING 52257 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2240 5540 8940 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CC22 ENCODING 52258 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 2240 5540 8940 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+CC27 ENCODING 52263 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7740 2240 2240 5540 8940 0040 0700 0000 7FE0 1040 0F80 ENDCHAR STARTCHAR U+CC28 ENCODING 52264 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 0870 0840 1440 1440 2240 4140 0040 ENDCHAR STARTCHAR U+CC29 ENCODING 52265 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 1440 6340 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CC2C ENCODING 52268 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0870 1440 6340 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+CC2E ENCODING 52270 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 1440 6340 0100 27E0 2240 2240 1D80 ENDCHAR STARTCHAR U+CC30 ENCODING 52272 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 0870 1440 6340 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CC38 ENCODING 52280 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 1440 6340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CC39 ENCODING 52281 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 1440 6340 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CC3B ENCODING 52283 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 1440 6340 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+CC3C ENCODING 52284 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 1440 6340 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CC3D ENCODING 52285 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 1440 6340 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CC3E ENCODING 52286 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 1440 6340 0000 3FE0 0100 0680 3860 ENDCHAR STARTCHAR U+CC44 ENCODING 52292 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 0120 7D20 1120 1120 29E0 2920 4520 8320 0120 0120 ENDCHAR STARTCHAR U+CC45 ENCODING 52293 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 2200 FA00 2380 2200 5200 8A00 0000 3F80 ENDCHAR STARTCHAR U+CC48 ENCODING 52296 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 7D20 11E0 1120 2920 4520 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+CC4C ENCODING 52300 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 7D20 11E0 2920 4520 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+CC54 ENCODING 52308 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 7D20 11E0 1120 2920 4520 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+CC55 ENCODING 52309 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 7D20 11E0 1120 2920 4520 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+CC57 ENCODING 52311 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 7D20 11E0 1120 2920 4520 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+CC58 ENCODING 52312 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 7D20 11E0 1120 2920 4520 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+CC59 ENCODING 52313 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 7D20 11E0 1120 2920 4520 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+CC60 ENCODING 52320 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F70 0840 0840 0840 1470 1440 2240 4140 0040 ENDCHAR STARTCHAR U+CC64 ENCODING 52324 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0070 7F40 0870 1440 6340 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CC66 ENCODING 52326 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0070 7F40 0870 1440 6340 0100 27E0 2240 2240 1D80 ENDCHAR STARTCHAR U+CC68 ENCODING 52328 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F70 0840 1470 6340 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CC70 ENCODING 52336 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0070 7F40 0870 1440 6340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CC75 ENCODING 52341 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0070 7F40 0870 1440 6340 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CC98 ENCODING 52376 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 09C0 0840 0840 1440 1440 2240 4140 0040 ENDCHAR STARTCHAR U+CC99 ENCODING 52377 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7FC0 0840 1440 6340 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CC9C ENCODING 52380 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7FC0 0840 1440 6340 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CCA0 ENCODING 52384 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 09C0 1440 6340 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CCA8 ENCODING 52392 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7FC0 0840 1440 6340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CCA9 ENCODING 52393 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7FC0 0840 1440 6340 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CCAB ENCODING 52395 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7FC0 0840 1440 6340 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+CCAC ENCODING 52396 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7FC0 0840 1440 6340 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CCAD ENCODING 52397 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7FC0 0840 1440 6340 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CCB4 ENCODING 52404 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 00A0 7CA0 10A0 13A0 28A0 28A0 44A0 82A0 00A0 00A0 ENDCHAR STARTCHAR U+CCB5 ENCODING 52405 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP 2100 F900 2700 2100 5100 8900 0000 3F80 ENDCHAR STARTCHAR U+CCB8 ENCODING 52408 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7CA0 13A0 10A0 28A0 44A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+CCBC ENCODING 52412 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7CA0 13A0 28A0 44A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+CCC4 ENCODING 52420 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7CA0 13A0 10A0 28A0 44A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+CCC5 ENCODING 52421 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7CA0 13A0 10A0 28A0 44A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+CCC7 ENCODING 52423 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7CA0 13A0 10A0 28A0 44A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+CCC9 ENCODING 52425 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7CA0 13A0 10A0 28A0 44A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+CCD0 ENCODING 52432 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7FC0 0840 0840 09C0 1440 1440 2240 4140 0040 ENDCHAR STARTCHAR U+CCD4 ENCODING 52436 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 01C0 7F40 09C0 1440 6340 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CCE4 ENCODING 52452 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 01C0 7F40 09C0 1440 6340 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CCEC ENCODING 52460 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 00A0 7FA0 10A0 10A0 2BA0 28A0 44A0 82A0 00A0 00A0 ENDCHAR STARTCHAR U+CCF0 ENCODING 52464 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7FA0 10A0 13A0 28A0 44A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+CD01 ENCODING 52481 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7FA0 10A0 13A0 28A0 44A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+CD08 ENCODING 52488 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 0000 7FC0 0400 0A00 1100 2080 4440 0400 FFC0 ENDCHAR STARTCHAR U+CD09 ENCODING 52489 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0400 FFE0 0000 7FC0 0040 0040 ENDCHAR STARTCHAR U+CD0C ENCODING 52492 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0400 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+CD10 ENCODING 52496 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0400 FFE0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+CD18 ENCODING 52504 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0400 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CD19 ENCODING 52505 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0400 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CD1B ENCODING 52507 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0400 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+CD1D ENCODING 52509 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0400 FFE0 0000 3F80 4040 3F80 ENDCHAR STARTCHAR U+CD24 ENCODING 52516 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1840 0040 7E40 1840 2470 2440 4A40 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+CD28 ENCODING 52520 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2270 4140 0840 7F40 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+CD2C ENCODING 52524 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2270 4140 0840 7FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CD39 ENCODING 52537 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2270 4140 0840 7F40 0000 1F80 2040 1F80 ENDCHAR STARTCHAR U+CD5C ENCODING 52572 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1840 0040 7E40 1840 2440 2440 4A40 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+CD60 ENCODING 52576 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2240 4140 0840 7F40 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+CD64 ENCODING 52580 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2240 4140 0840 7FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CD6C ENCODING 52588 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2240 4140 0840 7F40 0000 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CD6D ENCODING 52589 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2240 4140 0840 7F40 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CD6F ENCODING 52591 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2240 4140 0840 7F40 0000 0100 0680 3860 ENDCHAR STARTCHAR U+CD71 ENCODING 52593 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 1440 2240 4140 0840 7F40 0000 1F80 2040 1F80 ENDCHAR STARTCHAR U+CD78 ENCODING 52600 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 0000 7FC0 0400 0A00 1100 2080 5140 1100 FFC0 ENDCHAR STARTCHAR U+CD88 ENCODING 52616 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 1100 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CD94 ENCODING 52628 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0E00 0000 7FC0 0400 1B00 60C0 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+CD95 ENCODING 52629 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0400 7FC0 0040 0040 ENDCHAR STARTCHAR U+CD98 ENCODING 52632 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0400 4400 4000 3FC0 ENDCHAR STARTCHAR U+CD9C ENCODING 52636 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 1100 60C0 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+CDA4 ENCODING 52644 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0400 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CDA5 ENCODING 52645 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 4440 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CDA7 ENCODING 52647 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0400 0200 0D00 70C0 ENDCHAR STARTCHAR U+CDA9 ENCODING 52649 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0400 3F80 4040 3F80 ENDCHAR STARTCHAR U+CDB0 ENCODING 52656 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 1440 6340 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+CDC4 ENCODING 52676 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 7C40 28C0 4440 4440 0040 7F40 0840 0880 1540 2220 ENDCHAR STARTCHAR U+CDCC ENCODING 52684 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP 21 F9 21 51 89 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+CDD0 ENCODING 52688 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 7CA0 29A0 44A0 44A0 00A0 7EA0 10A0 3020 2000 1FE0 ENDCHAR STARTCHAR U+CDE8 ENCODING 52712 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 1440 6340 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+CDEC ENCODING 52716 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 7C40 2840 4440 4440 0040 7FC0 0840 2840 2000 1FC0 ENDCHAR STARTCHAR U+CDF0 ENCODING 52720 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 7C40 2840 4440 3FC0 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CDF8 ENCODING 52728 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 7C40 2840 4440 4440 0040 7FC0 0840 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CDF9 ENCODING 52729 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 7C40 2840 4440 4440 0040 7FC0 2840 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CDFB ENCODING 52731 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 7C40 2840 4440 4440 0040 7FC0 0840 0100 0680 3860 ENDCHAR STARTCHAR U+CDFD ENCODING 52733 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 7C40 2840 4440 4440 0040 7FC0 0840 1F80 2040 1F80 ENDCHAR STARTCHAR U+CE04 ENCODING 52740 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0E00 0000 7FC0 0400 1B00 60C0 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+CE08 ENCODING 52744 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 1100 5100 4000 3FC0 ENDCHAR STARTCHAR U+CE0C ENCODING 52748 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 1100 60C0 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+CE14 ENCODING 52756 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 1100 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CE19 ENCODING 52761 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 1100 3F80 4040 3F80 ENDCHAR STARTCHAR U+CE20 ENCODING 52768 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 0000 7FC0 0400 0A00 1100 2080 4040 0000 FFC0 ENDCHAR STARTCHAR U+CE21 ENCODING 52769 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0000 7FC0 0040 0040 ENDCHAR STARTCHAR U+CE24 ENCODING 52772 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+CE28 ENCODING 52776 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+CE30 ENCODING 52784 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CE31 ENCODING 52785 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CE33 ENCODING 52787 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+CE35 ENCODING 52789 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 7FC0 0A00 1100 60C0 0000 FFE0 0000 3F80 4040 3F80 ENDCHAR STARTCHAR U+CE58 ENCODING 52824 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 0840 0840 1440 1440 2240 4140 0040 ENDCHAR STARTCHAR U+CE59 ENCODING 52825 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 1440 6340 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CE5C ENCODING 52828 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 1440 6340 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CE5F ENCODING 52831 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 1440 6340 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+CE60 ENCODING 52832 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 0840 1440 6340 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CE61 ENCODING 52833 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 0840 1440 6340 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+CE68 ENCODING 52840 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 1440 6340 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CE69 ENCODING 52841 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 1440 6340 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CE6B ENCODING 52843 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 1440 6340 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+CE6D ENCODING 52845 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 0840 1440 6340 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CE74 ENCODING 52852 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 7E40 0270 0440 0440 0840 7040 0040 0040 ENDCHAR STARTCHAR U+CE75 ENCODING 52853 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 0240 0440 7840 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CE78 ENCODING 52856 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 0240 0440 7840 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CE7C ENCODING 52860 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 0240 7C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CE84 ENCODING 52868 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 0240 0440 7840 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CE85 ENCODING 52869 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 0240 0440 7840 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CE87 ENCODING 52871 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 0240 0440 7840 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+CE89 ENCODING 52873 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E70 0240 0440 7840 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CE90 ENCODING 52880 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 0520 7D20 0520 09E0 0920 1120 6120 0120 0120 ENDCHAR STARTCHAR U+CE91 ENCODING 52881 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 0A00 FB80 1200 2200 C200 0000 3F80 ENDCHAR STARTCHAR U+CE94 ENCODING 52884 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 0920 1120 6120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+CE98 ENCODING 52888 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 0520 7920 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+CEA0 ENCODING 52896 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 0920 1120 6120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+CEA1 ENCODING 52897 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 0920 1120 6120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+CEA3 ENCODING 52899 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 0920 1120 6120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+CEA4 ENCODING 52900 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 0920 1120 6120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+CEA5 ENCODING 52901 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0520 7DE0 0920 1120 6120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+CEAC ENCODING 52908 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0270 7E40 0240 0440 0470 0840 7040 0040 0040 ENDCHAR STARTCHAR U+CEAD ENCODING 52909 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 7E40 0270 0440 7840 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CEC1 ENCODING 52929 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0270 7E40 0270 0440 7840 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CEE4 ENCODING 52964 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 7E40 03C0 0440 0440 0840 7040 0040 0040 ENDCHAR STARTCHAR U+CEE5 ENCODING 52965 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 0440 7840 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CEE8 ENCODING 52968 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 0440 7840 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CEEB ENCODING 52971 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 0440 7840 0000 3FC0 2000 2000 1FC0 ENDCHAR STARTCHAR U+CEEC ENCODING 52972 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 7C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CEF4 ENCODING 52980 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 0440 7840 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CEF5 ENCODING 52981 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 0440 7840 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CEF7 ENCODING 52983 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 0440 7840 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+CEF8 ENCODING 52984 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 0440 7840 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CEF9 ENCODING 52985 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7FC0 0240 0440 7840 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CF00 ENCODING 52992 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 09 09 F9 0F 11 11 21 C1 01 01 ENDCHAR STARTCHAR U+CF01 ENCODING 52993 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP F900 0900 FF00 1100 2100 C100 0000 3F80 ENDCHAR STARTCHAR U+CF04 ENCODING 52996 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 08A0 10A0 60A0 0000 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+CF08 ENCODING 53000 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7DA0 04A0 78A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+CF10 ENCODING 53008 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 08A0 10A0 60A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+CF11 ENCODING 53009 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 08A0 10A0 60A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+CF13 ENCODING 53011 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 08A0 10A0 60A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+CF15 ENCODING 53013 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7FA0 08A0 10A0 60A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+CF1C ENCODING 53020 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 03C0 7E40 0240 05C0 0440 0840 7040 0040 0040 ENDCHAR STARTCHAR U+CF20 ENCODING 53024 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 03C0 0440 7840 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CF24 ENCODING 53028 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 03C0 7C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CF2C ENCODING 53036 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 03C0 0440 7840 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CF2D ENCODING 53037 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 03C0 0440 7840 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+CF2F ENCODING 53039 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 03C0 0440 7840 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+CF30 ENCODING 53040 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 03C0 0440 7840 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+CF31 ENCODING 53041 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 03C0 7E40 03C0 0440 7840 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CF38 ENCODING 53048 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 09 0F F9 09 17 11 21 C1 01 01 ENDCHAR STARTCHAR U+CF54 ENCODING 53076 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 0040 7FC0 0840 0840 0840 0800 FFC0 ENDCHAR STARTCHAR U+CF55 ENCODING 53077 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0840 0800 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CF58 ENCODING 53080 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0400 FFE0 0000 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+CF5C ENCODING 53084 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0840 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+CF64 ENCODING 53092 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0840 0800 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+CF65 ENCODING 53093 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0840 0800 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CF67 ENCODING 53095 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0840 0800 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+CF69 ENCODING 53097 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0840 0800 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+CF70 ENCODING 53104 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 7E40 1270 1240 1240 1040 7F40 0040 0040 ENDCHAR STARTCHAR U+CF71 ENCODING 53105 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 1270 1040 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CF74 ENCODING 53108 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0270 0840 7F40 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+CF78 ENCODING 53112 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 1270 7FC0 0000 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CF80 ENCODING 53120 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 1270 1040 7F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+CF85 ENCODING 53125 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 1270 1040 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+CF8C ENCODING 53132 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 02A0 02A0 7EA0 22E0 22A0 22A0 20A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+CFA1 ENCODING 53153 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7CE0 24A0 20A0 7EA0 00A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+CFA8 ENCODING 53160 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 7E40 1240 1240 1240 1040 7F40 0040 0040 ENDCHAR STARTCHAR U+CFB0 ENCODING 53168 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 1240 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+CFC4 ENCODING 53188 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 0040 7FC0 2240 2240 2240 2200 FFC0 ENDCHAR STARTCHAR U+CFE0 ENCODING 53216 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 7FC0 0040 0040 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+CFE1 ENCODING 53217 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+CFE4 ENCODING 53220 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0400 4400 4000 4000 3FC0 ENDCHAR STARTCHAR U+CFE8 ENCODING 53224 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+CFF0 ENCODING 53232 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+CFF1 ENCODING 53233 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+CFF3 ENCODING 53235 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+CFF5 ENCODING 53237 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+CFFC ENCODING 53244 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 0240 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+D000 ENCODING 53248 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7CC0 0440 0040 7F40 0840 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D004 ENCODING 53252 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7CC0 0440 7F40 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D011 ENCODING 53265 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7CC0 0440 0040 7F40 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D018 ENCODING 53272 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 09 F9 09 09 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+D02D ENCODING 53293 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 04A0 7DA0 04A0 00A0 7EA0 10A0 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+D034 ENCODING 53300 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 0240 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+D035 ENCODING 53301 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 0440 0040 7FC0 0840 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D038 ENCODING 53304 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 0440 0040 7FC0 0840 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D03C ENCODING 53308 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 0440 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D044 ENCODING 53316 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 0440 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D045 ENCODING 53317 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 0440 0040 7FC0 0840 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D047 ENCODING 53319 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 0440 0040 7FC0 0840 0200 0500 0880 3060 ENDCHAR STARTCHAR U+D049 ENCODING 53321 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0440 7C40 0440 0040 7FC0 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D050 ENCODING 53328 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 0040 7FC0 0040 0040 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+D054 ENCODING 53332 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 1100 5100 4000 4000 3FC0 ENDCHAR STARTCHAR U+D058 ENCODING 53336 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D060 ENCODING 53344 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+D06C ENCODING 53356 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0040 0040 0040 7FC0 0040 0040 0040 0000 FFC0 ENDCHAR STARTCHAR U+D06D ENCODING 53357 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D070 ENCODING 53360 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0000 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D074 ENCODING 53364 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D07C ENCODING 53372 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+D07D ENCODING 53373 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D081 ENCODING 53377 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0040 7FC0 0040 0000 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+D0A4 ENCODING 53412 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 0240 7E40 0240 0440 0440 0840 7040 0040 0040 ENDCHAR STARTCHAR U+D0A5 ENCODING 53413 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 0440 7840 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D0A8 ENCODING 53416 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 0440 7840 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D0AC ENCODING 53420 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 7C40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D0B4 ENCODING 53428 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 0440 7840 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D0B5 ENCODING 53429 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 0440 7840 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D0B7 ENCODING 53431 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 0440 7840 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+D0B9 ENCODING 53433 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0240 7E40 0240 0440 7840 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D0C0 ENCODING 53440 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 7C40 4070 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+D0C1 ENCODING 53441 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C70 4040 4040 3F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D0C4 ENCODING 53444 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4070 4040 3F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D0C8 ENCODING 53448 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C70 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D0C9 ENCODING 53449 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C70 4040 3F40 0040 3FC0 0440 3C40 2040 1C40 ENDCHAR STARTCHAR U+D0D0 ENCODING 53456 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C70 4040 4040 3F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D0D1 ENCODING 53457 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C70 4040 4040 3F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D0D3 ENCODING 53459 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C70 4040 4040 3F40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+D0D4 ENCODING 53460 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C70 4040 4040 3F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+D0D5 ENCODING 53461 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C70 4040 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D0DC ENCODING 53468 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 4120 7920 4120 41E0 4120 3D20 0120 0120 0120 ENDCHAR STARTCHAR U+D0DD ENCODING 53469 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 8200 F380 8200 8200 7E00 0000 3F80 ENDCHAR STARTCHAR U+D0E0 ENCODING 53472 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 79E0 4120 4120 3F20 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+D0E4 ENCODING 53476 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 79E0 4120 3F20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D0EC ENCODING 53484 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 79E0 4120 4120 3F20 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+D0ED ENCODING 53485 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 79E0 4120 4120 3F20 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+D0EF ENCODING 53487 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 79E0 4120 4120 3F20 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+D0F0 ENCODING 53488 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 79E0 4120 4120 3F20 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+D0F1 ENCODING 53489 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 4120 79E0 4120 4120 3F20 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+D0F8 ENCODING 53496 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4070 7C40 4040 4040 4070 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+D10D ENCODING 53517 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4070 7C40 4070 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D130 ENCODING 53552 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 7DC0 4040 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+D131 ENCODING 53553 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 4040 3F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D134 ENCODING 53556 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 4040 3F40 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D138 ENCODING 53560 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D13A ENCODING 53562 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 3F40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+D140 ENCODING 53568 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 4040 3F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D141 ENCODING 53569 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 4040 3F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D143 ENCODING 53571 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 4040 3F40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+D144 ENCODING 53572 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 4040 3F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+D145 ENCODING 53573 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7DC0 4040 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D14C ENCODING 53580 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 81 81 F1 87 81 81 79 01 01 01 ENDCHAR STARTCHAR U+D14D ENCODING 53581 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP F900 8100 F700 8100 8100 7D00 0000 3F80 ENDCHAR STARTCHAR U+D150 ENCODING 53584 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 7BA0 40A0 40A0 3EA0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+D154 ENCODING 53588 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 7BA0 40A0 3EA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D15C ENCODING 53596 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 7BA0 40A0 40A0 3EA0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+D15D ENCODING 53597 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 7BA0 40A0 40A0 3EA0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+D15F ENCODING 53599 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 7BA0 40A0 40A0 3EA0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+D161 ENCODING 53601 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 40A0 7BA0 40A0 40A0 3EA0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+D168 ENCODING 53608 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 41C0 7C40 4040 41C0 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+D16C ENCODING 53612 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 41C0 7C40 41C0 4040 3F40 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D17C ENCODING 53628 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 41C0 7C40 41C0 4040 3F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+D184 ENCODING 53636 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 81 87 F1 81 87 81 79 01 01 01 ENDCHAR STARTCHAR U+D188 ENCODING 53640 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 43A0 78A0 43A0 40A0 3EA0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+D1A0 ENCODING 53664 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4000 4000 7F80 4000 4000 3FC0 0400 0400 FFC0 ENDCHAR STARTCHAR U+D1A1 ENCODING 53665 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0400 FFE0 0000 7FC0 0040 0040 ENDCHAR STARTCHAR U+D1A4 ENCODING 53668 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0400 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D1A8 ENCODING 53672 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0400 FFE0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D1B0 ENCODING 53680 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0400 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D1B1 ENCODING 53681 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0400 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D1B3 ENCODING 53683 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0400 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+D1B5 ENCODING 53685 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0400 FFE0 0000 3F80 4040 3F80 ENDCHAR STARTCHAR U+D1BA ENCODING 53690 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0400 FFE0 0000 7FC0 1100 7FC0 ENDCHAR STARTCHAR U+D1BC ENCODING 53692 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4070 3E40 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+D1C0 ENCODING 53696 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4070 3E40 0840 7F40 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D1D8 ENCODING 53720 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7EA0 40A0 7CA0 40A0 3EE0 00A0 10A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+D1F4 ENCODING 53748 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4040 3E40 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+D1F8 ENCODING 53752 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3E40 0840 7F40 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D207 ENCODING 53767 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3E40 0840 7F40 0000 0100 0680 3860 ENDCHAR STARTCHAR U+D209 ENCODING 53769 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3E40 0840 7F40 0000 1F80 2040 1F80 ENDCHAR STARTCHAR U+D210 ENCODING 53776 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4000 4000 7F80 4000 4000 3FC0 1100 1100 FFC0 ENDCHAR STARTCHAR U+D22C ENCODING 53804 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 4000 7FC0 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+D22D ENCODING 53805 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0400 7FC0 0040 0040 ENDCHAR STARTCHAR U+D230 ENCODING 53808 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0400 4400 4000 3FC0 ENDCHAR STARTCHAR U+D234 ENCODING 53812 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D23C ENCODING 53820 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0400 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D23D ENCODING 53821 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 4440 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D23F ENCODING 53823 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0400 0200 0D00 70C0 ENDCHAR STARTCHAR U+D241 ENCODING 53825 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0400 3F80 4040 3F80 ENDCHAR STARTCHAR U+D248 ENCODING 53832 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3E40 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+D25C ENCODING 53852 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 78C0 4040 3C40 0040 7F40 0840 0880 1540 2220 ENDCHAR STARTCHAR U+D264 ENCODING 53860 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 81 F1 81 79 01 FD 21 27 21 21 ENDCHAR STARTCHAR U+D280 ENCODING 53888 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3E40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+D281 ENCODING 53889 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 7840 4040 3C40 0040 7FC0 0840 3FC0 0040 0040 ENDCHAR STARTCHAR U+D284 ENCODING 53892 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 7840 4040 3C40 0040 7FC0 0840 2840 2000 1FC0 ENDCHAR STARTCHAR U+D288 ENCODING 53896 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 7840 4040 3FC0 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D290 ENCODING 53904 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 7840 4040 3C40 0040 7FC0 0840 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D291 ENCODING 53905 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 7840 4040 3C40 0040 7FC0 2840 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D295 ENCODING 53909 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 4040 7840 4040 3C40 0040 7FC0 0840 1F80 2040 1F80 ENDCHAR STARTCHAR U+D29C ENCODING 53916 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 4000 7FC0 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+D2A0 ENCODING 53920 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 1100 5100 4000 3FC0 ENDCHAR STARTCHAR U+D2A4 ENCODING 53924 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D2AC ENCODING 53932 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 1100 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D2B1 ENCODING 53937 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 1100 3F80 4040 3F80 ENDCHAR STARTCHAR U+D2B8 ENCODING 53944 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 4000 4000 7F80 4000 4000 3FC0 0000 0000 FFC0 ENDCHAR STARTCHAR U+D2B9 ENCODING 53945 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0000 7FC0 0040 0040 ENDCHAR STARTCHAR U+D2BC ENCODING 53948 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D2BF ENCODING 53951 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0000 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D2C0 ENCODING 53952 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D2C2 ENCODING 53954 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 FFE0 0000 7800 0800 7BC0 4240 3FC0 ENDCHAR STARTCHAR U+D2C8 ENCODING 53960 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D2C9 ENCODING 53961 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D2CB ENCODING 53963 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 4000 7F80 4000 3FC0 0000 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+D2D4 ENCODING 53972 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4040 3E40 0040 0040 7FC0 0040 0040 ENDCHAR STARTCHAR U+D2D8 ENCODING 53976 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3E40 0040 7FC0 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D2DC ENCODING 53980 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3FC0 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D2E4 ENCODING 53988 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3E40 0040 7FC0 0040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D2E5 ENCODING 53989 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3E40 0040 7FC0 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D2F0 ENCODING 54000 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 4040 7C40 4040 4040 4040 3F40 0040 0040 0040 ENDCHAR STARTCHAR U+D2F1 ENCODING 54001 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4040 3F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D2F4 ENCODING 54004 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4040 3F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D2F8 ENCODING 54008 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 3F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D300 ENCODING 54016 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4040 3F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D301 ENCODING 54017 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4040 3F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D303 ENCODING 54019 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4040 3F40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+D305 ENCODING 54021 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 4040 7C40 4040 4040 3F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D30C ENCODING 54028 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 2440 2440 2470 2440 2440 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D30D ENCODING 54029 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 2440 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D30E ENCODING 54030 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 2440 7F40 0000 3FC0 0440 0440 0440 ENDCHAR STARTCHAR U+D310 ENCODING 54032 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2470 2440 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D314 ENCODING 54036 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D316 ENCODING 54038 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 7F40 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+D31C ENCODING 54044 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 2440 7F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D31D ENCODING 54045 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 2440 7F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D31F ENCODING 54047 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 2440 7F40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+D320 ENCODING 54048 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 2440 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+D321 ENCODING 54049 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 2440 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D325 ENCODING 54053 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2470 2440 7F40 0040 3FC0 2000 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D328 ENCODING 54056 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0120 2920 2920 2920 29E0 2920 2920 7F20 0120 0120 ENDCHAR STARTCHAR U+D329 ENCODING 54057 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP FA00 0200 5380 5200 5200 FE00 0000 3F80 ENDCHAR STARTCHAR U+D32C ENCODING 54060 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0120 29E0 2920 2920 7F20 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+D330 ENCODING 54064 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0120 29E0 2920 7F20 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D338 ENCODING 54072 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0120 29E0 2920 2920 7F20 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+D339 ENCODING 54073 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0120 29E0 2920 2920 7F20 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+D33B ENCODING 54075 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0120 29E0 2920 2920 7F20 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+D33C ENCODING 54076 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0120 29E0 2920 2920 7F20 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+D33D ENCODING 54077 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7D20 0120 29E0 2920 2920 7F20 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+D344 ENCODING 54084 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 2470 2440 2440 2440 2470 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D345 ENCODING 54085 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0070 2440 2470 2440 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D37C ENCODING 54140 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 2440 25C0 2440 2440 2440 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D37D ENCODING 54141 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 25C0 2440 2440 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D380 ENCODING 54144 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 25C0 2440 2440 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D384 ENCODING 54148 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 25C0 2440 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D38C ENCODING 54156 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 25C0 2440 2440 7F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D38D ENCODING 54157 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 25C0 2440 2440 7F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D38F ENCODING 54159 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 25C0 2440 2440 7F40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+D390 ENCODING 54160 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 25C0 2440 2440 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+D391 ENCODING 54161 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 25C0 2440 2440 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D398 ENCODING 54168 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 01 51 51 57 51 51 51 FD 01 01 ENDCHAR STARTCHAR U+D399 ENCODING 54169 SWIDTH 935 0 DWIDTH 12 0 BBX 9 8 1 1 BITMAP F900 0100 5700 5100 5100 FD00 0000 3F80 ENDCHAR STARTCHAR U+D39C ENCODING 54172 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 00A0 2BA0 28A0 28A0 7EA0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+D3A0 ENCODING 54176 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 00A0 2BA0 28A0 7EA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D3A8 ENCODING 54184 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 00A0 2BA0 28A0 28A0 7EA0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+D3A9 ENCODING 54185 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 00A0 2BA0 28A0 28A0 7EA0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+D3AB ENCODING 54187 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 00A0 2BA0 28A0 28A0 7EA0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+D3AD ENCODING 54189 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 00A0 2BA0 28A0 28A0 7EA0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+D3B4 ENCODING 54196 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 25C0 2440 2440 25C0 2440 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D3B8 ENCODING 54200 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 01C0 2440 25C0 2440 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D3BC ENCODING 54204 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 01C0 2440 25C0 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D3C4 ENCODING 54212 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 01C0 2440 25C0 2440 7F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D3C5 ENCODING 54213 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 01C0 2440 25C0 2440 7F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D3C8 ENCODING 54216 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 01C0 2440 25C0 2440 7F40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+D3C9 ENCODING 54217 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 01C0 2440 25C0 2440 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D3D0 ENCODING 54224 SWIDTH 935 0 DWIDTH 12 0 BBX 8 11 1 -2 BITMAP F9 01 57 51 51 57 51 51 FD 01 01 ENDCHAR STARTCHAR U+D3D8 ENCODING 54232 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 03A0 28A0 2BA0 7EA0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D3E1 ENCODING 54241 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 03A0 28A0 2BA0 28A0 7EA0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+D3E3 ENCODING 54243 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7CA0 03A0 28A0 2BA0 28A0 7EA0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+D3EC ENCODING 54252 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0000 1100 1100 1100 7FC0 0000 0400 0400 FFC0 ENDCHAR STARTCHAR U+D3ED ENCODING 54253 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0400 FFE0 0000 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D3F0 ENCODING 54256 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0000 0400 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D3F4 ENCODING 54260 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 7FC0 0400 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D3FC ENCODING 54268 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0400 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+D3FD ENCODING 54269 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0400 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D3FF ENCODING 54271 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0400 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+D401 ENCODING 54273 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0400 FFE0 0000 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+D408 ENCODING 54280 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 2240 2240 7F70 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+D41D ENCODING 54301 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 2440 7F70 0840 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D440 ENCODING 54336 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 2240 2240 7F40 0040 0840 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+D444 ENCODING 54340 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 2440 7F40 0840 7F40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D45C ENCODING 54364 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0000 1100 1100 1100 7FC0 0000 1100 1100 FFC0 ENDCHAR STARTCHAR U+D460 ENCODING 54368 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0000 1100 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D464 ENCODING 54372 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 7FC0 1100 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D46D ENCODING 54381 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 1100 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D46F ENCODING 54383 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 1100 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+D478 ENCODING 54392 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 1100 7FC0 0000 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+D479 ENCODING 54393 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 0400 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D47C ENCODING 54396 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 0000 FFE0 4400 4400 4000 3FC0 ENDCHAR STARTCHAR U+D47F ENCODING 54399 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 0400 7FC0 4000 4000 3FC0 ENDCHAR STARTCHAR U+D480 ENCODING 54400 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 7FC0 0000 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D482 ENCODING 54402 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 7FC0 0000 FFE0 0400 7BC0 0A40 7A40 4240 3FC0 ENDCHAR STARTCHAR U+D488 ENCODING 54408 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 0400 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+D489 ENCODING 54409 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 0400 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D48B ENCODING 54411 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 0400 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+D48D ENCODING 54413 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 0400 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+D494 ENCODING 54420 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 7E40 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+D4A9 ENCODING 54441 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0040 28C0 7C40 0040 7F40 0840 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D4CC ENCODING 54476 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 7E40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+D4D0 ENCODING 54480 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0040 2840 7C40 0040 7FC0 0840 2840 2000 2000 1FC0 ENDCHAR STARTCHAR U+D4D4 ENCODING 54484 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 2840 7C40 0040 0040 7FC0 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D4DC ENCODING 54492 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0040 2840 7C40 0040 7FC0 0840 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D4DF ENCODING 54495 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7C40 0040 2840 7C40 0040 7FC0 0840 0200 0500 0880 3060 ENDCHAR STARTCHAR U+D4E8 ENCODING 54504 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 1100 7FC0 0000 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+D4EC ENCODING 54508 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 0000 FFE0 5100 5100 4000 3FC0 ENDCHAR STARTCHAR U+D4F0 ENCODING 54512 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 7FC0 0000 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D4F8 ENCODING 54520 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 1100 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+D4FB ENCODING 54523 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 1100 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+D4FD ENCODING 54525 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 0000 1100 7FC0 0000 FFE0 1100 3F80 4040 4040 3F80 ENDCHAR STARTCHAR U+D504 ENCODING 54532 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 7FC0 0000 1100 1100 1100 7FC0 0000 0000 0000 FFC0 ENDCHAR STARTCHAR U+D508 ENCODING 54536 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0000 0000 FFE0 4000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D50C ENCODING 54540 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 7FC0 0000 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D514 ENCODING 54548 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0000 FFE0 0000 7FC0 4040 4040 3FC0 ENDCHAR STARTCHAR U+D515 ENCODING 54549 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0000 FFE0 0000 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D517 ENCODING 54551 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7FC0 1100 1100 7FC0 0000 FFE0 0000 0400 0A00 1100 60C0 ENDCHAR STARTCHAR U+D53C ENCODING 54588 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7F40 0040 2440 2440 2440 2440 2440 7FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D53D ENCODING 54589 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 2440 7F40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D540 ENCODING 54592 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 2440 7F40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D544 ENCODING 54596 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 7F40 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D54C ENCODING 54604 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 2440 7F40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D54D ENCODING 54605 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 2440 7F40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D54F ENCODING 54607 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 2440 7F40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+D551 ENCODING 54609 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 7E40 0040 2440 2440 2440 7F40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D558 ENCODING 54616 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 0040 FF40 0040 3C70 4240 4240 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+D559 ENCODING 54617 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2470 4240 4240 3C40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D55C ENCODING 54620 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2440 4270 4240 3C40 0040 2040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D560 ENCODING 54624 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4470 4440 3840 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D565 ENCODING 54629 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4470 4440 3840 0040 3BC0 0A00 3BC0 2200 1FC0 ENDCHAR STARTCHAR U+D568 ENCODING 54632 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2470 4240 4240 3C40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D569 ENCODING 54633 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2470 4240 4240 3C40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D56B ENCODING 54635 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2470 4240 4240 3C40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+D56D ENCODING 54637 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2470 4240 4240 3C40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D574 ENCODING 54644 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3920 0120 FF20 0120 3920 45E0 4520 4520 3920 0120 0120 ENDCHAR STARTCHAR U+D575 ENCODING 54645 SWIDTH 935 0 DWIDTH 12 0 BBX 10 8 0 1 BITMAP 1100 FF00 45C0 4500 3900 0100 0000 1FC0 ENDCHAR STARTCHAR U+D578 ENCODING 54648 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 FF20 45E0 4520 3920 0120 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+D57C ENCODING 54652 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 FF20 45E0 4520 3920 0120 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D584 ENCODING 54660 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 FF20 45E0 4520 3920 0120 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+D585 ENCODING 54661 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 FF20 45E0 4520 3920 0120 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+D587 ENCODING 54663 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 FF20 45E0 4520 3920 0120 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+D588 ENCODING 54664 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 FF20 45E0 4520 3920 0120 0000 0840 0840 14A0 2310 ENDCHAR STARTCHAR U+D589 ENCODING 54665 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1120 FF20 45E0 4520 3920 0120 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+D590 ENCODING 54672 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 0040 FF70 0040 3C40 4240 4270 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+D5A5 ENCODING 54693 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF70 2440 4270 4240 3C40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D5C8 ENCODING 54728 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 0040 FF40 01C0 3C40 4240 4240 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+D5C9 ENCODING 54729 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 25C0 4240 4240 3C40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D5CC ENCODING 54732 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 25C0 4240 4240 3C40 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D5D0 ENCODING 54736 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 45C0 4440 3840 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D5D2 ENCODING 54738 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 45C0 4440 3840 0040 3DC0 0540 3D40 2140 1FC0 ENDCHAR STARTCHAR U+D5D8 ENCODING 54744 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 25C0 4240 4240 3C40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D5D9 ENCODING 54745 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 25C0 4240 4240 3C40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D5DB ENCODING 54747 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 25C0 4240 4240 3C40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+D5DD ENCODING 54749 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 25C0 4240 4240 3C40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D5E4 ENCODING 54756 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 00A0 FEA0 00A0 3BA0 44A0 44A0 44A0 38A0 00A0 00A0 ENDCHAR STARTCHAR U+D5E5 ENCODING 54757 SWIDTH 935 0 DWIDTH 12 0 BBX 10 8 0 1 BITMAP 1080 FE80 4780 4480 3880 0080 0000 1FC0 ENDCHAR STARTCHAR U+D5E8 ENCODING 54760 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 47A0 44A0 38A0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+D5EC ENCODING 54764 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 47A0 44A0 38A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D5F4 ENCODING 54772 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 47A0 44A0 38A0 00A0 0000 3FE0 2020 2020 1FE0 ENDCHAR STARTCHAR U+D5F5 ENCODING 54773 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 47A0 44A0 38A0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+D5F7 ENCODING 54775 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 47A0 44A0 38A0 00A0 0000 0080 0080 0340 1C30 ENDCHAR STARTCHAR U+D5F9 ENCODING 54777 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 47A0 44A0 38A0 00A0 0000 1FC0 2020 2020 1FC0 ENDCHAR STARTCHAR U+D600 ENCODING 54784 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 0040 FFC0 0040 3C40 43C0 4240 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+D601 ENCODING 54785 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FFC0 2440 43C0 4240 3C40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D604 ENCODING 54788 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FFC0 2440 43C0 4240 3C40 0000 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D608 ENCODING 54792 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FFC0 4440 45C0 3840 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D610 ENCODING 54800 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FFC0 2440 43C0 4240 3C40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D611 ENCODING 54801 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FFC0 2440 43C0 4240 3C40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D613 ENCODING 54803 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FFC0 2440 43C0 4240 3C40 0000 0200 0500 0880 3060 ENDCHAR STARTCHAR U+D614 ENCODING 54804 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FFC0 2440 43C0 4240 3C40 0000 0880 0880 1540 2220 ENDCHAR STARTCHAR U+D615 ENCODING 54805 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FFC0 2440 43C0 4240 3C40 0000 1F80 2040 2040 1F80 ENDCHAR STARTCHAR U+D61C ENCODING 54812 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 00A0 FFA0 00A0 38A0 47A0 44A0 44A0 38A0 00A0 00A0 ENDCHAR STARTCHAR U+D620 ENCODING 54816 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FFA0 44A0 47A0 38A0 00A0 0020 2000 2000 2000 1FE0 ENDCHAR STARTCHAR U+D624 ENCODING 54820 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FFA0 44A0 47A0 38A0 00A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D62D ENCODING 54829 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FFA0 44A0 47A0 38A0 00A0 0000 2020 3FE0 2020 1FE0 ENDCHAR STARTCHAR U+D638 ENCODING 54840 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 0000 FFC0 1100 2080 2080 1F00 0400 0400 FFC0 ENDCHAR STARTCHAR U+D639 ENCODING 54841 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 0000 7FC0 0040 0040 ENDCHAR STARTCHAR U+D63C ENCODING 54844 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D640 ENCODING 54848 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D645 ENCODING 54853 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 0A00 7B80 4200 3FC0 ENDCHAR STARTCHAR U+D648 ENCODING 54856 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D649 ENCODING 54857 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D64B ENCODING 54859 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+D64D ENCODING 54861 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 0000 3F80 4040 3F80 ENDCHAR STARTCHAR U+D651 ENCODING 54865 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0400 FFE0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+D654 ENCODING 54868 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 1440 2270 2240 1C40 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+D655 ENCODING 54869 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2270 1C40 0840 7F40 0000 3FC0 0040 0040 ENDCHAR STARTCHAR U+D658 ENCODING 54872 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2270 1C40 0840 7F40 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D65C ENCODING 54876 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2270 1C40 0840 7FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D667 ENCODING 54887 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2270 1C40 0840 7F40 0000 0100 0680 3860 ENDCHAR STARTCHAR U+D669 ENCODING 54889 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2270 1C40 0840 7F40 0000 1F80 2040 1F80 ENDCHAR STARTCHAR U+D670 ENCODING 54896 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 38A0 00A0 FEA0 28A0 44E0 44A0 38A0 10A0 7EA0 00A0 00A0 ENDCHAR STARTCHAR U+D671 ENCODING 54897 SWIDTH 935 0 DWIDTH 12 0 BBX 10 9 0 0 BITMAP 1080 FE80 44C0 4480 3880 1080 7E80 0000 3FC0 ENDCHAR STARTCHAR U+D674 ENCODING 54900 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 44E0 44A0 38A0 10A0 7EA0 0020 2000 2000 1FE0 ENDCHAR STARTCHAR U+D683 ENCODING 54915 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 44E0 44A0 38A0 10A0 7EA0 0000 0100 0680 3860 ENDCHAR STARTCHAR U+D685 ENCODING 54917 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 44E0 44A0 38A0 10A0 7EA0 0000 1FC0 2020 1FC0 ENDCHAR STARTCHAR U+D68C ENCODING 54924 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 1440 2240 2240 1C40 0840 7F40 0040 0040 ENDCHAR STARTCHAR U+D68D ENCODING 54925 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0840 7F40 0000 3FC0 0040 0040 ENDCHAR STARTCHAR U+D690 ENCODING 54928 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0840 7F40 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D694 ENCODING 54932 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0840 7FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D69D ENCODING 54941 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0840 7F40 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D69F ENCODING 54943 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0840 7F40 0000 0100 0680 3860 ENDCHAR STARTCHAR U+D6A1 ENCODING 54945 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0840 7F40 0000 1F80 2040 1F80 ENDCHAR STARTCHAR U+D6A8 ENCODING 54952 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 0000 FFC0 1100 2080 2080 1F00 1100 1100 FFC0 ENDCHAR STARTCHAR U+D6AC ENCODING 54956 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 1100 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D6B0 ENCODING 54960 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 1100 FFE0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D6B9 ENCODING 54969 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 1100 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D6BB ENCODING 54971 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 1100 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+D6C4 ENCODING 54980 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1F00 0000 FFE0 2080 2080 1F00 FFE0 0400 0400 0400 0400 ENDCHAR STARTCHAR U+D6C5 ENCODING 54981 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 0400 7FC0 0040 0040 ENDCHAR STARTCHAR U+D6C8 ENCODING 54984 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 0400 4400 4000 3FC0 ENDCHAR STARTCHAR U+D6CC ENCODING 54988 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 1F00 FFE0 0400 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D6D1 ENCODING 54993 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 1F00 FFE0 0400 7BC0 0A00 7B80 4200 3FC0 ENDCHAR STARTCHAR U+D6D4 ENCODING 54996 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 0400 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D6D7 ENCODING 54999 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 0400 0200 0D00 70C0 ENDCHAR STARTCHAR U+D6D9 ENCODING 55001 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 0400 3F80 4040 3F80 ENDCHAR STARTCHAR U+D6E0 ENCODING 55008 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0040 7F40 0840 0BC0 0840 0840 ENDCHAR STARTCHAR U+D6E4 ENCODING 55012 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 44C0 4440 3840 0040 7F40 0840 2840 2000 1FC0 ENDCHAR STARTCHAR U+D6E8 ENCODING 55016 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 44C0 3840 7F40 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D6F0 ENCODING 55024 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 44C0 4440 3840 0040 7F40 0840 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D6F5 ENCODING 55029 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 44C0 4440 3840 0040 7F40 0840 1F80 2040 1F80 ENDCHAR STARTCHAR U+D6FC ENCODING 55036 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 44A0 44A0 38A0 00A0 7EA0 10A0 13A0 10A0 10A0 ENDCHAR STARTCHAR U+D6FD ENCODING 55037 SWIDTH 935 0 DWIDTH 12 0 BBX 10 9 0 0 BITMAP 1080 FE80 4580 4480 3880 0080 7E80 1080 3FC0 ENDCHAR STARTCHAR U+D700 ENCODING 55040 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 45A0 44A0 38A0 00A0 7EA0 10A0 3020 2000 1FE0 ENDCHAR STARTCHAR U+D704 ENCODING 55044 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 45A0 44A0 3EA0 10A0 3FE0 0020 3FE0 2000 1FE0 ENDCHAR STARTCHAR U+D711 ENCODING 55057 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 10A0 FEA0 45A0 44A0 38A0 00A0 7EA0 10A0 1FC0 2020 1FC0 ENDCHAR STARTCHAR U+D718 ENCODING 55064 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0040 7F40 0840 0840 0840 0840 ENDCHAR STARTCHAR U+D719 ENCODING 55065 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4440 4440 3840 0040 7FC0 0840 3FC0 0040 0040 ENDCHAR STARTCHAR U+D71C ENCODING 55068 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4440 4440 3840 0040 7FC0 0840 2840 2000 1FC0 ENDCHAR STARTCHAR U+D720 ENCODING 55072 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4440 3840 7FC0 0840 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D728 ENCODING 55080 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4440 4440 3840 0040 7FC0 0840 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D729 ENCODING 55081 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4440 4440 3840 0040 7FC0 2840 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D72B ENCODING 55083 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4440 4440 3840 0040 7FC0 0840 0100 0680 3860 ENDCHAR STARTCHAR U+D72D ENCODING 55085 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4440 4440 3840 0040 7FC0 0840 1F80 2040 1F80 ENDCHAR STARTCHAR U+D734 ENCODING 55092 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1F00 0000 FFE0 2080 2080 1F00 FFE0 1100 1100 1100 1100 ENDCHAR STARTCHAR U+D735 ENCODING 55093 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 1100 7FC0 0040 0040 ENDCHAR STARTCHAR U+D738 ENCODING 55096 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 1100 5100 4000 3FC0 ENDCHAR STARTCHAR U+D73C ENCODING 55100 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 1F00 FFE0 1100 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D744 ENCODING 55108 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 1100 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D747 ENCODING 55111 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 1100 0200 0D00 70C0 ENDCHAR STARTCHAR U+D749 ENCODING 55113 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 1100 2080 1F00 0000 FFE0 1100 3F80 4040 3F80 ENDCHAR STARTCHAR U+D750 ENCODING 55120 SWIDTH 935 0 DWIDTH 12 0 BBX 10 10 0 -1 BITMAP 1F00 0000 FFC0 1100 2080 2080 1F00 0000 0000 FFC0 ENDCHAR STARTCHAR U+D751 ENCODING 55121 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0000 FFE0 0000 7FC0 0040 0040 ENDCHAR STARTCHAR U+D754 ENCODING 55124 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0000 FFE0 0000 4000 4000 3FC0 ENDCHAR STARTCHAR U+D756 ENCODING 55126 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0000 FFE0 0080 47E0 4240 3980 ENDCHAR STARTCHAR U+D757 ENCODING 55127 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0000 FFE0 0000 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D758 ENCODING 55128 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 FFE0 0000 7FC0 0040 7FC0 4000 3FC0 ENDCHAR STARTCHAR U+D759 ENCODING 55129 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 FFE0 0000 7BC0 0840 7840 4040 3C40 ENDCHAR STARTCHAR U+D760 ENCODING 55136 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0000 FFE0 0000 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D761 ENCODING 55137 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0000 FFE0 4040 7FC0 4040 3FC0 ENDCHAR STARTCHAR U+D763 ENCODING 55139 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0000 FFE0 0000 0400 1B00 60C0 ENDCHAR STARTCHAR U+D765 ENCODING 55141 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 1F00 0000 FFE0 0000 3F80 4040 3F80 ENDCHAR STARTCHAR U+D769 ENCODING 55145 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0400 FFE0 2080 2080 FFE0 0000 7FC0 4000 7F80 4000 3FC0 ENDCHAR STARTCHAR U+D76C ENCODING 55148 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1C40 0040 7F40 1440 2240 2240 1C40 0040 7FC0 0040 0040 ENDCHAR STARTCHAR U+D770 ENCODING 55152 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0040 7FC0 0040 2000 2000 1FC0 ENDCHAR STARTCHAR U+D774 ENCODING 55156 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 7FC0 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D77C ENCODING 55164 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0040 7FC0 0040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D77D ENCODING 55165 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0040 7FC0 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D781 ENCODING 55169 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 0840 7F40 2240 2240 1C40 0040 7FC0 0040 1F80 2040 1F80 ENDCHAR STARTCHAR U+D788 ENCODING 55176 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 3C40 0040 FF40 0040 3C40 4240 4240 4240 3C40 0040 0040 ENDCHAR STARTCHAR U+D789 ENCODING 55177 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2440 4240 4240 3C40 0000 3FC0 0040 0040 0040 ENDCHAR STARTCHAR U+D78C ENCODING 55180 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2440 4240 4240 3C40 0040 2000 2000 2000 1FC0 ENDCHAR STARTCHAR U+D790 ENCODING 55184 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FE40 4440 4440 3840 0040 3FC0 0040 3FC0 2000 1FC0 ENDCHAR STARTCHAR U+D798 ENCODING 55192 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2440 4240 4240 3C40 0000 3FC0 2040 2040 1FC0 ENDCHAR STARTCHAR U+D799 ENCODING 55193 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2440 4240 4240 3C40 0000 2040 3FC0 2040 1FC0 ENDCHAR STARTCHAR U+D79B ENCODING 55195 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2440 4240 4240 3C40 0000 0100 0280 0C40 3020 ENDCHAR STARTCHAR U+D79D ENCODING 55197 SWIDTH 935 0 DWIDTH 12 0 BBX 10 11 0 -2 BITMAP 1040 FF40 2440 4240 4240 3C40 0000 1F80 2040 2040 1F80 ENDCHAR ENDFONT simutrans-124.5/simutrans/history.txt000066400000000000000000014651151520675460500200700ustar00rootroot00000000000000Release of 124.5 (r11993 on 31-May-2026): FIX: (poppo) wayobj toll on other people's way and their removal CHG: (Nazalassa) combobox only searchable if not fitting on screen, "Type to search ..." shown then CHG: Stops get an orange/red status colour only when satisfaction (ratio of happy / (happy+unhappy)) is below 90/75% FIX: (janry+prissi) assertion in debug mode when deleting multitile buildings during city removal ADD: (Nazalassa) button to allow signals to pass convois in reverse CHG: parse script data in scenario files for system calls (manipulated savegames only) FIX: Wrong signal direction overlay arrows for signals on tiles with two ways (crossings, tram tracks) FIX: (janry) Crash when server replies with invalid or corrupted Content-Length header field FIX: shift the special keycodes to private unicode range to allow for latin2 characters FIX: Wrong generation of lakes during map generation when groundwater level is not 0 FIX: powerlines did not correctly calculated routing costs (exceeded max_route_steps after 40-50 tiles) CHG: (poppo) producing factories can also generate electricity (use with care) ADD: Optional 'electricity_producer' flag (can be 0 or 1) for factories to override electricity producer status based on object name ADD: (makie) more settings for inital climates in simuconf.tab FIX: (janry) admin packets only read or write for admin tool/server FIX: drive again to the end of the platform after choose signal selection FIX: pak specific config in user_dir/addons/PAK_NAM/config/simuconf.tab for reset extended settings FIX: (janry+prissi) new simple tool work_world for creation/loading/saving maps to avoid doing this during a step which may lead to crashes ADD: (nazalassa) permissions for halts, to let other players serve specific halts CHG: public player with built furts across rivers even for faster roads as long as there is one with max_speed of at least 25 km/h for road CHG: link factory tool inaccessible when factories are cross-connected by default CHG: (janry+prissi) pak loading with more checks against broken pak files and up to 33% faster Release of 124.4.1 (r11930 on 18-Apr-2026): CHG: reset ingame names to current language when startign a new map and expose the name language via the translator FIX: crash with winter graphics on bridges without winter graphics ADD: preparation for runttime switch of rendering engine FIX: capping factory mail and pax demand at (2^23-1)/9 = 932067 to avoid overflow issues Release of 124.4 (r11919 on 4-Apr-2026): FIX: stations on bridge heads drawn too low ADD: new freight sorting order "via owner": groups goods by owner of their next transfer stop and sort by amount (also change order a little and fixed several inconsitencies: Now all windows show same ordering) ADD: (Nazalassa+prissi) new freight sorting order "connection": groups goods by lines of their next stop and sort by amount (not available for convois, of course) CHG: Help texts will be first searched in pak directory FIX: could built powerlines under bridges by just building a zero length power bridge starting under a bridge ADD: New simple tool 43 to highlight single ribi ways CHG: Pause tool unpauses server waiting for clients FIX: no new city buildings were built when exceeded 2x1 size (only renovated) FIX: Missing collision check for water factories FIX: Crash when screenshotting in-game window with non-0 position FIX: Graphical glitch when dragging the forest tool across slopes if pakset does not have double grounds FIX: Possible save file corruption when reverting to default settings using the settings window FIX: single way tile did not force bridge direction FIX: multitile city building totally renovated, 3x faster city growth (at the start of the map in debug mode) ADD: (mostly Yona-TYT) flowtext recognised img tag. For now only tags "#?XXX" are supported, where ? is one of these letter GSDTW and XXX a number. G is a general tool icon, S and simple tool, D is dialog tool, T a toolbar and W and window gadget. CHG: default signal spacing for dragging to 5 (as 4 and smaller can slow down trains faster than 200 km/h) FIX: Public player cannot replace wayobjs of other players FIX: Zoom in immediately followed by zoom out if SDL raises mouse wheel event with 0 vertical scroll amount FIX: Do not print a warning, when replacing items by compat.tab FIX: 'makeobj list' cannot print properties of factory nodes FIX: (Nazalassa) Public unread message counter is increased for private messages FIX: (Nazalassa) Ticker messages do not respect player colours when the ticker is opened FIX: (Nazalassa) Cannot build tunnels directly above sloped tunnels if way_height_clearance=1 FIX: (Nazalassa) Cursors ignore player changes FIX: Crash when changing the sort mode of a halt while a convoi window is open ADD: (Nazalassa) Sound can now also be loaded from pakset addon directory FIX: (Nazalassa) Players can change nickname to one that is already in use FIX: Maintenance of roadsigns and signals was not booked FIX: (Nazalassa+prissi) toolbars can be automatically stacked again (option in simuconf.tab) FIX: Finance windows did not show powerline revenue as all revenue ADD: (FrankP) new construction sites for res, com, ind as index 7, 8 and 9 CHG: Increase maximum loading time of vehicles to 2^31-1 ms ADD: Makeobj now supports named directions for sloped way images (imageup[w]=...) FIX: Wrong expansion of parameterized image lists with no-ribi image (e.g. image[-,nesw]=...) FIX: longblock signal reservation broken FIX: wrong smoketile offsets outside factories autocorrecting CHG: Unified handling of pak set dirs and names CHG: Combobox try to show as much entries as possible ADD: (Nazalassa) bridge renovation when building on an existing bridge FIX: tunnel route search suceeded even if z coordinates of portals did not match FIX: overhauled townhall road building to cope with difficult terrain and avoid illegal tiles CHG: cities try harder to build slopes for expansion even in mountanious sites FIX: citybuildings no longer destroy power lines (version 2) FIX: no longer crash when city grows into fields CHG: test renovation before new buildings and use homeless and jobless numbers to decide to extend town borders FIX: bridge removal costs CHG: Show obsolete shows also distribution_weight=0 factories in the editor window FIX: Not allowed for powerbridges to end on ways ADD: Retain message of the day in servergames. Can be reshown in the game info/network dialog FIX: (poppo) trains could not choose if electrification was not to the end of track CHG: trains with both diesel and electric engine can go on non-electricfied track, but run without electric power there CHG: (k47) stations names drawn later in minimap FIX: (k47) world borders somethimes not correctly displayed FIX: city connects correct over slopes but not to depots CHG: if defined USE_TOWN_ROAD_BUILDER_TOOL the cityroad building tool will use the internal tool of the city (default off) ADD: new flag for bridges and ways in makeobj 60.9 "clip_below" Default is (1) but for slim structures (e.g. powerline) it could be set to zero for nicer display of structures below CHG: (poppo) jump tool now allows also for z coordinaate FIX: (poppo) last tool not show when too many tools to fit ADD: searchable comboboxes; for editing click twice CHG: Stops that are not connected to any other stops now have a blue status colour FIX: Fatal error message when building a city house that was copied using the pipette tool CHG: (poppo) extended selection of way resp. their obj with CNTRL and SHIFT and pipette tool ADD: new settings cost_kw_per_credit (default was 512) to change revenue from power. Smaller numbers more income FIX: Corrected format of transformer tool icon tooltip FIX: Inline tool icons change size when zooming in or out and scaled tool icons are enabled FIX: show convoi (and line) name when permanent tooltips are enabled for convois FIX: not connection to tunnels, bridges and elevated ways when extending city streets FIX: Overcrowded stops can cause unhappy return passengers in unconnected networks FIX: Factory editor could not select water factories from climate selection FIX: Transformers get removed by factory field expansion CHG: sound loudness slider now follows a x^4 curve, approximates an exponential loudness control for finer adjustments FIX: Crash when a scenario references a translation file which is actually a directory FIX: Topspeed replacement with city roads was broken. Multitle buildings also may have the roads around them never fully renovated Release of 124.3.1 (r11671 on 5-Apr-2025): CHG: option for pakset dump versus all other debug by appending 'p' to the debug level, e.g. "-debug 0p" to show only pak details to declutter server logs CHG: tram system type way can be laid over other tracks as well (like pak192 narrowgauge over normal rails) CHG: bridge builder is now only a two click tool (total renewal) Can again connect to complex ramps on elevated ways or cross rivers automatically FIX: Players gain money by building long stretches of expensive tunnels FIX: Pak files cannot represent cost of vehicles exceeding 2^32-1 Simu-cents FIX: Pak files cannot represent runningcost of vehicles exceeding 2^16-1 Simu-cents FIX: Pak files cannot represent fixed cost of vehicles exceeding 2^32-1 Simu-cents FIX: makro building with way order and stations extensions fixed FIX: Freelist allocated objects (tiles, ways, trees etc.) use 3x more memory than necessary FIX: Do not change underground mode when scrolling from the minimap ADD: Monthly maintenance for roadsigns and signals (maintenance=1234 in dat file) FIX: "Not a socket" error when server does not reply to initial connection attempt within 2 seconds on Windows FIX: Increased maximum possible cost, runningcost, and fixed cost of all pak objects (where applicable) to 2^63-1 Simu-cents to prevent overflows during pak creation FIX: Completely updated city renovation logic, now goes forward to multitile and back again FIX: bridge during town growth were built even if the inital test failed FIX: less moving townhalls and not builging townhalls over roads with asymmetric sizes CHG: with pause setting, server saves game after going to pause => immediately able to fulfill join requests and more current savegames on crashing servers ADD: CURRENCY is now translateable to a local currency (no conversions though) and can be also modified by the user FIX: make sure, the pak path and working path are really different Release of 124.3 (r11590 on 10-Jan-2025): FIX: open schedule get applied during rw (reload, quit, change language), line window crashes CHG: no way foreground drawn inside stations and depots (and other buildings) FIX: loading of roadsings broken for player >=8 and old versions CHG: create lost players during game loading to recover more from broken savegames FIX: handle focussed objects in tab correctly (like comboboxes) ADD: (from simutrans OTPR) tool 44 for generating macro building scripts including terrafroming and station and extension buildings (rebuilding may fail of connection station tiles had been deleted or are outside of the marked area) CHG: squirrel script can now also include any file in the script program directory ADD: pipette tool (started by ceeac, extended to copy and check for more objects) FIX: convoi length bar not so long to need immediately a scrollbar ADD: manually increase tool icon size in display settings window FIX: keep current tile's climate on lower/raise if climate mode is humidity based ADD: error message if digging below water table ADD: scenario can now allow and forbid specific way tools (also sped up the forbid list processing) CHG: Making the road in front of the city hall one tile longer to each side, so city growth works with any set cityrules FIX: also connect bridges to tunnels in pak128 double slope situations FIX: Station rotation preview ignored active player (always showed player 0 colours) FIX: if a convoi served a station but did not load anything (maybe full), another convoi did not check this stations and could get stuck even with waiting for zero load ADD: allow cubic rule that overrideds any forbidden cubic rules and explicit clear rules calls FIX: screnario allow and forbid rules were handling wildcard badly. Also, ignore_wt should be used for ignore this parameter (no enforced in rule creation) ADD: clear rules of a single player FIX: Possible crash when deleting stops during connection recalculation on large maps ADD: scaling of icon size is now in percent to be independent from actual icon sizes (icon_scaling = 100) FIX: Failed to find city- or streetlists for paksets installed in non-default locations (e.g. simutrans/paksets/) FIX: Removal tool removes powerlines of other players FIX: Player colours of scripted AIs appear to revert to the default colours on reload FIX: Tree distribution setting reverts to random when trees are disabled during new world generation FIX: some tools were not added in all neccesary places CHG: hm_city_tl, hw_way_tl, hm_house_tl, hm_set_owner_tl accepts now also coord instead coord3d Release of 124.2.2 (r11400 on 2-Sep-2024): ADD: Support for through depots (dims 2) FIX: proper building of parallel ways also for elevated ways FIX: no public chat after private FIX: halt are properly reconnected FIX: if convoi load in parralele without minimum load, the second convoi could leave empty FIX: again retire convois when empty FIX: obj_count() did not count ways and was a relict that may have caused rare strange errors in other places FIX: Longstanding bug with the size of scrollpanes with horizontal sliders FIX: company messages again, properly show messages in ticker, open chatframe for viewing on ticker click CHG: packing planqudrat_t reduces size by 2 byte (32 bit 12->10) or 6 byte (24->18) and accelerates sync_step by 10-20% (only on Intel on GnuC compatible compilers) FIX: rotation workin again Release of 124.2.1 (r11365 on 25-July-2024): FIX: player_ranking transport type was not set_rigid => crash on all general comparison FIX: do not carry over old chat messages to new games CHG: when merging stops, keep the name of the bigger stop FIX: insticky bottom on drag to allow scrolling of chat FIX: too many null mouse moving events in SDL2 broke double clicks CHG: double click copies chat text FIX: wrong colors and double messges for private chat messages FIX: was not iterating over all stops correctly => wrong passengers spawned Release of 124.2 (r11351 on 20-July-2024): FIX: BASE_DIR/config/simuconf.tab was not read when "-singleuser" was used FIX: right button scrolling on minimap was broken ADD: new option leftdrag_in_minimap in simuconf.tab to allow dragging in minimap with left mouse button (now default) FIX: reenable dragging of stations FIX: Cargo not always loading onto vehicles in large games CHG: Reconnect to network games unless kicked out with desync CHG: Allow again building roads through other people halts but do not take road ownership afterwards FIX: Always have topped or new windows on the screen FIX: Saving discarded the newest messages FIX: number of players online in chat window now correct (and also showing offline state) ADD: prefer to build parallel ways, if starting next to an existing way FIX: overflow in bits in private way sign could allow/deny unexpectant players FIX: loading pak from savegame again (so only savegame needed on commandline) CHG: Activate input field if chat is active or after sending message ADD: restore open windows when rejoining a network game ADD: restore pakset symbols after changing the gui skin CHG: don't save default player colours with the settings/game, use pakset simuconf.tab only CHG: if only_single_info=0 now tiling the info windows. If there is a convoi or halt, not way or other info will be shown (default for Android) CHG: Try to resize windows if they do not fit on screen CHG: Continue game on banner will really continue a game and not destroy the current one by loading an older version CHG: Better support for narrow but high (portrait) screens CHG: A new game always uses the settigns from the simuconf.tab. Use the buttons in the extended settings to load other values ADD: new simple tool 42 for toggle day night view CHG: line list, load dialoges and online game windows shrunk in width ADD: left moving minimap insted of dragging location to have easier use with touch devices Release of 124.1 (r11272 on 3-June-2024): FIX: Cannot build ship depots on dead-end canal or river tiles CHG: Freetype library is now a required dependency for non-server builds (previously it was optional) FIX: Possible crash for systems with more than 12 logical cores FIX: In-game pakset installer does not download paksets, caused by incorrect handling of HTTP response FIX: Required data directories not created on first installation on Windows FIX: passenger AI crashed the game FIX: renaming town crashed, and renaming factories was almost impossible, renaming convoys still required enter FIX: New world tool had wrong tooltip and hide building was never in selected state ADD: (ranran) new chat window (dialog tool 36) and chat message system using direct and company chat options as well CHG/FIX: Updating the line schedule of a convoi bound to this line will overwrite any pending changes from that schedule if open in the schedule window FIX: dimensions of info windows more approprioate again ADD: (ranran) player ranking frame as dialog tool 37 ADD: help files will be also searched in pak set text directories Release of 124.0 (r11164 on 5-May-2024): ADD: map view can be changed with any general tool by dragging the pointer above a threshold FIX: Cannot load PPM heightmaps ADD: new tool (and button in signal dialog) for removing signals only CHG: only load goods for a destination into a single convoi. This avoids loading in parallel of slow loading convois CHG: More search paths for paks, new parameters -set_base_dir, -set_pak_dir, -set_user_dir CHG: Simutrans tries for base files also in the current path without any parameter (also in program location) ADD: single toolbar mode: Enabled only one top-centered toolbar CHG: Mouse clicks etc. are now processed every frame (but maybe executed later) to avoid the lagging UI at large zoom out or with weak devices ADD: tooltip warning when trying to cross way without suitable crossing or curves on runways ADD: Pak files are now additionally searched for in up to 4 subdirectory levels within a pakset ADD: theme parameter gui_color_image_transparency to set transparency color for icons (Yona-TYT) CHG: messages, speed records, ticker use 3d coordinates CHG: a go-to-coordinate action will change to underground/normal view if the target is invisible FIX: Crash when using 'makeobj list ' in some cases FIX: Crash when using 'makeobj dump ' in some cases ADD: Signals can be replaced by overbuilding them using left-click FIX: Wrong brightness of colours in screenshots FIX: Graphical glitches related to translucent player colours FIX: Makeobj ignored switch images for tracks with no seasons (e.g. image[new2]=...) FIX: massive swallowing of touch event to keep the game responsive despite SDL2's denial of service flooding FIX: airplane convois do no longer crash the game (but are not working perfect yet ...) FIX: graphical glitches when road convois change direction in some cases and on overtaking with drive on left FIX: (THLeaderH) crossings upgrade when ways are upgraded CHG: state of switches (images) save during reloading ADD: new parameter in simuconf.tab "cityroad_speeds" for a timeline (year,new_speed) of speed limits of way with pavement ADD: rotate house tool will also switch railroad switches (eyecandy only) FIX: height map conversion version is ignored if it is selected after height map in the height map selection dialogue FIX: Withdraw All button in line window did not indicate withdraw status correctly in some cases FIX: Game lags when paused, especially on big maps FIX: Could connect elevated way to side of sloped elevated way FIX: more respone event handling at low frames per second FIX: consequently in lists left click->window richt click/Control+left click -> goto position ADD: obj_xxx_details in translation will display some more details on an obj, especially vehciles CHG: editing names or filter will become immediately active CHG: Windows installer no longer requiring admin rights ADD: Antialiased fonts and using TTF fonts of the system by default Release of 123.0.1 (r10421 on 29-Jan-2022): ADD: button to remove double entries in schedules FIX: up/down arrows in schedule broken FIX: Simulation speeds up uncontrollably in some cases if New World dialogue is open CHG: lang files are loaded if their name is *XX.tab or XX*.tab. The first is preferred to avoid confusion by name like ja-taken.tab ADD: selected convoi in minimap now magenta. Also network display properly updated when activating or closing windows ADD: Option to adjust screen scaling manually (either via display settings or '-screen_scale' command line option) ADD: All resizeable windows will have a minimize button in the title ADD: Illegal schedule entries are highlited and a button to clear them up will appear. ADD: Infinite mouse scrolling can be activated manually in the display settings; but it will fail with certain touch devices CHG: new versions of pak48.excentrique, pak64.german, pak128, pak128.German Release of 123.0 (r10317 on 30-Dec-2021): KNOWN BUG: ugly movement of pedestrians on diagonals/curves FIX: map enlargement crashes with new climate generator FIX: Wrong tooltip when building something with the finance window opened CHG: Sound now plays on GDI via Xaudio2 for individual volume control ADD: map only moves when numlock off, new tool to move to map view (simple_tool[36]=,keybinding,-1|0) ADD: new option in simuconf.tab "numpad_always_moves_map" regardsless of numlock state FIX: Wrong loading of 24 bit BMP heightmaps FIX: Wrong loading of 8 bit greyscale BMP heightmaps if width is not divisible by 4 ADD: New height conversion modes when loading heightmaps CHG: schedule/line now tab of convoi window instead independent window CHG: new line list, overhauled convoi list CHG: rivers now go to the sea and pass through lakes. A tiny stream becomes navigatable after a lake or merging with another stream FIX: MessageOptions can be three single images (id=0,1,2) instead of one large image FIX: Potential hang when loading zstd compressed save files when multi-threading is enabled CHG: crossing speed is now the max speed. No crossing, if one speed higher than maxspeed ADD: construction cost tooltip also for normal tunnel construction (clicking on slopes) FIX: Graphical glitches when resizing SDL2 window with -autodpi ADD: special key "SCORLLOCK", "DELETE", ESCAPE", BACKSAPCE" and "+" sign for shift as modifer in tools key binding ADD: waiting time in schedules is now entered in days hours minutes (but old games will still have the old steps) ADD: (Leartin) groundobj editor and totally overhauled edit windows FIX: Fatal error when querying servers running very old versions FIX: tunnel can now also connect to sidewards tunnel (as originally intended by the code) ADD: target tile for jumping to a position (via pos button, window icon or jump dialog) will be marked with the current cursor FIX: Crash when height difference between climate border and groundwater is >127 FIX: Crash on reload after lowering canal with a ship on a nearby tile FIX: Crash when liquidating company owning a non-empty underground depot FIX: Crash when liquidating company owning a house CHG: gradual unloading and loading if loading_time of a vehicle > 2000 CHG: schedule with load level zero and waiting time will force daparture at eaxactly this time ADD: chat window transparency in network mode now controlled by theme: gui_color_chat_window_network_transparency and gui_chat_window_network_transparency CHG: search dialoges now consistent design, text search, preserved during saveload ADD: menubar can be now top bottom left or right ADD: reselect a tool will close it (new default, set by reselect_closes_tool in menuconf.tab) FIX: Grid height not restored when removing building on a raised tile CHG: maintenance of powerline tunnel/underground transformer maintenance is now maintenance of powerline + that of tunnel ADD: reservation overlay now also shows single way directions and signal states (inspired from ranran) CHG: absolute departure time can be also more than once per month (repeats the time in equal intervals) FIX: Public player cannot make halts of other players public CHG: Smaller file sizes (~20% for forested maps) for single player saves CHG: Simutrans now allows more than one file per languange. The file must just ending in XX.tab, where XX is the language code ADD: Command line option '-scenario' starts requested scenario if available FIX: Trains brake two tiles too late when entering a terminal station via a long block signal CHG: window rollup also with double click CHG: stop_halt_as_scheduled=1 in simuconf.tab will stop trains at the desginated tile instead going to the end of the platform Release of 122.0 (r9274 on 10-Oct-2020): FIX: Rotation of multi-tile buildings at street corners ADD: different volumes for different sounds, realistic distance scaling (independent from screen size) CHG: Fade sounds with one over square root distance, and remove the dependency from display and tile size ADD: Depot list as dialog tool 32 FIX: city more strongly attempt to build double height bridges over navigable rivers ADD: Vehicle list as dialog tool 33 (to see also upcoming vehicles) ADD: goods waiting bar with now configurable: gui_waitingbar_width ADD: button to copy the last 20 messages to the clipboard CHG: factory smoke location now depended on factory. Four parameters smoketile[], smokeoffset[] (koord), and smokeuplift (16) and smokelifetime (2499) FIX: several list stored wrong coordinates after map rotation FIX: Too new pak file version check now enforced for all objects FIX: airplane should stuck much less upon runway rebuilds CHG: block reserver tool will also start a route search of any convoi on its tile (no matter the owner!) ADD: timeline switches off after 2999 to prevent retiring of everything ADD: new parameter for buildings "preservation_year" and "preservation_month" after that the building is excluded from renovation during town growth ADD: if shift is pressed during road building then city roads or faster ways will not be overbuilt ADD: more folders searched for fonts on non-windows systems FIX: broken connection could kill (especially server) games on Linux ADD: customisable loading bar colors with gui_color_loadingbar_inner and gui_color_loadingbar_progress CHG: record messages and some status messages are now not saved, while stuck and full stations expire after a month on server chat to keep overview ADD: new button in minimap to hide contour for higher contrast of other stuff CHG: Option to show convoi name and line name as overlay ADD: (inspired by Ranran) Option in city, curiosity, and factory lists to show only towns/factories already served by a player ADD: two more theme colors: gui_color_obsolete, gui_color_empty for lines and server ADD: (Yona-TNT) configurable offsets for player colors for gui elements ADD: convois (in convoi list and schedule) also show their next stop with a posbutton to jump there ADD: caseless matching in depots, lines, halt and convoi list dialogs even with umlaute CHG: fixed vehicle costs are now booked as running costs at the begin of a month, also convoi details changed to accomodate CHG: a server will pause without clients if '-pause' is set on the command line ADD: freight type filter on line window CHG: speedup of map generation by reorganising and multithreading lake code ADD: defining overlapping climate height limits will generate maps with climate patches at these heights FIX: (extremly old bug) after removing a house on a slope the world height grid was not restored FIX: factory extension during city growth could no cope with more than one supplier for two goods ADD: scripted tools (initial patch by THLeaderH) ADD: Humidity based climates (originally from kireon green) Attention, winter snowline set temperature at sea level, so important! ADD: (FrankP) happy signs for system fonts ADD: (ranran) new GUI element routebar FIX: space.w added in container not space.h for vertical distance CHG: On consequeutive traffic lights, only the first is obeyed to allow turns on four traffic lights crossings Release of 121.0 (r8870 on 1-Dec-2019): ADD: Map number is shown in the enlarge map window. FIX: Text area GUI components no longer suffer from line wrap around when displaying an extreme number of lines. FIX: Stop information window waiting list in "via (amount)" order will no longer count incorrectly when more than 8,388,607 units are headed to a single destination. FIX: SDL2 build running on Windows 10 in fullscreen mode now minimizes and maximizes responsively. CHG: gui overhaul: automatic placement and size calculation of elements ADD: use animated water graphics on shores FIX: Do not call DefWindowsProc for IME to mitigate crash on Windows1809 ADD: always use temporary save name *.sv_ during saving before deleting main file CHG: Make double pak warning translatable FIX: (ACarlotti) rotation of traffic light phases and overflow was broken for years FIX: fields now check for allowed climate, not same height ADD: (HyperSim inspired) new tool for merging distance stations (and lot of code cleanup of old merging tool) ADD: constrait[prev][0]=any require a vehile in front (and same for [next] to require a trailing vehicle) CHG: The acceleration tool now also alters the acceleration in fast forward mode ADD: Up to 3x3 size city buildings, all with up to 8 rotations (attention: New paks using this feature will crash old versions!) FIX: Prevent a crash when the schedule window is open with waypoints on bridges that were destroyed or had planning canceled. FIX: Goods List speed field will now allow selecting speeds appropiate for very fast vehicles. CHG: Revise Goods List for better clarity and usability. FIX: Construction of priority and longblock signals CHG: Start with random midi song ADD: Timeline for pedestrians FIX: IP4 address query for easyserver (to detect IP4 changes sometimes occuring for ADSL connections) ADD: new simuconf.tab parameter follow_convoi_underground to slice through grounds to follow a convoi in sliced mode oder full underground mode ADD: search for midi files first in pak folder, then user folder, then program folder ADD: GUI for pakset install script Release of 120.4.1 (r8600 on 23-Sep-2018): FIX: wrong graphics with greedy block signals FIX: world minimum height could not be set FIX: location forest could not work with factories smaller than 3x3, since not enough inner tiles were iterated Release of 120.4 (r8588 on 17-Sep-2018): FIX: properly handle dualstack servers by using both IPv4 and IP6 addresses (if no symbolic name is available) ADD: (HyperSim) sorting vehicles in depot ADD: system clipboard support now also with SDL2 FIX: underground slice view now correctly displays front tile walls when a tile infront is flat and 2 elevations below the underground slice OPT: significantly improved the performance of changing underground mode/slice when playing on very large maps ADD: More factory locations (shore, river, forest) to the existing city, water, land ADD: (THLeaderH) option to hide the revenue income messaging at stops ADD: (THLeaderH) configurable height setting for extremely heigh maps (use at own risk) ADD: (gauthier) "priority" signals (greedy multi block signals) FIX: different obj (ways, bridges, tunnels, ... ) with same name failed for checksum comparision Release of 120.3 (r8503 on 15-June-2018): ADD: new blue-white theme ADD: new general tool 40 which rotates buildings ADD: scenario_info now dialog tool nummer 31 ADD: Support Truetype fonts on windows up to 19 pts. For MAC and Linux support there is a way needed to find the font path. ADD: Server can be now started at the UI when loading a game. ADD: switch -easyserver on commandline to host a game with dynamic IP behind a router FIX: crash when resizing isometric minimap FIX: problems with scrolled lists ADD: (Ichou) force replacement of wayobj by holding ctrl key while building ADD: (THLeader) long convoi patch FIX: removed line management window memory leaks ADD: animated pedestrians, parameters: steps_per_frame (internal steps per frame, straight movement across tile is 256 steps) image[dir][a] (dir - direction, a - animation counter, a=0,1,..) CHG: pedestrian movement, new dat parameter: offset (of pedestrian image relative to right tile boundary, 0..255, default 20, 0 = on the right edge of tile, 128 = on center line of tile FIX: corrected container UI component focus logic (fixes http://forum.simutrans.com/index.php?topic=17277 ) FIX: added redraw logic for field removal (fixes http://forum.simutrans.com/index.php?topic=17308 ) FIX: added Unicode file path support for Windows builds FIX: removed Windows builds file search resource leak FIX: improved Windows build clipboard Unicode parsing ADD: sorting in depot (HyperSim) FIX: factories which gain or lose fields should no longer have a chance to break with JIT2 (fixes https://forum.simutrans.com/index.php?topic=17394 ) ADD: sync step barrier for multiplayer clients to prevent them running ahead of the server in the case of an unreliable connection CHG: various multiplayer related settings have been given more reliable default values FIX: fullscreen Windows GDI no longer alters display area when alt+tab cycled ADD: JIT2 demand based production rate scaling CHG: changed factory information view to be easier to understand, especially when operating with JIT2 CHG: speed can be directly selected in goods dialogue FIX: consistent and wider climate borders, also other waterlevels than -2 when loading a relief map Release of 120.2.2: (r8163 on 31-3-2017): ADD: Name filter in depot window FIX: Combo boxes now work much more reliable ADD: Shift+waybuilder on elevated ways selects the tile under the way (for upgrades) FIX: Crash when extending convoy length ADD: All configurable colors now rgb internally Release of 120.2.1: (r8121 on 26-Feb-2017): FIX: SDL2 with non-ASCII input FIX: crashes with SDL2 when resizing FIX: allow docks only next to water CHG: use almost no assembler any more, the compiler can do better than us Release of 120.2: (r8077 on 12-Feb-2016): FIX: sorting according to displayed name in house/cur/industry building lists ADD: scriptable AI players (folder AI which contains sqai now with distribution) ADD: (jk271) switch to load heightmaps with more height levels FIX: reload themes after restart FIX: demo.sve were never loaded, since the auto-load assumed existence even if there was none ADD/FIX: slopes for groundobj now working again. If there are groundobjs with climate water, then these will be used occasionally at the coastline ADD: ground name=shore with Image[slope][0,1=snow, if there] for display at shoreline FIX: smoke tile when rotated factory views ADD: length of max tiles checker after choose sign/signal configurable (max_choose_route_steps) CHG: update to squirrel 3.1 CHG: monthly fixed costs for vehicles now uint32 (needs makeobj 55.4) ADD: basic nearest pixel DPI awareness to prevent UI from being too small on high density displays FIX: no longer assert when duplicate tiles exists in convoy path list, instead it gets logged (please report log if it occurs) FIX: corrected hitbox and resize logic of scrolled list GUI element, which prevents crashes caused by invalid selection CHG: building cities, plant trees, make public, headquarters and artificial slope tools check for sufficient funds CHG: planting trees is no longer possible by companies when no_trees configuration is set CHG: make public tool now works on incline and tunnel entrance ways CHG: make public tool now treats way objects separately from ways ADD: make public tool can be dragged to rapidly make multiple objects public ADD: make public tool cost can be configured ADD: make public tool can be configured to not work on ways or way objects for companies CHG: notifications about ways being made public in multiplayer games are produced less frequently CHG: retained ways from liquidated companies are transferred to the public service player ADD: on multiplayer servers any client capable of using the public service player can bypass password checks for all companies FIX: network core correctly enumerates local bind addresses instead of repeatedly trying to bind to the first local address ADD: support service names for ports in network URNs CHG: cities can now build on tiles with bridge pillars, matching the logic that pillared bridges can be placed over city buildings FIX: incorrect scaling of electricity revenue with respect to month length FIX: electricity related information now accurately reflects the last tick of the network FIX: electricity networks are no longer prone to arithmetic overflow which could result in incorrect graphics or income FIX: electricity networks no longer skip a power tick or lose revenue accumulators during a save/load cycle CHG: simplified transformer information UI removing redundant or meaningless values CHG: factory power is solved in 1 tick instead of 2 CHG: various improvements to JIT2 to improve factory and electricity network stability CHG: factories which produce power log the amount of power production consumed instead of the amount of power produced CHG: factories which consume power log the amount consumed as negative FIX: F12 no longer brings up the ingame help window in the Steam version of Simutrans Release of 120.1.3: (r7753 on 2-Feb-2016): FIX: road vehicles search for new route when arriving at intersection and old route is invalid ADD: depot vehicle filter now uses selected good for weight/speed calculations when possible for multi-good carrying vehicles CHG:(Vladki) private road signs in the middle of a slope FIX: trains reserve blocks at positions of now removed signals, prevents step-by-step reserving coupled with ignoring red signal if signal is removed in front of a waiting train FIX: route search to next depot has to be done synchronous on all clients ADD: ctrl-click on flat-dock icon to choose orientation Release of 120.1.2: (r7720 on 7-Jan-2016): ADD: configurable compass for minimap, rotation tool, and main screen ADD: tool for last used tools FIX: improve bridge building FIX: (Ters) Map scrolling speed on Windows with high DPI displays FIX: (DrSuperGood) general JIT2 fixes CHG: better multiplayer client timing FIX: uninitialized variables leading to online desyncs Release of 120.1.1: (r7640 on 4-Nov-2015): FIX: corrected bookkeeping of stops and passengers, including walked ones which could lead to crashes Release of 120.1: (r7634 on 31-Oct-2015): FIX: sliced underground mode now ranges from lowest existing to highest existing ground FIX: several issues with bridge building and deletion at two vertical cliffs ADD: new industrial demand model just_in_time=2, which models factory ordering stuff ADD: new tool for trimming lines (linetool, parameter trim, line_id_id egal, linetype (integer), target percentage (integer): will delete lines until target met or less than four convois left on line ADD: configurable offsets for signs/signals (use offset_left = 0, for not moving, default 14) Release of 120.0.1: (r7373 on 15-Nov-2014): FIX: waypoints on runways to force certain runways for planes work finally (hopefully) FIX: some bridge starting on flat tile going done were not correct FIX: Unicode input under Windows 8 FIX: lot of issues with new landscape code Release of 120.0: (r7175 on 28-Apr-2014): CHG: zoom with mouse wheel zooms to the cursor position, not to the center of screen as it used to CHG: system events management rearranged, simutrans should more responsive to user events, specially on low framerates ADD: second_open_closes_win =1 in simuconf.tab will close open window instead top them on second call/click ADD: remember_window_positions=1 (default) will remember last window positions FIX: (S_N_C) net wealth was not consistent after buying vehicles ADD: (kierongreen) double heights (slopes), different water levels per tile, and per tile climates FIX: long block signals direct in front of stations gave deadlocks ADD: fixed maintenance for vehicles is now booked ADD: (jk271) way toll statistics now ADD: (neroden/kierongreen) stacked stations with different owners are now possible ADD: loading gui themes (designs) on runtime ADD: faster creating of hill ADD: matching pak is loaded automatically when no -objects is given with -load on commandline ADD: dragging map also with left mousebutton when the query tool is selected ADD: display also fixed cost in convoi details and depot (if set) ADD: enforce loading time (time will be long of all vehicles, default 1s) ADD: makeobj can now warn about unused entries (makeobj verbose pak... or makeobj debug pak...) CHG: Stretchable themes now in trunk CHG: use faster route search for ships (jump-point search) CHG: max in_transit now depends on the sum of all connected factories output storage times the in_transit percentage ADD: goods in transit are now recalculated whenever a game is reloaded CHG: overcrowded message no longer for factories supplied by producer but only interchanges CHG: Waypoints act no longer as signals ADD: server_motd_filename for file with a greeting message in online games ADD: more option to select network overlay in minimap Release of 112.3: (r6520 on 17-May-2013): CHG: Removed the restriction of having border heights at water level. Forces a savegame version step, new maps can't be properly shown in previous versions. CHG: Raise and Lower tools interact with the grid now, not with tiles. ADD: (jk271/Dwachs) finance history distinguishes transport type CHG: Added a new setting to simuconf.tab, background_color. Allows to change the color of the background. ADD: Departure and arrival times can be shown in the halt dialogue CHG: Updates to the in-game pointer mechanics, updates its position after using a tool, and hides when the pointer is outside the world. ADD: (neroden) clustering of houses, set "cluster_factor" in cityrules.tab to about 10 times the typical distribution weight ADD: Displaying the natural slope at the map border if requested (default on) Release of 112.2: (r6390 on 17-Mar-2013): ADD: maximum radius of factories of a chain configurable CHG: use more colors to show height levels in minimap CHG: loading screen reworked graphically and in functionality. Lowers the chance the OS will identify Simutrans as "not responding" when loading large files/paks. CHG: lots of extensions of scenarios Release of 112.1: (r6212 on 23-Dec-2012): ADD: transparent chat window in networkmode FIX: underground stations in networkmode ADD: (Markohs) Scenario and heightmap dialogs now support multi-directory ADD: (Markohs) File dialogs deletes now use the trash bin (by default) instead of a permanent delete in Windows platform. SHIFT overrides. FIX: waiting level in schedule dialog works with keys again FIX: convoi list filtering again in working order ADD: hide by default return ticket for rail based transport (enable by simuconf.tab) ADD: possibility to limit the amount of goods in transit (see simuconf.tab) ADD: city attractions and town halls can be defined to be build for more than 65535 citizens FIX: you will need a dock to connect to an oil rig => no islands or tunnel can be used any more => ships only load at docks ADD: display which factories supplied/received goods last month ADD: player without operations can be deleted after x month ADD: player without new building/replacing activities can lose password protection ADD: logging of ip/nickname of company creators ADD: nettool commands to lock, unlock, remove a company ADD: (Timothy) support for syslog (compile flag -DSYSLOG, command-line option -syslog) ADD: Restore city window on joining ADD: Restore chat window on joining ADD: Restore factory window on joining FIX: correctly remove stale freight from convois when stops are joined or tiles are deleted FIX: less desyncs when renaming, better processing server queue Release of 112.0: (r6000 on 22-Oct-2012): ADD: (wackdone) screenshots of topmost dialogue window only by CNTRL+screenshot tool click ADD: (wackdone) -pause on command line pauses the loaded game FIX: downgrading of stops using CONTRL works again ADD: scripted scenarios ADD: river algorithm faster and river number automatically scaled with map size CHG: proper support for non-rectangularly shaped factories, no dummy (0,0)-tiles ADD: support for non-rectangular shaped station extensions, attractions, monuments CHG: Allow to connect to channels built by anyone CHG: fields can now spawned in larger number than the initial min value CHG: number of factories in new world dialog is now indeed the total number of factories (too many newbie mistakes here) ADD: force a minimum distance between city attractions CHG: Bankruptcy check before subtracting maintenance ADD: Drag stops CHG: reduced memory consumption when compiled with COLOUR_DEPTH=0, no copies of object images kept in memory ADD: ctrl-click with link-factory tool actually removes links between factories ADD: station buildings can be restricted to above/under ground (parameter allow_underground, =0 only above, =1 only under, =2 both/default) CHG: rework scenario directory structure: scenario my_scen is in my_scen/, script file is my_scen/scenario.nut Release of 111.3.1 (r5843 on 31-Jul-2012): ADD: weight in dat fields are in tons but kg can be given as 1.350 CHG: goto / follow convoi functionality via window gadget CHG: no bridges over runways / taxiways ADD: check whether format specifiers of translated strings match those of the master string, start with -debug 2 to get error messages ADD: customize icon of toolbar separator (taken from menu.BarTools or extra menu object) FIX: crash when changing to other programs while in fullscreen with GDI version Release of 111.3 (r5772 on 10-Jun-2012): FIX: race condition with multithreads on GDI CHG: savegames will be saved as "_temp.sve" and only renamed after after success to the final name ADD: screen redraw will be now up to 50% faster when needed thanks to multi-threading ADD: hashtables for animations and smoke => much faster adding/removing ADD: (from yorkeiser/prissi) line display in minimap (toggle with show schedule together with pax, mail, freight button) CONTRL will use player colors CNG: Order and function of many minimap buttons rearranged ADD: enable dragging of lower & raise tools in network mode ADD: enable dragging of bridge, tunnel, elevated way tools in network mode ADD: zoomlevel in minimap from -16 to 16 ADD: Start paksetdownloader, when started without paksets FIX: (z9999+) Corrected speedbonus base factor calculations FIX: (z9999+) Nickname in chat also send to server for reloads FIX: (installer/pakset updater) better finding out if portable installation is needed ADD: change of waiting at stops option in minimap CHG: Withdraw all will not disassemble convois in depots for easier upgrading FIX: route search for runways with oneway signs was broken FIX: main menu was not updated when new modes of transport became available ADD: (kierongreen) powerline tunnels: allow_undergroud_transformers=1 (default) will allow transformers below ground FIX: deleting tunnel ground was booked twice FIX: deleting tunnel ground charged for way on tunnel, which is not charged when building CHG: Only founding a city when account is positive CHG: powerlines will connect in directions a powerline can be build on a tile ADD: threaded (but with graphic errors) tile redraw will by used automatically when fps too low FIX: AI used wrong factories for building chains and build too long routes Release of 111.2.2 (r5583 on 20-Mar-2012): CHG: Simutrans help will now always display left the help index and right the actual help => easier navigation and only one window always ADD: nickname for players, enable chat for observers, add messages when players join / change nick / leave FIX: deletion of stops on bridgehead with trams FIX: extended setting of bool was broken FIX: track reservation now saved with game (less deadlocks on load) Release of 111.2.1 (r5434 on 28-Feb-2012): (111.2 had a severe bug with zipped games) ADD: more info on bare ground ADD: show ownership in minimap ADD: bonus_basefactor = 125 incorporated, determines minimum income for slow vehicles in case of speedbonus ADD: hint text in empty schedules ADD: highlight (land)tiles, which are part of a schedule ADD: F1 open dialog help in all dialogs with a help ADD: only show line names which contains a pattern (optional) and other tuning of dialogues ADD: server-side authentication of players ADD: city, industry and attraction numbers scale with map size ADD: front images for ways ADD: goto-button to chat messages, coordinate taken from @x,y or (x,y) CHG: overhauled color choose dialog with ideas from Hajo FIX: lots of double free errors (mostly in network code) FIX: No new buying obsolete stuff, just allow appending Release of 111.1 (r5115 on 23-Jan-2012): FIX: owner of way gates were accidentally rotated ADD: speedbonus speed limited by way speedlimits and achievable convoi max speed ADD: record also walked pax FIX: CJK description of houses correctly breaking ADD: configure modes of transport for AIs FIX: better tree offsets on tiles, should give less errors with trees on roads, in water etc ADD: read (when with addon loading) also ~/Simutrans/pakxyz/config.simuconf.tab ADD: (falconne) hide only objects around cursor ADD: (falconne) hide factories and goods currently not on the map FIX: several bugs with airplanes and reservation ADD: cities build bridges over rivers FIX: clip buildings to not shine through bridges ADD: (falconne) filter vehicles in depots according to their freight FIX: pedestrians on crossings were considered cars after leading FIX: endless loop when fast forward and zooming out too much CHG: using old display routines for zoom out to speed up display CHG: cities will now grow gradually in time and size during creation, starting with 32 citizens in the first townhall year CHG: minimap now really isometric ADD: allow elevated ways over water with depth = 1 FIX: load savegames even if factory pak is missing CHG: pak addons now in folder addons, to have same structure in portable and normal installation ADD: warn about missing pak files after loading a game ADD: logging of speedbonus speed in line window Release of 111.0 (r4911 on 1-Nov-2011): ADD: factory charts ADD: storage capacities scale with base production for factories which do not support fields ADD: factory-going passengers/mail are handled separately ADD: new boost mechanism at factories for passenger, mail and electricity ADD: new factory expansion settings for factories without fields ADD: command-line tool NETTOOL to administer server ADD: start as many trains as you like in a depot without waiting CHG: when building artificial slopes on water, create underwater hill first ADD: cache information about savegames in save/_cached.xml to speed up savegame dialog FIX: presignal on last route index and crossing on last route index could stuck trains ADD: (neroden) display tiles/month equivalent for km/h in goods-list window ADD: way builder can use basic terraforming CHG: gui does not lose focus when new messages appear ADD: AI players use way builder with terraforming CHG: passengers and mail now prefer nearer attractions and cities as destinations, with attraction changing over the years CHG: enable building under flying aircrafts CHG: renovated factory dialog to include statistics and other indicators; preparation for factory renaming FIX: convoi reservation for street convois longer than one tile is now fixed CHG: road vehicles less likely to block intersections or drive through each other when turning ADD: traffic light phasing adjustment ADD: break (pause) button now internally handled as ASCII 16 (^P) ADD: coordinate display and goto button for almost any non-moving thing on the map ADD: loading convois will be overtaken ADD: new file streetlist_xx.txt will be used for stop names for cities. With one parameter the halt type will be used, with two the city name and the halt type ADD: configurable way toll for using other players way (see simuconf.tab) ADD: renaming of factories ADD: private way sign now blocking for specific players CHG: (Timothy) new serverlist without any registration needed ADD: new tool to show block reservations ADD: new tool to show owner of ways and all other objects Release of 110.0.1 (r4359 on 12-Mar-2011): ADD: try to build crossing that match maxspeed of the ways to be crossed ADD: sheep will not walk in cities anymore CHANGE: much smaller savegames in online mode with trees FIX: broken townhalls when the land was flattened before FIX: AI better cleans up when building of shipping lines fails FIX: line IDs are now tied to line handles => no double line ID any more FIX: crashes with window resizing under SDL FIX: convoys stuck after modifying schedule of line ADD: pak set specific pre-defined player colors and random player colors FIX: world enlargement FIX: loading lines with ID zero again Release of 110.0 (r4303 on 14-Feb-2011): CHANGE: default savegame now bzip2 (games 33% smaller) ADD: Odometer and distance per month available for convoys/lines ADD: (gerw) built tunnel without ways FIX: no longer possible for negative prices (cheat bus) ADD: (Dwachs) starting money per year ADD: lines will be listed numerically correct, if the contain a number ADD: bridge building with click-and-drag ADD: traffic light can set their phases now manually (in seconds) (new help text: trafficlight_info.txt, new string: -\nSet phases:-) ADD: some more network related settings like server_frames_ahead, network_ms_ahead, server_frames_per_step to control timing on the network ADD: translations are also search in user dir/scenario/text ADD: (gerw) auto-electrify depot added FIX: freelist sometimes returned wrong elements to free ADD (VS): makeobj ignored alpha channel now really CHANGE: (Knightly): Not scaling images displayed by gui_image_t anymore FIX: deleting tram signals again ADD: click-and-drag also for network games (except for building elevated ways, bridges, tunnels) ADD: tools should work on all clients regardless of underground mode ADD: tools work now correct with ctrl keys in networkmode FIX: a double block (presignal) robbed the following signal of its (pres/longblock/choosing) function CHANGE: no re-zooming in viewport FIX: text overflow in message boxes FIX: window resizing is triggered even if initial click is not in dragger area FIX: square button's clickable area not set according to accompanying text FIX: help text link's clickable area not correctly set FIX: pak file image data not trimmed according to x-offset and contains superfluous data ADD: factories now support multiple fields FIX: factory builder does not consider minimum fields in randomising production FIX: tooltip box hovering above viewport ADD: (mostly Dwachs) nicer crossing infos ADD: bridges/tunnel show their names in info window ADD: (inspired by gerw) choose signals will not choose if on the route is another choose signal or and end_of_choose FIX: text overflow in ground info CHANGE: ground info and thing info title bar color will change according to ownership ADD: allow_buying_obsolete_vehicles in simuconf.tab can disallow buying obsolete vehicles for better balancing FIX: forest density/size nor again reasonable dependent on map size ADD: factory stops can now get different names from building stops (%s factory %s %s must be new translated) FIX: build foundations only directly under elevated monorail depots FIX: ist_natur() return also false on halt tiles (like harbor) ADD: (gerw) broad tunnel patch (see pak64 sources: adding images [Nl] [Nm] [Nr] and so on) FIX: no longer no trees after 150 years ADD: no decimal point for very large amounts CHANGE: better displaying algorithm, should give much less clipping errors/graphics glitches with long vehicles/bridges/stations etc. ADD: configurable thousand exponent (like 10^4 for Japanese) by SEP_THOUSAND_EXPONENT FIX: Makeobj could not extract bridges (and possibly even more objects) under XP ADD: zooming in also for pak192 ADD: preparation for images larger than 64k resp 255*255 FIX: maintenance for ticks_per_month smaller 18 ADD: timeline for cityroads using 'city_road[0]=name[,intro][,retire]' in simuconf.tab ADD: winter image for PowerSource/ PowerDest: Image[2] and [3] as snowy versions of [0] and [1] ADD: winter image for sidewalk: Image[1],[4],[7],[10],[13] as snowy versions, Image [5][8][11][14] transition images ADD: minimum and maximum numbers of next towns to connect to a factory can be set too in simuconf.tab with factory_worker_minimum_towns/factory_worker_maximum_towns ADD: timeline for intercityroads using 'intercity_road[number]=name[,intro][,retire]' in simuconf.tab ADD: winter image and timeline for crossings: winter images of [image][0] are [image][1] ADD: powerline stabilisation pak which enforces a single transformer next to factories/power stations ADD: enable to use more than one powerline object ADD: texture for tunnel grounds (Obj=misc, Name=TunnelTexture, Image with numbers 0,3,6,9) ADD: simuconf.tab: add default values for cityroad ("city_road") and intercity road ("asphalt_road") CHANGE: TAB goes to next/Shift+TAB to previous edit box, moving and resizing now even when other windows open ADD: skinnable scrollbars ADD: setting dialogue tool (handle with care, should not be visible by default for pak sets!) ADD: resizer also part of skin CHANGE: also starts without any skin FIX: no signals to be built on switches, since they will not display correctly FIX: waiting time cycles also on left button ADD: (z9999+) limit size of toolbars in simuconf.tab FIX: (z9999+) loading old games before 88.00 CHANGE: forest settings moved to simuconf.tab ADD: delay and duration of tooltip configurable in simuconf.tab ADD: timeline for headquarters (pak-sets may need to be updated as the default intro date is 1900, default retire date is 2999) ADD: blinking cursor, selectable text and clipboard functions for text input fields ADD: industry increase and minimum city distance now also saved in savegame FIX: error in routing will clear itself after loading FIX: not restore fields if there is already a building on this tile CHANGE: minimap will center after zooming ADD: parameters of cityrules.tab saved in savegame (networkmode) ADD: active window title bar is highlighted (for options see simuconf.tab) CHG: canals cannot be built on canals of other players FIX: removed obsolete forest config parameters FIX: traffic light setting also network safe ADD: if present show copyright line from ground.outside.pak (which should contain pak set and revision) ADD: new general_tool[36] to build cityroad according to timeline FIX: (z9999) building a Barbour must check also for existing stops above/below FIX: (z9999) trains were braking on field too late after changing of route count FIX: (z9999) stopping before occupied crossings with a signal and many other signal fixes ADD: for text input, double-click to select a word and triple-click to select the whole text ADD: register lineless convoys at the halts and list them with links in the halt details dialog FIX: do not crash with prodbase 0 and ensure some basic production FIX: convoys did not properly looked up next signal for signals on closed crossings ADD: right-click on icon of selected toolbar closes the toolbar ADD: server uses a different savegame revision => testing already possible now ADD: savegame version can be set in settings ADD: simple chat system added (send message option in message window) ADD: semi-automatic announce of servers possible CHANGE: chat option now part of message center ADD: messages on joining and leaving CHANGE: renaming of stuff now in network games ADD: clients and server compare paksets before joining CHANGE: for two-click-tools: ctrl+click sets start marker, useful for point-to-point bridge building with two clicks in network games ADD: (gerw/Dwachs) build signals like ways; ctrl+click on icon opens dialogue to change parameters (help file: signal_spacing.h), in network games: ctrl+click and then drag ADD: station extensions can be built near public halts CHANGE: when joining with public station, the public station keeps its name CHANGE: Introduced symbols and tooltips for a much more compact status line ADD: dialogue can be changed (currently only minimap) ADD: possibility to compare pakset of client and server CHANGE: one year after bankrupt, clear slot for new player CHANGE: first load convoy waiting the longest time at a stop FIX: after bankruptcy, ways on public stops will remain unowned, so new people can connect to them FIX: transfer statistics too, when joining stops CHANGE: In order to lock a game the public player must be password protected. Unlocking the public player allows player change again. CHANGE: (Knightly) message system has now tabs for faster overview of categories CHANGE: cities try some terraforming to be able to build roads ADD: messages are saved in save games CHANGE: passwords are only checked/changed if is pressed in the password input box ADD: more messages for headquarters and making stops public CHANGE: application folder for simutrans now under /Library/Simutrans (default MAC standard) ADD: (TurfIt) right-click closes all toolbars, all dialogues and deselects all tools ADD: (TurfIt/Dwachs) Windows snap onto each other when they are close (controlled by window_snap_distance) ADD: server can do monthly announcements ADD: announcements can be configured on command line ADD: also schedules and line windows are kept during joining ADD: vehicles can immediately lose part of their value when starting running (used_vehicle_reduction) to make expensive vehicle unattractive when starting a game Release of 102.2.2 (r3131 on 8-Mar-2010): FIX: connecting upslope to upper way works in sliced mode (r2949) FIX: longer convoi's name could cause crash when entering depot FIX: lines were never freed upon deletion FIX: deleting factories/cities/attractions while those lists were open resulted in CTD FIX: stop types in station frame sometime painted on top of each other FIX: delete convoi only in next step if loading and return directly (and step reverse in karte_t) FIX: walk_city (for stop in towns with AI) was broken (r2990) FIX: distribute goods also on factories with less than 10 output FIX: latin2 characters with unicode>255 work again (via hack) FIX: (VS) do not buy same house twice FIX: (Knightly) only do action when mouse button release also on same button FIX: (Knightly) prevent pending mouse events when resizing ADD: support for townhalls with four rotations ADD: korean glyphs now in cyr.bdf (r2967/r3059) ADD: reading of bzip2 savegames from trunk ADD: (Dwachs) starting money per year FIX: crash when changing schedule after no_route on halt FIX: presignal before station failed FIX: wrong clipping of text FIX: (TurfIt) really empty factories input even in consumption factors not 100% FIX: stop factory building if more than 10 attempts failed FIX: (Dwachs) display correct length in depot FIX: isometric map repaired Release of 102.2.1 (r2928) 9-12-2009 FIX: minimap was not updated when a bridge was deleted FIX: AI could not built any lines FIX: grid slopes were not (re)set correctly FIX: min_building_density and harbour are now recognized in dat/tab files ADD: do not close load/save dialogue after deleting FIX: do not show more than the last 2000 messages in message center to prevent scrollbar malfunction FIX: margin calculation was wrong FIX: first halts are now first in connection list => consistently transfer at first possibility FIX: no money when selling convois at the depot FIX: (Knightly) inverse scrolling consistently for minimap and main view FIX: also ask when a single pakset with addons is present what to load FIX: game starts in normal mode (instead paused) again FIX: downgrading ways were cheaper than destruction => now downgrading will be as expensive as removal CHANGE: ignore empty entries and lines starting with # in citylists FIX: deleting line does not longer crash in halt_detail windows FIX: withdraw all with convois in depot causes crash (and maybe also not withdraw all) FIX: several issues with tram signals CHANGE: info windows now open less important first resp. for single window topmost object first (usually most important) ADD: toolbar, which did not fit their space can be scrolled (either manu menu or when toolbar_max_height = 1) ADD: new parameter for icons in windows title bars: gui_gadgetwidth and gui_gadget_height (although the latter is unused for now) FIX: Labelist with own player filter works again Release of 102.2 (r2785) 18-Oct-2009: CHANGE: cursor now works with full 3d-coordinates ADD: (mostly gerw) sparse data and overlay in main map for passenger destinations ADD: No connections found via overcrowded stops, if overcrowded flag active ADD: do not create goods/passenger/mail when the only route is over an overcrowded stop (simuconf.tab no_routing_over_overcrowded) ADD: more vehicle messages show in main screen CHANGE: ask for language only once (prepare for difficulty dialogue) ADD: UNDO for powerlines FIX: convoi details (and info- windows) were not always correctly (re)opened CHANGE: convoi, station and line handles autoextent their capacity FIX: changing schedule by stop mover did not work on open schedule windows CHANGE: climate dialogue now completely gui_numberinput_t CHANGE: convois will only choose stops that can fit their entire length FIX: (mostly gerw) tunnel maintenance costs ADD: (z9999) tunnel can have predefined way (new syntax, need new makeobj) ADD: completely ignore message button at beginning of dialogue CHANGE: faster map (non-isometric) on very large maps and no crashes with extremely large maps anymore ADD: paste letters under windows with CNTRL+V ADD: button "withdraw all" in line window CHANGE: simuconf.tab cost values can now go up to 48 bit (should be enough for insanely high starting capital) CHANGE: addons can be put now into the user folder with the same name than the main pak folder CHANGE: skins are now read first from program dir/skin then from own dir/skin and then from pak set ADD: (gerw) dragging of wayobj and many other tools, adding wayobj-remover CHANGE: (Dwachs) better intercityroad creation ADD: (Dwachs) sliced underground mode ADD: (z9999) 8 layout for city buildings allow also closed corners ADD: (Dwachs) buying cityhouse will protect them from renovation ADD: Underground slope (in principle even compatible to older savegames) using standard slope tool ADD: changing slopes now also with objects on top ADD: (Dwachs) tram track can be build in road tunnels ADD: (gerw) click-and-drag for tunnels ADD: (Nathan Samson) allow left_to_right_graphs (=1 in simuconf.tab) ADD: (z9999) better display of depots in minimap ADD: (Dwachs) zooming of map with mousewheel ADD: switches change graphics for diagonal going trains (only eyecandy) ADD: ambient sound effects (for all graphics + forest + beaches) CHANGE: less lagging updating/rerouting after schedule changes even with large stations ADD: (Dwachs) Public player can remove all ADD: two switches (-addons and -noaddons) to regulate loading of addons together with pakset CHANGE: (Dwachs) 2-5x faster city generation CHANGE: (Knightly) even faster routing of goods due to ignoring non-transfer stations in breath search ADD: switch to keep towns static ADD: (gerw) minimaps in city info window are clickable CHANGE: after 40 retries, factory construction will ignore climates to avoid broken chains (if possible at all) ADD: citygrowth parameter configurable in simuconf.tab ADD: setting dialog allow access to all settings also found in simuconf.tab before starting a game Release of 102.0 (r2366) 5-Mar-2009: FIX: connect stops again to public player FIX: marker had invalid position after loading a game => crash when adding new marker FIX: again income messages even with very many convois ADD: &#; for direct character input (but no unicode yet) to help files "HTML" ( happy face,  unhappy face) ADD: (z9999) capacity of a stop shown in tooltip ADD: (z9999) halt can have different storage capabilities for different good types (seperate_halt_capacities=1 in simuconf.tab) ADD: if pay_for_total_distance=1 in simuconf.tab, price is calculated for the distance you got nearer to destination ADD: (isidoro) jump to line window from halt_details FIX: mark deleted trees dirty when building roads CHANGE: message window now obeys also Simutrans UI (array/rightclick for goto on map, leftclick on text to show again) CHANGE: inactive tabs nor greyed out ADD: goto line also in convoi info window CHANGE: (z9999) finally useful marker with a simutrans consistent gui ADD: (gerw mostly) river are now created during map creation, new options in simuconf.tab ADD: no vehicle/cash sounds during fast forward ADD: (gerw/prissi) powerline remover FIX: lines always got id 1 => saving was not successful! CHANGE: language specific divider for amount possible (like million for most) using LARGE_NUMBER_STRING and LARGE_NUMBER_VALUE CHANGE: start at any month you like (parameter starting_month in simuconf.tab) FIX: fences and slopes in most situations now without graphic errors (some are currently unavoidable) ADD: (z9999) new sort option for good list by category FIX: (mostly Dwachs) way graphic for way in tunnel now matches speed and maintenance fixed ADD: avoid overcrowded stops (experimental, avoid_overcrowding=1 in simuconf.tab) Release of 101.0 (r2208) 3-Jan-2009: FIX: weg search stooped to early => not properly upgrading city road types in pak128 (z9999) FIX: crash of passenger AI before busses were available FIX: stop moving tool also deals with empty lines now FIX: retire dates again ignored during map creation FIX: no crashing when loading unavailable buildings FIX: crashes with movingobjects during creation FIX: planes were not always correctly descending FIX: harbour tile layout==16 tile were not calculated similar to stations CHANGE: -fullscreen command flag will try to detect current resolution => no need to specify size FIX: scrollbar text after resize now updated properly FIX: remember last selected tool FIX: preserve tick count after loading saved game => wait per month should be fine now FIX: allow harbours also on trees ... FIX: start on signal tile resp. drive through closed signals when open/close schedule dialogue ADD: multithread support for screen copy (max. 10% faster) for GDI FIX: new timing for idle and frame => fast forward speed more closely met, less CPU needed, smoother transitions FIX: too sensitive mouse repaired (moving during clicking) FIX: no crashes on power bridges at vertical slopes CHANGE: much faster town renovation/town growth => faster city generation FIX: only tools applicable in the current situation are visible CHANGE: savegame path instead .simutrans now ~/simutrans on Linux/BeOS, and ~/Documents/simutrans on MacOS FIX: fields next to a new station leads to crashes during name determination FIX: correct color for stops over townhalls for labels FIX: long block signals searched always on first leg too much and stuck trains which stop directly after them FIX: wayremover removed (partly) bridges even without permission FIX: bridges during removal removed too much maintenance ADD: make tooltips configurable (show_tooltips, tooltip_background_color, tooltip_text_color) in simuconf.tab ADD: (leopard) Native sound/midi support for MAC FIX: rotate also coordinates of messages ADD: target destinations in halt details now clickable FIX: raise and lower now also ok new border, better error messages FIX: (z9999) maglev and narrowgauge for everything, additional line symbols (MaglevStop, NarrowgaugeStop, ... ) for linemanager optional CHANGE: speed on diagonals only 20% faster => less gain, more realistic vehicle length FIX: no crossings on underground station tiles FIX: wayobj ignored replacement by compat.tab for catenaries CHANGE: now 16 syllables for town names %/&(0...9A...F)_CITY_SYLL CHANGE: maps got an own subdirectory, and format RLE compressed (usually less than 10% of ppm)/uncompressed 8-Bit and 24-Bit BMP allowed too FIX: trams were not using the max speed according to the trams speed table ADD: (z9999) daylight level now adjustable ADD: (isidoro/prissi) overtaking for road vehicles/citycars ADD: (z9999) right mouse button to minimize windows to title bar and restore CHANGE: payment is now only for the decrease in distance to the next transfer stop ADD: instead a number also a name of a menu object (with image zero) can be given for an icon in menuconf.tab ADD: (Gerd Wachsmuth) map enlargement during the game ADD: (VS) electric EMU/trolley bus tab added to depot CHANGE: AI now separate files, which would allow for a greater variety of AIs ADD: (Dwachs) depots remember last line FIX: construction of four layout stations and rotation of four layout buildings were completely broken FIX: graphic error with slopes next to bridges and tunnels ADD: new tool to make a stop a public one (to make public stops on map without player change) FIX: disabling sound and midi will be now saved and will disable them completely to avoid the SDL_mixer troubles FIX: x instead y offset written by makeobj ADD: (Dwachs) new object gui_numberinput_t for more convenient entering of numbers ADD: (gerw) sizeable loadsave frame CHANGE: savegame bump, no AI save state correctly, same for map extension, no koord saved anymore (up to 60% smaller savegames) CHANGE: settings are now saved for game relevant/user relevant to prepare for multiplayer (and do proper versioning) CHANGE: sea level now in single steps (before only double steps, but single steps in display) FIX: handling of wrongly defined fonts and unicode characters now correct again FIX: bridges could connect to "harbour" tunnels CHANGE: maximum map size now 16 million tiles FIX: overcrowded station messages again Release of 100.0 (r1867, stable) 28-June-2008: FIX: rotated townhalls could lead to crashes CHANGE: show connected stops at factory again ADD: factory edit modus after patch from z9999 FIX: removing of wayobj in tunnel or on bridges left wrong top speed. ADD: factory link tool from z9999 ADD: attraction built tool from z9999 ADD: citybuilding builder inspired by z9999 ADD: tree planting tool inspired by z9999 FIX: trees are now correctly sorted by the internal tile positions (before were shadow problems) CHANGE: smoother zoom out (experimental) FIX: x-offset for images should finally work ADD: following zoom-levels are now supported 1.5:1, 1.33:1, 1:1, 0.75:1, 0.66:1, 0.5:1, 0.33:1, 0.25:1 (maximum tile size zoomed in is 254!) FIX: no cars entering railroad crossings when they apparently cannot exit (may fail with crossings following immediately) FIX: no citycars entering a crossing, if there is no exit FIX: convois created by copying had no home depot FIX: during zoomout, the tiles were clipped 32 pixels too early FIX: rotation of buildings with layout>4 corrected ADD: more statistics for world (see in citylist) FIX: show signals again in minimap ADD: preliminary support for automatic configuration, clarification of the licence CHANGE: maintenance of player buildings now depends on its level FIX: reloading with no matching buildings for a climate could lead to crashes CHANGE: smoother movements of vehicles in all sizes and zoomlevels FIX: powerlines destroy on fields during loading CHANGE: all traffic lights have now synchronized phases ADD: new parameter in forestconf.tab: tree_climates for climates with at least one tree per tile and no_tree_climates for the reverse ADD: new parameter for cityrules.tab "minimum_building_desity" in percent: if the area covered by buildings is less than this, the city borders will not increase => more compact cities, not fading out along a single street (default: 25) CHANGE: completely renovated schedule entering window CHANGE: logarithmic traffic density display in minimap CHANGE: cst_depot_air for charging air_depot CHANGE: less subtypes for stops and depot (halfway) CHANGE: new customizable toolbars (90%) (see pak64/config/menuconf.tab) FIX: schedule window last line was not selectable ADD: waytypes narrowgauge_track and maglev_track now functional FIX: trains with freight_type but total load==0 were still creating connections (caotic) CHANGE: groundobj for new map again (z9999) CHANGE: seasons for groundobj and movingobj (z9999) CHANGE: enables all map edit tools again (z9999) FIX: random rotation building problem (z9999) FIX: initialize tool (z9999) FIX: check climate for tree (z9999) FIX: tree cost (z9999) FIX: power for low producing factory (z9999) ADD: Fill trees tool (z9999) ADD: Add forest tool (z9999) ADD: Add random city car tool (z9999) ADD: option to start without trees (z9999) ADD: max. speed information on depot window (z9999) CHANGE: allow any key for command, include #99999 (decimal code), ^A (Control A), F99 (F1-F15), COMMA (,) CHANGE: allow for < and > to be used in flowtexts ADD: stop mover tool (mostly z9999) tilewise for road, stopwise for water/air and platformwise for rail bound vehicles ADD: AI builds airports (sometimes still too close to border) and uses them more or less wisely FIX: no factories/attractions under monorail or powerlines and thus no disruption of them FIX: waypoints under/over stops were treated as stops for good routing CHANGE: all stops are now given more or less unique names. First factories, then buildings, then ?standard, where "?" is 1..9 or A...Z (if available) and standard is center, extern or suburb the later with one of the german directions nord, sued, west, ost, nordwest, ... If failed, it will be a numbered stop (translators: please translate on simutranslator!) CHANGE: deduct maintenance at the beginning of a month => no sudden dropping below zero any more CHANGE: no zooming in/out of vehicle images in depot and lists CHANGE: speedbonus taken now from config/speedbonus.tab (if present) ADD: scenarios (i.e. you can play for a goal), bankrupt only after net wealth is below zero CHANGE: scenario can have an additional factor for difficulty CHANGE: more description of files before loading (ppm/scenario) CHANGE: -debug parameter now takes a debug level (1,2,3,4 for errors only, warnings only, all messages,debug special messages) ADD: highlight selected toolbars/tools Release of 99.17.1 (r1550, stable) 17-Dec-2007: FIX: some errors concerning headquarters, AI and moneys frame CHANGE: again only first and last vehicle reserves block CHANGE: only producing factories are consuming power CHANGE: allow for offsets in the texture files => pak192 grounds possible CHANGE: new convoi filter for stuck convois 'clf_chk_stucked' FIX: could delete crossings under certain circumstances CHANGE: images in line tab, translation now tooltip, "TramStop" pak needed CHANGE: block reservation mode on will also enable "delete these reservations" cursor ADD: new obj named groundobj, new string "cost for removal" FIX: error with removing headquarters after rotation FIX: deleting non-continuous buildings and rotated buildings could crash simutrans, rotate factory base for rebuilding after loading FIX: invisible mousecursor by moving the mouse during right click scrolling outside of window fixed CHANGE: adding type to introduction/retirement message FIX: severe bug loading pedestrians or citycars Release of 99.17 (r1502) 19-Dec-2007: ADD: water animation (if images defined) animation images are just the second parameter in the definition; animation speed is set by water_animation_ms in simuconf.tab FIX: tiles in train middle at waypoints were not properly reserved FIX: sometimes planes were circling on the ground FIX: winter tile change for whole map also on large maps ADD: more statistics for cities / preparation for different growth models (strings now "citicens", "Growth", "Buildings", "Electricity", "Transported", "Passagiere", "Sended", "Post", "Arrived", "Goods") FIX: daynight cycle for show_month<=1 CHANGE: town growth is now stimulated by 40% passenger transport, 20% Mail transport, and 30% for not leaving storage of consumers with workers from this town empty FIX: powerlines and tunnel entries more intelligently handled during wayremoval CHANGE/FIX: close schedule windows when deleting a convoi or starting one from depot (removing the annoying error message when entering schedule is in progress) "Es wird bereits\nein Fahrplan\neingegeben\n" CHANGE: electricity production of a power station is now fixed at 4x nominal production (does not matter how much transformers => no cheating with solar plants) FIX: smoke offset revisited FIX: thing info is always redrawn => less redrawing errors FIX: headquarters was always added during drawing to money dialogue => to time left for event processing FIX: remove trees below monorail depot FIX: good filter now works with categories CHANGES: city cars spawn now only if neither passengers, mail, nor goods are transported CHANGES: to make list easier to navigate with left button only mouse, city list, attraction list, and factory list display jump to button FIX: two errors that could lead to stuck convois Release of 99.16 2-Dec-2007: FIX: image under fields were not recalculated after removal FIX: deleting a factory with an open info window lead to crash (also during map destroying) FIX: pak selector was not always shown FIX: crash when opening slopetools in underground mode FIX: windows gain not focus during dragging with mousebutton ADD: new error message 'Not possible in this rotation!' FIX: bridges ending on elevated monorail or flat tiles fixed ADD: screenshot under windows as png (if gdiplus.dll is present, i.e XP or .NET there) FIX: transparent/no season recognizing bridges FIX: double or "unknown" names for stations on bridges/tunnels for 99.14 and older games CHANGE: events now internally carry always the mousebutton states => fixing problem when scrolling with right mouse button (and so others too) ADD: airplanes now reserve the runway for the entire time of takeoff/landing (more realistic, smoother waiting circles) ADD: during world creation no tourist attractions newer than the current year will appear with timeline on FIX: under certain cases station (length!=8) stations loaded one vehicle too many FIX: old texts were not deleted from nonrotated hashtable during rotation ADD: 'pillar_asymmetric' for bridges with shorter pillars on north resp. west slopes and 'max_height' for bridges + new error message 'bridge is too high for its type!' ADD: new crossing logic: crossing is one object only (new strings: "invalid", "open", "request closing", "closed", "\nway1 reserved by" , "cars.\nway2 reserved by", "cars.\nstate") FIX: no stops on tunnel entries or on runways FIX: calc load and image correctly after leaving depot CHANGE: factory introduction dates (thus with older paks no factories before 1901) Release of 99.15 3-Nov-2007: FIX: AI terraforming was free of charge FIX: AI never changed convoi, when building does not succeeded FIX: powerlines did not disconnect during deletion CHANGE: road convois wait, if next tile is a crossing and they cannot pass CHANGE: if the stop of a factory is overcrowded and there is no goods waiting for a destination while there are some for other destination of this factory, the overflowing amount is rerouted. This avoids situations where nothing was delivered to a waiting convoi because a station was overflow and thus this convoi blocked everything. Now it will be gradually loaded. FIX: vehicle position was wrong after visit to depot => such convois will be vanish after reloading, sorry CHANGE: sort order for convois and halt match as default; also last setting is saved CHANGE: selection of map buttons is saved FIX: hausbauer now handles building deletion => complete deletion of stops and factories, even non-continuous and connected FIX: pax_transported, pax_erzeugt was not saved FIX (z9999): tree density setting form uint8 to uint16 FIX: empty schedule during loading for convois in depot => crash FIX: signal and missing roads where overlooked when leaving depots ADD: minimum factory distances more strongly enforced. Configurable via simuconf.tab (factory_spacing, default 6) ADD: new message category for warnings (like name of new player or traffic jams) ADD: AI now builds headquarters FIX: correctly building ways from/in depots and stops CHANGE: AI now announces headquarters construction with <<%s's\nheadquarter now\nat (%i,%i).>> FIX: redistribution of goods on stations with three destinations was faulty FIX: building taxiways again FIX: lines again automatically connect and detach from a stop FIX: depot closed, if there was only a single depot and the next/previous depot was searched FIX: reaching destination while just leaving depot will skip destination FIX: revenue will be only given for cars in station FIX: slope entering by a curve would not give slope penalty FIX: tram tracks on bridges were visible in underground mode FIX: build station extension building again on flattened slopes FIX (kierongreen): station layout patch supplied with some fixes ADD (kierongreen): powerline bridges FIX/CHANGE: window system was really a little broken FIX: remove tall buildings without residuals CHANGE: bus AI now tries to connect intown as well => less worse competition FIX: deletion of powerline was free up to now ... CHANGE: two less virtual functions in vehikel_t::sync_step() (most often called routine) => speedup FIX: convois with waypoints at bends were still jumping for certain vehicle lengths (sigh ...) CHANGE: GDI fullscreen mode correct and due to switching resolution ~50% faster redraws CHANGE: dragging during raising and lowering land enabled ADD: manufacturer name for each car shown in the convoi detail dialogue FIX: start/end way building under cars and filled depots CHANGE: preliminary map rotation (for these places behind a slope) with 'R' FIX: clear all old entries in the message list upon starting a new game FIX: text labels again on any height (did affect towns below bridges) FIX: frame delay time does now slower increase/decrease => smoother response CHANGE: all toolbars open directly below each others FIX: crash when zooming out images with negative y-offsets FIX: rotate map, when building a tree with nonrateable factories FIX: ground calculation fixed again FIX: smoke offsets FIX: more responsive interface by changing frame time calculation again for extreme cases ADD: frame around active window if requested (window_frame_active in simuconf.tab) FIX: toolbars now also change with underground mode ADD: new message for AI going bankrupt (Company bankrupt) FIX: no route message shown again FIX: roadsign rotation corrected FIX: snowline height fixed (automatically set be the end of the highest climate) CHANGE: if no pak defined in config or commandline then a) if only a single pak exists load this or b) display a dialog for choosing one Release of 99.14 31-July-2007: FIX: speed limit of channels for ships was never lifted FIX: again front images for traffic lights FIX: crash when building harbour outside of the map FIX: avoid jumping of trains when waypoint on first tile after a curve ADD: under windows nicer error messages for missing objects CHANGE: much better loading of games with missing objects CHANGE: stopping position of trains of length>12 FIX: factory positions in minimap FIX: screen resolution enlargement with GDI version resulted in crash FIX: crash when opening a depot, for that no vehicles exist FIX: town name management now like halt management FIX: player color for depot/color selection fixed ADD: ordering of menu entries for station buildings (level) and wayobjects (topspeed) CHANGE: money frame construction cost button name now 'Construction_Btn' CHANGE: enable UNDO always for active player FIX: convoi list can again hold more than 819 convois ADD: new buttons in display settings: 'show waiting bars' and 'show station names' for the previous '!' key function CHANGE: 'Q' now opens the options dialogue, where you can really quit the game FIX: factory crossconnection running amok hopefully solved (and some production values tweaked) ADD: (z9999) show current schedule/selected line in minimap (new strings: 'Show schedules', 'Shows the currently selected schedule', 'factory details', 'Shows consumer/suppliers for factories', 'CityLimit', '(%i)-') FIX: redraw problems after topping windows FIX: factory builder was tried to built the same factory always on the same region => now check next two regions in all directions => less incomplete chains CHANGE: AI deleted uneconomical vehicle and routes, if in trouble CHANGE: AI tries now also more or less successful ship route Release of 99.13 3-July-2007: FIX: airplanes got confused, when the ground under them changed FIX: (z9999) show factory detail info FIX: (kierongreen) Removing tram tracks from road caused crash FIX: restore speed limit after crossing deletion FIX: deletion of traffic lights led to crash with road deletion tool FIX: (z9999) traffic light position with drive on left fixed FIX: (z9999) filter overcrowded did not work in haltlist FIX: right map rotation in non-isometric view FIX: removing station from ground with other station one bridge/in tunnel could result in display/naming errors FIX: (marcr) minutes were wrong in no-month mode FIX: (robofish) underline error for help text H1 tags FIX: unreservation during convoi/track deletion fixed FIX: (z9999) factory reader and colors in minimap were wrong ADD: cityrules new parameter u/U ground is not/well suited for road ADD: cityrules new parameter renovation_percentage (default 12) to finetune new building/renovation ratio FIX: schedule was one off during reloading (and driving one field) due to missing initialisation of check_for_finish in vehikel_t(welt) FIX: convoi stuck on a slope, if pos_next was wrong CHANGE: calc_height only called if needed (about 5% faster game) ADD: (tomilepp) empty capacity line to freight_sorter CHANGE: sync_list changed from slist_tpl to ptrhashtable_tpl => remove from 5% to 0.3% game time for all sync-objects (cars, pedestrians, animated buildings, smoke, ... ) FIX: wrong outdated flag due to missing initialisation after loading of a convoi FIX: overflow in money frame color calculation ADD: now three lists for connecting stations, depending on the freight type => about 50% faster route search CHANGE: different (more compact) list of stations connected at a stop FIX: (kierongreen) station names in tunnels FIX: (z9999) monorail station extension buildings FIX: convoi could break up when during loadtime it was on its finally tile (also new detection for already broken convois) ADD: speed record for all kind of convois FIX: vehicles may have been saved with wrong pos_prev, which may caused errors at waypoints/stations FIX: blue night light finally working ADD: automatic line breaks in building description texts (inspired by tron) FIX: error with stale goods after deleting served stops FIX: no crashes with overlong headquarters strings CHANGE: more cpu-friendly wait for no route/waiting before a signal (and less fast stuck tooltips) FIX: finally really loading the Pxxx.bdf fonts (and generated new bdf-font based on old ones) ADD: (kierongreen) ways will look for replacement during loadtime in no matching besch found FIX: convoi speed reset to zero at traffic lights CHANGE: faster sorting in haltlist for waiting amount CHANGE: much faster scrolling with right mousebutton (previously this made an update for every pixel move) Release of 99.12 31-May-2007: FIX: explicitly check error codes when saving files FIX: cars now properly turn at road signs ADD: toggle grid now also in the visual settings dialogue "show grid" FIX: road remover fixed FIX: electric engine type was lost after saving CHANGE: UNDO now only denied, if something built on top of that way (including tunnels, bridges, stops, signals, electrifications ...) FIX: intended two way calculation during building of ways was calculating same route twice instead switching start and endpoints => strange routes could occur CHANGE: removed second route calculation and put a malus on a turn on the last field CHANGE: default way_double_curve=6 gives better results FIX: after showing bankrupt dialogue, fast forward is switched off FIX: crash, when a schedule had an illegal ground coordinate (new string: "Invalid coordinate") CHANGE: city car generation is now proportional to (log10(generated-transported+1)*density/16) Probability=1.00 mean on average one citycar per month FIX: wrong entry deleted during rerouting (vector_tpl will now assert there) FIX: waypoints work correctly with vehicles longer than 8 CHANGE: bits per month setting should now work up to 29, minutes are now displayed ADD: powerline category to money dialogue (and cleaned up, new string: This Month), also add headquarters to the dialogue FIX: powerline did not cost any maintenance (in simuconf.tab: cost_transformer=2500 and cost_maintain_transformer=100) ADD: b shows/hides reserved rails ADD: new roadsign type 'end_of_choose' to limit the range of free_route sings/signals FIX: construction over sidewalks did reset speed limit FIX: overflow when sorting vehicle list (defined signum for sint64) ADD: crossing between all ways (except air) are now allowed. See attached example. Priority will have the secondly defined way. FIX: do not show foreground for normal buildings FIX: starting long engines in a depot could cause all strange stuff Release of 99.11 4-May-2007: FIX: roads did repel each other too strongly. A little more clever handling has been introduced, but way_count_leaving_road is still too high. CHANGE: convois indicate error states also with tooltips FIX: factory crossconnection was one-sided ADD: midi shuffle will be remembered FIX: electric engines also recognized when not leading convoi FIX: advance savegame => field positions now saved ADD: more colors for convoi status: WHITE in depot, ORANGE: stuck/no route FIX: re-added city car info dialogue ("%s\nspeed %i\nmax_speed %i\ndx:%i dy:%i") FIX: speed of convois and city cars was frame rate dependent! FIX: deleting fields with factory CHANGE: vehicle now queue properly even on diagonal roads CHANGE: only the first vehicle in a traffic jam will report traffic jam conditions CHANGE: citycars are now only generated when there are not many passengers transported and the field is question is empty. Release of 99.10 17-Apr-2007: FIX: powerline connection fixed FIX: single slope up/down in one tile was broken FIX: powerline production AND consumption now based on factory production FIX: factory production per month now correctly displayed ADD: crossconnection for factories now switchable (crossconnect_factories_percentage in simuconf.tab) FIX: schedule sanity check could fail under certain circumstances (kierongreen) FIX: disallow going to depots if already under way (kierongreen) FIX: station with layout crash fixed (kierongreen) FIX: improved focus management FIX: vehicle filter list fixed for airplanes/ships (kierongreen) ADD: field support (kierongreen/prissi) (in simuconf.tab: cost_multiply_remove_field, in factory entry fields, new field with a number of image[x] definitions) ADD: miniwindow now shows also black background in underground mode (kierongreen) FIX: factories will now only smoke when producing, consuming or both FIX: crash related to building/replacing elevated ways FIX: loading wave sounds again CHANGE: simutrans now searches for data always in its root directory (-use_workdir to override this) CHANGE: simutrans user data on Unix now in "~/.simutrans" Release of 99.09.3 24-Mar-2007: ADD: jump to coordinate dialog added ("Jump to") called by "J" FIX: reloading games save in 99.09.2 from previous versions works could result in passengers without route FIX: colors in map window were still broken CHANGE: powerline now do not avoid slopes FIX: during building of powerlines the first tile had no net FIX: remove xyz strings were not translated Release of 99.09.2 16-Mar-2007: FIX: some errors with construction/deletion of oriented stations fixed FIX: bdf-fonts with more than 256 characters are loaded again FIX: tree with 3 seasons had same behaviour than the ones with two => ignore the third one FIX: loading demo-games with multiuser FIX: better compatibility with old save games (e.g. empty stops etc. ) FIX: reduce world size to (0,0) during deletion => less crashes, less complains in the log FIX: bus AI tries no longer to built on other players roads FIX: houses for stop names will be now selected in a fixed order => less double names ADD: you can limit the fast forward speed with fast_forward in simuconf.tab (but this is rather a course regulation) ADD: time multiplier for fast forward mode shown FIX: setting correct 2nd player color after reloading a game FIX: simu.log now saved in home directory FIX: automatic recognition of bitmap depth (failed with SDL on some old machines) => transparency fixed CHANGE: 16 bit color depth for GDI (may not work on all machines, please report) CHANGE: better recognition by the AI, if there is already a connection CHANGE: AI now built also tunnel and bridges, that are only one tile long FIX: also not altering old city roads (before only affected new city road) ADD: two more tweaks for the way builder: way_straight (cost for straight tile) and way_leave_road (to make existing roads more favourable) Release of 99.09.1 4-Mar-2007: FIX: time for pause/refresh interval now moved to sync_step() => handled internally each refresh => better responsiveness FIX: end of station with layout>4 in NS direction Release of 99.09 3-Mar-2007: FIX: on unix multiuser environments the path separator symbol was omitted FIX: marker did not work on elevated ways => no route, even if there was one FIX: no crash after deleting powerline crossing during game creation (but this was done by fixing symptoms, not the cause :() ADD: button for showing forest in minimap (new string "Forest") FIX: during rerouting of goods wrong entries were deleted FIX: singleuser_install in simuconf.tab was not read ADD: 8 player colors + second set of eight player colors, more colors to choose from, blue light color ADD: (kierongreen) possibility for automatic orientation (and facing of parallel) stops FIX: station coverage was wrongly detected FIX: ppm for all program for heightmaps (Photoshop created unexpected header) ADD: goods entries shrank further (now 12 Bytes pre packet) and saved lots of lookups ADD: obj=tree allows for seasons=3/5 with the third/fifth season having snowy winter tree image FIX: AI tried to built busstops in invalid positions FIX: InputSupplier[]=0 did not built new factories, even if required FIX: no depot on crossings or places with more than a single way CHANGE: a car will wait two months in a traffic jam, then it will complain. After three months it tries to turn; in unsuccessful, it will self-destruct. FIX: crash could occur with long_block_signals(); the fix will cause trains to slow down in front of long block signals similar to choose signals ADD: color for good in dat-file (color=index_nr, same as factory colors) CHANGE: category names now CATEGORY_xx and part of the pak set, replacing "piece goods", "bulk goods", "oil/gasoline", "cooled goods", "liquid food", "long goods" FIX: connection streets across slopes FIX: faster groundsearch also avoids artificial slopes Release of 99.08.1 6-Feb-2007: FIX: all tabs in depot show, if there was/is ever a matching vehicle FIX: all reserved signals after presignal or choose signals are now green CHANGE: save sound/midi volume FIX: midi now loaded correctly (first in usedir/music/music.tab, then program_dir/music/music.tab) FIX: unsigned long correctly loaded (i.e. load in schedules) FIX: correct baum types in the correct climate (hopefully final fix) CHANGE: bus AI will use always the cheapest road to survive a little longer FIX: games with failed to generate even a single town were saved wrongly FIX: houses without cities are again reconnected to the nearest city upon reloading FIX: length of string buffer for stations was 3 bytes too short Release of 99.08 4-Feb-2007: FIX: random_tree_for_climate was broken, and did return not always the right tree FIX: unmark map ground during underground deletion FIX: image for bridge ramps after reloading FIX: cities continue also ways on upslopes FIX: citycars properly turning, when there is no entry or only one tile after a crossing (and also make now 180° turns) FIX: (kierongreen) trains in small window are now correctly followed over bridges ADD: (kierongreen) withdraw of convois i.e. convoi is sold after it is empty (and no freight is loaded) 'withdraw' and 'Convoi is sold when all wagons are empty.' ADD: (kierongreen) no loading button to convoi info window added for manual control of a convois state FIX: crash when click on vehicle that is in a different depot than its home depot FIX: (kierongreen) underground mode resets not grid mode FIX: (kierongreen) height on slope during follow convoi was jumping CHANGE: shrank ware_t by eight bytes FIX: citycar destroyed themselves after a curve with a dead end FIX: wayremover in tunnel removed tunnel object also on starting and end tile CHANGE: goods now stored in an array instead of a list at stations => higher chances of joining => much faster (up to 50% for passenger/mail games) + less memory (up to 4MB); your mileage may vary. FIX: label were saved but not loaded FIX: crash when city tried to built new road to new townhall over existing player road CHANGE: only show goods which can be transported for status display at stations CHANGE: simuconf.tab is searched in pak/config/simuconf.tab after reading the main one in simuconf.tab (which is not needed) CHANGE: savegames and all other configuration stuff is now saved in the users home directory (resp. Eigene Dateien) if multiuser_install=1 (default) in simuconf.tab CHANGE: simuconf.tab files are now read in this order: program_dir/config/ then user_dir/config (for pak file path) than pak/config/ then user_dir/config for the rest CHANGE: new program option -singleuser to avoid using the users home directory (respective singleuser_install in simuconf.tab) CHANGE: removed maintenance_overhad, maintenance_ways from simuconf.tab, since it was not used FIX: forgot to charge for industry construction CHANGE: forrestconf.tab -> forestrules.tab, now maintained by the tree class itself FIX: building ways into depots again FIX: city does not build road through busstops, loading bays or depots FIX: underground depots are recognized again FIX: water image of artificial slopes fixed CHANGE: display of savegame pak-folder in the load/save dialog ADD: (kierongreen) long block signals: a train coming to this signal will only enter, if it can drive to the next signal, through all stops or waypoints (set is_longblocksignal=1 in the dat file) Release of 99.07 21-Jan-2007: FIX: crash when deleting empty tiles of middle parts of bridges FIX: wrong schedule entry pointer after loading FIX: crash when electrify before electrification FIX: citycars got stuck on slopes at height level -1 FIX: way remover with tunnels crashed at tunnel entry ADD: (prissi & kierongreen) transparent images for buildings, trees, and catchment area with an extended display dialogue (new strings: "hide transparent", "hide trees", "hide city building", "hide all building", "transparent station coverage", "show station coverage") FIX: improved citycar behaviour FIX: wayobj not dirty after creation => garbage on the screen ADD: shuffle for midis ADD: (kierongreen) colors for water height in minimap ADD: (kierongreen+prissi) better station coverage display in separate layer FIX: removing way was free of charge ADD: new label enables land buying (cost by 'cost_buy_land' in simuconf.tab, default -100), help file now possible ('label_frame.txt') FIX: wrong images after diagonal tile after bridges and crossings FIX: snowy bridges start on slope was wrong FIX: max midi volume now 255 => no overflow FIX: (kierongreen) pillar position was plainly wrong ADD: (kierongreen) using SDL_mixer for midi (now also with Linux, if possible) FIX: crash during deletion of bridges and tunnel (although just fixed symptoms) ADD: waterdepths (needs new ground obj "Water", with n images, n=0 is uppermost, 1=deeper, 2=even deeper, ... FIX/CHANGE: roads will not be overtaken, if they are not public property ADD: shadows for airplanes (still wrong at non-straight slopes) Release of 99.06 08-Jan-2007: (prissi) FIX: crash when closing the filter window before the main list window FIX: not zooming out menu icons FIX: drawing of single direction signs FIX: stations of AI were not added correctly FIX: some minimap setting were forgotten during season change FIX: curves in tunnel again FIX: way owner was lost after saving FIX: channels were not connected to the sea in all cases FIX: crash, when building has not tile (0,0) due to empty image on info FIX: diagonal wayobj again FIX: checked for wrong offsets in a schedule during load/save FIX: obey BBX completely in BDF-fonts FIX: landing airplanes now recognize the ground CHANGE: cleaned up midi code, now maximum 128 songs, "music.tab" now "music/music.tab" FIX: bug when loading stations etc. that were not animated but put into the animation list CHANGE: wayobj will be deleted before a station CHANGE: ground can no long be owned, only objects on it (need some tweaks with labels still) (kierongreen) CHANGE: snow for tunnel and bridges FIX: right mouse button scrolling in minimap Release of 99.05.1 17-Dec-2006: FIX: idle timer has eaten up all time doing additional frames FIX: issue with transformers FIX: crash in depot FIX: off-by-one error which can lead to non-unique city names FIX: DWITH in bdf-fonts was not used FIX: check for allowing artificial slopes was broken FIX: loading signals FIX: loading bridges FIX: display in depot for left hand driving CHANGE: depots now maintained in a list => underground and monorail depots are found again and shown in minimap FIX: crash when building tramways ADD: scrollbar for label frame (mip) FIX: now frames per second direct setable (refresh was not the right way for slow computer) new parameter "-fps", "-refresh" is obsolete; you can also use frames_per_second in simuconf.tab FIX: pillar of neighboring bridges now at the same positions FIX: depot search could crash game when pressed twice very fast Release of 99.05 03-Dec-2006: FIX: always ensure tile is empty before applying slopes (avoids wrong positions) FIX: deletion of thing not on the map FIX: buying cars with different predecessor/following numbers FIX: elevated tracks not shown FIX: waypoints for planes CHANGE: 4 bytes less per planquadrat, 4 less per ground (now down to 32 bytes per tile without objects from previous 48) CHANGE: koord3d is now only with 1 per height step => much less hassle with heights and again bytes saved CHANGE: squeezed another four bytes from tree structure => now only 20 bytes per tree => 16 MB less on 2048*2048 maps CHANGE: got rid of ding_t::step() => large maps should now be as fast as slow maps with the same factory, citycar and city count CHANGE: objects now take care of their image themselves => another four bytes off from ding_t (smaller is not possible, imho) FIX: larger than 1x1 building on slopes CHANGE: game will continue even while dragging (still little buggy) CHANGE: cityrules allow now up to 7x7; more fine control about the res/ind/com clustering too (see cityconf.tab) CHANGE: option to not showing vehicles which are not matching current action ('Show all' and 'Show also vehicles that do not match for current action.' strings added) CHANGE: zoom in and out 'map zoom' in the small map only with a control, more user friendly button positions 'Show legend', 'Show map scale', 'Show industry' ADD: auto-centering on minimap ADD: 45 rotated minimap 'isometric map' ADD: tunnel entrance only built with control key CHANGE: corner heights only bytes instead of long => less data to handle during savegames FIX: track remover for tunnel and monorails fixed FIX: building trough depots FIX: retire/intro messages for ways ('way %s now available:\n' and 'way %s cannot longer used:\n') ADD: winter ways form kierongreen Release of 99.04.2 20-Nov-2006: (prissi) FIX: images inside tunnel FIX: buying airplanes again FIX: minimap remembers size again FIX: old errors with too short vehicles CHANGE(tron): lots of cleaning up of data structures and doubtful code FIX: makeobj broken at several places, not really finished with fixing CHANGE: the same building must not be next to each other => nicer endgame towns FIX: AI no longer using elevated track or tramways as rails FIX: info window title for ways fixed Release of 99.04.1 15-Nov-2006: (prissi) FIX: in depot: articulated engines will be only bought forward respective backward from the current position on FIX: bridge/tunnel construction/deletion fixed FIX: retire messages for planes FIX: way maintenance was screwed up FIX: depots had no maintenance after reload FIX: button offsets Release of 99.04 13-Nov-2006: (prissi) FIX: elevated construction could only connect via straight connections FIX: tunnel rails were upgraded to even higher speeds than allowed FIX: no more accidental connection between bridges/tunnels FIX: no more upgrading track on bridges or in tunnels FIX: way search for ways below monorail fixed CHANGE: unified construction of rail, monorails, maglev, channels, airstrips by baue_schiene() FIX: position now nearly always centered for all zoom levels and pak sizes FIX: scrolling with right mouse button for all pak sizes FIX: rectangle in minimap now shows the real screen size (these three were in for ages ... ) CHANGE: (tron) assembler corrections, code cleanup, reader definition more C++ style CHANGE: tunnel code changed (might not load 99.03 savegames!) CHANGE: 6 byte less for planquadrat_t (i.e. per tile, although GCC gives us only four of them) CHANGE: 8 byte less for grund_t => 14 bytes less per tile (12 with GCC) => map requires ~ 15-25% less memory CHANGE: rewrote dingliste: always well defined position, about 10-20% faster insert of vehicles CHANGE: articulated vehicles are now always bought together, if there is only a single choice Release of 99.03 03-Nov-2006: (prissi) FIX: city creation on new map fixed (actually rectified deletion) FIX: tunnel can now properly handle all way types FIX: cost of tunnel construction FIX: left and right position of buttons (you may skin round buttons now ... ) FIX: translation now shows the correct signal type in info window FIX: button will update their translation every time a dialog is opened FIX 'convoi passed last\nmonth %i\n' '\nRibi (masked)' '\nRibi (unmasked)' 'km/h\n' translatable ADD: (kierongreen) underground mode (toggle with 'U') You can built depots and station, interrupt tunnels in the middle but not much more ADD: preliminary underground construction routines (user the tunnel icon in underground mode; must start on a tunnel!) ADD: elevated support for nearly all waytypes (but not tested) FIX: stations on slope got wrong height offsets CHANGE: maxspeed/friction calculation is now only done when entering a tile => 16x less function calls Release of 99.02.2 21-Oct-2006: (prissi) FIX: unreservation of tracks failed fatally if there was no ground connected to the next tile FIX: factory with there 0,0 on slopes did not recognized their position (not found by other factories, no production) CHANGE: if a city could not built a road at a certain slope, it built a house instead. FIX: button definitions for skins were ignored FIX: demo savegame loading at startup fixed CHANGE: same further optimisations of certain routines ADD: attribute for the help texts CHANGE: first delete station, then caternary Release of 99.02.1 03-Oct-2006: (prissi) FIX: game could lock up during creation when no matching tourist attractions were found for the available climates ADD: new flag in simuconf.tab "ground_info" to show info on ground tiles FIX: platzsucher did some meaningless comparisons and did not consider all suitable places (i.e. slopes) FIX: factory builder did add one too few to the distribution weight => with n factories with distribution_weight=1 the last was always forgotten FIX: wrong/missing foundation during construction on slopes FIX: if no townhall with bev=0 is available for a climate, no towns will be founded in this climate FIX: redraw problem when changing window size FIX: line remembers statistic settings in the line management window FIX: many warning with VC++ fixed, project included FIX: font may also have a maximum width of 12 pixel (was even faster than previous drawing routines) FIX: no longer support for animated ground (interfering with the texture code), removed "animated_grounds" from simuconf.tab Release of 99.02 27-Sep-2006: (prissi) FIX: display depots after reloading FIX: autoscrolling was still outcommented FIX: loading bar position in list display FIX: not roads on foundations CHANGE: progress bar now always half display width, two more messages "Loading map ..." and "Saving map ..." CHANGE: default setting for new map will be loaded from default.sve in the main directory. This file can be a savegame, but will be overwritten the next time a game is started. Release of 99.01.1 26-Sep-2006: (prissi) FIX: ground below houses was draw even if not needed FIX: crash when messages were generated at load time FIX: display error, when no passenger vehicles but engines were in a depot CHANGE: auto resize for scrollbars on all textarea objects (if they have some) FIX: building transformers, less reallocation of nets ADD: engine_type 'battery' added FIX: not loading passengers/mail at freight locations FIX: house where not recognized in old savegames CHANGE: after lot of tries settled now in binary heaps for pathfinding, up to 15% faster FIX: display of animated buildings fixed FIX: monorail and channel tunnels fixed FIX: (again) not connection through depots and 4-stops during city expansion Release of 99.01 18-Sep-2006: (prissi) FIX: redraw after window resize FIX: climate dialogue FIX: display in townhalls FIX: images in depot again FIX: pause at waypoints CHANGE: automatically loading pak/demo.sve (or whatever the pak folder is) on startup CHANGE: symbol.BigLogo.pak with maximum for image loaded during initial startup CHANGE: more complex tunnels, with cost=, maintenance=, topspeed=, waytype=, intro- and retire dates CHANGE: allowed tunnels for all but air, also removal or tunnel with tramrails should work FIX: city roads no longer destroy runways and the like FIX: more even house distribution with rotation Release of 99.00 10-Sep-2006: (prissi) CHANGE: full climate/season support CHANGE: sync_prepare removed Release of 90.00.1 3-Sep-2006: (prissi) FIX: unicode routine FIX: memory for citylist names was one too few FIX:/CHANGE: menus are now adapted during player change (in new module simmenu.cc) FIX: planes now also can do approaches with one way markers on them FIX: dummy vehicle for way removal/upgrades now always successful FIX: wrong prices after starting a map in beginner mode and next in normal mode Release of 90.00 2-Sep-2006: (prissi if not noted otherwise) FIX: clipping error at outmost right row for bitmaps FIX: forgot stop choose after closing schedule after line change FIX: force beginner/normal mode after loading ADD: (kierongreen) additionally freight image for vehicles (syntax 'freigthimage[no][direction]=' with the good definition in 'freightimagetype[no]=') FIX: more error check to makeobj pak for vehicles (wrong numbers etc.) FIX: missing cstring_t initialisation in slist_tpl; since cstring_t only used in translator.cc and searchfolder.cc, these two converted to slist_tpl ADD: color codes for lists of lines and convois (minus: red, not moving: yellow, obsolete: blue, no vehicles: white) FIX: slow building of ways with log of ~ding() error messages ADD: extract command for makeobj (also got rid of the need for ./ for defining the main file in most positions) FIX: restoring old signals was badly broken => many convois were stuck FIX: (tron) many errors fixed, working inline assembler for GCC 4.0, imported all old versions into SVN CHANGE: citycars lifetime now in ms => they will be more controllable, since generation is also linear with time (also cleanup of pedestrian code) FIX: overheadwires could be now replaced by a faster type, player was not check during destruction FIX: signals were always built without owner => everybody could destroy them FIX: problem with depots not found: saving and loading should solve this, no longer deleting other people depots Release of 89.03.2 21-Aug-2006: (prissi) FIX: just_in_time is saved with a map FIX: beginner mode properly set/reset with maps FIX: oneway signal on last tile of a route not a problem anymore FIX: height of bridgeheads fixed FIX: loading vehicle image display now matches state of the car FIX: loading vehicles just on their last tile (which also contains a signal) fixed FIX: next depot only finds own depots FIX: proper recalculations yearly income after loading FIX: only change game name if not autosaved FIX: powerlines over monorails and stations but not crossing bridges and elevated monorails in its height CHANGE: numbered station now numbered per town (two new strings '%s city %d %s' and '%s land %d %s') CHANGE: stops covering the townhall will be added monuments 'monument' and townhall/tourist attractions according to their name CHANGE: two more center names '6center' and '7center' Release of 89.03.1 17-Aug-2006: (prissi) FIX: reward for boats in harbours FIX: beginner mode restored after reloading FIX: selection for scrolled list (like in the line dialog) fixed FIX: some corrections for SimuTTD and half tile height in minimap and other places Release of 89.03 13-Aug-2006: (prissi) CHANGE: you can pak object with any size (smaller than 255). The tile size will be taken now from "ground.Outside.pak" (Go for big ships, Timothy) FIX: overhead maintenance take from pak, not from simuconf.tab any more FIX: code file cleaning up FIX: signal appearing immediately after direction changes FIX: mark dirty for higher buildings CHANGE: beginner mode 'Beginner mode' with 1,5times higher prices (can be configured in simuconf.tab) and no just in time factories FIX: under certain circumstances, the wayobj/wayremove found an electric engine and could not lay a caternary ... FIX: hopefully, convois will be no longer try to calculate a route to the position they are already FIX: only money for vehicles in stations FIX: correct number of vehicles reach into the station (hopefully) CHANGE: better progress indicator slowest process (city size growth and road connection) now take most of the bar FIX: full scrolling in map window FIX: no longer messing around in other players depot FIX: restored functionality of 'e' again ADD: generic names for city buildings: If no translation is available, they will be called 'residential house', 'industrial building', or 'shops and stores' Release of 89.02.4 12-Aug-2006: (prissi) FIX: images of bridges after built fixed CHANGE: company color for way and ground (but slower loading, since all images are scanned for company color) This was a heavy rework of the dirty system ... FIX: result of heavy renovation of tile dirty system: much less area copied during each redraw, less garbage pixels left FIX: citylists work again FIX: dummy roadsigns everywhere after reloading games (and info for debug purposes "\nwith sign/signal\n") => stuck trains FIX: coverage area signs now always refreshed after building/deletion FIX: Factorylistbuttontext translated twice Input -> Eingang -> Elevated station ... FIX: more error checking during parsing of help files, unlimited length (before maximum 8192 bytes) FIX: compile error on MAC spotted by Timothy FIX: convoi loosing its name after many actions in the depot ... FIX: choosing of bridges works again FIX: signal and roadsign position on slopes Release of 89.02.3 06-Aug-2006: (prissi) FIX: choose signals and convoi starting repaired FIX: waypoints in tunnels FIX: a little nicer vanishing in tunnels in westward direction ADD: new marker graph FIX: production of factories with large storage or high rates fixed Release of 89.02.2 06-Aug-2006: (prissi) FIX: loading pak translations fixed ADD: ids displayed for convois ("cl_btn_sort_id" sort by convoi ids) FIX: wayobj were not displayed FIX: minspeed signs (and many others) work again for cars (fixed the sprite sorter) FIX: trains ignoring signals after reloading ADD: trams rail over road bridges and through road tunnels FIX: public owner can now build stops anywhere; adjacent stop will be taken over by the public owner; the price is 36*monthly maint.*level! FIX: no depots for public service, since this player is not intended to run any convois FIX: display error with diagonal wayobjects ADD: generic help for factory window Release of 89.02.1 30-Jul-2006: (prissi) ADD: braking before red signals and at the end of stations in the following steps 200,100,50,25 (only stations) FIX: no airplanes servicing oil rigs any more ADD: new text "\nis reserved by:" for showing reservation on tracks FIX: bus checks for electric route Release of 89.02 24-Jul-2006: (prissi) FIX: roadsigns shown in the wrong menus FIX: not cost display for ways FIX: roadsigns error after loading FIX: wrong clipping for scrollbars ... still ... CHANGE: more than one translation for a language in pak/text/ possible CHANGE: if there is a file pak/compat.tab the names will be used to search for vehicles during loading => obsolete vehicles can be appropriately replace + better loading of 128 under 64 and vice versa FIX: correct sorting of goods by name CHANGE: no more railblocks => no signals on crossings (which did not worked well before too), but signals are now only a single tile! ADD: new object way-object for overheadwires or avalanche protection; allows for electric roads too ... top speed in wayobj is limited FIX: correct weight for attractions CHANGE: automatic align of city buildings to the next road, if there is more than one rotation FIX: tunnel construction FIX: clouds now better vanishing CHANGE: one way signs and signal discovered also during routing Release of 89.01 15-Jul-2006: (prissi) FIX: depot with wrongly initialize vehicle list could crash FIX: crash, when selling just under the last line FIX: crash when selling trains with PBS CHANGE: convoi list to array FIX: unreserve route when reserved track is deleted FIX: find route until station end without help FIX: AI now knows about short vehicles and can built smarter stations more often FIX: clipping of lists FIX: one way signals on platforms have been ignored ADD: dates and "waytype=" for signs (not only roadsigns, although only the no enter sign will work on all ways) FIX: crash when deleting world Release of 89.00 (preview) 1-Jul-2006: (prissi) ADD: path based signalling (PBS): There can be many trains in a block as long as their path do not intersect. Release of 88.10.5 (stable) 1-Jul-2006: (prissi) FIX: maintenance for bridges works again FIX: values in finance history init to zero at the start of new year ADD: fences at slopes Release of 88.10.4 24-Jun-2006: (prissi) FIX: margin calculation now (finally) taken into next month CHANGE: finance update only when dialog open or during new month FIX: monorail again above buildings FIX: remove ownership also with undo and track delete FIX: do not hide depots FIX: slopes where buildings are lower than the natural slope will be preserved FIX: signal at bridges now are drawn in the correct height and will not connect to tracks below FIX: open a schedule default mode now "add stop" also selected (not only button shown ... ) FIX: a convoi now only searches for depots that belongs to its owner CHANGE: color indicator now always go from zero to the actual maximum value in the minimap CHANGE: display value in chart under the mouse when clicking on it FIX: harbour under bridges fixed (only a single stop on a tile is now really enforced) CHANGE: search for stop now inline => likely faster search and routing of goods ADD: new error message concerning dock: "Dock must be built on single slope!" FIX: scrollbar in factory windows FIX: clipping errors when letters were vertical (top) clipped FIX: the "cost=" entry in the roadsign files were not correctly saved by makeobj FIX: forgot to add "needs_ground=" to the building section of makeobj. If needs_ground!=0, grass (or snow) will be drawn below a building Release of 88.10.3 19-Jun-2006: (prissi) FIX: wrong buildings on slopes, transparent bottoms FIX/CHANGE: forbid to delete a line assigned to convois (but you could do it without crash anyway again) FIX: set dirty of smoke CHANGE: only smoking chimneys when producing/consuming FIX: another try to fix the depot window to prevent wrong obsolete vehicles FIX: faster, less memory consuming route calculation (about factor of 10-50 for ships) FIX: waybuilder much faster calculation and finally builds diagonals as expected (may have one curve too much but is up to 4000% faster that way) FIX: waybuilder now only generates one sound per tile after finishing construction and you can read the total costs per tile (not painting over each other) CLEANUP: removed all support for savegames before 84.06 since they were not loadable anyway CHANGE: remember last name with that the game was saved CHANGE: hide all buildings (special up to five additional construction sites, if there) CHANGE: allows for non-square factories (leave the empty field out, not fully tested yet) Release of 88.10.2 10-Jun-2006: (prissi) FIX: trains/trams in depot reserved the according block automatically for convoi zero during reloading (even if this was a ship ... ) FIX: sprite sorter now inserts all sprites including pedestrians and citycars in the order they belong (drawing errors with trailer trucks fixed) CHANGE: hour glass during saving ADD: display of powerlines/depots in the map window FIX: delete road under the powerline of a different player CHANGE: after deleting everything from a tile one loses the ownership on this tile again FIX: when the number of vehicles changed during reopening of a depot dialog (either year or electrification) the game crashed ADD: you can create a roadsign which prevent citycars from entering. Use "is_private=1" in the roadsign definition (will be not part of the 64 set though!) ADD: draw way as a normal object by "draw_as_ding=1" in the definition of a way CHANGE: (internal) all defines now in simconst.h" Release of 88.10.1 4-Jun-2006: (prissi) FIX: textinput fields work again as expected, mouse sets cursor position FIX: several places were handling player colors incorrect or not at all FIX: several small clipping errors fixed CHANGE: compiles again with MS Visual C++ Express => better debugging, some little errors fixed, esp. overruns Release of 88.10 2-Jun-2006: (prissi) CHANGE: A* vehicle route finder strongly reworked, extensive tests with priority queues versus simple arrays => arrays are up to 10x faster (less lags with ships, no crashes any more) FIX: elevated monorail slopes during construction FIX: arrows with lines work again in the depot CHANGE: map array now displays always vehicles and is not updated when not open Release of 88.09.6 9-May-2006: (prissi) FIX: raising and lowering land works again as intended FIX: loading slopes for bridge heads FIX: assignment of more than 6 lines ... CHANGE: reader more endian compatible CHANGE: obsolete entry "2LIGHT_CHOOSE" FIX: translator looked words up twice (eating lots of performance with many dialogs open) FIX: line assignment during reloading fixed FIX: depot is updated once before showing it FIX: station ground name was copied on itself FIX: workaround for Linux 4.0.3 compiler bug (will use the C-routines for image display instead of assembler) FIX: several issues with unassigned values and fixed using valgrind Release of 88.09.3 4-May-2006: (prissi) FIX: recompiled with hopefully corrected files => bridges/marker correctly, line number ok again FIX: ownership of public owned road is taken when using trams FIX: last line in schedules selectable again CHANGE: slope now part of ground structure => less errors with monorail over houses CHANGE: gui-components and dialogs mostly separated, lots of code cleanup Release of 88.09.2 1-May-2006: (prissi) FIX: houses in cities on slopes again FIX: can again delete last town too FIX: translator automatically adds "\n" to stations => simutrans will remove this FIX: road under tramway works now as well ADD: scrollbars to save/load dialogs FIX: next month starting date could have been wrongly calculated FIX: age of old vehicles again calculated correctly FIX: depots have again an owner FIX: only clearing trees when building ways, stations and depots will remain (no planes etc. any more) FIX: block cleared correctly, when entering or leaving a depot FIX: no more building factories with distribution_weight=0 FIX: station bar indicator only at stop basis position FIX: bridges in miniwindow CHANGE: internally now allows cover tiles CHANGE: made redraw faster, but set refresh again to 1 for smoother movements. Use -refresh=2 for old speed FIX: engines shorter than 8 units stopped too early in a station => station tile indicator was wrong FIX: convoi mini world view scroll more smooth and displays planes correctly FIX: passengers from/to monuments after reloading FIX: one month too many after reloading FIX: player colors for AI changeable CHANGE: option window renovated (mostly internally, make much more consistent with current windowing system) Release of 88.09.1 23-Apr-2006: (prissi) FIX: signal on illegal coordinates now checked CHANGE: passenger generation now proportional to the inhabitants show ADD: passenger generation can be now adjusted using "passenger_factor" in simuconf.tab. 16 equals the old value. FIX: track deletion of bridges and tunnel now completely deletes them FIX: several errors/inconsistencies with the line management dialog fixed FIX: clipping error when switching players FIX: trees should now regrow at their intended rate FIX: map window needed sometimes a right click to activate CHANGE: mousewheel support for nearly all dialogs for the vertical scroll bar FIX: again normal number of pedestrians, since you can now built on them Release of 88.09 19-Apr-2006: (prissi) FIX: renovation townhall city update (again ...) FIX: lines were created double in depot and not shown in the selection CHANGE: city border are now recalculated after reloading => border can shrink again manually by this CHANGE: deleting a town now also deletes all houses within this town CHANGE: three stage map info window CHANGE: houses now indicate their town in the info "Town: %s\n" CHANGE: renovated player tool ADD: long tracks/monorails/road/channel removal tool FIX: choosesignal deletion was not handled on two places in the blockmanager CHANGE: scrollable help windows with "more intelligent" size calculation CHANGE: road construction also on used tiles, i.e. on top of pedestrians ... Release of 88.08.1 16-Apr-2006: (prissi) FIX: all unnecessary warnings corrected (and an error in the AI module found) FIX: some problem with initialized values fixed FIX: broken convoys after curves, much less "jumping" at waypoints FIX: line selector in depot now hopefully finally fixed FIX: wrong naming of industry stops with numbered stations FIX: 64er aircraft cost optimized for timeline Release of 88.08 14-Apr-2006: (prissi) FIX: line selection in depot window CHANGE: lines now sorted alphabetically CHANGE: new parameter for vehicles in dat files: length=xyz for the length i 1/16 of a tile (default: 8) => much easier stem engines with short tender etc.; but: vehicles with len!=8 better have 8 views, else ... The standard vehicle length are like this for 64: S or W: x=2*len, y=len SE: y=2*len SW: x=4*len CHANGE: no jumping at waypoint on diagonals CHANGE: depot window now displays minimum station tiles needed ("Station tiles:") FIX: signals on stations should now work as intended, even with pre- and choose-signals FIX: when current schedule entry is the current depot, not route message was show => now advance to next entry FIX: many wagon overlapping drawing errors with trains Release of 88.07 9-Apr-2006: (prissi) CHANGE: asynchronous updating of routes and rerouting to avoid lags when building stations/change schedules CHANGE: more CPU-friendly choosing for trains ADD: block tool (b) also checks for double or orphan block and block without signal and corrects it ADD: invalid block ids are now corrected during loading FIX: crash using (t) when no rails are available FIX: signal state in front of waiting train now always red, even for presignals and choose signals FIX: better check for double entries in schedule to avoid crashing CHANGE: line management convoi info more consistent color/names (removed string from translator: "Convois: %d\nProfit: %s"), added min size button FIX: minor display errors with line windows CHANGE: line now accessed via handles (more stable programming, less memory required) FIX: after changing max speed at depot the convoi info shows new max speed FIX: many errors with list management fixed FIX: no loading of smoke => no wrong images emerging after updates FIX: lower right border error when loading reliefdata fixed FIX: wrong speed on bridges after loading a saved game CHANGE: after creating a new line, this line is immediately selected in this schedule window (removed text: "New line created!\nYou can assign the line now\nby selecting it from the\nline selector above.") CHANGE: clicking somewhere else into the dialog will close the line drop down box in schedule and depot dialog FIX: construction of ship depots now again everywhere FIX: redraw error in depot (part of vehicle was visible in certain window sizes outside the box) Release of 88.06.3 ("stable") 18-Mar-2006: (prissi) FIX: deleting of a station did not update the corresponding routes FIX: sometimes route with itself as first interchange appeared FIX: choose signals at a corner now working FIX: more diagonals during road construction ADD: tramlines FIX: convoi changed in depot did not display correct capacity/loading info Release of 88.06.2 10-Mar-2006: (prissi) FIX: after passing a choose signal a single time, all presignals will stop the trains, if not the complete route is free FIX: faster rescheduling of goods now works on demand (after changing a schedule) FIX: error with choose signal fixed, if target tile was not a station FIX: changing ladegrad in schedule first selected invisible item on top of a scrolled list FIX: ladegrad was ignored, if absolutely nothing was there to load or unload CHANGE: choose signal will reserve the full way until the target location FIX: traffic light switched to seldom after reloading FIX: zoom out error with 128 pak and electrification Release of 88.06.1 05-Mar-2006: (prissi) FIX: one way signals/roadsigns work together with the choose points FIX: monorail ground saves slope (otherwise the slope could be changed by building below) CHANGE: schedules are now recalculated more often FIX: no tunnel or bridge destroyed by slopes anymore ADD: indicator for convois stuck for more than a month FIX: much less redraw troubles with slopes (will be drawn as foreground, if any object is behind them) ADD: season indicator FIX: UNDO did remove tracks but no electrification FIX: block reservation now saved CHANGE: made line chart more slim FIX: when vehicle braking was larger than acceleration in curves => acceleration to insane speeds FIX: removing halt with no ground during load FIX: only removing really empty bridges without stops FIX: several translator text adjusted with program texts 04-Mar-2006: (prissi) FIX: choose trains go to the end of a platform again FIX text changed to "Die Bruecke ist nicht frei!\n" FIX: correctly loading and saving monorails Release of 88.06 04-Mar-2006: (prissi) FIX: airplanes check for exit the map (although this should never happen) FIX: runways may only built up to four tiles next to the border FIX: no depots on top of depots FIX: too young trees with different timescales FIX: sort options in convoy dialog were not correctly remembered FIX: presignals works with monorail CHANGE: presignals now reserve needed blocks => hopefully less deadlocks (but may be screwed up during reloading) CHANGE: choose signals are possible (as result of the above change) CHANGE: buttons and labels are now translated, when the text is assigned Release of 88.05.1 01-Mar-2006: (prissi) FIX: townhall added wrong buildings during renovation => crash when generated passengers afterwards FIX: connecting to the end of an unconnected bridge (now again going through tunnel and bridges for way calculation) CHANGE: sync_prepare() only called one time before sync_step() => lost of useless objects are not called any more every frame FIX: trams find depots again CHANGE: built on top of cars or pedestrians (may cause graphics errors) FIX: oilrig and other water factories fixed when loading old sets FIX: wrong sound when closing tool bars FIX: again correct gear in convoi details FIX: several wrong sounds/smoke for vehicles FIX: more error checks for way builder FIX: change to entries to their translator entries: Oktober and Load game CHANGE: all list: left key info, right key goto Release of 88.05 25-Feb-2006: (prissi) CHANGE: runways now system_type=1, independent of max speed FIX: traffic density=0 => no cars generated FIX: townhall construction with uneven grounds FIX: maintenance adjusted according acceleration factor FIX: no longer roads through depots, bus stops or loading bays (hopefully) FIX: sound hashes were wrongly calculated => hopefully fixed FIX: player initialisation during map creation not allowed for passenger stations for player 0 and 1 24-Feb-2006: (prissi) FIX: crash in waybuilder when runway starts on streets or when a way costs no maintenance FIX: wrong slope display (was back to old state?!?) FIX: tunnel block connection FIX: connection rails with tram rails FIX: deleting bridges with roadsigns and stops CHANGE: extended bridge search for unknown bridges during loading CHANGE: all vehicles smoke, if requested FIX: stop of passenger generation when timer overflowed fixed CHANGE: only relative time steps used => much faster and more reliable for the game FIX: nearly complete renovation of blockmanager FIX: F12 key does not crash game anymore CHANGE: bridge may end at vertical slopes FIX: "harbour tunnel" works again (as some other complex tunnels too) 23-Feb-2006: (mip) ADD: citylist including sorting 21-Feb-2006: (prissi) FIX: checking all schedules during loading for illegal entries CHANGE: try even harder to find the right vehicle during loading (=> more chances of loading broken savegames/games from other paks) ADD: new type "monorailstop" for monorail stations and extension buildings (=> makeobj=33) ADD: slower timespeed possible (set bits_per_month in simuconf to values higher 18) and display of day possible (show_month>=2) see simuconf.tab Release of 88.04.4 19-Feb-2006: (prissi) FIX: saving depots FIX: display of attractions Release of 88.04.3 18-Feb-2006: (prissi) FIX: sum up freight of same catg again CHANGE: extended convoi detail dialog, mover kill button to details FIX: ugly tunnel bug changed translations: added: Leistung: %d kW removed: Transported Goods 15-Feb-2006: (prissi) FIX: track builder always failed in a station CHANGE: cleaned up the depot code code, monorail and trams are now classes FIX: monorails no go again to the end of a station FIX: expensive rails are no longer replaced by cheaper ones CHANGE: larger passenger destinations in city window CHANGE: unified color scheme for charts 13-Feb-2006: (prissi) FIX: error check when building stops on completely empty tiles FIX: deleting harbour: water again at correct position ... Release of 88.04.2 12-Feb-2006: (prissi) FIX: monorailine correct FIX: again tram depots FIX: again air stops FIX: remove unused vehicle list (that was really eating memory ... ) FIX: sell again vehicles in depots without removing them Release of 88.04.1 11-Feb-2006: (prissi) FINISHED: full monorail support including ramps (using a bridge) FIX: line financial calculation fixed FIX: line window 8-Feb-2006: (prissi) CHANGE: monorail line FIX: deleting habours CHANGE: goods (bonus) frame FIX: aircraft in depot broke savegame CHANGE: further monorail support 7-Feb-2006: (prissi) FIX: curiosity list was cutting names FIX: no passengers generated after loading of a map CHANGE: unified schedule routines FIX: convoy wait for loading on empty stations work again FIX: convoiinfo help window again FIX: connection neighbours rewritten to check all ground levels => should work with subways too (if they come) FIX: deleting busstops CHANGE: monorail now separate from rail vehicles CHANGE: blockmanager reworked in many aspects 6-Feb-2006: CHANGE: all station buildings routines unified => no crashes of no city there, only two error messages: "No terminal station here!" (4 layout) "No through station here!" (2 layout) => also terminal stations for bus stops, trains or channels CHANGE: all depot buildings routines unified => only one error messages: "Cannot built depot here!" CHANGE: cursor normal after building depot still not fixed: connection code 5-Feb-2006: FIX: line window crashed when opened twice ADD: covertiles over vertical trogs FIX: no vehicle beaming any more (was waiting in sync_step instead step!) Release of 88.04 3-Feb-2006(prissi): CHANGE: big code cleanup, german translation cleaned of all unnecessary strings => all official strings now there CHANGE: map legend now closes with main map FIX: linking order for gdi (=> again wav sound) ADD: error message, if a feature is not yet available. 2-Feb-2006(prissi): CHANGE: sound system now working; all sound are save under pak*/sound with a description either in sound.tab in the same folder or a wavefilename in the vehicle description CHANGE: new menu order, separated monorail and tram; Help button. New text RAILTOOLS, MONORAILTOOLS, TRAMTOOLS, ROADTOOLS, SHIPTOOLS, AIRTOOLS, Help and Fast forward CHANGE: loading indicator and dummy message if no simuconf.tab is found (to avoid 80% of the complains I get via mail!) 31-Jan-2006(prissi): ADD: cityrule T and t for not a stop/stop CHANGE: schedule do not allow waypoints for airplanes over stops FIX: deleting water bridges CHANGE: sound paks halfway done 30-Jan-2006(prissi): FIX: building/destroying bridges, wayfinder for bridges FIX: cost of houses FIX: crash when deleting stations FIX: go home button reactivated ADD: convoi details (new strings: "Manufactured:", "Power:", "Gear:") Release of 88.03.2 29-Jan-2006(prissi): FIX: display of number during new world dialog FIX: renovation townhall works again as intended FIX: convoi count during reloading fixed (leads to crash of line management window) Release of 88.03.1 29-Jan-2006(prissi): FIX: no crossings through stations/bus stops for normal tracks FIX: station naming with industry (%s building %s %s) for industry stops 28-Jan-2006(prissi): FIX: loading lines FIX: convoi info display: much less recalculations during loading/unloading CHANGE: now only the free load in terms of categories is given (not goods) FIX: removed several unused memory wasting variables from gebaeude_t CHANGE: now town passenger skips tourist attractions during town loop iteration => not iterated twice FIX: town statistics 27-Jan-2006(prissi): FIX: New year calculation for Autosave/timeline ... FIX: disable arrow buttons CHANGE: goods list design FIX: new world info some fixes FIX: bridge builder for channels 26-Jan-2006(prissi): FIX: init line ids when destroying maps CHANGE: order of buttons in halt list 25-Jan-2006(prissi): FIX: acceleration on slopes ADD: Repeatbuttons ADD: correctly routing through tunnels again FIX: wrong color in finance window FIX: monuments were not added to towns FIX: tunnel next slopes fixed 25-Jan-2006(mip): FIX: factory list and curiosity list Release of 88.03 23-Jan-2006(prissi): ADD: automatic alignment of station extension buildings (must be either defined with dim=x,y,layout (layout=1,2 or 4), x>=y and following order N E S W) 22-Jan-2006(prissi): CHANGE: now line management for every player 21-Jan-2006(prissi): CHANGE: station now have a capacity of 32*level; thus price reflect station capacity CHANGE: changed owner of several dialogues CHANGE: new world functional, new string "Intercity road len:", obsolete ones. "7WORLD_CHOOSE", "7WORLD_CHOOSE", " 18-Jan-2006(prissi): CHANGE: new town population/passenger destination system (takes into account only existing buildings) CHANGE: new world dialog renovated 17-Jan-2006(prissi): ADD: automatic slider recalculation at halt info and convoi info CHANGE: consistent colors for negative and positive amount in dialog (red/black) FIX: wrong maintenance calculation when replacing roads (include other players ones) 16-Jan-2006(mip): CHANGE: new attraction list ADD: overlooked the following translations: "\nelektrified", "\nnot elektrified", "\nRail block ", "\n\nRibi (unmasked) ", "\nRibi (masked) " 14-Jan-2006(prissi): FIX: building constants now configurable in simuconf.tab (see there) FIX: new player frame (mostly internal change) 13-Jan-2006(prissi): FIX: way is kept when building/removing bridges ADD: tooltip "Alters a schedule.", "loaded" for convoiinfo, further rearrangement CHANGE: goods in convois and stops are sorted by the same rules => same sorting options CHANGE: convoi freight info and stop freight info only updated when needed => saving lots of time ADD: translations "follow me", "Follow the convoi on the map", "Sort by" 11-Jan-2006(prissi): CHANGE: convoi_info new text "%i km/h (max. %ikm/h)" FIX: (un)loading in harbours CHANGE: following convois via convoi info FIX: crash building tracks under pillared roadbridge FIX: again schedules with only one stop possible 10-Jan-2006(prissi): CHANGE: following convois via convoi info FIX: crash building tracks under pillared roadbridge CHANGE: better error check before building harbours CHANGE: definition of wege FIX: first all down will flatten a tile (more intuitive), using a slope on identical couble slope will make it steeper 9-Jan-2006(prissi): CHANGE: convoi list: minus red, further polish of halt_info CHANGE: halt_info: obsolete strings "Akzeptiert" FIX: scroll_pane now clips correctly CHANGE: speed bonus now in percentage instead of absolute speed => higher bonus for earlier fast stuff, lower for new stuff (may require new balancing :() (but effect not too much, only very fast things get a little less) the formula is now speed_bonus_faktor = 100*speed/mean_speed-100 (i.e. the speed difference in percent) => goods_price*(1000+speed_bonus_faktor*bonus_faktor(10%=10))/1000; CHANGE: new good list, obsolete translations: "Preis", "Bonus", "Category", "Speed boni for road %i km/h, for rail %i km/h, for ships %i km/h, and for air %i km/h." CHANGE: new texts: "Speedbonus %i%%:\n road %i km/h, rail %i km/h, ships %i km/h, planes %i km/h.", "gl_txt_filter", "gl_speed_up", "gl_speed_down", "gl_btn_unsort", "gl_btn_sort_name", "gl_btn_sort_revenue" FIX: powerline construction now cost again money ... FIX: wrong tunnel slopes solved FIX: building on slopes not destroyed during reloading Release of 88.02.1 8-Jan-2006(prissi): FIX: error with slopes mostly fixed => you can even built a channel using slopes FIX: monorails now display again correctly FIX: no rails at tunnel start FIX: station alignment on bridge start fixed FIX: no automatic bridges and tunnels any more => no crashes for public road and other occasions Release of 88.02 7-Jan-2006(prissi): FIX: aircraft did not accelerate after reaching flight level CHANGE: "cooperate identity" exactly same dimensions for all list buttons started CHANGE: halt info, new text "Passengers %d %c, %d %c, %d no route\n\nCapacity: %i\n" instead "Passengers today:\n %d %c, %d %c, %d no route\n\n%s\n" CHANGE: elements of halt list rearranged => more compact design CHANGE: adjustable costs for many things, level for station buildings ... (see simuconf.tab) FIX: double character with GDI and numlock 1-Jan-2006(prissi): FIX: changed all income calculation to 64 bit => hopefully no overruns any more FIX: wayfinder check now for artificial slopes and starting tile CHANGE: cost of land changes more balanced, tunnels more expensive CHANGE: nicer indicator in lower line FIX: 8 bit SDL works again fine 31-Dec-2005(prissi): CHANGE: makeobj recognizes now all waytypes in all relevant objects FIX: way finder now takes care of powerlines when building bridges FIX: now slopes are correctly checked before built => more slopes possible FIX: selling airplanes do not delete a depot 29-Dec-2005 (prissi): FIX: deleting order change => no crashes when quitting a game with vehicles in depot that are assigned to a line CHANGE: ways now take care of their images themselves also for crossings CHANGE: new slope calculation working (needs new graphics) 28-Dec-2005 (prissi): CHANGE: way image is now calculated by the way, not by the ground => 4 Byte less per tile CHANGE: correct calculation of offset for double height slope building foundations 27-Dec-2005 (prissi): CHANGE: more flexible bridge search in wegebauer CHANGE: using existing bridges/tunnels when calculating new ways ADD: estimates cost for construction before building ADD: show difference to start location when building ways CHANGE: more prominent waiting display in the schedule window Release of 88.01.3 26-Dec-2005(prissi): FIX: cursor keys for SDL and Allegro FIX: mouse pointer for Allegro FIX: symbol alignment fixed for message options FIX: missed airplanes again included into 64 pak Release of 88.01.2 25-Dec-2005: two tiles drawing code operational; but, the effect is rather small and only seen in very hilly maps 22-Dec-2005: FIX(prissi): change factory precision to allow high-volume production FIX: redrawing title bar under not Allegro 21-Dec-2005 ADD(tron): new makefiles (all options now in config.default, copy config.template and edit this) ADD(prissi): citycars with destinations (not recommended, just to many which want to reach places without roads) FIX(prissi): Allegro now with mouse in menu bar 18-Dec-2005(prissi): ADD: colors for channels in minimap FIX: factory now consumes goods according to their setting (before: the number of products) If a factory produces less, it will consume less. FIX: 's' builds road again 13-Dec-2005(prissi): FIX: when deleting a line, the schedule from convois in depot will be deleted FIX: dinglist could cause a crash when asking for a single deleted object (i.e. on a road) ADD: factory added to stop names (when built close to it) 12-Dec-2005(prissi): FIX: powerlines again buildable FIX: route calculation of aircraft improved CHANGE: wait for loading percentage now several numbers with wrap around ADD: waypoints for planes ADD: aircraft circle during waiting for free stop Release of 88.01.1 10-Dec-2005(prissi): ADD: citylist with total inhabitants ADD: new switch in simuconf.tab "just_in_time" (default=1) which allows switching off just in time logistics CHANGE: new factory construction: try to connect always to an existing factory, and ignore the distance, if the connection factory is not an elemental source FIX: redraw error, when AI changed land height hopefully fixed CHANGE: map display updated also with several options on 6-Dec-2005(prissi): FIX: takeoff/landing direction FIX: overflow on large maps and long transport FIX: waybuilder now forbid curves on runway nearly 100% (only be deletion still possible) ADD: nl key file by stormoog FIX: no queuing over airstops for flying planes 3-Dec-2005(prissi): CHANGE: A* for ways (finally!) FIX: drive on left zoom out bug in 128 solved (this one was well hidden) FIX: key handling in SDL renovated FIX: more error checks when deleting signals to recover from orphan signals from before ... Release of 88.01 2-Dec-2005: FIX(prissi): fiddled a little around with the signal deletion routine. No crashes, after a some tries FIX(prissi): monorail again deletable FIX(prissi): identical blocks on different tracks when using undo fixed ADD(mip): Marko keys (set them with CNTRL+F2...F14) and recall them with simple F2...F14 FIX(prissi): tool bars again in more rows FIX(prissi): cleaned up sources, took in Allegro support (wow, is the fastest so far!) FIX(tron): nice makefiles 30-Nov-2005 (prissi): FIX: air depot/terminal alignment FIX: only straight runways FIX: power large than 65536 and gear larger than 255 are now handled correctly FIX: slower acceleration on runways FIX: when ground was changed, airplanes were stuck FIX: "nicer" calculation of speed during different flight positions FIX: made planes compatible with half height version FIX: error when zoomed out with different base tile sizes FIX: A* route calculation was actually wrong and very badly implemented FIX: AI even more biased towards networks FIX: fiddled a little around with the signal deletion routine. No crashes, after a some tries FIX: monorail again deletable FIX: identical blocks on different tracks when using undo fixed 28-Nov-2005: FIX(mip): curiosity list used strtok, which wrote into the read-only strings from translation! (also customize display: "%s (%i,%i) - (pax %i, post %i)" for translation) FIX(mip): FIX(hsiegeln): divers error messages around schedules and station buildings Release of 88.00 22-Nov-2005: FIX (hsiegeln): do not count vehicles in depot for line capacity FIX (hsiegeln): display in convoi and schedule list FIX (prissi): vehicles in depot are shown, even if obsolete (but apparently worked before too) FIX (prissi): speedboni are always initialized with default values CHANGE (prissi): zero number if goods waiting are omitted in haltlist FIX (prissi): vehicle going in wrong depots CHANGE (prissi): haltlist now also with CNTRL functionality (CNTRL+click = only jump to position) 20-Nov-2005 (prissi): FIX: no income generated at waypoints ADD: free chose of stops for cars after certain signposts (new sign flag "free_route=1") ADD: new entry for halt filter (type "hlf_chk_airport") ADD: indicator for city attraction in the attraction list FIX: cursor alignment for up/down fixed FIX: minimum income for any good is 1/8 of the normal income, even if speed bonus is negative 17-Nov-05 (hsiegeln/prissi): ADD: airplanes ADD: stations need support building for certain types. Now there are freight stations that needs a waiting hall to accept passengers. for the obj=building the type="" has been extended by the following types. station - Rail stop (2 dir) busstop - Road stop (2 dir) carstop - Road stop (4 dir) habour - in the sea (4 dir) wharf - channel stop (2 dir) airport - plane stop (4 dir) These can have additional flags strings: extension_building=1 - extents like the station buildings (1 dir) (cannot build on a way) enables_pax=1 - passengers can travel from here enables_post=1 - mail can start from here enables_ware=1 - good are accepted here Without these flags, a stop will not accept anything! A freight station must posses "type=station enables_ware=1" or it won't accept freight. For generic, not stop specific buildings, there are three entries left: hall - waiting hall (1 dir) post - post office (1 dir) shed - goods shed (1 dir) The halt list and halt dialogue are extended by a small symbol indicating what this station accepts. Release of 87.02 13-Nov-2005 (prissi) FIX: after reloading, check stations for post offices (was broken before) FIX: search for next station failed too often FIX: error message when AI failed building a route does not appear any more FIX: crossing channels with electric wires again ok FIX: going to end of station also on bridges FIX: avoid deleting parts of a bridge FIX: default filter state for convoi list is off CHANGE: A* for faster pathfinding and preference of faster ways for faster vehicles FIX: correct booking of roadsigns FIX: again sorted as intended by category amount transported engine type, speed, power, ... FIX: some further finetuning of cost, speeds etc. FIX: some goods were wrongly joined, if there were two goods for the same factory at the same final stop ADD: click on vehicle in depot will open depot window (should be like that from the beginning ... ) CHANGE: factory names for 64 changed again for compatibility with very old savegames (but 87.xx might not load!) CHANGE: renovated japan pak for timeline and consistent building names, many new trams ... Release of 87.01 4-Nov-2005 (prissi) FIX: loading old maps with obsolete docks FIX: no route next to attraction should now have a more meaningful number (so you can better guess how much you have to transport) FIX: mail from attractions and factories again 4x received mail CHANGE: some further small optimizations for faster drawing of dialogues CHANGE: vehicles now sorted in depot by intro year and engine type 3-Nov-2005 (mip) ADD: curiosity list 3-Nov-2005 (prissi) ADD: channels as waterways, habour as "...ChannelStop" FIX: mail flag correctly recalculated after changing a station FIX: deleting tunnels 30-Oct-2005 (prissi) FIX: builds again presignals FIX: under some circumstances, building on ways (e.g. all stations) were not correctly removed FIX: several data of pak64 vehicles finetuned, mail carriage added, busstop before 1900 added ADD: Fast forward (keycode W) ADD: indicator for fast forward and timeline (translatable "timeline" "no timeline") FIX: calculation of speed bonus changed => bonus not per car, but per passenger FIX: timeline was sometimes some years off; especially after loading old games Release of 87.00 27-Oct-2005 (prissi) CHANGE: train outside depot on same square are displayed first CHANGE: no goods are sent to overflowing industries CHANGE: halt status color also red, when goods are overflowing station CHANGE: accepts freight at all stops near factories (including bus stops) CHANGE: road, rail and ship bonus now depend on average speed, if timeline is on FIX: some errors loading older savegames fixed FIX: on animated tiles missing phases will return the tile at phase=0 (i.e. baumarkt) FIX: alignment error of up-/down slopepointer FIX: could remove a non-empty depot ... FIX: industry chain creation could crash without cities FIX: schedule is corrected for invalid entries after input ADD: full use of copyright entries for houses and vehicles 24-Oct-2005 (prissi) FIX: roadsign cost correctly display (and not free anymore) and now also on bridges FIX: return ticket finally working (was too easy to do it right?!?) CHANGE: close topmost window by ESC or DEL FIX: not replacing city roads below depots (fixed sending home error) FIX: removing overhead wires make now removes electrified status again CHANGE: Using Mersenne Twister algorithm for faster and better random numbers FIX: when renovating, check, if I can renovate something; if highest level reached: do nothing CHANGE: full building timeline for 64 pak with many new building (=> can play from 1900) CHANGE: era and copyright message for buildings ADD: many new trams, alignment corrected for existing ones ADD: new player1 tool: jump a year forward CHANGE: haltlist now member of planquadrat ADD: stations on bridges CHANGE: also for stops, station, schedule entries, roadsign signals: CNTRL is from up, normal from down Release of 86.10.5 16-Oct-2005 (prissi) FIX: time now corrected after loading (no jumps) CHANGE: city takes over city road ownership CHANGE: growth now size dependent (max 1.67 for <1000, max. 3 for <1000, max. 6 for other) FIX: good distribution always, when more than 10 produced: non-distributing factories should be gone FIX: passenger and mail coverage display fixed FIX: connection from factories and town were asymmetric and only passengers went to the last factory in the list CHANGE: train can drive now on any track (also on public owned roadcrossings ...) 15-Oct-2005 (prissi) FIX: deleting upper and lower objects tweaked a little FIX: also now deleting of tram rail on a road works out ADD: public stops for player 1 can be now constructed everywhere. New tiles of this stop will be also public. 14-Oct-2005 (prissi) FIX: timeline can be also altered afterwards on non-locked maps FIX: crash when lowering land at the lower right border CHANGE: selection of next building now via "chance=" (default 100) CHANGE: timeline enforced for ways, bridges, and buildings incl. stations and attractions (new makeobj needed, support now fully retire_year/retire_month and intro_year/intro_month) Release of 86.10.4 13-Oct-2005 (prissi) FIX: no power generation in foreign languages fixed FIX: after assigning the same line twice, convoi did not move anymore FIX: may be fixed problem in industry chain building FIX: current entry not changed when appending to a schedule FIX: vehicle will only load people up to the next time it reaches a stop CHANGE: CNTRL+roadbuilder will replace also fast by slower road FIX: first use of 's' for roadbuilding -> crashed program FIX: setting new schedule could crash on fast computers or long lines due to recursive calling ADD: two new settings to simuconf.tab "only_single_info" and "window_buttons_right" CHANGE: configure, that always a single window opens up CHANGE: for you arabic and hebrew writers and all brainwashed by gates: close button configurable to the right (goes best with Win98 skin ... ) ADD: timeline, starting year and cheating mode can be set in the starting menu (and will be fixed further one, allowing for competitions) ADD: new edit tool to protect a map from further change CHANGE: new spanish, portuguese, czech, and dutch translation for 64 set CHANGE: further reordering of deletion order ... FIX: if factories were connected via station areas, goods disappeared ... Release of 86.10.3 09-Oct-2005 (prissi) FIX: size of station coverage wrongly calculated FIX: order of object deletion a little adjusted CHANGE: can delete pedestrians (sound really like hitman on the nearby roof :() ADD: CNTRL+remove will remove ground objects first FIX: detail button in factory info was wrongly aligned FIX: error with construction site lookup some FIX: deleting signals should now work better than before Release of 86.10.2 5-Oct-2005 (prissi) FIX: fundament slope drawing errors corrected FIX: showing attraction in the minimap removed all attraction from the internal vector => crash FIX: crash when deleting the last stop of a schedule (revolved to increase the maxi-counter in fahrplan_t) => savegameversion 86.10 CHANGE: removed halt_list from factory, changed fpl to minivec_tpl (now max. 255 stops in a schedule) CHANGE: route of goods was calculated twice when delivering to a station FIX: stealing from competitor works now (and also more intelligent goods distribution from factories) ADD: status now also shown in factory image FIX: more than two near stops (error in inset template) FIX: correct translation (and maybe also player issues) solved with line management CHANGE: 't' and 's' remembers last used track/road type for construction FIX: update errors in depot 4-Oct-2005 (mip) ADD: factorylist (new entries in translation: fl_title, Fabrikname, Input, Output, Rating) Release of 86.10.1 3-Oct-2005 (prissi) FIX: city check now for all non-removable objects before expansion FIX: check with valgrind -> no errors left (apart from SDL and lost memory) ADD: preliminary monorail-support (vehicle needs still better alignment) ADD: waypoints on bridges (and other preparations for station on bridges) Release of 86.10 2-Oct-2005 (prissi) FIX: removed some out of bounds arry_tpl's access. FIX: error with deleting the last objects from a tile (errors in dingliste_t) FIX: traffic light graphics were wrongly ordered FIX: no longer burying ship depots under sea mountains CHANGE: ausflugsziele now weighted array (weighted_vector_tpl) -> faster selection of passengers CHANGE: cities now weighted array (weighted_vector_tpl) -> faster selection of passengers, more travellers to bigger cities CHANGE: intercity travellers will now also go back (=> more traffic from larger cities) CHANGE: goods will be now distributed to the stop which is a) not overflowing and b) has less in storage (so you can "steal" from another company) CHANGE: travellers destination cities are now weighted by their size FIX: minimap redrawing when changing grid ('#') fixed FIX: numbers in depot correctly updated FIX: no more crashes with pillars FIX: also when building stop after a schedule is set, this schedule will be recognized Release of 86.09.3 24-Sep-2005 (prissi) FIX: also loading games with too many objects on one tile (warning will be given) FIX: working again with factories with more than 30 destinations and 10 suppliers FIX: artificial slopes now nearly always ok (one case is not solvable) FIX: reloading streets with paveway are again 50km/h FIX: sending back pax was not trying original destination => too many no route FIX: forgotten comment: citycars deceased after 100 hops ... ADD: traffic lights Release of 86.09.2 23-Sep-2005 (prissi) FIX: citycar stopped also when passing a car FIX: correcting rail blocks when loading FIX: destroying building cost again money FIX: try to fix memory leak with powerlines FIX: not post for non-accepting stops (check for correct routing please) FIX: screenshot for windows again ADD: show accepted good types also in station details FIX: pillar show bridge info FIX: several alignment errors with yellow steel bridge ADD: brick rail viaduct ADD: length limit for certain bridges (max_lenght=) Release of 86.09.1 16-Sep-2005 (prissi) FIX: bridges now cost again money FIX: citycars and pedestrians behave now as intended FIX: citycars delete themselves at the end of their lifetime CHANGE: non-square buildings are allowed also without rotation FIX: click in town tool now on correct line FIX: overlarge windows now moved to left corner FIX: again water industries (was built in wrong order [pri=1]) ADD: resizeable depots ADD: preliminary support for pillars in bridges: "pillar_distance=1...x" (distance [0=default off]), backpillar[s]=..., backpillar[w]=.... Release of 86.09 13-Sep-2005 (prissi) ADD: Additional tool "natural slope" CHANGE: changing terrain becomes much more expensive (so use bridges and tunnels more often) FIX: some more drawing errors with slope fixed CHANGE: more equal distribution of tools in a toolbar CHANGE: citycars will no turn into a dead end (check 2 tiles ahead) FIX: again pedestrians (was broken since 86.07) CHANGE: new setting in simuconf.tab: "drive_left" will offset all citycars, trucks, roadsigns, ... to the other side of the road (there will be still some clipping errors) CHANGE: artificial slopes now includes the natural ones ADD: Isaac new roadsigns ADD: cityroad configurable "city_road_type" in simuconf.tab FIX: cars now also wait on diagonals CHANGE: new vehicle-sorter, thus trams look a little better and it know about left side driving 11-Sep-2005 (prissi) FIX: cursor keys with SDL working again FIX: remove obsolete keywords from translations, also small font will be ignored FIX: en.tab and de.tab for finances/map legend ADD: Some buildings from Moistboy and de/en translation texts ADD: road inside city limits will get a sidewalk after some time ADD: "Total inhabitants:" to citylist which shows the number of total inhabitants CHANGE: made dinglist to maximum size 255, and single objects are stored directly => much faster loading, saved another 2 bytes per tile and 32 byte per empty tile/one object tile CHANGE: removed fundament type from ding-types => 56 bytes less per building FIX: Some more error checking, when loading ways => can cope with unknown ways Release of 86.08.1 10-Sep-2005 (prissi) FIX: wrong engine types for obsolete rail type "electrified_track" in old packages FIX: SHIFT again dead key with SDL FIX: after removing stops from a schedule, the removed stop was not updates => passengers waiting for non-existent lines CHANGE: Allowing for 32 workers destinations (More would be easy, by may slowdown passenger generation a little.) CHANGE: haltlist to minivec_tpl => saved about 2 bytes per tile FIX: error check during scroll bar creation CHANGE: tried yet another set of routines for GDI which seems to work on all machines I could test CHANGE: newly arranged finance window, obsolete string now "Construction:", "New Vehicles:","Vehicle Running Costs:","Revenue:","Total:","Maintenance:","Balance:" Release of 86.08 3-Sep-2005 (prissi) FIX: convoi in depot which were assigned a line or a schedule started after reloading. CHANGE: Separated map and legend. Got rid of small font FIX: Closing button should check, if closing is ok. ADD: built straight ways by pressing control during mouseclick (just using a dumb minimizing distance algorithm) FIX: factory production calculation could overflow under bad circumstances. Release of 86.07.4 2-Sep-2005 (prissi) CHANGE: new map button "factories", no more clutter in the legend FIX: divers "delete" to "delete [] ", which are different operators. valgrind now only complains about SDL stuff. Release of 86.07.3 30-Jul-2005 (prissi) FIX: cursor alignment with 128 FIX: no money making by open schedule window during loading in a station FIX: message list now displays all messages again FIX: divers errors around the GDI display routines and further speed up + sound and midi FIX: cursor offset finally corrected for all display widths Release of 86.07.2 25-Jul-2005 (prissi) FIX: again correct train types for newer paks FIX: error during loading was using evt. uninitialised minimap CHANGE: order in railtools and tramstools roughly similar CHANGE: background for tunnel graphics (was returning always foreground) CHANGE: AI will always keep cityroads FIX: roads under harbour deleted FIX: AI now uses better calculation of income (before always the farthest factory won) CHANGE: zeiger now drawn on to of everything CHANGE: allow to crossconnect all factories (via crossconnect_factories=1 in conf/simuconf.tab) FIX: factories initiated more than one smoker instance during loading (could break savegames) FIX: cars and trains now really vanish in tunnels (more or less) CHANGE: wrote extremely simple direct copy in Windows-GDI. Seems 2x faster than SDL and the debugger works a little, yeah!!! Release of 86.07.1 15-Jul-2005 (prissi) FIX: fundament as subtype of grund was a bad idea: no more objects were detected! FIX: no new month after starting a new game! FIX: error in routings could led to a crash of the sort routines displaying the station info! CHANGE: extensive correction no done during loading of savegames. If you are lucky you may be able to load an 128 savegame (or a german) with the 64 and vice versa. CHANGE: weg_t are now the only waytypes (vehikel_besch_t::strasse no longer valid) CHANGE: made way for lower hills (actually there is a TTD-like graphic set under way ... ) Release of 86.07 13-Jul-2005 (prissi) Internal cleaning up of some data structures => more run-time memory, preparation for other machines/64 Bit Repaired/Changed some code to make it compatible with DevCpp Changed the list handling code: There was a lot of unneeded memory use (class instead struct) and some overhead. Also more friendly to different compilers. Changed the mempool code: Reuse the blocks also used for the list handling code (will be always size of 4, 8, 12, ... bytes up) CHANGE: removing now first deletes citycars, then roadsigns/signals, otherwise like before FIX: key help dialog is now a magic one (only open once) FIX: limit maximum speed even hill down to maximum_speed+20 (higher speed would likely cause a derailment in reality) FIX: now the absolute year is saved (avoid cheats) With saving and reloading every 500 years or so this should allow unlimited long games. CHANGE: Non-quadratic maps (my favorite is always 512x256) CHANGE: Unmagic cityinfo (so you can open more than one) and city info does not forget its settings anymore CHANGE: try to built a little more straighter road/tracks also for diagonals (not 100% working yet) ADD: presignals now possesses a third stage, i.e. next block free, but next one still not free FIX: Paranoia check during loading can partly repair broken savegames during loading/saving CHANGE: vehicle values drops now 50% in 20 years and changes monthly. CHANGE: passengers and goods are first loaded for the nearest stations the convoi goes. This allows to built local and station skipping highspeed lines. ADD: after three month not moving, citycars will turn around. If still stuck, they will report "To heavy traffic\nresults in traffic jam.\n" as a problem CHANGE: citycars will wait for next free hop in a step() (saving lots of calculation time) CHANGE: autosave value gives now interval in month (0=off) FIX: no more redraw errors with slopes behind buildings CHANGE: sorting by number of passenger for the next destination (i.e. connecting to xyz) 64 Graphics: ADD: new station building and new harbours from timeshock/converted 128px graphics Release of 86.06 6-Jul-2005 (prissi) FIX: calculation of slopes after lowering landscapes now correct FIX: connection with a near stop could fail for non-symmetrical station buildings FIX: full-window mode works more reliable FIX: crash, when the land on a route was removed FIX: From a stop under a bridge a vehicle can directly hop onto the bridge (error in koord3d comparison, might be also affected other places!) FIX: railwindow and editwindow excluded each other ADD: normal cars now also do not run over each other but wait like trucks etc. ADD: roadsigns for no entry and minimum speed ADD: name of current player in the bottom bar ADD: many new help texts from Gotthardlok Release of 86.05.1 23-Jun-2005 (prissi) FIX: now correct waytype when building tunnels CHANGE: mail now passenger tab in depots, wagons without payload (usually tender) also into engine tab FIX: delete of multi-tile buildings improved ADD: deleting industries works, if no station is connected ADD: deleting towns by deleting townhalls (the other houses are not affected by this; but only houses within city limits can generate passengers) FIX: avoid dropping of frame time below 10ms FIX: convoi info windows not correctly checked FIX: error message when raising/lowering tiles now correct (came never/too much) ADD: monthly autosave (option "autosave = 1" in simuconf.tab) as file autosaveXX.sve (XX=01..12) FIX: message repeat now changed a little: Now route/overcrowded messages will be repeated after one month 21-Jun-2005 (prissi) FIX: fullscreen mode from simuconf working. FIX: redraw error when raising water. ADD: 'P' to change player (player 0 is construction AI). ADD: Edit menu for player 0 (grow city, shrink city, build cityroad, build attraction) CHANGE: build industry now into edit menu Release of 86.05 17-Jun-2005 (prissi) FIX: no resize when dialog open FIX: AI busses waiting at tourist attractions were not added to routing tables (and updating tables now asynchronous process). FIX: passenger AI could cause random crashes. Also test for indirect connections was no working. Now only new route built, if more than three stations bypassed. CHANGE: rearranged icon in the slope tool CHANGE: 4 new slope graphs => much less drawing errors (backward compatible) CHANGE: forbid use of slopes on potentially dangerous tiles (message: "Tile not empty.") ADD: support for headquarters (in dat: obj=building, type=hq, parameters are level= 1,2,3,4,... and passengers=xyz) (Press 'H' to built) CHANGE: No stop at waypoints on straight tracks FIX: fullscreen-switch repaired Release of 86.04 11-Jun-2005 (prissi) FIX: Jumping trains with no route/wrong depot fixed. FIX: rotation for townhalls. CHANGE: add schedule will be reset after closing a window. ADD: Window resize of main window. ADD: Input conversion for non-latin languages. CHRxx (xx is hex between 80 and FF) will be translated from tab-file. Missing entry will give default. FIX: check when building docks Release of 86.03.4 03-Jun-2005 (prissi) FIX: crash during return to depot. FIX: problems at signals/depot exit to 75% solved. CHANGE: now a change for connecting even to distance industries. FIX: error with calculation of left over productivity of a factory. FIX: slower aging of messages. Release of 86.03.3 01-Jun-2005 (prissi) FIX: long routes also found on large maps by increasing the number of steps of the variable "max_route_steps" in simuconf.tab (default=100000, useful maximum size^2 [16 Byte/entry]) FIX: crash when town growth requires new industry, but nothing found. FIX: to fast disappearing of income messages. FIX: bug during entering a schedule when open multiple windows FIX: raise height until level 14. FIX: wrong season when loaded. Release of 86.03.2 25-May-2005 (prissi) CHANGE: station_coverage_size now saved with a savegame. For old map, default is 3. FIX: opening the minimap on faster systems yield FPS>1000. hopefully fixed (+ default refresh=2) FIX: retire messages now appear FIX: outdated or future vehicles now display with a blue bar instead crashing in a depot FIX: finetuning number of trucks for AI ADD: parameter "-fullscreen" for command line resp. "fullscreen = 0/1" in simuconf.tab Release of 86.03.1 23-May-2005 (prissi) FIX: overflow in vehicle array position (was not extended) CHANGE: made the drawing a little more fluid: everything will move smoother than before (just forced redraws before idle wait) With -refresh 2 even the 128 can be played very fast. FIX: Pause could cause a hangup/division by zero. FIX: Hopefully no longer factories on slopes or roads Release of 86.03 18-May-2005 (prissi) ADD: "citycar_life" to simuconf.tab (How many tiles can a simutrans car go, before it forever breaks ...) Answer about 2500 per year (depends on speed), default 35000 ADD: citycars now obey timeline (if switched on) ADD: sorting of road and bridges by their speed FIX: hopefully fixed some bugs relating to passengers at attractions FIX: some bug relation to AI and vehicles with trailer fixed? ADD: building post offices on slopes ADD: station buildings possible (name must end with "...StationBlg") Will also extend the storage by 128. ADD: allow larger maps (but will need lots of memory) and longer trains (up to 24 cars). ADD: display of station capacity ("Storage capacity") FIX: not starting up when japanese was selected CHANGE: calculation of FPS change to a real time based system to avoid very high FPS with high accelerations (downside: at very high stretch times vehicle movement less smooth) New entries for citycars are: speed, intro_year, intor_month, retire_year, retire_month 15-May-2005 (prissi) CHANGE: Trams only in tram depot (tram only if "waytype=schiene_tram") CHANGE: obsolete vehicles can be hidden if the timeline is on CHANGE: no electric units shown, if depot did not have catenaries CHANGE: new tab for passenger vehicles (to show EMU heads, use freigth=Passagiere and payload=0, to add them here) Name is "Pas_tab" (trains), "Bus_tab" (cars), and "Ferry_tab" for ships. FIX: depot window can be now redraw by clicking the obsolete button (will fix display after electrification or rezoom) FIX: second row should be selectable after rezoom in depots FIX: several small things increasing compatibility to standard C++ (compiles on GCC 2.95) FIX: citycar_writer fixed FIX: click with tool windows now obey used/unused images Release of 86.02.2 11-May-2005 (prissi) CHANGE: produced good go now first to the factories, which have a storage, that is not overcrowded CHANGE: removed the step-wise route recalculation at halts into a monthwise one. FIX: error, when good is already at target stop hopefully finally fixed CHANGE: retiring date for city cars and engines CHANGE: production now really the value in the dat-files => resetting several 64pak values FIX: factory location searcher works again as intended FIX: some things which placing bus stops FIX: no longer send cars to tram depots and vice versa FIX: no production during pause any more FIX: no longer many transparent pixels when zooming Release of 86.02.1 05-May-2005 (prissi) FIX: error when creating goods and all destinations were empty: only first was served FIX: no longer people waiting for transport to own stop FIX: numbers of created with no route at station display corrected corrected FIX: station type was not calculated after loading => station list empty FIX: presignal crashed, when deleted tiles of a calculated route FIX: pedestrian graphics fixed Release of 86.02 01-May-2005 (prissi) FIX: error when loading UTF8 citylists fixed FIX: rebuild destinations, when convoi was removed by x => no more stale freight waiting FIX: when convoi self_detructs, its value is booked back to the owner FIX: when station already is already destination => just happy ADD: vitus new pedestrians to 64-pak CHANGE: oilfield to Ravens oilfield 24-Apr-2005 (prissi) FIX: no route found, when factory was to close FIX: crashes, if delivery with unknown route?!? 20-Apr-2005 (prissi) FIX: ware could not unload at a dock => internal connect calculation no with halt basis_pos FIX: again detailed information from public stops CHANGE: allow more than one row in the tools CHANGE: color indicator on simloops and FPS CHANGE: random pedestrian now switch off also in dialog (6LIGHT_CHOOSE), 7WORLD_CHOOSE removed (identical) CHANGE: year now also updated in city chart FIX: UNDO repaired FIX/CHANGE: factory production from dat-files is now per whole factory and independent of the load of simutrans FIX/CHANGE: distribution now when more than 10 produced (instead 32) FIX: makeobj always appended a cursor and a symbol, even if none defined. CHANGE: factory list to vector_tpl, halt_list of grund_t now on demand -> should save lots of memory Release of 86.01 19-Apr-2005 (prissi) CHANGE: passenger to attraction/factory is equal to returning number; mail is 1/8 of returning. This comes for free and reduces the load considerably. CHANGE: when a ware was added to a station, the route was calculated again in case of pax CHANGE: passenger generation/routing cleaned up and reworked => much faster FIX: now really closing all windows before loading CHANGE: Building do not step anymore, if not necessary => much faster CHANGE: simloop/FPS system complete reworked to allow more time for other applications resp. a smoother scheduling all other times. But now no time stretch <1.0 possible CHANGE: factories are now built every 2^x*industry_increase_every 14-Apr-2005 (prissi) FIX: no more adding cities after loading corrected; also now in principle an unlimited number of cities should be possible (but something still breaks at 64 ... ) FIX/CHANGE: traffic to an attraction is now proportional to the level of this attraction FIX: ships could only unload under the harbour ... CHANGE: random now c instead of C++ => a little faster CHANGE: line in fillbox a little higher FIX: new GB citylist FIX: CMOCxx cause pre PentiumPro to crash => exchanged 10-Apr-2005 (prissi) CHANGE: trees have now also an distribution weight (default=3) FIX: transportation revenue also calculated, when vehicle was stopped for new schedule FIX: dirty_rect for vehicles was to small => traces were left! Release of 86.00.3 9-Apr-2005 (prissi) FIX/CHANGE: datatype for city array_tpl now vector_tpl -> more access checks CHANGE: townhall_info added to simuconf.tab to avoid passenger level info window of townhalls CHANGE: calculation intense station type is now only recalculated after construction work FIX: AI busses waited also on overcrowded lines and always created only one bus ... FIX: player images were displayed two times using slow and fast routines ... urg! FIX: tourist and city building passenger amount per time now comparable CHANGE: hopefully removed all spaces from pak64 file names (will break old savegames) FIX: mip_cobbelstone maintenance corrected FIX: trams corrected FIX: added new pedestrians from Vitus Release of 86.00.2 8-Apr-2005 (prissi) CHANGE: new signals for 64 CHANGE: different signal graphics for electrified and normal tracks (if more than 8 images defined) CHANGE: passenger AI will removed stuck or unused busses FIX: passenger AI will no longer build lines to unconnected factories FIX: pax at tourist attractions were added to all stops!?! 7-Apr-2005 (prissi) FIX: several routines replacing ground also destroyed binding to halt FIX: tourist attractions now generate mail again FIX: unhappy mail was also omitted FIX: presignals were not correctly removed CHANGE: 64pak: two rail roofed station added 5-Apr-2005 (prissi) CHANGE: assembler for display_text (17% faster) and display_fillbox (150% faster) FIX: variation of non-rotatable houses was ignored for special buildings CHANGE: tourist attraction are no longer built next to each other, but at least 1+b+h away Changes to Makeobj20 (prissi) (for Simutrans 86.xx and higher. Factories created with this version will crash Simutrans 85.04.1 and earlier.) CHANGE: pax_level for factories added. FIX: 96 and any other even size should work again. FIX: citycars were broken. CHANGE: "schiene_tram" added as waytype to accommodate for Trams. 3-Apr-2005 (prissi) FIX: station window display FIX: station bar indicator also included good None (therefore the color were one off) FIX: map crashed, when there was not a place for every town 29-Mar-2005 (prissi) FIX: removing a tile was killing all station info => station no longer connected FIX/CHANGE: tooltips are now only allowed for topmost windows CHANGE: some more assembler, now 999ms instead of 1462ms for display_img() (45% faster) CHANGE: added busstops to tram menu FIX: Finally nailed down bug of stealing goods from other companies FIX: Only own stops could be added to a schedule (so no stops where nobody could unload/load) FIX: factories tried to connect to overconnected suppliers => ok, there is then an error message, that too many pointers were added, but simutrans exists anyway Release of 86.00.1 27-Mar-2005 (prissi) FIX: coded the display_img new, now 40% faster than before FIX: city statistics were wrongly initialized FIX: docks: water ground left, catchment area now checked + dock removal possible FIX: makeobj allows now also for sizes != 64 and 128 26-Mar-2005 (prissi) FIX: factory catchment area was asymmetric (2x3) => change also to 2x2 FIX: tourist attraction display now normalized to biggest attraction CHANGE: another round of drawing optimizations: no redraw of menus or ticker background CHANGE: vertical clipping display further optimized, zoom on demand now working CHANGE: new parameter in simuconf.tab "station_coverage" sets the coverage area FIX: wrong factory catches ware of the same typ hopefully corrected CHANGE: added the tram-stuff for 64 Release of 86.00 20-Mar-2005 (prissi) NEW: statistics for towns FIX: all ground tiles were no properly initialized during loading! (weg1 and 2 could have a random image!) CHANGE: all images are loaded and coded to their proper values. Darkening is only on demand. (huge memory relief) CHANGE: zooming calculation change => drastic speed-up and in principle allows also for zooming by all integer factors (1,2,3,4,5, ... ) CHANGE: Debug message actually taken out, if compiled without debug support => leaner code and speed up. CHANGE: TramTools disabled, if no tram depot found CHANGE: Lots of german help files added (from Gotthardlok) 17-Mar-2005 (prissi) NEW: Season for grounds ("WinterGras" and "WinterShore") NEW: Seasons for trees (either all year (0-4) or summer/winter (0-4|5-9) or all four seasons with 20 images (sum|aut|win|spr) NEW: new parameter in simuconf.tab: "tree_info=0" switches tree info off 14-Mar-2005 (prissi) FIX: convois have again correct weight after loading a game FIX: AI wrongly fills undo array FIX: corrected some loadgame issues 11-Mar-2005 (prissi) CHANGE: quickstone_t shrank to unsigned short int (saves memory) CHANGE: vector_tpl now with removal and insert_at at certain position CHANGE: grund_t now remembers the 10 next stations (this seems like maximum number to me) This allows way faster search for stations. Furthermore, passenger now are preferably routed to a station of the player/passenger AI. CHANGE: 'v' to display the station coverage CHANGE: station coverage setting saved in simworld.cfg 9-Mar-2005 (prissi) CHANGE: Townhall also opens its info-window FIX: StalkeXRP changed to his real name in credits FIX: Austrian and finnish citylist had still old names FIX: Citycars again in dat files (was omitted during makeobj link, because commented in simwrite.cc ???) CHANGE: Savegame format now 86.00, since this will be 86 with trams and more CHANGE: Median citizen is again median ... CHANGE: New founded city has always 1/10 of median citizens at the beginning 4-Mar-2005 (prissi) FIX: factory and transformer builder now checks their building sites before more strictly 1-Mar-2005 (prissi) CHANGE: menu clean up: Post -> special tools NEW: cursor for slop tool (cursor.Slope.pak) NEW: large docks, as many as you like NEW: as many stations as you like NEW: as many busstops as you like CHANGE: factories now have a passenger level Following cursor.*.pak had become obsolete: BusStop, Crossing, Rail, Road, TrainStop, FreightTrainStop, TrainDepot, CarDepot, Post, ShipStop please define an icon and a cursor in your files! 24-Feb-2005 (prissi) NEW: larger ports possible NEW: passenger AI new also built lines to factories and attractions CHANGE: allow for higher initial mountains FIX: median citizens was actually maximum citizens 23-Feb-2005 (hsiegeln): NEW: presignals; -> trains will not enter a block, if the block to be entered thereafter is not free and they are waiting at a "presignal" NEW: presignals require the following PAK: misc.preSignals.pak TODO: presignals use old-signals icons on menu and for cursor; needs to be fixed TODO: gfx for preSignals currently use gantry-style; should use correct gfx; whatever that may be ... 22-Feb-2005 (hsiegeln): NEW: Margin graph in finance window NEW: Transported Goods graph in finance window FIX: net wealth initialization was missing for month graphs FIX: own vehicles couldn't be managed after loading game FIX: potential crash in line management window FIX: button enable/disable in line management window Release Simutrans 85.04.1 21-Feb-2005: FIX: cleaned up many images, removed many pixels => 200kB smaller base set CHANGE: added heavy ore truck by MHz CHANGE: doubled power needed for acceleration, but every car now only consumers kW equiv. to its weight CHANGE: passenger level added to factory window FIX: correct calculation and passenger display for all monuments FIX: string overflow for new monuments corrected FIX: tooltip corrected 19-Feb-2005: FIX: removed unnecessary pixels from cathedral and stadium FIX: night lights for stadium FIX: initial display width now 704 instead 702 CHANGE: added citycar pickup FIX: ship power values reworked FIX: clipping error for char display FIX: large map creation fixed CHANGE: now random map creates a map out of 9999 choices (theo. max 273025) CHANGE: removed blanks in ticker FIX: passenger generation from attraction and factories corrected FIX: passenger generation again level dependent: factories ~ 20 FIX: no transformers on water FIX: production could jump for very high timer loads (i.e. during zooming) => more performance FIX: new factories are again connected to stations CHANGE: bus AI added (player 2) including overcrowded lines => new message "Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n" CHANGE: mip_cobblestone now 50 km/h, cobblestone 60 CHANGE: AI can build now much more roads FIX: maintenance of city road reduced to 3.00, because difference is charged to player FIX: error with way replacement in AI (same way replaced was charged) FIX: no income generating by overbuilding roads Released Simutrans 0.85.04.0 11-Feb-2005: FIX: simultaneous AI now working without crash CHANGE: AI 1 always road only, AI 6 rail only FIX: more WF cars now in players and night colors FIX Book truck night colors and trailer FIX: MHz shipyard now in player colors FIX: fish wagon now in players color FIX: Again windows icon and version info FIX: clipping calculation for characters was faulty FIX: at some places wrong memory allocation repaired FIX: some windows were two times destroyed, now kill_list checked for doubles FIX: Linux binaries again linked with V5 libstdc++ CHANGE: added USA citylist CHANGE: cleaned up simuconf.tab (Tomas) FIX: Several help window texts reworked Released Simutrans 0.85.03.3 10-Feb-2005: CHANGE: Any "... Power Plant" or "...kraftwerk" will produce power => Wind Power Station (and Solar Power Station) possible CHANGE: Power production now proportional to factory production FIX: transformer was built and then immediately removed FIX: use libstdc++ version 3 under Linux Released Simutrans 0.85.03.2 9-Feb-2005: FIX: reduced passenger number again to initially intended value This is about 1/4 of the current value. Thus I doubled the number per building. FIX: no undo after bridges or tunnels FIX: UNDO failed for spieler=NULL (intercity roads) FIX: corrected season timing FIX: building transformer removes everything on that tile CHANGE: building powerline removes all trees on that tile CHANGE: finances also of the AI players "Finances of %s" FIX: since in current version, weight is only calculated during loading now the real weight 1000kg=1t is used Released Simutrans 0.85.03.1 7-Feb-2005: CHANGE: added UNDO for road and rail buildings ('z') +"UNDO failed!" error FIX: simworld.cfg was wrongly initialized => no messages at all FIX: city AI was also ignoring transformers during building FIX: for calculating of possible passengers only these who found a station near the destination was counted. Now also the one, who gave up, because no station was there are counted => city growth rates much lower, need a good service over whole map FIX: Renovation of townhalls: now all with roof texture and "real" grass FIX: Power station production increased after loading: check on load time FIX: error of an character allocation array (V. Meyer) FIX: superfluous parameters in check_for_crossing() removed. (V. Meyer) Released Simutrans 0.85.03 6-Feb-2005: FIX/CHANGE: replace building search in map window by simple gib_halt() FIX: No crowded stations messages from AI anymore FIX: Depot reached message was not in mailbox FIX: mailbox now starts always empty. FIX: after selecting a list, the info tool is selected back CHANGE: message settings will be save in simworld.cfg 5-Feb-2005: FIX: way building did not check for transformers CHANGE: when producing power, powerstations consumption raises 3x (to prevent building too many transformers) FIX: removing transformer when under power now reduced production again FIX: no more suicide divers at the fish_swarm FIX: factor for production increase could lead to very high values for low productions in 128 set Released Simutrans 0.85.02.1 4-Feb-2005: FIX: list windows were not open correctly (fixed by Markus Weber) FIX: tooltip for main menu CHANGE: citylist for dutch cities by AlienDNA FIX: could not connect to a road built by the AI FIX: effect of power on production was faulty FIX: new tourist attractions and factories announcement were not translated Released Simutrans 0.85.02.0 2-Feb-2005: CHANGE: german help files CHANGE: two new command line options "-timeline" and "-startyear" CHANGE: main menu again little different order and new icons CHANGE: list window toll added (after Markus Weber) CHANGE: vehicle image is now the loaded images (please comment) FIX/CHANGE: city do not grow until there is some transportation going on CHANGE: minimum now 1 city CHANGE: possible to set minimum citizen number CHANGE: removed tool for road/rail crossings since this is done automatically 30-Jan-2005: CHANGE: factory productions doubles with electricity CHANGE: indicator for transformer added for power status FIX: disallow building of city-AI on powerlines FIX: overcrowded station message again demi-FIX: on the expense of a small memory leak powerline now loads and connects under all conditions CHANGE: added mailbox (open with 'B') CHANGE: AI could now given a now int tab-file "player 5"->"H-Trans GmbH" CHANGE: central message manager, also added the following messages: "New factory chain\nfor %s near\n%s built with\n%i factories." "%s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants." "With a big festival\n%s built\na new monument.\n%i citicens rejoiced." "To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers." "%s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i)." "%s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i)." "Year %i has started." 25-Jan-2005 CHANGE: two new button to map windows added: "Powerlines" and "Tourists" 24-Jan-2005 FIX: powerline income was based on last consumption, now integrating and reduced FIX: during loading the AI was not marked as AI => convois had remove buttons again FIX: roadbuilder did not checked, whether I owned the ground or not => crossing with AI possible => removing road => strange things happened Released Simutrans 0.85.01.1 23-Jan-2005: FIX: loading old savegames again Released Simutrans 0.85.01.0 20-Jan-2005: FIX: linux: running in windows (again?) FIX: factories were not connected with cities FIX: tourist attractions again FIX: bridges obey speed limit again after loading (must be broken since before 0.84.17) CHANGE: animated cinema now with night colors CHANGE: some industrial building renovated, night pixels removed CHANGE: city factories have a minimum distance of 6 CHANGE: removed "BRIDGETOOL", "5WORLD_CHOOSE" CHANGE: now separate settings for land industry, city industry, and tourism called "Land industries", "City industries", "Tourist attractions" CHANGE: added a second power plant "Oil Power Plant" 18-Jan-2005: FIX: Adding transformer now connects more reliable with powerline FIX: powerline ground has no owner (unlike ways), so one can built a powerline on other grounds, like a bridge FIX: Romanian helpfile included FIX: AI won't destroy your powerlines any more FIX : AI now uses a little less trucks to prevent traffic jams and shift calculation a little towards trains FIX: factories belong to the player => could be removed CHANGE: Every second powerline is now just line, powerline bridges CHANGE: tool added "Plant tree" (key '/' now without function) CHANGE: removed drivables (key 'n') since they crashed anyway CHANGE: removed add pump (key 'P'), since now add transformer does the right type automatically CHANGE: removed unnecessary bridge menu pak-file Released Simutrans 0.85.00.0 16-Jan-2005: FIX: powerline works again (please, built from source to target!) FIX: powerline on slopes added CHANGE: new factory distribution code, distribution entirely by the same function, which built new suppliers CHANGE: added new concrete factory 11-Jan-2005: FIX: AI built railway tracks on water and over houses, disabled FIX: Create new industry (shift+i) new creates a random consumer instead of "Materialswholesale" FIX: New industries are crossconnected FIX: Producer and processing are now placed in cities, if requested in the dat file FIX: Several nightpixel removed, some buildings renovated, skyscrapers now with streetlights FIX: AI had problems which rectangular factories, if they were rotated FIX: AI crashed, when a factory was too close to the map borders CHANGE: "clean up" of the main menu CHANGE: new menu for "half documented" features: SPECIALTOOLS, Found new city, Build land consumer, Build city market, Build powerline, Build drain, Marker CHANGE: MHz shipyard, special cursor, new depot icon CHANGE: City builds new industry after some time (see cityconf.tab), also crossconnected CHANGE: complete hungarian and romanian translation, preliminary russian one 9-Jan-2005: FIX: vehicle selection was wrongly calculated (very good vehicles were also excluded) FIX: in translation a second pak-specific entry could not overlaid a previous entry => now their can be scenario-specific station and town names CHANGE: new city checks also for unique name CHANGE: in translation files all lines starting with '#' are ignored CHANGE: factory are constructed with their storage nearly full => AI can start immediately, less double lines Released Simutrans 0.84.22.0 3-Jan-2005: FIX: Route finding in station was faulty (ignored dead ends and electrification) FIX: Station did not grow properly together FIX: citylist is now loaded with the correct language FIX: clipping of small font in map window works again CHANGE: Many buildings nightpixel removed/exchanged CHANGE: Renovation of the KI - tries to built networks (factories missing a single good for production a rated higher) - checks more factories - always use the cheapest means of transport (only a little biased towards rails ;)) - all possible locations are now searched, including points near a previous built station - tries to built a single station - obeys timeline for vehicles - can electrify tracks CHANGE: Renovation of way/rail construction tool - automatically creates railroad crossings if possible - tunnels will always have the fastest way (not name dependent any more) CHANGE: Cities in a new map can now have a certain distance (see config/cityconf.tab) CHANGE: Japanese pak "Oil Power Plant" now have a powerline connection CHANGE: Full Unicode display support - renovation of all text displaying routines and therefore many dialogue boxes - support of BDF as an alternative font format (less than 256 chars of Unicode/ISO10626-1 coding only) - UTF8 encoded (2 Byte language) files are possible - 2 Byte output/editing works (no input due to SDL yet) - citylist can be either in text/ or pak/text/ (or where your pak-files are). Released Simutrans 0.84.20.0 28-Nov-04: FIX: fixed a crash if a vehicle was sold while the vehicle info window was open FIX: cities can now skip gaps in building levels (gaps up to 16 levels) -> this allows players to remove city building PAK files without unlinking higher level buildings from the chain unless they create gaps of 16 levels or more CHANGE: included MHz and Prissi in intro scroller and thanks.txt CHANGE: replaced animated fountain by better version created by MHz CHANGE: included reworked images by Prissi: - a0-townh-stage1.png: fixed roof edge - g0-houses01.png: grass and night color fix for one house - g0-industry.png * improved optics factory * fixed water cistern night colors * new industrial city building (fertilizer factory) * improved grid structure for cylindrical grey gas tank - ls-bridges*.png: fixed night colors - ls-shores.png: added one missing pixel 27-Nov-04: FIX: gear is now calculated correctly for all vehicles of a convoi (Prissi) FIX: station statistics are now shown correctly even if less than 16 goods are used in the scenario (Prissi) 26-Nov-04: CHANGE: included MHz new ship depot CHANGE: included updated czech translation by Ondrej Machulda Released Simutrans 0.84.19.0 ??-Nov-04: CHANGE: included Hendriks changes Released Simutrans 0.84.18.0 13-Nov-04: CHANGE: traffic density setting now ranges from 0 .. 16 CHANGE: replaced "old church" tourist attraction by better image from MHz FIX: traffic density 0 now creates no traffic at all FIX: tried to fix "unhappy passengers" counter (there was a fixed amount used instead of the actual number of passengers). Integrated Hendriks bug fixes: FIX: Sequence of "remove" and "update" in line management caused a crash FIX: finance window: text fixed FIX: finance window: debt warning is visible again FIX: line schedule updates now also affect convois waiting for freight FIX: depot windows sometimes displayed wrong vehicle information 10-Nov-04: FIX: fixed a bug that caused factories to stop producing goods if one of the outgoing storages became more than full (probably caused by another bug) Released Simutrans 0.84.17.0 06-Nov-04: Integrated Markus and Hendriks changes: FIX: "no connection" station list filter fixed (Markus Weber) FIX: rail stations without roof now have the type "rail station", too (Markus Weber) NEW: mouse wheel support (Hendrik Siegeln) FIX: combobox drop down list now can be closed by return (Hendrik Siegeln) FIX: fixed a memory leak in schedule list ui (Hendrik Siegeln) FIX: fixed a problem with line/schedule updates (Hendrik Siegeln) 05-Nov-04: CHANGE: replaced old rail depot by MHz' new rail depot CHANGE: replaced one more industrial building by a better version created by MHz 04-Nov-04: CHANGE: replaced three industrial buildings by better versions created by MHz CHANGE: replaced one residential building by a better version created by MHz CHANGE: improved two residential building images CHANGE: included updated hungarian translation by Ferencz Szekely CHANGE: included updated polish translation by Maciek Gajewski Released Simutrans 0.84.16.4 01-Nov-04: FIX: fixed some accesses to uninitialized values in relief map frame. -> A crash has been reported upon pressing 'm' but I couldn't reproduce it. I hope this was the reason and my changes will fix it. At least my memory debugger is happy now. Since I couldn't reproduce the crash I can't tell if it is fixed or not! Released Simutrans 0.84.16.3 31-Oct-04: FIX: depot maintenance cost is now calculated correctly after loading a saved game CHANGE: savegames are now sorted by date, newest on top of list 30-Oct-04: FIX: placing a marker and confirming by pressing OK no longer causes a crash -> this required quite a lot of changes to event handling, I hope I didn't break some other functionality Released Simutrans 0.84.16.2 25-Sep-04: NEW: included MHz new gasoline refinery FIX: opening the line management window now gives a click sound 24-Sep-04: FIX: included Hendriks fixes for the line management/button click problem FIX: tried to fix a freeze if the clock of the computer is set back NEW: included more Joachim Baums german texts about the trees 19-Sep-04: NEW: included MHZ' new car repair shop CHANGE: included updated czech translation by Ondrej Machulda 18-Sep-04: FIX: cities now build monuments again 08-Sep-04: NEW: included Joachim Baums german text about the beech trees Released Simutrans 0.84.16.1 06-Sep-04: FIX: clickable "world view" in factory info windows works again. FIX: color codes in goods list now match those in the station statistics. FIX: emergency vehicle remove button "X" now works again. FIX: furniture factory had miscolored edge (should've been transparent). FIX: schedule window scrollpane now behaves properly (Formerly it scrolled the contents out of the window if the "insert", "remove" etc. buttons were pressed) CHANGE: improved a few more german building descriptions. Released Simutrans 0.84.16.0 05-Sep-04: CHANGE: included MHZ' new furniture factory CHANGE: included updated slovak translation by Ján Krnác included updated prop-latin2.fnt (Ján Krnác ?) 04-Sep-04: CHANGE: included Hendriks depot window changes - Vehicle lists now have scrollbars 03-Sep-04: FIX: station statistics no longer show the special good "None" NEW: Added "Ballonstart" image painted by MHZ NEW: Added color codes to goods list CHANGE: tooltips of way building tools now show 2 decimals of the price and maintenance cost 01-Sep-04: CHANGE: replaced an industrial city building by a better version provided by MHZ CHANGE: improved a few city buildings NEW: new commercial city building "roof cafe" provided by MHZ CHANGE: improved a few german building descriptions 30-Aug-04: NEW: replaced one city building by a better version painted by MHZ NEW: tooltips for some of the schedule windows buttons FIX: yearly money chart root is now updated correctly (shows current year, not year of previously saved game) 28-Aug-04: CHANGE: finished including Hendriks update - factory info windows got scrollbars - schedules can be promoted to lines - finance window now stores monthly data for the past 12 months (savegames are converted automatically) - improved input focus handling NEW: new vehicle TPI Std 2-6-4T (steam) CHANGE: finance window now remembers size and settings if closed and reopened unless you load a saved game or start a new map. 28-Aug-04: CHANGE: started to include Hendriks update Released Simutrans 0.84.15.3 24-Aug-04: FIX: assigning a vehicle to a new line by using the choice box in the vehicles schedule window no longer causes a crash. FIX: fixed a crash if a way without an owner was built (this problem was introduced by the fix for correct maintenance cost after replacing a way with another type of way in 0.84.15.2) 23-Aug-04: CHANGE: back-ported 128 set basement images to 64x64 pixel set CHANGE: if a passenger destination is a water square, the destination is re-rolled. This doesn't avoid off-shore destinations entirely but reduces them a lot. CHANGE: factories now produce passengers (commuting workers) and mail properly CHANGE: theatre now has "Leuchtreklame" and lit windows 22-Aug-04: CHANGE: line management window now remembers size and settings if closed and reopened unless you load a saved game or start a new map. Released Simutrans 0.84.15.2 22-Aug-04: FIX: if a way is replaced by another way, maintenance costs are now calculated correctly FIX: finance window chart now considers starting year correctly CHANGE: improved some images - station floors - station roofs - loading bay images now have a few crates shown - new oak tree (slightly modified version from 128 set, original image by Tomas Kubes) - new mountain pine tree - improved fir image - improved cement mill image somewhat - added old roadster private car (slightly modified version from 128 set) CHANGE: tried to reduce the overall number of oil wells -> maybe the default industry density should be risen a little bit now CHANGE: added new city building rule for houses -> fills empty middle of 3x3 block of houses CHANGE: added translations for a few trees (don't know the names of some -> no description possible) 21-Aug-04: CHANGE: improved some images - some industrial town houses - some commercial town houses 20-Aug-04: CHANGE: improved some images - Townhall stage 0 and 1 - some residential town houses (night mode) - some industrial town houses (night mode) Released Simutrans 0.84.15.1 20-Aug-04: FIX: station can only store 128 units of goods per square -> this now also holds true for passengers generated by tourist attractions Released Simutrans 0.84.15.0 20-Aug-04: CHANGE: added a missing space to short freight info CHANGE: station can only store 128 units of goods per square and type for all types of goods -> for freight the limit was a total of 1000 before regardless of station size -> for passengers and mail it was 64/square before CHANGE: rectified and updated some of the german in-game help files. NEW: all depot window buttons now got tooltips NEW: display options window got a "?" gadget to open the associated help window. NEW: station details window got a "?" gadget to open the associated help window. FIX: fixed a bug in HTML (help) display if a link was the first word of a line, it couldn't be selected as a link. NEW: added factory details (optional) windows 15-Aug-04: NEW: most depot window buttons now got tooltips CHANGE: included updated slovak translation by Ján Krnác 14-Aug-04: CHANGE: continued to work on monorail track and vehicles 12-Aug-04: NEW: shaved off a few more CPU cycles from the graphics code 11-Aug-04: NEW: hand-optimized assembly routines for drawing tiles -> about 5% faster than previously used C code ??-Aug-04: CHANGE: continued to work on monorail track and vehicles Released Simutrans 0.84.14.0 06-Aug-04: CHANGE: added introduction dates for all ships CHANGE: added introduction dates for more busses 05-Aug-04: FIX: fixed a bug that caused signals to become partly transparent after zooming out and zooming in again FIX: fixed a bug that cause station names to be displayed in mini world view in some windows (e.g. station info) FIX: changed printers ink, chemicals and medicine from metric "tons" to metric "crates" NEW: "max_transfer" parameter added to simuconf.tab CHANGE: added introduction dates for all train engines 04-Aug-04: CHANGE: improved some images - pharmaceutical plant 03-Aug-04: CHANGE: improved some images - Serj Gaz 4 Goods truck is now a little bit larger - H-Trans Mail truck - Tree 1 was replaced by a better tree image - Commercial building "Office block" 02-Aug-04: NEW: introduction message for new vehicles 01-Aug-04: FIX: limited max distance for pathfinder to 480 steps. This fixes a crash of a route with almost 512 step was found and a train wants to add additional steps. The chosen parameters are safe for trains up to 31 carriages. This should be ok, since the player can't set up trains longer than 16 carriages. Released Simutrans 0.84.13.0 31-Jul-04: NEW: show up arrow for capped station statistic bars NEW: "show_names" parameter included in simuconf.tab CHANGE: included updated czech translation by Ondrej Machulda 29-Jul-04: NEW: on-map statistics for stations can now be toggled by pressing ! NEW: timeline can now be switched on/off for testing in simuconf.tab (default is off) 28-Jul-04: NEW: on-map statistics for stations NEW: improved city roads for 64x64 pixel tile set Released Simutrans 0.84.12.0 24-Jul-04: NEW: (Hajo) buildings mail and passenger levels now actually influence creation of mail and passengers FIX: one more bugfix to make Simutrans run past year 2612 CHANGE: I've tried a kind of 'optimization'. koord3d objects formerly had a size of 6 bytes, which is bad for both 32 bit and 64 bit systems. I've now squeezed them in 4 bytes -> this means they require less memory and are better aligned in memory, but the components now are 11 and 10 bits in size which adds some overhead to component accesses. Right now, I don't know if the tradeoff will be a win or a loss ... please let me know about your experiences. CHANGE: (DarioK) Passengers now check for destination station before doing pathfinding. This is slower for new games with only few stations, but faster for big games with a lot of interconnected stations FIX: (Hendrik) loading games with 0 factories works again 18-Jul-04: NEW: (Hajo) buildings now have separate mail and passenger generation levels Released Simutrans 0.84.11.0 10-Jul-04: CHANGE: language/translation file entries/translation facility now supports up to 4K of text per entry CHANGE: game time is now an unsigned 32 bit value. This change should allow to play Simutrans games beyond year 2612, the limit is now around year 3200 Included Hendriks updates: Bugfixes: - delete multiple lines in line management window, and immediate selection of other line could crash Simutrans - suppliers now get correctly registered, even for savegames - line management window "update line" selected wrong line Changes: - textinput supports clipping & scrolling & more - combobox uses new textinput component and is now inheriting gui_textinput_t - window titles now support clipping Released Simutrans 0.84.10.0 03-Jul-04: FIX: new map window now displays translated factory names Included Hendriks updates: Bugfixes: 1) now lists up to 24 suppliers of an industry (was 6 before) 2) improved focus handling of text input fields Bugfixes: the new "gueterbahnhof" was not shown in overview map overlays (coverage, etc) "operating profit" was adding vehicle's running cost to profit, rather than subtracting Change: added ev_key_mod to event struct (ev_key_mod holds key modifiers, such as SHIFT, ALT, STRG, etc); required for new features (see below) New: overview map enhancement (show connected industries; shows customers default, use SHIFT to show suppliers) map zooms in/out respecting SHIFT key Change: savegame version now 84007 (was required for new curve in finance window) if user clicks into the scrollbar, top-left or bottom-right the slider, now the scrollbar scrolls up/down accordingly 19-Jun-04: Included Hendriks updates: New: line filter in line management (all, truck, train, ship) using tabs New: introduced new curve to finance window: ops profit Change: simplified delete_line interface (now accepts simline_t *, too) fixed potential (very minor) memory leak in line management window (halt_list_item_t was instantiated always, now it is created if required, only); Change: savegame version now 84007 (was required for new curve in finance window) if user clicks into the scrollbar, above/left or below/right the slider, now the scrollbar scrolls up/down accordingly Released Simutrans 0.84.9.1 13-Jun-03 FIX: oil rigs are now initially linked to their stations again Released Simutrans 0.84.9.0 10-Jun-04 FIX: fixed an invalid memory read in image encoding (zooming from zoom level 2 to zoom level 1 could cause a crash) CHANGE: reduced output storage capacity of most factories CHANGE: default industry density is now 450 (was 420) 02-Jun-04 FIX: included Hendriks fix for an index problem in deleting a line CHANGE: improved a few buildings images CHANGE: replaced duckpond-park with tomas new park Released Simutrans 0.84.8.6 31-May-04 CHANGE/FIX: station<->factory link is now bidirectional, this means, situations in which a station was linked to a factory but the factory not linked to the station cannot occur anymore. In games with many stations and factories, this change should also save a little bit of CPU time for other tasks. Attention: the link has a length of 4 squares, this means, there can be at most 2 squares between the station and the factory (unless the station has an irregular shape, e.g. T-shaped). FIX: building powerlines no longer crashes the game FIX: new cities can only be built on empty, flat squares FIX: new cities no longer can be built on water FIX: added a missing space to station detail info string (indention of "goods needed by factories" list) CHANGE: splash screen now uses proportional font Released Simutrans 0.84.8.5 19-May-04 CHANGE: if user has not selected a language yet, Simutrans defaults to english now CHANGE: there are now 32 goods categories available for use in scenarios CHANGE: mouse pointer is now shown on splash screen FIX: added a missing space to station short freight info string (e.g. shown in line management) Released Simutrans 0.84.8.4 16-May-04 FIX: all road bridges can be used now FIX: stations can't be connected sideways anymore FIX: scenario specific translations now get packaged, too Released Simutrans 0.84.8.3 01-May-04 NEW: included Hendriks changes: - internal improvements in line management - fixed a crash if list and filter window were closed by pressing backspace FIX: fixed a bug in MakeObj that created all vehicles with engine type "steam" FIX: horses got engine type "bio" CHANGE: depot frame now deduces engine type from vehicle data instead from vehicle name (this means that in old translations, the type will be listed twice!) CHANGE: news ticker discards newly incoming messages that have the same text as the last shown message 15-Apr-04 CHANGE: moved scenario specific texts into scenario directory, and adapted translator to read them from there 14-Apr-04 CHANGE: cleaned up translation facility API Released Simutrans 0.84.8.2 11-Apr-04 FIX: lines entered from line management window now show up in depots again FIX: lines entered in another depot, are visible in all depots again after opening their info window 10-Apr-04 CHANGE: saved games of versions < 84006 became incompatible due to an accident. I can't revert this accident, so the game now treats such saved games as incompatible and gives an appropriate message instead of crashing Simutrans 0.84.8.1 for developers only 09-Apr-04 FIX: added a safety check to vehicle list, to avoid a crash if the entry of a just deleted vehicle is clicked FIX: button_t was missing an assignment operator which could cause a crash in certain circumstances Released Simutrans 0.84.8.0 04-Apr-04 NEW: freight train station - so far look only, no effect on gameplay yet 03-Apr-04 NEW: goods info window now shows weight per unit, too. 02-Apr-04 CHANGE: enabled -async option for SDL based Simutrans versions 01-Apr-04 CHANGE: adjusted weights of busses and trucks 31-Mar-04 CHANGE: adjusted 64x64 pixel tile set wagon weights CHANGE: AI now searches more locations for stations FIX: AI now properly raises/lowers land in order to build a station CHANGE: AI now uses "wait for 100%" setting if using trains 30-Mar-04 NEW: weight of freight is now considered in acceleration calculation CHANGE: small steam engine now has gear 1:2 CHANGE: big steam engine now has gear 1:1.2 CHANGE: pharma producer consumes 230% chemicals instead of 530% chemicals to produce medicine Released Simutrans 0.84.7.1 29-Mar-04 FIX: fixed a bug in loading engines with old style way type "electrified track" -> now they are listed in depots again, waytype is now silently changed to track FIX: included a fix by Hendrik to better initialize waiting goods statistic for less frequented stations NEW: included Stefan "MIP" Wuttichs cobblestone road 28-Mar-04 NEW: MakeObj now supports engine_type for vehicle DAT/PAK files: - diesel - electric - steam - bio - fuel_cell - hydrogene NEW: MakeObj now supports weight_per_unit for goods DAT/PAK files: - weight_per_unit NEW: steam engines now always produce steam clouds while driving FIX: fixed an array bounds violation in AI bridge building 27-Mar-04 NEW: cobblestone road FIX: 's' and 't' shortcuts work again 26-Mar-04 NEW: initial intercity road type can be configured in simuconf.tab NEW: added introduction date attribute for new ways types CHANGE: removed last reference to "Track" and "Road" (the old types) 25-Mar-04 FIX: fixed a bug in wegbauer_t that caused a crash if a way without description was to be built CHANGE: changed AI code to use new way types CHANGE: initial inter city roads now work properly with new road types 24-Mar-04 CHANGE: changed way builder to work with new way types CHANGE: changed city building code to work with new way types CHANGE: changed maintenance cost calculation to work with new way types CHANGE: road tools menu CHANGE: rail tools menu INTERN: restructured 64x64 pixel tile set way images and way descriptions 23-Mar-04 FIX: included Hendriks fix for typed lines NEW: prepared road tool icons NEW: road images for dirt road and gavel road INTERN: restructured 64x64 pixel tile set way images and way descriptions 22-Mar-04 CHANGE: continued to work on new way types (descriptions, menus, icons, cursors, DAT/PAK files) INTERN: reduced number of #includes in translator.h 21-Mar-04 CHANGE: continued to work on new way types: changed MakeObj and PAK loader classes to write and read new 'way' PAK files 20-Mar-04 FIX: included Hendriks bugfix for schedule list/waypoint crash released Simutrans 0.84.6.0 NEW: rail tools menu now shows costs of the actions NEW: road tools menu now shows costs of the actions CHANGE: changed error reporting mechanism for template (container) classes CHANGE: started to rework rail tools menu to allow several track types CHANGE: started to rework road tools menu to allow several road types 17-Mar-04 CHANGE: integrated Hendriks changes 15-Mar-04 FIX: industries are now created on all densities except density 0 14-Mar-04 CHANGE: pause only cancelled by key codes >= 32 CHANGE: pause now really stops game time (industries don't try to catch up with production after unpausing) 13-Mar-04 NEW: frameless window type CHANGE: changed news ticker to frameless window -> this means it can now receive click events NEW: ticker messages can now be bound to positions. If clicked an a position is bound the map is centered on the position. 12-Mar-04 CHANGE: reduced header file dependencies 11-Mar-04 FIX: fixed some GCC 3.2 compatibility issues in the template classes NEW: minimum industry density setting does not generate any industries at all released Simutrans 0.84.4.0 06-Mar-04 FIX: fixed a problem in removing a signal inside a station 05-Mar-04 CHANGE: included Hendriks changes - schedule window now has scrollbars - up to 32 stops per schedule allowed - minimize map doesn't leave buttons floating - minimize station and vehicle info window doesn't interfere with chart drawing anymore 04-Mar-04 FIX: "no route" vehicle list filter works again (was broken during introduction of new vehicle code between 0.83.x and 0.84.x released Simutrans 0.84.3.2 03-Mar-04 FIX: fixed a problem in removing signals FIX: fixed a text formatting problem in factory info window released Simutrans 0.84.3.1 29-Feb-04 NEW: city list window (hotkey 'T') 28-Feb-04 FIX: overhead wires no longer disable way info display CHANGE: finished to incorporate Hendriks new relief map CHANGE: way statistics now get saved with the game 27-Feb-04 CHANGE: started to incorporate Hendriks new relief map -> need to change the structure somewhat to fit better 22-Feb-04 CHANGE: fixed some mistakes that have been introduced while changing character arrays to the new buffer objects CHANGE: vehicle list now formats income with thousands separators CHANGE: included updated danish translation (now using ø) CHANGE: included updated japanese translation NEW: added toolbar icon for line management 21-Feb-04 CHANGE: reworked object info handling to use bounds-checked character buffer objects instead of plain character arrays. I hope this will help to solve some of the memory (i.e. buffer overrun) problems 20-Feb-04 NEW: added max_hops parameter (goods routing search depth) to simuconf.tab 19-Feb-04 CHANGE: changed city name list to use instead of released Simutrans 0.84.2.2 19-Feb-04 FIX: fixed a typo in simuconf.tab: display_height is now written properly (formerly the code checked for display_heigth) FIX: fixed a buf that made too fast vehicles miss the proper stopping point sometimes (could cause trains to get stuck near signals) released Simutrans 0.84.2.1 16-Feb-04 FIX: Bridge tools menu size now adapts properly to number of contained icons released Simutrans 0.84.2.0 15-Feb-04 FIX: added ø character to prop.fnt (needed for danish translation) CHANGE: included updated danish translation from Finn Rosenbech Jensen (not yet using ø, was made for former font version) NEW: included new, downscaled, oil rig from 128x128 pixel tileset made by James Starr WARNING: this one is 3x3 squares - the old one was 2x2 if you load old games, that have things near oil rigs, there might be bad effects if the new oil rigs overlap the existing things! WORKAROUND: get the old factory.Oelbohrinsel.pak from the 0.84.0.1 base package and use it instead of the new one. 14-Feb-04 FIX: fixed a bug that could cause a truck with a trailer to wait for itself to clear the road (= was stuck) NEW: added "topspeed" parameter to bridge dat files CHANGE: removed bridgepos parameter from bridge dat files NEW: speed limit for bridges (not really sure if that works for sure it won't affect existing bridges in old games, only newly built bridges) NEW: bridge tool tooltips now list additional information (price, speed limit) WARNING: the changes to the bridge DAT/PAK files introduced a slight incompatibility with former bridge PAK files: the old files do not store icon information for the menu. Thus if you use old PAK files with this version, the bridge tools menu will show empty icons. The tooltips will help to identify the correct sort of bridge. -> ATM this only affects the 128x128 pixel tile set and selfmade bridges. Both must be updated to include icon information and recompiled with MakeObj 0.1.6 -> See datfiles_15-Feb-04.zip for examples (MakeObj forum, examples section) 12-Feb-04 NEW: added more simuconf.tab entries NEW: new toolbar like tool chooser window (will be used to select bridge types and way types) CHANGE: changed bridge tool selector to use the new tool chooser 11-Feb-04 NEW (by Hendrik): line selection in depot window now got a drop-down list (click into the input field to open) CHANGE (by Hendrik): vehicle info window now shows total running cost NEW: a simple tree planting tool (hotkey /) CHANGE: vehicle info window shows top speed limit and max top speed of the pulling engine 09-Feb-04 FIX: added missing town hall upgrade at 3500 inhabitants released Simutrans 0.84.1.0 07-Feb-04 NEW: added 1% waiting time to schedule choices NEW: included japanese translation and fonts CHANGE: chosen language is no longer saved with game (this means that the chosen language won't be altered if you load a game that was created with a different language setting) CHANGE: improved coal mine image a little bit 06-Feb-04 FIX: fixed a problem that's been introduced during some changes while 0.83.x development: it is no longer possible to build roads or rails through a bridge abutment (was ok before 0.83.x, too). FIX: town hall upgrades no longer modify squares south of the new town hall if those squares are owned by a player CHANGE: factory info window now shows max production per day CHANGE: lowered coal consumption of power stations by 30% CHANGE: risen productivity of saw mill and material wholesale by 15% each 05-Feb-04 CHANGE: included new fi.tab provided by Antti Louko CHANGE: improved steel mill image 04-Feb-04 NEW: speed limits for ways (currently 50 km/h on city roads 130 km/h on other roads and 450 km/h on rails) released Simutrans 0.84.0.1 02-Feb-04 FIX: fixed a bug if a line was deleted while it had pending updates FIX: fixed a an access to an uninitialised variable if a line was deleted after loading a saved game released Simutrans 0.84.0.0 01-Feb-04 CHANGE: finished rework of convoi and vehicle class CHANGE: lowered productivity of coal mines CHANGE: improved townhall stage 1 image CHANGE: improved townhall stage 2 image NEW: vehicles now issue a message in the news ticker if they enter a depot FIX: fixed a bug in town hall expansion code FIX (by Hendrik): vehicle info window minimum size now considers chart width FIX (by Hendrik): station info window minimum size now considers chart width FIX (by Hendrik): list of lines stopping at a station is now properly updated after a change to the line NEW: included Paul Szczepaneks stadium NEW: included Danish translation by Finn Rosenbech Jensen 31-Jan-04 CHANGE: continued thorough rework of convoi and vehicle class 30-Jan-04 CHANGE: started thorough rework of convoi and vehicle class structures - moved route data from vehicle to convoi class - moved async step from vehicle to convoi class Code cleanup - moved strequ from simplan.cc to simstring.c, renamed to tstrequ released PAK update 28-Jan-04 CHANGE: - unified window colors - added shadows - reworked many building images 27-Jan-04 NEW: included Paul Szczepaneks tennis court, theatre and home market images. 26-Jan-04 CHANGE: improved car factory image CHANGE: improved steel mill image released Simutrans 0.83.7.1 25-Jan-04 CHANGE: replaced a residential city building with a better version from 128x128 pixel tile set (Modern house) CHANGE: replaced an industrial city building with a better version from 128x128 pixel tile set (Tools factory) CHANGE: adjusted city building AI to get better industrial and commercial city areas CHANGE: adjusted chart size and placement to allow bigger value ranges (line management, station info, vehicle info - suggestion by Hendrik Siegeln) CHANGE: improved oil field from 128x128 pixel tile set included CHANGE: increased probability to create a bookshop, car shop, or furniture shop in a city. 24-Jan-04 FIX: fixed an array bounds violation if loading a heightfield again after increasing the number of cities. FIX: fixed a problem in removing signals - now issues an error message if the signal combination cannot be removed safely. FIX: removed an erroneously lit pixel from one of the trees NEW: translatable thousand and fraction separators (language dependant number formatting) NEW: 4x7 font (map legend) now also available in latin-2 (needed for polish and czech translation) NEW: added lights to private cars 23-Jan-04 FIX: zooming now works reliably again (was broken due to introduction of skinning support) CHANGE: replaced wait loop by timer solution 22-Jan-04 NEW: continued to work on skin support -> support for user defined button skins released Simutrans 0.83.7.0 21-Jan-04 NEW: continued to work on skin support -> created example/default skin NEW: added tooltips to vehicle and station info window buttons CHANGE: changed some buttons shapes (factory info and depot info windows) 20-Jan-04 NEW: continued to work on non-darkening image drawing 19-Jan-04 NEW: started to work on non-darkening image drawing NEW: started to work on skin support released Simutrans 0.83.6.1 18-Jan-04 FIX: latin-2 font now also loaded if saved language setting needs latin-2 font FIX: intercity road offset can be 0,1 (64x64 pixel tileset) or 0,2 (128x128 pixel tile set) -> I hope it will make the intercity roads also work with the 128x128 pixel tile set CHANGE: intercity roads now have no owner anymore released Simutrans 0.83.6.0 18-Jan-04 NEW: support for latin-2 proportional font and new polish translation. The latin-2 font and polish translation update have been kindly provided by Maciek Gajewski CHANGE: included new italian translation and citylist. Both have been kindly provided by Stefano Bonzi CHANGE: small updates to chart layouts (station, vehicle and line management windows) 17-Jan-04 CHANGE: made line management window resizeable FIX: scrollbars in convoi and station info window now work again, even if the chart is hidden FIX: tried to fix the initial road connection problem with 128x128 pixel tile set 16-Jan-04 MERGE: included Hendriks changes - more vehicle statistics - more station statistics - heavily improved line management window - finance window 32 bit integer overflow fixed released Simutrans 0.83.5.0 14-Jan-04 NEW: initial road connections between cities. This slows down map creation noticeably. The maximum length of the roads (= time spent during map creation) can be configured with a simuconf.tab entry - if map creation is too slow now on your computer, edit simuconf.tab and lower the max road length. NEW: overhead wires now have a maintenance cost of 2 CR/month (configurable via simuconf.tab) 12-Jan-04 FIX: tried to get more information about PAK file loading problem on some Linux versions CHANGE: changed a commercial city building image+description 11-Jan-04 NEW: Debugging helper: buildings list window CHANGE: pak update - new old-style goods truck - two more high level industrial city buildings - one more high level commercial city building - replaced "Medium offices" with better image - changed levels of some buildings to achieve better look of cities - changed descriptions of some buildings - fixed two mismatched buildings descriptions 10-Jan-04 NEW: back ported Gaz-4 goods truck from 128x128 set to 64x64 09-Jan-04 NEW: started to work on introduction dates for vehicles Currently they are just stored in the PAK files and displayed in the depot window - no effect on gameplay yet. MakeObj 0.1.5 support two new attributes for vehicles: intro_year (int): Year of introduction intro_month (int): Month in year gear (int): gear of engines - will work as power multiplier. If you create PAK files, make sure to include them in your dat files!!! The gear value is an factor to distinguish freight train engines and passenger train engines - freight train engines use to have a larger gear value, this means they have less top speed but they can pull heavier trains. (Of course this is only an approximation to reality) I've assigned a few introduction dates and gear values to trucks, and some train engines, but I think I need help to assign proper values to all vehicles. CHANGE: risen max map size to 1024x1024 08-Jan-04 FIX: fixed a bug in rtrim() function that caused characters 128-255 to be treated as whitespace (effect: city names ending in umlauts or accented characters got those characters removed) released Simutrans 0.83.3.0 and upgraded food chain 08-Jan-04 FIX: Map legend now dynamically adapts to the number of configured factories (-> always big enough to list all factories) CHANGE: more finetuning on 64x64 image set and configuration CHANGE: rebalanced food chain configuration to better fit into the 64x64 object set 07-Jan-04 NEW: Goods overview/list now shows category of goods, too (Hotkey Shift-G) Back ported IL-209S goods truck and trailer released back-ported food chain Back-ported 128x128 food industry chain to 64x64 - Goods: canned_food, flour, beer, grain, meat, fish, food, milk - Factories: grain farm, fish_pond, fish swarm, cow farm, brewery, dairy, grain mill, bakery, food processing plant, cannery, supermarket - Vehicles: cooling wagon, milk wagon, cooling truck, milk tanker, fishing boat Updated 64x64 pixel tile set - lumber plantation size increased to 3x3 - further improved a few city buildings images 06-Jan-04 Updated 64x64 pixel tile set - improved a few city buildings images - included new printing works image - included new coal mine image - added Patricks Rock again (had been lost) - included improved sidewalks CHANGE: included new french translation by Dominique Gainche 04-Jan-04 CHANGE: included updated czech translation by Ondrej Machulda released Simutrans 0.83.2.2 04-Jan-04 FIX: included a fix by Hendrik to prevent a crash if the last vehicle of a convoi is removed from the convoi in the depot -> we are not 100% sure if this fix will not break something else 03-Jan-04 FIX: added missing variable initializers to convoi_t constructors (Hendrik, Hajo) -> This should fix a number of line-management related problems after loading a saved game -> This might also fix the trashed line names in the vehicle info window (the bug doesn't occur on my system, so I can't test if this fix really helps) released Simutrans 0.83.2.1 02-Jan-04 FIX: tried to fix the trashed line names in vehicle info window (the bug didn't occur on my system, so I can't test if this fix really helps). FIX: added 'special building' translation to de.tab FIX: uprising messages are now time-synced (raise equally fast on all computers) NEW: added 'w' key documentation to keyboard help CHANGE: changed behaviour of " key: only ordinary city buildings are hidden, special buildings stay visible CHANGE: line management window now opens fully inside screen/ window in default 800x600 resolution CHANGE: resolutions of width 1400 and 1600 now try to open in fullscreen mode (I can't test this on my system, so I don't know if it works well or not). 01-Jan-04 FIX: line numbers are now created in subsequent order again CHANGE: line 'name' is now initially set to all bytes 0 upon creating a new line. CHANGE: included citylist_pt.txt MERGE: included Hendriks changes: - Station info details window now list connected lines, too. released Simutrans 0.83.1.3 31-Dec-03 FIX: fixed a crash if the schedule of a line was changed (double free, Hendrik) FIX: fixed potential use of an uninitialized variable in schedule list ui released Simutrans 0.83.1.2 29-Dec-03 FIX: changed convoi waiting times from steps to real time -> with this fix, waiting times should consider the time lapse factor FIX: fixed a crash if station-connections are being rebuild while there is a vehicle without a schedule FIX: made two more entries of the lines management window translatable. FIX: fixed a bug that could produce the same line id twice (two lines with same id severely screw up the line management, this was an important fix). CHANGE: included new fi.tab provided by Antti Louko CHANGE: included citylist_en_gb.txt from the feedback forum CHANGE: added new entries for line management window to en.tab and de.tab released Simutrans 0.83.1.1 28-Dec-03 FIX: fixed this problem: 1. buy 4 wagons (without an engine) 2. add a waypoint to the schedule of the convoi 3. try to start the convoi 4. error message appears (convoi cannot drive, because it has no engine) 5. click on 'put in front' button 6. buy an engine => crash This fix required a major rework of the schedule handling. I'm not sure how stable the new code is. FIX: removing signals from circle-shaped rail blocks did not work properly. This is fixed now. CHANGE: road vehicles depot now has same spacing as rail vehicle depot (= 14 vehicles per row, 64x64 mode) CHANGE: re-added this to the 0.83 again (had been lost in early 0.83.x versions): passenger statistics in city window now shows unreachable passenger destinations in orange and reachable destination in yellow 27-Dec-03 CHANGE: added more debug/trace statements to PAK file loading code (hope this will some day help to track down the PAK file loading problems on some Linux systems) CHANGE: code cleanup released Simutrans 0.83.1.0 22-Dec-03 NEW: included Hendriks changes - Vehicle lines are now sorted - Fixed a crash due to an uninitialised variable in vehicle line list "press 'w' in new game, select "update" -> game crashes" -> now fixed. released Simutrans 0.83.0.1 released Simutrans 0.82.15.7exp 20-Dec-03 CHANGE: passenger statistics in city window now shows unreachable passenger destinations in orange and reachable destination in yellow 19-Dec-03 FIX: moved savegame format entry from forrestconf.tab to simuconf.tab released Simutrans 0.82.15.6exp 14-Dec-03 FIX: forest config is now read from 'config' subdirectory, too CHANGE: split forest config from simuconf.tab, now it read from forrestconf.tab FIX: tried to fix the goods distribution problem for factories bigger than 2x2 tiles released Simutrans 0.82.15.5exp 04-Dec-03 NEW: city names can now be read from name list files. Each language may have it's own city name list, i.e. text/citylist_cz.txt If no list file exists for a language the old city name creation scheme is used to create city names. released Simutrans 0.82.15.4exp 29-Nov-03 CHANGE: better distinguishable colors for finance window FIX: on slow computers it could happen that the vehicle starting sound was played several times. This should be fixed now. 28-Nov-03 FIX: fixed a variable overflow in finance window (Hendrik) FIX: finance window total money is now updated dynamically (Hendrik) CHANGE: replaced most strcpy() calls by a bounds checked, 0 terminating string copy function released Simutrans 0.82.15.3exp 27-Nov-03 FIX: fixed a potential buffer overflow is city building rules reading code FIX: fixed a potential crash if a curiosity building was removed released Simutrans 0.82.15.2exp 25-Nov-03 FIX: fixed a bug in city building rule evaluation code CHANGE: moved simuconf.tab to config subdirectory CHANGE: moved city building rules into config/cityrules.tab released Simutrans 0.82.15.1exp 24-Nov-03 FIX: fixed an uninitialized variable in station sorting code that could cause a program crash FIX: fixed an uninitialized variable in button display/event handling code CHANGE: added bounds checks to station sort mode text arrays access code CHANGE: changed Peters new city building rules to avoid cities only consisting of one single, extremely long road released Simutrans 0.82.15exp 22-Nov-03 FIX: fixed a potential crash if pressing the "X" button to remove vehicles from the map NEW: included Hendriks new finance window NEW: included Hendriks code to sort goods list in station info window. FIX: fixed a memory leak in Hendriks sorting code NEW: included catalan translation from Juan-Josep Bargues NEW: included updated czech translation from Ondrej Machulda 15-Nov-03 CHANGE: included Peters new city generation rules. They are about 30% slower than the old set of rules but create better cities, I think. 12-Nov-03 CHANGE: ships reference speed for speed bonus is now 40km/h. It was 80km/h before. Other vehicles still have a reference speed of 80km/h CHANGE: included Hendrik Siegeln in intro scroller FIX: vehicle list "no income" filter fixed (Hendrik) FIX: vehicle list "name" filter fixed (Hendrik) FIX: depot frame refresh after selling stored vehicles (Hendrik) FIX: adjusted sign height in 128x128 mode (Hendrik, Hajo) released Simutrans 0.82.14exp 01-Nov-03 NEW: buildings pak files (nodes) are now versioned NEW: support for "chance" value in special town buildings pak files. Chances are given in percent NEW: special city buildings are now built according to their chance value FIX: towns don't skip growth step anymore FIX: town halls are now always properly upgraded FIX: added missing translation for "Baumarkt" to de.tab CHANGE: cathedral is now a special city building with a 30% chance to be built for each city CHANGE: big stadiums now have a 40% chance to be built (was 100% chance before) CHANGE: drive in cinemas now have a 30% chance to be built (was 100% chance before) CHANGE: Deltic is now a diesel engine 31-Oct-03 CHANGE: got a saved game from Wolfgang Rufeger which crashed upon electrifying tracks. Reason for the crash: the track has no owner. I have no idea how that can happen. I've added an error check to rail block electrification routine - this at least avoids the crash, but it'd be better to know why the track lost the owner value CHANGE: changed a minor compatibility issue in ribi_t.h released Simutrans 0.82.13.1exp 21-Oct-03 FIX: fixed a minor glitch in scrolling/dragging the map if mouse pointer was close to the relief map CHANGE: vehicles now play their sound before departing instead of after arriving released Simutrans 0.82.13exp 19-Oct-03 FIX: good 'None' no longer shown in goods statistics NEW: Included updated czech translation by Ondrej Machulda NEW: improved two more building images 18-0ct-03 FIX: factories that don't produce anything now again consume input (got broken during of the changes for multiple products) FIX: plastics ship is available again (seems it was lost since the change to the pak file based configuration) CHANGE: updated keyboard help 16-Oct-03 NEW: statistics window, so far only displays transport fees and speed bonus for goods. (Press 'G' to open) NEW: speed bonus for fast transport of certain goods (see goods statistics window for details); 15-Oct-03 NEW: new locomotives: MJHN Deltic, MJHN Avocet, Light Br 5-7 CHANGE: improved window resizing 14-Oct-03 NEW: new city building new vehicle: Colin Motor Bus NEW: relief map is now draggable with right mouse button 13-Oct-03 CHANGE: improved a few building images CHANGE: included a new residential city building released Simutrans 0.82.12exp 12-Oct-03 NEW: new factory: Pharmaceuticals new shop: Chemist new good: Chemicals new good: Medicine CHANGE: further changes to factory code to distribute multiple products properly to all destinations CHANGE: adjusted income for some goods 11-Oct-03 NEW: researched how to version nodes in pak files. First tests implemented for factory pak files NEW: prepared factory code to support multiple products per factory NEW: new good: printers ink (produced by refinery) CHANGE: removed umlauts from pak file names -> this means old games cannot be continued because the names of factories and goods do no longer match 10-Oct-03 NEW: convoi info window now displays current destination FIX: buttons now ignore window resize events (made them flicker between pressed and unpressed state) CHANGE: improved a few building images CHANGE: included a new industrial city building CHANGE: reordered a few building images 09-Oct-03 CHANGE: implemented the faster movement code for private cars, too. CHANGE: lowered coal consumption of the power plants NEW: included better power plant image CHANGE: improved a few building images CHANGE: reordered a few building images released Simutrans 0.82.11exp 08-Oct-03 FIX: got a saved game with stuck vehicles. Changed vehicle movement code so that vehicles can drive again. -> side effect: new code is slightly faster than before and saves 4 bytes of memory per convoi :) CHANGE: continued work on new powerlines CHANGE: started to prepare better debugging support 03-Oct-03 CHANGE: continued work on new powerlines, fixed some bugs released Simutrans 0.82.10exp 17-Sep-03 FIX: ships can be started from depots again FIX: maintenance costs lowered and moved to simuconf.tab FIX: removed unnecessary waiting time for vehicles that wait for XY% full but already reached XY% CHANGE: continued work on new powerlines 16-Sep-03 CHANGE: continued work on new powerlines released Simutrans 0.82.9exp 15-Sep-03 FIX: fixed a very serious bug in AI station building. This bug was probably a long time there, but lately its effect caused a crash, due to changes to other program parts. Big thanks go to Duvelke for providing a saved game to reproduce this bug! 14-Sep-03 CHANGE: included updated italian, portuguese and dutch translations ??? CHANGE: added two more high level commercial buildings images CHANGE: removed one low quality commercial building image CHANGE: improved a few building images CHANGE: changed configuration of some engines and wagons 11-Sep-03 CHANGE: improved AI station building ability CHANGE: AI can now transport gasoline too - better said, it can now transport any good except mail an passengers 09-Sep-03 CHANGE: changed configuration of some trucks and trailers CHANGE: introduced new members of the tigress vehicles class released Simutrans 0.82.8exp 06-Sep-03 FIX: fixed a bug in heightfield loading code that sometimes created lines of land at the right and bottom map borders even if there was supposed to be sea there. CHANGE: Some config files now get closed immediately after reading - former Simutrans versions kept them open until the program ended. CHANGE: Adapted weight, prices and running costs of some wagons CHANGE: Changed some english and german translations entries 04-Sep-03 FIX: Another fix to load saved games with convois without vehicles but a schedule which are sometimes created by Volkers new depot code. Thanks go to Manfred Duesing for providing a saved game to reproduce this bug! CHANGE: risen height value range for heightfields from from -5...1 to -14...10 CHANGE: changed city building code to keep up with high growth factors. It was reported that the old code had problems with factory above +4, the new code should work well with higher factors, too 03-Sep-03 FIX: Fixed a type/range problem in getting heights from the map datastructure. This bug caused various height-related problems, including a crash if land was risen above 7 02-Sep-03 FIX: Fixed a bug in Volkers new depot code that allowed to remove depots which had vehicles stored in them. 01-Sep-03 CHANGE: Wagons and trailers now have speed limits, too NEW: High speed passenger and mail cars suitable for the Tigress I engine 31-Aug-03 CHANGE: new maps now can have cities up to size 3300 (was 1000 before) 30-Aug-03 CHANGE: separated coordinate and cstring classes again (Volker had tightly linked them) to get cleaner interfaces. released Simutrans 0.82.7exp 26-Aug-03 FIX: Got a saved game where convois had insanely high wait lock values. I don't know how that could happen - anyway the locks now have an upper bound of 1023 so that sooner or later such convois start to move again. CHANGE: Changed a potentially insecure instruction order in removing convois via the emergency button. Crashes have been reported, but couldn't be reproduced. I still hope this change will help. CHANGE: Reduced vehicle waiting time by another 5% CHANGE: Changed city information window to list unemployed and homeless people released Simutrans 0.82.6exp 23-Aug-03 FIX: Labels/signs now appear centered in 128x128 tile mode, too. CHANGE: Modified memory management: risen basic block size of pools to 16384 entries (had been 1024 before) This speeds up both allocation and deallocation of pooled objects. The overhead of partly used pools is small compared to Simutrans' overall memory consumption. (Currently this particularly speeds up map destruction, an operation that is performed before creating a new map, loading a new game and before quitting Simutrans). 21-Aug-03 FIX: Hajo: it seems Volkers new depot code sometimes saves convois without any vehicles. But Simutrans can't load them afterwards and crashes upon loading such a saved game. I've implemented a workaround that allows to load such games again, but I think this needs further investigation and testing. FIX: Fixed a temporary inconsistency of convoi data that lead to serious problems with the new reduced waiting time for vehicles. 20-Aug-03 FIX: Removing a mail office now resets a stations ability to collect, store and move mail. FIX: Car factory had input capacity 0/0t plastics. I'm not sure if this affected the game at all, but it's now set to 0/300t CHANGE: Only one mail office per station allowed CHANGE: Reduced overall loading time of vehicles. Trucks, busses and ships now need about 2 seconds to load, trains need 2 + (number_of_waggons/4) seconds to load. -> If you have a slow computer and Simutrans is near the max CPU power already, the vehicle loading times will grow. Try the -refresh n switch in that case (see readme.txt for details). 13-Aug-03 CHANGE: Reworked new vehicles/new pathfinding (not included in official release). released Simutrans 0.82.5exp 30-Jul-03 CHANGE: Limited max passenger search depth (route planning) to 300 stations (was 900 formerly) FIX: Heightfield loading now works well with artificial slopes - no extra support walls are created anymore 29-Jul-03 CHANGE: Lowered passenger rate for higher level buildings 28-Jul-03 FIX: Added missing initialization to slope array FIX: Added missing initialization to buildings after loading FIX: Fixed a 'race condition' in world destruction. In rare occasions, there was step() called on cities that just underwent destruction. CHANGE: lowered passenger rate for higher level buildings 27-Jul-03 NEW: UI button class got a tooltip feature. Added tooltips to some buttons that didn't have a meaningful label NEW: Passenger statistics for stations (happy, unhappy, no route found). Newly created passengers become unhappy if their chosen starting station has more than 64 passengers per segment already waiting there. Unhappy passengers will not use your transport service. "No route" in this case means that a passenger came to the station, couldn't find a route to his desired destination and went home. Included updated czech translation by Fast Ikarus Included updated polish translation by Lukasz Remis Included Tomas Kubes' monument 26-Jul-03 FIX: Fixed a crash if the height of a schedule entry (waypoint) was changed and the schedule window was opened after the height change FIX: Using the emergency button to remove vehicles now resets the rail block counters if the vehicle was a train NEW: tooltips for tool selection windows 22-Jul-03 FIX: Screenshot directory is now created with correct access permissions under Linux FIX: all 16 player color sets are loaded correctly again FIX: Fixed two minor bugs in world map creation that caused trouble if the water level was changed from default level 20-Jul-03 CHANGE: Continued work on artificial slopes - created menu icons and added slope tools to raise and lower land. NEW: Textures for slope support walls NEW: Added loading and saving of artificial slopes. FIX: Fixed a bug in heightfield loading (wrong filename suffix) (Heightfields are still searched in the "save" directory) 19-Jul-03 CHANGE: Continued work on artificial slopes - completed slope table, created a menu and tools 17-Jul-03 CHANGE: Continued work on artificial slopes - painted slope cut walls and implemented first version of slope mapping table. 16-Jul-03 CHANGE: Changed grid heights to sint8. This saves 65k for a standard 256x256 map CHANGE: Continued work on artificial slopes - grounds now have up to three images: artificial walls, the ground itself and a way image (this takes additional 128k of memory for a standard 256x256 map) 14-Jul-03 NEW: Started to work on artificial slopes 13-Jul-03 FIX: Fixed a bug in AI code that could crash the game if the AI was searching for suitable places to build a station near the map border CHANGE: Private cars speed risen to 60 km/h 12-Jul-03 CHANGE: Enlarged text buffer for convoi info window from 16k to 32k CHANGE: Lowered passenger generation at tourist attractions by 20% CHANGE: Included Tomas Kubes' sawmill in 64x64 image set NEW: Added emergency button to vehicle info window - it removes the vehicle from the map. This is useful to get rid of stuck vehicles. Be careful: the removed vehicle is gone forever! The button is labelled "X". NEW: AI will now remove vehicles if they have no suitable route (anymore) - i.e. due to a bug or player sabotage. 09-Jul-03 FIX: Tourist attractions are valid passenger destinations again - formerly this information was lost after loading a saved game FIX: Fixed bug in tourist attraction buildings own tourist generation code ... sometimes they produced insane amounts of tourists, followed by periods of no production at all. Generation rate is now set to 82 passengers per minute for perfect destination coverage (I never reached more than 30% coverage so far). 07-Jul-03 FIX: Fixed a bug in payment calculation while waiting for a given loading level (i.e. wait for 100%). CHANGE: During my tests it seems that maintenance costs are much too low. Thus I've doubled all maintenance costs. -> this is also an incentive to use ships. Unlike rails and roads, water doesn't need maintenance :) NOTE: Due to the changed payment calculation (introduced in 0.82.0exp), passenger transport gives higher revenues now. Maybe further adaptations must be made, but the new calculation scheme is correct, while the old scheme was wrong. 06-Jul-03 FIX: Compressed savegames work reliably now. (See simuconf.tab for details) CHANGE: Changed income calculation: now on every stop income is calculated for all transported goods, based on the distance since the last stop. Before, it happened that goods that stayed in the vehicle for several stops, only paid the last hop, instead of the full distance. CHANGE: Lowered (to 25%) amount of passengers generated at tourist attractions. Now up to 42 passengers are generated per minute (less if the player has not 100% destination coverage). Included new or updated language files: nl.tab - Martyn Minnis cz.tab - Kamil "FastIkarus" Ondrák fi.tab - Antti Louko pl.tab - Piotr Pietrzak sk.tab - Ján Krnác sv.tab - Julius Grantén pt.tab - Eric C. Olivera 28-Jun-03 Changes by Volker: CHANGE: New path structure - subfolders font, palette, save and screenshot are now used. Starting the new game will create them automatically and move the files there. Be careful: an old version will not find them afterwards. NEW: More work on new depot dialog (resizeable etc.) NEW: Speedbar for vehicles CHANGE: Removed old code - this can only read games saved with the prev. version. NEW: Savegames are now zipped by default - loading autodetects the savegame type. (Hajo: doesn't work on my system. Default seems to be set to binary, not zipped. Forcing zipped mode causes a crash). CHANGE: Trees are displayed small in hide-buildings-mode NEW: When using commandline "-load " the extension needs no longer be supplied. ??? CHANGE: industry distribution now generates larger average distances between industries 21-Jun-03 CHANGE: continued work on new vehicles and pathfinding 20-Jun-03 CHANGE: continued work on new vehicles and pathfinding 19-Jun-03 CHANGE: continued work on new vehicles and pathfinding 18-Jun-03 CHANGE: continued work on new vehicles and pathfinding 17-Jun-03 NEW: started to work on a new vehicle class that implements a different, more flexible pathfinding method About 0.82.pre1 This version is really just a preview. Volker told me he will further improve the depot dialog, but you can already try it a bit. Also Volker made a lot of internal changes that can affect stability but we hope that overall they will help further development. A few minor bug fixes are also included. You can use this version with the 0.81.35exp base package. 14-Jun-03 FIX: fixed a bug in player color tables (green color set got wrong values due to some wrong index calculations) FIX: fixed ground water level calculation in zoom modes with a raster width of less than 64 FIX: bridges can now be built on electrified track CHANGE: included Volkers changes (lots ... most notably the new depot window) 09-Jun-03 CHANGE: support for unzoomable images (icons ...) adapted MakeObj, display routines and dat files -> if image names in .dat files are prefixed with "> " they will not get zoomed in the game -> IMPORTANT NOTE: this changes makes all previous PAK files incompatible!!! 07-Jun-03 FIX: player color table initialisation wasn't executed after starting a new game. Table now gets initialised always. 29-May-03 CHANGE: enhanced MakeObj and Simutrans to use 7 shades of player colors (instead of 4). Also there are now 9 reserved player colors that can be used for future expansions -> This change introduces a slight incompatibility with old saved games. The player color settings will most likely become reset to color set 0 if loading an old game, some times a random other set can be chosen. This means you'll most likely have to choose your preferred color set again. 25-May-03 released 0.81.33exp 24-May-03 FIX: tourist generation now depends on real time (formerly it depended on simloops, but simloops vary too much to be a reliable measure) NEW: factory window now displays the category of produced goods if they are categorized goods 15-May-03 NEW: added category for cooled goods (category 4) CHANGE: changed calculation of town growth a bit (towns grow slower now) CHANGE: zoom level can now be changed by "page up" "page down" keys, too. (Zooming quality is still bad) 11-May-03 NEW: translations are now read from 'text' subdirectory. Simutrans can now read up to 40 translation files. Thanks to Adam Barclay for this nice addition. 08-May-03 CHANGE: cursor keys now scroll map, too 25-Apr-03 FIX: traced Simutrans for memory leaks and accesses of/to uninitialised memory 21-Apr-03 CHANGE: optimized some graphics routines (5% faster now) 20-Apr-03 CHANGE: reduced number of malloc()/free() calls for loading trees and buildings (-> games should load a bit faster now) 19-Apr-03 FIX: fixed a crash if the player or AI scheduled a new vehicle 18-Apr-03 FIX: after a schedule is changed, freight to stations that are no longer included in the schedule is removed from the vehicle FIX: saved games in which factories have negative amounts of input stored are now repaired (amount set to 0) upon loading. Also factories that have more than 15000 units of input are clipped to 15000 units. 15-Apr-03 FIX: fixed a memory leak in slist_tpl/nodelist 08-Apr-03 FIX: goods distribution didn't consider multiple destinations per station properly. This should be fixed now. 05-Apr-03 CHANGE: factories now distribute goods always to that station that currently has the least amount of that good stored CHANGE: reduced vehicle waiting time at stations by 15% in average. Unfortunately the waiting times vary strongly. CHANGE: improved shores images a bit FIX: tried to fix a bug in the AI code that sometimes created train stations with defective last tiles and then placed trains on those defective tiles with the effect that those trains never could move 29-Mar-03 CHANGE: removed last dependency to ADT package 23-Mar-03 CHANGE: optimized map lookup 22-Mar-03 CHANGE: optimized goods routing 21-Mar-03 CHANGE: optimized goods routing 20-Mar-03 CHANGE: optimized vehicle routing FIX: land vehicles now always find shortest path CHANGE: optimized routing of goods and passengers 16-Mar-03 CHANGE: changed ownership (player) management for things CHANGE: tried to reduce memory consumption, saved 8 bytes per map square that has no bridge or tunnel -> risen max map size to 768 for that people with powerful computers. FIX: fixed a potential buffer overflow in minivec_tpl FIX: fixed a potential buffer overflow in microvec_tpl 15-Mar-03 NEW: included Matthews changes for "min size" window gadget NEW: "new map" dialog now gives estimated memory requirement for chosen map size NEW: included 4 types of variant grass to break the uniformity if the landscape 13-Mar-03 FIX: fixed a problem if tunnels went under squares with name signs or bridges went over them FIX: fixed the dat file for TPI Class 26 engine 06-Mar-03 NEW: fixed another bug that caused a program freeze if a private car had gone 2^32 steps (overflow problem) 05-Mar-03 NEW: include Tomas' new forest creation routines 02-Mar-03 FIX: added some missing entries to german translation CHANGE: slightly risen productivity of gas stations 26-Feb-03 CHANGE: finance window now formats money as 1.000,00 FIX: negative total balance is now displayed in red 23-Feb-03 NEW: number of transported passengers now influences city growth (see +X in city info window. +1 means doubled growth, +2 means tripled growth rate. Will always be 0 after loading a new game and midnights. Needs a while to be recalculated.) NEW: new industry branch 'gasoline production' NEW: included Alberto Beccarias updated italian translation CHANGE: reenabled display/brightness control. Range is now -8 ... 0 0 being the brightest setting. 0 is default value. CHANGE: slightly risen the average distance of industries CHANGE: made church a special building released unofficial 0.81.23exp 22-Feb-03 NEW: tested -nosound and -nomidi switches on windows 20-Feb-03 NEW: -nosound and -nomidi switches to turn off sound and music 19-Feb-03 FIX: fixed a bug in building level-crossings on non-empty squares NEW: Materialwholesale now also accepts planks and steel NEW: Home market now also accepts planks 17-Feb-03 FIX: fixed a bug in help text viewer that caused a crash if more than two tags were included in the help text FIX: included Alberto Beccaria as italian translator into the intro scroller FIX: fixed a bug in image zooming if going back to zoom factor 1 CHANGE: included new help texts by Adam Barclay ??? Stopped development of zooming feature. Need to wait for Volkers new MakeObj with more flags for images (unzoomable images -> currently icons and other menu-images get zoomed, too). Very simple (fast) zoom routine is working now, zoom quality is bad. 12-Feb-03 NEW: continued to work on zooming feature 11-Feb-03 NEW: continued to work on zooming feature 10-Feb-03 NEW: started to work on zooming feature (use > and < keys to zoom in and out) 09-Feb-03 NEW: added legend to relief map window CHANGE: included Tomas Kubes' new english city name syllables in en.tab CHANGE: performance tuning - changed route_t and vector_tpl index parameters to unsigned, saving one if conditional on each access released unofficial 0.81.22exp 08-Feb-03 NEW: player starting money can now be configured in simuconf.tab NEW: factories now require different amounts of input to produce goods. I.e. to produce one ton of iron 2 tons of iron ore and 0.5 tons or coal are required CHANGE: included Tomas Kubes' new darkening (night mode) code FIX: fixed a wrong gridded grass tile that appeared to be invisible FIX: fixed landscape pointer position calculation in 128x128 tile mode FIX: fixed refresh area for landscape pointer movement in 128x128 tile mode FIX: level crossing only can be build on empty squares (this prevents a crash if you built them while a vehicle was passing this square) FIX: fixed input overflow problem in factories 06-Feb-03 CHANGE: more performance tuning - optimized dingliste bei(n) now takes an unsigned parameter, saving one if conditional - changed array_tpl and array2d_tpl parameters to to unsigned, saving one (two) if conditionals on each element access 05-Feb-03 CHANGE: more performance tuning - inlined ist_bruecke und ist_tunnel, added flags to grund_t - changed minivec get() parameter to unsigned, this saves one if condition - removed one if from karte_vollansicht_t::display_dinge - removed one if from grund_t::display_dinge - removed one if condition and one assignment from karte_t::sync_step - inlined grund_t::gib_weg, and optimized the routine slightly 04-Feb-03 CHANGE: tried to reduce the memory consumption - reduced by 8 bytes per ground. CHANGE: tried to optimize pedestrian routines CHANGE: profiling and overall performance tuning 01-Feb-03 FIX: mail office now has player colors FIX: overhead lines adapt their shape if tracks are removed or added FIX: fixed world/map scrolling in bigtile (128) mode FIX: fixed bridge icons for night mode with lights FIX: scrolling with numeric keypad works again FIX: Stadium 2, Layout 1 fixed (created invisible stadiums before) NEW: ESC closes all windows (BACKSPACE and DELETE do that too) 28-Jan-03 NEW: there is now a toggleable ground tile grid. Press # to switch the grid on/off. 27-Jan-03 NEW: finished buttons for the factory info window to jump to consumers, also added buttons to jump to the workers home towns 27-Jan-03 NEW: started to add buttons to the factory info window to jump to consumers conveniently 26-Jan-03 NEW: started to work on lights for the night mode released unofficial 0.81.20exp 25-Jan-03 FIX: road tunnels again get correct image after loading (was broken during migration to new pak files) FIX: private cars are no longer visible while passing a tunnel FIX: it is not longer possible to build rail stations twice on the same square if overhead lines are present CHANGE: saved 4 bytes of memory per convoy CHANGE: included improved paper mill from Tomas Kubes released 0.81.19exp 23-Jan-03 FIX: overhead lines on bridges are now (again) loaded correctly from saved games FIX: signals on bridges are now loaded correctly from saved games CHANGE: city building tool now included in toolbar FIX: city building cursor now has proper size FIX: removing roads and railroad track now costs 100 Cr 22-Jan-03 FIX: Again included this fix, it had somehow been lost: 07/13/02 - FIX: fixed a bug that caused an endless loop once a private car had gone more than 2**31 steps FIX: fixed overhead line images for diagonal track NEW: started work on line (route) management 20-Jan-03 CHANGE/FIX: also doubled running cost for 'Bennhardt' engines (had been overlooked yesterday) NEW: added Colins hong kong ferries NEW: added Bens industrial city buildings 19-Jan-03 FIX: fixed dat file entry for "large office building (by Trikky)" FIX: sand truck can now be used without trailer CHANGE: changed max map size from 576 to 640 (requires ~60MB ram and a whole lot CPU time plus memory bandwidth!) CHANGE: changed "Passenger %" for monuments from 150 to 60 CHANGE: enhanced capacity of all busses by about 5% CHANGE: changed capacity of mail wagon from 60 to 120 bags CHANGE: changed capacity of mail boat from 140 to 180 bags CHANGE: changed capacity of mail trucks to 50 and 60 bags CHANGE: doubled running costs of all vehicles. Lowered running costs for electric engines slightly. CHANGE: rose starting money to 150000 Cr CHANGE: newly generated passengers will not use stations where more than 64 passengers per square are already waiting Note: workers nevertheless use crowded stations! CHANGE: reduced passenger generation ratio from (building_level+2) to (building_level+4)/2 -> that is roughly the same as before for low-level buildings, but only have of the former rate for high-level buildings released 0.81.18exp 18-Jan-03 CHANGE: added updated italian translation from Alberto Beccaria released 0.81.17exp CHANGE: changed grounds to use memory pools (should save about 256K main memory for a 256x256 map, and also be faster) FIX: Ships can now be sent to depots again FIX: fixed a bug in restoring overhead line images when loading games that had been saved under a different OS or with a different set of PAK files - this also saves 4 bytes per saved overhead line tile 17-Jan-03 FIX: made some bugfixes for new bigtile mode FIX: fixed a bug in overhead line y-offset calculation that made overhead tiles on bridge ends move higher and higher if the overhead lines were re-built (again) NEW: added automatic tile size detection (PAK files reader) -> requires at least one full width image in tile set NEW: linked lists now allocate nodes from a pool. Slightly faster than before and conserves a little bit of memory CHANGE: changed toolbar window to work with 128 and 64 pak files CHANGE: additional station segments only cost half of a new segment CHANGE: new station segments now cost 2400Cr 16-Jan-03 TEST: made some tests with 128x128 tiles FIX: fixed a bug in MakeObj/PNG file reading FIX: fixed a bug in MakeObj image writing 15-Jan-03 NEW: preparation for configurable tile size 14-Jan-03 NEW: tried to implement a more efficient management for linked list nodes, try to reuse as many nodes as possible 12-Jan-03 FIX: fixed rail bridge images which seem to have been damaged during the conversion to the new multi-pak file approach. FIX: fixed a basement image which seems to have been damaged during the conversion to the new multi-pak file approach. 08-Jan-03 FIX: fixed a bug that sometimes move the city area out of map bounds if a town hall was renewed FIX: added correction code to read games with towns that suffer from the above mentioned problem 05-Jan-03 FIX: player cannot rename AI stations anymore FIX: VT95 and VT98 can be run as single cars again FIX: TPI F7A can be run on non-electrified track again FIX: hotkey 'e' used wrong landscape pointer image FIX: fixed VT95 images which seem to have been damaged during the conversion to the new multi-pak file approach. CHANGE: schedule window now displays destination coordinate (Schedules are platform-precise!) 04-Jan-03 CHANGE: bus stops now can be placed even if there are pedestrians there NEW: added monument inscriptions to en.tab FIX: fixed plastic wagon images (player color) which seem to have been damaged during the conversion to the new multi-pak file approach. FIX: overhead powerlines on bridge ramps on even ground have correct height offsets now FIX: overhead powerlines in tunnels are no longer visible FIX: removed extraneous second 'dock' button from ship tools menu 03-Jan-03 CHANGE: tried to reduce jitter in vehicle movement CHANGE: tried to optimize the updates of trees, buildings etc. overall this should help to run larger games, but I don't know how much improvement it will bring. FIX: fixed steel truck and steel trailer images which seem to have been damaged during the conversion to the new multi-pak file approach. 01-Jan-03 FIX: stations can now be built on electrified track, too FIX: Bennhardt BR140 now only runs on electrified track FIX: fixed a bug in AI code that tried to use electric engines on non-electrified track FIX: added missing STADIUM1 entry to en.tab and de.tab FIX: fixed mixed lines for 'Lade Spiel' in de.tab 22-Dec-02 CHANGE: lowered passenger % of townhalls. Now: 75, 75*4, 75*4, 75*4 CHANGE: added checks for two potential crashes in searching for locations of monuments and town halls CHANGE: repaired images for paper wagon, paper truck, paper trailer which seem to have been damaged during the conversion to the new multi-pak file approach. FIX: added missing wav files (lost since conversion ...) released unofficial 0.81.12exp 21-Dec-02 CHANGE: more work for overhead powerlines 20-Dec-02 CHANGE: more work for overhead powerlines 15-Dec-02 CHANGE: more work for overhead powerlines 12-Dec-02 NEW: started work on overhead powerlines for electrified track 11-Dec-02 NEW: non-station buildings can be hidden from display by pressing " (Shift-2 on a german keyboard). This is useful if large buildings block sight to something important. NEW: started to work on electrified, monorail and maglev track types electrified tracks work already, yet they still look the same as ordinary tracks 07-Dec-02 CHANGE: changed capacities: Furniture wagon: 20 crates Furniture truck: 16 crates Furniture trailer: 18 crates NEW: Option for semaphore signals released unofficial 0.81.11exp ??? released 0.81.10exp 30-Nov-02 CHANGE: improved/reworked H-Trans coal truck image FIX: fixed .dat file for furniture wagon 17-Nov-02 FIX: fixed some bugs in routing of goods (broken during changes for categorized goods) BUG: Station list filter for incoming/outgoing goods might work differently than before. I never used it, so my change is just a guess released unofficial 0.81.9exp 16-Nov-02 NEW: enlarged schedule window CHANGE: goods are now grouped into unique goods, parcel service and bulk goods. Vehicles that transport goods of the categories parcel and bulk goods can now transport _any_ good of those categories. I.e. a coal car can now also be used to transport iron ore. Parcel service: Books, Furniture Bulk goods: Sand, Stone, Coal, Iron Ore Due to this change the savegame format became incompatible. Sorry, no conversion is possible, even some of the pak files now have a different structure. CHANGE: enlarged text buffer for vehicle information window to reduce the danger of buffer overruns 26-Oct-02 FIX: After schedule changes, no-longer existing routes are checked and waiting goods are cleaned up. This should fix the long-standing bug that passengers and goods waited endlessly after a route was shut down and/or replaced by another route. This cleanup action is pretty time consuming. In games with many stations and vehicles, running on slow computers this will take several seconds to complete. released unofficial 0.81.8exp 20-Oct-02 FIX: Oil rigs now accept passengers and mail (again, was broken after 0.81.0) released unofficial 0.81.7exp 19-Oct-02 FIX: fixed a bug in loading saved games (height information was accessed before actually loaded) FIX: fixed a bug in AI bridge testing routines (AI tried to build bridges in unsuitable locations and ended up with broken routes) 12-Oct-02 FIX: random pedestrians can now actually be turned on and off in simuconf.tab FIX: fixed a bug which caused convois not being saved after visiting a depot without disassembling the convoi FIX: maintenance costs are now displayed in red CHANGE: relief map is now zoomable: right click into map to toggle zoom/unzoomed display released unofficial 0.81.6exp 06-Oct-02 CHANGE: tried to make relief map zoomable 06-Oct-02 FIX: updated/fixed more images/animations FIX: tried to fix the problem that some vehicles were not immediately visible after loading. FIX: fixed the problem of vehicles stored in depots being invisible after loading. released unofficial 0.81.5exp 05-Oct-02 FIX: updated/fixed more images FIX: italian translation is available again FIX: added Bennhardt train engines again CHANGE: message windows now appear above the mouse pointer and don't obstruct the players actions anymore. CHANGE: depot windows open in the top-left corner of the screen now. 04-Oct-02 FIX: fixed many mistakes in new config files and image set FIX: maintenance costs are now displayed correctly in finance window FIX: fixed a translation mistake in en.tab 03-Oct-02 FIX: fixed a bug in tree creation that was introduced with the new configuration system. FIX: fixed a bug in loading tunnel descriptions 20-Sep-02 CHANGE: Starting money again set to 100.000Cr CHANGE: made info windows for pedestrians and private cars configurable in simuconf.tab 29-Sep-02 CHANGE: changed passenger generation in cities (steps more frequently, but only every 8th row) 28-Sep-02 CHANGE: continued code cleanup -> all 'builder' classes now located in 'bauer' -> all 'searcher' classes now located in 'sucher' 26-Sep-02 CHANGE: continued code cleanup 25-Sep-02 MERGE: included Volkers changes to the config/object system CHANGE: started to move factory classes into a separate package CHANGE: did some code cleanup in simgraph16.c 22-Sep-02 NEW: station detail info now displays factory coordinates, too NEW: water (ground) animation can now be turned off in simuconf.tab (i.e. if you have a slow CPU and want to save some computation time for other tasks) They serve some useful purpose, though: the more you see around a spot, the better the place to build a station there. NEW: player/AI colors can now be configured by editing the file "special.pal" which is a text file containing 64 RGB colors in decimal values FIX: loading pedestrians works now FIX: fixed some problems in day&night mode: 1) vehicles/player properties darken now during nights 2) window title bars don't darken during nights CLEAN: did some more code cleanup in simgraph16.c CHANGE: interrupt routine now updates display first and then calculates next frame - i hope this will reduce the jitter in vehicle movement CHANGE: changed sync_step routine to be (hopefully) faster and more robust against the most common coding mistakes (deleting/removing a sync object while a sync step is running). CHANGE: tombstone arrays are now configurable in simuconf.tab 21-Sep-02 NEW: schedules are no longer lost if a vehicle enters a depot 20-Sep-02 FIX: repaired broken AI road/railroad building routines TIME: 2:00h 19-Sep-02 MERGE: continued merge of sources NEW: station detail info now includes connected industries CLEAN: cleanup in simgraph16.h and simgraph16.c TIME: 1:30h 18-Sep-02 MERGE: merged Volkers changes for new config system TIME: 2:00h 05-Sep-02 NEW: included italian translation from Alberto Beccaria NEW: built preview for BeOS CHANGE: updated thanks.txt TIME: 1:30h 02-Sep-02 CHANGE: changed layout of the ship depot. It looks a bit better than before, but some ships are just too oversized - why don't the artists follow the size guidelines? CHANGE: some trailers now fit to all trucks CHANGE: changed price, weight and running cost of the double decker busses FIX: fixed alignment of some ships TIME: 2:00h 01-Sep-02 FIX: bus stops are recolorable again FIX: changed and added some entries to de.tab FIX: changed and added some entries to en.tab FIX: included hu.tab fix from MrTLee CHANGE: tried to improve bus stop images CHANGE: changed sand wagon to be recolorable CHANGE: changed stone wagon to be recolorable TIME: 2:30h 31-Aug-02 CHANGE: raised starting money to 100000 Cr CHANGE: if a help text can't be found, the help text frame error now includes the filename of the missing file CHANGE: drive in cinema is now built at 600 inhabitants CHANGE: lowered productivity of coal mines and iron ore mines by 10% CHANGE: all of Bennhardts vehicles have reasonable prices and running costs TIME: 1:30h 30-Aug-02 CHANGE: changed tombstone implementation for stations to a more efficient variant CHANGE: removed brightness and contrast control from the display options dialog because they didn't work anymore since the transition to the 16 bit graphics code and those features most likely won't be implemented again. CHANGE: declared savegames previous to 0.80.10 incompatible because some vehicles couldn't be restored from such savegames properly. FIX: oil rigs don't raise the players maintenance costs anymore NEW: added missing stadium images TIME: 1:00h 29-Aug-02 CHANGE: included updated translations CHANGE: changed prices for "Bennhardt" train engines CHANGE: changed tombstone implementation for convois and railblocks to a more efficient variant FIX: fixed a crash if the vehicle list was left open and displaying a vehicle that enters a depot FIX: fixed stadium images for layout A FIX: fixed non-matching entries for Tigress I in language files and vehikel.tab, also removed an unused double entry for the Tigress I from vehikel.tab TIME: 3:00h 07/20/02 - FIX: fixed 2 wrong entries in gebaeude.tab - FIX: included Dennis' correction for en.tab TIME: 1:00h 07/14/02 - FIX: sped up buying vehicles in depots TIME: 1:00h 07/13/02 - FIX: fixed a bug that caused an endless loop once a private car had gone more than 2**31 steps TIME: 1:00h 07/11/02 - FIX: factory descriptions are restored after loading. This fixes a crash after loading saved games in 0.80.10a - FIX: (hopefully) oil rigs now accept passengers TIME: 1:00h 07/08/02 - FIX: heightfield loading now loads other files than "heightfield.ppm", too - FIX: confirming the heightfield file selector box with an invalid name no longer crashes the game. - FIX: better error messages for heightfield loading, errors no longer quit the program. - FIX: colors of the factories in the map window are now again the color configured in industry.tab TIME: 1:00h 07/07/02 - NEW: included 8 new train images painted by Benjamin "Bennhardt" Siegel - NEW: included the alignment-corrected trucks from Jens "JET" Emmel - NEW: included a new ship from Jens "JET" Emmel - FIX: buying a trailer or wagon without having an engine no longer crashes the game - KNOWN PROBLEMS: - ship depot display is messed up pretty much - all of Bennhardts engines have same price and running cost, sensible values will be discussed on the mailing list and included - released unofficial 0.80.10-16exp TIME: 2:00h 07/06/02 - NEW: finished heightfield loading - NEW: included the improved trees from Tomas Kubes (sorry Tomas I had to modify them slightly). - CHANGE: changed the toolbar icons TIME: 4:00h 07/05/02 - NEW: option to load heightfield (a PPM image where the brightness of a pixel determines the height for that spot on the map) TIME: 1:30h 07/01/02 - FIX: fixed a bug in Markus' patch for the depot window - when buying a new vehicle the cost of a lot of different vehicles was also withdrawn from the players account. - released unofficial 0.80.9a-16exp 06/30/02 - FIX: maintenance cost is now actually subtracted from players account every month. - FIX: fixed a wrong entry in gebaeude.tab - MERGE: included Owen Rudges Win32 midi playing routines - released unofficial 0.80.9-16exp 06/29/02 - NEW: maintenance costs for roads, railroads and buildings: - road/railroad: 5.00 Cr/month - each building: 50.00 Cr/month (There is a small bug here: ATM oil rigs belong to the player, and thus the player has to pay maintenance costs for them). - MERGE: merged Markus patch to display only matching carriages in the depot window TIME: 1:30h 06/23/02 - NEW: List of all schedules (also saved 4 bytes memory per schedule) - FIX: savegame frame shows local time instead of GMT - CHANGE: map frame now has a minimum sizeof 96x64 pixels TIME: 0:30h 06/15/02 - CHANGE: sped up graphics by another 2% - released unofficial 0.80.8-16exp TIME: 3:00h 06/14/02 - FIX: industries have several consumers again (Volker) - FIX: building a loading bay on a cities road sets the roads owner correctly now. - NEW: tunnels and bridges are displayed in the map (Volker) TIME: 1:00h 06/13/02 - FIX: window resizing bugs fixed TIME: 1:00h 06/09/02 - MERGE: merged Markus changes - station list now includes icons - MERGE: merged Volkers changes - internal changes to train and truck stations to streamline the class hierarchy - more and more strict tests for building roads/railroads - new industry distribution. Can consider weights, given by industry.tab, to prefer (or not) certain industry branches. - existing screenshots are preserved, new screenshots always get high enough numbers. - changed load/save dialog * Buttons for deleting files (take care!) * Displays save date and time (Just a proposal - test it, and let us know if you like it or not) - schedules: depots listed as depot and no longer as waypoint - TEST: tested Owens win32 media routines, but couldn't get them to work with my compiler - excluded in this release. - CHANGE: the Linux version now tries to open 640x480, 800x600 1024x768 and 1280x1024 in fullscreen. Use the -screensize option to force a windowed mode by using a different resolution. - released unofficial 0.80.7-16exp TIME: 4:00h 06/08/02 - NEW: map window remembers size and uses old size when open again - NEW: painted animated diesel smoke and steam (only used with vehicles currently) - CHANGE: smoke type is now read from current vehikel.tab instead from saved game. - CHANGE: included Volkers changes to allow processing an unlimited number of images with makepak16 TIME: 1:00h 06/07/02 - CHANGE: frame sync smoke is now a 5 frame animation TIME: 0:30h 06/05/02 - CHANGE: changed income calculation for transports to: distance * amount * price * (pow(0.97, distance)+0.4)/2.0 this is nearly linear for long distances and a bit better than linear for short distances. - CHANGE: vehicles only produce smoke while heavily accelerating TIME: 1:00h 06/02/02 - FIX: clicking a post office opens the station window again - FIX: fixed a memory leak during map destruction. TIME: 2:30h 06/01/02 - MERGE: merged Markus' changes for resizeable windows - CHANGE: adapted Markus' changes to fit better into Simutrans TIME: 1:30h 05/30/02 - NEW: help texts for relief map and finance window added - NEW: industry colors are now read from industry.tab - NEW: support for hyperlinks in help texts - CHANGE: better error messages for wrong entries in industry.tab TIME: 3:00h 05/29/02 - FIX: fixed "wait for xxx%" entry in en.tab so that the % number is always clearly visible in the schedule dialog - NEW: preparation for factory color configuration in industry.tab TIME: 1:00h 05/25/02 - CHANGE: restructured system dependant code - preparation to build system dependant media subsystems. Started to include Owen Rudges native win32 MIDI playing code. TIME: 1:00h 05/19/02 - FIX: Player color (object recoloring) feature works again. - CHANGE: removed colormap functions from HiColor system wrappers - CHANGE: buildings basements now use mempools - NEW: (Hellmade) included screenshot facility for 16 bit graphics engine - MERGE: Merged Volkers changes (Filter-Dialogs, many small patches) - released unofficial 0.80.6-16exp TIME: 3:00h 05/18/02 - FIX: (Hellmade) Double headed trains will be correctly disassembled in depots - FIX: (Hellmade) Buying more than 16 wagons for a train moves the additional wagons to the pool section TIME: 3:30h 05/09/02 - NEW: merged Volkers changes - FIX: fixed a clipping bug for coloured images in the 16 bit graphics engine. - CHANGE: info windows for buildings now carry title "Building" again. TIME: 1:30h 04/20/02 - NEW: integration of Flors new image set - FIX: fixed some display problems of the toolbar - FIX: building and removing tunnels costs some money now - FIX: building and removing bridges costs some money now - FIX: city building tool places townhall on the right spot now - FIX: industries productivity now varies (the range parameter from industry.tab is no longer ignored) - CHANGE: cementmill now requires stone - CHANGE: productivity/range entries in industry.tab updated - CHANGE: changed night to a more blueish color released unofficial 0.80.4-16exp TIME: 2:30h 04/18/02 - FIX: fixed a buffer overflow in simgraph16.c (dirty tiles list) - NEW: extended error checking capabilities for memory management -> can now detect overflows TIME: 1:30h 04/17/02 - FIX: F1 key now triggers general help in 16 bit version, too - NEW: internationalizeable help texts TIME: 0:45h 04/13/02 - CHANGE: "random map" buttons now generates a real random sequence of maps - FIX: tried to fix a bug that sometimes prevented the AI from building tunnels (the AI ended up with a broken route) - FIX: fixed a crash if a highest level building was build (crash only occurred in 0.80.2-16) TIME: 1:00h 04/11/02 - FIX: fixed a bug in player-triggered city building TIME: 0:30h 04/10/02 - NEW: player can found new cities - hotkey 'C' - CHANGE: hotkey for vehicle list changed from 'C' to 'V' TIME: 0:30h 04/07/02 - CHANGE: converted dirty tile list to bitfield. This saves some KB of memory. - FIX: fixed a potential access to an uninitialized route element - FIX: tried to fix the road vehicle lockup problem on level crossings TIME: 1:00h 04/06/02 - FIX: fixed some untranslated messages in bridge building - FIX: fixed some wrong entries in en.tab and de.tab - CHANGE: updated thanks.txt file TIME: 0:30h 04/04/02 - CHANGE: changed night darkness to be less dark - FIX: fixed a crash if land was raised from under a ship - FIX: fixed a crash if a stop outside the map was added or inserted into a schedule TIME: 0:30h 04/03/02 - FIX: fixed a bug in the 16 bit graphics code that caused a crash when a player-colored image with an invalid number was displayed. TIME: 0:30h 03/28/02 - CHANGE: merged Volkers 2nd update to house building - Release unofficial 0.80.2_16 TIME: 1:30h 03/27/02 - CHANGE: 16 bit graphics routines about 40% faster than before - FIX: fixed some typos in help texts TIME: 1:30h 03/26/02 - CHANGE: exchanged . and , keys for time lapse control - CHANGE: tried to optimize 16 bit graphics engine TIME: 1:30h 03/24/02 - FIX: pressing shift doesn't trigger keyboard help anymore - NEW: merged Volkers changes to house building - NEW: merged Markus changes to station list window TIME: 0:30h 03/09/02 - FIX: raising/lowering land at the border of the map no longer crashes. TIME: 0:30h 02/23/02 - NEW: included help texts for loading and saving games. TIME: 0:30h 02/22/02 - FIX: fixed a crash if a railroad track or road was build outside the map. TIME: 0:30h 02/17/02 - FIX: fixed a crash if a railroad track was built at the very edge of the map - CHANGE: news ticker is only displayed if there are news TIME: 0:30h 02/16/02 - NEW: added more help texts: language, options - FIX: fixed a bug in the flowtext display component TIME: 1:00h 02/15/02 - FIX: fixed the 'cloud offset overflow' problem - NEW: added more help texts: depot help TIME: 1:00h 02/13/02 - NEW: added more help texts TIME: 1:00h 02/11/02 - NEW: working on in-game helpsystem, added more help texts TIME: 1:00h 02/10/02 - NEW: working on in-game helpsystem, added more help texts TIME: 1:00h 02/09/02 - NEW: working on in-game help system TIME: 1:30h 02/07/02 - NEW: started working on in-game help system TIME: 1:30h 02/06/02 - CHANGE: made some changes for Markus Weber TIME: 0:30h 01/28/02 - FIX: Fixed a crash when a vehicle was sold. This bug was introduced during yesterdays changes to convoi routing. - FIX: Fixed a crash if vehicle.tab did not contain a vehicle that was required by depot.tab TIME: 1:00h 01/27/02 - CHANGE: Made convoi route preparation also an asynchronous task. - CHANGE: Because asynchronous tasks take longer to execute, vehicles stop longer at stations than before. To rebalance the game, the productivity of all industries was reduced by 10%. - CHANGE: Due to the new asynchronous parts in convoi setup vehicles may remain much longer in a inconsistent state. That problem is solved now. - NEW: released unofficial version 0.80.pre3 TIME: 2:00h 01/26/02 - CHANGE: Increased safety buffer in convoi info display. - CHANGE: Increased safety buffer in station info display. - CHANGE: Made convoi setup an asynchronous task. - CHANGE: Halved minimum acceleration. - CHANGE: Reduced uphill speed. - CHANGE: Reduced speed in curves. - CHANGE: Fiddled with frame time calculation to get smoother vehicle movement. TIME: 3:30h 01/24/02 - FIX: Fixed some bugs in asynchronous vehicle loading code. Overall asynchronous loading seems to allow to use quite some more vehicles than before. - FIX: Improved responsiveness to user input under high load. TIME: 2:00h 01/23/02 - FIX: Debugging - CHANGE: Made vehicle loading an asynchronous task. - CHANGE: Passengers and goods transfer at most 30 times from one transport to another to reach their destination. TIME: 2:00h 01/20/02 - CHANGE: Saved 8k memory per convoi. - CHANGE: Saved 8k memory per station. - CHANGE: Included a change from Markus Weber to display summary information in station info windows. - CHANGE: Changed layout of vehicle list to display vehicle with 3 figures (100+) better. - CHANGE: Using memory pools for water grounds - this saves 4 bytes per water square and speeds up allocation and deallocation of water grounds. It also helps to reduce memory fragmentation. - FIX: Fixed a possible buffer overflow in halt list. - FIX: Included a fix for setting the correct water level during loading a game. - NEW: released unofficial version 0.80.pre2 TIME: 3:00h 01/19/02 - FIX: Fixed a bug in clipped 16 bit image display routines - FIX: Fixed a bug in SDL sound routines that caused a crash in the windows version - FIX: Fixed a possible buffer overflow in route calculation of goods and passengers. - NEW: Fullscreen mode for Linux/SDL in 640x480, 800x600, 1024x768 and 1280x1024 (The Windows version always offered those as fullscreen modes) TIME: 3:00h 01/17/02 - NEW: RGB555 <-> RGB565 transform for 16 bit engine finished - NEW: optimized 16 bit engine TIME: 3:00h 01/16/02 - FIX: Fixed a bug that crashed Simutrans if the player managed to delete/destroy a private car. - NEW: Makepak16 reads 8/16/24 bit PNG files now. TIME: 1:00h 01/13/02 - MERGE: Andreas Rövers sound routines for SDL - TEST: Tested Simutrans with SDL 1.2 - NEW: updated 16 bit graphics routines (not complete yet) TIME: 4:00h 01/12/02 - NEW: -scenario option to make Simutrans read config files and daten.pak from that directory. TIME: 3:00h 01/09/02 - MERGE: included Markus' resizer component and the changes to the vehicle list -> Station list window and vehicle list window can be vertically resized now - CHANGE: changed look of resizer component to better fit the other gui components of Simutrans - NEW: released unofficial version 0.80.pre1 TIME: 1:00h 01/08/02 - FIX: automated test suite works again (was broken since Volkers changes during summer/autumn last year). - FIX: cities generate passengers to 2x2 squares sized tourist attractions, too. - CHANGE: using memory pools for trees - this saves 4 bytes per tree and speeds up allocation and deallocation of tress. It also helps to reduce memory fragmentation. - CHANGE: did some finetuning to make the framerate more constant - MERGE: include Markus' new station list window and button TIME: 5:00h 01/02/02 - MERGE: merged Volkers changes - Shift-M opens marker UI - Removing a bus stop from a square with a sidewalk keeps the sidewalk - Bugfixes for bridges and tunnels - Code cleanups - CHANGE: changed layout of the "options" dialog - CHANGE: changed layout of the "new world" dialog TIME: 2:30h 12/28/01 - CHANGE: Reverted some changes made by Volker: - city roads belong to nobody again - a square that the player builds a road or railroad on and that had no former owner belongs to the player again -> That fixes the 'bus station on city roads' problem - FIX: Included a bugfix from Volker to make bridges work again. It seems to work. - FIX: Added missing screen refresh after removing a long bridge TIME: 1:30h 12/20/01 - FIX: Trains can now be stopped while passing signals without bad effects, like corrupted signals and disrupted trains. - CHANGE: Reduced productivity of coal mines and oil fields. - Released unofficial version 0.79.11exp TIME: 2:00h 12/19/01 - CHANGE: Removed 'passenger X%' from industry info window - MERGE: Merged Volkers changes -> Bridges no longer need 'hills' to start/end but can be built on flat ground, too. - FIX: Goods with "error in routing", that means they cannot reach their destination any longer, are now removed from stations after a while. They are also removed from vehicles upon the next scheduled stop. - FIX: Fixed a bug in the AI code that sometimes prohibited the use of tunnels in the AIs routes - FIX: Partly loaded vehicles display the loaded/empty cars now. - NEW: Option in the schedule dialog to set a required loading grade. Vehicles can now wait for a minimum amount of loaded goods before departing. TIME: 6:30h 12/15/01 - MERGE: included Markus changes to world creation UI and convoi UI - CHANGE: Changed layout and text of the full load option in convoi info window. TIME: 1:10h 12/15/01 - CHANGE: prepared Makepak 1.07 for release TIME: 0:30h 12/12/01 - FIX: I got two savegames from beta testers that had an animation count of 0 for some factory buildings. That count should never be 0, even a non-animated building has a least one frame (image) to display. During loading of a game the count gets now set to 1, but how could there a 0 be saved? The 0 caused a crash after loading the game, so this 'fix' at least allows to load the saved games again. - FIX: fixed a crash after clicking the bridgebuilding tool twice - FIX: fixed a small display problem in the news ticker line - CHANGE: Changed lower bound of frame time to 28ms, this yields a frame rate of 35FPS which should be smooth display. - Released unofficial version 0.79.9exp TIME: 1:30h 12/09/01 - FIX: Passenger generation in cities is now bound to real time. I had wrongly assumed that the simulation loop is running always at the same speed but it varies too much. Now, passenger generation tends to be lower in the average case but it is constant regardless of your computers speed. - FIX: Volkers changes prevented the AI to build tunnels and bridges. I've repaired the AI's road/railroad building routines. I hope it works all ok again. - FIX: Newly built stations display world view camera, too. - CHANGE: Changed layout in station info and convoi info windows. - CHANGE: Optimized month change in city class. - CHANGE: Optimized routing of goods. - CHANGE: Optimized routing of vehicles. - CHANGE: Tried to reduce the jitter in frame rate. Frame time has now a lower bound of 30ms and a higher bound of 124ms. Interrupt checks were added to ensure a jitter of less than 5 ms in frame display; I've tested this on my system, slower systems might still have a larger jitter. (Timer precision is 5 ms, that means, the current jitter is below timer precision, more precision is impossible with this timer). - CHANGE: Waiting goods and passengers at station search a new route now and then. This is useful if a new shorter connection is build and the waiting goods/passengers are supposed to use it. - Released unofficial 0.79.8exp TIME: 7:00h 12/08/01 - CHANGE: Included Volkers changes - CHANGE: Signals can now be build near crossings - FIX: A few fixes to bridgebuilding - FIX: Unmasked ribis for ways were determined wrong - CHANGE: Tried to optimize Simutrans further but gained less than 1% overall - CHANGE: Changed news ticker to store only 4 messages instead of 10 - CHANGE: Increased capacity of furniture truck to 8 crates Increased capacity of furniture trailer to 10 crates - NEW: Included Jens 'JET wood trailer' - FIX: Changed some code in station info display to fix a buffer overflow. - FIX: Changed some code in vehicle info display to fix a buffer overflow. TIME: 2:00h 12/05/01 - CHANGE: changed some return types from value types to references. This seems to speed up execution. TIME: 1:00h 12/01/01 - FIX: oil rigs can no longer be removed using the 'raise land' tool. - FIX: oil rigs display a station info window again. - FIX: Fixed a bug that caused some landscape pointers to be displayed at wrong height - FIX: Fixed a bug that caused landscape pointers to be displayed twice - NEW: 'Game paused' splash message - NEW: Included Flor Wauters furniture trailer - CHANGE: reduced memory consumption of each water square by 4 bytes. - CHANGE: optimized screen refresh, gained about 10% speedup if many vehicles are visible TIME: 5:00h 11/29/01 - FIX: if the remover tool is applied to water, the water itself no longer gets removed - CHANGE: changed tree class to reduce the amount of memory modified per step TIME: 1:30h 11/28/01 - CHANGE: made mountain height and landscape roughness parametrizeable for Markus' new 'new world' dialog TIME: 0:45h 11/26/01 - FIX: fixed a crash if the bus station building tool was selected - FIX: included a fix from Volker: if a signals was to near to a crossing the rail block was not correctly traversed. Volkers change should fix that problem. - FIX: fixed a potential crash during map creation (placing power- connector for power plant sometimes failed. This is not fixed really, problematic power plants will just get no power connector. I'll change that once powerlines actually work ) - CHANGE: "Vehicle XY has no route" is only displayed for vehicles which the player actually owns. - CHANGE: Factory info window now carries the name of the industry as title. - PROBLEM: with the change from static to animated buildings the title of most building info windows changed from 'building' to 'house'. - Released unofficial 0.79.7exp TIME: 1:30h 11/25/01 - CHANGE: only wagons inside of a stations will be loaded/unloaded - CHANGE: loading/unloading has a base duration (32) and an additional duration for each wagon (8) - CHANGE: internal change: replaced many old 2d coordinates in paramterlists with 3d coordinates. - CHANGE: Added "Station XY is crowded" message to the newsticker - CHANGE: Factories don't check stations for good distribution as often. This should speed up the program. - FIX: rail block is marked 'left' if a train enters a depot - FIX: added missing screen refresh when saving a game per name - FIX: fixed a bug in blockmanager, which could cause and endless loop during world destruction - FIX: fixed several bugs in handle_as_id_tpl which caused crashes - FIX: fixed a crash when a vehicle with freight was sent to a depot and re-assigned to another convoi. - Released unofficial 0.79.6exp TIME: 8:00h 11/24/01 - NEW: minivec template, a very small vector type. - CHANGE: Reworked plan squares to use minivec type instead of a list type to store grounds. This saves at least 512K memory for a standard 256x256 squares map. In addition accesses should be faster now. - CHANGE: Removed an unused variable from ground class. It seems it was introduced by Volkers changes but actually not used. This saves 512K for a standard 256x256 square map. - CHANGE: Reordered variables in ground class, which saved another 4 bytes per ground. Why is the compiler too stupid to do this automatically? - CHANGE: Squeezed 4 bytes from every object of the thing class hierarchy (most numerous objects in Simutrans). This also shrinks the saved games. - CHANGE: Linked list templates now use a global list of free nodes. This shrinks each list object by 4 bytes, and it speeds up node allocation. - CHANGE: Depot window class and depot data class are now separated. - CHANGE: Code cleanup, reduced interdependencies of header files - CHANGE: Improved look of info windows - NEW: experimental support for multi-headed engines TIME: 6:15h 11/22/01 - CHANGE: Removed superclass infowin_t from weg_t. It was not needed and just consumed memory. - CHANGE: Removed superclass infowin_t from ding_t. It was not needed and just consumed memory. - NEW: Thing info window adapter class - CHANGE: Started to separate depot gui and depot model classes TIME: 2:10h 11/20/01 - NEW: Ground info window adapter class - CHANGE: Changed class hierarchy of ground classes to be independent from info window class -> this allows inlining of some functions and thus speeds up some things (3% speedup overall). - FIX: Info windows display correct ground name again, this feature was lost during Volkers changes. - FIX: Fixed a bug that crashed Simutrans if a ground was removed while the info window for this ground was still open. This also fixes a similar bug for things that are removed. TIME: 1:10h 11/18/01 - NEW: New engine: Tigress I (1144-1151) - CHANGE: Code cleanup - CHANGE: Trees are now generated according to their preferred placement on new maps. Some prefer lower terrain, some prefer the upper regions. - CHANGE: Trees generate up to three new trees during their lifetime. Formerly depending on the system Simutrans was running on sometimes much too many new trees were generated. - NEW: news ticker - CHANGE: Some smaller optimizations, avoided function calls, about 4% speedup in total for games with many vehicles TIME: 6:30h 11/17/01 - CHANGE: Merged Andreas' changes: - If a new schedule is set, only those goods are removed from a vehicle which cannot reach their destination anymore. Formerly all goods were removed. - CHANGE: Merged Markus' changes: - Ok and Cancel buttons for savegame file chooser. The new savegame chooser looks really cool :) - CHANGE: Merged Volkers changes: - fixed a small flaw in hashtable datatype - new class to load *.tab files, this loader should become standard in future for all *.tab files - new integer hashtable template - new bridgebuilder class which is responsible for building and removing bridges - New bridge types, new bridge building menu - Changes to pointer checking and memory management - CHANGE: Added comments for all methods of the convoi class - FIX: fixed a bug that left copies of the landscape pointer image visible when moving the landscape point uphill or downhill - CHANGE: Sped up routing (pathfinding) of goods, this allows to use larger transport networks - CHANGE: Routing of goods cannot be interrupted anymore this fixes a potential bug in routing, but also may cause a slowdown in really large games. I hope that the optimization of routing will reduce this problem. TIME: 4:30h 11/14/01 - NEW: Merged Markus Webers changes to the Simutrans UI - NEW: Merged Andreas' changes to the SDL system wrapper file - CHANGE: limited 'random map' to 0-999 - CHANGE: system wrapper can now choose between software or hardware cursor. Currently Allegro uses software cursor, SDL and X11 use hardware cursor. TIME: 1:15h 11/13/01 - FIX: fixed a bug in removing railroad tracks, which sneaked in with Andreas' changes - NEW: released unofficial 0.79.4exp TIME: 1:00h 11/12/01 - FIX: fixed a memory leak that sneaked in while optimizing vehicle routing - NEW: released unofficial 0.79.3exp TIME: 1:00h 11/11/01 - CHANGE: STL priority queue can't be used with Mingw32. Using self made priority queue. - FIX: fixed a bug in routing. The bug limited the usable distance of routes for all vehicles, and slowed routing down, especially if ships were routed. - CHANGE: changed routing of goods. It had a similar bug like routing of ships. I hope this change didn't break something. If it works it'll speed up routing of goods in complex networks quite a bit. - CHANGE: added tooltips for all tools of the toolbar - CHANGE: tripled text buffer size for station info. There were problems reported which might be related to a buffer overflow in station info display. New buffer size is 8192 bytes. Mingw32 does not support snprintf(), but with sprintf() I have no idea how to avoid buffer overflows. I just hope 12288 bytes is big enough. - CHANGE: doubled buffer size of convoi info text, too, just to be a bit more save. Now 8192 bytes buffer size. - CHANGE: included Flors updated drive in cinema included Flors general purpose ship included Flors updated furniture factory included Flors updated home market included Flors updated planks truck - CHANGE: included Volkers update: FIX: image calculation of single rail pieces on slopes fixed FIX: some problems with directional bits in rail block manager, simplified some code FIX: fixed problems when removing bridges and tunnels TIME: 6:00h 11/10/01 - FIX: fixed a typo in de.tab - FIX: included Andreas' fix for wrong curves in tracks - FIX: included Andreas' fix to remove trees if land is lowered - CHANGE: switched to STL priority queue for vehicle routing, this should speed routing up for long and complex routes, but is somewhat slower for short routes - CHANGE: optimized vehicle routing, now about 20% faster than before. This allows to use 20% more vehicles than before! Changes due to the switch to the STL priority queue are not included in this measurement. TIME: 3:00h 11/05/01 - CHANGE: included Tristans fixed vehikel.tab with better weight and power values for trucks, busses and ships - CHANGE: changed vehicle info display in road vehicle depot to display power and weight TIME: 0:15h 10/28/01 - CHANGE: changed vehicle routing. Fixed a bug in search for best route. Vehicles should now always find the shortest route. - CHANGE: cities build a stadium at a population of 2500, was formerly 2000. - NEW: cities build a drive in cinema at a population of 1500. - FIX: fixed a wrong entry in en.tab and de.tab ("Verkehrsteilnehmer") TIME: 3:00h 10/27/01 - FIX: removing ground from above tunnels works now - FIX: removing tunnels works now - FIX: removing bridges works now - CHANGE: removing bridges and tunnels no longer places rails on the end squares, but cuts the rails at the end of the former structure - CHANGE: added END markers to depot.tab sections and a check if given vehicle count matches actual vehicle count. - CHANGE: updated thanks.txt - NEW: included Jens wood transport ship TIME: 2:30h 10/21/01 - CHANGE: changes savegame format to load/store animated factories - CHANGE: enlarged scroll range in vehicle and station info windows - NEW: included Flors animated factories - NEW: included Flors new trucks 10/15/01 - NEW: animated factories - CHANGE: better animation support for all buildings 10/12/01 - CHANGE: changed makepak to read PNG images, converted all images to PNG format. 10/08/01 - NEW: added Flors new pause icon. - CHANGE: enlarged scrollable range in vehicle info windows 10/04/01 - FIX: if vehicles are stored in depots and the map was saved and loaded again, Simutrans crashed. This worked in former versions, I don't know why it was broken in between. Nevertheless this is fixed. (calc_bild() of the vehicle checked the ground of the map square which was not yet completely loaded at this moment, and this inconsistency made the game crash. Vehicles call calc_bild() often while moving. Theoretical there is a chance that the image of a vehicle is wrong now directly after loading, but I couldn't find any problems yet. - FIX: if a saved game from a Simutrans with a different vehicle.tab is loaded, some vehicle informations are not available. Formerly this caused a crash while loading the game. Now the information is replaced with 0 values. This has the result that the 'unknown' vehicles do not move (power and weight are set to 0), but the saved game can be loaded and all common vehicles (common in both vehicle.tab files from the saving and loading Simutrans) will work. - NEW: released unofficial 0.79.pre3 10/03/01 - FIX: fixed a buffer overflow in station name creation. - FIX: if the hardware clock is set back while Simutrans runs, the negative time difference is ignored. -> this change limits the time Simutrans can be run without stopping to about 23 days! -> this change saved 4 bytes memory in each goods-destination object both in main memory and saved games - FIX: due to Volkers changes, saved pedestrians didn't move anymore after loading. This is fixed, they move again. - NEW: The '!' key (Shift-1) toggles the display of station and town names. - NEW: The '.' and ',' keys can be used to change the time lapse setting. This is an experimental feature, if you want to be safe, do not use this. 09/30/01 - FIX: removing a building (or tree) sets ground to bare land (was broken due to Volkers changes) - FIX: factory owner is restored when loading a game (was broken due to Volkers changes) - FIX: vehicles are now displayed correctly (uncropped) again. (was broken due to Volkers changes) -> This fix does not work for vehicles on bridges, but since the problem only occurred with ships noticeably I think the fix is 'good enough'. We have no ships on bridges so far. - CHANGE: conserved about 160KB for a standard size map save file -> This change limits the number of players to 127. I guess, this isn't really a restriction. - CHANGE: changed text for "optionen > quit" to "New map", because it opens the new map dialog rather than quitting the program. - CHANGE: changed image for "Please wait, creating a new map" message window - NEW: released unofficial 0.79.pre2 09/29/01 - FIX: fixed a bug in Volker changes to routing; ships move again -> there is still a display problem in the graphics - FIX: if a text input field was active while the containing window is closed, the action bound to the input field is not triggered any more 09/26/01 - Merged Volkers changes -> binary save game format (saves about 30% space, saves and loads much faster, incompatible to previous versions) -> bugfixes for route calculation and object management in world map - FIX: fixed a problem in saving industries with Volkers new code: buildings no. 2 and 3 got mixed during saving - FIX: Volkers changes prohibited removing trees and town buildings It's now again possible to remove trees and houses - NEW: Released semi-official Simutrans version 0.79.pre1 09/23/01 - CHANGE: enabled producing industries to have more than one consumer. - NEW: More than one consumer per producer. This finally overcomes the strict 1:1 relation of producers and consumers and allows more flexibility in the game. - CHANGE: modified buttons of the schedule input window to match the look of most other buttons in Simutrans - FIX: fixed a display problem in train/convoi and station info windows 09/22/01 - CHANGE: instead of enabling all things as synchronous objects only a few are sync steppable now. This change should improve the performance of Simutrans, especially if many vehicles are used. - FIX: fixed a bug in route calculation that could crash the game 09/20/01 - CHANGE: cleaned up vehikel_t class. -> saved 4 bytes per vehicle -> removed two unneeded methods -> moved a method into convoi_t class where it belongs -> reduced number of method calls from convoi_t to vehikel_t during driving by 50%, this should speed up things a bit - FIX: fixed a wrong bounds check which could make the game crash in route_t 09/19/01 - FIX: if a game was loaded and right after loading saved all industry buildings showed up as construction areas forever after loading again. This problem is fixed now. - NEW: added a new industry: furniture factory 09/16/01 - FIX: textinput fields release focus now if the containing window is closed - FIX: it was possible to choose invalid color sets by clicking left or right of the color bars in the color chooser window. This problem is fixed now. - FIX: fixed a bug which crashed the game when a vehicle entered a depot while the vehicle info window was opened The following fixes fix problems introduced with the new tunnel and bridge data structures or were done by Volker who wrote the new data structures and associated code: - FIX: fixed visibility of vehicles entering and leaving tunnels - FIX: fixed visibility of vehicles driving uphill/downhill - FIX: fixed vehicle smoke creation for tunnels - FIX/NEW: Merged Volkers changes: - Fixes to compile Simutrans with MS Visual C++ - Fixed a problem in pipeline/powerline code - Positioning of the relief map works now with big maps, too - Added a replacement for 'opendir()' which is unknown to VC - Removed some printf's from blockmanager code - Optimized access to ground objects - Moved updates of the relief map into ground object class - Fixed an array bounds violation problem in vehicle routing code. This bug was probably very seldom triggered. - Removed unneeded methods from the squares objects - Changed vehicle movement a bit. - Fixed a problem in the bridge building tool 09/09/01 - CHANGE: World display in convoi and station windows fixed - FIX: Industries get a proper list of worker residential cities even after creating a new map. - FIX: Display of desired destinations in city info window fixed 09/08/01 - FIX: Renaming of newly build vehicles works now - BUG: The window title gets translated automatically. If by accident the vehicles new name is included as string in the language file the window title displays the translation for this string instead of the string itself. - NEW: Version stamp for saved games - NEW: Version check and error message when loading saved games - CHANGE: Cleaned up fahrer_t, vehikel_t hierarchies: removed methods which were no longer used after Volkers changes 09/06/01 - NEW: Released semi-official Simutrans version 0.78.12 - CHANGE: updated todo list - CHANGE: mail offices can no longer be build on roads and railroads - CHANGE: maps with invalid rail blocks can be loaded again (added Volkers hack). I'm not sure if this fixes the problem but at least you can load the maps again and try to go on with the game. -> This change is untested because I had no saved maps with invalid rail blocks. 09/04/01 - NEW: A limited file chooser for loading/saving games. Allows to enter names for games, up to 60 characters per game. Does not work on operating systems which have less than 60 chars per filename. This allows to have more than 5 different savegames. -> All existing savegame files need to be renamed to *.sve manually, because the new Simutrans version only detects *.sve files!!! - NEW: Clicking the entries of the vehicle list now opens the vehicle information window of the clicked vehicle. This way you can inspect your vehicle fleet very easily. 08/24/01 - FIX: "Bankrupt" message is displayed if a player is bankrupt (Was hidden due to some other changes in the code) - CHANGE: "You are in dept" messages now tell how many months are left to pay back the money. - CHANGE: updated todo list - NEW: Factories do not deliver goods to stations where more than 1000 units of the same good are already stored. This should support a fair distribution of goods if several routes exist and one route has only a tiny transport capacity. Formerly all routes got the same amount of goods and if the tiny routes transport capacity was below that, immense amounts of goods were piled up at the tiny routes source stop. Now the tiny route is capped once 1000 units are stored on it's stop and the other routes are preferred by the factory. - NEW: Released unofficial version 0.78.11exp 08/23/01 - NEW: new station info and station detail windows -> renaming stations is now possible -> station window now has scrollbars to scroll big goods/passenger lists - CHANGE: heavy workaround to get station name synced with displayed name (due to a design mistake in an earlier stage) - BUG: text input fields keep input focus if window gets closed. 08/22/01 - CHANGE: change convoi info window to use the new UI classes. This cleans up some code and streamlines more of the UI. - NEW: added text input facility for changing convoi name to convoi info window -> this allows to change vehicle names! - NEW: text area component (only used in new convoi window so far) 08/20/01 - FIX: Fixed a bug which crashed the game if a new map was created, then the game was quit and again a new map with more cities was created (caused by deleting the new number of cities instead of the old number). 08/17/01 - CHANGE: included Owens update for the money dialog Negative values are now displayed in red. - CHANGE: adapted a few more dialogs to the rule: unchangeable values -> black changeable/changing values -> white This way the player can see immediately which values are fixed and which are supposed to be changed/changing 07/01/01 - NEW: built a new BeOS release (0.78.10exp) 06/30/01 - NEW: merged Owens finance window update - NEW: (internal) label text can now have other colors than black - CHANGE: to unify the finance window look with existing dialogs all changing entries are displayed in white, all constant entries are displayed in black now 06/23/01 - NEW: assigned click sounds to more actions to give a better feedback to the user 06/22/01 - FIX: sometimes the interrupt routine was called while it was already running. In rare cases this could lead to a double free() of the same object. The second free was likely to crash the game (C++ standard says something about undefined behavior in that case) or do even worse. This is fixed now, the interrupt routine can't be called any more if it is running already. 06/20/01 - NEW: added command line switches -h, -?, -help and --help which all print a help message - NEW: added a few more debug (error and fatal error) messages to loading/saving railroad tracks with invalid rail block IDs. This won't help the player immediately but allows better bug reports. - NEW: added a -screensize option to choose arbitrary resolutions This also allows to run Simutrans windowed in multiple window sizes. - FIX: fixed a problem which crashed the game if a station building had its name set to NULL. This should not happen normally but NULL should be a allowed value for a name string (meaning 'no name'). Now the program should be able to cope with station building names which are NULL. Usually this problem was reported as "The game crashes if I click a station" - CHANGE: found some more printf calls and replaced them by calls to the new logging system. 06/17/01 - CHANGE: Info windows display a bit of the environment, too, now, instead of just the objects square. This required changes to the clipping check in simgraph.c - BUG: new clipping support for graphics isn't perfect. The last line of graphics in the main display is not drawn correctly. In general clipping for images seems to truncate the last line while other drawing functions are fine. Workaround: draw one additional line (this hides the effects). - NEW: finance statistics get loaded/saved. Old savegames can be read; all finance stats are set to 0 when reading old games. - NEW: finance statistics get reset to 0 every year - NEW: Released unofficial version 0.78.8exp 06/16/01 - NEW: Translated Owens finance window entries - NEW: Finished work on vehicle list window - NEW: Painted two icons (finances, vehicles) for the toolbar to open the new finances and vehicle list windows - FIX: Fixed two bugs in the scrollpane class which displayed scrollbars at wrong position if the scrollpane was not located at (0,0) - BUG: Finance statistics/informations are not yet loaded/saved - CHANGE: Increased max speed of private cars 06/15/01 - CHANGE: Merged Owens finance window routines - FIX: Finance window title bar is now displayed in player colors - NEW: Started working on vehicle list window 06/14/01 - NEW: Released official version 0.78.7exp 06/13/01 - NEW: Built official version 0.78.7exp 06/09/01 - FIX: building signals directly adjacent to tunnels/bridges works now. - FIX: removing signals near bridges/tunnels works now. - CHANGE: adjusted station timeout to new timeflow. It's now again 90 days (was less than 1 day) - BUG: The block repair tool does not work if there is a train waiting on a bridge and the block below the bridge shall be repaired. In this case the train on the bridge is counted as if it was on the ground block and the wagon counter of the ground block is set too high. 06/06/01 - FIX: fixed a problem during image calculation in the schiene class which could crash the game during destruction of a map, i.e. if a new game was loaded. 06/02/01 - CHANGE: Changed a lot more printf calls to calls to the new debug/logging class. I hope I have changed all of them! - CHANGE: Changed a suspicious piece of code in window management: if you had 31 or 32 windows open, and closed a window, the program read data behind the window array. This data was copied into the window array. In general there was too much data copied but usually within the window array. I don't know how harmful it is to read from behind arrays, but nevertheless this is fixed now. - CHANGE: Changed another suspicious piece of code in window management: similar to above but executed when a window was topped. - CHANGE: Increased the window array size from 32 to 64 entries, this means up to 64 subwindows can be displayed. - CHANGE: The behavior for "-log" was changed. The output is now written into the 'simu.log' file AND into the console window (was only 'simu.log' before). - CHANGE: Changed default resolution from 640x480 to 80x600. There were never ever problems with higher resolutions reported, so this seems to be fairly safe. - CHANGE: Stations without ground squares have name "Unnamed" until a ground is added to the station. This fixes a possible crash when inquiring stations to early or late. - FIX: fixed a bug which blocked the railway line if a one-way signal was removed. Now it's safe to remove one-way signals, too! 06/01/01 - CHANGE: Changed a lot of printf calls to calls to the new debug/logging class, rated as "Message", "Warning", "Error" and "Fatal error". Fatal error quit the program immediately. 05/27/01 - NEW: Added more error detections to the id_handle template (tombstone id checks). This checks should catch about 50% of all internal data corruption problems. (The other 50% can't be checked because in those cases the result of the corruption is a valid tombstone ID again). - CHANGE: changed "Warning: Convoi was saved in state 4 or 5" to "Message: Convoi was saved in state 4 or 5" because this is not an error, but normal program action. 05/26/01 - CHANGE: Tried to migrate to from Allegro 3.9.32 to Allegro 3.9.36 Compiled and installed new Allegro Library for Windows. -> Supports now DirectX up to version 7.0, hopefully this will save some of the "no suitable graphics driver found" problems. -> 'simuwin -res 5' runs Simutrans in a window nicely now (Windows version only, other versions have window by default) 05/24/01 - CHANGE: Translated debug messages in rail block manager - CHANGE: Loading a map now tells how many factories trains/convois are loaded - CHANGE: Improved id_handle_tpl for better checking if a handle is bound to an object - CHANGE: Adapted automatic tests for tombstone checking - CHANGE: Cleaned up code in rail block and rail block manager classes - CHANGE: Stations are now using handles and tombstones, too - CHANGE: Translated a few more messages from the tools 05/23/01 - NEW: Tombstone/id_handle templates allows to check for dangling pointer in all classes retrofitted with tomstone tables - CHANGE: Retrofitted rail block class with tombstone table - CHANGE: Split rail block classes and rail block management code into two files ------------------------------------------------------------------ Made a break to gather my thoughts and find new solutions for some problems with Simutrans. ------------------------------------------------------------------ 04/22/01 - NEW: Map preview in map selection window (new game window) - NEW: Included Tilli's new vehicle setup routines for smoother starting of vehicles. - CHANGE: relief map look changed 04/21/01 - CHANGE: Text input fields have a cursor now, changed handling of input focus a bit - CHANGE: rearranged ground and road images to allow multiple sets of ground images - FIX: renaming cities from loaded games works now 04/19/01 - NEW: Text input fields - CHANGE: City info window uses text input field 04/14/01 - FIX: found an array bounds violation in simgraph.c in a text drawing routine. This caused random crashes and other strange behavior if text was drawn half-outside the screen. - FIX: found an array bounds violation in simgraph.c in another text drawing routine. Nearly as evil as the bug mentioned above. - FIX: fixed an array bounds violation in simsound.cc If no sound card was detected the first MIDI title read from music.tab was read into midi_titles[-1] and overwrote an helper array of the AI. This made the game crash when the AI was using the values from this array. 04/13/01 - FIX: removing one of those smoke-clouds crashed the game. This is fixed now. This bug could be triggered either by using the 'remover' tool or by building something on squares which contained clouds. - CHECK: it was reported that removing rails or roads and thus breaking a vehicles route would crash the game. This could not be reproduced. - CHANGE: included new en.tab, fi.tab, fr.tab and es.tab (language files) 04/11/01 - FIX: fixed a bug which aborted the game if empty spaces in the depot were clicked while being in sell mode - FIX: midi playlist displays message "Musicplaying disabled/not available" if either no MIDI files could be loaded or if the soundcard could not be initialized successfully - FIX: Song name in playlist is updated if a new song starts playing 04/09/01 - Merged Owens new sound routines: a simple MIDI playlist UI for browsing the MIDI files forward and backward. - CHANGE: Unified look of playlist UI with other Simutrans UI 04/08/01 - Released 0.78.0 04/07/01 - CHANGE: en.tab and de.tab updated, included corrections from Seth and Dirk. - CHANGE: included new hungarian language file - CHANGE: translated a few more debug/info messages from german to english. - FIX: included Tilli's tree placement fix. 04/04/01 - FIX: clicking an empty entry in the engines area of a depot window no longer aborts the game. This bug was introduced during the changes in 0.77.6 when a bounds checked array was introduced as container for engines ... a failed bounds check aborted the game in this case. 04/01/01 - CHANGE: AI build trains of different length now - CHANGE: AI uses different engine types now - FIX: AI no longer buys trailers without trucks 03/31/01 - NEW: New industry: Bookshop (painted by Tuomas, tiled by me) - NEW: New vehicle: Book van (painted by Sergiu, modified by me) - NEW: New good: Books - NEW: New private car (painted by Dirk, recolored by me) - CHANGE: included new powerlines from Daniel - CHANGE: included new transformer station from Daniel - CHANGE: Doubled the running cost for all locos, they were smaller than the average truck running cost before. Now running costs for train engines are higher than truck running costs in average. - CHANGE: Raised the capacity of all trucks by 10-30%. Trucks should be able to compete (at least sometimes) with trains. - CHANGE: Power station has now upto three suppliers (was two formerly). I hope this is enough to produce electric energy more continuously. 03/28/01 - CHANGE: compiled a new Linux test version with a different setup of libraries (left out all DGA libs in Allegro setup). The new version runs on a wider range of Linux systems now. 03/23/01 - CHANGE: Cleanups for new map window, sets scroll positions to actual map coordinates when opening a new map window. - CHANGE: Text drawing routines can now choose from two fixed width fonts and one proportional font. - CHANGE: The image of the power connectors at power stations was changed to a grey power line segment. - CHANGE: Linux version can now be built with Allegro, sound and music works, mouse is self-managed as in windows fullscreen mode: - NEW: Sound effect for income/expenditure. 03/22/01 - NEW: Powerlines. Power stations get power connectors. The player can place transformer stations near other industries and connect power stations power connectors and the transformer stations with powerlines. Hotkeys: 'g' to place a transformer station 'l' to build a powerline - NEW: A 4x7 font, used in relief map to display town names. Small and hard to read. - CHANGE: Event handling during scrolling changed. Now the frame_time is no longer changed during dragging the map. This should prevent some possible problems (passenger rate) with a ridiculous fast looping main loop (which was the result of a map drag before). - CHANGE: Fixed width font drawing routines clip correctly now. 03/21/01 - CHANGE: Goods/destination objects changed from heap allocation to stack allocation and to value variables instead of pointers. The former solution (heap/pointers) was always a source of problems. The new implementation should be more robust against several error types (dangling pointers!). It's probably not more correct than before, but allows to keep playing even if an error occurred. This solution needs to copy more memory (16/24 bytes instead of four for each assignment but saves one indirection on each value access and saves calls to new/delete. All in all it seems to be roughly as fast as before (no measurable difference). I think, it's even faster now. - CHANGE: Changed event queue indices in simsys_d.c to unsigned types. They were always checked for higher bound, but not for lower bound. Making them unsigned prevents the occurrence of negative values, and a higher bound check is now sufficient to ensure values of valid range. Similar to the change above, this might not be more correct than before but it should be more robust against errors than before. - CHANGE: Changed a few more lists to list templates to gain type safe interfaces. Should allow better error checking by the compiler. - CHANGE: Changed memory allocation scheme of map squares. They prefer to keep memory instead of freeing it. They just free memory if they have space for more than 32 objects but contain less than 8 objects. In other cases they keep the allocated space. This reduces the number of calls to new/delete and should therefore speed up the program, but it might use up to four times more memory than before (couldn't verify this, the game needed 12 megs to play a 256x256 map before and still needs less than 13 megs for a 256x256 map). 03/20/01 - FIX: Fixed a bugs which destroyed the freelist of the node_container_t by accident. Cause was a freelist=0 in the constructor of the node_container while freelist is a static member and should not be nulled in the constructor.result was a memory leak. - CHANGE: Allow putback of node lists in node_container_t. This reduces the number of calls to new and delete and should be more efficient. It simplifies code and moved two implementations to a single source, thus enhancing maintainability. The queue_t and Stack classes benefit from this feature. - FIX: Fixed a memory leak in sound_frame_t. Sliders and labels are now data fields of the class instead allocated separately on the heap. This way they are automatically freed if the frame is deleted. - CHANGE: Updated two of my houses, adjusted window sizes and wall colors. Added a lamp (lit at night) to one of the houses. 03/19/01 - CHANGE: Some memory management changes started: - reduced number of calls to new/delete by reusing node objects (nodes are used in all linked data containers and are allocated/deallocated very frequently - changed HNode and KNode (goods routing and vehicle routing) memory management to use freelists. 03/18/01 - CHANGE: Image updates from Trikky and Oskari included - CHANGE: Mouse pointer no longer visible during loading, saving and while creating a new map. This prevents the 'footsteps' problem. 03/17/01 - CHANGE: Relative volumes of music/sound adjusted. - CHANGE: Sound volume has a wider adjustment range now. 03/15/01 - CHANGE: included Ricks updated images - FIX: Event handling problem in options dialog fixed, this fixes also event problems in the new savegame slot dialog. This also makes the buttons real 'pressable', not just 'touchable' as before. 03/14/01 - NEW: MIDI playing routines by Owen Rudge integrated - NEW: MIDI volume selector to sound control added - FIX: Saving games with Shift-S works again - CHANGE: Mouse position update changed, last change had a bug introduced 03/10/01 - NEW: A very simple savegame chooser. Allows to select one of five savegame slots for loading and saving a game - FIX: Fixed a bug in convoi (train) loading code. There was a little chance that convois (trains) were saved in an inconsistent state. This fix recovers the convoi state during loading the game. - CHANGE: added a new interface class for listening for button press events to the gui class hierarchy - CHANGE: tried to enhance timer precision from 5 ms to 2 ms (Only Windows version, other versions already had a precision of 1 ms); - CHANGE: replaced one of Trikkys buildings with an updated version - CHANGE: included steam loco sound - CHANGE: improved mouse pointer movement (only Windows version). 03/08/01 - CHANGE: idle_time now drops until below 50µs - CHANGE: passengers are routed in groups of up to 7 - CHANGE: passengers are appalled by crowded stations in groups of 8 (was 4 before) - CHANGE: passengers are appalled by not finding a route in groups of 4 (was 1 before) - CHANGE: tried to optimize haltestelle_t::suche_route() 03/05/01 - CHANGE: cities step only every fourth row per step (was every second row before) - CHANGE: buildings creates twice as much passengers as before to make up for the slower step frequency 03/04/01 - NEW: Sound control window (only volume control so far) - NEW: Label component for UI - CHANGE: Sped up display_img by another 3% with a tip from Frans - CHANGE: Included Frans in intro scroller and thanks.txt - FIX: If an animated building was renewed the animation count was not set correctly and a wrong image was displayed for some time. This is fixed now. - CHANGE: map window display maps <= 256x256 squares without scrolling, bigger maps are displayed in a scrollable window of 250x250 pixels in size. (Currently the scrollbars are just inactive during the display of smaller maps). - FIX: X-Windows: Exposure-event wird jetzt richtig verarbeitet 03/03/01 - NEW: Scrollpane class (scrolling canvas for UI components) - NEW: Container class for UI components - NEW: Frame class for UI components - CHANGE: Maximum map size is now 576x576 squares - CHANGE: Relief map got scrollbars 03/01/01 - CHANGE: A new private car - CHANGE: Improved some images (city buildings) 02/28/01 - CHANGE: Optimized the assembler routines even further with tips from Frans. Gained another 6-10% in speed. 02/24/01 - CHANGE: Large cities call INT_CHECK more frequently now. This makes vehicles movement smoother. - CHANGE: Passengers are routed in groups of three if possible, this enhances overall performance in games with big cities. - CHANGE: Drawing routines rewritten in assembly language, now at least 15% faster than before. - CHANGE: Route building for AI cleans up datastructures after route calculation. - CHANGE: Changed the AI and road/railroadbuilding code to reduce the 'stalls' in vehicle movement. 02/18/01 - FIX: 'waddling' trains fixed (by Till) - FIX: The pause mode is working correctly now Press 'p' to pause the game - CHANGE: Tree placement improved (by Till) - FIX: Cities no longer destroy bridges when building things below them - FIX: It's no longer possible to remove depots while there are vehicles in them - FIX: Off screen sounds are now muted (clipped) correctly even in higher screen resolutions - CHANGE: Sounds which are farther away are less loud than sounds from near things. - CHANGE: If an existing schedule is opened for editing, the default action should not be 'remove'. Now 'select' is the default action. 02/11/01 - CHANGE: Closing a tool window reverts the selected tool to the information tool. - FIX: Day/night switch on title screen works now. 01/28/01 - CHANGE: If there is no train/convoi yet, and a new engine is bought, a new train/convoi is automatically created and the engine is added. - FIX: VELO in en.tab changed to DIBU 01/24/01 - FIX: Clipping rectangle bug fixed 01/21/01 - FIX: Railway stations can't be built on level crossings anymore - FIX: TPDIesel entry corrected in de.tab/en.tab - CHANGE: included Dirks/Manfreds new de.tab 01/18/01 - FIX: There was a bug in the calculation of the direction of a newly built bus stop 01/14/01 - CHANGE: memory management for goods changed to list based management - CHANGE: included Oskaris new images - CHANGE: added Sounds for ship depot/harbour building tools - CHANGE: Cities build stadiums when they reach 2000 inhabitants - NEW: Basic support for animated buildings - FIX: The landscape pointer is only redrawn if really needed (shape changed or moved). This should save some percent of CPU time if the pointer isn't moved. 01/12/01 - FIX: level crossings can't be built on slopes anymore - FIX: bus stops can't be placed on level crossings anymore 01/10/01 - FIX: a bug in rail track display code (displayed end of lines under signals) - FIX: windows get events, even if the last mouse click was outside the window 01/07/01 - Added tools to create level crossings; this tool adds rails to roads. Together with the former level crossing tool, this gives a general purpose level crossing building tool. - Added infrastructure to bind sounds to tool selection and execution - Fixed bus-stop bug 01/05/01 - Added tools to create level crossings; this tool adds roads to railroads. This means you always need to build railroad tracks first. - Added icons for tool, a landscape pointer for tool and images for the crossings itself 01/04/01 - Changing the program to use the new ground and way classes - Ran some tests to see if the new ground classes work well 01/03/01 - Preparing the program for level crossings and combined traffic -> introduced a new class hierarchy for ground and way types which supersedes the old class hierarchy. 29.12.00 - Merge mit Niels Sourcen - Brücken und Tunnelwerkzeuge in Strassen- und Schienenbaufenster verschoben 28.12.00 - NEU: Tab-Panels für Depot-Fenster 24.12.00 - FIX: Falls Schienebau fehlgeschlagen wurde als Fallback Strasse gebaut ohne zu prüfen ob für die Waren überhaupt Strassenfahrzeuge verfügbar sind - CHANGE: Waren haben Koordinaten statt Koordinaten-Zeigern für die Ziele. Das behebt das Problem der mehrfachen Freigabe gleicher Koordinaten - CHANGE/FIX: Laden von Planquadraten ermittelt max_index jetzt anders. Das sollte das Problem max_index >= capacity beheben. Ist nach der Änderung nicht merh aufgetreten Ein assert() prüft diese Bedingung in der Debug-Version. 20.12.00 - CHANGE: Städte erzeugen beim renovieren keine neuen Gebaeude-Objecte sondern updaten die alten. Das behebt ein Speicherloch. 17.12.00 - CHANGE: GUI: Buttonlisten auf Vector-Template umgestellt - CHANGE: Fahrplan mit 0 Stops führt nicht mehr zum Absturz - CHANGE: Fahrplan benutzt jetzt auch array_tpl 16.12.00 - CHANGE: Beschleunigung wird jetzt aus dem Fahrzeuggewicht errechnet - CHANGE: Anheben von Wasser mit Konvertierung zu Land ist jetzt möglich - CHANGE: Endverbraucher (Fabriken) können jetzt in Städten platziert werden. - FIX: obj_ist_da() stürtze ab, wenn gar kein Objekt auf diesem Feld vorhanden war 10.12.00 - CHANGE: Ribi von Stadtstraßen werden jetzt richtig gesetzt - NEU: Kosten für Bau und Abriß von Tunnels und Brücken werden verrechnet - NEU: Gewicht und Leistung pro Convoi werden jetzt ermittelt 09.12.00 - NEU: Testfälle für Objektverwaltung im Planquadrat - FIX: "Überbauen" von nicht-entfernbaren Dingen ist jetzt nicht mehr möglich - CHANGE: Staedte steppen nur jede zweite Zeile, erzeugen dabei aber pro Gebaeude doppelt so viele Passagiere. Das sollte Rechenzeit sparen. 08.12.00 - CHANGE: Planquadrate haben dynamische Objektverwaltung und koennen bis zu 2^15 Objekte verwalten. 06.12.00 - CHANGE: diagonales Scrolling per Keyboard ist jetzt möglich - CHANGE: daten.pak enthält jetzt die Anzahl der gespeicherten Bilder - CHANGE: Städte bauen Stadien jetzt erst bei 1050 Einwohnern 05.12.00 - FIX: setRGB8multi wieder benutzt, ist bei Merge mit Niels sourcen am 30.11.00 entfallen 02.12.00 - FIX: 'done' Button im Fahrplan-Dialog öffnet nicht mehr das Infofenster eines darunterliegenden Objekts - CHANGE: Warteschleife fuer Wartezeiten von weniger als 1 ms - Freispielmodus eingebaut, ergänzt Testmodus - Pasis verbessertes Stadtinfo-Fenster eingebaut 30.11.00 - Merge mit Niels sourcen 29.11.00 - Hotkey q,x zu Q,X geändert - Hotkey t für Schienenbau - CHANGE: karte_t::destroy loescht alle Blocktrecken - CHANGE: 'max. 10/60 Passagiere' zu '10/60 Passagiere' geändert 27.11.00 Suche nach Fehler, der die Grafikstörungen hervorrufen könnte 2 Stunden erfolglos gesucht 26.11.00 - GUI um Tag und Nacht Modus an-/abzuschalten - FIX: Kennfarbe des Spielers wird bei Nacht richtig gespeichert/geladen 25.11.00 - weitere Arbeiten an Tag&Nacht Modus - CHANGE: mehr Lichter bei Nacht - FIX: Kennfarbe des Spielers bleibt bei Nacht erhalten 23.11.00 - weitere Arbeiten an Tag&Nacht Modus 22.11.00 - setRGB8multi fuer effizienteren Farbwechsel - mehr INT_CHECKS fuer besseres Fahrzeugfahren 20.11.00 - Merge mit Niels sourcen 19.11.00 - Tag-und-Nacht-Modus 16.11.00 - FIX: Fahrpläne haben jetzt Speicher für 16 Einträge - Merge mit Niels sourcen - FIX: Ricks Fabrik ist 599 statt 699 - Ricks toolbar 15.11.00 - einige casts durch dynamic_cast ersetzt - Vehikelbauer ist auf den vehbesch typen basiert statt auf tabellen. - KI prüft die Verfügbarkeit von Fahrzeugen vor dem Streckenbau - Abschluss der Industriekonfiguration, Einbau der Autofabrik 13.11.00 - slist_tpl: Typsicheres List-Template 12.11.00 - industry.tab fertig - Waren/Industrien sind jetzt frei konfigurierbar 11.11.00 - NEU: industry.tab (begonnen) 09.11.00 - NEU: Warendaten werden aus Datei gelesen, warenbauer als Factory-Klasse für Waren 08.11.00 - NEU: Pause-Funktion - NEU: Anzeige fuer Fracht in 'aktuell/maximal' 03.11.00 - NEU: eigene, einheitliche Random-Funktion, Plattformunabhängig - NEU: Reliefkarte "draggable" - NEU: Window wird bei Klick in Window-Koerper getoppt. 02.11.00 - NEU: Beschraenkungen fuer Fahrzeugkombinationen - FIX: Fahrzeuge auf Bruecken/Tunel werden jetzt von pruefer_ob_strecke_frei richtig gezaehlt - FIX: block_ersetzer behandelt Bruecken/Tunnel jetzt richtig - FIX: Signale entfernen funktioniert jetzt 01.11.00 - Window-Close Button wird richtig gedrueckt 31.10.00 - Repeat Buttons 30.10.00 - Merge abgeschlossen 29.10.00 - Merge mit Niels sourcen 28.10.00 - FIX: Umbau des GUI auf Komponenten 24.10.00 - FIX: Rückbau des Speichermanagements für gebäude_t - FIX: Einfahren in Bahnhof prüft jetzt auf Schienen - CHANGE: intro scroller text 23.10.00 - FIX: Anpassung Fenstergroesse an 11er LINESPACE - FIX: Zuege fahren über Bruecken statt untendurch - FIX: free_entries in memblock_t wurde nicht init, das ist jetzt behoben - CHANGE: Änderung im Speichermanagement für Gebäude 19.10.00 - NEU: Fenster können mit DEL und Backspace geschlossen werden - NEU: Wegpunkte werden mit Koordinaten aufgelistet - CHANGE: Textabstand auf 11 Zeilen erhöht - CHANGE: "Cr" wurde in creditszeichen umgeschrieben - CHANGE: Betriebskosten werden jetzt aus vehikel.tab gelesen und pro feld verrechnet. - FIX: Beim Entfernen eines Bahnhofs wird ein Schienenende wieder zu einem Schienenende 16.10.00 - FIX: beim Einfügen von Koordinaten in eine Route wurden die Hoehen nicht mit verschoben. Jetzt werden Hoehen von 0 eingefuegt - NEU: Scrolling per Tastatur 15.10.00 - NEU: Blickpunkt wird mit Karte gespeichert - NEU: neue Option "zeige passagiere" - NEU: Soundwerte in vehikel.tab und vehbesch.cc - NEU: Option -log schreibt logfile - NEU: Fortschrittanzeige beim Laden/Speichern und neuen Spiel 14.10.00 - CHANGE: neues memory management, wird von koord und sync_wolke_t benutzt, da diese Klassen sehr haeufig instanziiert und geloescht werden. 12.10.00 - FIX: Schienen über Schienen legen funktioniert jetzt richtig, Blockstrecken bleiben an den Signalen getrennt - Test geschrieben - FIX: Depots entfernen funktioniert jetzt richtig, im ersten Schritt wird das depot entfernt, im zweiten die Schiene - Test geschrieben - CHANGE: stepgruppen von 7 auf 14 hochgesetzt - CHANGE: Wasser stept jetzt jedesmal - CHANGE: Baeume steppen jedesmal und berechnen das Alter richtig, Baeume saemen an insgesamt drei tagen im Baumleben 08.10.00 - NEU: Sound-API begonnen 07.10.00 - FIX: Kontostand ist jetzt 64 bit Wert (long long) 05.10.00 - FIX: Post kann nur auf leerem Feld gebaut werden - FIX: besserer Text für Intro-Scroller 04.10.00 - NEU: Intro-Scroller 03.10.00 - FIX: routing um Faktor 35 beschleunigt - FIX: Kennfarbe wird gespeichert und geladen - NEU: Staedte bauen Stadien - TEST: Fussgaenger 30.09.00 - Züge halten in T-förmigen Bahnhöfen jetzt richtig - FIX: Routing von Waren zur Zielfabrik verbessert: statt entfernung halt->basis zu Fabrik wird jetzt die Fabrikliste der Haltestelle zu rate gezogen um zu sehen ob die ware abgeliefert werden kann. 27.09.00 - Automatische Tests für das Schienenverlegen begonnen - Testsuite ausgelagert 23.09.00 - simworld.cfg speichert einstellungen: Sprache 20.09.00 - Neues fr.tab integriert 19.09.00 - "delete void * considered harmful": der destructor wird nicht aufgerufen. Deswegen slist.delete_all entfernt und ersetzt. 17.09.00 - NEU: vector klasse und array template 16.09.00 - NEU: Testsuite begonnen 08.09.00 - FIX: bei Truecolor wird das Bild nach einer Änderung der Palette neu gezeichnet. 04.09.00 - FIX: Welt wird nach laden komplett neu gezeichnet - CHANGE: auflaufen der hinteren Waggons bei Halt vermieden - CHANGE: Züge klemmen nicht mehr nach halt in kurve, allerdings stimmt das setup nach dem Halt nicht mehr bis der Zug in einen Kopfbahnhof einfährt. - CHANGE: Fahrplan-UI selekiert add-mode oder remove, je nachdem ob der Fahrplan leer ist oder nicht 01.09.00 - Ungelöst: Haltestellen (docks?) um Ölfelder herum können nur geladen werden, wenn sie Spieler 0 gehören. Gehören sie Spieler 1 dann stürtzt das Programm beim zweiten Ladeversuch ab. - FIX: Züge mit mehr als 4 Waggons routen jetzt auch in Durchgangbahnhöfen richtig 31.08.00 - NEU: Oelfelder funktionieren jetzt - FIX: verteile_waren bedient meherer Haltetsellen jetzt richtig, hatte zuvor n-mal die Ware verteilt - FIX: uninit. Zeiger in simfab.cc behoben 30.08.00 - FIX: Verkehr fährt jetzt richtig über Hügel und Senken - CHANGE: Verkehr behindert Automobile 29.08.00 - FIX: Tunnel und Brückenbau funktioniert wieder - FIX: zwei möglicherweise uninit. zeiger in simplay.cc behoben. 28.08.00 - FIX: Bounds check bei raise_to/lower_to war falsch 24.08.00 - NEU: Anzeige der Mauskoordinaten (int Kartenkoordinaten) - CHANGE: Stadtnamen sind jetzt eindeutig. 23.08.00 - CHANGE: Auslagerung Depotdaten in "depot.tab" 20.08.00 - CHANGE: Auslagerung Gebaeudebeschreibungen in "gebaeude.tab" - FIX: X-Windows+dirty rectangle list jetzt auch in 16/32 bit - FIX: Redraw der Iconleiste nur wenn nötig - FIX: Kennfarbenumschaltung zeichnet ganzes Bild neu 19.08.00 - NEU: Altersangabe für Fahrzeuge - CHANGE: vehikel_t etwas überarbeitet - CHANGE: Berechnung von Restwert für verkaufte Fahrzeuge (ca. 70% nach erstem Jahr) 17.08.00 - FIX: Gebäudeindex für Gebeudearrays der Stadt wird in renoviere richtig geclipped - FIX: bug aus fahre() entfernt, der durch die Koordinatenumstellung verursacht worden war, eigentlich ein tippfehler - NEU: Vehikel können im Depot verkauft werden 16.08.00 - dirty rectangle list vervollständigt - FIX: maximal 8 Loks und 32 Fahrzeuge können in einem Dept eingelagert werden. 14.08.00 - dirty rectangle list weitergebaut 13.08.00 - dirty rectangle list weitergebaut - viele Umstrukturierungen - koords wo es sinn macht - einige vereinheitlichengen bei schreibweisen (setze) 12.08.00 - dirty rectangle list begonnen 09.08.00 - CHANGE: Züge fahren jetzt immer bis zum Bahnsteigende 07.08.00 - FIX: Stadt baut Gebäude nur auf Feldern ohne Besitzer. 04.06.00 - 32 Farben in der Palette sind jetzt zusätzlich verfügbar - GUI für farben aus simgui getrennt und nach gui verschoben 02.08.00 - generisches Makepak für alle Plattformen - Doppelte Info-Fenster für Haltestellen abgeschaltet 30.07.00 - FIX: Beim Trennen einer Ringstrecke wird jetzt die Blockbelegung richtig ermittelt 29.07.00 - Bugfixes - Riffe für unbefahrbares Wasser 26.07.00 - Abschliessende Arbeiten am Fahrplandialog - Einbau von Olufs Fähre 24.07.00 - Weitere Arbeit am Fahrplandialog - CHANGE: Fahrzeuge haben keinen eigenen Fahrplan mehr nur noch Zeiger auf den Convoi-Fahrplan - CHANGE: anderes Design für Fenstertitelleisten 23.07.00 - Weitere Arbeit am Fahrplandialog - Weitere Anpassungen für SDL - CHANGE: Statusanzeigende Buttons werden jetzt gedrückt dargestellt - CHNAGE: erweitertes event-handling für Info-Fenster 20.07.00 - Beginn Fahrplandialog (Fenster, Anzeige Fahrplan) 19.07.00 - Anpassungen für SDL (BeOS) 16.07.00 - FIX: start von convois mit nur einem Fahrzeug funktioniert wieder - CHANGE/FIX: Passagiererzeugungsrate pro stadt ist jetzt von der Stadtanzahl unabhängig, liegt bei ca. 1/4 der vorherigen Passagiererzeugungsrate; das limitiert jedoch die Stadtanzahl auf 64 Staedte insgesamt. 15.07.00 - FIX: Beim Absenken auf Wasserniveau werden alle Objekte des Planquadrates gelöscht 12.07.00 - CHANGE: Neues Gras - FIX: verbessertes/effizienteres clipping fuer display_img 09.07.00 - FIX: Softzeiger hinterlaesst keine Abdruecke mehr auf der Iconleiste - CHANGE: Gewinn wird jetzt nach route(max_n)-route(0) berechnet - FIX: Fahrplaene fuer Trabantenfahrzeuge werden vom Erstfahrzeug uebernommen 08.07.00 - Screenshot feature - FIX: Unzerreisbare Züge 06.07.00 - FIX: Verbesserung der Landschaftzeigerpositionierung vor allem bessere Platzierung des Signalzeigers - FIX: Wenn das Infofenster eines Fahrzeugs offen ist und man das Fahrzeug in ein Depot schickt erfolgt kein Absturz mehr - FIX: Signale entfernen nochmals korrigiert - FIX: Schienen entfernen nochmals korrigiert 05.07.00 - Fehlersuche in der Blockstreckenverwaltung - FIX: Bahnhoefe koennen nur auf eigenen Schienen gebaut werden 03.07.00 - FIX: laden kleinerer Karten jetzt möglich - CHANGE: Signale werden jetzt immer paarweise entfernt - Eddies Korrekturen zur en.tab eingefügt 29.06.00 - FIX: Entfernen von Privatfahrzeugen verboten - FIX: Depotnamen werden wieder i18n - Ricks engl. Stadtnamen eingebaut 28.06.00 - Finnische Übersetzung eingebaut - Sprachnamen stehen jetzt als 1. Zeile in den Sprachfiles 24.06.00 - FIX: Laden und Speichern des Warenziels, Erzeugung einer Warenzielkopie bei Teilen eines Warenpaketes - FIX: Waggons bekommen hat_fracht_bild wieder gesetzt, war Fehler im Vehikelbauer, fracht.is_empty() wird bei waggons richtig berücksichtigt. - FIX: Reliefkarte: Klick in die unteren 16 Zeilen funktioniert jetzt wieder - FIX: Lieferung von Waren mit Eingangsindex != 0 and Fabriken funktioniert jetzt wieder. - FIX: Baeume und Wolken rufen kein delete im step() auf. Das delete wird jetzt vom Planquadrat übernommen 23.06.00 - Passagiererzeugung in Fabriken - Passagiererzeugung in Staedten beruecksichtigt Fabriken - Passagiererzeugung in Staedten beruecksichtigt Ausflugsziele - Passagiererzeugung in Ausflugszielen - FIX: Kohlekraftwerk hatte ausgang[0] nicht allokiert - FIX: Warenendziel kann von Warenziel(Haltestelle) unterschiedlich sein, Vereinheitlichung verursachte Fehler im Routing. - FIX: Postgebaeude auf Fundament konnten den Boden nicht als Haltestellenboden setzten. Verzicht auf Fundament. 22.06.00 - FIX: Stadnamen internationalisiert - FIX: Haltestellennamen internationalisiert - neuer Fenstertyp w_info_autosize 21.06.00 - FIX: Bei der Teilung von Waren an Haltestellen werden die Zielkoordinaten der neuen Ware neu erzeugt - Speicherlöcher im Warenrouting behoben - Auslieferung von Waren an Fabriken funktioniert wieder 20.06.00 - Routing für Passagiere und waren funktioniert im Prinzip - CHANGE: altes, quotenbasiertes Passagiersammeln wurde aus dem Programm entfernt - Verdacht auf Speicherlöcher im Warenrouting - Erzeugung von Waren mit Zielen funktioniert jetzt im Prinzip 19.06.00 - Passagiererzeugung in den Städten begonnen - Stadtinfo-Fenster begonnen 18.06.00 - Ziele für Waren und Passagiere - Neue GUI-Hierarchie - CHANGE: Map-Window wurde zu einer eigenen Klasse 17.06.00 - Ziele für Waren und Passagiere, Ziellisten für Haltestellen - CHANGE: Haltestelleninfofenster werden über Boden geöffnet 15.06.00 - Interface comparable_t fuer ADT Menge - FIX: Plastiktransporter transportiert jetzt auch Plastik 14.06.00 - ADT "Menge" begonnen - CHANGE: Boden hat jetzt pointer auf Haltestellen - CHANGE: Haltestellen werden jetzt über Boden gesucht und gefunden - FIX: Boden hat virtuellen Destruktor, der Boden von Haltestelle abmeldet. 13.06.00 - FIX: Windows-Version laden/speichern funktioniert jetzt - Neues Statdgebaeude: Kirche 11.06.00 - Schiffe, Schiffsrouten - Tests und Bugfixes für Schiffe - FIX: laden der Fahrzeugtabelle lädt letztes Fahrzeug richtig - FIX: statt aussteigender Passagiere fahren die mitgefahrenen Pax 10.06.00 - Auslagerung der Fahrzeugdaten für alle Fahrzeugtypen vereinheitlicht - Bilder für Schiffsdepots + Zeiger - Schiffdepotwerkzeug - Schiffdepots - Schiffe (ungetestet) - Performanceoptimierungen in der Grafik 08.06.00 - Auslagerung der Fahrzeugdaten fertig - Bilder für Schiffsdepots begonnen 07.06.00 - Auslagerung der Fahrzeugdaten in vehikel.tab begonnen 04.06.00 - Erste Docks (Bilder + Werkzeug) 01.06.00 - FIX: top_win implementiert - Werkzeugwaehler Fenster 28.05.00 - Erste Version mit winallegro Einige Anpassungen an Windows-Spezifika - FIX: INT_CHECK wird in step jetzt nur noch alle 1024 planquadrate aufgerufen - step() von boden wird nur gerufen, wenn der boden will_step gesetzt hat 25.05.00 - FIX: Signale melden sich beim entfernen immer bei der Block- strecke ab. - Zugbilder um je 2 Pixel verbreitert, Depots angepasst 25.05.00 - Neue Diesellokomotive 22.05.00 - Lagerhaeuser zur Zwischenlagerung von Waren. 19.05.00 - Umstellung der Blockstrecken von Zug auf Wagenzählung macht Blockstreckenmanagement robuster - Info-Fenster der Signale zeigen jetzt die Blockbelegung an 17.05.00 - FIX: Brücken und Tunnels sind wieder befahrbar - Erzanhänger (für LKW) fertig 13.05.00 - FIX: Bug bei Entfernen von Brücken/Tunnels entfernt - FIX: Entfernen von Dingen ist wieder möglich 12.05.00 - FIX: Fahrzeuge fahren nicht mehr ueber Wiesen, falls der Verkehrsweg zwischendurch entfernt worden ist. - FIX: Verkehrsteilnehmer werden nur noch auf geraden Strassen erzeugt - FIX: Karte ist nach Start auf einer Stadt zentriert - Manfreds Denkmal eingebaut 11.05.00 - Baeume ueberarbeitet - Einige Haeuser ueberarbeitet - Erzbergwerk ueberarbeitet 10.05.00 - Alternative Landschaftserzeugung mit "Perlin Noise" 08.05.00 - Manfreds Staubkesselwaggon eingebaut - Autofabrik verbraucht jetzt Kunststoff - Fabrikfenster führt max. Prod jetzt separat auf 30.04.00 - Convois haben jetzt Zaehler fuer Jahresgewinn 29.04.00 - FIX: Schienentunnel/-bruecken entfernen funktioniert jetzt 24.04.00 - FIX: erstes/letztes wird bei Einlagerung in Depot geloescht - FIX: Halt mit/ohne beladen verhaelt sich jetzt gleich -> Zuege fahren vollstaendig in Bahnhoefe ein 23.04.00 - FIX: sync_steppable hat virtuellen destruktor - FIX: Depots ermitteln Fahrzeugnummern besser ??.04.00 - Umstellung DOS Version auf Allegro - Umstellung DOS Version auf DJGPP 2.03 18.04.00 - FIX: Land anheben/absenken unter Brücken ist verboten - FIX: es laesst sich nur noch ein kartenfenster oeffnen - FIX: Schienen unter Bruecken bauen laesst Bruecken intakt - FIX: moegliche Fehlerquellen beim Verbinden von Blockstrecken und beim Bau von Signalen in Ringstrecken korigiert 15.04.00 - FIX: delta_t bei asynchronem step wird jetzt richtig berechnet - FIX: int route.cc INT_CHECK Aufrufe eingefügt -> weniger ruckeln - CHANGE: markierungen von grund_t in karte_t verlegt - CHANGE: step steppt nur noch jedes 15. Objekt pro Durchlauf - CHANGE: step in grund_t wieder virtuell, ohne if 14.04.00 - FIX: laden und speichern von beliebig grossen Karten moeglich - Einstellung fuer Scrollgeschwindigkeit - Einstellung fuer Verkehrdichte 13.04.00 - FIX: display_img clipped jetzt oben wieder richtig - FIX: verkehrsteilnehmer_r "springen" nicht mehr - Vorbereitung "welt gui" zur interaktiven kartenwahl - Einstellungsobjekte für Spieleinstellungen 12.04.00 - Umbau des fabrikbauer_t fuer sinnvolle Platzierung der Industrien - kleine optimierung in display_img und display_color_img 09.04.00 - Umbau von vehikel_t zur Ableitung einer Klasse fuer den Individualverkehr 06.04.00 - Vorbereitungen fuer waehlbare Kartengroessen - Speichereinsparungen am Blockmanager (Markierungen als char statt bool) 05.04.00 - Speichereinsparungen an ding_t und grund_t (ca. 30%) 02.04.00 - Fahrzeuge werden auf der Reliefkarte angezeigt 01.04.00 - Umbau Fahrplaneingabe auf Werkzeugschnittstelle - KIs ebnen Land und entfernen Hindernisse beim Bahnhofsbau - FIX: Bahnhof entfernen setzt Blockstrecken richtig - FIX: Trennen von Blockstrecken verbessert - FIX: Fabriknamen werden sprachunabhängig gespeichert - FIX: Karte huepft nicht mehr bei erneutem Scrollen - FIX: KI orientiert sich am Lagerbestand bei Streckenplanung - FIX: "Gekaufte" Felder koennen nicht in der Hoehe veraendert werden 26.03.00 - Kunststoff- und Stahl-LKWs eingebaut - Manfred's Stahltransporter eingebaut - FIX: Convois werden aus Convoiliste entfernt, wenn sie ein Depot betreten - FIX: Gebeude koennen jetzt auch null-Texte speichern - FIX: Signale umstellen kostet kein Geld 25.03.00 - FIX: Tunnelmündungen erhalten richtigen Blockstreckenzeiger - FIX: Brueckenauffahrten erhalten richtigen Blockstreckenzeiger 22.03.00 - Verbesserte Grafikperformnce 30-40% schneller - Bessere Komprimierung des Daten.pak 21.03.00 - FIX: Zeiger laden funktioniert jetzt 19.03.00 - FIX: Signale aufstellen wird besser geprueft - FIX: KI berechnet gewinn jetzt richtig 18.03.00 - Helligkeits und Farbeinstellungen werden jetzt gespeichert - FIX: Brückenbau nur an Strassen/Schienenenden zugelassen - FIX: Fehlermeldung "... muss an einfachem Hang ..." uebersetzt - FIX: Zeiger fuer Brueckenbau stimmen jetzt - GUI fuer Laden, Speichern und Beenden - FIX: Kosten fuer Baeume faellen und Haeuser abreißen werden jetzt berechnet 17.03.00 - Neue Blockstreckenverwaltung abgeschlossen - Sehenswuerdigkeiten begonnen 13.03.00 - Mauszeiger Kann nun auch auf Linien plaziert werden Das ist für die neue Signalsetzfunktion notwendig - Ricks Korrekturen der en.tab integriert 12.03.00 - Blockstreckenverwaltung neu implementiert (Beginn) 11.03.00 - FIX: KI-Zustand wird gespeichert - Mehrere Farbpaletten zur Auswahl - Mehrere Helligkeitstufen zur Auswahl - FIX: Haltestelleninfo mit vielen Fabriken funktioniert jetzt - FIX: Fenster werden immer vollständig sichtbar geoeffnet 09.03.00 - FIX: Bahnhofsgrund verarbeitet ribis jetzt korrekt 04.03.00 - X-Windows: Pointer beim Scrollen unsichtbar - FIX: Entfernen fremder Strassen/Schienen nicht mehr moeglich - Fahrzeuge können in Depots zurückgeschickt werden - Hausbeschreibungen begonnen 28.02.00 - Verhindern mehrfacher Fahrplaneingabe. 27.02.00 - Anklicken des Bildes im Infofenster springt zum Objekt 26.02.00 - Französiche Übersetzung integriert - Ricks Bauwerke integriert - Fabriken haben jetzt info über abgegebene warenmenge - KI nutzt Warenabgabeinfo zur Streckenplanung - X-Windows version kann Pointer beim Scrollen im Window fixieren 21.02.00 - Bugfixes fuer KI -> tonnenweise Bugfixes!!! 20.02.00 - Baubaer kann jetzt lokale Optimierungen an Strecken vornehmen 19.02.00 - KI baut Strassen und Schienen automatisch - KI baut Bahnhöfe und Frachthöfe - Bugfixes fuer Schienenbau und Strassenbau - Bugfixes fuer Tunnel und Brueckenbau - Bugfix fuer zu viele sync_wolken 16.02.00 - Automatischer Brueckenbau fuer Baubaer funktioniert - KI schätzt Anzahl benötigter Fahrzeuge fuer Route 15.02.00 - Automatische Strassenueberbrueckung bei Schienenbau beggonnen 14.02.00 - Automatische Strassentunnelung bei Schienenbau begonnen 13.02.00 - Gebaeudenamen werden mit gespeichert - Bushaltestellen sind jetzt wg. Sichtbarkeit zweigeteilt - Frachthoefe sind jetzt wg. Sichtbarkeit zweigeteilt - Verbesserte Bilddarstellung im Infofenster - Verbesserung der KI; Strassenfahrzeuge werden 'sinnvoll' eingesetzt 12.02.00 - Pipelines prinzipiell fertig - Automobile fahren jetzt hintereinander - Automobile halten jetzt an belegten Kreuzungen 10.02.00 - Pipelines begonnen - Refactoring von simdings in Paket dings 09.02.00 - Entfernen von Strassenbruecken moeglich - Entfernen von Eisenbahnbruecken vorbereitet - Iconleiste wird nur gezeichnet wenn nötig (Performance +) 05.02.00 - Raffinerien - Maximalhoehe fuer Berge - Anzeige des sichtbaren Bereiches in der Reliefkarte - Fabriken: Produktion vom Lagerbestand abhängig - Masseinheiten fuer Waren - asynchrones Routing fuer convois - Reliefkarte zeigt sichtbaren Ausschnitt an - Neuer Bus 04.02.00 - Manfreds Oeltanker eingebaut - Raffinerien angefangen 30.01.00 - Grafiken überarbeitet - Neue timer-routinen beruecksichtigen wirklich verbrauchte zeit. - Strassen in den Staedten mit Gehweg - Kosten fuer Fahrzeuge werden verrechnet 29.01.00 - weitere i18n - makepak tool von simu getrennt - tooltips für Icons - Oelfeld - Synchrone Altersberechnung für Baeume und Gebaeude - Tabellen statt switch fuer Baeume und Vehikel 28.01.00 - FIX: Brücken "ins nichts" bauen stürtzt nicht mehr ab - Fenster aus Hauptfenster herausschiebbar 23.01.00 - Weitere i18n - Convoi fahren jetzt nach halt an Signal langsam an - aktuelle Sprache wird gespeichert 22.01.00 - "Neuer Fahrplan" Funktion für Convois - Erste Schritte zur Internationalisierung 20.01.00 - Overlay-Technik für Strassen und Schienen implementiert 19.01.00 - Fenster können automatisch nach Zeitspanne geschlosen werden 15.01.00 - FIX: Schienen über Schienen bauen kostet kein Geld - FIX: Schienen über Schienen bauen entfernt Züge nicht mehr - FIX: A* war fehlerhaft - Automatischer Brückenbau möglich - Darstellung von Schienen in der Reliefkarte verbessert - Makefile verbessert - Route ist jetzt eigenes Objekt, d.h. Routing ohne Fahrzeuge wird möglich - Passagiere steigen nicht mehr alle gleichzeitig aus, statistisch steigen bei n Haltestellen K/(n-1) Passagiere pro Haltestelle aus. - Haltestellenquote bezieht nun Anzahl wartender Pax/Post mit ein - FIX: Name der Fabriken weren jetzt gespeichert/geladen 14.01.00 - Verbesserungen im Stadtbau - FIX: Strassenbau in den Staedten erzeugt keine Phantomabzweigungen mehr - FIX: Richtige Positionierung der "Schwebetexte" - Wolken werden jetzt synchron entfernt 13.01.00 - Tunnels fertig - Convois bei "Halt ohne umdrehen" verbessert - Neue Diesellokomotive 10.01.00 - Tunnels; die Bilder für die Tunnelmündungen fehlen noch 08.01.00 - FIX: Laden/Speichern von Convois macht anz_ready persistent - SList für synchrone Objekte, damit beliebig viele sync. Obj. mgl. - Wolken als synchrone Objekte - FIX: Start-Button im Depot; Groesse wurde falsch berechnet - FIX: Frachtbildinformation wird jetzt gespeichert - Fehlermeldungen und Reaktion auf "OUT OF MEMORY" - Fehlermeldungen für Laden/Speichern 06.01.00 - Informationen für Fahrzeuge - Depotfenster überarbeitet 05.01.00 - Baugruben für Neubaus - FIX: Kennfarbe und Zeit speichern und laden - FIX: Rauchberechnung in Convoi, Zeitabhängig statt Geschwindigkeit - FIX: trennen von Ringstrecken jetzt moeglich 04.01.00 - Stahlwerk - Kohlebergwerk - FIX: Name von Haltestellen im Info-Fenster richtig - FIX: Frachtbild-Flag wird jetzt bei Fahrzeugen richtig gespeichert - Erzlaster - Dieselrauch fuer Dieselfahrzeuge 03.01.00 - Beschleunigung der Zeichenfunktionen - Animiertes Wasser - FIX: Lokomotive mit Fracht - FIX: Haltestellenkonstruktor - Wartezeitberechnung für Hauptschleife als PI-Regler 29.12.99 - Neues Bahnhofsdach - Depotinhalt wird gespeichert - Update der Reliefkarte nach laden - Neuer Haltestellenzeiger - Beschleunigung/Bremsen für Convois 25.12.99 - Neue Dampflokomotive - Erweiterungen für Laden/Speichern 23.12.99 - Erweiterungen für Laden/Speichern 18.12.99 - Neue Hierarchie für Depots (Bahndepot, Straßendepot) - Neue Hierarchie für Fahrpläne (Bahn, Auto) - Bugfixes für Vehikel, Convoi mit nur einem Vehikel - neues Verzeichnis dataobj 17.12.99 - Blockstrecken mit Brücken funktionieren jetzt (hoffentlich) evtl. Probleme mit Blockstrecken die mittels Brücke über sich selbst hinwegführen - Überarbeitung Brückenbau - Bugfixes für Vehikel beim Übefahren einer Brücke - Tests mit assert() - Berechnung der korrekten Kosten bei Landanheben/-absenken 16.12.99 - Haltestellen entfernen ist jetzt möglich - einige Sicherheitschecks eingebaut - Schatten für Bäume und Häuser 14.12.99 - Erstmals präzise Positionierung des Landschaftszeigers möglich 09.12.99 - asynchrone darstellung möglich - neue Image-Verwaltung ermöglicht kompakteres daten.pak 08.12.99 - Haltestellen mit SList für Grund und Fabriken, damit entfallen die Beschränkungen auf 16 Felder bzw. 16 Fabriken - Verbesserung von move_win durch offset zum Mauszeiger - Kleine Verschönerungen an Gebaeude-Bildchen 05.12.99 - Fahrzeuge mit variablen Geschwindigkeiten - Abbiegen mit nur einem Fahrzeug in der Kurve 04.12.99 - Verbessertes Clipping für Zeichenfunktionen - Verbesserte Window-Darstellung - Verbesserte Schilder-Darstellung - Softscrolling 03.12.99 - Convoi als Vehikel-Delegate 02.12.99 - Verbesserung convoi_t::ready() - Bahndepot 28.11.99 - Erster Versuch eines Dialoges für Zugzusammenstellung 27.11.99 - Algorithmus von Dijkstra zum Strassen/Schienenbau implementiert - Neue Gebaeude: Einkaufszentrum und Fabrik 5 - Automatisches Update der Reliefkarte 24.11.99 - Entfernen von Schienenenden möglich 22.11.99 - Entfernen von Signalen möglich - Entfernen von Scheinenstücken aus der Mitte von Blockstrecken ist möglich 21.11.99 - Shared Memory abschaltbar, neue Kommandozeile: "-net" schaltet Shared Memory ab "-map 34" startet karte 34 - Unterstützung für XWindows 24/32 Bit Farbtiefe (experimentell) - Unterstutzung für XWindows 16 Bit (565, 555) (experimentell) - Dialog für Wahl der Spielerfarben - FIX: Traversierung der Blockstrecken betritt jetzt nur Felder mit Schienen 20.11.99 - FIX: Signale rechts von den Schienen - FIX: Frachthof in Spielerfarbe - FIX: Convoi "ist_fahrend" init - FIX: Posthäuschen und Industrie werden beim Satdbau nicht mehr entfernt - FIX: Symbole in Iconleiste erscheinen jetzt in Spielerfarbe 16.11.99 - Weichen für Schienen 14.11.99 - Bugfix für Suche nach draw.fnt - Bugfix für fehlende "HOME" Umgebungsvariable - Bilder für Erzbergwerke vollständig - Buttons für Infofenster, es fehlt noch der Callback - Trennen/Vereinigen von befahrenen Blockstrecken jetzt möglich - Bugfixes für Blockstreckenverwaltung 13.11.99 - Alternative Push-Technologie als Ersatz für SIGNAL verfügbar - Strassenbau für Städte verbessert - neue größere und besser erkennbare Icons - verbesserte Werkzeuge für den Bau von Bahnhöfen, Bushaltestellen und Frachthöfen: automatische Richtungserkennung 06.11.99 - Blockstrecken jetzt auch für Convois (Züge) 03.11.99 - Bugfixes für Blockstrecken, alle Tests erfolgreich durchlaufen 02.11.99 - Einfach- und Doppelsignale - Bugfixes für Blockstrecken und Signalverwaltung 01.11.99 - Erste Blockstreckenverwaltung und Signale 09.09.99 - Simu jetzt unabhängig von draw - Simu 16 Bit-faehig 02.06.99 - Bild fuer Rathaus - Erzeugen und Verbrauchen von Waren in Fabriken funktioniert jetzt 31.05.99 - Anpassung Haltestellen an neue Waren - Neue Haltestelle Frachthof (n,s,w,o) - Bruecken jetzt auch mit Zug befahrbar 30.05.99 - Parametrisierbare Fabriken statt Klassenhierarchie - Fabrikbauer - Fahrzeugbauer 29.05.99 - Erste Industrie: Nutzwald und Saegewerk - Transportfahrzeug (LKW) fuer Holz 28.05.99 - Breitensuche statt Tiefensuche fuer Fahrwegsbestimmung (neue ADT Queue und Stack) ??? - Bruecken befahrbar 08.05.99 - Brueckenbau moeglich, aber Bruecken noch nicht befahrbar. neues bild "bridges.gif" fuer Tunnel und Brueckenbildchen 01.05.99 - simwin: Mausklick wirkt nur noch auf oberstes Fenster an dieser postion - Einfuehrung eines ADT SList fur bessere Kontrolle der Zugriffe und ohne Groessenbeschraenkung 15.04.99 - Bilder fuer Post, Postzeiger, Postlkw und Postlkwzeiger - neuer Warentyp: Post - grundlegendes Werkzeug wkz_postlinie - Bugfixes fuer vehikel_t beladen() beachtet jetzt warentyp speichern/laden existieren, aber nicht getestet - neue funktion fuer automobil_t set_has_fracht_bild() fuer Fahrzeuge ohne beladene Bilder 10.04.99 - display_img und display_color_img pruefen jetzt xmin < xmax und ymin < ymax (verursacht Abstuerze unter DOS) 02.04.99 - welt->lock sperrt jetzt events; notwendig fuer DOS-version - display_clear() eingespart, welt ist jetzt insel 20.03.99 - Passagierwaggon - korr. Bahnhof Ostwest - Verzicht auf rein virtuelle Methoden -> defensive Programmierung - Bugfix in kann_alle_entfernen(): beachtet jetzt Spieler - Frachthof neu 13.03.99 - Bessere Namensgebung fuer Haltestellen - Busse/Passagiere - Bushaltestelle - Bugfix Fenster verschieben - Passagiererzeugung 10.03.99 - Icons fuer Bahnhof Nordsued und Ostwest - Mauszeiger Bahnhof Nordsued und Ostwest - Grundlegende Namensvergabe fuer Bahnhoefe ("Boberg Bahnhof") - FIX: Stadtpositionen werden jetzt initialisiert - Grundlegendes Konzept fuer Bahnhofsbau: Module Problem bei Luecke -> neuer Bahnhof angenommen 07.03.99 - Icons fuer Raise, Lower, Abfrage und Remover - Mauszeiger: Abfrage, Remover - Eigene Datei fuer alle Werkzeuge -> Kapselung - Aenderung an der KI, aber Streckenplanung immer noch mangelhaft 06.03.99 - Neue Mauszeiger: Strassenbau, Schienenbau, Raise, Lower - Bessere Mauszeigerpositionierung - Icons fuer Reliefkarte, Strassenbau und Schienenbau written by Hj. Malthaner simutrans-124.5/simutrans/license.txt000066400000000000000000000156221520675460500200020ustar00rootroot00000000000000This is the simutrans source code. (c) 1997-2004 Hj. Malthaner (c) since 2005 The Simutrans Team Simutrans source code is provided by the current simutrans team (team64@simutrans.com) as copyright holder under the artistic license. This includes the translations from: https://translator.simutrans.com/ the documentation, images and .dat files included in the source distribution, the midi files and the fonts with the exception of the m+10r.bdf, which is taken from the M+ font project: https://mplus-fonts.osdn.jp/ ----------------------------------------------------------------------------- Third Party Libraries When downloading Simutrans, additional third party libraries may be included for compatibility purposes in the "lib" directory. Such files are not covered by the Artistic License. The list of included libraries and a link to their license text is listed below. libFLAC https://raw.githubusercontent.com/xiph/flac/master/COPYING.Xiph libbz2 http://sourceware.org/git/?p=bzip2.git;a=blob_plain;f=LICENSE;hb=HEAD libfluidsynth https://raw.githubusercontent.com/FluidSynth/fluidsynth/master/LICENSE libfreetype https://raw.githubusercontent.com/aseprite/freetype2/master/docs/LICENSE.TXT libglib https://raw.githubusercontent.com/GNOME/glib/main/COPYING libminiupnpc https://raw.githubusercontent.com/miniupnp/miniupnp/master/LICENSE libogg https://raw.githubusercontent.com/gcp/libogg/master/COPYING libpcre https://raw.githubusercontent.com/vmg/libpcre/master/LICENCE libpng https://sourceforge.net/p/libpng/code/ci/master/tree/LICENSE?format=raw libsndfile https://raw.githubusercontent.com/libsndfile/libsndfile/master/COPYING libvorbis-{enc} https://raw.githubusercontent.com/xiph/vorbis/master/COPYING libzstd https://raw.githubusercontent.com/facebook/zstd/dev/COPYING ----------------------------------------------------------------------------- The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: * "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. * "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. * "Copyright Holder" is whoever is named in the copyright or copyrights for the package. * "You" is you, if you're thinking about copying or distributing this Package. * "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) * "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End --------------------------------------------------------------------------- This means, translated to ebay terms: You may not charge for the program, if you charge shipping fees. simutrans-124.5/simutrans/license_squirrel.txt000066400000000000000000000021621520675460500217230ustar00rootroot00000000000000Copyright (c) 2003-2017 Alberto Demichelis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------- END OF COPYRIGHT simutrans-124.5/simutrans/music/000077500000000000000000000000001520675460500167315ustar00rootroot00000000000000simutrans-124.5/simutrans/music/01-Simutrans-Main-Theme.mid000066400000000000000000000630621520675460500235600ustar00rootroot00000000000000MThd`MTrkeCopyright (C)2006 shunter~ JAZ2 2006.10.06Q /MTrk%shunter 2006 JAZ2(My Song  Simutrans AGeneral MIDI @H !d/MTrk!JAZ2chord "chord @cGeneral MIDI` @@E+<+@+*E<@<+@+E+*@<EE+<+@+*<E@@+E+<+*<E@@@ @@EFC+>+;+*>C;>+;+C+*>C;C+;+>+*>C;;+C+>+*C@ @>@;C>FA+<+E+*<EA<+E+A+*A<E<+E+A+*<EAE+<+A+*E@ @E<AA@F@+;+D+*D;@@+D+;+*D@;;+@+D+*;@D@+;+D+*D@@@ @;@DFE+<+@+*<E@E+@+<+*<E@E+<+@+*<E@E+@+<+*E@< @@@E@FE+>+B+*B>EB+>+E+*BE>>+E+B+*>EBE+>+B+*EB @>B@E@FE+>+B+*B>E>+B+E+*>BEB+>+E+*B>EB+>+E+*>B@B @E@EFB+>+E+*>BEB+E+>+*EB>B+>+E+*BE>E+>+B+*B@B>EE@ @F@+C+<+*@C<C+@+<+*C<@C+<+@+*<@C<+@+C+*@C< @@@C@FC+@+<+*<C@@+C+<+*C@<C+@+<+*@<CC+<+@+*C @C@<@@@F@+;+D+*@;D;+@+D+*@;D;+D+@+*@D;D+;+@+*;@D @@@D@F;+@+D+*;@D@+D+;+*D@;;+D+@+*D;@@+;+D+*@@DD@ @;@FD+@+;+*;D@D+;+@+*@;D;+@+D+*D;@D+;+@+*@D@@;D@ @FD+@+;+*@D;@+D+;+*;D@;+D+@+*;D@;+D+@+*@@@ @D@D;FA+E+<+*E<AA+<+E+*<AE<+E+A+*<EA<+E+A+*E@ @A@AE<FE+<+A+*<EAA+<+E+*<EAE+<+A+*EA<<+E+A+*EA< @A@E@E+<+A+*AE<<+E+A+*<EAA+E+<+*E<A6<+A+E+*A<E<+E+A+*<EA<+E+A+*AE<0 @A@E@E+<+A+*<AEA+E+<+*E<A<+A+E+*<AE6<+E+A+*AE<E+<+A+*A<E<+A+E+*A<E0E@A@ @;+>+C+*;>C;+>+C+*>C;C+>+;+*>;C6C+>+;+*>;CC+>+;+*>C;>+C+;+*>;C0>@C@ @>+;+C+*>C;;+C+>+*;C>C+>+;+*>;C6;+C+>+*;C>>+;+C+*C;>;+C+>+*;C>0 @>@C@>+;+C+*;C>C+>+;+*>C;>+;+C+*>C;6;+C+>+*;C>;+>+C+*C>;>+C+;+*C;>0>@ @C@;+C+>+*C;>C+;+>+*C>;C+;+>+*C;>6C+>+;+*>C;>+C+;+*>C;;+C+>+*>;C0>@C@ @F@+=+E+*@=E@+=+E+*=@E=+E+@+*@E==+E+@+*@@=@EE@ @F@+=+E+*@=EE+=+@+*E=@=+@+E+*@E=E+=+@+*@=E @E@@@FE+@+=+*@E=@+E+=+*E@=@+E+=+*@=EE+@+=+*=E@@@E@ @FE+=+@+*@=E@+=+E+*@E==+@+E+*=@EE+=+@+*=@ @E@@E@FE+B+>+*B>EE+>+B+*>BEB+E+>+*>EBE+B+>+*>E@B@ @BEFB+>+E+*EB>B+E+>+*EB>B+E+>+*E>BB+>+E+*E@>B@B @EFB+>+E+*B>E>+B+E+*B>E>+E+B+*E>BE+B+>+*E> @E@BB@FB+E+>+*>EBB+E+>+*E>B>+B+E+*BE>B+>+E+*>BE @B@E@FC+<+@+*<C@<+C+@+*@C<@+<+C+*@C<C+<+@+*<@CC@ @@@F<+C+@+*@<C<+C+@+*@<CC+@+<+*C@<<+C+@+* @C@<@@@CFC+@+<+*@C<<+C+@+*@<CC+@+<+*<C@C+<+@+*@@C@ @C<@F@+C+<+*@C<C+@+<+*C<@C+<+@+*@C<@+C+<+* @@@C@C@<F@+<+E+*E@<<+E+@+*E@<<+@+E+*<@E<+@+E+*@@ @E@<@EF;+C+>+*;C>;+C+>+*>;C;+>+C+*C>;C+;+>+*>@C@ @;C>FE+<+A+*<AEA+E+<+*AE<<+E+A+*AE<<+A+E+*<EA@E@A @F@+D+;+*;D@D+@+;+*;@D;+@+D+*@;D@+;+D+*;@DD@ @@@F@+E+<+*@E<E+@+<+*@E<@+<+E+*E@<<+@+E+*E@@E< @@@FE+B+>+*BE>>+E+B+*>EBB+E+>+*E>BE+>+B+*E@B> @B@EF>+B+E+*E>BB+>+E+*BE>>+E+B+*B>EE+>+B+*E>BE@ @B@FE+B+>+*EB>>+B+E+*BE>E+B+>+*BE>>+E+B+*EB@ @>E@BF<+@+C+*<C@@+C+<+*<@CC+@+<+*@C<<+@+C+*@ @@@C<C@FC+@+<+*C@<<+@+C+*<@C@+C+<+*@<C@+C+<+*@ @C<C@@@F;+@+D+*D@;;+D+@+*D@;@+;+D+*@;D@+;+D+*;@ @@@DD@F@+;+D+*D@;;+D+@+*@;DD+;+@+*@D;D+;+@+*@@ @D@;D@F@+;+D+*;@DD+;+@+*;D@;+D+@+*@;D@+D+;+*D@@@; @D@FD+@+;+*D@;;+@+D+*;@D;+@+D+*;@D;+D+@+*D@@@@;D @F<+A+E+*EA<A+<+E+*EA<E+<+A+*<AEE+<+A+*EA<E@ @A@F<+A+E+*E<AE+<+A+*<EAA+E+<+*A<E<+A+E+*E@A@<EA @E+A+<+*<AEA+E+<+*E<AE+A+<+*<EA6<+A+E+*E<AE+A+<+*EA<A+<+E+*<EA0E@ @A@<+A+E+*EA<E+A+<+*<AE<+E+A+*E<A6A+E+<+*EA<E+<+A+*<EA<+A+E+*A<E0A@ @E@>+C+;+*C;>C+>+;+*>C;;+C+>+*>;C6;+C+>+*>;C>+C+;+*;>CC+;+>+*;>C0C@>@ @;+C+>+*;C>>+C+;+*>C;C+>+;+*C>;6C+;+>+*C>;;+>+C+*>;CC+>+;+*C>;0C@ @>@C+;+>+*C>;;+C+>+*>C;C+;+>+*;C>6C+;+>+*>;C;+C+>+*;C>;+>+C+*>C;0C@>@ @C+;+>+*;C>;+>+C+*;C>>+;+C+*C>;6>+C+;+*;C>>+;+C+*C>;C+;+>+*;>C0C@>@ @FE+=+@+*@=E@+=+E+*@E=E+=+@+*@=E=+@+E+*E=E@ @@@@F@+E+=+*E@==+@+E+*=E@=+@+E+*E@=E+@+=+*=E@ @E@@@FE+=+@+*E=@E+@+=+*@=EE+=+@+*E=@E+=+@+*EE@ @@@=@F@+=+E+*=@E@+=+E+*=E@@+E+=+*E@=@+E+=+*E@@@ @E=@F>+E+B+*BE>E+>+B+*B>EB+>+E+*B>E>+B+E+* @EB>B@E@FE+B+>+*EB>B+>+E+*>BEB+>+E+*EB>B+E+>+*> @BB@E@EFE+B+>+*B>EB+E+>+*EB>E+>+B+*>EBE+B+>+* @E@EBB@>FE+>+B+*E>B>+E+B+*>BE>+E+B+*>BEB+>+E+*B@ @E@BE>FC+<+@+*@<C@+C+<+*<C@<+@+C+*C@<C+<+@+*C@ @<@C@@F<+@+C+*@C<<+C+@+*@<C<+@+C+*<C@<+C+@+*CC@ @@@<@F<+@+C+*@C<C+<+@+*<@C@+<+C+*C<@@+<+C+*@@C@@<C @F@+<+C+*@<CC+@+<+*<C@<+@+C+*C@<C+@+<+*@@<@C @C@F<+E+A+*EA<E+A+<+*A<E<+E+A+*<EA<+A+E+*A@E@ @<AEF<+A+E+*AE<<+E+A+*AE<A+E+<+*E<AA+E+<+*<AE @A@E@FE+A+<+*<EAE+A+<+*A<E<+A+E+*A<EE+<+A+*<A@ @E@EAFE+<+A+*AE<<+A+E+*EA<<+E+A+*A<EE+A+<+*<EE@A@ @AF<+C+@+*C@<C+<+@+*@C<C+<+@+*C<@C(<(@(*C@ @@@C@<F@<C*<@CC@<*@<CC < @ *@<C<C@*@@@C@ @<CFC;>*>;C>;C*;C>>C;*>C;>;C*;> @>@CC@F;C>*>;C>;C*;C>C>;*C>;>C;*C>;/MTrk h0JAZ2 @General MIDIAm jlead`H*HE(EL*LJ*JH*0HJ(JG(G0M(MM*MO*OM*ML*L0P(PL(L0QTQ P*PQ*QP*PQ*QPDM*PQ*0QQTQ P*PN*NJ(0JJ(JITI JTJ0 BP0B0H*HC*CE*EG*GH*HC*CEEm*EG*G0@(@C(0CP(PP*PS*SQ*QP*0PSPS0L*LNNPPLTL L*LNNPPLEMTL S~SQ*QPPQQPPNNLTL HTH HTH H*HF*FE*EC*CETE ETE E*EC*CA*A@*@0H|H$F*FE*0EFTF ETE C`GMHC0JJHHGGCCJJHHGGCCJJHHGGCCJGMJHHGGCC0JJHHGGCCJJHHGGCCJJHHGGCCJEm7JHHGGC0CS*ST*TV*VX*XV*VT*TS*ST*T0S*SQ*QO*OQ*QS(S0LTL PTP QTQ P*PL*L0QPQ0Q|Q$P*PQ*QS(SUTU STS JJIIJ~JJJEEGGEEBBEEG*GE DM0E0J*JN*NQ*QJ*JN*NQ*QJ*JN*NLTL ITI J(JH*HT*TS*SQ*QO*OQ*QOCM*OM*ML*LM*ML*LJ*JH(0HH(HGTG HTH0 @P@00Q*QL*LM*MO*OL(L0O~OM*MJ(0JM(MM*MO*OM*ML*L0P(PL(L0QTQ P*PQ*QP*PQ*QPDM*PQ*Q0QTQ P*PN*NJ(J0J(JITI JT0J BPB00L(LL*LJJLLM*MQ*0QO~OM*ML(0LS*SQ*QPTP Q~QP*P0LPL0L(LL*LKKLLNNPPNNLLP(PP*PNNPPFdimQQSSQQPPM*MOOQQMTM M*MOOQQFMMTM T~TR*RQQRRQQOOMFMTM0 MTM E*EF*FH(H0JTJ H*HF*FE`GMH0EO*OS*SV*VO*OS*SV*VO*OS*S0QTQ NTN O(O0J(JJJGGHHJJH*HG*G0C~CB*BC`AMHC0ITI LTL Q(0QPTP NTN L(LQTQ S*SU*UV*VU*USAM*SQ*QX*XV*VU*US*SQ(QQ~QJTJ L*LN*NO*OQPQ0J*JQ*QJTJ J*JQ*QA7JTJ V*VUUSSQ*QO*ON*NL*LGMJTJ H*HT*TS*SQ*QO*OQ*QOCM*OM*ML*LM*ML*LJ*JH(H0H*HG*GH*HG*GH(H0<P0<0M*MO*OQ*QM*MO*OQ*QFMM*MT*0TY~YR*RQ`Dm7H0QMMLLJJHHE|E0$ETE ETE C*CA0CMNA0L*LM*MO*OHCMHH|*0HLx*LMt*MOp*OTlD7TT\*T0JX*JOT*ONP*NOL*OQH*QOD*ON@*NL<*0LJ8PJ/MTrk JAZ2base @[ ZGeneral MIDI`-P @*--P*-4P*4-PT- 4P*4-P*-4P*4 @7P*77P*7>P*>7PT7 >P*>7P*7>P*> @ )P*))P*)0P*0)PT) 0P*0)P*)0P* 0 @4P*44P*4;P*;4PT4 ;P*;4P*4;P*; @ -P*--P*-4P*4-PT- 4P*4-P*-4P* @ 42P*22P*29P*92PT2 9P*92P*29P* @9&P*&&P*&-P*-&PT& -P*-&P*&-P* @ -&P*&&P*&-P*-&PT& -P*-&P*&-P* @-$P*$$P*$+P*+$PT$ +P*+$P*$+P* @+$P*$$P*$+P*+$PT$ +P*+$P*$+P* + @(P*((P*(/P*/(PT( /P*/(P*(/P* @/(P*((P*(/P*/(PT( /P*/(P*(/P* / @(P*((P*(/P*/4PT4 8P*84P*4/P*/ @ (P*((P*(/P*/4PT4 8P*84P*4/P*/ @ )P*))P*)0P*05PT5 9P*95P*50P*0 @ )P*))P*)0P*05PT5 9P*95P*50P* 0 @)P*))P*)0P*00P*05P*55P*50P*00P*0 @ )P*))P*)0P*00P*05P*55P*50P*00P*0 @+P*++P*+2P*22P*27P*77P*72P*22P* 2 @+P*++P*+2P*22P*27P*77P*72P*22P* @2 +P*++P*+2P*22P*27P*77P*72P*22P* 2 @+P*++P*+2P*22P*27P*77P*72P*22P* 2 @-P*--P*-4P*4-PT- 4P*4-P*-4P* 4 @-P*--P*-4P*4-PT- 4P*4-P*-4P* @ 4-P*--P*-4P*4-PT- 4P*4-P*-4P*4 @ -P*--P*-4P*4-PT- 4P*4-P*-4P*4 @ &P*&&P*&-P*-&PT& -P*-&P*&-P* @-&P*&&P*&-P*-&PT& -P*-&P*&-P* @- &P*&&P*&-P*-&PT& -P*-&P*&-P* @ -&P*&&P*&-P*-&PT& -P*-&P*&-P* @ -$P*$$P*$+P*+$PT$ +P*+$P*$+P* @+ $P*$$P*$+P*+$PT$ +P*+$P*$+P* @+$P*$$P*$+P*+$PT$ +P*+$P*$+P*+ @ $P*$$P*$+P*+$PT$ +P*+$P*$+P* @ +-P*--P*-4P*4-PT- 4P*4-P*-4P*4 @7P*77P*7>P*>7PT7 >P*>7P*7>P*> @)P*))P*)0P*0)PT) 0P*0)P*)0P*0 @4P*44P*4;P*;4PT4 ;P*;4P*4;P* @;-P*--P*-4P*4-PT- 4P*4-P*-4P* @ 42P*22P*29P*92PT2 9P*92P*29P* @9 &P*&&P*&-P*-&PT& -P*-&P*&-P*- @ &P*&&P*&-P*-&PT& -P*-&P*&-P* @-$P*$$P*$+P*+$PT$ +P*+$P*$+P*+ @ $P*$$P*$+P*+$PT$ +P*+$P*$+P*+ @ (P*((P*(/P*/(PT( /P*/(P*(/P*/ @ (P*((P*(/P*/(PT( /P*/(P*(/P* @ /(P*((P*(/P*/4PT4 8P*84P*4/P*/ @(P*((P*(/P*/4PT4 8P*84P*4/P* @/)P*))P*)0P*05PT5 9P*95P*50P* @ 0)P*))P*)0P*05PT5 9P*95P*50P*0 @)P*))P*)0P*00P*05P*55P*50P*00P* @0)P*))P*)0P*00P*05P*55P*50P*00P*0 @ +P*++P*+2P*22P*27P*77P*72P*22P* @2 +P*++P*+2P*22P*27P*77P*72P*22P*2 @+P*++P*+2P*22P*27P*77P*72P*22P* @ 2+P*++P*+2P*22P*27P*77P*72P*22P* @2-P*--P*-4P*4-PT- 4P*4-P*-4P*4 @-P*--P*-4P*4-PT- 4P*4-P*-4P*4 @-P*--P*-4P*4-PT- 4P*4-P*-4P* @4 -P*--P*-4P*4-PT- 4P*4-P*-4P* @4&P*&&P*&-P*-&PT& -P*-&P*&-P* - @&P*&&P*&-P*-&PT& -P*-&P*&-P* @-&P*&&P*&-P*-&PT& -P*-&P*&-P* @ -&P*&&P*&-P*-&PT& -P*-&P*&-P* @-$P*$$P*$+P*+$PT$ +P*+$P*$+P* @+ $P*$$P*$+P*+$PT$ +P*+$P*$+P* @+ $P*$$P*$+P*+$PT$ +P*+$P*$+P*+ @$P*$$P*$+P*+$PT$ +P*+$P*$+P* + @)P*))P*)0P*0)PT) 0P*0)P*)0P* @0 )P*))P*)0P*0)PT) 0P*0)P*)0P* 0 @)P*))P*)0P*0)PT) 0P*0)P*)0P*0 @)P*))P*)0P*0)PT) 0P*0)P*)0P*0 @ $P*$$P*$+P*+$PT$ +P*+$P*$+M* @+$I*$$E*$+A*+$=T$ +5*+$1*$+-* @ ++)*++%*+2!*2+T+ 2*2+*+2 * @ 2+ *++*+2*2+T+ 2*2+*+2*2/MTrk.JAZ2General MIDIkdrum @ `#F.F#.*F#F*F**F#**(F*F*F*(*F#F*#F*#*F#**F**F**F#F#F#*F*(F*F#**(*F*F#F**F*#F##F*#.F#.*F*F*#F*#*F*F*(F*(*F*F*#F#**F#F#*F***F#F**F**F##F**F#(F(*F*#F**F#F#**F##F*.F#*F.*F*#F*F*#(F**F**F(#F**F##F**F#**F*F***F#F#*F*#F**F#(F*F*(*F*#F*#F#*F*#F#.F.*F#*F*#F*F*#*F*(F(**F*F*#F**F##F**F#**F#F*F*#F**F#(F*#*F*(*F*F*#F#*F*#F*#F#.F.#*F*F*#F#**F*F*(F(**F#F**F**F#F#**F#*F**F*#F##F**F**F#(F(**F#F**F#*F#F*#F.F#**F.#*F*#F**F#*F(F**F*(#F**F##F**F*#*F**F#F**F##F**F#(F**F*F*(*F*#F*#F#*F*.F##F#*F.*F*#F*#*F*F*(F*F(*#F**F##F*F*#**F*F*#F**F##F**F#(F**F*F(**F*#F##F**F#.F*#F#*F.#F**F*F*#(F**F(*F*#F**F*F#F*#*#*F*F*#F**F*#F#*F**F#(F*F*(#F**F#*F*#F##F*.F*F#.*F*#F*F*#*F*(F(*F**F*#F##F*F*#**F**F*F*#F#*F*#F#*F*(F(**F*F*#F#*F*#F*.F##F*F.#*F*#F#*F**F*(F(*F*#F**F*#*F#F*#*F**F#F**F**F##F#*F*(F*F*(*F*#F#*F*#F*.F##F*F.##F**F**F#(F**F(*F*#F**F*F*#F#*#*F**F*F*#F##F*F*(F*F*#**F(#F**F**F##F*#F#.F*F#.#F**F#**F*F*(F(**F#F**F##F**F*#*F*F***F#F**F##F*(F#*F*(*F#F**F**F##F*#F#.F.*F#*F*#F**F#*(F*F**F(*F*#F#F*#*F*#*F*F*#F**F**F##F#*F*(F**F(*F*#F**F##F#.F*#F.*F##F*F***F#*F*(F(*F*#F**F**F##F*#*F*F*#F**F##F**F#(F**F(*F*#F**F*#F#*F#.F*#F#.*F*F*#F*F#*(F**F*F*(#F**F*F#*#F*#*F**F#F**F**F##F#(F**F*F(*#F**F#*F*#F#.F*#F.*F##F*F**F*#*F*(F*F(**F*#F*F#F#**#*F**F#F**F**F##F#(F**F*F(**F*#F*#F#*F#.F*#F.*F#*F*#F#**F(F**F(**F#F**F*F#F#**#*F**F*F*#F##F**F#*F*(F*F*(*F*#F#*F*#F##F*.F.#*F#F**F*F*#*F*(F*F*(#F**F*F*#F##**F**F*F*#F*#F#*F**F#(F**F(*F*#F##F**F#.F*#F#.*F*F*#F#*F**F*(F*F*(#F**F*#F*F#*#*F**F#F**F##F**F*(F#*F*(*F#F**F#*F*#F##F*.F*F#.*F*#F**F#*F*(F(*F**F*#F*##F*F#**F**F#F**F#*F*#F#*F*(F(**F*F*#F*#F#*F*#F#.F.#*F#F**F**F#*F*(F*(*F*F*#F##F*F*#**F**F#F**F*#F#*F*(F#*F*(*F#F**F*#F#*F*#F#.F#.*F*F*#F**F#(F**F**F(*F*#F#F#*F**#*F**F#F**F#*F*#F#(F**F*(*F#F**F**F##F##F*.F.#*F*F*#F#*F*(F**F*(*F*F*#F#**F#F*#*F**F*F*#F*#F#*F**F#(F(**F#F**F*#F#*F*#F#.F*F.##F**F#*F**F*(F(*F*#F**F##F*F**#*F**F*F*#F#*F*#F#*F*(F*(*F*F*#F**F##F*.F##F#.*F*F*#F#**F(F**F*F*(#F**F#*F#F**#*F**F*F*#F**F##F#(F**F*(*F#F**F*#F#*F*.F##F*F.#*F*#F#*F**F(F*(*F**#F*F##F**F*#*F*F*#F**F**F##F*(F#*F*F*(*F*#F**F##F##F*.F.*F#*F*#F*#*F*F*(F(*F*#F**F*F*##F*#*F*F*#F**F#*F*#F*(F#*F*F*(*F*#F#*F*#F*.F##F#*F.#F**F**F#*F*(F(**F*F*#F#F*#*F#**F**F#F**F**F##F**F#(F(*F**F*#F**F##F#.F*#F#*F.#F**F*#*F*F*(F(*F*#F**F*#F*F#*#*F*F**F*#F*#F#*F#*F*(F*(*F*F*#F#*F*#F*#F#.F#.*F#F**F**F#*F*(F(**F#F**F#F#**F#**F**F*F*#F*#F#*F#(F**F*F*(#F**F##F**F*.F##F.#*F#F**F#**F(F**F**F(#F**F#F*F*#*#*F*F**F*#F**F##F**F#(F(**F*F*#F#*F*#F*#F#.F#*F.*F*#F#*F*(F**F(**F*F*#F*F*##F*#*F*F**F*#F##F**F#(F**F(**F#F**F##F**F*.F##F.#*F*F*#F#**F*F*(F(*F**F*#F#F*F#**#*F*F**F*#F**F##F**F#(F*F(**F*#F*#F#*F#.F*#F#.*F#F**F**F#(F**F(**F*F*#F#F*F*#*#*F*F*#F**F**F##F*(F#*F(**F*F*#F*#F#*F#.F*#F#*F.#F**F*F#**F*(F*(*F*F*#F##F*F**#*F*F**F*#F*#F#*F#*F*(F(**F#F**F#*F*#F*#F#.F#.*F#F**F**F#*F*(F*F*(#F**F*F#F#*#**F**F*F*#F*#F#*F#*F*(F*F*(#F**F#*F*#F#.F*#F#*F.#F**F**F#(F**F(**F#F**F*#F*F#*#*F*F*#F**F#*F*#F**F#(F*F*(#F**F#*F*#F*#F#.F*F#.*F*#F#*F**F*(F(*F**F*#F#F*F*#*#*F**F#F**F##F**F#*F*(F*F*(*F*#F*#F#*F#.F*#F.#*F#F**F**F#*F*(F*(*F*F*#F##F*F**#*F**F*F*#F*#F#*F#*F*(F*F*(*F*#F**F##F*#F#.F*F#.#F**F**F#(F**F**F(#F**F*F#F#*#**F**F*F*#F*#F#*F#*F*(F*F*(*F*#F*#F#*F#.F*#F.#*F#F**F**F#*F*(F*(*F#F**F*#F*F#*#*F**F*F*#F**F##F**F#(F*F*(#F**F#*F*#F*.F##F*F.##F**F**F#*F*(F(*F*#F**F*F#F#*#**F**F*F*#F*#F#*F#*F*(F*F*(#F**F#*F*#F*.F##F.#*F*F*#F#*F**F*(F*(*F*F*#F#*F#F*#**F**F*F*#F**F##F*(F#*F*F(**F*#F*#F#*F##F*.F*F#.#F**F**F#*F*(F(*F*#F**F*F#F#*#**F**F#F**F#*F*#F**F#(F*F*(*F*#F**F##F*.F##F.#*F#F**F**F#*F*(F*(*F*F*#F##F*F**#*F**F#F**F##F**F#(F**F*F(*#F**F##F**F##F*.F*F#.*F*#F#*F*(F**F**F(#F**F#F*F#**#*F**F*F*#F**F##F*(F#*F*F(*#F**F##F**F#.F*#F.#*F#F**F**F#(F**F(**F*F*#F##F*F**#*F**F*F*#F*#F#*F#*F*(F*F*(*F*#F**F##F*#F#.F*F#.*F*#F#*F*(F**F**F(*F*#F#F*F*#*#*F**F*F*#F*#F#*F#(F**F*F(**F*#F**F##F*#F#.F#.*F#F**F**F#*F*(F*(*F#F**F*#F*F#*#*F**F*F*#F**F##F**F#(F*F*(*F*#F*#F#*F#.F*#F*F.#*F*#F#*F*(F**F**F(*F*#F*F#F*##**F**F#F**F##F**F#(F**F*F(*#F**F#*F*#F*.F##F.#*F*F*#F#*F**F*(F*(*F#F**F**F#F##**F**F#F**F#*F*#F**F#(F*F*(#F**F#*F*#F*#F#.F*F#.#F**F**F#(F**F**F(*F*#F#F*F*#*#*F**F*F*#F**F##F*(F#*F*F(**F*#F**F##F*.F##F.#*F*F*#F#*F*(F**F(**F*F*#F#*F#F*#**F**F#F**F#*F*#F*(F#*F*F(**F*#F*#F#*F##F*.F*F#.#F**F**F#(F**F**F(*F*#F#F*F*#*#*F**F#F**F##F**F#*F*(F*F*(#F**F#*F*#F*.F##F.#*F*F*#F#*F*(F**F(**F#F**F**F#F##**F**F#F**F#*F*#F*(F#*F*F(*#F**F#*F*#F*.F##F*F.##F**F**F#*F*(F(*F*#F**F#F*F#**#*F**F*F*#F*#F#*F#*F*(F*F*(*F*#F*#F#*F#.F*#F.#*F#F**F**F#(F**F(**F#F**F**F#F##**F**F#F**F##F**F#(F**F*F(**F*#F*#F#*F##F*.F*F#.*F*#F#*F**F*(F(*F*#F**F#F*F#**#*F**F*F*#F**F##F*(F#*F*F(**F*#F*#F#*F##F*.F#.*F#F**F**F#*F*(F*(*F*F*#F##F*F**#*F**F#F**F#*F*#F**F#(F*F*(*F*#F**F##F*#F#.F*F#.#F**F**F#(F**F**F(*F*#F*F#F*##**F**F*F*#F*#F#*F#*F*(F*F*(*F*#F**F##F*#F#.F#.*F#F**F**F#*F*(F*(*F*F*#F#*F#F*#**F**F#F**F##F**F#(F**F*F(**F*#F*#F#*F##F*.F*F#.*F*#F#*F**F*(F(*F*#F**F*F#F#*#**F**F#F**F#*F*#F*(F#*F*F(**F*#F**F##F*.F##F.#*F#F**F**F#*F*(F*(*F#F**F**F#F##**F**F#F**F##F**F#(F**F*F(**F*#F**F##F*#F#.F*F#.#F**F**F#*F*(F(*F**F*#F#F*F*#*#*F**F#F**F##F**F#(F**F*F(**F*#F*#F#*F##F*.F#.*F*F*#F#*F**F*(F*(*F*F*#F#*F#F*#**F**F#F**F#*F*#F*(F#*F*F(**F*#F**F##F*.F##F*F.##F**F**F#(F**F**F(*F*#F#F*F*#*#*F**F*F*#F*#F#*F#(F**F*F(*#F**F##F**F##F*.F#.*F#F**F**F#*F*(F*(*F#F**F*#F*F#*#*F**F#F**F##F**F#*F*(F*F*(#F**F##F**F#.F*#F*F.##F**F**F#*F*(F(*F*#F**F#F*F#**#*F**F#F**F#*F*#F**F#(F*F*(#F**F#*F*#F*#F#.F#.*F#F**F**F#*F*(F*(*F*F*#F#*F#F*#**F**F*F*#F*#F#*F#(F**F*F(**F*#F*#F#*F##F*.F*F#.#F**F**F#(F**F**F(*F*#F*F#F*##**F**F#F**F#*F*#F**F#(F*F*(*F*#F**F##F*#F#.F#.*F*F*#F#*F*(F**F(**F*F*#F##F*F**#*F**F#F**F##F**F#*F*(F*F*(#F**F##F**F#.F*#F*F.##F**F**F#*F*(F(*F*#F**F#F*F#**#*F**F#F**F##F**F#*F*(F*F*(#F**F#*F*#F*.F##F.#*F#F**F**F#(F**F(**F*F*#F##F*F**#*F**F#F**F#*F*#F**F#(F*E*(*C*#C**A##A*#?#.?*=#.#;**;**9#*7*(7(*5*#3**3#1*1#**#*/**-#+**+##)**)#*'*('*%*(*#*##*#!#*!#.*#.#***##**(**(****##*#*#**** * *# *# #* #(***(***#**##*##.*#.#****#(****(**###***#***#***#*#*(*#(**#**##***#/MTrk8JAZ2Simutrans Main theme/simutrans-124.5/simutrans/music/02-Gotta-catch-that-train.mid000066400000000000000000000702641520675460500240630ustar00rootroot00000000000000MThd`MTrkfCopyright (C) 2006 shunter~ JAZ2Q  2006.10.06/MTrk(shunter 2006 JAZ2Gotta catch that train(My Song  @General MIDI /MTrk(0JAZ2Am @ &General MIDIrythm`E;H;<;@;*H@E<E;<;@;H;*@EH<6<;@;E;H;*E@H<6E;H;<;@;*@H<E@;<;E;H;Em7*@E<H@;H;E;<;*<HE@;;C;>;G;*G>;CG;C;;;>;*C;G>6G;C;>;;;*>;CG6;;G;>;C;*GC;>>;G;;;C;G7*C>G;G;;;C;>;*;CG><;E;A;H;*E<HAE;A;H;<;*A<EH6<;A;E;H;*AHE<6E;<;H;A;*<HAE<;E;H;A;Fmaj7*<EAHA;H;<;E;*<EAH;;D;G;@;*;D@G@;D;;;G;*;D@G6;;D;G;@;*@G;D6D;G;@;;;*@;DGG;@;AmD;;;*GD@;D;G;@;;;*@G;DH;E;<;@;*@E<HE;@;<;H;*<EH@6E;H;@;<;*E@H<6@;<;E;H;*<@EH@;H;<;AmE;*E@H<<;H;E;@;*@<EH@;;;D;G;*G@D;G;@;D;;;*@GD;6;;D;G;@;*;@DG6G;@;D;;;*;@DGFmaj7G;;;D;@;*G@;D;;G;@;D;*@D;GH;<;E;A;*<EHA<;E;A;H;*EA<H6<;E;A;H;*A<EH6E;H;A;<;*EH<AE;<;H;Fmaj7A;*H<EAE;<;A;H;*H<EA;;G;@;D;*@G;D;;@;D;G;*;D@G6D;G;;;@;*@;DG6;;D;@;G;*@DG;G;@;Am;;D;*;@DGD;G;@;;;*;GD@H;@;E;<;*<@HEE;<;H;@;*EH<@6<;E;H;@;*@<HE6H;<;@;E;*H@E<H;Em7E;<;@;*<@EH<;H;@;E;*@<EHC;>;G;;;*G;>C;;G;C;>;*>GC;6C;;;G;>;*;>CG6G;C;>;;;*>GC;>;G;G7;;C;*>G;C>;;;G;C;*C>;GA;H;<;E;*H<AEE;<;H;A;*HEA<6E;H;<;A;*AH<E6<;H;E;A;*H<EAH;A;E;Fmaj7<;*HAE<E;A;H;<;*EHA<;;G;D;@;*DG@;G;D;;;@;*@GD;6D;G;;;@;*;D@G6@;D;G;;;*@;GDG;C#m7;;@;D;*G;D@D;G;;;@;*GD@;@;=;I;E;*@=EI@;I;E;=;*E=@I6I;@;=;E;*E@=I6I;E;@;=;*E=I@@;I;Fmaj7E;=;*IE@=I;@;E;=;*=EI@H;E;<;A;*EH<A<;A;H;E;*A<HE6<;H;E;A;*AHE<6E;H;A;<;*AH<EA;<;Fmaj7H;E;*<AHEH;E;A;<;*HA<E@;D;;;G;*G;@D@;;;D;G;*GD@;6G;;;@;D;*D;@G6@;D;;;G;*GD;@D;@;G;;;EM*@;GDD;G;;;@;*D@G;@;G;;;D;*G;D@G;D;@;;;*D;@G6G;@;;;D;*;DG@6;;G;@;D;*G;@DG;E7;;@;D;*D@G;D;;;G;@;*DG;@fB;>;E;J;TEB>JlDMJ;>;E;B;TBEJ>l>;J;E;B;TEBJ>lB;E;>;J;D7TBE>JlC;@;H;<;T@HC<lCM@;C;<;H;T<CH@lC;<;H;@;TC<@HlH;@;C;Am7<;T<C@HlE;<;A;H;THAE<lFM<;A;H;E;T<AHElH;A;<;E;THEA<lFmaj7<;A;E;H;THEA<lG;;;D;@;TDG;@lD;;;@;G;EMTG;@DlG;;;D;@;TGD;@lE7@;G;D;;;TD;@G E;J;B;>;*EJB>E;B;>;J;*JEB>6>;J;B;E;*BEJ>6E;J;>;B;*J>BEE;>;D7B;J;*>EJBJ;B;E;>;*EJ>B<;H;E;A;*<AHEA;<;H;E;*E<HA6E;A;<;H;*HE<A6A;H;E;<;*A<HEG7A;H;E;<;*EHA<E;<;H;A;*AE<H>;;;G;C;*;>CG;;C;G;>;*>;CG6C;;;G;>;*G>;C6C;>;G;;;*>G;C>;G;GM;;C;*C;G>>;C;G;;;*C;G>>;;;C;G;*;>GCC;G;>;;;*;CG>6C;G;;;>;*C>G;6;;>;G;C;*;CG>G;C;;;A7>;*;CG>C;G;;;>;*;C>GI;=;@;E;*@EI==;E;@;I;*@IE=6I;=;@;E;*@I=E6I;@;E;=;*E@I=I;AM@;E;=;*I@E=E;I;@;=;*E@=I@;=;I;E;*IE=@@;=;E;I;*=I@E6=;@;I;E;*@E=I6I;E;@;=;*IE@=I;=;@;E;C#m7*=@IE=;@;I;E;*EI=@;;D;G;@;*@GD;D;;;@;G;*@;GD6@;;;D;G;*D@G;6G;D;@;;;*G@;DD;E7;;G;@;*DG@;D;@;;;G;*D;G@B;E;>;J;*JE>BB;E;>;J;*>EBJ6B;E;J;>;*>EJB6>;E;J;B;*E>BJJ;B;E;DM>;*JB>E>;E;B;J;*>BEJE;J;>;B;*JEB>B;>;J;E;*JE>B6J;E;B;>;*>BEJ6E;B;>;J;*EJB>B;J;D7E;>;*BJE>>;B;E;J;*BJ>EC;<;@;H;*C@H<H;C;<;@;*CH@<6<;H;@;C;*<@HC6@;H;C;<;*<C@HC;@;<;Cmaj7H;*<C@H@;H;<;C;*<@CH?;;;G;B;*?B;G;;B;?;G;*?B;G6B;G;;;?;*B?G;6?;;;B;G;*?;BG?;G;B7B;;;*?;GBB;;;G;?;*BG?;E;I;=;@;*@I=EI;=;E;@;*E=I@6I;E;@;=;*@I=E6=;I;E;@;*@=EI@;E;=;I;AM*I@E==;@;E;I;*=IE@I;@;=;E;*EI=@@;I;=;E;*E@=I6E;=;@;I;*=IE@6@;=;I;E;*E@=IA7I;=;E;@;*E=I@I;E;@;=;*=@IEG;>;;;C;*CG>;C;>;G;;;*>CG;6>;G;;;C;*CG;>6G;;;>;C;*C>;GG;;;>;GMC;*;>GCC;G;;;>;*CG;>;;>;G;C;*>;GCC;>;;;G;*>;GC6C;G;;;>;*;CG>6;;G;C;>;*GC;>C;;;G7G;>;*GC;>>;;;G;C;*C>;GA;E;<;H;*<EAHH;E;A;<;*H<EA6H;A;E;<;*H<AE6<;E;H;A;*AE<HH;E;<;Fmaj7A;*<HEA<;H;E;A;*EH<A@;H;<;E;*@EH<@;H;E;<;*E<@H6H;<;@;E;*<@EH6E;@;H;<;*@<EHE;@;<;Em7H;*E@<H<;E;@;H;*<HE@G;>;C;;;*>GC;G;>;;;C;*GC>;6G;>;C;;;*C>G;6;;>;C;G;*;G>C;;>;C;G;G7*C>;GC;G;>;;;*>GC;<;A;E;H;*<HAEE;A;<;H;*<HEA6H;E;A;<;*EA<H6H;E;A;<;*E<HAA;<;H;E;Fmaj7*EA<HE;<;H;A;*EA<H@;G;;;D;*@DG;D;G;;;@;*D;@G6D;@;;;G;*;@GD6;;G;D;@;*;G@DD;;;AmG;@;*D@;G;;@;D;G;*D@;G@;H;<;E;*@EH<H;E;<;@;*<@HE6<;@;E;H;*@EH<6<;@;E;H;*@H<EAm<;H;E;@;*H@<E<;E;@;H;*@E<H@;;;D;G;*@G;DD;G;@;;;*@;DG6G;@;D;;;*@D;G6G;@;D;;;*@;GDD;Fmaj7;;@;G;*GD@;D;G;;;@;*;G@DH;E;A;<;*H<AEH;A;E;<;*H<EA6H;A;<;E;*EAH<6A;H;<;E;*<HEA<;H;A;E;Fmaj7*AH<EE;<;A;H;*A<EH@;D;G;;;*@;DG@;D;G;;;*G;@D6@;D;G;;;*DG;@6G;;;@;D;*;DG@AmD;@;G;;;*;@DG;;@;G;D;*G@;DE;<;@;H;*EH<@E;<;@;H;*@HE<6@;<;H;E;*<HE@6@;E;<;H;*EH@<@;<;E;Em7H;*HE@<<;E;H;@;*HE<@G;>;C;;;*G;>CG;C;;;>;*;>GC6C;>;;;G;*>;GC6;;>;G;C;*>C;GC;G;G7>;;;*G;>CC;;;G;>;*;C>G<;E;A;H;*<HAEA;E;<;H;*HEA<6<;H;A;E;*EHA<6A;E;<;H;*HEA<Fmaj7<;A;H;E;*<AHEE;<;H;A;*HEA<G;;;@;D;*G@;D;;D;@;G;*D;G@6D;;;G;@;*@;DG6G;D;;;@;*D;G@G;@;D;;;C#m7*G@D;G;@;;;D;*;G@DI;@;=;E;*E@=IE;=;@;I;*=EI@6E;I;@;=;*E=@I6I;@;E;=;*=@EI@;I;E;Fmaj7=;*=@IEE;I;=;@;*E=I@<;A;H;E;*<AHE<;E;H;A;*<AEH6A;H;<;E;*AE<H6H;E;A;<;*E<AHH;Fmaj7<;A;E;*E<AHA;H;E;<;*AHE<G;@;;;D;*DG;@@;D;G;;;*;GD@6;;G;@;D;*;G@D6D;@;;;G;*@DG;G;EM;;D;@;*G@D;D;G;;;@;*;DG@;;D;@;G;*GD;@G;;;@;D;*D@G;6G;;;@;D;*G@;D6;;@;G;D;*D@;GG;@;;;D;E7*;G@D@;D;G;;;*D@G;B;>;J;E;*EB>JE;>;J;B;*J>BE6E;>;B;J;*BJ>E6>;J;E;B;*B>JEB;>;E;J;DM*JE>B>;E;B;J;*EBJ>J;E;B;>;*BJ>E>;B;J;E;*EJ>B6B;>;J;E;*>EBJ6E;J;B;>;*>JEBD7J;>;B;E;*>BEJB;J;>;E;*BE>J<;C;H;@;*C<@H@;<;H;C;*@CH<6<;@;C;H;*<CH@6C;@;H;<;*<C@HCMC;@;<;H;*C@<HC;H;<;@;*HC<@H;@;<;C;*H@<C@;C;<;H;*H@C<6<;@;H;C;*@<HC6@;<;H;C;*C<H@Am7<;C;H;@;*H<C@C;H;@;<;*<@CHE;<;A;H;*HA<E<;A;E;H;*EH<A6E;H;A;<;*AHE<6A;<;H;E;*A<EHFM<;E;A;H;*EHA<<;A;H;E;*E<AH<;H;E;A;*EAH<A;<;E;H;*HEA<6H;A;E;<;*<HEA6E;A;H;<;*<HAEH;Fmaj7<;A;E;*EH<AH;E;<;A;*EH<AG;D;;;@;*G;D@D;G;;;@;*@;DG6@;G;D;;;*G;D@6;;@;G;D;*@G;D;;@;D;G;EM*D@G;@;D;;;G;*@;GD;;D;@;G;*DG@;;;G;@;D;*G;@D6;;D;G;@;*D;@G6G;@;D;;;*G;@DG;D;E7@;;;*G@;DD;@;;;G;*DG;@B;>;E;J;*BE>JJ;B;E;>;*>BEJ6>;B;E;J;*>BEJ6>;E;J;B;*JEB>D7J;>;B;E;*JBE>E;>;J;B;*J>BEH;E;A;<;*AHE<A;E;<;H;*AEH<6E;<;A;H;*<EHA6E;H;<;A;*EAH<A;G7<;E;H;*E<HAA;<;H;E;*AEH<G;>;C;;;*>G;C>;G;;;C;*>;CG6;;>;C;G;*G>;C6C;>;G;;;*;C>GGM>;G;C;;;*;>GC;;>;C;G;*;G>C>;C;;;G;*C>G;>;G;;;C;*G>;C6G;C;;;>;*;CG>6;;>;C;G;*;C>G;;C;>;G;A7*C>;G>;C;;;G;*;GC>=;@;I;E;*@E=I@;E;I;=;*=@EI6I;E;@;=;*@IE=6I;E;=;@;*IE@=AME;@;=;I;*I@E==9E9@9I9*I@=E=7@7E7I7*@=IEE5I5=5@5*E=@I6I1=1@1E1*E=I@6E-I-@-=-*=EI@@+E+=+I+C#m7*@E=I@)E)I)=)*@=EI;'G'@'D'*G;D@@%G%;%D%*G@;D6;!G!@!D!*;D@G6;@GD*;G@DE7;GD@*D;G@GD@;*D@;GJB>E*BE>J>EJB*JB>E6EJB>*BJ>E6> J E B *EJ>BJ > DME B *E>BJE J B > *JB>E/MTrk 0JAZ2 @HLeadGeneral MIDI @T0t0QQOOMMLLM*MO*OQQOOMMLLM*MO*0OPPOONNMML*LJ*JL(L0Q(QQ*QLTL J*J0H(HG(G0Q*QM*MQ*QM*MQ*QM*MQ*QM*M0S*SL*LS*SL*LS*SL*LS*SL*LL~LO*OQ(QO~OQ*QJ(JQQOOMMLLM*MO*OQQOOMMLLM*MO*OPPOONNMML*LJ*JL(LLTL ETE ITI LTL M(MOTO MTM P*PL*LP*PL*LP*PL*LP*PL*LS*SP*PS*SP*PS*SP*PS*SP*0PJ~JJ*JJ*JI*IJ*JL*L0M~MM*MM*ML*LM*MO*O0HTH GTG E(E0CTC E*EG*GH(H0M*ML*LJ*JM*ML*LJ*JM*ML*0LM*ML*LJ*JM(MM*0ML~LKKLLNNPPNNLLK*KL*0LP~PNNPPQQSSQQPPN*NP*P0J*JI*IJ*JL*LNTN L*LN*N0Q(QM(M0J(JJJGGHHJJH*HG*0GC~CB*BC(C0I*IG*GI*IJ~JITI0 LPL00LTL PTP G(0GNTN Q*QP*PN(NNNLLJ*JNNLLJ*JNNLLJ*JNNLLJ*JNNLLJ*JNNLLJ*JO(O0KTK NTN PTP NTN0 Q(QPTP NTN0 LTL ITI Q(Q0OTO NTN L(LO*ON*NO*OQ*QS(ST~TS*SQ(Q0L~LO*OQ(Q0O~OQ*QJ(0JQQOOMMLLM*MO*OQQOOMMLLM*MO*O0PPOONNMML*LJ*JL(0LL?L LLH?H HHE?E EEETE0 G?G GGD?D DD@?@ @@@T@0 Q*QM*MQ*QM*MQ*QM*MQ*QM*M0S*SL*LS*SL*LS*SL*LS*SL*LL~LO*OQ(QO~OQ*QJ(JQQOOMMLLM*MO*OQQOOMMLLM*MO*OPPOONNMML*LJ*JL(LLTL ETE ITI LTL M(MOTO MTM P(PP*PSTS U*UPPP00O*ON*NL*LN*NSTS QTQ0 OTO N*NL*LJ(J0M*ML*LJ*JL*LQTQ OT0O MTM L*LJ*JH(H0H(HH*HA*AC*CE*E0HPH00LTL KTK L*LD*DE*EI*I0GP0G0N(NJTJ L*LN*N0Q(QM(M0O*OJ*JL*LN*NJ(0JG*GH*HJ*JL*LJ(0JL*LJ*JITI J~JI}*0IE{PE0*0/MTrk  JAZ2Base @General MIDI Y`-@*--@*-4@*4-@T- 4@*4-@*-4@*47@*77@*7>@*>7@T7 >@*>7@*7>@*>)@*))@*)0@*0)@T) 0@*0)@*)0@*04@*44@*4;@*;4@T4 ;@*;4@*4;@*;-@*--@*-4@*4-@T- 4@*4-@*-4@*44@*44@*4;@*;4@T4 ;@*;4@*4;@*;)@*))@*)0@*0)@T) 0@*0)@*)0@*04@*44@*4;@*;4@T4 ;@*;4@*4;@*;-@*--@*-4@*4-@T- 4@*4-@*-4@*47@*77@*7>@*>7@T7 >@*>7@*7>@*>)@*))@*)0@*0)@T) 0@*0)@*)0@*04@*44@*4;@*;4@T4 ;@*;4@*4;@*;-@*--@*-4@*4-@T- 4@*4-@*-4@*45@*55@*5<@*<5@T5 <@*<5@*5<@*<(@*((@*(/@*/(@T( /@*/(@*(/@*/(@*((@*(/@*/(@T( /@*/(@*(/@*/&@*&&@*&-@*-&@T& -@*-&@*&-@*-&@*&&@*&-@*-&@T& -@*-&@*&-@*-$@*$$@*$+@*+$@T$ +@*+$@*$+@*+$@*$$@*$+@*+$@T$ +@*+$@*$+@*+)@*))@*)0@*0)@T) 0@*0)@*)0@*0)@*))@*)0@*0)@T) 0@*0)@*)0@*0(@*((@*(/@*/(@T( /@*/(@*(/@*/(@*((@*(/@*/(@T( /@*/(@*(/@*/&@*&&@*&-@*-&@T& -@*-&@*&-@*-)@*))@*)0@*0)@T) 0@*0)@*)0@*0+@*++@*+2@*2+@T+ 2@*2+@*+2@*2+@*++@*+2@*2+@T+ 2@*2+@*+2@*2-@*--@*-4@*4-@T- 4@*4-@*-4@*4-@*--@*-4@*4-@T- 4@*4-@*-4@*4(@*((@*(/@*/(@T( /@*/(@*(/@*/2@*22@*29@*92@T2 9@*92@*29@*9&@*&&@*&-@*-&@T& -@*-&@*&-@*-0@*00@*07@*70@T0 7@*70@*07@*7/@*//@*/6@*6/@T/ 6@*6/@*/6@*69@*99@*9@@*@9@T9 @@*@9@*9@@*@-@*--@*-4@*4-@T- 4@*4-@*-4@*47@*77@*7>@*>7@T7 >@*>7@*7>@*>+@*++@*+2@*2+@T+ 2@*2+@*+2@*25@*55@*5<@*<5@T5 <@*<5@*5<@*<-@*--@*-4@*4-@T- 4@*4-@*-4@*47@*77@*7>@*>7@T7 >@*>7@*7>@*>)@*))@*)0@*0)@T) 0@*0)@*)0@*04@*44@*4;@*;4@T4 ;@*;4@*4;@*;-@*--@*-4@*4-@T- 4@*4-@*-4@*44@*44@*4;@*;4@T4 ;@*;4@*4;@*;)@*))@*)0@*0)@T) 0@*0)@*)0@*04@*44@*4;@*;4@T4 ;@*;4@*4;@*;-@*--@*-4@*4-@T- 4@*4-@*-4@*47@*77@*7>@*>7@T7 >@*>7@*7>@*>)@*))@*)0@*0)@T) 0@*0)@*)0@*04@*44@*4;@*;4@T4 ;@*;4@*4;@*;-@*--@*-4@*4-@T- 4@*4-@*-4@*45@*55@*5<@*<5@T5 <@*<5@*5<@*<(@*((@*(/@*/(@T( /@*/(@*(/@*/(@*((@*(/@*/(@T( /@*/(@*(/@*/&@*&&@*&-@*-&@T& -@*-&@*&-@*-&@*&&@*&-@*-&@T& -@*-&@*&-@*-$@*$$@*$+@*+$@T$ +@*+$@*$+@*+$@*$$@*$+@*+$@T$ +@*+$@*$+@*+)@*))@*)0@*0)@T) 0@*0)@*)0@*0)@*))@*)0@*0)@T) 0@*0)@*)0@*0(@*((@*(/@*/(@T( /@*/(@*(/@*/(@*((@*(/@*/(@T( /@*/(@*(/@*/&@*&&@*&-@*-&@T& -@*-&@*&-@*-)@*))@*)0@*0)@T) 0@*0)@*)0@*0+@*++@*+2@*2+@T+ 2@*2+@*+2@*2+@*++@*+2@*2+@T+ 2@*2+@*+2@*2-@*--@*-4@*4-@T- 4@*4-@*-4>*4-<*--:*-48*4-6T- 42*4-0*-4.*4(,*((**(/(*/(&T( /"*/( *(/*/2*22*29*92T2 9*92*29*9/MTrk0/JAZ2 ADrums @General MIDIT`#858*80.8#85#58*068(8*8#5.0#8.8*58(6(85.##8*8(58(8*#5(.858#80#*8(85.680(.8#8*5860.58*8#5#805#58#8*.80685(8.*8#06(#858*.8.(85#*8#8(58*(8#5.8#8(580#5(8*8.6806(#858*.80#.#8*855805.8#8#58*0.(8*8#6850*.8658(#8.(8#5*858#8(5*(8#.858(#80#568(8*8.0#8658*.8(0.(8*8##855805*(58#8.8#(80#.*868((850*6(8.858(#85(.#(8#8*858((8#5(*(8(8.858(#85.#(8(*8(8(68(*6(8#8.858((8.#(5(8#858(*80#5#8.8*580#68*8.5(80(6.858*#8#(85.*8#8(58*(85#.858(#80#(8*8.5680(6.8#8*5805.58#8#*80#*58#85.80#(8*8568.0(6.858*#8#(85.58*8(#8#(8*558.8(#805*8(8#68.0*6#8.8(580#.#8585*80#558.8*#80.#68*85(80*6#858(.8(85.#*858(#8(85#*#858(.80.568(8#*80*6#858(.80#5#8(8*8.580*#(.8(8585#80.(5*868#(80(*(8#8586.8(.#5(8*8#858((8#5*((8#858.8((8(.5(8#(868(*8*6(8(58.8(8#8((.5#(8*8(58#805*#8.8#580#5(8*8.680*(58.86#8.5#(8*858(#85*(8#58#8(.80#*8568.(80*(58#86.80#.*8#85580*#58#85.80#5*8(8.680*658#8(.85(8.#58*8(#85#(8*58#8(.80.568*8#(80*(.8#86580.#*8#855805*#858#.80.5*8(8#680(*.8#8658(8.#5*8#8(58#(85*#8.8(580.#(8685*806.8(58*#805#(858#8.*80#*(58#8.85(80#5.*868((80*658.8(8(#8.#5((858*8#8((8(5#*(8(8.858(#8#5(8.((8*8(686*((8.8#858((8(8#5(.#858(*80*5#858#.80.#68(85*80*(.8#8658#5(8.58*8(#8#5(8*#8(.8580.#*8(85680(*#8.865805.58*8##80*#58.85#80.#(8*85680(*58.86#85.(8##8*8(58(8#*558#8(.80#.68*85(80*658.8(#80#.*8#85580#*#8585.80#.68(85*80(6#8.8*58(8.5#58#8(*85(8*#.858(#80.#(8685*80*(58#86.80#*858#85(8.05#(58(8.8*#80(5.(8*8#680(*58(8#86.8(5#.(8*8(8#8(585#*((8.858(8(#8(8(5#.68*8((8*6((8.858#8((85(.#(8#8*8(580*#.8#85580.5(868#*80*58.86(#8(85.#*8#8(585#*(8.8#8(580.#68*85(80*6#858(.80#.58#85*80*#58.85#80#5*8(8.680*6#858(.85(8.#58*8(#8(8#5*58.8(#80.#*8685(806*#858(.805#58*8.#805#.858*#80.#68(85*80*658.8(#85.#(8*8#8(58#*(85.8#8(5805.68(8#*806*.858(#80#.*8#8(85580*#(58.8#85(80#5((8*8.6806(.8#858*(8.(5#(8#8*858((8#*(5(8.858(8(#8#5.(8(*8(8(68*(6(8#8.858((8(#(85.*8#8(580#5#858*.80.5(868#*80(*58#86.8.5#(8#858(*8(85*#58.8(#80#5*8(8.680*658#8(.80.#*8585#80#*.8585#805#68*8.(80(*58#86.8#.5(8*858(#8#5(8*#858(.80.#*8685(80*(.8586#80.#58#85*805*.8#8#5805#(868.*80(*.8586#8.#5(8#858(*8(8*5#.8#8(5805.68*8#(80(658.8*#80#5*858#8.(80*5#.858(8(#80#(.(8685*80(6.8#858*(8(5.#(8*8#8(8(58#5(*(8#858(8(.8(#5(8.*8(8(68*6((8#8.858((85(#(8.#8(58*805*#858#.80.#68(85*806*.8#8(58.5(8##8*8(58*#5(8#858(.80.5*8(8#680*(.8586#80.5#8*8#580*558.8##80#.(8685*80*(#8.8658(8#.5#8*8(58*(85#58.8(#80#.*8685(80*(58#86.80.558#8#*80*#.8#855805.68*8#(80*6.858(#8.#(85*8#8(585#*(858#8(.80.#68*85(806(58.8*#805##8*858.(805*(58(8#8#.80.5#68(8(*80*658(8.8(#8#(5.(8#858(8(*85#(*(8(858.8(#8(#(85.68(8(*8(6*(8.858#8((8.#5(8(58#8(*80*#58#85.805.*868#(80*658.8(#85(8#.58*8(#8#(85*#8.8(5805.*8(8#6806(.8#8*5805.58#8#*80*#58#85.80.5*8(8#6806(.858*#85.#(8#858(*8*(85#58#8(.80.#68*85(80*6.858(#80.5*8#8#5805*#858#.80#568*8.(80(6#858*.8(8#5.*8#8(585(8*#58#8(.80.#68(85*80*(.8586#80#5(8#8*8.580#(558#8.8*(80(.5(8*8#680(658#8.8*(85.(#(8(8#858(*8*#(5(8#8(8.8(585.(8(#*8(8(68*(6(8(8.858(#8.(#(8558#8(*80*558.8##80#5*868.(80*6.858(#8.#(85*858(#8#*(8558.8(#80#.*8(85680*658.8(#80#558*8.#80#5#858*.80.#*8685(80(*.8#86585(8#.58#8(*8(85#*#858(.80.568(8#*806(58#8*.80.5#858#*80#5#8.8*5805#(8*8.6806(.858*#85.#(8*8#8(58#5(8*#8.8(5805.68*8#(806*#8.8(580#5*858(8.#80#(5#858(8*.805.#68(8(*806*.8#8(8(58(5#.(8(8*858(#8#*(5(8.8(8#8(58(.5(8#68(8(*86(*(858.8(8(#8#(8(.5*858(#80#5.858*#80#.*8685(80(*58.86#8(8.5#*858(#85#(8*58.8(#805#*8(8.6806*.8#8(580#558*8.#80#5.8#8*580#568*8.(80(6#858*.85#.(858#8(*8#*(85.8#8(5805#68(8.*80*6.858(#80#.#8585*80#5.8#8*5805.68(8#*80(*.8#8658.5(8#58*8(#8*#(8558.8(#80.5(8*8#6806(.8#8*580.558#8(8#*80*(#.858#85(805(.*868#(80*(#8.8586(8(.#5(858(8*8(#8#(5*(8.8#8(8(58#.5(8(*8(8(68*(6(8(8.8#8(58.(5(8#58*8(#80#*.8#855805.68*8#(80(6#858*.85#.(858*8(#8*#(8558.8(#80#.(8685*80*(#8.865805##858.*805*#8.8#5805#68*8.(80*(.8586#8.#(8558*8(#8#(85*58#8(.80.#(8685*80*(58#86.80.5*8#8#5805*.8#8#5805.*868#(80(*58#86.8#5.(858*8(#8*#(85#8.8(5805.(8*8#6806(#858*.80#.58*8#85(80(#*.8(8#85580(5.*868#(80*(#858.86(8.(5#(858(8#8(*8*(5#(8(858#8(.85(.(8#(8*8(68(*6(858#8.8((8((8.#5#858(*80*558.8##80#5*868.(80(*#8.8658(8.#558*8(#8*#(85.8#8(580.568(8#*80*6#858(.805.58*8##80#558.8*#80.#68*85(80(6.858*#85.#(8#858(*85*(8##858(.80.568*8#(80(6.8#8*5805.#8*8#580#*.8585#80#.(8*85680*6.8#8(58.5(8##858(*85*(8##858(.805#*868.(80(*.8#86580.5(8*858##80#5*.8#858((80#(.68(85*806*(858#8(.8.5(#(858(8#8(*8*(5#(8(8#8.8(58#(5(8.*8(8(68*(6(8#858(8(.85#.(8(58*8(#80#*58.85#80#5(8*8.6806(.858*#85.#(8#8*8(58*5(8#.858(#80#5*868.(80(*.8#865805.58*8##80*#.8585#80#.68*85(80*(.8#8658.5(8#*858(#8#(8*558#8(.80.#*8685(80(*#8586.80.#58*85#80#558#8*.80.5*8(8#6806*58.8(#8.5#(858#8(*8#*(85#858(.80.568(8#*80*6.8#8(580.5*8#858#(80(5#.8(858*#80(#.68(85*806*.8#8(8(58(5#.(858(8*8(#8#(5*(8.8#8(8(58#.5(8(*868((8*6((8.858#8((8((8#5.#858(*80*5#858#.80.#68*85(80(658#8*.8(8#5.58*8(#8*#(8558.8(#805#(8*8.6806(.858*#805##858.*80*#.8#85580#5*8(8.6806*58#8(.8#5.(8#858(*85*(8##858(.80.568(8#*80*6.858(#80#.58#85*805##858*.80.#(8*85680*6#8.8(58#5(8.*8#8(58#5(8*.858(#805.*8(8#6806*#858(.80#.58#8*85(80(*#(8.8#85580.5(68*8#(806(58#8.8*(8(#5.(8(858*8(#8#5(*(8.8(858(#8(.#(85(868(*8(6*(8(8#8.8(58#(5(8.*8#8(5805#.8#8*5805.(8*8#6806(#858*.85#.(858#8(*8#*(85.8#8(5805#*868.(80(*58.86#80#5#8*8.580*5.8#8#5805.*8(8#680(6#8.8*58#5(8.58#8(*8*(85##8.8(5805.(8*8#6806(.8#8*5805.#8*8#5805##858*.80.#*8685(80(*58#86.8#5.(8#8*8(58*5(8#58#8(.80.#68*85(80(6.858*#80.#(8*8#855805#*58.8(8(#80.#5*868((80*(.8#8586(85(#.(858*8(8(#8#*5((858.8#8((8.5((8#(868(*8(6*(858#8(8(.8.(8(#5#8*8(5805*.858##80#.*8685(80(*.8586#8(85.##858(*85*(8##8.8(580#5(868.*80*(.8#86580#558#8.*80*5#8.8#580.5*868#(80(*58#86.8#5.(858*8(#8*#(85#8.8(5805.*8(8#6806*.858(#80#.#8585*80#5#8.8*5805#*8(8.680(6.858*#8.#(85#8*8(58*5(8#58.8(#80.5(868#*80*(58#86.805.58*8(8##80#(*#8.8585(80.(#*8(85680*6(8#8.8(585#(.(8#8*858((8(*#5(8.8#8(8(58#.5(8(68*8((86*((8(8#8.8(58#(5(8.58*8(#80#*58.85#80#5(868.*80*(58#86.8#5.(8*8#8(58#5(8*.858(#80#5*868.(80(*#8.865805##858.*805*58#8#.80.5(8*8#6806(.858*#8.#(85*8#8(58#5(8*.858(#80#5*8(8.6806*#8.8(5805#58*8.#80#558#8*.80.5(868#*80*(#8586.8#.(8558*8(#8*#(85.858(#80#5(8*8.6806(.858*#80.#(8*8#85580*(558#8.8#(80#(5(8*8.680(6.8#858*(8(#.5(8#858(8(*8*5#((8(8.8#8(58.(5(8#(868(*8(6*(8.8(8#8(58(.5(8##8*8(5805*#8.8#5805#68(8.*80*6.858(#8.#(85#8*8(58#*(85.858#8(0(8*8.68#50566.6*#6(054*4#45#.05#252.2#*05.60(0*0#0*#.5...6(#.(-5#,5,(*,5#*(+5*#*(.*0#5.*(6(((06*#&5&(.&0&$($,$$$3$##$5.&#(#(&3$#,("&($"#"&"(!#&!($&; # &(( , & $ 3 9 &3;($,9#&(((&&(&(&,17&((&;33,;17(&(&(&&$(#&($#&(,&(1&7(#$;33&;1($7,#(&&&#(($#$&((&&9($`$/9//0// - /- -0- -0+-++0+/MTrk:JAZ2Gotta catch that train/simutrans-124.5/simutrans/music/03-Sunday-drivers.mid000066400000000000000000000225421520675460500225700ustar00rootroot00000000000000MThd`MTrk.Copyright (C)2006 shunter~ JAZ2Q qSunday drivers 2006.10.06X`C M7E 7Dm 7Db 7C M7E 7Dm 7Db 7C M7Em 7Am 7Dm 7C M7Em 7Am 7Dm 7C M7Bb 7Am 7C M7@Bdim 7@Em 7@Am 7`Dm 7`C M7@Bdim 7@Em 7@Am 7`Dm 7`C M7G 7@C M7@C M7G 7@C M7@C M7F M7C M7C M7@Bdim 7F M7@C M7`Dm 7`G 7@C M7`Dm 7`G 7@C M7/MTrk3~ JAZ2 @HGeneral MIDIChord `[X`s]<JPG@H@I@K@`D@=DL@LG@=GP@PJ@=J>@=>A@=AE@EJ@=JM@=MQ@QI@=I=@==A@=AD@=DI@=IP@=PCD@=DL@LG@=GP@PJ@=J>@=>A@=AE@EJ@=JM@=MQ@QI@=I=@==A@=AD@=DI@=IP@=Pc@@@C@C@@ @C@ C@@=@H@=HC@=C;@=;@@=@C@=C@@=@G@GC@=CL@LH@=H<@<@@=@E@=E@@=@H@HE@=E9@=9A@=A>@=>E@=EJ@JE@ EJ@ JE@=Ec@@@C@C@@ @C@ C@@=@H@=HC@=C;@=;@@=@C@=C@@=@G@GC@=CL@LH@=H<@<@@=@E@=E@@=@H@HE@=E9@=9A@=A>@=>E@=EJ@JE@ EJ@ JE@=EH@=HL@=LH@=H@@=@H@=HC@=CH@=HL@=LJ@=JA@AF@ FA@ AF@=FA@=AJ@=JM@=MJ@=JA@=AF@=FA@=AJ@=JM@MJ@JH@=H@@=@E@=EH@=HE@=EL@=L#A@=AG@G;@=;@@=@G@=G<@=<@@@9@=9A@A#A@=AG@G;@=;@@=@G@=G<@=<@@@9@=9A@ACH@=HCH@=HcC@=CH@HC@=CL@=LH@=H<@=<C@C@@=@H@=HL@=LH@=HO@OH@H<@=<E@=EH@=HM@=MH@=HM@MH@=HA@=AE@=EH@=HM@=MH@=H<@=<C@=CH@=HC@=CL@ LH@ HL@=LO@OH@=H@@=@H@=HC@CL@=LH@=HO@OO@=OJ@=JG@=GS@=SM@ MS@ SM@=MS@=SJ@JH@=HM@=MQ@=Q#O@=OS@S#O@=OS@SS@]SQ@]QO@]OT0=T/MTrkJAZ2JPGeneral MIDIChords[G@H@I@K@ @]<@;@=;@@@@@=@L@LA@=A5@=55@=5>@>E@=EJ@=JJ@J=@==5@=5=@==A@=A=@==I@=IC;@=;@@@@@=@L@LA@=A5@=55@=5>@>E@=EJ@=JJ@J=@==5@=5=@==A@=A=@==I@=Ic7@77@77@ 77@ 77@=7<@=<<@=<4@=4;@=;;@=;7@=7;@;7@=7G@GE@=E4@4<@=<<@=<9@=9<@<9@=92@=25@=52@=2A@=AA@A>@ >>@ >9@=9c7@77@77@ 77@ 77@=7<@=<<@=<4@=4;@=;;@=;7@=7;@;7@=7G@GE@=E4@4<@=<<@=<9@=9<@<9@=92@=25@=52@=2A@=AA@A>@ >>@ >9@=9C@=CH@=H@@=@7@=7<@=<<@=<C@=CC@=CF@=F:@:A@ A:@ :A@=A:@=:>@=>J@=JF@=F:@=:A@=A:@=:>@=>J@JF@F@@=@9@=9@@=@@@=@<@=<@@=@#;@=;A@A4@=4;@=;C@=C4@=4<@<2@=25@5#;@=;A@A4@=4;@=;C@=C4@=4<@<2@=25@5CC@=CCC@=Cc<@=<C@C<@=<@@=@<@=<4@=4<@<7@=7C@=CC@=CC@=CL@L<@<5@=59@=9E@=EE@=EA@=AA@A<@=<9@=9A@=AE@=EA@=AA@=A4@=4<@=<C@=C7@=7C@ CC@ CC@=CC@CC@=C7@=7<@=<<@<H@=HC@=CC@CH@=HA@=AA@=AM@=MJ@ JJ@ JG@=GM@=MA@AA@=AH@=HH@=H#J@=JO@O#J@=JO@O00=0/MTrkJAZ2]<General MIDIyLead G[JP @G@H@I@K@@7@7<@=<@@=@C@=CH@=HC@C;@=;4@=4;@=;@@=@D@D@@=@L@LL@=LA@=A9@=9A@=AE@=EJ@=JJ@=JA@=A8@=8A@=AD@=DI@ID@DC7@7<@=<@@=@C@=CH@=HC@C;@=;4@=4;@=;@@=@D@D@@=@L@LL@=LA@=A9@=9A@=AE@=EJ@=JJ@=JA@=A8@=8A@=AD@=DI@ID@D7@=7<@=<7@=7@@=@;@=;4@=47@=7;@=;@@=@;@=;0@=09@=94@=4<@=<9@=9@@=@>@=>5@=59@=9>@=>9@=9>@=>7@=7<@=<7@=7@@=@;@=;4@=47@=7;@=;@@=@;@=;0@=09@=94@=4<@=<9@=9@@=@>@=>5@=59@=9>@=>9@=9>@=>C7@=7<@=<@@=@<@=<C@C@@@<@=<7@=7<@=<@@=@<@=<C@=C>@=>5@=5:@=:>@=>A@=A>@=>2@=2:@=:5@=5>@>:@=:A@=AF@F<@=<4@=4<@ <9@ 9<@=<@@=@<@=<E@EC4@=47@=72@=25@=52@=24@=4/@=/7@=70@=04@42@=25@5C4@=47@=72@=25@=52@=24@=4/@=/7@=70@=04@42@=25@5C0@=0+@=+4@=42@=2/@=/2@=24@40@=04@=40@0C0@=0+@=+4@=42@=2/@=/2@=24@40@=04@=40@0C<@=<@@=@C@=C<@=<4@=4<@=<7@=7@@=@C@=C<@=<5@=59@=9<@=<A@=A<@=<0@=09@=95@=5<@=<A@=A<@=<0@=07@=7<@=<7@=7@@=@C@=C<@=<4@=4<@=<7@7@@=@<@=<C@C34@ 40@=07@=72@=25@=52@=2;@=;5@=5;@=;0@=05@=50@=0C0@05@5-@=-7@=7/@=/;@=;C0@05@5-@=-7@=7/@=/;@=;;@];9@]97@]7<0=</MTrk JAZ2H@General MIDIBasen[G@J@I@K@]< @$@<$+@<+(@<(/@:G:C:*>;CGG:C:;:>:*;C>G6;:C:G:>:*>;CG6;:C:G:>:*C;G>>:;:G:C:*>G;C>:;:C:G:*>GC;G > H:C:@:<:*<HC@H:C:@:<:*<HC@6@:H:<:C:*H@C<6H:<:@:C:*@<HCC:@:H:<:*@C<HC:@:H:<:*@H<C@ H E:A:<:H:*EA<HE:<:A:H:*AE<H6A:H:E:<:*HA<E6A:H:<:E:*A<EHH:E:<:A:*H<AEA:E:<:H:*A E<AHH C:<:@:H:*HC<@<:C:@:H:*<CH@6<:@:H:C:*@<HC6H:@:C:<:*CH<@<:@:C:H:*H@<CC:H:<:@:*<H@C@ H ;:>:G:C:*G>C;;:C:>:G:*C>;G6G:C:>:;:*>;CG6C:;:G:>:*C>;G>:C:G:;:*CG>;;:>:C:G:*CG >> ;GC:@:H:<:*C@<H@:H:C:<:*@<CH6H:@:C:<:*H@<C6@:<:C:H:*H<@C<:@:C:H:*@HC<C:@:H:<:*H<H @C@ <:@:C:H:*HC<@@:<:C:H:*@HC<6<:@:C:H:*CH<@6C:@:<:H:*HC<@@:C:H:<:*<@CH<:C:H:@:*<@ H @HCH:<:C:@:*H@C<C:H:<:@:*CH@<6@:C:<:H:*H<C@6<:@:H:C:*H@C<H:C:@:<:*C@H<C:@:H:<:*CH @ @<H;:G:C:>:*>CG;;:>:G:C:*C>;G6G:C:>:;:*;>CG6G:>:C:;:*C>G;;:C:>:G:*GC;>;:>:G:C:*GG ;C> >C:<:@:H:*H<@CH:<:C:@:*CH<@6@:H:C:<:*H<C@6<:H:@:C:*<C@HH:C:<:@:*CH<@C:H:@:<:*H @ @<CH<:A:H:E:*HE<AE:<:A:H:*A<HE6H:E:<:A:*<AHE6H:A:E:<:*HA<E<:A:E:H:*HE<AA:H:<:E:*<EA H AH<:C:H:@:*HC@<H:<:@:C:*C<H@6H:<:@:C:*<CH@6H:C:@:<:*@H<CH:C:@:<:*@C<HH:C:<:@:*CH<@ @H G:>:;:C:*C;>GC:G:>:;:*;>GC6;:C:G:>:*G>;C6C:>:G:;:*;>CG>:;:G:C:*;GC>C:G:>:;:*>CG;G > @:C:H:<:*<C@H@:C:H:<:*H<@C6C:<:H:@:*<CH@6@:<:H:C:*C<H@<:@:H:C:*@<HCC:H:@:<:*H@ H <C@C:<:@:H:*@<HC<:C:H:@:*@CH<6<:C:H:@:*@HC<6<:C:@:H:*<CH@C:@:<:H:*C@H<C:<:H:@:*@<HCH @ H:<:@:C:*<@CHC:@:<:H:*@CH<6C:<:H:@:*<CH@6@:H:<:C:*@<HC<:@:H:C:*C<H@H:<:@:C:*HC<H @ @>:C:;:G:*CG>;;:C:G:>:*>;CG6C:;:>:G:*CG;>6>:;:C:G:*;>CGC:G:;:>:*>GC;;:>:C:G:*> G CG>;@:H:C:<:*<HC@H:<:@:C:*HC@<6C:<:H:@:*C<@H6<:C:H:@:*@HC<H:@:<:C:*C<@HH:<:@:C:*@C@ H<H A:E:<:H:*EAH<H:A:<:E:*H<AE6E:H:<:A:*AH<E6H:E:A:<:*HE<A<:E:A:H:*EHA<H:E:A:<:*H<AH EA H:C:<:@:*<HC@<:@:H:C:*C<@H6<:@:C:H:*@<CH6H:<:C:@:*@H<C<:H:C:@:*@<HCC:@:H:<:*H <CH@@ ;:>:G:C:*>G;CC:G:;:>:*>CG;6C:;:G:>:*C;>G6C:G:;:>:*>C;G>:;:C:G:*GC>;;:G:>:C:*C>G;G > <:@:C:H:*<C@H<:H:C:@:*@CH<6H:<:@:C:*@<CH6C:<:@:H:*<C@HH:@:C:<:*<H@C<:H:@:C:*C@H<H @ <:@:H:C:*@C<H@:C:<:H:*@H<C6@:H:C:<:*H<@C6<:@:C:H:*@HC<C:@:H:<:*C<H@C:H:@:<:*<@CHH @ <:H:@:C:*H@C<H:C:<:@:*@HC<6@:<:H:C:*CH@<6C:@:H:<:*@<HCH:@:<:C:*C@H<H:C:<:@:*H@C<@ H >:;:G:C:*;CG>G:>:;:C:*>;CG6G:>:C:;:*>CG;6C:;:G:>:*C;G>G:;:>:C:*>;CG;:G:>:C:*;>GG > CC:<:H:@:*HC@<H:<:C:@:*@HC<6<:C:H:@:*<CH@6<:C:H:@:*<H@CH:<:@:C:*@CH<<:C:@:H:*@H<CH @ E:H:A:<:*AEH<E:<:A:H:*EA<H6<:A:E:H:*E<HA6E:<:H:A:*<AEHA:<:H:E:*<EAH<:H:A:E:*A<EHH A H:@:C:<:*C@H<@:H:<:C:*<H@C6C:<:H:@:*@C<H6<:H:C:@:*HC@<C:@:H:<:*H@C<C:<:H:@:*<@ CH@H G:>:;:C:*C;>GG:>:C:;:*>GC;6;:G:C:>:*>CG;6;:C:G:>:*CG;>G:;:C:>:*>C;GG:>:C:;:*;>> G GC@:<:C:H:*<@CH<:C:H:@:*HC<@6@:<:H:C:*@C<H6C:@:H:<:*H<C@@:H:<:C:*@HC<@:H:C:<:*@ H@H C<<:H:C:@:*@H<CH:<:C:@:*HC<@6<:@:H:C:*C@H<6<:H:@:C:*C@<HC:<:@:H:*@CH<<:C:H:@:*HC@<@ H <:@:H:C:*C<H@<:H:@:C:*H@C<6H:C:<:@:*C<@H6@:C:H:<:*C@<H@:C:<:H:*C<@H@:<:C:H:*CHH @@ <>:C:G:;:*;>CG>:;:C:G:*;C>G6G:>:;:C:*>;CG6>:G:;:C:*>GC;C:G:;:>:*CG;>;:G:>:C:*G > ;>GCC:H:@:<:*<C@HC:<:@:H:*<@HC6H:<:C:@:*CH@<6C:<:H:@:*@<CHC:<:H:@:*C@<HH:@:<:C:*HC<H @ @A:E:H:<:*A<HEE:H:<:A:*EH<A6A:H:<:E:*H<AE6E:H:A:<:*AEH<E:H:A:<:*AH<E<:A:E:H:*E<AH HA @:H:C:<:*@H<C@:<:H:C:*<HC@6@:H:C:<:*C<@H6H:C:@:<:*@CH<H:C:<:@:*<@HCC:H:<:@:*C@ @HH <C:>:G:;:*C;G>>:G:;:C:*GC;>6>:;:C:G:*;>CG6G:C:>:;:*GC>;C:;:G:>:*G;C>>:G:C:;:*>C;G> G H:<:@:C:*@<HCC:@:<:H:*HC<@6H:<:@:C:*@CH<6H:<:@:C:*C<H@H:@:<:C:*<HC@<:H:@:C:*@@ H H<CC:@:<:H:*@H<CC:<:@:H:*<@CH6@:<:C:H:*@H<C6C:H:@:<:*CH@<<:H:C:@:*<@CH@:C:<:H:*C@H@ H <H:@:C:<:*HC<@H:@:<:C:*<CH@6@:H:C:<:*HC@<6@:C:H:<:*@<HCH:@:C:<:*<C@HH:@:C:<:*CH@H <@ G:;:C:>:*;C>GC:;:>:G:*C>;G6;:>:C:G:*;>GC6G:;:C:>:*GC>;G:;:C:>:*G;C>C:G:>:;:*C;>G> G C:<:@:H:*H<@C@:H:C:<:*<@HC6<:@:C:H:*H<C@6<:H:@:C:*<HC@C:@:<:H:*<@HC<:C:H:@:*C<@@ HH E:H:<:A:*AHE<<:H:E:A:*EA<H6A:H:<:E:*<EHA6<:E:A:H:*HEA<<:E:H:A:*HEA<E:A:H:<:*AA E<HH @:H:C:<:*CH@<@:<:C:H:*C<@H6<:@:H:C:*@CH<6@:H:<:C:*CH<@C:@:<:H:*<@CHC:H:@:<:*C@<H@ H ;:G:C:>:*CG;>C:>:G:;:*;CG>6G:C:;:>:*G;C>6G:>:;:C:*CG>;G:>:;:C:*;G>CC:G:>:;:*G > C;G>@:H:<:C:*C<H@H:@:C:<:*@<CH6<:H:@:C:*C@H<6<:C:@:H:*HC@<<:C:H:@:*H@C<<:C:@:H:*H@H @ C<@:H:<:C:*H<C@@:C:H:<:*H<C@6<:H:C:@:*C<H@6<:H:@:C:*<H@CC:H:<:@:*<C@HC:H:@:<:*H @ H@C<@:H:<:C:*<CH@C:H:@:<:*CH<@6H:<:C:@:*@HC<6@:H:<:C:*<H@C@:C:H:<:*@H<C<:@:C:H:*C@@ H <H;:G:C:>:*>;CGG:;:>:C:*>;GC6C:;:>:G:*;>CG6>:C:G:;:*;>CGG:>:;:C:*CG>;C:;:G:>:*>> G CG;H:C:@:<:*<@CH<:H:@:C:*HC@<6@:<:H:C:*@H<C6H:<:C:@:*HC@<<:C:@:H:*<@HCH:C:@:<:*C<@ @HH H:E:<:A:*HA<EA:E:<:H:*E<HA6A:H:E:<:*A<HE6H:<:A:E:*<HEAA:<:E:H:*HAE<A:H:E:<:*H A E<HAC:H:<:@:*H@<CC:@:<:H:*H<C@6@:<:C:H:*HC<@6C:<:@:H:*HC@<H:<:C:@:*C<H@@:H:<:C:*@<CH@ H G:>:C:;:*C>G;>:C:;:G:*G>;C6C:>:;:G:*;C>G6G:;:C:>:*>;GCG:>:C:;:*C;>G;:C:>:G:*GC>;> G <:C:H:@:*<C@HH:<:@:C:*<H@C6@:C:<:H:*H@<C6<:H:C:@:*H@C<C:@:<:H:*@<CH<:@:H:C:*@C@ H<H C:H:@:<:*<C@H@:C:H:<:*C<H@6C:<:@:H:*HC<@6@:<:H:C:*@C<HH:C:<:@:*H<@CC:@:H:<:*<HC@H @ C:H:<:@:*<H@CH:<:C:@:*@H<C6<:@:H:C:*H<C@6@:C:<:H:*<C@H<:C:H:@:*CH@<<:C:H:@:*CH<@H @ C:;:G:>:*C;G>G:;:C:>:*C;G>6>:C:;:G:*CG>;6;:C:>:G:*;G>CC:>:G:;:*>;GC>:G:;:C:*C>G;G > H:@:C:<:*<H@C<:C:@:H:*C<H@6C:H:@:<:*@HC<6C:@:<:H:*<@HCH:@:C:<:*C@H<C:H:<:@:*@ H<@H CE:H:<:A:*EAH<<:E:H:A:*EAH<6H:<:E:A:*HAE<6H:<:A:E:*EHA<A:E:<:H:*<EAH<:E:H:A:*H A HE<A<:C:@:H:*@H<CH:<:C:@:*<HC@6C:@:H:<:*<C@H6H:@:C:<:*C<H@<:@:H:C:*H<@C<:H:@:C:*CH@<H @ ;:C:>:G:*C;G>>:G:;:C:*>CG;6;:G:>:C:*>;CG6G:>:C:;:*>;GC>:C:G:;:*C>G;>:G:C:;:*;GC>G > @:<:H:C:*@C<H@:H:C:<:*<@CH6<:C:H:@:*<HC@6C:<:H:@:*<@CHH:C:@:<:*@HC<@:H:C:<:*<H@H @ CC:<:H:@:*H<C@@:C:H:<:*C<H@6@:C:H:<:*HC@<6C:<:@:H:*@CH<@:H:C:<:*H@<C@:C:H:<:*@ <H@CH <:C:@:H:*C@H<C:H:@:<:*C<H@6<:@:C:H:*<@HC6@:H:C:<:*C<H@@:H:C:<:*CH@<<:@:C:H:*HC@<@ H >:C:G:;:*>G;C>:C:G:;:*;>GC6;:G:C:>:*>;GC6;:>:G:C:*G>;CG:C:>:;:*>CG;G:;:C:>:*G > GC;>@:C:H:<:*<CH@H:<:C:@:*CH@<6C:@:H:<:*@<CH6H:@:<:C:*H<C@H:@:C:<:*<HC@@:<:C:H:*HCH @ <@A:H:E:<:*EA<HE:H:A:<:*EAH<6E:A:<:H:*E<HA6<:A:E:H:*AHE<E:A:<:H:*EHA<E:A:<:H:*AHE<A H H:C:<:@:*<H@C<:H:@:C:*C@H<6H:C:@:<:*H<@C6H:<:C:@:*<@CH<:@:H:C:*C<@H@:C:<:H:*HC@<@ H G:>:;:C:*G;C>>:;:C:G:*;GC>6;:>:G:C:*>GC;6>:;:C:G:*;G>C>:;:G:C:*;G>C;:G:C:>:*G;C>G > @:H:C:<:*H<@C<:@:H:C:*<C@H6H:C:@:<:*C@<H6<:H:@:C:*@CH<C:H:@:<:*CH@<H:@:<:C:*H @ <@CH@:<:H:C:*C<@H@:C:<:H:*<H@C6@:C:H:<:*H@C<6C:H:<:@:*C@H<@:C:<:H:*@H<CC9H9<9@9*H H@ C@<@8C8H8<8*@C<H<7@7C7H7*<@HC6@4<4C4H4*@CH<6<2@2H2C2*@<HC@1H1<1C1*H<C@@0H0<0C0*@ C@H<H <.@.H.C.*@<HC@-H-<-C-*@<CH6C+@+H+<+*C@<H6@(C(<(H(*H<@C@'H'<'C'*<CH@C&H&<&@&*@ @<H CH<%H%@%C%*CH@<H$@$C$<$*<HC@6C!T$ $<*$$:T$ $8*$$7*$$6*$"$4T$ $2*$$1T$ $.*$$-*$$,*"$$+T$ $(*$$'T$ $%*$$$*$$"*"$$!T$ $*$$T$ $*$$*$$*$/MTrk1cb @cb!@JAZ2Drumso @@General MIDI][( Q`*959#90.959#9*5#05*9.#(9690.959#96*(#.5(9#9(*959(9*#5#9.9(590(9*9#569.0#9(59*.960##9*9.59505#9.9*59#0#569(9.*90*596.9(#9#(95.#959*9((9*#5#959(.905.*9(9#690.9596*#9(0#5*9#9.590*#.9#955905#(969.*90*6#959(.9(9.#5#9*9(59#*5(959.9(#905#(9*9.690*6.959(#90.*9#9(9559#05(*59.9(9##905.(69*9#(90*(9#9.9659(5.(#(9(59(9#9*9#*(95(59.9(#9(95(#.(9*9(9(696(9*(.9(9#9(59(9(.#5#9*9(5905#59.9*#90.(969#*950(*59.96#9#(95.59#9(*9#5*(959#9(.905.(969#*906*#959(.90#.*9595#90*5#9.9#590#5*969.(90(659.9*#9.#(95*9#9(59#*5(959.9(#905.*969#(90*(#9596.905.#9*9#590#*.9595#90#5*9(9.6906(59.9*#9.5#(9#959(*95*(9##959(.90#.69(95*90(659#9*.905#(9*9#9.590*(59#9(9#.9505(#(969.*90(*(9.9596#9#5(.(9(9#9*9(59*#(5(9.9(9#9(59(9#.5(*9(9(69(96*((9#959(.9#(95.(59*9(#90*5.959##905.(969#*90*659.9(#95.(9#59*9(#95#(9*59.9(#90#*9(9.5690(59#9*.960.*95#9#590*#959#.9505.69(9#*906(.9#9*59.5#(9#9*9(595(9#*#9.9(590.#69*95(90(659#9*.905.59#9#*905#.9#9*590#5*9(9.690(*59#96.95#.(959*9(#95#(9*#9.959(0(9#*9.6950*59#96.9(05*9#9#59.(90*5(#9(9.9#590(#5*969.(906*59(9.9(#9.#5((9#959((9*95(*#(959(9.9(#9.#5(9(69(9(*9(*6(959(9#9(.9#(9.5(59*9(#90#559.9*#90#.*9(956906*59.9(#95.(9##959(*95*#(959.9(#905.*969#(90*6#9.9(5905##959.*90*5.9#9#590.#69(95*90*(59.96#9.5(9#59#9(*9*(9#559#9(.90.#69(95*90(*.9#965905.#9*9#590#*.9#955905.(9*9#6906(#959*.9(9#.5#9*9(59#5*(959#9(.90#5(9*9.690(#9596.9*0##9*9595.(90*5((959#9#.90(.5*969#(90*(#9(9.9659#.(5(9*9(9#9(59#*(5(9(959#9(.9#.((95(969(*9(6*(959.9(9(#9(#5(9.*959(#905#59.9*#905.(9*9#6906(.959*#9.(95#*959(#9*#(95#959(.905#*9(9.690(6.959*#905#*9#9.5905*#9.9#5905#(9*9.6906(.959*#95#(9.#9*9(59*5#(9.959(#905.69(9#*90(*#9596.90&9.5#&9&&&9&9&&-9-9---9--9+9-+9+++9++9)9+)9)))9))9#9*9(9)590(#*#9.9(95590(#.*9(95690*((9.9#9659.#(5(9#9(959(*95*(#(9.9(9#9(59(9.(5#69(9(*96((9*#9(9.9(59.5#((9*959(#90#5#959*.90#569*9.(90*(.9596#9.#5(9#959(*9(95*#59.9(#905#69(9.*90*6.959(#905#59#9.*90#559.9*#90#.*9695(906(59#9*.9.(9#559#9(*95#*(9#959(.90#569(9.*90*(#9.96590.5#959#*905*#959#.90#.*9695(906(#959*.9#.5(959#9(*95(9#*.959(#90.#69(95*906(#9.9*590#.*9#9595(905(#(9#959*.905#(*969.(906(59(9#9*.9(.5#(959(9#9(*95*#((9(959#9(.9(#5(9.*969((9(9*6(.9#959((9.#((95#959(*90#*.9595#90.#69(95*906(#9.9*59.#(9559#9(*9#5*(9.9#9(590#.(9695*906*#9.9(590.*9595#9#0#5#9*59.90*9.(9695#0(*59.96#95(9.##959(*95*#(959#9(.905#(9*9.690*(#9.96590.#59*95#905*.959##90#569(9.*90(6#9.9*59.#5(9#959(*9*(95#59#9(.90#.*9695(906#959(.9*0.#(9*9#95590*(559(9.9##905#.(969(*90*(59(9.96#9.5(#(959*9#9((9#*5((959(9.9(#95#(9.(69(9(*9(6*(9.9#9(9(59.5((9#59#9(*90*5.9#9#590.569(9#*90(659#9*.95#.(959#9(*9#(95*59.9(#90#.*9695(90(659.9*#90.5#9*9#5905#.9#9*590#5*9(9.690(*59#96.9#5(9.59*9(#9#*(95#959(.90#.*9695(90(659.9*#90#559*9.#90*5#959#.905#*969.(906*#959(.9.(9#559#9(*9#(95*.9#9(5905.69*9#(906(#959*.905.#9(959#*90*#559#9(9(.905.((969#*90*(.959#96(9#.(5(9#9(959(*9(#*5(9.9#959((9(9#(.5*9(9(69*(6(9(959.9(#9.(95(#59#9(*90*5#9.9#590.*969#(9506*.959(#95(9#.#959(*9#*5(9.959(#90.569(9#*906(59.9*#90.5#959#*90*5.959##905.(9*9#6906(.9#9*595.(9#*9#9(595#(9*.959(#90.5*9(9#6906(59#9*.90#5#959.*90*5#959#.905#*9(9.690(*59.96#9(9.#5*959(#9*#5(959#9(.905#69*9.(90(*59.96#90#5#9*959.(905(.959#9#(9*0(5.*969#(906(#9.959*(9.#(5(9(9*959(#9(#*5(9#959(9(.95#.((9*969((96((9*59.9#9((9.5(#(959*9(#905*#9.9#590.#(9*95690*(59#96.9(9.#5*9#9(595#(9*59#9(.90.569*9#(90*6#9.9(590.#59#95*905*59#9#.90#5*969.(906*#9.9(59#(9.5#959(*9#*5(9#959(.90#5(9*9.6906*.9#9(590#.#9595*90*5#9.9#590.#69(95*90(6#9.9*59#(9.5*959(#9#5(9*#959(.905.69*9#(90*6#959(.90#5(9#9*9.590#5((9.9#9*5905.(69(9#*90(*59.9#96(9.5(#(9*9#9(9(59*(5#(9(959.9(#9#.(95((9*9(69(9(*6(9#9.9(59(5.(9#*959(#905##959*.905#*9(9.690(*59#96.9.5(9##959(*9(9#*5#959(.90#569*9.(906(59.9*#90.5*959##90*#.9#95590#.(9695*90(*59#96.9.5(9#59#9(*9*#(95.9#9(5905.(9*9#690*(59.96#90.5*959##90*#.9#95590#.(9695*906(#959*.9.#(95#959(*9(9#*559.9(#905.69(9#*906*59.9(#90#5*9(959.#90*5(#9(9.9#5905(#*969.(906(.9#9(9*59#.5((9*959#9((95#(*(959#9(9(.9(9#5.(*9(9(69(6(9*#959.9((95#(.(9*959(#90*5#959#.905#*969.(906*59#9(.9.5(9#59#9(*9*#(95#9.9(590.569*9#(90*6.9#9(590#.#9595*90#*#9595.905#69*9.(90*6#9.9(59#(9.559*9(#95#*(9#9.9(590#.(9*956906*.959(#905.*959##90#5#959*.905#(9*9.690*(59#96.95(9#.*959(#9#5(9*59.9(#90.#(9695*906(.9#9*590.#*9(9#95590(5*(9#959#.90.#((9*95690*6#9(959(.9(#.5(9(959#9(*9(#*5(9.959(9(#9#((95.69*9((9(96*(#959.9((9#(.(9559#9(*90#*59#95.90#5(9*9.690*(59.96#9#5(9.#9*9(59(9#5*#959(.90#569*9.(906(.9#9*590#.*9595#90*#.9595#905.*9(9#690*6.9#9(595.(9#*959(#9#5(9*.9#9(5905.69*9#(90*6#959(.905##9*9.590#5.959*#905.(9*9#690*(.9596#9#.(95#9*9(59(9#5*.9#9(590.#(9695*90(*#9.965905#59#9*9.(905*##9.959((90(.#*9(95690(6.9(9#9*59(.5#(9#9*9(9(59*(5#(9(959.9(#9(95(#.69(9(*9(*(96(959#9(.95(.#(9*9#9(590*##9.95590.#(9695*906(#9.9*595#(9.*959(#9#5(9*59#9(.90#.*9(95690(*#9.96590.#59#95*905*59#9#.90#5(969.*906(.9#9*59.(9#559*9(#95#*(959.9(#905.*9(9#6906(#9.9*590.##9*955905*59.9##90.5(969#*906(59.9*#95(9.#*959(#9#5(9*#959(.905.(969#*906(.959*#90.5(9#9*9#590#5(#9(9.9*5905(#*9(9.690(6(9#9.9*59#(5.(9(9*9#9(59(#5*(9(9.9#9(595#(9.((9*9(69(9(*6(9#9.9(59(5.(9##9*9(590*5.959##905.69(9#*90(6.959*#9#.(95#959(*9(9#*5#9.9(590#.*9(95690*6#9.9(590.#*9595#90*#59#95.90#5(969.*90(*.9#96595.(9#59*9(#9#*(95#959(.90.#(9695*906(#9.9*590.#59#95*905*59.9##90.5*9(9#690(*.9596#9#.(95#959(*9(9#*5.959(#90.569(9#*906*#959(.90.##9*9(95590#(*59#9.95(90(#569*9.(90*((959#96.95(.#(9*959(9(#95(#*(9#9.9(9(59(9.#5(69*9((9*((9659#9.9((9#5(.(959*9(#905*#959#.905#*969.(906*.959(#9#.(95*959(#9#5(9*.959(#905#69(9.*90(659.9*#90.559#9#*905*59.9##90.5*969#(906*#9.9(59#(9.5#9*9(59#5*(9.959(#90.5(9*9#6906*.9#9(590#.59#95*90*#.9#95590#.*9695(906*#959(.9#(95.#9*9(595*(9##9.9(590.569*9#(90*6#959(.90#5*9(959.#90(#*(959#95.90.5(*969#(906((9.9#9*59.(5#(959#9*9((95*(#(959.9(9(#9#((9.5*9(9(69(9*(6(9.959(#9(#5(9.59#9(*90#*59.95#90.5*9(9#690(*.9596#9#.(95*959(#9(9*#5.9#9(590.#(9695*90(*59.96#90.5*959##90*##9.95590.#*9(95690*6#959(.9.#(95*959(#9#5(9*59#9(.90.5*969#(906*.9#9(590#.59#95*905*59.9##90.569(9#*90(6.959*#9#.(95*9#9(59(9*5#.9#9(590.#*9695(90*(59.96#90#5(959*9.#90(*5(959.9##90#5((969.*906*.959(9(#95.#((9*9(9#9(59(#5*(9#959(9(.9(95#.(*9(9(69(6(9*(959#9(.95(.#(959*9(#905*.9#9#590#.*9695(906*.9#9(595.(9#*959(#9#5(9*#959(.905.*969#(906*#9.9(590.#59#95*905*.959##905.*9(9#690(*#9.9659#(9.5*959(#9*#5(9.959(#90.569*9#(90(*59.96#90.559#9#*90*#59#95.90#5(9*9.690*(#9.9659#(9.5*959(#9#5(9*.959(#905#69*9.(90*6#959(.90#5#9(959.*90(*#59(9#95.90.(5*969#(906((959.9*#95(#.(9*9#959((9*5(#(9#9.959((9(5(9.#(969(*9(9(6*58.8#8((85(#(8.#7*7(570*5#6.6#560.#65*55(50*6.3#3(535.(3#52*2(#2(25#*.1#1(510.#*0(05600*6#/./(5/0.#*-5-5#-05*5,.,##,0.5*+(+#6+0(*#*.*65*().5#5)*)(#)#*((5#'5'(.'0.#6&*&5(&0*65%.%(#%0.#5/$/#//)#0)!))!)) ))) +)++0+00/0//-/--#*5-`#5.5*#`6.5(*#`.#5*6(05#.(0(/MTrk5JAZ2Simutrans B theme/simutrans-124.5/simutrans/music/05-Boring-afternoon.mid000066400000000000000000002365661520675460500231010ustar00rootroot00000000000000MThdMTrkm(c) shunter 2006~ JAZ2Q 2006.10.06Q 6~Q/MTrk_cbrcb rcb rcbc@cbd@cbf@ed JAZ2/MTrk kJAZ2]General MIDILead >[gY @@Q?QQ?Q@M?@M O?@O M?@M L?@L J?JH>>?>@@?@@ A?@A`A?A@C?C@E?E@CHC@>?><>H>>?>@@?@@ A?@A`C?C@E?E@J?J@H?H|J?J@J?@J J?@J`E?E@H?H@H?@H H?H`G?G@F?@F F?@F`C?C@E?E@E?@E E?E`>?>>?>@@?@@ A?@A`A?A@C?C@E?E@C?C@?`>?@> ? @>@>?>>?>@@?@@ A?@A`A?A@C?C@E?E@CHC@>6><>?>>?>@@?@@ A?@A`C?C@E?E@J?J@H?H|J?J@J?@J J?@J`E?E@H?H@H?@H H?H`G?G@F?@F F?@F F?F@C?C@E?E@E?@E E?E`>?>>?>@@?@@ A?@A`A?A@C?C@E?E@C?CA?@@?@@ >? A@>J?J@J?@J J?@J`L?L@MHM@L?@L J?@J`E?E@JHJ|H?H@H?@H H?@H`J?J@LHL@J?@J H?@H`FHF@E6E|C?CC?C@E?@E F?F`H?HH?H@J?@J L?L`J?J@J?JL?L@MLL?hM@LXJ?(JXL?L|J?J@J?@J J?@J`L?L@MHM@L?@L J?@J`E?E@J?J|H?H@H?@H H?@H`J?J@LHL@J?@J H?@H`FHF@E6E|C?CC?C@E?@E F?F`H?HH?H@J?@J L?L`J?J@J?J`M?M`LLJ?dLDJ\H?(HPJ?@J@>H>>?>@@?@@ A?@A`A?A@C?C@E?E@CHC@>6><>?>>?>@@?@@ A?@A`C?C@E?E@JHJ@H?H|J?J@J?@J J?@J`E?E@H?H@H?@H H?H`G?G@F?@F F?@F`C?C@E?E@E?@E E?E`>?>>?>@@?@@ A?@A`A?A@C?C@E?E@CHC@H`>?@> ? @>@>H>>?>@@?@@ A?@A`A?A@C?C@E?E@CHC@>6><>?>>?>@@?@@ A?@A`C?C@E?E@JHJ@H6H|J?J@J?@J J?@J`E?E@H?H@H?@H H?H`G?G@F?@F F?@F F?F@C?C@E?E@E?@E E?E`>?>>?>@@?@@ A?@A`A?A@C?C@E?E@C?CAH@@?@@ >? A>@J?J@J?@J J?@J`L?L@MHM@L?@L JH@J`E?E@J6J|H?H@H?@H H?@H`J?J@LHL@J?@J H?@H`FHF@E6E|C?CC?C@E?@E F?F`H?HH?H@J?@J L?L`J?J@J?JL?L@MLL?hM@LXJ?(JXL?L|J?J@J?@J J?@J`L?L@MHM@L?@L JH@J`E?E@J6J|H?H@H?@H H?@H`J?J@LHL@J?@J HH@H`FHF@E6E|C?CC?C@E?@E F?F`H?HH?H@J?@J L?L`J?J@J?J`M?M`LLJ?dLDJ\H?(HPJ?@JQ?QQ?Q@M?@M O?@O M?@M L?@L J?J@4=04>@=M4@=04B=S@4@=04B@=S4=@0@4B=S4=@0=S4B@@=40->5M@5-@05M@->-@50-:@5H5@-05H-:@-@5 @9E2:2@909E@2:9@20@2:9E2@90@2:9E@9202:9E@2@902:9E@@2909E2:@29@0@2:9E29@ @-75E@5-0-7@5E5-@05E@-7-5@0@-75E@-5027:E@:@2027:E@2:@027@:E2@:027:E@2@: :E@27@:20@:E27@:20@27:E:2@0@27:E2@: 47474@P@>7@>7 @7>P74C4@P@C77C@@C7P@74>4@>@77(@>*@7>P@C474PC7@C(@7@C7P@+9+@P@9@@*9.@9@@0@@697P@@99*@@.P9@@9+@(@ @0@9p9*@7@P9@@@2"9.P@292!@9$9@2 @912$P2@9@2090P92@@9-2% 9@2@2+9%P2@924@94@92@9 24@@92P>4@24@>@2>"@24>2@P2494@@2@9 @2 9$2@9P24>4@@2@>2>$@@2>P94@24P@92@2 99@2P@>424P2@>2@>>2@P@2494@92@9*2(@@92P24>4@P>@2@>(2>@2P2797@P@922"9@9@2P27@>7P>2@@2> 2@>P2:@9:@9@2@9/2-29@P@>:2:P@>2>-2@>2@P@494@P@@9@@2979@@0@@@9CP@9@93@7@P@9@94@0@ @0@9p92@@BP@9@@94@4P9@@@,97@P@9@@.9,@P@9@@@>97P@@9@@191P9@@9@@,P@@9@.@9,P9@@9%@'@`93@3@9@@9@93@@9@@PC3@93@C9@93@C!C9@P93@3@@9@@ @@#9@9@PC393@@9C@@9C#@C9P93@3@@@9@93@@@9@P93@C3@C9@@93C!9@CP@@393@@9@ @9@#9@@P@C393@9C@C#@9@9CP9323@@9@223@929@P>3@23@>2@>!@23>2@P23@93@92@ 29#@29@P23>3@@2@>>#2@>@2P24@94@9@29 24@92@P24>4@@>2@@>"24>@2P24@94@@29 9$@2 2@9P>424@@2@>2>$@@2>P94@24P@92@92 @92P>4@24P@2>>2@>@2P2494@@92@2(@9*@92P>424@P2@>2>(@@2>P@9424@92@@249 92@P>4@24@>@224>"@@2>P@2494@9@2 9$@2 92@P>424@@2>@>$2@@2>P54@<4P<5@5 <@<@5P@A454PA5@A5@A5@P@54<4@<@55(<*@@<5P@A454PA5@A(@5@A5P54<4@@<5@54< @<@5P54A4@@A@5A"@545A@P@54<4@@<5 <$@5 <@5PA4@54@5@A5A$@A@5P2494@P2@9@92 29@P24>4@P>@2@2>2@>P2494@@92@9*2(@@92P24>4@P@2>>(2@>@2P2494@@9@29 24@@92P@24>4@>@2>"24@2@>P>424@@@>2 @2 >$@>2P>424@@2@>2@>$>@2P>4@74P7>@7 @>>@7P@C474P7C@@7C@C7P@74C4@C@77(@C*@C7P@74C4PC7@C(@7C7@P>4@74@>7@@> 747@>P74@C4@C@774C"@@C7P74@>4@7>@ @7 >$7@>P74C4@@7@CC$7@@7CP@<4C4P@<CC@< C@<P<4@P@<@<<@P<4@C4@C<@C*<(@C<@P@<4P@<<@@<P94@@4@@@9@ 94@@9@PE4@94@E@9E"94@9E@P94@4@@@9@ @$9 @9@@PC494@@9C@@C$9C9@P@9424P2@9@2 9@92P>424@P2>@2>@@2>P2494@@92@@2(9*2@9P@>424P>@2>(@22@>P93@@3@@@9@@93@9@PC393@@C9@@93C!@9CP93@3@@@9@ @9@#@9@P93@C3@9C@C#9@C9@P93@23@9@2239@92@P23>3@@>@2@23>!2@>P "@]2HK[2J<C2:@9E29@09E@2:2@909E@2:2@909E2:@29@0-5691@9@5-P@9:58-,P95-@@58-#91-@59P9?-,@5?P5@-959@-/995@-9P5;@9;-/P@-59-/9;5;@@9-5T@5J-;9J45?-29?5-9O59@--2@5?9?@59-T5O@-?9OT9M2A99209M2A2909H2=92@5-/@2=9H29@0->5M@-5@0->5M@5-@0-B@5S5@-0@-B5S-@5 -B5S@@-50@5S-B-5@05S-B@-5@0-B@5S-@5 4B=S@=@404B=S@4@=0@4B=S4=@04B=S@4=@0@=S4B@=404B=S@4=@0@=S4B@4=04B=S@=4@0@5M->5@-0@5M->-5@0@5H-:5-@0-:@5H-5@ @9E2:29@09E@2:2@909E@2:@9209E2:@@920@7:>E>7@0>E@7:>7@0>E7:@>7@07:@>E7@> 27:E@:2@027@:E:2@0:E@27@:20:E@272:@0@27:E2:@027:E@@:2027:E@:2@027@:E2@: >L@7C@>7@@7C>6@>7P@7KP7@7K@7@P@7K@@7 73@7@P@7K@7@ @7/7@PE7:>@70@7:>E>7@0>E7:@@>70@>E7:7@> @=@419,49@@ @47@E91 94@@9,@=41@49@@ @=@9,419@@40@-75E5@-0@-75E@-50-7@5E-5@05E@-7@5-02:9E@@2902:9E@@9209E@2:@9202:9E@9@20@E@9:@9@09:@E@@@90@@E9:@9@0@E9:@9@@ @5E-75@-05E@-7@-505E@-7@-50-7@5E5@-0@9/@A444@@9 @H4:@94 94@@@E9147@94@@ 9147@@E49@@04;@J95@4@9@ @S4B@9; 94@@@J@4;9549@@ @J954;@49@@0->@5M-@50@5M->5-@05H@-:5@-0@-:5H-@509E@2:2@909E@2:@2909E@2:9@209E2:@@290@7:>E7@>0@7:>E7>@07:@>E>@70@7:>E7>@ @:E27@2:027@:E@:20@27:E:2@027:E@:2@0:E27@:2@0@27:E2:@0:E@27:@20@:E272@: 7C@>L@>7@@7C>6>@7P@7KP@7@7K@7P7K@@@7 73@@7P@7K@@7 7/@@7P@E7@>0@7:>E@>70>E7:@>@70@7:>E7>@ 9,@=@414@9@ @47@E91 94@@9,@=@4149@@ 9,@=@41@94@05E-7@5@-05E@-75-@0@C]2[2HKJ< "@9424@9@2249 @9@2P@24>4@>2@24@>">@2P@9424@92@ @9$2 92@P>4@24@2>@>$2@2>@P@9424P@9292 @@29P>4@24P@>2>@2@>2P@9424@92@2(9*@@92P>424@P2@>>(2@2>@P@2494@92@@249 @92P24@>4@>2@24@>"@>2P9424@@@92 2 9$@92@P@24>4@2@>>$@2>2@P@95@5@@9@@@ 95@@9PC5@95@C9@95C"@@9CP99@9@@@9@ @@'9#@9@P99C9@@9C@C'9@9C@P@=@9=@@@9@%@9=9@@P@C=9=@C9@9=@C'C@9P@=9=@@9@@ @9%@*@@9P@9=C=@9C@9!C*@@9CP99@29@9@2299#@2@9P29@>9@>2@@>%29@2>P25@95@@29 @9%2 2@9P25>5@@2>@2>%@>2@P2494@@9@2249 @2@9P@24>4@>@224>"@@>2P24@94@@92 @9$2 92@P24>4@@2@>>$@22@>P2494@P@29@92 9@2P>4@24P>@2@2>>2@P94@24@9@22(9*@@92P24>4@P2>@>(2@>2@P94@24@9@2@249 @92P>424@@>2@@24>">2@P@9424@2@9 @2 9$@29P24>4@@2@>>$@2@>2P<4@54P<5@@<5 5<@P@54A4P@5A5@A@5AP@54<4@<5@@5(<*<@5P54A4@PA5@A(@5A@5P@<454@<5@54< @@<5P@A454@A@5@A"545A@P54@<4@@5< @<$5 5<@P54@A4@5@A5@A$5A@P24@94P2@992 @2@9P24>4@P@2>2@>2>@P94@24@92@@2(9*2@9P>424@P@>2@2>(>@2P@9424@92@249 @2@9P@>424@>2@24@>"2>@P24@>4@@>2 >$@2 2@>P24@>4@2>@2@>$2>@P>474@P@>7>7 @7@>P74C4@PC@7C7@@C7P@C474@C7@@C*7(C7@P74C4@PC@77C(@C7@P@>474@>7@> @74>7@P74@C4@C7@@74C"7@CP>4@74@@7> @7 >$@>7P@74C4@7C@@7C$@C7P<4C4@PC<@< C@@C<P@<4P<@@<<@PC4<4@@C@<<(@C*<@CP@<4P@<<@@<P@@494@@9@94@@ 9@@P94@E4@E@9E"@94@E9P@4@94@@9@ @$9 @@@9PC494@@9C@9C$@C@9P94@24P29@@92 29@P@>424P>2@2>@>@2P24@94@9@2@2(9*92@P>424@P@2>@>(2>@2P9424@P9@292 @@92P>4@24P2@>>@2@>2P9424@@92@9*2(@29@P>4@24P>2@2>(@@2>P@9+@+P9@@@@*9.@@90@@697P@@9@.9*@P9@@@(@9+ @0@9p@9*@7P@@99+@@+P9@@9.@@*9@@0@697@P@@9@@.9*P9@@9+@(@ @0@9p9*@7@P@9@@9.2"P29@9$@2!9@2 @2$91P2@92090@P@922%@9- 9@22+@9%P@299424@@92@@9 249@2P24>4@@>2@24@>"2>@P@2494@92@ @9$2 92@P24>4@@2>@>$@2@2>P@2494P2@92 @929@P>4@24P>2@>2@>@2P@2494@9@29*2(@@29P24@>4P@2>>(2@@>2P@9424@9@29 24@92@P@>424@>2@>"24@2>@P@9424@92@ 9$2 @29@P24>4@@2@>@>$22>@P<4@54P<@5<5 @5<@P54A4@P@A5@A5@A5P@<454@<5@@5(<*5@<P@54A4P@A5A(@55A@P54<4@@<@5@54< @5<P54A4@@A5@@A"54A@5P<454@@5<@ <$@5 5<@PA454@@5A@5A$@5A@P@9424P@29@2 929@P@24>4P@2>>2@>@2P94@24@92@@9*2(@92P24>4@P2>@@2>(2@>P@2494@92@9 24@92@P24@>4@>@2@24>"@2>P@>424@2@> @2 >$2@>P>4@24@2>@@>$2>2@P@>474P>7@>7 @>@7P@C474P7C@C@7@7CP74@C4@C@7C*@7(7C@PC474@P@C7C(7@C7@P@>474@>@774> @@7>P@74C4@C7@@74C"7C@P>4@74@7@> >$@7 >7@P@C474@7C@C$7@7C@P@<4C4PC<@@C< @<CP@<4P@<<@<@PC4@<4@C@<<(@C*<C@P@<4P<@<@<@P94@4@@@9@@ 94@@@9PE494@@E9@@E"94E9@P94@@4@@@9 9 @$@@9@P@C494@9C@@9C$C9@P24@94P2@92 9@2@9P@24>4P2@>2>@@>2P24@94@92@9*@2(@92P@>424P2@>>(@2>2@P9+@@+P@@99.@*@@@90@6@97P@@9@@.9*P@@9@9+@( @0@9p@79*@P@@92"9.@P92@9$@2!9@2 912$@P2@9J<]2@[2 "HKC9E@2:92@0@2:9E92@0@9E2:@9209E2:@@29056-91@5@9-P58@-,9:P5-9@5891-#@95-@P9?5?-,@P95-@-/9959@@59-P-/@9;5;P-@95-/@5;9;9@-5T5J@-;9J4-29?5?-95O-5@95?@-29?9-@5T@5O-?9OT9M2A99202A9M9209H2=92@5-/9H@2=@920@2:9E92@02:9E@29@02:9E@29@0@9E2:29@ -75E@@5-0@-75E@-50-7@5E5-@05E-7@-@505E@-7@-50-75E@5@-0-75E@-@505E-7@-@5 @9L2C@92@962C@2@9P@2KP2@2K@@2P@2K@@2 23@2@P@2K@@2 @2/@2P@9L2CP9@22-@919@2P@2KP@2@2,2@P@2KP@2@2<@2P@2KP2@2,@2@P-75E@-5@ -7@5E--1 -@55E@-1-5@ 5E@-75-@05E-7@--15@--7@5E@5-0@-15E-@5 5M@-8-5@ @5H-:-5@0@5H-:@5-05M->@-@50@->5M-@5 5S@-B-5@05S-B@5-@0@-B5S@-50-B5S@-5@ 7B@@S7@@0@@S7B@7@0@7B@S@7@0@S7B@7@@0@S7B@@@70@7B@S7@@0@7B@S@@70@S@7B@7@05B-5@@@5-/MTrkecb@cb @cb @cbc@cbd@cbf@ed!JAZ2 @YJ<General MIDIBaseYY[ A@HK]}C[HK@J<]C!$SP$$EP$$GP$$ZP$$GP$$LP$$WP$$GP$$SP$$EP$$GP$$ZP$$GP$$LP$$WP$$GP$+UP++GP++NP++ZP++GP++JP++SP++GP+$SP$$EP$$GP$$ZP$$GP$$LP$$WP$$GP$+SP++EP++GP++ZP++GP++LP++WP++GP+!UP!!GP!!NP!!ZP!!GP!!JP!!SP!!GP!&SP&&EP&&GP&&ZP&&GP&&LP&&WP&&GP&&UP&&GP&&NP&&UP&&GP&&JP&&SP&&UP&!SP!!EP!!GP!!WP!!GP!!LP!!SP!!GP!&SP&&EP&&GP&&ZP&&GP&&LP&&WP&&GP&![P!!LP!!TP!!`P!!PP!!RP!!^P!!PP!!eP!!UP!!]P!!kP!!UP!!XP!!cP!!UP!&^P&&NP&&PP&&eP&&LP&&QP&&]P&&LP&&UP&&GP&&NP&&UP&&GP&&JP&&SP&&UP&&SP&&EP&&GP&&WP&&GP&&LP&&SP&&GP&&UP&&GP&&NP&&ZP&&GP&&JP&&SP&&GP&+SP++EP++GP++ZP++GP++LP++WP++GP++SP++EP++GP++ZP++GP++LP++WP++GP+!UP!!GP!!NP!!ZP!!GP!!JP!!SP!!GP!!UP!!GP!!NP!!ZP!!GP!!JP!!SP!!GP!&SP&&EP&&GP&&ZP&HKJ<@[]C!)NP))@P))GP))WP))@P))BP))LP))@P)+LP++>P++@P++UP++@P++EP++PP++@P+!KP!!=P!!OP!!EP!!KP!!@P!!RP!!AP!&JP&&P&&@P&&UP&&@P&&EP&&PP&&@P&&QP&&BP&&EP&&[P&&HP&&MP&&ZP&&HP&!YP!!IP!!^P!!RP!!YP!!LP!!aP!!NP!!aP!!RP!!gP!!ZP!!aP!(UP((MP(!k`!&\&!NP!!@P!!GP!!WP!!@P!!BP!!LP!!@P!!NP!!@P!!GP!!WP!!@P!!BP!!LP!!@P!&NP&&@P&&GP&&WP&&@P&&BP&&LP&&@P&&NP&&@P&&GP&&WP&&@P&&BP&&LP&&@P&&LP&&>P&&@P&&UP&&@P&&EP&&PP&&@P&&KP&&=P&&OP&&EP&&KP&&@P&&RP&&AP&)JP))P&&@P&&PP&&@P&&EP&&LP&&@P&&NP&&@P&&GP&&WP&&@P&&BP&&LP&&@P&+LP++>P++@P++UP++@P++EP++PP++@P++NP++@P++GP++WP++@P++BP++LP++@P+$LP$$>P$$@P$$UP$$@P$$EP$$PP$$@P$!KP!!=P!!OP!!EP!!KP!!@P!!RP!!AP!&JP&&P&&@P&&UP&&@P&&EP&&PP&&@P&&KP&&=P&&OP&&EP&&KP&&@P&&RP&&AP&!SP!!EP!!LP!!]P!!HP!!JP!!UP!!HP!!\P!!MP!!UP!!hP!!MP!!OP!!ZP!!MP!&WP&&HP&&PP&&bP&&EP&&GP&&QP&&EP&&NP&&@P&&GP&&WP&&@P&&BP&&LP&&@P&&LP&&>P&&@P&&UP&&@P&&EP&&PP&&@P&&KP&&=P&&OP&&EP&&KP&&@P&&RP&&AP&)JP))P&&@P&&PP&&@P&&EP&&LP&&@P&&NP&&@P&&GP&&WP&&@P&&BP&&LP&&@P&+LP++>P++@P++UP++@P++EP++PP++@P++NP++@P++GP++WP++@P++BP++LP++@P+$LP$$>P$$@P$$UP$$@P$$EP$$PP$$@P$!KP!!=P!!OP!!EP!!KP!!@P!!RP!!AP!&LP&&>P&&@P&&UP&&@P&&EP&&PP&&@P&&LP&&>P&&@P&&UP&&@P&&EP&&PP&&@P&!KP!!=P!!OP!!EP!!KP!!@P!!RP!!AP!!KP!!=P!!OP!!EP!!KP!!@P!!RP!!AP!&JP&&P&&@P&&UP&&@P&&EP&&PP&&@P&&KP&&=P&&OP&&EP&&KP&&@P&&RP&&AP&)JP))P&&@P&&PP&&@P&&EP&&LP&&@P&&NP&&@P&&GP&&WP&&@P&&BP&&LP&&@P&+LP++>P++@P++UP++@P++EP++PP++@P++NP++@P++GP++WP++@P++BP++LP++@P+$LP$$>P$$@P$$UP$$@P$$EP$$PP$$@P$!KP!!=P!!OP!!EP!!KP!!@P!!RP!!AP!&LP&&>P&&@P&&UP&&@P&&EP&&PP&&@P&!KP!!=P!!OP!!EP!!KP!!@P!!RP!!AP!&JP&&/@7/P@>7>-@727@>0><7>@P7>@7-@>2P7>@7/>*@ >07@p@7->=P>7@C2@<$P<@C@C&2@7$P7@>7!>&@>7@ 7%@>6P>@7@>474P@7>@>17' >@77/>'@P7>@9/@@/P@@992@@-9@@0@<@9>P@9@@9-@2P@@9@9/@* @09@p@@=9-P@@92$92@P92@9&@2!92@ @962%P2@99424@P9@291@2' 92@9'@2/P9@22/@9/P2@9229-@29@0@2>9P29@@2-92P@292/@9* 902@p@2-9=P29@@922$P92@@2!9&9@2 @962%P@29@2494P@292'91@ 92@9'2/@P2@92/@9/P92@9-@229@209<@2>P92@@2-92P29@9*@2/ 902@p@2-9=P9@2@7$>2P>7@@>&7!>@7 >6@7%P>7@@74>4P@7>@7'>1 >@77/@>'P@>7@>27$P7@>>&@7!>7@ @7%>6P@>7@74>4P>@7>17'@ >@77/>'@P>@7@/9/@P@9@@-92@@@909>@@@P@9@@9-@2P@9@@@*9/ @09@p9-@=@P@@92$@92P92@2!@9&92@ 2%@96P@92@HKJ< ^[2]2CA%%7">C7@P>)@C)7"P@C7>>"7@C C>7@P7"@>3C,P>C7@>)C)7"@7C@>P>)@C)7"P7@>C7"@>)C)@C>7P@C)7">)P@>7C9@@(C$@C@9PC+@@)9#P@9C@@)9@C$9@C@P9#C.@.@P9@C@9#@(@C(@9C@P@)@9#C)P@9@CC)9#@@)@9C@P9#@@)C)P@9C@>$2@9$>29@P>)@9)2#P@>29>!9#2 @29>@P@>)2#9)P9>2@@2+9->1@29>P2#@9)>)P@2>9>820@962@9>P9)2#@>)P@9>22>%@9!9@2>P@2$>*9*P92>@29>$@>9@2P>*2$@9*P@>299*>*2$@2>9@P>*2$@9*P>29@@9*>*2$>92@P@>*2$9*@>92@>%2!@9%>2@9P>*@9*2$P2>9@9"2"@>"@>29P>*@2$9*P@92>2$>*@9*92@>P9*@>*2$P@9>2>"@9%2!92@>@2$9*>*@P2@9>@> 9(2$29@>P@9+2$>+P>@929$>"2@>29@P@96>/2$P9@2>9.@>.2'9>2@P@9.2'>.P>@299.@2'>.>9@2P9.>.@2'P@>92@@09C,@9@CP@2C49*@P@@9C@9!C,@2C9@@P9*C8@@8P@C@9@C09*@09C@@P@2C29*@P@C9@@2@C29*@@9CP@9*@2C2P@9@C@0C,9@@C9@P@9*C4@2P@C9@@2@9!C,9@C@P9*C8@@8P@@9C@0C09*@9@C@P9*@2C2@P@9@C9*@2C2@9@C@T@>@94C>T4$9@(@99@4P@)4+9#1C9@4@@@)4$9@94@P9#@.4.@@@4@942@9'@-9@ @4/4)@9#@)P@9@4@)@4)9#@@94P4)9#@)@P@@499@(4$@@@49P@)@4+9#P94@@4$@)@9@@49P@.9#4.@@@4@942@@-9'@9 @4/4)9#@@)P@94@@)9#4)@@94@P9#@)4)@P@94@@9)>"2"2>9@P@>+9)2"P92>@2@>99@2>P9.>.@2"P29>@>)@2"9)>@29P@>)2"9)P>92@9)>)@2"2@9>P2"@>)9)@9@2> 29!>%@>29@P@2$>*9*P>2@9@29>$2>@9P>*9*@2$P2>@92$@>*9*29>@P>*2$@9*P>@29>*@9*2$@9>2P9*2$@>*@9>2@9%2!>%@9>@2P>*2$@9*P9>2@9">"2"@29@>P2$@9*>*P9@>29*>*2$@>29@P2$>*@9*P@>299%>"@2!92@>@@9*>*2$P>29@29(>$@92@>P9)2#@>+P@>299)>$2@@>92P@>.2#9.P@29>>(2#@9(>2@9P9)@2#>)P>2@9@2#9)>)9@2>P9)@2#>)P>9@25@<$A$@A5<PA)@5#<)P5A@<@5 A!<#A<5@PA)<)@5#P@5A<$@29$>92@P@2#>)9)P@29>2 >!@9#9>@2P@2#9)>)P>@29>19-2+@9@2>P>)2#@9)P>@922096>8@>92@P@2#9)>)P>29@>%2@5!25>@P>*2$5*@P@5>225@>$2>@5P@>*5*2$P@52>>*2$@5*52@>P@5*>*2$P>@525*@2$>*5@2>P2$5*@>*@5>@27!C%:%@:@C7PC*7$:*@P@C7:7"@:"C":C7@P:*C*@7$P@C:7:*7$@C*:C@7P@C*:*7$P7C@:7!C"@:%7:C@@:*C*@7$P7@C:7C%>!@>C@7P7$@>*C*PC>7@7C$>@7@C>P7$C*>*@P@>C7C*@>*7$C7>@P>*C*7$@PC@7>>*7$@C*7C@>P@>*7$C*@C>7@C%<%@<C@P@<*C*P<@C@C"<"@<CP<*@C*P<@C<*C*@<@CPC*@<*PC@<<"C%@C<@@@<*C*PC<@E$@(9@@@E9PE+@)@9#PE9@@9E$@)@@@9EP9#E.@.@P@E9@@C(@(9#C@@9P9#@C)@)P@@9CC)@@)9#@@C9P@9#C)@)P@9C@>$9$2@>9@2P@9)2#>)P2>9@>!@9#2 @2>9P2#@9)>)P9>@22-9/>-@2@9>P9)@2#>)P9@>22893@>-9>@2P2#9)@>)P@2>9@9@(4$94@@P@9#@)4+P94@@4$9@@)@@49P@.4.@9#@94@@42@@-9'9@ @4/@@)9#4)P@@494)@@)9#4@9@P9#@)@4)P@4@9>"2"@9)92>@P2"@>+9)P2>9@@>29@2>9P>.@9.2"P>2@9[2@J<CHK]2 ^2/9/@P29@@229-92@02>@9@P9@22194@P9@2@9F2>P@92@9:2:P92@94@2!P92@9824@P29@2,9/@`9`982#2P@29@9;28P2@9922/@`959225P2@928@9292@09D2F@P92@9520@P@299,22@ 90@2p@2/9AP@29@2(98P@92@2%9+92@ 2*@9927$P>7@@>&7!>7@ >6@7%P7>@@>474P@7>>1@7' >7@@7/>'P7>@>/@7/P@>772@>->@70><7>@P7@>@7->2P7>@7/>*@ >07@p@>=7-P>7@>2@7$P7@>>&7!@>7@ @>67%P7@>>4@74P>@7@>17' >7@@7/>'P@>7@>/7/P7>@@>-72@>70@><7>P7>@7->2@P>7@7/@>* >0@7p>=7-@P>7@5$<2@P@<5@5!<&<@5 @5%<6P<5@@<454P5<@<15'@ <@5<'@5/P<5@P<@5@5-<2P<5@<*5/@ <05@p@5-<=P5@<@922$P@292!@9&92@ 2%@96P29@24@94P@922'@91 92@@2/9'P@295/P@<55-<2@P@5<5/<*@ <0@5p@<=5-P@<57$@>2P7>@7!@>&>7@ >67%@P>@7@74>4P7@>>17'@ >@7@7/>'P7>@@@/9/P@@992@-@@@90@@<9>P@9@@9-@2P@@9@@*9/ @09@p9-@=@P@9@2/@9/P29@9-22@@2902>@9@99<@P@922-92@P9@22/9*@ 902@p2-@9=P2@9@@292P@9@@/95@@9@0@?9A@P@@9@93@8P@@9@0@95 @0@9p@E@92P@9@99@9@P@9@@6@9<@@90@H9K@P@9@@<96@P@9@99@3@ @0@9p96@@JP9@@2595@P2@992@2829@0@9D2FP2@920@95P@299,22@ 902@p2/@9AP2@9@2/9/P9@2229-@@2909<@2>P@292-@92P@292/9*@ 902@p2-9=@P29@>27$@P@>7@7!>&>7@ 7%@>6P>@774>4@P>@7>1@7' >@7@7/>'P>@77/@>/P7>@@72>->@70><7>@P7@>>27-@P@>77/>*@ >07@p@>=7-P@>7@7$>2P>@77!@>&>@7 >67%@P>@7>474@P>7@7'>1@ >@7@7/>'P@>7>/@7/P7@>@72>->@707>@>7-@>2P@>77/@>* >0@7p7-@>=P7>@5$@<2P@5<5!@<&<@5 <65%@P5<@@<454P@5<@<15' <5@@5/<'P<5@5/@<@P@<5<25-@P5<@<*@5/ <0@5p<=5-@P5<@7$@>2P@7>7!>&@>7@ >6@7%P>@7@>474P7>@>17'@ >@7@>'7/P@>7@9/@/P@9@@92@-9@@0@@<9>P@@9@@29-P@9@@*@9/ @09@p@@=9-P@@9@9/2/P@929-22@92@0@9<2>P29@ ^]2@C[2HKJ<@9!2>%@29>P@>*9*2$P@9>229@>$@9>2P2$>*@9*P>9@2>*2$9*@2>9@P>*2$9*@P2>@9>*2$@9*>92@P9*@2$>*@>92@9%>%@2!>92@P9*2$>*@P@92>@2"9">"29>@P9*@>*2$P@92>9*2$@>*>9@2P>*@2$9*P>9@2@>"9%2!92@>@9*>*2$@P2>9@9(2@>$2@9>P9)>+@2#P>9@22>$9)@9>@2P9.>.@2#P>92@2#@9(>(9>2@P@>)2#9)P@2>9@9)>)2#2@>9P@>)2#9)P@>299@@*4'@@49P4.9%@,@P4@@94'@@,9@9@4P@@19%41@@49@@459,@2@9 @4/9'@.4.@P@@49@4.@.9'@@94P4.@@.9'P@49@@94,@049@@P@29*@44P9@4@4,9!@@24@@9P@89*48@@49@@@4<9/@6@9 @4/429*@2@P@@94@@2429*@49@P9*@242@P94@@9.>'@2'@92>P@>09.2'P>@29>"@9"2>9@2P94@>42'P29@>>+2$@9+92>@P>+2$@9+P2>9@>+2$@9+@9>2P9+@2$>+@@>29 29!@>%@2>9P2$@9*>*P2@>99>$2@@29>P9*@>*2$P@>929*2$>*@9>@2P2$@>*9*P92>@>*2$@9*92>@P9*@2$>*@9>@2@9%>%2!@2>9P>*9*@2$P>@29>"2"9"@@>29P@2$>*9*P>92@9*>*@2$@92>P9*2$@>*P92@>2!9%@>"29>@@2$@>*9*P2@9>@29(>$>@29P@9)2#>+P@92>2>$@9)@2>9P9.@2#>.P@>929(2#@>(9@2>P9)@>)2#P2@>99)@2#>)29>@P9)2#>)@P>9@2@5<$A$@<5APA)<)@5#PA@<5@5 <#A!<A@5PA)@<)5#PA5@<$29$9>@2P>)9)@2#P2>@9@2 9#>!9>@2P9)2#@>)P92>@@>19-2+>2@9P2#@>)9)P@9>22096@>8@92>P9)2#@>)P2@>9@>%25!@5>2P2$5*>*@P@52>>$5@2@>25P@>*5*2$P>25@2$5*@>*@>52P5*2$@>*P2>5@5*@>*2$@>25P5*>*2$@@>5@2:%C%@7!@7:CP7$:*@C*P7C:@C"7"@:"@C:7P@:*C*7$PC@7:C*7$:*@7C@:P:*@7$C*PC@7:@C":%7!7:@C@C*7$:*@P:C@7>!C%7@@7C>P>*C*7$@P@C7>@7>C$C7>@P7$>*C*@PC7>@>*7$C*@7>@CP@7$>*C*P@C7>C*7$>*@@>C7P7$@C*>*@>C@7<%C%@C<@PC*<*@P<C@@<"C"C@<PC*<*@PC<@<*@C*@C<P@<*C*P@C<C%<"@C@<@<*C*@PC@<@9E$@(9@@EPE+@)9#@P@@9E9@)@E$E@@9P@E.@.9#P@@9E9#@@(C(@9@CP@C)9#@)P@9C@@)@9#C)@@9CP@C)@)9#PC9@@9%@>2">92@P@>)2"9)P@2>99"> @29>@2P@2">,93P>9@2@9)2">)>2@9P9)2">)@P9>@2@9)2">)>29@P>)2"@9)P>@292">@9%@>92P2"9)>)@P29@>29"@> >@92P>,2"@93P@>29@>)9)2"@>92P@2">)9)P2@9>9)>)2"@2>@9P9)>)2"@P@2>9@(C$@99C@@P@9#C+@)P@C9@@)C$@9@@9CP9#C.@.@P@C9@@9#@(C(@@9CP@C)9#@)P9@@C9#@@)C)C@9@P@)C)@9#PC9@@9C$@@(@9@CP@)9#@C+P@@C9@@)9C$9@C@PC.9#@@.P9@@C@@(9#C(@C9@P9#@@)C)PC@9@9#@)C)@C9@@P@9#@)C)P@9C@9$>$2@@2>9P>)9)@2#P92@>@2 9#>!>@29P2#>)@9)P2@9>>1@2+9-@29>P>)@2#9)P92>@9620>8@@92>P2#@>)9)P2@>9@>%29!>@92P@2$9*>*P29>@@>$922>9@P9*2$>*@P92@>9*2$>*@>2@9P2$@>*9*P29>@9*2$>*@@92>P9*2$>*@@>9@2@9%>%2!29@>P@>*9*2$P@92>@>"9"2"2@>9P9*2$>*@P>92@@2$>*9*@2>9P2$9*@>*P@>29>"9%2!@92>@@2$9*@>*P@2>9@>$29(2>9@P>+2#9)@P>29@>$9)2@@92>P>.9.@2#P>@29@9(>(2#9>2@P@>)9)2#P>9@29)@2#>)@29>P9)@2#>)P@92>@A$<$5@<5AP5#@A)<)P<A@5@A!5 <#@A5<PA)5#@<)P@5A<@A-$92@>P>)9)@2#P>92@>!2 9#@>2@9P2#9)>)@P@92>9->1@2+92>@P@2#9)>)P92>@96@>820@29>P2#9)@>)P@29>@>%5!2@5>2P@2$>*5*P5@>2@2>$5@>52P2$5*>*@P52>@@>*5*2$@5>2P@5*2$>*P5@2>5*2$>*@@>25P>*2$5*@@5>2@:%@C%7!C:7@P7$C*@:*P@C:7:"C"7"@7@C:P:*@C*7$PC:7@C*@7$:*@C:7P:*C*@7$P@:7CC"@:%7!:7@C@:*@7$C*PC7:@>!7@C%@C>7P@C*>*7$PC@>7C$>@7>7@CP@7$C*>*P>C@7C*7$@>*C@7>P@>*7$C*P7@>C7$>*@C*@C>7PC*@7$>*@>C7@<%C%@<C@PC*@<*P<@C@C"<"<C@P<*C*@PC@<<*C*@C<@P<*@C*PC<@C%<"@C<@@@<*C*PC@<@(E$9@@@E9PE+@)9#@P9@E@9@E$@)E@@9P9#@E.@.P@E9@@(9#@C(@@9CP9#C)@@)P@9@C@)@C)9#C9@@P9#C)@)@P@9C@>2"9%@29>@P2">)@9)P9>@2@29"> 9>2@P93>,@2"P@9>29)>)@2">92@P>)9)2"@P>@929)2">)@9>2@P@9)>)2"P>29@@(9@C$@C@9P@9#C+@)P@9@CC$9@@)@9@CP@.@C.9#PC9@@@@(9#C(C9@@P@9#@)C)P@@C99#@@)C)@9C@PC)@)9#@P@C9@2@>$9$2@9>P>)9)2#@P>92@@9#2 >!9@2>P@9)2#>)P9>2@CHKJ<@]2[2 ^2/@9/P@9222@9-9@202>9<@P92@@2-92P@299*2/@ 902@p2-9=@P92@@9:2:P2@991@2>P92@2194@P92@9F2>@P92@2:9:@P@2994@2!P@299824@P@292,@9/@98922#P9@2@9;28P2@9@922/`952259P2@928@9292@0@9D2FP@2995@20P92@@229, 902@p2/9A@P29@92@2$P@292!9&@92@ 2%@96P29@@2494P29@91@2' 9@22/@9'P92@@2/9/P@92@9-22@9209<2>@P@92@2-92P92@@9*2/ 902@p9=2-@P2@92$@92P92@9&2!@9@2 2%96@P2@9@9424P92@@2'91 9@29'2/@P92@2/@9/P2@9229-@9@202>9<@P2@9@922-P9@29*2/@ 902@p2-9=@P@2992@2$P@92@2!9&9@2 962%@P29@9424@P@92912'@ 92@2/9'@P@292/@9/P@9222@9-@290@9<2>P@922-@92P9@22/@9* 90@2p@2-9=P@29@922$P92@2!@9&92@ 2%@96P@92@2494P29@912'@ 9@29'@2/P92@@2%95P@92@2#9(9@2 @2'98P29@2;9;@P9@2982,@ 9@2@9,25P@92@2+9JP>@A?C?J?@;?7?`;7 5[A0CJCP>C575J5:50JC7:@09C@P91GP1@JP@E?9?L?1?C?`91 7[C0EL2CP25C 705ECPE>555E52505E2>02CP2@@5CP5ECPEE55525>5`25 2C>02E9CP92CP2@CP@95L525E50L2E902CP25CP5ECP@EE5>52555@052E>09GP91JP1@MP@9BEBLBCB1B`91 7bC0EL9PP91S 701@VP@1I9ICILIEI`19@ 7g@C0EL2KP25K 705EKPE5<><2<E<0E2>502CP25CP5ECPE55>525E5`25 2C>02E2CP2@@7CP7ECPE75>5E5250>2E70E5A5>5@25@p>E2A0E5>5A525 2 -<EE<@- 2C@2 -C >EA - 7CP7:CP:>CP>J5C575:50JC:7@0@7CP7:CP:>CP>75C5:5J50C:7J09CP95`]A @[ACHKJ<@2>5EJ070C0F0@pC@7JF0C0@J070F0@CFJ`7 F0J0>0C0@> :<@: 7<JCF@7 @E222@A2>2p2E>A022E2>2A2@E>A`2 A6E6>6@E 5D@5 2D>A@2 E?L?C?I?9?p@CI9LE0@9P@9 E?C?@L?@9?I?@L9ECI 4H@H@4`@ 9?I?L?E?C?`I@P>K@ AKEEK JKCL9C0@I090E0L0@E>A_JI9LCEI0E0C090L0p9ELCIE0@20>0A0@p2A>E0E020A0>0 2 -6EE6@- 2<@2 -< A>E - 20@@>0E0A0p2A>E0E8>828A8 2 ED-DE@- 2N@2 -R >AE - @H0E0A0@50pE5AH0A0H050E0@EAH`5 A0E0H0<0@< 9<@9 EHA5<@5 H050A0@E0@pEA5H0A050H0E0 5 HH606@0 5<@5 0< EAH 0 20@>0E0@A0pAE2>0E0A020>0@E>A`2 >0A0E0@E 5<@5 >A2<@2 @@L0E0I090pEL9I0L8I8E898 9 4DLLD@4 9N@9 4R ELI 4 @A020@>0E0p2EA>0E020>0A0@EA>`2 A0>0E0@E 5<@5 A>2<@2 E0J0C090L0pCJ9LE@E0A0>0@20p2EA>0E020>0A0`C @J<HK[A]A@@2CP25CP5ECPEE55525>5`52 2C>02EE828>8A8pA@2>E0@2G@2 2<@E<><A<@@2A>E EC-C@-`E A<2<><E<`>2AEP>>PA APEEPJP >A/2P2KP25KP5EKE@J/E5<@><E<2<`52 2G>02E2KP25KP5EKPE2<E<><5<0>52E02PP25PP5EPPE2?>?5?E?0E52>@09P@P91SP1@VP@1ILI9IEICI`19 7gC0EL9PP91S 701@VP@LI9I1IEICI`@91 7g@C0EL2KP25K 705EKPE2<E<5<><0>2E502CP25CP5ECPEE55525>5`52 2C>02E7CP@7:C@P:>CP>75J5C5:50J:C70C5@@F575J5pCJF70F575C5J5 7 J2<J<@2 7C@7 2C CJF 2 75J5F5C5pCF7J02C@@2 J5@75F5C57CJF0F5J5C575`JFCP77CP7:CP:>CP>C5:575J5`:7 7CC07J5CP@59C@P95A525pE@>2A0@-C@- A5>5@@25E5AE2>0E5>5A525`E>AP25CP59CP9CP>J5:5C5750:J7C09CP91GP1@JP@L?C?E?9?1?`19 7[C0E7L2CP25CP5@EC@PE>555E525`52 2C>02E2CP25CP5ECPEE525>555`52 2C>02E9CP92CP2@CP@@E5L595@25092EL0>5@25A5E5@pEA>20>525E5A5 2 EE<-<@- 2C@2 -C A>E - 9GP91JP1@MP@9BLB1BEBCB`@91 7b@C0E7L9PP91SP1@VP@EI9ILI1ICI`91 7gC07EL2KP25KP5EKPEE<><2<5<`52@ @2G>02E2CP25CP5ECPE>52555E5`52 2C>02E7CP7:CP:>CP>75:5J5C507JC:0C575@F5J5@pFJ7C0J575F5C5 7 JJ<2<@2 7C@7 2C JFC 2 J5F5C575p7FJC02C@2@ C5J575F5@7FCJ0C5F5J575`CJFP77CP7:CP:>CP>:575J5C5`:7 7CC07J5CP@59C@P9525p@>EA20@-C@- A5@25>5@E5>E2A0E525A5>5`E>AP25CP59CP9CP>:575C5J50J:7C09CP91GP1@JP@C?9?E?1?L?`91 7[C0E7L2CP25CP@5@ECPE25E555>5`CJ<]A[A@HK @@>0>5E2E020A0pAE>2028A8>8E8 2 -DEED@- 2N@2 -R >@AE - @I2L292C2E2pEILC9I8E898C8L8pECL9I@26@>6A6E6pA>2E0A626>6E6 2 EE8-8@- 2@@2 -@ >EA - A0@20>0@E0pA2>E0A828>8E8 2 -DEDE@- 2N@2 -R EA> - E0A050@H0@pHE5A0A050H0E0@EAH`5 A0<0E0H0@< 9<@9 HA5<E@5 50E0@@A0H0pHAE50E0H0A050 5 HH606@0 5<@5 0< EHA 0 @A0@20E0>0p>AE20>0A020E0@EA>`2 >0A0E0@E 5<@5 A>2<@2 @@L0E090I0p9LEI098I8L8E8 9 LDL4D@4 9N@9 4R ELI 4 @A0@E020>0p2EA>0A0E020>0@>AE`2 >0E0A0@E 5<@5 2<>A@2 @>0A020E0@pAE2>0>020E0A0@E>A`2 >0A0E0@E 5<@5 >A2<@2 @A0>0@E020p2E>A0A8>828E8 2 ED-DE@- 2N@2 -R AE> - @E0H050A0@pHE5A0E0H050A0@AEH`5 H0A0<0E0@< 9<@9 EH5<A@5 @E0A0@H050p5HAE050A0E0H0 5 06H6H@0 5<@5 0< AEH 0 >0@A0E0@20pA>2E0E0A0>020@>EA`2 E0>0A0@E 5<@5 >A2<@2 @I0L090E0@pEL9I098E8I8L8 9 L4DLD@4 9N@9 4R LEI 4 @@E0A020>0p2A>E0E0A0>020@>AE`2 A0E0>0@E 5<@5 >A2<@2 [AHK@J< @]AC@2CP25CP5ECPEE5>55525`52@ @2C>02E>828A8E8p2EA>02G@2 @A<><2<E<@@E>A2 EC-C@-`E E<A<><2<0A>P> APEJPEP >A/2P2KP25KP5EKE@J/EE<@5<><2<`25 2G>02E2CP25CP5ECPEE555>52502E5>0@E5@25A5>5p2>AE0E5>5A525 2 E<-<E@- 2C@2 -C >EA - E5>5A525p2A>E0-C@-@ @25>5E5A5>2EA0A5>525E5`AE>P22CP25CP5ECPE5525>5E5`52 2C>02E2CP2@@5CP5ECPE5525>5E5052E>0@A5>5E525@p2>AE0A5>5E525 2 E-<E<@- 2C@2 -C EA> - A5E5>525pEA>20-C@-@ >5@E525A52>EA0>525A5E5`A>EP22GP25GP5EGPE>8E8285802>5E02PP25PP5@EP@PE5?E?>?2?05E2>0%c @General MIDI Echo-Part-01YY[@CHK@}]J< ?>Q"QM"MJ"Q"JM"QV"M>"V>A">"AC">C"CH"CHJ"JH"HF"E"F>"E>A"@"A@"@>"@9%>>%9A">>"A>>">">>">>C"CH"HJ"H"JF"HFE"E>">A">%A9)>J%9H"JE"HEC"CF"FH"L"HL"LJ"LJM"ML"LJ"JH"HE"EC"CF"FH"HL"L"LJ"LJL"LL"LJ"E)JE>%>A">"A>">>C"CH"HJ"JH"HF"FE">"EA">A@"@@"@>">9%9>%A">>"A>">>C"CH"HJ"JH"HF"FE">"EA">A>">9%9J%JH"HE"C"EF"CH"FHL"LL"LJ"JM"ML"LJ"H"JE"HEC"CF"H"FL"HLL"LJ"L"JL"LLJ"Q%JQM"MJ"JQ"QM"V"MT)VTJ@J/MTrkcb@cb @cb @cbc@cbd@cbf@ed0JAZ2>%General MIDI Echo-Part-02YY h[@CHKJ<@ @]_}>Q"M"QMJ"Q"JQM"@Q"M`R"Q`V"RV>">A">"A>C"C"C@E"C`EF"`FH"J"HH"JF"HFE"C"E@CA"@A>"A">@"A@@"@>"@<">`9"<`99%@9:)`:<)`>%<>A"A>">>">>">">C">@CE"`EF"`H"FHJ"JH"HF"FE"C"E@A"C@A>"A">A>%@><)`9)<`99)9J%JH"E"HEC"CF"H"F HJ"`L"JLL"LJ"M"JML"LJ"JH"E"HEC"CF"H"F HJ"`JL"LL"LJ"JL"LL"LJ"@JH)`E)H`E)EEC)@CA)@A>%>A"A>">">C">@E"C`EF"`FH"HJ"JH"HF"FE"C"E@A"C@A>">A"A@"@@"@>"@<">`9"<`9%9@9:)`:<)`<>%>A"A>">>"C">@E"C`EF"`FH"HJ"JH"HF"E"FC"E@A"C@A>">A"A>"@><"`<9"`99%J%9H"JHE"EC"CF"FH" HJ"`L"JL"LJ"LJM"ML"LJ"JH"HE"EC"F"CH"F HJ"`JL"L"LJ"LJL"LL"J"LQ%JQM"J"MJQ"QM"@MQ"`QR"`V"RT)VTJ@J/MTrkcb@cb @cb @cbc@cbd@cbf@ed0JAZ2 @J<General MIDI Echo-Part-03YY "[@]@b}HKC>%A%G"C">AGA"C>">G"C"A>"A"GC>G"AC"E"@"GC@A"E"E@@>"A"AEA>9"="9A">"=E%C%A> =)9)EC`=9>%A%F"AC">9"FC<">"9<A"E">A"AC"F"EA@"E"FC@@EE"A"@A>"EA">AE)C)A%>%CEE"A">AAA"E>">AC"F"F="C9"A">"9=>9"A="9=A">"E"A"A>A"EA>"AC">F"CF9"="A">"=99"=">A=9A">"E)>AC)@)E)EC@@E)EA)@EA>%A%AF">C"CE"A"FEA>"A">AA"E"AF"EC"@"E"FC@EE"@A"@EAA">">9"A="=>"A"9>AC%E% CE=)9)`=A%9>%F"C"A>CA"FE"AEA">">E"AA"AC"F"ECFE"@"@E@E"A"@E>"AA"C%E%>AEA%C>%A>A"E"EA"A>">C"F"ACF9"="9=A">"9"A>="=>"A"9A>A"E"EA>"A"F"C">A="FC9"A">"9=A>="9"9=>"A">ApH)C)CH>@>/MTrkScb@cb @cb @cbc@cbd@cbf@ed0JAZ2A%@General MIDI Echo-Part-04YY ^[@]CHK}_9% @J<>"9"A9>";"9>A"9";>>"9"A9@9"A"9>`9":"A9`9;">":9";>">9;">>">>"C";CA">>"@A>C">"@C"<"C><C>";">;C"<">"C"C<C@">9"9@9">"99"@">9@9">"@9"A"9>@9A>%9%>9>"C">@"9"C9">"@99"<">9>"C"9<>C@"9"9">"@9@99)>A)@9A9%>%99"><"<C">"9C><"C"@"C<9"@>"99"@"9">9>"@99">99"<"9>"<C"<"C">CC9"<@">"9"@9>99"@"9"@>"9@9>A)9)@A99%>%9C">>"<"9"C>99"<>"9>9"<"<C"9>">@"9"C@9>"9">99"@"99"@>"@9"A"9>@A9%9>%9>>"C"C9"><"<9">"99>9"<"9<>"C">9"C@"9">"9@@>9"9A"@9A9%>%><"99"9>"C"<>CC"<"C<9"@"99"@>"@"9"9>9>"@9">99"<"<C"9>"><"C"C<C9"@"9@>"9"9">9@"@9">"9>9A%9%9A9">"9"A"9>9"9A>"@9"A">9`9A:"9"`9:9">"9<)C)><C>@>/MTrk#JAZ2/MTrk4JAZ2Boring afternoon/simutrans-124.5/simutrans/music/06-A-busy-day-at-the-depot.mid000066400000000000000000000744411520675460500240650ustar00rootroot00000000000000MThd `MTrk-(c) shunter 2006 ~ JAZ2Q5y(My Song /MTrk JAZ2` @<+T< <+T< <+*<<+*<<+*<<+* @<;+T; ;+T; ;+*;;+*;;+*;;+* @;<+T< <+T< <+*<<+*<<+*<<+*< @;+T; ;+T; ;+*;;+*;;+*;;+*; @<+T< <+T< <+*<<+*<<+*<<+*< @>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+*> @<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+* @<;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+*; @<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+*< @;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+* @;=+T= =+T= =+*==+*==+*==+*= @=+T= =+T= =+*==+*==+*==+* @==+T= =+T= =+*==+*==+*==+*= @=+T= =+T= =+*==+*==+*==+* @=>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+*> @>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+*> @<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+*< @;+T; ;+T; ;+*;;+*;;+*;;+*; @<+T< <+T< <+*<<+*<<+*<<+*< @;+T; ;+T; ;+*;;+*;;+*;;+* @;<+T< <+T< <+*<<+*<<+*<<+*< @>+T> >+T> >+*>>+*>>+*>>+*> @>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+*> @<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+*< @;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+* @;;+T; ;+T; ;+*;;+*;;+*;;+*; @<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+*< @;+T; ;+T; ;+*;;+*;;+*;;+* @;;+T; ;+T; ;+*;;+*;;+*;;+* @;;+T; ;+T; ;+*;;+*;;+*;;+* @;;+T; ;+T; ;+*;;+*;;+*;;+* @;=+T= =+T= =+*==+*==+*==+* @==+T= =+T= =+*==+*==+*==+* @==+T= =+T= =+*==+*==+*==+*= @=+T= =+T= =+*==+*==+*==+*= @>+T> >+T> >+*>>+*>>+*>>+*> @>+T> >+T> >+*>>+*>>+*>>+*> @>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+*> @<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+*< @<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+* @<<+T< <+T< <+*<<+*<<+*<<+* @<;+T; ;+T; ;+*;;+*;;+*;;+* @;;+T; ;+T; ;+*;;+*;;+*;;+* @;;+T; ;+T; ;+*;;+*;;+*;;+*; @;+T; ;+T; ;+*;;+*;;+*;;+* @;;+T; ;+T; ;+*;;+*;;+*;;+* @;;+T; ;+T; ;+*;;+*;;+*;;+*;/MTrk JAZ2` @@+T@ @+T@ @+*@@+*@@+*@@+* @@>+T> >+T> >+*>>+*>>+*>>+* @>A+TA A+TA A+*AA+*AA+*AA+*A @@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+*@ @B+TB B+TB B+*BB+*BB+*BB+* @BB+TB B+TB B+*BB+*BB+*BB+* @BB+TB B+TB B+*BB+*BB+*BB+*B @@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+*@ @A+TA A+TA A+*AA+*AA+*AA+*A @A+TA A+TA A+*AA+*AA+*AA+* @AA+TA A+TA A+*AA+*AA+*AA+*A @A+TA A+TA A+*AA+*AA+*AA+*A @>+T> >+T> >+*>>+*>>+*>>+*> @>+T> >+T> >+*>>+*>>+*>>+*> @>+T> >+T> >+*>>+*>>+*>>+*> @>+T> >+T> >+*>>+*>>+*>>+* @>@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+* @@B+TB B+TB B+*BB+*BB+*BB+* @BB+TB B+TB B+*BB+*BB+*BB+*B @B+TB B+TB B+*BB+*BB+*BB+* @BB+TB B+TB B+*BB+*BB+*BB+*B @@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @>+T> >+T> >+*>>+*>>+*>>+*> @A+TA A+TA A+*AA+*AA+*AA+*A @@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @B+TB B+TB B+*BB+*BB+*BB+*B @B+TB B+TB B+*BB+*BB+*BB+* @BB+TB B+TB B+*BB+*BB+*BB+*B @@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @A+TA A+TA A+*AA+*AA+*AA+* @AA+TA A+TA A+*AA+*AA+*AA+*A @A+TA A+TA A+*AA+*AA+*AA+*A @A+TA A+TA A+*AA+*AA+*AA+*A @>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+* @>@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+*@ @B+TB B+TB B+*BB+*BB+*BB+*B @B+TB B+TB B+*BB+*BB+*BB+*B @B+TB B+TB B+*BB+*BB+*BB+* @BB+TB B+TB B+*BB+*BB+*BB+*B @@+T@ @+T@ @+*@@+*@@+*@@+*@ @@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+*@ @A+TA A+TA A+*AA+*AA+*AA+*A @A+TA A+TA A+*AA+*AA+*AA+* @AA+TA A+TA A+*AA+*AA+*AA+*A @A+TA A+TA A+*AA+*AA+*AA+* @A@+T@ @+T@ @+*@@+*@@+*@@+* @@@+T@ @+T@ @+*@@+*@@+*@@+* @@>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+*> @>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+* @>>+T> >+T> >+*>>+*>>+*>>+*>/MTrk JAZ2` @E+TE E+TE E+*EE+*EE+*EE+* @EC+TC C+TC C+*CC+*CC+*CC+* @CE+TE E+TE E+*EE+*EE+*EE+*E @D+TD D+TD D+*DD+*DD+*DD+*D @E+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @C+TC C+TC C+*CC+*CC+*CC+*C @C+TC C+TC C+*CC+*CC+*CC+* @CD+TD D+TD D+*DD+*DD+*DD+*D @D+TD D+TD D+*DD+*DD+*DD+*D @D+TD D+TD D+*DD+*DD+*DD+*D @D+TD D+TD D+*DD+*DD+*DD+*D @E+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+*E @C+TC C+TC C+*CC+*CC+*CC+*C @C+TC C+TC C+*CC+*CC+*CC+*C @C+TC C+TC C+*CC+*CC+*CC+*C @C+TC C+TC C+*CC+*CC+*CC+* @CE+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @C+TC C+TC C+*CC+*CC+*CC+*C @C+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+*C @C+TC C+TC C+*CC+*CC+*CC+* @CE+TE E+TE E+*EE+*EE+*EE+*E @C+TC C+TC C+*CC+*CC+*CC+*C @E+TE E+TE E+*EE+*EE+*EE+*E @D+TD D+TD D+*DD+*DD+*DD+* @DE+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @C+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+*C @D+TD D+TD D+*DD+*DD+*DD+*D @D+TD D+TD D+*DD+*DD+*DD+*D @D+TD D+TD D+*DD+*DD+*DD+* @DD+TD D+TD D+*DD+*DD+*DD+*D @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+*E @C+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+* @CE+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @C+TC C+TC C+*CC+*CC+*CC+*C @C+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+*C @E+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EE+TE E+TE E+*EE+*EE+*EE+*E @E+TE E+TE E+*EE+*EE+*EE+* @EC+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+*C @C+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+* @CC+TC C+TC C+*CC+*CC+*CC+*C/MTrk JAZ2` @H+TH H+TH H+*HH+*HH+*HH+* @HG+TG G+TG G+*GG+*GG+*GG+* @GH+TH H+TH H+*HH+*HH+*HH+*H @G+TG G+TG G+*GG+*GG+*GG+*G @H+TH H+TH H+*HH+*HH+*HH+*H @J+TJ J+TJ J+*JJ+*JJ+*JJ+* @JJ+TJ J+TJ J+*JJ+*JJ+*JJ+* @JJ+TJ J+TJ J+*JJ+*JJ+*JJ+*J @H+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+* @HG+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+*G @H+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+*H @G+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+* @GI+TI I+TI I+*II+*II+*II+*I @I+TI I+TI I+*II+*II+*II+* @II+TI I+TI I+*II+*II+*II+*I @I+TI I+TI I+*II+*II+*II+* @IJ+TJ J+TJ J+*JJ+*JJ+*JJ+* @JJ+TJ J+TJ J+*JJ+*JJ+*JJ+*J @J+TJ J+TJ J+*JJ+*JJ+*JJ+* @JJ+TJ J+TJ J+*JJ+*JJ+*JJ+*J @H+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+*H @G+TG G+TG G+*GG+*GG+*GG+*G @H+TH H+TH H+*HH+*HH+*HH+*H @G+TG G+TG G+*GG+*GG+*GG+* @GH+TH H+TH H+*HH+*HH+*HH+*H @J+TJ J+TJ J+*JJ+*JJ+*JJ+*J @J+TJ J+TJ J+*JJ+*JJ+*JJ+* @JJ+TJ J+TJ J+*JJ+*JJ+*JJ+*J @H+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+*H @G+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+* @GG+TG G+TG G+*GG+*GG+*GG+*G @H+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+*H @G+TG G+TG G+*GG+*GG+*GG+* @GG+TG G+TG G+*GG+*GG+*GG+* @GG+TG G+TG G+*GG+*GG+*GG+* @GG+TG G+TG G+*GG+*GG+*GG+* @GI+TI I+TI I+*II+*II+*II+* @II+TI I+TI I+*II+*II+*II+* @II+TI I+TI I+*II+*II+*II+*I @I+TI I+TI I+*II+*II+*II+*I @J+TJ J+TJ J+*JJ+*JJ+*JJ+*J @J+TJ J+TJ J+*JJ+*JJ+*JJ+*J @J+TJ J+TJ J+*JJ+*JJ+*JJ+* @JJ+TJ J+TJ J+*JJ+*JJ+*JJ+*J @H+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+*H @H+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+* @HH+TH H+TH H+*HH+*HH+*HH+* @HG+TG G+TG G+*GG+*GG+*GG+* @GG+TG G+TG G+*GG+*GG+*GG+* @GG+TG G+TG G+*GG+*GG+*GG+*G @G+TG G+TG G+*GG+*GG+*GG+* @GG+TG G+TG G+*GG+*GG+*GG+* @GG+TG G+TG G+*GG+*GG+*GG+*G/MTrk 00JAZ2` @Q~QS*ST(0 @TX~XV*VV(0 @VM~ML*LM*ML*LM*MO*0 @OP~PQ*QS( @0SL?L MMO?O MML(L @0N?N OOQ?Q OON( @0NJ*JB*BC*CE*EJ*JB*BC*CE*0 @EJ*JI*IJ*JI*IJ(0 @JLLJJH~HLLJJH~H @0L~LJ*JH(H0 @STS WTW XTX W*WS*0S @XP @X0S(SS~SL* @LN*NL|L$L* @LM|M$L*LM*M @O(OQTQ OT @O0 HTH HHFFEEFFH*HM*MLLJJHHFF @0HTH HHFFEECCA(0 @AJTJ JTJ J*JH*HG*GE*0 @EGTG GTG G*GE*EC*CB* @0BOONNLLJJG| @G0$GTG GTG E*EC~0C @L*LQ*QP*PQ*QS*SQ*QP*PN* @0NLPL @0LLIIE(ED*DE*EG*G @I~IG*GE( @EJ(JJ*JITI J* @JBPB @0J*JV*VU*US*SQ*QS*SQ*QO*O @N*NO*ON*NL*LJ(J @HTH @*@A*AC( @CETE C*CA*A@( @@0H*H@*@A*AC*CH*H@*@A*AC*C @0H*HG*GH*HG*GH(0 @HL*LOTO QTQ O*OQTQ0 @ Q*QOTO O(OO*O0 @M~ML*LM*ML*LM*MO*O0 @P~PQ*QS(S @0L?L MMO?O MML(0 @LN?N OOQ?Q OON(0 @NJ*JB*BC*CE*EJ*JB*BC*CE* @E0J*JI*IJ*JI*IJ( @0JO*OH*HO*OH*HO*OH*HO*OH*0H @L*LM*ML*LJ*JH(H0 @PTP STS X(X0 @WTW UTU S( @SL(LL*LK*KI*ID*D @G*GE*ED*DB*B@( @@M(MM*ML*LJ*JE* @EH*HF*FE*EC*CA( @0AMTM L*LJ*JH*HJ*JH*HJ*J0 @MTM LTL M(0M @O*OQQSSOTO O*OQQSSOTO0 @ V~VT*TSSTTSSQQOT @O0 O(OO*OQ*QS*ST*T0 @V~VV*VV( @0VITI L*LI*II(I @0QTQ P*PN*NL( @LQP @Q0Q*QP*PQ*QS*SUTU QTQ @ J*JL*LN|N @$N*NJ*JJ*JJ*JJ*JI*IJTJ @ Q~QJ*JL*LN*NJTJ @ Q~QJ*JL*LN*NQT @Q H|H$G*GH* @HJ(JLTL JTJ @0 L*LH*HL*LH*HL*LH*HL*LH*0 @HO*OL*LO*OL*LO*OL*LO*OL* @0LM(MHTH JT @0J EP0 @E0Q(QOTO MT @0M T(TRTR QT @0Q HHGGEECC@|0@ @$@T@ @T@ >*><~0< @JTJ CTC GTG CTC @0 JTJ C| @0C @0t0 @0 @/MTrk JAZ2` @-P*--P*-4P*44P*49P*99P*94P*44P* @ 47P*77P*7>P*>>P*>CP*CCP*C>P*>>P* > @)P*))P*)0P*00P*05P*55P*50P*00P* 0 @4P*44P*4;P*;;P*;@P*@@P*@;P*;;P* ; @-P*--P*-4P*44P*49P*99P*94P*44P*4 @ 2P*22P*29P*99P*9>P*>>P*>9P*99P*9 @&P*&&P*&-P*--P*-2P*22P*2-P*--P* @ -&P*&&P*&-P*--P*-2P*22P*2-P*--P* @ -$P*$$P*$+P*++P*+0P*00P*0+P*++P* @+ $P*$$P*$+P*++P*+0P*00P*0+P*++P* @+ (P*((P*(/P*//P*/4P*44P*4/P*//P* @/ (P*((P*(/P*//P*/4P*44P*4/P*//P* / @(P*((P*(/P*//P*/4P*44P*4/P*//P* @/(P*((P*(/P*//P*/4P*44P*4/P*//P* / @)P*))P*)0P*00P*05P*55P*50P*00P* @0)P*))P*)0P*00P*05P*55P*50P*00P* 0 @)P*))P*)0P*00P*05P*55P*50P*00P*0 @)P*))P*)0P*00P*05P*55P*50P*00P* @0+P*++P*+2P*22P*27P*77P*72P*22P*2 @+P*++P*+2P*22P*27P*77P*72P*22P* @ 2+P*++P*+2P*22P*27P*77P*72P*22P*2 @+P*++P*+2P*22P*27P*77P*72P*22P* @2 -P*--P*-4P*44P*49P*99P*94P*44P*4 @-P*--P*-4P*44P*49P*99P*94P*44P* @ 4-P*--P*-4P*44P*49P*99P*94P*44P* @ 4-P*--P*-4P*44P*49P*99P*94P*44P* @4&P*&&P*&-P*--P*-2P*22P*2-P*--P* - @&P*&&P*&-P*--P*-2P*22P*2-P*--P* - @&P*&&P*&-P*--P*-2P*22P*2-P*--P* @-&P*&&P*&-P*--P*-2P*22P*2-P*--P* - @$P*$$P*$+P*++P*+0P*00P*0+P*++P*+ @ $P*$$P*$+P*++P*+0P*00P*0+P*++P*+ @ $P*$$P*$+P*++P*+0P*00P*0+P*++P* @ +$P*$$P*$+P*++P*+0P*00P*0+P*++P* @+-P*--P*-4P*44P*49P*99P*94P*44P* @47P*77P*7>P*>>P*>CP*CCP*C>P*>>P*> @ )P*))P*)0P*00P*05P*55P*50P*00P*0 @ 4P*44P*4;P*;;P*;@P*@@P*@;P*;;P* @; -P*--P*-4P*44P*49P*99P*94P*44P*4 @2P*22P*29P*99P*9>P*>>P*>9P*99P*9 @ &P*&&P*&-P*--P*-2P*22P*2-P*--P* - @&P*&&P*&-P*--P*-2P*22P*2-P*--P* - @$P*$$P*$+P*++P*+0P*00P*0+P*++P*+ @ $P*$$P*$+P*++P*+0P*00P*0+P*++P* + @(P*((P*(/P*//P*/4P*44P*4/P*//P* @ /(P*((P*(/P*//P*/4P*44P*4/P*//P* @ /(P*((P*(/P*//P*/4P*44P*4/P*//P*/ @(P*((P*(/P*//P*/4P*44P*4/P*//P* @/ )P*))P*)0P*00P*05P*55P*50P*00P* @ 0)P*))P*)0P*00P*05P*55P*50P*00P* @ 0)P*))P*)0P*00P*05P*55P*50P*00P* @ 0)P*))P*)0P*00P*05P*55P*50P*00P* @ 0+P*++P*+2P*22P*27P*77P*72P*22P*2 @ +P*++P*+2P*22P*27P*77P*72P*22P* @2 +P*++P*+2P*22P*27P*77P*72P*22P* @ 2+P*++P*+2P*22P*27P*77P*72P*22P*2 @ -P*--P*-4P*44P*49P*99P*94P*44P*4 @ -P*--P*-4P*44P*49P*99P*94P*44P* 4 @-P*--P*-4P*44P*49P*99P*94P*44P*4 @-P*--P*-4P*44P*49P*99P*94P*44P* @4&P*&&P*&-P*--P*-2P*22P*2-P*--P* @ -&P*&&P*&-P*--P*-2P*22P*2-P*--P*- @&P*&&P*&-P*--P*-2P*22P*2-P*--P* @- &P*&&P*&-P*--P*-2P*22P*2-P*--P* - @$P*$$P*$+P*++P*+0P*00P*0+P*++P*+ @ $P*$$P*$+P*++P*+0P*00P*0+P*++P* @ +$P*$$P*$+P*++P*+0P*00P*0+P*++P* @ +$P*$$P*$+P*++P*+0P*00P*0+P*++P* @+)P*))P*)0P*00P*05P*55P*50P*00P* @0 )P*))P*)0P*00P*05P*55P*50P*00P* 0 @)P*))P*)0P*00P*05P*55P*50P*00P* @0 )P*))P*)0P*00P*05P*55P*50P*00P* @0$P*$$P*$+P*++P*+0P*00P*0+P*++P*+ @$P*$$P*$+P*++P*+0P*00P*0+P*++P* @+ +P*++P*+2P*22P*27P*77P*72P*22P* 2 @+P*++P*+2P*22P*27P*77P*72P*22P*2 @+P*++P*+2P*22P*27P*77P*72P*22P* @2+P*++P*+2P*22P*27P*77P*72P*22P* 2 @+P*++P*+2P*22P*27P*77P*72P*22P* @2+P*++P*+2P*22P*27P*77P*72P*22P*2/MTrk#mJAZ2`$F1F9F*F @&F09&**F$10$F&F**F0*F*$&0*&F*F$F0&*$*F0*F$F&F*0$&*F*0&F*F9F1F @$F*01*9&*F$0&F*$F*F0&*$*F0$F*&F*F0$*F&*0*$F&F*F0&*$*F0$F9F @**F&F1F0$&91**F0*F&F*$F0*F&$*0&F*F*$F0&*F*$0*$F*F&F0*&*F$0*1F @&F$F9F*F01$**F&90*F$F&F*0&**F$0*F&F$F*0*F*$&0*F&F*$F0*$*F&0*F1F&F @*$F9F0$&*19*F0*F*&F$F0$*&*F0*F$F*&F0*F&*$0$F*F*&F0&$**F09F&F*F1F*$F @01$9*&*F0$F*F*&F0&*F$*0*F&F*$F0*$*F&0*&F$F*F0$*&*F0*F$F1F9F @*&F0$&9**F10&F$F*F*0*&*F$0$F&F**F0*&*F$0*F&F*$F0&$**F09F @$F*F1F&F*0&1$*9*F0$F*F*&F0&**F$0*F$F*&F0$*F*&0*F$F*&F0&$**F0*F*1F @9F&F$F0$1*9&*F0*F&F*$F0*$*F&0&F$F*F*0&$*F*0*F$F*&F0$&*F*0*&F9F @*F$F1F0*$19&*F0*F&F*$F0*F*&$0&F$F**F0&$**F0*F&F*$F0&*F$*0*9F*F1F @&F$F01*&$*F90$F*F*&F0$&**F0**F&F$F0*&*F$0&F**F$F0$**F&0*$F1F9F*F @&F091&*F*$0*$F&F*F0*$&*F0*F$F*&F0*$&*F0*F*$F&F0$*F&*0*9F$F1F&F*F @091$*&*F0&F*F*$F0**F&$0*F$F*&F0*F*&$0*&F*F$F0$**F&0*&F*F @1F$F9F0*&$91*F0$F*F*&F0$*F&*0*$F&F*F0&**F$0&F$F*F*0*F*&$01F&F @$F*F9F*0*F1$*&90*F&F$F*0$&**F0$F&F*F*0$*&*F0*F$F*&F0$*&*F09F$F* @1F*F&F0$&*1*F90*F&F$F*0$&**F0**F$F&F0&*$*F0$F*F&F*0*$&*F0$F9F @1F*F*&F0&*F1$9*0*&F$F*F0*F*&$0&F*$F*F0$*&*F0$F*F*&F0*&$*F0 @$F&F*9F*F1F0$*F*1&90*&F$F*F0*$&*F0*&F*F$F0**F$&0*F$F*&F0&**F$0 @*$F*F9F&F1F0*$1&9*F0**F&F$F0*$&*F0$F*&F*F0*F$&*0*F*&F$F0$**F&0*F*&F @9F$F1F0*&1$9*F0*$F*F&F0&$**F0*F*&F$F0*$*F&0*$F*F&F0*F*$&0*$F @9F&F*F1F0*1$&9*F0*F*&F$F0$**F&0&F$F**F0*&$*F0*F$F&F*0&*$*F0&F$F*F*9F1F @0*1&$9*F0&F**F$F0&$**F0&F$F**F0*F*$&0$F*&F*F0$*F&*0$F&F1F**F @9F0$1**F&90$F*&F*F0**F&$0**F&F$F0*F*$&0*F*&F$F0$*F*&0*9F&F*F$F1F @0*F1&*9$0**F$F&F0*F$*&0*&F*F$F0$*&*F0*$F&F*F0$&*F*01F*&F9F*F$F @091*F&$*0*F$F*&F0*&$*F0**F$F&F0**F&$0*F&F$F*0&$**F01F&F9F @*F$F*0&19$**F0*&F*F$F0*F$*&0**F$F&F0&$**F0$F&F*F*0*F*&$01F* @*F&F$F9F0$*F9*&10**F$F&F0&$**F0*F$F*&F0&**F$0*&F$F*F0*F&$*0 @9F$F*F&F1F*09**F1$&0&F*F*$F0*F&*$0*$F*F&F0*F$&*0*&F$F*F0*$&*F0*$F*F @9F1F&F01&*$9*F0&F*F$F*0&$*F*0**F&F$F0&*$*F0*$F*F&F0$**F&0$F* @&F9F*F1F09&$*F*10*F$F*&F0$*&*F0$F**F&F0*$&*F0&F*F*$F0**F$&0 @&F*F9F$F1F*01&$9**F0*F$F*&F0$&**F0&F$F*F*0$*&*F0$F*F&F*0*F*&$01F*F* @&F$F9F0$&*91*F0*&F$F*F0*&*F$0$F**F&F0$&**F0*F&F$F*0*F&*$0$F9F&F @**F1F0$*F&9*10&F$F*F*0$*&*F0&F*$F*F0&$**F0&F$F*F*0*F$&*09F @$F*1F*F&F01*&*F9$0**F&F$F0*$&*F0*$F*F&F0&$**F0$F&F*F*0$*&*F0*9F$F1F @*F&F09$1&**F0*&F$F*F0*&*F$0&F*F$F*0&$**F0$F&F*F*0&$*F*09F$F1F*F&F @*01$*F*9&0&F**F$F0*&$*F0*$F*F&F0&*$*F0&F$F*F*0&$**F0 @1F9F*F*&F$F0*F9*$&10*&F$F*F0&*$*F0$F*F&F*0&*$*F0$F&F*F*0&*$*F0 @1F&F$F*9F*F0*F*&$910*&F*F$F0&$**F0$F*F&F*0&*$*F0*F$F&F*0$&**F0$F1F*F @*9F&F0$*F1&*90*$F*F&F0*$*F&0*&F*F$F0$*&*F0$F&F*F*0$*&*F0*&F1F9F*F$F @0*F*91$&0*&F*F$F0&$**F0&F$F*F*0*$&*F0*F&F$F*0&$**F0*F$F1F9F* @&F0*F&$19*0**F&F$F0*$&*F0$F*F&F*0&*$*F0&F$F*F*0$*&*F0*F&F1F$F* @9F0$*F1&*90*$F*F&F0*F&$*0*$F*F&F0$*&*F0$F*F&F*0*F$*&0*&F @$F*F1F9F0*F*$&190*&F$F*F0&*$*F0&F$F*F*0*$&*F0$F*F&F*0*&$*F0 @9F*F$F*1F&F0*$&1*F90**F$F&F0$*&*F0**F&F$F0$*F*&0*&F$F*F0&*F$*0&F1F*9F$F @*F0&1*F$9*0*F&F*$F0*F&*$0&F*F$F*0**F$&0**F&F$F0&$**F0*1F&F*F9F$F @0*&19*F$0&F*F*$F0*&*F$0*&F*F$F0*F&*$0*&F$F*F0$*&*F01F$F9F&F*F* @0*91$&*F0$F&F*F*0*F*&$0*$F*F&F0&*$*F0*F*&F$F0*$&*F0*9F1F @*F$F&F01$&*9*F0$F**F&F0$&*F*0&F*F$F*0**F$&0*&F*F$F0&$**F0*1F$F*F @9F&F091*F&*$0*$F&F*F0**F$&0$F&F*F*0&**F$0*F*&F$F0&$**F0 @9F&F$F1F*F*0*9$1&*F0**F$F&F0*F*$&0**F$F&F0$&*F*0&F$F*F*0&$*F*0$F&F*F @*1F9F0*&1$9*F0**F&F$F0*F$&*0*&F*F$F0&*$*F0&F**F$F0*$&*F0*1F*F @&F9F$F01*F$*&90*$F&F*F0&*$*F0$F**F&F0&$*F*0*$F*F&F0*F*$&0*F$F @*&F1F9F0&*1$*F90**F$F&F0$*&*F0**F&F$F0$*F*&0*&F$F*F0&*F$*01F&F*$F @9F*F01&*F9$*0$F*F*&F0*F*$&0*$F*F&F0*&$*F0$F*F&F*0$&*F*09F*F&F* @$F1F0&*9*F$10$F&F*F*0&$**F0**F&F$F0$&**F0*F*$F&F0*F*$&0*F1F9F&F @$F*0*F*91&$0*F$F&F*0*$&*F0*F*&F$F0*$&*F0&F*F$F*0*F$*&09F @1F*F$F&F*0$19&**F0*$F*F&F0*F&*$0*&F$F*F0$**F&0*F$F&F*0*$&*F0$F1F* @*F9F&F09*&1$*F0**F&F$F0&*F$*0*$F*F&F0$&**F0*$F*F&F0$*&*F0 @1F*9F$F*F&F0*&9*F$10*&F$F*F0&$*F*0**F&F$F0$*&*F0*&F$F*F0$&**F01F9F @**F&F$F09&*1$*F0&F$F**F0$*&*F0*&F*F$F0*&*F$0$F&F*F*0*&$*F01F*$F&F9F @*F0&9*$*F10$F*&F*F0$&*F*0&F$F**F0*F&$*0$F*F*&F0&*$*F0*1F*F$F9F&F @09&*1*F$0*&F*F$F0*&$*F0&F*F*$F0&$*F*0$F&F**F0*&*F$0&F$F9F* @1F*F01&*$9*F0*F*&F$F0*F$&*0$F&F**F0*F&$*0$F&F**F0**F&$01F*F @*$F9F&F0*&1$9*F0*&F*F$F0*F$*&0$F*&F*F0*F$*&0*$F&F*F0$*&*F0*F @9F$F*&F1F0*F91$*&0*F&F$F*0$*&*F0&F*F*$F0&**F$0**F&F$F0*$&*F01F @9F*F$F*&F09*F*1&$0**F$F&F0*&$*F0**F&F$F0*F$&*0*F*$F&F0*F$*&0&F**F$F @9F1F09&*$1*F0*F$F&F*0*F$*&0&F**F$F0*F$*&0*F$F&F*0*F*&$0$F9F1F**F&F @0&1$9**F0**F$F&F0$**F&0&F*F*$F0*F*$&0&F*F$F*0$*&*F0&F9F*1F @*F$F0*F1$*&90*$F&F*F0$&*F*0&F*F$F*0&*$*F0$F*F*&F0*&$*F0&F*F$F*9F1F @091$**F&0*F$F&F*0*$&*F0*$F&F*F0$*&*F0*&F$F*F0*&$*F09F*F @$F&F1F*0*F1&9*$0*$F&F*F0&*F*$0*F&F*$F0$*F&*0&F**F$F0*&$*F0 @*F9F*$F1F&F0&**F19$0$F&F*F*0$&*F*0&F*F$F*0*$*F&0$F*&F*F0*&$*F0&F*1F9F$F @*F09$1&**F0*F$F&F*0*$*F&0&F*$F*F0&*F$*0$F*F&F*0&*$*F0&F @*F1F$F9F*0*F9*1$&0*&F*F$F0*&$*F0*$F*F&F0*$&*F0&F$F**F0$**F&0* @$F9F1F&F*F01&*F*9$0$F*F*&F0&*F*$0&F*F*$F0$*F&*0*&F*F$F0$*&*F0&F*F$F1F @*9F0*F9&*$10*&F$F*F0$*F&*0$F*&F*F0*$&*F0$F&F**F0*$&*F0*1F$F&F*F @9F0**F91&$0**F$F&F0&*F$*0&F*F$F*0*F&$*0**F&F$F0&*$*F0 @1F**F9F&F$F01$&*F*90**F$F&F0*&$*F0&F**F$F0&*$*F0&F*F$F*0*&*F$01F&F*9F @$F*F0*F91$*&0&F**F$F0&$**F0*F$F&F*0&$*F*0$F*F&F*0*$&*F0&F#F @*&&F#*FH*&#F&F&HF#H#F&FHHF#*F&&FHF&*H0.F#F9F5F&FH`5&#9./MTrk:JAZ2A busy day at the dpot/simutrans-124.5/simutrans/music/07-Transport-chaos.mid000066400000000000000000000455161520675460500227520ustar00rootroot00000000000000MThd`MTrk(c) shunter 2006~ JAZ2C 7C 7@F 7@C 7C 7F 7C 7C 7F 7C 7@G 7@G 7@F 7`F 7`C 7@G 7@G 7@F 7`F 7`C 7G 7G 7F 7F 7F 7C 7@C 7@C 7@F 7@C 7@C 7@C 7@F 7@C 7@G 7@C 7C 7@G 7@C 7C 7@G 7@C 7C 7@G 7@C 7C 7`C 7@F 7`C 7`C 7@F 7`C 7`C 7@F 7`C 7F 7X`Q G 7C 7C 7@C 7G 7@C 7@C 7G 7@C 7`G 7`G 7`C 7`C 7`G 7`G 7`C 7`C 7`G 7`G 7`C 7`C 7G 7F 7G 7C 7G 7F 7G 7C 7G 7F 7G 7C 7G 7F 7G 7C 7`G 7@G 7`C 7`G 7@G 7`C 7@C 7`F 7`C 7@C 7`F 7`C 7`F 7`F 7`G 7`C 7`F 7`F 7`G 7`C 7@C 7C 7@C 7C 7@C 7C 7/MTrk ~ JAZ2@H@=HC@ CD@ DE@ EF@ F@@ @A@ AB@ BC@ C?@ ?>@ >=@ =<@ <F@=FE@ EH@=H#C@=CF@FC@C<@=<C@=C<@=<CC@=CF@FC@C<@=<C@=C<@=<C@=C<@=<F@=F<@=<D@ DD@ DD@ DD@ D@@ @?@ ?=@ =<@ <C@=C<@<F@=FC@C<@<?@?<@=<A@ AA@=AE@E<@=<?@=?A@=A#E@EC@=CF@FC@C<@=<C@=C<@=<CC@=CF@FC@C<@=<C@=C<@=<C@=C<@=<F@=F<@=<D@ DD@ DD@ DD@ D@@ @?@ ?=@ =<@ <C@=C<@<F@=FC@C<@<?@?<@=<A@ AA@=AE@E<@=<?@=?A@=A#E@EcC@=CF@FcC@=CJ@J>@=>C@=CJ@=JH@H?@=?<@=<A@AcC@=CF@FcC@=CJ@J>@=>C@=CJ@=JH@H?@=?<@=<A@AF@=FH@=HC@=C>@=>C@=CJ@=JC@CJ@ JC@ CJ@=JC@=C>@=>C@=CJ@ JC@ CA@=AE@=EH@=HE@=EK@KH@=H?@?A@=AE@=EH@=HE@=EK@KH@=H?@=?E@=EA@=AH@=HE@ EH@ HK@K?@=?A@=A?@=?A@=A<@=<C@=CH@ HT@ TR@RC<@=<C@=CH@=HT@=TO@=OF@=F<@=<C@=CH@ HT@ TR@RC<@=<C@=CH@=HT@=TO@=OF@=F<@=<C@=CH@ HT@ TR@RC<@=<C@=CH@=HT@=TO@=OF@=F<@=<C@=CH@ HT@ TR@RC<@=<C@=CH@=HT@=TO@=OF@=FC?@=?A@A?@=?A@=A?@=?E@E<@=<?@=?A@A?@=?A@=A?@=?E@E>@ >7@ 7>@=>sK@X`]<JKC@I@[<H@ @G@=C>@ >C@ C>@=>C@=C>@=>>@]><@]<C0=CF@=FO@=OT@=TJ@=J>@=>C@=CF@=FO@=OT@=TJ@=J>@=>C@=Cc>@=>C@ C>@ >7@=7C@C<@=<C@Cc>@=>C@ C>@ >7@=7C@C<@=<C@Cc>@=>C@ C>@ >7@=7C@C<@=<C@C>@=>C@=C>@=>C@=C<@<9@ 9?@ ?<@=<B@ BA@ A@@ @?@ ?@@ @?@ ?=@ =<@ <?@=?E@ ED@ DB@ BA@ A7@=7>@=>7@=7C@=C>@>C@=C>@>>@=>C@=C>@=>C@=C<@<9@ 9?@ ?<@=<B@ BA@ A@@ @?@ ?@@ @?@ ?=@ =<@ <?@=?E@ ED@ DB@ BA@ A7@=7>@=>7@=7C@=C>@>C@=C>@>>@=>C@=C>@=>C@=C<@<9@ 9?@ ?<@=<B@ BA@ A@@ @?@ ?@@ @?@ ?=@ =<@ <?@=?E@ ED@ DB@ BA@ A7@=7>@=>7@=7C@=C>@>C@=C>@>>@=>C@=C>@=>C@=C<@<9@ 9?@ ?<@=<B@ BA@ A@@ @?@ ?@@ @?@ ?=@ =<@ <?@=?E@ ED@ DB@ BA@ A7@=7>@=>7@=7C@=C>@>C@=C>@><@=<C@C7@=7c<@=<C@C7@=7#<@=<C@=C<@=<:@:?@=?<@<C<@=<C@=C<@=<:@:?@=?<@<<@=<H@Hc<@=<H@ HK@ KC@=C#<@=<H@Hc<@=<H@ HK@ KC@=CCO@=OT@=TR@ RT@ T#O@=OT@=TR@ RT@ T#O@=OT@=TR@ RT@ TT@]T[0=[/MTrk-JAZ2@C@=CC?@=?A@ A?@=?#<@=<:@:<@<3@=3<@=<3@=3C<@=<<@<7@73@=3<@=<3@=3<@=<3@=3C@=C3@=3<@=<3@3C@=C?@?5@53@35@=5<@ <5@=5<@<5@=53@=3<@=<#<@<<@=<:@:<@<3@=3<@=<3@=3C<@=<<@<7@73@=3<@=<3@=3<@=<3@=3C@=C3@=3<@=<3@3C@=C?@?5@53@35@=5<@ <5@=5<@<5@=53@=3<@=<#<@<c<@=<:@:c>@=>C@C7@=7?@=?C@=CA@A9@=95@=5<@<c<@=<:@:c>@=>C@C7@=7?@=?C@=CA@A9@=95@=5<@<?@=??@=?7@=77@=7?@=?C@=C7@7A@ A?@ ?C@=C7@=77@=7?@=?C@ C7@ 7<@=<9@=9A@=A<@=<A@A?@=?9@95@=5?@=?A@=A<@=<A@A<@=<9@=9?@=?<@=<A@=A<@ <E@ EE@E9@=95@=59@=95@=57@=7<@=<F@ FK@ KO@OC3@=3<@=<F@=FK@=KH@=H<@=<7@=7<@=<F@ FK@ KO@OC3@=3<@=<F@=FK@=KH@=H<@=<7@=7<@=<F@ FK@ KO@OC3@=3<@=<F@=FK@=KH@=H<@=<7@=7<@=<F@ FK@ KO@OC3@=3<@=<F@=FK@=KH@=H<@=<C9@=95@59@=95@=59@=9?@?5@=53@=3<@<5@=55@=55@=5<@<[<7@sG@JK]<I@K@H@ 7+@ +7@=7A@=A3@ 37@ 77@=7>@=>7@=700=0C?@=?C@=CO@=OC@=C7@=7?@=??@=?C@=CO@=OC@=C7@=7?@=?c7@=7?@ ?7@ 7+@=+>@>0@=0?@?c7@=7?@ ?7@ 7+@=+>@>0@=0?@?c7@=7?@ ?7@ 7+@=+>@>0@=0?@?7@=7?@=?7@=7?@=?5@53@ 39@ 95@=5<@=<C+@=+7@=7+@=+>@=>7@77@=77@77@=7?@=?7@=7?@=?5@53@ 39@ 95@=5<@=<C+@=+7@=7+@=+>@=>7@77@=77@77@=7?@=?7@=7?@=?5@53@ 39@ 95@=5<@=<C+@=+7@=7+@=+>@=>7@77@=77@77@=7?@=?7@=7?@=?5@53@ 39@ 95@=5<@=<C+@=+7@=7+@=+>@=>7@77@=77@77@=7<@<+@=+c7@=7<@<+@=+#7@=7<@=<3@=33@3<@=<5@5C7@=7<@=<3@=33@3<@=<5@57@=7C@Cc5@=5A@ AA@ A7@=7#7@=7C@Cc5@=5A@ AA@ A7@=7CH@=HO@=OK@ KH@ H#H@=HO@=OK@ KH@ H#H@=HO@=OK@ KH@ H00=0/MTrkcJAZ2`C@=CF@FH@=HH@=HC@=C<@=<C@=CF@=FH@=HH@=HC@=C<@=<C@=CF@=FH@=HH@=HA@=A<@=<A@=AE@=EH@=HH@=HA@=A<@=<A@=AE@=EH@=HcC@=CF@FH@=HH@=HC@=C<@=<C@=CF@=FH@=HH@=HC@=C<@=<C@=CF@=FH@=HH@=HA@=A<@=<A@=AE@=EH@=HH@=HA@=A<@=<A@=AE@=EH@=H:@ :9@ 98@ 87@ 7>@=>7@7F@ FE@ ED@ DC@ C>@>4@ 45@ 56@ 67@ 7-@ -/@ /1@ 12@ 27@=7>@=>7@=7C@=C@@ @?@ ?=@ =<@ <3@32@ 23@ 34@ 45@ 59@=9<@=<?@=?A@A<@=<3@=39@=9<@=<?@=?A@=A<@ <0@ 03@=35@=59@9<@=<?@=?A@=A9@=9?@=?9@=9?@=?C7@7:@=:C@C>@=>7@=7>@=><@=<7@=7C7@7:@=:C@C>@=>7@=7>@=><@=<7@=7C7@7:@=:C@C>@=>7@=7>@=><@=<7@=7C7@7:@=:C@C>@=>7@=7>@=><@=<7@=7CC@C<@=<C@=C<@=<0@=0cC@C<@=<C@=C<@=<0@=0cC@C<@=<C@=C<@=<0@=0c7@=7<@=<C@=CF@=FC@=C<@=<7@=7<@=<C@CH@ HH@=HA@=A#E@=EH@HH@=HA@=A<@=<A@=AE@=EH@=HJ@[<H@]<s>@I@K@G@=>7@=7>@=>C@=CJ@=JC@=CC@0@=0.@CH0.7@=70@=0.@H .7@=70@=07@=70@=0+@=+2@=2+@=+0@=0.@=.7@=70@=0.@=.7@=70@=07@=70@=0+@=+2@=2+@=+c2@2>@=>2@=2c2@2>@=>2@=2c2@2>@=>2@=20@=07@=7<@=<C@=CF@=FH@=H0@=0H@=HH@=H>@=>7@=7C@C>@>J@=JC@=C0@=07@=7<@=<C@=CF@=FH@=H0@=0H@=HH@=H>@=>7@=7C@C>@>J@=JC@=C0@=07@=7<@=<C@=CF@=FH@=H0@=0H@=HH@=H>@=>7@=7C@C>@>J@=JC@=C0@=07@=7<@=<C@=CF@=FH@=H0@=0H@=HH@=H>@=>7@=7C@C>@>J@=JC@=CC7@7+@=+7@=77@ 7>@ >C7@7+@=+7@=77@ 7>@ ><@=<0@=07@70@03@=3<@=<0@=07@70@03@=3c0@=09@9#0@=09@9/MTrk JAZ2$h>$$h$(h>(+h+-h-.h6..h.h>h$h>$$h$"h>""h")h>))h)9h>90h0$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$"h>""h"-h-.h6..h.h>h)h>))h)'h>''h'&h&'h6''h'2h>20h0)h>))h)-h>-0h0&h&'h6''h'2h>20h0$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+)h>))h)-h>-0h02h23h633h3$h>$$h$)h>))h)-h>-0h02h23h633h32h>20h0$h>$$h$(h>(+h++h>++h+/h>/2h2+h>++h+;h>;2h2)h>))h))h>))h)$h>$$h$4h>4+h++h>++h+)h>))h)+h>++h+)h>))h))h>))h))h>))h)$h>$$h$"h>""h"-h-.h6..h.-h>-+h++h>++h+)h>))h)'h'(h6((h(&h>&&h&+h>++h+/h>/2h2'h'(h6((h(4h>42h2)h>))h)9h>90h0%h%&h6&&h&2h>20h0)h>))h)-h>-0h0&h&'h6''h'2h>20h0)h>))h)'h>''h'2h23h633h3$h>$$h$$h>$$h$"h>""h"$h>$$h$(h>(+h+$h>$$h$4h>4+h+)h>))h)9h>90h0$h>$$h$(h>(+h+$h>$$h$(h>(+h+$h>$$h$(h>(+h+)h>))h)-h>-0h0$h>$$h$(h>(+h++h>++h+/h>/2h2$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h++h>++h+/h>/2h2$h>$$h$(h>(+h+-h-.h6..h.h>h$h>$$h$4h>4+h++h>++h+;h>;2h2$h>$$h$"h>""h" h !h6!!h!-h>-+h+$h>$$h$(h>(+h++h>++h+/h>/2h2$h>$$h$(h>(+h+-h-.h6..h.h>h$h>$$h$$h>$$h$(h>(+h+)h>))h)$h>$$h$$h>$$h$(h>(+h+)h>))h)$h>$$h$$h>$$h$4h>4+h+)h>))h)$h>$$h$(h>(+h+-h-.h6..h.h>h$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+)h>))h)-h>-0h02h23h633h3$h>$$h$)h>))h)'h>''h'2h23h633h32h>20h0K@J@[<G@H@]<UI@+h>++h+)h>))h)(h>((h('h>''h')h$h>$)$h'h'$4h&h>4&$h+h+$h>$$h$$"h>""h"-h-.h6..h.h>h+h>++h+/h>/2h2$h>$$h$"h>""h"$h>$$h$(h>(+h+-h-.h6..h.-h>-+h++h>++h+/h>/2h2$h>$$h$+h>++h++h>++h+$h>$$h$$h>$$h$+h>++h++h>++h+$h>$$h$$h>$$h$+h>++h++h>++h+$h>$$h$$h>$$h$"h>""h"-h-.h6..h.-h>-+h++h>++h+/h>/2h2'h'(h6((h(4h>42h2)h>))h)'h>''h'2h23h633h32h>20h0+h>++h+/h>/2h24h45h655h5&h>&&h&$h>$$h$"h>""h"-h-.h6..h.-h>-+h++h>++h+/h>/2h24h45h655h54h>42h2)h>))h)9h>90h0&h&'h6''h'2h>20h0+h>++h+)h>))h)4h45h655h5&h>&&h&$h>$$h$(h>(+h+-h-.h6..h.-h>-+h++h>++h+;h>;2h24h45h655h5&h>&&h&)h>))h)-h>-0h0%h%&h6&&h&2h>20h0+h>++h+/h>/2h24h45h655h54h>42h2$h>$$h$4h>4+h+ h !h6!!h!-h>-+h++h>++h+/h>/2h2(h()h6))h)&h>&&h&)h>))h)-h>-0h0%h%&h6&&h&2h>20h0+h>++h+)h>))h)4h45h655h54h>42h2$h>$$h$+h>++h+)h>))h)+h>++h+$h>$$h$+h>++h+/h>/2h2+h>++h+$h>$$h$4h>4+h+$h>$$h$)h>))h)$h>$$h$(h>(+h+$h>$$h$)h>))h)$h>$$h$)h>))h))h>))h)+h>++h+$h>$$h$)h>))h))h>))h)+h>++h+$h>$$h$(h>(+h+$h>$$h$(h>(+h+ h !h6!!h!-h>-+h+$h>$$h$(h>(+h+$h>$$h$"h>""h" h !h6!!h!-h>-+h+$h>$$h$4h>4+h+$h>$$h$"h>""h"!h>!!h! h>  h )h>)'h'&h>&$h|$/MTrkKJAZ2#Z*(*#*!**%*&P*(*&*"****(#Z*#* ****(&P&**#**$*#Z*(*#*&**#**(&P*&*$**&*#Z*(*#***"**(&P&****$**(#Z*#*$**#*&P*(&** **"**(#Z*#*$**%**(&P&**!**'**(#Z*#*%** *&P*(&**!**'*#Z*(#**$**%**(&P&****"**(#Z#**%**'*&P*(*&*%**!*#Z*(*#*&****(&P*&*&**!**(#Z*#* **%*&P*(&**$**"**(#Z#**'**#**(&P&** **$*#Z*(*#*$**&*&P*(*&*$****(#Z*#*"**!*&P*(&****'*#Z*(*#*%***&P*(*&*!**$*#Z*(#****&**(&P*&*#** **(#Z#**#**&*&P*(*&*!**!**(#Z#**'** **(&P*&*#**$*#Z*(#**"**%*&P*(*&***&**(#Z*#*&***&P*(&** **"*#Z*(#**&***&P*(&** **'**(#Z#**$**#**(&P*&*$**"*#Z*(#****!**(&P*&*%**'**(#Z*#*"**#*&P*(*&*!**#**(#Z*#*#**!**(&P&**!**!**(#Z#**$**&*&P*(&**#**'**(#Z#****$*&P*(*&*%**%**(#Z*#*%***&P*(*&***%**(#Z#****#**(&P&**"**&*#Z*(#****&**(&P&**'** *#Z*(#****!*&P*(*&* ****(#Z*#*!**"**(#Z*#***"*#Z*(#**"****(&P&****#**(#Z*#*!**#**(&P&**!**&**(#Z#**!** *&P*(&**'**%**(#Z*#*$**%**(#Z*#*%**%*#Z*(#** **!*&P*(*&***&**(#Z*#*&****(&P*&***&*#Z*(#**%**!*&P*(&**#**"**(#Z*#*!** *&P*(*&*"***#Z*(*#*'***&P*(&****%**(#Z*#*$***&P*(&*****#Z*(*#*$**%*&P*(*&*&** **(#Z*#* **$*&P*(*&*%**'**(#Z#**!**%*&P*(&**$**$*#Z*(*#***"*&P*(&****'**(#Z#**#**#**(&P&** ***#Z*(*#*"**"**(&P*&*"****(#Z*#*'****(&P*&*!****(#Z*#*%***&P*(&**#****(#Z#**%****(&P&**&**'*#Z*(*#*#**"*&P*(*&*#**%**(#Z#**"**#*&P*(&**!**#*#Z*(#**'** **(&P*&***&*#Z*(#******(&P&**&**&*#Z*(#****%*&P*(&**#***#Z*(#**'**"**(&P&**#**$*#Z*(#**%****(&P&** ****(#Z*#*%****(&P*&*%**#*#Z*(*#*****(&P&**** *#Z*(#**#***&P*(&****!**(#Z#** **&*&P*(*&*$**'**(#Z*#*"**$**(&P&**!**!**(#Z#**'**"*&P*(&**#***#Z*(*#*%**&**(&P*&*%**'*#Z*(*#*** **(&P&** **'**(#Z*#*"**$**(&P&****$**(#Z#** **&*&P*(&**'**$**(#Z#**#** *&P*(*&*'***#Z*(#****'*&P*(&**'****(#Z#****"*&P*(&**&**%**(#Z#**'***&P*(&**!***#Z*(#**!**"**(#Z#**&**$**(&P*&*&**%**(#Z#**** *#Z*(#**$**'**(#Z*#*#**%**(&P*&***"*#Z*(*#*$**&*#Z*(*#* **#*#Z*(*#* **'**(&P*&* **'**(#Z#**'**'**(#Z*#*'** *&P*(*&*%***#Z*(#**'**%*&P*(*&*"**#*#Z*(*#***'**(&P&**'**%*#Z*(#**&**!**(&P*&*"**!*#Z*(#**"**$**(&P*&***!*#Z*(*#*#***&P*(*&*&**%**(#Z#** **'*&P*(&****%**(#Z*#*%**%**(&P*&*'***d#Z]<*(J@H@K@I@[<G@#** **#**(&P*&*#**%**(#Z#*****&P*(&** ****(#Z#**$**&**(&P*&*"**&*#Z*(*#*!****(&P*&***$**(#Z#**#**&**(&P*&*&** *#Z*(*#* ** *&P*(&**'**%**(#Z#**$****(&P*&***!**(#Z#**"**"**(&P*&***'*#Z*(*#***'**(&P*&***!*#Z*(*#*'**#*&P*(&** ** **(#Z#**'****(#Z*#***!*#Z*(*#*"** *#Z*(#**%**!**(#Z#**'** *#Z*(#**&**$*#Z*(*#***!**(#Z*#*$****(#Z#**#**%*#Z*(#*****#Z*(*#*****(#Z*#***!**(#Z#** ***&P*(&****&*#Z*(*#* **"**(&P*&*&***#Z*(*#*$**"*&P*(&**'**$*#Z*(*#*#**$**(&P*&***#**(#Z#**!** **(&P*&*!**!**(#Z#**!** *&P*(&**$**$**(#Z#**** **(&P*&***&**(#Z#**%**#*&P*(&**$**#**(#Z#**'****(&P*&*!**'*#Z*(*#*$** **(&P*&*$**$**(#Z#**$**#**(&P*&*'**'*#Z*(*#*$***&P*(&**$**%**(#Z#**#**"*&P*(&**"****(#Z#**$**!*&P*(&****'*#Z*(*#*!**!*&P*(&**'** *#Z*(*#*'**!**(&P*&*!**!**(#Z#**%****(&P*&*%**"**(#Z#****$*&P*(&**'**&*#Z*(*#*%**'*&P*(&****#**(#Z#**!***&P*(&**$**%**(#Z#**!**&*&P*(&****!*#Z*(*#*!**!*&P*(&**&**$*#Z*(*#***"*&P*(&**$***#Z*(*#*'**'*&P*(&**%***#Z*(*#*"** *&P*(&**%**#*#Z*(*#*$**#*&P*(&**$***#Z*(*#*#**#*&P*(&****'*#Z*(*#*#**$**(&P*&***!**(#Z#****!**(&P*&*&**'**(#Z#**&****(&P*&***!**(#Z#**!**$**(&P*&***&**(#Z#**&**%**(&P*&*!****(#Z#**"** *#Z*(#**$**"*&P*(*&***!*#Z*(#****'*#Z*(*#*'**"*#Z*(#**!** *&P*(*&*****(#Z*#*'**"*#Z*(*#*** **(&P*&*%***#Z*(*#*&**'**(#Z*#*$***#Z*(*#*&**!**(&P*&*%**$**(#Z#****"**(#Z*#***"**(#Z#**%**#*#Z*(#****"*#Z*(*#*"**$**(#Z*#*&** **(#Z#****"**(#Z*#*'**!**(#Z#**#**&**(#Z*#*&**$**(#Z#**$****(&P*&***!**(#Z#**!****(&P*&***&**(#Z#**#***&P*(&**"**$*#Z*(*#*!**%*&P*(&****#**(#Z#**"**#**(&P*&***%**(#Z#**%**%*&P*(&** **"*#Z*(*#*#***&P*(&**&**'**(#Z*#* **"**(&P*&* ***#Z*(#**$**'**(&P&****"*/MTrk3JAZ2Transport chaos/simutrans-124.5/simutrans/music/08-The-journey-home.mid000066400000000000000000000502221520675460500230110ustar00rootroot00000000000000MThd`MTrk\shunter (c) 2006~ JAZ2 2006.10.06Q '/MTrk,shunter (c) 2006 JAZ2 @@General MIDI(My Song /MTrk)JAZ2d @General MIDIChord` kH-Hk<-C-Ck@-@k*HC@<@-<-H-C-*@<HC6<-@-H-C-*CH<@6C-<-@-H-*<HC@<-C-H-@-*C@H<<-H-@-C-*@ k@kHkHCCk<C-G->-;-*CG>;;-G-C->-*CG>;6G-;->-C-*>G;C6G-C-;->-*>GC;>-G-;-C-*>C;GG-;-C->-*>kGkG>C;Ck k<-C-H-@-*HC@<<-H-C-@-*CH@<6<-H-@-C-*H@<C6<-@-C-H-*HC<@<-@-C-H-*H@C<H-@-<-C-*Ck k@H<C@kHk;-G->-C-*>C;GC-G->-;-*GC;>6;->-G-C-*>;GC6G->-;-C-*;>GC>-C-G-;-*;C>G;-G-C->-*CG>;Ck k>kGk<-H-E-@-*<HE@H-E-@-<-*<@HE6E-H-<-@-*<@HE6@-E-H-<-*@<EHE-<-H-@-*E<H@E-@-<-H-*@kHkH k@E<EkC-;-@-G-*G@;C@-;-C-G-*@C;G6C-@-G-;-*@G;C6C-;-G-@-*C@;GG-C-@-;-*C;@GG-;-@-C-*Gk;CkC@G k@kE-H-A-<-*<HEAH-<-A-E-*EH<A6E-<-A-H-*HEA<6E-H-<-A-*<AEH<-E-H-A-*E<HA<-E-H-A-*<AEHEkAk kHk;-G-C->-*>GC;G-;-C->-*>G;C6;-G-C->-*CG>;6G->-C-;-*;C>G;-G-C->-*G>C;;-G-C->-*CkGC>; kGk>kH-C-@-<-*<H@C<-H-@-C-*<H@C6@-H-<-C-*C<@H6<-@-H-C-*@CH<C-H-@-<-*<C@HH-C-@-<-* kCk<HkH@C@k>-C-;-G-*>;GCG-C-;->-*;G>C6;-G-C->-*>G;C6G-;-C->-*;GC>;-C->-G-*>GC;>-G-C-;-*G;CkGk k>kC>@-<-C-H-*H@C<H-C-<-@-*@<CH6@-H-<-C-*<HC@6C-H-<-@-*@HC<C-@-<-H-*C<@HC-<-@-H-*HCHk@kCk k@<;->-C-G-*CG;>;-G->-C-*G>C;6C-G-;->-*C>;G6>-;-G-C-*>GC;;->-G-C-*;G>C>-C-G-;-* kCG;>>kGkCkH-@-E-<-*<EH@H-@-E-<-*@HE<6E-@-<-H-*@HE<6<-@-E-H-*H<E@<-E-H-@-*HE<@<-H-E-@-*H@EEk@k k<HkG-@-;-C-*CG@;;-@-G-C-*@G;C6@-;-C-G-*G@C;6;-@-G-C-*C@;G@-;-G-C-*;@CGG-@-;-C-* k@kCkGkG;C@E-H-A-<-*H<AE<-A-E-H-*<AHE6<-E-H-A-*HAE<6H-E-A-<-*HAE<<-E-H-A-*<EHAA-E-H-<-*AE<HHk kAkEkG->-;-C-*>;GC>-;-G-C-*G>C;6G->-;-C-*>GC;6C->-;-G-*>C;GG->-;-C-*>CG;;->-C-G-*>kCkGk kC>;G@-C-H-<-*C@H<H-<-C-@-*@<HC6H-<-C-@-*<C@H6<-H-@-C-*H<C@C-H-@-<-*<CH@@-H-C-<-*@kHkCk k@CH<>-G-;-C-*C;G>>-G-C-;-*CG;>6G-;-C->-*;>GC6>-C-G-;-*;GC>>-C-;-G-*C;>G;-G->-C-*C>GkCkG k;>kH-<-@-C-*C@H<C-<-H-@-*H@<C6C-H-<-@-*CH<@6C-<-@-H-*CH<@H-@-<-C-*<CH@@-C-H-<-*Ck@kCH@<Hk kG-;-C->-*;C>G;->-G-C-*G;>C6;-G->-C-*GC>;6C-G->-;-*G;>C>-;-G-C-*C;G>;-G->-C-*>GC kCk>k;Gk@-E-<-H-*E<@HE-H-@-<-*<HE@6@-<-E-H-*<EH@6<-E-H-@-*@HE<@-H-E-<-*<E@H@-<-E-H-*@k kEkHkHE<@;-G-C-@-*;G@C;-C-@-G-*G;C@6C-@-G-;-*;GC@6C-@-G-;-*CG@;G-;-@-C-*@;CGC-;-G-@-*G;Ck@C@kGk kE-H-<-A-*A<EH<-H-E-A-*EAH<6H-E-A-<-*A<EH6A-E-<-H-*<AEHA-H-E-<-*<EHA<-A-H-E-* kHAE<HkAkEk>-G-C-;-*G;>C;->-C-G-*G>C;6;->-C-G-*CG>;6;-G->-C-*;G>C>-;-C-G-*C;G>G->-C-;-*>C;>kCkG kGk@-<-H-C-*@HC<H-<-@-C-*<@CH6C-H-<-@-*HC@<6C-@-<-H-*C@H<<-H-C-@-*HC<@H-@-C-<-*H@k kHkCkC<@G->-;-C-*;>CG>-G-C-;-*C;G>6;-G-C->-*G>;C6;-C-G->-*>GC;;->-G-C-*G;>CG->-;-C-*G;>CGk kCk>kC-<-H-@-*@<HC<-C-@-H-*C<@H6H-<-@-C-*C<H@6@-C-<-H-*H@C<H-C-@-<-*C<@HC-<-H-@-*HkH@@kCk k<C>-C-G-;-*G>C;;-G->-C-*;CG>6;->-G-C-*G>C;6;-G-C->-*>GC;;->-G-C-*GC;>C-;-G->-*>Gk;GCk kC>k<-H-@-E-*E<@H<-@-E-H-*E<@H6<-H-@-E-*H@<E6@-E-H-<-*@EH<E-@-H-<-*E<H@<-H-E-@-*@<HEEkHk k@kC-@-;-G-*@;CGC-;-G-@-*;@GC6@-;-C-G-*;CG@6;-@-C-G-*C;@G@-;-C-G-*CG@;G-C-;-@-*CGkCkG;@ k@k<-E-A-H-*HE<A<-H-E-A-*HEA<6H-A-E-<-*<AEH6E-A-<-H-*<EHAE-H-A-<-*AEH<<-H-E-A-*AE<HAkHkEk k;-G-C->-*G;C>>-G-C-;-*GC;>6>-G-;-C-*;G>C6>-G-C-;-*C>G;C->-;-G-*;>CG>-G-C-;-*;C>k kCkGk>GH-C-@-<-*C<H@C-<-H-@-*CH@<6C-<-@-H-*CH<@6@-<-H-C-*@<CHC-H-@-<-*<@HCC-<-H-@-*<@HCCkHk k@kC->-;-G-*C;G>>-;-C-G-*CG>;6>-;-C-G-*CG>;6G-;->-C-*C>G;G-C->-;-*;>CG>-G-;-C-*>GCGk; kCk>kH-@-<-C-*H<C@C-H-<-@-*@C<H6<-C-H-@-*H@<C6H-<-C-@-*C@<HC-<-H-@-*<HC@@-H-<-C-*H<@Ck kC@kHkC-G-;->-*;G>C>-C-;-G-*C;G>6>-G-C-;-*>;GC6C-G->-;-*C>;GG->-C-;-*>CG;>-;-G-C-*>k>CG;CkGk kE-@-H-<-*<E@HE-<-H-@-*<EH@6@-<-H-E-*<EH@6<-@-E-H-*H<E@H-@-E-<-*E<H@H-E-@-<-*<@HE kHk@kEk;-@-C-G-*@G;C;-@-C-G-*@G;C6G-C-@-;-*;GC@6@-;-C-G-*@CG;;-@-C-G-*@GC;C-;-@-G-*CkGk@@k k;CGA-E-H-<-*<AHEH-<-E-A-*EAH<6A-H-E-<-*AHE<6A-<-H-E-*<EHAH-E-A-<-*A<EHA-<-H-E-*Ak<HkAEkEH kG-C->-;-*G;>C;-G-C->-*>GC;6;-C->-G-*GC;>6;->-C-G-*>;CG;-G-C->-*GC;>G-;->-C-*>>k kGkGCk;C<-C-H-@-*<@HC<-H-C-@-*@<CH6H-<-@-C-*CH<@6@-C-H-<-*HC@<C-<-@-H-*<H@C<-H-C-@-*HHk< kCk@kC@;-G-C->-*>CG;;->-G-C-*G>;C6>-;-C-G-*C>;G6;-G-C->-*>;GC;-G-C->-*;C>GG-;-C->-*CCkGk k>k;G><-H-C-@-*CH@<<-H-C-@-*C<@H6H-C-<-@-*H@C<6<-H-C-@-*HC@<C-H-<-@-*<C@H<-H-C-@-*CHkH@< k@kCk;-G->-C-*;>GCC-G-;->-*C;>G6;-C->-G-*C;G>6>-C-;-G-*;CG>>-G-;-C-*;G>CG-;->-C-*>CkGk kG>k;CH-@-<-E-*E<H@H-E-<-@-*<EH@6@-<-H-E-*E@H<6H-@-<-E-*E<@H<-E-@-H-*@E<H<-E-@-H-*<@kHkH@E kEkC-@-;-G-*GC;@;-C-G-@-*GC;@6@-C-G-;-*G;C@6;-@-G-C-*CG;@C-;-G-@-*C;G@C-;-@-G-*@CCkGk@kG; kH-A-<-E-*HE<AA-<-E-H-*EA<H6<-E-H-A-*A<EH6<-A-H-E-*<AHEH-<-E-A-*EHA<H-E-A-<-*Ak kHkEkH<EA>-G-C-;-*>;GC;-C-G->-*GC>;6C-;-G->-*;G>C6C-G-;->-*G;>CC-G->-;-*GC;>G-;-C->-* k>kG;>CGkCkC-<-H-@-*@CH<H-C-<-@-*C@<H6@-H-C-<-*<@HC6C-@-H-<-*<C@HH-@-C-<-*@<HCH-@-<-C-*<C@HHk kCk@kG-C->-;-*G;>C;-C-G->-*>GC;6G->-;-C-*>G;C6C->-G-;-*G;C>C-G->-;-*>;CGG-;->-C-*>kC>;G kGkCkC-H-<-@-*<HC@C-H-@-<-*CH@<6<-H-@-C-*CH@<6C-H-<-@-*@C<HH-C-@-<-*@C<H@-C-<-H-*CHkCk k@H<@kG-C-;->-*C>G;C-;-G->-*;GC>6>-;-C-G-*>;CG6C-G->-;-*G;C>C->-G-;-*C;G>G-C->-;-*CkGGk k>k>;C<-E-H-@-*EH@<E-@-<-H-*E<H@6<-H-E-@-*HE@<6<-H-E-@-*<E@HE-H-<-@-*HE<@<-H-@-E-* k<EkE@HHk@kC-@-;-G-*G;C@C-;-G-@-*G;C@6C-@-;-G-*GC;@6@-G-;-C-*CG;@C-@-G-;-*C;@GC-;-G-@-*GkG k@;C@kCkA-H-E-<-*AH<EH-A-E-<-*<AEH6H-A-E-<-*EA<H6H-A-<-E-*EH<AE-A-<-H-*AE<HA-H-<-E-* k<HkEkAkAEHC-;-G->-*G;>CG-;-C->-*;>CG6G-;->-C-*G;C>6>-G-C-;-*C>G;G->-C-;-*C>;GC-G-;->-*Gk kCk>>kGC;C-@-H-<-*H<@CH-<-C-@-*@HC<6C-<-H-@-*<@HC6H-<-@-C-*CH<@<-@-C-H-*HC@<<-C-H-@-*H@Hk@kCk k<CG-C-;->-*GC>;;-G->-C-*GC>;6C-;-G->-*C>G;6G-C-;->-*C>G;>-G-;-C-*G>;CC-G-;->-*CGk>k kGCk;><-H-@-C-*@C<H@-C-H-<-*@CH<6<-@-C-H-*<C@H6<-H-@-C-*CH@<C-<-H-@-*H<C@@-<-H-C-*Hk kH@kCk<C@;->-G-C-*C>G;;-C-G->-*>;CG6G-C->-;-*;C>G6G->-;-C-*>CG;C-G->-;-*;C>GC->-;-G-*C;>GGkCk>k kH-<-E-@-*<E@HE-<-@-H-*<@EH6E-<-@-H-*HE@<6<-H-@-E-*@E<HH-E-<-@-*<EH@E-H-<-@-*Ek@kE kH@Hk<;-@-G-C-*;@GC@-;-G-C-*G@;C6@-;-C-G-*G@;C6;-C-@-G-*GC;@G-C-;-@-*GC;@;-@-G-C-*GkCk k@kG@;CH-A-<-E-*HE<A<-E-H-A-*<EAH6<-E-H-A-*EHA<6E-H-<-A-*H<AEA-E-H-<-*<EHAA&<&E&H&*EHkAH< kEkAkC>G;*G;>C;CG>*>G;C6;GC>*CG>;6;C>G*>CG;;>GC*GC>;C>;G*;GC>/MTrk0JAZ2General MIDILeadu @` @HTH JTJ L(0 @LGTG HTH J(0 @JH*HL*LJ*JL*LM*ML*LJTJ @0 M*MO*OM*ML*LJ(J @0LTL E| @E0$Lz*LJz*JLz|L @0$H~*HE~~EH~*HE~~E @0b @00H*HH*HG*GHTH CTC C*0 @CE*EC|C$C* @0CH*HG*GH*HG*GH*HLTL L* @L0Q*QO*OO*OO(OO*0O @Q~QTTSSQ( @Q0Q~QO*OL(0 @LQ*QQ*QQ|Q0 @ @$OTO TTT S*ST*TS*SQ* @QO(OJ(J @LTL OTO Q*QO~O @L~LL*LJ( @JQ~QTTSSQ( @QQz~QOz*OLz( @LM~ML~LJ~JH~HM~(ML~*LM~* @MO*OM*MO*OM*MS*SQ*QS*SQ*Q @HTH JTJ L(L @G|TG H|TH J|( @JL{*LC{TC C{*CC{TC E{*EG{* @GHy*HGyTG Gy*GGy( @GL}*LL}*LM}*ML}*LM}TM L}TL @ O*OO*OL*LO*OL( @LMMLLJJHHM(ML*LM*M @O*OM*MO*OM*MS*SQ*QS*SQ*Q @H(HLTL HT @H J~JH*HG( @GHTH CTC ETE @T@ @ C(C>( @>Q~QTTSSQ(Q @Qz~QOz*OLz(L @M~ML~LJ~JH~HM~(ML~*LM~* @MO*OM*MO*OM*MS*SQ*QS*SQ*Q @HTH JTJ L(L @G|TG H|TH J|(J @L*LCTC C*CCTC E*EG* @GH*HGTG G*GG(G @L*LL*LM*ML*LMTM LTL @ O*OO*OL*LO*OL( @LMMLLJJHHM(ML*LM* @MO*OM*MO*OM*MS*SQ*QS*SQ*0 @QHTH JTJ L(L0 @GTG HTH J( @J0H*HL*LJ*JL*LM*ML*LJT0J @ M*MO*OM*ML*LJ( @0JLTL E|E0 @$Lz*LJz*JLz|L0 @$H~*HE~~EH~*HE~~E @0b @00H*HH*HG*GHTH CTC C* @0CE*EC|C$C*0C @H*HG*GH*HG*GH*HLTL L* @L0Q*QO*OO*OO(OO*0 @OQ~QTTSSQ(0 @QQp~QOX*OLP(L0 @0 @/MTrkT"JAZ2R @General MIDIBase`$@ *$$@*$+@*++@*+0@*00@*0+@*++@* "++@*++@*+2@*22@*27@*77@*72@*22@*"2 $@*$$@*$+@*++@*+0@*00@*0+@*++@*" ++@*++@*+2@*22@*27@*77@*72@*22@*2 "-@*--@*-4@*44@*49@*99@*94@*44@*"4 4@*44@*4;@*;;@*;@@*@@@*@;@*;;@*; ")@*))@*)0@*00@*05@*55@*50@*00@* "0+@*++@*+2@*22@*27@*77@*72@*22@*2 "$@*$$@*$+@*++@*+0@*00@*0+@*++@*" ++@*++@*+2@*22@*27@*77@*72@*22@*2" $@*$$@*$+@*++@*+0@*00@*0+@*++@* "++@*++@*+2@*22@*27@*77@*72@*22@*2" -@*--@*-4@*44@*49@*99@*94@*44@*4 "4@*44@*4;@*;;@*;@@*@@@*@;@*;;@* ;")@*))@*)0@*00@*05@*55@*50@*00@* 0"+@*++@*+2@*22@*27@*77@*72@*22@* "2$@*$$@*$+@*++@*+0@*00@*0+@*++@* "++@*++@*+2@*22@*27@*77@*72@*22@* 2"$@*$$@*$+@*++@*+0@*00@*0+@*++@* "++@*++@*+2@*22@*27@*77@*72@*22@*2 "-@*--@*-4@*44@*49@*99@*94@*44@* "44@*44@*4;@*;;@*;@@*@@@*@;@*;;@* ;")@*))@*)0@*00@*05@*55@*50@*00@* "0+@*++@*+2@*22@*27@*77@*72@*22@* "2$@*$$@*$+@*++@*+0@*00@*0+@*++@* "++@*++@*+2@*22@*27@*77@*72@*22@*" 2$@*$$@*$+@*++@*+0@*00@*0+@*++@* "++@*++@*+2@*22@*27@*77@*72@*22@*"2 -@*--@*-4@*44@*49@*99@*94@*44@* 4"4@*44@*4;@*;;@*;@@*@@@*@;@*;;@*; ")@*))@*)0@*00@*05@*55@*50@*00@*" 0+@*++@*+2@*22@*27@*77@*72@*22@* 2"$@*$$@*$+@*++@*+0@*00@*0+@*++@*+ "+@*++@*+2@*22@*27@*77@*72@*22@*2" $@*$$@*$+@*++@*+0@*00@*0+@*++@* +"+@*++@*+2@*22@*27@*77@*72@*22@*2 "-@*--@*-4@*44@*49@*99@*94@*44@* 4"4@*44@*4;@*;;@*;@@*@@@*@;@*;;@* ;")@*))@*)0@*00@*05@*55@*50@*00@*0" +@*++@*+2@*22@*27@*77@*72@*22@*2" $@*$$@*$+@*++@*+0@*00@*0+@*++@*+ "+@*++@*+2@*22@*27@*77@*72@*22@*" 2$@*$$@*$+@*++@*+0@*00@*0+@*++@*" ++@*++@*+2@*22@*27@*77@*72@*22@*" 2-@*--@*-4@*44@*49@*99@*94@*44@*4" 4@*44@*4;@*;;@*;@@*@@@*@;@*;;@*; ")@*))@*)0@*00@*05@*55@*50@*00@*" 0+@*++@*+2@*22@*27@*77@*72@*22@* 2"$@*$$@*$+@*++@*+0@*00@*0+@*++@*+ "+@*++@*+2@*22@*27@*77@*72@*22@*2 "$@*$$@*$+@*++@*+0@*00@*0+@*++@* "++@*++@*+2@*22@*27@*77@*72@*22@*2" -@*--@*-4@*44@*49@*99@*94@*44@*" 44@*44@*4;@*;;@*;@@*@@@*@;@*;;@* ;")@*))@*)0@*00@*05@*55@*50@*00@*" 0+@*++@*+2@*22@*27@*77@*72@*22@* 2"$@*$$@*$+@*++@*+0@*00@*0+@*++@* "++@*++@*+2@*22@*27@*77@*72@*22@*" 2$@*$$@*$+@*++@*+0@*00@*0+@*++@*+ "+@*++@*+2@*22@*27@*77@*72@*22@*2" -@*--@*-4@*44@*49@*99@*94@*44@*" 44@*44@*4;@*;;@*;@@*@@@*@;@*;;@*; ")@*))@*)0@*00@*05@*55@*50@*009*" 0+1*++)*+2!*22*27*77 *72*22*2/MTrkJAZ2 FDrums @@General MIDIx`#0.0`&0#..00#0&.0.0#0#0.0.0&0#`.&#0.0`.&0.0#0&.#00#.00#0.0&0.0#`.0#0&.`#&0.0.0.#0&0#.00.#00.0#&0`&.0#0.`&0#.0.0&#0.0#.00.#00.0#&0`&#0..0`..0#&00&.#00#.00#0.0.0#&0`.#0&.0`#&0..00&.#00#.00#0.0.0#&0`.&.0#0`&0#.0.0#0&.0#.00.#00.0#&0`#T&.*T`*#*T0*#T0#T*T#&T#*&T&&&T&&T*T&&T&T&*.T&&T0..0&#0`#.0&0.0.&#00#.00#0.0.0&0#`#0.0.&`.0&0.#0#0.&0#.00.#00.0#&0`&#0..0`..0#&00.#0&0#.00.#00&0#.0`..0&#0`#.0.&00.#0&0#.00.#00.0#&0`&#0..0`.&0#.00#0&.0#.00.#00.0#&0`&.0.#0`.&0#.00#0.&0#.00.#00&0#.0`.#0&.0`#&0..00.&#00#.00#0.0&0#.0`..0&#0`..0#&00#0&.0#.00.#00&0#.0`.#0&.0`.&0#.00#0.&0#.00#0.0.0#&0`&.0.#0`..0#&00.&#00#.00.#00.0#&0`.#0&.0`#&0..00&.#00#.00.#00.0#&0`&.0.#0`..0#&00#0.&0#.00#0.0.0#&0`.#0&.0`.&0#.00#0.&0#.00.#00.0#&0`..0&#0`&0.0#.0&.#00#.00#0.0&0.0#`&#0.0.`&0#.0.0&#0.0#.00#0.0&0#.0`&.0.#0`#&0..00#0&.0#.00#0.0&0#.0`#0.&.0`..0#&00&.#00#.00#0.0&0#.0`&#0..0`#&0..00.#0&0#.00#0.0.0#&0`.#0&.0`..0#&00&.#00#.00#0.0&0#.0`..0&#0`#&0..00#0&.0#.00#0.0&0#.0`.#0&.0`.&0#.00.&#00#.00#0.0&0#.0`&#0..0`..0#&00.&#00#.00#0.0.0#&0`.#0&.0`#&0..00.&#00#.00#0.0&0#.0`.#0&.0`#&0..00#0.&0#.00#0.0.0#&0`..0&#0`.&0#.00.&#00#.00#0.0&0#.0`..0&#0`.&0#.00#0.&0#.00#0.0.0#&0`.#0&.0`#&0..00#0.&0#.00#0.0&0#.0`.#0&.0`#.0.&00#0&.0#.00#0.0&0#.0`&#0..0`#.0.&00#0&.0#.00#0.0&0#.0`.#0&.0`#.0.&00#0&.0#.00#0.0.0#&0`..0&#0`..0#&00#0&.0#.00#0.0&0#.0`..0&#0`..0#&00#0&.0#.00#0.0.0#&0`.#0&.0`#&0..00#0.&0#.00#0.0.0#&0`&#0..0`#.0.&00.#0&0#.00.#00.0#&0`..0&#0`.&0#.00#0.&0#.00#0.0.0#&0`..0&#0`..0#&00.#0&0#.00#0.0&0#.0`&.0.#0`..0#&00.#0&0#.00.#00.0#&0`..0&#0`#&0..00&#0.0#.00.#00&0#.0`&#0..0`#.0.&00.#0&0#.00.#00&0#.0`&.0.#0`#.0.&00.#0&0#.00.#00.0#&0`..0&#0`..0#&00.#0&0#.00.#00.0#&0`..0&#0`#.0.&00.#0&0#.00.#00.0#&0`..0&#0`.&0#.00&#0.0#.00.#00&0#.0`&#0..0`.&0#.00&#0.0#.00.#00&0#.0`&#0..0`#.0.&00.#0&0#.00.#00&0#.0`&#0..0`..0#&00.#0&0#.00.#00.0#&0`.#0&.0`#.0.&00.#0&0#.00.#00.0#&0`#T&*T.`#**T0#T*0&T#T#*T*#&T&&&T&T&&T&*T*&T&.T&&T0&#0..0`#&0..00&#0.0#.00#0.0&0#.0`&#0..0`#.0.&00.#0&0#.00#0.0.0#&0`.#0&.0`#.0.&00.#0&0#.00#0.0.0#&0`..0&#0`..0#&00.#0&0#.00#0.0.0#&0`.#0&.0`#.0.&00.#0&0#.00#0.0.0#&0`.#0&.0`#.0.&00&#0.0.0#0.#00.0&0#`&./T//T)T/0)T))T))T)))T)+T+T++0T0T00/T/Q//-M-I-3E-#E2E,E$E3$,2#2A$=2=&=(=#=20(,52#5#;5$$5&350595#;39,$0010-00-%,%3%0-!,3---#$0($)1,&9#;3$#-3))#;$&,91($ # ) )0$#)/MTrk4JAZ2The journey home/simutrans-124.5/simutrans/music/09-Simupolitan-Swing.mid000066400000000000000000000245501520675460500232510ustar00rootroot00000000000000MThdMTrkw By Copyright 2006 All Rights Reserved Generated by NoteWorthy ComposerQQ /MTrk V!Staff8 @LnLOn.OLnLHn.HJnJMn.MJnJGn.GHnPH0n.>@n@An.ACnCEn.EGnGHnH0n.>@n@An.ACnCEnE0EnECn.CAnACnC0GnGJn.JGnGCnC0EnEHn.HEnEAnA0LnLLn.LLnLLn.LLnLLn.LLnLLn.LLnLEn.EGnGHn.HJnJHn.HGnGHnH0n.>@n@An.ACnCEn.EGnGHnH0n.>@n@An.ACnCEnE0EnECn.CAnACnC0GnGJn.JGnGCnC0EnEHn.HEnEAnA0LnLLn.LLnLLn.LLnLLn.LLnLLn.LLnLEn.EGnGHn.HJnJHn.HGnGHnPHpn>@n.@An A EnEEnPE EnEHnPH HnHEnPE AnACnPC HnHCn.CHnHCn.CHnHLnPL LnLJn.JLnLJn.JGnGEn>EAn.AEnEAn.AMMn>MMn>M2LnLJn.JLnLJn.JGnGCn.CGnGJn.JLnLOn.OLnLHn.HJnJMn.MJnJGn.GHnPH0n.>@n@An.ACnCEn.EGnGHnH0n.>@n@An.ACnCEnE0EnECn.CAnACnC0GnGJn.JGnGCnC0EnEHn.HEnEAnA0LnLLn.LLnLLn.LLnLLn.LLnLLn.LLnLEn.EGnGHn.HJnJHn.HGnGHnH0n.>@n@An.ACnCEn.EGnGHnH0n.>@n@An.ACnCEnE0EnECn.CAnACnC0GnGJn.JGnGCnC0EnEHn.HEnEAnA0LnLLn.LLnLLn.LLnLLn.LLnLLn.LLnLEn.EGnGHn.HJnJHn.HGnGHn@HpGnGJn.JGnGCnC0EnEHn.HEnEAnA0LnLLn.LLnLLn.LLnLLn.LLnLLn.LLnLJn.JHnHEn.ECnCEn.EGnGHn.HGnGJn.JGnGCnC0EnEHn.HEnEAnA0CnPC0HnPH0LnPL0OnPO/MTrk )!Staff-18 @@nCnGnP>CG0;n>nCnP;>CpnCnGnP>CG0;n>nCnP;>C0@nCnHn@CHpnCnGnP>CG0;n>nCnP;>CpnCnGnP>CG0;n>nCnP;>C0@nCnHn@CH0@nCnHn@CHCnGnJnPCGJ CnGnInCGIAnEnHnPAEH AnEnHn AEH 33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3`3n 3`3n 3`3n 3/simutrans-124.5/simutrans/music/10-Easy-driving.mid000066400000000000000000001402051520675460500222050ustar00rootroot00000000000000MThd MTrkBCopyright (C) 2006 shunter~ JAZ2/MTrk JAZ2$RightGeneral MIDI][i ? @H]LXL@LXL@LX@L JXJ@LX`Am@L`@X@@ EX@E GX@G HX@H LX@L JXJ@JX@J JX@J LXL@GX@GLXL@LXL@LX@L JXJ@LX`Am@L`@X@@ EX@E GX@G HX@H LX@L JX@J JX@J JXJ@OXO@LX@LQX@Q QXQ@QX Q OXO@JX`DmJJX@J LX@L MX@M QX@Q CMOXOOX@O OX@O LX@L HX`HBdimMXM@MXM@MXM@JX@J GX`BdimGGX@G HX@H JX@J MX M LX LLXL@LXL@LX@L JXJ@LX`Am@L`@X@@ EX@E GX@G HX@H LX@L JXJ@JX@J JX@J LXL@GX@GLXL@LXL@LX@L JXJ@LX`Am@L`@X@@ EX@E GX@G HX@H LX@L JX@J JX@J JXJ@OXO@LX@LQX@Q QXQ@QX Q OXO@JX`DmJJX@J LX@L MX@M QX@Q CMOXOOX@O OX@O LX@L HX`HBdimMXM@MXM@MX@M JXJ@GX`BdimGGX@G HX@H JX@J MX@M LXLAm @X@@ EX@E GX@G HX@H JX@J LX@L`HX@H MXMMX@M JXJ@HX@H JXJ@CXC@LXLLXL@HXH@MXMMX@M JXJ@HX@H OXO@LXL@QXQQXAmQLX@L HXH@EX@E MXMMXTM,JXTJ,HXTH,GMGXGGX@G JXJ@CX@C LXLFmaj7QX@Q LXL@HX H EX@E MX@MMX@M JXJ@HX`GX H`GGX@G JXJ@LX@L QX@Q@LXL@LXL@LX@L JXJ@LX`Am@L`@X@@ EX@E GX@G HX@H LX@L JXJ@JX@J JX@J LXL@GX@GLXL@LXL@LX@L JXJ@LX`Am@L`@X@@ EX@E GX@G HX@H LX@L JX@J JX@J JXJ@OXO@LX@LQX@Q QXQ@QX Q OXO@JX`DmJJX@J LX@L MX@M QX@Q CMOXOOX@O OX@O LX@L HX`HBdimMXM@MXM@MXM@JX@J GX`BdimGGX@G HX@H JX@J MX M LX LLXL@LXL@LX@L JXJ@LX`Am@L`@X@@ EX@E GX@G HX@H LX@L JXJ@JX@J JX@J LXL@GX@GLXL@LXL@LX@L JXJ@LX`Am@L`@X@@ EX@E GX@G HX@H LX@L JX@J JX@J JXJ@OXO@LX@LQX@Q QXQ@QX Q OXO@JX`DmJJX@J LX@L MX@M QX@Q OXCMOOX@O OX@O LX@L HX`HMXBdimM@MXM@MX@M JXJ@GX`BdimGGX@G HX@H JX@J MX@M LXAm`L@@X@@ EX@E GX@G HX@H JX@J LX@L`HX@H MXMMX@M JXJ@HX@H JXJ@CXC@LXLLXL@HXH@MXMMX@M JXJ@HX@H OXO@LXL@QXQAmQXQLX@L HXH@EX@E MXMMXTM,JXTJ,HXTH,GXGMGGX@G JXJ@CX@C LXLQXFmaj7@Q LXL@HX H EX@E MX@MMX@M JXJ@HX`GX H`GGX@G JXJ@LX@L QXQ/MTrk8JAZ2@General MIDIHKY}J<]2YC[2 "Left @ "]2@J<HKC[2@)'9%7(<'`@)0'@7<9`@@7%9'<)0P79@07<@P0@@[2CJ< "]2HK>37(;1@+"^207>; +/2@>6@;6<@>;!7.@.@71;9>=@>@;>!24@@2A@>D;AN@>;@7>`C9@30$O@C77&^C9@0OC@<<`@7CB<07C@76^@7@CB@7`@7@>,@'9%-,`@9->@4(@P4@>'@@,9#`@@9>43@0@40@<%9&@%P9<@4-@@ @4@@@,97;67-+'@^21>;7O2>4;4=;>!2(/21;2>7C0:@>;C!2.@2A;6C/>8?C;>2.+O>.@7&+ @;-2_;>72/`>>;>>>;76070>>;:20;> 26`;C>F`;>@@7G02+@7;$J&@@,C'`;@@@7+CJP7@@-@C%J+`@@@C73J07@0H/@@0C0P@7;HC@7@H2@0C6@0HHJ.7;JEH/@7CJ@"C7<)@@6H2;H71C@ <7@ H9@C3@3@7@C7><7+;7>C;>2(=2!C9;/>7?C>;26?2A3;0>9>@A;>2.@7EA7*>7!A;72>0;@@0(@(>"C#`0C>@@7$@P7@@> C)@$`C>@@@7/0@70@@"<"C"P<@@7(C@ @7@@@C)<`@@<@C7*`@@)'<'77(9%`@)<970'@`9'7%<)0@@P707@@<9P@09&<(5'@@9@<5 0.@<1<@0<A.5(@ 5<.09+0-A < 09@;1+"@7(>3^20;>7 +/@2>6@;6<@;>!@7..@7159@9=>@95!94@@9A5A@9DN@952>@`7(;1+">3^20;>72 +/2@>6@;6<@>;!7.@.@71@>=;9>@>;!24@@2A>D;A@N@;>7>@`79437$$O477&]77940O470<`CB@70@C@76^@7CB@7@`@0C7@;77+><>C;>2(=2!C9;/>7?C>;26?2>39950>>@95@9.7A9>7*97!>57 90@5;74-86@('^;148; (/;@;4@84<@;8!4+@.@4182@;7>@;8!;.@@;A;8@86N@;841@`((88;=O;8;(];;880/4 ;849^HK@C[2]2 "J<4-('86@;7^;1@@;84 (/;@;4@84<@;8!@4+.@41;7@82>@8;!;.@@;A86;8@N@;841@`((;=98O9;;(];;890/4 ;949`81;94@;8;.^@;;68.@`48/;@84(+,@9%;,>'(4_>+@;9@2(P2@;'9#@>,`23@@;9>02@0;%@7&>%P;7>@@2- 2@@;!7>,@`2.>7;@@`@;(9+++@>+2`@@>;9+2+`9(>-@;+2@P9@@;>2:P2@;)@>,7+@;>@7 @21>6<@2>@C27+ 7>2021;/C > 2;@<-C70'@@6^71<C@O7C4@4=@C!7(/71C7@2H0:@CH@!7.@7AC8@6H/?H@C7.0O@7C=@80(@OC@7(^@0C8/7 @C76`C9@1HA70@HC7.^HA0C60@.@7HC0@7);6@>7C2;C21>; 72@ C9@>3;3<@C;>!2(@.@2-C7@>6;1/C@>;!@2-@2A>7;4@A7=A@>;@2.>@2@G7C<<+@7>GC@7(=7!@/G9C7?G@C76?7H3C9@0>H@@C7.@<EH7*C7!H@77C0@@96<7)'@5-^01<95 )/0@@<494<@<9!5+@.@51@<792>@9<!@0.@0A96<8@N@<9@51^7&+ ;->.`2/7;>`>>;>/5>;760705:9>2@095 96@`9F5C9`592G@+2@>.@7&+ ;-`2/>;7`>>;>>>;76070>>;:20;> 26`>F;C`>@;@7G02+7@J&C'@,@;$`@;J@@7+CP@7C%@-J+@`73@@@CJ0@70H/@0C0@P@7;H7C@@H2C6@0@0HHJ.7;JEH/@C@7J"5)<796@A2;A019< 5@0 A9@<393@0C7@7+;7><>C;>2(=2!>7C9;/?C>;26?2A3>9;0>A@>;@2.7EA7*>7!A;72>0;@@/C.<906G<@_CG@7/?7!C+G6@(O@CG7@,>@@HC@7>7AHKCH@?_@CH<7M?7@0 @5/0"A"E H#M,`HK@[2C]2 "J<ME5A0H@7&+ >.;-`;>7@@2/`>>;>>>;76070;:>>20>; 26`;C>F`7G>;02+7@@,;$@J&C'`JC;@7+@@P@7C%@-J+@`@@@JC730@70H/@C0@0P@7;HC@7@@0H2C6@0HHJ.JE7;H/@@7JC"C7@6@<)H2;H71C@ <7@ H9@C3@3<@H@C!@7(.@7-@H7C6@1/H@C@!7-@@7AC7@4@H7=H@C@@7.>@7A7@<<975+>A<90(=0!<79/A9?A<906?0A3>9<0>A@<>2.@5EA7*>7!A<72>0@<0'@C7<-@6^71C<@ 0/@7@C4@4<@@C!@<+.@<1@2C7@>@C@!7.@@7A@6@C8N@C@@<1`<=98)(<O<90(^<890O9<59`<991500<90.^@09.<6@`58/<@)599,@),7%<'`@0(9)7@<P@09'<,@7#`@03@<9700@0@<%9%5&P950-@<@ @0@<,9!@5`95<0.@@`0)+9(7+<+@@`)<7@@0+9`9+7(0<-@@P79<@@0:P0@<,5+@9)@5<@9 @01<6<0@<5+@A2 5<209/A01 < 09@+'@7-;6>7^21>7;O2>4;4=>;!2(/21>7;2A0:@A;>!@2.2AA/>8;6?A;>2.+O@2@@80(C=OC@7(^@0C8/7 C@76`@1C9HA70@HC7.^HA0C60@.@7HC0@96<7@5)A2;A01<9 50@ A9@<393@0A7@<<975+>A<90(=0!<79/A9?A9<06?0A390<9>A@<90.@5EA7*<7!A970<0@9>77-@+';6^217;> +/@2@>4;4<@;>!@7+.@71;2@>7>@>;!@2.@2A@>8;6N@>;71@`@=-(<8O<@4(^<0@8/7 @<99`<1@9904@<4.^@4@6<.@`98/@@-<9-,<%@'@`@@4(-<@P4@@,@<#`@43<@@04@0<%@@%9&P9<@@@4- 4@@@,$@C%@@*4`7%@@>0C@P@7@&>!@C+`@72@@>C07@0<&@C&@&P@<C7-@@ 7@@@"C-@<`@7/@<C@`7/9,7).@<^297;> +/2@@>?;?<@>;!75@.@71;A@>F>@>;!@2<@2A@>N;JN@;>@7F`;;707+)><^29>;7 +/@2@>?;?<@;>!@75.@7/HK]2C@ "J<[2@9@C?0(OC@7*^@5C?OC@@7@C7;77+><>C>;2(=2!;/>7C9?C>;26?2C3>9;0>@C>;@2.7EC7*>7!C;72>0;@>7@7-;6+'^21>;7 +/2@>4@;4<@>;!@7+.@71@>7;2>@;>!@2.@2A;6>8@N@>;71@`0(@8C=OC@77(^C8@0O@C<9`C9@1<07@C7.^@7C6@@.`<8/C@@0<>%0,@C'@,`0>C@7(@@P7@>#@'@C,`>C73@@@07@0C%@%<&@P<@@C7-@ 7@@@@!C,<`<@7.@C@`++;(>+@@9+7`2+9+;>@@`;+2>-9(@@P9>;@2:@P@2>,7+@;)@;>7@ 21@>6<@2>@C27+ 7>20;/21C > 2;@0-@77$'46^71704O77444=74!7(/71@2C7H0:@@HC!@7.7A@6C8H/?H@C7.$O@7)(98<=@O9<0(^90<8/0 9<06`<991AA00<A90.^AA0<609.@0A<)9+ 7&@;->.`7>2/;`>>;>>>;760709>5:2095 96`5C9@@9F`952G@+2@>.@;-+ 7&`;7>2/`>>;>>>;76070>>;:20>; 26`>F;C`;>@7G@02+@7@,C';$J&@`@@;CJ@7+P7@@J+@-C%`@C73@J@0@70C0@@0H/P@7;H@7C@C6H2@0@0HHJ.JE7;H/@JC7@"5)<796@A2;A019< 50@ @A9<393@0@C7><7+;7>C;>2(=2!C9>7;/?C>;26?2>39950>>@59@9.7A9>7*97!>57 905@4-@;786('^;18;4 (/@;@;484<@;8!4+@.@41@82;7>@8;!@;.@;A@;886N@;8@41`((;=88O8;;(];80;8/4 ;849^ "HK[2]2@J<C86@4-;7('^@;1@84; (/;@@;484<@;8!@4+.@41@82;7>@;8!;.@@;A;8@86N@8;41@`98((;=O9;;(];;890/4 ;949`;9814@;8;.^@;;6@8.`48/;@(48+,9%>'@;,4(_>@2(+@9;P@2>,9#;'@`23@@;9>02@0@>%7&;%P7;2-@@> 2@@7@>,;!`@7>2.;@`++>+;(9+@@2`;+>@@2+9`;+@9(@2>-P9@;>2:@P2@;)@7+>,@;>@7 21@>6<@2>C27+@ 7>20C21;/ > 2@;<-C70'@6@^71@C<O7C4@4=@C!7(/71C7@2H0:@@HC!7.@7A@6H/C8?H@C7.0O@7@C=0(@8OC@7(^@0C8/7 @C76`@1C9HA70CH@7.^HA0C60@.@7HC0@@>77);6C2;C21>; 72@ @C9>3;3!@2(.@2-C7@;1>6/C@>;!2-@@2A@>7;4A7=A@;>@2.>@2@C#0(>"@(`@7$@C>0@P@7C)@$> @`@@7/@C>0@70<"C"@"@P@<@7(C@ @7@@C)<@`7*<@@@C`<')'9%@@77(`@@0'<9)7`09'@7%<)@P7@07@9<P0@<(@5'9&@@95< @0.<1<@0<5(@A. 5<.00-A9+ < 0@9@>3+"7(;1^20>;7 +/2@@>6;6<@;>!7.@.@71@9=59>@59!@94@9A9D5A@N@592>@`;1+"7(>3^20>72; +/2@@>6;6<@>;!7.@.@71;9@>=>@;>!@24@2A;A@>DN@>;7>@`43797$$O477&]74079O470<`CB@70@@C76^@7@7@CB`@0C7@><7+;7>C>;2(=2!;/C9>7?C;>26?2A3;0>9>@A;>@2.7EA7*>7!A;72>0;@@60'@C7<-^71@C< 0/7@@C4@4<@C@!@<+.@</ "@J<C[2HK]2@(>"@C#0(`>C7$0@@@P@7C)> @$@`@C@>7/@0@70C"@<"@"P@<@C@7( 7@@@@C)<`C<@@@7*`((7;#@@7"`;7@(;$@P@;@7 ;)`@@;7;/0;@07"4";"@P47@;(@; @;@74;)@`4@;*@;7`;9%)'@@7(<'`@)79<@0'`7%@<)@9'0P707<9@@P@09&@5'<(@<5@9 0.@<1<@0<@A.5( 5<.09+A0- < 09@7(+">3;1@^20;>7 +/2@@>6;6<@;>!7.@.@71>=;9@>@>;!@24@2A@>D;AN@;>@7>`C9@30$O@C77&^C9@0O@C<<`CB@7<07@C76^@7@7CB@`@7A7@5+<<97>A<90(=0!<7A99/?A<906?0A3>9<0>A@><2.@5EA7*>7!A<72>0<@0'@6@<-C7^71@C< 0/@7@C4@4<@@C!<+@.@<1@2@C7>@C@!@7.@7A@C8@6N@C@<1@`)(<=<98O9<0(^90<8O9<59`<991500<90.^@0<6@9.`58/<@9)5),9,<'7%@`7)90(<@@P@09'@7#<,`0379@@<0@009%5&@<%P59@@0-< 0@@5@<,9!`9<0.@@5`@09(@<+)+7+`)7<9@@0+`@<-@9+7(0P7@0:<9@P@0<,@9)5+@@<95 01@<6<0@<@5+A2 5<209/01A < 0@9>77-;6+'@^21>7;O2>4;4=;>!2(/21;2>7A0:@>;A!2.@2A>8;6A/?A;>2.+O@2C=0(@@8OC@7(^C8@0/7 C@76`@1C9HA70HC@7.^HA0C60@.@7HC0@@<#)(9(7"`@0$)79@<P@07 <)9$@`@@0/79<00@09"@5"<"P59<@0(@ @0@@9<)5`@@0*9<5`)'9%7(@@0<'`)0'@7<9@`07%9'@@<)P707@@<9P0@<(9&5'@@@9<5 @0.<1<0@<@5(A. 5<.0A9+0- < 0@9@7(>3+";1^20;>7 +/2@@>6;6<@>;!7.@.@71@;9>=>@;>!24@@2A@>D;AN@>;@7>_7@*C.0>@C0@/MTrk!JAZ2u}C @YTrack3General MIDI]HKJ<Y@ @[ @@[]HK!J<C[HK!J<] @@C+K_+!K$K$!!5$=!$.$K @HK@!J<C[]q$/$=0$2!K!!?!N!K3!O!K!!5!O!K! != !B+K++?+N+K3+Q+K++?+N+KC+A$K_$!$K$$=0$.$Ks$/$=0$0+K++5#+?+K+ +=@+"$K$)=0)2+K++K_+!)K))=0).)Ks)/)=0)0+K++5#+?&K& &=@&"(K((=0(.(Ks(/(=0(0(K((5#(?HK!C]J< @[@(K((=0(.(Ks(/(=0(0(K((5#(?(K( (=@("+K++?+N+K3+O+K++5+O+K+ += +B$K$$?$N$K3$O$K$$5$O$K$ $= $B+K++=0+.+Ks+/+=0+0$K$$5#$?$K$ $=@$")K))=0).)Ks)/)=0)2+K++?+N+KC+A+K++?+N+KC+A$K_$!)K))=0).)Ks)/)=0)0+K++5#+?+K+ +=@+"$K?$=$K#$c)K|!@]CJ<HK[ @)+K++?+N+KC+A$K_$!$K$$=0$.$Ks$/$=0$0)K))5#)?+K+ +=@+"$K$$=0$.$Ks$/$=0$0)K))5#)?)K) )=@)")K))?)N)K3)O)K))5)O)K) )= )B+K++?+N+K3+O$K$$5$O$K$ $= $B)K))=0).)Ks)/)=0)0)K))5#)?)K) )=@)"+K++=0+.+Ks+/+=0+0!K!!5#!?!K! !=@!"!K!!?!N!K3!Q$P$)H0)P)H0)2+Z++Z~[ @C!HK]J<@+$K$$=0$.$Ks$/$=0$0+K++5#+?+K+ +=@+"+K++=0+.+Ks+/+=0+0$K$$5#$?$K$ $=@$"$K$$?$N$K3$O+K++5+O+K+ += +B$K$$?$N$K3$O)K))5)O)K) )= )B+K++?+N+KC+A+K++?+N+KC+A$K_$!)K))=0).)Ks)/)=0)0+K++5#+?&K& &=@&"(K((=0(.(Ks(/(=0(0(K((5#(?]C@HK[ @!J<(K((=0(.(Ks(/(=0(0(K((5#(?(K( (=@("+K++?+N+K3+O+K++5+O+K+ += +B$K$$?$N$K3$O$K$$5$O$K$ $= $B+K++=0+.+Ks+/+=0+2$K$)=0)2+K++K_+!)K))=0).)Ks)/)=0)0+K++5#+?+K+ +=@+"$K$$=0$.$KHK @C@!]J<[$(K()=0)2+K_+!$K$$=0$.$Ks$/$=0$0)K))5#)?+K+ +=@+"$K$$=0$.$Ks$/$=0$0)K))5#)?)K) )=@)")K))?)N)K3)O)K))5)O)K) )= )B+K++?+N+K3+O$K$$5$O$K$ $= $B)K))=0)2+K+?@/MTrk(JAZ2uTrack4}[2J<@C @YHK ^General MIDIY]2C746<)H2Z71`H9HCC3@3^77(\H7H ^CHK[2]2J<@CC6441`7-7`@@4C7CH7H^7.7^A7<5+97<7/67-^21b>>4;4^77+_0<2>>7`22.`<>>8<6`771^0(4(>"C#>/;+9_7$7_CC)> >@$`77/`C"<"C4"42<_7(7`<<C)@@C`7*7`<0>7)'7(<'9%`0'`<77%9'9<)`070`[2HKC]2 ^J<@409@<7(;1+">3^20b>>6;6;^7.7`>=;>;9`224`>;>D;A`7>7^)+430$C9b7&7^CC9@0`<<_;>2CBC447`776`CB@7@C`<,9%@'-,<4_4(`>'9#>@@,`434`<%9&@9@%CH@_4-4`9@@<-+<(@+-7_4+4`<+@-<@`4:4`@9+<9@,<)`441@6@\9E29+0@2@041<477-+';6^21b>>4;;4^22(^E9>;2>7;C0\2.2`;6C/>8C;>`22.^>./-+ 7&+7>_2/2_>>>;>;^767`>/:/>>C_226`>F>;C;_7G7`C'+J&<$4,`77+`CC%J+@-J`773`H/C0>;2/C440`77;`H@0H2C6C@`77;JJE`<4CC7<)46HH2Z771`H9HCC3@3@^77(\H7H41C64C`77-`C7@4@CH7H^77.^C7J<C><7+;77b2(^CC9;;/>>7`226^A3H4@;;0>9>`22.`;>>6;-A7A:C#>"0(4(C7;A>_7$`C@$>> C)`77/`C"C<"44"2_7(7`CC)<<@@`7*7`07<><')'9%7(`0'`9'<)7%<79`070`C<@9<(9&45'`00.<<1\A.5(50<.</0-9+09`709);1+">37(^20b>6>;;6^7.7^5<A7599=`-4`9D955A`2>2_;+>7(+">3;1^202a>6>;;6^7.7_59-;>;9>=`224`;A>>D;`77>_+C9@3$$b7&7^C@C9@0`<<_2>;CBC@@7`776`@7CB@C`;/CC9>7`226^>3>A<99950`-.`95-965>7>;;C97>5/;74-('^/1b;4;`44+_2-;;7`/./`;;8`441_(((;=;b//(^;8;`494^ ^[2J<CHK@]2;74-('^/1b;4;`44+`;7;`//.`;8;`441_(((;=98;b//(^;890;9`494`;;9`/./`;;6`448^+,9%>';,494;(/(/;_2(`;'9#>,;>9`223`;%>%;>7&`2-2`7>7;;!>,`2.2`9+>++9+>+/(`2+2`9(;+;>>-9`2:2`/)>,7>7+/`221>6>\7+C270>2>/212;;/`+9;C0'<-46C7^717bC4C@4^77(^>/4C42C7H0\77.`H/@6C@C8H`7.7^2<0C=0(448Cb7(7^@C8C@0`767_H441C9CHA\7.7^HAH/C6C/@.@_@07>77);6CC2Z21`C9C>3>;;3^2(2\C7CH4;1;>>6`22-`>7;4;>A7^22.^H77C47C<<+b7(^C7CH9@/H`767^H3A;>HC9C404`7.7`@CC6@-HH7;<@2)'5-<796^01b<<4949^5+5_7492<9<7`0.0`9<<896`551^+ >.7&;-C)H_2/`>>>;;>^767`5:9>79<05_-6`599F5C`22G`;-++ >.7&>;_22/`>>>;;>^776`;:95>>>-;_226`;C;>F>`7G7`0$J&@,+C'`7+7`J+@-C%JC@`737`@C;C0></7b2(^C9;/C>7>`226^A39A<>9/0/>`22.`;-;>>6A7A;7AC;4/C.H<06<9`7/`@(H6CHC+`77<_2/@,H@H>CC;`7>7`@CHCH@?HK`7M7\0<4>5/0"A"E HH#M,`]2[2HK@J< ^CME5A<)46C7H2Z71`H9HCC3@3^77(\HH7C441C6`77-`C7@4C@HH7^77.^@7H0@H<74;<4+77b/(^;77@9@;7/`/6/^@3@C7;9;70`/./`77-;;6@@7:@C4;@7796<75-)'^01b<4<994^55+_/<9<792`00.`<969<8`515^;-7&+ >.)_2/`>>>;>;^767`;:>>>;09<_226`;>>F;C`77G`+4,C'J&<$5_7+7`C%C@-JJ+`737`40H/2C0C4>;`7;7`@HCC6@0H2`JEJ7;7`<4C7C46<)H2HZ717`H9HCC3@@3^7(7\H7HCC6@@1`7-7`@4CC7@HH7^77.^JA7<45+<<97b0(^9/9<7A9<A`006^A3@CHA<>9<0`2.`><->6<AA7;795A00'<-C7@6<^71bC4C@@4^<<+_2CC742`7.7`C@@6C8`<<1_>0)(<=98<b0(^90<8<9`59_C47@919<9<`00.`<9.<69`558^9,7%),<'<9)_0(0_<99'77#<,`003`5&<%9%<95_0-0`<59<,59!`00.`97<)9()+<+7+`00+`99+<-7(<7`0:0`<,59)9<5+`001<6<\5+A250<2</099/01`)07/6>77-+'^21b>4>;4^22(^5<A;>>7;2A0\2.2`>8A;>A/;6`2.2^97+/C=0(48b7(^CC8@0`776_;>AC9441CHA\7.7^HAH/C6C/@.@_@20<7965)A2Z01`A9A<3<993^00(\AA7CH74<991<6`00-`<9<794AA7^0.0^A75A9<<97<5+b0(0^99/A<A9<7`060^A3A<99<90`0.0`9-<<69AA7;5A<+'7-/6>7^21b>>4;4^77+_0;;2>>7`22.`;>8;6>`771_/9+7<8-(@=b4(^@8<@<0`99_>;2@@9<1<`4.4`<@@6<.`998^@'<%-,-<@_4(4_<#@,@<`434`@%<@9&<%9_4-4`9@9$`7%`C>C+>!@@&`772`9@<&C&@&C<`77-`@"C@<C-<`77/`<77/09,>).<^29b>?>;?;^775^5A<;;A>>F`22<`>N;J>;`77F_+70+)>7><;;;^292a>>?;;?^775^]2 ^[2@CJ<HK;49C?0(b7*^@5C?C`+H7;7><7+b2(^>7;/>;CC9`226^C3@C4>;0>9;`22.`>>6;-;CC7;77->7/6+'>^212a>>4;4;^7+7`>>7//2`22.`>8;>;6`771_+C480(C=b7(7^CC8@0`<9_/>2;441CC9`77.`@C@.C6`<<8^>%0,C'4,C04_7(7_C,C>@'>#@_773`C<&C%44%<_7-7`<<@C@!C,`77.`9+>0;(++>+`2+`>9;+;9(>-`2:2`;>7+7@<;)>,C4`212>>6\C277+0>2>/;212;/`+9;0'C7C<-46^771bCC4@4^7(7^>4C7C42H0\7.7`@6C8H@H/C`7.7^2<0)(<=98b0(^<8909<`006_H@C4<991<9AA\0.0^AAA/<6</9.9_9;-+ >.7&)7_2/`>>>;>;^767`<A79>5:0_-6`99F5C5`2G2`;-7&+ >>.+;_2/2`>>>;>;^767`5->;:9>>;_226`;C>;>F`77G`+C'@,0$J&`7+7`C%@-@JCJ+`737`C@;@02>C07>CC9;/`226^>3A><99950`-.`59965->7>;5C;>97/;74-('^/1b;;4`44+_2-;7;`//.`;8;`441_(;((;=b/(/^;;8`449^J<]2 ^HK@C[2(';74-^/1b;4;`44+`;;7`//.`;;8`414_(;=((98;b//(^;;8909`449`;;9`/./`;6;`448^9%+,;,>'4/9;4(;/(_2(`;'>,9#>;9`223`;%>;7&>%`22-`>,>;7;!7`22.`>9+++/(>+9+`2+2`;>->9;+9(`22:`7+/)/7>,>`212>>6\7C27+0>2>/21;2;/`+9;460'C<-C7^771bC4C@4^7(7^>/4CC742H0\7.7`@C8HH/@6C`77.^20<0(448CC=b7(7^C8@@0C`767_H4C41C9HA\7.7^HAH/C6C/@.@_@0>77)7;6C2CZ21`C9C>>3;;3^22(\CC7H4;>6;1>`22-`>7;>;4A7^22.^0(4(>"C#7>C_7$`>C> @$C)`77/`4"CC"4<";A_7(7`C<@<C)@`77*`<7)'0><'9%7(2_0'`<)9'79<7%`070`9<C4<(5'9&@`0.0<<1\5A.5(0<.</00-9+9`09)7>3;17(+"^20b>>6;;6^77.^<A579=59`-4`9D595A`22>_;>+>3;1+"7(^202a>>6;;6^7.7_95-;;9>>=`224`;>D>;A`77>_+@3C9$$b77&^C@0C9@`<<_>;2CB@@7C`776`C@7@CB`C;/>7C9`226^A3<A9/>>9/0`22.`>;;->6A7A;;C7A460'<-C7^71bCC4@4^<<+^ ^]2HKC@[2J<C#4(>"0(/_7$`C)@$> C>_77/`4"<"CC"4_77(`@C<C)@<_77*`<>>7"((07;#02_/$`;7;)7 `///`C4@;7<;"4"7"`/(/`47;7;)4`/*/`<'47(/@C)'(779%`0'`9'79<7%<)`007`;49<5'<(9&`0.0<<1\A.55(0<.</9+0-09`07)9;17(>3+"^20b>6>;6;^7.7^A5<;9>>=;`242`>D;A>;`7>7_+0$C943b77&^CC9@0`<<_>;2C447CB`767`CB@C@7`9<0<`2.`<<->6>AA7;759A<C7@6<-00'^71bCC4@@4^<<+_242CC7`77.`@C@6C8`<1<_>098<=)(<b0(^<990<8`59_47C@<9991<`00.`9.<<69`558^9,),7%<'<)9_0(0_7<9'7#9<,`003`<9<%5&9%5_0-0`<,9!95<5`00.`)<+)+7+9(7<9`00+`<-7(9<79+`00:`5+9)<,59<`010<6<\55+A20<2</09019/`0)7+'>77-/6^21b>>4;4^22(^5A<>;;2>7A0\2.2`A/>A;;6>8`22.^9+7/0(48C=b7(^CC8@0`776_>A;C41C94HA\77.^HAH/C6C/@.@_<#)(@9(7"207_0$`97<7 <)9$`00/`<"5"9"9HC<4_00(`<<)5995`0*0`77(9%<')'<9)`00'`<977%<)9'`007`5'9&<(59<`00.<<1\55(A.0<.</0-909+`7)07(+"/1>3^20b>>6;6^7.7^A<5>=;9;>`242`;A>D;>`77>^@,C,0,9+/2>;7?0@C@/MTrk0JAZ2 0eGeneral MIDI[(J<}@Track5HKC](YY @J<HKC[(@](H.@]([(J<CHKHC.CJ<]([(CHK@H4@4G/>.@HG.>.G>H.@.GH.>.HC.>CA.>.A>;8J<[(@CHK](;H4@4H.<.HE/@H;)G)C)<C);)G)CG;<)C);GCH)EA)<)HH)<@HK](CJ<[(AH<C@4H4E/<.H.@H<)C)H/H<EHCC)H)<<)<HA)H)<)C>)G)AH<;)>G;G)>);)<)H)@);GHH)<@G.<;.HE/;9/GH.E<.9HH.<<.A8<H>.AC8HKC](@J<[(>CH4@4H@H.E/<.H<;)G)C)>/;CJ/EE)G;)D)JE>G)](@[(CHKJ<DG;H.A.HA>.C.>A.C>.A>J<[(C@HK](H.HG8@.<8@GA.C.<ACA.C8ACA.A>.><.A.<>.AC.@$C>C$@@C@/MTrk2JAZ2J<wDrumsGeneral MIDI['$@1$ @}HK#;,)YC L] '#,1@,$ ,@0$'0&A,0 ,&0'@,$ ,@,)[@J<#D]CHK #,@,#D ,#@,)'00$&2 '&,0@, ,@#;,) #,@,$ ,@,00$')&: '&0,@,$ ,@#D,) ,#@#D,$ ,#@&:,00$'0 0&,'@&+,$ &,&$ &1,$'#3 ',#1@, ,@')0,)&: '0,&@, ,@,$#; #,@#;, ,#@')&+0,$ '0&,&$ &, ,@1,$'#3 '1#,@, ,@&:')0,) ',&0@, ,@#;,$ #,@#;, ,#@,$&+0') ,&'0&$ &, ,@,#; ,#@, ,@0,$&:'$ &0,'@, ,@J<,[HK]@C#; ,#@#;, ,#@&:0,$') ',&0@, ,@,0#D #,@,$ ,@0+'5&H,0 ,'0&@,$ ,@#L,0 #,@'5#3,$ ,'#(',0'50+''0,& &'<,$ ,'&2 &1,$'#3 1',#@, ,@&:,)')0 0,'&@, ,@#;,$ #,@#;, ,#@&+,$0') '&,0&$ &, ,@,)1$#;'$#D,$ 1#,'#,@,,$ ,,@')0$&A,00$,)'0&A ,'0'&,0&@,$, ,,@J<C@[#D,)HK,$#D] #,,#@#;#D,, ,#,#@0$,)'0,)'0&A0$&2 ''0&&0,,@,, ,,@#D,$ ,#@, ,@&A0$,)') &,'0@, ,@,$#D #,@#;, ,#@,)&A0$'0 &,'0@, ,@,)#; #,@,$ ,@')&:0$,0 0,&'@,$ ,@,)#D #,@#D,$ #,@&:'00$,0 &0',@&+,$ ,&&$ &1$'$#;,) ',#1@,$ ,@&A'00$,0 0&',@,$ ,@#D,) #,@#D, ,#@&20$'0,) '&0,@, ,@,0#D ,#@,$ ,@0+&H,0'5 &0,'@,$ ,@#L,0 #,@'5#3,$ #',(',0'50+''0,& &'<,$ ',&2 &'1#3,$ ,'#1@, ,@&:,)0') &,0'@, ,@,$#; ,#@,#; ,#@&+0'),$ 0,&'&$ &, ,@'$#;1$,) 1',#@,$ ,@'0,0&A0$ &0',@,$ ,@,)#D ,#@,#D #,@,)'00$&2 ,'0&@, ,@1',$#3 1'#,@, ,@')&:0,) ,0&'@, ,@,$#; #,@,#; #,@&+0'),$ 0'&,&$ &, ,@#3,$'1 '#1,@, ,@0&:'),) 0'&,@, ,@,$#; ,#@#;, #,@,$&+')0 0'&,&$ &, ,@#;, ,#@, ,@&:0'$,$ 0',&@, ,@,#; ,#@#;, ,#@,$')0&: &,'0@, ,@,0#D ,#@,$ ,@,0&H'50+ 0',&@,$ ,@#L,0 ,#@#3'5,$ '#,(',0'50+'',0& &,$'< ',&2 &#D,0 #,@,$ ,@&H0+,0'5 '0,&@,$ ,@,0#L ,#@,$#3'5 ',#(',00+'5'',0& &,$'< ,'&2 &'1,$#3 ,'1#@, ,@&:,)0') ,'&0@, ,@,$#; #,@#;, ,#@0,$&+') &,0'&$ &, ,@1$#;,)'$ #1',@,$ ,@,00$&A'0 &',0@,$ ,@#D,) #,@#D, #,@,)'00$&2 '0&,@, ,@,)#; #,@,$ ,@0$&:'),0 ,0'&@,$ ,@,)#D ,#@#D,$ ,#@'0&:,00$ 0&',@&+,$ &,&$ &,)#; #,@, ,@&A'0,00$ 0,'&@,$ ,@,$#D #,@,$#; #,@&A0$,0'0 '0,&@,#* #,@#;,0 #,& &,$ ,@,0&A'00$ ,&0'@,$ ,@[@J<#D,0]CHK ,#@#D,$ #,@&A'),50$ ,'0&@,$ ,@#;,) ,#@, ,@,00$&A'0 0&,'@,$ ,@,$#D ,#@,$#; #,@&A0$'0,0 '0,&@#*, ,#@#D,$ ,#@, ,@0$,)&A') ',0&@, ,@#D,$ #,@,#; ,#@,)&A0$'0 ,'0&@, ,@,$#D #,@, ,@&A'),)0$ &0',@, ,@,$#D ,#@#;, #,@'0&A,)0$ 0&',@, ,@#;,) #,@,$ ,@&:,0')0$ ,0&'@,$ ,@#D,) ,#@#D,$ ,#@0$&:,0'0 &'0,@,$&+ ,&&$ &'$,)#;1$ ',#1@,$ ,@,0'0&A0$ &'0,@,$ ,@#D,) ,#@#D, #,@,)0$'0&2 &,0'@, ,@,)#; ,#@,$ ,@0$')&:,0 0,'&@,$ ,@#D,) #,@#D,$ #,@&:'00$,0 '&,0@&+,$ &,&$ &,)#; #,@, ,@,0&A0$'0 &',0@,$ ,@#D,$ ,#@#;,$ #,@0$,0'0&A &0',@,#* #,@#;,0 ,#& &,$ ,@&A0$'0,0 &'0,@,$ ,@#D,0 #,@,$#D #,@')&A,50$ &0,'@,$ ,@,)#; #,@, ,@'0,0&A0$ 0'&,@,$ ,@,$#D #,@,$#; #,@,0'0&A0$ ',0&@,#* ,#@,0#D ,#@,$ ,@,0'50+&H ,&0'@,$ ,@,0#L ,#@'5,$#3 ,'#(',0'50+''0,& &'<,$ ,'&2 &#D,0 #,@,$ ,@'5&H0+,0 '0&,@,$ ,@,0#L #,@,$'5#3 ',#(''50+,0''0,& &,$'< ',&2 &1',$#3 ,#1'@, ,@')0&:,) &0,'@, ,@,$#; #,@,#; ,#@')&+0,$ ',0&&$ &, ,@#;,)1$'$ #'1,@,$ ,@0$'0&A,0 0,&'@,$ ,@#D,) ,#@,#D #,@&20$,)'0 ,0&'@, ,@#;,) ,#@,$ ,@&:,00$') 0,'&@,$ ,@#D,) ,#@,$#D ,#@&:0$,0'0 ,0&'@,$&+ ,&&$ &#;,) ,#&$ &, ,@'00$,)&A '&,0@, ,@,)#D ,#@,#3 #,@,)'0&20$ ,'&0&+ &#3, ,#@1$'$#3&+ &'1#`J<[@HKC]`#3, #,@&20$'0,) ,0&'&+ &,#3 ,#@#;1$,)'$ ',1#@,$ ,@&A,0'00$ &',0@,$ ,@,)#D ,#@,#D #,@&2'0,)0$ &,0'@, ,@#;,) #,@,$ ,@'),00$&: 0',&@,$ ,@#D,) ,#@,$#D ,#@0$&:'0,0 &',0@,$&+ ,&&$ &#;,) ,#@, ,@&A0$,0'0 &,'0@,$ ,@,$#D #,@#;,$ ,#@,0&A0$'0 ,0&'@#*, ,#@#D,0 #,@,$ ,@,0'5&H0+ '&,0@,$ ,@#L,0 #,@,$'5#3 '#,(''5,00+'',0& &,$'< ,'&2 &#3',$1 1#,'@, ,@,)')0&: ',&0@, ,@,$#; ,#@,#; #,@,$&+')0 &,0'&$ &, ,@,)1$'$#; ',1#@,$ ,@,0&A'00$ &,'0@,$ ,@,)#D ,#@#D, ,#@,)0$'0&2 ',0&@, ,@#;,) #,@,$ ,@&:0$,0') 0&,'@,$ ,@,)#D #,@,$#D ,#@,0&:'00$ 0',&@&+,$ ,&&$ &#;,) ,#@, ,@&A0$'0,0 '0,&@,$ ,@,$#D ,#@,$#; ,#@&A'00$,0 &0',@,#* ,#@#;,0 ,#& &,$ ,@0$,0'0&A 0',&@,$ ,@#D,0 ,#@#D,$ #,@&A0$,5') 0,&'@,$ ,@#;,) ,#@, ,@,00$'0&A '0&,@,$ ,@,$#D #,@,$#; #,@&A'00$,0 ,&'0@#*, ,#@#D,$ #,@, ,@'),)&A0$ 0'&,@, ,@#D,$ #,@#;, ,#@,)0$&A'0 ',0&@, ,@,$#D ,#@, ,@,)&A0$') ',&0@, ,@#D,$ #,@#;, ,#@&A0$'0,) &0',@, ,@,)#; ,#@,$ ,@0$,0')&: ,&0'@,$ ,@#D,) ,#@,$#D ,#@0$'0&:,0 ,0'&@,$&+ ,&&$ &'$#;,)1$ 1,'#@,$ ,@'0,00$&A ,&'0@,$ ,@#D,) ,#@,#D #,@,)0$'0&2 '&0,@, ,@,)#; ,#@,$ ,@0$&:'),0 '0&,@,$ ,@,)#D ,#@,$#D ,#@,0'0&:0$ ,'&0@,$&+ ,&&$ &#;,) #,@, ,@0$'0,0&A 0,'&@,$ ,@,$#D ,#@,$#; ,#@0$,0'0&A &,'0@,#* #,@,0#; #,& &,$ ,@0$&A,0'0 '&,0@,$ ,@#D,0 #,@#D,$ ,#@,50$')&A 0',&@,$ ,@,$'#31 ',#1@, ,@')0,)&: 0,&'@, ,@,$#; #,@,#; ,#@0&+'),$ ,0&'&$ &, ,@'#5,&1 #',1@, ,@0,,',&= ,0&'@, ,@#@,( ,#@#@, #,@,('/0 &0 0&',&' &, ,@#C,# #,@, ,@,*0!'*&C &',0@, ,@#C,# #,@#C, #,@,*'10!&C ,&0'@, ,@#C,# #,@, ,@,*0!&C'* &'0,@, ,@,##C #,@,#C #,@0!&C'1,* ,'&0@, ,@,9#M ,#@,* ,@,9'@&T02 0',&@,* ,@#V,9 ,#@#:'@,* ,#'(',9'@02'',0& &'G,* ,'&: &,9#M #,@,* ,@,9'@&T02 0&,'@,* ,@#V[]C,9@J<HK #,@'@#:,* #,'('02'@,9'',0& &'G,* ',&: &,('!#71! ,'#1@,! ,@0 '/&@,/ '&0,@,! ,@,&#> #,@#>, ,#@0,&&.', ,0&'&& &, ,@1$#;,)'$ #'1,@,$ ,@,0&A'00$ '0&,@,$ ,@,)#D #,@#D, #,@0$&2'0,) &,0'@, ,@#;,) #,@,$ ,@0$')&:,0 &0,'@,$ ,@,)#D ,#@#D,$ ,#@&:,0'00$ ,&0'@,$&+ &,&$ &,)#; #,@, ,@&A'0,00$ '&,0@,$ ,@,$#D ,#@,$#; ,#@,00$'0&A &,'0@#*, ,#@,0#; ,#& &,$ ,@,0'00$&A 0,'&@,$ ,@,0#D #,@#D,$ #,@,5&A0$') 0&',@,$ ,@#;,) ,#@, ,@,0&A'00$ &',0@,$ ,@#D,$ ,#@,$#; ,#@0$'0&A,0 ,0'&@#*, #,@,$#D #,@, ,@&A,)0$') &'0,@, ,@,$#D #,@,#; #,@&A,)'00$ 0,'&@, ,@,$#D #,@, ,@,)')&A0$ '&,0@, ,@,$#D #,@#;, ,#@,)'00$&A &,'0@, ,@#;,) ,#@,$ ,@'),0&:0$ &,'0@,$ ,@,)#D ,#@,$#D #,@,0&:'00$ 0',&@,$&+ &,&$ &#D,0 #,@,$ ,@&H'50+,0 '&,0@,$ ,@#L,0 ,#@'5,$#3 ,#'(''50+,0'',0& &,$'< ',&2 &,0#D ,#@,$ ,@&H'50+,0 ',0&@,$ ,@,0#L #,@,$'5#3 #,'(''50+,0'',0& &,$'< ,'&2 &#31',$ #1',@, ,@,)&:')0 ,&0'@, ,@#;,$ #,@#;, #,@&+,$')0 ,&'0&$ &, ,@1$'$,)#; ,#1'@,$ ,@&A'00$,0 &0,'@,$ ,@#D,) ,#@#D, ,#@&2,)0$'0 0&,'@, ,@,)#; ,#@,$ ,@')&:,00$ 0,'&@,$ ,@,)#D #,@,$#D #,@'00$&:,0 ',0&@,$&+ &,&$ &,)#; #,@, ,@,00$&A'0 &0,'@,$ ,@,$#D ,#@,$#; ,#@,0&A0$'0 ,'&0@,#* #,@#;,0 ,#& &,$ ,@'0&A,00$ 0&,'@,$ ,@,0#DC][J<@HK #,@,$#D ,#@&A'),50$ 0&,'@,$ ,@#;,) #,@, ,@0$&A'0,0 '&,0@,$ ,@#D,$ ,#@#;,$ #,@0$'0,0&A &',0@#*, ,#@,$#D ,#@, ,@0$'),)&A 0,&'@, ,@,$#D ,#@#;, #,@'0&A,)0$ ',0&@, ,@#D,$ ,#@, ,@0$')&A,) &0',@, ,@#D,$ ,#@,#; #,@'00$,)&A '0,&@, ,@,)#; #,@,$ ,@,0&:0$') '0&,@,$ ,@,)#D #,@,$#D #,@'00$,0&: &0',@&+,$ ,&&$ &'$,)#;1$ 1,#'@,$ ,@0$'0&A,0 &'0,@,$ ,@,)#D #,@#D, ,#@'00$&2,) &'0,@, ,@#;,) #,@,$ ,@')0$,0&: ',&0@,$ ,@,)#D ,#@#D,$ #,@&:,00$'0 '0&,@,$&+ ,&&$ &',$#31 ,'#1@, ,@,)&:0') &0,'@, ,@#;,$ ,#@,#; ,#@&+')0,$ '&,0&$ &, ,@1',$#3 '1#,@, ,@')&:0,) ,'0&@, ,@#;,$ #,@#;, #,@&+0'),$ ,&0'&$ &, ,@,#; #,@, ,@'$0&:,$ 0,'&@, ,@#;, #,@,#; ,#@,$0')&: ,'0&@, ,@,0#D #,@,$ ,@'5&H0+,0 ,0&'@,$ ,@#L,0 ,#@#3,$'5 ',#(''50+,0'',0& &,$'< ',&2 &#D,0 ,#@,$ ,@,0'5&H0+ &',0@,$ ,@#L,0 #,@#3,$'5 ,'#(''5,00+''0,& &,$'< ,'&2 &1'#3,$ '#,1@, ,@,)')&:0 0&,'@, ,@#;,$ #,@,#; #,@0,$')&+ &,'0&$ &, ,@#;1$'$,) 1#,'@,$ ,@,00$&A'0 ,0&'@,$ ,@,)#D #,@#D, #,@'0&2,)0$ &0',@, ,@,)#; ,#@,$ ,@')&:,00$ ,0'&@,$ ,@#D,) #,@#D,$ #,@,0'0&:0$ &'0,@&+,$ &,&$ &,$'1#3 #',1@, ,@')&:,)0 '&,0@, ,@HK,$#;J<][C@ #,@#;, #,@&+0'),$ &0,'&$ &, ,@#3,$1' ,1#'@, ,@0,)')&: ,0'&@, ,@,$#; ,#@,#; ,#@,$&+')0 ,&'0&$ &, ,@,$#3'1 #1',@, ,@')&:0,) 0&',@, ,@#;,$ ,#@#;, ,#@0&+'),$ ,'0&&$ &, ,@,#; ,#@, ,@,$&:0'$ 0,'&@, ,@#;, ,#@,#; #,@')0&:,$ 0,'&@, ,@#D,0 ,#@,$ ,@,0&H0+'5 &,'0@,$ ,@,0#L ,#@,$#3'5 '#,('0+,0'5''0,& &'<,$ ,'&2 &1,$#3' 1,#'@, ,@0'),)&: 0,&'@, ,@,$#; ,#@,#; ,#@0')&+,$ 0&,'&$ &, ,@#;,)'$1$ 1,#'@,$ ,@'0,00$&A ',&0@,$ ,@,)#D #,@#D, ,#@&20$,)'0 &0,'@, ,@#;,) ,#@,$ ,@0$,0')&: &,0'@,$ ,@#D,) ,#@,$#D #,@,00$&:'0 0&',@&+,$ &,&$ &,)#; ,#@, ,@'00$&A,0 ,'&0@,$ ,@#D,$ #,@,$#; ,#@&A,00$'0 &0',@#*, #,@#;,0 ,#& &,$ ,@0$&A,0'0 ,'&0@,$ ,@,0#D #,@#D,$ ,#@0$,5&A') 0,&'@,$ ,@,)#; ,#@, ,@,0'00$&A ,&0'@,$ ,@,$#D ,#@,$#; ,#@'0&A,00$ &,'0@#*, #,@,$#D ,#@, ,@&A,)')0$ 0,'&@, ,@#D,$ #,@#;, #,@,)&A'00$ 0&,'@, ,@,$#D ,#@, ,@,)&A0$') ,'0&@, ,@#D,$ #,@#;, ,#@'0&A,)0$ 0',&@, ,@,$#3'1 1'#,@, ,@&:0,)') ,'&0@, ,@#;,$ ,#@,#; #,@,$')&+0 ,0'&&$ &, ,@1,$'#3 #1,'@, ,@')&:0,) &'0,@, ,@#;,$ #,@#;, ,#@,$&+')0 '&0,&$ &, ,@#;, ,#@, ,@,$&:0'$ &'0,@, ,@#;, ,#@,#; #,@&:')0,$ ,'&0@, ,@#D,0 #,@,$ ,@0+'5,0&H ,'&0@,$ ,@#L,0 ,#@,$#3'5 ,#'('0+,0'5'',0& &'<,$ ,'&2 &1.#.,.'.'$1$,$#$#,1',1#'/MTrk1JAZ2General MIDIPart-01[@YJ< @]}YCL@w HKMLMGGFFLLMMJLJLLLLLJGJGGLGJLJJJLLQQJJJOJHOHMGMGJJLLLLLJLJGGGLGLJJJLJLQQJJJJOOHHMMGGGLGLMMM@ML@LMJMJLMLMMOMOQMQMGGGGLHLHMGMGQQTTMMOOLJLGJGGGLLJJJJLQLJQJJJOOHHMMGGJJLLLLLLJJGGGGLLJJJLJQLQJJJJOOHHMMGGGLGLLLMJMLJLMMMOMQOQMGMGGGLLHHMGMGGGH@H/MTrk1JAZ2}J<HKL@ hCPart-02[@YYGeneral MIDIw] @@LM`MJ@JG`FGFF@HF@HLMLMJJL`O@LO`LLLLLLJJGGG@GH`JH`LJJLJJJL@ML`MO`QOQO@OM@MJJJ@JL`LM`MOOM@ML`LJ`JH@JH@JMMJJJLJLLLLLJGJGG@GH`HJ`JLLJJJLJ@ML`MO`OQQO@MO@MJJJ@JL`LM`MOOM@ML`LJ`HJ@JH@MJ@JM@GJGG@GH`JH`JLLMMMM@@LLMMJJLMLMMMOQO`OQ OM@MJ@JGGG@GH`HJ`LJHL@HJ@JM@MJ@JG`GJ@MJ`QMTQ`TQ@QO`OMMOOLLJGJGG@GH`HJ`JLLJJJLJ@ML`OM`OQQO@OM@MJJJ@JL`ML`OMOM@ML`LJ`JH@HJ@MJJMJJJLLLLLLJJGGG@HG`JH`JLLJJJJL@LM`OM`QOOQ@OM@MJJJ@JL`LM`MOMO@LM`LJ`JH@HJ@JM@MJ@GJGG@HG`HJ`JLLLLMMJJLMLMMMOOQ`OQ MO@MJ@JGGG@HG`HJ`LJLH@HJ@JM@MJ@GJGGGH@H/MTrkYJAZ2w[@Y]L*C}H*General MIDIPart-03YJ<@HK " @Q*HM*LG*C*QMGJ*H*CJQ*HM*QC*G*MH*GCJ* E*H*`JHEC*G*HGG*CC* GCG*C*`CGH*J*HG*JC*J*CGH* HJL*H*`M*Q*HLQG*MC*GCM*J*@G*MJC*@CGL*H*M*Q*HLQMG*C*J*CM*GJML*P*PLG*C*CGG*C* C*G*CG`GCH*J*HJC*G*CJ*GH* HH*L*J`HLM*Q*G*QMC*M*J*GC@G*MJC*@CGL*H*LHM*Q*QMC*G* G*GCC*`CGH*J*HJM*Q*QO*MJ*JO*L*OOLM*Q*G*QC*MH*CJ*GHJQ*M*J*O*MQOJH*L*Q*LHM*G*C*QM GC*CG*`CGJ*H*HM*JQ*QG*C*M`GCG*C*@GCJ*O*`JQ*OL*L,H,QL HH.LL.`HLM.Q.QJ.MO.OC*G*JCGC*G* CGG*C*`GJ*CH*G*C*JHCH*GJ* HJH*L*`LM*HQ*QC*G*MCGM*J*@MJG*C*@GH*CL*M*Q*HLQC*MG*CGM*J*JL*MP*PC*G*LCGG*C* GCG*C*`GJ*CH*G*C*JHCJ*GH* JHL*H*`HQ*LM*MC*G*QCGM*J*@MJG*C*@GH*CL*M*Q*HLQG*MC* GCC*G*`GH*CJ*JL*O*HLOQ*M*QMG*C*H*GCJ*JM*Q*HQMJ*O*OJH*L*M*Q*HLG*MQC* G*C*CG`GCJ*H*HJQ*M*QG*C*MGCH@H/MTrk YJAZ2General MIDIY}H*][@J<C ^@YPart-04 @C*HKwH*M*HC`MH*HE*@H*E*EH`HC*J*EC*H*JCM*HH*CHC*MJ*JCH*C*CHJ*C*C*JCH*CJ*HC*H*C*CJHCH*C*H*HM*CMJ*C*HCJJ*E*C*J*EJJCH*C*HH*CM*J*C*HMJCE*J*L*JG*EGLC*J*JH*C*CHCC*J*C*H*CJCH*HC*M*H*HCMC*J*HCE*JJ*EJJ*C*H*JCC*CHH*M*@E*MHH*@C*HJ*ECJC*H*CH*HM*MHJ*C*JCC*H*G*L*CHM*LGH*HMJ*C*JH*CC*H*CHM*HMJ*C*JC*H*CC*H*HC`CHM*H*`E*H*MH@HEJ*C*JCH*C*CE*HH*@HEE*H*@EHM*H*@HE*MH*@HC*J*ECJL*E*LEC,H,`CL.HC.@H.C.LC`CH.HM.HMC.J.JC,CH,HJ*C*CJCC*H*CHC*J*CH*JC*H*C*HCCH*HM*HMC*J*JJ*CE*EJ*C*JJCC*H*CHM*H*MJ*HC*E*J*JCJG*EL*GLJ*C*CH*JC*CC*J*HCJC*H*CHH*C*HM*CH*C*J*MHJE*CJ*EJJ*C*CH*JC*CM*H*H@MHH*E*@HEJ*C*JC*CH*L*G*CHGM*LH*MHC*J*JC*CH*HM*H*CMHJ*C*JCC*H*C*CHH*`HM*H*C`HMH*E*@EHJ*C*C*H*JCE*CHH*@E*H*HE@EHH*M*@MHH*E*@HJ*C*EJCH@H/MTrkiJAZ2 FGeneral MIDI  @Yyr( Easy drivingx/simutrans-124.5/simutrans/music/11-Stucked-Convoi.mid000066400000000000000000000512551520675460500225100ustar00rootroot00000000000000MThdMTrk By Copyright 2006 All Rights Reserved Generated by NoteWorthy ComposerQ Q QQ:Q4QyQ QQQ,Q?|QRQf:Qz#QkQQ$Q͛Q|QQ Q 'Q ?lQ WQ p<Q hQ Q YQ )Q Q Q ,*Q ImQ gZQ Q JQ ZQ -Q Q *;Q MQ q/MTrkn!Staff-4 @@nEnP@E0AnGnPAG0CnHnPCH0EnJnPEJ0Ln.LJn.JHn.HJn.JHn.HJn.JHn.HGn.GEn.ECn.CAn.A@n.@An.ACn.CAn.A@n.@>n.>@n.@An.ACn.CEn.E@n.@>n.>@n.@An.ACn.CAn.A@n.@>n.>n.>n.>@n.@An.ACn.CEn.ECn.CEn.EGn.GHn.HJn.JLn.LMn.MOn.OMn.MLn.LJn.JHn.HGn.GEn.ECn.CAn.A@n.@An.A@n.@An.ACn.CEn.EGn.GEn.ECn.CAn.A@n.@>n.>@n.@An.A@n.@>n.>n.>@n.@An.ACn.CAn.A@n.@An.A@n.@An.ACn.CEn.EGn.GEn.ECn.CAn.A@n.@Cn.CGn.GHn.HGn.GEn.ECn.CEn.ECn.CAn.ACn.CEn.EGn.GHn.HGn.GEn.ECn.CAn.ACn.CEn.EGn.GHn.HJn.JLn.LMn.MLn.LJn.JLn.LJn.JHn.HGn.GJn.JLn.LJn.JHn.HJn.JHn.HJn.JHn.HGn.GEn.ECn.CAn.A@n.@An.ACn.CAn.A@n.@>n.>@n.@An.ACn.CEn.E@n.@>n.>@n.@An.ACn.CAn.A@n.@>n.>n.>n.>@n.@An.ACn.CEn.ECn.CEn.EGn.GHn.HJn.JLn.LMn.MOn.OMn.MLn.LJn.JHn.HGn.GEn.ECn.CAn.A@n.@An.A@n.@An.ACn.CEn.EGn.GEn.ECn.CAn.A@n.@>n.>@n.@An.A@n.@>n.>n.>@n.@An.ACn.CAn.A@n.@An.A@n.@An.ACn.CEn.EGn.GEn.ECn.CAn.A@n.@Cn.CGn.GHn.HGn.GEn.ECn.CEn.ECn.CAn.ACn.CEn.EGn.GHn.HGn.GEn.EAn.ACn.CEn.EGn.GHn.HJn.JLn.LMn.MLn.LJn.JLn.LJn.JHn.HGn.GHn.HJn.JLnPL/MTrk [!Staff-3 @n.>@n^@An^ACn^CEn^E@n^@@n^@@n`@0>n.>@n^@An^ACn^CAnPA0En.ELn^LJn^JHn^HEn^EEn^E@n.@@n^@@n^@Dn.DEn.EEn^EEn^EEn^EEn^EEn^EHn^HJn^JLnL0nBnGnP;>BG0:n>nAnFnP:>AF0n.>@n^@An^ACn^CEn^E@n^@@n^@@n`@0>n.>@n^@An^ACn^CAnPA0En.ELn^LJn^JHn^HEn^EEn^E@n.@@n^@@n^@Dn.DEn.EEn^EEn^EEn^EEn^EEn^EHn^HJn^JLnL0nBnGnP;>BG0:n>nAnFnP:>AF0nBnGnP;>BG0:n>nAnFnP:>AF/MTrkU!Staff! @!n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ $n^$0n^0.n^.0n^0#n^#/n^/-n^-/n^/"n^".n^.,n^,.n^.0n.0+n^+(n^(+n^+$n`$P!n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ $n^$0n^0.n^.0n^0#n^#/n^/-n^-/n^/"n^".n^.,n^,.n^.0n.0+n^+(n^(+n^+$n`$P!n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ !n^!-n^-+n^+-n^-b+n^+(n^(+n^+&n^&2n^20n^02n^2b+n+ $n^$0n^0.n^.0n^0#n^#/n^/-n^-/n^/"n^".n^.,n^,.n^.B,n^,.n^."nP"/MTrk!Staff-1 @*n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n * *n * *n * *n * *n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n * *n * *n * *n * *n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n.**n.**n^**n^**n^*/MTrk!Staff-2 @@#n # #n # &n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # #n # #n # #n # #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # #n # #n # #n # #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^#&n & #n # &n&#n.##n^##n^##n&n^#&#n&n^#&#n&n^&0#/simutrans-124.5/simutrans/music/12-Steamin-across-the-prairies.mid000066400000000000000000002763661520675460500251520ustar00rootroot00000000000000MThd MTrk( 2006.10.06Q /MTrkv @General MIDICopyright (C) 2006 shunterJAZ2A 2006.10.06X`/MTrk%JAZ2 @General MIDIMelodyY x x2T[L] @H,24J 422T,2T2T,24J 4@4Q`2L4 2/G/2T\24H"4>4Nl42T.24A$4<4L^42@*2/N(/+R`-N+*.L-(./M"/+N<ADHJLOQTVZX]`+@(IJ(2W*24D"4>4Q\42G$2 /P,/2T\24H 4@4Q\428$2 /L\/2Z03L234N&$; L` p z242H,2/T&/ +R\+-M0.R-*./=0+M/8BDGIJMORUWYZ]+a@(C\(OP& %+2 3 58>@l %,79< = @C(@4=5* HJ0ITHO"OTt+ 6 >@ IOLT0JTLJ GD&G L^$L JT&J GQ$G L^(LJR,JGR$G F\(FEL.ECP"C1E\&4<?@2CL EC @J$@ E\0CPE&C@J*@CT0C@L*@>N$> ;L(;@\.@>R*>;M(;:T&: 9<*9?9W7A7 /=>@097?$7 4M&4 9Z07R9&74Q&4 7P.79H0:J9:;H(;>Z*>@F*@>E,>:>,:OP9@V?& %+2 3958>@D %,79< = @C(@4=5* HJO"ITH*OTt+ 6 >@ IOLT0JTLJ GD&G L^$L JT&J GQ$G L^(LJR,JGR$G F\(FEL.ECP"1CE\&4<?@2CL EC @J$@ E\0CPE&C@J*@CT0C@L*@>N$> ;L(;@\.@>R*>;M(;:T&: 9<*9?7A9W7 /=>@097?$7 4M&4 9Z07R9&74Q&4 7P.79H0:J9:;H(;>Z*>@F*@>E,>:>,:9@.?'<?@<9CTR@I@@A C C:@C@@CH@C@4CM@C@2CJ@C@ACI@C@<CI@ C@,CL@ C@.CP@C@: CM @C@<CACC4CC7@% '.2 46;=?@N>J*C?T>,?@D0CT@>Q&C>?W(?@M.@CTl *38=G QY:V SN@ 4 CEZ,EFR/ )057:=?@FEP^ECT CETECD,CER,E>1GW0 1=@   >Q0?T>HGEDG0?0EGGXBX=75;?@4N? NW Z]6`pY42T.24A$4<4L^42@*2/N(/+R`-N+*.L-(./M"/+N<ADHJLOQTVZX]`+@(IJ(2W*24D"4>4Q\42G$2 /P,/2T\24H 4@4Q\428$2 /L\/2Z03L234N&$; L` p z242H,2/T&/ +R\+-M0.R-*./=0+M/8BGDIJMORUWYZ]+a@(C\(2T,24J 4@4Q}^42L 2/G/2T\24H"4>4N? NW Z]6`pY42T.24A$4<4L^42@*2/N(/+R`-N+*.L-(./M"/+N<ADHJLOQTVZX]+`@(IJ(2W*24D"4>4Q\42G$2 /P,/2T\24H 4@4Q\428$2 /L\/2Z03L234N&$; L` p z242H,2/T&/ +R\+-M0.R-*./=0+M/8BGDIJMORUWYZ]+a@(C\(LdK\OX2K|ETELO N\JRNJPI^LXIL,K\ORL`K>OLEME OXLdK^KQ>>;@T6/&%+06;@=6,!  #(-/.013579:84,'18<? =<:<@A@0>H@ > @X\@>@,>;@,;9@,9:Z,:9N,97A,74-9/"4EZ   #&+.13578:<?$ ")06<CHMQVZ]`bdfhiklnopqstuvxyz{*|} l~}{wsnh`WP@>;L";@^*@>T>;D;@b0@>Q$> ;Q;:Z&: 9"07T9740&4 9^097X"74M 49`07P974M 47Q,79P0:N 9:;F ;>\">@R@CL<6+CG\  #&*-/24679:<=>?@<  "$%')+-/247:=ADGJLOQSUWY[]_`bcdfghiklnprtuwyz|}~~}{zywvtrpmjgd_ZUOIB;4-& J\GGW JGEFEG@GJLG8JGEDEGAGJ<GFJGEIEGAGJRGCJGECEGFGJLGCJGE@EGCGJIG5JGE?EG=GJMG8JGE<G@EJ=G GJJE:GGDGJHG=EGJ=5( LW   #&*.269<?@:  !$'*-03579;<>ACEGJLPSVY[^`bcefgij hJAfc_L[UNF=4+"G=JEDGEGAGJX G9J GEHEGCGJRG: JGEN EF^FEC(CTEC@GC:@CEGCFEC@=@C3EA CC?E @LC@ CN$@WC><@>;@;CH@NC>P@>;R;:\:;F9I;7R974D47?9F7(:Q9 9C:7M974L42G2/G,/2T,24J 4@4Q}^42L 2/G/2T\24H"4>4N? NW Z]6`p4Y2T.24A$4<4L^42@*2/N(/+R`-N+*.L-(./M"/+N<ADHJLOQTVXZ]`+@(IJ(2W*24D"4>4Q\42G$2 /P,/2T\24H 4@4Q\428$2 /L\/2Z03L234N&$; L` p z242H,2/T&/ +R\+-M0.R-*./=0+M/8BDGIJMORUWYZ]+a(C@\(OP& %+2 3 58>@l %,79< = @C(@4=5* HJ0ITHO"OTt+ 6 >@ IOLT0JTLJ GD&G L^$L JT&J GQ$G L^(LJR,JGR$G F\(FEL.ECP"1CE\&4<?@2CL EC @J$@ E\0CPE&C@J*@CT0@LC*@>N$> ;L(;@\.@>R*>;M(;:T&: 9<*9?9W7A 7 /=>@097?$7 4M&4 9Z07R9&74Q&4 7P.79H0:J9:;H(;>Z*>@F*@>E,>:>,:9@8'<?@<9CTR@I@@A C C:@C@@CH@C@4CM@@2CCJ@C@ACI@C@<CI@ C@,CL@ C@.CP@C@: CM @C@<CACC4CC7@% '.2 46;=?@N>J*C>?T,?@D0CT@>Q&C>?W(?@M.@CTl *38=G QY:V SN@ 4 CEZ,EFR/ )057:=?@EPF^ECT CETECD,CER,E>GW10 1=@   >Q0?T>HGG`ED?0E =GG3G~ERC<C EG@HGGJXHJL\LMZMO\OQZSZQTZSVZTXXV,V\ V@V`.VSWSBST0RZSR@R`.RQPQHQd@Q OZ4O\LdLDL^XLJ` J@J\.JLW*LfR`S` X\R`S XR^QRXTR,QO`OBQ`0Q0Q`.QOZ O@O`0L\OLHLbLO^(O8O^.OQ`0RTQ R SX*SVZXVTbUdXbT8XU2W`[\X`W8X[ QPQZ^VXXZVQTQXTU\S\ S:UQWR\*R`XQXXO^TO L\XLBO^X`TOXLT L$X\O^VXO LRL(X^OZRXO LT L$XWO\XOXLTL"O^XZ`LXXOLPO`XbPXO LW L$X^O^RXO LTL"XXObNXOLTL"O\XT`LTXOL&X\O`\XL`OLJD HPJ HGR EZG CREATCAK\LdOX2K~ETE LO N\JRNJPLXI^IL,ORL`K\K>LOEME K^OXLdK4N? NW Z]6`p4Y2T.24A$4<4L^42@*2/N(/+R`-N+*.L-(./M"/+N<ADHJLOQTVXZ]+`(I@J(2W*24D"4>4Q\42G$2 /P,/2T\24H 4@4Q\428$2 /L\/2Z03L234N&$; L` p z242H,2/T&/ +R\+-M0.R-*./=0+M/8BGDIJMORUWYZ+]a@(C\(2T,24J 4@4Q}^2L4 2/G/2T\24H"4>4N? NW Z]6`p4Y2T.24A$4<4L^42@*2/N(/+R`-N+*.L-(./M"/+N<ADHJLOQTVZX]`+(I@J(2W*24D"4>4Q\42G$2 /P,/2T\24H 4@4Q\428$2 /L\/2Z03L234N&$; L` p z242H,2/T&/ (e,BGDIJMORUWYZ]a@0(/MTrk'EWJAZ2[2General MIDI1YYx "@]2 @HKJ<}C "x[2]2@HKJ<CWB-9-B9 9#B9B E&E B)B 9-B-B9 9!B 9B E'E B$B C-;-C; ;!CC; G(G CC ;-C-C; C;%C; G)G C$C H-@-@H @%H @H @&@ H)H H-@-@H H @%H@ @'@ H$H ;-C&C; ;%C!;C G(G CC C-;-C; C!;%C; G)G C$C H-@-H@ @#HH@ @&@ H)H @-H-H@ @!H H@ @'@ H$H ;-C-;C ;!CC; G(G CC C-;-;C ;%CC; G)G C$C H-@-@H @%H @H @&@ H)H H-@-H@ H @%H@ @'@ H$H ;-C&;C C!;%;C G(G CC ;-C-;C C!;%C; G)G C$C @-H-@H H@#@H @&@ H)H H-@-@H H @!@H @'@ H$H ;-C-;C ;!CC; G(G CC C-;-;C ;%CC; G)G C$C @-H-@H @%H H@ @&@ H)H H-@-@H @%H H@ @'@ H$H C&;-;C C!;%C; G(G CC C-;-C; C!;%;C G)G C$C @-H-@H @#HH@ @&@ H)H @-H-H@ @!H H@ @'@ H$H C-;-;C ;!C;C G(G CC C-;-C; ;%CC; G)G C$C H-@-@H H @%H@ @&@ H)H H-@-H@ @%H @H @'@ H$H ;-C&;C C!;%C; G(G CC C-;-C; ;%C!;C G)G C$C @-H-@H H@#H@ @&@ H)H @-H-H@ H @!@H @'@ H$H H-@-@H H@!@H @(@ HH @-H-@H H@%@H @)@ H$H @-H-H@ @#H@H @&@ H)H H-@-H@ @!H H@ @'@ H$H @-H-H@ @!H@H @(@ HH H-@-@H @%H@H @)@ H$H @-H-@H H@#H@ @&@ H)H @-H-@H H @!@H @'@ H$H H-@-@H @!H@H @(@ HH H-@-@H @%H@H @)@ H$H ;-C-C; C ;%;C G&G C)C C-;-;C ;%C ;C G'G C$C 7-@&@7 7%@!7@ C(C @@ 7-@-@7 @!7%7@ C)C @$@ <-E-E< <#EE< H&H E)E <-E-<E E -E-E> E>#>E >&> E)E E->-E> >!E E> >'> E$E >-5-5> 5!>5> A(A >> 5->->5 >5%>5 A)A >$> F->->F >#FF> >&> F)F F->-F> F >!F> >'> F$F F->-F> >!FF> >(> FF F->->F >%FF> >)> F$F E->-E> >%E >E >&> E)E E->-E> >%E E> >'> E$E 7-@&@7 7%@!@7 C(C @@ @-7-@7 7%@!@7 C)C @$@ >-E-E> >#EE> >&> E)E >-E-E> >!E >E >'> E$E <-E-<E E2>C8C H<HC0C H2H CxJ<HK[2@ "]2WHA@A@H @2H-H@ @4@ H8H @AHA@H H-@0@H @5@ H1H @=H=H@ H*@-H@ @3@ H'H @=H=@H @2H*@H @5@ H/H @9H9H@ H(@.H@ @/@ H3H @9H9@H H(@.H@ @0@ H,H H1@9@H @.H*@H @1@ H%H H9@9@H @.H*@H @3@ H-H H9@9@H @,H(H@ @/@ H3H @9H9@H @*H(@H @0@ H,H H9@9@H @*H(@H @1@ H%H @9H9@H H(@.@H @3@ H-H ;9C9C; ;.C(C; G/G C3C C9;9C; ;.C(;C G0G C,C C1;9;C ;.C*;C G1G C%C ;9C9C; C*;.C; G3G C-C @9H9H@ @,H(@H @/@ H3H H9@9H@ H(@*@H @0@ H,H @9H9@H @*H(@H @1@ H%H H9@9@H H(@.H@ @3@ H-H @9H9@H @.H(@H @/@ H3H H9@9@H @.H(@H @0@ H,H @9H1H@ H*@.H@ @1@ H%H H9@9@H H*@.H@ @3@ H-H H9@9@H H(@,@H @/@ H3H H9@9H@ @*H(@H @0@ H,H H9@9H@ @*H(H@ @1@ H%H @9H9H@ H(@.@H @3@ H-H @9H9@H @,H(@H @/@ H3H H9@9H@ @*H(@H @0@ H,H H9@9@H @*H(H@ @1@ H%H @9H9@H @.H(@H @3@ H-H H9@9@H @,H(@H @/@ H3H H9@9H@ @*H(H@ @0@ H,H @9H9H@ @*H(@H @1@ H%H @9H9@H H(@.H@ @3@ H-H @9H9H@ @.H(H@ @/@ H3H H9@9@H @.H(H@ @0@ H,H @9H1@H @.H*@H @1@ H%H H9@9H@ H*@.@H @3@ H-H H9@9@H @,H(@H @/@ H3H @9H9@H H(@*H@ @0@ H,H @9H9H@ @*H(H@ @1@ H%H @9H9@H @.H(@H @3@ H-H H9@9H@ H(@.H@ @/@ H3H H9@9@H @.H(H@ @0@ H,H @9H1@H H*@.H@ @1@ H%H @9H9H@ @.H*@H @3@ H-H @9H9@H @,H(@H @/@ H3H @9H9H@ @*H(@H @0@ H,H H9@9H@ @*H(@H @1@ H%H H9@9@H H(@.H@ @3@ H-H H9@9@H H(@.@H @/@ H3H @9H9@H @.H(@H @0@ H,H @9H1@H H*@.H@ @1@ H%H H9@9@H H*@.H@ @3@ H-H A999A9 A(9,9A E/E A3A 99A9A9 A(9*9A E0E A,A A999A9 9*A(9A E1E A%A A9999A 9.A(A9 E3E A-A @9H9H@ @.H(@H @/@ H3H @9H9@H @.H(H@ @0@ H,H "@xJ<CW[2HK]299B99B 9,B(9B E/E B3B 99B9B9 9*B(9B E0E B,B 99B9B9 9*B(9B E1E B%B B9999B B(9.9B E3E B-B C9;9C; ;.C(C; G/G C3C ;9C9;C C(;.C; G0G C,C C1;9C; ;.C*C; G1G C%C C9;9C; C*;.;C G3G C-C @9H9@H H(@,@H @/@ H3H @9H9H@ @*H(H@ @0@ H,H C9;9;C ;*C(;C G1G C%C C9;9C; C(;.;C G3G C-C @9H9@H H(@.@H @/@ H3H @9H9@H H(@.H@ @0@ H,H ;9C1;C C*;.C; G1G C%C ;9C9C; ;.C*;C G3G C-C H9@9@H H(@,@H @/@ H3H @9H9@H @*H(H@ @0@ H,H C9;9;C C(;*;C G1G C%C C9;9;C ;.C(C; G3G C-C @9H9H@ H(@.@H @/@ H3H H9@9H@ @.H(@H @0@ H,H C1;9;C C*;.C; G1G C%C ;9C9C; C*;.;C G3G C-C @9H9@H @,H(@H @/@ H3H @9H9H@ @*H(@H @0@ H,H C9;9;C ;*C(;C G1G C%C ;9C9C; ;.C(;C G3G C-C H9@9H@ @.H(@H @/@ H3H @9H9@H H(@.@H @0@ H,H ;9C1;C ;.C*;C G1G C%C ;9C9C; C*;.;C G3G C-C H9@9@H @,H(H@ @/@ H3H H9@9H@ H(@*H@ @0@ H,H C9;9C; ;*C(C; G1G C%C C9;9;C ;.C(;C G3G C-C @6H6}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0@H@/MTrk3WJAZ2J<General MIDI2YYx ^[2]2@ @HK}C ^[2]2x@HKJ<CW<"< B"B HEBBHE EHBBEH > C"C >GC>GC CG>>GC >> C C CG>>GC GC>>GC C"C H"H H@C@CH HC@C@H C!C H!H @HCHC@ @CHC@H >> C"C G>C>GC CG>>CG >> C C CG>CG> >GCCG> C"C H"H @CHHC@ C@HC@H C!C H!H @CH@CH H@CH@C >> C"C GC>>GC >CG>GC >> C C CG>GC> C>GCG> C"C H"H C@HHC@ HC@HC@ C!C H!H HC@@CH CH@HC@ >> C"C CG>G>C >GC>GC >> C C >CGG>C CG>C>G C"C H"H H@CH@C @CH@HC C!C H!H @CH@HC HC@H@C >> C"C GC>C>G >GCCG> >> C C G>C>CG GC>G>C C"C H"H CH@@CH H@CHC@ C!C H!H @HC@CH HC@H@C >> C"C C>GC>G >CGGC> >> C C GC>>CG >CGGC> C"C H"H H@CH@C C@H@HC C!C H!H @CH@HC CH@@CH >> C"C CG>CG> GC>C>G >> C C GC>C>G CG>>CG C"C H"H @HC@CH H@CHC@ C!C H!H @CHHC@ H@CHC@ >> C"C C>GC>G >GCCG> >> C C >GCC>G CG>>GC C"C H"H HC@C@H C@HCH@ C!C H!H H@C@CH @CHCH@ CC H"H H@C@CH C@HH@C CC H H HC@@CH H@CH@C C"C H"H CH@CH@ CH@@HC C!C H!H HC@CH@ @CH@HC CC H"H H@CC@H CH@H@C CC H H CH@CH@ HC@HC@ C"C H"H C@H@CH @CHCH@ C!C H!H H@C@CH CH@C@H CC H"H CH@C@H H@CH@C CC H H @CHCH@ H@CCH@ >"> C"C CG>>GC G>CG>C >!> C!C GC>CG> G>CC>G ;; @"@ @CGG@C @GC@GC ;; @ @ C@G@GC @GCC@G @"@ E"E @EHH@E @EHH@E @!@ E!E H@EEH@ @EHH@E @@ E"E E@HEH@ E@H@HE @@ E E H@EHE@ EH@@EH @"@ E"E H@E@EH @EH@EH @!@ E!E H@EE@H EH@EH@ @@ E"E HE@H@E @EH@EH @@ E E E@HE@H @HEE@H C"C H"H H@CHC@ H@C@HC C!C H!H C@HH@C C@HHC@ CC H"H HC@@CH C@H@HC CC H H HC@H@C HC@CH@ <"< B"B EBHBHE EBHBEH "> C"C CG>C>G >CGG>C >!> C!C C>GC>G CG>>CG >> C"C G>CG>C >GCGC> >> C C >CG>GC CG>>CG @"@ E"E >E@@E> @>EE>@ @!@ E!E >E@@E> >E@>@E 99 >"> >AE>AE AE>>AE 99 > > E>AE>A >EAEA> A"A F"F FA>AF> AF>>FA A!A F!F A>FAF> A>FFA> AA F"F >FA>FA F>AAF> AA F F F>AA>F >AFA>F @"@ E"E @E>>E@ >E@E>@ @!@ E!E >E@@>E @E>@>E ;; @"@ G@CG@C @CG@GC ;; @ @ C@GGC@ CG@C@G @"@ E"E >E@E@> >E@@>E @!@ E!E E>@E>@ @E>@>E @@ E"E @HEH@E H@EEH@ @@ E E H@EE@H @EH@EH 8"8 ="= =DA=AD DA=A=D 8!8 =!= AD=AD= =ADD=A 88 ="= A=DAD= D=AA=D 88 = = DA==DA A=DAD= 8"8 ="= DA=A=D D=ADA= 8!8 =!= AD=AD= =ADD=A 88 ="= AD=DA= DA=AD= 88 = = D=ADA= AD=AD= C"C H"H C@HC@H @CHH@C C!C H!H CH@C@H HC@@CH WC]2HK[2xJ< ^@<*< A*A E$A"H"AHE H A E"HAE <(< A(A A"H%E%AEH A H E!AHE *> C*C G">%C#>GC > C G"CG> >(> C(C C"G%>%C>G C G"> GC> *> C*C C"G$>"C>G G"> C CG> >(> C(C C"G%>%>CG > C G!G>C >!> C*C C$>%G%>GC C > G!G>C >$> C(C C%G">%G>C C>G"G>C C*C H*H C%H#@"@CH C H @"H@C C(C H(H H"C%@%H@C @"C H HC@ *> C*C C">"G$>GC G"C > >CG >(> C(C G%C">%CG> G!> C GC> >!> C*C >%G%C$C>G C > G!CG> >$> C(C G">%C%CG> CG">>CG C*C H*H @"C%H#C@H C H @"CH@ C(C H(H @%H"C%@CH C H @"@CH >!> C*C C%>%G%>GC G">!C GC> >$> C(C >%G%C%GC> >!C!G"GC> C*C H*H @$H"C"C@H @"H C C@H C(C H(H H"C%@%C@H @!H C H@C >!> C*C >%C$G%>CG G!> C C>G >$> C(C G">%C%>GC CG">G>C C*C H*H H"@$C"@CH @"H C HC@ C(C H(H H"C%@%H@C H C @!H@C >!> C*C C$>%G%>CG G!C > CG> >$> C(C >%C%G">GC >CG"CG> C*C H*H @$H"C"@CH C H @"C@H C(C H(H @%H"C%@CH H C @!CH@ C!C H*H H$@%C%@HC H @!C @CH C$C H(H C%@"H%@HC C@"H@HC C*C H*H @"H#C%@CH H @"C HC@ C(C H(H C%@%H"HC@ @"C H H@C C!C H*H H%C%@%C@H @"H C!HC@ C$C H(H H%C%@%CH@ C!H!@"H@C 8*8 =*= ="D"A$=AD = A"D A=D 8(8 =(= ="A%D%D=A = A!D D=A *> C*C C#G">%GC> > G"C >CG >(> C(C C">%G%C>G G"C > C>G >!> C*C G%>%C%C>G C G">!G>C >$> C(C >%G%C%G>C >!G"C!GC> >*> C*C G$>"C">CG G"C > C>G >(> C(C C"G%>%G>C C G!> C>G >!> C*C C$G%>%G>C G!C > >CG >$> C(C >%C%G"C>G C>G"CG> >*> C*C C">"G$>CG > G"C CG> >(> C(C >%G%C"CG> G!> C >CG >!> C*C G%>%C$>GC G!> C >CG >$> C(C >%G"C%GC> >G"CGC> >*> C*C C"G$>">CG C > G"CG> >(> C(C G%C">%>CG G!C > >GC >!> C*C >%G%C$GC> C > G!CG> >$> C(C >%C%G"C>G G"C>C>G >*> C*C G">%C#GC> C G"> GC> >(> C(C C"G%>%GC> C G"> G>C >!> C*C >%G%C%GC> >!C G"CG> >$> C(C >%C%G%GC> G">!C!C>GPC,CPC!@!H$C@HPC+CPH$@!C!@CH @%@ C.C H)C)@)HC@ C#@%H&@HCPH,H @-H-@H @)H*@H H2H C8CH.H C1C]2x[2 ^@HKCJ<WC/C H/H @(H&C&H@C C#H#@&HC@ C-C H-H H&C)@)HC@ @%C#H#@CH C#C H,H H&@'C'CH@ H!@#C!@CH C%C H*H @$C'H'@HC @$C!H HC@ C*C H*H C%@"H#@HC C @"H @CH C(C H(H H"@%C%HC@ @"H C CH@ C!C H*H C%H%@%HC@ H C!@"CH@ C$C H(H C%@%H%C@H C!H!@"CH@ C*C H*H @$C"H"H@C @"C H CH@ C(C H(H H"@%C%HC@ H C @!@CH C!C H*H @%H$C%HC@ C @!H @CH C$C H(H C%H%@"C@H C@"HH@C >*> C*C G">%C#>GC C > G">CG >(> C(C C"G%>%CG> > G"C >GC >!> C*C G%>%C%CG> C >!G">GC >$> C(C G%>%C%GC> >!G"C!CG> C*C H*H @$C"H"CH@ @"C H C@H C(C H(H C%@%H"CH@ @!C H HC@ C!C H*H H$@%C%@HC H C @!CH@ C$C H(H H%@"C%H@C C@"HC@H C*C H*H @"H#C%C@H @"H C HC@ C(C H(H @%C%H"@HC H @"C C@H C!C H*H C%@%H%@HC C!@"H @CH C$C H(H @%H%C%@CH C!H!@"@HC C*C H*H H"C"@$H@C @"C H HC@ C(C H(H @%C%H"CH@ @!C H C@H C!C H*H @%C%H$@HC @!H C CH@ C$C H(H @"H%C%H@C @"HC@HC C*C H*H H"@$C"H@C C H @"@CH C(C H(H H"@%C%CH@ @!C H HC@ C!C H*H H$@%C%HC@ @!C H HC@ C$C H(H C%@"H%@HC H@"C@HC C*C H*H C"@$H"CH@ @"C H HC@ C(C H(H H"C%@%H@C @!H C H@C C!C H*H @%C%H$C@H H C @!@HC C$C H(H @"C%H%@CH CH@"@CH C*C H*H @"C%H#CH@ H C @"@CH C(C H(H C%H"@%H@C H @"C @HC C!C H*H @%C%H%@HC C!H @"@HC C$C H(H H%@%C%HC@ @"H!C!CH@ C*C H*H H"@$C"@CH C @"H @CH C(C H(H C%@%H"CH@ C H @!@HC C!C H*H H$@%C%@CH C H @!CH@ C$C H(H @"C%H%H@C CH@"@HC C*C H*H H#C%@"@HC C H @"@HC C(C H(H H"C%@%C@H C @"H H@C C!C H*H H%@%C%@CH @"H C!H@C C$C H(H @%C%H%@HC C!H!@"@HC C*C H*H H"@$C"@HC H @"C C@H C(C H(H C%@%H"H@C @!C H HC@ C!C H*H H$C%@%H@C @!C H HC@ C$C H(H C%@"H%@HC HC@"@CH C*C H*H @"C%H#CH@ @"H C H@C C(C H(H @%H"C%@CH @"C H HC@ C!C H*H H%@%C%C@H @"C!H CH@ C$C H(H C%@%H%H@C H!C!@"CH@ <*< A*A A"H"E$AHE A H E"EAH <(< A(A H%A"E%AEH E!A H HAE *> C*C >%G"C#G>C C > G"CG> >(> C(C >%C"G%C>G > C G"G>C >!> C*C G%>%C%>GC >!C G"GC> >$> C(C G%>%C%>GC C!>!G"C>G C*C H*H @$H"C"@HC H @"C @HC C(C H(H H"@%C%H@C H @!C C@H >!> C*C G%C$>%CG> G!C > >CG >$> C(C C%>%G"C>G >G"C>CG C*C H*H C%@"H#@CH @"H C C@H C(C H(H @%C%H"C@H H @"C C@H >!> C*C C%>%G%>CG >!C G">GC >$> C(C >%G%C%C>G G"C!>!CG> C*C H*H H"C"@$C@H @"H C CH@ C(C H(H C%@%H"H@C @!H C CH@ >!> C*C G%>%C$>GC C G!> C>G >$> C(C C%>%G">CG CG">>CG C*C H*H H#C%@"CH@ @"C H HC@ C(C H(H C%H"@%HC@ C @"H C@H >!> C*C G%>%C%G>C C >!G">CG >$> C(C C%G%>%CG> >!C!G"GC> C*C H*H @$C"H"C@H @"C H HC@ C(C H(H @%C%H"@CH H C @!H@C >!> C*C G%C$>%CG> C > G!GC> >$> C(C C%G">%GC> >G"CCG> C*C H*H @"H#C%H@C @"C H @HC C(C H(H C%H"@%HC@ H @"C C@H >!> C*C C%>%G%G>C >!C G"C>G >$> C(C >%G%C%>CG C!G">!>CG C*C H*H H"@$C"H@C C @"H CH@ C(C H(H H"C%@%CH@ C H @!@CH >!> C*C >%G%C$G>C C G!> >GC >$> C(C >%C%G"C>G >G"CC>G C"}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0C@/MTrk>'JAZ2[AGeneral MIDI3YYx @HK]A@ @J<}C @[A]A@xHKJ<C'@*G **:@@ **G@@* @*:@* @@*G *@@*: **G@@* @@*:* @+G@ ++:@@ +@+G@+ @+:@+ @+G@ +@@+: ++G@@+ @+:@+ $G@@ $@$:@ $@0G@0 0:@@0 @$G@ $@@$: $@@0G0 @0:@0 @+G@ ++:@@ +@+G@+ @+:@+ @+G@ ++:@@ +@+G@+ +:@@+ $G@@ $@$:@ $0G@@0 @@0:0 @@$G $@@$: $@0G@0 0:@@0 +G@@ +@@+: +@+G@+ @+:@+ @@+G ++:@@ ++G@@+ @+:@+ @$G@ $$:@@ $0G@@0 @0:@0 $G@@ $@@$: $0G@@0 0:@@0 +G@@ +@@+: +@@+G+ @@+:+ +G@@ +@+:@ ++G@@+ @@+:+ @@$G $$:@@ $@@0G0 @0:@0 $G@@ $$:@@ $@0G@0 @@0:0 +G@@ ++:@@ +@+G@+ @+:@+ +G@@ +@+:@ +@+G@+ @+:@+ @$G@ $@@$: $0G@@0 @@0:0 @@$G $@$:@ $0G@@0 @@0:0 @+G@ +@@+: ++G@@+ @+:@+ @@+G +@+:@ +@+G@+ @+:@+ @$G@ $@@$: $@0G@0 0:@@0 @@$G $@$:@ $@0G@0 @@0:0 +G@@ +@+:@ +@+G@+ +:@@+ @@+G ++:@@ +@@+G+ +:@@+ @@$G $$:@@ $0G@@0 0:@@0 @$G@ $@$:@ $0G@@0 @@0:0 @@+G +@+:@ +@+G@+ @@+:+ +G@@ +@+:@ +@@+G+ @@+:+ $G@@ $$:@@ $0G@@0 0:@@0 $G@@ $@$:@ $@0G@0 @@0:0 $G@@ $@$:@ $@@0G0 @@0:0 @$G@ $@@$: $0G@@0 0:@@0 @$G@ $@@$: $0G@@0 @0:@0 @@$G $$:@@ $@0G@0 0<@@0 @@$G $@@$; $0G@@0 @0:@0 @$G@ $$:@@ $@@0G0 @@0:0 @$G@ $$:@@ $0G@@0 0:@@0 $G@@ $@@$: $@0G@0 @@0:0 $G@@ $@@$: $0G@@0 @0:@0 $G@@ $@@$: $@0G@0 @@0:0 +G@@ +@@+: +@@+G+ +:@@+ @+G@ +@+:@ +@@+G+ @+:@+ (G@@ (@(:@ ((G@@( (:@@( (G@@ (@@(: ((G@@( @(:@( @@!G !!:@@ !@@-G- -:@@- @@!G !@@!: !-G@@- @@-:- !G@@ !@@!: !-G@@- @-:@- !G@@ !@@!: !@@-G- @@-:- !G@@ !!:@@ !-G@@- @@-:- @@!G !@!:@ !@@-G- @@-:- @@!G !!:@@ !@@-G- @-:@- @!G@ !@@!: !@@-G- @-:@- @@$G $@$:@ $@@0G0 @0:@0 @@$G $@@$: $0G@@0 0:@@0 @$G@ $$:@@ $@0G@0 @0:@0 @@$G $$:@@ $@0G@0 0:@@0 *G@@ *@*:@ **G@@* @@*:* *G@@ *@*:@ **G@@* @@*:* @@*G *@*:@ *@*G@* *:@@* @@*G *@@*: *@@*G* @@*:* @@+G +@+:@ +@@+G+ +:@@+ +G@@ +@+:@ ++G@@+ +:@@+ @+G@ +@+:@ +@@+G+ +:@@+ @+G@ +@@+: +@+G@+ @@+:+ @!G@ !@!:@ !@@-G- @-:@- !G@@ !@@!: !-G@@- @-<@- &G@@ &@@&; &&G@@& &:@@& @@&G &@@&: &&G@@& &:@@& @@"G "@@": "@@.G. @.:@. @@"G "@":@ "@@.G. .:@@. @@"G "@@": "@.G@. .:@@. "G@@ "":@@ ".G@@. @.:@. !G@@ !@@!: !@@-G- -:@@- @@!G !!:@@ !@-G@- @-:@- @@(G ((:@@ (@@(G( (:@@( @(G@ (@@(: ((G@@( @(:@( @@!G !!:@@ !@-G@- -:@@- @@!G !!:@@ !@-G@- @@-:- @!G@ !@@!: !@-G@- @@-:- !G@@ !@@!: !@-G@- @@-:- @%G@ %@%:@ %@%G@% @%:@% %G@@ %@@%: %@@%G% %:@@% @@%G %%:@@ %@%G@% %:@@% %G@@ %@%:@ %%G@@% @%:@% %G@@ %%:@@ %@@%G% @%:@% @@%G %@@%: %@%G@% %:@@% @%G@ %@@%: %@%G@% @%:@% %G@@ %@%:@ %@@%G% @@%:% $G@@ $@$:@ $0G@@0 @0:@0 $G@@ $@$:@ $@@0G0 @@0:0 @J<[AHKx]A'C @@)W )@@)F ))W@@) )F@@) @)W@ )@)F@ )@@)W) @)F@) @)W@ ))F@@ ))W@@) @)F@) @)W@ )@)F@ )@@)W) @@)F) +W@@ +@@+F ++W@@+ @@+F+ @@+W ++F@@ ++W@@+ @+F@+ )W@@ ))F@@ )@)W@) @@)F) )W@@ )@@)F )@@)W) @)F@) @+W@ ++F@@ ++W@@+ +F@@+ @+W@ ++F@@ +@+W@+ +F@@+ @@+W +@@+F ++W@@+ +F@@+ @@+W +@@+F +@+W@+ @+F@+ $W@@ $$F@@ $@0W@0 @@0F0 $W@@ $@@$F $0W@@0 0F@@0 @)W@ )@)F@ )@@)W) @)F@) @)W@ )@@)F ))W@@) @)F@) @+W@ +@@+F +@+W@+ @@+F+ +W@@ +@+F@ +@+W@+ +F@@+ @@+W +@@+F +@+W@+ +F@@+ +W@@ +@@+F +@@+W+ @+F@+ $W@@ $@$F@ $0W@@0 @@0F0 @$W@ $@$F@ $@0W@0 @0F@0 @@+W ++F@@ +@@+W+ @+F@+ @@+W +@+F@ +@@+W+ @+F@+ @$W@ $$F@@ $@0W@0 0F@@0 @@$W $$F@@ $0W@@0 @0F@0 @@+W +@+F@ ++W@@+ @+F@+ +W@@ +@@+F +@@+W+ @@+F+ $W@@ $@@$F $@@0W0 @@0F0 $W@@ $$F@@ $@@0W0 @@0I0 +W@@ ++H@@ +@@+W+ @@+F+ +W@@ ++F@@ +@+W@+ @@+F+ @@$W $@@$F $@@0W0 @@0F0 $W@@ $@$F@ $@0W@0 @@0F0 @$W@ $@@$F $@@0W0 0F@@0 @@$W $@@$F $@0W@0 @@0F0 $W@@ $$F@@ $@@0W0 0F@@0 @$W@ $@$F@ $0W@@0 0F@@0 $W@@ $@@$F $0W@@0 @0F@0 @$W@ $$F@@ $@0W@0 @@0F0 @@%W %%F@@ %@@%W% %F@@% @%W@ %@%F@ %%W@@% @%F@% )W@@ ))F@@ )@)W@) @)F@) )W@@ )@@)F )@)W@) )F@@) @@$W $$F@@ $$W@@$ @@$F$ @@$W $$F@@ $@$W@$ @$F@$ @@!W !@@!F !-W@@- @-F@- !W@@ !@!F@ !@@-W- -F@@- @)W@ )@)F@ )@@)W) )F@@) @)W@ )@)F@ )@@)W) )F@@) )W@@ )@)F@ )@@)W) )F@@) @)W@ ))F@@ )@)W@) @)F@) @+W@ +@+F@ ++W@@+ @+F@+ +W@@ +@@+F +@@+W+ @+F@+ +W@@ +@+F@ +@+W@+ @+F@+ +W@@ +@+F@ +@+W@+ @+F@+ @+W@ ++F@@ ++W@@+ @@+F+ +W@@ +@+F@ +@+W@+ @+F@+ @+W@ ++F@@ ++W@@+ @+F@+ @@+W ++F@@ +@@+W+ +F@@+ +W@@ ++F@@ +@+W@+ +F@@+ +W@@ +@+F@ +@+W@+ @+I@+ +W@@ +@@+H +@@+W+ @@+F+ +W@@ ++F@@ ++W@@+ +F@@+ @+W@ +@@+F +@@+W+ @@+F+ @@+W ++F@@ +@+W@+ @@+F+ +W@@ ++F@@ +@+W@+ @+F@+ @+W@ +@+F@ +@@+W+ @+F@+ @@+W +@@+F +@+W@+ +F@@+ @@+W ++F@@ +@+W@+ @@+F+ @@+W +@@+F +@@+W+ @@+F+ +W@@ +@+F@ ++W@@+ +F@@+ @$\@ $@$J@ $<\@@< #D,- ,2#a-?-#, 1J#g+@8DF>F#18+ F?+@#k+#F C$8.8#Q88C# ,$, C".2F+.CF 8.F+F8 #Q84&>,$D(,&8#D 8/,$,8 F+.2.F F+C%FC 8,$82#Q8,#8 C%,$,C F+.2.F 8/CF+C8F &>,$D'#Q#D,& ,$, F+8.D&.2D.F8 F+F ,$8#QD8.8#,D8 ,$, C".28%F+FC8. F+8.F8 #Q84,$&>C$C#&,8 8/,$,8 D$.2F+FD. C F+CF 8#Q82,$K-88,K# K+C$,$CK, K+.2F+.FK K+DF+8/K8FD #Q,$C$K+&>&#K,C ,$K+#D,K# #GF+K+.28.F8.K# F+&>#IK+F&#K #Q8.8C$88C# ,$, F+C".2.FC 8.F+F8 84#Q,$&>D(D#8&, ,$8/,8 .2F+F. C%F+FC ,$#Q88288,# C%,$C, F+.2.F 8/CF+C8F ,$D'&>#Q#&D, ,$, .2D&F+8..FD8 F+F 8,$#QD8.8D#8, ,$, C"F+8%.2F8.C F+8.8F ,$&>#Q84C$&#,C8 ,$8/,8 D$F+.2.DF C F+CF 8#Q82,$K-88,K# K+C$,$CK, K+.2F+.FK DF+K+8/8KDF &>,$#QK+C$K&#C, ,$K+#DK#, F+K+8..2#GF8#.K &>F+#IK+KF&# 8.8C$#Q88C# ,$, .2C"F+.CF F+8.8F ,$#QD(84&>D8#,& 8/,$,8 F+.2.F C%F+CF #Q882,$8,#8 C%,$,C .2F+F. F+8/C8FC &>,$D'#Q#D,& ,$, 8..2F+D&FD8. F+F 88.D,$#Q88D,# ,$, .28%C"F+F.C8 8.F+8F ,$C$#Q84&>&,8#C 8/,$,8 F+D$.2.FD F+C FC ,$#Q82K-888K,# K+C$,$K,C F+.2K+K.F K+DF+8/K8FD C$#Q&>,$K+KC,#& ,$#DK+,#K #GF+.2K+8.F8K.# F+&>K+#IF&K# #Q88.C$8#8C ,$, F+.2C"CF. F+8.8F #QD(&>84,$,D#8& 8/,$,8 .2F+.F F+C%FC 82,$#Q88,8# C%,$C, .2F+.F C8/F+C8F #QD'&>,$,#D& ,$, F+.2D&8..DF8 F+F #Q,$8D8#,D ,$, C$8'.5F.8C.F &B82F/F8& C(8:,(#Y,C#8 86,)8, &DF3.<D+F.&D C'F4FC 8K78?#`,-8K8,# K8,.C.KC, #^K9F9.B#.KF 8?&OD'K:F:F&K8D K<,1#gC2#,KC ,2&?K=K&, .IF>8DK?&Q8F.K& K@F?FK7M8.C$8#Q78C8# ,$, .2F+C"F.C F+8.8F D(&>,$84#Q,&D#8 8/,$,8 F+.2F. C%F+CF ,$#Q88288#, C%,$,C F+.2.F 8/F+CF8C &>D'#Q,$,#D& ,$, F+.2D&8.D8F. F+F 8#QD8.,$8#D8, ,$, .2F+C"8%8.CF 8.F+8F ,$C$#Q&>848,&#C ,$8/8, .2F+D$D.F C F+CF ,$#QK-8288K8,# ,$C$K+,KC F+K+.2.KF 8/K+F+D8DFK C$K+&>,$#Q#C,K& K+,$#DK,# K+8..2#GF+8F#.K K+&>F+#IFK&# 88.C$#Q88C# ,$, C".2F+CF. 8.F+8F D(#Q&>,$848#D,& 8/,$,8 F+.2F. F+C%FC #Q82,$888#, ,$C%,C .2F+F. F+8/C8FC ,$#QD'&>&D#, ,$, D&8.F+.2DF8. F+F #Q8.,$D88D,#8 ,$, C"F+8%.2F8.C 8.F+F8 C$&>#Q,$84&#C8, ,$8/,8 D$.2F+FD. C F+CF K-8#Q82,$8#8K, ,$C$K+K,C .2F+K+K.F D8/F+K+KFD8 C$#Q&>K+,$KC&,# ,$#DK+#K, .28.K+F+#G.K#F8 K+#I&>F+#K&F #Q8.C$88C8# ,$, F+C".2FC. 8.F+F8 ,$D(84#Q&>8#D,& ,$8/8, .2F+F. C%F+CF #Q82,$888#, C%,$,C .2F+F. F+C8/CF8 &>D'#Q,$,#D& ,$, D&8.F+.2F.D8 F+F D#Q8.,$88#D,8 ,$, F+.28%C"CF8. F+8.F8 C$84#Q&>,$,C&#8 ,$8/8, F+D$.2.FD C F+CF K-#Q82,$888,K# ,$C$K+,KC .2F+K+KF. 8/F+K+D8DKF K+&>#QC$,$,KC&# ,$K+#D,K# K+#GF+.28.#8.FK #IF+K+&>#FK& C$8#Q8.8#8C ,$, .2F+C"C.F F+8.8F 84,$D(#Q&>&,8#D 8/,$,8 .2F+.F F+C%FC #Q82,$888#, C%,$C, F+.2F. 8/CF+8CF #Q,$D'&>&#,D ,$, 8.F+D&.2FD8. F+F ,$#Q8D8,#D ,$, .5C$8'F.C.8F &BF/828F& #YC(8:,(8#,C 86,)8, F3&D.<D+&.FD C'F4FC ,-K7#`88?8#,8K ,.C.K8,KC .BK9#^F9.FK# F:D'K:8?&O&D8FK C2,1K<#gK,C# ,2K=&?&K, .IF>8DK?&Q8F.K& K@F?FK7M#QC$88.78C#8 ,$, .2F+C"F.C 8.F+F8 D(#Q84&>,$&8#D, ,$8/8, F+.2.F C%F+CF 82,$8#Q8,8# ,$C%C, F+.2.F F+8/C8FC &>D'#Q,$,#D& ,$, F+8.D&.2D.F8 F+F 8.#QD,$88#8,D ,$, C".28%F+FC8. 8.F+8F &>,$#QC$848&C#, 8/,$,8 D$.2F+FD. F+C FC K-,$882#Q8#K8, ,$K+C$,CK .2F+K+F.K 8/F+K+D8DKF K+#Q&>,$C$CK,#& #DK+,$#K, F+8.#G.2K+8K.#F #IF+&>K+KF#& #U882C&8#8C #G,&,# C$F.#J.5.F#C 82#MF.8F# D+#U&A949D&# ,&82,8 .5F..F C'F.FC 79#Y889,(0780#,87 05,(C),0C /5.9F0.F/ /585C F0F8/C D,18#Y-5#-1D ,(-4-, +4.984D*F08+.DF F0+3F+1*)D818)p,*x#\@]C8< @[HK7<80:J<807,#8 07C,,*C0, /8.<F3/.F 88/8F3C"/C8F -7#\D/1;-#1D ,*-7-, 88D-.<+7F3D8.+F +6F3+F1-848#YC)818#C ,(, C&.9F0.FC 84F08F D-&D8:,(#YD&#8, ,(858, .9F0.F C)F0FC #U885,&8,8# ,&C'C, F..5.F C82F.CF8 &A#UD*,&D#,& ,&, 82D(F..58.FD F.F 8.8D,$#Q8,D8# ,$, F+.28%C"FC.8 F+8.F8 84C$#Q,$&>,&C8# 8/,$,8 F+.2D$.FD F+C CF 828,$#QK-88#K, K+,$C$C,K F+.2K+.KF F+8/DK+KFD8 ,$#Q&>C$K+K,&C# K+#D,$,#K #GF+8.K+.2.F8K# K+F+#I&>&KF# C$8#Q8.88C# ,$, .2F+C"C.F 8.F+F8 #QD(&>84,$D#&8, ,$8/,8 F+.2.F C%F+FC 8,$#Q828#,8 ,$C%,C .2F+F. CF+8/FC8 ,$#QD'&>,D#& ,$, D&F+.28.FD.8 F+F ,$D#Q88.8#8,D ,$, C"F+.28%C.8F 8.F+8F C$,$#Q&>84,C&8# 8/,$,8 .2D$F+DF. C F+FC #QK-882,$8K8,# K+C$,$,KC .2K+F+K.F F+8/K+DKDF8 ,$K+C$&>#QCK#&, #DK+,$#,K 8.F+K+#G.28.F#K #IK+F+&>KF#& 8C$8.#Q8#8C ,$, F+C".2.FC F+8.F8 D(84#Q&>,$#&,D8 8/,$,8 F+.2F. F+C%CF 82,$#Q88#,8 C%,$,C F+.2.F 8/F+C8CF ,$#Q&>D'D,&# ,$, 8.D&.2F+.DF8 F+F 8.8D,$#Q88#,D ,$, 8%F+.2C".FC8 8.F+8F ,$C$&>84#Q8#C,& 8/,$,8 F+.2D$.FD F+C CF #QK-828,$8#K,8 C$,$K+,CK F+K+.2F.K F+8/K+DKF8D &>C$#QK+,$#,KC& #DK+,$#K, #G.28.F+K+K8#F. K+&>F+#IF#K& #QC$88.88#C ,$, F+.2C"CF. 8.F+F8 84#QD(&>,$8,D#& ,$8/8, .2F+.F C%F+FC 828,$#Q8,#8 ,$C%,C .2F+.F 8/CF+CF8 ,$D'#Q&>D,#& ,$, 8.F+D&.2DF.8 F+F D8,$#Q8,D# ,$, C$F.8'.58.FC &B82F/8F& #Y8:,(C(8,C# 86,)8, D+&D.<F3&.FD F4C'FC 8?#`K78,-88,K# ,.K8C.,CK .B#^F9K9#F.K F:&OD'K:8?F8D&K C2#gK<,1C,#K &?,2K=K&, .IK?&QF>8DK.&F8 K@F?FK7M8.C$8#Q78#C8 ,$, F+.2C".FC 8.F+F8 84,$D(#Q&>8,D&# 8/,$8, .2F+F. F+C%CF ,$#Q88288,# C%,$,C .2F+.F F+C8/F8C &>D'#Q,$#D&, ,$, F+.2D&8..FD8 F+F ,$8.8D#Q8#D,8 ,$, .2F+C"8%.C8F F+8.F8 #Q84C$,$&>&C8,# ,$8/8, .2F+D$.FD F+C CF #QK-,$8288#8K, K+,$C$CK, F+K+.2KF. F+8/K+DKDF8 ,$K+&>#QC$&KC#, K+#D,$K,# F+.28.#GK+KF8#. &>#IF+K+FK&# C$#Q88.88C# ,$, C".2F+FC. 8.F+F8 84,$&>D(#Q&D8,# 8/,$,8 F+.2F. C%F+CF ,$8#Q828#,8 C%,$C, .2F+.F F+C8/F8C ,$#QD'&>D#,& ,$, D&.28.F+.D8F F+F 8D#Q8.,$88,#D ,$, F+C"8%.2F8.C F+8.8F 84C$#Q,$&>C#&,8 8/,$,8 F+D$.2F.D C F+FC ,$K-828#Q8#8K, K+,$C$CK, K+.2F+.KF K+F+D8/D8KF #QC$K+,$&>KC&,# ,$#DK+,K# #G8.F+.2K+#F.K8 F+&>K+#I#FK& #Q8.8C$88#C ,$, F+C".2FC. F+8.8F 84,$D(#Q&>D&8#, 8/,$,8 .2F+F. C%F+CF ,$882#Q8#,8 ,$C%C, F+.2.F C8/F+F8C D'#Q&>,$,&#D ,$, D&.28.F+.8DF F+F #Q,$D8.88,8#D ,$, C"F+8%.2.F8C 8.F+F8 C$&>,$#Q84,8&C# ,$8/8, D$F+.2.DF F+C FC K-,$828#Q8#,8K K+,$C$K,C .2K+F+F.K 8/DK+F+8DFK K+#Q,$&>C$,#C&K #D,$K+#K, .2F+8.#GK+8.F#K K+&>#IF+FK#& #QC$8.88C#8 ,$, F+C".2FC. F+8.F8 ,$D(&>84#Q8D#&, 8/,$8, .2F+F. F+C%FC #Q828,$8#,8 C%,$C, .2F+.F 8/CF+C8F &>#Q,$D',D#& ,$, 8.D&.2F+F8D. F+F #Q8K+,$D8K#,D K,,$K, .5F.C$K.K.FC F/K/FK #Y8:C(,(&DK1,C#8&K ,)86K28K, D+F3K4.<DF.K K5C'F4FCK 8?8#`-;C[@]x1<J< @HK,-881,-# /:#Z,.C.#C,/ .BF9/<#`.#F/ D'-=#dF:8?D-#8F ->,1D3#g-#D, -?#a,2#,- 1J+@#g8DF>8F+#1 #k+@F?#F+ 8.8#QC$8#C8 ,$, .2F+C"FC. 8.F+F8 84,$D(#Q&>#,8D& ,$8/,8 F+.2F. F+C%CF #Q,$82888#, C%,$C, F+.2F. F+8/CF8C ,$&>#QD',&#D ,$, D&8.F+.28FD. F+F D#Q8.8,$8#,D8 ,$, F+C".28%.FC8 8.F+8F &>84#Q,$C$,#&8C 8/,$8, .2F+D$F.D F+C FC 82,$8K-#Q8K#,8 ,$C$K+K,C F+.2K+KF. K+F+8/DKF8D ,$#Q&>K+C$C,K&# ,$#DK+,#K .28.#GK+F+8.KF# #IK+&>F+&#KF #QC$88.88#C ,$, .2C"F+CF. F+8.8F #Q&>84,$D(,&#8D ,$8/,8 F+.2F. C%F+FC 8,$#Q828#8, ,$C%,C F+.2F. C8/F+C8F D'#Q&>,$D#&, ,$, .2F+8.D&F8.D F+F 8.D,$8#Q8D#8, ,$, 8%.2C"F+C8.F F+8.F8 84#Q&>C$,$C&8#, ,$8/,8 F+D$.2DF. F+C FC 82#Q8K-,$8K,#8 K+,$C$CK, K+.2F+FK. K+DF+8/KDF8 C$#Q&>K+,$,CK&# #D,$K+#,K F+#GK+.28.#F.8K &>K+F+#IF&K# #QC$88.88#C ,$, F+C".2C.F 8.F+8F 84D(,$#Q&>D,#8& ,$8/,8 F+.2F. F+C%CF #Q8,$828#,8 C%,$C, F+.2F. CF+8/FC8 #Q&>,$D'#D&, ,$, F+D&8..2DF8. F+F 8,$K+#QD8,#DK K,,$K, K.C$F..5.CKF F/K/KF #Y&D8:K1,(C(&KC8,# K2,)86,8K .<F3K4D+.DFK K5F4C'FCK 8?1<#`8,--;8#8-,1 /:C.,.#ZC#/, #`.BF9/</#.F F:8?#d-=D'DF-8# #g->,1D3D,#- ,2-?#a-,# +@#g8DF>1J1F#8+ +@#kF?+#F C$8.#Q888#C ,$, .2C"F+.FC F+8.8F 84#QD(&>,$8,#&D ,$8/8, .2F+.F C%F+FC #Q82,$88,#8 ,$C%C, .2F+.F 8/CF+8FC ,$&>#QD'&D#, ,$, .2D&F+8..DF8 F+F #Q88.D,$8,8D# ,$, .2C"8%F+CF8. F+8.F8 #Q&>,$C$84C#,8& ,$8/,8 D$F+.2D.F F+C CF #QK-82,$88,#K8 K+,$C$C,K K+.2F+F.K K+DF+8/KD8F ,$&>C$#QK+,C&#K K+#D,$K#, #G.28.K+F+.K8#F F+#IK+&>#FK& 8.#Q8C$8#8C ,$, C"F+.2CF. F+8.F8 D(#Q84&>,$,#D&8 8/,$8, .2F+F. C%F+CF ,$#Q8828#,8 C%,$,C F+.2F. F+C8/FC8 #QD'&>,$,D#& ,$, .28.D&F+.8FD F+F D.K.#.,.K$D$,$#$9K#D,K#,9D/MTrkc1JAZ2]General MIDIPart-01YYx [@}@HKJ<C}4 @xsnp24}2}220}xp4}02}442}}42}42}2424}xpO4}xpI}OM}I}M@}@@}9@xpO}9xp}IOIM}@M}@}@}@9xp}C9xsnidp}GCxp}BGBA}4}A2}4}42}2424}24}}4224}4}242}42}}244}2}2424}xpO4}xsnpG}OGG}}LGL}LE}LxpEO}xpON}xp}VNxpV}999}9:}xp:}9;}9;}@}@@}@=xsp=}=C=}xpC9}xpG}9}HG}GH}GGL}G}LMC}MCC}C4}42}2}442}24}24}}24xpO4}xp}IO}IMM@}@@}@}9xpC}9xsnidpGC}xspGG}H}Gxsnid_ZpX}HxpVX}}VVX}Vxsnid_ZpX}Oxsnid_Zp}OLxsnpQ}LxpQ[}xpZ[}xp}Zb}bb}4b}2442}}24}42}242}4}242}4}24}4242}2}4}24}<2|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0</MTrko1JAZ24General MIDIPart-02YYx h[@]@HKJ<}} @Cxsnp24}}2220}xp}20}222}222}2}22}2}42xpO4}xpM}O}MM@}M>}@@<>@}9<xp}9Oxp}MO}MMM@}>@}@><@9}<`<}9@<>`}C>xsniC}E`G}E`}GE@CE`}ACAA}2A}}222}222}}222}2}22}222}22}22}222}2}222}4}2xp4O}xsp}MO@LM`LJ`JG}GG}@GH`JH`L}J}JL@JH@HE}`}HE@JH`}JOxpN}O@N}Q@}VQxp}9V9}9}9:9:};}9};;@<;`><`>@}@@}}=@xsp==}@=>`>@`}C@C}A`A@`@>`<>`<9}xpH}9HH}}GHG}G@HG`JH`}MJM}M`JM`HJ`GH`C}GC}C2}C22}}222}222}22}24}xp4O}xpM}O}MMM}@}>@@<>@<9}`<}9@<>`C>}xsni}CE`E}Gxsp}GG}HGxsnid_Zp}XHxpX}VV}VX}Vxsnid_p}VX@VT`TS`S}Oxsnid_ZpLO}xs0}ML`OM`QO}`T}Q@VT`}[VxpZ[}@]}Z@}]bb}b}2b}2222}2}22}2}22}222}22}22}2}22}222}22}2}2<|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0</MTrk fYJAZ2H"General MIDIPart-03YYx "[@]@HKJ<C}L"} @xsnpG"}C"HLxpL"}H"GCxspG"C"}HLGL"H"C}LC"}HG"}H"L"CGHLG"}C"J"CH"}Gxp}H"HJL"xpK"H}I"L}Q"M"KIJ"}H"MQH"E"}HJ@}@M"}Q"EHxp}J"O"MQxpI"}OK"JIQ"M"}KQMJ"H"}E"}H"JH@}@M"}EQ"HxpJ"O"}MQxsnidpJO}C"G"`CGJ"O"}@}`N"OJ}M"O"MN}J"L"}JOH"C"G"}LHGC}L"H"C"}HLG"}CGL"H"LHG"}C"G}H"CL"}C"HLG"H"}L"GCLC"}G"HCH"L"G}C"H}G"LL"}GH"CLHC"}G"H"C}GJ"xpH"L"JH}xspLJ"}H"H@}`L"H"}HJ`HL}C"G"CO"L"G}@}`}`}OLH"E"H"}E"EH@}@EH}L"Q"`}LQE"H"@}`}L"HEH"xpQ"}N"LHxpC"}G"QNxp}GCL"Q"LM"QJ"}MJR"}M"xp}RML"Q"xpH"LE"}QxpI"HM"E}xsnpH"}IL"MJ"H"}HL`}`H"JH}L"`}`M"Q"}LHxpE"M}C"Q}Q"M"CEQG"}C"M G"}C"GC`H"J"}CG}Q"JHM"`}`}`}`O"MQJ"}xpJOH"}L"LG"}HC"L"CH"}GL}HC"G"CG}L"H"HG"C"}L}H"CGJ"xpH"L"}JHxpLH}I"K"}KIQ"M"QMJ"}H"HH"JE"}@}@M"HE}Q"xpM}J"O"QxsnidpJOG"C"}xsnpL$}H$GCxsnid_Zp}H"J"LHxpG"}JC"HxpH"}J"CGxsnid_pL"H"}JH@}`}`}L"HH"Lxsnid_ZpHJ"}H"LxsH"L"}HJ`M"}LHQ"xpQMH"}L"xpLQ"}HN"xpG"N}C"QxpGCL"H"}C"HL}G"}GH"L"CG"C"LH}C}L"H"GL}C"G"HL"}H"GCC"G"L}H}L"H"CGC"G"}LHGH"}CL"LC"HG"}L"}H"CGG"HL}C"}HCG|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0H/MTrk YJAZ2J<General MIDIPart-04YYx ^[@]@HKC} @H"C"}xsnp}C"J"HCxpC"}H"CJxspJ"}CC"HJCC"H"}}C"J"CHJCH"}C"J"C"C}HC"H"C}Jxsnp}I"D"CHH"M"}IDC"H"}HMCHE"L"}@}@LC"H"}Exsnp}D"I"CHIM"DH"}}C"H"HML"}E"HC@}@H"}EC"L`E"CH"H}@H"E}E"H`EHC"}J"xsnid_ZpB"H"JC}C"}J"BH}C"H"JCHC}J"C"H"CJ}C"C"}CHJ"H"C"}JCHC"CJ"}H"CC"}JHC"J"C}J}C"CH"J"CHC"}C"H"}CJJ"HC"}CH"}C"CJ}HC"CJ"C}H"C"JxsnidpC"}J"CHC"}G"JC@G"CG}H"`HGG"C"}`L"G}E"CxsnpC"}EH"LxpHCH"B"}xp}C"J"BHxp}L"CE"Jxp}F"M"LExpL"}FME"G"ELC"}@H"GCG"}`G"C"H}G`}CL"E"GxpD"}LI"Exs0}H"DID"`I"}D"DH`H"DI}C"xpCC"H"}HxspCH"HE"}C"}HEJ"xp}H"JC"CCH"M"}H`E"H}H"M`E"H"}EH`}`EHJ"C"}xpCJH"}C"CC"}HJ"C"JH"}CCC"HJ"}JC}H"C"HC"J"}CC}H"C"JxsnpD"HCI"}DM"H"I}MHC"H"}H}E"L"C@}@EL}H"C"`CE"}H"H@HH"E}E"`J"HEC"}xsnid_ZUPp}JCH$C$xsnid_ZUPpJ"}CHC"xp}H"C"JCxsnid_C"}G"CH`GH"C"C}xsnid_ZUPKFpCC"H}H"`}E"H"HC@E"EH}H"pxspEB"H}H"xpJ"HC"B}xpCC"J}H"HC"C}J"H"CJC"}}J"HCC"CJ}H"C"}CJ"C"HJH"CC"}C"}J"HC}C"H"JCC"}HJ"CH"J}CC"J"HC}C"C"}H"CJHC"C}J"HJ}C|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0H/MTrknJAZ2Steamin' across the prairiesYyx @r( @General MIDI/simutrans-124.5/simutrans/music/13-Stephenson-blues.mid000066400000000000000000000271171520675460500231130ustar00rootroot00000000000000MThd`MTrk 2006.10.06C 7Q C 7G 7G 7C 7C 7G 7G 7C 7C 7G 7G 7C 7C 7G 7G 7C 7C 7C 7C 7C 7C 7C 7G 7F 7C 7F 7@C 7@C 7@F 7@C 7G 7F 7C 7F 7@C 7@C 7@F 7@C 7G 7F 7C 7F 7@C 7@C 7@F 7@C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7@C 7@C 7@C 7`G 7`C 7@C 7@C 7@C 7`G 7`C 7G 7C 7C 7F 7@C 7@C 7@G 7@C 7/MTrko @General MIDICopyright (C) 2006 shunterJAZ2X` 2006.10.06/MTrk @JAZ2General MIDITrack1 0P0@0LPEPFPEF0L$<@@FOLOLOLFDJPFPLPJLF<0@]07@]7@NFNMN<@MF@$@BFB@B@Fa<JGCGSG7@7@\CSJ7MSJS?@SS2@\SMJ?2C@SIJI>@LI\LSJCSPA@JPMP\SJMA>7@@GGQGMGQMG7?@2@@GDMDJDGJM?C@@MEGEGEGMC27@A@]7A0@=0<@@@JFJ@@J@Fa<0@]07@@FHCH@HF@C7@OFO<@MOM@F$@M@M@FM@Fa<7@]72@?@]?2C@TBMBGB>@MTGACA@]>A7@]72@?@]?C@@GGGGGMGMGC27@A@]A70@ @NCNFNF@C0<@@@RFR@R@F@a<@MGTACA@@QNMNGNQMG>A7@SIMISM$SLMLQLMSQ72@?@]?MKC@SKTKTMS$MRSRSRSMC2A@7@@MQQQQMSA70@@FJJJLJLJFa0<@=<CLLL0@@TAGAC@MTG$GCMCGGCGMCA@@MCGCQCGQM>A7@]7?@GMAM2@CMGACA?C@]2C7@@GOAOQOGAQ70@@LFL@F$@QFQJQ@FJa07@]7C@]C<@d@OFOF@@LFL@@LF@<C@]C@M<@CMX@C<@M7@CMFMTCF@@BCBDBDEBPE@C@GCGFGT@CF70@CI@LNJN7@\LGJ7JFMFGFC@\GJM>C?@<@=?3@MFLFFFMLF$LHFHLLHLF3<C@]C0@3@@FO@OJOJ@F3@]>C@]CO@]OJ@]J>@]>C@]CO@]OJ@]J>@]>C@]CO@]OJ@]J>@]>C@]CO@]OJ@]J>@]>C@]CO@]OJ@]J>@]>C@]CO@]OJ@]J>@]>C@]CO@]OJ@]J>@]>C@]CO@]OJ@]JCC@]CF@]F>@]>C@]C>@=><@]<A@A<@=<C@]CF@]F<@=<C@=C<@]<C@]C<@=<CC@]CF@]F>@]>C@]C>@=><@]<A@A<@=<C@]CF@]F<@=<C@=C<@]<C@]C<@=<CC@]CF@]F>@]>C@]C>@=><@]<A@A<@=<C@]CF@]F<@=<C@=C<@]<C@]C<@=<cC@]CO@=OH@]HC@]CO@]OT@]TT@TT@]TT@]TO@]OH@]HT@]TT@]TO@]OH@]HT@]TcC@]CO@=OH@]HC@]CO@]OT@]TT@TT@]TT@]TO@]OH@]HT@]TT@]TO@]OH@]HT@]TcC@]CO@=OH@]HC@]CO@]OT@]TT@TT@]TT@]TO@]OH@]HT@]TT@]TO@]OH@]HT@]TcC@]CO@=OH@]HC@]CO@]OT@]TT@TT@]TT@]TO@]OH@]HT@]TT@]TO@]OH@]HT@]T#>@]>#>@]>>@]>C@]C>@]>J@]JH@=H<@]<F@]F<@]<C@=CF@]F<@]<?@]?<@]<C@]C<@=<>@]>C@]CC@]CH0=H/MTrk JAZ2G@General MIDIBaseZ[<]< @J@H@I@K@$h>$$h$(h>(+h+-h-.h6..h.h>h$h>$$h$4h>4+h+-h-.h6..h.-h>-+h++h>++h+/h>/2h2'h'(h6((h(4h>42h2+h>++h+/h>/2h2(h()h6))h)4h>42h2$h>$$h$"h>""h" h !h6!!h!-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.h>h+h>++h+/h>/2h24h45h655h5&h>&&h&+h>++h+/h>/2h24h45h655h54h>42h2$h>$$h$(h>(+h+-h-.h6..h.h>h$h>$$h$(h>(+h+-h-.h6..h.h>h+h>++h+/h>/2h2(h()h6))h)4h>42h2+h>++h+;h>;2h2(h()h6))h)&h>&&h&$h>$$h$(h>(+h+-h-.h6..h.h>h$h>$$h$(h>(+h+-h-.h6..h.-h>-+h++h>++h+/h>/2h24h45h655h54h>42h2+h>++h+)h>))h)(h()h6))h)&h>&&h&$h>$$h$4h>4+h+-h-.h6..h.h>h$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$"h>""h" h !h6!!h!-h>-+h+$h>$$h$"h>""h"-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+ h !h6!!h!h>h$h>$$h$4h>4+h+ h !h6!!h!h>h+h>++h+)h>))h)4h45h655h54h>42h2)h>))h)-h>-0h02h23h633h3$h>$$h$$h>$$h$(h>(+h+ h !h6!!h!h>h)h>))h)-h>-0h0$h>$$h$(h>(+h+$h>$$h$"h>""h")h>))h)9h>90h0$h>$$h$(h>(+h+ h !h6!!h!-h>-+h++h>++h+/h>/2h2'h'(h6((h(4h>42h2)h>))h)9h>90h0&h&'h6''h'2h>20h0$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+)h>))h)'h>''h'$h>$$h$4h>4+h+$h>$$h$4h>4+h+)h>))h)'h>''h'$h>$$h$(h>(+h+-h-.h6..h.-h>-+h++h>++h+)h>))h)'h'(h6((h(&h>&&h&)h>))h)9h>90h0%h%&h6&&h&2h>20h0$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+)h>))h)'h>''h'$h>$$h$(h>(+h+$h>$$h$4h>4+h+)h>))h)9h>90h0$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$"h>""h"-h-.h6..h.h>h$h>$$h$4h>4+h+-h-.h6..h.h>h$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+ h !h6!!h!-h>-+h+$h>$$h$"h>""h"-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.h>h$h>$$h$4h>4+h+-h-.h6..h.h>h$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$(h>(+h+-h-.h6..h.h>h$h>$$h$(h>(+h+-h-.h6..h.-h>-+h+$h>$$h$"h>""h" h !h6!!h!h>h$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$"h>""h"-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+$h>$$h$4h>4+h+ h !h6!!h!-h>-+h+$h>$$h$(h>(+h+$h>$$h$4h>4+h+$h>$$h$(h>(+h+$h>$$h$+h>++h+$h>$$h$"h>""h"$h>$$h$"h>""h"$h>$$h$(h>(+h+$h>$$h$+h>++h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h++h>++h+/h>/2h24h45h655h54h>42h2$h>$$h$"h>""h" h !h6!!h!-h>-+h+$h>$$h$4h>4+h+-h-.h6..h.-h>-+h+)h>))h)-h>-0h0$h>$$h$(h>(+h+$h>$$h$(h>(+h++h>++h+)h>))h)(h>((h('h>''h')h>)'h'&h>&$h|$/MTrkL @HJAZ2General MIDI  /MTrkT{JAZ2H@General MIDId[<]<J@I@BirdsK@0@G@ @x0$@x$0@x0+@x+$@x$7@x70@x00@x0+@x+0@x0H0@80H5@850@x0H5@850@x00@x00@x0$@x$0@x00@x0$@x$$@8$0@800@800@X0h0@800@X0+@X+5@85$@8$$@8$HH@H/MTrkO @General MIDIJAZ2Stephenson blues/simutrans-124.5/simutrans/music/14-Last-journey-of-the-Niagara.mid000066400000000000000000002241731520675460500247730ustar00rootroot00000000000000MThd MTrkyCopyright (C) 2006 shunter~ JAZ2X`AQ  2006.10.06/MTrkyJAZ2 x SlMelodyGeneral MIDI x[ @] x@BK ??BKy B@@&BBwwuBu sBsqBq B B(BssBuBuwBw@?B? B@BEKB@B==B@=E=E>>@>E>Eyy E??@?E?E@@4EwwuEu sEsEqq Eoo Emm kEkEii E EE kEkEmEmoEoEqqEss:qEqoEoEmm  E EooEqEqE?@?Ess uEuE??EwwE>@>EEy ?yBKE?E @B@ B{{yBy BwwuBuBssBqq Boo BBmmB oBoqBqsBsB  uBuBww@?B? @=B@EKBB=B@=E=E>>yE>Ey@> E??{E{E?@?@E@(yEy EwwEuuEssqEqEoomEmEkkEii Egg Eee E  EccEEEeeEgEgiEikEkmEm EooqEq8oEo Emm kEkiEi E  kEkmEmE oEoE?@?EqqsEsEuu?E?EwwE>@>@EyJK@>J>E@EyEJ??@?J?@J@wJwJuusJsqJq JJooJqJqJssuJuJww@@J@@?L?L>JLKJ >L==@=L=>L>@>L>L??L?@?@L@uLuLLssLLuuL?@?L=`=L>@>L??L=wLw=J=JK@=LLJ> >J??J@@@?J?JyyJ{{J@@yJywJwJ uJu JsJs qJq JooJ Jmm J(($Jkk /J/*JmmoJo%J% Jqq J  sJs uJuJ wJwL@LKJ@J?@?JLyyLww uLuLss LLLuu wLw @?L?LLyLJKy@J@J{{JyyJwwJuuJssqJqJooJmm JJooqJqsJsJuuJwwJ?@?JJ? ?Jyy@J@JIK@I@IwwIuuIssqIqIooIIIqqIssIuuIwwI?I@?`@II@A@G@IAGK@G@8yGy${G{GyGy wGwGuuGGssGqqGooGmmG0G<oGoqGqGssu#GuG#GwwnGuuGssG qGq  G GooGmmG@?G?GooGqGqG??Gss@>G>uGuGw>wG>@=G=G? ?BKGByy@B@.BwwBuusBs BqBqBBssBuu B Bww@?B? @B=B@CKB=BC= =C=`=C> >yCyC>`> ?C?C{{ C@@(yCy CwwuCusCsCqqCooCmmCkkiCi Cgg Cee C cCcCCCeegCg CiCikCkCmm oCoCqq8Coo Cmm kCkiCi  C kCkCmCm CooC?@?qCqsCsuCu?C?wCw@>C>yC?CC ?Cy@K @@@ @{{@yy w@wu@u @ssq@q o@o@@mm@@ooq@q@ss@  u@u@ww@?@? @@=@=CK@@@@=C=>C>C>@>yCy C??@?C?C@@{C{$Cyy wCwuCuCssqCqoCoCmm kCk CiigCg C CCC CiikCkmCmCoo CqqsCs$qCqoCo mCmkCk  C mCm CoCo @?CqC?q sCsCuuC??Cww@>C>IKI@@?C?@CCyIywIwIuusIsIqq I sIsIuuIww@?I?I@I@GKI`G@G`@EK@E@@?E?Eyy {E{}E} E@@{E{Eyy wEwuEuE  sEsEqqEoomEEmEkk Eii&E&HkEk Emm oEoEqEq EssEuEuwEw @EKE@yE?@?EEyE,wEw E uEu sEs Eqq@?E?EsE??sE>uEu@>wEwEEKEyEy?E?E?@?@E@ wE Ew Euu Ess EqqsEsEuuEEww@KE@?@?@@@8u@u@  0@@wwB@@?@?@BK@@ByywBwuBu BssBqqBooBBqqsBBs uBuBwwBB?@?@B@BKB=B= =B= yByB=`=B> >B>`>B? ?Bww@B@Buu sBBs qBq B  oBoBBmmD B oBo BqBq Bss B uBuBFBssBqBqBooBmmBkk BBiiB Bkk mBmBoBoBqqsBsBuuwBw ByyB ?BKB?B B@@B{{yBy wBwBuusBs qBqoBo mBmB  B"oBoqBqBsBsBuuwBw@?B? @=BB=B@BEKE= =`=E= >E> E>`>yEy ?E?@E@4Eww uEu sEsEqqEoomEmEk kE EEEmEmEooqEqEssPEqqoEo,E  EqqEE?Es@?sEuuE??EwwE>@> ?EE?BKEByyB@@.Bww BuuBsBsBqBqB"Bss Buu B wBwB?@? BB=BB@EK=@@=E=E>>yE>Ey@> E??{E{@?E?E@@(Eyy EwwEuuEssqEqoEoEmmkEk iEi gEgEeeE  EEEEggEiikEkEmm EooEqq:oEo Emm kEkEii E kEkEmmE oEoE?@?EqqsEsuE?Eu?wEw@>E>z@E@EEEzE>JK >J>`>J? ?@J@xJxJvvtJtJrrJppJnnJll J JJnnJpprJrtJtvJvJ@@@xJxL>@?JL?J >LKL==L=@=L>>L>@>L??L?@?@L@vLvtLtL!!rLrpLp LLrrL?tLt@?L>?@>L?v`=L=Lv=L=@=J=LLJK >J>J??JxxJ@@J?@?Jzz|J|~J~J@@J||JzzJxxJJvv tJtrJrJJppJnnlJlJjJjhJhJ((Jff 0J02JhhjJjJ%%Jll nJn  J Jpp rJrJJttvJvJ?JJKJ@y@?@JJyJ{{JyyJwwJuusJsJqJqoJo Jmm @?J?JooJ??qJq@>sJ>JsuJJuwJwJyJKJyJ??J?@?J@@wJwJuJusJsJqqoJomJmJooqJqJssJJuuwJwIKJ@?I?@I@yIywIw Iuu sIsI  qIqIssIuIuIAIGKxAIIx@@G@G@@BGzzG|G|zGz G xGxGvvGttrGrGpGpnGnGllGjjhGhG Gff&#G#GhhGjjlGlnGnGpprGr*G*GttGvvGxxPGvv G$$Gtt Grr pGp nGnlGl G GjjGhhGjGjG?@?GllnGnG??Gpp@>G>rGrGttG>>Gvvx@=G=Gx ?BKGG?G B@@BzzxBxBvv BttrBr  B pBpB rBr BttBvBvBxxB?@? =B=B@B@BCK@=C=>C>C>@> C?? zCzC?@?C@@,Cxx vCvCttrCrCppCnn lClCjj ChhCffCddCC0CfCfhChjCjCllCnnCpprCr$CppCnn CllCjj* C lClCCnnC?@?Cpp CrrCttC??CvvCxxC>@>C@K ?CC? @@@@zz$@xxv@v@tt r@r p@p n@@n@"p@pr@r t@ @tv@v@?@x@?x @@@CK=@@@=C=@=C>>C>@> ?C?zCzC?@?@C@*xCxCvv tCtrCr Cpp Cnn lClCjjC  CCC  lClCnnpCpCrrC%%Ctt(CrrCppCnn C C  pCpC rCr@?C? tCt vCv?C?Cxx@>C>CCzGKC>@C@z >C`>G> ?G?G@@GxxvGvGttGrrpGpnGnlGlGGGnnpGprGrtGtvG@@@GvxGxI> >@?GI?IKG=I=@=I=I>>@>I>I??I?@?I@@IvvtItIIrrIppIrIr@?tItI?I=@>I>?I?`=I=Ivv=@=E>EKE=I>I`>E>Ex ?E?@E@x@?E?{E{ E}}@E@E{{ExxEvvEtEtrErEppEnEnEllEjj,E,Ehh2E2jEjlElE%En%n pEp ErrEEtt Eww EKy@?EEyEE?@E@{E{yEyEwwE  uEuEssEqqoEoEmm@?E?Eo?E?oEqq@>EssE>uEuwEwEEEyEKy?E?E?@?@E@wEwE  uEuEssqEqEoomEmoEoqEqEssEuEuEwwEKE@?E?@E@EyywEw uEu EssE EqqEssEuEuEyE? ?E=KEy@=@w=wu=us=s=qq ="s=s=uuw=w==?@?@==@===>Ky>y>=@=>>>>>@> ?>?>?@?>ww>@@>uu >ss q>>q >oo >m > mk>k> >ii>H> > >kkm>m>>oo>qq>s>s>0>""4q>q">oom>m>kk>ii>gg >ee>>cc >>ee>  >ggi>ik>k>mmo>>o>qqs>su>uw>wJK =J=`=J=J> >JyyJ>`>J? ?J@@4JwwuJu sJsqJq oJo Jmm Jkk iJiJ JJ JkkJJmmoJoqJqsJs>*q>q>oo>mm  > o>o>>qq>?@?s>s u>u>??w>w>>@>J?JJyy ?LKJ @L@ L{{yLy wLwuLu LssLqq LooL  LmmLLooLqqsLsL  uLuLww@?L? `@L@GKLL@G@ALAG@@Gyy0{G{&GyGywGwGuuGssGGqqGoomGmGkkGiiGGgg,G,iGiGkkmGm oGoGqqGssG!! Guu wGw^uGusGs GqqGGoo GmmG  Gkk Gii G@?G?kGkmGGmG??Goo@>GqG>qGss>uwG>GuGw@=G=G@?IK@G?I@ IyywIwIuuIssI  IqqIssuIuwIwI?@?@JKIII@`IK`@J@JI@@ @?I?yIy{I{@I@IyywIwIIuu IIssIqqIIooI$$fIqqIssIIuuIwIwIyJKI? ?IIyJ@@{J{JyyJwwuJusJsJqqoJo mJmJoJo qJqJssuJuwJwJ?J@?IK@J@JI?@?yIyI@@wIw Iuu sIsIqqIsIs IuuwIw@?II?@y ?I@I?IyIGK@G@GwwuGuGGssqGq GG sGsGuuwGw@?GG?@AEKG@GGAE@@@@E@ yEy4{E{EyEy EwwuEusEsEEqqEoomEmEkkE0E!!8EmmEooEqqEss'E'uEu wEwfE"Eu"uEss qEq oEoE  mEmEkEk@?E?EmmoEoE??qEq@>E>EssEwEuE>uw>E=@=yEE>E@ >@EyCKE`>C>C? ?C@@wCwuCuCssCqqoCo C CmmCCooqCqCssCuuCw@@wC@G>GK@? >CCG?G==@=G=G>>@>G>?G?@?G?@G@uGuGsGs qGqGsGsG?@?uGu?G?`=G>G=@>=wG=GwGC=G@=CKC> >C??@C@@?C? yCyC{{@C@yCyCwwCuCusCs CCqqoComCm#C#kCkiCiC//Cgg7C7(CiikCk*C*mCmoCo C qCqCssCCuuwCw C=CGKC=G=@=>G>@>G>?G?G?@?G@@.Guu GGwwGGGCK ?G?yGyGC@@C{{CyyCwwCuuCssCCqqCoo CCCqqCssCuuCww@?CC?CBKC@@@?B?yByB@@ BwwBuu sBsBB BuuwBwBB?@?@KBA`@@@BA@B@@@@@@yy@&@ww@uu@@ssq@q@oo@0@!!Dq@q@ss@uu'@' w@wl"@" @uus@s@qq @ o@o @@?@?q@q?@?@ss@>@>u@uw@>@w>@=@=@@K@@@@`=K@`====@K @=@= >@>@>@>y@y?@? @{{@?@?@@@@yy@ww@uu@ss@qqo@o@mm@kk@iig@g  @ @ee@@g@g i@i k@k @mm o@o q@q @ssq@q o@o@mm@kk @ m@m o@o@?@?@@qq@ss@?u?@uw@w@>@>@@ ?@?=K=yy=@@=ww =uu =ssq=q==ss u=uw=w=@?=?@@?@K=yy=?=@=@@@4@ww u@u@s@sq@qo@o@?@?@q?@?qs@s@>@>@@uuw@w@y@Ky@@??@?@?@@@w@w@@uu s@s@qqo@o@qq@ssu@@uw@wEK`9@{@9{@ E9 9 E9`9yEy E: : `:E:wEw@;E;E<<uEuE<@<Ess=E=@=E=qEq>E>oEoE>@>mEmE??kEk @?E?iEi E@@EE  EEkkEmEm8EooE  HE&&H+E+XmEmE""E EkkiEiE EggEee E @?E?EggEEiikEkEmE??moEoEqEqsEsuEuE>@>E>>JKEE=Ex=xJ=@=J>>@>J> ?J?Jzz@?J?@J@*xJxJvvJttrJrpJp nJn Jll JjjJJhhJJJjjJlJlJnnpJprJrtJt JrrJppnJnJllJ  JnnJJpp @?J?Jrr tJtJv?J?vJxx@>J>J? ?JLKJ @L@zLz$LxxvLvtLt rLr Lpp nL Ln L"LppLrrtLtLLvvL?Lx@?x L@AGK@LALLG@@@G@@DzGz|G|GzGz GxxvGvGttGrGrpGpnGnGlljGjGhGh*"G"(jGjlGlGnn pGprGr G))tGt GvvGxxPGvv #G#Gtt rGr Gpp nGnG  Gll jGj GG?@?lGlnGn?G?pGpG>@>rGrtGtvG>Gv>@=xG=GxGG?IK@@?GI@ Ivv IttI  Ivv xIxI?@?IJK@I@I`J@`@IKJ@I@ @?I?Izz|I|I@@zIzxIxIvIv tItIrrI  IppnIn IllI Ijj IhIhHIjjlIlInn IppIIrr Itt IIvv Ixx I? ?JKII@J@JzzJxx vJv Jtt Jrr J"tJtJvvJJxJ?x@?JJ@IK@I?@?I@@zIzIxxvIvtItrIrIppInnIpIprIrtItIvv@?I?IxxIGK@ ?II@I?GzG@z@xGxGvvGttGrGrpGpGnnlGlGGnGn GpprGrtGtvGv@?GG?xGx@AG@EKGAG@@E@E@@BEzz|E|zEEzxExEvvtEtErrEpEpEnnlElEjjhEhE fEf&EhEhEjjlElEnnEpprErE!!EttEvvExxPEvvEtt ErrEpEp EnnEllE  jEjEhEh@?E?EjjEllEE?Enn?pEpE>@>ErrtEtEv>E>v@=ExxE=CKE>@@>E@C> >E?C?C?@?@C@CvvCttrCr  C C tCtCvvC@@@Cxx@?G?GKC >G>C=G=@=G=G>>G>@>G??G?@?@G@vGvGGttGvG?Gv@??G?@>G=`=G>G==x@=GCxC=GCK >C>?C?C@@C?@?zCz @C@xCx CvCv CttCCrrCpp$C$Cnn C11Cll C992nCnC,,CpprCr C CttCCvv CxxGKCC==CG=@=G>>@>G>G??@?G?@G@"vGvGtt GrrG GttvGvG""GxxGG? ?CKGG@C@CzzCxxCvvtCtrCrpCpCnnCCCpprCrCttCvvC?Cx@?CxC@ ?BKC?@CB@@zBzBxxBvvtBtrBrBppnBnBpBpBrrBttBvv@?xBB?BxB@@K`@BAB@@@A@@@N@zz@ @ @xx @vvt@t@@rrp@pn@n@ @ll&$@$"@nn"p@p@rrt@t@++@vvx@xZ@vv@%%@ttr@rp@p @ @nn @@?@?@pp@??r@r@t@>@>tv@>@v>@x@=@=x@@@@@@K`=K@`===@K= @=@= @>>@>@> ?@?@zz@?@?@@@@xx@vv@tt@rr@ppn@n@ll@jj@hhf@f @  @dd@f@f@ @hh @jj @ll @nn@pp@rrp@p @nn@ll @jj@  @ll n@n@?@p@?@pr@rt@tv@??@vx@x@>@>E>@@@@>@@EKE??E?@?@E@vEvtEtErrpEpnEn Ell EEnEnpEprErEttEE@@@EvvxExL>@LK`>L@E`=L==L= L=@=L>>@>L>L??L?@?@L@Lvv Ltt L Lr r pLpLLrrLt@?L?Lt`>L>v >`=LvL=L> =`<L<L=J=LJ>x@=Jx`>JKJ@@BvJvJ  0Jx?JKJxJJ?J?@?J@@ zJzxJx JvvtJtJJrrJ  pJp2rJrJJtt(J$J$ rJrJppJJnn4pJp JrrJtJtJvvxJxJ?@?@J@JJ8BK@@EB=B@EK=BE=@=E>>E>@>yEy E??E?@?E@@4EwwuEu sEsEqq oEo mEm Ekk iEiE EE kEkEmEmEooqEqEss:EqqoEoEmm E  oEoEEqq@?E?Ess Euu?E?wEw EBKE?y ?ByB @B@ B{{Byy wBwuBuBssqBq oBo BmBmB oBoqBqsBs B uBuwBwB?@? EEKB=E?Ey@B ?B@y= @E@@=E=>E>E>Ey@>yEwEwE??uEu {E{E?s@?Es@E@qEqE  EyEy wEwEuuEssEqqEoEsosmEmEEuuEkkEww@?E?iEi gEg eEe  E EccEEeEeEgEgiEikEkEmm oEoqEq8oEo Emm EkkiEi E  kEkEEmm oEo@?E?EqqsEsE?Euu?wEw@>E>y@>J>JKJyJE@@E?J?J?@?@J@JwwuJuJssqJq JJooJqJqJssJuu@@JwwJ@L>LK@?LJ >L?=L=L=@=>L>L>@>L??L?@?L@@uLuLLssLuLu@?L?L>@>`=@>L?L>?L==LwwL=J=@=LJJKJ> >?J?J@@@?J?yJy{J{@J@JyyJJww Juu JsJs qJq oJoJ mJm J(($kJk /J/*JmmJoo%J% Jqq  J Jss uJuJ wJw@@?LL@J?JLKyLyLww uLusLs LLuLu wLw @?L?LJyyJKL@J@{J{JyywJwuJuJssqJqoJoJmm JJooJqqsJsJuuwJwJ@?J?J@yJJ? ?IK@IyI@@IwwuIuIssqIqoIoIIIqqIssuIuIww@?I?IIGKIAA@`@I@G@@G@8Gyy$G{{GyGy wGwuGuGGssqGqGoomGmG0G@>uGu>GwwG>@=G= ?G?BKGByy@B@.BwwBuuBss BqBqBsBsuBuB  Bww@?B? BB@@=CCKB= =C=`=C= >C>yCy`>C> ?C?{C{ @C@(Cyy wCwuCuCssCqqCooCmmkCkiCi Cgg eCe C CccCCCeeCgg CiCikCkmCm oCoqCq8oCo mCm kCkiCi  C CkkCCmm Coo@?C?CqqsCsuC?Cu?Cww@>C>y@@K ?C?C@y @@@ @{{@yy @ww@uu s@s@qq @oo@@mm@@oo@qqs@s@  @uu@ww@?@? C@=CK@@@@=C=@=C>>C>@>Cyy ?C?C?@?C@@{C{$Cyy CwwCuusCsqCqCoomCm Ckk CiigCgC  CCC iCikCkmCmoCo CqqCss$CqqoCo mCmCkk  C mCm CCoo C?Cqq@? CssuCuC??Cww@>C>I@@IC@?C?IKyIyIwwIuusIsIqq I IssuIuwIwI?@?GK@GII@`G@G`@EK@E@E?@?yEy E{{}E} @E@E{{yEy wEwEuu E sEsEqqoEomEmEEkk Eii&E&HkEk mEm EooEEqq EssEEuuEww @@?EyEyEE?EKE@,wEw E Euu Ess qEq@?E?E?s?EsuEuE>@>wEwEEEKEyyE??@?E?@E@  wE EwEuu sEs EqqEssEuuEwEwE@K@?@?@@@8@uu@  0@@ww@@?@?BKB@B@yBywBwuBu sBsqBqBooBqBqBsBs BuuwBwBB?@?B=BK=B@B@ =B= yBy`=B=B> >B>`>B? ?wBwB@@uBu sBsB qBq  B BooBmBmD B oBo BqBq sBsB  BBuuFsBsBqBqoBomBmBkk BiBiB kBk mBmBoBoqBqBssuBuBww ByBB?yBKB ? @B@{B{yBy BwwBuuBss BqqoBo Bmm B B"BooBqqsBBsuBuBwwB?@? B@EK=B=B@EE= =E=`=E> > E>`>yEy E? ?@E@4Eww Euu EssqEqoEoEmmkE Ek EEEEmmEooEqqEssPEqqEoo,E  EqqE@?sE?EsEuuE??wEw@>E>BKE?B ?EByy@B@.wBw BuuBsBsBqBqB"sBs uBuB  wBw@?B? =EK@B@B=EB@=E=>E>@>yEyE> E??{E{@?E?E@@(Eyy EwwEuusEsEqqoEomEmEkk iEi gEgEee E EEEEggiEikEkmEm oEoqEq:Eoo mEm kEkiEiE  kEkmEmE oEoE?@?qEqEss?uE?EuwEwE>@>J@ >JKzEE@JzE>`>J>J? ?@J@xJxvJvtJtrJrJppnJnJllJ  JnJnpJpJrrJtt@@vJ@JvxJxJLK@? >LL?L>L==@=L=L>>@>L>?L?@?L?@L@vLvLttL!!LrrpLp LrLrLt@?tL?L=L?Lv@>v`=L>?=L=@=JKLJ=JJ> >Jx?J?x@J@@?J?Jzz|J|~J~J@@J||zJzxJxJvJv tJtJrrJpJpJnnlJlJJjjhJhJ((Jff J002hJhjJj%J%lJl nJn  J Jpp JrrJJttvJvJyJ@J@?J?Jy@JKJ{{yJywJwuJuJssJJqqoJo mJm @?J?oJo?J?JqqJ>@>JssJuJuJwwyJKJyJJ??J?@?@J@JwwJJuusJsqJqoJomJmoJoqJqsJsJJuuwJwJIKI?@?@I@yIywIw Iuu IssI  qIqIssIIuuGKAIxGxIAG@@G@@G@BzGzGG|| zG GzxGxGvvGttrGrGpGpnGnlGljGjhGhG Gff&G##hGhGjjGllnGnpGpGrrG**GttvGvxGxPGvv $G$tGt rGr Gpp nGnlGl G jGjGGhh@?G?GjjGllG?Gnn?pGp@>G>rGrtGtvG>Gv>@=G=GxxBKG? ?BG B@@zBzxBxvBv BttBrr  B BppB rBr BttBBvv@?xB?Bx B@=CKB@B=CC=@=C>>@>C> ?C? zCzC?@?C@@,Cxx vCvtCtCrrCppCnn lClCjj ChhCffdCdCC0CCffChhCjjlClCnnCppCrr$pCpnCn CllCjj* C lClCCnn@?C?Cpp CrrCtt?C?CvvCxxC>@> ?@KC?@C @@@z@z$@xxv@v@tt @rr p@p n@@n@"p@p@rr @ t@t@vv@?@?x@x @CKC@=@@@=@=C=C>>C>@> C??Czz@?C?C@@*CxxvCv CttCrr Cpp Cnn ClljCj C CC C lClnCnCppCrr%C%tCt(rCrCppnCn C C  CppC rCrC?@? tCt CvvC??xCx@>C>C@C> >GKCz@GGzG>`>G? ?@G@xGxGvvGttrGrGppGnnGllGGnGnpGprGrGttv@@G@GvxGxIIKG@? >I?I>I==I=@=I>>@>I>I??@?I?I@@vIvIttIIrrIppIIrrItI?t@?@>`=I=?I>I?I==vIvE=>EK@=E>EI`>E>E?E@ ?@xEx@?E?{E{ }E}@E@E{{ExxEvvEtEtErrEppEEnnlElEjjE,,EhhE22EjjEllnEnE%% Epp rErEtEt Eww EK@?EyE?EyEE@@{E{EyywEwE Euu sEsEqqoEomEmE?@??EooE?Eqq@>E>EssEuuEwwEKEyyEEE??E?@?@E@EwwE  EuusEsqEqEooEmmEooEqqEssEEuuEwwEEK@?E?@E@EyywEw Euu EssE qEqsEsEEuu ?y=KEE?=y==@@=ww=uus=s=qq ="=ssu=u=ww@?=?===@=>K==@>yy@=>=>>>>>@> ?>?@?>?>ww@>@u>u >ss q>>q >oo  >m> mk>k> i>i>H>>  >kk>mm>>ooq>q>s>s>0>""4>qq"o>o>mmk>k>iig>g >ee>>cc >>ee > >ggi>i>kkm>mo>o>q>q>ssu>u>wwJK@=>=>@J= =J=`= >J>Jyy`>J> ?J?J@@4JwwJuu sJsJqq oJo Jmm JkkJiiJ  JJ JkkJmJmoJoJqqJss>6q>qo>om>m >  o>o>q>q@?>?>ss >uu?>?>ww>>@>yLK ?JJ?LyL L@@ L{{yLy wLwuLu sLsqLq oLo L mLmLLooLqqLssL  uLuwLwL?@? @GK`@ALAL@G@GLG@@yGy0{G{&GyGywGwGuuGssGGqqoGoGmmkGkGiiGGgg,G,GiiGkkmGm GooqGqGs!sG! uGu wGw^GuuGss qGqGoGo Gmm G Gkk iGi GG?@?kGkGmGmG??GooqG>Gq@>sGsuGu>G>wGwG=@=G@?@G@IKG? IyyIwwuIusIs I qIqsIsIuuwIw@?I?I@JIJK@``@J@IKJI@@ @?I?yIy{I{@I@yIyIIwwIuu IsIsIqqIIoo$I$fqIqsIsIuIuIIwwJKI?JJy ?yI@J@J{{yJywJwuJusJsqJqJoo mJmJoJo JqqsJsJuuwJwJ?J@?IKJ@@J@?I?yIy@I@Iww Iuu sIsqIqIsIs uIuwIwI?I@?@I?Gy ?GKI@Iy@G@GwwGuuGsGsGqq GG GssGuuGwwG?@?GEKAGAG@G@@@E@E@@ Eyy4E{{EyEy wEwEuusEsEqEqEoomEmEkkE0E!!8mEmEooqEqsEs'E'Euu EwwfuE"Eu"sEs qEq EooE  EmmkEEk@?E?mEmEooE??EqqE>@>sEs>uwE>EuEwE=@= >CKy@E>CCyE@EC>`> ?C?C@@wCwCuuCssqCqCoo C CmmoCCoqCqCssCuuCwwC@@@CG>@? >G?GKGG==G=@=G>>@>G>G??G?@?@G@GuuGsGs qGqGGss@?uG?GuG?@>`=?G>G==G=GwwCK@=C=CGC> >?C?@C@@?C? yCy{C{C@@CyyCCwwuCuCss CqCqoCoCmm#C#kCkCiiC//gCgC77(iCiCkkC**mCmoCoC  CqqsCsCuCuCww =C=GGKC@=G=G>>G>@>G??@?G?G@@.uGu GGwwGGCCyGCKy ?G?@C@{C{yCywCwuCuCssCqCqoCo CCqCqsCsuCuCww@?C?CCC@@BKB?@?@B@yBy wBwBuu BssBB uBuwBwBB?@?B@@@@BA@K`@BA@@@@@yy@&@wwu@u@s@sq@q@oo@0!@!D@qqs@su@u'@' @wwl"@" @uus@sq@q@  o@o @@?@?@qq@??@ss@>@>u@uw@>@w>@=@=@K@@@@@`@=K`=@K=== @=@= >@>@>@>y@y@?? @{@?{@?@@@@yy@ww@uu@ssq@qo@o@mm@kki@i@gg @e@  e@@@gg i@i @kk m@m o@o q@q s@sq@q o@o@mmk@k@  @mm @oo@?@@?@qqs@su?@?@uw@w@>@>=K@?= ?@y=y@=@=ww =uu =ssq=q==ss u=u=ww@?==?y=?=@=@K@?@y@@@@4@ww u@u@s@s@qqo@o@?@?@q@??qs@s@>@>@u@uw@w@y@Ky@?@?@?@?@@@w@w@u@u @ssq@qo@o@qq@ss@@uu@ww{E{`9EK@9@ 9E9 E9`9yEy E: : `:E:wEwE;@;E<<uEuE<@<EssE==E=@=Eqq>E>EooE>@>EmmE??kEk E?@?Eii @E@EE  EkEkEEmm8Eoo E HE&&H+E+XEmm"E"E EkkEiiE EggEee E @?E?EggEEiikEkm?E?EmEooEqEqEssEuuE>@>>E>=JKxE=JxEJ=@=>J>J>@> ?J?JzzJ?@?@J@*xJxJvvtJtJrrJpp Jnn Jll JjjJhJhJJjJjJlJlJnnJppJrrJtt rJrJppJnnJllJ  JnnJJpp J?@?Jrr tJtvJ??JvJxxJ>@>LJJ?LK ? L@@Lzz$LxxLvvtLt Lrr pLp  LnL nL"pLprLrLtLtvLvLxL?x@? GLAL@A@LGKG@@@@G@DGzz|GG|zGz GxxvGvtGtGrGrpGpnGnlGljGjGhGh*"G"(jGjlGlnGn pGprGr G))tGt GvvxGxPvGv #G#tGt rGr Gpp nGnG  lGl Gjj G@?G?GllGnnG??Gpp@>rG>GrGtt>G>Gvv@=GxG=xIKG@?@IG?G@ vIv tItI  vIv xIxI?@?JKI@J@I``@IKJJ@I@@ @?I?zIzI||I@@IzzIxxIIvv tItIrr I pIpInn lIlI Ijj IIhhHIjjIllInn IppIIrr Itt IvIv Ixx I?IJJK ?J@@JzzJxx vJv Jtt rJr J"JttvJvJJ?Jx@?xJIK@J@I?@?I@@zIzIxxvIvtItIrrpIpnInIpIpIrrIttIvvxIxII?@?@II? ?I@GKGzz@G@xGxGvvtGtGrGrGppGnnGllGGnGn pGpGrrGttvGvGx@?xG?GEKAG@GA@G@@E@@E@BEzzE||zEzExExvEvEttrErEpEpnEnlElEjjEhhE Eff&EhEhEjjlElnEnEpprEr!E!tEtvEvxExPvEvtEt rErEpEp EnnEllE  EjjhEEhEjE?j@?EllE?nEnE?pEp@>E>ErrtEtv>EvE>E=Ex@=xE >E>CK@>E>E@@C??@?C?@C@CvvCttrCr  C C CttvCv@@C@CxxCG>@? >GKG?GG==G=@=>G>@>G>G??@?G?@G@vGvGtGtGG?Gvv@?G>@>`=G?G=?=G=GxC=CxCK@=CC> >?C?C@@C?@?zCz C@@Cxx CCvv CttCCrrCppC$$Cnn C11Cll 9C92CnnC,,pCprCrC  tCtCvCv CxxGKCC=G=G=@=>G>G>@>G??G?@?@G@"vGvGtt GrrG GttvGvG""xGxGG?CKC ?GC@@CzzCxxvCvtCtCrrCppCnnCCCpprCrCttCvv@?xC?CCx ?C@BKC@C?B@@BzzxBxBvvBttBrrBppBnnBBpprBrtBtBvvxB?@?BBx@B@K`@BAB@A@@@@@N@zz@@  @xx v@vt@t@r@rp@pn@n@ l@l&$@$"n@n"@pp@rrt@t+@+v@vx@xZv@v%@%@ttr@r@pp @ @nn @@?@?p@p@??r@r@>t@t@>v@>@v>@=@xx@=@K@@@@@`@=K`====@K @=@= >@>@>@> ?@?@zz@?@?@@@x@xv@v@ttr@rp@pn@n@llj@jh@h@ff  @ @dd@f@f@ @hh j@j l@l n@np@pr@rp@p @nn@ll j@j @ l@l @nnp@?@@p@?@rrt@tv@v@??@>@>x@x@E>@EK@>@@E?E?E?@?E@@vEvEttErrpEpEnn Ell EEEnnEppErrtEtEv@@EvE@xExEL@LK@`>L>`==L=L= @=L=>L>L>@>?L?@?L?L@@vLv tLt  rLrL LppLrLrLt@?tL?L`>L> >LvL=`=vL> =L<`<L=@=x`>JKJ=J>JxLJ@@BJvvJ  0J?JKxJ?JxJJ?JY@?@J@ JzzJX JxxJWJvJ[vJttJrJr JZ J JppJYJXrJrJJttJW JVJJUJ$$ JrrJTpJpJJnnJSJRpJp rJrJJQJttJvvJxJ?x@?JJ@@ JPJO JN JMJL JK JJJI JH JGJF JE JDJC JB JAJ@ J? J>J= J<J;0J@>>0>0<0<0<0</MTrkACJAZ2General MIDI @H  Melody@BK? B@&BBwBu BsBqB B(BsBuBBwB? BB=EKBB@E=E>E>Ey E?E?E@4EwEu EsEq Eo Em EkEiE EE EkEEmEoEqEs:EqEoEm E EoEEqE?Es EuE?EwE>E?EyBKEE B@ B{By BwBuBsBq Bo BBmB BoBqBsB BuBwB? EKB@BB=BE=E>E>Ey E?E{E?E@(Ey EwEuEsEqEoEmEkEi Eg Ee E EcEEEeEEgEiEkEm EoEq8Eo Em EkEi E EkEmE EoE?EqEsEuE?EwE>E@JKEyEJ>EJ?J?J@JwJuJsJq JJoJJqJsJuJ@JwL?L>JJLKL=L=L>L>L?L?L@LuLLsLLuL?L>L=L?LwL=J=LLJKJ>J?J@J?JyJ{J@JyJJw Ju JJs Jq JoJ Jm J($Jk J/*JmJoJ% Jq J Js JuJ JwJL@JJ?LKLyLw LuLs LLLu Lw LL?LyLJKJ@J{JyJwJuJsJqJoJm JJoJqJsJuJwJJ?J?JJyIKJ@I@IwIuIsIqIoIIIqIsIuIwII?I@G@GKIAIG@8Gy$G{GGy GwGuGGsGqGoGmG0GGuG>GwG=G?BKGByB@.BwBuBs BBqBBsBuB BwB? B@BB=BCKC=C=C>CyC>C?C{ C@(Cy CwCuCsCqCoCmCkCi Cg Ce C CcCCCeCg CCiCkCm CoCq8Co Cm CkCi C CkCCm CoC?CqCsCuC?CwC>CyC?C@KC @@ @{@y @w@u @s@q @o@@m@@o@q@s@ @u@w@? CK@@@=@@C=C>C>Cy C?C?C@C{$Cy CwCuCsCqCoCm Ck CiCgC CCC CiCkCmCo CqCs$CqCo CmCk C Cm CCo CqC? CsCuC?CwC>I@IKCC?CIyIwIuIsIqI IsIuIwI?I@IIGK`EKGG@E@E?Ey E{E} E@E{Ey EwEuE EsEqEoEEmEk EiE&HEk Em EoEEq EsEEuEw EEKE@EE?Ey,EwE Eu Es EqE?E?EsEuE>EwEEKEEyE?E?E@ EwE Eu Es EqEsEuEEw@KE@?@@8@u@ 0@@w@BK@?@B@ByBwBu BsBqBoBBqBBs BuBwB?BBB=B@BKB= ByB=B>B>B?BwB@Bu BsB Bq B BoBBmDB Bo BBq BsB BuBFBsBBqBoBmBk BBiB Bk BmBBoBqBsBuBw BKB?ByBB B@B{By BwBuBs BqBo BmB B"BoBqBsBBuBwB? BB@BEKB=E=E=E> E>Ey E?E@4Ew Eu EsEqEoEmEkE EEEEmEoEqEsPEqEo,E EqEEsE?EuE?EwE>EEE?BKByB@.Bw BuBBsBBqB"Bs BuB BwB? BB@EKB=BE=E>EyE> E?E{E?E@(Ey EwEuEsEqEoEmEk Ei EgEeE EEEEgEiEkEm EoEq:Eo Em EkEiE EkEmE EoE?EqEsEuE?EwE>JKE@EzEEE>J>J?J@JxJvJtJrJpJnJlJ JJnJpJrJtJ@JvJxJJL>LKL?L=L=L>L>L?L?L@LvLtL!LrLp LLrL?LtL=LvL>L?L=LJ=JKLJ>JxJ?J@J?JzJ|J~J@J|JzJxJJv JtJrJJpJnJlJJjJhJ(Jf J02JhJjJ%Jl Jn J Jp JrJJtJvJJ@JKJJyJ?J{JyJwJuJJsJqJo Jm J?JoJ?JqJ>JsJuJJwJyJJKJ?J?J@JwJJuJsJqJoJmJoJqJsJJuJwJIKI?I@IyIw Iu IsI IqIsIIuIxIGKIAIG@G@BGzGG|GzG GxGvGtGrGGpGnGlGjGhG Gf&G#GhGjGlGnGpGrG*GtGvGxPGv G$Gt Gr Gp GnGlG GjGGhGjG?GlG?GnGpG>GrGtG>GvG=GxG?BKGG B@BzBxBv BtBr B BpB Br BtBBvB?Bx B@BB=CKBC=C>C> C? CzC?C@,Cx CvCtCrCpCn ClCj ChCfCdCC0CCfChCjClCnCpCr$CpCn ClCj*C ClCCnC?Cp CrCtC?CvCxC>C?@KCC @@@z$@x@v@t @r @p @n@@"@p@r@t@ @v@x@? @@@@=CK@C=C>C> C?CzC?C@*CxCv CtCr Cp Cn ClCjC CCC ClCnCpCrC%Ct(CrCpCnC C CpC CrC? Ct CvC?CxC>C@CC>CzGKCG>G?G@GxGvGtGrGpGnGlGGGnGpGrGtGvG@GxIKI?GGI>I=I=I>I>I?I?I@IvItIIrIpIIrItI?I=I?I>IvI=IEKE>IE=E>ExE@E?E?E{ E}E@E{ExEvEEtErEpEEnElEjE,EhE2EjElE%En Ep ErEEt Ew EyE?EKEEE@E{EyEwEuE EsEqEoEmE?E?EoEqE>EsEuEwEKEEyEE?E?E@EwE EuEsEqEoEmEoEqEsEEuEwEKEE?E@EyEw Eu EsE EqEsEEuEEE?=KEy=@=w=u=s=q ="=s=u=w==?=@==>K=>y>=>>>> >?>?>w>@>u >s >>q >o >m> >k> >i>H>> >k>m>>o>q>>s>0>"4>q">o>m>k>i>g >e>>c >>e> >g>i>k>m>>o>q>s>u>wJKJ=J=J>JyJ>J?J@4JwJu JsJq Jo Jm JkJ JiJJ JkJJmJoJqJs>*>q>o>m > >o>>q>?>s >u>?>w>>JJ?LKJyJ L@ L{Ly LwLu LsLq LoL LmLLoLqLsL LuLwL? G@L@GKLALLG@Gy0G{&GGyGwGuGsGGqGoGmGkGiGGg,G,GiGkGm GoGqG!Gs Gu Gw^GuGs GqGGo GmG Gk Gi GG?GkGGmG?GoG>GqGsG>GwGuG=G?GI@IK IyIwIuIsI IqIsIuIwI?I@IJKI`J@IKJI@ I?IyI{I@IyIIwIu IIsIqIIoI$fIqIsIIuIwIJKIIyI?IJ@J{JyJwJuJsJqJo JmJJo JqJsJuJwJJ?IKJ@JI?IyI@Iw Iu IsIqIIs IuIwII?II@IyI?GKG@GwGuGGsGq GG GsGuGwG?GGGAEKG@E@E@ Ey4E{EEy EwEuEsEEqEoEmEkE0E!8EmEoEqEsE'Eu EwfEuE"Es Eq EoE EmEkEE?EmEoE?EqE>EsEuE>EwE=E>EEyE@CKEC>C?C@CwCuCsCqCoC CmCCoCqCsCuCwC@CGKG?G>CG=G=G>G>G?G?G@GuGGs GqGGsGuG?G>G=G?G=GwGCKC=GC>C?C@C? CyC{C@CyCCwCuCs CCqCoCmC#CkCiC/CgC7(CiCkC*CmCoC CqCsCCuCw GKCCC=G=G>G>G?G?G@.Gu GGwGGCKGG?GGyC@C{CyCwCuCsCCqCo CCCqCsCuCwC?CC@CBKB?ByB@ BwBu BsBB BuBwBB?B@@@B@KBA@@@@y@&@w@u@@s@q@o@0@!D@q@s@u@' @wl@" @u@s@q@ @o @@?@q@?@s@>@u@>@w@=@@K@@@`=K@`===@K @= @>@>@y@? @{@?@@@y@w@u@s@q@o@m@k@i@g @e@ @@@g @i @k @m @o @q @s@q @o@m@k@ @m @o@?@@q@s@u@?@w@>@?@@=K=y=@=w =u =s=q==s =u=w=?==y==?@K=@@@4@w @u@@s@q@o@?@?@q@s@>@u@@w@y@@K@?@?@@@w@@u @s@q@o@q@s@@u@w@{@@9EK E9 E9Ey E: E:EwE;E<EuE<EsE=E=EqE>EoE>EmE?Ek E?Ei E@EE EEkEEm8EoE HE&HE+XEmE"E EkEiE EgEeE E?EgEEiEkEmE?EoEEqEsEuE>E>ExEE=JKJ=J>J> J?JzJ?J@*JxJvJtJrJp Jn Jl JjJJhJJJjJJlJnJpJrJt JrJpJnJlJ JnJJp J?Jr JtJ?JvJxJ>LKJJ?J L@Lz$LxLvLt Lr Lp LnL L"LpLrLtLLvLxL? LALGKL@LG@G@DGzG|GGz GxGvGtGrGGpGnGlGjGGh*G"(GjGlGn GpGr G)Gt GvGxPGv G#Gt Gr Gp GnG Gl Gj GG?GlGnG?GpG>GrGtG>GvG=GxG?GGIKI@ Iv ItI Iv IxI?I@JKII`J@JIKI@ I?IzI|I@IzIxIIv ItIrI IpIn IlI Ij IIhHIjIlIn IpIIr It IIv Ix JKI?IIJ@JzJx Jv Jt Jr J"JtJvJxJ?JJ@JIKI?I@IzIxIvItIrIpInIIpIrItIvI?IIxGKII?I@GzG@GxGvGtGrGGpGnGlGGGn GpGrGtGvGxGG?GAEKG@GE@E@BEzE|EzEExEvEtErEEpEnElEjEhE Ef&EEhEjElEnEpErE!EtEvExPEvEt ErEEp EnElE EjEEhEjE?ElEEnE?EpE>ErEtEvE>ExE=E>E@C>CKEC?C?C@CvCtCr C C CtCvC@CxG?G>GKCCG=G=G>G>G?G?G@GvGGtGG?GvG>G?G=G=GCKCxC=GC>C?C@C?Cz C@Cx CCv CtCCrCpC$Cn C1Cl C92CnC,CpCrC CtCCv CxC=CGKCG=G>G>G?G?G@"GvGt GrG GtGvG"GxGGCKGG?C@CzCxCvCtCrCpCnCCCpCrCtCvC?CCxC?BKC@CB@BzBxBvBtBrBpBnBBpBrBtBvBBxB?@K@@B@BBA@@N@z@@ @x @v@t@@r@p@n@ @l&@$"@n"@p@r@t@+@v@xZ@v@%@t@r@p@ @n @@?@p@?@r@>@t@v@>@x@=@@@K@@`@=K`=@K== @= @>@> @?@z@?@@@x@v@t@r@p@n@l@j@h@f @ @d@@f@ @h @j @l @n@p@r@p @n@l @j@ @l @n@?@@p@r@t@?@v@x@>@@EK@E>@E?E?E@EvEtErEpEn El EEEnEpErEtEE@EvExL>LKL@EL=L= L=L>L>L?L?L@Lv Lt LrL LpLLrL?LtLL>L>LvL=L=L<J=JxJ>JKLJ@BJvJ 0JJJxJKJ?J?J@ JzJx JvJtJJrJ Jp2JrJJt(JJ$ JrJpJJn4Jp JrJJtJvJ?JxJJ@J8BK@B=B@EBEKE=E>E>Ey E?E?E@4EwEu EsEq Eo Em EkE EiEE EkEEmEoEqEs:EqEoEm E EoEEqE?Es EuE?Ew EE?ByBKB B@ B{By BwBuBsBq Bo BBmB BoBqBsB BuBwB? B@B=EKBEEyE? E@E=E>E>EyEEwE?Eu E{E?EsE@EqE EEy EwEuEsEqEsEoEmEEuEkEwE?Ei Eg Ee E EcEEEeEEgEiEkEm EoEq8Eo Em EkEi E EkEEm EoE?EqEsE?EuEwE>J>JKEJyE@JJ?J?J@JwJuJsJq JJoJJqJsJuJwJ@LKL?JL>LL=L=L>L>L?L?L@LuLLsLLuL?L=L?L>L>L=LwJ=LJKJJ>J?J@J?JyJ{J@JyJwJ Ju JJs Jq JoJ Jm J($Jk J/*JmJoJ% Jq J Js JuJ JwJ?L@LJLKLyLw LuLs LLLu Lw LL?LJKJyJ@J{JyJwJuJsJqJoJm JJoJqJsJuJwJJ?IyIKJ@J?JI@IwIuIsIqIoIIIqIsIuIwI?IG@IAIGKI@G@8Gy$G{GGy GwGuGGsGqGoGmG0GGuG>GwG=GBKG?ByB@.BwBuBs BBqBBsBuB BwB? B=BB@CKCC=C=C>CyC>C?C{ C@(Cy CwCuCsCqCoCmCkCi Cg Ce C CcCCCeCg CCiCkCm CoCq8Co Cm CkCi C CkCCm CoC?CqCsCuC?CwC>@@KC?@yC @@ @{@y @w@u @s@q @o@@m@@o@q@s@ @u@w@? CK@=@@C@C=C>C>Cy C?C?C@C{$Cy CwCuCsCqCoCm Ck CiCgC CCC CiCkCmCo CqCs$CqCo CmCk C Cm CCo CqC? CsCuC?CwC>C?I@IKICIyIwIuIsIqI IsIuIwI?GKI@GI`G@EKGE@E?Ey E{E} E@E{Ey EwEuE EsEqEoEmEEk EiE&HEk Em EoEEq EsEEuEw EEE?E@EKEy,EwE Eu Es EqE?EsE?E>EuEwEEEKEyE?E?E@ EwE Eu Es EqEsEuEEwE@K@?@@8@u@ 0@@wB@BK@?B@ByBwBu BsBqBoBBqBBs BuBwB?BBBKB=B@B= ByB=B>B>B?BwB@Bu BsB Bq B BoBBmDB Bo BBq BsB BBuFBsBBqBoBmBk BBiB Bk BmBBoBqBsBuBw BKB?BBBy B@B{By BwBuBs BqBo BmB B"BoBqBsBBuBwB? EEKB=BB@E=E=E> E>Ey E?E@4Ew Eu EsEqEoEmEkE EEEEmEoEqEsPEqEo,E EqEEsE?EuE?EwE>BKE?BEByB@.Bw BuBBsBBqB"Bs BuB BwB? B@EB=EKBE=E>EyE> E?E{E?E@(Ey EwEuEsEqEoEmEk Ei EgEeE EEEEgEiEkEm EoEq:Eo Em EkEiE EkEmE EoE?EqEsE?EuEwE>JKJzE>E@JEJ>J?J@JxJvJtJrJpJnJlJ JJnJpJrJtJ@JvJxLKL?L>LJL=L=L>L>L?L?L@LvLtL!LrLp LLrLtL?L?L>L=LvL=JJKJ=LJ>JxJ?J@J?JzJ|J~J@J|JzJxJJv JtJrJJpJnJlJJjJhJ(Jf J02JhJjJ%Jl Jn J Jp JrJJtJvJJ?J@JKJyJJ{JyJwJuJsJJqJo Jm J?JoJ?JqJ>JsJJuJwJKJyJJ?J?J@JwJJuJsJqJoJmJoJqJsJJuJwJIKI?I@IyIw Iu IsI IqIsIIuGxGKIAGIG@G@BGzGG|GzG GxGvGtGrGGpGnGlGjGhG Gf&G#GhGjGlGnGpGrG*GtGvGxPGv G$Gt Gr Gp GnGlG GjGGhG?GjGlG?GnGpG>GrGtGvG>G=GxGG?BKB B@BzBxBv BtBr B BpB Br BtBBvBxB? BCB@CKB=C=C>C> C? CzC?C@,Cx CvCtCrCpCn ClCj ChCfCdCC0CCfChCjClCnCpCr$CpCn ClCj*C ClCCnC?Cp CrCtC?CvCxC>@CC?@K @@@z$@x@v@t @r @p @@n@"@p@r@t@ @v@?@x CKC@=@@@C=C>C> C?CzC?C@*CxCv CtCr Cp Cn ClCjC CCC ClCnCpCrC%Ct(CrCpCnC C CpC CrC? Ct CvC?CxC>CGKGzGC@C>G>G?G@GxGvGtGrGpGnGlGGGnGpGrGtGvG@GxII>I?IKGI=I=I>I>I?I?I@IvItIIrIpIIrItI?I>I?I=I=IvE>EEKIE=E>E?ExE@E?E{ E}E@E{ExEvEEtErEpEEnElEjE,EhE2EjElEnE% Ep ErEEt Ew EyEEEKE?E@E{EyEwEuE EsEqEoEmE?EoE?EqEsE>EuEwEEyEKEE?E?E@EwE EuEsEqEoEmEoEqEsEEuEwEKEE?E@EyEw Eu EsE EqEsEEuE=K=y=E?=@=w=u=s=q ="=s=u=w=?===>K=@=>y>=>>>> >?>?>w>@>u >s >>q >o >m> >k> >i>H>> >k>m>>o>q>>s>0>"4>q">o>m>k>i>g >e>>c >>e> >g>i>k>m>o>>q>s>u>wJK>@>=J=J=J>JyJ>J?J@4JwJu JsJq Jo Jm JkJ JiJJ JkJJmJoJqJs>6>q>o>m > >o>>q>?>s >u>?>w>>JLyLLKJ? L@ L{Ly LwLu LsLq LoL LmLLoLqLsL LuLwL? L@LGKG@GLAG@Gy0G{&GGyGwGuGsGqGGoGmGkGiGGg,G,GiGkGm GoGqGsG! Gu Gw^GuGs GqGGo GmG Gk Gi GG?GkGmGG?GoGqG>GsGuGwG>G=IKG?GG@ IyIwIuIsI IqIsIuIwI?JKJI@I`JJ@IKI@ I?IyI{I@IyIwIIu IIsIqIIoI$fIqIsIIuIIwI?JyJKJIJ@J{JyJwJuJsJqJo JmJJo JqJsJuJwJ?JJ@JIKI?IyI@Iw Iu IsIqIIs IuIwI?III?GKGyI@G@GwGuGGsGq GG GsGuGwG?GG@GGAEKE@E@ Ey4E{EEy EwEuEsEEqEoEmEkE0E!8EmEoEqEsE'Eu EwfEuE"Es Eq EoE EmEEkE?EmEoE?EqE>EsE>EwEuE=ECKE@CCyE>C>C?C@CwCuCsCqCoC CmCCoCqCsCuC@CwG?GKG>GCG=G=G>G>G?G?G@GuGGs GqGGsG?GuG=G?G>GwG=CKCGC=C>C?C@C? CyC{C@CyCwCCuCs CCqCoCmC#CkCiC/CgC7(CiCkC*CmCoC CqCsCCuCw GCGKC=G=G>G>G?G?G@.Gu GGwGGCKGG?CCyC@C{CyCwCuCCsCqCo CCCqCsCuCwC?CC@BKCB?B@By BwBu BsBB BuBwBB?@@BBAB@@K@@@@y@&@w@u@@s@q@o@0@!D@q@s@u@' @wl@" @u@s@q@ @o @@?@q@?@s@>@u@>@w@=@@K@@@`=K@`@K=== @= @>@>@y@? @?@{@@@y@w@u@s@q@o@m@k@i@g @e@ @@@g @i @k @m @o @q @s@q @o@m@k@ @m @o@@?@q@s@?@u@w@>@==K@?=y=@=w =u =s=q==s =u=w==?=?@K@y=@=@@4@w @u@@s@q@o@?@q@?@s@>@u@@w@y@K@@?@?@@@w@@u @s@q@o@q@s@@u@wEK@9@E{ E9 E9Ey E: E:EwE;E<EuE<EsE=E=EqE>EoE>EmE?Ek E?Ei E@EE EEkEEm8EoE HE&HE+XEmE"E EkEiE EgEeE E?EgEEiEkEmE?EoEEqEsEuE>E>JxJKE=EJ=J>J> J?JzJ?J@*JxJvJtJrJp Jn Jl JjJJhJJJjJJlJnJpJrJt JrJpJnJlJ JnJJp J?Jr JtJvJ?JxJ>JLKJ?L L@Lz$LxLvLt Lr Lp LnL L"LpLrLtLLvLxL? LALGKGL@G@G@DGzGG|Gz GxGvGtGrGGpGnGlGjGGh*G"(GjGlGn GpGr G)Gt GvGxPGv G#Gt Gr Gp GnG Gl Gj GG?GlGnG?GpGrG>GtG>GvGxG=IKGIG@G? Iv ItI Iv IxI?IJI@JK`JIKJ@I@ I?IzI|I@IzIxIIv ItIrI IpIn IlI Ij IIhHIjIlIn IpIIr It IIv Ix II?JKJJ@JzJx Jv Jt Jr J"JtJvJxJ?JJ@IKJI?I@IzIxIvItIrIpInIIpIrItIvIIxI?I?GKI@IG@GzGxGvGtGrGGpGnGlGGGn GpGrGtGvG?GGxEKG@GGAE@E@BEzE|EEzExEvEtErEEpEnElEjEhE Ef&EEhEjElEnEpErE!EtEvExPEvEt ErEEp EnElE EjEhEE?EjElEE?EnEpE>ErEtE>EvExE=E@E>EE>CKC?C?C@CvCtCr C C CtCvC@CxCGKGG>G?G=G=G>G>G?G?G@GvGGtGGvG?G=G>G?G=GC=CxCCKC>C?C@C?Cz C@Cx CCv CtCCrCpC$Cn C1Cl C92CnC,CpCrC CtCCv CxGC=CGKG=G>G>G?G?G@"GvGt GrG GtGvG"GxGGCCKG?C@CzCxCvCtCrCpCnCCCpCrCtCvCxC?CCC@C?BKB@BzBxBvBtBrBpBnBBpBrBtBvBBxB?BBAB@@K@@@@N@z@@ @x @v@t@@r@p@n@ @l&@$"@n"@p@r@t@+@v@xZ@v@%@t@r@p@ @n @@?@p@?@r@>@t@v@>@x@=@@@@@K`=K@`=@K== @= @>@> @?@z@?@@@x@v@t@r@p@n@l@j@h@f @ @d@@f@ @h @j @l @n@p@r@p @n@l @j@ @l @n@p@?@@r@t@v@?@>@xEEKE>@@@E?E?E@EvEtErEpEn El EEEnEpErEtEEvE@ExLKL@L>EL=L= L=L>L>L?L?L@Lv Lt L Lr LpLLrL?LLtL>L>L=LvL<L=J=J>JxLJKJ@BJvJ 0JJ?JxJKJJYJ?J@ JzJX JxJWJ[JvJtJJr JZJ JpJYJXJrJJtJW JVJJ$JU JrJpJTJJnJSJRJp JrJJQJtJvJ?JxJ@J JPJO JN JMJL JK JJJI JH JGJF JE JDJC JB JAJ@ J? J>J= J<J;0J@>>0>0<0<0<0</MTrkJAZ2@General MIDI1YYx "[2]2}HK @J<C "[2HK]2@xJ<C`@7'`;$`>)7;>@0@71`;2`>HP>;7@p7'@`;$`>)7;@>071@`;2`>HP>@;798@`=(`@6 =@=9@CF 998`=(=0@0@6 =@=9@CFC09 21063 =@@09,=C@`=06>GC@6M@9P@>62@9/:@ "x]2C[2HKJ<9_7!@`;`>#7;@>07*@`;*`>7p7!@`;`>#;7>@0@7*`;*`>@7/@`;"`>. ;@;0`;@`C;@ 9/7P="0>0@. =@=0@C;C09P2' =@06"`9%P6`96(`93 @C@>8@P29@6>20@`62`95`>8 2 2.6@96`9->P@962>,@B&2"`6.`>9)`>2P6B@ 9`2 @,@`2+@@ >6&P6>8`6 `6@>@!@`E5 E2@@ @90@>2`B5`G8@7!`;`>#>7;>9 7*PB;*`><GP;@>7@70`;2`>5`C8`CC6@;@`=9`C@:`C:`=@7 =.@ C >@$@@@= @9"I&C,C06.`9)9`=2P6 "J<]2HKx@[2C@70I=`;2`>5`C8`>C@79`@7.;P;`>-P@>;7=&@E,9"`==.`@)E`E2P=0@`9 B,`2+@B E6&P6>8`6 `@9!>@6P>5 2>9@ @20@;2`>5`C8`C6@@C;`=9`C@:`C:`=@2 =.@ C >@$@@=@ @9"I&C,CPC6`=9`@:C`C:`=`=.@ C0@$@=@9 I9"C,I&C@06.@`9)9`=2P6099,P=I9@70@`;2`>5`C8`@7C`@7.;@>;`>-P>;@7@9"E,=&`=.=`@)E`E2P=0@`9 B,`2+@B E6&P6>8`6 `>@@9!6P>5 29@> @20@;2`>5`C8`@;@C6C`=9`C@:`C:`=@2 =.@ C >@$@@@= C,@I&9"CPC6`=9`@:C`C:`=`=.@ C0@$@@=9 II&9"C,@C06.@`99)`=2P6[2C@xHK]2J< "=I02,@06.`>;9'`>06>D@6J@9P>6@292@:2=>FP>>G2906M`239_656@6>69;`>>2 621@9P6(@0209#`23P6965@6 9;6`>>>2 6219P6(0209#`6@P97'@`;$`>>)7;>@071@`;2`>HP;>7@p@7'`;$`>);7>@0@71`;2`>HP>;7@@98`=(`@6 =@=9@CF 989`==(0@0@6 =@=9@CCF09P2/ =@@06(@`9,P6@ "x]2J<[2CHKC@26`68`9;`>? 2 2.6@96`9->P29@6@E&2B,96"`9.`B=)`B2P90=`6 9,`7+@9 B;&P;B8`; `@B>!@;PC5 7C>@E @70@;2`>5`C8`C6C`;;9`>>:C`C:`;@@7 =.@> C0@$@=@@ 9"C,@I&CPC6`=9`@:C`C:`=`=.@ C0@$@9=@ I&C,I9"@C06.@`9)9`=2P6099,P=@I9@20`62`95`>8 2 2.6@96`>9-P29@6B&@>,2"`6.`>9)`>2P6@B 9`2 @@,`2+@@ 6&>P6>8`6 `@@6>@!`E5 @2@E @90@>2`B5`G8 9 7.>@B;`G>-P;7>@C3;-@@; 7-`;1CPC+G+;0;*0C0>,0; 7C/`@GCC6@@> =9`@:C`C:`=`=.@ C0@$@=@@ C,@I&9"C06.`99)`=2P6]2CxHK@ "J<[2@=I20`62`95`>8`@6@2 7.@P9;`>->P@7;>9"9=&E,@`=.=`E@)`E2P=0@`9 @,`2+@@ 6&EP6=8`6 `=9!@@6P>5 2>@9 @20@;2`>5`C8 2 9.;@>=`@-CP9=@@@=-E3@= 9-`=1EPI+E+=0=*0E0@,0= 9E/`9$@E@I=%`E4I%@=P@= `@#E`C4 =p@I9@C@20`62`95`=8`6@=@2 7.@P9;`>-P>;7@=&9"@E,`==.`E@)`E2P=0@`9 @,`2+@@ E6&P6=8`6 `=@9!@6P>5 @>29 20@@;2`>5`C8 2 9.;@>=`C@-P9=@@E3=-@@= 9-`=1EPI+E+=P@I`>*@0E0@,0> 9C/`2-@@C 6/@6 936`>72 62.9P6&0209!`2-P@69@6/@6 693`>7>2 62*9P6"0209`@6P97"@`; `>$>;7@>07.@`;/`>DP7>;@p@7'`;$`>)7;>@071@`;2`>HP7@;>@98`=(`@6 =@=9@CF09 910==30@0CC@,C`C0=CG@=M@@@P9C=@9/: "J<]2@HKC[2x23@`65`98`>;`22-`6/6@6 9369_>7>2P69@`4.@9P7&040;!0;7<"`@ `C$@<C0<*`@*`C@21`6$`90 6@63@>B >69}2|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0@/MTrk!JAZ2]General MIDI2YYx @[J<@HK} @Cx[@HK] @J<C!+L`+ &KP&0+L`+ &KP&0!T0!P(W@(@!T0!P(W@(@&@@&`&U &@!L0!P&D@&!G0!@] @xHK[!CJ<+A`+ &?P&0+A`+ &?P&0+G0+P(L@(@!G0!P(L@(@&Dp&!@ !@!/P!&]P&!H!P&GP&0!8 !`&A`& !?P!0&G0&P(?@(@&Dp&(5 (@(%P(!GP!(6(P#G0#P!?@!@+A`+ &?P&0+GP+0&8 &`!GP!0(D (`&;`&@&;0&0 @x@][C!J<HK+GP+0(8 (`#A`# &?P&0!G0!P*?@*@&Dp&!5 !@!%P!&GP&!6!P+G0+P&?@&@!GP!0(D (`!GP!0(D (`&;`&@&;0&0!G`!@!<@! +GP+0(8 (`#A`# &?P&0!G0!P*?@*@&Dp&!5 !@!%P!&GP&!6!P+G0+P&?@&@!GP!0(D (`!GP!0(D (`&;`&@&;0&0J< @HK!Cx@][&9@&`&M &@!H0!P&H@&!K0!&bP&0&TP&0!C !`&TP&0!C !`+L`+ &KP&0+L`+ &KP&0!T0!P(W@(@!T0!P(W@(@&Pp&J<[] @C!@HKx&OP&0!; !`&A`& !?P!0*G0*P!?@!@+Dp+&5 &@&%P&+GP+&6&P+G0+P&?@&@+GP+0(D (`!GP!0(D (`&;`&@&;0&0!G`!@!<@! &GP&0!8 !`&A`& !?P!0&G0&P(?@(@&Dp&(5 (@(%P(!GP!(6(P#G0#P!?@!@+Dp+&5 &@&%P&+GP+&6&P+G0+P&?@&@!GP!0(D (`&;`&@&;0&0!][CJ<@x @HK&GP&0!8 !`+A`+ &?P&0!G0!P(?@(@&Dp&!5 !@!%P!&GP&!6!P+G0+P&?@&@!Dp!(5 (@(%P(!GP!(6(P!G0!P(?@(@!Dp!(5 (@(%P(+GP+(6(P&GP&0!8 !`+A`+ &?P&0!G0!P(?@(@&Dp&!5 !@!%P!&GP&!6!P+G0+P&?@&@!Dp!(5 (@(%P(!GP!(6(P!G0!P(?@(@&JP&0!? !`&JP&0!8 !`+D`+ &GP&0+L`+ &KP&0!T0!P(W@(@!@@!`!U !@(L0(P&D@&!G0!J<[xHK@ @!C]&JP&0!8 !`&JP&0#? #`$D`$ +?P+0&J0&P!S@!@&:}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0&@/MTrk JAZ2J<General MIDI3YY ^[2 @@HKC}]2 ^[2]2@HKCJ<J!@p@J@J!pJ@@I2`O@`L-`C@`I2I`O@O`LL-`C@CxE0B(=,FCOLIB E_@=C@DE4@@B8-B @0'@+>;CJ<[2 ^@]2HK>EJ@p@J@Jp@JG*@`O6`J&`C6`G@I*@`OO6`L&`C6CPJLL`CILO@@ B@E!>%P@>p>.PB@ E B`LP@>BL@`O!LJ@G `LJGO@pG,@`J'`V!`JJGVp@JC%@J!E G@@CCLGIPEpJ@LIC@ C%@I L!`]2[2 ^HKJ<C@I@E GLJ!C%C@CPEp@GG.`JJ@GG`QPJGQ@`G!NJ@@ `JN@@GpC,@`G'`Q!`G@C@ILCQCL@I L!@I C%`CIICLLLCI@ C%L!I @`@LCIG@E J!C%C@PEp@G.G`JJ@GG`QPJQG@`@NJG!@ `N@JG@p@C,`G'`Q!`IC@GLC@@ILCQ C%L!@I `CLILIC@LIC @I C%L!`CHK@]2J<[2 ^IC@B$E*>'gB E_@>CE7@G@@B;-B @3'@+>>E>@E/L`@>4`B.`L(@>>4`B.B`LL(`B@J!@pJL>@@J!p@J@I2`O@`L-`C@`II2`O@O`L-L`CC@`]2@HKC[2J< ^@ICE%>*@$BOP>@p>.PB@ E B`LP@@B>L`LI!@EB@IE@E `E@BC,@`G'`Q!`GCCJGE@GQEJC C%@L!I `CLILICCLI@ C%I @L!`@ILC>%E!B@ @P>@p>.PB@ E B`LP@>L@B`LO!J@G `JGL@OpG,@`J'`V!`JJVJ@G`@C,`G'`Q!`LCIG@C@LIC I C%L!PQC[2@ ^J<HK]2BE!@ CI@>%LB@P>@p@C.`EE@G`QPEGC@Q`I@O!L@ `IL@@OpC,@`G'`Q!`LGLCQ@`@E,`I'`S!`LpLES@I@ @=%BE!@=BP@p@C.`EE@G`QPE@GQC`LIO!@@ `IL@@Op@C,`G'`Q!`L@CGQL`@E,`I'`S!`I@S`>.@`B)`L#PEp>>.`B)B`L#L`B@J@p>LJ@J!@p@J@I2`O@`L-`C@pIL0CLI(C,VOI L_@CC@E4@D@B8-B @0'@+>;C@HK]2 ^[2J<BE#@>'@!P@>>.`BB)`L#!E?E@>BL@OpO>@B,@`J8`E(`>8`BJE>@@/MTrk6JAZ2HK @General MIDI4YY @[(](C@J<}lJ<]([(HK@ @CCkC@NEqIq@IJNENE](J<@[(CHK @JECZI`E`CENEJNIJENJNEJJNENEINENJEIBNGJEJNBNGBJJNB>NCZJ>C@NE`I`ENEIJNJ<]([(C@ @HK@JEI`E`ENEIJNJCNJN@NINJCE>NIGJ@>GGN>N>=NEN=E;JBN>NGBN;BB>>J=N>E`I`=ENJNIE @CHK[(@J<](>qBqEBJ>N>GXCk>G@NCEqIq@ENJNIE[(](@ @HKJ<CJEI`E`IEJNENJNENEJEJNJENEJINENGJEIBNBNJNGBJB>NJNJ>IN=NE`I`IIEJNEN]( @C[(J<@HK=JECe@NCEqIq@ENEJNI](J<HK@[( @CEJBk>kBDB>@B@/MTrk&JAZ28General MIDIDrumsYx @[]@HK @}J<#:*'1'C8#1*@* *@*2&6 &*@*#3 #*@#>HK]x*$ @[CJ<8@8#*@* *@*)&3 *&@* *@#78*$8*#@* *@&3*/ *&@#1* #*@*$8#>8#*@* *@*)&36/ &*6@. #+ .#@#7*$81$8*1#@* *@&3*) *&@*#1 #*@8#>*$8#*@* *@&3*) &*@*#+ #*@8#:1'*'8#1*@* *@*2&6 *&@*#3 *#@8*)#C8#*@* *@&8*. &*@* *@#>*,81,8#*1@*" *@*8&; &*@#7*" #*@8*,#F8#*@*" *@&;*1 &*@*" *@#>*,88#*@*" *@&;*8 *&@*'#7 #*@*,8#F8*#@*" *@&;68*1 &6*@#1*" #*@#>8*,8*#@*" *@&;*8 &*@*'#7 *#@#F8*,8*#@*" *@68&;*1 6*&@#1*" #*@8#>*,8#*@*" *@*8&; *&@*"#7 *#@*,&;8#F8*&#&) &&5 &&) &-5 --/ --; --/ -#>8*,8#*@*" *@*8&; &*@*"#7 #*@8&;*,#F8&*#&) &&5 &&) &-5 --/ --; --/ -#>81,*,8#*1@*$ *@*7&B &*@*)#? #*@*7#R88#*@#L*- #*@*D&N *&@*1#@ #*@8&88&@#F #@#I1/ 1# [8 @]@#X1@HKCJ<x81# &I*? &*@#<*. #*@#7*$1$88#*1@* *@&3*/ *&@#1* *#@8*$#>8#*@* *@&3*) &*@* *@#781$*$8#1*@* *@&3*/ *&@#1* *#@#>8*$8#*@* *@&3*) &*@* *@8*$#78#*@* *@*/&3 &*@#1* #*@8*$#>8#*@* *@&3*)6/ *&6@*#+ #*@#78*$8#*@* *@*/&3 *&@* #1 #*@#>8*$8#*@* *@6/&3*) 6*&@*#+ *#@#78*$8#*@* *@*/&3 *&@*#1 #*@*$8#>&38&#*&$ &&. &&$ &-. --) --3 --) -*$8#78#*@* *@&3*/ *&@#1* #*@*$&3#>88&#*&$ &&. &&$ &-. --) --3 --) -8*$1$#78*#1@* *@*)&3 *&@#1* *#@8#>*$8*#@* *@*)&3 &*@#+* *#@8*$#71$8#1*@* *@&3*/ *&@*#1 #*@8#>*$8*#@* *@*)&3 &*@* *@8*$#78*#@* *@&3*/ *&@#1* #*@*$8#>8#*@* *@&36/*) &*6@. #+ .#@#7*$88#*@* *@&3*/ &*@*#1 *#@#>*$88#*@* *@*)&3 &*@* *@#78*$8*#@* *@*/&3 *&@* #1 *#@#>8*$8#*@* *@6/&3*) &6*@*#+ #*@#7*$88*#@* *@*/&3 *&@*#1 *#@*$8#>8*#@* *@&3*) &*@* *. .*$#788#*@* *@&3*/ *&@#1* *#@*$#>88#*@* *@6/&3*) *&6@#+* #*@*$1$#788#*1@* *@&3*/ *&@#1* *#@#>8*$8*#@* *@&3*) &*@* *@#78*$8#*@* *@&3*/ *&@#1* #*@#>&38*$8*&#&$ &&. &&$ &-. --) --3 --) -#71$8*$8#*1@* *@*)&3 *&@#1* *#@ @J<HK[]#>*$xC8@8#*@* *@&3*) &*@*#+ *#@*$8#71$8#*1@* *@&3*/ *&@*#1 #*@#>8*$8#*@* *@&3*) &*@* *@*$8#78*#@* *@*/&3 *&@#1* #*@*$8#>8*#@* *@&3*)6/ *6&@#+. .#@#7*$88#*@* *@*/&3 *&@#1* #*@#>*$88*#@* *@*)&3 *&@* *@#78*$8*#@* *@&3*/ *&@* #1 *#@#>*$88*#@* *@*)&36/ 6&*@#+* *#@8*$#78*#@* *@*/&3 &*@*#1 *#@8*$#>8#*@* *@&3*) &*@* *. .*$8#78#*@* *@*/&3 *&@*#1 #*@8*$#>&38&*#&$ &&. &&$ &-. --) --3 --) -*$8#78*#@* *@*/&3 *&@*#1 #*@*$&38#>8*&#&$ &&. &&$ &-. --) --3 --) -*$8#71$8*#1@* *@*)&3 &*@#1* *#@*$#>88*#@* *@*)&3 &*@*#+ #*@#7*$81$8#1*@* *@&3*/ &*@*#1 *#@*$#>88*#@* *@&3*) &*@* *@*$8#78#*@* *@&3*/ *&@#1* *#@*$#>88#*@* *@&36/*) &*6@#+. #.@*$8#78#*@* *@&3*/ &*@*#1 #*@#>8*$8#*@* *@&3*) *&@* *@8*$#78*#@* *@&3*/ &*@#1* *#@#>*$88#*@* *@6/*)&3 &6*@#+* *#@#7*$88#*@* *@*/&3 &*@#1* #*@*$#>88*#@* *@*)&3 *&@* *. .#78*$8*#@* *@*/&3 &*@#1* *#@#>&3*$88*&#&$ &&. &&$ &-. --) --3 --) -8*$#78#*@* *@*/&3 *&@*#1 *#@&3*$#>88&#*&$ &&. &&$ &-. --) --3 --) -1$8*$#781#*@* *@&3*) *&@*#1 *#@ @[8C@HKx*$]#>J<8*#@* *@*)&3 &*@#+* #*@*'#:1'88*#1@*! *@*2&< &*@*%#; *#@*48#O8*#@#I*+ *#@*A&K *&@*/#> *#@&;88&@#I #@#L11 1# #^1F881# &N*D *&@*1#@ *#@#>81,*,8*1#@*" *@*8&; *&@#7*" #*@#F*,88*#@*" *@*1&; *&@*" *@*,1,8#>8#1*@*" *@*8&; &*@#7*" #*@#F8*,8*#@*" *@*1&; *&@*" *@*,#>88*#@*" *@*8&; &*@*'#7 #*@*,#F88#*@*" *@68&;*1 &*6@#1*" #*@*,#>88#*@*" *@&;*8 *&@#7*' *#@8*,#F8#*@*" *@&;68*1 *6&@*"#1 #*@*,8#>8*#@*" *@*8&; &*@*"#7 *#@8*,&;#F8&#*&) &&5 &&) &-5 --/ --; --/ -*,8#>8#*@*" *@&;*8 &*@*"#7 #*@&;8*,#F8*&#&) &&5 &&) &-5 --/ --; --/ -*,#>1,881*#@*" *@*1&; &*@#7*" *#@HK#F[x*,]J<@C @88*#@*" *@*1&; *&@*"#1 #*@*)#;81)8#1*@* *@&8*5 &*@#5* *#@*'8#A8*#@* *@&6*, *&@* *@#7*$88#*@* *@&3*/ *&@#1* *#@8#>*$8*#@* *@*)6/&3 &6*@. #+ .#@#7*$88#*@* *@*/&3 *&@#1* #*@8*$#>8#*@* *@&3*) &*@* *@*$#788*#@* *@*/&3 *&@#1* *#@#>8*$8*#@* *@&3*)6/ &6*@#+* #*@#78*$8#*@* *@&3*/ *&@#1* *#@#>8*$8*#@* *@&3*) *&@* *. .8*$#78#*@* *@&3*/ &*@*#1 #*@8#>&3*$8&*#&$ &&. &&$ &-. --) --3 --) -*$#788*#@* *@&3*/ *&@#1* #*@8#>&3*$8*#&&$ &&. &&$ &-. --) --3 --) -1$8*$#78#*1@* *@&3*) &*@*#1 #*@*$8#>8#*@* *@&3*) *&@#+* #*@8*$#71$8*#1@* *@*/&3 *&@#1* *#@*$8#>8#*@* *@*)&3 *&@* *@#7*$88#*@* *@*/&3 &*@*#1 *#@8*$#>8*#@* *@6/*)&3 *&6@. #+ #.@*$#788*#@* *@*/&3 &*@*#1 *#@#>*$88#*@* *@*)&3 &*@* *@8*$#78#*@* *@*/&3 *&@#1* *#@8*$#>8#*@* *@&36/*) *&6@#+* #*@*$#788*#@* *@*/&3 *&@#1* #*@#>*$88*#@* *@*)&3 &*@* *. .#78*$8#*@* *@*/&3 &*@* #1 #*@#>*$88#*@* *@*)6/&3 6&*@#+* *#@*$8#78#*@* *@*/&3 &*@#1* #*@8*$#>8#*@* *@*)&3 &*@* *@8*$#78*#@* *@&3*/ &*@*#1 *#@*$8#>&38&#*&$ &&. &&$ &-. --) --3 --) -*$1$8#78#1*@* *@&3*) *&@*#1 *#@8#>@xJ<[ @*$]HKC8#*@* *@&3*) *&@*#+ *#@*$8#71$8#*1@* *@&3*/ &*@*#1 *#@#>*$88*#@* *@&3*) *&@* *@8*$#78*#@* *@*/&3 &*@*#1 #*@#>8*$8#*@* *@&36/*) 6&*@#+. #.@8*$#78*#@* *@&3*/ &*@#1* #*@*$#>88*#@* *@*)&3 &*@* *@#7*$88*#@* *@*/&3 &*@#1* #*@#>*$88#*@* *@&36/*) 6*&@#+* *#@8#7*$8*#@* *@*/&3 &*@#1* #*@8*$#>8*#@* *@&3*) &*@* *. .#78*$8#*@* *@*/&3 *&@* #1 *#@*$#>88#*@* *@*)6/&3 6*&@*#+ #*@*$#788*#@* *@&3*/ *&@#1* *#@#>8*$8#*@* *@&3*) &*@* *@*$#788*#@* *@*/&3 &*@*#1 #*@8#>*$8#*@* *@6/&3*) *6&@#+. #.@#781$*$8#1*@* *@*/&3 *&@#1* #*@#>8*$8#*@* *@*)&3 *&@* *@*$8#78*#@* *@&3*/ &*@#1* *#@*$#>88#*@* *@*)&36/ 6*&@. #+ #.@8#7*$8*#@* *@&3*/ &*@#1* *#@8#>*$8*#@* *@&3*) *&@* *@#78*$8*#@* *@&3*/ *&@* #1 #*@8#>*$8#*@* *@6/*)&3 6&*@#+* #*@8*$#78*#@* *@&3*/ &*@#1* #*@8#>*$8*#@* *@&3*) &*@* *. .#78*$8#*@* *@&3*/ &*@* #1 *#@*$8#>8*#@* *@6/*)&3 *&6@#+* *#@*$1$#788#1*@* *@*)&3 &*@*#1 *#@8#>*$8*#@* *@*)&3 *&@*#+ *#@*'1'8#:81#*@* *@*2&6 &*@#3* #*@*)8#C8#*@* *@&8*. *&@* *@#:*'1'881*#@* *@*2&6 *&@*#3 *#@#>*$88#*@* *@*)&3 *&@* *@*'8#:8#*@* *@&6*2 *&@#3*# *#@8*)#C8#*@* *@*.65&8 6&*@* #. #*@#>8*,8*#@*" *@&;*8 &*@#7*' #*@*,8#F8#*@*" *@*1&;68 *6&@#1*" *#@8*,#>8#*@*" *@&;*8 &*@#7*" *#@8#F*,&;8*&#&) &&5 &&) &-5 --/ --; --/ -1,8*,#>8*#1@*$ *@&B*7 &*@*)#? *#@#R8*78*#@*-#L *#@*D&N *&@*1#@ #*@8&88&@#F #@1/#I 1# ]@HK[ @x8C#XJ<1@81# *?&I *&@#<*. *#@1$8*$#78#*1@* *@&3*) &*@#1* *#@#>*$88#*@* *@&3*) *&@*#+ *#@1'8#:*'81#*@* *@&6*2 &*@*#3 #*@*)#C88#*@* *@*.&8 *&@* *@#:8*'8#*@* *@&6*2 &*@*##3 #*@8#>*$8#*@* *@&36/*) &6*@#+* #*@8*'#:8#*@* *@&6*2 *&@*#3 #*@8*)&8#C8#&*&' &&2 &&' &-2 --- --8 --- -#&*&1&*#1*#11*#/MTrk6JAZ2 @General MIDIPart-01YYx [@]@HK}}C>J<xsnid_ZUPp}>CxsnpE}Cxsnp>}Exp}B>xpBE}xpE}E}EEJ}Exp}GJxpC}GCC}xspC}ExpEB}xpE}BxspEE}}JE}JI}IGGE}}ECxsnp}ECxpE>}xpJ}>IJ}I}GG}G}IGIG}GE}xpCE}xp@C}xsnpE@}xp}EJ}IJI}G}GG}GI}IG}EGxpCE}xp@}CxsnpJ}@xsnid_ZUPp}JCxsnp}ECxsnp}>Exp>}BxpBE}xpEE}}EEJE}xpJ}GxpGC}}CCxspCE}xpEB}xp}BExsp}EE}EJJI}}GIG}E}ECxsnpE}Cxp}JExsnp}JGxp}GI}IIE}Ixp}ECC}CC}@xsnp}E@}EEJE}xp}GJxpI}GII}IE}xpEC}CC}C}@xsp@@}@}>xpJ>}xp}CJxsnp}CExsnp}>Exsnip;>};}<xp>}<xp>>}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0>/MTrk6JAZ2 @General MIDIPart-02YYx h[@]@HKJ<>C}}xsnid_ZU0@}>@}@CxsnpE}CxspEC}@BC`B@`}>@xp>}BxpE}BxpE}E}EE@GE@GJ}JI}G}I@EG}@E}CCC}xspCE}xpB}Exp}EBxsp}EE GE`}IG}II@IG@GE}EE}CE}xsnpC}E}CE@CB`B@`@}>@C>}@I}CI}IIG}}GGG}G}GG}GExpC}E}BC}B@xs0}B@`BC`C}E E}G`I}GII}GI}}GGG}G}GGE}Gxp}CEB}C@}Bxs0@C}`CE`JE}xsnid_ZU0}GJ`EG`E}Cxsnp}ECxsp}CE@CB`B@`}>@xp}B>xp}EBxp}EE}EE@GE@JG}JI}GI}@}GE@E}CCC}xspE}CxpBE}xpB}Exsp}EE EG`IG}II}@IG@G}EE}EE}CxsnpEC}xp}EJxsnp}GJxpG}II}I@GI@G}Exp}CEC}C@C}xs0@B}`BC`E}CE}E@EG@GJ}xp}GJxpIG}II}@IG@}EGxpC}ECC}@C}xsp}@@@>}xp}J>@J}G`EG`}CExsnp}CExsnp>}Exsnp;}>};;};<xp}><xp}>>|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0>/MTrk $YJAZ2}General MIDIPart-03YYx "[@] @HKJ<@C>)B)}xsnid_ZUPpC,}BG,>xsnpE,@,}GCxs0=,}9,E@`}`>,}B,9=xsnp>'B'B>}xsnpG'>B}C'xsnip}CGE'@'xspB'E@}>'xp}B'>'B>xp>>'B'}Bxsp@'B>}E' }E'EC'@`>'C}B'E}B='9'>@=}E'@'9@G'}B'E@xpBG'}GC'xsnp}@'CGE'@}='9'E@`}`B'=>'}9@B>'}@'>@G'C'}@>9'}='GCG'=9}B'BG'GC'}GCE'}@'@EB'}G'xspG'GBC'}xp9'}CG='xsE'=9@'}`@>'E}B'xpG'C'}>B='}9'CG9=B'}G'G'G}C'BGC}E'@'B'@}G'ExspC'G'GB}xp9'G}='Cxs09E'@'}=`}`}B'>'E@xsnid_ZU0}BB,>,>`}`C,}G,B>xsnpG@,}E,Cxs0=,@}9,E`}`}B,>,=9xsnpB'>}>'BxsnpC'G'B}>xsnip}E'CG@'xsp@}B'>'Exp>B}>'B'xpB'>B>'}xsp@'E'}>B E'EC'}@`C}B'E>'>='9'B}@E'}=@'9@@B'}G'ExpC'G'G}BxsnpGC@'}E'xp}E@B'>'xsnpC'G'}B>xp9'}C='G@E'}C'9=@C}B'>'ExpC'}G'B>xpCG9'}='xs}@'E'9=px0C'@E'E}@CE>'}B'xpB>C'}G'xp9'}='CG@E'C'=9}@EB'C}>'xpG'BC'>}xp9'='GC}xsp=99'<'}9B)}>)<xs0>'B>}B'`}`C)}G)B>xsnp}E,CG@,xsnpE>)}B)@xsnp}B)>)>BC,@,}>Bxsp>)}B)C@xp}>B>|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0>/MTrkYJAZ29)General MIDIPart-04YYx ^[@]@HKJ< @C}B)}xsnid_ZUPp>,;,9}Bxsnp>;9,}@,xsnidp}9)>)9@xsnp}='B'>9=B9'>'}@}@}9>'>;'>'9'};>>'}>9C'@>'C;'>}Pxp9';@'}>xsp>'9'}@9xsnid_p@'9'>}9 }pxsp;'}9@B'}9';>'Bxsnip9@'>}9'xs0@99'>'}@9'>>'9}xpB'};'9>B;>'}C'9'}@'>C};'@9B'B}>'9';xsnp9@'9'}>xsnp}9'>'@9xp9>9'}>'xp};'B'9>}>'BC';@'C>}9'B'9@;'};>'9'B}xsnp}>9'@'9xsnid_ZUPKFA0}>,9,9@`}pxsnip@,}9,9>xsnidp>)}9)@9xsnp9B'='}>>'}=9'B@}@9>;'}>';>9'}>'}C'>9>'@C>>'};'Pxp>;9'@'}xsp>'@99'}xsnid_p9'}@'>9 }pxsp;'9@}B'B;9'}>'xsnip@'9}>9'xsnidp}>'C'9@xp@'9'>C}xp@9'}>'9xsnp9>}9'@'xsnid_Zp@}>'C'9xp>C}9'@'xp9@}>'9'xsnp9>@'9'}xsnid_0}@9>'9'`}pxsnip9,>}9@,xsnid_Zp@}9>)9)@,};,>9C)<)};@xp9)}B)C<xp>9}B|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0>/MTrkrJAZ2YGeneral MIDIr( @ @yxLast journey of the Niagara/simutrans-124.5/simutrans/music/15-The-Wayside-Blues.mid000066400000000000000000000316241520675460500230500ustar00rootroot00000000000000MThdMTrk By Copyright 2006 All Rights Reserved Generated by NoteWorthy ComposerQ X X@X /MTrkG!Staff-4 @@Hn>HHn^HFn>FCn^CCn>CCCn^CCn>CCn^C?n>?CCn^CAn>ACn^C?n>?CCn^CCn>CCn^C?n>?GJn^JJn>JLn^LGn>GCn^CBCn^CHn>HHn^HFn>FHn^HEn>EAnAHHn^HFn>FCn^CCn>CCCn^CCn>CCn^C?n>?CCn^CAn>ACn^C?n>?CCn^CCn>CCn^C?n>?GJn^JJn>JLn^LGn>GCn^CBCn^CHn>HHn^HFn>FHn^HEn>EAnAHHn^HFn>FCn^CCn>CCCn^CCn>CCn^C?n>?CCn^CAn>ACn^C?n>?CCn^CCn>CCn^C?n>?GJn^JJn>JLn^LGn>GCn^CBCn^CHn>HHn^HFn>FHn^HEn>EAnAGJn^JJn>JLn^LGn>GCn^CBCn^CHn>HHn^HOn>OOn^OOn>OHnHHn^HHnPHPKn>KJnpJ/MTrkv!Staff-2 @@ACG"$n$`n^"n>"$n$`n^"n>"$n$`n^"n>"$n$`+nP+0$n$ (n( +n+ (n ( +n^+)n) -n- 0n0 -n - 0n^0$n$ (n( +n+ (n ( +n^+$n$ &n& 'n' (n( )n) -n- 0n0 -n - 0n^0)n) -n- 0n0 -n - 0n^0$n$ (n( +n+ (n ( +n^+$n$ &n& (n( )n) +n+ &n& +n+ /n / 2n^2)n) $n$ )n) -n - 0n^0$n$ (n( +n+ (n ( +n^++n^++n^++n^++n^++n^++n^++n^++n^++n^++n^++n^++n^+$n$ (n( +n+ (n ( +n^+)n) -n- 0n0 -n - 0n^0$n$ (n( +n+ (n ( +n^+$n$ &n& 'n' (n( )n) -n- 0n0 -n - 0n^0)n) -n- 0n0 -n - 0n^0$n$ (n( +n+ (n ( +n^+$n$ &n& (n( )n) +n+ &n& +n+ /n / 2n^2)n) $n$ )n) -n - 0n^0$n$ (n( +n+ (n ( +n^++n^++n^++n^++n^++n^++n^++n^++n^++n^++n^++n^++n^+$n$ (n( +n+ (n ( +n^+)n) -n- 0n0 -n - 0n^0$n$ (n( +n+ (n ( +n^+$n$ &n& 'n' (n( )n) -n- 0n0 -n - 0n^0)n) -n- 0n0 -n - 0n^0$n$ (n( +n+ (n ( +n^+$n$ &n& (n( )n) +n+ &n& +n+ /n / 2n^2)n) $n$ )n) -n - 0n^0$n$ (n( +n+ (n ( +n^++n^++n^++n^++n^++n^++n^++n>+n^"n>"$n$`n^"n>"$n$`+nP+0$n$ (n( +n+ (n ( +n^+)n) -n- 0n0 -n - 0n^0$n$ (n( +n+ (n ( +n^+$n$ &n& 'n' (n( )n) -n- 0n0 -n - 0n^0)n) -n- 0n0 -n - 0n^0$n$ (n( +n+ (n ( +n^+$n$ &n& (n( )n) +n+ &n& +n+ /n / 2n^2)n) $n$ )n) -n - 0n^0$n$ (n( +n+ (n ( +n^++n^++n^++n^++n^++n^++n^++n+ &n& +n+ &n& +n+ /n / 2n^2)n) $n$ )n) -n - 0n^00n 0 0n^0.n . .n^.-n - -n^-,n , ,n^,+n+ %n>%$np$/MTrk!Staff-1 @*n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^*9n 9 *n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^*9n 9 *n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^*9n 9 *n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^*9n 9 *n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^**n>**n^*9n9 9n>99n^9/MTrk !Staff-3 @&n^&&n^&&n^&&n^&&n^&&n^&#n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^#&n^&&n^&&n^&&n^&&n^&&n^&#n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^##n>##n^#&n>&#n^#/simutrans-124.5/simutrans/music/16-Midnight-Express2.mid000066400000000000000000001217331520675460500231330ustar00rootroot00000000000000MThd xMTrk( 2006.10.06Q\/MTrk= @General MIDI0Copyright (C) 2006 shunterJAZ2~j @@ ~j11'~j.@@ '~j @1@ @@.~j@1@ ~j' @@ ~j @@ ~j @@ @~j@ @@ ~j @@ ~j @@ ~j~j@@ @@ ~j @.j@ jGVjXYMidnight Express/MTrk @General MIDI JAZ2{@][(Z @Bass8!Pk!!Ok!!Kk!!Vk!!Wk!!Pk!!MC!-^- !Xs!&QR&!WT!&L`&!T2<&+@p+/=|2U&/V'E2r'&Nt&*Gx-I&*N!HV-&&G"!^&P&l,>F&2*N",\*+Nt+/Gx2I&/N&HV2&+G"&^++Pl'>F+2%N"'\%&Xj& &;&H&&P&&[N&#Xj# #;%H&#P%&[N&(Nt,G(x/I&,N#HV/&(G"#^((Pl">F(2 N""\ !Nt!%Gx(I&%NHV(&!G"^!!Pl'>F!2%N"'\%&Nb&&4|#L&x!N#u!&Bp&*X|*-Lt&;-y&&Ex&*Xt+I"*V,C+|-R,V%?"-Z%&?t,O2&I,+Vp+/Wx2Q"/R/KJ22+K/x+(_x(&Cx+S&~+&Ex*X&t+I"*V,C+|-R,V%?"-Z%&?t,O2&I,+Vp+/Wx2Q"/R/KJ22+K/x+(_x&C(x&+S~+&G|&*Wb*-Up$F&-X$#Or##B#&Rl*Q&v*Xj ;H&P[N!Xj! !;#H&!P#%[N%&Y&Xt!Pt&X!w&&Y&Xt!Pt&X!w&+Vp+/Wx2Q"/R/KJ22+K/x+(_x&C(x+S&~++Vp+/Wx2Q"/R/KJ22+K/x+(_x&C(x+S&~+&G|&*Wb*-Up+F&-X+*Or**B*.Rl1Q.v1#Nt#&Gx*I&&NHV*&#G"^##Pl)>F#2'N")\' (Gp,L!(S/LF,6#G*/J(D"#^(?6(,((3n( (=b((Gx,W.(N/@.,B#E>/<#(@p(,=|/U&,V"E/r"!Ex!%Xt&I"%V'C&|(R'V ?"(Z!? t"O2!I"!Ex!%Xt&I"%V'C&|(R'V ?"(Z !?t'O2!I'&Gx*W.&N-@.*B!E>---<=M9+B+Q>B-9=C0;0@0>0YC>@;*7 =<9M6+@+P*@=96;0C0@0>0Y@;>C27 ><=M9+B++=9B>2 C0;0@0>0eC;>@ *7,=<9M6+B+j*96=BU>0;0@0C0Y;>@C27>2>9B;92E.>B9EE@>;B:92BBE9>27EBA9.E+>;)>2>?tE>9B +7>777;7+;>7;27EBA9.E+>;)>2>?t9B>E +7G3C0>8k@xx6+GC>z>?@C< >CR23@MB=>?9.E%9E>BhE@>9B49>A2!>B9E>767;7IBDz66B;3 >6;6 ;QD7@7;7>7474;@D> -7914)7>=8G@:-D46467X978l9D`9=7497@>767272;>96B0E3>827J@xx82EB>x>?@B< >B1C1@>>+7G8;)G@:+D;6;6>XC>8lCDCC>;G >>@9)27B1E8G@:2D9696>XB>8lBDCBE9> >8+7@C0G3k@xx6+GC>z>?@C< >CR@23MB=>?9.E%9EB>hE@>9B49>A2!E>B9>767;7IBDz66B;3 >6;6 ;Q(3M;=8?4.>%4>8;h>@;44>89<;8>4(-77C-=59242F7-=4927>C E5B292"B9>2E 272B592F2<B>9C1;)G8>>+7&@<+D;4;6>XC>8lCD`CG>;;7+7@77>7+;7;>23MB=>?9.E%9EB>hE@B4>99>A2!9B>E*7:C =2@562H@:*6=>7/767;7/;;>68>(7;1>84)&@<(D44468X;88l;D`;8>4>7@47;7D7@74>@D; -7914)=87>G@:-D46467X978l9D`9=747>-7=8@4)91&@<-D44467X978l9D`9=7427@E8B19)>>&@<2D9496>XB>8lBD`BE>9E3@B0>827J@xx82EB>x>?@B< >BR@+7ECA;.G+>;)>+>?C;G>27EBA9.E+>;)>2>?9B>E+7>+>9C;G.;2;C>GG@>;C:;2BGC>;27>C E5B292"2>BE9 ;7>767jBDz66B;3 6>;6 ;0(78C->5;242F(8>;4-7T44o-7DW7#964@/F@9/27EBA9.E+>;)>2>?t9E>B E<2;9->BB5G@:2D969:>XB>92;@E7B4>x>C@B@ >BR><@+;C4G7J@xx8+GC>x>C@C@ >CR@2;>2B?>=96E2B>9EED>?B>96BE9> >;7;;;+;+;;>72;>G E9B696">BE92 /8n>B;D63B*6B>;hBE;>>96C?/#6>;B(;E;E42>/8?)8(8C84>;957B=<-;4-&@<-D444:7X97?)>2>C>E9B><B4E72;J@xx82EB>x>C@B@ >BR+;@ECE;2G/>?)>+>C;>GCB;9;>;2;2;9B>>;7;+;;;+;>7;28MBB>D93E*9E>BhEE9CB9>>A2!>BE969;9>9IBFz68B;5 6;>6 ;Q(8M;B8D43>*4>;8h>E;94C8><>8;4(-;7G-=99646F7-=942;>G E9B696"9>EB2 2;696"2B<9> +;fCE;2G/>?)>+>C;GC>G<>B;-+;C5&@<+D;4;:>XC>;@2;>G B6E996"2>EB9 *8n=B:D63@*6@=:h@E=96C:>?*#@6:=/;>4B7;;x;C@>@ ;>1(;@](;?8=>2468;4>>D8?;>46>84; 8B><4-;5(;G@:(D464:8X;8 -;@f9E42=/7?)7-7C=479-;E9E42=/7?)7-7C=4972;EBE92E/>?)>2>Ct9EB> 2;]2B?>=E296B9>EED>?B>96BE>9BG7+;C4>x>C@C@ >C1B59->BE<2;@G@:2D969:>XB>9 +;@G<>B;-C5G@:+D;6;:>XC>;C 2;@>G-E9B696F>2EB9/;T68o/;HW;#>:6B3FB>/(;8G >9;646H>(84;-;T48o-7HW7#9:4@3F@9/B4E7><2;J@xx82EB>x>C@B@ >BR>7B72797@2>B9 >72797B72;>B9;7>7+777+;;7>>>E827B19)&@<2D9496>XB>8lBDCE>B9 +7@fCA;.G+>;)>+>?C>G;27>C E5B292H92>EB/7T64o/;DW;#>66B/FB>(7](;;8942>.8;4>>@8;;:42B48>;E7=7@7C7-7-;C@=E27EBA9.E+>;)>2>?>B9E27E3B0>8J@xx82EB>x>?@B< >BR77>7@;7+7+;;>7>8E3B027J@xx82EB>x>?@B< >BR+7@ECA;.G+>;)>+>?;GC>23MB=>?9.E%9EB>hE@B4>99>A2!9EB>/3M>=;?6.B%6B>;hB@>4;96>A/!6>;B(78C ;2>542";4>(8 -77C-=59242"-79=4 23nB=>?9.E%9E>BhE@B4>99><9>BE2<7E7>7B7jEDz96 E<3 <>BE9 <Q+7ECA;.G+>;)>+>?>;GCG3>8C0+7J@xx8+GC>x>?@C< >CR@23MB=>?9.E%9E>BhE@9>>9B4A2!B9E>@7B7F7=7IBDz66B:3 @=BF6 :Q;>/7>16)B8&@;8l>DC6>;B @(7f;A4.>+8;)8(8?4;8>(7E;A4.>+8;)8(8?>4;8-7E9A4.=+7;)7-7?t=749 E7@7C7-7=7-;C@=E679727>72;9>627E3>8B0J@xx82EB>x>?@B< >BR@+7>+C;>9;2G.C>;GG@>;C:;2;C>G E827B19)>>G@:2D9696>XB>8lBD`BE>9>>C1+7@G8;)&@<+D;4;6>XC>8lCD`C>G;23@MB=>?9.E%9E>BhE@B4>99>A2!B>9E>767;7IBDz66B;3 6;>6 ;Q(78C ;2>542H8(;>4-7T44o-7DW7#964@/F@9/27>C E5B292H>2EB927>C E5B292"B9E>2 2>;>,B,9, 2AB-H9>x2>-B+>+9+T>B29&>BE>E9E09>B&*@/MTrk.u @General MIDIJAZ2{@Guitar[A] T22+93=3B3E32=9EBJ/(4+73>3@34/>7@J*(4393=3=*94J/(4+73>3@3@>47/J2+93=3B3E39=2EBF4'7.>.@.>47@G*$1'4.9.=.B.;4=*19B&4'7.>.@.>@47G2+93>3B3E3E>B29F2)91>1B1E1A22)>BE991>1B1E19>E2B~2%9,>,B,E, >9B2EF2)91>1B1E1E9B>2N2+93>3B3E3B9E2>J2)91>1B1E1'BE2>9?2%9,>,B,E,@991EB>2>1B1E19BE>OE#B(>(9#2)9>91>1BEB1E1>E2B9N+(2+73;3>3C3C7;+2>F+'2)71;1>1C1@++'2C;>72)71;1>1C1+C;72>~+#2%7,;,>,C,C2>7;+F+'2)71;1>1C1;7+2>C,23 22'6.9.>.B.<2B9>6*2'6/9/>/B/,B9>26:2+6393>3B38>B692 23 22+6393>3B30B26>96+(2+73;3>3C3C;>72+F+'2)71;1>1C1@+2+'C>7;2)71;1>1C1;+2>C7~+#2%7,;,>,C,7>C;2+F+'2)71;1>1C1;7>C+2,23 22'6.9.>.B.<29>B6*2'6/9/>/B/,6>92B:/(6+;3>3B3G3/GB6;>F/'6);1>1B1G1@/6/';B6)>G;1>1B1G16>G;/B~4'8.>.@.<@48>*4'8/>/@/,8@>4:4+83>3@384>@8.4+83>3@308@>46-(4+73=3@3E37=-4E@F-'4)71=1@1E1@-4-'=E@74)71=1@1E1@4=-E7~-#4%7,=,@,E,4E@7=-F-'4)71=1@1E1-47=@E,23 22'9.>.B.E.<>29EB*2'9/>/B/E/,EB92>:2+93>3B3E38BE29> 23 22+93>3B3E30B9>E223 22'6.9.>.B.<296B>*2'6/9/>/B/,9>6B2:2+6393>3B3869B>2 23 22+6393>3B30B>2966+(2+73;3>3C3;C>2+7F+'2)71;1>1C1@+2+'7>;2)C71;1>1C17;>2+C~+#2%7,;,>,C,>;72+CF+'2)71;1>1C12+C>7;N2+6393>3B32>69BF2)6191>1B1A22)>9B66191>1B1B692>~2%6,9,>,B, 6>29BF2)6191>1B196>B2J2'7.;.>.C.>;27CC2'7/;/>/C/>2C;7D2+73;3>3C37C;2>E2+73;3>3C327;>C@6.9.>.B.>6B9C6/9/>/B/B>69H/(6+;3>3B3G3>;/GB6F/'6);1>1B1G1@/6/';>BG6);1>1B1G1;/>B6G\43 44'8.>.@.<>@84*4'8/>/@/,84>@64'7.=.@.E.7=@4EC4'7/=/@/E/=4E@7&23 22'6.9.>.B.<2B69>*2'6/9/>/B/,>B96266.<.>.<>6C6//6<>H+(2+73;3>3C372+>;CF+'2)71;1>1C1@+2+'C7;>2)71;1>1C1;7+C>2~+#2%7,;,>,C,>C2;+7F+'2)71;1>1C1>;7+C2J2'7.;.>.C.;7C>2C2'7/;/>/C/C>72;D2+73;3>3C372C>;E2+73;3>3C3C>27;D2+93>3B3E3E9>B2F2)91>1B1E1A22)BE9>91>1B1E1BE92>~*$1'4.:.=.B.;:14B=***%1'4/:/=/B/+B41=:*/3 //'6.;.>.B.6/;*/'6/;/>/B/,B>6/;:/+63;3>3B38;6B>/ /3 //+63;3>3B306/;>B64+83>3@3>48@F4)81>1@1B44)>@881>1@1>@48~4%8,>,@, 48@>F4)81>1@148>@N4'8.>.@.<48>@*4'8/>/@/,>84@:4+83>3@3848>@.4+83>3@3084@>6-$4'7.=.@.E.;7E@4-=*-%4'7/=/@/E/+=74E@-:-(4+73=3@3E37=74E-@.-(4+73=3@3E3/=7-@E424'7.=.@.E.=7@4EC4'7/=/@/E/7E=@4D4+73=3@3E3@=47EE4+73=3@3E34E7=@D2'6.9.>.B.<>2B69*2'6/9/>/B/,6B9>2:2+6393>3B38>269B.2+6393>3B30629>B29.>.B.E.>9BEC9/>/B/E/B9E>D93>3B3E3>BE9E93>3B3E3 9>EB@2'7.;.>.C.C2;7>C2'7/;/>/C/7>;C2D2+73;3>3C3>2;7CE2+73;3>3C32>7C;D2+6393>3B39>62BJ2)6191>1B1'29B>6?2%6,9,>,B,@661B92>91>1B196>BOB#>(9(6#2)661991>B>1B1>9B62N+(2+73;3>3C37;>+2CF+'2)71;1>1C1@+2+'>7;C2)71;1>1C17;>+2C~+#2%7,;,>,C,+2;C>7F+'2)71;1>1C17;>C2+N2+93>3B3E392E>BJ2)91>1B1E1'B9>E2/0 //$6';.>.B.G.;;>B/6G*/%6';/>/B/G/+G/;B6>:4'8.>.@.<>@84*4'8/>/@/,84>@-0 --$4'7.=.@.E.;47=@-E*-%4'7/=/@/E/+-=@4E7:2+6393>3B3>296BJ2)6191>1B1'2B6>9?2%6,9,>,B,@661B>9291>1B19B6>OB#>(9(6#2)661991>B>1B19>26B&2'9.>.B.E.4EB29>^2&9->-B-E-CBE9>2P2'6.9.>.B.469>2B^2&6-9->-B-?+!2>692#B7*;*>*C*7C>+;2+#2%7,;,>,C,7;>+2C+27%;%>%C%#7;>+2C>2#9*>*B*E* 9>E2B2%9,>,B,E,2B9>E29%>%B%E%$29BE>>+$2'7.;.>.C.F++(&773N;;8$+d+'7+.;&;*78;72>C26#9#>#B#V>296BS/#x;2x>'(>;/n4'8.>.@.H44+&883N>>8$4d4)84.>&8>*88>@-47%=%@%E%&77)-@=4E =.x@(n2'79.@=>.B.E.G22+&993N>>8$2d2)92.>&9>*89>EB2#6*9*>*B* 9>26B2%6,9,>,B,9>26B26%9%>%B%$B2>69+$2'7.;.>.C.37;>+2C^+$2&7-;->-C-<22#7+;9*C>>*B*E* >B29E2%9,>,B,E,9>E2B29%>%B%E%$>B9E2>+!2#7*;*>*C*+27>C;+#2%7,;,>,C,7;>+2C+27%;%>%C%#C72>;+>29%>%B%E%VEB>92S;) >.x;>n4'8/>/@/@88'X@>84 -$4'7.=.@.E.3-47@E=^-$4&7-=-@-E-\42&79--@=E>-B-E-M2299#BE><#B#E#O+!22#97*B<E;*>*C*27;C+>+#2%7,;,>,C,7;>+2C+27%;%>%C%#27;C+>>+$2&7-;->-C- 7;>+2C+(2+73;3>3C3 +27>C;+(2*72;2>2C2;+C72>J2&9->-B-E-V92EB>S*#x42x:'(:4*n/#6*;*>*B* 6>/B;/%6,;,>,B,>/;6B/6%;%>%B%$/6B>;>4&8->-@-@>844+83>3@3@>844*82>2@2@>84J48%>%@%(88)@>4 >.x@(n-$4'8>7.@=.@.E.F--(&773N==8$-d-'7-.=&7=*8=7E@4m-$4'7.=.@.E.3@E-7=4^-$4&7-=-@-E-<42&7-@6-=E9->-B- >B6922+6393>3B3 9>26B2*6292>2B2>B692J2&6-9->-B- 69B2>2+6393>3B3 9>6B22*6292>2B26>B29J+$2'7.;.>.C.F++(&773N;;8$+d+'7+.;&7;*8;72>Cm2'9.>.B.E.429BE>^2&9->-B-E-?+$2B>2'9E7.;.>.C.F++(&773N;;8$+d+'7+.;&7;*87;C2>26%9%>%B%VB>962S;) >.x>;n4&8->-@-P-%44'8@7/>=/@/E/>77'X4=7E-@62#6*9*>*B* 9>26B2%6,9,>,B,692>B26%9%>%B%$69>B223 22'9.>.B.E.<>B29E*2'9/>/B/E/,>B29E:2+93>3B3E38EB2>9 23 22+93>3B3E30EB>9229.>.B.E.B>9EC9/>/B/E/EB>9D93>3B3E3EB>9E93>3B3E3 EB>9"+0 ++$2'7.;.>.C.;C27;>+*+%2'7/;/>/C/+;C+27>:+(2+73;3>3C3727;>C+ +0 ++(2+73;3>3C3/>C+7;262'6.9.>.B.<9>26B*2'6/9/>/B/,B29>6:2+6393>3B389>26B.2+6393>3B3069B2>22'7.;.>.C.;>27CC2'7/;/>/C/;>27CD2+73;3>3C3>C7;2E2+73;3>3C3;>27CD2'9.>.B.E.2*2'9/>/B/E/,>B9E2:/$6';.>.B.G.;>BG6;/*/%6';/>/B/G/+>/6;GB43 44'8.>.@.<@>84*4'8/>/@/,>84@:4+83>3@38>@48 43 44+83>3@3048>@6-$4'7.=.@.E.;7=@-4E*-%4'7/=/@/E/+@E-7=4:-(4+73=3@3E37E-4=@7.-(4+73=3@3E3/47=E-@62+93>3B3E3>B29EF2)91>1B1E1A22)>EB991>1B1E1>B29E~2%9,>,B,E, BE9>2F2)91>1B1E1>B29EN2'6.9.>.B.<>B692*2'6/9/>/B/,9>26B:2+6393>3B3826>B9.2+6393>3B309>6B26+$2'7.;.>.C.;27;C+>*+%2'7/;/>/C/+7;>+2C:+(2+73;3>3C3727;C+>.+(2+73;3>3C3/7;>+2C62'9.>.B.E.<>B29E*2'9/>/B/E/,>B29E:2+93>3B3E38>B29E.2+93>3B3E30BE>296+$2'7.;.>.C.;7;>+2C*+%2'7/;/>/C/+7;>+2C:+(2+73;3>3C37+27>C;.+(2+73;3>3C3/;+C72>26.9.>.B.96B>C6/9/>/B/>B96H/$6';.>.B.G.;6;B/G>*/%6';/>/B/G/+/G>;6B68.>.@.>8@C8/>/@/@>8H-$4'7.=.@.E.;=@-74E*-%4'7/=/@/E/+-E=74@69.>.B.E.EB>9C9/>/B/E/EB>9D6.<.>.><6C6//6<>&+0 ++$2'7.;.>.C.;7;>+2C*+%2'7/;/>/C/+C+2;>7:+(2+73;3>3C377;>+2C +0 ++(2+73;3>3C3/+C;72>+0 ++$2'7.;.>.C.;7>C+2;*+%2'7/;/>/C/+C27;>+:+(2+73;3>3C37+7;>C2 +0 ++(2+73;3>3C3/27;C+>62'6.9.>.B.<9>6B2*2'6/9/>/B/,B69>2:*$1'4.:.=.B.;*4:=B1**%1'4/:/=/B/+B14:=*/3 //'6.;.>.B.<;>/6B*/'6/;/>/B/,;>/6B:/+63;3>3B38B>/;6 /3 //+63;3>3B30B>;6/28.>.@.@>8C8/>/@/>@8D83>3@3@8>E83>3@3"@>8D4'8.>.@.<84@>*4'8/>/@/,@>84:4+83>3@38@>84.4+83>3@30>@486-(4+73=3@3E37=@-4EF-'4)71=1@1E1@-4-'@=7E4)71=1@1E1-47@E=~-#4%7,=,@,E,7=@-4EF-'4)71=1@1E1@E-7=4J4'7.=.@.E.=@47EC4'7/=/@/E/@E7=4D4+73=3@3E3=@47EE4+73=3@3E3@E7=4D2+93>3B3E3>B29EF2)91>1B1E1A22)>EB991>1B1E1>B29E~2%9,>,B,E, 29BE>F2)91>1B1E1>B29EN2'6.9.>.B.<26>B9*2'6/9/>/B/,9>26B:2+6393>3B3869B2>.2+6393>3B309>6B26+(2+73;3>3C37;>+2CF+'2)71;1>1C1@+2+'>;7C2)71;1>1C127;C+>~+#2%7,;,>,C,7;>+2CF+'2)71;1>1C1C+2;>7N2'9.>.B.E.<>B29E*2'9/>/B/E/,BE9>2:2+93>3B3E38>B29E.2+93>3B3E30BE9>222'7.;.>.C.;>27CC2'7/;/>/C/27>C;D2+73;3>3C3;>7C2E2+73;3>3C3C27;>"23 22'6.9.>.B.<9>26B*2'6/9/>/B/,9>6B2:/(6+;3>3B3G3;>B/6GF/'6);1>1B1G1@/6/'B;6)G>;1>1B1G1>/G;6B~4'8.>.@.<84@>*4'8/>/@/,>@48:-(4+73=3@3E3-47@E=F-'4)71=1@1E1@-4-'@=7E4)71=1@1E1@E-7=4~2'6.9.>.B.<9>26B*2'6/9/>/B/,B29>6:2+93>3B3E3>B29EF2)91>1B1E1A22)B9E>91>1B1E1>B9E2V229;>;B;E; >E29BT23 22$9+>+B+E+>B29EN2,94>4B4E4E2>B9,299D>DBDED$92E>Bx23#2U@/MTrkF @General MIDI0JAZ2Strings/MTrk ( @HGeneral MIDIJAZ2[P{Melody J]IBzREoBE Bl3B?@eN@>u>P@c@PEc >eE&>M=K.=J>\<><=R@=>a0>HBh(EOB@E*GixJr GJZLeQJ`LGSJ,GEz.C]EqEnC-E$GdVGI=)LmIuJaLHEkJbEBm @m B&@E>m.>BGPFRGFGpOGJe@J`G{PF\ GFGp,G$JwJGz(G!Bv0BH@l@BcG@S B@BmQB'CpTElCE Bm(B(ADA BY>B@hM@>XK>;T0;b;f; He&HG[y@LGgG\@eFb GEEZF'E]BrJB A6$AB`QB9h9BkRBAO$BdAQBEiEh>i>BsJB.EoCE5JnPJ(IjNIJp%JSIk(GdI3G.JpPITJI J^HI] JIJaxCkJbGe CkJhGuEkJEEEjvEG`n+>B>d>GjTGJfJIiPI)Ge4G9\9Bk4B"@`%@>dO9=>)9NBZ)Gh B]G EfTB^E#Bh@mQ@?@$?@kR@Bk$D`BOIrDIYHK*GeHkGCeUC@et@Ev|E!BhFB EbEBX)B&Ba'@fB@0>I(>B[@`@FBEdCEE[OEE>f:><>aT;8 >@[;I@;J%>];N;V>>;GGPFRGFGpOGJe@J`G{PF\ GFGp,G$JwJGz(GJEku>hEL@k>E@0Bc2B.Il%Jr I&JGJk6JBJrKJIQ%G^I/GBr+BMBr*BNBrcB:@]Z@Gm@FOG6G\FGFHG!GdFlG @jzDm@sGhD9G ;m ;Bm4B@c&>Z@2>;l;pEzPGVEEp G&EHBs@BEh(AxE=A @rp9{@69aCm1CC`(BjC%B$BYB CmJC,EeEE Bm?B9@lp>m@H>@kK@Bm*B'Bm(@^B*@%@m(>m@&>(9mJ9.9l!9Bm9B=@mF@2>m+>'>X(>Bml>Cx7CAEi,ELGm7GAGhEG JlGJ1Ij(Jn I3J:Bt9BAp(BjA6BAu&ABXxElB,EHBsP@aB">l@,>>JpPITJI J^HI] JIJaxCkJbGe CkJhGuIoJ?JV IJLiPJa LJE[PGS EIwGOG[ IOG?L{CJK LJLpQLJf'GmJ/GJY#JIvCI8Ee=E:GeQGEj(GRE+G&>[T>)B]RBBe$BGXRGGe$GEf^EBzREoBE Bl3B?@eN@>u>P@c@PEc >eE&>M=K.=J>\<><=R@=>a0>HBh(EOB@E*GixJr GJZLeQJ`LGSJ,GEz.C]EqEnC-E$GdVGI=)LmIuJaLHEkJbEBm @m B&@E>m.>BGPFRGFGpOGJe@J`G{PF\ GFGp,G$JwJGz(G!Bv0BH@l@BcG@S B@BmQB'CpTElCE Bm(B(ADA BY>B@hM@>XK>;T0;b;f; He&HG[y@LGgG\@eFb GEEZF'E]BrJB A6$B`AQB9h9BkRBAO$BdAQBEiEh>i>BsJB.EoCE5JnPJ(IjNIJp%JSIk(GdI3G.JpPITJI J^HI] JIJaxCkJbGe CkJhGuEkJEEEjvEG`n+>B>d>GjTJfGJIiPI)Ge4G9\9Bk4B"@`%@>dO9=>)9NBZ)Gh B]G EfTB^E#Bh@mQ@?@$@k?R@Bk$D`BOIrDIYHK*GeHkGCeUC@et@Ev|E!BhFB EbEBX)B&Ba'@fB@0>I(>B[@`@FBEdCEE[OEE>f:><>aT;8 >@[;I@;J%>];N;V>>;GGPFRGFGpOGJe@J`G{PF\ GFGp,G$JwJGz(GJEku>hEL@k>E@0Bc2B.Il%Jr I&JGJk6JBJrKJIQ%G^I/GBr+BMBr*BNBrcB:@]Z@Gm@FOG6G\FGFHG!GdFlG @jzDm@sGhD9G ;m ;Bm4B@c&>Z@2>;l;pEzPGVEEp G&EHBs@BEh(AxE=A @rp9{@69aCm1CC`(BjC%B$BYB CmJC,EeEE Bm?B9@lp>m@H>@kK@Bm*B'Bm(@^B*@%@m(>m@&>(9mJ9.9l!9Bm9B=@mF@2>m+>'>X(Bm>l>Cx7CAEi,ELGm7GAGhEG JlGJ1Ij(Jn I3J:Bt9BAp(BjA6BAu&ABXxElB,EHBsP@aB">l@,>>JpPITJI J^HI] JIJaxCkJbGe CkJhGuIoJ?JV IJLiPJa LJE[PGS EIwGOG[ IOG?L{CJK LJLpQLJf'GmJ/GJY#JIvCI8Ee=E:GeQGEj(GRE+G&>[T>)B]RBBe$BGXRGGe$GEf^E/MTrk6 @General MIDIJAZ2@ E{[P]SolouC;g%>j;>@t@ B&@uB>w@'>9g297}'76VO69PT6^96G;~5;>t>=m2=9u!97[37;p.;9f(96g&6 4U847n)7 6Y46 2X421K@14e(4 2V52/W)/-26h 6 9n@96H 67EJ7;d;>pI>;:";=fG=W<(<=u0=@ @>z3>996sM2P627}H7 55 6`$9`6 =l9=@F@>{/>DI{+IVBr"BUAhAB\DB0GwJG*>T]>=IX>m=)>6A!A BVCB En)EBJGB)=VA=7;Yy9H ;I;O9 ;dI=IGu(GETQG`E%ItGGI JpnJ0Gy\GBYNB Bh&Br@IC@ Bj&BET:EGz(G Ir(ILr4G^ L6GEI$EGi:GJn2ETJ(EBE)GqB4GEq2BWE=B@F @Cd5CEw)E BQ2B>X0=Q>3=@k/>V@1>9= 9 :F8:=u'=;SF; 6:"69@:98`&87B;7 6b%6 9Q596R+62HQ24[^44ac42*4E2M46X16]9SJ909TI939\Q9'>fQ>'9[Y97\U76NA6/7`'7 ;F=;>T%>9W09;c/;9H197`,768667d.76F>64Z)42O=2 4K,46E169s9;=;!=~C= :I:;0=S;#=>s">@t1@"AVA Bk=BAk&A@L9@ >i,>=BM=;xA;:4QQ4(;nK;9yP9.4PQ9j 4i94.:k4R:%;pC;/6UZ6(;v;l1c'14p843h(32W)24g06Y4-69(97^7#9g,9;c;;>> @e8@>i%>=HG>b= >Ch4CBk"B A^1AB_,BF`8FJrJNq4NMu'M Ld6LKx&KJ\5JHe&HI`KI Lt LGSTGE'EGZGhGkDG HZ,IcHI7Lj3IQLLI!Ie[GSIoGIgSILb!IVLQLfI]Ly=Q+=>B9>Ba%B DZ.DIw&I GL=GB:'IsBbIGVWGBK)BJBfB C>"CGHGI^INtUNL@LNcUNLa%L4Lp KZL Jp K&JNz"NIb2IGe*GF^2FD[$DEi:EDt+DCR.CGh*GBU+BAs0A@W8@ >q'>=U4=:j-9N :79 7m+76SQ9g6*9;[5;>y'>=FG>f=>@tM@=)=>rS>=?!>q=A>@q%@BN6BC}$CD^9DGw"G EH3EGgGEY/ECb'CBDBETBELi=LIIIJcRJ GV$GSJz%JL>J:LGSJ&EfG4EC`'CB>RBE6EDpDCS5C Gl"GBf1BAk!A@R;@ ?^'? >P9>=R=@c=@=D= >_<>9=.:H 9:"=j'=;OI;>D!>Bk@B7El?EGfG Jg5JLi&LJiFJGVQGCEuC@}H@ >\(=D>j=>j<>7C@CBJ!B AUXABfFB I}*I GG(B:G >CBFs>@FGZ2GlHGH F?FHnDHEh|E#Bh+@WB&>A@> @_M@@>i>>/MTrkO @General MIDI0JAZ2Midnight Express/simutrans-124.5/simutrans/music/17-The-Benevolent-Dictators-March.mid000066400000000000000000000210031520675460500254460ustar00rootroot00000000000000MThd xMTrkCopyright (C) 2006 shunter~ JAZ2YQ$~j"")~j)( . ))@ ""c@J~j@ "c"@J)@)J $J ( .*@L@ "Nj))( . ))( *j5<jX[(]The Benevolent Dictators March @/MTrk!JAZ22@ '{[2Bass]%F>%:)F>):*F>*:,F>,:*F>*:!F>!:"F>"::5C%F>%:)F>):*F>*:,F>,:*F>*:,F>,:%F>%: :5 %F>%:)F>):*F>*:,F>,:*F>*:!F>!:"F>"::5C%F>%:)F>):*F>*:,F>,:*F>*:!F>!:"F>"::5C%LH%0 *A 7%FH%/MTrk r JAZ2]&2#PDrums @{[<&#_#P&2#&_#P&2&#_#P&2&##&(&&(&#P&2#&_#P&2#&_#P&2&#_#P&2&##&(&&(&&2#P&#_#P&2&#_&2#P&#_&2#P#&#&(&&(&&2#P#&_#P&2#&_#P&2#&_&2#P&##&(&&(&#P&2#&_#P&2#&_#P&2#&_#P&2#&#&(&&(&#P&2#&_#P&2&#_#P&2&#_&2#P#&#&(&&(&#P&2&#_#P&2#&_&2#P#&_&2#P#&#&(&&(&#P&2&#_#P&2#&_&2#P#&_&2#P&##&(&&(&&2#P'F1P#'1&_&2#P'F&#'_'F#P&2&#'_&2'F#P&'##&(&&(&&2#P'F'&#_&2'F#P&#'_#P'F&2#&'_'F#P&2#'&#&(&&(&'F#P&21P#&'1_#P&2'F#'&_&2'F#P'&#_&2'F#P&#'#&(&&(&#P&2&#_#P&2#&_#P&2&#_&2#P#&#&(&&(&'F#P1P&2#'1&_'F#P&2&'#_#P&2'F'&#_&2'F#P&#'#&(&&(&#P'F&2&'#_&2'F#P'&#_'F&2#P#'&_#P&2'F&'##&(&&(&1P&2#P'F&1#'_&2#P'F#'&_&2#P'F'&#_'F#P&2&#'#&(&&(&#P&2#&_#P&2&#_#P&2&#_&2#P#&#&(&&(&#P&2#&_#P&2&#_#P&2&#_&2#P#&#&(&&(&&2#P#&_&2#P#&_&2#P#&_#P&2#&#&(&&(&#P&2&#_#P&2&#_#P&2#&_#P&2&##&(&&(&&2#P#&_#P&2&#_&2#P#&_&2#P&##&(&&(&#P&2#&_&2#P#&_#P&2#&_&2#P#&#&(&&(&#P&2#&_#P&2#&_&2#P#&_#P&2#&#&(&&(&&2#P#&_#P&2#&_#P&2&#_#P&2#&#&(&&(&#P&2#&_#P&2&#_&2#P#&_&2#P&##&(&&(&&2#P#&_#P&2#&_&2#P#&_&2#P&##&(&&(&&2#P#&_&2#P#&_#P&2&#_#P&2#&#&(&&(&&2#P#&_#P&2#&_#P&2&#_#P&2#&#&(&&(&#P&2#&_&2#P#&_&2#P#&_#P&2#&#&(&&(&#P&2&#_&2#P#&_&2#P#&_#P&2&##&(&&(&&2#P#&_&2#P&#_&2#P&#_#P&2#&#&(&&(&&2#P#&_&2#P#&_&2#P#&_&2#P&##&(&&(&#P&2&#_&2#P#&_&2#P#&_&2#P&##&(&&(&&2#P#&_&2#P#&_&2#P#&_#P&2&##&(&&(&#P&2&#_#P&2&#_#P&2&#_#P&2&##&(&&(&&2#P#&_&2#P&#_#P&2&#_&2#P&##&(&&(&&2#P#&_&2#P#&_#P&2&#_#P&2#&#&(&&(&#P&2&#_&2#P#&_#P&2&#_#P&2&##&(&&(&#P&2&#_#P&2#&_#P&2&#_&2#P&##&(&&(&#P&2&#_&2#P#&_&2#P#&_&2#P&##&(&&(&&2#P#&_#P&2#&_&2#P&#_#P&2#&#&(&&(&&2#P#&_&2#P&#_#P&2&#_#P&2&##&(&&(&&2#P#&_&2#P#&_#P&2#&_&2#P#&#&(&&(&&21P'F#P1#'&_'F#P&2&#'_#P'F&2&'#_#P&2'F#&'#&(&&(&'F#P&2#'&_#P&2'F'&#_&2#P'F'#&_'F#P&2#&'#&(&&(&#P1P&2'F1'&#_'F&2#P#&'_#P'F&2&'#_#P&2'F'#&#&(&&(&&2#P&#_#P&2#&_#P&2&#_#P&2#&#&(&&(&1P&2#P'F&'#1_#P'F&2#&'_&2#P'F'#&_'F&2#P&'##&(&&(&&2#P&#_#P&2#&_#P&2&#_&2#P&##&(&&(&1P'F&2#P'#&1_#P&2'F'&#_&2'F#P'&#_#P'F&2&'##&(&&(&#P&2#&_&2#P#&_&2#P#&_&2#P&##&(&&(&#F&P&##&<&&<&&F#P&##&F&#&P#Z1P&1#O181/MTrkJAZ2{UPiano@] A["=/8>A<%513jA81%= ?AjA,8= 6/*5:<1>j:*16 6/<<3>-5j<3-6=B:?.>5?$5:=.M:==F5C.>.:.=5BDHAG=%15bAD1= 8I)>><=?:<?8)=H:G6%*5b:=*6=A8CAD,CW,=8A*>.I>1=6:1*6. 6A3CA<%5jA1%8= <>)5?/53D>1=6:16*.A<,5=/8>jA8,= :<*56/1>j:1*6 6A3C>8==:8=5%?/D<<>)553jD<5)?*>.I>1=6:16.*,>5I>8==:8=5,6/1>:<*5j:1*6 -5<<3>6/j<-36 FE.A=Eo=:0A<F5?;=3'=5.A:}AKIQDF=E1A=1ADIC@/MTrkgJAZ2Guitar[2{ ^\@] =EAQDP1L)1AD=EHPDQ?E5L)5DH?E:Q=P6E*L)*:=6EDPAQ=E,L),AD=E=P6E:Q*L)*:=6E6E?P:6=P.KAS0.:A=@DPAQ=E1L)1DA=EHPDQ?E5L)5HD?E=P6E:Q*L)*:=6EDPAQ=E,L),AD=E:Q=P6E*L)*:=6E?P6E=6AP1KDS01=DAAQDP=E1L)1DA=EDQ?EHP5L)5DH?E=P6E:Q*L)*:=6EAQ=EDP,L),AD=E6E=P:Q*L)*=:6E:6.K=PAS0.:A=@=EDPAQ1L)1DA=E?EHPDQ5L)5HD?E:Q=P6E*L)*=:6E=EDPAQ,L),DA=E=P6E:Q*L)*:=6E:6=P.KAS0.:A=:=I8AI9DJAIVFMS = IDAM-@/MTrk+JAZ2Strings/MTrk<JAZ2=R @Melody][^{=,=H ==G=DUjIW I/IQ IIHIPTOD=X=L=QDY=`IYPIHIYIPTQD PIdD\DIBWGd BG(DTId DIGBSGQ BG BG=KB=,GXBVBG-IaDSDI BTGT BG GJBU GBDWI_0DGZBMIBG)DUI_D I GMBRBG'BT=H=B%GRBTBG+IaDW(DI7IlhIHxFIHHJFIPHuABID9 AIGD=D@IlBGDD;BBED:AEBBEAAKB8?EAA:??CA2=G?&AG=-BFA*DDB:FEDrB?FoD<BiI5 D+HEI=HIO{D; IqFKDpD<F0B@D@BD?cBDD2ADB>BHAmDIBiFLDkHXFlIJHjKFIXKKO\K IZ)IHZ%HII`I/MTrkBJAZ2The Benevolent Dictators March/simutrans-124.5/simutrans/music/18-Ride-that-train.mid000066400000000000000000000765151520675460500226240ustar00rootroot00000000000000MThd xMTrkH~ JAZ2Q 2006.10.06/MTrkCopyright (C) 2007 shunter0JAZ2 @General MIDIj7_j~j1*@*1 @*F$@*cF@ ~j@ @$@ 1$(1@*@$@*1!1@*[@*(1@*@*@$~j@ 1$(@*@$@*$!@*[@ 1(1@ '@*1$!1@ @$~j@*1(@*[@ 1$(1@*@*1$(@ @$@ (1@*vj!@  @ F@ cF@ cA A ~j[@*!@ @ @@ !@ @@ !@ [@ XY] @Ride that train[(/MTrkv JAZ2 4General MIDIZBass][({@ @"Dx"|Lr~"U"MPZ"UX"O~r"_I"/$PW$!%LX% &TS&&'Da'"Lr"~'UX'"Pn""Hy"wO e"Qv")Zo)&We&)IV)#Dx|Lrz'Dx'|.Lr.z"Dx"|)Lr)z"Ds")Sh)&L[&"Zh""O`"&Pl& )Hp) +Kl+ 'O`'+Pl+ .Hp. 0Kl0 "O`""Pl" 'O`'.Pl. O`Pl "O`")Pl) 'O`'+Pl+ .Hp.0Kl0 1Hn1 0Lq0.Hp.+Oo+ "O`"Pl 'O`'"Pl" (O`("Pl" "O`""Pl" 0O`0+Pl+ 'Hp'#Kl# 5O`5+Pl+ 'Hp'!Kl! "O`"Pl 'O`'.Pl. O`Pl "O`"Pl "O`"&Pl& )Hp) +Kl+ 'Hn' %Lq%"Hp"Oo "O`")Pl) 'O`''Pl' (O`(.Pl. "O`"&Pl& 'O`'+Pl+ .Hp.0Kl0 1Hn1 0Lq0.Hp.+Oo+ "O`")Pl) 'O`'"Pl" (O`("Pl" "O`")Pl) $O`$'Pl' +Hp+#Kl# 5O`5+Pl+ 'Hp'!Kl! "O`")Pl) 'O`''Pl' (O`("Pl" "O`"Pl +O`+.Pl. 2Hp2#Kl# $O`$'Pl' +Hp+!Kl! "O`"Pl 'O`'"Pl" (O`("Pl" "O`""Pl" 'O`'+Pl+ .Hp.0Kl0 1Hn1 0Lq0.Hp.+Oo+ "O`")Pl) 'O`''Pl' (O`(.Pl. "O`""Pl" $O`$'Pl' +Hp+#Kl# 5O`5+Pl+ 'Hp'!Kl! "O`""Pl" 'O`'.Pl. (O`("Pl" "O`""Pl" +O`+.Pl. 2Hp2#Kl# 0O`0+Pl+ 'Hp'!Kl! "O`"&Pl& 'O`''Pl' (O`("Pl" "O`""Pl" 'O`'+Pl+ .Hp.0Kl0 1Hn1 0Lq0.Hp.+Oo+ "O`"&Pl& 'O`''Pl' (O`(.Pl. "O`"Pl 0O`0+Pl+ 'Hp'#Kl# )O`)'Pl' +Hp+!Kl! "O`"&Pl& 'O`''Pl' (O`("Pl" "O`""Pl" .Hn. ,Lq,)Hp)&Oo& 'Hn' %Lq%"Hp"Oo "O`""Pl" 'O`'.Pl. (O`("Pl" "O`")Pl) 'O`'+Pl+ .Hp.0Kl0 1Hn1 0Lq0.Hp.+Oo+ "O`"Pl 'O`'"Pl" O`"Pl" "O`"Pl $O`$'Pl' +Hp+#Kl# )O`)'Pl' +Hp+!Kl! "O`""Pl" 'O`'"Pl" (O`("Pl" "O`")Pl)"Dx"{Lr~"U"M)PT)"Za"Ly"sN\"K".@!"QI",&RL&.'I 'M"Q%"P'QJ' 'F'"XW"#.Qp.!Mt!"Da"Lr~"UX"PnDx|Lrz'Dx'|'Lr'z"D"W$De$I#U I# /MTrkJAZ2 @General MIDId*8[( @{]$LDrums$*b*\&j*&[*J$U*$W*S&!&*T$P*E!$*U*F&!*&U*M$J"*$+&O#&&W"&.&0!&*8$L*$b&j*\*&[*J$U*$W*S&!&*U$L*> *$T&q*W*&2& & *j$I$*1&`$&&3 &&& &&$&$`1$1U&.b$\!$&.)&(&&.M$O$.X.v$`&$&.-.)&0#.&$P*E!$*U*F&!*&U*M$J"*$+&O#&&W"&.&0!&$P*C$*W*U&&*V*J$[*$V&*O*&Z.$`&~.$&/&!$&$^. .$.&j#&&$[$&/2#20$h$00-J -$L*8$*b*\&j*&[*J$U*$W*S&!&*T*E$P!$*U*F&!*&U*M$J"*$+&O#&&W"&.&0!&*8$L$*b&j*\*&[$U*J*$W*S&!&*U*J$M*$S*S&y* &O$R*9*$1&.& &P&2&!&$O.]!$.R,)&*1,*&U$S*.*$Y&*P*&Y$O*=*$W*X&m &*U*=$S"*$,&C& &b&h$P*C$*W*U&&*V*J$[*$V*O&*&Z$M*J*$_*S&y*&_$R*9*$7&.&&P&7&&$`1l&F$1&S3l&$\!$&3)&(&&3M$O$3X3i&$`$&3-3L&0#3&$^3t$3V&$\3m)3$&"3O3 $k3\"3$S$i3&3&$,3@3 $l3M!3$.&#&3g$h$3$Q3$i&&$3.&#& $b3j*3$#&&$f3`#$P33y&g$c!$-3&&H3Z#&33$o3$&C& && $e&&$&6&&|& $i1o!$1S$f&33&$-3C#33o$f 3$X$\3&3$&+3O 3$e3B3$W$h&3&$3-3F"3$f3`!3$S&$_3q)3$&"&X3J3&1$n1$W$h3&3$&.3<3 3W$b$3Y$h&3"3$&*3$c.$1&!&&$c&$1& & &9$`&$&1& && $c&!&$U1l$`&F$1&S3l&$\!$&3)&(&&3M$O$3X3i&$`$&3-3L&0#3&3t$^$3V$\&3m)3$&"3O3 $k3\"3$S$i3&3&$,3@3 $n11$W3$h&3$&.3<3 3W$b$3Y3$h&"3$&*3$c.$1&!&&$c&$1& & $`&9&$&1& && $c&!&$U$`1l&F$1&S&3l$\!$&3)&(&&$O3M$3X3i$`&$&3-3L&0#3&$^3t$3V&$\3m)3$&"3O3 $k3\"3$S$i3&3&$,3@3 $n11$W&$h33$&.3<3 $b3W$3Y$h3&"3$&*3$c.$1&!&$c&&$1& & $`&9&$&1& && $c&!&$U1$n1$W3$h&3$&.3<3 3W$b$3Y&$h3"3$&*3$L *$T&q*W*&2& & *j$I$*1&`$&&3 &&& &&$&*8$L*$b*\&j*&[*J$U*$W*S&!&*T$P*E!$*U*F&!*&U*M$J"*$+&O#&&W"&.&0!&*>$U *$U&*_&*X*5$P*$U&*Z*&[*>$L *$T&q*W*&2& & *j$I$*1&`$&&3 &&& &&$&$L*8*$b&j*\*&[$U*J*$W*S&!&*T*E$P!$*U*F&!*&U*M$J"*$+&O#&&W"&.&0!&*F$X$*W&*j&*Y*C$S!$*J&d&Z&d&/MTrk8JAZ2]General MIDIi "Piano[( @@{yI\ O\M\J\ I+OMAF JMWJYC[A2C JM3I\O\ J\IOJ>I\OPJ\I:O JTT:T X\\\Y\XY\AX\\\Y\!YYMX\\RYYH\\VYYM\$\\Y\YR$Y\\YA\Y\\\YN"\\ Y\G[\W\BW [RKR U\YX=U Y.W\Q\R\U\QU WKSR W\P\T\K4PWTKPKN\WURV!NO!OWR*WOTWP\:PT W-W\N\RXNOF:O R WpL\M\R\UYLM UR/L\RSMYU\ LMUR.L\RUMYU\*L#UMR%R[AR L\U\MZRP$LR.UMRP#U\M\L\R-LM US\T[YTWYSTYWFS\Y\TYW\8S TWY,R\YSVV1RVY0Q\R[WTUYQRWUFQ\RYW\U\8Q RUW,WSP\TV1PTW@^\X\Y\X^YYZ^@^YY\^\^Y!Y\^:^YX\^\Y\X Y^Y\ ^7^YY\^\Y^Y\^K^YY\^YX\X ^YY\^A^YY\^W^YY\^V^YX\^ZY\XY^YW ^@^YY\^\ ^YVRXL\MU LoR M%L\R\MYLdFNRMFK\R\=KR&WXQ\RU QoW R%Q\W\RYQdKNWRKW\P\=PWIXJNP\M\IaJMPV?\%?H\C\XCHI\OZLXIKLOP\MVI\ J\IBMP JJ\D\DJ J%M'DJMmQ\R[W[Z\QRZW;QXRRWUZ\Q$RZW*Q\RWWUZ\Q ZRWQ\RSW\ZTQXZRW#Q\WXR\Z\"QR WZ!IXJNP\M\IJPM(C\H\DHFCFIXO\L\I#OL(J\D\DJF%DFVR\K\H\KHR_O?O KVH\R\bRO\HK!OMK\O\$KK6'KOH?O\HR\=ORK\O["KZHKOHO\R\@RO1MVP\I\IJ\EADJMPAD\J\DJDO\KLH[HH\UKO H)LVO\I\II\E@DILO@I\C\DICqA\%AJ\D\XDJjP\J\F\JFP_M?M JVF\P\bPM\FJ!MJQ\W\UZRZQRWUAQ\U\WYR\%QRUW2PZWSTT0PTW4MVP\I\IJ\EADJMPAJ\D\DJDC\H\DHFCFLVO\I\II\E@DILOI\@C\DICqA\%AD\J\XDJqY:Y ]\^\a\]^aA]\a\^\!^^M]aaR^^HaaV^a^M$a\^a^R$^a\^Aa^a\a^N"a\ ^aG`\\\B\ `WKW Z\^X=Z ^+JNIXP\M\IJPM/KVO\H\ HBKO'H\C\DH HKCKHLVO\I\II\E@DILOI\@C\DICD\J\DJFDFK\O\$KK6'KOH?O\HR\=ORK\O["KZHKOHO\R\@ROR\K\H\KHR_O?O KVH\R\bRO\HK!OIJNIXP\M\IaJMPV?\%?C\H\XCHLVO\I\II\E@DILOI\C\@DICoMLP\I[IJ\UMP J+L\O\$LM6'MOJ?JO\R\=ORL\O[ MNLNM JKOJR\O\@RO3K\O\$KK6'KOH?HO\R\=ORK\O["KZHKOHR\O\@RO/JNIXP\M\IJPM(H\C\DHFCFIXO\L\I#OLMLP\I[IJ\UMP JQ\R\W\ZYQR ZW/Q\WSRYZ\ QRZW.Q\WURYZ\*Q#ZRW%W[AW Q\Z\RZWP$QW.ZRWP#R\Z\Q\W-QR Z!IXJNP\M\IaJMP}C\H\DHFCFIXO\L\I#OL(D\J\DJFDFVR\K\H\KHR_O?O KVR\H\bRO\HK!OMK\O\$KK6'KOH?HO\R\=ORK\O["KZHKOHO\R\@RO0I\PZJXMXIAJ MPZ?\%?C\H\XCHI\OZLXIKLOXMLP\I[IJ\UMP JzR\M\J\MJR_O?O MVJ\R\bRO\JM!OR\K\H\KHR_O?O KVR\H\bRO\HK!OKMVP\I\IJ\EADJMPD\J\ADJDH\C\DHFCFI\OZLXIKLOJ\D\DJF%DFR\U\Q\W\Q WUKJR"T\W[P\K2P TW3Q\W\R\QRW@Q\WTRDQ:W R{I\PZMXJXIAJ MPXO\KLH[HH\UKO H'IXO\L\I#OL/P\MVI\ J\IBMP JJ\D\DJ JMDJMK\O\$KK6'KOH?O\HR\=ORK\O["KZHKOHO\R\@RO3K\O\$KK6'KOH?O\HR\=ORK\O["KZHKOHO\R\@RO0I\PZMXJXIAJ MPO\KVH\ HBKO'H\C\DH H%K'CKHLVO\I\II\E@DILO@C\I\DICD\J\DJFDFL\R\PZMZLMRPAP\L\M\RY%LMRP2KZRSOT0KOR;WXQ\RU QoW R%Q\W\RYQdKNWRKP\W\=PWIXJNP\M\IJPM?\%?H\C\XCHIXO\L\I#OLMLP\I[IJ\UMP JQ\R[W[Z\QRZW;QXRRWUZ\Q$RZW*Q\RWWUZ\Q ZRWQ\RSW\ZTQXZRW#Q\WXR\Z\"QR WZ"I\PZJXMXI#PJMKLO\H[HH\UKO H)O\LVI\II\E@DILO@I\C\DICqA\%AJ\D\XDJK\O\$KK6'KOH?O\HR\=ORK\O["KZHKOHO\R\@ROR\K\H\KHR_O?O KVH\R\bRO\HK!OJI\PZMXJXI#JPMKLO\H[HH\UKO H(I\OZLXIKLOZA\%AD\J\XDJI\ M\O\J\ I+OMAF JMWJYC[A2C JM3O\I\ J\IOJ>I\OPJ\I:O JpL\M\R\UYLM UR/L\RSMYU\ LMUR.L\RUMYU\*L#UMR%R[AR L\MZU\RP$LR.UMRP#M\U\L\R-LM U Q\R[W[Z\QRZW;QXRRWUZ\Q$RZW*Q\RWWUZ\Q ZRWQ\RSW\ZTQXZRW#Q\WXR\Z\"QR WZ L\M[R[U\LMUR;LXMRRUU\L$MUR*L\MWRUU\L UMRL\MSR\UTLXMUR#L\RXM\U\"LM RUS\T[WYYTSTYWFS\Y\TYW\8S TYW,YSR\VV1RVY>OTRWU\:OUR-OYRWU\8UOROUR\UVoOUR%RZO\U\7O RU"I\PZMXJXIAJ MP O\K\F\/OFKCM\K\E\"MKETR\N\H\"RNH@/MTrk6JAZ2]General MIDI@[({ @iGuitar h7]2[,72>7D2F*72I7\2d,72G297J*7 2D7J2R627@2H7G0272[7\72T2_7S!72a7Z2-72G.]4j5[4.5>.D5F*.5I7d.\,.7G79.-*. 7D5R.J65.@5H.G05..\7[.7T.S7_!.5a.Z7>5 .-:[3]9j93:>:F3D*3:I: 3-4[.N5I 4.5?.G5G4.5<.U7[:7.:7Y.]17.D5P.J15.65K.N@5 ..]7O&.7K.U7Y7. 5K.]'5.S5L;X`5[/5>C>R5K4>5A9j3]:[93:9:F3D*3:L7R0R-70:0R7R10 7>1R7R-71:7R1R11 72R8RI28+2"81$88R22R12 8;3N:I9[ 93:?3G:G43:<<[3U:<3:3]7R0R-70:0R7R10 7>7R1R-71:1R7R11 78R2RI28+812"$82R28R12 8;:[3Y,:3>:,3D*:&3b3:Z$:3T3':,$:3G:Q3*: 3B5M:`#5 :L:\5I&5:#5X7Z'5 7G5.:.!:55_7J*57N8R2R-82:8R2R12 8>0R7R-70:0R7R10 7>1R7R-71:7R1R11 78R2RI82+812"$88R2R212 8;5Q.KB. 5+5L.O=.55P.U<.5858.'5.5L.3(.5;:U3M6:3@:K3J0:3<^3_3<T3J7R0R-70:0R7R10 7>7R1R-71:1R7R11 78R2RI28+2"81$88R22R12 8;:I9[3N 93:?3G:G43:<3U<[:<3:0R7R-70:0R7R10 7>1R7R-71:7R1R11 78R2RI28+2"81$82R28R12 8;7\0^,07>0_7b*07I0]7e,07G0e7R*0 7D0^7\,07>0_7b*07I7e0],07G7R0e*0 7D2R8R-82:8R2R12 8>7R0R-70:0R7R10 7>1R7R-71:7R1R11 78R2RI28+812"$88R22R12 8;+M:`#+ :L:\+I&+:#7Z+X'+ 7G+.:.!:++_7J*7+N0^7\,07>0_7b*07I0]7e,07G7R0e*0 7D2R8R-82:2R8R12 8>0R7R-70:7R0R10 7>1R7R-71:1R7R11 78R2RI82+812"$88R22R12 8;3d:M=:3$3I:L83:<<_3W23<@3G7R0R-70:7R0R10 7>1R7R-71:7R1R11 72R8RI82+812"$82R28R12 8;3\:^,:3>3G:E*:3I38:G,:3G3::=*: 3D3Y:[,:3>:,3D*:&3:Z3b$3:T3':,$::Q3G3*: 3B2R8R-82:2R8R12 8>0R7R-70:7R0R10 7>1R7R-71:1R7R11 72R8RI28+812"$82R28R12 8;:^5\,:5>:E5G*:5I58:G,:5G5::=*: 5D:[3Y,:3>:,3D*:&3b:Z3$3:T3':,$:3:Q3G*: 3B2R8R-82:8R2R12 8>0R7R-70:0R7R10 7>7R1R-71:7R1R11 78R2RI82+812"$822R8R12 8=3K9W:M 93 :7:J3H*3:I<_3Z/3<C7R0R-70:7R0R10 7>1R7R-71:7R1R11 78R2RI82+812"$88R22R12 8;:`0M#0 :L:\0I&0:#7Z0X'0 7G:.0.!:00_7J*70N:[3Y,:3>:,3D*:&:Z33b$3:T:,3'$:3G3:Q*: 3B2R8R-82:8R2R12 8>0R7R-70:0R7R10 7>1R7R-71:7R1R11 78R2RI28+2"81$82R8R212 8;5[.]4j4.5>5F.D*.5I7d.\,.7G79.-*. 7D:U3M6:3@3J:K0:33_<^3<T0R7R-70:7R0R10 7>1R7R-71:7R1R11 72R8RI82+812"$88R22R12 8;9j:[3]93:>:F3D*3:I: 3-2R8R-82:8R2R12 8>0R7R-70:7R0R10 7>1R7R-71:1R7R11 78R2RI28+2"81$82R8R212 8;7\0^,07>7b0_*07I7e0],07G7R0e*0 7D0^7\,07>7b0_*07I7e0],07G0e7R*0 7D2R8R-82:8R2R12 8>0R7R-70:7R0R10 7>1R7R-71:7R1R11 78R2RI82+812"$82R28R12 8;7]2[,72>7D2F*72I2d7\,72G297J*7 2D7J2R627@7G2H0272[7\72T2_7S!77Z2a2-72G.]4j5[4.5>5F.D*.5I7d.\,.7G79.-*. 7D5R.J65.@.G5H05..\7[.7T7_.S!..Z5a7>5 ./3K9W:M 93 :7:J3H*3:I<_3Z/3<C5F.D*.5I7d.\,.7G.-79*. 7D.J5R65.@.G5H05.7[.\.7T7_.S!..Z75a>5 .25M/<5F5[>_%5>L>N5C,5>L:U3M6:3@3J:K0:3<^3_3<T:R3R-:3:3W9W#93UHMVCMJD#KUJMKHW>HVJTQMVJNM#O^2I\ODFHI*HIFRFAHIF7FTFQOa MWOMUFV6F=MV@M KT[K5JfPJMN)MJT*JJHO1H@HYKHM7*MG`+G$FS{FP?_*?Cd2CF_6H[FrFSHzFCSyCFWoJfFMJMN)JTM*JJHO1H@HYKHM7*MG`+G$FS{FCLTCH\8HKl#KJ9QJH>HMVCMJD#KUJMKHW>HVJTQMVJNM#O^2I\ODFHI*HIFRFAHIF7FTF/MTrk*mJAZ2 "General MIDIjSolo[<y@@"_[({ @._][({] "Z"O.O.".e:e."Z:5G.)G,T58T)Z,%?81?%&h2h1'&25U)U')5+N7N7+"X.XZ"..A"A".i:i.Z)@:5@.5),M8MZ1A8,%A%2j1&j'5Q2)Q&'5)+P7P7+"W.WZ.".C"C.".b:bZ:)N5N.,P)58PZ%<1<,82b&b1%'&)O25O')5+T7T7+.["[Z"..I"I..`":`Z)G5G.:58Z),ZZ8,%?1?1%2j&j'5W)W&2')5+W7W+7=CkJk ~(AkFk),-/0234 | {68:;< y>? x@B u t q p l k h g e d b a ^ ] [ Z X W U T6 V) W Y [ ] ^ a b e f i k n o q r u v@ x? y>;9 {65 }42 -+)%# )FCJA CiJiAi%Fi+-0@AB } z x v s q n m j h f e b a ] \ Z Y W V S Q4 P Q S T V W Y [ ^ _ e g l mA@ o p@ s?>= t<; v: x87 z64 {20/-+)(' ~%$# " JFCA%@ AiFiIiCi #$'-.0 ~1357 |:;=? y x u t q o l k a ^ \ [ Y W T Q M L I H% K L N O S U X Z _ a d f h i l n p r v wA z { ~ { @?><;:9861%FICA$ IkAkCkFk ~&*./1469:=@ } { z w v t s p n i h e c a _ Z Y W U S R P N% P Q U V Y Z \ ] _ ` c e i j l m s u y z@ |? ~> =<;:987643210.-,+IFAC@A ~Io?oEoAo"%'( }*0357:;?@ | { v u p n l k h g d b _ ^ \ [ W U R O K J J L N P R S V W [ ^ d h m o q s u wA y {@?> =< ;:987653210/.,*)(IAE? AkCkIkFk !&+,/01>? } { y x v t r q l k i g e c a ` ] \ X W T S M K K L N P R U W X \ ^ a c e f i j l k> m= o<; r s: u9 v86 x y5 |4 }31 0/. -,+CAIF,$@dAc$A #'F{ ~()*+ u,/34 n89:;=A l o } 1FF]I}F s" n&' g)156 h9<= k? m q { ~? w? ~ @9FF\Ir.F /0 ~2 |56 s89 o;<= k h>@ r z? l@ n t y }9F FXFCM !" {%) v+/4 o5 i8:<=? h j@ EAHCFiA!$ |& x()+/ m0 j46;>@ h {5FuF/FyHnRnOnKnAA F| ~ } v s!# o' n*.2=>? s v 1F FaIyF " x)3 t579 o;=? p@ w {=I I^FqI  |  v' o56 j8:; h=>@ n |@ d }BCAJFqA t$& q o() j+ h,-/ f1:;?@ gA l ~5FFWFIs  !$ v m(*0 j2469<=>? l 8IIT!IFq z0 k14 j56789; l> mA x ;F FUCr F {#/0 m568 g:;?@ f g q |  | r } 4F FbIxF" |$ x&(*, q. m/2 h4 g9<> i@ k { | {7I IbFu I$ o(* j,-/ f0 h15 s9 u z: {;<> }@A>7F FYFCo v#$& m') j+/34569 g= l?FCATFrA  $ z% t'+ n-/17 d9;< b>? f@ :F F\IsF v } m!# h%)/168 h: l; z<> @=I I_FkI! o"$ }(*345 i8 k9; m< o?B @>FFQ nCeF! i& f+2356 a9 _;=? ]@ ACANAFt"#' | z+- s0 q4 n89 l;<@ k m |2FuF.FyOmHmKmRm s t= v w<; y9 z8 | }7 543210/.- ,+*('&%#! MORU(A@FAc$A F{' ~()*+ u,/3 n489:;=A l o } 1FF]I}F s"& n' g)156 h9<= k m? q { ~? w? ~@ 9FF\Ir. F/ ~02 |56 s89; o< k= h>@ r z?@ l n t y }9F FXCMF !" {%) v+/ o45 i8:<=? h@ j EAHC!FiA$ |& x()+/ m04 j6;>@ h {5FuF/FyOnRnHnKnAA F| ~ } v s! o# n'*.2=>? s v 1F FaIyF  x")3 t579 o;=? p@ w {=I I^FqI  | ' v5 o68 j:;= h>@ n |@ d }BAJCFqA t$& q o() j+ h,-/ f1:;?@ gA l ~5FFWFIs !  v$ m(*0 j2469<=>? l 8IIT! zIFq0 k145 j6789; l> mA x ;F FUCr F {#/05 m68 g:;?@ f g q |  | r } 4F FbIxF"$ | x&(* q,. m/2 h g49< i> k@ { | {7I IbIFu $( o j*,- f/01 h5 s9 u: z {;<> }@A>7F FYCoF v#$&' m) j+/3456 g9= l?FATCFrA  $ z%' t+ n-/17 d9;< b>? f@ :F F\IsF v } m! h#%)/168 h: l; z< >@=I I_IFk! o" }$(*345 i89 k; m< o?B @>FFQ nFCe! i&+ f235 a69 _;= ]?@ AANC"AFt#' | z+ s-0 q4 n89 l;<@ k m |2FuF.FyRmKmHmOm? x@B u t q p l k h g e d b a ^ ] [ Z X W U T6 V) W Y [ ] ^ a b e f i k n o q r u v@ x? y>;9 {65 }4 2-+)%# )JACF Ji%AiFiCi+-0@AB } z x v s q n m j h f e b a ] \ Z Y W V S Q4 P Q S T V W Y [ ^ _ e g l mA o@ p@ s?>= t< v;: x87 z64 {20/-+)( ~'%$ #" JFAC%@,>eJeCeOeHeTe'T>CCeJe>eJOOeHTeHe'J>OTCHHe>eJeOeCeTe(O>HJTCB_G_J_>_S_N_'BG^NSB^>^J^J>N^S^G'BJN>SGN^G^>^S^B^J^(SBJNG>>WMWFWRWAWJW'F\JM>RAA\J\M\R\F>\'JRM>FA>bRbMbJbFbAb(MJ AR>FFiAiCiIi #$'-.0 ~1357 |:;=? y x u t q o l k a ^ \ [ Y W T Q M L I H% K L N O S U X Z _ a d f h i l n p r v wA z { ~ {@ ?><;:9861%$IFAC AkFkIkCk& ~*./1469:=@ } { z w v t s p n i h e c a _ Z Y W U S R P N% P Q U V Y Z \ ] _ ` c e i j l m s u y z@ |? ~> =<;:987643210.-,+ACFI@A1TWOW>WCWHWJW'OXCJXHXCX>XTXJH>OT'TO>CHJHZJZTZ>ZOZCZ(O>THCJJQSQBQNQGQ>Q'NRJSSRJRBR>RGR>NGB'NSJBG>NUJUGUBU>USU(GB>SNJROJOFOAO>OMO'J\R>\FM\A\>AMR\JF\'MRJAF>>aJaAaMaFaRa& ~AF>RJMEoAo?oIo"%' }(*0357:;?@ | { v u p n l k h g d b _ ^ \ [ W U R O K J J L N P R S V W [ ^ d h m o q s u wA y {@?> =< ;:987653210/.,*)(?IEA FkCkIkAk !&+,/01>? } { y x v t r q l k i g e c a ` ] \ X W T S M K K L N P R U W X \ ^ a c e f i j l k> m= o<; r: s u9 v86 x y5 |4 }31 0/. -,+IFCA,$@TJEZJHAHFGZFD7DA<^H^P>C@H/MTrkV0JAZ2General MIDIRide that train @/simutrans-124.5/simutrans/music/19-Rockin-trucker.mid000066400000000000000000000464301520675460500225640ustar00rootroot00000000000000MThdxMTrkfCopyright (C) 2007 shunter~ JAZ2 2006.10.06Q/MTrk0JAZ2 @General MIDI~j'@@@@!.j@~j1111@~j'@@@@!''~j@@@@~j@('@ 2006.10.06jVjXYRockin` trucker/MTrk= JAZ2@General MIDIBassZ @]{ @[(XNkTkNC+V+ LkVkNkRk]s$\Z$W3=$V $+$R.$Fq"bV""$YP$ ]) A"$XP$(&Z[&'T`'(Za()SF)*$TC$-)V1)?&R`&%\c%$X $eT:6$Z$$I6$M\+b[++Uq+~Y?2)V )+$M0$ R]Rp$^$kZ0@+IO+*+U;l +l&M,& TT$$_w$y+X+b)^)-)R8)!NZ!#VS#%$^$kZ0@$U@$0$_T$((Kh( )VI)'$ZF$*WM#Sc $YP$ ]) A"$XP$(&Z[&'T`'(Za(_wy&X&b)V )+$M0$ R]Rp$^$kZ0@W%J1 &Xo& So $\Z$W3=$V $+$R.$Fq"bV""$YP$ ]) A"$XP$(&Z[&'T`'(Za()SF)*$TC$-)V1)?&R`&%\c%$X $eT:6$Z$$I6$M\+b[+_wy&X&b)V )+$M0$ R]Rp$^$kZ0@W%J1 &Xo& So $_w$y+X+b)^)-)R8)!NZ!#VS#%$^$kZ0@$U@$0$U@$0)VI)'$ZF$*`;>$L<$;!\[!Lr$YP$ ]) A"$XP$(&Z[&'T`'(Za(_wy&X&b)V )+$M0$ R]Rp$X$!$N)$(Si()UM)+W%J1 &Xo& So $\Z$W3=$V $+$R.$Fq"bV""$YP$ ]) A"$XP$(&Z[&'T`'(Za()SF)*$TC$-)V1)?&R`&%\c%$\Z$W3=$V $+$R.$Fq"bV""_wy&X&b)V )+$M0$ R]Rp$^$kZ0@W%J1 &Xo& So $_w$y+X+b)^)-)R8)!NZ!#VS#%$^$kZ0@$U@$0$_T$((Kh( )VI)'$ZF$*`;>$L<$;!\[!Lr$\Z$W3=$V $+$R.$Fq"bV""_wy&X&b)V )+$M0$ R]Rp$^$kZ0@+IO+*+U;l +l&M,& TT$$`4$$O6$(]]()V`)*cU*#+L-+;:#I1# $h$/MTrk JAZ2]General MIDIDrums[7 @f @{`$X3E3$W3q&r!&3U$O3\3$Z&3r3&W3R$L3$[&3\&3X$V3K 3$W&z3m&3W3W$S$3[3]&_3&Z$U3i3$X3&&3X3$I3$#&&#&&#&&#22#2o2#00#-6-#$J3g$3Y3|&i3&W3\$M3$X&3v3&^$L3R3$[&3\&3X$V3K 3$W3m&z&3W$S3W$3[3]&_3&Z3i$U3$X&3&3X3\$M3$Y&3w&3Y3d$L $3W3l 33W&&3$J3g$3Y&i3|3&W$M3\3$X&3v3&^$M3\3$Y3w&&3Y$L3d $3W3l 33W&&3$X3E3$W&r3q!&3U$O3\3$Z&3r3&W3g$F3$&_&!&~&&!&&&!&&&&&b & && 3g$J$3Y&i3|3&W3\$M3$X&3v3&^3\$M3$Y3w&&3Y$L3d $3W3l 33W&&33K$P3$Y3w&&3\3v$O $3X3&&3[3$I3$#&&#&&#&&#22#2o2#00#-6-#1$V$1X3y&&335 33$O$33533z& &33E& &33\$[$33=3&3&33J3$U3o$3 3H3!3v&&3&o3E3&$S3R3$3B 3&3v3&3B3 3b$I$33B3!&3l3&&3='3&$D383$!38 3&3l&3 3J3 3M$P3$&&&&!&z&!1$^$1[3&3&3A3$P3_3$3H$3&i3w&33>& &33\$[$33=33&&33J33o$U3$ 3H3!3v&3&3E&o3&3g$M3$383 &3m&333 33$P3$ 3>!3&3&3 39&3&3m$S3$393!&3v &3332y32$P3e$30|3E033~&|3&--!3j$P3$36 3&w3t&3353$U3d$3 3E!3&3y&3&3P3&3S$P3$3O3 &3t!&33@33l$J$33C3 &3z&3 &3J&3 $R3X$3353 &3r&33A!3$S3r3$323 &3`&3&3M&3$R3b3$#3R&>3&#&]3j$F3$&#&3O&3#$L&3w&3$#0&w0&#0&, ,0&#0|&0&#1$V$1X3y&&335 33$O$3353&3z &33E& &33\$[$33=33&&33J33o$U$3 3H3!3v&3&&o3E3&3X$R$3353 &3r&33A!33r$S3$323 &3`&3&3M&33J$\3$3J 3&3i3&3> 3$`3E$33= 3&33&3R&&31$^$1[3&3&3A3$P3_3$3H$3&i3w&3&3> &3$P3S3$3O3 &3t!&33@3$J3l$33C3 &3z&3 &3J&3 3j$P3$36 3&w3t&3353$U3d$3 3E!3&3y&33P&&33m$S3$393!3v& 3&332y32$P3e$30|3E03&|3~3&--!3S$P3$3O3 &3t!&33@3$J3l$33C3 &3z&3 &3J&3 3S$P3$3O3 3t&!&33@3$J3l$33C3 3z&&3 &3J&3 $R3X$3353 &3r&33A!33r$S3$323 &3`&33M&&3$[3e$3#3E3#&3o3&#353#&33&#36$O3$#&3&3#353#1$V$1X&3y3&35 3$O3$33533z& &3&3E &33S$P3$3O3 &3t!&33@3$J3l$33C3 3z&&3 &3J&3 3S$P3$3O3 &3t!&33@3$J3l$33C3 &3z&3 &3J&3 $b3$33633&&336!33$f$3 &&000\ 0 ---$^1$1[3&3&3A33_$P3$3H$33w&i&33>& &3$P3S3$3O3 3t&!&33@3$J3l$33C3 &3z&3 3J&&3 $V1$1X3y&3&35 3$O3$33533z& &33E& &33b$R$3&>3R3&3j$F&]3&$3O&&33w&$L&3$0&w0 &&0, &,&0&0|&03j$P3$36 3&w3t&3353$U3d$3 3E!33y&&3&3P&33S$P3$3O3 &3t!&33@33l$J$33C3 &3z&3 &3J&3 3j$P3$36 3&w3t&33533d$U$3 3E!33y&&33P&3&$R3b3$#3R&>&3#$F&]3j3$&#&3O&3#&3w$L$&3#&w00&#&, 00&,#0|&&0#$P1o1$X&3&3Z3_$P$3&Z&&&&i &3\$M3$Y&3w&3Y$L3d $3W3l 33W&&33W$S$3[&_3]3&Z$U3i3$X3&&3X3]$X$3W3l&&3\3]$M3$-i-&3_3&&t&$X3E3$W3q&r!&3U3\$O3$Z3r&3&W3\$M3$Y3w&&3Y3d$L $3W3l 33W&&3$X3E3$W3q&r!&3U$O3\3$Z&3r3&W$L3C$3&g&&X &&&$S33$&m&-&q &&&3E$X3$W3q&r!&3U3\$O3$Z&3r3&W$M3\3$Y3w&&3Y$L3d $3W3l 33W&&33K$P3$Y3w&&3\$O3v $3X3&&3[3C$L$3#&g&#&X&#&&#$S3$3#&m&#&q&#&&#$V1$1X&3y&335 3$O3$33533z& &33E& &3$P3S3$3O3 3t&!&33@3$J3l$33C3 &3z&3 3J&&3 3R$S3$3B 33v&3&3B3 $I3b$33B3!3l&3&3=&'3&$P3S3$3O3 &3t!&33@3$J3l$33C3 &3z&3 3J&&3 $^1$1[3&3&3A33_$P3$3H$33w&i&3&3> &33S$P3$3O3 &3t!&33@3$J3l$33C3 &3z&3 3J&&3 3R$S3$3B 3&3v3&3B3 3b$I$33B3!&3l3&3=&'3&3`$P 3$333!&3d&33>3$U3b3$-V3H3-&|3d&33M&&33j$P3$36 3&w3t&33533d$U$3 3E!33y&&3&3P3&3S$P3$3O3 &3t!&33@3$J3l$33C3 &3z&3 &3J&3 $R3X$3353 &3r&33A!33r$S3$323 3`&&3&3M&33e$[3$#3E3#3o&&3#353#&33&#$O363$#3&&3#353#&&#&&#&!&#&&#22#22#00#00#--_&&A$t1Y1$/MTrk)-JAZ2[2General MIDI &wPiano{] @@`ZUQU7U+UZXU[UX7++U7UK7X [+Q\U9UTU2U\YU]UX929U2UK9Y ]T2WUOU4U(UWVUXUX4((U4UK4V XO(2c9e;]UY[\JTYV\Y92]VT\]<TEVHYR9f2uTYV]!T^T2 9ZU7UQU0UXUZ[UHX Q[70XU9UQU0U[U.Q X[9 0\UTU9U2U\YU]UHY T]92YUTU`[47Y`[0]TX0T7`7D`RTL0>`T0 7 3g`]Zn'Z`34S[Y`H`4[7V`\0ZTX0T`77LTW0>`T`T07Zk3j`[$Z`34L[V`<`4[7W0V`bT`07 `T027C 07@ 3k3!Rh%R @7H0F87Wp[l0XPW[X9N[ZX^0<[X 09[TX[[X7D[[0BXb"[X 07X\[M[X9N0I[bXi[X 90X_[Y[X7E0C[bXf[X 70 [UXYX[0E9D[_Xa[X 90[TX[ [XQR7I0D-7Q0 TZ&T0LVm9U09V T_TN]^5<`] 5>`T][`]N5I`b]i`] >5]_`Y`]D]a`] >5`T][ `]VRU5L5>V Y_Y7ZT^0[`\0T` 7 7-0G`UTL`T3kZn`` 07Z`[Q4O3`>`[47Y`[0]TX0T7`7D`RTL0>`T0 7 3g`]Zn'Z`34S[Y`H`[47V`\0ZTX0T`77LTW0>`T`T07Zk`[3j$Z`34L[V`<`4[7W0VT``b07 `T027C 07@ 3k3!Rh%R @2T+P>OS+2ORh4T4RSS 4 S 2I+J[_8[OI2+O4RR^&RSV4 S]KL]59 b?`H c ]@b 5>`[Y][<\5R ]bcn+<5 ] bF`E ]Gcb5L[]>Y`]5[>YW Y$7T0P>TS07T9TWh4WXS 9 X 7I0J`_8`TI70T9RW^&WXV9 X2U+PJ2++^t4\Vk+NV^+^fVe4+V2Q^+FA^uVm2+.4\V^24 [a([ 0N7POVTR"OT07OBT2OT9W0JTYOc'09OT?Na7XTYO\0RN7O0T:9[0R8907]KL]59 b?`H c ]@b 5>`[Y][<\5R ]bcn+<5 ] bF`E ]Gcb5L[]>Y`]5>[YW Y#7T0M*07 [N^F`KZVZ`[^9Y0U[]Zg^:`SZ`[^ 09 [U`N^6ZYZ[`^`X7Y0R[V^FZcZ[`^07^=[SZc`IZ`[^9\^=0M`QZh[UZ`[^30 9ZU7URUTU0UXU[UZ270:0U7UK7 0 X[TR0c7e;[UX[ZJT\RYX70[TRZ[<THXRRE0n7f,[XTRH^0 7H0Z`])Ecgc`)0`NcTc`2_)U^nb`/)2bYC^ Y\f`U0T)H]\\)0] ` YO 0KY0bd^k)O2]"2)bYM2P^);)Y2*Q6S *6QNT5WNNEQTNW6ZWN*MT\NfQN66T*WQN9WNMdQN6[T\N_*UM6N*WQT:6^*U6*HN!H0N7POVTR"OT70OBT2OT9W0JTYOc'09OT?NaTY7XO\0RN7O0T:9[0R809NZOWTZ!N7MO0IT570TfObNb.N9O0COT,N`TbO`09NO7TT0ML07(9`0Wa0 9 @2W+NG\:GOE&O+ .`Ff2O>O F/WO\GV..G/O2XCW+Q/CF`%F +.aGX2#. C]/R G/C `\T K4WT\LV3.L4THW7X0Q/HK`%K 03aLX7#3 H]4R L4H OYWj<[5@45OWQ=<Q8UM\Ve38M9MQ?V Q9 KZL]59 b?`H c ]@b 5>`[Y][<\5R ]bcn+<5 ] bF`E ]Gcb5L[]>Y`]5>[YW Y*W-*ZVWdQKTK*K/T*Q6`ZW 6QKWbZ`TKQ3XZWTTV23 *eT-NI"*N0N7POVTR"OT70OBT2OT9W0JTYOc'09OT?Na7XTYO\0RN7O0T:9[0R890NZTZOW!N7MO0IT570TfNbOb.N9O0COT,N`O`Tb09NO7TT0ML07(9`0Wa0 9 @2WG\+N:GOE&O+ .`Ff2O>O F/WO\GV..G/O2XCW+Q/CF`%F +.aGX2#. C]/R G/C S5GTY,TdSdYf5>STd5[a5 >7T0M*07 ^F[N`KZVZ`[^9Y0UZg[]^:`SZ`[^ 09 `N[U^6ZYZ^[``X7Y0R^F[VZcZ[`^07Zc[S`I^=Z`[^9\^=0M`QZh[UZ`[^30 92W+OG+2'4^+MfTdZfbad`Wb_X`]^_][] ZF+4Z[!+F2KdBbZ`abd_a`]a_2]+[\ ZM[XDXZ4X+Lb^`V_Gb`]U_+4][Y [ZW ZXS XV`VTa S^TQ]SQ 7J0G=HX70HNc9LRTTN ONNTOR9RJT\OX3740:OTR5NjT\R]70OCN9F OTR4TYNiR] OV9N7J@ OTR7E7Z@6lOa[h6O[OL[S[O77Y[`OXO[77I[cOY!O7[7H0F8[lWp70XPW[X9N[ZX^0<X[ 09[TX[[X7D[[0BXb"[X 70X\[M[X9N0I[bXi[X 90X_[Y[X7E0C[bXf[X 70 [UXYX[0E9D[_Xa[X 90[TX[ X[QR7I0D-7Q0 TZ&T0L9UVm09V T_TN`Z]^5<]` 5>`T][`]N5I`b]i`] >5]_`Y`]D]a`] >5`T][ `]VRU5L5>[ Y_Y+Z$E[]^g^[$+[N^T^[-_$UYn]`/$-]TCY TWf[U+T$HX\W$+X [ TO +KT+]d$OYk-]"$-]TM-P$;Y$T-Wc[O$H+RXPW $+X TM +M$9[T$+]`Yj-Y$U-$]YWe-b[S$LX^W$[X- $E+X+$#O`+c$N$ O+-dOC-O2TO\2OV_2R+JVG+24]+M;[\Rf4+RS7S2U+G[4[hRr+2 STR[4YS+P?+ 4-5Cf`\][Y^<> 5E]`Y,5>d ^[`SYZ>`Y^5D^`[][Y[5S(5>]`Y.7]T^HYN^0XRUORNHROT07THHPOKREHTOR9[HZTQ0GREOTHROT09TEHQOSRLHRTO7WH]TR0NOQRLHTOR7HHTEOTRI0THRO9\HdT^O^0ARRHORTHN0H92W+OG+2'4^+MfTdZbafd`Wb_X`_]^][] +ZF4Z[!+F2KdBbZ`abd_a`_]a2][\+ ZM[XDXZ+L4Xb^`Vb_G`]U_+4][Y [ZW ZXS XV`VTa S^TQ]SQ0Z7Q@[gOcO[OS[W[O 03e[`Wl7[W34I[EXW[X4[a7XWm0[W[ 07 [G0;7GWSW[ 07We3c[V W[ 3[LWY4P[W 4 ]KL]59 b?`H c ]@b 5>`[Y][<\5R ]bcn+<5 ] bF`E ]Gcb5L[]>Y`]5[>YW Y7N0JS5GTY,SdTdYf5>STd5[a5 >[[Xc7V0Y X0[77GWZ0A[H[W 03cXb[M7[ X34OWe[5[W4[T7WX^0Z([07X W^7G0+W3h[SX_ 073[X 4RW\!W4JHMHSHCH+W@?++@VMSC JOdC_C+`O C +O\CVOC@+XSCXO`+COCQOXCO@$\ZmT\[b`dZT[``HT@[>ZUZ`[T^]RX(P $ R ^[XOO[O)AN\Zc( NZMHYRMY)*SW`K]KWHbTcH T * [j+^Y)[YXQ+XVQTZVSYTSQQ OOQ MPO LSMJPLHSJGPENHCHG EC*$^ca>)W>>[?>Y.;^>D;7O-7R7:9[ 7b[aUC^<Ab CpAALd?L1H@*HJ^6JH?*HL`KL'H^I<>4VB>:?S4?@>SA>T/>@S;AR@1CSA3CEZ6EDCcJI>JHW/H_ca>)W>>[?>Y.;^>D;7O-7R7:9[ 7b[aUC^<Ab CpAALd?L1H@*HJ^6JH?*HL`KL'H^I<>4VB>:?S4?@>SA>T/>@S;AR@1CSA3CEZ6EDCcJI>JHW/H_)D I PBFBIBDBFeDFIIBFBDBPBP<%>"* IFD%PB8=B?B'M96D5=:B#6??>): B =B?B?BBFBe=B?FB?BFBB=B<">"' B?"=F1/;/B/D/rBDDRB)1LGEKE1BGKKMB81LGm1KBG 1LG8K8B8 BGK1B8K8G81L1GKBB+*D9=7+BBD(BF;=*F?+'D9:7+??D(?B;:'BD@B@H6,K9)=1GD B/*H9=;+BBH(BF?=*F@B1<DBIBsDB=BIBDI*@@B'1I=@D0$:?$B$r?BBG)?1DG B-*F9=9+BBF(BF==FFDID=DBDnFBI FDB=ID=!FBI=D&*=*<B<=.FB-KI1PGI1BKGG!KQ1PB)B*FI '<?<:.B)? G GB?BBBBKBe?BG?BBBGBKKB<*>"/ GB?*KBBF8?B'M9:D5??B#:BB>)? F ?BKBBBBFBe?BF?BBBKBKFB<">"' FB?"K,<?.D<G)=1DG *<B<=.F)@1DI ??09B?F?sB?:?FBF?*???'0F?:B)$9?$B$r?BBG)B*IF F8?BBB'M9:D5??B#:BB>)?'FB BBH8DB,M9)?,HD B/?P1H9;;+??H(BD?;?1DBB*<FBIBsFBIBIF=B)D,GK 1<B<;.D)D,KG =PA/1H9;;+==H(AD?;=1DB/*H9=;+BBH(BF?=*F@/1H9=;+@@H(@D?=1DF6?@B@0K9:B5?<@#:BB<)<0BF B@?@E6)K99B5?<@#9BB<)<)BE F6=@A@.K9:B5==@#:AA<)=.FA '$7=$?$r=??G)B*FI ?B'<BBFBsFB:BFBBB*??B''F?:BBB,<DBHBsDB)?,HD ?PB/1H9;;+??H(BD?;?1DI9FCBC*N9=E5BBC#=FF?)B I NCFBCICFCeBIF:C=CBCFCN<%?"* B:F*R%B2FC:2I2N2]=B:*NIFd@/MTrk ?JAZ2 "{[(]l@ Brass sectionx=(:(6(b:=66(1(t?Ly?=Id6=6?c1 6GF;FkG;82D2xB2Da8B;B7K71KG71BKGG7B7K71KmGK1B K7G71KB7 BGK1B7K71KG7K1GB =FIF?k=IF2:2xB2Fa:BC;(8(?(-;?8;(6(8(q68;}6(:(3(b36:;B71KG7K71BKG1KB7K7G7mK1BG 1KG7K7B7 BGK1B7G7K71K1GKB =(:(6(j=:6C3(:(6(-3:6<(6(tDLyDBId<B*(&&*.F#-#.>.2&2.&*F#7.F.*#>&F*(&*#-.6.#_*F%P*%=*(* #-*P*#_*P#-*#=*2* *F#-*#_%F*P%*?*(**P#-#*_%P*P%*?*2*#-*P*#_%F*Z*%>*(*#-*P#*_*Z%U*%>*2*#-*P#*_*Z%F*%?*(**P#-#*_%S*Z%*?*2**F#-#*_%F*P*%?*(**P#-*#_*P%P*%?*2*#-*F*#_%F*Z*%?*(**P#-#*_*Z%P%*?*2**P#-#*_%F*Z*%>*(*#-*P#*_*Z%U%*>*2**P#-#*:&& *Z*:&& *F#-*#:&#& *Z#7*#&!&&,& *F#-*#_*F%P%*=*(* *P#-*#_#-*P#*=*2* #-*F#*_%F*Z%*?*(**P#-#*_%P*Z*%?*2**F*_%P*F*%=*(* #-*P*#_#-*P#*=*2* #-*P*#_%F*Z*%>*(*#-*P*#>%Z%*Z*>*2**P#-*#_*Z%F*%>*(**P#-*#_%U*Z%*>*2*#-*F#*_%F*P*%?*(*#-*P*#_*P%P*%?*2**F#-*#_*Z%F%*?*(**P#-*#_*Z%P%*?*2**P#-*#_*Z*9&,& #-*P*#&&&& .Z#-&!.&#&,&&7& &#1P#2&#1_,Z3Z,3>3F3&3F&3_,Z3Z,3>3F33P3_,Z3Z3,?&3F3&&3P3&_,Z3Z3,?3F33P3_,Z3Z3,?3F3&3P3&_3Z,Z,3?3F33P3_3Z,Z3,?&3F3&3P3_,Z3Z3,?&3F&33F3_3Z,Z3,>32&&33P3_3Z,Z3,>32&&33P3_3Z,Z3,?3F3&3P3&_3Z,Z3,?3F33F3_,Z3Z,3?3F&3&3P3_3Z,Z,3?3F3&73F3&9#A# 3Z*F3*9&A& #K3P#39&-& 3Z*d3*&&0F&&0 #2&#1P#1&_,Z3Z3,>3F33F&3&_,Z3Z3,>3F33P3_3Z,Z3,?&3F3&&3P&3_,Z3Z,3?3F33F3_,Z3Z,3?3F&3&3P3?&#&,Z3Z,3?3F33P3_,Z3d,3?&3F3&3P3?32&-3&,Z3Z,3?3F&#&33F3_,Z3Z,3>&32&33P3_3Z,Z3,>&32&33P3_,Z3Z,3>3F&&33Z3_,Z3Z,3>3F33F3_,Z3Z3,>32&3&3P3_,Z3Z,3>&323&#73F#3_,Z3Z3,?&#3F&33P3?3(&-.(3&.3Z,d3,?&73F3&3d3_,Z3Z,3>3F3&3F&3_,d3Z,3>3F33P3_3Z,Z,3>3F&3&3Z3_,Z3Z3,>3F33P3_,Z3Z,3?3F3&3P3&_3Z,Z,3?3F33P3_,Z3Z3,?3F3&3P&3_,Z3Z3,?3F33F3_3Z,Z,3>32&3&3P3_,Z3Z,3>&323&3P3_,Z3Z3,>3F33Z3_3Z,Z,3>3F33P3_3Z,Z,3?3F3&3P&3_3Z,Z3,?3F33F&7&39#A# 3Z*F3*9&A& 3P#K#39&-& 3Z*d3*&&&0F&0 #(1F1#_3Z,Z3,<3F3 &3F3&_3Z,Z3,<3F3 3P3_3Z,Z3,?3F3&3P3&_,Z3Z3,?3F33F3_,Z3Z3,>32&3&3P3_3Z,Z,3>32&3&3P3_,Z3d,3?&3F3&3P3?&-323&3Z,Z,3?3F&#3&3P3_3Z,Z3,?3F33P&3&_,Z3Z3,?3F33P3_,Z3Z,3>3F33Z3_3Z,Z3,>3F33F3_,Z3Z3,>&32&33P3_,Z3Z3,>32&&3#73F#3_,Z3Z3,?&#3F&33P3?&-.(.&,d3Z3,?&73F&3#(1F1#<&#& 3Z,Z3,<3F3 &3F&3_,Z3Z,3<3F3 3P3_,Z3Z3,>3F&3&3Z3_,Z3Z,3>3F33F3_,Z3Z3,>32&3&3P3_3Z,Z,3>32&3&3P3_,Z3Z,3=#73F#3 3P3=&-& ,Z3Z3,=3F&#3& 3F3?&-&3Z,Z,3?3F&&33P3_3Z,Z3,?3F33P3_3Z,Z3,?&3F3&&3P3&_3Z,Z3,?3F33P3_3Z,Z3,?3F3&3P3&_,Z3Z,3?3F33F&7&3>#2#,Z3Z3,>&-3<&33F3>#<&-#&,d3Z3,>&3<3&#(1F#1_,Z3Z3,<&3F3& &3F3&_3Z,Z3,<3F3 3P3_,Z3Z3,?&3F&33P&&3_,Z3Z,3?3F33F3_,Z3Z3,?&3F&33P3_3Z,Z,3?3F33P3_,Z3Z3,?3F&&33P3_3Z,Z3,?3F33F3_3Z,Z3,>&32&33P3_,Z3Z,3>32&&33P3_3Z,Z3,?3F3&3P3&_3Z,Z3,?3F33P3_3Z,Z,3=3<&&3 3P3_3d,Z,3=&-3F3& &73F&3>#2#,Z3Z3,>&-3<&33F3>#<&-&#3Z,d,3>3<&&3*F#-*#_%F*Z%*?*(**P#-#*_*Z%P*%?*2###*#-*F*#_*Z%F*%?*(**P#-*#_*Z%P*%?*2**P#-*#_*Z%F*%>*(**P#-#*_*Z%U*%>*2**F#-#*_*Z%F*%?*(*#-*P*#_*Z%P*%?*2*#-*F#*_%P*Z%*=*(* #-*F*#_*Z%N*%=*2* #-*P*#_%F*Z%*>*(**P#-#*_*Z%U%*>*2**F#-#*_*P%F*%?*(**P#-#*_*P%P%*?*2###*#-.P.#_*<*>*(&&*#-.F.#>&2.2&.*F.F#7.#*>*(&F&**P#-*#_*Z%F*%=*(* *P#-#*_%U*Z*%=*2* *P#-#*_*Z%F*%>*(**P#-#*_*Z%U*%>*2*.6._*F%P*%=*(* #-*P*#_#-*P#*=*2* *P*_%F*Z%*>*(**P#-#*>%Z%*Z*>*2###*#-*F#*_*Z%F%*?*(**P#-#*_%P*Z%*?*2*.6#-#._*F%P*%=*(* *P#-#*_*P#-#*=*2* *F#-#*_%F*P%*?*(*#-*P*#_%P*P%*?*2*.P&#.&;#7# *P*;&#& #-*F*#;&*<&* .P&#&.& &&& #21P&#1&#_,Z3Z,3>3F33F&&3_3Z,Z3,>3F33P3_3Z,Z,3>3F33Z3_,Z3Z3,>3F33P3_,Z3Z,3?3F3&3P3&_,Z3Z3,?3F33P3_3Z,Z3,>3F33Z3_3Z,Z,3>3F33F3_3Z,Z3,>32&3&3P3_3Z,Z,3>&32&33P3_,Z3Z3,>3F33Z3_3Z,Z,3>3F33P3_3Z,Z3,?&3F3&3P&3&?&&,Z3Z3,?3F33F&73&>#2#,Z3Z,3>&-3<&33F3>#<&-&#,d3Z,3>&3<3&1F#(1#_3Z,Z,3<3F3 &3F3&_3Z,Z3,<3F3 3P3_,Z3Z3,>3F33Z3_3Z,Z,3>3F33F3_3Z,Z,3?3F&3&3P3_3Z,Z,3?3F33P3_3Z,Z3,>3F33Z3_3Z,Z3,>3F33F3_,Z3Z3,>32&&33P3_,Z3Z3,>32&3&3P3_,Z3Z,3?3F33P&&3_,Z3Z,3?3F33P3_3Z,Z,3=3<&3& 3P3_,Z3d,3=&-3F3& &-3Z3&7&#&#F3P,Z3,#&&020&.F&.&&)2)2F.P2.020)(#(#)#-1#1=&#& *P*=&& *F*=&-& .P.&&&& .F#7&F&.#7.<&&..F&-.&7##9(&P.Z&9.#_&&&&-#A&#&/MTrkJAZ2PianoD T]@{[KM4?MA8A P4V PVbD'M$GJ!V&P'MDGJVP`NE$:30R3NO,9$O3RQ>K)H9B2KQHBT]-< Q#%54%Q<5]]7<;Q75<Q<5]tNE+:50S3NO,9OS5+K)H9T>C2KTCHTZ-N#9 (34(N39ZZ7N79;3<N93ZrY-M#7 -24-M72YY7M77;2<M72YtME#:P320MM,9PM2# $&:370?3 $:?7^K)H9Q>C2KQCHQ"&7350>3 "7>5^P>G9J)A2JPGAOO4T OTbC'FK$H!T&O'CKFHOT[)&9350?3 )9?5UM4R MRbA'I$DF!R&M'AIDMFR]DAIFRM(2AFDIRMF3D)A3M-I/R IFADMR]0+ 0F7FK4F+K?7F?Y-M#9 (34(M39YY79;M73<M39YITLTM-MJA8JCT?E:JAETK$&:370?3 $:?7UM4T MTbA'K$EH!T&M'KAEHMT\M@J;.,:.MJJ+M+.* JM.+&;370A3 +;A7\NE$:30R3NO,9$3ROH9Q>K)C2KQCHXJ)R>F9A2JRAFTY-8 M#"24"M28YY78;M72<M82Yr[-O#: #34#O:3[[7:;O73<O3:[O)+ )E7EK4E+K?7E?fRLRM-MJA8R?ICD:IADRPNE$:30R3NN,9NR$3 M4T MTbA'EK$H!T&M'KAEHMTb>*F%?>F.?;F>G?F 5<5F2@A@F@F3AAAFwO4T OTbC'FK$H!T&O'KCFOHTbT>K)H9A2KTAHQ';ADFC>@'A>FP:126RKM?FCM|: 2RF22>-?DC?>?C+5x+ K4R KRb?'H$CF!R&K'H?CKRF`OE,:60T3OR,96T,RV>M)J9E2MVJEXV>J9M)C2MVJCT[-: O#*$54*O5:[[7O7:;5<O:5[]4(@1$-:.72:7@$4UOILHVKJIF6 LJVFO-O5KA&KOJ!N'$JN&KHO5$KJ'ONH9K)C2KTCHQ"&7350>3 "7>5UM4S MSbA'J$DG!S&M'AJDMGS[$&:370?3 $:?77)+ )E7EK4E+K?7E?Y-M#8 -$14-M18YY7M78;1<M18YdNE$:R330NN,9$N3RY-M#9 (34(M93YY7M79;3<M39YJRR)RDM1MRDKR M/M )&:370?3 ):?7\LE):30Q3LM,9M3)Ql.+ .C7CJ4C+J>7C>ME#:P320MM,9#2PM $&:370?3 $:?7UO4T OTbC'K$EH!T&O'KCEOHT^Y-7 M#-24-M72YY7M77;2<M27YrY-8 M#"24"M28YY7M78;2<M28Yr[-O#: #34#O:3[[7:;O73<O:3[o)&9350?3 )9?5\IE.$=%P3II, PI .= $&:360?3 $:?6\LE):Q330LM,9)3MQM$WM!A!rAU;L'OOULVG+K'L;K LVJ?R>ME>RMJ2NE$:R330NO,9RO$3 )&9350?3 )9?5ZM4?MA8A O4W OWbC'GM$K!W&O'MCGWOKYO4T OTbC'FK$H!T&O'CKFOHTYO4T OTbC'EK$H!T&O'CKETHO["&7350>3 "7>5^G9P>J)A2JPGAXK)H9T>C2KTCHXH9Q>K)C2KQCHW<-A?=D<=A)5x)T>K)H9B2KTHBOM4T MTbA'EK$H!T&M'AKEHTMYM4T MTbA'EJ$H!T&M'AJETHM]EAJHM(T2AHETJME)A3H3M-J/T JHAETMKC?K525 H?TC*TH5Y7(W4(T/(Q9(K7(H,YQKTWHbRLRM-MJA8R?JCC:JACRI3(?1$-:.72:7$?3UOIKHTKHIF6 HKTFOWK4R KRb?'CH$F!R&K'H?CRFK^^-R#< +64+R6<^^7<;R76<R6<^O2+ 2H7HM4H+MA7HA O4V OVbC'GM$J!V&O'MCGOJV[+&:370A3 +:A7E@14($-:.72:7$4@UOILHVKJIF6 JLVFO3TLTO-OJC8T?KCF:KCFT!J$E7?#xYD Y\EJ]DE?\2]7`O(`M"&7350>3 "7>5\ME#:20P3MM,9M#2PH9K)T>C2KTCHXK)H9Q>C2KQCHOM4R MRbA'J$CF!R&M'JACRFM;/+ /D7DJ4D+J>7D> $&:370?3 $:?7^K)H9T>C2KTCHQ.;ADFC=@A.=FP:1M?16RKFCM|: 1RF11K)T>H9B2KTBHXH9K)Q>A2KQHATMWM!ArAUGLCO LOUR&RV7R6M=>RMV+O4T OTbC'K$FH!T&O'KCFHTOcKC?K)2) H?TC*TH;M4?MA8AY-8 M#"24"M28YY7M78;2<M28YO0+ 0F7FK4F+K?7F? O4T OTbC'EK$H!T&O'CKEHTO["&7350>3 "7>5^P>J)G9A2JPAGT[-O#: #34#O3:[[7O7:;3<O3:[vH9K)Q>C2KQCH,MR)MDF1FMDKM F/FZ-N#: #34#N:3ZZ7:;N73<N:3ZtLE):Q330LM,9QM3) 2(>1"-9.52952">UMIJHTKHIE6 HJTEMWO4T OTbC'K$FH!T&O'CKFTHO^Y-M#9 (34(M39YY79;M73<M39Yo"&7350>3 "7>5^S>K9M)C2MSCKOO4T OTbC'K$FH!T&O'CKFTHObH9T>K)C2KTHCTM4?MA8AME#:20P3MM,9MP2# O4T OTbC'K$FH!T&O'CKFOHTbK)Q>H9C2KQCHQ"&8350=3 "8=5EN4T NTbB'FK$H!T&N'KBFNHTYM4T MTbA'K$EH!T&M'KAETHM`IE.$>%Q3IJ, QJ .> T7(R4(O/(K9(F7(C,FKTORCKC?K)2) H?TC*TH8'&7330>3 '7>3KAO5&KOJ!N'$JN&KHO5$KJ'ONXKYM@CMTY-M#7 -24-M72YY77;M72<M27YvJ)P>G9A2JPAGT[-: O##34#O:3[[7O7:;3<O:3[tLE):30Q3LO,93OQ)Y-7 M#-24-M27YY7M77;2<M27YvP>G9J)A2JPAGXT>K)H9C2KTCHQ)&9350?3 )9?52MR)MDF1FMDKM F/Fl0+ 0F7FK4F+K?7F?LE):30Q3LM,93QM)gMR)MDH1HMDKM H/H O4T OTbC'FK$H!T&O'KCFTOHcKC?K)2) TCH?*TH84L 5O 4G)P 5M)DKLTMUTLM>T @/MTrk($JAZ2Guitar[K{ 'Y]@9/>/Ap997>>7AA"9>Ae5.,(@;.>">;5,T@58;8># >5;l:.@0(7.?C":C70?T:8@?#78C# ?:C7l@5(9.?.Aj95?A 50@98?8A#?9A5c@2(<.5.>">52<T@<858># <5>k+(5/;/>p557;;7>+>">;5d0(7/:/?Cp7:77?:7?"0CC":7C?e9.0(@?.A"09?AT98@?8A# 9A?k.(7/>/Ap777>>7A.A"A7>e/(8.>.@A"8A>/T@>888A# 8A>l:.?.0(@C":C0?T@?8:8C# :?Cl0(@?.9.C"C?90T@?898C# 9?Cl.(@7.>.Aj.A>7 @.078>8A#A7.>c/(>.@8.A"/8>AT@88>8A# A8>l@0(:.?.Cj?C0: @00:8?8C#?0:Cc@5(9.?.C"C?59T@98?8C# 9?Cl@8.=..(A".8=AT@88=8A# 8=Al@.(8.=.A".=8AT88@=8A#8=Ag@0(:.?.BjB?0: @00:8?8B#?:0Bc@5(9.?.Aj?59A @5098?8A#95A?b.(9/>/Ap997>A>7A".9>Ad.(9/>/Ap998>A.>8A# >9Ah0(:/?/Cp::7??7CC"0?C:e?.9.5(@A"9?A5T?898@A# ?9Ak.(7/>/Ap777>A>7A".A>7d+(5/;/?p557;?;7+?"5?;d0(7/:/?Cp7:77?:7?"0CC"?:C7d0(9/?/Cp997??7C0C"?C9e.(@7.>.Aj7A.> @.078>8A#7.A>c@8./(>.A"/A8>T@>888A# >8Ak0(:/?/Cp::7?C?7C"0C?:e@5(9.?.CjC59? @5098?8C#59C?`.8 = Ak.A8=87=7A"=8Aa.!8&=&Ao.8A=88=8A#=8A_.(6//Ap997>A>7A".9>Ad.(9/>/Ap998>A.>8A# A>9i@0(:.?.Cj:?C0 00@:8?8C#:0C?b5(9/?/Ap997?A?75A"?A9e3+@:2>2Cj>:C3 @34:<><C%>:3Cc@3+:2>2Co3C>:@34:<><C%3:>Ca0+@:2?2Cj?:0C 04@:<?<C%?C0:c@0+:2?2CoC:0?04@:<?<C%0:?Ca72.+@<2#.7<T@7<<< 7<l@,+62<2?j6?<, ,4@6<<<?%<6,?c2+@52<2>j25<> @245<<<>%<5>2c@++52;2>j5>;+ @+45<;<>%>;5+c++52@:2>":+>5T@5<:<>% >:5l:252++@>"5>:+T5<@:<>%>:5d4$:*>*Ck:C>4:A>AC(:>Ca4):0>0Co4>:C:B>BC)C:>_0,:3?3C p::;?C?;0C%C?:d0,:3?3C p::<?0C?<C% :?Ch0,93?3A p9:;?A?;C%0:?Cd0,93?3A p99<0?A?<A% 9?Ai@.+72>2A">.7AT@><7<A% >7Ak/,83>3A p88;>A>;/A%>A8e@0+:2?2Cj:?0C @04:<?<C%0?C:c@5+92?2Cj?59C @549<?<C%59?Cc@.+72>2Aj.7>A .4@7<><A%.A>7c@/+82>2Aj/A>8 @/48<><A%/A>8b0,:3?3C p::;??;C0C%C:?e?25+92@C"C95?T@9<?<C% ?9Ck.,83=3A p88;==;A.A%8=Ad,,53:3= p55<:,=:<=% 5=:i0+@:2?Bj0:?B 04@:<?%B%0:?Bc0+@92?2AjA90? @049<?<A%90?Ac@.+92>2Aj.>A9 @.49<><A%>.9Ac@.+92>2AoA>9..4@9<><A%A9.>a?2:2@0+C"C?0:T?<:<@C% C:?k5,93?3A p99;?A?;5A%9A?e.+@72>2AjA>.7 @.47<><A%.7>Ac/+@82>2Aj/A>8 /4@8<><A%>/A8c0+@:2?2Cj?:C0 @04:<?<C%:?C0c0+@92?2CjC90? 04@9<?<C%C?90b.,73>3A p77;>A>;.A%>A7d/,83>3A p88;>A>;A%/8>Ae0+@:2?2Cj?:0C @04:<?<C%C?:0c@5+92?2Cj59?C 54@9<?<C%9?C5b.,83=3A p88;==;AA%.=8Ad,,53:3= p55<:=,:<=% =:5i@0+62:2?"60:?T@6<:<?% ?6:l5+@92?2Aj9A?5 @549<?<A%9?A5c@.+92>2Aj>A.9 .4@9<><A%.9>Ac@.+92>2AoA.9>@.49<><A%9A>.`0,:3?3C p::;?C?;0C%?C:d5,93?3A p99;??;AA%5A?9e.+5+@>2C">C5.T@><54C% C>5l++52;2@?"+5?;T;<@5<?% 5?;k0,73:3? C p7:7;?:;?%C0C%7C:?e5+@92?2Cj5?C9 @549<?<C%C5?9c.+@5+>2C"5C>.T><54@C% >5Cl/+@82>2Aj8A/> @/48<><A%>A/8b0,:3?3C p::;??;C0C%:C?d5,93?3C p99;??;C5C%C?9b.$8*=*AkA.8=8A=AA(8A=a,)50:0=o5,=:5B:B=):=5`:20+?@B"0B?:T@:<?%B% B:?k5,93?3A p99;?A?;5A%A9?b.$9*>*Ak>9A.9A>AA(>9Aa.)90>0Ao9.A>9B>BA)>A9`0+@:2?2Cj?C0: 04@:<?<C%0:?Cc0+@?292A"9A0?T@9<?<A% 9A?l@723+:2>C"7:3>CT@7<>%:<C% :>7Cl:2@3+72>C"3>C7:T>%@7<:<C%C>7:g?2@0+:2C"C:0?T@:<?<C% C:?l?2@0+:2C"?:C0T:<?<@C%:C?g@72.+<2#<7.T7<@<< <7l62,+<2@?",?<6T6<@<<?% ?6<l@2+52<2>j5>2< 24@5<<<>%>25<c@++52;2>j>+5; +4@5<;<>%5;>+c:2@52++>"5>:+T5<@:<>% >5:l++52:2@>"+5:>T5<:<@>%>:5g0+@42:2>j:>40 @044<:<>%0:4>c0+@42:2>o4:0>@044<:<>%4:0>a0+@72:2?Cj:70?C 04@7<:<?%C%7:C0?c0+@72:2?Co7:?C0@047<:<?%C%0C:7?`5,93?3A p99;?A?;A%5?A9d5,93?3A p99<?A5?<A% A?9i@.+5+>2Cj5>C. @.454><C%>C5.c@/+82>2Aj>A8/ @/48<><A%/A>8c:20+?2@C"0C:?T?<@:<C% C:?l?25+92@C"C?95T@9<?<C% C?9k.,73>3A p77;>>;A.A%7A>d/,83>3A p88;>A>;/A%8>Ad0,:3?3C p::;?C?;0C%?C:e92?25+@C"9C5?T@9<?<C% 9?Ci.$8*=*Ak.=8A8A=AA(=8Aa,)50:0=o5,:=5B:B=):=5`?0+:2@B":?0BT:<@?%B% B?:l@5+92?2A"A59?T?<@9<A% A9?i.$9*>*Ak.>9A9A>AA(9>Aa.)90>0Ao9A.>9B>BA)A9>`0+?2@:2C"C0:?T?<@:<C% ?C:l5+@92?2AjA?95 @549<?<A%9A?5c@.(7.>.AjA>7. .0@78>8A#7>A.c8.>.@/(A">A8/T@88>8A# 8A>k0(:/?/Cp::7?C?7C"0:?Ce5(@?.9.C"95?CT@?898C# 9C?l>.5(.(@C">C5.T@>850C# 5>Cl/(>.@8.A"A8>/T88>8@A# >A8k0(:/?/Cp::7??7C0C"C:?e5(@9.?.Cj9?5C @5098?8C#9?C5c.(@=.8.A".=8AT=888@A# A=8l8.=.@.(A"A.8=T=888@A#A=8g0(@:.?.Bj:B0? @00:8?8B#?:0Bc9.?.@5(A"?A95T98?8@A# ?A9l.(@:.>.A">:.AT@:8>8A# A:>l.(9.>.@A".9>AT@98>8A#9>Af0(:/?/Cp::7??7C0C":C?d5(9/?/Ap997?A?7A"59?Ad.(7/>/Ap777>>7A.A">A7e@+(5.;.?j5+?; @+058;8?#?+5;c@0(7.:.?Cj07:?C 00@78:8?#C#7:?0Cc5(@9.?.Cj59C? @5098?8C#?C59c@.(7.>.Aj>7.A @.078>8A#.7>Ab/(8/>/Ap887>A>7/A">A8d0(:/?/Cp::7??7CC"0:C?e@0(9.?.Cj9C?0 @0098?8C#?9C0c8..(@=.A".A8=T@=888A# A=8l=.8.@.(A"=A.8T88@=8A#=A8f0(:/?/Bp::7??7B0B"?B:d5(9/?/Ap997??7AA"5?9Ae@.(9.>.Aj.9>A @.098>8A#.A>9c@.(9.>.Ao>.A9.0@98>8A#>A9.`0(:/?/Cp::7?C?70C":C?d5(9/?/Ap997?A?7A"5?9Ad.,73>3q77;>>;.7>d.,73<3q77<.<<< <7f0$:*?*Ck:0C?:A?AC(:?Ca0):0?0CoC:?0:B?BC)?:C`<2@3+72?"37<?T@<<7<?% ?<7l620+:2@?"60?:T@6<:<?% 6:?l92@<22+AE"E29A<TA%@9<<<E% 9E<Ak+,53;3> p55;;>;;+=%;=5d+,53:3> p55;::;>>%+>:5d+,53:3> p55<>:+:<>% :>5f0$4*:*>k40>:4A:A>(4:>a0)40:0>o:0>44B:B>):>4]0$7*:*?Ck?7C:07A:A?(C(C?7:a0)70:0?Co:0C?77B:B?)C)?:7C`92@?20+A"A?90T@:<?<C% ?C:l0+?2@92A"09A?T?<@9<A%A9?g72>2.+@A".>A7T@7<><A% A>7l82>2/+@A"8A/>T@><8<A% A>8l?2:2@0+C":C?0T?<@:<C% :C?l?2@5+92C"5C?9T9<?<@C% 9C?l.+@5+>2C"5C.>T54@><C% 5C>k/,83>3A p88;>A>;A%/>8Ad0,:3?3C p:8;?C>;0@%>@8e@5+92?2CjC?95 54@9<?<C%95?Cc@.+82=2AjA=8. @.48<=<A%8.A=c.+@82=2Ao8A.=@.48<=<A%A.=8a@0+:2?2Bj0B:? @04:<?<B%?0:Bb5,93?3A p99;?A?;5A%A?9e@:2>2.+A".A>:T@><:<A% >:Al92>2@.+A"A.>9T9<><@A%9>Af0,:3?3C p::;??;C0C%:C?d5,93?3A p99;?A?;A%59A?d./97>7A"k9.A> .69?>?A' >9.Ah./97>7A"o.9A>.79@>@A( 9.A>h./97>7A"o>9.A.79@>@A( 9>A.F.>9H>HA-.9>Au@/MTrk3JAZ2Strings9 N{[U]@>5 . :2^@@8+9;.52:>A1@#:@078;+A?1@#?) 5 0?7@9 :<3^@9<A3<5)?@5&E@@x65;+A@E&5A<C@@x607:+@5;CA?1@#)07?:5@9?1@#"59)7?@>2A@@x6A@5/8"72>>1@#>58/?3:$@C@@x6$3?9 @C:?5 ) >3^@5)@3>9?">27A@@x6>"A5@/872>1@#@: 0 ?7 8/5><3^@) 5 ?9 @<0:73?>3^@?3)"=158@9>A@@xx.1A="8@:06?1@#)96:0?@5?1@#@9.559?)>1@$5>9@3:.?C@@x6C$9?@3:3?)A@@x6)A3?9>7"@2A@@x68 2>+ A7"@; A?5^@8?3:$@5?A;+C@@x6:@5 3C9 $??) >3^@>35?)9.75@>1@#7/5>85@.>1@#: 7 58/>0 @?>3^@:?)@93?07>3C@@x65.9@C8)?3=1@0 6 8.5?=: @<3^@?:6950)@3<?1@#. 5 >9 5@?9):2^@>)3?.@:29:5C@@x6) @?C9 5 ?):3>3^@>3@JAC5?)9"F&2^A"C2FCRHJ: 0K^K0?CRHOF:H'7 nF'H?O7NHF'6M&nNJETM6'FH2M< n2TEMJ<CMG&5L&nCL&5MCJRGM: +^CFOBM:RJ+4$L&^FOLCFOB$4K&3$^$ENFBK3COK&)3^3OFA)EBKN7 .JnAPG7.FOJA/J8 nF8C/GAPJOK&3$nCFOE$3KCKH&$3nKEH3C$AMC2J&"nMCJAAG2"PJ/8 nFCO8AP/JG$3K&nQK$FCHO3CK9 )nQK9)MDHCA1I&"^AHRBDM"1I0K: nKEA0BRH:KI&$3n$EEMAI3AK"2J&^COF"EJMA2K&)3nENBOFCK3)3K&)n3KB)EFAON.7 JnJFOAGPA.7J/8 nAGF8J/PCOK&$3nCCKEO3$KF$H&3nAKEH$3FOCJ7 .nFA.7OJPGA/J8 nPFC/OAG8J3$K&n$K3OCCFKE$3H&n$CKEH3MDAI&1"^"IMDA1HBR0K: nI:KAQRBH09 )KnK9AQA)HIQ9 J.^HAQR.9JCH: K0n:CHKA0QIRK9 )n9IMAQ)CAK2"J&n"J2AGOCCMM&+5nGOCRH+5MC0: KnCJQ0:HRCK9 K)nC9QK)JOFA.J7 nMDAOA7F.JJ&#2nA2#MJDCRJ0: KnRCJEOCK0:3)K&nEK3)COFPAI.8 ^F8IAF.BPNK&3$nAENFNK3$BK&)3n3)KANEEMA2"J&^A"J2EMRHC: K5nRC:EMCK5H3K&)nCJAMCK3)E"F&2^CJAF2R"CJK: 0^?OJFR:K0C'H7 nH?O'H7FRFN&6,n6RTHF,JEN2< Mn<M2EGCOJT5+M&nGCMEF+M5OJ&5&^QEFJ5FEM&L&$4^QLF$4EJRC0: K^HAQJCKR0:9 )K^QK)H9ACMA2"J&nMCAAGPJ2"/J8 nJ/GPOFCA83K&$nOC3QJKCF$)9 Kn9FAOCQJ)K.7 JnO7.AFJPGA8 /Jn/8JPFAOCG$3K&n3$KOCFCKE$J&3nAKMDE$3JCI&1"^NFBDM"I1A3K&$n3FNBKBEK$3$I&nEBK3$IHQAJ9 .^H9.QJCAOF)K&3nANOE)3KFCK&3)n3)AKE5.7N>1@#/58@>57.>1@#>85: @/0 ?7 >3^@5 :?) 03@>?79 <3^@@5 . >)7 59?3<:2^@@8/2:7>.55>1@#?8>7 @0 : /5<3^@<:07?3@)59?1@#8"15)9@?=A@@xx.?0 A8"=16 : @<3^@3<?:605@9)?1@#. 59>)@5 9 ?:2^@?$:2.@3>59:C@@x63:C?$@5 9 ?) <3^@5 7 . ?>@93<5):2^@@:; .2+ A78 >5?5^@0 ?+A;87 5?: @>3^@7:?3>05 9 ?@) >3^@2")@>7359?>A@@x6A7285/@>">1@#>8/3?$5:@C@@x6:$?C35 ?) @9 >3^@85@)95?>3.=1@$=.58?:@3B@@x69:@)3B??3$A@@x695@.A?93)>1@>.5?9: 7 5 @>3^@7?:33>5?9)@A@@x6JA@C?)9A3F&"2^C2F"JACFO$3K&^?HCFOC$K30"F&nF"0?HCFHN'M&6n6M'HNFTJEM< 2nEJT<M2CGM5&L&nJRE&GMCL5M: +^+:MREEJOFL&$4^RCJOFL$4E0K: ^JCRIAQ:K0)K9 ^9AOAFK)IQ.7 JnO7DFAM.JA2#J&nFMJ#2ADOC$K&3nEO3K$CFKCH&3$nOCKA$3HFE.J7 nFAOA7.MDJ2#J&nOFCM#JD2A$3K&nF$KJQOCC3K)9 n9AM)DKQCJ1I&"^"AHRD1IBMK0: nRBHQB:I0K9 K)n)HQIQABK99 .J^QH.9AJCRH: 5Kn5HEAKK:CR$3I&n3$IEJAAEK)H&2>EJAH)2@/MTrk JAZ2[Z ?]Voice 1 (Melody) Upc{zVw&VTG%TVS)YaVY Ww$WVN*WjVV_W"VWe,[^W2[1Yt&YXQ"XYg*\]Y\ [j#[ZG([HZ]Q["]^e-`N^Z`W_7W YSSYUmJUWgMWY]YUSYPWfU6WYSRY`Z&` YN7YYZ.[GY([[e2^Z[-^^X2^bS$bbLbe]Ue+Vw&VTG%TVS)YaVY Ww$WVN*WjVV_W"VWe,[^W2[1Yt&YXQ"XYg*\]Y\ [j#[ZG([HZ]Q["]^e-`N^Z`W_7W YSSYUmJUWgMWY]YUSYPWfU6WYSRY`Z&` YN7YYZ.[GY([[e2^Z[-^^X2^bS$bbLbe]Ue-Vw!VWX&WY_+VVY/V Rg'R VXpVYjDYRDRRN4NOa/ORm&RFkKFMZ>MPJS6JJl"JJ_HH_J!HJ_:J JgDJJj"JH_*QaH=Q QQQQf8QQQMQ*V_"VWQ:W VL0VW_MPJS6JJl"JJ_HH_J!HJ_:J JgDJJj"JH_*QaH=Q QQQQf8QQQMQ*V_"VWQ:W VL0VW_MPJS6JJl"JJ_HH_J!HJ_:J JgDJJj"JH_*QaH=Q QQQQf8QQQMQ*V_"VWQ:W VL0VW_OXOXAP>L6>A>XI>1>X:>.EZ@EwJX"JKJ:K>KXAP>L6>>e"> >X:>.EZ@EwJX"JKJ:K%H@HWJp&J)JL)MZJM/JG*KcJK#K^,OWK2O1Mm&MSPVPWOA"OPTG\TKX7K MLSMMVYILMPK_I6K)MG7MMS4M8RQ2ROYVUY+Jp&J)JL)MZJM/JG*KcJKOOW7O1Mm&MSPVPWOA"O#R^-TGRZTKX7K MLSMIfJIRMVYILMPK_I6K)MG7M~RQ2RVL$VVEVYVUY+Jp&J)JL)MZJMYKc"K#K^,OWK2O1Mm&MSPVPWOA"OPTG\TKX7K MLSMMVYILMPK_I6K)MG7M~RQ2ROYVUY-Jp!JKQ&K,JO4J F`'F JQpJQF=RFKCZ/CFf&F:dK:AS>AP>L6>A>XI>1>X:> >`D>>c">-EZ@E'KJ:K>KX% &Gq&!?a!!DP!(?(&G?&&= -?&k-Ab!PH!2U"!Ab!&Ab&Ab!Ab!#B %<#"&F% &AT@PB!AB!%B%}!Rk!!^s!!Cp!(C:(%>%&Ew->&S-+Fk+&JP4&(Gq(#?a#(=TI(#= a#!Ew(>!S(&Fp-<&g-Ab!AB!%B%&Ab&#Ab#!Ab!&G?&&= -?&k-Ab!C*!!@"!@PB#Ab#(PH2(!AT!#Ab#(PH(2Ab!C*!!@"!~!Ok!![s!&Ew&!>S!!Ab!&@P&B&&AT!AT!Ab#Ab#(AB( B !G?!!= (?!k(#B %<#"&F% &(PH2(Ab!Ab!&@e& -Jx': -O'%B% &Fk&!JPU"4!!B #<!"$F# $&C*&&@"&Ab!PH!U"2!B #<!"$F# $&Ab&@PB!AB!"B"#AT#(AB('B'(Ab(!@P!B!!}!Zk!!]s!!Cp!(C:(%>%&Cp-C&:-*>*+Fp2<+g2DG#=N#*#B#(Hp#; (#9#B#!Hp; !9B&@p-C &G&@-& (AB( B !DP!(?(&Ab&#AB# B !AT!&AT&@PB!C*!!@"!@PB#AT#C*@"!C*!!@"!#Ab#@PB(PH2(!@P!!B!}!SC!-V- !Qs!&Ep-A&-1-:2G-!2&?/&/MTrkJAZ2a YDrums @General MIDIp{]#< Drums (BB)[(,<#,_%F,P%,7,(#(,##<,F#,7#2#,P%F%,7#(,(,##<,<,#_,P%F,%7#(,(#,,F#<,#7#2#,P%F%,7,(#(#,,<#<#,_,P%F,%7,(#(,#,F#<#,7#2#%F,P%,7,(#(,##<,<,#_%F,P,%7#(#,F#<#,7,$,%F,P&<%&,7&$.(&.,<#<,#_%F,P%,7#(##<,F,#_%F,P,%7.#(#.,<#<#,_%F,P%,7#(#,F#<#,7,$,%F,P,%7.#(#.#<,<,#_,P%F%,7.#(#.#<,F#,_%F,P%,7#(.#.,<#<,#_%F,P,%7#(#,F#<#,_,P%F,%7#(..##<,<#,_,P%F%,7.#(.##<,F,#_,P%F,%7.#(.##<,<,#_,P%F,%7#(,(#,#<,F,#7#2#,P%F,%7,(#(#,,<#<#,_%F,P%,7#(##<,F,#_,P%F,%7#(..##<,<,#_%F,P,%7#(.(.#,F#<,#7&(&,P%F&<&%,7#(#,<#<#,_%F,P%,7#(,(#,#<,F,#7#2#,P%F%,7,(#(,#,<#<,#7,,,P%F,%7,(#(,##<,F#,7,#(,#,P%F%,7,(#(#,,<#<#,_%F,P,%7#(##<,F#,_%F,P,%7#(##<,<,#_,P%F,%7#(.#.,F#<#,_%F,P%,7#(#,<#<#,_,P%F,%7#(.#.#<,F,#_%F,P%,7#(##<,<,#_%F,P,%7#(#,F#<#,7&(&%F,P%,7#(.#.,<#<,#_,P%F,%7,(#(,#,F#<,#7#2#%F,P%,7,(#(#,,<#<#,_%F,P,%7#(#,F#<#,7&(&&<%F,P&%,7#(##C&219,C,&#1_,W%M%,7#/##C,M#,_%M,W%,7.(#/.#,B#B#,_,V%L%,7#.##B,L#,_,V%L,%7#.#,B#B,#7,,,V%L%,7#.,(,##B,L,#7,,%L,V%,7,(#.,#,D#D,#7,,%N,X%,7#0&(&##D,N#,7&2&,X%N,%7&<.#0&#.#B,B,#7,,,V%L%,7#.,(,##B,L,#7,,,V%L,%7,(#.#,,B#B,#_%L,V%,7#.#,L#B#,_,V%L%,7#..#.,B#B#,_%L,V%,7#.#,L#B,#_%L,V%,7.#.#.#D,D#,7,,,X%N%,7&(#0&##D,N#,7&2&%N,X,%7#0&<#&#<,<#,7,,%F,P%,7#(,(#,#<,F,#7#(,,#,P%F%,7#(,(#,,<#<#,_,P%F,%7#(##<,F,#7&(&,P%F,%7#(##<,<,#_%F,P,%7,(#(#,,F#<#,_,P%F,%7,(#(#,#<,<#,_,P%F%,7#(,(#,#<,F#,7#2#%F,P%,7#(,(,##<,<,#_%F,P,%7#(##<,F#,_%F,P,%7#(#,<#<,#7,,%F,P,%7#(,(,#,F#<#,7,,%F,P,%7#(,(,#,<#<#,7,,%F,P,%7,(#(#,,F#<#,7,#(,#,P%F%,7,(#(,##D,D#,_%N,X,%7#0##D,N#,7&0&,X&D%N&,%7#0#,<#<&212,#&1_,P%F%,7.##(.#,F#<#,_%F,P,%7#(#,<#<,#_%F,P,%7,(#(,##<,F,#_,P%F,%7#(,(,#,<#<#,7,,%F,P,%7#(,(#,,F#<#,7,,%F,P%,7,(#(#,#<,<#,_%F,P,%7#(..#,F#<#,_,P%F,%7.#(#.,<#<#,_,P%F%,7#(.#.#<,F,#_,P%F%,7#(..##<,<,#_%F,P,%7#(#,F#<,#_%F,P,%7#(#,<#<#,7,,,P%F%,7#(,(,#,F#<#,7#(,#,%F,P,%7#(,(#,,<#<#,_,P%F,%7#(#,F#<,#7,$,%F,P&<,&%7#(#,<#<#,7,,%F,P,%7,(#(,#,F#<#,7,#(,#%F,P,%7,(#(#,#<,<,#_%F,P%,7.#(#.#<,F#,_,P%F,%7#(##<,<,#7,,,P%F%,7,(#(#,#<,F,#7#(,#,%F,P,%7#(,(,#,<#<#,_%F,P%,7.#(#.#<,F,#_%F,P,%7#(#,<#<,#_,P%F%,7,(#(,#,F#<#,_,P%F%,7#(,(,##<,<#,_%F,P,%7.#(.#,F#<#,_,P%F,%7#(#,<#<,#_%F,P%,7,(#(#,#<,F#,7#2#%F,P%,7#(,(#,#<,<,#_%F,P%,7#(#,F#<,#7&(&&<,P%F&%,7.(#(#.,B#B#,_,V%L%,7#.#,L#B#,_,V%L,%7#.##B,B#,_%L,V,%7#.##B,L,#_%L,V,%7#...#,B#B,#_%L,V,%7#..#.#B,L,#_%L,V%,7#.##B,B#,7,,%L,V,%7#.,(,#,L#B#,7,,,V%L%,7#.,(,##B,B,#_%L,V,%7#.#,L#B#,_%L,V,%7#...#,D#D,#_%N,X,%7.#0.##D,N#,7&0&,X%N,%7#0##B,B#,_,V%L,%7#...##B,L#,_%L,V,%7#..#.#D,D#,_,X%N%,7#0##D,N#,7&0&,X%N&D%&,7.(#0.#&2#<12,<1,#&_,P%F,%7#(##<,F#,_,P%F%,7#(..#,<#<#,_,P%F,%7,(#(#,,F#<,#7#2#%F,P%,7,(#(,##<,<#,7,,,P%F,%7#(,(,##<,F,#7,,%F,P,%7,(#(,#,<#<#,_,P%F,%7#(,(,#,F#<,#_%F,P,%7#(,(,##<,<,#7,,,P%F%,7#(,(,#,F#<#,7,,%F,P,%7,(#(#,,<#<,#_,P%F,%7#(,(,#,F#<,#_%F,P,%7#(,(,#,<#<#,7,,%F,P%,7,(#(#,,F#<,#7,,%F,P%,7,(#(,#,<#<,#_%F,P,%7#(#,F#<,#7,$,%F,P,%7#(#,B#B#,_,V%L,%7#....##B,L,#_,V%L%,7#.#&,,@#@&#,7&6&+&&@,T%J#@&%#&,7#T&T1,1&#/MTrkJAZ2 +General MIDIVPiano @p{29[(]@ Piano (BB)72LJ-=:LB0>+EA9,`=E>9B(FK(GD(;3>7427CP@A;4>7G@*<=P*79G='1*6-4Bs4196GD@->BL?75(C7G!CG@LG>7@ 2<EP27BGE'>-=B9*s9B>=GP4<47CGG'>B;*@-s@;C>*DP9A$9-C4JBK1?(=<B 9Z =-1*94(FK(GD(>7427C;3P>7;@A4G@L3I3&5@NC&\>96>98>96a@IL-1N@-/LNNL&@><9167%>96L NI.I@G9B-C(4E(B2CEG(9E(<>P+<+7;G>'7-2*4Bs72;4-?EEIQ-Z='4#7,9$E79=EI4.J(GCG(@=C#@>4@G-C9B(4E(7H=N@974-=/Ak/ >=G;699;"(IEGJPI >6;9J4974NJ-D0;,>:NGAB+`>GB;D4?OQLE4Z>,@$C';#E>@CL;O.-FK-ED(=77C9342P7=94@AE@2Gh2>;;S9;B>9;EIGGNKNG-1:E-NGG@(B/>=9.;GsNBG>;92IP2LE(>49-B/=>&LB>=9 E6#E-,LFC-LL@93B%=D>4LI=B>9-97-EJ-E<07:4,@A9+`7@49<2KH@(G2H 2E6GE@@<>64DPCA$CLK>J7C;?(G<L CZ G>74C;-LP99$9@S7J(=G@(9==#-79@D7BE?9-05(<7@!<@@@E709 2<EP27BGE'9*-s9<>B4DPCA$C>JLK;?7C(G<L CZ G4>7;C@D157BE?9-(@=7!=@@@E719 G?;-9BBD25(>7B!>B@BG92; GP4<47DGG'B->B;*s;D>B4Ak4 >9C=L;@";(NELOPN O@;>C-<@P-7=G@'7B4*9-s947=-IP-JE(>4;>9-B/&JB;>9 E6#E-,JFC-JJ@93;D>4B%JI;B>9@9E0-JH3?-HE@@)H0E,@EH4593<7+49<"EB(HEE(B((HE=(E@C(4:9<4/@2IPQC(BH.EG)(B>7;GJH(G3J(B<G+I(+(E)B%7+>567%B&(B>&67>E >4EPG84(GC:;-SO@1>H)NCC;@ >ORN /4J/.;*C:>K@;m@;>COS(Sb(7FC92=@,>9&> C@ 272#E@D>@@K#>_(7R2H;G$>,4F;@724-KC- EB(=:467G9-LZ!E74I>9=#LE?%IQV-DE;-LU(7@4&9-=)s74L9=Q2=s2L49/6<>/(96>E4(BEL'E-ALJ B-EEL(6794 96B<(>@E>B4DSL4Z;1C:@,>=;>C@;2S-DLL-Z9,=:7=41;79=42L>-65;BJ?ED(B7E!BE@EJ;6> /DP>A$>GK2C9J6?(B<G >Z B269>/EE-?HQ-Z7,9$<'4#E79<HE4.2972JJ-JB09,EA<:>+`<>E9B+<>P+7;G>'2*4B7-s472;E?9-7B@D15(@=7!=@@E@719 *?BEEQ*Z6$0#4,9'E4960BE./FK/HD(62<3?79CP96?<BAHB4LP@9$@>JGS(CGG(C@=#>@4-FK-ED(7C4293=7P479=@AE@/LP;9$;9JBS(>GB(>;=#9;/(FK(GD(8C42>7;3P;84>BAGB@->BL?75GD(C7G!CG@LG>7@ @P-<-7=G@'4*9-7Bs9=47-1$-@+GU%@> 906#6>G9E9E -AG[-5EDGE>=69*BK> 69B;F ;&>&@EQIW$6:9/>4!IE 9>6'B"BITE=-46E-IB4&B69>09/.>B69#BED$E-Ak- 79E;9"4<=(GEEHPG 947H<2C>BEG(9E(B2<E9+FK+JD(@7;C>372P@;CA7>JCIQEE-?-Z9$7,4#='E79=E4I.*FK*HD(<3629C@7P<9@BA6HB/FK/HD(9C?7<362P6<9?BAHB75@-GDL?>B(GC7!CG@LG>7@ -LP99$9@S7J(@=G(=9=#-79/DP>A$>2C6?GK9J(B<G >Z B962>/J@4K(I2J 4G6IG8:>DDY(B7 D@@B>@8GP4<47CGG'@-;*>Bs@;C>-<@P-7=G@'7B4*9-s=9472:P2EA(>(BE;C69)E?69;>GOB(E7G-DNB@E-EJ;C>(9/6-OB$>E96;&>&@IWEQ$6:9/>4!IE 96>'B"BITE=-46-EIB4&B69>09/.B6>9#BED$E-<@P-7$9#B'<,E<B>N9J.4LP@9$@GS>J(GCG(@=C#@4>-LP99$97J@S(=G@(=9=#7-9-FK-ED(7C42<793P94@A<7E@JENQ2?2Z<,B'>$9#E<>BJN9.>BGD@-L?75(C7G!CG@LG>7@ 159-7BE?@D(=7@!=@@@E719 BD9BG?;-25(>7B!>B@GB92; BG(C;B(4E(>N8H48B>(;GP4<47CGG'>B;*@-s;@C>-97-EJ-4,=0E7:@A9+`7@49=->-@EQGW$6:9/>4!GE 96>'B"BGTE=-46-EGB4&69B>09/.6>9B#EDB$E-Sb-C9@,0=7F<9&< C@ 072(E@D>@(<_@K(0H7R9G$<,4F9470@2EPE82(EB:9-?1#JE?%GQV+DE;+JU(6@;)2&7-s267QJ;L04JG9O3?L4OGG)O0L,GLO;5@3C7+;C@"LB(LOE/B(L=/O(LGC(C?@6;:>@C;/G@(5G3JCC3(\2>48;9;42aC@G#1J@#/GNJL&@;<2741%;42G JC.C=T-8(9*=9=0-=/D>H(;9> >,;/>?S0,*?4> B9N<JZQC(-DQNJ#-QIJ_NT%>8B/90N JQ>B9JC'EMJ#E4DPCA$C7C>JLK;?(G<L CZ GC47>;-Lx-764 90=6s47=965J?>-;BED(B7E!BE@JE;6> /<BP/7>GB'9B;-6*s;>69-DP-J?ED >D7-C?/54B(>;7!;>@>C4/7 EEIQ-?-Z='9$7,4#E7=9E4I.4BB?-5,E>C@OL;.-97-EJ-4,7:E=09+@A`749@=JQ/?GE/Z;$6#9,>'E9;>G6J.B->BGDN?85(D7G!DG@GN>8B 4LP@9$@>JGS(CGG(C@=#4@>-97-EJ-E7:@A9+=04,`74@9=-Fo- 96QFB.>;F(N2#>B;9JGQ$N-@JQH(N<-QJI!NB3J9'QP>3;=(N=!QJINJ>B9;2KT2$BA9>>?XQ'B>9 Q/XQ NJ-=5-NQO(>691B7,B>9 E3(Q2IISE/2D9:-@I- 9QGUTN,&>WUNQ&@/MTrk.JAZ2General MIDI ]Guitar= @p@=@ Guitar (BB){[(]B(t2<=B9FB,=F9B2=e+<4F;Fp4;++<4F;F 4+;e*74@9@=(n*4*<4F99F==,=9*4c4<7F>Fq44<77F>>F>47\276@9@=(B(n262<96F9F=B=,B,=B269c4<7F>Fq44<77F>>F4>7\*74@9@=(r*4*<=4F99F=,*49=b477@>@@(n44<77F>@>F@,@74>c-76@r--<66F-6^-:6Co6--<6F6-]-<7F(r262<<>6F>,b+74@;@>(r+4+<>;4F;F>,>+4;b-77@=@@(n--<77F==F@@,=@-7c*<4F9Fq**<44F99F*49_*<3F9Fp9*3*<3F9F 9*3f/<7F>Fq//<77F>>F/7>\-77@=@@(r-7-<=@7F@,=F7=@-b/79@>@B(n/9/<9F>>FBB,/9B>c/<8F>Fq//<88F>>F/8>]4<7F>Fq44<77F>>F7>4\-77@=@@(n-7-<7F==F@@,7=-@e-<6Fq6--<6F -6i-<6Fq--<66F -6d276@9@=(B(r2962<=B9F6F=,B,2B9=6c2:6C9C=*B*l2B=962<9F6F=,B,B629=a-77@<@@(r-7-<@<7F@,@q44<77F>>F47>\-77@=@@(r-7-<=7F@=F@,-@=7c-<7F(n262<6F<>,2<6>e/<7F>Fp/>7/<7F>F /7>f-77@=@q--<77F==F=7-]/<9F>Fq//<99F>>F9>/\478@>@B(n44<88F>B>FB,4>B8b477@>@@(r474<>7F@>F@,>74@e-<7F=Fp7-=-<7F=F 7-=h2<6F9F=,o=6922<6F9F=, =296i2<6F9F=,o22<=696F9F=, 29=6e-=7GJp>/7/@7J>J 7>/i/@7J>Jp/>7/@7J>J >7/d4=7G>G@,n474B7M>@>M@047>@c4@7J>J@.l>@744B7M>M@0@4>7a-=7G=G@,r-7-B@=7M@0=M@-=7c-@7J=J@.l@-=7-B7M=M@0@=-7a2=6G9G=,B,n262B6M99M=B=0B0B629=c2@6J9J=.B.l=B2692B6M9M=0B09=B62a4=7G>G@,r474B@>7M>M@07>@4b-=7G=G@,n-7-B7M=@=M@0@7-=c-76@r--<66F-6_/<9F>Fp9>//<9F>F 9/>e-77@<@@(n--<77F<@@@(n44<77F>@>F@,7>4@b-77@=@@(n-7-<7F==F@@,=7-@c/<9F>Fq//<99F>>F>/9\478@>@B(r484<B>8FB,>F4B8>c4<7F>Fq44<77F>>F7>4]-<7F=Fq-.<77F==F.7=]279@=@q22<99F==F=29^2:9C=Cn=922<9F=F29=]279@=@q22<99F==F=92^2:9C=Cn=922<9F=F29=]-<7F(r+4+<>4F;;F>,;4+>c+<4F9Fq++<44F99F4+9\*74@9@<(r*4*<<94F9F<,<*94e/<3F9Fp9/3/<3F9F /39e477@>@@(r474<@>7F@,>F7>@4c-77@=@q--<77F==F=-7]/<9F>Fq//<99F>>F>/9]4<8F>Fq44<88F>>F4>8_4<7F>Fp>474<7F>F >47f-77@=@q--<77F==F=-7]-<6Fr--<66F6-^-<6Fo6--<6F-6_-<6Fq6--<6F 6-i-<6Fq-6-<6F 6-g-<7F@@(r474<@>7F>F@,@4>7c-77@=@q-+<74F=9F+49]-<7FFq44<77F>>F47>\-77@=@@(r-7-<@=7F@,=F-=@7b/79@>@B(n/9/<9F>>FBB,>9/Bc/<8F>Fq//<88F>>F/8>\477@>@@(r474<@7F>>F@,@>47b-77@=@@(r-7-<@=7F@,=F-7=@b276@9@=(B(n262<6F99F=B=,B,296=Bc2:6C9C=*B*l2=96B2<6F9F=,B,=296Bb-=7G,r+6+B;>6M;M>06>;+c+@6J;J>.l6;>++B6M;M>0+;6>d4@7J>Jp>744@7J>J 47>i4@7J>Jp4>4@77J>J 47>e4@7J>Jq44@77J>>J47>^4@7J>Jn>474@7J>J47>\-=7G=G@,r-7-B=@7M=M@07=@-c-@7J=J@.l@-7=-B7M=M@07@-=b2=6G9G=,p262B6M99M==0=269^2@6J9J=.m62=92B6M9M=09=62]+@4J;Jq++@44J;;J;+4_-@7J=Jp-=7-@7J=J =-7f276@;@q22<66F;;F6;2]-76@;@q--<66F;;F;-6]-<7F(n+4+<4F;;F>>,4>;+c-<7F=Fq--<77F==F7-=\*74@9@<(n*4*<4F9<9F<,4*9<e/<3F9Fp93//<3F9F /39f4<7F>Fq44<77F>>F4>7]-77@=@q--<77F==F-7=\/79@>@B(n//<99F>B>FB,>B9/c4<8F>Fq44<88F>>F>48\477@>@@(n474<7F>>F@@,>74@e-<7F=Fp=-70<9F?F 9?0e279@=@B(r292<=9FBB,=FB29=c2:9C=CB*l29B=2<9F=FB,29B=b2>6H9H=-B-p262>96H9H=B=-B-B2=96^2>6H9H=-B-m=296B2>6H9H=-B-9=62B`2>6H9H=-B-?269=B B@=I9I6@69B=2+6292=B6B=92?2P6H9H=-B- 629=Bm@/MTrk 1JAZ2 @General MIDI 2StringsKp{]@[( Strings (BB)2'9 B2=6g29B=>2('764 i>(74@2*'966 i6@9*('4 >276g4(7>2'=6B29 g2=9B('4 76>2g>(47*'@26 96g6@9*('>2764 g7>4(B22'=69 Y=92B<2-'764 i<-74B22'<69 i<9B2+'@27 ;6g;@+7-'=24 76g-7=4@2*'966 i9@*6?2/'6 96i/9?6>2('764 i7(>4-'=2764 g4-=7>2/'966 i>69/>2('864 i(84>('>24 76g(>74-'76=24 g7-4=B22';69 Y9;2B2'B2=69 W9=B2-'<24 76g<-74B22'<69 iB29<>2('4 76i4>(7-'=24 76g4-7=-'<24 76g4-7<2'<6B29 g<92B>2('764 i>7(4=2-'764 i-74=/'6 96>2g/6>9>2('4 86i>4(8('>24 76g7(4>=2-'764 i4=-7-';69 B2W9B;-C6<2-'49V<-4CH6B22'99V92BHG6B2+'79VG7B+>2('C649Y(4>CC6>2('49VC4(>=2-'C649YC4=-I6B22'99VIB29>2('49C6iC4>(C6=2-'49fC4=-2';69 B2g9B2;/'6 >296g6/>9<2-'764 i<7-42'B2<69 g2<B9@2+';67 i7;@+-'=2764 g-47=*'@2966 g9@6*?2/'966 i9?/6>2('764 i>7(4=2-'764 i=-74/'6 96>2g69/>('864 >2g8(>4('764 >2g47>(-'764 =2g4-=7B2-'9 ;6Y9B;-2'9 =6B2W2=9B-'4 76<2g47-<2'B2<69 gB29<+'7 ;6@2g;7@+-'=2764 g7-4=*'6 96@2g6*@9?2/'6 96i?/69>2('4 76i74>(-'764 =2g7-=4>2/'6 96i/69>>2('864 i8>(4>2('764 i7>4(-'4 =276g-=472';69 B2W;92B2'=6B29 W=29B<2-'764 i-74<B22'<69 i<B29>2('4 76i4>7(=2-'4 76i=-47-'76<24 g7<-4B22'9 <6i9B<2('764 >2g47>(-'76=24 g=-47/'>26 96g>/96>2('4 86i>(48('76>24 g7>(4-'=24 76g=-74-';6B29 WB;9-C6<2-'49V4C<-H6B22'99V9H2BB2+'G679YB+G7>2('49C6YC4>(>2('49C6Y4>C(=2-'C649Y-C4=I6B22'99V9IB2>2('C649iC>4(=2-'C649i=-C42'B2;69 gB;29/'96>26 g9/6><2-'4 76i-47<2'9 <6B2g<2B9@2+';67 i;@7+=2-'4 76i=-47*'6 96@2g69*@/'?2966 g?/69>2('764 i(74>=2-'4 76i4=-7/'6 >296g6/9>>2('4 86i>(48('4 76>2g47(>=2-'764 i7=4-B2-'9 ;6Y-9B;B22'99I6I29B@/MTrkLJAZ2\ ?Piano 2 @General MIDIp[(]Voice 1 (Melody) Up{QjQNgQHOQNHOQgNXQ OeN"OQ_1QQ_zQ Sy%UN SUVL#VV_PUgVFS_UNQQS7Q_G!_dZPbndTb]a?] [a[Y_!YZKRZ]gKZ? ]]FZ(]a]MadX d _t)_]F&[]]&ZN [EX_ZLZeX [aZ%]][o]mQjQNgQHOQNHOQgNXQ OeN"OQ_1QQ_zQ Sy%UN SUVL#VV_PUgVFS_UNQQS7Q_G!_dZPbndTb]a?] [a[Y_!YZKRZ]gKZ? ]Z]F(]a]MadX d _t)_]F&[]]&ZN [EX_ZLZeX [aZ%]][o];VjIV2Ql&QVVEVQA)V_Q'VWeWSg>SRaR_S_ZE$Z]e'][ay[XNNXVS]V-V]FVUeSSGU#SQL%QSVSOQ!ONV(NQ_QN_+NQ]&QNf6N__'aQ_]NaLZL]GZs_w+[A_E[XJ/VN XSRaR_S_ZE$Z]e'][ay[XNNXVS]V-V]FVUeSSGU#SQL%QSVSOQ!ONV(NQ_QN_+NQ]&QNf6N__'aQ_]NaLZL]GZs_w+[A_E[XJ/VN X$V.SZySPSL]S-SVFSXO@&O'OOOGJ!G-IXIKZJ ZLVEJVs[p+S:[ES5QGIQO?oO3NcQJ`NMJKN`$NMHX1HHXzH Jr.JMQXTQHJ9HW@W[SP_g[ _dUZ?U]VDRVZ`UZZ?(ZXVMX\Q \ [m)['VVVQGOUXQTUGZVuZmNcQE`NMEKN`$NMHX1HHXzH Or.OMQXPO`QJONHJ9H)[SP_g[ _dUZ?U]VDRVMV8'VZ?(ZXVMX\Q \ [m)[QQGOUXQTUGZVuZmNcWNN`$NMHX1HHXzH Or.OMQXPO`QJONHJ9H)[SP_g[ _0XZX3VDRVZ`KV8 ZV*XVMX\Q \ [m)[QQGRQZVuZ;OcIO!N:)NXN'NN^NvVLVS>$S.SZySPSL]S-SVFS%OOO\IXI1IV&ItZJ ZLVEJVS:HS5QGIQO?oO3NcWNN`JQN$J)HX1HHXzH Or.OMQXPO`QJONLJaL[SP_g[ _0XZX3VDRVZ`UZZ?(ZXVMX\Q \ [m)[QQGOUXQTUGZVuZ/MTrkJAZ2_Piano Harmony double @ @General MIDIp @]{Melody Doubled #1 [(EcQB`EMBKE`BQE$B)EX1EEXzE Gr.G#JE#JJXPI`JJINEJ9E)XSPVgXTVQZ?Q OZO3NDRNtQ?(QUVMUXQ X Sm)SQNGOLXNTLGQVuQmEcQB`EMBKE`$EMEX1EEXzE Gr.G#JE#JJXPI`JJINEJ9E)XSPVgXTVQZ?Q]NDRNQ`UQQ?(QUVMUXQ X Sm)SQNGOLXNTLGQVuQ;JcIJ2Ee&EIE:)JXE'JK^KvSLSN>$N.OZyOPJL]J-JVFJXG@&G'GOGCJ!C-EXEKUJ ULNEJNsSp+O:SEO5JGIJI?oI3EcQB`EMBKE`$EMEX1EEXzE Gr.GMJXTJEJ9ES@!SXSPVgXTVQZ?Q]NDRNQ`UQQ?(QUVMUXQ X Sm)S'OV*NG OELXNTLGQVuQmEcQB`EMBKE`$EMEX1EEXzE Gr.GMJXPI`JJINEJ9E)XSPVgXTVQZ?Q]NDRNMN8'NQ?(QUVMUXQ X Sm)SQNGOLXNTLGQVuQmEcWEE`$EMEX1EEXzE Gr.GMJXPI`JJINEJ9E)XSPVgXTViOZO3NDRNQ`KN8 QN*UVMUXQ X Sm)SQNGRNQVuQ;JcIJ!E:)JXE'JK^KvSLSN>$N.OZyOPJL]J-JVFJ%GOG\EXE1EV&EtUJ ULNEJNO:HO5JGIJI?oI3EcWEE`BQE$B)EX1EEXzE Gr.GMJXPI`JJINEJ9E)XSPVgXTViOZO3NDRNQ`UQQ?(QUVMUXQ X Sm)SQNGOLXNTLGQVuQ/MTrk\0JAZ2E @General MIDI ?Variable Journeys/simutrans-124.5/simutrans/music/23-Something-for-Silver-Sand.mid000066400000000000000000000610221520675460500245150ustar00rootroot00000000000000MThdxMTrkxCopyright (C) 2007 shunter~ JAZ2~j@ @ @   @ @ @@fj @   @ ~j 8 A1 FFA1 1  .@1.@. @1 ~j  @1@1. @@1~j. @  .@ 1 1 .@1.@1~j.@1   @1 @1.@1@1.~j.@   @    jgljXQ YSomething for Silver Sand/MTrk JAZ2P][({ ,Bass!P@B!)Q *R)p#I*P*D# J*p!Jp$R!p#U$H7#U"Jp!Ok!(SLU"= (Rp#U GR# !Op(E!lN(xPd!K m#L!d!K#p(B !g-M (d(P-N( #J<#4#I %H#(&O%"(E&(5(E"/D($/ /N"$M/p#Y$HU";#Jp#U U"/ # #R #Rp!St!(SDU"7( -X|-(Zt(%Kx%!G_!3U"!Vp(M !g-X (d([-N( #Ok#*SL=U" *(Up(#U:#$P #V$ %P#"&Z%%&(PB("Q "#V %P#"&Z%%(U&H(7 [kRk\k[s&YY!(&!-Hi-*Px*&R3&&!<!Ex#Dy# ?yQ vWx Qj!S m!%Tx*K %m%D*r!Q %oT!j 2KX!>2!-Kx*O-H*2 !@~!&SR&!;!(Qt(*RX*!G!-LP-!; !#Lx%L#q%&Bx*E&U#B*##1Sx%M1e)Q %o,O)p,!Dx"J !k"#=x$O#^$%/%1Jt1%Ed%2KX!>2!-Kx*O-H*2 !@~&S!R!;&!(Qt(*RX!G*!-LP-!; !#Gx%I#d&D %m*E&d%G*x'I%d)D 'm,E)d,-KX->(Kx%O(H%%2% @~!SR;!#Qt#%RXG%(LP(; #Vx/E #o*Q/j&W*u&(Ex/C(b4I /o/T4O"</"!L-!#!T (D!(((A( (Pq(#Hf!O#p(E!l((Nk(#RP<#!Vp(M !g-X (d([-N( #J<#4#I %H#(&O%"&(Pq(#Hf##V %P#"&Z%%&Jp#F %E# %&S(&Rp!Vp(M !g-X (d([-N( !Kp(B !g-M (d(P-N( #Ok#*SL=U" *IpNx I k#H t#J#p#Jp#UH7#U"JpxQkYkMkUs&Ox-M&b2S -o-^2W- )F)(NX#9(# *Lw*,SN, #=#/UP/#6 #Mx Gj!I m!%Jx*A %m%:*r!G %oJ!j &Nx'T &k(G'x)Y(^)*Fx+V*w,P+u-R,k-#JO# RU" %Jw%&6x*C&T*#H 1S#x%M1e)Q %o,O)p,!Dx"J !k#="x$O#^$%v! Dx@ P)O (N)X#9(# *Lw*,SN, #=#/UP/#6 #!P+!-!R -J!i(D-vH(g!J G!~!],!/MTrk7JAZ2*P#<][({Drums cZ#*_*Z%F%*>*(**P#<*#_%U*Z*%>*2*#<*F*#_%F*Z*%?*(**P#<*#_%P*Z%*?#2*2*#*F#<*#_%P*Z*%=*(* *F#<*#_*Z%N%*=*2* *P*_%F*Z*%>*2**P*>&&&7*Z%U%*&>*2**F#<#*_%F*P%*?*(*#<*P*#_%P*P%*?*2**F#<*#_%F*Z*%?*(**P#<*#_%P*Z%*?*2**F#<*#_*Z%F%*?*(**P#<*#_%P*Z%*?*2**F&2&*;#F# *P*;&2& *F#<#*;&& *d&2*&&&&(& *F#<#*_%P*Z%*=*(* *F#<*#_*Z%N*%=*2* *F#<*#_%F*P*%?*(*#<*P*#_%P*P*%?*2**F#<*#_%P*Z%*=*(* #<*F*#_%N*Z*%=*2* *F#<*#_*Z%F*%?*(**P#<*#_%P*Z%*?*2**F#<*#?&F&%F*Z%*?*(**P#<#*_%P*Z*%?*2**F#<#*_%F*Z*%?*(**P#<#*_*Z%P%*?*2**F#<#*_%F*P*%?*(**P#<*#_*P%P*%?*2**P*_%F*Z*%>*2**P*>&&%U&7*Z*%&>*2*#<&2*F#&*_*P%P%*=*2* *P#<#*_%P*Z*%=*2* *P#<*#_*Z%F*%>*(**P#<*#_%U*Z*%>*2*#<*F*#_%F*Z*%?*(**P#<*#_%P*Z%*?#2*2*#*F#2#*_%<*P*%?*(**F#<#*?&<&*P%P#P%*#?*(**P#<*#_%F*Z%*>*(**P#<*#_%U*Z*%>*2#2*#*P#<*#_*Z%F%*>*(**P#<*#_%U*Z*%>*2*#<*F*#_%F*P*%?*(**P#<#*_%P*P*%?*2**P*_%F*Z*%>*2**P*>&&%U*Z&7%&*>*2*&2&_*P%F3Z%3*>3F33F&(&3_%F3Z*d%*3>3F33P3_*F%F3Z%3*>3F33Z3_*d3Z*3>3F33F3_%<3Z*Z*3%?&(3F&33P3_%F3Z*d*3%?3F33P3?&&%F3Z*P%3*?3F&(&33P3_%F3Z*d%*3?3F33F3?&<&*Z%<3Z*%3?3F&(3&3P3_%F3Z*d%*3?3F33P3_3Z*F%F3%*>3F33Z3_3Z*d3*>3F33P3_*P%F3Z%*3=&(3<3& 3P3_*d%P3d*%3=3F&<3& 3F#F3#_%<3Z*Z%*3?3F&23&3P3?3(&<3&%d*d3Z3*%?3F&F3&3F3_3Z*Z%<3%*?&(3F&33P3_3Z*d%F*%3?3F33P3_3Z%F*F3%*>3F33Z3_3Z*d*3>3F33P3_3Z%F*P%3*=3<&(&3 3P3_*d%P3d*3%=3F&<3& #F3F3#?&<&*Z%<3Z%3*?3F&23&3P3?.(&<&.*d%d3Z3%*?3F#<&F&3##<*P#*_%F*Z%*>*(**P#<*#_*Z%U*%>*2**F#<#*_%F*P*%?*(**P#<#*_*P%P*%?*2**P#<#*_%F*Z%*>*(*#<*P#*_%U*Z%*>*2**P*_%F*Z%*>*2**P*>&&&7*Z%U%*&>*2**P#<#*_%F*Z%*>*(**P#<*#_%U*Z%*>*2**P#<*#_*F%F%*=*(* *P#<#*_%U*F%*=*2* #<*F#*_%F*P%*?*(**P#<*#_%P*P*%?*2**F*_%F*P%*=*2* *F#<*#_%U*P%*=.<. *F#<*#_%F*P*%?*(**P#<#*_*P%P%*?*2**F#<#*_%F*P%*?*(*#<*P#*_%P*P%*?*2**F#<*#_%F*Z%*?*(*#<*P*#_%P*Z%*?*2**P#<#*=&(& *Z*=&(& *F#<*#=&2& *Z#F*#&&&2& &2#<1(*F#1&*_%P*P*%=*2* *P#<#*_*Z%P%*=*2#2#* *P#<#*_%F*Z%*>*(*#<*P*#_%U*Z%*>*2**F#<#*_%F*P*%?*(**P#<#*_%P*P%*?*2**F#<#*_*Z%F*%?*(**P#<#*_%P*Z%*?*2**F#<#*_%F*Z%*?*(**P#<#*_%P*Z%*?*2**P#<#*_%F*Z%*>*(**P#<#*_*Z%U%*>*2#2*#*P#<*#_%F*Z%*>*(*#<*P#*_%U*Z*%>*2**F&2*&;#F# *P*;&2& #<*F#*;&& *d&2*&&&&(& 1(&21&_3Z*P%F3%*>3F33F&(3&_%F3Z*d%3*>3F33P3_*P%F3Z3%*?3F33P&(3&?&(&*d%F3Z*%3?3F33F3_*d%F3Z*%3>&(32&33P3_*d%F3Z*%3>32&(3&3P3_%F3Z*P3*%?3F&(3&3P3?&&3Z*d%F3*%?3F33F3_3Z*d%F*%3>32&(&33P3_%F3Z*d3*%>32&(&33P3_%F3Z*F*%3>3F33Z3_3Z*d3*>3F33F3_*d%F3Z*%3>&(323&3P3_%F3Z*d%3*>32&(&3#Z#=&F& 3Z*P*3=3P&<&3 3P&P3&=#P&2#& 3Z*d3*&(&3P&<3& 3P3_3Z*P%F%*3?3F33P&(3&_3Z*d%F3*%?3F33P3_3Z*F%F*3%>3F33Z3_3Z*d3*>3F33F3?&<&%<3Z*Z%3*?3F&(3&3P3_3Z%F*d*%3?3F33F#F3#_*Z%<3Z%3*?&23F&33P3?&<&%d3Z*d*3%?&F3F#<&3##<*P*#_%F*Z%*=*(* *P#<#*_%U*Z*%=#2*2*# *P#<*#_%F*F%*=*(* *P#<#*_*F%U%*=*2* *F#<*#?&F&%F*Z*%?*(**P#<#*_%P*Z%*?*2**P#<#*_*Z*<&(*<*& *F#<*#<&2& *P*<*<&<&* #<*P*#_%F*Z*%>*(**P#<#*_%U*Z%*>*2*#<*F#*_%F*P%*?*(**P#<*#_*P%P%*?*2**F#<#*_%F*P*%?*(**P#<*#_*P%P*%?*2**P#<#*_%F*Z*%>*(*#<*P*#>%Z%*Z*>*2**F#<#*_%F*P%*?*(**P#<#*_%P*P%*?*2**F#<*#_*P%F*%?*(**P#<*#_%P*P%*?*2*.6#<#._*F%P%*=*(* *P#<*#_*P#<#*=*2* &2*F&*;#F# *P*;&2& *F#<*#;&& *d&2*&&&&(& *F#<*#_%F*Z%*?*(*#<*P#*_%P*Z%*?*2**F#<#*_%F*Z%*?*(*#<*P*#_%P*Z%*?*2**F#<#*_%P*F*%=*(* *P#<#*_*P#<*#=*2* *F#<*#_*Z%F*%?*(**P#<*#_%P*Z%*?*2*#<*F*#_%F*P*%?*(**P#<#*_%P*P%*?*2*#<.6.#_%P*F%*=*(* *P#<#*_*P#<*#=*2* *F#<*#_%F*Z%*?*(**P#<*#_*Z%P*%?*2**P#<*#=&(& *Z*=&(& #<*F#*=&2& *Z#F*#&&&2& 1(&2&1>&F&3Z*P%F%*3>3F3&(3F&3_*d%F3Z3*%>3F33P3_%F*F3Z%3*>3F33Z3_3Z*d3*>3F33F3_3Z*d%F3*%>32&(3&3P3_%F3Z*d%3*>32&(3&3P3_*P%F3Z%*3=3F#F3# 3P3=&<& *d%P3Z*%3=3F&2&3 3F3_%F3Z*d3*%>32&(&33P3_*d%F3Z%3*>&(323&3P3_%F3Z*F3%*>3F33Z3_*d3Z*3>3F33F3_3Z*Z%<3*%?3F&(3&3P3_3Z*d%F*%3?3F33F#F3#_3Z%<*Z3%*?3F&2&33P3?&<&%d3Z*d*3%?3F#<&F3#&3F3@&(&*Z%<3Z*%3@&(3<3&3P3_3Z*d%F%3*@3F33P3_*F3Z%F*%3>3F33Z3_*d3Z*3>3F33F3_%F3Z*d%3*>&(323&3P3_3Z*d%F*3%>&(323&#F3F#3_%<3Z*Z*%3?3F&23&3P3?.(&<.&3Z%d*d%*3?3F&F&3#<1(#1=&2& *P*=&(& *F*=&<& .P.&<&&2& &F#F#&7&&&<&7&F#2#&7&&&&#P&<&#/MTrkJAZ2Piano=<G<@][7{ ^Z8<@<9><IBp=<G<@<8B8<>Ip4<=:<@G8E<>

D<:=<?<E9<4p?=8<><DB<9=38G@= ><G<B<9<G>9B;<8<=<@<`@8=;x=,>,B,9,B>B9=>.9.B.=.$B==4B4>4>9497>=B9B=>=9===$BH=>HB=H>9H9;B>9==98E8=8@8DE=@9 44 =?9?@?.9@='=A9A@A6@9=B4$$@<=<49<*@9=*=I9I@I-=@9K4/4@*9*=*.9=@'464=?9?@?9@=9?><B<;<G<`;G>787>B7BI78>IB->9;9D9@9u;D>@#><B<;><=<8<;B

=B<G<;<8><BpB;G<><8<GA<>F8GA> @898=8@9=?=@9@@@E@=9@E@<9<=<E<@=E><B<G<@9;<`B>D5>5;5G@5;FD@;>8C>C@C;C @;8> B<;<G<>><B<I<B8><G<;<B8B

G;><A<H<B8 @8=898 =9@?9@=@@@E@@E=9E=6=>=D>E6 9!9 9D>DBD.9B>'>FBF9F69>BB><B<8<I<I>8B6<=<9<B<@9=B6<9<6><@9>;<B<6><3>B; K=;=F=A=eF;AK :!: ADDD;D=D.;DA=';F=FAFDF6=A;DB:)#=7E7:L7DEL= 44 @>9>=>.=@9'=@9@@@69=@B4#$=;9;4@;*@=9*=H9H@H-9@=K4.49)@)=).=@9'454=>9>@>99@=?G<B<><`>BG<><D<GC<3DC>G L<=<G<D<LDG=E<I<N<><`EN>=<D<G<IL<`GDLI7B7>7=E7DB>IE 44 E>B>>>;>.>EB;'E@B@;@>@6;BE>B4##@8=84E8D=@E 44 =?9?@?.=9@'=A9A@A6@9=B4$$9<@<4=<*@9=*9I=I@I-9@=K4/4=*@*9*.@=9'464=?9?@?9=9@><;<B<B>;=<B<><8<`=8>><B<;<BG=<8<><GB B<G<;<><>;GB><A<H<8HA @898=8=9@?=@9@@@E@9E=@98@8=8D=@9 44 @?9?=?.@9='9A=A@A6@=9B4$$9<=<4@<*9=@*=I9I@I-9=@K4/4=*@*9*.9@='4649?@?=?9=@9?G<><B<;<`G;B>5D5@5>;5F;>@D8CIC>CBCl8B>I B<;<G<>G;B 8<B<I<><8>IB><B<G<;

;G<<A<><B8

A898@8=8<E8D@E9= 44 =?@?9?.9=@'@A=A9A69=@B4$$9<@<4=<*9@=*9I@I=I-=@9K4/4=*9*@*.=@9'4649?@?=?99@=E<><6E B<><I<8 =7B797e9B= 99 9>B>=>.B=9'=@9@B@6B9=B9#$=;9;9B;*=9B*=HBH9H-9B=K9.99)B)=).B9='959=>9>B>9=9BB<E<>N<><G<`G>A<K<F<N;<3AF;K @<=<E<`;G>B<8<;<B><`8>B=<@<G<;8<3=G@8 9<I<><BI @<8<=<G<=8@GI7>7B797I9B>-E9B9>9;9u>BE;#98E8=8@8D9@=E 44 9?=?@?.=@9'@A=A9A69=@B4$$=<@<49<*9=@*9I=I@I-@=9K4/4@*=*9*.@9='464@?9?=?9=9@?B<;<><G<`BG>87I7>7;B7IB8>-@9;9>9D9uD@>;#B<;<>

;I<8<B<B>IB ><G<B<;<>;BGB<><8<G

GB=<9<8@<@@=E8@8=8998D=@9E 44 @?=?9?.@=9'9A@A=A69=@B4$$9<=<4@<*9=@*=I@I9I-=9@K4/4=*9*@*.@9='464=?@?9?9=9@?><G<;<B<3>B;G%I,>,B,8,BB>I8I.B.8.>.$I>884I4B4B>47>8BII=B=>=8=$BI8>HIHBH>8H;I8>B;<><G<B;B 8<><=<B<B8=>;<><B;B ><8<A<<<><A8@<9<=<E<@=9E ><9<6<69>B<8<><=<6>8=B B898=8=9B?9@=@6@B@l=B96 ><6<E6EB<;<><3>;B A<;<F<KB;G ;7>7C787<;8C>/@9>9;9D9u@D;>#=@8L4.99xLQOL G9`GLK(LG3G948=C98LU458BGB=EL8G94 =%938E=6LIGA4+SK4G98S=L@/MTrk JAZ2{][F{ '][F{ 'P]P{ 'P][F{ 'P][F{ 'P[F[F{ '][F 'P@]PGuitar=%n=-68?=?=-8X*4:s*4*24::::4:*]/#6)9)>l//469>6<9<>%#/69>K,#4);)m,,4;44<;<$4;,J- 8%=%n8-=-68?=?8-=Z4 :%>%p4:46:?>>?>4:\/ 9%=%p//69=9?=?9=/Z48>s48428:>>:84>\- 8%=%n=-8-68?=?8-=X-8"="k=-8-58====8-V-4s--644? 4-R-4"p--544= 4-Q-8=s-8-28:==:-8=\-8=s-8-189==9=-8U4;">"p446;;?>>?;>4W4;">"r445;;=>>=4;>Y-49s--244:99:-94\-49s-4-14999949-Y-49s-4-34;99;4-9\-&4,9,r--14499999-4Z/6v//266:6/\/6v//1669/6]48>p4846>8?>?8>4R48">"m48458=>>=4>8S2 ;%s226;;?;2\4 8%>%p4468>8?>?84>Z/ 6%q/6/66?/6X/8>s/8/28:>>:8/>]-#9)=)m--4=99<=<$-=9M- 9%=%e-=9-59===-=9R-9"="e=9- -59===!-9=L-#9)=)k9-=-49<=<=-9U-49p-4-694?9?-94R-4"9"m-4-594=9=-49U-49m-4-54=99=#4-9Q- 4%9%m-4-494<9<94-U/6s//666? 6/R/6"p//566= /6U48>p44688?>>?8>4R48">"m44588=>>=>48Q/ 6%q6//66?/6X4 8%>%n48>468?>?8>4X/6v//266:6/^2 8%;%p28268?;;?2;8\4 9%=%p4469=9?=?94=\4#9)=)p49459=====94\4 9%=%p49459====94=X4&9,=,p4449=9<=<=94Z2&6,9,n2962<6F9F962X2#6)9)k6292;6D9D692R/&8,>,n8/>/<8F>F>8/X/#8)>)k/8>/;8D>D8/>R*"1"6(9(=r*1*:61:6C9=9C=**16=9\*"1"6(9(=r*16*9196B9=9B=)=*691Y*"1"6(9(=r*1*;61;6D=99D=*=*169\*.1.6595=!q*16*9196B99B==)961*=^2"9(>(p22>99H>>H9>2R2%9+>+m292<>9F>F2>9U2 9%>%m292<>9F>F#9>2Q2(9.>.m292<>9E>E9>2S/$6*s//;66D6/\/(6.s//966B/6^,!5';'p,,=55G;;G;,5R/$8*=*m//<88E==E=8/Q-"9(=(s-9-:9C==C9-=\-"9(=(s--999B==B9-=Y-"9(=(s-9-;9D==D9=-\-.95=5r-9-9=9B=B-9=V/# ;)s//<;:F/:W/# 9)u//;98D8/[/$8*>*p//;89D>>D>9/\/(8.>.p/8/9>8B>B/8>^-8=p-8-6=8?=?8-=R-8"="m-8-58====-=8R-#4)p--444<'-4M- 4%h4--54= 4-S- 8%=%p--688?==?-8=\-#8)=)p--588=====-8Z/9>s/9/29:>>:9/>\/9>s/9/199>>99>/Y- 4%9%n9-4-64?9?-94X-4"9"k-94-54=9=4-9R-4"9"k-49-54=9=!-94N-4"9"m--4494<9<49-R/ 6%q/6/66?/6X/6"n/6/56= 6/S/#8)>)m//4>88<><$>8/M/ 8%>%e/>8/58=>=8/>S2 ;%s226;;?2;[/#8)>)m//4>88<><$/>8J2 ;%q;226;?2;Z2 8%;%p22688?;;?82;Z-9=s-9-29:==:-9=\-9=s--1999==99-=Y-9=s-9-39;==;-9=\-&9,=,r-9-1=99=9-=9V49"="p44699?==?49=W49"="r44599====4=9U4 9%=%r44599====94=Q4 9%=%p44499<==<4=9[/6v//266:/6\/6v//16696/Y/ 8%>%n/8>/68?>?/8>X/8">"k>/8/58=>=8>/R/ 6%q6//66?/6\48>p48468?>>?8>4N/6v//266:/6^4 8%>%p48468?>>?>84^49=p44699?==?49=R49"="m4945=9===4=9U49=m49459====#=49Q4 9%=%m49449<==<=49R2(6/9/m229966B9B$269M2%6+9+e6292:6C9C962R2(8/;/m2298;8B;B$;28M2%8+;+e28;2:8C;C2;8R1(9/=/m1199=9B=B$9=1M1%9+=+e=191:9C=C1=9R1"9(=(e9=1 1:9C=C!=91L1(9/=/k19=199B=B19=S2$9*>*p22;9>9D>D2>9\2(9.>.p29299B>>B2>9\2$9*>*p2929>9B>B29>X2*91>1p29289A>>A92>[/(6/p//966B'/6M/%6+h/6/:6C 6/R1(5/;/m1195;5B;B$51;M1%5+;+e;511:5C;C5;1S-$9*=*p--;9=9D=D9=-\-(9.=.p-9-9=9B=B=9-\-$9*=*p-9-99B==B=9-X-*91=1p-9-8=9A=A-=9[/(6/p//966B'6/M/%6+h/6/:6C 6/U4!8'>'p484=8G>>G84>R4$8*>*m44<8>8E>E84>U-8=p--688?==?8-=R-8"="m-8-58====8=-R/#9)>)m//49>9<><$/>9M/ 9%>%e/>9/59=>=>9/Q-8=s-8-28:==:8-=\-8=s--1889==9-=8U4;">"p446;;?>>?4>;W4;">"r445;;=>>=;>4Y- 9%=%n-=9-69?=?-9=X-9"="k9=--59===9=-R-9"="k=9--59===!-9=N-9"="m-=-499<=<=-9T/ 6%s//666?6/\/#6)s//566=/6V28";"p22688?;;?28;W28";"r22588=;;=;28]/6s//666? 6/R48>p4468>8?>?48>N/ 6%q/6/66?6/Z4 8%>%p48468?>>?>48Z- 4%9%n9-4-64?9?-94X-4"9"k4-9-54=9=-49R-4"9"k94--54=9=!49-N-4"9"m--4944<9<9-4V-49p-4-694?9?94-R-4"9"m-4-54=99=-49U-49m-4-594=9=#94-Q- 4%9%m--444<99<-49Q/6v//266:6/\/6v//16696/[/ 8%>%p//689?>>?9>/\/#8)>)p//588=>>=8/>Z/ 6%q/6/66?/6\48>p4846>8?>?84>N/ 6%q6//66?6/\/8>p/8/6>8?>?/8>J49"="p44699?==?=94W49"="r44599====94=U4 9%=%r44599====4=9Q4 9%=%p44499<==<=49\2(6/9/m229696B9B$629M2%6+9+e2962:6C9C692M/#8)>)p//<88F>>F>8/W/#8)>)r//;88D>>D/8>[*$1$6*9*=o*16*;1;6D9=9D=**=961\*(1(6.9.=o*1*9619=96B9B=)1=*69\*$1$6*9*=o*1*96196B9=9B=)1*96=X**1*6191=o*16*818=96A9A=(9=6*1Z2"9(>(s292:9C>>C92>\2"9(>(s29299B>>B92>Y2"9(>(s22;99D>>D>29\2.95>5r29299B>>B2>9\/$6*s//;66D/6\/(6.s//966B/6\,$5*;*p,5,;6D;;D;,6\,(5.;.p,,95;5B;B,5;\-$9*=*p-9-;=9D=D9-=\-(9.=.p--99=9B=B-=9\-$9*=*p-9-99B==B-=9X-*91=1p-9-8=9A=A-9=V/# 6)s//<66F/6W/# 6)u//;66D/6Y4&8,>,n>484<8F>F>84X4#8)>)k>844;8D>D84>W-$8*=*p-8-7=8@=@-8=\-8 = p-8-5=8>=>-8=a-+82=27=-8-98B=B#-8=i@/MTrk1JAZ2[UStrings{ ,]@PJ2B/]JBD2>/]D>I2B/9IBJ2B/9JBG2>/]G>G2A/]AGE2=/9E=G2>/]>GD2>/]>DJ2B/]JBD2>/]D>I2B/9IBJ2B/9JBG2>/]G>G2A/]AGE2=/9E=G2>/]G>D2>/]>DJ2B/]BJD2>/]>DI2B/9IBJ2B/9JBG2>/]>GG2A/]AGE2=/9E=G2>/]>GD2>/]D>D7=7>= Dv@/MTrk rJAZ2][Zd J{Melodyf@g"@ B^.BDn!DEc:E8DsAD9:>?X?9QG98;dO; 9 9>R?> @[+>H@,>;C; W">@V5@@BL:B9DQAD9E_3EHG\AGjErEGAGEHEBJ/BEKE BH)B@Y@B2'@PB!@=0= @Q%@=F = ;P#;=:(=;R#;9A9;A;9F(96@(6 9?96@"64H!463!64R%134)1%1K$12I 203V 3 4W046^)64J842I2 4\:4d6H#68N.8:V:;=5;=O= >N.>@I&@BP-BLBR3BFB&C#&CBGBY)BRBV6BHs$HIO I&==*=>9)> ?a?@82@D^$DG`:GD!DE[2EBV"B @C8@@LZ4LIE"I JZ3J"G1GIY2IE*EG`5GBBEY7E@#BD@0BEV(B@E>B;Ee;E;6$;6BEKBEBWKB A2ABIDB:IgTIH1!IKHA>B*B EWE GO0GJ\!JNU0NQYQU\3UXl)XUKFUS&SUaUVKV UR(USW(SQ^(QN#/NOQOPW'P,S[ SL?2L;LN;LLULZLU#NSL0NQHQTm3TEUe5UETi2TBUb:U9TeU/TIUX_GX QQ&Q7Q7QTfKTQ/Q SEDS3QUINJQ)NeSZ,SR[RQA*QNN%NJB"JG=(GEA)E!CRCD0nDGCHGDDJG;JNV+NFMX$MNU:N2Lc;L4Jb;J+I[FI-KeL`KILhQ`JQ SR%Q=S7QQU!QZQZ$QRTi"TUR'UV^,VTB,TU^/UQD+Q?LA>L?OeBOL)PSL4PQi&QfQl:QQXQ QI/QGQW1QLQUSQSH"QLS5QP_*PCLv;LL`LLc9LLfL Lb@L6LjLLM1M NU8NMd$M L0UD>Z@d@A="ADAD GUGJu?JE`3EBIW?I:LfML'NWANL0LQe4QQl4QjQk@QVS2VW\%WXI&XWL#W V7,VUR&US5?S@Q=:Q=SIISQ',UCQSNb,NNSk.SFUc6UJVd I;LQa3I1QKL4I"LQ_JQ I))L5IHLQg>Q-Le)LM`MNR(NMa%M LA5LK["KJE.JHT HIP4I!LU$ILL5IEB=>B;EWOEBHWBMHE,GJE;GCE98E3EYLE,Hb IPHID@9B@B(B DW+D%Ed'E D)NM*M NQ9NLZL I9)I EZ&EB1JBSB?(B EF"EIZ(IJH J#NX&NQ>+QUY,U VZ*VSf$SQC$QNP"NJQ&JIF$IG:=G[!> ;F);%9a#955=5"6[G68F'89d:9;j*;N=g#=P>q*>T:a+:C=a*=;[+; 6,6 9e:97<6[7/6M4O&4N6^26K4Q14K6e566D.5K6255U"5 4=344D4 5R;55O 56O266O'65J255I!54U:4/MTrk=JAZ2Something for Silver Sand/simutrans-124.5/simutrans/music/24-needlessly-striking.mid000066400000000000000000001443121520675460500236530ustar00rootroot00000000000000MThdxMTrk`Copyright (C) 2007 shunter~ JAZ2Vj@ @ @ @ @ ~j@ @ ~j@@1@ '@ ~j@ '@ @ @ ~j@@1@ '~j@ '@ @ @ @ ~j@@j#jjXQ=YNeedlessly Striking/MTrk!JAZ2Bass][{@ PQ!Vt!%Y;%%Z&%(Wx%a(P(Q%&!X(w%f!@%%Z(([%u![( EOLIEYIPOJLM EOLIE3L>O-ID EILOO$L<I?E5 EOLIIGOGLKEN EOLII9O$E+L2 EIOLE8O)IAL; EOILLMOEERINELOIO%E,L5I;EIOLO&E7L9I> OELIIMERLMOL EOILI4L4E*O EIOLO'E7I@L< EOLIIIEIOBLG EOILI@L;E6O&OELIE?I@L=O-LOEIICE?LCO< EOILI8L=O*E/ OEILLEE;IFO8 ELOIEQOBIKLK EOILICL?O+E.EIOLL=O,E5IB EOLIOFLLIIEK EOLIO(E4L9I@ OEILE;IAL>O( EOLIOJLMEYIP EOLIE3IDO-L> EIOLO$I?E5L< EOLIENOGIGLK EOILI9L2E+O$ EOILO)E8IAL; EIOLOEINERLMEOLIO%I;L5E,ELOIO&I>E7L9 OELILMIMOLER EOILE*I4L4O ELOIO'L<I@E7 EOILEIIILGOB EOLII@L;E6O&EOLIE?I@L=O-LOEIO<ICLCE? EOILI8E/L=O* OEILLEO8IFE; EOLIIKOBEQLK EOILO+ICL?E.EOILL=O,IBE5 EOLIOFLLIIEK EOLIE4O(L9I@ EOILE;O(IAL> EOLILMIPEYOJ EOLIIDE3O-L> EIOLO$I?L<E5 EOILLKENIGOG EOLII9L2O$E+ EOILO)E8IAL; EIOLERINLMOEEOLIO%I;E,L5EILOE7O&L9I> OELIEROLIMLM EOILI4E*L4O EOLIE7L<O'I@ EOLILGEIIIOB EOLII@L;E6O&EOILE?L=O-I@LOIEICE?O<LC OEILI8L=E/O* OEILO8LEE;IF EOLIEQOBLKIK EIOLL?O+ICE.EOILL=IBO,E5 EOLIIILLOFEK EOLIO(L9E4I@ EOILE;O(IAL> EOLIOJLMEYIP EOLIE3L>IDO- EILOO$E5I?L< EOLIENIGOGLK EOILI9E+O$L2 EOILE8O)L;IA EOILINEROELMEOLIO%L5E,I;EILOO&L9I>E7 OELIIMERLMOL EOILE*L4I4O ELIOI@E7O'L< EOLIIIOBEILG EOILE6I@L;O&OEILE?L=O-I@LOEIO<ICLCE? OEILI8O*L=E/ OEILLEE;O8IF EOLIEQLKOBIK EOILO+L?ICE.EIOLL=IBE5O, EOLIOFLLIIEK EOLII@L9O(E4 OEILE;L>IAO( EOLILMOJEYIP EOLIE3O-IDL> EILOO$L<E5I? EOLIIGLKOGEN EOILI9E+L2O$ EIOLO)E8L;IA EIOLOEINLMERELOIO%I;E,L5EIOLE7O&I>L9 OELIEROLLMIM EOILI4E*L4O EIOLL<O'I@E7 EOLIIIOBEILG EOILL;E6I@O&EOILE?O-L=I@LOIEO<LCE?IC OEILI8E/O*L= OEILE;IFLEO8 EOLIIKLKOBEQ EOILICO+L?E.EOILL=IBE5O, EOLILLIIOFEK EOLIO(E4L9I@ OEILE;L>O(IA EOLILMIPEYOJ EOLIL>E3O-ID EILOO$E5L<I? EOLIOGENLKIG EOLII9L2E+O$ EOILO)E8IAL; EOILLMOEINEREOLIO%L5I;E,ELOIE7L9I>O& OELIIMLMEROL EOILI4E*L4O EOILI@E7L<O' EOILOBIIEILG EOILE6I@L;O&OELIE?L=O-I@LOIEICLCO<E? EOILI8E/O*L= OEILLEE;O8IF ELOIEQOBIKLK EIOLICL?O+E.EIOLL=E5IBO, EOLILLIIOFEK EOLIO(E4L9I@ EOILE;O(L>IA EOLIOJIPEYLM EOLIO-E3IDL> EILOO$I?L<E5 EOILLKENIGOG EOLII9L2O$E+ EIOLE8O)IAL; EOILEROEINLMEOLIO%L5E,I;EOLIO&E7L9I> OELIERLMOLIM EOILE*I4L4O ELIOI@O'E7L< EOILEILGIIOB EOLIE6I@L;O&OELIE?L=O-I@LOEIICE?O<LC EOILI8O*E/L= OEILLEE;O8IF EOLIIKLKEQOB EOILL?O+ICE.EIOLL=IBE5O, EOLIOFLLIIEK EOLII@L9E4O( OEILE;L>O(IA EOLIOJIPLMEY EOLIO-IDE3L> EILOO$I?E5L< EOLIENOGIGLK EOLII9O$E+L2 EOILO)E8IAL; EIOLERLMINOEELOIO%I;E,L5EIOLL9O&E7I> OELIIMERLMOL EOILL4E*I4O EOILL<I@O'E7 EOILEIOBLGII EOLIE6L;I@O&OEILE?L=O-I@LOEIQCT<NCJ? JTNQN8T*Q=J/ TJNQT8QEJ;NF JQTNNKQKJQTB JTNQNCQ?T+J.JNTQQ=T,J5NB JTQNNITFQLJK JTQNJ4T(Q9N@ TJNQJ;T(Q>NA JTQNTJJYQMNP JTQNT-J3Q>ND JNTQT$J5N?Q< JTQNQKJNTGNG JTQNN9Q2J+T$ JNTQJ8T)NAQ; JNTQTEQMJRNNJQTNT%N;Q5J,JNTQJ7T&Q9N> TJQNQMJRTLNM JTNQN4J*Q4T JQTNN@Q<J7T' JTNQJITBQGNI JTQNJ6Q;N@T&JTNQJ?N@Q=T-QTJNNCJ?QCT< TJNQN8J/T*Q= TJNQNFQEJ;T8 JQTNJQNKTBQK JTNQT+NCQ?J.JNTQQ=T,J5NB JTQNTFNIQLJK JTQNJ4N@Q9T( TJNQJ;Q>T(NA JTQNNPJYQMTJ JTQNQ>J3T-ND JNQTT$Q<N?J5 JTNQJNNGQKTG JTQNN9T$Q2J+ JNTQT)J8NAQ; JTNQTEQMJRNNJTQNT%J,Q5N;JNTQT&Q9N>J7 TJQNJRTLQMNM JTNQQ4J*N4T JNTQQ<N@T'J7 JTNQQGNITBJI JTQNQ;J6N@T&JTQNJ?T-N@Q=QTJNQCNCJ?T< JTNQN8J/Q=T* TJNQNFJ;QET8 JQTNNKJQQKTB JNTQQ?T+NCJ.JTNQQ=T,J5NB JTQNTFQLNIJK JTQNT(N@J4Q9 TJNQJ;NAT(Q> JTQNTJJYNPQM JTQNNDJ3Q>T- JNTQT$Q<J5N? JTQNTGQKNGJN JTQNN9T$Q2J+ JTNQT)J8Q;NA JTNQNNJRQMTEJQTNT%Q5N;J,JNTQQ9J7T&N> TJQNJRNMQMTL JTNQJ*N4Q4T JTQNJ7Q<N@T' JTNQTBQGNIJI JTNQQ;J6N@T&JTNQJ?T-Q=N@QTJNT;QBJ>NB TJNQN7T)Q<J. TJNQT7QDJ:NE JTQNJPTAQJNJ JNTQQ>T*NBJ-JTNQQ<NAJ4T+ JTQNQKTENHJJ JTQNJ3N?T'Q8 TJNQJ:Q=N@T' JTQNTIQLJXNO JTQNNCJ2T,Q= JNQTT#Q;N>J4 JTQNP;JBLBG> GPJLJ7P)G.L< PGJLP7LDJEG: GLPJGPJJLJPA GJPLJBL>P*G-GPJLL<G4JAP+ GPLJLKJHPEGJ GPLJL8J?G3P' PGJLG:L=J@P' GPLJLLJOPIGX GPLJG2JCL=P, GJPLP#L;J>G4 GPJLIBO;E>LB OEILI7E.L<O) OEILE:IEO7LD ELOIOAIJLJEP EIOLL>O*IBE-EOILL<E4IAO+ EOLILKIHOEEJ EOLIL8O'I?E3 EOILE:L=I@O' EOLIEXOILLIO EOLIE2L=O,IC EIOLO#E4L;I> EOILO<ICE?LC OEILI8O*E/L= OEILE;LEIFO8 EOLIEQLKOBIK EOILICL?O+E.EOILL=IBE5O, EOLIV<O?SC VOSS8O/V* VOSSEV8O; OSVSKVBOQ OVSV+SCO.OSVS=O5V, OVSO;IBLBE> EOILI7L<E.O) OEILE:O7LDIE EOLIIJOALJEP EIOLIBL>O*E-EOILL<E4IAO+ EOLILKIHOEEJ EOLIO'I?L8E3 OEILE:O'I@L= EOLIEXLLIOOI EOLIE2L=O,IC EIOLO#L;E4I> EOLILCICO<E? OEILI8O*E/L= OEILO8E;IFLE ELOIOBLKEQIK EOILICL?O+E.EOILL=O,E5IB EOLIO?SCV< OVSS8O/V* VOSO;SEV8 OSVOQVBSK OSVSCV+O.OVSS=O5V, OVSE>IBLBO; OEILI7E.L<O) OEILE:LDIEO7 ELOIIJLJOAEP EOILIBO*L>E-EIOLL<O+E4IA EOLIOEIHLKEJ EOLIL8I?E3O' OEILE:O'L=I@ EOLIIOEXOILL EOLIE2ICO,L= EIOLO#L;I>E4 EOLIO<ICLCE? OEILI8L=E/O* OEILE;O8LEIF EOLIOBEQIKLK EOILO+ICL?E.EOILL=O,E5IB EOLIO?SCV< OVSS8O/V* VOSO;V8SE OVSOQSKVB OVSV+SCO.OSVS=V,O5 OVSE?O<ICLC OEILI8L=E/O* OEILO8LEE;IF EOLIOBIKLKEQ EIOLL?O+ICE.EOILL=O,IBE5 EOLIIIOFLLEK EOLIL9E4I@O( OEILE;L>IAO( EOLIEYOJLMIP EOLIL>IDE3O- EILOO$I?E5L< EOLILKOGENIG EOLII9E+O$L2 EOILO)E8L;IA EOILERLMOEINEOLIO%I;E,L5EOILE7I>L9O& OELIERIMOLLM EOILL4I4E*O EILOO'I@L<E7 EOILIIEIOBLG EOILL;E6I@O&EOILE?O-I@L=LOEIIMOFEILM OEILIBO4LGE9 OEILLOEEIPOB ELOIE[IULUOL EIOLLIIMO5E8EOILLGO6ILE? EOLIOPISLVEU EOLIE>O2LCIJ EOILEELHIKO2 EOLIIZLWEcOT EOLILHE=O7IN EIOLO.LFIIE? EOLIIQEXLUOQ EOLIICL<E5O. EIOLEBO3IKLE EIOLIXOOLWE\EOLIO/E6IEL?EILOO0EALCIH OELILWIWOVE\ EOILE4I>L>O( EIOLIJLFO1EA EOLIESOLISLQ EOLIIJE@LEO0OELIEIIJLGO7LOEIOFIMEILM EOILIBLGO4E9 OEILOBEEIPLO EOLIIUE[OLLU EOILO5LIIME8EIOLLGILE?O6 EOLIOPISLVEU EOLIO2IJLCE> EOILEEO2LHIK EOLILWEcOTIZ EOLIE=LHINO7 EIOLO.E?IILF EOILLUOQIQEX EOILICO.L<E5 EOILEBO3LEIK EIOLE\IXOOLWEOLIO/IEE6L?EILOO0LCIHEA OELIOVIWLWE\ EOILL>I>E4O( ELOILFIJEAO1 EOILISLQESOL EOLILEIJE@O0EOILEILGIJO7LOIEIMOFEILM OEILIBE9LGO4 OEILLOIPOBEE ELOIE[LUOLIU EOILIMLIO5E8EIOLLGILE?O6 EOLIISLVOPEU EOLIE>IJO2LC EOILEELHO2IK EOLIIZEcOTLW EOLIE=INO7LH EILOO.IILFE? EOILLUOQEXIQ EOLIICE5L<O. EIOLEBO3IKLE EIOLIXE\LWOOELOIO/E6L?IEEOILEALCO0IH OELIIWOVLWE\ EOILI>L>E4O( ELOIEAO1LFIJ EOILESLQOLIS EOILLEIJE@O0OEILEIIJO7LGLOEIOFIMEILM EOILIBO4LGE9 OEILEEOBLOIP ELOIE[LUIUOL EIOLIMO5LIE8EOILLGO6ILE? EOLIISLVOPEU EOLIE>IJLCO2 OEILEEO2IKLH EOLIOTIZEcLW EOLIE=O7INLH EILOO.E?IILF EOILOQEXIQLU EOLIICO.E5L< EOILO3EBIKLE EIOLLWE\OOIXEOLIO/E6IEL?EILOLCO0IHEA OELIIWLWOVE\ EOILL>E4I>O( ELOIO1EAIJLF EOLIISOLESLQ EOILLEE@IJO0EOILEIIJLGO7LOEIEIOFIMLM OEILIBO4E9LG OEILOBIPEELO EOLIIUE[LUOL EOILIMLIO5E8EOILLGE?O6IL EOLIOPISLVEU EOLIO2E>LCIJ OEILEEO2LHIK EOLILWEcIZOT EOLILHO7INE= EILOO.LFIIE? EOLILUIQOQEX EOLIICO.L<E5 EOILEBO3LEIK EIOLE\IXLWOOELOIO/IEE6L?EILOLCO0IHEA OELILWE\IWOV EOILI>E4L>O( EOLIO1LFEAIJ EOILOLESLQIS EOILE@LEIJO0EOLIEIIJLGO7LOIEOFLMEIIM OEILIBLGO4E9 OEILEEIPOBLO EOLIOLIULUE[ EOILO5IMLIE8EIOLLGO6E?IL EOLIISLVOPEU EOLILCO2E>IJ OEILEELHO2IK EOLIIZOTEcLW EOLILHINO7E= EILOO.E?LFII EOLIEXLUIQOQ EOILICO.E5L< EIOLEBO3IKLE EOILE\IXOOLWELOIO/IEE6L?EIOLEAO0LCIH OELIIWOVLWE\ EOILL>E4I>O( EOILIJLFEAO1 EOLIOLISLQES EOILE@IJLEO0OELIEIO7IJLGLOIELMEIOFIM OEILIBO4E9LG OEILEEIPOBLO EOLIIUE[OLLU EIOLO5IMLIE8EIOLLGO6ILE? EOLIOPISLVEU EOLIE>LCO2IJ OEILEEO2IKLH EOLIEcOTIZLW EOLIO7E=INLH EIOLO.E?IILF EOILOQEXIQLU EOILICO.L<E5 EIOLO3EBIKLE EOILLWE\IXOOEOLIO/IEE6L?ELIOLCO0EAIH OELIE\OVIWLW EOILI>L>E4O( EOLILFO1IJEA EOLIOLESLQIS EOLIE@IJLEO0EOILEILGO7IJLOIEOFLMEIIM OEILIBO4E9LG OEILOBIPEELO EOLILUOLE[IU EIOLO5LIIME8EOILLGILE?O6 EOLIISOPLVEU EOLIIJE>O2LC EOILEEO2IKLH EOLIIZOTLWEc EOLIE=LHO7IN EILOO.IILFE? EOLIEXLUIQOQ EOLIICE5L<O. EOILO3EBLEIK EOILIXLWOOE\EOLIO/E6L?IEEOILIHEALCO0 OELIOVE\LWIW EOILL>E4I>O( EOILIJEAO1LF EOILESISLQOL EOLILEE@IJO0OELIEIIJLGO7LOIENMQMTFJI TJNQNBJ9T4QG TJNQQOJENPTB JQTNNUTLJ[QU JNTQT5NMQIJ8JNTQQGNLT6J? JTQNNSQVTPJU JTQNT2J>QCNJ JTNQJEQHT2NK JTQNJcNZQWTT JTQNQHNNT7J= JNQTT.QFJ?NI JTNQQUTQNQJX JTQNNCJ5T.Q< JTNQT3JBNKQE JNTQQWTOJ\NXJQTNT/J6NEQ?JTNQNHJAQCT0 TJQNJ\TVNWQW JTNQQ>N>J4T( JTNQNJQFJAT1 JTQNJSQQTLNS JTNQJ@NJQET0JTNQJINJT7QGQTNJJINMQMTF TJNQNBT4QGJ9 TJNQJENPTBQO JTQNQUJ[TLNU JTNQNMQIT5J8JNTQQGJ?T6NL JTQNNSQVTPJU JTQNJ>NJT2QC TJNQJET2NKQH JTQNTTNZJcQW JTQNT7J=NNQH JNTQT.J?QFNI JTQNTQNQQUJX JTNQNCQ<J5T. JTNQT3JBQENK JTNQNXJ\TOQWJQTNT/NEQ?J6JTNQT0QCNHJA TJQNJ\NWQWTV JTNQJ4N>Q>T( JTQNT1NJJAQF JTNQNSQQJSTL JTNQQEJ@NJT0TJQNJIQGNJT7QTNJJIQMNMTF JTNQNBJ9T4QG TJNQTBQOJENP JQTNNUTLJ[QU JNTQQINMT5J8JNTQQGT6NLJ? JTQNNSTPQVJU JTQNJ>NJQCT2 TJNQJET2QHNK JTQNJcNZQWTT JTQNQHJ=T7NN JNQTT.NIJ?QF JTQNNQQUTQJX JTNQNCQ<J5T. JTNQT3JBQENK JTNQQWJ\TONXJTQNT/J6NEQ?JTNQQCJANHT0 TJQNNWQWTVJ\ JTNQJ4N>Q>T( JTQNT1JANJQF JTNQNSJSTLQQ JTNQJ@QENJT0JTQNJINJT7QGQTNJNLQLJHTE TJNQNAJ8QFT3 TJNQQNNOTAJD JQTNTKQTNTJZ JTNQT4NLQHJ7JNTQQFT5NKJ> JTQNNRTOQUJT JTQNT1QBNIJ= JTNQJDQGT1NJ JTQNNYTSJbQV JTQNQGJ<NMT6 JNQTT-QENHJ> JTNQLLGHPEJL PGJLJAP3G8LF PGJLPAJOLNGD GPLJPKLTJTGZ GPJLJLLHP4G7GJPLLFJKG>P5 GPLJJRLUPOGT GPLJG=JILBP1 PGJLGDJJP1LG GPLJPSJYGbLV GPLJJMP6LGG< GJPLP-JHLEG> GPLJOEEHLLIL OEILIAO3LFE8 OEILIOLNOAED ELOIOKEZLTIT EOILLHO4ILE7EOILLFO5IKE> EOLIOOLUIRET EOLIE=O1LBII OEILEDO1LGIJ EOLIEbIYOSLV EOLIIME<LGO6 EILOO-IHLEE> EOLIOFLMIMEI OEILIBE9O4LG OEILIPEELOOB ELOIE[IULUOL EOILO5IMLIE8EIOLLGILE?O6 EOLIVFSMOI OVSSBV4O9 VOSVBSOOE OSVSUVLO[ OVSSMV5O8OSVSGV6O? OVSOEILEHLL OEILIAE8O3LF OEILLNEDOAIO EOLIEZOKITLT EIOLILLHO4E7EOILLFIKO5E> EOLIIROOLUET EOLIIIE=O1LB EOILEDIJLGO1 EOLIIYEbOSLV EOLILGO6E<IM EILOO-E>IHLE EOILEILMOFIM EOILIBLGO4E9 OEILEEIPLOOB EOLIOLIUE[LU EOILO5LIIME8EIOLLGILO6E? EOLIOIVFSM VOSSBO9V4 VOSOESOVB OVSSUVLO[ OVSV5SMO8OVSSGO?V6 OVSOELLILEH OEILIALFO3E8 OEILIOOALNED ELOIEZLTITOK EOILO4LHILE7EIOLLFO5IKE> EOLILUIROOET EOLIO1E=LBII EOILEDIJLGO1 EOLILVEbOSIY EOLIE<IMO6LG EIOLO-E>IHLE EOILOFEIIMLM OEILIBE9O4LG OEILEEIPLOOB EOLIIUOLE[LU EOILLIO5IME8EIOLLGILO6E? EOLISMOIVF OVSSBV4O9 VOSOEVBSO OVSSUO[VL OSVV5SMO8OVSSGV6O? OVSOFEILMIM OEILIBO4E9LG OEILLOEEIPOB EOLIOLLUE[IU EOILIMO5LIE8EOILLGE?ILO6 EOLIOPISLVEU EOLIO2E>LCIJ EOILEEIKLHO2 EOLILWOTEcIZ EOLIINE=O7LH EILOO.IIE?LF EOLIEXIQOQLU EOLIICL<O.E5 EOILO3EBIKLE EIOLE\IXLWOOEOLIO/L?E6IEEILOIHLCO0EA OELILWIWE\OV EOILE4L>I>O( EOILO1LFIJEA EOILLQISOLES EOLILEIJE@O0EOILEIIJO7LGLOIEE?ICO<LC EOILI8E/L=O* OEILO8LEIFE; EOLIEQOBLKIK EIOLL?O+ICE.EIOLL=E5O,IB EOLILLIIOFEK EOLIO(E4I@L9 EOILE;L>IAO( EOLIOJLMIPEY EOLIE3IDO-L> EIOLO$E5L<I? EOLIIGOGENLK EOLII9E+L2O$ EOILO)E8IAL; EIOLERLMINOEEOLIO%E,L5I;EILOI>L9O&E7 OELIIMERLMOL EOILL4I4E*O EOILL<O'I@E7 EOILIILGEIOB EOILI@L;E6O&EOLIE?O-L=I@LOIELCE?ICO< OEILI8L=O*E/ OEILIFLEO8E; EOLIOBLKEQIK EOILL?ICO+E.EOILL=O,IBE5 EOLIIIOFLLEK EOLIL9O(I@E4 OEILE;L>O(IA EOLIOJIPLMEY EOLIE3IDL>O- EIOLO$E5L<I? EOLIOGIGLKEN EOLII9O$L2E+ EOILO)E8L;IA EIOLERLMINOEEOLIO%I;E,L5ELIOO&I>L9E7 OELIIMOLERLM EOILE*L4I4O EOILI@E7O'L< EOLIIIOBLGEI EOLIE6L;I@O&EOILE?O-I@L=LOIEO<ICLCE? OEILI8L=E/O* OEILE;LEIFO8 ELOILKIKOBEQ EOILICL?O+E.EOILL=IBO,E5 EOLILLOFIIEK EOLII@O(L9E4 OEILE;L>O(IA EOLIEYOJLMIP EOLIL>IDO-E3 EIOLO$I?E5L< EOLILKIGENOG EOLII9L2E+O$ EOILO)E8IAL; EIOLOELMERINEOLIO%I;E,L5ELIOO&E7L9I> OELIOLERLMIM EOILE*L4I4O EOILE7O'L<I@ EOLIIIEILGOB EOLIE6I@L;O&OELIE?I@O-L=LOIEO<LCICE? OEILI8O*L=E/ OEILIFE;O8LE ELOIEQIKOBLK EOILO+ICL?E.EIOLL=E5IBO, EOLIIILLOFEK EOLII@L9O(E4 OEILE;L>IAO( EOLIEYOJLMIP EOLIL>IDE3O- EIOLO$L<E5I? EOILENIGLKOG EOLII9O$E+L2 EOILE8O)IAL; EIOLEROEINLMELOIO%I;L5E,EILOO&E7I>L9 OELIIMOLLMER EOILI4E*L4O EOLIL<E7O'I@ EOILEIOBLGII EOLIL;I@E6O&EOILE?O-L=I@LOIEICE?O<LC OEILI8E/O*L= OEILIFE;O8LE ELOIOBIKLKEQ EOILL?ICO+E.EOILL=IBO,E5 EOLILLOFIIEK EOLII@E4O(L9 EOILE;L>O(IA EOLIIPLMEYOJ EOLIO-IDL>E3 EILOO$L<I?E5 EOILOGIGENLK EOLII9O$E+L2 EOILO)E8L;IA EIOLLMOEINERELOIO%L5E,I;EILOL9O&I>E7 OELIERLMIMOL EOILI4L4E*O EOLII@L<O'E7 EOLILGEIIIOB EOILL;E6I@O&OEILE?L=I@O-LOIEO<ICE?LC OEILI8E/L=O* OEILLEE;O8IF EOLILKEQOBIK EOILO+ICL?E.EIOLL=E5IBO, EOLILLIIOFEK EOLII@L9E4O( EOILE;L>IAO( EOLIEYIPLMOJ EOLIIDE3L>O- EIOLO$L<I?E5 EOILENLKIGOG EOILI9L2O$E+ EOILE8O)IAL; EIOLINLMEROEEOLIO%L5I;E,ELIOE7O&I>L9 OELIIMERLMOL EOILI4E*L4O EOLII@L<E7O' EOLIEILGIIOB EOILL;E6I@O&EOILE?I@O-L=LOIELCICO<E? OEILI8O*L=E/ OEILLEIFO8E; ELOIOBEQLKIK EOILO+ICL?E.EIOLL=O,IBE5 EOLILLOFIIEK EOLII@E4O(L9 EOILE;IAL>O( EOLIEYLMOJIP EOLIIDO-L>E3 EILOO$E5I?L< EOILIGOGLKEN EOLII9O$E+L2 EOILE8O)L;IA EIOLINERLMOEEOLIO%E,L5I;EILOO&E7L9I> OELIIMEROLLM EOILL4E*I4O EOILL<I@E7O' EOILEIOBLGII EOLII@L;E6O&EOLIE?O-I@L=LOIEE?ICLCO< EOILI8L=O*E/ OEILE;IFLEO8 EOLIOBLKIKEQ EOILL?O+ICE.EIOLL=O,IBE5 EOLIOFLLIIEK EOLIL9I@O(E4 EOILE;L>IAO( EOLIEYOJIPLM EOLIL>O-IDE3 EILOO$E5L<I? EOLIOGLKIGEN EOILI9L2O$E+ EOILO)E8L;IA EIOLOEINERLMELOIO%I;L5E,EILOO&I>E7L9 OELIIMERLMOL EOILI4E*L4O EOLIO'E7L<I@ EOLILGOBEIII EOLIE6L;I@O&EOILE?L=I@O-LOIEJ?NCQCT< JTNQN8T*J/Q= TJNQNFT8J;QE JQTNQKTBJQNK JNTQQ?NCT+J.JTNQQ=T,J5NB JTQNQLTFNIJK JTQNJ4N@T(Q9 TJNQJ;T(Q>NA JTQNTJQMJYNP JTQNJ3Q>T-ND JNTQT$Q<J5N? JTNQQKTGJNNG JTNQN9T$J+Q2 JTNQT)J8Q;NA JNTQJRTENNQMJQTNT%N;Q5J,JNQTN>Q9T&J7 TJQNTLJRNMQM JTNQQ4J*N4T JTNQJ7Q<T'N@ JTNQQGNIJITB JTQNN@J6Q;T&JTQNJ?N@Q=T-QTNJT<J?NCQC TJNQN8T*Q=J/ TJNQNFQEJ;T8 JTQNNKTBJQQK JNTQT+Q?NCJ.JNTQQ=J5T,NB JTQNTFNIQLJK JTQNN@J4Q9T( TJNQJ;Q>NAT( JTQNNPJYQMTJ JTQNQ>T-NDJ3 JNTQT$N?Q<J5 JTQNQKNGJNTG JTQNN9T$J+Q2 JTNQT)J8Q;NA JNTQNNJRTEQMJQTNT%J,Q5N;JNQTT&N>J7Q9 TJQNJRQMTLNM JTNQQ4J*N4T JNQTN@T'J7Q< JTQNNIJITBQG JTNQQ;N@J6T&JTNQJ?T-Q=N@QTNJJ?NCQCT< JTNQN8T*Q=J/ TJNQT8QENFJ; JTQNNKJQQKTB JNTQNCT+Q?J.JTNQQ=J5T,NB JTQNQLNITFJK JTQNN@T(Q9J4 TJNQJ;NAQ>T( JTQNTJNPQMJY JTQNT-Q>J3ND JNTQT$J5N?Q< JTNQTGJNNGQK JTNQN9T$Q2J+ JTNQJ8T)NAQ; JNTQTENNQMJRJTQNT%N;Q5J,JNQTN>J7Q9T& TJQNQMJRNMTL JTNQN4J*Q4T JQNTQ<N@J7T' JTNQTBQGJINI JTNQQ;N@J6T&JTNQJ?T-Q=N@QTNJJ>QBNBT; TJNQN7T)J.Q< TJNQT7QDNEJ: JTQNJPTANJQJ JNTQT*NBQ>J-JTNQQ<T+J4NA JTQNNHTEQKJJ JTQNN?J3T'Q8 TJNQJ:T'N@Q= JTQNJXTIQLNO JTQNNCJ2Q=T, JNQTT#J4N>Q; JTNQG>P;LBJB GPJLJ7L<G.P) PGJLJEG:P7LD GLPJJJGPLJPA GJPLP*L>JBG-GJPLL<P+G4JA GPLJPELKJHGJ GPLJL8J?P'G3 GPJLG:J@P'L= GPLJGXPIJOLL GPLJP,JCG2L= GJLPP#J>G4L; GPLJIBLBE>O; OEILI7E.O)L< OEILE:LDO7IE EOLIIJEPOALJ EIOLL>O*IBE-EIOLL<IAE4O+ EOLIOEIHLKEJ EOLII?O'L8E3 OEILE:O'L=I@ EOLIIOEXLLOI EOLIL=O,E2IC EILOO#L;E4I> EOILO<ICE?LC OEILI8O*L=E/ OEILE;LEIFO8 EOLIEQIKOBLK EOILO+L?ICE.EIOLL=IBO,E5 EOLISCO?V< OVSS8V*O/ VOSSEO;V8 OSVSKVBOQ OVSSCV+O.OSVS=V,O5 OVSIBO;E>LB EOILI7E.L<O) OEILO7IEE:LD EOLIOALJIJEP EOILO*L>IBE-EIOLL<E4IAO+ EOLIOEIHLKEJ EOLIL8O'I?E3 OEILE:L=O'I@ EOLILLOIEXIO EOLIICE2O,L= EIOLO#I>L;E4 EOILLCE?O<IC OEILI8O*E/L= OEILIFE;LEO8 EOLIEQLKOBIK EOILO+ICL?E.EIOLL=E5IBO, EOLIO?SCV< OVSS8O/V* VOSO;V8SE OVSOQVBSK OVSV+SCO.OVSS=O5V, OVSO;LBE>IB OEILI7O)E.L< OEILO7LDE:IE EOLIEPLJIJOA EOILO*L>IBE-EIOLL<E4IAO+ EOLILKIHOEEJ EOLIE3L8O'I? OEILE:O'L=I@ EOLIOIIOLLEX EOLIO,L=ICE2 EIOLO#L;I>E4 EOILO<LCE?IC OEILI8O*E/L= OEILIFLEE;O8 EOLIEQIKLKOB EOILL?O+ICE.EIOLL=IBE5O, EOLIO?V<SC VOSS8V*O/ VOSV8SEO; OSVSKOQVB OSVSCV+O.OSVS=O5V, OVSE>IBO;LB OEILI7E.L<O) OEILIEO7LDE: EOLILJOAIJEP EIOLO*IBL>E-EIOLL<E4IAO+ EOLIIHLKOEEJ EOLII?L8O'E3 OEILE:L=O'I@ EOLILLOIEXIO EOLIE2L=O,IC EILOO#L;I>E4 EOILE>O;LBIB EOILI7E.L<O) OEILO7IELDE: EOLIIJEPLJOA EIOLIBO*L>E-EOILL<IAE4O+ EOLILKOEIHEJ EOLIE3L8O'I? OEILE:L=I@O' EOLIIOEXLLOI EOLIICL=O,E2 EILOO#E4I>L; EOILOIEHICLAIEOL OE*L$I4 IELOI.OL$E" EIOLLFINOEEP EILOE-I.O!L"LIEOI>L9E;O-EILOOHEPIPLKEILOO!E%I,L& IELOL4E:I9O'EIOLELLJIMOF EILOI3O!L-E'EILOE,L0I3O" IELOEOINLJOEvOIL9TEM=P@L7?CJ9=C@7@/MTrkWJAZ2@][ 2][{ P][{ P][{ {][{ P][{ P][{ APGuitarPQ-OoD@H?-MMDEDHI@MNSECKGHILQENJLGC9S=9CKGOG=9Ch-/7F=: @D EB~E -=7@-<=K7K4?EK@K@4-=E7>-6-,3366@<@?@D@',-934=67HD?<=H@HEHm@=47E-7S=S4F-B@SES @E4=-7oE]tCNyBE CnAEBm@LA9E@2S)3\2E4Y;W3 ;?P 4>;? 9M<"7B97 9< =KCB G= L9YG9CL= JHA@E;7.;4 ;7EAJFF<5KK89BF8<BFK95=9C;GFLBIC=9LG -/7F=: @D EB~E =-7@4?-<7K=K@KEK=4-7E@e-IM9X==CDHKZHC9=- 9M=;CKHK 9=CHi4A-99PR?T94-!@M?'ET@"@?E#H?@IEJLHIHPJ!EDH"CUE*CEMCDE@EC@ ?RER>G ?E #9G<9 9Z'2X9!3Y2%4\344V4 4R4 4Wa2949D D=AB@FGSEEQGJE@<2SD=AB@FGSEEQGJE@<2S@OKDBHGQ@IGB>-/7F=: @D EB4= E@-7=K(7F(-<$=7--3I7I==@B EF E7=@-+/7F ;: >DCBB>7+C;-/7F=: @D EB*@ = E-7@K(=F(7K-64977D@==D@DED 7=4-@E+6297D;D>DCD2+>7;C-OoD@H?-MMEDDHI@MNSkECKGHINLQGCL-6497D=D@DED 4-7=@E=B7B-448EB@B -E4=7@U+/7F;: >D CBI+C>;7-FOE])CNyBE CnAEBm@LAA-E@4S)7\4D4Y 74?P>;? 9M<"7B97+?H*@L?'EK&EH@&HJJ*HGJ%EB@H&@IE"?C@@;? ?@@:>P?$I9F<&7>9O4S9F-M7 4-9@/MTrk,JAZ2][7{ ?Melodyxg@_x@B[xBEYEWHAI^H'I+HWHIJIIEXME(BI37E=EBEBEY=E7=BVK]*KLU=LKMKJI*JHN&HES4ENE[xEBK4BE=GE[?G7G7?=E'ES1EBT.-F4F7F=F@BFEZyB74E-=8Ea0EBLMB@97@E]=I@I7I4E7@=@_@@BN.BET8E=HU!HIH'I4HYHIRFIEY=E8BL4B37E=E@EEY=@E7"E[7G=G@G'=7E@iJYJKOKL[LO\/ONKR"KJR3JHX!HEO#ERJ`xJHVyHEWpEBJxB7@B@ET=@1EE@>E;4H[7HEGAEC=/CEd@vXWO!WVP"VTOTQIQVaAVTY+T QLEQ'QUT[>@_I@#@O@@R&9K@=K@KC_99=@C E]AE=E\/EEET9E3EWE"EX8EE;EEQ"EEYEEK"EE[EEO"EEOE#ER"EEKE#EH"EE]EEV"EEVE#ES&EEUE#EI/E EPE'E@+EE`EKXQY4K QLK/LE[EJ->7>ER@>=>4>'@7-=4EMEQ'EE`EEOEEWEEM&EEVEET&EEVE'EKEEXE#EM"EESEEM+EEZE'LVHV/HLIK+IEQ+ERE_NE#BTZ4>\x>Y<> ?J3?@V?@CQ0C EV3EH\7HJK3JKX3KLSDLOM7OQ^@QOM+OQY8QAS]&STKZTQS8QO1OQYJQCLGLO`>L7L4OC7G> LO'L KU%KJS+JHT"H JSAJHZ0HEV8E=EY7E?E=EE7=?GJ[GGCG>G/JCG>LS"L O_'ORSbSTQ'T8Q`4Q OEO#Qb[Q"OU0OL?L"OaAOLKLJH'JK`8KJP'JHP#H J`8JHS'H=272@2EF+7=@E Hf4HEQ'ECDCEc4E CJ'C@E&@Cb7C@U4@>B>@b=@>Q0>c7>7me4M 7M7 9aI794/MTrk7JAZ2Needlessly Striking/simutrans-124.5/simutrans/music/25-Float-on-by.mid000066400000000000000000000653661520675460500217570ustar00rootroot00000000000000MThdxMTrkCopyright (C) 2007 shunter~ JAZ2 2006.10.06vj  ~j''~j'1  ~j  '''~j  1   ~j*NLe  ~j   jgnjXY Float on byQ @Q /MTrk'JAZ2]KGeneral MIDIBassw @{[P@ @`&NO& 2R2 2N&V2&2F82@-KH-0&N@&2N22F2-V-2K22E2"-K- &J5&%RK+V++V+V"&Z5&%&N&XF2^U+Z+"+K+^#&X&%&G&'+X+Z9!&RK&2V22V2&V&"-Z5-%-N-&XF&2&^U&2Z2"2K2&^&#-X-%-G-'2X2&Z9&!NO +R+ +N&V+&F8@+KH+0N@+N++F+&V&+K++E+"&KJ&A&RK&2V22V2&V&"-Z5-%-N-&XF&2&^U&2Z2"2K2&^&#-X-%-G-'2X2&Z9&!RK+V++V+V"&Z5&%&N&XF2^U+Z+"+K+^#&X&%&G&'+X+Z9!&RK&2V22V2&V&"-Z5-%-N-&XF&2&^U&2Z2"2K2&^&#-X-%-G-'2X2&Z9&!NO +R+ +NV++F8+@&KH&0N@+N++F+&V&+K++E+"&K& J5%WO #[# #W_#(O8(@TH0W@#W##O#_(T(@#N##T S#4!UO! -Y- -U-!]!-M8-@(RH(0!UO! -Y- -U-!]!-M8-@(RH(0RK(V((V(V"#Z5#%#N#XF2^U(Z("(K(^##X#%#G#'(X(Z9!!UO! -Y- -U!]-!-M8-@(RH(0!IK!-M--M-!M!"(Q5(%-E-!OF!2&RK&2V22V2&V&"-Z5-%-N-&XF&2&^U&2Z2"2K2&^&#-X-%-G-'2X2&Z9&!NO &R& &N&V+F8+@KH0N@&N&&F&V+K+@&E&&K &J4&RK&2V22V2&V&"-Z5-%-N-&XF&2&^U&2Z2"2K2&^&#-X-%-G-'2X2&Z9&!RK+V++V+V"&Z5&%&N&XF2^U+Z+"+K+^#&X&%&G&'+X+Z9!&RK&2V22V2&V&"-Z5-%-N-&XF&2&^U&2Z2"2K2&^&#-X-%-G-'2X2&Z9&!NO +R+ +N+&V&F8@+KH+0N@+N++F+&V&+K++E+"&K&JA&RK&2V22V2&V&"-Z5-%-N-&XF&2&^U&2Z2"2K2&^&#-X-%-G-'2X2&Z9&!NO +R+ +N&V+&F8@+KH+0N@+N++F+&V&+K++E+"&KJ&AWO #[# #W_#(O8(@TH0W@#W##O#_(T(@#N##T S#4!IK!-M--M-!M!"(Q5(%-E-!OF!2!UO! -Y- -U!]-!-M8-@(RH(0WO ([( (W(#_#O8@(TH(0W@(W((O(#_#(T((N("#T#SA!UO! -Y- -U!]-!-M8-@(RH(0!UO! -Y- -U-!]!-M8-@(RH(0"WO" .[. .W."_".O8.@)TH)0"W@".W..O.)_).T..N.")T) "S5"%$WO$ 0[0 0W0$_$0O80@+TH+0$W@$0W00O0+_+0T00N0"+T+ $S5$%"UO" .Y. .U"].".M8.@)RH)0!UO! !Y! !U!$]$!M8!@!RH!0UO "Y" "U"]+M8+@RH0RO )V) )R)ZRO )V) )R)$Z$'RK'3V33V3'V'")Z5)%)N)'XF'2'^U'3Z3"3K3'^'#)X)%)G)'3X3'Z9'!'WO' 3[3 3W3'_'3O83@0TH00'W@'3W33O30_03T33N3"0T0 'S5'%"RK".V..V."V"")Z5)%)N)"XF"2"^U".Z.".K."^"#)X)%)G)'.X."Z9"!'NO' "R" "NU""V'F8'@'KH'0'N@'"N""F"U"V'K'@"E""K "'J4'"RK".V..V."V"")Z5)%)N)"XF"2"^U".Z.".K."^"#)X)%)G)'.X."Z9"!'NO' 'R' 'N"V'"'F8'@'KH'0'N@''N''F'"V"'K''E'""K"'JA'"NO" .R. .N"V.".F8.@)KH)0"N@".N..F.)V).K..E.")K) "J5"%'NO' 3R3 3N3'V'3F83@.KH.0'N@'3N33F3.V.3K33E3".K. 'J5'%"RK".V..V."V"")Z5)%)N)"XF"2"^U".Z.".K."^"#)X)%)G)'.X."Z9"!'NO' 'R' 'N'"V"'F8'@'KH'0'N@''N''F'"V"'K''E'""K'J"A'$RK$0V00V0$V$"+Z5+%+N+$XF$2$^U$0Z0"0K0$^$#+X+%+G+'0X0$Z9$!UO )Y) )U$])$M8@)RH)0UO )Y) )U$])$M8@)RH)0$WO$ 0[0 0W$_0$0O80@+TH+0$W@$0W00O0+_+0T00N0"+T+ $S5$%IK)M))M)M"$Q5$%)E)OF2UO $Y$ $U$])M8)@RH0"WO" .[. .W."_".O8.@)TH)0"W@".W..O.)_).T..N.")T) "S5"/MTrk)JAZ2General MIDI C{Drums{] @[*`#d*U*##*7*#&d#d*U'K*#'&#*A*#*U#d*##*7*#*U'U&d#d#'&*#*7*##d*U#*#*7*#'U&d*U#d&'*##*A*#*Z#d#*##U*H*###d'U&d*U#*&'#.K.##d*U#*#*7*##d'K&d*U'*#&#*A*##d*U*##*7*#'U*U#d&d'*&##*7*##d*U*##*7*#*U'U#d&d&#'*#*A*##d*U*##*7#U*###d'U&d*U#'*&#.K.##d*U#*#*6*##d'K&d*U&#'*#*A*##d*U*##*7*#&d#d'U*U'#*&#*7*#*U#d*##*7**K*#d'K&d*U*#'&#*A**d*#d*U*###U*7#*#'U*U#d&d&*#'#.K.#*U#d*##*7*##d&d*U'K#*&'#*A*##d*U*##*7*#*U&d#d'U*&#'#*7*#*U#d*#0700U0070#d0U'U#'0-7--d--7--U-&d&#U.d#.#&p'K'&&A&#U.d.##*U#d#*#*7*#&d*U#d'K*#'&#*Z*#*U#d#*#*7*#*U#d'U&d*'#&#*7*#*U#d#*#*7*##d'K*U&d*#&'#*A**d**U#d#*.7.#U*7#*#*U#d&d'U&'#*#.K.#*U#d#*#*7*#&d'K#d*U&*'##*Z*##d*U#*#*7*#&d'U*U#d&#*'#*7*#*U#d*##*7*##d&d*U'K#*'&#*A*##d*U*###U*7#*#*U&d'U#d*'&##.K.#*U#d*##*7*#'K&d*U#d#*'&#*Z*#*U#d*##*7*#&d#d*U'U#'&*#*7*##d*U*##*7*#'K#d*U&d&'*##*A*##d*U*##*7#U#*#*U'U&d#d'#&*#.K.#*U#d#*#*7*#*U'K#d&d#&*'#*A*##d*U*##*7*#&d*U'U#d&#*'#*H*##d*U#*&-&.d.&#&*U&d'U#d*'#&#*K*&-&&p#d#&#&A&#&p'U#d'#&&7&&p&##d1U*U1#*&#&.U6K6.&-&#d&d'K'#&&7&6U.E.6&-&*U#d*#&&.P6K.6&7&#d'U&d&#'&#&.K6U.6&&*U#d*#&#&6K.U.6&-&#d&d'U&#'&7&6U.E6.&-&#d*U#*&&.P6U6.&7&&d#d'K&'#&#&6K.K6.&&*U#d*#&&6K.U.6&-&&d'U#d#'&&7&6K.Z.6&-&*U#d#*&&._6K.6&7&#d'U&d#'&&#&#U.X6U.6#&&#d&A&##&K&#'K#d#'&U&#&K&#d&U#&&7&&U#U#&&7&'U&d#d#'&&p&#U.d#..A.*U1U#d1#*&#&6K.U6.&-&'K#d&d#'&&7&.E6U6.&-&#d*U#*&&.P6K6.&7&'U&d#d#&'&#&.K6U.6&&*U#d#*&#&6K.U6.&-&'U#d&d&'#&7&6U.E.6&-&*U#d*#&&.P6U.6&7&'K#d&d'&#&#&6K.K6.&&*U#d*#&#&.U6K.6&-&'U&d#d&'#&7&.E6U.6&-&#d*U#*&&.P6U6.&7&#d'K&d&#'&#&.K6K.6&&&A#d&##&K&##d'K'#&U&#&K&&U#d&#&7&#U&U#&&7&'U&d#d#&'&p&#U.d.#.A.*U#d*#*K**7*#'U&d*U#d*&'##.H.#*U#d*#.7.*7*#&d'U*U#d'*#&#*7*##d*U#*#*7*#*U#d'K&d&'#*#*A**d**U#d#*#*7#U*##*U'U#d&d'&*##.K.##d*U#*#*7*##d'U&d*U&'*##.H.##d*U#*#*7*#&d'U#d*U#&'*#*7*#*U#d*##*7*#'K&d#d*U*&'##*A*##d*U*##*7*#*U&d'U#d'*#&#*7*#*Z#d*##*7*#'K#d*U&d#&'*#*A*##d*U#*#*7*##d&d*U'U'#*&#*7*##d*U#*#*7*##d&d'K*U'&*##*A*##d*U*##*7*#*U&d'U#d#*'&#*7*#*U#d#*#*7*#'U*U&d#d&*#'#*A*##d*U#*#*7*#*U#d&d'U*&#'#*7*#&A#d&##&K&##d'K#'&U&#&K&#d&U&#&7&&U#U#&&7&'U#d&d#&'&p&.d#U.#.A.*U#d#*#*7*#'U#d&d*U*#&'#.H.##d*U#*#*7*#'U&d*U#d#&*'#*7*##d*U#*#*7*#'U*U#d&d#'&*#*A*##d*U*###U*7*##'K*U#d&d#&*'#*K*.U.*Z#d*##*7*#*U&d#d'K'&#*#*A*#*U#d*##*7*#'U&d#d*U*&#'#*H*#*U#d*##*6*#*U#d&d'K'#&*#*A*##d*U#*#*7*##d&d*U'U*#'&#*7*##d*U#*#*6*#'K&d*U#d*#'&#*Z*#*U#d*##*7*##d*U'U&d*'#&#*7*#*U#d*##*7*#'K#d*U&d*'&##*A*#*U#d#*#*7#U*##'U&d*U#d&*'##.K.#*U#d#*#*7*#&d'K#d*U*'#&#*A*##d*U*##*7*#'U#d&d*U&*#'#*7*#&A#d&##&K&##d'K'#&U&#&K&&U#d#&&7&&U#U&#&7&&d'U#d&'#&p&.d#U#..A.*U1U#d1*#&#&.U6K.6&-&&d'K#d&'#&7&.E6U.6&-&#d*U*#&&.P6K.6&7&'U#d&d'#&&#&.K6U.6&&#d*U#*&#&.U6K6.&-&'d#d&d#&'&7&.E6U.6&-&*U#d#*&&6K.P6.&7&&d'd#d#'&&#&6U.K.6&&*U#d*#&#&.U6K6.&-&#d&d'd&'#&7&6U.E6.&-&*U#d*#&&.P6K.6&7&#d'd&d'&#&#&.K6U.6&&#d*U#*&#&.U6U.6&-&&d'U#d&#'&7&6K.E.6&-&*U#d*#&&#U6K.P.6#&7&#d'U&d'#&&#&6U&d&6&U&#d*U*#&#&.U6K.6&-&#d'U&d&#'&7&.E6U.6&-&#d*U#*&&6U.P.6&7&'K#d&d#'&&#&.K6K.6&&#d*U#*&#&.U6U6.&-&#d&d'U'&#&7&.E6K.6&-&#d*U*#&&6K.P#U#.6&7&#d&d'U&'#&#&&d6U&6&U&#d*U#*&#&6K.U.6&-&'d#d&d#&'&7&6U.E6.&-&#d*U#*&&.P6K.6&7&&d'd#d'#&&#&6U.K.6&&#d*U#*0700U0070#d0U'U#0'-7--d--7--U-&d&#U.d#.#&p'K&'&A&#U.d.###d*U1U*1#&#&6K.U6.&-&&d'K#d#'&&7&.E6U.6&-&*U#d#*&&.P6K.6&7&&d#d'U#&'&#&.K6U.6&&*U#d#*&#&.U6U.6&-&&d#d'U&'#&7&.E6K.6&-&#d*U#*&&6K.P#U#6.&7&#d'U&d'&#&#&&d6U&6&U&#d*U#*&&6K.U6..Z.'U#d&d&'#&7&6K.Z.6&-&*U#d*#&&._6K6.&7&'U&d#d#&'&#&6U.X#U#6.&&*U#d#*&#&.U6K.6&-&#d'd&d&'#&7&6U.E6.&-&#d*U*#&&.P6K.6&7&'U#d&d#'&&#&6U&d6&&U&*U#d#*&#&6K.U6.&-&#d'd&d'#&&7&.E6U.6&-&#d*U*#&&.P6K.6&7&#d'd&d'#&&#&6U.K6.&&#d*U*#&#&6K.U6.&-&#d&d'd&#'&7&.E6U.6&-&*U#d#*&&6K.P6.&7&'U#d&d'&#&#&6U&d6&&U&#d*U*#&&.U6K.6&-&&d'U#d'&#&7&6K.Z.6&-&#d*U*#&&._6K.6&7&#d'U&d#'&&#&.X6U.6&&*U#d#*&#&6K.U6.&-&'d&d#d&'#&7&6U.E6.&-&#d*U*#&&6K.P6.&7&#d'U&d#&'&#&&d6U&6&U&#d*U*#&#&.U6K.6&-&&d'U#d'&#&7&.E6U6.&-&#d*U*#&&.P6U.6&7&#d'K&d#'&&#&.K6K.6&&*U#d*#&&.U6K.6&-&&d'U#d'&#&7&.Z6K.6&-&*U#d#*&&6K._6.&7&#d'U&d#'&&#&.X#U6U#.6&&#d*U*#&&.U6K.6.Z.'U#d&d&'#&7&6K.Z.6&-&*U#d*#&&._6K6.&7&#d'U&d#'&&#&.X6U6.&&#d*U#*#&d&##d'U#'&K&&d&&K&#d&d#&&7&-U--7--U#d'U#'--7-)U))K)*U#d#*#*7*##d&d*U'K'&*##*A*#*U#d#*#*7*##d'U&d*U*'&##*7*##d*Z*##*6*#'K#d*U&d&#*'#*Z*##d*U*##*7*#*U#d'U&d&#'*#*7*##d*U*##*6*##d*U&d'K'*&##*A*#*U#d*##*7*#&d*U'U#d#*'&#*7*#*U#d#*#*7**K**U'K#d&d&'#*#*A*#*U#d*##*7#U#*##d'U*U&d&'*##.K.#*U#d*##*7*#'U&d#d*U*&#'#*A*#*U#d#*#*7*#'U*U#d&d&*#'#*7*##d*U#*#*7*#*U#d&d'K'#&*#*A*#*U#d*###U*7*##*U#d&d'U'#&*#.K.##d*U#*#*7*#*U#d'U&d&#'*#.H.##d*U#*#*7*#*U'U&d#d&#'*#*7*##d*U*#0700U0070'U0U#d0#'-7--d--7--U-&d&#U.d#.#'K&p'&&A&.d#U.##*Z#d*##*6*##d*U'K&d#&*'#*A*##d*U#*#*7*#'U*U#d&d'&*##*7*#*U#d*##*7*#'U*U#d&d&*#'#*A*##d*Z*###U*7#*##d*U&d'U'*&##.K.##d*U#*#*6*#'K*U&d#d#*&'#*A*#*U#d*##*7*#'U#d*U&d&#*'#*7*#*U#d*##*7*##d'U&d*U*'&##*A*##d*U*##*7#U*##&d*U#d'K*'#&#*K*.U.#d*U*##*7*#&d*U'U#d#*'&#.H.#*U#d*##*7*##d*U&d'U'*&##*7*#*U#d*##*7*##d*U'U&d&*'##*A*##d*U*##*7#U*###d'K&d*U'&#*#*K*.U.#d*U#*#*6*#*U#d&d'K#&*'#*A*#*U#d*##*7*#*U'U&d#d#*'&#*7*#&d#d#&&7&&d&&-&'A#d'#070#0K0#d0U#0#0U0#'A-U#d'#--7-)U))7)#d*U1U#*1&#&.U6K6.&-&&d'K#d'#&&7&6U.E6.&-&*U#d#*&&.P6K.6&7&'U&d#d'&#&#&6U.K6.&&*U#d*#&&6K.U.6&-&'U&d#d&#'&7&.Z6K.6&-&*U#d#*&&6K._6.&7&&d'U#d#&'&#&.X6U#U.6#&&*U#d*#&&6K.U.6&-&#d'U&d'#&&7&6K.Z6.&-&#d*U*#&&6K._6.&7&&d#d'U'#&&#&6U#U.X#6.&&*U#d*##&d&##d'U'#&K&&d&&K&&d#d&#&7&-U--7-'U#d-U#'--7-)U))K)*U1U#d*1#&#&.U6K.6&-&&d'K#d'#&&7&.E6U.6&-&*U#d#*&&.P6K.6&7&'U&d#d'&#&#&6U.K6.&&*U#d*#&#&6K.U6.&-&&d'U#d&'#&7&6U.E6.&-&#d*U*#&&.P6U.6&7&'K&d#d&#'&#&6K.K6.&&*U#d*#&&6K.U6.&-&&d'U#d'#&&7&.Z6K.6&-&#d*U#*&&6K._.6&7&&d'U#d&'#&#&.X6U6.&&*U#d*#&-&.d.&#&*U#d'U&d&*#'#*K*&-&#d&p#&#&A&##d&p'U'#&&7&&p&##d1U*U1*#&#&6K.U6.&-&&d'K#d&'#&7&.E6K.6&-&*U#d#*&&.P6K.6&7&#d&d'U&#'&#&6K.K6.&&*U#d#*&#&.U6d.6&-&#d'd&d#'&&7&6d.E6.&-&#d*U#*#6d&p6&#'d#d&p&'#&d&6d6/MTrk ZJAZ2PianoGeneral MIDIp ,{[O @]@`B-E@12F+LE@59P\LH9L'<9MK92 E BL+$;E1B1>$70>XB#BXGBE+7>; >E1=B12$$90>XB#BXIBE9>2= B%E>-;1+F+GE;57P\GH7G'<7MK7+ E B>G2I@9E4BEg@4@3BZBEE'E.025E B@ ;E1>B1+$$70>XB#BXGBE+7;> =1>-B%E2F+IE=59P\IHI'9<9MK92 E >BI>-B%E;1+F+GE;57P\GH7G'<7MK7+ E >BGBV;X>T(F^C@#>B;+;;>]4W CI(4;>@>>>9>-FX>9@-@E=J-FW@-=;X>TBV(F^C@#>B;+;;>]4W CI(4;>>>9>@>-FX@>9-=X@T-FW@=-E@1B-2F+LE@59P\LHL'9<9MK92 E BL;9+IB4>EE)f;4;3>Z>EE'E.0+5E >;B=92IB4>EE)f=4=3>Z>EE'E.025E =>BB%>-E;1+F+GE;57P\GH7G'<7MK7+ E >BGE1B@2$$9E1+$$70>XB#BXGBE7;+> EB%=1>-2F+IE=59P\IH9I'<9MK92 E B>IE1>+$;B1$70>XB#BXGBE>+7; BE;J>J(F;B>%4`;J(B]q4B;>J@E-FW-@>@E=J-FW-@=BE>J;J(F;B>%B]4`(;Jq;4B9>@>>>-FX9-@>@E=J-FW-@=>JAE.F>A).A]5`>JqA5>C>?>:>>>0F;:?C>c0!AT>X.FWA.>9><>A>-FX9-<A:>7>A>+FX7+:AAJFE)FgAF)EEAJ)Fg)AE>>:>5>3F;>:5c3!AJEE'FAE)'9`AJE]qA9E>-<1A.F+HE<55P\HH5H'<5MK5. A >H'I79>4:EA)f7473:Z:EA'A.0'5A :7>A1>1:.$9$5#>XE>A:9.5 A>%71:-'F+CE753P\CH3C'<3MK3' A :>C<1A>-.F+HE<55P\HH5H'<5MK5. A >H'I79>4:EA)f7473:Z:EA'A.0'5A 7:>.$A1:9>1$5#>XEA>9:5. 7A1:'$>1$3#>XC>A'37: >>:>C>?>0F;>?:Cc0!AJFE)FWAF)EEAJ)FW)EA?T>XCV0F^F@#?C>+7W?]>; FI07>?:><>A>5FX<5:AAXET)FWE)AP.F[<>5..F5PP>5<>P5P.F5< F5F"XF5F"5><"Z5><" @/MTrk ZJAZ2GuitarGeneral MIDIk j]{[s@ @b6.@.2F9.>2#2F12E@.+F>+#+F1E@+B;EEG7 9.B.2F@.29B@E9@9@e2E9>;.>.+FB.E.2;>BEE92#2F12E@@9@e2E9>+FE.B.>.;.>+#+F1+E@EG7 2F2E9.;.>+#+F1+E@G7E (FC.B.>.;.2CB9;>B.-F@.>->@9 -F=.@.9.>-=@9 ;.B.>.(FC.2B>C;B9.9.@.2>@9@92#2F12E@.+FE.;.>+#+F1E@+;BEG7 2F2E9.E.>+#+F1E@+;7EG2F3E9.B.2BE;>E92#2F12E@.B.;.2CB>;B9.9.2@9>@9.B.>(#(F1(B@CB;4BC -F2@9.:.>0#0F10C@7CF.F3A9'#'F1'E@.C.:.2:AC>A9..FE.H.A.2AHE>A9'#'F1'C@.'FC.A.2A:>CA9.9.>.#.F1.A@<9A5EA 'F2A9.:.>'#'F1'A@C:ACA3 .F2A9.:.C.>0#0F1C@0?7FC )F2F9US9SQ'Q UUQ QLL QQLLQ!Q L LF@2@2QYVSQUGVQKUUKQQ;UQU@UQ4QU2UQ1Q L%LQ6Q L'LQ1Q L #LO@'QZ@VRQUE VQJU UMQQ>UQUEQ6UQU:UQ5QSBSQ6QS:SQ3QL*Q9LQL(LQ3QL!L>J<"LNJJ6LJE4EJ3JE%EJ.J E"EJ$J L$L J J=@2@_QiVPQUFVUQDQUJQ>UQUGUQ9 QUGUQ;QSCSQ;QL6LQ>Q L'LQ<Q L#L3@ @+/>i@*2M@9>29@9@BC?L9QLLV[QUCVUVUUFVQDUQUPQFUQL8LQDQ L0J:LBJ L=LJ1JE0EJ5J E$E B(BE)EB!B@@@+;@2:+2949;1;L>QO LURQQCUUPQQCUQL5LQ=Q L)J5LJL.LJ,J E%EJ+J E E;F@,CT(1@/*(/727CBC;*BCB>0CGc ; >4:GC<>0CGG4>;%G ;JS>7JJY >4<JNN4N;';NM>*NL<>L>*>L6>#L>J$JJEJ @J_-B@ 4<- JID4 II\919 IGF!@(GGD&9GE(E(E#@`9EO@%(;CY@/9(C /B?BCM7=CGT7 ;AGGc >E;GNKEHE;6N>NNEJ;E>9NC6L9 > CL;' C<LA ;L C41B/J1BJ4;"B+J.;JB^@ -7Ge @4=-4;+ GGT;;5 G;ECEE= Ee->@@4:-49/9 ;.;$='=-@2J@9629>:>@@8LD@V\UHV UUSEBULQLQUZ E>3UQGQULUQ=QL7> QHLQ L,LJ3J L: E3 LEJ1J E+E>'>L@+?@2;+2999;B;LEQKLUWQ ;>>UQGVOQU>VUQ>Q L)>L Q.Q L,LJ1JL#LJ(J E$EJ$J E E@!&7@&-,-2524E;@H4EL@ JXEID JEDIIJEE;IE@?!EJ@E@:>9@>@7 20@>0>@=9&@>1>29E3 9E9>>8@+;@28+27.79@9VUKUQ5QSG>>+SQ:Q L0>LJ3>$JE% E >/@-@BFBCLEWCE(:G\@@(/4/ 76GEE7;$EC9>)C C65C >;.@N\ET4C @;6E>B N;GPOO4O>;(G;>A NJEC>;2NE ;4:CCLFL>3C>;:4;B@JA;)JB;G9>3G>!-:>9@G>@G>47EG)EEG4-E E=9"E @+@9@D9.@9>X4; > 4 =>==8-:@@49- =>:4>@5@9) 9;04!; 4=#=O@#(26R@2// 7G6/,( /'/>G76/&/>BJ B;-;C: ; C; B&B>-> ;;Z4,;,>B@I>BQ4@BCP ; EXCGREILGJPILLJNLLOMNQJOSJQSUDUVJXEV@ZKX @/-8 X>Z*- 4CX4]W ;-]; Q1:@I@$@2@;/Q ;;-;X;92X 9 U/UQI42QS6SL# 4Lb@/.6@52.5:2:AT<: <FW>HYF:':JFH:%: MCJ5(M5K<K:!: J2AJ F0FA&:- A<;:>I<AP>FMAFHTHJI JKEKMNOYMRQORTWTVRV$;VT@@ +7$+3?3V RM7BO8RORJ 7R 7-J>7KCJ>1KH1H>O/O?&F%FJ-? C(JCFF J#Jw@.9Hg@5>.:,5HFJ ::%:HIF0HJ;-J A/A.@-DJ_@5B 5 -5@J5H\9;MWH<8MA89<<,YA< A 'AC2CEPE@+BFS@3./>>F6:->?F?:?X +3AJ?CHACE<@)0ABE@A01:/53::3>@94:4@0);5!91>K>@'8>Y@"37'>5A]:/:'35 :a?=FC JJMUR< VARVY.JYR-FR?M@@ QQTP @TQ ?/"Q@?H;QH?0!?M7M#?* ?H/E1HE ?*? A&E&IE AP@<@Me(e c4c bXcWbcbWb^9^Y,Y^5^Y(Y^*^ YY ^^ Y YlT*TVATRVVSTTDVTV0VT+TV$VTTmF.HCFJIHKNJMDKR>MRY/"T, @@2TY^GQ^]:;M-MY)];M YMV#VM<R?)FFMRTSOJ;TFEOF R;M> RM)F1 FO0J08O J@MM@ .'M56.5:6: A*A/A6AH?HRX A@RQZAQ McACMOSAOJ<A:JM7AM H7HM\HLMHM\M H;H F0:5>2:>A6FAF7!<,F <H3A5HAJ+6@'>@ ..'J. 5<570J7CJ7JM3MRSRQT:3Q MM:MH>HM[MH<H F3FH@HF6FHCHJ9JF/FA4?@?AMMMA-AA:@./@ 57.5:-:<' Ar<$A.FWAF EU58EA:A>4>5CM: >-C>A7 : A525.-5>.:H5O5B > 5AJA:, C5:565M@C"M@'3JG@ .2'.5:J5HI977 H7F<#F:&A-:A?'? :C"Cq@:2@A6AC@C FMF:HMHJNJKMKMQM ORORMRTT@TV[@"? )2") 2D V 2d@.<Tl@ 5A.5;"MKRYQ>YMA@A M9VPVMM:@3:TGM@T :-3 R4J.JR?0"O,H&O H?MA1A HU :JPHJHOH HW:0 HFF ::' F : FF: F : A/:%A: ?;F=:.:?>9F>?<??S @.;@?>>51><<5 . 5/<5>D535 >:-5-5:9.95)5r7,5579:9:E:O< ?S> <@?>H<?K>5- ?AE 5CJAENC FNE CA:3F :EPCAHECFA EICFJE FHL HKR:7KHFJIHKNJKMHOKM QFORMQRTK: TVJWGV WYE [LY ]R@[^@3G]]F@^F[Y]3:63b_[b:?7 ?THT^I^ VJ.9VTJT [W['F.RC YRR'.>YMIM.VZ7GVM@M R]7R :@ HHHO^:O7S!FN7MXF+;MK]@K$M+JT@ +<$+2GJFP 23EFA<JY3AJ7B?3?HS7 3;H>7> >G3>7>?S7? ?V0B?>>76>07?>72?7<271 <7>W>0D?S?CICCDC HWHHc7F0 H7JSJJRJKVK @KQ)J@0C )05H5K MT:7A9AM F:: FFV<6FA3A MG<MMX<> MA5H>@I= HJFI J5=HN@<3<5 A<F@5&F<-H<E3E<9 <5: 5:@ ?S0;>39@7> 3 ? 0FZ77=F<>7>9< >>B>$7$ <1<:$7Y<VIYTUVRNTROFOKCJNKJHK HFHFCKCAC$A?E ? >,< <><&<:3:7:@0/ 759@ 57@7:80<=:>L<?O>?AHCHAFNC@);@ 0= )0FHGH<4A,HH<MYH<9MH6AH<F1F<0@ E654@ Eg<5@,@TWVATVY9YR?R M1M J=JH/H FEFA7A >0><(< :%:$:-<2:<:#@.0 <A#:". A</MTrkKJAZ2 Float on by @General MIDI/simutrans-124.5/simutrans/music/26-Tantalizingly-Unusual.mid000066400000000000000000001164121520675460500241410ustar00rootroot00000000000000MThdxMTrk{Copyright (C) 2007 shunter~ JAZ2~j     2006.10.06Tantalizingly Unusual~j  ~j 1  ~j   ~j  ~j  Vj   j#jjXQz#Y/MTrk "JAZ2 General MIDIBass|@{][2 @H@!W!\!B![!D'!Ha!Zq!(X(#!Z[!!W,!$C$"!S!!W!!X! C!C!!A!!K'!!W!a![`![0 X,!P!"!T!!X!!W!\!B![!D'!Ha!Zq!(X(#!Z[!!W,!C"!S!!W!![! !@!!F!!D!!H'!!L!a!^`!(^0( ![,!G"!W!([(!Z! !?!!E!!C!!G'!Ka!]`!![!#!][!!Z,!F"V!Z!!Y! !>!!D!!B!!L'!Xa!\`!(\0( !Y,!$E$"!U!!Y!![! !@!!F!!D!!H'!La!^!!^*!N![,!G"W![!!W!\!B!!@!!D'!Ha!Z!W(Z-(!W,!["!S!!W!&]$&$j<$&S4&$K<&V$/&$j;$&P&"&a`&&Y<&!_)! !Y/! !\*!F!c4!!D$!TM7!gP!(!g^!_4h<K$ X9b4<`)Ob>G !n!CN !`$!JA!Y/!]4!dn! $nr$&b4&$k<&N$$& $[9$&T&&$[<&Q$/&I&^,&L!h!Ca !d!Y*!`!G N!]V!"!mS!%)d$)$m$(G()P))G(\4(&e<(R&$(O(U((P!X'! Q<!R.!B!`/!i:!Rk! !`u!!\1!\<%<&< <6 <<'<$< <6< <7">[>:>R757(7%7 7=77'7)7%7 7E77%7)7*77577.7)7%7&7.7)7.7 7=77*7 7=77% 775775)7O7E&7R9?9,979,<0<< <<(< <( <<6<*<0<,<6<<-<<%< <# <767)7'7 7 77 7)7#77M7;=;Y;9,;L7J7 73 77*77( 77377/77*7 7* 77377/ 77(7 7,77/7 7/77(77:77B7 7377*7 7(77,7 7/77*7 7J7 7$7`7:&7R797,717,9*99 99"9 9" 9909*9*9,9099'9909 9- 97M 7/717,7)7,797-7H7h7H7h9R99) 99 99# 99# 99 99'99' 99) 99 99! 99 99# 99 99% 99/ 99899H99M 99;99;99/ 99;99; 99H#99C9.9M9b757(7%7 7=77'7)7%7 7E77%7)7*77577.7)7%7&7.7)7.7 7=77*7 7=77% 775775)7O7E&7R7>7%7&7 76 77/7&7&7 7677>7'7$7 767 7/7,7&7 767767$7&7 7>77/7%7&77+ 7 97"9[9:9R797,717,9*99 99"9 9" 9909*9*9,9099'9909 9- 97M 7/717,7)7,797-7H7h7H7h7I 77)77 77 77.77'7*7.77) 77' 77)7)7.7&7A7 9@9949 9299:9&9D9(94&9R9:59C757(7%7 7=77'7)7%7 7E77%7)7*77577.7)7%7&7.7)7.7 7=77*7 7=77% 775775)7O7E&7R9E9#9& 99 9999 99 99 9959*989,9E 99(99( 99(9929+9@9,9J995 99(99&99,9(989-9T9*90 9/>E>#>& >> >>>> >> >> >>5>*>8>,>E >>(>>( >>(>>2>+>@>,>J>>5 >>(>>&>>,>(>8>->T>*>E >/9E9#9& 99 9999 99 99 9959*989,9E 99(99( 99(9929+9@9,9J995 99(99&99,9(989-9T9*90 9/9P 99099 99 99599.9*95990 99. 9909)959&767)7'7 7 77 7)7#77M7;=;Y;9,;L7I 77)77 77 77.77'7*7.77) 77' 77)7)7.7&7A7 9@9949 9299:9&9D9(94&9R9:59C@>>4> >2>>:>&>D>(>4&>R>:5>C7D7*77,747 7477!77 77,7*7!7*7,7 7<77%77477<7+7!7)747 74 77477< 77K7^7<7`7%7&7 76 77/7&7&7 7677>7'7$7 767 7/7,7&7 767767$7&7 7>77/7%7&77+ 7 97"9[9:9R@Y@@Y@ @V@'@V@%@>>4> >2>>:>&>D>(>4&>R>:5>C9R99) 99 99# 99# 99 99'99' 99) 99 99! 99 99# 99 99% 99/ 99899H99M 99;99;99/ 99;99; 99H#99C9.9M9b<\<<3 <<' <<- <<- <<* <<1<<1 <<3 <<( <<+ <<# <<- <<& << <<' <767 737 7'7 7 77 7 7. 77#77M7;=;Y;9,;L7>7%7&7 76 77/7&7&7 7677>7'7$7 767 7/7,7&7 767767$7&7 7>77/7%7&77+ 7 97"9[9:9R7%7&7 76 77/7&7&7 7677>7'7$7 767 7/7,77 7%7 767)7'7 7 77 7)7#77M79=9Y99,9L<_*<@[@,@X@*@c@'@[@*959(959-9G9(9h&99K9 9;97C7"9F9@/MTrk ]JAZ2VGeneral MIDI 0Strings @{[7]@`C*@<E#>CE@@<C*E#>@CE@<E#C*>C@E@<C*E#>CE@C*E#@<>EC@@<C*E#>C@EE#C*@<>E@CC*@<E#>@ECEGJ.H5>EJHC5@GE.>@ECCGE5H.`H@GECC5G.`E.C5CG@@G>E@CEGJ.H5>HEJE.@GC5>E@CH.CGE5`G.C5@GEHC`@GCE.C5@G>C@EE.C5@G>@EC@GC5E.>EC@E.@GC5>EC@C5@GE.>@ECC5@GE.>@CEE.@GC5>CE@E.C5@G>E@CE.@GC5>@CEH5EGJ.>HJEC5@GE.>@CEH.CGE5`EC5CHG.@G`@G@CGC5E.>CE@J.H5EG>HJEE.C5@G>@CEH.CGE5`@GEHCG.C5`GC@GE.@C5>@ECE#C*@<>@CEE#@<C*>EC@@<E#C*>EC@@<C*E#>C@EC*@<E#>E@CE#C*@<>E@CE#C*@<>C@EC*E#@<>EC@EGJ.H5>HJE@GE.C5>@CECGE5H.`CEHC5@GG.`E.@GG@CC5>@CEJ.EGH5>HJEE.C5@G>@CEH.E5CG`@GC5EG.HC`CGE.@C5@G`E.C5@GEC@`CE@C;H0,HC@/MTrk kJAZ2General MIDIMelodys T{] @[E,BH\EOH.HHpHE(dE@+Z@ @@>(-@$>9@<" <2E^=GPE.GHXH!H6 H J* H9JHG9G0E_QECIQC@BC@9B>%><(B9 <eE[ 9%ECK*CEYxE@D9@>:;>@? @EE+;EGX*GH`9HJM7JGP@GHA4HE;$EC19CE(!E@1;@>Ht><.r9/<B98E,BH\EOH.HHpE(HdE@+Z@ @@>(-@$>9@<" <2E^=GPE.GHXH!H6 H J* H9JHG9G0E_QCIEQC@BC@9B>%><(B9 <eE[ 9%ECK*CEYx@DE9@>:;>@? @EE+;EGX*GH`9HJM7JGP@GHA4HE;$EC19CE(!@1E;@>Ht><.r<9/B9/MTrk= hJAZ2 HQSoloist{[] @@General MIDIkCQCDZD JvJER1EGH EDHGYEGLeJaLGWJHSGHMvM LzLHHHJLJOlOQeQOnL\OLHJJmHJKaKLXL JzHTJHEAJrEJHbHGZGHVH LmL OqLNOLHHG^G%OO Q.Op Q LROL HyHGEGHkH L^LO{O!OO9LvL%HH G[GEKE GfGHiH J`J-HHGkG EgE@K@?u?>J> X@>@l>[@d><}<+@r@E`EGxGHgG_HGEOEHeQJtHJGk\GHzH+EzEC_C@e@>J!@R>@CvC!E)E0@eAU@ @S A@>_>`=>b>@D @>;@;<\<>Q>@L@ CzC EaEGjGHwH IrI JyJIdIHqH@FCU@ CGuGF@FD@DEyECbC@F@AhAEdEHdH JpJLrLJ[JJhJLqLJHJ LkL LzJ>LJLcLMx!LTML OxOMkMLfL HOHEVEC4C Hx C=HCEOE CYC>-@m>@<><>g> 7%7 <^<747>D>@zAT@A@[<\@74<7>Z<7,7<_<<>f>B;E;>L>C}CAiA@CAp@@SA@>G0>q>;J@k;!>T@> CzCE!E GxEwGE%@\@!CCEE@l@>s >!C}C'@m@ z>9T99S>9

{> 9P9y9W>9i>R!>:~>{> ;q> ?z@^?@?r@S?@ 9X9?z@[?@ ?f? NL>,>YX>o@NM@+@YY@nCHQC'C\\Cl@\H@0@`8@ANMA+AYYAo@NM@+@YY@o@HP@(@\[@m@\G@1@`7@ANMA+AYYAo@L<@<@Y$@$Cb>C:C\EC@XP@(@][@D@F@/MTrk JAZ2]General MIDI rEcho 3  [( @{``5>>$>>. >>B->>= >>=>>b>>:>\E>2 < <`->>1>>. >>B >>; >>= >>=>>HQ>'>\\><\ <<7 <<% <<$ < < <<:`5>>$>>. >>B->>= >>=>>HR>&>\]><\ <<7 <<% <<$ < < <` >>6 >>$>>. >>B >>; >>= >>=>>HR>&>\]> < <`5>>$>>. >>B->>= >>=>>HQ>'>\\>L&>>3 >>0 >>8 >>0>>3 >>0 >>L>>6 >>* >>: >>4 >>2 >>2>>,>>4->>? >>1 >>D >>7>>> >>F>>NO>)>Y[><\ <<7 <<% <<$ <<:7:7Y&7S9Y9#9* 99" 99;99 99'9 9)99:9 9- 99(99-9909 9-9 9.9 979}7HR7&7\]77\ 77/ 77! 77770 77$7 777% 77P77,77$77) 77077& 77"77) 77B 77777,77, 77;77) 771 77977L>7:7Y&7"7NO7)7Y[77Y7#7* 77" 77;77 77'7 7)77:7 7- 77(77-7707 7-7 7.7 7777H7 7-77- 772 77F777 771 77;7 7NN7*7VE77NP7(7Y\77\ 77/ 77! 77770 77$7 777% 77P77,77$77) 77077& 77"77) 77B 77777,77, 77;77) 771 7797 7L?797Y'7!7\J7.7`:77\J7.7`:77\J7.7`:7@7\ 77/ 77! 77770 77$7 777% 77P77,77$77) 77077& 77"77) 77B 77777,77, 77;77) 771 77977L>7:7Y&7 9\K9-9`;9 7\K7-7`;7 ;HT;$;\_;7\ 77/ 77! 77770 77$7 777% 77P77,77$77) 77077& 77"77) 77B 77777,77, 77;77) 771 77977L>7:7Y&7"5NO5)5Y[5k7NQ7'7Y]79Y9#9* 99" 99;99 99'9 9)99:9 9- 99(99-9909 9-9 9.9 979{;HT;$;\_;i7\K7-7`;79NP9(9Y\9m7NO7)7Y[79Y9#9* 99" 99;99 99'9 9)99:9 9- 99(99-9909 9-9 9.9 979}7HR7&7\]7i9\K9-9`;9 9NQ9'9Y]97\ 77/ 77! 77770 77$7 777% 77P77,77$77) 77077& 77"77) 77B 77777,77, 77;77) 771 7797 7L?797Y'7S9\ 99/ 99! 99990 99$9 999% 99P99,99$99) 99099& 99"99) 9;b@;8;\G;27Y7#7* 77" 77;77 77'7 7)77:7 7- 77(77-7707 7-7 7.7 777}7XR7&7]]77F7%7>7'787)7?7(7A7)717,7?7*7>7,7N7)7D7,7N7:7F7/MTrkg JAZ2zGeneral MIDIEcho 5  @ {[(]-2BP2(2IG24NJ4.4Q:44SA474NH44BP4(4K\4,4@?494K'4 4=T4$4N_4*4BP4(4K\4l4BO4)4IF44BQ4'4K]4k4@@484K(4 4NK4-4Q;4 4NK4-4Q;4 4NK4-4Q;44@?494K'4!2BP2(2K\2+2BQ2'2K]2+4=T4$4N_4i4@@484K(4!4SA474NH44JS4%4O^4/MTrk JAZ2{General MIDIvEcho 5   @[(]m-IK---L;- /NB/6/II/->Q-'-G]-+-<@-8-G(-`-<@-8-G(- ->Q-'-G]-k->P-(-DG-A-<@-8-G(-`-<@-8-G(- ->Q-'-G]-k->Q-'-G]-k/NB/6/II/-FT-$-J_-/-9*-/MTrk9JAZ2Tantalizingly Unusual/simutrans-124.5/simutrans/music/27-March-Winds.mid000066400000000000000000001353421520675460500217760ustar00rootroot00000000000000MThd MTrkkCopyright (C) 2007 shunter~ JAZ2 March WindsAQ 5X`/MTrkUed JAZ2]GuitarY @[l@&\_@HK`E K P LD@HL`JK`HKJJKHHKJ0HEK`@K-KE`@EK`HKEHJKJLKH`-L2KJK2K2J`JK`HKJHJKJHK0HEK`2K>KE2`AK2>`AEKHGKGHKE`GK(KH(KG(HCKCEK`@KE`@K<0E0@D@>`-K>K@- 9K>KAKX>9->(KA`(>K`>AKHDKDEKA`(KDKE`DGK`@KG`>K@`(`4K0`K8K`8>(c`(-K05K04K5 45K 4K53K404K304K`>AK`EKA0HGKGHKEE`H2(KGKG((KHDKDEK`@KE`K<0E0@D>@`>K2K@ 9K>KAK`>2>A9)K`2K`5K20EKP@59D`3K9)K@@K>>K@(K8KEKP>(8@K<;K9K;98K85K54K2K40K20/K/-K 9K-`@9K2K @ B @EK> B @ EBEK @ B EKE@ B @ 2EB2K>K@ @ B >@AK B @ AEKB @ B HKE@ BEK@PBLD-K@@2H @ B @EK B @ BE @ B @ B @ (KBL-@`GKDK`GDGKDK`GKGDKD`DK(DG@K(K @ B D@DK@GK B @ DKGKBGD @ B DGGK@DK B @ @K@(KDK(BDG @ B @GK@DKD B @ BDKGKDG @ BGKDKGDGGKJK@D B @ JG(@B-K@KEKHK @ B @ B @ B @ B @ B @E-KH@ B @ BEK @ B @HKE B @ BHLK @ B @LJK2K-- @B@ B JEK@ B @ BEHK @ B HLK@ B @ L2B@JKJLK2KLJK0JHK`EKH0;50@AKE`-KA@K @2B @ B @ B @ B @ B @ -@B@-K`HK`EKH`ADE`-K@KA- @ B HK@@ B @ EKHB @ B @K@E B @ B@>K@(K- @ B @ B @ <GKB @ B @EKG B @ -K@B(EKE @ B @9K B @ B9 @ B @ B @ -B @ B @ B @ BE@^.bAK0EKA0DKE0EKD0AKE0EKA0EDK0EKD0AKE0AEK0DKE0DEK0E@K0@EK0EDK0DEK0E@K0EK@0DKE0DEK0@KE0@EK0EDK0DEK0E>K0>EK0EDK0DEK0>KE0>EK0EDK0EKD0>KE0>EK0EDK0DEK0EK0>AK0@KA0@>K0>K00;K<0>K;0>K>@K@AKA@K@AKDKADEKEGKHKGJDHGKJ0GHK0HKHHJDHDJHGK0GHK GK@HAB DEf@@GGKFA BC D EGK@G@HKG@HJK@J@GKACDEHK@G0@GKHACDEGGK@0EKG0EGK0GGK`@GGKA BCD E@DCBA@4G@EK @ B @ B @ B @ B @@E`9K K@@K>@@>KBCDFIJK,JIFDCB@2BDEFIK.@?K>@K>K?CK>@@KC>K@EK>@@KE>K@>@FK@F>KK@KHK@>&IFDB@ACEGJK JIGFDCA@@@H>KBDFIK&IFDB@ >>K@ @ B @ B @ @B @ B @ B>K@>&E @K>@0@@K0?D@0?K?0?@K0@K@0@K<0>K>0;K>0;K;0D<;K>K< ; >@@K@K>@KE@AD@A>K>@KAK@ E @ACD@KC@AKCKA E @EDCEAKCKACEK E EGD@0GHKGKH@AB CDE`D BA@@GHK0@HGKCE @GHK J2H @JGKBDE@HKG JKH LKJ MDL MLD LJD HKJ HLKLJKJHK GKH GEK NK@EABCDEFGHI JK LM NO Pt@N@K@LK LMK JKM JMK HKM HMK GKM GMK MEK MKE MDKEKDGKEGHKHLKLKDKHKHGKGKDHKKHGK@CE HKG@@GKHCE G@HKHGK@CE GHK@HJK0JHK @ B @ B @ B @92B9<2@H@<@K DK@DCDCAD@DACK@AKC@KA?K@@K?AK@@DA9K@7D99D78D97D8>K7;K>K>@K@AD@DA@>K>K2K`AK>`AEK0HGKGEEHK`GKH2(K(K(GHDKDEK`E@K`K0E0>@D@`>K@2K AK>K9K`29A)K>>`2K`5K20EKP@9D5`)93KK@K>8K@>K(KEKP8`9K-K<098;E>AeE1>8A8;>EAuA1E3>1kE0A%ED+>EA6>8E2:@E>AeE1>8A8@;>EAuA1>1E3kE0A%ED+>E%G8>2D3@(H@G@>Du@5>6@GCD3{>GD@%D8>5@8G)KDG@>uG6>8D9@9:@>@GDG8@D3>2@(ID>G@u>6D3GC@5{G>D@%@8D8>5G)J@D>@Gu>8G6D9@9@;@G>DuL2E8H6;LEHeE8@1H8;@EHuE1H1@3j@@0H%@@D+E@%@8E(H3IE@HuH3@CE6{E@H%@)H8E8KH@EuE8H9L6:@ELH@8@H3E(I@HEu@CE6H3{@EH%E8@)H8K@HEuH9L6E8:@ELHuE2A6>8@;E>Ae>8E1A8;>EAuE3>1A1kE0A%ED+>E>8E2A6:@E>Ae@E1>8A8;E>AuA1>1E3kE0A%ED+>E%G8>2@(D3H@G@>DuD3@@5GC>6{G@D>%@8D8>5G)KG>D@uD9@9>8G6:@G>@D@G8@(D3>2ID@>GuGCD3@5>6{@D>G%D8>5G)@8J@@G>DuG6@D9>8@9;@G>D@HHCE8E@EHiHC@EEGwE@H)EH@:HI@E@HI@H@@)HBEN90E*A.<AeE*A0-:@><EAu@A*E+>*kE0A%E:+>E%G/D+@">*I@DG>u@-G9D+>.z@>@GD%G#@/D0>-@K>@GDu>/G.@0D0;@G>DuL*H.E/:@LEHe@*H0E/@;E@HuG+@*D*kG0D%G:+@G%@/E"H+IH@Eu@9E.H+z@EH@%@#E/H0@KEH@uE/H0L.;ELHuE/L*H.;LEHe@*E/H0:@E@Hu@+E*H*@k@0H%@:+E@%@/H+E"IHE@u@9E.H+{HE@%H0@#E/J@H@EuL.@H0E/;LEHuE*A.>0E<AeE*-A0:@E><AuE+@>*A*kE0A%E:+>E%@/H+E"I@HEuE.@9H+{HE@%E/H0@#J@E@HuE/H0@L.;ELHuE*A.0;>E<AeA0E*-:@><EAuE+A*@>*kE0A%E:+>E%E/>"A+IE>AuE9A+>.{>EA%E#A0>/J@>AEuA0>/@E.;E>AuL*H.E/;LEHe@*E/H0;E@HuE*H*@+j@@0H%@:@+E@)@=E0H9E@HEiH9@:E2@C$G-IGC>uG-><C0{CG>%C4>'G5KGC>uG6J3C4:@JCG@>2C$G-IGC>uG-><C0{>GC%C/>#G0KGC>uC/J.G0:@JCGuE.A/@H*;HAEeE0<*A/;A<Eu<+A*E*k<0E%<:+A<%G2@$D.H@@GDu@@2D.G0{G@D%G(>2@5D6KGuG5 > @GD%@2E$H.H@H@EuH.E2@L0{HEL?x@CHK]AJ<[A "E/@A+>"I>AEu>.A+E9{E>A%A0>/E#J@E>Au@E.>/A0;>EAuD.G*@/;G@DeG*@/D0;G@DuG+D*@*j@G0D%G:@+@G%G/D+@"I@GDu@.D+G9{DG@%D0>-G#@/J@>DG@u>/@0D0G.@;@G>D@=H9E0E@HEiE<@:H9wH@E)E=H=@1@HE@I@@=@%>/C"G+IGC>u>9G+C.{C>G%A0>/E#KEA>u>/E.A0:@E>AuE.@H*A/;HAEeE0A/<*;A<EuA*<+E*k<0E%<:+A<%G0@"D,H@DG@u@/D,@G-{G@D%G$@0>-D0KGuG0 > @DG%@0E"H,H@EH@uH,@E/L-{LHE%E0@$H0K@u@0+EH@)@@E2HE?v@E@H)@HDED@6H@EI@D@)ES9@HGv@HK "C[A]AxJ<@@4E&H0IEH@uH0E3@?z@HE@%@%E2H2@K@HEuE2H3L0;LEHuE/L*H.;LEHe@*E/H0:@@EHu@+E*H*@k@0H%@:+E@ EH9@/E"H+I@HEuE.H+@9{HE@%@#E/H0J@HE@uH0L.E/@;LEHu>/E*A.;E>Ae>/E*A0;>EAuH*E*@+j@@0H%@:@+E@%G/@">*D+I@DG>u>.G9D+@-{>DG@%@#H0E/J@E@HuE/L.@H0;ELHuA.E*>0<Ae>-EAuA*E+@>*kE0A%E:+>E%G/D+@"ID@Gu@.D+G9{@GD%@/D0G#J@G@Du@G.@/D0;G@DuG*D.@/;G@DeG*@/D0;@GDuD*G+@*j@G0D%G:@+@G%G/@"D+I@DGuG9@.D+{@GD%@/C0G#>-J@G>@CuC0G.>/@@0;>@GCuH.L*E/;LEHeH0@*E/;@EHuH*L*C+j@C0L%@C:+HC%E/>"A+IE>AuA+E9>.{AE>%E#A0>/KA>EuE.A0>/:@>EAuA.E*@>/;E>Ae>/A0E*;E>AuA*E+>*kE0A%E:+>E%E/>"A+H@>AEuE9@>.A+{EA>%@/>-G#D0KD@>Gu@0>/G.D0:@>@GDu@L*E/H.;LEHeE/H0@*;@EHuA*>*E+kE0A%E:+>E%G/@"D+H@D@GuG9D+@@.{@DG%@/G#D0K@GDuD0@/G.;G@D>/G+C"H@CG>u@C.>9G+{GC>%G0C/>#KC>GuJ.G0C/;JCGB/G"J+H@JGBuG.@B9J+{BJG%D#=/A0KDA=uA0=/D.;D=AuI.E/L*:@LEIeI0@*@E/;@EIu@+E*I*k@0I%@:+E@%@/E"I+I@EIuE.I+@9z@I@E%I0@#@E/KIE@uI0L.E/;LEI=/E+B"I=BEu=9B.E+z@EB=%=#@B/E0K=BEuB/E0I.;BIEuB0I*E.?/;BI?Ee?/E0=*B-:@?=BEu@E*I*@+k@0I%@:+E@%@/H+E"IEH@u@9H+E.{@HE%@#H0E/J@EH@uL.E/@H0;LEHuH.E/L*;LEHe@*E/H0;E@HuE*@+H*j@@0H%@:@+E@%E/<*A+>"I><AEuE9<.>-A+{A<>E%>/A0E#J@A>EuE.@A0>/;>EAuH.L*E/;LEHeE/H0@*;@EHu@+H*E*j@@0H%@@:+E@%E/<*A+>"IAE><u<.A+>-E9{<EA>%>/A0E#J@AE>u>/@A0E.;E>Au>/A.E*;E>Ae>/A0E*;E>Au>*A*E+j@E0A%E:@+>E%@/I+E"II@EuI+E.@9{@EI%E/@#I0K@IEuI0L.E/:@ELIuI.L*@E/;LEIeI0E/@*;@EIu@+I*E*k@0I%@:+E@%@2I-E$H@E@Iu@I-@<E0{IE@%@'E4I5KEI@uI6E4L3;LEIu@6=8D2:@D=@e@=8@8D1;=D@u=1@1D3kD0@%DD+=D)BHKCG8EBKGiKCBEGGv@GKB)B:GH@KIGBKIBHB}D'@$=|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0@=@D/MTrkK'JAZ2BassYYx @[2@]2HKJ<}Cx @[2]2@HKJ<C'&` &@&`& &T& &` &@&`& &O& &` &@&`& &O& &` &@&`& &W &&` &@&`& &T& &` &@&`& &O& &` &@&`& &O& &` &@&`& &W &(` (@(`( (X( (` (@(`( (`( (` (@(`( (\( (` (@(`( (W( (` (@(`( (X( (` (@(`( (`( (` (@(`( (\( (` (@(`( (W( !` !@!`! !T! !` !@!`! !O! !` !@!`! !O! !` !@!`! !W !!` !@!`! !X! !` !@!`! !`! !` !@!`! !\! !` !@!`! !W! !` !@!`! !X! !` !@!`! !`! !` !@!`! !\! !` !@!`! !W! &` &@&`& &T& &` &@&`& &O& &` &@&`& &O& &` &@&`& &W &&` &@&`& &T& &` &@&`& &O& &` &@&`& &O& &` &@&`& &W &(` (@(`( (X( (` (@(`( (`( (` (@(`( (\( (` (@(`( (W( (` (@(`( (X( (` (@(`( (`( (` (@(`( (\( (` (@(`( (W( !u0!P!o !@!` !@!y !@!` !D!b|@]2 @CHKxJ<[2'!&Q &@&Q& &F& &Q &@&Q& &B& &Q &@&Q& &B& &Q &@&Q& &I &(Q (@(Q( (J( (Q (@(Q( (Q( (Q (@(Q( (N( (Q (@(Q( (I( !Q !@!Q! !F! !Q !@!Q! !B! (Q (@(Q( (B( (Q (@(Q( (I (!Q !@!Q! !J! !Q !@!Q! !Q! !Q !@!Q! !N! !Q !@!Q! !I! !Q !@!Q! !F! !Q !@!Q! !B! !Q !@!Q! !B! !Q !@!Q! !I !!Q !@!Q! !J! !Q !@!Q! !Q! !Q !@!Q! !N! !Q !@!Q! !I! &Q &@&Q& &F& &Q &@&Q& &B& &Q &@&Q& &B& &Q &@&Q& &I &!Q !@!Q! !J! !Q !@!Q! !Q! !Q !@!Q! !N! !Q !@!Q! !I! &Q &@&Q& &F& &Q &@&Q& &B& &Q &@&Q& &B& &Q &@&Q& &I &&Q &@&Q& &J& &Q &@&Q& &Q& &Q &@&Q& &N& &Q &@&Q& &I& !Q !@!Q! !F! !Q !@!Q! !B! !Q !@!Q! !B! !Q !@!Q! !I !!b0!P!] !@!Q !@!e !@!Q !@+V +@+V+ +O+ +V +@+V+ +V+ +[ +@+[+ +W+ +[ +@+[+ +R+ +V +@+V+ +O+ +V +@+V+ +V+ +Q +@+Q+ +N+ +Q +@+Q+ +I+ )Q )@)Q) )F) )Q )@)Q) )B) )Q )@)Q) )B) )Q )@)Q) )I )(V (@(V( (O( (V (@(V( (N( ([ (@([( (S( ([ (@([( (O( !V !@!V! !O! !V !@!V! !N! HK @[2]2CxJ<@'&Q &@&Q& &J& &Q &@&Q& &Q& &Q &@&Q& &N& &Q &@&Q& &I& (Q (@(Q( (F( (Q (@(Q( (B( (Q (@(Q( (B( (Q (@(Q( (I ((Q (@(Q( (J( (Q (@(Q( (Q( (Q (@(Q( (N( (Q (@(Q( (I( !b0!P!] !@!Q !@!e !@!Q !@+Q +@+Q+ +J+ +Q +@+Q+ +Q+ &Q &@&Q& &N& &Q &@&Q& &I& )Q )@)Q) )F) )Q )@)Q) )B) )Q )@)Q) )B) )Q )@)Q) )I )(Q (@(Q( (J( (Q (@(Q( (I( (Q (@(Q( (J( (Q (@(Q( (F( !Q !@!Q! !J! !Q !@!Q! !I! !Q !@!Q! !J! !Q !@!Q! !F! !i0!P!i !@![ !@!r !@![ !D!h|x]2[2HK @CJ<@'!!?![! !S! ![ !@![! ![! !V !@!V! !S! !V !@!V! !N! !Q !@!Q! !F! !Q !@!Q! !B! !Q !@!Q! !B! !Q !@!Q! !I !!Q !@!Q! !J! !Q !@!Q! !Q! !Q !@!Q! !N! !Q !@!Q! !I! &Q &@&Q& &F& &Q &@&Q& &B& !Q !@!Q! !B! !Q !@!Q! !I !(Q (@(Q( (J( (Q (@(Q( (Q( !Q !@!Q! !N! !Q !@!Q! !I! &Q &@&Q& &F& &Q &@&Q& &B& &Q &@&Q& &B& &Q &@&Q& &I &(Q (@(Q( (J( (Q (@(Q( (Q( (Q (@(Q( (N( (Q (@(Q( (I( (Q (@(Q( (F( (Q (@(Q( (B( (Q (@(Q( (B( (Q (@(Q( (I ((Q (@(Q( (J( (Q (@(Q( (Q( (Q (@(Q( (N( (Q (@(Q( (I( !Q !@!Q! !F! !Q !@!Q! !B! $Q $@$Q$ $B$ $Q $@$Q$ $I $&Q &@&Q& &J& &Q &@&Q& &Q& &Q &@&Q& &N& &Q &@&Q& &I& &Q &@&Q& &F& &Q &@&Q& &B& &Q &@&Q& &B& &Q &@&Q& &I &&Q &@&Q& &J& &Q &@&Q& &Q& (Q (@(Q( (N( (Q (@(Q( (I( !Q !@!Q! !F! !Q !@!Q! !B! &Q &@&Q& &B& &Q &@&Q& &I &(Q (@(Q( (J( (Q (@(Q( (Q( (Q (@(Q( (N( (Q (@(Q( (I( +Q +@+Q+ +J+ +Q +@+Q+ +Q+ +Q +@+Q+ +N+ +Q +@+Q+ +I+ #Q #@#Q# #J# #Q #@#Q# #Q# %Q %@%Q% %N% %Q %@%Q% %I% !Q !@!Q! !F! !Q !@!Q! !B! !Q !@!Q! !B! !Q !@!Q! !I !!Q !@!Q! !J! !Q !@!Q! !Q! !Q !@!Q! !N! !Q !@!Q! !I! *Q *@*Q* *J* *Q *@*Q* *Q* *Q *@*Q* *N* *Q *@*Q* *I* *Q *@*Q* *F* *Q *@*Q* *B* !Q !@!Q! !B! !Q !@!Q! !I !!Q !@!Q! !J! !Q !@!Q! !Q! !Q !@!Q! !N! !Q !@!Q! !I! !Q !@!Q! !F! !Q !@!Q! !B! !Q !@!Q! !B! !Q !@!Q! !I !&Q &@&Q& &J& &Q &@&Q& &Q& &Q &@&Q& &N& &Q &@&Q& &I& !Q !@!Q! !F! !Q !@!Q! !B! !Q !@!Q! !B! !Q !@!Q! !I !&Q &@&Q& &J& &Q &@&Q& &Q& &Q &@&Q& &N& &Q &@&Q& &I& &Q &@&Q& &F& &Q &@&Q& &B& &Q &@&Q& &B& &Q &@&Q& &I &!Q !@!Q! !J! !Q !@!Q! !Q! !Q !@!Q! !N! !Q !@!Q! !I! !Q !@!Q! !F! !Q !@!Q! !B! !Q !@!Q! !B! !Q !@!Q! !I !!V !@!V! !O! !V !@!V! !V! ![ !@![! !W! ![ !@![! !R! %` %@%`% %T% %` %@%`% %O% %` %@%`% %O% %` %@%`% %W %#u0#P#o #@#` #@#y #@#` #@}%J|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0%@/MTrkPJAZ2@YYx ^[2]2 Square waveHKJ<}Cx ^[2]2@HKJ<CPA*@A J 0A A A*0E*JA AA A* EE A@> EA*> >> >* AE> E0>*@> E 0> > >*0A*E> >> >* AE >@> EA*> >> >* AE> E4@*\G*@@@ @*G @D*0@@ @*0G%D@@ @ G@D*0@@ @* DG%@T@* EA*> >> >* AE> E0>*@> E 0> > >*0EA*> >> >* AE >@> EA*> >> >* AE> E4@*\G*@@@ @*G @D*0@@ @*0DG%@@ @ G@D*0@@ @* DG%@T@* EA$> >> >$ AE> E4@$\G$@@@ @$G @D$0@@ @$0DG@@ @ GD$@0@@ @$ DG@P@$@G@ E0@@ @$0E@@$0@@@ @$0G @@@ GD$@ @@ @$ DG@ G4@$\E$@@@ @$E @@$0@@@ @$0E@@ @ E@@$0@@@ @$0E@P@$@E@ E0@@ @$0@@$E0@@@ @$09 @@@ 9@@$0@@@ @$0E@ E4@$\E$@@@ @$E @@$0@@@ @$0E@@ @ E@$@0@@@ @$0E@PA$@AE H0AA A$0E$HA AA A$ EE A@> EA$> >> >$ AE> E4@$\E$@@@ @$E @@$0@@@ @$0E@@ @ E@$@0@@@ @$0E@PA$@AE H0AA A$0HE$A AA A$ EE A@> EA$> >> >$ AE> E4>$\E$>>> >$E >A$0>> >$0EA@> > EA$>0>> >$ AE>P@$@@E E0@@ @$0E@$@0@@@ @$0E @@@ E@$@0@@@ @$0E@ E8@`E$ @'@ @'@ E @@$)@@ @'@09$9@'<9<@$ @4$<</4tC%\G%CCC C%G E%C0CC C%0G#E@C > G>C(0>> >( CG#>T>%>> >%G >C%0>> >%0;C@> > ;>C$0>> >$ CG>PGA$@A H0AA A$0HE$A AA A$ E9 A@< 9C$< << <$ CE< E4@%\G%@@@ @%G @D%0@@ @%0G#D@@ @ G@D(0;(0D; D#PD@%\E%@@@ @%E @@%0@@@ @%0x ^HKC[2J<]2P@@A$\J$AAA A$J E$A0AA A$0EE@A > EA$>0>> >$ AE>P@$@E@ G0@@ @$0GD$@ @@ @$ DG @@@ GD$@ @@ @$ DG@ G4@$\G$@@@ @$G @D$0@@ @$0D;@@ @ ;@D$0@@ @$ DG@X@8G(E$ @'@ @'@ E @@$)@@ @'@09$9@'<9<@$ @4$<</4tC$\G$CCC C$G CE$0CC C$0EC>`A >E$A0AA A$ EJAP<$J@< E0<< <$0C$E< << <$ C9 <@< 9C$< << <$ CE< E4@$\G$@@@ @$G D$@0@@ @$0GD@@ @ G@D$0;$0D; DPD@$\E$@@@ @$E @$@0@@@ @$0E@@ @ E@@$0<$0@< @P@@ `E% @'@ @'@ E @@%)@@ @'@ 0E(9@'<"E<@( @4(<"</4x4*@$<*x ^[2@HKx]2J<CP@(\E(@@@@(E @@(0@@@ @(0E!@@ @ E@@%0@@@ @%0E!@P@$@E@ E0@@ @$0E@@$0@@@ @$0E @@@ E@$@0@@@ @$0E@ E0@<4@$\E$@@@ @$E @$@0@@@ @$0E@@ @ E@$@0@@@ @$0E@PA$@EA J0AA A$0JE$A AA A$ EAE`@ E@@$0@@@ @$0E@ E4@$\G$@@@ @$G @D$0@@ @$0DE@@ @ E@$@0@@@ @$0E@PA$@EA H0AA A$0HE$A AA A$ EE A@> EA$> >> >$ AE> E4@$\G$@@@ @$G D$@0@@ @$0GD@@ @ G@D$0@@ @$ DG@P@$@@G G0@@ @$0GD$@ @@ @$ D; @@@ ;D$@ @@ @$ DG@ G4@$\G$@@@ @$G @D$0@@ @$0D;@@ @ ;@C$0@@ @$ CG@P@$@G@ E0@@ @$0E@@$0@@@ @$0C @@@ CC$@ @@ @$ CC@ C4A$\J$AAA A$J AE$0AA A$0EE@A > E>C$0>> >$ CE>P>$@E> E0>> >$0A$E> >> >$ AE >@> EA$> >> >$ AE> E4>$\E$>>> >$E A$>0>> >$0;A@> @ ;@D$0@@ @$ DG@P@$@@G E0@@ @$0@E@$0@@@ @$0J @@A JE$A AA A$ EJA J4@$\G$@@@ @$G D$@0@@ @$0;D@@ @ ;D$@0@@ @$ DG@T>$>> >$G >C$0>> >$0GC@> > GC$>0>> >$ CG>T@$$@>E E0>> >$0EB$> >> >$ B>G`@ G@@$0@@@ @$0G@ G4@$\E$@@@ @$E @@$0@@@ @$09@@ @ 9@@$0@@@ @$0E@P@$@E@ E0@@ @$0E@$@0@@@ @$0E @@@ E@@$0@@@ @$0E@ E4A$\H$AAA A$H AE$0AA A$0EE@A > E>A$0>> >$ AE>P@$@@E E0@@ @$0@@$E0@@@ @$0E @@@ E@$@0@@@ @$0E@ E4A$\H$AAA A$H AE$0AA A$0EE@A > E>A$0>> >$ AE>P>$@E> E0>> >$0A$E> >> >$ AE >@> EA$> >> >$ AE> E4@$\G$@@@ @$G @@$0@@@ @$0G@@ @ G@@$0@@@ @$0G@P@$@G@ G0@@ @$0@$@G0@@@ @$0G @@@ G@$@0@@@ @$0G@ G4@%\G%@@@ @%G @@%0@@@ @%0G#@@ @ G@@(0@@@ @(0G#@P@*@G@ I 0@ @ @*0ID*@ @@ @* DD @@@ DD*@ @@ @* DD@ D8?$`G* ?'?$ ?'?$ G ?B*0?$ ?'?$)BG*9?';$G;B* B6*;$;/6p}@ |{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0@@/MTrk3JAZ2 Synth stringsYYx @[2]2HK@J<}C[2 @x]2@HKJ<C3;3>B@@x;3;>@>B@@y>`@;@3EBEE@x@@3EEBEE@YE@;3>B@@x;3;>@>B@@y>@EB@3EE@b;3 @J<]2xHKC[2@@E;+>7@6y>`@;@+E7EE6YE@@+E7EE6x@aE@+E7EE6x@aE9+>7>>6y>o9@+E7EE6r>.C:CC9 E@_>>.CC:CC9x>aC;.@:@@9Y@;@.E:EE9xxC @[2]2@HKJ<39+@>7>>6yE`>9;+@7@@6y@@+E7EE6b;>+C7CC6y@CEo>;+@7@@6Y@;@+E7EE6YE@@.E:EE9rHK@x @[2J<]2C3@0E>EE7@6y>;@;+@7@@6Y@;;+@7@@6x;a@9+>7>>6Y>99+>7>>6y>o9;+@7@@6x>+C7CC6Y@;B+CG7GG6x>BaG@+E7EE6x=+B7BB6YE@qB=@+E7EE6YE@9+<7>6y>`<99+<7>6Y<>9@+E7EE6YE@@.E:EE9YE@B3GBGG@r}8#GB=-|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML08@=/MTrk?JAZ2Yx @[]@HK8}DrumsC$J98*8J<8$9**5 *!/ !R, R$J&A*8 $*&*5!/ !*./ .!5R, R!!![$J@C8xHK @*8]J<8*$!!3*5 *!./!$ !.!/R, R!*8$J&A $&**5 *./!/ !.R,!! R!$J*888$**5 *!/./ !.R, R$J&A*8 $&**5!/ !*./ .R,!5 !R*88$J!!8!$*!3*5 *!./!$ !.R,!/ !R*8&A$J *&$*5 *./!/ .!!!R, !R*88$J8*$*5 *./!/ .!R, R$J&A*8 *$&!/*5 !*./ .R,!5 !R$J8!!*88$*!*5!3 !*./!$ .!!/R, !R$J&A*8 $*&*5 *./!/ .!!!R, !R8$K*98*$*6 *.0!9 .!R- R*9$K&B *&$!9&B*6 &*!.0&B &.R- R8&B$K*9!98$*&!*6&B!1 *!&!9&B.0 !&.R-!9&B &!R&B*9$K *&$*6 *&B.0!9 .&!!7R-&B !R&8$K*98$**6 *.0!9 !.R- R&B$K*9 *&$!9&B*6 &*!&B.0 &.R- R8!9*9$K&B8*!&$!1*6&B *!&!9&B.0 !&.!9&BR- R&!$K&B*9 $*&*6 *!9&B.0 &!.R-&B!7 &R!$J*898889*$*5 *!/ !R, R*8&A$J *&$*5!/ !*./ .R,!5 !R8$J!!*88$*!*5!3 !*!$./ !.!/R, !R&A*8$J *&$*5 *!/./ !.R,!! !R$J988*889*$*5 *!/ !R, R$J*8&A $&*!/*5 *!./ .!5R, !R*8!!8$J8!$**5!3 *!./!$ !.R,!/ !R*8&A$J &$**5 *./!/ !.!!R, !R988*8$J8*$9*5 *!/ !R, R*8&A$J $*&*5!/ !*./ .R,!5 !R$J!!*888$!**5!3 *!./!$ !.R,!/ R!&A*8$J $&**5 *./!/ .!!!R, R!8*8$J8*$*5 *./!/ .!R, R*8&A$J *$&!/*5 *!./ .R,!5 R!*8!!$J88$*!*5!3 !*./!$ .!!/R, R!*8&A$J &*$*5 *./!/ !.!!R, R!$J8*88*$*5 *./!/ !.R, R*8$J&A *&$*5!/ !*./ .R,!5 !R8!!*8$J8$!**5!3 !*./!$ .!R,!/ R!&A$J*8 *$&*5 *./!/ !.!!R, !R*98$K8*$*6 *!9.0 !.R- R$K*9&B $*&&B!9*6 !&*&B.0 .&R- R&B*9!98$K8&$!*!1&B*6 &!*&B.0!9 .!&R-!9&B R&!&B*9$K *&$*6 *!9&B.0 !&.!7&BR- !R&$K8*98*$*6 *.0!9 .!R- R*9$K&B &*$&B!9*6 &!*.0&B .&R- R$K&B*98!98*!$&*6!1&B &!*.0!9&B .!&!9&BR- R!&$K*9&B *$&*6 *&B!9.0 &.!&B!7R- &R!*D$ZED88*$E*DE1 *E.B!BE< .E!E1R? ERED$Z*D&O $E*&!B*DE1 E!*.BE< E.E1R?!D E!R$Z8!3ED*D8*!E$E1!D*D !*E!6E<.B E.!!BE1R? ER!*D$Z&OED $E*&E1*D E*!B.BE< !E.E1!3R? RE!8E6&J$U96*@8E9$*&`x@]HKJ<[C @88?$R*=E=&H &*E$*=E+ E*!:.:E5 .!E!.R8E+ E!R8*0$A8*$*- *.(!( .!R% R*0&8$A *&$*-!( !*.( .!-R% R!!8$A*08$!**-!+ *!!.( !.R%!( !R&8$A*0 &$**- *!(.( !.!R% !R8$9*08*$*- *!(.$ !.R$ R&1$=*0 $&*!(*- !*.' .!-R! R!!*0$A88!*$!+*- !*!." .!!(R% !R&8*0$9 $*&*- *!(.$ .!!R$ !R$A*09088*9$*- *!( !R% R$A&8*0 $&**-!( !*.( .!-R% R!*0$A8!8$*!*-!+ *!.(! !.R%!( !R$A&8*0 &*$*- *!(.( .!!R% R!90*08$A8$9**- *!( !R% R*0$A&8 $&**-!( *!.( .R%!- R!!$A8*08$!*!+*- *!!.( !.!(R% R!&8$A*0 $*&*- *.(!( !.!R% R!8$A*08$**- *!(.( !.R% R*0$A&8 &$*!(*- *!.( .!-R% R!!$A*088*!$!+*- !*.(! !.!(R% R!&8*0$A $&**- *!(.( !.!R% R!8$B*08*$*- *!0.( .!R& R*0$B&9 *$&*-!0&9 !&*.(&9 .&R& R$B&98!0*08&$!**-!*&9 *!&&9!0.( &.!!0R&&9 !&R$B&9*0 $&**- *!0&9.( &!.!.R&&9 &!R90$A*088$9**- *!( !R% R&8$A*0 &$*!(*- *!.( .!-R% R!8*0$A!8!*$!+*- *!!.( .!R%!( R!*0&8$A *$&*- *.(!( !.R%! R!8*0$98$**- *.$!( .!R$ R&1*0$= &*$*-!( *!.' .R!!- !R8$A*0!8$!*!+*- !*!." !.R%!( !R&8*0$9 $*&*- *!(.$ .!R$! !R$A8*08*$*- *.(!( !.R% R$A&8*0 *&$*-!( *!.( .R%!- !R!8*0$A8!*$*-!+ *!!.( !.!(R% R!*0&8$A *&$*- *.(!( .!!R% !R8$9*08*$*- *!(.$ !.R$ R&1$=*0 *$&!(*- *!.' .!-R! R!*08!$A8!$*!+*- !*."! .!!(R% R!&8$9*0 &$**- *.$!( .!!R$ !R$A8*08*$*- *.(!( .!R% R$A*0&8 $*&!(*- !*.( .R%!- !R$A*08!8$*!!+*- !*.(! !.R%!( R!&8$A*0 $&**- *.(!( .!R%! !R*0$988*$*- *!(.$ !.R$ R&1$=*0 &$**-!( !*.' .R!!- !R*08!$A8*$!*-!+ *!!." !.R%!( !R&8*0$9 &*$*- *!(.$ .!R$! R!8*0$A8$**- *.(!( .!R% R&8*0$A &*$!(*- *!.( .!-R% R!8*0!$A8*!$!+*- *!.(! !.R%!( !R*0&8$A $*&*- *!(.( !.R%! R!$9*088*$*- *.$!( !.R$ R$=*0&1 *$&!(*- *!.' .R!!- R!*0!$A88*!$*-!+ *!."! !.R%!( R!&8*0$9 &*$*- *!(.$ .!!R$ R!*08$A8*$*- *.(!( !.R% R$A*0&8 $*&!(*- *!.( .!-R% R!8*0!$A8!$**-!+ !*.(! !.R%!( !R$A&8*0 *$&*- *.(!( !.!R% R!*:E:8$O8E*$*:E) *E.7E3!7 .E!R5E) RE&D*:$OE: E*&$*:!7E) E*!.7E3 .E!:E)R5 R!EE:$O!+8*:8$*E!*:E)!: *!E.7E3!. !E.!7R5E) !REE:$O*:&D &$*E*:E) E*E3.7!7 E.!R5E)!+ !ER8$D92*28$*9*/ *!* !R' R$D&;*2 $&*!**/ *!.* .R'!/ R!8$G*5!8*$!!0*2 !*.,!" .!R*!, R!&>*5$G &*$*2 *.,!, !.R*! R!$;8*28$**/ *.%!* .!R% R&4*2$@ $&**/!* !*.) .!/R" !R8$A*0!8!$**-!+ *!."! .!!(R% !R$9*0&8 $&**- *.$!( .!R$! R!*0$A88*$*- *!(.( !.R% R*0$A&8 &$*!(*- !*.( .!-R% R!*0!8$A8*$!*-!+ *!.(! .!R%!( R!&8*0$A $&**- *!(.( .!R%! !R$E*388*$*0 *!3.+ !.R( R$E&<*3 &$*!3*0&< *&!.+&< .&R( R!6&?$H8*68&!*$&?!.*2 &!*.-&?!6 .&!!6&?R* R!&$H&?*6 &$**2 *&?!6.- .&!&?R*!4 R&!8$D*29289$**/ *!* !R' R$D*2&; $&**/!* !*.* .R'!/ !Rx @J<!8HK[@]C*0$A8*$!*-!+ !*!.( !.!(R% R!$A&8*0 &$**- *.(!( !.!R% !R$98*08*$*- *.$!( .!R$ R$=*0&1 &$*!(*- !*.' .!-R! R!*0$A8!8$*!*-!+ !*."! !.R%!( !R$9*0&8 $*&*- *!(.$ .!!R$ R!$A8*08*$*- *!(.( .!R% R*0&8$A $*&!(*- !*.( .!-R% R!!$A8*08!$*!+*- !*!.( !.!(R% R!&8*0$A &*$*- *!(.( .!R%! !R*0$988*$*- *!(.$ !.R$ R*0$=&1 *&$!(*- *!.' .R!!- R!$A!8*08*!$*-!+ !*!." !.R%!( !R$9&8*0 *$&*- *!(.$ .!!R$ R!E:$O*:88*$EE)*: E*E3.7!7 !E.R5E) ER&D*:E:$O $E&*E)!7*: !*E.7E3 E.R5E)!: !RE*:8!+$OE:8$E*!!:*:E) *!E.7E3!. !E.R5!7E) R!E*:&DE:$O $E&*E)*: *EE3!7.7 !.EE)R5!+ !ER8$9*08$**- *.$!( !.R$ R&1*0$= &$*!(*- *!.' .!-R! R!!$A*088$!**-!+ !*!." !.!(R% R!$9*0&8 $*&*- *!(.$ !.R$! !R$A8*08*$*- *!(.( .!R% R$A*0&8 &*$!(*- *!.( .R%!- !R*08$A!8$*!!+*- *!.(! !.R%!( R!*0&8$A &*$*- *!(.( .!R%! R!*0$B88*$*- *!0.( .!R& R*0$B&9 &*$*-!0&9 &!*.(&9 .&R& R8!0$B*0&98!*&$!*&9*- !*&!0.(&9 !&.&9R&!0 R&!*0&9$B &$**- *!0&9.( &.!!.&9R& R!&*0$A9088*9$*- *!( !R% R&8*0$A *&$*-!( !*.( .!-R% R!*08$A!8*!$!+*- *!.(! !.R%!( R!$A*0&8 $*&*- *.(!( .!R%! !R8E=$R*=8*$EE+*= *E!:E5.: E.!R8E+ RE$RE=*=&H *$&E!:*=E+ !*EE5.: E.E+!=R8 !ER*@E@!0$V88$E*!!@*@E. E!*E8!3.> !.ER;E.!> !RE&KE@$V*@ *$&EE.*@ *EE8!>.> .E!E.R;!0 ER!8*DE9$Y99&N8*E9$&`]J<[Cx @8@HK8?*D&O$ZED &$*E*DE1 E*!B.BE< E.!R?!3E1 !RE*5$>88$**2 *.(!, .!R( R&6$B*5 *$&!,*2 !*., .R$!2 !R*28!$D8*!$*/!. !*! .$ .!!*R' !R*2&;$; *$&*/ *!*.% .!R%! R!8$A*08*$*- *.(!( .!R% R$A*0&8 *&$!(*- !*.( .!-R% R!$A8!*08*!$!+*- !*.(! !.R%!( R!*0$A&8 $*&*- *!(.( .!R%! R!$98*08$**- *!(.$ !.R$ R*0&1$= &*$!(*- !*.' .!-R! R!!*08$A8$*!!+*- *!!." .!!(R% !R$9*0&8 *$&*- *!(.$ .!R$! !R*08$A8*$*- *!(.( !.R% R$A*0&8 *&$!(*- *!.( .R%!- R!*0$A8!8!*$!+*- *!.(! !.R%!( !R$A*0&8 $&**- *!(.( .!!R% !R8*0$98*$*- *.$!( !.R$ R*0$=&1 &*$!(*- !*.' .!-R! !R8*0!$A8*!$*-!+ *!."! .!R%!( !R&8*0$9 *&$*- *.$!( .!!R$ R!$A8*08$**- *.(!( !.R% R*0$A&8 &*$!(*- !*.( .!-R% R!8*0$A!8*!$*-!+ !*.(! !.!(R% !R&8$A*0 $*&*- *!(.( .!!R% R!8$9*08$**- *!(.$ .!R$ R&1*0$= &*$!(*- !*.' .R!!- R!*0!$A88*!$!+*- *!!." !.!(R% R!*0$9&8 *&$*- *.$!( .!R$! !R8$A*08$**- *!(.( !.R% R*0&8$A *$&*-!( !*.( .!-R% R!$A!*088*$!*-!+ !*!.( !.!(R% !R&8$A*0 &*$*- *.(!( !.!R% R!*08$98$**- *.$!( !.R$ R&1$=*0 *&$*-!( *!.' .!-R! !R!$A*088!*$!+*- *!."! !.R%!( R!&8$9*0 &*$*- *!(.$ !.!R$ !R*0$A88*$*- *.(!( !.R% R*0&8$A *$&*-!( *!.( .R%!- !R*08!$A8$!*!+*- !*.(! !.!(R% !R&8$A*0 $&**- *!(.( .!!R% !R$9*088*$*- *!(.$ !.R$ R&1*0$= *&$*-!( *!.' .R!!- !R*0!8$A8$!*!+*- *!!." .!!(R% !R$9&8*0 &$**- *!(.$ .!R$! !R$A8*08$**- *!(.( !.R% R$A&8*0 &*$*-!( !*.( .R%!- R!*08$A!8$!*!+*- !*!.( .!!(R% !R*0$A&8 *&$*- *!(.( .!!R% !R*08$98$**- *.$!( !.R$ R$=*0&1 &$**-!( *!.' .R!!- R!*08$A!8*$!!+*- !*."! .!!(R% R!&8*0$9 *&$*- *!(.$ !.R$! R!$A*088$**- *!(.( !.R% R*0$A&8 *$&!(*- !*.( .R%!- !R*0!$A88!$*!+*- *!.(! .!!(R% R!*0&8$A *&$*- *!(.( !.!R% !R$98*08*$*- *!(.$ .!R$ R$=&1*0 $*&!(*- !*.' .!-R! !R*0$A8!8$*!!+*- *!."! .!R%!( !R*0$9&8 $&**- *.$!( .!!R$ R!$98*08$**- *!(.$ !.R$ R$=&1*0 $*&!(*- *!.' .R!!- !R$A*0!88!$**-!+ !*."! .!R%!( R!$9&8*0 $*&*- *.$!( !.R$! !R$98*08*$*- *.$!( !.R$ R$=*0&1 &$**-!( *!.' .R!!- R!!8*0$A8$*!*-!+ !*!." .!R%!( R!&8$9*0 &*$*- *!(.$ !.R$! R!*0$A88*$*- *!(.( .!R% R*0&8$A *$&!(*- !*.( .!-R% R!$A8!*08*!$*-!+ *!!.( .!!(R% !R&8*0$A *&$*- *!(.( .!!R% !R*08$98*$*- *!(.$ !.R$ R$=&1*0 &$**-!( *!.' .R!!- !R$A*08!8!*$*-!+ !*."! !.R%!( R!$9&8*0 &$**- *.$!( !.!R$ R!*08$98*$*- *!(.$ !.R$ R*0&1$= &$**-!( !*.' .!-R! !R*0$A8!8!*$*-!+ !*!." .!R%!( !R*0&8$9 *$&*- *!(.$ .!R$! R!$A8*08*$*- *.(!( !.R% R&8$A*0 *&$*-!( *!.( .R%!- R!8!$A*08!$*!+*- !*.(! .!!(R% R!$A&8*0 &$**- *!(.( !.R%! !R$98*08$**- *.$!( !.R$ R&1$=*0 *&$!(*- !*.' .!-R! R!!8*0$A8$*!*-!+ !*."! .!!(R% R!$9&8*0 $&**- *.$!( .!!R$ !R$A*088*$*- *!(.( .!R% R*0$A&8 *&$*-!( *!.( .!-R% !R8$A*0!8$!**-!+ !*!.( !.R%!( !R*0$A&8 *&$*- *.(!( .!!R% R!8*0$98*$*- *.$!( .!R$ R$=&1*0 $*&!(*- *!.' .R!!- R!8$A*0!8*!$!+*- *!!." !.!(R% !R$9&8*0 $*&*- *!(.$ .!R$! !R8*0$A8*$*- *.(!( !.R% R&8*0$A &*$!(*- *!.( .R%!- R!8*0!$A8*!$!+*- !*.(! !.R%!( R!*0&8$A *$&*- *.(!( .!R%! R!*0$988*$*- *!(.$ .!R$ R$=*0&1 $&*!(*- !*.' .!-R! R!8!$A*08*$!!+*- !*."! !.!(R% !R*0&8$9 &*$*- *!(.$ .!!R$ !R$A8*08$**- *!(.( !.R% R&8$A*0 $&*!(*- !*.( .R%!- !R$A*08!8!*$!+*- *!.(! !.!(R% !R&8$A*0 $&**- *!(.( .!R%! !R*0$988*$*- *.$!( .!R$ R&1*0$= *$&!(*- !*.' .!-R! R!8*0!$A8!$**-!+ *!."! .!!(R% R!&8*0$9 *&$*- *!(.$ .!!R$ R!8$A90*089*$*- *!( !R% R*0$A&8 *$&*-!( *!.( .R%!- R!!*0$A88!$**-!+ *!.(! !.R%!( !R$A*0&8 *$&*- *.(!( .!!R% R!928*2$D8$9**/ *!* !R' R$D&;*2 $*&*/!* *!.* .R'!/ !R8*5!$G8*!$*2!0 *!.,!" .!!,R* R!$G*5&> *$&*2 *.,!, !.!R* !R*8$J88*$*5 *!/./ !.R, R*8$J&A &*$!/*5 *!./ .R,!5 !R*88!!$J8$!**5!3 !*!$./ .!R,!/ !R&A*8$J $*&*5 *!/./ .!R,!! !R*D$ZED88$*E*DE1 *EE<!B.B E.!R?E1 RE&OED$Z*D $*&E!BE1*D E!*E<.B .E!DE1R? R!E!3*DED8$Z8!$*E*DE1!D !*E!6.BE< E.!!BR?E1 RE!&O$ZED*D *E$&*DE1 *E.B!BE< .E!R?!3E1 ER!96*6$6*,9,$,9*$$*9/MTrk JAZ2J<YYx [@]@HKE)}C Vibraphone}xp>)E}xsnp>@)}xsnp}E)@xsnidp>)}Exsnp}@)>xsnp}@E)xp}H%Exp}L"HL"}LJ"L}J"}JJ2"}xp("}2xp}-"(-"}--}("xp<"(}<@"}@-"}xp}--"-"}-xsp}2"-}22"}2("xp>"(}>}>")"}>xp})-"xsnp-+%}xp+/%}xp/}9"xp4%}94)}4}-%4xp2"}-xp("}2xsp(}("-"}(xp2"}-}2"22-"}xp}("-(}("}(E"xsnid_ZUPpEE"}xp}E"EE"E}ED"}}DE"EE"}E"E}E}@"xp@}G"xspG"G}E"G}E}C"}C>"}>>"}>">xsp}>">>@"}A"}@A;"}xpG"};xpN"}GP"N}P}H"}HI"I}@"xp}@N"xp-"}N-"}--"-}}-"-xsp2"}-}22"}("2xp>"(}}>">)">}xp)-"}xsnidp-1)}xp/)}1xp}/1|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML01/MTrk JAZ2YYx h[@]@HKJ<}Echo}CE)`}EB)@B@)`}>)@xsnp@)>}xs0@}B)@}E)BxsniP}B)E@B@)`}>)@xsnp>@)}xs0}@B)@B}E)xpH%E}@HI"}@L"I}L}L"J"L}JJ"}2"}J`}/"2`-"/`,"-`,}("@*"}(@*}-"}-"-@*"-@}("*xp@"(}@"}@-"}@xp}--"-"}-x0-}/"@2"}/2"2}`2/"`-"/`,"-`,}("xp>"}(}>">)"}>@})*"@*-"}xsnp+%-}xp+/%}xp}/9"@}6"9@}4%6}4)4-%4}@/"-}@}2"/xp}2("xsp(("}@(*"@}-"*@/"}-@}/2"2"}2@2/"@-"}/xp}("-("}(}E"(xsnid_ZUPp}EE"xpE"E}}E"EE"E}EE"}E"}E}EE"@EB"@}B@"xp}G"@xsp}G"GG}C"C"}C>"C}}>>">>"}xsp>}>"}A">A"}A@?"A`?="`}=;"xp};G"`I"}G`IK"`L"K`}P"L}M"PI"}MG"I}@E"G`ED"`@"D}xp@N"}xp-"N}--"}-"}-}-"-x0}-/"@2"/}}22"`2/"`/-"`,"-`},("xp>"(}}>">}>)"@}*")@}*-"xsni-/)}`}/1)xp1/)}xp}/1|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML01/MTrk[NJAZ2WhistleYYx "[@]@HKE)}J<C}@)`}<)9)E@@}`A)>)}<9xsnpD)>A}@)xsnpD9)}@<)xsniP}@)E)9<@}`A)@}E>)xsnp@)>}AD)xsnp9)}<)@Dxs09"="}<9@9=>"}9"}E"9@">>"A"}@ExsAA"}>">`@"E">}Axp}A"@E>"@B"}>">A@D"@"}>Bxsnp9"@D}<"xsnidpA"9<}>" A">}>"A`A>@"E"}xp}A"E@>"xs0B">"A>}@>}9"B<"xsnp}C%G%9<xsnpA"}GCE"@E}AB"A"@@%AD%}Bxp}<%@9%Dxp}A">"<9xp}D"@">Axsnp<"D9"}@xpC"<9}G">"A"}GC@}@}E"A">AxpD"@"}EAxp9"<"}D@xsnid_ZUPKFpA">"}9<<"}A9">}@"D"<9D9"<"}@<9A">"}@>"B"A}>@D"@">B}xsnid_Zp>"DA"@}xsnip}>A@"D"}E"@DA"EA}A">"@}?">">A`>A"}>"?`>D"@"}Axp@}C"G"DxpG};"@"C="}?";@9";"=}?9=";9"}@}`}`9=="}9"xp=B"}9E"xspB9"="}Exsnip=A">"9} >"}>AA"`E"A>@"}xp>"@A"}Exs0>">AB"}@}9"=">Bxsnidp@)}=)=9xpB)G)}@=xpBG}=|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0=/MTrkNJAZ29)YYx ^[@]@HKJ<}}EchoC@)xp9>)9)}@xsnid_0};)>@)9@}9)@);@xsnidp}9)>)@9xsnid_0};)@)9>@@)}@;9)xsnidp9"}<"9@9}9"<>"xP}A">99"`}`A}9>"9"`9"@"}>9xs0}9"@>"9Pxsp>9}9"@"@";"}9@@"9"};@xsnidp}<"9"9@9<}9">"`A">9"}9`}`>"}9"9A`9"}@"9>xp@9<"}9"}<9"9>"xsp9>9"@"}xsnp>%9}@7%xp9%>%7>}xp9}<"7">@<"A"}7<@};%A<@%@};>);)@%>}9%;xp9@}9">"xp@"9}>;"xsp;"@>";}@@";"}>;@;@}@"9"xp@;">"}9}>;9">"@>A"}99"@7"}9<"Axp@"}7;"<;"@}>";>9"@"};xsnid_ZUPKFA<72-0>"99"@}@>@"};"9xp@;};"8"xsp;"}89";xpC"9;<"}9">"<}Cxsnip};"98">;8}@"9"@9>"9"}@}`9"9>="}`8";"}=9xp}9";>"8`;">"}>9`}>>"?";`>";"}>?`;;"}B">}="8"B;}9"@"8= 8"@"9}@`}9"@"8@xp="B"}9@xspB@"9"}=xsnip@9}<"9">"}9"9<`A">}9"9`}`9"}9A>"`9>}@"9"xp}@9<"9"9"9>"}<xsp@"}9">9xsnidp8)}9@=)xpB)}=;)8xp;=}B|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0=/MTrkCJAZ2yxr(Y @ March Winds/simutrans-124.5/simutrans/music/28-Road-to-Warm-Places.mid000066400000000000000000001101461520675460500232740ustar00rootroot00000000000000MThdxMTrkCopyright (C) 2007 shunter~ JAZ2 2006.10.06Road to Warm Places~j' .' (. ~j  ''  .' ~j ..' . ' .'~j !.  '  . ~j'      ~j !     vj     j'njXQY/MTrk&JAZ2]General MIDI Bassf{[@ @`$e<$$c4$$h9$$j#$Z$Z$ $g$"$f1$ \;$i<b$$q1$ $e0$ $e<$$c4$$h9$$j#$Z$Z$ $g$"$f1$ \;$i<b$$q1$ "e0" #e<##c4##h9##j##Z!Z! #g#"#f1# \;#i<b##q1# ,e0, b<+`4+e9+g#++h++d(+!a!!b!!f!!d8!!g!!C!&e<&2a22k2&d9&2d12 &c3& 2a92&f1& 2`<&h24&2`62&h0& 2_22k2&i,&2a62&j.&&c+&!f<-b!3- !e9!-e1- !d3! -b9-!g1! -a<-!i4!-a6-!i0! -`8-!j,!-b6-!k.!-d+-#e<#*`*/k/#d9#/d1/ #c3# /a9/#f1# /`<#h/4#/`6/#h0#*/k/#i,#/a6/#j.##c+#*f<6b*36 *e9*6e16 *d3* 6b96*g1* %a:%c<+a4+f9+h#+!^<-\!--m-!a9!-c--m-&f<&2b32 &e9&2e12 &d3& 2b92&g1& 2a<&i24&2a62&i0& 2`82&j,&2b62&k.&2d+2!e!b!!!f!!d8!!g!!q!&e<-`&-2k2&d9&2d12 &c3& 2a92&f1& 2`<&h24&2`62&h0&*2k2&i,&2a62&j.&&c+&!e2&282)2Q2 2P2 2B!22F2'2G2)2U2#-J--Z--A-&-5-'-P-!-.-'-8-&-M-$-@- -D--R-$->-&-8-)-g-)-B!--F-'-G-)-U-#/Q/)/I/./J//J//M//I//P/)/0/+/K/,/D/-/R/,/G/+/W/-/K//H//E/)/I/)/U/,/2/)*Q*)*I*.*J*.*M**I**P*)*?*+*K*,*/*-+Q+)+I+.+J+.+M++I+-J-$-A-&-5-'-0--6-2J2,2A2,2R2+2Q 22U22S2'2L2)2P2+2N2)2G2,2Y2)2E2*2n2&2,2.2D2,2\2)212,-J--Z--A-&-5-'-P-!-.-'-8-&-M-$-@- -D--R-$->-&-8-)-g-)-B!--F-'-G-)-3-#/J/,/A/,/R/+/Q //U//S/'/L/)/P/+/N/)/G/,/Y/)/E/*/n/&/,/./D/,/\/)/1/,*Q*)*I*.*J*.*M**I**P*)*?*+*K*,*/*-+Q+)+I+.+J+.+M++I+-S-,-A-,-R- -Z-$-U-/J//Z//A/&/5/'/P/!/./'/8/&/M/$/@/)/R/$/>/&/8/)/g/)/B!//F/'/G/)/U//Z/ +J+$+A+&+5+'+P+!+.+'+8+&+M+$+'+)-J-$-A-&-5-'-P-!-.-'-8-&-M-$--- -,-*J**Z**A*&*5*'*P*!*.*'*8*&*M*$*@*)*R*$*>*&*8*)*g*)*B!**F*'*G*)*3**6* +Q+)+I+.+J+.+M++I++P+)+?+++K+,+D+--S-,-A-,-R- -Z-$-U-.J.$.A.&.5.'.P.!/J/$/A/&/5/'/P/!/./'/8/&/M/$/'/)/Q/)/I/./J/./M//I//`/$/-////-J-$-)-&+Q+)+I+.+J+.+M++I++P+)+?+++K+,+D+--J-$-A-&-5-'-P--Z-J--$-K-&+;+$+1+&*J**Z**A*&*5*'*P*!*.*'*8*&*M*$*9*)*R*$*>*&*8*)*Q* *P* *B!**F*'*G*)*3*#+J+$+A+&+5+'+P+!+.+'+8+&+M+$+-+ +,+-S-,-A-,-R- -Z-$-U--S-'-S-)-P-+-N-)2J2,2A2,2R2+2Q 22U22S2'2L2)2P2+2N2)2G2,2Y2)2E2*2n2&2,2.2D2,2\2)2S2,-J--Z--A-&-5-'-P-!-.-'-8-&-M-$-@-)-R-$->-&-8-)-g-)-B!--F-'-G-)-3--6- /J//Z//A/&/5/'/P/!/./'/8/&/M/$/@/ /D//R/$/>/&/8/)/g/)/B!//F/'/G/)/3/#*S*,*A*,*R* *Z*$*U**S*'*S*)*P*+*.*)+J+$+A+&+5+'+P++Z+-J-$-A-&-5-'-P-!2J22Z22A2&252'2P2!2.2'282&2M2$2@2 2D22R2$2>2&282)2g2)2B!22F2'2G2)2U2#-J--Z--A-&-5-'-P-!-.-'-8-&-M-$-@- -D--R-$->-&-8-)-g-)-B!--F-'-G-)-3-#/J/,/A/,/R/+/Q //U//S/'/L/)/P/+/N/)/G/,/Y/)/E/*/n/&/,/./D/,/\/)/S/,*S*,*A*,*R* *Z*$*U**S*'*S*)*P*+*N*)+J+$+A+&+5+'+0+!-S-,-A-,-R- -Z-$-U-/J/,/A/,/R/+/Q //U//S/'/L/)/P/+/N/)/G/,/Y/)/E/*/n/&/,/./D/,/\/)/1/,+J+$+A+&+5+'+P+!+.+'+8+&+M+$+9+)-J-$-A-&-5-'-P-!-.-'-8-&-M-$-9-)*J*,*A*,*R*+*Q **U**S*'*L*)*P*+*N*)*G*,*Y*)*E***n*&*,*.*D*,*\*)*S*,+J+$+A+&+5+'+P+!+.+'+8+&+M+$+'+)-J-$-A-&-5-'-P-!.J.$.A.&.5.'.0.!/J/$/A/&/5/'/P/!/./'/8/&/M/$/9/)/J/$/A/&/5/'/P//Z//;/$/S/&-J-$-)-&+Q+)+I+.+J+.+M++I++P+)+?+++K+,+/+--J-$-A-&-5-'-P-!-J-$---&+;+$+1+&*J*$*A*&*5*'*P*!*.*'*8*&*M*$*9*)*R*$*>*&*8*)*Q* *P* *B!**F*'*G*)*U*#+J+$+A+&+5+'+P+!+.+'+8+&+M+$+-+ +,+-J-$-A-&-5-'-P-!-.-'-8-&-M-$--- -,-2J2$2A2&252'2P2!2.2'282&2M2$292)2R2$2>2&282)2Q2 2P2 2B!22F2'2G2)2U2#-J--Z--A-&-5-'-P-!-.-'-8-&-M-$-@- -D--R-$->-&-8-)-g-)-B!--F-'-G-)-3-#/J//Z//A/&/5/'/P/!/./'/8/&/M/$/@/)/R/$/>/&/8/)/g/)/B!//F/'/G/)/U//Z/ *J*$*A*&*5*'*P*!*.*'*8*&*M*$*'*)+J+$+A+&+5+'+P++Z-S+-,-A-,-R- -Z-$-3-2J22Z22A2&252'2P2!2.2'282&2M2$2@2)2R2$2>2&282)2g2)2B!22F2'2G2)2U22Z2 -Q-)-I-.-J--J--M--I--P-)-0-+-K-,-D---R-,-G-+-W---K--H--E-)-I-)-U-,-2-)/J//Z//A/&/5/'/P/!/./'/8/&/M/$/@/ /D//R/$/>/&/8/)/g/)/B!//F/'/G/)/U/#*Q*)*I*.*J*.*M**I**P*)*?*+*K*,*/*-+Q+)+I+.+J+.+.++++-J-$-A-&-5-'-P-!2S2,2A2,2R2 2Z2$2U22S2'2S2)2P2+2N2 @/MTrk ZJAZ2{QGeneral MIDI  Polysynth @H[2]@ZGZGVGZGVG[GZGZGZGZGZGLRG"NVLwORNvQUOPQQT-VU QV URxSM UlQSSQQ\]Q VX3UUVzSL UlQTSQQYhS[ QSQSzLPQhNS L$NNODN!NPfNNO=OTNkOQTiQQN Q*QX$VWQwUSVtSLUoSQSQQY%VUQuUQ VkQQUpSSQS SezS(SUzQTScLPQtNRLNNUtNGTxJJGqJNOyLLN.NMLQPNtOJQqNL OoLON-NSLELNyIMEoNFItLK N(NO L N QSzOOQfNMOoNLOfJML7LCJ9NQLNNO2OPN=NQO$LNNiNNLqJKNJJM*J LM)LLM"LIHxNSIN NV4OPN;NR ONLNyNMLuJJN!JJM0JGK>LRG"NVLwORNvQUOPQQT-VU QV URxSM UlQSSQQ\]Q VX3VUUzSL UlQTSQQYhS[ QSQSzLPQhNS L$NNODN!NPfNNO=OTNkOQTiQQN Q*QX$VWQwUSVtSLUoSQSQQY%VUQuUQ VkQQUpSSQS SezS(SUzQTScLPQtNRLNNUtNGTxJJGqJNOyLLN.NMLQPNtOJQqNL OoLON-NSLELNyIMEoNFItLK N(NO L N QSzOOQfNMOoNLOfJML7LCJ9NQLNNO2OPN=NQO$LNNiNNLqJKNJJM*J LM)LLM"LIHxNSIN NV4OPN;NR ONLNyNMLuJJN!JJM0JGK>LRG"NVLwORNvQUOPQQT-VU QV URxSM UlQSSQQ\]Q VX3UUVzSL UlQTSQQYhS[ QSQSzLPQhNS L$NNODN!NPfNNO=OTNkOQTiQQN Q*QX$VWQwUSVtSLUoSQSQQY%VUQuUQ VkQQUpSSQS SezS(SUzQTScLPQtNRLNNUtN/MTrk JAZ2 @gGeneral MIDIDistortion guitar  @{]@[$9Z2\m92D9]2`V29 2U9U29!9U2U 29)2^9_629B2Y9Y292 4R-SR-4b-c4^-44\-_ 4-#-W4R-4#-W4S-4)4_-a2-4 6W/M`6/L/e6`/6"6R/[^/6 1W*Ym1*D1Z*]V1*V2U+[^2+24+92+Z-Y4Y4-%4Y-[o-42\9Z392B2`9]V92 9U2U29!9U2U 29)9_2^692B2Y9YY92-M4W&4-J4`-e4-"4R-[ -46]/c\/6/^6Z>/6f/\6Z 6/6S/[6/6Q/S6/*^1X\*11U*Y>1*n2U+[+2+`2W+2[4Y-Y4-#-Y4Y4-%4Y-[+-44Y-Y 4- -Y4Y -4 /c6]\/66Z/^>6/f6Z/\ 6/6S/[6//S6Q6/U2Y+Y2+24\-W+-4*\1Z31*B*`1]V1* *U1U*1!*U1U *1)*^1_61*B1Y*Y21* +^2X+2+Y2U>+2n4U-[^-4-`4W4-Z4Y.Y4.%4Y.[o.46X/^\/6/Y6U>6/n6U/[H6/(/\6V6/-\4Ve4-2X+^\+22U+Y>+2n-i4c-44T-K-4(4S-Yd4--7474 -+Y2S*2++\2\2 +1\*b\*1*]1Y>1*f*[1Y *1*Z1R1**R1P1*!1]*_j*1 2V+L&2+J2_+df+2>4T-^ 4-%4S-U-4#4Y-T4-'4`-c-4W9\2b\292]9Y>92f2[9Y 292Z9R929P2R92!2_9]j29 4\-b\4--]4Y>4-f-[4Y -4-Z4R4--R4P4-!4]-_j-4 6Z/\36/B/`6]V6/ /U6U/6!6U/U /6)6_/^66/B/Y6YY6/1X*^\1*1U*Y>*1n2U+[^2++`2W2+4Y-Y4-#-Y4Y4-%-[4Y+-4-545 4-+2M9W&92J2e9`92"9R2[ 29-S4R4-`-c4^-4-_4\ 4-#4R-W-4#-W4S4-)4_-au-4/S6R6/`6^/c6/6\/_ 6/#6R/W/6#6S/W/6)6_/au/61V*\1*1_*_1*#*W1S>1*n2U+[+22W+`+2[4Y-Y4-#-Y4Y4-%4Y-[+-445-5 4-o2b9\292]9Y>92f9Y2[ 929R2Z299P2R92!2_9]j29 -S4R-4`4^-c-44\-_ 4-#4R-W-4#-W4S-4)4_-au-4/b6\\/6/]6Y>6/f/[6Y /6/Z6R6//R6P6/!/_6]j/6 *\1V1*1_*_1*#1S*W>1*n2U+[+22W+`+2[-Y4Y4-#4Y-Y4-%-[4Y+-44Y-Y 4- -Y4Y -4 9W2M&92J2e9`92"2[9R^29 -\4Zm4-D4]-`V4- 4U-U-4!-U4U -4)-^4_64-B4Y-YY4-6\/b\/6/]6Y>/6f6Y/[ /66R/Z6//R6P6/!/_6]j/6E1Y*Y^*1 2U+[^+2+`2W2+Z-Y4Y4-%4Y-[04-#2W9P92$2a9] 29@/MTrk7JAZ2Road to Warm Places/simutrans-124.5/simutrans/music/29-Runaway.mid000066400000000000000000000662011520675460500213070ustar00rootroot00000000000000MThdMTrk By Copyright 2007 All Rights Reserved Generated by NoteWorthy ComposerQ YQ ⃤Q Q QR`QR Q 'Q '/MTrk !Staffd @7n^77n^7>n^>7n^77n^7Fn^F7n^77n^7>n^>7n^77n^77n^7>n > @n @ 7n^77n^7>n^>7n^77n^7Fn^F7n^77n^7>n^>7n^77n^77n^77n 7 5n 5 7n^77n^7>n^>7n^77n^7Fn^F7n^77n^7>n^>7n^77n^77n^7>n > @n @ 7n^77n^7>n^>7n^77n^7Fn^F7n^77n^7>n^>7n^77n^77n^77n 7 5n 5 7n^77n^7:n : 7n^77n^7:n^:n^>n^>>n^>6n^6>n^>>n^>6n^6>n^>>n^>>n^>>n^>n^>n^>>n^>6n^6>n^>>n^>6n^6>n^>>n^>>n^>>n^>n^>n^>>n^>6n^6>n^>>n^>6n^6>n^>>n^>>n^>>n^>n^>n^>>n^>6n^6>n^>>n^>6n^6>n^>>n^>>n^>>n^>n^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^26>B2n6n>n^26>B2n6n>n^26>B2n6n>n^26>2n6n>n^26>n^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^26>B2n6n>n^26>B2n6n>n^26>B2n6n>n^26>2n6n>n^26>n^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^26>B2n6n>n^26>B2n6n>n^26>B2n6n>n^26>2n6n>n^26>n^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^26>B2n6n>n^26>B2n6n>n^26>B2n6n>n^26>2n6n>n^26>n^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^26>B2n6n>n^26>B2n6n>n^26>B2n6n>n^26>2n6n>n^26>n^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nn^7:>B7n:n>n^7:>B7n:n>n^7:>B7n:n>n^7:>5n:n>n 5:> 5n:nFE`>0>FAF0> A0AF`>F0A`>F>0>F>9F`AF09`@FA`AF0@`A>F>=F`>F?F=0@F?>`>F0@`@>F0<`>0>F>pHF`H0HF`HEFHFE`H0JFHF0H@JFFFFF`F0FF0HF0HF0@F`EF0@E0>F>AFCF0ACAF0CFA AFC`F0C>@F`@>F@F0>09F@`909F`>F09`>AF0A`EF`FF0E`FFFEF0EEFF0E0>F >`VFPV0RF`R0RF`QF0R`VFQVTF`VF0T`OFVTFOUFT`VF0U`VQF`TF0Q`LFTLOF`PF0O`QFPPQ=F0=>F`>F>CF>F0E0>F>@F>0@0@F0@AF`>F0A`9F>@9PJFJEF`MF0E0MLFJFLJQFpOFQOFOMFO`OF0MQF0O0LFQ@QF0LXFQ@X0XFVF0X`YFVpXFYVF0XV0VF VFV`YF0V0RFYR0QF`PF0Q`QFPpYFQYUF`WFU0XFW`UF0X`UVFpV`JF0LFJ0JFLPJFF`F0FF`EF0F`JFEJHF`JF0H`CFJ`HF0C`IFHJF0I`EF0J0HF0E`@FH@CF`DF0C`EF0D`E>F0>0>F>CF`C0F`>F>@F>0@0@F0@AF`A>F9F>09`JFJEF`MF0E0QF0MPOF0Q`OMFMOF0QF0Q0QFO`QOFOFOMFO`OF0M0QFO0LF0Q@QFLQXF@X0XFpX0XF`VF0X`VFV@YFV0XF0Y`XVFPV0VFVFV`YF0V`YRFQFR`PF0Q`QFPpQYFUFY`XF0U`UFXUVFpVEFDFDxEFE0E0EF>FE`>0>FAF0> A0AF`>F0A`@<>F>/MTrk9JAZ2]2General MIDITromboneYYo "[2 @@HKJ<}CHK "[2]2x@J<C9`F/>(A+ >AF A/F/>& AF> >(A.F)>AF F/A/J/PFJAp>+C/:( >:C C/:&>/ :>C C):(>.:>C F/>/C/PCF>p=(@+E/ @E= E/=&@/ =E@ @.=(E)=@E @/E/I/P@EIpE/=(@+ @=E =&@/E/ @E= @.=(E)=E@ E/I/@/PE@IpE$J$M(MJE@J"E M!MJE@JEMMEJ E$J$M$ JEMQ+J+M+0QMJ><905+>95 9/>/5& 59> 9.5(>)5>9 >/A/9/PA9>h><905+>95 5&>/9/ 59> 9.>)5(5>9 >/A/9/P>A9p>(A+F/ >FA >&A/F/ >FA A.F)>(>FA A/F/J/PAFJp@+E/<( @<E <&@/E/ @E< E)>(@.>E@ E/J/@/PE@JpJ$M(E$MJE@M!J"E MJE@MJEEMJ M$E$J$ EJMM+Q+J+0QMJE<@0=+E@= @/E/=& E=@ :(C)>.:>C C/F/>/PC>Fp@+=(E/ @=E @/E/=& E=@ 9.>)5(5>9 9/A/>/P>A9p<+A/9( 9A< 9&)9.59> 9/>/A/PA9>pA+F/>( A>F >&A/F/ >FA F)A.>(>FA J/F/A/PJAFhF<A0>+FA> >&A/F/ FA> F)>(A.>AF J/A/F/PJAFp@(C+H/ H@C @&C/H/ CH@ C.H)@(@CH C/H/L/PLCHp5(9+>/ 95> 5&9/>/ 59> >)5(9.5>9 A/9/>/PA9>pO$R(J$ORJ@O"J R!ROJ@RJORJO Q$L$U$ QLUX+Q+U+0QXU><905+>95 5&>/9/ 95> >)5(9.59> >/A/9/P>A9p:(C/>+ C:> >/C/:& >:C C)>.<(<C> >/C/H/PHC>0<00(+9)+?+C ++90+`-< -`&<0&(-9)-?-C --90-`-< -@J$E$M(MJE@M!E J"MJE@JMEEMJ E$J$M$ EJMQ+J+M+0MJQ0&B2B&2&B2B9@J< "[2xHK]2CX><905+>59 >/5&9/ 95> 5(>)9.5>9 A/9/>/P9>A&B2B2&02&(H<@+C0HC@ @&C/H/ @CH H)C.@(@CH H/C/L/PLCHpF/>(A+ >AF >&F/A/ FA> A.F)>(>FA A/F/J/PAFJ%<0%(-9)-?-C --90-`-< -`%<0%(-9)-?-C --90-`-< -@J$M(E$MJE@E J"M!MJE@JMEEMJ E$J$M$ EJMM+J+Q+0QJMC<<+>0C<> <&>/C/ >C< :(C)>.:C> F/>/C/P>FCp9(<+A/ 9<A A/9&/5( 95> 9/>/5& 59> =(E)@.=E@ E/I/@/PE@Ih><5+90>95 9/5&>/ 9>5 C)>.:(:C> >/C/F/P>FCpE/@+=( @E= @/E/=& @=E =(@.E)=@E E/I/@/PE@I&B2B2B&B2&9]2[2xHK "J<C@X><905+>95 >/5&9/ 95> >)9.5(59> 9/>/A/P9>Ap9+>/5( 9>5 5&>/9/ >95 >)5(9.59> 9/>/A/P>A92&`:(>+C/ :C> C/:&>/ :>C >.:(C):C> >/C/F/PC>Fp=(@+E/ @=E @/E/=& @=E =(E)@.=@E I/@/E/P@EIhE<=+@0E=@ @/E/=& @=E E)@.=(=@E @/I/E/P@EIp>/5(9+ 59> >/9/5& 5>9 5(9.>)59> >/A/9/P>9ApA+>(F/ AF> >&A/F/ FA> F)A.>(>FA A/F/J/PJAFpO$R(J$ROJ@R!J O"ORJ@JORJRO R$J$O$ JORV+R+O+0RVO0%<0%(-9)-?-C --90-`-< -`%<0%(-9)-?-C --90-`-< -@J$M(E$MJE@E M!J"JME@JMEEMJ J$M$E$ JEMM+J+Q+0QJM><905+>95 5&9/>/ >95 5(>)9.59> A/9/>/PA9>p>/5(9+ >95 5&9/>/ 5>9 9.>)5(5>9 >/A/9/P>9Ap5(9+>/ >95 5&9/>/ 5>9 >)9.5(5>9 A/9/>/P>A9pF/>(A+ F>A >&A/F/ >FA F)A.>(>AF A/F/J/PAFJhC<<+>0C<> <&>/C/ C>< E)=(@.=@E I/@/E/PI@Ep5(9+>/ 5>9 5&9/>/ >59 :(>.C):C> >/C/F/P>CF%<0%(-9)-?-C --90-`-< -@M(E$J$MJE@E J"M!JME@EJMEMJ E$J$M$ EJMM+Q+J+0MQJ><905+>59 5&9/>/ >59 >)5(9.59> A/>/9/P>9Ap<(>+C/ C<> >/C/<& C>< :(>.C):C> C/F/>/PC>Fp<+A/9( <9A 9&<905+>59 5&9/>/ >59 E)@.=(=@E I/@/E/P@EIp>/5(9+ 9>5 9/>/5& >95 >.C):(:>C >/C/F/P>FCp@+E/=( @=E @/E/=& =@E @.E)=(=E@ @/E/I/PIE@h><5+90>95 >/5&9/ >59 5(9.>)5>9 9/>/A/P9>Ap:(>+C/ :C> :&>/C/ C:> >.:(C):C> F/C/>/PC>Fp@+E/=( =@E @/E/=& E@= =(@.E)=E@ I/@/E/PIE@pJ$O(E$JOE@E O!J"JOE@JOEOEJ E$J$M$ JEMM+J+Q+0QMJC<>0:+C>: C/>/:& C:> >.:(C):C> F/>/C/P>CFp@+E/=( @E= =&@/E/ =@E @.E)=(=@E @/E/I/P@EIp=(@+E/ @E= E/=&@/ =@E =(E)@.=@E @/E/I/P@IEh><5+90>95 >/5&9/ 9>5 >)5(9.59> >/A/9/P9>ApF/>(A+ F>A A/F/>& >AF >(A.F)>FA F/J/A/PAFJp>+C/:( :C> :&>/C/ C>: >.C):(:C> >/C/F/PCF>pE/=(@+ E=@ E/=&@/ =@E @.E)=(=E@ @/E/I/P@IEp@+=(E/ E=@ =&E/@/ =@E @.=(E)=E@ E/@/I/PIE@pM(J$E$MJE@E M!J"MJE@JEMMEJ J$M$E$ JEMJ+M+Q+0JQM0&B2B02&P@x]2HK9C "J<[2X><5+90>59 >/9/5& 9>5 5(>)9.5>9 >/A/9/P9>Ah><905+>59 >/9/5& >95 9.5(>)59> A/9/>/PA>9h><5+90>95 >/9/5& 59> 9.5(>)59> A/9/>/PA>9pF/>(A+ FA> A/F/>& >AF >(A.F)>AF F/J/A/PFAJp@(C+H/ HC@ @&H/C/ C@H C.@(H)@CH L/C/H/PLHC}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0@/MTrk (!JAZ2[2General MIDIBassYYd @@]2 @HKJ<}C @[2x]2@HKJ<C!"O@")O@)"Q@")M@)+O@+&O@&+Q@+&M@&!M@!(N@(!N@!(M@(!M@!(N@(!N@!(M@(&^P&p!] ! !^ !@!^0!0!^ ! &N@&!M@!&N@&!P@!&N@&!M@!&N@&!P@!"O@")O@)"Q@")M@)!M@!(N@(!N@!(M@(&^P&p!] ! !^ !@!^0!0!^ ! !N@!(M@(+N@+&P@&!O@!(O@(&Q@&!M@!)M@)$N@$&N@&!M@!"N@")PP)p"NP"p)NP)p"N@")M@)"N@")P@)$O@$+O@+$Q@$+M@+&M@&!N@!&N@&!M@!+^P+p&] & (^ (@(^0(0(^ ( &N@&!M@!&N@&!P@!+O@+&O@&+Q@+&M@&+X@++N+O+T+ +[@+P!X@! (N(P(T( ([@(P!X@!!N!O!T! ![@!P!X@! (N(P(T( ([@(P&^P&p!] ! !^ !@!^0!0!^ ! &^&@&^&@&^x @]2HKJ<[2@C!@&!M@!&N@&!P@!&^&@&^&@$N@$+M@+$N@$+P@+"O@")O@)"Q@")M@)!X@!!N!O!T! ![@!P!X@! (N(P(T( ([@(P!X@!!N!O!T! ![@!P!X@! (N(P(T( ([@(P&^P&p!] ! !^ !@!^0!0!^ ! +N@+&M@&+N@+&P@&)O@)$O@$)Q@)$M@$!M@!(N@(!N@!(M@(&N@&!PP!p!NP!p(NP(p&N@&!M@!+N@+&P@&!O@!(O@(!Q@!(M@(&^&@&^&@&^@x[2CJ<! @]2HK@&!M@!&N@&!P@!&O@&!O@!&Q@&!M@!+M@+&N@&+N@+&M@&!N@!(PP(p!NP!p(NP(p!N@!(M@(!N@!(P@(&O@&!O@!&Q@&!M@!"M@")N@)"N@")M@)+^P+p&] & &^ &@&^0&0&^ & !X@!!N!O!T! ![@!P!X@! (N(P(T( ([@(P!X@!!N!O!T! ![@!P!X@! (N(P(T( ([@(P&^P&p!] ! !^ !@!^0!0!^ ! &N@&!M@!&N@&!P@!&O@&!O@!&Q@&!M@!&M@&!N@!&N@&!M@!"N@")PP)p"NP"p)NP)p+N@+&M@&!N@!(P@(&O@&!O@!+Q@+&M@&!X@!!N!O!T! ![@!P!X@! (N(P(T( ([@(P&^P&p!] ! !^ !@!^0!0!^ ! &N@&!M@!&N@&!P@!+O@+&O@&+Q@+&M@&)M@)$N@$)N@)$M@$!N@!(PP(p!NP!p(NP(p&N@&!M@!!N@!(P@(&O@&!O@!+Q@+&M@&!M@!(N@(!N@!(M@(&N@&!M@!&N@&!P@!+O@+&O@&+Q@+&M@&!M@!(N@(!N@!(M@(&^P&p!] ! !^ !@!^0!0!^ ! +N@+&M@&+N@+&P@&!O@!(O@(!Q@!(M@(!M@!(N@(!N@!(M@(&N@&!M@!&N@&!P@!"O@")O@)"Q@")M@)+O@+&O@&+Q@+&M@&!M@!(N@(!N@!(M@(!M@!(N@(!N@!(M@(&^P&p!] ! !^ !@!^0!0!^ ! &^&@&^&@&d &` @]2@x!HK[2J<C&N@&!M@!&N@&!P@!&N@&!M@!&N@&!P@!&N@&!M@!&N@&!P@!"O@")O@)"Q@")M@)$M@$+N@+$N@$+M@+}&T|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0&@/MTrk f=JAZ2General MIDIBrassYYx ^]2 @@HKJ<}[2Cx ^[2]2@HKJ<C=`5).2' 25.`.2'5) 25.`+.'2) .2+`+.'2) +2.X)9-9&1&)-G-;)6&1&-)/&(),-/ &-)@.5)2' .52`5).2' .25X&1)9-9&-)G-;)6&1&)-/&(),-/ )-&@-4)1' 4-1`-)&)' )&-`.2'5) 52.`5).2' 25.`7)04' 407`7)04' 407X29+1.9+2.G.6+12;+2./1,4/-( 41-@.'2)+ +.2`0'+2) 2+0! 5>9@59% 59@9%2929%5 59p9!14 941@-4% 4-@4%-4-4* 4H&1-9)9-&)G-;)6&1-&)/-/&(), -)&`2<>B >2 28>:>2>;2;>22=>>2> 2>>@ >22<>B@= ^CxHK[2J<]22<>B >2 28>:>22;>;>2>>2=2> 2>>@ >22>`2'5). 52.`2'5). 2.5=!49 94=@49% 94@19%919%4 49p419! 194@-4% 4-@4%-4-4* 4p=!49 4=9@49% 94@19%9149% 94p49!1 194@-4% -4@-4%4-4* 4H-9&1)9-)&G)6-;&1)-&/),&(-/ &)-@-')0) 0)-`-')0) 0)-`)'-)& &)-`-1'4) 4-1`-1'4) 41-`1'4)- 14-2<>B >2 28>:>22;>;>2>>2=>2 2>>@ 2>2<>Bx[2]2@HK ^=CJ<`)'&-) -&)`&)'-) &)-2>`-1'4) 14-`4)-1' 4-1`-)&)' )&-`)'-)& )&-X+1.929.+2G.6+12;.+2/2/+(., 2+.`=!49 4=9@9%4 49@19%919%4 49p149! 149@4%- -4@4%-4-4* 4p91!4 941@9%4 49@19%919%4 94p%49! 94%@-4% -4@4%-4-4* 4H-9&1)9)-&G)6&1-;-)&/&(),-/ )-&@)'-)& )&-`-)&)' -&)`.2'5) 5.2`.5)2' .52`&)'-) )&-`+.'2) .2+9=!4 49=@9%4 94@9%19149% 49p9!14 941@4%- -4@4%-4-4* 4H)9-9&1)-&G&1-;)6&)-/),-/&( )&-@0'2)+ 2+0`.'2)+ +.2`1'4)- 14-`1'4)- 4-1`&)'-) )&-`+.'2) +.2`+.'2) +.2`+.'2) 2+.X&1+9-9+&-G-;+6&1+-&/),-/&( &)-@-1'4) 14-`-1'4) -41`2'5). 2.5`2'5). .52`+.'2) +2.`+.'2) .2+X)9-9&1&)-G&1-;)6&-)/-/&(), )-&`2<>B 2> >:28>2>;2;>2>>2=>2 2>>@ 2>>F2CP>20C@[2]2 ^xJ<HK=`5).2' .52`.2'5) .52}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0@/MTrk/.JAZ2HKGeneral MIDIHarpYYx @[AC@]AJ<} @]Ax @@[AHKJ<C. e @ e`b b i%i0d d i%i0d d@V(VV% V/Y'Y ]&] V'V Y'Y]( ]/b&b V(VV% V/Y'Y@ @]&] V'V Y'Y ](] b&bb%b0] ] b%b0] ]`e e i%i0d d@V(VV% V/Y'Y ]&] V'V@ Y'@Y]( ]/b&b V(VV% V/Y'Y ]&] V'V Y'Y ](] b&bg%g0b b`] ] b%b0] ]`e @e j%@j0e e`g g b%b0] ]@[([[% [/^'^ b&b ['[ ^'^b( b/g&g ](]]% ]/a'a d&d@ @]'] a'a d(d i&ib%b0] ]`b b@`'`P['[P['[([' []']PX'XPX'X(U' Ub'bP]']@P@]'](]' ]]']PX'XPX'X(U' UV(VV% V/Y'Y ]&] V'V Y'Y]( ]/b&b V(VV% V/Y'@Y @]&] V'V Y'Y ](] b&b @b @ bbbbb] ][Y[YYYV VV VV@@QVOQOM MM MJ JJ JJ JE EC CA AA>V'A>>V>>>9V(9@7@V75V55 V52 V(2V @@b bbbbb] ][Y[YYYV VV VVHKC @x@J<.]A[A`b%@b0] ]@@@b bbbbb] ][Y[YYYVVV VV@@QVQOOM MM MJ JJ JJ JE EC CA AA>V'A>>V>>>9V(9V@7@57V55 V52 V(2VV`l%l0g g`e e@a'aP]']P]'](]' ]]']PX'XPX'X(U'@Ua'@aP]']P]'](]' ]]']PX'XPX'X(U' UV(VV% V/Y'Y ]&] V'V Y'Y]( ]/b&b V(@VV%@ V/Y'Y ]&] V'V Y'Y ](] b&bg%g0b b`` ` i%i0d d`d d g%g0b b`d d@@b @ bbbbb] ][Y[YYYV VV VVV@Q@QOOM MM MJ JJ JJ JE EC CA AA>AV'>>V>>>9V(@@79V57V55 5V2 V(2V @b @ bbbbb] ][Y[YYYV VV VV@x @[A]AHKJ<C.`b%@b0] ]`] ]@V`g%g0b b`d d i%@i0d @ d`] ] j%j0e e@[([[% [/^'^ b&b ['[ ^'^b( b/g&g [([[% [/^'^ b&b ['@[ ^'@^ b(b g&g a'aP]']P]'](]' ]]']PX'XPX'X(U' UU'UP]']P]'](]' ]]']PX'@XPX'@X(U' UV(VV% V/Y'Y ]&] V'V Y'Y]( ]/b&b V(VV% V/Y'Y ]&] V'V Y'Y ](@] b&@bb%b0] ]`] ] b%b0] ]`e e i%i0d d`b b@a'aP]']P]'](]' ]]']PX'XPX'X@(@U' UV(VV% V/Y'Y ]&] V'V Y'Y]( ]/b&b V(VV% V/Y'Y ]&] V'V Y'Y ](] b&b@@b%b0] ]`b b e%e0` ``d d i%i0d d`b b i%i0d d b%b0] ]`b b i%i0d d@V(V@@V% V/['[ ]&] V'V ['[]( ]/b&b V(VV% V/Y'Y ]&] V'V Y'Y ](] b&bg%g0b b@`d @ d i%i0d d b%b0] ]`e e`b b i%i0d d i%i0d d@V(VV% V/Y'Y ]&] V'V Y'@Y](@ ]/b&b V(VV% V/Y'Y ]&] V'V Y'Y ](] b&b @b @ bbbbb] ][Y[YYYV VV VV@@VQQOOM MM MJ JJ JJ JE EC CA AA>V'A>>V>>9>V(@9@7V75V55 5V2 V(2V V* V`[A.CHKJ<x @@]A`@b%b0] ] b%b0] ] b%b0] ]`e e l%l0g g@}V$|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0@V@/MTrk$JAZ2}General MIDIDrumsC#WJ<HK@]8 @ @H[8#P3?663P#N#H63;63P[8J<@x @#QC]HK8#H63C63P#O#H6$3I63P#O88#P63C63P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63P8#Y8#H63A63P#U#P3L6%63P#T88#P6#3H36P#K#P3I6$36P#V88#P6$3I63P#N#P3I6$63P8#P8#P3C663P#Q#P63>63P#W88#P63?36P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P8#h1 81#P3I636P#a1 #1H63I63P89#c1 891#H63C63&.&&7&&=#d&#H6$3>&V6&3H918#W819#P3?663P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P#O88#P63C36P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63P8#O8#P3C636P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63P8L%#GQ%8L#QP66PL!#AL#H6 6_1#BL!88L1#H6 6_L!#JL#H6$ 6W19#GL%Q%8981QL#P66P#AL!L#H6 6_81#BL!8#L1H6 6_#JL!#LH6$ 6W911 8#h819#1P63I63P#a1 1#H63I63P#c81 9819#H63C63&.&&7&&=#d&#H6$3>&V63&H91#W8891#P3?663P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P#W88#P3?663P#N#H63;63P#Q88#H63C63P#O#H6$3I63P8#O8#P3C636P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63PL%Q%8#G8L#QP66P#AL!L#H6 6_1#BL!881L#H6 6_#JL!#LH6$ 6W91#h81 198#1P63I63P1 #a#1H63I63P#c1 89819#H63C63&.&&7&&=#d&#H6$&V3>6&3H19#W8891#P63?36P#N#H63;63P#Q88#H63C63P#O#H6$3I63P8#O8#P3C636P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63P8#Y8#H63A63P#U#P6%3L36P8#T8#P3H6#63P#K#P6$3I63P8#V8#P3I6$63P#N#P3I6$36P#P88#P3C663P#Q#P63>63P8#W8#P3?663P#N#H63;63P#Q88#H63C63P#O#H6$3I63P#O88#P3C636P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63P8#Y8#H63A63P#U#P3L6%36P#T88#P3H6#36P#K#P3I6$63P8#h1 81#P3I636P1 #a1#H63I63P8#c91 8#91H63C63&.&&7&&=#d&#H6$3>&V63&H91#W8918#P3?636P#N#H63;63P#Q88#H63C63P#O#H6$3I63P#O88#P3C636P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63P#GL%Q%88#QLP66P#AL!L#H6 6_18L!#B81#LH6 6_L!#JL#H6$ 6W19L%#G8Q%819LQ#P66P#AL!L#H6 6_81#BL!81L#H6 6_#JL!#LH6$ 6W198#h1 198#1P3I636P1 #a1#H63I63P1 98#c89#1H63C63&.&&7&&=#d#&H6$3>&V6&3H911 8998191h1 9889171 9&I(+91(& (0&D&( ($&8(& &L(8(& 91 8819h91 ]x@ @HK[C8J<198791 (+&I19&( (0&D&( &8($(& (8&L(& #W88#P3?636P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P91 8891h91 8891791 &I(+91&( &D(0&( ($&8&( &L(8&( #W88#P3?636P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P#O88#P63C63P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63PL%#GQ%88QL#P66PL!#AL#H6 6_#B1L!88L#1H6 6_#JL!L#H6$ 6W918#GL%Q%198#LQP66PL!#AL#H6 6_1#B8L!8#1LH6 6_#JL!#LH6$ 6W911 #h89811#P3I663P#a1 #1H63I63P91 8#c819#H63C63&.&&7&#d&=&#H6$&V3>6&3H19#W8918#P3?663P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P8#O8#P63C36P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63P8#Y8#H63A63P#U#P3L6%63P8#T8#P3H6#36P#K#P3I6$63P#V88#P3I6$63P#N#P3I6$36P#P88#P63C36P#Q#P3>663P#W88#P63?63P#N#H63;63P#Q88#H63C63P#O#H6$3I63P#O88#P63C36P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63P81 9819h91 891871 9(+&I91(& &D(0(& ($&8&( &L(8&( 1 89819h1 9[xCJ<@8] @HK89171 9&I(+19(& (0&D&( ($&8(& (8&L&( 8#W8#P3?663P#N#H63;63P#Q88#H63C63P#O#H6$3I63P#O88#P63C63P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63P#Y88#H63A63P#U#P3L6%63P#T88#P3H6#36P#K#P6$3I63P8#V8#P6$3I63P#N#P6$3I63P8#P8#P63C36P#Q#P63>63P8#W8#P3?663P#N#H63;63P#Q88#H63C63P#O#H6$3I63P#O88#P3C636P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63P8#Y8#H63A63P#U#P6%3L63P#T88#P3H6#63P#K#P6$3I63P81 #h81#P3I663P1 #a#1H63I63P#c81 9891#H63C63&.&&7&&=#d#&H6$&V3>6&3H918L%#GQ%198QL#P66P#AL!L#H6 6_1#BL!881#LH6 6_#JL!#LH6$ 6W19L%#G8Q%189QL#P66PL!#A#LH6 6_8#B1L!8L#1H6 6_L!#J#LH6$ 6W91#h1 8918#1P3I663P1 #a#1H63I63P#c81 98#91H63C63&.&&7&#d&=&#H6$3>&V63&H91#W8891#P3?663P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P8#O8#P63C63P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63P8#Y8#H63A63P#U#P6%3L63P#T88#P6#3H63P#K#P3I6$36P#V88#P6$3I36P#N#P6$3I63P#P88#P3C663P#Q#P63>63P#W88#P63?63P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P8#O8#P63C36P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63PL%8Q%#G8L#QP66P#AL!L#H6 6_#B81L!81#LH6 6_L!#JL#H6$ 6W91#h81 891#1P3I663P#a1 #1H63I63P#c1 89819#H63C63&.&&7&&=#d&#H6$3>&V63&H918#W198#P3?636P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P#O88#P63C63P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63P8#Y8#H63A63P#U#P6%3L36P#T88#P3H6#36P#K#P3I6$36P#V88#P6$3I63P#N#P3I6$36P8#P8#P63C63P#Q#P3>663P8#W8#P3?636P#N#H63;63P#Q88#H63C63P#O#H6$3I63P#O88#P63C36P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63P81 #h8#1P3I636P1 #a#1H63I63P81 #c989#1H63C63&.&&7&#d&=#&H6$&V3>63&H19#W8981#P63?63P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P#O88#P63C36P#O#H63C63P#G88#H6$3I63P#Q#H6)3L63P#GQ%L%88QL#P66P#AL!#LH6 6_L!8#B181L#H6 6_#JL!#LH6$ 6W191 #h89181#P3I636P#a1 #1H63I63P#c1 898#91H63C63&.&&7&#d&=&#H6$3>&V6&3H198#W819#P3?663P#N#H63;63P#Q88#H63C63P#O#H6$3I63P8#O8#P63C63P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63P1 8#h8#1P63I63P1 #a#1H63I63P1 98#c891#H63C63&.&&7&#d&=#&H6$3>&V6&3H19#W8891#P63?36P#N#H63;63P#Q88#H63C63P#O#H6$3I63P8#O8#P3C636P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63P8#O8#P3C636P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63PL%#GQ%88L#QP66PL!#A#LH6 6_81L!#B81L#H6 6_#JL!L#H6$ 6W19#G8Q%L%198LQ#P66P#AL!L#H6 6_18#BL!8L1#H6 6_L!#J#LH6$ 6W9181 #h891#1P3I663P#a1 1#H63I63P81 9#c819#H63C63&.&&7&&=#d#&H6$3>&V6&3H191 8991819h1 98891791 &I(+19&( (0&D(& ($&8&( &L(8(& #l891"(5&J8#91(&pC @HKJ<8x@[]871$9(/&P19(& (4&J(& &>(((& (>&T(& 1 #l881#P3M663P1 #e#1H63M63P9#c1 881#9H63C63&.&&7&#d&=#&H6$3>&V6&3H198#W189#P3?636P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P8#W8#P63?36P#N#H63;63P8#Q8#H63C63P#O#H6$3I63P8#O8#P63C36P#O#H63C63P8#G8#H6$3I63P#Q#H6)3L63PL%Q%8#G8LQ#P66P#AL!L#H6 6_L!18#B8L1#H6 6_L!#JL#H6$ 6W9116#619#1/MTrk4JAZ2 @General MIDI Choir Aahs1YYx [@]@J"}C}J<HKxp}JQ"xpQ"}Q`Q"}Q }O"Q`O"}O }OL"xpX"}L`}X"X X}Y"xpYV"}xpR"}Vxp}Q"RxpQ}U"xp}V"UxsnpE"V}xp}A"Exp<"A}`}<"< >"}<xp@"}>`}C"@ ="C}`}=>" }<">`<>"} >}>"xsnp}H">xpH}J"`}J"J J}F"`F}I" I}E"xpEC"}`}CC" }C"C`}C"C }C9"`99"} 9}>"xsnp>}V"xsnidpV}O"xpV"O}xp}VL"xpQ"}L`Q"}Q Q}>"xp>}C"`C"C} }A"Cxp@"A}`}@"@ A"@}`C"}A CE"}`EF"} }@"F`}@"@ }9"@xsnp}9M"xpQ"}MxpO"}Q`O"}O }L"Oxp}X"L`X"}X Y"}XxpYV"}xpV}R"xpRQ"}xpQ}U"xp}V"UxsnpJ"}VxsnpJ"J}xpC"}J`C}C" CJ"}`}JJ" J}@"xp}@E"xsnpEC"}`C}C" CA"}xp@"}A`}@"@ }A"@`}C"A CE"}`}F"E @"F}`@@"} 9"@}xp9}J"xp}JQ"xp}QQ"`}Q"Q }QO"`OO"} O}L"xpL}X"`X"X} X}Y"xpV"}YxpV}R"xp}RQ"xp}U"QxpV"}Uxsnid_ZpE"V}xsnp}A"ExpA}<"xp<>}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0>/MTrk4JAZ2J"General MIDI Choir Aahs2YYx h[@]C @@}HK}J<`}L"J Q"}Lxp}QQ"`Q"Q} O"}Q`O}O" L"}OxpX"}L`}XX" }XY"`X"Y} }V"X`V}T" }R"TxpQ"}Rxp}U"QxpUV"}xsnpE"}Vxp}A"Exp}<"A`<}<" <>"}xp}>C"`}A"C A}>"`}>"> >"}>`>>"} >}>"xsP>A"} A}H"xpJ"}H`J}J" J}I"`}II" E"}IxpC"}E`}CC" }CC"`C}@" 9"@}`}99" >"}9xsnp>V"}xsniP}VT" O"}T`Q"}O }QV"`}R"V L"}RxpQ"}L`Q"Q} >"}Qxp}C">`}C"C A"}Cxp@"A}`}@"@ }C"@`C"}C }CF"`FF"} @"}F`@}>" 9"}>xsnpM"9}xpQ"}MxpQ}O"`}O"O }L"Oxp}X"L`}XX" }Y"X`X"Y} }V"X`VT"} }TR"xp}Q"RxpQU"}xp}UV"xsnpJ"}Vxsnp}J"J`JH"} C"H}`E"}C }EJ"`}F"J F}@"xp}@E"xsnpEC"}`C}C" A"}Cxp}@"A`@"}@ @C"}`C"}C C}F"`FF"} F}@"`@}>" >9"}xp9J"}`J}L" L}Q"xpQ"}Q`}Q"Q O"Q}`OO"} O}L"xpX"}L`X}X" }XY"`YX"} V"}X`T"}V }R"TxpRQ"}xpU"}Qxp}V"Uxsnid_Zp}VE"xsnp}A"Exp<"}Axp>}<|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0>/MTrkYJAZ2General MIDI Warm Pad1YYx "[@]>< @J<}HKCA<}@xsnpC<}><A>`>A<><}C C<}AF<>xp9<=<}FCxsnp}=9A<><xp<<:<>}A`:}:<><< C<F<}>:xsnpCF}=<9<xp><A<}9=xsnid_Zp>}A9<<<xp9<A<><}xpE<@<}A>`@EF<}C< CF9<}><xpA<>}E<9`><A<}EA >:<}<<AxsP<}:<:>< }>H<:C<xp}H><A<CxpF<}C<A>`=<CF9<} =}><A<9xp>AC<F<}xsPCF=<}9< ><}9=9<`>9}E<C< ECA<><}xsnid_ZUPp}<<@<>Axp:<}<<<@`:<}><<: }=<9<:>xp}9==<9<`E<=9}C< A<><E}Cxp>A}C<H<xpCA<H}E<xp9<}=<AExpA<9}><=`}@<E<A> A<}><@E`C<F<}A> F9<}=<C`><9<}=90xsnid_ZUpC<F<9}>xpCF9<=<}xsnp><A<9=}xp:<}<<A>`:<}><:< :F<}C<>xsnp9<}CF=<xp=9A<}><xsnid_ZpA:<>}<<`:}:<<>< H<>:}C<xpH}><CA<`>F<}AC< 9<}=<FCxpA<}=9><xsnp}C<AH<>xpCHA<E<}xp9<}EA=<xp=A<}><9`E<@<}>A A<><@}E`AC<}F<> =<}9<CF`=9}><9<0xsp>C<F<9}xsniP}A<><FC }F<>AC<xpC=<}9<Fxsnp=}><A<9xp>A<<:<}`><<:<}: F<}>:C<xsnp=<C}9<FxpA<><}9=xsnid_ZUPKFAAxpC>*}H|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0>/MTrkYJAZ2CGeneral MIDI Warm Pad2YYx ^[@]@HKJ<A<9<} @}xsnp}9<A><9`}A<>99< A<}A><9`AC<>}>< }@<9<C>xsnp9<><9}@xp>9A<:<}xp}><C<:Axp@<9<}>Cxsnp@9}><9<xsnpA<9>9<}xp:<A9A<}xpA}:@<9<xp9<><}9@xp9>@<}9<`}><:<9@ }:9<>@<`><@9<}9 }9<<<>9`9<9<><} :<}9A<>xsnp}C<:A<<xsP9<><}<C :<}9><>`}>:9<@<0xsp9><A<@}`C<}A>><0xPC@<>9<}0xsp}><9@9<xsnid_ZUPA<>9<}9 C<<<}A9xpA<}:<<Cxp}:A@<9<xsnp9<@9}><xp}>C<><9xp<<A<C>}xp}A<9<@<xp@}><99<`}9<@<>9 @9<9}:<`9><}C<: }9<@<C>xsnidp><}99<@xp}9<A<>9xpA<}><9A`A>><}C< @<}9<C>xsnp}><99<@xp>}A<9:<xpA:><}C<xp>C}@<9<xsnp@99<}><xsnid_Zp}:<A<>9xp:><}AC<`>9<C@<} ><@99<}`9>C<}>< }@<9<>CxsnidpC<9@}><xpA<}<<>CxpA@<9<}<xp@><9<}9`9<}@<9> @9<}9:<`C<><}9: 9<@<>}Cxsnp:<><}9@xp}>@<:9<xp@9}><9<`9<A<}>9 ><9A<}A`A>C<><} @<>C}9<xsnp9<}9@><xpA<>:<9}xpA}C<><:xp@<C9<>}xsnp><9<9}@xsnid_Zp9<}A<9>xsnpA<A}:<9xp}C<<<:Axp>*C}<|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONML0>/MTrke0JAZ2 @ @yYOn the WaterfrontGeneral MIDIr(/simutrans-124.5/simutrans/music/31-Courtenay-Bridge.mid000066400000000000000000000306051520675460500230140ustar00rootroot00000000000000MThdMTrkCourtenay Bridge By Copyright 2007 All Rights Reserved Generated by NoteWorthy ComposerQQ؁QQ/MTrk!StaffG @Jn J Jn J Jn J Ln L GnNGCn@C0>n>?n.?@n @ Cn C Cn C EnECn@C0CnCDn.DEn E >n > En E Dn D CnPC0@n@Cn.C@n@7n70Jn J Jn J Jn J Ln L GnNGCn@C0>n>?n.?@n @ Cn C Cn C EnECn@C0CnCDn.DEn E >n > En E Dn D CnPC0@n@Cn.C@n@7n70Jn J Jn J Jn J Ln L GnNGCn@C0>n>?n.?@n @ Cn C Cn C EnECn@C0CnCDn.DEn E >n > En E Dn D CnPC0@n@Cn.C@n@7n70Jn J Jn J Jn J Ln L GnNGCn@C0>n>?n.?@n @ Cn C Cn C EnECn@C0CnCDn.DEn E >n > En E Dn D CnPC0@n@Cn.C@n@7n@70CnCDn.DEn E >n > En E Dn D CnPC0@n@Cn.C@n@7n7/MTrk !Staff-1 @>nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CGnCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nBnHn.>BHB>nBnHn >BH`nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CGnCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nBnHn.>BHB>nBnHn >BH`nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CGOnOGn.GJnJOn.OGnGJn.JOnOGn.GJnJOn.OGnGJn.JOnOGn.GJnJOn.OGnGJn.JOnOGn.GJnJOn.OGnGJn.JOnOGn.GJnJOn.OGnGJn.JOnOGn.GLnLOn.OHnHLn.LOnOHn.HLnLOn.OHnHLn.LOnOHn.HLnLOn.OHnHLn.LOnOGn.GJnJOn.OGnGJn.JOnOGn.GJnJOn.OGnGJn.JOnOGn.GJnJOn.OQnQJn.JNnNEn.EJnJBn.BEnEGn.GHnHLn.LOnOHn.HLnLOn.OHnHLn.LOnOGn.GJnJOn.OGnGJn.JOnOGn.GJnJOn.OGnGJn.JOnOGn.GJnJOn.O>nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CGnCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nBnHn.>BHB>nBnHn >BH`nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CGnCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nBnHn.>BHB>nBnHn >BH`nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn.>CG>nBnHn.>BHB>nBnHn >BH`nCnGn.>CGB>nCnGn >CG`>nCnGn >CG0>nCnGn>CG/MTrk!Staff-2 @+n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 +n + -n - /n / 0n 0 4n 4 0n 0 .n . 0n 0 +n + .n . (n ( +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - 2n 2 -n - 2n 2 1n 1 0n 0 $n $ (n ( )n ) +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 +n + -n - /n / 0n 0 4n 4 0n 0 .n . 0n 0 +n + .n . (n ( +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - 2n 2 -n - 2n 2 1n 1 0n 0 $n $ (n ( )n ) +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 +n + -n - /n / 0n 0 4n 4 0n 0 .n . 0n 0 +n + .n . (n ( +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - 2n 2 -n - 2n 2 1n 1 0n 0 $n $ (n ( )n ) +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 +n + -n - /n / 0n 0 4n 4 0n 0 .n . 0n 0 +n + .n . (n ( +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - 2n 2 -n - 2n 2 1n 1 0n 0 $n $ (n ( )n ) +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - +n + /n / 2n 2 /n / 7n 7 +n + -n - /n / 0n 0 4n 4 0n 0 .n . 0n 0 +n + .n . (n ( +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - 2n 2 -n - 2n 2 1n 1 0n 0 $n $ (n ( )n ) +n + /n / 2n 2 /n / 7n 7 2n 2 /n / -n - 2n 2 -n - 2n 2 1n 1 0n 0 $n $ (n ( )n )0+n.+B+n +`+n +0+n+/MTrk _!Staff-3 @3n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3 3n~33n>33n 3`3n 3`3n 303n3/MTrk !Staff-4 @#n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n~%%n>%#n # %n~%%n>%#n # %n~%%n>%#n~##n>##n1n #1 #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n~%%n>%#n # %n~%%n>%#n # %n~%%n>%#n~##n>##n1n #1 #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n~%%n>%#n # %n~%%n>%#n # %n~%%n>%#n~##n>##n1n #1 #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n~%%n>%#n # %n~%%n>%#n # %n~%%n>%#n~##n>##n1n #1 #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n % #n # %n~%%n>%#n # %n~%%n>%#n # %n~%%n>%#n~##n>##n1n #1 #n # %n % #n # %n % #n # %n % #n # %n % #n # %n~%%n>%#n # %n~%%n>%1n 101n1/simutrans-124.5/simutrans/music/32-incidental-skies.mid000066400000000000000000001043041520675460500230760ustar00rootroot00000000000000MThd xMTrkMCopyright (C) 2007 shunter~ JAZ2j~j@'@@1@~j@@@@@@@1@$@@'~j1@1@@@@@~j@@@$@@'@1@@fj@@@@@@@@@NjXQYIncidental Skies/MTrk #JAZ2[_Bass@]{ @`$Z6$$^C$ Q !8$!$W2$$Z-&a$#$^&$!'+$i!5$$eP$Q%!<*!$^@$$T8$T !D#!+Z6++^(#Q+/#!#B$#$W2$$Z$ %U-%#&L#&^5^S%8#a>e!W1$$Z6$$^C$ Q !8$!$W2$$Z-&a$#$^&$!'+!$i5$$eP$Q%!<*$^!@$$T8$T !/#!$T+$%$T<$T(!T'!$T*$&$T>$T(!T&!$T/$!$T/$!T'!T!!$T)$'$T-$#T(!T !)Z6))^()!QP!!B$!"W2""Z("#U6##L+$i#1$$e($&g(&(&=*&'^,'"'T(')T5))X#)$T+$%$T<$T(!T'!$T*$&$T>$T(!T&!$T/$!$T/$!T'!T!!$T)$'$T-$#T(!2 !+TD+ +T+]+T'+&TD& &T&[&2#&)a3))aH)$J"$&4(&)Z>))Z@)$W!$)?$)$T+$%$T((N$((((T((+T*+&+T +(F;(+T#+$a4$$^PM$$!G($^!%$$^M$!Q(Z!)b6))f(!Y)P!J!$!"_2""b("#]6##T+$q#1$$m($&o(&(&E*&'f,'"'\(')\5))`#)$^3$$^L$^ !^! $^*$&$^M$^!^! $^:$$^I$^!^!$^1$$^$&^$&$^$ !8!!)n3))nH)$W"$&A(&)g>))g@)$d!$)-$)(f>((j!(W(-$('^5''^S'%'^#'&i<&&i-&K&K#&!e8!!bA!&X(*4 &*+c#+=$k4$$r>$Q&!I($d!8$$kF$ I(!N$!$r8$$nJ$W$!8($k !$*$n*Z$&!N,&F!&$o7$$oE$ !^(Q!$k8$$hA$!^!!^! !$o:$$kA$T%!T*$o!0$$dL$N !a$!)^D) )^)])^')$^D$ $^$[$^&$)^I))^/)I)^!)$^I$$^()^$>))^ )$g4$$dPS$$!M($d!%$$dM$!W(`!'$k>$ $gG$ W%!W#!$k@$$gD$ !`! E$+e6++i(+#\/#!#M$#$b2$$e$ %`-%#&4#&)^3))^L)$^$ &^& )^*)&)^M)$^$&8& $n4$$u>$T&!L($g!8$$nF$ L(!Q$!$b6$$fC$ Y !@$!$_2$$b(+i$((f+#($:#$)f6))jC) $]$ &D$&)c2))f-+m)#)j+)&3+&)u5))qP$])%$&H*)j&@))`8)$` $&P#&$k4$$r>$Q&!I($d!8$$kF$ I(!N$!$r8$$nJ$W$!8($k !$*$n*Z$&!N,!&v&)n4))u>)$T&$&L()g&8))nF) $L($&Q$&(`>(#d!#WW$'`>'"d!"WU"W$&T+&%&T(&*N(*(*T(-T**-&-T -*F;*-2#-m#=$a4$$^PM$$!G($^!%$$^M$!Q(Z!'$e>$ $aG$ Q%!Q#!$e@$$aD$ !Z! ,$$Z6$$^C$ Q!B$!$W2$$Z-$#^#!L+$i!1$$e(&g$(&(&<*'^&,'"'T((T'5((X#()T3))TL)$T$ &T& )T*)&)TM)$T$&T& )T:))TI)$T$&T&)T1))T)+T$+)T) &2!&$Z6$$^C$ Q !8$!$W2$$Z-$&a#$^&$!'+$i!5$$ePQ$%!<*$^!@$$T8$T !D#!+TD+ +T+]+T'+&TD& &T&[&2#&)TD) )T)])T')$TD$ $T$[$T#$$a4$$^PM$$!G($^!%$$^M$!Q(Z!$ZO$$^C$58$$W2$$Z($(&^#&(Z#()b6))f()!YP!J!$!"_2""b("#]6##T+$q#1$$m($&o(&(&E*'f&,'"'\(')\5))`#)$o7$$oE$ !^(Q!$k8$$hA$!^!!^! !$o:$$kA$T%!T*!$o0$$dL$N !a$!)^3))^L)$^$ &^& )^*)&)^M)$^$&8& ^5^S%8#'f>''j!'W'K$'&i<&&i-&K&K#&!e8!!bA!&X(*4 &*+m#+A$W$7$WMW$!W!$W1$!$W5$)0W$+W0+$o6$$v)$L$o4$/MTrk;JAZ2{ @x]Drums[`6Z#d*d*6#*<6<6*#d6Z*m*#6&s6<6&*V6Z6*6<*d6*6Z*h#d#*6*F6<*66Z*k#d6*#&s6<&6*P6Z6**d6<*6*s#d6Z#*6*<6<6*#d*m6Z#*6&s6<6&*V6Z*6*s6<6*6Z#d*h*6#6<*F*6#d6Z*k*6#&s6<6&6Z*P*6.d6<6.6Z*d#d6*#*<6<*6#d*m6Z*6#6Z&s6&*V6<6**d6Z6*6<#d*h*#6*F6Z6**k#d6<#6*&s6Z6&*P6<*6*d6Z6*6Z#d.Z#.66<*Z6*.d6Z#d#.66<&s6&6Z*V6*6<*d6*#d*h6Z6*#*F6<*66Z*k#d6*#&s6<&6*P6Z*6*d6<*66Z*d#d6#**<6<*6*m6Z#d*#66Z&s&66<*V6*6Z*d6**h6<#d6*#*F6Z*6#d*k6<6*#&s6Z6&6<*P6*6Z*d6*#d6Z*d*6#*<6<*6#d*m6Z*6#6<&s&6*V6Z6**d6<6*6Z#d*h*6#6<*F*6*k#d6Z6#*&s6<6&6Z*P6**d6<*6*d6Z#d6*#*<6<*6#d6Z*m#6*6Z&s&6*V6<6*6Z*d*66<#d*h6*#*F6Z6*6<#d*k*#66Z&s6&6<*P6*6Z*d*66Z#d*d*#6*<6<6**m6Z#d6*#6<&s6&6Z0F606<0d60#d-P6Z-6#-Z6<-6)P#d6Z6)#)Z6<&s6&)6Z6#Z6<1Z16##d*d6Z6*#*<6<6*6Z#d*m6#*6<&s&66Z*V6**d6<*66Z#d*h6#**F6<*6*k6Z#d#*66<&s6&6Z*P6**d6<*6*s6Z#d6#**<6<6*#d*m6Z6*#&s6<&66Z*V*6*s6<6*#d6Z*h#6**F6<*66Z#d*k*6#6<&s&6*P6Z*6.d6<6.#d*d6Z6*#6<.H.6#d*m6Z6*#6<&s6&*V6Z6**d6<6*#d*h6Z*#6*F6<*66Z#d*k*#6&s6<6&*P6Z6*6<*d6*6Z#d*d6#*6<*<*6#d*m6Z#*66Z&s6&6<*V*6*d6Z6*#d6<*h#*6*F6Z6*#d*k6<#6*6Z&s&6*P6<6*6Z*d6**d6Z#d6#*6<*<*66Z#d*m#6*6Z&s6&6<*V6**d6Z6*#d6<*h*#6*F6Z6*6<*k#d6*#6Z&s&66<*P6**d6Z6**d6Z#d6*#*<6<*6#d6Z*m#*6&s6Z6&6<*V*6*d6Z*6#d*h6<#*6*F6Z6**k#d6<#*66Z&s&66<*P*6*d6Z*66Z#d*d6*#*<6<6*#d6Z*m#6*&s6<6&6Z*V6*6<*d*6#d*h6Z6*#*F6<6*6Z#d*k#6*6<&s6&*P6Z6**d6<*6*d6Z#d*#66<*<*6*m#d6Z6#*&s6Z&66<*V*6*d6Z*6#d*h6<#6*6Z*F6*#d6<*k#*66Z&s6&6<*P6*6Z*d6*#d6Z*d*6#*<6<6*6Z*m#d6#*&s6<6&6Z*V*66<*d*6#d6Z*h#*6*F6<6*#d6Z*k#*66<&s&66Z*P6*6<*d*6*s#d6Z*#66<*<*6#d6Z*m#*66<&s&66Z*V6**s6<*66Z#d*h6#**F6<6*6Z*k#d#6*&s6<&6*P6Z*66<.d.6*d6Z#d#6**<6<*6*m6Z#d*#66Z&s6&6<*V*6*d6Z*6#d6<*h#*6*F6Z*6#d*k6<#6*6Z&s&66<*P6*6Z*d6*&s#d6Z&6#6<&Z&66Z&s#d6#&&6Z6&0d6<066Z0Z06#d0s6<60#6Z0Z606<-s-66Z-d-66<)s6))d6Z6)EdFs#d6d1d3sE6F31#FZ6<F6#dEd6d3sFs6E3F#Ed6<FZ&sF&6E3sFsEP6dE6F3FZ6<EdFE63s6dEd#dFs#6EF3FZ6<F66dEd#d3sFs#F63E&s6<FZEd&EF6Fs3sEP6dF6E3FZ6<F6#dFs3sEd6d#EF366<FZ6F3s6dFsEd#dE#F36&sEdFZ6<EF6&EP6d3sFsEF36FZEd#Z6<E6#F#dEd3s6dFsE6#3F6<FZ6FFs3s#d6dEdE#63F&sEd6<FZE6F&3sEP6dFs36EFFZ6<6FFsEd#d3s6d36E#F6<FZ6FFs6dEd3s#d36#FEEd&sFZ6<&6EF3s6dEPFs6E3F6<FZEdEF6Ed3s6d#dFs#EF36FZ6<6FFs3sEd6d#dFE36#FZ&sEd6<6&FE3sFsEP6d6EF36<FZ6FFs3s#dEd6dF3#6E6<FZF66dEd#d3sFs6#FE3EdFZ&s6<EF&66d3sEPFsFE366<EdFZ6EFFs3s#dEd6dE63#F6<FZF6Fs6d#d3sEdF6#E3FZ&sEd6<6&FEEP6d3sFsEF636<FZ6F3s6dEdFs#dF3#6EFZ6<F63s#dEdFs6d3#EF6&sFZEd6<6FE&6d3sEPFs63EF6<EdFZE6FFs3s#dEd6d3#EF66<FZ6F3s#dEdFs6d#F36E6<&sEdFZ&E6F3s6dFsEPE63FFZ6<F66dEdFs#d3s63EF#FZ6<F66d#d3sEdFs63EF#FZEd6<&sF6&EEPFs3s6d6EF3FZEd6<6EF6dEd3s#dFsFE#636<FZF6#dFsEd6d3sF63#E&sFZ6<Ed&6FE6dEPFs3s63EF6<FZ6FFs6d3s#dEdE6#F36<FZ6F3sEdFs6d#d36FE#&sEd6<FZFE6&Fs3sEP6d6E3F6<FZEdFE63sEd#d6dFs#3FE6FZ6<6FEdFs#d3s6d36EF#6<EdFZ&s6&FE3sEP6dFs3F6EFZ6<F6*d6Z#d*6#6<*<*6*m6Z#d*6#&s6<6&0F6Z606<0d606Z#d-P-6#-Z6<6-)P#d6Z6#))Z6<&s&6)6Z61Z6<#Z61#3sFs#d6d1dEd#FE136FZ6<F6EdFs6d#d3s36FE#Ed&s6<FZF6E&FsEP6d3s36FEEd6<FZ6FE3sFsEd#d6dF63#E6<FZF63sEdFs#d6d36EF#6<&sFZEdEF&66dFsEP3sF36E6<FZ6FFs#d6dEd3s3FE6#FZ6<F66d#d3sEdFsFE3#6&sEdFZ6<&6FEEPFs6d3sFE36FZ6<Ed#ZE#6FFs3s#dEd6dE#36F6<FZF66d3s#dEdFs#36EF6<&sEdFZ6F&E3s6dEPFsE3F6FZ6<6FFs#dEd3s6dF3E#6FZ6<6F3sEdFs6d#d3#EF6EdFZ6<&sE&6F6dEPFs3sF63EFZ6<Ed336FE3sFs6dEd#d3EF6#FZ6<6FEd6dFs3s#dE#36F&sEdFZ6<6FE&3sFs6dEP6F3E6<FZF66dFs#d3sEd63EF#FZ6<6FEd6dFs3s#dE#3F6&sEdFZ6<FE&6EPFs3s6dE63FFZEd6<E6FEd#dFs6d3s3E6#F6<FZF66d3sEd#dFs36E#F&sEdFZ6<F&E6Fs3sEP6d3E6FFZ6<6F#dEdFs3s6d3F#6E6<FZF63s#dEdFs6dF#36E&sFZ6<Ed&E6FFs3sEP6d63FEEd6<FZ6FEFs3sEd#d6d6F#E3FZ6<6F6d#d3sEdFs#E6F3&sEdFZ6<6FE&3sFs6dEP3F6EFZ6<F6Fs3s#dEd6d36FE#FZ6<6FEdFs6d3s#d#3F6E&sEdFZ6<&6FEEPFs6d3s36FE6<FZEdFE6#dEdFs6d3s#FE636<FZ6F3sEdFs6d#dE3#6F&sEd6<FZEF6&3sEPFs6d3FE6FZ6<6F#d6dFs3sEd6E#3FFZ6<6F#dFs6d3sEdF#E636<EdFZ&s6&FEEPFs6d3s6FE36<#ZFZEd#6FE3sEdFs6d#dE6#3F6<FZF63s#dEd6dFsF6E#3FZ6<&sEd6FE&EP3s6dFs3E6FFZ6<6FEdFs#d3s6dF6E3#6<FZF66dEd3s#dFsF#E63FZEd6<&sEF&66d3sEPFs6FE3FZEd6<6FE6d#dEd3sFs6FE3#6<FZF6Fs3s#dEd6dE#3F6FZ&s6<Ed6FE&6dEPFs3s36FEFZ6<6F#d6d3sEdFsE36F#6<FZF6Fs3s#d6dEd#3FE6FZEd6<&sF&6EEPFs3s6dE63FFZ6<EdEF6#d3sFsEd6dEF36#FZ6<6F6dFsEd#d3s63#EFEd&s6<FZEF6&6dFsEP3s36EFFZ6<F6#dEd6d3sFs6EF#3FZ6<6F3s#dEdFs6d6FE#36<FZ&sEd&F6E6d3sEPFs6FE3Ed6<FZE6F#d6d3sEdFsE6F#3FZ6<6FEd#dFs3s6d63F#EEdFZ&s6<&FE6EP6d3sFs6FE3FZ6<6F#d6d3sEdFs6F#E36<FZF6Fs#d6d3sEd36#FE&s6<EdFZE&F6EP3s6dFsEF636<3FZEd6FE3Ed3s#dFs6d6EF#36<FZF63sFs6dEd#d#E6F36<&sEdFZE&6F6dFsEP3sE36FFZ6<F66Z#d*d*6#*<6<*6*m#d6Z6*#&s6<6&6Z0F060d6<066Z-P#d-#6-Z6<-6)P6Z#d6)#)Z&s6<6&)6Z61Z6<#Z6#1Fs#dEd1d3s6d#6F31E6<FZF6FsEd6d3s#dEF#366<&sFZEdEF&6EPFs3s6d3FE6EdFZ6<F6E#dEdFs3s6dE6#3F6<FZF6Ed6d3sFs#dF36E#FZEd&s6<F6&EFs3sEP6dE36F6<FZF6Ed6dFs3s#d#E3F6FZ6<F6#d6dEdFs3sF6#3E6<&sFZEd6EF&3sFs6dEP3E6FFZ6<Ed6EFEdFs3s#d6d6E#3F6<FZ6FFs3s#dEd6d#3F6E&s6<FZEd&EF6Fs6dEP3s3FE6FZ6<F6Ed6dFs3s#d6#E3FFZ6<6F3s6dEdFs#d63#FEFZ6<&sEdFE&6Fs3s6dEPE63FEdFZ6<EF6#dFs6dEd3sFE3#66<FZF6Ed#d3s6dFs#EF63&s6<EdFZ&FE66dFsEP3s6EF3FZ6<6FEd#d3s6dFs#FE636<FZF6Fs6d#d3sEd3#6FEEd&s6<FZEF6&6dEP3sFs3E6FFZ3Ed6<3FE6#d6dEdFs3s6F3#EFZ6<6FFs#d3sEd6d6E3#FFZ6<&sEd&6FE6dFsEP3s36EF6<FZF6Fs6d#d3sEd6EF3#6<FZF63s6dFsEd#dEF63#Ed&s6<FZ&EF63s6dFsEP3EF6#Z6<EdFZ#EF66dEd3sFs#d3E#6F6<FZF66d#d3sFsEdEF3#6Ed&sFZ6<F&E6Fs6dEP3s63FEFZ6<6F6dEdFs3s#d63FE#6<FZF66d3sEd#dFs#E36FFZ&sEd6<F6E&6dFsEP3sEF636<&<FZEd6FE&6dFs#dEd3s6E#F36<FZF63s#dEdFs6dFE#366<&sEdFZ6FE&Fs6d3sEP36FE#Z6<FZ6F#Fs#dEd6d3s6E#3FFZ6<6F3s6dFsEd#dEF63#Ed&sFZ6<E6F&3sFsEP6dF36E6<3FZEd6FE3#dEd3sFs6dF3E6#FZ6<6FFs3sEd6d#d6E3F#&sEd6<FZ&F6E6dFs3sEP3FE6FZ6<F6*d#d6Z*#66<*<6*6Z#d*m6#*6<&s6&6Z0F060d6<066Z-P#d6-#-Z6<-6#d6Z)P)6#)Z&s6<&6)6Z61Z#Z6<#161d#d6Z1#6*<6<6*#d*m6Z#*66<&s6&*V6Z*6*d6<*6#d*h6Z#*66<*F6*#d6Z*k#6*&s6<6&6Z*P*6*d6<6*#d*d6Z#*6*<6<*6#d*m6Z#*66<&s&6*V6Z6**d6<*66Z#d*h*6#*F6<*6*k6Z#d*6#&s6<&6*P6Z6*6<*d6*#d*d6Z#*66<*<6*6Z*m#d6*#6Z&s&6*V6<6*6Z*d6*6<#d*h6#*6Z*F6**k6<#d*6#6Z&s&6*P6<6**d6Z*6#d*d6Z#*66<*<6*6Z#d*m6#*6Z&s&66<*V*6*d6Z*6#d*h6<#*66Z*F6**k6<#d#*6&s6Z6&6<*P*6*d6Z*6#d6Z*d#6**<6<*66Z#d*m*6#&s6<6&6Z*V*66<*d6*6Z*h#d#6*6<*F6*6Z#d*k6#*&s6<&6*P6Z6*6<*d6*#d6Z*s#6**<6<*6*m6Z#d*6#6<&s6&6Z*V6**s6<*6#d6Z*h#6**F6<6*6Z*k#d6*#&s6<&6*P6Z*66<.d6.6Z#d*d6#*6<*<6**m6Z#d6#*&s6<6&6Z*V*6*d6<*6*h#d6Z*#66<*F6*#d*k6Z*6#6<&s&6*P6Z6**d6<6*#d*d6Z#*66<*<6*6Z*m#d*#66Z&s&6*V6<6*6Z*d*6#d*h6<#*6*F6Z*6#d6<*k#6*6Z&s&6*P6<6**d6Z*6*d6Z#d#*66<*<6*6Z#d*m6#*6<&s6&6Z*V*66<*d6*#d6Z*h#6**F6<*6*k#d6Z*#6&s6<&66Z*P*66<*d6*6Z#d*s6#*6<*<6*6Z*m#d6*#6<&s&6*V6Z6**s6<*6*h6Z#d*6#*F6<*6*k6Z#d6#*&s6<6&6Z*P*66<.d.6*d6Z#d*6#*<6<*6*m6Z#d#*66Z&s&66<*V*66Z*d6*#d*h6<*6#*F6Z*6#d*k6<#*66Z&s6&6<*P*66Z*d6*#d6Z*d#6*6<*<*6#d*m6Z#*66<&s&66Z&d&6&s6<6&6Z&s&66<&d&6&s6Z6&6<&s&6&j6Z6&&s6<6&Ed1d3s6dFs#dEF631#FZ6<6F6d3s#dEdFsFE#36Ed&sFZ6<E6F&3sFs6dEP3E6FEdFZ6<F6E#d3s6dFsEd#F63EFZ6<6FFsEd#d3s6d3#EF6Ed6<FZ&sE&F63s6dEPFsF3E66<FZF66dEdFs3s#d6#3FEFZ6<3d63FEd3sFs#d6d3E6#F&s6<FZEdF6&E6dEPFs3s63FEFZ#ZEd6<#FE6#dFs6dEd3sF3#E6FZ6<6F3s6dFsEd#d63#EFEdFZ6<&sFE&66dFsEP3s36EF6<FZF66dFs#d3sEd63#FEFZ6<6F#d3s6dEdFsFE63#&s6<EdFZ&FE6EP3sFs6dE6F3FZ6<Ed6EF3s#dEdFs6d3FE#6FZ6<6F6dFs3s#dEd#3F6E6<FZEd&s6&EFFs3s6dEPEF63FZ6<6F#dFs6d3sEdE#36F6<FZF6EdFs6d3s#dE#36FEd&s6<FZ&EF66dEP3sFs6F3EFZEd6<E6FFs3s6dEd#d#FE63FZ6<6F#dFs3s6dEdE63F#FZEd&s6<F6&EEP6dFs3sE3F6#Z6<FZ6F#3s6dEd#dFs3F#E66<3dFZ3F63sFs#d6dEdF3E6#EdFZ&s6<&FE66dFs3sEP6E3FEd6<FZ#Z6EF#Fs#dEd6d3s#3F6E6<FZF6#dEdFs3s6dE#63F6<&sFZEdEF&63sFs6dEP6FE36<FZF6Fs#d6dEd3s3FE6#6<FZ6FEd6d3s#dFs36EF#6<FZ&sEd6E&F6dFsEP3sF63E#ZFZ6<Ed#6EF#d3s6dFsEdF63E#6<FZF6#dFs3sEd6d3F#6EEd&s6<FZEF6&3sFs6dEPFE366<FZF6Fs3sEd#d6d36F#EFZ6<6F#d6dFs3sEd6#E3F6<&sEdFZFE&6Fs6d3sEPFE366<FZEdFE66d3sEdFs#d3#6FEFZ6<F6Ed6dFs3s#d6E#3F6<&sEdFZE&6F6d3sEPFsF6E3FZ6<6F6Z#d*d6#*6<*<6**m6Z#d#*66<&s&6&d6Z6&&s6<&6&s6Z6&6<&d&66Z&s&66<&s6&&j6Z6&6<&s&6EdFs#d6d3s1d36#F1EFZ6<6FFsEd#d6d3s#EF36FZ&sEd6<F6E&EP6dFs3sF6E3FZ6<Ed6EF#dFs3s6dEd63FE#6<FZF6#d3sEd6dFs6E3#F&s6<EdFZ&FE63sFs6dEP6F3E6<FZ#ZF#66dEd#d3sFs#EF63FZ6<6F3s1P6dEdFs#d#FE6136<EdFZFE6EP6dFsEF66<Ed#d1d3dFZE#F136/MTrk tJAZ2 Muted Guitar@_{[\]_ 1{[\]_ 1{[\] 1]{[\__ 1{[\]_ 1{[\]_ 1{[\] 1`0N?00=00@A00A00NA00A0000A00N>00=004<00$0+N6++= ++@8++A+ +N8++A+ +<8++;+ 5S655A 55D855)5 2S622A 22D822E2 0N600= 00@800A0 0N800A0 0<800;0 0N700A"0 0A500A0 0N500= 004300= 00N600= 00@800A0 0N800A0 0<800;0 0N700A"0 0A500A0 0N500= 004300= 05S?55A55DA55E55SA55E55?A55>55S@55E55E>55E55S>55A557<55'50N?00=00@A00A00NA00A0000A00N>00=004<00$0+N6++= ++@8++A+ +N8++A+ +<8++;+ 5S655A 55D855E5 5S855E5 5?855%5 0N?00=00@A00A00NA00A0000A00N>00=004<00$00N?00=00@A00A00NA00A0000A00N>00=004<00=05S655A 55D855E5 5S855E5 5?855%5 4S?44A44DA44E4.N6..= ..@8..'. 2S622A 22D822E2 2S822E2 2?822>2 +N?+!0N600= 00@800A0 0N800A0 0<800;0 0N700A"0 0A500A0 0N500= 004300= 00N?00=00@A00A00NA00A0000A00N>00=004<00$00N600= 00@800A0 0N800A0 0<800;0 0N700A"0 0A500A0 0N500= 004300$ 00N600= 00@800A0 0N800A0 0<800;0 0N700A"0 0A500A0 0N500= 004300= 0+N6++= ++@8++A+ +N8++A+ +<8++#+ 5S655A 55D855E5 5S855E5 5?855%5 0N?00=00@A00A00NA00A0055S@55E55E>55E55S>55A557<55'50N600= 00@800A0 0N800A0 0<800;0 0N700A"0 0A500A0 0N500= 004300$ 05S?55A55DA55E55SA55E55?A55%54S?44A44DA44)43S633A 33D833E3 2S622A 22D822E2 2S822E2 2?822%2 +N6+*0N600= 00@800A0 0N800A0 0<800;0 0N700A"0 0A500A0 0N500= 004300$ 00N?00=00@A00A00NA00A0000A00N>00=004<00=05S?55A55DA55E55SA55E55?A55>55S@55E55E>55E55S>55A557<55'50N?00=00@A00A00NA00A0000A00N>00=004<00=0+N6++= ++@8++A+ +N8++A+ +<8++;+ 5S655A 55D855E5 5S855E5 5?855%5 0N?00=00@A00A00NA00A0055S@55E55E>55E55S>55A557<55'57S?77A77DA77E77SA77E77?A77>77S@77E77E>77E77S>77A777<77'75S655A 55D855E5 5S855E5 5?855%5 4S?44A44DA44E43S?33A33DA33E32S622A 22D822E2 2S822E2 2?822>2 +N6+*0S?00A00>A00800SA00>00?A00>00;N00;00;&0*0;.0j@/MTrkJAZ2][({ ^Z][(@ ^Z][({ ^Z][({ ^Z][({ ^Z][({ ^Z][({ ^Z Clean Guitar{[7;<;@;E;C@7E<7HACAF>C5;2265@;@>@C@"5>C;5F25(7K<<>@E@>E>?<@E7 7A<<>@E@>E>?@E<7 7A?>A>@9A? 9A?AAAA9?VA8?89199B??BAAB@A9? A,?,9'#9?5;9FA?FAF#?59AO7AACAE2528;5BC>;B>BCB>;52CTC'>,;,5' 5275A;;AC>>ACAE223C5>;5=;=>=C=>C52;TC1>8;8515C>9D;?DADG99@A??@A@$?A9u9L?LALG99BA??BAB?A9{0<7K:J@JCJHJFHC:7@7<:G@GCGHG:H7@C{9I?IAIG99S?A?SASA?9]A0?09*99P??PAAPG99FA??FAF9?A8H>H@HH>8@8G>G@G$8@8M>8X:K=KCKFKF::G=CF=GCGFG#:C=FE9OPCPI>C5;27I<>7677F<<77N7N@N5I;I>ICIE252F;>C5S;S>SCSC>25;]C*>0;05* 2D5;5P;P>>PCCPE22;C>;55F;F>FCF25;C>9H?HAHH?A99G?GAG(A?9q9N?NAN@9?A 9G?GAG#?A9F7O:J@JCJHJE7:7=:H@@HCHCHHHC7@H:TH3C;@;:3 @CH:P9Q?QAQA9?TA8?891A9?9L?LALG99J??JAAJ9A?TA0?09*99D??DAAD@A?9 9G?GAG9?ASA<?<9599H??HAAH@?9A A>?>9699L??LAAL9?AL0A7E:R@RHRCR5C@7:0H7?:K@KCKHKCH7:@|7::E@ECEHE>H7:C@ 7=:H@HCHHHHC7@:VH5C=@=:57>::I@@IHCCIHI>:@C7H H,C2@2:,":@0=CH7A:M@MCMHM#HC70@:O9H?HAHH9A?9G?GAG(A9?q9N?NAN@9?A 9G?GAG#?A9v8H>H@HH8@>8G>G@G$8@8M>8Q3>:ICI=IFI5FC=:3?6IIG66S><S<>6]>0<06*66P<>PG66F<>F6><2D5P;P>PCPI>;5C27A<<>E@@>E>FEE-<7@@4<47-#E@08<77F@F<<>@E@>E>?7@E< 7A??>AA>GAA4?9?49-$A?9089F?FAF&9?0A'049B?BABu9?0A#089F?FAFA9?0b7;<;@;E;CE@<77HDCDE2265;C5@>;@>@C@"C2>5;u2@5L;L>LCLE2528;C>5B;B>BCB>2;5C{5?9K?KAK55A?99D?DAD9?A|7AX@X@8>T@/>/8)8@>:K=KCKFKF::G=FC=GCGFG#C:=Fu9IPCPI5C>2;7J:J@JCJF77K::K@C@KCK:7@CTC-@4:47-77J::J@C@JCJF77F:C@:F@FCFC@:7TC7@?:?7777G::GC@@GCGFCC3:@7@;:;73#70C:C@7O:O@OCO&@7:C0'0?7K:K@KCKt07:@C@/MTrkq=JAZ2[({ "Brass@]f8KFEIAJCKEA KPENAMEAKEYKPAZ E AK!CDFZLJ?CFL FPLJCGFCLCJLWFfCFL$EK?BAH? EAP?%E@A*AE?PAFEF?F(AE?FC.F;?.[? CFDEZCDLJ?CEL CGEPLJECLLWCJEfCELLCHEDHH>ECH HKEDCKHE CXETHNCRHCE8Ca(EYC"ECH(<<C<kAJKFEICEKA ENKPAMEAKAZKPEY E AKAELLFCIDECLELLICLECLLPEXCP!ELC9OcCc2OC;CPMIGLBCGM CIMPGGGCMMSG\C]GCM]EFA@4EAEFA]AEJEKCH@B@ CEP@%E@C*CE@PEFCF@F(C@EpLDF1CHFFLCLDF[CRCFLaCBFDLDFCLWADKJEZ?AEK EPAGKJEAKKWAJEfAEKLLNFMCN8FCLLNCQFMFCLLQFcC^F CLFCH HKCKFDHF CXFTCRHNHCF8Ca(FYC"FCH(<<C<A@EF4EAEFA]AEHC.F;?.@CF?L@/MTrknJAZ2Fiddlev][n{ wH`BH KN&LIK>L MR&NSM9N?;=69512-/,*'&$# "OZ !"#$'&+(-/0247;9=?@.QZOWQO7O Ta{QRTGQKK#LOKRLOF$OQROQHR HKVOK H1 HJSDJ HUu?>=;:98654310/.-,+*('&%$#!   H  &:@ON(U@6?:B@ FA@@?U?SD?6CSBEC GLINP"R&T*V2Y8\<^Hd@`NgXlRi^ojudrpxv{z}~Cryz}^oxtPh4ZDb Q G@ EW>ECS)C??L?<'$<>NI> 9.9<@R<?L#?<6O< 909<@L<?O?F> W'VTG#WJT)WV;!VTD)TWM(V;WVT>#TWM&WV; VTF!TZ9#ZYEY WGW TJ!TR5,O7R#OP;!PQJ'TJQ#TVRDVTCT WK)XLWIXTPATOH&MFOGMH1HKNJKLN$LHLnHJRLJKM&KLCMLMR MOLRO?9<52/(,&$" QY" &$(/,416;9=>@6QOO.O?><;:8641,/(#%! QZ "%'+-0358:=?@&Q OQ3O>=<;9:86513/,(#&!    Q[   !#&,(/3186:?=@/QOSOO)JTBEIN Q4Z(U:]@`DbJePhRiXlZm^o`pdrfsbq`pZm^oVkPhLfDb<^4ZN&THA@/JHHmHJW_JJUbJJWGJEYBEHKO"R(U6[0X:]Ba>_HdDb @`Db:]4Z(UL Q GB@GECUr>=<:9865421./-+*('C&% '(*-,0.1357:>@/MTrk:JAZ2Melody Harmony (below)/MTrk7JAZ2 Octaves (no Patch)/MTrk:JAZ2[p]Jazz Guitar (melody) T{A3S#4L3.47@ 79K9W7G47 9NE9 7SX7 8U8 97E9[1>(99]J> << ?QY<<>GQ>ZH> W\><@F<6>`=>>L>>Q4>>Q>>N.> 9Ps9 7Zf7y3T*34F(7=4%79J9W7G47 9NE9 7SX7 8U8 97E9[1>(99]J> << ?QY<<>GQ>ZH> W\><@F<6>`=>>L>>Q4>>Q>>N.> 9Ps9 7Zf7y3T*4F3(7=4%79J9E$!!$C7$$?G$('J#'(4W+=(+ -DE-m,K, --E-0=02Q12(--0?V0#2SJ2 00 3GY002=Q20H30-GQ-+?-@+9-7'HW$' &B$Q&$:I$'K'(>7(>+E9+@+DH+.+FH+4+E<+0@(0+B:+)?%':)V$'$ &?K&$D8$,'H '(9K( +>"-E+1-60P10D3G13E5U?543GB3:5Wp5 3C?35S(5S0G 0T0;03K134>&3B4B34>"094B0+1#-G+K-++0ME0.2PH2 0GA0(080i3735P95:5KB577N<7:7R:7,06F062V=22B22G422G22D.2+Pf+y'J*'(<(+3(%+-@-0K:0?-KE-7+HH+-H -Y$/ $$>E$!!$C7$$?G$('J#'(4W+=(+ -DE-m,K, --E-0=02Q12(--0?V0#2SJ2 00 3GY002=Q20H30-GQ+?--@+9-7'HW$' &B$Q&$:I$'K'(>7(>+E9+@+DH+.+FH+4+E<+0@(0+B:+)?%':)V'$$ &?K&$D8$,'H '(9K( +>"+-E1-60P10D3G13E5U?543GB3:5Wp5 3C?35S(5S0G 0T0;03K134>&3B4B34>"094B0+1#-G+K-++0ME0.2PH2 0GA0(080i3735P95:5KB577N<7:7R:7,06F062V=22B22G422G22D.2+Pf+y'J*(<'(+3(%+-@-0P-0#0)(04:454B(5K4955P5 6B966P67PP9P7%;P92;W3<'T3'<'Wq'T'3'<'Wq,V'H,('L'V,KU,'Vq')VH)($L$V)KU)$Vq$"\T"QD)"XU""Sx"&Qr&'T3'<"Wr'N"p''Zi',\T,'QD'),XU,,Sx,,Qr,)\T)$QD$))XU))Sx)Q)r)"\T"QD)"XU""Sx&Q"r&'\T'"QD")'XU''Sx&Q'r&'W3'>'Z3'< W3 > Z3 <%^H%(,T,V%SU%,^q,W3<)Zq)"`T"Uv"Us""`T"Uv"Us"'`a')OW)+[d+,VH,('L'V,KU,'Vq'T3<$Wr)N$pZ)i"\T"Qv"Qs"'T3'>'W3'<,VH,('L'V,KU,'Vq',X3,<'[r R'p ,^i,)_T)$TD$))[U))Vx)!Tr!"_T"TD)"[U""Vx"&Tr&'X3'<"[r'R"p'^'i',_T,'TD'),[U,,Vx,T,r,)_T)$TD$))[U))Vx))Tr)"_T")TD))"[U""Vx"T"r"'_T'"TD")'[U''Vx&T'r&'W3'>'Z3'=,`T,'Uv,U's,%^H%(,T,V%SU%,^q,)Vp$Z)j$"`T"Uv"Us""gJB"'"\v"&\s&'W3'<'Zq,X'3,<'[r R'p,^ i,)_T)$TD$))[U))Vx!T)r!"W3">"Z3"='Vp"Z'j",_T,'TD'),[U,,Vx,+Tr+,\T,'QD'),XU,,Sx(Q,r()\T)$QD$))XU))Sx!Q)r!"\T")QD))"XU""Sx"Q"r"'T3'<"Wr'N"p'Z'i',\T,'QD'),XU,,Sx,(Qr()T3)<$WrN$p)Zi)"\T")QD))"XU""Sx""Qr"'VH'(.L.V'KU'.Vq.'W3'<'Zq' W3 <,Zq,%_T% TD )%[U%%Vx(T%r()W3)<Zq"`T"Uv"Us""W3">"Z3"='`T'"Uv"'Us',T3,<'Wr N'p ,Zi,)T3)<$WrN$p)Zi)"\J"7"'$Q"v$%Qs%']a')LW)",Xd,,Sp'W,j' T3 <,Wq,N,,S'N#'M,^O,! f /MTrkJAZ2{x][( @Drums`#<*P*#_&<*P&*_#<*P*#_&F*F*&7*<*#<*P#*_&<*P*&_#<*P*#_&F*F&*_#<*P#*_&<*P*&_#<*P#*_&F*F&*7*<*#2*P*#_&<*P*&_#2*P#*_&F*F&*7&Z*2*&#<*P*#_&<*P*&_#<*P*#_&F*F&*7*<*#<*P#*_&<*P&*_#<*P*#_&F*F&*7*<*#<*P#*_&<*P&*7*2*#<*P*#_&F*F*&7*<*#<*P*#_&<*P&*_#<*P#*_&F*F*&_#<*P#*_&<*P&*_#<*P#*_&F*F*&7*<*#<*P*#_&<*P*&_#<*P*#_&F*F&*7*<*#<*P*#_&<*P&*7*2*#<*P*#_&F*F&*_#2*P#*_&<*P*&_#2*P#*_&F*F&*7*2*#<&2*21<*#1&_*P&F*&_*P#<*#_&F*F*&7*P**P#<#*_*P&<&*_#<*P#*_&F*F&*_*P#<*#_*P&<&*_#<*P#*_&F*F&*7*<**P#<#*_*P&<&*_#<*P#*_&F*F*&7*<**P#<#*_*P&<*&7*2*#<*P*#_*F&F&*_*P#<*#_&<*P*&7*2*#<*P#*_*F&F&*_*P#<*#_&<*P*&_#<*P#*_&F*F*&7*<*#2*P*#_&<*P&*7*2*#2*P#*_&F*F*&7*2*#<1F&<1#&_3P&F&3733#F3<3#_&P3F3&7333<#2#3_&<3F3&73(3#<3F3#_3<&F&3733#23<#3_&<3F&37333F#<#3_3<&F&3733#23<#3_3F&<3&73(3#<3F#3_&F3<&37333<#23#_3F&<3&733#<3F3#_&F3<3&7333<#2#3_&<3F3&73(3#<3F#3_&F3<3&733#<3F#3_&F3P3&7333F#F3#_&F3P3&73(3#<3F#3_3P&F3&733#<3F#37&F&&Z3P3&73(&F3&*P#<#*_&<*P*&_#<*P*#_*F&F&*7*(**P#<*#_&<*P&*7*2**P#<#*_*F&F*&7*<*#<*P#*_&<*P*&_*P#<#*_*F&F&*_#<*P*#_&<*P*&_*P#<#*_*F&F*&7*<*#<*P*#_*P&<&*7*2**P#<*#_&F*F*&_*P#<#*_*P&<*&_#<*P*#_&F*F&*7*<**P#<#*_*P&<&*_#<*P#*_&F*F&*7*<**P#2#*_&<*P&*7*2*#2*P#*_*F&F*&7*2&Z*&#<&<1F#1&_3P&F3&7333<#F3#_&P3F&373(33F#<#3_&F3P&3733#F3F#3_&F3P3&73(33F#<#3_&F3P3&73(3#F3F3#_3P&F3&733#23<3#_&<3F&373(33F#<3#_&F3<&3733#<3F3#_3P&F&373(3#F3F3#_&F3P&373(33<#2#3_3F&<3&73(3#<3F3#_&F3<&37333<#2#3_&<3F&373(33F#<#3_3<&F3&733#<3F3#70203P&F&370<3033F-<3-7-<-&P3P3&7-P3-3#<&<1F&#1_&F3P3&73(33<#F3#_3F&P&373(3#<3F3#_3P&F3&73(33F#F3#_&F3P&37333F#<#3_&F3P&3733#F3F#3_&F3P&3733#<3F3#_3P&F3&733#F3F#3_3P&F3&73(33<#2#3_&<3F&373(3#<3F3#_3<&F3&733#23<3#_3F&<3&73(33F#<#3_&F3<&373(33F#<#3_3P&F3&73(3#F3F#3_3P&F3&73(33F#<3#7020&F3P3&70<3303F-<3-7-<-&P3P3&7-P3(-31F&<#<1&#_&F3P&373(33<#F3#_&P3F3&73(3#<3F3#_&F3P&373(3#F3F3#_3P&F3&733#<3F3#_3P&F3&7333F#F3#_&F3P3&7333<#2#3_&<3F&3733#<3F#3_&F3<&37333F#<#3_&F3P3&73(3#F3F3#_3P&F3&73(3#23<3#_&<3F&373(33F#<#3_&F3<&373(3#23<3#_3F&<3&73(3#<3F#3_&F3<&373(33F#<3#7020&F3P&373(0<033F-<3-7-<-3P&P&373(-P3-#<1F&<#1&_3P&F3&73(3#F3<3#_&P3F&37333F#<#3_&F3P3&733#F3F3#_&F3P&3733#23<#3_&<3F&373(3#<3F#3_&F3<&373(3#<3F3#_&F3P3&733#F3F3#_&F3P&373(3#23<#3_&<3F&3733#<3F#3_&F3<3&733#23<3#_&<3F&373(3#<3F#3_&F3<&373(3#<3F3#_&F3P3&73(33F#F#3_&F3P&373(3#<3F3#_3P&F3&733#<3F3#7&F&&Z3P3&73(3&2*21<#<&#1*_&F*P*&_*P#<*#_*F&F*&7*P*#<*P*#_*P&<*&_*P#<#*_&F*F&*7*<*#<*P*#7*(**P&<&*_#<*P*#_&F*F*&7*(**P#<*#7*(*&<*P&*_#<*P*#_*F&F&*_*P#<#*_&<*P&*_#<*P*#_*F&F*&_*P#<*#_&<*P&*7*2*#<*P#*_*F&F*&_*P#<#*_&<*P*&_*P#<*#_*F&F*&7*(*#<*P*#_&F*P&*_*P#<#*_*F&F*&7*P*#<*P#*_*P&<*&7*2*#<*P*#_&F*F*&7*<**P#<#*_*P&<*&7*2*#<*P*#_&F*F&*7*(**P#<*#_&<*P*&_#<*P*#_*F&F&*_*P#<#*_&<*P*&_#<*P#*_*F&F&*_*P#<#*_&<*P&*7*2*#<*P*#_*F&F&*_*P#<#*_&<*P*&_*P#<#*_&F*F&*_*P#<*#_&<*P*&_*P#<#*_&F*F*&7*<*#<*P*#_*P&F*&7*F**P#<#*_&F*F&*7*P*#<&<1F&1#_3P&F3&733#F3<3#_3F&P3&73(33F#<3#_&F3P&373(33F#F#3_&F3P3&73(3#23<3#_3F&<3&73(3#<3F3#_3<&F3&73(33<#23#_&<3F&37333F#<#3_&F3<3&733#<3F3#_3P&F3&733#F3F3#_3P&F3&7333<#23#_&<3F3&73(3#<3F3#_3<&F3&73(3#23<3#_3F&<3&73(33F#<#3_&F3<&37333F#<#3_&F3P&3733#<3F#37&F&3P&Z&37&F33&*2#<&21<*&1#_&F*P*&_#<*P*#_*F&F*&7*P**P#<#*_&<*P*&_#<*P*#_*F&F&*7*<*#<*P*#_&<*P&*7*2**P#<#*_*F&F*&7*<*#<*P*#_*P&<&*_*P#<#*_&F*F&*7*<**P#<#*_&<*P*&_*P#<#*_&F*F*&7*<**P#<#*_*P&<*&7*2*#<*P*#_*F&F*&7*<**P#<#*_*P&<&*_*P#<*#_*F&F*&7*<**P#2#*_*P&<*&7*2**P#2*#_*F&F*&7*2&Z&*3F#<#3_&F3P&37333F#<3#7&F&&Z3P&37&F3(&3#P*P#*7#F#*P&F*&_&212#P*P*1#&/MTrk JAZ2_[]Piano{ O@dF9,%<1?GNF?,< :L3;73:71C9,%?;81=.5<8 ==:A5;8C.0N8:.5==F:@30'+7EN3':=7?A:8I<1F:!<F><;FL7<F?:?=FL<;7<F?:C;F50<@.+8EN<58.>=F50:@8E.+N=8:5. =;7I';30:?N@L3:'=7@F=<;?C,0.?<,F8K33),8?CHSKRF],7FH K ,?@/MTrkpJAZ2Z][<{ 'Z][<{ 'Z][<{ 'Z][<{ZZ '[<{ '][<Z 'Guitar]][# '@{{O338=<=?=D=D3<?8N388B@=$66@:8`4b A5A6 4*5 8&8V6b3Y836875Z8ZO58&3i8eI38'5F@K87,:L 8D8E+:.@@6d805:6H58V@58":ZV:'C_D5BVC,BAJ*DP,AENDFRE6MNBPYM7O_P5NVO,NMJ.PDFM%P&MOMM1K8sMRK7K MPPKIP)MQHKOM K3HZ6F=HFGDW=DFc1K:FrM[K6K% MKOQPFOCP P`HPM[0PZM3YX*W@YlYm-W4YPS\YW#R0 S$PJYRR^#PRT\7PFT9PMC#@OcMPIOLMcPGHY M#DgH*AQO@ =O#:B A =@=U:Dc@=?L@ A??=Aq=a.=<&$:0<)AN :?)A=6?F^=A8F%?=A$ALFH?C)A2?FCFOAT ?DAF FI?@@@@0? FP=aJ?Y+=+AT%?*ADUGFQI:FAO0=5A:=1@ =QFH?= F*@?^HH$?HIWAG@'A I;HK?U$?=NFDHR= FLb MWLJMLcMQLMOv.OKR_3OH R%OGK7IK@KQFK@ FINFGK@3@-FI%@IgFFQO@ BO#?B F B@?IBDK)?&=[ 8?8T==8:i@8NAK :@=`DY8=AD5Bk:Y+:3P8QB5>35 83U8N/0738Q@1^8W03= 1F38@ 5\=dD@@:5=2?XAJ!?cA :V;P!:;89.8:^:\8r;8 7k57 5W352S3@ 1Q:H2;1 :*@3^C_D5BVC,BAJ*DP,AENDFRE6MNBPYM7O_P5NVO,NMJ(Pk FM)P%PaWP PdOP^ P4P0PcIP2PaVP"Pa1PPUcPP`cPRJOP/R RKWFSFPTDSRTRDWW?S\ TWSRR>T`PK'R!@@&PKO7 W MEOKDM&KRGPQRO7 P8T.R:OT PFRPAMs)MKU3KHI?F? H-D FDSAs)A?U3?C'HC;Aa3AL?3(?AeBA?Y-;`?":5;!8=:":c?U818: ? 8?G:CP8H ?@ :8n@ B`DDA'B D?:yA[?6?% A?CQDFCCD D`HDA[7DUA-C_D5AFC*C_ A@KQFRCH1F F3H D: K F@@YD1Dc?\ AF?+DA$?UDU@D?;DU?7AF?$A DDZ?Z@@(?D7_856V7,65J*8P,59N8:R96ANBDYA7C_D5BVC,BAJ)D>:A%D'D=o?7OA/ D?D?AFFEDD=FMDD@pD/MTrk<JAZ2Journey to Times Gone By/simutrans-124.5/simutrans/music/34-flyingaway.mid000066400000000000000000000150241520675460500220240ustar00rootroot00000000000000MThdMTrkw By Copyright 2007 All Rights Reserved Generated by NoteWorthy ComposerQ 'Q '/MTrk<!StaffI @JnPJ0Ln^LJn^JEn^EGn^GHn.HGn.GHnpH0Jn^JHn^HCn^CEn^EGn.GEn.EGn0G0Jn J OnO0Sn S QnPQ0JnPJ0Ln^LJn^JEn^EGn^GHn.HGn.GHnpH0Jn^JHn^HCn^CEn^EGn.GEn.EGn0G0Jn J OnO0Sn S TnPT0OnONn^NOnPO0OnOOn^OQnPQ0JnPJ0Ln^LJn^JEn^EGn^GHn.HGn.GHnpH0Jn^JHn^HCn^CEn^EGn.GEn.EGn0G0Jn J OnO0Sn S QnPQ0JnPJ0Ln^LJn^JEn^EGn^GHn.HGn.GHnpH0Jn^JHn^HCn^CEn^EGn.GEn.EGn0G0Jn J OnO0Sn S TnPT0OnONn^NOnPO0OnOOn^OQnPQ0OnONn^NOnPO0OnOOn^OQnPQ/MTrk!Staff-2I @GnPG0Hn^HGn^GBn^BCn^CCn.C@n.@CnpC0Gn^GEn^E@n^@Bn^BCn.C>n.>Cn0C0Gn G JnJ0On O NnPN0GnGGn^GHnPH0HnHHn^HJnPJ0GnPG0Hn^HGn^GBn^BCn^CCn.C@n.@CnpC0Gn^GEn^E@n^@Bn^BCn.C>n.>Cn0C0Gn G JnJ0On O NnPN0GnPG0Hn^HGn^GBn^BCn^CCn.C@n.@CnpC0Gn^GEn^E@n^@Bn^BCn.C>n.>Cn0C0Gn G JnJ0On O NnPN0GnGGn^GHnPH0HnHHn^HJnPJ0GnGGn^GHnPH0HnHHn^HJnPJ/MTrk&!Staff-4! @(n((n^((n(&n^&$n$$n^$$n$$n^$$n$$n^$$n$$n^$&n&&n^&&n&&n^&nn^nn^nn^nn^$n$$n^$$n$n^nn^nn^&n&&n^&&n&&n^&nn^nn^$n$$n^$$n$n^nn^nn^&n&&n^&&n&&n^&(n((n^((n(&n^&$n$$n^$$n$$n^$$n$$n^$$n$$n^$&n&&n^&&n&&n^&nn^n&n^&(n((n^((n(&n^&$n$$n^$$n$$n^$$n$$n^$$n$$n^$&n&&n^&&n&&n^&nn^nP/MTrk v!Staff-1 @7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>0n^07n^7;n^;<7n>7<7n>7n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>2n^26n^67n^79n>97n>7>n^>2n^26n^67n^79n>97n>7>n^>7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>0n^07n^7;n^;<7n>7<7n>7n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>2n^26n^67n^79n>97n>7>n^>2n^26n^67n^79n>97n>7>n^>/n^/4n^47n^7;n>;7n>7;n^;/n^/4n^47n^7;n>;7n>7;n^;0n^07n^7;n^;<7n>7<7n>7@<@n^@4n^47n^7@<@n^@2n^26n^67n^79n>97n>7>n^>2n^26n^67n^79n>97n>7>n^>7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>0n^07n^7;n^;<7n>7<7n>7n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>2n^26n^67n^79n>97n>7>n^>2n^26n^67n^79n>97n>7>n^>7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>0n^07n^7;n^;<7n>7<7n>7n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>2n^26n^67n^79n>97n>7>n^>2n^26n^67n^79n>97n>7>n^>/n^/4n^47n^7;n>;7n>7;n^;/n^/4n^47n^7;n>;7n>7;n^;0n^07n^7;n^;<7n>7<7n>7@<@n^@4n^47n^7@<@n^@2n^26n^67n^79n>97n>7>n^>2n^26n^67n^79n>97n>7>n^>7n^7;n^;n>>;n>;>n^>7n^7;n^;n>>;n>;>n^>/n^/4n^47n^7;n>;7n>7;n^;/n^/4n^47n^7;n>;7n>7;n^;0n^07n^7;n^;<7n>7<7n>7@<@n^@4n^47n^7@<@n^@2n^26n^67n^79n>97n>7>n^>2n^26n^67n^79n>97n>7>n^>7n^7;n^;n>>;n>;>n^>CnPC/MTrk!Staff-3 @#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&0#n# #n^#&nP&09nP9/simutrans-124.5/simutrans/music/35-deep-ride.mid000066400000000000000000002014251520675460500215130ustar00rootroot00000000000000MThdxMTrkCopyright (C) 2007 shunter~ JAZ2njvj  FK@@ (  L  ~j'*0$*$*$ * ~j(**L *@S@$*  'FK@@~j 0$ $ L  0$ $*$~j *0**L*@S@$* '~jFK@@'*(  L   ( ~j    (  L  @S@  j'XQY Deep Ridec @[(]/MTrk Q JAZ2][{@ [BassZpR[]8TV8][["x"x$Z$+m+ $P$($P$(#P#(#x#'Z'.m. )x$)x"$x"x$Z$+m+ 'x"'x".""$C$ $L("$Z")m) $x$x'Z'.m. $Z$+m+ . " L" "Z"$Z$+m+ "Z")m) $.$"$"$+C+ +($+x$x$x$x'Z'.m. )x$)x$"x"x$Z$+m+ 'Z'.m. )x)$x"$Z")m) $Z$+m+ O[\[Z$m$ 'x'$x&$Z&)m) $Z$+m+ Z$m$ )x)$x'$Z'.m. "Z"#x#$Z$+m+ !Z!(m( Z 'm' +x+&x*&P*(!P!(*P*(!P!()x$)x+$x+&x&"Z")m) $Z$+m+ $x$x$x$x'x'"x)"x)$x"$x"x$Z$+m+ 'x'"x"Z$m$ "x"x$Z$+m+ P[W8^".""""")C) )()$x$x$P$(+P+($x$+P+('Z'.m. )x$)x"$.""""")C) )L($)x$x'x"'x"Z$m$ "x"x$x$x'x"'x$".$"$"$+C+ +(,+x",x$"Z$+m+ "Z")m) $x$x$x$x$x$x'Z'.m. )x$)x"$Z")m) $Z$+m+ 'x"'x"Z$m$ "Z")m) $Z$+m+ M[Q[)x$)x'$.'"'"'0C0 0L(&0Z&)m) $Z$+m+ .""$C$ $()$x$)x$'x"'x""Z"#Z#$Z$+m+ !x!x Z 'm' +x&+x&*P*(!P!(*P*(!P!()x$)x$Z&m& ".""""")C) )L()$Z$+m+ $x$x$x$x'Z'.m. Z$m$ "Z")m) $Z$+m+ 'Z'.m. )x$)x"$x"x$Z$+m+ [[S["Z")m) $Z$+m+ $P$(+P+($x+$P+('Z'.m. Z$m$ "x"x$x$x'Z'.m. )x$)x$"x"x$Z$+m+ 'Z'.m. $Z$+m+ ,x",Z"$x$x"Z")m) $Z$+m+ $x$x$x$x'Z'.m. Z$m$ "Z")m) $x$x'Z'.m. )x$)x$"Z")m) $Z$+m+ \8MP8XZ$m$ 'x$'x$&x&x$x$x)x$)x)$x$)x$'x"'x""."""L""#Z#$.$"$"$+C+ +(!+x!x,x,'x+'Z+2m2 *P*(!P!(*P*(!P!(Z$m$ Z&m& "Z")m) $Z$+m+ $P$($P$(#P#(#x'#Z'.m. Z$m$ "x"x$Z$+m+ 'Z'.m. )x$)x"$x"x$Z$+m+ X[M[$P$(+P+($x+$P+($Z$#2$D$/MTrk=JAZ2{s]Drums[ @p$*@P**+(&*@$*P**+(*$*@(&(*+*($**@&P**+($&**@P*+*($&**@P**+($**@(&(**+(*@*$&P**+(*@$&*P*+*(*@&$*P*+*($*@*(&(*+*(&*$*@P*+*(&*$*@P**+(&$**@P*+*($**@(&(&*+*&$*@&*&;&*+*&$**@P*+*(*@*$&P*+*(*@*$(&(**+(&*$*@P**+($*&*@P*+*($*@&*P**+($*@*(&(&**+&*$&*@&;*&*+&*$*@P**+(&$*@*P*+*(*@$*(&(*+*(**@$&P**+($**@&P**+(*@*$&P*+*($**@(&(**+(&$**@P**+(*@&*$P**+(&*$*@P*+*($**@(&(*+*(*@$*&P**+(*@*&$P**+(&*$*@P**+(*@*$(&(**+(*@*&($(**+($&**@P**+(*@*&$P**+($**@(&(**+(&*$*@P*+*($&*@*P*+*(*@$*&P**+(*@*$(&(**+&&*@*$&&;&*+*&$**@P*+*(&$**@P*+*($&**@P*+*(*@$*&P**+(*@*&$P*+*(*@$*&P**+(*@*$(&(*+*(**@&($(**+($&**@P*+*(&$**@P*+*(*@*$(&(**+(&$**@P**+($&**@P*+*(*@$*&P*+*(*@*$(&(*+*(&*$*@P**+(*@*&$P*+*(&*$*@P*+*(*@*$(&(*+*(&*$*@P**+($&**@P**+(&*$*@P**+($**@(&(*+*(*@*&($(*+*(*$&*@P**+(*@*$&P*+*($*&*@P*+*(*@$*&P**+($&**@P**+(*@*$&P*+*($**@(&(**+&&*@*$&&;**+&&$**@P**+(*@*$&P*+*($**@(&(**+(*@*$&P*+*($*&*@P*+*(&$**@P*+*(*@*$(&(**+(*@$*&P**+($*&*@P**+(*@*$&P**+(*@&*$P**+(&$**@P**+($&**@P**+(&*$*@P**+($**@(&(**+(&$*@*P*+*(*@&*$P*+*(&*$*@P**+($**@(&(**+(&*$*@P*+*($&**@P**+(*@$*&P**+($**@(&(**+(&**@($(*+*(*@&*$P**+(*@$*&P**+(*@&*$P*+*(&$**@P**+(*@&*$P**+(&*$*@P**+($**@(&(**+(&$**@P*+*(*@&*$P**+(&$**@P**+($&**@P**+(*@*$&P**+(*@*&$P**+(&*$*@P*+*($**@(&(*+*(&$**@P*+*($&**@P**+(&*$*@P**+(*@*$(&(**+(&$**@P**+(*@&*$P*+*(*@*$&P*+*(*@*$(&(**+(*@$*&P**+($&**@P**+(&*$*@P*+*($**@(&(**+(*@*$&P*+*(&*@$*P**+(*$*@&P*+*(*@$*(&(**+(&*$*@P**+($*&*@P*+*($*@*&P*+*($**@(&(*+*(*@$*&P**+(*@*&$P**+(*@*$&P*+*(*@*$(&(*&*+&&*$*@&;*+&*&*@*$P**+(*@$*&P**+(*@&*$P*+*(&$**@P**+($&**@P**+(*@$*&P*+*(*@*$(&(**+(*@*&($(**+($*&*@P**+(*@*$&P**+($*&*@P**+(*@*$&P**+(*@*&$P**+(*@*$&P**+(*@*$(&(*+*(*@*&($(**+(*@*&$P**+(*@*$&P*+*(*@*$(&(**+(*@*$&P**+(*@*&$P**+(*@*$&P*+*(*@*$(&(*+*(*@$*&P**+(*@&*$P**+(*@$*&P*+*(*@*$(&(**+(*@$*&P**+(*@*&$P**+(&$**@P*+*($**@(&(*+*(&**@($(*+*($*&*@P**+(&*$*@P**+(*@*$(&(**+(&*$*@P**+(*@&*$P**+(&*$*@P*+*($**@(&(*+*(*@*$&P**+(*@&*$P*+*(*@$*&P**+($**@(&(*+*(*@*$&P**+(*$&*@P**+(&*$*@P**+(*@*$(&(**+(*@*$&P**+(*@*&$P**+(*@*$&P**+(*@&*$P**+(*@*$&P**+(*@&*$P**+(*@*$&P*+*($**@(&(**+(&**@($(*+*(*@*&$P**+(&*$*@P*+*(*@*$(&(**+(&*$*@P*+*(*@*&$P**+(&$**@P*+*(*@*$(&(*+*(&**@($(*+*(*@*&$P**+(&*$*@P*+*(*@*$(&(**+(*@*$&P**+($*&*@P**+(&$**@P*+*($**@(&(*+*(*@*$&P**+($*&*@P*+*(*@*$&P*+*(*@*$(&(**+(*@*$&P**+(*@*&$P**+(*@$*&P*+*(*@*$(&(**+(*@*$&P**+(*@*&$P**+(&*$*@P*+*($**@(&(*+*(*@*$&P**+($*&*@P**+(&$**@P*+*($**@(&(*+*(&*$*@P*+*(*@*&$P**+(*@*$&P*+*(*@*$(&(**+(&*$*@P*+*(*@*&$P**+(*@*$&P*+*(*@*$(&(**+(&*$*@P*+*(*@*&$P**+(*@*$&P*+*(*@*$(&(**+(&*$*@P*+*(*@*&$P**+(&$**@P*+*($**@(&(**+(&**@($(*+*($*&*@P*+*(*@*$&P*+*(*@*$(&(**+(*@*$&P**+(*@*&$P**+(&$**@P*+*($**@(&(*+*(*@*$&P**+(*@*&$P**+(*@*$&P*+*($**@(&(*+*(&*$*@P*+*($*&*@P**+(*@$*&P*+*($**@(&(*+*(&**@($(**+(*@&*$P**+(&*$*@P*+*(*@*$(&(*+*(&$**@P*+*($*&*@P**+(*@*$&P*+*($**@(&(*+*(&**@($(*+*($*&*@P**+(*@*$&P*+*(*@*$(&(*+*(*@$*&P**+(*@*&$P*+*(&$**@P*+*(*@*$(&(**+(*@*$&P**+($*&*@P*+*(*@*$&P*+*($**@(&(**+(&*$*@P*+*(*@*&$P*+*(*@$*&P*+*(*@*$(&(**+(*@*$&P**+(*@*&$P*+*(*@$*&P**+(*@*&$P*+*(&$**@P*+*($*&*@P**+(*@*$&P*+*(*@*$(&(*+*(&**@($(*+*($*&*@P**+(*@*$&P*+*(*@*$(&(*+*(&$**@P*+*(*@*&$P*+*(*@$*&P*+*($**@(&(**+&&*@*$&&;**+&&*@*$P**+(*@*$&P**+($**@(&(*+*(&*$*@P**+(*@&*$P**+(*@*$&P*+*(*@*$(&(**+(&*$*@P**+(*@&*$P**+(&*$*@P**+($**@(&(*+*(*@*$&P**+($&**@P*+*(*@*$&P*+*($**@(&(*+*(&*$*@P**+(*@&*$P**+(&*$*@P**+(*@&*$P**+(&*$*@P**+($&**@P*+*(*@*$&P*+*(*@*$(&(*+*(&**@($(**+(*@&*$P**+(*@*$&P**+(*@*$(&(**+(&*$*@P**+(*@&*$P**+(*@*$&P**+(*@*$(&(&*+*&*@*$&&;&**+&$**@P*+*(*@*$&P*+*(*@*$(&(**+(&*$*@P*+*($*&*@P**+(*@$*&P*+*($**@(&(*+*(*@*&($(**+(*@*&$P**+(&*$*@P*+*(*@*$(&(**+(*@*$&P**+(*@*&$P**+(*@$*&P*+*($**@(&(*+*(&*$*@P*+*($*&*@P*+*(&*$*@P*+*($**@(&(*+*(&*$*@P*+*($*&*@P**+(&$**@P*+*(*@*$(&(**+(&*$*@P**+(*@&*$P**+(&*$*@P*+*($**@(&(**+(&*$*@P*+*($*&*@P**+(*@*$&P*+*(*@*$(&(**+(&*$*@P*+*(*@*&$P*+*(*@$*&P**+($*&*@P**+(*@*$&P**+($*&*@P**+(&*$*@P*+*($**@(&(*+*(&*$*@P*+*($*&*@P**+(*@*$&P*+*($**@(&(**+(&*$*@P*+*(*@*&$P*+*(&$**@P*+*(*@*$(&(*+*&&&*$*@&;**+&&*@*$P**+(*@*$&P**+(*@*$(&(**+(*@*$&P**+($&**@P*+*(*@*$&P*+*($**@(&(*+*(*@*$&P**+(*@*&$P*+*(*@$*&P**+($*&*@P*+*(*@*$&P**+($*&*@P*+*(&*$*@P*+*(*@*$(&(&**+&&$**@&;*+&*&$**@P**+(*@$*&P*+*(*@*$(&(*+*(&*@*$P*+*(&*@$*P*+*(*@&$*P*+**n&**/MTrkDJAZ2][ { <ZPiano@p@Z+Z>Z7Z7>@9>Z7Z@Z7>@7Z@Z>Z+7>@5>Z7Z+Z@Z7>@7Z>Z@Z7>@+1+Z@Z7Z>Z>7@@Z>Z7Z7>@ ++7Z>Z@Z+Z>7@+9Z+Z>Z<92>Z< Z<9>52>Z9Z5Z< >Z+92>Z9Z+Z+9 9Z5Z>Z<2Z9Z5Z<>95 0ZJZLZCZLJC8LZCZJZ0LJCCZLZJZ$ZLJC;JZCZLZ$CJLJZLZ0ZCZLCJ>LZJZCZ 0LJCCZJZ$ZLZL CJ;$CZLZJZ LCJ0ZCZLZJZLCJ=JZCZLZLJ0CJZLZ$ZCZJLC4$CZLZJZLJCLZCZ0ZJZLJC;CZJZLZLJ0CLZJZCZ$ZLCJ5$ J6L6C6 JCLJZ2ZAZFZAJF/JZFZAZ 2JAFAZ.ZFZJZJFA4AZJZFZ . JAF7ZZ5Z.Z>5:..Z5Z>Z:Z:>5.>Z:Z5Z)Z>5: ):6>656)6>:5 ) @Z7Z0Z@7 0.0Z@Z7Z@7 00Z@Z7Z7@00Z@Z7Z @77Z:Z3Z0?Z7?:3 3Z?Z:Z7Z7?: 3"Z:Z?Z7Z7?:""7Z?Z:Z"Z :7?"5ZZ5:>9>Z:Z5Z:5>:Z5Z>Z.5:>556:6>6.65:>HZCZ4Z.LZCLH/CZLZHZ 4LCHHZCZ0ZLZLHC4HZCZLZ 0 LCHCZ'ZKZFZKFC8CZFZKZ'KFCCZKZFZ'ZKFC;F6C6K6'CFK@ZZ5Z5:>9>Z5Z:Z5:>>Z5Z:Z.5:>5.6:6>6565:>@ZZ.Z> :5 .">Z5Z:Z.Z >:5.:Z>Z5Z)Z>5:))Z:Z>Z5Z>5:)LZ4ZHZCZCLH/CZHZLZ 4LCHHZLZ0ZCZLHC4HZLZCZ 0 LCHCZ3ZFZKZCKF/CZFZKZ 3KCFFZ'ZCZKZKFC4K6F6C6 ' KCFZ.Z5:>9:Z>Z5Z5:>>Z5Z:Z.5:>556>6.6:65:>7Z@Z.Z.Z>5:..6>6:656:>5.4Z@Z7ZZ:Z5Z.Z>5:..Z:Z>Z5Z:>5.;Z3Z?Z6Z6?;3 36;666?66?; 3@Z7ZZ +">Z<+CZZ&ZC<>&&ZCZ>Z&9Z6ZZ5Z.Z>5 ...Z>Z5Z>5 ..Z>Z5Z5>..Z5Z>Z >5CZHZ0Z.LZLHC8LZCZHZ0LHCLZ$ZCZHZLHC;LZHZCZ$CHL@Z7ZZ5Z.Z>5 ...Z>Z5Z>5 ..Z5Z>Z5>..656>6 >5@ZZ)5Z5:>9>Z:Z5Z:5>>Z:Z5Z.5:>5:6>656.65:>@Z7Z.0Z@7 0.0Z@Z7Z7@ 00Z7Z@Z7@006@676 @7KZFZCZ0+ZKFC8CZFZKZ+KFCKZ+ZFZCZKFC;CZKZFZ+CFKZ5Z.Z5:>95Z:Z>Z5:>7Z0Z@Z.Z.Z>5 ...Z5Z>Z5> ..Z5Z>Z5>..656>6 >57ZZ)5Z.Z>5 ...Z>Z5Z>5 ..Z>Z5Z5>..Z>Z5Z >5@Z7Z.0Z@7 0.0Z@Z7Z7@ 00Z7Z@Z7@00676@6 @7'ZCZKZ0FZKFC8CZFZKZ'KFCCZFZKZ'ZKFC;KZCZFZ'CFK@ZZ:Z.Z> :5 .":656>6.6 >:5.LZ4ZHZCZCLH/HZLZCZ 4LCH0ZLZCZHZLHC4LZCZHZ 0 LCH:Z5Z>Z.Z> 5: .">Z:Z5Z.Z >:5.5Z:Z>Z)Z>5:))Z>Z5Z:Z>5:)LZHZ4ZCZCLH/CZLZHZ 4LCH0ZHZLZCZLHC4CZLZHZ 0 LCHHZCZ4ZLZLCH=HZCZLZ4 LHCHZLZCZ0ZLCH9HZCZLZ 0CLHHZ4ZCZLZCLH=CZHZLZ 4LCHHZCZLZ0ZLCH70C6H6L6LHC?Z:Z7Z'Z? :7 '"?Z7Z:Z'Z ?:7'7Z?Z:Z"Z?7:""676?6:6?7:"9ZAZZ5Z.Z> 5: ."5Z>Z:Z.Z >:5.:Z5Z>Z)Z>5:))6:6>6565>:)Z.Z5Z:Z5:>95Z:Z>Z5:>Z2Z> 5: 2":Z5Z>Z2Z >:52>Z5Z:Z)Z>5:))6:656>6>5:)Z:Z.Z>5:..Z>Z5Z:Z:>5.KZBZGZ3ZBKG/G6K6B6 3KBGHZ0ZLZCZLHC8CZHZLZ0LHC$ZCZHZLZLHC;L6H6C6$CHLZ++Z>ZC<+>ZCZ &ZCZ&ZC>< & 9Z6ZBZZ.Z> 5: .">Z:Z5Z.Z >:5.>Z5Z:Z)Z>5:))Z:Z5Z>Z5>:)@ZZ:Z5Z5>:2 2Z:Z>Z5Z5>: 2)Z5Z:Z>Z5>:)":656>6)6 :5>)@ZZ5Z.Z> 5: .">Z:Z5Z.Z >:5.Z:Z.Z>5:..Z:Z>Z5Z:>5.5Z>Z:Z)Z>5: )5Z:Z>Z)Z>:5 ) CZLZ0ZHZLHC8HZCZLZ0LHCCZHZ$ZLZLHC;L6C6H6$CHL7Z?Z'Z?7 '.'Z?Z7Z?7 ''Z?Z7Z7?''6?676 ?7HZMZEZ'5ZEMH/MZEZHZ 5MEHHZ)ZEZMZMHE4M6H6E6 ) MEH.Z>Z:Z5Z5:>9>Z:Z5Z:5>:Z5Z>Z.5:>5:Z.Z>Z5Z5:>@Z7Z.Z.Z5Z:Z5:>9>Z5Z:Z5:>5Z:Z>Z.5:>55Z:Z.Z>Z5:>@Z.7Z0Z@7 0.0Z@Z7Z7@ 00Z@Z7Z7@006@676 @7:Z7Z0?Z'Z?7:''Z7Z:Z?Z:?7':Z?Z7Z"Z?7: ":6?676"6?:7 " MZEZHZ)ZMHE8MZEZHZ)MHE)ZMZEZHZMHE;E6M6H6)EHMFZAZJZ.ZJFA8AZJZFZ.JFAAZ"ZJZFZJFA;FZJZAZ"AFJ@ZZ5Z2Z>5 2.2Z5Z>Z>5 22Z5Z>Z5>226>656 >5HZLZ0Z2CZLHC8CZHZLZ0LHC$ZHZLZCZLHC;H6C6L6$CHLHZAZEZ)ZHEA8EZAZHZ)HEA)ZHZAZEZHEA;H6E6A6)AEHHZ)ZEZMZMHE8EZHZMZ)MHEHZ)ZMZEZMHE;M6H6E6)EHM7Z?Z'Z?7 '.'Z7Z?Z?7 ''Z7Z?Z7?''Z?Z7Z ?7AZ.ZJZ'FZJFA8J6A6F6.JFAKZ/ZGZBZKGB8BZGZKZ/KGB7Z@ZHZEZLZ 6LHE*ZHZLZEZL EH;*LZHZEZ LEH9Z)Z5Z?Z59?95Z?Z9Z59?9Z5Z?Z)59?5?69656)659?+ZGZJZ)CZJGC8JZCZGZ+JGCCZ+ZGZJZJGC;CZGZJZ+CGJ.Z>Z:Z5Z5:>9:Z5Z>Z:5>:Z>Z5Z.5:>5.6>6:6565:>CZ0ZHZ.LZLHC8CZHZLZ0LHC$ZHZCZLZLHC;HZCZLZ$CHLCZ0ZLZHZLCH=CZLZHZHL0CLZ$ZHZCZLHC4$CZLZHZLHCLZCZ0ZHZLHC;HZCZLZLH0CLZCZ$ZHZLHC5$ C6H6L6 HCLCZ3ZKZFZCKF/FZKZCZ 3KCFFZCZKZ'ZKFC4F6C6K6 ' KCF9ZAZL7L@L 7@>*@L>L7L7>@@LJ@L@C@7L>L7@LC>J'>L@L7L@>77L>L@L7@><>L7L@L@7>7LJ@C@@LL@>L@7>LCJ)7-@->->@79LL@L7L7@>*@L7L>L7@>7LL@>L@LC@J@7@C>LJ,>L@L7L>7@@L7L>L7@>47L>L@L7>@7LJ@C@L@@L>L7@>CLJ#@L7L>L7>@@L>L7L7@>6@L7L>L7@>@LJ@C@L@>L7L7@>CLJ'@L7L>L7@>7L@L>L@7>5>L@L7L7@>@L7LL@C@J@>L@7>LCJ@L7L>L>@75L>L:L5>:*>L5L:L5>:J@F@A@:L5L>L5>A:JF,>L5L:L:5>7LL5L:L>:595L>L:L>:55LA@J@:L>LF@>:5AFJ'5L:L>L:>57LL5L:L5>:*>L5L:L5>:J@F@5L>LA@:L5>A:JF,>L5L:L:5>L5>:*5->-:-5>:@LL5L5>:*>L5L:L5>:5LF@A@J@>L:L5>A:JF,:->-5-:5>@L7L@767L@L@77L@L7@9@L7L7@@L7LL>:59:L5L>L>:5A@J@:L5LF@>L>:5AFJ'>L5L:L>:5L:L5>:*>L5L:L5>:5LJ@:L>LA@F@5>A:JF,>-:-5-:5>@L7L@76@L7L@77L@L7@97L@L7@:L?L7L?:79?L:L7L?:7K@7LF@C@:L?L?:7CFK'7-?-:-?:77LL:L5>:*:L>L5L5>:7L@LL5>:*>L:L5L5>:5LJ@:L>LA@F@5>A:JF,>-:-5-:5>@LL5>:*:-5->-5>:?L;L6L6?;*6-?-;-6?;7LLCL*CLL<C>H@L<CH>OJ,C-<->-><CL>;79;L7L>L>;7;LG@J@C@>L7L>;7CJG';-7->->;7>L5L:L5>:*:L>L5L5>:5LA@>LF@J@:L5>A:JF,5L:L>L:5>7L@LL:L5L5>:*5L:L>L5>:5LA@J@>L:LF@5>A:JF,>L5L:L:5>@LL>56>L5L>55L>L5>9>-5->5@LL:L>:59>L:L5L>:5L5L5>:*>L5L:L5>::LA@5LF@>LJ@5>A:JF,>-5-:-:5>L:L5L5>:*>L5L:L5>::L>LJ@5LF@A@5>A:JF,5-:->-:5>L5L5>:*5L:L>L5>::L>LF@A@5LJ@5>A:JF,:L>L5L:5>L>565L>L>5L:L5>:*>L:L5L5>:J@5LA@>LF@:L5>A:JF,>L:L5L:5>7L@L@767L@L@7@L7L7@9@L7L@7@L7LL5>:*5L>L:L5>::LF@A@>LJ@5L5>A:JF,:->-5-:5>7L@LL>:595L>L:L>:5:LA@>LJ@F@5L:>5AJF'>L5L:L>:5@LL5L:L5>:*>-:-5-5>:L:L5L5>:*5L>L:L5>:>LA@5L:LF@J@5>A:JF,>L5L:L:5>@LL5>:*>L5L:L5>:6L?L?66?-6-?6L*LCL<C>>LJ@O@H@CLOJ,LCL><C9LL:L5L5>:*5L:L>L5>::LF@J@>LA@5L5>A:JF,:->-5-:5>@LL5L>:59>L5L:L>:5A@>LJ@F@5L:L:>5AJF'5L>L:L:>5@LL:L5L5>:*>L:L5L5>:>L5L:LA@J@F@5>A:JF,>-5-:-:5>L5L>:595L>L:L>:5@L7L@767L@L@7@L7L7@9@L7L7@5L>L:L5>:*>L5L:L5>:J@F@:L5L>LA@5>A:JF,5L:L>L:5>7L@LL5>:*:L>L5L5>:J@>L5LF@A@:L5>A:JF,5->-:-:5>L:L5L>:595L>L:L>:5:LF@>L5LA@J@>:5AJF':->-5->:5L:L>:59>-:-5->:57L@LL>:59:L5L>L>:5J@:L5LF@>LA@:>5AJF'5L:L>L:>57L@LL5>:*>L5L:L5>:5L:LF@>LJ@A@5>A:JF,:L5L>L:5>@L7LL:L5>:*5L:L>L5>::L5LJ@F@A@>L5>A:JF,5L:L>L:5>@LL:L5L5>:*>L5L:L5>:7L@L@767L@L@7@L7L7@9@L7L@7L:L5L5>:*:L5L>L5>:>L5L:LJ@A@F@5>A:JF,>L:L5L:5>@LL:L5L5>:*5->-:-5>:?L;L6L6?;*6L;L?L6?;7L@L@76@L7L@7@L7L7@9@L7L@7L*LCL<C>H@J@O@LCL<CH>OJ,CLL><CL;L>;79>L;L7L>;77LJ@;LC@>LG@;>7CGJ';L>L7L>;7:L>L5L5>:*>L:L5L5>:5L:LJ@>LA@F@5>A:JF,:L>L5L:5>7L@LL>565L>L>5>L5L5>95->-5>L>565L>L>5>L5L5>95->-5>@LL>56>L5L>57Ln7nOZ/>7>n7nOOZ4n]4!4nX4An5nOHn>n7>8A+n5HhE<A++n>n@ZB@+CnJZ0n>iJ 0JZ$n@njJ @+n@Z>n$C@+C>)n>nFZ@F>2nAnJZ)hJA2ABJ6.B(.A@nn5Z.nh2>5Z):nB>)An.nFZ:iF ."nFZ>n(>"FCnnEA>F.B>B.J6>'.0n@nCZJHZEC@H0n@n0LZ@J0L@BL6H6(HL2ZAn:n@>n5Z.nh2>+n@n.A5HZ:@H@4nCnLZ+hLC4LZ0nCn(0L@n0nTZC7nU7@T7nXZ@nT@7X7n@nTZ&7@@nT7nOZFO@730FZ?n"n@F?3nCnKZ"hKC3Cn'nKZ('CAnHZK$n@HAMZ5nEn$hME5MZ)nEn(EM)n:n>n)AZ9A ) :>.nFZAn>nf> .AFA6"B>B(">+nHZA@n@H@4nCnLZ+hLC40nCnLZ(LC?nFZ0"n@F?KZ3nCn"hKC3K6'BCB(K'$nAnEZCn:nAnE2Z5Z.nh2:.HZ@n>5A+n@H@Cn4nLZ+hLC4LZCn0n(L0FZ?nC"n@F?3nKZCn"hKC3K6+BCB(K+0nCZHZC@nEC@HLZ@n00n@J0LHZ@nLZ(@LDZ'nH?nBD'HBK6,B?%H,>n2Z:nKAn5Z.nq52AHZ+n:.>@n@H@Cn4nLZ+hLC4LZCn0n(C0n@F>AnJZ2n)hJA2J62BAB(2JZ?)nB>)AnFZ.n:%A.3Z;n?nBnF6Z/nq/36;?nCnH&n,D9H & >CCnJZ+nHnfC +HJHZCn+n(+HBnn(A>+>Z)nC:nB>).nFZAn:iF ."nFZ>n(A">+nHZF@n@H@CnLZ4n+hLC4L6CB0B(0CTZ0n7nL@nU7@T7n@nXZT@7XTZ7n@n&7@@nT7nOZFO@730"n?nCZ:n9C " :?FZ?n'nCnf? 'CF'n?nCZ('CnEA>F.nJZ.>n>J.J>BF6J6(>J+n@ZFnAn:n2Z<5Z.nh2A:5@nCn4Z7Z0nh4@0"n?n<C7FZ@F?3nCnKZ"hKC3Cn+nKZ(C+CZ@nZ:n8HPB>)F6.BAB:%AF@nHZCZ.0nEC@H@nLZ00n@J0L@nHZLZ(@L>n)nHFZ@F>JZ2nAn)hJA2AnJZ.n(J.4Z0nH4@n@Cnm@ 0C 'nCZ?nFZEC?F'nKZ'?n?J'K?nFZKZ(?Fn9A ) :>FZ.nAn>nf> .AFAZ>n"n("A+n@n9C + <@@nCn0nHZf@ 0CHC6$B@B(@$3Z?nCn:nC7Z'nh3?C':AnZB>)FZAn.n:iF ."B>BF6(AF"HZ+n>@n@H@CnLZ4n+hLC4CB0BL6(C0?Z:nL"nB?"FZCn'n:iF '?BF6'B(C?F4ZCnnFZ,)n@F>ABJ62B)!J2CZHZ@nA0nEC@H@nLZ00n@J0LLZ@nHZ(H@:n>n)nLAZ9A ) :>>nFZ.nAnf> .AF"n>nAZ(A>@nZ):nB>)An.nFZ:iF .>B"BF6(F"AHZ0n@n>CZEC@HLZ0n0@n@J0L@nHZLZ(@L"n:nCZH?n9C " :?Cn?n'nFZf? 'CF'n?nCZ(?'$nEZAnCnAZ)n):n9A ) :>.n>nFZAnf> .AF"n>nAZ(A>@n4Zn:nAZ?)n9A ) :>>B.BABF6(F>A+n@Z.nEA>FJZ>n22n>J2JJ6F6>B(JF@Z+n>n9A ) :>.BAB>BF6(AF.*n?n>GZ@G?K63BBB*!KBCZ@nnHAZBA&Cn+nGZ;>fG +>BG6AB+B(AG>+>nAZFZC.nEA>F>n.n.JZ>J.J>nJZFZ(FJ@n4Z7Z0nh4@<7CHn4n@n0>0nH4Cn@@nm@ 0C ?n"nFZ@F?3nCnKZ"hKC3CB'BK6(K'HZ)nEZCAnEEAHAnMZ))nAJ)MMZHZAn(AHFZAZ>nM.nEA>F.n>n.JZ>J.J>BF6J6(F>+n@nn:n2ZAnE5Z.nh2.A:0n@nCZ>5HZEC@H@n0n0LZ@J0L@nLZHZ(L@3Z?n:nHCn7Z+nh3+7@Z+n:?Cn9A ) :>.BAB>BF6(F.>@Z+nAnAn:n$5Z.nh25.>ACnnE5Z.nh2A:5>0nCZ@n.HZEC@HLZ@n00n@J0LHZ@nLZ(LHFZ"n@?n@F?CnKZ3n"hKC3KZ'nCn(C'Enn:n2ZE5Z.nh2.A>ZH?D:nB>).BABF6:%A.HZ0nCZF@nEC@H0n@n0LZ@J0L@nLZHZ(@H>nFZ.nLAZEA>F.nJZ.>n>J.JJZ>nFZ(J>+n@ZF0nH4Cn@@nm@ 0C ?n:n3ZCn7Z'nh3C?:7HZEZ)n'AnEEAH)nAn)MZAJ)MM6ABH6(MA:n>ZH)nB>).nFZAn:iF .F6>B"B(>AFCZ0n@n"HZEC@H0nLZ0@n@J0L@BH6L6(@H"n?ZL:nB?"Cn'nFZ:iF '?nFZ'n(?'FEZ$nnAZ))n9A ) :>FZ>nAn.nf> .AFAZ>n"n(A>+nZ<8?:nB>)AB.BF6:%.F@n+nAHZ@H@Cn4nLZ+hLC4L6CB0B(LC$nEZ0?n@E?KZ3nAn$hKA3HZAn)n(H)nA9'FZ@F>AnJZ2n)hJA2An2nJZ(J2n5Z.nq>2:GZ*nA5.?n@G?KZ3nBn*!B3@n4ZnCZD&nBC&+nHnJZ>iJ +J6+BCB(CJ+nAn+nCZ?GZECAGMZ+nJZ+An>J+AJ M>BG6ABJ6(J>AFZ)nG>n@F>2nJZAn)hJA2.BABJ6(.J@n0nCZAHZEC@HLZ@n00n@J0LHZLZ@n(HL4n@n@Hn8@40nH@nCnnEA>FJZ.n.>n>J.J>nFZJZ(F>CZHZ@nJ0nEC@H0nLZ0@n@J0LHZ@nLZ(L@"n?nCZH:n9C " :?CnFZ'n?nf? 'CFC6?B'B('?EZAn$nCn9A ) :>>n.nAnFZf> .AF"BA6>B("AHZ@n>+n@H@LZ4nCn+hLC4L6CB0B(L03Z:nCn?nC7Z+nh3:7C?@n4Zn@F>2BJ6AB)!2JHZ@nA+n@H@LZCn4n+hLC4LZ0nCn(C07nSZ0nLXA[0XT3OT3R=AR"R=MTCRYSTDT1YTyYbTc7TYMWIRTT>Rd@9RFW @TTWOa RQPOZTThQ2TUm2TPUK[@!TK!@J:R2$JRT\EQA0O4 Q6OTZMFJ=4MJFHJM.M M-H6F@ HJIAS @AJMHW@@>:@.@(@H>gAcI><A <>UAZJA#<@?f@` ?ZC_@G@`C?O @;>A<B@9>%@>2q>AJ:HBA A-<6:!>J<4> A:F2$>FH\EEA0C4 E6CH#E>HHD/JQHFH,JJE H,JMV4QNM4QT@2TYTTYE VBTTD%T V6[7V W? YX=WV[X V&[6WEX3V W1@@:X["T3&TWdST* WTQ6>TbQ!TgTGQA9Oi QCKO#OPKT7OR_AR RQOa1SWORIS OhJ`FQ6LCOJ#LQJ*O0.JOLKH?(LH C7HUEF C#HE+HZCZ@@"CHEGO H7 G6KLH&@@KLWO[0LOQAMOAQM9L7XLNXRL* NN>LBNQQ/SJQBSN!.NMc[MM[JYIHI J"Gc HM%GOGE\+AT E!A@VG/AQ>S@ @@JA><>.<:6>@\,A[ @9CVA/?CC@?@f2?_@C? :@(:<1<?O!>7?IA=>.C_HYA D:DE3 C HE AJ:HBA A-<6:!>J<4> AAO H7F8HKMSHD5MHyMbHc/JbHM1RZJ5J>R4J$@ KXCPLPKC LBAUJJ@ JA@@EHP.@FD H T@R[T@RTXOVQCO&TQXgLTDH)>k+> <]R<7M 7#:q7:M=P> H)>k+> <]R<7M 7#:q7:M=P> `?Q>$?>Y+5<:N:X<>\>>n:><]<Z<Dc<9V 9;iV9`;Z97Y(7:U):&H)>k+> <]R<7M 7#:q7:M=P> H)>k+> <]R<7M 7#:q7:M=P> H)>k+> <]R<7M 7#:q7:M=P> `?Q>$?>Y+5<:N:X<>\>>n:><]<Z<Dc<9V 9;iV9`;Z97Y(7:U):&H)>k+> <]R<7M 7#:q7:M=P> R)>u+> W=P> <](<%R)>u+> W=P> <](<%j?[>$?>c+5<:X:b<>f>>x:>d<Dc<9` 9;sV9j;Z97c(7:_):&<}<{<*<$R)>u+> W=P> <](<%R)>u+> W=P> <](<%R)>u+> W=P> <](<%j?[>$?>c+5<:X:b<>f>>x:>d<Dc<9` 9;sV9j;Z97c(7:_):&<}<{<*<$R)>u+> W=P> <](<%/( (P(%N.%(0"(+Y2++U(#F +!#%#Y$#&B6&&W(#D&.#&L&&N7&&U(&F&)&&;)#F&0##D(!?#*!!J"!&RH&!H! &PK& !bU!!F"!!\7!!3!&J3&&P,*U&1**L(-R*!-$-R-*U-*#-R%-&Y/&!&R.&*[8**U'*-L2--P(-*Y8*-R -&N7&&U(&F&)&&;)#F&0##D(!?#*!!,"!&[2&&N!&*G3**;(-J*--E -*N?*&P&Y2U F+%Y$B6W(#D.#&L&+H9++N+R+/P(#] *##R(&Y#3&&["&&Y2&&U(&F &!&%&Y$&#B6##W(!D#.!!L!&H9&&N+&*R/**P(+] **++R(+-Y3--["-!Y0! !R(%R !,%%Y% (>/( (P(%N.%(P"(+Y2++U(#F +!#%#Y$#&B6&&W(#D&.#&L&&J3&&P,&*U1**L(-R*!-$-R-*U-*#-R"-&RH&!H! &PK& !bU!!F"!!\7!!U!&Y2&&U& &F+&%&Y$&&B6&&W(*D&.*-L-2H922N+&R2/&&P(*] &***R(-Y*3--["-&J3&&P,*U&1**L(-R*!-$-R-*U-*#-1"-&R1&&F((U&(!(R#()P7))B+*U):**4"*+Y2++U+ +F++%+Y$++B6++W(/D+./2L27H977N++R7/++P(/] +*//R(2Y/322["2&Y2&&U(&F &!&%&Y$&#B6##W(!D#.!!L!&H9&&N+*R&/**P(+] **++R(-Y+3--["-!Y0! !R(%R !,%%Y% (>/( (P(%N.%(P"(+Y2++U(#F +!#%#Y$#&B6&&W(#D&.#&-&&J3&&P,*U&1**L(-R*!-$-R-*U-*#-1"-&b4&&F"&&\7&&3&&i0& &1!&'i0' '1!'(c2((_( /P+/%/c$/,L6,,a(#N,.##V#4R944X+(\4/((Z(,g (*,,\(/c,3//<"/(c2((_( P (! % c$ #L6##a( N#. #V#(R9((X+ \(/  Z(#g *##\(c#3e"!b7!!i(!Z!)!!O)Z!0X(S*8"!h4!!]"!%J7%(6($h4$$]"$(J7(+[+(c2((_((P (!(%(c$(%L6%%a(#N%.##V#(R9((X+,\(/,,Z(-g ,*--\(/c-3//<"/#c0# #\('\ #,''c' *H/* *Z*'X.'*Z"*!c0! !\(%\ !,%%c% (H/( (Z(%X.%(6"(+e2++a(#R +!#%#e$#&N6&&c(#P&.#&4&u\p#U$p#$U&S3&&Y,&*^1**U(-[*!-$-[-*^-*#-[%-&b/&!&[.*d&8**^'*-U2--Y(*b-8*-6 -&b7&&i(&Z&)&&O)&#Z0##X(!S#*!!8"!&j2&&]!&*V3**J(-Y*--T -*]?*&_&+^5++g(+O+&+&+\$+(R*(&(X(&\(,&&K'&+r,+$+R(#^ +-##g# $^8$$V(&g$3& +X+&c2&&_(&P &!&%&c$&#L6##a(!N#.!!V!&R9&&X+*\&/**Z(+g **++\(-c+3--<"-!c0! !\(%\ !,%%c% (H/( (Z(%X.%(Z"(+e2++a(#R +!#%#e$#&N6&&c(#P&.#&X&&T3&&Z,*_&1**V(-\*!-$-\-*_-*#-\"-&h4&&]"&*J7*!fU!!J!!`7!!5!&S3&&Y,*^&1**U(-[*!-$-[-*^-*#-[%-&b/&!&[.&*d8**^'*-U2--Y(*b-8*-[ -&T3&&Z,&*_1**V(-\*!-$-\-*_-*#-\"-&]1&&Q((`&(!(]#()[7))M+*`):**:"*+c2++_(+P +!+%+c$+(L6((a(&N(.&&V&+R9++X++/\///Z(0g /*00\(2c0322<"2&^5&&g(&O&&&&&\$&#R*#&#X(!\#,!!K'!&r,&$&R(^ &-g ^8V(!g3! &4&!c0! !\(%\ !,%%c% (H/( (Z(%X.%(Z"(+e2++a(#R +!#%#e$#&N6&&c(#P&.#&X&&T3&&Z,&*_1**V(-\*!-$-\-*_-*#-\"-&RH&!H! &Pi&&k0& &H!&'k0' 'H!'(c2((_( (P+(%(c$((L6((a(,N(.,/V/4R944X+4(\/((Z(,g (*,,\(,/c3//e"/c2_ P+%c$L6a( N. #V#(R9((X+(\/Z( g *  \( #c3##<"#!b7!!i(!Z!)!!O)Z!0X(S*^"!f4!!J!!`7!!5!$h4$$]"$(J7(+[+(c2((_((P (!(%(c$(%L6%%a(#N%.##V#(R9((X+(,\/,,Z(-g ,*--\(-/c3//<"/#c0# #\('\ #,''c' *H/* *Z*'X.'*6"*!c0! !\(%\ !,%%c% (H/( (Z(%X.%(6"(+e2++a(#R +!#%#e$#&N6&&c(#P&.#&X&u\p#U$p#$U&Y2&&U& -F+-%-Y$-*B6**W(!D*.!!L!2H922N+&R2/&&P(*] &***R(-Y*3--6"-&J3&&P,*U&1**L(-R*!-$-R-*U-*#-R"-&R1&&F((U&(!(R#()P7))B+*U):**4"*+Y2++U(+F +!+%+Y$+(B6((W(&D(.&&L&+H9++N+/R+///P(0] /*00R(2Y03226"2&Y2&&U(&F &!&%&Y$&#B6##W(!D#.!!L!&H9&&N+*R&/**P(+] **++R(-Y+3--6"-!Y0! !R(%R !,%%Y% (>/( (P(%N.%(P"(+Y2++U(#F +!#%#Y$#&B6&&W(#D&.#&-&&N7&&U(&F&)&&;)#F&0##D(!?#*!!J"!&b4&&F"&&\#&2!bU!!W"!%D7%(U(&J3&&P,*U&1**L(-R*!-$-R-*U-*#-R%-&Y/&!&R.*[&8**U'*-L2--P(*Y-8*-1 -&J3&&P,*U&1**L(-R*!-$-R-*U-*#-R"-&R1&&F((U&(!(R#()P7))B+*U):**4"*+Y2++U+ +F++%+Y$++B6++W(/D+./2L27H977N++R7/++P(/] +*//R(2Y/322["2&W/&!&D& *Y)*'*5* +L$+,+D#+-W'-)-U(-&W/&!&W& &W1&&R"&#W-###P("i#6""=!"!Y0! !R(%R !,%%Y% (>/( (P(%N.%(P"(+Y2++U(#F +!#%#Y$#&B6&&W(#D&.#&L&&J3&&P,*U&1**L(-R*!-$-R-*U-*#-R"-&b4&&W"&*D7*-U-&i0& &F!&'i0' '1!'(S3((Y,,^(1,,U(/[,!/$/[/,^-,#/[%/(b/(!([.,d(8,,^',/U2//Y(,b/8,/[ /c2_ P+%c$L6a( N. #V#(R9((X+\(/Z( g *  \(#c 3##e"#!T3!!Z,%_!1%%V((\%!($(\(%_-%#(7"(!f4!!J!!`7!!5!$f4$$J$$`7$$Y$c2_ P+%c$L6a( N. #V#(R9((X+\(/Z( g *  \(#c 3##<"##c0# #\('\ #,''c' *H/* *Z*'X.'*6"*!c0! !\(%\ !,%%c% (H/( (Z(%X.%(6"(+e2++a(#R +!#%#e$#&N6&&c(#P&.#&4&u\p#U$p#$X&d(&-&b"& $f!$"$]$#L,#"#Y#"k"0"P" !bB! #[#%[I%&dE&a&ne&/MTrk$-JAZ2Drumss][{ rp*d#F#*7*F&*&*s&Z&*7&&#F*d*#*2*&&*s&Z*&7&(&*d#F#*7&&*s&Z*&7&&*d#F*#7&&*s&Z*&7*d&(#<*&#*d#F#*7&&*s&Z*&7&&#F*d#*7&#<#&&Z*s&*7#<&(#&#F&Z*Z&#*7&F&*Z#F#*&<&*n&P&*0Z*P&F#F#*0&7-P-*Z#d#*)F))d)*d#F#*7&&*s&Z*&7&&*d#F*#7&&*s&Z&*7&(#<*d&#*#F*d*#7&&*s&Z*&7&&*d#F#*7&&*s&Z&*7*d#<&(*&##F*d*#7&&*s&Z&*7&&*d#F#*7&#<#&&Z*s&**Z*&(&*d#F#*7&&&Z*s*&7&&#F*d*#7&&&Z*s*&7&(#<&#*d#F*#7&&*s&Z*&7&&#F*d#*7&&*s&Z*&7&(*d&**d#F#*7&&&Z*s&*7&&*d#F#*7&&&Z*s&*7&(&*d#F*#7&&&Z*s*&7&&*d#F*#7&&*s&Z*&7.H.#F*d#*7&&*s&Z&*&F&*d*#F#*s*7.H&Z.&7.P.#F*d#*7&&*s&Z*&7&&#F*d*#7&&*s&Z*&7&(*d&*#F*d#*7&&*s&Z*&7&&#F*d#*7&#<&#&Z*s&**Z**d&(*&#F*d*#7&&*s&Z*&7&&#F*d*#7&&&Z*s&*7#<.H.#&P#Z*P*&#7&<&*Z&d&*7)F*n*)&Z*P&*7*<0P0*&Z*Z&*-Z-*<)Z)**d#F1Z*#17&&*s&Z&*7&&*d#F#*7&#<#&&Z*s&*7&(#<&##F*d*#7&&*s&Z&*7&&*d#F*#7&&*s&Z*&7&(&*d#F*#7&&*s&Z&*7&&*d#F#*7&6&&Z*s*&7.H.*d#F*#7&&&Z*s*&7&&*d#F*#7&&*s&Z*&7&(&#F*d#**<*&&*s&Z*&7&&#F*d*#7&#<&#*s&Z*&7.H#<#.#F*d*#7&&&Z*s*&7&&*d#F*#7&6&&Z*s*&7&(&#F*d#**<*&&&Z*s&*7&&#F*d*#7&#<#&*s&Z&*7&(#<#&*d#F#*7&&*s&Z&*7&&*d#F*#7&#<&#&Z*s&*7&(&*d#F*#7&&*s&Z&*7&&*d#F#*7&&*s&Z&*7&(&*d#F#*7&&*s&Z&*7&&#F*d*#7&&*s&Z*&7*d&(&**d#F*#7&&&Z*s&*7&&*d#F#*7&#<&#*s&Z&*7&(&#P&(*Z#&*&2&&d&*Z&d*&&(&&d&&2#P*Z&#*&s&&F&*Z&Z&*&d&&&1Z3Z#P#137&&&d3s3&7&(&3Z#P3#7&&3s&d3&7#<&(&##P3Z3#7&&3s&d3&73d&(&3#P#3d3&&3Z&d&37&(#<#&#P3Z#37&&&d3s3&7&(&#P#3d3#<&#&&d3Z3&7&(&3Z#P#37&&3s&d3&7&(&3Z#P3#7&&&d3s3&7#<&(&#3Z#P3#7&&&d3s3&7&(&#P#3d3&#<#&&d3Z&37#<3l3#3Z#P3#7&&&d3s3&7&(3d3&#P#3d3&&3Z&d&37#<&(&##P3Z3#7&&3s&d&37&(&3Z#P3#7#F&&#3s&d&37&(#<#&3Z#P3#7&&3s&d3&7&(&3Z#P#37#F&&#&d3s&37&(&#P3Z3#7&&&d3s&37&(&#P3Z3#7&&&d3s3&73l3#P3Z3#7&&&d3s&37&(&#P3Z#37&#F#&3s&d&37#<3l3##P3Z#37&&&d3s3&7&(&3Z#P3#7&&&d3s3&7&(#<&#*F#Z&s*&#&(&&(&&d*F&*&(&&d&*F#Z*#_*F*_#P1U1#7&&&d3s3&7&(&#P3Z#37&&3s&d3&7#<&(&#3Z#P3#7&&&d3s3&7&(&#P#3d3&&3Z&d&37&(#<#&#P3Z3#7&&3s&d&37&(3d&3#P#3d3&&3Z&d3&7&(&3Z#P#37&&3s&d3&7&(&#P#3d3&&&d3Z&37#<&(&#3Z#P3#7&&&d3s&37&(&#P3Z#37#F&&#&d3s3&7#<&(&#3Z#P3#7&&&d3s&37&(&#P#3d3&&3Z&d3&7&(&3Z#P3#7&&3s&d3&7&(&3Z#P3#7&&3s&d&37&(&3Z#P3#7&&&d3s&37&(3d&3#P#3d3&&3Z&d&37#<3l#3#P3Z#37&&&d3s&37&(&#P3Z#37&&3s&d3&7&(&#P3Z#37&&3s&d&37&(&3Z#P3#7&&&d3s&37&(&#P3Z#37&&&d3s&37&(&#P#3d3&&3Z&d&373l3#P*Z&(&#*&2&&d&&d*Z&*&(&&d*n&**Z&2#P*#&&s&&F&&Z*Z&*&d&&&1Z3Z#P#137&&3s&d3&7&(&3Z#P3#7&&3s&d3&7&(&#P3Z#37&&&d3s3&73d&(&3#P#3d3&&&d3Z3&7&(&#P3Z#37&&&d3s&37&(&#P#3d3&&3Z&d&37&(#<&#3Z#P3#7&&&d3s&37&(&3Z#P3#7&&3s&d&37&(#<&#3Z#P3#7&&&d3s3&7&(3d&3#P#3d3&&&d3Z&37&(&3Z#P#37&&3s&d3&7&(&#P3Z#37#F&&#&d3s3&7#<&(&#3Z#P3#7&&3s&d3&7&(&#P3Z3#7#F&&#3s&d3&7&(&3Z#P3#7&&3s&d3&7&(&#P#3d3#<&&#&d3Z3&7#<&(&#3Z#P3#7&&3s&d3&7&(&#P#3d3&&3Z&d3&7&(&#P3Z#37&&3s&d3&7&(&#P#3d3&&&d3Z3&7#<&(&#3Z#P3#7&&3s&d&373d&(&3#P#3d3&#<&#3Z&d&37&(#<&#&Z*Z#F*&#_#F*Z#*&2&&<&&F#F*Z&#*&P&&Z&&Z*Z#F&*#&d&&s&#P3Z1Z#317&&3s&d&37&(&3Z#P3#7&&&d3s3&73l#<#3#P3Z#37&&3s&d&37&(3d3&#P#3d3&&3Z&d&37&(#<#&3Z#P3#7&&3s&d3&7&(&3Z#P3#7&&&d3s3&73l#<#3#P3Z#37&&&d3s&37&(&3Z#P#37#F&#&3s&d&373l#<3#3Z#P#37&&3s&d3&7&(&#P3Z3#7&#F#&3s&d&373l#<3#3Z#P#37&&&d3s&37&(&#P3Z3#7&#F#&3s&d&37#<&(#&#P3Z#37&&3s&d3&7&(&#P3Z#37&&&d3s3&7#<&(#&3Z#P3#7&&3s&d&37&(&#P#3d3&&3Z&d3&73l33Z#P3#7&&3s&d3&7&(&3Z#P3#7&&&d3s&37&(&3Z#P3#7&&3s&d3&7&(&3Z#P#37&&3s&d3&7&(&3Z#P3#7&&3s&d3&7&(&#P3Z3#7&#F&#3s&d&37#<&(#&#F*Z&Z*&#7&F&*Z#F*#&<&&P&*P0Z#F0#*_*Z#d*#_1Z#F*d*1#7&&*s&Z*&7&&*d#F#*7&&*s&Z*&7&(&*d#F#*7&&&Z*s&*7&&*d#F*#7#<&&#&Z*s&*7.H.*d#F*#7&&*s&Z*&7&&#F*d#*7&&*s&Z*&7#<&(&##F*d#*7&&*s&Z&*7&&#F*d*#*2*&6&&Z*s*&7&(&#F*d#*7&&&Z*s*&7&&#F*d#*7&&*s&Z&*7*d#<.H*#.*d#F*#7&&*s&Z&*7&&*d#F*#7&&*s&Z&*7&(#<#&#F*d*#7&&&Z*s&*7&&#F*d#*7#<&6&#*s&Z&*7&(#<#&#F*d*#7&&*s&Z*&7&&*d#F*#7&#<&#*s&Z&*7#<&(&##F*d#**<*&&*s&Z*&7&&#F*d#*7&#<&#*s&Z&*7&(*d*&#F*d*#7&&*s&Z*&7&&#F*d#**2*&6&*s&Z&*7&(#<&#*d#F#*7&&&Z*s&*7&&*d#F#*7#<&&#&Z*s&**Z*&(&*Z&Z#Z*#&_*Z#<&<*#&&P&*n&d&*#Z*Z#*7&<&*Z&d#<#&*&(&#P&2#&#F*d1Z*#1*<*&&*s&Z&*7&&#F*d#**2*&&*s&Z&*7&(&#F*d*#7&*F&*&Z*s&*7&&#F*d*#7&6#<&#*s&Z*&7&(&*d#F*#7&&&Z*s*&7&&#F*d*#7#<&#&&Z*s&**Z*#<&(&#*d#F#*7&&*s&Z*&7&&*d#F#*7&&&Z*s&*7*d&(#<#*&*d#F#*7*F&&**s&Z&*7&&*d#F#*7#<&6&#*s&Z&*7&(#<#&#F*d#*7&&*s&Z*&7&&*d#F#**2*&6&&Z*s*&7&(&*d#F*#7&&*s&Z&*7&&*d#F*#7&&*s&Z*&7&(&*d#F*#7&&*s&Z&*&F&*d*#F#*s*7.H&Z&.7.P.*d#F*#7&&&Z*s*&7&&*d#F#*7&#<#&*s&Z*&*Z*&(&#F*d#*7&&*s&Z&*7&&#F*d*#7&#<#&*s&Z&*7&(#<#&#F*d*#7*F&*&&Z*s&*7&&*d#F#*7&#<&#&Z*s*&7#<&(#&&P#Z*P&*#7&<&&d*Z*&7*<)F)**P&Z*&7*<0P0*&Z*Z&*-Z-*<)Z*)1Z3Z#P13#7&&&d3s3&7&(&3Z#P3#7&&3s&d&373l#<#33Z#P3#7&&&d3s3&7&(&#P3Z#37&&3s&d&373l3#P3Z#37&&&d3s3&73d&(3&#P#3d3&#<&#3Z&d&37#<3l3#3Z#P#37&&3s&d3&7&(&#P#3d3&&&d3Z&37&(&#P3Z3#7&&&d3s&37&(&3Z#P3#7&#F#&&d3s&37&(#<&#3Z#P3#7&&&d3s&373d&(3&#P#3d3&&3Z&d&373l33Z#P3#7&&&d3s&37&(3d3&#P#3d3&&&d3Z&37&(&3Z#P#37&&&d3s&37&(&#P#3d3&&3Z&d3&73l33Z#P3#7&&3s&d3&7&(&#P3Z3#7&&&d3s&373l3#P3Z3#7&&3s&d3&7&(3d3&#P#3d3&&&d3Z&37&(#<&#3Z#P3#7&&3s&d&37&(&#P#3d3&&&d3Z&37&(&*P#Z&P*#&7&<&*Z&d*&7*<)F*)*P*_*Z*_*Z#d#*7&d&*Z*7&<&&s*Z*&7#Z#*Z&s*&7&<&&s&7&d&#d#7&s&#s1Z1#/MTrkvJAZ2]{ Organi@[pI9E;B.L7PBEENBALEBIL)LAG7DGLhQ>J0NA`NJE>QNCQ<J.E1JNQE*ENBJ7CJN IBC7 CIJ+NB(E1EQOEQJJBJ NNAJ7DJNQ;H+J=N5H JNQ>1 >JHSJOD@SJO>@>Q+NFHJANHQ GHC1JAOCJG >$>C/LDB;G7H)BG0CLH&J9GDC->CGJ2LFHNCFELCH>+ >G7C=J;HJCG>3 >HSLQCU>HCLQ3N=J&;QB7JN BNDJHQQCQJN5ICE8p@>LCEIKLE+@I# ELGI@PKL@uS=J9OI]O JS'Q;J=N5JNQ>1 >ODJHSJ@SJO>@>JJQ+NFANJQQDJ0NDsQNJ C=sE.sEC NIJ6Q4BNQJN:QIJBN JQHB8#BN4J.Q0ANJQB8BJ6Q@N>KNJQ,NAJ7-NJ NEQ8J+H*/HJNQ;H8QPNAJ:JQNHSS8OEJ*/JOS;SPOAJ8OSJvJ.O4SA OJSJ>SUO>SOJrJ&Q3N=QB7JNBQQJHNDCJNQ5E=I5L;EIL91 9NJJDEH@NEJ9@9IFL+EJAEILOAS>J0`OJSG>S<J.GOC1JOSQ8NEJ*/NJQ;J8QPNANQJvJ=PN.NJJ7NB[JNF7KBKKF GCLKNGL%GALUPGXGLPP<GAL4KLPGPBLOG4/LGP;PZLKGBLPGvG8L>PK LGPLHP_GHPLGsL=IIE89ILE='=LKIRE;;I EL=I=LNITEKGIELwE:LNINLIEiLNH:ONLOHLTGKPK?GD3LPDPIG?LK1GLPPQLTG?LG PMD8 DPOLQGC;PGLD3 DGMLGPQ=GLPG?KN(GGCN9NKG ?1?GRNNK]0C'MC>A&A>C4>4A>CL:H@LHA<C>>8(FYJMFGRlG >AJCMQHF\MHHAML#HMUHALL#HL2NTQKJK`JE3NQEJ?QINK1JNQQQNTJ?NJ QME8 ENQQOJC;QJNE3 EJMNGQQ=JNQ7J2NGQ; J E[*E&E^9E NQ2Q;JGH2N9 H E[*E&E^9E JNQSHJ:OK`OJGHSSFJ8GOM1OJSG4GSMO3(J/JSO SQOXJZOJSsJMNMJNJMQHJ QPE<ESKOFJHLSJE5O ESQJKOM>OJSINE?(EL9ECLIE =1=ERLNI]0C'A-,>CA$>4C4A>A>CH@L:LHA<>8C>(JMFYFGRlG C>JAH-E0J',EJH$E4J4H>EHJO@S:SOJ>H<E8(QMMYMNRNJHENQ JGsN8sJN CAILfCI QBNOJ4/NJQ;JBQZNKNQJvJ8N>QK NJQQ_NHJHQNJrJ2Q;NG J E[*E&E^9E QN2H4QBJ5NOJHQN9QZNKJDHBJQNHSJ4OOSB/JOS;JBOKSZOSJvJ8O>SK OJSOHJHS_SOJrNKJC-NJ#JANUQGXJNQQ<N4JAKNQJyLHIKE:`IE@HLLFIM@E81IEL@$@C'>0A-,C>A$C4>4A>>ACL:H@LHA<C>>8(JMFYFGRNGC>JA J?NN(JJCQ9QNJ B1BQNJRN]E4@CEN:J@NJE>@8C<(HYLMHIRlI LCE@EGPI8sEI HALLfLH LOPBG4/LGP;LKGBPZLPGvL>G8PK LGPP_LHGHPLGrG2N;KG G B[*B&B^9B KNL=IIE8ZILE='=LKIRE;;I EL=I=LNITEKGIELzOKJ:SH`OJSGHOMGSFJ81OJSG$GMQHF\HMMLHA#HMULLHA#HLUQ4J6NI!NQJQIN:JBN JQHB8#BN4J.Q0ANJQB8BN>J6Q@KNJQ'N3Q1J/6JQ>=N>JFQHNJFQJN1NEQ8H*J+JHQN9H8NAJ:QPJQNHvGHC1JA.CJG >$>G7C/H)B;LDBG0CLH&GDC-J9>CGJ2HNCFLFELCH>+ >C=G7J;HJCG>3 >HSCULQ>HCLQ6N8J12NQJB1$BQHJ8NBJQNJB0(JFN<BQHGNQJ6IAE7DEIhS>J0OA`OJG>SJ.S<GOC1JOSGGQ7J.N9PJNNNJALNJQJ=sN.sJN}C0E9LDIDELCI#QRN]JXM? M(O3M!G=QJE OOGEMNOM? G/ E6MC6GEB2CBNRJEJ NNRJP=N>JFNJQHFQJN1Q1H/N3J36JQ>=H N>JFQHHANJFQHJN1J8OAUOJ+OGJ09JOOAJ2AJO OPJPAJ2O NEQ8J* J&NQ;J8QPNANQJvN4J.QA NJQJ>QUN>QNJO@0C-E'M@CE&C>@4E4C@EN:J@NJE>@8C<(HYLMHIRlI @CELO9S7J.PJOJAONLOJSJ*Q8NE/JNQ;J8QPNANQJSJ=sN.NJ NBJ7[JNF7KBKFK GKD9LV!DbD,-GDGR,GD-LDLEGM(D< GLGRLODGGLGRLIBG LGTL8@G7LNGL@)@LVGR;GL=@0E'C-,@EC$E4C>@4C@EN:J@NJE>@8C<(LMHYHIRNELI@C EGsI8IE HGPL8LHXPAG<LA0LPGLRPEG=/GLPPKLRG:4PGLLOPRGC+LGPLEPAG=(D1 PGLDPMLTG?5LPGLTGEPO+LGPLCG9PC/PGD6LD LEP?GA5LGPG?PKLK1GLPDG2KGN;G5B[*B&B^9B KN5L=IIE89ILE='=LKIRE;;I EL=I=LNITEKGIELS=OIJ8ZOSJC'CSKORJ;;O JSCICSNOTJKGOJS'MQHF\HMMLHA#MHUHALL#HL,HKVRNVNQ4NAQVJKNHJG?V?MCM P=M=JP PGMJ LXF?ON0OFLV-JKNNEK&JNEBMA?8CIJIII4JICM?>KL8HIBK4HLB>V@/MTrkJAZ2[][{ &Z][{ &Z][{ &Z][{ &Z][{ &][{ &GuitarZ]{ &Z@Zd9H=UBUEU!=EB9!9H=UBUEU=EB9t7E;R@RCR!;C@7!7I;V@VCV7;C@r2D9H>UBUEUJU B29>JE!2D9H>UBUEUJUE>92BJs2D9U>UBU!2>B9!-D4H7U=U@UEU E@4=-7h2H9[>[B[9>B222<9L>LBL9B>2w2H9[>[B[B>9222D9U>UBU92>B2D9H>UBUEUJU J29>BE!2D9H>UBUEUJUJE>9B2s2D9HUCUGUJU7JGC>-7H>UCUGUJU7JC>Gs7H>UCUGUJU>7JGC+7H>UCUGUJU>CJG7q2A9E>RBRERJR 92JBE>!2D9I>VBVEVJV>9EBJ2s2A9E>RBRERJR2>J9BE+2H9M>[B[E[J[>BE92Js-A4E9R=R@RER -94@=E!-D4I9V=V@VEV@4E=9-v7E>RCRGRJR >CJ7G!7I>VCVGVJVC>J7Gr2D9H>UBUEUJU 9JEB>2!2D9H>UBUEUJU>92BEJs2D9H>UBUEUJU BE2>9J!-D4H7U=U@UEU -7E@=4h2H9M>[B[E[J[ >BJE2922<9@>LBLELJLBE9>2Jw2H9M>[B[E[J[ JB>92E22D9H>UBUEUJU J9B>2E2A9E>RBRERJR EB>29J!2D9I>VBVEVJVJ2E9B>s2A9ERCRGR C;>7G!7I;V>VCVGV7>CG;s7E;R>RCRGR>7;CG+7M;[>[C[G[>CG7;m2H9M>[B[E[J[ JE>92B22<9@>LBLELJLB2E>J9w2H9M>[B[E[J[ B92>EJ22D9H>UBUEUJU 9BEJ2>-A4E9R=R@RER 9=@E4-!-D4I9V=V@VEV-=E49@u7H>UCUGUJU C7>JG!7H>UCUGUJU7>CGJt2A9E>RBRERJR B>2JE9!2D9I>VBVEVJVJE29>Br2D9H>UBUEUJU J92E>BUN).N3D:U?UCUD3C?:I4O;]@]D]G] GD4@;!4O;]@]D]G]GD4;@s4O;]@]D]G]G4;@D+4O;]@]D]G]GD@4;t4L;Z@ZDZGZ @GD;4!4O;^@^D^G^4@D;Gs4L;Z@ZDZGZD;@G4+4T;c@cDcGcD@G;4L-J4O9]=]@]E]'@94E=-n-J4O9]=]@]E]!E@=94->-J4O9]=]@]E]'4=-9E@0J7O<]@]C]H]'70<@CHrLML6NYNL_L5N]Ns/H6L;Z?ZBZGZ';?BG6//K6O;^?^B^G^!6;?BG/n-J4O9]=]@]E] @94E=-!-J4O9]=]@]E]94-=@Es7O>]C]G]J] GC>7J!7O>]C]G]J]C>JG7M6\MM.#MO0J7O<]@]C]H]07<@CHM2J9O>]B]E]J] B>9EJ2!2J9O>]B]E]J]9JB2E>s2J9O>]B]E]J]JB9E2>+2J9O>]B]E]J]>BEJ92M2J9]>]B](2B9>n2J9]>]B]">92B2J9O<]B]E]J]'9B2J<En2J9O<]B]E]J]!<92JEB?7L;Z>ZCZGZ 7G;>C!7O;^>^C^G^;7C>Gs7L;Z>ZCZGZ;7>GC+7T;c>cCcGc>;CG7p2H9L>ZBZEZJZ 2>BJE9!2K9O>^B^E^J^2BE9>Js2H9L>ZBZEZJZE2B>J9+2O9T>cBcEcJc2JB9>Es-H4L9Z=Z@ZEZ'9=4-E@-K4O9^=^@^E^!4@9=-Eo7L>ZCZGZJZ'>7CGJ7O>^C^G^J^!GJ>7Ci2H9L>ZBZEZJZ'B>J92E2K9O>^B^E^J^!J9B2>En2J9O>]B]E]J]'>J29BE-J4O7]=]@]E]'47=E-@@2J9O>]B]E]J]'9>BEJ2n2J9O>]B]E]J]!9J2>BE>2J9O>]B]E]J]EB2J9>$2J9O>]B]E]J]!2J9BE>m2H9L>ZBZEZJZ'B2JE9>2K9O>^B^E^J^!JEB2>9k2J9O<]B]E]J] JBE2<9!2J9O<]B]E]J]<J9BE2s7O;]>]C]G] 7GC>;!7O;]>]C]G];7>CGs7O;]>]C]G]GC7>;+7O;]>]C]G]>CG7;q2H9L>ZBZEZJZ B>92JE!2K9O>^B^E^J^2>JEB9s2H9L>ZBZEZJZ>92BEJ+2O9T>cBcEcJcJE>92BO-J4O9]=]@]E]'9E4=@-n-J4O9]=]@]E]!4@=E-97O;]>]C]G]';7GC>n7O;]>]C]G]!;7>CG?2H9L>ZBZEZJZ'EB>9J22K9O>^B^E^J^!2J>BE9k2J9O>]B]E]J]'B92>JENN).N3J:]?]C]>3?:C)4O;]@]D]G]'4;@DGn4O;]@]D]G]!G;@D4>4O;]@]D]G]DG;@4$4O;]@]D]G]!4;@DGj4L;Z@ZDZGZ D@;G4!4O;^@^D^G^D@;G4s4L;Z@ZDZGZGD@;4+4T;c@cDcGcGD4;@v-H4L9Z=Z@ZEZ'4@E9=--K4O9^=^@^E^!4E-@9=k-J4O9]=]@]E]'-4E@=90J7O<]@]C]H]'<70@CH@4O;]@]D]G]'G@;4Dn4O;]@]D]G]!G4;D@>4O;]@]D]G]@DG4;$4O;]@]D]G]!4D;@GF/J6O;]?]B]G]'GB?;6/n/J6O;]?]B]G]!;?BG/6>-J4O9]=]@]E] E-49=@!-J4O9]=]@]E]-94@E=M7O>]C]G]J]'C7>GJn7O>]C]G]J]!7G>CJLM6\MM.#MO0J7O<]@]C]H]CH@7<0N2A9E>RBRERJR EB>J29!2D9I>VBVEVJVE29>BJs2A9E>RBRERJRB2EJ9>+2H9M>[B[E[J[JEB>92p2A9R>RBR!9>B2!2D9V>VBVB2>9u2D9H[C[G[J[JGC>727@>LCLGLJL>7JCGw7M>[C[G[J[JGC7>27H>UCUGUJU C>7JG|2H9M>[B[E[J[ 29>JBE22<9@>LBLELJL2J9>EBw2H9M>[B[E[J[ JEB>9222D9H>UBUEUJU EJB>92-A4E9R=R@RER E@9=4-!-D4I9V=V@VEV=9@E-4p7E>RCRGRJR CGJ>7!7I>VCVGVJV>7JGCv2A9E>RBRERJR JB9>E2!2D9I>VBVEVJVEB>J92r2D9H>UBUEUJU JEB>29!-D4H7U=U@UEU -E47=@n2A9E>RBRERJR J9EB>2!2D9I>VBVEVJV2BEJ9>s2A9E>RBRERJRBJ>E29+2H9M>[B[E[J[>2JB9Es2A9E>RBRERJR 9BEJ>2!2D9I>VBVEVJV>92JBEp2A9E[C[G[>C7G;27@;L>LCLGL;7GC>w7M;[>[C[G[7GC>;27H;U>UCUGU >C;G72A9E>RBRERJR 92>JBE!2D9I>VBVEVJV2E9B>Js2A9E>RBRERJRJB>E29+2H9M>[B[E[J[>92EBJv-A4E9R=R@RER E@94=-!-D4I9V=V@VEV@=9E-4p7E;R>RCRGR ;7>CG!7I;V>VCVGV;C>G7u2D9U>UBU!9>B2!2D9U>UBU29B>s2D9H>UBUEUJU >BEJ29UN).N3D:U?UCUD:3C?ULML6NYNL_L5N]Nu4O;]@]D]G] G@;4D!4O;]@]D]G];@G4Ds4O;]@]D]G]4DG;@+4O;]@]D]G]GD4;@s-J4O9]=]@]E] 4@E9=-!-J4O9]=]@]E]4E-@9=s-J4O9]=]@]E]'-4E@=90J7O<]@]C]H]'<70@CH@4O;]@]D]G]'GD@4;n4O;]@]D]G]!G@D4;>4O;]@]D]G]@DG;4$4O;]@]D]G]!G4@D;j/H6L;Z?ZBZGZ'/GB?;6/K6O;^?^B^G^!GB6?/;l-H4L9Z=Z@ZEZ'@=E49--K4O9^=^@^E^!94-@=EH7O;]>]C]G]'CG;>7n7O;]>]C]G]!G;C>7LM6\MM.#MO0J7O<]@]C]H]7@HC<0M2J9O<]B]E]J] J2E9B<!2J9O<]B]E]J]<29BEJ{2J9O<]B]E]J]B<EJ29'2J9O<]B]E]J]92<BEJ@/MTrkHJAZ2[ Muted guitar s@]{p;U4W&;4*;D4P4; =P4F4=44?=D= 44H;54;);<4F;44J=F4=-4P=@4= 4P;84;.4H;D;4 4N=64 =-4L=P4=4L;<4;44H;B$;44U=D = 46=441=4 4W;U&;4*4P;D4; =P4F4=44?=D4= 4H;54;*4F;<4;=F4J4=-4P=@4= 4P;84;.4H;D;4 =64N4 =-=P4L4=;<4L4;44H;B$;4=D4U = 464R=W=4 -W4U&4-*-P4D-4 6P-F-646D-?-6 -H45-4*-F4<4-6F-J-6--P6@-6 -W4U&4-*4D-P-4 -F6P-697U0WG07,0P7D07 9P0F0940,9/90;U4W&4;*4P;D4; 4F=P4=44?=D4= ;54H4;*;<4F4;=F4J4=-=@4P4= 4P;84;.4H;D;4 4N=64 =-=P4L4=;<4L4;4;B4H$;44U=D = 46=W4R=4 /W6U&6/*/P6D/6 /F8P/848D/?/8 65/H/6*6</F/6/J8F/8-8@/P/8 -W4U&4-*-P4D-4 -F6P-646D-?-6 45-H-4*4<-F4-6F-J-6-6@-P-6 2U+W&2+*2D+P+2 +F4P+444D+?+4 25+H+2*+F2<+2+J4F+4-4@+P+4 5a<_\5<_ :]6[>\8\A2Y(U$SPMHD@.?>=<;:9876543 210/.-,+*'&$# "> #"$(&,0.5379;?=@f>mQ>4AYKA E\ E G]FGJb9J+L[ABCD F GHJLNOP"R$S(U&T*V2Y0X6[4Z8\:]>_@`BaFcHdLfPhRiVk\nXl^o`pLdr `p^oZmVkPhDbLf>_4Z:]$S*VPMJ GELVCBA@ BCE G FILJONP"R Q&T(U*V0X2Y6[4Z:]<^@`>_BaFcDbLfHdNgRiPhVkXlZm\nLXlVkRiNgHd@`4Z:]P(UK FB@J\wGHJQGJa?>=;:9876543120-.,*('%"# J '#,05:@?]E_#EG[1GJ`3JIJlGZJLG6LbACDE GHJKMOP"R$S*V(U0X8\4Z:]>_@`HdDbLfRiPhXlVkZm\n ZmVkRiHdNgBa:]2YL&TPK GC@ GSIG.JcQLPJ#GPLFGJdCaJ CHEKPN$S(U0X4Z8\<^@`DbHdNgLfPhTjVkXlZm\nZmXlVkTjPhRiNgHdLfDb>_@`:]6[2Y*V&T"RPNKH FBD@Sr?=<;:86752401/-,*('%#"! >!#'%*/-4168:<>@[A`.BXA(EXB#E9T#9 ;O8;>Q>!>L>0>Xx;C>;>`>*@d@!@hB`@BAElEEBWB@fv@>dC>3>T >V>[4>>?>@V<@ @D@BLCBEU ECZ>CBQ-CLB5CEj?E8GZ~Ga@B'@ B\YBEYxE>CJ@L>>@4BD"BbEd+EGD#G CRxCB=:BC^qCEV}@OEa@ER.GbEEG/Jg?JE[uEB[xBEe}ED\9D?D\3DFD\5D GF/D\G)D BH/@CB(@ B\6BABO0@[B7@5D\_:]<^6[4Z0X*V&T"ROMJ GD@A ObNLPOLQd CA FD GKIMNP$S Q&T0X*V4Z8\:]@`>_HdDbLfTjPhXlZm^obqfsjurynwv{x|z}~ Qx|tznw`pxtVkNgDb*V8\N GA@Oc+BADE F GHIKJMLN QP"R(U&T4Z0X6[<^8\>_@`BaDbFc Ba@`:]>_*V6[$SNHB@OLRtLJ\NLaJL Jf.GYJGE]#C_EABD FE GIKJMNOP Q"R&T$S*V(U0X2Y4Z6[:]<^>_@`BaFcHdLfJeNgPhTjRiVkRiLfPhHd<^CBa@b4ZN&T F@!@ @^&@C`6CEjAC GEIKMOP$S&T*V6[2Y:]>_@`DbLfHdRiPhVk\nXlbq^odrjuxtnwpxtzz}x||~~x|~z}x|v{rytzpxnwlvjuxtfsbq`p^o\nZmXlVkRiPhNgLfJeDbFc@`Ba<^:]6[4Z0X*V(U&T$S Q"RPNMJKH GECA@ CVECEiACE GILPN"R(U0X8\4Z<^Fc@`LfVkPh`pZmfslvpx|~x|~E nwx|drZmPhDb:]0XN$SI FDEcC DH FJMO(U"R2Y6[<^HdBaNgZmTj`pdrjuv{pxz}~%Ev{lvbqJeVk>_ Q2YJC@EdC G"RM*V8\BaRiJeZmdr`pjuxtlv fsxt^obqZmRiVkNgFcJeBa@`<^8\:]4Z2Y0X*V(U$S&TO QM GJD@B CYEL@bCCd@QC@K#Cc@KEiCwABCD FEH GIJLMNOP Q"R$S(U*V0X2Y6[8\:]<^>_@`BaDbFcHdUE.LfNgPhRiTjVkXlZm\n^o\nXlVkRiNgLfHdJeFcBa@`>_<^:]8\6[2Y0X(U&T"R$S QPONLKJI G FEgECAB@EGl>=<; G<=>@?9JpRF`JG[FdGE`OA]ETA B C DE F GIJLN QO$S(U*V2Y4Z:]8\<^>_ :]>\6[A8\2Y(U$SPMH@D.?>=<;:9876543 210/.-,+*'$&# "> "#$(&,.05379;?=@f>mQ>4AYKA E\ E G]FGJb9J+L[ACBD F GJHLNOP"R$S(U&T*V0X2Y4Z6[8\>_:]@`FcBaHdLfPhVkRiXl\n^o`pLdr ^o`pZmVkPhDbLf>_:]4Z$S*VPMJ GECLVBA@ BCE G FILJNOP Q"R&T(U*V0X2Y4Z6[:]<^@`>_BaDbFcHdLfNgPhRiVkXlZm\nLXlRiVkNgHd@`:]4Z(UPKB F@J\wJGHQGJa?>=;:9867534210-.,*(%'"# J '#,50:@?]E_#EG[1GJ`3JIJlGZJLG6LbACD GEHJKOMP"R$S*V(U0X8\4Z:]>_@`HdDbLfPhRiXlVkZm\n VkZmRiHdNgBa:]L2Y&TPKC G@ GSIG.JcQLPJ#GPLFGJdCaJ CEHKNP$S0X(U4Z8\<^@`DbHdNgLfPhTjXlVkZm\nZmXlVkTjRiPhNgHdLfDb>_@`:]6[2Y&T*V"RPNKH FBD@Sr?=<;:86754210/,-*('%#"! >!#%'*-/4168:><@[A`.BXA(EXB#GYE,GJaIJJWJ@GlGWEb%EAP.ABDE GHLJN"RP$S&T*V2Y0X4Z6[A2Y0X*V&TP"RNKHEC@A A_1ABDE GHJNLO Q$S(U*V6[4Z8\<^>_@` <^:]6[2Y"R(UNHBA@>[d;R>N@e;ABCDE F GIJLMNOP Q"R&T(U*V0X4Z6[:]>_<^@`DbFcHdJeLfPhNgRiTj Vk@TjRiNgJeHdDb<^@`8\*V4Z"R(UPLN GIECB@XAa(AEQAE GZ+GJ__<^ <^6[:]2YA(U$SLO GC@ >Yb;V>I@`;1ABCED F GIJKLMNOP Q$S(U&T0X2Y4Z8\:]<^>_@`BaDb@Ba>_:]2Y8\$S*VPJN GDA@VA^+ABO)BEV#EG](GJitGVJDG E5+EJ[dJGaIGELEL\)ABED GHJNL QO"R(U&T*V2Y4Z8\6[:]<^>_L@` >_<^8\6[2Y(U$SPMI FCA@L\#ABD FI GKOM Q$S(U*V2Y6[:]>_@`BaDbHdJeLfHdFcBa>_:]4Z*VO$SDJL@J[qG\JQGJj? >=<;:986543210/.-,(+&'$#" J "$,'/38>@aJkFJ1KhLK3KmKLb L.LdKL+O[P[ O6PS\&U]SFXjUSX*QqABDE F GIJKNMOP Q$S&T(U0X*V4Z6[8\:]<^>_@`DbFcHdJeLfNgPhTjRiVkXl\n^o`pbqfsxtjulvnwpxrytzv{x|z}|~~z|~z}x|v{tzrypx nwlvjuxtfsdrbq`p^o\nZmXlVkRiNgPhLfJeHdFcDbBa@`>_:]<^4Z6[0X&T*V"ROMJ GDA@ ObQNLPOLQd CAD F GIKNMP$S Q&T0X*V4Z8\:]@`>_HdDbLfPhTjXl^oZmbqfsjunwryv{z}x|~ Qx|tznw`pxtVkNgDb8\*V GNA@Oc+BAED F GHIKJMLNP Q"R(U&T0X4Z6[8\<^@`>_BaDbFc Ba@`:]>_*V6[$SNHB@OLRtLJ\NLaJL Jf.GYJGE]#C_EABDE F GIJKMONP Q"R$S&T(U*V0X2Y4Z6[:]<^>_@`FcBaHdLfJeNgPhTjRiVkRiPhLfHd<^CBa4Z@b&TN@ F!@ @^&@C`6CEjAC GEIKMOP$S&T*V2Y6[:]>_@`DbLfHdRiPhVkXl\nbq^odrjuxtnwtzpxx|z}|~~x|~z}x|v{rytzpxnwlvjuxtfsbq`p^o\nZmXlVkRiPhLfNgJeDbFcBa@`<^:]6[4Z0X*V(U$S&T Q"RPNMJK GHEAC@ CVECEiAEC GILNP"R0X(U8\4Z<^@`FcLfVkPhZm`pfslvpxx||~~E x|nwdrZmPhDb0X:]N$SIDEc FC D FHJMO(U"R2Y<^6[HdBaNgTjZm`pdrjuv{pxz}~%Ev{lvbqJeVk>_ Q2YJ@CEdC G"RM*V8\BaRiJeZmdr`pxtjulv xtfsbq^oZmVkRiNgJeFc@`Ba<^:]8\4Z2Y0X*V(U$S&T QOM GJDB@ CYEL@bCCd@QC@K#Cc@KEiCwABCD FE GHIJLMNOP Q"R$S(U*V0X2Y6[8\:]<^>_@`BaDbFcHdLfNgPhRiTjVkXlZm\n^o\nXlVkRiNgLfHdJeBaFc@`>_<^8\:]6[2Y0X(U&T$S"R QPONLKJI G FECAB@6>E=<;<=>@?/MTrkJAZ2{ O][SoloQEI ELpBDH FIMKNP Q"R$S&T1(U*V0X2Y4Z6[8\:]<^>_@`BaDbFc HdJeLf NgPh,Ri6Tj Vk)XlZmQ\ny^o\nZm#XlVkTjRi#PhNg LfJe&HdFc Db Ba@` >_<^:]8\6[2Y0X(U&T"R QPNOMLJIH G FDEC@BJZLWJJaRJJ\LJOeABED FIHKMO$S Q(U0X4Z:]8\>_Db@`JeHdNgTjPhVkZm\n`pbqdrxtjunwlvpxrytzv{x|z}|~~x|v{pxjuOdrXl^oRiJeBa4Z:]&TLPDHA@ OiB FNJ$S0X:]@`HdPhTjXl\n^oZmVkRiLf>_Fc8\$S0XINC@OQMONQMT OFJZM{J\E`.GQE%GJS#JL[+JaLDJ J\!JHA\-BPA)EWB!G^E(G>bG>->dN>#Ec0EGf1BCD FEI GJKMNO Q"R&T(U*V0X4Z8\6[:]4Z6[0X"R(UGO GLC@ EW EG_ ABDEI GJLNO Q"R&T$S(U0X2Y4Z6[8\:]@`>_BaFcHdLfPhRiVkTjXl\nZm^oG\nXlVkLfRiFc8\@`$S0XNDI@EbiE FQGX F(GJ]+JL`AEC GHKMP"R(U0X:]6[@`JeDbNgPhTjVkXlTjRiNgLHd>_Db8\$S0XOJ FB@L_CA FILO&T Q2Y*V6[>_:]@`HdDbNgLfPhRiPhNgJeFc@`6[<^L*VM"RB G@JcJG[TGJd JOd AC FDI GLNO Q$S*V2Y6[<^Db@`JeNgRiZmVk^o`pdrxtlvpxx|tzz}~o|~z}x|v{rytzpxnwlvjuxtdrbq`pZm^oXlRiVkPhHdLfDb@`<^8\2Y$S*V QKNHEC@O!Qm+QSdCS.VdBC FHKN$SP(U6[2Y<^@`FcLfPhVkZm`pxtdrnwv{ryx||~~Vx|ryjubqTj\nNgDb>_6[0X$SLPHDA@dVcADBE GHJIKMNP"R&T*V2Y6[:]Db>_HdNgRiVk`p\njudrnwtzx||~~3z}x|nwryjudr`p\nVkPhLfFc:]@`4Z$S*VLOHEB@V#VfmV!Vi+T\ VQYT)O_QLJRO.JM^LMO_ ACE GKINP"R&T*V4Z<^8\BaNgHdXlRi^olvdrry~x|Ox|tznw`pfsZmTjPhJeDb>_@`<^Of@`BaJeFcNgRiXl\n`pfsnwjurytzv{pxryjufs`pTjZmNgBaHd<^6[0X Q&TNKI G FEDCBA@+M\OAMJ[FJ!V`OY%ABCDE F GHI JKLMNOP Q$S&T*V(U2Y0X4Z:]8\<^@`BaDbHdVLfPhNgRiVkXlTjRiPhLfDbJe<^@`6[O0X&T QNKD G@1OV8O!M\%M)O^ AC FILO Q&T0X:]6[@`LfFcPhVkZmdr`pxtlvpxrynwjufs`pOZmRiLf>_Db*V6[$SOK GDBM\@3MMT&MJIJO[1O Ob1OOY#OMSMMOQ\&ACDE FH GJKLMNOP Q"R$S&T(U*V2Y0X4Z6[8\:](<^>_ @`Ba Db Fc HdJeLfNg PhRiTjVkXlZm\n^o`pbqdrxtfslvnwryv{x|~|~MQ,x|tzxtpxbq\nVkPhBaJe6[<^0X&T QNKHC F@ Oc;OMb.MJS6JHb.H EQ;EC^.ASCFA >=>AblACdKCAQACbBCD FI GLJMOP Q"R&T*V(U2Y6[8\<^@`BaLfHdNgVkRiZm^o`pxtdrjunwpxtzv{x||~~&Cz}lvrybqZmEdRiDb4ZN@<@C@.EC_BCEH GKMO Q$S(U2Y6[:]Db@`NgHdRiTjXl\n`pbqfsjulvnwpxnwjubqfs^oXlRiBaJe:]2Y&THNC@ Ed.EC\(ACBE F GHKJMNP&T"R*V2Y6[<^@`DbHdLfPhTjXl\n`pdrjuxtnwpxtzv{rylvpxCxtbqEa^oPhVkFc:]*VI@>@:AC@3E C\ BCE GJIMNP"R&T(U0X2Y8\6[:]@`>_BaFcHdNgLfPhRiVkXlZm^obq`pjufslvnwtzryv{rypxnwjufs`pC\nTjDbNg<^*VN@Ee;?@B@E Ca&BE GJOM Q(U$S0X4Z6[:]@`>_DbHdLfRiNgXlVk\nbq`pfslvjurypxv{x||~~ Cv{Egz}jupxbq\nTjLfBa8\E$S@;<@C@EC\BD FIPM$S*V2Y<^8\@`HdDbLfPhRiVkXl\n`p^obqfsxtnwjupxrytzv{z}|~~C ryx|lvdr\nRiEk0XBaD7@5>@ F@?@ E C\ADC FHJOMP(U$S*V4Z8\:]>_BaFcHdNgLfRiVkTjXl\nZm^obqdrxtjulvnwpxClvfsjubqZm`pEdTjDbNg8\*VH@>;@C@E#C`BD GJNP$S(U2Y8\>_BaHdNgTjXl^obqlvfspxx|tz~z}#z}v{pxlvdrC`pPhXlHd@`8\P*VEXIA@&EE>_EEc9EE^EEd(EGVGJa#JE[&EGPGJbJ GO+IPGIL]#LGP)INGIL^#LGK&GINI L[#LGL&GIQILa#LGH+IOGIL]#LGN%IKG#ILZLL\.LLT1NaLFNLWLNJOPNNROLXN#LIRNIL\nLO`QLQOLABQcDC F GHIKLNO Q"R&T(U*V0X6[4Z8\>_:]@`BaDbHdFcPhLfRiVkXl\n`pdrnwxtpxz}v{~Qx|ryjubqVk\nPhQiDbJe@`8\<^6[4Z8\:]<^@`DbHdLfTjPhXl^o`pjufsnwrypxv{x|z}|~~&Qz}x|rynwxtbqVk\nPhJeDbQa@`:]6[2Y*V(U&T (U0X4Z8\:]@`DbJeNgRi\nXl`pxtdrpxlvryx|v{z}~+Q ryx|drju^oPhVkHdQ^<^@`8\2Y4Z*V 2Y6[8\<^@`DbNgHdRi\nVk`pdrxtpxlvtzv{x||~~Qv{z}rylvfs`pRiZmLfDb<^(UQi4ZPL GDA@ BDHMP(U4ZBa:]JePhVk`p\ndrnwxtpxryv{x|z}|~~x|tznwxt`pVk\nPhDbJe>_8\4Z*V$S QKNHEC@QYQfDB GJMP$S*V4Z8\>_BaFcPhLfTjXl\n`pfsbqxtjunw juQfsbq`pVk\nRiLfFc<^@`6[0X&T QOLI GDQcB@3QO[&OLLLQfBE GJOM&T"R*V6[2Y:]@`DbJeNgRi\nXlbqfslvpxtzz}x|~Qz}tzlvdr\nRiHd2Y>_"REL@ O`NO La=LXSS^ XSVjB GDJMO&T"R*V8\4Z<^@`BaLfHdPhXlTjbq^ofslvpxx|tz|~~Vx|fspxXa^oRiHd<^2Y$SMA G@&XVf BEHK QN*V&T4Z<^8\Fc@`JeRiNgVkZm\nbq`pdrjuxtnwlvpxrytzv{z}~Apxx|VdrJeX\Xl<^*VPIC@ X CL G Q6[(UX]@`RiJebqZmxtv{px~z}Qz}tzx|nwrylvfsjudrbq`p^o!\nZmXlVkTjRiPhNgJeHdFcDbBa>_:]<^6[8\4Z*V0X&TP"RMOJH FDV\A@XlV SboQbSTQScS(ScBDI FN Q(U<^4ZDbTjLf\ndrlvz}ry~Sx|ryjubqZmFcPh2Y<^"R GM@AS\dSN_+ACDH FJMP(U"R0X:]6[Db@`JeTjPhXl`p\nxtdrlvrypxtzv{Npxrylvxtbq`pZmTjPhHdBa6[<^0X&TPN[JM GDECE FJHM"RO(U8\2Y@`<^FcJeNgPh LfHdBa<^6[*V"RHNC@NL[xLLd(LJSJ GN!FaG#FER ECR#CEi&ECMC@L)C`@(C@Q$@>P>>\F>=;<:867432/0.,*&($#!>#!&.*158>;@/MTrk0JAZ2 Faded Things/simutrans-124.5/simutrans/music/37-inevitably-engrossed.mid000066400000000000000000000647641520675460500240310ustar00rootroot00000000000000MThd xMTrkCopyright (C) 2007 shunter~ JAZ2jg~j 1 1  @1 F@1@1 @ @11 ~j@ F @1@1@ @@@L@~j1  @1 F @1 @1@@11 @ F @1@ @~j@11  @11 F @1@1@@1@ @~jL@11  @ F  @ @@ @  @ F  @~j @ @ @  @ F  @ @@ @vj@ @  L @  @ F  @ @@ @ njXQ YInevitably Engrossed/MTrk JAZ2@[({ @dBass]a!SpW!jT3<(Wq(!\J!7'!Qv'Q!s'(Sp#W(j#!TR!TR #Sf# Sf!TR!TR#Sp#Wj#Sf# Sf#TR# Sf !Sf! Sf#TR#TR !Sf! Sf #Sf# Sf !Sf!TR#Sp#Wj#TR#TR #Sf# SfUkLkXkRs&_T&!TD!)&[U&&Vx%T&r%&_T&!TD!)&[U&&Vx&&Tr&#`T#Uv#Us#XR #Xf# #Xf# Xf#XR# Xf !Sf! Sf #Sf# Sf!TR!TR#\J7#'#Qv#"Qs"#Sf#TR#TR#TR^kRkWk(Zs(!Sf! Sf #Sf# Sf!TR!TR#\T#Qv#Qs##TR# Sf#TR#TR !Sf!TR#TR#TR!TR! Sf #Sf#TR!TR! Sf#T3#>#W3#<#TR#TR #Sf#TR[kOkVkYs&_T&-TD-)&[U&&Vx&T&r&&^H&(!T!V&SU&!^q!#W3#>#Z3#>Xf#XR##XR# Xf #Xf#XR !Xf! Xf #Xf# Xf!XR!XR#Vp#Zj#XR#XR#XR# XfVk^kOkTs!Xf!XR #Xf#XR!XR! Xf #Vp#Zj#Xf# Xf#XR#XR !Xf!XR #Xf#XR!TR!TR #Sf# Sf!TR!TR#Sp#Wj#Sf# Sf#TR# SfPk\k]kZs&^H&(-T-V&SU&-^q-&_T&!TD!)&[U&&Vx&T&r&#W3#>#Z3#>Xf #Xf##XR#XR #Xf#XR!TR!TR #Sf#TR !Sf!TR#T3#>#W3#>#Sf# Sf #Sf#TRTk[k[C(e( Ws!N!SN#M!^O!!!fK!/MTrkJAZ2 @]Drums[({}`#<*P#*_*P&<&*_#<*P*#_*F&F*&7*<*#<*P*#_*P&<*&7*2**P#<#*_*F&F*&_#<*P*#_*P&<*&7*2*#<*P#*_*F&F*&7*<**P#<*#_&F*P*&7*F**P#<#*_&F*F*&7&P*P*&*P#<#*_*P&<*&7*2*#<*P#*_*F&F&*_*P#<*#_*P&<&*_*P#<*#_*F&F*&7*(**P#<*#_*P&<&*_#<*P#*_&F*F&*_#<*P*#_*P&<&*_#<*P*#_*F&F*&7*(**P#<*#_&<*P*&7*2*#<*P*#_&F*F*&7*<*#<*P#*_&<*P&*7*2**P#<#*_*F&F&*_*P#<*#_*P&<&*_#<*P#*_&F*F&*7*<*#<*P#*_&F*P*&7*F**P#<#*_*F&F*&7*P**P#<#*_*P&<*&_#<*P#*_&F*F&*7*<**P#<#*_&<*P*&_*P#<*#_&F*F*&7*<**P#<#*_*P&<*&7*2**P#<#*_&F*F*&7*(**P#<#*_*P&<*&_*P#<#*_&F*F&*7*<**P#<#*_*P&<*&_*P#<*#_&F*F*&7*(*#<*P#*_*P&<*&_*P#<#*_&F*F&*7*(**P#<#*7*(**P&<*&_*P#<*#_&F*F&*7*(**P#2#*_*P&<*&_#2*P#*_*F&F*&7.H.&<1F#<1#&_&F3P3&73(3#F3<3#_3F&P3&73(3#23<#3_&<3F&373(33F#<3#_3<&F3&7333F#<3#_&F3P&373(33F#F3#_3P&F3&7333<#2#3_3F&<&373(3#<3F3#_&F3<3&73(3#<3F3#_3P&F&373(33F#F#3_&F3P3&73(33F#<3#_3P&F3&73(33F#F3#_&F3P&373(33F#<3#_&F3P3&733#F3F#3_3P&F&3733#<3F3#_3P&F3&733#<3F3#7&F&3P&Z3&73(3#<*P#*_*P&<*&_*P#<#*_*F&F*&7*(**P#<#*_&<*P&*_*P#<#*_&F*F&*7*<*#<*P*#_*P&<*&7*2**P#<#*_*F&F&*7*<**P#<#*_&<*P&*7*2**P#<#*_&F*F*&7*(**P#<#*_*P&<&*_*P#<*#_*F&F&*7*<**P#<#*_&<*P*&_*P#<*#_*F&F&*7*<**P#<*#_*P&<*&_#<*P#*_*F&F*&7*<*#<*P*#_*P&F&*_*P#<*#_&F*F*&7&P*P*&1<*2&2#<1&*#_&F*P*&7*F*#<*P#*_*F&F*&7*P**P#<*#_&<*P&*_*P#<#*_*F&F*&7*<**P#<#*_*P&<*&7*2*#<*P#*_*F&F*&7*(*#<*P#*_*P&<*&_*P#<#*_&F*F*&_*P#<#*_&<*P*&_*P#<*#_*F&F&*_#<*P#*_&<*P*&_*P#<*#_&F*F&*7*<**P#<#*_&<*P&*_*P#<*#_*F&F*&7*<**P#<#*_*P&F*&7*F**P#<#*_&F*F*&7*P*#<&21<*2#&1*_*P&F&*_#<*P*#_&F*F&*7*P**P#<#*_*P&<*&_*P#<*#_*F&F&*7*<**P#<*#_*P&<*&_*P#<#*_*F&F&*_#<*P*#_*P&<*&_*P#<#*_*F&F*&7*(*#<*P#*_&<*P*&_*P#<#*_*F&F*&7*<*#<*P*#_*P&<&*_*P#<#*_*F&F*&7*<**P#<#*_*P&<*&7*2*#<*P#*_&F*F*&7*<**P#2#*_&<*P&*_*P#2*#_&F*F*&7*2*#<1F&<#1&_&F3P3&73(3#F3<3#_&P3F&373(33<#23#_3F&<3&73(3#<3F#3_&F3<&37333F#<#3_&F3P3&73(33F#F#3_&F3P&373(3#23<#3_3F&<3&73(33F#<3#_3<&F3&7333<#2#3_3F&<&3733#<3F3#_3<&F3&73(3#23<#3_3F&<3&73(33F#<3#_&F3<&373(3#23<3#_&<3F3&73(3#<3F3#_&F3<&37333F#<#370203P&F&370<3(033F-<3-7-<-3P&P&373-P3-#<&<1F&1#_&F3P&373(33<#F3#_&P3F&373(33F#<3#_3P&F3&7333F#F#3_&F3P3&7333<#2#3_&<3F&373(33F#<3#_&F3<&373(3#23<3#_3F&<3&7333F#<#3_&F3<&3733#<3F#3_&F3P&373(3#F3F3#_&F3P3&73(33F#<#3_&F3P&3733#F3F#3_3P&F3&733#<3F3#_3P&F3&73(3#F3F3#_&F3P3&733#<3F3#_&F3P&373(3#<3F#37&F&3P&Z&37&F3&3&<#<1F1&#_&F3P&37333<#F3#_&P3F&373(33F#<#3_3P&F&37333F#F#3_3P&F3&73(33F#<3#_3P&F3&733#F3F#3_&F3P&373(33<#2#3_&<3F3&733#<3F3#_&F3<&37333F#<3#_3P&F3&733#F3F#3_3P&F3&73(33<#2#3_3F&<&37333F#<#3_3<&F3&7333F#<3#_3P&F3&73(33F#F3#_3P&F3&733#<3F#37020&F3P&370<330-<3F-37-<-&P3P&37-P3(3-*P#<#*_*P&<*&_*P#<#*_*F&F*&7*(*#<*P#*_&<*P&*7*2**P#<#*_*F&F&*7*<**P#<#*_*P&<*&7*2**P#<#*_&F*F&*7*<*#<*P*#7*(**P&<&*_#<*P*#_&F*F*&7*(*#<*P*#_&<*P&*7*2*#<*P*#_*F&F*&7*<*#<*P#*7*(**P&<&*_#<*P*#_&F*F&*_#<*P*#_*P&<*&_*P#<*#_&F*F&*7*<*#<*P*#_&F*P*&_#<*P*#_*F&F&*7*P&P*&1F&<#<&1#_3P&F3&73(33<#F#3_3F&P&373(3#23<3#_&<3F&37333F#<#3_&F3<&373(3#23<#3_3F&<&3733#<3F3#_3<&F3&733#23<#3_3F&<3&73(33F#<#3_3<&F&373(3#23<3#_&<3F&373(33F#<#3_&F3<&37333<#23#_3F&<&3733#<3F3#_&F3<&373(3#23<#3_3F&<3&73(33F#<#3_&F3<3&733#<3F#37020&F3P&3730<303F-<3-7-<-3P&P3&73(-P-3#<*P*#_&<*P*&_#<*P*#_*F&F*&7*<*#<*P*#_&<*P*&_#<*P*#_*F&F&*_*P#<#*_&<*P*&_*P#<#*_&F*F&*7*<*#<*P#*_&<*P*&_#<*P*#_&F*F*&_#<*P#*_*P&<&*_*P#<#*_*F&F*&7*<*#<*P#*7*(*&<*P&*7*2**P#<#*_*F&F&*_#<*P*#_&<*P*&_#<*P*#_&F*F*&_*P#2#*_*P&<&*_*P#2#*_&F*F*&7*2*3F#<3#70203P&F&370<330-<3F-37-<-3P&P3&73-P3-*P#P*#7#F#*P&F*&_*P#P&2121#&*/MTrkJAZ2@ O{[F]xPianom8K13-,B8-1>F40;@(+9EN49>(;8K13-,B8-1 >9(%;;9G41&4(>9; =F9@40'-+6E6-649=@;9I*;60=?@6*9=>F60;@/+9E6;/6>9>;8I(;40;?]>;48(:6K13-,*16-@;9I*;60=?]@=*96?;9I/;=?60q@L/69?=@>F;@60/+9E^;>/69 >;8I(;40;?b;>4(8 >9;;/%9G61^/6>9;>F;@40(+8E6(>84;8A'18\81 =A@=:C6;*0^*:6@=9A28^92>F;@40(+8E^>(;48=F409@6E-+^6=94-9A48^94 >9/%;;9G616;69/>8I>;(;;?40](8;4>=99;-%(416G\9-=469K43*,R9*49K33/,9/3:9K23/,R29/8K23(,R2(89A28^29 >9;;(%418G6;>(84 =F9@40'8E\=948 @==A6;:C*0^6:*=@>F;@60/+9E6;>9/6:8K23(,R(28=K632,8>ABC 9<9 >=B6 24>AA=;C9;20.>A;92?F;@609E/+N/?6;99K43*,R9*4 ;A?=6;9C/0^;/69?9A28^92 @9*%=;61:G^6@*=:9A28^928K23(,*(286A'18661@9=;*%(9G61\=@*699A28^29 >9;;(%418G^4(8>; =9-%9;6G41^964-=9A486499I?;/;=?60q@L?6=9/@;A>=6;9C/0^9/>6;>F;@408E(+^8;4(>9A28^92 >;8I(;40;?b;4(>8 =94%9;8G6=849@9*%=;(61:G\*=@:69K23/,R92/8A28^82=F409@-+6E6-469= @F=@60'9E*+\6=@9*9K23/,R/29 >9(%;;8G41^84(;>6K13-,R61-9K43*,R*94 ?9/%=;9G61&69/=? >F;@60'9E/+\6;>/9>F40;@8E(+^8(4;>>F;@60/+9E^6;>/9 >;8I(;40;?b84>;(8K13-,R18-:A48^4:9A28^928K23(,R82(=F9@40-+6E^94=6-@F=@609E*+^6@=*9 >=;A6;9C/0^>;69/ >=;A8C4;(06>8;(4=9-%9;(416G\=-964 @==A6;9C*0^=96@*9K33/,B/399K23/,R92/8K23(,R82(9A28^92 >9(%;;418G^>(;84=F409@8E^84=9 @9=;*%61:G^6:=*@9K23/,R2/98K23(,R82( B;=I2;90>?N@y9=>26-%B*9Cl=:6>FK=6>9@>AA=9;;C20;9A>2:9K33/,B3/99A48^499K33/,R93/9K23/,R29/ =A@=6;:C*0^*6=@:9A28^29 >9(%;;418G6>(;84:6K13-,R-619A48^499A28^928K23(,R2(8 ==9A4;6C-06=964-@;9I*;=?60]6@9=*?;9I/;60=?q@L=?9/6@>9;;/%9G61^/69;>>F40;@(+8E^8(;4>9K23/,R92/8A28^82 9A==8C4;b948=@F60=@:E*+^6:*=@ >=;A6;9C/0^9;6/> >9(%;;8G41^>(;84 9A==6C4;-066-=949I@;*;60=?=9*6@ >9/%;;619G^>9;/6>F;@40(+8E^(>4;8=F409@-+6E69-=46 @F=@60'*+9E\6=@9* ?==A9C6;/0N?6=9/ >9;;/%619G696;/>>9(%;;(8G41\>;(48>F;@60/+9E^/6;9>8A28^828A18^81:K43*,*4*::9K23/,*/92:8K23(,R28(6K13-,*-16@==A6;9C'*0\@=96*9A28^298K23(,*(82:6K13-,R61-@F=@60*+9E^9*=6@9A38N93>F60;@/+9E^/9;6>>F40;@(+8E^(84;> ;A>=9C6;/0^;96/>8A28^28 ==9A4;8C:49=8:I@;*;60=?=@:*69A28^29>F40;@(+8E^(8>4; B92%>;=G91>B="BP=H2B=!9*2eA9>;2%;G91;>A"AP;H2A;!9*2e?9/%;;619G&;/?969A'486499I?;/;60;?9/;6? ;A>=9C6;/06>/;69:I@;*;60=?]@*=:6:9K23/,*2/9 >F40;@'8E(+\;4>(86A18^619K43*,R9*49A286298I>;(;40;?>(;48 9A==6C4;-0^-94=69A486949I?;/;=?60q@L/=6?9@ 9K23/,R/29 >;8I(;40;?b;48(>9K23/,*92/>;8I(;;?40];4(8> =F409@'8E6=894:A'48\:49A28^928K23(,*(28 =9974/8@-7 4 = 89--3-4;:4>45=N8R9?-794 8 =-#@/MTrkJAZ2][<{ 'd][<{ 'd][<{ 'd]]{ 'd][<{ 'd[<{Guitar 'd][<{ 'd@[=B=B>69[(+/-2686;6@6i//3282=8=;(@;=@=;/@28[-+66=6@6j66====@-@=@=6[*+1-4696=6B6i11344=99==B*==B=B941=[/-3696=6B6j33=99==/B==B=B93=S/83B9B=BBB3/9B=U9=;=B=9B;Q/32=8=;=@=;2@8/Q6=9=>=B=9>B6Q/32=8=;=@=8@/2;Q4=8===@=8@4=[*+1-46:6=6B6i11344=::=*=B==B==:4B1Q9=;=B=;9BQ/32=8=;=@=82@;/Q6===@=@=6Q134=9===B=9=41BQ6=9=>=B=B>69Q/32=8=;=@=@82;/[-+66=6@6j66====-@@=6=@Q134=9=@=B=1@9B4Q9===B=B9=S3B9B=BBB39=B_/-6696>6B6j66=99=>>=/BB=B9>6[(+/-2686;6@6i//322=88=;@(;=@=/@;82[/-6696>6B6j66=99=>/>=BB=69B>[(+/-2686;6@6i//322=88=@(;;=@=/82;@[--4686=6@6j44=88====-@@=4=8@Q134=:===B=:4=1BQ6=9=>=B=B>96[(+/-2686;6@6i//3282=8=(;@;=@=2@;8/Q9===B=9B=Q9B=BBB=9BU9;=;B;=B9H9@=@B@9=B[56<6>6E6j<<=>>=5EE=<>ES5BBEBE5<>U<;>;E;E<>'E/>6<65/5<5@><@>@EE@<5E>Q3=9=;=39;S/83B9B;B/39;U134=9===B=14=9B[/-3696;6j33=99=/;;=93;8/-6696>6B6 66=99=>/B>=B=69B>Q4=:===:4=Q6=9=>=B=9>6BQ/32=8=;=@=8@;2/Q6===@==6@Q134=9===B=1B=94Q6=9=>=B=96>BQ/32=8=;=@=/@82;[-+66=6@6j66====@-@=6=@Q134=9===B==14B9[/-3696=6B6j33=99==/==BB=39B=S/83B9B=BBB39/=B_/-6696>6B6j66=99=>>=B/B=9>B6Q/32=8=;=@=/2;8@[*+/-2696;6j//322=99=;*;=;9/2Q/32=8=;=@=/8@;2Q4=8===@==8@4Q134=:===B=:B=14[/-6696>6B6j66=99=>/>=BB=6B>9Q/32=8=;=@=;2@8/[-+66=6@6j66====@-@=@=6[*+1-4696=6B6i11344=99=B=*==B=4=1B9Q6=9=>=B=6>B9Q/32=8=;=@=/@8;2[-+66=6@6j66====@-@=@=6Q134=9===B=B=491Q3=9===B==3B9Q3B9B=BBB3=9BU6=9=>=B=>69BQ/32=8=;=@=;@2/8Q6=9=>=B=6>B9Q/32=8=;=@=@/;82Q4=8===@=8@=4Q134=:===B=4:B=1Q9=;=B=B9;Q/32=8=;=@=/8@2;Q6===@=@6=Q4=9====49[2696;6B6j99=;;=2BB=9B;[(+/-2686;6@6i//3282=8=;(@;=@=8/2;@Q6===@=@=6Q134=9===B==149B[3696=6B6j99====B3B=B=9S3B9B=BBB9B3=U9=;=B=;B9[(+/-2686;6@6i//3282=8=@(;;=@=8;2/@Q6=9=>=B=6>9B[(+/-2686;6@6i//3282=8=(;@;=@=28;/@Q4=8===@=84=@Q134=:===B=4B:=1[/-6696>6B6j66=99=>B/>=B=B9>6Q/32=8=;=@=@8/2;Q9===B=B=9Q9B=BBB=B9U9;=;B;9=BH9@=@B@B=9Q235=<=>=5<>2357 -45285BB2-5<>U225;<;>;<52>J-3265@<@>@>-25<Q6=9=?=B=6B?9367 6/86B9B?BBB6/?9BU134=9===B=49=B1Q6=9=?=B=?6B9Q9=;=B=;B9Q:===C=C=:Q6=9=>=B=69B>[(+/-2686;6@6i//3282=8=@(;;=@=/;28@[-+66=6@6j66====@-@=@6=Q134=9===B=941=BQ6=9=>=B=>B96[(+/-2686;6@6i//322=88=;@(;=@=8;2/@Q6===@=6=@Q134=9===B=B=194Q9===B==B9Q9B=BBB9=BU6=9=>=B=9B6>Q/32=8=;=@=@2;8/[/-6696>6B6j66=99=>/B>=B=69>B8(+/-2686;6@6 //322=@;8(8=;=@=@28/;[--4686=6@6j44=88==@-==@=4@8=Q134=:===B=B1:=4Q9=;=B=9B;Q/32=8=;=@=@;82/Q6===@=@=6Q134=9===B=49B=1Q9=;=B=9;BQ/32=8=;=@=82;/@[-+66=6@6j66====@-@==6@Q4=9===4=9Q9===B=9=BQ9B=BBB9B=U6=9=>=B=>9B6Q/32=8=;=@=82/;@Q6=9=>=B=9B>6[(+/-2686;6@6i//3282=8=;@(;=@=@;/82Q4=8===@=@48=Q134=:===B=B:=14Q9=;=B=;9BQ/32=8=;=@=28/;@Q6===@==6@Q4=9===B=4B=9[2696;6B6j99=;;=2BB=9B;Q/32=8=;=@=/8;2@Q6===@=6=@[*+4696=6B6j44=99====B*B=B=94Q3=9===B=39=BS/83B9B=BBBB93=/_/-6696>6B6j66=99=>>=B/B=9B6>Q/32=8=;=@=82@/;Q6=9=>=B=B>69[(+/-2686;6@6i//3282=8=;(@;=@=8;@2/Q4=8===@==8@4Q134=:===B=B:=14[*+/-2696;6j//322=9;*9=;=/2;9Q/32=8=;=@=;82/@82696=6B6 99==B==2B=9B=S2B9B=BBBB29=U9;=;B;B9='B/=6962/292@=9@B=@B@29=BQ<=>=E=<E>QBEB<>EU<;>;E;><EH<@>@E@>E<Q6=9=?=B=96B?S/86B9B?BBB?B6/9U4=9===B==94B8/-3696;6 33=9;/9=;=9;3Q9=;=B=;9BQ:===C=:=CQ9=;=B=9B;Q/32=8=;=@=8@2;/[-+66=6@6j66====@-@==@6Q134=9===B=1=94B[/-6696>6B6j66=99=>/>=BB=6B>9Q/32=8=;=@=8;@2/Q6===@==6@Q134=9===B=B4=91Q9===B==B9S3B9B=BBBB=93U6=9=>=B=6B9>Q/32=8=;=@=/@28;[2696;6B6j99=;;=2BB=;9BQ/32=8=;=@=;@/28Q4=8===@==84@Q134=:===B==:4B1Q9=;=B=;9B[(+/-2686;6@6i//322=88=;@(;=@=/8;@2Q4=8===@=@4=80@/=6864/-8484B8B=@=B@B-48=@b-.4787=7@7"-8=@4D-24;8;=;@; -48=@_@/MTrkR0JAZ2]Strings@[<{ hd`ZU jZvZ(PU ZS)Y3YSQ)WSQWQ XkXQQ)WcWQQ)VcVQR)XcXRVQ&kQVP&VkVPZU&!UU%vU ZS)Y3YSQ)WSWQQ)XcXQQ)WcQWQ VkQVR)XcXRQ)VcQVP)VcPVN)UcNUQ)XcQXQ&VkQVP)VcVPN)UcUNQ)XcXQWQ&!QQ#QWQ)VcVQP)VcVPQ)VcVQP)VcVPP)UcUPR)XcRXQ)VcVQVP kPVN)UcUNXQ kXQQ&VkVQP)VcVPUN&kNUQ&XkQXQ)WSWQQ&VkVQP VkVPQ)VcQVVP kPVUP kPUR)XcXRQ)VcQVP)VcVPU)Z3ZUS)Y3YSWQ jWqQQ)XcXQWQ&kQWQ)VcVQR)XcRXVQ kVQP)VcPVP UTUPe@/MTrkJAZ2/MTrk:JAZ2Melody Harmony (below)/MTrk/JAZ2 Piano (3Pt)/MTrkfJAZ2[K{ EVoice 1 (Melody)]dDaS@PDJ@DdL@ND@@=H,@Z=A@3@W4@3@bu@ @UG@ITKIE_6BCE>BGQGGFBZY>gB5=W >E>T =6@\>*BY@?B G`ZDLGnDETUEIQvIL[@L=cx@e=r@DQ'DL9Su@U9N@=Q_=BS0B@SD@BSIBDSMDESuEISDIDSHD>PT>!GVmG=W&>J=P>IWIGc^G9R.9>=U== @V@BP2B EetE,BT*B>T2>=7R;E=d;GgG E[7EB>W*>MGb7G?EU=EGB5(BIi9I;BPZG[B2G9GQLSG9LIOI]DaS@PDJ@DdL@ND@@=H,@Z=A@3@W4@3@bu@ @UG@ITKIE_6BCE>BGQGGFBZY>gB5=W >E>T =6@\>*BY@?B G`ZDLGnDETUEIQvIL[@L=cx@e=r@DQ'DL9Su@U9N@=Q_=BS0B@SD@BSIBDSMDESuEISDIDSHD>PT>!GVmG=W&>J=P>IWIGc^G9R.9>=U== @V@BP2B EetE,BT*B>T2>=7R;E=d;GgG E[7EB>W*>MGb7G?EU=EGB5(BIi9I;BPZG[B2G9GQLSG9LIOI]DaS@PDJ@DdL@ND@@=H,@Z=A@3@W4@3@bu@ @UG@ITKIE_6BCE>BGQGGFBZY>gB5=W >E>T =6@\>*BY@?B G`ZDLGnDETUEIQvIL[@L=cx@e=r@DQ'DL9Su@U9N@=Q_=BS0B@SD@BSIBDSMDESuEISDIDSHD>PT>!GVmG=W&>J=P>IWIGc^G9R.9>=U== @V@BP2B EetE,BT*B>T2>=7R;E=d;GgG E[7EB>W*>MGb7G?EU=EGB5(BIi9I;BPZG[B2G9GQLSG9LIOI/MTrkQJAZ2 E{[K]Voice 2 (Harmony) d@aS=P@J=@dL=N@@=9H,9Z9A939Wk98WD83;bu; =UG=ETKE@_$@?_ ?C?>?BQGBF>ZY9g>59W 9<98W;T 86=\;*>Y=?> B`Z>LBn>@TU@FQoFEQEE[dED[WD8cx=e8r=@Q'@L6Su=U6N=8Q_8=S0==SD==SI=@SM@@Su@ESDE?SH?9PT9!DVmD9W&9J9P9EWEDc^D4R.4>:U=: =V==P2= BetB,>T*>9T2997R8E9d8BgB B[7BB9W*9MBb7B?BU=B<=GW=6T6[9dO95NL5(9_.9I*Df>>D=5!=>5>Ei9E;>PZD[>2D9@QIS@9IFO FEOE]@aS=P@J=@dL=N@@=9H,9Z9A939Wk98WD83;bu; =UG=ETKE@_$@?_ ?C?>?BQGBF>ZY9g>59W 9<98W;T 86=\;*>Y=?> B`Z>LBn>@TU@FQoFEQEE[dED[WD8cx=e8r=@Q'@L6Su=U6N=8Q_8=S0==SD==SI=@SM@@Su@ESDE?SH?9PT9!DVmD9W&9J9P9EWEDc^D4R.4>:U=: =V==P2= BetB,>T*>9T2997R8E9d8BgB B[7BB9W*9MBb7B?BU=B<=GW=6T6[9dO95NL5(9_.9I*Df>>D=5!=>5>Ei9E;>PZD[>2D9@QIS@9IFO FEOE]@aS=P@J=@dL=N@@=9H,9Z9A939Wk98WD83;bu; =UG=ETKE@_$@?_ ?C?>?BQGBF>ZY9g>59W 9<98W;T 86=\;*>Y=?> B`Z>LBn>@TU@FQoFEQEE[dED[WD8cx=e8r=@Q'@L6Su=U6N=8Q_8=S0==SD==SI=@SM@@Su@ESDE?SH?9PT9!DVmD9W&9J9P9EWEDc^D4R.4>:U=: =V==P2= BetB,>T*>9T2997R8E9d8BgB B[7BB9W*9MBb7B?BU=B<=GW=6T6[9dO95NL5(9_.9I*Df>>D=5!=>5>Ei9E;>PZD[>2D9@QIS@9IFOF/MTrkJAZ2Voice 3 (Harmony) { E][Kd=aS8P=J8=dL9N=@94H46H6Z6A636Wk64WD438bu8 8UG8@TK@=_69C=>9?QG?F9ZY6g952W 6E8T 269\8*9Y9?9 >`Z;L>n;=TU=@Qo@BQBB[dB@[W@4cx8e4r89Q'9L2Su9U2N92Q_29S098SD88SI8=SM==Su=@SD@9SH96PT6!>Vm>4W46W6J6P6>W>>c^>1R.1>4U=4 :V::P2: >et>,9T*96T2647R2E4d2=g= =[7=B6W*6M=b7=?=U==<9GW91T1[5dO50NL0(5_.55T^59eh9?SHE[?;E?>U_>>^@>:=Zv@G =L@>Q9>!@[wE\@=E*B^(B=>C2>T >I>DYDX8hQ4R 8?4I 4J1X4;14O649[G9-9Od96Pc6c>b5>89I*>f9>>95(9>i9>;9PZ>[92>9=QDS=9D@O @BOB]=aS8P=J8=dL9N=@94H46H6Z6A636Wk64WD438bu8 8UG8@TK@=_69C=>9?QG?F9ZY6g952W 6E8T 269\8*9Y9?9 >`Z;L>n;=TU=@Qo@BQBB[dB@[W@4cx8e4r89Q'9L2Su9U2N92Q_29S098SD88SI8=SM==Su=@SD@9SH96PT6!>Vm>4W46W6J6P6>W>>c^>1R.1>4U=4 :V::P2: >et>,9T*96T2647R2E4d2=g= =[7=B6W*6M=b7=?=U==<9GW91T1[5dO50NL0(5_.55T^59eh9?SHE[?;E?>U_>>^@>:=Zv@G =L@>Q9>!@[wE\@=E*B^(B=>C2>T >I>DYDX8hQ4R 8?4I 4J1X4;14O649[G9-9Od96Pc6c>b5>89I*>f9>>95(9>i9>;9PZ>[92>9=QDS=9D@O @BOB]=aS8P=J8=dL9N=@94H46H6Z6A636Wk64WD438bu8 8UG8@TK@=_69C=>9?QG?F9ZY6g952W 6E8T 269\8*9Y9?9 >`Z;L>n;=TU=@Qo@BQBB[dB@[W@4cx8e4r89Q'9L2Su9U2N92Q_29S098SD88SI8=SM==Su=@SD@9SH96PT6!>Vm>4W46W6J6P6>W>>c^>1R.1>4U=4 :V::P2: >et>,9T*96T2647R2E4d2=g= =[7=B6W*6M=b7=?=U==<9GW91T1[5dO50NL0(5_.55T^59eh9?SHE[?;E?>U_>>^@>:=Zv@G =L@>Q9>!@[wE\@=E*B^(B=>C2>T >I>DYDX8hQ4R 8?4I 4J1X4;14O649[G9-9Od96Pc6c>b5>89I*>f9>>95(9>i9>;9PZ>[92>9=QDS=9D@O@/MTrk8JAZ2Inevitably Engrossed/simutrans-124.5/simutrans/music/38-positive-thrill.mid000066400000000000000000000452651520675460500230260ustar00rootroot00000000000000MThdxMTrk^Copyright (C) 2007 shunter~ JAZ2j~j'@'''@@@~j'@(@ @@@'~j'@@@'@(@ ~j@'@@'vj@@@@!@ @@PjXQYPositive Thrill/MTrk{ JAZ2[(]d ,{Bass@+Qw+t&L&f+Rw+}&P&V+Ly+s&N&\+K+.&@!&+QI+,#RL#0$Qw$tLf$Rw$}+P+T+Qw+t&L&f+Rw+}&P&U2K2j&U&T$Dx$|$Lr$y+Hy+w&O &e+Qv+2Zo2/We/&IV&"$Hn$ "Lq"HpOo )Hn) 'Lq'$Hp$!Oo! $O`$(Pl( +Hp+ -Kl- +O`+&Pl& $O`$(Pl( )Hn) 'Lq'$Hp$!Oo! *O`*-Pl- 0Hp0#Kl# $O`$(Pl( +Hp+ -Kl- -Hn- +Lq+(Hp(%Oo% &O`&!Pl! HpKl +Hn+ )Lq)&Hp&#Oo# $@W$ (JZ(+DT+$-AR-'&Hn& $Lq$!Hp!Oo O`#Pl# &Hp&(Kl( )Hn) (Lq(&Hp&#Oo# O`#Pl# &Hp&(Kl( )Hn) (Lq(&Hp&#Oo# $O`$(Pl( +Hp+-Kl- .Hn. -Lq-+Hp+(Oo( O`#Pl# &Hp&(Kl( )Hn) (Lq(&Hp&#Oo# &O`&*Pl* -Hp- /Kl/ $@W$ (JZ(+DT+$-AR-'O`#Pl# &Hp&(Kl( )Hn) (Lq(&Hp&#Oo# $@W$ (JZ(+DT+$-AR-(O`!Pl! $Hp$ &Kl& $@W$ (JZ(+DT+$-AR-'O`Pl $O`$Pl O`!Pl! $Hp$ &Kl& *O`*-Pl- 0Hp0#Kl# $O`$(Pl( +Hp+ -Kl- -Hn- +Lq+(Hp(%Oo% &O`&)Pl) -Hp-*Kl* +@j+&Mq&Fj#Os#$Hn$ "Lq"HpOo &Hn& $Lq$!Hp!Oo +I +M&Q%&P+QJ+ +F+&XW&#2Qp2*Mt*+K+Z2P&2J+Q+X&Ps&#8k# $I $MQ%P$QJ$ $F$XW#+Qp+*Mt* +Ly+s&N&\+K+.&@!&+QI+,%RL%/&Dx&|!Lr!z$Dx$|Lry+I +M&Q%&P+QJ+ +F+&XW&#2Qp2#Mt#$O`$(Pl( +Hp+ -Kl- O`!Pl! $Hp$ &Kl& $@W$ (JZ(+DT+$-AR-'+O`+&Pl& $O`$+Pl+ O`!Pl! $Hp$ &Kl& *O`*-Pl- 0Hp0#Kl# $@W$ (JZ(+DT+$-AR-(!O`!%Pl% (Hp( *Kl* &O`&)Pl) -Hp-*Kl*+Oh+)Ye)(Iy(&So&$O`$(Pl( +Hp+ -Kl- &O`&*Pl* -Hp- /Kl/ YU#Zb#$\Z$!%eW%$&aZ&ii/MTrkH(JAZ2 @.]][({W$ODrums!$.R,)&*1,*&U*.$S*$Y&*P*&Y*E$P!$*U&*F!*&U*M$J"*$+&O#&&W"&.&0!&*8$L$*b*\&j*&[*J$U*$W*S&!&*U*>$L *$T&q*W*&2& & *j$I$*1&`$&&3 &&& &&$&.J$X.,$",.*\&J* &\$S*C *$V*Z&*&[$O*=*$W*X&m &*U$S*="*$,&C& &b&h$P*C$*W*U&&*V*J$[*$V&*O*&[$L*> *$T&q*W*&2& & *j$I$*1&`$&&3 &&& &&$&$L*8$*b&j*\*&[$U*J*$W*S&!&*T$O*=*$W*X&m &*U$S*="*$,&C& &b&h*F$X$*W&*j&*Y*C$S!$*T&v*E"*&T*C$J $*U&.` &.U$P.`!$.,&y& &`#&-&j& $`1l&F$1&S3l&$\!$&3)&(&&$O3M$3X3i$`&$&3-3L&0#3&$`3r 3$V&t$h3i3&$,&03@3&$[3r$3Y$_&3q&3$.3@#3$l3M!3$.&#&$h3g$3$Q3$i&&$3.&#& 3j$b*3$#&&$M3$3,&J"&&$c$&.22 $b!$.0e0 --Y1$n1$W&3$h3$&.3<3 3W$b$3Y3&$h"3$&*3$c.$1&!&&$c&$1& & &9$`&$&1& && $c&!&$U1l$`&F$1&S&3l$\!$&3)&(&&$O3M$3X3i&$`$&3-3L&0#3&3t$^$3V&$\3m)3$&"3O3 $k3\"3$S$i3&3&$,3@3 3M$l!3$.&#&$h3g$3$Q3$i&&$3.&#& 3j$b*3$#&&$c.>.$1&!&$c&&$1& & $`&9&$&1& && $c&!&$U$i1o!$1S$f3&3&$-3C#33o$f 3$X$\&33$&+3O 33r$` 3$V&t$h3i3&$,3@&03&3r$[$3Y$_&3q&3$.3@#3$n11$W&3$h3$&.3<3 $b3W$3Y3$h&"3$&*3.$1&!&&$c&$1& & $`&9&$&1& && $c&!&$U$L*8*$b&j*\*&[*J$U*$W*S&!&*T$P*E!$*U&*F!*&U*M$J"*$+&O#&&W"&.&0!&$O.]!$.R,)&*1,*&U*.$S*$Y*P&*&Z*>$L *$T&q*W*&2& & *j$I$*1&`$&&3 &&& &&$&*8$L$*b*\&j*&[*J$U*$W*S&!&*T*C$L $*U*H&!&*-*(%*$Y*H$*X*S&]*&-&z &*F$X$*W&*j&*Y*C$S!$*T&v*E"*&T*E$P!$*U&*F!*&U*M$J"*$+&O#&&W"&.&0!&$L*8*$b&j*\*&[$U*J*$W*S&!&*T$O*=*$W*X&m &*U$S*="*$,&C& &b&h$U*> *$U*_&&*X*5$P*$U&*Z*&Z.B$M!.$,&t!&$P.w$.2&!& $J.t.$&+ &&e!&&$M &$&;"&&M$&1l$`&F$1&S&3l$\!$&3)&(&&$O3M$3X3i&$`$&3-3L&0#3&$`3r 3$V$h&t3i3&$,&03@3&$[3r$3Y$_&3q&3$.3@#33M$l!3$.&#&$h3g$3$Q3$i&&$3.&#& $b3j*3$#&&$M3$3,&J"&&$c$&.22 $b!$.0e0 --Y1$n1$W&3$h3$&.3<3 $b3W$3Y&$h3"3$&*3.$1&!&&$c&$1& & $`&9&$&1& && $c&!&$U$n11$W$h&33$&.3<3 3W$b$3Y3&$h"3$&*3.$7&&$c&$&7&&&9$`&$&1&&&$c&&$_$Z&s&$_2s2_$s$7$s$2s2_-s&-&_,Z,7&s.s$s&$.7,2,/MTrkHJAZ2[({ 1PPiano@]O\M\J\I\IM OCSJ O\H\L\C4HOLCPCF\OUJV!FG!GOJ*LWOOH\:HL O-O\F\JXFGF:G J OJ\I\M\O\I OMCJJ"O[L\H\C2H LO3O\I\J\IJO@I\OTJDI:O JTV:V Z\[\^\Z[^AZ\^\[\![[MZ^^R[[H^^V[[M^$^\[^[R$[^\[A^[^\^[N"^\ [^G]\Y\BY ]TKT W\[X=W [.O\CO5[\U\V\U[V7T\X\TX[IXUT[O\[X T9O+R\V\S\ R$VSUUJ\"V\J UVJHVDJJ?VVYJJVVV<JV&P\Q[TYVTPQVTFP\QYT\V\8P QTV,VSO\SV1OSV:T\O\K\KL#LOT HCHT\M\Q\,MQTT>O\K\)LHK3OLT:H\T\PH TI\J[O[R\IJRO;IXJROUR\I$JRO*I\JWOUR\I RJOI\JSO\RTIXRJO#I\OXJ\R\"IJ OR$N\O\R\NRO H(HM\Q\DQM0J\M\$JK6'KMH?M\HQ\=MQJ\M[ KNJNK HKMHM\Q\@QM3K\O\$KL6'LOH?O\HR\=ORK\O[ LNKNL HKOHO\R\@RO0L\RZMXOXLAM ORZC\%CL\F\XFLL\Q\$LM6'MQM?Q\MT\=QTL\Q[ MNLNM MKQMT\Q\@TQ3K\N\$KK6'KNH?N\HQ\=NQK\N["KZHKNHQ\N\@QN(N\O[TTRYNOTRFN\R\OYT\8N OTR,M\TSQV1MQT@QXK\LU KoQ L%K\Q\LYKdENQLEJ\Q\=JQ!M\Q\$MM6'MQJ?JQ\T\=QTM\Q["MZJKQJT\Q\@TQ1VS$VY\VBYVY\YVO"Y\ VYGX\T\BT XOLO R\VY=R V7VXJ\-J V8H\T\JTRNHRHVT\IHR\T&RfP\Q[VTTYPQVTFP\V\QYT\8P QVT,O\VSSV1OSV-I\J\O\RYIJ RO/I\OSJYR\ IJRO.OUI\JYR\*I#RJO%O[AO I\R\JZOP$IO.RJOP#R\J\I\O-IJ R$O\CO5[\U\V\U[V7T\X\TX[IXUT[O\[X T9O+R\V\S\ R$VSJ\UU"V\J UJHVVDJJ?VVYJJVVV<JVV:V Z\[\^\Z[^AZ\^\[\![[MZ^^R[[H^^V[^[M$^\[^[R$[^\[A^[^\^[N"^\ [^G]\Y\BY ]TKT W\[X=W [!I\J\O\RYIJ RO/I\OSJYR\ IJRO.OUI\JYR\*I#RJO%O[AO I\R\JZOP$IO.RJOP#J\R\I\O-IJ R"P\V\TZQZPQVTAP\T\Q\VY%PQVT2OZVSST0OSV3N\T\RZOZNOTRAR\N\TYO\%NOTR2MZTSQT0MQT8C\O\7OC6I\MZJ\OZIOMJ>O\H\L\8HL O1O\I\J\I"OJI\>\ J\>I>CJJQ>J>>JO>>E JJ?0J> N\O[TTRYNOTRFN\R\OYT\8N ORT,TSM\QV1MQT8S\Y\WZTZSTYWAS\W\T\YY%STYW2RZYSVT0RVY;TXN\OU NoT O%N\T\OYNdHNTOHT\M\=MTMNLXR\O\LaMORTOLR\K[KL\UOR L0`\T\B` TuM\X\Y\MXMMYMYSMFYYYMMOYYKMM;YMK\N\$KK6'KNH?N\HQ\=NQK\N["KZHKNHQ\N\@QN0N\T\OZRZNOTRAR\N\TYO\%NOTR2MZTSQT0MQT;QXK\LU KoQ L%K\Q\LYKdENQLEQ\J\=JQ!M\Q\$MM6'MQJ?JQ\T\=QTM\Q["MZJKQJQ\T\@TQ(I\J[MYOTIJOMFI\JYM\O\8I JMO,OSH\LV1HLO@[\O\B[ OuH\S\T\HSHMTHTSHFTTYHHOTTKHH;THP\VUQYY\*P#YQV%V[AV P\QZY\VP$PV.YQVP#Q\Y\P\V-PQ Y'C\O\7OC6I\MZJ\OZIMOJ>O\H\L\8HL O1O\I\J\I"OJI\>\ J\>I>CJJQ>J>>JO>>E JJ?0J>C\O\7OC6I\MZJ\OZIOMJ>O\H\L\8HL O1I\O\J\I"OJI\>\ J\>I>CJJQ>J>>JO>>E JJ?0J>T\CT5`\Z\[\Z`[7Y\]\Y]`I]UY[T\`] Y9T+W\[\X\ W$[XZUO\"[\O ZOH[[DOO?[[YOOV[[<O[-I\J\M\O\I,JMOCCCOYLYH\9HLOCLCOYF\JX5FG8_GOJC\O\|LFOCL O\C\ECOP\Q[TYVTPQVTFP\QYT\V\8P QVT,VSO\SV1OSV@TXN\OU NoT O%N\T\OYNdHNTOHM\T\=MTI\J[O[R\IJRO;IXJROUR\I$JRO*I\JWOUR\I RJOI\JSO\RTIXJRO#I\OXJ\R\"IJ OR{V9V Z\[\^\Z[^AZ\^\[\![[LZ^^Q[[G^^U[[L^$^\[^J\M\$JK6'KMH?M\HQ\=MQJ\M[ KNJNK HKMHQ\M\@QM2N\O\R\NRO H(HM\Q\DQM-L\RZOXMXL#ORMOLR\K[KL\UOR L0YXS\TU SoY T%S\Y\TYSdMNYTMY\R\=RYpQ\K\H\KHQ_N?N KVH\Q\bQN\HK!NJN\T\RZOZNOTRAN\R\TYO\%NOTR2MZTSQT0MQT;X\]YW\W ]XX\]A]XX\]W]XX\]V]XW\]ZX\WX]XW ]@]XX\]\ ]X T\M\J\MJT_Q?Q MVT\J\bTQ\JM!QS\M\J\MJS_O?O MVS\J\bSO\JM!OQT\CT/Z\`\ [\Z!`[:Y\]\ Y]`C ]OYUTV`] Y:T*Y\]\Z\ Y$]ZQ\\V"]\Q \QK]]\QQL]]\QQY]]?Q]/RZ QIOCRL?QOJ[ LJVO\H\I\#IOHFZ'CYF0C?C\OCv+\L+O@/MTrkUJAZ2[({ ^d@]Guitar+K1W2M 1+ 272J+H*+2I4_+Z/+4C4Q+J44+B+I5b,+ 5?5^+L35+4I+H&+4E4`+R3+42L+`-2 +91j+]2[1+2>+D2F*+2I+\4d,+4G+-49*+ 4D2R+J62+@+G2H02++\4[+4T4_+S!++Z42a>2 +-0K7W(07M7F0:J0G80:0U9] 90D0b9K$090^7g.07G+d2M=2+$+I2L8+2<4_+W2+4@+G4T.+4G5l+R&+5K5a+P:+5+D4I%+4N4b+W$++02N442 +?6j0]7[60797F0D*07L9d0\,09?990-*09E5b?K-? 5>5G?J85?5U>] >5D5b>K$5>d,5>G>95-*5 >D3Y9[,93>3D9,*9&3b9Z3$39T3'9,$99Q33G*9 3H6j0]7[60797F0D*07L9d0\,09?990-*09G-L3X4N 3- 474K-I*-4I6`-[/-6C6R-K46-;9\2^,29>2_9b*29I9e2],29G9R2e*2 9D5m+S&+5K+Q5b:+54J+E%+4N+%4B$+2c4+;(+ 2D:K0b-: 0>0G:J80:9]0U 90D0b9K$097g0^.07M8j2]9[82999F2D*29L;d2\,2;?;92-*2;E2I1[+N 1+2?+G2G4+2<4[+U:4+:4Y+]14+D+J2P12+6+N2K@2 ++]4O&+4K+U4Y4+ +]2K'2+Q2I1[+N 1+2?2G+G4+2<+U4[:4+:+]4Y14+D2P+J12+6+N2K@2 ++]4O&+4K+U4Y4+ +]2K'+2Q0K7W(07M7F0d5\,5>G>95-*5 >D0d7M=70-7L0I807<9_0W209@9T0G.09G:R5R-:5::R5R15 :4R:RI:4+:14"$:4R4:R14 :;5b?K-? 5>?J5G85?5U>] >5D5b>K$5>5G5J+G8+54]+U 4+D4K+b$+42g+^.+2G:m0S&0:K0Q:b:0:9J0E%09N0X9N$07^01947 0;2L8X9N 82 979K2I*29I;`2[/2;C;R2K4;2=+K1W2M 1+ 272J+H*+2I4_+Z/+4C+J4Q44+B+I5b,+ 5?5^+L35+4I+H&+4E4`+R3+42L+`-2 +9+d2M=2+$+I2L8+2<4_+W2+4@+G4T.+4G5l+R&+5K5a+P:+54I+D%+4N+W4b$+2N+0442 +97I6[0N 607?0G7G407<0U9[:90:0]9Y190D0J7P17060N7K@7 00]9O&09K9Y0U90 0]7K'70Q+N2I1[ 1+2?2G+G4+2<+U4[:4+:+]4Y14+D+J2P12+6+N2K@2 ++]4O&+4K+U4Y4+ +]2K'2+Q9Q2KB2 9+9L2O=299P2U<298982'929L23(29;7M0d=70-7L0I807<0W9_209@9T0G.09G2I+N1[ 1+2?2G+G4+2<4[+U:4+:4Y+]14+D+J2P12+6+N2K@2 ++]4O&+4K4Y+U4+ 2K+]'+2V7Y0M(07H7H0>/70F0[9_%09L9N0C,09L5b?K-? 5>?J5G85?>]5U >5D5b>K$5>5^3D9,*9&9Z3b3$93T9,3'$99Q3G3*9 3B7M0d=70-7L0I807<0W9_209@9T0G.09G-d4M=4--4L-I8-4<6_-W2-6@6T-G.-6G5Y<[,<5><,5D*<&[#[VSV?[4S9NSV[@@/MTrkJAZ2 @]Melody[({Ob+OOb:O8OH0LWOG J`J GP4J9GJ ORrOGZAG JLiJC_,LbCAL H^,HKJO(JL^mLOLR1OfL>O QJ,QOP+O#La_L6JTlGFJ.GJRJOVDO4JVsJGV.GAEZ!E(EFE"EWi<tC\>uETCyFZEwHPFH-CH#C @QA@ @H,C^@=CCX(CE^mECa7CAFfAF CJ-EJCFECa_CzEDEHb:H GD/EWG>E CW4ENCE C\,H[C8H JW1LNJ&LNFNO`>O J^SJ$JkJOe@O QRuQLVxOPLzJJOuJLRwGOL1GfHb:H JK(J LbxOYL&O$QbDQ4NgGN3LaoJOLvJ7Ob+OOb:O8OH0LWOG J`J GP4J9GJ ORrOGZAG JLiJC_,LbCAL H^,HKJO(JL^mLOLR1OfL>O QJ,QOP+O#La_L6JTlGFJ.GJRJOVDO4JVsJGV.GAEZ!E(EFE"EWi<tC\>uETCyFZEwHPFH-CH#C @QA@ @H,C^@=CCX(CE^mECa7CAFfAF CJ-EJCFECa_CzEDEHb:H GD/EWG>E CW4ENCE C\,H[C8H JW1LNJ&LNFNO`>O J^SJ$JkJOe@O QRuQLVxOPLzJJOuJLRwGOL1GfHb:H JK(J LbxOYL&O$QbDQ4NgGN3LaoJOLvJ7Ob+OOb:O8OH0LWOG J`J GP4J9GJ ORrOGZAG JLiJC_,LbCAL H^,HKJO(JL^mLOLR1OfL>O QJ,QOP+O#La_L6JTlGFJ.GJRJOVDO4JVsJGV.GAEZ!E(EFE"EWi<tC\>uETCyFZEwHPFH-CH#C @QA@ @H,C^@=CCX(CE^mECa7CAFfAF CJ-EJCFECa_CzEDEHb:H GD/EWG>E CW4ENCE C\,H[C8H JW1LNJ&LNFNO`>O J^SJ$JkJOe@O QRuQLVxLOPzJJOuJLRwGOL1GfHb:H JK(J LbxOYL&O$QbDQ4NgGN3LaoJOLvJ/MTrk3JAZ2Positive Thrill/simutrans-124.5/simutrans/music/39-bangin-mover.mid000066400000000000000000001240371520675460500222500ustar00rootroot00000000000000MThdxMTrkkCopyright (C)2007 shunter~ JAZ2j'~jq A'@ A'@@'1 @'@~j@' @'+@@'~j@@1 @'@@1 @~j@@' @'@@'~j@@ @@@ ~j@@@ @@fj@@@ @ljXQY Bangin' Mover/MTrkJAZ2[(]Melodyp '{VCl3CB@a+>\ @K6<4;f9Y;39E;i9;9i>@U-@ @o@@i2@A@obCl@2C59sEo9GECou@OC&AV @dA@nx@Cl3CB@a+>\ @K6<4;f9Y;39E;i9;9i>@U-@ @o@@i2@A@obCl@2C59sEo9GECou@OC&AV @dA>OC> ic>@iU@CiRC9i!8P9E89>*_9>A@\7@@CaFCAqA@\1@LElFEH_XH,V5>E@sJ@Ee>E5@g?@6>if>(>YE>@R#@AkJA \ @K6<4;f9Y;39E;i9;9i>@U-@ @o@@i2@A@ob@Cl2C59sEo9GECou@OC&AV @dAI@">kEE+E$C_2CC@OM@,@JI@y<.RC>7E[wEVGb-GIE[.CQEBC0@L2@FGU/GEEK6E@C\NC I@">kEE+E$C_2CC@OM@,@JI@y<.RC>7E[wES;>AdDAME`5EHZ6> @RT@>TC>)CYrCEa>E>@O>Gb-GIE[.CQEBC0@L2@FGU/GEEK6E@C\NC I@">kEE+E$C_2CC@OM@,@JI@y<.RC>7E[wETCl3CB@a+>\ @K6<4;f9Y;39E;i9;9i>@U-@ @o@@i2@A@obCl@2C59sEo9GECou@OC&AV @dA@nx@Cl3CB@a+>\ @K6<4;f9Y;39E;i9;9i>@U-@ @o@@i2@A@obCl@2C59sEo9GECou@OC&AV @dA>OC> ic>@iU@CiRC9i!8P9E89>*_9>A@\7@@CaFCAqA@\1@LElFEH_XH,V5>E@sJ@Ee>E5@g?@6>if>(>YE>@R#@AkJA \ @K6<4;f9Y;39E;i9;9i>@U-@ @o@@i2@A@obCl@2C59sEo9GECou@OC&AV @dAZ+29+Q2Z+ 2 2a+F+282\+_+2E;>0g7m0717S0K079d0U9069\0O09@Z=M4ZEO-Q-49-Q4Z- 4 -F4a-48-_4\-4=E@2Q9ZJOAMEZ2999Z2Q2 9 2F9a2982_9\29AJE2Z+Q;MEO>Z2+92Z+Q+ 2 2a+F+282\+_+2;E>0QCZJO7Z@M7097Z0Q0 7 7a0F0787\0_077Z0O07:7\0Q0 77_0Z0 767U0O077Z0M07;7\0X070\7a0 720Z7Z07 0U7\0790Z7\070Z7\:70 0a7a 0@J7C2X9X092Q\VZ2ZNX9O QNV29;\2\1;22O;\;22Z9aNXVZQ\ QNV 922U9Z922_;\0;2;\2K;2NOVS2\Q_9a QNVNHVFQO9QV2NNM2K9QQMVK VQN29VMQS;_NK2U QNVVHNKQQ Q;VN2VZN\2Q;\Q_ QNV2;VZQ\2\NX9\ QNVVSNMQZ 9QNV22KNO9\VUQX QNV292ZVUQXNO;_ QVNNOQUVM;2QNVQ\;aNU2UVZ QNV; 2JO2Q9ZAMEZ2999Z2Q2 9 9a2F2989\2_299Z2O29:9\2Q2 92Z9_2 969U2O29 AEJ2SCj+dGaGCGMCO 2+CGGH2XCQ+dCG +2 AZ4\EU+gEAEHAF 4A+EAF+_4_EHAE+4@_2g+_CO@CCK@M2 +@C+a@H2ZCK@C+2+mA_4_>\A>>KAO4A+>A\4m+u>ZA> 4+0g7m0710K7S070U9d0969\0O09EOM@2G1+<GCZJO7Z0Q@M7097Z0Q0 7 7a0F0787\0_077Z0O07:0Q7\0 77_0Z0 760O7U070M7Z07;7\0X077a0\0 727Z0Z07 0U7\0797\0Z070Z7\:70 0a7a 07@CJ9X2X092NXVZQ\9O2Z QNV29;\2\1;2;\2O;29aVZQ\NX2Z QNV 922U9Z922_;\0;2;\2K2;9a2\Q_VSNO QNVQOVFNH9Q2NVNMVKQM9Q2K QVN92QSNK;_2UVM QNVQQNKVH ;QVN2N\2Q;\VZQ_ QNV2;NXVZ9\Q\2\ QNVVSQZNM 9QN2VQX9\2KVUNO QNV92VUNO2ZQX;_ QVNNOVMQU;Q2NVQ\NUVZ;a2U QNV; 22QJO9ZEZAM9292Q9Z2 9 9a2F2989\2_299Z2O29:2Q9\2 92Z9_2 962O9U29 JEA+Q2Z>ZCO;M+29+Q2Z+ 2 +F2a+28+_2\+22Z+O+2:2\+Q+ 22_+Z+ 26+O2U+2 C;>+QCZ7Z@MHO7+97Z+Q+ 7 +F7a+787\+_+7CH@4Z-QEO@ZM@G12+G<_5X4>5>_5a5>M\5_H\E__5_1>55aM0>_5>M QXTO<_5aMQMQTTFMKQK <5QTMT>\V\RMVVDRFM< >5RMVVHRH>\MH5dR5VM>TXQX5_<\MXQMT5QHT8<M:QMTT6<\M<Q>5d Q <M5TV_M\5_>SR_RMVVHMMRH>5RMV>p5pVKRKMHRMV>5 9U92HM2XEFJU;Z%JE ;2H\C>2\;ZH;C29ZE@JU2U8292a9\EJ92;U2\4;2;d2a;22\9\7292_9_922a;_22;2_;g;2 9ZAMEZJO2Q9292Q9Z2 9 2F9a2982_9\299Z2O29:9\2Q2 92Z9_2 969U2O29 JEA;M>Z2ZCO+Q2+92Z+Q+ 2 2a+F+282\+_+22Z+O+2:+Q2\+ 22_+Z+ 26+O2U+2 ;>CJO@M7ZCZ0Q7090Q7Z0 7 0F7a0787\0_070O7Z07:0Q7\0 77_0Z0 767U0O077Z0M07;0X7\070\7a0 727Z0Z07 0U7\0797\0Z070Z7\:70 0a7a 0@JC72X9X0922ZQ\9OVZNX QNV292\;\1;2;\2O;2Q\2Z9aNXVZ QNV 922U9Z922_;\0;2;\2K;2VSNOQ_2\9a QNVVFNHQO9QVN2QM2KNM9QVK VQN29QS2UVMNK;_ QNVNKVHQQ Q;VN2;\Q_N\VZ2Q QNV2;Q\2\9\VZNX QVNVSQZNM Q9N2VQXVUNO2K9\ QNV92NO2ZQXVU;_ QVNNOQUVM;Q2VN2UQ\;aNUVZ QNV; 29ZAMEZ2QJO9292Q9Z2 9 9a2F2989\2_292O9Z29:2Q9\2 99_2Z2 962O9U29 JEA2d+\)2+%+_2O+2 +a4_-4++a4a+42\+ZAUFQ,2FA+>K2\+_CU+24_+\04++a4\+4 C>0g7m0710K7S070U9d9069\0O09@Z-Q4ZM@2G1+G<7a0d2700_@Z7ZHUC\C07@H 9_0X4909_0a900_H\C\@_7ZC@H70#0\7X700_9_1909_H00a09H 0a7_HQLXOOHLOLKOFHK 70LOHO>H<LH0_7X7OH0L9\Q\M_H\0_MHQQDH<MF 90MHQQH0dHH9\MHMQ90H7\OX0_HXLXLHO0O8H:7LHLHOO60d7\L>H< L HO70H\9SQ_M_0_MHQQHHMMH90MHQMKQK0p9pHHMHQ90 9_2\/92 2Z9Z29 J\;_EZ2Z.;EJ22\;\JOEK;J2E 2XMZHK9_)MH922Z9UM:H>92HM2X;ZEFJU%JE ;2H\;Z2\C>HC2;JUE@2U9Z8929\2aJE922\;U4;2;d2a;29\2\7929_2_92;_2a2;22_;g;2 9ZAMEZ2QJO2999Z2Q2 9 2F9a2982_9\299Z2O29:2Q9\2 92Z9_2 962O9U29 AJE2Z;M>Z+QCO+292Z+Q+ 2 2a+F+28+_2\+22Z+O+2:2\+Q+ 2+Z2_+ 26+O2U+2 >C;@MCZHO7Z0Q7090Q7Z0 7 0F7a0780_7\07CH@-QM@G12+G<0d7a2077Z@Z0_C\HUC07@H 9_0X4909_0a907ZC\@_H\0_C@H70#0\7X700_9_1900a9_H009H 7_HQLX0aOOHLOLKOFHK 07LHOO>H<LH0_7X7OH0L0_9\H\M_Q\MHQQDMFH< 90MHQHHMHQH0d9\0MQ9H7\0_LXOXHXLHO0H:O87LHLHO7\H<O60dL> L 7O0H0_M_9SH\Q_MHQQHHMMH90MHQQK0p9pHHMKMHQ90 JO2Q9ZEZBM2992Q9Z2 9 2F9a2982_9\299Z2O29:2Q9\2 99_2Z2 962O9U299Z2M29;9\2X299a2\2 929Z2Z29 9\2U2992Z9\292Z9\:92 2a9a 2J9EBJO2QAMEZ9Z9299Z2Q2 9 2F9a2989\2_299Z2O29:2Q9\2 92Z9_2 962O9U29 EJA;MCO+Q>Z2Z+292Z+Q+ 2 +F2a+282\+_+22Z+O+2:2\+Q+ 2+Z2_+ 262U+O+2 ;C>7ZHO@M0QCZ0790Q7Z0 7 700F9a7 0989\0_09@CHEO-Q4Z@ZZ2+92Z+Q+ 2 2a+F+282\+_+2>;C5d_5X4>5>_5a>55_H\_1>55a>_M05>M <_TO5aMQQXMQTQKTFMK 5<QMTQH5_M<<QM5T>\R_M\5_V\RMVM<VDRF >5RMVVHMHRH>\5dRV5>MMX5_<\QXTXQMT5QHM:<T8QMT5dQ>M<<\T6 Q T<5M>S5_M\V_R_RMVMMRHVH>5RMVRKVK>p5pMHRMV>5 OO5Q2Z92HMJU2X;ZEF%JE ;2H\;Z2\C>H2;CJUE@2U9Z8299\2aJE922\;U4;2;d2a;29\2\7292_9_922a;_2;22_;g;2 AMEZJO2Q9Z2999Z2Q2 9 2F9a2989\2_299Z2O29:2Q9\2 99_2Z2 962O9U29 EJA;M>ZCO2Z+Q+292Z+Q+ 2 +F2a+282\+_+2+O2Z+2:2\+Q+ 2+Z2_+ 26+O2U+2 >;C0d7a2077Z@ZHUC\0_C07@H 0X9_4909_0a90@_0_C\H\7ZC@H70#0\7X700_9_1909_H00a09H OOLX7_HQ0aHLOLKHKOF 07LOH7XO>LHH<0_7LO0H9\Q\0_M_H\MHQQDMFH< 90MHQQH0d9\HHMHH0QM90_HXLXOX7\LHO0O8H:7LHLHO0d7\L>O6H< L 0H7OH\9SQ_0_M_MHQQHMHHM90MQHMK9p0pHHQKMHQ90 9X2X092Q\2ZNXVZ9O QNV29;\2\1;2;\2O;2NXVZ9aQ\2Z QNV 929Z2U922_;\0;2;\2K;2Q_VS2\NO9a QNVNHQOVF9Q2NVQM2K9QVKNM QVN29NKQS;_2UVM QNVNKQQVH Q;VN2N\;\2QQ_VZ QNV2;9\VZQ\2\NX QVNQZNMVS 9QN2V2KVUNO9\QX QNV922ZNO;_VUQX QVNNOQUVM;Q2VN;a2UNUQ\VZ QNV; 2AMJO2Q9ZEZ9299Z2Q2 9 2F9a2982_9\292O9Z29:2Q9\2 92Z9_2 969U2O29 AJE>Z2Z+Q;MCO2+9+Q2Z+ 2 +F2a+28+_2\+2+O2Z+2:2\+Q+ 2+Z2_+ 26+O2U+2 >C;+Q7ZCZHO@M+797Z+Q+ 7 7a+F+78+_7\+7@HC-Q4ZZ;MCO2Z+29+Q2Z+ 2 +F2a+28+_2\+2>;CJO7Z@MCZ0Q7097Z0Q0 7 7a0F0787\0_070O7Z07:7\0Q0 77_0Z0 767U0O070M7Z07;0X7\077a0\0 727Z0Z07 0U7\0790Z7\070Z7\:70 0a7a 0C@J7RB\J\E_,EBJyB\J\E_%EJBDB\JZE_EBJ!BZEdJ_!EBJTVpK2\+24_+\04+4\+a+4 >C0QHO@M7ZCZ0797Z0Q0 7 0F7a0787\0_07HC@EOZ2Z2+9+Q2Z+ 2 2a+F+282\+_+2;C>@M0QCZJO7Z7090Q7Z0 7 7a0F0787\0_077Z0O07:7\0Q0 77_0Z0 767U0O070M7Z07;0X7\077a0\0 727Z0Z07 0U7\0790Z7\070Z7\:70 0a7a 0C7J@RJ\B\E_,EJByB\E_J\%EJBDJZB\E_EBJ!BZJ_Ed!EBJTVp\4_A_A>>KAO4+A>>ZA\4m+uA> 4+@M7Z+QCZHO+797Z+Q+ 7 +F7a+78+_7\+7H@CEO@ZM@G12+G<5d_4>5>_5a>55__1>55aM0>_5>M TO<_MQ5aQXQMTQKTFMK 5<QMTM<5_QHT>\V\5_RMVRFVDM< >5RMV5d>\VHMHRH5MV>R5_TX<\QXMXQMT5M:T8<QHQMTM<5dT6Q><\ Q <5TMR_5_M\V_>SRMVVHMMRH>5RMV5pMHVKRK>pRMV>5 _5X4>5>_5a>5M\E__1>5M05a>_5>M 5aQXTO<_MQMQTQKMKTF 5<QTMM<5_T>\5_V\M\RMVRFVDM< >5RMVMHRH5d>\VH>RMV5MXTX5_QX<\QMT<T8M:5QHQMTQ><\T65dM< Q <5MTV_>S5_R_M\RMVRHVHMM>5RVM5pRKMH>pVKRMV>5 9X2X0929OVZ2ZQ\NX QNV29;\2\1;22O;\;2NX2ZVZ9aQ\ QNV 929Z2U922_;\0;2;\2K;2NO2\9aQ_VS QNVVFNHQO9QN2VNMVK9Q2KQM QVN29VM2UNKQS;_ QNVQQNKVH ;QVN2Q_VZ2QN\;\ QNV2;2\VZNX9\Q\ QNVQZVSNM 9QNV2VUQX2K9\NO QNV92VUNO;_2ZQX QVNVMQUNO;2QVNVZ2UQ\;aNU QNV; 2JO9Z2QAMEZ9292Q9Z2 9 9a2F2989\2_299Z2O29:9\2Q2 99_2Z2 969U2O29 JAE2ZCO;M+Q>Z+292Z+Q+ 2 +F2a+282\+_+2+O2Z+2:2\+Q+ 22_+Z+ 262U+O+2 C>;JO0Q7Z@MCZ0790Q7Z0 7 0F7a0787\0_070O7Z07:0Q7\0 70Z7_0 760O7U070M7Z07;7\0X070\7a0 720Z7Z07 0U7\0797\0Z070Z7\:70 0a7a 0C7@JRJ\B\E_,EJByB\E_J\%EJBDJZB\E_EBJ!BZEdJ_!EBJTVp\A_4_A>AO>K4A+>+u4m>ZA\A> 4+0g7m0717S0K079d0U0960O9\094ZM@2G1+G<JXCa@\ @JC,CU@KJO@CJAZJXE\"EAJ,AHJHEMAJECa@\JZ*C@J%@HCOJM@CJJZE_AU"EJA*JXASE_JAE@ZJZCa|C@JHO_VXL\iOLV+@/MTrkuJAZ2@[(Guitar{ ^U]0I7I!077*7 0I9I#099/9 -:7I!-7R7I-:#7-W2I9I!299*9 2I;I#2;9/9 2I+I!+22*2 4I+I#+42/2 7I0I!077*7 9I0I#099/9 7I-:!-77*77I-:#-77/7 9I2I!299*9 2I;I#2;9/9 +:5I!+55*55I+:#+55/5 7I0>!077*77I0>#077/77I0>%707*77I0>!077'77I0>%077/!70>7I"707870>7I%707, 77I0>2077"7-:6I!-6R6I-:#-6T6I-:%6-S6I-:!-6Q6I-:$6-T-:6I!-6S-:6I%6-S6I-:2-6D9I2I!299*9 ;I2I#2;9/9 2I!077*77I0>#077/70>7I%707*77I0>!077'77I0>%077/!70>7I"707877I0>%707, 77I0>2077"72>9I!299*99I2>#299/92>9I%929*99I2>!299'99I2>%299/!92>9I"929892>9I%929, 99I2>2299"92I9I!299*9 ;I2I#2;9/9 2IP5P#5>>6> 5PP5P!5>>.>P">5>?>P5P25>>)>5PP5P#5>>6> 5PP5P!5>>.>P">5>?>5PP5P25>>)>2D9P!29R9P2D#92T9P2D%92S9P2D!29Q9P2D$92T2D9P!29S2D9P%92S9P2D229D2P9P!29919 2P;P#2;969 2P6I!266*66I2>#266/66I2>%626*66I2>!266'66I2>%266/!62>6I"626862>6I%626, 66I2>2266"62I9I!299*9 2I;I#2;9/9 2I!29R9I2>#29W5I+:!+5R5I+:#5+W7P0P!07717 0P9P#09969 0P7P@077179P0P!099.97P0P%0776!70P9P"909?90P7P%7073 79P0P2099)99P2P!29919 2P;P#2;969 2P!07R7I0>#07T0>7I%70S7I0>!07Q7I0>$70T0>7I!07S7I0>%70S7I0>207D2>6I!26R6I2>#26T6I2>%62S6I2>!26Q6I2>$62T2>6I!26S2>6I%62S6I2>226D2I9I!299*9 ;I2I#2;9/9 2I!29R9I2>#29W5I+:!+5R5I+:#5+W7I0I!077*7 0I9I#099/9 0I7I@707*79I0I!099'97I0I%077/!70I9I"909890I7I%707, 79I0I2099"92I9I!299*9 2I;I#2;9/9 2I!299*99I2>#299/92>9I%929*99I2>!299'9 5I+:!+5R5I+:#5+T5I+:%5+S5I+:!+5U7I0I!077*7 0I9I#099/9 -:7I!-7R7I-:#-7W9I2I!299*9 ;I2I#2;9/9 +I2I!+22*2 4I+I#+42/2 P5P#5>>6> 5PP5P!5>>.>P">5>?>5PP5P25>>)>5D9P!59R9P5D#59T9P5D%95S9P5D!59Q9P5D$95T5D9P!59S5D9P%95S9P5D259D2P9P!29919 ;P2P#2;969 2P!26R6I2>#26T6I2>%62S6I2>!26Q6I2>$62T2>6I!26S6I2>%62S6I2>226D9I2I!299*9 ;I2I#2;9/9 2Id;d9d2d+d0Bd=d9d4d/dp/=;9B2>9B+4pBd>d9d6d/dp/9B>6@d>d;d4d2d+d [] v+>;@24Bd>d;d9d2d+d +>;B29Bd=d9d4d/d`/9B=4+d2d4d;d>d@d@24+>;Bd>d;d9d2d+d0Bd=d9d4d/dp/=;9B2>9B+4pBd>d9d6d/dp/9B>6@d>d;d4d2d+d+>;@24Bd>d;d9d2d+d +>;B29Bd=d9d4d/d`/9B=4+d2d4d;d>d@d@24+>;-d4d7dd;d9d2d+d0Bd=d9d4d/dp/=;9B2>9B+4pBd>d9d6d/dp/9B>6@d>d;d4d2d+d+>;@24Bd>d;d9d2d+d +>;B29Bd=d9d4d/d`/9B=4+d2d4d;d>d@d@24+>;Bd>d;d9d2d+d0Bd=d9d4d/dp/=;9B2>9B+4pBd>d9d6d/dp/9B>6@d>d;d4d2d+d+>;@24Bd>d;d9d2d+d +>;B29Bd=d9d4d/d`/9B=4+d2d4d;d>d@d@24+>;-d4d7dd`>2+d7d@7+2d>d@>2-d9d@9-2d>d`>24d@d @4Cd7d@7C-d9d@9-0dd`>2+d7d@7+2d>d@>2-d9d@9-4d@d@@47dCd`C7 !d9d-d -9!`!d!QdEd9d9EQOdCd7d7COLd@d4d4@LQd9dEd@E9QTdd`>2+d7d@7+2d>d@>2-d9d@9-2d>d`>24d@d @4Cd7d@7C-d9d@9-0dd`>2+d7d@7+2d>d@>2-d9d@9-4d@d@@47dCd`C7 !d9d-d -9!`!d!QdEd9d9EQOdCd7d7COLd@d4d4@LQd9dEd@E9QTdd;d9d2d+d +>;B29j[] bvBd>d;d9d2d+d@+>;B29@4d-d7ddCdGdLdPL9>-GC04d-d9d>dCdGdLdLC-94G>-d9d@dCdHdLdPL9@-HC0@dd;d9d2d+d +>;B29`Bd>d;d9d2d+d@+>;B29@4d-d7ddCdGdLdPL9>-GC04d-d9d>dCdGdLdLC-94G>-d9d@dCdHdLdPL9@-HC0@ddCdGdLdPLC9>G09d>dCdGdLdPLC9>G09d>dCdGdLdPLC9>G`NdIdXdSdpSNIXpZdJdNdSdpSZJNSdZdJdLdpLSZJpSdNdZdJdxJSNZXNdIdXdSdpSNIXpZdJdNdSdpSZJNSdZdJdLdpLSZJpSdNdZdJdxJSNZhBdUdQdLdGddCdGdLdPLC9>G09d>dCdGdLdPLC9>G09d>dCdGdLdPLC9>G/MTrk&[SintQZ e6Z e[]T[] eKv9d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;Ct9d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;Ct9d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;Ct9d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;Ct9d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;Ct9d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C$7d;d>dCdJd7;C49d@dCdHdLddCdJd7;C47d;d>dCdJd7;C<7d;d>dCdJd7;C,7d;d>dCdJd7;C/MTrkISolo 010n m6n m[]NdBdpBNLd@dP9dEdxE9x@LNdBd,LdBN@d,L@>dJdv[] mn6>DdId=dJEdJdD=I>d,EJ>@dGdLd,@@dBd9dEdLG,9BE@Ed9d 9E;dGdxId;=dG =IJd>d >J@dLdxId@=dLxI=BdEdJd>d@>BEJ9dEdxE9NdBdxBNJd>dx>JLd@dx@LJd>dx>JId=dx=IBd6dx6B@d4dx4@6d;d>d >;6`9d=d@d4d@49=@;d>dBd6d@6;>B>d@dEd9dP9>@EpEdJd>dP>JEBdId=dx=IB@dGd;dx;G@;d>dBdEd9dP9B;>E0Ed9d-d-9ECd+d7d7+C@d(d4d4(@TdHd0dd&d2d<2J>&dx>VJ2xTdHd0ddx>VJ2xXdLd4d@dp]dQd9dEdx@4XLh]EQ9[dOd7dCd,XdLd4dC7[O@d,4LX@dx>VJ2xXdLd4d@dx@XL4x[dOd7dCdxC[O7x]dQd9dEdxE]Q9xXdLd4d@dx@XL4x[dOd7dCdxC[O7x]dQd9dEdxE]Q9xXd@dLdxL@XxTddx>2JHd0dd&d2dx2J>&Ld@d(d4dx4L@(OdCd+d7dx7OC+Ld@d(d4dx4L@(OdCd+d7dx7OC+QdEd-d9dx9QE-OdCd+d7dx7OC+QdEd-d9dx9QE-SdGd/d;dx;SG/QdEd-d9dx9QE-SdGd/d;dx;SG/TdHd0ddx>VJ2XdLd4d@dx@XL4[dOd7dCdxC[O7NdBdpBNLd@dP9dEdxE9x@LNdBd,LdBN@d,L@>dJd,>DdId=dJEdJdD=I>d,EJ>@dGdLd,@@dBd9dEdLG,9BE@Ed9d 9E;dGdxId;=dG =IJd>d >J@dLdxId@=dLxI=BdEdJd>d@>BEJ9dEdxE9NdBdxBNJd>dx>JLd@dx@LJd>dx>JId=dx=IBd6dx6B@d4dx4@6d;d>d >;6`9d=d@d4d@49=@;d>dBd6d@6;>B>d@dEd9dP9>@EpEdJd>dP>JEBdId=dx=IB@dGd;dx;G@;d>dBdEd9dP9B;>E0Ed9d-d-9ECd+d7d7+C@d(d4d4(@TdHd0dd&d2d<2J>&dx>VJ2xTdHd0ddx>VJ2xXdLd4d@dp]dQd9dEdx@4XLh]EQ9[dOd7dCd,XdLd4dC7[O@d,4LX@dx>VJ2xXdLd4d@dx@XL4x[dOd7dCdxC[O7x]dQd9dEdxE]Q9xXdLd4d@dx@XL4x[dOd7dCdxC[O7x]dQd9dEdxE]Q9xXd@dLdxL@XxTddx>2JHd0dd&d2dx2J>&Ld@d(d4dx4L@(OdCd+d7dx7OC+Ld@d(d4dx4L@(OdCd+d7dx7OC+QdEd-d9dx9QE-OdCd+d7dx7OC+QdEd-d9dx9QE-SdGd/d;dx;SG/QdEd-d9dx9QE-SdGd/d;dx;SG/TdHd0ddx>VJ2XdLd4d@dx@XL4[dOd7dCdxC[O7/MTrkIBateria %0`%%`%%0`%%`%1o$Ft*py***A1/*B$7*p$n * *e*pw*,**n*pF$1*p$e*d*p*c**j**=$0$~*o**n*x* **a**?$1*p$p *m*px***l*+$M*p$x**g*.y*! **f*#4$D*p$`* *f* *o* *q*6$A*p$l ** @[]d*z***o*E$4*p*$F *l*v*% **h**$M*j$t**b*y*** *f*p-$L*p$e* *e*pw* **l**@$0$~**pv*y***q*p*>$/&p*p$p*m*&Oy*** *&V&* *&P9$5*h$x*q*x* * *&J* *2_:$.*o$`*2z2U*p2o*< **k*2*/&$I*p$l /{/;2* *c1v**&O*n*<$/4*p$F* *31.*-&M* **_* *'$G*k$t&t6q*n*x*-*a*%&**&o68$6*i$e*m*y***&M*! *1$:$|*p*s*x**d* * * $@*k$p*o*x*% **e* *:$4*p6s$x&x*m*!w* *a**&&p *46$-*d$`v*&?*&*j*p *l*#0$G*o$l&l*&6l*m*x****n* *#&$6*j$C*p* y*!*b*# * *6$1*m$t&t6qq**j*(*e*1&*&o*8$ 6)*p$d*n*w* **"&M*2*@$-$|*m *n*'x*#* *g*<>$:*p$p**a*#x* *k*&* *:$0*p$x6r&xu*!*j*? * *d*5&&p*'$6-*p$`*q*&?&n*5*u*)* *2$4*n$l&l&6to* *c*3* *e*9*!&$-6*p$F*p*0x***d**9I$.*p$t&t6rs*!*r*% **f*)&&o*0$6%*p$e*k*/ *p*.*$&L*3$B$~*k**l*t*2*f*3**$F*p$p*o*7w*-**n*/G$0*p$w&x6q *e*'**d*-*q*&&p*@$-*k$`6o*0&?&*a*<* *e*: *$P*p$l&l&6lq** *j*y***&!$6 *p$F*r*5x*2 **i*0D$4*p$t&t6t * *c*9t* **b*&*&o7$68*p$ev*2**b*3,&L****$@$|*p *p*'w** *o*!G$1*p$p**n*x* **n*&* $R*p$x6p&xq**x* **g*&&p*6$67*p$` *m*#&?&o* **d*! *&&@0$@*a$l6p/yp* *h1b*&&O*m*8*($/6*p$F**710*+&K* **g*> *C$**W$t&t6kq*&*d***d*5&&o*!$6M*p$es* *d*6**"&L*8*>$0$~*px*& **h**q*( * $L*p$pw*( *j***q*A *:$6*p$x&x6n*m*x*&**m*&*&p5$[] @46*p$`*k*&?&o***a**/D$2*j$l&l&6sq* *m* **f*7*%&$ 6)*j$Fu*,*q*2 **f*0 *$W*p$t&t6qq**p*** *b*7&*&o6$G*p$es*"*i*,**!&L*1 *?$,$~*p*t*x*&**l**F$1*p$pr***p* *p**<$3*p$x&x6qq* *d** *c*#&&p*@$-*p$` 6i*5&?&*`* **m**0$A*p$l&l&6n*o*/w* * *e*# *&$&6*p$F*q*y*** x*& *B$-*p$t&t6kq* *o* **k*&&o*=$1*p$d6g*'*b***&M**7$8$|*gx*$ *m*#**k* *;$2*p$pw**u* **l*,9$>*p$x&x6s *d*:**n*!x**&&p*6$2*p$`v*6&?&*h* **e*+ *<$3*p$l&l&6lp*)* *g**n* *&"$6*k$Fu*0*u***n*+*I$&*p$t&t6nr* **l**q*&&&o*76$4*p$ev**b*( **&L*/6$?$~*p**p*x*$**d*&Z*8$3&p*p&$ps&&O*%*g* * *j&**&P4$<*p$x&x6n&l&;*.&*d* * *b* *2x 60$-*p$` 2q&X**e2*7*2v *2\** */$@*p$l6o2%2F* *i1~**-*&On**&6$4*p68$F2>1/* * &M*66* *d*, *:6 $)*p$t&t6ps*1*v*+66* *(6?*3&*&o%$J*l$e6%$6Q*8*c*p6 *6&L** *-$!6$~&~*m6pu*,*p60*+6**l*0!&%$2*p*$p67*6H*)t6*=6$ * *a**9$68*p$x&x6p*t**p*66; * 6`*+&*&p*$D*p6*$`96=*-&?&*E6$*2*6@*c**C$/*p$l&l6p&6d* * *H6*6**m*** &$6 *j60$Ft*$*a*06# **6V* *0$%6*p$t&t6pu**e6*6*6) *h*'&&o:6$1*o$e68**j*U6$*1*6-*&M**H$ 6$~&~*p6pu*!**l*$66&*q* * &$$0*p$p6%6b**g*6,**'6I* *6$9*k$x6p&x96<**_*=6.*6 *h*+&*&p7$6.*p$`6!v*&?&*;60*'64**j**=6 $)*p$l&l6p&s*3* *M6*0*6Ev*; *&$8*p$E6+6f*3*_6*6 **i*: *7$7*p$t&t6p*6S*\6*6D* *a*&*&oC$6*l6+$eu**s* 6* *6&M*H*9$ 6+$~&~*p6p*q*0i6*$6<**a*#*&$P*p$m6-6V**q6!*2**6P*3 *$!6,*p$x6p&xu* **g*56B*.6@*!&&p*A$,*o$^6*6Y*&?&* *B6!**6>u*$^*p$j6p&l&* 6Y*#*169*=66* *a* &&$06*p$F* * 6T*( *p*6"*#6L*(*J$+*p$r&t6p:6;*( *665*69*s*(&&o* *8$ 6&*p$b6*o*+}*-6 * *&6E*3F$6$}&~*p6p **h*9M6(&&\*6 **i*4*.p;$6"*p$p65v*-*&-.%*6*&K* *6F*+ *4$ 6*p$n&x6p&p&;**'&J6+**61* *c&* &p?6$**p$`6& 2*k*-*2s *2=6*6#*#&J**5 2{3$28*p$j6p262;* *F61~**'6 &O*a* K$.*p$F**2;1,*-*6&L*y*2**=$2*j$q *n*7x* **n*+*:$>*n$bs*:*f***j*E *@$*${*p *o*u*3 **o*A5$C*p$n**d*Cx***p*$*6$6*p$u*r*%x****e*4*E$-*p$\v*! *n*3**l*) *C$,*p$i*q*"y***a*# *&$G*p$Ct**b***r**@$1*p$ox*-*b*/ **l**+J$,*p$ev*4*b*6* *m*2*H$($~*pv*2*c*E* *f*+ *9$7*p$ow*0 * *f*u**; *8$5*p$ww* **a**u* **$A*p$_v*$**h*"*p*1$E1~*p$l&l*g*#**n**C10*#* &$66*k$F;69* 6 * *F6*/6*h6*6 *@$66p*p$t&tu6*5* *a6* *6 6E*1&6 *&o36 $6*p$b6/t*6 **:66 *0*6&+&)6$*?6*=$2$~&~*p6pF6-*$6 * *+66*96*o*36*&$666*p$nv*!6*366*)6*o**6 *5$3*p$w&x6p 6A66**P66*6**p*&6*&p$W*p6$`6 6j*=6 &?&*d6*6(* *%6<*?6 *1$F*p$k&l66p&*6a*;6 m6 *+6 6 *d*/6* *&$66*p66$Fu*06 **f6*+ *606:**+6 H$,*p$t6&t6p6_*96**I6"66*&*S6**5&6&o@$/*p6$e56A*76 *666*p6* *&6 6%*D*6?$2$~&~*p6p!6T*6 *5686*6**b*?*6 &!$66 *p$p6t6 *' **g*06*66c*6 8$ 66*p$u6p&x**f*86*F6 6 *6v*.6 &&p**$6$6&*p$]6 u*76&?&*m*6 ** 6'6;6*+$ 6+*p$l&l6p& *6`*66**@6-66**u*&6#& $F*p$F66*6X**;6t*6& * *66@*86*D$&*p$q&t6p6 6W*"6*X6 6 * 6&**n*!&6&o*A$,*p$d666O*96 6*B6 *6$* *&M*#6*3$6,$~6p&~*p-6K**6*Q6*66 * *g*36&$P*p$p68*66D*96 *U6*6**6^*"6*B$,6p*p$x6&x,6I*6*]6 *6$* *6R*&6*&p9$666*o$^O6'*6&?&* *c*6*76<*56C$6 *p$l&l6p&*6Z*26 *36+6 *6**k6*)'&$@*k6$F** 666:*%6 e6*6* *d**!6B$3*j$t&t6p6"6B*6 *g* 66* *6\* &6&o*3$:*p$`6*66J*76*C6/*'6*6*&K*=62$A$~&~*n6p**66a*(6 D64*6* *!6B*&6*&"$6*p$p6*%6O6*7&I* *g*6&K!6 6H*;&6&&P**2$6*p$x6p46B*26*2t *`* 6*2e66 &27*A*6 62l.2 $3*p$`6s*6/b* 6 6656***/x*:/2*866$7*l*$k6p*/z>/3*966g61~*p6*&O*6Q*,6*@6$6*p6$E /;10*6** &D6 * 66 *m*36 *>$.*k$t&t6p66M**6k6**"6 *6Z*5&6 &o;$ 6 6*p6 $b **g*26=665*6**'&K*6 *4$6${&~*p6p 6&6B*46 *e6*(6 * *6N6*#*6&$$(6 *p$p6*m* 6)6K*/6 **6J*)6 *6<$%6*p$t&x6pt*!6*e*"6 **66=6*"&&p&$66*p6$[*+6C*6 &?& *!66&* 6* *062*'6 H$,6p*p$i&l&*36/*'6*c*46* * 66A&Q*%&6*#$$6#6=*p$@.v*p*6 ,.666* 6 * &V*36* *4$&6*n$n6&t6pt6*6 *\66*6 * *c*0&6 &o*4$6"*p$`6+96<*6*b*#6)*6*&G6*-6 *76 $($x6&~*p6p*t*6 `6*p6 *(66&* 6 **&$$3*p$m6*/6D*6q6*6**6l*6*@$6**p$x&x6p 6j**6 <606**6 *m*!6&*6&p<$3*k$`6*a66* 6 &?*&*e*6*6a*D6*+$E6*j$e&l6p&56?*;6 *069*"6*6*l*$6 *&6$-*o$F6*16=*&626B* *6*%6I*'6 *666$4*p$t&t6p*p*6A62*6**6O*&&*6&o=6$6*p$d60t*;6*t*%6* *6& 6B*%6*,$A$~&~*p6p6m6*C6* *L66 * *6Q*66*$ &'6(6*p$p6'p**36*%6?*6*6X*-6*?$0*p$t6p&x66V*6 *_66*6**]*5&6 &p*+$B*p$`6/66&6**6 &?&n*"*6.*6#6-*46*9$7*p$k&l6p&6^*#6 **D6+*6 *#6N***6#&$$+6-*p$F6 6[6 *7**266*p6-U6 **6 * *=$**p$t&t6p6W6*#* *T66*p6t6*?&&o**:6$.*l6"$e6]6*-6*'69* 6* *&M*6 ?$!6$~&~*p66p*f*66 * *`6 *"6$ *6e6 * *&%$6 6*p$l6%o*&O*7 *Z6*6)*m&*0*6&P1$?*p$u6p66F*6**2z>6,*7636&K* 6*2 *0$2,*p$`6>6[*-6*6/~ 26+6**60 *k*+6! */t6 $6/$*j$i6pt*5*6 /*5/'6 **6A/v6-/)*86*/v+$ /601v*p6:$F*6V*&6 06/B6*6-* *116*C6*6$;*p$t&t6p*o6*6R6 6* * *6J*4&6 *&o(6$2*p6+$e 6i*6 *]66 *6 * *&J6**,6$68$~&~*p6pv*)6 *+67*6**6X*6 *&$'6*l$p6,&6O*6*.66-*(6"* *b*6*&$>6 *p$r6p&x*6g6 *x*+6% **66P*6 &&p*<$6-*l$_6%,6J*)6&?&*6T*6(* *f*26*6$'6-*j$l&l6p&*/6=*46h6 *.6(**26;*6*!&$2666W*p$Ft6 *2*D60*6* *"6?**/6G$6 6 *m$q&t6pv*56*+6868*+**6B*&6&o*:$4*p*6$$b6"6+6*;6 r*16/6**&M*#6L$ 6$~&~*p6p **!6E*,6 *Y6&*6'*-6C*6*1$66&p*p$pu*&O *m*** *&V&* *&P9$5*h$x*q*x* * *&J* *2_:$.*o$`*2z2U*p2o*< **k*2*/&$I*p$l /{/;2* *c1v**&O*n*<$/4*p$F* *31.*-&M* **_* *'$G*k$t&t6q*n*x*-*a*%&**&o68$6*i$e*m*y***&M*! *1$:$|*p*s*x**d* * * $@*k$p*o*x*% **e* *:$4*p6s$x&x*m*!w* *a**&&p *46$-*d$`v*&?*&*j*p *l*#0$G*o$l&l*&6l*m*x****n* *#&$6*j$C*p* y*!*b*# * *6$1*m$t&t6qq**j*(*e*1&*&o*8$ 6)*p$d*n*w* **"&M*2*@$-$|*m *n*'x*#* *g*<>$:*p$p**a*#x* *k*&* *:$0*p$x6r&xu*!*j*? * *d*5&&p*'$6-*p$`*q*&?&n*5*u*)* *2$4*n$l&l&6to* *c*3* *e*9*!&$-6*p$F*p*0x***d**9I$.*p$t&t6rs*!*r*% **f*)&&o*0$6%*p$e*k*/ *p*.*$&L*3$B$~*k**l*t*2*f*3**$F*p$p*o*7w*-**n*/G$0*p$w&x6q *e*'**d*-*q*&&p*@$-*k$`6o*0&?&*a*<* *e*: *$P*p$l&l&6lq** *j*y***&!$6 *p$F*r*5x*2 **i*0D$4*p$t&t6t * *c*9t* **b*&*&o7$68*p$ev*2**b*3,&L****$@$|*p *p*'w** *o*!G$1*p$p**n*x* **n*&* $R*p$x6p&xq**x* **g*&&p*6$67*p$` *m*#&?&o* **d*! *&&@0$@*a$l6p/yp* *h1b*&&O*m*8*($/6*p$F**710*+&K* **g*> *C$**W$t&t6kq*&*d***d*5&&o*!$6M*p$es* *d*6**"&L*8*>$0$~*px*& **h**q*( * $L*p$pw*( *j***q*A *:$6*p$x&x6n*m*x*&**m*&*&p5$46*p$`*k*&?&o***a**/D$2*j$l&l&6sq* *m* **f*7*%&$ 6)*j$Fu*,*q*2 **f*0 *$W*p$t&t6qq**p*** *b*7&*&o6$G*p$es*"*i*,**!&L*1 *?$,$~*p*t*x*&**l**F$1*p$pr***p* *p**<$3*p$x&x6qq* *d** *c*#&&p*@$-*p$` 6i*5&?&*`* **m**0$A*p$l&l&6n*o*/w* * *e*# *&$&6*p$F*q*y*** x*& *B$-*p$t&t6kq* *o* **k*&&o*=$1*p$d6g*'*b***&M**7$8$|*gx*$ *m*#**k* *;$2*p$pw**u* **l*,9$>*p$x&x6s *d*:**n*!x**&&p*6$2*p$`v*6&?&*h* **e*+ *<$3*p$l&l&6lp*)* *g**n* *&"$6*k$Fu*0*u***n*+*I$&*p$t&t6nr* **l**q*&&&o*76$4*p$ev**b*( **&L*/6$?$~*p**p*x*$**d*&Z*8$3&p*p&$ps&&O*%*g* * *j&**&P4$<*p$x&x6n&l&;*.&*d* * *b* *2x 60$-*p$` 2q&X**e2*7*2v *2\** */$@*p$l6o2%2F* *i1~**-*&On**&6$4*p68$F2>1/* * &M*66* *d*, *:6 $)*p$t&t6ps*1*v*+66* *(6?*3&*&o%$J*l$e6%$6Q*8*c*p6 *6&L** *-$!6$~&~*m6pu*,*p60*+6**l*0!&%$2*p*$p67*6H*)t6*=6$ * *a**9$68*p$x&x6p*t**p*66; * 6`*+&*&p*$D*p6*$`96=*-&?&*E6$*2*6@*c**C$/*p$l&l6p&6d* * *H6*6**m*** &$6 *j60$Ft*$*a*06# **6V* *0$%6*p$t&t6pu**e6*6*6) *h*'&&o:6$1*o$e68**j*U6$*1*6-*&M**H$ 6$~&~*p6pu*!**l*$66&*q* * &$$0*p$p6%6b**g*6,**'6I* *6$9*k$x6p&x96<**_*=6.*6 *h*+&*&p7$6.*p$`6!v*&?&*;60*'64**j**=6 $)*p$l&l6p&s*3* *M6*0*6Ev*; *&$8*p$E6+6f*3*_6*6 **i*: *7$7*p$t&t6p*6S*\6*6D* *a*&*&oC$6*l6+$eu**s* 6* *6&M*H*9$ 6+$~&~*p6p*q*0i6*$6<**a*#*&$P*p$m6-6V**q6!*2**6P*3 *$!6,*p$x6p&xu* **g*56B*.6@*!&&p*A$,*o$^6*6Y*&?&* *B6!**6>u*$^*p$j6p&l&* 6Y*#*169*=66* *a* &&$06*p$F* * 6T*( *p*6"*#6L*(*J$+*p$r&t6p:6;*( *665*69*s*(&&o* *8$ 6&*p$b6*o*+}*-6 * *&6E*3F$6$}&~*p6p **h*9M6(&&\*6 **i*4*.p;$6"*p$p65v*-*&-.%*6*&K* *6F*+ *4$ 6*p$n&x6p&p&;**'&J6+**61* *c&* &p?6$**p$`6& 2*k*-*2s *2=6*6#*#&J**5 2{3$28*p$j6p262;* *F61~**'6 &O*a* K$.*p$F**2;1,*-*6&L*y*2**=$2*j$q *n*7x* **n*+*:$>*n$bs*:*f***j*E *@$*${*p *o*u*3 **o*A5$C*p$n**d*Cx***p*$*6$6*p$u*r*%x****e*4*E$-*p$\v*! *n*3**l*) *C$,*p$i*q*"y***a*# *&$G*p$Ct**b***r**@$1*p$ox*-*b*/ **l**+J$,*p$ev*4*b*6* *m*2*H$($~*pv*2*c*E* *f*+ *9$7*p$ow*0 * *f*u**; *8$5*p$ww* **a**u* **$A*p$_v*$**h*"*p*1$E1~*p$l&l*g*#**n**C10*#* &$66*k$F;69* 6 * *F6*/6*h6*6 *@$66p*p$t&tu6*5* *a6* *6 6E*1&6 *&o36 $6*p$b6/t*6 **:66 *0*6&+&)6$*?6*=$2$~&~*p6pF6-*$6 * *+66*96*o*36*&$666*p$nv*!6*366*)6*o**6 *5$3*p$w&x6p 6A66**P66*6**p*&6*&p$W*p6$`6 6j*=6 &?&*d6*6(* *%6<*?6 *1$F*p$k&l66p&*6a*;6 m6 *+6 6 *d*/6* *&$66*p66$Fu*06 **f6*+ *606:**+6 H$,*p$t6&t6p6_*96**I6"66*&*S6**5&6&o@$/*p6$e56A*76 *666*p6* *&6 6%*D*6?$2$~&~*p6p!6T*6 *5686*6**b*?*6 &!$66 *p$p6t6 *' **g*06*66c*6 8$ 66*p$u6p&x**f*86*F6 6 *6v*.6 &&p**$6$6&*p$]6 u*76&?&*m*6 ** 6'6;6*+$ 6+*p$l&l6p& *6`*66**@6-66**u*&6#& $F*p$F66*6X**;6t*6& * *66@*86*D$&*p$q&t6p6 6W*"6*X6 6 * 6&**n*!&6&o*A$,*p$d666O*96 6*B6 *6$* *&M*#6*3$6,$~6p&~*p-6K**6*Q6*66 * *g*36&$P*p$p68*66D*96 *U6*6**6^*"6*B$,6p*p$x6&x,6I*6*]6 *6$* *6R*&6*&p9$666*o$^O6'*6&?&* *c*6*76<*56C$6 *p$l&l6p&*6Z*26 *36+6 *6**k6*)'&$@*k6$F** 666:*%6 e6*6* *d**!6B$3*j$t&t6p6"6B*6 *g* 66* *6\* &6&o*3$:*p$`6*66J*76*C6/*'6*6*&K*=62$A$~&~*n6p**66a*(6 D64*6* *!6B*&6*&"$6*p$p6*%6O6*7&I* *g*6&K!6 6H*;&6&&P**2$6*p$x6p46B*26*2t *`* 6*2e66 &27*A*6 62l.2 $3*p$`6s*6/b* 6 6656***/x*:/2*866$7*l*$k6p*/z>/3*966g61~*p6*&O*6Q*,6*@6$6*p6$E /;10*6** &D6 * 66 *m*36 *>$.*k$t&t6p66M**6k6**"6 *6Z*5&6 &o;$ 6 6*p6 $b **g*26=665*6**'&K*6 *4$6${&~*p6p 6&6B*46 *e6*(6 * *6N6*#*6&$$(6 *p$p6*m* 6)6K*/6 **6J*)6 *6<$%6*p$t&x6pt*!6*e*"6 **66=6*"&&p&$66*p6$[*+6C*6 &?& *!66&* 6* *062*'6 H$,6p*p$i&l&*36/*'6*c*46* * 66A&Q*%&6*#$$6#6=*p$@.v*p*6 ,.666* 6 * &V*36* *4$&6*n$n6&t6pt6*6 *\66*6 * *c*0&6 &o*4$6"*p$`6+96<*6*b*#6)*6*&G6*-6 *76 $($x6&~*p6p*t*6 `6*p6 *(66&* 6 **&$$3*p$m6*/6D*6q6*6**6l*6*@$6**p$x&x6p 6j**6 <606**6 *m*!6&*6&p<$3*k$`6*a66* 6 &?*&*e*6*6a*D6*+$E6*j$e&l6p&56?*;6 *069*"6*6*l*$6 *&6$-*o$F6*16=*&626B* *6*%6I*'6 *666$4*p$t&t6p*p*6A62*6**6O*&&*6&o=6$6*p$d60t*;6*t*%6* *6& 6B*%6*,$A$~&~*p6p6m6*C6* *L66 * *6Q*66*$ &'6(6*p$p6'p**36*%6?*6*6X*-6*?$0*p$t6p&x66V*6 *_66*6**]*5&6 &p*+$B*p$`6/66&6**6 &?&n*"*6.*6#6-*46*9$7*p$k&l6p&6^*#6 **D6+*6 *#6N***6#&$$+6-*p$F6 6[6 *7**266*p6-U6 **6 * *=$**p$t&t6p6W6*#* *T66*p6t6*?&&o**:6$.*l6"$e6]6*-6*'69* 6* *&M*6 ?$!6$~&~*p66p*f*66 * *`6 *"6$ *6e6 * *&%$6 6*p$l6%o*&O*7 *Z6*6)*m&*0*6&P1$?*p$u6p66F*6**2z>6,*7636&K* 6*2 *0$2,*p$`6>6[*-6*6/~ 26+6**60 *k*+6! */t6 $6/$*j$i6pt*5*6 /*5/'6 **6A/v6-/)*86*/v+$ /601v*p6:$F*6V*&6 06/B6*6-* *116*C6*6$;*p$t&t6p*o6*6R6 6* * *6J*4&6 *&o(6$2*p6+$e 6i*6 *]66 *6 * *&J6**,6$68$~&~*p6pv*)6 *+67*6**6X*6 *&$'6*l$p6,&6O*6*.66-*(6"* *b*6*&$>6 *p$r6p&x*6g6 *x*+6% **66P*6 &&p*<$6-*l$_6%,6J*)6&?&*6T*6(* *f*26*6$'6-*j$l&l6p&*/6=*46h6 *.6(**26;*6*!&$2666W*p$Ft6 *2*D60*6* *"6?**/6G$6 6 *m$q&t6pv*56*+6868*+**6B*&6&o*:$4*p*6$$b6"6+6*;6 r*16/6**&M*#6L$ 6$~&~*p6p **!6E*,6 *Y6&*6'*-6C*6*1$666Wy6 *;63666B6J6 6"6pw6<6p6/MTrkSinofZ ^6Z ^[]JZd`Z ]d`]*[] ^PvZd`Z Vd`V`LdpLpOdpOpJdpJPLdpLpOdpOpJdpJQdpQOdpOLdpLXdpXVdpVQdpQTdpTSdpSQdpQPXdpXPVdpV0]dp]pQdpQVdpVP]dp]PbdpbP[dp[P]dp]`dp`bdpbXdpXVdpV]dp][dp[XdpX]dp][dp[XdpX]dp][dp[XdpX]dp][dp[XdpX/MTrk  PercussovF pDdxD@dx@9 /=d   8=9d    + 9, d nF []JDdxDxBdxB@dx@>d/ I>x=dx=;dx;9d. J9xDdxDBdxB@dx@>dx>=dx=;dx;# D #[]_6DdxDBdxB@dx@>dx>=d Z=;dx;9dx98d, L8@JdxJIdxIGd` GxEdxExDdxD _@dx@x < 9 6 1* . , * (< & % #< "       ,  M 5 2    ( 5GdxGxEdxEDdxDBd  JB? ( !@d *< -6@>d /( 3 4$> 5a 8} 5( 3c 0 . ,+ *( ' % #,   1  ! # 'XDdxDBdxB@dx@>d@ &8>=dh $ !%= ;d ( d;9d@ `9 < L " k  P ( XGdd, T> Q P OBdd :( 8> 5 1 0=d , ' # =        )GdxGxEdxExDdxD ]Bd    %B  ( ! &Ed *< -"EDd 0 3P 7 DBdT 8$B@d d. 8 1( .>=d , ' %   =;d     !; 9d  ( !97 < (  ( (  ^ #R '< - /, - * 'GdxGxEdxEDdxD !`Bd9   B@d u@. ( >dx>=dx=p;dx;9dx9Q     #   ! (  ; ! DdxDBdxB@dx@>dx>=dF  =;d < ( ;9d (  398d !< %88 ' *( ,6 1H 4 5Jd 3P 0JIdxIGdxG . , ' % Ed ( @E ( MDd P D (   '@dx@A V 1GdxGxEdxEDdxDBd rBx@dx@>dx>a _DdxDBdxB@dx@>dx>=dQ O=;d ;9d 9# ]Gdd<>d<><=d<=dV ">=dx=;dx;9dC 59/MTrkfBaterax = -\ /5 1( 4( 6( 9C

dCdGdLd`L>CG QdCdHdLd`LQCHPCdHdLdQdpQCHL`>dCdGdLd`L>CG CdHdLdQd`QCHLPCdHdLdQdpQCHL`>dCdGdLd`L>CG CdHdLdQd`QCHLPCdHdLdQdpQCHL`>dCdGdLd`L>CG CdHdLdQd`QCHLPCdHdLdQdpQCHL`>dCdGdLd`L>CG QdCdHdLd`LQCHPCdHdLdQdpQCHL`>dCdGdLd`L>CG CdHdLdQd`QCHLPCdHdLdQdpQCHL`>dCdGdLd`L>CG CdHdLdQd`QCHLPCdHdLdQdpQCHL`>dCdGdLd`L>CG/MTrkFundo1F e6F e[]JQdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@LdIdGdCd=d9d@9IGL=C@QdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@LdIdGdCd=d9d@9IGL=CJ[] ePvQdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@QdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@QdLdHdCd@d9d@9LHQ@C@>dLdGdCd9d@9G>LC@QdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@QdLdHdCd@d9d 9LHQ@C`QdLdHdCd@d9d 9LHQ@C`QdLdHdCd@d9d 9LHQ@C`QdLdHdCd@d9d 9LHQ@C`QdLdHdCd@d9dP9LHQ@C0QdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@LdIdGdCd=d9d@9IGL=C@QdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@LdIdGdCd=d9d@9IGL=C@QdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@QdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@QdLdHdCd@d9d@9LHQ@C@>dLdGdCd9d@9G>LC@QdLdHdCd@d9d@9LHQ@C@LdGdCd>d9d@9CLG>@QdLdHdCd@d9d 9LHQ@C`QdLdHdCd@d9d 9LHQ@C`QdLdHdCd@d9d 9LHQ@C`QdLdHdCd@d9d 9LHQ@C`QdLdHdCd@d9dP9LHQ@C/simutrans-124.5/simutrans/music/41-Libertador.mid000066400000000000000000000703261520675460500217450ustar00rootroot00000000000000MThd xMTrk9untitledMarcos Antonio XYQ '/MTrk Piano []`Dd-d@dIdEdpE@IDp-(d?dGdDdpDG?p(IdEdDd@d-dp@DIEp-?d(dGdDdpDG?p(IdEdDd@d-dp@DIE 9 6 4 1 / - +# *( ( '( &- '-GdDd?d(dp?GDp(HdCdAdd-d-A<H$CEJL>xNdKdGdBd=d/d`/KGN=Bh@x(d4d8d;d?dDdxD48(?;,@LdJd>d-dEd-$CH<A >ELJnKdGdBd=d/dNd-/d4d=dBd9dGd<94B=/Gw/GBNK=y9d=dDdId@dx@D9=I4=d@dDdId9d<9D=@I<8d?dBdGd;d<;B8?G,8d;dBdGd?dLdJd>d-dEd-$CH<A >ELJnKdGdBd=d/dNd`NGBK/=oDd-d@dIdEdpE@IDp-(d?dGdDdpDG?p(IdEdDd@d-dp@DIEp-?d(dGdDdpDG?p(IdEdDd@d-dp@DIEp-GdDd?d(dp?GDp(HdCdAdd-d-A<H$CEJL>xNdKdGdBd=d/d`/KGN=Bh@x(d4d8d;d?dDdxD48(?;,@LdJd>d-dEd-$CH<A >ELJnKdGdBd=d/dNd-/d4d=dBd9dGd<94B=/Gw/GBNK=y9d=dDdId@dx@D9=I4=d@dDdId9d<9D=@I<8d?dBdGd;d<;B8?G,8d;dBdGd?dLdJd>d-dEd-$CH<A >ELJnKdGdBd=d/dNd`NGBK/=/MTrk*Baixo 01% D[]`!dx!Z!d! < 8 5 3 0 . ,-d - 0YZdp!d( +< * (! ') & $!d % 3!pdxZd 47 1 . , * (!dx!Z!d!pdxZdp$dx$d<>J@LExKdGd?dDd`DKG?`PdLdIdEdxEPLI4PdLdIdEdd<>J@LEiKdGd?dDd`DKG?yPdLdIdEdxEPLI4PdLdIdEdd<>J@LEiKdGd?dDd`DKG?oPdIdEdDd@dx@EPIDhPdLdGdDd?dx?GPLDhPdIdEdDd@dx@EPIDhPdLdGdDd?dx?GPLDhPdIdEdDd@dx@EPIDhPdLdGdDd?dx?GPLDhMdHdCdAdd<>J@LExKdGd?dDd`DKG?`PdLdIdEdxEPLI4PdLdIdEdd<>J@LEiKdGd?dDd`DKG?yPdLdIdEdxEPLI4PdLdIdEdd<>J@LEiKdGd?dDd`DKG?/MTrkHarpfP []0% &*4 ?BDF;UsV_IdxILdd9d]dQdNMQE>9]JxXd4dKdGd@d;d_dSdLKJIGFE7DCBSX@4KG;_sWYZ[\]^ `ab cdeghiUYMXWUR QP+NM8L KJ IG FEDCDFGHI^G$Fp;d$GHI KMNOP;;d R;TU'V$W;dY;ZB;d[;\==dT]^==d=Z=d=Z=d=Z@d@`Z_^\ Z Y BdX BWU Dd8d8DEd9d9E VWGd;d;GYZ [Id=d\]=I^ `Kd?dab?Kc dLd@deg@Lhi jNdBdlnBNopqsPdDduvDPwxz|XdSd@dIdLd}~L@XSI(|j`\ZYXWUVXdSdLdId@d@LXSIUTS,XdSd@dIdLdL@XSIRiXdSd@dIdLdL@XSIiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiXdSd@dIdLdL@XSIiXdSdLdId@d@LXSIiXdSd@dIdLdL@XSIiXdSd@dIdLdL@XSIiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSG-adUdUa_dSdS_\dPdP\TXdLdWYLXZ [\] ^` PdDdDPabNdBdBNc dLd@d e@LId=d=IGd;d;G7caGdLd;d;LGGdNd@d@NGGdPdDdDPGGdNdBd `BNG_GdLd_dSd@d@GL^GdKd?d\ ?GKUdDdLdPdGdGLDUPQdBdIdNdEd ZEIBQNPd@dLdIdSdDdY DI@SPLX Nd?dGdKdBd WBG?NKS_DdLdPd_dUdId UILDPUPdDdLdSdGdGPDLSPdDdDPSdGdT GS_S7R#Q#PBSdGd;dSRQ PNM L7KBdBM NODd8d P8DR TEd9d9E UGd;d;GVId=d=IW ;GSKd?d?KLd@d@LNdBdBNPdDdDPXdSd@dIdLdL@XSIiXdSdLdId@d@LXSIiXdSd@dIdLdL@XSIiXdSd@dIdLdL@XSIiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiXdSd@dIdLdL@XSIiXdSdLdId@d@LXSIiXdSd@dIdLdL@XSIiXdSd@dIdLdL@XSIiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSG-adUdUa_dSdS_\dPdP\XdLdLX-PdDdDPNdBdBNLd@d@LId=d=IGd;d;GKGdLd;d;LGGdNd@d@NGGdPdDdDPGGdNdBdBNGGdLd_dSd@d@GLGdKd?d?GKUdDdLdPdGdGLDUPQdBdIdNdEdEIBQNPd@dLdIdSdDdDI@SPLNd?dGdKdBdBG?NKS_DdLdPd_dUdIdILDPUPdDdLdSdGdGPDLSPdDdDPSdGdGS_XSdGd;d`;GSo@d\dPdPZPdPLdGdd9d]dQdQE>9]JxXd4dKdGd@d;d_dSd`SX@4KG;_@;dx;;d;Z;d;Z;d;Z=dx==d=Z=d=Z=d=Z@d@%BdB-Dd8d8DEd9d9EGd;d;GId=d=IKd?d?KLd@d@LNdBdBNPdDdDPXdSd@dIdLdL@XSIiXdSdLdId@d@LXSIiXdSd@dIdLdL@XSIiXdSd@dIdLdL@XSIiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiXdSd@dIdLdL@XSIiXdSdLdId@d@LXSIiXdSd@dIdLdL@XSIiXdSd@dIdLdL@XSIiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSG-adUdUa_dSdS_\dPdP\XdLdLX-PdDdDPNdBdBNLd@d@LId=d=IGd;d;GKGdLd;d;LGGdNd@d@NGGdPdDdDPGGdNdBdBNGGdLd_dSd@d@GLGdKd?d?GKUdDdLdPdGdGLDUPQdBdIdNdEdEIBQNPd@dLdIdSdDdDI@SPLNd?dGdKdBdBG?NKS_DdLdPd_dUdIdILDPUPdDdLdSdGdGPDLSPdDdDPSdGdGS_XSdGd;d-BdB-Dd8d8DEd9d9EGd;d;GId=d=I;GSKd?d?KLd@d@LNdBdBNPdDdDPXdSd@dIdLdL@XSIiXdSdLdId@d@LXSIiXdSd@dIdLdL@XSIiXdSd@dIdLdL@XSIiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiXdSd@dIdLdL@XSIiXdSdLdId@d@LXSIiXdSd@dIdLdL@XSIiXdSd@dIdLdL@XSIiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSGiWdSd@dGdKdK@WSG-adUdUa_dSdS_\dPdP\XdLdLX-PdDdDPNdBdBNLd@d@LId=d=IGd;d;GKGdLd;d;LGGdNd@d@NGGdPdDdDPGGdNdBdBNGGdLd_dSd@d@GLGdKd?d?GKUdDdLdPdGdGLDUPQdBdIdNdEdEIBQNPd@dLdIdSdDdDI@SPLNd?dGdKdBdBG?NKS_DdLdPd_dUdIdILDPUPdDdLdSdGdGPDLSPdDdDPSdGdGS_XSdGd;d`;GS/MTrk aMetal=Q [Z]bR'TUVY[ Z[\] ^`ae[ g hi[ [j l n[ opq [ [['[[-XdSdEdIdPdLd [[LSEXPI[! [&[*[/[3 [8[? [D  # 4 < ?[I B E[N H J N R V [Q X Z [T ][W ` [Y c[Z f i m>Dd@dGdLdD@GLDd@dGdLdLD@G-Dd@dGdLdLD@G` l f a ^ Y V S P KXdSdPdDd@dGdLd J BL@SPXGD : 0 ' $i # "       R 2  uGdLdEd@d@GLE@dPdLdGd ]G@PL] ] ] ] LdGdDd;d@d@DLG;- XdSdPdLdGdBd " * HBSPXGL P V] W X Y [ ] ^] _ ` a c d e] f]Id@dSdPdNdLdGdDdDIN@SPLGy e d bq d g i' l n q tHdCdAdd ">LJE ! P ! " % & ( , . 1 4 76 ; < ?@dDdGdKdK@DGq@dDdGdLdL@DGqEdIdLdPdPEILd>LJEoEdIdLdPdPEILd>LJE]XdSdEdIdPdLdLSEXPIBDd@dGdLdD@GLDd@dGdLdLD@G-Dd@dGdLdLD@GXdSdPdDd@dGdLdL@SPXGDAGdLdEd@d@GLE@dPdLdGdG@PL-LdGdDd;d@d@DLG;-XdSdPdLdGdBdBSPXGLId@dSdPdNdLdGdDdDIN@SPLGmHdCdAdd>LJEe@dDdGdKdK@DGq@dDdGdLdL@DGqEdIdLdPdPEILd>LJEoEdIdLdPdPEILd>LJE/MTrkSaxA@DdxDGdd>EdE@d@AdADdDGdGHdHJdU JGd TGHdSRHLdLJdJOdOQdQNdNLdLSdSTdTXdXVdVKdGdDd?d@;d4QPNM LKJ I GFE DCB@ ? >= ; 98;DKG? 7 64 2 10/ -+ * )(&% $#" !     @      - .8;FIK MN OP2A3 45 68B9:;?@A&B DF GH"@d;dx@dx@?dI K7M?;dN[;@;Dd=dx@dx@?dx?;d1MG;=DEd9d4Gd;dL;E9GKJIGFEDCB@Sd;dGd@d8dDdhD@;8xSGSd;dGd?d6dBdJ?>B?;67=ASGSd;d@dGd8dDdhD;@8xSGSd?d;dGd6dBdhB?;6xSGUd=d@dGd8dDdhD=@8xUGSdGd;d?d6dBdRXdPdPXWdNdNWUdLdLUSdKdKSQdIdIQS;?6BGPdGdGPNdEdENLdDdDLKdBdBKAdd>EdE@d@AdADdDGdGHdHJdJGdGHdHLdLJdJOdOQdQNdNLdLSdSTdTXdXVdVKdGdDd?d@;d`;DKG?h@X@d;dx@dx@?dx?;dx;@;Dd=dx@dx@?dx?;dx;=DEd9d4Gd;d<;E9GpSd;dGd@d8dDdhD@;8xSGSd;dGd?d6dBdhB?;6xSGSd;d@dGd8dDdhD;@8xSGSd?d;dGd6dBdhB?;6xSGUd=d@dGd8dDdhD=@8xUGSdGd;d?d6dBdRXdPdPXWdNdNWUdLdLUSdKdKSQdIdIQS;?6BGPdGdGPNdEdENLdDdDLKdBdBKAdd>@d@DdDGdGIdIJdJ@LdL$@ZLdLIdIGdGZGdGIdILdLNdNPdP"Ldd>@d@DdDGdGIdIJdJ@LdL$@ZLdLIdIGdGZGdGIdILdLNdNPdP~PdxPNd!U.E#'L<'L'J'+'I1'&N2&&H& &L<&L&J&+&I1&N2H N2H 'L<'L'J'+IU"1'L<'L'J''(_ 'q&F3&"S<"F "Ko"EK5L<LJ+I1S2=!T8M^GR2<!Sb &Ko&E"K5"N2H &T2&&>&!&U.&&E#&L<LJ+I1'IX' 'P'"I"F'P'N2H "L<"L"J"+"I1""L<"L"J"+"I1""N2""H" 'L<'L'J'+IU"1&Ko& Ko "T2"">"!"U.""E#""L<"L"J"+"I1"&L<&L&J&+&I1&&T2&&>&!&U.&&E#&$L<$L$J$+$I1$+L<&L+J&+I1R2<!Sb ToE"T5"$U<$U$J$+$R1$"W<"W"J"+$T$&N&'W2''Q' &W2&&Q& "W2""Q" '\2''F'!']8''V^'PU""W2""Q" W2Q &\2&&F&!&]8&&V^&&P&"W2""Q" U<UJ+R1"]2""G"!"^.""N#"&Ko& "Ko" &Ko& "Ko" N  "_z)F"3)&S<&"F" &N<&N&J&+&K&"E""L<"L"J"+"I1"+N<+N+J+++K+'E'&J&-J-)G)&Q&"N2""H" Ko Ko "N2""H" L<LJ+I1N2H )N<)N)J)+)K)$E$&Ko& "Ko" &L<&L&J&+IU"1"U<"U"J"+"R1"W2Q &W<&W&J&+(T()N)W2Q W2Q "U<"U"J"+"R1"!To! &T2& &_z-F&3-)S<)&F& 'W2'QU" To $To$ \2F!]8V^P&U<&U&J&+&R1&'U<'U'J'+RU"1"To" 'To' "U<"U"J"+"R1""U<"U"J"+"R1"&W<&W&J&+(T()N)\2F!]8V^P"U<"U"J"+"R1"&U<&U&J&+&R1&"To"E!T5!U<UJ+R1U<UJ+R1"U<"U"J"+"R1""U<"U"J""(&_ "q-F&3-)S<)&F& 'U<'U'J'+'R1'U<UJ+R1&\2&&F&!&]8&&V^&&P&&To&ET5U<UJ+R1"\2""F"!"]8""V^""P"]2G!^.N#&To&E"T5""U<"U"J"+"R1""W2""Q" "W2""Q" !W !! !&_z-F&3-)S<)&F& &\2&&F&!&]8&&V^&&P&&W<&W&J&+&T&"N""W2""Q" !To! To &W2&&Q& &\2&&F&!&]8&&V^&&P&&To& To To $To$ 'W2''Q' RX +Y+&R&"O"Y"\2""F"!"]8""V^""P"ToE'T5'U<UJ+R1"U<"U"J"+"R1"&U<&U&J&+&R1&W2Q &U<&U&J&+&R1&W2Q &U<&U&J&+&R1&&W2&&Q& $W2$$Q$ "W<"W"J"+"T"$N$"W2""Q" "U<"U"J"+"R1""To" To \2F!]8V^P&U<&U&J&+&R1&&To& "To" &To& "To" W2Q &U<&U&J&+&R1&"W2""Q" U<UJ+R1To To 'U<'U'J'+'R1'"U<"U"J"+"R1""]2""G"!"^.""N#"'W2''Q' To &To& "[2""E"!"\b" &W2&&Q& W2Q &W2&&Q& To To &\2&&F&!&]8&&V^&&P&\2F!]8V^PU<UJ+R1'W2''Q' "To"ET5W2Q &To&ET5RX +Y+&R&"O"YW2Q &U<&U&J&+&R1&"U<"U"J"+"R1"&W2&&Q& &W<&W&J&+(T()N)U<UJ+R1U<UJ+R1RX +Y+&R&"O"YU<UJ+R1"W2""Q" ToE&T5&&To& "To" U<UJ+R1"W2""Q" W2Q W2Q W2Q "U<"U"J"+"R1""W2""Q" )U<$U)J$+R1&Z2&&D&&[Q&&Z"&&G&'&Z9&&QT&#&^#&&To& "To" To &To& [2E!\b W2Q &U<&U&J&+&R1&W2Q "W2""Q" "W2""Q" &W<&W&J&+&T&"N"!To!(_ m$F3$!S8:8C8;C2CAB'ACA:>'GA9>9:9C93A50'C:>AA+BA6>6:6C6qC:>A+'DA8>8:8C8;A2CB'CACA:>'EAB>B:BCBC:>AA/>/:/C/0' C:>A+?F:C:A:J:mJACF++BC6A6>6F6qF>AC+'BA6>6:6C6qC:>A'+EA=>=:=C=3C<CA2+C:>AA+JAE>E:ECEC:>AA6>6:6C6wC8 F8C!C5A;C:>AFC A+'GC9A9>9F93A50'F>ACA2?A:>:<:E:mE<>A22?A:>:<:E:mE<>A2+GA9>9:9C93C50+C:>AC+EFBCBABJBJACFF/C/A/J/0+ JACF'QC<A<><F<9F9A9C9FAA52'CACF>A'ECBAB>BFBF>ACC/A/>/F/0' F>AC+?A:>:::C:mC:>A+.BE6A6>6H6qH>AE.+BA6>6:6C6qC:>A++QA<><:<C<9J9C9F9JCC52+FCC:>A+?A:>:::C:]C:>A++?C:A:>:F:]F>AC+2BA6>6<6E6qE<>A2.BE6A6>6H6qH>AE.+GF9C9A9J93C50+JACFC2BA=>=<=E=!22@c296;923:2H@5:5HE@E<>AE+?A:>:::C:mC:>A+'GC9A9>9F93A50'F>ACA)GA9?9<9E93A50)E<?AA.QC<A<><F<9F9A9C9FAA52.CACF>A.ECBAB>BFBF>ACC/A/>/F/0. F>AC.?>:<:9:A:mA9<>.'EAB>B:BCBC:>AA/>/:/C/0' C:>A2BA6>6<6E6qE<>A2+DA8>8:8C8;F2FCB+CC:>A.?>:<:9:A:]A9<>..GE9A9>9H93A50.H>AEA2?A:>:<:E:mE<>A22?A:>:<:E:]E<>A20??:<:::C:mC:<?0+E>=<=7=3H<HH2+7<>H+J>E>6<676wH8 J8H!H5C;7<>JH C++J>><>7>q7<>+.JE>A>>>H>qH>AE.0X?DAHA3A=0.H>AEA'GAB>B:BCBmC:>A'&M>J:J7J7:>>7:7770& 7:>.OCAAA>AFA3A=0.F>ACA'RAM>M:MCMC:>AA>>>:>C>wA@ C@A!A=>CAC:>CA >'.OEAAA>AHA3A=0.H>AEA+GAB>B:BCBmC:>A+2GAB>BA2.MEJAJ>JHJH>AEE7A7>7H70. H>AE+GAB>B:BCBmC:>A+.REMAM>MHMH>AEE>A>>>H>wA@ E@A!A=>CAH>EEA >.2BA6>6<6E6qE<>A2.B>6<696A6qA9<>.2BA6>6<6E6qE<>A2.BE6A6>6H6qH>AE.+GA9>9:9C93C50+C:>AC.GE9A9>9H93A50.H>AEA2?A:>:<:E:mE<>A2.EE=A=>=H=3A<A>2.AH>E>+EAB>B:BCB A>:CC:>A+ 2QA<><<<E<C:>AeJ9E9H9JEE502H<>AEE.GE9A9>9H93A50.H>AEA)BA6?6<6E6qE<?A)+BA6>6:6C6qC:>A+.?>:<:9:A:mA9<>.+GA9>9:9C93C50+C:>AC+?A:>:::C:mC:>A+)PC<?<<<F<9H8C8F8HCC42)FCF<?C2BA6>6<6E6qE<>A2.BE6A6>6H6qH>AE.&G>9:9793F50&7:>F.OEAAA>AHA3A=0.H>AEA+GCBAB>BFBmF>AC+2OAA>AAE+OAA>A:ACA3C=0+C:>AC+GFBCBABJBmJACF+.GEBAB>BHBmH>AE.-JC>?><>E>qE<?C-2JA>>><>E>qE<>A22OAA>AAE'OAA>A:ACA3A=0'C:>AA+JF>C>A>J>qJACF+0L?@<@:@C@;F:FCJ0CC:<?+R>M:M7M7:>>>:>7>wF@ J@F!F=CC7:>JF C+2OAA>AAE'GAB>B:BCBmC:>A'.JE>A>>>H>qH>AE.'JC>A>>>F>1F>AC' .NECAC>CHCmAEA>;.H>EA>.O>AA2OAA>AAE+PAK>K:KCK!++Nc+7>=5H 7/5C;+D+A8C:>ACA.OEAAA>AHA3A=0.H>AEA2OAA>AAE.JE>A>>>H>qH>AE.-JC>?><>E>qE<?C-+GAB>B:BCBmC:>A++OCAAA>AFA3C=0+F>ACC.OEAAA>AHA3A=0.H>AEA.GEBAB>BHBmH>AE.2GAB>BA2'MAJ>J:JCJC:>AA7>7:7C70' C:>A)OFACA?AHA3C=0)H?CFC2RAM>MAA>>><>E>wE@ H@E!E=ACE<>AHE A22JA>>><>E>qE<>A2+JA>>>:>C>qC:>A+)OCA?AKHK!..Nc.5>=2H 5/2A;.D.>8AH>EA>)PAK?K=3H 5/3A;)D)?8AE<?A?2JA>>><>E>qE<>A2.J>><>9>A>1A9<>. .NAC>C:CmAEA>;.:>A>.GAB>B:Bm:>A..XEDAD>DHD9H@A@E@HAA<2.EAEH>A-MCJ?JAAE2GAB>BA 2OAA>AA2 .NEFAF>FHFEgHCACECHAA?0.E>AEHA-JC>?><>E>qE<?C-)JA>?><>E>qE<?A)2OAA>AAE2GAB>BA22JA>>><>E>qE<>A2+JC>A>>>F>qF>AC++JA>>>:>C>qC:>A+0L?@<@:@C@;F:FCJ0CC:<?'MAJ>J:JCJC:>AA7>7:7C70' C:>A+OAA>A:ACA3C=0+C:>AC.REMAM>MHMH>AEE>A>>>H>wA@ E@A!A=>CAH>EEA >.+JA>>>:>C>qC:>A+'JC>A>>>F>qF>AC'+GCBAB>BFBmF>AC+.MEJAJ>JHJH>AEE7A7>7H70. H>AE2OAA>AAE+OAA>A:ACA3C=0+C:>AC2OAA>AAE+OAA>A:ACA3C=0+C:>AC2XAD>DA2GAB>BA20O?ADHD9H@A@E@HAA<2.EAEH>A.MEJAJ>JHJH>AEE7A7>7H70. H>AE.J>><>9>A>qA9<>.+JA>>>:>C>qC:>A++TAJ>J:JCJ$C:>AA9>9:9C9wCBYCF?0C:>A F+2OAA>AAE2JA>>><>E>qE<>A2.LE@A@>@H@;A:EJ.EAEAH>2JA>>><>E>qE<>A2.JE>A>>>H>qH>AE.+GFBCBABJBmJACF+2OAA>AA2 .NEFAF>FHFEgHCACECHAA?0.E>AEHA)MAJ?J?><>E>qE<?A)+LA@>@:@C@;F:FCJ+CC:>A'MAJ>J:JCJ A>:CC:>A' .NECAC>CHCC:>A_AEA>;.H>EA>.TEJAJ>JHJ$H>AEE9A9>9H9wABYAE?0AH>E E.'GAB>B:BCBmC:>A')JA>?><>E>qE<?A)2JA>>><>E>qE<>A2.PEKAK>KHK!..Nc.5>=2H 5/2A;.D.>8AH>EA>2MAJ>JAA7>7<7E702 E<>A+GCBAB>BFBmF>AC+&O>A:A7A3F=0&7:>F)JF>C>?>H>qH?CF))LA@?@<@E@;A:EJ)EAEA<?2JAE>E;92;:2HH5B5HEHE<>AE+TAJ>J:JCJ$C:>AA9>9:9C9wCBYCF?0C:>A F++OAA>A:ACA CC:>A+ 'NAF>F:FCFCgFCACCCFAA?0'C:>ACA.JE>A>>>H>qH>AE.)JA>?><>E>qE<?A)+OAA>A:ACA3C=0+C:>AC2JA>>><>E>qE<>A2+JA>>>:>C>/C:>A+ +NAF>F:FCFuJCCCFCJCC?0+F:>ACC+OAA>A:ACA3C=0+C:>AC2XCD>D9D9O@E@J@OEE<22JE9>C.OEAAA>AHA3A=0.H>AEA&G>B:B7Bm7:>&2OAA>AAE+GAB>B:BCBmC:>A+)OAA?AC:CCCA_CECA;+:>ACA+MFJCJAJJJJACFF7C7A7J70+ JACF.OEAAA>AHA3A=0.H>AEA)JA>?><>E>qE<?A)2JA>>><>E>qE<>A22JA>>><>E>qE<>A2.LE@A@>@H@;E:EAJ.AEH>A+GAB>B:BCBmC:>A+.O>AA+OAA>A:ACA CC:>A+ )NCC?CD:D9J@A@F@JAA<2.FA:>A.GAB>B:Bm:>A.)OAA?ACAA@>@<@E@wJFsJHFE<>A2H2FAF>FAA=>=<=E=zQF6QHF9HJF2E<>AJ2JA>>><>E>qE<>A2.J>><>9>A>qA9<>.)JC>?><>F>qF<?C)2LA@>@<@E@;H:HEJ2EE<>A+PCKAK>KFK!++Nc+7>=5H 7/5C;+D+A8CF>ACA)MAJ?JBA2)GAB?BCHCmAEA>;.H>EA>.M>J>7<797A70. A9<>2XAD>DA-JC>?><>E>qE<?C-)JA>?><>E>1E<?A) )NAC?CB:BCB!++Ec+7EY7+EN+C:>A .GCBAB>BFB!..Ec.5EY5.EN.F>AC )GAB?BA:ACA3C=0+C:>AC)JA>?><>E>qE<?A).JE>A>>>H>qH>AE.2GAB>BA2+MAJ>J:JCJC:>AA7>7:7C70+ C:>A2JA>>><>E>qE<>A2'LC@A@>@F@;A:CJ'CACAF>.RAM>M:M:>AA>>>:>wA@ F@A!A=>CA:>FA >.2GAB>BA22MAJ>J<EE<>A 2 .NEFAF>FHFE<>AgHCACECHAA?0.E>AEHA.OEAAA>AHA3A=0.H>AEA2XAD>DA.G>B.-JC>?><>E>qE<?C-2LA@>@<@E@;E:HJ2HEE<>A.TEJAJ>JHJ$H>AEE9A9>9H9wABYAE?0AH>E E..\EWAW>WHWH>AEE@A@>@H@2EFAGEHMEH>AAAF.H AE6.CJ0ECAC>CHCMAEJ .Mg@/MTrk" @General MIDITrack 8  K @{[(]@0>G7;A< AA>>>A77.>7A:G3;>< >>>::A33.:3>:G5;>< >>>::A55.:5>>G9;A< AA>>>A99.>9A@@/MTrk2 @General MIDITrack 9  K @{[(]vCF"CcCECzCGaCOCECJFEFfCEF"ECEC:CGaCoEEEjEGaE_CGaCoCECZCECJCECEEA5E@CECZCE6CAEA(EGTEEM{EFCF"CcCGaC*CA5CPEEEJFKFFE"F(EGaE*EA5E@EGaE&EM_E/MTrk  @General MIDITrack 10  K @{[(]J?G.??BB?K?Bk??Hc?AGmA}>F0> >;q> >;5>AGmA>Hl>>> ADk>>J>>Jo> >:/>>Hf>AC$AA3A$AHfAABkAAHcA>Hf>sCBkCCHcCAHfA>Gm>`AKA>G%>:>V>Z>AGmAAAA<,AA)A*5AADkAAJAAJoA A:/Af>K6>#>Fj>>;q>>35>bAKAAC A%AA5ACBkCCAAF"CHAA;qAA35Ab>K>AGmAAI$A ACAAA@VA AH$A AL5A>Bk>>Hc>>fCKC>Bk>>Hc>>I$> >CA>>@V> >Gm> AGmA CHf> >Hf>^>K>CH$C CFACC?PCAH$A AL5AABkAAHcAAI$A ACAAA@VA AH$A AL5A>Bk>>Hc>AH$A AFAAA?PA AGmA AGmA >Gm> AGmAAHlAAA AC^AA1AAF0A A;qA A;5AABkAAHcA>Hl>>> AFjAA;qAA35AAHfAAGmA\AK6A#><#>>Ds>`>K>AGmA AGmA >Hf>^AKAAC$AA3A>Bk>>A><">>>Ds> >H$> >L5>`AKA?Gm>?A>F,>>>>;q>>35>CBkCCHcC>Gm> CGmC> ABkAAHcACAA;qAA35AABkAAHcAAHfA >H$> >FA>>?P>AHfA >Hf>AGmA ?Gm?>Bk>>Hc>>Hf>sABkAAA>F"AH>>;q>>35>>Bk>>Hc>AF0A A;qA A;5AAN0AA6qAAIHAb>Gm>>G.>>BB>K?Gm> AGmA`AKA>Hf>AC$AA3A$?Hf? AHfA AG.AABBARAH A#AGm> >Gm>>Hl>>> >)>*5>ADkAAJAAJoA A:/ACGmCb>K>AF0A A;qA A;5A AHfA >H >#AFjAA;qAA35A>Bk>>Hc>AF0A A;qA A;5A AH$A AL5ACBkCCHcC?Bk??Hc?AHfA^AKAAGmA >Gm> ADkAAJAAJoA A:/AAH$A AL5A >Gm>AGmA AGmAAHfA >Hf> AHfA AH$A AL5A AGmA AGmA>H$> >L5>ABkAAHcAADkAAJAAJoA A:/AfCKCAGmA >Gm>AG.AABBARAH$A AL5A >Hf> >H$> >L5> Dk>>J>>Jo> >:/>f>K6>#>)>*5>AGmA>F0> >;q> >;5> AGm>AA><,>>)>*5>>Hf> CHfC AHfA>Bk>>Hc>AF0A A;qA A;5A>Bk>>Hc><#>>Ds>>Bk>>Hc>ABk>>Hc>ABkAAHcA>Bk>>A<<">%<)<*5<Bk>>Hc>>H$> >L5> Gm> AGmAAGmA AGmAAG.AABBAK)>*5>>Hf>f<A<<;q<<35<ADkAAJAAJoA A:/AfCMCCG"CTAHfA> >Ds>>Gm>ABkAAHcAn>K>AGmA CGmCAHf> AHfA AHfAAEo>4:D::J)::=1::Fe: B&> :Fe: >I>v:Eo::<*::?e::86: :EM:x:<:5:?e::B8::/'::B*: A:G.>G::1:@G;>>,>@>BT>#>Ct>>;q>>*3>:<*::?e::86:G.>>4b>>A1> >F.>>3/>IEo>?GF??>J?0?1?@>B&> :Fe: :Fe: :<*::?e::86:>F.>>0K>'>I1> >F.>>3/>I:B&: F.>>3/>I:B&: >F.>>0K>'>I1> Eo> Eo> :Fs:::>Fh>>>B&> :Fs:::>Ct>>;q>>*3>>Fe> ?Eo?E:N9:><*>>?6>>> :G::>J:.:%::<*::?e::86::N.::;:Eo>:Fe: ><*>>?e>>86>:N.::;:ZJ:.:%::F.::3/: ><*>>?e>>86>B&> :Eo: ?Eo?D:?e::B8::/'::B*:HGF>>>J>0>1>>Eo>>A>C.>F>>;q>>*3>E>B&> Fe> Eo> BT>#Eo>><*>>?e>>86>:Fe: >N.>>;>Gu>>1>D>>J)>>=1>:I:t>Eo> :<*::?e::86:>Q:>>;q> >33>>Fe> :F:#>Ct>>;q>>*3>E:B&: Fe> ><*>>?e>>86>>Eo> :Eo:F:D::J)::=1:Q9>Eo> :Fe: Fe> ?F.??3/? ?Eo? :Eo: :F.::3/:I>B&>J>D>>J)>>=1>><*>>?e>>86>?Eo?E;>6>B>>77>I>v?Eo?:Q:::;q: :33: Fe> >Fe>H:B&: J:.:%:>:B&: >GF>>>J>0>1>?I?vEo>D:B&:J>B&>J:BT:#:Gu::1:9Fe9H:B&: :F.::3/: 9Fe9 Eo>?Eo? Ae>>D8>>1'>>Da>>:7>?Fe? :GF::>J:0:1:9I9v9Eo9DEo>D>?e>>B8>>/'>>B*>HFe> >Fe> J<0<1<>Fe> D>>J)>>=1>>P.>>=>>+~>/MTrk @HGeneral MIDITrack 12  K @{[(]@79;7K7>/77H7&7?R7#7;Z759m5)7A37 7477E:77(u7765759m5)7A37 7477:s7775;B55>W559m5 :9m:5AR5#5=*55@@55*57:e7 9B.99599:e9+5?R5#5;Z55:55@/5:?R:#:;Z:5:e5 :=:t59m5+59V55EP5 595$5\ 5559V55EJ55BH5583554H599m99A5;,955>W5#9AR9#9=*99@@99*9>59S55>795e77(u77;57&99V99EP9 7;77>W7799t99(u99;59&79V77EP7 99m9)5A35 545:;::Dy::>:::::C/:,5?R5#5;Z5!59V55EJ55BH5583554H51:9V::EP:+7?R7#7;Z77;77Dy77>779m7 99m9 :;::DP:+9?R9#9;Z97:77@/759V55EP5 ::::@y::=:9:99C/9,5?R5#5;Z59;99Dy99>9::::C/:,5?R5#5;Z5::::@y::=:99m9 :9m: 59m5 99m9 5:s5559Bh99%9!9E:99(u99659$9?R9#9;Z95:s55599t99(u99;599:99@/9 99m9)5A35 545#99S99>5;9.55DP5*59V55EP5 3B.335399m9c5B.5555?5?R55A5;5$55DP5 5:55C/5,:9V::EP: 79m75A59,5H55(u55;55&:?R:#:;Z:59m5 :9m:+79V77EJ77BH7783774H719?R9#9;Z9:;::DP: :9m::A99,:H99(u99;59v9:e9 5:55@y55=5t5:e5 99m9 79m7+5?R5#5;Z55:55@/5:?R::A59:[55(u55;55&7?R7#7;Z7:E:::(u::65:9@:99#z9 9F99=9t59m5+39V33EP3 59;5K5>/55H5&59V55EJ55BH5583554H559m5 99m9+:9V::EP: 5:e5 :B.::5:7:77@/7 9:e9 99;9K9>/99H99:9#9;B99>W979m7 39m3+99V99EP9 9;99Dy99>999m9 59m5 59m5)7D37 7077:s7775;B55>W5#9AR9#9=*99@@99*95=5t:9m:+59V55EP5 :E:::(u::65:9:99@/9 5:5#99t99(u99;59&5?R5#5;Z59E:99(u996599:99C/9,:?R:#:;Z:!7?R7#7;Z79:e9+:9V::EP: :9m: 59m5+5AR5#5=*55@@55*59:99C/9 59m5):D3: :0:99m9 99m9 5:55@/5 5:e5 9:e9 9:99C/9 99m9 59m5 7:77C/7,:?R:#:;Z:!:AR:#:=*::@@::*:>:9V::EP: 99m9 59m5 99;9K9>/99H99:99C/9 5:e5{39m3 39m3 9:99@y99=9$5AR5#5=*55@@55*5>59S55>7;5377>W79=9t99m9 5E:55(u5565599m95A5;,555>W55:e5 9:e9 ::e:{9E:99(u99659$5?R5#5;Z53:3#5;=55DP5*5?R5#5;Z5:;::DP: 9=9t99m9 99m9 :9m:+5?R5#5;Z5!9?R9#9;Z9!5?R55A3;5)33>W33:33@/3,5?R5#5;Z55:55C/5 3:33@/3 9:99CO99?+99=99C9k9C!99C959m5:9m: 99m9 59;5K5>/55H53E:33(u33653$99V99EP9+39S33>5;3355>W55:e5{7=7o3A39,3H33(u33;53&5AR5#5=*55@@55*5~9:e9 5;55DP5 3=3t59m5+9?R9#9;Z9!59V55EP5 99m9 :9m:+:9V::EJ::BH::83::4H:19AR9#9=*99@@99*95:55@/5 9:99@/9 ::::@/: 9;99DP9 9:99@/9 79m7 99m9k:D::M^::?1::Ba:/MTrkd @General MIDITrack 14  K @{[(]o+4+v.7v./MTrkl @General MIDI WTrack 18  K{[(]f:6:o7H727L7=7K747j7H7K7= 7 5H5;7>7t7H7K7= 7G5B 55>55H5i7H7%5B 55>5"5A 5056 5N5?5i59 550 5c7>7"5A 5056 5F5B 55>5^5>5"5A 5056 5 :H:K:= :):A :0:6 : 5H5K5= 5U5>55H5K5= 5O5@5e5! 535'5 5B 55>5H525L5=5K5459A 95B 55>5^9>9H929L9=9K949j5H5K5= 5):A :0:6 :d9>97A 7076 7 :H:K:= : :H:K:= :5H5i:H:K:= :O:@:e:! ::6::H:K:= :e5>5"9A 9096 9N9?9i99 990 9c:>:7H7K7= 7)7A 7076 7N7?7i79 770 7 7@79@ 9 :E ::A:^:F:"7S 707H 7&7H 77D7H787L7C7K7:7&:E ::A:"5S 505H 5(9M 909B 9N9K9i9E 99< 9 :C:K:8 :e5F5"9M 909B 9N9K9i9E 99< 9 5H5w:A :(5>5":A :0:6 : 5H5K5= 5):A :0:6 :(5A 5056 5 9H9K9= 9)9A 9096 9(9A 9F9H9K9= 9G5B 55>5"5A 5056 5(3A 3036 3 9H9i:H:9E 99A95C5K58 5e9F9^5F5^5F5^9F97@7i9@9i9C9K98 9e7F75@5a7J7K7? 7O7B7e7# 7787#9S 909H 9F7E 77A79@97@ 7>:C:K:8 :e9F95J5K5? 5O5B5e5# 558519E 99A9N5F5^5F5@:E ::A:9C9K98 9G5E 55A5^7F7:C:K:8 : 5Q5K5F 5O5I5e5* 53505d9@93E 33A35J5K5? 5O5B5e5# 5585A3H 33D3H383L3C3K3:3j5@5:S :0:H :(:S :0:H :F:E ::A:@7E 77A7@5E 55A5^5G5H5;5L5F5K5=59S 909H 9z7@79E 99A9"9M 909B 9N9K9i9E 99< 9y5@57@ 7l:S :0:H : 5C5K58 5)9M 909B 9N9K9i9E 99< 955E 55A5@:E ::A:^9F9^5F59M9K9B 9G5E 55A5"9S 909H 9d9F9@:E ::A:7C7K78 7 :C:K:8 :e:F:5@ 5l5M 505B 5N5K5i5E 55< 5E9E 99A9t9@9;5F59C9K98 9):S :0:H :d9F907E 77A7^:F:@9H 99D9H989L9C9K9:9&:E ::A:|9@99E 99A9"5S 505H 5j5Q5K5F 5O5I5e5* 53505>5G5H5;5L5F5K5=5D5F5@7E 77A7|9@95S 505H 5(9@ 9N5@55S 505H 5d5F5@9E 99A99C9K98 99S 909H 9F5E 55A5@3E 33A3^5F5"5S 505H 5 :C:K:8 :9@9Y5C5K58 5 :C:K:8 :)5S 505H 5(3S 303H 3 3C3K38 3G5E 55A5^5F5"3S 303H 3d9G9H9;9L9F9K9=9D9C9H9<9K9E989@9:@ :N9@9C5G5H5;5L5F5K5=5D3F3@5E 55A53M3K3B 3 5C5K58 5)5S 505H 5 9C9K98 9U3F3@5H 55D5H585L5C5K5:57M 707B 7N7K7i7E 77< 7E9E 99A95C5K58 5)3@ 3F9C9K98 9 5C5K58 5 9@9a:J:K:? :O:B:e:# ::8:_9G9H9;9L9F9K9=9j9C9K98 9 :C:K:8 :):S :0:H :F9E 99A9":S :0:H :(7@ 7N9@9:M :0:B :N:K:i:E ::< :E9L 99@9R9<9L9G9K9>9/MTrk @General MIDITrack 20  K ,{[(]|0600-0G040m0? 0E3733' 3|36 3P3-3-3? 3c33i2? 2 26 2P2-22622-2G242{26 2P2-2G2& 2s2& 2c3356 5P5-5G5& 5S3? 326 2P2-222q23 2[2; 222q2= 2u2722' 22? 22622-2G242{26 2P2-2G2& 22; 2s5* 52= 2P242G2- 2c5 5S282202G272 5; 5#.A .k.?.L.;.J5* 5=282202G272}22q2= 2 2: 2P222E3 3_3: 3P323k2= 2P242G2- 2s2* 2S2 2S282202G272g2>22. 2>2; 25= 5P545G5- 5m5>55. 5383303G373g5>55. 5^55q5= 5E282202G272m2 2K0>00. 0 282202G2722= 2P242G2- 22 2S282202G272k5: 5P525?2>22. 25: 5P525w.8..0.G.7.+.= .P.4.G.- .!2> 2P252G2- 2{2-2H28 2e2>2*2: 2P222u.; .c. .i2; 2m585505G575O.>... .f2; 2C5A 5k5?5L5;5Z2; 2-282202G272/MTrk  @General MIDITrack 21  K ,{[(].3 .P.+.G.$ .1+3 +P+++U.$ .M+3++++G+1+W+3++++G+1+}++q+0 + .3 .P.+.G.$ .--S-3--+-G-1-++3 +P+++G+$ +++W+3 +P+++G+$ ++$ +k+:+k+0 +i.3 .P.+.G+3++++G+1+-.< .c..i.< .C.< .k.:.L.6.z.< .-3 -P-+-G-$ -03 0P0+0G0$ 0+< +c++q+0 +50500-0G040+.9 .P.1.G.* ...Y.8 .M.8..0.G.6.W.5..-.G.4.}++I.8 .+9 +P+1+G+* +..q.9 .=.4..$ .+3 +P+++G+$ +..i-< -c..i++Y..+3 +P+++].< .S++i))3.5..-.G.4.}+8 +-9 -P-1-G-* -1+9 +P+1+G+* +1+9 +P+1+G+* +1.9 .P.1.G.* ..9 .P.1.G.* .)07 0P0/0]+= +k+<+L+8+z--S.5..-.G.4.].8 .S+= +k+<+L+8+.7 .P./. -7 -P-/- +9 +P+1+G+* +1+9 +P+1+G+* +m.5..-.G.4.}.8 .M-5----G-4-+.9 .P.1.G.* .s-+ -k-A-k-8 - +7 +P+/+G)5))-)G)4)G)8))0)G)6).7 .P./.]..i..S.5..-.G.4.g-5----G-4-g-5----G-4-+-: -P-2-G-+ -s--;);)), )\+7 +P+/+=+8 +c..q.9 .E+;++, +H+5++-+G+4+g.5..-.G.4.+9 +P+1+G+* +]+5++-+G+4+-9 -P-1-G-* -m0500-0G040}.8 .c.8 ..9 .P.1.G.* .1.7 .P./.U++q+9 +q.7 .P./.?.;.., .+9 +P+1+G+* +s..;+;++, +P.5..-.G.4.+.9 .P.1.G.* .m.8..0.G.6.++)7 )P)/)U-+ -k-A-k-8 -+: +P+2+G++ +!+9 +P+1+G+* +m.5..-.G.4.+.7 .P./.U++Y+++9 +P+1+G+* +s.8 .)5))-)G)4)++9 +P+1+G+* +++i.8 .C+8 +c.8 .c++i))i)8 )C))7+: +P+2+G++ +!)9 )P)1)G)* )m-5----G-4-})( )-;--, -P);)), ))9 )P)1)G)* )m+8++0+G+6+m..q.9 .K+8 +C+8 + .7 .P./.k-: -P-2-G-+ -c.8 .c..Y..Y..q.9 .E.;..,.G...'.2j./MTrkm @General MIDIMelody > f{[(]}0d<07d<70d<07d470d<03d<30d<07d47>d<>3d<37d<73d<3:d<:7d<7:d<:7d<70d<03d<37d<73d<37d<7>d<>0d<07d47:dx:7d<73d<37d47>dx>7dx73d<37d<7:d<:7dx7>d<>3d<3:d<:7d<7:d<:>d<>:d<:7dx7>d<>:d<:>d<>3d<37d<70d<07d<70d<07d<7>d<>3d<3:d<:3d<37dx7:d<:3d<3:dx:7dx7:d<:>d<>3d<3:d<:0d<07d<7:d<:7dx7:d<:7dp7>d<>:dx:>d<>:d4:7d47>d<>3d<3:d<:>d<>7d<7:d<:>d<>7d<7:d<:>d<>7d<7>d<>:d<:>d<>7d<7>dx>3d<3:d<:7d<7:d<:7dx73d<3:d<:3d<3:d<:Add<>7d<7:dx:>d<>7d<7:d<:7d47:d<:>d<>7d<7>d<>3d<3:d<:>d<>:d<:>d<>:d<:7d<7:d4:AdxA>d<>7d<7:d4:>d<>7dx7>d<>7d<7>d<>Addx>:dx:>d<>Add<>Edd<>0d<07dp7>d<>0d<07d47>d<>:d<:>d<>0d<07d<73d<37d<7:dx:7dx73d<37d<7>d<>7d<7>dx>7d<7:d4:>d<>7d<73dx3:d4:>d<>:d<:7d<7:d<:7d<7:d<:Add<>7d<7:d<:>d4>7d<7:d<:3d<37dx7:d<:3d<3:d<:0d<07d<7>d<>7d<7>d<>:dx:7d<7:d<:7d<7>d<>7dx7>d<>7d<7:d<:7d<7>d<>7d<7:d<:>d<>7d<7:d<:>d<>7d<7:d<:Add4>:d4:7d<7:dx:7d<7>dp>:d<:>dx>:d<:>d<>Edd<>7d<7:dx:>d<>7d<7>d<>3d<3:d<:Add<>7d<7:d<:7d<7>dx>:d<:7d<7:d<:>d<>:d<:Addx>:d<:>d<>Addx>7dx7>d4>Add<>:dx:>d4>Edd<>Add<>Add<>3d<37d<73d<3:d<:7d<7:d<:0d<07d47>d<>:d<:>d<>7d47>d<>7d<7>d<>Edd<>3d<37d<7:d4:>d<>Add<>7d<7>d<>Add<>:d<:7dx7:d4:Add<>:dx:>dp>7d<7:d<:Add<>7d<7>d<>7d<7:d<:7d<7>dx>:d<:Add4>7d<7>d<>3d<3:d4:Add<>7d<7>d<>:d<:>d,>:d<:>d<>Edd<>Addx>:d<:>dx>:dx:AdxA>d<>AdxA>d<>Add<>7d<7>d<>7d<7>d<>7d<7:d<:>d<>:dx:>d<>Add<>:d<:>d<>:d<:Add<>Edd4>:d<:>d<>7d<7>d4>Edd<>Add<>Add<>:d<:>d<>:d<:>d<>Eddx>Ad4A>dp>EdpE0d<03d<37d<73d<37d<7>d<>0d<07d<73d<37d<7:dx:7d<73d<3:d<:7d<7:dx:>d<>0d<07d<70d<07d<7>d<>7d<7>d<>3d<37dx7:d<:7d<7>d<>:d<:7dx7:d<:>d<>:d<:Add<>7dp7>dp>:d4:>dx>3d<3:d<:0d<07d<7:d<:7d<7:dx:3d<37d<7:d<:7d<7>d<>7d<7:d<:3d<3:d4:Add<>3d<3:d<:Add<>:d<:>d<>:d<:>d<>7d<7:d<:>dx>7d<7:d<:>d<>7d<7>d<>7d<7>dx>:d<:>d<>:dx:>d<>:dx:AdxA>dx>Add<>3d<3:d<:>d<>:d,:>d<>:d<:Add<>3d<3:d4:Add<>:d4:Add<>:dx:Add<>Addp>7d<7:d<:Addp>AdxA>d<>0d<07d47:dx:7dx73d<37d<7>d<>7d<7>d<>3d<37d<7:d4:>d<>Add<>0d<07d<7>d<>7d<7>d4>7d<7:d<:7d<7>d4>Edd<>:d<:>dx>7d<7:d<:>d<>:d<:>d<>Edd4>AdxA7d<73d<37d47>dx>:dx:7d<7:d<:>d<>:dx:Add<>:d<:>d<>Add<>:d<:>d<>:d<:>d<>7d<7>d<>7d<7>d<>Eddx>Addx>:dx:>d<>Add<>Addx>Addp>Edd<>:dh:Add<>Add<>Add<>Add<>:d<:>d<>7d<7>dx>:d<:Add<>Addx>:d<:Add<>Eddx>Addp>Add<>Eddx>:d<:7d<7:d4:>d<>7dx7>d<>7d<7>d<>Addx>7dx7:d<:>d<>:d<:Add4>Eddp>Edd<>Add<>:d<:>d<>AdxA>dx>:d<:>d<>Edd<>Edd<>7d<7:d4:>d<>:d<:7dx7>d4>Add<>:d<:>d<>:d<:>d<>Edd<>7d<7:d4:/MTrk*1 @General MIDIDrums (Y  }L4MdFdKdd>d>d>d>'><"<;";99775544 jdjidigdgededddbdb`d`_d_]b][b[Y\YX\XVVVTVTSPSQKQOKOMEML?LJ?JH9HG9GE3EC-CA-A@'@>"><"<;;997755..-~-+~+)~)(~(&~&$~$#~#!Y!YYYYY333333$/MTrk !D---- System Setup --------------------------------------------------/@---- CH Setup --------------------------------------------------y 0 @d[ @G---- End of CH Setup --------------------------------------------------|/MTrkE!D---- System Setup --------------------------------------------------/@---- CH Setup --------------------------------------------------y $ 1d[ ed @G---- End of CH Setup -------------------------------------------------- %!dp!dpdpdp!dp!dpdpddddx $ K< K KKK#K#!K!K!K741.,*&$"!         "/MTrk,!D---- System Setup --------------------------------------------------/@---- CH Setup --------------------------------------------------y  "d[ @G---- End of CH Setup --------------------------------------------------4d(dx4(-d!dp-!d+dp+)ddp)d(dp(-d!dp-!+ddp+)ddp)(dd(d(d(d(d(d(d(x4d0d-d<40--d0d4d-04-d-4d47d79d95d2d0d-dZ520-4d/d,dx4/,-d0d4dZ-044d0d-d40-9d94d40d0-d--d2d5dZ-25,d(d4d,(44d,d/d4,/4d0d-d<40--d0d4d-04-d-4d47d79d95d2d0d-dZ520-4d/d,dx4/,-d0d4dZ-044d0d-d40-!d&d)dZ!&) dd(d (4d47d(d d#d7( #4d44d0d-d<40--d0d4d-04-d-4d47d79d95d2d0d-dZ520-4d/d,dx4/,-d0d4dZ-044d0d-d40-9d94d40d0-d--d2d5dZ-25,d(d4d,(44d,d/d4,/4d0d-d<40--d0d4d-04-d-4d47d79d95d2d0d-dZ520-4d/d,dx4/,-d0d4dZ-04Ed@dd4d7d<747d4d>74>d7d4d>74d4d7d<747d4d>74>d7d4d>74d>d0d4d7d>047d9d5d>959d5d=d95=>d2d7d;d>27;;d2d7d;27>d7d4d0d>7404d7d>d0d47>04d7dd59>?d9d5d?95@d;d8d4d@;84;d8d4d/d;84/,d/d4d8d,/484d/d,d(dZ4/,(9d0d4d-d904-0d4d-d04-0d4d9d-d049-7d4d0d+d740+5d-d)d0d5-)04d-d)d0d4-)0)d-d5d0d)-50)d-d9d0d)-907d/d2d7/22d/d6d2/67d2d/d5d72/5;d/d2d5d7d;/257>d0d4d7d>047d4d7d<747d4d>74>d7d4d>74d4d7d<747d4d>74>d7d4d>74d>d0d4d7d>047d9d5d>959d5d=d95=>d2d7d;d>27;;d2d7d;27>d7d4d0d>7404d7d>d0d47>04d7dd59>?d9d5d?95@d;d8d4d@;84;d8d4d/d;84/,d/d4d8d,/484d/d,d(dZ4/,(9d0d4d-d904-0d4d-d04-0d4d9d-d049-7d4d0d+d740+5d-d)d0d5-)04d-d)d0d4-)0)d-d5d0d)-50)d-d9d0d)-907d/d2d7/22d/d6d2/67d2d/d5d72/5;d/d2d5d7d;/257>d0d4d7d>047d:6>?d3d8d1d4d$d,d14$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, 1d4d$d,d14$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d(d9d$,(9d1d(d$d1($d1d4d(d$d14($ $d(d$( $d(d$( $d(d$(($d(d$(($d(d$(zEd7d 7 E\Ed7d 7 EdEd7d 7 EdEd7d 7 E\Ed7d 7 EdEd7d 7 EdEd7d 7 E\Ed7d 7 E ,3(3,( ,<(<,( ,A(A,( ,J(JEd7d 7 ,(E ,P(P,( ,Y(Y,( ,_(_,( $d(d1d,d$(1,z$d(d1d$(1F$d(d1d$(1($d(d1d$(1(1d4d,d$d14,$ ,d, ,d, ,d, ,d(d,( ,d, ,d, ,d(d,( ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, ,d$d,$ ,d, ,d, ,d, ,d(d,( ,d, ,d, ,d(d,( ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, $d,d$, ,d, ,d, ,d, ,d(d,( ,d, ,d, ,d(d,( ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, $d,d$, ,d, ,d(d,( ,d, ,d$d,$ 1d$d(d1$((1d(d$d1($(1d(d$d1($(1d(d$d1($(1d(d$d1($F1d4d$d,d14$, ,d, ,d, ,d$d,$ ,d(d,( ,d$d,$ ,d, ,d$d,$ ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, $d,d$, ,d, ,d, ,d$d,$ ,d(d,( ,d$d,$ ,d, $d,d$, ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, $d,d$, ,d, ,d, ,d$d,$ ,d(d,( ,d$d,$ ,d, ,d$d,$ ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, $d,d$, ,d, ,d, ,d$d,$ ,d(d,( ,d$d,$ ,d, $d,d$, ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, 1d4d$d,d14$, ,d, ,d, ,d$d,$ ,d(d,( ,d$d,$ ,d, ,d$d,$ ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, $d,d$, ,d, ,d, ,d$d,$ ,d(d,( ,d$d,$ ,d, $d,d$, ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, ,d$d,$ ,d, ,d, ,d$d,$ ,d(d,( ,d$d,$ ,d, ,d$d,$ ,d, ,d(d,( ,d$d,$ ,d, ,d(d$d,($ ,d, ,d$d(d,$( ,d, $d(d1d)d$(1)F$d(d1d)d$(1)F$d(d1d)d$(1)7$d(d)d1d$()1M1d4d$d,d14$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, 1d4d$d,d14$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, ,d$d(d,$( ,d, $d,d$, ,d, ,d(d$d,($ ,d, $d,d$, ,d, $d(d9d,d$(9, ,d, ,d, $d(d1d,d$(1, ,d, ,d, $d(d1d,d$(1, ,d, ,d, $d(d1d,d$(1, ,d, ,d, $d(d1d,d$(1, ,d, 1d4d$d,d14$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, 1d4d$d,d14$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, $d(d,d$(, ,d, $d,d$, ,d, ,d$d(d,$( ,d, $d,d$, ,d, ,d(d$d,($ ,d, $d,d$, ,d, $d(d9d,d$(9, ,d, ,d, $d(d1d,d$(1, ,d, ,d, $d(d1d,d$(1, ,d, ,d, $d(d1d,d$(1, ,d, ,d, $d(d1d,d$(1, ,d, $d,d(d&d$,(&$d(d,d&d$(,&(d$d,d&d($,&(d$d,d&d($,&(d$d,d&d($,&(d$d,d&d($,& 1d(d$d1($d14d$d,d14$, ,d, ,d, ,d, (d,d(, ,d, ,d, (d,d(, ,d, (d,d(, $d,d$, ,d, (d,d(, ,d, ,d, ,d, $d,d$, ,d, ,d, ,d, (d,d(, ,d, ,d, (d,d(, ,d, (d,d(, $d,d$, ,d, (d,d(, ,d, ,d, ,d, ,d$d,$ ,d, ,d, ,d, ,d(d,( ,d, ,d, ,d(d,( ,d, ,d(d,( ,d$d,$ ,d, ,d(d,( ,d, ,d, ,d, ,d$d,$ ,d, ,d(d,( ,d, ,d$d,$ ,d(d,( ,d, ,d(d,( $(,$(, $(,$(, $(,$(, $(,$(, $(,$(, $(,149#d$(,149#L/MTrk !D---- System Setup --------------------------------------------------/@---- CH Setup --------------------------------------------------y  d[ @G---- End of CH Setup --------------------------------------------------/MTrk !D---- System Setup --------------------------------------------------/@---- CH Setup --------------------------------------------------y  d[ @G---- End of CH Setup --------------------------------------------------/MTrk !D---- System Setup --------------------------------------------------/@---- CH Setup --------------------------------------------------y  d[ @G---- End of CH Setup --------------------------------------------------/MTrk !D---- System Setup --------------------------------------------------/@---- CH Setup --------------------------------------------------y  d[ @G---- End of CH Setup --------------------------------------------------/MTrk !D---- System Setup --------------------------------------------------/@---- CH Setup --------------------------------------------------y  d[ @G---- End of CH Setup --------------------------------------------------/simutrans-124.5/simutrans/music/44-Above-the-sky.mid000066400000000000000000001441361520675460500223000ustar00rootroot00000000000000MThdMTrkcCopyright (C) 2018yTCgz URLQB@SetupXpQStartX/MTrk System Setup!=---- Reset --------------------------------------------------~ < AB@A<@---- S̐ݒ --------------------------------------------------A---- Drum lj -------------------------------------------------- AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@  AB@ @---- CH Setup --------------------------------------------------=---- Layer --------------------------------------------------K---- End of System Setup --------------------------------------------------/MTrk%!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y P TP[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@QdMdHdXQMHxQdMdHdpQMH@GdJdOdXGJOxGdJdOdpGJO@OdLdHdXOLHxOdLdHdpOLH@LdHdCdXLHCxHdLdCdpHLC@MdJdEdXMJExMdEdJdpMEJ@LdGdDdXLGDxLdGdDdpLGD@LdHdEdXLHExLdEdHdpLEH@EdIdLdXEILxLdIdEdpLIE@QdMdHdXQMHxQdMdHdpQMH@GdJdOdXGJOxGdJdOdpGJO@OdLdHdXOLHxOdLdHdpOLH@LdIdEdXLIExEdIdLdpEIL@NdFdIdXNFIxNdIdFdpNIF@PdKdHdXPKHxHdKdPdpHKP@IdDdNdpIxxIxNxxDNINxDdIdNdPxxPxDINUxxUxNdIdDdZxxZxNID\xx\xDdIdMdaxxaxDIMNdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@QdMdHdXQMHxQdMdHdpQMH@GdJdOdXGJOxGdJdOdpGJO@OdLdHdXOLHxOdLdHdpOLH@LdHdCdXLHCxHdLdCdpHLC@MdJdEdXMJExMdEdJdpMEJ@LdGdDdXLGDxLdGdDdpLGD@LdHdEdXLHExLdEdHdpLEH@EdIdLdXEILxLdIdEdpLIE@QdMdHdXQMHxQdMdHdpQMH@GdJdOdXGJOxGdJdOdpGJO@OdLdHdXOLHxOdLdHdpOLH@LdIdEdXLIExEdIdLdpEIL@NdFdIdXNFIxNdIdFdpNIF@PdKdHdXPKHxHdKdPdpHKP@IdDdNdpIxxIxNxxDNINxDdIdNdPxxPxDINUxxUxNdIdDdZxxZxNID\xx\xDdIdMdaxxaxDIMNdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@IdDd@dXID@x@dDdGdp@DG@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxPdLdGdDdpPLGD@UdPdLdXUPLxLdPdSdpLPS@NdLdIdEdXNLIExPdEdIdLdpPEIL@NdEdIdLdXNEILxPdLdIdEdpPLIE`SdLdIdEd`SLIENdGdLdDdXNGLDxLdGdDdpLGD@@d;d8dEdX@;8ExDd@d;d8dpD@;8@/MTrk =!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y U @P[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------@ UPB6NZX@0%B0JD0oF0I>JhBJ0@H0=F0;D08B06@ @B@0B0D0F0H0J BJ`_J0N00Q0XT0X&QZzQZ0 U0BO0{I03D&@`@002<0L:0e80~6(>5$B6NZ@B6NZX@0%B0JD0oF0I>JhBJ0@H0=F0;D08B06@ @B@0B0D0F0H0J BJ`_J0N00Q0XT0X&QZzQZ0 U0BO0{I03D&@`@002<0L:0e80~6(>5$B6NZ@ UPNUX@0%B0JD0oF0I>JhBJ0@H0=F0;D08B06@ @B@0B0D0F0H0J BJ`_J0N00Q0XT0X&QZzQZ0 U0BO0{I03D&@`@002<0L:0e80~6(>5$NU@NUX@0%B0JD0oF0I>JhBJ0@H0=F0;D08B06@ @B@0B0D0F0H0J BJ`_J0N00Q0XT0X&QZzQZ0 U0BO0{I03D&@`@002<0L:0e80~6(>5$NU@ UPNUX@0%B0JD0oF0I>JhBJ0@H0=F0;D08B06@ @B@0B0D0F0H0J BJ`_J0N00Q0XT0X&QZzQZ0 U0BO0{I03D&@`@002<0L:0e80~6(>5$NU@NUX@0%B0JD0oF0I>JhBJ0@H0=F0;D08B06@ @B@0B0D0F0H0J BJ`_J0N00Q0XT0X&QZzQZ0 U0BO0{I03D&@`@002<0L:0e80~6(>5$NU/MTrkg!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y W @[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------  W!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp W!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp&dP&&dP&!dp!dpdPdPdp#dp#$dP$$dP$dpdp$dP$$dP$dpdpdPdPdp!dp! dP dP dp#dp#!dP!!dP!!dp!#dp#%dP%%dP%!dp!dp&dP&&dP&!dp!dpdPdPdp#dp#$dP$$dP$dpdp!dP!!dP!!dp!dpdPdPdpdp dP dP dp $dp$%dP%%dp%p%dp%pdp`/4%d0<70I:0V=$@$%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<% W!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp W!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp&dP&&dP&!dp!dpdPdPdp#dp#$dP$$dP$dpdp$dP$$dP$dpdpdPdPdp!dp! dP dP dp#dp#!dP!!dP!!dp!#dp#%dP%%dP%!dp!dp&dP&&dP&!dp!dpdPdPdp#dp#$dP$$dP$dpdp!dP!!dP!!dp!dpdPdPdpdp dP dP dp $dp$%dP%%dp%p%dp%pdp`/4%d0<70I:0V=$@$%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<%d<% W!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!!dp!dpdpdpdpdpdpdpdpdpdpdpdpdpdpdpdp/MTrk!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y V @[ ed@& Ked@ed @edG---- End of CH Setup -------------------------------------------------- VEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GD VEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GD VEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GDEdLdIdELI@dGdDd@GD/MTrk!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y `[ ded@&ed@ed @edG---- End of CH Setup -------------------------------------------------- ` XddwXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx XdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx ` XddwXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx XdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx ` XddwXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx XdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx ` XddwXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx XdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx ` XddwXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx XdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx ` XddwXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx XdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLxXdxXxSdxSxUdxUxQdxQxSdxSxNdxNxPdxPxLdxLx @DPxDDdCdX;>Cx;d>dCdp;>C@Cd@dd9dXA>9x9d>dAdp9>A@@d;d8dX@;8x@d;d8dp@;8@@ddCdX;>Cx;d>dCdp;>C@Cd@ddCdX;>Cx;d>dCdp;>C@Cd@dd9dXA>9x9d>dAdp9>A@@d;d8dX@;8x@d;d8dp@;8@@ddCdX;>Cx;d>dCdp;>C@Cd@d!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk4!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------#d#Z#E#Z#C#Z#=#Z#7#R#I##d#Z#B#Z#>#Z#:#Z#8#R#L#2#d#B#d#Z#F#Z#B#Z#=#Z#7#R#d##d#Z#C#Z#?#Z#9#Z#4#R#d##d#Z#H#Z#A#Z#>#Z#;#R#d##d#Z#F#Z#@#Z#;#Z#9#R#d#2#d#B#d#Z#C#Z#A#Z#=#Z#:#R#d##d#Z#F#Z#B#Z#?#Z#<#R#d##d#Z#F#Z#F#Z#D#Z(d(R#d#Z#F#Z#D#Z#B#Z#@#Z#>#Z(Z(B#d#Z#E#Z#B#Z#?#Z(d(R#d#Z#C#Z#A#Z#>#Z#<#Z#8#Z(Z(B#d#Z#C#Z#>#Z#8#Z(d(R#d#Z#E#Z#B#Z#@#Z#=#Z#:#Z(Z(B#d#Z#A#Z#A#Z#>#Z(d(R#d#Z#E#Z#C#Z#A#Z#?#Z#<#Z(Z(B#d#Z#B#Z#A#Z#@#Z(d(R#d#Z#d#Z#A#Z#?#Z#>#Z#;#Z(Z(B#d#Z#A#Z#>#Z#=#Z(d(R#d#Z#C#Z#=#Z#;#Z#9#Z#8#Z(Z(B#d#Z#G#Z#D#Z#B#Z(d(R#d#Z#E#Z#D#Z#A#Z#@#Z#>#Z#=#Z#;#Z#;#Z#:#Z#:#Z#8#Z#6#Z#5#Z#5#Z#3#Z#2#Z#1#Z#0#Z#/#Z#-#Z#,#Z#+#Z#)#Z#'#Z#%#Z#d1d.d#1.R#d#R(d.d(.R#d#Z#H#Z#E.E#.Z#C#Z#@#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d9d(.9Z(d(Z(d(Z(d(Z#d1d.d#1.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d0d#0Z#d0d#0Z#d0d#0Z#d0d#0Z/d#d/#Z-d#d-#Z+d#d+#Z)d#d)#Z#d1d.d#1.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d9d(.9Z(d(Z(d(Z(d(Z#d1d.d#1.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z9d/d9/R#d-d#-R#d+d#+R#d*d1d)d#*1)B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*R#d#R*d(d*(R#d#R#d*d1d#*1B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d1d#*1R*d#d*#R*d(d*(R*d#d*#R*d#d*#R#d*d#*R*d(d*(R*d*R#d*d1d#*1R*d#d*#R*d(d*(R*d#d*#R*d#d*#R#d*d#*R*d(d*(R*d*R#d*d2d1d#*21Z0d0Z/d*d/*Z-d-Z*d+d*+R#d*d)d#*)B#d*d)d#*)B*d#d)d*#)4d#d#Z#d#Z#d#Z#d#Z#d##d##d##d##d##d##d##d#4#d1d.d#1.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d9d(.9Z(d(Z(d(Z(d(Z#d1d.d#1.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d0d#0Z#d0d#0Z#d0d#0Z#d0d#0Z/d#d/#Z-d#d-#Z+d#d+#Z)d#d)#Z#d1d.d#1.B*d*"*d*"*d*"*d*"*d*"*d*"*d*"*d*"*d*"*d*"*d*"*d*"*d*"*d*"*d*"(d.d9d(.9Z(d(Z(d(Z(d(Z#d1d.d#1.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z9d/d9/R#d-d#-R#d+d#+R#d*d1d)d#*1)B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*R#d#R*d(d*(R#d#R#d*d1d#*1B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d#*B*d*R#d#R*d*B*d(d*(B#d*d1d#*1R*d#d*#R*d(d*(R*d#d*#R*d#d*#R#d*d#*R*d(d*(R*d*R#d*d1d#*1R*d#d*#R*d(d*(R*d#d*#R*d#d*#R#d*d#*R*d(d*(R*d*R#d*d2d1d#*21Z0d0Z/d*d/*Z-d-Z*d+d*+R#d*d)d#*)B#d*d)d#*)B*d#d)d*#)4d#d#Z#d#Z#d#Z#d#Z#d##d##d##d##d##d##d##d#4#d1d.d#1.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d9d(.9Z(d(Z(d(Z(d(Z#d1d.d#1.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d#Z#d#Z#d.d#.Z#d#Z#d#R(d.d(.B#d.d#.R#d#R(d.d(.R#d4d#Z#d#Z#d0d0#Z#d0d0#Z#d0d0#Z#d0d0#Z/d#d#/Z-d#d#-Z+d#d#+Z)d#d#)Z1d#d#1F4#d#Z#d#Z#d#Z#d#R#d#R(d(R#d#2#d#Z#d#Z#d#Z#d#Z#d#R#d#R(d(R#d#2#d#Z#d#Z#d#Z#d#Z#d#R#d#R(d(R#d#2#d#Z#d#Z#d#Z#d#Z#d#R#d#R(d(R#d#B#d#R#d#Z#d#Z#d#Z#d#Z#d#R#d#R(d(R#d#2#d#Z#d#Z#d#Z#d#Z#d#R#d#R(d(R#d#2#d#Z#d#Z#d#Z#d#Z#d#R#d#R(d(R#d#29d0d(d90(Z#d0d#0Z#d0d#0Z#d0d#0Z0d#d0#Z#d0d#0Z1d0d(d10(Z#d0d#0Z#d0d#0Z#d0d#0Z/d(d9d/(9Z#d/d#/Z#d-d#-Z#d-d#-Z#d+d#+Z#d+d#+Z#d1d)d(d#1)(b/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/simutrans-124.5/simutrans/music/45-Misty-Forest.mid000066400000000000000000001651601520675460500222300ustar00rootroot00000000000000MThdMTrk=Copyright 2011 by GobanboshiQ p<XY/MTrk0!Ye @Ye @YK @/MTrk8!e @Ye @Ye @YK @/MTrkv!Ye @Ye @YF< D9d6d2d9629d4d1d9419d6d2d9629d4d1d9419d6d2d9629d4d1d9419d6d2d9629d4d1d9419d6d2d9629d4d1d9419d6d2d9629d4d1d9419d6d2d962;d8d4d;849d6d1d9619d6d2d962;d8d4d;849d6d1d9619d6d2d9629d4d1d9419d6d2d9624d9d1d4919d6d2d9624d9d1d4919d6d2d9624d9d1d4919d6d2d9624d9d1d4919d6d2d9624d9d1d4919d6d2d9628d;d4d8;46d9d1d6919d6d2d9628d;d4d8;46d9d1d6919d6d2d9628d`;d4d 8;46d9d1d@691@9d6d2d962;d8d4d;846d9d1d6919d6d2d9629d4d1d941Z9d6d2d0Y@X@WV@UT@SR@Q@PO@NM@LK@JI@H@GF@ED@CB@A@@?@>=@<;@:@98@76@54@32@1@0/@.-`9629d4d1d`,+@*@)(@'&@%$@#@"!@ @@@@@@@@@@ @ @  @ @@@@@0941@Z/MTrkJ! d d d$d*d,dp$*,>dp>`*dEdp*E,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEdp*E,dp,p$d>dEdp$>E*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,*d$d,dp*$,>dp>,d.Kp,.p*dEdp*E,dp,pEd>d$dpE>$,d*dp,*>dp>$dEdp$E*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEdp*E,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,pEd$d,d>d&dpE$,>&,d*dp,*>d,d&dp>,&Ed$dpE$*d,d,d&dp*,,&*d,d&dp*,&,d,d>d&dp,,>&.Kp.,d,d&dp,,&$d*d,dp$*,>dp>,d.Kp,.p*dEd,d&dp*E,&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEd,d&dp*E,&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d$d,dp*$,>dp>,d.Kp,.p*dEd,d&dp*E,&,dp,p>d$dEdp>$E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEd,d&dp*E,&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,d,d&dp>,,&,d&d.Kp,&.,dp,$d,d*dp$,*>dp>,d.Kp,.p*dEd,d&dp*E,&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEd,d&dp*E,&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d$d,dp*$,>dp>,d.Kp,.p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$,d*dp,**d,d&dp*,&,d>dp,>.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEd,d&dp*E,&,dp,p$dEd>dp$E>,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,p>dEd$dp>E$*d,dp*,>d,d&dp>,&$d,dEd&dp$,E&*d,dp*,*d,d&dp*,&>d,d,d&dp>,,&$d,d&d.Kp$,&.,d,d&dp,,&*d$d,dp*$,>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,p>d$dEdp>$E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEd,d&dp*E,&,dp,p$dEd>dp$E>,d*dp,*>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,d,d&dp,,&*d$d,dp*$,>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,p>d$dEdp>$E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEd,d&dp*E,&,dp,p$dEd>dp$E>,d*dp,*>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,d,d&dp,,&p*dEd,d&dp*E,&,dp,p>d$d,dEd&dp>$,E&*d,dp*,>d,d&dp>,&$d,dEd&dp$,E&*d,dp*,*d,d&dp*,&>d,dp>,,d&d.Kp,&.,d,d&dp,,&$d*d,dp$*,>dp>,d.Kp,.p*dEdp*E,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEdp*E,dp,p$dEd>dp$E>,d*dp,*>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,d.Kp,.p*dEdp*E,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEdp*E,dp,p$dEd>dp$E>,d*dp,*>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>d,d$dEd&dp>,$E&*d,dp*,>d,d&dp>,&Ed$dpE$*d,d,d&dp*,,&*d,d&dp*,&>d,d,d&dp>,,&.Kp.,d,d&dp,,&,d$d*dp,$*>dp>,d.Kp,.p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$,d*dp,**d,d&dp*,&,d>dp,>.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*d,dEd&dp*,E&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,p$d>dEdp$>E*d,dp*,>dp>Ed$dpE$,d*dp,**d,d&dp*,&,d>dp,>.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*d,dEd&dp*,E&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,d,d&dp>,,&,d&d.Kp,&.,dp,,d$d*dp,$*>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$,d*dp,**d,d&dp*,&,d>dp,>.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*d,dEd&dp*,E&,dp,p$d>dEdp$>E*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,pEd$d>dpE$>,d*dp,*>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*d,dEd&dp*,E&,dp,p$dEd>dp$E>,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,pEd>d$dpE>$,d*dp,*>d,d&dp>,&Ed$d,d&dpE$,&,d*dp,**d,d&dp*,&>d,dp>,,d$d&d.Kp,$&.,d,d&dp,,&$d*d,dp$*,>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,pEd$d>dpE$>,d*dp,*>dp>Ed$dpE$,d*dp,**d,d&dp*,&,d>dp,>.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*d,dEd&dp*,E&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,d,d&dp,,&$d*d,dp$*,>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,pEd$d>dpE$>,d*dp,*>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*d,dEd&dp*,E&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,d,d&dp,,&p*d,dEd&dp*,E&,dp,pEd$d,d>d&dpE$,>&,d*dp,*>d,d&dp>,&Ed$d,d&dpE$,&*d,dp*,*d,d&dp*,&>d,dp>,,d&d.Kp,&.,d,d&dp,,&$d*d,dp$*,>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,p$d>dEdp$>E*d,dp*,>dp>Ed$dpE$,d*dp,**d,d&dp*,&,d>dp,>.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*d,dEd&dp*,E&,dp,p$dEd>dp$E>,d*dp,*>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*d,dEd&dp*,E&,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,d&dp,&*d$d,dp*$,>dp>,d.Kp,.p*d,dEd&dp*,E&,dp,p>d$dEdp>$E*d,dp*,>dp>Ed$dpE$,d*dp,**d,d&dp*,&,d>dp,>.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEd,d&dp*E,&,dp,pEd>d$dpE>$,d*dp,*>dp>Ed$dpE$*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEd,d&dp*E,&,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*d,d&dp*,&>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,d,d&dp,,&p*dEd,d&dp*E,&,dp,p>d$d,dEd&dp>$,E&*d,dp*,>d,d&dp>,&$d,dEd&dp$,E&*d,dp*,*d,d&dp*,&>d,dp>,,d&d.Kp,&.,d,d&dp,,&$d*d,dp$*,>dp>,d.Kp,.p*dEdp*E,dp,p$d>dEdp$>E*d,dp*,>dp>$dEdp$E*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,$d*d,dp$*,>dp>,dp,p*dEdp*E,dp,p$d>dEdp$>E*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,*d,d$dp*,$>dp>,dp,p*dEdp*E,dp,p>dEd$dp>E$*d,dp*,>dp>Ed$dpE$*d,dp*,*dp*>d,dp>,.Kp.,dp,d$d*d,d0cb0$*,>d`a>`,d.K@_0,.`^]*dEd\`*E,d0[@,ZY`$d>dEd0XW0$>E*d,dV`*,>d0UT0>$dEd`S$ER*d,d@Q0*,*d`P*O>d,dN`>,.K0M@.L,dK`,*d,d$d0JI0*,$>dH`>,d0GF0,`ED*dEd@C0*E,d`B,A@`Ed>d$d0?@E>$>,d*d=`,*>d0<;0>Ed$d:`E$*d,d0980*,*d`7*6>d,d@50>,.K`4.3,d2`,$d*d,d01@$*,0>d/`>,d0.-0,`,*dEd0+*0*E,d`),(@'0$dEd>d`&$E>%,d*d$`,*>d0#@>"$dEd!`$E*d,d0 0*,*d`*>d,d00>,.K`.,d@0,*d,d$d`*,$>d`>,d0@,`*dEd00*E,d`,00Ed>d$d` E>$ ,d*d@ 0,*>d` > Ed$d`E$*d,d0@*,*d`*>d,d00>,.K`.,d00,@d/MTrk !0n0n0n ID`]dQd`=`<`;0:`9`8`7`605`4`3`2`100`/`.`-`,0+`*`)`(`'0&`%`$`#`"0!` ```0````0````0````0 ` ` ` ` `0````0```Ua@n`Qd]d`=`<`;0:`9`8`7`605`4`3`2`100`/`.`-`,0+`*`)`(`'0&`%`$`#`"0!` ```0````0````0````0 ` ` ` ` `0````0```aU@n@/MTrk !ddd @Z!d!-d-<+d+,d,<+d+BA*dFEG(H9IBJK'IDC@Z9D37.*n)H%)d)@(d*EDG*J;KCKKhIGlD6B@:=v72,&] ; ( '0'd'@&d&Z%d%Zd$<&d$&<d$<&d$&<d$<&d$&<d$<&dp&dpd$<(d$(<d$<(d$(<d$<(d$(<d$<(dp(dpd$<*d$*<d$<*d$*<d$<*d$*<d$<*dp*dxxd$<*d$*<d$<*d$*<d$<*d$*<d$<(dp(dpd$<&d$&<d$<&d$&<d$<&d$&<d$<&dp&dpd$<(d$(<d$<(d$(<d$<(d$(<d$<(dp(dpd$<*d$*<d$<*d$*<d$<*d$*<d$<*dp*dxxd$<*d$*<d$<*d$*<d$<*d$*<d$<*dp*dpD*d*,d,-d4-<,d4,<*d$*<(d$(<&d4&<%d4%<&dd&<%d$%<&dd&<%d4%<#d4#0 =0 <0 :0 90 80 70 6E@0 50 4E 30 20 10 00 /0 -0 ,0 +0 *0 )0 (0 '0 &0 %0 $0 #0 "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 E@0 0 E 0 0 B@0 0 B 0 0 9@0 0 9 0 0 ;D0 0 ; 0 0 =H0 0 = 0 0 @L0 0 @ 0   =Q0 0 = 0 0 @U0 0 @ #0 %0 'BY0 *0 ,B /0 10 3E^0 60 8E :0 =0 ?@d 0 #0 %@(Bd (0 *0 -B / Ed$ 20 4$E 7Bd :0 <0 ?B(Ed A0 D0 FE I Gd$ K0 N$G Q @Ed0 C0 GEGd J0 M0G QId0 T0 WILd [0 ^ `L OId0 T0 ZILd _0 e0L jNd0 p0 uNQd {$ ( Q ~0 }0 |0 {0 z0 y0 x0 w0 v0 u0 t0 r0 q0 p0 o0 n0 m0 l0 k0 j0 i0 h0 g0 f0 e0 d0 c0 b0 a0 `0 _0 ^0 ]0 \0 [0 Y0 X0 W0 V0 U0 T0 S0 R0 Q0 P0 O0 N0 M0 L0 K0 J0 I0 H0 G0 F0 E0 D0 C0 A0 @T 9@0 0 9 0 0 ;D0 0 ; 0 0 =H0 0 = 0 0 @M0 0 @ 0   =Q0 0 = 0 0 @V0 0 @ #0 %0 'BZ0 *0 ,B /0 10 3E_0 60 8E :0 =0 ?@d 0 #0 %@(Bd (0 *0 -B / Ed$ 20 4$E 7Bd :0 <0 ?B(Ed A0 D0 FE I Gd$ K0 N$G Q @Ed0 C0 GEGd J0 M0G QId0 T0 WILd [0 ^ `L OId0 T0 ZILd _0 e0L jNd0 p0 uNQd {$ ( QB@ ~0 },B |0 {0 z0 y0 x0 w0 v0 uE@ t0 r,E q0 p0 o0 n0 m0 l0 k0 jI@ i0 h,I g0 f0 e0 d0 c0 b0 a0 `E@ _0 ^,E ]0 \0 [0 Y0 X0 W0 V0 U0 T0 S0 R0 Q0 P0 O0 N0 M0 L0 K0 J0 I0 H0 G0 F0 E0 D0 C0 A0 @tE@xExB@xBxD@xDhG@xGhL@xLhG@xGG@xGxD@xDxB@xBhE@xEhI@xIhE@xEE@xExB@xBxE@xEhI@xIhN@xNhI@xIhN@xNhI@xIhE@xEhI@xIhB@xBhE@xEhI@xIhE@xEE@xExB@xBxD@xDhG@xGhL@xLhG@xGG@xGxD@xDxB@xBhE@xEh AI@0 @0 ?I >0 =0 <0 :0 90 80 70 6E@0 50 4E 30 20 10 00 /0 -0 ,0 +0 *0 )0 (0 '0 &0 %0 $0 #0 "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 E@0 0 E 0 0 B@0 0 B 0 0 9B0 0 9 0 0 ;C0 0 ; 0 0 =E0 0 = 0 0 @F0 0 @ 0   =H0 0 = 0 0 @I0 0 @ #0 %0 'BK0 *0 ,B /0 10 3EL0 60 8E :0 =0 ?@N 0 #0 %@(BO (0 *0 -B / EP$ 20 4$E 7BQ :0 <0 ?B(ER A0 D0 FE I GS$ K0 N$G Q @ET0 C0 GEGU J0 M0G QIV0 T0 WILV [0 ^ `L OIW0 T0 ZILX _0 e0L jNY0 p0 uNQY {$ ( QB@ ~0 },B |0 {0 z0 y0 x0 w0 v0 uE@ t0 r,E q0 p0 o0 n0 m0 l0 k0 jI@ i0 h,I g0 f0 e0 d0 c0 b0 a0 `E@ _0 ^,E ]0 \0 [0 Y0 X0 W0 V0 U0 T0 S0 R0 Q0 P0 O0 N0 M0 L0 K0 J0 I0 H0 G0 F0 E0 D0 C0 A0 @tE@xExB@xBxD@xDhG@xGhL@xLhG@xGG@xGxD@xDxB@xBhE@xEhI@xIhE@xEE@xExB@xBxE@xEhI@xIhN@xNhI@xIhN@xNhI@xIhE@xEHB@xBhE@xEhI@xIhE@xEE@xExB@xBxD@xDhG@xGhL@xLhG@xGG@xGxD@xDxB@xBhE@xEh AI@0 @0 ?I >0 =0 <0 :0 90 80 70 6E@0 50 4E 30 20 10 00 /0 -0 ,0 +0 *0 )0 (0 '0 &0 %0 $0 #0 "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 E@0 0 E 0 0 B@0 0 B 0 0 9@0 0 9 0 0 ;D0 0 ; 0 0 =H0 0 = 0 0 @L0 0 @ 0   =Q0 0 = 0 0 @U0 0 @ #0 %0 'BY0 *0 ,B /0 10 3E^0 60 8E :0 =0 ?@d 0 #0 %@(Bd (0 *0 -B / Ed$ 20 4$E 7Bd :0 <0 ?B(Ed A0 D0 FE I Gd$ K0 N$G Q @Ed0 C0 GEGd J0 M0G QId0 T0 WILd [0 ^ `L OId0 T0 ZILd _0 e0L jNd0 p0 uNQd {$ ( Q ~0 }0 |0 {0 z0 y0 x0 w0 v0 u0 t0 r0 q0 p0 o0 n0 m0 l0 k0 j0 i0 h0 g0 f0 e0 d0 c0 b0 a0 `0 _0 ^0 ]0 \0 [0 Y0 X0 W0 V0 U0 T0 S0 R0 Q0 P0 O0 N0 M0 L0 K0 J0 I0 H0 G0 F0 E0 D0 C0 A0 @T/MTrk>!dd"d`" "d""d "`/MTrk! @x @`d @bK ; adpa_dp_]#ad]@a`_dp_adpabdPbadPaad`aadPa_dP_]dp]p_b0_pdda2`daa2dd`a`d`adpa_dp_ad@a`_dp_adpabdPbadPaad`aadPa_dP_]d`]Zd`Z\d`\]d`]_d`_ad`a_dp__dP_]dn]r_d_9]do]Q_dP_p]d@]]dP]\dP\Zd`ZXd@XZd@ZUd UUdpUVdpVXdPXVdPVUd`USd S ]d@]]dP]\d`\pZd`Z\d \\dp\ZdpZX#\dPX\Z#]dPZ]\#_dp\_p]#adh]xa_d\#`_\\#_dH\x_]#adh]xa\#_dp\_\#_dX\x_]#ad`]abd]#Pb]dda#Pdadda#`dadda# daadpa_dp_]#ad]@a`_dp_adpabdPbadPaad`aadPa_dP_]dp]p_d0_pdda2`daa2dd`aPdpadpa_dp_]dad]@a`_dp_adpabdPbadPaad`aadPa_dP_]d`]Zd`Z\d`\]d`]_d`_ad`a_dp__dP_]dp]p_d@_]d@]_d@_]d@]]dP]\dP\Zd`ZXd@XZd@ZUd UUdpUVdpVXdPXVdPVUd`USd S ]d@]]dP]\d`\pZd`Z\d \\dp\ZdpZX#\dPX\Z#]dPZ]\#_dp\_p]2adh]xa_d\2p_\p\2_dH\x_]2adh]xa\2_dp\_\2_dX\x_]2adp]pa]2bdP]ba2ddPada2dd`ada2dd adaPpadPpdfPPfdPPdaP`adP`dfPpffPpf_#hPP_phiPa#@ia@/MTrk ! 2 2 2 Y BdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpGdpGpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGpIdpIpBdpBpGdpGR2BdR1BpGd0^GB/.Id".NIR-BdpBB,.Gdr+~G"*NIdpI)^BdB(.Br'~GdR&GpId%^Ir$~Bd"#NBR"GdpGB!.Idr ~IRBdpB^GdB.G"NIdRIpBd^Br~Gd"NGpId^IB.Bdr~BRGdpG^IdB.I"NBdRBpGdB.Gr~Id" NIpBd ^BB .Gd" NGR IdpI^Bdr~B"NGdRGpIdB.Ir~BdRBpGd^GBP2/MTrk* ! K K K J8G>RLd>@pL>pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@pLd=@pL=pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=LpId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@pLd=@pL=pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>LpId@@pI@pB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=LpId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@pLd=@pL=pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>LpId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=Lp@@Idp@IpLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>LpId@@pI@pB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@pLd=@pL=pId@@pI@pB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>LpId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@pLd=@pL=pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p=@Ldp=LpId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=LpId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@pEpB@LdpBLpE@IdpEIB@LdpBL`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@pEpB@LdpBLpE@IdpEIB@LdpBL`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@pLd=@pL=p@@Idp@IpB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@pEpB@LdpBLpE@IdpEIB@LdpBL`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@pEpB@LdpBLpE@IdpEIB@LdpBL`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@pLd=@pL=pId@@pI@pB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLB>@Ldp>Lp@@Idp@IpB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=LpId@@pI@pB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>LpId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@p>@Ldp>LpId@@pI@pB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=LpId@@pI@pB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB@>@Ldp>LpId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p>@Ldp>Lp@@Idp@IpB@LdpBLpE@NdpENpB@LdpBLpE@NdpENB@IdpBI`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@pLd>@pL>pId@@pI@pLdB@pLBpNdE@pNEpB@LdpBLpE@NdpENB@IdpBI`@@p@pLd=@pL=pId@@pI@pLdB@pLBpE@pEpLdB@pLBpIdE@pIELdB@pLB`@@p@p=@Ldp=Lp@@Idp@IpB@LdpBLpE@NdpENpLdB@pLBpNdE@pNEIdB@pIB`@@p@RKLd>@J^L>I@HId@@"GNI@rF~LdB@rE~LBBD.E@C^EB@ALdB@"@NLBr?~IdE@r>~IELdB@B=.LB@r7~L>B6.Id@@5^I@4@3LdB@"2NLBr1~NdE@r0~NEB/.B@Ld.^BL-@,E@Nd"+NENB@Idr*~BIr)@(.@@'^@&@%Ld=@"$NL=r#~Id@@r"~I@B!.LdB@ ^LB@E@"NEr~LdB@r~LBB.IdE@^IELdB@@LB"@~@@r~@B.=@Ld^=L@@@Id"N@Ir~B@Ldr ~BLB .E@Nd ^EN @ LdB@"NLBr~NdE@r~NEIdB@B.IBp@@@"N@r K/MTrk!2d i QdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPNdQdVdPNQVpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPUdQdLdpUQLpVdQdNdpVQNpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPNdQdVdPNQVpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPUdQdLdpUQLpVdQdNdpVQNpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPNdQdVdPNQVpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPUdQdLdpUQLpVdQdNdpVQNpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPNdQdVdPNQVpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPUdQdLdpUQLpVdQdNdpVQNpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPNdQdVdPNQVpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPUdQdLdpUQLpVdQdNdpVQNpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPNdQdVdPNQVpQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPQdUdpQUPUdQdpUQpVdQdpVQRaKQdUdpUQPQdUdpUQPQdUdpUQPQdUdpUQ2a_AQdUdpUQPQdUdpUQ2_]7QdUdpUQPNdQdVdPVQNR]Z2QdUdpUQPQdUdpUQ2Z\<QdUdpUQ2\QdUdaQdUd"=NQUB<;:9@8NdQdVdr7654.NQV32NQdUdB10QUr/@.-,~QdUd+*NQUr)('&%QdUd"$NQUB#"! @QdUdr~QU@NQdUdBQUr@~QdUdNQUr  QdUd" NQUB  @UdQdLdr~UQLNVdQdNdB.VQNpd/MTrk!x ; UnpUSnpSUn@U`SnpSUnpUVnPVUn`UpUnpUpUnPUSnPSQnpQpSn0SRVIVXnpXpXnXUnpUSnpSUn@U`SnpSUnpUVnPVUn`UpUnpUpUnPUSnPSQn`QNn`NPn`PQn`QSn`SUn`USn3S=SnPSQnnQrSnS9QnoQQSn@SQnPQpQnPQPnPPNn`NLn@LNn@NIn0IpInpIJnpJLnPLJnPJIn`IGnG0QnPQpQnPQPn`PpNn`NPn PPnpPNnpNPnL-PPLQnN-PQNSnP-pSPpQ-UnhQxUSnP-pSPpP-SnHPxSQ-UnhQxUP-SnxPxSP-SnXPxSQ-UnxQhUQ<V2WIQVWU<Xn`UXpUKXxpUXpUUX} UXUnpUSnpSUn@U`SnpSUnpUVnPVUn`UpUnpUpUnPUSnPSQnpQpSn0SRVIVXnpXpXnXUnpUSnpSUn@U`SnpSUnpUVnPVUn`UpUnpUpUnPUSnPSQn`QNn`NPn`PQn`QSn`SUn`USnpSSnPSQnpQpSn@SQn@QSn@SQnPQpQnPQPnPPNn`NLn@LNn@NIn0IpInpIJnpJLnPLJnPJIn`IGnG0QnPQpQnPQPn`PpNn`NPn PPnpPNnpNPnL-PPLQnN-PQNSnP-pSPpQ<UnhQxUSnPdEd IdH>E Ih>dEd IdH>E IFPUPJ1QdPJxQxI/LdPIxLxJ.QdPJxQxJ*PdPJxPxL/QdPLxQxN+SdpNxSxP(UdPPxUxS2XdpSxXxN2Sd0NxSxQdN2xQxNSdP2xSxPQ2UdPQxUxQ2VdPQxVxS2Xd0SxXxVdQ2xVxQUdP2xUxPQ2VdPQxVxUdxUxSdxSxQdxQxPdxPxJ2NdPJxNxJ1QdPJxQxLdHLxJ.QdPJxQxJ*PdPJxPxL/QdPLxQxN+SdpNxSxP(UdPPxUxQdxQxSdxSxPd8PhQdxQxSdxSxI*PdPIxPxE*LdPExLxI*NdIxN/simutrans-124.5/simutrans/music/46-House-in-the-station.mid000066400000000000000000002204731520675460500236070ustar00rootroot00000000000000MThdMTrkQSXY/MTrk System Setup!/MTrk@! iNxN NxN iN2xN NJxN iNcxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN i  i  i  i  iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN  iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN iNdxN NdxN i  i  i  i  i  i  i  i  i  i  i  i  i  i  i  i/MTrk?x! Wededdd#d +\#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd!dp!d(dp(#d/d4#/<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd(dp(p d,d@@ ? ?o>o>>>)=)=2<2<6;6;8:8:x8x8W7W76644$0$0%+%+"%"% ,d@@d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd!dp!d(dp(#d/d4#/<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd(dp(p d,d4 ,<d#d4#<c(c4(< d,d4 ,<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd!dp!d(dp(#d/d4#/<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d#dp#d%dp%pd%dx%Zd#d#'&'&%d1d/./.4343x7x7;;N=N=@@E@E@(FEFEGFGFGGZDZD-B-B@@y=y=;;h8h8M5M5~1~1U.U.~*~*P&P&w!w!HH//  WW0 0   PP((TT%1jjo>W>W> > >c<c< : :_5_5D/D/t)t)""  < ,<@@d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd!dp!d(dp(#d/d4#/<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd(dp(p d,d4 ,<d#d4#<c(c4(< d,d4 ,<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd!dp!d(dp(#d/d4#/<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d!dp!pd dp d!dp!pd#dp#pd#d4#<d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d dp d!dp!d(dp(pd!dp!d(dp(pd dp d#dp#d%dp%pd%dx%x%d1dp%1#d/dp#/d"dp" d,d4 ,<d%dp%d'dp'"d.dp".pd'dp'"d.dp".pd%dp%d'dp'pd%dp%d'dp'pd%dp%pd%dp%d"dp"d#dp#d*dp*pd#dp#d*dp*pd"dp"d#dp#pd"dp"d#dp#pd%dp%d*dp*#d/dp#/d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#dp#d)dp)d*dp*%d1dp%1pd*dp*%d1dp%1pd)dp)d*dp*pd)dx)xd)dp)pd"dp"d)dp)"d.dp".d%dp%d'dp'"d.dp".pd'dp'"d.dp".pd%dp%d'dp'pd%dp%d'dp'pd%dp%pd%dp%d"dp"d#dp#d*dp*pd#dp#d*dp*pd"dp"d#dp#pd"dp"d#dp#pd%dp%d*dp*#d/dp#/d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#dp#d)dp)d*dp*%d1dp%1pd*dp*%d1dp%1pd)dp)d*dp*pd)dx)xd)dp)pd"dp"d)dp)"d.dp".d%dp%d'dp'"d.dp".pd'dp'"d.dp".pd%dp%d'dp'pd%dp%d'dp'pd%dp%pd%dp%d"dp"d#dp#d*dp*pd#dp#d*dp*pd"dp"d#dp#pd"dp"d#dp#pd%dp%d*dp*#d/dp#/d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#dp#d)dp)d*dp*%d1dp%1pd*dp*%d1dp%1pd)dp)d*dp*pd)dx)xd)dp)pd"dp"d)dp)"d.dp".d%dp%d'dp'"d.dp".pd'dp'"d.dp".pd%dp%d'dp'pd%dp%d'dp'pd%dp%pd%dp%d"dp"d#dp#d*dp*pd#dp#d*dp*pd"dp"d#dp#pd"dp"d#dp#pd%dp%d*dp*#d/dp#/d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#d4#<d)dp)d*dp*%d1dp%1pd*dp*%d1dp%1pd)dp)d*dp*pd)dp)d*dp*pd*d4*<*d6d4*6<d*d4*<d%dp%d'dp'"d.dp".pd'dp'"d.dp".pd%dp%d'dp'pd%dp%d'dp'pd%dp%pd%dp%d"dp"d#dp#d*dp*pd#dp#d*dp*pd"dp"d#dp#pd"dp"d#dp#pd%dp%d*dp*#d/dp#/d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#dp#d)dp)d*dp*%d1dp%1pd*dp*%d1dp%1pd)dp)d*dp*pd)dx)xd)dp)pd"dp"d)dp)"d.dp".d%dp%d'dp'"d.dp".pd'dp'"d.dp".pd%dp%d'dp'pd%dp%d'dp'pd%dp%pd%dp%d"dp"d#dp#d*dp*pd#dp#d*dp*pd"dp"d#dp#pd"dp"d#dp#pd%dp%d*dp*#d/dp#/d#dp#d%dp% d,dp ,pd%dp% d,dp ,pd#dp#d%dp%pd#dp#d%dp%pd#dp#pd#dp#d)dp)d*dp*%d1dp%1pd*dp*%d1dp%1pd)dp)d*dp*pd)dp)d*dp*pd*dx*xd*d4@@@@@@8?8? ? ???o>o>W>W>K>K>B;B;=9=9<*@@/MTrk!Qn J Ed4E! d O@4d;c@cDcp4`;@D`Dc=c@cxD=@XDc@c=cxD@=XDc@c=cxD@=X=c@cDcx=@Dh=c@cDcx=@DhDc@c=cxD@=XEc@c=cxE@=X=c@cEcx=@EXEc@c=chE@=h=c@cEcx=@Eh=c@cEcx=@Eh?cBcGcx?BGXDc=c@cxD=@XDc@c=cxD@=XDc@c=cxD@=X=c@cDcx=@Dh=c@cDcx=@DhDc@c=cxD@=XEc@c=cxE@=X=c@cEcx=@EXEc@c=chE@=h=c@cEcx=@Eh=c@cEcx=@Eh?cBcGcx?BGX@c=cDcx@=DX=c@cDcx=@DX=c@cDcx=@DXDc@c=cxD@=hDc@c=cxD@=h=c@cDcx=@DXEc@c=cxE@=X=c@cEcx=@EXEc@c=chE@=h=c@cEcx=@Eh=c@cEcx=@Eh?cBcGcx?BGXDc=c@cxD=@XDc@c=cxD@=XDc@c=cxD@=X=c@cDcx=@Dh=c@cDcx=@DhDc@c=cxD@=hEc@c=cpE@=PEc@c=cpE@=P=cDcAcp=DA`=dGdDdAd<=GDA4Gc=cAcDcPG=ADp?UFUBU ?FB`?UFUBUp?FB`?UBUFUp?BF`FUBU?UpFB?pGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`GUBU?UpGB?p=UAUDU =AD`=UAUDUp=AD`=UDUAUp=DA`AUDU=UpAD=pFU=UBU F=B`FUBU=UpFB=`AU=UFUpA=F`=UFUAUp=FApFU?UBU F?B`?UFUBUp?FB`?UBUFUp?BF`?UFUBUp?FBpGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`GUBU?UpGB?p=UAUDU =AD`=UAUDUp=AD`=UDUAUp=DA`=UAUDUp=ADp=UBUFU =BF`FUBU=UpFB=`BU=UFUpB=F`BUFU=UpBF=p8212p81Dc=c@cxD=@X=c@cDcx=@DX=c@cDcx=@DX=c@cDcx=@Dh=c@cDcx=@DhDc@c=cxD@=h9242p94Ec@c=cxE@=X=c@cEcx=@EX=c@cEch=@Eh=c@cEcx=@Eh=c@cEcx=@Eh?cBcGcx?BGh8212p81Dc@c=cxD@=XDc@c=cxD@=X=c@cDcx=@DX=c@cDcx=@Dh=c@cDcx=@Dh=c@cDcx=@DhEc@c=cpE@=PEc@c=cpE@=`GcBc?chGB?hGcBc?cxGB?hGcBc?cPGB?p1282p18Dc=c@cxD=@X=c@cDcx=@DX=c@cDcx=@DX=c@cDcx=@Dh=c@cDcx=@DhDc@c=cxD@=h4292p49Ec@c=cxE@=X=c@cEcx=@EX=c@cEch=@Eh=c@cEcx=@Eh=c@cEcx=@Eh?cBcGcx?BGh1282p18Dc@c=cxD@=XDc@c=cxD@=X=c@cDcx=@DX=c@cDcx=@Dh=c@cDcx=@Dh=c@cDcx=@DhEc@c=cpE@=PEc@c=cpE@=`GcBc?chGB?hGc?cBcxG?BhGcDc@cPGD@`Dc=c@cxD=@XDc@c=cxD@=XDc@c=cxD@=X=c@cDcx=@Dh=c@cDcx=@DhDc@c=cxD@=XEc@c=cxE@=X=c@cEcx=@EXEc@c=chE@=h=c@cEcx=@Eh=c@cEcx=@Eh?cBcGcx?BGXDc=c@cxD=@XDc@c=cxD@=XDc@c=cxD@=X=c@cDcx=@Dh=c@cDcx=@DhDc@c=cxD@=XEc@c=cxE@=X=c@cEcx=@EXEc@c=chE@=h=c@cEcx=@Eh=c@cEcx=@Eh?cBcGcx?BGX@c=cDcp@=D`=c@cDcp=@D`=c@cDcp=@D`Dc@c=cpD@=pDc@c=cpD@=p=c@cDcp=@D`=c@cEcp=@E`Ec@c=cpE@=`Ec@c=cpE@=`Ec@c=cpE@=pEc@c=cpE@=pGcBc?cpGB?`=c@cDcp=@D`=c@cDcp=@D`=c@cDcp=@D`Dc@c=cpD@=pDc@c=cpD@=pDc@c=cpD@=pEc@c=cpE@=PEc@c=cpE@=`?cBcGcp?BG`Gc?cBcpG?BpDcGc@cPDG@`@c=cDcp@=D`=c@cDcp=@D`=c@cDcp=@D`Dc@c=cpD@=pDc@c=cpD@=p=c@cDcp=@D`=c@cEcp=@E`Ec@c=cpE@=`Ec@c=cpE@=`Ec@c=cpE@=pEc@c=cpE@=pGcBc?cpGB?`=c@cDcp=@D`=c@cDcp=@D`=c@cDcp=@D`Dc@c=cpD@=pDc@c=cpD@=pDc@c=cpD@=`=c@cEcp=@E`Ec@c=cpE@=`Ec@c=cpE@=`Ec@c=cpE@=pEc@c=cpE@=pGcBc?cpGB?`@c=cDcp@=D`=c@cDcp=@D`=c@cDcp=@D`Dc@c=cpD@=pDc@c=cpD@=p=c@cDcp=@D`=c@cEcp=@E`Ec@c=cpE@=`Ec@c=cpE@=`Ec@c=cpE@=pEc@c=cpE@=pGcBc?cpGB?`@c=cDcp@=D`=c@cDcp=@D`=c@cDcp=@D`Dc@c=cpD@=pDc@c=cpD@=p=c@cDcp=@DpEc@c=cpE@=PEc@c=cpE@=P=cDcAcp=DA`=dGdDdAd<=GDA4Gc=cAcDcPG=ADp?UBUFU ?BF`BUFU?UpBF?`BU?UFUpB?F`FU?UBUpF?BpBUGU?U BG?`BU?UGUpB?G`BU?UGUpB?G`GU?UBUpG?Bp=UAUDU =AD`=UAUDUp=AD`=UDUAUp=DA`AUDU=UpAD=pFU=UBU F=B`FUBU=UpFB=`AU=UFUpA=F`=UFUAUp=FApFU?UBU F?B`?UFUBUp?FB`?UBUFUp?BF`?UFUBUp?FBpGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`GUBU?UpGB?p=UAUDU =AD`=UAUDUp=AD`=UDUAUp=DA`=UAUDUp=ADp=UBUFU =BF`FUBU=UpFB=`AU=UFUpA=F`AU=UFUpA=Fp?UFUBU ?FB`?UFUBUp?FB`?UBUFUp?BF`FUBU?UpFB?pGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`GUBU?UpGB?p=UAUDU =AD`=UAUDUp=AD`=UDUAUp=DA`AUDU=UpAD=pFU=UBU F=B`FU=UBUpF=B`AU=UFUpA=F`=UFUAUp=FApFU?UBU F?B`?UFUBUp?FB`?UBUFUp?BF`?UFUBUp?FBpGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`GUBU?UpGB?p=UAUDU =AD`=UAUDUp=AD`=UDUAUp=DA`=UAUDUp=ADp=UBUFU =BF`FUBU=UpFB=`BU=UFUpB=F`BU=UFUpB=FpFUBU?U FB?`?UBUFUp?BF`?UBUFUp?BF`?UBUFUp?BFpGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`?UBUGUp?BGp=UAUDU =AD`=UDUAUp=DA`=UDUAUp=DA`=UDUAUp=DAp=UBUFU =BF`FUBU=UpFB=`AU=UFUpA=F`AUFU=UpAF=p?UBUFU ?BF`?UBUFUp?BF`?UBUFUp?BF`?UBUFUp?BFpGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`?UBUGUp?BGp=UAUDU =AD`=UDUAUp=DA`=UDUAUp=DA`=UDUAUp=DAp=UBUFU =BF`FUBU=UpFB=`BU=UFUpB=F`BUFU=UpBF=pFUBU?U FB?`?UBUFUp?BF`?UBUFUp?BF`?UBUFUp?BFpGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`?UBUGUp?BGp=UAUDU =AD`=UDUAUp=DA`=UDUAUp=DA`=UDUAUp=DAp=UBUFU =BF`FUBU=UpFB=`AU=UFUpA=F`AUFU=UpAF=p?UBUFU ?BF`?UBUFUp?BF`?UBUFUp?BF`?UBUFUp?BFpGUBU?U GB?`?UBUGUp?BG`?UBUGUp?BG`?UBUGUp?BGp=UAUDU =AD`=UDUAUp=DA`=UDUAUp=DA`=UDUAUp=DAp/MTrk 3!0P n"BdNdpBNDdPdpDPQdEdpQEGdSd4IdUdd'F(Fx,p>'h(>d'P(Pp,Zx,>'h(>d'd(Zh>'x(,d(dx,h((xp((dp,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x(*xxx?x7'<.<>x,x?x7x'x>xx?,x'7*xx*>x.x,.4,xx,x*xx*x?x'x7x.x>xx?x7'<.<>x(,x?x7x'x>x(xx,x?7'*xx*>x(.x(xp((x<.4,x4x9xx,x*xx*x.xx44.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4(,x(wx,x(*x(Hx*x(.x(Tp((]<.4(,x(dx,x(*x(lx*x(.x(sp((x<.  ((xx4 ,(x< e4x| dx ePxp,x9x @d,x*xx*x.x,.49,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x(.x(xp((x<.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4(,x(;x,x(*x(Gx*x(.x(Sp((]<.4(,x(Ox,x(*x(Zx*x(.x(cp((k<.4(,x(^x,x(*x(gx*x(.x(pp((x<.4(,x(xx,x*xx*x(.x(xp((x<.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,x9xx,x*xx*x.x,.4(9,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x*xx*x.x,.4,xx,x*xx*x.x,.4(,x(xx,x(*xx*x.x,.4,x9xx,x*xx*x.x,.49,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,x*xx*x.x,.4,xx,H,x'x>xx,x'x>h>x'xp,xx>',h>x'xp'x>x&x'x,xx&wx&',&mx&x&&Np&&5p&/MTrk4! nb4dp4p9dp9p@dp@pGdpGpLdpLpQdpQpPdpPn `X  u,& `*u B ? @ y,& Y*y r  u, 0 fu My, C0 Uy gu," y u ly, Y0 Fy @u, f u fy, M Ay fu,  `*u B ? @ y,& Y*y r  u, 0 fu My, C0 Uy gu," y u ly, Y0 Fy @u, f u fy, M Ay fu,  0 du(y, H0 -y u+ 0 u )y+" =.y R&u* f0 z  u& iy). S"y >u) (0  u  y(& '*y M"u' r 0 au(y' C0 %y   u&& *u '"y% :0 My ` u$& s  u& fy$. L"y 3u# 0 u& &y". L"y r  u"  ` u ]y! <0 y  u & *u %"y  60 Hy Z u& l*u ~ y h0 Py" 9u* !&u  y& &*y L"u r 0 hu(y P0 9y !u  u y /0 Fy(u ^0 u  u& hy. P"y 9u !0 u y "0 Dy" fu  `  u& ]y. <"y u 0 u -y A0 +y(u , u y $0 5y B u& "*u  y 0 'y" :u A0 -u y"  y u < ?0 *u(y , y u ., C u& /y. "y   u & *u 4 A y & +*y "u  0 u(y  = @0 &y u 0 u !y " 2$ > y& +u . "u  y & *y %"u  8 > ?u *y  ,  y& u . 5 B u& 4y . '"y u   u y  0 /y(u  > @0 -u y"  y u 3 @ @ u& -y. "y  u& *u -"y A0 -y u"   u& y. *"y >u 30 'u" y* &y  u 0 u (y" < By < B u&   u& y. '"y 9 ? u& -*u "y  0 y(u *0 = D u& /y. "y u 0 !u" 0y* ? Ay ,u ,  u& y. %"y 5u B0 5u (y" .y &u  0 u(y 0 /y > A u& 2*u $"y 0   y& u. '"u 9 ? y& ,*y "u  0 u(y +, ?y "u  0 u(y 0 $y 0u < Au(yF C y A 4dp4p9dp9p@dp@pGdpGpLdpLpQdpQpPdpPP4dp4p9dp9p@dp@pGdpGpLdpLpQdpQpPdpPP4dp4p9dp9p@dp@pGdpGpLdpLpQdpQpPdpPP6@x6x:Bx:x=Cx=xBExBx6Fx6x:Hx:x=Ix=xBKxBx6Lx6x:Nx:x=Ox=xBQxBx6Rx6x:Tx:x=Ux=xBWxBx6Xx6x:Zx:x=[x=xB]xBx6^x6x:`x:x=ax=xBcxB `X  u,& `*u B ? @ y,& Y*y r  u, 0 fu My, C0 Uy gu," y u ly, Y0 Fy @u, f u fy, M Ay fu,  `*u B ? @ y,& Y*y r  u, 0 fu My, C0 Uy gu," y u ly, Y0 Fy @u, f u fy, M Ay fu,  0 du(y, H0 -y u+ 0 u )y*" =.y R&u* f0 z  u& iy). S"y >u) (0  u  y(& '*y M"u( r 0 au(y' C0 %y   u'& *u '"y& :0 My ` u&& s  u& fy%. L"y 3u% 0 u& &y$. L"y r  u$  ` u ]y# <0 y  u#& *u %"y" 60 Hy Z u"& l*u ~ y! h0 Py" 9u!* !&u  y & &*y L"u  r 0 hu(y P0 9y !u  u y /0 Fy(u ^0 u  u& hy. P"y 9u !0 u y "0 Dy" fu  `  u& ]y. <"y u 0 u -y A0 +y(u , u y $0 5y B u& "*u  y 0 'y" :u A0 -u y"  y u < ?0 *u(y , y u ., C u& /y. "y   u& *u 4 A y& +*y "u  0 u(y = @0 &y u 0 u !y" 2$ > y& +u. "u  y& *y %"u 8 > ?u *y ,  y& u. 5 B u& 4y. '"y u  u y 0 /y(u  > @0 -u y "  y u  3 @ @ u& -y . "y  u & *u -"y A0 -y u "   u& y . *"y >u  30 'u" y * &y  u 0 u (y" < By < B u&   u& y. '"y 9 ? u& -*u "y  0 y(u *0 = D u& /y. "y u 0 !u" 0y* ? Ay ,u ,  u& y. %"y 5u B0 5u (y" .y &u  0 u(y 0 /y > A u& 2*u $"y 0   y& u. '"u 9 ? y& ,*y "u  0 u(y +, ?y "u  0 u(y 0 $y 0u < Au(yF C y A 4dp4p9dp9p@dp@pGdpGpLdpLpQdpQpPdpPP4dp4p9dp9p@dp@pGdpGpLdpLpQdpQpPdpPP4dp4p9dp9p@dp@pGdpGpLdpLpQdpQpPdpPP6Fx6x:Gx:x=Hx=xBIxBx6Jx6x:Kx:x=Lx=xBMxBx6Ox6x:Px:x=Qx=xBRxBx6Sx6x:Tx:x=Ux=xBVxBx6Wx6x:Xx:x=Yx=xBZxBx6[x6x:\x:x=^x=xB_xBx6dp6p;dp;pBdpBpIdpIpNdpNpSdpSpRdpRn/MTrk0!bd , ,F ,b@dp@pEdpEpLdpLpSdpSpXdpXp]dp]p\dSKLKp\SLP@dp@pEdpEpLdpLpSdpSpXdpXp]dp]p\dSKLKp\SLP@dp@pEdpEpLdpLpSdpSpXdpXp]dp]p\dSKLKp\SLP@dp@pEdpEpLdpLpSdpSpXdpXp]dp]p\dSKMKYdUdR\SMYUZdUd`ZU\dWd@\WWdRd@WRWdYd WY\dWd`\W\dWdP\WSdWdPSWWdYd WYSdYd`SYWdZd`WZYd\d@Y\Zd^d`Z^UdZd@UZUdYd`UYWdZd`WZYd\d`Y\Zd^d@Z^\d_d@\_Yd\d@Y\Yd\d`Y\Zd^d`Z^\dWd@\WWdRd@WRWdYd@WYZd^d@Z^\dWdP\WSdWdPSWWdYd@WYRdUd`RUUdYd`UYWdZd`WZUdYd@UYWdZd`WZUdYdBB$66BRF&::FRI'=UYI=RdWdRN)BBNRB*6 RWB6PdUdRF,: :FRI-= PUI=RdWdRN/B BNR6 B1RW6BUdYdR: F2F:R= I4I=RBN5UYBNUdYdR6B7B6R:F8F:R=I:UY=IRdWdRBN<NBR6B=RW6BRdWdR:F?F:R=I@I=RBNBNBR6BCB6R:FEF:R=IGRW=IRBNHBNR@dp@pEdpEpLdpLpSdpSpXdpXp]dp]p\dSKLKp\SLP@dp@pEdpEpLdpLpSdpSpXdpXp]dp]p\dSKLKp\SLP@dp@pEdpEpLdpLpSdpSpXdpXp]dp]p\dSKMKYdUdR\SMYUZdUd`ZU\dWd@\WWdRd@WRWdYd WY\dWd`\W\dWdP\WSdWdPSWWdYd WYSdYd`SYWdZd`WZYd\d@Y\Zd^d`Z^UdZd@UZUdYd`UYWdZd`WZYd\d`Y\Zd^d@Z^\d_d@\_Yd\d@Y\Yd\d`Y\Zd^d`Z^\dWd@\WWdRd@WRYdWd@YWZd^d@Z^\dWdP\WSdWdPSWYdWd@YWRdUd`RUUdYd`UYWdZd`WZUdYd@UYWdZd`WZUdYd@UYRdWd`RWPdUd`PURdWd`RWUdYdPUYUdYdPUYRdWd`RWRdWd RWY_^_`Y^\dWd@\WWdRd@WRYdWd YW\dWd`\W\dWdP\WSdWd`SpWWdYd WYSdYd`SYWdZd`WZYd\d@Y\Zd^d`Z^UdZd@UZUdYd`UYWdZd`WZYd\d`Y\Zd^d@Z^\d_d@\_^dad`^aZd^d`Z^Yd\d`Y\Zd_d`Z_Zd^d@Z^Yd\d@Y\WdZd@WZZd_d@Z_Zd_dPZ_\dadP\aWdZd@WZRdUd`RUUdYd`UYWdZd`WZUdYd@UYWdZd`WZUdYd@UYRdWdRNBBNRB6RWB6PdUdRF::FRI=PUI=RdWdRNBBNRB6RWB6UdYdRF::FRI==IRNBUYNBUdYdRB66BRF::FRI=UYI=UdZdRNBBNRB6UZB6UdZdRF::FRI==IRNBBNRB66BRF::FRI=UZI=RN B NBRBdpBpGdpGpNdpNpUdpUpZdpZp_dp_p^dNKUKp^NUn/MTrkP!0F J Ed4EL1d =d1, ` =1dx1 <` ;1d :` 9x1 8` 71dx1 6` 51d 4` 3x1 2`1d0 1H1 0`1d0 /` . -H1 ,`1d0 +H1 *`1d0 )` ( 'H1 &`1d0 %H1 $`1d0 #H1 "`1d0 !H1 `1d0 H1 `1d0 H1 `1d0 `  H1 `1d0 H1 `1d0 `  H1H 01d0 H1H 01d0  ` H1H 01d0 H1H 01d0 H1H `  `  `  `  0 1dt 1\ 1dt 1\ 1dt `  1\ 1dt 1\ 1dt `  1\ 1dt 1x1d `  ` 1x1d ` 1x1d `  ` 1x1d ` 1x1d ` 1x1d ` 1x1d ` !1x1d "` #1x1d $` % &` '1x1d (` )1x1d *` + ,` -1x1d .d1, /L1d 0 1` 2d1, 3L1d 4d1, 5L1d 6 7` 8d1, 9L1d :d1, ;L1d d1, ?L1d @d1, AL1d Bd1, CL1d D E` Fd1, GL1d Hd1, IL1d J K` Ld1, ML1dD N41, OL1dD P` Q R41, SL1dD T41, UL1dD V` W X41, YL1dD Z41, [L1dD \41, ]L1dD ^41, _L1dD `41, aL1dD b41, cL1dD d` e f41, gL1dD h41, iL1dD j` k l41\ m1dD n41\ o1dD p q` r41\ s1dD t41\ u1dD v w` x41\ y1dD z41\ {1dD |41\ }1dD ~41\  1d0 H1 ` 1d` 1H 01d0 ` ` ` 1 ` 1d` 1H 01d0 ` ` ` 1H 0 1d` 1H 01d0 ` ` ` 1H 0 1d` 1H 01d0 ` ` ` !1H "01d0 #0 $1H %01d0 &H1 '` (1d` )1H *01d0 +H1 ,0 -01d0 .H1 /` 01d` 1` 2` 3H1 4` 51d0 6H1 7` 81d` 9` :` ;H1 <` =1d` >1 ?` @1d` A` B` CH1 D` E1d` F1 G` H1d` I` J` KH1 L` M1d` N1H O0 P1d` Q1H R01d0 SH1 T` U1d` V1H W01d0 X0 Y1H Z01d0 [` \` ]` ^1H _01d0 `H1 a0 b01d0 c` d` e` f1H g01d0 hH1 i` j1d0 k` l` m` n1H o01d0 pH1 q` r1d` s0 t` u` v1H w01d0 xH1 y` z1d` {1 |` }1d` ~1H 0/simutrans-124.5/simutrans/music/47-Salty-Breeze.mid000066400000000000000000001561011520675460500221660ustar00rootroot00000000000000MThdMTrkCopyright (C) 2018 Salty Breeze by RykSebQB@SetupXp Salty BreezeQ X by RykSebNightNight2DayDay2Night'Night2'Day'Day2'End1End2/MTrk System Setup!=---- Reset --------------------------------------------------~ < AB@A<@---- S̐ݒ --------------------------------------------------A---- Drum lj -------------------------------------------------- AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@  AB@ @---- CH Setup --------------------------------------------------=---- Layer --------------------------------------------------K---- End of System Setup --------------------------------------------------@/MTrk3y!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  Fd[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------'-(,6438F =F!=PDD84-,,>-C8P4@1@e148-,-E,?4H1H8C?814,-eD3,B/?6K3MNBHqGBjLGo36,/J/B,73<6N936/,L@KA%GBLFKY,=KO-D L128M,D=bG3"8-,1>,0 -C174G8R8418,-,?-c 8;1.4F)418,- ,>BE /? D1@6RKG%D-C8P4@1@e,-841-E,?4H1H8C?814,-,B/?6K3M36,/J/B,73<6N936/, ,=-D128M08,-1>,0-C174G8R9481 -,!,?-c 8;1.4F)418,- ,>"/?1@6RZ3@104<3)3F4 /)/K%,O3/1,B 61F-F 4F8F]8 1 4 -,,4-18@1?4Gh,-814|-M,F1C8E4Gh-,184o,6/;6E1:_3H1*63,/|,5/H6V3C,5*24J1B-B 3/6 14*-8/M6R,=3K3 6/,p*@-D8N1Dv1-8*F/E,C1B6KA;P|6;4Rs43T|/ 13,U-(,643138F814-,,>-C8P4@1@e,-841-E,?4H1H8C?814,-,B/?6K3MDKZ?Hj?KL@@2tNKB;@Li3LZ@HBN6,/8L@ K\?IRK? /B,73<6NxLg@RA36/@L,DE87uD8,=GO;?-D128MKIQ=@;G;=I#8,-1>,0-C174G8R9481 -,!,?-c 8;1.4F)418,-,W#XBW.X,> YO/?1@6RZ]YHZX?:X:WM|X7 WDX"Sh3@104<3)3FS4\UIP8I1 4 -,,4-18@1?4Gh,-814|-M,F1C8E4Gh-,184o,6/;6E1:_3H1N`BL|BN~LP@@C@LKG?8?KELC@5s6@LSfGQ3,/|,5/H6V3C|SGaUCI5IU ,5*2POD?4J1B-B 3/6hDP$14*-S]GJ /M6R,=3KSG 3 6GE;7/,gNKB< *@-D8N1DG;GPKD*9@3=7`9*@=9?@4*9=@3=A*@ @9<=;>,<B>?B *2?B;,,4;5??BIg?B;,,,;@?;BPe,;?B;F,:?6BGi;,?Bd;1,.B9?=B9<G{=@*29@?97*9=:[* @=9 *B9H@?=8e*9@=*E9G@G=:e*9@=l9I@]=I*EpGS@j=n9G,;BBA??*?4B,;,4;JBc?G\B?;,,4;cBG?,\,;B?,7;8B9?9^,;B?w,6;W?JB]~IWB?e,;DI3=A*9@Z9Su@ 91=*%@K=?*798U*@=9==*E9D@Gf=*9@}*99A=?@Wp*9=@i9E*A@W=FQ=@9,;9?MBW *A;?B,,??BBO;]\?;B, ,CBA?C;Mh,B?;y,=B@?7;At,B?;lBS?G,?;Fy?RB,;=<*<D>@K9?1D=@*9@D=;DA9A*Ak=@9:D **:;=?9B<8Bp*;?B8z8S*P;?BK?Ks8*;B?fBM?S;**581R?;8B* 9G@@>G*=G*@>9*E9M@O>P|>@*9s*<9E>F@Zh*9>@{*D>C9M@S\*9> @yBM;c,]?cqB;,?{,I;^BI?OK;B? , :7+5AH>@LA>:+:4A8+/>:S>A+::>+9A3>7`:+A>:?A4+9>p:>A3>A+@ A:<><>-<C>@B +2@C<--4<5@?CIg@C<--,<@@;CPe-<@C>B:<H{>A+2:A?:7+9>:[+ A>: +B:HA?>8e+:A>+E:GAG>:e+:A>l:IA]>I+EpHSAj>n:H-A+9AZ:SuA :1>+%AK>?+7:8U+A>:>=+E:DAGf>+:A}+9:A>?AWp+:>Ai:E+AAW>FQ>A:-<9@MCW +A<@C--?@BCO<]\@<C- -CCA@C<+<E>AK:?1E>A+:AD>;EA:A+Ak>A::E ++:<=@9C<9Bp+<@C9z9S+PFAZh+:>A{0D>C:MAS\0:> AyAM:c0]>caA:0>-(,643138F814-,,>-C8P4@1@e,-841-E,?4H1H8C?814,-,B/?6K3M36,/J/B,73<6N936/, ,=-D128M08,-1>,0-C174G8R9481 -,!,?-c 8;1.4F)418,- ,>"/?1@6RZ3@104<3)3F4 /)/K%,O3/1,B 61F-F 4F8F]8 1 4 -,,4-18@1?4Gh,-814|-M,F1C8E4Gh-,184o,6/;6E1:_3H1*63,/|,5/H6V3C,5*24J1B-B 3/6 14*-8/M6R,=3K3 6/,p*@-D8N1Dv1-8*F/E,C1B6KA;P|6;4Rs43T|/ 13,U-(,643138F814-,,>-C8P4@1@e,-841-E,?4H1H8C?814,-,B/?6K3MDKZ?Hj?KL@@2tNKB;@Li3LZ@HBN6,/8L@ K\?IRK? /B,73<6NxLg@RA36/@L,DE87uD8,=GO;?-D128MKIQ=@;G;=I#8,-1>,0-C174G8R9481 -,!,?-c 8;1.4F)418,-,W#XBW.X,> YO/?1@6RZ]YHZX?:X:WM|X7 WDX"Sh3@104<3)3FS4\USPGE8 1 4 -,,4-18@1?4Gh418-,HSG(NO -M,F1C8E4GhN-,184LCxLFKN1,6/;6E1:iKDC DT3H1N`BL|BN~LP@@C@LKG?8?KELC@5s6@LSfGQ3,/|,5/H6V3C|SGaUCI5IU ,5*2POD?4J1B-B 3/6hDP$14*-S]GJ /M6R,=3KSG 3 6GE;7/,gNKB< *@-D8N1DG;GPKD*9@3=7`9*@=9?@4*9=@3=A*@ @9<=;>,<B>?B *2?B;,,4;5??BIg?B;,,,;@?;BPe,;?B;F,:?6BGi;,?Bd;1,.B9?=B9<G{=@*29@?97*9=:[* @=9 *B9H@?=8e*9@=*E9G@G=:e*9@=l9I@]=I*EpGS@j=n9G,;BBA??*?4B,;,4;JBc?G\B?;,,4;cBG?,\,;B?,7;8B9?9^,;B?w,6;W?JB]~IWB?e,;DI3=A*9@Z9Su@ 91=*%@K=?*798U*@=9==*E9D@Gf=*9@}*99A=?@Wp*9=@i9E*A@W=FQ=@9,;9?MBW *A;?B,,??BBO;]\?;B, ,CBA?C;Mh,B?;y,=B@?7;At,B?;lBS?G,?;Fy?RB,;=<*<D>@K9?1D=@*9@D=;DA9A*Ak=@9:D **:;=?9B<8Bp*;?B8z8S*P;?BK?Ks8*;B?fBM?S;**581R?;8B* 9G@@>G*=G*@>9*E9M@O>P|>@*9s*<9E>F@Zh*9>@{*D>C9M@S\*9> @yBM;c,]?cqB;,?{,I;^BI?OK;B? , :7+5AH>@LA>:+:4A8+/>:S>A+::>+9A3>7`:+A>:?A4+9>p:>A3>A+@ A:<><>-<C>@B +2@C<--4<5@?CIg@C<--,<@@;CPe-<@C>B:<H{>A+2:A?:7+9>:[+ A>: +B:HA?>8e+:A>+E:GAG>:e+:A>l:IA]>I+EpHSAj>n:H-A+9AZ:SuA :1>+%AK>?+7:8U+A>:>=+E:DAGf>+:A}+9:A>?AWp+:>Ai:E+AAW>FQ>A:-<9@MCW +A<@C--?@BCO<]\@<C- -CCA@C<+<E>AK:?1E>A+:AD>;EA:A+Ak>A::E ++:<=@9C<9Bp+<@C9z9S+PFAZh+:>A{0D>C:MAS\0:> AyAM:c0]>caA:0>-(,643138F814-,,>-C8P4@1@e,-841-E,?4H1H8C?814,-,B/?6K3MDKZ?Hj?KL@@2tNKB;@Li3LZ@HBN6,/8L@ K\?IRK? /B,73<6NxLg@RA36/@L,DE87uD8,=GO;?-D128MKIQ=@;G;=I#8,-1>,0-C174G8R9481 -,!,?-c 8;1.4F)418,-,W#XBW.X,> YO/?1@6RZ]YHZX?:X:WM|X7 WDX"Sh3@104<3)3FS4\UIP8I1 4 -,,4-18@1?4Gh,-814|-M,F1C8E4Gh-,184o,6/;6E1:_3H1N`BL|BN~LP@@C@LKG?8?KELC@5s6@LSfGQ3,/|,5/H6V3C|SGaUCI5IU ,5*2POD?4J1B-B 3/6hDP$14*-S]GJ /M6R,=3KSG 3 6GE;7/,gNKB< *@-D8N1DG;GPKD-C8P4@1@e148-,-E,?4H1H8C?814,-eD3,B/?6K3MNBHqGBjLGo36,/J/B,73<6N936/,L@KA%GBLFKY,=KO-D L128M,D=bG3"8-,1>,0 -C174G8R8418,-,?-c 8;1.4F)418,- ,>BE /? D1@6RKG%DGS1GA?R?C@S@N;];`IUlIrGW^GtGK2I5G/K@IL]KVLKWrKDODLIZfIDRnD}BCtBDC{;FDX;@Th@gNZdNZPObPtNZjNPG~PSZvSPZ]P/NZNiPZPgLf}ND LCNKOTK5LTALAGXmG#IR}IKO>K,DZ&DCGM1GBWB]DW,DH=R=PBfBkBK>DYBwD9=Z= DcsDAI^lI*Kh_K6LS'L KW+KG] GS]SNcN(PiPSRSPWVPlUL(UZU[EUMSS SPUZU^PK*PLS[ SbPc PqNXtNPbP/MTrk!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y # 4P[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx !dP!x-dx-x(dx(!dx!x!dx!-dx-!dx!x dP x,dx,x'dx' dx x%dx%*dx*,dx,1dx1dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx dP dp x'dx' dx x dx 'dx' dx xdpxlxp dx ,dx,x'dx' dx x dx dx x%dx%dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx !dP!x-dx-x(dx(!dx!x!dx!-dx-!dx!x dP x,dx,x'dx' dx x%dx%*dx*,dx,1dx1dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx dP dp x'dx' dx x dx 'dx' dx xdpxlxp dx ,dx,x'dx' dx x dx dx x%dx%dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx !dP!x-dx-x(dx(!dx!x!dx!-dx-!dx!x dP x,dx,x'dx' dx x%dx%*dx*,dx,1dx1dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx dP dp x'dx' dx x dx 'dx' dx xdpxlxp dx ,dx,x'dx' dx x dx dx x%dx%dpx%dx%dxx*dx*dxxdx*dx*%dx%dxx*dx*x dp x'dx' dx x,dx, dx x dx ,dx,'dx' dx x,dx,xdpx%dx%dxx*dx*dxxdx*dx*%dx%dxx*dx*x dp x'dx' dx x,dx, dx x dx ,dx,'dx' dx ,dx,%dx%,dx,dpx%dx%dxx*dx*dxxdx*dx*%dx%dxx*dx*x dp x'dx' dx x,dx, dx x dx ,dx,'dx' dx x,dx,xdpx%dx%dxx*dx* dx x,dx,'dx' dx ,dx,x dx xdpx%dx%dxx*dx*dxxdx*dx*%dx% dx ,dx,'dx' dx dpx&dx&dxx+dx+dxxdx+dx+&dx&dxx+dx+x!dp!x(dx(!dx!x-dx-!dx!x!dx!-dx-(dx(!dx!x-dx-xdpx&dx&dxx+dx+dxxdx+dx+&dx&dxx+dx+x!dp!x(dx(!dx!x-dx-!dx!x!dx!-dx-(dx(!dx!-dx-&dx&-dx-dpx&dx&dxx+dx+dxxdx+dx+&dx&dxx+dx+x!dp!x(dx(!dx!x-dx-!dx!x!dx!-dx-(dx(!dx!x-dx-xdpx&dx&dxx+dx+!dx!x-dx-(dx(!dx!-dx-x!dx!xdpx&dx&dxx+dx+dxxdx+dx+&dx&!dx!-dx-(dx(!dx!dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx !dP!x-dx-x(dx(!dx!x!dx!-dx-!dx!x dP x,dx,x'dx' dx x%dx%*dx*,dx,1dx1dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx dP dp x'dx' dx x dx 'dx' dx xdpxlxp dx ,dx,x'dx' dx x dx dx x%dx%dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx !dP!x-dx-x(dx(!dx!x!dx!-dx-!dx!x dP x,dx,x'dx' dx x%dx%*dx*,dx,1dx1dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx dP dp x'dx' dx x dx 'dx' dx xdpxlxp dx ,dx,x'dx' dx x dx dx x%dx%dpx%dx%dxx*dx*dxxdx*dx*%dx%dxx*dx*x dp x'dx' dx x,dx, dx x dx ,dx,'dx' dx x,dx,xdpx%dx%dxx*dx*dxxdx*dx*%dx%dxx*dx*x dp x'dx' dx x,dx, dx x dx ,dx,'dx' dx ,dx,%dx%,dx,dpx%dx%dxx*dx*dxxdx*dx*%dx%dxx*dx*x dp x'dx' dx x,dx, dx x dx ,dx,'dx' dx x,dx,xdpx%dx%dxx*dx* dx x,dx,'dx' dx ,dx,x dx xdpx%dx%dxx*dx*dxxdx*dx*%dx% dx ,dx,'dx' dx dpx&dx&dxx+dx+dxxdx+dx+&dx&dxx+dx+x!dp!x(dx(!dx!x-dx-!dx!x!dx!-dx-(dx(!dx!x-dx-xdpx&dx&dxx+dx+dxxdx+dx+&dx&dxx+dx+x!dp!x(dx(!dx!x-dx-!dx!x!dx!-dx-(dx(!dx!-dx-&dx&-dx-dpx&dx&dxx+dx+dxxdx+dx+&dx&dxx+dx+x!dp!x(dx(!dx!x-dx-!dx!x!dx!-dx-(dx(!dx!x-dx-xdpx&dx&dxx+dx+!dx!x-dx-(dx(!dx!-dx-x!dx!xdpx&dx&dxx+dx+dxxdx+dx+&dx&!dx!-dx-(dx(!dx!dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx !dP!x-dx-x(dx(!dx!x!dx!-dx-!dx!x dP x,dx,x'dx' dx x%dx%*dx*,dx,1dx1dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx dP dp x'dx' dx x dx 'dx' dx xdpxlxp dx ,dx,x'dx' dx x dx dx x%dx%dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx !dP!x-dx-x(dx(!dx!x!dx!-dx-!dx!x dP x,dx,x'dx' dx x%dx%*dx*,dx,1dx1dPx*dx*x%dx%dxxdx*dx*dxx dP x,dx,x'dx' dx x dx ,dx,x dx dP dp x'dx' dx x dx 'dx' dx xdpxlxp dx ,dx,x'dx' dx x dx dx x dx %dH%v/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y P @F[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------\/MTrk!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y 0 @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------_d %x &x (x )x +x ,x -x /x 0x 2x 3x 5x 6x 7x 9x # :x x @x Ax Cx Dx Fx Gx Hx Jx Kx Mx Nx Px Qx Rx Tx Ux Wx Xx Zx [x \x ^x _x ax bx dx ex fx hx ix kx lx nx ox px rx sx ux vx xx yx zx |x }x __v  ~P }( |P {( z( y( w( v( u( s( q( p( n( l( j( g( e( c( `( ^( [( X( U( R( O( L( H( E( A( >( :( 6( 2( .( )( %( !( ( ( ( ( (  _ \   ________````_ \ __v  _________````___b _ _h ~( } | {@ z@ y xp wp vp uH tp sH r qH p oH n m l k jx i h gx fx e dx cx b ax `P__( _x ^x ]x \x [P Zx Yx Xx WP Vx Ux TP Sx Rx QP Px OP NP Mx LP Kx JP IP Hx GP FP EP Dx CP BP AP @P ?x >P =P D>~CI:CA?F@NA$@-Vr>_COCmATGA*CTCZ@L:@?_>^DJEbD,E'CVBC%AK/ARCTClUJ>=9f'9Kl>]>^>@Cf6CBC^YC&E^hE _>^DJEbD,E'CVBC%AK/ARCTClUJ>=9f'9Kl>]>^>@Cf6CBC^YC&E^hE /MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------\/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------\/MTrkR!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------,UP, 'dP' ,UP,,UP, 'dP' ,UP,,UP, 'dP' ,UP,,UP, 'dP' ,UP,,UP, 'dP' ,UP,,UP, 'dP' ,UP, &dP& ,UP,(&@P&(&d'dP&' ,UP,(&dP& &dP&(,UP,(&dP&($t'd3dP$'3 &d.MP&. $t*_1dP$*1 .MP. $t'd&d,U ,F$'&(*_P*(.MP. $t*_P$* .MP. $t&d,U ,F$&(*_'dP*'(.MP. $t*_P$* .MP. $t'd&d,U ,F$'&(*_P*(.MP. $t*_P$* .MP. $t&d,U ,F$&(*_'dP*'(.MP. $t*_P$* .MP. $t'd&d,U ,F$'&(*_P*(.MP. $t*_P$* .MP. $t&d,U ,F$&(*_'dP*'(#iP#(,UP,($t*_3dP$*3@FP@ Fd?FPF?(@F3d$tP@3$(FdPF@F'dP@'($t&d,U3d ,F$&3(Fd?F*_'dPF?*'(@F#iP@#(Fd@FPF@($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.M#iP.#(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.M#iP@.#(Fd@FKFPF@K($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U8M ,F$'&8(Fd?F*_PF?*(@F.M2FP@.2(Fd#i2F'dPF#2'($t*_P$*(KF/F'dPK/'(.M'dP.'(@F'd#i-FP@'#-($t&d,U3d ,F$&3(?F*_'dP?*'(@F#iP@#(KF'dPK'($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.M#iP.#(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.M#iP@.#(Fd@FKFPF@K($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U8M ,F$'&8(Fd?F*_PF?*(@F.M2FP@.2(Fd#i2F'dPF#2'($t*_P$*(KF/F'dPK/'(.M'dP.'(@F'd#i-FP@'#-($t&d,U3d ,F$&3(?F*_'dP?*'(@F#iP@#(KF'dPK'($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.M#iP.#(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.M#iP@.#(Fd@FKFPF@K($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U8M ,F$'&8(Fd?F*_PF?*(@F.M2FP@.2(Fd#i2F'dPF#2'($t*_P$*(KF/F'dPK/'(.M'dP.'(@F'd#i-FP@'#-($t&d,U3d ,F$&3(?F*_'dP?*'(@F#iP@#(KF'dPK'($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.M#iP.#(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.M#iP@.#(Fd@FKFPF@K($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U8M ,F$'&8(Fd?F*_PF?*(@F.M2FP@.2(Fd#i2F'dPF#2'($t*_P$*(KF/F'dPK/'(.M'dP.'(@F'd#i-FP@'#-($t&d,U3d ,F$&3(?F*_'dP?*'(@F#iP@#(KF'dPK'($t*_1dP$*1 .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@F'dP@'($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t&d,U ,F$&(Fd?F*_'dPF?*'(@F.MP@.(Fd@FKFPF@K($t*_P$* .MP.(@FKFP@K($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdPF($t*_P$*(KFPK(.MP.(@FP@($t'd&d,U ,F$'&(Fd?F*_PF?*(@F.MP@.(FdKFPFK($t*_P$* .MP.(@FKFP@K('d&d,U ,F'&(Fd?F*_PF?*(@F3d#iP@3#(Fd&BPF&(*_&dP*&(KFPK(#i&BP#&(@F'dP@'(&dP&(Fd?F'd&dPF?'&(@F#i&dP@#&(Fd@FKF#iPF@K#(1d$tP1$ ,UP,(@FKFP@K('dP'(Fd?FPF?(@F,UP@,(FdPF KFPK(,UP,(@FP@('dP'(Fd?FPF?(@F,UP@,(FdKFPFK,UP,(@FKFP@K('dP'(Fd?FPF?(@F,UP@,(FdPF KFPK(,UP,(@FP@('dP'(Fd?FPF?(@F,UP@,(Fd@FKFPF@K,UP,(@FKFP@K('dP'(Fd?FPF?(@F,UP@,(FdPF KFPK(,UP,(@FP@('dP'(Fd?FPF?(@F,UP@,(FdKFPFK,UP,(@FKFP@K('dP'(Fd?FPF?(@F,UP@,(FdPF KFPK(,UP,(@FP@('dP'(Fd?FPF?(@F,UP@,(Fd@FKFPF@K(3dP3 ,UP,(@FKFP@K('dP'(?FP?(@F,UP@,KFPK(,UP,(@FP@('dP'(?FP?(@F,UP@,(KFPK,UP,(@FKFP@K('dP'(?FP?(@F,UP@,KFPK(,UP,(@FP@('dP'(?FP?(@F,UP@,(@FKFP@K,UP,(KFPK('dP' ,UP,KFPK(,UP, 'dP' ,UP,(KFPK,UP,(KFPK('dP' ,UP,KFPK'dP' 'd,UP',(KF#iPK#($t1dP$1$P$/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------\/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------\/MTrk!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y X j2[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`SdxSpSWxS`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT`TdxTpTWxT./MTrk !D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  d[ ed@&ed@ed @edG---- End of CH Setup -------------------------------------------------- =8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$>8<>,>-<><>=<>,>3<>4>?<>4>0<><>?<>$>8<>,>-<><>=<>,>3<>4>?<>4>0<><>?<>$>8<>,>-<><>=<>,>3<>4>?<>4>0<><>?<>$>8<>,>-<><>=<>,>3<>4>?<>4>0<><>?<>$>8<>,>-<><>=<>,>3<>4>?<>4>0<><>?<>$>8<>,>-<><>=<>,>3<>4>?<>4>0<><>?<>$>8<>,>-<><>=<>,>3<>4>?<>4>0<><>?<>$>8<>,>-<><>=<>,>3<>4>?<>4>0<><>?<>$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=,=-<=<==<=,=3<=4=?<=4=0<=<=?<=$=8<=, y=-<=< x==<=, u=3<=4 s=?<=4 q=0<=< p=?<=$ m=8<=, j=-<=< i==<=, f=3<=4 d=?<=4 b=0<=< a=?<=$ ]=8<=, Z=-<=< Y==<=, V=3<=4 T=?<=4 R=0<=< Q=?<=$ N=8<=, K=-<=< J==<=, G=3<=4 E=?<=4 C=0<=< B=?<=$ >=8<=, ;=-<=< :==<=, 7=3<=4 5=?<=4 3=0<=< 2=?<=$ /=8<=, ,=-<=< +==<=, (=3<=4 &=?<=4 $=0<=< #=?<=$ =8<=, =-<=< ==<=, =3<=4 =?<=4 =0<=< =?<=$ =8<=, =-<=< ==<=, =3<=4 =?<=4 =0<=< =?<=b/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------\/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------\/simutrans-124.5/simutrans/music/48-Techno-movement.mid000066400000000000000000002074211520675460500227330ustar00rootroot00000000000000MThdMTrk"Copyright (C) 2018---Technological Movement---(C) 2018 RykSebCan be used anyways if you givethe credit properly.QB@SetupXpQ6Technological MovementX by RykSebA2A3BB2CC2DD2EE2/MTrk System Setup!=---- Reset --------------------------------------------------~ < AB@A<@---- S̐ݒ --------------------------------------------------A---- Drum lj -------------------------------------------------- AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@  AB@ @---- CH Setup --------------------------------------------------=---- Layer --------------------------------------------------K---- End of System Setup --------------------------------------------------/MTrk{Keys!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  *d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------.4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7ES@F9D>0<9@E>U4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7ES@F9D>0<9@E>,4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7ES@F9D>0<9@E>,4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7@SS9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7@SS9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I>J4?C;>%4/4K;A>G\;>44C;D>]#; > 4 ES@F9D>0<9@E>U4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?>JQ;>4/4K;A>G\;>44C;D>]#; > 4 ES@F9D>0<9@E>U4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I>J4?C;>%4/4K;A>G\;>44C;D>]#; > 4 ES@F9D>0<9@E>U4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I>J4?C;>%4/4K;A>G\;>44C;D>]#; > 4 ES@F9D>0<9@E>U4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I>J4?C;>%4/4K;A>G\;>44C;D>]#; > 4 ES@F9D>0<9@E>U4/5;@N9H(95.4@94<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I4?78>JQ;>47/4K7S;A>G\;>474C;D7G>]#; > 4 7CS@F9D<0<9@C<$4/5;@N9H(95.4@C4<9N@PF9@44J@L9O5Q\95@4>S9L5J4Ag594>;I>J4?C;>%4/4K;A>G\;>44C;D>]#; > 4 ES@F9D>0<9@E>/MTrk Bell!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y X Td[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------TdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpVdpVpTdpTpVdpVpXdpXpTdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpSdpSpQdpQpSdpSUdpUXdpX]dp]TdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpVdpVpTdpTpVdpVpXdpXpTdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpSdpSpQdpQpSdpSUdpUXdpX]dp]TdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpVdpVpTdpTpVdpVpXdpXpTdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpSdpSpQdpQpSdpSUdpUXdpX]dp]TdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpVdpVpTdpTpVdpVpXdpXpTdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpSdpSpQdpQpSdpSUdpUXdpX]dp]TdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpVdpVpTdpTpVdpVpXdpXpTdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpSdpSpQdpQpSdpSUdpUXdpX]dp]TdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpVdpVpTdpTpVdpVpXdpXpTdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpSdpSpQdpQpSdpSUdpUXdpX]dp]TdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpVdpVpTdpTpVdpVpXdpXpTdpTpSdpSpLdpLpJdpJpHdpHpEdpE`HdpHJdpJLdpLTdpTpSdpSpJdpJpLdpLpSdpSpQdpQpSdpSUdpUXdpX]dp]/MTrk .Pad!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y 2 <2[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------& 8xLdXdx 9P :$ ;, p ?4 @x A4 B4 Cx Dx Ex Fx Gx Hx Ix JxJV KOd[dx Lx M< Nx Ox P< Qx R< Sx T< Ux V< Wx X< Y< Zx [< \< ]x ^< _< `4 =p <4 ;4 :p 94 84 7p 6 7< 6dLXJdVd 5x 44 3x 2x 14 0x /x .x -4 ,x +x *4 )x (Z * JVOd[dn +x ,x -x .x /x 0< 1x 2x 3x 4x 5x 6x 7x 8x 9x :FO[Qd]d2 ;x x ?< @x Ax Bx Cx Dx Ex Fx Gx Hx Ix Jx K Q]F L/MTrkLead!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y P Ld[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------#@XLX+@LO@ZLZD@L@VLV9@L>@]L]S@L,CQOQCCO/>UJU4>JA@YLYR@L>ZJZ'>J.>XJXV>J>eJe->JA>QJQf>J@WLWQ@L(>bJbE>J,]J]E>J8@eLe@L]HVTV/HTDG_S_2GSAHbTb.HTHGcSc~GSkC`O`?CO5>cJc>JXCfOf CO\@]L]v@Lj@XLX$@LE@]L]b@L7]C]97C?9WEW19E5COOOACO0>WJW@>J;@]L]3@LD]J]3>JD>ZJZ?>J4@XLX>@L/>]J]4>J87[C[-7CO;cGc7;GB]J]9>J3MPAP1MAB@ZLZ?@L1GZSZ?GS8JWVW9JV8HZTZ?HT8G_S_1GS8CZOZMCO#JXVXiJVOe[eiO[CZOZICO/@ZLZI@L/LZXZiLXJ]V]xIZUZJV3IU'GeSeGGS)EbQb6EQHJZVZ;JV/IOUO=IU2J`V`DJV@LcXc&LXTEZQZEQP$]dQd!X*(3(M8(<(}=(?(C?(]?(e?(@W]QOKUUUUUUUUH>,&2<>2<@4<@4<<0<<0/MTrkELead2!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y d 1d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------Ld`LPEdpEJdpJLdpLpJdpJpEdpEpGdpGHd`HGdpGpCdpCp@dp@p>dp>@dp@0EdpEGdpGHdpHLd`LPEdpEJdpJLdpLpOdpOpJdpJpCdpCpQdpQOdpOCdpCpMdpMLdpLCdpCGdpGHdpHPTdpTSdpSpOdpOJdpJLd`LPEdpEJdpJLdpLpJdpJpEdpEpGdpGHd`HGdpGpCdpCp@dp@p>dp>@dp@0EdpEGdpGHdpHLd`LPEdpELdpLOdpOpMdpMpLdpLpJdpJSdpSTdpTOdpOCdpCQdpQEdpESdpSGdpGTdpTHdpHVdpVJdpJTdpTHdxHVdpVJdxJXdpX`[d[0QdpQSdpSTdpT[d`[]d`]Xd`X[dp[xTdhT0QdxQSdxSTdxTVdxVXd`XVd`VOd`OpXdpXVd`VOd`O`QdxQSdxSTdxT]dx][d`[Vd`VSd`Sp[dp[Vd`VUd`UVd`VXd`XTdpTPOdpOTdpT]dp][d@[pOdpOTdpT]dp][d`[]d`]Xd`X[dp[pSdxSTdhT8OdxOQdxQSdxSTdxTVdxVXd`XVd`VOd`OpXdpXVd`VOd`O`QdxQSdxSTdxT]dx][d`[Vd`VSd`S[d`[]d`]P/MTrkgGt!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  Yd[ ed@&ed@ed @edG---- End of CH Setup ---------------------------------------------------7x-48x4x9Bx9xdx>dx>=Yx=7Jx7x9Ux9x=Rx=9Mx97Ax79Ux97Gx74dx>dx>=Yx=7Jx7x9Ux9x=Rx=9Mx97Ax79Ux97Gx74dx>dx>\`>dx>dx>=Yx=7Jx7x9Ux9x=Rx=9Mx97Ax79Ux97Gx74dx>dx>=Yx=7Jx7x9Ux9x=Rx=9Mx97Ax79Ux97Gx74dx>dx>=Yx=7Jx7x9Ux9x=Rx=9Mx97Ax79Ux97Gx74dx>dx>=Yx=7Jx7x9Ux9x=Rx=9Mx97Ax79Ux97Gx74t))G)`$:x(O$2(Y(Zi( (P((E!GA!W-t))G)`$:x(O$2(Y(Zi( (P((E!GA!W-!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------&/MTrkDrumL!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------F*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPF(1dP1 F*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPF(1dP1 F*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPF(1dP1 F*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPF(1dP1 F*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFFCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFFCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPF(1dP1F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFFCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFFCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPF(1dP101dP101dP101dP101dP1p1dP1p1dP11dP11dP11dP11dP1 F*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPF(1dP1 F*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPFF*PF(FHPF F@PF(F#PF(FHPFF*PF(FAPF F)PF FCPFF$PF(FAPF FCPF(F%PF(FFPF F@PF FLPF F\PF(F6PF(FHPF(1KP1/MTrkDrumR!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  Xd[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------*9P**,P**@P**0P**9P**,P**@P**0P**9P**,P**@P**0P**9P**,P**@P**0P* 7dP7 .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP.*9P**,P**@P**0P**9P**,P**@P**0P**9P**,P**@P**0P**9P**,P**@P**0P**9P**,P**@P**0P**9P**,P**@P**0P**9P**,P**@P**0P**9P**,P**@P*7dP7 .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP. 7dP7 .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP. 7dP7 *ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP* 7dP7 *ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP**ZP* 7dP7 .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P*(*CP*(.QP. *9P* .QP. *,P* .QP. *@P* .QP. *0P* .QP. *9P* .QP. *,P* .QP.(*@P*(*@P* .QP.*2P**IP**7P**AP**2P**IP**7P**AP**2P**IP**7P**AP**2P**IP**7P**IP* 7KP7/simutrans-124.5/simutrans/music/49-Last-Sunday.mid000066400000000000000000001555711520675460500220400ustar00rootroot00000000000000MThdMTrkCopyright (C) 2018---------Last Sunday---(C) 2018 RykSebCan be used anyways if you givethe credit properly.------QB@SetupXpQ Last SundayX by RykSebBB2CC2DD2EE2F/MTrk System Setup!=---- Reset --------------------------------------------------~ < AB@A<@---- S̐ݒ --------------------------------------------------A---- Drum lj -------------------------------------------------- AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@  AB@ @---- CH Setup --------------------------------------------------=---- Layer --------------------------------------------------K---- End of System Setup --------------------------------------------------/MTrk*EP!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  Ed[ ed@&ed@ed @edG---- End of CH Setup -------------------------------------------------- +=5C:P|:5+Y5B+Z:]x:5+n+O5C:]q+5:5>+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^-O+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^2OAW6Gc6A2rAS6D2]Z62A AM2S6FtA26|6FAP2P~6A2Q5F2f?fz5v5Q=d?2=5+=7C>Pr= >7+Y5B+Z:]x:5+n+O5C:]q+5:5>+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^-O+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^2OAW6Gc6A2rAS6D2]Z62A AM2S6FtA26|6FAP2P~6A2Q5F2f?fz5v5Q=d?2=5+=7C>Pr= >7+Y5B+Z:]x:5+n+O5C:]q+5:5>+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^-O+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^2OAW6Gc6A2rAS6D2]Z62A AM2S6FtA26|6FAP2P~6A2Q5F2f?fz5v5Q=d?2=5+B>M5Kq=O>5++I>F5D\+>5{+\>J5H<+>5!+D>D5835>+#+]>N5Ky>5+P)a>N5K>)5I>Q5I)YA> 5))AK5N)p_A5 )y5KAF)Z:)A5 AF5J)ak9LA{5)B95N>L'Z+>' 517O>P'QM>'7'N7H>@;7'>,><'Q7M87>' 'j7Q>Od>7%'V>M9M&m)> &9*&A>H9@&>9F&K >L9Q>& >9%&d>K9=Z>9&^&M>O9Wp:Z#>a9p3N>?$S&:S>$3D$U>J3KG>3 $3O$Y>L6> 3 $'$P>J3M=>3$}3\>N$ab3>w>25U&U%$h>&5:>85O&Fq5> &q&O>C5C<& 5>5:>@&M>5&T5K>E&i5>8>@'37E&> 7'6>K7>'SM7>')7C']>Wm>7'x7A>F'Sg>7'X7F>E'WY>7'7>C8>E'RC '> 707:'RC9>=iC>7'S9:>@C@ )TxC>9)@9OCH>V)\mC 9>)b)TCL9T>k!9C)>5$BFM>K@F>$$IFF>D\$F>{$\FJ>H<$F>!$DFD>83>F$#$]FN>KyF>$P&aENM9M)m)> )9*)A>H9@)>9F)K >L9Q>) >9%>K)d9=Z>9)^)M>O9W:Z>:N9p3N>?$S)k>$3D$U>J3KG>3 $3O$Y>L6> 3 $'$P>J3M=>3$}3\>N$ab3>w>25U&U%$h>&5:>85O&Fq5> &q&O>C5C<& 5>5:>@&M>5&T5K>E&i5>8>@'37E&> 7'6>K7>'SM7>')7C']>Wm>7'x7A>F'Sg>7'X7F>E'WY>7'7>C8>E'RC '> 707:'RC9>=iC>7'S9:>@C@ )TxC>9)@9OCH>V)\mC 9>)b)TCL9T>k!9C)>1+=5C}5+EI9J`5B+ZIDJ"5+_FY+O5Cr5+5>+;n5 +g5@+SgF&5CO +MC -E7FiH[D7-BH\H`-h7GHH7-FS/F?HN-S7Kq7-u7D-Zf7-HYIk7O-hI HMz7-FI'H9F.WFF-9.XIE .]9GJXIAJ9.jFh9G.]u9.o9G.f9.BCPF-C`O_ 9D.ZOYQs9.~Q`-ORk7GXR 7-Tq)T;Ob7D-]|OMbG7-RM!O\7F-Sx-7|7F-P~7 -Q5F2f:fz5v7S:2O>75dz+=`=f5+\>E=E>!5B+ZjAh5+=A1+O5C`=h+5b>E =5>+;J>$5 +UAb5@+S Ax=n 5 +Z>R=gCq-E7F>C\Ch@7-=CmCy-h7GZA@C4A17-Cy -S7KmC-7u7DFy-Zf7-F|Hq`7O-h5HVF|rF 7-ZH\9F.WHIh[IJkX9.c.]9G\9.9G.]u9.JJ%9G.frJP9.>J$9D.ZM`rMJ[m9.J MwM#2O6GOu2O Rw$62RT`]6D2]Z62 2S6Fx62}2P 2%ST9Q> SOP Q MO O LGMJGL5F2fHE+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^-O+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^2OAW6Gc6A2rAS6D2]Z62A AM2S6FtA26|6FAP2P~6A2Q5F2f?fz5v5Q=d?2=5+B>M5Kq=O>5++I>F5D\+>5{+\>J5H<+>5!+D>D5835>+#+]>N5Ky>5+P)a>N5K>)5I>Q5I)YA> 5))AK5N)p_A5 )y5KAF)Z:)A5 AF5J)ak9LA{5)B95N>L'Z+>' 517O>P'QM>'7'N7H>@;7'>,><'Q7M87>' 'j7Q>Od>7%'V>M9M&m)> &9*&A>H9@&>9F&K >L9Q>& >9%&d>K9=Z>9&^&M>O9Wp:Z#>a9p3N>?$S&:S>$3D$U>J3KG>3 $3O$Y>L6> 3 $'$P>J3M=>3$}3\>N$ab3>w>25U&U%$h>&5:>85O&Fq5> &q&O>C5C<& 5>5:>@&M>5&T5K>E&i5>8>@'37E&> 7'6>K7>'SM7>')7C']>Wm>7'x7A>F'Sg>7'X7F>E'WY>7'7>C8>E'RC '> 707:'RC9>=iC>7'S9:>@C@ )TxC>9)@9OCH>V)\mC 9>)b)TCL9T>k!9C)>5$BFM>K@F>$$IFF>D\$F>{$\FJ>H<$F>!$DFD>83>F$#$]FN>KyF>$P&aENM9M)m)> )9*)A>H9@)>9F)K >L9Q>) >9%>K)d9=Z>9)^)M>O9W:Z>:N9p3N>?$S)k>$3D$U>J3KG>3 $3O$Y>L6> 3 $'$P>J3M=>3$}3\>N$ab3>w>25U&U%$h>&5:>85O&Fq5> &q&O>C5C<& 5>5:>@&M>5&T5K>E&i5>8>@'37E&> 7'6>K7>'SM7>')7C']>Wm>7'x7A>F'Sg>7'X7F>E'WY>7'7>C8>E'RC '> 707:'RC9>=iC>7'S9:>@C@ )TxC>9)@9OCH>V)\mC 9>)b)TCL9T>k!9C)>1+=5C:P|:5+Y5B+Z:]x:5+n+O5C:]q+5:5>+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^-O+;:]m:5 +g5@+S:R :5 +Y-E7FR9F.Ws9>.c.]9G>W\>9.9G>O.]u9>.o9G>P.f9>.b9D>`.Z >9.^2OAW6Gc6A2rAS6D2]Z62A AM2S6FtA26|6FAP2P~6A2Q5F2f?fz5v5Q=d?2R5p=h+d2d5d>dL25<+Z>/MTrkLead!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y E Kd[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------p>KE>3ER}ECRLC#>I#>GAD>RA~>]K >>>Mv>wDBEPDhCJEC:>M>HAR>DA>dW =A7F>X7 >S|>/E`ECSC>L->MAP\>\A`>7M*>d>M>PD;EWD]CRE1C>MG>,AM;A<>N>-AUtA2BK]B6CU9C>G>K<n>^>KE>3ER}ECRLC#>I#>GAD>RA~>]K >>>Mv>wDBEPDhCJEC:>M>HAR>DA>dW =A7F>X7 >S|>/E`ECSC>L->MAP\>\A`>7M*>d>M>PD;EWD]CRE1C>MG>,AM;A<>N>-AUtA2BK]B6CU9C>G>K<n>^>KE>3ER}ECRLC#>I#>GAD>RA~>]K >>>Mv>wDBEPDhCJEC:>M>HAR>DA>dW =A7F>X7 >S|>/E`ECSC>L->MAP\>\A`>7M*>d>M>PD;EWD]CRE1C>MG>,AM;A<>N>-AUtA2BK]B6CU9C>G>K<n> /MTrk#Lead2!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y ; /x[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------HW|HwH]uH[F]*FrCPAC+AF@AMH]?HgHSHcFO'FCW:C:I.JRIJIMTMNJRJ(HGuH:IO{I(JJJ%CFvC&FVsF8ALA:CSCH>De>$AOJAB:FBCGhC>WU>KCCzC&HOpH4JP]J9EMEFN5ESF~EAOA)BMuBCPsC#>Fv>&AR6A>Es>oHMeJ<HLJMdHMHVJI HAJMdjMHZuJKHKJMh/M2HTpJJ HEJ*M`MuRf/RHRvRPQr QiQfsQXRluRMfeM7Oo6OU>+7hz7Ra> fr>a7fJ7-S>I<`|<3>Sk>4]>gH_zJI HOJ"MR_M_HZJRH9J4MZ!MZHf~JGHoJM`XMHWxJ]HFJMh[MoRi?RQRr1RJQo QpQoaQ>RnR4T]`T8Mo'MZOfNO8Jl=J MlwHi!MRHoJr:J'Fh#FSHoGH< A< D< G< J< M< P< S< V< Y< \< _< b< e< g< j< m< p< s< v< y< |(Qd]< RVQ]Od[<@O[Vdb<VbRd^<R^Qd]<Q]Od[< A< D< G< J< M< P< S< V< Y< \< _< b< e< g< j< m< p< s< v< y< |(Qd]< RVQ]Od[<@O[Vdb<VbRd^<R^Qd]<Q]Od[|8>2*: @,>p5Px57Px7P5Px57Px7F5Px57Px7Z5Px57Px7P5Px57Px7P5Px57Px7F5Px57Px7Z:Px:7Px7n4Px47Px7r5Px57Px7P5Px57Px7F5Px57Px7Z5Px57Px7P5Px57Px7P5Px57Px7F5Px57Px7Z:Px:7Px7n4Px47Px7r5Px57Px7P5Px57Px7F5Px57Px7Z5Px57Px7P5Px57Px7P5Px57Px7F5Px57Px7r>dx>:cx:x2*: @,>/MTrk Atmosphere!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y c @([ ed@&ed@ed @edG---- End of CH Setup -------------------------------------------------- Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77FG:19F:?Fd:F9F:7 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77FG:19F:?Fd:F9F:7 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77FG:19F:?Fd:F9F:7 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77FG:19F:?Fd:F9F:7 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77FG:19F:?Fd:F9F:7 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 Cd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77CG719C7?Cd7F9C77 hCd7F9C77CE709C7?CF719C7?Cd7F9C77CG719C7?CG719C7?Cd7F9C77CE709C7?Cd7F9C77FG:19F:?Fd:F9F:7 #7Fx7>Fx>AFxAFFxFJFxJMFxMTFxTVFxV7Fx7>Fx>AFxAFFxFJFxJMFxMTFxTVFxV5Fx5Fx>CFxCFFxFJFxJOFxORFxR3Fx3:Fx:>Fx>CFxCFFxFJFxJOFxORFxR2Fx29Fx9>Fx>EFxEHFxHOFxOQFxQVFxV2Fx29Fx9>Fx>EFxEHFxHOFxORFxROFxOWdxWV^xVR\xROZxOVXxVRVxROUxOJSxJRQxROOxOJMxJHKxHOIxOJGxJHExHCCxCY_xYT]xTQ[xQMYxMTWxTQUxQMTxMHRxHQPxQMNxMHLxHEJxEMHxMHFxHEDxEABxA?Ex?CGxCFIxFJJxJOLxOJNxJOPxORRxRVTxV[Vx[VXxVRWxROUxOJTxJFRxFCQxC?Ox?CQxCFSxFJUxJOVxOJXxJOZxOR[xRY]xY[_x[TaxTV^xVRYxROTxOJOxJCJxC^]x^[[x[WYxWVWxVRUxROSxOKQxKJOxJ[Mx[VXxVTWxTRUxROSxOJRxJHPxHFNxFY\xYVZxVTXxTQUxQMSxMJQxJHOxHEMxEV]xVT[xTQYxQMWxMJUxJHSxHEQxEAOxA>Ox>?Qx?CSxCFUxFJWxJKYxKM[xMR]xRY_xYR[xRMYxMKVxKJTxJFRxFCPxC>Nx>Lx>ANxAEQxEHSxHJVxJMXxMQZxQV\xVQYxQMVxMJOxJHSxHEWxEA[xA>_x>WdxWV^xVR\xROZxOVXxVRVxROUxOJSxJRQxROOxOJMxJHKxHOIxOJGxJHExHCCxCY_xYT]xTQ[xQMYxMTWxTQUxQMTxMHRxHQPxQMNxMHLxHEJxEMHxMHFxHEDxEABxA?Ex?CGxCFIxFJJxJOLxOJNxJOPxORRxRVTxV[Vx[VXxVRWxROUxOJTxJFRxFCQxC?Ox?CQxCFSxFJUxJOVxOJXxJOZxOR[xRV]xVY_xY[ax[^ex^`ax`^]x^[Zx[YVxYVSxVTPxTRMxROJxOJHxJHDxHF@xFC4x><0x<7+x7/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk>!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y P @F[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk<!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk<!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/MTrk<!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @d[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------/simutrans-124.5/simutrans/music/50-Snowy-Road.mid000066400000000000000000001314331520675460500216550ustar00rootroot00000000000000MThd MTrk Snowy Road#Copyright (C) 2018 AnoKTOK / RykSebyTCgz URLQB@SetupXpQ/AXA2 Q0Q7`Q'PBQ/B2CC2D`Q QRPQ 5PQ 5Q!莃`Q/EE2FF2F3F4/MTrk System Setup!=---- Reset --------------------------------------------------~ < AB@A<@---- S̐ݒ --------------------------------------------------A---- Drum lj -------------------------------------------------- AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@  AB@ @---- CH Setup --------------------------------------------------=---- Layer --------------------------------------------------K---- End of System Setup --------------------------------------------------/MTrk Ice Rain!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y ` @d[ edC&ed@ed @edG---- End of CH Setup --------------------------------------------------`Ax`eAxe`A`^@x^c@xc^@^\@x\a?xa\?\[?x[`?x`[?[`dx`x^dx^xWdxWx`dx`^dx^xWdxWhVdxVWdxW`dx`^dx^xWdxWxRdxRxRdxRYdxYxWdxWxVdxVWdxWxRdxRx`dx`x^dx^xWdxWx`dx`^dx^xWdxWhVdxVWdxWRdxRYdxYxRdxRYdxYxRdxRYdxYWdxWVdxVWdxWxRdxRxMdxMOdxORdxRWdxWxVdxVxOdxOxWdxWVdxVxOdxO`MdxMOdxOKdxKhTdxT[dx[TdxTRdxRxTdxTxOdxORdxRTdxTOdxORdxRMdxMxOdxOxHdxHxMdxMOdxOxHdxHxMdxMOdxOMdxMHdxHxCdxCHdxHJdxJMdxMOdxOTdxTVdxVYdxY[dx[^dx^[dx[YdxYWdxWRdxRMdxMHdxH6 >` M` Z` e` l` n` k` d` Y` Lj =V .` !` ` ` ` ` ` $` 2` A` O` \` f` l` n` j` c` W` J` ;` ,` ` ` ` ` ` ` &` 4B >( >` M` Z` e` l` n` k` d0[!0 YH[[5 L`[ =[N` .[H !0YN0 HY ` WN` WH 0YNg0 $HYgRNg' 2`g Ag:` ORgH \0e:0 fHe l` nc:` jcH c0e:0 WHe^: J` ;` ,^H ` ` ` ` ` ` &` 4B > >` M` Z` e` l` n` k` d0V!0 YHVV5 L`V =VN` .VH !0WN0 HW ` VN` VH 0bRN0 $HbRMNb' 2`b Ab:` OMbH \0c:0 fHc l` nb:` jbH c0^:0 WH^Y: J` ;[N` ,YH 0[0 ` ` ` ` ` &` 4B > >TNg:` M` Z0Tg0 e` l` n` k` d` Y` L` = .YN`:` !YH 0`WN0 HW ` VN` VH $0WN0 2HWRN A` Oe:` \ReH f0c:0 lHc n` jb:` cbH W0c:0 JHc^: ;` ,` ^H 0WN0 HW ` VN` VH &0WN0 4B >W" >V^N M` Z^R e&c:: l>c" nVb:[N k` db[R Y&c:: L>cx .j:bN` !jbH ` ` ` cNg:` cgH $` 2` A` O^Nb:` \^bH f` l` n` j` c` W` J` ;` ,` ` ` ` ` ` ` &` 4B >( >` M` Z` e` l` n` k` d` Y&[!: L>[[5F C2[ =[NV ."[> !:YN& RY `  WNV "W> :YNg& $RYgRNg' 2` A gg:V O"Rg> \:e:& fRe l` n c:V j"c> c:e:& WRe^: J` ;` ,"^> ` ` ` ` ` ` &` 4B >( >` M` Z` e` l` n` k` d0Y!0 YHYY5 L`Y =YNV ."Y> !:[N& R[ `  YNV "Y> :eVN& $ReVe'RN 2` A ee:V O"Re> \:g:& fRg l` n e:V j"e> c:b:& WRb^: J` ; [NV ,"^> :[& ` ` ` ` ` &` 4B >( >` M` Z` e` l` n` k` d0W!0 YHWW5 L`W =WNV ."W> !:RN& RR `  WNV "W> :YNc& $RYc^Nc' 2` A cc:V O"^c> \:^:& fR^ l` n c:V j"c> c:e:& WRej: J` ; jc:[NV ,"c[> ` ` ` ` ` ` &` 4B >( >b:YN` MbYH Z` e` l` n` k` d` Y` L` =@^:VNV ."^V> !` ` ` ` ` ` $` 2` A <@f:_NV O"f_> \` f` l` n` j` c` W` J` ; @V ,` ` ` ` ` ` ` &` 4B >( >` M` Z` e` l` n` k` d` Y` L`cNxcHdNxdH_Nx_\Nx\HcNxcHdNxddNxdH_Nx_HhNxhfNxfH/MTrkStrings!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y d Nd[ edC&ed@ed @edG---- End of CH Setup --------------------------------------------------UFU\F\ZFZWFWXFXSFSXFX_F_aFa\F\ZFZ_F_ZFZXFXSFSNFNKFKIFpI/MTrk!EPiano!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  3d[  ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------7?x77?x77?`77?x77?x77?x77?x77?`77?x77?x77?x77?x77?`77?x77?x77?x77?x77?`77?x77?x75?x55?x55?`55?x55?x55?x55?x55?`55?x55?x55?x55?x55?`55?x55?x55?x55?x55?`55?x55?x53?x33?x33?`33?x33?x33?x33?x33?`33?x33?x33?x33?x33?`33?x33?x33?x33?x33?`33?x33?x30?x00?x00?`00?x00?x00?x00?x00?`00?x00?x02?x22?x22?`22?x22?x22?x22?x22?`22?x22?x27?0:x707?0:x707?0:`707?0:x707?0:x707?0:x707?0:x707?0:`707?0:x707?0:x707@0:x707@0:x707@0:`707@0:x707@0:x707@0:x707@0:x707@0:`707@0:x707@0:x705@.:x5.5@.:x5.5@.:`5.5@.:x5.5@.:x5.5@.:x5.5@.:x5.5@.:`5.5@.:x5.5@.:x5.5@.:x5.5@.:x5.5@.:`5.5@.:x5.5@.:x5.5@.:x5.5@.:x5.5@.:`5.5@.:x5.5@.:x5.3@,:x3,3@,:x3,3@,:`3,3@,:x3,3@,:x3,3@,:x3,3@,:x3,3@,:`3,3@,:x3,3@,:x3,3@,:x3,3@,:x3,3@,:`3,3@,:x3,3@,:x3,3@,:x3,3@,:x3,3@,:`3,3@,:x3,3@,:x3,0@):x0)0@):x0)0@):`0)0@):x0)0@):x0)0@):x0)0@):x0)0@):`0)0@):x0)0@):x0)2@+:x2+2@+:x2+2@+:`2+2@+:x2+2@+:x2+2@+:x2+2@+:x2+2@+:`2+2@+:x2+2@+:x2+0?x00?x00?`00?x00?x00?x00?x00?`00?x00?x00?x00?x00?`00?x00?x00?x00?x00?`00?x00?x0.?x..?x..?`..?x..?x..?x..?x..?`..?x..?x..?x..?x..?`..?x..?x..?x..?x..?`..?x..?x.,?x,,?x,,?`,,?x,,?x,,?x,,?x,,?`,,?x,,?x,,?x,,?x,,?`,,?x,,?x,,?x,,?x,,?`,,?x,,?x,+?x++?x++?`++?x++?x++?x++?x++?`++?x++?x++?x++?x++?`++?x++?x++?x++?x++?`++?x++?x+0?x00?x00?`00?x00?x00?x00?x00?`00?x00?x00@x00@x00@`00@x00@x00@x00@x00@`00@x00@x0.@x..@x..@`..@x..@x..@x..@x..@`..@x..@x..@x..@x..@`..@x..@x..@x..@x..@`..@x..@x.,@x,,@x,,@`,,@x,,@x,,@x,,@x,,@`,,@x,,@x,,@x,,@x,,@`,,@x,,@x,,@x,,@x,,@`,,@x,,@x,+@x++@x++@`++@x++@x++@x++@x++@`++@x++@x++@x++@x++@`++@x++@x++@x++@x++@`++@x++@x+$d0UH::>B%?Qb>^AC*:y?7Ag?WD>@;?2$0R?N)>N)2(,-3MCV 0A?)3 0,%CW+6.1 5>29CXAIWCp?H%AKAH'? 2+.C5 >; $S'FHA,HH]0Q >2F=:6>;CH?P4Hr?CF>ABFU:6AFY$/0?\>J1?d?E>tAP+?'3E)C0>B<4ACXB083)6CN5E.:+CC8'FV^CHWF`CCHXAC]ChA95+,.0Q<7J2>VoKnSJM#K?sF\CAAJ Fn1C/?LHO4F'H?C@ A>J|AJCC_AB>4>Ct>?SJ>S :E%?y7GFCG{7 <:C?5F(8F2?FDAUA'CYOC2FjpFIHK1HB5!8?x7FIU:FJd!IAFfJKnFK6MS|M4ND6O^5FN!8F2?FxFF.7:AO58?F87?:?>?`7:0?x05?x57?x7:?x>:<9??x<<9x<<9`<<9x<<9x<<9x<<9x<<9`<<9x<<9x?<<9x<<9x<<9`<<9x<<9x<<9x<<9x<<9`<<9x<<9x<:9>?x::9x::9`::9x::9x::9x::9x::9`::9x::9x>::9x::9x::9`::9x::9x::9x::9x::9`::9x::9x:?9x??9x??9`??9x??9x??9x??9x??9`??9x??9x??9x??9x??9`??9x??9x??9x??9x??9`??9x??9x?C?79xC7C?79xC7C?79`C7C?79xC7C?79xC7C?79xC7C?79xC7C?79`C7C?79xC7C?79xC7A?:9xA:A?:9xA:A?:9`A:A?:9xA:A?:9xA:>?p>F7Fp>7?Fx??Fx?<9F?x<F<9F?x<F<9F?`<F<9F?x<F<9F?x<F<9F?x<F<9F?x<F<9F?`<F<9F?x<F<9F?x<F<9F?x<F<9F?x<F<9F?`<F<9F?x<F<9F?x<F<9??x<?<9??x<?<9??`<?<9A?x<A<9C?x<C:9A?x:A:9A?x:A:9A?`:A:9A?x:A:9A?x:A:9A?x:A:9A?x:A:9A?`:A:9A?x:A:9A?x:A:9>?x:>:9>?x:>:9>?`:>:9>?x:>:9>?x:>:9A?x:A:9A?x:A:9A?`:A:9??x:?:9A?x:A69??x6?69??x6?69??`6?69??x6?69??x6?69??x6?69??x6?69??`6?67=?x6=??87x?8F?69xF6F?69xF6F?69`F6F?69xF6F?69xF6F?69xF6F?69xF6F?69`F6D?69xD6C?69xC6??59x?5??59x?5??59`?5??59x?5??59x?5??59x?5??59x?5??59`?5C?5?xC5A?4?xA4F?A?xFAF?A?xFAF?A?`FAF?xFF?xF??G?p?G;?p;8?@Kp8@6?BKp6B=9D?x==9x==9`==9x==9x==9x==9x==9`==9x==9xD==9x==9x==9`==9x==9x==9D?x==9x==9`==9x==9xD=;9B?x;;9x;;9`;;9x;;9x;;9x;;9x;;9`;;9x;;9xB;;9x;;9x;;9`;;9x;;9x;;9B?x;;9x;;9`B;;9@?x;@;9B?x;B@?x@@9x@@9`@@9x@@9x@@9x@@9x@@9`@@9x@@9x@@9x@@9x@@9`@@9x@@9x@@9x@@9x@@9h@;?x;@?x@B?xBD?89xD8D?89xD8D?89`D8D?89xD8D?89xD8D?89xD8D?89xD8D?89`D8D?89xD8D?89xD8B?;9xB;B?;9xB;B?;9`B;B?;9xB;B?;9xB;;???p?=Fp;=?F8?p?@Fx@@Fx8@=9G?x=G=9G?x=G=9G?`=G=9G?x=G=9G?x=G=9G?x=G=9G?x=G=9G?`=G=9G?x=G=9G?x=G=9G?x=G=9G?x=G=9G?`=G=9G?x=G=9G?x=G=9@?x=@=9@?x=@=9@?`=@=9B?x=B=9D?x=D;9B?x;B;9B?x;B;9B?`;B;9B?x;B;9B?x;B;9B?x;B;9B?x;B;9B?`;B;9B?x;B;9B?x;B;9??x;?;9??x;?;9??`;?;9??x;?;9??x;?;9B?x;B;9B?x;B;9B?`;B;9@?x;@;9B?x;B79@?x7@79@?x7@79@?`7@79@?x7@79@?x7@79@?x7@79@?x7@79@?`7@77>?x7>@?97x@9;9G?x;G;9G?x;G;9G?`;G;9G?x;G;9G?x;G;9G?x;G;9G?x;G;9G?`;G;7D?x;DG??7xG?@9L?x@L@9L?x@L@9L?`@L@9L?x@L@9L?x@L@9L?x@L@9L?x@L@9L?`@LL?D?xLDK?B?xKBG?B?PGB??G?x?G??G?x?G??G?p?G;Fp;8?@Kp8@?K6?p?6;d-d4P48P=Ph=8;x-/MTrkAArp!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y P d@[ edC&ed@ed @edG---- End of CH Setup --------------------------------------------------O dOM dMO dOR dRO dOM dMO dOR dRO dOM dMOdORdROdOMdMOdORdROdOMdMOdORdROdOMdMOdORdROdOMdMOdORdRWdWVdVRdR^d^YdYWdWYdYRdRYdYWdWYdYRdRYdYWdWYdYRdRYdYWdWYdYRdRYdYWdWYdYR dRY dYW!dWY!dYR!dRY"dYW"dWY#dYR#dR[$d[Y$dYW%dWR%dRO%dOM&dMO&dOT'dTO'dOM(dMO(dOT)dTO)dOM)dMO*dOT*dTO+dOM+dMO,dOT,dTO-dOM-dMO-dOT.dTO.dOM/dMO/dOT0dTO0dOM0dMO1dOT1dTW2dWV2dVR3dRO3dOM4dMK4dKM4dMR5dRM5dMK6dKM6dMR7dRM7dMK8dKM8dMR8dRM9dMK9dKM:dMR:dRM;dMK;dKMdRM>dMK?dKM?dMV@dVW@dWV@dVRAdR^Ad^[Bd[YBdY[Cd[TCdT[Dd[YDdY[Dd[TEdT[Ed[YFdY[Fd[TGdT[Gd[YGdY[Hd[THdTWIdWVIdVWJdWRJdRWKdWVKdVWKdWRLdRWLdWVMdVWMdWRNdRWNdWVOdVWOdWROdRYPdYWPdWYQdYRQdRYRdYWRdWYSdYRSdRYSdYWTdWYTdYRUdRYUdYWVdWYVdYRYdRYZdYV[dVY\dYR\dRY]dYV^dVY^dYR_dRF`dFMadMRadRTbdTVcdVYcdY^dd^bedbcddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[bddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddY^dd^cddc^dd^YddY^dd^cddc^dd^YddY^dd^cddc^dd^YddY^dd^cddc^dd^YddY^dd^cddc^dd^YddY^dd^YddYRddRYddY^dd^cddc^dd^YddY^dd^YddYRddRYddY[dd[^dd^[dd[RddR[dd[RddROddORddR[dd[^dd^[dd[RddR[dd[RddROddORddRYddY^dd^YddYRddRYddYRddRMddMRddRFddFMddMRddRTddTVddVYddY^dd^bddbcndcbndb^nd^[nd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[cddcbddb^dd^[dd[bddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddYbddbcddcbddbYddY^dd^cddc^dd^YddY^dd^cddc^dd^YddY^dd^cddc^dd^YddY^dd^cddc^dd^YddY^dd^cddc^dd^YddY^dd^YddYRddRYddY^dd^cddc^dd^YddY^dd^YddYRddRYddY[dd[^dd^[dd[RddR[dd[RddROddORddR[dd[^dd^[dd[RddR[dd[RddROddORddRYddY^dd^YddYRddRYddYRddRMddMRddRGddGNddNSddSUddUWddWZddZ_dd_cddcddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\cddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZ_dd_dddd_dd_ZddZ_dd_dddd_dd_ZddZ_dd_dddd_dd_ZddZ_dd_dddd_dd_ZddZ_dd_dddd_dd_ZddZ_dd_ZddZSddSZddZ_dd_dddd_dd_ZddZ_dd_ZddZSddSZddZ\dd\_dd_\dd\SddS\dd\SddSPddPSddS\dd\_dd_\dd\SddS\dd\SddSPddPSddSZddZ_dd_ZddZSddSZddZSddSNddNSddSGddGNddNSddSUddUWddWZddZ_dd_cddcddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\ddddcddc_dd_\dd\cddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZcddcddddcddcZddZ_dd_dddd_dd_ZddZ_dd_dddd_dd_ZddZ_dd_dddd_dd_ZddZ_dd_dddd_dd_ZddZ_dd_dddd_dd_ZddZ_dd_ZddZSddSZddZ_dd_dddd_dd_ZddZ_dd_ZddZSddSZddZ\dd\_dd_\dd\SddS\dd\SddSPddPSddS\dd\_dd_\dd\SddS\dd\SddSPddPSddSZddZ_dd_ZddZSddSZddZSddSNddNSddSGddGNddNSddSUddUWddWZddZ_dd_cddcddddcddc_cd_\bd\d`ddc_dc_^d_\\d\d[ddcYdc_Xd_\Wd\dUddcTdc_Sd_\Qd\dPddcNdc_Md_\Ld\dJddcIdc_Hd_\Fd\dEddcCdc_Bd_\Ad\d?ddc>dc_=d_\;d\d:ddc9dc_7d_\6d\d4ddc3dc_2d_\0d\d/ddc.dc_,d_\+d\d)ddc(dc_'d_\%d\d$ddc#dc_!d_\ d\dddcdc_d_\d\dddcdc_d_\d\dddcdc_d_\d\/MTrkArp2!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y P 2[ edE&ed@ed @edG---- End of CH Setup --------------------------------------------------F[ d[Y dY[ d[^ d^[ d[Y dY[ d[^ d^[ d[Y dY[d[^d^[d[YdY[d[^d^[d[YdY[d[^d^[d[YdY[d[^d^[d[YdY[d[^d^cdcbdb^d^jdjedecdcede^d^edecdcede^d^edecdcede^d^edecdcede^d^edecdcede^ d^e dec!dce!de^!d^e"dec"dce#de^#d^g$dge$dec%dc^%d^[%d[Y&dY[&d[`'d`['d[Y(dY[(d[`)d`[)d[Y)dY[*d[`*d`[+d[Y+dY[,d[`,d`[-d[Y-dY[-d[`.d`[.d[Y/dY[/d[`0d`[0d[Y0dY[1d[`1d`c2dcb2db^3d^[3d[Y4dYW4dWY4dY^5d^Y5dYW6dWY6dY^7d^Y7dYW8dWY8dY^8d^Y9dYW9dWY:dY^:d^Y;dYW;dWYd^Y>dYW?dWY?dYb@dbc@dcb@db^Ad^jAdjgBdgeBdegCdg`Cd`gDdgeDdegDdg`Ed`gEdgeFdegFdg`Gd`gGdgeGdegHdg`Hd`cIdcbIdbcJdc^Jd^cKdcbKdbcKdc^Ld^cLdcbMdbcMdc^Nd^cNdcbOdbcOdc^Od^ePdecPdceQde^Qd^eRdecRdceSde^Sd^eSdecTdceTde^Ud^eUdecVdceVde^Yd^eZdeb[dbe\de^\d^e]deb^dbe^de^_d^R`dRYadY^ad^`bd`bcdbecdejddjnednjdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*edNe*jdNj*edNe*`dN`*edNe*jdNj*edNe*`dN`*edNe*jdNj*edNe*`dN`*edNe*jdNj*edNe*`dN`*edNe*jdNj*edNe*`dN`*edNe*`dN`*YdNY*`dN`*edNe*jdNj*edNe*`dN`*edNe*`dN`*YdNY*`dN`*bdNb*edNe*bdNb*YdNY*bdNb*YdNY*VdNV*YdNY*bdNb*edNe*bdNb*YdNY*bdNb*YdNY*VdNV*YdNY*`dN`*edNe*`dN`*YdNY*`dN`*YdNY*TdNT*YdNY*RdNR*YdNY*^dN^*`dN`*bdNb*edNe*jdNj*ndNn jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*jdNj*idNi*edNe*bdNb*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*idNi*jdNj*idNi*`dN`*edNe*jdNj*edNe*`dN`*edNe*jdNj*edNe*`dN`*edNe*jdNj*edNe*`dN`*edNe*jdNj*edNe*`dN`*edNe*jdNj*edNe*`dN`*edNe*`dN`*YdNY*`dN`*edNe*jdNj*edNe*`dN`*edNe*`dN`*YdNY*`dN`*bdNb*edNe*bdNb*YdNY*bdNb*YdNY*VdNV*YdNY*bdNb*edNe*bdNb*YdNY*bdNb*YdNY*VdNV*YdNY*`dN`*edNe*`dN`*YdNY*`dN`*YdNY*TdNT*YdNY*SdNS*ZdNZ*_dN_*adNa*cdNc*fdNf*kdNk*odNo*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*fdNf*kdNk*fdNf*adNa*fdNf*kdNk*fdNf*adNa*fdNf*kdNk*fdNf*adNa*fdNf*kdNk*fdNf*adNa*fdNf*kdNk*fdNf*adNa*fdNf*adNa*ZdNZ*adNa*fdNf*kdNk*fdNf*adNa*fdNf*adNa*ZdNZ*adNa*cdNc*fdNf*cdNc*ZdNZ*cdNc*ZdNZ*WdNW*ZdNZ*cdNc*fdNf*cdNc*ZdNZ*cdNc*ZdNZ*WdNW*ZdNZ*adNa*fdNf*adNa*ZdNZ*adNa*ZdNZ*UdNU*ZdNZ*SdNS*ZdNZ*_dN_*adNa*cdNc*fdNf*kdNk*odNo kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*kdNk*jdNj*fdNf*cdNc*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*jdNj*kdNk*jdNj*adNa*fdNf*kdNk*fdNf*adNa*fdNf*kdNk*fdNf*adNa*fdNf*kdNk*fdNf*adNa*fdNf*kdNk*fdNf*adNa*fdNf*kdNk*fdNf*adNa*fdNf*adNa*ZdNZ*adNa*fdNf*kdNk*fdNf*adNa*fdNf*adNa*ZdNZ*adNa*cdNc*fdNf*cdNc*ZdNZ*cdNc*ZdNZ*WdNW*ZdNZ*cdNc*fdNf*cdNc*ZdNZ*cdNc*ZdNZ*WdNW*ZdNZ*adNa*fdNf*adNa*ZdNZ*adNa*ZdNZ*UdNU*ZdNZ*SdNS*ZdNZ*_dN_*adNa*cdNc*fdNf*kdNk*odNo*kdNk*jdNj*fcNf*cbNc*k`Nk*j_Nj*f^Nf*c\Nc*k[Nk*jYNj*fXNf*cWNc*kUNk*jTNj*fSNf*cQNc*kPNk*jNNj*fMNf*cLNc*kJNk*jINj*fHNf*cFNc*kENk*jCNj*fBNf*cANc*k?Nk*j>Nj*f=Nf*c;Nc*k:Nk*j9Nj*f7Nf*c6Nc*k4Nk*j3Nj*f2Nf*c0Nc*k/Nk*j.Nj*f,Nf*c+Nc*k)Nk*j(Nj*f'Nf*c%Nc*k$Nk*j#Nj*f!Nf*c Nc*kNk*jNj*fNf*cNc*kNk*jNj*fNf*cNc*kNk*jNj*fNf*cNc/MTrk*Lead!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y p n[ edD&ed@ed @edG---- End of CH Setup --------------------------------------------------TdpTRdpRTdpTYdxYTdxT@RdpRPdpPRdpRWdxWRdxR@PdpPOdpOPdpPUdxUPdxP@OdpOPdpPOdpOMdxMKdxK@TdpTRdpRTdpTYdxYTdxT@RdpRPdpPRdpRWdxWRdxR@PdpPOdpOPdpPUdxUPdxP@OdpOPdpPOdpOMdxMKdxKPFdpFMdpMKdpKMdpMOdxORdxRPTdxTVdxVWdpWVdxVOdxO8TdxTVdxVWdxWYdxYRdxRRdxRP^dx^^dx^P[dx[[dp[YdpYWdpWYdxYRdxRH[dx[YdpYWdpWVdpVWdxWRdxRHOdxORdxRHYdxYP^dx^^dx^HedxePjdxjjdxjRdxRPWdxWWdxWPYdxYYdxY^dx^HVdxVhWdxWpRdxRRdxR(RdxRpYdxYYdxYP^dx^^dx^Pedxeedxepcdxccdxcpbdxbbdxbp[dx[[dx[XT(pTT-pTT2pTT9pTTBpTTMpTTZpTTdpTRdpRTdpTYdxYTdxT@RdpRMdpMRdpRWdxWYdxY@RdpRP^dp^P[dp[YdpYWdpWYdxYRdxROdhO`RdxRRdpR^dp^PedpePTdpTRdpRTdpTYdxYTdxT@RdpRMdpMRdpRWdxWYdxY@RdpRP^dp^P[dp[YdpYWdpWYdxYRdxR@[dp[YdpYWdpWYdxYRdxR@UdpUSdpSUdpUZdxZUdxU@SdpSNdpNSdpSXdxXZdxZ@SdpSP_dp_P\dp\ZdpZXdpXZdxZSdxSPdhP`SdxSSdpS_dp_PfdpfPhdphSdpSUdpUZdxZUdxU@SdpSNdpNSdpSXdxXZdxZ@SdpSP_dp_P\dp\ZdpZXdpXZdxZSdxS@\dp\ZdpZXdpXZdxZ_dx_@\d(\_d(_ddd/MTrk :Lead2!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  hn[ edB&ed@ed @edG---- End of CH Setup --------------------------------------------------TdC,pTRdpRTdpCTYdC,xYCTdC,xTCC,@CC,PCC,xCC,xCC,@CRdA,pRPdpPRdpARWdA,xWARdA,xRAA,@AA,PAA,xAA,xAA,@APd?,pPOdpOPdp?PUd?,xU?Pd?,xP??,@??,P??,x??,x??,@?Od>,pOPdpPOdp>OMd>,xM>Kd>,xK>>,@>>,P>>,x>>,x>>,@>TdC,pTRdpRTdpCTYdC,xYCTdC,xTCC,@CC,PCC,xCC,xCC,@CRdA,pRPdpPRdpARWdA,xWARdA,xRAA,@AA,PAA,xAA,xAA,@APd?,pPOdpOPdp?PUd?,xU?Pd?,xP??,@??,P??,x??,x??,@?OdA,pOPdpPOdpAOMdA,xMAKdA,xKAA,PFdpAFMdC,pMKdpKMdpCMOdC,xOCRdC,xRCC,PTdxTVdxCVWdC,pWVdxVOdxOpCC,xCC,xCC,@CC,PCC,xCC,xCC,XTdxTVdxVWdxCWYdA,xYXAA,xAA,xAA,HRdxARRdA,xRXAA,xA^dA,x^A^dA,x^P[dxA[[dA,p[YdpYWdpAWYdA,xYARdA,xRAA,H[dxA[YdA,pYWdpWVdpAVWdA,xWARdA,xRAA,HOdxAORd<,xRX<<,x<<,x<Yd<,xYP^dx<^^d<,x^X<<,x<<,x<ed<,xePjdx<jjdC,xjXCC,xCC,xCC,@CC,RdxRXCC,xCC,WdxCWC,WdxWPYdxCYA,YdxYXAA,xAA,xAA,@AA,^dx^XAA,xAA,xAA,VdxVhWdxWpRdxARA,RdxRXAA,xAA,xAA,@AA,PAA,xAA,xAA,`RdxRpYdxAY<,YdxYX<<,x<<,^dx<^<,^dx^Pedx<e<,edxepcdxccdxcx<<,x<<,bdx<b<,bdxbp[dx<[[dx[XT(pTT-pTT2pTT9pTTBpTTMpTTZpTTdpTRdpRTdpTYdxYTdxT@RdpRMdpMRdpRWdxWVdxV@RdpRPVdpVP[dp[YdpYWdpWYdxYRdxROdhO`RdxRRdpR^dp^PedpePTdpTRdpRTdpTYdxYTdxT@RdpRMdpMRdpRWdxWVdxV@RdpRPVdpVP[dp[YdpYWdpWYdxYRdxR@[dp[YdpYWdpWYdxYRdxR8UdpUSdpSUdxUZdxZUdxU8SdpSNdpNSdxSXdxXWdxW@SdpSPWdpWH\dp\ZdpZXdxXZdxZSdxSPdhP`SdxSSdpS_dp_PfdpfPUdpUSdpSUdxUZdxZUdxU8SdpSNdpNSdxSXdxXWdxW@SdpSPWdpWH\dp\ZdpZXdxXZdxZSdxS8\dp\ZdpZXdxXZdxZ_dx_@dd(dfd(fkdk/MTrk Bass!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y ! Hd[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------^+d@+$d`$p$dP$p$Up$p$dP$+d`+$Z`$"d`"p"dP"p"Up"p"dP")d`)"Z`" d` p dP p Up p dP 'd`' Up dpd`pdPpUppdP&Z`&+Z+D$d$"d"@.d@. d +d@+d@dd$d$p+d@+$d@$"d"p)d@).d@. d p'd@' d@ dd$d $pdp$ih$Zh$d2C?7>+=<;:{8o7c6B6$@"d"Zp"dH"xk9.dP:5; ^;'<p<8=}=>>>^>>? @l.3d`3=.d|=a>F?@.'ZH'dp ZH ='d>{>@~' Z` d@&d@&+ZP+=&Z|=a>F?@w&dX$d$+Zp+0d@0$d@$"d")Zp)T=.dE=>c>1?@4."d@" d 'd`',d@, d@ d &d`&+d2x?>J== z<V;2:9k7G6 +#56491=.A+D(H% I$ '@$d`-?w<A: 8T53g01.{+E)'X$""k  $@pd `d0"Zh"#dh#$Z3>W>{>?@$`T="Z=|=a>F?@p"$dh$'Zh' <+dI>@+)Z8)hdx"Z`"$d@$+Zx@i=R;;9$7 5v2 2 +@h$P@$PZ@UnZX<;+d0;<0<<0=<9=(S<)d +8>h><@Jtd8 )Z d0 p'Z`',dP, dP `d0p&Z`&+dP+dP` d0p$d`$)d`)ZPp)d`)d0p&Z`&+d`+$Zp$&i`&"Zp"n` d p'dp',dH,x dH xd p&dp&+dH+xd`&d`&d %Z`%*d@*d@d@$Z`$d`d`&Z`&Zp&Zp&U<.d<@<.+Zp+ d p'dp',dH,x dH xd p&dp&+dH+xZ`&d`&d %Z`%*d@*d@dP$Zp$)dX)hd`&d`&,dp, Zp ,dQ?<0><=EAEFE d$$+d\ ed:>AF+8FFCF.d$ h i\$7:>C.EAEFE> lB02d mr:>AF28FFAF+7:>AEAEFE.d:>AF.8FFAF+7:>A?EHEFECE0d?HFC0>EAEFEJE.dJKE>AF.KF7dAFME:>7AM0F7F>FFF?F07>F?.F5FFFAF>F.5FA>,F?FCF5F:F,?C5:AF+F2F:F5FA+2:50F7F>FFF?F07>F?.F5FFFAF>F.5FA>,F5F:FKFCF,5:KCAF+F2F:F5FMFA+2:5hM `CF5F8FFA3:.>3F1dFFAF7F7F4 f< g4 hp i4 jp k4 l7:F\ mp n4 op p4 qp r4 sp t4 u)0?>:+dFF?F5F:F\ v4 w wx x4 yp zp {p |4 }>?>F2 ~l b+F5:> sHF,d3F7FF5F7F+F>57*d?F6F:FFF*?6:F)dCF?FJFP 7`+>F,d3F7FHFH7CF5F,3C5+d>F5F7FFFF7JF:F+>5J:*d:FAFKFKA?FFF*:?F)dCF?F8F)C?8@+d>FCFHC>x+-@ x?FDF,dH?Dx,@ zIF-d8F@FI-8@,dGF?F6F,G?6+d@F7F;FGF+@7;G*dDF@F=F9F*D@=9,d8F;F?FGF,8;?G-d4F8FIFI8DF6F-4D6,d?F6F8FGFG8KF;F,?6K;+d7F;FBFLFLB@FGF+7;@G*dDF=F9F*D=9,d8FBF;F,8B;-d4?8?=?DF`-48=D/MTrkDrum!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @n[ ed@&ed@ed @edG---- End of CH Setup -------------------------------------------------- n@*J<*4*<<*4*J<*$*J<*,*8<*,*G<*,*:<*L*J<*4*<<*4*J<*$*J<*,*8<*,*G<*,*:<*,:<,|*J<*4*<<*4*J<*$*J<*,*8<*,*G<*,*:<*L*J<*4*<<*4*J<*$*J<*,*8<*,*G<*,*:<*,.J<.4,J<,<*:<*<$U<$*J%U<*%4*<<*4*J<*$*J<*,*8<*,*G<*4%U<%<*:<**:<*<$7<$<$U<$*J%U<*%4*<<*4*J<*$*J<*,*8<*,*G<*4%U<%<*:<*,$U<$<*:<*<.H<.4$U,H<$,,*:<**J%U<*%4*<<*4*J<*$*J<*,*8<*,*G<*4%U<%<*:<*,*4<*,,:$7<,$<$U3Z<$3*J<*4*<<*4*J<*4$B<$4*J$U3Z<*$3,*8<*,*G<*,*:<*,.J<.$ u$c7c<$7$c37<$3$*;<*$$c3@<$3$*;<*4$<<$<*;<*<$c37<$3$c3@<$3$c37<$3$*;<*$$c3@<$3$*;<*,$<*;<$*<$c37<$3$.B<.4$B<$<37<3<$c3@,B<$3,$c37*B<$3*$*;<*$$c3@<$3$*;<*4$<<$<*;<*<$c37<$3$*2<*$$c3@.B<$3.$c37*;<$3*4*2<*4*;<*$$c3@*;<$3*$*;<*,*;$c<*$<%c.B7B<%.74$B<$4$c,;<$,4*M<*<$B37%;<$3%<$c7c,;<$7,4*;%G<*%4-2<-<*2<*,$c.B37<$.34,;<,4*2<*4*2<*<%;<%<$c3@<$3<*2<*<%G<%4.B<.4$<,2<$,<*B<*<$c37<$34*B<*4*2<*!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------h/simutrans-124.5/simutrans/music/51-Summer-Intersection.mid000066400000000000000000001567721520675460500236050ustar00rootroot00000000000000MThd MTrkQ X/MTrk?dr! d$d1d,dp$1,*dp*.dp.p*dp*p3dp,dp3,$d,dp$,*dp*.dp.p*dp*3d`3,dp,$d,dp$,*dp*.dp.p*dp*p3dp,dp3,$d,dp$,*dp*.dp.p*dp*3d`3,dp,$d,dp$,*dp*.dp.p*dp*p3dp,dp3,$d,dp$,*dp*.dp.p*dp*3dp6p36,dp,,d$dp,*dp$*.dp.$d&dp*dp$&*p3d&d$dp,dp3&$,,dp,*dp*.dp.p*dp*3dp$d6p3$6,dp,,d5d$dp,5$*dp*.d6p.6p*d&d$d5dp*&$5p5d$d3dp5$,dp3,,d6$dp,6$*d&dp*&.d5dp.56p6*d$d&dp*$&3dp5d$dp35$,d&dp,&,d5d$dp,5$*dp*.d6$dp.6$p*d&d$d5dp*&$5p5d63dp56,dp3,,d6$dp,6$*d&d1p*&1.d5d$dp.5$6p6*d$d&d12p*$&&d3dp1&5d$dp35$,d&dp,&,d$d1dp,*dp$1*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&1p31,dp,,d$dp,*dp$*.d1#&dp.1p&*d$dp*1(&dp$13dp&,dp3,,d&d12$dp,&1*d&dp$*&.d&dp.&1d&d'dp1&'@,d1d$dp,1*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3d'dp,d&dp3',&,dp,,d*dp,*.d&dp.&,dp,*dp*,dp,'d63dp'6p3,d5d1d$dp,51*dp$*.d6p.6p*d5d&d$dp*5p&$5d3dp5,dp3,,d6$dp,6*dp$*.d5dp.56p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.dp6.p5d*d$d&dp5*p$&5d3dp5,dp3,6,d$dp6,*dp$*5d.dp5.6p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.dp6.p5d*d$d&dp5*p$&5d3dp5,dp3,6,d$dp6,*dp$*5d.dp5.6p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.d,d&dp6.,p&5d*d$dp5*,d&dp$,&5d3dp5,dp3,61&d-d,dp61&-,,d$d1dp,*dp*$.dp.p*d&d$dp1*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$d&dp,*dp$&*.dp.&d$dp&*dp$*3dp$d&dp3$&,dp,,dp,*dp*.d'dp.'p*d$dp*$p$d3d&dp$,dp3&,,dp,*dp*.d'dp.'p*d$dp*$3dp$d&dp3$,dp&,,dp,*dp*.d&d'dp.&'p*d$d&dp*p$&&d3dp&,dp3,,d$dp,*dp$*.d&dp.&p*d&d$dp*&&d3dp$&p3,d&dp,&,d$d5dp,$5*dp*.d6p.6p*d5d&d$dp*5&$p$d5d3dp$5,dp3,,d$d6p,$6*d&dp*&.d5dp.56p6*d$d&dp*$&3dp$d5dp3$5,d&dp,&,d5d$dp,5$*dp*.d6$dp.6$p*d&d$d5dp*&$5p5d63dp56,dp3,,d6$dp,6$*d&d1p*&1.d5d$dp.5$6p6*d$d&d12p*$&&d3dp1&5d$dp35$,d&dp,&,d1d$dp,1*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d$d&dp*3dp$&p3,dp,,d$dp,*dp$*.dp.p*d$d&dp*p$&3d'dp,d&dp3',&,dp,,d*dp,*.d&dp.&,dp,*dp*,dp,'d63dp'6p3,d5d1d$dp,51*dp$*.d6p.6p*d5d&d$dp*5p&$5d3dp5,dp3,,d6$dp,6*dp$*.d5dp.56p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.dp6.p5d*d$d&dp5*p$&5d3dp5,dp3,6,d$dp6,*dp$*5d.dp5.6p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.dp6.p5d*d$d&dp5*p$&5d3dp5,dp3,6,d$dp6,*dp$*5d.dp5.6p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.d,d&dp6.,p&5d*d$dp5*,d&dp$,&5d3dp5,dp3,61&d-d,dp61&-,P&d,dp&,$d.dp$.$d&d3dp$&3,dp,,d$d1dp,*dp*$.dp.p*d$d&dp1*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.d'dp.'p*d$d&dp*$d3dp$&$6'dp36',dp,,d5d1d$dp,51*dp$*.d6p.6p*d5d&d$dp*5p&$5d3dp5,dp3,,d6$dp,6*dp$*.d5dp.56p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.dp6.p5d*d$d&dp5*p$&5d3dp5,dp3,6,d$dp6,*dp$*5d.dp5.6p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.dp6.p5d*d$d&dp5*p$&5d3dp5,dp3,6,d$dp6,*dp$*5d.dp5.6p6*d$d&dp*3dp$&5dp35,dp,5d,d$dp5,*dp$*6.d,d&dp6.,p&5d*d$dp5*,d&dp$,&5d3dp5,dp3,6&d-d'd,d1p6&-',1,d$d1dp,*dp*$.dp.p*d$d&dp1*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$d1dp,*dp*$.dp.p*d$d&dp1*p$&3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d$dp,*dp$*.dp.p*d&d$dp*3dp&$p3,dp,,d$dp,*dp$*.dp.p*d&d$dp*p&$3dp,dp3,,d&d$dp,*dp&$*.dp.&d$dp&*dp$*3dp&d$dp3&$,dp,,dp,*dp*.d'dp.'p*d$dp*$p$d3d&dp$,dp3&,,dp,*dp*.d'dp.'p*d$dp*$3dp$d&dp3$,dp&,,dp,*dp*.d&d'dp.&'p*d$d&dp*p$&&d3dp&,dp3,,d$dp,*dp$*.d&dp.&p*d&d$dp*&&d3dp$&p3,d&dp,&,d5d$dp,5$*dp*.d6p.6p*d&d$d5dp*&$5p5d$d3dp5$,dp3,,d6$dp,6$*d&dp*&.d5dp.56p6*d$d&dp*$&3dp5d$dp35$&dp&,d5d$dp,5$*dp*.d6p.6p*d&d$d5dp*&$5p5d$d3dp5$,dp3,,d6$dp,6$*d&dp*&.d5dp.56p6*d$d&dp*$&3dp5d$dp35$&dp&,d5d$dp,5$*dp*.d6p.6p*d&d$d5dp*&$5p5d$d3dp5$,dp3,,d6$dp,6$*d&dp*&.d5dp.56p6*d$d&dp*$&3dp5d$dp35$,d&dp,&,d5d$dp,5$*dp*.d6$dp.6$p*d&d$d5dp*&$5p5d63dp56,dp3,,d6$dp,6$*d&d1p*&1.d5d$dp.5$6p6*d$d&d12p*$&&d3dp1&5d$dp35$,d&dp,&$d1d,dp$1,*dp*.dp.p*dp*p3dp,dp3,$d,dp$,*dp*.dp.p*dp*3d`3,dp,$d,dp$,*dp*.dp.p*dp*p3dp,dp3,$d,dp$,*dp*.dp.p*dp*3d`3,dp,$d,dp$,*dp*.dp.p*dp*p3dp,dp3,$d,dp$,*dp*.dp.p*dp*3d`3,dp,*d$dp*p$p,d$dp,p$p3d.d$dp3.p$/MTrk1 chodpiano!f @Ed=d@dDd`E=@DpEd=d@dDd`E=@DpDdGd=d@dZDG=@fGd@d=dDd0G@=DpBd=d>dEd`B=>EpEd=d>dBd`E=>BpGg>g@gEgIG>@EwGd>d@dDd0G>@Dp=dEdDd@d`=ED@pEd=d@dDd`E=@DpDdGd=d@d`DG=@`Bd?dGdEd0B?GEpEd=d>dBd`E=>Bp=dDdBd@d`=DB@pAdFdBd=dpAFB=pGdd ;BG>`@dEddEdBdp<>EB`;dBdGd>dP;BG>pDd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep?dBd;d`?B;p=d@dDdP=@Dp=d@dDdp=@Dp=d@dBdp=@B=d@dDdp=@Dp=d`=Dd@d=dpD@=p=dDd@dp=D@=dBdEdp=BEp?dDd;dp?D;pDd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep@dBd=d`@B=p=d?dDdP=?DpDd9d@d?dpD9@?pBd?d;dpB?;pDd@d;d`D@;`Dd@d;dpD@;p=d@dEd`=@E`Dd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep?dBd;d`?B;p=d@dDdP=@Dp=d@dDdp=@Dp=d@dBdp=@B=d@dDdp=@Dp=d`=Dd@d=dpD@=p=dDd@dp=D@=dBdEdp=BEp?dDd;dp?D;p;d?dBdP;?Bp;dDd@dP;D@pEd?dBd`E?Bp=dBdEd`=BEp=d@dDd0=@Dp=dEd@dP=E@p8dp8;d@dDdp;@DpBd?d;dpB?;`Bd?d;dpB?;pDd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep?dBd;d`?B;p=d@dDdP=@Dp=d@dDdp=@Dp=d@dBdp=@B=d@dDdp=@Dp=d`=Dd@d=dpD@=p=dDd@dp=D@=dBdEdp=BEp?dDd;dp?D;pDd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep@dBd=d`@B=p=d?dDdP=?DpDd9d@d?dpD9@?pBd?d;dpB?;pDd@d;d`D@;`Dd@d;dpD@;p=d@dEd`=@E`Dd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep?dBd;d`?B;p=d@dDdP=@Dp=d@dDdp=@Dp=d@dBdp=@B=d@dDdp=@Dp=d`=Dd@d=dpD@=p=dDd@dp=D@=dBdEdp=BEp?dDd;dp?D;p;d?dBdP;?Bp;dDd@dP;D@pEd?dBd`E?Bp=dBdEd`=BEp=d@dDd0=@Dp=dEd@dP=E@p8dp8Dd@d;d`D@;p;d?dBdp;?BpBd?d;dpB?;pDd9d=d@dPD9=@pBd=d9d?dPB=9?pEd;d?dBdpE;?BPEd?d;dBdpE?;BpEd@d;dBdpE@;Bp?d@dDd;d`?@D;p;dDd@d`;D@p=dAdDd;dp=AD;p=dDdAd;d =DA;`Fd;d?dBdPF;?BpDd?d;dAdPD?;ApGd=dAdDdpG=ADPGdAd=dDdpGA=DpGdBd=dDdpGB=Dp?dAdDdGd0?ADGp;dFdBd?dp;FB?pAd=dBdFdPA=BFp=dFdCd?dP=pFC?:dEdAd>d`:EA>pAd:d>dEdpA:>E`d:dpFA>:`:dFd>dAdp:F>A`Fd>dCd:d`F>C:pFdCd>d:dpFC>:pEdCd>d:dpEC>:p7d:dp7:p:dCd?d`:C?pCdEd:d?dpCE:?`9dEdAd>d`9EA>pEd9d>dAdpE9>A`FdAd>d9dpFA>9pAdFd>d:dCdpApF>:Cp:d>dFdCdp:>FC`:d>dEdCdp:>ECp9dp9@dEdd`:EA>pAd:d>dEdpA:>E`d:dpFA>:`:dFd>dAdp:F>A`Fd>dCd:d`F>C:pFdCd>d:dpFC>:pEdCd>d:dpEC>:pHdd9dEdCd`>9ECp:dFdCd>dp:FC>`CddAd`E9>ApFd9d>dAdpF9>A`d<EAp:d>dAdFdp:>AF`d ;BG>`@dEddEdBdp<>EB`;dBdGd>dP;BG>pDd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep?dBd;d`?B;p=d@dDdP=@Dp=d@dDdp=@Dp=d@dBdp=@B=d@dDdp=@Dp=d`=Dd@d=dpD@=p=dDd@dp=D@=dBdEdp=BEp?dDd;dp?D;pDd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep@dBd=d`@B=p=d?dDdP=?DpDd9d@d?dpD9@?pBd?d;dpB?;pDd@d;d`D@;`Dd@d;dpD@;p=d@dEd`=@E`Dd@d=d`D@=`Dd?d;d`D?;`=d@dEd`=@Ep?dBd;d`?B;p=d@dDdP=@Dp=d@dDdp=@Dp=d@dBdp=@B=d@dDdp=@Dp=d`=Dd@d=dpD@=p=dDd@dp=D@=dBdEdp=BEp?dDd;dp?D;p;d?dBdP;?Bp;dDd@dP;D@pEd?dBd`E?Bp=dBdEd`=BEp=d@dDd0=@Dp=dEd@dP=E@p8dp8Dd@d;d`D@;p;d?dBdp;?BpBd?d;dpB?;pDd9d=d@dPD9=@pBd=d9d?dPB=9?pEd;d?dBdpE;?BPEd?d;dBdpE?;BpEd@d;dBdpE@;Bp?d@dDd;d`?@D;p;dDd@d`;D@p=dAdDd;dp=AD;p=dDdAd;d =DA;`Fd;d?dBdPF;?BpDd?d;dAdPD?;ApGd=dAdDdpG=ADPGdAd=dDdpGA=DpGdBd=dDdpGB=Dp?dAdDdGd0?ADGp;dFdBd?dp;FB?pAd=dBdFdPA=BFp=d?dCdFdP=?CFpDd?dAdHd`D?AHpDd?dAdHd`D?AHpId?dFdCdI?FC`dFdAd`D>FA`Ad>dFdDd`A>FD`Id?dFdCd`I?FCpId?dFdCd`I?FCpHdCd?dDdpHC?DpDdHddAdp:E>A`:dEdAd>dp:EA>p:dAdEd>dp:AE>pDd9d=d@dPD9=@pBd=d9d?dPB=9?pEd;d?dBdpE;?BPEd?d;dBdpE?;BpEd@d;dBdpE@;Bp?d@dDd;d`?@D;p;dDd@d`;D@p=dAdDd;dp=AD;p=dDdAd;d =DA;`Fd;d?dBdPF;?BpDd?d;dAdPD?;ApGd=dAdDdpG=ADPGdAd=dDdpGA=DpGdBd=dDdpGB=Dp?dAdDdGd0?ADGp;dFdBd?dp;FB?pAd=dBdFdPA=BFp=d?dCdFdP=?CFp:dEdAd>d`:EA>pAd:d>dEdpA:>E`d:dpFA>:`:dFd>dAdp:F>A`Fd>dCd:d`F>C:pFdCd>d:dpFC>:pEdCd>d:dpEC>:p7d:dp7:p:dCd?d`:C?pCdEd:d?dpCE:?`9dEdAd>d`9EA>pEd9d>dAdpE9>A`FdAd>d9dpFA>9pAdFd>d:dCdpApF>:Cp:d>dFdCdp:>FC`:d>dEdCdp:>ECp9dp9@dEdd`:EA>pAd:d>dEdpA:>E`d:dpFA>:`:dFd>dAdp:F>A`Fd>dCd:d`F>C:pFdCd>d:dpFC>:pEdCd>d:dpEC>:pHdd9dEdCd`>9ECp:dFdCd>dp:FC>`d`9FA>pHdCd?ddHdEdAd`>HEA`HdEdAd>dPHEA>p>dHdEdAdp>HEAp:dEdAd>d`:EA>pAd:d>dEdpA:>E`d:dpFA>:`:dFd>dAdp:F>A`Fd>dCd:d`F>C:pFdCd>d:dpFC>:pEdCd>d:dpEC>:p7d:dp7:p:dCd?d`:C?pCdEd:d?dpCE:?`9dEdAd>d`9EA>pEd9d>dAdpE9>A`FdAd>d9dpFA>9pAdFd>d:dCdpApF>:Cp:d>dFdCdp:>FC`:d>dEdCdp:>ECp9dp9@dEdd`:EA>pAd:d>dEdpA:>E`d:dpFA>:`:dFd>dAdp:F>A`Fd>dCd:d`F>C:pFdCd>d:dpFC>:pEdCd>d:dpEC>:pHdd9dEdCd`>9ECp:dFdCd>dp:FC>`CddAd`E9>ApFd9d>dAdpF9>A`d<EAp:d>dAdFdp:>AF`d ;BG>`@dEdd<BEp@dGdd ;BG>`@dEddEdBdp<>EB`;dBdGd>dP;BG>pEd=d>dBd`E=>Bp=dDdBd@d`=DB@pAdFdBd=dpAFB=p/MTrkbass!'n-dp-p-dp-,dp,p,dp,*dp*p*dp*`,dp,`*dP*p*dp*(dp(p%dp%&dp&p(dp(`&dp&`*dp*,dp,-dp-p-dp-,dp,p,dp,*dp*p*dp*p'dp'-dp-`/dp/p-dp-p&dp&*dp*p%dp%*d`*p*dp*%dp%p*dp*p*dp*%dp%$dp$p&dp&(dp(p$dp$+dp+p#dp#&dp&p#dp#*dp*#dp#p#dp#!dp!p#dp#$dp$p$dp$-dp-(dp(#dp#*d`*/dp/-dp-&dp&p!dp!%dp%p,dp,p dp p,dp,p!dp!p#dp#*df*j%dP%p,dp,p*dp*(dp(p%d`%p,dp,p*dp*p/dp/p%dp%p,dp,p dp p,dp,p!dp!p#dp#*d`*p%dP%p'dp'p*dp*p(dP(p/dp/p-dp-,dp,(dp(p%dp%p,dp,p dp p,dp,p!dp!p#dp#*df*j%dP%p,dp,p*dp*(dp(p%d`%p,dp,p*dp*p/dp/p#dp#p*dp*p(dp(p,dp,p-dp-'dp'p*dp*`,dP,p*dp*p(dp(p,dp,p(dp(/dp/p/dp/@%dp%p,dp,p dp p,dp,p!dp!p#dp#*df*j%dP%p,dp,p*dp*(dp(p%d`%p,dp,p*dp*p/dp/p%dp%p,dp,p dp p,dp,p!dp!p#dp#*d`*p%dP%p'dp'p*dp*p(dP(p/dp/p-dp-,dp,(dp(p%dp%p,dp,p dp p,dp,p!dp!p#dp#*df*j%dP%p,dp,p*dp*(dp(p%d`%p,dp,p*dp*p/dp/p#dp#p*dp*p(dp(p,dp,p-dp-'dp'p*dp*`,dP,p*dp*p(dp(p,dp,p/dp/-dp-,dp,(dp(-dp-*dp*#dp#dp!dp!p-dp-p*dp*p-dp-p#dp#p(dp(*dp*`-dp-p(dp(p(dp(p,dp,p#dp#p%dp%,dp,p%dp%`'dp'p#dp#p/dp/p,dp,p/dp/p%dp%p*dp*,dp,`/dp/p*dp*p*dp*p.dp.p'dp'p*dP*0"dp"p.dp.p!dp!p$d`$p$dp$"dp"p)dp)p!dp!p&dp&p+dp+p&dp&p.dp.p)dp)+dp+p&dp&`"dp"p'dp'p'dp'p.dp.p'dp'p&dp&2dp2p-dp-`)dp)p+dp+p+dp+p2dp2p+dp+p(dp(4dp4p-dp-`$dp$p"dp"p.dp.p!dp!p$d`$p$dp$"dp"p)dp)p!dp!p&dp&p+dp+p&dp&p.dp.p)dp)+dp+p&dp&`"dp"p'dp'p'dp'p.dp.p'dp'p&dp&2dp2p-dp-`)dp)p'dp'p'dp'p0dp0p'dp'p&dp&2dp2p-dp-`)dp)+dp+.dp.p5dp5p+dp+p3dp3p&dp&p-dp-.dp.`0d0p'd'p,dP,p0dp0p+dp+p*dP*p.dp.p,dp,.dp.p%dp%$dp$p&dp&(dp(p$dp$+dp+p#dp#&dp&p#dp#*dp*#dp#p#dp#!dp!p#dp#$dp$p$dp$-dp-(dp(#dp#*d`*/dp/-dp-&dp&p!dp!%dp%p,dp,p dp p,dp,p!dp!p#dp#*df*j%dP%p,dp,p*dp*(dp(p%d`%p,dp,p*dp*p/dp/p%dp%p,dp,p dp p,dp,p!dp!p#dp#*d`*p%dP%p'dp'p*dp*p(dP(p/dp/p-dp-,dp,(dp(p%dp%p,dp,p dp p,dp,p!dp!p#dp#*df*j%dP%p,dp,p*dp*(dp(p%d`%p,dp,p*dp*p/dp/p#dp#p*dp*p(dp(p,dp,p-dp-'dp'p*dp*`,dP,p*dp*p(dp(p,dp,p/dp/-dp-,dp,(dp(-dp-*dp*#dp#dp!dp!p-dp-p*dp*p-dp-p#dp#p(dp(*dp*`-dp-p(dp(p(dp(p,dp,p#dp#p%dp%,dp,p%dp%`'dp'p#dp#p/dp/p,dp,p/dp/p%dp%p*dp*,dp,`/dp/p*dp*p*dp*p.dp.p'dp'p*dd*\-dp-,dp,+dp+*dp*)dp)p5dp5p3dp3p5dp5p'dp'p,dp,.dp.`1dp1p,dp,p,dp,p0dp0p'dp'p)dp)0dp0p)dp)`+dp+p%dp%p1dp1p,dp,p0dp0p"dp"p&dp&)dp)`,dp,p'dp'p.dp.p3dp3p+dp+p,dp,0dp0p)dp)p'dp'p$dp$)dp)p5dp5p3dp3p5dp5p'dp'p,dp,.dp.`1dp1p,dp,p,dp,p0dp0p'dp'p)dp)0dp0p)dp)`+dp+p%dp%p1dp1p,dp,p0dp0p$dp$p(dp()dp)`.dp.p#dp#p/dp/p*dp*p.dp.p,dp,)dp)p.dp.,dp,&dp&p"dp"!dp!p-dp-p*dp*p-dp-p#dp#p(dp(*dp*`-dp-p(dp(p(dp(p,dp,p#dp#p%dp%,dp,p%dp%`'dp'p#dp#p/dp/p,dp,p/dp/p%dp%p*dp*,dp,`/dp/p*dp*p*dp*p.dp.p'dp'p*dP*0"dp"p.dp.p!dp!p$d`$p$dp$"dp"p)dp)p!dp!p&dp&p+dp+p&dp&p.dp.p)dp)+dp+p&dp&`"dp"p'dp'p'dp'p.dp.p'dp'p&dp&2dp2p-dp-`)dp)p+dp+p+dp+p2dp2p+dp+p(dp(4dp4p-dp-`$dp$p"dp"p.dp.p!dp!p$d`$p$dp$"dp"p)dp)p!dp!p&dp&p+dp+p&dp&p.dp.p)dp)+dp+p&dp&`"dp"p'dp'p'dp'p.dp.p'dp'p&dp&2dp2p-dp-`)dp)p'dp'p'dp'p0dp0p'dp'p&dp&2dp2p-dp-`)dp)+dp+"dp"p.dp.p!dp!p$d`$p$dp$"dp"p)dp)p!dp!p&dp&p+dp+p&dp&p.dp.p)dp)+dp+p&dp&`"dp"p'dp'p'dp'p.dp.p'dp'p&dp&2dp2p-dp-`)dp)p+dp+p+dp+p2dp2p+dp+p(dp(4dp4p-dp-`$dp$p"dp"p.dp.p!dp!p$d`$p$dp$"dp"p)dp)p!dp!p&dp&p+dp+p&dp&p.dp.p)dp)+dp+p&dp&`"dp"p'dp'p'dp'p.dp.p'dp'p&dp&2dp2p-dp-`)dp)p'dp'p'dp'p0dp0p'dp'p&dp&2dp2p-dp-`)dp)+dp+.dp.p5dp5p+dp+p3dp3p&dp&p-dp-.dp.`0d0p'd'p,dP,p0dp0p+dp+p*dP*p.dp.p,dp,.dp.p%dp%$dp$p&dp&(dp(p$dp$+dp+p#dp#&dp&p#dp#*dp*#dp#p#dp#!dp!p#dp#$dp$p$dp$-dp-(dp(#dp#*d`*/dp/-dp-&dp&p!dp!$dp$p&dp&(dp(p$dp$+dp+p#dp#&dp&p#dp#*dp*#dp#p#dp#!dp!p#dp#$dp$p$dp$-dp-(dp(#dp#*d`*/dp/-dp-&dp&p!dp!/MTrkguitar!n@pEP4IdpJ>@:dFd:FQJd>dpJ>@Jd>dpJ>`Kd?dpK?`MdAd`MACdpCPCdpCpCdxCxCdpCpBdpB`BdpBpBdxBxBdpB@dp@P@dp@p@dx@x@dp@p9dEdp9E`Gd;dPG;Dd8dhD8xDd8dxD8xDd8dxD8xDd8dxD88Ed9dPEx9Gd;d`G;p:dFd`:FpFd:d`F:Gd;d`G;p:dFd`:FpFd:d`F: :dFd`:FpJd>dpJ>@:dFd:FQJd>dpJ>@:dFd`:FpJd>dpJ>@:dFd:FQJd>dpJ>@Jd>dpJ>`Kd?dpK?`MdAd`MACdpCPCdpCpCdxCxCdpCpBdpB`BdpBpBdxBxBdpB@dp@P@dp@p@dx@x@dp@pEdpE`EdxExEdxExEdpEpCdpCPCdpCpCdxCxCdpCpBdpB`BdpBpBdxBxBdpB@dp@P@dp@p@dx@x@dp@p9dEdp9E`Gd;dPG;p/MTrk signlead! >n$B2dpJ>PHddpJ>PHddp>pCdpCp?dp?pCdpCpFdpFpDdpDPdpJ>PHddpJ>PHdbp>EbpEJbpJFbpFMbpMRbpRTbpTpAdpAp?dp?pAdpApFdpFpCdpCP?dp?pAdpApCdpCp?dp?pCdpCpFdpFpDdpDPdp>pCdpCp?dp?pCdpCpFdpFpDdpDPFp>pEFpEPAFpApAFpApEFpEpEFpE`CFpC`CFpCPQcpQMcpMHcpHCcpC@cp@Fp>pEFpEPAFpApAFpApEFpEpEFpE`FFpF`CFpCp>cp>EcpEJcpJFcpFMcpMRcpRTcpTp@d`@Dd`DGd`GId I`IdpIJdpJNd`NLdPLpKdPKpGdPGpDd`D@d`@Dd`DGd`GIdPIpKd`KMd@MNdPNPdPPRd@FdpF`RpDdpDpBdpB <p</MTrk str!4PPd PQd QSd@SUd USd SQd@QPd PQd QSd@SUdPUSdPSRdRPdpPpOdOpJdNdJNpLdLpNdNpKR@KLS@LNUPNPVPPNW@LXxNpLxLYL0QdQSd@SQdKQuPdPPNdPNPd`PMdPMNdPNPd`PRdRSd@SNd@NPdPRd`R`OpOQ!pQR0pRT7pTVdVTd@TYd@YVd VTd`TRd@ROd@OQd@QRd@RVdPVTdPTYd`YVd@VQd@QRdPRTdPTOd`OVdVTd@TYd@YVd VTd`TRd@ROd@ORdRTdPTVd`VpVd`VWdPWYd`YpTd TVd`VWd`WYdPYVdPVTdxTRdRpPd PvRd0RpNdPNpOdOpJdNdJNpLdLpNdNpKR@KLS@LNUPNPVPPNW@LXxNpLxLYL0QdQSd@SQdKQuPdPPNdPNPd`PMdPMNdPNPd`PRdRSd@SNd@NPdPRd`R`IdpIKdpKMdpMOdpOPd@POd@OMdMOd@OMd@MPd POd`OMdVMjKd`KMd`MPd@PMd@MKdKMdPMpMd`MOd`OPd@POd@OMdMOd@OMd@MPd POd`OMd@MKd`KMd`MOd@OPd`PRd`RPa@PRa`RSa`SQa@QMa@MQdQSd@SQdKQuPdPPNdPNPd`PMdPMNdPNPd`PRdRSd@SNd@NPdPRd`R VdVTd@TYd@YVd VTd`TRd@ROd@OQd@QRd@RVdPVTdPTYd`YVd@VQd@QRdPRTdPTOd`OVdVTd@TYd@YVd VTd`TRd@ROd@ORdRTdPTVd`VpVd`VWdPWYd`YpTd`Vd@T@VTd@TYd@YVd VTd`TRd@ROd@OQd@QRd@RVdPVTdPTYd`YVd@VQd@QRdPRTdPTOd`OVdVTd@TYd@YVd VTd`TRd@ROd@ORdRTdPTVd`VpVd`VWdPWYd`YpTd TVd`VWd`WYdPYVdPVTdxTRdRpPd PvRd0RpNdPNpOdOpNdNpLdLpNdNpOdOpNdNpLdLpNdNpPd PQd QSd@SUd USd SQd@QPd PQd QSd@SUdPUSdPSRd`R/MTrkfaffa!cePcPf`IdNdQdhINQhLdIdPdhLIPhRdIdMd`dx^x\xYxZxaxdxkxqhRIMxeIdFdxIFxKdFdxKFxHdLdCdhHLCXHdLdCdhHLCxLdHdCdxLHCxCdGdLdpCGLpGdBdJdhGBJhGdBdJdhGBJxJdBdGdxJBGxIdBdGdxIBGxEd@dHdhE@HXEd@dHdhE@Hx@dHdEdx@HExJd@dCdEdpJ@CEpEdJdhEJhSdJdZdGdNdPSJZGNPDIpDIpI$N$pINpLdPdUd@LPUDIpDIpI$N$pINpLdPdUd@LPUQdIdLdhQILhIdLdQdhILQhQdLdIdhQLIhIdLdQdhILQ(DIpDIpI$N$pINpLdPdUd@LPUDIpDIpI$N$pINpLdPdUd@LPU0DdGdLdpDGLpLdGdDdxLGDxLdGdDdxLGDxLdGdDdxLGDIdSdMdPdGdhISMPGhKdFdRdNdhKFRNhRdNdMdIdFdhRNMIFJdMdFdEd`JMFEpJdMdEdFdpJMEF@c@hx`xZx^xbxfxjxoxcJdMdFdEd`JMFEpJdMdEdFdpJMEF`f`JdAdEdhJAEhKdAdFdhKAFhfMdHdCdxaxYxVxZxcxgxqxrxsxtxkxphoxMmxjxgxdx[xUxNxJx@x9x6x0hxx(H`C(f`AdHdFdxAHFxIdFdAdxIFAxKdFdAdxKFAxHdLdCdhHLCXHdLdCdhHLCxLdHdCdxLHCxLdGdCdxLGChGdBdJdhGBJhGdBdJdhGBJxJdBdGdxJBGxIdBdGdxIBGxEd@dHdhE@HXEd@dHdhE@Hx@dHdEdx@HExJd@dCdEdpJ@CEpEdJdhEJhSdJdZdGdNdPSJZGNPDIpDIpI$N$pINpLdPdUd@LPUDIpDIpI$N$pINpLdPdUd@LPU0DdGdLdpDGLpLdGdDdxLGDxLdGdDdxLGDxLdGdDdxLGDIdSdMdPdGdhISMPGhKdFdRdNdhKFRNhRdNdMdIdFdhRNMIFxIdSdMdPdGdhISMPGhKdFdRdNdhKFRNhRdNdMdIdFdhRNMIFJdMdFdEd`JMFEpJdMdEdFdpJMEF@JdMdFdEd`JMFEpJdMdEdFdpJMEF@JdMdFdEd`JMFEpJdMdEdFdpJMEF@JdMdFdEd`JMFEpJdMdEdFdpJMEF@EdAdJdhEAJhFdAdKdhFAKhsMdHdCdpqpixcxgxqxrxsxtxkxphoxMmxjxgxdx[xUxNxJx@x9x6x0hxx(H`C(f`AdHdFdxAHFxIdFdAdxIFAxKdFdAdxKFAxHdLdCdhHLCXHdLdCdhHLCxLdHdCdxLHCxLdGdCdxLGChGdBdJdhGBJhGdBdJdhGBJxJdBdGdxJBGxIdBdGdxIBGxEd@dHdhE@HXEd@dHdhE@Hx@dHdEdx@HExJd@dCdEdpJ@CEpBdEdJdWBEJhBdLd>dEdxBL>ExBdLd>dEdxBL>ExBdLd>dEdxBL>EhHdLdCdhHLCXHdLdCdhHLCxLdHdCdxLHCxLdGdCdxLGChGdBdJdhGBJhGdBdJdhGBJxJdBdGdxJBGxIdBdGdxIBGxEd@dHdhE@HXEd@dHdhE@Hx@dHdEdx@HExJd@dCdEdpJ@CEpEdJdhEJhSdJdZdGdNdPSJZGNf`dx^x\xYxZxaxdxkxq`e`/MTrk1synth01! ULdpLpPdpPpSdpSpUd U`UdpUVdpVZdpZpXdPXpWdPWpSdPSpPdpPpLdpLpPdpPpSdpSpUdPUpWdpWpYdPYpZd`Zp\a`\p^d0^QFpQMFpMHFpHCFpC@Fp@Fp>EFpEJFpJFFpFMFpMRFpRTFpTpAdpAp?dp?pAdpApFdpFpCdpCP?dp?pAdpApCdpCp?dp?pCdpCpFdpFpDdpDPdp>pCdpCp?dp?pCdpCpFdpFpDdpDPFp>EFpEJFpJFFpFMFpMRFpRTFpTpLdpLpPdpPpSdpSpUd U`UdpUVdpVZdpZpXdPXpWdPWpSdPSpPdpPpLdpLpPdpPpSdpSpUdPUpWdpWpYdPYpZd`Zp\a`\p^d0^0/simutrans-124.5/simutrans/music/52-Dreamy-Oriental-Nights.mid000066400000000000000000002215741520675460500241110ustar00rootroot00000000000000MThd MTrkHDreamy Oriental NightsCopyright (C) 2018 RykSebyTCgz URLQB@SetupXpQ Dreamy Oriental NightsX0Q ԣPmusic by RykSebQ 6 QD`BB2CC2D3D4B'B'2C'C'2D'D'2D'3D'4B''B''2QQ#/MTrk System Setup!=---- Reset --------------------------------------------------~ < AB@A<@---- S̐ݒ --------------------------------------------------A---- Drum lj -------------------------------------------------- AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@ AB@  AB@ @---- CH Setup --------------------------------------------------=---- Layer --------------------------------------------------K---- End of System Setup --------------------------------------------------//MTrk Koto!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y k Hl[ ed@&ed@ed  @edG---- End of CH Setup --------------------------------------------------N:Yp:<]p<?Rp?Ad`AC]`C>_`>:X`:?d`?>dp>:[p:7dp7p:dp:7dp75d`55\p53Up35dp57[p73\p35\p57_@7p:[p:<[p<?dp?Ad`ACX`CFd`FCY`CKd`KJdpJF\pFCXpCp?Xp?CdpCAYpAp?Xp?C\pCAcpA?]p?aB>:]5:+?a2?.>\p>:]:Y7U47,:dp7Z":O5R7~5C5[l53Xq5Z3i7U5R3T7Y5X+3F7Z85]7:\qZ*>8:X5:)?YJ?>Xs:R>f7O :7B:`p7[:[5T#7545at3L 5f5T3}5W7Xt75Vy3\*5g3`5\p7V5o7!:cq:(b;{:(\6(=2(.()($`%(B!(#@$`h$x$dp$x$Vx$x$qx$p$d`$"`h"x"dp"x"Vx"x"qx"p"d`" `h x dp x Vx x qx p d` `hxdpxVxxqxpd_$`g$x$dp$x$Vx$x$qx$p$d`$"`h"x"dp"x"Vx"x"qx"p"d`" `h x dp x Vx x qx p d` `pwlpxdpxlpx"pp"#`p#$`h$x$dp$x$Vx$x$qx$p$d`$"`h"x"dp"x"Vx"x"qx"p.dp."dp" `h x dp x Vx x qx x'dx',dp, dp x`pydpxVxxqxpdo+do+$`h$x$dp$x$Vx$x$qx$p$d`$"`h"x"dp"x"Vx"x"qx"p.dp."dp" `h x dp x Vx x qx x'dx',dp, dp `pw+lp+xdpx+lp+x"pp"#`p#$do$q$dp$)dx)+Zx+x$dx$xUx$dh$x"do"q"dp")dx)"Zx"xdxp"dh"x do q dp $dx$+Zx+x$dx$p dh xdoqdp&dx&)Zx)x"dx"pdp+dp+doqdp"dx"$Zx$xdxx$Zx$dhxdoqdp&Zx&dxx"dx"x&Zx&dp"dp" do q dp 'dx'+Zx+x$dx$p dh xdoydpxdx&Zx&)nx)"_x"xdp+dp+Zx$do$q$dp$)dx)+Zx+x$dx$xUx$dh$x"do"q"dp")dx)"Zx"xdxp"dh"x do q dp $dx$+Zx+x$dx$p dh xdoqdp&dx&)Zx)x"dx"pdp+dp+doqdp"dx"$Zx$xdxx$Zx$dhxdoqdp&Zx&dxx"dx"x&Zx&dp"dp" do q dp 'Zx' dx x,dx,'Zx'x dp ,dp,+dp+x&dp&xdpxnx&dx&xnxh Z @,d\x= =H<o;;?:g9978,@d@ d@ %d@%(d@(*d@*#d@#(d@(#d@#d@ d@ %d@%(d@(*d@*,d@,%d@% d@ d@ d@ %d@%(d@(*d@*#d@#(d@(#d@#d@ d@ %d@%(d@(*dp*dx*dx*x%dx%dx%dx%,dp, dx ,dx,x dx 'dx',dx,%dx%x%dx%p%dp%x%dx%x%dx%p%dx%x%dx%%dx%x*dx*,dx,x%dx% dx x%dx% dx x"dx"%dd@(Q=(!;(r8(B6(4(d1(4/(-(U*%@_pxYxxZx*Vx*%^x% ip x [x ,\x,'Up' \x %dx%x%\x%x%fp%(`x(%Vx%x#lx#%bx%x fx #Sx#%\p%ZxxZxx%bx%*^p* bx x rx x _x ,fx,#fx#%bx%'lx'(^x(x(fp(#Xp#(cx(#Ox# Xx x#dx#%Rx%(\x(x,fx,#Kx#apx[xx^x*fx*%`x% Yp x dx ,Zx,'Ux'#`x# Px %]x%x%Xx%x%[x% Nx *Vx*,dx,x ^x %bx%x Zx #_x#%fp%WxxVx%]x%*dp*Xx*\x*x _x x Qx ,dx,'Tx'#[x# Tx %dx%x%Ux%*]x*,dx,x/lx/,bx,x%Xx%,qx,#`x#%mp% [p _pxYxxZx*Vx*%^x% ip x [x ,\x,'Up' \x %dx%x%\x%x%fp%,`x,%Vx%x#lx#%bx%x fx #Sx#%\p%ZxxZxx%bx%*^p* bx x rx x _x ,fx,#fx#%bx%'lx'(^x(x(fp(#Xp#(cx(#Ox# Xx x#dx#%Rx%(\x(x,fx,#Kx#apx[xx^x*fx*%`x% Yp x dx ,Zx,'Ux'#`x# Px %]x%x%Xx%x%[x% Tx *Vx*,dx,x ^x %bx%x Zx #_x#%fp%dxxbx%Xx%*fp*Tx*ix*x rx x ax ,qx,'bx'#hx# \x %dp%x%dx%x%dp%,dx,%dx%x%dx%x%d<@(J=(;(_8()6(t3(>1( /% -(D&(g9 @$`h$x$dp$x$Vx$x$qx$p$d`$"`h"x"dp"x"Vx"x"qx"p"d`" `h x dp x Vx x qx p d` `hxdpxVxxqxpd_$`g$x$dp$x$Vx$x$qx$p$d`$"`h"x"dp"x"Vx"x"qx"p"d`" `h x dp x Vx x qx p d` `pwlpxdpxlpx"pp"#`p#$`h$x$dp$x$Vx$x$qx$p$d`$"`h"x"dp"x"Vx"x"qx"p.dp."dp" `h x dp x Vx x qx x'dx',dp, dp x`pydpxVxxqxpdo+do+$`h$x$dp$x$Vx$x$qx$p$d`$"`h"x"dp"x"Vx"x"qx"p.dp."dp" `h x dp x Vx x qx x'dx',dp, dp `pw+lp+xdpx+lp+x"pp"#dp#@ `$xh aP bP cP dP ex fP gP hP ix jP kP lP mx nP oP pP qx rP sP tP ux vP wP xx yP zP {P |x3? }( ;(a6 ~(82(.(e)(<%(!t($ ) /MTrkVPad!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y 0 AM[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------^ `> "< #FRdFd2 $4 %x &x 'x (4 )x *x +4 ,x -x .x /4 0x 1x 24 3x 4x 5x 64 7x 8x 9x :4 ;x x ?x @x A4 Bx Cx D4 Ex Fx Gx H4 Ix Jx Kx L4 Mx Nx O4 Px Qx Rx S4 Tx Ux V4 Wx Xx Yx Z4 [FRFWdKdWKYdHd`YWd`WVd`VWd`HWTdAdOd@TAORdCdMd@RTd< =2 < << ?< A< D< G< J< L< O< R< T< W< Z< ]< _< b< e< g< j2 l2 p< s< v< y< CMT< m< c< Y< N< D< 9< /< $ !< <  `@ MRdFdd7dF>7RdFdd7dF>7 idFd:>FCd8ddFdAd7>FAHd8d?dCdH8?C7d>dJdFd7>JFdFdMd:>FMCd8ddAdFd@7>AFxFd>d7dxF>7pFd7d>dxF7>O X7d>dFd# U< Q< N< JA H< F< C[ D D< F< G< H< J< K< M< N< O< Q< R< T( U< W< Z< \< _< a c7>F c5dd7d:d@:0d@>?dCd@C70>d8dFd@?>8:dAd@F3dCd@:A?d@3CDd5d@?D5Jd7dFdAd@AJ7Mdd0d:d@F>?dHd@0:?H8dCdRd5d@58C:dAd7d@R:A7Od>d?d7d@7?OTd8dCd@C8<Ad:d7d@7TAVdFd3d@FVWd?d@:3W?[dd7dF>7RdFdd7dF>7 idFd:>FCd8ddFdAd7>FAHd8d?dCdH8?C7d>dJdFd7>JFdFdMd:>FMCd8diAiFi@7>AFxFi>i7ixF>7pFi7i>ixF7>T A8d?dGd @( ?( >( =( <( ;( :( 9( 8( 7( 6( 5( 4 3 2P 3( 4( 5( 6( 7( 8( 9P :( ;( <( =( >( ?( @( AP B( C( D( E( F( G( H( IP J( K( L( M( N( O P P8?G6d=d9d@6=9?d8d;d@;1d@?@dDd@D81?d9dGd@@?9;dBd@G4dDd@;B@d@4DEd6d@@E6Kd8dGdBd@BK8Nd=dDd@GNLd;d@;L=Id9d@DI98dBdGd P(8B?d1d;d Qx R< Sx Tx U< Vx Wx Xx Y< Z(G?@dIdP [x \< ]x ^x _x `< ax bx c(1;@I9dDdSd6d@69D;dBd8d@S;B8Pd?d=d1d@1?@d8d@8@PUd9dDd@D9=Bd;d8d@8UBWdGd4d@GWXd@d@;4X@\d=d9dDd@\=9DZd;dBd8d@Z;B8Xd8dGd=d@=88d@d`8GDd=d`=DXUd6dEd@@U6ESd8dGdBd@S8GUdGd=d?d bp a@ `@ _@ ^ ]@ \< \?GB@d ]P ^P _( `P aP bP cP dP e( fP gP hP iP j( kP lP mP nP oP pU=@ qP rP sP tP u( vP w h6d=d9d@6=9?d8d;d@;1d@?@dDd@D81?d9dGd@@?9;dBd@G4dDd@;B@d@4DEd6d@@E6Kd8dGdBd@BK8Nd=dDd@GNLd;d@;L=Id9d@DI98dBdGd@8B?d1d;d@G?@dId@1;@I9dDdSd6d@69D;dBd8d@S;B8Pd?d=d1d@1?@d8d@8@PUd9dDd@D9=Bd;d8d@8UBWdGd4d@GWXd@d@;4X@\d=d9dDd@\=9DZd;dBd8d@Z;B8Xd8dGd=d@=88d@d`8GDd=d`=DXUd6dEd@@U6ESd8dGdBd$ cH d\ e2 e f2S8GUdGd=dF gd hd id jx kd ld md nx od pd qd rx s< u< v< wP x< y< zP {< |F=GB cRdFdd7dF>7RdFdd7dF>7-/MTrk Keys!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  X\[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------0dp0Cd?PP:PAdS>:A.Px.>L:LA_S>:A.dx.>P:PAdS>:A.Zx.>L:LA_S>:A.dx.>P:PAdS>:A,dp,8Pd:PS5>:+Px+5L>_:LS5>:+dx+5P>d:PS5>:+Zx+5L>_:LS5>:+dx+5P>d:P.dS5>:%./dx/0dp0Cd?PPS:F>.Px.:LF_>LS:F>.dx.:PFd>PS:F>.Zx.:LF_>LS:F>.dx.:PFd>PS:F>,dp,8Pd:PS5>:%+dp+5P>d:P&dS5>:%&)dx)*dx*+dx+5P>d:P`5>:5P>d:P+dp+&d`5>:&+dx+.dx.0dp0Cd?PP:PSC>:.Px.C_>L:LSC>:.dx.Cd>P:PSC>:.Zx.C_>L:LSC>:.dx.Cd>P:PSC>:,dp,8Pd:PS5>:+Px+5L>_:LS5>:+dx+5P>d:PS5>:+Zx+5L>_:LS5>:+dx+5P>d:P.dS5>:%./dx/0dp0Cd?PPS:F>.Px.:LF_>LS:F>.dx.:PFd>PS:F>.Zx.:LF_>LS:F>.dx.:PFd>PS:F>,dp,8Pd:PS5>:%+dp+5P>d:P&dS5>:%&)dx)*dx*+dx+5P>d:P`5>:5X>n:X+nP5>:+p0dp0Cd?PP:PAdS>:A.Px.>L:LA_S>:A.dx.>P:PAdS>:A.Zx.>L:LA_S>:A.dx.>P:PAdS>:A,dp,8Pd:PS5>:+Px+5L>_:LS5>:+dx+5P>d:PS5>:+Zx+5L>_:LS5>:+dx+5P>d:P.dS5>:%./dx/0dp0Cd?PPS:F>.Px.:LF_>LS:F>.dx.:PFd>PS:F>.Zx.:LF_>LS:F>.dx.:PFd>PS:F>,dp,8Pd:PS5>:%+dp+5P>d:P&dS5>:%&)dx)*dx*+dx+5P>d:P`5>:5P>d:P+dp+&d`5>:&+dx+.dx.0dp0Cd?PP:PSC>:.Px.C_>L:LSC>:.dx.Cd>P:PSC>:.Zx.C_>L:LSC>:.dx.Cd>P:PSC>:,dp,8Pd:PS5>:+Px+5L>_:LS5>:+dx+5P>d:PS5>:+Zx+5L>_:LS5>:+dx+5P>d:P.dS5>:%./dx/0dp0Cd?PPS:F>.Px.:LF_>LS:F>.dx.:PFd>PS:F>.Zx.:LF_>LS:F>.dx.:PFd>PS:F>,dp,8Pd:PS5>:%+dp+5P>d:P&dS5>:%&)dx)*dx*+dx+5P>d:P`5>:5X>n:X+nP5>:+p0dp0Cd?PP:PAdS>:A.Px.>L:LA_S>:A.dx.>P:PAdS>:A.Zx.>L:LA_S>:A.dx.>P:PAdS>:A,dp,8Pd:PS5>:+Px+5L>_:LS5>:+dx+5P>d:PS5>:+Zx+5L>_:LS5>:+dx+5P>d:P.dS5>:%./dx/0dp0Cd?PPS:F>.Px.:LF_>LS:F>.dx.:PFd>PS:F>.Zx.:LF_>LS:F>.dx.:PFd>PS:F>,dp,8Pd:PS5>:%+dp+5P>d:P&dS5>:%&)dx)*dx*+dx+5P>d:P`5>:5P>d:P+dp+&d`5>:&+dx+.dx.0dp0Cd?PP:PSC>:.Px.C_>L:LSC>:.dx.Cd>P:PSC>:.Zx.C_>L:LSC>:.dx.Cd>P:PSC>:,dp,8Pd:PS5>:+Px+5L>_:LS5>:+dx+5P>d:PS5>:+Zx+5L>_:LS5>:+dx+5P>d:P.dS5>:%./dx/0dp0Cd?PPS:F>.Px.:LF_>LS:F>.dx.:PFd>PS:F>.Zx.:LF_>LS:F>.dx.:PFd>PS:F>,dp,8Pd:PS5>:%+dp+5P>d:P&dS5>:%&)dx)*dx*+dx+5P:P>d`5:>5X:X+n>nP>5:+x+Z2Z:ZAdx+2:A/MTrkBell!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y b ,V[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------^Fd@FKd@KMd@MRd@R`Td`TOd`ORd`RMd`MOd`OKd`K@Wd`WVd`V`Rd`R`Od`O@Td`TRd`R`Fd@FKd@KMd@MRd@R`Td`TOd`ORd`RMd`MOd`OKd`K@Wd`WVd`V`Rd`R`Od`O@Td`TRd`R p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x p_4x__Kx_ `x_4x__Kx_x x_4x__Kx_x `_Kx_h ,@Fd@FKd@KMd@MRd@R`Td`TOd`ORd`RMd`MOd`OKd`K@Wd`WVd`V`Rd`R`Od`O@Td`TRd`R(^E(jdP^(jFYE(edPY(e/MTrk-Chord!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  .h[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------Px5>x5D>Px5>p5D>Px5>x7D?Px7?x7D?Px7?p7D?Px7?p7D?Px7?x7D?Px7?p7D?Px7?p:P5Dx:5x:P5Dx:5p:P5Dx:5p:P5Dx:5x:P5Dx:5p:P5Dx:5p3DPx5>x5D>Px5>p5D>Px5>p3D>Px3>x3D>Px3>p3D>Px3>p>P3Dx>3p>P3Dx>3p>P3Dx>3?P7Dx?7AP8DxA8xAP8DxA8pAP8DxA8p7DCPx7Cx7DCPx7Cp7DCPx7Cp5D>Px5>x5D>Px5>p5D>Px5>p5D?Px5?x5D?Px5?p5D?Px5?p5DPx5>x5D>Px5>p5D>Px5>p3D>Px3>x3D>Px3>p3D>Px3>p>P3Dx>3p>P3Dx>3p>P3Dx>3?P7Dx?75DPx7>x7D>Px7>p7D>Px7>p5DAPx5Ax5DAPx5Ap5DAPx5Ap5D?Px5?x5D?Px5?p5D?Px5?p5DCPx5Cx5DCPx5Cp5DCPx5Cp5DAPx5Ax5DAPx5Ap5DAPx5Ap5DPx5>x5D>Px5>p5D>Px5>x7D?Px7?x7D?Px7?p7D?Px7?p7D?Px7?x7D?Px7?p7D?Px7?p:P5Dx:5x:P5Dx:5p:P5Dx:5p:P5Dx:5x:P5Dx:5p:P5Dx:5p3DP`7:>/MTrkLead!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y P T`[ edC&ed@ed @edG---- End of CH Setup --------------------------------------------------NMdHAxMHOdJAxOJRdMAxRMOdJAxOJxKdFAxKFMdHA`MHKdFA`KFMdHAxMHOdJA`OJJdEA\JEHdCA\HCFdAAxFAHdCA`HCAdAxC>FdAAhFAHdCA`HCHOdJAxOJMdHAxMHFdAAhFAHdCA`HCHAdAxC>FnAGhFAHdCA`HCFnAGpFAHdCA`HCOsJJpOJMcH?`MHKhFCxKFMnHG`MHOnJG`OJRnMGxed p RMxMnHGMHxKnFG`KFMnHG`MHOnJG k=P? @:OJxHnCGpHCxCn>GpC>xFnAG`FAHnCG`HCKnFG`KFMnHG`MHOnJG`OJRnMG`RMVnQG">*>>>R>f>z>?"?b?@GVQRVGQ.q?WnRGVQVWR@VnQGpVQOnJGpOJxMnHGXMHpHnCGpHCJnEGpJEKnFGpKFRnMGpRMxOnJGXOJpFnAGpFAHnCGpHCJnEGpJEKnFGpKFxv?FnAGXFA`@RnMGpRMTnOGpTO[nVGX[VYnTG`YTYnTG`YTWnRG`WRYnTG`YTRnMG`RMTnOG`TOOnJG`OJMnHG`MHOnJG`OJRnMGHRMxFnAGHFAxWnRGHWRxKnFG`KFxJnEGxJEKnFGxKFMnHGxMH[nVGY=2> ?c? @_[VpOnJGpOJxRnMGpRMxOnJGpOJx@MnHGpMHx@OnJGpOJFnAGxFAxFGKnpFKxJnEGpJExFnAG`FAAnG`C>xHnCG`HCHnCGpHCKnFG`KFHnCG`HCKnFG`KFMnHG`MHOnJG`OJxTnOG`TOTnOGpTOWnRGpTnOGWRPTOWnRG`WRYnTG`YTw;[nVG7<w<6=v=6>v>6?u?@s@ [VxYnTG`YTWnRG`WRRnMG`RMOnJG`OJMnHG`MHKnFG`KFHnCG`HCFnAG`FACn>G`C>OnJG`OJOnJG`OJ@ped @MdHAxMHOdJAxOJRdMAxRMOdJAxOJxKdFAxKFMdHA`MHKdFA`KFMdHAxMHOdJA`OJJdEA\JEHdCA\HCFdAAxFAHdCA`HCAdAxC>FdAAhFAHdCA`HCHOdJAxOJMdHAxMHFdAAhFAHdCA`HCHAdAxC>FnAGhFAHdCA`HCFnAGpFAHdCA`HCOsJJpOJMcH?`MHKhFCxKFMnHG`MHOnJG`OJRnMGxed p RMxMnHGMHxKnFG`KFMnHG`MHOnJG P=L? @:OJxHnCGpHCxCn>GpC>xFnAG`FAHnCG`HCKnFG`KFMnHG`MHOnJG`OJRnMG`RMVnQG >>+>A>W>m>??_?@GVQRVGQ.WnRGVQVWRYnTGpYTVnQGpVQWnRGxWRRnMGXRMpKnFGpKFTnOGpTORnMGpRMMnHGpMHFnAGxFAOnJGXOJpHnCGpHCJnEGpJEMnHGpMHKnFGpKFx@Cn>Gi@s?f?X?K?=?0?#???z>m>`>R>D>7>*>>> >i=G=%==b<@<<{;Y;7;;s:Q:/: :k9I9@9^8|7786+5(#4y3P3'3~2T2+22X1/11]030 0=0J/V.b-n,z++*) ) z' '&9%!+%eC>@?n:Gh?:FnAGPFAxHnCGxHCJnEGxJEKnFG`KFFnAGhFAMnHGXMHxRnMGxRMTnOGxTOVnQGxVQWnRG<=>>a? @wWRRnMGhRMYnTGpYTpRnMGpRM8>`n[GW>?^? @ @/`[@^nYG@^Yx|=cn^G>S>?Q?|?@`c^xbn]Gxb][nVGH[Vx`n[GP`[x^nYGx^YWnRGHWRxRnMG`RM^nYG`^YC;[nVG <P<=\=">i>/?t?@s@q?{?x?u?q?n?j?g?d?`?]?Y?V?S?O?L?H?E?B?>?;?8?s[V T@SnNGXSNxSnNGNSN*SnNGDSNSnNGNSN*SnNGCSNiTnOG;TOUnPGNUP*PnKGNPK*SnNGNSN*NnIGNNI*PnKGNPK*KnFGNKF*LnGGNLG*GnBGNGB*InDGNID*Dn?GND?*GnBGNGB*Bn=GNB=*Dn?GND?*=n8Gx=8XnSGTXS$GnBGTGB$WnRGTWR$GnBGTGB$XnSGdXSGnBGTGB$SnNGTSN$XnSGTXS$GnBGTGB$WnRGTWR$GnBGTGB$XnSGdXSGnBGTGB$NnIGTNI$ZnUGTZU$_nZGxedL_Zan\GTa\$\nWG<\W SnNGTSN$WnRGTWR$XnSGTXS$GnBGTGB$WnRGTWR$GnBGTGB$XnSGTXS$SnNGTSN$GnBGTGB$XnSGTXS$GnBGTGB$WnRGTWR$GnBGTGB$XnSGTXS$SnNGTSN$GnBGTGB$XnSGTXS$GnBGTGB$WnRGLWR$XnSGTXSZnUGTZU$GnBGTGB$\nWGT\W$XnSG(XSHXnSG:XS>XnSG:XS>XnSG:XS>LnGGmLG XnSG:XS>XnSG:XS>_nZGH_ZSnNGxSN @an\Gha\@InDGxIDNnIG`NISnNG`SN@XnSG`XS @dn_Gxd_ed&?cn^G(=(4;(W918U5"3n0;.=-[(z# g9Qic^@ped P TpMdHAxMHOdJAxOJRdMAxRMOdJAxOJxKdFAxKFMdHA`MHKdFA`KFMdHAxMHOdJA`OJJdEA\JEHdCA\HCFdAAxFAHdCA`HCAdAxC>FdAAhFAHdCA`HCHOdJAxOJMdHAxMHFdAAhFAHdCA`HCHAdAxC>FnAGhFAHdCA`HCFnAGpFAHdCA`HC:?OsJJRnMG ?<2?<^?n@H DRM/MTrk{Arp!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y Q .i[ edB&ed@ed @edG---- End of CH Setup --------------------------------------------------NGdTG$SdTS$IdTI$UdTU$LdTL$XdTX$NdTN$ZdTZ$NdTN$ZdTZ$PdTP$\dT\$PdTP$\dT\$KdTK$WdTW$KdTK$WdTW$GdTG$SdTS$GdTG$SdTS$LdTL$XdTX$LdTL$XdTX$KdTK$WdTW$GdTG$SdTS$DdTD$PdTP$IdTI$IdTI$SdTS$IdTI$PdTP$IdTI$NdUN#BdUB#NdUN#BdUB#BdUB#NdUN#LdUL#BdUB#NdUN#BdUB#PdUP#DdUD#LdUL#@dU@#NdUN#BdUB#DdUD#PdUP#DdUD#PdUP#DdUD#PdUP#DdUD#PdUP#DdUD#PdUP#GdTG$SdTS$IdTI$UdTU$LdTL$XdTX$NdTN$ZdTZ$UdTU$NdTN$PdTP$\dT\$WdTW$PdTP$SdTS$_dT_$\dT\$SdTS$PdTP$\dT\$WdTW$PdTP$XdTX$ddTd$\dT\$SdTS$WdTW$cdTc$SdTS$_dT_$PdTP$\dT\$UdTU$XdTX$UdTU$\dT\$XdTX$UdTU$NdTN$ZdTZ$NdTN$ZdTZ$LdTL$XdTX$PdTP$\dT\$ZdTZ$NdTN$XdTX$LdTL$UdTU$IdTI$SdTS$GdTG$IdTI$UdTU$IdTI$UdTU$IdTI$UdTU$IdTI$PdTP${?IdTI$DdTD$=dT=$8dT8$v?1dT1$=dT=$8dT8$1dT1Q/MTrk!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y Q 98[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------@  FP2FF 9FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*FF FP2FF \FF2FF 9Fd*F2  FP2F2 { \FF2F2 w 9Fdd sFF2 o FP2F2 k \FF2F2 h 9Fdd dFF2 ` FP2F2 \ \FF2F2 X 9Fdd TFF2 P FP2F2 L \FF2F2 H 9Fdd DFF2 @ FP2F2 = \FF2F2 9 9Fdd 5FF2 1 FP2F2 - \FF2F2 ) 9Fdd %FF2 ! FP2F2  \FF2F2  9Fdd FF2  FP2F2  \FF2F2  9Fdd FF2 < /MTrkMQDrum!D---- System Setup --------------------------------------------------<@---- CH Setup --------------------------------------------------y  @[ ed@&ed@ed @edG---- End of CH Setup --------------------------------------------------3<<336SA<3S3<<336SA<3S3<<336SA<3S439<3 3F QA23 Q 3[<3~&j<&<&M<&4&L<&<&j<&<&N<&<.O<.<&O<&<2d,O4d$d<2,4$40d<0<0d$F<0$<&d<&T><`T< ``8T8<`T< `3T3<`T< ``.T.<`T< `)T)<`T< ``%T%<`T< ` T <`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< `` T <`T< ` T <`T< `` T <`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``%T%<`T< `+T+<`T< ``1T1<`T< `6T6<`T< ``DTD<`T< `RTR<`T< ``fTf<`T< `XTX<`T< ``QTQ<`T< `JTJ<`T< ``DTD<`T< `>T><`T< ``8T8<`T< `3T3<`T< ``.T.<`T< `)T)<`T< ``%T%<`T< ` T <`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< `` T <`T< ` T <`T< `` T <`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``%T%<`T< `+T+<`T< ``1T1<`T< `6T6<`T< ``DTD<`T< `RTR<`T< ``fTf<`T< `XTX<`T< ``QTQ<`T< `JTJ<`T< ``DTD<`T< `>T><`T< ``8T8<`T< `3T3<`T< ``.T.<`T< `)T)<`T< ``%T%<`T< ` T <`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< `` T <`T< ` T <`T< `` T <`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``%T%<`T< `+T+<`T< ``1T1<`T< `6T6<`T< ``DTD<`T< `RTR<`T< ``fTf<`T< `XTX<`T< ``QTQ<`T< `JTJ<`T< ``DTD<`T< `>T><`T< ``8T8<`T< `3T3<`T< ``.T.<`T< `)T)<`T< ``%T%<`T< ` T <`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< `` T <`T< ` T <`T< `` T <`T< `T<`T< ``T<`T< `T<`T< `aUU>T><`T< ``8T8<`T< `3T3<`T< ``.T.<`T< `)T)<`T< ``%T%<`T< ` T <`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< `` T <`T< ` T <`T< `` T <`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``%T%<`T< `+T+<`T< ``1T1<`T< `6T6<`T< ``DTD<`T< `RTR<`T< ``fTf<`T< `XTX<`T< ``QTQ<`T< `JTJ<`T< ``DTD<`T< `>T><`T< ``8T8<`T< `3T3<`T< ``.T.<`T< `)T)<`T< ``%T%<`T< ` T <`T< ``T<`T< `T<`T< ``T<`T< `T<`T< ``T<`T< `T<`T< `` T <`T< ` T <`T< `` T <`T< `T<`T< ``T<`T< `T<`T)/simutrans-124.5/simutrans/music/53-Where-Thomassons-Lie.mid000066400000000000000000000506141520675460500235740ustar00rootroot00000000000000MThd MTrk\XYT Q*x1XQ*x2x3x4 x5 x6x7/MTrkXInst 1x P?PZ;4?Z>4>Z?4?Z>4AZ?Z?4:Z>46Z:44Z643Z4<1Z3<7Z148Z747Z848Z74:Z8<;Z:<7Z;48Z747Z8x7Z7x8Z7x:Z8<;Z:<7Z;x8Z7x:Z8<;Z:x7Z;<8Z7x:Z8<;Z:x=Z;x?Z=<@Z?xI $xOP O(KP K(QP Q(OPH?p?H>xIH?p?HIxIH?pCHIxBHCxCHB<@HCP?P=4GZF4FZG4GZF4IZGFGd>GF<>FGZ>/MTrk1EInst 1xE EhOP O(KP K(QP Q(OPH?p?H>x?p?Hx?pCHxBHCxCHB<@HC 2.) Simutrans version: -> 3.) Description of the found problem: -> 4.) How can this problem be reproduced? (Note: if the problem can't be reproduced, there is only a very small chance that it can be fixed.) -> 5.) What did you/the game do when the problem occurred? (Only needed if (4) is left blank.) -> 6.) Effect on gameplay of the found problem? -> 7.) Did you discover a workaround for the problem? -> 8.) Severity of the problem/urgency to fix it? (One of high, medium, low or a more detailed description) -> simutrans-124.5/simutrans/readme.txt000066400000000000000000000231201520675460500176050ustar00rootroot00000000000000----------------------------------------------------------------------------- The most common error is the attempt to start the program without installing a graphics set. This is not possible! *** PLEASE MAKE SURE TO HAVE INSTALLED A PAK-SET TOO! *** Simutrans cannot start without the graphics in the pak set! ----------------------------------------------------------------------------- The purpose of this document is to explain the configuration options of Simutrans. There are currently two styles of options: 1) entries in the simuconf.tab file 2) command line options Simuconf.tab ------------ Simuconf.tab contains a rather large number of general configuration settings for Simutrans. Simuconf.tab can be found at several locations. First searched is config/simuconf.tab. Next is pak/config/simuconf.tab (or whatever pak-path your specified. Finally, simutrans/simuconf.tab in your home directory ("My Documents" under Windows resp. "~" under Unix). It is a text file and can be edited with a text editor. Each entry has a comment explaining what the entry is good for and what values are allowed for this entry. cityrules.tab ------------- cityrules.tab is either located pak/config/cityrules.tab or in simutrans/cityrules.tab in your user directory. It is a text file and can be edited with a text editor. cityrules.tab contains the set of rules which determines the extension of towns during city growth. There are two city related settings in the top, the minimum distance between cities (minimum_city_distance) and the step whenever a new industry will be founded (industry_increase_every). Set this to zero, if no new industry should be founded during a game. The actual rules are of two types: house building rules and road building rules. Each rule has a probability (house_xyz.chance) between -1 and -7 and a square with describes allowed surroundings for this house rule. The square is composed by ASCII rows, with each symbol standing for: S = must not be road s = must be road n = must be bare land H = must not be house h = must be house T = must not be a stop t = must be a stop U = way must not be buildable on slope u = way must be buildable on slope . = matches anything Simutrans command line options ------------------------------ Alphabetical list of options: -addons -announce -debug n=1..4 -freeplay -fullscreen -help -lang en -load bla[.sve] -log n=0|1 -noaddons -nomidi -nosound -objects path/ -pause -res n=1..5 -screensize 600x350 -server -server_dns IP/name -server_name text -server_admin_pw pwd -singleuser -timeline n=0|1 -use_workdir Detailed Description -------------------- 1.) Choose a resolution: 1a.) Choose a 'standard' resolution simutrans -res n where n is one of 1,2,3,4,5 The switch -res chooses the resolution at program start: 1 = 640x480 2 = 800x600 3 = 1024x768 4 = 1280x1024 Resolution no. 5 runs Simutrans in a window instead of fullscreen mode (Windows version only) THIS SWITCH IS DEPRECATED AND ONLY KEPT FOR COMPATIBILITY! Please use -screensize WxH instead (see section 1b) ! Use this switch at your own risk! Using a wrong resolution may damage your monitor! Up to date monitors should support all resolutions, but older monitors may have problems with the higher resolution settings. Don't expect all resolutions to be available with your graphics card. The program will abort the starting process if the graphics card doesn't support the chosen resolution. The default resolution was 640x480. Simutrans version 0.78.4 and newer have a default resolution of 800x600. Please note that bigger resolutions slow down the game. If Simutrans runs too slowly on your system, use a smaller screen resolution. Windowed mode (-res 5 on Windows) may be slower than fullscreen, but this depends strongly on your resolution and machine. 1b.) Choose an arbitrary resolution simutrans -screensize 900x400 More general the syntax is -screensize WIDTHxHEIGHT where width and height are integral values separated by a small x. This format is strict. Simutrans will not tolerate spaces or other format elements. I suggest only to use widths being a multiple of 16 and even heights, other may cause problems. Full screen support by -fullscreen. Use with care! I'm sure you can screw up things if you use values which are not supported by your system. Use on your own risk ... i.e. using -screensize 1600x1400 might damage your monitor if the graphics card switches to that resolution, but your monitor cannot! I give absolutely no warranty for any results of using that option!!! Please note that if width is set below 640, parts of the toolbar will not be visible and can't be used. In general setting width to less than 640 and height to less than 400 will most likely cause trouble ... the program does not check the values! As said above, use with care, no warranties are given! 2.) Copy output messages into a logfile: simutrans -log 1 This writes all messages which are output after the game switches to graphics mode to a file named "simu.log". This file might be helpful when reporting problems, you might consider to log a replay of the problem and attach the log file to your bug report at the simutrans forum. Please zip (compress) large log files before sending them. Don't forget your explanation of the problem is much more important than the log file when reporting problems. The -debug switch turns on additional debug messages. The recommended combination is "-log 1 -debug 3" Behind -debug you can give a debug level. 1 only errors [default], 2 logs also warnings, 3 all messages and 4 will generate a division by zero on a fatal error to allow for a backtrace. 3.) Run Simutrans in free playing mode (bankrupt check turned off): simutrans -freeplay 4.) Getting help simutrans -h simutrans -? simutrans -help simutrans --help All print a quick help message, which contains all supported command line options. 5.) starting a server (without port number, it will be on port 13353 simutrans -server [portnr] It is recommended to start server-games without addons with the pak set without modifications. If you want to announce your server, you need to supply a valid dns name or IPv4 or IPv6 [] number via the commandline option like "-server_dns myserver.homunix.org:13355" Similarly you can set server name via -server_name "Testserver". More server related settings can be specified in simuconf.tab. 6.) Use alternative PAK files If you have an alternative PAK file set for Simutrans, such as the winter scenario, you can use the following command line argument: simutrans -objects winter_pak/ [-addons|-noaddons] "winter_pak" is the directory where the PAK files are stored. Use the name of your setup instead! The trailing slash is mandatory. If you have a non-portable installation (i.e. no single user mode), then you can have additional add-ons in your personal simutrans directory. That way those are kept, when updating the main pak sets. You can additionally specify "-addons" or "-noaddons" to force loading or prevent addons to be loaded with a pakset. This overrides the setting in simuconf.tab. "-noaddons" is default. If you specify "-use_workdir" the simutrans environment from the current directory will be taken. Otherwise simutrans will take the environment from the directory where the executable is located. 7) Turn sound and music off (as of Simutrans 0.81.23exp) 7a) Turn sound off simutrans -nosound 7b) Turn music off simutrans -nomidi This also helps if Simutrans crashes on startup due to buggy or incompatible sound drivers. This is a problem especially in 64 bit Linux. 8) Switch on/off multiplayer -singleuser Will not search for files in your home directory. Simutrans will write all files in its data directory (usually the same directory as the directory containing the executable), which must not be write protected. 9) Timeline and startyear Example: simutrans -timeline 1 -startyear 1950 9b) Timeline simutrans -timeline <1 or 0> 0 = all vehicles are available from the start of the game 1 = More realistic. In 1930 are only some historical vehicles available. New vehicles will be introduced from time to time. Example: simutrans -timeline 1 -startyear 1930 Default value: setting use_timeline in simuconf.tab 9b) Start year simutrans -startyear Sets the starting year of the game Example: simutrans -timeline 1 -startyear 1950 Default value : setting starting_year in simuconf.tab 10) load a game and do not show the initial window simutrans -load mygame.sve If you use -pause, then the loaded game will be paused. Same would happen as a server when using the pause without connections feature. 11) define a language on startup using the iso codes simutrans -lang en All the above mentioned options can be combined, i.e. simutrans -res 2 -log -objects winter_pak/ runs Simutrans in test mode, writes output to "simu.log", switches to a resolution of 800x600 pixels and uses the winter pak files. Not all options are available on all platforms, i.e. -async and -net are only supported on Linux/X-Windows. If you run into problems, or if you have questions, please visit the feedback forum: https://forum.simutrans.com/ Suggestions how to improve Simutrans are also always welcome. If you're unsure how to report problems or change request, there are two example forms packaged with simutrans. Use "problem_report.txt" for problem reports and "change_request.txt" for change requests if you send reports by email. There are some Simutrans related web pages, you may want to visit them: The official Simutrans home page: https://www.simutrans.com This readme file was written by Hansjrg Malthaner, November 2000, Updated 8-Sep-2010 by Markus Pristovsek Last update 16-Dec-2011 by Dwachs simutrans-124.5/simutrans/script/000077500000000000000000000000001520675460500171155ustar00rootroot00000000000000simutrans-124.5/simutrans/script/ai_base.nut000066400000000000000000000010301520675460500212220ustar00rootroot00000000000000/** * Base file for AI players */ /** * initialization, called when AI player starts * @param pl player number of our AI player */ function start(pl) { } /** * the heart-beat of the AI player, * called regularly, does not need to finish */ function step() { } /** * initialization, called when savegame is loaded * default behavior: calls start() * @param pl player number of our AI player */ function resume_game(pl) { start(pl) } /** * Happy New Month and Year! */ function new_month() { } function new_year() { } simutrans-124.5/simutrans/script/hm_lib/000077500000000000000000000000001520675460500203475ustar00rootroot00000000000000simutrans-124.5/simutrans/script/hm_lib/hm_bridge_tl.nut000066400000000000000000000025121520675460500235160ustar00rootroot00000000000000include("hm_lib/hm_global") include("hm_lib/hm_functions") include("hm_lib/hm_find_obj") include("hm_lib/hm_obj_selector") class hm_bridge_tl extends hm_base_tl { desc_name = null start = null ziel = null wtype = null constructor(d_name, s, z, wt = null) { desc_name = d_name start = coord3d(s[0],s[1],s[2]) ziel = coord3d(z[0],z[1],z[2]) wtype = wt hm_commands.append(this) } // returns [error_message, desc] // selector and finder are not available // because get_desc() does not exist for a bridge object. function _get_desc(){ local d = hm_get_bridge_desc(desc_name, wtype) if(d==null) { local message = format(translate("Bridge %s (%s) is not found!"), translate(desc_name), desc_name) return [message, null] } return [null, d] } function exec(player, origin) { local dr = _get_desc() if(dr[0]!=null) { return dr[0] // there was a error in obtaining the desc } local desc = dr[1] local err = command_x.build_bridge(player, origin+start, origin+ziel, desc) if(err!=null) { //calc_route() failed to find a path. local message = format(translate("Bridge building path from (%s) to (%s) is not found!"), (origin+start).tostring(), (origin+ziel).tostring()) return message } else { return null } } } simutrans-124.5/simutrans/script/hm_lib/hm_city_tl.nut000066400000000000000000000035351520675460500232400ustar00rootroot00000000000000include("hm_lib/hm_global") class hm_city_tl extends hm_base_tl { population = 0 desc_name = 0 pos = null rotation = 0 constructor(pop, dn, p, r) { population = pop desc_name = dn pos = coord3d(p[0],p[1],0) rotation = r hm_commands.append(this) } function exec(player, origin) { local tp = origin + pos local tile = square_x(tp.x,tp.y).get_ground_tile() if(tile) { return command_x(tool_add_city).work(player, tile, format("%d,%s,%d", population, desc_name, rotation)) } return "No suitable ground!" } } class hm_city_set_population_tl extends hm_base_tl { pos = null population = 0 constructor(pop, p) { pos = coord(p[0],p[1]) population = pop hm_commands.append(this) } function exec(player, origin) { local tp = origin+pos local tile = square_x(tp.x,tp.y).get_ground_tile() if(tile) { return command_x(tool_change_city_size).work(player, tile, format("%d", population)) } return "No suitable ground!" } } class hm_set_owner_tl extends hm_base_tl { pos = null ownerid = 0 has_z = false constructor(o, p) { pos = coord3d(p[0],p[1],0) if(p.len()>2) { pos.z = p[2] has_z = true } ownerid = o hm_commands.append(this) } function exec(player, origin) { local tp = origin+pos local tile = square_x(tp.x, tp.y).get_tile_at_height(tp.z) if(tile == null && !has_z) { tile = square_x(tp.x, tp.y).get_ground_tile() } if(tile) { local pl = player_x(ownerid) return command_x(tool_set_owner).work(player, tile, format("%d,0",ownerid)) // foreach(obj in tile.get_objects()) { // // owner to0l // } } if(tile.has_ways() && ownerid == 15) { // no way to convert to pavement roads right now } return null } } simutrans-124.5/simutrans/script/hm_lib/hm_depot_tl.nut000066400000000000000000000035431520675460500234020ustar00rootroot00000000000000include("hm_lib/hm_global") class hm_depot_tl extends hm_base_tl { desc = null desc_name = null pos = null wtype = null constructor(sta_name, p, wt = null) { desc_name = sta_name pos = coord3d(p[0],p[1],p[2]) wtype = wt // find descriptor /* foreach (d in building_desc_x.get_building_list(building_desc_x.depot)) { if(d.get_name()==desc_name) { desc = d break } }*/ hm_commands.append(this) } // returns [error_message, desc] function _get_desc(){ if(desc_name.slice(0,2)=="?f") { local key_str = "d" + desc_name.slice(2) local d = hm_found_desc.get(key_str) if(d==null) { local message = format(translate("Depot key %s is not defined."), desc_name.slice(2)) return [message, null] } else if(d[0]==null) { local message = format(translate("No depot was detected between %s."), hm_found_desc.get_pos_str(key_str)) return [message, null] } return [null, d[0]] } else if(desc_name.slice(0,2)=="?s") { local idx = desc_name.slice(2).tointeger() local d = hm_depot_selector().get_desc(idx) if(d==null) { local message = format(translate("Selected depot %s is not available."), desc_name.slice(2)) return [message, null] } return [null, d] } else { local d = hm_get_building_desc(desc_name, wtype, building_desc_x.depot) if(d==null) { local message = format(translate("Depot %s (%s) is not found!"), translate(desc_name), desc_name) return [message, null] } return [null, d] } } function exec(player, origin) { local dr = _get_desc() if(dr[0]!=null) { return dr[0] // there was a error in obtaining the desc } local desc = dr[1] return command_x.build_depot(player, origin+pos, desc); } } simutrans-124.5/simutrans/script/hm_lib/hm_find_obj.nut000066400000000000000000000025761520675460500233470ustar00rootroot00000000000000include("hm_lib/lib_obj_finder_v2") // get(key) returns [desc, start_pos, end_pos] class hm_found_desc { found_descs = {} function set(key, desc, start, end) { found_descs[key] <- [desc, start, end] } function get(key) { if(key in found_descs) { return found_descs[key] } return null } function get_pos_str(key) { if(key in found_descs) { local e = found_descs[key] return "("+e[1][0]+","+e[1][1]+","+e[1][2]+") and ("+e[2][0]+","+e[2][1]+","+e[2][2]+")" } return null } } // key prefix: "w" function hm_find_way(key, start, end) { local desc = ObjFinder(player, [start, end]).findWay() hm_found_desc().set("w" + key, desc, start, end) } // key prefix: "s" function hm_find_sign(key, start, end) { local desc = ObjFinder(player, [start, end]).findSign() hm_found_desc().set("s" + key, desc, start, end) } // key prefix: "p" function hm_find_station(key, start, end) { local desc = ObjFinder(player, [start, end]).findPlatform() hm_found_desc().set("p" + key, desc, start, end) } // key prefix: "c" function hm_find_wayobj(key, start, end) { local desc = ObjFinder(player, [start, end]).findWayObj() hm_found_desc().set("c" + key, desc, start, end) } // key prefix: "d" function hm_find_wayobj(key, start, end) { local desc = ObjFinder(player, [start, end]).findDepot() hm_found_desc().set("d" + key, desc, start, end) } simutrans-124.5/simutrans/script/hm_lib/hm_functions.nut000066400000000000000000000134121520675460500235740ustar00rootroot00000000000000 this.hm_all_waytypes <- [wt_road,wt_rail,wt_water,wt_monorail,wt_maglev,wt_tram,wt_narrowgauge,wt_air,wt_power] this.hm_all_systemtypes <- [st_flat, st_elevated, st_tram, st_runway] function hm_get_way_desc(desc_name, wt, st) { local obj = null //gui.add_message_at(player, "hm_get_way_desc " + desc_name + " wt " + wt + " st " + st, world.get_time()) if ( wt == null || st == null ) { // not set waytype and/or systemtype // object not found, break script foreach(wt in hm_all_waytypes) { foreach (st in hm_all_systemtypes) { foreach (w in way_desc_x.get_available_ways(wt, st)) { if(w.get_name()==desc_name) { obj = w break } } } } } else { // set waytype and systemtype // object not available then replace local list = way_desc_x.get_available_ways(wt, st) foreach (w in list) { //gui.add_message_at(player, "w.get_name() " + w.get_name(), world.get_time()) if(w.get_name()==desc_name) { obj = w //gui.add_message_at(player, "found desc_name " + obj.get_name(), world.get_time()) break } if ( obj == null ) { obj = w //gui.add_message_at(player, "fallback " + obj.get_name(), world.get_time()) } } } return obj } function hm_get_bridge_desc(desc_name, wt) { local obj = null if ( wt == null ) { // not set waytype // object not found, break script foreach(wt in hm_all_waytypes) { foreach (b in bridge_desc_x.get_available_bridges(wt)) { if(b.get_name()==desc_name) { obj = b break } } } } else { // set waytype // object not available then replace local list = bridge_desc_x.get_available_bridges(wt) foreach (b in list) { if(b.get_name()==desc_name) { obj = b break } if ( obj == null ) { obj = b } } } return obj } function hm_get_sign_desc(desc_name, wt, sigtype) { local obj = null if ( wt == null || sigtype == null) { // not set waytype // object not found, break script foreach(wt in hm_all_waytypes) { foreach (s in sign_desc_x.get_available_signs(wt)) { if(s.get_name()==desc_name) { obj = s break } } } } else { // set waytype and signal type // object not available then replace local list = sign_desc_x.get_available_signs(wt) foreach (s in list) { if(s.get_name()==desc_name) { obj = s break } if ( obj == null ) { local found = false switch ( sigtype ) { case 1 : found = s.is_one_way() break case 2 : found = s.is_choose_sign() break case 4 : found = s.is_private_way() break case 8 : found = s.is_signal() break case 10 : // signal and choose found = s.is_choose_sign() break case 16 : found = s.is_pre_signal() break case 64 : found = s.is_longblock_signal() break case 128 : found = s.is_end_choose_signal() break case 256 : found = s.is_priority_signal() break case 512 : found = s.is_traffic_light() break default : // does not know how to handle that combination (like minimum speed and oneway) } if ( found ) { obj = s } } } } return obj } function hm_get_wayobjs_desc(desc_name, wt, overhead) { local obj = null if ( wt == null ) { // not set waytype // object not found, break script foreach(wt in hm_all_waytypes) { foreach (s in wayobj_desc_x.get_available_wayobjs(wt)) { if(s.get_name()==desc_name) { obj = s break } } } } else { // set waytype // object not available then replace local list = wayobj_desc_x.get_available_wayobjs(wt) foreach (s in list) { if(s.get_name()==desc_name) { obj = s break } if ( obj == null ) { obj = s } else if ( obj.get_topspeed() < 90 && s.get_topspeed() > 90 ) { obj = s } if ( overhead == 1 && !obj.is_overhead_line() ) { obj = null } } } return obj } /** * search player building object * * @param desc_name object name * @param wt waytype * @param building_type building_desc_x::building_type * * @todo harbour, flat_harbour, station_extension */ function hm_get_building_desc(desc_name, wt, building_type) { local obj = null local goods = {} local list = building_desc_x.get_building_list(building_type) // searche waytypes foreach (b in list) { if(b.get_name()==desc_name && (b.is_available(world.get_time()) || world.use_timeline() == false) && (wt == b.get_waytype() || wt == null) ) { obj = b break } if ( obj == null && (b.is_available(world.get_time()) || world.use_timeline() == false) ) { //gui.add_message_at(player, "waytype: " + b.get_waytype(), world.get_time()) if ( b.get_waytype() == wt ) { if( building_type == b.get_type() && (wt == b.get_waytype() || wt == null) ) { obj = b //gui.add_message_at(player, "fallback " + obj.get_name(), world.get_time()) } } } } if ( obj == null && wt == null && building_type != building_desc_x.depot ) { // not set waytype -> 0 searches all ways foreach (b in building_desc_x.get_building_list(building_type)) { if(b.get_name()==desc_name && (b.is_available(world.get_time()) || world.use_timeline() == false) ) { obj = b break } } } return obj } simutrans-124.5/simutrans/script/hm_lib/hm_global.nut000066400000000000000000000004611520675460500230240ustar00rootroot00000000000000this.hm_commands <- [] // array to store the commands this.player <- null class hm_slope { FL = 0 N1 = 4 N2 = 8 S1 = 36 S2 = 72 E1 = 28 E2 = 56 W1 = 12 W2 = 24 UP = 82 //UP2 = 92 DOWN = 83 //DN2 = 93 } class hm_base_tl { function exec(player, origin) { return null } } simutrans-124.5/simutrans/script/hm_lib/hm_house_tl.nut000066400000000000000000000021201520675460500234000ustar00rootroot00000000000000include("hm_lib/hm_global") class hm_headquarter_tl extends hm_base_tl { level = 0 pos = null constructor(l, p, r) { level = l pos = coord3d(p[0],p[1],p[2]) // rotation ignored for now hm_commands.append(this) } function exec(player, origin) { local hq_builder = command_x(tool_headquarter) if(player.get_headquarter_level() <= level) { return hq_builder.work(player, pos+origin, null) } return format("Headquarter already at higher level %d versus %d", player.get_headquarter_level(), level) } } class hm_house_tl extends hm_base_tl { desc_name = 0 pos = null z = 256 rotation = 0 constructor(dn, p, r) { desc_name = dn pos = coord(p[0],p[1]) rotation = r hm_commands.append(this) } function exec(player, origin) { local tp = pos + origin local tile = square_x(tp.x,tp.y).get_ground_tile() // house only on ground if(tile) { return command_x(tool_build_house).work(player, tile, format("1%d%s", rotation, desc_name)) } return "No suitable ground!" } } simutrans-124.5/simutrans/script/hm_lib/hm_misc_tl.nut000066400000000000000000000017571520675460500232270ustar00rootroot00000000000000include("hm_lib/hm_global") class hm_rotate_building_tl extends hm_base_tl { pos = null constructor(p) { pos = coord3d(p[0],p[1],p[2]) hm_commands.append(this) } function exec(player, origin) { local tl = command_x(tool_rotate_building) return tl.work(player, origin+pos) } } class hm_error_message_tl extends hm_base_tl { text = null constructor(s) { text = s hm_commands.append(this) } function exec(player, origin) { return text } } class hm_chat_message_tl extends hm_base_tl { text = null pos = null constructor(s, p) { text = s pos = coord3d(p[0], p[1], p[2]) hm_commands.append(this) } function exec(player, origin) { gui.add_message_at(player, text, origin+pos); return null } } class hm_exec_func_tl extends hm_base_tl { func = null param = null constructor(f, p) { func = f param = p hm_commands.append(this) } function exec(player, origin) { return func(player, origin, param) } } simutrans-124.5/simutrans/script/hm_lib/hm_obj_selector.nut000066400000000000000000000055111520675460500242370ustar00rootroot00000000000000 class hm_selector_base { messages = [] // text to show when users select the way descs = [] function get_desc(idx) { // given idx can be out of range. return idx <= descs.len() ? descs[idx-1] : null } function need_selection() { return descs.len()tp.z && err == null) { err = command_x.set_slope(player, tile, hm_slope.DOWN) tile = square_x(tp.x, tp.y).get_ground_tile() } if(err) { return err } return command_x.set_slope(player, tp, slope) } } /** * * */ class hm_test_area_tl extends hm_base_tl { start = null ziel = null terraform = null constructor(s, z, t = null) { start = coord3d(s[0],s[1],s[2]) ziel = coord3d(z[0],z[1],z[2]) terraform = t hm_commands.append(this) } function exec(player, origin) { // ground check local s_tile = origin+start local e_tile = origin+ziel local count_x = abs(s_tile.x - e_tile.x) + 1 local count_y = abs(s_tile.y - e_tile.y) + 1 local check_z = false if ( terraform == 0 ) { check_z = true } //gui.add_message_at(player_x(1), "count_x " + count_x, world.get_time()) //gui.add_message_at(player_x(1), "count_y " + count_y, world.get_time()) local test_st = s_tile if ( s_tile.x >= e_tile.x && s_tile.y >= e_tile.y ) { test_st = e_tile } local err = null for ( local i = 0; i < count_x; i++ ) { local t = square_x(test_st.x+i, test_st.y).get_ground_tile() //gui.add_message_at(player, "tile " + coord3d_to_string(t), world.get_time()) for ( local j = 0; j < count_y; j++ ) { local tile = square_x(t.x, test_st.y+j).get_ground_tile() local tile_tree = tile.find_object(mo_tree) local tile_groundobj = tile.find_object(mo_groundobj) local tile_moving_object = tile.find_object(mo_moving_object) if ( tile.is_empty() ) { // tile is empthy //err = null } else if ( tile_tree != null || tile_groundobj != null || tile_moving_object != null ) { // tile have tree, groundobj or moving_obj // These are optical objects and do not prevent construction. //err = null } else { // The tile has objects that prevent construction. err = "not_free" //gui.add_message_at(player_x(1), "tile is not free " + coord3d_to_string(tile), world.get_time()) break } if ( check_z ) { //gui.add_message_at(player_x(1), "tile terraform test " + coord3d_to_string(tile), world.get_time()) if ( s_tile.z != tile.z || tile.get_slope() > 0) { err = "terraform" break } } } if ( err != null ) { break } } if ( err == "not_free" ) { // tile is not free local message = translate("Selected area not free for build!") return message } else if ( err == "terraform" ) { // terraform necessary local message = translate("The terrain is not suitable for construction.") return message } else { return null } } } simutrans-124.5/simutrans/script/hm_lib/hm_station_tl.nut000066400000000000000000000034171520675460500237500ustar00rootroot00000000000000include("hm_lib/hm_global") class hm_station_tl extends hm_base_tl { desc = null desc_name = null pos = null wtype = 0 rotation = 15 building = 34 // generic stop constructor(sta_name, p, wt = 0, r = 15, b = 34) { desc_name = sta_name pos = coord3d(p[0],p[1],p[2]) wtype = wt rotation = r hm_commands.append(this) building = b } // returns [error_message, desc] function _get_desc(){ if(desc_name.slice(0,2)=="?f") { local key_str = "p" + desc_name.slice(2) local d = hm_found_desc.get(key_str) if(d==null) { local message = format(translate("Station key %s is not defined."), desc_name.slice(2)) return [message, null] } else if(d[0]==null) { local message = format(translate("No station was detected between %s."), hm_found_desc.get_pos_str(key_str)) return [message, null] } return [null, d[0]] } else if(desc_name.slice(0,2)=="?s") { local idx = desc_name.slice(2).tointeger() local d = hm_station_selector().get_desc(idx) if(d==null) { local message = format(translate("Selected station %s is not available."), desc_name.slice(2)) return [message, null] } return [null, d] } else { local d = hm_get_building_desc(desc_name, wtype, building) if(d==null) { local message = format(translate("Station %s (%s) is not found!"), translate(desc_name), desc_name) return [message, null] } return [null, d] } } function exec(player, origin) { local dr = _get_desc() if(dr[0]!=null) { return dr[0] // there was a error in obtaining the desc } local desc = dr[1] return command_x.build_station(player, origin+pos, desc, rotation); } } simutrans-124.5/simutrans/script/hm_lib/hm_tunnel_tl.nut000066400000000000000000000020271520675460500235700ustar00rootroot00000000000000include("hm_lib/hm_global") include("hm_lib/hm_functions") include("hm_lib/hm_find_obj") include("hm_lib/hm_obj_selector") class hm_tunnel_tl extends hm_base_tl { desc_name = null start = null ziel = null constructor(d_name, s, z) { desc_name = d_name start = coord3d(s[0],s[1],s[2]) ziel = coord3d(z[0],z[1],z[2]) hm_commands.append(this) } function exec(player, origin) { local tool = command_x(tool_build_tunnel) local sp = origin+start local tile = tile_x(sp.x, sp.y, sp.z); local moWay = tile.find_object(mo_tunnel); local err = null if(!moWay) { // build tunnel entrance tool.set_flags(2) err = tool.work(player, sp, desc_name) } if(!err) { err = tool.work(player, origin+start, origin+ziel, desc_name) } if(err!=null) { //calc_route() failed to find a path. return "Tunnel building path from (" + (origin+start).tostring() + ") to (" + (origin+ziel).tostring() + ") is not found!" } else { return null } } } simutrans-124.5/simutrans/script/hm_lib/hm_way_tl.nut000066400000000000000000000052731520675460500230710ustar00rootroot00000000000000include("hm_lib/hm_global") include("hm_lib/hm_functions") include("hm_lib/hm_find_obj") include("hm_lib/hm_obj_selector") class hm_way_tl extends hm_base_tl { desc_name = null start = null startz = 256 finish = null finishz = 256 wtype = null stype = null constructor(d_name, s, z, wt = null, st = null) { desc_name = d_name start = coord3d(s[0],s[1],0) if(s.len()>2) { start.z = s[2] startz = 0 } finish = coord3d(z[0],z[1],0) if(z.len()>2) { finishz = finish.z = z[2] } wtype = wt stype = st hm_commands.append(this) } // returns [error_message, desc] function _get_desc(){ if(desc_name.slice(0,2)=="?f") { local key_str = "w" + desc_name.slice(2) local d = hm_found_desc.get(key_str) if(d==null) { local message = format(translate("Way key %s is not defined."), desc_name.slice(2)) return [message, null] } else if(d[0]==null) { local message = format(translate("No way was detected between %s."), hm_found_desc.get_pos_str(key_str)) return [message, null] } return [null, d[0]] } else if(desc_name.slice(0,2)=="?s") { local idx = desc_name.slice(2).tointeger() local d = hm_way_selector().get_desc(idx) if(d==null) { local message = format(translate("Selected way %s is not available."), desc_name.slice(2)) return [message, null] } return [null, d] } else { local d = hm_get_way_desc(desc_name, wtype, stype) //gui.add_message_at(player, "_get_desc - hm_get_way_desc() " + d, world.get_time()) if(d==null) { local message = format(translate("Way %s (%s) is not found!"), translate(desc_name), desc_name) return [message, null] } return [null, d] } } function exec(player, origin) { local dr = _get_desc() if(dr[0]!=null) { return dr[0] // there was a error in obtaining the desc } local desc = dr[1] local stp = start+origin if(startz==256) { local tile = square_x(stp.x,stp.y).get_ground_tile() if(tile == null) { return "No suitable ground!" } stp = coor3d(tile); } local ftp = finish+origin if(finishz==256) { local tile = square_x(ftp.x,ftp.y).get_ground_tile() if(tile == null) { return "No suitable ground!" } ftp = coord3d(tile); } local err = command_x.build_way(player, stp, ftp, desc, true) if(err!=null) { //calc_route() failed to find a path. local message = format(translate("Way building path from (%s) to (%s) is not found!"), (stp).tostring(), (ftp).tostring()) return message } return null } } simutrans-124.5/simutrans/script/hm_lib/hm_wayobj_tl.nut000066400000000000000000000042241520675460500235570ustar00rootroot00000000000000include("hm_lib/hm_global") // valid only for wt_rail class hm_wayobj_tl extends hm_base_tl { desc_name = null start = null ziel = null wtype = null overhead = null constructor(d_name, s, z, wt = null, oh = null) { desc_name = d_name start = coord3d(s[0],s[1],s[2]) ziel = coord3d(z[0],z[1],z[2]) wtype = wt overhead = oh hm_commands.append(this) } // returns [error_message, desc] function _get_desc(){ if(desc_name.slice(0,2)=="?f") { local key_str = "c" + desc_name.slice(2) local d = hm_found_desc.get(key_str) if(d==null) { local message = format(translate("Wayobj key %s is not defined."), desc_name.slice(2)) return [message, null] } else if(d[0]==null) { local message = format(translate("No wayobj was detected between %s."), hm_found_desc.get_pos_str(key_str)) return [message, null] } return [null, d[0]] } else if(desc_name.slice(0,2)=="?s") { local idx = desc_name.slice(2).tointeger() local d = hm_wayobj_selector().get_desc(idx) if(d==null) { local message = format(translate("Selected wayobj %s is not available."), desc_name.slice(2)) return [message, null] } return [null, d] } else { local d = hm_get_wayobjs_desc(desc_name, wtype, overhead) //gui.add_message_at(player, "_get_desc - hm_get_way_desc() " + d, world.get_time()) if(d==null) { local message = format(translate("Wayobj %s (%s) is not found!"), translate(desc_name), desc_name) return [message, null] } return [null, d] } } function exec(player, origin) { local dr = _get_desc() if(dr[0]!=null) { return dr[0] // there was a error in obtaining the desc } local desc = dr[1] local err = command_x.build_wayobj(player, origin+start, origin+ziel, desc) if(err!=null) { //calc_route() failed to find a path. local message = format(translate("Wayobj building path from ($s) to (%s) is not found!"), (origin+start).tostring(), (origin+ziel).tostring()) return message } else { return null } } } simutrans-124.5/simutrans/script/hm_lib/lib_obj_finder_v2.nut000066400000000000000000000100301520675460500244270ustar00rootroot00000000000000// originally obtained from https://github.com/128na/sugoi-simutrans-squirrel-scripts/blob/master/lib_obj_finder_v2.nut at 2021/01/20 // some functions were modified by himeshi class ObjFinder { player = null; area = null; constructor(pl, a = [ [0, 0, -2], [10, 10, 2] ]) { player = pl; area = a; } // 指定値間の数値をイテレートする function _stepGenerator(from, to) { if (from < to) { for (local i = from; i <= to; i++) { yield i; } } else { for (local i = from; i >= to; i--) { yield i; } } } // 指定座標空間をイテレートする function _coord3dGenerator() { foreach(x in _stepGenerator(area[0][0], area[1][0])) { foreach(y in _stepGenerator(area[0][1], area[1][1])) { foreach(z in _stepGenerator(area[0][2], area[1][2])) { yield coord3d(x, y, z); } } } } // 指定名のオブジェクトが指定座標空間にないときのメッセージを返す function getMissingObjText(type) { return "範囲" + "[" + coord3d(area[0][0], area[0][1], area[0][2]) + "] - [" + coord3d(area[1][0], area[1][1], area[1][2]) + "]" + "に" + type + "がありません。"; } // 軌道を指定座標空間から探す function findWay() { foreach(pos in _coord3dGenerator()) { local tile = tile_x(pos.x, pos.y, pos.z); gui.add_message_at(player, "findWay() coord :" + coord3d_to_string(tile), world.get_time()) local moWay = tile.find_object(mo_way); local hasOwnedWay = moWay && moWay.get_owner().get_name() == player.get_name(); if (hasOwnedWay) { return moWay.get_desc(); } } } // 架線を指定座標空間から探す function findWayObj() { foreach(pos in _coord3dGenerator()) { local tile = tile_x(pos.x, pos.y, pos.z); local moWobj = tile.find_object(mo_wayobj); local hasOwnedWay = moWobj && moWobj.get_owner().get_name() == player.get_name(); if (hasOwnedWay) { return moWobj.get_desc(); } } } // 標識を指定座標空間から探す function findSign() { foreach(pos in _coord3dGenerator()) { local tile = tile_x(pos.x, pos.y, pos.z); local moSign = tile.find_object(mo_signal); if (moSign) { return moSign.get_desc(); } local moRSign = tile.find_object(mo_roadsign); if (moRSign) { return moRSign.get_desc(); } } } // プラットフォームを指定座標空間から探す function findPlatform() { foreach(pos in _coord3dGenerator()) { local tile = tile_x(pos.x, pos.y, pos.z); local moBld = tile.find_object(mo_building); local hasOwnedPlatform = moBld && moBld.get_owner().get_name() == player.get_name(); if (hasOwnedPlatform) { return moBld.get_desc(); } } } // マーカーを指定座標空間から探す function findLabel() { foreach(pos in _coord3dGenerator()) { local tile = tile_x(pos.x, pos.y, pos.z); local obj = tile.find_object(mo_label); local hasOwnedWay = obj && obj.get_owner().get_name() == player.get_name(); if (hasOwnedWay) { return obj; } } } // depot function findDepot() { foreach(pos in _coord3dGenerator()) { local tile = tile_x(pos.x, pos.y, pos.z); local moBld = tile.find_object(mo_depot); local hasOwnedDepot = moBld && moBld.get_owner().get_name() == player.get_name(); if (hasOwnedDepot) { return moBld.get_desc(); } } } } simutrans-124.5/simutrans/script/hm_toolkit_v3.nut000066400000000000000000000015151520675460500224300ustar00rootroot00000000000000 include("hm_lib/hm_way_tl") include("hm_lib/hm_wayobj_tl") include("hm_lib/hm_bridge_tl") include("hm_lib/hm_tunnel_tl") include("hm_lib/hm_slope_tl") include("hm_lib/hm_sign_tl") include("hm_lib/hm_station_tl") include("hm_lib/hm_depot_tl") include("hm_lib/hm_remove_tl") include("hm_lib/hm_find_obj") include("hm_lib/hm_misc_tl") include("hm_lib/hm_obj_selector") include("hm_lib/hm_house_tl") include("hm_lib/hm_city_tl") function init(pl) { this.player = pl hm_build() return true } function work(pl, pos) { local os = hm_obj_selector() if(os.need_selection()) { return os.select_obj(pl, pos) } // obj selection eneded or is not needed. foreach (cmd in hm_commands) { local err = cmd.exec(pl, pos) if(err!=null && err.len()>0) { return err } } return null } function exit(pl) { return true } simutrans-124.5/simutrans/script/new_scenario_template.nut000066400000000000000000000055421520675460500242220ustar00rootroot00000000000000/** * Template file for scenarios * it contains a minimal interface */ /// specify the savegame to load map.file = "savegame.sve" /// short description to be shown in finance window /// and in standard implementation of get_about_text scenario.short_description = "Template Scenario" scenario.author = "User" scenario.version = "0.0" scenario.translation = "The Unknown Translators" /// scenario relies on this version of the api scenario.api = "120.0" /** * These functions should return text strings (or ttext instances) * the text is shown in the corresponding tab in the scenario info window * all text can be dynamic and change with progress in scenario * * These information can be customized per player. * Players are identified by their numbers: pl == 0 is the first player slot, * pl == 1 is the 'Public player' */ function get_info_text(pl) { // General information } function get_rule_text(pl) { // Rules to be obeyed } function get_result_text(pl) { // Display current standing } function get_goal_text(pl) { // Describe the goals to be achieved to win } // Commented out: Will use implementation of scenario_base.nut, // which needs scenario.short_description, scenario.author, scenario.version // // function get_about_text(pl) // { // // Information about author, version, etc // } // Commented out: Will use implementation of scenario_base.nut, // which needs scenario.short_description // // function get_short_description(pl) // { // // Short string will be displayed in finance window // } /** * Called upon start of scenario, initialize global variables here */ function start() { } /** * Main function: returns percentage of completion * If returned value >= 100 then scenario is won * If less than zero scenario is lost */ function is_scenario_completed(pl) { return 100 // complete } /** * Called after loading a savegame of a played scenario */ function resume_game() { } /** * Called at the beginning of a new month */ function new_month() { } /** * Called at the beginning of a new year */ function new_year() { } /** * Table that contains data that will be saved in savegame * only plain data is saved: no classes / instances / functions, no cyclic references */ persistent = {} // Attention: do not call API functions here in global scope. // If you do so, they will be called before the savegame is loaded, // and result in undefined behavior. /** * Called when user clicks to build. * Error messages are sent back over network to clients. * Does not work with waybuilding, use the rules.forbid_* functions in this case. * * @param pos is a table with coordinate { x=, y=, z=} * @param tool is a table with current tool properties {coord3d start_pos, bool is_drag_tool, bool is_ctrl, bool is_shift} * @return null if allowed, an error message otherwise */ function is_work_allowed_here(pl, tool_id, pos, tool) { return null } simutrans-124.5/simutrans/script/scenario_base.nut000066400000000000000000000062011520675460500224410ustar00rootroot00000000000000/** * Base file for scenarios */ // declare arrays and slots map <- {} map.file <- "" map.settings <- {} // some meta info scenario <- {} scenario.short_description <- "This is a random scripted scenario" scenario.author <- "" scenario.version <- "" scenario.translation <- "" scenario.api <- "112.3" // table to hold routines for forbidding/allowing player tools rules <- {} // table containing all waytypes all_waytypes <- [wt_road, wt_rail, wt_water, wt_monorail, wt_maglev, wt_tram, wt_narrowgauge, wt_air, wt_power] // placeholder array for all the map editing tools map.editing_tools <- [ tool_add_city, tool_change_city_size, tool_land_chain, tool_city_chain, tool_build_factory, tool_link_factory, tool_lock_game, tool_build_cityroad, tool_increase_industry, tool_step_year, tool_fill_trees, tool_set_traffic_level, dialog_edit_factory, dialog_edit_attraction, dialog_edit_house, dialog_edit_tree, dialog_enlarge_map] // forbidden tools // default: map editing tools, switch player scenario.forbidden_tools <- [tool_switch_player] foreach(tool_id in map.editing_tools) { scenario.forbidden_tools.append(tool_id) } /** * Called when filling toolbars, activating tools * Results are not transferred over network, use the rules.forbid_* functions in this case * * @param name is parameter (string) i.e. description for way tools * @return 1 if allowed, null otherwise */ function is_tool_allowed(pl, tool_id, wt, name) { if (pl == 1) return true return scenario.forbidden_tools.find( tool_id )==null; // null => not found => allowed } /** * Called when user clicks to build etc. * Error messages are sent back over network to clients. * Does not work with waybuilding etc use the rules.forbid_* functions in this case. * * @param name is parameter (string) i.e. description for way tools * @param pos is a table with coordinate { x=, y=, z=} * @return null if allowed, an error message otherwise */ function is_work_allowed_here(pl, tool_id, name, pos, tool) { return null } function is_schedule_allowed(pl, schedule) { return null } // declare getter functions function get_map_file() { return map.file; } function get_short_description(pl) { return scenario.short_description; } function get_api_version() { return ("api" in scenario) ? scenario.api : "112.3" } function get_about_text(pl) { return "Scenario: " + scenario.short_description + "

Author: " + scenario.author + "

Version: " + scenario.version + "

Translation: " + scenario.translation } function get_rule_text(pl) { return "Do what you want." } function get_goal_text(pl) { return "The way is the target." } function get_info_text(pl) { return "Random scenario." } function get_result_text(pl) { return "You are owned." } function get_debug_text(pl) { return debug.get_forbidden_text() } /** * initialization, called when scenario starts */ function start() { } /** * initialization, called when savegame is loaded * default behavior: calls start() */ function resume_game() { start() } /** * Happy New Month and Year! */ function new_month() { } function new_year() { } simutrans-124.5/simutrans/script/script_base.nut000066400000000000000000000256661520675460500221620ustar00rootroot00000000000000/** * Base file for scripting */ // table to hold routines for debug debug <- {} function min(a, b) { return a < b ? a : b } function max(a, b) { return a > b ? a : b } /** * load / save support * the persistent table will be written / restored during save / load * only plain data is saved: no classes / instances / functions, no cyclic references */ persistent <- {} /** * writes the persistent table to a string */ function save() { if (typeof(persistent) != "table") { throw("error during saving: the variable persistent is not a table") } local str = "persistent = " + recursive_save(persistent, "\t", [ persistent ] ) return str } function is_identifier(str) { return (str.toalnum() == str) && (str[0] < '0' || str[0] > '9') } function recursive_save(table, indent, table_stack) { local isarray = typeof(table) == "array" local str = (isarray ? "[" : "{") + "\n" foreach(key, val in table) { str += indent if (!isarray) { if (typeof(key)=="string") { if (is_identifier(key)) { str += key + " = " } else { str += "[\"" + key + "\"] = " } } else { str += "[" + key + "] = " } } while( typeof(val) == "weakref" ) val = val.ref switch( typeof(val) ) { case "null": str += "null" break case "integer": case "float": case "bool": str += val break case "string": str += "@\"" + val + "\"" break case "array": case "table": if (!table_stack.find(val)) { table_stack.push( table ) str += recursive_save(val, indent + "\t", table_stack ) table_stack.pop() } else { // cyclic reference - good luck with resolving str += "null" } break case "generator": str += "null" break case "instance": default: if ("_save" in val) { str += val._save() break } str += "\"unknown(" + typeof(val) + ")\"" } if (str.slice(-1) != "\n") { str += ",\n" } else { str = str.slice(0,-1) + ",\n" } } str += indent.slice(0,-1) + (isarray ? "]" : "}") + "\n" return str } ///////////////////////////////////// /** * translatable texts */ class ttext { text_raw = "" // raw text text_tra = "" // translated text // tables with information to replace variables {key} by their values var_strings = null var_values = null // these cannot be initialized here to {}, otherwise // all instances will refer to the same table! constructor(k) { if (type(k) != "string") { throw("ttext must be initialized with string") } text_raw = k text_tra = k var_strings = [] var_values = {} find_variables(text_raw) } function get_translated_text() { return translate(text_raw) } function find_variables(k) { var_strings = [] // find variable tags local reg = regexp(@"\{([a-zA-Z_]+[a-zA-Z_0-9]*)\}"); local start = 0; while (start < k.len()) { local res = reg.capture(k, start) if (res) { local vname = k.slice(res[1].begin, res[1].end) var_strings.append({ name = vname, begin = res[0].begin, end = res[0].end }) start = res[0].end } else { start = k.len(); } } } function _set(key, val) { var_values[key] <- val } function _tostring() { // get translated text local text_tra_ = get_translated_text() if (text_tra_ != text_tra) { text_tra = text_tra_ // new text, search for identifiers find_variables( text_tra ) } // create array of values, sort it per index in string local values = [] foreach(val in var_strings) { local key = val.name if ( key in var_values ) { local valx = { begin = val.begin, end = val.end, value = var_values[key] } values.append(valx) } } values.sort( @(a,b) a.begin <=> b.begin ) // create the resulting text piece by piece local res = "" local ind = 0 foreach( e in values) { res += text_tra.slice(ind, e.begin) res += e.value ind = e.end } res += text_tra.slice(ind) return res } } /** * text from files */ class ttextfile extends ttext { file = null constructor(file_) { file = file_ base.constructor("") } function get_translated_text() { return load_language_file(file) } } ///////////////////////////////////// function _extend_get(index) { if (index == "rawin" || index == "rawget") { throw null // invoke default delegate return } local fname = "get_" + index if (fname in this) { local func = this[fname] if (typeof(func)=="function") { return func.call(this) } } throw null // invoke default delegate } /** * this class implements an extended get method: * everytime an index is not found it tries to call the method 'get_'+index */ class extend_get { _get = _extend_get } ///////////////////////////////////// class coord { x = -1 y = -1 constructor(_x, _y) { x = _x; y = _y } function _add(other) { return coord(x + other.x, y + other.y) } function _sub(other) { return coord(x - other.x, y - other.y) } function _mul(fac) { return coord(x * fac, y * fac) } function _div(fac) { return coord(x / fac, y / fac) } function _unm() { return coord(-x, -y) } function _typeof() { return "coord" } function _tostring() { return coord_to_string(this) } function _save() { return "coord(" + x + ", " + y + ")" } function href(text) { return "
" + text + "" } } class coord3d extends coord { z = -1 constructor(_x, _y, _z) { x = _x; y = _y; z = _z } function _add(other) { return coord3d(x + other.x, y + other.y, z + getz(other)) } function _sub(other) { return coord3d(x - other.x, y - other.y, z - getz(other)) } function _mul(fac) { return coord3d(x * fac, y * fac, z * fac) } function _div(fac) { return coord3d(x / fac, y / fac, z / fac) } function _unm() { return coord3d(-x, -y, -z) } function _typeof() { return "coord3d" } function _tostring() { return coord3d_to_string(this) } function _save() { return "coord3d(" + x + ", " + y + ", " + z + ")" } function href(text) { return "" + text + "" } function getz(other) { return ("z" in other) ? other.z : 0 } } /** * class that contains data to get access to an in-game factory */ class factory_x extends coord { _get = _extend_get function _tostring() { return "factory_x@" + coord_to_string(this) } /// input / output slots, will be filled by constructor input = {} output = {} // constructor is implemented in c++ } /** * class that contains data to get access to an production slot of a factory */ class factory_production_x extends extend_get { /// coordinates of factory x = -1 y = -1 good = "" /// name of the good to be consumed / produced index = -1 /// index to identify an io slot max_storage = 0 /// max storage of this slot scaling = 0 constructor(x_, y_, n_, i_) { x = x_ y = y_ good = n_ index = i_ } } /** * class to provide access to the game's list of all factories */ class factory_list_x { /// meta-method to be called in a foreach loop function _nexti(prev_index) { } /// meta method to retrieve factory by index in the global C++ array function _get(index) { } } /** * class that contains data to get access to an in-game player company */ class player_x extends extend_get { nr = 0 /// player number function _tostring() { return "player_x@" + nr } constructor(n_) { nr = n_ } } /** * class that contains data to get access to an in-game halt */ class halt_x extends extend_get { id = 0 /// halthandle_t function _tostring() { return "halt_x@" + id } constructor(i_) { id = i_ } } /** * class that contains data to get access to a line of convoys */ class line_x extends extend_get { id = 0 /// linehandle_t function _tostring() { return "line_x@" + id } constructor(i_) { id = i_ } } /** * class to provide access to line lists */ class line_list_x { halt_id = 0 player_id = 0 } /** * class that contains data to get access to a tile (grund_t) */ class tile_x extends coord3d { _get = _extend_get function _tostring() { return "tile_x@" + coord3d_to_string(this) } function get_objects() { return tile_object_list_x(x,y,z) } } class tile_object_list_x { /// coordinates x = -1 y = -1 z = -1 constructor(x_, y_, z_) { x = x_ y = y_ z = z_ } } /** * class that contains data to get access to a grid square (planquadrat_t) */ class square_x extends coord { _get = _extend_get function _tostring() { return "square_x@" + coord_to_string(this) } } /** * class to provide access to convoy lists */ class convoy_list_x { use_world = 0 halt_id = 0 line_id = 0 } /** * class that contains data to get access to an in-game convoy */ class convoy_x extends extend_get { id = 0 /// convoihandle_t function _tostring() { return "convoy_x@" + id } constructor(i_) { id = i_ } } /** * class to provide access to the game's list of all cities */ class city_list_x { /// meta-method to be called in a foreach loop function _nexti(prev_index) { } /// meta method to retrieve city by index in the global C++ array function _get(index) { } } /** * class that contains data to get access to a city */ class city_x extends coord { _get = _extend_get function _tostring() { return "city_x@" + coord_to_string(this) } } /** * class to access in-game settings */ class settings { } /** * base class of map objects (obj_t) */ class map_object_x extends coord3d { _get = _extend_get function _tostring() { return "map_object_x@" + coord_to_string(this) } } class schedule_entry_x extends coord3d { function _tostring() { return "schedule_entry_x@" + coord3d_to_string(this) } /// load percentage load = 0 /// waiting wait = 0 } class dir { static none = 0 static north = 1 static east = 2 static northeast = 3 static south = 4 static northsouth = 5 static southeast = 6 static northsoutheast = 7 static west = 8 static northwest = 9 static eastwest = 10 static northeastwest = 11 static southwest = 12 static northsouthwest = 13 static southeastwest = 14 static all = 15 static nsew = [1, 4, 2, 8] } class slope { static flat=0 static north = 3+1 ///< North slope static west = 9+3 ///< West slope static east = 27+1 ///< East slope static south = 27+9 ///< South slope static northwest = 27 ///< NW corner static northeast = 9 ///< NE corner static southeast = 3 ///< SE corner static southwest = 1 ///< SW corner static raised = 80 ///< special meaning: used as slope of bridgeheads static all_up_slope = 82 ///< used for terraforming tools static all_down_slope = 83 ///< used for terraforming tools } class time_x { raw = 1 year = 0 month = 1 } class time_ticks_x extends time_x { ticks = 0 ticks_per_month = 0 next_month_ticks = 0 } /** * The same metamethod magic as in the class extend_get. */ table_with_extend_get <- { _get = _extend_get } /** * table to hold routines to access the world */ world <- {} world.setdelegate(table_with_extend_get) // table to hold routines for gui access gui <- {} simutrans-124.5/simutrans/script/script_compat.nut000066400000000000000000000023411520675460500225140ustar00rootroot00000000000000/** * File with compatibility functions to run scripts written for earlier versions of the api. */ function compat(version) { if (version == "*") { return; // wants bleeding edge stuff } local f = type(version)=="string" ? version.tofloat() : 112.3; // default version if (f <= 112.3) { f = 112.3 } // sorted in descending order w.r.t. api version local all_versions = [ { v = 120.1, f = compat_120_1}, { v = 112.3, f = compat_112_3}, ] foreach(vt in all_versions) { if (f - 0.01 <= vt.v) { vt.f.call(this) } } } function compat_112_3() { print("Compatibility mode for version 112.3 in effect") // convoy_list_x() deprecated, set default value to iterate through global list convoy_list_x.use_world <- 1 } function compat_120_1() { print("Compatibility mode for version 120.1 in effect") // 120.1 broke player_x::is_active (r7603) player_x.is_active <- function() { try { this.get_name() // will throw if no player with this number exists return true } catch(ev) { return false } } // gui.add_message got extra parameter (r7890) gui.add_message_new <- gui.add_message gui.add_message <- function(text) { gui.add_message_new( player_x(-1) /*will be set to active player*/, text) } } simutrans-124.5/simutrans/script/tool_base.nut000066400000000000000000000024221520675460500216140ustar00rootroot00000000000000// stubs for functions needed by scripted tools function init(player) { return true } function exit(player) { return true } // one-click tools function work(player, pos) { return null } // two-click tools function do_work(player, start, end) { return null } function mark_tiles(player, start, end) { } /** * Can the tool start/end on pos? If it is the second click, start is the position of the first click * 0 = no * 1 = This tool can work on this tile (with single click) * 2 = On this tile can dragging start/end * 3 = Both (1 and 2) * */ function is_valid_pos(player, start, pos) { return 2 } // dummy auxiliary function, will be regularly called function step() { // do not implement } // check whether all functions support the flags parameter function correct_missing_flags_argument() { if (work.getinfos().parameters.len() == 3) { work_old <- work work = function(player, pos, flags) { return work_old(player, pos) } } if (do_work.getinfos().parameters.len() == 4) { do_work_old <- do_work do_work = function(player, start, end, flags) { return do_work_old(player, start, end) } } if (mark_tiles.getinfos().parameters.len() == 4) { mark_tiles_old <- mark_tiles mark_tiles = function(player, start, end, flags) { mark_tiles_old(player, start, end) } } } simutrans-124.5/simutrans/text/000077500000000000000000000000001520675460500165755ustar00rootroot00000000000000simutrans-124.5/simutrans/text/be.tab000066400000000000000000000567051520675460500176700ustar00rootroot00000000000000§Belaruskaja PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: be Belaruskaja # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable выкл. cl_btn_filter_enable укл. cl_btn_filter_settings Настаўленні cl_btn_sort_asc узыходна cl_btn_sort_desc сыходна cl_btn_sort_income даход cl_btn_sort_name назва cl_btn_sort_type тып clf_btn_alle усё clf_btn_invers інв. clf_btn_keine нічога gl_btn_sort_bonus прэмія gl_btn_sort_name назва gl_btn_sort_revenue прыбытак gl_btn_unsort несартаваны hl_btn_filter_disable выкл. hl_btn_filter_enable укл. hl_btn_filter_settings Настаўленні hl_btn_sort_asc узыходна hl_btn_sort_desc сыходна hl_btn_sort_name назва hl_btn_sort_type тып hl_btn_sort_waiting груз hlf_btn_alle усё hlf_btn_invers інв. hlf_btn_keine нічога #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Das Feld gehoert\neinem anderen Spieler\n Участак ужо\nналежыць іншаму гульцу!\n Der Besitzer erlaubt das Entfernen nicht Уладальнік\nне дазваляе\nзнесці гэтага.\n Diese Zusammenstellung kann nicht fahren!\n Гэты састаў не можа ехаць!\n Flugzeughalt muss auf\nRunway liegen!\n Немагчыма размясціць\nпункту прыпынка\nсамалёта па-за дарогай!\n Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Немагчыма размясціць\nбудынка аэрапорта\nў абраным месцы!\n Hier kann kein\nSignal aufge-\nstellt werden!\n Тут немагчыма\nўстанавіць\nсветлафора!\n Monorails are not available yet! Манарэйкавы транспарт\nяшчэ не даступны! Post muss neben\nHaltestelle\nliegen!\n Дадатак трэба\nбудаваць ля\nпрыпынку/станцыі!\n Schiffhalt muss im\nWasser liegen!\n Караблі могуць\nспыняцца толькі\nна вадзе!\n Zughalt muss auf\nSchiene liegen!\n Чыгуначная станцыя\nпавінна размяшчацца\nна рэйках!\n #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s\nвозіць турыстаў паміж\n%s ды\n%s ля (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s\nперавозіць цяпер\nрабочых з %s\nда прадпрыемства\n%s\nля (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s цяпер\nпрацуе з %i а/м паміж\n%s (%i,%i)\nды %s\nля (%i,%i).\n %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s мае цяпер\nчыгуначную\nлінію паміж %s\nля (%i,%i) ды\n%s\nля (%i,%i)\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Прылады рэдагавання мапы LISTTOOLS Спісы MONORAILTOOLS Манарэйка RAILTOOLS Чыгунка ROADTOOLS Аўтадарога SHIPTOOLS Суднаходства SLOPETOOLS Змяненне рэльефу SPECIALTOOLS Дадатковыя будаўнічыя ды іншыя прылады TRAMTOOLS Трамвай #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ New factory chain\nfor %s near\n%s built with\n%i factories. Новы прамысловы ланцуг:\nдля прадпрыемства %s\nля %s\nствораны яшчэ %i\n. New vehicle now available:\n%s\n З'явілася\nновая тр. адзінка:\n\n"%s" !\n\n Remove vehicle from map. Use with care! Асцярожна! Выдаляе транспарт з мапы. Screenshot\ngespeichert.\n Здымак запісаны.\n Sends the convoi to the last depot it departed from! Адпраўляе тр. адзінку ў найбліжэйшае дэпо! With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s святкуе\nўсталяванне новага помніка.\nУ фэсце бяруць\nудзел %i грамадзян. #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ \nBauzeit bis да \nBauzeit von \nАб'екты гэтага тыпу\nмогуць з'яўляцца\nз \nCan't open heightfield file.\n \nНемагчыма адкрыць файла з мапай вышыні.\n \nelektrified \nЭлектрыфікаваны \nHeightfield has wrong image type.\n \nНяправільны тып графічнага файла.\n \nnot elektrified \nНеэлектріфікаваны \nRibi (masked) \nRibi (masked) \nRibi (unmasked) \n\nRibi (unmasked) %d buildings\n Будынкі: %d \n %d convois %d (саставы) %d Einzelfahrzeuge im Depot %d тр. адзінак у дэпо %i km/h (max. %ikm/h) %i км/гадз (макс. %i км/гадз) %s building %s %s %s %s %s %s has entered a depot. %s\nчакае\nў дэпо. %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. Горад %s\nз павелічэннем\nнасельніцтва да %i\nмае цяпер новую ратушу. %s\nis crowded. Станцыя\n%s\nперапоўнена! <няма лініі> 1 convoi 1 тр. састаў 1 Einzelfahrzeug im Depot 1 тр. адзінка ў дэпо 1LIGHT_CHOOSE Яркасць: 1WORLD_CHOOSE Настаўленні новай мапы: 2LIGHT_CHOOSE Каляровасць: 2WORLD_CHOOSE Нумар мапы: 3LIGHT_CHOOSE Хутк. пракр.: 4LIGHT_CHOOSE Адваротная пракрутка 5LIGHT_CHOOSE Пешах. ля прыпынкаў 5WORLD_CHOOSE Гарады: 6LIGHT_CHOOSE Пешаходы ў горадзе 6WORLD_CHOOSE Інтэнсіўнасць д/руху: 8WORLD_CHOOSE Змена дню і ночы Abfrage Рэжым агляду Abnehmer Спажывец About Звесткі Abriss Выдаліць/знесці Absenken Чарпануць зямлі Add Stop Дадаць Add stops for backward travel Дадаць прыпынкі для вяртання aircraft_tab Грузавыя Airport Аэрапорт AIRTOOLS Паветраны транспарт All Усе Allow player change Гульца можна мяняць Alters a schedule. Змяняе расклад. Angenommene Waren Грузы для навакольных прадпрыемстваў Anhaenger_tab Прычэпы Anheben Насыпаць зямлі Apply Line На лінію April Красавік Arbeiter aus: Рабочыя з: Arrived Прыбыло грузу Assets Рухомы капітал August Жнівень Bahndepot Дэпо (цягнікі) Bankrott:\n\nDu bist bankrott.\n Банкрот:\n\nВы банкрот.\n Baum Дрэва Baustelle Пабудовы Beenden Выйсці BF Ч bio Біялагічны Blockstrecke ist\nbelegt\n \nУчастак заняты іншай\nтр. адзінкай!\n Boden Зямля Bridge is too long for this type!\n Нельга пабудаваць маста\nабранага тыпу гэткай\nвелькай даўжыні!\n Bruecke Мост Bruecke muss an\neinfachem\nHang beginnen!\n Мост павінен пачынацца\nад роўнага схілу!\n Build city market Пабудаваць гандлёвае прадпрыемства Build drain Трансфарматар Build land consumer Пабудаваць новае прадпрыемства Build monorail depot Монарэйкавае дэпо Build powerline Будаваць лініі электраперадачы Build presignals Папярэднія семафоры Build ship depot Карабельнае дэпо Build signals Семафоры Build train depot Чыгуначнае дэпо Build tram depot Трамвайнае дэпо Build truck depot Аўтамабільнае дэпо Built artifical slopes Будаваць штучны схіл Built random attraction Пабудаваць новую славутасць Bus_tab Аўтобусы Cancel Скасаваць Capacity: %d%s %s\n Груз: %3d%s, %s\n Capacity: %s\nLoad: %d (%d%%) Груз: %s\n» %d (%d%%) Cash На рахунку Change player Змяніць гульца Chart Дыяграма Choose operation executed on clicking stored/new vehicles Абраць дзеянне пры пстрыканні на існуючым/новым транспарце citicens жыхары City industries Гарадскі гандаль: City list Спіс гарадоў City size Насель-\nніцтва city_road Гарадская дарога cl_title Спіс транспартных адзінак cl_txt_sort Сартаванне: clf_chk_cars Аўтатранспарт clf_chk_indepot у дэпо clf_chk_name_filter Па назве: clf_chk_noincome без прыбытку clf_chk_noline без лініі clf_chk_noroute няма шлаху clf_chk_noschedule без раскладу clf_chk_ships Караблі clf_chk_spezial_filter Па стану: clf_chk_trains Цягнікі clf_chk_type_filter Па тыпу: clf_chk_waren Па грузе: clf_title Настаўленні спісу тр. адзінак COLOR_CHOOSE\n Калі ласка,\nабярыце\nВаш колер: Constructed by Аўтар: Constructed by %s Аўтар: %s Construction_Btn Будаўніцтва convoi %d of %d тр. састаў %d з %d Convoi has been sent\nto the nearest depot\nof appropriate type.\n Тр. адзінка\nадпраўлена ў\nнайбліжэйшае дэпо.\n Convois: %d\nProfit: %s Саставаў: %d\nПрыбытак: %s Convoys Саставы Copy Convoi Капіраваць Copy the selected convoi and its schedule or line Капіраваць абраны састаў, захоўваючы расклад і лінію Create a new line based on this schedule Стварыць новую лінію па гэтаму раскладу curlist_title Славутасці для турыстаў December Снежань Del Stop Выдаліць Delete Line Выдаліць лінію Delete this file. Выдаліць гэты файл. Denkmal Помнік Departed Адпраўлена гр. Destination месца прызн. Details Звесткі diesel Дызель Direkt erreichbare Haltestellen Прамы маршрут да прыпынкаў Display settings Відарыс Dock Док Durchsatz Макс. Eigenbesitz\n Грамадская\nуласнасць\n\n Ein %s\npasst hier nicht.\n "%s"\nсюды не пасуе.\n Einstellungen aendern Змяніць настаўленні electric Электрычны Electrify track Электрыфікацыя Erzeuge neue Karte.\n Калі ласка, \nпачакайце,\nствараецца\nновая мапа...\n\n (Пры велькім памеры\nможа спатрэбіцца\nнекалькі мінут.)\n Es wird bereits\nein Fahrplan\neingegeben\n Расклад ужо абраны.\nСпачатку закончыце\nпланаваць гэты!\n Fabrikanschluss Звязаныя прадпрыемствы Fabrikname Назва Factories Прадпр-вы Fahrplan Расклад Fahrzeuge koennen so nicht entfernt werden Нельга ўбраць\nтранспарту\nгэткім чынам!\n Fahrzeuge: Тр. адзінкі: Farbe Колер Fast forward Паскорыць час February Люты Ferry_tab Пасажырскія Fertig Гатова Filename Імя файла: Filter: Фільтр: Finanzen Фінансы fl_title Спіс прадпрыемстваў Flug_tab Пасажырскія follow me сачыць Follow the convoi on the map. Рухаць камеру ўслед за саставам. Found new city Заснаваць новы горад Fracht Груз Free Capacity Свабодны аб'ём Full load Чакаць загр.: Fussgaenger Пешаход GAME PAUSED Гульня прыпынена Gear: Прывод: Gebaeude Будынак Gewicht Вага Gewinn Даход Give the selected vehicle(s) an individual schedule Даць абранаму транспарту асабісты расклад gl_title Спіс грузаў go home Дадому Goods list Спіс грузаў Gross Profit Гадавы прыбытак Grow city Павялічыць колькасць жыхароў Growth рост H А Happy Здаволеныя пас. Helligk. Відарыс Help Дапамога Hier warten/lagern: Тавары і пасажыры\nна станцыі:\n hl_title Спіс прыпынкаў/станцый hl_txt_filter Фільтр: hl_txt_sort Сартаванне: hlf_chk_airport Аэрапорт hlf_chk_anleger Докі hlf_chk_bahnhof Вакзалы hlf_chk_bushalt Прыпынкі hlf_chk_frachthof Пагруз. пляцоўкі hlf_chk_keine_verb без сувязі hlf_chk_name_filter Па назве: hlf_chk_spezial_filter Па стану: hlf_chk_type_filter Па тыпу: hlf_chk_waren_abgabe Пастаўляе: hlf_chk_waren_annahme Прынімае: hlf_title Фільтр спісу станцый Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Адпаведнага дэпо ня знойдзена!\nЗмяніце расклад\nяк трэба самі. Homeless Бяздомныя hydrogene Вадародны Input Спажыта Ins Stop Уставіць Intercity road len: Даўжыня прыгар. дарог: Intro. date: Выпуск. ад: January Студзень July Ліпень June Чэрвень Kann Spielstand\nnicht laden.\n Немагчыма прачытаць\nстану гульні!\n Kann Spielstand\nnicht speichern.\n Немагчыма запісаць\nстану гульні!\n keine нічога Keine Einzelfahrzeuge im Depot Няма тр. адзінак у дэпо Lade Relief Загруз. рэльеф Laden Загрузіць Land industries Прадпрыемствы: LANG_CHOOSE\n Калі ласка,\nабярыце мову:\n Last Year Летась: leer пусты Legend Тлумачэнне знакаў Leistung Магутнасць Leitung Лінія электр. letzen Monat: diesen Monat: папярэднім месяцы: гэтым месяцы: Line Лінія Line Management Кіраванне лініямі Lines serving this stop Прыпынак ліній LKW_tab Грузавікі loaded загружаны loaded passenger/freight Загружана пас./грузу Lock game Забараніць змену гульца Lokomotive_tab Цягнікі Mailbox Паштовая скрынка Mailbox Options Настаўленні пошты Maintenance Утрыманне Manufactured: Выраблены: Map roughness Гарыстасць: March Сакавік Margin (%%) Маржа Marker Знак Max. speed: Макс. хутк.: Maximum 254 stops\nin a schedule!\n Не дазваляецца\nмець болей за 254 прыпынка ў раскладзе!\n May Травень Median Citizen per town Сярэдні памер гарадоў: Meldung Паведамленне Menge колькасць MessageOptionsText \nНовы год\n\nДзейнасць Ш.І.\n\nГарадскія навіны\n\nНяма маршруту\n\nНовыя прадпрыем.\n\nболтать\n\nНовы транспарт\n\nПерапаўненне ст.\n\nПапярэджанні\n\nTraffic jams\n\nScenario Modify the selected line Змяніць расклад абранай лініі Months Месяцы Mountain height Вышыня гор: Music playing disabled/not available Музыка адключана/няма файлаў. Music volume: Гучнасць муз.: Net Wealth Увесь капітал Neue Karte Новая мапа Neue Welt Новы свет new convoi Новы састаў New Line Новая лінія New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Створана новая лінія!\nМожна назначыць лінію,\nабраўшы яе\nў спісе вышэй. New Vehicles Новы транспарт no convois няма транспарту no goods waiting няма нічога для перавозкі No Route Без маршруту Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Абраны транспарт не мае раскладу.\n Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Не магчыма зараз змяніць\nраскладу руху!\nПаўтарыце пазней! November Лістапад Now active as %s.\n Цяпер Вы -\n%s.\n Ok Добра On this map, you are not\nallowed to change player!\n Мапа замкнёна!\nЗмена гульца\nна гэтай мапе\nзабаронена.\n Operation Бягучыя выд. Ops Profit Прыбытак Optionen Настаўленні Origin Месца адпраўлення Output Выраблена paletten (яшчыкі) Pas_tab Пасажырскія Passagiere пасажыры Passagierrate \nПопыт\nна пасажыраў Passagierziele Пасажыры/пошта ў Pause Прыпыніць Plant tree Дрэвы player -1 чалавек player 0 бог player 1 Napik 128 AS player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Spedition VM player 5 H-Trans GmbH player 6 PSK & Co KG Post пошта Postrate \nПопыт\nна пошту Power Эл. т. Power: Магутнасць: Powerlines Лініі электр. Produktion Вытворчасць Profit Прыбытак promote to line Новая лінія q1 Вясна q2 Лета q3 Восень q4 Зіма Random map Выпадковая мапа Rating Забесп. Reliefkarte Мапа Restore natural slope Аднавіць натуральны схіл Restwert: Кошт продажу: Retire. date: Спын. вытв.: return ticket +Адваротны шлях Revenue Выручка sack (мяшкі) sail Вецер Schienentunnel Чыгуначны тунэль Schiff_tab Грузавыя Schiffdepot Суднавае дэпо Schleppkahn_tab Баржы Screenshot Здымак Sell the selected vehicle(s) Прадаць абраны транспарт SEP_FRACTION , SEP_THOUSAND . September Верасень Serves Line: На лініі: Service Ад/прыбыўшыя Ship Судна Show also vehicles no longer in production. Паказваць тр. адзінкі, якія ўжо не вырабляюцца. Show obsolete +састарэлыя Show/hide statistics Паказаць/схаваць статыстыку Shrink city Паменшыць колькасць жыхароў Size (%d MB): Памер (%d MБ): Sort by Сартаваць Sort waiting list by Катэгорыя сартавання спісу: Sound Гук Sound settings Гукавыя настаўленні Sound volume: Гучнасць: special freight спец. груз Speedlimit Макс. хутк. Speichern Захаваць Spieler Гулец Spieler(mz) Гульцы Spielstand wurde\ngeladen!\n \nСтан гульні\nбыў прачытаны!\n Spielstand wurde\ngespeichert!\n \nСтан гульні\nбыў запісаны!\n Sprache Мова Stadtinformation Звесткі пра горад Start Пуск Start the selected vehicle(s) Запусціць абраны транспарт Starte Spiel Пачаць гульню Status Стан steam Пара Step timeline one year Праскочыць праз год Storage capacity Змяшчальнасць Strassendepot А/м дэпо Strassentunnel Аўтатунэль Tage alt (узрост, дні) There are still vehicles\nstored in this depot!\n У гэтым дэпо яшчэ\nзнаходзіцца транспарт!\n This Year Гэты год: Tile not empty. Нельга выпаўніць,\nбо поле занятае.\n tl_title Спіс гарадоў To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. %s\nпабудаваў %s\nз дапамогай %i падатка-\nплацельшчыкаў, каб прыцягнуць\nбольш турыстаў.\n To heavy traffic\nresults in traffic jam.\n Шчыльны дарожны рух:\nдарожная пробка.\n tonnen т Total inhabitants: Насельніцтва: Tourist attractions Славутасці мясцінаў: Tourists Славутасці Towns Гарады Tracks Рэйкі Traffic Дарожны рух Train Цягнік Transported перавезена Truck Аўто Tunnel muss an\neinfachem\nHang beginnen!\n Тунэль павінен пачынацца\nад роўнага схілу!\n Unemployed Беспрацоўныя Unhappy Нездаволеныя п. units/day адзінак/дзень\n Update Line Змяніць лінію Use timeline start year Год пачатку гульні: Vehicle %s can't find a route! %s\nня можа знайсці\nдарогі! Vehicle details Падрабязныя звесткі пра састаў. Verbrauch Спажыванне verkaufen Продаж Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Запазычанасць:\n\nМесяцаў\nвыплаціць доўг: %d \n via прамеж. пункт via Menge транзіт, кольк. voranstellen д-не ўперад Waggon_tab Вагоны waiting Чакае перав. Water level Узровень вады: Wegpunkt пункт на мапе Wert Кошт WRONGSAVE \nНесумяшчальная версія запісу стану гульні. \nГульні загрузіць немагчыма.\n Year %i has started. Пачаўся %i год. Years Гады Zielort месца прызн. Zu nah am Kartenrand Заблізка ад канца мапы,\nкаб нешта будаваць. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ 1center %s %s 1extern %s Сельскі %s 2center %s Галоўны %s 2extern %s Блізкі %s 3center %s Цэнтральны %s 3extern %s Прыгарад %s 4center %s Сярэдні %s 4extern %s Дальні %s 5center %s Рабочы %s simutrans-124.5/simutrans/text/bg.tab000066400000000000000000000071521520675460500176620ustar00rootroot00000000000000§Bulgarian PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: bg Bulgarian # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 2.06 2024 # # # ################################################################################ #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Арктически климат desert Пустиня temperate Умерен #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Cannot built depot here! Тук не можеш да депо(гараж) Not enough money! Недостатъчно средства(пари) #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ air Въздух airplane Самолет Airport Летище Apply Line използвай линия April Април Arbeiter aus: Работниците живеят в: Arrivals from\n пристигане от\n Arrived Доставено Assets Автомобили стойност August Август Boost (%%) Увеличете (%%) Build air depot Построй хангар Build maglev depot Построй МагЛев депо Build narrowgauge depot Построй теснолинейно депо Build powerline Построй далекопровод follow me следвам Forest Гора Found new city Основи град fuel_cell Горивна клетка Full load Пълен товар Fundament Основа go home в депо Goods Стоки Goods AI Стоки на AI Goods list Списък на стоки Growth Растеж Hangar Хангар Happy Щастливи / Радостни Help Помощ Help text not found Текстът не е намерен hide all building Скрий всички сгради hide city building Скрий жилищните сгради hide station names Скрий имената на гарите(спирките) hide trees Скрий дърветата Homeless Бездомен hydrogene Водород industrial building Индустриална сграда Invalid coordinate Невалидни кординати January Януари join game Присъедини се July Юли June Юни Load scenario Зареди сценарий Lokomotive_tab Локомотиви Neue Karte Нова карта Stadtinformation статистика на града Start старт Start the selected vehicle(s) Отправи избраното превозно средство Starte Spiel Старт игра simutrans-124.5/simutrans/text/ca.tab000066400000000000000000001321621520675460500176550ustar00rootroot00000000000000§Catala PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: ca Catala # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable Desactivat cl_btn_filter_enable Activat cl_btn_filter_settings Opcions cl_btn_sort_asc Ascendent cl_btn_sort_desc Descendent cl_btn_sort_id Identificació interna cl_btn_sort_income Ingressos cl_btn_sort_name Nom cl_btn_sort_type Tipus clf_btn_alle tot clf_btn_invers inv. clf_btn_keine res gl_btn_sort_bonus per bonus gl_btn_sort_name per nom gl_btn_sort_revenue per ingressos gl_btn_unsort desordenat hl_btn_filter_disable Desactivat hl_btn_filter_enable Activat hl_btn_filter_settings Opcions... hl_btn_sort_asc Ascendent hl_btn_sort_desc Descendent hl_btn_sort_name Nom hl_btn_sort_type Tipus hl_btn_sort_waiting En espera hlf_btn_alle tot hlf_btn_invers inv. hlf_btn_keine res Networks Xarxes Queueing Cua d'espera Road toll Peatge Scenario Escenari Transfers Transbordaments #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic clima molt fred desert clima desèrtic mediterran Mediterrani rocky Alpí temperate Templat tropic Tropical tundra Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed No s'ha pogut carregar\nel script de l'escenari!\n Can't buy obsolete vehicles! No es poden comprar vehicles obsolets! Cannot alter water No es pot alterar l'aigua Cannot built depot here! No puc construir una cotxera aquí. Cannot built this station/building\nin underground mode here. L'edifici no es\npot construir sota terra.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. No puc crear una línia genèrica!\nSeleccioneu el tipus de línia\nemprant el filtre. Cannot create socket No s'ha pogut crear el socket Convoi handles exhausted! Els controladors de combois estan esgotats! Convoy already deleted! El comboi ja ha estat eliminat! Das Feld gehoert\neinem anderen Spieler\n Aquesta parcel·la\nés propietat\nd'un altre jugador!\n Der Besitzer erlaubt das Entfernen nicht El propietari no dóna\npermís per a\neliminar-ho.\n Diese Zusammenstellung kann nicht fahren!\n No puc conduirn\naquesta combinació!\n Flugzeughalt muss auf\nRunway liegen!\n Una aturada d'avió\nha de col·locar-se sobre una pista de\nrodatge! Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Aquest edifici aeroportuari\n\nno es pot construir aquí!\n Hier kann kein\nSignal aufge-\nstellt werden!\n ¡No podeu\ninstal·lar senyals\naquí!\n In order to lock the game, you have to protect the public player by password! Per a protegir la partida ha de crear una contrasenya pel jugador Servei Públic! Lost connection\nto server! S'ha perdut la connexió\namb el servidor! Lost synchronisation\nwith server. S'ha perdut la sincronització\namb el servidor. Maglevhalt muss auf\nMaglevschiene liegen!\n L'estació de monocarril ha d'estar\nsobre una via de monocarril Monorailhalt muss auf\nMonorail liegen!\n Una estació de monocarril\ns'ha de construir sobre una\nvia de monocarril! Monorails are not available yet! Els monocarrils encara no estan disponibles! Narrowgaugehalt muss auf\nNarrowgauge liegen!\n L'estació s'ha de construir sobre una via estreta! No suitable way on the ground! Les estacions s'han de construir sobre un tram de via recta! No through station here! No es pot construir l'estació aquí!\nEs necessita una secció recta de via! Not enough money! No tens prou diners\nper a construir això! On narrowgauge track only!\n Només per via estreta! Only public player can lock games! Només el jugador Servei Públic pot protegir la partida! Out of funds No tens prou diners\nper a això! Post muss neben\nHaltestelle\nliegen!\n ¡Ubiqueu una oficina postal\nprop d'una estació!\n Protocoll error (expecting game) Error de protocol (s'esperava una partida) Schiffhalt muss im\nWasser liegen!\n ¡Sols podeu col·locar\nestacions marítimes\na l'aigua!\n Server busy Servidor ocupat. Terraforming not possible\nhere in underground view No és possible modificar el\nterreny en mode subterrani.\n Upgrade must have\na higher level L'actualització ha de tenir\nun nivell superior. Vehicle %s cannot choose because stop too short! El vehicle %s no pot escollir perquè l'estació és massa curta! Zughalt muss auf\nSchiene liegen!\n ¡Sols podeu col·locar\naturades de\nferrocarril a\nles vies!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Ajuda

Índex

Sobre Simutrans

%1$s

Com es fa servir

%2$s

Com començar una partida

%4$s

Com jugar

%5$s

Eines

%3$s

Altres finestres

%6$s Keyboard Help\n

Keyboard Help

\n Teclat\n

Ajuda del teclat

\n

\nL'ajuda del teclat mostra les funcions per a les tecles que es poden prémer.\n

\n

\nL'ajuda del teclat s'obre quan es prem una tecla no assignada a l'ajuda general.\n

\n

\nLes tecles premudes diferencien les majúscules (fent servir [Shift] per a aquestes lletres).\n

\n

\nLes tecles que tenen funcions assignades són:\n

\n

\n[Tecles de direcció]: desplaça la pantalla del joc en la direcció de la fletxa.
\n[Esborrar]: tanca totes les finestres, barres de tasques i ajudes contextuals del joc en curs.
\n[Esborrar], o [Escape]: tanca la finestra d'un nivell superior, barres de tasques o ajuda contextual del joc en curs.
\n[Introduïu]: s'empra per a confirmar accions.
\n[Pàgina amunt], o [>]: amplia la imatge.
\n[Pàgina baix], o [símbol "menor que"]: allunya la imatge.\n[F1]: obre l'ajuda de Simutrans.

\n

\n[1]: desplaça el joc cap al sud.
\n[2]: desplaça el joc cap al sudest.
\n[3]: Desplaça el joc cap a l'est.
\n[4]: desplaça el joc cap al sudoest.
\n[6]: desplaça el joc cap al nordest.
\n[7]: desplaça el joc cap a l'oest.
\n[8]: desplaça el joc cap al nordoest.
\n[9]: desplaça el joc cap al nord.\n

\n

\n[Shift] + ratolí: empreu-ho per al mapa per a veure les connexios de lacadena industrial.
\n[CTRL] + eina: construeix (senyals i aturades) a un nivell superior; o carreteres de baixa velocitat damunt una de més ràpida; o carreteres i vies rectes (sobretot diagonals).
\n[CTRL] + ([F2] a [F12]): memoritza l'eina activa a una de les tecles de [F2] Decrease water height Drenar l'aigua un nivell Highlight railroad tracks Destacar ferrocarrils Highlite depots Destacar cotxeres Highlite electrical transmission lines Destacar línies elèctriques Highlite factories Destacar indústries Highlite forests Destacar boscos Highlite tourist attraction Destacar atraccions turístiques Increase water height Augmentar l'aigua un nivell Overlay city limits Veure límits de ciutats Overlay passenger destinations when a town window is open Destacar els destins dels passatgers quan s'obre una finestra d'una població Overlay schedules/network Veure línies/xarxes Overlay town names Veure noms de poblacions Please click on the map to add\nwaypoints or stops to this\nschedule. Faci clic sobre el mapa\nper afegir punts de pas o\nparades a la línia. Set tile climate %s Convertir aquest terra en clima %s. Show capacity and if halt is overcrowded Veure capacitat i estacions plenes Show how many convoi reach a station Veure quans combois passen per les estacions Show how many people/much is waiting at halts Veure quanta gent o mercaderies esperen a les estacions Show initial passenger departure Veure origen dels passatgers Show level of city buildings Veure nivell dels edificis Show mail service coverage/mail network Veure cobertura/xarxa del servei de correus Show passenger coverage/passenger network Veure cobertura/xarxa del servei de passatgers Show speedlimit of ways Veure els límits de velocitats de les infraestructures Show the change of waiting at halts Veure el canvi del temps d'espera des de l'últim mes Show the owenership of infrastructure Veure els propietaris de les infraestructures Show transported freight/freight network Veure xarxa de transport de mercaderies Show usage of network Veure l'ús de les xarxes Shows a listing with all industries on the map. Veure totes les indústries al mapa Sum of departure/arrivals at halts Veure total de sortides i arribades a les estacions #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s ara\nofereix serveis de bus\nentre %s\ni l'atracció\n%s\na (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s ara\nofereix serveis de bus\nentre %s\ni la indústria\n%s\na (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s ara\nofereix servei de\n%i camions entre\n%s a (%i,%i)\ni %s\na (%i,%i).\n %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\nha obert una línia\nferroviària entre\n%s a (%i,%i)\ni %s\na (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Línia aèria per\n%s\nara en servei entre\n%s \ni %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Línia de Ferry per\n%s\nara en servei entre\n%s \ni %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Ara els viatgers\nagafen els autobusos\nde %s entre\n %s\ni %s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Eines de mapa LISTTOOLS Llistes MAGLEVTOOLS Eines de maglev MONORAILTOOLS Eines de monocarril NARROWGAUGETOOLS Eines de ferrocarril de via estreta RAILTOOLS Ferrocarrils ROADTOOLS Carreteres SHIPTOOLS Vaixells SLOPETOOLS Eines d'alçades i talussos SPECIALTOOLS Eines especials de construcció TRAMTOOLS Eines de tramvia #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s ha construït una nova base d'operacions. Factory chain extended\nfor %s near\n%s built with\n%i factories. L'economía creix:\n%s prop de %s s'hi afegeixen a la cadena de producció.\n%i noves indústries establertes. New %s now available:\n%s\n Nou %s ara disponible:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Nova cadena industrial\nper %s\naprop de %s\nconstruïda amb\n%i indústries. New vehicle now available:\n%s\n \n Nou vehicle disponible:\n\n\n » %s «\n\n Now %u clients connected. Actualment hi ha %u client(s) connectat(s). Remove vehicle from map. Use with care! Eliminar vehicle del mapa. Fes-ho servir amb precaució! Screenshot\ngespeichert.\n Fotografia\nsalvada.\n Sends the convoi to the last depot it departed from! Envia el comboi a la cotxera d'on va sortir l'última vegada. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. Fent-hi una festassa, a\n%s\nhan inaugurat un nou monument.\nHan fet feliços a %i ciutadans. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Buscar línia #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (a la cotxera) [CTRL] CTRL [END] FI [HOME] INICI \nBauzeit bis fins a \nBauzeit von \n hi apareix amb data \nCan't open heightfield file.\n \nNo puc obrir el plànol\ntopogràfic.\n \ndirection: \ndestinacions: \nelektrified \nelectrificat\n \nHeightfield has wrong image type.\n \nEl plànol té un tipus d'imatge incorrecte.\n \nis reserved by: \nreservat pel tren \nminimum speed: \nvelocitat mínima: \nnot elektrified \nsense electrificar\n \nRibi (masked) \ndestinacions\m (ocultat): \nRibi (unmasked) \ndestinacions\n (visibles): \nSet phases: \nAjustar fase: \nsingle way \nSentit únic \nway1 reserved by L'accés 1 està reservat per \n \nway2 reserved by L'accés 2 està reservat per \n \nwith sign/signal\n \nNo té senyalètica\n %d buildings\n %d edificis\n %d Einzelfahrzeuge im Depot %d vehicles aturats aquí. %i km/h (max. %ikm/h) %i km/h (màx. %ikm/h) %i years %i months old. %i anys %i mesos d'antiguitat. %s at (%i,%i) now public stop. %s a (%i,%i) és ara una estació pública. %s building %s %s %s %s %s %s city %d %s %s ciutat %d %s %s factory %s %s %s - Estació %s %s has entered a depot. %s ha entrat a la cotxera. %s land %d %s %s comarca %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s\nha construït\nun nou ajuntament\nen arribar\nals %i habitants %s\nis crowded. %s\nestà saturada! %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\nvelocitat %i\nv.màxima %i\ndx:%i dy:%i %s\nwas liquidated. %s\ ha aturat la seva activitat. %u Player (%u locked)\n %u jugadors (%u bloquejats)\n Crear línia nova 1 Einzelfahrzeug im Depot Aquí roman 1 vehicle. 1LIGHT_CHOOSE Lluentor: 1WORLD_CHOOSE Opcions del nou mapa: 2LIGHT_CHOOSE Colors: 2WORLD_CHOOSE Número de mapa: 3LIGHT_CHOOSE Vel. desplaç.: 4LIGHT_CHOOSE Inverteix desplaçament 5LIGHT_CHOOSE Mostra peatons a aturades 5WORLD_CHOOSE Número ciutats: 6LIGHT_CHOOSE Peatons a les ciutats 6WORLD_CHOOSE Dens. de trànsit: 8WORLD_CHOOSE Usa mode dia i nit A bridge must start on a way! Sols podeu construir\nponts a vies existents\no rampes buides. Abfrage Explora Abnehmer Consumidor About Quant a About scenario Copyright Abriss Elimina Absenken Rebaixa terreny Abspanntransformator Transformador industrial Accelerate time Accelera temps Act. load: %u MW\n Càrrega actual: %u MW\n Active player only Només jugador actiu Add forest Afegir bosc Add random citycar Afegir cotxe privat add server Afegir servidor Add Stop Afegeix aturada. Add stops for backward travel Afegeix aturades per a la tornada. aircraft_tab Avions de càrrega airplane avió Airport Aeroport AIRTOOLS Eines d'aeroport All Totes all convoi tooltips Estat dels combois Allow city growth Permetre creixement urbà Allow player change Permet canvi de jugador allowed climates:\n Climes disponibles:\n Alters a schedule. Afegir/treure aturades a/d'una ruta Angenommene Waren Indústries properes demanden anhaengen Afegeix Anhaenger_tab Remolcs Anheben Eleva terreny Appends stops at the end of the schedule Afegeix parades al final de la línia Apply Line Aplica línia April Abril Arbeiter aus: Treballadors de: Arrived Ha arribat Assets Actius Aufloesen Desassembla Aufspanntransformator Transformador August Agost Autohalt muss auf\nStrasse liegen!\n Les aturades\nhan d'estar\ndamunt una calçada. Available Disponible Bahndepot Cotxera ferrocarril Bankrott:\n\nDu bist bankrott.\n Bancarrota:\n\nEsteu en bancarrota.\n battery Bateria Baum Arbre baum builder Sembra arbres Baustelle Lloc en\nconstrucció Bauzeit Període de construcció Beenden Surt Beginner mode Mode novell Besonderes Gebaeude Atracció turística BF - Estació bio biològic Blockstrecke ist\nbelegt\n Un altre tren\nempra\naquest segment de via\n Boden Terra Bonusspeed: %i km/h Màxima velocitat real: %i km/h Boost (%%) Incrementar producció (%%) bridge is too high for its type! No puc construir un pont d'aquest tipus a tanta alçària. Bridge is too long for this type!\n La distància entre pilars és massa llarga per a aquest tipus de pont. Bruecke Pont Bruecke muss an\neinfachem\nHang beginnen!\n El pont deu\ncomençar a un\npendent recte!\n Brueckenboden pont Build air depot Construir hangar build choosesignals Posa un senyal de selecció d'andana Build city market Posa un mercat a la ciutat més propera Build drain Transformador elèctric build HQ Obre la base d'operacions Build land consumer Posa una central elèctrica Build maglev depot Posa una cotxera de tren levitant Build monorail depot Posa una cotxera de monorail Build narrowgauge depot Construir cotxera de via estreta Build powerline Posa una línia elèctrica Build presignals Posa senyals de bloqueig Build road depot Posa un garatge Build ship depot Posa unes drassanes Build signals Instal·la senyals Build train depot Construeix cotxera de trens Build tram depot Posa una cotxera de tramvia Build truck depot Construeix cotxera de camions Building costs estimates Cost estimat de construcció Buildings Edificis Built artifical slopes Construeix rampes artificials Built random attraction Construeix una atracció aleatòria Bus_tab Autobusos Can only move from halt to halt or waypoint to waypoint. Només es pot moure entre aturades o punts de pas. Cancel Cancel·la Cannot connect to offline server! No es pot connectar a un servidor apagat! Capacity: Capacitat: Capacity: %.0f MW Capacitat: %.0f MW\n Capacity: %d%s %s\n Capacitat: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Capacitat: %s\n? %d (%d%%) Cars are not available yet! Els cotxes encara no estan disponibles. cars.\nstate cotxes\n Cash Balanç Change player Canvia de jugador Chart Gràfic Chat_msg Xat Choose direction Escollir direcció Choose operation executed on clicking stored/new vehicles Trieu quina operació voleu fer quan feu clic a vehicles nous o emmagatzemats. chooses a random map Tria un mapa a l'atzar. citicens Ciutadans City attraction Atracció turística de ciutat City industries Indústries urbanes City list Llistat de ciutats City size Mida de la ciutat city_road Carrer citybuilding builder Constructor d'edificis CityLimit Límits de la ciutat cl_title Llistat de vehicles cl_txt_sort Ordena per: Clear block reservation Mostra/oculta les vies reservades clf_chk_aircrafts avions clf_chk_cars Autobús/camió clf_chk_indepot és a cotxeres clf_chk_maglev Tren levitant clf_chk_monorail Monorail clf_chk_name_filter Nom filtre: clf_chk_narrowgauge Trens de via estreta clf_chk_noincome Sense ingressos clf_chk_noline no té línia assignada clf_chk_noroute no troba el camí clf_chk_noschedule Sense ruta assignada clf_chk_obsolete Obsolet clf_chk_ships Vaixells clf_chk_spezial_filter Filtre especial: clf_chk_stucked embussat clf_chk_trains Trens clf_chk_trams Tramvies clf_chk_type_filter Tipus: clf_chk_waren Tipus de càrrega: clf_title Mostra sols aquests vehicles: Climate Control Canvia el clima closed tancat. COLOR_CHOOSE\n Per favor, trieu\nun color de la\ntaula:\n Company bankrupt Companyia en fallida Company_msg Competència Comparing pak files ... Comparant arxius pak... Configure AI Configuració IA Configure AI setttings Propietats Configuració IA Congratulation\nScenario was complete in\n%i months %i years. Enhorabona!\nHeu complet l'objectiu en \%i mesos i %i anys! Connected stops Estacions connectades Connected with server Connectat amb servidor Constructed by Fet per Constructed by %s Fet per %s construction speed Velocitat de construcció Construction_Btn Cost de construcció Consumed Consumit convoi %d of %d Comboi %d de %d convoi error tooltips Textos d'error en combois Convoi has been sent\nto the nearest depot\nof appropriate type.\n He enviat el convoi\na la cotxera adient.\n Convoi is sold when all wagons are empty. Vendré el vehicle quan estigui buid. convoi mouseover tooltips Textos de ratolí en combois convoi passed last\nmonth %i\n \nconvois que circularen\nel mes passat: %i\n Convois combois Convois: %d\nProfit: %s Vehicles: %d\nBenefici: %s Convoys Convois Copy Convoi Copia el convoi Copy the selected convoi and its schedule or line Copia el convoi i la seva ruta cost for removal cost de demolició Costs Costos Create a new line based on this schedule Crea una nova línia basada en aquesta ruta curiosity builder Constructor de monuments curlist_title Llistat d'atraccions CURRENCY € Currently playing: Llista de pistes: Customers live in: Els clients viuen a: deactivated in online mode Desactivat en mode online Deccelerate time Desaccelera el temps December Desembre decrease underground view level baixar nivell subterrani Del Stop Elimina Delete Line Esborra la línia Delete the current stop Eliminar aquesta parada Delete the selected line (if without associated convois). Eliminar la línia seleccionada (si no té combois associats) Delete this file. Esborra aquest fitxer. Delivered Subministrats Demand Demanda Demand: %.0f MW Demanda: %.0f MW\n Denkmal Monument Departed Va sortir Depots Cotxeres Der Tunnel ist nicht frei!\n El túnel no està buid.\n Destination Destinació Destroying map ... Destruint mapa... Details Detalls Die Bruecke ist nicht frei!\n El pont no està lliure!\n diesel dièsel Direkt erreichbare Haltestellen Aturades connectades directament disable midi Lleva la música MIDI Display settings Ajustaments pantalla Distance Distància Dock Moll Dock must be built on single slope! Els molls només poden bastir-se damunt un pendent. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Teniu %d mesos per a pagar allò que deveu. Durchsatz Màx. prod. Economy Economia i ciutats Eigenbesitz\n Propietat pública\n Ein %s\npasst hier nicht.\n Un '%s'\nno es pot emprar aquí.\n Einstellungen aendern Canvia opcions electric elèctric Electricity Electricitat Electricity producer\n\n Productor d'electricitat\n\n Electrics_tab Elèctrics Electrify track Electrifica la línia enlarge map Eixamplar mapa enter a value between %i and %i entri un valor entre %i i %i Enter address Introduir adreça Enter Password Introduir contrasenya Error Errada Erzeuge neue Karte.\n Per favor, espereu.\nEstic creant un\nmapa nou ...\n\n (Pot trigar-se\n alguns minuts\n per a mapes grans.)\n Es wird bereits\nein Fahrplan\neingegeben\n Esteu planificant\nuna ruta.\nFinalitzeu-la abans\nde tornar-ne a crear!\n Fabrikanschluss Indústries connectades Fabrikname nom de la fàbrica Factories Fàbriques factory details Detalls de la fàbrica factorybuilder Constructor d'indústries Fahrplan Enruta Fahrtziel Destinació: Fahrzeuge koennen so nicht entfernt werden No podeu eliminar\nels vehicles\naquí.\n Fahrzeuge: Vehicles: Farbe Color jugador Fast forward Accelerar temps February Febrer Ferry_tab Vaixells Fertig Fet Filename Fitxer: Filter: Filtre: Finances of %s Finances de %s Finanzen Finances find mismatch Comparant paks fl_title Llistat d'indústries Flug_tab Passatgers d'avió follow me Seguiu-me. Follow the convoi on the map. Segueix el convoi en el mapa. Forest Boscos Found new city Crear una nova població Fracht Mercaderia Frame time: Retard: Free Capacity Capacitat freeplay mode Sense fallida Friction: quoeficient de fricció: fuel_cell pila de combustible Full load Càrrega mínima Fundament Cimentació Fussgaenger Vianant Game info Informació de la partida GAME PAUSED JOC EN PAUSA Game_msg General Gear: Marxa: Gebaeude Edifici Generated Generat Generation: %.0f MW Generació: %.0f MW\n Gewicht Pes Gewinn Ingressos Give the selected vehicle(s) an individual schedule Atorga al(s) vehicle(s) seleccionat(s) un itinerari individual. gl_btn_sort_catg per categoria gl_title Llistat de béns go home Vés a cotxera. Goods Mercaderies Goods AI Mercaderies IA Goods list Llistat de béns Gross Profit Benefici brut Groundobj Objecte Grow city Amplia la ciutat. Growth Creixement urbà H - Parada Happy Content Haus kaufen Comprar casa Helligk. Pantalla Help Ajuda Help text not found No trobo el text d'ajuda. hide all building Amaga tots els edificis hide city building Amagar edificis urbans hide objects under cursor Amagar objectes sota el cursor hide station names amagar noms d'estacions hide transparent transparents, no amagats hide trees Amagar àrbres Hier warten/lagern: Passatge/mercaderia esperant: Higher transport fees, crossconnect all factories Taxes de transport més grans, desactivar Just-in-Time Highlite schedule Destacar parades d'itineraris hl_title Llistat d'estacions hl_txt_filter Filtre: hl_txt_sort Ordena per: hlf_chk_airport Aeroports hlf_chk_anleger Vaixells hlf_chk_bahnhof Estació de ferrocarril hlf_chk_bushalt Parada d'autobús hlf_chk_frachthof Zona de càrrega hlf_chk_keine_verb Sense connexió hlf_chk_maglevstop Estació de maglev hlf_chk_monorailstop Estació de monocarril hlf_chk_name_filter Nom filtre: hlf_chk_narrowgaugestop Estació via estreta hlf_chk_overflow Saturat hlf_chk_spezial_filter Filtre especial: hlf_chk_tramstop Estació de tramvia: hlf_chk_type_filter Tipus transport: hlf_chk_waren_abgabe Producció: hlf_chk_waren_annahme Tipus de càrrega: hlf_title Mostra sols aquestes estacions: Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Cotxera inicial no trobada!\nHa d'enviar manualment\nel comboi a la cotxera. Homeless Sense sostre: hydrogene Hidrògen Idle: Inactiu: ignore climates ignorar climes In the industry legend show only currently existing factories Només indústries existents Increase Industry density Afegir cadena industrial increase underground view level pujar nivell subterrani industrial building Edifici industrial Init map ... Iniciar mapa... Input Subministrament Ins Stop Inserta Insert stop before the current stop Inserta una parada abans d'aquesta parada. Intercity road len: Long. carretera interurbana: Intro. date: Introducció: invalid no vàlid. Invalid coordinate Ordre no vàlida isometric map Mapa isomètric January Gener join game Jugar online July Juliol Jump to Anar a June Juny Kann Spielstand\nnicht laden.\n ¡És impossible llegir/obrir\nl'arxiu guardat!\n Kann Spielstand\nnicht speichern.\n ¡És impossible obrir\nl'arxiu per a\nguardar-lo!\n Kein Besitzer\n Sense propietari keine res Keine Einzelfahrzeuge im Depot No hi ha vehicles emmagatzemats. Keyboard_Help\n Ajuda del teclat\n koord Coordenades Kreuzung Cruïlla labellist_title Llista de marcadors Lade Relief Carrega topografia Laden Carrega Land attraction Atracció turística d'interior Land industries Cadenes industrials: LANG_CHOOSE\n Per favor, trieu\nun idioma:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Mes passat: Last Year Any passat: Leaving depot! Sortint de la cotxera! leer buit Legend Llegenda del mapa Leistung Potència Leistung: %d kW Potència: %d kW Leitung Línia elèctrica letzen Monat: diesen Monat: darrer mes: aquest mes: Line Línia Line Management Administració de línies Lineless convoys serving this stop Comboi sense línia servint aquesta parada: Lines serving this stop Línies servint aquesta parada LKW_tab Camions Load game Carregar joc load height data from file Carregar topografia des d'arxiu Load scenario Carrega escenari loaded carregat loaded passenger/freight Ordenar passatgers/mercaderia per Loading (%i->%i%%)! Carregant (%i->%i%%) Loading addon paks ... Carregant objectes extra ... Loading map ... Carregant el mapa... Loading paks ... Carregant paks ... Loading skins ... Carregant temes d'aparença ... Lock game Bloquejar canvi de jugador (demana confirmació) Lokomotive_tab Màquines de tren m3 m³ maglev vehicle vehicle maglev maglev_track via maglev Maglevdepot Cotxera de maglev Mail Demand %d\n Demanda de correu %d\n Mailbox Bústia Mailbox Options Opcions de la bústia Maintenance Manteniment make stop public (or join with public stop next) costs %i per tile and level Fer pública la parada (o fusionar-la amb la següent) costa %i$ per quadricula i nivell Manual (Human) Manual (Humà) Manufactured: Fabricat: Map roughness Terreny abrupte: map zoom ampliar mapa March Març Margin (%%) Marge (%%) Marker Col·loca un cartell max màx. Max Boost (%%) Augment máx.(%%) Max income: Ingressos màx. Max. speed: Vel. màx. Maximum 254 stops\nin a schedule!\n Màxim 254 parades\nper línia!\n maximum length of rivers long. màxima rius Maximum tile height difference reached. La diferència total\nd'alçada entre dos\ncaselles ha de ser\ninferior a dos nivells. Maxspeed Velocitat màxima May Maig Median Citizen per town Població per ciutat: Meldung Missatge Menge quantitat MessageOptionsText \nAny nou\n\nNotícies AI\n\nNotícies Ciutats\n\nSense Ruta\n\nNoves Indústries\n\nXat\n\nNous Vehicles\n\nEstacions Saturades\n\nProblemes\n\nEmbussos\n\nEscenari min mín. minimum length of rivers long. mínima rius Missing pakfiles Falten objectes (pak)! Modify the selected line Modificar la línia seleccionada. Monate alt Mesos d'antiguitat Monorail Monocarril monorail vehicle vehicle de monocarril monorail_track via de monocarril Monorailboden Suport de monocarril Monoraildepot Cotxera de monocarril month wait time Espera (mesos) Months Mesos Mountain height Alçada muntanyes: Movingobj Objecte mòbil Music playing disabled/not available Música desactivada o no disponible Music volume: Volum de la música: mute sound treure so Name Nom Narrowgauge Via estreta Narrowgauge are not available yet! Encara no estan disponibles els vehicles de via estreta! narrowgauge vehicle vehicles de via estreta narrowgauge_track Via estreta Narrowgaugedepot Cotxera de trens de via estreta Net ID: %p ID xarxa: %p\n Net Wealth Valor net Net wealth near zero Valor net prop de zero Neue Karte Nou Neue Welt Crea un nou món new convoi Nou comboi New Line Nova línia New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Nova línia creada!\nPots assignar la línia\nseleccionant-la des del\ndesplegable 'línia' d'adalt. New Vehicles Vehicles nous Nickname: Nick: no buildings hidden no hi ha edificis ocults no convois sense combois No goods are loaded onto this convoi. Aquesta configuració no transporta mercaderies no goods waiting No hi ha mercaderia esperant no load Sense càrrega No Route Sense ruta No stop here! Aquesta eina s'ha de fer servir sobre una parada! No suitable ground! No és el terreny adient! No terminal station here! No hi ha estació terminal! no timeline tots temps no tree sense arbres Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n ¡Assigneu una ruta\nal vehicle abans\nde donar-li l'ordre\nde sortida!\n none res nord Nord nordost Nord-est nordwest Nord-oest Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! No està permès!\nLa línia ferroviaria no pot\ncambiar-se ara.\nProvi-ho més tard. Not enough fields would remain. No hi ha prou\nespai al voltant\d'aquesta granja. November Novembre Now active as %s.\n Ara actiu com %s.\n Number of rivers rius Object Objecte Odometer: %s km Quilometratge: %s km Ok Acceptar Oktober Octubre On loan since %i month(s) En números vermells durant %i mesos On this map, you are not\nallowed to change player!\n La partida està bloquejada!\nNo es pot canviar de jugador!\n Only city chains Només indústries de ciutat Only first %d differing paks reported. There are probably more. Només els primers %d paks diferents reportats. Probablement n'hi hagi més. Only land chains Només cadenes industrials de ciutat Only one transformer per factory! Cada fàbrica només pot tenir un transformador! Only show goods which are currently handled by factories Només mostrar mercaderies processades actualment per les indústries open obert Operation Costos d'operació Ops Profit Benefici operació Optionen Opcions Or enter a server manually: O entri un servidor manualment: Origin Origen ost Est Output Producció Ownership Propietari Pak which may cause severe errors: Els següents objectes (pak) falten, cosa que pot provocar errors en les xarxes de transport: Pak which may cause visual errors: Els següents objectes (pak) han estat substituïts per objectes semblants: Pak(s) different: Pak(s) diferents: Pak(s) missing on client: Pak(s) no trobats al cliente: Pak(s) not on server: Pak(s) no trobats al servidor: Pakset differences Diferències de paksets paletten paquets Pas_tab Trens de passatgers Passagiere Passatgers Passagierrate Nivell de passatgers Passagierziele Destinació del passatge/correu Passenger AI IA de passatgers Passenger Demand %d\n Demanda de passatgers: %d\n Passengers %d %c, %d %c, %d no route Passatgers %d %c, %d %c, %d sense ruta Passengers %d %s, %d %s, %d no route Passatgers %d %s, %d %s, %d sense ruta Password Contrasenya Pause Pausa Pax <%i> Mail <%i> Passatgers <%i> Correu <%i> PaxDest Destins Percent Electricity Producció elèctrica (%% demanda): Planes are not available yet! Encara no estan disponibles els avions! Plant tree Plantar un arbre player Jugador player -1 Humà player 0 servei públic player 1 Napik 128 AS player 10 jugador 10 player 11 jugador 11 player 12 jugador 12 player 13 jugador 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Spedition VM player 5 H-Trans GmbH player 6 PSK & Co KG player 7 jugador 7 player 8 jugador 8 player 9 jugador 9 Please choose vehicles first\n Si us plau, seleccioneu primer els vehicles!\n Post Correu Postrate Nivell de correu Power Potència Power (MW) Potència (MW) Power: Potència: Powerlines Línies elèctriques Problems_msg Problemes Produced Produït Production of %s has been stopped:\n%s\n El %s %s\ns'ha deixat de fabricar\n Production/Boost Producció/Augment Produktion Producció Profit Benefici promote to line Passar a línia\n\n q1 Primavera q2 Estiu q3 Tardor q4 Hivern Query server Contactar servidor rail car Vagó random aleatori Random age Període a l'atzar Random map Mapa l'atzar Rathaus Ajuntament Rating Qualificació ratio_pax rati pass. Relevant Filtrats Reliefkarte Mapa Remove Eliminar remove airstrips Eliminar pistes remove channels Eliminar canals remove interm. signals Eliminar senyals intermitges remove maglev tracks Eliminar vies de maglev remove monorails Eliminar monocarril remove narrowgauge tracks Eliminar trams de via estreta remove powerlines Eliminar línies elèctriques remove roads Eliminar carreteres remove tracks Eliminar vies de tren Remove wayobj %s Eliminar objecte %s replace other signals Substituir altres senyals: replace stop Actualitzar parada request closing Petició de tancament residential house Edifici residencial Restore natural slope Restaurar pendent natural Restwert: Valor de venda: Retire. date: Descatalogat: return ticket Viatge tornada Revenue Ingressos Revision: Revisió: road carretera road vehicle vehicle de carretera Roadsign Senyal de trànsit Rotate map Girar mapa Rotation Rotació Routing Encaminant sack sacs sail vent Saving map ... Guardant mapa... Scenario complete: %i%% Escenari completat: %i%% Scenario Debug Depurar Scenario Error Log Errors de script Scenario Goal Objectius Scenario Info Informació Scenario information Informació Scenario Result Resultat Scenario Rules Normes Schedule changing! Canviant ruta! Schienentunnel Construeix túnel ferroviari Schiff_tab Vaixells Schiffdepot Drassana Schleppkahn_tab Barcasses Screenshot Captura de pantalla Seasons Estacions Sehenswuerdigkeit Atracció turística Select a server to join: Seleccionar un servidor per connectar-hi: Sell the selected vehicle(s) Ven els vehicles seleccionats sended enviat SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 September Setembre Server did not respond! El servidor no ha respòs! Serves Line: Serveix la línia: Service Servei set signal spacing Establir espai entre senyals Setting Configuració Ship Vaixell shops and stores Botigues i oficines Show all Veure tot show all building Veure tots els edificis Show also vehicles no longer in production. Veure també els vehicles obsolets. Show also vehicles that do not match for current action. Veure també vehicles que no poden fer-se servir en l'acció seleccionada. Show even servers with wrong version or pakset Veure fins i tot servidors amb versions i paksets no compatibles. show grid Veure quadrícula Show industry Llista d'indústries Show legend Llegenda Show map scale Escala Show mismatched Veure incompatibles Show obsolete Veure obsolets Show offline Veure apagats Show only used Veure només els usats Show schedules Veure itineraris Show servers that are offline Veure servidors apagats Show servers where game version or pakset does not match your client Veure servidors amb versions o paksets no compatibles show station coverage Veure cobertura d'estacions show station names Veure noms d'estacions show waiting bars Veure barres d'estat show/hide block reservations Veure/amagar reserves de blocs show/hide object owner Veure/amagar propietari Show/hide statistics Veure/amagar estadístiques Shows buttons on special topics. Mostra els botons de la llegenda del mapa Shows consumer/suppliers for factories Mostra consumidors/proveïdors de les indústries Shows the color code for several selections. Representa al mapa la selecció fent servir una escala de colors Shows the currently selected schedule Mostra l'itinerari seleccionat\n Shrink city Reduïr ciutat shuffle midis barrejar midis Signal Senyal signal spacing Espai entre senyals Sim: Cicles: Similar view as the main window Vista semblant a la principal del joc Size (%d MB): Mida de mapa (%d MB): sliced underground mode Vista per alçades slot empty Slot buit Smart hide objects Amagar sota el cursor Sort by ordenar per Sort waiting list by Ordenar la llista d'espera per Sound So Sound settings Ajustaments de so Sound volume: Volum dels efectes: special freight Càrrega especial Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Bonus per velocitat\nCarretera %i km/h, Ferrocarril %i km/h\nVaixells %i km/h, Avions %i km/h.\n\n Speedlimit Límit velocitat Speichern Guarda Spieler Jugador Spieler(mz) Jugadors Spielerliste Llista de jugadors Spielstand wurde\ngeladen!\n \n¡Partida carregada!\n Spielstand wurde\ngespeichert!\n \n¡Partida salvada!\n Sprache Idioma Sprachen Idiomes Stadtinformation Estadístiques de la població Start Arrenca Start the selected vehicle(s) Arrencar els vehicles seleccionats. Starte Spiel Començar Station tiles: Caselles d'estació: Station_msg Estacions Status Estat parades steam vapor Step timeline one year Avançar cronologia un any. Stops Parades Storage Magatzem Storage capacity Capacitat d'emmagatzematge Strassendepot Cotxera camió/bus Strassentunnel Construeix túnel de carretera street car tramvia sued Sud suedost Sud-est suedwest Sud-oest Summer snowline Cota estival Supplied: %.0f %% Subministrat: %.0f %% Suppliers Proveïdors Tage alt dies antiguitat There are still vehicles\nstored in this depot!\n Encara hi ha vehicles\napartats en aquest dipòsit\n This Month Aquest mes: This Year Aquest any: Tile not empty. La casella no és buida\nSi us plau, elimini tot\nabans de construir\nun nou talús. timeline Cronologia tl_title Llista de totes les ciutats To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. %s ha construït\nun %s\namb l'ajuda de\n%i contribuents.\nEs preveu un augment\ndel turisme! To heavy traffic\nresults in traffic jam.\n L'excés de transit ha\nprovocat un embús!\n Toggle day/night view Activar vista dia/nit Toggle vehicle tooltips Activar missatges de vehicles tonnen t Total inhabitants: Població: Tourist attractions Zones de turisme: Tourists Zones de turisme Town_msg Nous destins Town: %s\n Ciutat de %s.\n Towns Ciutats track via Tracks Vies Traffic Trànsit Train Tren Trains are not available yet! Els trens encara no estan disponibles! Tram Tramvia tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. tramvia %i km/h, monocarril %i km/h\nmaglev %i km/h, via estreta %i km/h. tram_track via de tramvia Tramdepot Cotxera de tramvia Trams are not available yet! Els tramvies encara no estan disponibles! Transferring game ... Transferint partida... Transformer only next to factory! Els transformadors s'han d'ubicar al costat d'una indústria! Translation Traducció transparent station coverage cobertura transparent Transported Viatges TrolleyBus_tab troleibús Truck Camió Tunnel muss an\neinfachem\nHang beginnen!\n ¡El túnel ha de\ncomençar a una\npendent recta!\n Tunnel must start on single way! Un túnel ha de començar en una sola carretera! Tunnelboden Túnel underground mode vista subterrània UNDO failed! No es pot desfer.\nNomés es pot desfer la\nconstrucció d'una ruta si\nno s'han construït sobre la\ncarretera senyals, estacions,\nparades ni cap altra cosa. Undo last ways construction Desfer la darrera via/carretera creada Unemployed Aturats Unhappy Descontents units/day /dia, Update Line Modificar itinerari upgrade HQ Millorar seu central Usage: %.0f %% Ús: %.0f %% Usage/Output Consum/Producció Use beginner mode Activar mode lliure Use timeline start year Cronologia des de Vehicle %s can't find a route! El vehicle %s\nno pot trobar l'itinerari! Vehicle %s is stucked! El vehicle %s està embussat! Vehicle details Detalls del vehicle Verbrauch Consum Vergroessere die Karte\n Expandir el mapa.\n verkaufen Vendre Verkehrsteilnehmer cotxes de ciutat Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n ¡Teniu un deute!\n\n¡Us resten %d mesos\nper a pagar-lo!\n via per (detall) via %s\n per %s\n via Menge per (quantitat) voranstellen Posar al davant Waggon_tab Vagons waiting esperant Waiting for clearance! Esperant via lliure! Walked caminant Warnings_msg Avisos Wasser Aigua Water Canal Water level Nivell de l'aigua: water vehicle Vaixell way %s cannot longer used:\n El tipus de via %s ja no es pot fer servir.\n way %s cannot longer used:\n%s\n el tipus de via %s ja no es pot utilitzar:\n way %s now available:\n Nou tipus de via disponible: %s\n Ways not connected Les vies no estan connectades Wegpunkt Punt de pas Wert Valor west Oest Winter snowline Cota hivernal withdraw Vendre Withdraw All Vendre tot WRONGSAVE La versió salvada és\nincompatible. No se pot llegir\nl'arxiu.\n Year %i has started. Ha començat l'any %i. Years Anys Your primary color: El seu color primari: Your secondary color: El seu color secundari: Zielort destí zooming in apropar zooming out allunyar Zu nah am Kartenrand Massa prop de\nl'extrem del mapa\nper a construir.\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nou rècord mundial de maglevs: %.1f km/h per %s. New world record for monorails: %.1f km/h by %s. Nou rècord mundial de monocarrils: %.1f km/h per %s. New world record for motorcars: %.1f km/h by %s. Nou rècord mundial d'automòbils: %.1f km/h per %s. New world record for narrowgauges: %.1f km/h by %s. Nou rècord mundial de trens de via estreta: %.1f km/h per %s. New world record for planes: %.1f km/h by %s. Nou rècord mundial d'avions: %.1f km/h per %s. New world record for railways: %.1f km/h by %s. Nou rècord mundial de ferrocarrils: %.1f km/h per %s. New world record for ship: %.1f km/h by %s. Nou rècord mundial de vaixells: %.1f km/h per %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL de Mar &1_CITY_SYLL d'Avall &2_CITY_SYLL de Túria &3_CITY_SYLL -roja &4_CITY_SYLL d'Ebre &5_CITY_SYLL castell &6_CITY_SYLL marxant &7_CITY_SYLL d'Amunt &8_CITY_SYLL cor &9_CITY_SYLL ssa &A_CITY_SYLL dels Reis &B_CITY_SYLL per &C_CITY_SYLL Això no existeix! &D_CITY_SYLL Això no existeix! &E_CITY_SYLL Això no existeix! %0_CITY_SYLL Això no existeix! %1_CITY_SYLL Vila %2_CITY_SYLL La Pobla %3_CITY_SYLL Murada %4_CITY_SYLL Santa %5_CITY_SYLL Riba %6_CITY_SYLL Cala %7_CITY_SYLL Mana %8_CITY_SYLL Massia %9_CITY_SYLL Nova %A_CITY_SYLL Casa %B_CITY_SYLL Beni %D_CITY_SYLL Això no existeix! %E_CITY_SYLL Això no existeix! %F_CITY_SYLL Això no existeix! 1center %s %s 1extern %s%s Eixample 1suburb %s %s %s 2center %s%s Centre 2extern %s%s Baixador 2suburb %s Camps verds %s %s 3center %s%s Principal 3extern %s%s Extrarradi 3suburb %s vila %s %s 4center %s%s Avinguda 4extern %s%s Bosc 4suburb Això no existeix! 5center %s%s Passeig 5extern Això no existeix! 5suburb Això no existeix! 6center %s Connexió %s 6extern Això no existeix! 6suburb Això no existeix! 7center %s ciutat %s Acenter %s central %s Aextern %s barri %s Asuburb %s, zona suburbana %s Bcenter %s centre de %s Ccenter %s centre %s %s Csuburb %s afores %s simutrans-124.5/simutrans/text/cn.tab000066400000000000000000001553411520675460500176760ustar00rootroot00000000000000§Sim. Chinese PROP_FONT_FILE wenquanyi_9pt.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: cn Sim. Chinese # # Encoding: UTF-8 # # Font: wenquanyi_9pt.bdf # # Date Created: 18.04 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times 显示时间格式为dd:hh:mm AMBIENT_SOUND 环境音 CASH_SOUND 取现声音 cl_btn_filter_disable 禁用 cl_btn_filter_enable 启用 cl_btn_filter_settings 设置 cl_btn_sort_asc 升序 cl_btn_sort_desc 降序 cl_btn_sort_id 内部ID cl_btn_sort_income 收入 cl_btn_sort_name 名称 cl_btn_sort_type 类型 clf_btn_alle 全选 clf_btn_invers 反选 clf_btn_keine 取消选择 climate area percentage 调整单一气候 Continue Game 继续游戏 CROSSING_SOUND 穿过警告 FACTORY_SOUND 工厂噪音 Find matching convois 寻找符合条件的编队 gl_btn_sort_bonus 红利 gl_btn_sort_name 名称 gl_btn_sort_revenue 收益 gl_btn_unsort 未排序 height based 海拔气候 hl_btn_filter_disable 禁用 hl_btn_filter_enable 启用 hl_btn_filter_settings 设置 hl_btn_sort_asc 升序 hl_btn_sort_desc 降序 hl_btn_sort_name 名称 hl_btn_sort_type 类型 hl_btn_sort_waiting 等待 hlf_btn_alle 全选 hlf_btn_invers 反选 hlf_btn_keine 取消选择 humidities 湿度边界 Install 安装 Pak 包 Lake 湖 Lake height 湖泊最高水位 Maximize height levels 最大化高度级别 moisture land 高过地面 moisture water 高过水面 Networks 运输网 Num pad keys always move map 忽略小键盘锁定 Open Sea 开放海洋 Queueing 排队中 rainfall 湿度 Reselect closes tools 重选一个工具取消选中它 Return to menu 返回主菜单 Road toll 通行费 Scenario 场景信息 sea 海洋气候 Single GUI 单窗口模式 Sound range: 声音范围: Start this as a server 用这局游戏启动服务器。 temperature borders 温度限制 temperature-humidity based 天气气候 TOOL_SOUND 工具动作声音 TRAFFIC_SOUND 交通噪音 Transfers 客流 Tutorial 教程 #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic 极地气候 desert 沙漠气候 mediterran 地中海气候 rocky 高山气候 temperate 温带气候 tropic 热带气候 tundra 苔原气候 #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed 载入场景脚本失败 A building blocks the construction 前面有栋楼挡路了! Bridge blocked by way below or above. 桥梁高度不足以支撑下方的道路。 Can't buy obsolete vehicles! 不能购买过时的载具! Cannot alter water 这条河流不能被排干或填埋 Cannot build on a double slope! 只能建在单一斜坡或平坦的地面上! Cannot built depot here! 不能在这里建造车库 Cannot built this station/building\nin underground mode here. 该建筑不能建于地底 Cannot connect to the\ncenter of a double slope! 桥梁不能连接到双坡的中间!\n使用地形工具解决此问题。 Cannot create generic line!\nSelect line type by\nusing filter tabs. 不能创建通用线路!\n用筛选选项卡\n选择线路类型。 Cannot create socket 网络初始化失败! Cannot rotate this building! 不能旋转此建筑! Client closed connection during transfer 传输过程中客户端连接中断。 Convoi handles exhausted! 载具总数已达到最大 Convoy already deleted! 已删除编队! Corrupt file 文件已损坏或已截断。 Could not open file 不能访问文件。检查权限。 Das Feld gehoert\neinem anderen Spieler\n 该陆地属于其他玩家 Depots cannot be built on runways! 不能在跑道上建机库! Depots must be built on flat dead-end way tiles! 仓库只能建在平坦路面的尽头! Der Besitzer erlaubt das Entfernen nicht 该建筑拥有者拒绝移除建筑\n汽车或行人在路上,不能移除 Diese Zusammenstellung kann nicht fahren!\n 非法组合 Failed to write file 不能写入文件。(权限问题?) Flugzeughalt muss auf\nRunway liegen!\n 飞机必须放置在滑行道上 Forbidden by scenario 此场景禁止 Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n 不能在这里建造飞机场 Hier kann kein\nSignal aufge-\nstellt werden!\n 不能在这里放置道路标志 In order to lock the game, you have to protect the public player by password! 为锁定游戏,你必须设置密码保护公共玩家! Loading a new game will end the current server session! 载入新游戏将结束当前网络游戏! Lost connection\nto server! 服务器已停止响应!\n游戏目前\n仅支持本地游玩! Lost synchronisation\nwith server. 游戏已与\n服务器\n断开同步,\n重新加载!\n Maglevhalt muss auf\nMaglevschiene liegen!\n 磁悬浮车站必须建造在磁悬浮轨道上 Monorailhalt muss auf\nMonorail liegen!\n 单轨车站必须建造在单轨轨道上 Monorails are not available yet! 单轨铁路还不可用 More than one possibility to build this dock found. 码头可采用多种方向搭建。按住 Ctrl 键并单击再次选中码头,然后在弹出的窗口中选择方向。 Narrowgaugehalt muss auf\nNarrowgauge liegen!\n 窄轨车站必须建造在窄轨轨道上 No bridges over runways! 不要在跑道上架设桥梁! No curves on runways 跑道不能转弯! No suitable crossing 没有合适的路口!(最高速度太快?) No suitable way on the ground! 车站只能被建造在\nstraight等适合的地方 No through station here! 车站必须建造在路的尽头或道路直线段上 Not allowed to copy object. 你不能建造那个对象!\n(原因:玩家错误或对象已过时) Not enough bytes transferred 传输提前结束! Not enough clearance. 两条道路之间需要留有足够的空间(高度过于接近) Not enough empty space 剩余空间不足。 Not enough money! 资金不足 Not initialized 不能初始化库。 On narrowgauge track only!\n 只适用于窄轨轨道上 Only public player can lock games! 只有公共玩家可以锁定此游戏 Only up and down movement in the underground! 地下图块只能使用全部向上/向下工具进行升降! Out of funds 没有足够的钱购买此物件 Post muss neben\nHaltestelle\nliegen!\n 车站扩展建筑必须建设在车站或码头附近 Protocoll error (expecting game) 传输协定错误(等侍游戏) Schiffhalt muss im\nWasser liegen!\n 轮船停靠点只能建造在靠近码头的水面上 Server busy 服务器忙! Server version too new 服务器的程序版本更新。 Ship depots must be built on water! 造船厂必须建在水上! Show all revenue messages 显示所有收益信息 Show no revenue messages 不显示收益信息 Show only player's revenue 仅显示玩家的收益 Terraforming not possible\nhere in underground view 不能在地底修整地形 This tunnel branches. You can try Control+Click to remove. 这条隧道有超过 2 个入口。\n你可尝试按住 Ctrl 键并点击来移除这条隧道。 Too far away to merge stations! 距离太远,不能合并。 Unknown error 未知错误 Upgrade must have\na higher level 新站点的楼层\n必须更高。 Vehicle %s cannot choose because stop too short! 载具 %s 不能选择,因为站点太短! Watertable reached 图块不能再往下放了!\n已触及地下水位。 Zughalt muss auf\nSchiene liegen!\n 火车车站必须建造在铁路轨道上 #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s 帮助

帮助索引

关于模拟交通

%1$s

用法

%2$s

如何开始游戏

%4$s

玩法

%5$s

工具

%3$s

其他窗口

%6$s Keyboard Help\n

Keyboard Help

\n 键盘绑定\n

键盘帮助

\n

\n

按下未分配的键或从常规帮助打开键盘帮助。\n

\n

\n按键区分大小写(按住 [Shift] 键输入大写字母)。\n

\n

\n具有指定功能的按键包括:\n

\n

\n[方向键]: 沿箭头方向滚动游戏视图。
\n[Backspace]: 关闭所有窗口、工具栏和帮助文本。
\n[Delete],或[Esc]: 关闭顶部窗口、工具栏或帮助文本。
\n[Page-Up],或[>]: 放大游戏画面。
\n[Page-Down],或[<]: 缩小游戏画面。\n[CTRL] + 工具: 建造更直接的路径(甚至更快的路径)或在查询或删除期间反转选择顺序。
\n

At least one stop is connected to the town. 至少一个站点与城市中的一座建筑物相连。 At least one tile is connected to one stop. 至少有一栋建筑物与玩家的站点相关联。 Color according to transport capacity left 颜色表示可用运输能力。 Color-coded terrain according to altitude. 显示/隐藏地形轮廓阴影。 Decrease water height 使水面降低一级高度 Highlight railroad tracks 高亮显示铁路轨道 Highlite depots 高亮标记车厂 Highlite electrical transmission lines 高亮标记电力线 Highlite factories 高亮标记工厂 Highlite forests 高亮标记森林 Highlite tourist attraction 高亮标记旅游景点 Increase water height 将水面升高一级高度 Open station/stop details 显示站点详情 Overlay city limits 显示城市边界。 Overlay passenger destinations when a town window is open 若城市窗口已打开:显示城市中的乘客/邮件目的地。 Overlay schedules/network 在地图上标出所有现有连接。 Overlay town names 显示城市名称。 Please click on the map to add\nwaypoints or stops to this\nschedule. 点击地图上的目的地即可添加途经点和站点。 Remove double stops from schedule 删除连续重复条目 Set tile climate %s 将此地面转换为%s带 Show capacity and if halt is overcrowded 显示存储容量和状态。 Show how many convoi reach a station 显示有多少支编队到达站点 Show how many people/much is waiting at halts 显示有多少在等待 Show initial passenger departure 显示乘客和邮件的出发地。 Show level of city buildings 根据客流量显示城市建筑。 Show mail service coverage/mail network 显示邮件服务覆盖范围 Show passenger coverage/passenger network 显示乘客网络覆盖范围 Show speedlimit of ways 显示道路最高限速。 Show the change of waiting at halts 显示等待时间自上个月以来发生了哪些变化 Show the owenership of infrastructure 展示基础设施所有权 Show transported freight/freight network 显示货运/货运网络。 Show usage of network 按图块和月份显示载具 Shows a listing with all industries on the map. 显示全部产业的图例。 Sum of departure/arrivals at halts 显示到达和离开的载具 #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s 现在\n提供与\n景点 %s 间\n的公共汽车\n服务%s\nat (%i,%i)。\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s 现在\n提供与\n工厂 %s 间\n的公共汽车\n服务%s\nat (%i,%i)。\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s 现在运营 %i 货车往返于\n%s(%i,%i) 和 %s (%i,%i) 之间 %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s 在\n%s(%i,%i) 和 %s(%i,%i)\n之间开设了一条新铁路线 Airline service by\n%s\nnow between\n%s \nand %s.\n %s 已开通\n %s \n和 %s \n之间的\n航班服务。\n Ferry service by\n%s\nnow between\n%s \nand %s.\n %s 已开通\n %s \n和 %s \n之间的\n渡轮服务。\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s 现在\n%s 和 %s\n之间开设了一条新巴士线 #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS 地图编辑工具 LISTTOOLS 列表管理 MAGLEVTOOLS 磁悬浮铁路工具 MONORAILTOOLS 单轨/磁悬浮铁路工具 NARROWGAUGETOOLS 窄轨铁路工具 RAILTOOLS 铁路工具 ROADTOOLS 道路工具 SHIPTOOLS 船运工具 SLOPETOOLS 地形工具 SPECIALTOOLS 其他工具 TRAMTOOLS 电车/轻轨工具 #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s 建设了一个新总部 Factory chain extended\nfor %s near\n%s built with\n%i factories. 经济逐渐繁荣\n%s 扩大产业, 于 %s 附近建设了 %i 间工厂 Net wealth less than 10%% of starting capital! 总资产不足初始资本的 10%%! New %s now available:\n%s\n 新 %s 目前可用: \n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. 工业发展: %s 新工业链\n于 %s 附近建立了 %i 工厂 New vehicle now available:\n%s\n 新交通工具现在可用\n\n -- %s -- Now %u clients connected. 现在有 %u 个用户已连接 overtaking 超车 Remove vehicle from map. Use with care! 移除该运输工具(请小心使用!) Screenshot\ngespeichert.\n 游戏截图已保存 Sends the convoi to the last depot it departed from! 将运输队调回最后的发车库 Server preparing game ... 服务器正在准备地图传输 ... Trees 树木 With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s 为某盛大的节日建立了纪念馆\n%i 名市民感到十分高兴 #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter 线路筛选器 #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%.2f$/km %.2f$/m) (%.2f$/km %.2f$/月) (%.2f$/km) (%.2f$/km) (%i)- (%i) (in depot) (在仓库) [0] south-facing (0) 面向南 [1] east-facing (1) 面向东 [2] north-facing (2) 面向北 [3] west-facing (3) 面向西 [4] southeast corner (4) 东南角 [5] northeast corner (5) 东北角 [6] northwest corner (6) 西北角 [7] southwest corner (7) 西南角 /month /月 \nBauzeit bis 直到 \nBauzeit von 出现于 \nCan't open heightfield file.\n 不能打开高度图 \ndirection: 方向 \nelektrified 电气化 \nHeightfield has wrong image type.\n 高度图图像格式不正确 \nis reserved by: 已被火车占用 \nminimum speed: 最低时速: \nnot elektrified 未电气化 \nRibi (masked) 通行方向(右): \nRibi (unmasked) 通行方向(左): \nSet phases: 调节南北/东西的绿灯持续秒数: \nsingle way 单向道路 \nway1 reserved by 道路1被占用: \nway2 reserved by 道路2被占用: \nwith sign/signal\n \n设有交通标志/信号灯\n %d buildings\n %d 建筑物\n %d convois %d 运输队 %d Einzelfahrzeuge im Depot %d 载具存放在这 %d more convois %d 编队 %i car(s), %i 汽车, %i km/h (max. %ikm/h) %ikm/h(最高 %ikm/h) %i years %i months old. 树龄: %i 年 %i 个月 %s at (%i,%i) now public stop. %s 已成为公共运输中转站 %s building %s %s %s %s %s %s city %d %s %s %d %s %s factory %s %s %s %s %s has entered a depot. %s\n已到达仓库。 %s has left. %s 已离开服务器。 %s land %d %s %s %d %s 外 %s now known as %s. %s 现在因 %s 而著名。 %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s 的居民\n达到 %i\n因此兴建了\n一个新市政厅 %s\nis crowded. %s 过度拥挤。 %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\n时速 %i\n最高时速 %i\ndx:%i dy:%i %s\nwas liquidated. %s已破产清算。\n所有运营活动停止,\n所有资产已出售。 %u Client(s)\n %u 用户\n %u days ago %u 天前 %u hours ago %u 小时前 %u minutes ago %u 分钟前 %u Player (%u locked)\n %u 个玩家 (%u 个已锁定) %u years ago %u 年前 <清除行程> <创建新路线> %s - %s
\n [%s]: %s
\n

Error

错误: 主 pak 目录中存在重复对象

In

Warning

addons for

警告: 插件目录中存在重复对象

Addons for <单独行程表> <未指定载具路线> <暂无行程表> <线路行程表> 1 convoi 1支编队 1 Einzelfahrzeug im Depot 1个载具存放在这 1 minute ago 一分钟前 1 year ago 1年前 100 km/h = %i tiles/month 100 km/h = %i 块/月 1LIGHT_CHOOSE 亮度: 1th 1. 1WORLD_CHOOSE 新游戏的设置: 21th 21. 22th 22. 23th 23. 2LIGHT_CHOOSE 颜色: 2th 2. 2WORLD_CHOOSE 地图编号: 31th 31. 3LIGHT_CHOOSE 鼠标卷动速度: 3th 3. 4LIGHT_CHOOSE 鼠標逆向卷动 5LIGHT_CHOOSE 车站的行人 5WORLD_CHOOSE 城市总数: 6LIGHT_CHOOSE 镇上的行人 6WORLD_CHOOSE 交通密度: 8WORLD_CHOOSE 日夜模式 A bridge must end on a way! 桥梁的尽头必须与道路相连! A bridge must start on a way! 这桥的两端只能建于自己拥有的平地上且需在一条线上 Abfrage 视察工具 Abnehmer 消费者 About 关于 About scenario 版权 Abriss 摧毁/移除 Absenken 降低地形 Abspanntransformator 变压站 Accelerate time 加快游戏速度 Act. load: %u MW\n 实际负荷: %u MW Active player only 仅限活跃玩家 Add forest 扩大森林 Add random citycar 随机新增私家车 add server 新增服务器 Add Stop 追加站 Add stops for backward travel 为回程路段增设车站 air 跑道/滑行道 Aircraft 航线 aircraft_tab 货机 airplane 飞机 Airport 机场 AIRTOOLS 机场工具 All 所有 all convoi tooltips 所有编队工具提示 Allow city growth 允许城市增长 Allow player change 允许更换玩家 allowed climates:\n 适宜气候: \n Alters a schedule. 从列车时间表增加/减少车站 and arranged by 编曲 Angenommene Waren 附近工厂所需要的货物: anhaengen 添加 Anhaenger_tab 拖车 Anheben 提高地形 Appends stops at the end of the schedule 为时间表的后面增添车站 Apply Line 应用路线 Apr. 4月 April 四月 Arbeiter aus: 工人居住在: Arrivals from\n 出发地:\n Arrived 抵达 Assets 资产 Aufloesen 解散 Aufspanntransformator 变压站 Aug. 8月 August 八月 auto 自动 Autohalt muss auf\nStrasse liegen!\n 巴士、车辆停靠站必须建在道路上 Available 可用 Available at custom date 可用 Bahndepot 火车车库 Bankrott:\n\nDu bist bankrott.\n 破产:\n\n你已经破产, 游戏结束 based on Simutrans 84.22.1 基于模拟交通 84.22.1 battery 电池 Baum 树 baum builder 栽种树木 Baustelle 建筑地 Bauzeit 建筑时间: Beenden 结束游戏 Beginner mode 初学者模式 Besonderes Gebaeude 旅游景点 BF 站 bio 天然生物 Blockstrecke ist\nbelegt\n \n这段铁路正被其他列车使用。\n Boden 地 Bonus Multiplier: %i%% 奖励倍数: %i%% Bonus Speed: %i km/h 奖励速度: %i km/h Bonusspeed: %i km/h 设计最高时速:%i km/h Boost (%%) 查看大图 Borderless (disabled on fullscreen) 无边框全屏(软件模式) bridge is too high for its type! 不能在这里建造\n因为离地面的高度超出了其桥脚的高度限制 Bridge is too long for this type!\n 桥梁跨度太大\n请选择其他桥梁或分段建造 Bruecke 桥 Bruecke muss an\neinfachem\nHang beginnen!\n 桥必须建在平直的斜坡上 Brueckenboden 桥 Build air depot 建造飞机舱 build choosesignals 建月台选择信号灯 Build city market 在最近的城市建一个新市场。 Build drain 电力转化站 build HQ 建设总部 Build land consumer 建一个新的发电厂 Build maglev depot 建设磁浮列车厂 Build monorail depot 建一个单轨列车库 Build narrowgauge depot 兴建窄轨列车厂 Build powerline 建电线 Build presignals 建预信号灯 Build road depot 建车库 Build ship depot 建船厂 Build signals 建信号灯 Build train depot 建火车库 Build tram depot 建有轨电车库 Build truck depot 建货车库 Building costs estimates 建筑预算 Buildings 建筑物数量 Built artifical slopes 建人造斜坡 Built random attraction 随建任何景点 Bus_tab 巴士 Can be overgrown 可过度发展 Can only move from halt to halt or waypoint to waypoint. 只可以从 \n一个站到宁一个站或 \n一个停顿点到宁一个停顿点移动。 Cancel 取消 Cannot connect to offline server! 不能连接至离线服务器! Capacity 容量 Capacity: 容量 Capacity: %.0f MW 负载量: %.0f MW\n Capacity: %d%s %s\n 运载量: %3d%s %s Capacity: %s\nLoad: %d (%d%%) 容量: %s\n? %d (%d%%) Cars are not available yet! 车辆仍未制造。 cars.\nstate 车辆\n Cash 账户余额 Category 类别 Change label style 更改地名和站点名称的风格 Change player 换玩家 Chart 图表 Chat_msg 聊天 Choose direction 选择方向 Choose operation executed on clicking stored/new vehicles 请为新交通工具/在库的交通工具,选择制定操作。 chooses a random map 随选地图。 citicens 居民 Cities can only be built on flat empty ground! 城市只能建立在平坦的地面上! City attraction 城市景点 City industries 城市市场: City list 城市列表 City size 城市规模 city_road 城市道路 citybuilding builder 城市建筑工具 CityLimit 城市范围限制 cl_title 载具列表 cl_txt_sort 排序: clamp 限制数值范围 Cleanup schedule 删除重复条目 Clear block reservation 显示/重置占用路段 clf_chk_aircrafts 飞机 clf_chk_cars 巴士/卡车 clf_chk_indepot 在车库 clf_chk_maglev 磁悬浮列车 clf_chk_monorail 单轨铁路 clf_chk_name_filter 筛选器名称: clf_chk_narrowgauge 窄轨铁路 clf_chk_noincome 无收入 clf_chk_noline 无指定路线 clf_chk_noroute 无适当路线 clf_chk_noschedule 无行程表 clf_chk_obsolete 过时 clf_chk_ships 船 clf_chk_spezial_filter 特殊筛选器: clf_chk_stucked 困住 clf_chk_trains 列车 clf_chk_trams 电车 clf_chk_type_filter 禁显示类别: clf_chk_waren 禁显示货物类: clf_title 交通工具列表禁显示工具 Click to open the finance dialog. 点击以打开财务对话框。 Climate 气候 Climate Control 设置气候 Climates 气候带 closed 栏截 Closes topmost line window when new line selected. 选中新线路时,关闭顶部线路窗口。 COLOR_CHOOSE\n 请从以下选择一个颜色:\n Company bankrupt 你的公司已经破产! company_chat 公司聊天 Company_msg 竞争对手 Comparing pak files ... 正在比对游戏资料档案... Composed by 作曲 Configure AI 配置 AI Configure AI setttings 配置 AI 设置 Congratulation\nScenario was complete in\n%i months %i years. 恭喜!\n历经 %i 月 %i 年, 此游戏场景已完成! Connect factory 连接工厂 Connected stops 使用的站: Connected with server 已连接到伺服器 connection 换乘点 Connections 换乘点 Constructed by 绘画者: Constructed by %s 由 %s 绘制 construction speed 建设速度 Construction_Btn 建设开支 Consumed 已消耗 contains the following doubled objects:

以下对象包含不止一次:

convoi %d of %d 交通工具 %d / %d convoi error tooltips 显示交通工具的错误提示 Convoi following mode 正在被跟踪隧道内编队 Convoi has been sent\nto the nearest depot\nof appropriate type.\n 交通工具已经被召回到最近\n同类的库。\n Convoi is sold when all wagons are empty. 在编队完全空时,它将会被卖。 convoi mouseover tooltips 滑鼠指标下的交通工具显示提示 convoi passed last\nmonth %i\n \n上月交通流量: %i\n Convois 交通工具 Convois: %d\nProfit: %s 载具: %d\n利润: %s Convoys 编队 Copy Convoi 复制此交通工具 Copy the selected convoi and its schedule or line 复制此交通工具的与路线/行程表 Copy to clipboard 将文本复制到剪贴板。 Cost 运营成本 cost for removal 清除费 Cost per unit 单位成本 Cost: %8s (%.2f$/km %.2f$/m)\n 成本: %s (%.2f$/km %.2f$/月)\n Cost: %8s (%.2f$/km)\n 成本: %8s (%.2f$/km)\n Costs 成本 Create a new line based on this schedule 根据此行程表开设一条新路线 curiosity builder 景点工具 curlist_title 景点列表 CURRENCY ¥ CURRENCY_LEFT 1 Current game 当前游戏 Currently playing: 进行中: Customers live in: 顾客住在: Date format 日期格式 DAY_SYMBOL 日 deactivated in online mode 不适用于连线游戏 Dec. 减速 Deccelerate time 放慢时间 December 十二月 decrease underground view level 将切面推落一层 Del Stop 删除 Delete Line 删除路线 Delete the current stop 清除此站 Delete the selected line (if without associated convois). 删除选定的线路(如无关联的编队) Delete this file. 删除此文件。 Delivered 送达 Demand 需求 Demand: %.0f MW 需求: %.0f MW\n Denkmal 古迹 Departed 已出发 Departs at 出发于 (dd:hh:mm) Departure after %% 装载或等待 (dd-hh-mm) Departure board 出发时间 Departures per month 每月出发数 Departures to\n 前往\n Depot 仓库 Depots 车库 Der Tunnel ist nicht frei!\n 隧道内仍有载具在行驶 Destination 到达 Destroying map ... 销毁旧地图... Details 详情 Developed by the Simutrans Team 由模拟交通制作组开发 Die Bruecke ist nicht frei!\n 桥上存在其他玩家的所有物 diesel 柴油 direct_chat_to: 私信至 directmail 发信 Direkt erreichbare Haltestellen 从这里开始的直达路线 disable midi 关闭背景音乐 Display bars above factory to show the status 显示有关工厂的信息,包括工厂名称、货物数量和产量。 Display settings 显示设置 Distance 行走距离 distributing cities 分布城市 distributing factories 分布工厂 Do not show 不显示 Dock 码头 Dock must be built on single slope! 码头只能建造在沿海单面斜坡上 Downloading 下载中 dp_title 仓库列表 Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen 仅余 %d 个月还清债务 Durchsatz 最大 Economy 经济及城市 Eigenbesitz\n 公共财产 Ein %s\npasst hier nicht.\n 空间不足, 不能建造 %s Einstellungen 设定 Einstellungen aendern 变更选项 electric 电 Electricity 电力 Electricity producer\n\n 电力供应商 Electrics_tab 电气化 Electrify track 电气化路轨 enlarge map 扩大地图 enter a value between %i and %i 请输入介乎于%i与%i之间的数值 Enter address 输入服务器地址 Enter Password 更改玩家名称及密码 Error 错误 Erzeuge neue Karte.\n 新地图创建中, 请稍候\n(大地图耗时会稍久一些) Es wird bereits\nein Fahrplan\neingegeben\n 正在安排行程表\n请在重新安排前先完成设定 Extract files 从压缩包提取pak文件。 Fabrikanschluss 相关厂商 Fabrikname 工厂名称 Factories 工厂 factory details 工厂详情 factorybuilder 工厂工具 Fahrplan 行程表 Fahrtziel 目的地 Fahrzeuge koennen so nicht entfernt werden 非法移除运输工具 Fahrzeuge: 载具 Farbe 玩家颜色 Fast forward 快速进行 February 二月 Ferry_tab 渡轮 Fertig 已完成 Filename 文件名 Files from: 文件来自: Filter: 筛选: Finances of %s %s 的资产 Finanzen 资产 find mismatch 比对游戏资料档 fl_title 工厂列表 Flug_tab 客机 follow me 跟随 Follow the convoi on the map. 在地图上跟随运输队 font size 字体尺寸 (仅支持 .ttf 和 .otf 字体) For questions and support please visit: 如有疑问或需要帮助,请访问 Forest 森林 Found new city 开发新城市 FPS: 帧率: Fracht 货物 Frame time: 帧时: Free Capacity 空载量 freeplay mode 自由模式 Freight 货运 Friction: 当前摩擦系数 fuel_cell 燃料箱 Full load 最小负载 Fullscreen (changed after restart) 全屏模式(硬件模式,切换需要重启) Fundament 地基 Fussgaenger 行人 Game info 游戏信息 GAME PAUSED 游戏已暂停 Game_msg 一般 Gear: 齿轮 Gebaeude 建筑 General 一般 generate script 生成基础设施宏脚本 Generated 已生成 Generation: %.0f MW 发电量: %.0f MW\n Gewicht 重量 Gewinn 收入 Give the selected vehicle(s) an individual schedule 给所选的载具制定独立的行程表 gl_btn_sort_catg 类别 gl_title 商品列表 go home 回车库 Goods 货物 Goods AI 电脑玩家-货运 Goods list 货物清单 Gross Profit 资金流动 Groundobj 目标 groundobj builder 地面对象建造器 Grow city 发展城市 Growth 城市发展 GUI settings GUI H 站点 Hangar 机库 Happy 开心 Has slope graphics 有坡度图形 Has Snow 下雪 Haus kaufen 购买房屋 Helligk. 显示 Help 帮助 Help text not found 帮助文档不存在 hide all building 隐藏所有建筑 hide city building 隐藏城市建筑 Hide labels 隐藏地点和站点名称 hide objects under cursor 隐藏鼠标指针下的物体。 hide station names 隐藏车站名称 hide transparent 透明化隐藏建筑 hide trees 隐藏树木 Hier warten/lagern: 待运乘客/货物 Higher transport fees, crossconnect all factories 提高运输费用, 关闭及时制度 highlight single ways 显示交通路线的终点/中断点。 Highlite schedule 高亮显示行程表中的站点 hl_title 车站列表 hl_txt_filter 分类: hl_txt_sort 排序方法: hlf_chk_airport 机场 hlf_chk_anleger 码头 hlf_chk_bahnhof 铁路车站 hlf_chk_bushalt 公共汽车站 hlf_chk_frachthof zai hlf_chk_keine_verb 未连接 hlf_chk_maglevstop 磁悬浮列车站 hlf_chk_monorailstop 单轨铁路车站 hlf_chk_name_filter 名称: hlf_chk_narrowgaugestop 窄轨铁路车站 hlf_chk_overflow 超载 hlf_chk_spezial_filter 特殊: hlf_chk_tramstop 有轨电车停靠点 hlf_chk_type_filter 类别: hlf_chk_waren_abgabe 生产: hlf_chk_waren_annahme 物资 hlf_title 车站列表筛选 Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. 找不到仓库\n请手动设置行程表令其回到仓库 Homeless 流浪者 hydrogene 氢气 Icon size: 图标尺寸 Idle: 闲置: ignore climates 忽略气候限制 In the industry legend show only currently existing factories 仅在显示产业图例中当前存在的工厂 In Transit 在转运途中 Increase Industry density 增加产业密度 increase underground view level 将切面推上一层 industrial building 工业建筑 Industry overlay 产业状态指标 Infinite mouse scrolling 无限地图滚动 Infinite scrolling using mouse 不能在触摸屏上使用! Init map ... 初始化地图中... Input 原料 Ins Stop 插入站 Insert stop before the current stop 在此车站前增加车站 Install graphics 安装图形包 Install paks Pak 图形包套件已下载并安装。 Insufficient funds! 资金不足! Intercity road len: 城际道路长度 Intro. date 开发日期 Intro. date: 开发日期 invalid 未定义 Invalid coordinate 指令无效 Invert stops 反转站序 isometric map 等距视图 January 一月 join game 连线游戏 July 七月 Jump to 跳至 June 六月 just now 现在 Kann Spielstand\nnicht laden.\n 不能载入存储的游戏 Kann Spielstand\nnicht speichern.\n 不能打开目标文件 Kein Besitzer\n 无物主 keine 没有 Keine Einzelfahrzeuge im Depot 无载具停留在此 Keyboard_Help\n 快捷键帮助 koord 座标 Kreuzung 十字路口 labellist_title 玩家标记列表 Lade Relief 载入高度图 Laden 载入 Land attraction 郊外景点 Land industries 工业链 LANG_CHOOSE\n 请选择界面语言 LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month 上月 Last used tools 上次使用的工具 Last Year 上年度 Leaving depot! 正离开仓库! leer 空的 Left button drags minimap too 鼠标左键还可拖动小地图 legacy (large heights) 传统(双倍高度) legacy (small heights) 传统(半高) Legend 图例 Leistung 功率 Leistung: %d kW 功率: %d kW Leitung 电线 length: %d 长度: %d letzen Monat: diesen Monat: 上月 本月 Line 路线 Line Management 路线管理 line name mouseover tooltips 线路名鼠标悬停提示 linear 线性 Lineless convoys serving this stop 途经本站的车辆 Lines serving this stop 途经本站的路线 List of industries on the map 地图上的产业列表 LKW_tab 货车 Load game 载入游戏 load height data from file 从文档载入高度图 Load mode: 加载模式: Load scenario 载入场景 Load script tool 加载脚本工具 Load Scripted AI 加载脚本化 AI Load settings from 应用设置于 loaded 承载 loaded passenger/freight 排列乘客/货运,按 Loading (%i->%i%%)! 载入 (%i->%i%%) 中! Loading (%i%%) departure %s! 载入 (%i%%) 中,出发时间 %s! Loading (%i%%)! 载入 (%i%%) 中! Loading addon paks ... 正在载入附加的游戏资料档... Loading map ... 载入地图中... Loading paks ... 正在载入游戏资料档... Loading skins ... 正在载入样式... Loading time: 完全载入于: Lock Control key 锁定控制键 Lock game 禁止切换玩家 (需确认) Lokomotive_tab 机车 m3 立方米 Maglev 磁浮铁路 maglev vehicle 磁浮铁路车辆 maglev_track 磁浮路轨 Maglevdepot 磁浮列车厂 Mail Demand %d\n 最大邮件量 %d\n Mail level 邮件级别 Mailbox 消息中心 Mailbox Options 消息中心选项 Maintenance 维修 make stop public (or join with public stop next) costs %i per tile and level 公有化车站(或与邻接的公共车站合并)每格每等级的成本为$%i Make way or stop public (will join with neighbours), %i times maintainance 让道或改为公共站点(与邻近站点连接),维护成本为 %i 倍。 Manual (Human) 人类玩家 Manufactured: 制造商 Map roughness 地图粗糙度 map view 地图 map zoom 缩放 Mar. 3月 March 三月 Margin (%%) 利润 Marker 设定标记 max 最高 Max Boost (%%) 最大增加 (%%) Max income: 最高收入 Max. speed 最高速度 Max. speed: 最高速度 Max. waiting time 最大等待时间 (dd:hh:mm) Maximum 254 stops\nin a schedule!\n 一个行程表最多包含254个途径点 maximum length of rivers 河流最长长度 Maximum tile height difference reached. 两个地形之间的高度差已达到最大 Maxspeed 最高速度 May 五月 Median Citizen per town 城市平均人口 Meldung 消息 Menge 总额 merge stop 站点融合 MessageOptionsText \n新年度\n\n玩家消息\n\n城市消息\n\n沒有路线\n\n新目标\n\n聊天\n\n新载具\n\n车站爆满\n\n问题\n\n交通拥挤 min 最低 minimum length of rivers 河流最短长度 Minimum load 最低载入 (%%) Missing pakfiles 对象丢失! Modify the selected line 修改所选路线 Monate alt 个月 Monorail 单轨铁路 monorail vehicle 单轨列车 monorail_track 单轨路轨 Monorailboden 高架道路 Monoraildepot 单轨列车车厂 Month 月 month wait time 最大等候时间(月) Monthly departures 固定出发时间 Months 月份 Monument 纪念碑 Monuments 纪念碑 Mountain height 山高: Move the map 移动地图 Movingobj 移动物体 Music playing disabled/not available 背景音乐不可用。 Music volume: 背景音乐音量: mute sound 静音声音 Name 名称 Narrowgauge 窄轨 Narrowgauge are not available yet! 窄轨铁路还不可用 narrowgauge vehicle 窄轨铁路车辆 narrowgauge_track 窄轨铁路 Narrowgaugedepot 窄轨铁路车库 Net ID: %p 电网编号: %p\n Net Wealth 净资产 Net wealth near zero 公司资产几乎耗尽! Neue Karte 新地图 Neue Welt 新建游戏 new convoi 新车队 New Line 新路线 New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. 已开设新路线!\n请在路线列表中选择该路线,\n并修改其资料 New Vehicles 新交通工具 Nickname: 聊天名: no buildings hidden 无隐藏建筑物 no convois 无运输工具 No goods are loaded onto this convoi. 此编队不会装载任何对象。 no goods waiting 无积压货物 no load 取消装载 No player 无运输公司 No Route 无路线 No stop here! 该工具需要使用在一个停靠点上。 No suitable ground! 地形不合适! No suitable townhall available for this climate! 没有适合这种气候的市政厅! No terminal station here! 不能在此建设总站!请建车建\n设地点是否在对应路线的末端。 no timeline 不使用时间轴 no tree 没有树林 No vehicles are available for purchase. 没有载具可供购买。 No. of Factories 工厂和站点: Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n "我该去哪里?"\n在派出载具前,必须先给其制定行程表或者安排到已有的行程表 none 无 nord 北 nordost 东北 nordwest 西北 Not allowed to make publicly owned ways! 不允许修建公共道路。 Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! 路线寻找期间,不能修改行程表 Not enough fields would remain. 此处场地预留空间不足。 Not online 不在线 November 十一月 now 现在 Now active as %s.\n 当前玩家是 %s\n nowhere 无位置 Number of rivers 河流数量 Object 物体 Oct. 10月 Odometer: %s km %s 千米 Ok 确认 Oktober 十月 On loan since %i month(s) 负债状况已经维持了%i个月 On mouseover 在鼠标指针位于下方时 on the (dd-hh-mm) 首次出发/月 On this map, you are not\nallowed to change player!\n 游戏已锁定, 不能切换玩家\n Only city chains 只显示城市的工业链 Only first %d differing paks reported. There are probably more. 只显示首%d个不相同的游戏资料档。可能会有更多。 Only full Unicode fonts 仅限完整的 Unicode 字符集 Only land chains 只显示郊外的工业链 Only one transformer per factory! 一间工厂只能设置一个变压器 Only show goods which are currently handled by factories 仅显示工厂现在加工的商品。 open 通行 Operation 运营费用 Ops Profit 运营利润 Optionen 选项 Or enter a server manually: 或手工输入服务器地址: ORDINAL_DAY_SYMBOL 日 Origin 出发地 ost 东 Output 输出 Ownership 所有权 Pak which may cause severe errors: 以下对象(pak)缺失,可能会导致运输链故障: Pak which may cause visual errors: 以下对象(pak)已被类似对象替换: Pak(s) different: 不相同的游戏资料档: Pak(s) missing on client: 用户端所欠缺的游戏资料档: Pak(s) not on server: 伺服器上所没有的游戏资料档: Pakset differences 游戏资料集之间的差别 paletten 装货箱 Pas_tab 客运火车 Passagiere 乘客 Passagierrate 乘客量 Passagierziele 乘客/邮件目的地 Passenger AI 电脑玩家-客运 Passenger Demand %d\n 最大乘客容量 %d\n Passengers %d %c, %d %c, %d no route 乘客 %d %c, %d %c, %d 无路线 Passengers %d %s, %d %s, %d no route 乘客 %d %s, %d %s, %d 无路线 Password 密码 Pause 暂停 Pax <%i> Mail <%i> 乘客 <%i> 邮件 <%i> Pax level 乘客数量级别 PaxDest 目的地 Percent Electricity 供电率(%%需求) personal_chat 私聊 Pipette 重建所选对象 Planes are not available yet! 飞机还不可用 Plant tree 种植树木 player 玩家 player -1 人 player 0 公共服务 player 1 Napik 128 AS player 10 玩家10 player 11 玩家11 player 12 玩家12 player 13 玩家13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 货运代理人 VM player 5 H-Trans公司 player 6 PSK & Co KG player 7 玩家7 player 8 玩家8 player 9 玩家9 Player ranking 玩家评级 Please choose vehicles first\n 请先选择载具 Post 邮件 Postrate 邮件量 Power 电力 Power (MW) 功率 (MW) Power: 功率 Power: %4d kW\n 功率: %4d kW\n Powerlines 电缆 Price 售价 Problems_msg 问题 Produced 已生产 Produces: %.1f units/minute 生产: %.1f 单位/分 Production of %s has been stopped:\n%s\n %s 已停产\n%s Production/Boost 生产数据 Produktion 生产 Profit 利润 promote to line 改进路线 public_chat 公屏聊天 q1 春季 q2 夏季 q3 秋季 q4 冬季 Query server 查询服务器 rail car 火车 random 随机 Random age 随机树龄 Random map 随机地图 Rathaus 市政府 Rating 评价 ratio_pax 乘客运载率(%%) Relevant 相关 Reliefkarte 地图 Remove 移除标签 remove airstrips 拆除飞机跑道和滑行道 remove channels 移除运河 remove interm. signals 拆除中间的信号灯 remove maglev tracks 拆除磁浮路轨 remove monorails 拆除单轨铁路 remove narrowgauge tracks 拆除窄轨铁路 remove powerlines 拆除电缆 remove roads 拆除道路 remove signal 移除信号灯 remove tracks 拆除路轨 Remove wayobj %s 拆除道路 %s replace other signals 置换其他标志 replace stop 更换车站 request closing 请求拒绝 residential house 居民房 Restore natural slope 还原原来的斜坡 Restwert: 转售价格 Retire date 退役日期 Retire. date: 退役日期 return ticket 复制此回程 Revenue 载具收入 Revenue/unit/100 tiles 每 100 图块的利润 Revert schedule 撤销更改 Revision: 修正 road 道路 road vehicle 道路车辆 Roadsign 道路标记 Rotate Building 旋转建筑 Rotate map 旋转地图 Rotation 旋转 Routing 寻找路线中 Running cost 运行成本 sack 袋 sail 风 Save 保存游戏 Save generated script 保存生成的脚本 Saving map ... 地图存储中... Scenario complete: %i%% 场景已完成: %i%% Scenario Debug 调试 Scenario Error Log 脚本错误! Scenario Goal 场景目标 Scenario Info 简要信息 Scenario information 当前场景详情 Scenario Result 进度 Scenario Rules 规则 Scenario_ 场景 Schedule changing! 正在更新行程表! Schienentunnel 建造铁路隧道 Schiff_tab 船 Schiffdepot 造船厂 Schleppkahn_tab 驳船 Screen scale: 屏幕比例: Screenshot 截图 Scroll threshold 滚动阈值 Search: 搜索: Seasons 季节 Sehenswuerdigkeit 旅游胜地 Select a server to join: 选择一个服务器以加入: Select a theme for display 选择一个显示主题 Select display font 选择显示字体 Select one or more graphics to install (Ctrl+click): 选择一个或多个要安装的图形包(Ctrl + 单击): Select soundfont 选择 MIDI 音色库 Sell the selected vehicle(s) 卖出所选载具 Selling of the program is forbidden. 禁止出售此程序。 sended 邮件已发送 SEP_FRACTION . SEP_THOUSAND , SEP_THOUSAND_EXPONENT 3 Sept. 9月 September 九月 Served by 服务提供方 Served by me 由我提供服务 Server did not respond! 服务器无回应 Serves Line: 服务路线 Service 服务 set signal spacing 设置信号灯间距 Setting 设置 Ship 船 shops and stores 商店和办公楼 Show all 全部显示 show all building 显示所有建筑 Show also vehicles no longer in production. 同时显示不再生产的载具 Show also vehicles that do not match for current action. 同时显示不符合所选指令的载具 Show always 始终显示 Show climates 显示气候带 Show contour 显示高度 Show even servers with wrong version or pakset 同时显示使用错误游戏版本或资料集的服务器 Show finances for transport type 显示运输类型的财务状况: Show future 显示未来的项目 show grid 显示网格 Show industry 显示工业 Show legend 显示图例 Show map scale 显示地图比例 Show mismatched 显示不兼容 Show networks 显示网格 Show obsolete 显示已淘汰的 Show offline 显示离线 Show only used 隐藏未使用的 Show outline 仅基础设施 Show schedules 显示路线表 Show servers that are offline 同时显示当前离线的服务器。 Show servers where game version or pakset does not match your client 同时显示程序或图形包版本与你的客户端不匹配的服务器。 show station coverage 显示车站服务范围 show station names 显示车站名称 show waiting bars 显示等候图表 show/hide block reservations 显示/隐藏预留块 Show/hide estimated arrival times 显示/隐藏预计到达和出发时间 show/hide object owner 显示/隐藏对象的所有方 Show/hide statistics 显示/隐藏数据统计 Shows buttons about networks overlay. 显示网格叠加的按钮。 Shows buttons on network overlay. 显示叠加在连接网络上的选项。 Shows buttons on special topics. 显示选择字段,例如用途或产业。 Shows consumer/suppliers for factories 显示工厂的客户和供应商 Shows the color code for several selections. 标示所用的颜色代码,例如,用于路径用途。 Shows the currently selected schedule 显示当前所选的路线表 Shrink city 缩小城市 shuffle midis 随机音乐 Signal 信号灯 signal spacing 信号灯间距 Sim: 模拟周期: Similar view as the main window 将视角倾斜至与大视角类似的角度。 Single toolbar only 仅显示一个工具栏 Size (%d MB): 大小 (%d MB): Size (area) 区域面积 sliced underground mode 切面地图模式 slot empty 空槽 Smart hide objects 智能隐藏对象 Sort by 排序 Sort by: 排序: Sort waiting list by 排列等候列表,按 Sound 音效 Sound settings 音效设置 Sound volume: 音效音量 Soundfonts are located in the music directory. 音乐文件位于音乐目录。 special freight 特殊货物 Speed Bonus 奖励速度 Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. 速度加成\n道路 %i km/h 铁路 %i km/h\n船运 %i km/h 空运 %i km/h Speedlimit 速度限制 Speichern 保存 Spieler 玩家 Spieler(mz) 玩家 Spielerliste 玩家列表 Spielstand wurde\ngeladen!\n 游戏载入成功 Spielstand wurde\ngespeichert!\n 游戏存储成功 Sprache 语言 Sprachen 语言 Stadtinformation 城市统计数据 Start 开始 Start the selected vehicle(s) 派遣所选载具 Starte Spiel 开始游戏 station labels 站 Station tiles: 站台长度 Station_msg 车站 Status 车站状态 steam 蒸汽 Step timeline one year 跳至下一年 Stops 车站 Storage 保管库 Storage capacity 容量 Strassendepot 车库 Strassentunnel 建造道路隧道 street car 電車 sued 南 suedost 东南 suedwest 西南 Summer snowline 夏季雪线 Supplied: %.0f %% 已供应:%.0f %% Suppliers 供应商 Tage alt 天 The following graphics are unmaintained: 以下图形包已停止维护: The generated script was saved!\n 宏已成功保存。\n可用脚本工具重放。 The gradient does not fit a tunnel 此坡度不适合修建隧道。 The script cannot be saved!\n 宏保存失败。 Theme selector 主题选择器 There are still vehicles\nstored in this depot!\n 仓库内还有载具 This Month 本月 This Year 本年度 Tile not empty. 在使用斜坡工具前, 请先清除地面物件 timeline 遵从时代 tl_title 城市列表 To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. %s 为了吸引更多的旅客\n兴建了 %s\n(在 %i 纳税人的支持下) To heavy traffic\nresults in traffic jam.\n 交通过于繁忙导致交通阻塞 To: %s 至: %s Toggle day/night view 切换日/夜视图 Toggle vehicle tooltips 切换车辆工具提示 tonnen 吨 Toolbar position: 工具栏位置: Total inhabitants: 居民总数 Tourist attractions 旅游景点 Tourists 景点 Town_msg 新目的地 Town: %s\n %s 市 Towns 城市 track 路轨 Tracks 路轨 Traffic 交通 traffic settings 交通 Train 火车 Trains are not available yet! 火车还不可用! Tram 电车 tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. 电车 %i km/h, 单轨列车 %i km/h\n磁悬浮列车 %i km/h, 窄軌列车 %i km/h tram_track 电车轨 Tramdepot 电车车库 Trams are not available yet! 电车还不可用! Transferring game ... 正在传送游戏... Transformer only next to factory! 变压站必须建造在与工厂毗连的平坦空地上 Translation 翻译 transparencies 透明 transparent background 透明背景 transparent station coverage 透明显示车站覆盖范围 Transported 路程 Trees disabled! 不显示树木! TrolleyBus_tab 无轨电车 Truck 货车 tt_Other 其他 Tunnel muss an\neinfachem\nHang beginnen!\n 隧道入口必须建造在斜坡上 Tunnel must start on single way! 隧道入口必须连接道路或铁路 Tunnelboden 隧道 under the Artistic License 已获艺术许可 underground mode 地下视图 UNDO failed! 不能撤销上次操作\n仅在某路段上没有信号标志、站点或其他建筑物时, 才能使用撤销道路建设操作 Undo last ways construction 取消上次道路建设 Unemployed 失业 Unhappy 不悦 units/day 单位/月 Unloading (%i%%)! 卸下 (%i%%) 中! Update Line 更新路线 upgrade HQ 升级总部 Usage: %.0f %% 使用率: %.0f %% Usage/Output 用途/输出 Use beginner mode 启用新手模式 Use timeline start year 使用时间轴: 从 Vehicle %s can't find a route! 载具 %s 不能找到路綫 Vehicle %s is stucked! 载具 %s 被卡住了 Vehicle count: 载具计数: Vehicle details 载具详情 Vehicle Name 名称 Vehicle Power 功率 vehicles stored 仓库内的载具 Verbrauch 消耗量 Vergroessere die Karte\n 扩大地图 Verkauf 立即移除! verkaufen 贩售模式 Verkehrsteilnehmer 城市车辆 Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n 仅余 %d 个月\n还清债务。\n vh_title 列出全部载具 via 通过(细节) via %s\n 通过 %s via Menge 通过(数量) via owner 通过(所有者) voranstellen 前置 Waggon_tab 车辆 waiting 等待中 Waiting for clearance! 等待清理中 Walked 徒步 walking 步行 Warnings_msg 交通状况 Wasser 水 Water 运河 Water level 水平面 water vehicle 船只 way %s cannot longer used:\n 不能再建造 %s 路。\n way %s cannot longer used:\n%s\n 不能再建造 %s 路。\n way %s now available:\n %s 路现在可用。\n Way toll 通行费 Ways not connected 道路未连接 waytype 道路类型 Wegpunkt 途径点 Weight 重量 Weight: 重量: Weight/unit 重量/单位 Welcome to Simutrans 欢迎来到模拟交通 Welcome, %s! 欢迎, %s! Wert 价格 west 西 Wind direction 风向 Winter snowline 冬季雪线 withdraw 撤销 Withdraw All 全部撤销 WRONGSAVE 档案可能已损坏, 不能载入游戏 Year 年 Year %i has started. %i 年已开始 YEAR_SYMBOL 年 Years 年 Your primary color: 识别颜色 1 Your secondary color: 识别颜色 2 Zielort 目的地 zooming in 放大 zooming out 缩小 Zu nah am Kartenrand 过于靠近地图边缘, 不能建造 #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. %2$s 创造了新磁悬浮列车世界纪录: %1$.1f km/h New world record for monorails: %.1f km/h by %s. %2$s 创造了新单轨世界纪录: %1$.1f km/h New world record for motorcars: %.1f km/h by %s. %2$s 创造了新汽车世界纪录: %1$.1f km/h New world record for narrowgauges: %.1f km/h by %s. %2$s 创造了新窄轨世界纪录: %1$.1f km/h New world record for planes: %.1f km/h by %s. %2$s 创造了新航空世界纪录: %1$.1f km/h New world record for railways: %.1f km/h by %s. %2$s 创造了新铁路世界纪录: %1$.1f km/h New world record for ship: %.1f km/h by %s. %2$s 创造了船运新世界纪录:\n%1$.1f km/h #________________________________script_ai_text_________________________________ #________________________________script_ai_text_________________________________ %s build additional convoy to line: %s %s 为线路增派编队: %s %s build rail line from %s (%s) to %s (%s) %s 开通了 %s (%s) 到 %s (%s) 的铁路运输线路 %s build road line from %s (%s) to %s (%s) %s 开通了 %s (%s) 到 %s (%s) 的公路运输线路 %s build ship line from %s (%s) to %s (%s) %s 开通了 %s (%s) 到 %s (%s) 的船运线路 %s extends the route from %s (%s) to %s (%s) %s 将路线从 %s (%s) 扩展到 %s (%s) %s optimize way line from %s (%s) to %s (%s) %s 优化从 %s (%s) 到 %s (%s) 的路线 %s removes convoys from line: %s %s 从线路 %s 移除了编队 vehicles of the line %s were retired 线路 %s 上的载具退役了 #_________________________________tooltip_text__________________________________ #_________________________________tooltip_text__________________________________ Add convois with similar schedule to this line. 查找并添加目的地相同的载具。 Attach current coordinate to the comment 将地图上当前位置的坐标附加到消息中。 Enter intervall in days, hours, minutes 以天、小时、分钟为单位输入时间段。 Mirrors order of stops 反转站点顺序 Revert to original schedule 反转所有行程表更改。 Tutorial not available for this pakset. Please try loading Pak128. 这个 Pak 图形包套件没有教程。Pak、Pak128 和 Pak192,以及其他一些套件,都有教程。 #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL 油绳 &1_CITY_SYLL 切斯特 &2_CITY_SYLL 吨 &3_CITY_SYLL 市 &4_CITY_SYLL 集聚地 &5_CITY_SYLL 行政区 &6_CITY_SYLL 火腿 &7_CITY_SYLL 域 &8_CITY_SYLL 口 &9_CITY_SYLL 锁 &A_CITY_SYLL 穿过 &B_CITY_SYLL 由 &C_CITY_SYLL 埋藏 &D_CITY_SYLL 用处 &E_CITY_SYLL 井 &F_CITY_SYLL 家 &G_CITY_SYLL 希望 &H_CITY_SYLL 木头 &I_CITY_SYLL 喇叭 &J_CITY_SYLL 教堂 &K_CITY_SYLL 修道院 &L_CITY_SYLL 生活 &M_CITY_SYLL 林根 &N_CITY_SYLL 错过 &O_CITY_SYLL 泊 &P_CITY_SYLL 磨 &Q_CITY_SYLL 鲁斯 &R_CITY_SYLL 骑 &S_CITY_SYLL 安静的 &T_CITY_SYLL 城市 &U_CITY_SYLL 礼物 &V_CITY_SYLL 石头 &W_CITY_SYLL 施泰滕 &X_CITY_SYLL 谷 &Y_CITY_SYLL 部队 &Z_CITY_SYLL 村庄 %0_CITY_SYLL 松树 %1_CITY_SYLL 艾坪 %2_CITY_SYLL 马利 %3_CITY_SYLL 灰 %4_CITY_SYLL 泉 %5_CITY_SYLL 大选 %6_CITY_SYLL 巴灵 %7_CITY_SYLL 赫普 %8_CITY_SYLL 布伦特 %9_CITY_SYLL 仁恩 %A_CITY_SYLL 旧 %B_CITY_SYLL 山丘 %C_CITY_SYLL 新 %D_CITY_SYLL 岩山 %E_CITY_SYLL 沃尔 %F_CITY_SYLL 橡树 %G_CITY_SYLL 诺德 %H_CITY_SYLL 上 %I_CITY_SYLL 奥尔登 %J_CITY_SYLL 金枪鱼 %K_CITY_SYLL 普富伦 %L_CITY_SYLL 休息 %M_CITY_SYLL 玫瑰 %N_CITY_SYLL 西洪 %O_CITY_SYLL 施拉姆 %P_CITY_SYLL 夏天 %Q_CITY_SYLL 石头 %R_CITY_SYLL 索德 %S_CITY_SYLL 昂特 %T_CITY_SYLL 森林 %U_CITY_SYLL 水 %V_CITY_SYLL 韦塞尔 %W_CITY_SYLL 博 %X_CITY_SYLL 维斯 %Y_CITY_SYLL 威廉 %Z_CITY_SYLL 冬天 0center %s 轴 %s 0extern %s 旁道 %s 0suburb 日光浴场村 1center %s %s 1extern %s 分支 %s 1suburb %s %s %s 2center %s 中心 %s 2extern %s 外 %s 2suburb %s 草地 %s %s 3center %s 主 %s 3extern %s 土地 %s 3suburb %s 村庄 %s %s 4center %s 里 %s 4extern %s 外围 %s 4suburb %s 农村 %s 5center %s 市中心 %s 5extern %s 转换 %s 5suburb %s 镇 %s 6center %s 中心 %s 6extern %s 交换 %s 6suburb %s 郊外 %s %s 7center %s 城市 %s 7extern %s 平原 %s 7suburb %s 公园 %s 8center %s 商业 %s 8extern %s %s 边 %s 8suburb %s 卫星 %s 9center 9extern 9extern %s 旁道 %s 9suburb %s 中部 %s simutrans-124.5/simutrans/text/cn/000077500000000000000000000000001520675460500171755ustar00rootroot00000000000000simutrans-124.5/simutrans/text/cn/airtools.txt000066400000000000000000000105661520675460500216020ustar00rootroot00000000000000机场工具

机场工具

机场工具包含用以建造空中交通网络的工具。工具可以建造或拆除:飞机滑行道和跑道、空管站(用于接送货物或乘客)、飞机库(用于购买和管理航空运输载具)以及各种机场建筑物。如果游玩时伴随了时间线设定,则随着模拟交通中时间的推移,游戏中会出现更多工具选项。

点击游戏画面顶部的飞机图标打开工具栏。
将鼠标光标悬停在工具选项上(打开或单击工具栏后),即可查看名称以及在适当情况下显示的内容:建设成本、括号中的维护成本和最高速度限制。

{提示: 建造一个简单机场的一种方式:
一) 建造并连接滑行道和跑道;
二) 在滑行道末端放置停靠站;
三) 按需添加机场建筑。}

工具包括(从左到右):

滑行道: 该工具用于建造飞机从空降站移动到跑道的地面区域。在游戏视图中,滑行道建造在平坦的地面上。
重要: 如果滑行道与跑道末端相连,飞机不能起飞和降落。
建造滑行道: 点击工具进行选择(光标变为滑行道),然后点击地形作为起点(游戏视图中显示推土机,并在显示屏右下角显示地图坐标),最后点击地形或跑道作为终点。
{提示: 使用销毁/移除功能移除滑行道的各个部分。 撤销 [z] 操作不能退还施工费用。}

跑道: 工具用于建造简易机场跑道,供飞机起降。在游戏画面中,跑道建在平坦的地面上,并且可以相互交叉。
建造跑道: 点击工具进行选择(光标变为方向),然后点击地形作为起点(游戏视图中显示推土机),最后点击地形作为终点。
{提示: 跑道两端请勿连接,否则飞机不能起飞或降落。使用摧毁/移除功能可移除跑道的各个部分。撤销 [z] 操作不会退还建造费用。}

拆除简易跑道: 在游戏视图中,两点之间没有飞机时,该工具可用于移除滑行道或跑道的一段。使用该工具会产生建筑费用
要移除一条跑道: 点击工具(光标变为红色十字); 然后点击要删除的跑道(在游戏视图中选中红色十字显示的删除点); 最后点击跑道上的另一个点,移除到第一个删除点为止的部分。 {提示: 跑道末端不要连接,否则飞机不能起飞或降落。使用摧毁/移除功能移除跑道的各个部分。}

飞机: 这些工具用于建造飞机库,以便购买和管理飞机。机库需要维护,并且通常建在一段滑行道的末端。
建造一个机库: 点击工具(将光标变为仓库),然后点击滑行道的末端。
{提示: 使用摧毁/移除命令移除仓库。}

空管站登机桥: 使用该工具可在游戏视图中创建停靠点,供飞机接送乘客或货物。
如果一个空管站没有建在现有的站点旁边,则会形成一个新的空管站。
停机坪建在一段滑行道的末端,需要一定的维护成本,并覆盖货物、旅客和邮件的存放区域。只有在有空闲的停机坪时,飞机才会降落在机场。
建造一个空管站: 点击工具进行选择,然后点击滑行道的末端。
{提示: 使用摧毁/移除功能移除空管站。按 [v] 键可在游戏视图中显示/隐藏货物及乘客的集散区域。}

机场建筑: 该工具可为站点建造扩展功能,这会增加维护成本,并扩大货物及旅客的容量及服务范围。在某些工具选项的角落,会显示一个图标(用于站点列表和站点信息),该图标指示扩展功能允许站点处理哪些项目。
建造一个扩展建筑: 点击工具选择扩展(光标会变为扩展),然后在游戏视图中点击现有站点旁边所需位置。新创建的扩展现在会成为站点的一部分。
{提示: 使用摧毁/移除功能移除扩展区域。按 [v] 键可在游戏视图中显示/隐藏货物及乘客的集散区域。}

simutrans-124.5/simutrans/text/cn/baum_build.txt000066400000000000000000000045171520675460500220500ustar00rootroot00000000000000植树

植树

植树窗口下可种植单棵树木。可通过地图编辑器打开。
提示:此工具栏上还有一个添加森林按钮,用于一次性添加森林。

顶部是具体气候带选项
- 全部表示列出所有可用的树木,无论其气候带如何,但只有与土壤气候带相匹配的树木才能种植。如果你想忽略气候带检查,可启用旁边的忽略气候带选项。启用后,种植树木时将不再检查气候带。
你若根据目标土壤选择气候带,则系统只会显示与该气候带相符的树木。
你可在下方更改名称的显示方式和列表的排序方式。
- 翻译: 按游戏中使用的名称显示并排序。这取决于所选语言。若所选语言无可用的译文,则系统显示对象名称。
- 对象: 按程序内部对象名称显示并排序。
旁边的按钮可将排序方式反转为降序。

选单

- 左侧是树木选择列表。点击其中一棵树即可查看更多信息。
- 右侧显示所选树木的信息。
- 名称: 树名。
- 任意解释性文本
- 适宜气候: 所有适宜的气候带。
- 季节: 含有支持图像的季节数。
-- 2: 意味着只有有雪和无雪的两种图像。适用于针叶树。
-- 4: 表示春季、夏季、秋季,冬季只有一种图像。
-- 5: 指有雪和无雪的春季、夏季、秋季和冬季。
- 绘制: 图像制作者的名字。
- 树的图像。
要种植树木,在游戏世界窗口下从列表中选择所需位置并单击。

放置选项

- 忽略气候: 此选项可取消气候限制,允许你在不适合的气候条件下种植树木。
- 随机树龄: 若激活,所种树木的年龄将随机化。通常情况下,树木在最幼小的树龄开始成长,它们会生长成熟,然后在多年后枯萎死亡。不久之后,同一地点又会长出另一棵小树。
注意: 许多在同一树龄种植的树木会同时出现秃顶并死亡的情况。这类似于森林枯萎病。

simutrans-124.5/simutrans/text/cn/chat.txt000066400000000000000000000052501520675460500206570ustar00rootroot00000000000000聊天窗口

聊天窗口

多人游戏过程中,可通过主工具栏访问聊天窗口。可与所有在线玩家发送消息。

顶部会显示“透明背景”复选框,你可以通过它来开启或关闭透明功能。这在小屏幕设备上尤其有用。
接下来列出了三个标签页,分别包含三种不同的聊天类型:
- 公共聊天
- 公司聊天
- 私人聊天
之后会显示消息日志,所有消息都会以气泡的形式显示。如果此区域没有任何显示,则表示尚未发送任何消息。
底部是输入框,旁边有一个箭头符号,点击它可以将当前视图的坐标添加到消息中。添加的坐标会以箭头符号的形式显示在聊天记录中,位于你的对话气泡旁边。

发送消息,请点击输入框,输入文本,然后按回车键发送。
你的消息将立即显示在日志中。
要使光标离开输入行,只需再次按回车键或单击任意位置即可。
如果聊天窗口仍然处于活动状态,按下 esc 键将关闭聊天窗口。

除了在日志中显示外,收到的消息还会显示在屏幕底部的新闻栏中,并在工具栏的聊天窗口按钮内以红点上白色数字的形式提示。

公共聊天室中的消息是公开的,因此游戏中的任何成员都可以看到它们。
此外,公共聊天记录将在当前多人游戏地图的整个持续时间内保留。

公司聊天信息对同一公司内的活跃玩家可见。不过,任何人都可以选择任何公司并查看其聊天记录,即使该公司设置了密码保护。
聊天记录也会在地图活动期间一直保存。

私聊功能允许两名玩家互发消息,而其他人看不到。
在此标签页中,可以选择任何当前在线的合作玩家并向其发送信息。
或者,点击任何日志中玩家姓名旁边的箭头符号,即可直接进入私聊。
退出多人游戏会话时,私人聊天记录将被删除。

请记住:
- 按下制表键 [tab] 会将输入行中已有的文本也发送出去。此外,在新玩家同时连接到当前游戏时,已输入的文本也可能会意外地发送出去。 >
- 消息长度限制为226个字符。不能插入自动换行(段落)符或显示表情符号。
- 双击任意气泡即可将其包含的文本复制到剪贴板,并显示“已复制”的提示。例如,在论坛中交换文章链接时,此功能尤其有用。

simutrans-124.5/simutrans/text/cn/citybuilding_build.txt000066400000000000000000000067551520675460500236200ustar00rootroot00000000000000建造城市建筑

建造城市建筑

建造城市建筑窗口允许放置单个建筑物。这是一个地图自定义选项,如果你切换到公共服务玩家,然后打开地图编辑工具,即可使用此选项。

此对话框分为四个部分:
1) 左上角:筛选器和排序选项。
2) 左下角:可用建筑选单。
3) 右上角:放置选项。
4) 右下角:选中建筑的相关信息和图片。

1 - 筛选器和排序选项

*服从时间: 选单仅包含在当前模拟交通年份中可用的建筑物。这与“指定日期下可用”选项互斥。
*指定日期下可用: 选单仅包含在指定日期可用的建筑物。这与“服从时间”选项互斥。
*显示过时信息: 扩大选单,使其包含当前模拟交通年份中已过时的建筑物。
*气候: 按适用的气候条件筛选。
*排序条件: 按如下任意参数为条件排序选单:
**翻译: 显示建筑物在当前语言设定下的名称。若当前语言没有对应的翻译,系统则使用对象名称。
**对象: 显示并使用模拟交通内部对象的名字排序。
**乘客级别: 按乘客产生量或需求量排序。
**邮件级别: 按邮件产生量或需求量排序。
**引入日期: 按引入日期(建筑物开始出现的日期)排序。
**退役日期: 按退役日期(建筑物不再显示的时间)排序。
**面积: 按建筑物的图块大小排序(注意:某些图形包可能只有一块图块大小的建筑物)。
*住宅: 显示/隐藏住宅建筑。
*商铺和办公室: 显示/隐藏商业建筑。
*工业建筑: 显示/隐藏工业建筑。

2 - 选单

选单中显示了所有符合指定选项的城市建筑。点击列表中的建筑即可查看该建筑的更多信息。要放置建筑,请在从列表中选择建筑后,在游戏窗口中点击所需放置的位置。

文字的颜色有如下含义:
*蓝色文字表示这是一栋住宅建筑。
*绿色文字表示这是一个商铺或一栋办公楼。
*黑色文字表示这是一座工业建筑。

3 - 放置选项

*忽略气候: 此选项可取消气候限制,允许你在不适合的气候条件下放置建筑。
*旋转: 如所选建筑物可从不同角度显示,则可在此处选择特定视图。图像将随之改变。如果设置此选项为随机,系统则会使用建筑物的随机视图。

4 - 信息

有关建筑物的信息包含:
*类型: 建筑类型(住宅、工业或商铺和办公楼)。
*名称和描述: 如果对象没有名称,则游戏中仅显示描述。如果对象既没有名称也没有描述,则游戏中仅显示对象名称。
*适用气候: 允许放置建筑的气候条件(如未设置“忽略气候”选项)。
*乘客级别: 衡量乘客产生量或需求量的指标。
*邮件级别: 衡量邮件产生量或需求量的指标。
*引入日期: 该建筑物出现的日期。
*作者: 绘制该对象的作者姓名(如有)。
*图像: 建筑物图像。

simutrans-124.5/simutrans/text/cn/citylist_filter.txt000066400000000000000000000040711520675460500231510ustar00rootroot00000000000000城市列表

城市列表

城市列表提供有关城市的信息。此窗口包含两个独立的标签页:一个用于显示城市列表,另一个用于显示图表。
列表菜单中点击城市列表,或按[T]键,即可打开列表。

- 总居民: 地图上显示的人口总数(所有城市的总人口)。括号内显示的是近期人口增长情况。

城市列表选项卡列出了地图上的每个城市,及其名称、人口和增长

排序: 提供列表排序方式的选项。

- 名称:按城市名称的字母顺序对列表排序。
- 市民:按人口数量对列表排序。
- 城市增长率:按增长率对列表排序。

- 升序 / 降序:反转列表顺序。

点击列表中的项目以查看更多关于该城市的信息

图表选项卡提供有关这些城市的总体信息。

- 市民: 所有城市的总人口(与总居民相同)。
- 城市增长: 所有城市的总和城市增长
- 城市: 地图上显示的城市总数。
- 工厂: 地图上工厂的总数。
- 编队: 运输载具总数。
- 城市汽车: 城市汽车总数。
- 乘客比率: 占总客运量的百分比。
- 乘客: 产生的乘客总数(包括未运送的乘客)。
- 投递邮件: 邮件投递总量的百分比。
- 邮件: 邮件总数(包括尚未寄出的邮件)。
- 供应: 对每家工厂的仓库进行检查,检查结果显示仓库不为空的次数。
- 存货: 每个工厂的仓库检查确认次数。

simutrans-124.5/simutrans/text/cn/citywindow.txt000066400000000000000000000103231520675460500221350ustar00rootroot00000000000000城市统计

城市统计

城市统计显示所选城市区域统计数据,并可于此处更改城市名称。城市区域是指城市、城镇或乡村;下文中的“城市”一词指代任何城市区域。

每个城市都有自己的市政厅,城市围绕市政厅发展壮大。客运和邮件运输促进了经济增长,这包括修建道路或由公共服务玩家接管道路,出现新的城市建筑和产业,以及城市边界的扩展。

在游戏视图中使用检查工具点击市政厅,或在城市列表中点击城市,即可打开城市统计信息。在这里,你可以查看和修改城市名称、一些统计数据、两张小地图,以及一个图表,该图表可以显示与城市相关的某些因素随时间推移的发展情况。

名称: 城市、城镇或乡村的指定名称显示在城市统计顶部的方框中。
这些城市区域名称用于为城市范围内的站点命名。 要更改名称,只需单击该框并输入新名称即可。
{提示: 按 [!] 键可在游戏视图中市政厅上方的城市名称间切换显示。有关城市名称生成方式的更多信息,请参阅 readme_citylist.txt(位于 ...simutrans/text/ 目录下)。}

名称框下方提供了一些基本的城市统计数据:
城市规模: 其当前人口数量。括号内为近期人口增长量。
建筑: 城市中的建筑数量(住宅/商业/工业)。
地图坐标: 表示城市边界(这些边界会随城市发展而变)。第一组数值表示左上角,第二组数值表示右下角。随着城市发展,城市边界可能会重叠。

目前该市的失业人口无家可归人口数量。随着城市扩张,这些数字将会增加;随着新建筑的兴建,这些数字将会减少。 这些计数之间的平衡决定了将建造哪种类型的城市建筑(住宅/商业/工业)。

允许城市增长按钮可开启和关闭城市发展功能。

城市统计中的游戏世界小地图以彩色圆点显示城市内乘客和产生的邮件的目的地。
右侧小地图显示当前的游戏日历月份的数值,左侧小地图显示上个游戏日历月份的数值。每个点的颜色表示目的地是否可以到达:
- 黄点: 由一个不拥挤的站点通过一段路线以开始行程(站点信息中显示笑脸)。
- 暗橙点: 没有到这个目的地的线路。
- 橙点: 行程起点站人满为患(站点信息显示不高兴的表情)。如果车站人满为患,系统不会检查路线。

迷你地图下方有一个图表,显示了该城市的统计数据。
y轴表示数量,x轴表示时间。 点击图表索引标签可更改x轴的刻度:
: 显示游戏中过去12个日历年的年度数值。
: 显示游戏中过去12个月的月度数值。

点击图表选项按钮,即可激活或停用每个按钮对应的曲线。选中按钮后,按钮会以缩进形式显示。
曲线与相应选项按钮的颜色相匹配:
市民: 居住在该城市的人口数量
城市增长: 人口变化(取决于城市规模和为乘客和邮件提供的运输服务)。
建筑数量: 这座城市拥有的市政建筑数。
城市汽车: 这座城市拥有的汽车数量。
旅客: 已进入交通网络的乘客数量(在站点信息中以笑脸显示)
乘客: 该市产生的乘客总数。
投递邮件: 已进入运输网络的邮件
邮件: 该市产生的邮件总数。
供应:城市工厂货物入库次数
存货:城市工厂仓库检查确认次数。

{提示: 与城市相关的设置可在 simuconf.tab 和 cityrules.tab 文件中修改。}

simutrans-124.5/simutrans/text/cn/climates.txt000066400000000000000000000060101520675460500215340ustar00rootroot00000000000000地形设定

地形设定

地形设置窗口用于设置游戏世界的地形和气象方面的选项。此窗口会与创建新游戏窗口一起打开。

要调整每个设置,请单击每个选项旁边的箭头按钮,或在框中输入数字。

地图选项

水位等级 - 设置地图上水位的高度(默认值为 -2)。数值越高,陆地面积越少,水体面积越大。

山峰高度 - 设置地形的最大高度。

地图粗糙度 - 设置地形粗糙度。数值越高,沿海地区的起伏越大(看起来更逼真)。过于粗糙的地图不能生成预期数量的城市、产业或道路。

警告: 应避免高于33的高度(玩家不能到达)。如有需要,可通过降低水位以生成更多地面。

雪线选项
雪线是指地图上积雪覆盖的高度。在模拟交通中,雪线会根据以下设置随时间变化。

夏季雪线 - 夏季雪线(终年积雪)。此值为可在气候选项中选择的最高地形值。北极气候由此形成。

冬季雪线 - 设置冬季雪线的最低高度。要使地图上更大范围地显示积雪,请将此值调低。

气候选项
为新地图设置不同的气候类型。

每种气候类型旁边的值定义了该气候出现在地形图中的高度。较高地形的气候带会被较低地形的气候带覆盖。要关闭某种气候,请将其值设为列表中更靠前气候的值(如果是沙漠气候,则设置为水位)。

注意: 建筑物、产业和植被都被它们的创造者赋予了特定的气候条件。

沙漠气候 - 降水量极少,气温极端。树木稀少。地势平坦。

热带气候 - 这是赤道附近非干旱地区气候特征的典型,海平面和低海拔地区常年保持高温。

地中海气候 - 中等程度多变的阴雨天气。

温带气候 - 分布于热带和极地之间的地区,气候多变。

苔原气候 - 低温,位于极地冰盖边缘。

高山(岩石)气候 - 气候干燥,冬季寒冷。

其他选项

无树 - 启用此选项可阻止模拟交通绘制树木。树木渲染会占用CPU资源。请在CPU速度较慢或内存较小的电脑上启用此选项。

湖泊 - 启用此选项,模拟交通可通过在地面上挖坑注水以创建地图上的湖泊。

河流数 - 设置地形图中可生成和利用的最大数量的河流。狭窄的河流不能通行任何类型的船只。

河流最短长度 - 设置地图上河流的最小长度(以图块为单位)。

河流最长长度 - 设置地图上河流的最大长度(以图块为单位)。

simutrans-124.5/simutrans/text/cn/color.txt000066400000000000000000000014031520675460500210520ustar00rootroot00000000000000玩家颜色

玩家颜色

玩家颜色选项可设置公司的颜色。
载具、建筑物和其他物品都采用公司颜色。

玩家颜色选项位于游戏选项中。

点击颜色条以修改公司颜色。
玩家颜色名称条的改变(新公司名称条的颜色不会改变)即表示选中了新颜色。

默认的人类玩家的颜色是淡蓝色。
部分对象没有颜色编码,不能显示所选颜色。

{提示:要更改AI玩家颜色,请使用玩家列表P+更改玩家,在修改前,首先选中AI玩家。}

simutrans-124.5/simutrans/text/cn/convoi.txt000066400000000000000000000054651520675460500212450ustar00rootroot00000000000000载具列表

载具列表

载具列表显示信息,并提供控件来筛选和列出不同的编队(编队由一个可运行的载具或载具单元的组合组成)。

打开方式:点击列表管理中的载具列表工具,或按 [V] 键。载具列表有四个选项按钮:两个用于对列表中的项目进行排序,两个用于对列表中显示的内容进行设置。
按钮下方列出了符合筛选条件的编队
{提示: 如果没有列出任何编队,请更改筛选选项。如果列表中的项目仅部分可见,请调整载具列表的大小或使用滑块滚动列表。}

点击选项按钮可在选项之间循环切换(选项按钮名称会更改),或打开筛选设置控件:

排序: 列表中显示的编队的顺序由两个选项按钮控制。

- 类型: 按运输方式排序(按公路车辆、铁路车辆、船舶和飞机升序排列)。
- 名称: 按指定名称,以 ASCII 码顺序字母数字排列:大写字母在前,小写字母在后(默认情况下,这是在仓库为编队购买或组装的第一个载具)。
- 收入: 按利润(收入减去运营成本)排序。
- 内部ID: 按编队的唯一ID号下单(编队首次购买或在仓库组装时默认分配,并显示在编队信息的标题栏中)。

- 升序 / 降序改变列表的顺序。

筛选器: 两个选项按钮,用于打开/关闭列表选择条件和访问控制,以更改条件。

- 启用 / 禁用: 点击以切换列表中编队的筛选条件。

- 设置选项打开后,即可更改筛选条件。

点击列出的项目以查看有关该编队的更多信息。

每个编队列出的项目包括:

内部ID: 编队的唯一ID号(在编队首次购买或在仓库中组装时默认分配,并显示在编队信息的标题栏中)。

名称: 已分配(默认情况下,这是购买或组装编队时的第一个载具)。

收入 显示利润(产生的收入减去发生的运营成本)。

线路: 显示已分配的线路,并指示编队是否在仓库内。

图表: 显示成分和当前负载水平。

simutrans-124.5/simutrans/text/cn/convoi_filter.txt000066400000000000000000000034351520675460500226050ustar00rootroot00000000000000载具列表筛选器

载具列表筛选器

载具列表筛选器里有可用于决定在载具列表中显示哪些编队的选项。

点击载具列表中的设置,打开载具列表筛选器

启用筛选选项后,可通过选项按钮选择要在载具列表中显示的编队。只有符合所有设定条件的编队才会列出。

点击方形按钮设置筛选条件(按钮选中后会缩进):

筛选器名称: 按名称选择编队。使用方式: 点击按钮进行选择,然后点击名称框并准确输入所需的名称(选项区分大小写)。

筛选器类型: 如果选中,将按运输类型列出编队。使用方法:点击按钮进行选择,然后点击运输类型: 公路、铁路、水路和航空。

特殊筛选器: 根据以下条件选择要列出的编队(点击按钮进行选择,然后点击):
- 无路径 - 找不到通往下一目的地的路径。
- 无日程安排 - 未分配行程
- 无收入 - 不产生利润。
- 在仓库里 - 在仓库中。
- 无线路 - 未分配线路

运载对象: 根据运输不同货物和乘客的能力选择编队。点击名称旁边的按钮选择项目。使用滑动条滚动列表。选项包括:
- 全部: 选择全部对象。
- : 未选择任何商品。
- 反选 反选当前选择的物品。

simutrans-124.5/simutrans/text/cn/convoidetail.txt000066400000000000000000000032651520675460500224240ustar00rootroot00000000000000编队详情

编队详情

编队详情: 提供有关编队(一个可运行的载具或载具单元的组合)的更多信息,此选项下可立即出售编队。

编队详情可从编队信息中的详细信息选项按钮打开。
若所有载具单元都不可见,请调整编队详情选项的尺寸或使用滑块滚动。

编队详情页面顶部,显示的信息包括:

功率: 编队功率(编队中所有载具单位的总功率)。

转售价值: 编队售出后会收到款项(编队的购买价格减去任意折旧价)。

警告: 点击出售模式选项按钮即可立即出售编队。无需进一步确认。编队将从游戏中移除,玩家账户余额将获得相应的出售金额。

编队详情: 列出编队中的所有载具单位。
每辆车的信息包括:
- 载具单元图像: 载具单元的图像(同样用于仓库
- 制造日期: 如果启用了时间线设置,游戏则会提供生产月份和年份,以及推出日期。
- 转售价: 转售价值。
- 功率: (机动载具单元)产生的功率
- 摩擦: 当前摩擦系数
- 最低收入: 该载具单位的最大可能收入取决于该载具单位所属编队运输的对象物类型。
- 运载物品: 该载具目前所载物品的数量和目的地。

simutrans-124.5/simutrans/text/cn/convoiinfo.txt000066400000000000000000000146321520675460500221150ustar00rootroot00000000000000编队信息

编队信息

编队信息提供有关用于运输货物和乘客的编队(作业载具或载具单元的组合)的信息。
它列出了编队携带的物品,并且还提供了访问更多详细信息和出售编队的控件;在游戏视图中跟踪编队;以及更改编队的名称和行程

编队由至少一个动力(机动)载具单元和任意附属拖挂/拖厢组成。
编队的例子包括: 一队马拉着马车;一辆火车头连着煤水车和车厢;一辆公共汽车;一辆货车;一辆卡车连着拖车;一辆连接的电车;一艘船;一艘拖着驳船的拖船;或者一架飞机。
根据运输类型,可从仓库组装和购买编队。
{提示: 车队的车辆单元数量有上限,具体取决于 pak 图形包。列车通常限制为24节车厢。}

在游戏视图中使用检查工具点击编队,或点击载具列表线路管理中列出的编队,即可打开编队信息
编队信息的标题栏显示编队的名称和唯一ID号(有关这些的更多信息,请参见下文)。
编队信息包含名称框;迷你视图;有关编队的信息;选项按钮;以及当前运载的所有对象的列表(如果不是所有物品都可见,请调整编队信息的尺寸或使用滑块滚动)。

编队信息的顶部显示的信息包括:

内部ID: 编队的唯一ID号(于仓库首次购买或组装编队时默认分配)。

- 名称: 显示编队的指定名称(默认情况下,这是在仓库为编队购买或组装的第一个载具单元名)。
点击名称框并输入新名称,即可更改编队的名称。

- 速度: 以km/h为单位显示当前速度。
编队的最大设计速度在括号内(由编队中最慢的载具单位决定)。
绿色进度条显示当前速度相对于最大速度的比值。

- 收入: 载具在本游戏日历年度内获得的利润(收入减去运营成本)。
括号内为护航队每地图格的运营成本(以哈约币计)。

- 总重量: 编队及其所载对象的当前总重量(吨)。
括号内为当前所载对象的重量。
绿色进度条显示可用于运载货物和乘客的运力百分比。
黄色条表示在时刻表控制中设置的等待级别(编队继续前进所需的最低货物和乘客数量)。
{提示: 要设置或更改线路上所有编队的等待级别,请使用线路管理仓库控件中的更新线路选项按钮。要设置或更改某个编队的等待级别(该编队不影响其所在线路上的其他编队),请使用编队信息或仓库控制中的行程表选项按钮。}

- 目的地: 编队行程表中的下一个停靠站或途经点。
绿色进度条显示编队距离下一目的地路程的进度。

- 服役线路: 已将此线路分配给此编队。通过三角形图标在线路管理窗口中打开该线路。(仅当已分配线路时才可见。)

- 迷你视图: 显示这个编队。 点击迷你视图,以将游戏视图中心对准该编队。

点击编队信息中的按钮,以打开控制按钮或选择选项:

行程表: 打开控制功能,可以更改编队的路线以及编队继续前进所需的最低货物和乘客数量。

前往仓库: 派编队前往最近的合适的仓库。所运载的任何对象均会丢失,但会收到运送到仓库的运费收入。
在仓库中,编队保留其行程表和分配的线路

跟随: 按钮缩进时,游戏视图将与编队一起移动。
要取消选择此选项,需再次单击该按钮或左键单击主地图

图表: 点击此选项按钮可在编队信息中切换图表(图表可见时按钮会缩进)。
当选择图表选项时,图表会显示过去12个月的统计数据(x轴)。
点击图表选项按钮,即可在图表上查看所选信息(选择选项后按钮会缩进)。
图表上的线条颜色与图表选项按钮的颜色相对应。:
- 可用容量: 显示编队中未使用的空间。
- 运载: 表示运输的货物、乘客和邮件的数量。
- 收益: 表示运输产生的收入。
- 运输费用: 表示编队在运输过程中产生的费用。
- 利润: 表示运输所得利润(收益减去运营成本)。

详情: 打开编队详情,其中包含更多信息以及立即出售编队的选项。

对乘客/货物进行排序: 列出编队目前携带的对象。
显示的信息包括运输数量、编队容量、对象类型、最终目的地和下一转运
该选项按钮(其名称会根据你的选择而改变)用于对所运载对象(乘客、邮件和货物)列表进行排序。:
- 目的地: 按最终目的地站点的指定名称对运载对象进行排序,按ASCII码顺序(大写字母在前,小写字母在后)进行字母数字排序。
- 数量: 按数量降序排列所运载物品。
- 通过(详情): 按分配的第一个转运名称对运载的对象进行排序,按ASCII码顺序(大写字母在前,小写字母在后)。
- 通过(数量): 按数量对运往第一个转运的对象进行排序。
- 通过(所有者): 按下一的所有者对所运载物品的数量进行排序。

simutrans-124.5/simutrans/text/cn/curiosity_build.txt000066400000000000000000000034161520675460500231530ustar00rootroot00000000000000建造旅游景点

建造旅游景点

对话框分为四个部分。
**左上角是可用的建筑选单
**左下角显示的是所选建筑物的图片
**右上角显示了一些可用选项
**右下角是关于所选城市建筑的信息

选单

选单中列出了所有可通过右上角指定选项(城市景点陆地景点纪念碑)访问的特殊建筑物和地标。 有你可以借助两个选项卡更改景点建筑物的标识方式。
**翻译: 显示建筑物在当前语言设定下的名称。如果当前语言没有对应的翻译,系统则使用对象名称。
*对象: 显示该建筑类型的模拟交通内部对象名称。

文字的颜色有如下含义:
**蓝色文字表示必须建造在城市里的旅游景点
**绿色文字表示必须建造在乡下里的旅游景点
**黑色文字表示纪念碑,能建造一次

选项

**忽略气候: 此选项可禁用与各种建筑类型相关的气候限制。
**使用时间线年份: 此选单仅包含当前模拟交通年份内可用的建筑物。
**显示过时信息: 扩大选单,使其包含当前模拟交通年份已过时的建筑物。
**城市景点: 此选单中将包含城市景点。
**陆地景点: 选单中将包含陆地景点。
**纪念碑: 选单中将包含纪念碑。
**旋转: 如果所选建筑可从不同角度显示,则可在此处选择特定视角。左下角的图像会随之改变。如设置此选项为“随机”,系统则会随机显示建筑的任意视角。

simutrans-124.5/simutrans/text/cn/curiositylist_filter.txt000066400000000000000000000035131520675460500242330ustar00rootroot00000000000000景点列表

景点列表

景点列表包含所有既是旅客和邮件的目的地又是出发地的旅游景点的信息。

打开景点列表:点击列表管理中的景点列表工具。
{提示: 如果列表中的项目仅部分可见,可调整景点列表大小或使用滑块滚动列表。}

排序: 可设置旅游景点的显示顺序。
点击按钮可在选项之间循环切换(更改选项按钮的名称):

- 名称: 按名称字母顺序排列。
- 乘客级别: 按旅客和邮件目的地的相对受欢迎度排序。

- 升序 / 降序: 反转列表顺序。

点击景点列表中列出的项目,查看有关该旅游景点的更多信息。
{提示: 点击更多信息中的旅游景点图片,即可将游戏画面切换到该景点。}

每个旅游景点列出的项目包括:

状态颜色条的颜色表示景点的评分:
- : 不属于站点的服务范围。
- 绿: 设有乘客停靠站。
- : 已关联邮件收发站。
- 松绿: 设有乘客和邮件停靠站。
- : 相关站点人满为患。
- : 所有相关站点都人满为患。

建筑图像: 显示它是否是城市景点。

旅游景点的名称

乘客级别: 括号内的数值表示该目的地作为旅客和邮件目的地的相对受欢迎度。

{提示: 为提供最佳服务,整个旅游景点必须位于一个或多个站点的服务范围内。}

simutrans-124.5/simutrans/text/cn/depot.txt000066400000000000000000000161031520675460500210520ustar00rootroot00000000000000仓库

仓库控件

仓库用于购置和管理载具。不同类型的运输工具拥有各自的仓库。在电气化轨道上的仓库仅显示电力载具。
如果启用时间线进行游戏,那么随着模拟交通中时间的推移,游戏中会出现更多的载具单元。

建造工具栏中提供了用于建造不同类型仓库的工具,这些仓库对应以下运输方式:火车库单轨列车库有轨电车库公路车库船坞飞机库

在游戏视图中,使用检查工具点击仓库,打开仓库控件,其中包含有关编队(一个可运行的载具或载具单元的组合)的信息,并可购买和管理编队。
仓库控件的标题栏显示仓库为哪种类型的载具提供服务。

仓库控件可以调整尺寸(点击控件标题栏上的向下箭头即可恢复原始尺寸)。
点击标题栏上的左/右箭头,即可在相同运输类型的仓库之间循环切换(将游戏视图中心对准仓库)。

仓库控件顶部,显示仓库中的编队数量、编队名称和线路

编队名称: 点击编队名称旁边的左/右箭头按钮,即可在仓库中循环浏览编队。
更改编队名称:单击编队名称框并输入新名称(默认情况下,这是为新编队购买或组装的第一个载具单元的名称)。

线路名称: 按名称点击左/右箭头按钮选择运输类型的线路,或点击线路名称框打开线路的下拉列表(点击下拉列表中的线路进行选择)。
更改线路名称: 点击线路名称框并输入新名称(默认情况下,这是创建线路时分配的编号)。

编队图像: 在编队和线路名称框下方,会显示任意选中编队的图像。
将鼠标悬停在所选编队中的载具单元图像上,即可查看有关该载具单元的详细信息(显示在仓库控件的底部)。
点击编队图像中的载具单元,将其从选定的编队中移除并存放在仓库中。

图像下方显示了所选编队中的载具单元数量、编队的方格长度(所有载具单元必须能够进入合适的站点以接送货物和乘客),以及分配的线路。

仓库控件的选项按钮(点击使用)包括:

开始: 如果能找到路线,则可将选定的编队从仓库派出。你可以按住 Ctrl 键启动所有编队。

行程表: 打开控制界面,可更改编队站点等待的路线和物品数量。

拆卸: 将选定编队中的载具单元放入存储中。

出售: 出售当前编队,账户余额将存入转售金额。按住 Ctrl 键可出售所有编队。

新线路: 打开控件以定义行程(编队在站点等待的路线和最小物品数量),可用于多个编队

分配线路: 将显示在仓库控件顶部的线路名称框中的线路分配给选定的编队

更新线路: 打开控件以更改所选线路的行程表(编队在站点等待的路线和最小物品数量)。

复制编队: 购买并组合另一个与所选编队的组成、行程线路相同的编队。

下方显示了所有可供购买的载具单元的选项按钮图形。
载具单元图像: 载具单元按类别划分,可通过点击索引标签查看。索引标签将载具单元分为以下几类:客运和邮件载具单元、其他动力(机动)载具单元以及其他拖挂/货厢。
载具单元图像上方的白色数字表示编队解散后存储的数量。存储的载具单元可出售,也可在新编队组装时使用。

将鼠标悬停在载具单元图像上,即可查看载具信息:
名称: 括号内为机动载具所使用的动力类型。
花费: 购买价格,括号内为每公里运营成本。
功率: 机动载具的发动机功率和最高速度。
容量: 载具单元可承载的对象物类别和数量。
重量: 以吨为单位。
最高速度: 非机动载具单元最高速度。
引入日期: date when vehicle unit becomes available if timeline is on.如时间线功能已开启,则显示载具单元可用的日期。
退役日期: 载具单元报废日期。
建造: 为模拟交通创建载具单元的人。
齿轮: 功率比。
转手价值: 出售先前购买的载具单元后可收到的金额。

点击索引标签下的载具单元图像,即可根据载具选项按钮(位于仓库控件左下角)的设置选择购买或出售该载具。该按钮可在三个选项之间循环切换:
附加: 购买载具单元并将其添加到选定的编队中,或创建一个新的编队。
放在前面: 购买载具单元并将其添加到当前编队的前面,或创建一个新的编队。
出售模式: 出售存储的载具单元。

颜色条: 在载具单元图形下方,指示哪些载具单元及较老的载具单元可以相互连接以形成可操作的编队(颜色条会随着载具选项按钮的不同设置而改变)。
颜色表示:
- 绿: 载具单元可使用。
- : 载具单元不能用。
- : 要组成一支可运转的编队,必须在前面或后面放置另一个载具单元。
- : 车载单元仍可使用,但已过时。
{提示: 如果编队所有载具单元都显示绿色条,则表示编队可运行;如果还剩下部分黄色条,则表示缺少载具单元组件。}

显示过时信息: 如启用时间线模式,则缩进的方形按钮(点击使用)会显示已退役的载具单位。

simutrans-124.5/simutrans/text/cn/depotlist.txt000066400000000000000000000004601520675460500217450ustar00rootroot00000000000000仓库列表

仓库列表

此对话框列出了地图上的所有仓库。可通过选项卡按车辆类型进行筛选。右键单击条目或单击箭头可将地图上的位置更改为仓库区域,或左键单击条目以打开仓库对话框。

simutrans-124.5/simutrans/text/cn/display.txt000066400000000000000000000106631520675460500214110ustar00rootroot00000000000000显示设置

显示设置

显示设置窗口包含控件,可调整游戏效果并提供有关计算机性能的信息。

点击游戏选项菜单中的显示按钮,打开显示设置窗口。

点击方形按钮选择选项(选中相应选项后按钮会缩进),或使用箭头按钮调整设置:

显示网格: 显示各个地块之间的线条;在地形改造时非常有用。

地下视图: 显示地下世界;在这里,你可以利用隧道建造交通网络;所有地下层都一览无余。

切片地图视图: 在此模式下,你还可建造地下隧道;地图会在指定层级进行切割,并只会显示相应层级的隧道;可使用箭头调整层级,或直接在框中输入数字。

昼夜循环: 如选中,游戏将在白天和黑夜之间循环。

亮度: 设置游戏画面的明暗度;数值越高画面越暗;数值过低或过高均会导致问题。

滚动反向: 反转游戏窗口中游戏视图的滚动方向。

滚动速度: 设置游戏视图的滚动速度。

透明,而非隐藏: 被选为隐藏的对象(例如树木或某些建筑物)将变为半透明,而不是隐藏。

隐藏树木: 隐藏所有树木。

建筑显示模式: 你可通过箭头在“不隐藏建筑物”、“仅隐藏城市建筑物”或“隐藏所有建筑物”间选择。

智能隐藏对象: 根据设置,鼠标指针周围一定数量的图块内的对象会被隐藏;图块数量可用箭头设置,也可直接在框中输入数字来设置。

透明的车站覆盖范围: 站点/车站的覆盖范围以透明方式显示,而不是以网格形式;并以玩家自己的颜色着色。

显示车站覆盖范围: 显示每个站点/车站的覆盖范围。

显示车站名: 切换显示每个站点/车站名称的样式。

显示等待条: 可切换是否显示等待队列中的货物/乘客占车站总容量比例的微型条形图。

城镇中的行人: 切换显示城市区域内行人的移动。

车站的行人: 切换显示行人在站点下车的画面。

交通密度: 决定城市区域的私家车数量。

城市区域新增私家车的数量取决于城市区域的大小和交通密度设置;数值越高,私家车数量越多,而“0”则不会新增私家车。

编队工具提示: 自定义游戏视图中显示的有关编队的信息量。

显示行程表停靠站: 如启用此功能,则在打开载具行程表时会显示所有停靠点和途经点。

提示: 可在 simutrans/config/ 文件夹中的 simuconf.tab 文件中更改模拟交通启动时的更多选项和默认值。

显示性能信息

这些设置下方会显示运行模拟交通时计算机的性能信息。一切正常时,数字显示为白色;如显示为红色或黄色,你可能需要更改设置。

如果你通过游戏视图顶部的快进图标 >>[,]/[.] 键来改变游戏中时间流逝的速度,这也可能会改变数字颜色。

帧时间: 显示帧与帧之间的实际时间间隔。

闲置: 显示计算机在无活动间暂停的时间。当该值大于0时,表示计算机有能力在运行模拟交通的同时执行其他任务。

FPS: 数值越高,载具移动越流畅。在快进模式和网络模式下,帧率固定;其他模式下,帧率则根据负载而变化。默认值可通过命令行参数 “-fps” 或在 simuconf.tab 文件中设置。如果该数值始终显示为红色,则表示计算机性能不足以运行当前设置(请尝试缩小游戏窗口大小)。

模拟循环: 每秒模拟循环次数。如果数字始终为红色,则表示计算机运行速度太慢,不能满足当前设置(请尝试使用面积更小、城市区域更少的地图)。

simutrans-124.5/simutrans/text/cn/edittools.txt000066400000000000000000000122711520675460500217470ustar00rootroot00000000000000游戏和地图编辑工具

游戏和地图编辑工具

游戏编辑工具允许对当前游戏世界进行进一步的自定义。
工具功能: 改变城市区域的人口;建设城市道路、景点、发电厂和产业设施;取消以其他玩家公司身份运营的选项;并将游戏推进一年。

不论通过玩家菜单还是 P+ 菜单中选择以公共服务玩家模式运行游戏,均可从特殊施工工具(或通过按键 [P] 选择)中打开工具栏。即使再次使用切换玩家的选项,工具栏也会保持打开状态。
将鼠标光标悬停在工具选项上(打开或单击工具栏后),即可查看某些工具选项的名称和更多信息。

工具选项包括(从左到右):

发展城市: 该工具可将城市区域的人口增加100,并在必要时修建新道路和建筑物。
使用方式: 点击工具进行选择,光标变为红色向上箭头,然后点击城市区域。

收缩城市: 该工具将城市区域的人口减少100。只有当无家可归者和失业率上升时,城市区域才会继续增长。
使用方式: 点击工具进行选择,光标变为红色向下箭头,然后点击城市区域。

城市道路: 该工具用于在两点之间建造城市道路,作为公共服务玩家,所有玩家均可使用。城市道路不得建造在其他建筑物占据的区域,也不得穿越崎岖地形、水域和障碍物。在两点之间建造的城市道路可利用其路径上的现有道路。
将鼠标光标悬停在工具选项上(打开或单击工具栏后),即可查看公共服务玩家的名称、建设和维护成本以及最大速度限制。 要建造城市道路:点击工具进行选择(光标变为城市道路),然后点击游戏视图作为道路的起点(游戏视图中会显示推土机,游戏视图底部栏右侧会显示地图坐标),最后点击地形作为道路的终点。
{提示: Different types of roads may connect. Use bridges & tunnels to connect roads across rough terrain, or to avoid obstructions. Use Terraforming tools to change terrain, to enable paths for road. Use Destroy/Remove to remove city-road & some obstructions. Use [Ctrl] at same time for extra functions.不同类型的道路可以连接。用桥梁和隧道连接崎岖地形上的道路,或避开障碍物。用 [Ctrl] 键可执行更多功能。}

建造随机景点: 该工具会建造一个乘客想去的景点。不同的景点会带来不同数量的乘客和邮件。建造的景点类型随机,并只有在找到合适的、无障碍物的空间时才会出现。
建造景点: 点击工具进行选择(光标变为黄色圆圈中的红色水滴图标),然后在游戏视图中地形上所需的位置点击。
{提示: 公共服务玩家可使用摧毁/移除功能移除景点。使用摧毁/移除和/或地形工具创建适合成为景点的地点。}

建造一座新的发电站: 该工具用于建造一座发电站(以及可能的供应商)。建造的发电站类型是随机的,并且只有在找到合适的、无障碍物的空间时才会出现。
建造新的发电站: 点击工具或按 [I] 键选择(光标变为黄色圆圈中的红色水滴图标),然后在游戏视图中地形上所需的位置点击。
{提示: 公共服务玩家可使用摧毁/移除工具移除发电站。使用摧毁/移除和/或地形改造工具创建适合建设发电站的地点。}

在最近的城市建造一个新市场: 该工具会在城市区域内建造一个终端零售市场(以及可能位于城市区域之外的供应商)。建造的产业类型是随机的,并且只有在找到合适的、无障碍物的空间时才会出现。
使用方式: 点击工具进行选择(光标变为黄色圆圈中的红色水滴图标),然后在游戏视图中点击以在最近的城市区域建造。
{提示: 公共服务玩家可使用摧毁/移除工具以移除发电站。使用摧毁/移除和/或地形改造工具来创建适合产业发展的区域。}

禁止进一步更换玩家: 该工具移除了以其他玩家公司身份操作的选项,以及打开游戏(地图)编辑工具的选项。
使用方式: 点击工具进行选择(光标变为锁形),然后点击游戏画面中的任意位置,无需确认。

按年划分的阶梯时间表: 游戏中的进度会每年向前推进。如启用时间线模式进行游戏,随着时间的推移,游戏中会出现更多运输和建造选项,而速度较慢的载具的收益会降低(因为收入根据可用运输工具的平均速度计算)。
使用方式: 点击工具,无需进一步确认(游戏视图下方底部栏左侧的日期会发生变化)。

simutrans-124.5/simutrans/text/cn/enlarge_map.txt000066400000000000000000000004261520675460500222120ustar00rootroot00000000000000地图扩展帮助

扩展地图

可以向右和向下扩展地图. 如果想要向左或向上扩展地图, 必须先将地图旋转, 再扩展, 最后再旋转回去.
地图扩展预览在对话框窗口的右上显示.

simutrans-124.5/simutrans/text/cn/factorylist_filter.txt000066400000000000000000000041441520675460500236510ustar00rootroot00000000000000工厂列表

工厂列表

工厂列表显示当前游戏中所有工厂(商品供应商和消费者)的信息。

打开工厂列表: 点击列表管理中的工厂列表工具。
{提示: 如列表中的项目仅部分可见,则调整 产业/工厂列表的尺寸或使用滑块滚动列表。}

排序: 可通过选项设定所列产业的排序方式。
点击按钮可在选项之间循环切换(更改选项按钮的名称):

- 工厂名称: 按名称字母顺序排列。
- 输入: 按输入存储容量排序。
- 输出: 按输出存储容量排序。
- 生产: 按产业的生产能力排序。
- 评级: 按状态颜色条(见下文)对产业订单进行排序。
- 耗能: 按给产业供应的电力排序。

- 升序 / 降序 反转列表顺序。

点击工厂列表中列出的项目,查看有关该产业的更多信息
各产业列出的项目包括:

状态颜色栏的颜色表示产业运作情况(也用于产业信息):
- : 不需补给。
- : 虽然交通便利,但产业供应链供应不足。
- 绿: 处于最佳状态。
- : 目前运行正常,但仍有改进空间。
- : 运营正常,但部分产业供应链存在供应过剩。

红色闪电图标: 如果该产业通过变压器供电,则会显示此图标。

工厂名称: 产业的名称。

括号内的 3 个值,代表:
当前输入: 已储存待加工的货物数量。
当前输出: 已储存待运输的货物数量。
总生产: 工厂的生产率(每月最大产量)。

simutrans-124.5/simutrans/text/cn/finances.txt000066400000000000000000000067111520675460500215310ustar00rootroot00000000000000财务

财务

财务窗口显示了玩家公司财务状况的概览。

要打开,点击主视图顶部的金钱图标,按 [f] 键,或点击玩家对话框中的玩家姓名。

数值以哈约币为单位,并列出当前和过去的日历月或年份。点击相应的标签页即可更改图表的时间跨度。
- : 显示年度值。
- : 显示月度值。

该图表以 y 轴表示数量,x 轴表示时间。你可同时设置显示多条曲线,将鼠标悬停在图表上的某个点上,即可显示该点对应的数值。

你可用右上角的建造总部按钮建造或升级你的公司总部(前提是扩展包提供了相应的图像素材)。总部将作为高级景点,并且在某些场景中是必需的。


数值(点击名称按钮查看图表详情),包括:

载具收益 - 客运、货运和邮件运输的总收入。

运营成本 - 所有编队在运输途中产生的总成本。
{提示: 载具每公里运营成本可在仓库和编队信息中查看。}

维护 - 维护交通网络所产生的总成本。
{提示: 每月维护网络的费用(维护 (月度)显示在财务的右侧)将在游戏日历月结束时扣除。}

道路通行费 - 编队经由其他路线行驶会产生费用。如果你使用其他玩家的路线则需要付费。但如果你提供非常好的道路连接,你也可以从其他玩家那里获得收益。

输电线路 - 从高功率输电中出售电力所产生的收入。

营业利润 - 运输利润、道路通行费、高功率传输利润减去维护和运营成本(以上所有成本之和)。

新载具 - 载具买卖的支出和收入。

工地 - 交通网络建设、地形设计、高功率输电线路建设,以及使用摧毁/移除和其他拆除工具对道路(轨道、道路、运河和简易机场)进行拆除所产生的总成本。

现金周转 - 总收入(高于此金额的所有收入之和)。

旅程 - 所有运输物品的数量(包括乘客、邮件和货物)。中转次数按每段航程计算一次。


第二行还显示了四个额外的值。这些是累积值:

账户余额 - 当前可用于建造或购买载具的现金(也显示在游戏视图下方的底部栏中心)。

资产 - 截至上个日历月末,公司所有载具的价值。
{提示: 每个月运营载具的价值都会略有贬值,载具的当前售价可在编队详情中查看,或在载具位于车库时查看。}

利润 - 是营业利润与收益的比率。

净财富 - 账户余额加上资产,这就是公司的价值。如果净资产变为负数,你的公司就破产了,你就输掉了这局游戏。

{提示: 游戏中的一些费用、初始余额和财务状况可在 simuconf.tab 文件中更改。}

场景信息

如果你玩某个场景,就会显示场景目标和进度百分比。

simutrans-124.5/simutrans/text/cn/general.txt000066400000000000000000000060551520675460500213610ustar00rootroot00000000000000模拟交通帮助

通用帮助

- 欢迎
- 游戏界面
- 鼠标帮助 (待写)
- 键盘帮助
游戏选项
- 语言
- 玩家颜色
- 显示设置
- 声音和音乐
- 玩家
- 载入游戏
- 保存游戏
- 新游戏
- - 气候控制
- - 载入高度图

主菜单
- 游戏选项
- 地图
- 检查工具
- 地形设计工具
- 铁路/火车工具
- 单轨工具
- 电车工具
- 公路工具
- 港口工具
- 机场工具
- 专用建筑工具
- 游戏编辑工具
- 摧毁/移除

管理菜单
- 线路管理
- Lists
- - 停靠站/车站列表
- - - 停靠站/车站列表筛选器
- - 载具列表
- - - 载具列表筛选器
- - 城市列表
- - 对象物列表
- - 产业列表
- - 景点列表
- 信息中心
- 财务

其他选项
- 保存截图
- 暂停
- 快进
- 帮助

其他对话框
-仓库控件
-行程控件
- 站点信息
- - 站点详情
- 编队/载具信息
- - 编队/载具详情
- 城市信息
- 产业信息

如果这里找不到帮助,请尝试:
http://guide.simutrans.com, 或
http://wiki.simutrans.com, 或
http://simutrans-tips.com, 或
http://forum.simutrans.com

simutrans-124.5/simutrans/text/cn/goods_filter.txt000066400000000000000000000054211520675460500224200ustar00rootroot00000000000000对象物列表

对象物列表

对象物列表包含有关游戏中可运输的不同物品及运输这些物品将产生多少收入的信息。

打开方式: 点击列表管理中的对象物列表工具,或按 [G] 键。

游戏中可运输的物品列表显示: 收入、速度加成百分比、对象物类别、重量。
{提示: 如果列表中的项目仅部分可见,则调整对象物列表尺寸或使用滑块滚动列表。}

速度奖励: 显示为获得所列收益,在不同运输方式下所需的编队最高平均速度。如最高平均速度高于所列速度,某些物品会获得更多收益;反之,如低于所列速度,则收益可能会减少。
启用时间线设置后,速度值会随时间推移而变化。这种变化通常发生在引入速度更快或更经济的编队之后。
可用箭头按钮按百分比调整速度并更新所有显示的收入。如想了解编队在最高平均速度未达到奖励速度时能获得多少收入,无需手动计算,此功能非常有用。

排序: 显示项目列表的显示顺序。
点击选项按钮可在各种排序选项之间循环切换(更改选项按钮的名称):

- 按名称: 按字母顺序排列。
- 按收入: 按收入排序。
- 按奖金: 按速度奖励 % 排序。

- 升序 / 降序 反转列表顺序。

每个物品的详细信息如下:

色块: 与游戏视图中站点上方的对象物颜色条显示的颜色相同,用于指示等待运输的物品数量。
{提示: 按 [!] 键在游戏视图中切换站点上方的对象物颜色条。}

收入: 在当前支付模式下,运输一个单位一格的距离所获得的收入。
计算方法为 {(基本收入) * (1 + ((最高平均速度) / (速度奖励) - 1) * (% 奖励))}。

% 奖励: % 收入变化与编队最大速度呈线性关系,在奖励速度附近与速度奖励成反比。

对象物类别: 表示运输所需的载具类型。特殊货物需要特定类型的载具。
{提示: 载具的容量显示了载具可运输哪些物品。}

重量: 显示一单位对象物的重量(以千克为单位)。
运输过程中,运输的物品会增加载具重量,导致载具行驶时比空载时需要更大的动力。

simutrans-124.5/simutrans/text/cn/groundobj_build.txt000066400000000000000000000034601520675460500231110ustar00rootroot00000000000000放置地形对象

放置地形对象

通过“放置地形对象”对话框,你可以放置各种对象以美化地图。此对话框可通过地图编辑器打开。这些地型对象并非游戏必需,它们纯粹用于视觉效果,不会妨碍开发。地形对象包括冰河时代遗留的岩石、有鸭子的小池塘、花朵、灌木、仙人掌、地标等等。

顶部是可供选择的特定气候带
?全部表示列出所有可用的地形对象,无论其气候带如何,但只有与土壤气候带相匹配的地形对象才能放置。你若想忽略气候带检查,可启用旁边的忽略气候带选项。这将阻止在放置对象时检查气候带。
你若根据目标土壤选择气候带,则系统只会显示匹配的地形对象。

你可在下方更改名称的显示方式和列表中的排序方式。
?翻译: 显示游戏中所使用的名称。这取决于所选语言。若所选语言无可用的翻译,则系统显示对象名称。
?对象: 显示程序内部对象的名称。
?移除成本: 仅排序,显示翻译名称。
旁边的按钮可将排序方式切换为降序。

选单

?左侧是包含地形对象的选单。
?右侧显示所选地形对象的信息。
名称、任何说明文字、所有适用的气候带、是否有雪的图形、哪些季节有图形、移除此对象需要多少费用以及谁绘制了此图像。
4 季: 表示春季、夏季、秋季和冬季。
?选中对象的图像显示在右下角。

选项

?忽略气候带: 此选项会在放置对象时禁用气候带分配。地形对象。

simutrans-124.5/simutrans/text/cn/haltlist.txt000066400000000000000000000067521520675460500215740ustar00rootroot00000000000000站点列表

站点列表

站点列表显示信息,并提供控件以筛选和列出不同的站点(但不是仅供水上运输载具使用的站点),载具在这些站点上下客和接送货物。

打开方式: 列表管理中的站点列表工具。站点列表包含四个选项按钮,其中两个用于对列表中的项目进行排序,另外两个用于应用选项以选择要在列表中显示的项目。按钮下方是符合筛选条件的站点列表。
{提示: 如未列出任何站点,请更改筛选器选项。如列表中的项目仅部分可见,请调整 站点列表尺寸或使用滑块滚动列表。}

点击按钮可循环切换选项(更改选项按钮名称)或打开筛选器设置控件:

排序: 两个选项按钮决定了列表中显示的站点的顺序:

- 类型: 按可接受的载具类型划分订单。
- 未分类: 按随机顺序排序
- 名称: 按 ASCII 码顺序(大写字母在前,小写字母在后)对分配的名称进行字母数字顺序排序。
- waiting: 根据货物和乘客数量与等候容量和提供的运输服务之间的关系进行订单处理。

- 升序 / 降序: 反转列表顺序。

筛选器: 两个选项按钮,用于打开/关闭列表选择条件和访问控件以更改条件。

- 启用 / 禁用: 点击切换筛选条件。

- 设置: 点击打开控件以更改筛选条件。

点击站点列表中列出的项目,查看有关该站点的更多信息
每个站点列出的物品包括:

状态颜色条: 颜色显示站点的运行情况,并反映其拥挤程度。颜色条也用于站点信息和线路管理中,并与游戏显示中站点上方颜色条显示的颜色相同:
- : 无服务,也未等待。
- 绿: 无需改进。
- : 站点略微拥挤(主要是转运货物)。
- : 车站容纳的乘客超过其容量的 1.5 倍,或有超过 200 名不满的乘客,或工厂因车站过度拥挤而不得不停止生产。
{提示: 按 [!] 键切换游戏显示中站点上方的颜色条。}

名称: 已分配。 {提示: simuconf.tab 中提供了为站点名称分配编号的选项。}

载具图标: 指示哪些类型的载具可用站点(也用于站点信息和线路管理)。
图标包含: 公交车(用于公路客运车辆)、卡车(用于公路货运车辆)、火车、轮船和飞机。有轨电车可根据站点类型以公交车图标或火车图标表示。

货运图标: 显示站点可处理哪些物品(乘客、货物和/或邮件)(也用于站点信息和线路管理)。
{提示: 添加相应的扩展功能可改变站点可处理的物品类别。邮局 可添加到站点以处理邮件。}

等待: 站点 内不同货物和乘客的详细信息。

simutrans-124.5/simutrans/text/cn/haltlist_filter.txt000066400000000000000000000037351520675460500231370ustar00rootroot00000000000000站点列表筛选器

站点列表筛选器

站点列表筛选器具有确定哪些站点(但不包括仅供水上运输载具使用的站点)显示在站点列表中的选项,这些站点是载具上下客的地方。

点击站点列表中的设置,打开站点列表筛选器

筛选选项启用时,单选按钮可选择要列入车站列表的站点。只有符合所有设定条件的站点才会被列出。

点击按钮选择/取消选择筛选条件(选中后按钮会缩进):

筛选器名称: 按名称选择站点。使用方法:点击按钮进行选择,然后点击名称框并准确输入所需的名称(区分大小写)。

筛选器类型: 如果选中,它将列出至少包含以下一项的站点。使用方法:点击按钮。(仅供水上载具使用的站点不会列在站点列表中)

特殊筛选器:
- 溢出: 未使用。
- 无连接: 选择没有交通服务的站点

需求: 指的是所选对象旅程的终点。点击名称旁边的按钮选择对象。使用滑动条滚动项目列表。选项包括:
- 全部: 选择所有对象。
- : 不选中对象。
- 反选: 反转当前的选择。

生产: 指的是所选对象旅程的起点。点击名称旁边的按钮选择对象。使用滑动条滚动项目列表。选项包括:
- 全部: 选择所有对象。
- : 不选中对象。
- 反选: 反转当前的选择。

simutrans-124.5/simutrans/text/cn/industry_info.txt000066400000000000000000000106111520675460500226310ustar00rootroot00000000000000产业信息

产业信息

产业信息显示与所选产业(对象物供应商或消费者)相关的详细信息,可将游戏视图移动到游戏世界中的相关位置。

对象物供应商和消费者相互连接,构成产业供应链,最终到达终端消费者。有些产业既是消费者又是供应商,位于产业供应链的中间位置。若玩家不是在新手模式下进行游戏,在消费者端的供应过剩时,产业将停止供应。
产业也是乘客和邮件的目的地和始发地。如站点的服务范围覆盖产业建筑的任何部分,并且该站点能够处理该对象物,则载具可通过站点向产业运送货物和乘客。 {提示: 近海停靠站/石油钻井平台/养鱼场/集水区/石油/邮件/乘客/未列入站点清单。}

可在创建新世界中设置产业供应链的初始数量。 随着城市区域的发展,游戏中会出现更多产业。从大约2000年开始,每当城市区域的人口翻一番,就会出现新的产业。
{提示: 使用游戏编辑工具中的在最近的城市建造新市场工具,即可在城市区域添加新产业。在 cityrules.tab 文件中更改新产业出现的时机(industry_increase_every = 0 表示不生成新产业)。}

在游戏视图中使用检查工具点击产业,或点击在产业列表中列出的产业,即可打开产业信息
如所有项目都不可见,请使用滑动条滚动项目。

所选产业的名称显示在产业信息的标题栏中。
有关所选产业和选项的其他信息包括:

生产: 该产业每月/每天将供应(如果是供应商)或消耗(如果是最终消费者)的对象物的最大数量(单位)。
{提示: 增加变电站并向产业供电可提高对象物生产/消费率。}

产业图片: 显示的是产业建筑的景象。点击图片可将游戏视角切换到产业建筑。

状态颜色条: 图片下方表示该产业的运作情况(也用于产业列表中):
- : 无需输入。
- : 虽然交通便利,但产业供应链供应不足。
- 绿: 处于最佳状态。
- : 目前运行正常,但仍有改进空间。
- : 运营正常,但部分产业供应链存在供应过剩。

消费者: 列出了选定产业生产的对象物在产业供应链中的目的地。
目的地产业及其地图坐标以箭头按钮的形式列出。点击箭头按钮即可将游戏视图移动到目的地。

供应商: 显示所选产业在所需的产业供应链中的对象物的来源。
供应商所在产业及其地图坐标以箭头按钮的形式列出。点击箭头按钮即可将游戏视图切换到供应商位置。

工人住所: 显示附近哪些城市区域有员工居住。来自这些城市区域的乘客和邮件需要到达所选产业。
城市区域及其地图坐标以箭头按钮的形式列出。点击箭头按钮可将游戏视图切换到供应商页面。

乘客级别: 是作为旅客目的地的相对受欢迎程度。
邮件级别: 是作为邮件目的地的相对受欢迎程度。
{提示: 产业建筑只需有一平方格位于站点的服务范围内,即可为乘客和邮件提供最佳服务。}

生产: 详细介绍产业产出情况。
展示的信息包括: 对象名称;当前存储对象数量(按最大存储对象容量计算);对象分类;及输出量与输入量的百分比。

消耗: 详细介绍行业投入情况。
展示的信息包括:对象名称;当前存储对象数量(按最大存储对象容量计算);对象分类;以及投入量与产出量的百分比。

服务于该产业的站点列于行业信息的底部。

simutrans-124.5/simutrans/text/cn/inspection_tool.txt000066400000000000000000000121021520675460500231420ustar00rootroot00000000000000检查工具

检查工具

检查工具用于打开对话框,提供各种项目的信息或选项控件。

点击游戏视图顶部的放大镜图标或按 [a] 键打开检查工具(光标变为放大镜)。

使用检查工具:将放大镜光标置于游戏视图中的所需位置,然后单击以打开对话框。如果多个对象位于所需位置,则再次单击将打开第二个对话框。 {提示: 在 simuconf.tab 中,有选项可设定选择打开给定光标位置对应的一个对话框或所有对话框。}

在游戏视图中使用工具点击以下项目,即可打开各种对话框:

仓库 - 打开控件以在选定车库采购和管理车辆。

站点 - 打开选定站点的站点信息

载具 - 打开选定载具的编队/载具信息

市政厅 - 打开选定城市区域的城市信息。 {提示: simuconf.tab 中提供了选项,以打开显示市政厅相关信息的对话框。}

产业 - 打开选定产业的产业信息

城市建筑: 选择后游戏会打开一个对话框,提供有关该建筑的信息(对话框标题栏显示“建筑”)。对话框中含有的信息包括:
- 描述: 显示有关建筑物的信息。如无描述,则显示“住宅”、“工业建筑”或“商店”(对于商业建筑)。
- 所属: 表示该市政大楼所属的城市区域。
- 乘客级别: 是作为旅客目的地的相对受欢迎度。
- 邮件级别: 是作为邮件目的地的相对受欢迎度。
- 出现于: 如启用时间线模式进行游戏,则显示建筑物出现的年份。
- 价值: 显示使用摧毁/移除工具移除建筑物时产生的费用。
- 绘制: 为模拟交通绘制建筑物的人名。

纪念碑 / 旅游景点选项会打开一个对话框,显示有关景点的信息(对话框标题栏会显示它是纪念碑还是旅游景点)。对话框中含有的信息包括:
- 公共财产: 表示该对象不归任何玩家公司所有。
- 描述: 提供有关该景点的信息。
- 乘客级别: 作为旅客目的地的相对受欢迎度。
- 邮件级别: 作为邮件目的地的相对受欢迎度。
- 出现于: 如启用时间线模式进行游戏,则显示建筑物出现的年份。
- 绘制: 为模拟交通绘制景点的人名。

道路和轨道选项会打开一个对话框,提供有关道路或轨道的信息(对话框的标题栏显示道路的类型——如果是建在道路上的有轨电车轨道,标题栏显示道路的类型)。
对话框中包含的信息:
- 最高速度: 道路或轨道上允许的最高速度。
- Ribi (unmasked):
一个内部计数器,用于指示行进方向。
- Ribi (masked): 一个内部计数器,用于指示方向,同时考虑任何信号或标志。
- 带有标志/信号: 如果有信号或标志出现,则会显示该信号或标志。
- 电气化/非电气化: 表示该路段是否已经过电气化改造。
- 上月经过的编队: 表示上个游戏日历月中使用此路段的载具的数量。
- 保留: 表示哪个载具已预留轨道,待其通过后方可使用。
- 路坡: 表示坡度方向(如有)。
- 地图坐标: 如底部栏所示
如电车轨道建在道路上: 信息会显示两次,第一次是道路信息,第二次是电车轨道信息。

信号/标志: 打开一个对话框,对话框的标题栏会显示它是信号灯还是路标。 对话框中包含的信息包括:
- 方向: 这是一个内部计数器,用于指示信号的应用方向。

输电线路 / 变电站: 打开对话框,显示有关电网的信息(对话框标题栏显示“输电线路”)。显示的信息包括当前的电力需求和供应情况。

树木: 打开一个对话框(标题栏显示“树”)。显示的信息包括:
- 名称: 它提供了树木种类。
- 年龄: 树龄(以月为单位)。

土地: 打开一个对话框(标题栏显示“土地”)。显示的信息包括:
- : 它提供了树木种类。
- 地图坐标: 如图所示,位于底部栏

{提示: simunconf.tab 中提供了开启私人道路使用者和行人对话的选项。}

simutrans-124.5/simutrans/text/cn/jump_frame.txt000066400000000000000000000010031520675460500220550ustar00rootroot00000000000000跳转至

跳转至

按下 [J] 键即可打开对话框,并可跳转到指定坐标。这对于快速移动到游戏世界中的指定位置非常有用。

游戏世界中的每个图块都有两个坐标 (x,y),其中 (0,0) 位于地图的左上角。光标坐标会永久显示在状态栏上。
说明: 地图旋转(使用 [R] 键执行)将旋转所有图块坐标。

simutrans-124.5/simutrans/text/cn/labellist_filter.txt000066400000000000000000000003231520675460500232540ustar00rootroot00000000000000标记列表帮助

玩家标记列表

这里列出了地图上所有的标记.
仅显示活跃玩家选项 只显示活跃玩家设置的标记.

simutrans-124.5/simutrans/text/cn/language.txt000066400000000000000000000014571520675460500215300ustar00rootroot00000000000000语言

语言

语言窗口用于更改游戏的文本语言。

此窗口从游戏选项窗口和创建新游戏窗口打开。

模拟交通的默认语言为英语。要使用其他语言玩游戏,请选择你想要使用的语言旁边的按钮。

游戏过程中可随时更改语言。要更改语言,请选择游戏选项->语言。

创建新游戏时语言更改会立即生效,而游戏中途更改语言时,关闭语言窗口也会立即生效。

如所选语言没有对应的翻译,系统会显示英文。

为了帮助将模拟交通翻译成你的语言,
访问 https://translator.simutrans.com/

simutrans-124.5/simutrans/text/cn/linedetails.txt000066400000000000000000000101751520675460500222370ustar00rootroot00000000000000线路信息

线路信息

线路信息显示并可编辑单条线路(一组共享同一路线的编队)的属性。线路信息可从仓库编队信息线路列表打开。

顶部是线路名称。点击方框即可更改。下方显示编队数量、已用运力和利润。下方有四个标签页。

行程表位于最左侧的标签页。你可在此处修改该线路所有编队行程表。切换标签页或关闭“线路信息”后,时刻表将立即被新的时刻表替换。如线路信息是从仓库编队信息窗口打开,则此标签页会被自动选中。

图表以图形方式显示了有关该线路及其所分配车辆的各种信息(x 轴为时间,单位为月):

- 可用容量 - 载具中可供货物和乘客使用的剩余空间量。
- 运送 - 使用这条线路的货物和乘客数量。
- 收益 - 车辆产生的收入金额。
- 运营成本 - 车辆在运输过程中产生的费用。
- 利润 - 收入减去成本(收益减去运营成本)。
- 编队 - 分配给该线路的编队数量。

载具列表 显示分配给此线路的所有编队。如某个编队未列出,则是该编队被手动分配了新的行程表,因此已从该线路中移除。

有三个管理按钮:
- 删除线路删除此线路并关闭此窗口。所有已连接的编队将继续按当前计划进行。
- 全部撤回: 除仓库外,线路上的所有运输编队都将退役。这些编队将不再接收任何新对象物,并在空载后售出。此选项可用于更新该线路的编队: 打开服务于这条线路的仓库,组建一支新的编队,并将其分配给这条线路。然后选择全部撤回,按需多次复制仓库中的新编队,并开始组建全部的新编队。

本线路的编队列表位于按钮下方。点击列表中的项目即可查看该编队的更多信息(使用滑块滚动列表)。
每个编队列出的项目包括:
名称: 已分配(默认情况下,这是为编队购买或组装的第一个载具单元)。
收入: 显示利润(收入减去运营成本)。
线路: 显示分配给编队的线路名,并显示它是否位于仓库中。
图像: 显示运载物品的级别和当前编队组成。

站点列表 显示站点。点击列表中的站点即可打开其站点信息。
每个站点列出的项目包括:
名称: 该站点的名称(不显示途经点)。颜色表示该站点的拥挤程度。该颜色也用于站点列表和站点信息中,并与游戏画面中站点上方颜色条显示的颜色相同:
- : 已停止服务。
- 绿: 无需改进。
- : 仍有改进空间。
- : 建议进行改进。
名称后跟载具图标,指示哪些类型的载具可用该站点(也用于站点信息和站点列表)。图标包括: 公交车(用于公路客运车辆)、卡车(用于公路货运车辆)、火车、轮船和飞机。有轨电车根据车站类型的不同,用公交车图标或火车图标表示。
下一行包含货运图标,显示站点可处理的对象物(乘客、货物和邮件)(也用于站点信息和站点列表),后面跟着等待的乘客或货物数量的摘要文本。
{提示: 添加合适的扩展功能可改变站点可处理的对象物类别。邮局可添加到站点以启用邮件处理功能。}

simutrans-124.5/simutrans/text/cn/linemanagement.txt000066400000000000000000000047611520675460500227320ustar00rootroot00000000000000线路列表

线路列表

线路列表管理线路(一组共享同一路线编队

Lines consist of Stops where vehicles pick-up and drop-off goods & passengers (water-transport vehicles may use any square of water in the catchment area of a dock) and waypoints (used to direct vehicles if more than one option is possible or provide interim destination points).线路由站点——载具在此停靠,上下客(水上运输工具可用码头集水区内的任何水域)——,以及途经点(用于在有多个选项时引导车辆或提供中间目的地)组成。

点击游戏视图顶部的网络图标或按 [w] 打开线路管理,可以调整尺寸(点击标题栏中的向下箭头可恢复原大小)。

线路列表列出了现有线路(要滚动列表:使用列表右侧的滑块),可以按名称或运输类型进行筛选(单击列表上方的索引标签进行选择):
全部: 列出所有载具的线路。
火车: 列出所有铁路车辆的线路。
单轨列车: 列出所有单轨/磁悬浮车辆的线路。
电车: 列出所有电车的线路
卡车: 列出所有道路车辆的线路。
: 列出所有水上运输工具的航线。
飞行器: 列出所有飞机的航线。

列表中出现的线路名称的颜色表示以下含义::
- 没有载具分配给线路
- 未运营,未产生利润或亏损
- 盈利
- 拥有过时的车辆
- 线路亏损

将鼠标悬停在某条线路上,即可显示更多信息。点击列表中的线路,即可打开 线路,查看该线路的停靠站、收入和编队信息,或更改行程表。

有两种管理方案可供选择:

新线路: 创建一个新的线路窗口。这需要先选择一种运输类型。
删除线路: 从游戏中移除当前线路。属于该线路的所有编队将按照与该线路类似的单独的行程表行驶。

单一GUI: 如激活并单击某条线路,则所选线路窗口将替换最上面的线路。

simutrans-124.5/simutrans/text/cn/list.txt000066400000000000000000000032351520675460500207140ustar00rootroot00000000000000列表

列表

列表 提供了打开控件以查看游戏当前状态信息的选项(以及调整运输不同货物、乘客和邮件所获得的相对收入的控件)。

玩家可以在以下列表中获取信息: 站点编队城市区域;从不同货物、乘客和邮件中获得的收入;产业;以及旅游景点。

点击游戏视图顶部的列表图标打开列表
打开或点击工具栏后,将鼠标光标悬停在列表工具选项上即可查看名称。

点击工具选项打开信息控件,从左到右依次为:

站点列表: 站点, 载具在此上下客和接送货物(但不包括仅供水上运输工具停靠的站点)。

载具列表: 公路和铁路车辆、飞机和船舶。

所有城镇列表: (村庄、城镇和城市)。

所有对象物列表: 从不同货物、乘客和邮件中获得的收入(包括调整相对收入)。

工厂列表: 产业 (商品消费者和供应商)。

景点列表: 旅游景点。

simutrans-124.5/simutrans/text/cn/load.txt000066400000000000000000000024521520675460500206600ustar00rootroot00000000000000加载/title> <h1><strong>加载</strong></h1> <p> <strong>加载</strong>会重新启动一个<a href="save.txt">已保存</a>的游戏(继续游戏),也可用于删除这些游戏。重新启动的游戏会替换当前存档,当前存档将丢失。 </p> <p> <strong>加载</strong>可通过<a href="new_world.txt">创建新世界</a>和<a href="options.txt">游戏选项</a>或按 <a href="keys.txt">[L]</a> 键打开;并列出可用游戏的名称、日期和时间(保存时间)。<br> 如所有游戏都不可见: 使用列表右侧的<a href="mouse.txt">滑动条</a>滚动查看游戏名称。 </p> <p> 点击列表中的名称即可重新开始游戏;或在加载界面顶部的框中输入名称: 点击方框,输入名称,然后按 [Enter] 或 [Return] 键,或点击<em>确定</em>。<br> 输入错误名称时,你将收到错误消息: 关闭消息并重试。 </p> <p> 警告: 点击游戏名称旁的 x 按钮即可立即永久删除游戏。<br>谨慎使用此功能。 </p> <p> 重要: 模拟交通会定期发布新版本,以前版本的模拟交通游戏不能在新版本中运行;请阅读新版本的发行说明,以了解旧版本是否兼容。 </p> <p> 点击<em>取消</em>以关闭<strong>加载</strong>;或点击左上角的 x;或使用键盘。 </p> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������simutrans-124.5/simutrans/text/cn/load_relief.txt���������������������������������������������������0000664�0000000�0000000�00000002706�15206754605�0022210�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<title>加载高度图

加载高度图

加载高度图窗口用于打开自定义地形文件(用于创建新游戏),也可用于删除这些文件。它会从创建新游戏窗口打开,并列出可用自定义地形文件的名称、保存日期和时间。如所有文件名都不可见,请使用列表右侧的滑动条滚动浏览文件名。

单击列表中的名称即可加载自定义地形图,或在窗口顶部的文本框中输入名称: 点击方框,输入名称,然后按 [Enter] 键或 [Return] 键,或点击确定(错误的名称不会生效,可重试)。

警告: 点击文件名左侧的 x 按钮(如已在 simuconf.tab 中启用),即可立即永久删除该文件;文件名将从列表中移除,并且加载高度图窗口将关闭。谨慎使用此功能!

一些定制地形可在 http://maps.simutrans.com 在线获取。新文件必须为 .ppm 或 .bmp 格式(256 色 RLE 压缩为最佳),并放置在名为 simutrans/maps/ 的个人目录(或文件夹)中。

提示: 有关如何创建自定义地形的信息可在 http://wiki.simutrans.com 在线获取

点击取消以关闭加载高度图窗口,或点击左上角的 x 按钮,或使用键盘关闭。

simutrans-124.5/simutrans/text/cn/mailbox.txt000066400000000000000000000040431520675460500213720ustar00rootroot00000000000000消息中心和消息中心选项

消息中心

消息中心 列出游戏中的消息,并打开控件以选择消息在游戏中的显示方式。

点击游戏画面顶部的邮箱图标,或按 [B] 键打开消息中心,其中列出了游戏中的消息。
点击一条消息即可将游戏视图移动到关联位置。

点击选项按钮(点击以使用)打开消息中心选项,其中包含用于决定消息显示方式的控件。


消息中心选项

消息中心选项有三列方形按钮,用于选择消息的显示方式。

从左到右,各列分别选择消息的显示方式:游戏视图底部的滚动文本;临时消息;或永久消息。
{提示: 点击消息左上角的 x 或使用键盘关闭永久消息。}

点击方形按钮选择以下消息的选项(选中后按钮会缩进):

新年: 新的一年开始。

AI新闻: AI玩家已开通新线路。

城市新闻: 一个城市区域正在建造一个更大的市政厅。

无路线: 一个载具找不到前往下一个目的地站点或途经点)的路线。

新产业: 由于城镇的发展,一个新的产业链(包括终端消费者和所有必要的供应商)已经建立。

游客: 已建成一座城市景点或纪念碑。

新载具: 新载具投入使用,或一个载具退役(如使用时间线模式)。

站点已满: 站点拥挤不堪。

问题: 玩家公司负债累累,以及其他重要信息。

simutrans-124.5/simutrans/text/cn/map.txt000066400000000000000000000140171520675460500205160ustar00rootroot00000000000000地图窗口

地图窗口

地图提供了一幅当前世界的地图,其中包含地形、重要地点和交通网络等信息。还可借助地图移动世界视图。

点击顶部菜单中的地图图标或按 [m] 键打开地图,地图可调整尺寸或最小化。最上面的三个按钮可启用更多选项。
 选择: 显示各种按钮,以便在地图上叠加更多信息。
 颜色代码: 显示最小值-最大值颜色标尺,显示某些选项的活动级别。
 产业列表: 打开一个表格,表格中包含地图上显示的产业的颜色。

地图有不同的缩放级别:使用鼠标滚轮、Page Up/Down 键或按钮进行切换。你还可使用等距地图按钮倾斜地图以适应显示。

地图上,水域显示为蓝色,低洼地带颜色较深。屏幕上当前可见的世界区域以黄色方框标示。要移动视野范围,请在地图上所需位置单击鼠标左键,或单击并拖动黄色方格。右键单击并拖动即可在地图窗口中移动区域,无需使用滚动条。

在默认地图上(未使用任何选项时),城市区域和景点为灰色,输电线路为蓝绿色,隧道为棕色,高架道路为浅棕色,桥梁为浅灰色。地面道路则有其独特的颜色:公路近乎黑色;铁路和有轨电车轨道为深棕色;运河为蓝色;跑道为深黄色。所有其他道路(例如窄轨、磁悬浮或单轨铁路)均为浅红色。站点为红色。通路上的载具为黄色。

点击网格按钮,即可在地图上叠加类似地铁线路图的抽象线路网格。用黄色按钮,你可将其限制为仅显示客运、邮件或货运线路。

用选择按钮可叠加许多附加信息。这些信息分为三组。白色和黄色按钮互斥,同时用会禁用任何其他白色选项。绿色按钮用于叠加显示,可以与其他任何选项组合使用。第三组是红色按钮,用于显示站点。此站点组中,一次只能激活一个按钮。

选项如下:
城市: 城市区域的名称。
城市边界: 标明城市边界;新建房屋和道路只能在城市边界内建造。
建筑物数量: 城市建筑的颜色会根据其等级而变化。等级决定了该建筑产生乘客或邮件的概率。
目的地: 如果选择了某个城镇,此选项会将当前乘客目的地叠加显示(类似于城市信息中的小地图)。
景点: 在旅游景点的位置画圆圈。圆圈的颜色和大小将表示该旅游景点的客流量(使用最小-最大比例)。
工厂: 高亮显示产业(消费者和供应商)。如果此叠加层处于激活状态,并且鼠标指针悬停在地图上的产业上,则会显示名称和连接客户的白色线条;按住 [Shift] 键可显示连接供应商的红色线条。颜色与产业列表中显示的产业颜色相同。
乘客: 根据站点的所有者,将乘客覆盖区域着色为相应颜色。如网格叠加层已启用,游戏只会显示乘客换乘信息。
邮件: 根据站点的所有者,以相应颜色标示邮件接收区域。如网格叠加层已启用,游戏仅显示乘客关联信息。
货运: 所有运输路线上的货物和旅客运输量(采用最小值-最大值比例)。如网格叠加层处于激活状态,游戏仅显示货运连接。
站点状态(及其后所有的红色选项)用圆圈高亮显示站点站点状态颜色表示站点的拥挤程度。相同的颜色也显示在站点列表站点信息线路管理的状态颜色条中,以及站点名称上方的颜色条中。颜色条的尺寸表示站点的乘客容量大小。
等待: 显示在站点等待运输的乘客、邮件和货物的数量。颜色和大小表示等待的数量(采用最小-最大比例)。
排列: 显示本月旅客、邮件和货物的等待量变化情况。尺寸表示总变化量,颜色用绿色表示减少,红色表示增加(采用最小-最大比例)。
服务: 颜色和尺寸表示本月有多少个载具服务于某个站点(使用最小-最大比例)。
转运: 尺寸和颜色表示到达/离开车站的人数(使用最小-最大比例)。
起点: 颜色和尺寸表示有多少乘客和邮件想要从站点开始他们的旅程,即快乐、不快乐和无路线乘客的总和(使用最小-最大比例)。
拥堵: 根据一个月内通过的载具数量对道路进行着色(使用最小-最大比例)。
轨道: 铁路和有轨电车轨道、桥梁和隧道均以白色显示(电气化线路为红色)。信号灯以黄色显示。
限速: 按其允许的(使用最小-最大缩放)最大速度对所有通路进行着色。
仓库: 高亮显示仓库。颜色表示运输类型:船舶,黄色;公路,橙色;铁路,红色;飞机,绿色;有轨电车,蓝色;单轨列车,灰色。
输电线路: 显示输电线路上的负载。
森林: 画面中呈现亮绿色的森林。
所有权: 将地图上的所有事物都涂上其所有者的颜色(橙色表示无人拥有)。

simutrans-124.5/simutrans/text/cn/monorailtools.txt000066400000000000000000000152641520675460500226470ustar00rootroot00000000000000单轨/磁悬浮工具

单轨/磁悬浮工具

单轨/磁悬浮工具用于建造单轨/磁悬浮交通网络。这些工具可建造或移除:单轨和磁悬浮轨道;桥梁;隧道;信号灯;仓库;站台;以及扩展建筑。如启用了时间线模式游玩,随着模拟交通中时间的推移,会有更多工具选项出现。

点击游戏画面顶部的单轨/磁悬浮图标打开工具栏。
将鼠标光标悬停在工具选项上(打开或单击工具栏后),即可查看名称以及(如适用)建设成本、括号内的维护成本、最高速度限制和最大长度。

单轨/磁悬浮工具从左至右依次为:

单轨/磁悬浮轨道: 该工具用于在两点之间建造单轨/磁悬浮列车的轨道。轨道只能沿坡度方向建造,不能穿越崎岖地形、水域和障碍物。新建轨道可利用其路径上的现有轨道。
建造轨道的方式: 点击工具选择轨道(光标变为轨道),然后点击游戏视图选择轨道起点(游戏视图中会显示推土机,地图坐标显示在游戏视图底部栏的右侧),最后点击游戏视图选择轨道终点。
{提示: 不同类型的轨道可连接(但由其他玩家建造的轨道不能连接)。使用地形工具可改变地形,从而为轨道开辟路径。使用摧毁/移除功能可移除轨道上的单个部件和部分障碍物。同时按下 [Ctrl] 键可执行更多功能。撤销上次的轨道建造(按 [z] 键)不会返还建造费用。}

拆除单轨: 该工具会在游戏视图中两点之间移除单轨/磁悬浮轨道(没有车辆存在时),同时移除路径上的站台和信号灯。
使用该工具会产生建设成本
拆除单轨的方式: 点击工具(光标变为红色十字);然后点击要删除的轨道(在游戏视图中选择红色十字显示的删除点);最后点击轨道上的另一个点,删除到第一个删除点为止的部分。
{提示: 以另一名玩家的身份行动,移除他们的轨道。}

坡道桥梁: 这些工具用于在游戏视图中建造直线桥梁,供单轨/磁悬浮列车在两段单轨/磁悬浮轨道之间通行。桥梁有最大跨度限制。这些工具可以将桥梁从轨道的一端建造到合适的位置(另一轨道的末端或跨度范围内的稍高一些的地面)。
建造坡道或桥梁的方式: 点击工具进行选择(光标会变为桥梁),然后点击轨道末端(桥梁起点)进行建造。有些障碍物、缺少合适的桥梁末端位置这两种因素会阻止桥梁的建造:在需要跨越的两侧放置轨道,然后重试。
{提示: 使用摧毁/移除工具移除桥梁(点击桥梁末端)以及一些障碍物以建桥。使用轨道工具将桥梁末端连接到轨道上。}

单轨信号灯: 这些工具在游戏视图中为轨道上的单轨列车和磁悬浮列车建造信号灯。信号灯引导和调节轨道、桥梁、交叉口和站点(车辆上下客的地方)上的车辆流量。
可以创建双向和单向信号灯。在轨道上创建双向信号灯的方式:单击工具选择信号灯(光标将变为信号灯);然后单击轨道。创建单向信号灯的方式:使用信号光标再次单击同一点,即可在单向信号灯和双向信号灯之间循环切换。
重要: 注意不要设置单向信号灯,以免载具不能到达目的地;默认情况下,车辆靠右行驶(可在 simuconf.tab 中更改)。
- 信号灯: 只有在前方通往行程表中下一个信号灯或目的地(站点或航路点)的轨道上没有其他车辆时,车辆才能继续行驶。在单向模式下,载具只能沿一个方向通行。
- 预信号灯: 如前方轨道区域(三个连续信号灯之间或通往行程表下一目的地)没有其他车辆占用,车辆即可通行。在单向模式下,载具只能沿一个方向通行。
- 选择信号灯: 在多站台站点处,引导车辆驶向空站台:通过此信号灯的车辆可使用其下一目的地的任意空站台,而不仅限于其行程表上指定的站台。如未找到空站台或通往下一目的地的畅通路线,则车辆在信号灯处等待。
{提示: 使用摧毁/移除功能移除信号灯。按住 [Ctrl] 键可在低洼轨道的桥梁上放置信号灯。}

单轨列车仓库: 该工具用于建造单轨/磁悬浮车辆及车厢的仓库。仓库需要维护费用,并且在游戏视图中建造在单轨/磁悬浮轨道的末端。
建造单轨列车仓库的方式: 点击工具(将光标变为仓库),然后点击轨道末端。

火车站台: 这些工具用于建造车站站台,供单轨/磁悬浮车辆接送乘客和邮件。
车站站台未与现有站点相邻建造时,此操作将创建一个新的站点。
火车站台需要维护费用,并且建在轨道上(但不能在轨道的弯道和交叉口处)。
火车站台设有货物、乘客和邮件的集散区域。不同的站台对货物、乘客和邮件的容量不同。在某些工具选项的角落有一个图标(用于车站列表站点信息)显示该站台允许站点处理哪些对象物。
建造站台的方式: 点击工具进行选择(将光标更改为平台),然后点击轨道。
{提示:使用摧毁/移除功能移除火车站台。扩展站台并建造多站台停靠站(通过在相邻轨道上建造更多站台段)以容纳更长、更多的车辆,从而增加运力和覆盖范围。按 [v] 键可在游戏视图中显示/隐藏货物和乘客的覆盖范围。按住 [Ctrl] 键可在桥梁或高架轨道上建造,以高于下方轨道。}

单轨列车基础设施: 该工具可建造站点的扩展建筑,并增加站点的容量。单轨列车基础建在单轨/磁悬浮轨道的末端,需要维护。
建造单轨列车基础设施的方式: 单击工具进行选择(光标变为地基),然后在轨道的末端单击所需位置。
{提示: 使用摧毁/移除命令移除单轨铁路基础设施。}

simutrans-124.5/simutrans/text/cn/new_world.txt000066400000000000000000000112241520675460500217360ustar00rootroot00000000000000创建新游戏

创建新游戏

创建新游戏窗口用于设置新游戏的选项。这将使用所选设置创建一个新地图。你可以配置许多游戏变量,包括地形大小、初始城市区域数量和产业数量。创建新游戏窗口中还会显示所选地形的预览。

此窗口打开时,所有其他游戏选项都将被禁用。要关闭创建新游戏窗口返回当前游戏,单击标题栏上的 x 按钮。

箭头按钮用于调整设置,按钮用于切换开/关选项或打开新控件。

地图编号 - 选择游戏中使用的地形。
点击数字框并输入所需的地图编号,或使用箭头按钮切换地形。预览区域会显示所选地形在游戏中的显示效果(水体为蓝色,低洼地带颜色比高地深)。

尺寸 - 设置地图尺寸,格式为宽 x 高。上方的数字框表示宽度,下方的数字框表示高度。地图所需的内存显示在括号内。
注意: 大型地图需要更多内存,生成时间也更长。游戏和其他图形在 pak64 图形包下需要额外 88MB 内存,在 pak128 图形包下需要额外 144MB 内存。一般来说,在配备 256MB 以内内存的计算机上,不要尝试运行大于 512 x 512 方格的地图。

随机地图 - 随机选择新的地形。其他设置不变。

加载高度图 - 加载自定义地形。打开加载高度图窗口。

城市数量 - 设置游戏中城市区域的初始数量。如地图太小或地形过于崎岖,则生成的城市区域数量会较少。你可通过切换到公共玩家模式手动添加更多城市区域。

每座城市的人口中位数 - 设置所有城市区域初始人口平均值。模拟交通会尽量生成更多的小村庄,而非大城市。

城际公路长度 - 设置游戏开始时城市区域之间公共道路的最大长度。将此值设置得越高,就可以连接越远的城区。

工厂和商店 - 设置城区外产业供应链的初始数量。随着城市区域人口的增长,更多产业供应链将自动出现。
注意: 如地形多为丘陵,系统会找不到合适的平坦空间以建立产业链,致使出现的产业减少。

旅游景点 - 设置城市区域外,吸引乘客和邮件的地点的初始数量。除游戏开始时新建的城镇或城市生成的景点外,城市区域外还会显示旅游景点

启用年份时间线 - 设置游戏起始年份。点击方形按钮即可启动有时间限制的游戏。随着时间的推移,游戏会出现更多载具、建筑和产业。

如未选择此选项,则游戏中的所有载具、建筑物和产业均可使用。城市区域会显示各个时代的建筑,而速度较慢的载具不如速度较快的车辆盈利能力强,因为收入根据所用运输工具的平均速度计算。

启用新手模式 - 设置此选项可提高游戏中运输货物和乘客的收入。默认情况下,在“新手模式”下,你将获得 150% 普通模式下的收入 。此外,消费者端供应过剩时,产业不会停止供应。

设置 - 打开扩展设置窗口。在这里可访问 simuconf.tab 文件中所有可用的参数。

地形设置 - 打开地形设置窗口,此窗口控制各种地图设置:粗糙度、水位、气候、森林和河流。

加载游戏 - 加载已保存的游戏。这将显示加载游戏窗口,其中包含用于重新开始之前已保存游戏的控件。加载已保存的游戏时,系统将替换当前游戏,无需确认。

加载场景 - 加载一个场景,即一个需要完成特定目标的游戏。这与通常开放式的模拟交通游戏方式有所不同。
注意: 某些图形包无任何场景。

开始游戏 - 点击此按钮即可开始游戏,创建一个新世界,并使用所选设置。祝你游戏愉快!

退出 - 点击此按钮以退出游戏。

提示: 要更改启动模拟交通时的默认值或更改其他全局属性,请修改 simuconf.tab 文件中的值。

simutrans-124.5/simutrans/text/cn/options.txt000066400000000000000000000027431520675460500214370ustar00rootroot00000000000000游戏选项

游戏选项

游戏选项打开游戏的操作和显示控件;从游戏选项退出模拟交通。

点击游戏视图顶部的软盘图标,打开游戏选项

点击按钮以打开新控件:

语言: 设置游戏中显示的语言。

玩家: 打开不同玩家的公司列表。

玩家颜色: 设置玩家公司的颜色。

显示: 模拟交通中游戏显示效果的设置。

声音: 游戏中声音和音乐的设置。

新游戏: 打开控件以开始新游戏(会同时关闭当前游戏中的其他工具栏、文本和选项)。

加载游戏: 打开控件以重新开始已保存的游戏。

保存: 打开控件以保存当前游戏,便于稍后继续游戏。

加载场景: 打开控件以开始玩某个场景,即必须完成某个特定目标的游戏。

场景信息: 通常情况下此功能处于禁用状态,加载场景时会显示当前场景的详细信息。

退出: 退出模拟交通,当前游戏未保存!

simutrans-124.5/simutrans/text/cn/password.txt000066400000000000000000000017751520675460500216120ustar00rootroot00000000000000更改玩家名称和密码

更改玩家名称和密码

更改玩家名称和密码可通过两种方式调用:

可通过点击玩家列表中玩家姓名右侧的方框,或切换到受密码保护的玩家并发出命令。

如玩家名称右侧有绿色方框,则表示该玩家未设置密码保护,你可点击绿色方框并输入密码来认领并设置密码保护。如名称不能选择,你必须输入有效的密码。

你可通过编辑顶行中的文本来更改任何没有密码的玩家的名称,或更改你已通过输入密码解锁的玩家的名称。

玩家列表中,受密码保护的玩家姓名右侧会显示一个红色方框。输入正确的密码后,方框会变成绿色,你就可以使用该玩家进行游戏。解锁密码会保存在本地,因此,只要不重启程序,你只需输入一次密码即可。

simutrans-124.5/simutrans/text/cn/players.txt000066400000000000000000000045521520675460500214230ustar00rootroot00000000000000玩家列表

玩家列表

玩家列表可显示不同玩家公司的信息,并包含不同玩家公司的控制选项。

点击游戏选项中的玩家按钮,或按 [k] 键打开玩家列表,其中显示玩家名称和账户余额

共十四名玩家: 人类玩家、公共服务玩家和十二个空缺位置。这些空缺位置可由其他玩家(人类)或两种类型的人工智能填补。

点击方形按钮即可启用一个玩家。如果该玩家是AI,则可以通过此按钮停止或继续其扩张。在游戏过程中关闭一个AI玩家不会移除其运输网络。

点击箭头按钮即可在当前游戏中扮演该玩家公司。屏幕上会显示玩家已切换的确认信息: 点击信息左上角的 x 或使用键盘关闭信息。

点击球员姓名即可查看其财务信息。如该玩家位尚未分配,你可选择该位置应由哪种类型的玩家占据。要启用此功能,使用最左侧的方框。

玩家可设置密码保护。如果你以受保护玩家的身份进行游戏,那么你可以查看许多统计数据,但不能更改任何与该玩家相关的游戏设置。
受保护的玩家名称右侧会显示一个红色方框。如未设置密码或玩家未锁定,则该方框显示为绿色。点击该方框即可打开密码对话框,你可在其中输入或设置密码。

对于网络游戏而言,玩家之间的合作非常重要。你可将站点设为公开站点。任何玩家都可连接或直接驶入公开站点。此外,你还可通过公开连接板块以共享轨道。通过闸门,可以控制哪些公司可在此类轨道上运行。

玩家不得在其他玩家的轨道或公路上建造对象或放置路标,也不得移除其他玩家建造的对象(但是,公共服务玩家建造的一些物品可用摧毁/移除工具移除)。

底部有一个选项可启用自由游玩模式(若公共玩家未被锁定)。在此模式下,你永远不会破产。否则,若公司价值为负,玩家将被淘汰,该玩家位将在一年后解锁。

simutrans-124.5/simutrans/text/cn/privatesign_info.txt000066400000000000000000000010101520675460500232740ustar00rootroot00000000000000私人道路入口大门

私人道路入口大门

通过此对话框,你可禁止所有未选中的玩家载具通过此闸门进入你的通路(公共玩家设置将影响城市车辆。)获准通行的玩家将看到敞开的闸门或路障,而被禁止通行的玩家将看到关闭的闸门或路障。

此标志由其中一个建造工具栏(例如铁路工具道路工具)建造。

simutrans-124.5/simutrans/text/cn/railtools.txt000066400000000000000000000203011520675460500217420ustar00rootroot00000000000000铁路工具

>铁路/火车/轨道工具

铁路工具用于建造铁路运输网络。这些工具可建造:铁轨(并进行电气化改造或去电气化改造);桥梁;隧道;信号灯;仓库;站台;以及扩展建筑。若启用时间线模式,随着时间的推移,游戏中会出现更多工具选项。

点击游戏画面顶部的火车图标打开工具栏。将鼠标光标悬停在工具选项上(打开或点击工具栏后),即可查看名称以及(如适用)以下信息:建造成本、括号内的维护成本、最高速度限制和最大长度。

铁路工具从左至右依次包括:

铁轨: 工具用于在两点之间建造轨道,供铁路车辆通行。轨道只能沿坡向在斜坡上建造,不能穿越崎岖地形、水域和障碍物。新建轨道时,可利用其路径上现有的轨道。
建造轨道的方式: 点击工具选择轨道(光标变为轨道),然后点击游戏视图选择轨道的起点(游戏视图中会显示推土机,游戏视图底部栏右侧会显示地图坐标),最后点击游戏视图选择轨道的终点。
{提示: 不同类型的轨道可连接(但由其他玩家建造的轨道不能连接)。使用桥梁和隧道连接崎岖地形上的轨道,或避开障碍物。使用地形工具改变地形,为轨道开辟路径。使用“破坏/移除”工具移除单个轨道部件和部分障碍物。同时按下 [Ctrl] 键可执行更多功能。按 [t] 键选择上次使用的轨道工具。}

电气化改造轨道: 此工具可将游戏画面中两点之间的轨道、桥梁和隧道电气化,以便电动车辆通行。
电气化改造轨道的方式: 点击工具或按 [e] 键(将光标变为电轨图标)进行选择,然后在游戏视图中点击轨道作为电气化的起点(在游戏视图中放置一个电轨图标),最后点击轨道上的第二个点,将轨道的一部分电气化至起点。
{提示: 使用摧毁/移除功能将轨道切换回非电气化轨道。}

移除轨道: 该工具会在游戏画面中两点之间移除轨道和电力轨道(两点之间没有车辆时)(请注意:路径上的站台、信号灯、隧道和桥梁也会被移除)。使用该工具会产生建设成本
移除轨道的方式: 点击工具(光标变为红色十字);然后点击轨道(选择游戏视图中红色十字显示的删除点);最后点击轨道上的第二个点,删除两个删除点之间的部分。
{提示: 以另一名玩家的身份行动,移除他们的轨道。}

铁路桥: 在游戏画面中,此工具用于在两段轨道之间建造直线桥梁,供轨道车辆通行。桥梁有最大跨度限制。
此工具可建造桥梁,将轨道的一端连接到合适的地方(轨道的另一端或稍高一些的陆地,在跨度范围内)。
建造铁路桥的方式: 点击工具进行选择(光标变为桥梁),然后点击轨道末端(桥梁起点)进行建造。
有些障碍物、缺少合适的桥梁末端位置这两种因素会阻止桥梁的建造: 在要架桥的两侧铺设轨道,然后重试。
{提示: 用摧毁/移除工具移除桥梁(点击桥梁末端)以及一些障碍物以建造桥梁。用轨道工具将桥梁末端连接到轨道上。}

铁路隧道: 该工具可在游戏视图中建造一条直线隧道,连接两段轨道,供轨道车辆穿过陆地。隧道不能倾斜:车辆进出隧道的高度相同。
该工具用于在斜坡上的轨道末端建造隧道。建造隧道的方法:点击该工具(光标会变成隧道),然后点击斜坡上的轨道末端作为隧道入口。
若找不到合适的出口(没有障碍物的斜坡),不会建造隧道:在要连接的两侧放置轨道,然后重试。
{提示: 用地形工具和摧毁/移除工具创建适合隧道入口和出口的位置。用轨道工具将隧道出口连接到轨道。}

铁路信号灯: 此工具在游戏视图中为轨道上的铁路车辆建造信号灯。信号灯引导和调节轨道、桥梁、交叉口和站点上的车辆流量。
可建造双向和单向信号灯。要在轨道上建造双向信号灯,需进行以下操作: 点击工具选择信号灯(光标会变为信号灯);然后点击轨道。要创建单向信号灯:再次点击同一点,光标仍为信号灯,即可循环切换单向信号,再切换回双向信号。
重要: 注意不要设置单向信号灯,以免车辆不能到达目的地;默认情况下,车辆靠右行驶(可在 simuconf.tab 中更改)。
如你在单击信号灯时按住 Control 键,则会激活多信号灯建造器
- 信号灯: 只有在前方通往下一个信号灯或行程表中目的地(站点途经点)的轨道上没有其他车辆时,车辆才能通行。在单向模式下,车辆只能沿一个方向行驶。
- 铁路关闭: 车辆不会越过信号灯(仅在单向模式下可用)。
- 预信号灯: 如前方轨道区域(三个连续信号灯之间或通往行程表中的下一个目的地之前)没有其他车辆占用,则车辆可以通行。在单向模式下,车辆只能沿一个方向通行。
- 选择信号灯: 在多站台站点引导车辆驶向空站台:通过此信号的车辆可使用其下一目的地的任何空站台,而不仅限于其行程表中指定的站台。如未找到空站台或通往下一目的地的畅通路线,车辆会在信号灯处等待。
{提示: 用摧毁/移除功能移除信号灯。按住 [Ctrl] 键可在低洼轨道的桥梁上放置信号灯。}

火车仓库: 该工具用于建造列车和车厢的采购和管理仓库。仓库需要维护费用,并且在游戏视图中建造在轨道末端。
建造火车仓库的方式: 点击工具(将光标变为仓库),然后点击轨道末端。
{提示: 用摧毁/移除命令移除仓库。仓库仅在位于电力轨道上时才会显示电力车辆。}

铁路站台: 工具用于建造车站站台,供铁路车辆接送货物、乘客和邮件。
车站站台未与现有站点相邻建造时,此操作将创建一个新站点。
铁路站台需要维护成本,并且建在轨道上(但不在轨道的弯道和交叉口处)。
铁路站台设有货物、乘客和邮件的集散区域。不同的站台对货物、乘客和邮件的容量不同。在某些工具选项的角落有一个图标(用于车站列表和站点信息),该图标会显示该站台允许站点处理哪些类型的货物、乘客和邮件。
建造车站站台的方式: 点击工具进行选择(将光标更改为平台),然后点击轨道。
{提示: 用摧毁/移除功能移除铁路站点。延长站台并建造多站台停靠站(通过在相邻轨道上建造更多站台段)以容纳更长、更多的车辆,从而增加运力和覆盖范围。按 [v] 键可在游戏视图中显示/隐藏货物和乘客的覆盖范围。按住 [Ctrl] 键可在低洼轨道上的桥梁上建造站点。}

扩展: 此工具为站点建造扩建部分和建筑物,这会增加车站容量、覆盖范围和维护成本。
在某些工具选项的角落有一个图标(也用于站点列表和站点信息),此图标显示该扩展建筑允许站点处理哪些项目。
建造扩展建筑的方式: 点击工具进行选择(光标变为扩展),然后在游戏视图中点击现有站点旁边所需的位置。新扩展现在已成为站点的一部分。
{提示: 用摧毁/移除工具移除扩展建筑。}

simutrans-124.5/simutrans/text/cn/removal_tool.txt000066400000000000000000000041541520675460500224440ustar00rootroot00000000000000移除工具

摧毁/移除工具

移除工具用于在游戏视图中摧毁和移除单个游戏图块上不需要的对象和施工障碍物。

使用方式: 点击游戏画面顶部的红色十字图标(pak128 中为推土机图标)或按下 [r] 键打开移除工具(光标会变为红色十字覆盖尘土云)。将光标定位在游戏画面中,黄色轮廓线显示工具将要操作的位置(地图坐标显示在底部栏)。然后点击游戏画面中的物体即可将其移除。如果你拥有移除权限,该对象将从地图中移除,且不会发出任何警告。

游戏画面中会立即显示费用,财务窗口中的建造费用项下也会显示相关费用。移除物品的费用与建造费用相同(针对玩家物品)。摧毁城市建筑的费用与使用检查工具打开的对话框中显示的建筑价值相同。铲除树木也需花费一定的费用。

移除工具也可用于移除单个行人和私人城市汽车。

从单个道路/轨道地块中移除物品的顺序: 行人或私人城市汽车、信号灯、站点/车站站台、路径的电气化设施、桥梁或隧道、路径本身(如果是轨道在道路之上,则先移除轨道)。

提示:
- 要移除玩家载具,请从编队详情窗口出售/退役该载具。
- 要移除某个产业、景点或市政厅,先切换到公共服务玩家。
警告: 拆除市政厅时,该城市区域内的所有城市建筑也会被拆除。
- 要摧毁/移除其他玩家的对象,先选择以其他玩家的身份进行游戏。
- 要移除轨道或道路的一段,使用移除轨道工具(也可在有轨电车工具中找到)或移除道路工具。

simutrans-124.5/simutrans/text/cn/roadtools.txt000066400000000000000000000211571520675460500217520ustar00rootroot00000000000000道路工具

道路工具

道路工具用于建设货物和客运的道路运输网络。这些工具可建造或移除:道路、桥梁、隧道、信号灯、仓库、扩展建筑和站点。如启用了时间线模式,随着时间的推移,模拟交通中会出现更多工具选项。

点击游戏画面顶部的卡车图标打开工具栏。
将鼠标光标悬停在工具选项上(打开或单击工具栏后)即可查看各项对应名称:施工成本、括号内的维护成本、最高速度限制和最大长度。

道路工具从左至右依次包含:

道路: 此工具用于在两点之间修路,供公路车辆通行。道路不得修建在其他建筑物占据的区域。道路只能沿坡向修建,不得跨越崎岖地形或水域。新建道路可利用其路径上的现有道路。
修路方式: 点击工具选择道路类型(光标变为道路),然后在游戏视图中点击道路起点(游戏视图中显示推土机,游戏视图底部栏右侧显示地图坐标),最后在游戏视图中点击道路终点。
{提示: 不同类型的道路即使由不同的玩家建造,也可以连接起来。使用桥梁和隧道连接崎岖地形上的道路,或避开障碍物。用地形改造工具改变地形,为道路开辟路径。用摧毁/移除功能移除单个道路段和部分障碍物。同时按下 [Ctrl] 键可执行更多功能。撤销([z] 键)操作不会返还建造费用。按 [s] 键选择上次用的道路工具。}

拆除道路: 在没有车辆时,该工具会在游戏画面中两点之间移除道路(路径上的路障、信号灯、隧道和桥梁也会被移除)。用该工具会产生建设成本
拆除道路的方式: 单击工具(光标变为红色十字);然后单击要删除的路段(在游戏视图中选择红色十字显示的删除点);最后单击路段上的另一个点,以删除到第一个删除点为止的路段。
{提示: 以另一名玩家的身份行动,移除他们的道路。}

公路桥: 此工具用于在游戏画面中连接两段道路,建造供车辆通行的直线桥梁。桥梁有最大跨度限制。工具可以将桥梁从道路的一端建造到合适的位置(例如另一处道路的尽头或略高的地势,但必须在桥梁跨度范围内)。
修桥方式: 点击工具选择桥梁(光标会变为桥梁),然后点击道路的尽头(桥梁的起点)。有些障碍物、缺少合适的桥端位置这两种因素会阻止桥梁的建造:在需要跨越的两侧建造道路,然后重试。
{提示: 用摧毁/移除工具移除桥梁(点击桥梁末端)以及一些障碍物以进行桥梁建造。用道路工具将桥梁末端连接到道路。}

公路隧道: 该工具在游戏画面中连接两段道路,建造一条笔直的隧道,供车辆穿过陆地。 隧道不能倾斜: 车辆进出时高度相同。
该工具用于在坡道上的道路末端建造隧道。建造隧道的方法:点击工具(光标变为隧道图标),然后点击坡道上的道路末端作为隧道入口。若找不到合适的隧道出口(无障碍物的坡道),则不能建造隧道:在需要连接的两侧铺设道路,然后重试。
{提示: 使用地形工具和摧毁/移除工具创造适合做隧道入口和出口的位置。用道路工具将隧道出口连接到道路。}

道路信号灯和路障: 该工具可在游戏视图中为道路车辆建造信号灯和路障。信号灯和路障用于引导和调节道路、桥梁、交叉路口和站点(货物和乘客上下车地点)的车辆通行。部分信号灯可建造双向和单向版本。
重要: 注意不要设置妨碍车辆到达目的地的信号灯,默认情况下车辆靠右行驶(可在 simuconf.tab 中更改)。
- 交通灯: 该工具可在游戏视图中建造信号灯,用于调节路口车辆的通行,使其通行或等待一段时间。点击该工具进行选择(光标将变为信号灯),然后点击道路进行建造。在路口再次点击可更改信号灯的方向。
- 单侧路障: 该工具用于设置路障,以阻止车辆使用道路的某一侧。点击该工具进行选择(光标会变为障碍物),然后点击道路进行设置,再次点击同一位置即可更改路障适用的道路侧。
- 道路封闭: 该工具用于设置路障以阻止车辆通行。点击该工具进行选择(光标将变为路障),然后点击道路进行设置。
- 禁止驶入: 该工具用于设置信号灯,阻止车辆在道路的某一侧通行(类似于单侧路障)。点击该工具进行选择(光标会变为信号灯),然后点击道路进行设置,再次点击同一位置即可切换信号灯的放置方向。此外,还有一种名为私人通道入口闸门的建筑,这种闸门仅允许特定玩家的车辆通行。
- 最低80km/h: 该工具用于建造限速信号,车辆最高时速低于 80 公里/小时时,该信号会阻止车辆通行。点击该工具进行选择(光标会变为信号),然后点击道路进行建造,再次点击同一位置可更改信号适用的道路一侧。
- 选择点: 该工具建造一个信号灯,引导公路车辆前往空闲的公路站点,以便在设有多个公路站点的站点接送货物和乘客:经过此信号灯的车辆可使用其下一目的地的任何空闲公路站点,而不仅限于其行程表中指定的站点。若未找到空闲的公路站点或通往下一目的地的畅通路线,则车辆将在信号灯处等待。单击该工具进行选择(光标将变为信号灯),然后单击道路进行建造,再次单击同一位置可更改信号灯的朝向为适用道路的方向。
- 高速公路: (仅在 pak128 图形包中可用)工具建造一个信号灯,该信号灯只允许最高速度限制超过 80km/h 的车辆在道路上沿一个方向行驶。
点击工具进行选择(光标变为信号灯),然后点击道路进行建设,再次点击同一位置可改变信号灯所适用的道路方向。
{提示: 用摧毁/移除命令移除信号灯。用 [Ctrl] 键在横跨低洼道路的桥梁上放置信号灯。}

道路仓库: 该工具用于建造一个仓库,以便购买和管理公路车辆及拖车。仓库需要维护费用,并且在游戏视图中建造在道路的尽头。
建造道路仓库的方式: 点击工具(将光标变为仓库),然后点击道路尽头。
{提示: 用摧毁/移除命令移除仓库。}

停车场: (仅在 pak128 图形包中可用)此工具为站点建造扩展建筑,从而增加站点对货物和乘客的容量和覆盖范围,以及维护成本。
建造扩展建造的方式: 点击工具选择扩展(光标会变为扩展),然后在游戏视图中,在道路上点击现有站点旁边所需的位置。新扩展建筑现在成为站点的一部分。
{提示: 用摧毁/移除命令移除扩展建筑。}

公路停靠站: 此工具建造为公路车辆接送货物和乘客的场所。
公路停靠站未建在现有站点旁边时,此操作将创建一个新的站点。
公路停靠站建在道路上,需要维护费用,并具有一定的货物、乘客和邮件服务范围。在某些工具选项的角落有一个图标(用于站点列表和站点信息),该图标显示公路停靠站允许站点处理哪些对象。不同的公路停靠站具有不同的货物、乘客和邮件处理能力。
建造公路停靠站的方式: 点击工具进行选择(光标变为公路停靠站),然后在游戏视图中点击道路上所需的位置。
{提示: 用摧毁/移除功能移除公路停靠站。按 [v] 键可在游戏视图中切换货物和乘客的覆盖范围。公路停靠站不能放置在其他玩家已建造的道路上。按 [Ctrl] 键可在低洼道路的桥梁上建造公路停靠站。创建可容纳多个车辆的停靠站,以增加容量和覆盖范围。}

simutrans-124.5/simutrans/text/cn/save.txt000066400000000000000000000034721520675460500207020ustar00rootroot00000000000000<strong>存档</strong>

存档:把当前游戏记录到文件中(为了可以在以后继续进行游戏)& 这些文件可以被删除。

存档打开游戏选项或者按[S]键;& 列出游戏的名称,日期&之前游戏的时间。

如果所有的游戏都看不见;使用右边的滚动条将名字滚出。

当前游戏可以被储存打一个新的文件或者覆盖一个已经存在的游戏文件。

为了储存游戏到一个新的文件,请在存档键上方的盒子里填写保存文件的名称,输入名称&按回车键或者返回键或者点击OK; 新的文件名称被列出来。

注意:如果列出的文件名称是使用过的:以前的文件内容被当前的游戏文件内容替代,&以前的内容丢失。

默认当前游戏出现在文件名称盒子里,点击OK 保存这个文件(以前的数据被当前数据替代被丢失)

为了将以前的游戏有当前的游戏文件覆盖,点击文件列表中的文件名(以前的游戏将要被当前的游戏文件替代)

文件名可以是一个由字母,数字,符号组成的最大不超过57个字符。保存后(最多)只有20个字符可以被现实在保存文件&装载文件中。

警告:通过文件名点击X键游戏将被立即有就删除,文件名将被移除列表。使用此功能要小心。

{提示:从载入游戏中重新开始一个已经被保存过的游戏,

改变自动保存{自动游戏保存}到simuconf.tab文件中}。

点击取消键用来关闭存档键;或者点击左上角的X;或者使用键盘关闭。

simutrans-124.5/simutrans/text/cn/scenario.txt000066400000000000000000000003221520675460500215360ustar00rootroot00000000000000剧本帮助

剧本

Simutrans支持剧本模式. 在剧本中, 必须完成一定的目标: 连接某个工厂, 迅速变富, 或建造一个不朽的总部.

simutrans-124.5/simutrans/text/cn/schedule.txt000066400000000000000000000100751520675460500215350ustar00rootroot00000000000000行程表

行程表控件

行程表控件为编队或路线设置行程表(编队编队行进所需的路线和最低货物及乘客数量)。
此控件列出了停靠站(编队在此接送货物和乘客)和途经点(如有多条可用路径,则可用于引导编队前往某条路径或提供中间目的地点),以及它们的地图坐标。

行程表控件可通过编队信息仓库控件中的行程选项按钮打开。
可通过线路管理仓库控制中的新建线路和更新线路选项按钮打开行程表路线的控件。

从编队信息打开行程表控件时,分配的线路(如有)将显示在控件的顶部。
线路名称: 按名称点击左/右箭头按钮可在线路间循环切换,或点击名称框打开线路下拉列表(点击下拉列表中的线路进行选择)。
点击名称框并输入新名称以更改线路名称。

等待: 设置编队出发所需的最低货物和乘客数量。 该百分比数字指的是编队的总容量,而不是所运载的某类物品的容量。
编队将在站点等待,直到车上人数达到规定比例。
操作方式: 点击列表中的站点进行选择(> 表示已选中的站点),然后使用箭头按钮循环浏览百分比。
{提示: 要设置或更改线路上所有编队的等待级别,用线路管理仓库控件中的更新线路选项按钮。要设置或更改某个编队(且不影响指定线路上的其他编队)的等待级别,用编队信息或仓库控件中的行程选项按钮。}

选项按钮(点击使用,选中时按钮会缩进)包括:

添加站点: 通过此选项可设置一个站点,供载具接送货物和乘客(水上运输工具可使用码头集水区内的任何水域)或设置路线上的途经点。
添加站点或途经点的方式:点击相应选项进行选择(光标变为停止箭头),然后在游戏视图中点击所需设置的站点或途经点位置(根据载具类型,选择道路、轨道或水域上的相应位置)。该站点或途经点现在会显示在行程表控件中。
{提示: 载具不会在途经点接送货物和乘客。}

插入: 此选项会在路线上现有站点或途经点之前放置一个站点/途经点。
插入站点或途经点的方式:单击列表中现有的站点/途经点进行选择(按名称 > 表示已选中站点/途经点),然后单击插入选项按钮(光标变为停止箭头),然后在游戏视图中单击所需设置的站点或途经点位置(根据载具类型,找到在道路、轨道或水面上的相应点)。
新的站点或途经点列在行程表控件中,位于先前选择的站点/途经点之前。
{提示: 载具不会在途经点接送货物和乘客。}

移除: 此选项会从路线中删除站点或途经点。
点击选项按钮选中,然后点击列表中的项目以移除。

向后复制: 此选项会按相反顺序将现有的站点和途经点添加到线路中。等待级别也会被复制。点击选项按钮即可使用:行程表控件列出新线路。

提升至路线: (仅在从编队信息打开行程表控件时可用)用当前行程创建一个新线路。新线路将分配一个编号并列入线路管理中。

simutrans-124.5/simutrans/text/cn/script_generator.txt000066400000000000000000000017651520675460500233210ustar00rootroot00000000000000另存为脚本工具

另存为脚本工具

在这里,你之前通过拖动区域选择的所有建筑物和对象都会保存为一个脚本。稍后运行该脚本时,它会构建所有这些建筑物和对象。保存脚本之前,你必须为其命名。或者,你可点击下方列出的现有脚本之一并覆盖它。
你可通过在不再需要的脚本前面放置 X 以删除它们。

点击确定保存新的脚本工具。

你可在个人文件夹的 simutrans 目录中找到已保存的工具,该目录下的 save 子目录中也包含已保存的游戏状态。在 Windows 系统中,该目录位于“我的文档”中。在 Linux 系统中,该目录可能隐藏在“/home/[user]/.simutrans”中。
在这个个人目录中,它存储在 simutrans/addons/[Grafikset]/tool/ 中。
可使用常用编辑器查看和编辑脚本。其结构非常容易理解。

simutrans-124.5/simutrans/text/cn/script_tool.txt000066400000000000000000000004471520675460500223040ustar00rootroot00000000000000加载脚本工具

此窗口列出了用 Squirrel 编写的、可扩展模拟交通游戏功能的工具。
这些工具位于 simutrans/[Graphics Set]/tool/ 目录的子目录或 simutrans/addons/[Graphics Set]/tool/ 目录。

simutrans-124.5/simutrans/text/cn/server.txt000066400000000000000000000022511520675460500212440ustar00rootroot00000000000000游戏信息 / 服务器

游戏信息 / 服务器

游戏信息 / 服务器将显示游戏的当前运行状态,包括日期、地图大小、公司数量、城镇数量、市民数量、工厂数量、正在运行的载具数量等等。

若你当前玩的游戏不是在线模式,则会显示一个下拉列表,其中包含其他可连接的服务器。若此列表为空,则表示你选择的 pak 图形包版本或程序版本无可用的服务器。你可点击显示全部按钮查看所有现存服务器。

选择服务器后,你将看到关于服务器状态的游戏信息概览。(若服务器宕机,可能需要一些时间才能确认。)概览中还会显示服务器的 Pak 图形包版本和程序版本。若两者都显示为黑色,则表示你的 pak 图形包版本和程序版本与服务器匹配,你可点击在线游玩按钮加入游戏。

若 pak 图形包不匹配,该条目将显示为红色。你可点击比较 pak 按钮,获取有关缺失、不同或多余 pak 图形包文件的报告(这需要一些时间)。

simutrans-124.5/simutrans/text/cn/settings.txt000066400000000000000000000011761520675460500216030ustar00rootroot00000000000000扩展设置

扩展设置

通过扩展设置可编辑中央配置文件 simuconf.tab 以修改游戏引擎的几乎所有参数。因此,所有参数的名称都与其在 simuconf.tab 中的对应名称完全相同,你也可在此文件中找到这些参数的文档说明。

若模改了现有的游戏,你需要保存并重新加载才能应用设置。某些设置(例如工厂连接)只会影响新创建的对象。

注意:
不适当的参数设置很容易损坏游戏,或毁掉正在进行的游戏。

simutrans-124.5/simutrans/text/cn/shiptools.txt000066400000000000000000000121621520675460500217640ustar00rootroot00000000000000码头/港口工具

码头/港口工具

码头/港口工具用于建造海洋和水路运输网络。这些工具可建造或移除:运河、船闸、各种码头和船舶仓库。如启用时间线模式,随着时间的推移,模拟交通中会出现更多工具选项。

点击游戏画面顶部的船形图标以打开工具栏。
将鼠标光标悬停在工具选项上(打开或单击工具栏后),即可查看对应的名称:建设成本、括号中的维护成本和最高速度限制。

码头/港口工具从左至右包含:

运河: 该工具可在游戏画面中的两点之间建造水道,供船只和驳船通行。
运河只能沿坡向在斜坡上修建,不能穿越崎岖地形。在两点之间修建的运河可利用沿途已有的运河。
运河建造方式: 点击工具进行选择(光标变为运河),然后点击地形作为起点(游戏视图中显示推土机,游戏视图底部栏右侧显示地图坐标),最后点击地形作为终点。
{提示: 不同玩家建造的运河不能相互连接。用地形工具来改变地形,以便运河可以通行。用摧毁/移除功能可移除运河的单个部分和一些障碍物。撤销上次的路线连接([z] 键)不会退还建造费用。}

移除运河: 该工具可在游戏画面中两点之间移除一段没有载具的运河(注意:路径上的运河障碍物也会被移除)。用该工具会产生一定的建造费用。
移除运河的方式: 点击工具(光标变为十字形);然后点击要删除的运河(在游戏视图中选择用红色十字标记的删除点);最后点击运河上的另一个点,删除到第一个删除点为止的部分。

船舶升降机: 该工具用于建造运河船闸和桥梁,以便在游戏视图中将运河上的船只或驳船升降一级。桥梁具有最大跨度,并建造在运河的末端,连接到合适的位置(另一个运河末端或跨度范围内的稍高一些的陆地)。
建桥方式: 点击工具选择桥梁(光标变为升降机),然后点击运河末端(桥梁起点)。有些障碍物、缺少合适的桥端位置这两种因素会阻止升降机的建造:请在需要跨越的运河两侧建造桥梁,然后重试。
{提示: 用摧毁/移除功能移除桥梁(点击升降机末端)以及一些障碍物以建桥。
运河工具将桥梁末端与水道连接起来。}

码头(pak128 中的运河码头): 在游戏视图中,此工具可用于在运河上建造一个码头,供水上运输载具接送货物和乘客。
若码头未建在现有站点旁边,此操作会创建一个新的站点。
码头设有货物、乘客和邮件的集散区域。工具选项的角落里有一个图标(用于站点列表站点信息),显示码头允许站点处理哪些物品。
建造码头的方式: 点击工具(将光标变为通道挡块);然后在游戏视图中点击运河进行建造。
{提示: 用摧毁/移除功能移除码头。按 [v] 键可在游戏视图中显示/隐藏货物和乘客的集散区域。
仅供水上运输载具停靠的站点不会列入站点/车站列表中。}

船舶停靠站: 此工具可用于建造各种对象物,在游戏画面中建造码头,供水上载具接送货物和乘客。
码头建在笔直海岸线的边缘。
若码头没有建在现有站点旁边,此操作会创建一个新的站点。
码头设有货物、乘客和邮件的集散区域。工具选项的角落里有一个图标(用于车站列表和站点信息),显示该码头允许站点处理的对象种类。不同的码头对货物、乘客和邮件的处理能力不同。
建造码头的方式: 点击工具进行选择(光标变为码头),然后点击水边的斜坡进行建造。
{提示: 用摧毁/移除命令移除码头。
码头的面积可通过在相邻的方格上建造建筑来扩大。水上运输工具可使用码头集散区内的任何水域。
仅供水上运输载具停靠的站点不会列入站点/车站列表中。
海上产业,例如石油钻井平台不需要码头,水上运输载具可使用海上产业集散区内的任何水域。
按 [v] 键显示/隐藏游戏视图中的货物和乘客集散区域。}

船舶仓库: 此工具用于建造购买和组装船舶、船只和驳船的仓库。仓库需要维护费用,并且在游戏视图中建造在水面上。
建造船舶仓库的方式: 点击工具(将光标变为仓库),然后在游戏视图中点击水。
{提示: 用摧毁/移除命令移除仓库。}

simutrans-124.5/simutrans/text/cn/signal_info.txt000066400000000000000000000014231520675460500222260ustar00rootroot00000000000000信号灯信息窗口

信号灯信息窗口

信号灯信息。
· 信号灯名称
· 描述: 并非始终可用
· 所有者: 哪个玩家拥有此信号灯
· 方向有效性: 仅在特定方向上有效;其他方向路线均已关闭。
- 1 = 南面也斜向西
- 2 = 西面也斜向北
- 3 = 北面和斜向南
- 4 = 北面也斜向北
- 5 = 南北向
- 8 = 东面也斜向东或向南
- 9 = 沿对角线向东和向西
- 10 = 东西向
- 12 = 沿对角线向东和向西
· 移除信号灯: 将移除此信号灯
· 绘制: 图形设计者姓名

simutrans-124.5/simutrans/text/cn/signal_spacing.txt000066400000000000000000000020371520675460500227210ustar00rootroot00000000000000信号灯间隔

一般用法:


-- 按住 Ctrl 键并点击信号灯图标即可打开此图形用户界面。
-- 在轨道上点击并拖动鼠标即可预览待建信号灯,松开鼠标按钮即可开始建造。建造的信号灯将只允许列车沿你拖动鼠标的方向通行。
-- 在网络游戏中:首先按住 Ctrl 键并点击轨道,然后松开鼠标按钮。在你按住鼠标按钮并移动鼠标时,即可看到预览。再次松开按钮即可开始建造。

此窗口中的参数:

-- 信号灯间隔: 相邻信号灯之间应间隔多少个方格放置(对角线算作半个方格)。若选中信号灯,该值将显示在图标上。
-- 删除中间信号灯: 若启用此选项,路径上的其他信号灯将被删除。
-- 替换其他信号灯: 若启用此选项,我们想要建造信号灯的其他信号灯所在的图块将被删除。

这些参数可针对所有不同的信号灯独立设置。

simutrans-124.5/simutrans/text/cn/simutrans.txt000066400000000000000000000043301520675460500217630ustar00rootroot00000000000000关于模拟交通

欢迎来到模拟交通!

首先,选择一张地图: 你可从模拟交通数千张地图中选择一张,或根据高度图创建新地图。然后,你将扮演一位年轻企业家,继承了祖父母的一笔钱:你渴望让他们感到骄傲,雄心勃勃地想要建立一家成功的运输公司,而你现在的挑战就是打造一个繁荣的运输帝国。

随着时间的推移,将你的公司发展成一家成功的企业,同时通过运输货物和满足数百万旅客的出行需求,确保经济平稳运行。你可通过铁路、公路、船舶甚至航空运输旅客、邮件和货物。

每张地图都有其独特之处。仔细观察你周围的环境:它们的未来掌握在你和你的竞争对手手中。

你可以先从为发电站供应所需物资开始。用赚到的钱,逐步扩大规模,为其他工厂供货——甚至可以利用中央枢纽进行货物转运。只运输货物的缺点是城镇不会发展,新的产业也不会出现。

或者,你可以直接选择客运(和邮件)运输。但这更具挑战性,因为模拟交通游戏世界的居民都希望前往特定的目的地。 有些人需要去工厂上班,有些人想去市中心购物,还有些人想去旅游景点。但如果你的运输网络不提供给乘客们心仪的出行线路,让他们前往自己心仪目的地,乘客就不会乘坐你的载具。

你需要建立一个交通网络,尽可能覆盖更多热门目的地。网络建成后,你需要不断升级改造以满足需求,所以务必提前规划!如果乘客满意你的服务,城镇就会发展,新的产业就会出现,经济才能平稳运行。

你可以随心所欲地玩,从 1880 年开始,到 2050 年结束,或者选择这两个日期之间的任意时间开始。在此期间,游戏会不断推出新的载具和建筑。你可以与 AI 对战,也可以独自游戏。但请注意:如果你的净资产连续三个月为负,你将破产,游戏结束。你可在财务窗口中查看自己的净资产。

模拟交通团队和社区祝你游戏愉快!

simutrans-124.5/simutrans/text/cn/slopetools.txt000066400000000000000000000043431520675460500221450ustar00rootroot00000000000000地形工具 - 抬高/降低土地

地形工具

地形工具 抬高和降低地形,可用于创造合适的施工场所。

点击游戏画面顶部的斜坡图标打开工具栏。打开或点击工具栏后,将鼠标光标悬停在工具上即可查看工具名称和建造成本

工具在无障碍物的区域(例如道路、轨道、建筑物、站点和车站)运行。

点击工具图标选中它,光标会发生变化,然后将光标定位到游戏视图中的地形上,点击即可使用:

抬升土地/降低土地: 抬升/降低地形。 光标是向上/向下箭头。工具也可通过按 [u] 键和 [d] 键打开。

建造人工斜坡: 此操作会创建一个斜坡,方向由工具栏图标指示。此过程会创建一个人工制造的垂直表面。光标是一个黄色立方体轮廓,用于定位斜坡。

抬升图块/降低图块: 升高/降低特定方格(地块)的地形。此过程会生成一个人工制造的垂直表面。光标是一个黄色立方体轮廓,用于选择图块。

恢复自然坡度: 将地形恢复为自然地形,并在此过程中移除人工制造的垂直表面。光标是一个黄色立方体轮廓,用于定位工具。

提示:
- 与网格搭配更易使用地形工具,按下 [#] 键并放大游戏视图。
- 用摧毁/移除工具移除一些障碍物。
- 根据地图的不同,工具可以将陆地降低至水中或从水中抬升出来。
- 将地势降低到一定水平即可形成河道。
- 坡度工具可用于在地下创建隧道边坡。
- 根据你玩的图形包,地形工具工具栏会包含其他工具(通常由公共服务玩家使用): 提升/降低湖泊中的水位,将地面转换为特定的气候带,等等。另参阅游戏和地图编辑工具

simutrans-124.5/simutrans/text/cn/sound.txt000066400000000000000000000026641520675460500210760ustar00rootroot00000000000000声音设置

声音设置

声音设置窗口包含音效和背景音乐的控件。从游戏选项菜单打开。
注意: 为了获得更好的音效,建议在 Windows 系统中玩模拟交通的 SDL 编译版本。

用箭头按钮调整设置,或用滑块减小/增大音量。

音效音量: 设置游戏中的音效音量。可以使用静音按钮完全关闭声音。

模拟交通中许多游戏对象都能发出声音: 环境音效(光标静止数秒后出现在光标位置)、路口音效(车辆接近时)、工厂音效(生产货物时)、载具音效(离开站点/车站时)。具体使用哪些音效取决于你玩的图形包和置入的音效包。

音乐音量: 设置背景音乐音量。用静音 MIDI 音乐按钮可完全关闭音乐。若未播放音乐,控件无效。
音乐曲目(MIDI 格式)存储在 simutrans/music/ 文件夹中。

当前播放: 切换音乐;箭头按钮循环切换可用的音乐曲目。

随机音轨: 音乐曲目随机播放。

注意: 若用命令行选项“-nosound”或“-nomidi”启动模拟交通,游戏不会播放声音或音乐。

有关模拟交通游戏音乐的更多信息,访问 http://wiki.simutrans.com

simutrans-124.5/simutrans/text/cn/special.txt000066400000000000000000000131411520675460500213560ustar00rootroot00000000000000专用建筑工具

专用建筑工具

专用建筑工具可建造与交通无关的对象,还可以让你在游戏中扮演另一个玩家的角色。

在游戏视图顶部,点击黄色圆圈中的红色水滴图标(pak128 图形包中为起重机图标)以打开工具栏。
将鼠标光标悬停在工具选项上(打开或单击工具栏后)以查看对应的名称:施工成本

专用建筑工具从左至右包含:

邮局: 此工具可以建造邮局,使得站点能处理邮件。邮局可提高站点的容量和覆盖范围,但需要维护成本。
建造邮局的方式: 点击工具进行选择,然后在游戏视图中点击现有站点。新的邮局现在成为站点的一部分。
{提示: 用摧毁/移除功能移除邮局和一些障碍物。}

车站扩展: 此工具用于为站点建造扩展建筑物。扩展建筑物可增加货物和乘客的容量和覆盖范围,但也会增加维护成本。在某些工具选项的角落有一个图标(用于站点列表和站点信息),该图标显示扩展建筑物允许站点处理哪些对象物。
建造扩展的方式: 点击工具进行选择(光标会变为扩展),然后在游戏视图中,在现有站点旁边点击所需位置。新创建的扩展现在成为站点的一部分。
{提示: 用摧毁/移除工具移除扩展。}

P+ 更换玩家: 若在游戏开始时选择了允许玩家变更,则系统允许以其他玩家的身份进行游戏。作为公共服务玩家,可以使用额外的选项
点击工具图标(或按 [P] 键)即可切换玩家。切换成功后会显示确认信息:点击信息左上角的 x 或使用键盘关闭窗口。

建立新城市: 此工具用于建立一个新的城市区域
建立新城市的方式: 点击工具或按 [C] 键进行选择(光标会变为市政厅),然后在游戏视图中点击所需位置。新建城市需要 5 百万元,无需进一步确认即可建造。
{提示: 在游戏视图中点击市政厅,选择摧毁/移除工具即可移除新的城市建筑。}

植树: 该工具用于在游戏画面中放置一棵树。
植树方式: 点击工具进行选择(光标变为树状),然后点击所需位置。
{提示: 用摧毁/移除命令移除树木。}

建设输电线路: 该工具用于建造输电线路,为发电厂的变电站和产业之间供电(以提高生产力)。电力供应带来收入。这些输电线路不能与轨道和道路交叉。
建设高压输电线路的方式: 点击工具或按 [l] 键选择(光标变为电力线),然后点击位于发电站旁边的变电站,最后点击产业中的变电站,为其供电以提高产量。

变电站: 该工具用于在发电厂或产业建造变电站,以连接到高压输电线路。
建造变电站的方式: 点击工具或按 [g] 键选择(光标变为黄色圆圈中的红色水滴),然后在游戏视图中点击产业或发电厂旁边的所需位置。
建在发电厂的变电站必须通过输电线路与产业的变电站连接,才能输送电力。
若无供电,游戏画面中的变电站会显示黄色闪电;若有供电,则会显示红色闪电。
{提示: 产业和发电厂可拥有多个变电站以增加电力供应。用摧毁/移除功能移除变电站。}

制作标志: 该工具可打开控件,以在游戏视图中放置带有个性化文本的标志,并可用于将游戏视图移动到不同的位置。
点击工具进行选择,或按 [M] 键,光标将变为文本标记,然后在游戏画面中点击所需位置,以打开制作标志控件。在制作标志控件顶部会显示所选位置的地图坐标和一个空白文本框,在文本框中输入所需文本(点击文本框,输入文本,然后点击确定或按 [Enter] 或 [Return] 键)即可放置标志。新标志会显示在游戏画面中,并在重新打开制作标志控件时显示在控件列表中(包含地图坐标)。
将游戏视角移动到标志点: 打开制作标志控件,然后单击列表中所需的位置。
移除标志: 点击制作标志工具或按 [M] 键选择,光标将变为文本标记;然后点击游戏视图中标志的坐标;然后在制作标志控件中点击移除
关闭制作标志控件: 点击控件左上角的 x;或点击取消;或用键盘。
{提示: 光标的地图坐标显示在游戏视图下方底部栏的右侧。按 [!] 键可切换游戏视图中标志的文本显示。}

更换站点: 此工具可用于替换行程表中使用该站点的所有编队和线路中的某个站点。它还可用于更改站点内的具体停靠站(例如,将所有线路移至火车站的不同站台)。选择此工具后,单击要从行程表中移除的站点(或站点的一部分),然后单击将来要使用的站点(或站点的一部分)。之前使用第一个站点的所有编队现在都将使用第二个站点。根据你的行程表设置,在更改期间,前往第一个站点的编队可能会受到一些影响。

simutrans-124.5/simutrans/text/cn/station.txt000066400000000000000000000154471520675460500214320ustar00rootroot00000000000000站点信息

站点信息

站点信息包含有关站点(载具在此接送货物和乘客)的信息。还包含用于访问更多详细信息的控件和更改站点名称的控件。

站点包括编队可接送货物和乘客的地方以及任意扩展建筑物。

用于创建编队接送货物的地点,此工具可在相应运输类型的工具栏中找到:站台单轨/磁悬浮列车站点电车站点公路站点码头;以及空管站
{提示: 水上运输载具可使用码头集散区内的任何一块水域进行货物和乘客的接送。可在相邻的水域上建造更大的码头。码头可容纳多种类型的运输工具。}

在游戏视图中通过检查工具点击站点,也可点击站点列表线路管理中列出的站点,即可打开站点信息,其标题栏中会显示站点的名称。
站点信息包含一个名称框;站点的迷你视图;站点信息;选项按钮;以及站点上所有待运输对象的列表(若所有物品都不可见,调整站点信息选项的尺寸或使用滑块以滚动对象):

名称: 指定的站点名称显示在站点信息顶部的名称框中。
默认情况下,站点名称在首次建造时自动分配,同时取决于与站点相关的城市区域以及站点覆盖范围内的任何产业
改名方式: 点击名称框并输入新名称。
{提示: 按 [!] 键可在游戏视图中切换显示的站点名称。按 [v] 键可在游戏视图中切换显示的站点覆盖范围。对站点进行编号的选项可在 simuconf.tab 中找到。}

状态颜色条: 颜色指示车站的拥挤程度。状态颜色条也用于车站列表和线路管理,其颜色与游戏画面中车站上方颜色条的颜色相同。:
- : 无服务,也没有等待任何对象。
- 绿: 无需改进。
- : 站点略微拥挤(通常是转运对象物)。
- : 若车站客流量超过其容量的1.5倍,或有超过200名不满的乘客,或工厂因车站过度拥挤而被迫停产,这些车站将在月初被报告为过度拥挤。
{提示: 按 [!] 键切换游戏视图中站点上方显示的状态颜色条(指示站点的拥挤程度)。}

图形图标也用于车站列表和线路管理:
- 货运图标指明站点可处理哪些对象(乘客、货物和邮件)。
{提示: 添加合适的扩展设施可改变站点可处理的对象类别。可添加邮局至站点以启用邮件处理功能。}
- 载具图标指示哪些类型的载具可使用站点。图标包括:公共汽车(客运)、卡车(货运)、火车(铁路)、船舶和飞机。有轨电车可根据站点的类型用公共汽车图标或火车图标表示。

存储容量: 站点可容纳的每个对象(货物、乘客和邮件)的数量。

站点图片: 这是站点和任何可见编队的缩略图。
点击图片将游戏画面中心定位到站点。

乘客数量表示从该站点出发的乘客数和邮件数:
- 笑脸: 表示高兴的乘客人数,这些乘客居住在站点覆盖范围内,他们到达不拥挤的站点,找到前往目的地的路线并等待载具。
- 不悦的脸: 表示不高兴的乘客人数,这些乘客居住在站点覆盖范围内,但由于站点过于拥挤,他们没有等待载具。
- 无路: 表示离开的乘客人数,这些乘客居住在站点覆盖范围内,但由于没有找到前往目的地的路线而离开。
{提示: 让乘客满意有助于城市区域的发展。在 simuconf.tab 中更改转运货物和乘客的最大次数。}

货物和乘客在等待: 列出在站点等待运输的物品,并按以下类别分组:乘客;邮件;货物类型。
显示的对象货物信息包括数量、类型、最终目的地和首个中转站。
选项按钮(选中后名称会更改)对组内项目进行排序:
- 目的地: 对项目排序,按 ASCII 码顺序(大写字母在前,小写字母在后),对最终目的地站点的指定名称进行排序。
- 数量: 按数量降序排列项目。
- 通过(详情): 按 ASCII 码顺序(大写字母在前,小写字母在后)对项目进行排序,排序依据是第一个转运站的指定名称。
- 通过(数量): 按数量对运往第一个转运站的物品进行排序。
- 通过(所有者): 按下一的所有者对运载物品的数量进行排序。
- 换乘点: 按乘客的下一个换乘点(无论是直达线路还是无线路的列车)对物品进行排序。优先考虑等待时间最长的乘客。
{提示: 游戏视图中站点上方的货物颜色条(按 [!] 键切换)表示等待运输的对象数量,颜色与对象列表中 颜色方块中的颜色相同。}

图表: 点击选项按钮切换显示站点信息中的图表(图表可见时按钮会缩进)。
选择图表选项时,图表会显示过去 12 个月的统计数据(x 轴)。
点击图表选项按钮查看图表信息(选中选项后按钮会缩进)。
图表上的线条颜色与图表选项按钮的颜色相对应:
- 高兴: 到达站点的乘客/邮件数量,已经找到了前往目的地的路线,并开始了他们的旅程。
- 不悦: 到达站点但因车厢拥挤而未等候交通工具的乘客/邮件数量。
- 无路: 到达站点但因未找到前往目的地的路线而离开的乘客/邮件数量。
- 等待: 站点等待运输的货物、邮件和乘客的数量。
- 到达: 编队在终点站(作为最终目的地或中转点)卸下的货物、邮件和乘客数量。
- 出发: 编队在站点搭载的货物和乘客数量。
- 载具: 已从站点出发的编队数量。

详情: 此选项按钮可打开站点详情,显示有关站点的更多信息。

simutrans-124.5/simutrans/text/cn/station_details.txt000066400000000000000000000027721520675460500231340ustar00rootroot00000000000000站点详情

站点详情

站点详情显示有关站点(车辆在此接送货物和乘客)的更多信息。

站点详情可通过站点信息中的详情选项按钮打开。
若所有项目都不可见,调整站点详情选项的尺寸或使用滑块滚动项目)。

所列信息包括:
连接的工厂: 站点覆盖范围内产业的名称和地图坐标(括号内)。
{提示: 按 [v] 键切换游戏中站点覆盖范围的视图。}

附近产业所需的商品: 站点的覆盖范围内,产业所需的对象物类型。

途经此站点的线路: 经过此站点的线路
线路按 ASCII 码顺序(大写字母在前,小写字母在后)以字母数字顺序排列。

服务于此站点的无线路编队: 未分配到任何线路的编队在此停靠。

从此出发的直达路线: 列出无需换乘即可到达的站点名称以及可交付的对象类型(货物和乘客)。
以 ASCII 码顺序(大写字母在前,小写字母在后),按运输类型、站点名称和可交付对象的类型(每个站点也会显示),对站点进行字母数字排列。

simutrans-124.5/simutrans/text/cn/trafficlight_info.txt000066400000000000000000000010261520675460500234160ustar00rootroot00000000000000交通灯控制

交通灯控制

你可以控制交通信号灯红绿灯的相位频率。此处可设置的数字是南北方向和东西方向绿灯的持续时间(以秒为单位)。

由于所有交通信号灯都会同相切换,因此可在第三个字段中额外增加偏移量,通过持续的交通信号灯,以实现后续交通流在某个方向上的畅通无阻。

交通信号灯由道路施工工具栏建造。

simutrans-124.5/simutrans/text/cn/tramtools.txt000066400000000000000000000174521520675460500217730ustar00rootroot00000000000000有轨电车/轻轨工具

有轨电车/轻轨工具

有轨电车/轻轨工具用于建设有轨电车运输网络。这些工具可建造:有轨电车轨道(并进行电气化或去电气化)、信号系统、有轨电车仓库和有轨电车站。 若启用了时间线模式,随着时间的推移,模拟交通中会出现更多工具选项。

点击游戏画面顶部的电车图标打开工具栏。
将鼠标光标悬停在工具选项上(打开或单击工具栏后)即可查看对应的名称: 施工成本和最高速度限制。

有轨电车/轻轨工具从左至右包含:

电车轨道: 此工具用于建造轨道,供两点之间的轨道车辆通行。
电车轨道可沿道路和在城市区域内修建,但不得建在其他建筑物占据的区域。轨道只能沿坡向修建,不得跨越崎岖地形、水域和障碍物。新建轨道可利用其路径上的现有轨道。
建轨道的方式: 点击工具选择轨道(光标变为轨道);然后点击游戏视图选择轨道起点(游戏视图中显示推土机,并在游戏视图下方的底部栏右侧显示地图坐标);最后点击游戏视图选择轨道终点。
{提示: 不同类型的轨道可连接(但由其他玩家建造的轨道不能互相连接)。通过桥梁 和隧道跨越崎岖地形连接轨道或避开障碍物。用摧毁/移除功能移除单段轨道和部分障碍物。同时按下 [Ctrl] 键可执行更多操作。撤销([z] 键)操作不会返还建造费用。}

电气化轨道: 此工具可对游戏画面中两点之间的轨道、桥梁或隧道进行电气化改造,供电动载具使用。
电气化改造轨道的方式: 点击工具或按 [e](光标变为电轨图标)进行选择,然后在游戏视图中点击轨道作为电气化的起点(在游戏视图中放置一个电轨图标),最后点击轨道上的第二个点,将轨道从此处至起点的部分电气化。
{提示: 用摧毁/移除功能将轨道改回非电气化轨道。}

移除轨道: 在没有车辆时,此工具可用于在游戏画面中两点之间移除轨道和电力轨道,同时移除路径上的站台、公路停靠站、信号灯、隧道和桥梁。使用该工具会产生建设成本
移除轨道的方式: 点击工具(光标变为红色十字);然后点击要删除的轨道(在游戏视图中选择红色十字显示的删除点);最后点击连接的轨道上的第二个点,删除到第一个删除点为止的部分。
{提示: 以另一名玩家的身份行动,移除他们的轨道。}

轨道信号灯: 此工具用于在游戏视图中为轨道上的车辆建造信号灯。信号灯引导和调节轨道、桥梁、交叉口和站点(车辆在此接送货物和乘客)上的车辆流量。
可创建双向和单向信号灯。在轨道上创建双向信号灯的方式:单击工具选择信号(光标变为信号);然后单击轨道。创建单向信号灯的方式:用信号光标再次单击同一点,即可在单向信号灯和双向信号灯间循环切换。
重要: 注意不要设置单向信号灯,以免载具不能到达目的地;默认情况下,车辆靠右行驶(可在 simuconf.tab 中更改)。
- 信号灯: 只有在前方通往下一个信号灯或行程表(一个站点或途经点)目的地的轨道上无其他车辆时,车辆才能通行。在单向模式下,车辆只能沿一个方向行驶。
- 铁路关闭: 车辆不会越过信号灯(仅在单向模式下可用)。
- 预信号灯: 如前方轨道区域(三个连续信号灯之间或通往行程表中的下一个目的地之前)没有其他车辆占用,则车辆可以通行。在单向模式下,车辆只能沿一个方向通行。
- 选择信号灯: 在多站台站点引导车辆驶向空站台: 通过此信号的车辆可使用其下一目的地的任何空站台,而不仅限于其行程表中指定的站台。如未找到空站台或通往下一目的地的畅通路线,车辆会在信号灯处等待。
{提示: 用摧毁/移除功能移除信号灯。按住 [Ctrl] 键可在低洼轨道的桥梁上放置信号灯。}

有轨电车仓库: 此工具可建造一个用于购买和管理电车的车库。
仓库需要维护费用,并且在游戏视图中建在轨道的末端。
建造有轨电车的方式: 点击工具(光标变为仓库),然后点击轨道末端。
{提示: 仓库仅显示位于电力轨道上的电力车辆。用摧毁/移除功能移除仓库。}

电车站台: 此工具用于建造车站站台,供轨道车辆接送货物、乘客和邮件。
车站站台未与现有站点相邻建造时,此操作将创建一个新站点。
站台需要维护成本,并且建在轨道上(但不在轨道的弯道和交叉口处)。
电车站台设有货物、乘客和邮件的集散区域。不同的站台可能具有不同的乘客和邮件容量。在某些工具选项的角落有一个图标(用于车站列表和站点信息),该图标显示该站台允许站点处理哪些类型的货物。
建造车站站台的方式: 点击工具进行选择(将光标更改为平台),然后点击轨道。
{提示: Remove Rail-stops with Destroy/Remove. Extend station-platforms & build multi-platform Stops (by building more platform sections on adjacent Track) to accommodate longer and more vehicles, increase capacity & catchment area. Press [v] to show/hide catchment area for goods & passengers in game-view. Hold down [Ctrl] to build on bridges above lower Tracks.用摧毁/移除功能移除轨道站点。延长站台并建造多站台站点(通过在相邻轨道上建造更多站台段)以容纳更长、更多的车辆,从而增加运力和覆盖范围。按 [v] 键可在游戏视图中显示/隐藏货物和乘客的集散区域。按住 [Ctrl] 键可在低洼轨道的桥梁上建造。}

停车场: (仅在 pak128 图形包中可用)此工具为站点建造扩展建筑,从而增加货物和乘客的容量并增大集散区域,以及维护成本。
建造扩展建筑的方式: 点击工具选择扩展(光标会变为扩展),然后在游戏视图中,点击道路上现有站点旁边的所需位置。新扩展建筑现在成为站点的一部分。
{提示: 用摧毁/移除功能移除扩展建筑。}

公路停靠站: 此工具为有轨电车和公路车辆建造接送货物、邮件和乘客的场所。
若公路停靠站未建在现有站点旁边,此操作将创建一个新的站点。公路停靠站建在道路上,需要维护,并具有一定的货物、乘客和邮件服务范围。在某些工具选项的角落有一个图标(用于站点列表和站点信息),该图标显示公路停靠站允许站点处理哪些类型的对象。不同的公路停靠站具有不同的货物、乘客和邮件处理能力。
建造公路停靠站的方式: 点击工具进行选择(光标变为公路停靠站),然后在游戏视图中点击道路上的所需位置。
{提示: 用摧毁/移除功能移除公路停靠站。按住 [Ctrl] 键可在低洼道路的桥梁上建造。}

simutrans-124.5/simutrans/text/cn/underground.txt000066400000000000000000000033711520675460500222760ustar00rootroot00000000000000地下建筑

地下建筑

模拟交通中有两种可能的地下视图,均可通过显示设置窗口或快捷键访问。

地下视图: 按下 [U] 键,即可立即显示所有地下层,并隐藏所有地上对象。默认情况下会显示地面网格,但在此网格模式下很难将物体放置在所需位置,因此建议使用下一个地图视图进行地下建造。

切片地图视图: 通过 [Ctrl+U],你可选择让模拟交通显示地下部分的某一层。此视图会隐藏所选图层上方的所有对象,无论它们位于地下还是地表。你可在显示设置窗口中调整当前视图级别,也可以使用 pak 图形包某些特有的快捷键进行调整。活动图层的网格图案会显示在视图中。由于鼠标指针只能在当前层级内移动,因此施工操作更加便捷。

一般来说,地下通道用隧道建造工具建造。要建造地下通道,必须先建造隧道或隧道入口(在斜坡上按住 Ctrl 键并点击隧道工具)。

要在地下另一层进行建造,使用地形工具菜单中的建造人工斜坡工具。只需用所需的工具单击道路/轨道的末端即可。

可用常规的有轨电车/轻轨工具在现有道路上铺设地下有轨电车轨道。

只有自有站点/车站才能上下叠加建造。车站的扩建部分不能建在地下。

注意: 如果不能继续在地下建造,请检查地面是否有障碍物。

simutrans-124.5/simutrans/text/cn/vehiclelist.txt000066400000000000000000000012021520675460500222440ustar00rootroot00000000000000全部载具列表

游戏中所有载具类型列表

默认情况下,所有当前和未来即将到货的载具都会显示出来。显示未来载具也是此列表的真正含义。在仓库中,即将到货的载具不可见,因此玩家可更好地规划未来。

显示未来载具: 通过取消选择,列表可限制显示为当前可购买的车辆。
显示已过时车辆: 此选项会展开列表,列出所有已过时的车辆。此功能在仓库中也同样可用。
在这之后,全部选项可限制为货物类型以及乘客或邮件。

simutrans-124.5/simutrans/text/cn/window.txt000066400000000000000000000201041520675460500212420ustar00rootroot00000000000000游戏界面使用

游戏界面

玩家通过游戏界面游玩模拟交通,游戏界面显示当前游戏信息、提供工具栏和游戏控件的访问权限以及当前游戏世界的视图。模拟交通可通过鼠标键盘进行控制。键盘布局可在帮助菜单的键盘绑定部分找到,也可按下 [F1] 键查看,此外,每次按下无功能键时键盘布局也会显示。
重要: 按键区分大小写。

游戏窗口

显示当前游戏世界的一部分。你可使用鼠标滚轮、[PgUp]/[PgDown] 键或主工具栏上的两个专用图标来放大和缩小地图。地图滚动方式有多种:
- 按住鼠标右键,向任意方向滚动地图,直至到达所需位置。默认设置下,地图会逆着鼠标的移动而滚动。你可在显示设置窗口中更改此行为。
- 使用数字键盘。
- 通过选择地图窗口上的相应点,直接跳转到目标地图区域。
- 通过跳转到对话框跳转至特定坐标(按 [J] 键)。

你可编辑多个对象的名称,例如城镇、工厂、车站、载具和运输线路。要进行编辑,在由检查工具(见下图)打开的对话框中,用鼠标单击名称字段。编辑会立即生效。按 [Return] 即可退出该字段。

标题栏

标题栏左侧显示当前运行的模拟交通游戏的版本和发布日期。标题栏右侧包含用于最小化、最大化游戏界面及退出模拟交通的控件。

游戏窗口顶部有一排图标(主工具栏),可用于在玩模拟交通时访问游戏工具栏和游戏控件。这些图标(点击即可使用)分成了多个菜单组中:

主菜单

选项: 软盘图标用于打开一个对话框,你可从中加载、保存或开始新游戏。

地图: 世界概览地图会显示玩家载具的移动轨迹,并显示一些统计信息。选择地图上的某个点,游戏窗口就会跳转到相应的图像区域。

检查工具: 放大镜图标代表查询工具。激活后,光标也会变成放大镜。用它选中一个对象(建筑物、载具等),上方会显示一个信息窗口。建议你经常返回放大镜图标,尤其是在刚刚使用过移除工具之后。这样可以避免误删对象,而是调出对象信息。

地形工具: 倾斜地形图标可用于打开一个工具栏,你可通过它更改每个图块的坡度来修改地图。

建造工具栏: 打开一组用于建造各种载具的工具栏。
- 铁路工具
- 单轨/磁悬浮工具
- 有轨电车/轻轨工具
- 道路工具
- 码头/港口工具
- 机场工具

注意: 某些 pak 图形包包含额外的建筑工具栏,例如窄轨铁路工具
每个工具栏都有一个仓库图标。仓库建成后,你可进入仓库窗口以访问载具。

专用建筑工具: 单击黄色圆圈中的红色水滴图标(pak128 图形包中为起重机图标)以打开此工具栏,其中包含各种施工工具,可辅助交通网络的建设。

摧毁/移除工具: 红色十字图标(pak128 图形包中为推土机图标)用于从地图中移除各种对象。

管理菜单

线路管理: 你可在此处(网格图标)设置、更改或删除运输线路,还可查看有关运输线路的信息。

列表: 打开列表页的图标会看到一个包含七个列表的子菜单:
- 站点列表
- 载具列表
- 城市列表
- 对象物列表
- 工厂列表
- 景点列表
- 标志列表

消息中心: 点击此邮箱图标,系统将显示自游戏开始或上次加载游戏以来生成的所有消息。

财务: 点击此金钱图标将打开一个窗口,显示有关你当前财务状况的所有重要信息。

其他菜单

截图: 在使用相机图标按钮时,当前游戏窗口(包括工具栏,但不包括标题栏)的快照将以 .png 图像的形式保存在 simutrans/screenshot/ 文件夹中。也可用 [c] 键捕获图像。

暂停: 如果你想暂时远离运输公司的喧嚣,可通过这个咖啡杯图标点一杯热茶或咖啡。当然,在模拟交通中,这项服务是免费的!
也可按 [p] 键来暂停/继续游戏。

快进: 你若觉得模拟交通游戏中的时间流逝太慢,可以按此 >> 图标加速。也可按 [W] 键选中/取消选中快进。
警告: 在快进模式下,游戏不能流畅运行。显示设置窗口底部的显示性能信息部分会显示计算机的性能是否能够满足此更改。

帮助: ? 图标可打开帮助索引,也可按 [F1] 键打开。

注意: 根据你玩的 pak 图形包,主工具栏上可能会有更多图标:放大/缩小显示网格旋转地图等。

状态栏

游戏世界窗口底部的状态栏会随游戏进程不断更新信息。状态栏上显示的信息,从左到右包括:
- 日期和时间: 在模拟交通中,一年分为月、日和小时。日期控制着时间线(见下文)激活时载具和建筑物的可用性。日期前面会显示一个代表气象季节的图标。对于某些气候设置,地面可能会随季节变化。
- 玩家: 当前玩家的姓名会以玩家的颜色显示。
- 账户余额: 玩家当前拥有的现金会显示在此并持续更新。此金额已考虑了建设成本和其他费用(购买载具、维护运输基础设施和载具运营成本)。
- 位置坐标: 方括号内显示的是光标当前所在图块的 xy 坐标以及高度。坐标 (0,0) 位于地图的左上角,高度零点位于水面或水面以上(取决于创建地图时水位的设置)。
- 游戏速度: 数值 (T=1.00) 表示当前游戏速度。你可用 [,]/[.] 键以小幅度加快/减慢时间。显示设置窗口底部的显示性能信息部分会显示计算机是否能够处理此更改。在快进模式下,游戏会另显示一个图标 >>
- 服从时间: 通过日历图标显示当前游戏中是否已启用时间线模式。

滚动条

有时,游戏世界窗口底部的状态栏上方会显示消息。要显示的消息可在消息中心选项中进行定义。
提示: 点击一条消息可将游戏视图中心定位到与之相对应的位置。

simutrans-124.5/simutrans/text/cz.tab000066400000000000000000001372331520675460500177120ustar00rootroot00000000000000§Cestina PROP_FONT_FILE prop-latin2.fnt ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: cz Cestina # # Encoding: UTF-8 # # Font: prop-latin2.fnt # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable Ne cl_btn_filter_enable Ano cl_btn_filter_settings Nastavení cl_btn_sort_asc Vzestupně cl_btn_sort_desc Sestupně cl_btn_sort_id Interní ID cl_btn_sort_income Příjmu cl_btn_sort_name Jméno cl_btn_sort_type Typu clf_btn_alle vše clf_btn_invers inv. clf_btn_keine nic gl_btn_sort_bonus Podle bonusu gl_btn_sort_name podle názvu gl_btn_sort_revenue podle příjmu gl_btn_unsort nesetříděno hl_btn_filter_disable Ne hl_btn_filter_enable Ano hl_btn_filter_settings Nastavení hl_btn_sort_asc Vzestupně hl_btn_sort_desc Sestupně hl_btn_sort_name Jméno hl_btn_sort_type Typu hl_btn_sort_waiting Čeká hlf_btn_alle vše hlf_btn_invers inv. hlf_btn_keine nic Networks Schéma sítě Queueing Změna ček. zásob Road toll Mýto Scenario Info. o scénáři Transfers Přestupy #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Polární klima desert Poušť mediterran Středomoří rocky Skaliska temperate Mírné tropic Tropy tundra Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Nahrávání scénáře selhalo Can't buy obsolete vehicles! Nákup zastaralých vozidel není možný! Cannot alter water Tato plocha nemůže být vysušena či zaplavena Cannot built depot here! Zde nemůžete\npostavit depo! Cannot built this station/building\nin underground mode here. Budova nesmí stát\npod zemí.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. Nelze vytvořit linka typu 'Vše'!\n\nVyberte typ linky\naktivací příslušné karty.\n Cannot create socket Neni možno vytvořit Socket! Convoi handles exhausted! Počet souprav dosáhl maximální hodnotu. Convoy already deleted! Konvoj byl již zrušen! Das Feld gehoert\neinem anderen Spieler\n Tuto část země\nvlastní jiný hráč!\n Der Besitzer erlaubt das Entfernen nicht Odstranění není dovoleno!\n Diese Zusammenstellung kann nicht fahren!\n Tato kombinace\nje nepojízdná!\n Flugzeughalt muss auf\nRunway liegen!\n Letadlo může vykládat\npouze na stojánce! Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Tato letištní budova\nsem nepasuje Hier kann kein\nSignal aufge-\nstellt werden!\n Zde nemůžete postavit\nnávěstidla!\n In order to lock the game, you have to protect the public player by password! Pro zamčení hry je nutné veřejné služby zablokovat heslem! Lost connection\nto server! Server neodpovídá1\nHra pokračuje na lokálním počítači! Lost synchronisation\nwith server. Hra mimo synchronizaci se serverem!\nProsím, restartujte! Maglevhalt muss auf\nMaglevschiene liegen!\n Zastávka maglevu musí být umístěna na příslušném druhu trati! Monorailhalt muss auf\nMonorail liegen!\n Zastávka musí\nležet na trati!\n Monorails are not available yet! Ještě nelze stavět jednokolejku/visutou železnici! Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Stanice úzkokolejky musí být\nna trati úzkokolejky No suitable way on the ground! Nevhodný úsek pro stavbu stanice nebo zastávky! No through station here! Zde nelze umístit stanici! Not enough clearance. Mezi cestami musí být výškový rozdíl alespoň dvou úrovní. Not enough money! Stavba překráčí finanční možnosti! On narrowgauge track only!\n Pouze na úzkokolejné trati!\n Only public player can lock games! Pouze hráč 'Veřejné služby' smí uzamknout hru. Out of funds Málo peněz! Post muss neben\nHaltestelle\nliegen!\n Tato budova musí\nbýt umístěna\nvedle existující\nzastávky stanice! Protocoll error (expecting game) Chybný protokol (čekající hru) Schiffhalt muss im\nWasser liegen!\n Zastávky lodí mohou\nležet pouze na vodě\npřed přístavem!\n Server busy Server je zaneprázdněn. Terraforming not possible\nhere in underground view Úprava terénu neni možná v podzemí! This tunnel branches. You can try Control+Click to remove. V tunelu je křižovatka - tunel je nutné zbourat jiným způsobem. Upgrade must have\na higher level Nová stanice musí být vyšší úrovně Vehicle %s cannot choose because stop too short! Vozidlo %s si nemůže vybrat zastávku, protože je příliš krátká! Zughalt muss auf\nSchiene liegen!\n Zastávky vlaku mohou\nležet pouze na\nželeniční trati!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s

Rejstřík

*: pouze v angličtině

Obecné

%s

Použití

%s

Nástroje

%s

Začínáme

%s

Jak hrát

%s

Ostatní:

%s Keyboard Help\n

Keyboard Help

\n Klávesové zkratky\n

Klávesové zkratky

\nSimutrans zná následující klávesové zkratky :

\nCtrl + kliknutí - Skrýt informační okno (občasně)
\nCtrl + tool - pracovat na vršní úrovni
\n\nEsc, Delete: Zavřít přední okno
\nBackspace: Zavřít vše okna
\nPage-up/Page-down, </>: Zoom zvětšit/zmenšit
\nF1 - Přehled Simutrans Pomoc
\n1 - Posunout mapu jihozápadně
\n2 - Posunout mapu jižně
\n3 - Posunout mapu jihovýchodně
\n4 - Posunout mapu západně
\n6 - Posunout mapu východně
\n7 - Posunout mapu severozápadně
\n8 - Posunout mapu severně
\n9 - Posunout mapu severovýchodně
\n

\nNásledující zkratky jsou volně použitelný
\n Decrease water height Snížit vodní hladinu Highlight railroad tracks Zvýrazní na mapě železniční tratě Highlite depots Zvýrazní na mapě depa Highlite electrical transmission lines Zvýrazní elektrické rozvodné sítě vysokého napětí. Highlite factories Zvýrazní/skryje na mapě továrny Highlite forests Zobrazí na mapě lesy Highlite tourist attraction Zobrazí/skryje na mapě turistické cíle Increase water height Zvednout vodní hladinu Overlay city limits Zobrazí/skryje na mapě hranice (limity) města. Overlay passenger destinations when a town window is open Překrýt cíle cestujících, pokud je otevřené okno měst Overlay schedules/network Zobrazí/skryje schéma dopravní sítě Overlay town names Zobrazí/skryje na mapě názvy měst. Please click on the map to add\nwaypoints or stops to this\nschedule. Přidejte stanice a kontrolní body do jízdního řádu. Set tile climate %s Změnit klima na %s Show capacity and if halt is overcrowded Zobrazí velikost a stav zastávky. Show how many convoi reach a station Zobrazí, kolik souprav dorazilo do stanice. Show how many people/much is waiting at halts Zobrazí kolik čeká na zastávkách Show initial passenger departure Zobrazí místa nástupu cestujících do dopravní sítě. Show level of city buildings Zobrazí stupeň (level) u městských budov Show mail service coverage/mail network Zobrazí dosah poštovní sítě Show passenger coverage/passenger network Zobrazí dosah sítě pro přepravu cestujících Show speedlimit of ways Zobrazí maximální povolenou rychlost na komunikacích. Show the change of waiting at halts Zobrazí součet počtu příjatých a odbavených cestujících (odbaveného nákladu) na zastávce Show the owenership of infrastructure Zobrazí vlastníka infrastruktury Show transported freight/freight network Zobrazí/skryje množství přepraveného nákladu nebo schéma nákladní dopravní sítě Show usage of network Zobrazí na mapě hustotu provozu Shows a listing with all industries on the map. Seznam továren na mapě Sum of departure/arrivals at halts Součet množství přijatáho a odbaveného zboží (cestujících, pošty) na zastávce. #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s zřídil\npro turisty nové auto-\nbusové spojení mezi\n%s a\n%s\n(na %i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s\nnyní přepravuje praco-\nvníky z %s\ndo továrny\n%s\n(na %i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s nyní\nvyužívá %i\nnáklaďáky k přepravě\nmezi %s\n(na %i,%i) a\n%s (na %i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\notevírá novou železnici\nmezi %s\n(na %i,%i) a\n%s\n(na %i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n %s zajištuje nýni letecké spojení\n mezi městami %s a %s Ferry service by\n%s\nnow between\n%s \nand %s.\n Hráč\n%s\nzahájil lodní provoz mezi\n%sa%s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\nzavedl novou příměstskou\nautobusovou linku mezi\n%s a\n%s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Editace mapy LISTTOOLS Seznamy MAGLEVTOOLS Nástroje pro maglev MONORAILTOOLS Nástroje pro jednokolejku/visutou železnici NARROWGAUGETOOLS Nářadí pro úzkokolejky RAILTOOLS Železniční doprava ROADTOOLS Silniční doprava SHIPTOOLS Lodní doprava SLOPETOOLS Úprava terénu SPECIALTOOLS Speciální stavby TRAMTOOLS Tramvaje #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s vybudoval nové ředitelství. Factory chain extended\nfor %s near\n%s built with\n%i factories. Ekonomický růst! Řetězec průmyslu pro %s poblíž %s byl rozšířen o %i továren. New %s now available:\n%s\n Nové %s je nyní k dispozici\n'%s'\n New factory chain\nfor %s near\n%s built with\n%i factories. Postaven nový řetěz průmyslu\n pro %s u\n%s, čítající\n%i továren\n New vehicle now available:\n%s\n Je dostupné\nnové vozidlo:\n\n' %s '\n\n Now %u clients connected. Právě je připojeno %u hráčů. Remove vehicle from map. Use with care! Odstranit vozidlo z mapy. Používejte jen při problémech! Screenshot\ngespeichert.\n Screenshot uložen.\n Sends the convoi to the last depot it departed from! Poslat soupravu do nejbližšího vhodného depa With a big festival\n%s built\na new monument.\n%i citicens rejoiced. S velikou slávou\nv %s odhalili\nnovou atrakci.\n%i občanů jásá. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter filtr #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (v depu) \nBauzeit bis do \nBauzeit von \nDostupné od \nCan't open heightfield file.\n \nSoubor s terénem nelze načíst.\n \ndirection: \nsměry: \nelektrified elektrifikováno \nHeightfield has wrong image type.\n \nŠpatný formát obrázku.\n \nis reserved by: \nrezervován pro vlak \nminimum speed: \minimální rychlost: \nnot elektrified \nnení pod proudem \nRibi (masked) \n (maska): \nRibi (unmasked) \n\nSměry\n\n (bez masky): \nSet phases: \ndélka semafórové fáze rovně/příčně \nsingle way \nJednosměrka \nway1 reserved by Trasa 1 rezervována hráčem\n \nway2 reserved by Trasa 1 rezervována hráčem\n \nwith sign/signal\n \nse značkou/semaforem\n %d buildings\n %d budov\n %d convois %d souprav %d Einzelfahrzeuge im Depot %d vozidel zde parkuje. %i km/h (max. %ikm/h) %i km/h (max. %i km/h) %i years %i months old. Stáří: %i roků a %i měsíců %s at (%i,%i) now public stop. %s je od teď veřejnou zastávkou %s building %s %s %s, %s %s city %d %s %s města %d %s %s factory %s %s %s - %s %s %s has entered a depot. Vozidlo "%s"\nzajelo do depa. %s land %d %s %s, Venkov %d %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. Ve městě %s si úředníci nechali postavit\nnovou budovu,\nkdyž jejich město přesáhlo\n%i obyvatel. %s\nis crowded. %s\nje přeplněna. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\nrychlost %i\nmaximální rychlost %i\ndx:%i dy:%i %s\nwas liquidated. Společnost %s zbankrotovala a byla zlikvidována. Provoz byl zastaven a majetek byl prodán. %u Client(s)\n %u hráč(ů)\n %u Player (%u locked)\n %u hráčů (z toho %u zamknutých)\n <> <> <> <žadná linka> <> 1 convoi 1 souprava 1 Einzelfahrzeug im Depot 1 vozidlo zde parkuje. 1LIGHT_CHOOSE Jas: 1WORLD_CHOOSE Nastavení pro novou hru: 2LIGHT_CHOOSE Barvy: 2WORLD_CHOOSE Číslo mapy: 3LIGHT_CHOOSE Rychlost posuvu: 4LIGHT_CHOOSE Inverze posuvu 5LIGHT_CHOOSE Chodci na zastávkách 5WORLD_CHOOSE Počet měst: 6LIGHT_CHOOSE Chodci ve městě 6WORLD_CHOOSE Hustota provozu: 8WORLD_CHOOSE Střídat den a noc A bridge must start on a way! Most musí začínat na cestě! Abfrage Inspekční lupa Abnehmer Odběratelé About O objektu About scenario Autor scénáře Abriss Odstranit Absenken Snížit zemi Abspanntransformator Trafostanice továrny Accelerate time Zrychlený čas Act. load: %u MW\n Aktuální přenos: %u MW\n Active player only Pouze aktivní hráč Add forest Zasadit les Add random citycar Vytvořit náhodné vozidlo add server Přidat server Add Stop Přidat zast. Add stops for backward travel Přidat zastávky pro jízdu nazpět air ranvej aircraft_tab Nákladní letadla airplane letadlo Airport Letiště AIRTOOLS Stavba letiště All Vše all convoi tooltips Zobrazit všechny nápovědy souprav Allow city growth Dovolit růst města Allow player change Umožnit změnu hráče allowed climates:\n Dostupné v podnebí:\n Alters a schedule. Přidat/Odstranit zastávky z jízdního řádu Angenommene Waren Zboží vyžadované pro okolní průmysl anhaengen Připoj Anhaenger_tab Přívěsy a návěsy Anheben Zvýšit zemi Appends stops at the end of the schedule Přidat zastávku na konec linkového vedení Apply Line Přiřadit linku April Duben Arbeiter aus: Zaměstnanci bydlí v: Arrivals from\n Příjezdy\n Arrived Přijato Assets Majetek Aufloesen Rozebrat Aufspanntransformator Trafostanice elektrárny August Srpen Autohalt muss auf\nStrasse liegen!\n Nelze postavit mimo silnici! Available K dispozici Bahndepot Vlakové depo Bankrott:\n\nDu bist bankrott.\n Bankrot:\n\nVaše firma zkrachovala.\n battery pohon: baterie Baum Strom baum builder Zasadit stromy Baustelle Staveniště Bauzeit Stavební doba Beenden Konec Beginner mode začátečnický mód Besonderes Gebaeude Turistická atrakce BF Stanice bio pohon: bio Blockstrecke ist\nbelegt\n V tomto železničním\núseku je jiný vlak\n Boden Země Bonusspeed: %i km/h Nejvyšší možná rychlost: %i km/h Boost (%%) Zvýšení produkce (%%) bridge is too high for its type! Tento typ neumožňuje takovouto výšku mostu! Bridge is too long for this type!\n Most tohoto typu nemůže být tak dlouhý! Bruecke Most Bruecke muss an\neinfachem\nHang beginnen!\n Mosty musí začínat na\nrovině nebo na sklonu!\n Brueckenboden most Build air depot Postavit hangár build choosesignals Postavit staniční signalizaci Build city market Postavit v nejbližším městě obchodní síť Build drain Postavit transformátorovu stanici build HQ Postavit sídlo Build land consumer Postavit novou elektrárnu Build maglev depot Postavit depo maglevu Build monorail depot Postavit depo jednokolejné dráhy Build narrowgauge depot Postavit depo úzkokolejky Build powerline Napnout dráty vysokého napětí Build presignals Postavit přednávěstidla Build road depot Postavit silniční depo Build ship depot Postavit loděnici Build signals Postavit návěstidla Build train depot Postavit železniční depo Build tram depot Postavit tramvajovou vozovnu Build truck depot Postavit garáže Building costs estimates Příbližná cena stavby Buildings Budovy Built artifical slopes Upravit svah Built random attraction Přidat turistickou atrakci Bus_tab Autobusy Can only move from halt to halt or waypoint to waypoint. Přehození možno jenom\nod stanice k stanici nebo\nod bodu k bodu Cancel Ne Cannot connect to offline server! K nedostupnému serveru se nelze připojit. Capacity: Kapacita: Capacity: %.0f MW Kapacita sítě: %.0f MW\n Capacity: %d%s %s\n Kapacita: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Kapacita: %s\n>> %d (%d%%) Cars are not available yet! Automobily nelze zatím stavět!\nJeště nebyly vynalezeny. cars.\nstate vozidla\n Cash Zůstatek Change player Změnit hráče Chart Graf Chat_msg Chat Choose direction Zvolte orientaci! Choose operation executed on clicking stored/new vehicles Vybrat operaci provedenou při kliknutí na zaparkovaná/nová vozidla v depu chooses a random map Vyber náhodnou mapu citicens Obyvatelé City attraction Městské památky City industries Průmysl ve městě City list Seznam měst City size Velikost city_road Městská silnice citybuilding builder Přidat městskou budovu CityLimit Hranice města cl_title Seznam vozidel cl_txt_sort Řadit podle: Clear block reservation Odstranit rezervační značky clf_chk_aircrafts Letadla clf_chk_cars Bus/TIR clf_chk_indepot V depu clf_chk_maglev MagLevy clf_chk_monorail Jednokolejky clf_chk_name_filter Jméno: clf_chk_narrowgauge Úzkokolejky clf_chk_noincome Bez příjmu clf_chk_noline Bez linky clf_chk_noroute Ztracen cíl cesty clf_chk_noschedule Bez řádu clf_chk_obsolete zastaralé clf_chk_ships Lodě clf_chk_spezial_filter Speciální filtr: clf_chk_stucked Uvízlé clf_chk_trains Vlaky clf_chk_trams Tramvaje clf_chk_type_filter Typ vozidla: clf_chk_waren Náklad: clf_title Nastavení filtru pro seznam vozidel Climate Control Nastavení podnebí closed Zavřeno. COLOR_CHOOSE\n Vyberte si, prosím,\nVaši barvu:\n Company bankrupt Společnost zbankrotovala Company_msg Protihráči Comparing pak files ... Porovnávání *.pak souborů ... Configure AI Nastavit umělou inteligenci Configure AI setttings Nastavení UI Congratulation\nScenario was complete in\n%i months %i years. Gratulace!\n\nScénář byl úspěšně dodehrán za %i měsíc(e) a %i rok(y) Connected stops Zastávky v dosahu objektu: Connected with server Spojen s serverem Constructed by Postavil: Constructed by %s Postavil: %s construction speed rychlost výstavby Construction_Btn Stavby Consumed Spotřebováno convoi %d of %d Souprava č. %d z %d convoi error tooltips Zobrazit jen varovné nápovědy Convoi has been sent\nto the nearest depot\nof appropriate type.\n Souprava byla poslána do nejbližšího depa vhodného typu. Convoi is sold when all wagons are empty. Souprava je prodána, jakmile jsou všechna její vozidla vyprázdněna. convoi mouseover tooltips Nápověda souprav na požádání convoi passed last\nmonth %i\n \nSouprav za poslední\nměsíc: %i\n Convois Soupravy Convois: %d\nProfit: %s Souprav: %d\nZisk: %s Convoys Soupravy Copy Convoi Kopírovat Copy the selected convoi and its schedule or line Kopírovat vybranou soupravu včetně jízdního plánu či linky cost for removal Cena za odstranění Cost: %8s (%.2f$/km %.2f$/m)\n Cena: %s (%.2f$/km %.2f$/m)\n Cost: %8s (%.2f$/km)\n Cena: %8s (%.2f/km)\n Costs Výdaje Create a new line based on this schedule Vytvořit novou linku, založenou na tomto jízdním plánu curiosity builder Přidat kuriozitu curlist_title Turistické cíle Currently playing: Právě se přehrává: Customers live in: Zákazníci žijí v: deactivated in online mode Neaktivní Deccelerate time Zpomalit časovou osu December Prosinec decrease underground view level Snížit úroveň řezu skrz terén Del Stop Smazat Delete Line Smazat linku Delete the current stop Smazat aktivní zastávku Delete the selected line (if without associated convois). Zrušit vybranou linku (není-li obsluhována) Delete this file. Smazat tento soubor. Delivered Doručeno Demand Poptávka Demand: %.0f MW Spotřeba: %.0f MW\n Denkmal Pomník Departed Odbaveno Departure board Příjezdy/Odjezdy Departures to\n Odjezdy\n Depots Depa Der Tunnel ist nicht frei!\n Tunel nelze zasypat! Destination Míření Destroying map ... Ruším mapu ... Details Detaily Die Bruecke ist nicht frei!\n Most neni volný! diesel pohon: dieslový Direkt erreichbare Haltestellen Přeprava odkud, kam a čeho disable midi Vypnout Midi Display settings Nastavení zobrazení Distance Délka/Měsíc Dock Přístav Dock must be built on single slope! Doky lze postavit pouze na rovném pobřeží! dp_title Seznam dep Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Máte ještě %d měsíc(e) na splacení dluhu. Durchsatz Max. Economy Hospodářství a města Eigenbesitz\n Veřejný majetek\n Ein %s\npasst hier nicht.\n '%s'\nse sem nehodí!\n Einstellungen aendern Změna nastavení electric pohon: elektrický Electricity Elektřina Electricity producer\n\n Výrobce elektřiny\n\n Electrics_tab Trakce Electrify track Elektrifikovat trať enlarge map Změnit velikost mapy enter a value between %i and %i Vložte hodnotu mezi %i a %i Enter address Zadejte adresu Enter Password Změna hráčova jména a hesla Error CHYBA Erzeuge neue Karte.\n Prosím vyčkejte,\nvytvářím novou mapu...\n\nČím je větší mapa,\nhustota průmyslu,\ndopravy apod., tím\ndéle trvá vytváření\nmapy. Es wird bereits\nein Fahrplan\neingegeben\n Jízdní řád už\nse upravuje!\n Fabrikanschluss Spojení s průmyslem Fabrikname Název továrny Factories Továrny factory details Detaily o továrně factorybuilder Přidat továrnu Fahrplan Jízdní řád Fahrtziel Místo určení: Fahrzeuge koennen so nicht entfernt werden Vozidla nemohou být\nodstraněna touto\ncestou!\n Fahrzeuge: Vozidla: Farbe Barva Fast forward Zrychlit čas February Únor Ferry_tab Trajekty Fertig Hotovo Filename Soubor: Files from: Soubory: Filter: Filtrovat: Finances of %s Finance %s Finanzen Finance find mismatch Porovnat Vaše *.pak soubory fl_title Seznam továren Flug_tab Osobní letadla follow me Následuj Follow the convoi on the map. Následuj soupravu po mapě. Forest Les Found new city Založit nové město Fracht Náklad Free Capacity Volná kapacita freeplay mode Freeplay Friction: okamžitý koeficient tření: fuel_cell pohon: palivové články Full load Čekat na: Fundament Podklad Fussgaenger Chodec Game info Informace o hře GAME PAUSED HRA POZASTAVENA Game_msg Všeobecné Gear: Převod: Gebaeude Budova General Všeobecně Generated Vyprodukováno Generation: %.0f MW Výroba: %.0f MW\n Gewicht Hmotnost Gewinn Příjem: Give the selected vehicle(s) an individual schedule Přiřadit vybranému(ým) vozidlu(ům) zvláštní jízdní řád gl_btn_sort_catg podle kategorii gl_title Seznam zboží go home Do depa Goods Zboží Goods AI AI (Zboží) Goods list Seznam zboží Gross Profit Hosp. výsledek Groundobj Objekt groundobj builder Přidat krajinný objekt Grow city Zvětšit město Growth Růst města H Hangar Hangár Happy Spokojeno Haus kaufen Koupit dům Helligk. Zobrazení Help Pomoc Help text not found Nápověda neexistuje hide all building Skryté všechny budovy hide city building Skryté městské budovy hide objects under cursor Skrýt objekty pod kursorem hide station names Skryté názvy stanic hide transparent Průhledné skrývání objektů hide trees Skryté stromy Hier warten/lagern: Čekající zboží a osoby: Higher transport fees, crossconnect all factories Zvýšit přepravní poplatky, spojit všechny továrny Highlite schedule Zvýraznit jízdní řád hl_title Seznam stanic hl_txt_filter Filtrovat: hl_txt_sort Řadit podle: hlf_chk_airport Letiště hlf_chk_anleger Přístav hlf_chk_bahnhof Nádraží hlf_chk_bushalt Autobus hlf_chk_frachthof Naklád hlf_chk_keine_verb Mimo provoz hlf_chk_maglevstop MagLev hlf_chk_monorailstop Jednokolejka hlf_chk_name_filter Jméno: hlf_chk_narrowgaugestop Úzkokolejka hlf_chk_overflow Přeplněný hlf_chk_spezial_filter Speciální filtr: hlf_chk_tramstop Tramvaj hlf_chk_type_filter Typ stanice hlf_chk_waren_abgabe Dodává: hlf_chk_waren_annahme Příjme: hlf_title Filtrovat Seznam stanic podle: Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depo nenalezeno!\nSouprava je třeba poslat\ndo depa ručně. Homeless Bezdomovců hydrogene pohon: vodíkový Idle: Nepřiřazeno: ignore climates Ignorovat podnebí In the industry legend show only currently existing factories Zobrazit pouze existující továrny In Transit Na cestě Increase Industry density Zahustit průmysl increase underground view level Zvýšit úroveň řezu skrz terén industrial building Továrna Init map ... Vytvářím mapu... Input Příjem Ins Stop Vložit Insert stop before the current stop Přidat zastávku nad označenou zastávkou Intercity road len: Meziměstské vazby: Intro. date: Dostupnost od: invalid neurčeno. Invalid coordinate Neplatné souřadnice isometric map Izometrická mapa January Leden join game Hrát on-line July Červenec Jump to Posunout na June Červen Kann Spielstand\nnicht laden.\n Uloženou hru nelze načíst!\n Kann Spielstand\nnicht speichern.\n Do souboru s uloženou\nhrou nelze zapisovat!\n Kein Besitzer\n Bez majitele\n keine žádný Keine Einzelfahrzeuge im Depot Žádná zaparkovaná vozidla Keyboard_Help\n Klávesové zkratky\n koord souřadnice Kreuzung Přejezd labellist_title Seznam značek Lade Relief Načíst terén Laden Načíst Land attraction Venkovská památka Land industries Průmysl mimo města LANG_CHOOSE\n Vyberte si, prosím,\nVáš jazyk:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Minulý měsíc Last used tools Naposledy použité nástroje Last Year Minulý rok: Leaving depot! Opouští depo! leer prázdný Legend Legenda Leistung Výkon Leistung: %d kW Výkon: %d kW Leitung Vedení letzen Monat: diesen Monat: minulý měsíc: tento měsíc: Line Linka Line Management Správa linek Lineless convoys serving this stop Vozidla bez zadané linky, která obsluhují tuto stanici Lines serving this stop Linky obsluhující tuto stanici LKW_tab Náklaďáky Load game Nahrát hru load height data from file Nahrát výškovou mapu ze souboru Load scenario Nahrát scénář loaded naloženo loaded passenger/freight Seřadit náklad dle Loading (%i->%i%%)! Naloženo %i z %i%% Loading addon paks ... --- Načítám rozšíření --- Loading map ... --- Nahrávám mapu --- Loading paks ... --- Nahrávám základní grafiku --- Loading skins ... --- Nahrávám grafické rozhraní --- Lock game Znemožnit další změnu hráčů Lokomotive_tab Lokomotivy maglev vehicle magnetické vozidlo maglev_track Trať maglevu Maglevdepot Depo maglevu Mail Demand %d\n Poptávaná pošta Mailbox Poštovní schránka Mailbox Options Nastavení zpráv Maintenance Údržba make stop public (or join with public stop next) costs %i per tile and level Zveřejnit zastávku za %i? na políčko a úroveň Manual (Human) Nový hráč Manufactured: Vyrobeno: Map roughness Zaoblení mapy: map zoom Přiblížení March Březen Margin (%%) Výdělek (%%) Marker Značka Max Boost (%%) Max. zvýšení produkce (%%) Max income: Max. zisk: Max. speed: Max. rychlost: Maximum 254 stops\nin a schedule!\n Nanejvýš 254 zastávek\nv jízdním řádu!\n maximum length of rivers Max délka řek Maximum tile height difference reached. Výška okolních polí je příliš odlišná! Maxspeed Nejvyšší rychlost May Květen Median Citizen per town Průměrná velikost města Meldung Zpráva Menge Počet merge stop Sloučit zastávky MessageOptionsText \nNový rok\n\nNovinky AI\n\nZe měst\n\nBloudění\n\nNový průmysl\n\nDiskuse\n\nNová vozidla\n\nPřeplnění\n\nProblémy\n\nZácpy\n\nScénář minimum length of rivers Min. délka řek Missing pakfiles Chybí některé soubory grafické sady (paku) Modify the selected line Změnit vybranou linku Monate alt měsíců starý Monorail Jednokolejka monorail vehicle vozidlo jednokolejky monorail_track Trať monorailu Monorailboden Nosník magnetické dráhy Monoraildepot Depo jednokolejky month wait time měsíce čeká Months Měsíce Monument Památník Monuments Památníky Mountain height Výška hor: Movingobj pohyblivý objekt Music playing disabled/not available Přehrávání hudby deaktivováno/nedostupné Music volume: Hlasitost hudby: mute sound Vypnout zvuk Name Jméno Narrowgauge Úzkokolejná dráha Narrowgauge are not available yet! Úzkokolejka není dosud k dispozici! narrowgauge vehicle úzkokolejné vozidlo narrowgauge_track Trať úzkokolejky Narrowgaugedepot Depo úzkokolejné dráhy Net ID: %p Síť: %p\n Net Wealth Jmění Net wealth near zero Vaše jmění se blíží k nule. Neue Karte Nová mapa Neue Welt Vytvořit novou mapu new convoi Nová souprava New Line Nová linka New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Vytvořena nová linka!\nMůžete jí nyní přiřadit\njejím zvolením v\nhorní části okna. New Vehicles Nová vozidla Nickname: Přezdívka no buildings hidden Ukázat budovy no convois Žádné konvoje No goods are loaded onto this convoi. V tomto konvoji není naloženo žádné zboží no goods waiting žadné zboží na odbavení no load nenakládat No Route Nenašlo spoj No stop here! Nářadí je pouze přístupné na stanicích! No suitable ground! Nevhodný terén! No terminal station here! Zde nelze postavit\nkoncovou stanici! no timeline Bez časové osy no tree Bez stromů No. of Factories Továrny a obchody Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Nejprve nastavte\njízdní řád!\n none nic nord Severní nordost Severovýchodní nordwest Severozápadní Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Nepovoleno!\nJízdní řád nemůže\nbýt právě změněn.\nZkuste to později! Not enough fields would remain. Kolem této farmy zbývá\nmálo políček. November Listopad Now active as %s.\n Nyní aktivní jako %s.\n Number of rivers Počet řek Object Podle ID Odometer: %s km Kilometrovník: %s km Ok Ano Oktober Říjen On loan since %i month(s) Jste zadlužen již %i měsíc(ů)! On this map, you are not\nallowed to change player!\n V této hře\nnemůžete hrát\nza jiného hráče!\n Only city chains Pouze městský průmysl Only first %d differing paks reported. There are probably more. Ohlášeno jen %d odlišných souborů. Ale je jich asi více. Only land chains Pouze venkovský průmysl Only one transformer per factory! U továrny smí být jen jedna trafostanice! Only show goods which are currently handled by factories Zobrazit jen zboží dodávané nebo odebírané některou z továren na stávající mapě. open otevřeno Operation Provoz vozidel Ops Profit Zisk vozidel Optionen Nastavení Or enter a server manually: Nebo zadejte adresu serveru ručně Origin Odbaveno ost Východní Output Odběr Ownership Vlastnictví Pak which may cause severe errors: Následující objekty chybí a mohou způsobit chybu v průmyslu Pak which may cause visual errors: Pak, který může způsobit chyby v zobrazení Pak(s) different: Odlišné soubory: Pak(s) missing on client: Soubory chybějící u hráče: Pak(s) not on server: Soubory chybějící na serveru: Pakset differences Rozdíly mezi sestavami paletten palet Pas_tab Osobní a poštovní vozy Passagiere cestujících Passagierrate Vzrůst osob Passagierziele Cíle cestujících/pošty Passenger AI AI (Osobní) Passenger Demand %d\n Poptávka po cestujících Passengers %d %c, %d %c, %d no route osob %d %c, %d %c, %d bez spoje Passengers %d %s, %d %s, %d no route osob %d %s, %d %s, %d bez spoje Password Heslo: Pause Pauza Pax <%i> Mail <%i> Cestující <%i> Pošta <%i> PaxDest Cíl cesty Percent Electricity Elektrifikováno (%%) Planes are not available yet! Letadla nejsou zatím dostupná!\nŽádné komerčně využitelné stroje\nnebyly ještě vynalezeny. Plant tree Zasadit strom player Hráč player -1 Člověk player 0 Veřejné služby player 1 Napik 128 a.s. player 10 Hráč 10 player 11 Hráč 11 player 12 Hráč 12 player 13 Hráč 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Spedition VM player 5 H-Trans GmbH player 6 PSK & Co KG player 7 Hráč 7 player 8 Hráč 8 player 9 Hráč 9 Please choose vehicles first\n Nejprve vyberte vozidlo!\n Post pošty Postrate Pošta Power Výkon Power (MW) Dodaná el. enegrie (MW) Power: Výkon: Power: %4d kW\n Výkon: %4d kW\n Powerlines Dráty v. n. Problems_msg Upozornění Produced Vyrobeno Production of %s has been stopped:\n%s\n Zastavena výroba %s '%s' Production/Boost Výroba/Zvýšeno Produktion Výroba Profit Zisk promote to line Vytvořit linku q1 Jaro q2 Léto q3 Podzim q4 Zima Query server Dotázat se serveru rail car železniční vozidlo random náhodně Random age Náhodné stáří Random map Náhodná mapa Rathaus Radnice Rating Stav ratio_pax %% osob Relevant Použitelné Reliefkarte Mapa Remove Odstranit znak remove airstrips Odstranit pojížděcí dráhu remove channels Zbourat plavební kanály remove interm. signals Odstranit vložené semafory remove maglev tracks Odstranit trať MagLevu remove monorails Odstranit trať jednokolejky remove narrowgauge tracks Odstranit trať úzkokolejky remove powerlines Odstranit vedení vysokého nápěti remove roads Zbourat silnice remove tracks Odstranit trať Remove wayobj %s Odstranit dopravní stavbu %s replace other signals Nahradit ostatní semafory replace stop Prohodit zastávky request closing Zavření požadovano residential house Obytný dům Restore natural slope Obnovit přírodní terén Restwert: Cena za prodej: Retire. date: Zastaralé od: return ticket Zpětná jízda Revenue Příjmy Revision: Revize: road Silnice road vehicle silniční vozidlo Roadsign Silniční značka Rotate Building Otočit budovu (nástupiště) Rotate map Rotace mapy Rotation Rotace Routing Spojení sack balíků sail vítr Saving map ... Ukládám mapu ... Scenario complete: %i%% Splnění scénáře: %i%% Scenario Debug Ladění scénáře Scenario Error Log Chyby ve scénáři Scenario Goal Cíl scénáře Scenario Info Základní informace o scénáři Scenario information Podrobnosti scénáře Scenario Result Splněno Scenario Rules Podmínky pro splnění cíle scénáře Schedule changing! Změna linkového vedení! Schienentunnel Postavit tunel Schiff_tab Nákladní lodě Schiffdepot Loděnice Schleppkahn_tab Čluny Seasons Roční období Sehenswuerdigkeit Turistická atrakce Select a server to join: Zvolte server, ke kterému se chcete připojit. Select a theme for display Vybrat motiv rozhraní Sell the selected vehicle(s) Prodat vybrané(á) vozidlo(a) sended Pošta čeká SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 September Září Server did not respond! Server neodpovídá! Serves Line: Obsluha linky: Service Vozidla set signal spacing Nastavení vzdálenosti semaforů Setting Nastavení Ship Lodě shops and stores Obchody a kanceláře Show all Zobrazit vše show all building Zobrazit všechny budovy Show also vehicles no longer in production. Zobrazovat také zastaralá vozidla Show also vehicles that do not match for current action. Zobrazuj i vozidla nevhodná pro nastavenou akci. Show even servers with wrong version or pakset Ukázat rovněž servery s špatnou verzí či špatnými sestavami show grid Zobraz mřížku Show industry Průmysl Show legend Legenda Show map scale Škála Show mismatched i jiné verze Show obsolete Zobrazovat zastaralá Show offline zobrazit nedostupné Show only used Zobrazit jen používané Show schedules Zobraz jízdní řády Show servers that are offline Zobrazí též nedostupné servery. Show servers where game version or pakset does not match your client Zobrazí též servery s jinou verzí programu nebo paku než má váš program. show station coverage Zobrazit dosah stanice show station names Zobrazit názvy stanic show waiting bars Zobrazení čekajících nákladů show/hide block reservations zobrazit/skrýt rezervate traťových úseků Show/hide estimated arrival times Zobrazit/skrýt předpokládáané časy příjezdů show/hide object owner zobrazit/skrýt vlastníka objektu Show/hide statistics Zobraz/schovej grafy Shows buttons on special topics. Zobrazení tlačítek možností minimapy Shows consumer/suppliers for factories Zobraz dodavatele/odběratele pro továrny Shows the color code for several selections. Zobrazí barevné měřítko některých možností minimapy Shows the currently selected schedule Zobraz aktuálně vybraný plán Shrink city Zmenšit město shuffle midis Náhodné pořadí Signal Semafor signal spacing Vzdálenost mezi semafory Sim: Simloops: Similar view as the main window Podobný náhled jako v hlavním okně Size (%d MB): Velikost (%d MB): sliced underground mode Zobrazení vrstvy č. slot empty Volný Smart hide objects Chytré skrývání objektů Sort by Seřadit náklad podle Sort waiting list by Řadit seznam čekajícího zboží podle Sound Zvuk Sound settings Nastavení zvuku a hudby Sound volume: Hlasitost zvuku: special freight spec. zboží Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Bonus za rychlost\nSilnice %i km/h, Železnice %i km/h\nVoda %i km/h, Vzdůch %i km/h Speedlimit Max. rychlost Speichern Uložit Spieler Hráč Spieler(mz) Hráči Spielerliste Obchodní rejstřík Spielstand wurde\ngeladen!\n \nHra úspěšně načtena!\n Spielstand wurde\ngespeichert!\n \nHra úspěšně uložena!\n Sprache Jazyk Sprachen Jazyk Stadtinformation Statistiky města Start the selected vehicle(s) Vyjet s vybraným(i) vozidlem(y) Starte Spiel Začít hru Station tiles: Zastavěná plocha: Station_msg Stanice Status Stav steam pohon: parní Step timeline one year Přidej jeden rok Stops Zastávky Storage Zásoby Storage capacity Úložná kapacita Strassendepot Silniční depo Strassentunnel Postavit tunel street car tramvajový vůz sued Jižní suedost Jihovýchodní suedwest Jihozápadní Summer snowline Sněžná čára v létě Supplied: %.0f %% Dodáno: %.0f %% Suppliers Dodavatelé Tage alt dní starý There are still vehicles\nstored in this depot!\n V tomto depu stále\nparkují vozidla!\n This Month Tento měsíc This Year Tento rok: Tile not empty. Pole není volné.\nPřed úpravou terénu\nodstraňte stavby. timeline Časová osa tl_title Seznam měst To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Aby přilákali návštěvníky,\nradní ve městě %s postavili\n %s z financí svých\n%i daňových poplatníků. To heavy traffic\nresults in traffic jam.\n Nadměrný provoz způsobil\ndopravní kolaps.\n Toggle day/night view Měnit den/noc Toggle vehicle tooltips Přepnout nápovědu pro vozidla tonnen tun Total inhabitants: Celkem obyvatel: Tourist attractions Turistické atrakce Tourists Turisté Town_msg Město Town: %s\n Obec: %s\n Towns Města track trať Tracks Tratě Traffic Provoz Train Vlaky Trains are not available yet! Vlaky ještě nebyly\nvynalezeny! Tram Tramvaj tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. Tramvaje %i km/h, Jednokolejky %i km/h\nMagLevy %i km/h, Úzkokolejky %i km/h tram_track tramvajová trať Tramdepot Depo tramvají Trams are not available yet! Tramvaje ještě nebyly\nvynalezeny! Transferring game ... Převádění hry... Transformer only next to factory! Měnírny jsou dostupné pouze\n na prázdních a plochých políčkách\nsousedujících k továrnám Translation Podle jména transparent station coverage Průhledný dosah stanice Transported Přepraveno TrolleyBus_tab Trolejbusy Truck Náklaďáky tt_Other Ostatní Tunnel muss an\neinfachem\nHang beginnen!\n Tunely musí začínat\ni končit na sklonu!\n Tunnel must start on single way! Tunel musí začínat\nna volném konci cesty!\n Tunnelboden Tunel underground mode Zobrazení celého podzemí UNDO failed! Nelze vzít zpět Undo last ways construction Vymazat poslední krok stavby cesty Unemployed Nezaměstnaných Unhappy Nespokojeno units/day /den, Update Line Změnit linku upgrade HQ Vylepšit sídlo Usage: %.0f %% Využito: %.0f %% Usage/Output Využito/Výkon Use beginner mode Použít začátečnický mód Use timeline start year Začít od roku Vehicle %s can't find a route! Souprava %s\nnemůže pokračovat v jízdě!\n Vehicle %s is stucked! Souprava %s uvízla! Vehicle details Podrobnosti Verbrauch Spotřeba Vergroessere die Karte\n Zvětšit mapu Verkauf Odstranit verkaufen Prodávat Verkehrsteilnehmer Osobní auta Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Jste v mínusu!\n\nMáte %d měsíc(e)\nna zaplacení dluhu!\n vh_title Katalog vozidel via Přes via %s\n přes %s\n via Menge Přes + Počet voranstellen Na začátek Waggon_tab Vagóny waiting Čeká Waiting for clearance! Čeká na uvolnění úseku Walked Odešlo Warnings_msg Doprava Wasser Voda Water voda Water level Úroveň vody: water vehicle vodní vozidlo way %s cannot longer used:\n Typ trati %s nemůže být dále používán.\n way %s cannot longer used:\n%s\n Typ trati %s nemůže být dále používán:\n%s\n way %s now available:\n Typ trati %s je nyní k dispozici. Way toll Mýtné Ways not connected Možno jen na spojených cestách! Wegpunkt Bod Weight: Hmotnost: Wert Cena west Západní Winter snowline Sněžná čára v zimě withdraw Vyřadit Withdraw All Vyřadit vše WRONGSAVE \nNekompatibilní verze\nsouboru. Tuto uloženou hru nelze\nnačíst.\n Year %i has started. Začal rok %i! Years Roky Your primary color: Vaše primární barva Your secondary color: vaše sekundární barva Zielort Cíl zooming in Přiblížit zooming out Oddálit Zu nah am Kartenrand Příliš blízko\nk okraji mapy!\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nový světový rychlostní rekord maglevu: %.1f km/h soupravou %s. New world record for monorails: %.1f km/h by %s. Nový světový rychlostní rekord monorailu: %.1f km/h soupravou %s. New world record for motorcars: %.1f km/h by %s. Nový světový rychlostní \nrekord vozidla: %.1f km/h \nsoupravou %s. New world record for narrowgauges: %.1f km/h by %s. Nový světový rychlostní rekord úzkokolejky: %.1f km/h soupravou %s. New world record for planes: %.1f km/h by %s. Nový světový rychlostní\n rekord letadla:\n %.1f km/h\n strojem %s. New world record for railways: %.1f km/h by %s. Nový světový rychlostní\n rekord vlaku:\n %.1f km/h\n soupravou %s. New world record for ship: %.1f km/h by %s. Nový světový rychlostní\n rekord lodě:\n %.1f km/h\n plavidlem %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL ešov &1_CITY_SYLL ovice &2_CITY_SYLL ichov &3_CITY_SYLL onín &4_CITY_SYLL ovičky &5_CITY_SYLL iněves &6_CITY_SYLL any &7_CITY_SYLL nice &8_CITY_SYLL otín &9_CITY_SYLL kov &A_CITY_SYLL edly &B_CITY_SYLL asy &C_CITY_SYLL áňky &D_CITY_SYLL aň &E_CITY_SYLL áňka &F_CITY_SYLL avice &G_CITY_SYLL ičina &H_CITY_SYLL ec &I_CITY_SYLL ice &J_CITY_SYLL ička &K_CITY_SYLL ičky &L_CITY_SYLL ín &M_CITY_SYLL ínka &N_CITY_SYLL ina &O_CITY_SYLL ivice &P_CITY_SYLL ivín &Q_CITY_SYLL ov &R_CITY_SYLL ová &S_CITY_SYLL ové &T_CITY_SYLL ovec &U_CITY_SYLL oves &V_CITY_SYLL ovice &W_CITY_SYLL oslav &X_CITY_SYLL oslavice &Y_CITY_SYLL oslavičky &Z_CITY_SYLL ůvka %0_CITY_SYLL Bab %1_CITY_SYLL Bor %2_CITY_SYLL Dobr %3_CITY_SYLL Dol %4_CITY_SYLL Host %5_CITY_SYLL Hrad %6_CITY_SYLL Chot %7_CITY_SYLL Kamen %8_CITY_SYLL Král %9_CITY_SYLL Rad %A_CITY_SYLL Slav %B_CITY_SYLL Str %C_CITY_SYLL Třeb %D_CITY_SYLL Beč %E_CITY_SYLL Blš %F_CITY_SYLL Břez %G_CITY_SYLL Čím %H_CITY_SYLL Drozd %I_CITY_SYLL Hal %J_CITY_SYLL Kost %K_CITY_SYLL Kudl %L_CITY_SYLL Kras %M_CITY_SYLL Les %N_CITY_SYLL Mohel %O_CITY_SYLL Mohyl %P_CITY_SYLL Mysl %Q_CITY_SYLL Olbram %R_CITY_SYLL Oleš %S_CITY_SYLL Příbraz %T_CITY_SYLL Rud %U_CITY_SYLL Val %V_CITY_SYLL Vedr %W_CITY_SYLL Vod %X_CITY_SYLL Všerub %Y_CITY_SYLL Žár %Z_CITY_SYLL Daleč 0center %s 0extern %s - %s planina 0suburb %s - Pod Kosířem 1center %s - Staroměstská 1extern %s - %s okraj %s 1suburb %s - %s příkopy %s 2center %s - Centrum 2extern %s - %s okres %s 2suburb %s - %s sídliště %s 3center %s - %s město 3extern %s - Křižovatka 3suburb %s - %s předměstí %s 4center %s - %s náměstí %s 4extern %s - Chaty 4suburb %s - %s čtvrť %s 5center %s - hřbitov 5extern %s - Rozcestí 5suburb %s - Nová Kolonie 6center %s - Hostinec 6extern %s - %s kraj %s 6suburb %s - %s Průmyslový park %s 7center %s - Škola 7extern %s - %s lesy %s 7suburb %s - %s sady %s 8center %s - Obchodní dům 8extern %s - %s pole %s 8suburb %s - Pošta 9center %s - Zahradní město 9extern %s - Hájovna 9suburb %s - Sokolovna Acenter %s - Achátová Aextern %s - %s polesí %s Asuburb %s - Kachní kámen Bcenter %s - Barvířská Bextern %s - Háje Bsuburb %s - K Borku Ccenter %s - Bělidlo Cextern %s - Hluchov Csuburb %s - Luční Dcenter %s - Hedvábnická Dextern %s - K rokli Dsuburb %s - Malý val Ecenter %s - Hradební Eextern %s - Na Strouze Esuburb %s - Mezi Mlaty Fcenter %s - Hrnčířská Fextern %s - Na Blatech Fsuburb %s - Na Baterii Gcenter %s - Hůrka Gextern %s - U Brůdku Gsuburb %s - Na Hatích Hcenter %s - Kolářka Hextern %s - Hliňák Hsuburb %s - Na Láni Icenter %s - Křížový vrch Iextern %s - Na kopečku Isuburb %s - Na Obci Jcenter %s - Na hradbách Jextern %s - U Hložku Jsuburb %s - Na Písku Kcenter %s - Na Mlejnku Kextern %s - Na Holé pláni Ksuburb %s - U Průhonu Lcenter %s - Oskol Lextern %s - V Sadech Lsuburb %s - Na Šancích Mcenter %s - Provaznická Mextern %s - Na skalách Msuburb %s - Na Valech Ncenter %s - Sedlářská Nextern %s - Na stráni Nsuburb %s - Na Výsluní Ocenter %s - Soukenická Oextern %s - Na Vyhlídce Osuburb %s - Písečná Pcenter %s - Špitálská Pextern %s - Kozí vrch Psuburb %s - Pekařská Qcenter %s - U Slunců Qextern %s - Soumarská Qsuburb %s - Pod Hradbami Rcenter %s - Park Rextern %s - U dubu Rsuburb %s - Průhonek Scenter %s - Solný Trh Sextern %s - U palouku Ssuburb %s - Kovárna Tcenter %s - Špejchar Textern %s - U studánky Tsuburb %s - U lip Ucenter %s - Velký val Uextern %s - Větrná Usuburb %s - V Aleji Vcenter %s - Pod věží Vextern %s - Pastviny Vsuburb %s - V Hlinách Wcenter %s - Zákostelní Wextern %s - Zahájí Wsuburb %s - Vodní Xcenter %s - Zelinářská Xextern %s - Třešňovka Xsuburb %s - V pecích Ycenter %s - Ztracenka Yextern %s - Luční Ysuburb %s - Vystrkov Zcenter %s - Nekázanka Zextern %s - Újezd Zsuburb %s - Za Humny simutrans-124.5/simutrans/text/cz/000077500000000000000000000000001520675460500172115ustar00rootroot00000000000000simutrans-124.5/simutrans/text/cz/airtools.txt000066400000000000000000000151051520675460500216100ustar00rootroot00000000000000Nápověda - letecká doprava

Letecká doprava

Panel stavba letiště obsahuje nástroje pro výstavbu letecké dopravní sítě - pojížděcí dráhy, ranveje, stání pro letadla, hangáru a dalších budov.

Nabídku s nástroji pro leteckou dopravu zobrazíte kliknutím na ikonu letadla v menu v horní části hrací plochy.

Podržíte-li kurzor nad ikonou nástroje, (po té, co jste otevřeli nabídlu pomocí kliknutí na symbol s letadlem) zobrazí se popisek obsahující název, náklady na výstavbu, v závorce měsíční náklady na údržbu a další doplňující text. U ranveje a pojížděcí dráhy je to maximální rychlost, u stání pro letadla a terminálů je to kapacita.

Jestliže jste při výběru nové hry použili volbu začít roku, pak se s postupem času budou ve hře objevovat nové typy ranveje a letištních budov.

Postup, jak na stavbu jednoduchého letiště:
(i) Postavte ranvej.
(ii) Připojte k ranveji pojížděcí dráhu. Pojížděcí dráhu nepřipojujte ke koncům ranveje. Letadla by pak nemusela vzlétnout. Připojte ji mezi druhé a předposlední políčko (včetně).
(iii) Na pojížděcí dráhu umístěte stání pro letadlo.
(iv) Postavte hangár, popřípadě další budovy podle vašich požadavků.

Menu stavba letiště obsahuje tyto nástroje (z leva do prava):

Pojížděcí dráha

Pojížděcí dráha umožňuje letadlům pohyb mezi hangárem, přistávací drahou a stáními pro letadla. Pojížděcí dráhu je možné postavit jen vodorovné ploše, stavba ve svahu není možná.

Pojížděcí dráhu postavíte stejným způsobem, jako silnici nebo železnici:
(i) Klikněte na ikonu s symbolem pojížděcí dráhy. (Kurzor se změní na pojížděcí dráhu.)
(ii) Klikněte na políčko, kde má pojížděcí dráha začínat. (Kurzor se změní na buldozer a v pravém dolním rohu na stavové liště se zobrazí rozdíl souřadnic mezi takto zvoleným počátkem a aktuální polohou kurzoru.)
(iii) Klikněte na pole, kde má pojížděcí dráha končit.

UPOZORNĚNÍ: Jestliže je pojížděcí dráha připojena ke koncům ranveje, nebo se vyskytuje na poli ve směru přistávání nebo startu, letadla mohou odmítat vzlétat nebo přistávat.

Tip: K odstranění jednotlivých částí pojížděcí plochy použijte tlačítko Odstranit nebo Klávesovou zkratku [z] (zpět). Oboje vám nenahradí vynaložené prostředky ale při použití klávesové zkratky [z] nepřijdete peníze za odstranění.

Ranvej

Ranvej slouží letadlům pro přistání a vzlet. Ranvej je možné postavit jen na vodorovné ploše, nelze ji postavit na svahu. Ranveje se mohou navzájem křížit.

Ranvej musí být rovná. Ranvej lze postavit ve směrech sever/jih a východ/západ. Diagonální ranveje nejsou povoleny.

Stavba ranveje:
(i) Klikněte na nástroj. (Kurzor se změní na ranvej).
(ii) Klikněte na počáteční pole. (Kurzor se změní na buldozer.)
(iii) Klikněte na koncové políčko.

Tip: Konce ranveje nechte volné, nepřipojujte k nim pojížděcí dráhu. Letadla by pak nemusela vzlétat nebo přistávat.

Odstranit pojížděcí dráhu

Nástroj odstraňuje úseky pojížděcí dráhy nebo ranveje, pokud na ní nejsou přítomna žádná letadla. Peníze za odstranění se také účtují do položky výstavba.

Odstranění ranveje nebo pojížděcí plochy:
(i) Klikněte na nástroj (Kurzor se změní na červený křížek.)
(ii) Klikněte na políčko, odkud se má začít odstraňovat a ještě jednou klikněte na políčko, kde se má skončit s odstraňováním.

Tip: Dejte pozor, jesli se mezi na cestě mezi oběma políčky nenalézá značka zakazující vjezd v jednom ze směrů. Nástroj pro odstraňování tento směr následuje. Mohlo by se vám stát že omylem odstraníte jinou než zamýšlenou část.

Hangár

Hangár je budova v níž lze nakupovat, přechovávat a prodávat letadla a vzducholodě, stejně jako vlaky v depu. Hangár lze postavit na konci pojížděcí dráhy. Každý hangár má také měsíční náklady na údržbu, proto nestavte více hangárů, než potřebujete.

Stavba hangáru:
(i) Klikněte na nástroj. (Kurzor se změní na hangár.)
(ii) Klikněte na konec pojížděcí dráhy.

Tip: Hangár odstraníte pomocí tlačítka Odstranit. Hangár musí být prázdný, jinak nejde odstranit.

Stání pro letadlo

Stání pro letadlo je místo, kde do letadel nastupují a z letadel vystupují cestující nebo je nakládáná a vykládána pošta nebo náklad.
Pokud není stání pro letadlo postaveno vedle jiného stání nebo letištní budovy, vytvoří novou zastávku
Stání pro letadlo se staví na konec pojížděcí dráhy, má náklady na údržbu a spádové území.

Stavba stání pro letadla:
(i) Klikněte na nástroj z nabídky. Kurzor se změní na stání pro letadlo a zvýrazní se spádová oblast pole pod kurzorem.
(ii) Klikněne na konec pojížděcí dráhy.

Tip: Velikost spádové oblasti můžete změnit při vytváření nové hry v nastavení (záložka Spojení, volba station_coverage)
Stání pro letadlo odstraníte tlačítkem Odstranit.
Klávesou [v] Zobrazíte/skryjete spádové území.
Letadla mohou přistávat, pouze pokud je na letišti dostatek volných stání pro letadla. Jinak krouží nad letištěm a spotřebovávají palivo (a peníze).

Letištní budova

Letištní budova zvyšuje kapacitu letiště stejným způsobem, jako nádražní budova kapacitu železniční stanice.

Stavba budovy:
(i) Klikněte na ikonu požadovaného rozšíření. (Kurzor se změní na ikonu obsahující rozšíření.)
(ii) Klikněte na požadovanou pozici vedle stávající zastávky/stání pro letadlo. Budova se od teď považuje za součást letiště.

Tip: Budovu lze odstanit pomocí ikony Odstranit.
Stiskem tlačítka [v] na klávesnici zobrazíte, respektive skryjete spádové území letiště.

Nápověda odpovídá verzi 111.4, poslední úprava 21.10.2012.

simutrans-124.5/simutrans/text/cz/baum_build.txt000066400000000000000000000023041520675460500220540ustar00rootroot00000000000000Nápověda - zasadit stromy

Zasadit stromy

Dialog vám umožňuje hrát si na zahradníka a sázet stromy.

Dialog je dostupný, pokud jste se přepli na hráče Veřejné služby.

Okno dialogu je rozděleno na čtyři části:
V levém horním rohu jsou dva panely umožňující vybírat mezi dostupnými druhy stromů. Můžete vybírat stromy podle názvu nebo podle ID.

Pokud zvolíte nějaký strom, pak se v levém dolním rohu zobrazí jeho obrázek.
V pravém horním rohu jsou zobrazeny volby.

Volba Ignorovat podnebí umožňuje sázet stromy bez ohledu na podnebný pás. Pokud ji povolíte, můžete sázet třeba tropické palmy do sněhu.

Volba Náhodné stáří umožňuje sázet stromy různé velikosti. Pokud není povolena, sázíte jen malé stromečky.

Pod volbami jsou informace o daném stromu: název, podnebný pás nebo podnebné pásy a kdo daný stromek nakreslil.

Tip: Celý les zasadíte pomocí tlačítka Zasadit les z menu editace mapy.

Nápověda odpovídá verzi 112.1, poslední úprava 31.12.2012.

simutrans-124.5/simutrans/text/cz/bridges.txt000066400000000000000000000056271520675460500214030ustar00rootroot00000000000000Nápověda - stavba mostů

Stavba mostů

Mostem lze překlenout údolí nebo jiná překážka.

Každý most má určenu maximální možnou délku. Pro přemostění delších úseků nezbyde než v nabídce hledat jiný, nebo si pomoci ostrůvky a celkovou vzdálenost rozdělit na více částí. Mosty lze stavět i přes hluboká údolí s nerovným terénem, omezení je kladeno jen na počáteční a koncové pole. Most musí začínat a končit na vodorovné ploše, svahu šikmém ve směru mostu nebo na umělé vyvýšenině. Začátek a konec mostu nemusí být ve stejné výšce, může mezi nimi být rozdíl jedné úrovně. Mosty lze stavět jen ve směru hranic políček, tj. nelze je (zatím) stavět diagonálně. Existuje však druh železniční trati, který připomíná most, neboť je nad terénem. Tuto trať lze stavět i diagonálně.

Všechny mosty, ať železniční, silniční, pro lodní dopravu, nebo pro dráty vysokého napětí se staví stejným způsobem. Silniční most má jednu výjimku: lze na něj navíc přidat i koleje pro tramvaj.

Stavíme most

Následující postup lze uplatnit pro všechny typy mostů, pokud je zde psáno silnice, můžete si místo ní dosadit železnici, nebo jiný druh dopravy mající mosty:
(i) Postavte silnici k oběma předpokládaným koncům mostu, přičemž silnice bude i a prvním a posledním políčku budoucího mostu.
(ii) Klikněte na symbol mostu. Zvolte si ten který vám bude svými parametry nejvíce vyhovovat.
(iii) Klikněte na pole se zamýšleným počátkem mostu a tlačítko držte stisknuté.
(iv) Najeďte kurzorem myši nad políčko, kde má most končit, a pusťte tlačítko. Most je hotov.

Existuje také alternativní postup. Musíte však mít předem správně přichystané oba konce silnice, jinak se může stát, že utratíte peníze za dlouhý most, který nechcete. Místo kroků (iii) a (iv) klikněte na začátek zamýšleného mostu a pusťte tlačítko. Most se vystaví sám až ke kusu silnice na druhé straně.

Častu chybou při stavbě mostu je, že není na koncích připojen k silnici (nebo železnici). Přetáhněte silnici (železnici) i na počáteční a koncové pole mostovky, jinak přes něj auta (vlaky) nebudou jezdit.

Tip: Někdy se most nevyplatí stavět. Mosty mají obecně vyšší náklady na údržbu než je u běžné silnice (trati, elektrického vedení, ..). Podívejte, jestli se u menších terénních nerovností nevyplatí prohlubeň zasypat.

Odstranění mostu

Klikněte na tlačítko Odstranit a pak klikněte na začátek nebo na konec mostovky.

->Nápověda pro stavbu silnic
->Nápověda pro stavbu železnic

->Informace o překladu

simutrans-124.5/simutrans/text/cz/citybuilding_build.txt000066400000000000000000000042021520675460500236150ustar00rootroot00000000000000Nápověda - přidání městské budovy

Přidat městskou budovu

Dialog umožňuje ručně umísťovat městké budovy.

Okno je rozděleno do čtyř částí.
V levém horním rohu je seznam budov.
V levém dolním rohu se zobrazí obrázek zvolené budovy.
V pravém horním rohu jsou tlačítka pro zapnutí/vypnutí jednotlivých filtrů pro množinu budov.
Pod tlačítky se nachází část obsahující podrobné informace o daném domu: název, doplňující text, údaje o množství cestujících a pošty, období, ve kterém je budova dostupná, a jméno nebo přezdívka autora.

Seznam budov

Seznam odsahuje všechny dostupné městské budovy.
Budovy je možné řadit podle jména nebo podle identifikátoru. Výběr budov lze omezit na určitou skupinu pomocí tlačítek nacházejících se v pravém horním rohu.

Jména a identifikátory mají barvu v závislosti na typu budovy:
Modrá barva je pro obytné budovy.
Zelená barva označuje obchody a kanceláře.
Černá barva je určena pro průmyslové budovy.

Tlačítka voleb

Ignorovat podnebí: Tlačítko ruší omezení budovy na příslušný podnebný pás, budovu pak můžete umístit kamkoliv.
Začít roku: Tlačítko zapíná omezení výběru budov podle data. Pokud je volba zapnuta, neuvidíte budovy, které se již nestaví, protože jsou zastaralé. Také neuvidíte budovy, které bude možno postavit v budocnosti.
Zobrazit zastaralé: Povolí zobrazit zastaralé typy budov.
Obytný dům: Zobrazí obytné budovy.
Obchody a kanceláře: Zobrazí obchody a kanceláře.
Továrna: Zobrazí továrny.
Předchozí volby tři lze libovolně kombinovat: například nechat zobrazit jen obytné budovy a kanceláře.
Rotace: Má-li budova obrázky pohledů z více úhlů, potom pomocí šipek můžete měnit náhled budovy zobrazený v levém dolním rohu.

Nápověda odpovídá verzi 112.1, poslední úprava 2.1.2013.

simutrans-124.5/simutrans/text/cz/citylist_filter.txt000066400000000000000000000037161520675460500231720ustar00rootroot00000000000000Nápověda - seznam měst

Seznam měst

Seznam měst obsahuje podrobné údaje statistické údaje o městech, počtu obyvatel, budov, přepravnených cestující, poště apod.

Seznam měst otevřeme kliknutím na ikonu Seznamy následovným kliknutím na ikonu Seznam měst. Seznam měst lze též otevřít klávesou [T].

Číslo za textem Celkem obyvatel sděluje celkový počet obyvatel ve hře. Je to součet obyvatel všech obcí. Číslo v závorkách znázorňuje růst všech měst.

Vlevo pod textem řadit podle se nachází dvě tlačítka ovlivňující řazení. První tlačítko umožňuje seřadit města podle názvu (Jméno), počtu obyvatel (Obyvatelé) nebo přírůstku obyvatel (Růst města). Druhým tlačítkem přepínáme mezi vzestupným a sestupným řazením.

Kliknutím na název města zobrazíte okno s podrobnými informacemi o daném městě.

Kliknutím na trojúhelníkovité tlačítko před názvem města se přemístíte na pozici daného města.

V seznamu měst jsou tyto položky: název města, počet obyvatel a v kulatých závorkách se nachází přírůstek obyvatel.

Název města můžete změnit v dialogu města. Dialog města otevřete kliknutím na název města nebo na příslušnou radnici.

Růst města je zde počítán jako vážený aritmetický průměr přírůstku obyvatel města za poslední měsíce s vahami 5,4 a 1 zaokrouhlený na desetiny.

Tlačítkem Graf zobrazíte graf a příslušná tlačítka umožňující zobrazit v grafu odpovídající položky. Máte na výběr mezi grafem za posledních 12 měsíců nebo posledních 12 let.

Nápověda odpovídá verzi 112.1, poslední úprava 2.1.2013.

simutrans-124.5/simutrans/text/cz/citywindow.txt000066400000000000000000000120221520675460500221470ustar00rootroot00000000000000Nápověda - dialog města

Dialog města

Dialog města zobrazuje statistické údaje o zvoleném městu nebo vesnici, dvě mapy a graf zobrazující veličiny v závislosti na příslušných tlačítkách. Umožňuje také změnit název. Dialog lze otevřít kliknutím na radnici nebo na název v seznamu měst.

Dialog obsahuje následující položky:

První položkou je název města nebo vesnice. Je zobrazen na titulku okna můžete jej nalét v Seznamu měst. Název města je použit při vytváření jmen stanic a zastávek uvnitř hranic města.
Název města změníte kliknutím do pole s názvem a přepsáním na jiný název. Názvy měst jsou načítány ze soubory text/citylist_cz.txt z adresáře příslušného paku. Zachováte-li kódování, máte možnost si do seznamu doplnit své oblíbené město.

Tip: Pomocí klávesy [!] lze přepnout způsob zobrazení názvu města nad radnicí.

Velikostukazuje počet obyvatel. V závorkách je přírůstek obyvatelstva. (Je to průměrný přírůstek obyvatel za poslední tři měsíce, přičemž každý měsíc má jinou váhu.)

Číslice před nápisem Budov ukazuje počet budov daného města.

Dva páry čísel o řádek níže jsou mapové souřadnice hranic města. První souřadnice označuje levý horní roh, druhá pravý dolní. Souřadnice se mění s růstem města a mohou se překrývat.

Čísla za nápisy nezměstnaných a bezdomovců označují počet obyvatel bez zaměstnání a bydliště. Tato čísla ovlivňují, jaké budovy (obytné/odchodní/průmyslové) se budou stavět.

Volbou Dovolit růst města povolujeme nebo zakazujeme růst města. Když volba není povolena, město neporoste.

Mapy v dialogu města ukazují cílová místa pro dopravu cestujících a pošty z tohoto města. Mapa v pravo ukazuje data pro aktuální měsíc, na mapě vlevo jsou data z minulého měsíce. Cílová místa, jednotlivé tečky na mapě, jsou vybarvena následovně:

- žlutá tečka: Cesta k cíli existuje a nástupní stanice/zastávka není přeplněná (usměvavý smajlík v dialogu zastávky).
- oranžová tečka: K cíli neexistuje cesta.
- červená tečka: K cíli existuje cesta, ale nástupní zastávka je přeplněná (neveselý smajlík v dialogu zastávky).

Pod mapami se nachází graf ukazující statistiku pro dané město. Na vodorovné ose je zobrazen čas, na svislé ose množství. Máte na výběr mezi zobrazením po měsících nebo po letech.
Roky: zobrazí roční hodnoty za 12 let
Měsíce: zobrazí měsíční hodnoty za posledních 12 měsíců

Kliknutím na tlačítka pod grafem zobrazíte nebo skryjete odpovídající křivku grafu. Barva křivky odpovídá barvě tlačítka:

Obyvatelé: Počet obyvatel žijících ve městě.
Růst města: Změna počtu obyvatel.
Budovy: Počet budov ve městě.
Osobní auta: Kolik bylo generováno osobních aut.
Přepraveno: Kolik cestujících vstupilo do dopravní sítě (veselý smajlík) v dialogu stanice/zastávky
Cestující: Celkový počet cestujících vygenerovaných v daném městě.
Pošta čeká: Pošta, která vstoupila do dopravní sítě.
Pošta: Celkové množství pošty vygenerované v daném městě.
Přijato: Kolikráte bylo zjištěno, že továrna ve městě má na skladě zboží, které odebírá.
Zboží: Počet kontrol, při kterých bylo zjišťováno, zda-li má továrna ve městě zboží, které odebírá.

Tip: Nastavení týkající se měst může být měněno v souborech simuconf.tab a cityrules.tab.

Růst města je podporován přepravou cestujících a pošty a dodáváním zboží do továren uvnitř hranic města. Růst města zahrnuje stavbu nových silnic. Město také přebírá stávající silnice v blízkosti nově postavených budov do vlastnictví, tj. hráče nazývaného Veřejné služby.

Tip: Zamýšlíte-li hrát dlouho a nechat město vyrůst, vyplatí se postavit městu základní síť přímých rovných ulic, někdy i o šíři 2 polí. Až se město rozroste, po rovných ulicích budou jezdit autobusy mnohem rychleji, než v křivolakých uličkách. Na širokých ulicích (o dvou polích) snadno postavíte obousměrnou tramvajovou trať. Navíc nepotřebujete bourat budovy a tak nepřicházíte o obyvatele. Tyto budoucí ulice stavte tou nejlevnější silnicí s co nejmenšími náklady na údržbu. Město si je samo vylepší, až je bude potřebovat.

Nápověda odpovídá verzi 112.1, poslední úprava 2.1.2013.

simutrans-124.5/simutrans/text/cz/climates.txt000066400000000000000000000047731520675460500215660ustar00rootroot00000000000000Nápověda - nastavení podnebí

Nastavení podnebí

Dialog Nastavení podnebí umožňuje nastavit terén a podnebné pásy. Dialog otevřete kliknutím na tlačítko Nastavení podnebí v dialogu s názvem Vytvořit novou mapu.

Hodnoty upravíte kliknutím na šipku vedle políčka.

Nastavení terénu

úroveň vody - Nastavuje úroveň vody na mapě. Vyšší číslo znamená více vodních ploch a méně pevné země.

Výchozí hodnota -2

Nastavení výšky hor určuje maximální výšku terénu.

Výchozí hodnota 160

Zaoblení mapy - Určuje členitost terénu. Čím vyšší číslo, tím členitější terén s mnoha prohlubněmi a malými vyvýšeninami.

Nastavení sněhu

Obsahuje nastavení týkající se sněžné čáry. Sněžná čára je zde výška, od které se objevuje na mapě sníh.

Sněžná čára v létě - výška sněžné čáry v létě.

Sněžná čára v zimě - výška sněžné čáry v zimě.

Nastavení podnebí

Umožňuje nastavit na mapě podnebné pásy.
Hodnota vedle jednotlivých podnebných pásů určuje výšku, ve které se daný podnebný pás objeví. Podnebný pás vypnete nastavením hodnoty na 0 (nulu).

Poušť - Velmi málo srážek, vysoké rozdíly v teplotách, málo stromů.

Tropy - Typické pro území okolo rovníku. Relativně stálá vysoká teplota.

Středomoří - Mírně proměnlivé teplé počasí.

Mírné - Podnebný pás se střídáním všech čtyř ročních období.

Tundra - Nízké teploty, hranice polárních čepiček.

Skaliska - Suché klima, chladné zimy.

Ostatní nastavení

Bez stromů - Kliknutím na tlačítko zamezíte vytváření stromů. Vykreslování stromů spotřebovává strojový čas. Tuto vylbu použijte, máte-li pomalejší procesor nebo málo operační paměti.

Počet řek - Nastavuje maximální počet řek, které mohou být vytvořeny při tvorbě nové mapy.

Min. délka řek - Nastavuje minimální délku řek (počet políček).

Max délka řek - Nastavuje maximální délku řek (počet políček).

Nápověda odpovídá verzi 112.1, poslední úprava 4.1.2013.

simutrans-124.5/simutrans/text/cz/color.txt000066400000000000000000000005421520675460500210710ustar00rootroot00000000000000Nápověda - výběr barev

Výběr barev

Zde si můžete vybrat barvu Vaší společnosti. Je to barva, kterou bude mít Váš majetek, jako jsou vlaky, nákladní auta, depa, stanice atd.
Ovšem není to pravidlem, některý majetek tuto barvu mít nebude.

->Informace o překladu

simutrans-124.5/simutrans/text/cz/convoi.txt000066400000000000000000000024511520675460500212510ustar00rootroot00000000000000Nápověda - seznam vozidel

Seznam vozidel

Seznam vozidel obsahuje všechna vozidla daného hráče. Otevřete jej z nabídky seznamy.

Seznam nabízí tyto možnosti:

1.) Řazení:
Postupným klikáním na první přepínací tlačítko měníte kritéria, podle kterých bude seznam řazen: jméno, příjem, typ nebo interní ID.
Vedlejším tlačítkem nastavujete, zda má být řazen sestupně nebo vzestupně.

2.) Filtr:
Kliknutím na třetí tlačítko aktivujete, nebo deaktivujete filtr.
Možnosti tohoto filtru můžete měnit po kliknutí na tlačítko nastavení.

Barva textu s názvem vozidla poskytuje další informace:
- černá - Vše je vpořádku
- červená - Provoz soupravy je prodělečný.
- bílá - Souprava je v depu.
- žlutá - Od začátku roku stojí ve stanice a čeká na náklad.
- oranžová - Zaseklé v dopravní zácpě nebo nemůže najít cestu k cíli.

->Nápověda pro filtr

->Informace o překladu

Nápověda odpovídá verzi 112.1, poslední úprava 21.1.2013.

simutrans-124.5/simutrans/text/cz/convoi_filter.txt000066400000000000000000000021751520675460500226210ustar00rootroot00000000000000Nápověda - filtr seznamu vozidel

Filtr seznamu vozidel

Filtr nabízí tyto možnosti:

1.) Filtrovat jméno:
V seznamu budou zobrazena pouze vozidla, která se jmenují podle zadání.

2.) Filtrovat typ:
V seznamu budou zobrazena pouze vozidla zvoleného typu.

3.) Speciální filtrování:
V seznamu budou zobrazena pouze vozidla, která nemají příjem, nemají definován jízdní plán, nemouhou nalézt trasu dle jízdního plánu, jsou v depu nebo nemají přiřazenou žádnou linku.

4.) Podle přepravovaného zboží:
V seznamu budou zobrazena pouze vozidla, která přepravují zvolené komodity. Tlačítko vše vybere všechny komodity, tlačítko nic naopak všechny odvybere. Tlačítkem inv obrátíte aktuální výběr.

->Nápověda pro seznam vozidel

->Informace o překladu

Nápověda odpovídá verzi 112.1, poslední úprava 4.1.2012.

simutrans-124.5/simutrans/text/cz/convoidetail.txt000066400000000000000000000041001520675460500224250ustar00rootroot00000000000000Nápověda - detaily soupravy

Detaily soupravy

Dialog detaily soupravy poskytuje informace o soupravě/konvoji.

Dialog otevřete kliknutím na tlačítko Detaily v dialogu s informacemi o soupravě.

Tip: Jestliže nejsou všechny položky viditelné změnte velikost okna myší.

Informace zobrazené v levém horním rohu zahrnují:
Výkon soupravy (součet výkonu všech vozidel v soupravě).
Kilometrovník ukazuje počet ujetých kilometrů za celou dobu životnosti soupravy.
Zastavěná plocha je počet políček potřebných ke správnému naložení/vyložení celé soupravy.
Cena za prodej je množství peněz získané při prodeji soupravy.
Nejvyšší možná rychlost je nejvyšší rychlost, jaké může souprava dosáhnout, aniž by překročila nejvyšší rychlost kteréhokoliv z vozidel.

UPOZORNĚNÍ: Při kliknutí na tlačítko Prodávat je celá souprava okamžitě prodána. Žádná další potvrzení není požadováno.

Detaily vozitel v soupravě

V seznamu jsou ukaždého z vozidel zobrazeny následující iformace:
- obrázek vozidla je stejný obrázek, jaký můžete vidět v depu).
- název vozidla je název, pod jakým jste vozidlo kupovali v depu.
- Vyrobeno ukazuje měsíc a rok výroby.
- Cena za prodej jsou peníze získané okamžitým prodejem.
- Výkon je výkon jednotlivých vozidel.
- Převod je číslo, kterým se násobí výkon.
- Okamžitý koeficient tření ukazuje jak moc jsou vozidla bržděna. Při průjezdu zatáčkami a při stoupání je vyšší, než při jízdě po rovině.
- Max. zisk je maximální možný výnos z typu zboží naloženého v tomto vozidle
- naložené položky včetně množství.

Nápověda odpovídá verzi 112.1, poslední úprava 21.1.2013.

simutrans-124.5/simutrans/text/cz/convoiinfo.txt000066400000000000000000000141751520675460500221330ustar00rootroot00000000000000Nápověda - informace o soupravě

Informace o soupravě

Dialog informace o soupravě poskytuje údaje o soupravě/konvoji a přepravovaném nákladu, obsahuje tlačítka sloužící k ovládání nebo sledování konvoje a zobrazuje náhled.

Soupravou/konvojem je myšleno vozidlo nebo skupina vozidel spřažených v jeden celek. Konvoj se skládá z aspoň jednoho vozidla opatřeného pohonem (motorizované jednotky) a několika tažených vozidel. Konvojem je například vlak, auto s přívěsem nebo autobus.

Dialog otevřete kliknutím levým tlačítkem myši na vozidlo. Také jej lze zobrazit ze seznamu vozidel nebo ze seznamu linek.

Konvoje se sestavují a kupují v depu, které odpovídá příslušnému druhu přepravy.

Poznámka: Počet vozidel v konvoji je omezen. Jeden konvoj může osahovat maximálně 24 železničních vozidel, u silniční dopravy jsou to 4 vozidla.

V titulku okna je zobrazen jedinečný identifikátor a jméno konvoje. Horní část dialogu informace o soupravě obsahuje tyto údaje:

vnitřní ID: ID je identifikátor přiřazený při sestavení konvoje v depu.

název: Pole umožňuje změnu názvu. Jako výchozí jméno je použito označení prvního vozidla zakoupeného nebo sestaveného v depu.

rychlost: Rychlost v km/h, jakou vozidlo právě jede. V závorce je maximální dovolená rychlost (maximální rychlost nejpomalejšího vozidla). Zelená čára je grafické procentuální znázornění aktuální rychlosti.

příjem: Jsou peníze vydělané provozem konvoje (příjmy po odečtení provozních nákladů) v aktuálním kalendářním roce. V závorce jsou provozní náklady na 1km.

hmotnost: součet hmotnosti vozidla a hmotnosti nákladu, v závorkách je hmotnost nákladu (v metrických tunách). Zelená čára ukazuje aktuální obsazenost, žlutá obsazenost požadovanou podle nastavení v jízdním řádu.

místo určení: Ukazuje název příští zastávky nebo bodu na cestě v jízdním řádu konvoje. Zelený progress-bar ukazuje, kolik zbývá cesty do dalšího bodu.

obsluha linky: Linka, ke které je konvoj přiřazen. Tlačítko na začátku řádku zobrazí správu linek. (Je dostupné pouze u vozidel přiřazených k lince.)

náhled: Kliknutím na náhled se přemístíte na pozici konvoje.

Tip: K nastavení nebo změně čekání pro všechna vozidla dané linky použijte dialog správa linek. Ke změně jízdního řádu jen u tohoto vozidla použijte tlačítko jízdní řád. Používejte linky, je to přehlednější a jednodušší na správu.

tlačítka uprostřed

jízdní řád: Kliknutí otevře okno s nastavením jízdního řádu. Zde nastavíte délku čekání a jak moc musí být vozidlo naloženo.

do depa: Pošle konvoj do nejbližšího vhodného depa. Při vjezdu do depa přijdete o všechny převážené položky. Peníze za přepravu jsou počítány jako při přepravě do místa s depem.
V depu si konvoj zachová svůj jízdní řád a přiřazenou linku.

nenakládat: Zakáže do vozidla nakládat, zboží je v zastávkách/stanicích pouze vykládáno. Tlačítko je výhodné použít, když chcete například poslat osobní vlak do depa (a přidat k němu několik vagonů) a nechcete, aby vám ze stanice zmizeli cestující.

následuj: Zapne nebo vypne sledování pohybů vozidla.
Sledování vypnete opětovným kliknutím na tlačítko nebo kliknutím na mapu.

graf: Kliknutím na tlačítko zobrazíte/skryjete graf.
Graf obsahuje údaje za posledních 12 měsíců. Jednotlivé křivky zobrazíte kliknutím na příslušné tlačítko pod grafem. Barva čáry v grafu odpovídá barvě tlačítka pod grafem.
- Volná kapacita nevyužité místo v konvoji.
- Přepraveno množství zboží, cestujících a pošty, které bylo přepraveno.
- Příjem peníze zaplacené za přepravu.
- Provoz vozidel náklady na provoz vozidel.
- Zisk zisk z přepravy ( příjem minus provoz).
- Délka/Měsíc počet ujetých kilometrů v daném měsíci.
- Nejvyšší rychlost Teoretická rychlost soupravy, pokud vezmeme v úvahu rychlostní limity vozidel a cesty/kolejí. Je to minimum z maximální rychlosti komunikace a maximální rychlosti soupravy. Používá se k výpočtu bonusu za rychlost přepravy.

Detaily: Otevře dialog detaily soupravy obsahující podrobné informace o jednotlivých vozidlech. Umožňuje také prodat celou soupravu.

Řadit zboží dle

Obsahuje informace o přepravovaném množství, kapacitě konvoje, typ položek, cílové stanici a první přestupní stanici.
Tlačítko přepíná mezi těmito způsoby řazení:
- cíl: Setřídí *) převážené položky podle názvu cílové stanice.
- přes: Setřídí *) převážené položky podle jména první přestupní stanice.
- přes+počet: Setřídí převážené položky podle množství směřujícího do první přestupní stanice.
- počet: Setřídí položky podle počtu.

*) Pozn: Položky jsou řazeny podle ASCII-kódu, tedy v pořadí: čísla, velká písmena bez diakritiky, malá píslena bez diakritiky. Znaky s diakritikou nejsou v ASCII tabulce ale jen v jejích rozšířeních (např. latin2) a mají přiřazena vyšší čísla. Proto jsou při řazení bude Ostrava před Českými Budějovicemi.

Nápověda odpovídá verzi 112.1, poslední úprava 17.2.2012.

simutrans-124.5/simutrans/text/cz/curiosity_build.txt000066400000000000000000000047251520675460500231730ustar00rootroot00000000000000Nápověda - přidat kuriozitu.

Stavba památek

Dialog slouží k ručnímu přidání památek do krajiny. Je rozdělen na čtyři části.

V levém horním rohu je seznam dostupných budov.
Vlevo dole je zobrazen obrázek vybrané budovy.
Vpravo nahoře jsou tlačítka sloužící k rozšíření nebo zúžení výběru dostupných památek.
Vpravo dole jsou zobrazeny podrobnosti o vybrané budově.

Seznam budov

V seznamu jsou vypsány všechny zvláštní budovy a památky, které jsou dostupné podle kritérií nastavených na pravé staně dialogu (městské památky, venkovské památky, památníky). Obě záložky obsahují stejné památky, jen jinak označené.
- Podle jména: Záložka obsahuje budovy setříděné podle názvu uvedeného v překladu. Pokud není překlad dostupný, použije se identifikátor.
- Podle ID: Pod touto záložkou jsou budovy řazené podle vnitřního identifikátoru pro daný objekt.

Budovy jsou rozlišeny pomocí rozdílné barvy textu:
- modrá barva: památky, které lze postavit jen ve městě
- zelená barva: památky, které lze postavit jen na venkově
- černá barva: památníky; na každé mapě může být jen jeden

Volby

- ignorovat podnebí: Každá památka má omezení, ve kterých podnebných pásech může být postavena. Tímto tlačítkem lze omezení vypnout.
- Začít roku: Seznam bude obsahovat pouze budovy které jsou již v daném roce dostuné. V seznamu nebudou budovy, které se objeví v příštích letech.
- Zobrazit zastaralá: V seznamu se zobrazí i budovy v dané době považované již za zastaralé.
- Městské památky: Zobrazí v seznamu památky, které lze postavit ve městě.
- Venkovské památky: Zobrazí v seznamu památky, které lze postavit mimo území města.
- Památník: Zobrazí v seznamu památníky (budovy, které mohou být na mapě jen jedenkrát).
- rotace: Jestliže daná budova umožňuje pohled z více úhlů, můžete pomocí šipek mezi nimi přepínat. Tím se bude měnit obrázek v levém dolním rohu. Jestliže je zobrazen nápis náhodně, použije se náhodně vybraný pohled.

Nápověda odpovídá verzi 112.1, poslední úprava 5.1.2012.

simutrans-124.5/simutrans/text/cz/curiositylist_filter.txt000066400000000000000000000036451520675460500242550ustar00rootroot00000000000000Nápověda - turistické cíle

Turistické cíle

Seznam turistických cílů obsahuje informace o všech památkách. Památky jsou zdrojem cestujících a pošty i jejich cílem.

Dialog otevřete z panelu seznamy.

Řadit podle: Seznamy jsou řazeny podle různých kritérií. Způsob řazení změníte kliknutím na příslušné tlačítko.

- Jméno Setřídí seznam podle názvu.
- Vzrůst osob řazeno podle množství cestujících a pošty.

- Vzestupně / sestupně Přepíná mezi vzestupným a sestupným řazením seznamu.

Kliknutím text položky ze seznamu zobrazíte okno s podrobnými informacemi o daném objektu.

Kliknutím na tlačítko na začátku řádku se přemístíte k dané památce.

U každé památky jsou uvedeny následující informace:

Barevný indikátor znázorňuje dosažitelnost.

- žlutá barva: ve spádovém území se nenachází žádná stanice nebo zastávka.
- zelená barva: U památky se nachází aspoň jedna zastávka pro cestující.
- modrá barva: U památky se nachází aspoň jedna zastávka odbavující poštu.
- tyrkysová (světle modrozelená) barva: U památky je aspoň jedna zastávka pro cestující a aspoň jedna zastávka pro poštu.
- oranžová barva: Zastávka u památky je přeplněná.
- červená barva: Všechny zastávky u památky jsou přeplněné.

Ikona budovy před názven památky označuje městskou památku.

název památky

vzrůst osob: Hodnota v závorkách označuje relativní oblíbenost památky jako cíl cesty pro cestující.

Nápověda odpovídá verzi 112.1, poslední úprava 5.1.2012.

simutrans-124.5/simutrans/text/cz/depot.txt000066400000000000000000000102701520675460500210650ustar00rootroot00000000000000Depo

Dialog depa

V depu můžete nakupovat, prodávat, spojovat či přepojovat do konvojů (souprav) všechna vozidla zaparkovaná v depu. Dialog depa vám zobrazuje, jaká vozidla můžete nakoupit a jaká jste již nakoupil.

Směrem shora dolů se okno depa skládá z několika pruhů s různými funkcemi.

Úplně nahoře je počitadlo, zobrazující pozici v celkovém seznamu konvojů v depu. Šipkami u prvního textového pole lze mezi zaparkovanými konvoji přepínat. Textové pole zobrazuje jejich název, který je zde možno i upravit.

Pod tímto se nalézá totožný prvek, umožňující výběr linky. Více o linkách viz dále.

V oblasti mezi textovými poli a záložkami je zobrazen právě aktivní konvoj a podrobnosti o něm. Udán je zde počet vozidel v konvoji, potřebná délka stanice, hodnota konvoje při prodeji, dosažitelná rychlost naprázdno (a s nákladem) a nakonec linka, kterou konvoj obsluhuje.

Kliknutím na některé vozidlo zobrazeného konvoje je toto vozidlo "zakonzervováno" - přestává být součástí konvoje a je vidět pouze jako číslice nad jeho druhem v tabulce dostupných vozidel. Odtud může být znovu vzato a zařazeno do jiného konvoje.

Dále je zde sada tlačítek pro práci s vybraným konvojem. Úplně nalevo je tlačítko start, kterým konvoj "pustíte" z depa - pokud již má nastaveno jízdní plán. Dalším tlačítkem jízdní řád jej lze upravit individuálně pro vybraný konvoj. Tlačítkem rozebrat můžete celý konvoj rozpojit a jednotlivá vozidla zakonzervovat. Tlačítkem prodat prodáte celý aktuální konvoj.

Druhá řádka tlačítek usnadňuje práci při tvorbě většího množství konvojů. Tlačítko nová linka založí novou linku, otevře okno pro úpravu jejího jízdního plánu a vybere ji v seznamu výše. Tlačítkem přiřadit linku je aktuální konvoj přiřazen na vybranou linku. Změnit linku umožňuje upravit jízdní řád vybrané linky. Kopírovat vytvoří duplikát aktuálního konvoje z vozidel dostupných v depu nebo nově nakoupených, včetně přiřazení linky nebo nastavení jízdního plánu.

V tabulce uprostřed se záložkami jsou zobrazena koupitelné druhy vozidel. Seznam může mít celkem 4 karty:
* vozidla pro osobní dopravu,
* elektrické osobní soupravy (tramvaje, trolejbusy, el. jednotky...),
* pohonná vozidla (lokomotivy, tahače apod.) a
* připojitelná vozidla (vagóny a přívěsy).
Pokud nejsou v některých z těchto kategorií dostupná žádná vozidla, příslušná záložka nemusí být zobrazena. Zakonzervovaná vozidla jsou zde zobrazena velkou číslicí nad ikonou vozidla.

Pod tabulkou je trojice prvků měnících její obsah a zacházení s ním. Zobrazit vše přepíná mezi zobrazením všech vozidel či pouze těch, která lze zařadit na konce aktuálního konvoje. Zobrazovat zastaralá ukáže v depu i již nevyráběné druhy vozidel.

Tlačítko přepíná druh zacházení s tabulkou koupitelných vozidel při kliknutí na její položku. Přidat koupí daný druh vozidla a připojí jej na konec aktuálního konvoje. Pokud je k dispozici zakonzervovaný kus, je použit namísto nákupu nového. další režim je na začátek, který je totozžný, ale vozidlo se přidá na začátek místo na konec. Poslední možnost je prodat, která umožňuje prodávat vozidla zakonzervovaná ve vašem depu.

Úplně dole jsou zobrazeny informace o vozidle, jehož ikona je pod kurzorem.

K vytvoření konvoje je vhodné postupovat od předu ke konci, tedy např. nejprve koupit lokomotivy a poté přikupovat vagóny. Najednou je možné pracovat pouze s jedním konvojem. Přesun vozidel mezi konvoji je možný pomocí zakonzervování (viz výše).

Viz také: ->Nápověda k jízdnímu plánu

simutrans-124.5/simutrans/text/cz/display.txt000066400000000000000000000023761520675460500214270ustar00rootroot00000000000000Nápověda - nastavení zobrazení

Nastavení zobrazení

Dialog "Nastavení zobrazení" nabízí možnost změny zobrazování různých vizuálních prvků v Simutrans.

Jas - Přidá nebo ubere jas
Barvy - Testovací prvek, bez efektu
Rychlost posuvu - Velikost tohoto čísla udává rychlost posuvu obrazovky (pomocí myši nebo pomocí šipek)

Inverze posuvu - Způsobí, že posuv nahoru posune obrazovku dolu, posuv doleva ji posune doprava atd.
Zobrazovat chodce - Způsobí, že se ve městech nebudou na ulicích zobrazovat chodci (ti kteří chodí do nebo ze zastávek autobusu).
Výhodné, např. pokud máte pomalejší počítač. Náhodné chodce lze vypnout v konfiguračním souboru simuconf.tab.
Střídat den a noc - Aktivuje střídání simulace dne a noci v průběhu hry. Při simulaci noci některé objekty ve hře (vozidla, domy...) svítí.

Dolní část dialogu obsahuje různé informace o současném stavu hry, které jsou zde hlavně z testovacích a vývojářských důvodů.

->Nápověda pro nastavení hry

->Informace o překladu

simutrans-124.5/simutrans/text/cz/edittools.txt000066400000000000000000000120451520675460500217620ustar00rootroot00000000000000Nápověda - ediatace Mapy

Editace mapy

Nástroje pro editaci mapy slouží k úpravě již hotové mapy. Nástroje umožňují změnu počtu obyvatel měst, stavbu měststých silnic, památek, elektráren a průmyslu, změnu aktuálního hráče a přidání jednoho roku ke stávajícímu datu.

Nabídku s nástroji otevřete pokud jsete přihlášen jako hráč Veřejné služby. K přepínání mezi hráči slouží tlačítko P+ v nabídce se speciálními stavbami nebo klávesa [P]).
Po najetí kurzorem myši nad jednotlivé nástoje se zobrazí název nástroje, popřípadě i doplňující informace, k danému nástroji.

Nástroje zahrnují,tyto položky. Pořadí položek se může lišit v závislosti na jednotlivých grafických sadách (anglicky paksets).

Zvětšit město: Nástroj zvýší počet obyvatel města přibližně o 100 obyvatel. Jsou postaveny nové silnice a budovy.
Použití: Klikněte na nástroj a pak klikněte na město.

Zmenšit město: Nástroj umožňuje snížit počet obyvatel daného města. Počet obyvatel se sníží o 100.
Použití: Klikněte na nástroj a pak klikněte na město.

Založit nové město: Nástroj slouží k založení nového města.
Použití: Klikněte na nástroj a pak klikněte na pole, kde chcete mít nové město. Město se postaví na zvoleném poli nebo v jeho blízkosti.

Zasadit strom *: Nástroj umožňuje osázet terén stromy. Klikněte na nástroj a pak na místo v krajině.

Založit les * Nástroj slouží k výsadbě lesa. Klikněte na nástroj a pak dvakrát klikněte na různá místa v terénu. Prostor mezi nimi bude osázen tromy.

Městská silnice: Nástroj slouží ke stavbě městské silnice. Při umístění kurzoru nad ikonu se zobrazí cena a v závorce cena za údržbu. Použití: Městská silnice se staví stejným způsobem, jako ostatní silnice. Klikněte na symbol městské silnice, pak klikněte na začátek a výstavbu dokončete kliknutím na konec. Cestu se nepodaří vytvořit, pokud se mezi začátkem a koncem nachází překážka (řeka, železnice, členitý terén atd.)
Tip: Nerovnosti odstraníte pomocí nástrojů na úpravu terénu, řeku překonáte mostů, skrz kopec se dostavete pomocí tunelu. Druhým možný způsob: Po kliknutí na začátek držte levé tlačítko myši. Umístěte kurzor na konec budoucí cesty a pusťe tlačítko.

Tip: Nechcete-li, aby se souběžné cesty spojovaly, mějte v průběhu pokládání stlačenu klávesu Ctrl.

Řeka *: Nástroj umožňuje přidat do krajiny řeku. Máte na výběr z více možností, od potoka po veletok.

Zahustit průmysl: Po kliknutí na tlačítko se objeví na mapě další továrny.

Přidat továrnu: Kliknutí na tlačítko otevře dialog pro přidání továrny.

Propojení továren: Aby bylo mezi továrnami dodáváno zboží, musejí mít továrny mezi sebou spojení (uzavřený kontrakt). Automaticky vytvořené továrny toto spojení mezi sebou mají. Pokud ručně přidáte do hry továrnu, potřebujete také nastavit, odkud má odebírat suroviny a kam dodávat výrobky. K tomu slouží toto tlačítko.
Klikněte na tlačítko (změní se kurzor). Pak klikněte postupně na obě továrny, které mají uzovřít kontrakt.

Přidat kuriozitu: Nástroj slouží k přidání památek, pomníků a jiných zajímavostí do krajiny. Tyto objekty přitahují turisty a poštu.
Po kliknutí na ikonu se zobrazí okno s výběrem konkrétní budovy.

Přidat městskou budovu: Kliknutím otevřete dialog s nabídkou městských budov.

Znemožnit další změnu hráčů: Nástroj slouží k zamezení přepínání mezi různými hráči. Aby šel tento nástroj použít, musí mít hráč nazývaný Veřejné služby nastavené heslo.
Použití: Klikněte na ikonu nástroje a pak klikněte kamkoliv na herní plochu.

Přidej jeden rok: Přidá jeden rok ke stávajícímu letopočtu. Pokud jste použili volbu začít roku, mohou se objevit nová dostupná vozidla a některá ze stávajících vozidel mohou být označena za zastaralá. Pomalejší vozidla mohou snížit zisk a nebo začít prodělávat, protože výnos z přepravy závisí na průměrné rychlosti.
Pro použití klikněte na tlačítko.

Změnit velikost mapy: Kliknutí na nástroj otevře dialog sloužící ke zvětšení rozměrů mapy.

* Nástroj není dostupný ve všech grafických sadách (anglicky paksets).

simutrans-124.5/simutrans/text/cz/enlarge_map.txt000066400000000000000000000016201520675460500222230ustar00rootroot00000000000000Nápověda - Změnit velikost mapy

>Změnit velikost mapy

Dialog slouží k rozšíření již vytvořené mapy.

Mapu lze rozšířit směrem vpravo a směrem dolu. Pokud chcete mapu rozšířit jiným směrem, otočte ji, zvětšete ji a otočte zpět.
Tip: K otočení použijte klávesu R.

Budoucí velikost mapy lze zadat pomocí klavesnice nebo šipkami na stranách políčka.
Horní políčko slouží k zadání rozměru zleva doprava, druhé políčko k zadání rozměru shora dolu.

Největší čtvercová mapa má delku strany 4096 políček, největší obdélníková mapa má delku strany 8192 polí (při délce kratší strany 2048 polí).

Počet měst je počtem nově vytvořených měst.

Medián ovlivňuje počet obyvatel v nově vytvářených městech.

simutrans-124.5/simutrans/text/cz/factory_build.txt000066400000000000000000000045431520675460500226060ustar00rootroot00000000000000Nápověda - přidat továrnu

Přidat továrnu

Dialog umožňuje různě umisťovat továrny na mapu. Otevřete jen z menu editace mapy.

Okno dialogu je rozděleno do čtyř částí.
V levém horním rohu se nachází seznam továren.
V levém dolním rohu se zobrazí obrázek naposledy zvolené továrny
V pravém horním rohu jsou tlačítka pro zapnutí/vypntí filtrů jednotlivých druhů továren.
V pravém dolním rohu (pod tlačítky) se nachází podrobné informace o vybrané továrně.

Seznam továren

V seznamu jsou vypsány všechny továrny. Obě záložky obsahují stejné továrny, seznamy se liší jen pořadím.
- Podle jména: Záložka obsahuje továrny setříděné podle názvu uvedeného v překladu. Pokud není překlad dostupný, použije se identifikátor (ID).
- Podle ID: Zde jsou továrny řazené podle vnitřního identifikátoru daného objektu.

Barvy textu mají následující význam:
- zelený text: Továrna zboží pouze vytváří, nic není nutno dovážet.
- černý text: Továrna spotřebovává suroviny a produkuje výrobky.
- modrý text: Továrna je pouze spotřebitelem zboží.

Volby

Ignorovat podnebí: Vypne omezení, ve kterých podnebných pásech lze továrnu postavit.

Začít roku: V seznamu jsou zobrazeny pouze továrny dostupné v danou dobu.

Zobrazovat Zastaralá: V seznamu jsou zobrazeny i již zastaralé továrny.

Pouze městský průmysl: Zobrazí se továrny, které lze postavit pouze ve městě.

Pouze venkovský průmysl: Zobrazí se pouze továrny stavěné mimo města. Když je zatrženo, staví se celý řetěz průmyslu. To znamená, že kromě zvolené továrny se postaví i její dodavatelé.

Rotace: Jestliže má daná továrna umožňuje pohled z více úhlů, můžete mezi nimi přepínat pomocí šipek. Přepínání ovlivňuje obrázek v levém dolním rohu. Jestliže je zobrazen nápis náhodně, použije se náhodně zvolený podled.

Výroba: Zde nastavíte rozsah výroby. Číslo určuje počet vyrobených/spotřebovaných kusů/tun/metrů krychlových za jeden měsíc.

simutrans-124.5/simutrans/text/cz/factorylist_filter.txt000066400000000000000000000053141520675460500236650ustar00rootroot00000000000000Seznam továren

Seznam továren

Seznam továren obsahuje informace o všech továrnách umístěných na herní ploše.

Seznam továren otevřete z menu Seznamy.

Řadit podle: Volba ovlivňuje pořadí zobrazených továren.
Kliknutím na tlačítko přepínáte mezi následujícími možnostmi:

- Název továrny Řadí se abecedně podle názvu.
- Příjem Řadí podle množství přijatého zboží na skladě.
- Odběr Řadí podle množsví výrobků čekajících na odvoz.
- Výroba Pořadí je podle aktuální výrobní kapacity.
- Stav Seřadí továrny podle barevného indikátoru stavu.
- Výkon Seřadí továrny podle zvýšení výkonu způsobeného dodáním elektrické energie.

- Vzestupně / sestupně: Přepíná mezi vzestupným a sestupným řazením seznamu.

továrny

Kliknutím na položku v seznamu zobrazíte podrobnější informace o dané továrně.

U každé továrny jsou zobrazeny následující položky:

barevný indikátor stavu: Barvy vypovídají o výrobě v dané továrně. Stejné barvy jsou užity v informacích o továrně.
- bílá barva: Továrna nic nepožaduje nebo nemá kontrakt s žádným dodavatelem.
- žlutá barva: Továrna má odběratele, ale nikdo od ní nedopravuje zboží.
- zelená barva: Optimální stav, továrna vyrábí.
- oranžová barva: V provozu, sklad s aspoň jednou surovinou je plný, ale zatím je pro koho vyrábět a ve skladu s výrobky je volné místo.
- červená barva: Hodně surovin na skladu a není kam dodávat výrobky (mají plno).

červený blesk je zobrazen u továren připojených k elektrické síti.

symbol cestujících: Továrna leží ve spádové oblasti aspoň jedné zastávky pro cestující.

symbol pošty: Továrna leží ve spádové oblasti aspoň jedné zastávky pro poštu.

název továrny: Název továrny uvedený v překladu.

Hodnoty uvedené v závorkách mají následující význam:
- přijaté zboží: Množství vstupních surovin čekajících na zpracování.
- čeká na odvoz: Množství hotových výrobků čekajících na odvoz.
- aktuální produkce: aktuální míra produkce - kolik výrobků při současném tempu výroby je továrna schopna vyrobit za měsíc.

simutrans-124.5/simutrans/text/cz/finances.txt000066400000000000000000000056401520675460500215450ustar00rootroot00000000000000Nápověda - finance

Finance (klávesová zkratka: f)

V dialogu finance naleznete tabulku rozpisu vašich příjmů a výdajů za tento a minulý rok, a také graf vybraných položek.

Tabulku příjmů lze zobrazit po měsících nebo po letech. Údaje lze zobrazit i jen pro jednotlivé způsoby přepravy (od verze 112.3). Hodnoty jednotlivých následujících položek jsou zobrazitelné v grafu.

levý sloupec

Přepraveno - Kolik zboží, cestujících a pošty jste přepravili.

Příjmy - Součet peněz, vydělaných přepravou zboží a cestujících

Provoz vozidel - Provozní náklady za všechna vozidla

Údržba - Výdaje za udržování vašich staveb v dobrém stavu - opravy, úklid atd. Čím více tratí, cest, mostů, tunelů a staveb, tím je větší tato částka.
Jak vysoká je tato částka na tento měsíc, je uvedeno na posledním řádku dialogu.
Tato částka vám bude odečtena najednou, na začátku každého měsíce. Vozidel se tato položka (zatím) netýká.
(Měsíční náklady na údržbu vozidel jsou v simutrans experimental).

Mýto - Mýtné za jízdu vašich vozidel po silnicích jiných hráčů (platíme) a za jízdu cizích vozidel po vašich silnicích (je nám placeno). Výši mýtného lze ovlivnit v nastavení při vytváření nové mapy v záložce Hospodářství a města volbami toll_running_cost_percentage a toll_waycost_percentage.

Zisk z vozidel - Součet předchozích čtyř položek včetně znaménka. (Příjmy + provoz vozidel + údržba + mýto)

Nová vozidla - Příjmy a výdaje za prodaná či zakoupená vozidla

Stavby - Součet výdajů za vše co jste postavili - železnice, silnice, stanice atd.

Hosp. výsledek - Celkový součet příjmů a výdajů za tento popř. minulý rok

pravý sloupec

Zůstatek - Váš celkový zůstatek peněz - stav vašeho konta.

Majetek - součet cen všeho vašeho majetku - vozidel. Budovy, silnice, železnice, apod. se nepočítají. Cena každého objektu postupně časem klesá.

Výdělek - zisk z vozidel dělený příjmy

Jmění - součet zůstatku a hodnot všech vozidel

Grafy

Stiskem tlačítek v pravé horní části dialogu můžete zobrazit vývoj zvolené hodnoty za současný rok a předcházejících 24 let. (Od verze 112.3, dříve jen 11 let)
Význam hodnot v grafu po stisknutí patřičných tlačítek je stejný jako v tabulce příjmů a výdajů.

->Informace o překladu

Nápověda odpovídá verzi 112.3, poslední úprava 31.12.2013.

simutrans-124.5/simutrans/text/cz/goods_filter.txt000066400000000000000000000057351520675460500224440ustar00rootroot00000000000000Seznam zboží

Seznam zboží

Seznam zboží obsahuje informace o různých věcí, které je možné transportovat po mapě. Dále je možné ovládat výdělek za jednotlivé zboží.

Seznam je dostupný: Buď kliknutím na nářadí 'Seznam zboží' ve skupině Seznamy nebo tlačením [G].

Listina přístupných zboží na transport obsahuje detaily: Příjem, % bonus za rychlost, kategorie zboží a váhu.
{Tip: Když neni celý text zobrazený, pomůže zvětšení okna Seznam zboží anebo použití posuvníků.}

Bonus za rychlost: Dostupnost bonusu je závislá na typu vozidla. Listina obsahuje rychlosti, které jsou nutné dosáhnout soupravou, aby byl vyplacen zvláštní příjem za rychlou dodávku za zboží nebo za cestující (jsou dokonce i případy, že je příjem odebran za pomalou dodávku).
Když hrajete s časovou osou, Simutrans nutně upravuje rychlosti kvůli dostupnosti nových, rychlejších vozidlech.
Šipky naladějí příjmy a rychlosti bonusového systemu.

Řadit podle: zmožnuje změnění řazení záznamů listiny.
Klikněte tlačítka na zvolení řazení (nápis tlačítka se změní):

- Nic netřídí záznam.
- Podle názvu třídí jména podle abecedy.
- Podle příjmu třídí podle vydělku za transport jednotky zboží o jedno políčko.
- Podle bonusu třídi podle % bonusu za rychlost (% dodatečného příjmu, když použitá souprava dosáhne nutnou rychlost pro získání bonusu).

- Vzestupně / Sestupně mění pořadí třízení.

Jsou zobrazené detaily pro každý záznam:

Barevný čtverec má stejnou barvu jako hrana nad stanicí ve hře, ukazující příjem zboží a čekajicí zboží.
{Tip: Tiskněte [!] ve hře na střídání hran na příjem zboží nad stanicemi.}

Příjem znamená výdělek za transport jednotky zboží o jedno políčko.
{Tip: Hrajte začátečnický mód na zvýšení příjmu až do 150% (můžete nastavit v simuconf.tab)}

% Bonus je % dodatečného příjmu, když souprava překročí nutnou rychlost na získ rychlostného bonusu.
Kalkulační vzorec na příjem je komplexní a bere ohled na maximální rychlost soupravy a srovnává jí s maximalní rychlosti všech dostupnćh vozidel (nejen existujicí ve hře).

Kategorie zboží ukazujicí specifikované typy vozidla na transport tohoto zboží.
{Tip: Kapacita vozidel naznačuje, které zboží mohou transportovat.}

Hmotnost v kilogramech.

simutrans-124.5/simutrans/text/cz/haltlist.txt000066400000000000000000000012341520675460500215760ustar00rootroot00000000000000seznam stanic

Seznam stanic

Seznam nabízí tyto možnosti:

1.) Řazení:
Postupným klikáním na první přepínací tlačítko měníte kritéria, podle kterých bude seznam řazen.
Vedlejším tlačítkem nastavujete, zda má být řazen sestupně nebo vzestupně.

2.) Filtr:
Kliknutím na třetí tlačítko aktivujete, nebo deaktivujete filtr.
Možnosti tohoto filtru můžete měnit po kliknutí na tlačítko nastavení.

->Nápověda pro filtr

->Informace o překladu

simutrans-124.5/simutrans/text/cz/haltlist_filter.txt000066400000000000000000000021151520675460500231420ustar00rootroot00000000000000Filtr seznamu stanic

Filtr seznamu stanic

Filtr nabízí tyto možnosti:

1.) Filtrovat jméno:
V seznamu budou zobrazeny pouze stanice, které se jmenují podle zadání.

2.) Filtrovat typ:
V seznamu budou zobrazena pouze stanice zvoleného typu.

3.) Speciální filtrování:
V seznamu budou zobrazeny pouze stanice, které nemají spojení nebo jsou přeplněné.

4.) Podle vyžadovaného průmyslu:
Seznam zobrazí pouze stanice, které vyžadují pro průmysl na který jsou napojeny zvolené komodity. *

5.) Podle produkovaného průmyslu:
Seznam zobrazí pouze stanice, které produkují zvolené komodity. *

* Tlačítko vše vybere všechny komodity, tlačítko nic naopak všechny odvybere. Tlačítkem inv obrátíte aktuální výběr.

->Nápověda pro seznam stanic

->Informace o překladu

simutrans-124.5/simutrans/text/cz/industry_info.txt000066400000000000000000000135741520675460500226600ustar00rootroot00000000000000Informace o továrně

Informace o továrně

Dialog informace o továrně zobrazuje detaily o vybrané továrně, jejích dodavatelích a odběratelích a umožňuje přemístit se na pozice s továrnou.

Dialog vyvoláte kliknutím inspekční lupou na továrnu nebo na název v seznamu továren.

Továrny mohou fungovat osamoceně (např. větrná elektrárna) nebo být součástí rozsáhlého průmyslového řetězu. Mohou výt čistě producenty primárních surovin (uhlí, železná ruda, ...), zpracovatelé (ocelárna, pila) nebo koncoví spotřebitelé.
Továrny jsou také cílem cesty pro cestující a poštu.

{Tip: Stačí, aby ve spádové oblasti stanice/zastávky bylo jen jedno pole továrny.}

Počáteční množství řetězů průmyslu lze zadat při vytváření nové mapy. Jak města rostou, objevují se další továrny. O kolik musí vzrůst počet obyvatel, aby se na mapě objevila další továrna závisí na nastavení.

V titulku je název vybrané továrny.

V poli na prvním řádku lze měnit název továrny, např. Uhelný důl Adéla.

Za textem Max. se nachází maximální produkce továrny za jeden měsíc. (Nebo den, záleží na nastavení zobrazení času.) Množství vyprodukovaných výrobků můžete zvýšit dodáním elektrické energie cestujících a pošty. Zda-li ke zvýšení produkce (a o kolik) dojde záleží na konkrétní továrně. Tyto informace zjistíte po kliknutí na tlačítko graf v záložce výroba/zvýšeno

Výroba: Informace o typech výrobků a jejich množství.
Informace obsahují název výrobku, množství výrobku na skladě, kapacitu skladu pro daný výrobek, kategorii (v čem se dá zboží přepravovat) a procentuelní poměr v jakém je vyráběn.

spotřeba: Informace o vstupních surovinách a jejich množství.
Informace obsahují název suroviny, množství na skladu, kapacitu skladu pro danou surovinu, kategorii zboží a procentuelní poměr vstupních surovin.

Kliknutí na Obrázek továrny vás přemístí se na pozici s továrnou.

barevný indikátor stavu pod obrázkem továrny má stejný význam jako v seznamu továren:
- bílá barva: Továrna nic nespotřebovává nebo nemá uzavřen kontrakt s žádným dodavatelem.
- žlutá barva: Továrna má uzavřeny kontrakty, ale není u ní žádná stanice nebo zastávka.
- zelená barva: Optimální stav, továrna vyrábí.
- oranžová barva: V provozu, sklad s aspoň jedním výrobkem je plný, ale zatím je pro koho vyrábět nebo je dost místa ve skladu na hotové výrobky.
- červená barva: U továrny jsou zastávky ale zboží se z nich nedopravuje nebo odběratelé mají plné sklady surovin nebo v provozu ale sklady surovin jsou přeplněné.

Tlačítko graf zobrazí nebo skryje panely s grafy k příslušné továrně.

Tlačítko detaily otevře okno s podrobnostmi o daném typu továrny; pokud typ továrny nemá nápovědu, tlačítko není zobrazeno.

Odběratelé: Seznam odběratelů zboží z dané továrny. V závorkách za názvem továrny jsou její souřadnice.
Kliknutím na šipku na začátku řádku vás přemístí k zvolenému odběrateli.

Dodavatelé: Seznam dodavatelů surovin pro danou továrnu. V závorkách za názvem továrny jsou uvedeny její souřadnice.
Kliknutím na šipku na začáku řádku se přemístíte k příslušnému dodavateli.

Zaměstnanci bydlí v: Ukazuje ve kterých okolních městech žijí zaměstnanci. Z těchto měst můžete k továrně dopravovat cestující a poštu.
Šipky před názvy měst služí k přemístění do příslušného města.

Zastávky v dosahu objektu: Zastávky, v jejichž spádovém území se továrna nachází. Na ně lze dopravovat cestující, poštu a suroviny pro továrnu.

graf - zboží

Pod grafem je tabulka, v vprním sloupci je název vyrobeného zboží nebo spotřebované suroviny. V dalších sloupcích jsou tlačítka sloužící k zobrazení/skrytí hodnot v grafu.
- zásoby: množství suroviny/výrobku na skladu
- došlá pošta !?(přijato): množství suroviny přivezené v daném měsíci
- spotřebováno: množství spotřebované suroviny v daném měsíci
- doručeno: množství zboží vyexpedovaného na okolní zastávky
- vyrobeno: množství vyrobeného zboží

graf - výrobky/zvýšeno

Záložka spotřeba/zvýšeno obsahuje tato tlačítka:
- výroba: Kolik výrobků je továrna schopna měsíčně vyrobit
- využito/výkon: Kolik bylo využito elektrické energie.
- zvýšení produkce: Jak moc (o kolik procent) byl výkon továrny zvýšen dodáním elektrické energie, cestujících a pošty
- Max.zvýšení produkce: O kolik procent lze nejvíce ovlivnit výkon továrny dodáním elektrické energie, cestujících, pošty.
- poptávka: Kolik MW přikonu elektrické energie, cestujících a pošty elektrárna potřebuje pro optimální výkon.
- cestující: Množství vyprodukovaných, odbavených a přijatých cestující.
- pošta: Množství vyprodukované, odbavené a přijaté pošty.

simutrans-124.5/simutrans/text/cz/inspection_tool.txt000066400000000000000000000150521520675460500231650ustar00rootroot00000000000000Inspekční lupa

Inspekční lupa

Nástroj inspekční lupa se používá k otevírání dialogů, k zobrazení informací nebo výběru z voleb.

Nástroj vyberete kliknutím na ikonu se zavětšujícím sklem v hlavním menu nebo stiskem klávesy [a]. Pokud je nástroj zvolený, kurzor má tvar lupy.

{Tip: Kliknete-li inspekční lupou na zastávku s vlakem, zobrazí se nejprve okno s informacemi o zastávce. Budete-li však držet klávesu Ctrl, zobrazí se nejprve okno s informacemi o vlaku.}

Poznámka k užití: Vyskytuje-li se na stejné pozici více objektů, které při kliknutí inspekčí lupou zobrazí dialog, pak opětovné kliknutí otevře další dialog v pořadí.
{Tip: Které dialogu se budou otevírat a které nikoliv lze určit v nastavení nebo v souboru simuconf.tab}

Kliknutím inspekční lupou na objekty ve hře otevře příslušné dialogy:

Depo : Nástojem otevřete dialog depa sloužící k nákupu a správě vozidel.

Zastávka/stanice: Otevře dialog s informacemi o zastávce.

Konvoje : Otevře informace o konvoji.

Radnice : Otevře dialog s informacemi o městu.
{Tip: Volba townhall_info umožňuje povolit zobrazení informací též o budově radnice. viz nastavení (záložka všeobecné) nebo simuconf.tab}

Továrna : Otevře dialog s informacemi o továrně.

Městská budova : Otevře dialog poskytují informace o městské budově. Dialog obsahuje následující informace:
- majitel: Obvykle je zde napsáno: bez majitele, pokud budovy koupíte bude zde vaše jméno.
- popisek: Informace o budově. Pokud budova nemá přiřazený žádný doplňující popisek, je zobracen nápis 'obytný dům', 'továrna', nebo 'obchody a kanceláře'.
- Obec: Název města/vesnice kam dům patří.
- Vzrůst osob: Relativní obliba u cestujících.
- Pošta: Relativní obliba u pošty.
- dostupné od ~ do ~ : Interval, ve kterém je daná budova dostupná, pokud jste při vytváření mapy zadali volbu začít roku.
- Cena: Kolik bude stát koupě nebo odstranění budovy.
- Postavil: Jméno autora, který budovu nakreslil.

Pomníky / Turistické atrakce: Otevře dialog poskytující informace o památce. V titulku je napsáno jestli se jedná o pomník nebo o turistickou atrakci.
- Veřejný majetek znamená, že objekt není vlastněn hráčovou společností.
- Popisek poskytuje informace o památce.
- Vzrůst osob je relativní obliba mezi cestujícími.
- Pošta je relativní obliba u poštovních zásilek.
- Dostupné od: Odkdy se budova objeví ve hře, pokud hrajete s volbou začít roku.
- postavil: Kdo to pro simutrans nakreslil.

Silnice a železnice: Otevře dialog poskytující informace o železnici, silnici, plavebním kanálu ...
Pokud se jedná o tramvajovou trať na silničním podkladu, okno dialogu obsahuje informace o obou.
Informace obsažené v dialogu:
- Max. rychlost: Maximální povolená rychlost.
- (bez masky): číslo jehož jednotlivé bity ukazující směry cesty.
- (maska): číslo, jahož jednotlivé bity ukazují směr cesty pokud se bere ohled na povolený směr jízdy.
Pozn. ribi nejsou ryby ale zkratka pro Richtungsbits.
- elektrifikováno/nelektrifikováno oznamuje, jestli je cesta elektrifikovaná.
- souprav za poslední měsíc počet souprav, které použily cestu v minulém měsíci.
- rezervován pro oznamuje, že zde něco pojede.
- mapové souředanice zobrazeny v titulku okna

silniční semafory Otevře dialog semaforu.

železniční návěstidla Otevře dialog s železničními návěstidly.
Informace obsažená v dialogu zahrnuje:
- směry bitová maska směrů.

vedení: Kliknutím na elektrické vedení zobrazíte s informacemi o aktuálním stavu sítě:
- jméno majitele sítě
- síť: identifikátor sítě
- kapacita sítě: větší množství elektrické energie vám jedna síť dopravovat nepovolí.
- spotřeba: aktuální spotřeba v síti - poptávané množství el. enegrie, může být i vyšší než množství dodávané el. energie
- výroba: Množství aktuálně vyráběné elektrické energie.
- aktuální přenos: aktuálně dodávané množství el. energie.
- dodáno % : Kolik % vyrobené el. energie je spotřebováváno

trafostanice u elektrárny Otevře okno s informacemi o právě dodávaném množství elektrické energie továrnou do sítě.
- majitel
- výroba

trafostanice u továrny Otevře okno s informacemi o právě dodávaném množství elektrické energie továrnou do sítě.
- síť: identifikátor sítě elektrické sítě.
- spotřeba: Kolik MW el. energie továrna potřebuje.
- aktuální přenos: Kolik MW je aktuálně dodáváno do továrny.
- dodáno % : Kolik procent el. energie požadované továrnou je dodáno.

strom otevře dialog s nápisem 'Strom'. Zobrazené informace zahrnují:
- název druhu stromu.
- věk stromu v rocích a měsících.
{Pozn: musí být povoleno v nastavení (položka tree_info). Pokud není povoleno, dialog se stromem se neotevře.}

zemský povrch otevře okno 'země'). Zobrazené informace zahrnují:
- klimatický pás v závislosti na nadmořské výšce.
- mapové souřadnice zobrazené v ditulku okna.
Pozn: musí být povoleno v nastavení (položka ground_info) .

simutrans-124.5/simutrans/text/cz/labellist_filter.txt000066400000000000000000000017731520675460500233020ustar00rootroot00000000000000Seznam nápisů

Seznam nápisů

Zde jsou vypsány všechny nápisy vyskytující se na mapě.

Seznam nápisů otevřete z menu seznamy.

{Tip: Nápisy lze použít například pro pojmenování řek nebo místo nástěnky v hře po síti. }

Dialogu umožňuje nápisy řadit podle:
- hráč: Řadí podle hráče.
- jméno: Řadí nápisy podle textu.
- souřadnice: Nápisy jsou řazeny podle souřadnic.

Druhé tlačítko přepíná mezi vzestupným a sestupným způsobem řazení.

Tlačítko označené pouze aktivní hráč umožňuje zobrazit nápisy pouze od právě aktivního hráče.

Kliknutí na šipku na začátku řádku vás přemístí k nápisu.

Kliknutí na text nápisu zobrazí okno s obrázkem místa, kde se nápis nachází.

simutrans-124.5/simutrans/text/cz/language.txt000066400000000000000000000014411520675460500215350ustar00rootroot00000000000000Nápověda - výběr jazyka

Jazyk

Simutrans je vícejazyčná hra, a tak pokud existuje překlad do Vašeho jazyka, můžete si jej zvolit.
Po změně jazyka začne hra vypisovat všechny (přeložené) texty, ve Vámi zvoleném jazyce. Stávající otevřená okna je třeba znovu otevřít, aby v nich došlo k vypsání textů ve zvoleném jazyce.

U stávajících měst zůstanou jejich jména, která byla vytvořena podle jazyka při generování nové mapy. Proto je výhodné vybrat si jazyk ještě před generováním nové mapy, abyste v něm měli jména měst.

->Informace o překladu

Chcete-li pomoci s překladem Simutranů do vašeho jazyka,
navštivte https://translator.simutrans.com

simutrans-124.5/simutrans/text/cz/linemanagement.txt000066400000000000000000000146621520675460500227470ustar00rootroot00000000000000Nápověda - správa linek

Správa linek

Dialog správa linekposkytuje informace o linkách a umožňuje je spravovat.

Linka je tvořena jednou nebo více soupravami a společným jízdním plánem.

Dialog správa linek otevřete kliknutím na ikonu v hlavním menu, stiskem klávesy [w] nebo kliknutím na tlačítko před názvem linky v dialogu informace o soupravě.

Vlevo nahoře je seznam existujících linek. Linky mohou být filtrovány podle způsobu přepravy:
Vše: seznam linek všech druhů dopravy.
jednokolejka: seznam linek jednokolejky.
vlaky: seznam železničních linek.
úzkokolejka: seznam úzkokolejných železničních linek.
tramvaj: seznam tramvajových linek
náklaďáky: seznam linek silniční dopravy.
lodě: seznam linek lodní dopravy.
letadla: seznam linek letecké dopravy

{Poznámka: Ne všechny způsoby přepravy jsou vždy v dané grafické sadě dostupné.}

Barva názvu linky v seznamu má následující význam:
bílá barva - linka nemá přiřazeno žádné vozidlo
žlutá barva - není v provozu, nevytváří zisk ani ztrátu
černá barva - linka je v zisku
modrá barva - linka obsahuje zastaralá vozidla
červená barva - linka prodělává

tlačítka pod seznamem

Pod seznamem existujících linek jsou tlačítka voleb pro správu linek
Aby bylo možné linku změnit nebo smazat, musí být nějaká linka vybrána. Linku vyberete kliknutím na její název v seznamu v levém horním rohu.

Tlačíka pod seznamem linek nabízejí tyto možnosti:

nová linka:Tlačítkem otevřete nový jízdní plán. Jízdní plán obsahuje trasu, minimální množství nákladu a maximální dobu čekání u jednotlivých stanic.
Nový jízdní plán přiřadíte zavřením okna s jízdním plánem.
{Poznámka: Musíte vytvářet linku pro konktétní typ dopravního prostředku (např tramvaj), abstraktní linku nejde vytvořit.}
{Poznámka: Body v jízním plánu mimo zastávky a stanice jsou pouze průjezdné, nic se na nich nenakládá ani nevykládá. Body ovliňují rezervování tratě pro vlaky.}

Změnit linku: Tlačítkem otevřete stávající jízdní plán. Změna se projeví při uzavření okna s jízním plánem.
Dříve než kliknete na tlačítko, vyberte požadovanou linku kliknutím na její název v seznamu.

Smazat linku: Odstraní vybranou linku. Žádné další potvrzení není vyžadováno.

seznam stanic

Pokud je zvolena libovolná linka, pak se v levém dolním rohu zobrazí seznam stanic.
Položky zobrazené u každé stanice zahrnují:

barevný indikátor stavu: Barva ukazuje, jestli stanice není přeplněna. Stejné barevné schéma je použito i v seznamu stanic a zastávek a také u zastávek ve na herní ploše.
- žlutá: zatím bez provozu.
- zelená: v provozu, existuje dostatečná volná kapacita.
- oranžová: zastávka začíná být přeplněna.
- červená: přeplněno, aspoň pro jeden typ přepravovaných položek není volná kapacita.

jméno přiřazené k zastávce.

číslo zastávky (jako součást názvu), je třeba povolit v nastavení nebo v simuconf.tab.

ikona vozidla ukazuje které druhy dopravy mohou zastávku použít (vlak, silniční doprava, ...)

ikona(y) zboží zobrazuje, které položky (cestující, pošta, náklad) je zastávka schopna odbavit.

čeká množství čekajících cestující, pošty a zboží.

graf vpravo

Na pravé straně od seznamu linek se nachází graf, pole pro změnu názvu linky a seznam vozidel zvolené linky.

Hodnoty v grafu zobrazíte kliknutím na odpovídající políčko pod grafem. Na vodorovné ose je zobrazen čas v měsících.

volná kapacita - Množství volného prostoru (který by mohl být využit pro přepravu ale zůstal nevyužitý).

přepraveno - množství přepraveného zboží / přepravených cestující.

příjmy - peníze za dopravu.

provoz vozidel - provozní náklady.

zisk - peníze za dopravu po odečtení provozních nákladů.

soupravy - počet souprav přiřazených k dané lince.

délka/měsíc - Počet kilometrů ujetých vozidly dané linky.

Maxspeed - teoretická maximální rychlost - jsou z ní počítány bonusy za rychlejší přepravu.

Editovatelné pole s názvem linky slouží ke změně názvu. {Tip: Pokud si linky vhodně pojmenujete lépe se v seznamu budete orientovat i při větším množství linek. Např.: Louny 31415
Pojmenované linky využijete hlavně v autobusové dopravě. }

seznam souprav

Pokud je vybrána nějaká linka, v pravé dolním rohu se zobrazí seznam souprav přiřazených k lince.
Více informací o konkrétní soupravě získáte, pokud na ni v seznamu kliknete.
U souprav jsou zobrazeny následující položky:
název který byl přiřazen soupravě (výchozí název je vytvořen podle prvního vozidla soupravy při koupi v depu).
počet souprav přiřazených k daná lince
kapacita všech souprav linky dohromady
zisk/ztráta (označená příjem) provozní zisk/ztráta (příjem po odečtení provozních nákladů)
Poznámka: Pozor vozidla také platí mýto a to zde zatím (ve verzi 111.1) není započítáno.
naložené množství položek
procentuální naložení souprav linky v závorkách
linka: ke které lince je souprava přiřazena.
obrázek zobrazení řazení vozidel v soupravě.

simutrans-124.5/simutrans/text/cz/list.txt000066400000000000000000000023751520675460500207340ustar00rootroot00000000000000Nápověda - seznamy

Seznamy

Menu seznamy je rozcestníkem k dostupným seznamům. Díky nim máte přehled.

Menu otevřete kliknutím na ikonu seznamy v horní části obrazovky.

Menu obsahuje následující položky:

seznam stanic: Seznam všech stanic a zastávek aktuálního hráče. V seznamu nejsou zahrnuty přístavy s pouze lodní dopravou (např. ropné plošiny).

správa linek: Seznam všech linek.

seznam vozidel: Seznam všech silničních a železničních vozidel, letadel a lodí.

seznam měst: Seznam všech měst.

seznam zboží: Seznam všech dostupných typů přepravovatelného zboží. Pomůže v rozhodování, co se vyplatí přepravovat.

seznam továren: Seznam všech továren na hrací ploše.

turistické cíle: Všechny památky, památníky a jiné turistické zajímavosti.

seznam znaků: Seznam všech nápisů umístěných do hry.

simutrans-124.5/simutrans/text/cz/load.txt000066400000000000000000000013621520675460500206730ustar00rootroot00000000000000Nápověda - načíst

Načíst

Přes dialog načíst můžete načíst dříve uloženou hru a pokračovat v ní. Ovládání dialogu je velice podobné jako u dialogu ukládání.
Jednoduše vyberete hru, ve které chcete pokračovat, a klikněte na její jméno. Dialog se zavře, hra se načte, a pokud vše proběhne bez problému, zobrazí se potvrzovací zpráva.

Pozor: Hry uložené z jiných verzí Simutrans nemusejí být kompatibilní s touto verzí. Pokud se takovouto nekompatibilní hru pokusíte načíst, obdržíte chybové hlášení.

->Nápověda pro ukládání

->Informace o překladu

simutrans-124.5/simutrans/text/cz/load_relief.txt000066400000000000000000000021161520675460500222170ustar00rootroot00000000000000Nápověda - načíst terén

Načíst terén

Dialog slouží k načtení výškové mapy z obrázku. Může být také použit k odstranění již nepotřebných obrázků.

Dialognačíst terém otevřete z dialogu Vytvořit novou mapu tlačítkem načíst terén.

Kliknutím na jméno souboru vyberete a otevřete soubor.

Soubor můžete vybrat také napsáním názvu do pole nahoře a následovným kliknutím na tlačítko ano.

Kliknutím na tlačítko ne dialog zavřete bez načtení čehokoliv.

Znak x slouží ke okamžitému smazání souboru s mapou. Pozor, na nic se neptá.

Některé předpřipravené terény jsou dostupné online na http://maps.simutrans.com.

Nové soubory musí být ve formátu .ppm nebo .bmp (nejlépe 256 bareb RLE compressed) a musí mýt umístěny v adresáři simutrans/maps/.

{Tip: Ke tvorbě nových map můžete použít například program gimp.}

simutrans-124.5/simutrans/text/cz/mailbox.txt000066400000000000000000000037461520675460500214170ustar00rootroot00000000000000Nápověda - poštovní schránka

Poštovní schránka

Poštovní schránka obsahuje seznam zpráv a umožňuje určit, které zprávy se budou zobrazovat také na herní ploše.

Dialog poštovní schránky otevřete z hlavního menu nebo stiskem klávesy [B].

Tlačítko nastavení otevře dialog s nastavením, které zprávy se mají zobrazovat.

Pod jednotlivými záložkami se skrývají roztříděné zprávy.

Nastavení

Volby obsahují tři sloupce čtvercových tlačítek.

První tlačítko zobrazí zprávu dole na liště, druhé v novém okně a třetí tlačítko zobrazí zprávu v novém okně nastálo (dokud ji hráč sám nezavře).

Nový rok: Začal nový rok.

Novinky AI: Umělá inteligence otevřela novou linku.

Z měst: Obec staví novou radnici.

Bloudění: Vozidlo nemůže najít cestu do dalšího bodu v jízdním plánu. Mezi nejčastější příčiny patří nespojené cesty, špatně postavený semafor či chybějící elektrifikace.

Nový průmysl: Stavba nové továrny, řetězu továren nebo nové propojení továren způsobené vzrůstem počtu obyvatel.

Diskuse: Zprávy od ostatních hráčů (při hře po síti).

Nová vozidla: Je dostupné nové vozidlo nebo dříve dostupné vozidlo se stalo zastaralým a už se nevyrábí (pokud jste při vytváření hry povolili začít roku).

Přeplnění: Stanice nebo zastávka je přeplněna.

Problémy: Hráčova společnost je zadlužená nebo jiné závažné zprávy.

dopravní zácpy: Vozidla někde uvízla.

simutrans-124.5/simutrans/text/cz/mainmenu.txt000066400000000000000000000071161520675460500215700ustar00rootroot00000000000000Nápověda - hlavní menu

Hlavní menu

Hlavní menu je lišta s nástroji nacházející se na horní straně okna. Vzhled ikon v hlavním menu a jejich pořadí se liší v závislosti na grafické sadě. Některé ikony nemusejí být dostupné v závislosti na na čase. Například v 19. století není zobrazena ikona s nástroji pro jednokolejku.

V hlavním menu naleznete ikony s těmito nástroji:

nastavení hry: Za disketou se nachází nastavení jazyka, barev, způsobů zobrazení, hráčů stejně jako možnost uložení, uložení nebo ukončení hry.

mapa: Zobrazí dialog s mapou. Obsahuje rozličné volby pro různá zobrazení informací o objektech nacházejících se na povrchu i pod ním.

lupa: Inspekční lupu využijete při zjišťování informací o objektech. Můžete s ní klikat na cokoliv, aniž by bylo něco rozbito.

úprava terénu: Nástroje pro úpravu terénu slouží k urovnání terénu pro výstavbu železniční sítě a také k výstavbě stoupajících a klesajících tunelů.

menu s konstrukčními nástroji je značně proměnlivé v závislosti na grafické sadě, ne vše je vždy dostupné. Naleznete v něm nástroje pro výstavbu:
- železnice
- jednokolejky/visutá železnice
- tramvajové trati
- silniční dopravy
- lodní dopravy
- letecké dopravy

speciální nástroje: Nabízejí nástroje, které se jinam nevešly, například pro stavbu elektrické sítě.

odstranit: Nástroj k odstraňování objektů ze hry.

editace mapy: Nástoj slouží k úpravě stávající mapy. Nástroj je dostupný jen hráči označenému jako Veřejné služby.

správa linek: Obsahuje seznam linek, nástoje pro jejich správu a statistické údaje o nich.

seznamy Menu je rozcestníkem k jednotlivým seznamům:
- seznam zastávek
- seznam vozidel
- seznam měst
- seznam zboží
- seznam továren
- seznam turistických cílů

poštovní schránka: Hlášení, zprávy, informace o novinkách a dopisy od spoluhráčů najdete ve své poštovní schránce. Reklamních letáků netřeba se obávat.

finance: Přehled o hospodaření vaší firmy.

screenshot: Sejme snímek z obrazovky a uloží jej do adresáře screenshot.

Pauza: Pozastaví běh času ve hře. I při pauze můžete stavět a kupovat vozidla.

zrychlit: Zrychlí řádově běh času. Užitečné, když nechcete čekat. Vypnete opětovným stisknutím.
{Tip: Rychlost plynutí času změníte klávesami čárka [,] a tečka [.].}

simutrans-124.5/simutrans/text/cz/map.txt000066400000000000000000000023051520675460500205270ustar00rootroot00000000000000Nápověda - mapa

Mapa

V tomto okně můžete prohlížet vaši mapu z ptačí perspektivy, a to včetně včetně měst, průmyslu, dopravy a dalších volitelných údajů.

Každý typ průmyslu je označen jinou barvou, s vysvětlivkami v dolní části okna s mapou. Pokud nad nějaký objekt průmyslu přemístíte ukazatel myši, zobrazí se jeho název a vyznačí se i s názvy odběratelé.

Pokud chcete vidět více detailů, kdekoliv v okně s mapou klikněte pravým tlačítkem myši, což způsobí přiblížení mapy (až do 2 stupňů). Oddálení je možné stisknutím tlačítka SHIFT a pravého tlačítka myši.

V dolní části okna se nalézají tlačítka, po jejichž stisknutí se na mapě zobrazí (barvou dle intenzity) vybraná volba.
Při volbách "Cestujících" a "Pošty" bude jednou barvou zobrazeno území, které obsluhují vaše stanice pro cestující nebo pro poštu. Při volbě "Tratě" budou barevně zvýrazněny a odlišeny různé druhy železničních traťí - bílou normální, červenou elektrifikované, žlutou budou zvýrazněny semafory.

->Informace o překladu

simutrans-124.5/simutrans/text/cz/monorailtools.txt000066400000000000000000000037401520675460500226570ustar00rootroot00000000000000Nápověda - nástroje pro jednokolejku a maglev

Nástroje pro jednokolejku/visutou železnici

Nástroje pro jednokolejku/visutou železnici slouží k výstavbě dopravní sítě pro přepravu cestujících.

Pokud jste při vytváření mapy zadali volbu začít roku, nástroje mohou být do určitého data nedostupné.

Menu s nástroji otevřete kliknutím na ikonu označenou nástroje pro jednokolejku/visutou železnici v hlavním menu.

Podržíte-li kurzor myši na ikonou s nástroji zobrazí se název, cena, (měsíční náklady na úrdžbu) a maximální rychlost.

Nástroje obsahují (zleva doprava):

přízemní jednokolejka: Trať postavíte stejným způsobem, jako železnici.

visutá jednokolejka: Může vést i nad domy a silnicemi.

návěstidla: Použití návěstidel je stejné jako u železnice a tramvají.

Odstranit visutou dráhu/jednokolejku: Nástroj odstraní trať visuté dráhy nebo jednokolejky. Při odstraňování dejte pozor na jednosměrné semafory.

mosty: Mosty slouží k přemostění údolí a jiných překážek.

depo: Vozidla pro jednokolejku/visutou dráhu koupíte v depu. Depo postavíte na konci dráhy.

zastávky: Nástroj slouží k výstavbě stanic. Klikněte na nástroj a pak pole s dráhou jednokolejky.

základy jednokolejky: Pomocí nástroje postavíte rozšíření stanice. Do stanice se vejde více cestujících. Také se zvýší náklady na údržbu.

simutrans-124.5/simutrans/text/cz/mouse.txt000066400000000000000000000007421520675460500211050ustar00rootroot00000000000000Nápověda - myš

Myš

levé tlačítko slouží ke klikání na objekty pomocí inspekční lupy, výstavbě a veškerému ovládání hry.

při stisknutém pravém tlačítku myši můžete posouvat s mapou.

kolečko myši slouží k přiblížení se k terénu nebo k oddálení se od něj.

Chování myši může být ovlivněnou stisknutím klávesy [Ctrl]

simutrans-124.5/simutrans/text/cz/new_world.txt000066400000000000000000000052541520675460500217600ustar00rootroot00000000000000Nápověda - nová mapa

Nová mapa

Dialog nová mapa vám poskytuje možnost vytvořit si mapu podle vlastních představ. Můžete ovlivnit reliéf mapy, hustotu průmyslu, počet měst aj.

Níže následuje krátký popis jednotlivých nastavení nové mapy:

Číslo mapy - Určuje reliéf mapy - kde jsou hory, voda atp.

Velikost mapy - Určuje, jak bude mapa velká, tzn. kolik bude mít čtverečků. Od 8x8, až po 4096x4096. Standardně je nastaveno 256x256. Mapa může být i obdélníková, například 256x8064. Obdélníkové mapy mohou mít jeden rozměr větší než 4096 polí.
Důležité: Velké mapy jsou hodně náročné na paměť, zvažte proto, zda máte dostatečně velkou operační paměť, aby hra fungovala plynule.

Hustota průmyslu - Určuje, jaká bude hustota průmyslu - továren, obchodů aj. - na generované mapě. Pokud si chcete hru ztížit, nastavte malé číslo, bude potom obtížnější propojit průmysl.

Počet měst - Určuje počet měst na mapě. Dostupné nastavení je od 0 až do 999 měst. Standardně je nastaveno 16.

Hustota provozu - Hustota soukromých aut ve městech. Standardně je nastaveno 8. Doporučujeme si zvolit malé číslo, přílišná hustota není vhodná. Později mohou překážet vašim vozidlům.

Úroveň vody - Nastavuje, jak vysoko bude voda nad dnem. Při nízkém čísle moho být na mapě zálivy a průlivy.

Výška hor - Určuje velikost nejvyšších hor na mapě. Dostupné nastavení je od 0 až do 160. Nízké nastavení vytvoří malé kopce a průsmyky.

Zaoblení mapy - Hodnota, která určuje, jak hladké bude přechody mezi hranami reliéfu.

Náhodná mapa - Vybere náhodnou mapu (číslo mapy). Neovlivňuje ostatní nastavení.

Zobrazovat chodce - Nastaví, zda budou zobrazováni chodci. Toto nastavení je možné změnit během hry v dialogu zobrazení.

Střídat den a noc - Zapne mód střídání dne a noci. Toto nastavení je možné změnit během hry v dialogu zobrazení.

Načíst - Zobrazí dialog pro načtení hry.

Začít hru - Vytvoří novou mapu podle nastavení. Pokud je nastavena velká mapa či jsou nastaveny velké hodnoty např. počtu měst či husoty průmyslu, vytváření zabere delší dobu.

Konec - Ukončí hru bez dalšího potvrzení.

->Informace o překladu

simutrans-124.5/simutrans/text/cz/options.txt000066400000000000000000000022571520675460500214530ustar00rootroot00000000000000Nápověda - nastavení hry

Nastavení hry

Dialog nastavení je branou pro nastavení rozličných možností chování Simutrans během hraní.
Následuje seznam voleb (s odkazy na detailnější informace), a krátký popis každého nastavení.

Jazyk - Umožňuje nastavit jazyk pro Simutrans.
Barva hráče - Mění barvu vaší společnosti.
Zobrazení - Změní nastavení obrazovky, jako denní a noční mód aj.
Zvuky - Nastavení voleb pro zvuky v Simutrans.
Hráči - Nastaví vaše počítačové konkurenty.
Načíst - Načte dříve uloženou hru.
Uložit - Uloží právě hranou hru.
Nová mapa - Začít novou hru (nemůže být přerušeno žádným dalším dialogem)
Konec - Ukončí hru bez dalšího potvrzení.

Více informací o každé položce najdete přímo v její nápovědě nebo po kliknutí na odkaz výše.

->Informace o překladu

simutrans-124.5/simutrans/text/cz/password.txt000066400000000000000000000006321520675460500216150ustar00rootroot00000000000000Nápověda - změna hráčova jména a hesla

změna hráčova jména a hesla

Dialog slouží ke změně názvu společnosti a nastavení hesla.

Dialog vyvoláte kliknutím na čtverec vpravo vedle hráčova jména v obchodním rejstříku (seznamu hráčů)

Jesliže je za hráčovým jménem červený čtverec, je chráněn heslem.

simutrans-124.5/simutrans/text/cz/players.txt000066400000000000000000000011411520675460500214260ustar00rootroot00000000000000Nápověda - seznam hráčů

Seznam hráčů

V dialogu seznamu hráčů můžete aktivovat a deaktivovat počítačové hráče, a prohlížet stav jejich kont.

K aktivaci/deaktivaci určitého počítačového hráče jednou klikněte na tlačítko vedle jména (barvy) hráče. Pokud hráče jednou aktivujete a později deaktivujete, tak nic co postavil se nezmění, tzn. že jeho dopravní spoje budou dále fungovat. Hráče pouze 'uspíte', takže nebude stavět nic nového, ani zavádět nové spoje.

->Informace o překladu

simutrans-124.5/simutrans/text/cz/privatesign_info.txt000066400000000000000000000012561520675460500233240ustar00rootroot00000000000000Závora

závora

Závora umožňuje zakázat nebo povolit ostatním hráčům vstup na silnici nebo železniční trať. Je užitečná k odfitrování aut veřejného hráče, která by jinak bránila vjezdu nákladních vozidel k nákladové rampě.

Závoru postavíte pomocí nástojů pro stavbu železnice nebo silnice.

V levém horním rohu dialogu je napsáno, komu závora patří.

Zamáčklé tlačítko před jménem hráče znamená vstup povolen.

Tip: Vozidel veřejné správy se lze zbavit kombinací závory a jednosměrky (na výjezdu).

simutrans-124.5/simutrans/text/cz/railtools.txt000066400000000000000000000070621520675460500217670ustar00rootroot00000000000000Železniční doprava

Železniční doprava

Tento dialog obsahuje nástroje pro stavbu kolejí, tunelů, návěstidel, stanic, dep, přejezdů a elektrifikování trati.

První jsou symboly různých druhů železničních tratí: Kliknutím na některý z nich se kurzor změní v obrázek odpovídajícího druhu železniční tratě. Různé druhy tratí se liší v max. rychlost, ceně, a ceně za údržbu. Parametry každého druhu se zobrazí po najetí kurzorem nad jeho obrázek. Cena v závorce je cena z údržbu.
Pro stavbu železnice kliknete na místo, kde má trať začínat - musí být prázdné - a poté si vyberete místo, kde má trať končit. Nesmí být moc daleko a musí být spojitelné (nesmí například překážet silnice). Problém se silnicemi řeší přejezdy.

Symbol stanice: Kliknutím na něj se kurzor změní v obrázek části stanice. Stanice jsou používány pro naložení všech druhů výrobků a surovin, ale i cestujících a pošty. Nádraží může být jakkoliv velké, ale jeho jednotlivé části spolu musí sousedit a navíc nádraží delší než 8 čtverečků jsou zbytečná, protože takto dlouhý vlak ani nelze postavit. Stavějte nádraží kdekoliv na trati, mimo stoupání, zatáček a jiných nerovností. Kapacita čekajícího zboží a lidí je neomezená, avšak pokud je od jednoho produktu na nádraží více než 1000 kusů, továrna, která jej produkuje přestane dodávat. Začne až tehdy, když počet opět klesne pod 1000 kusů.

Symbol návěstidel: Ty se používají, pokud potřebujete na jedné trati jezdit s více soupravami. Základní tvar je obousměrný, postaví se jedním kliknutím na existující železnici. Pokud kliknete podruhé na to samé místo,semafor učiní trať jednosměrnou z jedné strany. Třetím kliknutím obrátíte semafory a trať je jednosměrná na druhou stranu. Takto je možné určovat, po jaké koleji pojedou vlaky z jedné strany a naopak. Když semafory zbouráte a budete mít trať elektrifikovanou, elektrifikace zůstane nepoškozená.

Symbol tunelu: Kliknutím na něj se kurzor změní v obrázek tunelu. Tunely se používají pro protnutí hory v místech, kde by objížďka značně prodloužila dobu cesty vlakové soupravy. Pro stavbu tunelu musíte nějprve postavit na místě, kde chcete začít železnici. Místo, kde má tunel končit musí být přesně naproti začátku tunelu. Tunel také nemůže stoupat, jeho konec musí být ve stejné výšce jako začátek.

Symbol přejezdu: Kliknutím na něj se kurzor změní v obrázek železničního přejezdu. Používá se v místě, kde se silnice protíná se železnicí. Přejezd musí být postaven na již existují rovné části železnice nebo silnice.

Symbol železničního depa: Kliknutím na něj se kurzor změní v obrázek depa. Depa umožňují nakoupit všechna dostupná kolejová vozidla, spojovat soupravy a vytvářet či přiřazovat jízdní plány. Ke stavbě si vyberte místo, kde končí železnice - nejlepší je tedy postavit si odbočku.

Pozor: Každá část nádraží má kapacitu dvou kusů vozidel. Pokud je vlak delší než nádraží, zadní část vlaku nemůže být vyložena ani naložena. Dávejte proto pozor, aby nádraží bylo dostatečně veliké.

->Stavba mostů
->Nápověda pro depo

->Informace o překladu

simutrans-124.5/simutrans/text/cz/removal_tool.txt000066400000000000000000000006731520675460500224620ustar00rootroot00000000000000Odstranit

odstranit

Nástroj odstranit použijte k odstranění nežádoucích věcí z jednoho pole.

Nástroj najdete v hlavním menu pod ikonou s črveným křížkem nebo buldozerem (pak128).

Nástroj vyvoláte též klávesou [r].

Nástroj může být použit i k odstanění chodců a městských vozidel.

simutrans-124.5/simutrans/text/cz/roadtools.txt000066400000000000000000000052261520675460500217650ustar00rootroot00000000000000Nápověda - silniční doprava

Silniční doprava

Přes dialog silniční doprava můžete stavět silnice, tunely, zastávky, nakládací místa aj.

První jsou symboly různých druhů silnic: Kliknutím na některý z nich se kurzor změní v obrázek odpovídajícího druhu silnice. Různé druhy silnic se liší v max. rychlost, ceně, a ceně za údržbu. Parametry každého druhu se zobrazí po najetí kurzorem nad jeho obrázek. Cena v závorce je cena z údržbu.
Pro stavbu silnice kliknete na místo, kde má silnice začínat - musí být prázdné - a poté si vyberete místo, kde má silnice končit. Nesmí být moc daleko a musí být spojitelné (nesmí například překážet železnice). Problém se železnicí řeší přejezdy.

Symbol zastávek: Kliknutím na něj se kurzor změní v obrázek autobusové zastávky. Mohou zde tedy stavět pouze autobusy a také dodávky na poštu. Pokud chcete přepravovat poštu, musí být u zastávky postavena budova pošty. Kapacita zastávky je jeden autobus (poštovní dodávka) v každém směru a 64 cestujících na jedno políčko zastávky (počítá se i budova pošty). Pro stavbu vyberte jakékoliv rovné místo na silnici.

Symbol nakládacího depa: Kliknutím na něj se kurzor změní v obrázek tohoto depa. Nakládací depa jsou používány pro naložení všech druhů výrobků a surovin. Nakládací depo má kapacitu dvou aut a 1000 kusů každého druhu zboží.

Symbol tunelu: Kliknutím na něj se kurzor změní v obrázek tunelu. Tunely se používají pro protnutí hory, v místech, kde by objížďka značně prodloužila dobu cesty vozidla. Pro stavbu tunelu musíte nejprve postavit silnici. Místo, kde má tunel končit musí být přesně naproti začátku tunelu. Tunel také nemůže stoupat, jeho konec musí být ve stejné výšce jako začátek.

Symbol přejezdu: Kliknutím na něj se kurzor změní v obrázek železničního přejezdu. Používá se v místě, kde se silnice protíná se železnicí. Přejezd musí být postaven na již existují rovné části železnice nebo silnice.

Symbol silničního depa: Kliknutím na něj se kurzor změní v obrázek depa. Depa umožňují nakoupit všechna dostupná vozidla, spojovat přívěsy a vytvářet či přiřazovat jízdní plány. Ke stavbě si vyberte místo, kde končí silnice - nejlepší je tedy postavit si odbočku.

->Stavba mostů
->Nápověda pro depo

->Informace o překladu

simutrans-124.5/simutrans/text/cz/save.txt000066400000000000000000000022761520675460500207170ustar00rootroot00000000000000Nápověda - uložit

Ukládání hry

Pokud chcete uložit Vaši rozehranou hru, abyste jí mohli později načíst a pokračovat v ní, lze tak učinit přes dialog uložit. V tomto dialogu (stejně jako v dialogu načíst) také můžete smazat dříve uložené hry.

Hru můžete uložit do nového souboru, či přepsat soubor již existující. Pro uložení do nového souboru musíte nejprve vložit jméno tohoto souboru, a poté kliknout na tlačítko uložit. Dialog se zavře, hra se uloží, a pokud vše proběhne bez problému, zobrazí se potvrzovací zpráva.

Při mazání a přepisování souborů dbejte zvýšené opatrnosti, protože pokud soubor jednou smažete (či přepíšete novou hrou), již nelze obnovit.
Ke smazání tedy klikněte na tlačítko X, umístěné vedle jména souboru, který chcete smazat.
Pro přepsání dříve uložené hry pouze klikněte na jméno souboru k přepsání.
Hra bude bez dalšího potvrzení smazána (nebo přepsána).

->Nápověda pro načítání

->Informace o překladu

simutrans-124.5/simutrans/text/cz/scenario.txt000066400000000000000000000007671520675460500215670ustar00rootroot00000000000000Nápověda - scénář

Scénář

Můžete hrát se scénářem - máte stanoveny cíle, jež musíte dosáhnout. Cílem může být dosáhnout určitého počtu přepravených cestující, připojit továrnu, mít na účtu nějaký předem stanovený finanční obnos.

Okno dialogu obsahuje seznam scénářů sčetně jejich popisu.

Kliknutím na název scénáře spustíte novou hru.

Tlačítkem ne zavřete dialog.

simutrans-124.5/simutrans/text/cz/schedule.txt000066400000000000000000000043651520675460500215560ustar00rootroot00000000000000Nápověda - jízdní plán

Jízdní plán

Jízdní plán je možné vytvářet třemi způsoby:

1.)Klasicky v nějakém depu koupíte dopravní prostředek a zmáčknete tlačítko jízdní plán. Zobrazí se vám tabulka. Přidejte si zastávky kliknutím kurzoru na požadované místo zastavení. Je možné je zase odstraňovat tlačítkem smazat. Kliknete na hotovo a můžete dopravní prostředek vypustit tlačítkem start.

2.)Přes funkci Nová linka. Po kliknutí na něj se vám zobrazí stejná tabulka jako v předchozím případě, postup je stejný, až na to, ze linka se uloží a je možné ji pojmenovat (třeba Praha 456), později vyvolat (řádek nahoře) a tlačítkem nastavit linku přiřadit její zastávky dalšímu libovolnému vozidlu.

3.)Stiskněte klávesu "w", což je správa linek, o tom byla trochu řeč v minulém případe. Tam můžete přidat novou linku, smazat linku, či změnit pro všechna vozidla linky najednou(!). Postup operací je stejný, jako v předchozích případech.

Nastavení čekat na x% určuje, na kolik procent musí být vozidlo plné, aby mohlo vyjet. Tímto zabráníte zbytečnému ježdění poloprázdných či úplně prázdných vozidel.

Tlačítkem Zpětná jízda vložíte na konec jízdního plánu aktuální zastávky, avšak v obráceném pořadí a bez poslední a první. Vozidlo se tak nebude od poslední zastávky vracet k první hned, ale pojede postupně přes ostatní zastávky v jízdním plánu v obráceném pořadí.

Doporučujeme využívat pouze linky a ne staré formy jízdních řádů, je to neefektivní při přidávání nových vozidel na stejnou trasu. Linky jsou také přehlednější a v dialogu správy linek můžete také sledovat mnoho statistických údajů.
Pokud ale nečekáte že by se mohla linka později rozšířit na více než 1 vozidlo, v nepoužívání linek vám samozřejmě nic nebrání.

->Nápověda pro depo

->Informace o překladu

simutrans-124.5/simutrans/text/cz/server.txt000066400000000000000000000015541520675460500212650ustar00rootroot00000000000000Hra po síti

Hra po síti

Okno Informace o hře poskytuje údaje o herních serverech dostupných pro hru po síti, současném stavu hry na nich, a to včetně detailů jako je datum, počet hráčů, souprav apod.

V horní části okna je zobrazen seznam herních serverů. Server vyberete kliknutím na text.

Po ním jsou tlačítka omezující výběr na servery se stejnou verzí programu a servery se stejným pakem.

Pokud požadovaný server není v seznamu, zadejte jeho ip adresu do políčka.

Tlačítkem dotázet se serveru se serveru zeptáte na informace o právě probíhající hře.

Pomocí tlačítka hrát online se připojíte k vámi zvolenému serveru. Počkáte si, až se načte mapa a můžete hrát.

simutrans-124.5/simutrans/text/cz/settings.txt000066400000000000000000000430701520675460500216160ustar00rootroot00000000000000Nápověda - nastavení

Nastavení

V nastavení můžete změnit takřka každý parametr hry, který byste mohli změnit ruční úpravou souboru simuconf.tab. Každý parametr má stejný význam jako jemu odpovídající řádek v souboru simuconf.tab. Tam také naleznete aktuální verzi domumentace (v angličtině).

Upozornění:
Neuváženou změnou parametrů můžete snadno udělat hru příliš obtížnou až nehratelnou nebo naopak příliš lehkou až vás nebude bavit. Některé volby mění náročnost hry na výkon počítače.

Všeobecně

savegame_version: Verze formátu, v jakém má být hra uložena.

Výchozí hodnota: Nejnovější verze hry, např. 112.1.

drive_left: Povolením přepnete na levostraný provoz.

Výchozí hodnota: Vypnuto

signals_on_left: Semafory a silniční značky se budou zobrazovat na levé staně komunikace.

Výchozí hodnota: Vypnuto

autosave: Jak často se má hra automaticky ukládat:
- 0: neukládat
- 1: ukládat měsíčně
- 3: ukládat čtvrtletně
- 12: ukládat ročně

Výchozí hodnota: 0

fast_forward: Jak moc to poběží rychle po kliknutí na ikonu zrychlit čas.

Výchozí hodnota: 50

numbered_stations: Použije při vytváření názvů stanic čísla. Pokud zakážete, zastávky mají názvy složené z názvu města a názvů okolních budov (památek, továren) apod. Doporučuji nepovolovat.

Výchozí hodnota: Vypnuto

show_names: Způsoby zobrazení názvů stanic:
- 0: Jména měst a zastávek nejsou zobrazena.
- 1: Jména měst jsou v šedivém rámečku, jména zastávek v modrém.
- 2: Jména měst nejsou zobrazena, nad zastávkou je zobrazen barevný indikátor stavu a grafické znázorníní množství čekajících položek.
- 3: Jsou zobrazena jména měst a zastávek (jako při hodnotě 1) a navíc indikátor (jako při hodnotě 2)
Pozn: Způsob zobrazení názvů měst a zastávek je možné měnit i v průběhu hry pomocí klávesy [!].

Výchozí hodnota: 3

bits_per_month: Parametr určuje, jak rychle ve hře plyne čas. Zvýšení hodnoty o jedna prodlouží délku měsíce ve hře na dvojnásobek. Protože měsíc trvá dvakrát déle, továrny mají dvojnásobnou produkci a infrastruktura vás stojí dvojnásobek na údržbu. Ale přepravíte dvakrát tolik.
Výchozí hodnota: 20

Tip: Připadá-li vám, že se vlaky pohybují jako hlemýžď, ale nechcete, aby čas plynul moc rychle? Zvyšte bits_per_month o 1 a pak hrajte při dvojnásobné rychlosti.

add_player_name_to_message
Ve verzi 112.0 tato volba už chybí.

use_timeline:
- 0: Vypne začít roku, začít roku nepůjde zapnout.
- 1: Zapne začít roku, začít roku nepůjde vypnout.
- 2: Vypne začít roku
- 3: Zapne začít roku

Výchozí hodnota: 2

Tip: nastavte na 1 nebo 3.

starting_year: Rok, kdy bude začínat nová hra. Pokud nastavíte příliš nízké číslo, nemusí být dostupná žádná vozidla. Vhodné nastavení závisí na grafické sadě.

Výchozí hodnota: 1930

Tip: Pokud jste začali moc brzy a nejsou dostupná vhodná vozidla, přepněte se na hráče Veřejné služby a v menu editace mapy přidejte (i opakovaně) jeden rok.

starting_month: Měsíc, v jakém začne nová hra. Leden je 0, únor 1, atd.

Výchozí hodnota: 0

show_month: Ovlivňuje způsob zobrazení data v levém dolním rohu obrazovky. Hodnoty mají následující význam:
- 0: roční období (léto 1876)
- 1: měsíc (červen, léto 1876 3:47h)
- 2: japonské datum (léto 1876/červen/2 3:56h)
- 3: americké datum (léto, červen 2 1876 3:47am)
- 4: české datum (léto, 2. červen 1876 3:47h)
- 5: japonské datum bez ročního období (1876/červen/12 2:47h)
- 6: americké datum bez ročního období (červen 2 1876 3:47am)
- 7: české datum bez ročního období (18. červen 1876 3:47h)

Výchozí hodnota: 3

Pozn: V originále jsou volby 4 a 7 nazvány "německé datum".
Tip: Nastavte 4 nebo 7.

random_grounds_probability: Nastavuje četnost výskytu nepohybujících se objektů zpestřujících krajinu (groundobj), jako jsou balvany, květiny, krtiny, jezíka, kamení apod.
0 - vypnuto
jiná hodnota - na 1 pole s groundobj připadne n políček bez groundobj. Tj. při random_grounds_probability=4 bude připadat na každá 4 políčka bez groundobj jedno pole s 1 groungobj.

Výchozí hodnota: 10

random_wildlife_probability: Podobné jako předchozí, ale pro pohybující se objekty: ovce, husy, kamzíky apod.

Výchozí hodnota: 1000

pedes_and_car_info: Zapne zobrazování informací o chodcích a automobilech veřejné správy. Může být užitečné, pokud chcete vědět, jaká osobní auta jezdí po silnicích, ale může obtěžovat, chcete-li infornace o vlastních vozidlech nebo zastávkách.

Výchozí hodnota: Vypnuto

tree_info: Zapne/vypne, že při kliknutí inspekční lupou na strom se zobrazí informace.

Výchozí hodnota: Zapnuto

grounf_info: Zapne zobrazení informací o terénu. Při kliknutí na zemský povrch se pak zobrazí souřadnice a název příslušného podnebného pásu.

Výchozí hodnota: Vypnuto

townhall_info: Povolí zobrazení informací o budově radnice (po kliknutí na budovu). Pokud není povoleno, kliknutí na budovu radnice zobrazí pouze informace o městě.

Výchozí hodnota: Vypnuto

only_single_info:

Výchozí hodnota: Zapnuto


Zobrazení

frames_per_second: Kolikrát je za 1s překreslována obrazovka.
Pozn.: Do 112.0 bylo k nalezení pod záložkou Všeobecně.

Výchozí hodnota: 25

simple_drawing_tile_size:

Výchozí hodnota: 24

simple_drawing_fast_forward:

Výchozí hodnota: Zapnuto

water_animation_ms:
Pozn.: Do 112.0 bylo k nalezení pod záložkou Všeobecně.

Výchozí hodnota: 250

window_buttons_right: Tlačítka umístěná v záhlaví okna se zobrazí na pravé straně.

Výchozí hodnota: Vypnuto

window_frame_active:

Výchozí hodnota: Vypnuto

front_window_bar_color:

Výchozí hodnota: 1

front_window_text_color:

Výchozí hodnota: 215

bottom_window_bar_color:

Výchozí hodnota: 4

bottom_window_text_color:

Výchozí hodnota: 209

show_tooltips: Zapne zobrazování kontextové nápovědy - textu, co se objeví při najetí myší nad tlačítko.

Výchozí hodnota: zapnuto

tooltip_background_color:

Výchozí hodnota: 4

tooltip_text_color:

tooltip_delay: Za jak dlouho se po najetí myši nad tlačítko s kontextovou nápovědou zobrazí nápověda (v milisekundách).

Výchozí hodnota: 500

tooltip_duration: Jak dlouho bude zobrazena kontextová nápověda. (v milisekundách)

Výchozí hodnota: 5000

cursor_overlay_color

left_to_right_graphs: Mění směr osy x v grafech.
Pozn.: Do 112.0 bylo k nalezení pod záložkou Všeobecně.
Doporučuji zapnout.

Výchozí hodnota: Vypnuto

Hospodářství a města

remove_dummy_player_months:
Pozn.: Nové, od verze 112.1

Výchozí hodnota: 6

unprotect_abandoned_player_months:
Pozn.: Nové, od verze 112.1

Výchozí hodnota: 0

starting_money: Množství peněz, s jakým začínáte.

pay_for_total_distance:
0 - platba za rozdíl vzdálenosti zbývající do cíle (může být záporná)
1 - platba za rozdíl vzdálenost do příští přestupní stanice
2 - platba za rozdíl vzdálenosti od minulé stanice

Obtížnost zvýšíte nastavením hodnoty na 1 nebo 0 (ještě těžší).

Výchozí hodnota: 2

bonus_base_factor:
Pozn.: Nové, od verze 112.1

Výchozí hodnota: 125

first_beginner

Výchozí hodnota: Vypnuto

beginner_price_factor: Kolikrát má začátečník dostat víc zaplaceno za přepravu. Údaj je v promile, tj. 1000 je stejně jako normálně.

Výchozí hodnota: 1500, tj 1,5 krát.

allow_buying_obsolete_vehicles: Povolí kupovat i již zastaralá vozidla.
Tip: Doporučuji povolit, v některých grafických sadách jsou mezery, a tak můžete používat aspoň zastaralá vozidla.

Výchozí hodnota: Zapnuto

used_vehicle_reduction: Ovlivňuje snižování hodnoty vozidel s přibývajícím věkem.

Výchozí hodnota: 0

toll_running_cost_percentage: Nastavuje výši mýta v závislosti na provozních nákladech vozidla/soupravy. K celkové výši mýta je připočítána i následující položka. Mýto se platí majiteli komunikace. Je to jeden z mála způsobů, jak může veřejná správa získat finance.
U některých grafických sad bývá nastaveno na 10 (čti 10% z provozních nákladů).
Tip: Zapnutím výběru mýta můžete trochu přidat hře na obtížnosti.

Výchozí hodnota: 0

toll_waycost_percentage: Nastavuje procentuelní výši mýta v závislosti na měsíčních nákladech na údržbu komunikace (při bits_per_month=18). Takto získaná částka je připočítána k předcházející položce (toll_runnin_cost_percentage). Obvyklá hodnota je 5.

Výchozí hodnota: 0

just_in_time: Pokud má odběratel plné sklady, dodavatel přestane dodávat zboží. Když ve skladu objejí volné místo, dodavatel opět začne todávat zboží.
Poznámka: Pokud má dodavatelův sklad kapacitu větší než 15000, pak funguje jako "černá díra" pro všechno dodané zboží které přesáhne hodnotu 15000.

Výchozí hodnota: Zapnuto

maximum_intransit_percentage:
Pozn.: Nové, od verze 112.1

Výchozí hodnota: 0

crossconnect_factories

Výchozí hodnota: Vypnuto (Pro hru skompilovanou s volnou OTTD_LIKE je to Zapnuto)

crosconnect_factories_percentage

Výchozí hodnota: 33 (Pro hru skompilovanou s volnou OTTD_LIKE je to 100)

industry_incerase_every: Při jakém růstu obyvatel dojde i k růstu průmyslu. (Postaví se nová(é) továrna(y), nově se propojí stávající.) Nižší číslo znamená víc továren.

Výchozí hodnota: 2000

factory_spacing: Minimální vzdálenost mezi továrnami.
Tip: Máte-li rádi dlouhé vlaky nebo nechcete-li mít výrobce a spotřebitele nalepené na sebe, doporučuji zvýšit. Aspoň na dvojnásobek. Při přílišném zvýšení hodnoty nebude dost místa vhodného pro výstavbu továren.

Výchozí hodnota: 6

electric_promile

Výchozí hodnota: 330

allow_undeground_transformers:
Pozn.: Od verze 112.0

Výchozí hodnota: Zapnuto

passenger_factor

Výchozí hodnota: 16

minimum_city_distance: Minimální vzdálenost mezi městy.
Tip: Doporučuji zvýšit na dvojnásobek.

Výchozí hodnota: 16

special_building_distance:
Pozn.: Od verze 112.0

Výchozí hodnota: 3

factory_worker_radius: Z jakého okolí dojíždí do továrny zaměstnanci.

Výchozí hodnota: 77

factory_worker_minimum_towns: Z kolika minimálně měst dojíždí do továrny zaměstnanci.

Výchozí hodnota: 1

factory_worker_maximum_towns: Z kolika nejvýše měst budou do továrny dojíždět zaměstnanci.

Výchozí hodnota: 4

factory_arrival_periods

Výchozí hodnota: 4

factory_enforce_demand

Výchozí hodnota: Zapnuto

factory_worker_percentage

Výchozí hodnota: 33

tourist_percentage

Výchozí hodnota: 16

locality_factor[].year

locality_factor[].factor

passenger_multiplier

Výchozí hodnota: 40

mail_multiplier

Výchozí hodnota: 20

goods_multiplier

Výchozí hodnota: 20

growth_factor_villages

Výchozí hodnota: 400

growth_factor_cities

Výchozí hodnota: 200

growth_factor_capitals

Výchozí hodnota: 100

random_pedestrians

Výchozí hodnota: Zapnuto

stop_pedestrians

Výchozí hodnota: Zapnuto

citycar_level

Výchozí hodnota: 5

default_citycar_life

Výchozí hodnota: 36

Spojení

separate_halt_capacities

Výchozí hodnota: Vypnuto

avoid_overcrowding

Výchozí hodnota: Vypnuto

no_routing_over_overcrowded: Vypne přepravu čehokoliv přes přeplněné zastávky.
Pokud se zastávka stane přeplněnou (je na ní více cestujících/pošty/zboží, než má kapacitu), nebude cestujícími použita jako přestupní stanice.
Tip: Zapněte pro zvýšení obtížnosti.

Výchozí hodnota: Vypnuto

station_coverage: Ovlivňuje velikost spádové oblasti stanice - kolik polí od zastávky bude pokryto.
Pozn: Nastavení se liší v závislosti na grafické sadě, 2 je občas málo (při více hráčích není pomalu kam dávat ve městech zastávky), 4 může být pro někoho příliš moc, 3 je dobrý kompromis.

Výchozí hodnota: 2

max_route_steps

Výchozí hodnota: 1 000 000

max_hops

Výchozí hodnota: 2000

max_transfers

Výchozí hodnota: 9

Následující parametry jsou využívány při automatické výstavbě silnic na začátku hry. Toto neměňte, pokud nevíte, co děláte!!!
way_straight (1)
way_curve (2)
way_double_curve (6)
way_90_curve (15)
way_tunnel (8)
way_max_bridge_length (15)
way_leaving_road (25)

Výdaje

maintenance_building

cost_multiply_dock: Náklady na výstavbu přístavu.

cost_multiply_station: Náklady na výstavbu železniční stavice.

cost_multiply_road_stop: Náklady na výstavbu silniční zastávky. Výsledná cena vzniká vynásobením této konstanty úrobní zasávky. (Vyšší úroveň znamená vyšší kapacitu)

cost_multiply_airterminal: Náklady na výstavbu letištního terminálu.

cost_multiply_post: Náklady na výstavbu pošty.

cost_multiply_headquarter: Náklady na výstavbu sídla společnosti.

cost_depot_air: Náklady na výstabu hangáru.

cost_depot_rail: Náklady na výstavbu železničního depa.

cost_depot_road: Náklady na výstavbu silničního depa.

cost_depot_ship: Náklady na výstavbu doků.

cost_buy_land: Náklady na umístění cedulky.

cost_alter_land: Náklady na změnu terénu pouhým nasypáním zeminy nebo vykopáním jámy.

cost_set_slope: Náklady na změnu terému, změny však potřebují opěrné zdi.

cost_found_city: Cena za založení nového města.

cost_multiply_found_industry

cost_remove_tree: Náklady na odstranění jednoho stromu.

cost_multiply_remove_haus: Náklady za odstranění domu. Číslo se pak násobí úrovní domu.

cost_multiply_remove_field: Náklady na odstranění jednoho políčka s polem.

cost_transformer: Cena za výstavbu trafostanice.

cost_maintain_transformer: Cena za údržu trafostanice.

Nastavení podnebí

Viz samostný dialog nastavení podnebí

forest_base_size

forest_map_size_divisor

forest_inverse_spare_tree_density

max_no_of_trees_on_square: Kolik stromů se může maximálně vyskytovat na jednom poli.
Pozn: Překreslování stromů je náročné na výkon.

Výchozí hodnota: 3

tree_climates

Výchozí hodnota: 0

no_tree_climates

Výchozí hodnota: 0

Nápověda odpovídá verzi 112.1, poslední úprava 1.1.2013

simutrans-124.5/simutrans/text/cz/shiptools.txt000066400000000000000000000024121520675460500217750ustar00rootroot00000000000000Nápověda - lodní doprava

Lodní doprava

Tento dialog vám umožňuje stavět doky a přístavy.

První je symbol přístavů. Kliknutím na něj se vám kurzor změní v obrázek přístavu. Přístavy dovolují naloďovat cestující a zboží z nedalekých továren a měst. Ke stavbě přístavu si vyberte místo na konci pevniny, které přechází na vodu. Lze také stavět velké přístavy - musí se však navzájem dotýkat, či být jinak spojeny - např. železniční zastávkou.

K nastavení lodní zastávky musíte vybrat políčko, které je přímo před přístavem, ne však samotný přístav!Buďte rozvážní se stavbou přístavů, odstraňování přístavů ještě není zcela funkční!

Dva podobné symboly jsou pro stavbu doku, rozdíl je pouze v jejich orientaci, severní nebo východní. Kliknutím na jeden z nich se vám kurzor změní v obrázek doku. Doky jsou určeny pro nákup všech dostupných typů lodí, a pro správu lodí zde zaparkovaných (prodávání, nastavování linek a jízdního plánu aj.). Ke stavbě si vyberte místo na vodě, které chcete, může to být jakýkoliv vodní čtverec.

->Informace o překladu

simutrans-124.5/simutrans/text/cz/signal_spacing.txt000066400000000000000000000017261520675460500227410ustar00rootroot00000000000000Nápověda - nastavení vzdálenosti semaforů

Nastavení vzdálenosti mezi semafory

Dialog slouží k nastavení vzdálenosti mezi semafory, pokud jsou umisťovány pomocí tažení.

Dialog vyvoláte kliknutím myši na ikonu se semaforem v nabídce s nástroji pro železnici, tramvaje nebo jednokolejku při stisknuté klávese [Ctrl].

Semafory můžete umísťovat tažením na již existující trať. Tj. stejným způsobem jako když jste stavěli koleje.

vzdálenost mezi semafory: Nastavuje, na kolik polí připadá jeden semafor.

Odstranit vložené semafory: Odstraní semafory které přesahují nastavené množství jednoho semaforu na n políček.

Nahradit vložené semafory: (zatím nic nedělá??)

simutrans-124.5/simutrans/text/cz/signals.txt000066400000000000000000000074311520675460500214170ustar00rootroot00000000000000Nápověda - semafory

semafory

Text popisuje způsob, jak umisťovat semafory pro železnici, úzkokolejku, tramvaje a jednokolejku.

Při prvním kliknutí se umístí návěstidlo pro oba směry, při dalším kliknutí se změní na jednosnměné, přičemž průjezd opačným směrem je zakázán. Dalším kliknutím změníte směr. Čtvrtým kliknutím se vrátíte k návěstidlu pro oba směry.

návěstidlo traťováho bloku (červené)
Pouští vlak pokud je následující blok volný.
Příklad: V bloku U1 a U4 se nachází vlaky. Na návěstidlu S1 je červená, protože v bloku U1 je vlak. Na návěstidlech S2 resp. S3 je zelená, protože bloky U2 resp. U3 jsou prázdné. Vlak nacházející se v bloku U4 bude pokračovat až k návěstidlu S1 a tam bude čekat do té doby, než bude blok U1 volný. Používejte jej pro dvoukolejné tratě nebo na výhybnách u jednokolejných tratí.
-[--U1---S1---U2---S2---U3---S3---U4----
---------S---------S---------S--------]-
S . . návěstidlo
U . . blok mezi návěstidly
[ ] . směr jízdy (vlevo, vpravo)

návěstidlo vlakové cesty (žluté)
Návěstidlo navíc kopíruje stav následujícího návěstidla. Je užitečné například u křížení dlou tratí.

návěstidlo úseky s více zastávkami: Toto návěstidlo použijete při slepých odbočkách s více zastávkami. Užitečné například v tramvajové dopravě.

návěstidlo volby staniční koleje
Používejte před stanicemi s více kolejemi, návěstidlo vlaku vybere volnou kolej. Vlaky projíždějící stanicí se na tomto návěstidlu zastaví, pokud nemají volnou celou cestu do svého cíle. Tomu lze předejít umístěním návěstidla zákazu volby staniční koleje na každý z výjezdů ze stanice.
_ _ _ _ x------x
-[--ZV-x--------x-C---
----C--x--------x-ZV--]-
_ _ _ _ x------x
C . . návěstidlo volby staniční koleje
ZV .. návěstidlo zákazu volby staniční koleje
x . . křížení kolejí
[ ] . směr jízdy vlaků (vlevo, vpravo)

návěstidlo zákazu volby staniční koleje Toto návěstidlo automaticky vybere pro vlak vhodnou volnou kolej. Viz ukázky užití návěstidel v železničních stanicích.

ukázky použití návěstidel ve stanicích

legenda:
--- koleje
-+- koleje s křížením
-E- zákaz volby koleje
PS návěstidlo vlakové cesty (žluté)
PS[ PS] návěstidlo vlakové cesty (žluté) průjezdné jedním směrem
CS návěstidlo volby koleje
CS[ CS] návěstidlo volby koleje průjezdné jedním směrem (směr je osnačen závorkou)
S S[ S] návěstidlo traťového bloku, průjezdné vlevo, průjezdné vpravo
[ ] směr jízdy u dvoukolejné trati [[ ]] jednosměrka

dvou- a vícekolejné nádraží na jednokolejné trati
----+--E-[[--+---PS---Nástupiště--PS--+--CS[---+----
_ _ +---CS]--+---PS---Nástupiště--PS--+--E-]]--+
Pozn: pokud není dostupné [[ a ]], můžete je v tomto schématu nahradit za PS[ a PS]

dvoukolejné nádraží na dvoukolejné trati
-[--S[----E---+---PS---Nástupiště--PS--+--CS[---S[---[-
-]--S]---CS]--+---PS---Nástupiště--PS--+---E----S]---]-

vícekolejné nádraží s průjezdnými kolejemi na dvoukolejné trati
_ _ _ _ _ _ _ +---PS---Nástupiště---PS---+
-[--S[---E---+-+---PS[---------------E----+--CS[---S[---[-
-]--S]--CS]--+-+---E-----------------PS]--+---E----S]---]-
_ _ _ _ _ _ _ +---PS---Nástupiště---PS---+

simutrans-124.5/simutrans/text/cz/simutrans.txt000066400000000000000000000014621520675460500220020ustar00rootroot00000000000000Nápověda - o překladu

O překladu

Překlad je organizován online komunitou pomocí webové aplikace - https://translator.simutrans.com/.
Původní verze Ondřej Machulda a Jiří Voseček.

Oficiální stránky Simutrans se nacházejí na adrese http://www.simutrans.com. Zde naleznete poslední verze, odkazy, a další informace...
Aktuální informace o dění okolo Simutransu naleznete na blogu - http://blog.simutrans.com.
Velké množství informací obsahuje fórum - http://forum.simutrans.com.
Addons - "přídavky" do Simutrans lze najít na http://addons.simutrans.com a na fóru.

Simutrans (c) Hj. Malthaner & Simutrans team

simutrans-124.5/simutrans/text/cz/slopetools.txt000066400000000000000000000031511520675460500221550ustar00rootroot00000000000000Nápověda - úprava terénu

úprava terénu

Nástroje pro úpravu terénu umožňují zvýšit nebo snížit terén, tytvořit za pomoci opěrných zdí rovinu nebo jen udělat svah šikný v požadovaném směru. Slouží také k tvorbě stoupajících a klesajících tunelů.

Menu s nástroji otevřete kliknutím na ikonu úprava terénu v hlavním menu.

Nástroj můžete použít na polích bez silnic, železnic, budov apod. Výjimku tvoří nástroj upravit svah použitý na koncích komunikací.

Nástroj vyberete kliknutím n příslušnou ikonu.

zvýšit zemi/snížit zemi: zvýší/sníží terén
Kursor se změní na šipku nahoru nebo dolu.
Ke zvýšení resp. snížení lze též použít klávesové zkratky [u] resp. [d].
{Tip: Pokud místo klikání budete držet levé tlačítko myši, můžete snadno vytvořit velkou plochu terénu o stejné nadmořské výšce.

upravit svah: upraví svah do požadovaného tvaru za pomoci opěrných zdí. Maximální rozdíl mezi sousedními poli je omezený na rozdíl 2 výškových stupnů.
Pozn: pomocí tohoto nástroje se dá vytvořit stoupání a klesání v tunelu.

obnovit přírodní terén: Obnoví na daném políčku přirozený snah, odstraní opěrné zdi.

{Tip: Úprava terénu se provádí snáze se zapnutou mřížkou, zapnete ji klávesou [#] }

simutrans-124.5/simutrans/text/cz/sound.txt000066400000000000000000000016701520675460500211060ustar00rootroot00000000000000Nápověda - zvuk a hudba

Zvuk a hudba

Zde můžete podle svého přizpůsobit zvuky, které uslyšíte, a také hudbu, která bude hrát na pozadí hry.

Nastavení hlasitosti zvuků a hudby se provádí pomocí lišty, při pohybu doprava se zvyšuje hlasitost, při posunu doleva se hlasitost snižuje. Hlasitost zvuku ovlivňuje hlasitost zvuků při stavění a hlasitost zvuků vozidel. Hlasitost hudby ovlivňuje hlasitost právě přehrávané písničky.
Poslední tlačítka slouží k převíjení hudby. Levé tlačítko převine písničku na začátek, pravé na konec. Pokud nejsou nakonfigurovány žádné písnicky, jsou tyto tlačítka neaktivní.

Pozor: Hudba není dodávána společně se Simutrans, musíte si ji sami do adresáře Music nakopírovat. Tam také najdete další nápovědu (anglicky).

->Informace o překladu

simutrans-124.5/simutrans/text/cz/special.txt000066400000000000000000000037131520675460500213760ustar00rootroot00000000000000Nápověda - speciální stavby

Speciální stavby

Panel s nástroji speciálních staveb obsahuje stavbu, které nejsou přímo spojené s konkrétním způsobem přepravy.

Panel se speciálními stavbami otevřete z hlavního menu.

Speciální stavby obsahují nástroje:

Pošta: Nástrojem postavíte poštu. Poštu lze posstavit jen vedle již existující stanice.

Rozšíření stanice: Ke stanici lze přistavět budovy rozšiřující její kapacitu, jako například překladiště automobilů, stáje pro dobytek, sklady, zásobník plynu, zásobník na kapaliny apod. Tyto budovy jsou v každé grafické sadě trochu jiné.

P+: Změní hráče. Stejného efektu dosáhnete klávesovou [P]. Existuje-li více hráčů, zobrazte si klávesou [k] obchodní rejstřík a v něm vyberte požadovaného hráče.

založit nové město: Nástrojem postavíte nové město.

Zasadit strom: Nástrojem umístíte na mapu náhodný strom.

elektrické vedení: Nástrojem postavíte vedení vysokého napětí.

Transformátorová stanice: Nástrojem postavíte trafostanici. Trafostanice postavená u továrny může zvýšit produkci továrny.

Značka: Nástroj umístí na herní ploch nápis. Může sloužit například k pojmenování řeky nebo jako nástěnka pro ostatní hráče v síťové hře.

prohodit zastávky:Prohodí v jízních řádech dvě pole na mapě. Nespleťte si ji s následujícím nástrojem, mají podobné ikony.

Zveřejnit zastávku: Převede zastávku na hráče Veřejné služby.

simutrans-124.5/simutrans/text/cz/station.txt000066400000000000000000000031621520675460500214350ustar00rootroot00000000000000Stanice

Dialog stanice

Informační dialog lze zobrazit po kliknutí na libovolnou stanici. Poskytuje různé užitečné informace o stanici, včetně možnosti zobrazení grafů. Můžete také u každé vaší stanice kdykoliv změnit název.

Dialog se skládá ze seznamu, několika ovládacích tlačítek nahoře, a popřípadě také z části s grafy (viz níže).
Vstupní pole nahoře vám umožňuje změnit název. Tlačítko detaily zobrazí detaily o stanici - jaké je odsud přímé spojení, jaké linky obsluhují tuto stanici nebo třeba jaké zboží je napojeným průmyslem produkováno a vyžadováno.

Hlavní informace jsou zobrazeny uprostřed a v dolní části dialogu. Na seznamu se ukazují komodity, které čekají na odvezení ze stanice. Komodity lze řadit pomocí tlačítka přes/počet/cíl.

Seznam zobrazuje komodity již tříděné podle druhu, každý druh komodit je pohromadě v jedné části.

Dále jsou komodity uvnitř každé části tříděny podle toho, kam má být která odvezena, přes jakou přestupní stanici či dle počtu (závisí na tlačítku přes/počet/cíl).

Tlačítkem Chart lze zapnout zobrazení grafů. Zde si pomocí tlačítek můžete vybrat, jakou statistickou informaci o stanici chcete zobrazit.

Poznámka: Dialog je neustále obnovován, proto vidíte vždy aktuální informace o stanici, bez potřeby dialog znovu zavírat a otevírat.

->Informace o překladu

simutrans-124.5/simutrans/text/cz/station_details.txt000066400000000000000000000016331520675460500231430ustar00rootroot00000000000000Detily stanice

detaily stanice

Detaily stanice obsahují informace o stanici, které se nevešly do dialogu stanice.

Okno obsahuje následující informace:

Připojené továrny: Továrny nacházející se ve spádové oblasti dané stanice
{Tip: Spádovou oblast zobrazíte klávesou [v].}

Zboží vyžadované pro okolní průmysl: Zboží, které můžete prostřednictvím této zastávky dovážet.

linky obsluhující tuto stanici: Seznam linek obsluhujících tuto stanici.

Vozidla bez zadané linky ...: Pokud používáte linky, tak by zde nemělo být nic.

Přeprava odkud, čeho a kam: Seznam zboží dopravovaného z této stanice včetně míst kam je dopravováno.

simutrans-124.5/simutrans/text/cz/trafficlight_info.txt000066400000000000000000000016611520675460500234370ustar00rootroot00000000000000Nápověda - silniční semafory

silniční semafory

Silniční semafory slouží k řízení dopravy na křižovatkách.

Nástroj pro umístění silničního semaforu naleznete v menu silniční dopravy.

Okno s nastavením silničních semaforů získáte kliknutím inspekční lupou na pole se semaforem. U semaforů lze nastavit délku fáze pro jednotlivé směry z časový posun sepnutí.

V prvním poli (zleva) se nachází délka fáze (v sekundách) pro průjezd severojižním směrem (z pravého horního rohu do levého dolního rohu).

V druhém poli nastavíte délku fáze (v sekundách) pro průjezd od východu na západ a od západu na východ.

V třetím poli upravíte časový posuv, v jakém mají semafory spínat, aby byl co nejplynulejší průjezd.

simutrans-124.5/simutrans/text/cz/tramtools.txt000066400000000000000000000042431520675460500220010ustar00rootroot00000000000000Nápověda - tramvaje

tramvaje

Nabídka obsahuje nástroje ke stavbě tramvajové přepravní sítě - kolejí, troleje, zastávek a depa.

Tramvaje mají nižší provozní náklady než autobusy což je však vyváženo vysokými náklady na údržbu tramvajových tratí. Provozovat tramvaje se vyplatí při přepravě vyššího počtu cestujících nebo ve městech s velkým počtem aut.

Nabídku s nástroji k výstavbě tramvajové tratě otevřete kliknutím na ikonu tramvaje v hlavním menu.

Najedete-li kurzorem myši nad ikonu s naástrojem, zobrazí se popisek obsahující označení nástroje, náklady na výstavbu, (náklady za údržbu) a maximální rychlost. Menu obsahuje tyto nástroje:

tramvajová trať: Nástroj slouží k výstavbě tramvajové trati. Tramvajovou trať je možné umístit i na existující silnice. Většina tramvají jezdí na elektrickou energii, trať bude potřeba ještě elektrifikovat.

mosty: Tramvajový most obvykle moc nevyužijete. Tramvajovou trať postavíte i na silničním mostu.

tunel: Nástroj použijete pro výstovbu tunelu, trati pro podzemní šalinu.

trolej: Nástrojem elektrifikujete tramvajovou trať. Bez elektrifikace byste mohli používat jen koňmi takžené tramvaje.

odstranit trať: Nástroj odstraňuje trať včetně elektrifikace. Při odstraňování dejte pozor na jednosměrné semafory, ať odstraníte jen to, co chcete.

semafory: Nástroj slouží k umístění semaforů na trať.

tramvajové depo: V depu koupíte tramvaje. Nezapomeňte jej elektrifikovat, jinak elektrické tramvaje nebudou dostupné.

tramvajová zastávka: Nástrojem postavíte tramvajovou zastávku. Tramvaje mohou zastavovat i na autobusových zastávkách.

simutrans-124.5/simutrans/text/cz/underground.txt000066400000000000000000000042671520675460500223170ustar00rootroot00000000000000Nápověda - tunely a podzemí

tunely a podzemí

Tunely můžeme stavět dvěma způsoby: buď rovné přímo z povrchu nebo komplikovanější za pomoci podzemního módu.

přímé tunely

Přímý tunel postavíte kliknutím na ikonu tunelu a následovným kliknutím na svah kopce, kde má tunel začínat. Aby se tunel postavil, musejí být svahy u ústí tunelu správně šikmé.

komplikovanější tunely

Komplikovanější tunely je nutno stavět počástech. Nejprve postavíme stup pod zem. Klikneme na nástroj s tunelem a pak držíme stisknutou sklávesu [Ctrl] a myší klikneme na místo s zamýšleným počátetkem tunelu. Pak se přepneme do podzemního modu.

V podzemním modu nebo při zobrazení jednotlové výškové vrstvy stavíte podzemní trať stejným způsobem, jako nadzemní, pouze používáte ikonu tunel.

Tunely mohou stoupat a klesat. Postavte tunel k místu zamýšleného stoupání/klesání. V menu úprava terénu klikněte na upravit svah (to s opěrnou zdí). Pak klikněte na konec železniční trati v tunelu. A máte v tunelu kopec.

V tunelu můžete umisťovat stanice, semafory a depo stejným způsobem jako na povrchu.

podzemní mód(y)

Ve hře existují dva způsoby, jak zobrazit komunikace pod zemským povrchem.

podzemní mód: Stiskem klávesy [U] se zobrazí jen terén bez všech budov a stromů. Krom toho se zobrazí všechny tunely. To je užitečné, pokud se chcete podívat, co vše se nachází pod zemí. Umožňuje stavět tunely pod městem, k výstavbě tunelů pod vysokohostkým masivem je vhodnější použít zobrazení po vrstvách.

zobrazení po vrstvách: Slouží k zobrazení komunikací v jednostlivých výškových vrstvách. Použijete jej nejen v podzemí ale i při stavbě železniční trati pod mostem.
Najeďte kurzorem myši na pole v úrovni, kterou chcete zobrazit. Stistněte [Ctrl U].

simutrans-124.5/simutrans/text/cz/window.txt000066400000000000000000000021411520675460500212570ustar00rootroot00000000000000Nápověda - uživatelské rozhraní

uživatelské rozhraní

Hra simutrans se ovládá pomocí myši a klávesových zkratek.

Uživatelské rozhraní je rozděleno na tři části: hlavní menu, zobrazení herní plochy a stavovou lištu.

stavová lišta: Stavová lišta v dolní části zobrazuje důležité aktuální informace:

- datum a čas: Aktuální datum a čas, způsob zobrazení data a času upravíte v nastavení.

- jméno hráče: Je jméno právě aktivního hráče. Mezi hráči lze přepínat klávesou P.

- stav konta: Kolik peněz máte na účtu.

- souřadnice: Souřadnice aktuální polohy kurzoru myši.

- rychlost plynutí času: Jak rychle ve hře plyne čas. Plynutí času změníte klávesami [.] a [,].

simutrans-124.5/simutrans/text/de.tab000066400000000000000000001654541520675460500176740ustar00rootroot00000000000000§Deutsch PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: de Deutsch # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 31.05 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Abfahrtszeiten als Datum im Monat allow reverse passage Freie Durchfahrt in Gegenrichtung AMBIENT_SOUND Umgebungsgeräusche CASH_SOUND Kassenklingeln cl_btn_filter_disable aus cl_btn_filter_enable an cl_btn_filter_settings einstellen cl_btn_sort_asc aufsteigend cl_btn_sort_desc absteigend cl_btn_sort_id ID-Nummer cl_btn_sort_income Gewinn cl_btn_sort_name Bezeichnung cl_btn_sort_type Typ clf_btn_alle alle clf_btn_invers inv. clf_btn_keine keine climate area percentage Größe Klimaregion Continue Game Weiterspielen CROSSING_SOUND Bahnübergänge FACTORY_SOUND Fabriklärm Find matching convois Finde gleiche Fahrzeuge gl_btn_sort_bonus nach Bonus gl_btn_sort_name nach Namen gl_btn_sort_revenue nach Erlös gl_btn_unsort unsortiert height based Klima nach Höhe hl_btn_filter_disable aus hl_btn_filter_enable an hl_btn_filter_settings einstellen hl_btn_sort_asc aufsteigend hl_btn_sort_desc absteigend hl_btn_sort_name Bezeichnung hl_btn_sort_type Typ hl_btn_sort_waiting Wartend hlf_btn_alle alle hlf_btn_invers inv. hlf_btn_keine keine humidities Feuchtigkeitgrenzen Install Installiere Paks Lake See Lake height Maximale Seehöhe Maximize height levels Mehr Höhenstufen moisture land Anstieg über Land moisture water Anstieg über Wasser Networks Transportnetz Num pad keys always move map Numlock ignorieren Open Sea Meer Queueing Tendenz Wartend rainfall Luftfeuchte Reselect closes tools Werkzeug abwählen durch erneute Auswahl Return to menu Neues Spiel Road toll Maut Scenario Missionsstatus sea maritim Single GUI Einfenstermodus Sound range: Reichweite: Start this as a server Dieses Spiel als Server starten. temperature borders Temperaturgrenzen temperature-humidity based Klima nach Wetter TOOL_SOUND Werkzeuggeräusche TRAFFIC_SOUND Verkehrslärm Transfers Durchsatz #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Polarklima desert Wüstenklima mediterran Mittelmeerklima rocky Alpines Klima temperate gemäßigtes Klima tropic Tropenklima tundra Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Das Laden des Szenarioskripts ist fehlgeschlagen A building blocks the construction Ein Gebäude ist im Weg! Bridge blocked by way below or above. Die Höhe der Brücke über dem Weg darunter reicht nicht aus. Can't buy obsolete vehicles! Es können keine veralteten Fahrzeuge gekauft werden! Cannot alter water Das Becken kann nicht gefüllt bzw. geleert werden. Cannot build on a double slope! Kann nur auf flachem Hang oder Ebenem Grund gebaut werden! Cannot built depot here! Hier kann kein Depot gebaut werden!\n\nEin Depot kann nur auf einem Wegende\n\nmit dem passenden Typ gebaut werden.\n Cannot built this station/building\nin underground mode here. Dieses Gebäude / Haltestelle kann nicht\nim Untergrund gebaut werden. Cannot connect to the\ncenter of a double slope! Eine Brücke kann nicht in der Mitte eine steilen Kachel enden!\nEvt. kann man dies mit den Erdbewegungswerkezugen ändern. Cannot create generic line!\nSelect line type by\nusing filter tabs. Kann keine Linie vom Typ\n'Alle' erzeugen.\n\nWählen Sie einen\nanderen Linientyp aus Cannot create socket Kann Netzwerk nicht initialisieren! Cannot rotate this building! Dieses Gebäude kann nicht gedreht werden! Client closed connection during transfer Die Verbindung wurde vom Client während des Herunterladens getrennt. Convoi handles exhausted! Maximale Fahrzeugzahl (Convois) erreicht. Convoy already deleted! Fahrzeuge wurden schon gelöscht! Corrupt file Die Datei ist kaputt oder zu kurz. Could not open file Konnte nicht auf die Datei zugreifen. Das Feld gehoert\neinem anderen Spieler\n Das Grundstück\ngehört einem\nanderen Spieler!\n Depots cannot be built on runways! Ein Hangar kann nicht auf Landebahnen gebaut werden! Depots must be built on flat dead-end way tiles! Ein Depot kann nur auf einem flachen Wegenden gebaut werden! Der Besitzer erlaubt das Entfernen nicht Der Besitzer\nerlaubt das\nEntfernen nicht.\n Diese Zusammenstellung kann nicht fahren!\n Diese Zusammenstellung\nkann nicht fahren!\n Failed to write file Konnte die Datei nicht schreiben. Flugzeughalt muss auf\nRunway liegen!\n Flugzeuge können\nnur auf Flughäfen landen!\n Forbidden by scenario Verboten in diesem Szenario Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Hier kann dieses\nFlughafengebäude nicht\ngebaut werden!\n In order to lock the game, you have to protect the public player by password! Um die Karte gegen Spielerwechsel zu schützen, muß die Öffentliche Hand passwortgeschützt sein! Loading a new game will end the current server session! Das Laden eines neuen Spieles wird das laufende Netzwerkspiel beenden! Lost connection\nto server! Server antwortet\nnicht mehr!\n\nSpiel ist nur\nnoch lokal! Lost synchronisation\nwith server. Spiel ist nicht\nmehr synchron zum\nServer!\n\nBitte neu\nladen!\n Maglevhalt muss auf\nMaglevschiene liegen!\n Magnetbahnhalt muss auf Magnetschiene liegen. Monorailhalt muss auf\nMonorail liegen!\n Monorailhalt muss auf\nMonorailschiene liegen!\n Monorails are not available yet! Noch keine Monorailbahn verfügbar! More than one possibility to build this dock found. Mehrere Ausrichtungen für Bau des Docks möglich. Dock noch einmal mit Strg+Klick auswählen und die Ausrichtung im folgenden Fenster wählen. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Schmalspurbahnhalt muss auf\nSchmalspurbahnschienen liegen!\n No bridges over runways! Keine Brücken über Landebahnen bitte! No curves on runways Keine Kurven auf Startbahnen! No suitable crossing Keine passende Kreuzung! (Weg zu schnell?) No suitable way on the ground! Halte können nur auf\ngeraden ebnen geeigneten\nWegen gebaut werden! No through station here! Hier kann kein Bahnhof oder\neine Haltestelle\ngebaut werden. Not allowed to copy object. Kann diese Ding nicht kopieren!\n(Braucht anderen Benutzer oder ist veraltet.) Not enough bytes transferred Vorzeitiges Übertragungsende! Not enough clearance. Zwischen zwei Wegen müssen zwei Höhenstufen liegen! Not enough empty space Nicht genug freier Platz zum Speichern. Not enough money! Zum Bauen reicht das\nGeld nicht mehr! Not initialized Konnte die Bibliothek nicht initialisieren. On narrowgauge track only!\n Ein Schmalspurhalt muss auf Schmalspurgleisen liegen. Only public player can lock games! Spieler können nur durch die öffentliche Hand geschützt werden. Only up and down movement in the underground! Eine Kachel im Untergrund muss kann nur mit dem Absenken/Anheben-Werkzeugen verändert werden! Out of funds Dein Geld reicht nicht mehr dafür! Post muss neben\nHaltestelle\nliegen!\n Ein Stationsgebäude (z.B. \nPostfiliale, Lager)kann \nnur auf einem freien Feld \nneben einer Haltestelle \ngebaut werden! Protocoll error (expecting game) Protokollfehler Server busy Server antwortet nicht! Server version too new Der Server benutzt eine neuere Programmversion. Ship depots must be built on water! Eine Werft muss auf dem Wasser gebaut werden! Show all revenue messages Alle Buchungen an Halten Show no revenue messages Keine Buchungen an Halten Show only player's revenue Nur eigene Buchungen anzeigen Terraforming not possible\nhere in underground view Diese Landschaftsveränderung ist\nnur auf dem Kartenboden möglich.\n This tunnel branches. You can try Control+Click to remove. Dies ist ein Tunnelsystem. Evt. kann es mittels Strg+Klick gelöscht werden! Too far away to merge stations! Halt zu weit entfernt zum Verbinden. Unknown error Allgemeiner Fehler Upgrade must have\na higher level Neue Station muss ein\nhöheres Level haben. Vehicle %s cannot choose because stop too short! Die Haltsuche von %s scheitert an zu kurzen Halten! Watertable reached Kann das Land nicht unter das\nGrundwasserniveau absenken. #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s

Inhalt

%1$s

Hinweise zur Bedienung

%2$s

Spiel starten

%4$s

Wie spiele ich?

%5$s

Werkzeuge

%3$s

weitere Dialoge

%6$s Keyboard Help\n

Keyboard Help

\n Tastaturbefehle\n

Tastaturbefehle

\nDie folgenden Tastaturbefehle kennt Simutrans:

\nCtrl(Strg) + Klick - unterdrücke Infofenster (nur an einigen Orten)
\nCtrl(Strg) + Werkzeug - auf oberer Ebene agieren bzw. nur gerade Wege bauen.
\n\nEsc, Delete: Oberstes Fenster schließen
\nBackspace: Alle Fenster schließen
\nBild hoch/Bild runter, </>: Zoom kleiner/größer
\nCursortasten und Ziffernblock verschieben die Karte\n

\nFolgende Befehle sind frei belegbar:
\n At least one stop is connected to the town. Mindestens ein Halt des Spielers ist an ein Gebäude in der Stadt angebunden. At least one tile is connected to one stop. Mindestens ein Gebäude ist mit einem Halt eines Spielers verbunden. Color according to transport capacity left Farbe zeigt freie Transportkapazität an. Color-coded terrain according to altitude. Zeige/Verstecke Geländehöhenrelief. Decrease water height Wasser ablassen Highlight railroad tracks Schienewege hervorheben Highlite depots Zeige alle Depots. Highlite electrical transmission lines Hochspannungslinien hervorheben. Highlite factories Zeige Fabriken. Highlite forests Zeige Wälder. Highlite tourist attraction Zeige Sehenswürdigkeiten. Increase water height Wasserstand um eins erhöhen Open station/stop details Stationsdetails anzeigen Overlay city limits Zeige Stadtgrenzen. Overlay passenger destinations when a town window is open Falls Stadtfenster göffnet: Zeige Passagier-/Postziele der Stadt. Overlay schedules/network Lege Plan alle bestehenden Verbindungen über die Karte. Overlay town names Zeige Stadtenamen. Please click on the map to add\nwaypoints or stops to this\nschedule. Wegpunkte und Halte werden durch\nAnklicken der Ziele auf der Karte\nhinzugefügt. Remove double stops from schedule Aufeinanderfolgende Doppeleinträge entfernen Set tile climate %s %s für diesen Boden Show capacity and if halt is overcrowded Zeige Haltkapazität und -status. Show how many convoi reach a station Zeige Anzahl haltender Fahrzeuge. Show how many people/much is waiting at halts Zeige Anzahl wartend. Show initial passenger departure Zeige Fahrtantritt Passagiere/Post. Show level of city buildings Zeige Stadtgebäude entsprechend der Passagiererzeugung. Show mail service coverage/mail network Zeige Stationsabdeckung/Verbindungen Post. Show passenger coverage/passenger network Zeige Stationsabdeckung/Verbindungen Passagiere. Show speedlimit of ways Zeige Höchstgeschwindigkeit von Wegen. Show the change of waiting at halts Zeige die Veränderung wartend/lagernd. Show the owenership of infrastructure Färbe Infrastruktur entsprechend dem Besitzer ein. Show transported freight/freight network Zeige Frachtransport/Frachtverbindungen. Show usage of network Zeige die Zahl der Fahrzeuge pro Weg. Shows a listing with all industries on the map. Zeigt eine Legende der Industriezweige. Sum of departure/arrivals at halts Zeige die Summe Ankünfte + Abfahrten. #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n Touristen nutzen nun\n%ss\nVerbindung zwischen\n%s und\n%s bei (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s\nschafft nun Arbeiter\naus %s\nzur Fabrik\n%s\nbei (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s fährt\nnun mit %i LKW zwischen\n%s (%i,%i)\nund %s\nbei (%i,%i).\n %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s hat eine\nneue Bahnverbindung\nzwischen %s\nbei (%i,%i) und\n%s\nbei (%i,%i) eingeweiht.\n Airline service by\n%s\nnow between\n%s \nand %s.\n %s hat\neine Flugverbindung zwischen\n%s und\n%s eingerichtet. Ferry service by\n%s\nnow between\n%s \nand %s.\n Schiff Ahoi!\n%s Kreuzfahrten\nbringt nun Passagiere von\n%s nach %s. Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\nhat eine neue\nReisebusverbindung\nzwischen %s\nund %s\neröffnet.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Karteneditor LISTTOOLS Listenverwaltung MAGLEVTOOLS Magnetbahnbau MONORAILTOOLS Monorailbau NARROWGAUGETOOLS Schmalspurbahnbau RAILTOOLS Eisenbahnbau ROADTOOLS Straßenbau SHIPTOOLS Kanäle, Häfen und Werften SLOPETOOLS Gelände verändern SPECIALTOOLS Weitere Werkzeuge TRAMTOOLS Straßenbahnbau #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). Neuer Firmensitz von %s eröffnet. Factory chain extended\nfor %s near\n%s built with\n%i factories. Wirtschaft brummt:\n%s bei %s erweitert:\n%i neue Fabriken. Net wealth less than 10%% of starting capital! Gesamtvermögen ist weniger als 10%% des Startkapitals! New %s now available:\n%s\n %s '%s'\nist nun verfügbar. New factory chain\nfor %s near\n%s built with\n%i factories. Gründerzeit:\nDurch %s\nbei %s\nentstehen %i weitere\nFabriken.\n New vehicle now available:\n%s\n Ein neues Fahrzeug ist\nab jetzt verfügbar:\n\n»%s«\n\n Now %u clients connected. Aktuell %i Mitspieler. overtaking überholend Remove vehicle from map. Use with care! Fahrzeug wird vom Bildschirm entfernt. Verkaufswert wird als Gewinn gutgeschrieben. Sends the convoi to the last depot it departed from! Schickt den Fahrzeugverband in das nächste Depot. Server preparing game ... Server bereitet Kartenübertragung vor ... Trees Bäume With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s ehrt die \nwackeren Pioniere von Simutrans \nmit einem neuen Denkmal.\n%i Zuschauer sind mit dabei. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Linienfilter #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%.2f$/km %.2f$/m) (%.2f$/km %.2f$/Monat) (%.2f$/km) (%.2f$/km) (%i)- (%i) (in depot) (im Depot) [0] south-facing (0) südwärts [1] east-facing (1) ostwärts [2] north-facing (2) nordwärts [3] west-facing (3) westwärts [4] southeast corner (4) Südostecke [5] northeast corner (5) Nordostecke [6] northwest corner (6) Nordwestecke [7] southwest corner (7) Südwestecke [CTRL] Strg [END] Ende [HOME] Pos1 /month /Monat \nBauzeit bis bis \nBauzeit von \nGebaut ab \nCan't open heightfield file.\n \nKann Reliefdatei nicht lesen.\n \ndirection: \nRichtungen: \nelektrified \nelektrifiziert\n \nHeightfield has wrong image type.\n \nFalscher Dateityp.\n \nis reserved by: \nreserviert von \nminimum speed: \nMindestgeschwindigkeit: \nnot elektrified \nnicht elektrifiziert\n \nRibi (masked) \nRichtungen (erl.) \nRibi (unmasked) \nRichtungen (alle) \nSet phases: \nAmpelphasen:\n / dann \\ dann Offset \nsingle way Keine Einfahrt! \nway1 reserved by \nWeg 1 reserviert bei \nway2 reserved by \nWeg 2 reserviert bei \nwith sign/signal\n \nHat Verkehrszeichen/Signal\n %d buildings\n %d Gebäude\n %d convois %d Fahrzeuge %d Einzelfahrzeuge im Depot %d Einheiten im Depot %d more convois %d weitere Fahrzeuge %i car(s), %i Wagen, %i years %i months old. %i Jahre und %i Monate alt. %s at (%i,%i) now public stop. %s ist nun öffentlicher Umsteigepunkt. %s building %s %s %s %s %s %s city %d %s %s %d %s %s factory %s %s %s %s Betriebshof %s has entered a depot. %s\nhat das Depot erreicht. %s has left. %s hat den Server verlassen. %s land %d %s %s %d Land %s %s now known as %s. %s heißt jetzt %s. %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. Bürgermeister will\nnicht mehr frieren:\n%s\nbaut ein neues Rathaus.\n %s\nis crowded. %s\nist überfüllt. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\nHöchstgeschwindigkeit %3$i km/h\n\nFährt gerade %2$i km/h.\n\n %s\nwas liquidated. %s is bankrott gegangen.\nDie Firma wurde aufgelöst,\nund alles verkauft. %u Client(s)\n %i Mitspieler\n %u days ago vor %u Tagen %u hours ago vor %u Stunden %u minutes ago vor %u Minuten %u Player (%u locked)\n %i Unternehmen (%i mit Passwort)\n %u years ago vor %u Jahren

Error

Fehler: Doppelte Objekte im Hauptverzeichnis

Im

Warning

addons for

Warnung: Doppelte Objekte im Addon-Verzeichnis

Addons für 1 convoi 1 Fahrzeugverband 1 minute ago gerade eben 1 year ago vor einem Jahr 100 km/h = %i tiles/month 100 km/h = %i Felder/Monat 1LIGHT_CHOOSE Helligkeit: 1th 1. 1WORLD_CHOOSE Einstellungen für neue Karte: 21th 21. 22th 22. 23th 23. 2LIGHT_CHOOSE Farbigkeit: 2th 2. 2WORLD_CHOOSE Kartennummer: 31th 31. 3LIGHT_CHOOSE Scrollgeschwindigkeit: 3th 3. 4LIGHT_CHOOSE Inverses Kartenscrolling 5LIGHT_CHOOSE Fußgänger (Haltestellen) 5WORLD_CHOOSE Anzahl Städte: 6LIGHT_CHOOSE Fußgänger (Stadt) 6WORLD_CHOOSE Verkehrsdichte: 8WORLD_CHOOSE Tag und Nacht wechseln A bridge must end on a way! Eine Brücke muss auf einem Weg enden! A bridge must start on a way! Eine Brücke darf nur\nauf einem eigenen Wegende\noder einem freien Hang\nBeginnen oder enden!\n Abfrage Abfragewerkzeug Abnehmer Verbraucher About Details About scenario Copyright Abriss Abrisswerkzeug Absenken Land absenken Abspanntransformator Umspannwerk (Fabrik) Accelerate time Spielablauf beschleunigen Act. load: %u MW\n Verbrauch: %u MW\n Active player only nur eigene Marker Add forest Wald aufforsten Add random citycar zufälliges Stadtfahrzeug erstellen add server Server hinzufügen Add Stop Halt anfügen Add stops for backward travel Füge Stops für Rückfahrt hinzu air Roll-/Startbahn Aircraft Fluglinie aircraft_tab Frachtflugzeuge airplane Flugzeug Airport Flughafen AIRTOOLS Flughafenbau All Alle all convoi tooltips Fahrzeuge: alle Tooltips Allow city growth Stadt darf wachsen Allow player change Spieler darf wechseln allowed climates:\n Klimazonen\n Alters a schedule. Fahrplan verändern and arranged by und arrangiert von Angenommene Waren -- Waren, die hier angeliefert werden können anhaengen Anhängen Anhaenger_tab Anhänger Anheben Land anheben Appends stops at the end of the schedule Halt am Fahrplanende hinzufügen Apply Line Linie zuweisen Arbeiter aus: Arbeiter wohnen in: Arrivals from\n Ankunft aus\n Arrived Angekommen Assets Fahrzeugwert Aufloesen Auflösen Aufspanntransformator Umspannwerk (Kraftwerk) auto automatisch Autohalt muss auf\nStrasse liegen!\n Autohalt muss auf\nStraße liegen!\n Available Verfügbar Available at custom date Verfügbar nach based on Simutrans 84.22.1 aufbauend auf Simutrans 84.22.1 battery Akkumulator baum builder Pflanze Baum Beginner mode Anfängermodus Besonderes Gebaeude Touristenattraktion BF Bahnhof bio Bio Blockstrecke ist\nbelegt\n \nBlockstrecke ist\nbelegt!\n Boden Land Bonus Multiplier: %i%% Bonus-Multiplikator: %i%% Bonus Speed: %i km/h Bonus-Geschwindigkeit: %i km/h Bonusspeed: %i km/h Referenzgeschwindigkeit: %i km/h (für Speedbonus) Boost (%%) erhöht um (%%) Borderless (disabled on fullscreen) Bildschirmfüllend (Softwaremode) bridge is too high for its type! Tal ist zu tief für\ndiesen Brückentyp!\n Bridge is too long for this type!\n Diese Brücke hat nicht die\nbenötigte Spannweite!\n Bruecke Brücke Bruecke muss an\neinfachem\nHang beginnen!\n Die Brücke muss an\neinem geraden\nHang beginnen!\n Brueckenboden Brücke Build air depot Flughafenhangar bauen build choosesignals Einfahrtsignale Build city market Neues Stadtgeschäft Build drain Umspannwerk build HQ Firmensitzbau Build land consumer Auf dem Land wird eine neue Industriekette gebaut Build maglev depot Magnetbahndepot wird gebaut - Kosten pro Depot: Build monorail depot Baue Depot für Monorail Build narrowgauge depot Schmalspurbahndepot bauen Build powerline Überlandleitung wird gebaut - Kosten pro Feld: Build presignals Zwei-Block-Signal Build road depot Bus- und LKW-Depot wird gebaut - Kosten pro Depot: Build ship depot Schiffsdepot wird gebaut - Kosten pro Depot: Build signals Signale Build train depot Bahndepot wird gebaut - Kosten pro Depot: Build tram depot Straßenbahndepot wird gebaut - Kosten pro Depot: Build truck depot Bus- und LKW-Depot wird gebaut - Kosten pro Depot: Building costs estimates ungefähre Baukosten Buildings Stadtgebäude Built artifical slopes Aufschüttungen Built random attraction Neue Sehenswürdigkeit Bus_tab Busse Can be overgrown kompatibel mit Bäumen Can only move from halt to halt or waypoint to waypoint. Kann nur von Halt zu Halt\noder von Wegpunkt zu\nWegpunkt verschieben Cancel Abbrechen Cannot connect to offline server! Server ist offline und nicht erreichbar. Capacity Kapazität Capacity: Kapazität Capacity: %.0f MW Kapazität: %.0f MW Capacity: %d%s %s\n Zuladung: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Kapazität: %s\n» %d (%d%%) Cars are not available yet! Es gibt noch nicht einmal Pferde!\nBitte erst nach dem Urknall starten ... cars.\nstate Fahrzeugen\n Cash Kontostand Category Warenart Change label style Stil der Orts- und Haltsnamen Change player Spielerwechsel Chart Statistik Chat_msg Chat Choose direction Ausrichtung auswählen Choose operation executed on clicking stored/new vehicles Wähle, was mit angeklickten gelagerten/neuen Fahrzeugen geschehen soll. chooses a random map Wählt eine Zufallskarte aus citicens Einwohner Cities can only be built on flat empty ground! Eine Stadt kann nur auf flachen Boden gegründet werden! City attraction Stadtattraktionen City industries Stadtgeschäfte City list Stadtliste City size Größe city_road Stadtstraße citybuilding builder Stadtgebäude bauen CityLimit Stadtgrenzen cl_title Fahrzeugliste cl_txt_sort Sortiert nach: clamp Wertebereich begrenzen Cleanup schedule Doppelhalte entfernen Clear block reservation Reservierte Wege anzeigen/löschen clf_chk_aircrafts Flugzeuge clf_chk_cars Busse/LKWs clf_chk_indepot im Depot clf_chk_maglev Magnetbahnen clf_chk_monorail Monorails clf_chk_name_filter Namen filtern: clf_chk_narrowgauge Schmalspurbahnen clf_chk_noincome kein Gewinn clf_chk_noline ohne Linie clf_chk_noroute ohne Route clf_chk_noschedule ohne Fahrplan clf_chk_obsolete veraltet clf_chk_ships Schiffe clf_chk_spezial_filter Spezialfilter: clf_chk_stucked blockiert clf_chk_trains Züge clf_chk_trams Straßenbahn clf_chk_type_filter Typen filtern: clf_chk_waren Waren filtern: clf_title Fahrzeugliste - Einstellungen Click to open the finance dialog. Anlicken öffnet die Finanzen des Spielers Climate Klimazone Climate Control Einstellungen Landschaft Climates Klimazonen closed Geschlossen Closes topmost line window when new line selected. Schliesst das oberste Linienfenster, wenn eine neue Line gewählt wurde. COLOR_CHOOSE\n Bitte wählen Sie\nzwei Kennfarbe\ndurch Anklicken:\n Company bankrupt Die Firma ist pleite! company_chat Firmeninterner Chat Company_msg Konkurrenz Comparing pak files ... Abgleich der pak-Dateien läuft ... Composed by Komponiert von Configure AI KI-Einstellungen Configure AI setttings Parameter für Computergegner anpassen Congratulation\nScenario was complete in\n%i months %i years. Glückwünsche\n\nDas Spielziel wurde in\n\n%i Monat(en) und %i Jahr(en)\n\nerreicht!\n Connect factory Industrieanlagen mit Verträgen verbinden Connected stops Haltestelle (mit dieser Fabrik verbunden): Connected with server Mit Server verbunden connection Verbindung Connections Verbindungen Constructed by Gemalt von Constructed by %s Gemalt von %s construction speed Baugeschwindigkeit Construction_Btn Ausgaben Bau Consumed Verbraucht contains the following doubled objects:

enthält folgende Objekte mehrfach:

convoi %d of %d Fahrzeugverband %d von %d convoi error tooltips Fahrzeuge: Fehlertooltips Convoi following mode Verfolgtes Fahrzeug im Tunnel Convoi has been sent\nto the nearest depot\nof appropriate type.\n Fahrzeug wurde zum nächsten\npassenden Depot geschickt.\n Convoi is sold when all wagons are empty. Fahrzeugverband wird vom Bildschirm entfernt, sobald dieser leer ist. Verkaufswert wird dem Kontostand gutgeschrieben. convoi mouseover tooltips Fahrzeuge: Maustooltips convoi passed last\nmonth %i\n \nIm letzten Monat vorbeigefahrene \nFahrzeuge: %i\n Convois Fahrzeuge Convois: %d\nProfit: %s Fahrzeuge: %d\nProfit: %s Convoys Fahrzeuge Copy Convoi Kopieren Copy the selected convoi and its schedule or line Der Fahrzeugverband wird mit Fahrplan oder Linienzugehörigkeit kopiert. Copy to clipboard Text in die Zwischenablage kopieren. Cost Betriebskosten cost for removal Kosten für Entfernung Cost per unit Kosten/Einheit Cost: %8s (%.2f$/km %.2f$/m)\n Kosten: %s (%.2f$/km, %.2f$/Monat)\n Cost: %8s (%.2f$/km)\n Kosten: %8s (%.2f$/km)\n Costs Kosten Create a new line based on this schedule Eine neue Linie mit diesem Fahrplan anlegen. curiosity builder Sehenswürdigkeiten bauen curlist_title Touristenziele CURRENCY € Current game Spiel in Hintergrund Currently playing: Gerade abgespielt wird: Customers live in: Kunden kommen aus: Date format Datums- und Uhrzeitformat deactivated in online mode funktionslos im Onlinemode Dec. Dez. Deccelerate time Spielablauf verlangsamen December Dezember decrease underground view level Ebene durch Karte tiefer Del Stop Löschen Delete Line Linie löschen Delete the current stop ausgewählten Halt löschen Delete the selected line (if without associated convois). Linie löschen (Nur möglich ohne Fahrzeuge!) Delete this file. Diese Datei löschen. Delivered Geliefert Demand Bedarf Demand: %.0f MW Last: %.0f MW Denkmal Monument Departed Abgefahren Departs at Abfahrt um (TT:SS:MM) Departure after %% geladen oder Wartezeit (TT:SS:MM) Departure board Fahrtanzeiger Departures per month Abfahrten pro Monat Departures to\n Abfahrt nach\n Destination Zielort Destroying map ... Kartenspeicher wird freigegeben ... Developed by the Simutrans Team Entwickelt vom Simutrans-Team Die Bruecke ist nicht frei!\n Die Brücke ist nicht frei!\n diesel Diesel direct_chat_to: Direktnachricht an directmail Wurfsendung Direkt erreichbare Haltestellen -- direkte Verkehrsverbindungen: disable midi Midi-Musik ausschalten Display bars above factory to show the status Zeigt über Fabriken deren Name, Warenmengen und Produktion an. Display settings Anzeigeoptionen Distance Strecke/Monat distributing cities Erzeuge Städte distributing factories Verteile Fabriken Do not show Nichts anzeigen Dock Hafen Dock must be built on single slope! Ein Hafen muss an einem\ngeraden Ufer gebaut werden!\n Downloading Herunterladen dp_title Depotliste Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Du hast %d Monate Zeit, deine Schulden zurückzuzahlen Durchsatz Max. Economy Wirtschaft & Städte Eigenbesitz\n Öffentl. Eigentum\n Ein %s\npasst hier nicht.\n Ein '%s'\npaßt hier nicht.\n Einstellungen aendern Einstellungen ändern electric Elektrisch Electricity Strom Electricity producer\n\n Stromerzeuger\n\n Electrics_tab Elektrotraktion Electrify track Fahrleitung enlarge map Kartenfläche vergrößern enter a value between %i and %i Erlaubte Werte zwischen %i und %i Enter address Serveradresse eingeben Enter Password Spielernamen und Passwort ändern Error Auch der Computer irrt... Erzeuge neue Karte.\n Bitte warten,\nerzeuge neue\nKarte...\n\n (Kann bei großen\n Karten ein paar\n Minuten dauern.)\n Es wird bereits\nein Fahrplan\neingegeben\n Es wird bereits\nein Fahrplan\neingegeben!\n Extract files Packe Zip-Archiv aus. Fabrikanschluss -- Fabriken, mit dieser Haltestelle verbunden Factories Fabriken factory details Fabrikverbindungen factorybuilder Industrieanlagen bauen Fahrtziel Fahrtziel: Fahrzeuge koennen so nicht entfernt werden Fahrzeuge können so\nnicht entfernt werden.\n Farbe Spielerfarbe Fast forward Schnelllauf February Februar Ferry_tab Fähren Filename Dateiname: Files from: Dateien aus: Finances of %s Finanzen von %s find mismatch Abweichende Paks fl_title Fabrikliste Flug_tab Passagierflugzeuge follow me verfolgen Follow the convoi on the map. Dem Fahrzeug folgen. font size Zeichengröße For questions and support please visit: Fragen und Support gibt es via Forest Wälder Found new city Gründe neue Stadt FPS: Bildrate: Frame time: Bildfrequenz: Free Capacity Freie Kapazität freeplay mode Ohne Bankrott Freight Fracht Friction: Momentaner Bremswert: fuel_cell Brennstoffzelle Full load Warten auf Mindestbeladung Fullscreen (changed after restart) Bildschirmfüllend (Hardware mode, wechsel braucht Neustart) Fussgaenger Fußgänger Game info Kurzinformation / Netzwerkspiele GAME PAUSED Spiel angehalten Game_msg Allgemeines Gear: Übersetzung: Gebaeude Gebäude General Allgemeines generate script Infrastrukturmakro erstellen Generated Erzeugt Generation: %.0f MW Erzeugung: %.0f MW Gewinn Gewinn Give the selected vehicle(s) an individual schedule Gib dem ausgewählten Fahrzeug einen eigenen Fahrplan. gl_btn_sort_catg nach Kategorie gl_title Warenliste go home zum Depot Goods Waren Goods AI Fracht-KI Goods list Warenübersicht Gross Profit Bruttogewinn Groundobj Objekt groundobj builder Landschaftsobjekte bauen Grow city Einwohnerzahl erhöhen Growth Wachstum GUI settings Oberfläche Happy Zufrieden Has slope graphics auch auf Hanglagen Has Snow mit Schnee Helligk. Anzeige Help Hilfe Help text not found Er kann nur die Hilfetexte\nanzeigen, die schon geschrieben\nsind. Wenden Sie sich\nmit ihren Fragen an eines\nder Simutrans-Foren. hide all building alle Gebäude minimieren hide city building Stadtgebäude minimieren Hide labels Ort- und Haltnamen nicht anzeigen hide objects under cursor Dinge im Umkreis um den Mauszeiger minimieren. hide station names Stationsnamen ausblenden hide transparent Transparenz statt minimieren hide trees Bäume minimieren Hier warten/lagern: Wartende Passagiere/Güter: Higher transport fees, crossconnect all factories höhere Transportgewinne, keine Produktionsstopps wenn überfüllt highlight single ways Enden / Unterbrechungen von Verkehrswegen anzeigen. Highlite schedule Fahrplanhalte hervorheben hl_title Haltestellenliste hl_txt_filter Filter: hl_txt_sort Sortiert nach: hlf_chk_airport Flughäfen hlf_chk_anleger Häfen hlf_chk_bahnhof Bahnhöfe hlf_chk_bushalt Bushaltestellen hlf_chk_frachthof Frachthöfe hlf_chk_keine_verb keine Verbindung hlf_chk_maglevstop Magnetbahnhof hlf_chk_monorailstop Monorailhalt hlf_chk_name_filter Namen filtern: hlf_chk_narrowgaugestop Schmalspurbahnhof hlf_chk_overflow überfüllt hlf_chk_spezial_filter Spezialfilter: hlf_chk_tramstop Tramhaltestelle hlf_chk_type_filter Typen filtern: hlf_chk_waren_abgabe Gibt ab: hlf_chk_waren_annahme Nimmt an: hlf_title Haltestellenliste - Einstellungen Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Kein Heimdepot gefunden!\nÄndern Sie den\nFahrplan entsprechend\nmanuell. Homeless Obdachlos hydrogene Wasserstoff Icon size: Werkzeugsymbolgröße Idle: Leerlauf: ignore climates alle Klimazonen In the industry legend show only currently existing factories Zeige Legende nur für existierende Fabriken. In Transit Unterwegs Increase Industry density Industriedichte erhöhen increase underground view level Ebene durch Karte höher industrial building Industriegebäude Industry overlay Industriestatusanzeige Infinite mouse scrolling Unbegrenzte Kartenbewegung Infinite scrolling using mouse Funktioniert nicht mit manchen Touchpens! Init map ... Kartenfelder initialisieren ... Input Eingang Ins Stop Halt Einfügen Insert stop before the current stop Halt vor aktueller Fahrplanposition einfügen Install graphics Grafikset hinzufügen Install paks Paksets werden heruntergeladen und installiert. Insufficient funds! Zu wenig Geld! Intercity road len: Verbindungstraßenlänge: Intro. date Einführung Intro. date: Einführung: invalid undefiniert Invalid coordinate ungültiger Eintrag Invert stops Reihenfolge umdrehen isometric map isometrische Anzeige January Januar join game Online spielen July Juli Jump to Zentrieren auf June Juni just now jetzt Kann Spielstand\nnicht laden.\n Kann Spielstand\nnicht laden!\n Kann Spielstand\nnicht speichern.\n Kann Spielstand\nnicht speichern!\n Kein Besitzer\n kein Besitzer\n\n Keine Einzelfahrzeuge im Depot Keine Fahrzeuge im Depot Keyboard_Help\n Tastaturkürzel\n koord nach Orten labellist_title Markierungsliste Lade Relief Lade Höhenkarte Laden Spielstand auswählen und laden Land attraction Ausflugsziel Land industries Industriezweige LANG_CHOOSE\n Bitte wählen Sie\nIhre Sprache:\n LARGE_NUMBER_STRING Mio. LARGE_NUMBER_VALUE 1e6 Last Month Vormonat Last used tools Zuletzt verwendete Werkzeuge Last Year Vorjahr Leaving depot! Verlässt Depot! Left button drags minimap too Linke Maustaste bewegt auch Reliefkartenausschnitt legacy (large heights) Alter Modus (Doppelte Höhen) legacy (small heights) Alter Modus (Halbe Höhen) Legend Kartenlegende Leitung Stromleitung length: %d Länge %d letzen Monat: diesen Monat: letzten Monat: diesen Monat: Line Linie Line Management Linienverwaltung line name mouseover tooltips Fahrzeuge: Linie und Fahrzeugnamen linear Linear Lineless convoys serving this stop Einzelfahrzeuge, die hier halten Lines serving this stop -- Linien, die diese Haltestelle anfahren List of industries on the map Industrieliste LKW_tab LKW Load game Lade Spiel load height data from file Lade Relief aus Datei Load mode: Höheberechnungä Load scenario Wähle Mission Load script tool Skript-Werkzeug laden Load Scripted AI Skript-KI laden Load settings from Einstellungen übernehmen von loaded geladen. loaded passenger/freight Sortiere Passagiere/Güter nach Loading (%i->%i%%)! Beladen (%i->%i%%) Loading (%i%%) departure %s! Lädt (%i%%), Abfahrt %s! Loading (%i%%)! Beladen (%i%%) Loading addon paks ... zusätzliche Addons laden ... Loading map ... Lade Karte ... Loading paks ... Grafikset laden ... Loading skins ... eigene Skins laden ... Loading time: Voll Beladen in: Lock Control key Strg-Taste aktivieren/deaktivieren Lock game Karte gegen weiteren Spielerwechsel schützen. Lokomotive_tab Loks m3 m³ Maglev Magnetbahn maglev vehicle Magnetbahnfahrzeug maglev_track Magnetbahnstrecke Maglevdepot Magnetbahndepot Mail Demand %d\n max. Postaufkommen %d\n Mail level Postaufkommen Mailbox Meldungen anzeigen Mailbox Options Meldungsauswahl Maintenance Instandhaltung make stop public (or join with public stop next) costs %i per tile and level Erzeugen/verbinden mit öffentlichem Stop (%i$ pro Feld und Level) Make way or stop public (will join with neighbours), %i times maintainance In öffentliche Haltestelle umwandeln (mit Nachbarn verbinden), %i-fache Wartungskosten. Manual (Human) Mensch (keine KI) Manufactured: Hergestellt: Map roughness Bergigkeit map view Ansicht map zoom Vergrößerung Mar. März March März Margin (%%) Marge Marker Markierung Max Boost (%%) max. erhöht (%%) Max income: Maximaler Gewinn: Max. speed Maximalgeschwindigkeit Max. speed: Höchstgeschwindigkeit: Max. waiting time Max. Wartezeit (TT:SS:MM) Maximum 254 stops\nin a schedule!\n Nicht mehr als 254\nStopps pro Fahrplan!\n maximum length of rivers max. Länge Maximum tile height difference reached. Die Höhendifferenz\nzweier benachbarter Böden\ndarf nicht größer sein\nals zwei Stufen. Maxspeed Geschwindigkeit May Mai Median Citizen per town Mittlere Stadtgröße: merge stop Haltestelle zusammenlegen MessageOptionsText \nNeues Jahr\n\nKI baut\n\nStadtnachrichten\n\nKeine Route\n\nNeue Industrie\n\nMitteilungen\n\nNeue Fahrzeuge\n\nBahnhof voll\n\nProbleme\n\nStaus\n\nSzenario\n\nÖffentlicher Chat\n\nFirmeninterner Chat\n\nPrivate Nachrichten\n\n minimum length of rivers min. Länge Minimum load Mindestbeladung (%%) Missing pakfiles Es fehlen Objekte im Spiel! Modify the selected line Ändere den Fahrplan der gewählten Linie. Monate alt Monate alt. monorail vehicle Monorailfahrzeug monorail_track Monorailbahn Monorailboden Erhöhter Verkehrsweg Month Monat month wait time max. Wartezeit Monthly departures Feste Abfahrtzeiten Months Monate Monument Denkmal Monuments Denkmäler Mountain height Berghöhe Move the map Verschiebt Kartenausschnitt Movingobj bewegliches Objekt Music playing disabled/not available Musik nicht verfügbar/abgeschaltet Music volume: Musiklautstärke: mute sound Klänge aus Narrowgauge Schmalspurbahn Narrowgauge are not available yet! Noch keine Schmalspurbahnen verfügbar! narrowgauge vehicle Schmalspurbahnfahrzeug narrowgauge_track Schmalspurstrecke Narrowgaugedepot Schmalspurbahndepot Net ID: %p Netz-ID: %p\n Net Wealth Gesamtvermögen Net wealth near zero Firmenvermögen nahezu verbraucht! Neue Karte Neues Spiel Neue Welt Eine Neue Welt new convoi Neuer Fahrzeugverband New Line Neue Linie New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Neue Linie erzeugt!\nDie Linie kann nun oben\nfür diesen Fahrzeugverband\nausgewählt werden. New Vehicles Neue Fahrzeuge Nickname: Chatname: no buildings hidden keine Gebäude minimieren no convois Keine Fahrzeuge zusammengestellt No goods are loaded onto this convoi. Nichts mehr einladen. no goods waiting keine Güter vorhanden no load nicht laden No player Kein Transportunternehmen No Route Keine Route No stop here! Werkzeug wirkt nur auf Haltestellen! No suitable ground! Dieser Boden ist ungeeignet. No suitable townhall available for this climate! Kein Rathaus für dieses Klima vorhanden! No terminal station here! Ein Endhalt kann nur\nauf einem Endstück\ngebaut werden!\n no timeline alle Epochen no tree ohne Wälder No vehicles are available for purchase. Keine Fahrzeuge zum Kauf verfügbar. No. of Factories Fabriken und Geschäfte Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Noch kein Fahrplan\nvorhanden!\n none keine nord Nord nordost Nordost nordwest Nordwest Not allowed to make publicly owned ways! Das Bauen von Wegen der öffentlichen Hand ist nicht erlaubt. Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Nicht möglich!\nDer Fahrplan kann zur Zeit\nnicht geändert werden.\nVersuchen Sie es später\nnoch einmal! Not enough fields would remain. Um jeden Hof muss eine\nMindestanzahl von Feldern\nübrigbleiben. Not online Nicht Online now jetzt Now active as %s.\n Du bist jetzt der Spieler:\n %s. nowhere nirgendwo Number of rivers Anzahl Flüsse Object Objekt Oct. Okt. Odometer: %s km Kilometerstand: %s On loan since %i month(s) %i Monat(e) Konto überzogen On mouseover Wenn unter Mauszeiger on the Abfahrten/Monat, erste am (TT:HH:MM) On this map, you are not\nallowed to change player!\n Spiel geschützt!\nSpielerwechsel ist auf dieser\nKarte nicht möglich.\n Only city chains Fabrikkette (Stadt) bauen Only first %d differing paks reported. There are probably more. Nur die ersten %d abweichenden Objekte sind aufgelistet. Es können noch weitere Differenzen bestehen. Only full Unicode fonts nur komplette Unicodezeichensätze Only land chains Fabrikkette (Land) bauen Only one transformer per factory! Es ist nur ein Transformator je Industrie erlaubt! Only show goods which are currently handled by factories Nur Güter anzeigen, die gerade verarbeitet werden können open Geöffnet Operation Betriebskosten Ops Profit Betriebsgewinn Or enter a server manually: Oder gebe eine Serveradresse ein: Origin Abfahrtsort ost Ost Output Ausgang Ownership Besitzer Pak which may cause severe errors: Es fehlen folgende Objekte (pak), die zum Versagen von Transportketten führen können: Pak which may cause visual errors: Folgende Objekte (pak) wurden durch ähnliche ausgetauscht: Pak(s) different: unterschiedliche Objekte: Pak(s) missing on client: Folgende Objekte fehlen dir: Pak(s) not on server: Folgende Objekte sind dem Server unbekannt: Pakset differences Abweichenden pak-Objekte paletten Paletten Pas_tab Passagierzüge Passagierrate Passagieraufkommen Passagierziele Passagier-/Postziele Passenger AI Passagier-KI Passenger Demand %d\n max. Passagieraufkommen %d\n Passengers %d %c, %d %c, %d no route Fahrgäste %d %c, %d %c, %d ohne Route Passengers %d %s, %d %s, %d no route Fahrgäste %d %s, %d %s, %d ohne Route Password Passwort Pax <%i> Mail <%i> Passagiere <%i> Post <%i> Pax level Passagieraufkommen PaxDest Reiseziele Percent Electricity Anteil Kraftwerke (%%) personal_chat Direktnachrichten Pipette Diese Ding noch einmal bauen Planes are not available yet! Noch keine Fluggeräte erfunden! Plant tree Baum player Spieler player -1 Simutrans-Spieler player 0 Öffentliche Hand player 1 Napik 128 AS player 10 Spieler 10 player 11 Spieler 11 player 12 Spieler 12 player 13 Spieler 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Spedition VM player 5 H-Trans GmbH player 6 PSK & Co KG player 7 Spieler 7 player 8 Spieler 8 player 9 Spieler 9 Player ranking Spielerrangfolge Please choose vehicles first\n Bitte wählen sie zuerst einen Fahrzeugverband aus. Postrate Postaufkommen Power Strommenge Power (MW) Leistung (MW) Power: Leistung: Power: %4d kW\n Leistung: %4d kW\n Powerlines Freileitungen Price Kaufpreis Problems_msg Warnungen Produced Erzeugt Produces: %.1f units/minute aktuell %.1f pro Minute Production of %s has been stopped:\n%s\n %s '%s'\nwird nicht mehr hergestellt. Production/Boost Produktionskennzahlen promote to line Neue Linie public_chat Allgemeiner Chat q1 Frühling q2 Sommer q3 Herbst q4 Winter Query server Server abfragen rail car Schienenfahrzeug random Zufällig Random age Zufallsjahr Random map Zufallskarte Rating Status ratio_pax Verhältnis Relevant Nur Benötigte Remove Label löschen remove airstrips Landebahnen und Rollfeld entfernen remove channels Kanal zuschütten remove interm. signals Sign. an Zwischenstellen entf. remove maglev tracks Magnetbahnstrecke löschen remove monorails Monorailbahnstrecken löschen remove narrowgauge tracks Schmalspurbahnstrecke entfernen remove powerlines Übertragungsleitung entfernen remove roads Straßenzug löschen remove signal Signal entfernen remove tracks Eisenbahnstrecken entfernen Remove wayobj %s Wegzusatz von %s entfernen. replace other signals Andere Signale entfernen replace stop Haltepunkt verlegen request closing Schließung vorbereitet residential house Wohnhaus Restore natural slope Natürliche Neigung wiederherstellen Restwert: Verkaufswert: Retire date Außerdienststellung Retire. date: Außerdienststellung: return ticket Rückfahrkarte Revenue Einnahmen Revenue/unit/100 tiles Gewinn pro 100 Kacheln Revert schedule Änderungen verwerfen Revision: Revision: road Straße road vehicle Straßenfahrzeug Roadsign Verkehrsschild Rotate Building Gebäude drehen Rotate map Karte um 90° drehen Routing Verbindungen Running cost Betriebskosten sack Sack sail Wind Save Spiel speichern Save generated script Speichere erzeugtes Script Saving map ... Speichere Karte ... Scenario complete: %i%% Mission zu %i%% erfüllt. Scenario Debug Debug Scenario Error Log Skriptfehler Scenario Goal Missionsziele Scenario Info Zusammenfassung Scenario information Missionsstatus Scenario Result Spielstand Scenario Rules Regeln Scenario_ Scenario Schedule changing! Fahrplan geändert! Schiff_tab Schiffe Schleppkahn_tab Schleppkähne Screen scale: Bildschirmskalierung: Screenshot Bildschirmfoto Scroll threshold Kartenbewegungstoleranz Search: Suche: Seasons Jahreszeiten Sehenswuerdigkeit Sehenswürdigkeit Select a server to join: Wähle einen Server aus: Select a theme for display Visuelles Design ändern Select display font Zeichensatzauswahl Select one or more graphics to install (Ctrl+click): Wählen Sie eine oder mehrere Grafiksets zur Installation aus (Strg + Klick): Select soundfont Wähle MIDI Soundfont Sell the selected vehicle(s) Verkaufe das ausgewählte Fahrzeug. Selling of the program is forbidden. Simutrans darf nicht verkauft werden. sended abgeschickt SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 Sept. Sep. Served by Angebunden von Served by me Wenn von mir bedient Server did not respond! Server antwortet nicht! Serves Line: Bedient Linie: Service An-/Abfahrten set signal spacing Signalabstand festlegen Setting Einstellungen Ship Schiff shops and stores Gewerbegebäude Show all Alle zeigen show all building Alle Gebäude wieder anzeigen. Show also vehicles no longer in production. Auch Fahrzeuge anzeigen, die nicht mehr produziert werden. Show also vehicles that do not match for current action. Auch Fahrzeuge anzeigen, die im Moment nicht passen. Show always Immer anzeigen Show climates Klimazonen Show contour Geländreflief Show even servers with wrong version or pakset Auch Server anzeigen, die die falsche Programmversion bzw. das falsche Pak-Set haben. Show finances for transport type Zeige Finanzen für Transportart: Show future Auch zukünftige zeigen show grid Gitternetz einblenden Show industry Industrieliste Show legend Datenauswahl Show map scale Werteskala Show mismatched Zeige inkompatible Show networks Netzplan Show obsolete Auch veraltete zeigen Show offline Zeige offline Show only used unbenutzte ausblenden Show outline ohne Gelände Show schedules Linienpläne Show servers that are offline Zeige auch Server an, die zur Zeit offline sind. Show servers where game version or pakset does not match your client Zeigt auch Server an, deren Programm oder Pakset-Version nicht mit dem Client übereinstimmt. show station coverage Stationsabdeckung anzeigen show station names Stationsnamen anzeigen show waiting bars Güterwartebalken anzeigen show/hide block reservations reservierte Blöcke anzeigen Show/hide estimated arrival times Zeige ungefähre Ankunfts- und Abfahrtszeiten show/hide object owner Dinge in Besitzerfarbe anzeigen Show/hide statistics Zeige/verberge Statistik Shows buttons about networks overlay. Zeigt Transportnetzeinstellungen. Shows buttons on network overlay. Zeigt die Einstellungen für Netzpläne. Shows buttons on special topics. Zeigt Auswahlfelder z.B. für Benutzung oder Industrien. Shows consumer/suppliers for factories Zeigt Abnehmer (bzw. mit Shift: Versorger) der Fabrik unter dem Mauszeiger Shows the color code for several selections. Zeigt die verwendete Farbskala z.B. für Wegnutzung an. Shows the currently selected schedule Zeigt den aktuell ausgewählten Fahrplan als Liniengrafik. Shrink city Einwohnerzahl verringern shuffle midis Zufallsauswahl signal spacing Signalabstand Sim: Simloops: Similar view as the main window Kippt die Ansicht analog zur großen Ansicht. Single toolbar only Nur eine Werkzeugleiste anzeigen Size (%d MB): Größe (%d MB): Size (area) Grundfläche sliced underground mode Ebene durch Karte slot empty unbelegt Smart hide objects Mauszeiger freihalten Sort by sortiert nach Sort by: Sortiert nach: Sort waiting list by Sortiere Warteliste nach Sound settings Soundeinstellungen Sound volume: Klanglautstärke: Soundfonts are located in the music directory. Soundfonts die sich im Musikverzeichnis befinden: special freight Sonderfracht Speed Bonus Expressbonus Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Bonus in Prozent:\nMindestgeschwindigkeit Straße %i km/h,\nSchiene %i km/h, Wasser %i km/h, Luft %i km/h.\n Speedlimit Tempolimit Spieler(mz) Spieler Spielstand wurde\ngeladen!\n \nSpielstand wurde geladen!\n Spielstand wurde\ngespeichert!\n \nSpielstand wurde\ngespeichert!\n\n Sprachen Sprachauswahl Start Starten Start the selected vehicle(s) Starte das ausgewählte Fahrzeug. station labels Halte Station tiles: min. Haltlänge: Station_msg Überfüllungen Status Haltstatus steam Dampf Step timeline one year Jahr überspringen Stopping permissions Spieler, die auch hier halten dürfen Stops Haltestellen Storage Auf Lager Storage capacity Lagerkapazität Strassendepot Bus und LKW Depot Strassentunnel Straßentunnel street car Straßenbahnwagen sued Süd suedost Südost suedwest Südwest Summer snowline Sommerschnee ab Supplied: %.0f %% Geliefert: %.0f %% Suppliers Lieferanten The following graphics are unmaintained: Die folgenden Grafiksets werden nicht mehr betreut: The generated script was saved!\n Makro erfolgreich gespeichert.\nJetzt kann mit dem Skriptwerkzeug gebaut werden. The gradient does not fit a tunnel Die Steigung passt nicht für einen Tunnel. The script cannot be saved!\n Konnte Makro nicht speichern. Theme selector Auswahl Oberfläche There are still vehicles\nstored in this depot!\n Es sind noch Fahrzeuge\nin diesem Depot!\n This Month Laufender Monat This Year Laufendes Jahr Tile not empty. Das Feld ist besetzt\nund kann daher nicht\nverändert werden.\n timeline mit Epochen tl_title Stadtliste To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Der Verkehr in %1$s nimmt zu:\n%3$i waren bei der Eröffnung\nvon %2$s. To heavy traffic\nresults in traffic jam.\n Verkehrsmeldung:\nStau seit Monaten.\n To: %s An: %s Toggle day/night view Tag/Nachtmodus umschalten Toggle vehicle tooltips Tooltips für Fahrzeuge umschalten tonnen t Toolbar position: Position der Menüleiste: Total inhabitants: Gesamtbevölkerung: Tourist attractions Touristenattraktionen Tourists Attraktionen Town_msg Stadtwachstum Town: %s\n Teil der Stadt: %s\n Towns Städte track Eisenbahnstrecke Tracks Gleistrassen Traffic Verkehr traffic settings Verkehr Train Zug Trains are not available yet! Züge sind noch nicht verfügbar! Tram Straßenbahn tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. Straßenbahnen %i km/h, Monorailbahnen %i km/h\nMagnetbahnen %i km/h, Schmalspurbahnen %i km/h. tram_track Straßenbahnstrecke Tramdepot Straßenbahndepot Trams are not available yet! Noch keine Straßenbahnen verfügbar! Transferring game ... Karte wird übertragen ... Transformer only next to factory! Umspannwerke können nur auf\neinem freien geraden Feld\ndirekt neben einer Fabrik\ngebaut werden! Translation Übersetzung transparencies Sichtbarkeit transparent background transparenter Hintergrund transparent station coverage transparente Stationsabdeckung Transported Beförderungen Trees disabled! Bäume ausgeschalten! TrolleyBus_tab Trolleybusse Truck Straße tt_Other Sonstige Tunnel muss an\neinfachem\nHang beginnen!\n Der Tunnel muss an\neinem Hang beginnen!\n Tunnel must start on single way! Der Tunnel muss an\n einem Gleis oder\ einer Straße beginnen Tunnelboden Tunnel under the Artistic License unter der Artisitc License underground mode Untergrundansicht UNDO failed! Diese Aktion kann nicht\nmehr rückgänging gemacht \nwerden. Das geht nur \nunmittelbar nach dem \nWegebau! Undo last ways construction Letzten Wegebau rückgängig machen. Unemployed Arbeitslos Unhappy Unzufrieden units/day pro Monat\n Unloading (%i%%)! Entladen (%i%%) Update Line Linie ändern upgrade HQ Firmensitzumbau Usage: %.0f %% Auslastung: %.0f %% Usage/Output Strom Use beginner mode Anfängermodus Use timeline start year Mit Epoche ab Jahr Vehicle %s can't find a route! %s\nfindet keinen Weg\nmehr! Vehicle %s is stucked! %s\nsteckt fest!\n Vehicle count: Anzahl Fahrzeuge: Vehicle details Fahrzeugdetails Vehicle Name Name Vehicle Power Leistung vehicles stored Fahrzeuge im Depot Vergroessere die Karte\n Kartenfläche vergrößern.\n Verkauf Entfernen! verkaufen Verkaufen Verkehrsteilnehmer Stadtautos Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurückzuzahlen.\n vh_title Liste aller Fahrzeuge via %s\n über %s\n via owner via Spieler voranstellen Voranstellen Waggon_tab Waggons waiting wartend Waiting for clearance! Wartet auf freie Strecke! Walked zu Fuß walking Zu Fuß Warnings_msg Verkehrsmeldungen Water Kanal Water level Meereshöhe water vehicle Wasserfahrzeug way %s cannot longer used:\n Weg %s darf\nnicht mehr gebaut werden!\n way %s cannot longer used:\n%s\n %s steht nicht mehr zur Verfügung:\n way %s now available:\n Weg %s kann\nab sofort gebaut werden. Way toll Maut Ways not connected Wege sind nicht verbunden!\n waytype Fahrweg Wegpunkt freier Wegpunkt Weight Gewicht Weight: Gewicht: Weight/unit Gewicht Welcome to Simutrans Willkommen zu Simutrans Welcome, %s! Willkommen %s! west West Wind direction Windrichtung Winter snowline Winterschnee bis withdraw Ausmustern Withdraw All alle ausmustern WRONGSAVE \nInkompatible Spielstands-\nversion. Das Spiel kann\nnicht geladen werden.\n Year Jahr Year %i has started. Das Jahr %i hat\nbegonnen. Years Jahre Your primary color: Kennfarbe 1 Your secondary color: Kennfarbe 2 zooming in Darstellung vergrößern zooming out Darstellung verkleinern Zu nah am Kartenrand Zu nah am Kartenrand!\nBitte es weiter weg\nnochmal versuchen!\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Neuer Weltrekord für Magnetbahnen: %.1f km/h by %s. New world record for monorails: %.1f km/h by %s. Neuer Weltrekord\nfür Monorailbahnen: %.1fkm/h\ndurch %s. New world record for motorcars: %.1f km/h by %s. Bernd Bleifuß erreicht\n%.1fkm/h auf\n%s. \nNeuer Weltrekord! New world record for narrowgauges: %.1f km/h by %s. Neuer Weltrekord für Schmalspurbahnen: %.1f km/h von %s. New world record for planes: %.1f km/h by %s. Lilienthals Erben melden\nneuen Weltrekord:\n%.1f km/h flog\n%s. New world record for railways: %.1f km/h by %s. Da fliegt die Pleuelstange\nweg. Lokführer Braun holt\n%.1fkm/h heraus aus\n%s. New world record for ship: %.1f km/h by %s. Blaues Band mit\n%.1fkm/h vergeben\nan %s. #________________________________script_ai_text_________________________________ #________________________________script_ai_text_________________________________ %s build additional convoy to line: %s %s erhöht die Fahrzeugzahl der Linie: %s %s build rail line from %s (%s) to %s (%s) %s baut Eisenbahn-Linie von %s (%s) nach %s (%s) %s build road line from %s (%s) to %s (%s) %s baut Lkw-Linie von %s (%s) nach %s (%s) %s build ship line from %s (%s) to %s (%s) %s baut Schiffslinie von %s (%s) nach %s (%s) %s extends the route from %s (%s) to %s (%s) %s baut die Strecke von %s (%s) nach %s (%s) aus %s optimize way line from %s (%s) to %s (%s) %s optimiert Strecke von %s (%s) nach %s (%s) %s removes convoys from line: %s %s reduziert die Fahrzeugzahl der Linie: %s vehicles of the line %s were retired Fahrzeuge der Linie %s wurden ausgemustert #_________________________________tooltip_text__________________________________ #_________________________________tooltip_text__________________________________ Add convois with similar schedule to this line. Finde Fahrzeuge mit gleichen Zielen und füge sie hinzu. Attach current coordinate to the comment Hefte die aktuelle Position auf der Karte an die Nachtricht an. Enter intervall in days, hours, minutes Zeitspanne in Tagen, Stunden und Minuten eingeben. Mirrors order of stops Reihenfolge der Halte invertieren. Revert to original schedule Alle Änderung des Fahrplans zurücknehmen. Tutorial not available for this pakset. Please try loading Pak128. Es gibt kein Tutorial für dieses Pakset. Pak, Pak128 und pak192 und vielleicht schon mehr haben ein Tutorial. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL brigg &1_CITY_SYLL bach &2_CITY_SYLL berg &3_CITY_SYLL borstel &4_CITY_SYLL bronn &5_CITY_SYLL bruck &6_CITY_SYLL burg &7_CITY_SYLL büttel &8_CITY_SYLL dorf &9_CITY_SYLL essen &A_CITY_SYLL feld &B_CITY_SYLL steig &C_CITY_SYLL hagen &D_CITY_SYLL hausen &E_CITY_SYLL haven &F_CITY_SYLL heim &G_CITY_SYLL hofen &H_CITY_SYLL holz &I_CITY_SYLL horn &J_CITY_SYLL kirchen &K_CITY_SYLL kloster &L_CITY_SYLL leben &M_CITY_SYLL lingen &N_CITY_SYLL missen &O_CITY_SYLL moor &P_CITY_SYLL mühl &Q_CITY_SYLL reuth &R_CITY_SYLL rode &S_CITY_SYLL ruhe &T_CITY_SYLL stadt &U_CITY_SYLL stede &V_CITY_SYLL stein &W_CITY_SYLL stetten &X_CITY_SYLL tal &Y_CITY_SYLL trup &Z_CITY_SYLL weiler %0_CITY_SYLL Kehr %1_CITY_SYLL Alten %2_CITY_SYLL Alves %3_CITY_SYLL Breit %4_CITY_SYLL Bruch %5_CITY_SYLL Frei %6_CITY_SYLL Friedrichs %7_CITY_SYLL Fürsten %8_CITY_SYLL Gross %9_CITY_SYLL Kalten %A_CITY_SYLL Karls %B_CITY_SYLL Klein %C_CITY_SYLL Lauen %D_CITY_SYLL Ludwigs %E_CITY_SYLL Michel %F_CITY_SYLL Neuen %G_CITY_SYLL Norder %H_CITY_SYLL Ober %I_CITY_SYLL Olden %J_CITY_SYLL Tuna %K_CITY_SYLL Pfullen %L_CITY_SYLL Rast %M_CITY_SYLL Rosen %N_CITY_SYLL Schön %O_CITY_SYLL Schram %P_CITY_SYLL Sommer %Q_CITY_SYLL Steinen %R_CITY_SYLL Söder %S_CITY_SYLL Unter %T_CITY_SYLL Wald %U_CITY_SYLL Wasser %V_CITY_SYLL Wessel %W_CITY_SYLL Bo %X_CITY_SYLL Wies %Y_CITY_SYLL Wilhelms %Z_CITY_SYLL Winter 0extern %s Wald %s 0suburb %s %ssiedlung %s 1center %s %s 1extern %s Zweig %s 1suburb %s %s %s 2center %s Haupt %s 2extern %s Neben %s 2suburb %s Ausbau %s %s 3center %s Zentral %s 3extern %s Land %s 3suburb %s Vorstadt %s %s 4center %s %s Mitte 4extern %s Außen %s 4suburb %s, %s Kolonie 5center %s Transfer %s 5extern %s Wiesengrund %s 5suburb Entwicklungsgebiet %s %s %s 6center %s Zentrum %s 6extern %s Industriepark %s 6suburb %s %sring %s 7center %s Stadt %s 7extern %s Schutzhütte %s 7suburb %s %spark %s 8center %s Knoten %s 8suburb Neu-%s %s %s 9center %s Ring %s 9extern %s Wildfütterung %s 9suburb Bad %s %s Acenter %s Markt %s Bcenter %s Neustadt %s Ccenter %s Vorstadt %s %s Csuburb %s Brunnen %s Dcenter %s Kreisel %s Eextern %s Forellenteich %s Fextern %s Jungbrunnen %s Gextern %s Felsengrund %s Ksuburb %s %stangente %s Lcenter %s Oberstadt %s Mcenter %s Allee %s Ncenter %s %sstern %s Nsuburb %s An der %sbahn %s Oextern Am Ende Qcenter %s Oberstadt %s Rsuburb %s %sseite %s Ssuburb %s Promenade %s Vextern %s Westblick %s Wsuburb Nieder-%s %s Yextern %s Waldkindergarten %s simutrans-124.5/simutrans/text/de/000077500000000000000000000000001520675460500171655ustar00rootroot00000000000000simutrans-124.5/simutrans/text/de/airtools.txt000066400000000000000000000027151520675460500215670ustar00rootroot00000000000000Flughafenbau

Flughafenbauwerkzeuge

Diese Werkzeugleiste dient dem Bau von Rollfeldern, Startbahnen, Hangars, Gangways und Nebengebäuden. Bei der Auswahl eines Werkzeuges verwandelt sich der Cursor in das jeweilige Symbol.

Rollfelder und Startbahnen bilden die Verkehrswege des Flughafens. Sie werden wie Strassen gebaut, damit ein funktionstüchtiger Flughafen entsteht, müssen folgende Regeln eingehalten werden:
- Startbahnen müssen immer gerade sein, dürfen sich aber kreuzen,
- Rollfelder müssen mit der Startbahn verbunden sein, dürfen aber nicht an das Ende einer Startbahn anschliessen, sonst finden Flugzeuge keinen Weg mehr zum Ziel.

Im Hangar kannst Du Flugzeuge bauen sowie Linien einrichten und die Flugzeuge nach der Zuordnung zu Linien starten. Das Depot kann nur auf einem ebenen Rollfeldende platziert werden.

Gangways stellen die Haltepunkte der Flugzeuge zum Ein- und Ausladen dar. Wie ein Hangar kann eine Gangway nur auf ein Endstück des Rollfelds gesetzt werden, und erst mit dessen Bau wird der Flughafen als Haltestelle errichtet. Soll der Flughafen an eine bestehende Haltestelle angeschlossen werden, muss die Gangway diese direkt erweitern.

Nebengebäude für Flughäfen haben dieselben Funktionen wie alle anderen Nebengebäude.

simutrans-124.5/simutrans/text/de/baum_build.txt000066400000000000000000000060271520675460500220360ustar00rootroot00000000000000Bäume pflanzen

Bäume pflanzen

Mit dem Dialog "Bäume pflanzen" kannst du gezielt einzelne Bäume pflanzen. Er lässt sich über den Karteneditor zu öffnen.
TIPP: Diese Leiste enthält auch die Schaltfläche Wald aufforsten, mit der du ganze Wälder mit unzähligen Bäumen hinzufügen kannst.

Oben befinden sich die Auswahl für eine bestimmte Klimazone.
· Alle bedeutet alle zur Verfügung stehenden Bäume werden aufgelistet, unabhängig von den Klimazonen, aber nur Bäume die zur Klimazone des Bodens passende können gepflanzt werden. Will man die Prüfung auf Klimazonen ignorieren dann kann man die Option alle Klimazonen daneben aktivieren. Dann findet keine Prüfung auf die Klimazone beim Pflanzen des Baum mehr statt.
Wählt man eine Klimazone entsprechend dem vorgesehenen Boden aus, so werden nur noch die dafür passenden Bäume angezeigt.

Darunter kann man die Anzeige des Namens und die Sortierung in der Liste ändern.
· Übersetzung: Zeigt die Namen so an, wie diese im Spiel verwendet werden. Dies ist abhängig von der eingestellten Sprache. Wenn keine Übersetzung für die eingestellte Sprache vorhanden ist, wird der Objektname angezeigt.
· Objekt: Zeigt die Programm internen Objektnamen an und sortiert danach.
Der Button daneben kehrt die Sortierung auf absteigend um.

Auswahlliste

· links befindet sich die Auswahlliste mit den Bäumen. Klicken Sie auf einen Baum, um weitere Informationen zu diesem Baum anzuzeigen.
· Rechts werden die Informationen zum ausgewählten Baum angezeigt.
· Name: Name des Baums.
· ein eventueller erläuternder Text
· passenden Klimazonen: Alle für den Baum geeigneten Klimazonen.
· Jahreszeiten: Anzahl der Jahreszeiten, für die Grafiken verfügbar sind.
-- 2 bedeutet es gibt nur Grafiken mit und ohne Schnee. Das passt für Nadelbäume.
-- 4 bedeutet Frühling, Sommer, Herbst und nur eine Grafik für den Winter.
-- 5 bedeutet Frühling, Sommer, Herbst und Winter mit Schnee sowie auch ohne Schnee.
· Gemalt von: Der Name des Autors der Grafik.
· Bild des Baums.
Um den Baum zu pflanzen, klicke im der Karte auf die gewünschte Stelle, nachdem du den Baum aus der Liste ausgewählt hast.

Platzierungsoptionen

· alle Klimazonen: Diese Option deaktiviert die Prüfung, ob geeignet für diese Klimazone, beim Pflanzen des Baums.
· Zufallsjahr: Es wird beim Pflanzen des Baums ein zufälliges Baumalter gewählt. Normalerweise werden sie als junge kleine Bäume gepflanzt, wachsen im Laufe vieler Jahre bis ins hohe Alter und sterben schließlich ab. Dann es dauert nicht lange und ein junger Baum wächst an gleicher Stelle.
Achtung: Viele im gleichen Alter gepflanzte Bäume werden zu gleich verkahlen und sterben. Das sieht dann nach Waldsterben aus.

simutrans-124.5/simutrans/text/de/chat.txt000066400000000000000000000067571520675460500206640ustar00rootroot00000000000000Das Chatfenster

Das Chatfenster

Das Chatfenster kann, während Multiplayer-Spielen über die Haupt-Werkzeugleiste aufgerufen werden. Nachrichten können mit allen aktiven Mitspielenden ausgetauscht werden.

Zuoberst befindet sich ein Kontrollkästchen 'transparenter Hintergrund', mit dem die Sichtbarkeit des Chat-Fensters gesteuert werden kann. Dies kann zB. bei kleinflächigen Bildschirmen nützlich sein.
Darunter erscheinen die drei Reiter, mit denen zwischen den verschiedenen Chat-Arten gewechselt werden kann:
- Allgemeiner Chat
- Firmeninterner Chat
- Direktnachrichten (Privater Chat)
Danach folgt in Sprechblasen-Form der Chat-Verlauf.
Wurden noch keine Nachricht gesendet, ist das Feld einfach leer.
Zuunterst befinden sich die Eingabezeile für Nachrichten und links davon ein Pfeil-Symbol, für das beifügen der gegenwärtigen Koordinaten an die Nachricht. Angefügte Koordinaten werden durch ein Pfeil-Symbol direkt neben der Sprechblase vor dem Datum im Chat-Verlauf angezeigt.

Zum senden einer Nachricht, klickt man in die Eingabezeile, tippt die Nachricht ein und sendet durch Drücken von Eingabe/Enter ab.
Die gesendete Nachricht erscheint nun im Chatverlauf.
Um das leere Eingabefeld zu verlassen kann erneut Eingabe/Enter. gedrückt werden oder mit der Maus an einen beliebigen anderen Ort geklickt werden.
Durch Drücken von esc wird das aktive Fenster geschlossen.

Erhaltene Nachrichten werden ausser im Chat-Fenster, auch im News-Ticker, am unteren Bildschirmrand angezeigt und durch eine weisse Zahl in einem roten Punkt auf dem Chat-Button in der Werkzeugleiste.

Nachrichten im allgemeinen Chat sind für alle Mitspielenden lesbar.
Ausserdem bleibt dieser Chatverlauf über die gesamte Dauer der aktuellen Multiplayer-Karte bestehen.

Nachrichten im Firmeninternen Chat sind "nur" für Mitspielende sichtbar, die in derselben Firma aktiv sind. Theoretisch können jedoch alle eine bestimmte Firma auswählen und den Chat einsehen, auch wenn diese Passwortgeschützt ist.
Dieser Chatverlauf bleibt über die ganze Dauer der Karte bestehen.

Direktnachrichten können zwischen zwei Mitspielenden ausgetauscht werden, ohne dass diese Nachrichten für dritte ersichtlich sind.
Unter dem entsprechen Reiter kann eine aktiver Mitspielender ausgesucht, und anschliessend angeschrieben werden.
Alternativ kann auch im Chat-Verlauf das Pfeil-Symbol, neben des Namens eines Mitspielenden, angeklickt werden, um in den Direktnachricht-Chat mit dieser Person zu wechseln.
Der Chatverlauf wird bei verlassen der Sitzung gelöscht.

Beachte bitte:
- Das Drücken der Tab Taste bewirkt ebenfalls ein Absenden der Nachricht. Ausserdem kommt es vor, dass ein vorbestehender Text in der Eingabezeile unbeabsichtigt abgesendet wird, wenn gerade ein neues Spielmitglied beitritt.
- Nachrichten sind auf maximal 226-Zeichen begrenzt. Das Senden von Grafiken, wie zB. Emojis, ist nicht möglich. Ebenso ist das Einfügen von Zeilenumbrüchen nicht möglich.
- Durch Doppelklicken auf eine Sprechblase, wird deren Text in die Zwischenablage kopiert und durch ein kleines Meldefenster bestätigt (kopiert/copied). Dies ist beispielsweise beim Austausch von URL's zu Forum-Artikeln besonders praktisch.

simutrans-124.5/simutrans/text/de/citybuilding_build.txt000066400000000000000000000043331520675460500235760ustar00rootroot00000000000000Stadtgebäude bauen

Stadtgebäude bauen

Der Dialog ist in vier Bereiche aufgeteilt.
· oben links befindet sich die Auswahlliste mit den Stadtgebäuden
· unten links wird das Bild des ausgewählten Stadtgebäudes angezeigt
· oben rechts befinden sich einige Optionen
· unten rechts werden die Informationen des ausgewählten Stadtgebäudes angezeigt

Auswahlliste

In der Auswahlliste werden alle zur Verfügung stehenden Stadtgebäude angezeigt, wenn die entsprechenden Optionen aktiviert (Wohnhaus, Gewerbegebäude, Industriegebäude) sind. Oben befinden sich zwei Reiter, die die Anzeige umschalten.
· Übersetzung: Zeigt die Namen so an, wie diese im Spiel verwendet werden. Dies ist abhängig von der eingestellten Sprache. Wenn keine Übersetzung für die eingestellte Sprache vorhanden ist, wird der Objektname angezeigt.
· Objekt: Zeigt die internen Objektnamen an.

Die Schriftfarben haben folgende Bedeutung:
· blaue Schrift kennzeichnet Wohnhäuser.
· grüne Schrift kennzeichnet Gewerbegebäude.
· schwarze Schrift kennzeichnet Industriegebäude.

Optionen

· alle Klimazonen: Diese Option deaktiviert die Klimazonenzuordnung beim Bau des Stadtgebäudes.
· Mit Epoche ab Jahr: Es werden nur die Gebäude in der Auswahlliste angezeigt, die im aktuellem Spieljahr zur Verfügung stehen.
· Auch veraltete zeigen: Es werden auch die Gebäude in der Auswahlliste angezeigt, die in früheren Spieljahren mal zur Verfügung standen.
· Wohnhaus: Es werden die Wohnhäuser in der Auswahlliste angezeigt.
· Gewerbegebäude: Es werden die Gewerbegebäude in der Auswahlliste angezeigt.
· Industriegebäude: Es werden die Industriegebäude in der Auswahlliste angezeigt.
· Rotation: Sofern das ausgewählte Stadtgebäude mehrere Ansichten hat, kann hier eine bestimmte Ansicht ausgewählt werden. Die Grafik unten links wird entsprechen gewechselt. Bei der Auswahl von Zufällig, wird beim Bau des Stadtgebäudes irgendeine Ansicht gewählt.

simutrans-124.5/simutrans/text/de/citygrowth.txt000066400000000000000000000221271520675460500221350ustar00rootroot00000000000000Details zu Stadtwachstum

Stadtwachstum

Dieser Text erläutert wie es zum Stadtwachstum kommt. Wie es berechnet wird. Welche Gebäude entstehen und warum. Und wie man über die diversen Parameter Einfluss auf die Stadtentwicklung nehmen kann.

Eine Stadt wächst dann wenn Personen und Post an ihr Ziel befördert werden.
Und sie wächst auch dann wenn zur Stadt gehörende Märkte, Kraftwerke und Fabriken mit Waren versorgt werden.

Wachstumsfaktor Personenverkehr
Die relevanten Werte zum berechnen stehen im Stadtinformation Dialog
· Beförderungen: Anzahl Passagiere ihre Reise angetreten haben. Das sind die lächelnden Passagiere in den Haltestellen.
· zu Fuß: Anzahl Passagiere die zu Fuß gehen.
· Passagiere: Alle Passagiere die befördert werden wollen.
Wachstumsfaktor = (Beförderungen + zu Fuß) / Passagiere * passenger_multiplier
Der passenger_multiplier wird in der simuconf.tab oder im den Einstellungen gesetzt.
Standartwert wenn nicht geändert ist 40.
Der Wachstumsfaktor bewegt sich dann im Bereich 0 bis 40.

Wachstumsfaktor Post Transport
· abgeschickt: Anzahl Postsendungen die unterwegs sind.
· Wurfsendungen: Anzahl Postsendungen die vom Absender zu Fuß zugestellt worden sind.
· Post: Alle in der Stadt anfallenden Postsendungen.
Wachstumsfaktor = (abgeschickt + Wurfsendungen) / Post * mail_multiplier
Der mail_multiplier wird in der simuconf.tab oder im den Einstellungen gesetzt.
Standartwert wenn nicht geändert ist 20.
Der Wachstumsfaktor bewegt sich dann im Bereich 0 bis 20.

Wachstumsfaktor Waren Transport
· Angekommen: Anzahl gefüllter Eingangs-Lager von Fabriken die zur Stadt gehören.
· Waren: Anzahl Eingangs-Lager der Fabriken die zur Stadt gehören.
Achtung: Die Werte "Angekommen" und "Waren" sind etwas schwer Verständlich. Es werden die Eingangslager der Fabriken gezählt, aber nur bei Kraftwerken und Endverbrauchern. Mit Endverbraucher sind hier z.B. Märkte gemeint, aber Vorsicht auch eine verarbeitende Fabrik kann als Endverbraucher gelten, wenn sie mit keinem Abnehmer verbunden ist. Des weiteren zählen Fabriken ohne verbunden Lieferanten nicht. Es werden die Eingangslager bei jedem Tick gezählt, das sind nicht Tage eines Monats sondern im Pak128.german z.B. hat der Monat 50 Ticks. Wenn Arbeiter aus mehreren Städten in einer Fabrik arbeiten, dann wird diese Fabrik jeder dieser Städte als zugehörig betrachtet. Das gilt auch, wenn die Fabrik im Stadtgebiet einer anderen Stadt steht. Für das Stadtwachstum zählt letztlich das Verhältnis von gefüllten zu leeren Eingangslagern der Endverbraucher. Für ein hohes Stadtwachstum, könnte man auch die ungenutzte Fabriken mittels Öffentliche Hand entfernen.

Wachstumsfaktor = (Angekommen) / Waren * goods_multiplier
Der goods_multiplier wird in der simuconf.tab oder im den Einstellungen gesetzt.
Standartwert wenn nicht geändert ist 20.
Der Wachstumsfaktor bewegt sich dann im Bereich 0 bis 20.

Wachstum der Einwohner in der Stadt
Die Wachstumsfaktoren von Passagieren, Post und Waren werden addiert und durch den growthfactor_? geteilt.
Zum Teilen verwendet werden je nach Stadtgröße:
Dorf kleiner 1000 Einwohner growthfactor_villages=400
Stadt mit 1000 bis 10.000 Einwohner growthfactor_cities=200
Großstädte größer 10.000 Einwohner growthfactor_capitals=100
Die growthfactor_? werden in der simuconf.tab oder im den Einstellungen gesetzt.
Um den hier errechneten Wert werden die Arbeitslosen und Obdachlosen erhöht.
Erst wenn die Stadt Wohngebäude errichtet erhöht sich die Zahl der Einwohner.

Durch die gestaffelte Regelung bleiben Dörfer länger Dörfer. Beziehungsweise wird ein eher quadratisches Wachstum nachempfunden, da die Einwohnerzahl in Simutrans linear wächst.

Niedrigere Werte bei growthfactor_? oder höhere bei den ?_multiplier erhöhen das Wachstum.

Jedes pak kann eine eigene simuconf.tab mit eigenen Werten haben. Aus den Standartwerten ergibt sich bei 50 Ticks pro Monat.
Eine Dorf wächst bei optimaler Versorgung um maximal 10 Einwohner pro Monat, oder um 120 Einwohner pro Jahr.
Eine Stadt wächst um 20 Einwohner oder 240 pro Jahr.
Eine Großstadt um 40 pro Monat oder 480 pro Jahr.
Diese theoretischen Wachstumszahlen werden in der Praxis nicht ganz erreicht, weil es der Stadt nicht immer gelingt neue Häuser zu platzieren. Beim Bau von besonderen Gebäuden kann es auch zu Ausreißern nach oben im Wachstum kommen.

Entstehung Stadtautos
Anzahl = random((citycar_level / 16) * log10(Einwohner * (1 - max(Faktor beförderte Personen, Post, Waren))))
Der citycar_level wird in der simuconf.tab oder im den Einstellungen gesetzt.
Kurz: aus den Kategorien Personen, Post oder Waren wird die mit den meisten Transporten genommen und davon die nicht transportieren mit der Einwohnerzahl und citycar_level verrechnet ergibt die Anzahl Stadtautos.

Zusammenhang von Stadtwachstum, Arbeits- und Obdachlosenzahlen
Bei einer neu gegründeten Stadt sind zunächst alle Einwohner arbeitslos und obdachlos.
Die Arbeits- und Obdachlosigkeit bekämpft die Stadt (und nur sie!) mit dem Bau von Stadtgebäuden.
Sie baut neue Gebäude oder ersetzt Gebäude durch solche mit einem höheren Level.
Die Zahl der Arbeits- oder Wohnungslosen sinkt dann um Level * 10 des neuen Gebäudes.
Je besser das Verhältnis von zufriedenen zu generierten Passagieren (und Post) ist, desto schneller wachsen die Städte. Und je schneller sie wachsen, desto schneller steigen Arbeits- und Obdachlosenzahlen an, und desto schneller muss die Stadt wieder bauen, und umso mehr Kunden wollen Deinen Service nutzen. Ein Trugschluss ist es, mit der Beförderung von Arbeitern zu einer Fabrik die Arbeitslosigkeit senken zu wollen, im Gegenteil: Viele Passagiere werden zufrieden, und über das dadurch beschleunigte Wachstum steigen die Arbeitslosenzahlen sogar noch schneller an!
Die Titelzeile des Infofensters ist orange wenn die Arbeits- oder Obdachlosenzahlen über 70 steigen.

Entstehung der Gebäude in der Stadt
- Industrie: (ind) Industriegebäude dienen als Arbeitsstätten innerhalb der Stadt. Ihr Bau oder ihre Erneuerung verringert die Arbeitslosenzahl. Ihre Postrate ist halb so gross wie ihre Passagierrate. Diese Gebäude sind normale Stadthäuser und kennen keinen Warenverkehr.
- Wohngebäude: (res) In den Wohngebäuden leben die Einwohner. Ihr Bau oder ihre Erneuerung verringert die Obdachlosenzahl. Ihre Postrate ist gleich der Passagierrate.
- Gewerbeobjekte: (com) Zu den Gewerbeobjekten zählen u.a. Ladengeschäfte, Lokale, Freizeiteinrichtungen und Bürogebäude. Die Gebäude sind oft auch mit Wohnungen kombiniert. Ihr Bau oder ihre Erneuerung verringert sowohl die Arbeitslosen- wie Obdachlosenzahl. Ihre Postrate ist doppelt so hoch wie ihre Passagierrate.
· Monumente: (mon) Monumente werden zufällig, aber nur einmalig auf der Karte gebaut.
· Öffentliche Gebäude: (tow, cur) Für die Errichtung von öffentlichen Gebäuden wie grössere Rathäuser, Denkmäler und Stadtattraktionen sind die Schwellenwerte in den paks mit dem Parameter build_time=? festgelegt, ebenso eine Wahrscheinlichkeit, mit der die Attraktion gebaut wird.

Auswahl welches Gebäude gebaut wird: (ind, res, com)?:
Berechnung der Score Werte von denen der Typ des Stadtgebäudes abhängt. Die Parameter sind in der cityrules.tab zu finden.
defaults von Simutrans (res_start_score usw.)
--- ( start, near ind, near com, near res)
res ( 0, -8, 0, 8)
com (-10, 1, 8, 1)
ind ( 0, 8, 0, -8)

Erzeugung neuer Industrieketten:
· Industrieketten/Fabriken: Diese können vom Spieler mit Waren beliefert werden, oder liefern Waren.
Wächst die Stadt über industry_increase_every hinaus, werden zusätzlich Fabriken oder auch ganze Ketten gebaut. Diese Schwelle verdoppelt sich jedes mal. Wird der verdoppelte Wert überschritten, werden erneut Fabriken gebaut. Der Wert 0 verhindert das bauen von Fabriken. ?Die Fabrik kann in der auslösenden Stadt stehen, kann aber auch an einem beliebigen anderen Ort stehen.?
Der Wert von industry_increase_every kann in der simuconf.tab oder im den Einstellungen gesetzt werden.
Die Errichtung von Fabriken hat keine Auswirkung auf die Arbeitslosen- oder Obdachlosenzahl.

simutrans-124.5/simutrans/text/de/citylist_filter.txt000066400000000000000000000022531520675460500231410ustar00rootroot00000000000000Stadtliste

Stadtliste

Die Stadtliste wird mit Shift+T oder über die Listenverwaltung aufgerufen.
Im Fenster sind alle Städte der Karte gelistet zusammen mit ihrer Einwohnerzahl und dem aktuellen Wachstum. Die Liste kann nach Name, Einwohnerzahl und Wachstum sortiert werden. Das Feld Statistik öffnet einen Graphen, auf dem Informationen über die Karte abgefragt werden können, so zum Beispiel die Entwicklung der Gesamteinwohnerzahl.

Listeneinträge: Klickst Du einen Stadtnamen an, wird die Karte auf die Stadt zentriert, das Fenster der Stadtinformation öffnet sich.
· Stadtname: Den Namen der Stadt kannst Du in der Stadtinformation ändern.
· Einwohnerzahl: Die Zahl der Einwohner wird angezeigt.
· Wachstumsfaktor: Der Wachstumsfaktor lässt Rückschlüsse auf die Wachstumsrate der Stadt und deren Beförderungsversorgung zu. Je höher der Wert ist, desto geringer ist die Differenz zwischen Beförderungsbedarf und der erbrachten Beförderungsleistung.

simutrans-124.5/simutrans/text/de/citywindow.txt000066400000000000000000000112521520675460500221270ustar00rootroot00000000000000Stadtinformation

Stadtinformation

Die Stadtinformation zeigt Statistiken über die ausgewählte Stadt. Du kannst sie durch anklicken des Rathauses mit der Lupe öffnen. Das anklicken des Stadtnamens in der Stadtliste öffnet die Stadtinformation ebenfalls.

· Name der Stadt: Hier kannst du den Stadtname ändern. Bei Stadtgründung wird ein zufälliger aus der Datei pak/text/citylist_de.txt gewählt.
Vom Stadtnamen werden die Haltestellennamen abgeleitet.

· Größe: Die Einwohnerzahl der Stadt wird angezeigt.
· Bevölkerungszunahme: Die Zahl in Klammern hinter der Größe gibt das derzeitigen Wachstum der Bevölkerung an.
· Gebäude: Anzahl der Häuser in der Stadt.
· Stadtgebiet: Das Gebiet der Stadt umfasst das Quadrat, welches durch die beiden Koordinatenpaare (links oben und rechts unten) gebildet wird. Innerhalb der Stadtgrenzen realisiert die Stadt ihre Bauvorhaben. Mit dem Wachstum der Stadt erweitern sich deren Grenzen. Stadtgebiete können sich überlappen.
· Die Stadt generiert Passagiere und Post zunächst für Bebauungen entsprechend den in den Gebäudeinfofenstern angegebenen Passagier- und Postraten. Reise- und Versandziele können alle Felder der Stadtgebiete (also auch der Ausflug zu den Grünflächen der Städte), der Ausflugziele und Fabriken sein.

· Anzahl Arbeitslose und Obdachlose Mit diesen Werten entscheidet die Stadt ob sie baut und wenn ja, ob sie Wohnhäuser, Industrie- oder Gewerbegebäude baut.
· Stadt darf wachsen: Ein nicht gedrückter Knopf blockiert das Wachstums. Dann werden Häuser und Straßen nicht erneuert oder verändert. Nützlich bei manuell gebauten Städten.

Minikarten mit Passagier- und Postziele: Die beide Weltkarten zeigt die Ziele der Stadtbürger. Links für den letzten und damit für einen kompletten Monat, rechts bisher aufgelaufene Ziele im aktuellen Monat.
- gelber Punkt: Für jede angetretene Reise und für jede abgesandte Postsendung wird das Ziel mit einem gelben Punkt markiert.
- Dunkel oranger Punkt: Dieser wird an das beabsichtigte Ziele platziert, wenn keine Route gefunden wurde.
- Hell oranger Punkt: Dieser markiert das Ziel, wenn der Passagier unglücklich ist, weil bereits die Starthaltestelle überfüllt ist. Er sucht nicht im Fahrplan nach einer Route sondern geht sofort wieder nach Hause.

Statistiken: Die Farben der Kurven entsprechen jenen der Statistikschalter. Werte werden je nach der Einstellung der Anzeigezeiträume jahresweise oder monatweise aufaddiert. Die neuesten Werte stehen links. Klickt man einen Punkt der Kurve an, so wird der genaue Wert angezeigt.
· Einwohner: Die Entwicklung der Einwohnerzahl wird dargestellt.
· Wachstum: Das Bevölkerungswachstum der Stadt wird dargestellt.
· Stadtgebäude: Anzahl der Stadt Gebäude in der Stadt.
· Stadtautos: Passagiere die nicht befördert werden, fahren mit dem eigenen Auto.
· Beförderungen: Anzahl Passagiere die ihre Reise angetreten haben. Das sind die lächelnden Passagiere in den Haltestellen.
· zu Fuß: Anzahl Passagiere die zu Fuß gehen.
· Passagiere: Alle Personen der Stadt die befördert werden wollen.
Je näher beieinander die Graphen für "Beförderungen" und "Passagiere" liegen, desto schneller wächst die Stadt.
· abgeschickt: Anzahl Postsendungen die unterwegs sind.
· Wurfsendungen: Anzahl Postsendungen die vom Absender zu Fuß zugestellt worden sind.
· Post: Alle in der Stadt anfallenden Postsendungen.
Je näher beieinander die Graphen für "abgeschickt" und "Post" liegen, desto schneller wächst die Stadt.
· Angekommen: Anzahl Eingangs-Lager der Fabriken die zur Stadt gehören und die Waren enthalten. Es wird bei jedem Tick gezählt.
· Waren: Anzahl Eingangs-Lager der Fabriken die zur Stadt gehören.
Je näher beieinander die Graphen für "Angekommen" und "Waren" liegen, desto schneller wächst die Stadt.
Die Eingangslager zählen für diese Stadt, wenn auch Arbeiter der Fabrik in dieser Stadt wohnen. Das wird in der erste Tab der Fabrik angezeigt.

Stadtwachstum:
Nun bist Du als Spieler gefragt: Erschließe die Stadt mit dem öffentlichen Verkehr für Personen und Post. Versorge die Märkte mit Waren.
Ohne Versorgung kein Wachstum in der Stadt.
Detaillierte Informationen zum Stadtwachstum findest du hier

simutrans-124.5/simutrans/text/de/climates.txt000066400000000000000000000136271520675460500215400ustar00rootroot00000000000000Klimazonen

Kliamzonen Hilfe

Dieser Dialog erscheint immer zusammen mit dem Neue Welt Dialog. Mit ihm können diverse Details zu den Klimazonen und Bergen einer neuen Karte eingestellt werden.

· Meereshöhe: Die Vorgabe ist -2, es sind Werte von -40 bis 20 möglich. Je niedriger der Pegel ist, desto mehr Land erscheint und umgekehrt dehnen sich die Wasserflächen aus. Es kann noch 3 Ebenen tiefer (z.B. ein Tunnel) gebaut werden, jedoch nicht tiefer als -40. Je größere die von Meeren bedeckt Flächen sind, um so weniger Platz bleibt für Städte. Dann ist möglicherweise nur noch Transport mit Schiffen möglich. Achtung: Auch eine hohe "Maximale Seehöhe" (siehe Parameter weiter unten) kann zu meerähnlich großen Seen führen.

· Berghöhe: Die Vorgabe ist 160, es sind in Abhängigkeit der Bergigkeit (siehe nächsten Parameter) Werte zwischen 0 und 1000 möglich. Bei einer Berghöhe von 1000 und einer Bergigkeit von 0 erreichen die Berge bei einer hinreichend großen Karte eine Höhe von etwa Level 40. Zu hohe und steile Berge bieten wenig Platz für Städte und Industrien.

· Bergigkeit: Legt die Rauheit des Geländes fest. Die Vorgabe ist 4, möglich sind in Abhängigkeit der Berghöhe Werte von 0 bis 10. Er bestimmt wie viele kleinere Hügel und Täler generiert werden. Eine zu hügelige Karte wird eventuell nicht so viele Städte, Industrien oder Verbindungsstraßen erzeugen können, wie eingestellt.

· Winterschnee: Bis zu dieser Höhe herunter fällt im Winter Schnee. Im Laufe des Jahres sinkt bis zum Januar die Schneegrenze auf diesen Wert und steigt bis zum Juli wieder auf die Höhe des ewigen Eis (Gletscherzone). Der Wert hat keine Auswirkungen auf das Spiel sondern dient allein der Optik.

· Windrichtung: Die vorherrschende Windrichtung. Dieser Parameter hat nur Auswirkungen wenn die Klimazonen nach Wetter berechnet werden. Dann können die Bäume entsprechend der Luftfeuchte (Regengebiete) verteilt werden.

Tabs: Klima nach Höhe / Klima nach Wetter Hier wird entschieden nach welcher Methode die Klimazonen berechnet werden und die Grenzen der einzelnen Klimazonen bestimmt. Es sind immer nur die sichtbaren Werte relevant für die Erstellung der neuen Karte.

Klima nach Höhe

· Höhenwerte von bis: Für jede Klimazone kann hier die Höhe in absoluten Level des Spiels angegeben werden. Mindesthöhe ist die Meereshöhe (siehe oben 1. Parameter) Maximalwert ist 127. Die Höhenbereiche dürfen sich überlappen. Die Reihenfolge der Klimazonen ist beliebig. Es darf also auf die Wüste Tundra und danach Tropenklima folgen. Achtung: Jede Höhenstufe sollte mindestens einer Klimazone zugeordnet sein, sonst sieht die Karte etwas seltsam aus. Je nach Pakset können Gebäude, Industrien und Vegetation bestimmten Klimazonen zugeordnet sein und werden dann nur in diesen Klimazonen gebaut. Der beginn der Gletscherzone / Polarklima ist die frühere Sommerschneegrenze. Ab hier herrscht immer Winter. In dieser Gletscherzone gibt es sehr wenige oder keine Städte, Industrien und Vegetation. Achtung: Manche Paksets benötigen eine gleichmäßige Verteilung aller Klimazonen auf die Kartenfläche für die Fabrikketten, da auch deren Fabriken abhängig von den Klimazone gebaut werden.

· Größe Klimaregion: Die Vorgabe ist 10%, es sind Werte von 0% bis 100% möglich. Dieser Wert wird nur relevant wenn sich die Klimaregionen überlappen. Dann beeinflusst der Wert die Größe der Überlappungszonen.

Klima nach Wetter

· Anstieg über Land: Die Vorgabe ist 1, es sind Werte von 0 bis 10 möglich.

· Anstieg über Wasser: Die Vorgabe ist 1, es sind Werte von 0 bis 10 möglich.

· Feuchtigkeitsgrenzen: Die Vorgabe ist 65% bis 75%, es sind Werte von 0% bis 100% möglich.

· Temperaturgrenzen: Die Vorgabe ist 20,15,8,0,-10, es sind Werte von -30 bis 30 möglich. An diesen 5 Temperaturgrenzen wechselt die Klimazone von Wüstenklima bis Polarklima.

· Sommerschnee ab: Ab dieser Höhe beginnt der ewige Schnee. Dort gibt es sehr wenige Städte, Industrien und Vegetation.

Bäume, Seen, Flüsse

· Bäume: Die Vorgabe ist "Zufällig", es sind "Keine", "Zufällig" und wenn die Klimazonen nach Wetter zugeordnet wird auch "Luftfeuchte" möglich. "Keine" bedeutet auf der neuen Karte wird kein einziger Baum generiert.
Achtung: Normal werden Holzfällerlager in Wäldern platziert. Wird jedoch hier "keine" angegeben werden Holzfällerlager beliebig auf der Karte platziert, auch dann wenn der Spieler inzwischen Wälder angelegt hat.
Auf großen Karten beanspruchen Bäume beim Rendern viel CPU Zeit. Verwenden Sie auf PCs mit langsamerer CPU oder wenig Arbeitsspeicher die Option "Keine".

· Maximale Seehöhe: Die Vorgabe ist 8, es sind Werte von Meereshöhe (siehe 1. Wert oben) bis 127 möglich.
Warnung: Wird der Wert zu hoch gewählt können große Seen entstehen, die große Täler, oder weite Teile der Karte mit Wasser auffüllen.

· Anzahl Flüsse: Die Vorgabe ist 16, es sind Werte von 0 bis 1000 möglich.
Achtung: Sollten sie vorhaben die Flüsse selbst zu zeichnen, dann geben sie hier mindestens eine 1 an. Da ansonsten das Programm annimmt, dass auf der Karte keine Flüsse vorhanden sind und Gebäude die an Flüssen stehen sollen einfach beliebig platziert.

· min. Länge: Die Vorgabe für die Mindestflusslänge ist 20, es sind Werte von 0 bis 16 Kacheln unter der maximalen Länge möglich.

· max. Länge: Die Vorgabe für die maximale Flusslänge ist 512, es sind Werte von der minimalen Länge plus 16 bis 1024 möglich.

simutrans-124.5/simutrans/text/de/color.txt000066400000000000000000000005021520675460500210410ustar00rootroot00000000000000Farbauswahl

Farbauswahl

Du kannst die eigene Spielerfarbe festlegen. Die Farbe wird für die meisten Fahrzeuge und Gebäude des Spielers genutzt. Es gibt zwei Farben, eine Primäre und eine Sekundäre.

Die Standardfarbe ist primär hellblau und sekundär gelb.

simutrans-124.5/simutrans/text/de/convoi.txt000066400000000000000000000054121520675460500212250ustar00rootroot00000000000000Fahrzeugliste

Fahrzeugliste

Die Fahrzeugliste wird über die Listenverwaltung oder mit Shift+V aufgerufen und beinhaltet alle Fahrzeugverbände, also Züge, Busse, LKWs, Trams, Flugzeuge, Schiffe und Magnetbahnen des Spielers. In die Liste sind eine Sortier- und eine Filterfunktion eingebaut, um unter vielen Fahrzeugverbänden den gesuchten schneller auffinden zu können.
Der Filter kann entweder "an" oder "aus" sein. Wenn Du einen Fahrzeugverband nicht findest, wähle die Filtereinstellung "aus". Die Voreinstellung ist "aus". Über einstellen können die Filterkriterien ausgewählt werden.

Anzeigen in der Fahrzeugliste:
· Eine Ordnungszahl. Diese nummeriert nur die Liste durch und hat keinen Bezug zum Fahrzeugverband dahinter.
· Der Name (per Vorgabe der Name des ersten Fahrzeugs, das in den Fahrzeugverband eingereiht wurde).
· Der Gewinn des Fahrzeugverbandes für das laufende Jahr.
· Die Linie, welche dem Fahrzeugverband zugewiesen ist. Ist dem Fahrzeugverband keine Linie zugewiesen, wird auch nichts angezeigt. Bei Fahrzeugverbänden, die sich im Depot befinden, wird hier "(in depot)" angezeigt.
· Ein Bild des Fahrzeugverbandes, mit einem Balken für den aktuellen Ladegrad (siehe Fahrezugfenster).

Die Farbe des Names des Fahrzeuges hat folgende Bedeutung:
- schwarz: alles ok.
- rot: Erwirtschaftet keinen Profit.
- weiß: Steht in einem Depot.
- gelb: Steht seit Jahresbeginn (vermutlich an einer Haltestelle und wartet auf Fracht).
- orange: Steckt fest oder findet keine Weg zum Ziel.

Sortierkriterien: Die Sortierung der Fahrzeugliste kann nach 3 Sortierkriterien erfolgen. Die Anzeige-Reihenfolge wird nur beim Öffenen des Fensters und bei der Änderung der Sortierkriterien aktualisiert.

· Bezeichnung: Die Sortierung erfolgt in der Namensreihenfolge. Sie orientiert sich am ASCII-Code der Zeichen, also Zahlen, danach Großbuchstaben, danach Kleinbuchstaben, jeweils in numerischer bzw. alphabetischer Reihenfolge.
· Gewinn: Die Fahrzeugverbände werden nach ihrem im laufenden Jahr erzielten Gewinn (Einnahmen-Betriebskosten) sortiert.
· Typ: Die Liste wird nach Fahrzeugtypen sortiert. Die Typreihenfolge ist: Straßenfahrzeuge, Schienenfahrzeuge, Schiffe, Flugzeuge. Gleiche Zugmaschinen werden zusammengefasst. Innerhalb einer Fahrzeugkategorie wird alphanumerisch sortiert.
· aufsteigend/absteigend: Stellt die Liste auf den Kopf. Bei aufsteigend stehen die kleinsten Werte oben, bei absteigend die größten.

simutrans-124.5/simutrans/text/de/convoi_filter.txt000066400000000000000000000046061520675460500225760ustar00rootroot00000000000000Fahrzeuglistenfilter

Filtereinstellungen zur Fahrzeugliste

Im Fenster der Fahrzeugliste - Einstellungen können die Filteroptionen für die Anzeige in der Fahrzeugliste gesetzt werden. Ein eingedrückter Knopf bedeutet, dass das Kriterium erfüllt sein muss, um durch den Filter zur Anzeige zu gelangen. Dabei werden die Eigenschaften innerhalb einer Hauptgruppe mit logischem "OR" (eines der angewählten Kriterien muss erfüllt sein) und zwischen den Hauptmenüs mit logischem "AND" (alle angewählten Kriterien müssen erfüllt sein) verknüpft. Die Wirkung der Filtereinstellungen ist positiv, es werden nur Fahrzeugverbände angezeigt, welche die eingestellten Kriterien erfüllen.

Namen filtern: Im Textfeld kann der Name eines Fahrzeugverbandes bzw. mehrerer Fahrzeugverbände angegeben werden. Es werden nur diejenigen Fahrzeugverbände in die gefilterte Liste aufgenommen, deren Bezeichner mit dem Name des Filters in allen Zeichen (inkl. Leerzeichen) identisch ist. Achtung: Platzhalterfunktionen wie * oder ? werden nicht unterstützt.

Typen filtern: Die gefilterte Liste zeigt nur Fahrzeugverbände an, welche dem gefilterten Typ entsprechen. Trams und Magnetbahnen sind den Zügen zugeordnet.

Spezialfilter:
· ohne Route: Derzeit ist diese Funktion außer Betrieb.
· ohne Fahrplan: Fahrzeugverbände ohne Fahrplan werden gelistet.
· kein Gewinn: Fahrzeugverbände ohne positiven Profit im aktuellen Jahr werden gelistet.
· im Depot: Fahrzeugverbände, die in einem Depot stehen werden gelistet.
· ohne Linie: Fahrzeugverbände, die keiner Linie zugeordnet sind, werden gelistet.

Waren filtern: Fahrzeugverbände werden nach ihrer transportierten Warenart ausgewählt. Der Filter berücksichtigt teilweise nicht das Ladegut, sondern orientiert sich nach den Fahrzeugeigenschaften. So kann etwa ein Erzzug unter Steine auftauchen, wenn er Steinwagen benutzt.

Filterschalter:
· alle: Alle Güterarten ausgewählt.
· keine: Alle Güterarten abgewählt.
· inv.: Bei Betätigung wird die Güterauswahl umgekehrt.

Warenschalter: Über die Schalter der Warenarten kannst Du eine beliebige Filterauswahl treffen.

simutrans-124.5/simutrans/text/de/convoidetail.txt000066400000000000000000000017661520675460500224200ustar00rootroot00000000000000Erweiterte Fahrzeuginformationen

Erweiterte Fahrzeuginformationen

Dieses Fenster erreicht man durch Auswahl von Details im Konvoifenster. Es werden weitere technische Daten angezeigt, wie Leistung, insgesamt gefahrene km und der aktuelle Zeitwert.

Die Angabe min. Haltlänge gibt an, wie lang Stationen an denen gehalten werden soll sein müssen, damit alle Fahrzeuge geladen/entladen werden.

Die Angabe Maximalgeschwindigkeit gibt an, ........

Mit Entfernen! kann der Fahrzeugverband sofort an dieser Position verkauft werden, ohne zuvor in ein Depot fahren zu müssen.

Weiterhin gibt es noch Ausmustern, danach lädt der Fahrzeugverband nur noch seine Waren ab und wird verkauft, sobald er leer ist.

Darunter folgten alle Einzelfahrzeuge mir ihren technischen Daten. Bei Fahrzeugen mit Laderaum wird auch deren aktuelle Beladung angezeigt.

simutrans-124.5/simutrans/text/de/convoiinfo.txt000066400000000000000000000123651520675460500221060ustar00rootroot00000000000000Fahrzeugfenster

Fahrzeugfenster

Wählst Du mit dem Lupenwerkzeug einen Fahrzeugverband an, wird dessen Infofenster geöffnet. Du kannst das Fenster auch aus der Linienverwaltung oder Fahrzeugliste heraus aufrufen. Wähle dazu einen Eintrag in der Liste an. Das Fenster zeigt Dir einige technische Daten, eine Statistik und die Beladeliste.

Namensfeld: Der Name des Fahrzeugverbandes entspricht per Vorgabe der Bezeichnung des Fahrzeugs, das als erstes in den Konvoi aufgenommen wurde.

Aktuelle Daten:
· Geschwindigkeit, Wartungskosten: Die erste Geschwindigkeitsangabe ist die Höchstgeschwindigheit des Fahrzeugverbandes, die zweite die des Zugfahrzeugs. Der Wert in Klammern stellt die Laufkosten pro zurückgelegtem Feld des gesamten Fahrzeugverbandes dar. Die grüne Säule rechts daneben gibt die aktuelle Geschwindigkeit in relativ zur Höchstgeschwindigkeit an. Wird die aktuelle Geschwindigkeit nie erreicht, ist der Fahrzeugverband eventuell überladen oder der Weg lässt gerade keine höhere Geschwindigkeit zu.
· Leistung: Mit der Leistung wird die gesamte Antriebsleistung der aller Zugfahrzeuge in kW angegeben.
· Gewicht: Die erste Angabe stellt das Leergewicht des Fahrzeugverbandes dar, der Wert in Klammern ist das Gewicht der Ladung. Für das Gesamtgewicht müssen beide Werte zusammengezählt werden.
· Gewinn: Der Gewinn seit dem 1. Januar des Jahres wird angegeben. Ist der Wert negativ, fährt der Fahrzeugverband mit Verlust.
· Ladegrad: Der grüne Farbbalken gibt Dir Aufschluss über die aktuelle prozentuale Beladung des Fahrzeugverbandes. Ein (teil)gelber Balken bedeutet, dass der Fahrzeugverband auf die im Fahrplan eingestellte Mindestbeladung wartet.
· Fahrtziel: Das aktuelle Fahrplanziel wird angezeigt. Ist das Ziel eine Haltestelle, gibt Dir der erste Wert in Klammer darüber Auskunft, auf welchen minimalen Beladegrad der Fahrzeugverband warten wird. Du kannst den Wert für den Fahrzeugverband im Fahrplan ändern. Sollen alle Fahrzeuge der Linie von der Änderung betroffen sein, suche die Linie in derLinienverwaltung auf. Der farbige Balken dahinter gibt die Entfernung bis zum nächsten Halt an.
· Bedient Linie: Der Fahrzeugverband fährt nach dem Plan der angegebenen Linie. Weicht der Fahrplan nach einer Änderung vom Linienplan ab, oder fährt der Fahrzeugverband in keiner Linie, wird die Zeile nicht angezeigt. Mit dem Dreieck kann man direkt die Linie in der Linienverwaltung öffnen.

Fahrplan: Über die Schaltfläche kannst Du den Fahrplan des Fahrzeugverbandes aufrufen.

Sichtfenster: Den Fahrzeugverbandes siehts Du im Kartenausschnitt rechts daneben. Klickst Du ins Sichtfenster, springt die Kartenansicht und zentriert die aktuelle Position. Soll der Fahrzeugverband dauerhaft in Bildmitte zu sehen sein, benutze den Knopf verfolgen unter dem Kartenausschnitt.

nach Hause: Bedienst Du den Schalter, bricht der Fahrzeugverband seinen Weg im Fahrplan ab und steuert auf dem kürzesten Weg das nächstgelegene geeignete Depot an. Beim Erreichen des Depots geht sämtliche Fracht verloren; immerhin wird Dir der Ertrag für die geleistete Strecke ausbezahlt. Die Linie bzw. der Fahrplan sowie der letzte angesteuerte Fahrplanpunkt bleiben weiterhin gültig.

nicht laden: Der Fahrzeugverband wird ab sofort alle Mindestladung ignorieren und nur noch Fracht abladen.

Chart: Nach der Bedienung des Schalters öffnet bzw. schließt sich ein Statistikbereich mit grafischer Anzeige. Die Kurven entsprechen in den Farben jenen der selektierten Schalter und gewähren eine statistische Übersicht (monatsweise) über die letzten 12 Monate. Der aktuelle Monat steht links.
· Freie Kapazität: Der nicht genutzte Laderaum. Addiert für jeden Fahrplanabschnitt.
· Beförderungen: Der genutzte Laderaum. Addiert für jeden Fahrplanabschnitt.
· Einnahmen: Die Transporterlöse des Fahrzeugverbandes.
· Betriebskosten: Die anfallenden Kosten für den Betrieb des Fahrzeugverbandes. Die Schwankungen entstehen durch die unterschiedliche Laufleistung des Fahrzeugverbandes im Monat. Es entstehen nur Kosten, wenn der Fahrzeugverband sich bewegt.
· Profit: Der Profit ergibt sich aus Einnahmen minus Betriebskosten.
· Strecke/Monat: Die zurückgelegt Strecke aller Fahrzeuge der Linie.
· Geschwindigkeit:
· Maut: Kosten die durch Nutzung von Verkehrswegen anderer Spieler angefallen sind.

Beladeliste: Zeigt die Anzahl aller geladen Güter/Passagiere auf, geordnet nach Art. Diese Liste kann sortiert werden, nach Zielort, via (nächstes Ziel), via Menge (wieviel hat insgesamt den nächsten Halt als Ziel) oder Menge (am meisten zuerst). Diese Einstellung wird dann beim nächsten Öffnen für alle Fahrzeugverbände gemerkt.

simutrans-124.5/simutrans/text/de/curiosity_build.txt000066400000000000000000000050331520675460500231400ustar00rootroot00000000000000Sehenswürdigkeiten bauen

Sehenswürdigkeiten bauen

Der Dialog ist in vier Bereiche aufgeteilt.
· oben links befindet sich die Auswahlliste mit den Sehenswürdigkeiten
· unten links wird das Bild der ausgewählten Sehenswürdigkeit angezeigt
· oben rechts befinden sich einige Optionen
· unten rechts werden die Informationen der ausgewählten Sehenswürdigkeit angezeigt

Auswahlliste

In der Auswahlliste werden alle zur Verfügung stehenden Sehenswürdigkeit angezeigt, wenn die entsprechenden Optionen aktiviert (Stadtattraktionen, Ausflugsziel, Denkmal) sind. Oben befinden sich zwei Reiter, die die Anzeige umschalten.
· Übersetzung: Zeigt die Namen so an, wie diese im Spiel verwendet werden. Dies ist abhängig von der eingestellten Sprache. Wenn keine Übersetzung für die eingestellte Sprache vorhanden ist, wird der Objektname angezeigt.
· Objekt: Zeigt die internen Objektnamen an.

Die Schriftfarben haben folgende Bedeutung:
· grüne Schrift kennzeichnet Sehenswürdigkeiten, deren Standort sich außerhalb von Städten befindet.
· blaue Schrift kennzeichnet Sehenswürdigkeiten, deren Standort sich innerhalb von Städten befindet.
· schwarze Schrift kennzeichnet Denkmäler. Denkmäler können nur einmal pro Karte gebaut werden.

Optionen

· alle Klimazonen: Diese Option deaktiviert die Klimazonenzuordnung beim Bau der Sehenswürdigkeit.
· Mit Epoche ab Jahr: Es werden nur die Sehenswürdigkeiten in der Auswahlliste angezeigt, die im aktuellem Spieljahr zur Verfügung stehen.
· Auch veraltete zeigen: Es werden auch die Sehenswürdigkeiten in der Auswahlliste angezeigt, die in früheren Spieljahren mal zur Verfügung standen.
· Stadtattraktionen: Es werden die Sehenswürdigkeiten in der Auswahlliste angezeigt, deren Standort sich in Städten befindet.
· Ausflugsziel: Es werden die Sehenswürdigkeiten in der Auswahlliste angezeigt, deren Standort sich außerhalb von Städten befindet.
· Denkmal: Es werden die Denkmäler in der Auswahlliste angezeigt.
· Rotation: Sofern die ausgewählte Sehenswürdigkeit mehrere Ansichten hat, kann hier eine bestimmte Ansicht ausgewählt werden. Die Grafik unten links wird entsprechen gewechselt. Bei der Auswahl von Zufällig, wird beim Bau der Sehenswürdigkeit irgendeine Ansicht gewählt.

simutrans-124.5/simutrans/text/de/curiositylist_filter.txt000066400000000000000000000041001520675460500242140ustar00rootroot00000000000000Touristenzielliste

Touristenzielliste

Die Touristenzielliste wird über die Listenverwaltung geöffnet.
In der Touristenzielliste sind alle Sehenswürdigkeiten aufgeführt. Geplant ist die Erweiterung um Denkmäler. Ein Klick auf einen Eintrag öffnet den Gebäudetext (wir hoffen, sie haben Spaß daran) und stellt die Karte dar. Es gibt keine Sortierfunktionen.

· Status: Die Statusfarben sind
- Grün: Die Attraktion ist für den Passagierverkehr erschlossen.
- Blau: Die Attraktion ist für den Postverkehr erschlossen.
- Türkis: Die Attraktion ist für den Passagier- und Postverkehr erschlossen.
- Orange: Eine Haltestelle, die die Attraktion erschließt, ist überfüllt.
- Rot: Alle Haltestellen, die die Attraktion erschließen, sind überfüllt.
· Ein Gebäude neben dem Statusquadrat bedeutet, dass es sich um eine Stadtattraktion handelt.

· Name: Die Bezeichnung der Attraktion, gefolgt von den Kartenkoordinaten

· Passagierlevel: Nach dem Bindestrich folgen noch Passagier- und Postlevel. Attraktionen erzeugen von sich aus keine Passagiere, doch jeder Tourist, der dorthin will, erzeugt gleichzeitig seine Rückreise. Post wird ebenfalls nicht von sich aus erzeugt, doch eine geringe Anregung genügt, und die Touristen senden massenweise Ansichtskarten in die Welt hinaus.

· Erschliessung der Touristenziele: Einige Touristenziele bedecken eine grosse Landfläche. Um eine Fabrik anzuschließen reicht es aus, dass ein einziges Fabrikfeld im Bereich einer Haltestelle liegt. Im Gegensatz dazu werden von Attraktionen nur die Felder erschlossenen, die tatsächlich im Einzugsbereich von Haltestellen liegen. Um das Passagieraufkommen einer Attraktion vollständig zu nutzen, müssen also alle zugehörigen Felder durch den Einzugsbereich von Haltestellen abgedeckt werden. Touristen, deren Ziel auf einem nicht erschlossenen Feld liegt, finden sonst keine Route.

simutrans-124.5/simutrans/text/de/depot.txt000066400000000000000000000226131520675460500210450ustar00rootroot00000000000000Depots, Werften und Hangar

Depotfenster

Depotarten: Die Depots für Züge, Straßenfahrzeuge, Schiffe, Trams, Flugzeuge und Magnetschwebebahnen halten die jeweiligen Fahrzeugarten bereit. Im Depotfenster kannst Du Fahrzeuge kaufen und verkaufen sowie Fahrzeugverbände zusammenstellen, Zusammenstellungen ändern und auflösen. Im Aufbau unterscheiden sich die Depotfenster nicht.

Fenstertitelleiste: Rechts neben dem Fragezeichen befinden sich 2 Pfeilschaltflächen. Mit den Pfeilen kannst Du nacheinander zu den anderen Depots des gleichen Typs wechseln.

Fahrzeuglisten: Fahrzeugverbände, die sich im Depot befinden, werden in Fahrzeuglisten erfasst. Die Zeile gibt Aufschluss darüber, wieviele Fahrzeugverbände sich im Depot befinden. Sie zeigt auch an, welche Liste gerade ausgewählt ist.

Auswahl eines Fahrzeugverbandes: Mit den beiden Pfeilschaltflächen links und rechts des Edit-Feldes kannst Du allen Fahrzeugverbände durchschalten, die sich derzeit im Depot befinden. Möchtest Du einen neuen zusammenstellen, schalte bis zum Eintrag "Neuer Fahrzeugverband".
Im editierbaren Feld kannst Du den Namen des Fahrzeugverbandes nach Belieben ändern. Klicke mit der Maus in das Feld. Bestätige den Abschluss der Arbeit mit RETURN.

Linienauswahl: Du kannst eine bestehende Linie auswählen. Für die Durchschaltung der Linien stehen Dir die Pfeilschalter links und rechts der Linienauswahl zur Verfügung. Ein Klick in das Anzeigefeld öffnet ein Auswahlliste. In ihr kannst Du mit der Maus den gewünschten Eintrag auswählen. Es ist auch möglich, dort den Liniennamen zu editieren; aus eigener Erfahrung des Autors wird empfohlen, dies in der Linienverwaltung zu machen.

Fahrzeugansicht: Ein Fahrzeugverband besteht aus mindestens einem Einzelfahrzeug. Flugzeuge erlauben sind immer solo, Straßen- und Schiffsfahrzeugverbände können bis zu 4 Fahrzeuge, Schienenfahrzeugverbände bis zu 24 Einzelfahrzeuge enthalten. Der ausgewählte Fahrzeugverband wird in dem Feld darunter grafisch dargestellt.
· Wenn Du die Maus über ein Einzelfahrzeug führst, werden im unteren Bereich des Fensters dessen Fahrzeugdaten angezeigt.
· Klickst Du ein Einzelfahrzeug an, wird es aus dem Fahrzeugverband entfernt und im Depot eingelagert. Eine Ordnungszahl im Einzelfahrzeugdepot zeigt an, wie viele Fahrzeuge des Typs Du eingelagert hast.
· Unter der grafischen Ansicht des Fahrzeugverbandes werden die Anzahl der Fahrzeuge, die benötigte Haltenstellenlänge, der Verkaufswert und die bediente Linie angezeigt.

Die Funktionen der gruppierten Schaltflächen:
Start: Schicke einen Fahrzeugverband mit Fahrplan auf die Reise, es wird der im Fahrplan ausgewählte Halt angefahren.
Fahrplan: Das Fahrplan-Fenster wird geöffnet, in welchem Du einen individuellen Fahrplan erstellen kannst.
Auflösen: Der aktuelle Fahrzeugverbandes wird aufgelöst, alle Fahrzeuge werden ins Depot eingelagert.
Verkauf: Der aktuelle Fahrzeugverbandes wird zum aktuellen Verkaufswert verkauft.

Neue Linie:Das Linienplan-Fenster wird geöffnet, in welchem Du die Haltepunkte einer neuen Linie eintragen kannst.
Linie zuweisen: Weise dem Fahrzeugverbandes die aktuelle Linie der Linienauswahl zu.
Linie ändern: Im Linienplan-Fenster kannst Du die oben angezeigte Linie bearbeiten.
Kopieren: Kaufe einen Fahrzeugverbandes, der in seiner Zusammenstellung dem aktuellen Fahrzeugverbandes entspricht. Der Name und ein zugewiesener Fahrplan/Linie werden übernommen.

Einzelfahrzeugdepot: Im Depot sind alle derzeit verfügbaren Fahrzeuge des Depottyps nach ihrem Ladevermögen sortiert abgebildet. Mittels der Tab-Reiter sind sie nach Personen- und Postbeförderung, angetriebene und nicht angetriebene Fahrzeuge eingeteilt.
· Elektrisch betriebene Fahrzeuge werden nur angezeigt, wenn das Feld, auf welchem das Depot steht, elektrifiziert ist.
· Wenn Du die Maus über ein Fahrzeug führst, werden im unteren Bereich des Fensters dessen Fahrzeugdaten angezeigt.
· Fahrzeuge werden erst ab einem bestimmten Datum verfügbar. Einige Fahrzeuge veralten und werden außer Dienst gestellt. Du kannst sie, über den Knopf Auch veraltete Fahrzeuge zeigen, rechts unten, anzeigen lassen und sie kaufen.
· Möchtest Du, dass alle Fahrzeuge unabhängig von deren Einführungs-Datum verfügbar sind, dann wähle die Einführungsdaten bei der Welterstellung ab.

Depotaktionen

Aktionsschalter: Der Schalter rechts unten wechselt bei der Betätigung zwischen 3 Funktionseinstellungen.
· anhängen: Das angeklickte Einzelfahrzeug wird am Schluss des Fahrzeugverbandes angehängt.
· voranstellen: Das angewählte Einzelfahrzeug wird an der Spitze des Fahrzeugverbandes vorangestellt.
· verkaufen: Verkaufe ein eingelagertes Einzelfahrzeug. Der Verkaufswert aller eingelagerter Einzelfahrzeug des Typs ist bei den Fahrzeugdaten ersichtlich.

Die Farbbalken unter den Einzelfahrzeug im Depot ändern sich je nach Schalterstellung. Wenn der Farbbalken auf einer Seite gelb ist, gehört ein weiteres Einzelfahrzeug zur betriebsfähigen Zusammenstellung. Wird dieses Einzelfahrzeug gekauft, wird auch das zugehörige Einzelfahrzeug dazugekauft.

Die Farben bedeuten:
· grün: Das Einzelfahrzeug kann verwendet werden.
· rot: Das Einzelfahrzeug ist für die Aktion nicht verwendbar.
· gelb links: Ein anderes Einzelfahrzeug muss vorangestellt werden.
· gelb rechts: Ein anderes Einzelfahrzeug muss folgen.
· blau: Das aktuelle Jahr liegt außerhalb der Einsatzzeit des Einzelfahrzeug. Das Einzelfahrzeug kann verwendet werden.

Ein Fahrzeugverband ist einsatzbereit, wenn er für alle Einzelfahrzeug grüne Balken zeigt. Verbleiben teilgelbe Balken, sind noch Einzelfahrzeug beizustellen, etwa Zugmaschine, Tender oder Schlussfahrzeug.

Achtung Beachte bei Schienenfahrzeugverbänden, dass alle Fahrzeuge des Fahrzeugverbandes in die angefahrenen Haltestellen passen, sonst werden nur die Fahrzeuge beladen, die in der Haltestelle stehen. Das bedeutet, wenn eine Mindestbeladung eingestellt ist, die größer ist als die Ladekapazität der in der Haltestelle stehenden Einzelfahrzeuge ist, fährt der Fahrzeugverband niemals ab. An jedes Haltestellensegment passen in der Regel 2 Fahrzeuge.

Die Anzahl von eingelagerten Einzelfahrzeugen eines Typs ist an einer weißen Zahl links über dem Einzelfahrzeuge ersichtlich. Du kannst die Einzelfahrzeuge verkaufen oder in diesem Depot zu neuen Fahrzeugverbänden wiederverwenden.

Unter dem Einzelfahrzeugdepot wird die Anzahl aller hier eingelagerter Einzelfahrzeuge angezeigt.

Einzelfahrzeugdaten: Führst Du die Maus über ein Einzelfahrzeugen im Fahrzeugverband (oben) oder im Einzelfahrzeugdepot (mittig), werden dessen Daten im unteren Teil des Fensters eingeblendet:

Linke Spalte angetriebene Fahrzeuge:
· Typbezeichnung und in Klammern die Betriebsart (Dampf, Diesel, Elektrisch).
· Neupreis und in Klammern die Betriebskosten je Streckenkilometer.
· Leistung in KW und die Höchstgeschwindigkeit.
· Fahrzeuggewicht.
· Zuladung (wenn möglich) mit Transporteinheiten und Art des Gutes.
Linke Spalte antriebsloser Fahrzeuge:
· Typbezeichnung .
· Neupreis und in Klammern die Betriebskosten je Streckenkilometer.
· Zuladung mit Transporteinheiten und Art des Gutes.
· Fahrzeuggewicht.
· Höchstgeschwindigkeit.

Rechte Spalte:
· Einführungsdatum.
· Außerdienststellungsdatum (nicht bei allen Fahrzeugen).
· Bei angetriebenen Fahrzeugen das Übersetzungsverhältnis.
· Bei gekauften Fahrzeugen der Verkaufspreis.

Frachteinheiten:
Den Warenarten sind 5 Beladeeinheiten zugeordnet:
· Flüssigkeiten: m³
· Lose Güter: Tonnen (t)
· Größere Objekte: Stück
· Palettierte Güter: Paletten
· Lose Gebindeeinheiten: Sack
Bei Passagieren wird die Anzahl angegeben.

Güterarten:
Güter sind bei Simutrans nach 6 Kategorien klassifiziert:
· Gefriergut, Öl/Benzin, Schüttgut, Sonderfracht, Langholz und Stückgut.

· Einigen Güterarten können in den verschiedenen Paks unterschiedliche Frachteinheiten und Güterarten zugewiesen sein.
· Sonderfrachten benötigen zum Transport immer spezielle Fahrzeuge.
· Andere Fahrzeuge können jedes Gut aus ihrer Kategorie laden.
· Aus historischen Gründen haben einige Fahrzeuge immer noch irreführende Bezeichnungen. Das Kohleschiff kann heute nicht nur für Kohle, sondern für jegliches Schüttgut verwendet werden.
· Die Fabriken- und Lagerinfos zeigen an, welche Güter zu welcher Kategorie zählen und welche Fahrzeuge dafür geeignet sind. Ist keine Kategorie angegeben sondern ein Spezialfahrzeugtyp, wird ein Spezialfahrzeug benötigt.

simutrans-124.5/simutrans/text/de/depotlist.txt000066400000000000000000000007601520675460500217400ustar00rootroot00000000000000Depotliste

Depotliste

Die Depotliste zeight alle Garagen, Hangar, Werften, Bahnbetriebwerke und natürlich auch Depots, also jene Orte wo man Fahrzeuge kaufen oder neu zusammenstellen kann. Mit dem Tab kann man bestimmte Transportmittel auswählen. Wie bei allen Listen, springt ein Rechtsklick, Klick mit Control, oder ein Linksklick auf das Dreieck an die Stell der Karte mit dem Depot. Ein Linksklick auf eine Zeile öffnet dirkt das Depot.

simutrans-124.5/simutrans/text/de/display.txt000066400000000000000000000142671520675460500214050ustar00rootroot00000000000000Anzeigeeinstellungen

Anzeigeeinstellungen

Der Dialog Anzeige ist in die Reiter Oberfläche, Kartendarstellung, Bäume & Gebäude, Stationen und Verkehr unterteilt.

Oberfläche

· Auswahl Oberfläche öffnet ein Fenster wo Darstellung der Oberfläche geändert werden kann.
· Zeichensatzauswahl öffnet ein Fenster wo die Größe und die Art der Schrift geändert werden kann.
· Position Hauptmenü legt fest, wo sich das Hauptmenü (oben, links, rechts, unten) befindet.
· Vollbildmodus legt fest, ob Simutrans im Vollbild ausgeführt wird.
· rahmenloser Fenstermodus legt fest, das Simutrans als Fenster im Vollbild ohne Fensterelementen ausgeführt wird.
· Tool deaktivieren mit erneuter Auswahl legt fest, das ein erneuter Klick auf den Button das Tool wieder deaktiviert.

Auslastungsanzeigen
Im unteren Teil des Dialogs werden einige Information zur Auslastung des Computers angezeigt:
· Bildfrequenz: zeigt die tatsächliche Zeit zwischen zwei Frames.
· Leerlauf: Zeigt an, wie lange der Computer zwischen zwei Bildschirmupdates pausiert.
· Bildrate: Je höher der Wert, desto gleichmäßiger (ruckelfrei) bewegen sich die Fahrzeuge. Der Wert stellt die Bildfolgen pro Spielsekunde dar. Im Schnellen Vorlauf und im Netzwerkmodus ist die Bildrate fixiert, ansonsten wird sie nach Rechenlast angepasst. Den Wert kann man mit dem Kommandzeilenparameter -fps oder in der simuconf.tab einstellen.
· Simloops: Anzahl der Simulationsschleifen pro Spielsekunde. Wenn der Wert dauerhaft unter 5 fällt, ist der Computer voll ausgelastet. Einige Funktionen in Simutrans können aussetzen, zum Beispiel fahren Züge nicht mehr ab. In dem Fall sollte eine kleinere Karte mit weniger Städten gespielt werden.

Kartendarstellung

· Gitternetz einblenden: Blendet in der Welt ein Gitternetz ein und aus.
· Untergrundansicht: In diesem Modus kann man Tunnelstrecken bauen, sofern schon eine Strecke unter der Erde besteht. Die Tunnelstrecken aller Ebenen werden zusammen angezeigt.
· Ebene durch Karte: Auch in diesem Modus kann man Tunnelstrecken bauen. Die Karte wird auf der eingestellten Ebene aufgeschnitten und nur Tunnelstrecken auf der jeweiligen Ebene werden angezeigt. Mit den Pfeilen kann die Ebene eingestellt werden. Es kann aber auch direkt die Nummer der Ebene eingegeben werden.
· Tag und Nacht wechseln: Soll es nachts dunkel werden in Simutrans? Gebäudefenster, Fahrzeuge und Signalmittel zeigen sich in Nachtbeleuchtung.
· Helligkeit: Lege mit den Pfeilen die Helligkeit des Spiels fest. Nutze auch die Tasten ) (heller) und ( (dunkler). Werte über 0 lassen sich nur über die Tastatur einstellen. ACHTUNG: Die Einstellbarkeit ist nach oben und unten nicht begrenzt, es kommt aber sehr bald zu einer Falschfarbenanzeige.
· Inverses Kartenscrolling: Kehre die Scrollrichtung bei Bedarf um, die Karte bewegt sich dann in Mausrichtung.
· Zifferntasten bewegen immer die Karte
· Scrollgeschwindigkeit: Diese Einstellung erhöht die Kartenscrollgeschwindigkeit.
· Datums- und Uhrzeitformat ändert die Darstellung der Spielzeit unten links in der Statuszeile.

Bäume & Gebäude

· Transparent statt minimieren: Wählt den Modus aus, der benutzt werden soll, um Häuser oder Bäume zur besseren Übersicht zu verstecken. Diese Einstellung hat Auswirkungen auf die beiden nächsten Einstellungen.
· Bäume minimieren: Zeigt nur ganz kleine bzw. transparent Bäume an.
· Gebäudeanzeigemodus: Es kann mit Hilfe der Pfeile ausgewählt werden, ob keine Gebäude, nur Stadtgebäude oder alle Gebäude minimiert werden.
· Mauszeiger freihalten: Je nach Einstellung werden eine bestimmte Anzahl von Feldern um den Mauszeiger minimiert bzw. tranparent. Die Anzahl der Felder kann mit den Pfeilen oder direkt durch Eingabe eingestellt werden.
· Industriestatusanzeige: Blendet den Industrienamen sowie Wareneingang (blau) und Warenausgang (orange) auf der Hauptkarte ein.

Stationen

· transparente Stationsabdeckung: Wählt den Modus aus, der benutzt werden soll, um die Reichweite von Halten anzuzeigen. Ist transparent gewählt färbt sich der Boden entsprechend der ersten Spielerfarbe des Besitzers, während ausgeschaltet je Fliese nur ein Kästchen in Spielerfarbe zu sehen ist.
· Stationsabdeckung anzeigen: Zeigt die Stationsabdeckung an oder blendet sie aus.
· Markierungen auf der Hauptkarte: Legt den Modus fest, wie Markierungen bzw. Schilder im Spiel angezeigt werden. Diese können mit der ersten Spielerfarbe im Hintergrund, mit transparentem Grund und als Schriftfarbe die Spielerfarbe, in Standardschriftfarbe mit einem spielerfarbenen Feld davor oder überhaupt nicht angezeigt werden.
· Güterwarenbalken anzeigen: Blendet die Anzeige für wartende Waren an den Halten ein oder aus.

Verkehr

· Fußgänger (Stadt): Wenn aktiviert werden sich Fußgänger durch die Städte bewegen.
· Fußgänger (an Halten): Wenn aktiviert werden Fußgänger an Haltestellen aus Fahrzeugen aussteigen.
· Verkehrsdichte: Dieser Wert beeinflusst die Anzahl der privaten PKWs in den Städten.
· Fahrzeuge: Tooltipps: Je nach Modus werden Tooltipps verschieden angezeigt. Wenn Maustooltipps gewählt ist werden Tooltipps nur auf dem Feld angezeigt, wo sich die Maus befindet. Sind alle Tooltipps ausgewählt werden immer alle Tipps angezeigt. Fehlertooltipps werden nur bei Zwischenfällen angezeigt.
· Fahrzeugverfolgung im Tunnel
· Fahrplanhalte hervorheben: Ist diese Funktion aktiviert werden bei geöffneten Fahrplan alle Halte- und Wegpunkte angezeigt.
· Finanzbewegungen auf der Hauptkarte

simutrans-124.5/simutrans/text/de/edittools.txt000066400000000000000000000070321520675460500217360ustar00rootroot00000000000000Karteneditor

Karteneditor

Mit den Werkzeugen des Karteneditors kannst Du in den natürlichen Wachstumsprozess von Städten und Industrien eingegreifen. Das Menü lässt sich über einen Button im Menü Spezialwerkzeuge öffnen.

Eine zu leicht spielbar organisierte Welt wird Dir bereits mit wenig Spielerfahrung jeden Anreiz und jeden Spielspaß rauben. Setze die Werkzeuge gar nicht oder überlegt ein. Realisiere bauliche Situationen, wie sie bei der Autogenerierung praktisch nicht erreicht werden können. Ein dichtes und schwer handhabbares Industrierevier auf einer Insel, der Fremdenverkehrsort mit seiner Attraktion in der schwer zugänglichen Gebirgslage, der Erlebnispark in der Nähe einer kleinen Stadt, die Erdölförderung nur an ausgewählten Küstenstreifen oder die Landwirtschaft als Schwerpunkt in einer ländlichen Umgebung stellen nur einige Beispiele aus der reichen Möglichkeitenpalette dar.

· Stadtwachstum: Setze eines der beiden Werkzeuge in dem Gebiet der Stadt ein, deren Einwohnerzahl Du ändern möchtest. Nutzt Du das Werkzeug im Überschneidungsgebiet von Städten, wirkt sich die Aktion nur auf eine Stadt aus.
- Einwohnerzahl erhöhen: Das Wachstumswerkzeug stellt einen nach oben weisen Pfeil dar. Bei jeder Werkzeugnutzung wird die Stadt um 100 Einwohner wachsen. Es werden sofort Gebäude erneuert oder frisch gebaut, da mit der Aktion die Arbeits- und Obdachlosenzahlen steigen.
- Einwohnerzahl verringern: Das Wachstumswerkzeug stellt einen nach unten weisen Pfeil dar. Die Einwohnerzahl der Stadt wird bei jeder Werkzeugnutzung um 100 schrumpfen. Alle Gebäude bleiben erhalten. Die Werte für Arbeitslose und Obdachlose geraten in negative Bereiche. Die Stadt wird erst wieder bauliche Entwicklung zeigen, wenn durch Wachstum Bedarf an Baumaßnahmen entstanden ist.

· Gründe neue Stadt: Hast Du zu wenige Städte auf Deiner Karte, kannst Du mit diesem Werkzeug neue Städte gründen. Die Stadt entsteht dort, wo Dumit dem Werkzeug hinklickst. Aber Vorsicht, jede Stadtgründung kostet Dich eine Menge Geld.

· Stadtstraße bauen: Das Bauwerkzeug stellt eine Straße dar. Straßen, die Du mit dem Werkzeug baust, können von allen Spielern kostenlos genutzt werden.

· Industriedichte erhöhen: Es wird eine zufällige Industriekette gebaut.

· Industrieanlagen bauen: Öffnet den Dialog Industrieanlagen bauen.

· Industrieanlagen verbinden: Mit diesem Werkzeug können Industrien verbunden werden. Dies funktioniert nur dann, wenn eine Warenverbindung möglich ist.

· Sehenswürdigkeiten bauen: Öffnet den Dialog Sehenswürdigkeiten bauen.

· Stadtgebäude bauen: Öffnet den Dialog Stadtgebäude bauen.

· Pflanze Baum: Öffnet den Dialog Pflanze Baum.

· Wald aufforsten: Markiere eine Fläche, auf die kreisförmig ein Wald aufgeforstet wird.

· Jahr überspringen:Die Simutrans-Zeit wird um ein Jahr nach vorne gestellt. Neue Fahrzeuge werden verfügbar, wenn ihr Einführungsjahr erreicht wird.

· Spielerwechsel sperren: Klicke mit dem Werkzeug auf eine beliebigen Kartenposition. Dadurch ist der Wechsel zur öffentlichen Hand oder zu den Computerspielern nicht mehr möglich.

simutrans-124.5/simutrans/text/de/enlarge_map.txt000066400000000000000000000006771520675460500222120ustar00rootroot00000000000000Kartenfläche vergrößern

Kartenfläche vergrößern

In diesem Dialog kann die bestehende Spielkarte nach Rechts und Unten vergrößert werden. Möchte man nach links / oben erweitern, muss erst die Karte gedreht und dann mit diesem Dialog vergrößert werden. Nach dem Vergrößern kann man die Karte wieder zurück drehen.
In der rechten oberen Ecke wird eine Vorschau der vergrößerten Karte angezeigt.

simutrans-124.5/simutrans/text/de/factory_build.txt000066400000000000000000000047571520675460500225710ustar00rootroot00000000000000Industrieanlagen bauen

Industrieanlagen bauen

Der Dialog ist in vier Bereiche aufgeteilt.
· oben links befindet sich die Auswahlliste mit den Industrien
· unten links wird das Bild der ausgewählten Industrie angezeigt
· oben rechts befinden sich einige Optionen
· unten rechts werden die Informationen der ausgewählten Industrie angezeigt

Auswahlliste

In der Auswahlliste werden alle zur Verfügung stehenden Industrien angezeigt. Oben befinden sich zwei Reiter, die die Anzeige umschalten.
· Übersetzung: Zeigt die Namen so an, wie diese im Spiel verwendet werden. Dies ist abhängig von der eingestellten Sprache. Wenn keine Übersetzung für die eingestellte Sprache vorhanden ist, wird der Objektname angezeigt.
· Objekt: Zeigt die internen Objektnamen an.

Die Schriftfarben haben folgende Bedeutung:
· grüne Schrift kennzeichnet Industrien, die Waren nur erzeugen
· schwarze Schrift kennzeichnet Industrien, die Waren benötigen und daraus neue Waren produzieren
· blaue Schrift kennzeichnet Industrien, die Waren nur benötigen

Optionen

· alle Klimazonen: Diese Option deaktiviert die Klimazonenzuordnung beim Bau der Industrie.
· Mit Epoche ab Jahr: Es werden nur die Industrien in der Auswahlliste angezeigt, die im aktuellem Spieljahr zur Verfügung stehen.
· Auch veraltete zeigen: Es werden auch die Industrien in der Auswahlliste angezeigt, die in früheren Spieljahren mal zur Verfügung standen.
· Stadtgewerbekette bauen: Es werden nur die Industrien in der Auswahlliste angezeigt, deren Standort sich in Städten befindet. Außerdem wird, so möglich, eine ganze Kette, d.h. alle davon abhängigen Farbiken erstellt.
· Fabrikkette auf dem Land: Es werden nur die Industrien in der Auswahlliste angezeigt, deren Standort sich außerhalb von Städten befindet. Außerdem wird, so möglich, eine ganze Kette, d.h. alle davon abhängigen Farbiken erstellt.

· Rotation: Sofern die ausgewählte Industrie mehrere Ansichten hat, kann hier eine bestimmte Ansicht ausgewählt werden. Die Grafik unten links wird entsprechen gewechselt. Bei der Auswahl von Zufällig, wird beim Bau der Industrie irgendeine Ansicht gewählt.
· Produktion: Hier kann die vordefinierte Produktionsrate der ausgewählten Industrie geändert werden.

simutrans-124.5/simutrans/text/de/factorylist_filter.txt000066400000000000000000000023301520675460500236340ustar00rootroot00000000000000Fabrikliste

Fabrikliste

Die Fabrikliste wird über die Listenverwaltung geöffnet.
In der Fabrikliste sind alle Fabriken der Welt gelistet. Die Sortierung ist nach allen aufgeführten Kriterien möglich; klicke zum Wechseln auf den Button. Ein Klick auf einen Eintrag öffnet das Infofenster zur Fabrik und zentriert die Karte auf diese.

· Status: Zeigt den Status der Fabrik an.
Die Farben bedeuten:
- Weiss: Industrie produziert nicht
- Gelb: Industrie ist nicht angeschlossen
- Grün: Industrie hat Optimum
- Orange: Industrie funktioniert, aber Verbesserungen sind möglich
- Rot: Industrie muß überprüft werden

· Strom: Zeigt an, ob die Fabrik ans Stromnetz angeschlossen ist. Dies wird durch einen roten Blitz dargestellt.

· Fabrikname: Die Bezeichnung für die Fabrik.

· Eingang: Die Summe aller im Rohstofflager der Fabrik wartenden Güter.

· Ausgang: Die Summe aller im Fertigwarenlager transportbereiter Güter.

· Produktion: Die Produktionsrate der Fabrik.

simutrans-124.5/simutrans/text/de/finances.txt000066400000000000000000000100001520675460500215030ustar00rootroot00000000000000Finanzen

Finanzen

Die Finanzen können im Finanzfenster betrachtet werden. Dieses kann durch Drücken von f oder über die Hauptmenüleiste geöffnet werden.
Dort sind die Informationen über die Finanzen zusammengefasst.

Im unteren Teil des Fensters ist eine Grafik, die den Verlauf eines oder mehrer Werte anzeigt. Welche diese anzeigen soll, kannst Du mit den farbigen Schaltflächen darüber auswählen. Du kannst die Ansicht zwischen Jahren und Monaten umschalten.

Mit der Schaltfläche Firmensitzbau kann ein Firmensitz gebaut werden. Gibt es bereits einen Firmensitzes, heißt die Schaltfläche Firmensitzumbau. Unterhalb der Schaltfläche wird ein Kartenausschnitt mit dem Standort des Firmensitzes angezeigt.
Der Firmensitzbau/-umbau steht nur zur Verfügung, wenn das verwendete Pakset Firmensitzgebäude enthält.

Die Positionen und Schaltflächen der linken Hälfte sind wie folgt:

Einnahmen - Die gesamten Einnahmen aus dem Transport von Passagieren und Gütern.
Betriebskosten - Die Kosten für den Betrieb aller Fahrzeuge. Die Betriebskosten pro gefahrenem Kilometer je Fahrzeugtyp können im Depot eingesehen werden, für Fahrzeugverbände im Fahrzeugfenster.
Instandhaltung - Die angefallenen Kosten für die Instandhaltung der Infrastruktur. Die Kosten der Instandhaltung pro Monat werden rechts angezeigt und jeweils am letzten des Monats fällig, also dann immer soviel auf dem Konto vorrätig haben!
Maut - Wenn Fahrzeuge auf fremden Wegen (zum Beispiel Straßen anderer Spieler) unterwegs sind, kann ein Maut (Wegzoll) anfallen. Dies hängt vom Pakset ab. Je nachdem, ob andere Spieler deine Wege benutzen oder umgekehrt, kann man so Geld verdienen oder muss bezahlen.
Freileitungen - Der Gewinn durch den Verkauf von Strom.
Betriebsgewinn - Der operative Gewinn (exkl. Abschreibungen auf Fahrzeuge), also was mit der Transporttätigkeit, Maut, Freileitung an sich verdient wird minus der Betriebskosten und Instandhaltung. Summe aller obigen Positionen.

Neue Fahrzeuge - Alle Ausgaben (Einnahmen) für den Kauf (Verkauf) von Fahrzeugen.
Ausgaben Bau - Hier steht der Betrag, der für den Bau von Schienen, Strassen, Signalen usw. ausgegeben wurde. Auch Abrisskosten zählen zu den Baukosten.
Bruttogewinn - Die Summe aller Einnahmen (Geldzuflüsse) und Ausgaben (Geldabflüsse), der sogenannte Cash-Flow. Summe aller obigen Positionen.

Beförderungen - Die Summe aller Einheiten, die transportiert worden sind.

Die Positionen und Schaltflächen der rechten Hälfte sind wie folgt:

Kontostand - Der aktuelle Kontostand. Dieses Geld steht zur Verfügung, um zu bauen oder Fahrzeuge und Züge zu kaufen. Der Kontostand steht auch in der Statusleiste unten in der Mitte. Ist der Kontostand negativ erscheint monatlich eine Warnung.
Fahrzeugwert - Der Wert Deiner Fahrzeugflotte per Ende des letzten Monats.
Marge - Die erzielte Nettomarge (Betriebsgewinn/Kosten). Je höher diese ist, desto mehr wird aus den angefallenen Kosten herausgeholt. Die Werte werden laufend aktualisiert, sind aber unter dem Monat wegen fehlender Unterhaltskosten zu hoch. Aussagekräftiger sind Zahlen des Vormonats oder Vorjahres. Achtung: Es kann durchaus sinnvoll sein, Linien mit geringer Marge zu betreiben, z.B. um eine Industrie zu versorgen.
Gesamtvermögen - Die Summe von Kontostand und Fahrzeugwert, also der Firmenwert. Ist das Gesamtvermögen am Ende eines Monats negativ, ist die Firma pleite und das Spiel verloren. Sinkt das Gesamtvermögen unter 10% des Startkapitals, erscheint am Monatsersten eine Warnung.

Mission-Spiele

Bei Mission-Spielen wird zusätzlich der Mission-Auftrag und der erreichte Mission-Status (in %) angezeigt.

simutrans-124.5/simutrans/text/de/general.txt000066400000000000000000000057361520675460500213560ustar00rootroot00000000000000Simutrans

Simutrans Hilfe

Hilfe zum Spiel
·Simutrans
·Nutzung der Hilfe
·Spielsteuerung
··Untergrund
·Tastaturbelegung
·Hauptmenü
·Neue Welt

Hauptmenü (siehe auch Hauptmenü)
·Reliefkarte
·Optionen (s.u.)
· Abfragewerkzeug
·Geländewerkzeuge
·Eisenbahnbauwerkzeuge
·Monorailbauwerkzeuge
·Trambauwerkzeuge
·Straßenbauwerkzeug
·Häfen und Werft
·Flughafenbauwerkzeuge
·Spezialbauwerkzeuge
· Löschwerkzeug

·Linienverwaltung
·Listenverwaltung
· ·Haltestellenliste mit Filtereinstellungen
· · öffnet Haltestellenfenster und Haltestellen Detailinformation
· ·Fahrzeugliste mit Filtereinstellungen zur Fahrzeugliste
· · öffnet Fahrzeugfenster und Fahrzeugdetailfenster
· ·Stadtliste
· · öffnet Stadtinformation
· ·Warenliste
· · öffnet Fabrikfenster
· ·Fabrikliste
· ·Touristenzielliste
·Mailbox und Mailbox Optionen
·Finanzen

· Bildschirm speichern
· Pause
· Simulation maximal beschleunigen (kann zu ruckeln führen)!

Dialoge unter Optionen
·Einstellungen
·Sprachauswahl
·Farbauswahl
·Anzeigeeinstellungen
·Sound- & Musikeinstellungen
·Spieler Menü
·Lade Menü
·Speichern Menü
·Neue Welt und Lade Relief Menü

Weitere Dialoge
·Karteneditor
·Depotfenster
·Fahr- und Linienplanfenster

simutrans-124.5/simutrans/text/de/goods_filter.txt000066400000000000000000000035061520675460500224120ustar00rootroot00000000000000Warenliste

Warenliste

Die Warenliste wird mit Shift+G oder über die Listenverwaltung aufgerufen.
In der Warenliste sind Passagiere sowie alle Güter, die Dir im gespielten Grafikset ( pak ) zum Transport anvertraut werden können, gelistet. Dabei können momentan nicht vorhandene Güter ausgeblendet werden.

· Fracht: Passagiere und alle Güterarten in Simutrans werden nach einer internen Sortierung gelistet. Vor jeder Frachtart befindet sich ein Farbquadrat. Die gleichen Farben findest Du auch an den Säulen über den Haltestellennamen wieder. Sie zeigen dort an, wieviel Einheiten je Güterart an den Haltestellen gelagert sind. Die Anzeige läßt sich über die Taste ! ein- und ausblenden.

· Preis: Der Transporterlös pro Einheit eines Gutes für ein zurückgelegtes Feld.

· Bonus: Werden Güter, und vor allem Passagiere und Post auf einem Verkehrsträger schneller als oben angezeigt befördert, wird auf den Grundumsatz pro weitere 10 km/h der Bonus aufgerechnet; ist das Fahrzeug langsamer, kann der Umsatz auch sinken. Die Berechnungsformel ist komplex. Maßgebend für die Höhe des Bonus ist die zulässige Höchstgeschwindigkeit des Konvois. Die Höchstgeschwindigkeit der befahrenen Strecke und die tatsächliche Beförderungszeit finden keine Berücksichtigung.

· Kategorie: Jede Frachtart ist einer Güterkategorie zugeordnet. Für jede Güterkategorie ist ein bestimmter Fahrzeugtyp vorgesehen. Alle Sonderfrachten können nur mit spezifischen Fahrzeugen transportiert werden. Im Depotfenster kannst Du die Güterarten unter "Zuladung" einsehen.

· Gewicht: Das Gewicht einer Einheit eines Gutes in kg.

simutrans-124.5/simutrans/text/de/groundobj_build.txt000066400000000000000000000046671520675460500231130ustar00rootroot00000000000000Landschaftsobjekte platzieren

Landschaftsobjekte platzieren

Mit dem Dialog "Landschaftsobjekte platzieren" kannst verschieden Objekte zur Verschönerung der Karte platzieren. Er ist über den Karteneditor zu öffnen. Diese Landschaftsobjekte sind für das Spiel nicht notwendig, Sie dienen nur der Optik und behindern eine Bebauung nicht. Landschaftsobjekte sind Felsen die die Eiszeit hinterlassen hat, kleine Teiche mit Enten, Blumen, Büsche, Kakteen, Landmarken, oder ähnliches.

Oben befinden sich die Auswahl für eine bestimmte Klimazone.
· Alle bedeutet alle zur Verfügung stehenden Landschaftsobjekte werden aufgelistet, unabhängig von den Klimazonen, aber nur Landschaftsobjekte die zur Klimazone des Bodens passende können platziert werden. Will man die Prüfung auf Klimazonen ignorieren dann kann man die Option alle Klimazonen daneben aktivieren. Dann findet keine Prüfung auf die Klimazone beim Platzieren mehr statt.
Wählt man eine Klimazone entsprechend dem vorgesehenen Boden aus, so werden nur noch die dafür passenden Landschaftsobjekte angezeigt.

Darunter kann man die Anzeige des Namens und die Sortierung in der Liste ändern.
· Übersetzung: Zeigt die Namen so an, wie diese im Spiel verwendet werden. Dies ist abhängig von der eingestellten Sprache. Wenn keine Übersetzung für die eingestellte Sprache vorhanden ist, wird der Objektname angezeigt.
· Objekt: Zeigt die Programm internen Objektnamen an.
· Kosten für Entfernung Danach wird nur sortiert, es werden die Übersetzungsnamen angezeigt.
Der Button daneben dreht die Sortierung auf absteigend um.

Auswahlliste

· links befindet sich die Auswahlliste mit den Landschaftsobjekten
· rechts werden die Informationen des ausgewählten Landschaftsobjekts angezeigt
Der Name, ein eventueller Erläuterungstext, alle passenden Klimazonen, ob es eine Grafik für Schnee gibt, für welche Jahreszeiten Grafiken vorhanden sind, was es kostet diese Objekt abzureißen, und von wem die Grafik gemalt wurde.
4 Jahreszeiten bedeutet Frühling, Sommer, Herbst Winter.
· unten rechts wird das Bild des ausgewählten Objekts angezeigt

Optionen

· alle Klimazonen: Diese Option deaktiviert die Klimazonenzuordnung beim platzieren des Landschaftsobjekts.

simutrans-124.5/simutrans/text/de/haltlist.txt000066400000000000000000000062041520675460500215540ustar00rootroot00000000000000Haltestellenliste

Haltestellenliste

Die Haltestellenliste beinhaltet alle Haltestellen, also Bahnhöfe, Frachthöfe, Bus- und Tramhaltestellen, Anlegestellen sowie Flughäfen des aktiven Spielers. In die Liste ist eine Sortierfunktion eingebaut. Über einen Filter kannst Du Haltestellen nach definierten Kriterien anzeigen lassen.
Der Filter kann entweder "an" oder "aus" sein. Wenn Du eine Haltestelle nicht findest, prüfe, ob der Filter "aus" ist. Die Voreinstellung ist "an". Über einstellen können die Filterkriterien ausgewählt werden.

Sortieren nach: Der Schalter verfügt über 4 Stellungen, welche Du nacheinander wählen kannst.
· Bezeichnung: Die Liste wird in der Namensreihenfolge alphanumerisch sortiert.
· Wartend: Die Haltestellen werden nach der Anzahl wartender Personen und Güter sortiert. Dabei werden die Werte aller wartenden Einheiten addiert.
· Typ: Die Sortierung der Liste erfolgt nach Fahrzeugtypen in folgender Reihenfolge: Lastwagen, Schienenfahrzeuge, Schiffe, Busse und Flugzeuge. Bohrinseln haben keinen Typ.
· Nr.: Die Liste wird nach den Ordnungszahlen der Haltestellen sortiert.
· aufsteigend/absteigend: Die Liste wird bei jeder Schalterbedienung in umgedrehter Reihenfolge dargestellt.

Listeneinträge
· Ordnungszahl: Vom Spiel werden für alle Haltestellen Ordnungszahlen vergebenen.
· Name der Haltestelle: Die Namen entsprechen jenen, welche über den Haltestellen angezeigt werden. Mit einem klick auf den Haltestellennamen öffnet sich das Haltestellenfenster und die Hauptkarte zentriert auf die Haltestelle. Im Haltestellenfenster kannst Du den Namen der Haltestelle ändern.
· Angenommene Kategorien: Unter der Ordnungsnummer können drei Symbole für Passagiere, Post und Güter erscheinen. Fehlen Symbole, so werden die entsprechenden Kategorien nicht angenommen. Dies kann durch Erweiterung der Haltestelle mit zusätzlichen Haltestellengrafiken oder Nebengebäuden geändert werden.
· Wartende Güter und Passagiere: Personen und jede Güterart sind in einer Zeile gelistet.
· Statusanzeige der Haltestelle:
- Grün: Bei der Haltestelle ist alles in Ordnung.
- Gelb: Die Haltestelle ist nicht erschlossen (kein Fahrzeug kam vorbei, nichts warted).
- Orange: Die Haltestelle ist mäßig überfüllt mit Umsteigepassagieren/Waren.
- Rot: Die Haltestelle ist völlig überfüllt (mehr als 1,5fache Kapaziät) bzw. mehr als 200 Personen/Briefe haben ihre Reise wegen Überfüllung nicht angetreten oder eine Fabrik konnte ihre Waren nicht loswerden und musste deswegen die Produktion einstellen.

· Symbol des Haltestellelentyps: Die Symbole ermöglichen Dir eine schnelle Orientierung über die Fahrzeugtypen der Haltestellen. Die Symbole sind Bus, Lastwagen, Lok, Schiff, Flugzeug, Magnetbahn. Liegt eine Tram-Haltestelle auf einer Straße, ist deren Symbol eine Straßenbahn, ansonsten ist es eine Lok.

simutrans-124.5/simutrans/text/de/haltlist_filter.txt000066400000000000000000000036731520675460500231300ustar00rootroot00000000000000Haltestellenlistenfilter

Filtereinstellungen zur Haltestellenliste

Im Fenster für die Einstellungen zur Haltestellenliste können die Filteroptionen für die Anzeige der Haltestellen gesetzt werden. Ein eingedrückter Knopf bedeutet, das Kriterium muss erfüllt sein, um durch den Filter zur Anzeige zu gelangen. Dabei werden die Eigenschaften innerhalb eines Hauptmenüs mit logisch "OR" (eines der angewählten Kriterien muss erfüllt sein) und zwischen den Hauptmenüs mit logisch "AND" (alle angewählten Kriterien müssen erfüllt sein) verknüpft.

Namen filtern: Du kannst im Textfeld einen Name angeben. Es werden nur diejenigen Stationen in die gefilterte Liste aufgenommen, deren Namen dem Eintrag genau entsprechen. Achtung Platzhalterfunktionen wie * oder ? werden nicht unterstützt.

Typen filtern: Es gelangen nur Haltestellen in die gefilterten Liste, welche mindestens ein Element des selektierten Typs enthalten. Haltestellen von Industrien im Wasser sind keinem Typ zugeordnet. Sie werden durch den Filter nicht erfasst, werden also auch nicht unter Anlegestelle angezeigt.

Spezialfilter:
· überfüllt: Die Funktion ist derzeit außer Betrieb.
· keine Verbindung: Unter die Kategorie fallen Haltestellen, die an keine Linie angeschlossen sind.

Güterarten:
· Nimmt an: Alle Haltestellen werden gelistet, welche die selektierten Güterarten annehmen können.
· Gibt ab: Alle Haltestellen werden gelistet, von welchen aus die selektierten Güterarten abfahren können.
Umladehaltestellen werden in beiden Spalten nicht berücksichtigt!

Auswahlschalter:
· alle: Alle Güterarten werden ausgewählt.
· keine: Alle Güterarten werden abgewählt.
· inv.: Bei Betätigung wird die Güterauswahl umgekehrt.

simutrans-124.5/simutrans/text/de/industry_info.txt000066400000000000000000000052241520675460500226250ustar00rootroot00000000000000Fabrikdetails

Fabrikfenster

Das Fabrikfenster erscheint, sobald auf eine Fabrik geklickt wird. Es enthält verschiedene Informationen über diese Farbik.

Fabriken können Verbraucher, Produzenten oder verarbeitende Industrie sein. Reine Verbraucher sind zum Beispiel Kraftwerke oder ein Supermarkt, reine Produzenten eine Kohlegrube oder eine Müllhalde; ein Stahlwerk oder eine Raffinerie sind verarbeitende Fabriken.

Unter dem Namensfeld findet sich die akteulle Produktion pro Monat (für 100%). Sind danben Symbole für Passagiere/post/Strom, dann kann die Produktion auch durch Transport von Arbeitern, Post bzw. Elektrizitätsversorgung erhöht werden. (Ist dies momentan der Fall, erschient das jeweilige Symbol auch im Bild der Fabrik rechts daneben.)
Unter dem Bild zeigt ein Balken den Status der Fabrik an. Die Farben bedeuten:
- Weiss: Industrie produziert nicht
- Gelb: Industrie ist nicht angeschlossen
- Grün: Industrie hat Optimum
- Orange: Industrie funktioniert, aber Verbesserungen sind möglich
- Rot: Industrie muß überprüft werden

Bei Produzenten listet Produktiondie Warenmengen auf, die auf Lager ist / und die maximale Lagermenge je Ware.
Bei Verbrauchern werden unter Verbrauch die Vorräte, / die unterwegsbefindlichen Waren / und die maximale Lagerkapazität je Ware angezeigt.
Stauen sich produzierte Waren, dann sind alle Abnehmer überfüllt oder es gibt keine Verbindung. Ist das Lager für ankommende Waren überfüllt, dann fordert die Fabrik auch keine neuen Waren mehr an. (Diese Verhalten kann mit dem Eintrag "just_in_time" in der simuconf.tab angepasst werden.)

Da in Simutrans eine Fabrik feste Verträge mit anderen Fabriken hat, sind in dem Dialog als nächstes die Verbraucher aufgelistet. Nur diese nehmen Waren dieser Fabrik an. Ein Klick auf das Dreieck springt zu der jeweiligen Fabrik. Bei reinen Verbrauchern fehlt dieses Feld.

Als nächstes kommen die Lieferanten, die diese Fabrik mit Rohstoffen versorgen. Nur von diesen werden Waren angenommen. Auch hier kann man mit dem Dreieck wieder zu der jeweiligen Fabrik springen.

Darunter findet man die Städte, aus denen die Arbeiter für diese Fabrik kommen mit dem Passagier- und Postaufkommen der Fabrik. Ist die Liste leer, dann sind die Städte zu weit entfernt.

Als letztes findet man noch eine Liste der Stationen, die mit dieser Fabrik verbunden sind.

Rechts unter dem Bild der Fabrik findet man eine Knopf um eine detaillierte Statistik zu der Fabrik zu sehen.

simutrans-124.5/simutrans/text/de/labellist_filter.txt000066400000000000000000000010241520675460500232430ustar00rootroot00000000000000Markerliste

Markerliste

In der Markerliste werden alle gesetzten Marker der Karte angezeigt.
Mit der Option nur eigene Marker wird die angezeigte Liste auf die eigenen Marker eingeschränkt.

Es stehen 2 Schaltflächen zum sortieren der Liste zur Verfügung. Die linke Schaltfläche sortiert nach: · Bezeichnung · nach Orten · Spieler Die rechte Schaltfläche sortiert jeweils nach aufsteigend und absteigend.

simutrans-124.5/simutrans/text/de/language.txt000066400000000000000000000011501520675460500215060ustar00rootroot00000000000000Sprachauswahl

Sprachauswahl

Über die Sprachauswahl kannst Du die Sprache einstellen, in der Simutrans angezeigt wird. Wenn die Sprache vor dem Erzeugen einer neuen Karte ausgewählt wird, werden auch die Ortsnamen in der entsprechenden Sprache erstellt.

Die Sprache kann jederzeit geändert werden. Geöffnete Dialoge behalten teilweise die alte Sprache bei und werden erst nach erneutem Öffnen in der neuen Sprache angezeigt.

Du kannst helfen, Simutrans in andere Sprachen zu übersetzen.
Gehe dafür auf https://translator.simutrans.com

simutrans-124.5/simutrans/text/de/linedetails.txt000066400000000000000000000053121520675460500222240ustar00rootroot00000000000000Linienfenster

Linienfenster

· Liniennamen ändern: Der Name der Linie wird angezeigt. Klicke zum Editieren des Namens ins Namensfeld und bestätige die Änderung mit RETURN.

Linienstatistik: Die wichtigsten statistischen Daten der Linie werden unterhalb des Linien-Namens angezeigt.
· Fahrzeuge: Anzahl der Fahrzeugverbände der Linie.
· Auslastungsbalken: Im Balken wird die prozentuale Auslastung der Linie grafisch dargestellt.
· Profit: Der Profit der Linie im aktuellen Monat.
· Kapazität: Die genutzte Ladekapazität der Linie, in Klammern die prozentuale Auslastung.

Darunter sind vier Reiter mit weiteren Einstellungen.

Fahrplan ändert den Fahrplan für alle Fahrzeugverbände, die zur Linie gehören. Die Änderung wird bei Schießen des Fensters oder bei Anwählen eines anderen Reiters übernommen.

Statistik: zeigt die monatsweise Statistiken der Linie der letzten 12 Monate as Kurven. Der aktuelle Monat steht links. Die Farben der Kurven entsprechen den Farben der Schalter.
· Freie Kapazität: Der nicht genutzte Laderaum aller Fahrzeugverbände der Linie, summiert für jeden Fahrplanabschnitt.
· Beförderungen: Der genutzte Laderaum alle Fahrzeugverbände der Linie, summiert für jeden Fahrplanabschnitt.
· Einnahmen: Die Erlöse aller Fahrzeugverbände dieser Linie.
· Betriebskosten: Die anfallenden Kosten für den Betrieb aller Fahrzeugverbände der Linie.
· Profit: Der Profit ergibt sich aus Einnahmen minus Betriebskosten.
· Fahrzeuge: Die Anzahl der Fahrzeugverbände, welche die Linie befahren.
· Strecke/Monat: Die zurückgelegt Strecke aller Fahrzeuge der Linie.
· Geschwindigkeit:
· Maut: Kosten die durch Nutzung von Verkehrswegen anderer Spieler angefallen sind.

Halltestellenliste: Alle angefahrenen Haltestellen der Linie sind hier aufgeführt. Jeweils nur einmal, auch wenn sie im Fahrplan mehrmals angefahren werden. Freie Wegpunkte werden nicht angezeigt. Die ANzeige is wie in der Haltestellenliste.

Fahrzeugliste: All hier aufgeführte Fahrzeuge gehören zu dieser Linie. Fehlt ein dort vermuteter Fahrzeugverband, ist es möglich, dass sein Fahrplan geändert worden ist und er dadurch aus der Linienzugehörigkeit gefallen ist. Die Liste verhält sich wie die Fahrzeugliste. Zum Auswählen eines Fahrzeugverbandes auf diesen klicken.

simutrans-124.5/simutrans/text/de/linemanagement.txt000066400000000000000000000061321520675460500227140ustar00rootroot00000000000000Linienverwaltung

Linienverwaltung

Die Linienverwaltung wird über die Hauptmenüleiste oder mit der Taste w aufgerufen. Je nach verwendeten Grafikset kann sich der Button auch in anderen Menüs befinden und die Tastenkombination kann anders sein.
In der Linienverwaltung werden alle eingerichteten Linien des aktiven Spielers aufgelistet.

Im oberen Teil befinden sich Filterelemente.
Ganz oben befindet sich ein Eingabefeld um Linien mit Namen bzw. Namensteilen zu filtern. Darunter befindet sich ein Auswahlfeld um die Linien nach Waren zu filtern.
Darunter befindet sich eine Sortierung. Die Liste kann mit den Entsprechenden Einträgen sortiert werden. Der Button hinter dem Auswahlfeld legt die Richtung (aufsteigend/absteigend) der Sortierung fest.

Darunter können Linienaktionen ausgeführt werden:
· Neue Linie: Ein neues Linienfenster öffnet sich. Der Button ist nur aktiv, wenn die Linienauswahl (Reiter über der Liste) nicht auf alle steht.
· Linie löschen: Die markierte Linie wird ohne Rückfrage gelöscht. Der Button ist nur aktiv, wenn der Linie keine Fahrzeugverbände zugewiesen sind (weißer Linienname).

Die Option einzelne Linienfenster rechts daneben bewirkt, das beim anklicken einer anderen Linie das gerade aktive Linienfenster geschlossen wird. Ist die Option nicht aktiv, werden die Linienfenster gleichzeitig geöffnet. Geöffnete Linienfenster sind in der Liste markiert.

Auflistung im unteren Teil: Hier sind die Linien mit ihren Bezeichnungen alphanumerisch gelistet. Wird eine Linie angeklickt, dann öffnet sich das Linienfenster der Linie. Erneutes anklicken schließt das Fenster wieder.
Über die Reiter lässt sich die Liste filtern:
· Alle: Alle Linien werden angezeigt.
· Zug: Alle Zug-Linien (Bahn, Trams und Magnetbahnen) werden angezeigt.
· Monorail: Alle Magnetbahnen-Linien werden angezeigt.
· Tram: Alle Straßenbahn-Linien werden angezeigt.
· Straße: Alle LKW-Linien (Busse, Posttransporter, LKW) werden angezeigt.
· Schiff: Alle Schiffslinien werden angezeigt.
· Flug: Alle Fluglinien werden angezeigt.
Je nach verwendetem Grafikset kann die Anzahl und die Bezeichnung abweichen.

Die Farbe des Liniennamen hat folgende Bedeutung:
· scharz Alles ok.
· weiß: Dieser Linie sind keine Fahrzeugverbände zugeordnet.
· gelb: Auf dieser Linie hat sich kein Fahrzeugverband bewegt (Betriebskosten gleich 0).
· rot: Der Profit dieser Linie ist negativ.
· blau: Mindestens ein Fahrzeugverband auf dieser Linie ist veraltet. Wird von Rot überlagert.
· orange: Mindestens ein Fahrzeugverband auf dieser Linie steht im Stau.

Verweilt der Mauszeiger länger über einem Liniennamen, werden einige Daten der Linie eingeblendet.

simutrans-124.5/simutrans/text/de/list.txt000066400000000000000000000015441520675460500207050ustar00rootroot00000000000000Listenverwaltung

Listenverwaltung

Die Listenverwaltung wird über die Hauptmenüleiste aufgerufen.
Im Listenverwaltungsmenü gibt es mehrere Einträge. Je nach Grafikset können Einträge fehlen.

Linienverwaltung
Fahrzeugliste Spieler
Haltestellenliste
Depotliste
vorhandene Fahrzeuge Spiel
Stadtliste
Warenliste
Fabrikliste
Touristenzielliste
Markerliste
Spielerliste

simutrans-124.5/simutrans/text/de/load.txt000066400000000000000000000033551520675460500206530ustar00rootroot00000000000000Lademenü

Spielauswahldialog (Ladedialog)

Der Ladendialog öffnet ein gespeichertes Spiel zum Weiterspielen.
Achtung: Das aktuelle Spiel (welches gerade im Hintergrund läuft) wird beim Laden eines anderen Spiels ohne Rückfrage beendet.

Eingabefeld Dateiname: Durch Eingabe eines Dateinames kann der Spielstand geladen werden. Es muss die Entertaste oder der OK Button gedrückt werden.

X - Löschen: Achtung: Das X vor jedem Namen löscht den entsprechenden Spielstand. Es wird nicht nachgefragt sondern sofort gelöscht. Das X wird nur angezeigt, wenn in der simuconf.tab der Parameter show_delete_buttons = 1 gesetzt ist.

Schaltfläche mit Namen: Durch einen Klick auf einen Button wird der entsprechende Spielstand geladen. Der Spielstand muss mit dem gestartetem pak (Grafikset) und der Simutrans-Version kompatibel sein.

Information des paks (Grafiksets): Gibt an mit welchem pak (Grafikset) der Spielstand gespeichert wurde.

Datum und Uhrzeit: Gibt an zu welchem Datum und welcher Uhrzeit der Spielstand gespeichert wurde.

Dieses Spiel als Server starten. Wird diese Option gesetzt, dann startet das ausgewählte Spiel als Serverspiel und kann über das Netzwerk/Internet erreicht werden.

OK Läd das Spiel was im Feld Dateiname angegeben wird.

Abrechen Schließt den Dialog ohne ein Spiel zu laden.

Autosave
Spielstände, die durch die autosave-Option gespeichert wurden, tragen den Namen 'autosave' mit 2 angehängten Ziffern. Die Ziffern geben den gespeicherten Monat an.

simutrans-124.5/simutrans/text/de/load_relief.txt000066400000000000000000000011321520675460500221700ustar00rootroot00000000000000Höhenkarte-Lademenü

Höhenkarte-Lademenü

Im Höhenkarte-Lademenü werden alle Relief-Karten gelistet, welche sich im Verzeichnis simutrans/maps bzw. Benutzerverzeichnis/simutrans/maps befinden. Es handelt sich um Graustufenbilder, die Du in verschiedenen Grafik- und Bildbetrachtungsprogrammen aus farbigen Bitmapdateien oder aus Graustufenbitmaps erstellen kannst. Sie werden im Format .ppm gespeichert.

Weitere Details zur Nutzung von Lade-Menüs findest Du in der Hilfe zum Lademenü.

simutrans-124.5/simutrans/text/de/mailbox.txt000066400000000000000000000033501520675460500213620ustar00rootroot00000000000000Mailbox und Mailbox Optionen

Mailbox und Mailbox Optionen

In der Mailbox sammeln sich alle Meldungen, die seit dem Spielstart bzw. dem letzten Laden eines Spielstandes angefallen sind.
Bei einigen Meldungen kannst Du den Bildschirm auf den Ort zentrieren, welcher die Meldung ausgelöst hat. Klicke dazu die Meldung an.

Optionen: Über den Optionen-Schalter gelangst Du zum Dialog mit den Mailboxeinstellungen. Dort kannst Du für jede Mitteilung einstellen, ob und wie sie angezeigt werden soll. Anzeigekombinationen sind möglich.

Mitteilungen:
· Neues Jahr: Ein neues Jahr hat begonnen.
· KI baut: Ein Computergegner hat eine neue Linie in Betrieb genommen.
· Stadtnachrichten: Die Stadt baut ein größeres Rathaus.
· Keine Route: Das in der Meldung angegebene Fahrzeug findet keinen Weg mehr.
· Neue Industrie: Durch das Stadtwachstum wurde eine neue Industriekette mit Stadtgeschäft gebaut.
· Neue Attraktion: Ein neues Touristenziel oder Denkmal wurde errichtet.
· Neue Fahrzeuge: Ein neues Fahrzeug ist verfügbar, oder ein Fahrzeug ist veraltet.
· Bahnhof voll: Die angegebene Haltestelle ist überfüllt.
· Warnungen: Du hast 3 Monate Zeit, deine Schulden zurückzuzahlen, etc.

Bedeutungen der Schalter:
· linke Spalte: Die Meldung wird im Ticker (Lauftext über der Statusleiste) ausgegeben.
· mittlere Spalte: Ein Dialog erscheint für kurze Zeit und beendet sich dann selbst.
· rechte Spalte: Ein Dialog informiert Dich. Er wird solange angezeigt, bis Du ihn schließt.

simutrans-124.5/simutrans/text/de/mainmenu.txt000066400000000000000000000151441520675460500215440ustar00rootroot00000000000000Hauptmenü

Hauptmenü, die Buttonleiste

Einstellungen: Über die Diskette gelangst Du zu den Einstellungen sowie zum Laden und Speichern. Du kannst auch eine neue Karte generieren lassen bzw. das Spiel beenden.

Reliefkarte: Die Übersichtskarte über die Welt mit ihren Objekten zeigt die Bewegungen der Spielerfahrzeuge an und gestattet Dir einige statistische Ansichten. Wenn Du einen Punkt auf der Reliefkarte anwählst, springt die Spielkarte zum entsprechenden Bildausschnitt.

Lupe: Die Lupe symbolisiert das Abfragewerkzeug. Ist es aktiv, wird der Cursor ebenfalls zur Lupe. Wählst Du mit ihr ein Objekt an (Gebäude, Fahrzeug), werden Angaben (Infofenster) darüber eingeblendet. Empfehlenswert ist es, immer wieder zu der Abfrage zurückzuwechseln, vor allem, wenn Du gerade mit dem Abrisswerkzeug gearbeitet hast. So vermeidest Du, ein Objekt aus Versehen abzureissen, anstatt die Objektinformationen aufzurufen.

Geländewerkzeuge: Die Rampe öffnet ein Menü, mit welchem Du die Höhen- und Hanglagen in der Karte verändern kannst. Gelände anheben/absenken sind links auch im Hauptmenü verfügbar.

Bauwerkzeugmenüs: Hinter den Werkzeugen verbergen sich die jeweiligen Baumenüs.
· Eisenbahnen
· Magnetbahnen/Monorails
· Straßenbahnen
· Straßen
· Häfen und Werften
· Flughäfen

An Fahrzeuge gelangen: Jedes der Baumenüs enthält ein Depotwerkzeug (s. Hilfen zu den Baumenüs). Nach dem Bau eines Depots wird bei dessen Anwahl das Depotfenster geöffnet.

Spezialbauwerkzeuge: Das sich öffnende Menü enthält verschiedene Bauwerkzeuge, die den Aufbau der Transportnetze ergänzen.

Abrisswerkzeug: Nach der Auswahl des Werkzeuges wird der Cursor zur durchkreuzten Wolke. Ein mit dem Werkzeug angewähltes Objekt wird ohne weitere Warnung von der Karte entfernt, sofern Du zu dessen Abriss berechtigt bist. Bei Abrissarbeiten entstehen unterschiedliche Kosten, die sofort von Deinem Kassenbestand abgebucht werden.
· Eigene Bauwerke sowie Stadtstraßen und private Gebäude wie Wohn- , Büro- und Gewerbeliegenschaften (oranger Fensterbalken im Infofenster) können abgerissen werden. Beachte, dass beim Gebäudeabriss auch die Gebäudekosten als Unkosten anfallen.
· Spezielle Gebäude wie Rathäuser oder Sehenswürdigkeiten sowie Einrichtungen anderer Spieler können nur durch den jeweiligen Eigentümer abgerissen werden (Vorsicht bei erschlossenen Industrien und Rathäusern).
· Auf Schienen und Straßen sind bestimmte Abrissreihenfolgen festgelegt. Sofern auf der Abrisskachel vorhanden, werden zuerst Signale, danach Haltestellen, schliesslich eine ggf. vorhandene Fahrleitung, und zuletzt der Betriebsweg selbst abgerissen.
· Sollen Abrissarbeiten auf Brücken oder Schwebebahnen durchgeführt werden, muss beim Abreissen die Ctrl(Strg)-Taste gedrückt gehalten werden. Sonst werden die Objekte auf der unteren Ebene, z.B. ein Haus unter einer Brücke, entfernt.

Linienverwaltung: Linien können eingerichtet, geändert und gelöscht werden, sowie Informationen über die Linien eingesehen werden.

Listenverwaltung: Das Symbol des Listenblattes führt zu einem Untermenü mit sechs Listen:
· Haltestellenliste
· Fahrzeugliste
· Stadtliste
· Warenliste
· Fabrikliste
· Touristenzielliste

Briefkasten: Alle Meldungen, die seit dem Spielstart bzw. seit dem letzten Laden eines Spielstandes angefallen sind, werden in einer Liste aufgeführt.

Finanzfenster: Das Kontefei von Hajo, dem Initiator von Simutrans, schmückt die Geldnote, welche in Simutrans die Finanzkraft repräsentiert. Das sich öffnender Fenster zeigt alles Wissenswerte über Deine momentane Finanzlage.

Screenshot: Bei der Nutzung des Kamera-Buttons wird ein Schnappschuss des aktuellen Kartenausschnittes als Bitmap-Datei im Verzeichnis simutrans/screenshot gespeichert.

Pause: Falls Du Dir eine Pause vom harten Alltag des Transportunternehmers gönnen möchtest, bestelle über den Button ruhig eine heiße Tasse Tee oder Kaffee. Der Service ist in Simutrans natürlich gratis.
Vorlauf:Falls Dir Simutrans zu langsam läuft, kannst Du hier beschleunigen. Es kann auch zu ruckeln führen, speziell mit Schiffen!

Statusleiste

Über der Statuszeile, am unteren Hauptfensterrand, werden gelegentlich Nachrichten angezeigt. Welche Meldungen erscheinen sollen, lässt sich in den Mailbox-Optionen festlegen. Die Statusleiste enthält Anzeigen, die sich im Spielverlauf permanent aktualisieren:
· Datum und Uhrzeit: In Simutrans ist ein Jahr in 12 Monate und ein Monat in 24 Stunden unterteilt. Über das Datum wird die Verfügbarkeit von Fahrzeugen und Gebäuden gesteuert, wenn die 'timeline' aktiviert ist (siehe unten). Vor der Jahreszahl werden die meteorologischen Jahreszeiten angezeigt. In einigen Szenarien kann sich der Untergrund mit den Jahreszeiten ändern.
· Spieler: Der Name des aktuellen Spielers wird in dessen Spielerfarbe angezeigt, sofern es ein Computerspieler ist.
· Kasse: Der Kassenbestand des aktuell agierenden Spielers wird angezeigt und permanent aktualisiert.
· Positionskoordinaten: In der Klammer werden die X- und Y-Koordinaten sowie die Höhenebene der Kachel angezeigt, über welcher sich der Cursor gerade befindet.
· Spielgeschwindigkeit: Der Wert (T=1.00) gibt die momentane Spielgeschwindigkeit an, siehe unter Spielsteuerung, Abschnitt Spielgeschwindigkeit. Bei Vorlauf wird die T-Anzeige durch zwei Pfeile ersetzt.
· mit Epochen: Zeigt an, ob die timeline aktiviert ist oder nicht, siehe Neue Welt erstellen.

simutrans-124.5/simutrans/text/de/map.txt000066400000000000000000000104211520675460500205010ustar00rootroot00000000000000Kartenfenster

Kartenfenster

Das Kartenfenster öffnest Du entweder mit dem m auf der Tastatur oder über das Kartensymbol in der Hauptmenüleiste.

Die Reliefkarte zeigt die Welt mit allen Objekten wie Industrien, Verkehrswegen, Städten und Fahrzeugen. Du kannst schnell zu jedem beliebigen Ort auf der Karte springen, indem Du die Position in der Reliefkarte anklickst.

Auswahl

Blendet die farbigen Schaltflächen ein, mit denen verschiedene Informationen in der Karte hervorgehoben werden können. Wenn nicht anders beschrieben, werden Anzeigen nach einem Farbschema von rot bis blau relativ zueinander eingefärbt.

Städtenamen blendet die Städtenamen weiss in der Karte ein. (Kann auch durch Drücken der Strg-Taste erreicht werden.
Passagiere markiert grob alle Gebiete in Spielerfarbe, welche im Einzugsgebiet von Haltestellen mit Personenverkehr liegen. Eine genaue Anzeige auf der Hauptkarte liefert die Taste v.
Postmarkiert grob alle Gebiete in Spielerfarbe, welche im Einzugsgebiet von Haltestellen mit Briefkästen bzw. Postämtern liegen.
Fracht zeigt, wieviel Fracht (Einheiten von Passagieren und Gütern) auf den Verkehrswegen transportiert wird.
Haltstatus zeigt an, ob an einer Haltestelle unzufriedene Passagiere erzeugt werden. Die gleiche Anzeigefarbe wird auch in der Haltestellenliste und im Statusbalken beim Haltestellennamen verwendet.
An-/Abfahrten zeigt an, wieviele Fahrten zu und ab einer Haltestelle gegangen sind.
Verkehr zeigt die Auslastung einer Strecke an.
Abfahrtsort zeigt an, wieviele Waren und Personen diese Haltestelle als Ausgangspunkt haben.
Zielort zeigt an, wieviele Waren und Personen diese Haltestelle als Endpunkt haben.
Wartend zeigt an, wieviele Waren und Personen an einer Haltestelle auf Beförderung warten.
Gleistrassen hebt Eisenbahnlinien weiß hervor; elektrifizierte Bahnstrecken werden braun hervorgehoben. Signale werden mit gelben Punkten markiert.
Tempolimit zeigt Höchstgeschwindigkeiten auf Straßen und Schienen an.
Freileitungen trägt die Hochspannungsleitungen in die Karte ein. Die Farbe kodiert die aktuelle Stromleistung beim öffnen der Karte (wird nicht automatisch aktualisiert!).
Attraktionen vergrößert die eingetragenen Touristenattraktionen in der Karte und färbt sie nach ihrem Passagieraufkommen ein.
Fabriken vergrößert die Anzeige der Industrien in der Karte.

Depots Hebt alle Depots in orange, gelb, rot und rosa hevor, je nach Wegtyp.
Wälder Geschlossene Waldgebiete (mit mehr als zwei Bäumen pro Kachel) werden hellgrün hervorgehoben.
Stadtgrenzen: Umrahmungen der Stadtgebiete, d.h. alle Gebiete, die zu einer Stadt direkt gehören.
Reiseziele: Falls eine Stadt ausgewählt ist, werden die Reiseziele dieser Stadt auf der Karte angezeigt (analog zu der kleinen Karte der Stadtinformation.

Werteskala
Blendet den Balken ein, der die Farben zeigt, nach denen einige Objektauswahlen bestimmte Kartenbereiche entsprechend dem dargestellten Wert einfärben.

Industrieliste
Farbskala der Industrien.

Vergrößerung
Wähle die Vergrößerung von 1:6 bis 16:1 Pfeilschalter oder Mausrad.
Bei 1:1 entspricht eine Kachel einem Pixel auf der Karte.

Isometrische Anzeige:
Die Reliefkarte wird analog zur Spielfeld-Ausrichtung ausgerichtet.

Fabrikverbindungen:
Abnehmer der Industrien Mit der Maus auf eine Industrie zeigen (weisse Linien).
Zulieferer der Industrien Shift + mit der Maus auf eine Industrie zeigen (rote Linien).
Hinweis: Sollte nichts angezeigt werden, dann einmal in das Kartenfenster klicken.

Linienpläne
Bei geöffneter Linienverwaltung wird die dort markierte Linie in der Reliefkarte angezeigt.

simutrans-124.5/simutrans/text/de/monorailtools.txt000066400000000000000000000015411520675460500226300ustar00rootroot00000000000000Monorailbau

Monorailbauwerkzeuge

Diese Werkzeugleiste dient dem Bau von Monorailgleisen, Brücken, usw. Wird Simutrans in einem kleineren Fenster gespielt, können die letzten Symbole auf die nächste Zeile umgebrochen sein. Bei der Auswahl eines Werkzeuges verwandelt sich der Cursor in das jeweilige Symbol.

Monorails gibt es im Unterschied zur normalen Eisenbahn auch als Hochbahn. Sie lässt sich über einstöckige Häuser und oberhalb von Straßen bis in die Innenstädte legen. Um ein Hochbahngleis mit einem ebenerdigen zu verbinden, baut man eine Brücke. Hinweis: Die Positionierung der Maus bei Hochbahnen erfolgt auf dem Feld darunter.

Für die weiteren Erläuterungen siehe die Eisenbahnbauwerkzeuge.

simutrans-124.5/simutrans/text/de/new_world.txt000066400000000000000000000130321520675460500217250ustar00rootroot00000000000000Neue Welt

Erstelle eine neue Welt

Der Neue Welt-Dialog erscheint bei jedem Spielstart. Im laufenden Spiel kann man ihn nach dem Anwählen der Schaltfläche Neue Karte im Dialog Einstellungen aufrufen. Dieser wird mit dem Diskettensymbol in der Hauptmenüleiste geöffnet.

Im Dialog werden die Parameter eingestellt, die für die Generierung der Karte gelten sollen. Die Einstellungen erfolgen über die Pfeilschaltflächen der Einstelloptionen. Ein Klick auf einen Pfeil nach links verringert, ein Klick auf einen Pfeil nach rechts erhöht den entsprechenden Wert.

Einstellungen für neue Karte:

· Kartennummer: Es stehen 10.000 Simutrans-Karten zur Auswahl. Über sie werden die groben Strukturen der Welt festgelegt.

· Vorschaubild: Das Bild zeigt die ausgewählte Karte und aktualisiert sich bei jeder Änderung der Kartenparameter.

· Kartengröße: Wähle mit den Pfeilschalter, welche Abmessungen die Karte haben soll. Die obere Zahl gibt die Breite (x-Richtung), die untere die Höhe (y-Richtung) der Karte an. Die Einstellbereiche liegen zwischen 64 bis 4096. Der Vorgabewert ist 256x256. Unterscheiden sich die Werte, wird das Vorschaubild verzerrt dargestellt.
Die Angabe in Klammern zeigt Dir, wieviel Arbeitsspeicher ein Spiel der eingestellten Kartengröße mindestens belegen wird.

· Zufallskarte: Wähle eine zufällige Karte aus. Die oben gemachten Einstellungen werden nicht verändert.

· Lade Relief: Die getroffenen Einstellungen werden anstatt auf die angezeigte Karte auf ein im Verzeichnis simutrans/maps bzw. Benutzerverzeichnis/simutrans/maps gespeichertes Relief übertragen. Der Relief laden Dialog wird eingeblendet.

· Anzahl Städte: Bestimmt die Anzahl der Städte auf der Karte. Der Vorgabewert ist 16. Ist eine Karte zu rauh, oder ist die Zahl der Städte zu hoch, werden weniger als die eingestellte Anzahl von Städten generiert. (Der Mindestabstand der Städte ist in der Datei simutrans/config/cityrules.tab als Wert der Variablen minimum_city_distance angegeben).

· Mittlere Stadtgröße: Der Wert stellt eine mittlere Bezugsgröße dar. Beim Vorgabewert von 1600 bei 16 Städten werden ca. eine Stadt mit 8000-16000 Einwohnern, ein oder zwei mit 1000-5000 und die übrigen mit 200-600 generiert. Es ist durchaus erwünscht, dass immer mal wieder auch einige sehr große Städte erzeugt werden.

· Verbindungsstraßenlänge: An Anfang werden alle Städte miteinander verbunden, wenn dies mit einer Straße kleiner als die angegebenen Zahl möglich ist. Dabei ist eine gerade Straße 2 Einheiten teuer, Kurven und Steigungen mehr.

· Industriezweige: Der Wert gibt an, wieviele Industriezweige beim Kartenstart erzeugt werden sollen. Durch das Wachstum von Städten werden weitere Industriezweige erzeugt. Wann jeweils ein neuer Industriezweige erzeugt wird, regelt der Wert der Variablen industry_increase_every in der simucon.tab. Der dortige Vorgabewert 2000 bedeutet, dass jeweils, wenn eine Stadt die Größe von 2000, 4000, 8000 usw. Einwohnern erreicht, ein neuer Industriezweig entsteht. Mit dem Wert 0 wird die Errichtung neuer Industriezweige ausgeschaltet. Hat ein Endverbraucher mehrere Waren (z.B. die Apotheke: Medizin und Chemikalien), dann gilt jeder als eigene Industriekette.
Die Vielfalt an Industriezweigen kann durch Ergänzungspaks erhöht werden.

· Touristenattraktionen: Stelle die Anzahl von Ausflugszielen wie Burgen, Jahrmarktattraktionen usw. ein.

· Mit Einführungsdaten ab: Drücke den Schalter ein, wenn Fahrzeuge und Gebäude erst ab ihrem Entwicklungsjahr verfügbar sein sollen. Das Startjahr wird mit den beiden Pfeilschaltern geändern. Die Vorgabe ist in der simutrans/config/simuconf.tab als Wert der Variablen starting_year eingetragen. Sinnvolle Werte sind z.B. 1910, 1930, 1950. Vor 1835 gibt es nur Pferdefuhrwerke.

· Anfängermodus: In diesem Modus liefern alle Fabriken an alle geeigneten Abnehmer für diese Waren. Außerdem sind die Frachtgewinne um den Faktor 1,5 erhöht.

· Einstellungen: Ruft die Erweiterten Einstellungen auf, wo sich fast alle Parameter von Simutrans beinflussen lassen können.

· Einstellungen Landschaft: ruft die Landschaftseinstellungen auf, wo Klimazonen, Bergigkeit, Grundwasserspiegel, Flüsse und vieles mehr vorgegeben werden kann.

· Lade Spiel: Lade einen bereits vorhandenen Spielstand. (siehe Lade Menü)
Achtung: Der laufende Spielstand wird nicht gespeichert und geht verloren.

· Lade Szenario: Es stehen verschiedene Szenarien zur Auswahl. (siehe Szenario Menü)
Achtung: Der laufende Spielstand wird nicht gespeichert und geht verloren.

· Starte Spiel: Eine neue Karte wird entsprechend den Einstellungen generiert, und ein neues Spiel startet.
Achtung: Der laufende Spielstand wird nicht gespeichert und geht verloren.

· Beenden: Das Spiel wird beendet.
Achtung: Der laufende Spielstand wird nicht gespeichert und geht verloren.

simutrans-124.5/simutrans/text/de/options.txt000066400000000000000000000030331520675460500214200ustar00rootroot00000000000000Einstellungen

Einstellungen, Laden, Speichern

Der Dialog Einstellungen ist zweigeteilt. Auf der linken Seite befinden sich die Spieleinstellungen. Auf der rechten Seite kannst Du Spielstände laden, speichern, eine neue Karte starten, Missionen laden sowie das Spiel beenden.

·Sprache: Wähle die Sprache, welche Simutrans nutzen soll. Die Sprachdateien befinden sich im Verzeichnis simutrans/text. In den Unterverzeichnissen sind die Hilfetexte der Sprachen abgelegt.
·Spieler: Stelle die Anzahl der Computerspieler ein.
·Spielerfarbe: Einstellen der eigenen Spielerfarbe.
·Anzeige: Einstellungen für Grafik und Maus sowie Angaben über die Auslastung des Computers.
·Sound: Einstellungen für Sound und Musik.

·Neues Spiel: Starte eine neue Karte.
·Laden Spiel: Spielstand laden.
·Speichern: Spielstand speichern.
·Wähle Mission: Öffnet das Auswahlfenster der vorhandenen Missionen.
·Missionsstatus: Diese Schaltfläche ist nur aktiv, wenn eine Mission gespielt wird. Sie öffnet das Missionsinformationsfenster.

· Beenden: Das Spiel wird beendet (ohne den Spielstand zu sichern).

simutrans-124.5/simutrans/text/de/password.txt000066400000000000000000000020661520675460500215740ustar00rootroot00000000000000Spielernamen und Passwort ändern

Spielernamen und Passwort ändern

Es gibt zwei Möglichkeiten, warum du diesen Dialog siehst:

1.) Du hast auf das Kästchen rechts vom Spielernamen in der Spielerliste geklickt.
2.) Der aktive Spieler ist passwortgeschützt und du wolltest ein Kommando ausführen.

Ist im Dialog der Name nicht änderbar, dann ist der Spieler passwortgeschützt und du musst das Passwort eingeben. (Die Passwörter werden lokal gespeichert; solange du Simutrans nicht neu startest, musst du das Passwort nicht mehr eingeben.)

Kannst du sowohl Namen wie Passwort eingeben, dann ist der Spieler für dich verfügbar. Sobald der Name geändert oder ein neues Passwort eingeben wurde, wird dies nun für den Spieler verwendet.

Den Status eines Spieler kannst du in der Spielerliste erkennen. Ist dort eine grüne Box, ist der Spieler für dich zugänglich. Ist sie Rot, muss hier ein korrektes Passwort eingegeben werden.

simutrans-124.5/simutrans/text/de/players.txt000066400000000000000000000037001520675460500214050ustar00rootroot00000000000000Spielerliste

Spieler Menü

Im Spieler-Menü sind alle Spieler aufgelistet.
Ganz oben befindet sich der menschliche Spieler.
Darunter befindet sich die Öffentliche Hand. Die öffentliche Hand kümmert sich nur um den Ausbau der Städte und Industrien sowie Sehenswürdigkeiten und Denkmälern.

Wenn Du auf den Pfeil vor dem jeweiligen Spieler klickst, dann wechselst Du zu diesem Spieler. Voraussetzung dafür ist, das der Spielerwechsel nicht deaktiviert wurde.

Darunter befinden sich mehrere Felder, wo weitere Spieler (KI-Spieler) ausgewählt werden können. Ist das Feld grau, so ist noch kein Spieler ausgewählt. Nach der Auswahl eines Spielers, erscheint vor dem Feld eine kleine Schaltfläche, mit der der Spieler aktiviert und deaktiviert werden kann.

Aktivierte Computerspieler (KI-Spieler) werden sich alsbald auf die Suche nach neuen Transportmöglichkeiten machen und eigene Linien eröffnen. Wenn Du den KI-Spieler wieder deaktivierst, wird er keine neuen Verbindungen mehr suchen, den bisherigen Betrieb aber weiterführen.

KI-Spieler: Die Firmennamen der KI-Spieler stehen auf Schaltflächen, die die Spielerfarben des entsprechenden KI-Spielers tragen. Drücke eine dieser Schaltflächen, um dessen Finanzfenster einzusehen. Das aktuelle Barvermögen der KI-Spieler wird rechts angezeigt.

Unter der Liste mit den Spielern befindet sich die Option Ohne Bankrott. Aktivierst Du diese Option, dann gehen die Spieler (und du selbst) nicht Pleite, wenn das Gesamtvermögen negativ wird.

Für Spiele im Netzwerkmodus können Spieler passwortgeschützt werden. Ein passwortgeschützter Spieler ist durch ein rotes Kästchen neben dem Namen markiert. Ist kein Passwort gesetzt oder der Spieler freigeschaltet, ist dieses Kästchen grün. Durch Klick auf das Kästchen öffnet sich ein Dialog zur Passworteingabe.

simutrans-124.5/simutrans/text/de/privatesign_info.txt000066400000000000000000000011671520675460500233010ustar00rootroot00000000000000Privatweg

Privatweg

Mit diesem Schild kann festgelegt werden, wer auf diesem Weg passieren darf. Die einzigen Fahrzeuge der öffentlichen Hand sind die Stadtautos. Alle anderen Felder betreffen die anderen Spieler.

Spieler aktiviert, heißt dass Fahrzeuge des Spieler passieren dürfen. Dieser Spieler sieht dann auch ein offenes Tor oder wie immer auch die Durchfahrt angezeigt wird.

Dieses Verkehrzeichen gibt es in den Wegebauwerkzeugen, z.B bei den Eisenbahnwerkzeugen oder den Straßenbauwerkzeugen.

simutrans-124.5/simutrans/text/de/railtools.txt000066400000000000000000000240131520675460500217360ustar00rootroot00000000000000Eisenbahnbau

Eisenbahnbauwerkzeuge

Diese Werkzeugleiste dient dem Bau von Eisenbahngleisen, Fahrleitungen, Brücken, Tunneln, Signalen, Depots und Bahnhöfen. Wird Simutrans in einem kleineren Fenster gespielt, können die letzten Symbole auf die nächste Zeile umgebrochen sein. Bei der Auswahl eines Werkzeuges verwandelt sich der Cursor in das jeweilige Symbol.

Gleise können mit unterschiedlichen Höchstgeschwindigkeiten befahren werden. Ist die Timeline aktiviert, werden Gleise mit schnellerer Höchstgeschwindigkeit erst nach und nach verfügbar. Höchstgeschwindigkeit und Baukosten werden in den Tooltips angezeigt. Der Abriss kostet genausoviel wie das Bauen; einzige Ausname ist, wenn direkt nach einer Bauaktion, die Taste z für Rückgängigmachen betätigt wurde (siehe weiter unten).
Simutrans benutzt nur leere Felder oder bereits vorhandene Gleise. Brücken, Tunnel, Straßen usw. werden nicht benutzt. Allerdings werden automatisch Bahnübergänge gebaut. Am Hang kann nur in Neigungsrichtung gebaut werden.
Beim ersten Platzieren des Gleisbauwerkzeuges in der Weltkarte wird der Anfang der Strecke festgelegt, beim zweiten der Endpunkt. Das Spiel baut zwischen beiden Punkten die kürzestmögliche Strecke. Wenn keine Verbindung gefunden wird, passiert nach dem Anklicken des Endpunktes nichts. Eine Bauaktion lässt sich mittels z gratis rückgängig machen (ohne Baukostenerstattung), solange nichts anderes gebaut wurde.
Hinweis: Um eine diagonale Strecke oder parallel zu einem bestehenden Gleis zu bauen, beim zweiten Klick die Ctrl(Strg)-Taste gedrückt halten.

Fahrleitungen sind zum Betrieb von E-Loks und Trams notwendig. Im Depotfenster werden verfügbare E-Loks bzw. Trams nur angezeigt, wenn der Block des Depots elektrifiziert ist. Die Linienstrecken, welche von E-Loks bzw. Trams befahren werden sollen, musst Du komplett elektrifizieren, da die E-Konvois ansonsten keinen Weg finden. Sie verlassen dann das Depot nicht oder bleiben am letzten Linienpunkt vor der Fehlersituation stehen.
Das Fahrleitungswerkzeug funktioniert ähnlich wie das Gleisbauwerkzeug. Der erste Klick definierte den Startpunkt und der zweite Klick den Endpunkt. (Dabei beachtet das Werkzeug allerdings alle Einrichtungssignale!)

Eisenbahnbrücken beginnen immer an Gleisenden. Für das Brückenende sucht Simutrans in der geraden Weiterführung des Gleisendes ein vorhandenes Gleisende, welches in die Richtung des Anfangspunktes weist. Wird eine passende Hanglage vorgefunden, so wird die Brücke an sie angelehnt. Das weiter führende Gleis kann in dem Fall auch nachträglich gebaut werden, muss aber bis auf das Brückenende gebaut werden. Um eine Brücke an einem Hang beginnen zu lassen, das Gleis bis auf den Hang bauen und dann das Brückenbauwerkzeug benutzen. (In neueren Versionen kann eine Brücke auch an einer senkrechten Wand enden. Dazu sollte vorher dort schon eine Schiene gelegt worden sein.)

Eisenbahntunnel können nur gerade und nur in einer Höhenebene gebaut werden. Als Startpunkt des Tunnels muss das Gleisende in einer Hanglage enden. Setze das Tunnelbauwerkzeug auf das Gleisstück in der Hanglage. Der Tunnel wird komplett gebaut, wenn Simutrans in der gegenüberliegneden Bergflanke eine gerade Hanglage vorfindet. Der Gleisbau ist dann auf dem Tunnelausgang fortzusetzen. Erhältst Du eine Hinweismeldung, war das gegenüberliegende Hangstück nicht gerade oder ist bebaut. Eine wichtige Bauhilfe stellt die Nutzung des Gitternetzes dar (in der größten Kartenansicht schaltet die Taste # es ein und aus); mit ihm lässt sich der Durchstoßpunkt besser finden.
Willst du nur ein Tunnelportal bauen, dann kannst du einen Ctrl (Strg)+Mausklick machen. Danach kann du dann in den Untergrundmodus wechseln (mit U oder unter Anzeigeeinstellungen) und mit dem Tunnelwerkzeug weitere Strecken bauen.

Signale werden genutzt, um die Bewegungen der Züge zu kontrollieren. Die Grundstellung von Signalen bei Simutrans ist "Halt" (rotes Signal). Erreicht ein Zug ein Signal, dann bekommt er nur grün, wenn die Strecke bis zum nächsten Signal auf seiner Route frei ist und auch für keinen anderen Zug reserviert wurde. Die Strecke ist dann für diesen Zug reserviert. Die Reservierung wird hinter dem Zug aufgelöst. Diese Streckenteile können danach für Querende oder Abzweigende Züge genutzt werden. ACHTUNG: Bahnhöfe enthalten unsichtbare Signale, denn ein Zug reserviert die Strecke nur bis zu seiner Haltestelle. Soll die Strecke bis zum nächsten echten Signal reserviert werden, müssen Langstreckensignale (s.u.) verwendet werden!
- Signalbau Signale können nur auf Gleisen gebaut werden, aber nicht auf Kreuzungen. Beim Bau eines Signals wird auf jeder Gleisseite ein Signal aufgestellt. Die Strecke kann in beiden Richtungen befahren werden. Klickst Du die Platzierungsstelle erneut mit dem Signalwerkzeug an, wird ein Signal entfernt, die Strecke kann nur in Signalrichtung befahren werden. In der Regel befinden sich die Signale in Simutrans rechts vom Gleis. Beim nächsten Klick auf die Positionierungsstelle wird ein Signal auf der Gegenseite plaziert. Die Strecke kann nun nur in der Gegenrichtung befahren werden. Beim nächsten Klick erscheinen wieder beide Signale, die Strecke ist wieder in beide Richtungen befahrbar.
Wird ein Signal mit Strg+Maustaste ausgewählt, kann mit dem Multisignalwerkzeug eine ganze Reihe von Signalen auf einmal gebaut werden.
- Wegfindung Wurden auf einer Strecke Richtungssignale in der verkehrten Richtung aufgestellt, können die Züge der Linie keinen Weg finden und verlassen das Depot oder den Bahnhof nicht bzw. bleiben am letzten Linienpunkt vor dem nicht passierbaren Signal stehen. Es ist somit empfehlenswert, dem Verhalten der Züge auf neu eingerichteten Strecken anfänglich etwas erhöhte Aufmerksamkeit zu widmen.
- Signalarten:
-- einfaches Signal Es ist das normale Signal, oft Formsignal, Standardlichtsignal, oder einfach nur Signal genannt. Erreicht ein Zug das Signal, dann wird versucht die Strecke bis zum nächsten Signal, oder der nächsten Haltestelle des Zugs, zu reservieren. Gelingt das, dann bekommt er grün. Ansonsten bleibt das Signal rot und der Zug muss er warten bis die Strecke frei wird.
-- Zweiblocksignal Zwei-Block-Signale (gelegentlich auch Vorsignal genannt) geben die Fahrt erst frei, wenn das nächsten Signal ebenfalls frei zeigt. Es werden zwei freie Blöcke zur weiterfahrt benötigt. Sie sind prädestiniert für verschiedene Deckungsaufgaben.
Als Einfahrsignale regeln sie, dass ankommende Züge den Weichenbereich nicht befahren, solange sich noch ein Zug im Bahnhofsblock befindet. Bei mehrgleisigen Kreuzungen kann durch Zwei-Block-Signalen erreicht werden, dass Züge erst dann den Kreuzungsbereich befahren, wenn danach noch 1 Block frei ist. So vermeidest Du Situationen, in denen Züge auf Kreuzungen stehen bleiben.
-- Auswahlsignal Auswahlsignale (Einfahrtsignale) geben die Fahrt erst frei, wenn sie für den Zug einen freien Weg bis zum Zielbahnhof, das ist der nächste im Fahrplan angegebene Halt, reservieren konnte. Wenn der im Fahrplan angegebene Bahnsteig nicht frei ist, dann wird der nächste ausreichend lange Bahnsteig des Zielbahnhofs ausgewählt. ACHTUNG: Der Zielbahnhof ist nicht zwingend der nächste nach dem Auswahlsignal kommende Bahnhof. Ein Zug bleibt solange vor einem Auswahlsignal stehen, bis ein passender Bahnsteig des Zielbahnhofs und die ganze Strecke bis dorthin frei ist! Man kann das umgehen, indem man für Züge, bei denen dies nicht erwünscht ist, einen Wegpunkt hinter das Auswahlsignal setzt. Damit ist der nächste im Fahrplan angegebene Halt ein Wegpunkt und das Auswahlsignal arbeitet dann für diesen Zug wie ein normales Signal.
-- Auswahl-Ende-Signal Beendet die Suche des Auswahlsignals für dieses Gleis. Bahnsteige hinter diesem Signal werden bei der Suche nach einem freien Bahnsteig nicht berücksichtigt.
-- Langstreckensignale geben die Fahrt erst frei, wenn sie für einen Zug einen freien Weg bis zum nächsten echten Signal (auch durch mehrere Bahnhöfe hinweg) reservieren konnten. Dies ist z.B. für lange eingleisige Abschnitte mit einem oder mehreren Bahnhöfen nötig, um Blockaden zu vermeiden.
-- VorrangsignalDieses Signal reserviert über mehrere Vorrangsignale hinweg so viele Streckenabschnitte wie möglich. Es wird bis zu einer Haltestelle oder einem normalen Signal reserviert. Damit ist es möglich Ausweichgleise zu bauen. Die Hauptstrecke erhält Vorrangsignale, das Ausweichgleis erhält eine Haltestelle oder ein normales Signal. Fährt nun ein langsamer Zug auf die Ausweiche, kann sich der nachfolgende schnelle Zug die Hauptstrecke an der Ausweichstelle vorbei reservieren und der langsame Zug muss warten bis der schnelle Zug vorbei ist.

Im Depot kannst Du Züge zusammenstellen sowie Linien einrichten und die Züge nach der Zuordnung zu Linien starten. Das Depot kann nur auf einem ebenen Gleisende platziert werden. Für E-Loks muss das Gleis zudem elektrifiziert sein. (siehe Depotfenster)

Bahnhöfe sind detailiert in der Hilfe zum Haltestellenfenster beschrieben. Die verschiedenen Haltestellengrafiken können die Haltestelle veranlassen, Passagiere, Post oder Güter umzuschlagen; es macht später jedoch keinen Unterschied mehr, auf welcher Grafik welcher Zug hält, er wird immer be- und entladen, wenn die Station für den Umschlag eingerichtet ist.

Bahnhofseingang und -vorplatz können verwendet werden, wenn der Bahnhof auf erhöhtem Gelände mit Mauer gebaut ist. Sie werden vor die Mauer gebaut. Sie gehören zu den Nebengebäuden.

Nebengebäude siehe Spezialbauwerkzeuge

simutrans-124.5/simutrans/text/de/roadtools.txt000066400000000000000000000116151520675460500217400ustar00rootroot00000000000000Straßenbau

Straßenbauwerkzeuge

Diese Werkzeugleiste dient dem Bau von Straßen, Brücken, Tunneln, Signalisationen, Depots und Haltestellen. Bei der Auswahl eines Wergzeuges verwandelt sich der Cursor in das jeweilige Symbol.

Wege und Straßen können mit unterschiedlichen Höchstgeschwindigkeiten befahren werden. Ist die Timeline aktiviert, werden Strassen mit schnellerer Höchstgeschwindigkeit erst nach und nach verfügbar. Höchstgeschwindigkeit und Baukosten werden in den blauen Tooltips angezeigt. Der Abriss kostet für alle Straßen genausoviel wie deren Bau.

Simutrans benutzt nur leere Felder bzw. bereits vorhandene Straßen. Brücken, Tunnel, Eisenbahnstrecken usw. werden nicht in die Bauaktion mit einbezogen. Allerdings werden automatisch Bahnübergänge gebaut. Am Hang kann nur in Neigungsrichtung gebaut werden.

Beim ersten Platzieren des Strassenbauwerkzeuges in der Weltkarte wird der Anfang der Strecke festgelegt, beim zweiten der Endpunkt. Das Spiel baut zwischen beiden Punkten die kürzestmögliche Strecke. Wenn keine Verbindung gefunden wird, passiert nach dem Anklicken des Endpunktes nichts. Eine Bauaktion lässt sich mittels z gratis rückgängig machen (ohne Baukostenerstattung), solange nichts anderes gebaut wurde.

Straßenbrücken beginnen immer an Straßenenden. Für das Brückenende sucht Simutrans in der geraden Weiterführung des Straßenendes ein vorhandenes Straßenende, welches in die Richtung des Anfangspunktes weist. Wird eine passende Hanglage vorgefunden, so wird die Brücke an sie angelehnt. Die weiter führende Straße kann in dem Fall auch nachträglich gebaut werden, muss aber bis auf die Brücke fortgebaut werden. Um eine Brücke an einem Hang beginnen zu lassen, die Straße bis auf den Hang bauen und dann das Brückenbauwerkzeug benutzen. (In neueren Versionen kann eine Brücke auch an einer senkrechten Wand enden. Dazu sollte vorher dort schon eine Straße gebaut worden sein.)

Straßentunnel können nur gerade und nur in einer Höhenebene gebaut werden. Als Startpunkt des Tunnels muss ein Straßenende in einer Hanglage verbaut sein. Setze das Tunnelbauwerkzeug auf das Straßenstück. Der Tunnel wird komplett gebaut, wenn Simutrans in der gegenüberliegneden Bergflanke eine gerade Hanglage vorfindet. Erhältst Du eine Hinweismeldung, war das gegenüberliegende Hangstück nicht gerade. Eine wichtige Bauhilfe stellt die Nutzung des Gitternetzes dar (in der größten Kartenansicht schaltet die Taste # es ein und aus); mit ihm lässt sich der Durchstoßpunkt besser finden.
Willst du nur ein Tunnelportal bauen, dann kannst du einen Ctrl (Strg)+Mausklick machen. Danach kann du dann in den Untergrundmodus wechseln (mit U oder unter Anzeigeeinstellungen) und mit dem Tunnelwerkzeug weitere Strecken bauen.

Die Verkehrssignalisation wird genutzt, um die Bewegungen der Strassenfahrzeuge zu kontrollieren. Die meisten Schilder sind selbsterklärend. Eine Besonderheit ist das Schild Privatweg, das nur Fahrzeuge bestimmte Spieler passieren lässt.

Der Wegweiser dient zur Verteilung von Fahrzeugen auf gleichartige Halteplätze an der selben Haltestelle, wenn (bei Vorbeifahrt am Wegweiser) der vom Fahrplan festgelegte Halteplatz besetzt ist. Er wird in Fahrtrichtung vor der Haltestelle aufgestellt.
Die Ampeln können an Kreuzungen und Einmündungen aufgestellt werden und regeln den Verkehr. Durch geschicktes Einstellen der Ampelphasen lassen sich grüne Wellen realisieren.
Die Baustellenabschrankung sorgt dafür, dass kein Fahrzeug mehr ein Strassenstück befährt. Die Straße wird ganz oder einseitig gesperrt. Bei der einseitigen Sperrung mehrmals klicken um die Straßenseite zu wechseln.
Das Einfahrt verboten Schild macht eine Strasse zur Einbahnstrasse, der Verkehr rollt dort nur noch in Gegenrichtung.
Das Schild Mindestgeschwindigkeit lässt auf einem Strassenstück nur noch Fahrzeuge fahren, die mehr als 80 km/h erreichen können.

Mit den beiden letzten Signalen zusammen kann man Autobahnen nachbilden.

Im Depot kannst Du Bus-, Post- und Güterkonvois zusammenstellen sowie Linien einrichten und die Konvois nach der Zuordnung von Linien starten. Das Depot kann nur auf einem ebenen Straßenende platziert werden.

Haltestellen sind detailiert in der Hilfe zum Haltestellenfenster beschrieben. Es gibt unterschiedliche Typen von Haltestellen: Bushaltestellen ermöglichen den Passagierverkehr; Frachthöfe den Güterverkehr; Posthaltestellen (wenn vorhanden) den Postverkehr, diese können das Postgebäude ersetzen. Wo welches Fahrzeug letztlich hält, macht keinen Unterschied.

simutrans-124.5/simutrans/text/de/save.txt000066400000000000000000000024701520675460500206670ustar00rootroot00000000000000Speichern

Dialog Speichern

Gib zum Speichern des Spielstandes einen Namen in das Textfeld Dateiname ein und bestätige mit ENTER oder drücke den OK Button.

Achtung: Wählst Du einen bereits bestehenden Eintrag an, wird der vorhandene Spielstand ohne weitere Meldung überschrieben.
Autosave-Spielstände werden bei aktivierten Autosave automatisch überschrieben.

X - Löschen: Achtung: Das X vor jedem Namen löscht den entsprechenden Spielstand. Es wird nicht nachgefragt sondern sofort gelöscht. Das X wird nur angezeigt, wenn in der simuconf.tab der Parameter show_delete_buttons = 1 gesetzt ist.

Schaltfläche mit Namen: Durch einen Klick auf einen Button wird der entsprechende Spielstand geladen. Der Spielstand muss mit dem gestartetem pak (Grafikset) und der Simutrans-Version kompatibel sein.

Information des paks (Grafiksets): Gibt an mit welchem pak (Grafikset) der Spielstand gespeichert wurde.

Datum und Uhrzeit: Gibt an zu welchem Datum und welcher Uhrzeit der Spielstand gespeichert wurde.

Abbrechen: Schließt den Dialog ohne das Spiel zu speichern.

simutrans-124.5/simutrans/text/de/scenario.txt000066400000000000000000000024361520675460500215360ustar00rootroot00000000000000Missionen

Spielziele

Simutrans hat normalerweise kein bestimmtes Ziel. Es gibt jedoch die Möglichkeit, Spiele mit Spielziel zu definieren. Im Spielzieldialog werden Spiele mit einem Spielziel aufgelistet. Nach der Anwahl eines Eintrages wird eine neue Karte mit Spielziel ohne weitere Rückfrage geladen.

Achtung! Das aktuelle Spiel wird verworfen und nicht automatisch gesichert!

Der aktuelle Stand in Prozent kann im Finanzdialog eingesehen werden. Wird das Spielziel am Ende eines Monates erreicht (also 100%) und das Konto ist nicht überzogen, erscheint eine Mitteilung. Das Spiel kann aber trotzdem normal fortgesetzt werden. Folgende Spielziele gibt es:


· Beliefere eine Fabrik (und alle ihre Zulieferer).
· Erreiche den vorgegebenen Kontostand.
· Baue ein Hauptquartier und die vorgegebene Anzahl profitabler Züge.
· Transportiere mondestens die vorgegebene Anzahl von Reisenden pro Monat.

Weitere Spielziele können hinzugefügt werden. Die Dateien befinden sich im Verzeichnis scenario. Dieses befindet sich im Verzeichnis des verwendeten Paksets. Die Szenarios bestehen jeweils aus einem Spielstand und einer Beschreibungsdatei mit dem Spielziel.

simutrans-124.5/simutrans/text/de/schedule.txt000066400000000000000000000112251520675460500215230ustar00rootroot00000000000000Fahrplaneingabe

Fahrplanfenster

Im Fahrplanfenster kannst Du den Fahrplan für den ausgewählten Fahrzeugverband oder den Linienfahrplan für die ausgewählte Linie bearbeiten. Ein Fahrzeugverband, dessen Fahrplan aufgerufen wurde, hält an und wartet, bis das Fahrplanfenster wieder geschlossen ist.

Bedient Linie (nur bei Fahrzeugverbänden ausserhalb des Depots): Das Textfeld zeigt die Linie an, in welcher der Fahrzeugverband fährt. Klicke zum Ändern der Linie ins Feld. Es öffnet sich eine Auswahlliste, in welcher alle verfügbaren Linien gelistet sind. Du kannst auch den Eintrag "keine Linie" wählen und den Fahrzeugverband nach einem individuellen Fahrplan fahren lassen.
Die Schaltfläche Neue Linie erstellt eine neue Linie mit dem aktuellen Fahrplan.

Wartezeit: Die Wartezeit ist nur aktiv, wenn das Fahrzeug auf eine Mindestbeladung in diesem Fahrplanhalt warten soll. Die Wartezeit ist in Teilen pro Monat angegeben, also von ganzer Monat (1/1), halber Monat (1/2), ... 1 Tag (1/32), ... usw. Ist die Wartezeit überschirtten, fährt das Fahrzeug weiter, auch wenn die Mindestbeladung noch nicht erreicht wurde.

Mindestbeladung: Die Mindestbeladung wird nur in Fahrplanhalten abgefragt. Du kannst mittels der Pfeilschalter für jede Haltestelle des Fahrplans einen der verfügbaren Werte zwischen 0% und 100% einstellen. Der Fahrzeugverband wartet an der Haltestelle, bis der eingestellte Wert der Mindestbeladung erreicht ist. Zum Auswählen der Haltestelle klicke auf diese, achte aber darauf das der Schalter "Löschen" nicht eingedrückt ist.

Fahrplanhalte: Die gelisteten Haltestellen und freien Wegpunkte werden vom Konvoi der Reihe nach angefahren. Die Fahrplanhalte sind durchnummeriert. Der eingedrückte Pfeil vor einer Zeile zeigt an, zu welchem Fahrplanhalt der Fahrzeugverband momentan unterwegs ist. Wartet er auf Beladung, zeigt der Pfeil auf die aktuelle Haltestelle. Beim Verlassen des letzten Fahrplanhaltes wird wieder der erste Fahrplanhalt angesteuert.


Fahrplanbearbeitung

Die Schaltfächen haben folgende Bedeutung:
· Hinzufügen: Der neue Fahrplanhalt wird am Ende der Liste angefügt.
· Einfügen: Der neue Fahrplanhalt wird vor dem mit eingedrücktem Pfeil gekennzeichneten Fahrplanhalt eingefügt.
· Löschen: Der per Mausklick angewählte Fahrplanhalt wird aus der Liste gelöscht. Nach löschen eines Fahrplanhaltes wird die Schaltfläche Hinzufügen wider aktiviert.

· Rückfahrkarte: Alle Fahrplanhalte werden bis zum Maximum von 254 Gesamteinträgen in umgekehrter Reihenfolge (ohne 1. und letzten Halt) an den bestehenden Fahrplan angefügt. Bei Verwendung von Richtungsstrecken (einseitige Signale bei Eisen-, Straßen- und Einschienenbahnen) wird die Rückfahrkarte allerdings nicht einsetzbar sein, da der Haltepunkt bei der Rückfahrt nicht direkt angefahren werden kann. Wenn ein Weg gefunden wird, erfolgt dieser dann mit einem großen Umweg.

Bei der Nutzung einer der Schaltflächen "Hinzufügen" oder "Einfügen" ändert sich der Cursor in einen Haltepunktmarker. Klickst Du mit dem Werkzeug auf ein Haltestellenfeld oder auf einen freien Wegpunkt, welcher vom Fahrzeugverband befahren werden kann (je nach Fahrzeugart zBsp. Straßen-, Wasser-, oder Gleisfeld), wird der Punkt als Fahrplanhalt in die Liste der Fahrplanhalte aufgenommen.
· Haltepunkte werden mit dem Namen der Haltestelle, der eingestellten Mindestbeladung und den Koordinaten des Feldes in die Liste eingetragen.
· Freie Wegpunkte werden mit der Bezeichnung "freier Wegpunkt" und den Koordinaten des Feldes in die Liste eingetragen. Ein Halt erfolgt nicht. Die Strecke bis zum nächsten Fahrplanhalt wird eingelesen, ein kurzes Abbremsen des Konvois ist möglich. Vermeide nach Möglichkeit die Platzierung von Wegpunkten in Kurven. Es wird zwar keine Störung verursacht, doch ordentliches Fahrverhalten kann nicht gewährleistet werden.
· Bis zu 254 Fahrplanhalte können je Fahrplan eingerichtet werden.

· Wenn du fertig bist, dann schließe einfach das Fahrplanfenster. Der Fahrzeugverband liest den Fahrplan neu ein und setzt die Reise zum dann als nächsten Halt markierten Punkt fort. Wird eine Linie als ganzes geändert, halten alle ihr zugewiesenen Fahrzeuge an, lesen den neuen Fahrplan ein und fahren weiter. Sind Zwischenhalte eingefügt worden, kann dies dazu führen, dass einige Konvois umkehren, da an derselben Position ihres Fahrplans nach der Änderung ein anderer Fahrplanhalt steht.

simutrans-124.5/simutrans/text/de/script_generator.txt000066400000000000000000000022571520675460500233060ustar00rootroot00000000000000Als Script Werkzeug speichern

Als Script Werkzeug speichern

Hier werden alle Bauwerke, die du zuvor mit der aufgezogenen Fläche ausgewählt hast, als Script abgespeichert. Wenn du das Script später ausführst dann wird versucht alle diese Bauwerke und Objekte zu bauen. Bevor du das Script abspeichern kannst musst du einen Namen vergeben. Alternativ kannst du eines der vorhanden und unten aufgelisteten Scripte anklicken und damit überschreiben.
Mit X davor kannst du nicht mehr benötigte Scripte löschen.

Mit OK wird das neue Script Werkzeug gespeichert.

Das abgespeicherte Werkzeug findest du im persönlichen Verzeichnissen simutrans in dem auch die gespeicherten Spielstände im Unterverzeichnis save zu finden sind. Bei Windows ist das in den "Eigene Dateien" zu finden. Bei Linux ist es (eventual versteckt) als "/home/[user]/.simutrans" zu finden.
Innerhalb diesem persönlichen Verzeichnis ist es in simutrans/addons/[Pakset]/tool/ gespeichert.
Das Script kann mit einem normalen Editor angeschaut und geändert werden. Der Aufbau ist recht einfach zu verstehen.

simutrans-124.5/simutrans/text/de/script_tool.txt000066400000000000000000000005041520675460500222660ustar00rootroot00000000000000Lade Script Tools

Dieses Fenster listet Tools auf, die mit Squirrel geschrieben wurden und Simutrans um Funktionen erweitern.
Die Tools befinden sich in Unterverzeichnissen im Verzeichnis simutrans/[Grafikset]/tool/ oder simutrans/addons/[Grafikset]/tool/.

simutrans-124.5/simutrans/text/de/server.txt000066400000000000000000000023501520675460500212340ustar00rootroot00000000000000Kurzinformation / Netzwerkspiele

Kurzinformation / Netzwerkspiele

Hier wird kompakt über einen Spielstand informiert. Neben einer kleinen Karte wird das Datum, die Kartengröße und die Zahl der Städte, Fabriken, Bewohner, Fahrzeuge usw. angezeigt.

Falls das aktuelle Spiel kein Netzwerkspiel ist, kann man aus einer Liste oben Server auswählen, deren Programmversion und Pakset zu dem gerade Laufendem passt. Ist die Liste leer, gibt es gerade keinen passenden Server. Dann kann man sich durch Alle zeigen alle Server anzeigen lassen.

Nach dem Auswählen eines Server wird dieser angefragt. Läuft der Server gerade nicht, kann dies einen Moment dauern bis eine Fehlermeldung erscheint und der Eintrag rot unterlegt wird. Im Regelfall wird der Server aber mit seinen Kurzinformationen antworten. (s.o.)

Stimmen Programmversion und Pakset überein, dann sind diese Einträge schwarz. Mittels Online spielen tritt man dem Server bei und kann dann mitspielen. Ist dagegen der Pakset-Eintrag rot, dann sind in deinem Pakset Dateien zuviel, unterschiedlich oder fehlen. In diesem Fall kann man sich mit Abweichende Paks die Abweichung anzeigen lassen.

simutrans-124.5/simutrans/text/de/settings.txt000066400000000000000000000034741520675460500215760ustar00rootroot00000000000000Erweiterte Einstellungen

Erweiterte Einstellungen

In den erweiterten Einstellungen können alle Parameter verändert werden, die auch dauerhaft per in der Konfigurationsdatei (simuconf.tab) gespeichert sind. Der Name des jeweiligen Parameter ist identisch mit dem in der Konfigurationsdatei.

Achtung! Wenn die Bedeutung eines Parameters nicht bekannt ist, dann sollte dieser nicht verändert werden. Einige Parameterkombinationen können Simutrans unspielbar machen oder zu Abstürzen führen.

Die Parameter können mittels simuconf.tab auf die Standardwerte zurückgesetzt werden. Mittels Default.sve wird der Stand des letzten gestarteteten Spiels wieder geladen.

Es gibt dabei vier Unterkategorien:

· Allgemeines:
Einstellungen zur Anzeige und zum Zeitablauf. Nützlich dürfte insbesondere autosave sein, welches den Abstand zwischen automatisch gespeicherten Spielständen in Monaten angibt.

· Wirtschaft & Städte:
Hier befinden sich alle Einstellungen für das Wirtschaftsystem und das Wachstum von Städten.

· Verbindungen:
Hier können die Einstellungen für Waren- und Passagierverbindungen, sowei für den Wegebau eingestellt werden.

· Kosten:
Diese Seite enthält alle Kosten, die in Simutrans nicht durch pak-Dateien vorgegeben sind. Dies sind vor allem Instandhaltungs- und Wartungskosten sowie Kosten für Aufschüttungen.

Die komplette Beschreibung aller Einstellungen findet sich in der Konfigurationsdatei simuconf.tab in Verzeichnis config im Programmverzeichnis.

Die meisten Parameteränderungen nur vor dem Start eines Spieles möglich. Einige können auch durch Speichern und erneutes Laden aktiviert werden.

simutrans-124.5/simutrans/text/de/shiptools.txt000066400000000000000000000047721520675460500217640ustar00rootroot00000000000000Häfen und Werften

Häfen und Werften

Diese Werkzeugleiste dient dem Bau von Kanälen, Hafenanlagen und Werften. Bei der Auswahl eines Werkzeuges verwandelt sich der Cursor in das jeweilige Symbol.

Kanäle erlauben es Schiffen, ins Binnenland vorzudringen. Simutrans benutzt nur leere Felder oder bereits vorhandene Kanalstücke. Am Hang kann nur in Neigungsrichtung gebaut werden, Schleusengrafiken werden automatisch erstellt.
Beim ersten Platzieren des Kanalbauwerkzeuges in der Weltkarte wird der Anfang der Strecke festgelegt, beim zweiten der Endpunkt. Das Spiel baut zwischen beiden Punkten die kürzestmögliche Strecke. Wenn keine Verbindung gefunden wird, passiert nach dem Anklicken des Endpunktes nichts. Eine Bauaktion lässt sich mittels z gratis rückgängig machen (ohne Baukostenerstattung), solange nichts anderes gebaut wurde.

Hafenanlagen dienen als Schiffsanlegestellen. An Häfen werden Schiffe be- und entladen.

Hafenbau Binnenhäfen können auf geraden Kanalstücken, Meerhäfen nur an geraden Stränden gebaut werden. Plaziere zur Errichtung eines Meerhafens das Hafenbauwerkzeug auf einer Strandkachel. Zur Errichtung größerer Hafenanlagen ist es möglich, mehrere Häfen direkt aneinander zu bauen. In einigen Simutrans-Versionen stehen Dir mehrteilige Hafenanlagen zur verfügung, welche Du in beliebiger Anordnung platzieren kannst.

Fahrplanhalte Schiffe gehen im Wasser vor Anker. Bei Binnenhäfen ist der Haltepunkt im Kanal, Setze die Fahrplanhalte bei Meerhäfen nicht auf den Hafen, sondern auf Wasserfelder, die an den Hafen direkt angrenzen.

Haltestelle Weiteres zum Aufbau von Haltestellen ist detailiert in der Hilfe zum Haltestellenfenster beschrieben.

Werften dienen zum Bau von Schiffen. Die angebotenen Schiffstypen stehen Dir zum Kauf zur Verfügung. Richte Schifflinien ein, weise sie den georderten Schiffen zu, lasse die Schiffe auslaufen. Die verschiedenen Werfttypen unterscheiden sich lediglich in ihrer Ausrichtung. Platziere sie im freien Wasser, Es ist nicht möglich, eine Werft auf dem Strand oder in einem Kanal zu bauen. Auch weit draußen auf See kannst Du Werften errichten. Berücksichtige bei der Plazierung aber die Kartengrenzen. Deine Spielwelt ist immer von Wasser umgeben. Die Ränder stehen der Nutzung allerdings nicht zur Verfügung. Sie dienen dem gefälligen Kartenabschluss.

simutrans-124.5/simutrans/text/de/signal_info.txt000066400000000000000000000016151520675460500222210ustar00rootroot00000000000000Signal Infofenster

Signal Infofenster

Informationen zum Signal.
· Name des Signals
· Beschreibung Nicht immer vorhanden
· Besitzer Welchem Spieler gehört das Signal
· Richtungen Gültigkeit in welche Richtung gültig, für andere Richtung ist die Strecke gesperrt.
- 1 = Süd auch diagonal nach Westen
- 2 = West auch diagonal nach Norden
- 3 = diagonal nach norden und Süden
- 4 = Nord auch diagonal nach Norden
- 5 = Nord und Süd
- 8 = Ost auch diagonal nach Osten oder Süden
- 9 = diagonal nach Osten und Westen
- 10 =Ost und West
- 12 = diagonal nach Norden und Süden
· Signal entfernen diese Signal wird entfernt · Gemalt von Name vom Ersteller der Grafik

simutrans-124.5/simutrans/text/de/signal_spacing.txt000066400000000000000000000024201520675460500227050ustar00rootroot00000000000000Signalabstand festlegen

Hinweise zur Benutzung:


Strg+Mausklick auf ein Signalwerkzeug öffnet dieses Fenster.

Ähnlich wie beim Bauen von Schienen oder Straßen wird mit Klick-und-Ziehen auf der Strecke eine Vorschau der Signale gezeigt, und gebaut, sobald die Maustaste losgelassen wird.

Die Durchfahrtsrichtung der Signale wird durch die Richtung vorgegeben, in die die Maus gezogen wurde. Stehen schon Signale in entgegengesetzter Richtung, dann wird gar kein Signal gebaut.


Einstellungen in diesem Fenster:

Signalabstand: Abstand in Kacheln, der zwischen den Signalen eingehalten wird. Diagonalen zählen als eine halbe Kachel. Dieser Wert wird auf dem Signal-Icon gezeigt, falls das entsprechende Werkzeug aktiv ist.

Signale an Zwischenstellen entfernen: Falls aktiviert, werden andere Signale, die auf dem Weg stehen, entfernt.

Andere Signale ersetzen: Falls aktiviert, werden andere Signale entfernt, die auf Stellen stehen, wo mit diesem Werkzeug ein Signal gesetzt werden würde.

Diese Einstellungen können für jedes Signal und Verkehrszeichen unabhängig gesetzt werden.

simutrans-124.5/simutrans/text/de/simutrans.txt000066400000000000000000000071531520675460500217610ustar00rootroot00000000000000Über Simutrans

Simutrans

Versetze Dich in die Situation eines jungen Transportunternehmers. Du startest mit einem geringen, jedoch ausreichenden Grundkapital. Wähle eine der 10'000 verfügbaren Simutrans-Karten oder lade eine Reliefkarte. Die gleiche Ausgangskarte wird sich bei mehrmaligem Laden immer wieder mit einem anderen Szenario generieren. Als Basis der Generierungen dienen Deine getroffenen Grundeinstellungen.

Beurteile die angetroffene Situation. Die Entwicklung der kleinen Welt liegt nun in Deiner Hand. Du kannst etwa den einen oder den anderen Industriezweig erschließen, also Fertigungsbetriebe mit den Produkten der vertragsverbundenen Rohstoffproduzenten beliefern. Baue die benötigten Transportverbindungen. Dir stehen Schiene, Straße und Schifffahrt als Verkehrsträger zur Verfügung. Natürlich können Passagiere und Güter zwischen Verkehrsträgern umsteigen bzw. umgeladen werden.

Der Vorteil der Strategie des anfänglichen Gütertransportes besteht darin, mit oft wenig Aufwand nach kurzer Zeit die Mittel für kostenträchtigere Vorhaben zur Verfügung zu haben. Der Nachteil soll nicht verschwiegen werden: Die Städte zeigen derzeit keinerlei Wachstum.

Eine andere Strategie besteht darin, vom Spielbeginn an das Wachstum der Städte anzuregen. Werden den Stadtbewohnern Beförderungsmöglichkeiten zur Verfügung gestellt, können sie nun ihre Reiselust ausleben. Doch jeder potentielle Reisende interessiert sich unabhängig von Deinen Linienangeboten für ein ganz bestimmtes Reiseziel. Versuche also, so viele Städte wie möglich zu erschließen, sorge für Verbindungen der Städte untereinander. Achte auch auf die Erschließung der öffentlichen Einrichtungen wie Vergnügungseinrichtungen, Ruinen und sagenumwobene Königschlösser. Viele Touristen werden zu den Sehenswürdigkeiten strömen, ohne die Mären und die Legenden, welche um die oft düsteren Gemäuer ranken, je zerstören zu können. Transportiere auch die anfallende Post der Einwohner.

Guter Service spricht sich rum. Je mehr Reisende mit deinem Service zufrieden waren und je mehr Post Du befördern konntest, desto zügiger werden die Städte wachsen. Neuer Wohnraum wird gebaut. Komplette Produktionsketten werden aus dem Boden schießen, als bestände die ganze Welt nur aus Maurern und Polieren. Die Betriebe handeln untereinander Abnahmeverträge aus. Für den expansionsbereiten Transportunternehmer und erfahrenen Logisten entwickeln sich ungeahnte Möglichkeiten.

Eine gute Wegeplanung wird Dich bei der optimalen Nutzung der Straßen- und Schienennetze unterstützen. Lass nicht mehr Fahrzeuge in einer Linie fahren, als Bedarf besteht. Knauser aber auch nicht. Vermeide nach Kräften Angebotsengpässe im Reiseverkehr. Ein zur Hälfte ausgelastetes Fahrzeug fährt immer noch profitabel.

Es besteht keine zeitliche Beschränkung. Spiele, bis Deine gesteckten Ziele auch wirklich erreicht sind. Du kannst mit bis zu 6 KI-Spielern im Kampf um Märkte und Transportaufträge wetteifern. Schnappe anderen Spielern Passagiere und Güter vor der Nase weg. Vielleicht magst Du auch ganz auf KI-Spieler verzichten? Die Entscheidungen liegen bei Dir. Doch verausgab Dich vor allem in der Frühphase Deines aufstrebenden Unternehmens nicht. Auch Transportunternehmern droht der Konkurs, wenn sie Schulden nicht fristgerecht tilgen können.

Das Simutrans-Team und die nach Kräften mitwirkende Community wünschen Dir viel Langzeitspaß und grauzellen-trainierende Knobelkuren bei der Erkundung und der Erschließung völlig individueller Welten.

simutrans-124.5/simutrans/text/de/slopetools.txt000066400000000000000000000026011520675460500221300ustar00rootroot00000000000000Aufschüttungen und Böschungen

Gelände verändern

Wenn eine Bahnlinie/Straße durch unwegsames Gelände geführt werden soll, empfehlen sich die Erdbewegungswerkzeuge. Sie können nur eingesetzt werden, wenn alle betroffenen Felder unbebaut sind.

Gelände anheben/absenken Der Cursor verwandelt sich in einen nach oben/unten zeigenden Pfeil. Mit dem Werkzeug wird das Gelände am Gitterpunkt unter dem Cursor angehoben/abgesenkt. Das Gitternetz (nur in der größten Kartenansicht) lässt sich mittels der Taste # ein- und ausschalten.

Rampen legenDie nächsten vier Werkzeuge legen Rampen in südlicher, nördlicher, westlicher und östlicher Richtung an. Mit einem fünften Tool werden Hangfelder eingeebnet. Über so bearbeitetes Gelände können dann normal Wege gebaut werden.

Beim Einsatz der Geländewerkzeuge können senkrechte Böschungen entstehen. Über diese Klippen können keine Wege gebaut werden, sie können aber Endpunkte Brücken sein.

Niveau absenken/anheben Senkt oder hebt das Grundniveau eines Feldes. Mit diesem Werkzeug kann man im Untergrundmodus Rampen erzeugen.
Achtung: Auf dem Kartenboden erzeugt Absenken auf das neue Niveau des Meeresspiegel einen Kanal.

Renaturierung Stellt den ursprünglichen Zustand wieder her.

simutrans-124.5/simutrans/text/de/sound.txt000066400000000000000000000023131520675460500210550ustar00rootroot00000000000000Sound- und Musikeinstellungen

Sound- & Musikeinstellungen

Zum Abspielen von Sounds und Musik muss die SDL-Version von Simutrans installiert sein. Unter Windows wird die sdl.dll benötigt. Sounds (WAV-Format) und Musikstücke (MIDI-Format) werden im Verzeichnis simutrans/sound abgelegt.
Wird Simutrans mit -nosound oder -nomidi gestartet, dann sind Sound bzw. Musik nicht benutztbar.
ACHTUNG: Insbesondere Musik funktioniert nur auf bestimmten Systemen.

Durch Verschieben der Regler nach links oder rechts kannst Du die Lautstärke der Musik und der Geräusche festlegen. Je weiter rechts die Regler stehen, desto lauter erfolgt die Ausgabe.

Klanglautstärke: Lautstärke der Klänge im Spiel. Die Klänge können, mit dem Knopf Klänge aus, ganz ausgeschalten werden.
Musiklautstärke: Lautstärke der Musik im Spiel. Die Musik kann, mit dem Knopf Musik aus, ganz ausgeschlaten werden.

Das Musikstück, das gespielt werden soll, kannst Du mit den Pfeilknöpfen auswählen, sofern Musik verfügbar ist.

Wenn die Zufallsauswahl aktiviert ist werden die Musikstücke in zufälliger Reihenfolge abgespielt.

simutrans-124.5/simutrans/text/de/special.txt000066400000000000000000000162071520675460500213540ustar00rootroot00000000000000Spezialbauwerkzeuge

Spezialbauwerkzeuge

In dieser Werkzeugleiste sind Bauwerkzeuge zusammengefasst, die nicht direkt mit den Transportnetzen zu tun haben. Bei der Auswahl eines Wergzeuges verwandelt sich der Cursor in das jeweilige Symbol.

Nebengebäude Für die Gestaltung der Haltestellen stehen Dir verschiedene Nebengebäude zur Verfügung. Platziere sie entsprechend Deinen kreativen Vorstellungen direkt neben beliebige Haltestellenobjekte. Mit ihnen kannst Du auch flächige Haltestellenverbünde erreichen. Sie erweitern das Einzugsgebiet und das Fassungsvermögen der Haltestelle. Die meisten Nebengebäude bringen Haltestellen auch dazu, Passagiere, Post oder Güter umzuschlagen, besonders seien die Postgebäude erwähnt. Einige Nebengebäude sind auch in den anderen Bauwerkzeugen zu finden, unterscheiden sich aber in ihrer Funktion nicht von denen in diesem Menü.

Spielerwechsel Bei der Betätigung der Schaltfläche P+ oder der Tastenkombination Shift+P wechselst Du zunächst zum Spieler "öffentliche Hand". Das Menü Karteneditor wird geöffnet. Beim Weiterschalten gelangst Du nacheinander zu den KI-Spielern, danach wieder zum menschlichen Spieler. Hattest Du das Menü des Karteneditors geöffnet gelassen, kannst Du es nun auch als Spieler nutzen. Du erhältst weitreichende Gestaltungsmöglichkeiten des Spiels. Kreative Aspekte werden Dich mit etwas Übung sehr interessante Aufgabenstellungen finden und verwirklichen lassen.

Stadtgründungswerkzeug Wo immer auf der Karte Du das Werkzeug platzierst, wird eine neue Stadt entstehen. Doch Vorsicht, von Deinem Kassenbestand werden ohne Vorwarnung 5.000.000 Credits abgezogen.

Aufforstwerkzeug Während Rodungsarbeiten mit Unkosten verbunden sind, stellt Dir Simutrans Setzlinge für Aufforstungen gratis zur Verfügung. Die Landschaftsverschönerung kann eine reizvolle Aufgabe sein. Ökologische Aspekte sind derzeit aber nicht im Spiel implementiert.

Stromnetz Einige der Produktionsstätten könnten ruhig mehr produzieren? Versorge sie mit Strom, und sie werden es tun. Platziere je ein Umspannwerk direkt angrenzend an ein Kraftwerk und dem Betrieb, den Du versorgen möchtest. An jedes Kraftwerk und jeden Betrieb kann nur ein Umspannwerk angeschloßene werden. Ein Kraftwerk kann aber mehrere Betriebe mit Strom versorgen. Durch die Verbindung der beiden Umspannwerke durch eine Überlandstromleitung wird der Betrieb mit Strom versorgt. Der Stromtransport stellt auch eine interessante Einnahmequelle dar. Solange Strom fließt, erhöht sich die Produktion jedes versorgten Betriebs. Ob eine Fabrik genug Strom erhält, sieht man im Spiel an einem roten Blitz auf dem Umspannwerk. Je nach Grafikset können Umspannwerke auch im Untergrund gebaut werden. Dazu muß man in den Untergrundmodus (mit U oder unter Anzeigeeinstellungen) wechseln und das Umspannwerk-Werkzueg ausgewählt haben. Dann erscheint unter den Betrieben eine rote Grundfläche auf der das Umspannwerk plaziert werden muß.



Überlandstromleitungen Baukosten werden in den blauen Tooltips angezeigt. Der Abriss kostet für Überlandstromleitungen genausoviel wie deren Bau.

Simutrans benutzt nur leere Felder bzw. bereits vorhandene Überlandstromleitungen. Brücken, Tunnel, Eisenbahnstrecken usw. werden nicht in die Bauaktion mit einbezogen. Allerdings werden automatisch Bahn- und Straßenübergänge gebaut. Am Hang kann nur in Neigungsrichtung gebaut werden.

Beim ersten Platzieren des Stromleitungswerkzeuges in der Weltkarte wird der Anfang der Strecke festgelegt, beim zweiten der Endpunkt. Das Spiel baut zwischen beiden Punkten die kürzestmögliche Strecke. Wenn keine Verbindung gefunden wird, passiert nach dem Anklicken des Endpunktes nichts. Eine Bauaktion lässt sich mittels z gratis rückgängig machen (ohne Baukostenerstattung), solange nichts anderes gebaut wurde.

Strombrücken beginnen immer an einem Stromleitungsenden. Für das Strombrückenende sucht Simutrans in der geraden Weiterführung des Stromleitungsendes ein vorhandenes Stromleitungsende, welches in die Richtung des Anfangspunktes weist. Wird eine passende Hanglage vorgefunden, so wird die Brücke an sie angelehnt. Die weiter führende Stromleitung kann in dem Fall auch nachträglich gebaut werden, muss aber bis auf die Brücke fortgebaut werden. Um eine Strombrücke an einem Hang beginnen zu lassen, die Stromleitung bis auf den Hang bauen und dann das Strombrückenbauwerkzeug benutzen. (In neueren Versionen kann eine Strombrücke auch an einer senkrechten Wand enden. Dazu sollte vorher dort schon eine Stromleitung gebaut worden sein.)

Stromtunnel / Untergrundstromleitungen können nur gerade und nur in einer Höhenebene gebaut werden. Als Startpunkt des Stromtunnels muss ein Stromleitungsende in einer Hanglage verbaut sein. Setze das Tunnelbauwerkzeug auf das Stromleitungsstück. Der Stromtunnel wird komplett gebaut, wenn Simutrans in der gegenüberliegneden Bergflanke eine gerade Hanglage vorfindet. Erhältst Du eine Hinweismeldung, war das gegenüberliegende Hangstück nicht gerade. Eine wichtige Bauhilfe stellt die Nutzung des Gitternetzes dar (in der größten Kartenansicht schaltet die Taste # es ein und aus); mit ihm lässt sich der Durchstoßpunkt besser finden.
Willst du nur ein Stromtunnelportal bauen, dann kannst du einen Ctrl (Strg)+Mausklick machen. Danach kann du dann in den Untergrundmodus wechseln (mit U oder unter Anzeigeeinstellungen) und mit dem Stromtunnelwerkzeug weitere Strecken bauen.

Marker Den Marker kannst Du für die Einrichtung von Gedächtnisstützen nutzen. Plaziere ihn auf einer beliebigen Kachel. Editiere das sich öffnende Textfeld, bestätige dann mit "OK". Bei wiederholter Plazierung des Markers auf dem gleichen Feld kannst Du dich auch für die Entfernung des Markers entscheiden.
Ein markiertes Feld geht in das Spielereigentum über. Es bleibt Dein Eigentum, auch wenn Du den Marker wieder entfernst. So ist es möglich, Felder vor der Bebauung zu schützen.

Haltepunkt verlegen: Mit diesem Tool können Sie einen Halt oder Wegpunkt in den Fahrplänen aller Linien und Einzelfahrzeugen, die diesen verwenden, verschieben. Der Haltepunkt ist die rot markierte Kachel, wenn der Fahrplan geöffnet ist. Der Fahrplan des Fahrzeugs oder der Linie darf jedoch nicht geöffnet sein, sonst wird diese nicht mit verschoben. Es kann damit die Haltestelle oder auch die geplante Kachel innerhalb einer Haltestelle geändert werden. (z. B. um alle Linien von einem Bahnsteig zu einem anderen Bahnsteig eines Bahnhofs zu verschieben). Klicken Sie nach Auswahl des Werkzeugs auf den Haltepunkt oder Bahnsteig, den Sie in den Linienplänen ersetzen möchten, und dann auf den Haltepunkt oder Teil einer Haltestelle, den Sie in Zukunft verwenden möchten. Die Fahrpläne werden sofort geändert, jedoch nicht die Fahrziele der Fahrzeuge die bereits unterwegs sind.

simutrans-124.5/simutrans/text/de/station.txt000066400000000000000000000224141520675460500214120ustar00rootroot00000000000000Haltestellen

Haltestellenfenster

Das Haltestellenfenster wird angezeigt, wenn Du mit dem Abfragewerkzeug (Lupe) auf ein Feld mit einer Haltestelle oder einem Erweiterungsgebäude klickst. Haltestellen sind Ausgangs-, Ziel- oder Umschlagplätze für Passagiere, Post und Güter. Hält ein Fahrzeug an einer Haltestelle, dann werden automatisch Passagiere, Post und Güter aus, um- bzw. eingladen. Haltestellen können aus einem oder mehreren Elementen wie Bahnhöfen, Bus- oder Tramhalten, Häfen, Flughäfen sowie Erweiterungsgebäuden bestehen.

Baust Du eine Bushaltestelle auf einer Straße, Hafen im Kanal oder an der Küste, Bahnhofshalle auf der Schiene oder ein Terminal für ein Flugzeug, so entsteht dort automatisch eine neue Haltestelle. Baust Du ein Element auf eine Kachel unmittelbar neben oder über eine bestehende Haltestelle (auch schräg über Eck) wird die bereits bestehende Haltestelle erweitert. Auf diese Weise können U-Bahn-Station, ebenerdiger Bahnhof, Hochbahn, Anlegestellen für Schiffe oder auch Flughafenterminals in einer einzigen Haltestelle vereinigt werden, an der Passagiere von einem Verkehrsmittel ins andere umsteigen können. Der Weg muss im Bereich der Haltestelle gerade, kreuzungsfrei und eben sein, darf aber auch auf Brücken, Hochstrecken oder in Tunnels liegen. Einige Elemente können zusätzliche Einschränkungen haben und zum Beispiel nur auf Wegenden gebaut werden.

An einer Haltestelle dürfen nur Fahrzeuge halten, die den gleichen Besitzer wie die Haltestelle haben. Eine Ausnahme sind Haltestellen, die der öffentlichen Hand gehören. Hier können alle Spieler halten und so gegenseitig Passagiere, Post und Güter austauschen.

Auf der rechten Seite des Haltestellenfensters siehst Du ein Bild der Haltestelle. Klicke darauf, um das Hauptfenster auf die Haltestelle zu zentrieren.

Jede Haltestelle hat drei wesentliche Eigenschaften: Kapazität, Einzugsgebiet und Akzeptanz. Neben den Halten gibt es Erweiterungsgebäude die die Kapazität der Haltestelle erhöhen, das Einzugsgebiet vergrößern, und/oder den Umschlag weiterer Kategorien (Passagiere, Post, Güter) ermöglichen.

Haltestellen haben ein begrenztes Einzugsgebiet. Du kannst es mittels v oder unter Anzeigeeinstellungen ein- oder ausblenden. Passagiere, Post und Güter werden nur für Ziele innerhalb dieses Gebietes angenommen. Sehenswürdigkeiten müssen über ihre ganze Baufläche von Haltestellen erschlossen sein, sonst wird ein Teil der Passagiere, die einen Ausflug dorthin planen, unzufrieden. (s.u.)

Der Name einer Haltestelle kann in dem Feld oben direkt geändert werden. Jede Änderung erscheint sofort auf der Karte. Unter dem Namensfeld befindet sich eine Reihe von Icons. Links ist der Statusbalken (s. u.). Die Icons daneben zeigen Dir, welche Kategorien (Passagiere, Post, Güter) die Station akzeptiert. Damit eine dieser drei Kategorien akzeptiert wird, muss mindestens ein Element der Haltestelle diese akzeptieren. Welche Akzeptanz ein Haltestellenelement ermöglicht, kannst Du an den Symbolen auf den Icons in den entsprechenden Baumenüs erkennen. Die folgenden Symbole zeigen an, welche Verkehrsmittel (Straßenfahrzeug, Zug, Schiff, etc.) die Haltestelle anfahren.

Rechts davon steht die Lagerkapazität für Passagiere, Post und Fracht. Sind an einer Station mehr Einheiten eines Gutes vorhanden als die Kapazität, werden keine neuen mehr angenommen, die Haltestelle ist überfüllt. Die Kapazität steigt mit jedem Element, das an die Haltestelle angebaut wird. Welche Kapazität ein bestimmtes Element hat, wird in den Baumenus angezeigt. Die Auslastung der Haltestelle zeigt der Statusbalken ganz links an (derselbe, der auch auf der Karte über dem Namen steht). Er hat folgende Bedeutung:
- Grün: Bei der Haltestelle ist alles in Ordnung.
- Gelb: Die Haltestelle ist nicht erschlossen (kein Fahrzeug kam vorbei, nichts wartet).
- Orange: Die Haltestelle ist mäßig überfüllt.
- Rot: Die Haltestelle ist völlig überfüllt (mehr als 200 Einheiten über Limit) oder mehr als 200 Passagiere/Post haben ihre Reise wegen Überfüllung nicht angetreten oder eine Fabrik konnte ihre Waren nicht loswerden und musste deswegen die Produktion einstellen. In der Mailbox kannst Du einrichten, ob und wie Simutrans Dich jeweils am Monatsersten über solche Haltestellen informieren soll.

Akzeptiert eine Haltestelle Passagiere oder Post, stehen unterhalb der Symbole weitere Informationen. Das zufriedene Gesicht () gibt an, wieviel Passagiere/Post von dieser Haltestelle aus ihre geplante Reise angetreten haben. Das unzufriedenen Gesicht () zählt die Passagiere/Post, die wegen Überfüllung ihre Reisepläne fallengelassen haben. ohne Route zählt die, die keine Verbindung zu ihrem Ziel gefunden und die Reise somit nicht angetreten haben. Es gibt mehrere mögliche Ursachen:
- Das Reiseziel liegt nicht im Einzugsbereich einer Haltestelle, oder diese nimmt keine Passagiere/Post an;
- Es besteht keine Verbindung zum Ziel;
- Es wären mehr Umstiege notwendig, um das Ziel zu erreichen, als sich die Passagiere gefallen lassen. Die maximale Zahl der Umstiege, die für die Passagiere (und auch Güter) zumutbar ist, ist in der Variablen max_transfers in der Datei simutrans/config/simuconf.tab festgelegt.

Je mehr zufriedene Passagiere pro Monat befördert wurden, umso schneller werden die Städte auf der Karte wachsen. Für weitere Information siehe Stadtinformation.

Im unteren Bereich des Haltestellenfensters befindet sich die Passagier- und Warenliste. Hier sind sämtliche Passagiere und Güter aufgelistet, die an der Haltestelle auf den Weitertransport warten.
Zuerst wird die Gesammtmenge der Ware angezeigt, z.B.

·83 Passagiere warten

Diese wird dann weiter aufgeschlüsselt nach den einzelnen Zielen. Ist zum Erreichen des Ziels einer oder mehrere Umstiege notwendig, wird die nächste Umsteigehaltestelle angehängt.

··27 Passagiere > Berlin Mitte H über Berlin Hauptbahnhof
··11 Passagiere > Stuttgart Bahnhof

Es gibt verschiedene Arten der Auflistung und Sortierung. Du kannst sie über die linke der drei Schaltflächen oberhalb der Liste ändern. Die Einstellung wird gespeichert und für alle anderen Haltestellenfenster übernommen.
Zielort:
Die Einträge werden alphabetisch nach Zielorten sortiert. Menge:
Die Listensortierung erfolgt nach der Menge der Passagiere/Güterarten, die das gleiche Ziel haben.
via:
Die Liste wird nach den ersten Transferhaltestellen alphabetisch sortiert.
via Menge:
Die Listensortierung erfolgt nach der ersten Transferhaltestelle. Die Liste wird nach der Menge sortiert.
via Spieler:
Gruppeirt wird nach dem Besitzer des nächsten Umstieges oder Ziel. Die Liste wird nach der Menge sortiert.
Verbindung:
Gruppiert wird nach der Linie oder dem Fahrzeug des nächsten Umstieges oder Ziel. Die Liste wird nach der Menge sortiert.

Die Schaltfläche Details öffnet das Fenster Haltestellendetails. Dort findest Du zusätzliche Informationen über die Haltestelle.

Über die Schaltfläche Statistik kannst Du einen grafischen Statistikbereich ein- und ausblenden. Die Statistik zeigt die Entwicklung einiger Werte über die letzten 12 Monate. Über die Schaltflächen unter der Statistik lassen sich die entsprechenden Werte ein- und ausblenden.
Zufrieden / Unzufrieden / Keine Route:
Alle zufriedenen / unzufriedenen Passagiere, bzw. Passagiere, die keine Route gefunden haben.
Angekommen / Abgefahren:
Alle angekommenen / abgefahrenen Passagiere und Güter, unabhängig davon, ob die Haltestelle das Endziel oder Umsteigepunkt war.
Wartend:
Die Gesammtzahl aller wartenden Passagiere und Güter.
Fahrzeuge:
Die Zahl aller Fahrzeuge, welche die Haltestelle angefahren haben.



Über einer Haltestelle in der Welt befinden sich mehrere Anzeigeelemente. Unter Anzeigeeinstellungen oder durch mehrmalige Betätigung von ! kannst Du die einzelnen Elemente ein- oder ausblenden.

Der Name der Haltestelle steht in einem Kasten in der Farbe des Spielers, dem sie gehört. Er kann im Haltestellenfenster geändert werden.

Über dem Haltestellennamen befindet sich ein waagrechter Statusbalken. Hier kannst Du den Status der Haltestelle erkennen (s.o).

Über dem Statusbalken befinden sich senkrechte Farbsäulen. Jede Säule steht für eine bestimmte Ware. Die Farben der Säulen entsprechen jenen der Farbquadrate in der Warenliste. Je höher die Säule, desto mehr Einheiten dieser Ware warten auf den Weitertransport. Befindet sich ein kleiner weißer Pfeil über der Säule, warten mehr Einheiten des Gutes als die Haltestelle aufnehmen kann. Es werden nur Säulen von Waren (bzw. Warengruppen) angezeigt, die auch tatsächlich umgeschlagen werden.

simutrans-124.5/simutrans/text/de/station_details.txt000066400000000000000000000023701520675460500231160ustar00rootroot00000000000000Haltestellendetails

Haltestellen Detailinformation

Angeschlossene Fabriken: Alle im Einzugsgebiet der Haltestelle befindlichen Fabriken werden alphabetisch gelistet. Die Einzugsgebiete kannst Du mittels der Taste v anzeigen lassen.

Angenommene Waren: Alle Warenarten, welche von an die Haltestelle angeschlossenen Fabriken benötigt werden, sind alphabetisch gelistet.

Linien, die diese Haltestelle anfahren: Alle Linien, welche die Haltestelle anfahren, sind alphanumerisch gelistet. Ein Klick auf den Pfeil öffnet die Linienverwaltung.

Einzelfahrzeuge, die hier halten: Alle Fahrzeuge, die keiner Linie zugeordnet sind, aber die Haltestelle anfahren, sind alphanumerisch gelistet. Ein Klick auf den Pfeil öffnet das Fahrzeugfenster.

Verkehrsverbindungen (direkt): Alle Haltestellen sind gelistet, welche direkt über Linien von der Haltestelle aus angefahren werden. Dem Namen der Haltestelle folgt jeweils in einer weiteren Zeile nach einem Bindestrich die Beförderungsart. Über den Pfeil vor dem Haltestellennamen kannst du direkt zu dieser Haltestelle springen.

simutrans-124.5/simutrans/text/de/trafficlight_info.txt000066400000000000000000000007071520675460500234130ustar00rootroot00000000000000Ampelphasen einstellen

Ampelphasen einstellen

Mit diesem Dialog werden die Ampelphasen eingestellt. Dabei gibt es zwei Richtungen: Nord-Süd (erstes Feld) und Ost-West (zweites Feld). Da alle Ampel auf der Karte synchron schalten, kann man im dritten Feld eine Verschiebung angeben, um eine grüne Welle zu realisieren.

Ampeln gibt es in den Straßenbauwerkzeugen.

simutrans-124.5/simutrans/text/de/tramtools.txt000066400000000000000000000024311520675460500217520ustar00rootroot00000000000000Straßenbahnbau

Straßenbahnbauwerkzeuge

Diese Werkzeugleiste dient dem Bau von Straßenbahngleisen, Fahrleitungen, Depots und Haltestellen. Bei der Auswahl eines Werkzeuges verwandelt sich der Cursor in das jeweilige Symbol.

Straßenbahnschienen: Der Bau von Schienennetzen ist in der Hilfe zu den Eisenbahnbauwerkzeugen beschrieben. Straßenbahngleise können auch auf Straßen und durch Straßentunnel und über Straßenbrücken gebaut werden. (Falls das Verlegen durch Tunnel/über Brücken nicht klappt, hilft evt. beim Bauen die Ctrl-Taste gedrückt zu halten.) Die Benutzung von Eisenbahngleisen neben Straßen ist möglich.

Fahrleitungen und Signale: Entsprechen denen in den Eisenbahnbauwerkzeugen.

Im Depot kannst Du Straßenbahnen zusammenstellen sowie Linien einrichten und die Fahrzeuge nach der Zuordnung von Linien starten. Das Depot kann nur auf einem ebenen Gleisende platziert werden.

Straßenbahnhaltestellen: Der Bau von Haltestellen ist in der Hilfe zum Haltestellenfenster beschrieben. Auf Straßen ist die Bushaltestelle zu verwenden, neben Straßen benutzt Du die Bahnhöfe.

simutrans-124.5/simutrans/text/de/underground.txt000066400000000000000000000051061520675460500222640ustar00rootroot00000000000000Bauen im Untergrund

Bauen im Untergrund

Es gibt 2 mögliche Untergrundansichten.
vollständige Untergrundansicht
Mit U schaltest Du in die vollständige Untergrundansicht. In dieser Ansicht werden alle Oberflächenobjekte ausgeblendet.
Untergrundansicht als Ebene
Mit Strg + U schaltest Du in die Untergrundansicht mit Ebenen. In dieser Ansicht werden alle Objekte ausgeblendet, die sich über der ausgewählten Ebene befinden. Das sind alle Oberflächenobjekte und auch alle Objekte im Untergrund, die sich oberhalb der ausgewählten Ebene befinden.

Der Ebenenwechsel erfolgt mit + (höher) und - (tiefer) oder über die Anzeigeeinstellungen.

Mauszeiger (Cursor)
volle Untergrundansicht
Bei der vollen Untergrundansicht wird das Raster der Oberfläche angezeigt. Bei unbebauten Feldern possitioniert sich der Mauszeiger an diesem Raster. Bei bebauten Feldern possitionirt sich der Mauszeiger auf die bebauten Felder.
Da das Possitionieren des Mauszeigers über das Oberflächenraster recht schwierig ist, sollte beim bauen im Untergrund die Ebenenansicht verwendet werden.

Untergrundansicht als Ebene
Bei der Ebenenansicht wird ein Raster auf der aktiven Ebene eingeblendet. Der Mauszeiger possitioniert sich an diesem Raster. Da sich der Mauszeiger immer in der aktiven Ebene befindet, ist das Bauen sehr viel leichter.

Streckenbau

allgemein


Im allgemeinen werden Strecken im Untergrund mit den Bauwerkzeugen für Tunnel gebaut. Um im Untergrund bauen zu können, muss ein Tunnel oder ein Tunneleingang (Taste Strg + Klick mit Tunnelwerkzeug auf Hangfeld) gebaut werden.

Ebenenwechsel
Um die Ebene im Untergrund zu wechseln, sind die Werkzeuge Niveau absenken/anheben aus dem Menü Geländewerkzeuge zu verwenden. Um die Ebene zu wechseln, einfach mit dem entsprechenden Werkzeug auf das Streckenende klicken.

Sonderfall Straßenbahn


Straßenbahnschienen werden auch im Untergrund mit den normalen Schienenwerkzeugen auf die Straße gebaut.

Stationen und Nebengebäude
Nur eigene Stationen können übereinander gebaut werden.

Nebengebäude können nicht im Untergrund gebaut werden.

Signale
Können verwendet werden wie an der Oberfläche.

simutrans-124.5/simutrans/text/de/vehiclelist.txt000066400000000000000000000013641520675460500222450ustar00rootroot00000000000000Liste aller Fahrzeuge

Auflistung aller im Spiel vorhanden Fahrzeugtypen

Standardmäßig werden alle derzeit aktuellen und zukünftig noch kommenden Fahrzeuge gezeigt. Das zeigen der Zukünftigen ist auch der eigentliche Sinn dieser Liste. In den Depots sind die kommenden Fahrzeuge nicht einsehbar. Der Spieler kann damit besser für die Zukunft planen.

Auch zukünftige zeigen Durch Abwahl, kann die Liste eingegrenzt werden, auf die derzeit kaufbaren Fahrzeuge.
Auch veraltete zeigen erweitert die Liste um die bereits veralteten Fahrzeuge. Diese Funktion ist gleich der in den Depots.
Dahinter bei Alle kann auf die Warenart sowie Passagiere oder Post eingeschränkt werden.

simutrans-124.5/simutrans/text/de/window.txt000066400000000000000000000104071520675460500212370ustar00rootroot00000000000000Spielsteuerung

Spielsteuerung

Allgemeines
Simutrans lässt sich über die Maus und teilweise auch über die Tastatur steuern; die Tastaturbelegung ist über den gleichnamigen Menüpunkt im Hilfemenü (Taste F1) verfügbar und erscheint auch jedesmal, wenn eine Taste ohne Funktion gedrückt wird (z. B. 5).
Die Tasten sind in der Hilfe weiß hervorgehoben. Achtung es wird zwischen Groß- und Kleinschreibung unterschieden.

Auswahlen und Platzierungen
Die Nutzung der Schaltflächen und Objektauswahlen sowie Objektplazierungen im Spiel erfolgen über Klicken mit der linken Maustaste. In der Hilfe werden die Begriffe "auswählen" und "platzieren" bzw. Begriffe mit vergleichbarem Sinn benutzt.

Bewegen der Spielkarte
· Halte die rechte Maustaste gedrückt und verschiebe die Karte in beliebiger Richtung zu Deiner Wunschposition. In der Grundeinstellung verschiebt sich die Karte entgegen der Mausbewegung. Du kannst das Verhalten über die Einstellungen ändern.
· Die Karte lässt sich auch über die Tastatur (Ziffern auf dem Nummernblock) bewegen.
· Du hast auch die Möglichkeit, den gewünschten Kartenausschnitt durch Anwahl des entsprechenden Punktes auf der Reliefkarte direkt anzuspringen.

Ändern der Kartenansicht (zoomen)
Die Ansicht der Karte lässt sich in 4 Größenstufen darstellen. Verfügt Deine Maus über ein Mausrad, kannst Du die Größenstufe durch drehen des Mausrades einstellen. Mit den Tasten Page Up und Page Down erreichst Du den gleichen Effekt.

Spielgeschwindigkeit
Die in der Statusleiste angezeigte Spielgeschwindigkeit (T=1.00) entspricht der Standardgeschwindigkeit. Alle Aktivitäten im Spiel laufen wirklichkeitskonform.

Mitunter mag Dir eine höhere Geschwindigkeit sinnvoll erscheinen, vor allem, wenn der Kassenbestand knapp wird und Du auf Belieferung und das Kassieren der Frachten wartest. Ändere die Geschwindigkeit bei Bedarf mit den Tasten . (schneller) und , (langsamer). Einiges an Tempo wird das Spiel problemlos umsetzen. Bei Geschwindigkeiten, die die Leistung Deines Computers überschreiten, sind Unregelmäßigkeiten im Spielablauf, wie etwa in den Bahnhöfen stehenbleibende Züge, zu geringe Passagiererzeugung oder fehlerhafte Beladung bis hin zum Einfrieren des Spiels, nicht ausgeschlossen. Bei denAnzeigeeinstellungen befinden sich diesbezügliche Kontrollmöglichkeiten. Ein automatischer schneller Vorlauf lässt sich über die Taste W oder das Vorlaufsymbol in der Hauptenüleiste ein- und ausschalten.

Einstellung der Helligkeit
Zur Regulierung der Helligkeit siehe Anzeigeeinstellungen.

Editieren von Namen
Die Namensfelder in einigen Fenstern, wie Haltestellen-, Fahrzeug- und Liniennamen, sind editierbar. Sie sind von der Fensteroberfläche zurückgesetzt und zeigen glatte Beschriftungsflächen. Zum editieren, klicke mit der Maus in das Namensfeld. Bearbeite nun den Text. Editierungen werden unmittelbar wirksam. Drücke zum Verlassen des Feldes die Taste RETURN.

Pfeilschalter
In vielen Fenstern findest Du Wertangaben zwischen zwei Pfeilschaltern. Mit einem Klick auf den linken Schalter verringerst Du den Wert, mit einem Klick auf den rechten erhöhst Du den Wert. Halte die linke Maustaste gedrückt, wenn Du größere Wertebereiche durchlaufen möchtest.

Auswahlmenüs
Linienauswahlmenüs in den Depotfenstern und in den Fahrplanfenstern sind wie die Namensfelder zurückgesetzt und zusätzlich mit Pfeilschaltern versehen. Wenn Du auf eine Menüfläche klickst, wird das Auswahlmenü geöffnet. Wählst Du einen Eintrag aus, springt er in die Anzeigezeile und stellt nun die neue Auswahl dar. Die Zeile kann auch editiert werden, aus eigener Erfahrung rät der Autor, Editierungen bevorzugt in speziell dafür vorgesehenen Fenstern vorzunehmen.

Untergrund
siehe bauen im Untergrund

simutrans-124.5/simutrans/text/dk.tab000066400000000000000000000256101520675460500176670ustar00rootroot00000000000000§Dansk PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: dk Dansk # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable inaktivt cl_btn_filter_enable aktivt cl_btn_filter_settings indstil filter cl_btn_sort_asc stigende cl_btn_sort_desc faldende cl_btn_sort_income indtægt cl_btn_sort_name navn cl_btn_sort_type type clf_btn_alle alle clf_btn_invers inv. clf_btn_keine ingen hl_btn_filter_disable inaktivt hl_btn_filter_enable aktivt hl_btn_filter_settings indstil filter hl_btn_sort_asc stigende hl_btn_sort_desc faldende hl_btn_sort_name navn hl_btn_sort_type type hl_btn_sort_waiting varelager hlf_btn_alle alle hlf_btn_invers inv. hlf_btn_keine ingen #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Das Feld gehoert\neinem anderen Spieler\n Dette område\nejes allerede af\nen anden spiller!\n Der Besitzer erlaubt das Entfernen nicht Ejeren tillader\nikke at\nfjerne dette.\n Diese Zusammenstellung kann nicht fahren!\n Denne sammensætning\nkan ikke køre!\n Hier kann kein\nSignal aufge-\nstellt werden!\n Her kan ikke\nbygges et jernbanesignal!\n Post muss neben\nHaltestelle\nliegen!\n Et postkontor skal\nplaceres nær ved\nrutemål/station!\n Schiffhalt muss im\nWasser liegen!\n Skibsrutemål\nskal placeres\ni vandet\nnær en havn!\n Zughalt muss auf\nSchiene liegen!\n Togrutemål\nskal placeres\npå jernbanespor!\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ RAILTOOLS Jernbaneanlæg ROADTOOLS Vejanlæg SHIPTOOLS Havneanlæg SLOPETOOLS Rampeanlæg #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ Remove vehicle from map. Use with care! Fjern køretøjet fra kortet. Forsigtig brug tilrådes! Screenshot\ngespeichert.\n Skærmbilledet\ner gemt.\n #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ \nCan't open heightfield file.\n \nKan ikke åbne relieffilen.\n \nHeightfield has wrong image type.\n \nRelieffelt har\nforkert format.\n %d convois %d køretøjer i listen %d Einzelfahrzeuge im Depot %d enkeltkøretøjer i depotet %s has entered a depot. %s er tilbage i depot. %s\nis crowded. %s er overfyldt. 1 convoi 1 køretøj i listen 1 Einzelfahrzeug im Depot 1 enkeltkøretøj i depotet 1LIGHT_CHOOSE Lysstyrke: 1WORLD_CHOOSE Indstillinger for ny verden: 2LIGHT_CHOOSE Farver: 2WORLD_CHOOSE Kortnummer: 3LIGHT_CHOOSE Scrolltempo: 4LIGHT_CHOOSE Omvendt scrollning 5LIGHT_CHOOSE Vis fodgængere 5WORLD_CHOOSE Antal byer: 6WORLD_CHOOSE Trafiktæthed: 8WORLD_CHOOSE Dag og nat indstilling Abfrage Inspektionsværktøj Abnehmer Aftagere Abriss Fjern/riv ned Absenken Sænk landniveau Add Stop Tilføj Angenommene Waren Modtager følgende varer Anhaenger_tab Anhængere Anheben Hæv landniveau Apply Line Tilknyt rute Arbeiter aus: Ansatte bosat i: Arrived Ankommet Assets Aktiver Bahndepot Togdepot Bankrott:\n\nDu bist bankrott.\n Bankerot:\n\nDu er bankerot.\n Baum Træ Baustelle Byggeplads Beenden Afslut BF station Blockstrecke ist\nbelegt\n Sporstrækningen er\noptaget af\net andet tog\n Boden Mark/land Bruecke Bro Bruecke muss an\neinfachem\nHang beginnen!\n Broer skal\nbegynde på\nskråninger!\n Build ship depot Byg skibsdepot Build signals Byg jernbanesignal Build train depot Byg togdepot Build truck depot Byg vejdepot Cancel Fortryd Capacity: %d%s %s\n Kapacitet: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Kapacitet: %s\n» %d (%d%%) Cash Kassebeholdning Chart Statistik City size Størrelse cl_title Køretøjsliste cl_txt_sort Sorteret efter: clf_chk_cars busser/lastbiler clf_chk_name_filter Filtrer på navn: clf_chk_noincome ingen indtægt clf_chk_noroute ingen rute clf_chk_noschedule ingen ruteplan clf_chk_ships skibe clf_chk_spezial_filter Specialfilter: clf_chk_trains tog clf_chk_type_filter Filtrer på type: clf_chk_waren Filtrer på varer: clf_title Køretøjsliste filter COLOR_CHOOSE\n Vælg venligst en\nfarve fra paletten:\n Construction_Btn Byggeudgifter convoi %d of %d Køretøj %d af %d Convois: %d\nProfit: %s Køretøjer: %d\nProfit: %s Convoys Konvojer Currently playing: Afspiller aktuelt: Del Stop Fjern Delete Line Slet rute Delete this file. Slet denne fil Denkmal Mindesmærke Departed Afgået Details Detaljer Direkt erreichbare Haltestellen Direkte ruter herfra Display settings Udseende Dock havn Durchsatz Max. Eigenbesitz\n Offentlig ejendom\n Ein %s\npasst hier nicht.\n En '%s'\npasser ikke her.\n Einstellungen aendern Ændr indstillinger Electrify track Byg el-master Erzeuge neue Karte.\n Vent et øjeblik.\nSkaber ny verden...\n\nDet kan tage lidt tid\nfor store kort.\n\n Es wird bereits\nein Fahrplan\neingegeben\n Der bliver allerede\nplanlagt en ruteplan.\nGør denne færdig først! Fabrikanschluss Forbundne industrier Fahrplan Ruteplan Fahrzeuge koennen so nicht entfernt werden Køretøjer kan ikke\nfjernes på denne måde.\n Fahrzeuge: Køretøj: Farbe Spillerfarve February Februar Fertig Færdig Filename Filnavn: Finanzen Finanser Found new city Byg en ny by Fracht fragt Free Capacity Fri kapacitet Full load Min. last: GAME PAUSED PAUSE Gebaeude Bygning Gewicht Vægt Gewinn Indtægt Goods list Vareoversigt Gross Profit Overskud H stoppested Happy Tilfredse Helligk. Udseende Hier warten/lagern: Ventende varer og passagerer: hl_title Rutemålsliste hl_txt_filter Filter: hl_txt_sort Sorteret efter: hlf_chk_anleger havn hlf_chk_bahnhof jernbanestation hlf_chk_bushalt busstoppested hlf_chk_frachthof lastplads hlf_chk_keine_verb ingen forbindelse hlf_chk_name_filter Filtrer på navn: hlf_chk_spezial_filter Specialfilter: hlf_chk_type_filter Filtrer på type: hlf_chk_waren_abgabe Producerer: hlf_chk_waren_annahme Forbruger: hlf_title Rutemålsliste filter Homeless Hjemløse Ins Stop Indføj Intro. date: Indførsel: January Januar July Juli June Juni Kann Spielstand\nnicht laden.\n Kan ikke finde\ndet angivne spil!\n Kann Spielstand\nnicht speichern.\n Kan ikke gemme\nspillet!\n keine ingen Keine Einzelfahrzeuge im Depot Ingen enkeltkøretøjer i depotet Lade Relief Indlæs reliefkort Laden Indlæs LANG_CHOOSE\n Vælg venligst dit\nforetrukne sprog:\n Last Year Sidste år leer tom Leistung Ydelse letzen Monat: diesen Monat: sidste måned: denne måned: Line rute Line Management Rute administration Lines serving this stop Rutemål for rute LKW_tab Køretøjer Lokomotive_tab Lokomotiver m3 m³ Maintenance Vedligeholdelse Map roughness Bjergstejlhed: March Marts Max. speed: Tophastighed: May Maj Meldung Meddelelse Menge Mængde Mountain height Kuperet terræn: Music playing disabled/not available Musik slået fra/ikke tilgængelig Music volume: Musik lydstyrke: Net Wealth Samlet formue Neue Karte Ny verden Neue Welt Skab ny verden new convoi New Line Ny rute New Vehicles Nye køretøjer no convois Ingen køretøjer i listen no goods waiting ingen ventende varer No Route Ingen rute Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Giv køretøjerne\nen ruteplan, før\ndu vælger "Start"\n Ok OK Operation Driftsomk. paletten paletter Passagiere passagerer Passagierrate passagerer Passagierziele Passager/post destinationer player 2 Trikky Transport player 3 Meyer Moving Co. Please choose vehicles first\n Vælg venligst et køretøj først\n Post post Power Strømstyrke q1 forår q2 sommer q3 efterår q4 vinter Random map Tilfældigt kort Reliefkarte Reliefkort Restwert: Salgsværdi: Revenue Indkomst sack sække Schienentunnel Byg jernbanetunnel Schiff_tab Skibe Schiffdepot Skibsdepot SEP_FRACTION , SEP_THOUSAND . Serves Line: Følger rute: Show/hide statistics Vis/skjul statistik Size (%d MB): Størrelse (%d MB): Sort waiting list by Sorter ventelisten efter Sound Lyd Sound settings Lydindstillinger Sound volume: Lydstyrke: special freight Specialgods Speichern Gem Spieler Spiller Spieler(mz) AI-spillerliste Spielstand wurde\ngeladen!\n \nSpillet er indlæst!\n Spielstand wurde\ngespeichert!\n \nSpillet er gemt!\n Sprache Sprog Stadtinformation Byinformation Starte Spiel Start Strassendepot Vejdepot Strassentunnel Byg vejtunnel Tage alt dage gammelt There are still vehicles\nstored in this depot!\n Der er stadig køretøjer\nparkeret i depotet!\n This Year Dette år tonnen t. Towns Byer Train Tog Tunnel muss an\neinfachem\nHang beginnen!\n Tunneler skal\nbegynde på\nskråninger!\n Unemployed Arbejdsløse Unhappy Utilfredse units/day /dag, Update Line Opdater rute Verbrauch Forbrug verkaufen Sælge Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Du har gæld!\n\nDu har %d måneder til\nat betale din gæld! voranstellen Sætte foran Waggon_tab Togvogne waiting Venter Water level Vand niveau: Wegpunkt rutemål Wert Værdi WRONGSAVE Inkompatibel spil-\nversion. Kan ikke indlæse\nfilen.\n Zielort Destination Zu nah am Kartenrand En havn skal\nbygges på\net lige kyststykke\n #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &1_CITY_SYLL by &2_CITY_SYLL borg &3_CITY_SYLL huse &4_CITY_SYLL bro &5_CITY_SYLL købing &6_CITY_SYLL skov &7_CITY_SYLL strup &8_CITY_SYLL dal &9_CITY_SYLL mose &A_CITY_SYLL kær %1_CITY_SYLL Frederiks %2_CITY_SYLL Christians %3_CITY_SYLL Mølle %4_CITY_SYLL Kirke %5_CITY_SYLL Bonde %6_CITY_SYLL Ravn %7_CITY_SYLL Ege %8_CITY_SYLL Gammel %9_CITY_SYLL Ny %A_CITY_SYLL Lille %B_CITY_SYLL Store 1center %s %s 1extern %s lokal %s 2center %s Central %s 2extern %s rand %s 3center %s Hoved %s 3extern %s provins %s 4center %s Midt %s 4extern %s omegns %s 5center %s Centrums%s Ucenter Alt-%s %s simutrans-124.5/simutrans/text/dk/000077500000000000000000000000001520675460500171735ustar00rootroot00000000000000simutrans-124.5/simutrans/text/dk/color.txt000066400000000000000000000007451520675460500210600ustar00rootroot00000000000000Farve vælger

Farve vælger

Udseende:
Dialogen består af en tekst med tilhørende farvepalette, hvor en farve vælges ved at venstreklikke på farven.

Beskrivelse:
Her kan du vælge din firmafarve. Firmafarven er den farve, som alle dine besiddelser såsom toge, bygninger etc. vil have. Bemærk venligst, at ikke alle objekter vil være "farvekodet". Det er et valg, der er overladt til den respektive designer.

simutrans-124.5/simutrans/text/dk/convoi.txt000066400000000000000000000040711520675460500212330ustar00rootroot00000000000000Køretøjsliste dialog

Køretøjsliste dialog

Beskrivelse:
Køretøjsliste dialogen giver et overblik over samtlige køretøjer (lastbiler, busser, hestevogne, toge og skibe) med mulighed for at sortere og/eller filtrere disse efter parametre såsom navn, indtægt og type.

Udseende:
Øverst i dialogen er overskrifterne: 'Sorteret efter:' og 'Filter:'.
Til 'Sorteret efter:' er knyttet to "tilstands-knapper", der som default indstilling henholdsvis har tilstandende 'navn' samt 'stigende'.
Til 'Filter:' er der knyttet en 'tilstands-knap' med default tilstanden 'inaktiv' samt en knap med titlen 'indstil filter'.
Resten af dialogen består af en liste indeholdende samtlige dine køretøjer. For alle køretøjer er angivet dets nummer, navn og fortjeneste.

Ved at venstreklikke på et køretøj i listen kan man få dets
køretøj dialog frem.

Køretøjerne i listen kan sorteres ved hjælp af "tilstandsknapperne" under 'Sorteret efter:' overskriften.
Knappen til venstre giver sorteringstypen og knappen til højre angiver, om denne sortering skal foretages 'stigende' eller 'faldende'.
De tre sorteringstyper er:

Navn - Sorterer køretøjerne efter navn. Enten alfabetisk eller omvendt alfabetisk alt efter indstillingen af knappen til venstre.

Indtægt - Sorterer køretøjerne efter størrelsen af køretøjets indtjening. Stigende eller faldende alt efter højre-knappens tilstand.

Type - Sorterer køretøjerne efter deres type, hvor rækkefølgen: biler, toge, skibe anses for stigende.

For at lette overblikket af listen kan køretøjerne filtreres efter forskellige kriterier. Dette sker ved hjælp af knapperne under 'Filter:' overskriften.
Knappen til venstre er en "tilstands-knap". Denne knap kontrollerer filteranvendeksen med tilstandene 'inaktivt' og 'aktivt'. Knappen til højre med teksten 'indstil filter'
åbner forfilter dialogen

simutrans-124.5/simutrans/text/dk/convoi_filter.txt000066400000000000000000000046131520675460500226020ustar00rootroot00000000000000Køretøjsliste filter

Køretøjsliste filter dialog

Beskrivelse:
Denne filter dialog anvendes til at udvælge (filtrere) de køretøjer, som du ønsker at betragte i
Køretøjsliste dialogen

Udseende:
Til venstre findes tre filteroverskrifter, henholdsvis 'Filtrer på navn:', 'Filtrer på type:' og 'Specialfilter:'. Til højre for disse findes en liste med overskriften 'Filtrer på varer:'. Under denne overskrifter findes tre knapper med teksterne 'alle', 'ingen' og 'inv.'. Listen består af en trykknap for samtlige varetyper.

Samtlige fire overskrifter har en chechbox-knap tilknyttet.

Bemærk - For at få foretaget en filtrering med en eller flere af de fire filtrerings muligheder skal:
a) Chechbox-knappen til venstre for overskriften trykkes ned.
b) Tilstandsknappen under overskriften 'Filter:' i
Køretøjsliste dialogen sættes til 'aktivt'

De tre filtreringsmuligheder til venstre er:

Filtrer på navn:
Her er der mulighed for at udvælge køretøjer med et fælles navn. Dette gøres ved at skrive en del af eller hele det ønskede navn i listboksen under overskriften. F.eks vil en indtastning af 'Ben' give alle toge, hvis navn begynder med Bennhardt. Bemærk at søgningen er "case-sensitive"

Filtrer på type:
Her er der mulighed for at udvælge køretøjer efter deres type. De mulige typer er Busser/lastbiler, tog, og skibe.

Special filter:
Her er der mulighed for at udvælge køretøjer, der opfylder følgende:
- Ingen rute (ikke kan finde en forbindelse)
- Ingen ruteplan har (ikke har fået en ruteplan tilknyttet)
- Ingen indtægt (i indeværende år)

Listen til højre giver mulighed for at udvælge køretøjer udfra de varer, der transporteres på køretøjet. Bemærk at dette filter ikke er helt up-to-date, idet der for bulkgods og stykgodsvarer filtreres efter køretøjsbetegnelsen fremfor varetypen. Dvs. hvis der transporteres kul i en stenvogn, vil køretøjet fremgå under varetypen "sten" i stedet for "kul". De tre knapper under overskriften anvendes til at vælge henholdsvis alle eller ingen af de mulige varetyper samt til at foretage den "inverse" trykknap udvælgelse.

simutrans-124.5/simutrans/text/dk/convoiinfo.txt000066400000000000000000000024631520675460500221120ustar00rootroot00000000000000Køretøj dialog

Køretøj dialog

Beskrivelse:
For alle køretøjer (busser, lastbiler, toge og skibe) kan denne dialog åbnes ved at klikke på køretøjet. Dialogen giver nyttig information om køretøjet, dets forbindelser og dets aktuelle lastgrad.

Udseende:
Øverst i dialogen er køretøjets navn. Derefter følger en række oplysninger om køretøjet. Under disse oplysninger er en knap med teksten 'Statistik'. Herefter er et visningsareal, hvor oplysninger om den aktuelle last såsom mængde og destination fremgår.
Øverst til højre i dialogen er en knap med teksten 'Ruteplan' og herunder et billedudsnit med køretøjet. Ved at klikke på dette billedudsnit, vil køretøjet blive centreret på kortet. Sluttelig er der en slet-knap, der vil fjerne køretøjet uden at spørge først.

De to knapper har følgende funktion:

Statistik
Statistik knappen udvider dialogen med et grafisk visningsareal og en række trykknapper, der hver repræsenterer et køretøjsnøgletal. Ved at trykke på en knap bliver udviklingen af det ønskede nøgletal illustreret grafisk i visningsarealet.

Ruteplan
Denne knap åbner en køretøjets ruteplan dialog.

simutrans-124.5/simutrans/text/dk/depot.txt000066400000000000000000000125201520675460500210470ustar00rootroot00000000000000Depot

Depot Dialog

I det følgende benyttes betegnelsen "køretøj" som samlebetegnelse for såvel enkeltkøretøjer som sammensatte køretøjer, toge og skibe.

Beskrivelse:
I et depot har du mulighed for at købe, sælge og lave ruteplaner for diverse køretøjer alt efter depottype. F.eks vejkøretøjer og anhængere i et vejdepot, toge og togvogne i et togdepot samt skibe i et skibsdepot.
Depot dialogen indeholder information om de køretøjer, du er i færd med at købe samt de køretøjer, som du allerede har købt, men endnu ikke har startet.

Udseende:
Øverst er en liste, der indeholder alle de købte køretøjer, som endnu ikke er blevet sendt ud af depotet (har fået tildelt ruteplan og er blevet startet). Det aktuelle køretøj er vist og de andre køretøjer fås frem ved at benytte scroll-knapperne ved siden af listen
Herunder er en analog liste, der indeholder alle de ruter, der er defineret. Den aktuelle rute er vist og de øvrige ruter fås ved at benytte scroll-knapperne
Dernæst følger et areal, hvor et billede af det aktuelle køretøj er vist.
Herunder er fire knapper, der er tilknyttet køretøjslisten: 'Start' 'Ruteplan', 'Adskil' og 'Sælg'. Disse knapper giver mulighed for at starte, at lave ruteplan til, at adskille samt at sælge det aktuelle køretøj.
Derunder er fire knapper, der er tilknyttet rutelisten: 'Ny rute' 'Tilnyt rute', 'Opdater rute' og 'Slet rute'. Disse knapper giver mulighed for at oprette en ny rute, at tilknytte den aktuelle rute til det aktuelle køretøj, at opdatere samt at slette den aktuelle rute. Derunder er to faneblade der indeholder de køretøjer og anhængere (Lokomotiver/togvogne, skibe), der kan købes
Herunder er et areal, der bruges til visning af oplysninger for køretøjerne på fanebladene. For at se oplysningerne om et køretøj, skal musen holdes over det pågældende køretøj på fanebladet.
Til højre på visningsarealet er en tilstands-knap, der ved tryk skifter imellem tre tilstande, henholdsvis: 'koble på', 'sætte foran' og 'sælge'.

Køb køretøj:
For at købe et køretøj, må der ikke være valgt et aktuelt køretøj - køretøjslisten "klikkes fremad" indtil den viser 'vælg nyt køretøj'. Derefter købes køretøjet ved at venstreklikke på det i fanebladet. Køretøjet vil derefter være vist som det aktuelle køretøj i det øverste visningsareal. Ved at anvende tilstands-knappen nederst til højre er det muligt at se hvilke køretøjer/anhængere, der henholdsvis kan "sættes foran/kobles på" det købte køretøj (dette markeres ved grønne bjælker i fanebladet under de mulige køretøjer)
Når det aktuelle køretøj er færdigsammensat, kan det tildeles en ruteplan og startes, eller du kan købe og sammensætte et nyt køretøj ved at "klikke fremad" på køretøjslisten foroven i dialogen.

Start køretøj:
Efter tildeling af ruteplan kan det aktuelle køretøj startes ved at trykke på 'Start' knappen.

Tildel ruteplan:
For at tildele det aktuelle køretøj en "individuel "ruteplan trykkes på 'Ruteplan' knappen, hvorefter ruteplanen defineres vha. den fremkomne ruteplansdialog. Dette er kun fordelagtigt, hvis du ved, at køretøjet er det eneste, der vil benytte denne rute. Ifald du vil indsætte flere køretøjer på den samme rute, er det bedre først at definere ruten ved et trykke på 'Ny rute' og udfylde den fremkomne ruteplansdialog. Derefter figurerer ruten, som den aktuelle rute i rutelisten og kan tilknyttes det aktuelle køretøj ved at trykke på 'Tilknyt rute' knappen.

Adskil køretøj:
Det er muligt at adskille det aktuelle køretøj ved at trykke på 'Adskil' knappen. Køretøjet vil blive opdelt (f.eks i en lastbil og en anhænger) og derefter optræde som til rådighed stående enkeltkøretøjer på fanebladene. Disse enkeltkøretøjer kan benyttes til nye køretøjssammensætninger eller sælges ved at vælge "sælge-tilstanden" på tilstandsknappen nederst til højre og venstreklikke på enkeltkøretøjet i fanebladet.

Sælg køretøj:
For at sælge (hele) det aktuelle køretøj trykkes på 'Sælg' knappen.

Opret ny rute:
For at oprette en ny rute trykkes på 'Ny rute' knappen og derefter benyttes ruteplansdialogen.

Tilknyt rute til køretøj:
For at tilknytte en rute til et køretøj vælges køretøjet som det aktuelle køretøj i køretøjslisten. Dernæst vælges den ønskede rute i rutelisten (eventuelt oprettes den først som nævnt ovenfor) og herefter trykkes på 'Tilknyt rute' knappen.

Opdater eller slet rute:
For at opdatere eller slette en rute vælges den ønskede rute i rutelisten og herefter trykkes enten på Opdater rute' knappen, hvorefter ruteplansdialogen fremkommer eller på 'Slet rute' knappen, hvorefter ruten slettes fra rutelisten.

For senere at sende et køretøj tilbage i depotet, skal du indføje depotet i køretøjets ruteplan - f.eks som næste rutemål. Efter køretøjet er kommet tilbage i depotet figurerer det i køretøjslisten og kan efter behag udbygges, adskilles, tildeles rute eller sælges.

simutrans-124.5/simutrans/text/dk/display.txt000066400000000000000000000033661520675460500214110ustar00rootroot00000000000000Udseende dialog

Udseende dialog

Beskrivelse:
Udseende dialogen giver mulighed for at ændre diverser indstillinger vedrørende Simutrans' udseende.

Udseende:
Øverst i dialogen er tre lister med teksterne 'Lysstyrke:', 'Farver:' og 'Scrolltempo:'. Hver liste har "scroll-knapper" tilknyttet. Herunder er tre "checkbokse" med tilhørende tekster 'Omvendt scrollning', 'Vis fodgængere' og Dag og nat indstilling. Nederst er en række aktuelle belastningsoplysninger vist.

Følgende indstillinger er mulige:

Lysstyrke:
Gør kortet lysere eller mørkere.

Farver:
Ingen effekt

Scrolltempo:
Jo højere indstilling, desto hurtigere vil skærmenbilledet scrolle.

Omvendt scrollning
Her kan indstilles om kortet skal bevæge sig op eller ned, når der scrolles opad og analogt med hensyn til venstre og højre. (fly-lignende kontroller). Det er nemmere at afprøve, end at skulle forklare :-)

Vis fodgængere
Her kan indstilles om computeren skal skabe og vise fodgængere i byerne og omkring rutemål. Hvis din makine døjer med at opdatere skærmbilledet, så slå denne indstilling fra.

Dag og nat indstilling
Her indstilles om der alt efter tidspunktet skal skiftes imellem dag (lyst) og nat (mørkt) indstilling. Prøv det af !

Den nederste halvdel af dialogen indeholder information omkring hvorledes din computer klarer den aktuelle belastning. Disse værdier er ikke interessante med mindre du ved, hvad de står for.
VigtigtDu kan blive bedt om at nævne disse værdier, når du diskuterer performance problemer og andre bugs.

simutrans-124.5/simutrans/text/dk/finances.txt000066400000000000000000000045371520675460500215330ustar00rootroot00000000000000Finanser dialog hjælp

Finanser (f)

Beskrivelse:
Finanser dialogen viser dig såvel din aktuelle finansielle status som din status for sidste år. Dette giver dig indblik i din samlede profit eller tab !
Dialogen indeholder forskellige kategorier og viser den finansielle status for hver enkelt.

Udseende:
Øverst til venstre er for kategorierne: Byggeudgifter, Nye køretøjer, Driftsomkostninger, Vedligeholdelse, Indkomst og Overskud vist to kolonner med resultaterne for henholdsvis dette år og sidste år.
Herunder er to linier, der henholdvis viser Kassebeholdning og vedligeholdelsesudgifterne pr. måned.
Nederst i dialogen er et grafisk visningsareal, hvor en enkelt, flere eller alle kategorier kan afbildes grafisk. Dette gøres for hver enkelt kategori ved at vælge kategorien på knapperne øverst til højre i dialogen.

Kategorierne er følgende:

Byggeudgifter:
De samlede udgifter for alt du har bygget: Jernbaner, bygninger, veje, signaler, havne etc.

Nye køretøjer:
De samlede udgifter for alle køretøjer: Toge, togvogne, lastbiler/busser, anhængere eller skibe, som du har købt.

Driftsomkostninger:
De samlede kørselsomkostninger for alle dine køretøjer.
For hvert enkelt køretøj kan omkostninger pr. kørt km. ses under oplysningerne for køretøjet i depotdialogen.

Vedligeholdelse:
Det beløb du skal betale for at vedligeholde dine jernbaner/veje/bygninger.

Indkomst:
Den samlede indtjening for alle køretøjer.
Fortjenesten (indtjening - driftsomkostninger) for hvert enkelt køretøj kan ses i menuen: Køretøjsliste.

Overskud:
Det samlede overskud. Udregnes som summen af de ovennævnte kategorier.

Kassebeholdning:
Din kontante kassebeholdning.
Hvis dette beløb ved udgangen af en måned er negativt tre (3) gange i træk, er du bankerot og har "tabt" spillet.
Bemærk at vedligeholdelsesudgifter betales pr. måned ved udgangen af måneden

Aktiver:
Dette udregnes som den samlede salgsværdi af alle dine køretøjer.

Samlet formue:
Dette udregnes som summen af aktiver og kassebeholdning.

simutrans-124.5/simutrans/text/dk/haltlist.txt000066400000000000000000000043421520675460500215630ustar00rootroot00000000000000Rutemålsliste dialog

Rutemålsliste dialog

Beskrivelse:
Rutemålsliste dialogen giver et overblik over samtlige rutemål (lastepladser, jernbanestationer og busstoppesteder) med mulighed for at sortere og/eller filtrere disse efter parametre såsom type, varer og varelager.

Udseende:
Øverst i dialogen er overskrifterne: 'Sorteret efter:' og 'Filter:'.
Til 'Sorteret efter:' er knyttet to "tilstands-knapper", der som default indstilling henholdsvis har tilstandende 'navn' samt 'stigende'.
Til 'Filter:' er der knyttet en "tilstands-knap" med default tilstanden 'inaktiv' samt en knap med titlen 'indstil filter'.
Resten af dialogen består af en liste indeholdende samtlige dine rutemål. For alle rutemål er angivet dets nummer, navn, type (markeret ved et lastbil-, bus-, tog- eller skibssymbol) samt hvilket varelager, der afventer afhentning.

Ved at venstreklikke på et rutemål i listen kan man få dets rutemåls dialog frem.

Rutemålene i listen kan sorteres ved hjælp af "tilstandsknapperne" under 'Sorteret efter:' overskriften.
Knappen til venstre giver sorteringstypen og knappen til højre angiver, om denne sortering skal foretages 'stigende' eller 'faldende'.

De fire sorteringstyper er:

Navn
Sorterer rutemålene efter navn. Enten alfabetisk eller omvendt alfabetisk alt efter indstillingen af knappen til venstre.

Varelager
Sorterer rutemålene efter størrelsen af rutemålets varelager. Stigende eller faldende alt efter højre-knappens tilstand.

Type
Sorterer rutemålene efter deres type, hvor rækkefølgen: Biler, busser, toge, skibe anses for stigende.

Nummer
Sorterer rutemålene efter deres nummer.

For at lette overblikket af listen kan rutemålene filtreres efter forskellige kriterier. Dette sker ved hjælp af knapperne under 'Filter' overskriften.
Knappen til venstre er en "tilstands-knap". Denne knap kontrollerer filteranvendeksen med tilstandene 'inaktivt' og 'aktivt'. Knappen til højre med teksten 'indstil filter'
åbner forfilter dialogen

simutrans-124.5/simutrans/text/dk/haltlist_filter.txt000066400000000000000000000040441520675460500231270ustar00rootroot00000000000000Rutemålsliste filter dialog

Rutemålsliste filter dialog

Beskrivelse:
Denne filter dialog anvendes til at udvælge (filtrere) de rutemål, som du ønsker at betragte i
Rutemålsliste dialogen

Udseende:
Til venstre findes tre filteroverskrifter, henholdsvis 'Filtrer på navn:', 'Filtrer på type:' og 'Specialfilter:'. Til højre for disse findes to lister med overskrifterne 'Forbruger:' og 'Producerer:'. Under begge disse overskrifter findes tre knapper med teksterne 'alle', 'ingen' og 'inv.'. Listerne består af en trykknap for samtlige varetyper.

Samtlige fem overskrifter har en checkbox-knap tilknyttet.

Bemærk - For at få foretaget en filtrering med en eller flere af de fem filtrerings muligheder skal:
a) Checkbox-knappen til venstre for overskriften trykkes ned.
b) Tilstandsknappen under overskriften 'Filter:' i
Rutemålsliste dialogen sættes til 'aktivt'

De tre filtreringsmuligheder til venstre er:

Filtrer på navn:
Her er der mulighed for at udvælge rutemål med et fælles navn. Dette gøres ved at skrive en del af eller hele det ønskede navn i listboksen under overskriften. F.eks vil en indtastning af 'Gam' give alle rutemål, hvis navn begynder med Gammel. Bemærk at søgningen er "case-sensitive"

Filtrer på type:
Her er der mulighed for at udvælge rutemål efter deres type. De mulige typer er lastepladser, busstoppesteder, jernbanestationer og havne.

Special filter:
Her er der mulighed for at udvælge rutemål, der er overfyldte og rutemål, der ikke er forbundne.

De to lister giver mulighed for at udvælge rutemål udfra de varer, der forbruges eller produceres på rutemålet. De tre knapper under overskriften anvendes til at vælge henholdsvis alle eller ingen af de mulige varetyper samt til at foretage den "inverse" trykknap udvælgelse.

simutrans-124.5/simutrans/text/dk/language.txt000066400000000000000000000022131520675460500215150ustar00rootroot00000000000000Sprog dialog hjælp

Sprog dialog

Beskrivelse:
Simutrans er et multilingualt spil, der tillader spilleren at vælge sit foretrukne sprog for alle viste tekster. (forudsat en oversættelse eksisterer)

Udseende:
Dialogen består af en række radio-knapper. En radioknap for hvert muligt sprog.

Når du vælger et sprog i radioknap-menuen vil samtlige tekster blive vist på dette sprog - med to undtagelser:

1) De fleste åbne dialoger skifter først sprog, når de genåbnes.

2) Eksisterende byer vil ikke ændre navn.

For at give byer navne på dit foretrukne sprog, skal du vælge sprog før du skaber et nyt kort.

Hvis dit foretrukne sprog skulle mangle, findes der et par oversættelser på:

http://www.simustation.de.vu og
http://www.simutrans.de
Disse oversættelser er dog ikke altid fuldstændige og up-to-date.

En anden mulighed ville selvfølgelig være, at du selv lavede en oversættelse ;-)

For at hjælpe med at oversætte Simutrans på dit sprog,
besøg https://translator.simutrans.com

simutrans-124.5/simutrans/text/dk/load.txt000066400000000000000000000022751520675460500206610ustar00rootroot00000000000000Indlæs hjælp

Indlæs dialog

Beskrivelse:
Indlæs dialogen giver mulighed for at fortsætte et tidligere gemt spil. Indlæs dialogen er opbygget analogt til
Gem dialogen

Udseende:
Øverst i dialogen er overskriften 'Filnavn:' med en tilhørende tekstboks. Denne tekstboks giver mulighed for at indtaste navnet på et tidligere gemt spil. Derunder er en liste bestående af de spil, der befinder sig i simutrans/save mappen. For alle spil er vist en slet-knap, spillets navn samt "gem-tidspunktet".
Nederst er en 'OK' og en 'Fortryd' knap.

Et spil indlæses enten ved at taste navnet på spillet i tekstboksen og trykke på 'OK' knappen eller ved at trykke på spillets navn i listen. Hvis du prøver at indlæse et gemt spil, som ikke eksisterer vil du få en fejlmeddelelse, der fortæller dette.

Vigtigt !
Gamle gemte spil fra tidligere Simutrans versioner kan være inkompatible.
Læs venligst "release notes" for hver ny version, du anvender for at se, om dette er tilfældet.
Imellem "major versions" vil inkompabilitet højst sandsynlig være tilfældet.

simutrans-124.5/simutrans/text/dk/map.txt000066400000000000000000000007661520675460500205220ustar00rootroot00000000000000Reliefkort dialog hjælp

Reliefkort dialog

Beskrivelse:
Reliefkort dialoget giver et fugleperspektivs overblik over verden, med informationer om byer, industrier, veje, jernbanespor o.l.

Hver industritype er markeret med sin egen farve. Et overblik over denne farvekodning findes nedenunder kortet.

Hvis du ønsker at "se nærmere" på verden, kan du højreklikke på kortet, hvorved der skiftes imellem to forskellige zoom niveauer.

simutrans-124.5/simutrans/text/dk/new_world.txt000066400000000000000000000063541520675460500217440ustar00rootroot00000000000000Skab verden dialog hjælp

Skab verden dialog (X/Q)

Beskrivelse:
Skab verden dialogen giver mulighed for at skabe et nyt kort (spil) med indstillinger, der vil influere på, hvorledes den nye verden vil se ud. Disse indstillinger kan være alt fra kortstørrelse til industritæthed.

Udseende:
Øverst til venstre findes otte indstillingsmuligheder for det nye kort.
Til højre herfor er et "preview" areal, der viser, hvorledes det nye kort vil se ud.
Under disse indstillinger er en knap til valg af tilfældigt kortnummer.
Nederst i dialogen findes fire knapper til indlæsning af gemte spil eller reliefkort, til at starte et nyt spil og til at afslutte Simutrans.

Indstillingsmulighederne for et nye kort er følgende:

Kortnummer:
Bestemmer hvordan verden vil se ud inklusiv beliggenheden af byer, vand og land. I "preview" arealet kan hvert enkelt kort ses (opdateret med de andre valgte indstillinger).

Størrelse:
Bestemmer hvor mange kvadrater kortet vil bestå af. Størrelsen varierer fra 128*128 til 640*640 kvadrater. Default størrelsen er 256*256.
Vigtigt - Jo større et kort desto mere memory bliver benyttet. Vær opmærksom på om du har tilstrækkelig memory til at spille et spil med den valgte kortstørrelse.

Industritæthed:
Tætheden af industrier. Jo lavere tæthedesværdi desto sværere vil spillet blive, da der bliver færre industrier at transportere imellem. Default værdien er 125.

Antal byer:
Bestemmer hvor mange byer, der placeres på kortet. Værdierne går fra 2 til 64 med 32 som default værdi.

Trafiktæthed:
Tætheden af private biler i byerne. Default værdien er 8.

Vand niveau:
Afspejler hvor højt grundvandsspejlet er (hvor mange vandfelter, der vil være). Jo lavere værdi desto flere landfelter vil kortet indeholde. Default værdien er 4

Kuperet terræn:
Bestemmer højden af det højeste bjerg. Jo lavere værdi desto færre og lavere bjerge og dale. Værdierne går fra 0 til 160 med default værdi 160.

Bjergstejlhed:
Angiver hvor stejle bjergene vil være. Jo lavere værdi desto "jævnere" vil bjergstigningerne være.

Tilfældigt kort:
Vælger et tilfældigt kortnummer (opdateret med de valgte indstillinger)

Vis fodgængere
Bestemmer om fodgængere skal være synlige eller ej. Indstillingen kan ændres under spillet
ved at benytte Udseende dialogen.

Dag og nat indstilling
Bestemmer om der alt efter klokkeslet skal skiftes imellem dag og nat belysning. Denne indstilling kan ligeledes ændres under spillet
ved at benytte Udseende dialogen.

De fire knapper er:

Indlæs spil:
Indlæser et tidligere gemt spil.
Se Indlæs dialogen for nærmere information

Indlæs reliefkort:

Start spil
Starter et nyt spil med de valgte indstillinger.

Afslut
Afslutter Simutrans - "no questions asked".

simutrans-124.5/simutrans/text/dk/options.txt000066400000000000000000000022561520675460500214340ustar00rootroot00000000000000Indstillinger dialog hjælp

Indstillinger dialog

Beskrivelse:
Denne dialog er en indgang til alle andre dialoger i Simutrans.

Udseende:
Dialogen består af ni (9) knapper, der hver aktiverer en ny dialog.

Nedenfor er en liste over de mulige dialoger med en kort beskrivelser af, hvad den aktiverede dialog indeholder.

Sprog
Tillader valg af hvilket sprog Simutrans benytter til tekster og navne.

Spillerfarve
Giver mulighed for valg af firmafarve.

Udseende
Ændr udseende indstillinger såsom "dag og nat indstilling"

Lyd
Vælg dine lyd- og musikpræferencer.

Spillerliste
Kontroller AI-spillerne i spillet.

Indlæs
Indlæs et tidligere gemt spil.

Gem
Gem det aktuelle spil.

Nyt kort
Start et nyt spil.

Afslut
Afslut spillet. Bemærk at der ikke først bliver spurgt om det aktuelle spil skal gemmes.

For en mere detaljeret beskrivelse, vælg venligst "hjælp" i den enkelte dialog.

simutrans-124.5/simutrans/text/dk/players.txt000066400000000000000000000013061520675460500214130ustar00rootroot00000000000000AI-spillerliste dialog hjælp

AI-spillerliste dialog

Beskrivelse:
AI-spillerliste dialogen giver mulighed for at bestemme antallet af computerstyrede spillere i spillet.

Udseende:
Dialogen består af seks (6) checkbox-knapper med angivelse af den enkelte spillers firmafarve.

For at aktivere/stoppe en AI-spiller skal den pågældende checkbox markeres. Bemærk at selvom en AI-spiller "deaktiveres" vil vedkommendes eksisterende ruter fortsat modtage indtægter. Deaktivering af en AI-spiller får blot vedkommende "til at sove".

Dialogen giver ligeledes et overblik over kassebeholdningen for hver enkelt konkurrent.

simutrans-124.5/simutrans/text/dk/railtools.txt000066400000000000000000000120111520675460500217370ustar00rootroot00000000000000Jernbaneanlæg

Jernbaneanlæg dialog

Beskrivelse:
Denne dialog indeholder værktøjer til at bygge jernbanespor, signaler, tunneller, el-master, stationer, depoter og jernbaneovergange.

Udseende:
Dialogen består af syv (7) symboler, der hver repræsenterer et byggeværktøj.

Gående fra venstre mod højre samt oppefra og ned er de forskellige symboler:

Byg jernbaneanlæg:
Dette værktøj vil bygge et jernbanespor imellem to kort-kvadrater. Ved venstreklik på symbolet forandres cursoren til et jernbanekryds-symbol. For at bygge et jernbanespor venstreklikkes på start-kvadratet. Cursoren forandres derved til en bulldozer. Venstreklik derefter på slut-kvadratet. Simutrans vil nu bygge et jernbanespor imellem de to kvadrater.
Der bliver kun bygget på frie kvadrater. Veje og andre spilleres jernbanespor vil ikke blive krydset. Benyt jernbaneovergange, broer eller tunneller til at krydse disse "forhindringer".
Hvis der ikke kan findes en forbindelse imellem de to kvadrater vil der ikke blive bygget noget overhovedet.

Byg jernbanesignal:
Dette værktøj benyttes til at bygge jernbanesignaler på jernbanespor. Ved venstreklik på symbolet forandres cursoren til et signalsymbol.
For at bygge et signal venstreklikkes på et jernbanespor, hvor signalet skal placeres. Signaler bygges på kvadrat-grænser, men faktisk bygges et signal på begge tilstødende kvadrater. Der kan kun bygges et signal på et kvadrat.
For at fjerne et signal benyttes "Fjern/riv ned" menuen. Dette vil fjerne signalet men lade jernbanesporet intakt.
Signaler benyttes til at begrænse toges rutemuligheder. De kan f.eks benyttes til at tillade to toge at benytte den samme sporstrækning.
Ensrettede signaler kan benyttes til at tvinge tog i en bestemt retning.
Et ensrettet signal fås ved at placere et normalt signal og derefter venstreklikke på signalet igen. Ved et yderligere venstreklik bliver retningen for det ensrettede signal ændret og endnu et venstreklik får signalet tilbage til et normalt "tovejs-signal"
Check omhyggelig om et ensrettet signal vender rigtigt, da togene i modsat fald ikke vil kunne finde en rute.

Byg jernbanetunnel:
Dette værktøj benyttes til at bygge jernbanetuneller. Ved venstreklik på symbolet ændres cursoren til et tunnelsymbol.
Tunneller benyttes til at lave lige jernbanestrækninger igennem et bjerg. Benyt tunneller til at undgå at skulle bygge udenom eller over bjerget.
For at bygge en tunnel skal jernbanesporene, der skal forbindes, bygges først. Byg de to jernbanespor så de ender overfor hinanden og opad en skråning på det bjerg, tunnellen skal gå igennnem. Det er vigtigt, at sporene er på samme niveau på hver side af bjerget.
Venstreklik derefter på en af spor-enderne, hvorefter de vil blive forbundet med en tunnel.

Byg el-master:
Dette værktøj benyttes til at bygge el-master på en jernbanestrækning. Ved venstreklik på symbolet ændres cursoren til et el-mastsymbol.
For at elektrificere en jernbanestrækning venstreklikkes på et vilkårligt jernbanespor på strækningen.
El-master er nødvendige for at kunne benytte toge, der har el som drivmiddel. Hvis der mangler el-master, vil et sådant tog ikke kunne finde en rute.

Byg jernbanestation:
Dette værktøj benyttes til at bygge en jernbanestation på en jernbanestrækning. Ved venstreklik på symbolet ændres cursoren til et stationssymbol.
Stationer benyttes af toge til at hente eller aflevere varer. Stationer bygges af stationselementer og kan være af vilkårlig størrelse.
For at bygge et stationselement venstreklikkes på det jernbanespor-kvadrat, hvor elementet skal placeres. Stationselementer kan ikke bygges på skråninger, i kurver eller på sporkryds.
Ethvert stationselement, der placeres ved siden af en eksisterende station, vil blive en del af denne station. Ethvert element kan indeholde to (2) togvogne.
Kontroller at en station kan indeholde det længste tog, der skal stoppe der. Togvogne, der ikke er inde i stationer vil ikke blive lastet eller losset.

Byg togdepot:
Dette værktøj benyttes til at bygge et togdepot på en jernbanestrækning. Ved venstreklik på symbolet ændres cursoren til et depotsymbol.
Depoter benyttes til at købe, sælge og opdatere toge og togvogne.
For at bygge et depot venstreklikkes på et eksisterende jernbanespor-afslutning, hvor depotet ønskes placeret.

Byg jernbaneovergang:
Dette værktøj benyttes til at bygge en jernbaneovergang over en vej. Ved venstreklik på symbolet ændres cursoren til et jernbaneovergangssymbol.
For at bygge en jernbaneovergang venstreklikkes på et lige vejstykke. Jernbaneovergange benyttes til at krydse egne veje. For at krydse andre spilleres veje skal benyttes tuneller eller broer.

simutrans-124.5/simutrans/text/dk/roadtools.txt000066400000000000000000000064061520675460500217500ustar00rootroot00000000000000Vejanlæg dialog hjælp

Vejanlæg dialog

Beskrivelse:
Denne dialog indeholder værktøjer til at bygge veje, busstoppesteder, tunneller, lastepladser, vejdepoter og jernbaneovergange.

Udseende:
Dialogen består af seks (6) symboler, der hver repræsenterer et byggeværktøj.

Gående fra venstre mod højre samt oppefra og ned er de forskellige symboler:

Byg vejanlæg:
Dette værktøj vil bygge en vej imellem to kort-kvadrater. Ved venstreklik på symbolet forandres cursoren til et vej-symbol. For at bygge en vej venstreklikkes på start-kvadratet. Cursoren forandres derved til en bulldozer. Venstreklik derefter på slut-kvadratet. Simutrans vil nu bygge en vej imellem de to kvadrater.
Der bliver kun bygget på frie kvadrater og over egne og andre spilleres veje. Andre spilleres jernbanespor vil ikke blive krydset. Benyt jernbaneovergange, broer eller tunneller til at krydse disse "forhindringer".
Hvis der ikke kan findes en forbindelse imellem de to kvadrater vil der ikke blive bygget noget overhovedet.

Byg busstoppested:
Dette værktøj benyttes til at bygge busstoppesteder på veje. Ved venstreklik på symbolet forandres cursoren til et stoppestedssymbol. Venstreklik derefter på en vej, hvor du vil have stoppestedet placeret.

Byg tunneller:
Dette værktøj benyttes til at bygge tuneller. Ved venstreklik på symbolet ændres cursoren til et tunnelsymbol. Tunneller benyttes til at lave lige vejstrækninger igennem et bjerg. Benyt tunneller til at undgå at skulle bygge udenom eller over bjerget.
For at bygge en tunnel skal vejene, der skal forbindes, bygges først. Byg de to veje så de ender overfor hinanden og opad en skråning på det bjerg, tunnellen skal gå igennnem. Det er vigtigt, at vejene er på samme niveau på hver side af bjerget.
Venstreklik derefter på en af vejafslutningerne, hvorefter de vil blive forbundet med en tunnel.

Byg lasteplads:
Dette værktøj benyttes til at bygge lastepladser. Lastepladser benyttes af dine lastbiler til at laste eller aflevere varer. En lasteplads bygges ved at venstreklikke på symbolet, hvorved cursoren forandres til et lastepladssymbol, og derefter venstreklikke på en vejafslutning, hvor du vil have lastepladsen placeret. Bemærk at der højest kan være to køretøjer på en lastplads af gangen

Byg vejdepot:
Dette værktøj benyttes til at bygge
vejdepoter. Depoter benyttes til at købe, sælge og opdatere dine køretøjer. Depoter bygges, ligesom lastepladser på vejafslutninger ved at venstreklikke på symbolet, hvorved cursoren forandres til et depotsymbol, og derefter venstreklikke, hvor depotet skal placeres,

Byg jernbaneovergang:
Dette værktøj benyttes til at bygge en jernbaneovergang over et jernbanespor. Ved venstreklik på symbolet ændres cursoren til et jernbaneovergangssymbol. Jernbaneovergange benyttes til at krydse jernbanespor. For at bygge en jernbaneovergang venstreklikkes på et lige sporstykke, hvorefter veje via jernbaneovergangen kan krydse jernbanesporet.

simutrans-124.5/simutrans/text/dk/save.txt000066400000000000000000000022721520675460500206750ustar00rootroot00000000000000Gem Help

Gem dialog hjælp

Beskrivelse:
Hvis du ønsker at spille et spil over et længere tidsrum, bliver du nødt til at gemmme det på et tidspunkt. Simutrans giver mulighed for at gemme alle aktuelle data i en fil, der kan indlæses på et senere tidspunkt.

Udseende:
Øverst i dialogen er overskriften 'Filnavn:' med en tilhørende tekstboks. Denne tekstboks giver mulighed for at indtaste et navn, som spillet skal gemmes under. Derunder er en liste bestående af de spil, der befinder sig i simutrans/save mappen. For alle spil er vist en-slet knap, spillets navn samt "gem-tidspunktet".
Nederst er en 'OK' og en 'Fortryd' knap.

Et spil gemmes enten ved at taste et navn for spillet i tekstboksen og trykke på 'OK' knappen eller ved at gemme under et tidligere navn. Det sidste gøres ved trykke på spillets navn i listen, hvorved det tidligere gemte spil overskrives.

Det er muligt at slette gemte spil ved at trykke på slet-knappen. Bemærk at dette sletter spillet uden mulighed for at genskabe det !! Efter der er trykket på slet-knappen lukkes dialogen uden yderligere meddelelser.

simutrans-124.5/simutrans/text/dk/schedule.txt000066400000000000000000000045421520675460500215350ustar00rootroot00000000000000Ruteplan dialog

Ruteplan dialog

Beskrivelse:
Ruteplan dialogen benyttes til at definere ruter imellem bustoppesteder, lastepladser, jernbanestationer, havne eller olieplatforme. Til en rute kan et eller flere køretøjer tilknyttes og startes, hvorefter de vil blive med at gennemkøre ruten, indtil de sendes tilbage i depot.

Udseende:
Øverst i dialogen er en liste med overskriften 'Min. last'. Til listen er to scroll-knapper tilknyttet.
Derunder følger et visningsareal, der viser navne, nummer, Min. lastningsgrad samt koordinater for de forskellige rutemål.
Nederst er fire knapper med teksterne 'Tilføj', 'Indføj', 'Fjern' og 'Færdig'. Som default indstilling er knappen 'Tilføj' trykket ned.

Opret en rute:
For at oprette en rute trykkes på 'Tilføj' knappen, hvorved cursoren forandres til et rutemål-symbol. Derefter venstreklikkes på de ønskede rutemål (ved en havn klikkes i vandet lige ved havnen) i den ønskede rækkefølge. Bemærk at der pt. er en maksimusgrænse på seksten (16) rutemål
For samtlige rutemål kan der indstilles en "Minimums lastgrad". Minimum lastgraden er den procentsats af den samlede lastevne, som mindst skal være fyldt på køretøjet, før det fortsætter til næste rutemål. For at foretage denne indstilling klikkes til venstre for rutemålet i visningsarealet. Herved bliver rutemålet markeret som det aktuelle rutemål. Den ønskede minimums lastgrad indstilles derefter ved at bruge listens scroll-knapper. Bemærk at et køretøj, der endnu ikke har fået påfyldt den ønskede minimums lastgrad, vil vente til dette er tilfældet og kan derved spærre rutemålet for andre køretøjer. Tilsidst trykkes på 'Færdig' knappen, hvorved ruten er defineret og dialogen lukkes.

Opdater en rute:
En rute kan opdateres på følgende måde:
- Ved at trykke på 'Tilføj' knappen og tilføje yderligere rutemål bagest i rækkefølgen af rutemål
- Ved at trykke på 'Indføj' knappen hvorefter en nyt rutemål indføjes før det aktuelt markerede rutemål i listen. Dette er den nemmeste måde at sende køretøjer tilbage i depot på.
- Ved at trykke på 'Fjern' knappen, hvorefter det aktuelt markerede rutemål kan fjernes fra listen ved at klikke på det.

simutrans-124.5/simutrans/text/dk/shiptools.txt000066400000000000000000000025571520675460500217710ustar00rootroot00000000000000Havneanlæg dialog hjælp

Havneanlæg dialog

Beskrivelse:
Denne dialog indeholder værktøjer til at bygge havne og skibsdepoter.

Udseende:
Dialogen består af tre (3) symboler, hvoraf et repræsenterer havnebygning og de to andre skibsdepoter.

Gående fra venstre mod højre samt oppefra og ned er de forskellige symboler:

Byg havn:
Dette værktøj benyttes til at bygge havne. Ved venstreklik på symbolet forandres cursoren til et havnesymbol. Herefter kan der bygges en havn ved at venstreklikke på en lige kyststrækning. Havne benyttes af skibe til at laste eller losse varer.
Bemærk at havne pt. ikke kan fjernes direkte. Det kan gøres ved fjerne havnen, hæve landniveauet og derefter sænke landniveauet.

Byg skibsdepot:
Et skibsdepot benyttes til at bygge, sælge og tilknytte ruter til skibe. Der er to symboler til bygning af skibsdepoter. Disse er dog pånær udseendet fuldstændig ens. Et skibsdepot bygges ved at venstreklikke på symbolet, hvorved cursoren forandres til et skibsdepot symbol, og derefter venstreklikke på et hvilket som helst vandfelt, hvor depotet ønskes placeret. Som regel er det en fordel at placere depotet i nærheden af et eller flere fremtidige rutemål.

simutrans-124.5/simutrans/text/dk/sound.txt000066400000000000000000000021051520675460500210620ustar00rootroot00000000000000Lydindstillinger dialog hjælp

Lydindstillinger dialog

Beskrivelse:
I denne dialog kan du såvel ændre indstillingerne for lyden og musikken, der afspilles under spillet, som det musikstykke, der aktuelt afspilles.

Udseende:
Øverst i dialogen er to scrollbars med overskrifterne: 'Lydstyrke:' og 'Musik lydstyrke:'
Derunder er overskriften 'Afspiller aktuelt:' med to scroll-knapper tilknyttet.

Lydstyrken for diverse lydeffekter (toge, bulldozers ol.) indstilles med den øverste scrollbar. Lydstyrken for baggrundsmusikken indstilles med den nederste scrollbar.

Nederst i dialogen vises det aktuelt afspillede musikstykke. Der kan vælges tidligere eller senere musiknumre ved at benytte scroll-knapperne.

Bemærk at Simutrans ikke inkluderer baggrundsmusik. Denne må tilføjes manuelt. Oplysninger om hvorledes dette gøres, kan findes på Simutrans hjemmesiden : www.simutrans.org

For helt at slå lyden fra kan Simutrans startes med en -nosound option.

simutrans-124.5/simutrans/text/dk/station.txt000066400000000000000000000047171520675460500214260ustar00rootroot00000000000000Rutemål

Rutemål dialog

Beskrivelse:
For alle rutemål (busstoppesteder, lastepladser, jernbanestationer og havne) på kortet kan denne dialog åbnes ved at klikke på rutemålet. Bemærk, hvis der er køretøjer på rutemålet, vil en køretøjs dialogligeledes blive åbnet for hvert køretøj. Dialogen giver nyttig information om rutemålet, dets forbindelser og den aktuelle varesituation.

Udseende:
Øverst i dialogen er rutemålets navn og nummer. Derefter følger en oversigt over passagerer befordret fra rutemålet denne dag. Dernæst kommer en oversigt over aktuelt ventende varer (og passagerer). Hertil er knyttet to knapper med teksten 'Statistik' og 'Destination' samt et visningsareal, der giver oplysninger om mængden af og destinationen for de ventende varer.
Øverst til højre i dialogen er en knap med teksten 'Detaljer' og herunder et billedudsnit af rutemålet. Ved at klikke på dette billedudsnit, vil rutemålet blive centreret på kortet.

De tre knapper har følgende funktion:

Statistik:
Statistik knappen udvider dialogen med et grafisk visningsareal og en række trykknapper, der hver repræsenterer et rutemålsnøgletal. Ved at trykke på en knap bliver udviklingen af det ønskede nøgletal illustreret grafisk i visningsarealet.

Destination:
Dette er en "tilstandsknap" til sortering af oplysningerne om ventende varer i visningsarealet. De mulige sorteringsmuligheder (tilstande) er efter 'Destination', 'via' og 'Mængde'

Detaljer:
Denne knap åbner en ny dialog med følgende yderligere rutemålsoplysninger:

- Forbundne industrier:
Angiver hvilke nærliggende industrier, der benytter dette rutemål til at modtage eller aflevere varer. Denne oplysning er et glimrende check for, om et rutemål "er placeret tæt nok" på en industri til at kunne betjene denne. Et rutemål kan udmærket betjene flere industrier samtidigt

- Modtager følgende varer:
Angiver hvilke varer, der kan leveres til rutemålet.

- Direkte ruter herfra:
Angiver hvilke andre rutemål, der er direkte forbindelse til samt hvilke varer, der leveres til eller fra dette rutemål.

- Stoppested for rute:
Angiver hvilke definerede (nummererede) ruter, der har dette rutemål som stoppested.

simutrans-124.5/simutrans/text/en.tab000066400000000000000000001102251520675460500176700ustar00rootroot00000000000000English PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: en English # # Encoding: ISO-8859-1 # # Font: Prop-Latin1.bdf # # Date Created: 31.05 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Display times as day:hh:mm of the month AMBIENT_SOUND ambient sounds CASH_SOUND cashing sound cl_btn_filter_disable Disabled cl_btn_filter_enable Enabled cl_btn_filter_settings Settings cl_btn_sort_asc Ascending cl_btn_sort_desc Descending cl_btn_sort_id Internal ID cl_btn_sort_income Income cl_btn_sort_name Name cl_btn_sort_type Type clf_btn_alle all clf_btn_invers inv. clf_btn_keine none climate area percentage Size single climate CROSSING_SOUND crossing warnings FACTORY_SOUND factory noises gl_btn_sort_bonus Bonus gl_btn_sort_name Name gl_btn_sort_revenue Revenue gl_btn_unsort Unsorted height based Climate from height hl_btn_filter_disable Disabled hl_btn_filter_enable Enabled hl_btn_filter_settings Settings hl_btn_sort_asc Ascending hl_btn_sort_desc Descending hl_btn_sort_name Name hl_btn_sort_type Type hl_btn_sort_waiting Waiting hlf_btn_alle all hlf_btn_invers inv. hlf_btn_keine none humidities humidity borders Lake height max. lake height moisture land increase over land moisture water increase over water Queueing Waiting Trend rainfall humidity Reselect closes tools Re-selecting a tool de-selects it Road toll Road Toll Scenario Scenario Info sea marine climate Start this as a server Start a server using this game. temperature-humidity based Climates from weather TOOL_SOUND tool action sounds TRAFFIC_SOUND traffic noises Transfers Passing through #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Arctic desert Desert mediterran Mediterranean rocky Alpine (rocky) temperate Temperate tropic Tropical tundra Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Cannot alter water This basin cannot be further drained or filled. Cannot build on a double slope! Can be only built on a single slope or flat ground! Cannot built depot here! Cannot build a depot here. Cannot built this station/building\nin underground mode here. This station/building cannot be built underground. Cannot connect to the\ncenter of a double slope! A brige cannot connect to the middle of a double slope!\nUse landscaping tool to fix this. Convoi handles exhausted! Maximum number of convoys reached. Corrupt file File corrupt or truncated. Could not open file Could not access file. Maybe check permissions? Das Feld gehoert\neinem anderen Spieler\n This piece of\nland is owned by\nanother player!\n Der Besitzer erlaubt das Entfernen nicht The owner refuses\npermission to\nremove this!\n Diese Zusammenstellung kann nicht fahren!\n This combination\ncannot start!\n Failed to write file Could not write out file. (Permissions?) Flugzeughalt muss auf\nRunway liegen!\n Plane stand must\nbe on a taxiway. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n You cannot build\n\nairport building here.\n Hier kann kein\nSignal aufge-\nstellt werden!\n You cannot place\nrail signals here.\n Maglevhalt muss auf\nMaglevschiene liegen!\n A maglev stop must be placed on maglev track! Monorailhalt muss auf\nMonorail liegen!\n Monorail stops must\nbe on monorail track. Monorails are not available yet! Monorails are not yet available. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n A narrowgauge stop must be placed on narrowgauge track No curves on runways Runways cannot make turns! No suitable crossing No suitable crossing! (max speed too fast?) No suitable way on the ground! Stops can be built only on\nstraight level matching ways! No through station here! Stations must be placed on dead-end ways or straight sections with no intersections. Not allowed to copy object. You cannot build that object!\n(Reason: Wrong player or obsolete) Not enough clearance. Between two ways a minimum height difference of two is needed. Not enough empty space Not enough free space left. Not enough money! You do not have enough\nmoney to buy this! Not initialized Could not initialize library. On narrowgauge track only!\n Must be placed on narrow gauge track only! Only public player can lock games! Only a public player can lock games! Only up and down movement in the underground! An underground tile can be only raised and lowered using the all up/down tools! Out of funds You don't have enough money left for this! Post muss neben\nHaltestelle\nliegen!\n A station extension building\n(ex. post office, warehouse)\nmust be placed on a free tile\nnext to an existing stop/station.\n Protocoll error (expecting game) Protocol error (expecting game) Schiffhalt muss im\nWasser liegen!\n A ship stop can only\nbe placed on water\nnear a dock!\n Server version too new The server uses a more recent program version. Terraforming not possible\nhere in underground view Terraforming is not possible underground. This tunnel branches. You can try Control+Click to remove. This tunnel has more than 2 entrances. You can try ctrl+click to remove this tunnel. Watertable reached Cannot lower tile any further!\nReached the water table. Zughalt muss auf\nSchiene liegen!\n Rail stations must\nbe placed on the\nrailroad track.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Help

Help Index

About Simutrans

%1$s

How to use

%2$s

How to start a game

%4$s

How to play

%5$s

Tools

%3$s

Others windows

%6$s Keyboard Help\n

Keyboard Help

\n Keyboard Binding\n

Keyboard Help

\n

\n

Keyboard Help opens when an unassigned key is pressed or from General Help.\n

\n

\nKey presses are case sensitive (use [Shift] for upper-case letters).\n

\n

\nKeys with assigned functions include:\n

\n

\n[Arrow Keys]: scroll game-view in direction of arrow.
\n[Backspace]: close all windows, toolbars and help-texts.
\n[Delete], or [Escape]: close top window, toolbar or help-text.
\n[Page-Up], or [>]: zoom-in game-view.
\n[Page-Down], or [<]: zoom-out game-view.\n[CTRL] + tool: build straighter ways (even over faster ways) or reverse selection order during query or deletion.
\n

Color-coded terrain according to altitude. Show/hide terrain contour shading. Decrease water height Drain water one level Highlite depots Highlight depots Highlite electrical transmission lines Highlight electrical transmission lines Highlite factories Highlight factories Highlite forests Highlight forests Highlite tourist attraction Highlight tourist attraction Increase water height Raise the water by one level Open station/stop details Show station details Set tile climate %s Convert this ground into %s zone Show how many convoi reach a station Show how many convoys reach a station Show how many people/much is waiting at halts Show how many/much is waiting Show initial passenger departure Show the passenger and post origin. Show mail service coverage/mail network Show mail service coverage Show passenger coverage/passenger network Show passenger network coverage Show the change of waiting at halts Show how waiting changed since last month Show the owenership of infrastructure Show ownership of infrastructure Show usage of network Show vehicles per tile and month Sum of departure/arrivals at halts Show the number of arrived and departed vehicles #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s\nat (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s\nat (%i,%i). Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\nopens a new overland\nbus service between\n%s and\n%s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Map Editing Tools LISTTOOLS Lists MAGLEVTOOLS Maglev Tools MONORAILTOOLS Monorail Tools NARROWGAUGETOOLS Narrow-gauge Railroad Tools RAILTOOLS Railroad Tools ROADTOOLS Road Tools SHIPTOOLS Shipping Tools SLOPETOOLS Landscaping Tools SPECIALTOOLS Special Construction Tools TRAMTOOLS Trams/light rail Tools #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s's has built a new headquarter. Factory chain extended\nfor %s near\n%s built with\n%i factories. Economy going strong:\n%s near %s extends chain.\n%i new factory founded. New factory chain\nfor %s near\n%s built with\n%i factories. Industry growth: New factory chain\nfor %s near\n%s built with\n%i factories. New vehicle now available:\n%s\n \n A new vehicle is\n now available:\n\n\n -- %s --\n\n Now %u clients connected. Right now %u client(s) are connected. Remove vehicle from map. Use with care! Remove vehicle from map. (Use with care!) Screenshot\ngespeichert.\n Screenshot\nsaved.\n Sends the convoi to the last depot it departed from! Sends the convoy to the last depot it departed from. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. With a big festival,\n%s built\na new monument.\n%i citizens rejoiced. #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%.2f$/km %.2f$/m) (%.2f$/km %.2f$/month) (%.2f$/km) (%.2f$/km) (%i)- (%i) \nBauzeit bis until \nBauzeit von \nAppears from \nCan't open heightfield file.\n \nCannot open height map.\n \ndirection: \ndirections: \nelektrified \nelectrified\n \nHeightfield has wrong image type.\n \nHeightfield has wrong\nimage type.\n \nis reserved by: \nreserved by \nminimum speed: \nminimum speed: \nnot elektrified \nnot electrified\n \nRibi (masked) \ndirections\m (masked): \nRibi (unmasked) \ndirections\n (unmasked): \nSet phases: \nSet phases time\n / then \\ then offset \nsingle way \nSingle Way \nway1 reserved by \nway 1 reserved by \nway2 reserved by \nway 2 reserved by %d convois %d convoys %d Einzelfahrzeuge im Depot %d vehicles stored here. %i car(s), %i cars, %i km/h (max. %ikm/h) %i km/h (max. %i km/h) %i years %i months old. Age: %i years and %i months %s at (%i,%i) now public stop. %s has become public interchange. %s building %s %s %s %s %s %s city %d %s %s %d %s %s factory %s %s %s %s yard %s land %d %s %s %d outer %s %s now known as %s. %s is now known as %s. %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s \nbuilt a\nnew townhall\nwhen it reached\n%i inhabitants. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nmaximum speed %3$i km/h\n\ncurrent speed %2$i km/h.\n\n %s\nwas liquidated. %s went bankrupt and\nhas been liquidated.\nAll operation was halted,\nand all assets have been sold. %u Player (%u locked)\n %u Players (%u locked)\n %s - %s
\n [%s]: %s
\n

Error

Error: Double objects in main pak directory

In

Warning

addons for

Warning: Doubled objects in the addon directory

Addons for 1 convoi 1 convoy 1 Einzelfahrzeug im Depot 1 vehicle stored here. 1 minute ago one minute ago 1LIGHT_CHOOSE Brightness: 1WORLD_CHOOSE Settings for a New Game: 2LIGHT_CHOOSE Colours: 2WORLD_CHOOSE Map number: 3LIGHT_CHOOSE Scroll Speed: 4LIGHT_CHOOSE Scroll Inverse 5LIGHT_CHOOSE Pedestrians at stops 5WORLD_CHOOSE Number of cities: 6LIGHT_CHOOSE Pedestrians in towns 6WORLD_CHOOSE Traffic density: 8WORLD_CHOOSE Day and Night cycle A bridge must start on a way! You do not own the ends of this bridge's path, or the path is obstructed.\n Abfrage Inspection Tool Abnehmer Consumer: About scenario Copyright Abriss Destroy/Remove Absenken Lower land Abspanntransformator Transformer Add random citycar Add random city car add server Add server Add stops for backward travel Add stops for backward travel. air runway/taxiway Aircraft Airline aircraft_tab Freight planes AIRTOOLS Airport Tools all convoi tooltips all convoy tooltips allowed climates:\n Allowed climates:\n Alters a schedule. Add/removes stops to/from a schedule Angenommene Waren Goods needed by nearby industries anhaengen Append Anhaenger_tab Trailers Anheben Raise land Appends stops at the end of the schedule Add stop at end of schedule Apply Line Assign line Arbeiter aus: Workers live in: Aufloesen Disassemble Aufspanntransformator Transformer Autohalt muss auf\nStrasse liegen!\n Bus or car stops\nmust be\nplaced on the road. Bahndepot Train depot Bankrott:\n\nDu bist bankrott.\n Bankrupt:\n\nYou are bankrupt!\n battery Battery Baum Tree baum builder Plant trees Baustelle Construction site Bauzeit build time Beenden Quit Beginner mode Beginner Mode Besonderes Gebaeude Tourist attraction BF station bio biological Blockstrecke ist\nbelegt\n \nRail block is in use\nby another train!\n Boden Ground Bonusspeed: %i km/h Max. possible speed: %i km/h bridge is too high for its type! The bridge is too high for its type! Bridge is too long for this type!\n Bridge span is too\nlong for this type of\nbridge.\n Bruecke Bridge Bruecke muss an\neinfachem\nHang beginnen!\n Bridges must\nstart on a\nstraight slope!\n Brueckenboden bridge Build air depot Build aircraft hanger build choosesignals Build platform choose signals Build city market Build a new market in the nearest city. Build drain Transformer station build HQ Build HQ Build land consumer Build a new power station. Build powerline Build Power Transmission Line Build presignals Build twoblock signals Build road depot Build Garage Build ship depot Build Shipyard Build truck depot Build Garage Building costs estimates Estimated construction cost Buildings City buildings Built artifical slopes Build artificial slopes Built random attraction Build a random attraction. Bus_tab Buses Can only move from halt to halt or waypoint to waypoint. Can only move \nfrom halt to halt or \nfrom waypoint to waypoint. Capacity: %.0f MW Capacity: %.0f MW\n Capacity: %d%s %s\n Capacity: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Capacity: %s\n\nProportion: %d (%d%%) Cars are not available yet! Cars are not yet available. cars.\nstate cars\n Cash Account Balance Chat_msg Chat chooses a random map Chooses a random map. citicens Citizens City industries Markets in cities City list City List city_road City road citybuilding builder Build urban buildings CityLimit City limits cl_title Vehicle List cl_txt_sort Sort by: Cleanup schedule Remove double entries Clear block reservation Show/reset reserved ways clf_chk_aircrafts Airplanes clf_chk_cars Buses/trucks clf_chk_indepot in a depot clf_chk_maglev Maglevs clf_chk_monorail Monorails clf_chk_name_filter Filter names: clf_chk_narrowgauge Narrow-gauge trains clf_chk_noincome Make a loss clf_chk_noline No line clf_chk_noroute No route clf_chk_noschedule No schedule clf_chk_obsolete Obsolete clf_chk_ships Ships clf_chk_spezial_filter Special filter: clf_chk_stucked Stuck clf_chk_trains Trains clf_chk_trams Trams clf_chk_type_filter Filter types: clf_chk_waren Filter goods: clf_title Vehicle List Filter Climate Control Landscape Settings closed closed. COLOR_CHOOSE\n Please choose two\ncolours from the\nimage on the right: company_chat Company chat Company_msg Competitors Congratulation\nScenario was complete in\n%i months %i years. Congratulations!\nThe scenario had been \ncompleted within \n%i months and %i years! Connected stops Connected Stops: Connected with server Connected to server Constructed by Painted by Constructed by %s Painted by %s Construction_Btn Construction Costs contains the following doubled objects:

contains the following objects more than once:

convoi %d of %d Convoy %d of %d convoi error tooltips convoy error tooltips Convoi has been sent\nto the nearest depot\nof appropriate type.\n Convoy has been sent\nto the nearest depot\nof appropriate type.\n Convoi is sold when all wagons are empty. The vehicle will be sold as soon as it is completely empty. convoi mouseover tooltips convoy mouseover tooltips convoi passed last\nmonth %i\n \nconvoys passed last\nmonth: %i\n Convois Convoys Convois: %d\nProfit: %s Vehicles: %d\nProfit: %s Copy Convoi Copy convoy Copy the selected convoi and its schedule or line Copy the selected convoy and its schedule or line cost for removal Cost to remove Cost: %8s (%.2f$/km %.2f$/m)\n Cost: %s (%.2f$/km %.2f$/month)\n curiosity builder Curiosity builder curlist_title List of Tourist Destinations CURRENCY $ deactivated in online mode Not active while online Deccelerate time Decelerate time Del Stop Remove Delete Line Delete line Delete the selected line (if without associated convois). Delete selected line (if no convoys are associated) Demand: %.0f MW Demand: %.0f MW\n Denkmal Monument Departs at Departs at (dd:hh:mm) Departure after %% load or waiting for (dd-hh-mm) Departure board Departure time Der Tunnel ist nicht frei!\n Tunnel is not empty.\n Destination Arrivals Destroying map ... Destroying old map ... Die Bruecke ist nicht frei!\n The bridge is not free!\n direct_chat_to: Direct message to directmail Leaflets Direkt erreichbare Haltestellen Direct routes from here disable midi Mute MIDI music Dock dock Dock must be built on single slope! Docks must be built on a single-sloped tile. dp_title Depot list Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen You have %d months to repay your debt. Durchsatz Max. Economy Economy and cities Eigenbesitz\n Public property\n Ein %s\npasst hier nicht.\n A '%s'\ndoesn't fit here!\n Einstellungen aendern Options Electrics_tab Electrics enlarge map Enlarge map Enter Password Change player name and password Erzeuge neue Karte.\n Please wait while a new\nmap is being created.\n\n(This may take a few \nminutes for big maps.)\n Es wird bereits\nein Fahrplan\neingegeben\n A schedule is\nbeing planned.\nFinish it first\nbefore re-scheduling!\n Extract files Extract pak files from zip. Fabrikanschluss Connected factories Fabrikname Factory name factory details Factory links factorybuilder Factory builder Fahrplan Schedule Fahrtziel Destination: Fahrzeuge koennen so nicht entfernt werden Vehicles cannot be\nremoved this way!\n Fahrzeuge: Vehicles: Farbe Player Colour Fast forward Fast Forward Ferry_tab Ferries Fertig Done Filename Filename: Finanzen Finances find mismatch Compare your pak fl_title Factory List Flug_tab Passenger Aircraft follow me Follow Follow the convoi on the map. Follow the convoy on the map. font size Font size (only .ttf and .otf fonts) Found new city Found a new city Fracht Freight Frame time: Frame Time: Friction: current friction factor: fuel_cell fuel cell Full load Wait for minimum load Fundament Foundation Fussgaenger Pedestrian GAME PAUSED Game Paused Game_msg General Gebaeude Building generate script Generate an infrastruktur macro script Generation: %.0f MW Generation: %.0f MW\n Gewicht Weight Gewinn Income: Give the selected vehicle(s) an individual schedule Edit the selected vehicle(s) individual schedule or assigned line gl_btn_sort_catg Category gl_title Goods List go home Go to Depot Gross Profit Cash Flow Groundobj Object groundobj builder Place Landscape Objects Growth City Growth GUI settings GUI H stop Haus kaufen Buy house Helligk. Display Help text not found The help text is missing. hide all building hide all buildings hide city building hide city buildings hide transparent Transparent instead of hidden hide trees Hide trees Hier warten/lagern: Goods and passengers waiting: Higher transport fees, crossconnect all factories Higher transport fees, disable Just-in-time. highlight single ways Indicates the end/interruption of traffic ways. Highlite schedule Highlight stops in schedule hl_title Station List hl_txt_filter Filter: hl_txt_sort Sorted by: hlf_chk_airport Airports hlf_chk_anleger Dock hlf_chk_bahnhof Railway station hlf_chk_bushalt Bus stop hlf_chk_frachthof Loading bay hlf_chk_keine_verb No connection hlf_chk_maglevstop Maglev station hlf_chk_monorailstop Monorail stop hlf_chk_name_filter Filter names: hlf_chk_narrowgaugestop Narrow-gauge station hlf_chk_overflow Overcrowded hlf_chk_spezial_filter Special filter: hlf_chk_tramstop Tramway stop hlf_chk_type_filter Filter types: hlf_chk_waren_abgabe Goods out: hlf_chk_waren_annahme Goods in: hlf_title Station List Filter Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depot cannot be found.\nYou must send the\nvehicle there manually. hydrogene Hydrogen ignore climates Ignore climates In the industry legend show only currently existing factories Show only currently existing factories in the industry legend Increase Industry density Increase industry density industrial building Industrial buildings Infinite mouse scrolling Infine map scrolling Infinite scrolling using mouse May not work with touch screens! Init map ... Initializing map ... Ins Stop Insert Stop Insert stop before the current stop Insert stop before current stop Install paks Pak sets are downloaded and installed. Intercity road len: Intercity road length: invalid undefined. Invalid coordinate Invalid Order isometric map Isometric view join game Play Online Kann Spielstand\nnicht laden.\n Cannot load saved game! Kann Spielstand\nnicht speichern.\n Cannot open\ntarget file\nfor writing! Kein Besitzer\n Without owner\n keine none Keine Einzelfahrzeuge im Depot No vehicles stored here. Keyboard_Help\n Keyboard Help\n koord Coordinates Kreuzung Crossing labellist_title List of Markers Lade Relief Load Height Map Laden Load a new game Land industries Industry Chains: LANG_CHOOSE\n Please choose your\npreferred language:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Last Month: Last Year Last Year: leer empty Legend Map legend Leistung Power Leistung: %d kW Power: %d kW Leitung Power line letzen Monat: diesen Monat: last month: this month: LKW_tab Trucks Load game Load Game load height data from file Load height data from file. Load scenario Load Scenario Load settings from Apply settings from loaded passenger/freight Sort passengers/freight by Loading (%i->%i%%)! Loading (%i->%i%%) Loading time: fully loaded in: Lock game Disallow player changes (requires confirmation). Lokomotive_tab Locomotives m3 m maglev_track Maglev Track Maglevdepot Maglev Depot Mailbox Messages Mailbox Options Message Options make stop public (or join with public stop next) costs %i per tile and level Make stop public (or join with neighbouring public stop), costs %i$ per tile and level Map roughness Map roughness: map view Map map zoom zoom Margin (%%) Margin Marker Place Marker Max income: Max. Income: Max. waiting time Max. Waiting time (dd:hh:mm) Maximum 254 stops\nin a schedule!\n Maximum of 254 stops\nin a schedule!\n maximum length of rivers Max. length of rivers Maximum tile height difference reached. The maximum height\ndifference between\ntwo tiles has\nbeen reached. Maxspeed Maximum Speed Median Citizen per town Median citizens per city: Meldung Message Menge amount MessageOptionsText \nNew Year\n\nPlayers News\n\nCity News\n\nNo Route\n\nNew Destinations\n\nNotification\n\nNew Vehicles\n\nStation Full\n\nProblems\n\nTraffic Jams\n\nScenario\n\nGeneral Chat\n\nCompany Chat\n\nPrivate Chat\n\n min min. minimum length of rivers Min. length of rivers Minimum load Minimum load (%%) Missing pakfiles Objects are missing! Monate alt months old. monorail_track Monorail Track Monorailboden Elevated way Monoraildepot Monorail Depot Monthly departures Fixed departure times Mountain height Mountain height: Movingobj moving object Music playing disabled/not available Music disabled/unavailable. mute sound Mute sound Narrowgauge Narrow-gauge Narrowgauge are not available yet! Narrow gauge tracks are not available yet. narrowgauge_track Narrowgauge Track Narrowgaugedepot Narrow-gauge depot Net ID: %p Net ID: %p\n Neue Karte New Game Neue Welt Create a New Game new convoi New convoy New Line New line no convois No convoys No goods are loaded onto this convoi. No goods will be loaded onto this convoy no load Unload only No stop here! Tool must be used on stop tiles. No terminal station here! Can't build a terminal \nstation here! An end piece \nof flat road/track is needed. no timeline all eras no tree No trees No. of Factories Factories and shops: Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n "Where do I go?"\nYou must give this convoy a schedule or assign it to a line before giving it the go order. nord North nordost Northeast nordwest Northwest Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! The vehicle's schedule\nmust not be changed\nduring route-finding. Not enough fields would remain. Not enough fields would\nremain around this farm. Ok OK Oktober October on the departure/month first at (dd-hh-mm) On this map, you are not\nallowed to change player!\n This game is locked.\nNo player changes are possible.\n Only city chains Build city industry chain Only land chains Build land industry chain open open. Operation Operation Costs Ops Profit Operational Profit Optionen Options ost East Pak which may cause severe errors: Following objects (pak) are missing, which may cause failure of transport chains: Pak which may cause visual errors: The following objects (pak) were replaced by similar objects: Pak(s) different: Different pak(s): paletten crates Pas_tab Passenger Trains Passagiere Passengers Passagierrate Passenger level Passagierziele Pass./mail destinations Password Password: Pause Pause/Unpaused Pax <%i> Mail <%i> Passengers <%i> Mail <%i> PaxDest Destinations Percent Electricity Electricity output (%% demand): personal_chat Private chats Pipette Rebuild the selected object Planes are not available yet! Aircrafts are not yet available. Plant tree Plant trees player Player player -1 Human player player 0 public service player 1 Napik 128 AS player 11 Player 11 player 12 Player 12 player 13 Player 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Freight Forwarder VM player 5 H-Trans Ltd player 6 PSK & Co KG player 7 Player 7 player 8 Player 8 player 9 Player 9 Please choose vehicles first\n Please choose vehicles first!\n Post Mail Postrate Mail level Problems_msg Problems Produces: %.1f units/minute currently %.1f/minute Production of %s has been stopped:\n%s\n Production of %s has been discontinued:\n%s\n Produktion Production: promote to line Promote to line public_chat General chat q1 Spring q2 Summer q3 Fall q4 Winter Random map Random Map Rathaus City Hall ratio_pax Pass. Ratio Reliefkarte Map Remove Remove label remove airstrips Remove runway or taxiway remove channels Remove channels remove interm. signals Remove interm. signals remove maglev tracks Remove maglev tracks remove monorails Remove monorails remove narrowgauge tracks Remove narrowgauge tracks remove powerlines Remove powerlines remove roads Remove roads remove tracks Remove tracks replace other signals Replace other signals replace stop Replace stop residential house Residential houses Restwert: Resale Value: Retire. date: Retire Date: return ticket copy backward Revenue Vehicle Earnings Revert schedule Revert changes Roadsign Road Sign Rotate Building Rotate building Running cost Running costs sack bags sail wind Save Save game Scenario Debug Debug Scenario Error Log Script Errors Scenario Info Short Info Scenario information Current scenario details Scenario Result Progress Scenario Rules Rules Scenario_ Scenario Schienentunnel Build railroad tunnel Schiff_tab Ships Schiffdepot Shipyard Schleppkahn_tab Barges Sehenswuerdigkeit Tourist attraction Sell the selected vehicle(s) Sell the selected vehicle(s). sended Sent Mail SEP_FRACTION . SEP_THOUSAND , SEP_THOUSAND_EXPONENT 3 Serves Line: Serves line: set signal spacing Set signal distance Setting Settings shops and stores Shops and offices show all building Show all buildings Show also vehicles no longer in production. Show also vehicles that are no longer in production. Show also vehicles that do not match for current action. Show also vehicles that can't be used by the selected action. Show contour Show heights Show even servers with wrong version or pakset Show also servers providing the wrong version or pakset. show grid Show grid Show industry Industry List Show legend Selections Show map scale Colour Codes Show outline infrastructure only show station coverage Show station coverage show waiting bars Show waiting bars Show/hide statistics Show/Hide Statistics Shows buttons on network overlay. Show option to overlay a connection network. Shows consumer/suppliers for factories Show consumers/suppliers for factories Shows the currently selected schedule Show the currently selected schedule shuffle midis Shuffle tracks signal spacing Signal spacing Sim: Simloops: sliced underground mode Sliced map view Sort by sort by special freight Special freight Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Speedbonus in percent\nrequires minimum speed for road %i km/h,\nrail %i km/h, ships %i km/h, planes %i km/h, Speedlimit Speed limit Speichern Save Spieler Player Spieler(mz) Players Spielerliste Player List Spielstand wurde\ngeladen!\n \nGame successfully loaded.\n Spielstand wurde\ngespeichert!\n \nGame successfully saved.\n Sprache Language Sprachen Languages Stadtinformation City Statistics Start the selected vehicle(s) Dispatches the selected vehicle(s). Starte Spiel Start Game station labels Stations Station tiles: Station Tiles: Station_msg Stations Status Stop status Step timeline one year Step time-line by a year. Stopping permissions Other player who can use this stop Strassendepot Garage Strassentunnel Build road tunnel sued South suedost Southeast suedwest Southwest Suppliers Suppliers: Tage alt days old. The generated script was saved!\n Macro saved successfully.\nCan be replayed with the script tool. There are still vehicles\nstored in this depot!\n There are vehicles\nin this depot!\n This Month This month: This Year This Year: Tile not empty. Clear the tile before\nusing a Slope Tool. timeline Obey era tl_title City List To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Traffic in %1$s on the rise:\n%3$i tax payer made the construction\nof %2$s reality. To heavy traffic\nresults in traffic jam.\n Heavy traffic\ncauses a traffic jam.\n tonnen t Total inhabitants: Total inhabitants: Tourist attractions Tourist attractions: Tourists Attractions Town_msg New Destinations Town: %s\n City of %s.\n Towns Cities traffic settings Traffic Trains are not available yet! Trains are not yet available. tram_track tram track Tramdepot Tram depot Trams are not available yet! Trams are not yet available. Transformer only next to factory! Transformers must be on\nempty flat ground\nadjacent to a factory! transparencies Transparency transparent station coverage Transparent station coverage Transported Trips TrolleyBus_tab Trolleybuses tt_Other Other Tunnel muss an\neinfachem\nHang beginnen!\n Tunnels must\nstart on a straight\nslope!\n Tunnel must start on single way! Tunnels must start on a single way. Tunnelboden Tunnel underground mode Underground view UNDO failed! Undo is not possible anymore.\nYou can undo a route \nconstruction only as long as \nno signals/stations/stops or \nanything else was built on \nthe way. units/day /month, Update Line Edit line upgrade HQ Upgrade HQ Use timeline start year Use timeline from year: Vehicle %s can't find a route! Vehicle %s\ncan't find a route! Vehicle %s is stucked! Vehicle %s is stuck! Vehicle details Vehicle Details Verbrauch Consumption: Vergroessere die Karte\n Enlarge map.\n Verkauf Remove Now! verkaufen Sell Verkehrsteilnehmer City Cars Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n You have %d months\nto pay off your debt.\n vh_title List of all vehicles via via (detail) via %s\n via %s\n via Menge via (amount) via owner via (owner) voranstellen Put in front Waggon_tab Cars walking Walking Warnings_msg Traffic Wasser Water Water Canal Water level Sea level: way %s cannot longer used:\n Way-type %s can no longer be used.\n way %s cannot longer used:\n%s\n way-type %s can no longer be used:\n way %s now available:\n Way-type %s is available now.\n Wegpunkt Waypoint Wert Value west West withdraw Retire WRONGSAVE \nIncompatible saved game.\nCannot load file.\n Zielort destination zooming in Zoom in zooming out Zoom out Zu nah am Kartenrand You cannot build\nso close to the\nedge of the map. #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Levitating on magnets at\n%.1f km/h by %s:\nNew world record! New world record for monorails: %.1f km/h by %s. One rail built for speed:\nExactly %.1f km/h\nreached by %s\nmakes this a new world record! New world record for motorcars: %.1f km/h by %s. Only skid marks are left\nfrom speeding at %.1f km/h\nby %s\nto reach a new world record! New world record for planes: %.1f km/h by %s. Shrinking distances again:\n%2$s flew %1$.1f km/h.\nNew world record! New world record for railways: %.1f km/h by %s. Thundering over the tracks\nat %.1f km/h.\n%s sets a new world record for trains.\m New world record for ship: %.1f km/h by %s. Blue Riband for cruising\nat %.1f km/h was won by\n%s. #_________________________________tooltip_text__________________________________ #_________________________________tooltip_text__________________________________ Mirrors order of stops Invert stop order Revert to original schedule Revert all schedule changes. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL wick &1_CITY_SYLL chester &2_CITY_SYLL ton &3_CITY_SYLL ville &4_CITY_SYLL den &5_CITY_SYLL borough &6_CITY_SYLL ham &7_CITY_SYLL field &8_CITY_SYLL mouth &9_CITY_SYLL lock &A_CITY_SYLL cross &B_CITY_SYLL by &C_CITY_SYLL bury &D_CITY_SYLL stead &E_CITY_SYLL well %0_CITY_SYLL Pine %1_CITY_SYLL Apping %2_CITY_SYLL Malli %3_CITY_SYLL Ash %4_CITY_SYLL Spring %5_CITY_SYLL Polling %6_CITY_SYLL Baring %7_CITY_SYLL Hepp %8_CITY_SYLL Brent %9_CITY_SYLL Ren %A_CITY_SYLL Old %B_CITY_SYLL Hill %C_CITY_SYLL New %D_CITY_SYLL Tor %E_CITY_SYLL Wal %F_CITY_SYLL Oak 1center %s %s 1extern %s branch %s 1suburb %s %s %s 2center %s central %s 2extern %s external %s 2suburb %s Greenfields %s %s 3center %s main %s 3extern %s land %s 3suburb %s village %s %s 4center %s inner %s 4extern %s outer %s 4suburb %s rural %s 5center %s downtown %s 5extern %s transfer %s 5suburb %s town %s 6center %s hub %s 6extern %s exchange %s 6suburb %s outskirt %s %s 7center %s city %s 7extern %s plains %s 7suburb %s park %s 8center %s business %s 8extern %s %s margin %s 8suburb %s satellite %s 9center %s axis %s 9extern %s bypass %s 9suburb %s mezzo %s simutrans-124.5/simutrans/text/en/000077500000000000000000000000001520675460500171775ustar00rootroot00000000000000simutrans-124.5/simutrans/text/en/airtools.txt000066400000000000000000000107511520675460500216000ustar00rootroot00000000000000Airport Tools

Airport Tools

Airport Tools contain tools to construct an air transport network. Tools may build or remove: taxiways & runways for aircraft, Airstops (used to pick-up & drop-off, cargo or passengers), aircraft depots (to purchase and manage air transport vehicles) and and various airport buildings. If playing with timeline, then as time passes in Simutrans more tool-options may appear.

Click airplane-icon at top of game-view to open toolbar.
Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name & where appropriate: construction cost, maintenance cost in brackets, and maximum speed limit.

{Tips: One way to build a simple airport is:
i) construct and connect Taxiway(s) and Runway(s);
ii) place airstop(s) on end(s) of taxiway(s);
iii) add airport buildings as required.}

Tools may include, from left to right:

Taxiway: tool builds ground movement areas for aircraft to go from an Airstop to a Runway. Taxiways are built on level ground in game-view.
IMPORTANT: If Taxiways are connected to the end of Runways aircraft may not take-off and land.
To build a Taxiway: click on tool to select (changes cursor to taxiway), then click on terrain for start-point (shows a bulldozer in game-view and displays map co-ordinates at bottom right of display), & finally click on terrain or runway for end-point.
{Tips: Use Destroy/Remove to remove individual pieces of Taxiway. Undo [z] does not refund constructions costs.}

Runway: tools build an airstrip, used by aircraft to take-off and land. Runways are built on level ground in game-view and may cross each other.
To build a Runway: click on tool to select (changes cursor to way), then click on terrain for start-point (shows a bulldozer in game-view), & finally click on terrain for end-point.
{Tips: Leave ends of Runways unconnected otherwise aircraft may not take-off or land. Use Destroy/Remove to remove individual pieces of Runway. Undo [z] does not refund constructions costs.}

Remove airstrips: tool removes a segment of Taxiway or Runway, when no aircraft are present, between two points in game-view. Use of tool incurs a construction cost.
To remove an airstrip: click on tool (changes cursor to a red-cross); then click on strip that is to be deleted (selects deletion-point shown by a red-cross in game-view); & finally click at another point on strip, to remove portion up to first deletion-point. {Tips: Leave end of Runways unconnected otherwise planes may not take-off or land. Use Destroy/Remove to remove individual pieces of airstrips.}

Air Depot: tools build hangars for purchasing & managing aircraft. Hangars have a maintenance cost & are built on the end of a piece taxiway.
To build a Hangar: click on tool (changes cursor to a depot), then click on an end of a taxiway.
{Tips: Remove depots with Destroy/Remove.}

Airstops&boarding bridge: tools build Stops in game-view, for aircraft to pick-up and drop-off passengers or cargo.
An Airstop when not built adjacent to an existing Stop will create a new Stop.
Airstops are built on the end of a piece taxiway; and have a maintenance cost and catchment area for goods, passengers & mail. An aircraft will only land at an airport if there is an free Airstop available.
To build an Airstop: click on tool to select and then click on an end of a taxiway.
{Tips: Remove Airstops with Destroy/Remove. Press [v] to show/hide catchment area for goods & passengers in game-view.}

Airport buildings: tools construct extensions for Stops which may increase maintenance costs and the capacity & catchment area for goods & passengers. In the corner of some tool-options an icon (used in Station List & Stop Information) shows which items the extension enables the Stop to handle.
To build an extension: click on tool to select extension (changes cursor to extension), then click required position, next to an existing Stop in game-view. The new extension is now considered to be part of the Stop.
{Tips: Remove extensions with the Destroy/Remove. Press [v] to show/hide catchment area for goods & passengers in game-view.}

simutrans-124.5/simutrans/text/en/baum_build.txt000066400000000000000000000052771520675460500220560ustar00rootroot00000000000000Plant Trees

Plant Trees

With the "Plant Trees" dialog, you can plant individual trees. It can be opened via the Map Editor.
TIP: This bar also contains the Add Forest button, which allows you to add entire forests with countless trees.

At the top are the selection for a specific climate zone.
All means all available trees are listed, regardless of the climate zone, but only trees that match the soil's climate zone can be planted. If you want to ignore the climate zone check, you can activate the Ignore climates: option next to it. Then, the climate zone will no longer be checked when planting the tree.
If you select a climate zone based on the intended soil, only the trees that match that zone will be displayed.

Below, you can change the display of the name and the sorting in the list.
Translation: Show and sort by names as they are used in the game. This depends on the selected language. If no translation is available for the selected language, the object name is displayed.
Object: Show and sort by program internal object names.
The button next to it reverses the sorting to descending.

Selection list

On the left is the selection list with the trees. Click on one to see more information about that tree.
On the right, the information of the selected tree is displayed.
Name: Name of the tree.
any explanatory text
Allowed climates: All suitable climate zones.
Seasons: Numbers of seasons for which graphics available.
-- 2 means there are only graphics with and without snow. This is suitable for conifers.
-- 4 means spring, summer, autumn, and only one graphic for winter.
-- 5 means spring, summer, autumn, and winter with and without snow.
Painted by: The name of the author of the graphics.
Image of the tree.
To plant the tree, click in the game-world window on the desired location after selecting it from the list.

Placement options

Ignore climates: This option deactivates the climate restrictions, allowing you to plant the tree on non-allowed climates.
Random age: If active, a random tree age is selected. Normally, they are planted as small trees at a young age, grow to old age over the course of many years, and finally die. Then it's not long before another young tree grows in the same spot.
Caution: Many trees planted at the same age will become bald and die at the same time. This then resembles forest dieback.

simutrans-124.5/simutrans/text/en/chat.txt000066400000000000000000000061421520675460500206620ustar00rootroot00000000000000The Chat Window

The Chat Window

The chat window can be accessed during multiplayer-games, via the main toolbar. Messages can be exchanged with all active players.

On top, you will find the 'transparent background' checkbox, by which the see-through feature can be toggled on and off. This can be especially helpful on small-screen-devices.
Following, the three tabs are listed, housing the three different chat types:
- General chat
- Company chat
- Private chat
Afterwards the message log is shown, where all messages are displayed as speech bubbles. If nothing is displayed in this area, then simply no messages have been sent yet.
At the bottom are placed the input line and next to it an arrow-symbol, which allows you to add the current view's coordinates to your message. Attached coordinates will be shown as an arrow-symbol in the chat log, next to your speech bubble.

For to send a message, click in the input line and type your text and send it by pressing the enter key.
Your message will immediately appear in the log.
For the cursor to leave the input line simply hit enter again or just click anywhere.
The esc key will close the chat window, if still active.

Received messages are displayed, other than in the log only, also prompted in the news bar at the bottom of the screen and are indicated by a white number on a red dot within the chat-window-button in the tool bar.

Messages in the general chat are public, so any member of the game can see them.
Further, the general chat's log will persist for the hole duration of the current multiplayer map.

Company chat messages are visible "but" for players active in the same company. Tough anybody can select any given company and therefore read their company chat, even if the company is password protected.
This chat log will be conserved throughout the duration of the map as well.

The private chat allows two co-players to exchange messages without anyone else being able to read along.
While in this tab any currently active co-player can be selected and texted to.
Alternatively the arrow-symbol next to a player's name in any log can be clicked on to, to directly transit to the private-chat.
The private chat's log will be erased when leaving the multiplayer session.

Please note:
- hitting the tabulator key will send any existing text in the input line as well. Further, typed in text can unintentionally get send, when simultaneously a new player gets connected to the current game. >
- Message length is limited to 226 characters. Word wrap (paragraphs) or emoji-graphics cannot be inserted respectively be displayed.
- Double-clicking on any bubble will copy it's contained text to your clipboard, accompanied by a notification 'copied'. This is especially helpful when exchanging article URLs from the forum for instance.

simutrans-124.5/simutrans/text/en/citybuilding_build.txt000066400000000000000000000077041520675460500236150ustar00rootroot00000000000000Build urban buildings

Build urban buildings

The Build urban buildings window allows placement of individual building. It is a map customisation option, available if you switch to public service player and then open the Map Editing Tools.

The dialogue is divided into four sections:
1) Top left: Filter and sort options.
2) Bottom left: A selection list of available buildings.
3) Top right: Placement options.
4) Bottom right: Information and picture of the chosen building.

1 - Filter and sort options

Obey era: The selection list will only contain buildings that are available at the current Simutrans year. This is mutually exclusive with "Available at custom date" option.
Available at custom date: The selection list will only contain buildings that are available at the specified date. This is mutually exclusive with "Obey era" option.
Show obsolete: Expand the selection list to also contain buildings that are obsolete at the current Simutrans year.
Climate: Filter by allowed climates.
Sort by: Sort the list by any of the following parameters:
Translation: Show and sort by names in the given language as defined in the pakset. If there is no translation available, the object name will be used.
Object: Show and sort by Simutrans internal object name.
Pax Level: Sort by quantity of passengers generated or demanded.
Mail Level: Sort by quantity of mail generated or demanded.
Intro. date: Sort by date of introduction (when the building starts appearing).
Retire date: Sort by date of retirement (when the building stops appearing).
Size (area): Sort by size of building in tiles (Note: some paksets may only have buildings of one tile).
Residential houses: Show/hide residential buildings.
Shops and offices: Show/hide commercial buildings.
Industrial buildings: Show/hide industrial buildings.

2 - Selection list

In the selection list are shown all urban buildings that are available with the options specified. Click on a building in the list to see more information about that particular building. To place the building, click in the game-world window on the desired location after selecting it from the list.

The color of the text has the following meaning:
Blue text signifies a residential building.
Green text signifies a shop or office building.
Black text signifies an industrial building.

3 - Placement options

Ignore climates: This option deactivates the climate restrictions, allowing you to plant the tree on non-allowed climates.
Rotation: If the chosen building can be shown from different angles, then it is possible to chose a specific view of it here. The image will change to reflect this. If this option is set to random a random view of the building will be used.

4 - Information

Information about buildings contains:
Type: Building type (Residential, Industrial, or Shop and offices).
Name and Description: It will display only the description if it has no name. It will display the object name only if it has no name and no description.
Allowed climates: Climates where the tree is allowed to be planted (if "Ignore climates" option is not set).
Passenger level: Metric of quantity of passengers generated or demanded.
Mail level: Metric of quantity of mail generated or demanded.
Introduction date: Date since the building appears.
Author: The name of the author who painted the object (if available).
Image: Image of the building.

simutrans-124.5/simutrans/text/en/citylist_filter.txt000066400000000000000000000043561520675460500231610ustar00rootroot00000000000000City List

City List

The City List provides information about cities. This window consists of two separate tabs: one for a list of cities and another for charts.
Open the list by clicking on City List in the Lists menu or by pressing [T].

- Total inhabitants: the total number of citizens on the map (the total population of all cities). Recent increase in population is shown in brackets.

The City List tab lists every city on the map, displaying their names, population and growth factor.

Sorted by: provides options on how the list is sorted.

- Name sorts the list alphabetically by city name.
- Citizens sorts the list by population.
- City Growth sorts the list by growth rate.

- ascending / descending reverses the order of the list.

Click on an item in the list to reveal more information about that city.

The Charts tab provides information about the cities collectively.

- Citizens: total population of all cities (same as Total inhabitants).
- City Growth: total combined city growth of all cities.
- Cities: total number of cities on the map.
- Factories: total number of factories on the map.
- Convoys: total number of transport vehicles.
- City Cars: total number of city cars.
- Pass. Ratio: percentage of total passengers transported.
- Passengers: total number passengers generated (including those who have not been transported).
- Sent Mail: percentage of total mail delivered.
- Mail: total mail generated (including that which has not been transported).
- Arrived: the number of times the storage of every factory was checked where that storage was not empty.
- Goods: the number of times the storage of every factory was checked.

simutrans-124.5/simutrans/text/en/citywindow.txt000066400000000000000000000112441520675460500221420ustar00rootroot00000000000000City Statistics

City Statistics

City Statistics shows statistics about a selected urban area and can be used to change the name of it. An urban area is a city, town or village; below the term city is used for any urban area.

Each city has its own Town Hall around which it grows. Transportation of passengers and mail furthers growth, which involves roads being built or taken over by the public service player; new city buildings and industries appearing; and city limits extending.

Click on a Town Hall in game-view with the Inspection Tool or a city listed in City List to open City Statistics. Here you can see and modify the name of the city, some statistics, two mini-maps, and a graph where you can display the development over time for certain factors pertaining to the city.

Name: the assigned name of a city, town or village appears in a box at the top of City Statistics.
Names of such urban areas are used to create names for stops within the city limits. To change the name simply click on the box and type in a new name.
{Tip: Use [!] to toggle the display of the name of a city above the Town Hall in game-view. See readme_citylist.txt (in ...simutrans/text/) for more information on how city names are generated}.

Under the name box are given some basic city statistics:
City size: its current number of citizens. The recent increase in population is shown in brackets.
Buildings: number of city buildings (residential/commercial/industrial) in the city.
Map co-ordinates: indicating the boundary of city limits (these will change as the city grows). The first pair of values indicates the top left corner, and the second the lower right corner. As cities grow, city limits may overlap.

Current numbers of unemployed and homeless inhabitants of the city. These will increase when the city expands its limits, and decrease when new city buildings are erected. The balance between these counters determines which type of city building (residential/commercial/industrial) will be constructed.

The Allow city growth button allows you to toggle city growth on and off.

Mini-maps of the game world in City Statistics show the destinations of passengers and mail generated within the city as coloured dots.
The right hand mini-map shows values for the current game calendar month, and the left one for the last game calendar month. The colour of each dot indicates if the destination can be reached or not:
- yellow dot: there is a route and an uncrowded stop to start journey (happy face shown in Stop Information).
- dark orange dot: there is no route to this destination.
- orange dot: the stop at the start of the journey was overcrowded (unhappy face in Stop Information). There is no check for a route if station is overcrowded.

Below the mini-maps there is a graph showing statistics for the city.
On the y-axis quantity is shown and on the x-axis time. Click on graph index tabs to change the scale of the x-axis:
Years: shows annual values for last 12 game calendar years.
Months: shows monthly values for last 12 game months

By clicking on the graph option buttons, you activate and deactivate the curve for each given button. When a button is selected it is shown indented.
The curves match the colour of the corresponding option button:
Citizens: The number of people that live in the city
City growth: Changes in population (dependent on city size and transport service provided for passengers and mail).
No. of buildings: How many city buildings there are in the city.
City cars: How many city cars have been generated
Trips: How many passengers that have entered the transport network (shown as a happy face in Stop Information)
Passengers: The total number of passengers generated in the city.
Sent mail: Mail that has entered the transport network
Mail: Total number of mail generated in the city.
Supply:Number of times there were goods in storage at city factories
Goods:Number of times storage of city factories have been checked.

{Tip: Settings related to cities can be changed in simuconf.tab and cityrules.tab}.

simutrans-124.5/simutrans/text/en/climates.txt000066400000000000000000000152521520675460500215460ustar00rootroot00000000000000Landscape Settings

Landscape Settings

This window sets options for topographical and meteorological aspects of the game-world. It opens along with Create a New Game window.

To adjust each setting, click the arrow-buttons on the side of each option, or input a number in the box.

Sea level - Sets how high the Sea level is on the map. The default setting is -2 values ranging from -40 to 20 are possible. The lower the level, the more land appears. Conversely, higher values result in an expansion of sea surface and less land. Construction is possible up to 3 levels below the sea level (e.g., for tunnels), but never deeper than -40. The larger the areas covered by seas, the less space remains available for cities. In such cases, transport via ships may be the only feasible option. Note: A high "Maximum Sea Height" (see the parameter below) can also result in large, sea-like lakes.

Mountain height - The default setting is 160; depending on the "Map roughness" setting (see the next parameter), values between 0 and 1000 are possible. With a Mountain Height of 1000 and a "Map roughness" of 0, mountains on a sufficiently large map will reach a height of approximately Level 40. Mountains that are excessively high and steep offer little space for cities and industries.

Map roughness - Sets the roughness of the terrain. The default setting is 4. Depending on the "Mountain Height" parameter, values ranging from 0 to 10 are possible. This parameter determines the density of smaller hills and valleys, the rugged of the terrain generated on the map. A terrain that is too hilly may be unable to generate as many cities, industries, or connecting roads as specified in the settings.

Winter snowline - Snow falls during the winter season down to this specific elevation. Over the course of the year, the snow line descends to this value by January and rises back up to the level of the "eternal ice" (glacier zone) by July. This value has no influence on gameplay mechanics and serves only visual purposes.

Wind direction - The prevailing wind direction. This parameter only takes effect if climate zones are calculated based on weather patterns. In that case, trees can be distributed according to humidity levels (rainy areas).

Tabs: Climate from height / Climates from weather - This is where you can decide which method is used to calculate the climate zones and determine the boundaries of each individual zone. Only the currently visible values are relevant for generating the new map.

Climate from height

Elevation Range (From/To): For each climate zone, you can specify the elevation range here using the game's absolute height levels. The minimum elevation is sea level (see the 1st parameter above); the maximum value is 127. Elevation ranges are permitted to overlap. The order of the climate zones is arbitrary; for instance, Tundra may follow Desert, and Tropical climate may follow that.
Attention: Every elevation level should be assigned to at least one climate zone; otherwise, the map may look somewhat strange. Depending on the Pakset in use, buildings, industries, and vegetation may be tied to specific climate zones and will therefore only be constructed within those zones. The start of the Glacier Zone (or Polar Climate) corresponds to the former "summer snow line." From this point upward, winter conditions prevail year-round. The Glacier Zone features very few, or no cities, industries, or vegetation. Note: Some Paksets require an even distribution of all climate zones across the map area to ensure proper functioning of factory chains, as the placement of their associated factories is also dependent on the specific climate zones.

Climate Region Size: - The default setting is 10%; values from 0% to 100% are possible. This value becomes only relevant when climate regions overlap; in such cases, it influences the size of the resulting overlap zones.

Climates from weather

Elevation Increase (Land) - The default setting is 1; values from 0 to 10 are possible.

Elevation Increase (Water) - The default setting is 1; values from 0 to 10 are possible.

Humidity Limits: The default range is 65% to 75%; values from 0% to 100% are possible.

Temperature Limits - The default values are 20, 15, 8, 0, -10; values from -30 to 30 are possible. At these 5 temperature thresholds, the climate zone transitions from desert climates to polar climates.

Summer snowline - The snowline during summer (eternal snow). The default is 32 possible values are from "Sea level" up to 127. Arctic climate begins from here. Very few cities, industries, or vegetation will appear in these areas.

Trees, Lakes, Rivers

Trees - The default setting is "Random"; the available options are "None", "Random", and if climate zones are assigned based on weather "Humidity". "None" means that not a single tree will be generated on the new map.
Attention: Typically, logging camps are placed within forests; however, if "None" is selected here, logging camps will be placed anywhere on the map, even if the player has subsequently planted forests.
On big maps trees take up a lot of CPU time to render. Use "None" option on PCs with slower CPU or low RAM.

max. lake height - The default is 8 values ranging from Sea Level (the 1st value listed above) up to 127 are possible.
Warning: If this value is set too high, large lakes may form, filling vast valleys and significant portions of the map with water.

Number of rivers - The default setting is 16, values ranging from 0 to 1000 are possible. Sets the maximum number of rivers that can be generated and used in a terrain map. Narrow rivers cannot be navigated by boats or ships of any kind.
Attention: If you intend to draw the rivers yourself, enter a value of at least 1 here. Otherwise, the program will assume that there are no rivers on the map and will simply place buildings intended to be situated along rivers at random locations.

Min. length of rivers - The default setting for the minimum river length is 20, values ranging from 0 to 16 tiles below the maximum length are possible.

Max. length of rivers - The default setting for the maximum river length is 512, values ranging from the minimum length plus 16 up to 1024 (in tiles) are possible.

simutrans-124.5/simutrans/text/en/color.txt000066400000000000000000000013521520675460500210570ustar00rootroot00000000000000Player Colour

Player Colour

Player Colour sets the colour of the company.
Vehicles, buildings & other items show company colour.

Player Colour opens from Game Options.

Click on colour-bar to change company colour.
Titlebar of Player Colour changes (not to new company colour) to indicate new colour is selected.

Default human player colour is light blue.
Some items are not colour-coded & may not display chosen colour.

{Tips: To change AI player colour use Player List or P+ Change Player to select AI player first}.

simutrans-124.5/simutrans/text/en/convoi.txt000066400000000000000000000060141520675460500212360ustar00rootroot00000000000000Vehicle List

Vehicle List

Vehicle List shows information and has controls to filter and list different Convoys (a Convoy is made up of an operational vehicle or combination of vehicle units).

To open: click on vehicle-list tool in List Management or press [V]. Vehicle List has four option buttons: two are used to order items in the list and two are used to change settings for what is shown in the list.
Below buttons is a list of Convoys that fit criteria for filter options.
{Tips: If no Convoys are listed change Filter options. If items in list are only partially visible, then re-size Vehicle List or scroll list using slider-bar.}

Click on option-buttons to cycle through options (changes name of option-button) or open Filter Settings controls :

Sorted by: Two option buttons determine the order of Convoys shown in list.

- Type orders by type of transport (in ascending order of road vehicle, rail vehicle, boat and aircraft)
- Name orders by assigned name, alphanumerically within ASCII-code order: capital letters before lower case letters (by default this is make of first vehicle unit purchased or assembled for Convoy).
- Income orders by profit (income generated minus operating costs).
- Internal ID orders by unique ID number of convoy (assigned by default when Convoy is first purchased or assembled in a depot and shown in titlebar of Convoy Information).

- ascending / descending reverses order of list.

Filter: Two option buttons, switch on/off selection criteria for list and access controls to change criteria.

- enabled / disabled click to toggle filter criteria for Convoys in list.

- Settings opens controls to change filter criteria.

Click on a item listed to see more information about that Convoy.

Items listed for each Convoy include:

Internal ID: a unique ID number of convoy (assigned by default when Convoy is first purchased or assembled in a depot and shown in titlebar of Convoy Information).

name that has been assigned (by default this is make of first vehicle when Convoy is purchased or assembled).

income shows profit (income generated minus operation costs incurred).

Line: shows assigned Line and indicates if the Convoy is in a depot.

graphics show the composition and the current load level.

simutrans-124.5/simutrans/text/en/convoi_filter.txt000066400000000000000000000035041520675460500226040ustar00rootroot00000000000000Vehicle List Filter

Vehicle List Filter

Vehicle List Filter has options to determine which Convoys, are shown in Vehicle List.

Click on Settings in Vehicle List to open Vehicle List Filter.

Option-buttons select Convoys to be listed in Vehicle List when filter option is enabled. Only Convoys that fit all criteria set, are listed.

Click on a square-button to set criteria for filter (button is indented when selected):

Filter names: selects Convoy by name. To use: click on button to select, then click on name-box and type required name exactly (option is case sensitive).

Filter types: will list Convoys by type of transport if selected. To use: click on button to select, then click on transport type(s): road, rail, water and air.

Special filter: selects Convoys to list by following criteria (click on button to select, then click on):
- no route - cannot find a path to next destination.
- no schedule - not assigned a route.
- no income - not generating profit.
- in a depot - those in a depot.
- no line - not assigned a Line.

Filter goods: selects Convoys by ability to transport different goods & passengers. Click on button next to name to select item(s). Use slider-bar to scroll item(s) in list. Options include:
- all selects all items.
- none no items are selected.
- inv. inverses current selection of items.

simutrans-124.5/simutrans/text/en/convoidetail.txt000066400000000000000000000034561520675460500224300ustar00rootroot00000000000000Convoy Details

Convoy Details

Convoy Details provides more information about a Convoy (an operational vehicle or combination of vehicle-units) and can be used to sell the Convoy immediately.

Convoy Details can be opened from the Details option-button in Convoy Information.
If not all vehicle-units are visible re-size Convoy Details or use the slider-bars to scroll.

At the top of Convoy Details, the information shown includes:

Power: of Convoy (combined power of all vehicle-units in Convoy).

Resale Value: money received if Convoy is sold (purchase price of Convoy less any depreciation).

WARNING: Click on Sell mode option-button to sell Convoy immediately. No further confirmation is required. The Convoy is removed from the game and the player balance credited with the selling price.

Convoy Details lists all the vehicle-units in Convoy.
Information listed for each vehicle-units includes:
- vehicle-unit-graphic a picture of the vehicle-unit (also used in depots)
- Manufactured: month and year of manufacture, and introduction date if playing with timeline
- Resale Value: amount received if sold
- Power: power generated (by motorised vehicle-units)
- Friction: current friction level
- Max income: Maximum possible income of this vehicle-unit based on type of item transported by this Convoy
- items on board: amount and destination of items currently carried by this vehicle-unit.

simutrans-124.5/simutrans/text/en/convoiinfo.txt000066400000000000000000000156431520675460500221220ustar00rootroot00000000000000Convoy Information

Convoy Information

Convoy Information provides information about a Convoy (an operational vehicle or combination of vehicle-units) which is used to transport goods and passengers.
It lists items carried by a Convoy and also has controls to access further details and sell the Convoy; follow the Convoy in game-view; and change the name and Schedule of the Convoy.

A Convoy is composed of at least one powered (motorised) vehicle-unit and any attached trailers/carriages.
Examples of Convoys include: a team of horses pulling a carriage; or a train engine attached to a tender and carriages; or a bus; or a van; or a truck attached to a trailer; or connected tram cars; or a ship; or a tugboat pulling a barge; or an aircraft.
Convoys are assembled and purchased from the depot for their type of transport.
{Tips: A Convoy has a maximum limit of vehicle-units in its composition, dependent on the pakset. Trains are typically limited to 24 carriages.}

Click on a Convoy in game-view with the Inspection Tool or click on a Convoy listed in Vehicle List or Line Management to open Convoy Information.
The title-bar of Convoy Information shows the name and the unique ID number of the Convoy (see below for more about these).
Convoy Information contains a name-box; a mini-view; information about the Convoy; option buttons; and a list of all items currently carried (if not all items are visible re-size Convoy Information or use the slider-bars to scroll).

Information shown at the top of Convoy Information includes:

Internal ID: a unique ID number for the Convoy (assigned by default when the Convoy is first purchased or assembled in a depot).

- name: shows the assigned name of the Convoy (by default this is the name of the first vehicle-unit purchased or assembled in a depot for the Convoy).
Click on the name-box and type the new name to change the name of the Convoy.

- speed: The current speed is shown in km/h.
The maximum possible speed of the Convoy is in parentheses (determined by the slowest vehicle unit in the Convoy).
A green progress-bar indicates the current speed relative to the maximum speed.

- Income: profit made by vehicle (income generated less operation costs) in the current game-calendar-year.
The operational cost of the Convoy in Hajo Credits per game square is in parentheses.

- Weight: current combined weight of the Convoy and items carried by it, in tonnes.
The current weight of items carried is in parentheses.
A green progress-bar indicates the percentage of capacity used to carry goods and passengers.
A yellow bar indicates a Wait For level (minimum quantity of goods and passengers required by Convoy to proceed) set in Schedule Controls.
{Tips: To set or change Wait For levels for all Convoys on a Line, use the Update Line option-button in Line Management or Depot Controls. To set or change the Wait For level for a Convoy and no other Convoy on its assigned Line, use the Schedule option-button in Convoy Information or Depot Controls.}

- Destination: The next stop or waypoint in the Convoy's Schedule.
A green progress-bar indicates the progress by Convoy to its next destination.

- Serves line: Line assigned to this Convoy. The triangle opens the line in the Line management window. (Only visible if a line is assigned.)

- mini-view shows this Convoy. Click on the mini-view to centre the game-view on this Convoy.

Click on buttons in Convoy Information to open controls or select options:

Schedule: opens controls to change the route and the minimum quantity of goods and passengers required by Convoy to proceed.

go to depot: sends Convoy to the nearest appropriate depot. Any items being carried are lost, though income is received for transportation to the depot.
In the depot, the Convoy keeps its Schedule and assigned Line.

follow me: button when indented moves game-view with Convoy.
To deselect this option, click on the button again or left-click on the main Map.

Chart: click this option-button to toggle the graph (button is indented when graph is visible) in Convoy Information.
The graph shows statistics for the last 12 months (x-axis) when a graph-option is selected.
Click on a graph-option-button to see the selected information on the graph (button is indented when option selected).
The colours of lines on the graph correspond to the colours of the graph-option-buttons:
- Free Capacity indicates unused space in the Convoy.
- Traveled indicates the number of goods, passengers and mail transported.
- Proceeds indicates income generated by transportation.
- Operation Costs indicates costs incurred by Convoy when in transit.
- Profit indicates profit received from transportation (Proceeds less Operation Costs).

Details opens Convoy Details which contains more information and an option to sell the Convoy immediately.

Sort Passengers/freight by: lists items currently carried by the Convoy.
Information shown includes quantity carried, capacity of Convoy, type of item, final destination and next transfer stop.
The option-button (which changes its name with your selection) sorts the list of items (passengers, mail, and goods) carried:
- destination: sorts items by assigned name of final destination stop, alphanumerically by ASCII-code order (capital letters before lower case letters).
- amount: sorts items carried by quantity in descending order.
- via (detail): sorts items carried by assigned name of first transfer stop, alphanumerically by ASCII-code order (capital letters before lower case letters).
- via (amount): sorts items carried by quantity headed to first transfer stop.
- via (owner): sorts items carried by quantity by the owner of the next stop.

simutrans-124.5/simutrans/text/en/curiosity_build.txt000066400000000000000000000042061520675460500231530ustar00rootroot00000000000000Build attraction buildings

Build attraction buildings

The dialogue is divided into four sections.
top left is a selection list of available buildings
bottom left is shown a picture of the chosen building
top right some options may be set
bottom right is information about the chosen city building given

Selection list

In the selection list are given all special buildings and landmarks that are available with the options specified top right (City attractions, Land attractions, Monuments). There are two tabs that will allow you to change how the attraction buildings are identified.
Translation: Shows the name of the building in the given language. If there is no translation available in the given language, the object name will be used.
Object: Shows the Simutrans internal object name for the building type.

The color of the text has the following meaning:
blue text attraction has to be built in cities
green text attraction has to be built on the countryside
black text monuments: can only be built once

Options

ignore climates: This option deactivates the climate restrictions associated with various building types.
Use timeline year: The selection list will only contain buildings that are available the current Simutrans year.
Show obsolete too: Expand the selection list to also contain buildings that are obsolete the current Simutrans year.
City attraction: There will be city attractions in the selection list.
Land attraction: There will be land attractions in the selection list.
Monuments: There will be monuments in the selection list.
Rotation: If the chosen building can be shown from different angles, then it is possible to chose a specific view of it here. The image bottom left will change to reflect this. If this option is set to random a random view of the building will be used.

simutrans-124.5/simutrans/text/en/curiositylist_filter.txt000066400000000000000000000041261520675460500242360ustar00rootroot00000000000000Attraction List

Attraction List

Attraction List has information about all tourist attractions that are both a destination and an origin for passengers and mail.

To open Attraction List: click on attraction-list-tool in List Management.
{Tips: If items in list are only partially visible, then re-size Attraction List or scroll list using slider-bar.}

Sorted by: has options to determine the order of tourist attractions shown.
Click on buttons to cycle through options (changes name of option-button):

- Name orders alphabetically by name.
- Passenger level orders by relative popularity as a destination for passengers and mail.

- ascending / descending reverses order of list.

Click on a item listed inAttraction Listto see more information about that tourist attraction.
{Tips: click on picture of tourist attraction in more information to move game-view to that attraction.}

Items listed for each tourist attraction include:

status-colour-bar colours indicate the rating of the attraction:
- yellow:does not fall in the catchment area of a Stop.
- green: has associated Stop(s) for passengers.
- blue: has associated Stop(s) for mail.
- turquoise: has associated Stop(s) for passengers and mail.
- orange: an associated Stop is overcrowded.
- red: all associated Stops are overcrowded.

building-graphic indicates if it is a city attraction.

name of the tourist attraction.

passenger level in brackets is a value that indicates the relative popularity as a destination for passengers and mail.

{Tips: For provision of an optimum service the whole tourist attraction must fall within the catchment area of either one or several Stops.}

simutrans-124.5/simutrans/text/en/depot.txt000066400000000000000000000166361520675460500210670ustar00rootroot00000000000000Depots

Depot Controls

Depots are used to purchase and manage vehicles. Different types of transport have their own depot. Depots only show electric vehicle-units if on electric tracks.
If playing with timeline, then as time passes in Simutrans more vehicle-units may appear.

Tools for the construction of different depots are available from the construction toolbar for the type of transport: Train Depot; Monorail Depot; Tram Depot; Road Depot; Ship Depot and Aircraft Hangar.

Click on a depot in game-view with the Inspection Tool to open Depot Controls that give information about and allow the purchase and management of Convoys (an operational vehicle or combination of vehicle-units).
The titlebar of Depot Controls indicates which type of vehicle transport the depot serves.

Depot Controls can be re-sized (click on down-arrow on titlebar of controls to return to original size).
Click on left/right-arrows on titlebar to cycle through depots of the same transport type (centres game-view on depot).

At the top of Depot Controls the number of Convoys in depot, Convoy name and Line is shown.

Convoy Name: Click on left/right-arrow-buttons by Convoy Name to cycle through Convoys in depot.
To change Convoy Name: click on convoy-name-box and type new name (by default this is make of first vehicle-unit purchased or assembled for a new Convoy).

Line Name: Click on left/right-arrow buttons by name to select Lines for transport type of depot or click on line-name-box to open a drop-down-list of Lines (click on Line in drop-down-list to select).
To change Line Name: click on line-name-box and type new name. (by default this is assigned number when Line is created).

convoy-graphic Below the Convoy and Line name-boxes, a graphic of any selected Convoy is shown.
Move mouse over a vehicle-unit-graphic in the selected Convoy to see details about the vehicle-unit (shown at bottom of Depot Controls).
Click on a vehicle-unit in the convoy-graphic to remove it from the selected Convoy and store it in the depot.

Below the graphic the number of vehicle-units in the selected Convoy, length of tiles of the Convoy (all vehicle-units must fit into an appropriate Stop to pick-up and drop-off goods and passengers), and assigned Line are shown.

Option-buttons (click to use) for Depot Controls include:

Start: sends the selected Convoy out of the depot if a route can be found. You can hold Ctrl to start all convoys.

Schedule: opens controls to change the route and quantity of items that a Convoy will wait for at a Stop.

Disassemble: puts the vehicle unit(s) in selected Convoy into storage.

Sell: sells the current Convoy, account balance is credited with Resale Value. You can hold Ctrl to sell all convoys.

New Line: opens controls to define a Schedule (a route and minimum quantity of items that a Convoy will wait for at a Stop), which may be used for multiple Convoys.

Assign Line: assigns Line displayed in line-name-box at top of Depot Controls to selected Convoy.

Update Line: opens controls to change the Schedule (a route and minimum quantity of items that a Convoy will wait for at a Stop) of selected Line.

Copy Convoy: purchases and assembles another Convoy with the same compostion, Schedule and Line as the selected Convoy.

Below option-buttons graphics of all vehicle-units available for purchase are shown.
vehicle-unit-graphics: vehicle units are split into categories, which can be viewed by clicking on the index-tabs. Index-tabs divide vehicle-units into following groups: vehicle-units for passengers and mail, other powered (motorised) vehicle-units, and other trailers/carriages.
A white number over vehicle-unit-graphic indicates quantity stored if a Convoy has been disassembled. Stored vehicle-units can be sold or are used when a new Convoy is assembled.

Move mouse over a vehicle-unit-graphic to see information about vehicle:
Name: and in brackets type of power used for motorised vehicles.
Cost: purchase price and in brackets operation cost per kilometre
Power: engine power and maximum speed for motorised vehicle-units.
Capacity: quantity and goods category that can be carried by vehicle-unit.
Weight: in tonnes.
top speed: of non-motorised vehicle-units.
Intro. date: date when vehicle unit becomes available if timeline is on.
Retire date: date vehicle-unit becomes obsolete.
Constructed by: person who created vehicle-unit for Simutrans.
Gear: ? power ratio.
Resale Value: amount of money received if a previously purchased vehicle-unit is sold.

Click on a vehicle-unit-graphic under index-tabs to either purchase or sell it depending on setting of vehicles-option-button which is found at bottom-left of Depot Controls. The vehicles-option-button cycles through three options:
Append: purchases a vehicle-unit and adds it to a selected Convoy, or creates a new Convoy.
Put in front: purchases a vehicle-unit and adds it to front of current Convoy, or creates a new Convoy.
Sell Mode: sells stored vehicle units.

color bars under the vehicle-unit-graphics indicate which vehicle-units and the order vehicle-units can be attached to each other, to form an operational Convoy (colour bars may change with different settings of the vehicle-option button).
Colours indicate:
- green: vehicle-unit can be used.
- red: vehicle-unit can not be used
- yellow another vehicle-unit must be placed in front or behind to make an operational Convoy.
- blue: vehicle-unit can be used but is obsolete.
{Tips: a Convoy is operational, if it shows green-bars for all vehicle-units; if a part-yellow-bar remains then a missing component vehicle-unit is required.}

Show obsolete: if playing with timeline, square-button when indented (click to use) displays vehicle-units that have been retired.

simutrans-124.5/simutrans/text/en/depotlist.txt000066400000000000000000000005221520675460500217460ustar00rootroot00000000000000Depot list

Depot list

This dialog lists all the depots on the map. By using a tab, they could be filtered by the vehicle types. Right click on an entry or click on the arrow to change the position on the map to the depot area or left click on the entry to open the dialog of the depot.

simutrans-124.5/simutrans/text/en/display.txt000066400000000000000000000115111520675460500214040ustar00rootroot00000000000000Display Settings

Display Settings

Display Settings window has controls for how the game appears and provides information on computer performance.

Click on Display button in Game Options menu to open Display Settings window.

Click on square buttons to select options (buttons are indented when the relevant option is selected), or use the arrow-buttons to adjust settings:

Show grid: shows the lines between individual tiles; can be useful when terraforming.

Underground view: reveals beneath the ground; here you can build transport networks using tunnels; all levels of underground are displayed together.

Sliced map view: in this mode you can also build underground; the map is cut at the specified level and only tunnels on the respective level are displayed; the level can be adjusted with the arrows, or entered directly as a number in the box.

Day and Night cycle: if selected, game will cycle between day and night.

Brightness: sets how light/dark view of game appears; higher values are darker; too low or high values produce problems.

Scroll Inverse: reverses the direction of scroll for game-view in Game Window.

Scroll Speed: sets the scroll-speed for game-view.

Transparent instead of hidden: objects that are selected as hidden (trees or certain buildings) will instead appear semi-transparent.

Hide trees: all trees are hidden.

Buildings display mode: you can select with the help of arrows whether 'no buildings', 'only city buildings' or 'all buildings' are hidden.

Smart hide objects: depending on the setting, objects across a certain number of tiles around the mouse pointer are hidden; the number of tiles can be set with the arrows or directly by entering a number in the box.

Transparent station coverage: shows the catchment area of stops/stations as transparent, rather than as a grid; colored in player's own color.

Show station coverage: displays the catchment area for each stop/station.

Show station names: toggles the style in which the name of each stop/station is displayed.

Show waiting bars: toggles whether the miniature bar graphs showing the proportion of waiting goods/passengers as against the station's total capacity is displayed or not.

Pedestrians in towns: toggles the display of pedestrians moving across urban areas.

Pedestrians at stops: toggles the display of pedestrians getting off a vehicle at stops.

Traffic density: determines the amount of private cars in urban areas.

The number of new private cars generated in urban areas is dependent on size of urban area and on traffic density setting; higher values create more private traffic, while '0' generates none.

Convoy tooltips: customizes the amount of information displayed about convoys in game-view.

Show schedule's stops: if activated, all stops and waypoints are displayed when a vehicle's schedule is open.

TIP: More options and default values for when Simutrans starts can be changed in simuconf.tab file, located in simutrans/config/ folder.

Display performance information

Below these settings, information is shown on computer performance when running Simutrans. Numbers are white when everything is working well; if they are red or yellow then you may need to change your settings.

If you change how fast time passes in the game, by using Fast Forward icon >> at the top of game-view, or [,] / [.] keys, this may also change the number colour.

Frame Time: Shows the actual time between frames.

Idle: Shows how long the computer pauses between two screen updates. When above 0, the computer has capacity to run other tasks along Simutrans.

FPS: Higher values mean vehicles appear to move more smoothly. In Fast Forward and Network mode the frame rate is fixed, otherwise it varies according to the workload. A default value can be set with the command line parameter '-fps' or in simuconf.tab file. If this number remains red, the computer is too slow for current settings (try reducing the size of the game-view window).

Simloops: Number of simulation loops per second. If number remains red, the computer is too slow for current settings (try a smaller map with fewer urban areas).

simutrans-124.5/simutrans/text/en/edittools.txt000066400000000000000000000120441520675460500217470ustar00rootroot00000000000000Game and map editing tools

Game and map editing tools

Game edit tools allow further customisation of the current game world.
Tools can: change populations of urban areas; build urban roads, attractions, power plants & industry; remove option to operate as another player company; and also advance the game by one year.

The toolbar opens when option to operate as public service player is selected from either Players or P+ in Special Contruction Tools (or by keypress [P]). The toolbar remains open even if option to change player is used again.
Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name & more information for some tool-options.

Tool-options include, from left to right:

Grow city: tool increases the population of an urban area by 100, constructing new roads and buildings if necessary.
To use: click on tool to select, changes cursor to an red-up-arrow, then click on urban area.

Shrink city: tool reduces the population of an urban area by 100. Urban areas will only continue to grow if homeless and unemployment levels rise.
To use: click on tool to select, changes cursor to an red-down-arrow, then click on urban area.

City road: tool builds an urban road between two points, as the public service player that every player may use. City-roads may not be built on areas occupied by other buildings, & may not find a path across rough terrain, water and obstructions. City-road built between points, may use existing road in its path.
Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name, construction and maintenance cost incurred by public service player and maximum speed limit. To build a City-road: click on tool to select (changes cursor to city-road), then click on game-view for start-point of road (shows a bulldozer in game-view and map co-ordinates on right of bottom-bar under game-view), & finally click on terrain for end-point for road.
{Tips: Different types of roads may connect. Use bridges & tunnels to connect roads across rough terrain, or to avoid obstructions. Use Terraforming tools to change terrain, to enable paths for road. Use Destroy/Remove to remove city-road & some obstructions. Use [Ctrl] at same time for extra functions.}

Build a random attraction: tool constructs an attraction that passengers will want to visit. Different attractions also generate diffferent amounts of passengers and mail. The type of attraction built is random and only appears if suitable space free of obstructions is found.
To build an attraction: click on tool to select (changes cursor to red-drop-in-yellow-circle-icon), then click on required position on terrain in game-view.
{Tips: Use Destroy/Remove as the public service player to remove attractions. Use Destroy/Remove and/or Landscape tools to create suitable places for attractions.}

Build a new power station: tool constructs a power station (and possible suppliers). The type of power plant built is random and only appears if suitable space free of obstructions is found.
To build a power plant: click on tool or press [I] to select (changes cursor to red-drop-in-yellow-circle-icon), then click on required position on terrain in game-view.
{Tips: Use Destroy/Remove as the public service player to remove power stations. Use Destroy/Remove and/or Landscape tools to create suitable places for power stations.}

Build a new market in the nearest city: tool constructs an end-consumer of goods in an urban area (and possible suppliers not necessarily in the urban area). The type of industry built is random and only appears if suitable space free of obstructions is found.
To use: click on tool to select (changes cursor to red-drop-in-yellow-circle-icon), then click in game-view to build consumer in nearest urban area.
{Tips: Use Destroy/Remove as the public service player to remove power stations. Use Destroy/Remove and/or Terraforming tools to create suitable places for industry.}

Disallow further player change: tool removes option to operate as other player companies and to open Game (map) edit tools.
To use: click on tool to select (changes cursor to a padlock), then click anywhere in game-view, no confirmation is required.

Step time-line by a year: advances date in game by one year. If playing with timeline, as time passes more transport and construction options may become available and slower vehicles are not as profitable (as income is calculated by average speed of available transport) .
To use: click on tool, no further confirmation is required (date changes on left of bottom-bar under game-view).

simutrans-124.5/simutrans/text/en/enlarge_map.txt000066400000000000000000000004661520675460500222200ustar00rootroot00000000000000Map enlargement

Enlarge map

Here, one can extend the map to the right and the bottom. If one wants to extend to the left / top, one has to rotate the map first, enlarge then, and rotate back.
A preview of the enlarged map is shown in the top-right of the dialoge window.

simutrans-124.5/simutrans/text/en/factorylist_filter.txt000066400000000000000000000044351520675460500236560ustar00rootroot00000000000000Factory List

Factory List

Factory List shows information about all factories (suppliers and consumers of goods) in the current game.

To open factory List: click on factory-list-tool in List Management.
{Tips: If items in list are only partially visible, then re-size Industry/factory List or scroll list using slider-bar.}

Sorted by: has options to determine the order of listed ndustries listed.
Click on buttons to cycle through options (changes name of option-button):

- factory name orders alphabetically by name.
- input orders by input storage capacity.
- output orders by output storage capacity.
- Production orders Industry by production capability.
- Rating orders Industry by status-colour-bar (see below).
- Power orders Industry by electricity supplied.

- ascending / descending reverses order of list.

Click on a item listed inFactory Listto see more information about that Industry.
Items listed for each Industry include:

status-colour-bar colours indicate operation of the Industry (also used in Industry Information):
- white: does not require supplements.
- yellow: connected by transport, but Industry Supply Chain has poor supply.
- green: at optimum status.
- orange: operational but improvements possible.
- red: operational but part of Industry Supply chain has excess supply.

red-lightning-strike-icon is shown if the Industry has electricity supplied through transformer.

factory name: name of the industry.

In brackets 3 values represent:
current input: amount of goods stored ready to be processed.
current output: amount of goods that are ready for transportation.
total production: production rate of the factory (maximum amount of goods produced per month)

simutrans-124.5/simutrans/text/en/finances.txt000066400000000000000000000076041520675460500215350ustar00rootroot00000000000000Finances

Finances

The Finances window provides an overview of the financial status of a player's company.

To open, click the money icon at top of main view, press [f], or click on a player's name in the Players dialog.

Values are in Hajo Credits and are listed for present and past calendar months or years. Click on the corresponding tab to change the timespan of the graph.
- Years: shows annual values.
- Months: shows monthly values.

The graph displays the y-axis as quantity and the x-axis as time. You can have several curves displayed at the same time and if you hover the cursor over a point on the graph, a tooltip will display the number.

The Build HQ button in the upper-right corner allows you to build or upgrade your company headquarters (if the graphics are provided by the pakset). The headquarters will serve as a high-level attraction and may be required for some scenarios.


Values (click on name-button to view details on graph), include:

Vehicle Earning - total income from the transport of passengers, goods, and mail.

Operation Costs - total cost incurred by all convoys when in transit.
{Tips: The operation cost per kilometer for vehicles can be seen in a depot and from Convoy Information.}

Maintenance - total cost incurred maintaining the transportation network.
{Tips: The cost per month for maintaining the network (Maintenance (monthly) shown on the right of Finances) is deducted at the end of the game calendar month.}

Road toll - when running convoys over foreign way, a cost may occur. If you are using other players ways, you have to pay. But if you provide are very good road connection, you may also earn from others.

Powerlines - income generated from the selling of electricity from high power transmission.

Operational Profit - profit from transportation, road toll, high power transmission less the maintenance and operational costs (sum of everything above).

New Vehicles - expenditure on and income received from the purchase and sale of vehicles.

Construction site - total cost incurred by construction of transport networks, landscaping, high power transmission lines, and using Destroy/Remove and other removal tools for ways (tracks, road, canals and airstrips).

Cash Flow - total income (sum of everything above this).

Trips - number of everything moved (counting passengers, mail, and goods). Transfers are counted once per leg.


In the second row four additional values are seen. Those are accumulated values:

Account Balance - the current cash available for construction or purchasing vehicles (also shown in the centre of bottom-bar under game-view.

Assets - the value of all company vehicles at the end of the last calendar month.
{Tips: each month the value of operational vehicles depreciate slightly, current selling value for vehicles can be seen in Convoy Details or when vehicle is in a depot.}

Margin - is the ratio of Operational Profit to Proceeds.

Net Wealth - Account Balance plus Assets. This is the company value. If the net wealth becomes negative, your company is bankrupt and you loose the game.

{Tips: Some costs, starting balance and finances in game can be changed in simuconf.tab}

Scenario information

If you play a scenario, then the scenario goal and the percentage of the progress is displayed.

simutrans-124.5/simutrans/text/en/general.txt000066400000000000000000000061131520675460500213560ustar00rootroot00000000000000Simutrans Help

General Help

- Welcome
- Game Interface
- Mouse Help (to be written)
- Keyboard Help
Game Options
- Language
- Player Colour
- Display Settings
- Sound & Music
- Players
- Load Game
- Save Game
- New Game
- - Climate Control
- - Load Height Map

Main Menu
- Game Options
- Map
- Inspection Tool
- Landscaping Tools
- Railroad/Train Tools
- Monorail Tools
- Tram Tools
- Road Tools
- Harbour Tools
- Airport Tools
- Special Construction Tools
- Game Edit Tools
- Destroy/Remove

Management Menu
- Line Management
- Lists
- - Stops/Station List
- - - Stops/Station List Filter
- - Vehicle List
- - - Vehicle List Filter
- - City List
- - Goods List
- - Industry List
- - Attractions List
- Message Centre
- Finances

Other Options
- Save Screenshot
- Pause
- Fast Forward
- Help

Other Dialogs
-Depot Controls
-Schedule Controls
- Stop Information
- - Stop Details
- Convoy/Vehicle Information
- - Convoy/Vehicle Details
- City Information
- Industry Information

If no help is found here, try
http://guide.simutrans.com, or
http://wiki.simutrans.com, or
http://simutrans-tips.com, or
http://forum.simutrans.com.

simutrans-124.5/simutrans/text/en/goods_filter.txt000066400000000000000000000057411520675460500224270ustar00rootroot00000000000000Goods List

Goods List

Goods List contains information on the different items that can be transported in game and how much income transporting them will produce.

To open: click on goods-list-tool in List Management or press [G].

A list of items available for transport in the game indicates: revenue received, % speed bonus, goods category, weight.
{Tips: If items in list are only partially visible, then re-size Goods List or scroll list using slider-bar.}

Speedbonus: shows the Convoy average maximum speed for different transport types required to earn the listed revenues. If average maximum speed is faster than the listed speed some items may earn more but if it is slower they may earn less.
When timeline setting is enabled the speed values may change as time progresses. The change usually follows the introduction of faster or more economical Convoys
The arrow-buttons can be used to adjust the speed by a percentage and update all the displayed revenues. This is useful when wanting to find out what revenue a Convoys gets when its average maximum speed is not exactly at the speedbonus speed without having to do the calculation manually.

Sorted by: shows which order the item list is displayed in.
Click on option-buttons to cycle through various sorting options (changes name of option-button):

- by name in alphabetical order.
- by revenue orders by income received.
- by bonus orders by % for speedbonus.

- ascending / descending reverses the order of the list.

Details listed for each item include:

colour square is the same colour displayed in goods-colour-bars in game-view above a Stop to indicate quantity of items that are awaiting transport.
{Tips: Use [!] to toggle goods-colour-bars above a Stop in game-view.}

revenue is the income received for transporting one unit a distance of one tile in the current payment model.
It is calculated by {(base revenue) * (1 + ((average maximum speed) / (speedbonus) - 1) * (% bonus))}.

% bonus is % revenue change linearly with Convoy maximum speed inversely with speedbonus around the speedbonus speed.

goods category indicates type of vehicle needed for transport. Special freight requires specific vehicles.
{Tips: The capacity of vehicles shows which items a vehicle can transport}.

Weight shows how much one unit of cargo weighs in kilogrammes.
This is added to vehicle weight during transport making the vehicle require more power to move than when empty.

simutrans-124.5/simutrans/text/en/groundobj_build.txt000066400000000000000000000042731520675460500231160ustar00rootroot00000000000000Place Landscape Objects

Place Landscape Objects

With the "Place Landscape Objects" dialog, you can place various objects to enhance the map. It can be opened via the Map Editor. These landscape objects are not necessary for the game; they are purely visual and do not hinder development. Landscape objects include rocks left behind by the Ice Age, small ponds with ducks, flowers, bushes, cacti, landmarks, or similar.

At the top are the selection for a specific climate zone.
All means all available landscape objects are listed, regardless of the climate zone, but only landscape objects that match the climate zone of the soil can be placed. If you want to ignore the climate zone check, you can activate the Ignore climates option next to it. This will prevent the climate zone from being checked when placing the object.
If you select a climate zone based on the intended soil, only the matching landscape objects will be displayed.

Below, you can change the display of the name and the sorting in the list.
Translation Displays the names as they are used in the game. This depends on the selected language. If no translation is available for the selected language, the object name is displayed.
Object Displays the program's internal object names.
Cost to remove Only sorting, the translation names are displayed.
The button next to it switches the sorting to descending.

Selection list

On the left is the selection list with the landscape objects.
On the right, the information for the selected landscape object is displayed.
The name, any explanatory text, all suitable climate zones, whether there is a graphic for snow, which seasons have graphics, what it costs to demolish this object, and who painted the graphic.
4 seasons means spring, summer, autumn, and winter.
The image of the selected object is displayed at the bottom right.

Options

Ignore climates: This option deactivates the climate zone assignment when placing the object. Landscape object.

simutrans-124.5/simutrans/text/en/haltlist.txt000066400000000000000000000072601520675460500215710ustar00rootroot00000000000000Station List

Station List

Station List shows information and has controls to filter and list different Stops (but not Stops just for water transport vehicles), where vehicles pick-up and drop-off goods & passengers.

To open: click on station-list-tool in List Management. Station List has four option buttons, two are used to order items in a list and two are used to apply options to select items to show in list. Below buttons is a list of Stops that fit criteria for filter options.
{Tips: If no Stops are listed change Filter options. If items in list are only partially visible, then re-size Station List or scroll list with slider-bar.}

Click on buttons to cycle through options (changes name of option-button) or open Filter Settings controls:

Sorted by: two option-buttons determine the order of Stops shown in list:

- Type orders by type of vehicle that is accepted.
- unsorted ?orders in random order.
- Name orders alphanumerically within ASCII-code order (capital letters before lower case letters) of assigned name.
- waitingorders by amount of goods & passengers present in relation to waiting capacity and transport service provided.

- ascending / descending reverses order of list.

Filter: Two option buttons, switch on/off selection criteria for list and access controls to change criteria.

- enabled / disabled click to toggle filter criteria.

- Settings click to open controls to change filter criteria.

Click on a item listed inStation Listto see more information about that Stop.
Items listed for each Stop include:

status-colour-bar: colours indicate operation of Stop with regards to how overcrowded it is. The colour-bar is also used in Stop Information and Line Management and is the same colour displayed in the colour-bar above Stop in game-display:
- yellow: no service and nothing waitung.
- green: no improvements necessary.
- orange: stop is slightly overcrowded (with transfer goods mostly).
- red: stop hold more than 1.5 of its capacity, or more than 200 unhappy passengers, or a factory must stop its production because the station is overcrowded.
{Tips: Use [!] to toggle colour-bar above Stop in game display.}

name that has been assigned {Tips: Option to assign numbers in Stop name available in simuconf.tab}.

vehicle icon(s) indicate which types of vehicle can use Stop (also used in Stop Information and Line Management).
Icons include: bus (for road passenger vehicles), truck (for road goods vehicles), train, boat and airplane. Trams can be indicated by a bus-icon or a train-icon depending on type of Stop.

freight icon(s) indicate which items (passengers, goods and/or mail) the Stop can handle (also used in Stop Information and Line Management).
{Tips: adding appropriate extensions changes the category of items a stop can handle. Post offices may be added to Stops to enable handling of mail}.

waiting details of different goods and passengers at Stop.

simutrans-124.5/simutrans/text/en/haltlist_filter.txt000066400000000000000000000040711520675460500231330ustar00rootroot00000000000000Station List Filter

Station List Filter

Station List Filter has options to determine which Stops (but not Stops just for water transport vehicles), where vehicles drop-off and pick up passengers, are shown in Station List .

Click on Settings in Station List to open Station List Filter.

Option-buttons select Stops to be listed in Station List when filter option is enabled. Only Stops that fit all criteria set, are listed.

Click on a button to select/deselect criteria for filter (button is indented when selected):

Filter names: selects Stops by name. To use: click on button to select, then click on name-box and type required name exactly (option is case sensitive).

Filter types: will list Stops that have at least one of the following items if selected. To use: click on button(s). (Stops just for water transport vehicles are not listed in Station List)

Special filter:
- overflowing ?not in use.
- no connection selects Stops with no transport service.

Goods needed: refers to Stops where selected item(s) end their journey. Click on button next to name to select item(s). Use slider-bar to scroll items list. Options include:
- all selects all items.
- none no items are selected.
- inv. inverses current selection of items.

Production: refers to Stops where the selected item(s) start their journey. Click on button next to name to select item(s). Use slider-bar to scroll items list. Options include:
- all selects all items.
- none no items are selected.
- inv. inverses current selection of items.

simutrans-124.5/simutrans/text/en/industry_info.txt000066400000000000000000000112531520675460500226360ustar00rootroot00000000000000Industry Information

Industry Information

Industry Information shows details about a selected Industry (a supplier or consumer of goods) and allows game-view to be moved to related positions in game-world.

Suppliers and consumers of goods link to provide an Industry Supply Chain which terminates in an end-consumer. Some Industry is both a consumer and supplier and lies in the middle of an Industry Supply Chain. If not playing in beginners mode, Industry will cease supply if consumer has excess supply.
Industries may also be a destination for and origin of passengers and mail. Vehicles collect and deliver goods and passengers to an Industry through Stops if the catchment area of the Stop covers any portion of the Industry building and if the Stop can handle the item. {Tips: Off-shorestops/Oil rigs/fish farms/catchment area/oil/mail/passengers/not listed in station list}

The intial number of Industry Supply Chains can be set in Create New World. As urban areas grow more industry may appear. New industry appears each time the population of an urban area doubles from about 2000 onwards.
{Tips: Add Industry to an urban area using Build a new market in the nearest city tool in ?Game Edit Tools. Change when new industries are generated in cityrules.tab (industry_increase_every = 0 generates no new industry)}.

Click on an Industry in game-view with the Inspection Tool or an Industry listed in ?Industry List to open Industry Information.
Use slider-bar to scroll items if all are not visible.

Name of selected Industry is shown in the titlebar of Industry Information.
Other information about selected Industry and options include:

Production: maximum rate of goods the Industry will supply (if a supplier) or consume (if it is an end-consumer) in units per ?month/day.
{Tips: Adding transformer stations and adding supplying electricity to an Industry increases rate of goods produced/consumed}.

Industry picture shows a view of Industry building. Click on picture to move game-view to Industry building.

status-colour-bar under picture indicates operation of Industry (also used in ?Industry List):
- white: does not require input.
- yellow: connected by transport, but Industry Supply Chain has poor supply.
- green: at optimum status.
- orange: operational but improvements possible.
- red: operational but part of Industry Supply chain has excess supply.

Consumer: lists destinations in Industry Supply Chain for goods produced by selected Industry.
Destination industries and their map co-ordinates are listed by an arrow-button. Click on arrow-button to move game-view to destination.

Suppliers: shows origin of goods in Industry Supply Chain required by selected Industry.
Supplier industries and their map co-ordinates are listed by an arrow-button. Click on arrow-button to move game-view to supplier.

Workers live in: shows in which nearby urban areas employees live. Passengers and mail from these urban areas will need to reach the selected Industry.
Urban areas and their map co-ordinates are listed by an arrow-button. Click on arrow-button to move game-view to supplier.

Passenger level: is the relative popularity as a destination for passengers .
Mail level: is the relative popularity as a destination for mail.
{Tips: only one square of an Industry building need fall in the catchement area of a Stop to provide an optimum service for passengers and mail}.

Production: gives details on output of Industry.
Information shown includes: name of item; quantity currently stored by maximum storage capacity for item; goods category; and percentage level of output compared to input.

Consumption: gives details on input of industry.
Information shown includes: name of item; quantity currently stored by maximum storage capacity for item; goods category; and percentage level of input compared to output.

Stops which serve the Industry are listed at the bottom of Industry Information.

simutrans-124.5/simutrans/text/en/inspection_tool.txt000066400000000000000000000125451520675460500231570ustar00rootroot00000000000000Inspection Tool

Inspection Tool

Inspection Tool is used to open dialogues that provide information or option-controls for various items.

Click magnifying-glass-icon at top of game-view or press [a] to open Inspection Tool (changes cursor to a magnifying-glass).

To use Inspection Tool: position magnifying-glass-cursor in game-view at required postion and click to open dialogue. If more than one item is at required postion, a further click will open a second dialogue. {Tips: Option to open one dialogue or all dialogues for a given cursor position available in simuconf.tab}

Click on following items in game-view with tool to open various dialogues:

Depots - opens controls to purchase and manage vehicles at that depot.

Stops - opens Stop Information for that Stop.

Vehicles - opens ?Convoy/Vehicle Information for that vehicle.

Town Hall - opens City ?Information for that urban area. {Tips: Option to open dialogue providing information about Town Hall available in simuconf.tab}

Industry - opens Industry Information for that industry.

City Buildings opens a dialogue that gives information about the building (titlebar of dialogue is 'Building'). Information contained in the dialogue includes:
- description provides information about the building. If no description is available then either 'residential house', or 'industrial building', or 'shops and stores' (for commercial buildings) is shown.
- City of: indicates which urban area the City Building is part of.
- Passenger level: is the relative popularity as a destination for passengers .
- Mail level: is the relative popularity as a destination for mail.
- Appears from: indicates year the building appears if playing with timeline.
- Value:: indicates cost incurred if building is removed with Destroy/Remove Tool.
- Painted by: name of person who drew building for Simutrans

Monuments / Tourist Attractions opens a dialogue that gives information about the attraction (titlebar of dialogue indicates whether it is a monument, or tourist attraction). Information contained in the dialogue includes:
- Public property indicates that item is not owned by a player company.
- description provides information about the attraction.
- Passenger level: is the relative popularity as a destination for passengers .
- Mail level: is the relative popularity as a destination for mail.
- Appears from: indicates year the building appears if playing with timeline.
- Painted by: name of person who drew attraction for Simutrans

Roads and Tracks opens a dialogue that gives information about the Road or Track (titlebar of dialogue indicates what type of way it is - in the case of tram tracks built on road titlebar indicates type of Road).
Information contained in dialogue:
- Max Speed: maximum speed limit allowed on road or track.
- Ribi (unmasked):
is an internal counter that indicates direction of way.
- Ribi (masked): is an internal counter that indicates direction of way taking into consideration any signals or signs.
- with sign/signal is shown if there is a signal or sign present.
- electrified/not electrified indicates whether way is electrified.
- convoi passed last month indicates number of Vehicles that used way in the previous game-calendar-month.
- reserved by indicates which vehicle has reserved a track for use till it has passed.
- wayslope indicates direction of slope if any
- map-coordinates as shown on bottom-bar
In the case of tram tracks constructed on road: information is shown twice, first for the road and then for the tram track.

Signals/Signs opens a dialogue, the titlebar of the dialogue indicates whether it is a signal or a road sign. Information contained in dialogue includes:
- direction ?is an internal counter that indicates direction to which signal applies.

Powerlines / Transformer stations opens dialogue that provides information about electricity supply grid (titlebar of dialogue is 'Power Lines'). Information shown includes current demand and supply.

Trees opens a dialogue (the titlebar is 'Tree'). Information shown includes:
- name which provides species of tree.
- age age of tree in months.

Land opens a dialogue (the titlebar is 'Land'). Information shown includes:
- slope which provides species of tree.
- map co-ordinates as shown in bottom-bar.

{Tips: Option to open dialogues on private road users and pedestrians available in simunconf.tab}.

simutrans-124.5/simutrans/text/en/jump_frame.txt000066400000000000000000000010521520675460500220630ustar00rootroot00000000000000Jump to

Jump to

Dialog opens with [J] key and enables jumping to the specified coordinates. This is useful to move quickly to certain positions on the game-world.

Every tile on the game-world has two coordinates (x,y), with (0,0) located on the top-left corner of the map. Cursor coordinates are permanently displayed on the status bar.
NOTE: A map rotation (performed with [R] key) will rotate all tile coordinates.

simutrans-124.5/simutrans/text/en/labellist_filter.txt000066400000000000000000000004061520675460500232600ustar00rootroot00000000000000List of Signs

List of player signs

Here, all signs on the map are listed.
The option Active player only shows only signs of the active player.

By clicking on a entry, you can edit the text there. simutrans-124.5/simutrans/text/en/language.txt000066400000000000000000000016251520675460500215270ustar00rootroot00000000000000Languages

Languages

The Languages window changes the language used in the game.

It opens from the Game Options window and with the Create a New Game windows.

The default language for Simutrans is English. To play the game in a different language, select the button next to the language you would like to play in.

Language can be changed any time during game play. To change the language, select Game Options -> Language.

Language change happens immediately when creating a new game and on closing the Languages window when changing mid game.

If a translation is not available in the selected language, it may appear in English.

To help in translating Simutrans in your language,
visit https://translator.simutrans.com/

simutrans-124.5/simutrans/text/en/linedetails.txt000066400000000000000000000110751520675460500222410ustar00rootroot00000000000000Line Information

Line Information

The Line Information show and can edit the properties of a single Line (a group of Convoy that share the same route). The Line Information is either opened from the Depot, the Convoy Information, or the Line List.

At the top is the name of the line. CLick in the box to change it. Below are the number of Convoy, the used capacity, and the profit.Below are four tabs.

Schedule is the leftmost tab. Here the Schedule for all Convoy in this line can be altered. Their schedule will be replaced with the new as soon as the tab is changed or the Line Information is closed. The tab will be seletced automatically, if the Line Information was opened from the Depot or the Convoy Information.

Chart shows various information about this Line and the vehicles assigned to it, on a graph (the x-axis is time in months):

- Free Capacity - amount of spare space in vehicle(s) for goods & passengers.
- Travelled - amount of goods and passengers that used Line.
- Proceeds - amount of income generated by vehicles.
- Operation Costs - cost incurred by vehicles when in transit.
- Profit - income generated less costs incurred (Proceeds less Operational Costs).
- Convoys - number of Convoys assigned to Line.

Vehicle List shows all Convoys assigned to this Line. If a Convoy is missing, this Convoy was probbably assinged a new schedule manually and thus was removed from the Line.

There are three management buttons:
- Delete Line deletes this Line, and closes this window. All connected Convoys will continue with the current schedule.
- Withdraw All retires all Convoys in the Line outside a Depot. The Convoys will not longer take any new load and will be sold whenever they become empty. This can be used to update the Convoys of this Line: Open a Depot that serves this Line, assemble a new Convoy, and assign it to this Line. Then select Withdraw All, copy the new Convoy in the Depot as often as needed, and start all new Convoys.

The Convoys of this line are listed below the buttons. Click on an item listed to see more information about that Convoy (use slider-bar to scroll list).
Items listed for each Convoy include:
name that has been assigned (by default this is make of first vehicle unit purchased or assembled for Convoy).
income shows profit (income generated less operation costs).
Line: shows the name of Line assigned to Convoy and indicates if it is in a depot.
graphics show the compostion and the current level of items on board.

Station List shows the Stops. Click on a Stop listed to open its Stop Information.
Items listed for each Stop include:
name of that stop (waypoints are not shown.). The colour indicate the operation of the Stop with regards to how overcrowded it is. The colour is also used in Station List and Stop Information and is the same colour displayed in the colour-bar above a Stop in game-display:
- yellow: not in service.
- green: no improvemments required.
- orange: improvements possible.
- red: improvements recommended.
The name is followed by the vehicle icon(s) indicating which types of vehicles can use Stop (also used in Stop Information and Station List). Icons include: bus (for road passenger vehicles), truck (for road goods vehicles), train, boat and airplane. Trams can be indicated by a bus-icon or a train-icon depending on type of station.
The next line contains the freight icon(s) which indicate the items (passengers, goods, and mail) a Stop can handle (also used in Stop Information and Station List) followed by a summary text of the waiting amounts of passengers or goods.
{Tips: adding appropriate extensions can change the category of items a Stop can handle. Post offices can be added to Stops to enable handling of mail}.

simutrans-124.5/simutrans/text/en/linemanagement.txt000066400000000000000000000044741520675460500227350ustar00rootroot00000000000000Line List

Line List

Line List manage Lines (a group of Convoy that share the same route)

Lines consist of Stops where vehicles pick-up and drop-off goods & passengers (water-transport vehicles may use any square of water in the catchment area of a dock) and waypoints (used to direct vehicles if more than one option is possible or provide interim destination points).

Click on network-icon at top of game-view or press [w] to open Line Management, which can be re-sized (click on down-arrow in titlebar to return to original size).

The Line List lists existing Lines (to scroll list: use slider-bar on right of list), which can be filtered by name or by transport type (click on a index-tab above list to select):
All: lists Lines for all vehicles.
Train: lists Lines for all railroad vehicles.
Monorail: lists Lines for all Monrail/maglev vehicles.
Tram: lists Lines for all trams.
Truck: lists Lines for all road vehicles.
Ship: lists Lines for all water transport vehicles.
Air: lists Lines for all aircraft.

Colour of name of Line that appears in List indicates following:
white - no vehicle assigned to Line
yellow - not operational, not generating profit or loss
black - making profit
blue - has obsolete vehicles
red - line making loss

If you hover over a Line, some more info will be shown. Click on a Line in list to open the Line and see information about Stops, income, and Convoys on this Line or change the schedule.

Two management option are available:

New Line: creates a new Line window. This requires that a transport type is selected.
Delete Line: removes the current line from the game. All convois belonging to that line will driving with an individual schedule similar to the line.

Single GUI: if activated and a Line is clicked, the topmost Line window will be replaced by the selected Line.

simutrans-124.5/simutrans/text/en/list.txt000066400000000000000000000034041520675460500207140ustar00rootroot00000000000000Lists

Lists

Lists has options to open controls for information about the current status of the game (and also a control to adjust relative income recieved from the transport of different goods, passengers & mail).

Information is available for a player in lists about: Stops; Convoys; urban areas; incomes received from different goods, passengers & mail; industry; and tourist attractions.

Click on list-icon at top of game-view to open Lists.
Hover mouse-cursor, after opening or clicking on toolbar, over list-tool-option to see name.

Click on tool-option to open controls for information, from left to right, about:

Station List: Stops, where vehicles pick-up and drop-off goods & passengers (but not Stops just for water-transport-vehicles).

Vehicle List: road and rail vehicles, aircraft and boats.

List of all towns: urban areas (villages, towns and cities).

List of all goods: incomes received from different goods, passengers & mail (includes control to adjust relative income).

Factory List: industry (consumers and suppliers of goods).

Attraction List: tourist attractions.

simutrans-124.5/simutrans/text/en/load.txt000066400000000000000000000024031520675460500206560ustar00rootroot00000000000000Load

Load

Load re-starts a saved game (to continue play) & can be used to delete these games. A game that is re-started replaces the current world which is lost.

Load opens from Create New World & Game Options or by pressing [L]; & list names, date & time (of save) of available games.
If all games are not visible: use slider-bar on right-side of list to scroll names.

Click on name in list to re-start game; or enter name into box at top of Load: click on box, type name, & press [Enter] or [Return] or click on OK.
An incorrect name will receive an error message: close message, try again.

WARNING: Click x-button by name to DELETE game immediately & permanently.
Use this function carefully.

IMPORTANT: New releases of Simutrans appear regularly & games from previous releases of Simutrans may not work with later versions; please read release notes for later version to see if old games are compatible.

Click on Cancel to close Load; or x in top-left-corner; or use keyboard.

simutrans-124.5/simutrans/text/en/load_relief.txt000066400000000000000000000030251520675460500222050ustar00rootroot00000000000000Load Height Map

Load Height Map

Load Height Map window is used to open a file of a custom-made terrain (for use when creating a new game) & can also be used to delete these files. It opens from Create a New Game window and it lists the names, dates & times (of save) of available custom-terrain files. If not all filenames are visible, use the slider-bar on the right-side of list to scroll through the names.

Click the name in the list to load a custom-terrain map, or enter a name into the text box at the top of the window: click on box, type name & press [Enter] or [Return], or click on OK (an incorrect name will have no effect, try again).

WARNING: Click x-button (if enabled in simuconf.tab) on the left of the name to DELETE the file immediately & permanently; the filename is removed from list and Load Height Map window closes. Use this function carefully!

Some custom-made terrains are available online at http://maps.simutrans.com. The new file must be in .ppm or .bmp format (best 256 colors RLE compressed) and placed in the personal directory (or folder) named simutrans/maps/

TIP: Information on how to create custom terrains is available online at http://wiki.simutrans.com

Click on Cancel to close Load Height Map window, or x-button in the top-left corner, or use the keyboard.

simutrans-124.5/simutrans/text/en/mailbox.txt000066400000000000000000000042021520675460500213710ustar00rootroot00000000000000Message Centre & Message Centre Options

Message Centre

Message Centre lists messages from game and opens controls to select how messages are shown in game.

Click on mailbox-icon at top of game-view or press [B] to open Message Centre, which lists messages from game.
Click on a message to move game-view to a related position.

Options button (click to use) opens Message Centre Options which has controls to determine how messages are displayed.


Message Centre Options

Message Centre Options has three columns of square-buttons which select how messages are displayed.

From left to right, the columns select whether messages are shown as: a scroll-text at bottom of game-view; a temporary message; or a permanent message.
{Tips: Click on x in top-left-corner of message or use keyboard to close permanent messages.}

Click on square-buttons to select message options (buttons are indented when selected) for following messages:

New Year: a new year has started.

AI News: an AI player has opened a new line.

City News: an urban area is building a bigger townhall.

No Route: a vehicle can not find a route to its next destination (a Stop or waypoint).

New Industries: a new industry chain (an end-consumer and any necessary suppliers) has been built due to town growth.

Tourist: a city attraction or memorial has been built.

New Vehicles: a new vehicle becomes available, or a vehicle is retired (if timeline is used).

Station full: a Stop is overcrowded.

Problems: player company is in debt and other critical messages.

simutrans-124.5/simutrans/text/en/map.txt000066400000000000000000000144461520675460500205260ustar00rootroot00000000000000Map Window

Map Window

The Map provides a map of the current world giving information on terrain, important locations, and transport networks. It also provides a way to move the view on the world.

Click on map-icon at the top menu or press [m] to open Map, which can be re-sized or minimised. The three topmost buttons enable further options
 Selections show various buttons to overlay further information on the map
 Color Codes shows a min-max colour scale that shows activity level for some of the options.
 Industry List open s table with the colours of industries seen on the map.

The map has different zoom levels: use mouse-wheel, page up/down or the buttons to change them. You can also tilt the map to match the display using the isometric map button.

On the map, water is blue and lower land is darker than higher land. The currently visible part of the world on the screen is indicated by a yellow-square outline. To move this field of vision across the world, left-click on map at desired location or left-click-drag the yellow-square. With right-click drag you can move the area in the map window without using the scrollbars.

On the default map (when not using the options) urban areas and attraction are grey, powerlines turquoise, tunnels are brown, elevated ways light brown, and bridges light grey. On the ground ways have their distinct colors: Roads appear nearly black; rail and tram tracks are dark brown; canals are blue; and runways are dark yellow. All other ways (like narrow gauge, maglev, or monorail tracks) are light red. Stops are red. Vehicles on ways are yellow.

The button Networks will overlay an abstract line network like a subway map over the map. Using the yellow button, you can restrict it to only show passenger, Mail or freight connections.

Using the selection button many additional informations can be overlaid. Those fall in three groups. White and yellow buttons are exclusive and will disable any other white option. Green buttons are for overlays that can be combined with any other. The third group are the red buttons for stops. From this stop group only one can be active at a time.

The following options exist:
Cities names of urban areas.
City Limits show the city limits; new houses and roads are only built inside the city limits.
No. of buildings colors each city building according to its level. The level determines the probability to generate passengers or mail here.
Destinations If a town selected, this option overlays the current passenger destinations (similar to the small map in the City Information.)
Attractions draws circles on the location of tourist attractions. The color and size will indicate the amount of traffic this tourist attractions generates (use min-max scale).
Factories: highlights industry (consumers and suppliers). If this overlay is active and the mouse pointer hovers over an industry on map, it will show the name and white lines linking customers; hold down [Shift] to show red line to suppliers. The color is the color of the industry can be seen fro the Industry List.
Passengers colors the catchment area for passengers by Stops with the color of the respective owner. If the network overlay is active, it will only show passenger connections.
Mail colors catchment area for mail by Stops with the color of the respective owner. If the network overlay is active, it will only show passenger connections.
Freightlevel of goods and passengers transported on alls ways (uses min-max scale). If the network overlay is active, it will only show freight connections.
Stop Status (and all following red options) highlight the stops by circles. Stop Status color indicate how overcrowded a stop is. The same colour is shown in status-colour-bars in Station List, in the Stop Information, Line Management and finally in the color-bar above the name of a stop. The size indicates the capacity of a stop for passengers.
Waiting shows the amount of passengers, mail and goods waiting for transport at a Stop. Color as and size indicate the amount waiting (uses min-max scale).
Queing shows how much the amount of passengers, mail and goods waiting has changed within this month. The size indicates the total change, while the colors are green for decreasing and red for increasing (uses min-max scale).
Service color and size indicate how many vehicles served a station in this months (uses min-max scale).
Transfers size and color indicate how many arrived/departed at a station (uses min-max scale).
Origin color and size show how many passengers and mail wanted to start their journey at a Stop, i.e. the sum of happy, unhappy and no route passengers (uses min-max scale).
Traffic colors ways by the number of vehicles passed within a month (uses min-max scale).
Tracks rail amd tram tracks, bridges, and tunnels are shown as white (red if electrified). Signals are shown as yellow.
Speed Limit colors all ways with respect to their maximum speed allowed (uses min-max scale).
Depots highlights depots. Colors indicate transport types: ship, yellow; road, orange; rail, red; aircraft, green; trams, blue; and monorail, grey.
Powerlines show the load on power transmission lines.
Forest shows forests in bright green.
Ownership colors all things on the map with the color of their owner (orange for unowned).

simutrans-124.5/simutrans/text/en/monorailtools.txt000066400000000000000000000154701520675460500226500ustar00rootroot00000000000000Monorail/maglev Tools

Monorail/maglev Tools

Monorail/maglev Tools construct a Monorail/maglev transport network. Tools may build or remove: monorail and maglev track; bridges; tunnels; signals; depots; station-platforms; & extensions. If playing with timeline, then as time passes in Simutrans more tool-options may appear.

Click monorail/maglev-icon at top of game-view to open toolbar.
Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name & where appropriate: construction cost, maintenance cost in brackets, maximum speed limit & maximum length.

Monorail/maglev Tools include, from left to right:

Monorail/maglev Track: tools build Tracks, for monorail/maglev vehicles between two points. Tracks can only be built on slopes in the direction of the slope & may not find a path across rough terrain, water and obstructions. New Track built may use existing Track in its path.
To build Track: click on tool to select Track (changes cursor to track), then click on game-view for start-point of Track (shows a bulldozer in game-view and map co-ordinates at on right of bottom-bar under game-view), & finally click on game-view for end-point for Track.
{Tips: Different types of Tracks may connect (however not when built by some other players). Use Landscape tools to change terrain, to enable paths for Track. Use Destroy/Remove to remove individual pieces of Track & some obstructions. Use [Ctrl] at same time for extra functions. Undo last route construction, [z], does not refund constructions costs.}

Remove monorails: tool removes monorail/maglev Track, when no vehicles are present, between two points in game-view (platforms and signals in path are removed too).
Use of tool incurs a construction cost.
To remove Track: click on tool (changes cursor to a red-cross); then click on Track that is to be deleted (selects deletion point shown by a red cross in game-view); & finally click at another point on track, to remove portion up to first deletion point.
{Tips: Operate as another player to remove their Tracks.}

Ramps and Bridges: tools build straight bridges, for monorail/maglev vehicles to pass, between two pieces of monorail/maglev Track in game-view. Bridges have a maximum span. Tools build bridges from an end of a Track to a suitable place (another Track-end or slightly higher land, within span).
To build a ramp or bridge: click on tool to select (changes cursor to bridge), then click on the end of a Track (start-point of bridge) to build. Some obstructions or lack of a suitable place for bridge-ends will prevent bridge being built: place Track at both sides that is to be bridged, & try again.
{Tips: Use Destroy/Remove to remove bridges (click on bridge-end) & some obstructions to bridge building. Use Track tools to connect bridge-ends to Track.}

Monorail Signals: tools build signals for monorail and maglev vehicles on a Track in game-view. Signals direct & regulate flow of vehicles on Tracks & bridges, junctions & Stops (where vehicles pick-up and drop off passengers).
Two-way & one-way versions of signals can be built. To build a two-way signal on Track: click on tool to select signal (changes cursor to signals); then click on Track. To build a one-way signal: click again at same point, with signals-cursor, to cycle through one-way signals, back to a two-way signal.
IMPORTANT: Take care not to place one-way signals that prevent vehicles reaching their destination, by default vehicles drive on the right (can be changed in simuconf.tab).
- Signals: vehicles proceed only if Track ahead up to next signal or destination in Schedule (a Stop or waypoint) is not occupied by another vehicle. In one-way mode vehicles pass in one direction only.
- PreSignals: vehicles proceed if area of Track ahead (either between three consecutive signals or to next destination in Schedule) is not occupied by another vehicle. In one-way mode they let vehicles pass in one direction only.
- ChooseSignals: direct vehicles to an empty station-platform, at a multi-platform Stop: a vehicle passing this signal may use any empty platform at its next destination, and not just the one assigned in its schedule. If no empty platform or clear route to next destination is found, then vehicles wait at signal.
{Tips: Remove signals with Destroy/Remove. Hold down [Ctrl] to place signals on bridges over lower Tracks.}

Monorail Depot: tool builds a depot for purchasing & managing monrail/maglev vehicles and carriages. Depots have a maintenance cost & are built on the end of monorail/maglev Track in game-view.
To build a monorail depot: click on tool (changes cursor to a depot), then click on end of Track.

Train-stops: tools build station-platforms, used by monorail/maglev vehicles to pick-up and drop-off passengers & mail.
A station-platform when not built adjacent to an existing Stop will create a new Stop.
Train-stops have a maintenance cost & are built on track (but not at bends and junctions in track).
Train-stops have a catchment area for goods, passengers & mail. Different station-platforms may have different capacities for goods, passengers & mail. . In the corner of some tool-options an icon (used in Station List & Stop Information) shows which items the station-platform allows a Stop to handle.
To build a a station-platform: click on tool to select (changes cursor to a platform), then click on Track.
{Tips: Remove train-stops with Destroy/Remove. Extend station-platforms & build multi-platform Stops (by building more platform sections on adjacent track) to accommodate longer and more vehicles, increase capacity & catchment area. Press [v] to show/hide catchment area for goods & passengers in game-view. Hold down [Ctrl] to build on bridges or elevated Track above lower Tracks.}

Monorail Foundation: tool constructs an extension to a Stop and increases the capacity of the Stop. Monorail foundations are built on ends of monorail/maglev Track and have a maintenance cost.
To build an monorail foundation: click on tool to select (changes cursor to foundation), then click required position on the end of a track.
{Tips: Remove monorail foundations with Destroy/Remove.}

simutrans-124.5/simutrans/text/en/new_world.txt000066400000000000000000000121711520675460500217420ustar00rootroot00000000000000Create a New Game

Create a New Game

The Create a New Game window sets options for a new game. This creates a new map with the selected settings. You can configure many game variables including the size of terrain, initial number of urban areas and the number of industries. A preview of the selected terrain is also displayed in the Create a New Game window.

When this window is open all other game-options are disabled. To return to current game by closing the Create a New Game window, click the x-button located on the titlebar.

Arrow-buttons adjust settings and buttons switch on/off options or open new controls.

Map number - Selects a terrain to use for the game.
Click on number-box and type the required map number or use the arrow-buttons to cycle through the terrains. The preview area shows the selected terrain as it would appear in the game (water is blue, lower land is darker than higher land).

Size - Sets the map size as W x H. The number-box on top is the width, and the one below it is the height. The memory required for the map is displayed in brackets.
NOTE: Large maps need more memory and take more time to generate. The game and other graphics require an extra 88MB in pak64 and 144MB in pak128. As a general rule of thumb, on a computer with 256MB RAM do not try maps much larger than 512 x 512 squares.

Random Map - Selects a new terrain at random. Other settings do not change.

Load Height Map - Loads a custom terrain. Opens the Load Height Map window.

Number of cities - Sets the initial number of urban areas in your game. If the map is too small or too rough, less cities will be created. Further new cities can be added manually by switching to public player.

Median citizens per city - Sets the average initial population of all urban areas. Simutrans will try to generate more small villages than large cities.

Intercity road length - Sets the maximum length of public roads between urban areas at the start of the game. Set this to a higher value to connect urban areas further appart.

Factories and shops - Sets the initial number of industry supply chains outside urban areas. More industry chains will appear automatically as the urban area's population increases.
NOTE: If the terrain is very hilly, suitable flat space may not be found for creating industry chains, therefore less industries appear.

Tourist attractions - Sets the inital number of sites outside of urban areas that attract passengers and mail. Tourist attractions appear outside urban areas in addition to any attractions generated with new towns or cities at the start of the game.

Use timeline from year - Sets the start year for your game. Click the square-button to play your game with time based constraints. As time passes in the game, more vehicles, buildings and industries appear.

When this option is not selected, all vehicles, buildings, and industries are available and used in your game. Urban areas appear with buildings from all eras and slower vehicles are not as profitable as faster ones, as income is calculated by the average speed of transport vehicle used.

Use beginner mode - Sets the option that allows a higher level of income to be received in game-play from transporting goods and passengers. By default, when played in 'beginner mode', you will receive 150% of value as received when playing in normal mode. Furthermore, industry will not cease supply when consumer(s) have excess supply.

Settings - Opens the Extended Settings window. Here all parameters can be accessed that are available also in simuconf.tab file.

Landscape Settings - Opens the Landscape Settings window, which controls various map settings: roughness, water level, climates, forests, and rivers.

Load Game - Loads a saved game. This displays the Load Game window with controls to re-start a previously saved game. When a saved game is loaded, it replaces the current game without confirmation.

Load Scenario - Loads a scenario, i.e. a game where a certain goal must be fulfilled. This is an alternative to the usual, open-ended Simutrans playing style.
NOTE: Some paksets may not have any scenarios.

Start Game - Starts a game by creating a new world, using the selected settings. Click this button to start playing Simutrans. Good Luck and Enjoy your Game!

Quit - Click this button to exit game.

TIP: To change default values when starting Simutrans or to change other global properties, modify values in the simuconf.tab file.

simutrans-124.5/simutrans/text/en/options.txt000066400000000000000000000027461520675460500214440ustar00rootroot00000000000000Game Options

Game Options

Game Options opens controls for the operation & presentation of the game; exit Simutrans from Game Options.

Click on floppydisc-icon at top of game-view to open Game Options.

Click on button to open new controls:

Language: sets the language used in game.

Players: opens a list of different player companies.

Player Colour: sets the colour of the player's company.

Display: settings for how the game appears in Simutrans.

Sound: settings for sound & music in the game.

New Game: opens controls to start a new game (closes other toolbars, texts & options in current game).

Load Game: opens controls to re-start a saved game.

Save: opens controls to store the current game, that can be played later.

Load Scenario: opens controls to play a scenario, i.e. a game where a certain goal must be fulfilled.

Scenario Info: Normally disabled, it shows current scenario details when one is loaded.

Quit: exits Simutrans, current game is not saved!

simutrans-124.5/simutrans/text/en/password.txt000066400000000000000000000022211520675460500215770ustar00rootroot00000000000000Change Player Name and Password

Change player name and password

Change player name and password is invoked in two ways:

Either by clicking on the box to the right of a player's name in the Player List or
By changing to a password protected player and issued a command.

If the player has a green box to the right of the name, it is not password protected, and can be claimed and password protected by pressing the green box and entering a password. If the name is not selectable, you must specify a valid password.

You can change the name of any player that does not have a password or that you have unlocked by entering the password by editing the text in the top line.

A password protected player will have a red box right of the name in the Player List. Once you enter the correct password, the box will turn green and you will be able to play as that player. The entered password to unlock is kept locally, so you have to enter the password only a single time, as long as you do not restart the program.

simutrans-124.5/simutrans/text/en/players.txt000066400000000000000000000047311520675460500214240ustar00rootroot00000000000000Player List

Player List

Player List provides information & has controls for different player companies.

Click on Players-button in Game Options or press [k] to open Player List which shows player names & Account Balances.

There are fourteen players: human, public service and twelve empty slots. The can be either filled by other players (human) or by two types of AI.

Click on the square-button will enable a player. If this player is an AI, it can be stopped or continued to expand by this button. Switching off an AI player during a game will not remove its transport network.

Click on arrow-button to operate as that player company in the current game. A confirmation message appears that player has been changed: click on x in top-left corner of message or use keyboard to close.

Click on player name to see their Finances. If the player slot is not assigned, you can select what kind of player should occupy this slot. To activate it, use the leftmost square box.

Players can be protected by passwords. If you play as a protected player you have access to many statistics but are not allowed to change anything game relevant for this player.
A protected player is marked by a red box to the right of his name. If no password is set or the player is unlocked, this box is shown in green. Click on the box to open the password dialog, where the passwort can be typed or set.

For network games cooperation between players may be desirable. You can make you stops public. Any player can either connect or directly drive to a public stop. Also tracks can be shared, by making the connection tiles public. Control over which companies are allowed to run on such a track can be done via gates.

Players may not build items or place waypoints on another player's track or road, or remove items built by another player (however some items built by the public service player may be removed using Destroy/Remove).

At the bottom is an option to enable freeplay mode (if the public player is not locked). In this mode you will never get bankrupt. Otherwise after getting a negative company values, a player will be eliminated and the slot freed after a year.

simutrans-124.5/simutrans/text/en/privatesign_info.txt000066400000000000000000000010701520675460500233040ustar00rootroot00000000000000Private Way Entrace Gate

Private Way Entrance Gate

With this dialogue you can exclude all unchecked players from entering your way with their vehicles through this gate. (The public player setting will affect the city cars.) The player, which is allowed to pass, will see an open gate or barrier, and the excluded player will see a closed gate or barrier.

This sign is built from one of the way building tool bars (like railroad tools or road tools).

simutrans-124.5/simutrans/text/en/railtools.txt000066400000000000000000000204031520675460500217470ustar00rootroot00000000000000Railroad Tools

>Railroad/Train/Rail Tools

Railroad Tools construct a train transport network. Tools may build: railtrack (and electrify or remove it); bridges; tunnels; signals; depots; station platforms; & extensions. If playing with timeline, then as time passes in Simutrans more tool-options may appear.

Click train-icon at top of game-view to open toolbar. Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name & where appropriate: construction cost, maintenance cost in brackets, maximum speed limit & maximum length.

?Railroad Tools include, from left to right:

Rail Track: tools build Tracks for rail vehicles between two points. Tracks can only be built on slopes in the direction of the slope & may not find a path across rough terrain, water and obstructions. New Track built, may use existing Track in its path.
To build Track: click on tool to select Track (changes cursor to Track), then click on game-view for start-point of Track (shows a bulldozer in game-view and map co-ordinates on right of bottom-bar under game-view), & finally click on game-view for end-point for Track.
{Tips: Different types of Tracks may connect (however not when built by some other players). Use bridges & tunnels to connect tracks across rough terrain, or to avoid obstructions. Use Landscape tools to change terrain, to enable paths for Track. Use Destroy/Remove to remove individual pieces of Track & some obstructions. Use [Ctrl] at same time for extra functions. Press [t] to select last Track tool used.}

Electrify Track: tools electrify Track, bridges and tunnels between two points in game-view, for electric vehicles.
To electrify Track: click on tool or press [e] (changes cursor to electric-track-icon) to select, then click on Track in game-view for start-point of electrification (places a electric-track-icon in game-view), & finally click on a second point on track, to electrify portion of track up to start-point.
{Tips: Use Destroy/Remove to change back to non-electric tracks.}

Remove Tracks: tool removes Track & electric Track, when no vehicles are present, between two points in game-view (Please note: platforms, signals, tunnels & bridges in path are removed too). Use of tool incurs a construction cost.
To remove Track: click on tool (changes cursor to a red-cross); then click on Track (selects deletion-point shown by a red-cross in game-view); & finally click at second point on track, to remove portion up to first deletion-point.
{Tips: Operate as another player to remove their Tracks.}

Rail Bridge: tools build straight bridges, for rail vehicles to pass, between two pieces of Track in game-view. Bridges have a maximum span.
Tools build bridges from an end of a Track to a suitable place (another Track-end or slightly higher land, within span).
To build a bridge: click on tool to select (changes cursor to bridge), then click on the end of a Track (start-point of bridge) to build.
Some obstructions or lack of a suitable place for bridge-ends will prevent bridge being built: place Track at both sides that is to be bridged, & try again.
{Tips: Use Destroy/Remove to remove bridges (click on bridge-end) & some obstructions to bridge building. Use Track tools to connect bridge-ends to Track.}

Rail Tunnel: tool builds a straight tunnel, for rail vehicles to pass through land, between two pieces of Track in game-view. Tunnels cannot incline: vehicles enter & leave at the same height.
The tool builds a tunnel, on an end of Track placed on a slope. To build a tunnel: click on tool (changes cursor to a tunnel), then click on Track-end on slope for the tunnel entrance.
If a tunnel cannot find a suitable exit (a slope free from obstructions), it will not be built: place Track on both sides that is to be connected, & try again.
{Tips: Use Landscape tools & Destroy/Remove to create suitable places for tunnel entrances and exits. Use Track tools to connect tunnel-exits to Track.}

Rail Signals: tools build signals for rail vehicles on a Track in game-view. Signals direct & regulate flow of vehicles on Tracks & bridges, junctions & Stops.
Two-way & one-way versions of signals can be built. To build a two-way signal on Track: click on tool to select signal (changes cursor to signals); then click on Track. To build a one-way signal: click again at same point, with signals-cursor, to cycle through one-way signals, back to a two-way signal.
IMPORTANT: Take care not to place one-way signals that prevent vehicles reaching their destination, by default vehicles drive on the right (can be changed in simuconf.tab).
If you hold the control key when clicking on a signal, the multiple signal builder is activated.
- Signals: vehicles proceed only if Track ahead up to next signal or destination in Schedule (a Stop or waypoint) is not occupied by another vehicle. In one-way mode vehicles pass in one direction only.
- RailClose: vehicles do not proceed beyond signal (available in one-way mode only).
- PreSignals: vehicles proceed if area of Track ahead (either between three consecutive signals or to next destination in Schedule) is not occupied by another vehicle. In one-way mode they let vehicles pass in one direction only.
- ChooseSignals: direct vehicles to an empty station-platform, at a multi-platform Stop: a vehicle passing this signal may use any empty platform at its next destination, and not just the one assigned in its schedule. If no empty platform or clear route to next destination is found, then vehicles wait at signal.
{Tips: Remove signals with Destroy/Remove. Hold down [Ctrl] to place signals on bridges over lower Tracks.}

Train Depot: tool builds a depot for purchasing & managing trains and carriages. Depots have a maintenance cost & are built on the end of Track in game-view.
To build a train depot: click on tool (changes cursor to a depot), then click on end of track.
{Tips: Remove depots with Destroy/Remove. Depots show electric vehicles only if on electric track.}

?Rail-stops: tools build station-platforms, used by rail vehicles to pick-up and drop-off goods, passengers & mail.
A station-platform when not built adjacent to an existing Stop will create a new Stop.
Rail-stops have a maintenance cost & are built on Track (but not at bends and junctions in Track).
Rail-stops have a catchment area for goods, passengers & mail. Different station-platforms may have different capacities for goods, passengers & mail. In the corner of some tool-options an icon (used in Station List & Stop Information) shows which items the station-platform allows a Stop to handle.
To build a station-platform: click on tool to select (changes cursor to a platform), then click on Track.
{Tips: Remove Rail-stops with Destroy/Remove. Extend station-platforms & build multi-platform Stops (by building more platform sections on adjacent track) to accommodate longer and more vehicles, increase capacity & catchment area. Press [v] to show/hide catchment area for goods & passengers in game-view. Hold down [Ctrl] to build on bridges above lower Tracks.}

Extensions:tools construct extensions & buildings for Stops which may increase capacity, catchment area and maintenance cost.
In the corner of some tool-options an icon (also used in Station List & Stop Information) shows which items the extension allows the Stop to handle.
To build an extension: click on tool to select (changes cursor to extension), then click required position, beside an existing Stop, in game-view. The new extension is now considered to be part of the Stop.
{Tips: Remove extensions with Destroy/Remove.}

simutrans-124.5/simutrans/text/en/removal_tool.txt000066400000000000000000000042361520675460500224470ustar00rootroot00000000000000Removal Tool

Destroy/Removal Tool

Removal Tool is used to destroy and remove unwanted items and obstructions to construction on a single game-tile in the game-view.

To Use: Click red-cross-icon (bulldozer-icon in pak128) at top of the game-view or press [r] key to open Removal Tool (it changes cursor to a red-cross-over-dust-cloud). Position cursor in game-view, a yellow outline indicates position on which tool will operate (map coordinates are indicated in the bottom-bar). Then click on object in the game-view to remove it. If you are authorized for removal, the object will be removed from the map without warning.

Cost incurred is displayed immediately in game-view and appears also under Construction Costs item in Finances window. The cost of removal is equivalent to the cost of construction (for player items). The cost of destroying city buildings is the same as their value as shown in dialogue opened with Inspection Tool. There is also a cost for destroying trees.

Removal Tool can be used to remove individual pedestrians and private citycars as well.

The order in which items are removed from a single road/track tile: pedestrian or private citycar, signals, stop/station platform, electrification of way, bridge or tunnel, way itself (track first if on road).

TIPS:
To remove a player vehicle, sell/retire it from Convoy Details window.
To remove an industry, attraction or town hall, switch to public service player first.
WARNING: When removing a town hall, all city buildings from that urban area are also removed.
To destroy/remove another player items, select to play as other player first.
To remove segments of track or road use Remove tracks tool (also found in Tram tools) or Remove roads tool.

simutrans-124.5/simutrans/text/en/roadtools.txt000066400000000000000000000212611520675460500217500ustar00rootroot00000000000000Road Tools

Road Tools

Road Tools construct a road transportation network for goods and passengers. Tools may build or remove: roads, bridges, tunnels, signals, depots, extensions, & Stops. If playing with timeline, then as time passes in Simutrans more tool-options may appear.

Click truck-icon at top of game-view to open toolbar.
Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name & where appropriate: construction cost, maintenance cost in brackets, maximum speed limit & maximum length.

Road Tools include, from left to right:

Road: tools build road between two points for road vehicles. Roads may not be built on areas occupied by other buildings. Roads can only be built on slopes in the direction of the slope & may not be built across rough terrain or water. New road built may use existing road in its path.
To build road: click on tool to select road type (changes cursor to road), then click on in game-view for start-point of road (shows a bulldozer in game-view and map co-ordinates on right of bottom-bar under game-view), & finally click on game-view for end-point for road.
{Tips: Different types of roads may connect even when built by different players . Use bridges & tunnels to connect roads across rough terrain, or to avoid obstructions. Use terraform tools to change terrain, to enable paths for road. Use Destroy/Remove to remove individual pieces of road & some obstructions. Use [Ctrl] at same time for extra functions. Undo [z] does not refund constructions costs. Press [s] to select last roadway tool used.}

Remove roads: tool removes road, when no vehicles are present, between two points in game-view (Road-stops, signals, tunnels in bridges in path are removed too). Use of tool incurs a construction cost.
To remove road: click on tool (changes cursor to a red-cross); then click on track that is to be deleted (selects deletion point shown by a red-cross in game-view); & finally click at another point on track, to remove portion of track up to first deletion point.
{Tips: Operate as another player to remove their roads.}

Road Bridge: tools build straight bridges, for vehicles to pass, between two pieces of road in game-view. Bridges have a maximum span. Tools build bridges from an end of a road to a suitable place (another road-end or slightly higher land, within span).
To build bridge: click on tool to select bridge (changes cursor to bridge), then click on the end of a road, (starting-point of bridge). Some obstructions or lack of a suitable place for bridge-ends may stop bridge being built: build road at both sides that is to be bridged, & try again.
{Tips: Use Destroy/Remove to remove bridges (click on bridge-end) & some obstructions to bridge building. Use Roadway tools to connect bridge-ends to road.}

Road Tunnel: tool builds a straight tunnel, for vehicles to pass through land, between two pieces of road in game-view. Tunnels cannot incline: vehicles enter & leave at the same height.
Tool builds a tunnel, on an end of a road placed on a slope. To build a tunnel: click on tool (changes cursor to a tunnel), then click on road-end on slope for the tunnel entrance. If a tunnel cannot find a suitable exit (a slope free from obstructions), it will not be built: place road on both sides that is to be connected, & try again.
{Tips: Use Landscape tools & Destroy/Remove to create suitable places for tunnel entrances and exits. Use Roadway tools to connect tunnel-exits to roads.}

Road signals & barriers: tools build signals & barriers, for road vehicles, in game-view. Signals & barriers direct & regulate flow of vehicles on roads & bridges, and at junctions & stops (where goods and passengers are picked-up and dropped-off). Two-way & one-way versions of some signals can be built.
IMPORTANT: Take care signals not place signals that prevent vehicles reaching their destination, by default vehicles drive on the right (can be changed in simuconf.tab).
- Traffic lights: tool builds signals in game-view to regulate the flow of road vehicles at junctions, either allowing them to proceed or making them wait for a while. Click on tool to select (changes cursor to signal), then click on road to build. Click again at junctions to change orientation of signals.
- single barrier: tool builds a barrier to prevent road vehicles using one side of a road. Click on tool to select (changes cursor to barrier), then click on road to build, click again at same position to change side of road to which barrier applies.
- road closed: tool builds a barrier to prevent road vehicles proceeding. Click on tool to select (changes cursor to barriers), then click on road to build.
- no entry: tool builds a signal to prevent road vehicles proceeding on one side of a road (similar to a single barrier. Click on tool to select (changes cursor to signal), then click on road to build, click again at same position to change side on road that signal is placed. There is also a variant called private way entrance gate which only let pass traffic of certain players.
- minimum 80km/h tool builds a signal to prevent road vehicles proceeding if their maximum speed is below 80km/h. Click on tool to select (changes cursor to signal), then click on road to build, click again at same position to change side of road to which signal applies.
- Choose Point: tool builds a signal to direct a road vehicle to an empty Road-stop to pick-up or drop-off goods and passengers at a Stop with multiple Road-stops: a vehicle passing this signal may use any free Road-stop at its next destination, and not just the one assigned in its schedule. If no empty Road-stop or clear route to next destination is found, then vehicles wait at signal. Click on tool to select (changes cursor to signal), then click on road to build, click again at same position to change side of road to which signal applies.
- highways: (available only in pak128) tools build a signal that only allow vehicles to proceed in one direction on a road if their maximum speed limit is over 80km/h.
Click on tool to select (changes cursor to signal), then click on road to build, click again at same position to change direction on road to which signal applies.
{Tips: Remove signals with Destroy/Remove. Use [Ctrl] to place signals on bridges over lower roads.}

Road Depot: tool builds a depot for purchasing & managing road vehicle and trailers. Depots have a maintenance cost & are built on the end of road in game-view.
To build a Road Depot: click on tool (changes cursor to a depot), then click on end of road.
{Tips: Remove depots with Destroy/Remove.}

Car Parking: (available only in pak 128) tools construct extensions for Stops which increase capacity & catchment area for goods & passengers, and maintenance cost.
To build an extension: click on tool to select extension (changes cursor to extension), then click required position on a road, beside an existing Stop in game-view. The new extension is now considered to be part of the Stop.
{Tips: Remove extensions with the Destroy/Remove.}

?Road-Stops: tools build places for road vehicles to pick-up and drop-off goods & passengers.
A Road-stop when not built adjacent to an existing Stop will create a new Stop.
Road-stops are built onroad& have a maintenance cost and a catchment area for goods, passengers & mail. In the corner of some tool-options an icon (used in Station List & Stop Information) shows which items the Road-stop allows a Stop to handle. Different Road-stops may have different capacities for goods, passengers & mail.
To build a Road-stop: click on tool to select (changes cursor to roadstop), then click required position on a road in game-view.
{Tips: Remove Road-stops with Destroy/Remove. Press [v] to toggle catchment area for goods & passengers in game-view. Roadstops cannot be placed on road built by some other players. Use [Ctrl] to build on bridges over lower roads. Create Stops with multiple places for vehicles to increase capacity and catchment area.}

simutrans-124.5/simutrans/text/en/save.txt000066400000000000000000000032161520675460500207000ustar00rootroot00000000000000Save

Save

Save stores the current game to a file (to continue play at a later time) & can be used to delete these files.

Save opens from Game Options or by pressing [S]; & list names, date & time of previous games.
If all games are not visible: use slider-bar on right-side of list to scroll names.

The current game can be saved to a new file or over an existing game.
To save game to new file, enter new name into box at top of Save: click on box, type name & press [Enter] or [Return] or click on OK; the new filename is listed.
IMPORTANT: If a listed filename is used: old game is replaced, by current game, & lost.

By default the current game appears in filename box, to save to this file: click OK (old data is lost and replaced with current data).
To save game to file over an old game: click on a name in list (old game is replaced by current game).

Filenames can be a combination of letters, numbers & symbols to a maximum length of 57 characters; after save, only 20 (approx.) characters are shown in Save & Load.

WARNING: Click x-button by filename to DELETE game immediately & permanently, the filename is removed from list. Use this function carefully.

{Tips: Re-start a saved game from Load.
Change autosave (automatic game save) in simuconf.tab}.

Click on Cancel to close Save; or x in top-left-corner; or use keyboard.

simutrans-124.5/simutrans/text/en/scenario.txt000066400000000000000000000003521520675460500215430ustar00rootroot00000000000000Scenario

Scenarios

You can play scenarios with simutrans. Here you have to fulfill certain goals: connect a factory, get rich as soon as possible, or erect a monumental headquarter.

simutrans-124.5/simutrans/text/en/schedule.txt000066400000000000000000000103531520675460500215360ustar00rootroot00000000000000Schedules

Schedule Controls

Schedule Controls sets the Schedule (route and minimum quantity of goods and passengers required by a Convoy to proceed) for a Convoy or a Line.
It listsStops (where Convoys pick-up and drop-off goods and passengers) and waypoints (which can be used to direct Convoys to a path if more than one is available or provide interim destination points) with their map co-ordinates.

Schedule Controls for a Convoy opens from Schedule option-button in Convoy Information and Depot Controls.
Controls for the Schedule of a Line open from New Line and Update Line option-buttons in Line Management and Depot Controls.

When Schedule Controls is opened from Convoy Information the assigned Line (if any) is shown at top of controls.
Line Name: Click on left/right-arrow buttons by name to cycle through Lines or click on name-box to open a drop-down-list of Lines (click on Line in drop-down list to select).
Click on name-box and type new name to change name of Line.

Wait For: sets the minimum quantity of goods and passengers required by a Convoy to proceed. The percentage figure refers to the total capacity of the Convoy and not an indiviual type of item carried.
A Convoy will wait at Stop until required percentage is on board.
To use: click on Stop in list to select (> by name shows that Stop has been selected), then use arrow-buttons to cycle through percentages.
{Tips: To set or change Wait For levels for all Convoys on a Line, use Update Line option-button in Line Management or Depot Controls. To set or change the Wait For level for a Convoy and no other Convoy on assigned Line, use Schedule option-button in Convoy Information or Depot Controls.}

Option-buttons (click to use, button is indented when selected) include:

Add Stop: option places a Stop used by vehicles to pick-up or drop-off goods and passengers (water transport vehicles may use any square of water in the catchment area of a dock) or waypoint on route.
To add a Stop or waypoint: click on option to select (changes cursor to halt-arrow), then click required point in game-view for Stop or waypoint (point on road, track or water as appropriate for type of vehicle). The Stop or waypoint, is now listed in Schedule Controls.
{Tips: Vehicles do not pick-up or drop-off goods & passengers at waypoints.}

Insert: option places a Stop or waypoint on route, before an existing Stop/waypoint.
To insert a Stop or waypoint: click on an existing stop/waypoint in list to select (> by name shows Stop/waypoint is selected), then click on Insert option-button (changes cursor to halt-arrow), then click required point in game-view for Stop or waypoint (point on road, track or water as appropriate for type of vehicle).
The new Stop or waypoint is listed in Schedule Controls before the previously selected Stop/waypoint.
{Tips: Vehicles do not pick-up or drop-off goods or passengers at waypoints.}

Remove: option deletes a Stop or waypoint from the route.
Click on option-button to select, then click on item in list to remove.

replicate backwards: option adds existing Stops and waypoints in reverse order to route. Wait For levels are also replicated. Click on option-button to use: Schedule Controls lists new route.

Promote to Line: (only available when Schedule Controls is opened from Convoy Information) creates a new Line using current Schedule. The new Line is assigned a number and listed in Line Management.

simutrans-124.5/simutrans/text/en/script_generator.txt000066400000000000000000000020601520675460500233100ustar00rootroot00000000000000Save as Script Tool

Save as Script Tool

Here, all the structures the buildings und objects you previously selected with the dragged area are saved as a script. When you run the script later, it will attempt to build all of these structures and objects. Before you can save the script, you must assign a name. Alternatively, you can click on one of the existing scripts listed below and overwrite it.
You can delete scripts you no longer need by placing an X in front of it.

Click OK to save the new script tool.

You can find the saved tool in the personal simutrans directory, which also contains the saved game states in the save subdirectory. On Windows, this is located in "My Documents." On Linux, it can be found (possibly hidden) as "/home/[user]/.simutrans".
Within this personal directory, it is stored in simutrans/addons/[Pakset]/tool/.
The script can be viewed and edited using a standard editor. The structure is quite easy to understand.

simutrans-124.5/simutrans/text/en/script_tool.txt000066400000000000000000000004521520675460500223020ustar00rootroot00000000000000Load Script Tools

This window lists tools written with Squirrel that extend Simutrans functionality.
The tools are located in subdirectories in the simutrans/[Graphics Set]/tool/ or simutrans/addons/[Graphics Set]/tool/ directory.

simutrans-124.5/simutrans/text/en/server.txt000066400000000000000000000022041520675460500212440ustar00rootroot00000000000000Game information / Server

Game information / Server

Game information / Server will show you the current state of the game running, including the date, map size, number of companies, towns, citizens, factories, vehicles running, and so on.

If your current game is not already an online game, it will show you a dropdown list with other servers to connect. If this list is empty, there are no servers for your pak version or program version. You can then use the show all button to see all existing servers.

Upon selection of a server you will see a game information overview of the state of the server. (If the server is down, it may take a while to confirm this.) It will also display the pak set and program version of the server. If both are black, then your pak set and program version match the server and you can join by pressing Play online.

If there is a mismatch of the pak set, the entry will be red. You can then use the button Compare your pak to get a report on missing, different, or extra paks. (This may take a while.)

simutrans-124.5/simutrans/text/en/settings.txt000066400000000000000000000012701520675460500216000ustar00rootroot00000000000000Extended Settings

Extended settings

Extended settings allows to modify nearly every parameter of the game engine in the same way editing the central configuration file simuconf.tab. Therefore all parameters have exactly the same name as their counterpart in simuconf.tab, where you can also find the documentation of these parameters.

If you modify an existing game, you may need to solve and reload to apply the settings. Some settings (like factory connections) will only affect newly created objects.

Attention:
You can easily create an unplayable game or make you ruin game.

simutrans-124.5/simutrans/text/en/shiptools.txt000066400000000000000000000123701520675460500217670ustar00rootroot00000000000000Dock/harbour tools

Dock/harbour tools

Dock/harbour tools construct a marine and waterway transport network. Tools may build or remove: canals, locks, various docks, and a ship depot. If playing with timeline, then as time passes in Simutrans more tool-options may appear.

Click boat-icon at top of game-view to open toolbar.
Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name & where appropriate: construction cost, maintenance cost in brackets, and maximum speed limit.

Dock/harbour tools include, from left to right:

Canal: tool builds waterways, for boats and barges, between two points in game-view.
Canals can only be built on slopes in the direction of the slope & may not find a path across rough terrain. Canals built between points, may use existing canal in its path.
To build a canal: click on tool to select (changes cursor to canal), then click on terrain for start-point (shows a bulldozer in game-view and map co-ordinates on right of bottom-bar under game-view), & finally click on terrain for end-point.
{Tips: Canals built by different players may not connect. Use Landscape tools to change terrain, to enable paths for canals. Use Destroy/Remove to remove individual pieces of canal & some obstructions. Undo last route connection [z] does not refund constructions costs.}

Remove channels: tool removes portion of canal, when no vehicles are present, between two points in game-view (NB: Channel-stops in path are removed too). The tool has an associated construction cost.
To remove canal: click on tool (changes cursor to a cross); then click on canal that is to be deleted (selects deletion-point shown by a red cross in game-view); & finally click at another point on canal, to remove portion up to first deletion-point.

Ship Hoist: tool builds a canal lock and bridge to raise/lower a boat or barge by one level on a canal in game-view. The bridge has a maximum span and is built on the end of a canal to a suitable place (another canal-end or slightly higher land, within span).
To build bridge: click on tool to select bridge (changes cursor to hoist), then click on the end of a canal, (starting-point of bridge). Some obstructions or lack of a suitable place for bridge-ends may stop hoist being built: build canal at both sides that is to be bridged, & try again.
{Tips: Use Destroy/Remove to remove bridges (click on hoist-end) & some obstructions to bridge building.
Use Canal tools to connect bridge-ends to water ways.}.

Wharf (Canal Quay in pak128) tool builds a dock on a canal in game-view for water-transport-vehicles to pick-up and drop-off cargo and passengers.
A dock when not built adjacent to an existing Stop will create a new Stop.
A dock has a catchment area for goods, passengers & mail. In the corner of the tool-option is an icon (used in Station List & Stop Information) shows which items the dock allows a Stop to handle.
To build a dock: click on tool (changes cursor to a channelstop); then click on a canal in game-view to construct.
{Tips: Remove docks with Destroy/Remove. Press [v] to show/hide catchment area for goods and passengers in game-view.
Stops just for water transport vehicles will not be listed in ?Stops/Station list}.

Ship-stops: tools build various items that construct docks in game-view for water-transport-vehicles to pick-up and drop-off cargo and passengers.
Docks are built at edges of straight coast.
A dock when not built adjacent to an existing Stop will create a new Stop.
Docks have a catchment area for goods, passengers & mail. In the corner of the tool-option is an icon (used in Station List & Stop Information) that shows which items the dock allows a Stop to handle. Different docks may have different capacities for goods, passengers & mail.
To build a dock: click on tool to select (changes cursor to a dock) and then click on a slope by water to build
{Tips: Remove docks with Destroy/Remove.
Size of docks can be increased by building on adjacent squares. Water-transport vehicles may use any square of water in the catchment area of a dock.
Stops just for water transport vehicles will not be listed in ?Stops/Station list.
Off-shore Industry such as Oil Rigs do not require a dock, water-transport vehicles may use any square of water in the catchment area of an off-shore Industry.
Press [v] to show/hide catchment area for goods and passengers in game-view.}

Ship Depot: tool builds a depot for purchasing & managing ships, boats and barges. Depots have a maintenance cost & are built on water in game-view.
To build a ship depot: click on tool (changes cursor to a depot), then click on water in game-view.
{Tips: Remove depots with Destroy/Remove.}

simutrans-124.5/simutrans/text/en/signal_info.txt000066400000000000000000000016101520675460500222260ustar00rootroot00000000000000Signal Info Window

Signal info window

Information about the signal.
Name of the signal
Description Not always available
Owner Which player owns the signal
Directions Validity: Valid in which direction; for other directions the route is closed.
- 1 = South also diagonally to the west
- 2 = West also diagonally to the north
- 3 = diagonally to the north and south
- 4 = North also diagonally to the north
- 5 = North and south
- 8 = East also diagonally to the east or south
- 9 = diagonally to the east and west
- 10 = East and west
- 12 = diagonally to the north and south
Remove signal this signal will be removed
Painted by Name of the graphic creator

simutrans-124.5/simutrans/text/en/signal_spacing.txt000066400000000000000000000020011520675460500227120ustar00rootroot00000000000000Signal spacing

General usage:


-- Ctrl-click on signal icon brings up this gui.
-- Click-and-drag on track brings preview of signals to be built, release the mouse button to build. Signals will be built allowing trains to pass only in the direction you dragged the mouse.
-- In network games: first ctrl-click on the track and release mouse button. While you hold down mouse button and move the mouse, you see preview. Release button again to build.

Parameters in this window:

-- Signal spacing: how many tiles apart consecutive signals should be placed (diagonal count as 1/2 tile). This value is shown on the icon if the signal is selected.
-- Remove interm. signals: if toggled, other signals on the way will be deleted.
-- Replace other signals: if toggled, other signal on tiles we would like to build our signals on will be deleted.

These parameters can be set for all different signals independently.

simutrans-124.5/simutrans/text/en/simutrans.txt000066400000000000000000000047521520675460500217750ustar00rootroot00000000000000About Simutrans

Welcome to Simutrans!

First, select a map: either choose one of Simutrans' thousands of maps or create a new one from a height map. Then put yourself in the shoes of a young entrepreneur with some money from your grandparents: eager to make them proud, with ambitions of establishing a successful transport company, your challenge now is to build a prosperous transport empire.

As time passes, develop your company into a successful enterprise, whilst at the same time keeping the economy running smoothly by delivering freight & satisfying the travel requirements of millions of passengers. You can transport passengers, mail and goods by rail, road, ship and even air.

Every map has something different to offer. Take a good look at your surroundings: their future lies in the hands of you and your competitors.

You may find it easiest to begin by supplying a power station with the goods it needs. With the money you've made, gradually expand by supplying other factories - maybe even using central hubs where goods are reloaded. The disadvantage of only transporting goods is that towns and cities won't grow and new industries will not appear.

Alternatively, you may immediately go for passenger (and mail) transport. This is more challenging, as the inhabitants of the Simutrans world all wish to travel to specific destinations Some need to go to factories to work, some want to go shopping in the city centre, and others want to visit tourist attractions. But if you don't offer a connection to their desired destination, passengers will not travel with you at all.

You will need to build a transport network where as many popular destinations as possible are reachable. When your network is established, you will need to continuously upgrade it to keep abreast of demand, so plan ahead! If passengers like your service, towns will grow, new industries will be established, and only then will the economy run smoothly.

You can play as long as you like. Start in 1880 and finish in 2050, or set a start time anywhere between these dates. There will be new vehicles and buildings throughout this time. Play against the AI or on your own. But watch out: if your net wealth is negative for three months in a row, you will go bankrupt and the game is over. Check your net wealth in the Finances window.

The Simutrans Team and the community wish you an enjoyable time playing Simutrans!

simutrans-124.5/simutrans/text/en/slopetools.txt000066400000000000000000000044211520675460500221440ustar00rootroot00000000000000Landscaping Tools - raise/lower land

Landscaping Tools

Landscaping Tools raise & lower terrain and may be used to provide suitable places for construction.

Click on sloping-land-icon at top of the game-view to open toolbar. After opening or clicking on toolbar, hover the mouse cursor over the tools to see the names and construction costs.

Tools operate on areas empty of obstructions (e.g. roads, tracks, buildings, stops & stations).

Click on the tool icon to select it, changing cursor, then position the cursor on terrain in the game-view and click to use:

Raise land/Lower land: raises/lowers terrain. Cursor is an upward/downward arrow. Tools also open with [u] and [d] keys.

Build artificial slopes: creates a slope, in direction indicated by a toolbar icon. May create an artificial vertical surface in the process. Cursor is a yellow-cube outline which positions slope.

Raise square/Lower square: raises/lowers a specific square (tile) of terrain. May create an artificial vertical surface in the process. Cursor is a yellow-cube outline which selects square.

Restore natural slope: returns terrain to a natural landscape, removing artificial vertical surfaces in the process. Cursor is a yellow-cube outline which positions tool.

TIPS:
Landscaping Tools are easier to use with grid, press [#] and zoom-in the game-view.
Use Destroy/Removal Tool to remove some obstructions.
Depending on map, tools can raise land into/out of water.
Lowering the land to a certain level creates a channel.
Slope tools can be used to create tunnel slopes underground.
Depending on the pakset you play, Landscaping Tools toolbar may contain additional tools (usually used by public service player): raise/drain water in lakes, convert ground to a specific climate zone, etc. See also Game and map editing tools.

simutrans-124.5/simutrans/text/en/sound.txt000066400000000000000000000030301520675460500210640ustar00rootroot00000000000000Sound Settings

Sound Settings

Sound Settings window has controls for sound effects & background music. It opens from Game Options menu.
NOTE: To get better sounds in Windows an SDL-compiled version of Simutrans is recommended.

Use arrow-buttons to adjust settings or slider-bars to decrease/increase volume.

Sound volume: sets volume of sound effects in game. Sounds can be switched off completely with the Mute sound button.

Many Simutrans objects are able to produce sounds: ambiental (at cursor position after several seconds of no movement), way crossings (when approached by vehicles), factories (when producing goods), vehicles (when departing stop/stations). It depends on the pakset you play what sounds are implemented.

Music volume: sets volume of background music. Music can be switched off completely with the Mute MIDI music button. If no music is available then controls will not function.
Music tracks (in MIDI format) are stored in folder simutrans/music/.

Currently playing: changes music; arrow-buttons cycle through available music tracks.

Shuffle tracks: music tracks are played at random.

NOTE: If Simutrans is started with command-line option '-nosound' or '-nomidi', sound or music will not be used.

For more information about music in Simutrans visit http://wiki.simutrans.com

simutrans-124.5/simutrans/text/en/special.txt000066400000000000000000000140471520675460500213660ustar00rootroot00000000000000Special Construction Tools

Special Construction Tools

Special Construction Tools construct items that are not traffic related and also allow you to operate as another player in the game.

Click red-drop-in-yellow-circle-icon (crane-icon in pak128) at top of game-view to open toolbar.
Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name & where appropriate: constructioncost.

Special Construction Toolsinclude, from left to right:

Post Office: tools build a post office that enable a Stop to handle mail. Post Offices increase the capacity and catchment area of the Stop and have a maintenance cost.
To build a post office: click on tool to select, then click in game-view by an existing Stop. The new Post Office is now considered to be part of the Stop.
{Tips: Remove Post Offices and some obstructions with Destroy/Remove.}

Station Extensions: tools construct extensions & buildings for Stops. Extensions may increase capacity & catchment area for goods & passengers, and maintenance costs. In the corner of some tool-options an icon (used in Station List & Stop Information) shows which items the extension allows the Stop to handle.
To build an extension: click on tool to select (changes cursor to extension), then click required position, beside an existing Stop, in game-view. The new extension is now considered to be part of the Stop.
{Tips: Remove extensions with Destroy/Remove.}

P+ Change Player: allows game-play as another player if Allow player change was selected at the start of the game. Extra options are available as the public service player.
Click on tool (or press [P]) to cycle through players. A confirmation message appears that player has been changed: click on x in top-left corner of message or use keyboard to close.

found a new city: tools establishes a new urban area.
To build a new city: click on tool or press [C] to select (changes cursor to town hall), then click required position, in game-view. The new city costs 5 million credits and will be built without further confirmation.
{Tips: Click on the town hall in game-view with Destroy/Remove to remove the new city buildings.}

Plant trees: tool places a tree in game-view.
To plant a tree: click on tool to select (changes cursor to tree), then click required position.
{Tips: Remove trees with Destroy/Remove.}

Build Power Transmission Line: tool builds powerlines that supply power between transformer stations at power plants and industry (to increase productivity). The supply of power generates income. These powerlines cannot cross diagonal tracks and roads.
To build a high power transmission line: click on to tool or press [l] to select (changes cursor to power lines), then click on a transformer station placed next to a power station, & finally click on a transformer station at an industry to supply it with power to increase production.

Transformer station tool builds a transformer station at power plants or industry for connection to high power transmission lines.
To build a transformer station: click on tool or press [g] to select (changes cursor to a red-drop-in-yellow-circle), then click required position beside an industry or power plant, in game-view.
Transformer stations built at power plants must be connected to transformer stations at industry with powerlines to supply electricity.
If no electricity is being supplied, the transformer station in game-view shows a yellow-lightning-strike, when electricity is being supplied it shows a red-lightning-strike.
{Tips: Industry and power plants can have multiple transformer stations to increase electricity supply. Remove transformer stations with Destroy/Remove.}

Make a sign: tool opens controls to place a sign with personalised text in game-view and can be used to move game-view to a different point.
Click on tool to select or press [M], changes cursor to a text-marker, then click at required point in game-view to open Make a Sign controls. At the top of Make a Sign controls are co-ordinates on map of selected position and an empty box, enter required text in to box (click on box, type the text and click OK or press [Enter] or [Return]) to place sign. The new sign is shown in game-view and listed in Make a Sign controls (with map co-ordinates) when re-opened.
To move game-view to a sign point: open Make a Sign controls and click on required position in list.
To remove a Sign: click on Make a Sign tool or press [M] to select, changes cursor to a text-marker; then click on co-ordinates of sign in game-view; then click on Remove in Make a Sign controls.
To close Make a Sign controls: click on x in top-left-corner of controls; or click onCancel; or use keyboard.
{Tips: Map co-ordinates of cursors can be seen on right of bottom-bar under game-view. Use [!] to toggle texts of signs in game-view.}

Replace a stop: This tool can be used to replace a stop in the schedules of all of the convoys and lines that use it. It can also be used to change the scheduled tile within a stop (for example, to move all the lines to a different platform of a railway station). After selecting the tool, click on the stop (or part of a stop) that you wish to remove from the schedules and then on the stop (or part of a stop) that you wish to use in future. All the convoys that previously used the first stop will now use the second stop. Depending on the setup of your schedules, there may be some disruption to convoys travelling to the first stop at the moment of the change.

simutrans-124.5/simutrans/text/en/station.txt000066400000000000000000000166261520675460500214340ustar00rootroot00000000000000Stop Information

Stop Information

Stop Information provides information about a Stop (where vehicles pick-up and drop-off goods and passengers). It also has controls to access further details and change the name of the Stop.

A Stop consists of a place where a Convoy can pick-up or drop-off goods and passengers and any extension buildings.

Tools for the construction of places where Convoys can accept or deliver are available from the toolbar for the respective type of transport: ?station-platforms; monorail/maglev train stops; Tram Stops; Road-stops; Docks ; and Airstops.
{Tips: Water-transport vehicles may use any square of water in the catchment area of a dock to pick-up and drop off goods and passengers. Larger Stops can be built by constructing on adjacent squares. Stops can be built that accommodate more than one type of transport}.

Click on a Stop in game-view with the Inspection Tool or click on a Stop listed in Station List or Line Management to open Stop Information which shows the name of the Stop in its titlebar.
Stop Information contains a name-box; a mini-view of Stop; information about the Stop; option buttons; and a list of all items ready for transport at Stop (if all items are not visible re-size Stop Information or use slider-bars to scroll items):

name: assigned name of Stop is shown in name-box at top of Stop Information.
By default name is assigned automatically when the Stop is first built and depends on urban area related to Stop and any industry within catchment area of Stop.
To change name: click on name-box and type new name.
{Tips: Use [!] to toggle view of name above a Stop in game-view. Use [v] to toggle the view of the catchment area of the Stop in game-view. Option to number Stops available in simuconf.tab}

status-colour-bar: colours indicate the status of the Stop, i.e. how overcrowded it is. The status-colour-bar is also used in Station List and Line Management and is the same colour displayed in the colour-bar above a Stop in game-display:
- yellow: no service and nothing waiting.
- green: no improvements necessary.
- orange: stop is slightly overcrowded (with transfer goods usually).
- red: stop hold more than 1.5 of its capacity, or more than 200 unhappy passengers, or a factory must stop its production because the station is overcrowded. These stops will be reported as overcrowded at the beginning of a month.
{Tips: Use [!] to toggle view of status-colour-bar (that indicates how crowded a Stop is) above a Stop in game-view.}

Graphic Icons are also used in Station List and Line Management:
- freight icon(s) indicate which items (passengers, goods and mail) a Stop can handle.
{Tips: adding appropriate extensions can change the category of items a Stop can handle. Post offices can be added to Stops to enable handling of mail}.
- vehicle icon(s) indicate which types of vehicle can use Stop. Icons include: bus (for passengers), truck (for goods), train (for rail), boat and airplane. Trams can be indicated by a bus-icon or a train-icon depending on type of Stop.

Storage capacity: amount of each item (goods, passengers and mail) the Stop can accommodate.

Stop picture: is a mini-view of the Stop and any visible Convoys.
Click on the picture to centre game-view on Stop.

Passengers numbers indicate the amount of passengers and mail who start their journey at the Stop:
- happy face indicates the number of passengers, who have arrived at an uncrowded Stop as a result of its catchment area and have found a route to their destination and waited for transport.
- unhappy face indicates number of passengers who have arrived at Stop as a result of its catchment area but did not wait for transport as it was overcrowded.
- no route indicates number of passengers who have arrived at Stop as a result of its catchment area but left as they did not find a route to their destination.
{Tips: Keeping passengers happy helps growth of urban areas. Change maximum number of transfers allowed for goods and passengers in simuconf.tab}.

Goods and passengers waiting: lists items that wait for transport at Stop in groups of: passengers; mail; and type of goods.
Information shown for items includes quantity, type, final destination and first transit Stop.
Option-button (changes name with selection) sorts of items within groups:
- destination: sorts items, alphanumerically within ASCII-code order (capital letters before lower case letters), by assigned name of final destination Stop.
- amount: sorts items by quantity in descending order. - via (detail): sorts items, alphanumerically within ASCII-code order (capital letters before lower case letters), by assigned name of first transfer Stop.
- via (amount): sorts items by quantity headed to first transfer Stop.
- via (owner): sorts items carried by quantity by the owner of the next stop.
- connection: sorts items by their next connection, either line(s) or lineless convois. Most waiting passengers first.
{Tips: goods-colour-bars above a Stop in game-view (use [!] to toggle) indicate quantity of items that wait for transport and are the same colour as colour-squares in Goods List.}

Chart: click option-button to toggle graph (button is indented when graph is visible) in Stop Information.
The graph shows statistics for the last 12 months (x-axis) when a graph-option is selected.
Click on graph-option-button to see information on graph (button is indented when option selected).
Colours of lines on graph correspond to colours of graph-option-buttons:
- Happy number of passengers/mails, who have arrived at the Stop, have found a route to their destination and so started their journey.
- Unhappy number of passengers/mail who have arrived at the Stop but did not wait for transport as it was overcrowded.
- No Route number of passengers/mail who have arrived at the Stop but left as they did not find a route to their destination.
- waiting: amount of goods, mail, and passengers at the Stop that wait for transport.
- Arrived: amount of goods, mail, and passengers dropped-off by a Convoy at Stop as a final destination or transfer point.
- Departed: amount of goods and passengers picked-up at the Stop by a Convoy.
- Vehicles: amount of Convoys that have departed from Stop.

Details: option-button opens Stop Details that provides further information about the Stop.

simutrans-124.5/simutrans/text/en/station_details.txt000066400000000000000000000031131520675460500231240ustar00rootroot00000000000000Stop Details

Stop Details

Stop Details provides more information about a Stop (where vehicles pick-up and drop-off goods and passengers).

Stop Details opens from Details option-button in Stop Information.
If all items are not visible re-size Stop Details or use slider-bars to scroll items)

Information listed includes:
Connected factories: names and map co-ordinates (in brackets) of industry in catchment area of Stop.
{Tips: Use [v] to toggle view of catchment area of stops in game.}

Goods needed by nearby industries: types of goods required by industry in catchment area of Stop.

Lines serving this Stop: Lines that use this Stop.
Lines are listed alphanumerically within ASCII-code order (capital letters before lower case letters).

Lineless convoys serving this stops: Convoy not assigned to a line, which stop here.

Direct routes from here:lists name of Stops that can be reached without a transfer and type of items (goods and passnegers) that can be delivered.
Stops are listed alphanumerically within ASCII-code order (capital letters before lower case letters) by ?type of transport, name of Stop, and type of item that can be delivered (which is also shown for each Stop).

simutrans-124.5/simutrans/text/en/trafficlight_info.txt000066400000000000000000000011201520675460500234130ustar00rootroot00000000000000Traffic light control

Traffic light control

You can control the frequency of red and green phases for traffic lights. The numbers you can choose here, is the duration of the green phase in seconds for north/south and east/west directions.

Since all traffic lights will switch in phase, you can give an extra offset in the third field to realize an unobstructed traffic flow in certain directions through subsequent traffic lights.

A traffic light is built from the road construction toolbar.

simutrans-124.5/simutrans/text/en/tramtools.txt000066400000000000000000000170211520675460500217650ustar00rootroot00000000000000Tram/light rail tools

Tram/light rail tools

Tram/light rail tools construct a tram transport network. The tools may build: tramtrack (and electrify or remove it), signals, a tram depot, and tram stops. If playing with timeline, then as time passes in Simutrans more tool-options may appear.

Click tram-icon at top of game-view to open toolbar.
Hover mouse-cursor over tool-option (after opening or clicking on toolbar) to see name & where appropriate: construction cost, & maximum speed limit.

Tram/light rail tools include, from left to right:

Tram Track: tools build Track, for rail vehicles between two points.
Tram Track can be built along roads and in urban areas, but not on areas occupied by other buildings. Tracks can only be built on slopes in the direction of the slope & not over rough terrain, water and obstructions. New Track built may use existing track in its path.
To build Track: click on tool to select Track (changes cursor to track); then click on game-view for start-point of Track (shows a bulldozer in game-view and displays map co-ordinates on right of bottom-bar under game-view); & finally click on game-view for end-point for Track.
{Tips: Different types of tracks may connect (however not when built by some other players). Use bridges & tunnels to connect tracks across rough terrain or to avoid obstructions. Use Destroy/Remove to remove individual pieces of Track & some obstructions. Use [Ctrl] at same time for extra functions. Undo [z] does not refund constructions costs.}

Electrify Track: tools electrify Track, bridges or tunnels between two points in game-view, for use by electric vehicles.
To electrify Track: click on tool or press [e] (changes cursor to electric-track-icon) to select, then click on Track in game-view for start-point of electrification (places a electric-track-icon in game-view), & finally click on a second point on Track, to electrify portion of Track up to start-point.
{Tips: Use Destroy/Remove to change back to non-electric Tracks.}

Remove Tracks: tool removes Track & electric Track, when no vehicles are present, between two points in game-view (station-platforms, road-stops, signals, tunnels & bridges in path are removed too). Use of tool incurs a construction cost.
To remove Track: click on tool (changes cursor to a red-cross); then click on track that is to be deleted (selects deletion-point shown by a red cross in game-view); & finally click at a second-point on connected Track, to remove portion up to first deletion-point.
{Tips: Operate as another player to remove their Tracks.}

Rail Signals: tools build signals for rail vehicles on a Track in game-view. Signals direct & regulate flow of vehicles on tracks & bridges, junctions & Stops (where vehicles pick-up and drop-off goods and passengers).
Two-way & one-way versions of signals can be built. To build a two-way signal on track: click on tool to select signal (changes cursor to signals); then click on track. To build a one-way signal: click again at same point, with signals-cursor, to cycle through one-way signals, back to a two-way signal.
IMPORTANT: Take care not to place one-way signals that prevent vehicles reaching their destination, by default vehicles drive on the right (can be changed in simuconf.tab).
- Signals: vehicles proceed only if Track ahead up to next signal or destination in Schedule (a Stop or waypoint) is not occupied by another vehicle. In one-way mode vehicles pass in one direction only.
- RailClose: vehicles do not proceed beyond signal (available in one-way mode only).
- PreSignals: vehicles proceed if area of Track ahead (either between three consecutive signals or to next destination in Schedule) is not occupied by another vehicle. In one-way mode they let vehicles pass in one direction only.
- ChooseSignals: direct vehicles to an empty station-platform, at a multi-platform Stop: a vehicle passing this signal may use any empty platform at its next destination, and not just the one assigned in its schedule. If no empty platform or clear route to next destination is found, then vehicles wait at signal.
{Tips: Remove signals with Destroy/Remove. Hold down [Ctrl] to place signals on bridges over lower Tracks.}

Tram Depot: tool builds a depot for purchasing & managing trams.
Depots have a maintenance cost & are built on the end of track in game-view.
To build a tram depot: click on tool (changes cursor to a depot), then click on end of Track.
{Tips: Depots only show electric vehicles if they are on electric Track. Remove depots with Destroy/Remove.}

Rail-stops: tools build station-platforms, used by rail vehicles to pick-up and drop-off goods, passengers & mail.
A station-platform when not built adjacent to an existing Stop will create a new Stop.
Rail-stops have a maintenance cost & are built on Track (but not at bends and junctions in Track).
Rail-stops have a catchment area for goods, passengers & mail. Different station-platforms may have different capacities for passengers and mail. In the corner of some tool-options an icon (used in Station List & Stop Information) shows which items the station-platform allows a Stop to handle.
To build a a station-platform: click on tool to select (changes cursor to a platform), then click on Track.
{Tips: Remove Rail-stops with Destroy/Remove. Extend station-platforms & build multi-platform Stops (by building more platform sections on adjacent Track) to accommodate longer and more vehicles, increase capacity & catchment area. Press [v] to show/hide catchment area for goods & passengers in game-view. Hold down [Ctrl] to build on bridges above lower Tracks.}

Car Parking: (available only in pak 128) tools construct extensions for Stops which increase capacity & catchment area for goods & passengers, and maintenance cost.
To build an extension: click on tool to select extension (changes cursor to extension), then click required position on a road, beside an existing Stop in game-view. The new extension is now considered to be part of the Stop.
{Tips: Remove extensions with the Destroy/Remove.}

?Road-stops: tools build places for trams and road vehicles to pick-up and drop-off goods, mail, and passengers.
A Road-stop when not built adjacent to an existing Stop will create a new Stop. Road-stops are built onroad& have a maintenance cost and a catchment area for goods, passengers & mail. In the corner of some tool-options an icon (used in Station List & Stop Information) shows which items the Road-stop allows a Stop to handle. Different Road-stops may have different capacities for goods, passengers & mail.
To build a Road-stop: click on tool to select (changes cursor to roadstop), then click required position on a road in game-view.
{Tips: Remove Road-stops with the Destroy/Remove. Hold down [Ctrl] to build on bridges above lower roads.}

simutrans-124.5/simutrans/text/en/underground.txt000066400000000000000000000037001520675460500222740ustar00rootroot00000000000000Building Underground

Building Underground

There are two possible underground views in Simutrans, and both can be accessed from Display Settings window or using key shortcuts.

Underground view: By pressing the [U] key, all underground levels are revealed at once and all objects that are above ground are hidden. The surface grid is displayed by default, but it is quite difficult to place objects at desired location using this grid pattern, and therefore next map view is recommended for underground construction.

Sliced map view: With [Ctrl+U] you can choose to display a single elevation level of what is underground in Simutrans. This view hides all objects that are over the selected layer, either underground or on surface. The current view level can be adjusted from Display Settings window, or using some keys which are pakset specific. A grid pattern of the active layer is shown. Since the mouse pointer can only be moved within the current level, construction work is easier.

In general, underground routes are built using tunnel building tools. In order to build underground, a tunnel or a tunnel entrance (Ctrl + click with tunnel tool on a slope) must be built first.

To build on another layer underground, use Build artificial slopes tools from the Landscaping Tools menu. Simply click on the end of the road/track with the desired tool.

Tram tracks can be laid underground with the normal Tram/light rail tools on existing roads.

Only own stop/station can be built on top of each other. Station extensions cannot be built underground.

NOTE: If cannot build further underground, check on surface for obstacles.

simutrans-124.5/simutrans/text/en/vehiclelist.txt000066400000000000000000000012101520675460500222450ustar00rootroot00000000000000List of all vehicle

List of all in the game Vehicle types

By default, all vehicles currently coming current and future will be shown. Show the future is also the real sense of this list. In the depots, the upcoming vehicles are not visible. The player can better plan for the future.

Show future by deselection, the list can be limited to the currently purchasable vehicles.
Show obsolete expands the list with the already outdated vehicles. This feature is the same in the depots.
Behind it at All can be restricted to the type of goods as well as passengers or post.

simutrans-124.5/simutrans/text/en/window.txt000066400000000000000000000217541520675460500212600ustar00rootroot00000000000000Game Interface Usage

Game Interface

Simutrans is played through the Game Interface, which provides information about the current game, access to toolbars & game-controls and a view of the current game-world. Simutrans can be controlled via mouse and via keyboard. The keyboard layout is available in the help menu at Keyboard Binding section, or pressing [F1] key, and also appears every time a key without function is pressed.
IMPORTANT: Keys are case-sensitive.

Game Window

Shows a portion of the current game-world. You can zoom-in and zoom-out using the mouse wheel, or [PgUp]/[PgDown] keys, or two dedicated icons found on the main toolbar. Scroll the map in several ways:
Hold down the right mouse button and scroll the map in any direction towards your desired position. In the default setting, the map scrolls against the movement of the mouse. You can change this behaviour in Display Settings window.
Use the numeric keypad.
Jump directly to a desired map section by selecting the appropriate point on the Map Window.
Jump to specific coordinates using Jump to dialog (press [J] key).

The names of several objects are editable, such as towns, factories, stations, vehicles and transport lines. To edit, click with the mouse in the name field of the dialog opened by the Inspection Tool (see below). Edits become effective immediately. Press [Return] to exit the field.

Title Bar

The left part of the titlebar shows the version and release date of Simutrans that is currently running. The right hand side of the titlebar contains controls to minimize, maximize the Game Interface and also to exit Simutrans.

At the top of the game window is a row of icons (main toolbar) used to access game-toolbars and game-controls when playing Simutrans. Icons (click to use) are grouped in several menus:

Main Menu

Options: The floppy-disk-icon opens a dialogue from where you can load, save or start a new game.

Map: The overview map of the world with its objects shows the movements of the player vehicles and allows seeing some statistical views. If you select a point on this map, the game window jumps to the corresponding image section.

Inspection Tool: The magnifying-glass-icon symbolizes the query tool. If it is active, the cursor also becomes a magnifying glass. If you select an object with it (building, vehicle, etc.), an information window will be displayed above it. It is advisable to always return to magnifying glass, especially if you have just worked with the Removal Tool. So you avoid destroying an object accidentally, instead of calling up the object information.

Landscaping Tools: The sloping-land-icon opens a toolbar from where you can alter the map by changing the slope of each tile.

Building Toolbars: Opens a set of building toolbars for each type of transportation.
Railroad Tools
Monorail/Maglev Tools
Tram/light rail Tools
Road Tools
Shipping Tools
Airport Tools

NOTE: Some paksets may contain additional building toolbars, e.g. Narrow Gauge Railroad Tools.
Each toolbar has a depot icon. Once a depot is built, you can enter the depot window to access vehicles.

Special Construction Tools: The toolbar that opens by clicking red-drop-in-yellow-circle-icon (crane-icon in pak128) contains a variety of construction tools that complement the construction of transportation networks.

Destroy/Removal Tool: The red-cross-icon (bulldozer-icon in pak128) is used to remove various objects from the map.

Management Menu

Line Management: Transport lines can be set up here (network-icon), changed or deleted, as well as information about them can be viewed.

Lists: The symbol of the list sheet leads to a submenu with seven lists:
Station List
Vehicle List
City List
Goods List
Factory List
List of Tourist Destinations
List of Markers

Message Centre: All messages that have been generated since the start of the game or since the last time a game was loaded will be listed when clicking this mailbox-icon.

Finances: This money-icon opens a window that shows everything worth knowing about your current financial situation.

Other Menus

Screenshot: When using the camera-icon button, a snapshot of the current game window (including toolbars, but without titlebar) is saved as a .png image in folder simutrans/screenshot/. An image can also be captured with [c] key.

Pause: If you want to take a break from the hustle and bustle of the transport company, order a hot cup of tea or coffee over this coffee-mug-icon. The service is of course free in Simutrans!
The key [p] can also be used to pause/unpause game.

Fast Forward: If time in Simutrans passes too slow for you, you can accelerate it, by pressing this >> icon. Fast forward can also be selected/deselected with key [W].
WARNING: In fast forward mode, game may not run smoothly. Display performance information section at the bottom of Display Settings window indicates if the computer can cope with the change.

Help: The ?-icon opens the help index, also with [F1] key.

NOTE: Depending on pakset you play, there might be more icons on the main toolbar: Zoom in/Zoom out, Show grid, Rotate map, etc.

Status Bar

The status bar at the bottom of game-world window contains some information that is constantly updating as the game progresses. Information provided, from left to right on the status bar includes:
Date and Time: In Simutrans, one year is divided into months, days and hours. The date controls the availability of vehicles and buildings when the 'timeline' is activated (see below). In front of date an icon with meteorological season is displayed. For some climate settings, the ground may change with the seasons.
Player: The name of the current player is displayed in its player color.
Account balance: The cash of current player is displayed and permanently updated. It takes into account construction and other costs (purchasing vehicles, maintaining transport infrastructure and operation costs of running vehicles).
Position coordinates: Inside brackets is displayed the x and y coordinates and the height of the tile over which the cursor is currently located. The coordinates (0,0) are located in the top-left corner of the map and the height zero is at water level or above it (depends on water level settings at the creation of the map)
Game speed: The value (T=1.00) indicates the current game speed. You can accelerate/decelerate time in small increments using [,]/[.] keys. Display performance information section at the bottom of Display Settings window indicates if the computer can cope with the change. In fast forward mode, an additional icon >> is displayed.
Obey era: Indicates by a calendar-icon whether a timeline has been selected in the current game.

Scroll Bar

Occasionally, messages are displayed above the status bar at the bottom of the game-world window. The messages to be displayed can be defined in the Message Centre Options.
TIP: Click on a message to center game-view to a related position.

simutrans-124.5/simutrans/text/eo.tab000066400000000000000000001323151520675460500176750ustar00rootroot00000000000000§Esperanto PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: eo Esperanto # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable Nevalidigita cl_btn_filter_enable Validigita cl_btn_filter_settings Agordoj cl_btn_sort_asc Kreske cl_btn_sort_desc Malkreske cl_btn_sort_id ID nombro cl_btn_sort_income Enspezo cl_btn_sort_name Nomo cl_btn_sort_type Tipo clf_btn_alle Ĉiuj clf_btn_invers Inverse clf_btn_keine Nenio gl_btn_sort_bonus Kromenspezo gl_btn_sort_name Nomo gl_btn_sort_revenue Enspezo gl_btn_unsort Neordigite hl_btn_filter_disable Neebligite hl_btn_filter_enable Ebligite hl_btn_filter_settings Agordoj hl_btn_sort_asc Kreske hl_btn_sort_desc Malkreske hl_btn_sort_name Nomo hl_btn_sort_type Tipo hl_btn_sort_waiting Atendantoj hlf_btn_alle Ĉio hlf_btn_invers Inverse hlf_btn_keine Neniu Maximize height levels Maksimumigi altecon Networks Transportretoj Queueing Atendantoj Road toll Vojimposmo Scenario Scenaro Start this as a server Komenci servilon per ĉi tiu ludo. Transfers Transvagoniĝantoj #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Polusa klimato desert Dezerta klimato mediterran Mediteranea klimeto rocky Altmonta klimato temperate Mezvarma klimato tropic Tropika klimato tundra Tundra klimato #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Malsukcesis legi scenaron Bridge blocked by way below or above. Ponto estas malhelpata de vojo supre aŭ sube.\n Can't buy obsolete vehicles! Ne povas aĉeti elservigitajn veturilojn. Cannot alter water Ĉi tiu terkavo ne povas esti plenigita nek malplenigita. Cannot build on a double slope! Povas konstrui nur sur unuobla deklivo aŭ ebenejo. Cannot built depot here! Ne povas konstrui veturilejo ĉi tie. Cannot built this station/building\nin underground mode here. Ne povas konstrui ĉi tiun stacidomon en subtero.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. Ne povas krei universalan linion. Elektu linian tipon uzante tabon. Cannot create socket Ne povas krei ingon. Cannot rotate this building! Ne povas turni ĉi tiun konstruaĵon. Client closed connection during transfer Kliento ĵus malkonektis dum transdoni. Convoi handles exhausted! Atingis maksimumajn nombrojn de veturilo. Convoy already deleted! Veturilo jam estas eliminita! Could not open file Ne povas malfermi la dosieron Das Feld gehoert\neinem anderen Spieler\n Ĉi tiu loko estas okupita\nde alia ludanto!\n Der Besitzer erlaubt das Entfernen nicht La proprulo malpermesas malkonstrui ĉi tion!\n Diese Zusammenstellung kann nicht fahren!\n Ĉi tiu kombinado ne povas ekveturi!\n Flugzeughalt muss auf\nRunway liegen!\n Aviadila haltejo devas esti sur kurejo.\n Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Vi ne povas konstrui hangaron ĉi tie.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Ne povas meti signalilon ĉi tie.\n In order to lock the game, you have to protect the public player by password! Por ŝlosi la ludon, vi devas protekti la publikan ludanton de pasvorto! Loading a new game will end the current server session! Malfermi novan ludon finigos la nunan servilan ludon! Lost connection\nto server! Perdis konektadon al servilo. Lost synchronisation\nwith server. Perdis sinkronigon kun servilo. Maglevhalt muss auf\nMaglevschiene liegen!\n Magnetvoja stacio devas esti sur magnetvojo! Monorailhalt muss auf\nMonorail liegen!\n Unurela stacio devas esti sur unurelo! Monorails are not available yet! Unurela ankoraŭ ne estas uzebla! More than one possibility to build this dock found. Trovis pli ol unu eblecon por konstrui la dokon. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Etŝpura fervoja stacio devas esti sur etŝpura fervojo! No bridges over runways! Ne povas konstrui ponton super kurejo! No suitable way on the ground! Oni ne povas konstrui ion ĉi tie. No through station here! Oni ne povas konstrui haltejon ĉi tie! Not enough bytes transferred Malsukcesis sendi datumon. Not enough clearance. Minimuma alteca diferenco necesas inter du vojoj. Not enough money! Mankas mono al vi. On narrowgauge track only!\n Nur sur Etŝpura vojo! Only public player can lock games! Nur publika ludanto rajtas ŝlosi ludon. Out of funds Mankas mono al vi! Post muss neben\nHaltestelle\nliegen!\n Oni devas konstrui stacidomon kaj poŝtejon apud haltejo. Protocoll error (expecting game) Protokola eraro Schiffhalt muss im\nWasser liegen!\n Ŝipa povas halti sur nur akvo ĉirkaŭ doko aŭ haveno! Server busy Servilo okupita Server version too new La servilo uzas pli novan version. Show all revenue messages Montri enspezoj de ĉiuj ludantoj Show no revenue messages Malmontri enspezoj de ĉiuj ludantoj Show only player's revenue Montri nur enspezo de tiu ĉi ludanto Terraforming not possible\nhere in underground view Oni ne povas terformi subteron. This tunnel branches. You can try Control+Click to remove. Ĉi tiu tunelo havas pli ol du enirejon. Vi povas forigi ĉi tiu tunelon per Ctrl+alklaki. Upgrade must have\na higher level Promocio devas havi pli altan nivelon. Vehicle %s cannot choose because stop too short! Veturilo %s ne povas trovi por halti pro tro mallongeca haltejo. Zughalt muss auf\nSchiene liegen!\n Fervoja stacio devas esti sur fervojo. #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s

Enhavo

*: nur la angla

Ĝeneralo

%s

Uzado

%s

Iloj

%s

Komenci

%s

Kiel ludi

%s

aliaj:

%s Keyboard Help\n

Keyboard Help

\n Klavara helpo\n

Klavara helpo

\n Color according to transport capacity left Koloro laŭ resto de transporta enhaveco Decrease water height Malaltigi akvan nivelon Highlight railroad tracks Emfazi fervojojn Highlite depots Emfazi veturilejojn Highlite electrical transmission lines Emfazi elektroretojn Highlite factories Emfazi fabrikojn Highlite forests Emfazi arbarojn Highlite tourist attraction Emfazi altiraĵojn Increase water height Altigi akvan nivelon Open station/stop details Malfermi haltejan detelon Overlay city limits Montri urblimojn Overlay passenger destinations when a town window is open Montri pasaĝeraj celejoj kiam urba fenestro malfermiĝas Overlay schedules/network Montri transportreton Overlay town names Montri urbnamojn Please click on the map to add\nwaypoints or stops to this\nschedule. Bonvolu klaki sur la mapo por aldoni vojpunktojn aŭ haltejojn al la liniplano. Set tile climate %s Ŝanĝi ĉi tiean klimaton je %s Show capacity and if halt is overcrowded Montri enhavecon kaj ĉu haltejo estas superenhaveca. Show how many convoi reach a station Montri kiom da veturiloj atingis stacion Show how many people/much is waiting at halts Montri kiom atendas Show initial passenger departure Montri pasaĝeran kaj poŝtaĵan elirpunkton Show level of city buildings Montri nivelon de domoj Show mail service coverage/mail network Montri poŝtan kovrareon Show passenger coverage/passenger network Montri pasaĝerservan kovrareon Show speedlimit of ways Montri rapidecan limon Show the change of waiting at halts Montri kiom da atindantoj kreskis/malkreskis ekde lasta monato Show the owenership of infrastructure Montri proprulon de substrukturo Show transported freight/freight network Montri transportitajn frajtojn Show usage of network Montri trafikon pokahele kaj pomonate Shows a listing with all industries on the map. Montri liston de ĉiuj fabrikoj ekzistantaj sur la mapo Sum of departure/arrivals at halts Montri kiom da veturiloj alvenitaj kaj eliritaj #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s nun\nofertas busan servon\ninter %s\nkaj altiraĵo\n%s\nĉe (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s nun\nofertas busan servon\ninter %s\nkaj fabriko\n%s\nĉe (%i,%i).\n %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\nmalfermis novan fervojon\ninter %s\nĉe (%i,%i) kaj\n%s\nĉe (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Aerlinia servo de\n%s\nnun estas ofertata inter\n%s \nkaj %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Pramŝipa servo de\n%s\nnun estas ofertata inter\n%s \nkaj %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Vojaĝantoj nun\nuzas busoj de\n%s inter\n%s \nkaj %s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Mapaj ŝanĝiloj LISTTOOLS Listoj MAGLEVTOOLS Magnetvojaj iloj MONORAILTOOLS Unurelaj uloj NARROWGAUGETOOLS Etŝpuraj fervojaj iloj RAILTOOLS Fervojaj iloj ROADTOOLS Vojaj iloj SHIPTOOLS Ŝipaj iloj SLOPETOOLS Terformiloj SPECIALTOOLS Specialaj iloj TRAMTOOLS Tramaj iloj #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s ĵus konstruis novan ĉefsidejon. Factory chain extended\nfor %s near\n%s built with\n%i factories. "%s" grandigis sian aferon apud %s, kaj konstruis novajn %i fabrikojn. Net wealth less than 10%% of starting capital! Via riĉeco estas nun malpli ol 10%% de komenco! New %s now available:\n%s\n Nova %s estas nun uzebla:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Nova fabriko %s ĉirkaŭ %s konstruis kun %i fabrikoj. New vehicle now available:\n%s\n Nova veturilo estas nun uzebla:\n%s\n Now %u clients connected. Nun %u ludantoj estas en ĉi tiu servilo. overtaking Preterpasi Remove vehicle from map. Use with care! Atentu! Forigos veturilon el mapo! Screenshot\ngespeichert.\n Ekranfotis. Sends the convoi to the last depot it departed from! Sendas la veturilon al la plej proksima veturilejo. Server preparing game ... Servilo preparas ludon ... With a big festival\n%s built\na new monument.\n%i citicens rejoiced. Kun granda festo, %s konstruis novan monumenton, kaj %i loĝantoj ĝojis. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Linia filtrilo #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (En veturilejo) \nBauzeit bis ĝis \nBauzeit von \nAperas de \nCan't open heightfield file.\n \nNe povas malfermi altecan mapon.\n \ndirection: \ndirekto: \nelektrified \nelektrizita\n \nHeightfield has wrong image type.\n \nLa alteca mapo havas malĝustan tipon.\n \nis reserved by: \nrezervita de \nminimum speed: \nminimuma rapideco: \nnot elektrified \nneelektrizita\n \nRibi (masked) \nDirekto\m (maskita): \nRibi (unmasked) \Direkto\m (nemaskita): \nSet phases: \nAgordi faza tempo\nSurd-nordo Orient-Okcidento: \nsingle way \nUnudirekto \nway1 reserved by \nUnua vojo estas rezervita de \nway2 reserved by \nDua vojo estas rezervita de \nwith sign/signal\n \nkun trafiksigno/signalilo\n %d buildings\n %d domoj\n %d convois %d vagonaroj %d Einzelfahrzeuge im Depot %d veturiloj enmetiĝas ĉi tie. %i car(s), %i aŭtoj, %i km/h (max. %ikm/h) %i km/h (maks. %i km/h) %i years %i months old. Aĝo: %i jaroj kaj %i monatoj %s at (%i,%i) now public stop. %s ĵus fariĝis publika haltejo. %s building %s %s %s %s %s %s city %d %s %s %d-a %s %s factory %s %s %s %s %s %s has entered a depot. %s ĵus revenis veturilejen.\n %s land %d %s %s %d-a ekstera %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s \nkonstruis \nnovan urbdomon\nkiam ĝi atingis \n%i loĝantojn. %s\nis crowded. %s amasas. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nmaksimuma rapideco %3$i km/h\n\nnuna rapideco %2$i km/h.\n\n %s\nwas liquidated. %s bankrotis kaj estis likvidita .Tiu operacio estis haltita, kaj ĉiuj aktivaĵoj estis venditaj. %u Client(s)\n %u Kliento(j)\n %u Player (%u locked)\n %u Ludanto (%u ŝlosita)\n

Error

Averton: Duoblaj objektoj en ĉefa paka dosierujo

En

Warning

addons for

Averton: Duoblaj objektoj en ĉefa paka dosierujo

Aldonoj por 1 convoi 1 veturilo 1 Einzelfahrzeug im Depot 1 veturilo enmetiĝas ĉi tie. 100 km/h = %i tiles/month 100 km/h = %i ĉeloj/monato 1LIGHT_CHOOSE Heleco: 1WORLD_CHOOSE Agordoj por nova mapo: 2LIGHT_CHOOSE Koloroj: 2WORLD_CHOOSE Mapa nombro: 3LIGHT_CHOOSE Ruluma rapideco: 4LIGHT_CHOOSE Inversigi rulumi 5LIGHT_CHOOSE Montri piedirantojn 5WORLD_CHOOSE Nombro de urboj: 6LIGHT_CHOOSE Montri piedirantojn 6WORLD_CHOOSE Trafika denseco: 8WORLD_CHOOSE Mallumigi dum nokto A bridge must end on a way! Ponto devas fini sur vojo! A bridge must start on a way! Bonvolu konstrui ponton sur senkurba vojo. Abfrage Inspektilo Abnehmer Konsumanto: About Klarigo About scenario Pri scenaro Abriss Malkonstrui Absenken Malaltigi teron Abspanntransformator Transformilejo Accelerate time Rapidigi tempon Act. load: %u MW\n Sendkvanto: %u MW\n Active player only Montri nur aktivan ludanton Add forest Fari arbaron Add random citycar Meti aŭton sur vojo add server Aldoni servilon Add Stop Aldoni Add stops for backward travel Aldoni reveturajn haltejojn air Aviadilo aircraft_tab Aviadilo airplane Aviadilo Airport Flughaveno AIRTOOLS aviadilaj iloj All Montri ĉion all convoi tooltips Montri veturilajn statojn ĉiam Allow city growth Permesi al urbo kreski Allow player change Permesi al ludanto ŝanĝi allowed climates:\n Permesitaj klimatoj:\n Alters a schedule. Administri liniplanon Angenommene Waren Fabrikaĵoj bezonataj de apudaj fabrikoj anhaengen Aldoni Anhaenger_tab Postveturiloj Anheben Altigi teron Appends stops at the end of the schedule Aldoni haltejon fine de liniplano Apply Line Asigni linion April Aprilo Arbeiter aus: Laboristoj loĝas en: Arrivals from\n Alvenoj de\n Arrived Alvenintoj Assets Aktivo Aufloesen Malkunigi Aufspanntransformator Transforma stacio Aug. Aŭg. August Aŭgusto Autohalt muss auf\nStrasse liegen!\n Busoj kaj veturiloj devas esti konstruitaj sur la strato\n Available Uzebleco Bahndepot Trajna veturilejo Bankrott:\n\nDu bist bankrott.\n Bankroto:\n\nVi Bankrotis!\n battery Baterio Baum Arbo baum builder Plantilo Baustelle Konstruejo Bauzeit Konstrutempo Beenden Fini ludon Beginner mode Mi estas komencanto! Besonderes Gebaeude Altiraĵo BF Stacio bio Vivaĵo Blockstrecke ist\nbelegt\n \nBloksekcio estas uzata de alia veturilo!\n Boden Tero Bonusspeed: %i km/h Maks. ebla rapideco: %i km/h Boost (%%) Pliigite (%%) bridge is too high for its type! Ne povas konstrui tian ponton je tiel alta nivelo. Bridge is too long for this type!\n La distanco estas tro longa por konstrui tian ponton.\n Bruecke Ponto Bruecke muss an\neinfachem\nHang beginnen!\n Ponto devas ekkonstrui sur rekta vojo.\n Brueckenboden Ponto Build air depot Konstrui hangaron build choosesignals Konstrui elektsignalilon Build city market Fari novan bazaron en la plej proksima urbo. Build drain Konstrui ĉefsidejon build HQ Konstrui ĉefsidejon Build land consumer Konstrui novan centralon Build maglev depot Konstrui magnetvojan veturilejon Build monorail depot Konstrui unurelan veturilejon Build narrowgauge depot Konstrui etŝpuran veturilejon Build powerline Konstrui elektran draton Build presignals Konstrui ekssignalilon Build road depot Konstrui aŭtan veturilejon Build ship depot Konstrui ŝipfarejon Build signals Konstrui signalilon Build train depot Konstrui fervojan veturilejon Build tram depot Konstrui traman veturilejon Build truck depot Konstrui aŭtan veturilejon Building costs estimates Kosto Buildings Urba konstruaĵo Built artifical slopes Konstrui deklivojn Built random attraction Konstrui hazardan altiraĵon Bus_tab Buso Can only move from halt to halt or waypoint to waypoint. Oni povas nur movgi linplanon \nde haltejo al haltejo aŭ \nde vojpunkto al vojpunkto. Cancel Nuligi Cannot connect to offline server! Oni ne povas aliri al nekonektita servilo! Capacity Enhaveco Capacity: Enhaveco: Capacity: %.0f MW Enhaveco: %.0f MW\n Capacity: %d%s %s\n Enhaveco: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Enhaveco: %s\nEnhavo: %d (%d%%) Cars are not available yet! Aŭto ... eĉ ĉevalo ankoraŭ ne ekzistas... cars.\nstate aŭtoj\n Cash Kotantaĵo Change player Ŝanĝi ludanton Chart Grafikaĵo Chat_msg Retbabilado Choose direction Elektu direkton Choose operation executed on clicking stored/new vehicles Elekti agon farotan al alklakita veturilo chooses a random map Elekti hazardan mapon citicens Loĝantoj City attraction Urba altiraĵo City industries Bazaroj en urboj City list Urbolisto City size Urba grandeco city_road Urba vojo citybuilding builder Konstrui urbajn domojn CityLimit Urblimo cl_title Veturilolisto cl_txt_sort Klasi de: Clear block reservation Refari rezervitajn vojojn clf_chk_aircrafts Aviadiloj clf_chk_cars Aŭtoj clf_chk_indepot En veturilejo clf_chk_maglev Magnetvojaj vagonaroj clf_chk_monorail Unurelaj vagonaroj clf_chk_name_filter Filtro de namo: clf_chk_narrowgauge Etŝpuraj fervojaj vagonaroj clf_chk_noincome Malenspezo clf_chk_noline Senlino clf_chk_noroute Senvojo clf_chk_noschedule Senliniplano clf_chk_obsolete Malnovaĵoj clf_chk_ships Ŝipoj clf_chk_spezial_filter Speciala filtro: clf_chk_stucked Haltado clf_chk_trains Trajnoj clf_chk_trams Tramoj clf_chk_type_filter Tipa filtrilo: clf_chk_waren Ŝarĝaĵa Filtrilo: clf_title Veturilolista Filtrilo Climate Control Klimetaj agordoj closed fermita COLOR_CHOOSE\n Elektu du kolorojn./n Company bankrupt Kompanio bankrotis Company_msg Konkuruloj Comparing pak files ... Komparas pakseton ... Configure AI Agordi AI Configure AI setttings Agordi AI Congratulation\nScenario was complete in\n%i months %i years. Graturon!\nScenaro finiĝis komplete dum %i monatoj kaj %i jaroj. Connected stops Korespondaj haltejoj: Connected with server Eniris al selvilo. Constructed by Kreita de Constructed by %s Kreita de %s construction speed Rapideco de konstruo Construction_Btn Konstruaj kostoj Consumed Konsumaĵo contains the following doubled objects:

enhavas sekvantajn objectojn pli ol unu:

convoi %d of %d Veturilo %d de %d convoi error tooltips Montri nur veturilo kun problemo Convoi has been sent\nto the nearest depot\nof appropriate type.\n Tiu ĉi Veturilo estas sendita al la plej proksima veturilejo.\n Convoi is sold when all wagons are empty. Vendi la veturilon post kiam ĝi malpleniĝos. convoi mouseover tooltips Montri veturilan staton surmontrile convoi passed last\nmonth %i\n \ntrafiko de lasta monato: %i\n Convois Vagonaroj Convois: %d\nProfit: %s Veturiloj: %d\nEnspezo: %s Convoys Vagonaroj Copy Convoi Kopii vagonaron Copy the selected convoi and its schedule or line Kopii elektitan veturilon kaj liniplanon Cost Kosto cost for removal Kosto por malkonstrui Cost per unit Unuopa kosto Cost: %8s (%.2f$/km %.2f$/m)\n Kosto: %s (%.2f$/km %.2f$/m)\n Cost: %8s (%.2f$/km)\n Kosto: %8s (%.2f$/km)\n Costs Kostoj Create a new line based on this schedule Krej novan linon surbaze de tiu ĉi liniplano curiosity builder Altiraĵa konstruilo curlist_title Altiraĵolisto Currently playing: Nuna muziko: Customers live in: Klientoj loĝas en: deactivated in online mode Oni ne rajtas uzi en interreta ludo Deccelerate time Malrapidigi tempon December Decembro decrease underground view level Malaltigi subteran nivelon Del Stop Forigi Delete Line Foriri linion Delete the current stop Forigi la elektotan haltejon Delete the selected line (if without associated convois). Forigi elektotan linion (se neniu veturiloj havas) Delete this file. Forigi tiun ĉi dosieron Delivered Liveraĵoj Demand Mendaĵoj Demand: %.0f MW Bezono: %.0f MW\n Denkmal Monumento Departed Elirintoj Departure board Horaro Departures to\n Eliroj al\n Depots Veturilejoj Der Tunnel ist nicht frei!\n Tunelo ne estas malplena.\n Destination Celoj Destroying map ... Finas malnovan mapon ... Details Detaloj Die Bruecke ist nicht frei!\n La ponto estas ne libera!\n diesel Dizelo directmail Rektaj poŝtaĵoj Direkt erreichbare Haltestellen Haltejoj ireblaj de tie ĉi disable midi Malsoni MIDI-muzikon Display settings Vidigaĵaj agordoj Distance Distanco distributing cities Dismetas urbojn distributing factories Dismetas fabrikojn Dock doko Dock must be built on single slope! Oni devas fari dokojn sur unuobla deklivo. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Vi devas repagi vian ŝuldon %d monatojn. Durchsatz Maks. Economy Ekonomio Eigenbesitz\n Publikaĵo\n Ein %s\npasst hier nicht.\n '%s' \nne adaptiĝas ĉi tie!\n Einstellungen Agordoj Einstellungen aendern Opcioj electric Elektro Electricity Elektra povumo Electricity producer\n\n Elektrika fonto\n\n Electrics_tab Elektra motorvagono Electrify track Elektrizi fervojon enlarge map Grandigi mapon enter a value between %i and %i %i ĝis %i Enter address Enigi adreson Enter Password Ŝanĝi nomon de ludanto kaj pasvorton Error Eraro Erzeuge neue Karte.\n Bonvolu atendi ĝis novan mapon kreis.\n\n Eble bezonas kelkajn minutojn por vastaj mapoj.\n Es wird bereits\nein Fahrplan\neingegeben\n Linplano nun estas planata. \nFinu ĝin unue antaŭ re-linplani.\n Fabrikanschluss Konektitaj fabrikoj Fabrikname Fabriknomo Factories Fabrikoj factory details Fabrikaj ĉenoj factorybuilder Konstruilo de fabriko Fahrplan Linplano Fahrtziel Venonta haltejo: Fahrzeuge koennen so nicht entfernt werden Oni ne povas forigi veturilojn tiel!\n Fahrzeuge: Veturiloj: Farbe Ludantkoloro Fast forward Rapidigi tempon February Februaro Ferry_tab Pramo Fertig Foriri Filename dosiernomo: Files from: Dosiernomo: Filter: Filtrilo: Finances of %s Financoj de %s Finanzen Financoj find mismatch Kompari vian pakseton fl_title Fabrikolisto Flug_tab Aviadilo follow me Sekvi Follow the convoi on the map. Sekvi la veturilon sur la mapo. font size Grando de tiparo Forest Arbaro Found new city Fondi novan urbon Fracht Frajtaĵo Frame time: Frama tempo: Free Capacity Libera enhaveco freeplay mode Liberludo Friction: faktoro: fuel_cell Hidrogeno Full load Minimuma ŝarĝo: Fundament Tero Fussgaenger Piedirantoj Game info Ludinformo GAME PAUSED Paŭzas Game_msg Ĝeneralo Gear: Poveca proporcio: Gebaeude Konstruaĵo General Generalo Generated Faritaj Generation: %.0f MW Farkvanto: %.0f MW\n Gewicht Pezeco Gewinn Enspezo: Give the selected vehicle(s) an individual schedule Agordi apartan liniplanon al la elektita(j) veturilo(j) gl_btn_sort_catg Kategorio gl_title Fabrikaĵolisto go home Irigi al veturilejo Goods Fabrikaĵo Goods AI Frajta AI Goods list Fabrikaĵolisto Gross Profit Malneta profito Groundobj Terobjekto Grow city Kreski urbon Growth Kreskado H haltejo Hangar Hangaro Happy Feliĉuloj Haus kaufen Aĉeti domon Helligk. Vidigaĵo Help Helpo Help text not found Ankoraŭ ne estas helpteksto. hide all building Malaperigi ĉiujn domojn hide city building Malaperigi urbajn domojn hide objects under cursor Malaperigi objektojn sub montrilo hide station names Malaperigi stacidomajn nomojn hide transparent Diafanigi anstataŭ malgrandigi hide trees Malaperigi arbojn Hier warten/lagern: Atendanta fabrikaĵoj kaj pasaĝeroj: Higher transport fees, crossconnect all factories Plimultigi enspezojn, kaj nevalidigi ĵus-en-tempo. Highlite schedule Montri liniplanajn haltejojn hl_title Haltejolisto hl_txt_filter Filtrilo: hl_txt_sort Ordigitaj de: hlf_chk_airport Flughavenoj hlf_chk_anleger Doko hlf_chk_bahnhof Fervoja stacio hlf_chk_bushalt Busa haltejo hlf_chk_frachthof Ŝarĝejo hlf_chk_keine_verb Neniu korespondado hlf_chk_maglevstop Magnetvoja stacio hlf_chk_monorailstop Unurela stacio hlf_chk_name_filter Noma filtrilo: hlf_chk_narrowgaugestop Etŝpura fervoja stacio hlf_chk_overflow Saturita hlf_chk_spezial_filter Speciala filtrilo: hlf_chk_tramstop Trama haltejo hlf_chk_type_filter Tipa filtrilo: hlf_chk_waren_abgabe Fabrikaĵo: hlf_chk_waren_annahme Konsumaĵo: hlf_title Filtrilo de stacilisto Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Ne povas trovi veturejon.\nVi devas sendi mane la veturilon tien. Homeless Senhejmuloj hydrogene Hidrogeno Idle: Senfari: ignore climates Ignori klimatojn In the industry legend show only currently existing factories Montri nur fabrikaĵojn, kiuj estas uzata de fabrikoj ekzistaj nuntempe. In Transit Transitantoj Increase Industry density Multigi fabrikojn increase underground view level Altigi subteran nivelon industrial building Fabrikoj Init map ... Preparas mapon ... Input Materialo Ins Stop Enŝovi Insert stop before the current stop Enŝovi haltejon antaŭ elektita haltejo Insufficient funds! Mankas mono al vi! Intercity road len: Longeco de interurba vojo: Intro. date Enkonduka dato Intro. date: Enkonduka dato: invalid Nedifinita Invalid coordinate ★Malĝusta alteco★ isometric map Turnita mapo January Januaro join game Partopreni al interreta ludo July Julio Jump to Moviĝi al aldifinita koordinato June Junio Kann Spielstand\nnicht laden.\n Ne povas malfermi la ludon! Kann Spielstand\nnicht speichern.\n Ne povas sekurigi la ludon. Kein Besitzer\n Sen proprulo\n keine Nenio Keine Einzelfahrzeuge im Depot Neniu veturilo enmetiĝas ĉi tie. Keyboard_Help\n Klavara Helpo\n koord Koordinatoj Kreuzung Nivelpasejo labellist_title Markolisto Lade Relief Malfermi altecan mapon Laden Rekomenci ludon Land attraction Altiraĵo Land industries Fabrikaj ĉenoj: LANG_CHOOSE\n Bonvolu elekti vian ŝatatan lingvon.\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Lasta monato Last used tools Ilo uzita laste Last Year Lasta jaro: Leaving depot! Eliras el veturilejo! leer malplena Legend Enhavo Leistung Povumo Leistung: %d kW Povumo: %d kW Leitung Povuma linio length: %d Longeco: %d letzen Monat: diesen Monat: Lasta monato: Ĉi-monato: Line Linio Line Management Linia administrado Lineless convoys serving this stop Senliniaj veturiloj servantaj tiun ĉi haltejon Lines serving this stop Linioj servantaj tiun ĉi haltejon List of industries on the map Listo de fabrikoj sur la mapo LKW_tab Ŝarĝaŭtoj Load game Malfermi mapon load height data from file Uzi mapan dosieron. Load scenario Malfermi scenaron loaded Ŝarĝitaj loaded passenger/freight Ordigi pasaĝeroj/frajtoj de Loading (%i->%i%%)! Ŝarĝas (%i->%i%%) Loading addon paks ... Legas aldonitaj pakoj ... Loading map ... Legas mapon ... Loading paks ... Legas pakojn ... Loading skins ... Legas haŭtojn ... Lock game Ŝlosi ludanto Lokomotive_tab Lokomotivo m3 m³ Maglev Magnetvojo maglev vehicle Magnetvoja veturilo maglev_track Magnetvojo Maglevdepot Magnetvoja veturilejo Mail Demand %d\n Poŝtaĵa mendado %d\n Mailbox Mesaĝoj Mailbox Options Mesaĝaj opcioj Maintenance Bontenado make stop public (or join with public stop next) costs %i per tile and level Igi haltejon publika (aŭ unuigi al ĉirkaŭa publika haltejo) kostos po %i$ por kehalo kaj nivelo Make way or stop public (will join with neighbours), %i times maintainance Unuigi vojon aŭ haltejon kiel publika kun ĉirkaŭa publika stacio. Ĝi kostos %i-oblan konservan elspezon. Manual (Human) Ludanto (homo) Manufactured: Fablikita en: Map roughness Mapa malebeneco: map zoom Zomi March Marto Margin (%%) Profiteco (%%) Marker MEti signon max maks Max Boost (%%) Plifabrikeco (%%) Max income: Maksimuma enspezo: Max. speed Maks. rapideco Max. speed: Maks. rapideco: Maximum 254 stops\nin a schedule!\n Maksimume 254 haltejojn oni povas aldoni!\n maximum length of rivers Maks. longeco de riveroj Maximum tile height difference reached. La maksimuma diferenco de alteco inter du apudaj kehaloj estas 2 gradoj.\n Maxspeed Maksimuma rapideco May Majo Median Citizen per town Mezaj loĝantoj po-urbe: Meldung Mesaĝo Menge Kiomo MessageOptionsText \nNovjaro\n\nLudantaj novaĵoj\n\nUrbaj novaĵoj\n\nSenvojo\n\nNovaj celoj\n\nBabilado\n\nNovaj veturiloj\n\nPlenaj haltejoj\n\nProblemoj\n\ntrafikŝtopiĝoj\n\nScenaro min min. minimum length of rivers Min. longeco de riveroj Missing pakfiles Mankas objektoj! Modify the selected line Modifii la elektitan linion Monate alt Monata aĝo Monorail Unurelo monorail vehicle Unurela veturilo monorail_track Unurela trako Monorailboden Altigita vojo Monoraildepot Unurela veturilejo month wait time monata atenda tempo Months Monatoj Monument Monumento Monuments Monumentoj Mountain height Monta alteco: Movingobj Vivaĵo Music playing disabled/not available Muziko estas neebligita. Music volume: Muzika laŭteco: mute sound Nuligi sonon Name Nomo Narrowgauge Etŝpura fervojo Narrowgauge are not available yet! Etŝpura fervojo ankoraŭ ne estas farita! narrowgauge vehicle Etŝpura fervoja veturilo narrowgauge_track Etŝpura fervojo Narrowgaugedepot Etŝpura fervoja veturilejo Net ID: %p Reto ID: %p\n Net Wealth Tuta aktivo Net wealth near zero Via riĉeco estas preskaŭ nula. Neue Karte Nova mapo Neue Welt Krei novan mondon new convoi Nova veturilo New Line Nova linio New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Nova linio estas kreita!\nVi povas asigni la linion de selekti ĝin el la liniselektilo supre. New Vehicles Novaj veturiloj Nickname: Salutnomo: no buildings hidden Montri ĉiujn domojn no convois Neniu vagonaro No goods are loaded onto this convoi. Igi ĉi tiun veturilon nur malŝarĝi. no goods waiting Neniu fabrikaĵo atendas no load Nur malŝarĝi No Route Senvojo No stop here! Ĉi tie ne estas haltejo. No suitable ground! Ĉi tie ne estas uzebla kahelo! No terminal station here! Oni ne povas konstrui finhaltejon ĉi tie! Vojfinejo necesas. no timeline Nelaŭjare no tree Ne fari arbojn No. of Factories Fabrikoj kaj butikoj: Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Ŝargu linplanon antaŭ ekveturigi la veturilon.\n none nenio nord Nordo nordost Nordoriento nordwest Nordokcidento Not allowed to make publicly owned ways! Estas ne permesita publikigi! Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Ne povas ŝanĝi la veturilan liniplanon dum vojtrovado. Not enough fields would remain. Nesufiĉaj kampoj restos ĉirkaŭ tiu ĉi farmo. November Novembro now nun Now active as %s.\n Nun aktivas kiel %s.\n nowhere Nenie Number of rivers Nombro de riveroj Object Objekto Oct. Okt. Odometer: %s km Veturdistanco: %s km Ok Akcepti Oktober Oktobro On loan since %i month(s) Ŝuldas %i monatojn On this map, you are not\nallowed to change player!\n Ĉi tiu ludo estas ŝlosita.\nNeniu ludanto povas interŝanĝi viajn rolojn. Only city chains Konstrui urban fabrikan ĉenon Only first %d differing paks reported. There are probably more. Nur unuan %d malsamajn pakojn raportis. Verŝajne estas pli. Only full Unicode fonts Montri nur sufiĉe Unikodan tiparon Only land chains Fari fabrikan ĉenon Only one transformer per factory! Nur unu substacio por fabriko! Only show goods which are currently handled by factories Montri nur fabrikaĵojn, kiuj estas uzitaj de fabrikoj nuntempe open Malfermita Operation Kuradaj kostoj Ops Profit Servada profito Optionen Opcioj Or enter a server manually: Entajpi servilnomon: Origin Ekirejo ost Oriento Output Fabrikaĵo Ownership Proprulo Pak which may cause severe errors: Mankas sekvantaj pakoj, kiuj eble kauzos malordon de transportaj ĉenoj: Pak which may cause visual errors: La sekvantaj objektoj estas anstataŭigitaj per similaj objektoj: Pak(s) different: Malsamaj pakoj: Pak(s) missing on client: Mankaj pakoj: Pak(s) not on server: Kromaj pakoj: Pakset differences Pakseta diferenco paletten paledoj Pas_tab Pasaĝera vagono Passagiere Pasaĝeroj Passagierrate Pasaĝera nivelo Passagierziele Celoj Passenger AI Pasaĝera AI Passenger Demand %d\n Pasaĝera postulo: %d\n Passengers %d %c, %d %c, %d no route Pasaĝeroj %d %c, %d %c, %d senvoje Passengers %d %s, %d %s, %d no route Pasaĝeroj %d %s, %d %s, %d senvoje Password Pasvorto: Pause Paŭzi Pax <%i> Mail <%i> Pasaĝeroj <%i> Poŝtaĵoj <%i> PaxDest Celoj Percent Electricity Elektra eligo (%%): Planes are not available yet! Aviadilo ankoraŭ ne estas farita. Plant tree Arbumi player Ludanto player -1 Homludanto player 0 Publika servo player 1 Napiko 128 AS player 10 Ludanto 10 player 11 Ludanto 11 player 12 Ludanto 12 player 13 Ludanto 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Freight Forwarder VM player 5 H-Trans Ltd player 6 PSK & Co KG player 7 Ludanto 7 player 8 Ludanto 8 player 9 Ludanto 9 Please choose vehicles first\n Bonvolu elekti veturilojn unue!\n Post Poŝtaĵoj Postrate Poŝta nivelo Power Elektra povumo Power (MW) Elektra povumo (MW) Power: Povumo: Power: %4d kW\n Povumo: %4d kW\n Powerlines Elektrolinioj Price Prezo Problems_msg Problemoj Produced Produktitaj Produces: %.1f units/minute Produktaĵoj: %.1f /minuto Production of %s has been stopped:\n%s\n %s "%s" ne plu estas aĉetebla. Production/Boost Produkto/pliigo Produktion Produktado: Profit Profito promote to line Promocii al linio q1 Printempo q2 Somero q3 Aŭtuno q4 Vintro Query server Montri servilan informon rail car Vagono random Hazarde Random age Hazarda aĝo Random map Hazarda mapo Rathaus Urbdomo Rating Takso ratio_pax Pasaĝera Rilatumo Relevant rilataj Reliefkarte Mapeto Remove Malkonstrui remove airstrips Malkonstrui kurejojn remove channels Malkonstrui kanalojn remove interm. signals Malkonstrui interajn signalilojn remove maglev tracks Malkonstrui magnetvojojn remove monorails Malkonstrui unurelojn remove narrowgauge tracks Malkonstrui etŝpurajn fervojojn remove powerlines Malkonstrui elektroliniojn remove roads Malkonstrui vojojn remove tracks Malkonstrui fervojojn Remove wayobj %s Malkonstrui vojobjektojn %s replace other signals Anstataŭigi aliajn signalilojn replace stop Anstataŭigi haltejon request closing fermita residential house Hejmoj Restore natural slope Reigi deklivon Restwert: Vendprezo: Retire. date: Retiriĝa dato: return ticket Kopii malantaŭen Revenue Veturilaj enspezoj Revision: Revizio: road vojo road vehicle Voja veturilo Roadsign Trafiksigno Rotate map Turni mapon Rotation Direkto Routing Vojplano sack sakoj sail vento Saving map ... Sekurigas mapon ... Scenario complete: %i%% Scenara progreso: %i%% Scenario Debug Sencimigo Scenario Error Log Eraroj Scenario Goal Celo Scenario Info Informo Scenario information Scenaria informo Scenario Result Rezulto Scenario Rules Reguloj Schedule changing! Ŝanĝas liniplanon! Schienentunnel Konstrui fervojan tunelon Schiff_tab Ŝarĝoŝipoj Schiffdepot Ŝipfarejo Schleppkahn_tab Barĝoj Screenshot Foti ekranon Search: Serĉi: Seasons Sezonoj Sehenswuerdigkeit Altiraĵo Select a server to join: Elekti servilon: Select a theme for display Elekti haŭton Select display font Elekti tiparon Sell the selected vehicle(s) Vendi elektota(j)n veturilo(j)n sended Liveritaj poŝtaĵoj SEP_FRACTION , SEP_THOUSAND , SEP_THOUSAND_EXPONENT 3 September Septembro. Server did not respond! Servilo ne respondis! Serves Line: Servas la linion: Service Servo set signal spacing Agordi signalilan distancon Setting Agordoj Ship Ŝipo shops and stores Butikoj Show all Montri ĉion show all building Montri ĉiujn domojn Show also vehicles no longer in production. Montri ankaŭ veturilojn, kiuj ne plu estas produktitaj. Show also vehicles that do not match for current action. Montri ankaŭ veturilojn, kiuj ne povas esti uzataj al sekvanta pozicio. Show even servers with wrong version or pakset Montri ankaŭ servilojn, kiuj ne adaptiĝas kun via versio aŭ pakseto. show grid Montri kradon Show industry Fabrikolisto Show legend Montri enhavon Show map scale Kolorskalo Show mismatched Montri neuzeblajn servilojn Show obsolete Montri malaktualaĵojn Show offline Montri senkonektajn servilojn Show only used Montri ekzistantajn aĵojn Show schedules Montri liniplanojn Show servers that are offline Montri senkonektajn servilojn Show servers where game version or pakset does not match your client Montri servilojn, kiuj havas malsamajn versiojn aŭ paksetojn show station coverage Montri haltejoareon show station names Montri haltejonamojn show waiting bars Montri atendbastonojn show/hide block reservations Montri/malmontri blokrezervadojn Show/hide estimated arrival times Montri/malmontri taksitajn alvenajn tempojn show/hide object owner Montri/malmontri proprulon Show/hide statistics Montri/malmontri statistikojn Shows buttons on special topics. Montri butonojn pri specialaj temoj. Shows consumer/suppliers for factories Montras konsumantojn/ofertantojn por fabrikoj Shows the color code for several selections. Montri la kolornombron por kelkaj elektoj. Shows the currently selected schedule Montri la elektitan liniplanon Shrink city Malkreski urbon shuffle midis Senorde Signal Signalilo signal spacing Distanco inter signaliloj Sim: Kalkulfojo: Similar view as the main window Turni mapeton kiel ĉefmapo Size (%d MB): Grandeco (%d MB): sliced underground mode Tranĉi nivelon de la mapo slot empty Smart hide objects Malmontri ĉirkaŭ kursoro Sort by Ordigi per Sort by: Ordigi per: Sort waiting list by Ordigi atendolisto per Sound Sono Sound settings Agordoj de sono Sound volume: Laŭteco de sono: special freight Speciala frajtaĵo Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Kromenspezo laŭ rapideco procente\npostulas almenaŭa rapideco por vojo %i km/h,\nfervojo %i km/h, ŝipoj %i km/h, aviadiloj %i km/h, Speedlimit Rapidolimo Speichern Sekurigi mapon Spieler Ludanto Spieler(mz) Ludantoj Spielerliste Ludantolisto Spielstand wurde\ngeladen!\n \nSukcese malfermis ludon.\n Spielstand wurde\ngespeichert!\n La ludo estas sukcese sekurigita. Sprache Lingvo Sprachen Lingvoj Stadtinformation Urba statistiko Start Ekveturigi Start the selected vehicle(s) Ekveturigi la elektitan veturilon. Starte Spiel Komenci ludon Station tiles: Longo de haltejo: Station_msg Stacidomoj Status Halteja stato steam vaporo Step timeline one year Flugi al venonta jaro Stops Haltejoj Storage Enhavaĵo Storage capacity Enhaveco Strassendepot Veturilejo Strassentunnel Konstrui vojtunelon street car Tramo sued Sudo suedost Sudoriento suedwest Sudokcidento Summer snowline Somera neĝnivelo Supplied: %.0f %% Oferto: %.0f %% Suppliers Ofertantoj: Tage alt tage aĝas. Theme selector Elekti temon There are still vehicles\nstored in this depot!\n Estas veturiloj en la veturilejo!\n This Month Ĉi-monato: This Year Ĉi-jaro: Tile not empty. Nuligu la kahelon antaŭ konstrui deklivon. timeline Laŭjare tl_title Urbolisto To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Por altiri pli turistojn al %s, %s estis konstruita je helpo de loĝantoj. To heavy traffic\nresults in traffic jam.\n Multe da trfariko kaŭzas trafikan ŝtopiĝon.\n Toggle day/night view Ŝanĝi inter tago kaj nokto Toggle vehicle tooltips Ŝanĝi montri staton de veturiloj tonnen t Total inhabitants: Tutaj loĝantoj: Tourist attractions Altiraĵoj: Tourists Altiraĵoj Town_msg Urbo Town: %s\n Urbo: %s\n Towns Urboj track fervojo Tracks Fervojo Traffic Trafiko Train Vagonaro Trains are not available yet! Fervojo ankoraŭ ne estas farita. Tram Tramo tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. tramo %i km/h, unurelo %i km/h\nmagnetvojo %i km/h, Etŝpura fervojo %i km/h. tram_track Tramo Tramdepot Trama veturilejo Trams are not available yet! Tramo ankoraŭ ne estas farita. Transferring game ... Transdonas ludon ... Transformer only next to factory! Transforma stacio devas esti apud fabriko! Translation Traduko transparent station coverage Surmontri efektareon de haltejoj Transported Transportitaj Trees disabled! Arboj ne estas uzeblaj! TrolleyBus_tab Trolebusoj Truck Ŝarĝaŭto tt_Other Aliaj Tunnel muss an\neinfachem\nHang beginnen!\n Tuneloj devas esti sur rekta deklivo! Tunnel must start on single way! Tuneloj devas esti sur rekta vojo. Tunnelboden Tunelo underground mode Subtera vido UNDO failed! Vi ne rajtas malfari. Vi povas malfari vojon nur kiam vi ne konstruis signarilojn, staciojn, haltejojn aŭ ion ajn sur la vojo. Undo last ways construction Malfari lastan vojon Unemployed Senlaboruloj Unhappy Malfeliĉuloj units/day unuopoj/monato\n Update Line Modifii linion upgrade HQ Promocii ĉefsidejon Usage: %.0f %% Uzado: %.0f %% Usage/Output Uzado/Eligo Use beginner mode Mi estas komencanto. Use timeline start year Komenci ludon de la jaro: Vehicle %s can't find a route! Veturilo %s ne povas trovi vojon! Vehicle %s is stucked! Veturilo %s haltadas! Vehicle details Veturilaj dataloj Vehicle Name Veturila nomo Vehicle Power Povumo Verbrauch konsumo: Vergroessere die Karte\n Vastigas mapon ... Verkauf Forvendi tuj verkaufen Vendi Verkehrsteilnehmer Privataŭtoj Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Vi havas ŝuldon. Bonvolu pagi tion ĝis post %d monatoj. via tra via %s\n tra %s\n via Menge Tra (kiomo) voranstellen Meti antaŭen Waggon_tab Ŝarĝvagonoj waiting Atendas Waiting for clearance! Haltigita! Walked Piediri walking Piedirante Warnings_msg Trafiko Wasser Akvo Water Kanalo Water level Akva nivelo: water vehicle akvoveturilo way %s cannot longer used:\n Vojtipo %s ne plu povi esti uzota.\n way %s cannot longer used:\n%s\n Vojtipo %s ne plu povi esti uzota:\n%s\n way %s now available:\n Vojtipo %s estas uzebla nun.\n Way toll Paspago Ways not connected Vojo ne estas konektita. Wegpunkt Vojpunkto Weight Pezeco Weight: Pezeco: Wert Valoro west Okcidento Winter snowline Vintra neĝnivelo withdraw Retiri Withdraw All Retiri ĉion WRONGSAVE Malĝusta dosiero. Year %i has started. La jaro %i ĵus komencis. Years Jaroj Your primary color: Via unua koloro Your secondary color: Via dua koloro Zielort celejo zooming in Zomi zooming out Malzomi Zu nah am Kartenrand Vi ne povas konstrui tro proksima al la rando de mapo. #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nova monda rekordo de magnetvojo: ĉe %.1f km/h de %s! Gratulon! New world record for monorails: %.1f km/h by %s. Nova monda rekordo de unurelo: ĉe %.1f km/h de %s! Gratulon! New world record for motorcars: %.1f km/h by %s. Nova monda rekordo de aŭto: ĉe %.1f km/h de %s! Gratulon! New world record for narrowgauges: %.1f km/h by %s. Nova monda rekordo de etŝpura fervojo: ĉe %.1f km/h de %s! Gratulon! New world record for planes: %.1f km/h by %s. Nova monda rekordo de avadilo: ĉe %.1f km/h de %s! Gratulon! New world record for railways: %.1f km/h by %s. Nova monda rekordo de fervojo: ĉe %.1f km/h de %s! Gratulon! New world record for ship: %.1f km/h by %s. Nova monda rekordo de ŝipo: ĉe %.1f km/h de %s! Gratulon! #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL ejo &1_CITY_SYLL ponto &2_CITY_SYLL kampo &3_CITY_SYLL vilaĝo &4_CITY_SYLL urbo &5_CITY_SYLL monteto &6_CITY_SYLL insulo &7_CITY_SYLL fonteto &8_CITY_SYLL plato &9_CITY_SYLL monto &A_CITY_SYLL eto &B_CITY_SYLL ego &C_CITY_SYLL rivero &D_CITY_SYLL rivereto &E_CITY_SYLL fonto &F_CITY_SYLL kastelo &G_CITY_SYLL ŝtono &H_CITY_SYLL bano &I_CITY_SYLL urbato &J_CITY_SYLL valo &K_CITY_SYLL bordo &L_CITY_SYLL valeto &M_CITY_SYLL kabo &N_CITY_SYLL marĉo &O_CITY_SYLL haveno &P_CITY_SYLL golfo &Q_CITY_SYLL deklivo &R_CITY_SYLL lago &S_CITY_SYLL lageto &T_CITY_SYLL hotelo %0_CITY_SYLL Herb %1_CITY_SYLL Arbar %2_CITY_SYLL Grand %3_CITY_SYLL Verd %4_CITY_SYLL Blu %5_CITY_SYLL Ruĝ %6_CITY_SYLL Brun %7_CITY_SYLL Flav %8_CITY_SYLL Flor %9_CITY_SYLL Ĉef %A_CITY_SYLL Nov %B_CITY_SYLL Arb %C_CITY_SYLL Antaŭ %D_CITY_SYLL Vic %E_CITY_SYLL Ambaŭ %F_CITY_SYLL Bird %G_CITY_SYLL Alt %H_CITY_SYLL La %I_CITY_SYLL Vast %J_CITY_SYLL Riz %K_CITY_SYLL Anĝel %L_CITY_SYLL Post %M_CITY_SYLL Tra %N_CITY_SYLL Eben %O_CITY_SYLL Unu %P_CITY_SYLL Du %Q_CITY_SYLL Tri %R_CITY_SYLL Kvar %S_CITY_SYLL Kvin %T_CITY_SYLL Ses %U_CITY_SYLL Sep %V_CITY_SYLL Ok %W_CITY_SYLL Naŭ %X_CITY_SYLL Dek %Y_CITY_SYLL Cent %Z_CITY_SYLL Mil 1center %s %s 1extern %s ekstera %s 1suburb %s ĉirkaŭa %s 2center %s centra %s 2extern Antaŭ %s %s 2suburb %s %s simutrans-124.5/simutrans/text/es.tab000066400000000000000000001707531520675460500177110ustar00rootroot00000000000000§Espanol PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: es Espanol # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Muestra fechas como día:hh:mm del mes AMBIENT_SOUND Sonidos ambientales CASH_SOUND Sonido de facturación cl_btn_filter_disable Desactivado cl_btn_filter_enable Activado cl_btn_filter_settings Opciones cl_btn_sort_asc Ascendente cl_btn_sort_desc Descendente cl_btn_sort_id ID interno cl_btn_sort_income Ingresos cl_btn_sort_name Nombre cl_btn_sort_type Tipo clf_btn_alle Todo clf_btn_invers Invertir clf_btn_keine Nada climate area percentage Tamaño de un clima único Continue Game Continuar Partida CROSSING_SOUND Advertencias de cruce FACTORY_SOUND Sonidos de fábricas Find matching convois Encuentra convoyes coincidentes gl_btn_sort_bonus Bonificación gl_btn_sort_name Nombre gl_btn_sort_revenue Ingresos gl_btn_unsort Sin ordenar height based Clima desde altura hl_btn_filter_disable Desactivado hl_btn_filter_enable Activado hl_btn_filter_settings Opciones hl_btn_sort_asc Ascendente hl_btn_sort_desc Descendente hl_btn_sort_name Nombre hl_btn_sort_type Tipo hl_btn_sort_waiting En espera hlf_btn_alle Todo hlf_btn_invers Invertir hlf_btn_keine Nada humidities Límites de humedad Install Instalar paksets Lake Lago Lake height Máxima altura del lago Maximize height levels Maximizar niveles de altitud moisture land Aumentar sobre la tierra moisture water Aumentar sobre el agua Networks Redes Num pad keys always move map El teclado numérico siempre mueve el mapa Open Sea Mar Abierto Queueing Cola de espera rainfall Humedad Reselect closes tools Re-seleccionar una herramienta la cierra Return to menu Volver al menú Road toll Peaje Scenario Información de Escenario sea Marino Single GUI Interfaz Única Sound range: Rango de sonido: Start this as a server Comienza un servidor usando esta partida temperature borders Límites de temperatura temperature-humidity based Climas desde la meteorología TOOL_SOUND Sonidos de Herramientas TRAFFIC_SOUND Sonidos de Tráfico Transfers Transbordos #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Ártico desert Desértico mediterran Mediterráneo rocky Alpino temperate Templado tropic Tropical tundra Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed ¡No se ha podido cargar\nel script del escenario!\n A building blocks the construction Un edificio bloquea la construcción. Bridge blocked by way below or above. Puente obstruído por otra vía debajo o encima. Can't buy obsolete vehicles! ¡No se pueden comprar vehículos obsoletos! Cannot alter water No se puede alterar el agua Cannot build on a double slope! ¡Sólo se puede construir en una única pendiente o en terreno llano! Cannot built depot here! ¡Aquí no se puede construir un depósito! Cannot built this station/building\nin underground mode here. El edificio/estación no se puede construir bajo la superficie. Cannot connect to the\ncenter of a double slope! ¡No se puede conectar a la\nmitad de una pendiente doble! Cannot create generic line!\nSelect line type by\nusing filter tabs. ¡No se puede crear una línea genérica!\nSeleccione el tipo de línea\nutilizando las pestañas de filtro. Cannot create socket No se pudo crear el socket Cannot rotate this building! ¡No se puede rotar este edificio! Client closed connection during transfer El cliente cerró la conexión durante la transferencia Convoi handles exhausted! Se ha alcanzado el máximo número de convoys. Convoy already deleted! ¡El convoy ya ha sido eliminado! Could not open file No se pudo abrir el archivo Das Feld gehoert\neinem anderen Spieler\n ¡Esta parcela\nes propiedad\nde otro jugador!\n Depots cannot be built on runways! ¡Los depósitos no pueden construirse en pistas de aterrizaje! Depots must be built on flat dead-end way tiles! ¡Los depósitos deben construirse en la casilla final de una vía! Der Besitzer erlaubt das Entfernen nicht El propietario no da\npermiso para\neliminarlo.\n Diese Zusammenstellung kann nicht fahren!\n ¡Esta combinación\nno puede arrancar!\n Flugzeughalt muss auf\nRunway liegen!\n ¡La parada del avión debe\ncolocarse sobre una pista de\naterrizaje! Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n No puedes construir\nun aeropuerto aquí.\n Hier kann kein\nSignal aufge-\nstellt werden!\n No puedes colocar\n señales de tren aquí\n In order to lock the game, you have to protect the public player by password! ¡Para proteger la partida debe crear una contraseña para el jugador Servicio Público! Loading a new game will end the current server session! ¡Cargar un juego nuevo finalizará la sesión actual del servidor! Lost connection\nto server! ¡La conexión con el\n servidor se ha perdido! Lost synchronisation\nwith server. Se ha perdido la sincronización\n con el servidor. Maglevhalt muss auf\nMaglevschiene liegen!\n ¡La estación de monorriel debe estar sobre una vía de monorriel! Monorailhalt muss auf\nMonorail liegen!\n ¡Una parada de monorriel\ndebe colocarse sobre una\nvía de monorriel! Monorails are not available yet! ¡Aún no están disponibles los monorrieles! More than one possibility to build this dock found. Se encontró más de una forma de construir este muelle. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n ¡La estación debe estar en una vía estrecha! No bridges over runways! ¡No se puede construir un puente sobre una pista de aterrizaje! No curves on runways ¡Las pistas de aterrizaje no pueden tener curvas! No suitable crossing ¡No es un cruce adecuado! (¿La velocidad máxima es demasiado alta?) No suitable way on the ground! ¡Las estaciones deben construirse sobre una superfície plana! No through station here! ¡Sin estación intermedia aquí! Not enough bytes transferred No se transmitieron bytes suficientes. Not enough clearance. Entre dos vías, se necesita una diferencia de altura mínima de dos. Not enough money! ¡No tienes suficiente dinero\npara construir esto! On narrowgauge track only!\n ¡Sólo permitido en vía estrecha!\n Only public player can lock games! ¡Solo el jugador Servicio Público puede proteger la partida! Only up and down movement in the underground! ¡En el subsuelo sólo se puede subir y bajar usando las herramientas de arriba / abajo! Out of funds ¡No tienes suficiente dinero\npara esto! Post muss neben\nHaltestelle\nliegen!\n ¡Un edificio de extensión\n(oficina postal, almacén)\ndebe ubicarse junto\na una estación!\n Protocoll error (expecting game) Error en protocolo (se esperaba una partida) Schiffhalt muss im\nWasser liegen!\n ¡Sólo puedes colocar\nestaciones marítimas\nen el agua!\n Server busy Servidor ocupado. Server version too new El servidor usa una versión más reciente del programa. Ship depots must be built on water! ¡Los depósitos de barcos deben construirse sobre el agua! Show all revenue messages Mostrar todos los mensajes de ingresos. Show no revenue messages No mostrar mensajes de ingresos. Show only player's revenue Mostrar sólo ingresos del jugador. Terraforming not possible\nhere in underground view No es posible modificar el\nterreno en modo subterráneo.\n This tunnel branches. You can try Control+Click to remove. Este túnel tiene más de 2 entradas. Puede probar Ctrl+clic para eliminar este túnel. Too far away to merge stations! ¡Demasiado lejos para fusionar las paradas! Upgrade must have\na higher level La actualización debe tener\nun nivel superior. Vehicle %s cannot choose because stop too short! El vehículo %s no puede escoger porque la estación es demasiado corta! Zughalt muss auf\nSchiene liegen!\n ¡Sólo puedes colocar\nparadas de\nferrocarril en\nlas vias!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Ayuda

Índice

Acerca de Simutrans

%1$s

Cómo usar

%2$s

Cómo empezar una partida

%4$s

Cómo jugar

%5$s

Herramientas

%3$s

Otras ventanas

%6$s Keyboard Help\n

Keyboard Help

\n Atajos de Teclado\n

Ayuda de Teclado

\n

\n

Ayuda de Teclado Se abre cuando una tecla sin asignar es presionada.\n

\n

\nSe distingue entre mayúsculas y minúsculas (usa [Mayús] para teclas mayúsculas).\n

\n

\nLas teclas con funciones asognadas incluyen:\n

\n

\n[Teclas de dirección]: Mueven la cámara del juego en dirección de la flecha.
\n[Retroceso]: Cierra todas las ventanas, barras de herramientas, y textos de ayuda.
\n[Suprimir], o [Escape]: Cierra la ventana superior, barra de herramientas, o texto de ayuda.
\n[AvPág], o [>]: Acercar la cámara.
\n[RePág], o [<]: Aleja la cámara.\n[CTRL] + herramienta: construye vías rectas (incluso sobre vías más rápidas), o selección inversa durante inspección o eliminación.
\n

At least one stop is connected to the town. Al menos una parada está conectada a la ciudad. At least one tile is connected to one stop. Al menos una parada se encuentra conectada a la fábrica. Color according to transport capacity left Color según capacidad de transporte restante Color-coded terrain according to altitude. Mostrar/ocultar sombreado del contorno del terreno. Decrease water height Drenar el agua un nivel Highlight railroad tracks Resaltar vías de tren Highlite depots Resaltar depósitos Highlite electrical transmission lines Resaltar líneas eléctricas Highlite factories Resaltar industrias Highlite forests Resaltar bosques Highlite tourist attraction Resaltar atracciones turísticas Increase water height Aumentar el agua un nivel Open station/stop details Mostrar detalles de la estación Overlay city limits Superponer límites de ciudades Overlay passenger destinations when a town window is open Superponer destinos de los pasajeros cuando se abre una ventana de una ciudad Overlay schedules/network Superponer itinerarios/redes Overlay town names Superponer nombres de ciudades Please click on the map to add\nwaypoints or stops to this\nschedule. Pulse sobre el mapa\npara añadir puntos de ruta o paradas\na al itinerario. Remove double stops from schedule Eliminar paradas duplicadas del itinerario Set tile climate %s Ajustar el clima de la casilla %s Show capacity and if halt is overcrowded Mostrar capacidad y estaciones llenas Show how many convoi reach a station Mostrar cuántos convoys pasan por las estaciones Show how many people/much is waiting at halts Mostrar cuánta gente o mercancías esperan en las estaciones Show initial passenger departure Mostrar origen de pasajeros y correo. Show level of city buildings Mostrar nivel de los edificios Show mail service coverage/mail network Mostrar cobertura del servicio de correos Show passenger coverage/passenger network Mostrar cobertura de la red de pasajeros Show speedlimit of ways Mostrar los límites de velocidad de las infraestructuras Show the change of waiting at halts Mostrar variación del tiempo de espera desde el último mes Show the owenership of infrastructure Mostrar propietario de infraestructura Show transported freight/freight network Mostrar red de transporte de mercancías Show usage of network Mostrar vehículos por casilla y mes Shows a listing with all industries on the map. Mostrar todas las industrias en el mapa Sum of departure/arrivals at halts Total de salidas y llegadas a las estaciones #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n Ahora %s\nofrece servicios de autobús\nentre %s\ny la atracción turística\n%s\nen (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n Ahora %s\nofrece servicios de autobús\nentre %s\ny la fábrica\n%s\nen (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nopera ahora\n%i camiones entre\n%s en (%i,%i)\ny %s\nen (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\nha abierto un nuevo\nferrocarril entre\n%s\nen (%i,%i) y\n%s\nen (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Línea aérea por\n%s\nahora en servicio entre\n%s \ny %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n El servicio de Ferry por\n%s\nestá ahora en servicio entre\n%s \ny %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s ha abierto un servicio\n de autobús entre\n %s\ny %s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Herramientas de Edición de Mapa LISTTOOLS Listas MAGLEVTOOLS Herramientas de Maglev MONORAILTOOLS Herramientas de Monorriel NARROWGAUGETOOLS Herramientas de Trenes de Vía Estrecha RAILTOOLS Herramientas de Trenes ROADTOOLS Herramientas de Carreteras SHIPTOOLS Herramientas de Barcos SLOPETOOLS Herramientas de Terreno SPECIALTOOLS Herramientas Especiales de Construcción TRAMTOOLS Herramientas de Tranvía/Tren Ligero #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s ha construido una nueva sede Factory chain extended\nfor %s near\n%s built with\n%i factories. La economía crece:\n%s cerca de %s se añade a la cadena de producción.\n%i nueva fábrica establecida. Net wealth less than 10%% of starting capital! ¡Patrimonio neto inferior al 10%% del capital inicial! New %s now available:\n%s\n Nuevo %s ya disponible:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Crecimiento industrial: Nueva cadena industrial\npara %s\ncerca de %s\nconstruida con\n%i fábricas. New vehicle now available:\n%s\n \n Un nuevo vehículo está disponible:\n\n\n -- %s -- \n\n Now %u clients connected. Actualmente hay %u cliente(s) conectado(s). overtaking Adelantamiento Remove vehicle from map. Use with care! Eliminar vehículo del mapa. ¡Úselo con precaución! Screenshot\ngespeichert.\n Captura de pantalla\nguardada.\n Sends the convoi to the last depot it departed from! Envía el convoy al último depósito del que salió. Server preparing game ... El servidor está preparando el juego... Trees Árboles With a big festival\n%s built\na new monument.\n%i citicens rejoiced. Con un gran festival\n%s\ninauguró un nuevo monumento.\n%i ciudadanos contentos. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Filtro de Línea #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%.2f$/km %.2f$/m) (%.2f$/km %.2f$/mes) (%i)- (%i) (in depot) (en el depósito) [0] south-facing [0] orientado hacia el sur [1] east-facing [1] orientado hacia el este [2] north-facing [2] orientado hacia el norte [3] west-facing [3] orientado hacia el oeste [4] southeast corner [4] esquina sureste [5] northeast corner [5] esquina noreste [6] northwest corner [6] esquina noroeste [7] southwest corner [4] esquina suroeste [DELETE] [SUPRIMIR] [END] [FIN] [HOME] [INICIO] [SCROLLLOCK] [BLOQ DESPL] [SHIFT] [MAYÚS] /month /mes \nBauzeit bis hasta \nBauzeit von \nAparece desde \nCan't open heightfield file.\n \nImposible abrir plano\ntopográfico.\n \ndirection: \nDirecciones: \nelektrified \nelectrificado \nHeightfield has wrong image type.\n \nEl plano es un tipo de imagen incorrecto.\n \nis reserved by: \nreservado por el tren \nminimum speed: \nvelocidad mínima \nnot elektrified \nSin electrificar \nRibi (masked) \nDirecciones\m (ocultas): \nRibi (unmasked) \nDirecciones\n (visibles): \nSet phases: \nAjustar tiempo de fases\n / luego \\ y luego desplazamiento \nsingle way \nVía Única \nway1 reserved by \nVía 1 reservada por \nway2 reserved by \nVía 2 reservada por \nwith sign/signal\n \ncon la señal\n %d buildings\n %d Edificios\n %d convois %d Convoyes %d Einzelfahrzeuge im Depot %d Vehículos almacenados aquí. %i car(s), %i Coches, %i km/h (max. %ikm/h) %i km/h (máx. %ikm/h) %i years %i months old. Antigüedad: %i años y %i meses. %s at (%i,%i) now public stop. %s se ha convertido en intercambio público. %s building %s %s %s %s %s %s city %d %s %s %d %s %s factory %s %s %s - Parada %s %s has entered a depot. %s ha llegado al depósito. %s has left. %s se ha ido. %s land %d %s %s tierra %d %s %s now known as %s. %s ahora se conoce como %s. %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s derrochó su dinero en un\nnuevo ayuntamiento\ncuando se alcanzaron los\n%i habitantes. %s\nis crowded. ¡%s\n está saturada! %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nVelocidad máxima %3$i km/h\n\nVelocidad actual %2$i km/h.\n\n %s\nwas liquidated. %s cayó en bancarrota y\n ha sido liquidado.\nSe interrumpieron todas su operaciones,\n y todos sus activos han sido vendidos. %u Client(s)\n %u Cliente(s)\n %u days ago hace %u dias %u hours ago hace %u horas %u minutes ago hace %u minutos %u Player (%u locked)\n %u Jugadores (%u con contraseña)\n %u years ago hace %u años %s - %s
\n [%s]: %s
\n

Error

Error: Objetos duplicados en el directorio principal del pak

En

Warning

addons for

Advertencia: Objetos duplicados en el directorio del addon

Addons para 1 convoi 1 convoy 1 Einzelfahrzeug im Depot 1 vehículo almacenado aquí 100 km/h = %i tiles/month 100 km/h = %i casillas/mes 1LIGHT_CHOOSE Luminosidad: 1th 1. 1WORLD_CHOOSE Opciones para una Nueva Partida: 21th 21. 22th 22. 23th 23. 2LIGHT_CHOOSE Colores: 2th 2. 2WORLD_CHOOSE Número de mapa: 31th 31. 3LIGHT_CHOOSE Velocidad de desplazamiento: 3th 3. 4LIGHT_CHOOSE Invertir Desplazamiento 5LIGHT_CHOOSE Peatones en paradas 5WORLD_CHOOSE Número ciudades: 6LIGHT_CHOOSE Peatones en ciudades 6WORLD_CHOOSE Densidad de tráfico: 8WORLD_CHOOSE Ciclo Día y Noche A bridge must end on a way! ¡Un puente debe terminar en un camino! A bridge must start on a way! No eres dueño del final del camino de este puente, o el camino está obstruído.\n Abfrage Herramienta de Inspección Abnehmer Consumidor: About Acerca de About scenario Copyright Abriss Destruir/Eliminar Absenken Rebajar terreno Abspanntransformator Transformador Accelerate time Acelerar Act. load: %u MW\n Consumo eléctrico: %u MW\n Active player only Solo jugador activo Add forest Añadir bosque Add random citycar Añadir coche privado aleatorio add server Añadir servidor Add Stop Añadir Parada Add stops for backward travel Añadir paradas para el viaje de vuelta. air aéreo Aircraft Aerolínea aircraft_tab Aviones de carga airplane Avión Airport Aeropuerto AIRTOOLS Herramientas de Aeropuertos All Todo all convoi tooltips Toda la información sobre convoyes Allow city growth Permitir crecimiento urbano Allow player change Permitir cambio de jugador allowed climates:\n Climas disponibles:\n Alters a schedule. Añade/quita paradas a/de un itinerario. and arranged by con arreglos de Angenommene Waren Bienes demandados por industrias cercanas anhaengen Añadir Anhaenger_tab Remolques Anheben Elevar terreno Appends stops at the end of the schedule Añade parada al final del itinerario Apply Line Asignar línea Apr. Abr. April Abril Arbeiter aus: Los trabajadores viven en: Arrivals from\n Llegadas desde\n Arrived Llegó Assets Activos Aufloesen Desmontar Aufspanntransformator Transformador Aug. Ago. August Agosto auto Automático Autohalt muss auf\nStrasse liegen!\n ¡Las paradas de autobús/camión deben situarse en la carretera! Available Disponible Available at custom date Disponible en fecha personalizada Bahndepot Depósito de Trenes Bankrott:\n\nDu bist bankrott.\n Bancarrota:\n\nEstás en numeros rojos.\n based on Simutrans 84.22.1 basado en Simutrans 84.22.1 battery Batería Baum Árbol baum builder Plantar árboles Baustelle Sitio en construcción Bauzeit Tiempo de construcción Beenden Salir Beginner mode Modo Principiante Besonderes Gebaeude Atracción turística BF Estación bio biológico Blockstrecke ist\nbelegt\n \nEste segmento de\nvia está en uso\npor otro tren\n Boden Tierra Bonus Multiplier: %i%% Multiplicador de bonificación: %i%% Bonus Speed: %i km/h Bonificación por Velocidad: %i km/h Bonusspeed: %i km/h Velocidad máxima posible: %i km/h Boost (%%) Incremento de producción (%%) Borderless (disabled on fullscreen) Ventana sin bordes (deshabilitado en pantalla completa) bridge is too high for its type! ¡El puente es demasiado alto para su tipo! Bridge is too long for this type!\n ¡El tramo de puente es\n demasiado largo para\n este tipo de puente! Bruecke Puente Bruecke muss an\neinfachem\nHang beginnen!\n ¡El puente debe\nempezar en una\npendiente recta!\n Brueckenboden Puente Build air depot Construir Hangar build choosesignals Construir señales de selección de andén. Build city market Construir un nuevo mercado en la ciudad más cercana. Build drain Subestación transformadora build HQ Construir Sede Build land consumer Construir una nueva central eléctrica. Build maglev depot Construir depósito de maglev Build monorail depot Construir depósito de monorriel Build narrowgauge depot Construir depósito de vía estrecha Build powerline Construir tendido eléctrico Build presignals Construir señales de bloqueo de dos tramos (pre-señales) Build road depot Construir depósito de carretera Build ship depot Construir Astillero Build signals Construir señales Build train depot Construir depósito de trenes Build tram depot Construir depósito de tranvías Build truck depot Construir depósito de camiones Building costs estimates Coste de construcción estimado Buildings Edificios de la ciudad Built artifical slopes Construir pendiente artificial Built random attraction Construir atracción turística aleatoria Bus_tab Autobuses Can be overgrown Puede ser demasiado grande Can only move from halt to halt or waypoint to waypoint. Desplazamiento solo entre paradas o puntos de paso. Cancel Cancelar Cannot connect to offline server! ¡No se puede conectar a un servidor desconectado! Capacity Capacidad Capacity: Capacidad: Capacity: %.0f MW Capacidad: %.0f MW\n Capacity: %d%s %s\n Capacidad: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Capacidad: %s\n\nProporción: %d (%d%%) Cars are not available yet! ¡Todavía no están disponibles los automóviles! cars.\nstate Coches\n Cash Efectivo Category Categoría Change label style Cambiar estilo de nombres Change player Cambiar de jugador Chart Gráfica Chat_msg Chat Choose direction Elegir dirección Choose operation executed on clicking stored/new vehicles Elija la operación a ejecutar al pulsar sobre vehículos nuevos o almacenados chooses a random map Elige un mapa al azar. citicens Ciudadanos Cities can only be built on flat empty ground! ¡Las ciudades solo pueden construirse sobre terreno llano! City attraction Atracción turística de ciudad City industries Mercados en las ciudades City list Lista de ciudades City size Tamaño de la ciudad city_road Carretera urbana citybuilding builder Construir edificios urbanos CityLimit Límites de la ciudad cl_title Lista de Vehículos cl_txt_sort Ordenar por: clamp Limitar rango Cleanup schedule Eliminar entradas duplicadas Clear block reservation Mostrar/Ocultar vías reservadas clf_chk_aircrafts Aviones clf_chk_cars Autobuses/camiones clf_chk_indepot en un depósito clf_chk_maglev Maglevs clf_chk_monorail Monorrieles clf_chk_name_filter Filtrar nombres: clf_chk_narrowgauge Trenes de vía estrecha clf_chk_noincome Con pérdidas clf_chk_noline Sin línea clf_chk_noroute Sin ruta clf_chk_noschedule Sin itinerario clf_chk_obsolete Obsoleto clf_chk_ships Barcos clf_chk_spezial_filter Filtro especial: clf_chk_stucked Atascado clf_chk_trains Trenes clf_chk_trams Tranvías clf_chk_type_filter Filtrar tipos: clf_chk_waren Filtrar bienes: clf_title Filtro de la Lista de Vehículos: Climate Clima Climate Control Ajustes de Terreno Climates Climas closed Cerrado. Closes topmost line window when new line selected. Cierra la ventana de la línea superior cuando se selecciona una nueva línea. COLOR_CHOOSE\n Por favor elige\ndos colores de la\nimagen de la derecha:\n Company bankrupt Compañía en bancarrota Company_msg Competencia Comparing pak files ... Comparando archivos pak... Composed by Compuesta por Configure AI Configuración IA Configure AI setttings Propiedades Configuración IA Congratulation\nScenario was complete in\n%i months %i years. ¡Felicidades!\nCompletaste el escenario en\n%i meses y %i años. Connected stops Paradas conectadas: Connected with server Conectado al servidor Connections Conexiones Constructed by Creado por Constructed by %s Creado por %s construction speed Velocidad de construcción. Construction_Btn Costes de Construcción Consumed Consumido contains the following doubled objects:

contiene los siguientes objetos repetidos:

convoi %d of %d Convoy %d de %d convoi error tooltips Información de error en convoyes Convoi following mode Modo seguir al convoy Convoi has been sent\nto the nearest depot\nof appropriate type.\n El convoy ha sido enviado\nal depósito apropiado\nmás cercano. Convoi is sold when all wagons are empty. Se venderá el vehículo cuando esté completamente vacío. convoi mouseover tooltips Información de convoyes al pasar el ratón por encima convoi passed last\nmonth %i\n \nConvoyes durante el último mes: %i\n Convois Convoyes Convois: %d\nProfit: %s Vehículos: %d\nBeneficio: %s Convoys Convoyes Copy Convoi Copiar convoy Copy the selected convoi and its schedule or line Copia el convoy seleccionado y su itinerario o línea. Copy to clipboard Copiar al portapapeles Cost Coste cost for removal Coste de demolición Cost per unit Coste por unidad Cost: %8s (%.2f$/km %.2f$/m)\n Coste: %s (%.2f$/km %.2f$/mes)\n Cost: %8s (%.2f$/km)\n Coste: %8s (%.2f$/km)\n Costs Costes Create a new line based on this schedule Crear una nueva línea a partir de este itinerario curiosity builder Constructor de monumentos curlist_title Lista de Atracciones Turísticas CURRENCY € Currently playing: Reproduciendo actualmente: Customers live in: Los clientes viven en: Date format Formato de fecha deactivated in online mode Desactivado en modo online Dec. Dic. Deccelerate time Decelerar December Diciembre decrease underground view level Bajar nivel de vista subterránea Del Stop Eliminar Delete Line Eliminar línea Delete the current stop Eliminar esta parada Delete the selected line (if without associated convois). Eliminar línea seleccionada (si no tiene convoyes asociados) Delete this file. Eliminar este archivo. Delivered Entregados Demand Demanda Demand: %.0f MW Demanda: %.0f MW\n Denkmal Monumento Departed Salió Departs at Sale el (dd:hh:mm) Departure after %% de carga o esperar durante (dd-hh-mm) Departure board Hora de salida Departures per month Salidas al mes Departures to\n Salidas hacia\n Depot Depósito Depots Depósitos Der Tunnel ist nicht frei!\n ¡El túnel no está vacío!\n Destination Llegadas Destroying map ... Destruyendo viejo mapa... Details Detalles Developed by the Simutrans Team Desarrollado por el equipo de Simutrans Die Bruecke ist nicht frei!\n ¡El puente no está libre!\n diesel Diésel directmail Correo Directo Direkt erreichbare Haltestellen Rutas directas desde aquí disable midi Silenciar música MIDI Display bars above factory to show the status Muestra barras sobre las fábricas que indican la cantidad almacenada de producción Display settings Ajustes de pantalla Distance Distancia distributing cities Distribuyendo ciudades distributing factories Distribuyendo industrias Do not show No mostrar Dock Muelle Dock must be built on single slope! ¡El muelle debe construirse sobre una única casilla en pendiente! Downloading Descargando dp_title Lista de Depósitos Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Te quedan %d meses para saldar tu deuda. Durchsatz Máximo Economy Economia y ciudades Eigenbesitz\n Propiedad pública\n Ein %s\npasst hier nicht.\n Un '%s'\nno puede usarse aquí.\n Einstellungen Opciones Einstellungen aendern Opciones electric Eléctrico Electricity Electricidad Electricity producer\n\n Productor de electricidad\n\n Electrics_tab Eléctricos Electrify track Electrificar vía enlarge map Agrandar mapa enter a value between %i and %i Introduce un valor entre %i y %i Enter address Introducir dirección Enter Password Cambiar nombre del jugador y contraseña Erzeuge neue Karte.\n Por favor, espera,\ncreando un\nmapa nuevo ...\n\n (Puede tardar\n algunos minutos\n para mapas grandes.)\n Es wird bereits\nein Fahrplan\neingegeben\n Se está planificando\nun itinerario.\n¡Termínalo antes\nde crear un nuevo itinerario!\n Extract files Extrayendo archivos Fabrikanschluss Fábricas conectadas Fabrikname Nombre de fábrica Factories Fábricas factory details Enlaces de la fábrica factorybuilder Constructor de fábricas Fahrplan Itinerario Fahrtziel Destino: Fahrzeuge koennen so nicht entfernt werden ¡No se pueden\neliminar vehículos\nde esta forma!\n Fahrzeuge: Vehículos: Farbe Color del Jugador Fast forward Acelerar February Febrero Ferry_tab Ferris Fertig Hecho Filename Archivo: Files from: Archivos de: Filter: Filtro: Finances of %s Finanzas de %s Finanzen Finanzas find mismatch Comparar paks fl_title Lista de Fábricas Flug_tab Aviones de Pasajeros follow me Seguir Follow the convoi on the map. Sigue al convoy en el mapa. font size Tamaño de la fuente (sólo fuentes .ttf y .otf) For questions and support please visit: Para preguntas o apoyo técnico, visite: Forest Bosque Found new city Fundar una nueva ciudad Fracht Mercancía Frame time: Retardo: Free Capacity Capacidad Libre freeplay mode Modo libre Freight Carga Friction: Fricción actual: fuel_cell Pila de combustible Full load Esperar a carga mínima del Fullscreen (changed after restart) Pantalla completa (aplicado tras reiniciar) Fundament Cimientos Fussgaenger Peatón Game info Información del juego GAME PAUSED Partida En Pausa Game_msg General Gear: Marcha: Gebaeude Edificio Generated Generado Generation: %.0f MW Generación: %.0f MW\n Gewicht Peso Gewinn Ingresos: Give the selected vehicle(s) an individual schedule Editar el itinerario invididual o asignar línea a los vehículos seleccionados gl_btn_sort_catg Categoría gl_title Lista de Bienes go home Ir al Depósito Goods Bienes Goods AI IA de Bienes Goods list Lista de Bienes Gross Profit Beneficio bruto Groundobj Objeto groundobj builder Constructor de objetos del terreno Grow city Ampliar ciudad. Growth Crecimiento Urbano GUI settings GUI H Parada Happy Felices Has slope graphics Tiene gráficos de pendiente Has Snow Tiene nieve Haus kaufen Comprar casa Helligk. Pantalla Help Ayuda Help text not found Texto de ayuda no encontrado. hide all building Ocultar todos los edificios hide city building Ocultar edificios urbanos Hide labels Ocultar nombres de lugares y paradas hide objects under cursor Ocultar objetos bajo el cursor hide station names Ocultar nombres de estaciones hide transparent Transparentar en lugar de ocultar hide trees Ocultar árboles Hier warten/lagern: Bienes y pasajeros esperando: Higher transport fees, crossconnect all factories Tarifas de transporte mayores, desactivar Justo-a-tiempo Highlite schedule Destacar paradas en el itinerario hl_title Lista de Estaciones hl_txt_filter Filtro: hl_txt_sort Ordenar por: hlf_chk_airport Aeropuertos hlf_chk_anleger Muelle hlf_chk_bahnhof Estación de tren hlf_chk_bushalt Parada de Autobús hlf_chk_frachthof Muelle de carga hlf_chk_keine_verb Sin conexión hlf_chk_maglevstop Estación de Maglev hlf_chk_monorailstop Estación de Monorriel hlf_chk_name_filter Nombre filtro: hlf_chk_narrowgaugestop Estación de tren de vía estrecha hlf_chk_overflow Saturado hlf_chk_spezial_filter Filtro especial: hlf_chk_tramstop Parada de tranvía hlf_chk_type_filter Filtrar tipos: hlf_chk_waren_abgabe Bienes salientes: hlf_chk_waren_annahme Bienes entrantes: hlf_title Filtro de Lista de Estaciones Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. No se encuentra el depósito.\nDebes enviar manualmente\nel convoy al depósito. Homeless Sin techo hydrogene Hidrógeno Idle: Inactivo: ignore climates Ignorar climas In the industry legend show only currently existing factories Mostrar sólo las fábricas existentes en la leyenda de industrias In Transit En Tránsito Increase Industry density Incrementar Tejido Industrial increase underground view level Subir nivel de vista subterránea industrial building Edificios industriales Industry overlay Interfaz de Industria Infinite mouse scrolling Desplazamiento infinito con el ratón Infinite scrolling using mouse ¡El desplazamiento infinito puede fallar con ciertos dispositivos! Init map ... Iniciando mapa... Input Entrada Ins Stop Insertar Insert stop before the current stop Inserta una parada antes de la parada actual. Install graphics Instalar gráficos Install paks Los paksets se descargarán e instalarán. Insufficient funds! ¡Fondos insuficientes! Intercity road len: Long. carretera interurbana: Intro. date Fecha intro. Intro. date: Fecha intro.: invalid indefinido. Invalid coordinate Orden no válida Invert stops Invertir paradas isometric map Vista Isométrica Jan. Ene. January Enero join game Partida Online July Julio Jump to Saltar a June Junio Kann Spielstand\nnicht laden.\n ¡Imposible leer/abrir\nla partida guardada!\n Kann Spielstand\nnicht speichern.\n ¡Imposible abrir\nel achivo para\nguardar!\n Kein Besitzer\n Sin propietario\n keine Nada Keine Einzelfahrzeuge im Depot No hay vehículos almacenados. Keyboard_Help\n Ayuda del Teclado\n koord Coordenadas Kreuzung Cruce labellist_title Lista de Marcadores Lade Relief Cargar Mapa Topográfico Laden Cargar una nueva partida Land attraction Atraccion turística de interior Land industries Cadenas Industriales: LANG_CHOOSE\n Por favor escoge\ntu idioma preferido:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Último Mes Last used tools Últimas herramientas usadas Last Year Último año: Leaving depot! ¡Saliendo del Depósito! leer Vacío legacy (large heights) Legado (Doble altura) legacy (small heights) Legado (Altura única) Legend Leyenda del mapa Leistung Potencia Leistung: %d kW Potencia: %d kW Leitung Línea eléctrica length: %d Longitud: %d letzen Monat: diesen Monat: último mes: este mes: Line Línea Line Management Administración de Líneas line name mouseover tooltips Información sobre el nombre de línea al pasar el cursor por encima linear Linear Lineless convoys serving this stop Convoyes sin línea sirviendo en esta parada: Lines serving this stop Líneas sirviendo a esta parada. List of industries on the map Lista de industrias en el mapa LKW_tab Camiones Load game Cargar Partida load height data from file Cargar topografía desde archivo. Load mode: Cargar modo: Load scenario Escenario Load script tool Cargar herramienta desde script Load Scripted AI Cargar IA desde script loaded cargado loaded passenger/freight Ordenar pasajeros/mercancía por Loading (%i->%i%%)! Cargando (%i->%i%%) Loading (%i%%) departure %s! ¡Cargando (%i%%) salida %s! Loading (%i%%)! Cargando (%i%%) Loading addon paks ... Cargando objetos extra ... Loading map ... Cargando el mapa... Loading paks ... Cargando paks ... Loading skins ... Cargando temas... Loading time: Completamente cargado en: Lock Control key Mantener presionada tecla Control Lock game Impedir cambio de jugador (exige confirmación). Lokomotive_tab Locomotoras m3 m³ maglev vehicle vehículo maglev maglev_track Vía de Maglev Maglevdepot Depósito de Maglev Mail Demand %d\n Demanda de correo %d\n Mail level Nivel de correo Mailbox Buzón Mailbox Options Opciones de Buzón Maintenance Mantenimiento make stop public (or join with public stop next) costs %i per tile and level Hacer pública la parada (o unirla con una parada pública vecina) cuesta %i$ por casilla y nivel Make way or stop public (will join with neighbours), %i times maintainance Convertir a vía o parada pública (conectará con las vecinas), %i veces el mantenimiento Manual (Human) Manual (Humano) Manufactured: Fabricado: Map roughness Terreno abrupto: map view Mapa map zoom Ampliar March Marzo Margin (%%) Margen (%%) Marker Colocar Marcador max máximo Max Boost (%%) Incremento máximo(%%) Max income: Ingresos Máximos: Max. speed Velocidad máxima Max. speed: Velocidad Máxima: Max. waiting time Tiempo de Espera Máx. (dd:hh:mm) Maximum 254 stops\nin a schedule!\n ¡Máximo 254 paradas\npor itinerario!\n maximum length of rivers Longitud máxima de ríos Maximum tile height difference reached. La máxima diferencia\nde altitud entre dos\ncasillas ha sido\nalcanzada. Maxspeed Velocidad Máx. May Mayo Median Citizen per town Media de población por ciudad: Meldung Mensaje Menge Cantidad merge stop Fusionar parada MessageOptionsText \nAño Nuevo\n\nNoticias de Jugadores\n\nNoticias de Ciudades\n\nSin Ruta\n\nNuevos Destinos\n\nChat\n\nNuevos Vehículos\n\nEstaciones Saturadas\n\nProblemas\n\nAtascos\n\nEscenario min mín. minimum length of rivers Longitud mínima de rios Minimum load Carga mínima (%%) Missing pakfiles ¡Faltan objetos (pak)! Modify the selected line Modificar la línea seleccionada. Monate alt Meses de antigüedad. Monorail Monorriel monorail vehicle Vehículo de Monorriel monorail_track Vía de Monorriel Monorailboden Vía Elevada Monoraildepot Depósito de Monorriel Month Mes month wait time Tiempo de Espera (Meses) Monthly departures Horarios de salida fijos Months Meses Monument Monumento Monuments Monumentos Mountain height Altura de montañas: Move the map Mover el mapa Movingobj Objeto móvil Music playing disabled/not available Música desactivada/no disponible. Music volume: Volumen de la música: mute sound Silenciar sonido Name Nombre Narrowgauge Vía estrecha Narrowgauge are not available yet! ¡Aún no están disponibles los vehículos de vía estrecha! narrowgauge vehicle Vehículo de vía estrecha narrowgauge_track Vía estrecha Narrowgaugedepot Depósito de trenes de vía estrecha Net ID: %p ID de red: %p\n Net Wealth Valor neto Net wealth near zero Valor neto cerca de cero Neue Karte Nueva Partida Neue Welt Crear Partida Nueva new convoi Nuevo convoy New Line Nueva línea New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. ¡Nueva línea creada!\nPuedes asignar la línea\nseleccionándola desde el\ndesplegable 'línea' de arriba. New Vehicles Nuevos Vehículos Nickname: Apodo: no buildings hidden Sin edificios ocultos no convois Sin convoyes No goods are loaded onto this convoi. Este convoy no cargará bienes ninguno no goods waiting Sin bienes esperando no load Sólo descarga No player Sin jugador No Route Sin ruta No stop here! ¡Debes usar esta herramienta en paradas! No suitable ground! ¡No es el terreno adecuado! No suitable townhall available for this climate! ¡No hay un edificio de ayuntamiento apropiado para este clima! No terminal station here! ¡No se puede construir una estación\n terminal aquí! Se necesita un tramo\n final de carretera/vía. no timeline Todas las épocas no tree Sin árboles No vehicles are available for purchase. No hay vehículos disponibles para su compra. No. of Factories Fábricas y tiendas: Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n ¡Asigna una ruta\nal vehículo antes\nde darle la orden\nde partida!\n none Nada nord Norte nordost Nordeste nordwest Noroeste Not allowed to make publicly owned ways! ¡No se permite hacer vías públicas! Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! ¡No está permitido!\nLa línea ferroviaria no puede\ncambiarse ahora.\nPruebe más tarde. Not enough fields would remain. No habrá suficientes\ncampos alrededor de esta\ngranja. November Noviembre now ahora Now active as %s.\n Ahora activo como %s.\n nowhere en ningún sitio Number of rivers Número de ríos Object Objeto Odometer: %s km Kilometraje: %s km Ok Aceptar Oktober Octubre On loan since %i month(s) Numeros rojos desde %i mes(es) On mouseover Al pasar por encima el ratón on the salida(s) al mes, comenzando el (dd-hh-mm) On this map, you are not\nallowed to change player!\n ¡Esta partida está bloqueada!\n¡No se puede cambiar de jugador!\n Only city chains Construir cadena industrial urbana Only first %d differing paks reported. There are probably more. Sólo se informa de los primeros %d paks diferentes. Probablemente haya más. Only full Unicode fonts Sólo fuentes Unicode completas Only land chains Construir cadena industrial de interior Only one transformer per factory! ¡Cada fábrica solo puede tener un transformador! Only show goods which are currently handled by factories Mostrar sólo bienes procesados actualmente por fábricas open Abierto. Operation Costes de Op. Ops Profit Beneficio Operacionales Optionen Opciones Or enter a server manually: O introduce un servidor manualmente: ORDINAL_DAY_SYMBOL º Origin Orígen ost Este Output Producción Ownership Propietario Pak which may cause severe errors: Los siguientes objetos (pak) faltan, lo que puede provocar errores en las redes de transporte: Pak which may cause visual errors: Los siguientes objetos(pak) han sido reemplazados por objetos similares: Pak(s) different: Pak(s) diferentes: Pak(s) missing on client: Pak(s) no encontrados en el cliente: Pak(s) not on server: Pak(s) no encontrados en el servidor: Pakset differences Diferencias entre paksets paletten Paquetes Pas_tab Trenes de Pasajeros Passagiere Pasajeros Passagierrate Nivel de pasajeros Passagierziele Destinos de pasajeros/correo Passenger AI IA de pasajeros Passenger Demand %d\n Demanda de pasajeros: %d\n Passengers %d %c, %d %c, %d no route Pasajeros %d %c, %d %c, %d sin ruta Passengers %d %s, %d %s, %d no route Pasajeros %d %s, %d %s, %d sin ruta Password Contraseña Pause Pausa Pax <%i> Mail <%i> Pasajeros <%i> Correo <%i> Pax level Nivel de pasajeros PaxDest Destinos Percent Electricity Producción de electricidad (%% demandada): Planes are not available yet! ¡Todavía no están disponibles los aviones! Plant tree Plantar árboles player Jugador player -1 Jugador Humano player 0 Servicio Público player 1 Ricos sobre Rieles player 10 Jugador 10 player 11 Jugador 11 player 12 Jugador 12 player 13 Jugador 13 player 2 Transportes Triunfales player 3 Moviéndose Mucho S.A. player 4 Mercancía a Mogollón S.L. player 5 Rey de la Carretera S.L. player 6 Compañía Muevelotodo player 7 A Todo Tren S.A. player 8 Viajes Justo a Tiempo player 9 Jugador 9 Please choose vehicles first\n ¡Jugador, selecciona primero los vehículos!\n Post Correo Postrate Nivel de Correo Power Potencia Power (MW) Potencia (MW) Power: Potencia: Power: %4d kW\n Potencia: %4d kW\n Powerlines Tendido eléctrico Price Precio Problems_msg Problemas Produced Producido Produces: %.1f units/minute Produce: %.1f unidades por minuto Production of %s has been stopped:\n%s\n La producción de %s ha sido descontinuada:\n%s\n Production/Boost Producción/Incremento Produktion Producción: Profit Beneficio promote to line Convertir en línea q1 Primavera q2 Verano q3 Otoño q4 Invierno Query server Contactar servidor rail car Vagón random Aleatorio Random age Edad aleatoria Random map Mapa al azar Rathaus Ayuntamiento Rating Calificación ratio_pax Ratio Pasajeros Relevant Relevante Reliefkarte Mapa Remove Eliminar etiqueta remove airstrips Eliminar pistas de aterrizaje o rodaje remove channels Eliminar canales remove interm. signals Eliminar señales intermedias remove maglev tracks Eliminar vías de maglev remove monorails Eliminar monorrieles remove narrowgauge tracks Eliminar tramos de vía estrecha remove powerlines Eliminar tendido eléctrico remove roads Eliminar carreteras remove signal Eliminar señal remove tracks Eliminar vías Remove wayobj %s Eliminar objeto %s replace other signals Sustituir otras señales replace stop Reemplazar parada request closing Petición de cierre residential house Edificios residenciales Restore natural slope Restaurar pendiente natural Restwert: Valor de Reventa: Retire date Fecha de retirada Retire. date: Fecha de Retirada: return ticket Viaje de vuelta Revenue Ingresos Revenue/unit/100 tiles Ingresos por unidad y 100 casillas Revert schedule Deshacer cambios Revision: Revisión: road Carretera road vehicle Vehículo de carretera Roadsign Señal de tráfico Rotate Building Rotar edificio Rotate map Rotar mapa Rotation Rotación Routing Calculando ruta Running cost Coste operacional sack Sacos sail Viento Save Guardar Saving map ... Guardando mapa... Scenario complete: %i%% Escenario completado: %i%% Scenario Debug Depurar Scenario Error Log Errores de script Scenario Goal Objetivo Scenario Info Información Scenario information Detalles del escenario Scenario Result Progreso Scenario Rules Normas Scenario_ Escenario Schedule changing! ¡Cambio de itinerario! Schienentunnel Construir túnel ferroviario Schiff_tab Barcos Schiffdepot Astillero Schleppkahn_tab Barcazas Screen scale: Escala de pantalla: Screenshot Captura de pantalla Scroll threshold Umbral de desplazamiento Search: Buscar: Seasons Estaciones Sehenswuerdigkeit Atracción turística Select a server to join: Selecciona un servidor para unirte: Select a theme for display Selecciona un tema para mostrar Select display font Selecciona una fuente de texto Select one or more graphics to install (Ctrl+click): Selecciona uno o más paksets para instalar (Ctrl+click): Select soundfont Seleccionar soundfont Sell the selected vehicle(s) Vender los vehículos seleccionados Selling of the program is forbidden. Está prohibida la venta de este programa. sended Correo enviado SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 Sept. Sep. September Septiembre Served by Servido por Served by me Servidas por mi empresa Server did not respond! ¡El servidor no respondió! Serves Line: Sirve en línea: Service Servicio set signal spacing Establecer espacio entre señales Setting Configuración Ship Barco shops and stores Tiendas y oficinas Show all Mostrar todo show all building Mostrar todos los edificios Show also vehicles no longer in production. Mostrar también vehículos obsoletos. Show also vehicles that do not match for current action. Mostrar también vehículos que no pueden ser empleados en la acción seleccionada. Show always Mostrar siempre Show climates Mostrar climas Show contour Mostrar alturas Show even servers with wrong version or pakset Mostrar también servidores con versiones o paksets incompatibles. Show finances for transport type Mostrar finanzas del tipo de transporte Show future Mostrar futuros show grid Mostrar cuadrícula Show industry Lista de Industrias Show legend Selecciones Show map scale Escala Show mismatched Mostrar incompatibles Show networks Redes Show obsolete Mostrar obsoletos Show offline Mostrar offline Show only used Mostrar sólo los utilizados Show outline Mostrar infraestructura Show schedules Mostrar itinerarios Show servers that are offline Mostrar servidores offline Show servers where game version or pakset does not match your client Mostrar servidores con versiones o paksets que no concuerdan con los tuyos show station coverage Mostrar cobertura de estaciones show station names Mostrar nombres de estaciones show waiting bars Mostrar barras de espera show/hide block reservations Mostrar/ocultar reservas de tramos Show/hide estimated arrival times Mostrar/ocultar horarios estimados de llegada show/hide object owner Mostrar/ocultar propietario Show/hide statistics Mostrar/ocultar Estadísticas Shows buttons about networks overlay. Muestra los botones de configuración de la vista de redes. Shows buttons on network overlay. Mostrar opción para superponer una red de conexión. Shows buttons on special topics. Mostrar los botones de la leyenda del mapa Shows consumer/suppliers for factories Mostrar consumidores/proveedores de las fábricas Shows the color code for several selections. Representa en el mapa la selección usando una escala de colores Shows the currently selected schedule Mostrar el itinerario seleccionado Shrink city Reducir ciudad shuffle midis Música Aleatoria Signal Señal signal spacing Distancia entre señales Sim: Ciclos: Similar view as the main window Vista similar a la principal del juego Single toolbar only Barra de herramientas única Size (%d MB): Tamaño (%d MB): Size (area) Tamaño (área) sliced underground mode Vista subterránea por capas slot empty Casilla vacía Smart hide objects Ocultación inteligente de objetos Sort by Ordenar por Sort by: Ordenar por: Sort waiting list by Ordenar lista de espera por Sound Sonido Sound settings Ajustes de sonido Sound volume: Volumen del sonido: Soundfonts are located in the music directory. Las fuentes de sonido se guardan en el directorio 'music'. special freight Mercancía especial Speed Bonus Bonificación por Velocidad Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. La bonificación por velocidad en porcentaje\nrequiere velocidad mínima de carretera %i km/h,\n de tren %i km/h, de barcos %i km/h, de aviones %i km/h, Speedlimit Límite de velocidad Speichern Guardar Partida Spieler Jugador Spieler(mz) Jugadores Spielerliste Lista de Jugadores Spielstand wurde\ngeladen!\n \n¡Partida cargada con éxito!\n Spielstand wurde\ngespeichert!\n \n¡Partida guardada con éxito!\n Sprache Idioma Sprachen Idiomas Stadtinformation Estadísticas de la Ciudad Start Arrancar Start the selected vehicle(s) Arrancar los vehículos seleccionados. Starte Spiel Comenzar Partida station labels Estaciones Station tiles: Casillas de Estación: Station_msg Estaciones Status Estado de las paradas steam Vapor Step timeline one year Avanzar un año en la cronología. Stops Paradas Storage Almacén Storage capacity Capacidad de almacenamiento Strassendepot Depósito de carretera Strassentunnel Construir túnel de carretera street car Tranvía sued Sur suedost Sureste suedwest Suroeste Summer snowline Cota estival Supplied: %.0f %% Suministrado: %.0f %% Suppliers Proveedores Tage alt días de antigüedad. The following graphics are unmaintained: Los siguientes paksets están abandonados: The gradient does not fit a tunnel ¡La pendiente no tiene la altura adecuada para un túnel! Theme selector Selector de temas There are still vehicles\nstored in this depot!\n ¡Hay vehículos\nen este depósito!\n This Month Este mes: This Year Este año: Tile not empty. Limpia la casilla antes de usar\n una Herramienta de Pendiente Artificial. timeline Usar cronología tl_title Lista de Ciudades To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. El tráfico en %1$s está aumentando:\n%3$i contribuyentes hicieron posible\nla construcción de %2$s. To heavy traffic\nresults in traffic jam.\n El exceso de tráfico pesado\nprovoca un atasco.\n Toggle day/night view Alternar vista día/noche Toggle vehicle tooltips Alternar información sobre vehículos tonnen t Toolbar position: Posición de la barra: Total inhabitants: Población: Tourist attractions Atracciones turísticas: Tourists Atracciones turísticas Town_msg Nuevos Destinos Town: %s\n Ciudad de %s\n Towns Ciudades track Vía Tracks Vías Traffic Tráfico traffic settings Tráfico Train Tren Trains are not available yet! ¡Todavía no están disponibles los trenes! Tram Tranvía tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. tranvía %i km/h, monoriel %i km/h\nmaglev %i km/h, vía estrecha %i km/h. tram_track Vía de tranvía Tramdepot Depósito de tranvías Trams are not available yet! ¡Todavía no están disponibles los tranvías! Transferring game ... Transfiriendo partida... Transformer only next to factory! ¡Los transformadores deben estar junto a una fábrica! Translation Traducción transparencies Transparencia transparent background Fondo transparente transparent station coverage Cobertura de estación transparente Transported Viajes Trees disabled! ¡Árboles deshabilitados! TrolleyBus_tab Trolebuses Truck Camión tt_Other Otro Tunnel muss an\neinfachem\nHang beginnen!\n ¡Los túneles deben\ncomenzar en una\npendiente recta!\n Tunnel must start on single way! ¡Los túneles deben comenzar en una sola carretera! Tunnelboden Túnel under the Artistic License bajo la Artistic License underground mode Vista subterránea UNDO failed! No es posible deshacer.\nSólo puede deshacerse la\nconstrucción de una ruta si\nno se construyeron sobre la\ncarretera señales, estaciones,\nparadas ni ninguna otra cosa. Undo last ways construction Deshacer última construcción de vías Unemployed Desempleados Unhappy Descontentos units/day /mes, Unloading (%i%%)! Descargando (%i%%) Update Line Editar línea upgrade HQ Mejorar Sede Usage: %.0f %% Uso: %.0f %% Usage/Output Consumo/Producción Use beginner mode Usar modo libre Use timeline start year Usar cronología desde el año: Vehicle %s can't find a route! ¡El vehículo %s\n no puede encontrar una ruta! Vehicle %s is stucked! ¡El vehículo %s está atascado! Vehicle count: Número de vehículos: Vehicle details Detalles del Vehículo Vehicle Name Nombre del Vehículo Vehicle Power Potencia del Vehículo vehicles stored vehículos almacenados Verbrauch Consumo: Vergroessere die Karte\n Agrandar mapa.\n Verkauf ¡Eliminar ahora! verkaufen Vender Verkehrsteilnehmer Coches Ciudad Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n ¡Te quedan %d meses\npara pagar tu deuda!\n vh_title Lista de todos los vehículos via por (detalle) via %s\n por %s\n via Menge por (cantidad) voranstellen Poner al frente Waggon_tab Vagones waiting esperando Waiting for clearance! ¡A la espera de vía libre! Walked Caminados walking Caminando Warnings_msg Tráfico Wasser Agua Water Canal Water level Nivel del agua: water vehicle Vehículo acuático way %s cannot longer used:\n El tipo de vía %s ya no se puede usar.\n way %s cannot longer used:\n%s\n el tipo de vía %s ya no se puede usar:\n way %s now available:\n Vía %s disponible.\n Way toll Peaje Ways not connected Las vías no están conectadas waytype Tipo de vía Wegpunkt Punto de paso Weight Peso Weight: Peso: Weight/unit Peso/unidad Welcome to Simutrans Bienvenido a Simutrans Welcome, %s! ¡Te damos la bienvenida, %s! Wert Valor west Oeste Wind direction Dirección del viento Winter snowline Cota invernal withdraw Retirar Withdraw All Retirar todo. WRONGSAVE \nPartida guardada\nincompatible. Imposible leer\nel archivo.\n Year Año Year %i has started. Ha comenzado el año %i. Years Años Your primary color: Tu color primario: Your secondary color: Tu color secundario: Zielort Destino zooming in Acercar cámara zooming out Alejar cámara Zu nah am Kartenrand No puedes construir\nedificios tan cerca\ndel borde del mapa.\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Levitando sobre imanes a \n%.1f km/h por %s:\n ¡Nuevo récord mundial! New world record for monorails: %.1f km/h by %s. Un riel construido para la velocidad: \nExactamente %.1f km/h \nalcanzado por %s.\n,¡se convierte en el nuevo récord mundial! New world record for motorcars: %.1f km/h by %s. Sólo quedan marcas de derrape\n de tanto correr a %.1f km/h\n alcanzado por %s.\n,¡ha logrado un nuevo récord mundial! New world record for narrowgauges: %.1f km/h by %s. Nuevo récord mundial para trenes de vía estrecha: %.1f km/h por %s. New world record for planes: %.1f km/h by %s. Disminuyendo distancias otra vez:\n%2$s voló a %1$.1f km/h.\n¡Nuevo récord mundial! New world record for railways: %.1f km/h by %s. Tronando sobre las vías\n a %.1f km/h.\n%s consigue un nuevo récord mundial para trenes.\n New world record for ship: %.1f km/h by %s. ¡A toda máquina!\n%.1f km/h de velocidad récord\n alcanzada por %s. #________________________________script_ai_text_________________________________ #________________________________script_ai_text_________________________________ %s build additional convoy to line: %s %s construye convoy adicional en la línea: %s %s build rail line from %s (%s) to %s (%s) %s construye una vía de tren desde %s (%s) a %s (%s) %s build road line from %s (%s) to %s (%s) %s construye una línea de carretera desde %s (%s) a %s (%s) %s build ship line from %s (%s) to %s (%s) %s construye una línea de barcos desde %s (%s) a %s (%s) %s extends the route from %s (%s) to %s (%s) %s extiende la ruta desde %s (%s) a %s (%s) %s optimize way line from %s (%s) to %s (%s) %s optimizar la línea desde %s (%s) a %s (%s) %s removes convoys from line: %s %s elimina convoys de la línea: %s vehicles of the line %s were retired vehículos de la línea %s fueron retirados #_________________________________tooltip_text__________________________________ #_________________________________tooltip_text__________________________________ Add convois with similar schedule to this line. Añade convoyes con itinerarios similares a esta línea. Enter intervall in days, hours, minutes Introduce el intervalo en días, horas, y minutos. Mirrors order of stops Invierte el orden de las paradas. Revert to original schedule Deshace los cambios al itinerario. Tutorial not available for this pakset. Please try loading Pak128. Tutorial no disponible para este pakset. Pruebe a cargar otros como el Pak128, Pak192,... #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL del Mar &1_CITY_SYLL del Monte &2_CITY_SYLL de Abajo &3_CITY_SYLL de Arriba &4_CITY_SYLL de la Vereda &5_CITY_SYLL de la Frontera &6_CITY_SYLL de la Cañada &7_CITY_SYLL del Valle &8_CITY_SYLL del Río &9_CITY_SYLL de la Reina &A_CITY_SYLL del Rey &B_CITY_SYLL de la Torre &C_CITY_SYLL de la Cruz &D_CITY_SYLL de la Campiña &F_CITY_SYLL hogar &I_CITY_SYLL hielo %0_CITY_SYLL Peligros %1_CITY_SYLL Sordillos %2_CITY_SYLL Peleas %3_CITY_SYLL Villaviciosa %4_CITY_SYLL Cenicero %5_CITY_SYLL El Ciego %6_CITY_SYLL La Pera %7_CITY_SYLL Alcantarilla %8_CITY_SYLL Meadero %9_CITY_SYLL Codos %A_CITY_SYLL Montón %B_CITY_SYLL Brincones %C_CITY_SYLL Calaveras %D_CITY_SYLL Malcocinado %E_CITY_SYLL Consuegra %F_CITY_SYLL Tembleque %G_CITY_SYLL Albaricoques %H_CITY_SYLL Melón %I_CITY_SYLL Matagorda %J_CITY_SYLL El Golfo %K_CITY_SYLL Moscardón %L_CITY_SYLL El Payo %M_CITY_SYLL Cotillas 0center %s centro %s 0extern %s aldea %s 0suburb %s suburbios %s 1center %s %s 1extern %s %s Ensanche 1suburb %s %s %s 2center %s %s Central 2extern %s %s Apeadero 2suburb %s Verdes Praderas %s %s 3center %s %s Principal 3extern %s %s Afueras 3suburb %s villa %s %s 4center %s %s Rambla 4extern %s %s Bosque 4suburb %s Colonia %s %s 5center %s %s Alameda 5extern %s Pradera %s 5suburb Parque Tecnológico %s %s %s 6center %s Conexión %s 6extern %s Parque Industrial %s 6suburb %s afueras %s %s 7center %s ciudad %s 7extern %s llanuras %s 7suburb %s parque %s 8center %s Nodo %s 8extern %s %s margen %s 8suburb %s satélite %s 9center %s Circunvalación %s 9extern %s bypass %s 9suburb %s medio %s Acenter %s central %s Aextern %s barrio %s Asuburb %s, zona suburbana %s Bcenter %s centro de %s Bextern ¡No existe! Bsuburb ¡No existe! Ccenter %s centro %s %s Cextern ¡No existe! Csuburb %s afueras %s Dcenter ¡No existe! Dextern ¡No existe! Dsuburb ¡No existe! simutrans-124.5/simutrans/text/es/000077500000000000000000000000001520675460500172045ustar00rootroot00000000000000simutrans-124.5/simutrans/text/es/airtools.txt000066400000000000000000000134251520675460500216060ustar00rootroot00000000000000Ayuda sobre Herramientas de Aeropuertos

Herramientas de Aeropuertos

Herramientas de Aeropuertos contiene herramientas para construir una red de transporte aéreo. Las herramientas pueden construir o eliminar: Pistas de Rodaje y Pistas de Aterrizaje para aviones, Paradas Aéreas (usadas para dejar o recoger mercancías y pasajeros), hangares de aviones (para comprar y gestionar los aviones) y algunos edificios más de aeropuertos. Si tienes activa la cronología, entonces a medida que pase el tiempo en Simutrans aparecerán más opciones.

Pulsa el icono del avión en la parte superior de la pantalla para abrir el menú.
Posiciona el ratón sobre cada icono de la barra (tras abrir el menú) para ver el nombre y otros datos: coste de construcción, coste de mantenimiento entre paréntesis, y límite de velocidad máxima.

{Consejos:Una forma sencilla de construir un aeropuerto simple es:
1) Construir y conectar Pistas de Rodaje y Pistas de Aterrizaje;
2) Construir Paradas Aéreas al final de las Pistas de Rodaje;
3) Construir las extensiones del aeropuerto (por ejemplo, Torres o Terminales).}

Las herramientas pueden incluir, de izquierda a derecha:

Pista de Rodaje: Herramienta que construye los pasillos por donde se moverán los aviones para ir de las Paradas Aéreas a las Pistas de Aterrizaje. Las Pistas de Rodaje son construidas al nivel del suelo en el juego.
IMPORTANTE: Si las Pistas de Rodaje están conectadas al final de las pistas de aterrizaje, puede que los aviones no puedan despegar ni aterrizar.
Para construir una Pista de Rodaje: pulsa en la herramienta para seleccionar (el cursor cambiará a pista de rodaje), después pulsa en el terreno para marcar el punto de inicio (muestra una excavadora y las coordenadas del punto en la parte inferior derecha de la pantalla), finamente pulsa en el terreno o en la Pista de Aterrizaje para marcar el punto de finalización.
{Consejo: Usa Eliminar para quitar partes individuales de una Pista de Rodaje. La opción Deshacer[z] no reembolsa los costes de construcción.}

Pista de Aterrizaje: Herramienta para construir pistas, usadas por los aviones para despegar y aterrizar. Las pistas de aterrizaje son construidas al nivel del suelo, y se pueden cruzarse entre sí.
Para construir una pista de aterrizaje: pulsa en la herramienta para seleccionar(el cursor cambiará a vía), después pulsa en el terreno para indicar el punto de inicio (muestra un excavadora), finalmente pulsa en el terreno para indicar el punto de finalización.
{Consejo: Deja el final de las pistas de aterrizaje sin conexión, porque sino los aviones no podrán despegar ni aterrizar. Usa Eliminar para quitar partes individuales de las pistas de aterrizaje. La opción Deshacer [z] no reembolsa los costes de construcción.}

Eliminar pistas: Herramienta que elimina un segmento de Pista de Rodaje o de una Pista de Aterrizaje, cuando no hay aviones presentes, entre dos puntos. El uso de esta herramienta supone un coste de construcción.
Para eliminar una pista: pulsa en la herramienta (el cursor cambiará a un X roja); después pulsa en la pista que quieres eliminar (en el tramo a borrar se muestra una X roja); finalmente pulsa en otro punto de la pista, para eliminar un tramo desde el punto inicial. {Consejo: Deja el final de las Pistas de Aterrizaje sin conexión, ya que sino los aviones no podrán despegar ni aterrizar. Usa Eliminar para quitar partes individuales de las pistas.}

Hangar: Herramienta para crear hangares para comprar y gestionar aviones. Los hangares tienen costes de mantenimiento y deben ser construidos en el tramo final de una Pista de Rodaje.
Para construir un hangar: pulsa en la herramienta (el cursor cambiará a un depósito), después pulsa en el tramo final de una Pista de Rodaje.
{Consejo: Elimina hangares con la herramienta Eliminar.}

Paradas Aéreas & puertas de embarque: Herramientas para construir Paradas, para que los aviones embarquen o dejen pasajeros o mercancías.
Una Parada Aérea, cuando no es construida adyacente a una Parada ya existente, creará una nueva Parada.
Las Paradas Aéreas deben ser construidas sobre el final de un tramo de Pista de Rodaje; y tienen un coste de mantenimiento y áreas de cobertura para pasajeros, correo y mercancías. Un avión solo aterrizará en un aeropuerto si hay una Parada Aérea libre y disponible.
Para construir una Parada Aérea: pulsa en la herramienta elegida,y después pulsa sobre el tramo final de una Pista de Rodaje.
{Consejos: Elimina Paradas Aéreas con la herramienta Eliminar. Pulsa [v] para mostrar/ocultar el área de cobertura para pasajeros y mercancías.}

Edificios de Aeropuertos: Herramientas para construir extensiones para Paradas las cuales podrían incrementar los costes de mantenimiento, así como la capacidad y el área de cobertura de pasajeros y mercancías. En la esquina de algunas herramientas, un icono (usado en Lista de Estaciones e Información sobre la Parada) muestra qué elementos podrá aceptar la Parada si se añade dicha extensión.
Para construir una extensión: Pulsa en la herramienta de extensión seleccionada (el cursor cambiará a extensión), después pulsa en el lugar elegido, cerca de una parada existente. La nueva extensión será considerada parte de la parada.
{Consejo: Elimina extensiones con la herramienta Eliminar. Pulsa [v] para mostrar/ocultar el área de cobertura de pasajeros y mercancías.}

simutrans-124.5/simutrans/text/es/baum_build.txt000066400000000000000000000050001520675460500220430ustar00rootroot00000000000000Ayuda sobre Plantar Árboles

Plantar Árboles

La ventana de Plantar Árboles te permite plantar árboles individuales. Es una opción de personalización del mapa, disponible si cambias al jugador del servicio público, y abres las Herramientas de Edición de Mapa.
CONSEJO: También puede estar disponible el botón Añadir Bosque en esta barra, para añadir bosques de una vez.

La ventana se divide en cuatro secciones:
1) Parte superior izquierda: Opciones de filtrado y ordenación.
2) Parte inferior izquierda: Una lista de selección con los árboles disponibles.
3) Parte superior derecha: Opciones de colocación.
4) Parte inferior derecha: Información e imagen del árbol seleccionado.

1 - Opciones de filtrado y ordenación

· Clima: Filtra por los climas permitidos.
· Ordenar por: Ordena la lista según alguno de los siguientes parámetros:
·· Traducción: Muestra y ordena por el nombre en el idioma dado tal como se define en el pakset. Si no hay traducción disponible, se utilizará el nombre del objeto.
·· Object: Muestra y ordena por el nombre del objeto interno utilizado por Simutrans.

2 - Lista de selección

En la lista de selección se muestran todos los árboles que están disponibles con las opciones especificadas. Pulsa en un árbol de la lista para ver más información sobre ese árbol en particular. Para plantar el árbol, pulsa en la ventana del mundo del juego en la ubicación deseada después de seleccionarlo en la lista.

3 - Opciones de colocación

· Ignorar climas: Esta opción desactiva las restricciones climáticas, permitiendo plantar el árbol en climas no permitidos.
· Edad aleatoria: Si está activo, la edad del árbol a plantar será aleatoria. Normalmente, se plantan a la edad más joven, crecerán hasta la madurez y luego morirán en el transcurso de muchos años.

4 - Información

La información sobre árboles contiene:
· Nombre: Nombre del árbol.
· Climas permitidos: Climas en los cuales se permite plantar el árbol (si la opción "Ignorar climas" no está activa).
· Estaciones: Número de estaciones.
· Autor: El nombre del autor que pintó el objeto (si está disponible).
· Imagen: Imagen del árbol.

simutrans-124.5/simutrans/text/es/bridges.txt000066400000000000000000000064671520675460500214010ustar00rootroot00000000000000Ayuda: construyendo puentes

Construyendo puentes

Se puede utilizar un puente para salvar un valle u otro obstáculo.

Cada puente tiene una longitud máxima posible especificada. Para salvar tramos más largos solo tendrás que buscar otro en el menú, o utilizar las islas y dividir la distancia total en varias partes. Se pueden construir puentes incluso a través de valles profundos con terreno irregular, la restricción solo se aplica en los campos iniciales y finales. El puente debe comenzar y terminar en una superficie horizontal, en una pendiente inclinada en la dirección del puente o en una cresta artificial. El inicio y el final del puente no tienen por qué estar a la misma altura, puede haber una diferencia de un nivel entre ellos. Los puentes sólo se pueden construir en la dirección de los límites de la casilla, es decir, (todavía) no se pueden construir en diagonal. Sin embargo, existe un tipo de vía férrea que se asemeja a un puente porque está sobre el suelo. Esta pista también se puede construir en diagonal.

Todos los puentes, ya sean de ferrocarril, de carretera, de transporte o de cables de alta tensión, se construyen de la misma manera. El puente de carretera tiene una excepción: también se le pueden añadir vías de tranvía.

Construyendo un puente

El siguiente procedimiento se puede aplicar a todo tipo de puentes, si aquí está escrito un camino, puedes sustituirlo por un ferrocarril u otro tipo de transporte que tenga puentes:
(i) Construya un camino hacia ambos extremos previstos del puente, siendo el camino y el primer y último espacio del futuro puente.
(ii) Haga clic en el símbolo del puente. Elige el que más te convenga según sus parámetros.
(iii) Haga clic en el campo con el origen del puente deseado y mantenga presionado el botón.
(iv) Mueva el cursor del mouse sobre el cuadrado donde debe terminar el puente y suelte el botón. El puente está terminado.

También existe un procedimiento alternativo. Sin embargo, debes tener ambos extremos del camino debidamente preparados con anticipación, de lo contrario puedes terminar gastando dinero en un puente largo que no deseas. En lugar de los pasos (iii) y (iv), haga clic en el comienzo del puente deseado y suelte el botón. El puente se extenderá hasta el tramo de carretera del otro lado.

Un error común al construir un puente es que no está conectado a la carretera (o al ferrocarril) en los extremos. Arrastra la carretera (ferrocarril) también a las casillas de inicio y fin del puente; de lo contrario, los coches (trenes) no pasarán por encima.

Consejo: A veces no vale la pena construir un puente. Los puentes generalmente tienen costes de mantenimiento más elevados que las carreteras normales (vías, líneas eléctricas, ..). Comprueba si merece la pena rellenar la depresión para detectar pequeñas irregularidades del terreno.

Eliminación del puente

Haga clic en el botón Eliminar y luego haga clic en el inicio o el final de la plataforma del puente.

->Ayuda para la construcción de carreteras
->Ayuda para la construcción de ferrocarriles

->Acerca de la traducción

simutrans-124.5/simutrans/text/es/citybuilding_build.txt000066400000000000000000000107301520675460500236130ustar00rootroot00000000000000Construir edificios urbanos

Construir edificios urbanos

La ventana de Construir edificios urbanos te permite colocar edificios urbanos. Es una opción de personalización del mapa, disponible si cambias al jugador del servicio público, y abres las Herramientas de Edición de Mapa.

La ventana se divide en cuatro secciones:
1) Parte superior izquierda: Opciones de filtrado y ordenación.
2) Parte inferior izquierda: Una lista de selección con los edificios disponibles.
3) Parte superior derecha: Opciones de colocación.
4) Parte inferior derecha: Información e imagen del edificio seleccionado.

1 - Opciones de filtrado y ordenación

· Usar cronología: La lista de selección solo contendrá los edificios que están disponibles en el año actual de Simutrans. Esto se excluye mutuamente con la opción "Disponible en fecha personalizada".
· Disponible en fecha personalizada: La lista de selección solo contendrá los edificios que estén disponibles en la fecha especificada. Esto se excluye mutuamente con la opción "Usar cronología".
· Mostrar obsoletos: Expande la lista de selección para incluir también edificios obsoletos en el año actual de Simutrans.
· Clima: Filtra por los climas permitidos.
· Ordenar por: Ordena la lista según alguno de los siguientes parámetros:
·· Traducción: Muestra y ordena por el nombre en el idioma dado tal como se define en el pakset. Si no hay traducción disponible, se utilizará el nombre del objeto.
·· Object: Muestra y ordena por el nombre del objeto interno utilizado por Simutrans.
·· Nivel de pasajeros: Ordenar por cantidad de pasajeros generados o demandados.
·· Nivel de correo: Ordenar por cantidad de correo generado o demandado.
·· Fecha intro.: Ordenar por fecha de introducción (cuando el edificio comienza a aparecer).
·· Fecha de retirada: Ordenar por fecha de retirada (cuando el edificio deja de aparecer).
·· Tamaño (área): Ordenar por tamaño de edificio en casillas (Nota: algunos paksets pueden tener sólo edificios de una casilla).
· Edificios residenciales: Muestra/oculta los edificios residenciales.
· Tiendas y oficinas: Muestra/oculta los edificios comerciales.
· Edificios industriales: Muestra/oculta los edificios industriales.

2 - Lista de selección

En la lista de selección se muestran todos los edificios que están disponibles con las opciones especificadas. Pulsa en un edificio de la lista para ver más información sobre ese edificio en particular. Para colocar el edificio, pulsa en la ventana del mundo del juego en la ubicación deseada después de seleccionarlo en la lista. El color del texto tiene el siguiente significado:
· Texto azul indica que es un edificio residencial.
· Texto verde indica que es un edificio comercial.
· Texto negro indica que es un edificio industrial.

3 - Opciones de colocación

· Ignorar climas: Esta opción desactiva las restricciones climáticas, permitiendo colocar el edificio en climas no permitidos.
· Rotación: Si el edificio elegido se puede mostrar desde diferentes ángulos, entonces es posible elegir una vista específica del mismo aquí. La imagen cambiará para reflejar esto. Si esta opción se establece en Aleatorio, se utilizará una vista aleatoria del edificio.

4 - Información

La información sobre árboles contiene:
· Tipo: Tipo de edificio (Residencial, Industrial, o Tiendas y oficinas).
· Nombre y descripción: Mostrará solo la descripción si no tiene nombre. Mostrará el nombre del objeto solo si no tiene nombre ni descripción.
· Climas permitidos: Climas en los cuales se permite plantar el árbol (si la opción "Ignorar climas" no está activa). · Nivel de pasajeros: Métrica de cantidad de pasajeros generada o demandada.
· Nivel de correo: Métrica de cantidad de correo generado o demandado.
· Fecha de introducción: Fecha desde que aparece el edificio.
· Autor: El nombre del autor que pintó el objeto (si está disponible).
· Imagen: Imagen del edificio.

simutrans-124.5/simutrans/text/es/citygrowth.txt000066400000000000000000000010271520675460500221500ustar00rootroot00000000000000 Detalles sobre el crecimiento de la ciudad

Crecimiento de la ciudad

Este texto explica cómo se produce el crecimiento de la ciudad. Cómo se calcula, qué edificios se están construyendo y por qué. Y cómo influir en el desarrollo urbano a través de los distintos parámetros.

Una ciudad crece cuando las personas y el correo se transportan a su destino.
Y crece incluso cuando los mercados, las centrales eléctricas y las fábricas pertenecientes a la ciudad reciben suministros.

simutrans-124.5/simutrans/text/es/citylist_filter.txt000066400000000000000000000037731520675460500231700ustar00rootroot00000000000000Lista de Ciudades

Lista de Ciudades

Lista de Ciudades muestra los detalles acerca de las areas urbanas (villas, pueblos y ciudades) y poblaciones.

Para abrirla: pulsa en la herramienta Lista de Ciudades, en Administracion de Listas o pulsa [T].
Lista de Ciudades tiene dos botones de opcion, los cuales determinan el orden en que se muestran las areas urbanas y sus poblaciones.
{Consejos: Si los elementos listados solo son parcialmente visibles, entonces ajusta la ventana de la Lista de Ciudades o desplaza la lista usando la barra lateral.}

Total de habitantes: es el numero total de habitantes en el juego (la poblacion total de todas las areas urbanas). El reciente incremento de poblacion se muestra entre parentesis.

Ordenado por: tiene opciones para determinar el orden en que son mostradas las areas urbanas en la lista.
Pulsa en los botones de opcion para pasar a la siguiente opcion (cambia el nombre del boton de opcion):

- Nombre lista alfabeticamente (mayusculas antes que minusculas) los nombres.
- Ciudadanos ordena por poblacion.
- Crecimiento urbano ordena por tasa de crecimiento (depende del terreno cubierto por el area urbana y el numero de pasajeros transportado).

- ascendente / descendente invierte el orden de la lista.

Pulsa en un elemento listado en la Lista de Ciudadespara ver mas informacion sobre esa area urbana.
Los elementos listados para cada area urbana incluyen:

nombre asignado al area urbana (puede ser cambiado en el menu Informacion de la Ciudad)..

numero de habitantes de un area urbana, entre parentesis se muestra el incremento de poblacion.

simutrans-124.5/simutrans/text/es/citywindow.txt000066400000000000000000000103471520675460500221520ustar00rootroot00000000000000Ayuda de Estadisticas de Poblacion

Estadisticas de Poblacion

Estadisticas de Poblacion muestra estadistica sobre una area urbana seleccionada (villa, pueblo o ciudad) y se puede usar para cambiar el nombre del area urbana.

Cada arear urbana tiene su Ayuntamiento alrededor del cual crece. El transporte de pasajeros y correo ayuda al crecimiento, los cual supone: carreteras construidas y gestionadas por el jugador servicio publico; aparecen los nuevos edificios y las industrias; y el limite de la ciudad se extiende.

Pulsa en el Ayuntamiento con al herramienta de Exploracion o en una area urbana listada en la Lista de Ciudades para abrir las Estadisticas de Poblacion, las cuales contienen: caja de texto con el nombre; estadisticas; dos mini-mapas; y una grafico del area urbana.

nombre: el nombre asignado al area urbana aparece en una caja de texto en lo alto de la ventana Estadisticas de Poblacion.
Los nombres de las areas urbanas se usan en los nombres por defecto de las Paradas.
Para cambiar el nombre: pulsa en la caja de texto y escribe un nuevo nombre.
{Consejos: Usa [!] para activar la vista del nombre del area urbana, sobre el Ayuntamiento. Lee mas en el archivo readme_citylist.txt (en el directorio ...simutrans/text/) para ayuda en la generacion de nombres para areas urbanas}.

Bajo la caja de texto de nombre , se muestran estadisticas del area urbana:
Tam. ciudad: es el numero de habitantes (poblacion del area urbana). el incremento reciente en la poblacio se muestra entre parentesis.
edificios: numero de edificios en el area urbana.
coordenadas del mapa: indica los limites de la ciudad (cambia a medida que crece el area urbana).

Los contadores se incrementan con el crecimiento del area urbana y decrecen con la construccion de nuevos edificios
Desempleados: contador que determina que tipo de edificio (residencial/comercial/industrial) sera construido.
Sin hogar: contador que determina que tipo de de edificio (residencial/comercial/industrial) sera construido.

mini-mapas del mundo de juego en la ventana Estadisticas de la poblacion idica los destinos de los pasajeros y el correo.
Los destinos se muestran como puntos coloreados segun el mes actual de juego (mini-mapa de la derecha) y el ultimo mes de juego (mini-mapa de la izquierda).El color de los punto de destino da detalles acerca de los pasajeros y el correo:
- amarillo: encontro una ruta y una Parada no abarrotada para comenzar el viaje (se muestra una cara sonriente en la ventana Informacion de la Parada).
- naranja: no encontro ruta a su destino.
- rojo: encontro una ruta pero la parada inicial estaba abarrotada (se muestra una cara triste en la ventana Informacion de la Parada).

Grafico muestra las estadisticas del area urbana.
El eje y es la cantidad, el eje X es el tiempo.
Pulsa en la etiqueta para cambiar la escla de tiempo del eje X:
Años: muestra valores anuales para los ultimos diez años de juego.
Meses: muestra valores mensuales para los ultimos 12 meses de juego

Pulsa en los botones de opciones del grafico para ver informacion en el grafico (el boton se oscurece cuando seleccionas la opcion).
El color de las lineas del grafico corresponde con los colores de los botones de opcion:
Ciudadanos: poblacion.
Crecimiento urbano: cambios en la poblacion (depende la poblacion y los servicios de transporte proveidos para pasajeros y correo).
Recorrido: pasajeros y correo que gestionados a traves de la red de transporte (mostrado como una cara feliz en la ventana Informacion de la Parada)
Pasajeros: Numero total de pasajeros y correo generado en el area urbana.

{Consejos: Las opciones relacionadas con areas urbanas se pueden cambiar en los archivos simuconf.tab y cityrules.tab}.

simutrans-124.5/simutrans/text/es/climates.txt000066400000000000000000000027051520675460500215520ustar00rootroot00000000000000Ayuda de los Controles del Clima

Controles del Clima

Controles del Clima establece las opciones para los aspectos topograficos y meteorologicos del juego

Controles del Clima se abre con el botonCrear un Nuevo Mundo.

Ajusta los parametros con los botones:

Opciones:

Nivel del agua: establece la altura del nivel del mar en el mapa.

Altura: establece la altura maxima del terreno.

Terreno abrupto: establece la irregularidad del terreno; un valor alto crea un terreno mas escarpado.

Opciones de la cota de nieve:

Cota de nieve veraniega: establece la cota de nieve en verano

Cota de nieve invernal: establece la cota de nieve en invierno.

Opciones del clima:

clima desertico: muy poca lluvia y temperaturas extremas

clima tropical: tipico de lugares cercanos al ecuador, altas temperaturas constantes a nivel del mar y suaves mesetas).

clima mediterraneo: clima ligeramente humedo y cambiante

clima templado: se da en areas entre los tropicos y los polos, clima cambiante.

clima artico (tundra): bajas temperaturas, muy cerca de los polos

clima rocoso: cercano al desertico.

simutrans-124.5/simutrans/text/es/color.txt000066400000000000000000000016501520675460500210650ustar00rootroot00000000000000Ayuda sobre Color

Color del Jugador

Color establece el color del jugador.
Los vehiculos, edificios y otros elementos reflejan el color del jugador.

Color se abre desdeOpciones de juego.

Pulsa en uno de los colores para cambiar el color del jugador.
La ventana de Color cambia (no al nuevo color elegido) para indicar que un nuevo color ha sido seleccionado.

El color por defecto es azul claro para el jugador humano.
Algunos elementos no tienen la opcion de color del jugador, y puede que no muestren el color elegido.

{Consejo: Para cambiar el color de los jugadores controlados por la maquina usa laLista de Jugadores o P+ Cambiar de Jugador para seleccionar primero un jugador controlado por la maquina}.

simutrans-124.5/simutrans/text/es/convoi.txt000066400000000000000000000067741520675460500212600ustar00rootroot00000000000000Lista de Vehiculos

Lista de Vehiculos

Lista de Vehiculos muestra informacion y tiene controles para filtrar y listar los diferentes Convoys (un Convoy esta compuesto por un unico vehiculo o una combinacion de varias unidades).

Para abrirlo: pulsa en la herramienta de Lista de Vehiculos en Administracion de Listas o presiona [V]. Lista de Vehiculos tiene cuatro botones de opciones, dos se usan para ordenar los elementos de la lista y los otros dos se usan para aplicar opciones a los elementos seleccionados de la lista.
Los botones de debajo son una lista de Convoys que concuerdan con el criterio usado en las opciones de filtro.
{Consejos: Si no se listan Convoys cambia las opciones de Filtrado.Si los elementos de la lista son parcialmente visibles, entonces ajusta la ventana Lista de Vehiculos o desplaza ls lista usando la barra de desplazamiento.}

Pulsa en los botones de opcion para pasar a la siguiente opcion (cambia el nombre del boton) o abre el menu Opciones de Filtro:

Ordenar por: Dos botones de opcion determinan el orden de los Convoys mostrados en la lista.

- Tipo ordena por tipo de transporte (en orden ascendente de vehiculo de carretera, vehiculo de carretera, vehiculo de via ferrea, barcos y aviones)
- Nombre ordena por nombre asignado, alfanumericamente: mayusculas entes que minusculas (por defecto esto se hace para la primera unidad del un vehiculo comprado o unido para formar un Convoy).
- Ingresos ordena por beneficio (ingresos generados menos costes operativos). - ID interno ordena por un numero ID unico para cada convoy (asignado por defecto cuando un Convoy es comprado o ensamblado por primera vez en un deposito mostrado en la titulo de la ventana de Informacion del Convoy).

- ascendente / descendente invierte el orden de la lista.

Filtro: Dos botones de opcion, enciende/apaga el criterio de selecciona para la lista y los controles de acceso para cambiar los criterios.

- habilitar / deshabilitar pulsa para aplicar los criterios de flitrado para los Convoys en lista.

- Ajustes abre controles para cambiar los criterios de filtrado.

Pulsa en un elemento listado para ver mas informacion acerca de ese Convoy.

Los elementos listados por cada Convoy incluyen:

ID interno: un numero ID unico por convoy (asignado por defecto cuando el Convoy es comprado o ensamblado por primera vez en un deposito y se muestra en el titulo de la ventana deInformacion del Convoy).

nombre que has sido asignado (por defecto se hace cuando el primer vehiculo es comprado o ensamblado).

ingresos muestra los beneficios (ingresos generados menos costes operativos cuando esta en movimiento).

Linea: muestra la Linea asignadae indica si el Convoy esta en un deposito.

graficos muestra el tipo y nivel de carga.

simutrans-124.5/simutrans/text/es/convoi_filter.txt000066400000000000000000000045051520675460500226130ustar00rootroot00000000000000Filtro de la Lista de Vehiculos

Filtro de la Lista de Vehiculos

Filtro de la Lista de Vehiculos tiene opciones que determinan que Convoys, se muestran en la Lista de Vehiculos.

Pulsa en Opciones en la Lista de Vehiculos para abrir el Filtro de la Lista de Vehiculos.

Los botones de Opcion seleccionan los Convoys que apareceran en la Lista de Vehiculos cuando este activada la opcion de Filtro. Solo los Convoys que se ajusten al los criterios seran listados.

Pulsa en el boton cuadrado para establecer los criterios de flitrado (el boton de oscurece cuando es seleccionando):

Filtrar nombres: selecciona los Convoys por el nombre. Para usarlo: pulsa en el boton para seleccionar, despues pulsa en la caja de texto Nombre Filtro y teclea en nombre requerido con exactitud (distingue entre mayusculas y minusculas).

Filtros de tipo : listara los Convoys por tipo de transporte seleccionado. Para usarlo: pulse en el boton seleccionado, despues pulse en el tipo(s) de transporte: autobuses/camiones, trenes, barcos y aviones.

Filtros especiales: selecciona Convoys para listarlos segun los siguientes criterios (pulsa en el boton Filtro Especial, despues pulsa en la opcion eleguida ):
- sin camino - no puede encontrar el camino al proximo destino.
- sin horario - no tiene asignada una ruta.
- sin ingresos - no genera beneficios.
- en el deposito - aquellos que estan en un deposito.
- sin linea - no tiene asignada una Linea.

Filtro de Mercancias: selecciona los Convoys por la capacidad de transportar diferentes mercancias y pasajeros. Pulsa en el boton junto al nombre del elemento(s) seleccionado(s). Usa la barra de desplazamiento para desplazar los elementos en la lista. Entre las opciones se incluyen:
- todo selecciona todos los elementos.
- nada no selecciona ningun elemento.
- inv. invierte la seleccion actual de elementos.

simutrans-124.5/simutrans/text/es/convoidetail.txt000066400000000000000000000037131520675460500224310ustar00rootroot00000000000000Ayuda de Detalles del Convoy

Detalles del Convoy

Detalles del Convoy da mas informacion acerca de un Convoy (un vehiculo solo o una combinacion de unidades) y puede usar para vender rapidamente el Convoy.

Detalles del Convoy se abre desde el boton Detalles en la ventana Informacion del Convoy.
Si todos los elementos no son visibles ajusta la ventana de Detalles o usa la barra de desplazamiento para desplazar los elementos.

En lo alto de la ventana Detalles del Convoy se muestra la siguiente informacion:

Potencia: de Convoy (potencia combinada en el caso de un convoy de varias unidades).

Valor de Venta: el dinero recibido si se vende el Convoy (precio de compra menos un ligera depreciacion).

CUIDADO: Pulsa en el boton Modo Venta para vender el Convoy inmediatamente. No requiere confirmacion de la opracion de venta. El Convoy es eliminado del juego y se añade la cantidad de la venta al balance de jugador .

Detalles del Convoy lista todas la unidades del Convoy.
La informacion listada para todas las unidades de convoy incluyen:
- grafico del vehiculo: un dibujo de la unidad (tambien usado en depositos).
- Fabricado: mes y año de fabricacion, asi como fecha de introduccion si estas jugando con cronologia.
- Valor de Venta: cantidad recibida en caso de venta.
- Potencia: potencia generada (por vehiculos motorizados).
- Friccion: nivel actual de friccion.
- Ingresos maximos: ingresos del vehiculo por tipo de elemento transportado por el Convoy.
- Elementos cargados: cantidad y destino de los elementos actualmente cargados por el vehiculo.

simutrans-124.5/simutrans/text/es/convoiinfo.txt000066400000000000000000000156771520675460500221360ustar00rootroot00000000000000Informacion del Convoy

Informacion del Convoy

Informacion del Convoy muestra informacion sobre un Convoy (un vehiculo individual o una combinacion de varias unidades) usado para transportar pasajeros o mercancias.
Se lista los elementos cargados por un Convoy y tambien tiene controles para acceder a mas detalles y para la venta del Convoy; seguir la trayectoria del Convoy; y cambiar el nombre y la Ruta de un Convoy.

Un convoy esta compuesto por al menos una unidad motorizada (motorised) y un numero cualquiera de vagones/remolques.
Ejemplos de convoys: un grupo de caballos arrastrando un carruaje; una locomotora parte de un tender y vagones; un autobus; una camioneta; un camion con remolque; un tranvia de varios vagones; un barco; un remolcador empujando una barcaza; o un avion.
Los convoys son ensamblados y vendidos en el deposito segun el tipo de transporte.
{Consejos: Un convoy tiene un numero maximo de unidades segun su tipo: trenes, 24; vehiculos de carretera, 4 .}

Pulsa en un convoy con la Herramienta Explorar o pulsa en un convoy listado en la Lista de Vehiculos o en Administracion de Lineas para abrir la ventana Informacion del Convoy
El titulo de la ventana Informacion del Convoy muestra un numero ID y el nombre del (por defecto esto ocurre solo con el primer vehiculo del convoy ensamblado en el deposito).
Informacion del Convoy contiene una caja de texto para el nombre; una mini-vista e informacion acerca del convoy; botones de opcion; y una lista de los elementos cargados actualmente (si todos los elementos no son visibles ajusta la ventana Informacion del Convoy o usa la barra de desplazamiento).

La informacion mostrada en la parte superior de la ventana Informacion del Convoy:

ID interno: un numero ID unico por convoy (asignado por defecto cuando el convoy se compra o ensambla en el deposito).

- nombre: muestra el nombre asignado al convoy (asignado por defecto cuando el convoy se compra o ensambla en el deposito).
Pulsa en la caja de texto del nombre y escribe el nuevo nombre del Convoy para cambiarlo.

- velocidad: muestra la velocidad actual en km/h.
Entre parentesis, el limite maximo de velocidad del Convoy (determinado por la unidad mas lenta en un Convoy).
La barra verde indica la velocidad actual en relacion al limite maximo de velocidad.

- Ingresos: beneficios generados por el vehiculo (ingresos generados restando los costes de operacion) en el año actual de juego.
Entre parentesis, el coste operacional del Convoy en creditos Hajo por cuadricula.

- Peso: peso actual combinado del Convoy y la carga en toneladas.
Entre parentesis, el peso de la carga actual.
La barra verde indica la capacidad usada, para cargar mercancias y pasajeros.
La barra amarilla indica el nivel de Carga (cantidad minima de mercancias y pasajeros necesaria para continuar a la siguiente parada) establecida en los controles de Enrutar.
{Consejos: Para establecer o cambiar el nivel de carga en cada parada, para todos los convoys de una linea, usa la opcion Actualizar linea en la ventana Administracion de lineas o en los Controles del Deposito. Para establecer o cambiar el nivel de carga en cada parada, para un convoy solamente, en una linea, usa el boton Enrutar dentro de la ventana Informacion del Convoy o en los Controles del Deposito.}

- Destino: La proximo Parada o punto de paso en la Ruta del Convoy.
La barra verde indica el progreso hacia la siguiente parada.

- Sirve la linea: indica cualquier Linea asignada al Convoy.

- mini-vista muestra el Convoy.
Pulsa en la mini-vista para centrar la vista del juego en el Convoy.

Pulsa en los botones de la ventana Informacion del Convoy para abrir controles o seleccionar opciones:

Enrutar: abre los controles para cambiar la ruta y la cantidad minima de mercancias y pasajeros necesaria para continuar a la siguiente parada.

Ir a casa: manda el Convoy al deposito apropiado mas cercano. La carga se pierde, pero se recibe un ingreso por transporte al deposito.
En el deposito, el Convoy mantiene su Ruta y la Linea asignada.

sigueme: boton que permite seguir al Convoy (se oscurece si esta activada la opcion).
Para deseleccionar la opcion, pulsa el boton de nuevo o el boton izquierdo en la ventana del Mapa.

Grafica: pulsa el boton para mostrar el grafico (el boton se oscurece cuando el grafico esta visible) en la Informacion del Convoy.
El grafico muestra estadisticas de los ultimos 12 meses (eje x) cuando se selecciona la opcion.
Pulsa en los botones de opcion para ver informacion en el grafico (el boton se oscurece cuando la opcion esta seleccionada).
Las lineas de colores en el grafico corresponden a los colores de los botones:
- Capacidad libre indica el espacio no usado en el Convoy.
- Recorrido indica el numero de mercancias, pasajeros y correo transportado.
- Ingresos indica los ingresos generados por transporte.
- Operacion indica los costes generados por el Convoy en uso.
- Beneficio indica el beneficio recibido por transporte (Ingresos menos Costes de Operacion).

Detalles abre la ventanaDetalles del Convoy que contiene mas informacion y opciones para vender el Convoy.

Ordenar pasajeros/mercancia por: lista elementos actualmente cargados por el Convoy.
La informacion mostrada incluye la cantidad y capacidad del Convoy, tipo de carga, destino final y primera Parada.
El boton de opcion (cambia de nombre con cada seleccion) ordena la carga en grupos de pasajeros, correo y mercancias:
- destino: ordena la carga alfanumericamente, mayusculas antes que minusculas, por nombre del destino final.
- por (detalle): ordena la carga alfanumericamente, mayusculas antes que minusculas, por nombre del destino inicial.
- por (cantidad): ordena la carga por cantidad disponible en el destino intermedio.
- cantidad: ordena la carga por cantidad en orden descendente.

simutrans-124.5/simutrans/text/es/curiositylist_filter.txt000066400000000000000000000156771520675460500242600ustar00rootroot00000000000000Informacion del Convoy

Informacion del Convoy

Informacion del Convoy muestra informacion sobre un Convoy (un vehiculo individual o una combinacion de varias unidades) usado para transportar pasajeros o mercancias.
Se lista los elementos cargados por un Convoy y tambien tiene controles para acceder a mas detalles y para la venta del Convoy; seguir la trayectoria del Convoy; y cambiar el nombre y la Ruta de un Convoy.

Un convoy esta compuesto por al menos una unidad motorizada (motorised) y un numero cualquiera de vagones/remolques.
Ejemplos de convoys: un grupo de caballos arrastrando un carruaje; una locomotora parte de un tender y vagones; un autobus; una camioneta; un camion con remolque; un tranvia de varios vagones; un barco; un remolcador empujando una barcaza; o un avion.
Los convoys son ensamblados y vendidos en el deposito segun el tipo de transporte.
{Consejos: Un convoy tiene un numero maximo de unidades segun su tipo: trenes, 24; vehiculos de carretera, 4 .}

Pulsa en un convoy con la Herramienta Explorar o pulsa en un convoy listado en la Lista de Vehiculos o en Administracion de Lineas para abrir la ventana Informacion del Convoy
El titulo de la ventana Informacion del Convoy muestra un numero ID y el nombre del (por defecto esto ocurre solo con el primer vehiculo del convoy ensamblado en el deposito).
Informacion del Convoy contiene una caja de texto para el nombre; una mini-vista e informacion acerca del convoy; botones de opcion; y una lista de los elementos cargados actualmente (si todos los elementos no son visibles ajusta la ventana Informacion del Convoy o usa la barra de desplazamiento).

La informacion mostrada en la parte superior de la ventana Informacion del Convoy:

ID interno: un numero ID unico por convoy (asignado por defecto cuando el convoy se compra o ensambla en el deposito).

- nombre: muestra el nombre asignado al convoy (asignado por defecto cuando el convoy se compra o ensambla en el deposito).
Pulsa en la caja de texto del nombre y escribe el nuevo nombre del Convoy para cambiarlo.

- velocidad: muestra la velocidad actual en km/h.
Entre parentesis, el limite maximo de velocidad del Convoy (determinado por la unidad mas lenta en un Convoy).
La barra verde indica la velocidad actual en relacion al limite maximo de velocidad.

- Ingresos: beneficios generados por el vehiculo (ingresos generados restando los costes de operacion) en el año actual de juego.
Entre parentesis, el coste operacional del Convoy en creditos Hajo por cuadricula.

- Peso: peso actual combinado del Convoy y la carga en toneladas.
Entre parentesis, el peso de la carga actual.
La barra verde indica la capacidad usada, para cargar mercancias y pasajeros.
La barra amarilla indica el nivel de Carga (cantidad minima de mercancias y pasajeros necesaria para continuar a la siguiente parada) establecida en los controles de Enrutar.
{Consejos: Para establecer o cambiar el nivel de carga en cada parada, para todos los convoys de una linea, usa la opcion Actualizar linea en la ventana Administracion de lineas o en los Controles del Deposito. Para establecer o cambiar el nivel de carga en cada parada, para un convoy solamente, en una linea, usa el boton Enrutar dentro de la ventana Informacion del Convoy o en los Controles del Deposito.}

- Destino: La proximo Parada o punto de paso en la Ruta del Convoy.
La barra verde indica el progreso hacia la siguiente parada.

- Sirve la linea: indica cualquier Linea asignada al Convoy.

- mini-vista muestra el Convoy.
Pulsa en la mini-vista para centrar la vista del juego en el Convoy.

Pulsa en los botones de la ventana Informacion del Convoy para abrir controles o seleccionar opciones:

Enrutar: abre los controles para cambiar la ruta y la cantidad minima de mercancias y pasajeros necesaria para continuar a la siguiente parada.

Ir a casa: manda el Convoy al deposito apropiado mas cercano. La carga se pierde, pero se recibe un ingreso por transporte al deposito.
En el deposito, el Convoy mantiene su Ruta y la Linea asignada.

sigueme: boton que permite seguir al Convoy (se oscurece si esta activada la opcion).
Para deseleccionar la opcion, pulsa el boton de nuevo o el boton izquierdo en la ventana del Mapa.

Grafica: pulsa el boton para mostrar el grafico (el boton se oscurece cuando el grafico esta visible) en la Informacion del Convoy.
El grafico muestra estadisticas de los ultimos 12 meses (eje x) cuando se selecciona la opcion.
Pulsa en los botones de opcion para ver informacion en el grafico (el boton se oscurece cuando la opcion esta seleccionada).
Las lineas de colores en el grafico corresponden a los colores de los botones:
- Capacidad libre indica el espacio no usado en el Convoy.
- Recorrido indica el numero de mercancias, pasajeros y correo transportado.
- Ingresos indica los ingresos generados por transporte.
- Operacion indica los costes generados por el Convoy en uso.
- Beneficio indica el beneficio recibido por transporte (Ingresos menos Costes de Operacion).

Detalles abre la ventanaDetalles del Convoy que contiene mas informacion y opciones para vender el Convoy.

Ordenar pasajeros/mercancia por: lista elementos actualmente cargados por el Convoy.
La informacion mostrada incluye la cantidad y capacidad del Convoy, tipo de carga, destino final y primera Parada.
El boton de opcion (cambia de nombre con cada seleccion) ordena la carga en grupos de pasajeros, correo y mercancias:
- destino: ordena la carga alfanumericamente, mayusculas antes que minusculas, por nombre del destino final.
- por (detalle): ordena la carga alfanumericamente, mayusculas antes que minusculas, por nombre del destino inicial.
- por (cantidad): ordena la carga por cantidad disponible en el destino intermedio.
- cantidad: ordena la carga por cantidad en orden descendente.

simutrans-124.5/simutrans/text/es/depot.txt000066400000000000000000000177711520675460500210750ustar00rootroot00000000000000Cochera

Controles de Cochera

Las cochera se usan para comprar y gestionar vehiculos. Los diferentes tipos de transporte tienen su propia cochera. Las cocheras solo muestran los vehiculos electricos si estan sobre una via electrificada.
Si estan jugando con cronologia, a medida que el tiempo pase en Simutrans apareceran mas vehiculos.

Las herramientas para construir los distintos tipos de cochera estan disponibles en la barra de herramientas de construccion de cada tipo de transporte: Cochera ferroviaria; Cochera de Monorrail; Cochera de Tranvia; Deposito de carretera; Astillero ; y Hangar.

Pulsa en un deposito con la Herramienta de Exploracion para abrir Los controles de la Cochera que te dara informacion acerca de la compra y gestion de Convoys (un vehiculo operativo o una combinacion de varias unidades).
La barra de Titulo de la ventana Controles de Cochera indica el tipo de transportes que gestiona dicha cochera.

Controles de Cochera puede cambiar de tamaño (pulsa en la flecha hacia abajo en la barra de titulo de la ventana para volver al tamaño original).
Pulsa en las flechas hacia izquierda/derecha en la barra de titulo para pasar a la siguiente cochera que gestione el mismo tipo de vehiculo (centra la vista del juego en esa cochera).

En la parte superior de la ventana Controles de Cochera se muestra el numero de Convoys en la cochera, el nombre del Convoy y la Linea.

Nombre del Convoy: pulsa en los botones de flechas izquierda/derecha para pasar de un Convoy a otro en la cochera.
Para cambiar el nombre del Convoy: pulsa en la caja de texto e introduce un nuevo nombre (por defecto esto se hace para el primera unidad comprada o añadida para un nuevo Convoy).

Nombre de Linea: pulsa en los botones de flechas izquierda/derecha para seleccionar las lineas por tipo de transporte o pulsa en la caja de texto para abrir una lista de Lineas.
Para cambiar el nombre de la Linea: pulsa en la caja de texto e introduce un nuevo nombre. (por defecto se le asigna uno cuando se crea una Linea).

Grafico del convoy debajo de las cajas de texto de los nombres del Convoy y la Linea, se muestra el grafico del Convoy seleccionado.
Situa el raton sobre el grafico para ver los detalles acerca del vehiculo (se muestran en la parte inferior de la ventana Controles de Cochera).
Pulsa en el grafico del convoy o en alguna de las unidades para eliminarlo del mismo y almacenarlo en la cochera.

Debajo del grafico se muestra el numero de unidades de Convoy seleccionado, longitud de cuadriculas del Convoy (debe caber en una Parada del tamaño adecuado para carga y descargar pasajeros y mercancia), y la linea asignada.

Los botones de opcion (pulsa para usar) de Controles de Cochera incluyen:

Arrancar: envia el Convoy seleccionado fuera del deposito si este ultimo puede encontrar una ruta.

Enrutar: abre los controles para cambiar la ruta y la cantidad de elementos que un Convoy puede cargar en una Parada.

Desenganchar: almacena las unidades de Convoy seleccionado.

Vender: vende el Convoy actual, el balance aumente con la cantidad de Valor de Reventa.

Nueva Linea: abre los controles para definir una Ruta (una ruta y una minima cantidad de elementos que el Convoy puede cargar en una Parada), que pueda ser usado por varios Convoys.

Aplicar Linea: asigna la Linea mostrada en la caja de texto de linea en la parte superior de la ventana Controles de Cochera al Convoy seleccionado.

Actualizar Linea: abre los controles para cambiar la Ruta (una ruta y una minima cantidad de elementos que el Convoy puede cargar en una Parada) de la linea seleccionada.

Copiar Vehiculo: compra y añade otro convoy con la misma composicion, Ruta y Linea que el convoy seleccionado.

Debajo de los botones de opcion se muestran graficos de los vehiculos disponibles para comprar.
graficos de los vehiculos: los vehiculos se dividen en categorias, los cuales se pueden ver pulsando en las distintas pestañas. Las pestañas dividen los vehiculos en los siguientes grupos: vehiculos para pasajeros y correo, otros vehiculos (motorizados), y otros trailers y carruajes.
Un numero blanco sobre el grafico del vehiculo indica la cantidad de unidades almacenadas si algun Convoy ha sido desensamblado. Las unidades almacenadas se pueden vender o usarse en un nuevo Convoy.

Situa el cursor sobre el grafico del vehiculo para ver informacion acerca del vehiculo:
Nombre: y entre parentesis el tipo de motor usado.
Coste: precio de venta y entre parentesis el coste de operacion por kilometro
Potencia: potencia del motor y velocidad maxima para vehiculos motorizados.
Capacidad: cantidad y categoria de mercancia que puede llevar el vehiculo.
Peso: en toneladas.
Velocidad maxima: para vehiculos no motorizados.
Fecha intro: fecha en que estara disponible el vehiculo si esta activada la cronologia.
Fecha de retirada: fecha en que el vehiculo se vuelve obsoleto.
Construido por: creador del vehiculo para Simutrans.
Marcha: ratio de potencia.
Valor de Venta: cantidad de dinero recibida por la venta de un vehiculo previamente comprado.

Pulsa en los graficos de vehiculos debajo de las pestañas para comprar o vender un vehiculo dependiendo de la opcion del boton de opciones situado en la parte inferior derecha de la ventana de Controles de cochera. El boton de opcion tiene tres modos:

Añadir: compra un vehiculo y lo añade al Convoy seleccionado, o crea un nuevo Convoy.

Poner al frente: compra un vehiculo y lo añade el la parte frontal del convoy actual, o crea un nuevo Convoy.

Modo Venta: vende todos los vehiculos almacenados.

Barras de colores bajo los vehiculos indica que vehiculos y en que orden pueden ser añadidos a otros, para formar un Convoy operativo (las barras de color pueden cambiar con diferentes opciones de los vehiculos).
Los coloures indican:
- verde: el vehiculo puede ser usado.
- rojo: el vehiculo no puede ser usado
- amarillo: otro vehiculo debe ser puesto al frente o al final para crear un Convoy operativo.
- azul: el vehiculo se puede usar pero esta obsoleto.
{Consejos: un Convoy es operativo, si muestra barras verdes en todas las unidades de convoy; si existe alguna unidad con una barra amarilla necesitaras algun vehiculo que has olvidado.}

Mostrar obsoletos: si estas jugando con cronologia, el boton cuadrado mostrara (pulsa para usar) los vehiculos retirados.

simutrans-124.5/simutrans/text/es/display.txt000066400000000000000000000055571520675460500214260ustar00rootroot00000000000000Ayuda de Opciones de Pantalla

Opciones de Pantalla

Pantalla tiene controles acerca de la apariencia del juego; y da informaccion acerca del rendimiento del ordenador.

Pulsa en el boton Pantalla en el menu Opciones de Juego para abrir Opciones de Pantalla.

Pulsa en los botones para seleccionar las distintas opciones (el boton se oscurece cuando seleccionas la opcion), o usa los botones de flechas para ajustar las opciones:

Usar modo dia y noche: vista nocturna del juego.

Luminosidad:ajusta el brillo de la imagen; numeros negativos para oscurecer; valores muy altos o muy bajos pueden dar problemas; usa [+] para incrementar el valor sobre 0.

Invertir desplazamiento: invierte la direccion de desplazamiento en la Ventana de juego.

Velocidad de desplazamiento: establece la velocidad de desplazamiento de la Ventana de Juego.

Peatones en ciudades: muestra los peatones que aparecen en las areas urbanas.

Pedestrians en paradas: muestra los peatones que aparecen cuando un vehiculo llega a una Parada.

Densidad de trafico: establece el numero de vehiculos privador en las areas urbanas.
El numero de nuevos vehiculos priados que apareccen en las areas urbanas en crecimiento depende del tamaño y la densidad de trafico. Los valores altos crean mas trafico, el 0 no genera nada.

{Consejos: Se pueden cambiar mas opciones y valores en el archivo simuconf.tab}.


Informacion de Pantalla:

Debajo de estas opciones , se muestra informacion acerca del rendimiento del ordenador durante la ejecucion del juego Simutrans.
Si los numeros (normalmente en blanco) estan en rojo o amarillo, tendras que cambiar los parametros.
Cambios en el ritmo en el que pasa el tiempo en el juego, T, (usando el icono Acelerar >> en la parte superior de la pantalla, o [,]/ [.]) pueden cambiar el color de los numeros.

Frame Time: La primera cifra es el tiempo entre frames; la segunda es el tiempo exacto entre frames.

Inactivo: Cuando esta por debajo de 0, el ordendor tiene capacidad para ejecutar mas programas.

FPS: Valores altos significan animaciones mas suaves. Si los numeros estan en rojo, el ordenador es demasiado lento para los parametros actuales (intenta reducir la resolucion de la pantalla del juego).

Sim: Si esta en numeros rojos, el ordenador es demasiado lento para los parametros actuales - intentalo con un mapa de menor dimension con menos ciudades.

simutrans-124.5/simutrans/text/es/edittools.txt000066400000000000000000000143451520675460500217620ustar00rootroot00000000000000Ayuda de Herramientas de edicion de mapa

Herramientas de edicion de mapa

Herramientas de edicion de mapa permite personalizar el terreno de la partida actual.
Las herramientas pueden: cambiar poblaciones de las ciudades; crear carreteras urbanas, atracciones turisticas, centrales energeticas e industrias; la opcion Eliminar para jugar como otro jugador; y tambien para avanzar un año en la cronologia del juego.

La barra de herramientas se abre cuando juegas como el jugador servicio publico, que se puede seleccionar desde el menu Jugadores o el boton P+ en el menu Herramientas especiales de construccion(tambien pulsando la tecla [P]). La barra de herramientas continua abierta incluso cuando usas de nuevo la opcion cambiar de jugador.
Pasa el raton sobre la opcion (despues de abrir el menu o pulsando en la barra de herramienta) para ver el nombre y demas informacion de varias opciones-herramientas.

Las herramientas-opciones incluyen, de izquierda a derecha:

Ampliar ciudad: herramienta que incrementa la poblacion de una ciudad en 100 habitantes, construyendo nuevas carreteras y edificios si es necesario.
Para usarla: pulsa en la herramientas para seleccionar, el cursor cambiara a una flecha hacia arriba, y despues pulsa en una ciudad.

Reducir ciudad: herrramienta que reduce la poblacion de una ciudad en 100 habitantes. Las ciudades solo creceran si los niveles de Sin hogar y Desempleados aumentan.
Para usarla: pulsa en la herramientas para seleccionar, el cursor cambiara a una flecha hacia abajo, y despues pulsa en una ciudad.

Carretera urbana: herramienta que construye una carretera urbana entre dos puntos, y que como jugador servicio publico todo jugador deberia usar. Las carreteras urbanas no pueden ser construidas en areas ocupadas por otros edificios, y no encontraran un camino en terreno escarpado, agua y obstrucciones. La carretera urbana construida entre puntos, puede usar carreteras existentes en su camino.
Situa el cursor sobre la herramienta-opcion (despues de abrir o pulsando en la barra de herramientas) para ver el nombre, costes de construccion y mantenimiento para el jugador servicio publico y limite de velocidad maxima. Para construir una carretera urbana: pulsa en la herramienta parar seleccionar (el cursor cambiara de forma), despues pulsa en la vista del juego para marcar el punto de inicio (mostrara una bulldozer y las coordenadas en la parte derecha de la barra inferior de la pantalla), y finalmente pulsa en el terreno para el punto final.
{Consejos: Los diferentes tipos de carreteras se puede conectar. Usa los puentes y tuneles para conectar las carreteras a traves de terreno escarpado o para evitar obstaculos. Usa las Herramientas del Terreno para cambiar el terreno, para crear caminos para carreteras. Usa la herramienta Eliminar para eliminar carreteras urbanas y algunos obstaculos. Usa la tecla [Ctrl] al mismo tiempo para habilitar funciones extra.}

Construir atracciones turisticas aleatorias: herramienta para construir una atraccion turistica que los pasajeros querran visitar. Las diferentes atracciones generan tambien diferentes cantidades de pasajeros y correo. El tipo de atraccion construida es aleatorio y solo aparece si existe el suficiente espacio libre de obstaculos.
Para construir una atraccion: pulsa en la herramienta para seleccionar (el cursor cambiara de forma, punto rojo en circulo amarillo), despues pulsa en la posicion deseada del terreno.
{Consejos: Usa la herramienta Eliminar siendo jugador servicio publico para eliminar atracciones turisticas. Usa la herramienta Eliminar o las herramientas del Terreno para crear espacios adecuados para las atracciones.}

Construir nueva central electrica: herramienta que construye una central electrica (y sus posibles proveedores). El tipo de central electrica construida es aleatorio y solo aparece si existe el suficiente espacio libre de obstaculos..
Para construir una central electrica: pulsa en la herramienta o pulsa la tecla [I] para seleccionar (el cursor cambiara de forma, punto rojo en circulo amarillo), despues pulsa en la posicion deseada en el terreno.
{Consejos: Usa la herramienta Eliminar siendo jugador servicio publico para eliminar centrales electrica. Usa la herramienta Eliminar o las herramientas del Terreno para crear espacios adecuados para las centrales electricas.}

Construir un nuevo mercado en la ciudad mas cercana: herramienta que construye un consumidor final de mercancias en una ciudad (y los posibles proveedores, no necesariamente en la ciudad). El tipo de industria construida es aleatorio y solo aparece si existe suficiente espacio libre de obstaculos.
Para usarlo: pulsa en la herramienta para seleccionar (el cursor cambiara de forma, punto rojo en circulo amarillo), despues pulsa en una ciudad para construir un consumidor final.

{Consejos: Usa la herramienta Eliminar jugando como jugador servicio publico para eliminar consumidores. Usa la herramienta eliminar o las herramientas del terreno para crear espacios libres de obstaculos para las industrias.}

Impedir cambio de jugador: herramienta que elimina la opcion para jugar como otro jugador y abre el menu Herramientas de edicion de mapa.
Para usarlo: pulsa en la herramienta seleccionada (el cursor cambia a un candado), y despues pulsa en cualquier parte. No requiere confirmacion.

Avanzar cronologia un año: avanza el tiempo en un año. Si estas jugando con cronologia, a medida que pasa el tiempo apareceran mas transportes y opciones de construcion, y los vehiculos mas lentos que no sean rentables desapareceran (ya que los ingresos se calculan a partir de la velocidad media del vehiculo) .
Para usarlo: pulsa en la herramienta. No requiere confirmacion (la fecha cambia en la parte izquierda de la barra en la parte inferior de la pantalla).

simutrans-124.5/simutrans/text/es/factorylist_filter.txt000066400000000000000000000053041520675460500236570ustar00rootroot00000000000000Lista de Fabricas/Industrias

Lista de Fabricas/Industrias

Lista de Fabricas/Industrias muestra informacion acerca de todas las fabricas (suministradoras y consumidoras de mercancias) en la parida actual.

Para abrir la Lista de Fabricas/Industrias: pulsa en la herramienta de Lista de Fabricas en el icono Administracion de listas.
{Consejos: Si no todos los elementos son visibles, entonces ajusta la ventana de Lista de Fabricas o desplaza la lista mediante la lista de desplazamiento.}

Ordenado por: tiene opciones que determinan el orden de las Fabricas listadas.
Pulsa en los botones para para pasar a la siguiente opcion (cambia en nombre del boton de opcion):

- Nombre de fabrica ordena alfabeticamente por nombre.
- Suministro ordena por capacidad de almacenamiento del suministro.
- Produccion ordena por capacidad de almacenamiento de la produccion.
- Produccion ordena por produccion.
- Rating ordena por el color de la barra de estado (mira mas abajo).
- Potencia ordena por electricidad suministrada.

- ascendente / descendente invierte el orden de la lista.

Pulsa en un elemento listado en la Lista de Fabricas para ver mas informacion acerca de esa Fabrica.
Los elementos listado para cada Industria incluyen:

barra de estado en color: el color indica la operatibilidad de la Industria (tambien se usa en Informacion de la Industria):
- blanco: no requiere mercancias.
- amarillo: conectada por transporte, pero la cadena de suministros tiene un pobre suministro.
- verde: en estado optimo.
- naranja: operativa pero es necesita mejoras.
- rojo: operativa pero parte de la cadena de suministros tiene exceso de suministro.

El icono rojo de un rayo se muestra si la Fabrica requiere suministro electrico mediante una subestacion transformadora.

Nombre de la Fabrica: nombre de la industria.

Entre parentesis 3 valores que representan:
Consumo actual: cantidad de mercancias almacenadas y listas para ser procesadas.
Produccion actual: cantidad de mercancias preparadas para el transporte.
Produccion total: indice de produccion de la fabrica (cantidad maxima de mercancias producidas por mes/dia)

simutrans-124.5/simutrans/text/es/finances.txt000066400000000000000000000065731520675460500215460ustar00rootroot00000000000000Ayuda de Finanzas

Finanzas

Finanzas provee de un vista general del estado financiero del jugador.

Pulsa en el icono del dolar en la parte superior, o presiona [f], o pulsa en el nombre del jugador en Jugadores, para abrir Finanzas.

Los valores (en Creditos Hajo ) estan listados para el año actual y los siguientes.
Este año: año actual en juego.
Ultimos años: años previos de juego.

Los valores anuales pueden mostrarse en un grafico de los ultimos diez años, o de los ultimos doce meses del año actual en juego.
Pulsa en el boton con el nombre de valor para mostrar los detalles en el grafico: El eje Y es la cantidad, el eje X es el tiempo.
Pulsa en el pestaña del grafico para cambiar la escala del eje X:
Años: muestra los valores anuales de los ultimos 10 años de juego.
Meses: muestra los valores mensuales de los 12 ultimos meses de juego

Entre los valores (pulsa en los botones con los nombres para ver los detalles), se incluyen:

Ingresos - ingresos totales del transporte de pasajeros, mercancias, correo y eneriga electrica.

Operacion - coste total generado por todos los Convoys en movimiento.
{Consejos: El coste operacional por kilometro se puede ver en el deposito y en la venta Informacion del Convoy.}

Mantenimiento - coste total del mantenimiento de la red de transporte.
{Consejos: El coste mensual de mantenimiento de la red (Mantenimiento (mensual) se muestra a la derecha en la ventana Finanzas) es deducido el final del mes.}

Beneficio por Operaciones - beneficio por transporte de mercancias, pasajeros, correo y energia electrica (Ingresos menos Costes de Operacion y Mantenimiento).

Nuevos Vehiculos - creditos gastados e ingresados por la venta y compra de vehiculos.

Construccion - coste total generado por la construccion de redes de transporte, modificaciones del terreno, lineas de transporte de energia electrica, y por el uso de la herramienta Eliminar.

Beneficio bruto - ingresos totales menos los costes totales.

Efectivo - los creditos actualmente disponibles para construir y comprar vehiculos (se muestra en el centro de la parte inferior de la pantalla.
{Consejos: Si el jugador humano esta en numeros rojos (balance negativo) durante tres meses consecutivos, el jugador se declara en bancarrota y el juego termina}.

Activos - El valor de todos los vehiculos del jugador al final del ultimo mes.
{Consejos: cada mes el valor de los vehiculos en uso desciende ligeramente, el valor actual se puede ver en Detalles del Convoy o cuando el vehiculo esta en un deposito.}

Valor Neto - Balance mas Activos.

Margen - porcentaje entre el Beneficio por operacion y los Ingresos.

Recorrido - total de mercancias, pasajeros y correo transportados.

{Consejos: Algunos costes, tales como la cantidad inicial y las finazas se pueden cambiar a traves del archivo simuconf.tab}

simutrans-124.5/simutrans/text/es/general.txt000066400000000000000000000066521520675460500213730ustar00rootroot00000000000000Ayuda de Simutrans

Ayuda General

- Bienvenido
- Interfaz del Juego/Ventanas
- Ayuda de Raton (en construccion)
- Ayuda de Teclado

Opciones de Juego
- Idioma
- Color del Jugador
- Opciones de Pantalla
- Sonido y Musica
- Jugadores
- Cargar Juego
- Guardar Juego
- Nuevo Juego
- - Controles del Clima
- - Cargar Topografia

Menu Principal
- Opciones de Juego
- Mapa
- Herramienta de Exploracion
- Herramientas de Modelado del Terreno
- Herramientas de Ferrocarriles/Vias
- Herramientas de Monorail
- Herramientas de Tranvia
- Herramientas de Carreteras
- Herramientas de Puertos
- Herramientas de Aeropuertos
- Herramientas Especiales de Construction
- Herramientas de Edicion del Mapa
- Herramienta Eliminar

Menus de Administracion
- Administracion de Lineas
- Listas
- - Lista de Paradas/Estaciones
- - - Filtro para Paradas/Estaciones
- - Lista de Vehiculos
- - - Filtro de Vehiculos
- - Lista de Ciudades
- - Lista de Mercancias
- - Lista de Industrias
- - Lista de Atracciones
- Buzon
- Finanzas

Otras Opciones
- Guardar Captura de Pantalla
- Pausa
- Acelerar
- Ayuda

Otros Menus
-Controles de las Cocheras
-Controles de Enrutar
- Informacion de las Paradas
- - Detalles de las Paradas
- Informacion de los Convoys/Vehiculos
- - Detalles de los Convoys/Vehiculos
- Informacion de las Ciudades
- Informacion de las Industrias

Si nada de esto te ayuda, prueba en
http://docs.simutrans.com
http://wiki.simutrans.com
http://simutrans-tips.com
http://forum.simutrans.com

simutrans-124.5/simutrans/text/es/goods_filter.txt000066400000000000000000000073331520675460500224330ustar00rootroot00000000000000Lista de Mercancias

Lista de Mercancias

Lista de Mercancias contiene informacion de los diferentes elementos que pueden ser transportados en la partida, y tiene controles para cambiar los ingresos recibidos del transporte.

Para abrirlo: pulsa en la herramienta Lista de Mercancias en el icono de la Administracion de Listas o presiona la tecla [G].

La lista de elementos disponibles para transportar en la partida indica: remuneracion recibida, % bonus por velocidad, categoria de la mercancia y tonelaje.
{Consejos: Si no se puedes ver todos los elementos de la lista, entonces ajusta la ventana de la Lista de Mercancias o desplaza la lista usando la barra de desplazamiento.}

Bonus por velocidad: muestra la velocidad necesaria requerida para los distintos tipos de mercancias, a la que un Convoy es capaz de transportar cosas, para recibir un ingreso extra por transporte repido de mercancias y pasajeros (en algunos casos se puede penalizar por demasiada lentitud).
Si estas jugando con cronologia, a medida que pasa el tiempo en Simutrans, la velocidad necesaria requerida cambia con la introduccion de nuevos vehiculos.
Los botones de flechas ajustan los ingresos recibidos y la velocidad a la que debe ir un Convoy para recibir bonus por velocidad (o incurrir en una penalizacion por demasiada lentitud).

Ordenado por: tiene opciones que determinan el orden de los elementos mostrados en la lista.
Pulsa en los botones de opcion para pasar de una opcion a otra (el nombre del boton cambiara):

- sin ordenar ordenado aleatoriamente.
- por nombre ordenado alfabeticamente.
- por ingresos ordenado por los ingresos recibidos por transporte de una unidad por cuadricula del juego.
- por bonus ordena por % de bonus de velocidad (% de ingreso extra si el Convoy usado es capaz de ir a la velocidad necesaria requerida para tener derecho a dicho bonus).

- ascendente / descendente invierte el orden de la lista.

Los detalles listados para cada elemento incluyen:

cuadrado de color es el mismo color mostrado en la barra de estado de color de mercancias, en cada Parada para indicar la cantidad de elementos que esperan a ser transportados.
{Consejos: Usa [!] para mostrar la barra de colores de mercancias sobre una Parada.}

ingresos son los ingresos recibidos por transportar una unidad a una distancia de una cuadricula del juego.
{Consejos: Usa el Modo Principiante para incrementar los ingresos un 150% (puedes cambiarlo tambien en el archivo simuconf.tab)}

% bonus es el % de ingresos extra recibidos si el Convoy usado es capaz de ir a la velocidad necesaria requerida para tener derecho a bonus (en algunos casos se puede incurrir en una penalizacion si el vehiculo es demasiado lento).
La formula de calculo para los ingresos es compleja y considera la maxima velocidad del Convoy junto con la velocidad media de los vehiculos disponibles,y no la velocidad del vehiculo mostrada durante la partida.

tipo de mercancia indica el tipo de vehiculo necesario para el transporte. La carga especial requiere vehiculos especificos.
{Consejos: La capacidad de los vehiculos muestra que elementos puede transportar dicho vehiculo}.

peso se muestra el peso de una unidad en kilogramos.

simutrans-124.5/simutrans/text/es/haltlist.txt000066400000000000000000000102471520675460500215750ustar00rootroot00000000000000Lista de Estaciones

Lista de Estaciones

Lista de Estaciones muestra informacion y tiene controles para filtrar y listar las diferentes Paradas (pero no se incluyen las paradas de vehiculos maritimos), donde los vehiculos cargan y descargan pasajeros y mercancias.

Para abrirla: pulsa en la herramienta de Lista de Estaciones dentro de Administracion de Listas. Lista de Estaciones tiene cuatro botones de opcion, dos usados para ordenar elementos de la lista y otros dos para aplicar opciones a los elementos seleccionados para mostrar en la lista. Los botones de debajo hay una lista de Paradas que cumplen los criterios establecidos por las opciones de filtrado.
{Consejos: Si no se ha listado ninguna Parada cambia las opciones de Filtrado. Si no se ven todos los elementos de la lista, entonces ajusta la ventana de Lista de Estaciones o desplaza la lista con la barra de desplazamiento.}

Pulsa en los botones para pasar de una opcion a otra (cambiara el nombre del boton) o abre los controles de Opciones de Filtro:

Ordenado por: dos botones de opcion que determinan el orden de las Paradas mostradas en la lista:

- Tipo ordena por tipo de vehiculo.
- sin orden ordena en orden aleatorio.
- Nombre ordena alfanumericamente (mayusculas antes que minusculas) por el nombre asignado.
- esperando ordena por cantidad de mercancias y pasajeros presentes en relacion a la capacidad disponible y el servicio de transporte proveido.

- ascendente / descendente invierte el orden de la lista.

Filtrar: dos botones de opcion, activa/desactiva los criterios de seleccion para listar y controles de acceso para cambiar los criterios.

- activar / disactivar pulsa para aplicar los criterios de filtrado.

- Opciones pulsa para abrir los controles para cambiar los criterios de filtrado

Pulsa en un elemento listado en Lista de Estaciones para ver mas informacion acerca de esa Parada.
Los elementos listados para cada Parada incluyen:

barra de color de estado: los colores indican la operatibilidad de una Parada en relacion con como de atestada esta. La barra de color se usa tambien en Informacion de la Parada y Administracion de Listas y es el mismo color mostrado en la barra de color mostrada sobre la Parada:
- amarillo: sin servicio de transporte.
- verde: no se necesitan mejoras.
- naranja: es posible que necesite mejoras.
- rojo: es recomendable hacer mejoras.
{Consejos: Usa [!] para mostrar la barra de color sobre la Parada.}

nombre que le ha sido asignado {Consejos: Tienes la opcion de asignar numeros en el nombre de la Parada a traves del archivo simuconf.tab}.

icono de vehiculo indica que tipos de vehiculos se pueden usar en la Parada (tambien se usa en Informacion de la Parada y en Administracion de Listas).
Los iconos incluyen: bus (para vehiculos de pasajeros por carretera), camion (para vehiculos de mercancias por carretera), tren, barco y avion. Tranvias se indica mediante el icono de bus o tren en el tipo de Parada.

icono(s) de carga indica que elementos (pasajeros, mercancias y/o correo) puede gestionar la Parada (tambien se usa en Informacion de la Parada y en Administracion de Listas).
{Consejos: construyendo las extensiones apropiadas, cambiara la categoria de los elementos que puede gestionar la parada. Las Oficinas Postales pueden construirse junto a Paradas para permitir la gestion de correo}.

esperando detalles de las diferentes mercancias y pasajeros en la Parada.

simutrans-124.5/simutrans/text/es/haltlist_filter.txt000066400000000000000000000050071520675460500231400ustar00rootroot00000000000000Filtro de la Lista de Estaciones

Filtro de la Lista de Estaciones

Filtro de la Lista de Estaciones tiene opciones que determinan que Paradas (pero no se incluyen las paradas de barcos), donde los vehiculos cargan y descargan pasajeros, se muestran en la Lista de Estaciones .

Pulsa en Opciones en la Lista de Estaciones para abrir el Filtro de la Lista de Estaciones.

Los botones de opcion seleccionan Paradas de la Lista de Estaciones cuando la opcion del filtro esta activa. Solo se muestran las Paradas que cumplen dichos criterios.

Pulsa en un boton para seleccionar/deseleccionar criterios de filtrado (el boton se oscurece cuando lo seleccionas):

Filtrar nombres: selecciona las Paradas por nombre. Para usarlo: pulsa en el boton para seleccionar, pulsa en la caja de texto y escribe el nombre exacto requerido (distingue entre mayusculas y minusculas).

Filtrar tipos: listara las Paradas que tengan al menos uno de los elementos seleccionados. Para usarlo: pulsa en los botones. (Las paradas de barcos no aparecen en la Lista de Paradas)

Filtro Especial :
- desbordamiento no esta en uso.
- sin conexion selecciona las Paradas sin servicio de transporte.

Tipo de carga: hace referencia a las Paradas que son el destino final de los elementos seleccionados. Pulsa en el boton junto al nombre para seleccionar los elementos. Usa la barra de desplazamiento para desplazar los elementos de la lista. Entre la opciones se incluyen:
- todo selecciona todos los elementos.
- nada no selecciona ningun elemento.
- inv. invierte la seleccion actual de elementos.

Produccion: hace referencia a las Paradas que son el origen de los elementos seleccionados. Pulsa en el boton junto al nombre para seleccionar los elementos. Usa la barra de desplazamiento para desplazar los elementos de la lista. Entre la opciones se incluyen:
- todo selecciona todos los elementos.
- nada no selecciona ningun elemento.
- inv. invierte la seleccion actual de elementos.

simutrans-124.5/simutrans/text/es/industry_info.txt000066400000000000000000000131341520675460500226430ustar00rootroot00000000000000Informacion de Industrias

Informacion de Industrias

Informacion de Industrias muestra detalles acerca de la industria seleccionada (un proveedor o consumindor de mercancias) y permite que la vista del juego se posicione en determinados lugares.

Los proveedores y consumidores de mercancias estan conectados para crear una Cadena Industrial de Suministro que termina en un consumidor final. Alguna industria es a la vez consumidor y proveedor, situandose en la mitad de la Cadena Industrial de Suministro. Si no estas jugando en beginners mode, la industria dejara de suministrar si el consumidor esta saturada de mercancias.
Las industrias son un destino y origen para pasajeros y correo. Los vehiculos carga y descargan mercancias y pasajeros en una industria mediante las Paradas si el area de influencia de la parada cubre una porcion del edificio de la industria y si la Parada puede manejar la mercancia de la industria. {Consejos: Paradas maritimas/Plataformas petroliferas/Granja de peces/areas de influencia/petroleo/correo/pasajeros/ no son listados en la lista de estaciones}

El numero inicial de cadenas industriales de suministro se puede establecer en la ventana Crear un nuevo Mundo. A medida que las ciudades crecen, pueden aparecen nuevas industrias. Una nueva industria aparece cada vez que la poblacion de una ciudad dobla su poblacion en 2000 habitantes.
{Consejos: Añade industrias a una ciudad usando Construir un nuevo mercado en la ciudad mas cercan en en menu Herramientas especiales de cosntruccion. Cambia cuando las nuevas industrias son generadas en el archivo cityrules.tab (industry_increase_every = 0 no genera nuevas industrias )}.

Pulsa en una industria con la herramienta Explorar o en una industria listada en la Lista de Fabricas para abrir la ventana de Informacion de la Fabrica.
Usa la barra de desplazamiento para desplazar los elementos si no todos son visibles.

El nombre de la Industria seleccionada se muestra en el titulo de la ventana de Informacion de la Fabrica<.
Se muestra otra informacion y opciones, incluyendo:

Produccion: cantidad maxima de mercancias que suministrara la industria (si es un proveedor) o consumira (si es un consumidor final) en unidades por mes/dia.
{Consejos: Añadiendo subestaciones transformadoras y suministro electrico a una industria incrementa la cantidad de mercancias producidas/consumidas}.

Representacion de la Industria muestra una vista del edificio de la industria. Pulsa en el grafico para centrar la vista de juego en el edificio.

barra de color de estado bajo la representacion indica el estado operativo de la industria (tambien usado en la Lista de Fabricas):
- blanco: no requiere entrada de mercancias.
- amarillo: conectado por transporte, pero la cadena industrial de suministro tiene un suministro pobre.
- verda: en estado optimo.
- naranja: operativa pero es posible mejorar.
- rojo: operativo pero la cadena industrial de suministro tiene un exceso de suministro.

Consumidores: lista destinos en la Cadena Industrial de suministros para las mercancias producidas por la industria seleccionada.
Las industrias destino y sus coordenadas en el mapa son listadas junto a botones de flecha. Pulsa en el boton de la flecha para mover la vista del juego a ese destino.

Suministradores: muestra el origen de las mercancias en la Cadena Industrial de suministros, requerida por la industria seleccionada.
Las industrias suministradoras y sus coordenadas del mapa se listan junto a botones de flecha. Pulsa en el boton de la flecha para mover la vista al suministrador.

Trabajadores de : muestra en ciudades cercanas viven los empleados. Los pasajeros y correo de estas ciudades necesitaran ser llevados a la industria seleccionada.
Las ciudades y sus coordenadas del mapa se listan junto a botones de flecha. Pulsa en el boton de la flecha para mover la vista al suministrador.

Nivel de pasajeros: es la popularidad relativa como destino de pasajeros.
Correo: es la popularidad relativa como destino de correo.
{Consejos: unicamente una cuadricula de la industria necesita estar en el area de influencia de la parada, para dar un servicio optimo para pasajeros y correo}.

Produccion: da detalles de la produccion de la Industria.
La informacion mostrada incluye: nombre del elemento; cantidad actualmente almacenada en relacion a la maxima capacidad de almacenamiento por elemento; categoria de mercancia; y nivel de porcentaje de produccion comparada con el suministro.

Consumo: da detalles del consumo de la industria.
La informacion mostrada incluye: nombre del elemento; cantidad actualmente almacenada en relacion a la maxima capacidad de almacenamiento por elemento; categoria de mercancia; y nivel de porcentaje de produccion comparada con el consumo.

Paradas que sirven a la industria son listadas en la parte inferior de la ventana Informacion de la Fabrica.

simutrans-124.5/simutrans/text/es/inspection_tool.txt000066400000000000000000000144331520675460500231620ustar00rootroot00000000000000Herramienta de Exploracion

Herramienta de Exploracion

Exploracion se usa para abrir ventanas de dialogo que dan informacion u opciones de control para distintos elementos.

Pulsando en el icono de la lupa en la barra de herramientas, en la parte superior de la pantalla, o pulsando [a] para abrir Exploracion (cambia el cursor a una lupa).

Para usar la herramienta de Exploracion: posiciona el cursor de la lupa en el sitio deseado y pulsa para abrir la ventana de dialogo. Si hay mas de un elemento en ese sitio, vuelve a pulsar para abrir una segunda venta de dialogo. {Consejos: La opcion de abrir una o varias ventanas de dialogo se puede modificar en el archivo simuconf.tab}

Pulsando en los siguientes elementos,se abren distintas ventanas de dialogo:

Depositos - abre los controles para comprar y administrar vehiculos en el deposito.

Paradas - abre la ventana Informacion de la Parada para esa parada.

Vehiculos - abre la ventana Informacino del Convoy/Vehiculo para ese vehiculo.

Ayuntamiento - abre la ventana Estadisticas de Poblacion para esa area urbana. {Consejos: La opcion de abrir una o varias ventanas de dialogo para dar informacion acerca de un poblacion esta disponible en el fichero simuconf.tab}

Industrias - abre la ventana Informacion de la Industria para esa industria.

Edificios de Ciudad abre una ventana de dialogo que da informacion acerca del edificio (El titulo de la ventana es 'Edificio'). La informacion contenida en la ventana incluye:
- descripcion da informaccion acerca del edificio. Si no esta disponible la descripcion se muestra como 'casa residencial', 'edificio industrial', 'tiendas y almacenes' (para edificios comerciales).
- Ciudad de: indica a que area urbana pertence el edificio.
- Nivel de Pasajeros: es la popularidad relativa como destino de pasajeros.
- Nivel de Correo: es la popularidad relativa como destino para correo.
- Aparece desde: indica el año en que aparece el edificio si esta jugando concronologia.
- Valor:: indica los costes de eliminacion del edificio si se usa la herramienta Eliminar.
- Pintado por : nombre de la persona que diseño y pinto el edificio para Simutrans

Monumentos / Atracciones turisticas abre una ventana de dialogo que da informacion acerca de una atraccion (el titulo de la ventana indica si es un monumento o una atraccion turistica). La informacion contenida incluye:
- Propiedad Publica indica que el elemento no pertenece a ningun jugador.
- descripcion da informacion acerca de la atraccion.
- Nivel de Pasajeros: es la popularidad relativa como destino de pasajeros.
- Nivel de Correo:es la popularidad relativa como destino para correo.
- Aparece desde:indica el año en que aparece el edificio si esta jugando concronologia.
-Pintado por : nombre de la persona que diseño y pinto la atraccion para Simutrans

Carreteras y Vias abre una ventana de dialogo que da informacion acerca de una carretera o via (el titulo de la ventana indica el tipo de via que es - en el caso de vias de tranvia sobre carreteras, indica el tipo de carretera).
Information contained in dialogue:
- Vel. Max: velocidad maxima permitida en esa via o carretera.
- Ribi (unmasked):
es un contador interno que indica la direccion de la via.
- Ribi (masked): es un contador interno que indica la direccion de la via teniendo en cuenta las señales.
- with sign/signal is shown if there is a signal or sign present.
- electrificado/no electrificado indica si la via esta electrificada.
- vehiculos el mes pasado indica el numero de Vehiculos que han usado la via en el mes previo de juego.
- reservado por indica el tipo de vehiculo que ha reservado la via pasa su uso hasta que haya pasado por ella.
- inclinacion indica la direccion de inclinacion si existe
- coordenadas del mapa mostradas en la barra inferior de la pantalla
En el caso de la vias del tranviaconstruidas sobre carreteras: la informacion se muestra dos veces, la primera por la carretera y la segunda por la via del tranvia.

Señales/Signos abre la ventana de dialogo, el titulo de la ventana indica si es una señal normal o de carretera. La informacion mostrada incluye:
- direccion es un contador interno que indica la direccion impuesta por la señal.

Tendido electrico / Subestacion transformadora abre una ventana de dialogo que da informacion acerca del suministro de electricidad (el titulo de la ventana es 'Linea Electrica'). La informacion mostrada incluye :
- Potencia: valor que indica la cantidad de energia electrica suministrada.
- Disponible:: valor mostrado para subestacion Transformadora en Industria indicando la cantidad relativa de energia suministrada.
- Net: es un numero ID del red. Dos puntos con el mismo ID estaran conectados.

Arboles abre una ventana de dialogo, cuyo titulo es 'Arbol'. La informacion mostrada incluye:
- nombre : dado por la especie de arbol.
- edad : edad del arbol en meses.

Terreno abre una ventana de dialogo, cuyo titulo es 'Terreno'. La informacion mostrada incluye:
- pendiente : da las especies de arboles.
- coordenadas del mapa : son mostradas en la barra inferior de la pantalla.

{Consejos: La opcion de abrir ventanas de dialogo para carretera y peatones esta disponible en el archivo simunconf.tab}.

simutrans-124.5/simutrans/text/es/language.txt000066400000000000000000000013401520675460500215260ustar00rootroot00000000000000Ayuda de Idioma

Idioma

Idioma cambia el idioma usado en el juego

Idioma se abre desde Opciones del juego & con Crear un nuevo mundo.

Para jugar en otros idiomas: haga click en el buton correspondiente al idioma deseado, antes de empezar a jugar.

El nuevo idioma aparecera reflejado solamente en barras de herramientas nuevas o reabiertas,
en el menu opciones y en algunos de los textos del juego. Si la traduccion no esta disponible
en el idioma seleccionado, aparecera en Ingles.

Visite https://translator.simutrans.com para ayudar con la traduccion

simutrans-124.5/simutrans/text/es/linemanagement.txt000066400000000000000000000214641520675460500227400ustar00rootroot00000000000000Administracion de Lineas

Administracion de Lineas

Administracion de Lineas muestra informacion y controles para gestionar Lineas (rutas y cantidad minima de mercancias y pasajeros que cargara el Convoy en la parada) para uno o varios Convoys con la misma ruta.

Una Linea consta de una serie de Paradas donde los vehiculos cargan y descargan pasajeros y mercancias (los transportes maritimos usan una cuadricula cualquiera de agua en el area de influencia del puerto) y puntos de paso (usados para dirigir vehiculos si hay mas de una ruta posible o para proporcionar puntos intermedios de destino).

Pulsa en el icono de la red en la barra de la parte superior de la pantalla o pulsa [w] para abrir la ventana de Administracion de Lineas, de la cual puedes ajustar su tamaño (pulsa en la flecha hacia abajo de la barra de titulo para volver al tamaño original).

La parte izquierda de la ventana Administracion de Lineas lista las lineas existentes (para desplazar la lista: usa la barra de desplazamiento en la parte derecha de la lista), que puede filtrarse por tipo de transporte, pulsando en las distintas pestañas que hay sobre la lista para seleccionar:
Todo: lista Lineas de todos los vehiculos.
Tren: lista Lineas de todos los vehiculos ferroviarios.
Monorrail: lista Lineas de todos los vehiculos Monorrail/maglevs.
Tram: lista Lineas de todos tranvias.
Camion: lista Lineas de todos los vehiculos de carretera.
Barco: lista Lineas de todos los transportes maritimos.
Aire: lista Lineas de todos los aviones.

El color del nombre de la Linea indica los siguiente:
blanco - no hay vehiculos asignados a esta Linea
amarillo - no operativa, no genera beneficios o perdidas
negro - genera beneficios, sobra capacidad, necesita mejoras
azul - genera beneficios, sobra poca capacidad
rojo - linea con perdidas

Bajo la lista de lineas existentes estan los botones de opcion para gestionar las Lineas.
Pulsa en una Linea de la lista para seleccionarla (el nombre aparecera resaltado) y consulta informacion acerca de las Paradas, ingresos, y Convoys en la Linea seleccionada.
Si los detalles de las Paradas o Convoys son parcialmente visibles, ajusta el tamaño de la ventana Administracion de Lineas o usa las barras de desplazamiento para desplazar la informacion.

Los botones de opcion, en la parte izquierda, incluyen (pulsa para seleccionar):

Nueva linea:opcion que abre controles para definir la Ruta ( una ruta y la cantidad minima de mercancias y pasajeros que cargara el Convoy en cada Parada) de una nueva Linea.
Añade cuantas Paradas o puntos de paso como sea necesario, ajusta los niveles de carga para cada Parada, y por ultimo cierra la ventana de Enrutar para crear la nueva Linea (pulsa en la X de la parte superior izquierda de la ventana o usa el teclado). A la nueva Linea se le asignara un numero y se listara en la ventana Administracion de Lineas.
{Consejos: Selecciona el tipo de transporte usando las pestañas antes de crear una nueva Linea. Los vehiculos no cargan o descargan mercancias o pasajeros en los puntos de paso.}

Actualizar linea: abre los controles para alterar y gestionar una Ruta ya existente (una ruta y la cantidad minima de mercancias y pasajeros que cargara el Convoy en cada Parada) de una Linea. EL cambio afectara a todos los vehiculos de la Linea, una vez hayas cerrado la ventana de Enrutar (pulsa en la X de la esquina superior izquierda de la ventana).
Pulsa en el nombre de una Linea para seleccionarla (el nombre aparecera resaltado) y despues pulsa en el boton de opcion para abrir la ventana de Enrutar.

Borrar linea: borra la Linea seleccionada (una ruta y la cantidad minima de mercancias y pasajeros que cargara el Convoy en cada Parada).
Pulsa en el nombre de una Linea para seleccionarla (el nombre aparecera resaltado) y despues pulsa en el boton de opcion para eliminarla. No requiere confirmar la operacion.

Cuando se selecciona una Linea, las Paradas se muestran en la parte inferior izquierda de la ventana Administracion de Lineas. Pulsa en una Parada de la lista para abrir una ventana con informacion acerca de la misma.
Los elementos listado por cada Parada incluyen:
Barra de estado en color: los colores indican el estado operativo de una Parada en relacion a como de abarrotada esta. La barra de estado en color se usa tambien en la Lista de Estaciones y en Informacion de la Parada y el color es el mismo mostrado sobre cualquier Parada:
- amarillo: fuera de servicio.
- verde: no requiere mejoras.
- naranja: es posible que necesite mejoras.
- rojo: mejoras recomendadas.
{Consejos: Usa [!] para mostrar la barra de color sobre las Paradas.}
Nombre que ha sido asignado a la Parada.
Numero de Parada (Las Paradas son numeradas si esta activa esta opcion en el archivo simuconf.tab).
icono(s) de vehiculo indica que tipos de vehiculos pueden usar la Parada (se usa tambien en Lista de Estaciones y en Informacion de la Parada). os iconos incluyen: bus (para vehiculos de pasajeros por carretera), camion (para vehiculos de mercancias por carretera), tren, barco y avion. Los tranvias se indican mediante el icono del bus o tren dependiendo del tipo de estacion.
icono(s) de mercancias indica que elementos (pasajeros, mercancias y correo) puede manejar la Parada (tambien se usa en Informacion de la Parada y Lista de Estaciones).
{Consejos: añade las extensiones apropiadas para cambiar la categoria de las mercancias que puede manejar la Parada. Las oficinas de correos se pueden añadir a las Paradas para poder manejar correo}.
esperando detalles de las diferentes mercancias y pasajeros.

En la parte derecha de la ventana Administracion de Lineas se muestra informacion en un grafico, una caja de texto y una lista de Convoy para la Linea seleccionada.

Pulsa en un nombre de Linea de la lista (el nombre se resalta). Pulsa en los botones de opcion para mostrar informacion, acerca de la Linea seleccionada y los vehiculos asignados a esta, en el grafico (el eje x es el tiempo en meses):

- Capacidad libre - cantidad de espacio sobrante en los vehiculos para mercancias y pasajeros.

- Recorrido - cantidad de mercancias y pasajeros que han usado esa Linea.

- Ingresos - cantidad de ingresos generado por los vehiculos.

- Operacion - coste generado por los vehiculos en transito.

- Beneficio - ingresos generados menos los costes generados(Ganancias menos Costes Operativos).

- Convoyes - numero de Convoys asignados a la Linea.

Para renombrar una Linea: Pulsa en el nombre de una Linea de la lista (el nombre se resaltara), y despues pulsa en la caja de texto (debajo de las opciones del grafico, sobre la lista de Convoys) y escribe un nuevo nombre.

Cuando se selecciona una Linea (pulsa en el nombre de una Linea de la lista para resaltarlo), los Convoys asignados a la Linea se listan bajo el grafico.
En la parte superior de la lista de vehiculos se muestra: el numero total de Convoys asignados a la Linea; ingresos (ganancias menos costes operativos), capacidad, y cantidad actual de mercancias y pasajeros cargada(% de la maxima capacidad entre parentesis) the todos los Convoys de la linea.
Pulsa en un elemento de la lista para ver mas informacion acerca de dicho Convoy (usa la barra de desplazamiento para desplazar la lista).
los elementos listados por cada Convoy incluyen:
nombre: que ha sido asignado (por defecto esto se hace con el primer vehiculo comprado o añadido a un Convoy).
ingresos: muestra los beneficios (ingresos generados menos costes operaciones).
Linea: muestra el nombre de la Linea asignada al Convoy e indica si esta en una cochera.
graficos: muestra la composicion y el nivel actual de los elementos cargados.

simutrans-124.5/simutrans/text/es/list.txt000066400000000000000000000037351520675460500207300ustar00rootroot00000000000000Ayuda de Listas

Listas

Listas tiene opciones para abrir los controles con informacion acerca del estado actual de la partida (y tambien tiene controles para ajustar los ingresos relativos recividos del transporte de pasajeros, mercancias y correo).

La informacion esta disponible para el jugador en listas : Paradas; Convoys; ciudades; ingresos recibidos de las diferentes mercancias, pasajeros y correo; industrias; y atracciones turisticas.

Pulsa en un icono tipo lista en la barra de herramientas de la parte de arriba de la pantalla para abrir Listas.
Pasa el raton por encima de los iconos, para ver los nombres.

Pulsa en la herramienta seleccionada para abrir los controles con informacion,de izquierda a derecha, acerca de:

Lista de Estaciones: Paradas, donde los vehiculos cargan y descargan mercancias y pasajeros (no incluye las paradas de vehiculos maritimos).

Lista de Vehiculos: vehiculos sobre carretera y via del tren, aviones y barcos.

Lista de todas las ciudades: ciudades (villas, pueblos y ciudades).

Lista de todas las mercancias: ingresos recividos de las diferentes mercancias, pasajeros y correo (incluye controles para ajustar los ingresos relativos).

Lista de Fabricas: industrias (consumidores y suministradores de mercancias).

Lista de Atracciones : atracciones turisticas.

simutrans-124.5/simutrans/text/es/load.txt000066400000000000000000000033261520675460500206700ustar00rootroot00000000000000Ayuda de Cargar

Cargar

Cargar retoma un juego salvado (para continuar jugando) y puede ser usado para borrar esas partidas. Un juego que es retomado sustituye el mundo actual en juego, el cual se pierde.

Cargar se abre desde Crear un Nuevo Mundo y desde Opciones de Juego o presionando [L]; y lista el nombre, fecha y hora de las partidas salvadas disponibles.
Si no estan visibles todos las partidas: usa las barras de desplazamiento en la parte izquierda de la lista para desplazar los nombres.

Pulsa en un nombre de la lista para retomar una partida salvada; o teclea un nombre en la caja de texto en la parte de arriba de la ventana Cargar: pulsa en la caja de texto, escribe el nombre, y presiona [Enter] o [Return], o pulsa en OK.
Un nombre de partida incorrecto, se notificara mediante un mensaje de error : cierra el mensaje y prueba de nuevo.

CUIDADO: Pulsando en el boton X BORRARAS LA PARTIDA SALVADA de forma inmediata y permanente.
El nombre de la partida se eliminara de la lista y la ventana de Cargar se cerrara. USA ESTA FUNCION CON MUCHO CUIDADO.

IMPORTANTE: Nuevas versiones de Simutrans aparecen regularmente y las partidas salvadas de versiones previas podrian no funcionar con las ultimas versiones; por favor, lee las especificaciones de cada version para saber si las partidas antiguas son compatibles.

Pulsa en Cancelar para cerrar Cargar; o X en la parte superior derecha de la ventana; o usa el teclado.

simutrans-124.5/simutrans/text/es/load_relief.txt000066400000000000000000000032111520675460500222070ustar00rootroot00000000000000Ayuda de Cargar Topografia

Cargar Topografia

Cargar Topografia se usa para abrir un fichero de un mapa de altura del terreno (para usar en un juego nuevo) y puede ser usado para borrar este tipo de ficheros.

Cargar Topografia se abre desde Crear un Nuevo Mundo; y lista el nombre, fecha y hora (del juego salvado) del los distintos terrenos disponibles.
Si no puedes ver todos los elementos: usa la barra de desplazamiento en la parte derecha de la lista, para desplazar los nombres.

Pulsa en un nombre de la lista para usar una terreno; o introduce en nombre en la caja de texto en lo alto de la ventana: pulsa en la caja de texto, escribe el nombre, y pulsa [Enter], [Return] o pulsa en OK (un nombre incorrecto no tendra efecto, intentalo de nuevo).

CUIDADO: Pulsando en el boton X de la parte izquierda de un nombre de terreno, SE BORRARA EL FICHERO PERMANENTE E INMEDIATAMENTE, el nombre del fichero es eliminado de la lista y la ventana Cargar Topografia se cerrara. Usa esta funcion con cuidado.

Algunos terrenos estan disponibles en http://addons.simutrans.com.
El fichero nuevo debe tener formato ppm y estar en el directorio (o carpeta) llamada...simutrans/save/

La informacion acerca de como crear terrenos esta dsiponible en http://wiki.simutrans.com

Pulsa en Cancelar para cerrar la ventana de Cargar Topografia; o X en la parte superior izquierda de la ventana; o usa el teclado.

simutrans-124.5/simutrans/text/es/mailbox.txt000066400000000000000000000046031520675460500214030ustar00rootroot00000000000000Ayuda del Buzon

Buzon

Buzon lista mensajes de la partida y abre los controles para elegir como seran mostrados los mensajes.

Pulsa en el icono del buzon en la parte superior de la pantalla o presiona [B] para abrir el Buzon, el cual lista mensajes del juego.
Pulsa en un mensaje para mover la pantalla al lugar al que hace referencia el mensaje.

Opciones boton (pulsa para usar) que abre las Opciones del Buzon las cuales tienen controles que determinan como son mostrados los mensajes.


Opciones del Buzon

Opciones del Buzon tiene tres columnas de botones cuadrados que selecionan como son mostrados los mensajes.

De izquierda a derecha, las columnas seleccionan los criterios para mostrar los mensajes como: un texto en la parte inferior de la pantalla; como un aviso temporal; o como aviso permanente.
{Consejos: Pulsa la X en la esquina superior derecha del mensaje o usa el teclado para cerrar los avisos permanentes}

Pulsa en los botones cuadrados para seleccionar las opciones del mensaje para los siguientes mensajes:

Año nuevo: un nuevo periodo de 12 meses ha comenzado.

Noticias de la IA : un jugador controlado por la maquina ha abierto una nueva linea.

Noticias de las Ciudades: una ciudad esta contruyendo un ayuntamiento mas grande.

Sin Ruta: un vehiculo no puede encontrar la ruta al siguiente destino (una Parada o un punto de paso).

Nuevas Industrias: una nueva cadena industrial (un suministrador o un consumidor final) ha sido construido debido al crecimiento de una ciudad.

Turismo: una atraccion o monumento en la ciudad ha sido construido.

Nuevos Vehiculos: un nuevo vehiculos esta disponible, o es retirado (si la linea cronologica esta en uso).

Estacion llena: una Parada esta saturada.

Problemas: el jugador esta endeudado, y otros mensajes criticos.

simutrans-124.5/simutrans/text/es/map.txt000066400000000000000000000121271520675460500205250ustar00rootroot00000000000000Ayuda del Mapa

Mapa

Mapa muestra un mapa del mundo actual dando informacion del terreno, localizaciones importantes y redes de transporte. Tambien es una forma de mover la ventana del juego por el mundo creado.

Pulsa en el icono del mapa en la parte superior de la pantalla o pulsa [m] para abrir la ventana del Mapa, se puede ajustar o minimizar la ventana. Las flechas, en la parte superior del mapa, abren/cierran etapas : opciones del mapa, una escala de color min-max que muestra el nivel de actividad de algunas de las opciones, y una clave de colores de las industrias que se ven en el mapa.

El mapa tiene tres escalas diferentes: pulsa el boton derecho en el Mapa (o pon el cursor y usa la rueda del raton) para pasar de una escala a otra. Si no es visible el mapa entero entonces cambia la escala del mapa, o ajusta la ventana del Mapa, o mueve el mapa para ver la porcion deseada: pulsa el boton derecho para arrastrar (o usa las barras de desplazamiento en los laterales de la ventana).

En el mapa, el agua es azul y el terreno elevado es mas claro que el terreno bajo. La parte de mundo,que es mostrada, se representa con un cuadrado blanco en el Mapa: la parte superior de la pantalla corresponde con la parte superior izquierda del cuadrado blanco (El norte es la parte superior del mapa, el Noroeste es la parte superior de la pantalla). Si el cuadrado blanco cae fuera del mapa, esa parte se muestra como agua.

Para mover la vista del juego a grandes distancias, pulsa el boton izquierdo en el punto requerido o para arrastrar el cuadrado blanco.

Apunta con el cursor a una fabrica en el mapa, para ver el nombre y una linea blanca que enlaza con los consumidores que parpadean; manten pulsado [Shift] primero para mostrar una linea roja hasta los suministradores.

En el mapa (cuando no se estan usando las opciones): ciudades, atracciones, tuneles, canales aparecen en gris oscuro; carreteras en negro; vias de tren y tranvia en gris; puentes en gris claro; Paradas en rojo/amarillo; vias de monorail/maglev en naranja; areas de aeropuertos en blanco; y vehiculos en amarillo.

Pulsa en los botones de opcion para mostrar/ocultar detalles en el mapa acerca de:

Ciudades: nombres de las areas urbanas.

Pasajeros: areas de influencia para pasajeros de las Paradas.

Correo: areas de influencia para correo de las Paradas.

Mercancia: nivel de mercancias y pasajeros por las carreteras y vias (usa la escala min-max ).

Estado de la Parada: los colores indican la operatibilidad de la Parada en relacion con como de abarrotafa esta. El mismo color se muestra en las barras de color de estado en la Lista de Estaciones, en la ventana de Informacion de la Parada y en Administracion de Listas , y es el mismo color mostrado en la barra de color que hay sobre el nombre de la Parada).

Servicio: nivel de mercancias y pasajeros usando la Parada (usa la escala min-max).

Trafico: nivel de los vehiculos en carreteras y vias (usa la escala min-max).

Origen: nivel de las mercancias y pasajeros que comienzan el viaje en una Parada (usa la escala min-max).

Destino: nivel de las mercancias y pasajeros que terminan el viaje en una Parada (usa la escala min-max).

En espera: nivel de las mercancias y pasajeros esperando por transporte en una Parada (usa la escala min-max).

Vias: vias de tren, tranvia y señales; las vias, puentes y tuneles se muestran en blanco (en rojo si estan electrificadas) y las señales se muestran en amarillo.

Limite de velocidad: las carreteras y vias se muestran en relacion a la maxima velocidad permitida (usa la escala min-max).

Lineas electricas: el tendido electrico (usa la escala min-max para mostrar el nivel de suministro en el momento en que abriste el Mapa).

Atracciones turisticas: nivel de pasajeros que generan las atracciones turisticas (usa la escala min-max), la opcion tambien resalta las atracciones en el mapa.

Fabricas: resalta las industrias; ilumina los consumidores y suministradores en el mapa.

Depositos: resalta los depositos. Los colores indican el tipo de transporte: barco en amarillo; vehiculo de carretera en naranja; tren en rojo; avion en verde; tranvia en azul; y monorail en gris.

simutrans-124.5/simutrans/text/es/monorailtools.txt000066400000000000000000000177171520675460500226630ustar00rootroot00000000000000Ayuda de Herramientas de Monorail/Maglev

Herramientas de Monorail/Maglev

Herramientas de Monorail/Maglev construye un red de transporte mediante Monorail/Maglev. Las herramientas pueden crear o eliminar: vias de monorail y maglev; puentes; tuneles; señales; cocheras; estaciones; y extensiones. Si estas jugando concronologia, a medida que pase el tiempo apareceran nuevas herramientas.

Pulsa en el icono de Herramientas de monorail/maglev en la barra de la parte superior de la pantalla para abrir el menu de herramientas.
Situa el cursor del raton encima de los icones (tras abrir la barra de herramientas) para ver el nombre, los costes de construccion, costes de mantenimiento, limite maximo de velocidad y maxima longitud.

Herramientas de Monorail/Maglev incluye, de izquierda a derecha:

Via de Monorail/maglev : herramienta para construir vias para vehiculos monorail/maglev entre dos puntos. Las vias solo pueden ser construidas en cuestas en la direccion de dichas cuestas, y pueden no encontrar camino en terreno escarpado, agua u obstaculos. La nueva via puede usar parte de las vias ya existentes en su camino.
Para construir la via: pulsa en el icono de la herramienta Via (el cursor cambia a una via), despues pulsa en el terreno para establecer el punto inicial (se mostrara una excavadora y las coordenadas del punto en la parte izquierda de la barra inferior de la pantalla), por ultimo pulsa para el punto final de la via.
{Consejos: Los diferentes tipos de via se pueden conectar (excepto cuando intentas construir sobre vias de otros jugadores). Usa las Herramientas del terreno para modificar el terreno, para permitir la construccion de vias. Usa la herramienta Eliminar para eliminar partes individuales de la via y algunos obstaculos. Usa la tecla [Ctrl] al mismo tiempo para acceder a funciones extra. Deshaz la ultima ruta construida pulsando la tecla [z]. No se reembolsan los costes de construccion.}

Remove monorails: herramienta para eliminar vias de monorail/maglev, cuando no hay vehiculos presentes, entre dos puntos (las estaciones y señales tambien se eliminan).
El uso de esta herramienta implica un coste de construccion.
Para eliminar vias: pulsa en la herramienta (el cursor cambia a un cruz roja); despues pulsa en la via que quieres eliminar (en el punto seleccionado para borrar se muestra una cruz roja); por ultimo pulsa en otro punto de la via, para eliminar la parte entre ambos puntos.
{Consejos: Opera como otro jugador para eliminar sus vias.}

Rampas y Puentes: herramientas que construyen puentes rectos, para que pasen vehiculos monorail/maglev, entre dos puntos con via. Los puentes tienen una maximo de distancia entre pilares. Las herramientas construyen puentes partiendo desde el final de una via hasta otro punto adecuado (otro final de via o punto elevado).
Para construir una rampa o puente: pulsa en el icono de la herramienta (el cursor cambia a un puente), a continuacion pulsa en un tramo final de via (punto de comienzo del puente) para construirlo. Algunos obstaculos o la falta de espacio adecuado para emplazar el final del puente hara que el puente no se construye: construye via en ambos lados de futuro emplazamiento del puente, y prueba de nuevo.
{Consejos: Usa la herramienta Eliminar para destruir puente (pulsa en el final del puente) y algunos de los obstaculos para la construccion del puente. Usa las herramientas de Vias para conectar las partes finales del puente a las vias.}

Señales de Monorail: herramientas para construir señales para vehiculos monorrail y maglev en la via. Las señales dirigen y regulan el flujo de vehiculos en vias y puente, cruces y Paradas (donde los vehiculos cargan y descargan pasajeros).
Se pueden contruir señales de sentido unico o doble sentido. Para construir un señal de doble sentido en una via: pulsa en el icono de la herramienta (cambia el cursor a una señal); despues pulsa en la via. Para construir una señal de sentido unico: pulsa de nuevo en el mismo punto, con el cursor de señal, para pasar de un tipo de señal a otro.
IMPORTANTE: Ten cuidado en no poner señales de sentido unico, ya que pueden evitar que un vehiculo llegue a su destino, por defecto los vehiculos conducen por la derecha (se puede cambiar en el archivo simuconf.tab).
- Señales: los vehiculos continuan solo si el tramo de via hasta la siguiente señal o destino de la Ruta (a Parada o punto de paso) no esta ocupado por otro vehiculo. En el modo de sentido unico, los vehiculos se mueven en una unica direccion.
- PreSignals: los vehiculos continuan si el tramo de via (entre tres señales consecutivas o hasta el proximo destino en la Ruta) no esta ocupado por otro vehiculo. En el modo sentido unico, las preseñales dejan pasar a los vehiculos unicamente en un sentido.
- ChooseSignals: dirigen vehiculos hacia una estacion vacia, una Estacion multiple: un vehiculo pasando esta señal puede usar una estacion vacia en su proximo destino, y no justamente el que esta asignado en su Ruta. Si no hay estaciones vacias o una ruta clara hasta su proximo destino, los vehiculos esperaran en la señal.
{Consejo: Elimina las señales con la herramienta Eliminar. Manten pulsado [Ctrl] para poner señales en puentes sobre otras vias.}

Cochera de Monorail: herramienta que construye una cochera para comprar y gestionar vehiculos y vagones de monorrail/maglev.Las cocheras implican un coste de mantenimiento y se construyen al final de una via de monorrail/maglev.
Para construir una cochera de monorail: pulsa en la herramienta (el cursor cambia a una cochera), y a continuacion pulsa en un final de via.

Paradas de Tren: herramientas para construir estaciones, usadasd por vehiculos monorrailes/maglev para cargar o dejar pasajeros y correo.
Una estacion, cuando no se construye junto a otra Parada existente dara lugar a una nueva Parada.
Las Paradas tienen un coste de mantenimiento y se construyen sobre vias (pero no en curvas ni cruces).
Las paradas tiene un area de influencia para mercancias, pasajeros y correo. Las diferentes estaciones tienen distintas capacidades para mercancias, pasajeros y correo. . En la esquina superior de algunas de las herramientas un icono (usado en Lista de Estaciones y en Informacion de la Parada) muestra que elementos puede manejar la Parada.
Para construir una Parada: pulsa en el icono de la herramienta (el cursor cambia a una estacion), y despues pulsa en la via.
{Consejos: Elimina las Paradadas mediante la herramienta remove monorails. Las Paradas con extensiones y estaciones multiples (construyendo mas secciones de estacion en la via adyacente) pueden soportar vehiculos mas largos, incrementar su capacidad y area de influencia. Pulsa la tecla [v] para mostrar/ocultar el area de influencia para mercancias y pasajeros. Manten pulsado [Ctrl] para construir puentes o vias elevadas sobre otras vias.}

Monorail Foundation: herramienta que construye una extension de una Parada e incrementa la capacidad de dicha Parada. Las Monorail foundations se construyen en finales de vias de monorrail/maglev e implican un coste de mantenimiento.
Para construir una monorrail foundation: pulsa en la herramienta (el cursor cambia a foundation), y despues pulsa en el lugar deseado en el final de una via.
{Consejos: Elimina las monorail foundations mediante la herramienta Eliminar.}

simutrans-124.5/simutrans/text/es/new_world.txt000066400000000000000000000125611520675460500217520ustar00rootroot00000000000000Ayuda de Crear un Nuevo Mundo

Crear un Nuevo Mundo

Crear un Nuevo Mundo establece las opciones para una nueva partida, con un nuevo mapa y nuevas opciones.
Se incluyen muchas variables : tamaño del terreno, numero inicial de cuidades e industrias.
Se muestra una vista previa del terreno, segun las opciones elegidas.

Crear un Nuevo Mundo se abre junto con las ventanas de Idiomas y Control del Clima cuando arranca Simutrans (las otras opciones del juego estan deshabilitadas) y tambien se puede abrir desde el boton Opciones de Juego en la parte superior de la pantalla (las otras barras de herramientas, textos y opciones de la partida actual se cierran).
Los atajos del teclado para abrir la ventana Crear un Nuevo Mundo incluyen pulsar las teclas [Q] and [X].

Para volver a la partida actual, cierra la ventana Crear un Nuevo Mundo: pulsa en la X en la esquina superior izquierda de la ventana; o usa el teclado.

Los botones de las flechas ajustan las opciones y el resto activan/desactivan las opciones o abren nuevos controles:

Numero de Mapa: selecciona el terreno a usar en la partida.
Puls en la caja de texto y escribe el numero deseado o usa los botones de flechas para pasar de un terreno a otro.
La vista previa muestra el terreno seleccionado (el agua es azul, el terreno bajo es mas oscuro que el terreno elevado).

Tamaño: establece el tamaño del mapa (la cifra superior es el ancho, la inferior es el alto).
El numero entre parentesis es la cantidad de memoria requerida por el mapa
IMPORTANTE: Los mapas granges necesitan mas memoria y tardan mas tiempo en crearse. El juego y los graficos requieren aproximadamente 88MB en el pak64 y 144MB en el pak128. Como regla general,en un ordenador con 256MB RAM no intentes mapas mas grandes de 512 x 512.

Mapa al azar: selecciona un nuevo terreno de forma aleatoria , conservando el resto de las opciones.

Cargar topografia: abre los controles para cargar un mapa personalizado entre los disponibles.

Numero de ciudades: establece el numero inicial de ciudades en el juego.

Poblacion media por ciudad: establece la poblacion inicial de las ciudades.
Esta opcion es un valor medio, Simutrans tendera a generar mas ciudades pequeñas que grandes.

Longitud carretera interurbana: establece la longitud inicial de las carretera entre las ciudades.

Dens. de trafico: establece en numero de vehiculos privados en las ciudades.
Los valores alto dan lugar a mas vehiculos privados,el valor 0 no genera ninguno. A medida que crecen las ciudades, aparecen nuevos coches.

Fabricas fuera de las ciudades: establece el numero inicial de Cadenas industriales de suministro fuera de las ciudades.
Si el terreno tiene muchas cuestas, apareceran pocas industrias.

Mercados en las ciudades: establece el numero inicial de consumidores finales de mercancias en las ciudades.
Los nuevos consumidores (y los posibles suministradores, no necesariamente en ciudades) aparecen cada vez que se dobla la problacion a partir de los 2000 habitantes.

Atracciones turisticas: establece el numero inicial de lugares fuera de las ciudades que atraen pasajeros y correo.
Estas aparecen en las afueras de las ciudades, ademas de las atracciones generadas dentro de los pueblos y ciudades al inicion de la partida. Las atracciones tambien generan pasajeros y correo.

Usar cronologia desde el año: establece el año de inicio de la partida. Cuando la opcion esta seleccionada, a medida que pasa el tiempo en la partida, pueden aparecer mas vehiculos y edificios.
Cuando no esta seleccionada: la partida comienza en el ao elegido pero estan disponibles todos los vehiculos y edificios; las ciudades aparecen con edificios de todos los periodos; y los vehiculos mas lentos no son tan rentables (ya que en el calculo de los ingresos influye la velocidad media del vehiculo de transporte).

Permitir cambio de jugador: opcion que permite jugar como un jugador IA o como servicio publico (esta opcion no se puede cambiar un vez has iniciado una partida nueva).

Beginner mode: opcion que permite un nivel mas alto de ingresos que recibes por transportar mercancias y pasajeros(por defecto: el 150% del valor recibido en el modo normal). Ademas, las Industrias no pararan de suministrar cuando el consumidor tenga exceso de suministro.

Cargar juego: abre los controles para retomar una partida salvada, reemplazando el mundo actual que se pierde.

Empezar juego: crea un mundo nuevo, usando las opciones establecidas. Pulsa para comenzar a jugar - Buena suerte y divierte..

{Consejos: Los valores por defectos y otras propiedades cuando arranca Simutrans se pueden modificar en el archivo simuconf.tab}

simutrans-124.5/simutrans/text/es/options.txt000066400000000000000000000024721520675460500214450ustar00rootroot00000000000000Ayuda de Opciones de Juego

Opciones de Juego

Opciones de Juego abre controles para configurar eljuego; puedes salir del juego desde Opciones de Juego.

Pulsa en el icono del diskette en la parte superior izquierda de la pantalla para abrir Opciones de Juego.

Pulsa el boton para abrir el menu correspondiente:

Idioma: establece el idioma usado en el juego.

Color: establece el color de jugador

Pantalla: parametros para configurar la apariencia del juego.

Sonido: parametros para el sonido y la musica en el juego.

Jugadores: abre el menu para configurar los diferentes jugadores.

Cargar: abre el menu para cargar un juego salvado.

Guardar: abre el menu para guardar la partida.

Nuevo Mapa: abre el menu para empezar un nuevo juego (cierra el resto de menus y ventanas activas del juego).

Salir: sales de juego, la partida actual en juego no es salvada.

simutrans-124.5/simutrans/text/es/players.txt000066400000000000000000000042751520675460500214340ustar00rootroot00000000000000Ayuda de Lista de Jugadores

Lista de Jugadores

Lista de Jugadores da informacion y tiene controles para gestionar los distintos jugadores.

Pulsa en el boton Jugadores en Opciones de juego o pulsa [k] para abrir la Lista de Jugadores que muestra los nombres y balance economico de los jugadores.

Hay 8 jugadores : humano, servicio publico y 6 jugadores IA. Los controles permiten jugar como diferentes jugadores si se activo la opcion Permitir cambio de jugador al inicio de la partida.

Pulsa en las flechas para jugar como ese jugador en la partida actual. Aparecera un mensaje de confirmacion diciendo que has cambiado de jugador: pulsa en la X de la parte superior izquierda del mensaje o usa el teclado para cerrarlo.

Pulsa en el nombre del jugador para ver sus Finanzas.

Pulsa en el boton cuadrado junto al nombre del jugador IA para activar/desactivar dicho jugador (el boton se oscurece cuando esta activado el jugador).

Los jugadores IA no caen en bancarrota y gestionan vehiculos con maxima eficiencia.
Desactivar un jugador IA durante la partida no eliminara su red de transporte.
Algunos jugadores IA pueden usar solo vehiculos por carretera o via, o transportar solo mercancias o pasajeros.

{Consejos: Los vehiculos no usaran las vias o carreteras de otros jugadores, asi como tampoco Paradas (sin embargo, los vehiculos de todos los jugadores pueden usar elementos creados por el jugador servicio publico).
Los jugadores no pueden construir elementos o poner puntos de paso en las vias de otros jugadores, asi como tampoco en carreteras, o eliminar elementos creados por otros jugadores (sin embargo algunos elementos creados por el jugador servicio publico pueden ser eliminados usando la herramienta Eliminar).
Desactiva la opcion cambio de jugador y accede a herramientas extra como el jugador servicio publico.}

simutrans-124.5/simutrans/text/es/railtools.txt000066400000000000000000000234771520675460500217720ustar00rootroot00000000000000Herramientas de Ferrocarriles

Herramientas de Ferrocarriles

Herramientas de Ferrocarriles sirven para construir una red de transporte con ferrocarriles. Puedes construir: vias de tren (electrificarlas y eliminarlas); puentes; tuneles; señales; cocheras; estaciones; y extensiones. Si estas jugando con cronologia, a medida que pase el tiempo apareceran mas opciones.

Pulsa en el icono del tren en la barra de la parte superior de la pantalla para abrir la barra de herramientas. Situa el cursor sobre los iconos de la barra (tras abrirla) para ver el nombre, los costes de construccion, los costes de mantenimiento entre parentesis, limite de velocidad maxima y longitud maxima.

Herramientas de Ferrocarriles incluye, de izquierda a derecha:

Vias de Ferrocarril: herramientas para construir vias para ferrocarriles entre dos puntos. Las vias solo se pueden construir en cuestas, en la direccion de la cuesta y pueden no encontrar camino a traves de terreno escarpado, agua u obstaculos. Los nuevos tramos pueden usar algunos ya existentes en su camino.
Para construir vias: pulsa en la herramienta para seleccionarla (el cursor cambia a una via), despues pulsa en el terreno para establecer el punto de inicio (mostrara una excavadora y las coordenadas a la derecha, en la barra de la parte inferior de la pantalla), y pulsa de nuevo para establecer el punto de finalizacion.
{Consejos: Los diferentes tipos de vias se pueden conectar (siempre que no hayan sido construidas por algun otro jugador). Usa los puentes y tuneles para conectar vias a traves de terreno escarpado, o para evitar obstaculos. Usa las Herramientas del Terreno para cambiar el terreno, y habilitar caminos para las vias. Usa la herramienta Eliminar para eliminar partes individuales de la via o algunos obstaculos. Usa [Ctrl] al mismo tiempo para acceder a funciones extra. Pulsa la tecla [t] para seleccionar la ultima de las Herramientas de Ferrocarriles usada.}

Via electrificada: herramientas para electrificar vias, puentes y tuneles entre dos puntos, para vehiculos electricos.
Para electrificar un via: pulsa en la herramienta o pulsa la tecla [e] (el cursor cambia una via electrificada) para seleccionarla, despues pulsa en la via para establecer el punto inicial (situa un icono de electrificacion en dicho punto), y por ultimo pulsa en un segundo punto de la via, para electrificar el tramo entre ambos puntos.
{Consejos: Usa la herramienta Eliminar para volver a vias no electrificadas.}

Remove Track: herramienta que elimina vias normales y electrificadas, cuando no hay vehiculos presentes, entre dos puntos (ATENCION: estaciones, señales, puentes y tuneles entre ambos puntos tambien se eliminan). El uso de esta herramienta implica costes de construccion.
Para eliminar vias: pulsa en la herramienta (cambia el cursor a una cruz roja); despues pulsa en la via (situa una cruz roja en el punto seleccionado); por ultimo pulsa en un segundo punto de la via, para eliminar el tramo entre ambos puntos.
{Consejos: Cambia de jugador para poder borrar sus vias de ferrocarril.}

Puente ferroviario: herramientas para construir puentes rectos, para vehiculos ferroviarios, entre dos puntos de una via. Los puentes tiene una distancia maxima entre pilares.
Las herramientas construyen puentes desde un final de via hasta un lugar adecuado (otro final de via o un terreno elevado).
Para construir un puente: pulsa en la herramienta para seleccionarla (cambia el cursor a un puente), despues pulsa en un final de via (sera el inicio del puente) para construirlo.
Algunos obstaculos o la falta de un terreno adecuado pueden dar lugar a que el puente no se construya: construye via a ambos lados del emplazamiento del puente, y prueba de nuevo.
{Consejos: Usa la herramienta Eliminar para eliminar los puentes (pulsa en el inicio o final del puente) o alguno obstaculos que impiden la construccion del puente. Usa las distintas herramientas de ferrocarriles para conectar los lados del puente a tramos de via.}

Rail Tunnel: Herramientas para construir tuneles rectos, para vehiculos ferroviarios, entre dos puntos. Los tuneles no pueden estar en cuesta: los vehiculos entran y salen de ellos por accesos que estan a la misma altura.
La herramienta construye un tunel, en un final de via situado sobre una cuesta. Para construir un tunel: pulsa en la herramienta (el cursor cambia a un tunel),pulsa en un final de via situado sobre una cuesta para construir la entrada del tunel.
Si el tunel no puede encontrar una salida adecuada (una cuesta libre de obstaculos), el puente no se construira: construye via en ambos lados y prueba de nuevo.
{Consejos: Usa las Herramientas de Modelado del Terreno y la herramienta Eliminar para crear terreno adecuado para las entradas y salidas de tuneles. Usa las Herramientas de Ferrocarilles para conectar las salidas del tunel mediante vias.}

Señales: herramientas que construyen señales para ferrocarriles en una via. Las señales dirigen y regulan el flujo de los vehiculos en vias, puentes, cruces y Paradas (donde los vehiculos cargan y descargan pasajeros y mercancias).
Se pueden construir versiones de sentido unico y doble. Para construir una señal de doble sentido en una via: pulsa en la herramienta para seleccionarla (el cursor cambia a una señal); y despues pulsa en la via. Para construir una señal de sentido unico: pulsa de nuevo en el mismo punto, con el cursor de señal, para pasar de un tipo de señal a otro .
IMPORTANTE: Ten cuidado con no situar señales de sentido unico que impidan a los vehiculos llegar a su destino, por defecto los vehiculos conducen por la derecha (se puede cambiar en el archivo simuconf.tab).
- Señales: los vehiculos prosiguen solo si el tramo hasta la siguiente señal o destino en la Ruta (una Parada o punto de paso) no esta ocupado por otro vehiculo. En el modo de sentido unico los vehiculos solo van en una direccion.
- RailClose: los vehiculos no prosiguen mas alla de la señal (solo disponible en modo de sentido unico).
- PreSignals: los vehiculos prosiguen si el area (entre tres señales consecutivas o hasta el proximo destino en la Ruta) no esta ocupado por otro vehiculo. En el modo de sentido unico los vehiculos solo van en una direccion.
- ChooseSignals: dirigen los vehiculos a una estacion vacia, dentro de una Parada multiplataforma: un vehiculo que pasa por esta señal puede usar una estacion vacia como proximo destino, y no justamente el que se le ha asignado en su Ruta. Si no hay estaciones vacias o una ruta clara hasta el proximo destino, el vehiculo esperara en la señal.
{Consejos: elimina las señales con la herramienta Eliminar. Manten pulsado [Ctrl] para situar señales en puentes sobre vias.}

Cochera ferroviaria: herramienta para construir una cochera para comprar y gestionar trenes y vagones.
Las cocheras tiene un coste de mantenimiento y deben construirse sobre el final de una via.
Para construir una cochera ferroviaria: pulsa en la herramienta (el cursor cambia a una cochera), y pulsa sobre el final de una via.
{Consejos: Las cocheras solo muestran los vehiculos electricos si estan sobre una via electrificada. Elimina las cocheras con la herramienta Eliminar.}

Estaciones de tren:herramientas para construir estaciones, usadas por vehiculos sobre vias para cargar y descargar mercancias, pasajeros y correo.
Una estacion, cuando no se construye adyacente a una Parada existente creara una nueva Parada.
Las Paradas sobre vias tienen un coste de mantenimiento y se construyen sobre vias (pero no en curvas ni cruces).
Las Paradas sobre vias tienen un area de influencia para mercancias, pasajeros y correo. Las diferentes estaciones tienen distintas capacidades para pasajeros y correo. En la esquina de algunos iconos de herramientas hay un pequeño icono (usado en Lista de Estaciones e Informacion de la Parada) que muestra que elementos puede manejar la Parada.
Para construir una estacion: pulsa en la herramienta para seleccionarla (el cursor cambia a una estacion), y despues pulsa en la via.
{Consejos: Elimina las paradas en via mediante la herramienta Eliminar. Extiende las estaciones y construye estaciones multiples (construyendo varias secciones de estacion en vias adyacentes) para acomodar mas vehiculos y mas largos, incrementando la capacidad y el area de influencia para pasajeros y mercancias. Pulsa la tecla [v] para mostrar/ocultar el area de influencia de las paradas para pasajeros y mercancias. Manten pulsado [Ctrl] para construir puentes sobre vias.}.

Extensiones: herramientas para construir extensiones y edificios para Paradas, las cuales pueden incrementar su capacidad, area de influencia y coste de mantenimiento.
En la esquina de los botones de algunas herramientas hay un mini icono (usado en Lista de Estaciones e Informacion de la Parada) que muestra que elementos puede gestionar la Parada gracias a la extension.
Para construir una extension: pulsa en la herramienta para seleccionarla (cambia el cursor a una extension), y despues pulsa en el lugar deseado junto a una Parada ya existente. La nueva extension se considerara parte de la Parada.
{Consejos: Elimina las extensiones con la herramienta Eliminar.}

simutrans-124.5/simutrans/text/es/removal_tool.txt000066400000000000000000000045011520675460500224470ustar00rootroot00000000000000Herramienta Eliminar

Herramienta Eliminar

Herramienta Eliminar se usa para destruir y eliminar elementos no deseados y obstaculos que impiden construir durante la partida.

Uso: Pulsa el icono de la X roja (un icono de bulldozer en el pak128) en la barra de la parte superior de la pantalla o presiona [a] para abrir la herramienta Eliminar (cambia el cursor a una X sobre una nube de polvo blanca).
Posiciona el cursor, un cuadrado amarillo indica la posicion donde actuara la herramienta (las coordenadas del mapa estan indicadas en la barra de la parte inferior de la pantalla). Entoces, solo tienes que pulsar en el elemento para eliminarlo.

Se muestra el coste de la operacion y se suma a las cifras de Construccion. El coste de eliminacion es equivalente al coste de contruccion para los elementos del jugador.
El coste de destruir edificios de la ciudad se muestra como su valor en la ventana de dialogo que se abre son la herramienta de Exploracion. El coste de destruir arboles es 100 creditos Hajo.

Herramienta Eliminar puede ser usada para eliminar peatones y coches de ciudades.

El orden de eliminacion de los elementos de una cuadricula del juego es: peatones o coches privados, señaales, parada o estacion, tendido electrico, puente o tunel, via y finalmente carretera.

{Consejos: Para eliminar un vehiculo de un jugador, puedes venderlo desde Detalles del Convoy.
Para eliminar una Industria, Atracciones y Ayuntamiento primero cambia al jugador Servicio Publico. NOTA: el uso de la herramienta Eliminar sobre un Ayuntamiento elimina todos los edificios de la cuidad.
Usa [Ctrl] para usar la herramienta en un nivel superior.
Para eliminar elementos de otros jugadores , primero selecciona jugar como otro jugador first.
Para eliminar segmentos de carreteras o vias usa Eliminar vias (tambien la puedes encontrar en Herramientas de Tranvia) o Eliminar carreteras.}

simutrans-124.5/simutrans/text/es/roadtools.txt000066400000000000000000000246731520675460500217670ustar00rootroot00000000000000Ayuda de Herramientas de Carreteras

Herramientas de Carreteras

Herramientas de Carreteras sirven para construir una red transporte por carretera para mercancias y pasajeros. Puedes construir o eliminar: carreteras, puentes, tuneles, señalesm cocheras, extensiones y Paradas.Si estas jugando con cronologia, a medida que pase el tiempo apareceran mas opciones.

Pulsa en el icono del camion en la barra de la parte superior de la pantalla para abrir la barra de herramientas. Situa el cursor sobre los iconos de la barra (tras abrirla) para ver el nombre, los costes de construccion, los costes de mantenimiento entre parentesis, limite de velocidad maxima y longitud maxima.

Herramientas de Carreteras incluye, de izquierda a derecha:

Carreteras: herramientas para construir carreteras para vehiculos entre dos puntos. Las carreteras solo se pueden construir en cuestas, en la direccion de la cuesta y pueden no encontrar camino a traves de terreno escarpado, agua u obstaculos. Los nuevos tramos pueden usar algunos ya existentes en su camino.
Para construir carreteras: pulsa en la herramienta para seleccionarla (el cursor cambia a una carretera), despues pulsa en el terreno para establecer el punto de inicio (mostrara una excavadora y las coordenadas a la derecha, en la barra de la parte inferior de la pantalla), y pulsa de nuevo para establecer el punto de finalizacion.
{Consejos: Los diferentes tipos de carreteras se pueden conectar (siempre que no hayan sido construidas por algun otro jugador). Usa los puentes y tuneles para conectar carreteras a traves de terreno escarpado, o para evitar obstaculos. Usa las Herramientas del Terreno para cambiar el terreno, y habilitar caminos para las carreteras. Usa la herramienta Eliminar para eliminar partes individuales de la carretera o algunos obstaculos. Usa [Ctrl] al mismo tiempo para acceder a funciones extra. Pulsa la tecla [s] para seleccionar la ultima de las Herramientas de Carreteras usada.}

Remove roads: herramienta que elimina carreteras, cuando no hay vehiculos presentes, entre dos puntos (ATENCION: paradas, señales, puentes y tuneles entre ambos puntos tambien se eliminan). El uso de esta herramienta implica costes de construccion.
Para eliminar carreteras: pulsa en la herramienta (cambia el cursor a una cruz roja); despues pulsa en la carretera (situa una cruz roja en el punto seleccionado); por ultimo pulsa en un segundo punto de la carretera, para eliminar el tramo entre ambos puntos.
{Consejos: Cambia de jugador para poder borrar sus carreteras.}

Puente de carretera: herramientas para construir puentes rectos, para vehiculos de carretera, entre dos puntos de una carretera. Los puentes tiene una distancia maxima entre pilares.
Las herramientas construyen puentes desde un final de carretera hasta un lugar adecuado (otro Para construir un puente: pulsa en la herramienta para seleccionarla (cambia el cursor a un puente), despues pulsa en un final de carretera (sera el inicio del puente) para construirlo.
Algunos obstaculos o la falta de un terreno adecuado pueden dar lugar a que el puente no se construya: construye carretera a ambos lados del emplazamiento del puente, y prueba de nuevo.
{Consejos: Usa la herramienta Eliminar para eliminar los puentes (pulsa en el inicio o final del puente) o alguno obstaculos que impiden la construccion del puente. Usa las distintas herramientas de Carreteras para conectar los lados del puente a tramos de carretera.}

Tunel(carretera): Herramientas para construir tuneles rectos, para vehiculos de carretera, entre dos puntos. Los tuneles no pueden estar en cuesta: los vehiculos entran y salen de ellos por accesos que estan a la misma altura.
La herramienta construye un tunel, en un final de carretera situado sobre una cuesta. Para construir un tunel: pulsa en la herramienta (el cursor cambia a un tunel),pulsa en un final de carretera situado sobre una cuesta para construir la entrada del tunel.
Si el tunel no puede encontrar una salida adecuada (una cuesta libre de obstaculos), el puente no se construira: construye carretera en ambos lados y prueba de nuevo.
{Consejos: Usa las Herramientas de Modelado del Terreno y la herramienta Eliminar para crear terreno adecuado para las entradas y salidas de tuneles. Usa las Herramientas de Carretera para conectar las salidas del tunel mediante carreteras.}

Señales y barreras: herramientas para construir señales y barreras, para vehiculos de carretera. Las señales y barreras diriguen y regulan el flujo de vehiculos en carreteras y puentes , y en cruces y paradas (donde se cargan y descargan mercancias y pasajeros. Se pueden construir versiones de sentido unico o doble de algunas señales.
IMPORTANTE: Ten cuidado con no situar señales de sentido unico que impidan a los vehiculos llegar a su destino, por defecto los vehiculos conducen por la derecha (se puede cambiar en el archivo simuconf.tab).
- Traffic lights (semaforos): herramientas para construir semaforos para regular el rafico en los cruces, permitiendo el paso o haciendo esperar un rato. Para construir un semaforo: Pulsa en la herramienta para seleccionarla (el cursor cambia a una señal); y despues pulsa en la carretera. Pulsa de nuevo en el cruce para cambiar la orientacion de los semaforos.
- Barrera de sentido unico: herramienta que construye una barrera para forzar el uso de dicho tramo en un solo sentido. Para construir una barrera: Pulsa en la herramienta para seleccionarla (el cursor cambia a una barrera); y despues pulsa en la carretera. Pulsa de nuevo en el tramo para cambiar la orientacion de la barrera.
- road closed: herramienta que construye un señal que evita que continuen los vehiculos por ese tramo. Para construir una barrera: Pulsa en la herramienta para seleccionarla (el cursor cambia a una barrera), despues sobre un punto del tramo deseado.
- no entry: herramienta que construye una señal que permite la circulacion por un unico sentido de la carretera(similar a una barrera de sentido unico. Para construir una señal: Pulsa en la herramienta para seleccionarla (el cursor cambia a una señal); y despues pulsa en la carretera. Pulsa de nuevo en el tramo para cambiar la orientacion de la señal.
- minimum 80km/h herramienta que construye una señal que evita que continuen los vehiculos cuyo limite de velocidad maximo este por debajo de 80km/h. Para construir una señal: Pulsa en la herramienta para seleccionarla (el cursor cambia a una señal); y despues pulsa en la carretera. Pulsa de nuevo en el tramo para cambiar la orientacion de la señal.
- Choose Point: dirigen los vehiculos a una Parada vacia, dentro de una Parada multiplataforma: un vehiculo que pasa por esta señal puede usar una parada vacia como proximo destino, y no justamente el que se le ha asignado en su Ruta. Si no hay Paradas vacias o una ruta clara hasta el proximo destino, el vehiculo esperara en la señal. Para construir una señal: Pulsa en la herramienta para seleccionarla (el cursor cambia a una señal); y despues pulsa en la carretera. Pulsa de nuevo en el tramo para cambiar la orientacion de la señal.
- Autopistas: (solo disponible en el pak128) herramientas para construir una señal que solo permita el paso de vehiculos en una sola direccion, si su limite de maxima velocidad es mayor de 80km/h.
Para construir una señal: Pulsa en la herramienta para seleccionarla (el cursor cambia a una señal); y despues pulsa en la carretera. Pulsa de nuevo en el tramo para cambiar la orientacion de la señal.
{Consejos: Elimina las cocheras mediante la herramienta Eliminar. Usa [Ctrl] al mismo tiempo para situar señales en los puentes sobre carreteras.}

Deposito de carretera: herramienta para construir una cochera para comprar y gestionar vehiculos de carretera.
Las cocheras tiene un coste de mantenimiento y deben construirse sobre el final de una carretera.
Para construir un deposito de carretera: pulsa en la herramienta (el cursor cambia a una cochera), y pulsa sobre el final de una carretera.
{Consejos: Elimina las cocheras mediante la herramienta Eliminar.}

Car Parking: (solo disponible en el pak 128) herramientas para construir extensiones para Paradas las cuales incrementan su capacidad y area de influencia para pasajeros y mercancias, asi como el coste de mantenimiento.
Para construir una extension: pulsa en la herramienta para seleccionarla (el cursor cambia a extension), despues pulsa en el lugar deseado de la carretera, junto a una Parada existente. La nueva extension es considerada ahora parte de la Parada.
{Consejos: Elimina las extensiones con la herramienta Eliminar.}

Paradas de carretera: herramientas para construir lugares donde los tranvias y los vehiculos de carretera cargan y descargan pasajeros, mercancias y correo.
Una parada de carretera, cuando no se construye adyacente a una Parada existente creara una nueva Parada Las paradas de carretera se construyen sobre carreteras y tienen un coste de mantenimiento y una area de influencia para mercancias, correo y pasajeros. En la esquina de algunos iconos de herramientas hay un pequeño icono (usado en Lista de Estaciones e Informacion de la Parada) que muestra que elementos puede manejar la Parada. Las diferentes Paradas de carretera pueden tener distintas capacidades para pasajeros, mercancia y correo.
Para construir una Parada de carretera: pulsa en la herramienta para seleccionarla (el cursor cambia a una Parada de carretera), despues pulsa en el lugar deseado de la carretera,
{Consejos: Elimina las paradas de carretera mediante la herramienta Eliminar.Manten pulsado [Ctrl] para construir puentes sobre carreteras. Las paradas de carretera no se pueden construir sobre carreteras que pertenezcan a otros jugadores. Puedes crear Paradas multiples para incrementar el area de influencia y la capacidad de las mismas.}

simutrans-124.5/simutrans/text/es/save.txt000066400000000000000000000043331520675460500207060ustar00rootroot00000000000000Ayuda de Guardar

Guardar

Guardar almacena la partida actual en un fichero (para continuar jugando mas tarde) y puede ser usado para borrar partidas guardadas.

Guardar se abre desde Opciones de Juego o presionando [S]; y muestra una lista con nombre, fecha y hora de las partidas guardadas.
Si no son visibles todas las partidas: usa la barra de desplazamiento en la parte derecha de la lista para desplazar los nombres.

La partida actual puede ser salvada en un nuevo fichero o en un fichero ya existente.
Para salvar en un fichero nuevo, introduce el nombre en la caja de texto de la parte superior de la ventana Guardar: pulsa en la caja de texto, introduce el nombre y pulsa [Enter] o [Return] o pulsa en OK; el nuevo fichero se mostrara en la lista.
IMPORTANTE: Si se usa un fichero listado, la partida antigua en reemplazada, por la partida actual y se pierde.

Por defecto, la partida actual aparece en la caja de texto Archivo, para salvar esta ultima en ese fichero: pulsa en OK (los datos antiguos se pierden y son reemplazados por los datos de la partida actual).
Para salvar una partida en un fichero de una partida antigua: pulsa en el nombre dentro de la lista (la partida antigua es reemplazada por la partida actual).

Los nombres de fichero pueden ser una combinacion de letras, numeros y simbolos con una longitud maxima de 57 caracteres; despues de salvar, solo se muestran 20 caracteres en las ventanas Guardar y Cargar.

CUIDADO: Pulsando en el boton X BORRARA LA PARTIDA DE FORMA INMEDIATA Y PERMANENTE, el fichero se elimina de la lista y la ventana Guardar se cierra. Use esta funcion con cuidado.

{Consejos: Reinicia un juego salvado desde la ventana Cargar.
Puedes cambiar la opcion de auto-guardado (guardado automatico de la partida) en el fichero simuconf.tab}.

Pulsa en Cancelar para cerrar la ventana Guardar; o en el boton X en la esquina superior izquierda; o usa el teclado.

simutrans-124.5/simutrans/text/es/schedule.txt000066400000000000000000000116121520675460500215420ustar00rootroot00000000000000Controles de Enrutar

Controles de Enrutar

Controles de Enrutar establece el programa de ruta (la ruta y la cantidad minima de mercancias y pasajeros requerida por un Convoy para continuar ) de un Convoy o una Linea.
Lista las Paradas (donde los Convoys cargan y descargan mercancias y pasajeros) y puntos de paso (que pueden ser usados para dirigir Convoys por un camino si hay mas de uno disponible o dar puntos intermedios hasta el destino) con sus coordenadas del mapa.

Controles de Enrutar para un Convoy se abre desde el boton Enrutar en la ventana Informacion del Convoy y en los Controles del Deposito.
Los controles para un programa de ruta de una Linea se abren desde los botones Nueva linea o Actualizar linea en Administracion de Lineas y en los Controles del Deposito.

Cuando los Controles de Enrutar se han abierto desde la ventana Informacion de Convoy la linea asignada (si tiene alguna) se muestra en lo alto de los controles.
Nombre de la linea : Pulsa en los botones de flechas para pasar de una linea a otra o pulsa en la caja de texto para desplegar un lista de Lineas (pulsa en una Linea de la lista para seleccionar).
Pulsa en la caja de texto y escribe un nuevo nombre para cambiar el nombre de la Linea.

Cargar (Min): establece la cantidad minima de mercancias y pasajeros que requiere el Convoy para continuar. El porcentaje se refiere a la capacidad total del Convoy y no de un tipo de mercancia individual.
Un Convoy esperara en la Parada hasta que se carge el porcentaje requerido.
Para usarlo: pulsa en la Parada en la lista para seleccionar (> por nombre muestra que esa Parada ha sido seleccionada), entonces usa las flechas para cambiar el porcentaje.
{Consejos: para establecer o cambiar la Carga (Min) para todos los Convoys de una Linea, usa el boton Actualizar la linea en Administracion de Listas o en Controles del Deposito. Para cambiar ls Carga de un Convoy y no el resto de los de la Linea, usa la opcion Enrutar en Informacion del Convoy o en Controles de Deposito.}

Los botones de opcion (pulsa para usar, el boton se oscurece cuando se selecciona) incluyen:

Añadir Parada: esta opcion pone una Parada usada por vehiculos para cargar y descargar mercancias y pasajeros (los vehiculos de transporte maritimo pueden usar cualquier cuadricula de agua en el area de influencia del puerto) o un punto de paso en la ruta.
Para añadir una Parada o Punto de paso: pulsa en la opcion a seleccionar (cambia el cursor a un cartelito), entonces pulsa en el punto requerido para la Parada o Punto de paso (un punto en la carretera, via de tren o agua segun el tipo de vehiculo). La Parada o Punto de paso , es listada en Enrutar.
{Consejos: Los vehiculos no cargan ni descargan pasajeros ni mercancias en los Puntos de paso.}

Insertar: esta opcion pone una Parada o punto de paso en la ruta, antes que una Parada/Punto de paso ya existente.
Para insertar una Parada/Punto de paso: pulsa en una parada/punto de paso existente de la lista para seleccionar (se muestra > en el nombre de la parada/punto de paso seleccionado), despues pulsa en el boton Insertar (cambia el cursor a un cartelito), y finalmente pulsa en el punto requerido de un Parada/punto de paso (un punto de una carretera,via de tren o agua segun el tipo de vehiculo).
La nueva Parada/Punto de paso aparecera en la lista de Enrutar antes de la Parada/Punto de paso previamente seleccionado.
{Consejos: Los vehiculos no cargan ni descargan pasajeros ni mercancias en los Puntos de paso.}

Eliminar: esta opcion borra una Parada o Punto de paso de la ruta.
Pulsa en el boton Eliminar, y despues pulsa en un elemento de la lista para eliminarlo.

Viaje de vuelta: esta opcion añade las Paradas y Punto de paso existentes en orden inverso a la ruta. Los niveles de Carga (Min) tambien se copian. Pulsa en el boton de opcion para usar: Enrutar lista la nueva ruta.

Convertir en Linea: (solo disponible cuando se abre Enrutar desde la ventana Informacion de Convoy) creando una nueva Linea usando el programa actual de ruta. La nueva linea se le asigna un numero y se lista en Administracion de Lista.

simutrans-124.5/simutrans/text/es/shiptools.txt000066400000000000000000000146601520675460500220000ustar00rootroot00000000000000Ayuda de Herramientas de Barcos/Puertos

Herramientas de Barcos/Puertos

Herramientas de Barcos/Puertos construye una red de transporte maritima y fluvial. La herramientas pueden construir o eliminar: canales, esclusas, puertos varios, y astilleros . Si estas jugando con cronologia, a medida que pase el tiempo apareceran mas herramientas y opciones.

Pulsa en el boton de Barco en la parte superior de la pantalla para abrir la barra de herramientas.
Situa el cursor sobre los botones (despues de abrir o pulsar en la barra de herramientas) para ver el nombre y el coste: coste de construccion, entre parentesis el coste de mantenimiento maintenance cost in brackets, y el limite de velocidad.

Herramientas de Barcos/Puertos incluye, de izquierda a derecha:

Canal: construye canales, para barcos y barcazas, entre dos puntos.
Los canales solo se pueden construir en cuestas, en la direccion de la cuesta y puede no ser posible crear el camino si el terreno es escarpado. Los canales construidos entre puntos, pueden usar otros canales en su camino.
Para construir un canal: pulsa en la herramienta para seleccionar (el cursor cambia de forma, a un canal), despues pulsa en el terreno para el punto de inicio (muestra una bulldozer y las coordenadas del punto en la parte derecha de la barra inferior de la pantalla), y pulsa de nuevo para establecer el punto final.
{Consejos: Los canales construidos por diferentes jugadores no se conectan. Usa las Herramientas del Terreno para cambiar el terreno, para habilitar los caminos para canales. Usa la herramienta Eliminar para eliminar partes individuales del canal y algunos obstaculos. Deshacer la ultima ruta mediante la tecla [z] no reembolsa los costes de construccion.}

Remove channels: herramienta que elimina una parte del canal, cuando no hay vehiculos, entre dos puntos (ATENCION: Las paradas en el canal se eliminan tambien). La herramienta tiene asociados costes de construccion.
Para eliminar un canal: pulsa en la herramienta (el cursor cambia de forma, una cruz); despues pulsa en el canal a eliminar (en el punto seleccionado se muestra una cruz roja); y por ultimo pulsa en otro punto del canal, removiendo dicha parte del canal hasta el punto inicial.

Puente levadizo: herramienta que construy una esclusa y un puente levadizo para elevar/descender un barco o barcaza a otro nivel en un canal. El puente tiene un tamaño maximo y se construye al final del canal, en un lugar adecuado (otro canal en terreno elevado, sin limite).
Para construir un puente: pulsa en la herramienta para seleccionar puente levadizo (el cursor cambia de forma, un puente), pulsa en el final del canal. Algunos obstaculos o la falta de espacio adecuado para el final de puente puede hacer que no se construya: construye un canal a ambos lados para crear el puente levadizo, y prueba de nuevo.
{Tips: Use Destroy/Remove to remove bridges (click on hoist-end) & some obstructions to bridge building.
Use Canal tools to connect bridge-ends to water ways.}.

ChannelStop (Canal Quay en pak128) herramienta que construye un puerto en un canal para transporte fluvial, cargando y descargando mercancias y pasajeros.
La Parada del Canal, cuando no es contruida adyacente a una Parada existente, creara una nueva parada.
La Parada del Canal tiene un area de influencia para mercancias, pasajeros y correo. La esquina del icono de la herramienta es un mini icono que (usado en la Lista de Estaciones y en la ventana de Informacion de la Parada) muestra que elementos permite manejar la Parada del Canal, a una parada.
Para construir un Parada de Canal: pulsa en la herramienta (el cursor cambia de forma, una Parada del Canal); despues pulsa en un canal para cosntruir.
{Consejos: Elimina las Paradas de Canal con la herramienta Eliminar. Pulsa la tecla [v] para mostrar/ocultar el area de influencia para mercancias y pasajeros.
La paradas para vehiculos maritimos y fluviales no se listan en la Lista de Paradas/Estaciones}.

Ship-stops/Puertos: herramientas que construyen varios elementos de puertos para vehiculos maritimos y fluviales, para descargar y cargar mercancia y pasajeros.
Los puertos se construyen en los bordes de la costa.
Un puerto, cuando no es construido adyacente a una Parada existente, creara una nueva parada.
La Parada del Canal tiene un area de influencia para mercancias, pasajeros y correo. La esquina del icono de la herramienta es un mini icono que (usado en la Lista de Estaciones y en la ventana de Informacion de la Parada) muestra que elementos permite manejar el puerto, a una parada. Los diferentes puertos pueden tener diferente capacidad para mercancias, pasajeros y correo.
Para construir un puerto: pulsa en la herramienta seleccionada (el cursor cambia de forma, un puerto) y despues pulsa en una zona de la costa, cerca del agua
{Consejos: Elimina los puertos mediante la herramienta Eliminar.
El tamaño de los puertos se puede aumentar, construyendo en cuadriculas adyacentes. Los transportes maritimos pueden usar cualquier cuadricula del agua dentro del area influencia de un puerto.
Las Paradas maritimas no aparecen en la Lista de Estaciones/Paradas.
La Industria maritima tal como Plataformas Petroliferas no requieren un puerto, los vehiculos maritimos pueden usar cualquier cuadricula dentro del area de influencia de una Industria maritima.
Pulsa la tecla [v] para mostrar/ocultar el area de influencia para mercancias y pasajeros.}

Astillero: herramienta que construye un astillero para comprar y administrar barcos, barcas y barcazas. Los Astilleros tiene costes de mantenimiento y se construyen en el agua, cerca de la costa.
Para construir un Astillero: pulsa en la herramienta (el cursor cambia de forma, un astillero), y despues pulsa en el agua, en un lugar cercano a la costa.
{Consejos: Elimina los Astilleros mediante la herramienta Eliminar.}

simutrans-124.5/simutrans/text/es/simutrans.txt000066400000000000000000000045151520675460500217770ustar00rootroot00000000000000Simutrans

Bienvenido a Simutrans...

Eligue uno, entre los miles, de los terrenos de Simutrans. Despues ponte en la piel de un joven emprendedor con algo de dinero de la herencia de tus abuelos: se ambicioso para que se sientan orgullosos, con ansias de crear una exitosa empresa de transportes, tu desafio es crear una empresa prospera.

A medida que pasa el tiempo, deberas convertir tu empresa en un imperio del transporte, manteniendo la contabilidad sobre ruedas gracias a la entrega de la carga y a la satisfaccion de los deseos de viajar de millones de pasajeros cada mes.
Transporta pasajeros, correo y mercancias por tren, carretera, mar e incluso por aire.

Cada terreno tiene algo diferente que ofrecer. Echa un vistazo a los alrededores...su futuro esta en tus manos y las de tus competidores.

Como comienzo, puede ser mas facil empezar suministrando mercancias a la Central Energetica. Despues, conecta otras fabricas, tal vez usando paradas intermedias donde cargar las mercancias.
La desventaja de transportar solo mercancias es que los pueblos y ciudades no creceran y no se crearan nuevas industrias.

Alternativamente, puedes ir a por el transporte de pasajeros (y correo). Esto es un desafio mayor, ya que los habitantes de este mundo quieren viajar a las localizaciones que ellos han eleguido.
Algunos necesitan ir a las fabricas para trabajar, otros quieren ir a comprar a centros comerciales en la ciudad principal y otros quieren hacer turismo.

Si no les das una conexion a su destino, no viajaran contigo de ninguna forma. Necesitaras construir redes de transporte. Cuando hayas establecido dichas redes, necesitaras actualizarlas para ajustarlas a la demanda. Asi que planifica un poco antes.
Si a los pasajeros les gusta tu servicio, las cuidades creceran y se crearan nuevas industrias, y solo asi la economia ira sobre ruedas.

Puedes jugar tanto como quieras . Empieza en 1880 y termina en 2050, si lo deseas. En cada epoca tendras disponibles nuevos vehiculos y edificios.
Juega contra la IA o tu solo. Solo si estas en numeros rojos durante tres meses, llegaras a la bancarrota y el juego terminara.

El Equipo de Simutrans y la Comunidad de usuarios desean que pases un buen rato aqui en Simutrans...

simutrans-124.5/simutrans/text/es/slopetools.txt000066400000000000000000000040141520675460500221470ustar00rootroot00000000000000Ayuda de Herramientas del Terreno - elevar/bajar el terreno

Herramientas del Terreno

Herramientas del Terreno eleva y baja el terreno y puede ser usado para crear lugares adecuados para la construccion.

Pulsa en el icono del terreno en cuesta, en la parte superior de la pantalla para abrir la barra de herramientas.
Pon el cursor encima, despues de abrir o pulsar en la barra de herramientas, sobre una herramienta para ver el nombre y los costes de construccion.

Las herramientas se utilizan en areas vacias con obstrucciones (e.g. carretras, vias, edificios, paradas y estaciones).

Pulsa en la herramienta para seleccionar, el cursor cambiara, despues posiciona el cursor en el terreno y pulsa en el mismo para usar:

Elevar terreno/Rebajar terreno: eleva/rebaja el terreno
El cursor es una flecha hacia arriba o hacia abajo.
Las herramientas tambien se pueden abrir pulsando [u] y [d].

Pendientes: crea una pendiente, en la direccion indicada por la herramienta. Se puede crear una superficie vertical durante el proceso.
El cursor el un cubo amarillo solo con lineas que posiciona la pendiente.

Cuadrado elevado/rebajado: rebaja/eleve un cuadrado de terreno especifico. Se puede crear una superficie vertical durante el proceso.
El cursor el un cubo amarillo solo con lineas que selecciona la pendiente.

Restaurar pendiente natural: devuelve el terreno a su estado natural, eliminando las superficies artificiales verticales durante el proceso.
El cursor el un cubo amarillo solo con lineas que posiciona la pendiente.

{Consejo: Las herramientas del Terreno son faciles de usar con la cuadricula, presiona [#] y acerca el zoom.
Usa la herramienta Eliminar para eliminar algunos obstaculos.
Dependiendo del mapa, las herramientas puede elevar el terreno dentro o fuera del agua.}

simutrans-124.5/simutrans/text/es/sound.txt000066400000000000000000000016161520675460500211010ustar00rootroot00000000000000Ayuda sobre Ajustes de Sonido

Ajustes de Sonido

Ajustes de Sonido tiene controles para los efectos de sonido y la musica.

Ajustes de Sonido se abre desdeOpciones de Juego.

Use las flechas para ajustar los parametros o la barra de desplazamiento para subir/bajar el volumen:

Volumen de los efectos: establece el volumen de los efectos de sonido en el juego.

Volumen de la Musica: establece el volumen de la musica. Si no esta disponible, el control no funcionara.

Jugadores actuales: cambia la musica; las flechas saltan a la siguiente/anterior cancion entre las disponibles.

ATENCION: El juego no incluye archivos de musica.
Ayuda a crear musica para el juego en http://wiki.simutrans.com

simutrans-124.5/simutrans/text/es/special.txt000066400000000000000000000152531520675460500213730ustar00rootroot00000000000000Ayuda de Herramientas Especiales de Construccion

Herramientas Especiales de Construccion

Herramientas Especiales de Construccion construye elementos que no tienen relacion con el trafico y tambien permite jugar como otro jugador en el juego.

Pulsa en el icono de punto rojo dentro del circulo amarillo (el icono de la grua en el pak128) en la barra de la parte superior de la pantalla para abrir la barra de herramientas.
Situa el cursor sobre cada icono (despues de abrir o pulsar en la barra de herramientas) para ver el nombre y los costes de construccion.

Herramientas Especiales de Construccionincluye, de derecha a izquierda:

Post Office: herramientas para construir una oficina postal que permite a una Parada manejar correo. Las oficinas postales incrementan la capacidad y el area de influencia de una Parada y acarrean un coste de mantenimiento.
Para construir una oficina postal: pulsa en la herramienta seleccionada, despues pulsa en una para existente. La nueva oficina postal se considerara parte de la Parada.
{Consejos: Elimina las oficinas postales y los obstaculos mediante la herramienta Eliminar.}

Extensiones de Estacion: herramientas para construir edificios y extensiones para Paradas. Las extensiones pueden incrementar la capacidad y el area de influencia para pasajeros y mercancias, asi como el coste de mantenimiento. En la parte superior de algunos botones de herramienta aparece un mini icono (usado en la Lista de Estaciones y en la ventan de Informacion de la Parada) que muestra que elemento puede gestionar la parada, gracias a la extension.
Para construir una extension: pulsa en la herramienta seleccionada (el cursor cambia de forma, un extension), despues pulsa en la posicion deseada, junto a una parada existente. La nueva extension sera considerada parte de la Parada.
{Consejos: Elimina las extensiones con la herramienta Eliminar.}

P+ Cambiar de Jugador: permite jugar como otro jugador si la opcion Permitir cambio de jugador fue seleccionada en el comienzo de la partida. Las opciones extra estan disponibles jugando como jugador servicio publico.
Pulsa en la herramienta (o pulsa la tecla [P]) para cambiar de jugador. Aparecera un mensaje de confirmacion, notificando que el cambio de jugador: pulsa en la x de la parte superior de la ventana del mensaje o pulsa Esc para cerrar el mensaje.

Fundar nueva ciudad: herramienta que establece una nueva ciudad.
Para fundar una nueva ciudad: pulsa en la herramienta o pulsa la tecla [C] (el cursor cambia de forma, un ayuntamiento), y despues pulse en la posicion deseada. La nueva ciudad cuesta 5 milliones de credits y sera construida sin requerir confirmacion.
{Consejos: Pulsa en el ayuntamiento con la herramienta Eliminar para eliminar los edificios de la ciudad.}

Plantar un arbol: herramienta que planta un arbol.
Para plantar un arbol: pulsa en la herramienta seleccionada (el cursor cambia de forma, un arbol), y despues pulsa en la posicion deseada.
{Consejos: Elimina arboles con la herramienta Eliminar.}

Construir tendido electrico: herramienta que construye tendido electrico entre subestaciones transformadoras, centrales electricas e industrias (para aumentar la productividad). El suministro de energia electrica genera ingresos. Las lineas de tendido electrico no se pueden cruzar en diagonal con vias ni carreteras.
Para construir una linea de tendido electrico: pulsa en la herramienta o pulsa la tecla [l] (el cursor cambia de forma, un torre de tendido electrico), y despues pulsa en una subestacion transformadora situada junto a una central electrica. Por ultimo pulsa en la subestacion transformadora de una industria para suministrar energia electrica, para aumentar la produccion.
{Consejos: El tendido electrico se puede construir por etapas, pulsando en los lugares deseados del terreno. Elimina el tendido electrico mediante la herramienta Eliminar.}

Subestacion transformadora herramienta que construye una subestacion transformadora en centrales electricas o en industrias para conectar las lineas de tendido electrico.
Para construir una subestacion: pulsa el icono o la tecla [g] (el cursor cambia de forma, un circulo amarillo con un punto rojo), despues pulsa en el lugar deseado cerca de una central electrica o una industria.
Las subestaciones construidas junta a Centrales deben estar conectadas con otras subestaciones de industrias mediante tendido electrico para poder suministrar energia.
Si no se suminustra electricidad, la subestacion mostrara un rayo amarillo, cuando haya suministro aparecera un rayo rojo.
{Consejos: Las industrias y las Centrales electricas pueden tener varias subestaciones para incrementar la capacidad de suministro. Elimina las subestaciones mediante la herramienta Eliminar.}

Marcador: herramienta que abre los controles para situar un marcador con texto personalizado que puede usarse para mover la vista del juego a diferentes puntos.
Pulsa el icono o la tecla [M], el cursor cambiara a un marcador de texto, despues pulsa en el lugar deseado para abrir los controles del Marcador. En la parte superior de los controles estan las coordenadas de la posicion seleccionada en el mapa, ademas de una caja de texto vacia, introduce el texto deseado en la caja de texto (pulsa en la caja, escribe el texto y pulsa en OK ,[Enter] o [Return]) para situar el marcador. El nuevo marcador se mostrara en el juego y se listara en los controles de Marcador (junto con las coordenadas) cuando reabras los controles.
Para mover la vista del juego a un marcador: Abre los controles de Marcador y pulsa en la posicion deseada dentro de la lista.
Para eliminar un Marcador: pulsa en la herramienta Marcador o la tecla [M], el cursor cambiara a un marcador de texto; despues pulsa en las coordenadas del marcador a borrar; finalmente pulsa en Remove dentro de los controles de Marcador.
Para cerrar los controles de marcador: pulsa en la X de la parte superior izquierda de la ventana de los controles; o pulsa Cancel; o usa el teclado.
{Consejos: Las coordenadas del cursor se muestran en la parte izquierda de la barra inferior. Usa [!] para mostrar los textos de los marcadores en la vista del juego}

simutrans-124.5/simutrans/text/es/station.txt000066400000000000000000000200221520675460500214220ustar00rootroot00000000000000Informacion de la Parada

Informacion de la Parada

Informacion de la Parada da informacion acerca de una Parada (donde los vehiculos cargan y descargan mercancias y pasajeros). Tambien tiene controles para acceder a mas opciones y cambiar el nombre de la Parada.

Una Parada consiste en un lugar donde un Convoy puede cargar y descargar pasajeros y mercancias y una serie de edificios de extension.

Las herramientas para construir los lugares donde los Convoys pueden cargar o descargar estan disponibles en la respectiva barra de herramientas segun el tipo de transporte: estaciones/plataformas; paradas de monorail/maglev; Paradas de Tranvia; Paradas de carretera; Muelles ; y Aeropuertos.
{Consejos: los transportes maritimos pueden usar cualquier cuadricula en el agua dentro del area de influencia de un muelle, para cargar y descargar mercancias y pasajeros. Se pueden construir paradas mas grandes construyendo en cuadriculas adyacentes. Las paradas pueden construirse para uso de mas de un tipo de transporte}.

Pulsa en una Parada con la Herramienta Explorar o pulsa en una Parada listada en la Lista de Estaciones o en Administracion de Lineas para abrir la ventana Informacion de la Parada que muestra el nombre de la Parada como titulo de la ventana.
Informacion de la Parada contiene una caja de texto con el nombre; un mini vista de la Parada; informacion acerca de la Parada; botones de opcion; y una lista de todos los elementos preparados para transportar en la Parada (si no son visibles todos los elementos ajusta el tamaño de la ventana Informacion de la Parada o usa la barra de desplazamiento):

Nombre: nombre asignado a la Parada, que se muestra en una caja de texto en la parte superior de la ventana Informacion de la Parada.
Por defecto, el nombre se asigna automaticamente cuando se construye por primera vez y depende de la ciudad relacionada con la Parada y cualquier industria dentro del area de influencia de la Parada.
Para cambiar el nombre: pulsa en la caja de texto del nombre y teclea uno nuevo.
{Consejos: Usa la tecla [!] para mostrar la vista del nombre sobre la Parada. Usa la tecla [v] para mostrar el area de influencia de la Parada. La opcion de numerar las paradas esta disponible en el archivo simuconf.tab}

barra de estado de color: los colores indican el estado operativo de la Parada en relacion a como de abarrotada esta. La barra de estado de color se usa tambien en la Lista de Estaciones y en Administracion de Lista, y se muestra el mismo color en la barra sobre la Parada:
- amarillo: fuera de servicio.
- verde: no se necesitan mejoras.
- naranja: las mejoras son posibles.
- rojo: se recomiendan mejoras.
{Consejos: Usa la tecla [!] para mostrar la barra de estado de color(indica como de saturada esta la Parada) sobre la Parada.}

Iconos graficos tambien usado en la Lista de Estaciones y Administracion de Lista:
- icono(s) de carga indica que elementos (pasajeros, mercancias y correo) puede manejar una Parada.
{Consejos: añadiendo las extensiones apropiadas se puede cambiar la categoria de las mercancias que puede manejar una Parada. Las Oficinas de Correo se pueden añadir a las paradas para manejar correo}.
- icono(s) de vehiculos indica que tipos de vehiculos pueden usar la Parada. Los iconos incluidos: autobus (para pasajeros), camion (para mercancias), tren (por via ferrea), barco y avion. Los tranvias se indican mediante el icono del autobus o del tren dependiendo del tipo de Parada.

Capacidad de almacenamiento: cantidad de carga de cada tipo (mercancias, pasajeros y correo) que se puede almacenar en la Parada.

Dibujo de la Parada: es una mini vista de la Parada y cualquier convoy visible.
Pulsa en el dibujo para centrar la vista del juego en la Parada.

Pasajeros cifras que indican la cantidad de pasajeros que inician su viajes en dicha Parada:
- cara feliz indica el numero de pasajeros, que han llegado a una parada no saturada como resultado de su area de influencia, han encontrado una ruta a su destino y esperan transporte.
- cara triste indica el numero de pasajeros que han llegado a una Parada como resultado de su area de influencia, pero no esperan transporte porque la Parada esta Saturada.
- sin ruta indica el numero de pasajeros que han llegado a una Parada como resultado de su area de influencia, pero no encuentran una ruta a su destino.
{Consejos: Mantener a los pasajeros contentos ayuda al crecimiento de las ciudades. Se puede cambiar el numero maximo de intercambios permitidos para los pasajeros y las mercancias en el archivo simuconf.tab}.

Pasajeros/mercancia esperando: lista la carga que esta esperando por transporte en la Parada por grupos: pasajeros; correo; y tipo de mercancias.
La informacion de la carga que se muestra incluye cantidad, tipo, destino final y primera Parada intermedia.
Los botones de opcion (el nombre cambia cuando lo pulsas) ordena la carga en gtupossorts of items within groups:
- destino: ordena elementos, alfanumericamente (mayusculas antes que minusculas), por el nombre de la Parada destino final.
- cantidad: ordena elementos por cantidad en orden descendente.
- via (detalle): ordena elementos, alfanumericamente (mayusculas antes que minusculas), por el nombre de la primera Parada intermedia.
- via (cantidad): ordena elementos por cantidad dirigida a la primera Parada intermedia.
{Consejos: las barras de color de mercancias sobre la Parada (usa la tecla [!] para mostrar) indica la cantidad de elementos que esperan transporte y tienen el mismo color que los cuadrados de color en la Lista de Mercancias.}

Grafica: pulsa en el boton de opcion para mostrar el grafico (el boton se oscurece cuando el grafico esta visible) en la ventana Informacion de la Parada.
La grafica muestra las estadisticas de los ultimos 12 meses (eje x) cuando la opcion grafico esta seleccionada.
Pulsa en el boton grafico para ver la informacion en el grafico ((el boton se oscurece cuando el grafico esta visible).
El color de las lineas en el grafico se corresponde con el color de los botones de opcion:
- Felices indica el numero de pasajeros, que han llegado a una parada no saturada como resultado de su area de influencia, han encontrado una ruta hacia su destino y esperan transporte.
- Descontentos indica el numero de pasajeros, que han llegado a una parada como resultado de su area de influencia pero no esperan transporte porque estaba saturadad.
- Sin ruta indica el numero de pasajeros, que han llegado a una parada como resultado de su area de influencia pero no encuentran una ruta hacia su destino .
- En espera: cantidad de pasajeros y mercancias que esperan transporte en una Parada.
- Llego: cantidad de pasajeros y mercancias descargadas por un Convoy en una Parada que es destino final o intermedio.
- Partio: cantidad de pasajeros y mercancias cargadas por un Convoy en una Parada.
- Vehiculos: cantidad de vehiculos que han pasado por la Parada.

Detalles: boton de opcion que abre la ventana Detalles de la Parada y da mas informacion acerca de la Parada.

simutrans-124.5/simutrans/text/es/station_details.txt000066400000000000000000000032271520675460500231370ustar00rootroot00000000000000Detalles de la Parada

Detalles de la Parada

Detalles de la Parada da mas informacion acerca de una Parada (donde los vehiculos cargar y descargan pasajeros y mercancias).

Detalles de la Parada se abre desde el boton Detalles en la ventana de Informacion de la Parada.
Si no son visibles todos los elementos ajusta la ventana Detalles de la Parada o usa la barra de desplazamiento para desplazar los nombres)

La informacion listada incluye:
Industrias conectadas: nombres y coordenadas del mapa (entre parentesis) de industrias en el area de influencia de la Parada.
{Consejos: Usa [v] para mostrar el area de influencia de las paradas.}

Industrias cercanas demandan: tipos de mercancias requeridas por las industrias dentro del area de influencia de la Parada.

Lineas sirviendo a esta parada: Lineas que usan esta Parada.
Las lineas son listadas en orden alfanumerico (mayusculas antes que minusculas).

Paradas conectadas directamente :lista los nombres de Paradas que pueden se alcanzadas sin realizar un transbordo o cambio del tipo de mercancias o pasajeros.
Las paradas son listadas en orden alfanumerico (mayusculas antes que minusculas) en el caso de tipo de transporte, nombre de la Parada, y tipo de elemento que puede ser descargado (se muestra tambien para cada Parada).

simutrans-124.5/simutrans/text/es/tramtools.txt000066400000000000000000000217451520675460500220020ustar00rootroot00000000000000Ayuda de Herramientas de Tranvia/tren ligero

Herramientas de Tranvia/tren ligero

Herramientas de Tranvia/tren ligero contruye una red de transporte con tranvias. Las herramientas pueden construir: vias de trnvia (y electrificarlas o eliminarlas), señales, cocheras de tranvia, y paradas de tranvia. Si estas jugando con cronologia, a medidad que pase el tiempo iran apareciendo nuevas opciones.

Pulsa en el icono del tranvia en la barra de la parte superior de la pantalla para abrir la barra de herramientas.
Situa el cursor sobre los iconos (tras abrir la barra de herramientas) para ver el nombre y demas datos: costes de construccion, y limite de velocidad maxima .

Herramientas de Tranvias/tren ligero incluye, de izquierda a derecha :

Vias de tranvia: herramientas para construir vias, para vehiculos que van por railes entre dos puntos.
Las vias de tranvia se pueden construir sobre carreteras y en ciudades, pero no en areas ocupadas por otros edificios. Las vias solo se pueden construir en cuestas en la misma direccion de la cuesta y no sobre terreno escarpado, agua u obstaculos. Las nuevas vias pueden usar tramos de vias ya existentes en su camino.
Para construir una via: pulsa en la herramienta para seleccionarla (el cursor cambia a una via); despues pulsa en el terreno para establecer el punto de partida de la via (se muestra una excavador y las coordenadas del punto en la barra de la parte inferior de la pantalla); y por ultimo pulsa en el terreno para establecer el punto final del tramo de via.
{Consejos: Los diferentes tipos de via se pueden conectar (siempre que no pertenezcan a otros jugadores). Usa los puentes y tuneles para conectar vias a traves de terreno escarpado o para evitar obstaculos. Usa la herramienta Eliminar para eliminar partes individuales de un tramo de via asi como obstaculos. Manten pulsado [Ctrl] al mismo tiempo para obtener funciones extra. Deshacer [z] no reembolsa los costes de construccion.}

Via electrificada: herramientas para electrificar vias, puentes o tuneles entre dos puntos, para uso de vehiculos electricos.
Para electrificar una via: pulsa en la herramienta para seleccionarla o la tecla [e] (el cursor cambia a un icono de un rayo), despues pulsa en una via para establecer el punto de inicio de la electrificacion (situa un icono de via electrificada en dicho punto), y por ultimo pulsa en otro punto para establecer el punto final, electrificando el tramo entre ambos puntos.
{Consejos: Usa la herramienta Eliminar para quitar la electrificacion de la via.}

Remove Tracks: herramienta que elimina vias normales y electrificadas, cuando no hay vehiculos presentes, entre dos puntos (las estaciones, paradas, señales, tuneles y puentes en el camino tambien se eliminan). El uso de esta herramienta conlleva costes de construcion.
Para eliminar un via: pulsa en la herramienta para seleccionarla (el cursor cambia a una cruz roja); despues pulsa en la via que vas a borrar (en el punto seleccionado se muestra una cruz roja); y por ultimo pulsa en un segundo punto que este conectado con el primero, eliminando el tramo de via entre ambos.
{Consejos: Deberas cambiar de jugador para eliminar vias de otros jugadores.}

Señales: herramientas que construyen señales para vehiculos sobre railes en una via. Las señales dirigen y regulan el flujo de los vehiculos en vias, puentes, cruces y Paradas (donde los vehiculos cargan y descargan pasajeros y mercancias).
Se pueden construir versiones de sentido unico y doble. Para construir una señal de doble sentido en una via: pulsa en la herramienta para seleccionarla (el cursor cambia a una señal); y despues pulsa en la via. Para construir una señal de sentido unico: pulsa de nuevo en el mismo punto, con el cursor de señal, para pasar de un tipo de señal a otro .
IMPORTANTE: Ten cuidado con no situar señales de sentido unico que impidan a los vehiculos llegar a su destino, por defecto los vehiculos conducen por la derecha (se puede cambiar en el archivo simuconf.tab).
- Señales: los vehiculos prosiguen solo si el tramo hasta la siguiente señal o destino en la Ruta (una Parada o punto de paso) no esta ocupado por otro vehiculo. En el modo de sentido unico los vehiculos solo van en una direccion.
- RailClose: los vehiculos no prosiguen mas alla de la señal (solo disponible en modo de sentido unico).
- PreSignals: los vehiculos prosiguen si el area (entre tres señales consecutivas o hasta el proximo destino en la Ruta) no esta ocupado por otro vehiculo. En el modo de sentido unico los vehiculos solo van en una direccion.
- ChooseSignals: dirigen los vehiculos a una estacion vacia, dentro de una Parada multiplataforma: un vehiculo que pasa por esta señal puede usar una estacion vacia como proximo destino, y no justamente el que se le ha asignado en su Ruta. Si no hay estaciones vacias o una ruta clara hasta el proximo destino, el vehiculo esperara en la señal.
{Consejos: elimina las señales con la herramienta Eliminar. Manten pulsado [Ctrl] para situar señales en puentes sobre vias.}

Cochera de Tranvia: herramienta para construir una cochera para comprar y gestionar tranvias.
Las cocheras tiene un coste de mantenimiento y deben construirse sobre el final de una via.
Para construir una cochera de tranvia: pulsa en la herramienta (el cursor cambia a una cochera), y pulsa sobre el final de una via.
{Consejos: Las cocheras solo muestran los vehiculos electricos si estan sobre una via electrificada. Elimina las cocheras con la herramienta Eliminar.}

Paradas sobre vias: herramientas para construir estaciones, usadas por vehiculos sobre vias para cargar y descargar mercancias, pasajeros y correo.
Una estacion, cuando no se construye adyacente a una Parada existente creara una nueva Parada.
Las Paradas sobre vias tienen un coste de mantenimiento y se construyen sobre vias (pero no en curvas ni cruces).
Las Paradas sobre vias tienen un area de influencia para mercancias, pasajeros y correo. Las diferentes estaciones tienen distintas capacidades para pasajeros y correo. En la esquina de algunos iconos de herramientas hay un pequeño icono (usado en Lista de Estaciones e Informacion de la Parada) que muestra que elementos puede manejar la Parada.
Para construir una estacion: pulsa en la herramienta para seleccionarla (el cursor cambia a una estacion), y despues pulsa en la via.
{Consejos: Elimina las paradas en via mediante la herramienta Eliminar. Extiende las estaciones y construye estaciones multiples (construyendo varias secciones de estacion en vias adyacentes) para acomodar mas vehiculos y mas largos, incrementando la capacidad y el area de influencia para pasajeros y mercancias. Manten pulsado [Ctrl] para construir puentes sobre vias.}

Car Parking: (solo disponible en el pak 128) herramientas para construir extensiones para Paradas las cuales incrementan su capacidad y area de influencia para pasajeros y mercancias, asi como el coste de mantenimiento.
Para construir una extension: pulsa en la herramienta para seleccionarla (el cursor cambia a extension), despues pulsa en el lugar deseado de la carretera, junto a una Parada existente. La nueva extension es considerada ahora parte de la Parada.
{Consejos: Elimina las extensiones con la herramienta Eliminar.}

Paradas de carretera: herramientas para construir lugares donde los tranvias y los vehiculos de carretera cargan y descargan pasajeros, mercancias y correo.
Una parada de carretera, cuando no se construye adyacente a una Parada existente creara una nueva Parada Las paradas de carretera se construyen sobre carreteras y tienen un coste de mantenimiento y una area de influencia para mercancias, correo y pasajeros. En la esquina de algunos iconos de herramientas hay un pequeño icono (usado en Lista de Estaciones e Informacion de la Parada) que muestra que elementos puede manejar la Parada. Las diferentes Paradas de carretera pueden tener distintas capacidades para pasajeros, mercancia y correo.
Para construir una Parada de carretera: pulsa en la herramienta para seleccionarla (el cursor cambia a una Parada de carretera), despues pulsa en el lugar deseado de la carretera,
{Consejos: Elimina las paradas de carretera mediante la herramienta Eliminar.Manten pulsado [Ctrl] para construir puentes sobre carreteras.}

simutrans-124.5/simutrans/text/es/window.txt000066400000000000000000000127571520675460500212700ustar00rootroot00000000000000Ayuda de Interfaz/Ventana del juego

Interfaz/Ventana del juego

Simutrans se juega mediante una Interfaz/Ventana del juego que da informacion acerca de la partida actual; acceso a los controles y herramientas del juego; y una vista del mundo de la partida actual.

Barra de titulo: la parte izqierda de la barra de titulo de la Interfaz/Ventana del juego muestra la version y la fecha de creacion de la version de Simutrans que estas usando.
La parte derecha de la barra de titulo tambien tiene controles para minimizar, maximizar la Interfaz/Ventana del juego y tambien para salir de Simutrans. Esta barra se muestra solo cuando no estas jugando en modo pantalla completa

Iconos: una fila de iconos, usados para acceder a las herramientas y controles del juego mientras estas jugando a Simutrans, Esta justo debajo de la barra de titulo. Los iconos (pulsa para usarlos) estan agrupados en tres conjuntos:

- Los iconos del Menu principal, que incluyen:
- Disco floppy: abre las Ociones de juego
- Mapa: abre el Mapa
- Lupa selecciona la herramienta Explorar
- Icono de la cuesta: abre las herramientas del Terreno
- Tren: abre las herramientas de trenes y vias
- Monorail/maglev: abre la barra de herramientas de monorail
- Tranvia: abre la barra de herramientas de tranvia
- Camion: abre la barra de herramientas de transporte por carretera
- Barco: abre la barra de herramientas de transporte maritimo
- Avion: abre la barra de herramientas de transporte aereo
- Punto rojo en circulo amarillo (icono de grua en el pak128): abre la barra de herramientas especiales
- Cruz roja (icono de bulldozer en pak128): selecciona la herramienta Eliminar

- Los iconos de Administracion, que incluyen:
- Icono de lineas: abre el menu de Administracion de Lineas
- Icono de listas: abre el menu de Listas
- Icono del buzon: abre el menu del Centro de Mensajes
- Icono del dinero: abre el menu de Finanzas

- Los iconos de Otras opciones, que incluyen:
- Icono de la camara: captura la imagen actual de la Interfaz/Ventana del juego (sin la barra de titulo) y la salva en formato bmp en el directorio ...simutrans/screeshot/
{Consejo: Tambien se puede hacer un captura pulsando la tecla [c].}
- Icono de la taza de cafe: el juego entra/sale del modo pausa.
{Consejo: Pulsando tambien la tecla [p] puedes pausar el juego}.
- Icono >> : activa/desactiva el modo Acelerar. (cambia la escala en la que el tiempo , T, transcurre en el juego).
{Consejos: Acelerar se puede activar /desactivar pulsando la tecla [W]
IMPORTANTE: El juego puede no ejecutarse de forma fluida;el menu de Pantalla indica si tu ordenador puede soportar ese rendimiento.}
- ?: abre el menu de la Ayuda de Simutrans

Una parte de la vista del juego del mundo actual se muestra debajo de los Iconos.
Puede hacer zoom de la vista y tambien desplazarla para ver el resto de partes del mundo actual usando el raton o el teclado.

barra de mensajes temporales aparece y muestra mensajes en la parte inferior de la vista del juego.
{Consejo: Pulsa en el mensaje para centrar la vista del juego sobre el acontecimiento al que hace referencia.}

Una barra inferior muestra informacion acerca de la partida actual, y se situa en la parte inferior de la vista del juego.
La informacion mostrada, de izquierda a derecha en la barra inferior incluye:
fecha y hora: fecha y hora actual de la partida, seguida por el icono y nombre de la estacion del año.
balance economico: dinero actual disponible para construccion y otros costes (comprar vehiculos, mantenimiento de las infraestructuras de transporte y los costes operacionales de los vehiculos operativos).
coordenadas: indica la posicion del cursor en la vista de juego. Las tres cifras representan las coordenadas X, Y y la altura.
T: indica la escala de paso del tiempo en el juego.
{Consejos: Usa la tecla [.] / [,] para acelerar/decelerar el tiempo, T ; en el menu Pantalla indica si el ordenador puede soportar ese rendimiento.
El uso de Acelerar reemplaza el valor de T por ">>" en la barra inferior.}
cronologia: indica si se esta usando cronologia en la partida actual.

simutrans-124.5/simutrans/text/et.tab000066400000000000000000000167661520675460500177150ustar00rootroot00000000000000§Eesti PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: et Eesti # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 3.04 2025 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable blokeeritud cl_btn_filter_enable võimaldatud cl_btn_filter_settings Seaded cl_btn_sort_asc suurenev cl_btn_sort_desc vähenev cl_btn_sort_id Sisemine ID cl_btn_sort_income Sissetulek cl_btn_sort_name Nimi cl_btn_sort_type Tüüp clf_btn_alle kõik clf_btn_invers inv. clf_btn_keine ei ole #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic arktiline kliima #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Cannot built depot here! Siia ei saa depood ehitada. Cannot create generic line!\nSelect line type by\nusing filter tabs. Ei saa luua üldist liini!\nVali liini tüüp\nkasutades sakke. #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s nüüd\npakub bussiühendust\n %s\nja\n%svahel\(%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s nüüd\npakub bussiühendust\n %s\nja\n%svahel\(%i,%i).\n #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (in depot) (depoos) %d buildings\n %d ehitisi %d convois %d konvoid %d Einzelfahrzeuge im Depot %d sõidukit siin %i km/h (max. %ikm/h) %i km/h(maks. %ikm/h) %s building %s %s %s %s %s %s city %d %s %s linn %d %s %s has entered a depot. %s sisenes depoosse. %s land %d %s %s maa %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s\nehitas\nuue raekoja\nkui jõudis\n%i elanikuni. %s\nis crowded. %s\n on ülerahvastatud 1 convoi 1 konvoi 1 Einzelfahrzeug im Depot 1 sõiduk siin. 1LIGHT_CHOOSE Heledus: 1WORLD_CHOOSE Uue mängu seaded: 2LIGHT_CHOOSE Värvid: 2WORLD_CHOOSE Kaart number: 3LIGHT_CHOOSE Kerimiskiirus: 4LIGHT_CHOOSE Pöördkerimine 5LIGHT_CHOOSE Jalakäijad peatustes 5WORLD_CHOOSE Linnade arv 6LIGHT_CHOOSE Jalakäijad linnas 6WORLD_CHOOSE Liiklustihedus: 8WORLD_CHOOSE päeva ja öö vaheldumine A bridge must start on a way! Sild peab\nhakkama \nteel. Abfrage Ülevaatamise vahend Abnehmer Tarbija About Teave Abriss Hävita/Eemalda Absenken Madalda maad Add Stop Lisa peatus Add stops for backward travel Lisa peatused tagasiretkele. aircraft_tab Veolennukid Airport Lennujaam AIRTOOLS Lennujaama vahendid All Kõik Allow player change Luba mängija vahetamist Alters a schedule. Lisa/eemalda peatus sõiduplaani/sõiduplaanist Angenommene Waren Kaubad mida läheduses olevad tööstused vajavad anhaengen Lisama Anhaenger_tab Järelkärud Anheben Tõsta maad Apply Line Rakenda liin April aprill Arbeiter aus: Töötajad elavad: Arrived Kohale jõudnud Assets Väärtused Aufloesen Võta koost lahti August august Autohalt muss auf\nStrasse liegen!\n Auto- või bussipeatus \npeab olema \nasetatud teele. Available Kasutatav Bahndepot Rongidepoo Bankrott:\n\nDu bist bankrott.\n Pankrot: \n\nSa oled pankrotis!\n battery Aku Baum Puu Baustelle Ehitusala Beenden Välju Beginner mode Algaja re?iim Besonderes Gebaeude Vaatamisväärsus BF jaam bio bioloogiline Blockstrecke ist\nbelegt\n \nRaudtee on kasutuses\nteise rongi poolt!\n Boden Maa Bridge is too long for this type!\n Sild on liiga\npikk valitud\nsillatüübi jaoks. Bruecke Sild Bruecke muss an\neinfachem\nHang beginnen!\n Sillad peavad\nalgama sirgelt\nteelõigult! Brueckenboden sild build choosesignals Ehita signaale Build city market Ehita uus market lähimasse linna. Build drain Trafo build HQ Ehita peakorter Build land consumer Ehita uus elektrijaam. Build monorail depot Ehita monoraudtee depoo Build powerline Ehita elektriliin Build presignals Ehita topeltbloki signaalid Build road depot Ehita garaa? Build ship depot Ehita laevatehas Build signals Ehita signaale Build train depot Ehita rongidepoo Build tram depot Ehita trammidepoo Build truck depot Ehita garaa? Buildings Ei eksisteeri! Built artifical slopes Ehita kunstlikke kaldeid Built random attraction Ehita suvaline atraktsioon. Bus_tab Bussid Cancel Tühista Capacity: %s\nLoad: %d (%d%%) Mahutavus: %s\n» %d (%d%%) Cars are not available yet! Autod ei ole veel saadaval. Cash Kontobilanss Change player Vaheta mängijat Chart Diagramm Choose operation executed on clicking stored/new vehicles Vali tegevus vajutades uutele/olemasolevatele sõidukitele chooses a random map Valib juhusliku kaardi. citicens Linnaelanikud City industries Marketid linnas City list Linnade nimekiri City size Linna suurus city_road Linnatänav CityLimit Linna piirid cl_title Sõidukite nimekiri cl_txt_sort Sordi: clf_chk_aircrafts lennukid clf_chk_cars Bussid/veoautod clf_chk_indepot depoos clf_chk_name_filter Filtreeri nimed: clf_chk_noincome sissetulekut pole clf_chk_noline liini pole clf_chk_noroute marsruuti pole clf_chk_noschedule sõiduplaani pole clf_chk_ships Laevad clf_chk_spezial_filter Erifilter: clf_chk_stucked kinni clf_chk_trains Rongid clf_chk_type_filter Filtri tüübid: clf_chk_waren Filtreeri kaupu: clf_title Sõidukinimekirja filter Climate Control Sea kliimaolud COLOR_CHOOSE\n Palun vali \ncolour tabelist:\n Constructed by Värvis Constructed by %s Värvis %s Construction_Btn Ehitus maksab convoi %d of %d Konvoi number %d %d-st Convoi has been sent\nto the nearest depot\nof appropriate type.\n Sõiduk on saadetud lähimasse sobivasse depoosse. Convoi is sold when all wagons are empty. Sõiduk müüakse niipea kui see on täiesti tühi. CURRENCY € Neue Karte Uus kaart player 2 Trikky Transport player 3 Meyer Moving Co. Size (%d MB): Suurus (%d MB): #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ %1_CITY_SYLL Harju %2_CITY_SYLL Järve %3_CITY_SYLL Põhja %4_CITY_SYLL Põlva %5_CITY_SYLL Kevade %6_CITY_SYLL Lehe %7_CITY_SYLL Puju 1center %s %s 1extern %s haru %s 2center %s kesk %s 2extern %s välis %s 3center %s põhi %s 3extern %s maa %s 4center %s sise %s 4extern %s välis %s 5center %s vahetus %s 6center %s kesk %s 7center %s linna %s simutrans-124.5/simutrans/text/fi.tab000066400000000000000000001155041520675460500176710ustar00rootroot00000000000000§Suomi PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: fi Suomi # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Esitä ajat päivä:tunti:kk kuukaudessa AMBIENT_SOUND ympäristöäänet CASH_SOUND rahantuloääni cl_btn_filter_disable pois päältä cl_btn_filter_enable päällä cl_btn_filter_settings Asetukset cl_btn_sort_asc Nouseva cl_btn_sort_desc Laskeva cl_btn_sort_id sisäinen tunniste cl_btn_sort_income Tulot cl_btn_sort_name Nimi cl_btn_sort_type Tyyppi clf_btn_alle kaikki clf_btn_invers päinv. clf_btn_keine ei mitään Continue Game Jatka peliä FACTORY_SOUND tehtaiden äänet Find matching convois Etsi yhteensopivia ajoneuvoja gl_btn_sort_bonus bonus gl_btn_sort_name nimi gl_btn_sort_revenue tuotto gl_btn_unsort Lajittelematon height based Ilmasto korkeuden perusteella hl_btn_filter_disable pois päältä hl_btn_filter_enable päällä hl_btn_filter_settings Asetukset hl_btn_sort_asc nouseva hl_btn_sort_desc laskeva hl_btn_sort_name Nimi hl_btn_sort_type Tyyppi hl_btn_sort_waiting Odottaa hlf_btn_alle kaikki hlf_btn_invers päinv. hlf_btn_keine ei mitään humidities kosteuksien rajat Install Asenna Lake Järvi Lake height maksimi järvenkorkeus Maximize height levels Lisää korkeustasoja Networks Verkkokaavio Open Sea Avomeri rainfall kosteus Return to menu takaisin valikkoon Road toll Tiemaksu Scenario Skenaario sea meri Start this as a server Aloita serveri tällä pelillä Tutorial Alkuopas #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic arktinen ilmasto desert aavikkoilmasto mediterran välimerenilmasto rocky alppi-ilmasto temperate lämmin ilmasto tropic troopp. ilmasto tundra tundrailmasto #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Bridge blocked by way below or above. Silta estynyt alhaalta tai ylhäältä Can't buy obsolete vehicles! Et pysty ostamaan vanhentunutta kulkuneuvoa! Cannot build on a double slope! Et pysty rakentamaan tuplarinteeseen! Cannot built depot here! Et voi rakentaa tallia tähän. Cannot built this station/building\nin underground mode here. Rakennusta ei voi rakentaa maan alle. Cannot create generic line!\nSelect line type by\nusing filter tabs. Et voi luoda yleisreittiä!\nValitse ensin reitin tyyppi\n Cannot create socket Yhteyden muodostaminen ei onnistu! Cannot rotate this building! Ei pysty kääntämään tätä rakennusta! Convoi handles exhausted! Saavutettiin kuljetusvälineiden\nenimmäismäärä. Convoy already deleted! Saattue jo poistettu! Corrupt file Tiedosto korruptoitunut tai liian lyhyt. Could not open file Ei pysty käsittelemään tiedostoa. Tarkista oikeudet? Das Feld gehoert\neinem anderen Spieler\n Maa on toisen\npelaajan omistuksessa.\n Depots cannot be built on runways! Varikkoa ei pysty rakentamaan kiitoteille! Der Besitzer erlaubt das Entfernen nicht Omistaja ei salli\ntehdä tätä.\n Diese Zusammenstellung kann nicht fahren!\n Tämä yhdistelmä\nei toimi.\n Failed to write file Ei pysty kirjoittamaan tiedostoa. (Oikeudet?) Flugzeughalt muss auf\nRunway liegen!\n Pysäkin pitää olla rullausalueella! Forbidden by scenario Kielletty tässä tilanteessa Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Tätä lentokenttärakennusta\n\nei voi rakentaa tähän! Hier kann kein\nSignal aufge-\nstellt werden!\n Rautatieopastinta\nei voi rakentaa\ntähän.\n In order to lock the game, you have to protect the public player by password! Pelin lukitsemiseksi sinun täytyy suojata julkinen pelaaja salasanalla! Loading a new game will end the current server session! Uuden pelin lataaminen lopettaa nykyisen serveri-istunnon! Lost connection\nto server! Palvelin ei vastaa! Lost synchronisation\nwith server. Synkronisaatio palvelimen\nkanssa epäonnistui. Maglevhalt muss auf\nMaglevschiene liegen!\n Maglev-pysäkin voi rakentaa vain maglev-radalle! Monorailhalt muss auf\nMonorail liegen!\n Monorail-asema täytyy\nasettaa Monorail-radalle! Monorails are not available yet! Monorailit eivät ole vielä käytössä! More than one possibility to build this dock found. Useampi mahdollisuus rakentaa tämä telakka löytyy. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Kapearaidepysäkin oltava kapeariteisella raiteella. No bridges over runways! Ei siltoja yli kiitoteiden! No curves on runways Ei mutkia kiitoteillä No suitable crossing Ei sopivaa ylitystä! (liian nopea maksiminopeus?) No through station here! Et voi rakentaa asemaa tähän! Not allowed to copy object. Et pysty rakentamaan esinettä!\n(Syy: Väärä pelaaja tai vanhentunut) Not enough bytes transferred Ei tarpeeksi tavuja siirretty Not enough clearance. Polkujen välillä täytyy olla vähintään kahden tason korkeusero. Not enough empty space Riittämätön määrä vapaata tilaa jäljellä. Not enough money! Sinulla ei ole tarpeeksi varoja tämän rakentamiseen! Not initialized Kirjastoa ei voinut alustaa. On narrowgauge track only!\n Tämä onnistuu vain kapearaideradalla!\n Only public player can lock games! Vain julkinen pelaaja voi lukita pelejä! Only up and down movement in the underground! Maanalaista laattaa voi nostaa ja laskea vain käyttämällä kaikkia ylös/alas -työkaluja! Out of funds Sinulla ei ole tarpeeksi rahaa tätä varten! Post muss neben\nHaltestelle\nliegen!\n Postitoimisto pitää\nrakentaa aseman tai\npysäkin lähelle.\n Protocoll error (expecting game) Protokollavirhe (odotetaan peliä) Schiffhalt muss im\nWasser liegen!\n Laivan pysähdyspaikan\nvoi asettaa vain\nveteen lähelle\nlaituria.\n Server busy Palvelin kiireinen Server version too new Palvelin käyttää uudempaa ohjelmaversiota. Ship depots must be built on water! Laivatelakat täytyy rakentaa veden päälle! Show all revenue messages Näytä kaikki tuloviestit Show no revenue messages Älä näytä tuloviestejä Show only player's revenue Näytä vain pelaajan tulot Terraforming not possible\nhere in underground view Maata ei voi muokata\nmaanalaisessa näkymässä. This tunnel branches. You can try Control+Click to remove. Tämä tunneli haarautuu. Voit kokeilla Control+Click poistaaksesi sen. Too far away to merge stations! Liian kaukana yhdistääksesi asemat! Unknown error Tuntematon virhe Upgrade must have\na higher level Päivityksen täytyy olla korkeampaa tasoa Vehicle %s cannot choose because stop too short! Ajoneuvo %s ei voi valita, koska pysäkki liian lyhyt! Watertable reached Pohjavesialue saavutettu Zughalt muss auf\nSchiene liegen!\n Rautatieaseman\nvoi rakentaa\nvain rautatielle.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Keyboard Help\n

Keyboard Help

\n Pikanäppäinohje\n

Pikanäppäinohje

\n

\nPikanäppäinohje näyttää eri näppäimiä painamalla tapahtuvat toiminnot.\n

\n

\nPikanäppäinohje avautuu painamalla näppäintä, joka ei ole muussa käytössä, tai Yleisohjeen kautta.\n

\n

\nNäppäinkomennot huomioivat kirjainkoon (käytä [Shift]-näppäintä saadaksesi isot kirjaimet).\n

\n

\nSeuraavilla näppäimillä on omat toimintonsa:\n

\n

\n[Nuolinäppäimet]: vieritä pelinäkymää nuolen suunnassa.
\n[Backspace]: sulje kaikki ikkunat, työkalupalkit ja ohjetekstit.
\n[Delete], tai [Escape]: sulje ylin ikkuna, työkalupalkki tai ohjeteksti.
\n[Enter], tai [Return]: käytä vahvistaaksesi eri toimintoja.
\n[Page-Up], tai [>]: lähennä pelinäkymää.
\n[Page-Down], tai [ vähemmän kuin -merkki]: loitonna pelinäkymää.\n[F1]: avaa Simutrans-ohje.

\n

\n[1]: vieritä pelinäkymää etelään.
\n[2]: vieritä pelinäkymää kaakkoon.
\n[3]: vieritä pelinäkymää itään.
\n[4]: vieritä pelinäkymää lounaaseen.
\n[6]: vieritä pelinäkymää koilliseen.
\n[7]: vieritä pelinäkymää länteen.
\n[8]: vieritä pelinäkymää luoteeseen.
\n[9]: vieritä pelinäkymää pohjoiseen.\n

\n

\n[Shift] + hiiri: käytä Kartalla nähdäksesi Teollisuusketjujen yhteydet.
\n[CTRL] + työkalu: rakenna (osoittimia ja Pysäkkejä) ylemmälle tasolle; tai rakenna hitaita teitä tai ratoja nopeampien päälle; tai rakenna suorempia teitä tai ratoja.
\n[CTRL] + ([F2] - [F12]): asettaa valitun työkalun tietyllä näppäinkomennolla [F2] - [F12] käytettäväksi.\n

\n

#____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s liikennöi\nnyt bussilinjaa\nvälillä %s\n - %s (%i,%i).\n\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s liikennöi\nnyt bussilinjaa\nvälillä %s\n - %s (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). Yhtiön %s %i\nrekkaa huolehtivat\nnyt kuljetuksista välillä\n%s (%i,%i)\n - %s\n (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\navasi uuden rautatien\nvälille %s\n (%i,%i) - \n%s\n (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n %s\navasi uuden lentoreitin välille\n%s -\n%s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n %s\ntarjoaa nyt\nlauttayhteyden välillä\n%s -\n%s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Matkustajat käyttävät nyt\nyhtiön %s\nbusseja välillä\n%s - %s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Maastonmuokkaus LISTTOOLS Listat MAGLEVTOOLS Maglev-työkalut MONORAILTOOLS Monorail NARROWGAUGETOOLS Kapearaidetyökalut RAILTOOLS Rautatie ROADTOOLS Tie SHIPTOOLS Laiva SLOPETOOLS Rinnetyökalut SPECIALTOOLS Erikoistyökalut TRAMTOOLS Raitiovaunu #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s on rakentanut uuden pääkonttorin. Factory chain extended\nfor %s near\n%s built with\n%i factories. Talouskasvu kiihtyy:\n%s laajentaa tuotantoa\nkaupungin %s alueella.\n%i uutta tehdasta perustettiin. New %s now available:\n%s\n Uusi %s saatavilla:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Uusi %s on\n alihankkijoineen avattu\nkaupungin %s\nlähellä, %i uutta tehdasta\n rakennettiin. New vehicle now available:\n%s\n \n Uusi kulkuneuvo on käytettävissä:\n\n\n » %s «\n\n Remove vehicle from map. Use with care! Poista kulj.väline. (Käytä varoen!) Screenshot\ngespeichert.\n Kuvankaappaus\ntallennettu.\n Sends the convoi to the last depot it departed from! Lähettää kulkuneuvon lähimpään talliin. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s rakensi uuden\nmuistomerkin. %i asukasta\nvietti kotiseudustaan ylpeänä\nansaittua vapaapäivää. #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (tallissa) \nBauzeit bis \nvuoteen \nBauzeit von \nValmistettu vuodesta \nCan't open heightfield file.\n \nKorkokuvatiedostoa\nei voi avata.\n \ndirection: \nSuunnat: \nelektrified \nSähköistetty\n \nHeightfield has wrong image type.\n \nKorkokuva on\nvääräntyyppinen.\n \nis reserved by: \ntiellä on juna \nminimum speed: \nAlin sallittu nopeus: \nnot elektrified \nEi sähköistetty\n \nRibi (masked) \nSuunnat\m (maski): \nRibi (unmasked) \Suunnat\n (ei maskia): \nSet phases: \n säädä pituutta P-E/I-L: \nsingle way \nYksisuuntainen \nway1 reserved by Tien 1 varannut\n \nway2 reserved by Tien 2 varannut\n \nwith sign/signal\n \nmerkillä/opastimella\n %d buildings\n %d rakennusta %d convois %d kuljetusvälinettä %d Einzelfahrzeuge im Depot %d kulj.välinettä tallissa %i km/h (max. %ikm/h) %i km/h (max. %i km/h) %i years %i months old. %i vuotta %i kuukautta vanha. %s building %s %s %s %s %s %s city %d %s %s %d %s %s has entered a depot. %s on tallissa. %s land %d %s %s maa %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s rakensi\nuuden kaupungintalon\nsaavuttaessaan %i\nasukkaan rajan. %s\nis crowded. %s\on ruuhkainen. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\nnopeus %i\nmax. nopeus %i\ndx:%i dy:%i %s\nwas liquidated. %s\non lakkautettu. 1 convoi 1 kuljetusväline 1 Einzelfahrzeug im Depot 1 kulj.väline tallissa. 1LIGHT_CHOOSE Kirkkaus: 1WORLD_CHOOSE Uuden kartan asetukset: 2LIGHT_CHOOSE Värit: 2WORLD_CHOOSE Kartan numero: 3LIGHT_CHOOSE Vieritysnopeus: 4LIGHT_CHOOSE Käänteinen vieritys 5LIGHT_CHOOSE Näytä jalankulkijat 5WORLD_CHOOSE Kaupunkien määrä: 6LIGHT_CHOOSE Jalankulkijat kaupungeissa 6WORLD_CHOOSE Liikennetiheys: 8WORLD_CHOOSE Käytä päivä/yö-tilaa A bridge must start on a way! Sillan täytyy alkaa tiestä. Abfrage Tutkimistyökalu Abnehmer Kuluttajat About Tietoja Abriss Tuhoa Absenken Laske maata Accelerate time Nopeuta aikaa Active player only Vain aktiivinen pelaaja Add forest Lisää metsää Add random citycar Lisää yksityisauto Add Stop Lisää pysäkki Add stops for backward travel Lisää pysäkkejä paluumatkalle air kiitorata/rullausalue aircraft_tab Rahtikoneet airplane Lentokone Airport Lentokenttä AIRTOOLS Lentokentät All Kaikki all convoi tooltips kaikki ajoneuvon tiedot Allow city growth Salli kasvu Allow player change Salli pelaajan vaihtaminen allowed climates:\n Sallitut ilmastot:\n Alters a schedule. Lisää/poista pysäkkejä reitille/ltä Angenommene Waren Lähiyritysten tarvitsemat tavarat anhaengen Liitä Anhaenger_tab Perävaunut Anheben Nosta maata Appends stops at the end of the schedule Lisää pysäkkejä reitin loppuun Apply Line Laita reitille April huhtikuu Arbeiter aus: Työntekijöiden asuinpaikat: Arrived Saapunut Assets Pääoma Aufloesen Pura August elokuu Autohalt muss auf\nStrasse liegen!\n Pysäkki täytyy asettaa tielle! Available Saatavilla Bahndepot Veturitalli Bankrott:\n\nDu bist bankrott.\n \n\nYhtiösi on tehnyt konkurssin.\n battery akku Baum Puu baum builder Kasvata puita Baustelle Rakennustyömaa Bauzeit Rakennusaika Beenden Lopeta Beginner mode Aloittelijatila Besonderes Gebaeude Turistikohteet BF asema Blockstrecke ist\nbelegt\n Rataosuus on toisen\njunan käytössä.\n Boden Maa bridge is too high for its type! Silta on liian korkea. Bridge is too long for this type!\n Silta on liian\npitkä! Bruecke Silta Bruecke muss an\neinfachem\nHang beginnen!\n Sillan täytyy\nalkaa mäestä.\n Brueckenboden silta Build air depot Rakenna hangaari build choosesignals Rakenne valintaosoittimia Build city market Rakenna uusi kauppa lähimpään kaupunkiin Build drain Rakenna muuntaja build HQ Rakenna pääkonttori Build land consumer Rakenna uusi voimala Build maglev depot Rakenna maglev-talli Build monorail depot Rakenna Monorail-talli Build narrowgauge depot Rakenna kapearaidetalli Build powerline Rakenna voimalinjoja Build presignals Rakenna esiosoittimia Build road depot Rakenna autotalli Build ship depot Rakenna telakka Build signals Rakenna osoittimia Build train depot Rakenna veturitalli Build tram depot Rakenna raitiovaunutalli Build truck depot Rakenna autotalli Building costs estimates Kustannusarvio Buildings Rakennuksia Built artifical slopes Rakenna keinotekoisia rinteitä Built random attraction Rakenna turistikohde Bus_tab Bussit Can only move from halt to halt or waypoint to waypoint. Voidaan siirtä vain\npysäkiltä toiselle\ntai reittipisteeltä toiselle. Cancel Peruuta Capacity Kapasiteetti Capacity: Kapasiteetti: Capacity: %.0f MW Kapasiteetti: %.0f MW\n Capacity: %d%s %s\n Kapasiteetti: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Kapasiteetti: %s\nLasti: %d (%d%%) Cars are not available yet! Autot eivät ole vielä käytettävissä. cars.\nstate autoa\n Cash Kate Change player Vaihda pelaajaa Chart Tiedot Choose direction Vaihda suunta Choose operation executed on clicking stored/new vehicles Valitse varastoitua/uutta kulkuvälinettä klikattaessa suoritettava toiminto chooses a random map Luo sattumanvarainen kartta citicens Asukkaita City attraction Kaupunkinähtävyys City industries Kaupat kaupungeissa City list Kaupunkilista City size Asukkaita city_road Katu citybuilding builder Talonrakentaja CityLimit Rajat cl_title Kuljetusvälinelista cl_txt_sort Järjestys: Clear block reservation Näytä/nollaa reittivaraukset clf_chk_aircrafts Lentokoneet clf_chk_cars Bussit/rekat clf_chk_indepot tallissa clf_chk_maglev Maglev-junat clf_chk_monorail Yksiraidejunat clf_chk_name_filter Suodata nimet: clf_chk_narrowgauge Kapearaidejunat clf_chk_noincome ei tuloja clf_chk_noline ei reittiä clf_chk_noroute ei reittiä clf_chk_noschedule ei reittiä clf_chk_ships Laivat clf_chk_spezial_filter Erik. suod.: clf_chk_stucked jumissa clf_chk_trains Junat clf_chk_trams Raitiovaunut clf_chk_type_filter Suod. tyyppi: clf_chk_waren Suod. tavarat: clf_title Kuljetusvälinelistan suodatus Climate Control Ilmaston asetukset closed suljettu COLOR_CHOOSE\n Valitse yhtiöllesi\nväriteema:\n Company bankrupt Yhtiö vararikossa Congratulation\nScenario was complete in\n%i months %i years. Onnittelut!\nSkenaario selvitetty\n%i kuukaudessa ja %i vuodessa. Connected stops Yhdist. pysäkit Constructed by Maalannut: Constructed by %s Maalannut: %s Construction_Btn Rakennuskulut convoi %d of %d Kuljetusväline %d / %d convoi error tooltips ajoneuvojen virhetiedot Convoi has been sent\nto the nearest depot\nof appropriate type.\n Ajoneuvo on lähetetty lähimpään sopivaan talliin. Convoi is sold when all wagons are empty. Kulkuneuvo myydään, kun kaikki vaunut ovat tyhjiä. convoi mouseover tooltips ajoneuvojen tiedot hiirellä convoi passed last\nmonth %i\n Ajoneuvoja viime\nkuussa %i\n Convois Ajoneuvot Convois: %d\nProfit: %s Ajoneuvot: %d\nTuotto: %s Convoys Ajoneuvot Copy Convoi Kopioi ajoneuvo Copy the selected convoi and its schedule or line Kopioi valittu ajoneuvo ja sen reitti. cost for removal poistaminen maksaa Costs Kulut Create a new line based on this schedule Luo uusi linja, joka perustuu tähän reittiin curiosity builder Nähtävyysrakentaja curlist_title Turistikohdelista CURRENCY € Currently playing: Tällä hetkellä soi: Deccelerate time Hidasta aikaa December joulukuu decrease underground view level vähennä maanalaisen näkymän tasoa Del Stop Poista Delete Line Poista linja Delete the current stop Poista nykyinen pysäkki Delete this file. Poista tämä tiedosto. Demand: %.0f MW Tarve: %.0f MW\n Denkmal Muistomerkki Departed Lähtenyt Depots Tallit Der Tunnel ist nicht frei!\n Tunneli ei ole tyhjä.\n Destination Määränpää Details Lisätiedot Die Bruecke ist nicht frei!\n Silta ei ole vapaa. Direkt erreichbare Haltestellen Suorat yhteydet asemille disable midi Mykistä midi-musiikki Display settings Näytön asetukset Distance Matka Dock laituri Dock must be built on single slope! Laituri pitää rakentaa suoraan rinteeseen! Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Sinulla on %d kuukautta maksaa velkasi. Durchsatz Max. Economy Talous ja kaupungit Eigenbesitz\n Julkista omaisuutta\n Ein %s\npasst hier nicht.\n '%s'\nei sovi tähän.\n Einstellungen aendern Muuta asetuksia electric sähkö Electricity sähkö Electricity producer\n\n Sähköntuottaja\n\n Electrics_tab Sähkö Electrify track Sähköistä rata enlarge map Suurenna karttaa enter a value between %i and %i Syötä arvo %i:n ja %i:n väliltä Error Virhe Erzeuge neue Karte.\n Odota, uutta karttaa\nluodaan.\n\nTämä saattaa kestää\nmuutaman minuutin\nisoilla kartoilla.\n Es wird bereits\nein Fahrplan\neingegeben\n Aikataulua\ntehdään parhaillaan.\nSaata se päätökseen\nennen kuin uusit sen!\n Fabrikanschluss Yhdist. yritykset Fabrikname Tehtaan nimi Factories Tehtaat factory details Tehtaan yhteydet factorybuilder Tehdasrakentaja Fahrplan Reitti Fahrtziel Määränpää: Fahrzeuge koennen so nicht entfernt werden Kuljetusvälinettä ei\nvoi poistaa tällä\ntavalla.\n Fahrzeuge: Kulj.välineet: Farbe Väritys Fast forward Nopeuta aikaa February helmikuu Ferry_tab Matkustajalautat Fertig Valmis Filename Tiedosto: Filter: Suodatus: Finances of %s %s:n talous Finanzen Talous fl_title Tehdaslista Flug_tab Matkustajakoneet follow me Seuraa Follow the convoi on the map. Seuraa ajoneuvoa kartalla. Forest Metsät Found new city Perusta uusi kaupunki Fracht Rahti Frame time: Ruudunpäivitysaika: Free Capacity Tyhjää freeplay mode vapaapeli Friction: Kitkakerroin: fuel_cell polttokenno Full load Lasti: Fussgaenger Jalankulkija GAME PAUSED TAUOLLA Gear: Välitys: Gebaeude Rakennus General Yleinen Generation: %.0f MW Tuotanto: %.0f MW\n Gewicht Paino Gewinn Tulot: Give the selected vehicle(s) an individual schedule Anna valituille ajoneuvoille yksilöllinen reitti. gl_btn_sort_catg kategoria gl_title Lista kaikista tavaroista go home Lähetä talliin Goods Tavaraa Goods AI Rahti-AI Goods list Tavaralista Gross Profit Kokonaistuotto Groundobj Objekti Grow city Kasvata kaupunkia Growth Kasvu H pysäkki Hangar Hangaari Happy Tyytyväisiä Haus kaufen Osta talo Helligk. Näyttö Help Ohje Help text not found Ohjetekstiä ei löydy! hide all building Piilota kaikki rakennukset hide city building Piilota kaupunkien rakennukset hide station names Piilota pysäkkien nimet hide transparent Piilotetut läpinäkyviä hide trees Piilota puut Hier warten/lagern: Matkustajia/tavaroita asemalla: hl_title Pysäkkilista hl_txt_filter Suodatus: hl_txt_sort Järjestys: hlf_chk_airport lentokenttä hlf_chk_anleger laituri hlf_chk_bahnhof rautatieasema hlf_chk_bushalt bussipysäkki hlf_chk_frachthof lastausalue hlf_chk_keine_verb ei yhteyttä hlf_chk_maglevstop Maglev-pysäkki hlf_chk_monorailstop Yksiraidepysäkki hlf_chk_name_filter Suodata nimet: hlf_chk_narrowgaugestop Kapearaideasema hlf_chk_overflow Ylikapasiteetti hlf_chk_spezial_filter Erik. suod.: hlf_chk_tramstop Raitiovaunupysäkki hlf_chk_type_filter Suodata tyypit: hlf_chk_waren_abgabe Tuotanto: hlf_chk_waren_annahme Tavaran tarve: hlf_title Pysäkkilistan suodatus Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Sopivaa tallia ei löydy.\nSinun täytyy lähettää\najonevo talliin manuaalisesti. Homeless Kodittomia hydrogene vetyä Idle: Vapaana: ignore climates Älä huomioi ilmastoja Increase Industry density Lisää teollisuuden määrää increase underground view level lisää maanalaisen näkymän tasoa industrial building tuotantolaitos Init map ... Luodaan karttaa... Input Raaka-aineet Ins Stop Lisää eteen Insert stop before the current stop Lisää pysäkki ennen nykyistä pysäkkiä Intercity road len: Maanteiden pituus: Intro. date: Julkaisupäivä: invalid ei määritelty. Invalid coordinate Virheellinen komento isometric map isometrinen kartta January tammikuu July heinäkuu Jump to Siirry kohteeseen June kesäkuu Kann Spielstand\nnicht laden.\n Tallennetun pelin\nlataus epäonnistui.\n Kann Spielstand\nnicht speichern.\n Tallennus epäonnistui.\n Kein Besitzer\n Ei omistajaa\n keine ei mitään Keine Einzelfahrzeuge im Depot Ei kulj.välineitä tallissa. Keyboard_Help\n Pikanäppäimet\n koord koordinaatit Kreuzung Risteys labellist_title Merkkilista Lade Relief Lataa korkokuva Laden Lataa Land attraction Maaseutunähtävyys Land industries Tehtaita: LANG_CHOOSE\n Valitse kieli\nkäyttöliittymälle:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Year Viime vuosi: Leaving depot! Lähdössä tallista! leer tyhjä Legend Selite Leistung Teho Leistung: %d kW Teho: %d kW Leitung Voimalinja letzen Monat: diesen Monat: viime kuu: tämä kuu: Line linja Line Management Linjojen hallinta Lineless convoys serving this stop Linjattomat ajoneuvot tällä pysäkillä Lines serving this stop Linjat, jotka käyvät tällä pysäkillä LKW_tab Rekat Load game Lataa peli load height data from file Lataa korkeusdata tiedostosta. Load scenario Lataa skenaario loaded ladattu loaded passenger/freight Järjestä matkustajat/rahti: Loading (%i->%i%%)! Lastataan (%i->%i%%) Loading addon paks ... Ladataan lisäosia... Loading map ... Avataan karttaa... Loading paks ... Ladataan grafiikoita... Loading skins ... Ladataan käyttöliittymää... Lock game Estä pelaajan vaihto (edellyttää vahvistusta) Lokomotive_tab Veturit m3 m³ maglev vehicle Maglev-ajoneuvo maglev_track Maglev-rata Maglevdepot Maglev-asema Mailbox Postilaatikko Mailbox Options Postilaatikon asetukset Maintenance Ylläpito make stop public (or join with public stop next) costs %i per tile and level Tee pysäkistä julkinen (tai liitä se viereiseen julkiseen pysäkkiin). Maksaa %i$ jokaiselta ruudulta ja tasolta. Manual (Human) Pelaaja Manufactured: Valmistaja: Map roughness Maaston tasaisuus: map zoom suurennos March maaliskuu Margin (%%) Voitto (%%) Marker Merkkaustyökalu Max income: Maksimitulot: Max. speed: Huippunopeus: Maximum 254 stops\nin a schedule!\n Reitillä voi olla vain\n254 pysäkkiä!\n maximum length of rivers Jokien max.pituus Maximum tile height difference reached. Korkeusero \nkahden ruudun\nvälillä saa\nolla enintään 2. May toukokuu Median Citizen per town Asukkaita keskimäärin: Meldung Viesti Menge Määrä MessageOptionsText \nUusi vuosi\n\nPelaajauutiset\n\nKaupunkiuutiset\n\nEi reittiä\n\nUudet yritykset\n\nChat\n\nUudet ajoneuvot\n\nAsema täynnä\n\nOngelmat\n\nRuuhkat\n\nSkenaario minimum length of rivers Jokien min.pituus Modify the selected line Muokkaa valittua linjaa Monate alt kuukautta vanha. monorail vehicle yksiraidejuna monorail_track Monorail-rata Monorailboden Monorail-raiteen tuki Monoraildepot Monorail-talli month wait time Odotusaika (kk) Months Kuukaudet Monument Muistomerkki Monuments Monumentit Mountain height Mäkien korkeus: Movingobj liikkuva objekti Music playing disabled/not available Musiikin soitto pois päältä. Music volume: Musiikin voimakkuus: mute sound Mykistä Name Nimi Narrowgauge Kapearaiteinen Narrowgauge are not available yet! Kapearaide ei ole vielä saatavilla! narrowgauge vehicle kapearaidejuna narrowgauge_track Kapearaiderata Narrowgaugedepot Kapearaidetalli Net ID: %p Verkko: %p\n Net Wealth Nettovarallisuus Neue Karte Uusi kartta Neue Welt Luo uusi maailma new convoi Uusi kuljetusväline New Line Uusi linja New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Uusi linja on luotu.\nVoit liittää sen kulkuneuvoon\nvalitsemalla sen\nlinjavalikosta yläreunassa. New Vehicles Uudet kulj.välineet no buildings hidden Ei piilotettuja rakennuksia no convois Ei kuljetusvälineitä No goods are loaded onto this convoi. Kuljetusväline ei ota uutta lastia no goods waiting ei tavaroita odottamassa no load Ei lastia No Route ei reittiä No stop here! Työkalua voi käyttää\nvain pysäkkien kohdalla. No suitable ground! Alusta ei ole sopiva! No terminal station here! Ei päätepysäkkiä tässä! no timeline Ei aikalinjaa no tree Ei puita Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Aseta kuljetusvälineelle\nensin reitti.\n none ei mitään nord Pohjoinen nordost Koillinen nordwest Luode Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Ei sallittu!\nEt voi muuttaa \nkulkuneuvon reittiä \njuuri nyt. \nYritä myöhemmin uudestaan. Not enough fields would remain. Maatilan ympärille ei jäisi\ntarpeeksi peltoja. November marraskuu Now active as %s.\n Nyt aktiivisena pelaaja %s. Number of rivers Jokien määrä Object Objekti Odometer: %s km Matkamittari: %s km Oktober lokakuu On loan since %i month(s) Veloissa %i kuukauden ajan On this map, you are not\nallowed to change player!\n Peli lukittu!\nEt voi vaihtaa pelaajaa!\n Only city chains Vain kaupunkiketjut Only land chains Vain maaseutuketjut Only one transformer per factory! Vain yksi muuntaja tehdasta kohden! open auki. Operation Toiminta Ops Profit Tuotto toiminnasta Optionen Asetukset Origin Alkuperä ost Itä Output Tuotanto paletten laatikkoa Pas_tab Matkustajajunat Passagiere matkustajaa Passagierrate Matkustajia Passagierziele Matkustajien ja postin määränpäät Passenger AI Matkustaja-AI Passengers %d %c, %d %c, %d no route Matkustajia %d %c, %d %c, %d ei reittiä Passengers %d %s, %d %s, %d no route Matkustajia %d %s, %d %s, %d ei reittiä Pause Tauko PaxDest Määränpäät Percent Electricity Sähköntuotto (%% tarpeesta): Planes are not available yet! Lentokoneet eivät ole vielä käytettävissä! Plant tree Istuta puita player pelaaja player -1 Pelaaja player 0 Julkiset palvelut player 1 Tiger Trans oy player 10 Pelaaja 10 player 11 Pelaaja 11 player 12 Pelaaja 12 player 13 Pelaaja 13 player 2 Trikkyn Kuljetus player 3 Oy Simutransport Ab player 4 Meyerin Kuljetus oy player 5 Net shipping oy player 6 Katajamäen Kuljetus player 7 Pelaaja 7 player 8 Pelaaja 8 player 9 Pelaaja 9 Please choose vehicles first\n Valitse kulkuneuvot ensin!\n Post postia Postrate Postia Power Teho Power: Teho: Powerlines Voimalinjat Production of %s has been stopped:\n%s\n %s %s on poistunut tuotannosta.\n Produktion Tuotanto Profit Tuotto promote to line Aseta linjalle q1 Kevät q2 Kesä q3 Syksy q4 Talvi rail car juna random satunnainen Random age satunnainen vuosi Random map Arvo kartta Rathaus Kaupungintalo Rating Tila ratio_pax Matk. osuus Reliefkarte Kartta Remove Poista remove airstrips Poista kiitorata remove channels Poista kanava remove maglev tracks Poista maglev-rata remove monorails Poista Monorail-raiteet remove narrowgauge tracks Poista kapearaiderata remove powerlines Poista voimalinjoja remove roads Poista tie remove tracks Poista rautatie Remove wayobj %s Poista kohde %s replace stop Siirrä pysäkki request closing pyydä sulkemista residential house Asuinrakennus Restore natural slope Palauta luonnollinen muoto Restwert: Myyntiarvo: Retire. date: Eläköitym. päivä: return ticket Menopaluu Revenue Tulot road tie road vehicle ajoneuvo Roadsign Liikennemerkki Rotate map Käännä karttaa Rotation Kierto Routing Reitit sack säkkiä sail tuuli Saving map ... Tallennetaan karttaa... Scenario complete: %i%% Skenaarion edistyminen: %i%% Schedule changing! Vaihtaa reittiä! Schienentunnel Tunneli (rautatie) Schiff_tab Laivat Schiffdepot Telakka Schleppkahn_tab Proomut Screenshot Ota kuvankaappaus. Seasons Vuodenajat Sehenswuerdigkeit Turistikohde Sell the selected vehicle(s) Myy valitut kulkuneuvot. sended Läht. posti SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 September syyskuu Server did not respond! Palvelin ei vastannut! Serves Line: Palvelee linjalla: Service Palvelu Setting Asetukset Ship Laiva shops and stores Kauppoja ja toimistoja Show all Näytä kaikki show all building Näytä rakennukset Show also vehicles no longer in production. Näytä myös tuotannosta poistuneet kulkuneuvot. Show also vehicles that do not match for current action. Näytä myös kulkuneuvot, jotka eivät sovellu kyseiseen toimintoon. show grid Näytä ruudukko Show industry Näytä tehtaat Show legend Selite Show map scale Mittakaava Show obsolete Näytä vanhenneet Show schedules Näytä reitit show station coverage Näytä asemien peittävyys show station names Näytä asemien nimet show waiting bars Näytä odottavat pylväinä Show/hide statistics Näytä/piilota tiedot Shows consumer/suppliers for factories Näytä tehtaiden saapuvat/lähtevät Shows the currently selected schedule Näytä valittu reitti Shrink city Pienennä kaupunkia shuffle midis sekoita kappaleet Sim: Silmukoita: Size (%d MB): Kartan koko: sliced underground mode Viipalenäkymä slot empty tyhjä Sort by Järjestä: Sort waiting list by Järjestä odottavien lista: Sound Äänet Sound settings Ääniasetukset Sound volume: Äänenvoimakkuus: special freight Erikoisrahti Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Nopeusbonus\ntie: %i km/h, rautatie: %i km/h\nlaivat: %i km/h, lentokoneet %i km/h Speedlimit Nopeusrajoitus Speichern Tallenna Spieler Pelaaja Spieler(mz) Pelaajat Spielerliste Pelaajalista Spielstand wurde\ngeladen!\n \nPeli ladattu.\n Spielstand wurde\ngespeichert!\n \nPeli tallennettu.\n Sprache Kieli Sprachen Kielet Stadtinformation Kaupungin tiedot Start Käynnistä Start the selected vehicle(s) Käynnistä kulkuneuvo(t). Starte Spiel Aloita peli Station tiles: Asemaruudut: Status Pysäkin tila steam höyry Step timeline one year Siirry vuosi eteenpäin. Storage capacity Varastokapasiteetti Strassendepot Autovarikko Strassentunnel Tunneli (tie) street car raitiovaunu sued etelä suedost kaakko suedwest lounas Summer snowline Lumiraja kesällä Suppliers Alihankkijat Tage alt päivää vanha. There are still vehicles\nstored in this depot!\n Tämän tallin sisällä on\nyhä kuljetusväline(itä)!\n This Month Tässä kuussa This Year Tämä vuosi: Tile not empty. Ruutu ei ole tyhjä. timeline Aikalinja tl_title Lista kaikista kaupungeista To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Houkutellakseen enemmän\nturisteja %s\nrakensi kohteen\n%s\n %i veronmaksajan avulla. To heavy traffic\nresults in traffic jam.\n Liikenneruuhka!\n Toggle day/night view Vaihda päivä/yö -tilaa Toggle vehicle tooltips Vaihda kulkuneuvovihjeitä tonnen tonnia Total inhabitants: Asukkaita: Tourist attractions Turistikohteita: Tourists Turistikohteita Town: %s\n Kaupunki:\n %s\n Towns Kaupungit track raide Tracks Raiteet Traffic Liikenne Train Juna Trains are not available yet! Junat eivät ole vielä käytettävissä. Tram Raitiovaunu tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. raitiovaunut %i km/h, monorail %i km/h\nmaglev %i km/h, kapearaide %i km/h. tram_track raitiovaunurata Tramdepot Raitiovaunutalli Trams are not available yet! Raitiovaunut eivät ole vielä käytettävissä! Transferring game ... Siirretään peliä... Transformer only next to factory! Muuntamon voi rakentaa vain\ntyhjääm, tasaiseen ruutuun\ntehtaan viereen. Translation Käännös transparent station coverage Läpinäkyvä asemien peittävyys Transported Kuljetettu TrolleyBus_tab Johdinautot Truck Rekka Tunnel muss an\neinfachem\nHang beginnen!\n Tunnelin täytyy\nalkaa mäestä.\n Tunnel must start on single way! Tunnelin täytyy alkaa\nyksittäisestä tiestä! Tunnelboden Tunneli underground mode Maanalainen näkymä UNDO failed! Et voi perua enää.\nReitin rakentamisen voi perua\nvain, jos reitille ei ole\nrakennettu osoittimia,\nasemia, pysäkkejä tai\nmitään muutakaan. Undo last ways construction Peruuta edellinen tien-/radanrakennustyö Unemployed Työttömiä Unhappy Pettyneitä units/day yksikköä/kk\n Update Line Päivitä linja upgrade HQ Päivitä pääkonttori Usage: %.0f %% Käyttöaste: %.0f %% Use timeline start year Käytä aikalinjaa vuodesta Vehicle %s can't find a route! Kulkuneuvo %s\nei löydä reittiä! Vehicle %s is stucked! Kulkuneuvo %s on jumissa! Vehicle details Kulkuneuvon tiedot Verbrauch Kulutus Vergroessere die Karte\n Suurenna kartta. verkaufen Myy Verkehrsteilnehmer Henkilöautoja Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Yhtiölläsi on velkoja!\n\nSinulla on %d kuukautta\naikaa maksaa velkasi.\n via Kautta via %s\n kautta %s\n via Menge Kautta (määrä) voranstellen Liitä eteen Waggon_tab Vaunut waiting odottamassa Waiting for clearance! Odottaa vuoroaan! Water Vesi Water level Veden taso: water vehicle Vesikulkuneuvo way %s cannot longer used:\n Tietyyppiä %s ei voida enää käyttää.\n way %s cannot longer used:\n%s\n Tietyyppiä %s ei voida enää käyttää:\n way %s now available:\n %s nyt saatavilla:\n Ways not connected Pisteitä ei ole yhdistetty. Wegpunkt Reittipiste Wert Arvo west Länsi Winter snowline Lumiraja talvella withdraw Lakkauta Withdraw All Lakkauta kaikki WRONGSAVE Tallennetun pelin versio\nei ole yhteensopiva.\nLataus ei onnistu.\n Year %i has started. Vuosi %i on alkanut. Years Vuodet Zielort Määränpää zooming in Lähennä zooming out Loitonna Zu nah am Kartenrand Et voi rakentaa tähän,\nsillä kartan reuna on\nliian lähellä.\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Uusi nopeusennätys maglev-junille: %.1f km/h (%s). New world record for monorails: %.1f km/h by %s. Uusi nopeusennätys\nmonorail-junille: %.1f km/h\n(%s). New world record for motorcars: %.1f km/h by %s. Uusi nopeusennätys\nautoille: %.1f km/h\n(%s). New world record for narrowgauges: %.1f km/h by %s. Uusi nopeusennätys\nkapearaideradoilla: %.1f km/h\n(%s). New world record for planes: %.1f km/h by %s. Uusi nopeusennätys\nlentokoneille: %.1f km/h\n(%s). New world record for railways: %.1f km/h by %s. Uusi nopeusennätys\nrautateillä: %.1f km/h\n(%s). New world record for ship: %.1f km/h by %s. Uusi nopeusennätys\nmerellä: %.1f km/h\n(%s). #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &1_CITY_SYLL kaupunki &2_CITY_SYLL kylä &3_CITY_SYLL joki &4_CITY_SYLL linna &5_CITY_SYLL metsä &6_CITY_SYLL laakso &7_CITY_SYLL keto &8_CITY_SYLL kulma &9_CITY_SYLL niitty &A_CITY_SYLL pohja %1_CITY_SYLL Uusi %2_CITY_SYLL Vähä %3_CITY_SYLL Peruna %4_CITY_SYLL Viher %5_CITY_SYLL Armon %6_CITY_SYLL Omena %7_CITY_SYLL Oja %8_CITY_SYLL Lahti %9_CITY_SYLL Vanha %A_CITY_SYLL Kivi %B_CITY_SYLL Kauko 1center %s taajama %s 1extern %s haara %s 2center %s keskusta %s 2extern %s sivu %s 3center %s kylä %s 3extern %s maaseutu %s 4center %s sisus %s 4extern %s ulkoinen %s 5center %s risteys %s 6center %s keskus %s 7center %s kaupunki %s simutrans-124.5/simutrans/text/fi/000077500000000000000000000000001520675460500171735ustar00rootroot00000000000000simutrans-124.5/simutrans/text/fi/new_world.txt000066400000000000000000000107511520675460500217400ustar00rootroot00000000000000Uusi peli -ohje

Uusi peli

Uusi peli asettaa uuden pelin ominaisuudet.
Muokattavia asetuksia ovat mm. maaston koko ja kaupunki- ja teollisuusalueiden määrä pelin alussa. Asetusten mukaisesta uudesta kartasta näkee esikatseluversion.

Uusi peli avautuu yhdessä Kielet- ja Ilmaston asetukset -ikkunoiden kanssa, kun Simutrans käynnistyy. Sen saa esille myös Peliasetukset-kuvakkeesta (tällöin muut työkalut, ikkunat ja tekstit sulkeutuvat).
Pikanäppäimiä käytettäessä Uusi peli avautuu näppäimillä [Q] and [X].

Palataksesi nykyiseen peliin Uusi peli-ikkunasta klikkaa rastia vasemmassa yläkulmassa.

Nuolipainikkeet säätävät asetuksia ja painikkeet vaihtavat päälle/pois tai avaavat uusia asetuksia:

Kartan numero: valitsee pelissä käytettävän kartan numeron.
Klikkaa numerolaatikkoa ja kirjoita haluamasi numero tai käytä nuolipainikkeita siirtyäksesi eri maastovaihtoehtojen välillä.
Esikatselukuva näyttää valitun maaston (vesi on sinistä, maa on vaaleampaa korkeammilla kohdilla).

Kartan koko: asettaa kartan sivujen pituudet (ylempi arvo on leveys, alempi korkeus). Suuret kartat vaativat enemmän muistia ja niiden luominen kestää pidempään. Esim. tietokoneella, jossa on 256 Mt RAM-muistia, on kokoa 512 x 512 oleva kartta käytännössä maksimi sujuvaan peliin.

Arvo kartta: valitsee satunnaisen kartan l. kartan numeron. Muut asetukset säilyvät ennallaan.

Lataa korkokuva: avaa uuden ikkunan valmiiksi tehtyjen maastopohjien lataamiseksi.

Kaupunkien määrä: asettaa kaupunkien lukumäärän pelin alussa.

Asukkaita keskimäärin: asettaa kaupunkien väkiluvun pelin alussa.
Tämä on keskiarvo; Simutrans pyrkii luomaan enemmän pieniä kyliä kuin suuria kaupunkeja.

Maanteiden pituus: asettaa kaupunkienvälisten teiden pituuden.

Liikennetiheys: asettaa henkilöautojen määrän kaupungeissa.
Korkeat arvot luovat enemmän yksityisliikennettä, 0 ei yhtään. Väestön suuretessa ilmaantuu uusia autoja.

Tehtaita: asettaa tuotantoketjujen määrän pelin alussa.
Jos maasto on hyvin mäkistä, voi lopullinen määrä jäädä pienemmäksi.

Sähköntuotto: asettaa voimaloiden enimmillään tuottaman sähkön määrän prosentteina muiden tehtaiden enimmillään tarvitsemasta määrästä.
Useiden tehtaiden ja laitosten tuotantoa voidaan tehostaa kytkemällä ne sähköverkkoon.

Turistikohteita: asettaa turistaa ja postia puoleensavetävien nähtävyyksien määrän pelin alussa.
Nämä esiintyvät kaupunkialueiden ulkopuolella eivätkä ole riippuvaisia kaupunkeihin luotujen nähtävyyksien määrästä. Nähtävyyksien lähistöltä syntyy myös turistien ja postin paluuliikennettä.

Käytä aikalinjaa vuodesta: asettaa pelin aloitusvuoden. Kun neliöpainike on alhaalla, aikalinja on valittu ja pelin ajan kuluessa voi ilmaantua uusia ajoneuvo- ja rakennusvaihtoehtoja.
Kun neliöpainike on alhaalla ja aikalinja siis pois päältä, peli alkaa yhä valittuna vuonna, mutta kaikki ajoneuvot ja rakennukset ovat käytettävissä välittömästi. Kaupungeissa on tällöin rakennuksia usealta aikakaudelta ja hitaammat ajoneuvot eivät ole yhtä tuottoisia kuin muuten (kuljetuspalkkioihin vaikuttaa kulloinkin saatavilla olevien ajoneuvojen keskinopeus).

Salli pelaajan vaihtaminen: antaa mahdollisuuden ohjata tekoälyn(AI) tai julkiset palvelut -pelaajan yhtiötä (asetusta ei voi vaihtaa pelin alettua).

Lataa peli: avaa uuden ikkunan, jossa voi avata tallennetun pelin. Nykyinen (tallentamaton) peli menetetään.

Aloita peli: luo uuden pelimaailman valituilla asetuksilla. Klikkaa aloittaaksi - onnea matkaan ja nauti pelistä!

{Vinkki: Simutransin käynnistyessä näytettäviä oletusarvoja voi muokata tiedostossa simuconf.tab}

simutrans-124.5/simutrans/text/fp.tab000066400000000000000000000257411520675460500177030ustar00rootroot00000000000000§Arpitan PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: frp Arpitan # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 2.06 2024 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable dèsactivâ cl_btn_filter_enable activâ cl_btn_filter_settings règllâjos cl_btn_sort_asc crèssant cl_btn_sort_desc dècrèssant cl_btn_sort_id Identificacion intèrna cl_btn_sort_income Bènèfiço cl_btn_sort_name Nom cl_btn_sort_type Tipo clf_btn_alle tôt clf_btn_invers envèrsar clf_btn_keine nyun gl_btn_sort_bonus pèr prima gl_btn_sort_name Pèr nom gl_btn_sort_revenue Pèr bènèfiço gl_btn_unsort Dèsordonâ hl_btn_filter_disable dèsactivâ hl_btn_filter_enable activâ hl_btn_filter_settings Règllâjos hl_btn_sort_asc crèssent hl_btn_sort_desc dècrèssent hl_btn_sort_name Nom hl_btn_sort_type Tipo hl_btn_sort_waiting Atenta hlf_btn_alle tôt hlf_btn_invers envèrsar hlf_btn_keine nyun Networks Têla de transpôrt Queueing Coa d'atenta Scenario Informacions de mission Transfers Travèrsant #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic climâ arctico desert Climâ dèsèrtico mediterran Climâ mèditèrranèen rocky climâ montanyârd temperate climâ tempèrâ tropic climâ tropicâl tundra climâ de tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Cannot built depot here! Impossiblo de consture\nun dèpot iqui. Cannot built this station/building\nin underground mode here. Impossiblo de construre un\b\nbâtiment dessos la tèrra.\n Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Pas possiblo de\n\nconstrure d'aèropôrt iqui.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Pas possiblo de\nconstrure de sinyâl iqui.\n In order to lock the game, you have to protect the public player by password! Pèr vèrrolyér la partia, vos devéds protègér lo joyòr avoèc un contresìnyo! #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Highlight railroad tracks Têla fèrrovièra salyents Highlite depots Dèpôs salyents Highlite electrical transmission lines Linyes èlèctriques salyentes Highlite factories Favèrges salyentes Highlite forests Jors salyentes Increase water height Ôgmentar l'égua d'un nivél #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (u dèpot) [END] [FIN] [HOME] [POS1] \nBauzeit bis tant qu'a AIRTOOLS Otìlyâjo d'aèropôrt All Tôt Apply Line Aplicar la linye April Avrily Arbeiter aus: Travalyòrs de: Arrivals from\n Arrivâ de\n Arrived Arrivâ Assets Bens Aufloesen Dèsassemblar Aufspanntransformator Tansfòrmatòr August Ót Available Disponiblo Bahndepot Depot fèrrovièro Bankrott:\n\nDu bist bankrott.\n Bancarota:\n\n\nVos êtes en falyita!\n battery Bateria Baum Âbro baum builder Plantar un âbro Baustelle Sito de\n\nconstruccion Bauzeit Data de construccion Besonderes Gebaeude Atraccion toristica BF Gâra bio biologico Blockstrecke ist\nbelegt\n Plôt de ralys\nutilisâ pèr\nun âtro tren\n Boden Tèrra Bonusspeed: %i km/h Vèlocitâ maxima possibla: %i km/h Boost (%%) Ôgmentar la productivitâ bridge is too high for its type! Cèti pont est trop yôt por son tipo! Bridge is too long for this type!\n Cèti pont est trop long por son tipo. Bruecke Pont Brueckenboden Pont Build air depot Construre una remisa build choosesignals Construre un sinyâl de dicèrnement de plataforma Build city market Construre un novél marchérdiens la citâ la més prôche. Build drain Transfòrmatòr build HQ Construre la bâsa d'operacions Build land consumer Construre una centrâla èlèctrica. Build maglev depot Construre un dèpot de tren a èmant Build monorail depot Construre un dèpot de monoraly Build narrowgauge depot Construre un dèpot a vi èhtrêta Build powerline Construre una linye èlèctrica Build presignals Construre un sinyâl avanciê Build road depot Construre un garâjo Build ship depot Construre una câla Build signals Construre un sinyâl Build train depot Construre un dèpot de trens Build tram depot Construre un dèpot de tramevèts Build truck depot Construre un garâjo Building costs estimates Èstimacion de la construccion Buildings Construre un bâtiment Built artifical slopes Construre de bârmes Built random attraction Construre una novèla atraccion Cancel Anular Cannot connect to offline server! Conèxion impossibla u sèrvòr sur la têla! Capacity: Capacitâ Capacity: %.0f MW Capacitâ: %.0f MW\n Capacity: %d%s %s\n Capacitâ: %3d%s %s\n cars.\nstate vêtures\n Cash Balance Change player Changér de joyòr Chart Grafico Chat_msg Barjacâjo Choose direction Sèleccionar la dirèccion chooses a random map Sèlèccionar una mapa alèatoèra. citicens Citoyens City attraction Atraccion de la citâ City industries Favèrges diens les citâs City list Lista de citâs City size Talye de la citâ citybuilding builder Construccions diens la citâ CityLimit Limites de la citâ cl_title Lista de vèyicllos cl_txt_sort Dicèrnar pèr: clf_chk_aircrafts avions clf_chk_indepot diens un dèpot clf_chk_maglev Tren a èmant clf_chk_monorail Monoraly clf_chk_name_filter Filtro pèr nom: clf_chk_narrowgauge Trens a vi èhtrêta clf_chk_noline Sens linye clf_chk_noroute Sens chemin clf_chk_noschedule Sens itinèrèro clf_chk_obsolete Obsolèto clf_chk_ships batiôs clf_chk_spezial_filter Filtro spèciâl: clf_chk_stucked blocâ clf_chk_trains Trens clf_chk_trams Tramevèts clf_chk_type_filter Filtro pèr tipo: clf_chk_waren Filtro pèr charâ: clf_title Filtro vèyicllo Climate Control Règllâjos du climâ closed fèrmâ. Construction_Btn Côte de construccion December Dècembro Depots Dèpots Display settings Règllâjo d'afichâjo Dock Pôrt electric èlèctrico Electricity Èlècrticitâ Electricity producer\n\n Productòrs d'èlèctricitâ\n\n Electrify track Èlèctrifiar les vis enlarge map Ôgmentar la mapa February Fèvriér follow me Vens avoèc mè Forest Jor Found new city Fondar una novèla citâ FPS: Vindâjo d'ìmâges Fracht Frêt Frame time: Dèlê: Free Capacity Capacìtâ rèsta freeplay mode Sens bancarota Friction: Valyòr de friccion fuel_cell pila combustiblo Full load Charâ minima Fundament Fondacion Fussgaenger Pedon Game info Informacion de partia GAME PAUSED JUÈ EN ARRÉTA Game_msg G·ènèral Gear: Engrenâjo: Gebaeude Construccion General G·ènèral Generated G·ènèrâ Generation: %.0f MW G·ènèracion: %.0f MW\n Gewicht Pês Gewinn Renta: Give the selected vehicle(s) an individual schedule Balyér u(s) vèyiclo(s) sèlèccionât(s) un itinèrèro individuèl gl_btn_sort_catg Pèr catègori gl_title Lista de bens go home U dèpô Goods Bens Goods AI Charâ IA Goods list Lista de bens Gross Profit Bènèfiço lôrdo Groundobj Objèt Grow city Agrantir la citâ. Growth Crèssua citâ H avri Hangar Remisa Happy Content Haus kaufen Achetar la mêson Helligk. Afichâjo Help Èda Help text not found Lo tèxto d'èda fôte. hide all building cachér totes les construccions hide city building cachér les construccions de citâ hide objects under cursor cachér los èlèments sos lo cursòr hide station names cachiér los noms de stacion hide transparent transparents, pas chachiês hide trees cachér los âbros Hier warten/lagern: Passagérs e charâ en atenta: hl_title Lista de stacions hl_txt_filter Filtros: hl_txt_sort Ordonar pèr: hlf_chk_airport Aèropôrts hlf_chk_anleger Pôrt hlf_chk_bahnhof Gâra hlf_chk_frachthof Zona de charâ hlf_chk_keine_verb Sens conèxion hlf_chk_maglevstop Gâra de tren èman hlf_chk_monorailstop Gâra de monoraly hlf_chk_name_filter Filtro pèr nom: hlf_chk_narrowgaugestop Gâra de vi èhtrêta hlf_chk_overflow Saturâ hlf_chk_spezial_filter Filtro spèciâl: hlf_chk_tramstop Avri de tramevèt hlf_chk_type_filter Filtro pèr tipo: hlf_chk_waren_abgabe Produccion: hlf_chk_waren_annahme Matiére premiéra: hlf_title Filtro de nom de stacion Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Impossiblo de trovar lo dèpô.\nVos devéds i mandar\nlo vèyiclo manuèlment. Homeless Galapian hydrogene Hidrog·ènë Idle: Inactivo: ignore climates Inyorar lo climâ In the industry legend show only currently existing factories Ót In Transit en transito Increase Industry density Ôgmentar la densitâ de favèrges increase underground view level Sèrrar industrial building Construccion de favèrge Init map ... Après iniciar la mapa... Input Entrâ Ins Stop Apondre Intro. date: Data d'introduccion: invalid invalido. isometric map Mapa isomètrica January Janviér join game Partia sur la têla July Julyèt Jump to Alar a June Joen Kann Spielstand\nnicht laden.\n Impossiblo d'ovrir\nlo document enrègistrâ!\n Kann Spielstand\nnicht speichern.\n Impossiblo d'èhcrire\ndiens lo document enregistrâ!\n Kein Besitzer\n Sens propriètèro\n keine nyun Keine Einzelfahrzeuge im Depot Nyun vèyicllo u dèpot Keyboard_Help\n Èda de claviér\n koord Coordonâs Kreuzung Croesement labellist_title Lista de marcòrs Lade Relief Chargér una mapa d'altituda Laden Chargér un novél juè Land attraction Atraccion rurâla Land industries Chêna de favèrges Load game Chargér una partia load height data from file Chargiér un document de mapa d'altituda. Load scenario Chargér una mission loaded chargiê loaded passenger/freight Dicèrnar los passagérs e lo trèt pèr Loading (%i->%i%%)! Chargiment (%i->%i%%) March Mârs May Mê Narrowgauge Vi èhtrêta Narrowgauge are not available yet! La vi èhtrêta n'est p'oncor disponibla ora. nord Nôrd November Novembro Oktober Octobro Optionen Opcions September Sèptembro Sprache lengoua Sprachen Lengoues Start Dèpârt Starte Spiel Comencér simutrans-124.5/simutrans/text/fr.tab000066400000000000000000001346611520675460500177070ustar00rootroot00000000000000§Francais PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: fr Francais # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Affichage des heures sous forme de jour:hh:mm du mois AMBIENT_SOUND Sons d'ambiance cl_btn_filter_disable désactivé cl_btn_filter_enable activé cl_btn_filter_settings réglages cl_btn_sort_asc croissant cl_btn_sort_desc décroissant cl_btn_sort_id ID interne cl_btn_sort_income Revenu cl_btn_sort_name Nom cl_btn_sort_type Type clf_btn_alle tout clf_btn_invers inv. clf_btn_keine rien climate area percentage Taille d'une région climatique Continue Game Continuer Partie Find matching convois Trouver les convois correspondants gl_btn_sort_bonus par bonus gl_btn_sort_name par noms gl_btn_sort_revenue par recette gl_btn_unsort non trié hl_btn_filter_disable désactivé hl_btn_filter_enable activé hl_btn_filter_settings réglages hl_btn_sort_asc croissant hl_btn_sort_desc décroissant hl_btn_sort_name Nom hl_btn_sort_type Type hl_btn_sort_waiting Attente hlf_btn_alle tout hlf_btn_invers inv. hlf_btn_keine rien humidities Limites d'humidité Install Installer paks Lake lac Networks Réseau de transport Open Sea mer Queueing Tendance d'attente Reselect closes tools Re-sélection d'un outil le ferme Return to menu Retour au menu Road toll Péages routiers Scenario Scénario Transfers Traversant #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic climat arctique desert climat désertique mediterran Climat méditerranéen rocky montagneux temperate Climat tempéré tropic climat tropical tundra Toundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Le chargement du scénario a échoué Can't buy obsolete vehicles! Impossible d'acheter des véhicules obsolètes Cannot alter water Le niveau d'eau du bassin ne peut pas être modifié Cannot built depot here! Un dépôt ne peut pas\nêtre construit ici. Cannot built this station/building\nin underground mode here. Ce bâtiment ne peut pas\nêtre construit sous terre.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. Impossible de créer une ligne générique!\nSélectionnez un type de ligne\navec les onglets. Cannot create socket Impossible de créer un socket Convoi handles exhausted! Nombre maximum de convois atteint Convoy already deleted! Convoi déjà supprimé Das Feld gehoert\neinem anderen Spieler\n Ce terrain appartient\nà un autre joueur!\n Der Besitzer erlaubt das Entfernen nicht Le propriétaire\nne permet pas\nde retirer ceci.\n Diese Zusammenstellung kann nicht fahren!\n Cette combinaison\nne fonctionne pas!\n Flugzeughalt muss auf\nRunway liegen!\n Les aérogares doivent\nêtre sur une taxiway. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Vous ne pouvez pas\nconstruire d'aéroport\nici. Hier kann kein\nSignal aufge-\nstellt werden!\n Impossible de\nconstruire un\nsignal ici!\n In order to lock the game, you have to protect the public player by password! Pour verrouiller le jeu, vous devez protéger le service public par un mot de passe ! Lost connection\nto server! Connexion au\nserveur perdu!\n Lost synchronisation\nwith server. Perte de synchronization\navec le serveur. Maglevhalt muss auf\nMaglevschiene liegen!\n Un arrêt de maglev doit être placé sur des voies de maglev Monorailhalt muss auf\nMonorail liegen!\n Un arrêt de monorail doit être posé sur une voie de monorail. Monorails are not available yet! Les monorails ne sont pas encore disponibles. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Arrêt ou gare doit être sur une voie étroite! No bridges over runways! Ne peut pas construire de ponts au-dessus des pistes! No suitable way on the ground! Les arrêts ne peuvent être construits\nseulement sur le niveau droit correspondant aux voies No through station here! Impossible de placer\nun arrêt ici ! Not enough clearance. Une différence minimale de hauteur de 2 est requise pour construire des voies superposées Not enough money! Vous n'avez pas assez\nd'argent pour construire! On narrowgauge track only!\n Seulement pour les voies étroites!\n Only public player can lock games! Seul le service public peut verrouiller le jeu Out of funds Vous n'avez pas assez d'argent pour faire cela ! Post muss neben\nHaltestelle\nliegen!\n Une extension doit\nêtre placée près\nd'un arrêt ou gare!\n Protocoll error (expecting game) Erreur de protocole Schiffhalt muss im\nWasser liegen!\n Un arrêt de\nbateau peut\nseulement être\nplacé sur l'eau!\n Server busy Le serveur est occupé! Terraforming not possible\nhere in underground view Transformation du terrain impossible sous-terre. Upgrade must have\na higher level Mise à jour doit avoir un niveau plus élevé. Vehicle %s cannot choose because stop too short! %s ne peut pas choisir: tous les arrêts sont trop courts! Zughalt muss auf\nSchiene liegen!\n Un arrêt de train\npeut seulement\nêtre placé\nsur un rail !\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Aide

Contenu

*n'existe qu'en version anglaise

Simutrans en général

%1$s

Aide à la manipulation

%2$s

Engager un jeu

%4$s

Comment ça se passe ?

%5$s

Outils de gestion

%3$s

Dialogues supplémentaires

%6$s Keyboard Help\n

Keyboard Help

\n Commandes au clavier\n

Configuration :

\n9 - Déplacer la carte vers le nord-est
\n1 - Déplacer la carte vers le sud-ouest
\n7 - Déplacer la carte vers le nord-ouest
\n3 - Déplacer la carte vers le sud-est
\n6 - Déplacer la carte vers l'est
\n2 - Déplacer la carte vers le sud
\n8 - Déplacer la carte vers le nord
\n4 - Déplacer la carte vers l'ouest
\nDelete - Fermer l'écran actuel
\nBackspace - Fermer tous les écrans
\n

Color-coded terrain according to altitude. Change la couleur du terrain en fonction de l'altitude Decrease water height Baisser le niveau d'eau du bassin de 1 Highlight railroad tracks Surligner le réseau ferroviaire Highlite depots Montrer les dépôts. Highlite electrical transmission lines Surligner les lignes électriques Highlite factories Surligner les fabriques. Highlite forests Afficher les zones boisées Highlite tourist attraction Montrer les attractions touristiques Increase water height Augmenter le niveau d'eau du bassin de 1 Overlay city limits Afficher les frontières de la ville Overlay passenger destinations when a town window is open Afficher les trajets des passagers lorsque la ville est visible Overlay schedules/network Afficher les cadences et le réseau Overlay town names Afficher le nom des villes Please click on the map to add\nwaypoints or stops to this\nschedule. Cliquer sur la carte pour ajouter\ndes points de passage ou des arrêts pour cet\nitinéraire Set tile climate %s Transforme ce terrain en climat %s Show capacity and if halt is overcrowded Afficher la capacité et si l'arrêt est surchargé Show how many convoi reach a station Afficher combien de convois atteignent un arrêt Show how many people/much is waiting at halts Afficher combien de passagers attendent Show initial passenger departure Montrer les sources de départ de passagers Show level of city buildings Afficher la densité de passagers par bâtiment Show mail service coverage/mail network Afficher La couverture et le trafic des messages Show passenger coverage/passenger network Afficher l'état moyen du trafic des passagers Show speedlimit of ways Voir les limitations de vitesse des routes Show the change of waiting at halts Afficher les variations d'attentes aux arrêts Show the owenership of infrastructure Afficher les différents propriétaires des infrastructures Show transported freight/freight network Afficher le trafic du fret de marchandises Show usage of network Afficher la charge du trafic Shows a listing with all industries on the map. Afficher la liste de toutes les industries sur la carte Sum of departure/arrivals at halts Total des arrivées/départs des arrêts #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s Offre maintenant un service de bus\nentre %s\net l'attraction\n%s\nà (%i;%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s Offre maintenant un service de bus entre %s et l'usine %s à (%i,%i). %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s Maintenant %i camion(s) roule entre %s à (%i,%i) et %s à (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s a ouvert une nouvelle voie de chemin de fer entre %s à (%i,%i) et %s à (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n %s\nouverture d'une nouvelle ligne\naérienne entre \n%s \net %s.\n\n Ferry service by\n%s\nnow between\n%s \nand %s.\n %s\nouverture d'une nouvelle ligne\nde ferry entre\n%s et \n%s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Les voyageurs utilisent maintenant les bus de %s entre %s et %s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Outils d'édition de carte LISTTOOLS Gestion des listes MAGLEVTOOLS Outils Maglev MONORAILTOOLS Outils monorails NARROWGAUGETOOLS Construire voies étroites RAILTOOLS Outils ferroviaires ROADTOOLS OUTILS ROUTIERS SHIPTOOLS Transport maritime et fluvial SLOPETOOLS Modification du terrain SPECIALTOOLS Outils Constructions spéciales TRAMTOOLS Outils trams #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s's a construit un nouveau quartier général. Factory chain extended\nfor %s near\n%s built with\n%i factories. L'économie est florissante :\n%s à côté de %s s'étend.\n%i nouvelles industries sont fondées. New %s now available:\n%s\n Nouveau %s disponible:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Croissance industrielle pour\n%s à côté de\n%s :\n%i nouvelles usines\nsont construites. New vehicle now available:\n%s\n \n Un nouveau véhicule\n est maintenant disponible:\n\n\n » %s «\n\n Now %u clients connected. %i clients branchés à cet instant.\n\n Remove vehicle from map. Use with care! Supprimer un véhicule. A utiliser avec précautions! Screenshot\ngespeichert.\n Capture d'écran\nsauvegardée.\n Sends the convoi to the last depot it departed from! Envoie le convoi au dernier dépôt qu'il a quitté. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s a construit un nouveau monument. %i citoyens ont été réjouis. #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%.2f$/km %.2f$/m) (%.2f$/km %.2f$/mois) (%.2f$/km) (%.2f$/km) (%i)- (%i) (in depot) (au dépôt) /month /mois \nBauzeit bis jusqu'en \nBauzeit von \nApparaît dès \nCan't open heightfield file.\n \nimpossible d'ouvrir le\nfichier de dénivelé.\n \ndirection: \ndirections: \nelektrified \nélectrifié\n \nHeightfield has wrong image type.\n \nLe dénivelé n'est pas\nau bon format d'image.\n \nis reserved by: \nréservé par \nminimum speed: \nvitesse minimale \nnot elektrified \nnon électrifié\n \nRibi (masked) \n (masqué): \nRibi (unmasked) \nRibi\n\n (non masqué): \nSet phases: \nAjuster temps ns/ew:\n\n \nsingle way \nVoie unique \nway1 reserved by voie 1 réservée par\n \nway2 reserved by voie 2 réservée par\n \nwith sign/signal\n \navec signe/signal\n %d buildings\n %d bâtiments\n %d Einzelfahrzeuge im Depot %d véhicules dans ce dépôt %i years %i months old. âgé de %i ans et %i mois. %s at (%i,%i) now public stop. %s at(%i,%i) est maintenant un arrêt public %s building %s %s %s %s %s %s city %d %s %s ville %d %s %s factory %s %s %s %s m %s has entered a depot. %s est entré au dépôt. %s land %d %s %s terrain %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s \na construit un\nnouvel hôtel\nde ville quand\nelle a atteint\n%i habitants. %s\nis crowded. %s\nest pleine. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\nvitesse actuelle %i\nvitesse maximale %i\ndx:%i dy:%i %s\nwas liquidated. %s\n a fait faillite. Toutes les activités ont été stoppées. %u Client(s)\n %i client(s)\n %u Player (%u locked)\n %u Joueurs (%u avec mot de passe)\n pas de ligne/itinéraire 1 Einzelfahrzeug im Depot 1 véhicule dans ce dépôt 1LIGHT_CHOOSE Luminosité: 1WORLD_CHOOSE Paramètres du nouveau jeu: 2LIGHT_CHOOSE Couleur: 2WORLD_CHOOSE Carte N°: 3LIGHT_CHOOSE Défilement: 4LIGHT_CHOOSE Défilement inversé 5LIGHT_CHOOSE Piétons aux arrêts 5WORLD_CHOOSE Nb villes: 6LIGHT_CHOOSE Flâneurs 6WORLD_CHOOSE Densité du trafic : 8WORLD_CHOOSE Mode jour/nuit A bridge must start on a way! Les ponts doivent commencer\nà une section existante. Abfrage Inspection Abnehmer Client About À propos About scenario Copyright Abriss Détruire Absenken Abaisser le sol Abspanntransformator Transformateur Accelerate time Accélérer le temps Act. load: %u MW\n Charge: %u MW\n Active player only seulement joueur actif Add forest Créer une forêt Add random citycar Générer une automobile add server Ajouter un serveur Add Stop Aj. Arrêt Add stops for backward travel Ajouter des arrêts pour le retour air Piste de décollage/Voie de circulation aircraft_tab Avions cargo airplane avion Airport Aéroport AIRTOOLS Outils Aviation All Tout all convoi tooltips Info-bulles convoi:toutes Allow city growth Permettre l'agrandissement des villes Allow player change Autoriser le changement de joueurs allowed climates:\n Climats autorisés: Alters a schedule. Modifier l'itinéraire Angenommene Waren Produits requis par les industries proches anhaengen Attacher Anhaenger_tab Remorques Anheben Elever le sol Appends stops at the end of the schedule Ajouter un arrêt à la fin de l'itinéraire Apply Line Assoc. ligne April Avril Arbeiter aus: Employés de : Arrivals from\n Arrivée depuis\n Arrived Arrivé Assets Biens Aufloesen Désassembler Aufspanntransformator Transformateur August Août Autohalt muss auf\nStrasse liegen!\n Un arrêt de bus doit être placé sur une route. Available Disponible Available at custom date Disponible à partir de Bahndepot Dépôt ferroviaire Bankrott:\n\nDu bist bankrott.\n Banqueroute:\n\nVous êtes en faillite.\n battery Batterie Baum Arbre baum builder Planter un arbre Baustelle Site de\nconstruction Bauzeit Época: Beenden Quitter Beginner mode Mode débutant Besonderes Gebaeude Attraction touristique BF gare Blockstrecke ist\nbelegt\n Bloc de rails\nutilisé par\nun autre train\n Boden Terrain Bonusspeed: %i km/h Vitesse maximale possible : %i km/h (pour le bonus de vitesse) Boost (%%) Augmentation de productivité (%%) bridge is too high for its type! Ce pont est trop haut pour ce type de pont ! Bridge is too long for this type!\n Ce pont est trop long pour ce type de pont! Bruecke Pont Bruecke muss an\neinfachem\nHang beginnen!\n Le pont doit\ncommencer sur\nune pente droite!\n Brueckenboden pont Build air depot Construire un hangar build choosesignals Construire un signal de choix de quai Build city market construire un nouveau marché dans la ville la plus proche Build drain Transformateur build HQ Construire HQ Build land consumer construire une nouvelle centrale électrique Build maglev depot Construire un dépôt de Maglev Build monorail depot Construire un dépôt pour monorail Build narrowgauge depot Construire un dépôt à voie étroite Build powerline Ligne à haute tension Build presignals Construire un signal avancé Build road depot Construire un dépôt routier Build ship depot Construire une gare maritime Build signals Signalisation Build train depot Dépôt de trains Build tram depot Construire un dépôt de tram Build truck depot Dépôt routier Building costs estimates Estimation des coûts de construction Buildings Bâtiments Built artifical slopes Construire des pentes artificielles Built random attraction Construire une attraction choisie au hasard. Bus_tab Bus Can only move from halt to halt or waypoint to waypoint. Peut seulement se déplacer \nd'arrêt en arrêt ou de \npoint de passage en point de passage. Cancel Annuler Cannot connect to offline server! Connection impossible, serveur hors ligne ! Capacity: Capacité Capacity: %.0f MW Capacité: %.0f MW\n\n\n Capacity: %d%s %s\n Capacité: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Capacité: %s\n» %d (%d%%) Cars are not available yet! Les voitures ne sont pas encores disponibles! cars.\nstate voitures\n Change player Changer de joueur Chart Diagramme Chat_msg Chat Choose direction Choisisez la direction Choose operation executed on clicking stored/new vehicles Choisissez l'opération au clic sur les véhicules stockés/neufs chooses a random map Choisir une carte au hasard. citicens Citoyens City attraction Attraction urbaine City industries Marchés dans les villes City list Liste des villes City size Taille de la ville city_road Rue de la ville citybuilding builder Construction urbaines CityLimit limite ville cl_title Liste de véhicules cl_txt_sort trier par: Clear block reservation Afficher/cacher block reservations clf_chk_aircrafts avions clf_chk_cars Bus/camions clf_chk_indepot dans un dépôt clf_chk_maglev Maglevs clf_chk_monorail Monorails clf_chk_name_filter Filtre par nom: clf_chk_narrowgauge Trains à voies étroites clf_chk_noincome pas de revenu clf_chk_noline pas de ligne clf_chk_noroute pas de chemin clf_chk_noschedule pas d'itinéraire clf_chk_obsolete Obsolète clf_chk_ships bateaux clf_chk_spezial_filter Filtre spécial: clf_chk_stucked bloqué clf_chk_trains Trains clf_chk_trams Tramways clf_chk_type_filter Filtre par type: clf_chk_waren Filtre marchandises: clf_title Filtre véhicule Climate Climat Climate Control Contrôle Du Climat closed fermé. COLOR_CHOOSE\n Veuillez choisir\nune couleur\ndu tableau:\n Company bankrupt Companie en faillite! Company_msg Concurrents Comparing pak files ... Comparer les fichiers des paks... Configure AI Configurer l'IA Configure AI setttings Configurer les paramètres de l'IA Congratulation\nScenario was complete in\n%i months %i years. Félicitation!\nLe scénario a été \ncomplété en \n%i mois et %i années! Connected stops Arrêts connectés Connected with server Branché au serveur Constructed by Dessiné par Constructed by %s Dessiné par %s construction speed vitesse de construction Construction_Btn Construction Consumed Consommé convoi %d of %d convoi %d sur %d convoi error tooltips Info-bulles convoi:erreurs Convoi has been sent\nto the nearest depot\nof appropriate type.\n Le convoi a été envoyé\nau dépôt - du type\napproprié - le plus\nproche. Convoi is sold when all wagons are empty. Le véhicule sera vendu une fois qu'il sera complètement vide. convoi mouseover tooltips Info-bulles convoi:mouseover convoi passed last\nmonth %i\n Nombre de convois\npassés par ici le mois\ndernier : %i\n Convois Convoi Convois: %d\nProfit: %s Véhicule: %d\nProfit: %s Convoys Convois Copy Convoi Copier le convoi Copy the selected convoi and its schedule or line Copier le convoi sélectioné ainsi que son itinéraire ou sa ligne cost for removal cout de remplacement: Cost: %8s (%.2f$/km %.2f$/m)\n Coût: %8s (%.2f$/km, %.2f$/mois)\n Cost: %8s (%.2f$/km)\n Coût : %8s (%.2f$/km)\n Costs Coûts Create a new line based on this schedule Créer une nouvelle ligne basée sur cet itinéraire curiosity builder Construire une attraction curlist_title Liste des attractions CURRENCY € Currently playing: Jouant: Customers live in: Clients de : deactivated in online mode désactivé en mode online Deccelerate time Ralentir le temps December Décembre decrease underground view level réduire le champ de la vue souterraine Del Stop Sup. Arrêt Delete Line Sup. Ligne Delete the current stop Supprimer l'arrêt actuel Delete the selected line (if without associated convois). Supprimer la ligne sélectionnée (si aucun convoi ne l'utilise) Delete this file. Détruire ce fichier. Delivered Délivrés Demand Demande Demand: %.0f MW Demande: %.0f MW\n Denkmal Monument Departed Parti Departure after %% chargement ou départ après (jj-hh-mm) Departure board Tableau des départs Departures to\n Départ pour\n Depots Dépôts Der Tunnel ist nicht frei!\n Le tunnel est occupé. Destroying map ... Destruction ancienne carte ... Details Détails Die Bruecke ist nicht frei!\n Le pont est occupé! Direkt erreichbare Haltestellen Gares/arrêt(s) connecté(s) disable midi Arrêter la musique MIDI Display settings Réglages d'affichage Distance distance Dock quai Dock must be built on single slope! Les quais doivent être construits sur une pente ! dp_title Liste des dépots Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Vous avez %d mois pour recouvrir votre dette. Durchsatz Prod. max. Economy Économie et villes Eigenbesitz\n Prop. publique\n Ein %s\npasst hier nicht.\n Un(e) '%s'\nne tiend pas ici.\n Einstellungen aendern Modifier les options electric électrique Electricity Electricité Electricity producer\n\n Producteur d'électricité\n\n Electrics_tab Electrique Electrify track Électrifier les voies enlarge map Elargir la carte enter a value between %i and %i Entrez une valeur entre %i et %i Enter address Entrer l'adresse Enter Password Entrer un mot de passe Error Erreur Erzeuge neue Karte.\n Patientez S.V.P.,\ncréation d'une\nnouvelle carte?\n\n (Avec une grande\ncarte cela peut durer\nquelques minutes)\n Es wird bereits\nein Fahrplan\neingegeben\n Un trajet est\nen cours de\nprogrammation.\nTerminez-le d'abord\navantde le refaire!\n Fabrikanschluss Fabrique(s) liée(s) Fabrikname nom de fabrique Factories Fabriques factory details Détails fabriques factorybuilder Construction de fabriques Fahrplan Itinéraire Fahrtziel Destination: Fahrzeuge koennen so nicht entfernt werden Les véhicules ne\ndoivent pas être\nretirés de\ncette façon.\n Fahrzeuge: Véhicules: Farbe Couleur joueur Fast forward Avance rapide February Février Ferry_tab Ferrys Fertig Fin Filename Nom fichier: Filter: Filtre: Finances of %s Finances de %s Finanzen Finances find mismatch comparaison de paks fl_title liste des fabriques Flug_tab Avions de passagers follow me Suivez moi. Follow the convoi on the map. Suivre le convoi sur la carte. Forest forêts Found new city Créer une nouvelle ville Fracht Chargement Frame time: Image Time: Free Capacity Capac. restante freeplay mode Mode jeu libre Friction: facteur de friction actuel: fuel_cell pile à combustible Full load Attendre un chargement minimal Fundament Fondations Fussgaenger Piéton GAME PAUSED JEU EN PAUSE Game_msg Général Gear: Couple: Gebaeude Construction General Général Generated Généré Generation: %.0f MW Génération: %.0f MW\n Gewicht Masse Gewinn Revenu Give the selected vehicle(s) an individual schedule Donner au(x) véhicule(s) sélectionné(s) un itinéraire individuel gl_btn_sort_catg Catégorie gl_title Liste de tous les biens go home Au dépôt ! Goods Biens Goods AI Fracht-KI Goods list Liste des biens Gross Profit Profit Groundobj Objet du sol Grow city Agrandir ville Growth Croiss. ville H arrêt Happy Content Haus kaufen Acheter la maison Helligk. Affichage Help Aide Help text not found Le texte d'aide est manquant. hide all building cacher tout bâtiment hide city building cacher bâtiments urbains hide objects under cursor masquer les objets sous le curseur hide station names Cacher nom des arrêts hide transparent transparent au lieu de caché hide trees Cacher les arbres Hier warten/lagern: En attente de chargement: Higher transport fees, crossconnect all factories Pour plus de rentrée d'argent, désactiver le "just in time" (arrêt de production en cas de saturation) highlight single ways Afficher les fins/interruptions des itinéraires de circulation. Highlite schedule Montrer les arrêts de la ligne hl_title Liste des arrêts hl_txt_filter Filtrer: hl_txt_sort Trier: hlf_chk_airport Aéroports hlf_chk_anleger Quai hlf_chk_bahnhof Gare hlf_chk_bushalt Arrêt de bus hlf_chk_frachthof Zone chargement hlf_chk_keine_verb pas de connection hlf_chk_maglevstop Gare Maglev hlf_chk_monorailstop Gare monorail hlf_chk_name_filter Filtre par nom: hlf_chk_narrowgaugestop Gare voie étroite hlf_chk_overflow Sur capacité hlf_chk_spezial_filter Filtre spécial: hlf_chk_tramstop Arrêt tramway hlf_chk_type_filter Filtre par type: hlf_chk_waren_abgabe Production: hlf_chk_waren_annahme Matières premières: hlf_title Filtre nom des gares Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Le dépôt ne peut pas\nêtre trouvé. Vous devez\nenvoyer le véhicule\nmanuellement. Homeless Sans domicile hydrogene hydrogène Idle: Arrêté: ignore climates Ignorer le climat In the industry legend show only currently existing factories Ne montrer que les industries existantes In Transit en transit Increase Industry density Augmenter la densité d'industries increase underground view level Augmenter le champ de la vue sous-terraine industrial building bâtiment industriel Init map ... Initialisation de la carte ... Input entrée Ins Stop Ins. Arrêt Insert stop before the current stop Insérer un arrêt avant celui-ci Intercity road len: Longueur routes intervilles: Intro. date: Date, dès: invalid invalide. Invalid coordinate ordre interdit isometric map carte isométrique January Janvier join game Partie réseau\n July Juillet Jump to Aller à June Juin Kann Spielstand\nnicht laden.\n Impossible de\nlire/ouvrir\nle fichier\nde sauvegarde!\n Kann Spielstand\nnicht speichern.\n Impossible d'ouvrir\nle fichier\nde sauvegarde\nen écriture!\n Kein Besitzer\n Sans propriétaire\n keine aucun Keine Einzelfahrzeuge im Depot Aucun véh au dépôt Keyboard_Help\n Aide clavier koord coordonnées Kreuzung Croisement labellist_title Liste des marqueurs Lade Relief Charger un relief Laden Charger Land attraction Attractions rurales Land industries Fabriques rurales LANG_CHOOSE\n Veuillez choisir votre langue:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Mois dernier Last Year Année passée Leaving depot! Sort du dépôt ! leer vide Legend Légende de la carte Leistung Energie Leistung: %d kW Puissance: %d kW Leitung Ligne à haute tension letzen Monat: diesen Monat: mois dernier: ce mois-ci: Line Ligne Line Management Gestion des lignes Lineless convoys serving this stop Convois sans ligne à cet arrêt Lines serving this stop Lignes desservant cet arrêt LKW_tab Camions Load game Charger partie load height data from file Charger les données d'altitude depuis un fichier. Load scenario Charger scénario loaded chargé loaded passenger/freight Trier passagers/marchandises par Loading (%i->%i%%)! Chargement (%i->%i%%) Loading (%i%%)! Chargement (%i%%) Loading addon paks ... Chargement des add-ons. Loading map ... Ouverture de la carte ... Loading paks ... Chargement des paks ... Loading skins ... Chargement des apparences ... Lock game Refuser d'avantage de changement de joueur (besoins de confirmation) Lokomotive_tab Locos m3 m³ Maglev LevMag maglev vehicle Véhicule à sustentation magnétique maglev_track Voie de Maglev Maglevdepot Dépôt des trains à sustentation magnétique Mail Demand %d\n Demande de courrier %d\n Mail level Taux d’envois postaux Mailbox Centre des messages Mailbox Options Options des messages make stop public (or join with public stop next) costs %i per tile and level faire un arrêt public (ou joindre un arrêt public voisin) coût %i$ par case et niveau Make way or stop public (will join with neighbours), %i times maintainance Transformer en arrêt public (relier avec les voisins), %i-coût de maintenance. Manual (Human) Manuel (humain) Manufactured: Construction: Map roughness Dénivelé carte: map zoom zoom March Mars Margin (%%) Marge Marker Marqueur Max Boost (%%) Augmentation max. (%%) Max income: Revenu max.: Max. speed: Vitesse max.: Max. waiting time Temps d'attente max Maximum 254 stops\nin a schedule!\n Au max 254 arrêts\ndans un ittinéraire ! maximum length of rivers Long. max. rivières Maximum tile height difference reached. La différence de\nhauteur maximum\nentre deux cases\na été atteinte. Maxspeed Vitesse maximale May Mai Median Citizen per town Moyenne d'habitants par ville: Meldung Message Menge Montant total merge stop Regrouper les arrêts de bus MessageOptionsText \nNouvelle année\n\nInfos joueurs\n\nInfos villes\n\nPas de route\n\nIndustrie\n\nChat\n\nNouveaux véhicules\n\nGare pleine\n\nProblèmes\n\nBouchons\n\nScénario\n min min. minimum length of rivers Long. min. rivières Minimum load Charge minimale Missing pakfiles Objets manquants dans le pak ! Modify the selected line Modifier la ligne sélectionnée Monate alt mois. monorail vehicle Monorail monorail_track Voies de Monorail Monorailboden Support Monorail Monoraildepot Dépôt de monorail Month Mois month wait time Attend max. (mois) Monthly departures Selon l'horaire Months Mois Mountain height Hauteur montagne: Movingobj Object mouvant Music playing disabled/not available Ambiance musicale autorisé/non autorisé Music volume: Volume musical: mute sound Mute Name Nom Narrowgauge Voie étroite Narrowgauge are not available yet! Il n'y a pas encore de voies étroites narrowgauge vehicle Véhicule voie étroite narrowgauge_track Voie étroite Narrowgaugedepot Dépôt de train à voi étroite Net ID: %p ID Réseau: %p\n Net Wealth Richesse Net wealth near zero Richesses proche de zéro Neue Karte Menu général Neue Welt Nouveau Monde new convoi Nouveau convoi New Line Nouvelle ligne New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Nouvelle ligne crée!\nVous pouvez maintenant atribuer la ligne en la sélectionnant dans le sélectionneur de ligne ci-dessus. New Vehicles Nouveau Véhicule Nickname: Pseudo : no buildings hidden Pas de bâtiments masqués no convois Pas de convoi No goods are loaded onto this convoi. Aucune marchandise ne sera chargée dans ce convoi no goods waiting Pas de marchandises en attente no load Ne pas charger No Route Pas de chemin No stop here! L'outil doit être utilisé dans une case d'arrêt. No suitable ground! Terrain inadéquat! No terminal station here! Impossible de construire un \ndépôt ici! L'extrémité d'une \nroute/voie plane est nécéssaire. no timeline sans chronologie no tree Pas d'arbre No. of Factories Usines et magasins Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Aucun itinéraire\nprogrammé :\ndépart impossible !\n none aucun nord Nord nordost nord-est nordwest Nordouest Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Pas autorisé!\nL'itinéraire du convoi\nne peut pas être changé actuellement.\nEssayez plus tard! Not enough fields would remain. Manque de place pour\nles champs de cette ferme. November Novembre Now active as %s.\n Maintenant actif comme %s.\n Number of rivers Nombre de rivières Object Objet Odometer: %s km Odomètre: %s km\n\n Oktober Octobre On loan since %i month(s) Débiteur depuis %i mois on the départ par mois, à partir de (jj-hh-mm) On this map, you are not\nallowed to change player!\n Il n'est pas possible de\n changer les joueurs dans\ncette partie!\n Only city chains Seul chaine product urbaine Only first %d differing paks reported. There are probably more. Seulement %d des premiers paks ont été indiqués. Il y en a sûrement d'autres. Only land chains Seul chaine product rurale Only one transformer per factory! Seulement un transformateur par manufacture! Only show goods which are currently handled by factories Afficher seulement les biens qui sont actuellement utilisés par les fabriques open ouvert Operation Opération Ops Profit Profit opérationnel Optionen Options Or enter a server manually: Ou indiquer manuellement un server Origin Origine ost Est Output sortie Ownership Propriétés Pak which may cause severe errors: Les paks suivants sont absents et causent ainsi des erreurs dans les chaînes de transports: Pak which may cause visual errors: Les paks suivants ont été remplacés par des objets similaires: Pak(s) different: Paks différents: Pak(s) missing on client: Pak(s) absents du client: Pak(s) not on server: Pak(s) absents du serveur: Pakset differences Différence de paks paletten Pal. Pas_tab Trains voyageurs Passagiere Passagers Passagierrate Taux passagers Passagierziele Destination passagers/courrier Passenger AI Passagers AI Passenger Demand %d\n Demande pas. %d\n Passengers %d %c, %d %c, %d no route Passagers %d %c, %d %c, %d destination inaccessible Passengers %d %s, %d %s, %d no route Passagers %d %s, %d %s, %d destination inaccessible Password Mot de passe Pax <%i> Mail <%i> Passagers <%i> Courrier <%i> Pax level Taux passagers PaxDest Destinations Percent Electricity Pourcentage d'électricité Planes are not available yet! Les avions ne sont pas encore dispos! Plant tree Planter un arbre player joueur player -1 joueur humain player 0 service publique player 1 Napik Transport player 10 Joueur 10 player 11 Joueur 11 player 12 Joueur 12 player 13 Joueur 13 player 2 Trikky Express player 3 TransMeyer & Cie player 4 Transitaire Sàrl player 5 Freeline SA player 6 P.S.K. & Cie player 7 Joueur 7 player 8 Joueur 8 player 9 Joueur 9 Please choose vehicles first\n Veuillez d'abord choisir les véhicules\n Post Message Postrate Taux d'envois postaux Power Energie Power (MW) Puissance (MW) Power: Puissance: Power: %4d kW\n Puissance: %4d kW\n Powerlines Lignes HT Problems_msg Problèmes Produced Produit Produces: %.1f units/minute actuellement %.1f/minute Production of %s has been stopped:\n%s\n La production de %s a été arrêtée:\n%s\n Production/Boost Production/Augmentation de production Produktion Production Profit Bénéfices promote to line En faire une ligne q1 Printemps q2 Eté q3 Automne q4 Hiver rail car véhicule ferroviaire random aléatoire Random age Époque aléatoire Random map carte quelconque Rathaus Mairie Rating Taux ratio_pax Rapport de passagers Relevant Utilisés sur la carte Reliefkarte Carte (relief) Remove Enlever remove airstrips Détruire des Taxiways remove channels Détruire des canaux remove interm. signals Retirer des signaux interm. remove maglev tracks Détruire des voies de Maglevs remove monorails Détruire des voies de monorail remove narrowgauge tracks Détruire des voies étroites remove powerlines enlever lignes à haute tension remove roads Détruire des routes remove tracks Détruire des voies Remove wayobj %s Enlever wayobj %s\n\n replace other signals Remplacer les autres signaux replace stop Changer l'arrêt request closing fermeture de la demande residential house maison résidentielle Restore natural slope Restaurer le relief naturel Restwert: Prix de vente: Retire. date: Date de retrait: return ticket Chemin retour Revenue Revenu Revision: Révision: road route road vehicle véhicule routier Roadsign Signal routier Rotate Building Faire pivoter le bâtiment Rotate map Tourner la carte Routing Traversée sack sacs sail vent Save Enregistrer partie Saving map ... Sauvegarde de la carte ... Scenario complete: %i%% Scénario terminé: %i%% Schedule changing! Modification de l'itinéraire ! Schienentunnel Tunnel ferroviaire Schiff_tab Navires Schiffdepot Dépôt maritime Schleppkahn_tab Barges Screenshot Prendre une capture d'écran. Seasons saisons Sehenswuerdigkeit Attraction touristique Select a server to join: Choisir un serveur Select a theme for display Modifier le thème Sell the selected vehicle(s) Vendre le(s) véhicule(s) sélectionné(s). sended envoyé SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 September Septembre Server did not respond! Le serveur ne répond plus Serves Line: Ligne desservie: set signal spacing Définir distance des signaux Setting Paramètres Ship Bateau shops and stores magasins et bureaux Show all Tout montrer show all building Montrer tous les bâtiments Show also vehicles no longer in production. Montrer aussi les véhicules plus en activité. Show also vehicles that do not match for current action. Montrer aussi les véhicules ne pouvant être utilisés pour cette action. Show even servers with wrong version or pakset Afficher même les serveurs de versions différentes ou hors paks show grid Afficher la grille Show industry Industries Show legend Légende Show map scale échelle carte Show mismatched Voir ce qui ne correspond pas Show obsolete Montrer Anc. Véhic. Show offline hors - ligne Show only used Afficher seulement ce qui est utilisé Show schedules Afficher l'itinéraire Show servers that are offline Afficher les serveurs hors-ligne Show servers where game version or pakset does not match your client Afficher les serveurs dont la version ou le pak ne correspond pas à votre client show station coverage Afficher la couverture des arrêts show station names Afficher le nom des arrêts show waiting bars Afficher la barre d'indice d'attente show/hide block reservations Afficher/masquer les blocs réservés Show/hide estimated arrival times Voir/Cacher les horaires d'arrivée potentielles show/hide object owner Afficher/masquer le propriétaire Show/hide statistics Aff./Masquer les statistiques Shows buttons on special topics. Affichage des boutons de visualisation et de contrôles Shows consumer/suppliers for factories Afficher les clients/fournisseurs des industries Shows the color code for several selections. affiche le code couleur de plusieurs sélections Shows the currently selected schedule Affiche l'itinéraire actuel Shrink city Réduire la ville shuffle midis Musique midi aléatoires signal spacing Espacement de signaux Sim: Simloops: Similar view as the main window Copie d'écran de la fenêtre principale Size (%d MB): Dimensions: (%d MB) sliced underground mode mode sous-terrain slot empty slot libre Sort by trier par Sort waiting list by Trier la liste d'attente par Sound Son Sound settings Réglages du son Sound volume: Volume sonore: special freight Marchandises spéciales Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Bonus de vitesse\nroute %i km/h, rail %i km/h\nbateau %i km/h, avion %i km/h. Speedlimit vitesse limite Speichern Sauver Spieler Joueur Spieler(mz) Joueurs Spielerliste Liste des joueurs Spielstand wurde\ngeladen!\n Partie chargée !\n Spielstand wurde\ngespeichert!\n Partie sauvegardée !\n Sprache Langue Sprachen Langues Stadtinformation Statistiques de la ville Start Départ Start the selected vehicle(s) Expédier le(s) véhicule(s) sélectionné(s). Starte Spiel C'est parti ! Station tiles: Case de quai: Station_msg Station Status Etat de l'arrêt steam vapeur Step timeline one year Avancer le temps d'un an. Stops Arrêts Storage Stockage Storage capacity Capacité de stockage Strassendepot Dépôt routier Strassentunnel Tunnel routier street car voiture citadine sued Sud suedost Sud-Est suedwest Sud ouest Summer snowline Altitude neige d'été Supplied: %.0f %% Fournis: %.0f %% Suppliers Fournisseurs Tage alt jours (âge) There are still vehicles\nstored in this depot!\n Il y a encore des\nvéhicules dans le dépôt !\n This Month Ce mois This Year Cette année Tile not empty. Nettoyez la case avant\nd'utiliser l'outil de pente. timeline Contraintes historiques tl_title Liste des villes To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Pour attirer plus de touristes, %s construit un %s avec l'aide de %i contribuables. To heavy traffic\nresults in traffic jam.\n Un trafic dense génère un embouteillage. Toggle day/night view Alterner jour/nuit Toggle vehicle tooltips Afficher les infos des véhicules tonnen t Total inhabitants: Total d'habitants: Tourist attractions Attractions touristiques Tourists Attractions Town_msg Nouvelles destinations Town: %s\n Ville de %s.\n Towns Villes track voie Tracks Voies Traffic Trafic Trains are not available yet! Les chemins de fer ne sont pas encore disponibles. tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. tram %i km/h, monorail %i km/h\nmaglev %i km/h, voie étroite %i km/h. tram_track voie de tramway Tramdepot Dépôt de Trams\n Trams are not available yet! Les trams ne sont pas encore disponibles. Transferring game ... Transfert de la partie... Transformer only next to factory! Les transformateurs doivent être\nplacés près\nd'une usine ! Translation Traduction transparent station coverage couverture stations transparente Transported Transporté(s) TrolleyBus_tab trolley bus Truck Camion tt_Other autres Tunnel muss an\neinfachem\nHang beginnen!\n Le tunnel doit\ncommencer sur une\npente droite!\n Tunnel must start on single way! Les tunnels doivent démarrer sur une voie unique. Tunnelboden Tunnel underground mode vue souterraine UNDO failed! Annuler n'est plus possible.\nVous pouvez annuler la\nconstruction d'une route tant\nqu'aucun signal/gare/arrêt ou\nquoique ce soit n'a été construit\nsur la route. Undo last ways construction Annuler la dernière voie construite Unemployed Chômeurs Unhappy Fâché units/day par mois\n Update Line MAJ. ligne upgrade HQ Améliorer HQ Usage: %.0f %% Utilisation: %.0f %% Usage/Output Electricité Use beginner mode Mode débutant Use timeline start year Jouer avec chronologie dès: Vehicle %s can't find a route! Le véhicule %s ne parvient pas à trouver son chemin! Vehicle %s is stucked! Le véhicule %s est bloqué! Vehicle details Détails du véhicule Verbrauch Consommation Vergroessere die Karte\n Agrandir la carte.\n Verkauf Éliminer! verkaufen Vendre Verkehrsteilnehmer Utilise la route Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Endettement:\n\nIl vous reste\n%d mois\npour rembourser.\n vh_title Liste de tous les véhicules via %s\n via %s\n via Menge via (groupé) voranstellen Placer en tête Waggon_tab Wagons waiting en attente Waiting for clearance! J'attends une voie libre ! Walked à pieds walking à pied Warnings_msg Trafic Wasser Eau Water Canal Water level Niveau de la mer: water vehicle véhicules aquatiques way %s cannot longer used:\n Les voies de type %s ne peuvent plus être utilisées.\n way %s cannot longer used:\n%s\n Les voies de type %s ne peuvent plus être utilisées:\n%s\n way %s now available:\n Les voies de type %s sont maintenant disponibles.\n Way toll péage ferroviaire Ways not connected Voies non connectées Wegpunkt Point de passage Weight: Poids Wert Valeur west Ouest Wind direction Direction du vent Winter snowline Altitude neige hiver withdraw Retirer Withdraw All Retirer tout WRONGSAVE Chargement impossible:\nFormat de sauvegarde\nincompatible.\n Year Année Year %i has started. L'année %i a commencé! Years Années Your primary color: Votre couleur primaire : Your secondary color: Votre couleur secondaire : Zielort Destination zooming in Zoomer zooming out Dézoomer Zu nah am Kartenrand Trop près du\nbord de la carte\npour construire.\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nouveau record du monde pour les maglevs: %.1f km/h por %s. New world record for monorails: %.1f km/h by %s. Nouveau record du monde pour les\nmonorails: %.1f km/h\nby %s. New world record for motorcars: %.1f km/h by %s. Nouveau record du monde pour les\nvéhicules à moteur:\n %.1f km/h\nby %s. New world record for narrowgauges: %.1f km/h by %s. Nouveau record du monde pour les trains à voie étroites: %.1f km/h por %s. New world record for planes: %.1f km/h by %s. Nouveau record du monde pour les\navions:\n %.1f km/h\nby %s. New world record for railways: %.1f km/h by %s. Nouveau record du monde pour les \ntrains:\n %.1f km/h \nby %s. New world record for ship: %.1f km/h by %s. Nouveau record du monde pour les\nbateaux:\n %.1f km/h\nby %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL village &1_CITY_SYLL -en-Gohelle &2_CITY_SYLL -en-Pévèle &3_CITY_SYLL -Ancoisne &4_CITY_SYLL -les-Marais &5_CITY_SYLL -en-Artois &6_CITY_SYLL -les-Près &7_CITY_SYLL -Noulette &8_CITY_SYLL -Fermont &9_CITY_SYLL -Capelle &A_CITY_SYLL -Thumesnil %1_CITY_SYLL Noyelle %2_CITY_SYLL Bruay %3_CITY_SYLL Calonne %4_CITY_SYLL Houplin %5_CITY_SYLL Billy %6_CITY_SYLL Hénin %7_CITY_SYLL Mons %8_CITY_SYLL Allennes %9_CITY_SYLL Vitry %A_CITY_SYLL Sailly %B_CITY_SYLL Camphin 1center %s %s 1extern %s-correspondance %s 1suburb %s %s %s 2center %s %s central 2extern %s %s extérieure 2suburb %s Champs %s %s 3center %s-place %s 3extern %s %s des champs 3suburb %s village %s %s 4center %s %s intérieur 4extern %s des marais %s 4suburb %s colonie %s %s 5center %s %s de transfert 5extern %s Wiesengrund %s 5suburb %s: %s Alfama 6center %s Hub %s 6extern %s: %s Évora 6suburb %s banlieue %s 7center %s ville %s 7extern %s: %s Gen. Carneiro 7suburb %s: %s Itaguaí 8center %s Knoten %s 8extern %s %s marge %s 8suburb %s satellite %s 9center %s Ring %s 9extern %s hameau %s 9suburb %s extension %s simutrans-124.5/simutrans/text/fr/000077500000000000000000000000001520675460500172045ustar00rootroot00000000000000simutrans-124.5/simutrans/text/fr/airtools.txt000066400000000000000000000110101520675460500215720ustar00rootroot00000000000000Aide sur les outils Aviation

Les outils Aviation

La barre d'outils "Aviation" contient les éléments nécessaires à la construction d'un réseau aérien. Ces outils permettent de construire (ou détruire) les pistes et voies de roulement (taxiways), les arrêts (lieux de chargement-déchargement pour les passagers, le courrier ou les biens), les dépôts (permettant d'acheter et gérer le matériel aérien - càd les avions la plupart du temps), ainsi que diverses extensions. Si vous jouez avec la timeline activée, certains bâtiments apparaîtront progressivement, vous donnant ainsi plus de choix.

Cliquez sur l?icône "avion" dans la barre supérieure pour ouvrir la barre d'outils aviation. Passez le curseur de la souris sur les icônes des différents outils pour connaître leur nom et certaines données : coût de construction, coût de la maintenance et vitesse maximum supportée.Sachez que les aéroports doivent être construits sur des surfaces planes, il n'y a pas de pentes possibles

La méthode à suivre pour construire simplement un aéroport :
1) construire une piste et un taxiway, les connecter
2) placer un arrêt sur une extrémité de taxiway
3) ajouter des bâtiments annexes si nécessaires

La barre d'outils Aviation contient, de gauche à droite :

Taxiway : permet le déplacement des avions entre la piste de décollage/atterrissage et l'arrêt. Ces taxiways sont construits au niveau du sol.
IMPORTANT : les taxiways ne doivent pas être connectés aux pistes par l'extrémité des ces dernières, mais sur le coté des pistes, sous peine d'empêcher le décollage ou l?atterrissage.
Pour construire un taxiway, cliquez sur l'outil "taxiway" désiré (le curseur se change en "taxiway"). Cliquez sur le point de départ du taxiway, puis sur le point d'arrivée (qui peut être une case vide ou une piste).

Piste : permet de construire une piste, utilisée par les avions pour décoller ou atterrir. Les pistes sont construites au niveau du sol, et peuvent éventuellement se croiser, mais ne peuvent être construites en diagonale.
Pour construire une piste, cliquez sur l'outil "piste" désiré (le curseur se change en "piste"). Cliquez sur le point de départ de la piste, puis sur le point d'arrivée, et ce en ligne droite stricte.
Les pistes doivent être connectées aux taxiways, sinon les avions ne pourront pas atterrir ou décoller.

Destruction de voies aériennes : permet de supprimer des segments de pistes ou de taxiways, si aucun avion n'est présent dessus. Cet outil de destruction est payant.
Pour détruire un segment, cliquer sur l'outil de destruction de voies aériennes (change le curseur en croix rouge). Cliquer sur la première extrémité du segment à supprimer, puis sur la deuxième.

Dépot Aérien : permet de construire un hangar pour acheter et gérer la flotte aérienne. Les hangars ont un cout de maintenance, et doivent être construits sur une extrémité de taxiway.
Pour construire un hangar, cliquez sur l'outil "hangar" désiré, puis sur une extrémité de taxiway.

Arrêts aériens et zones de chargement : permet la construction d'arrêts permettant aux avions de charger ou décharger des passagers, du courrier ou des biens. Si un tel arrêt n'est pas construit au contact d'un arrêt existant, cela va créer un nouvel arrêt.
Les arrêts ont un coût de maintenance et doivent être construits sur une extrémité de taxiway. ces arrêts possèdent une capacité de stockage de passagers ou biens. Un avion n'atterrira que si l'un de ces arrêts est libre et connecté à la piste via le taxiway.
Pour construire un arrêt, cliquez sur l'arrêt désiré, puis sur une extrémité de taxiway.
La touche "v" permet de montrer ou masquer la zone de couverture de l'arrêt.

Bâtiments aériens et extensions : permet de construire des extensions pour les stations. Ces extensions peuvent augmenter les coûts de maintenance, et permettre d'augmenter la capacité de stockage en passagers ou biens.
Une petite image dans le coin de l?icône permet de connaître le type d'extension (pour passagers, courrier ou biens)
Pour construire une extension, cliquez sur l'outil d'extension désirée puis placez là au contact d'un arrêt ou extension existant. Elle devient alors un élément de l'arrêt.

simutrans-124.5/simutrans/text/fr/baum_build.txt000066400000000000000000000022051520675460500220470ustar00rootroot00000000000000Aide sur la plantation des arbres Planter un arbre

la fenêtre de plantation des arbres est séparée en quatre parties :
- en haut à gauche : liste de sélection des différents arbres disponibles
- en bas à gauche : image de l'arbre sélectionné
- en haut à droite : réglages des options
- en dessous à droite : informations concernant l'arbre sélectionné

Liste de sélection


Dans cette liste, vous trouvez tous les arbres disponibles selon les options sélectionnées à droite. Ils sont classés de deux manières, accessibles via les onglets en haut de la liste :
translation : donne le nom de l'arbre dans une langue donnée (français par exemple). Si aucune traduction n'est disponible dans cette langue, c'est le nom interne de l'objet qui est affiché.
objet : donne le nom interne de l'objet dans simutrans

Options


Ignore climates : cette option désactive les restrictions de plantation dûes au climat.
Random year : avec cette option, l'arbre planté aura un âge aléatoire

simutrans-124.5/simutrans/text/fr/climates.txt000066400000000000000000000035301520675460500215470ustar00rootroot00000000000000Climate Control Help

Climate Control

Climate Control vous permet de modifier les options météorologiques et topographiques du monde à créer.

Climate Control s'ouvre automatiquement avecla création d'un nouveau jeu.

Niveau de la mer : Hauteur de l'eau (entre 0, terre exclusivement, et 5, avec alors des îlots).

Hauteur montagne : Hauteur des collines, entre 0 et 320.

Dénivelé carte : Entre 0 et 6. Une valeur élevée favorise l'existence sur terre d'une multitude de niveaux différents d'altitude.

Si hauteur et dénivelé sont simultanément élevés, il y aura beaucoup de montagnes, avec des pentes raides.

Si hauteur et dénivelé sont simultanément faibles, le monde généré sera très plat !

Si hauteur est élevée et que dénivelé est faible, il y aura peu de montagnes mais qui montent haut, les changements de niveaux étant éloignés (pente douce).

Si hauteur est faible et que dénivelé est élevé, alors le terrain est plat (les montagnes ont une pente raide... mais il n'y a pas de montagnes).

Summer snowline : Niveau à partir duquel il y a de la neige en été. Déterminé automatiquement à partie des choix suivants.

Winter snowline : Niveau à partir duquel il y a de la neige en été. Choisissez 0 si vous aimez le blanc. Le niveau maximal dépend des choix suivants.

Desert, tropic, mediteranean, temperate, tundra, rocky : Altitude à partie de laquelle vous pouvez commencer à trouver des terrains appropriés pour chaque climat. Ces options peuvent également être définie dans les fichiers de configuration pour Simutrans.


Actualisé pour Simutrans 100.0
05/07/2008. simutrans-124.5/simutrans/text/fr/color.txt000066400000000000000000000006721520675460500210700ustar00rootroot00000000000000Aide pour choix de couleur

Palette de couleur

Par ce menu, vous pouvez choisir la couleur de tous vos véhicules, gares etc... Pour sélectionner une couleur, cliquez simplement sur la couleur désirée. La couleur par défaut est le bleu clair. Tous les objets n'ont pas forcément votre couleur, ce sont les créateurs d'objets qui en décident.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/convoi.txt000066400000000000000000000017051520675460500212450ustar00rootroot00000000000000Liste des véhicules

Liste des véhicules

Cette liste vous affiche la liste de tous vos véhicules. (raccourci: Shift+V)

Si vous cliquez sur le nom ou l'image de la constitution de votre véhicule, une fenêtre de détail sur ce véhicule s'ouvre alors. Vous pourrez y voir toutes les informations comme le coût, revenu, chargement de votre véhicule.

Vous pouvez modifier les critère d'affichage des véhicules en appuyant sur le bouton à gauche : Nom, Revenu ou Type de véhicule. Le bouton juste à coté sert à afficher les véhicules selon l'ordre croissant ou décroissant.

Vous pouvez aussi filtrer la liste grâce aux boutons de droite. En cliquant sur le bouton de 'réglage' de filtrage, une fenêtre de sélection des critères de filtrage s'ouvrira. Puis vous pourrez activer le filtrage en appuyant sur le bouton 'activer'.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/convoi_filter.txt000066400000000000000000000025731520675460500226160ustar00rootroot00000000000000Filtres de véhicules

Filtrage de la liste des véhicules

Cet écran vous permet de définir des critères de filtrage pour afficher la liste de vos véhicules.

Les filtres sont organisés en groupe, indiqués en blanc. Vous pouvez activer un ou plusieurs groupe de filtre en appuyant sur les boutons associés à chaque groupe.

Filtre par Nom: Ecrivez tout ou partie du nom de véhicule, et appuyez sur le bouton associé pour valider le critère.

Filtre marchandises: Sélectionnez les marchandises transportées dans les véhicules que vous souhaitez voir affichés, et appuyez sur le bouton associé pour valider le critère.
Des boutons vous servent à sélectionner l'ensemble des critères marchandise ou aucun, ou encore d'inverser votre sélection.

Filtre par Type: Sélectionnez le(s) type(s) de véhicule que vous voulez afficher, et appuyez sur le bouton associé pour valider le critère.

Filtre spécial: Choissez ici les critères vous permettant de repérer les véhicules posant problème.

Important: Lorsque vous associez plusieurs critères, les véhicules affichés répondront à au moins un critère. ex: filtre par type : Train et bâteau, tous les trains et tous les bâteaux.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/depot.txt000066400000000000000000000060041520675460500210600ustar00rootroot00000000000000Les dépôts

Les dépôts

Dans un dépôt, vous pouvez acheter, vendre et définir les trajets de tous les véhicules d'un type particulier, par exemple : véhicules routiers si vous êtes dans un dépôt routier.

La fenêtre des dépôts contient les informations sur les véhicules que vous pouvez acheter, et ceux que vous avez déjà achetés. Dans les onglets à droite, sont affichés les véhicules que vous pouvez acheter. En haut à gauche, se trouve le convoi en cours d'assemblage (train). Au milieu à gauche, se sont les locomotives et camions que vous avez achetés. En bas à gauche, les wagons et remorques qui vous appartiennent.

Dans la partie basse, vous trouverez les informations concernant le véhicule actuellement sélectionné, et en dessous, il y a trois boutons.

Le plus à gauche est le bouton 'Départ', qui vous permet lancer le véhicule sur le trajet préalablement définit. Celui du milieu est le bouton 'Trajet', qui vous permet de définir un trajet pour le convoi en cours d'assemblage. Le bouton de droite est le bouton 'Vendre', qui vous permet de vendre des véhicules.

Pour voir les informations sur un véhicule particulier, survolez à la souris l'image correspondant. Vous les verrez alors dans la partie basse. Pour acheter un véhicule cliquez avec le bouton gauche de la souris sur le véhicule de votre choix dans l'onglet de droite. Il apparaîtra alors dans la partie gauche de la fenêtre. Le convoi que vous êtes en train de créer se trouve dans la partie supérieure de la fenêtre. Les autres véhicules (locomotives, camions, bateaux, chevaux) seront juste au dessous. Et les wagons, remorques et autres chariots apparaissent dans la partie du bas, juste au dessus de la partie 'informations'.

Pour créer un convoi, vous pouvez soit acheter tous les véhicules voulus les uns après les autres, en commençant par la locomotive ou le camion, puis par les wagons ou remorques. Ou alors, en sélectionnant les véhicules dans la partie gauche de la fenêtre. Vous ne pouvez agir que sur un seul véhicule à la fois. Pour vendre un de vos véhicules, sélectionnez le mode vente en appuyant sur le bouton 'Vendre', puis survolez à la souris le véhicule. Le prix de vente est alors affiché dans la partie 'informations', pour effectuer la vente, cliquez avec le bouton gauche de la souris sur le véhicule.

Pour définir le trajet d'un convoi cliquez sur le bouton 'Trajet' qui affichera la fenêtre de gestion de trajet. Une fois le trajet définit, vous ordonnez le départ du convoi en appuyant sur le bouton 'Départ'.

Pour renvoyer un convoi vers un dépôt, modifiez le trajet du convoi en lui indiquant le dépôt comme unique destination. Une fois arrivé dans le dépôt, le convoi sera désassemblé et chaque véhicule sera de nouveau disponible pour être réarrangé ou vendu.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/display.txt000066400000000000000000000024771520675460500214240ustar00rootroot00000000000000Aide sur les options d'affichage

Aide sur les options d'affichage

L'écran de gestion des options d'affichage permet de modifier de nombreuses paramètres qui vous permettrons de changer l'apparence visuelle de Simutrans.
Les options sont:

Luminosité - Eclairci et assombri l'affichage.
Couleurs - Sans effet
Défilement - La vitesse de défilement est proportionnelle au nombre défini.

Défilement inversé - inverse ou pas la droite / gauche, haut / bas et vice-versa quand vous faites défiler.
Afficher les piétons - Des piétons sont créés et affichés ou pas autour des villes. Si votre Configuration matérielle est juste, n'activez pas cette option.
Mode jour / nuit - Simule ou non les modes jour et nuit. Cette option activée, peut vous rendre une belle vue de nuit!

La moitié basse de l'écran vous informe sur la performance de votre ordinateur. Ces nombres n'ont pas d'intérêts pour le déroulement du jeu.
Important - On pourrait vous demander cette information lorsque vous êtes amené à remonter un dysfonctionnement.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/finances.txt000066400000000000000000000046461520675460500215450ustar00rootroot00000000000000Aide sur les finances

Finances [f]

Vous pouvez gérer vos finances par cet écran, accessible soit en pressant la touche 'f' ou en cliquant sur l'icône 'billet' dans la partie droite de la barre d'outils.
Vous y trouverez les informations financières de votre entreprise pour l'année passée et l'année en cours. Ainsi qu'un graphique permettant de voir rapidement l'état de vos finances. Un clic sur les entêtes de valeurs les affiche dans le graphique. Les onglets permettent de voir les valeurs des 10 dernières années et des 12 derniers mois

Revenu - Somme de tous les revenus dus au transport de marchandises et personnes.

Opération - Frais de fonctionnement des véhicules en circulation (Les frais de chaque véhicule sont indiqués dans les informations des dépôts et des convois

Maintenance - La somme des frais de maintenance des équipements qui vous appartiennent. Cette somme est déduite tous les mois et est indiquée à droite.

Péages routiers - Frais de péages pour l'utilisation des voies d'autres entreprises

Lignes électriques - Revenus des lignes électriques

Profit opérationnel - Profit sans les coûts de constructions et d'achats. Somme de revenu, opération, maintenance, péages et lignes électrique

Nouveaux véhicules - Somme de tous les véhicules achetés et vendus dans n'importe quel type de dépôt.

Construction - C'est la somme des dépenses concernant les constructions dans l'année en comprenant les signaux ferroviaires, les rails, les routes, les arrêts, les gares, les dépôts et les postes.

Profit - Profit de l'année en cours. Somme de profit opérationnel, nouveaux véhicules et construction.

Transporté(s) - Somme de quantité de biens transportés.

Cash - Argent disponible. Affiché au bas de l'écran.

Biens - Valeur des biens (véhicules, voies, ...) possédés.

Marge - Pourcentage entre profit opérationnel et revenu

Richesse - Somme cash et biens. Si cette valeur est négative, vous êtes en faillite.

Scénarios

Si vous jouez un scénario, l'objectif et le pourcentage de réussite est affiché.

simutrans-124.5/simutrans/text/fr/haltlist.txt000066400000000000000000000015011520675460500215660ustar00rootroot00000000000000Liste des arrêts

Liste des arrêts et gares

Cet écran vous montre tous les arrêts de bus, les gares et les quais de chargement du jeu.

Quand vous cliquez sur un arrêt, une fenêtre de détail de cet arrêt s'ouvre.

Il est possible de modifier l'ordre d'affichage des arrêt en appuyant sur le bouton à gauche. Les critères sont Nom, Attente, Type et numéro. Le bouton juste à coté permet de choisir l'ordre d'affichage croissant ou décroissant.

Vous pouvez aussi filtrer la liste grâce aux boutons de droite. En cliquant sur le bouton de 'réglage' de filtrage, une fenêtre de sélection des critères de filtrage s'ouvrira. Puis vous pourrez activer le filtrage en appuyant sur le bouton 'activer'.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/haltlist_filter.txt000066400000000000000000000032571520675460500231450ustar00rootroot00000000000000Filtrage des arrêts

Filtrage des arrêts, gares et quais de chargement

Cet écran vous permet de définir les critères de filtrage pour afficher la liste des arrêts.

Les filtres sont organisés en groupe, indiqués en blanc. Vous pouvez activer un ou plusieurs groupe de filtre en appuyant sur les boutons associés à chaque groupe.

Filtre par Nom: Ecrivez tout ou partie du nom de l'arrêt, et appuyez sur le bouton associé pour valider le critère.

Filtre par Matières premières: Sélectionnez les marchandises requises par les industries à proximité des arrêts que vous souhaitez voir s'afficher, et appuyez sur le bouton associé pour valider le critère.
Des boutons vous servent à sélectionner l'ensemble des critères marchandise ou aucun, ou encore d'inverser votre sélection.

Filtre par Production: Sélectionnez les marchandises produites par les industries à proximité des arrêts que vous souhaitez voir s'afficher, et appuyez sur le bouton associé pour valider le critère.
Des boutons vous servent à sélectionner l'ensemble des critères marchandise ou aucun, ou encore d'inverser votre sélection.

Filtre par Type: Sélectionnez le(s) type(s) d'arrêt que vous voulez afficher, et appuyez sur le bouton associé pour valider le critère.

Filtre spécial: Choissez ici les critères vous permettant de repérer les arrêts posant problème.

Important: Lorsque vous associez plusieurs critères, les arrêts affichés répondront à au moins un critère.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/language.txt000066400000000000000000000020561520675460500215330ustar00rootroot00000000000000Aide sur les langues

Ecran des langues

Simutrans est multilingue et vous permet de choisir la langue dans laquelle vous désirez afficher les textes. (Seulement si la traduction existe)
Quand vous changez la langue (en sélectionnt la langue voulue), le jeu va alors afficher tous les textes dans cette langue. Les écrans ne tiendront compte de la modification qu'à leur prochaine ré-ouverture.
Les villes existantes ne changeront pas de nom, et garderont les noms du jeu au démarrage. C'est pourquoi, lorsque vous souhaitez obtenir des noms de villes dans votre langue, choisissez la langue avant de créer un noveau jeu.

Si vous ne trouvez pas votre langue préférée, quelques traductions sont disponibles sur les sites http://www.simustation.de.vu et http://www.simutrans.de, mais elles ne sont pas toutes complètes, ni parfaitement à jour.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

Pour aider à traduire Simutrans dans votre langue,
visitez https://translator.simutrans.com

simutrans-124.5/simutrans/text/fr/load.txt000066400000000000000000000017331520675460500206700ustar00rootroot00000000000000Aide sur le chargement d'une partie

Charger un jeu

Le fait de charger une partie vous permet de continuer une partie que vous avez commencé auparavant. L'écran de chargement est très semblable à l'écran de sauvegarde.

Si vous tentez de charger un jeu inexistant, le jeu affichera un message d'erreur.

X - Pour supprimer un fichier de sauvegarde. Attention!, aucun message de confirmation n'apparaît.
Nom du fichier de sauvegarde - En cliquant dessus, Simutrans effectue le chargement de ce jeu.

Important Les sauvegardes effectuées avec d'anciennes versions de Simutrans ne sont pas toutes compatibles. Pour vous en assurer, veuillez vous reporter aux informations sur la version actuelle pour lire la mention de compatibilité. Entre les versions récentes et les plus anciennes, cela ne fonctionne pas.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/mailbox.txt000066400000000000000000000027551520675460500214110ustar00rootroot00000000000000Aide sur le centre des messages et leurs options

Centre des messages et options des messages

Dans le centre des messages arrivent tous les messages qui sont apparus depuis que le jeu a été créé ou chargé.

Avec le bouton Options, l'écran "Options des messages" va s'ouvrir. On y peut ajuster pour chaque message s'il apparaît dans une ligne en bas de l'écran principal de Simutrans (bouton de gauche), dans un écran temporaire (bouton du milieu) ou dans un écran permanent (bouton de droite).

De haut en bas, les messages suivants sont ajustables :
-Nouvelle année : un nouvel an a commencé.
-Nouveauté de l'AI : un joueur AI a ouvert une nouvelle ligne.
-Nouv dans villes : une ville construit une mairie plus grande.
-Pas de route : le véhicule nommé en ce message ne trouve pas de route pour le prochain arrêt.
-Industrie : par la croissance de la ville, une nouvelle chaîne industrielle avec un magasin a été construite.
-Touriste : une nouvelle attraction touristique ou un nouveau monument a été construit.
-Nouveau véhicule : un nouveau véhicule est disponible.
-Gare remplie : cet arrêt est surchargé.
-Problèmes : l'entreprise du joueur est débitrice ou autres messages critiques.

En cliquant sur un message avec le bouton gauche de la souris, on peut déplacer la vue du jeu sur l'événement.

simutrans-124.5/simutrans/text/fr/map.txt000066400000000000000000000050611520675460500205240ustar00rootroot00000000000000Aide sur la carte

Carte du jeu

On vient à la Carte du jeu en pressant 'm' ou par le symbol Carte dans la liste des symbols.

L'écran carte vous apporte une vue d'aigle sur le monde courrant avec des informations telles que les villes, les industries, les routes et les chemins de fer, vos véhicules etc. Vous allez atteindre chaque lieu en cliquant avec le bouton gauche.

En cliquant avec le bouton droit de votre souris, vous obtenez la carte en trois échelles differentes. La même fonction a la roue de la souris.

Chaque type d'industrie est référencé par une couleur différente, la légende est située au-dessous de la carte. Si le souris montre sur une industrie, les acheteurs sont soulignés; pressant simultanément la touche Shift, les fournisseurs sont mis en relief.

Les boutons colorés tout en bas de l'écran inscrivent certains informations dans la carte. S'il n'y a pas des indications de la couleur, l'échelle au droit de l'écran est utilisé.
Passagersmarque tous les territoires en blanc qui sont attachés à un arrêt.
Postemarque tous les territoires en bleu qui sont attachés à un bureau de poste.
Chargementmontre combien de chargement est transporté sur une pièce de la route ou du chemin de fer.
Statusmontre si les passagers à un arrêt sont transportes. La même vue est aussi utilisèe dans la liste des arrêts et à la ligne de status au nom de l'arrêt.
Servicemontre combien d'arrives et de départs se sont passés à un arrêt.
Circulationmontre la densité de la circulation sur une ligne.
Originemontre combien de passagers et de chargement ont cet arrêt comme point de départ.
Destinationmontre combien de passagers et de chargement ont cet arrêt comme déstination.
En attendantmontre combien de passagers et de chargement attendent à un arrêt pour être transportés.
Voiesouligne les lignes de chemin de fer en blanc; les lignes electrifiées sont soulignées en rouge. Les signaux sont marqués par un point jaune.
Limite de la vitessemontre rélativement les vitesses maximales sur les routes et les chemins de fer.
Lignesinscrit les lignes de transport dans la carte en rouge.
Attractionsenlarge les attractions touristiques inscrites dans la carte, et ils sont colorés selon leurs attractivités (Passagers).

Actualisé pour Simutrans 0.85.04.1
21.4.2005

simutrans-124.5/simutrans/text/fr/new_world.txt000066400000000000000000000107421520675460500217510ustar00rootroot00000000000000Aide sur un nouveau monde (nouveau jeu)

Créer un nouveau jeu

Les fenêtres qui permettent de créer un nouveau monde sont accessible au lancement du jeu et, en cours de partie, en cliquant sur la disquette (option) puis sur menu général.

Cet écran vous permet de créer une nouvelle carte (donc un nouveau jeu), en définissant les paramètres relatifs à l'époque, à la carte, à la météo, aux villes et aux industries.

Load game : Vous permet de charger l'un de vos jeux précédemment sauvegardé. Cliquez ici pour plus d'infos.

Charger un relief : si vous disposez d'un fichier avec un relief déterminé.

Numéro de carte : Sélectionnez l'une des nombreuses cartes à jouer, soit en entrant le n° souhaité, soit en utilisant les boutons qui permettent de passer à la carte précédente/suivante, soit en cliquant sur «carte quelconque» pour qu'un numéro de carte soit tiré au sort.

Dimensions : Nombre de carrés dont le monde généré sera composé. Valeur mini : 64, maxi : 4096. Attention à ne pas générer une carte trop grande pour la capacité de calcul de votre ordinateur. Une carte trop petite peut avoir pour conséquence un non respect de vos autres choix (s'il y a peu de terres, il ne pourra pas y avoir beaucoup de villes, par exemple).

Nb villes : Nombre initial de villes. D'autres villes peuvent être crées en cours de partie en payant une somme importante... ou en demandant au joueur service public de le faire.

Moyenne d'habitants par Ville : Nombre moyen de personnes vivant dans chaque ville (certaines villes seront bien évidemment plus grosses et d'autres plus petites, ce n'est qu'une moyenne).

Intercity road len : Longueur maximale de route reliant les villes entre elles. Une valeur élevée facilite les connexions routières entre les villes dès le début de la partie.

Densité Trafic : Densité Trafic : Trafic généré par l'ordinateur. Entre 0 et 16 (modifiable en cours de partie dans les options). Une valeur élevée représente un challenge pour gérer risques d'embouteillages et circulation de vos bus et camions. Une valeur nulle permet une circulation totalement privée.

Fabriques en dehors des villes : Nombre d'usines : bâtiments autonomes comme l'éolienne, sites d'extraction de matières premières, agriculture et élevage, industries de transformation.

Marchés dans les villes : Industries de vente dans les villes (supermarchés, etc.). Ces bâtiments consomment des biens (à y acheminer) pour les vendre à la population, il ne produisent aucun bien à transporter.

Tourist attractions : Nombre initial de sites touristiques, qui génèrent du trafic passagers et de courrier.

Use timeline start year : Si la case est cochée, les limitations applicables aux bâtiments et véhicules seront appliquées en fonction de l'année de départ choisie. Attention, il n'y a pas de construction de fabrique en cours de partie (sauf en jouant le joueur service public qui peut ajouter des attractions, des centrales électriques et des marchés dans les villes), et si l'année de départ est très ancienne il peut n'y avoir aucune fabrique sur la carte. Si la case n'est pas cochée, vous pouvez commencer en 1750 avec des usines électroniques et des éoliennes.

Autoriser le changement de joueur : Le titre est clair. Quand le changement est possible, il permet même d'incarner temporairement un joueur spécial qui bénéficie de quelques options spéciales et d'un découvert illimité : service public.

Beginner Mode : Les débutants et les mauvais gestionnaires peuvent cocher cette case pour bénéficier de revenu majorés de 50% et obliger les usines à toujours accepter les marchandises, même quand elles sont en surnombre vu la consommation du bâtiment.

C'est parti ! : Est-il nécessaire de préciser l'effet d'un click sur ce bouton une fois tous vos choix paramétrés ?

Pensez, en début de partie, à aller configurer, à partir du bouton en forme de disquette (option), les couleurs utilisées et les informations relatives aux joueurs (pour ajouter ou enlever des joueurs contrôlés par l'ordinateur, selon que vous préfériez jouer en solo ou avec des concurrents).

Actualisé pour Simutrans 100.0
05/07/2008.

simutrans-124.5/simutrans/text/fr/options.txt000066400000000000000000000021401520675460500214350ustar00rootroot00000000000000Aide sur les options de jeu

Options

L'écran des options de jeu est le carrefour vers les différents écrans de paramétrage de Simutrans allant du réglage fin au réglage de comportement. Ci-dessous, vous trouverez une liste des écrans disponibles et une brève description de chacun.

Langue - Définit la langue de jeu de Simutrans.
Couleur - Change la couleur de vos entreprises.
Affichage - Définit les paramètres d'affichage tels que les modes jour/nuit.
Son - Vos choix de musique d'ambiance.
Joueurs - Les différents joueurs adversaires.
Charger - Charger un jeu précédemment sauvegardé.
Sauvegarder - Sauvegarder la partie en cours.
Menu général - Démarrer un nouveau jeu (peut être annulé ensuite)
Quitter - Quitter, sans message de confirmation.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/players.txt000066400000000000000000000012431520675460500214240ustar00rootroot00000000000000Aide sur les joueurs adverses

Ecran des joueurs

Cet écran vous permet de choisir votre (vos) adversaire(s) dans le jeu. Ils sont gérés par le moteur d'Intelligence Artificielle du jeu.

Pour activer/désactiver un adversaire, cliquez simplement sur le bouton correspondant. Notez cependant que le fait de désactiver un adversaire ne va pas supprimer toutes ses routes, rails, véhicules, et il continuera aussi de percevoir ses revenus. Désactiver un adversaire le met tout simplement en veille.

L'écran vous indique aussi le solde de chacun de vos adversaires.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/railtools.txt000066400000000000000000000100501520675460500217510ustar00rootroot00000000000000Ferroviaire

L'écran de gestion ferroviaire

Cet écran comprend les outils de construction de rails, gare, dépôt, tunnel, passages à niveau, signalisation pour trains.

En haut à gauche, il y a l'outil de construction des rails. Cet outil permet de construire les rails entre les carrés. Cliquez et votre curseur prendra la forme d'un passage à niveau. Pour construire une voie ferrée, cliquez d'abord sur le point de départ, le curseur prendra alors la forme d'un bulldozer. Cliquez ensuite sur le point d'arrivée de votre voie ferrée. Simutrans construira alors le chemin de fer entre les deux carrés. Il n'utilisera uniquement que des carrés libres. Il ne franchira pas de route, n'utilsera pas de tunnels ou ponts. Si la construction n'est pas possible, il ne se passera rien.

A sa droite, l'outil de placement des signaux ferroviaires. Cliquez dessus pour changer le curseur en signal. Ces signaux sont utiles pour contraindre les mouvements des trains. Utilisez les pour autoriser deux trains à emprunter le même chemin, mais l'un après l'autre. Les signaux sens-unique forcent les trains dans un sens. Pour placer un signal sens-unique, placer un signal et cliquez le afin de le changer en 'sens-unique'. En cliquant plusieurs fois sur un signal, vous changerez son sens, et son type (Mono ou bi-directionnel). Soyez prudent dans l'utilisation des signaux 'sens-unique' car les trains ne sauront pas quel chemin prendre si vous vous êtes trompés. Pour placer un signal, cliquez ensuite sur la protion de rail où vous voulez le placer. Les signaux sont placés au bord des carrés, mais comptent pour un seul carré. Il ne peut y avoir qu'un seul signal par carré. Pour supprimer un signal, utilisez l'outil de destruction, et cliquez sur le carré où vous avez placé le signal. Ceci supprimera le signal en laissant intact le rail.

Le dernier symbole du haut est l'outil de construction de tunnels. Cliquez dessus et le curseur prendra l'image du tunnel. Les tunnels sont utilisés pour créer une seule de section de tunnel sous une montagne. Utilisez les pour éviter de contourner les montagnes. Pour placer un tunnel, vous devez d'abord avoir vos sections de rails. Placez le début et la fin des rails au bord de la montagne (de chaque côté), assurez-vous que les niveaux d'altitudes sont identiques. Pour construire le tunnel cliquez avec l'outil de placement sur l'une des deux extrémités.

Au dessous de ces symboles, il y a le passage à niveau. Cliquez dessus pour changer le curseur en passage à niveau. Ils sont utiles pour faire se croiser une route et une voie ferrée. Pour le placer, cliquez avec le bouton gauche sur une section de route.

A gauche, c'est le dépôt ferroviaire. Cliquez dessus pour changer le curseur en dépôt. Ils sont utilisés pour acheter, assembler, démonter et vendre des motrices et des wagons. pour placer le dépôt, cliquez sur un début/fin de rails.

Le dernier symbole est celui de la construction de gare. Cliquez dessus, le curseur prendra la forme d'un segment de gare. Les gares permettent aux trains de charger et décharger les passagers et marchandises. Les gares peuvent être composées de plusieurs segments alignés ou juxtaposés. Les gares peuvent contenir un nombre illimités de marchandises, mais les usines produisent lorsque moins de 1000 unités (tonnes, palettes, m3) attendent. Les gares sont construites à partir de segments individuels. Pour positionner un segment, cliquez sur le carré où vous souhaitez le placer. Les gares doivent être construites sur des rails existant, mais ne peuvent pas être placées dans des montées, descentes et intersections. Chaque segment adjacent avec une gare devient une partie de celle-ci, même si elle est située sur une autre section de rail. A chaque segment correspondent 2 wagons ou motrices. Assurez-vous de la longueur des gares pour vos trains les plus longs. Tout wagon se trouvant hors de la gare ne sera pas chergé ou déchargé.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/roadtools.txt000066400000000000000000000047571520675460500217700ustar00rootroot00000000000000Aide routière

L'écran de gestion routier

Cet écran vous donne accès a l'ensemble des outils de construction de routes, arrêts de bus, tunnels routiers, dépôts, passages à niveau, quai de chargement.

Dans le coin en haut à gauche se trouve l'outil de création de route. Cliquez dessus et le curseur prendra la forme du même symbole. Pour commencer la construction d'une route cliquez d'abord sur le point de départ, puis cliquez sur le point d'arrivée de la section. La route sera construite entre ces deux points.

A sa droite, le symbole indique la construction d'arrêts de bus. Cliquez dessus pour changer votre curseur en arrêts de bus. Les arrêts de bus permettent aux bus de prendre et déposer des passagers. Il peut contenir 2 bus, un dans chaque direction. Pour le placer, cliquez à l'endroit voulu. Il doit être positionné sur une portion de route, mais pas dans les intersections, ni les virages.

Le dernier symbole permet de construire les tunnels. Cliquez dessus et le curseur prendra l'image du tunnel. Les tunnels sont utilisés pour créer une seule de section de tunnel sous une montagne. Utilisez-les pour éviter de contourner les montagnes. Pour placer un tunnel, vous devez d'abord avoir vos sections de route. Placez le début et la fin de la route au bord de la montagne (de chaque côté), assurez-vous que les niveaux d'altitudes sont identiques. Pour construire le tunnel cliquez avec l'outil de placement sur l'une des deux extrémités.

Au dessous de ces symboles, il y a le passage à niveau. Cliquez dessus pour changer le curseur en passage à niveau. Ils sont utiles pour faire se croiser une route et une voie ferrée. Pour le placer, cliquez avec le bouton gauche sur une section de route.

A gauche, c'est le dépôt routier. Cliquez dessus pour changer le curseur en dépôt. Ils sont utilisés pour acheter, assembler, démonter et vendre des Camions, bus, fourgons postaux. Placer simplement le dépôt sur l'extrémité d'une portion de route à l'endroit voulu.

Le dernier symbole représente les quais de chargement/déchargement. Cliquez sur ce symbole et le curseur prendra sa forme. Les quais de chargement servent aux camions pour prendre et déposer des marchandises. Deux véhicules seulement peuvent emprunter un quai simultanément. Pour placer un quai de chargement, utilisez le même principe que pour les dépôts (ci-dessus).

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/save.txt000066400000000000000000000023361520675460500207070ustar00rootroot00000000000000Aide sur la sauvegarde

Sauvegarder un jeu

Si vous souhaitez jouer sur le même jeu pendant plusieurs jours, vous devrez sauvegarder le jeu à certains moments. Simutrans vous propose une solution vous permettant de sauvegarder l'ensemble de vos données dans un fichier.

L'écran de sauvegarde vous permet de sauvegarder vers un nouveau fichier ou d'écraser un fichier existant ou encore de supprimer un fichier. Pour sauvegarder un nouveau fichier, entrez le nom (sans extension) dans la zone de texte et cliquez sur le bouton 'sauver'. L'écran va alors se refermer et un message vous confirmera la bonne sauvegarde de votre jeu. Si vous souhaitez écraser un fichier existant, cliquez simplement sur son nom.

Pour la suppression de fichiers, soyez vigilants car une fois le fichier effacé, vous ne pourrez plus le retrouver. Même dans la poubelle (pour les versions Windows), vous ne le retrouverez plus. Pour supprimer un fichier cliquez sur le bouton 'X' en face du nom du fichier que vous voulez supprimer.
NB: Vous n'aurez pas d'autre information une fois le bouton pressé, l'écran va simplement se refermer.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/schedule.txt000066400000000000000000000074611520675460500215510ustar00rootroot00000000000000Itinéraires

Programmation des itinéraires

Il a deux manières de programmer ses véhicules :
1 - Dans les dépôts, à la création un nouveau véhicule ou convoi.
2 - Dans la fenêtre d'un véhicule en fonctionnement, par l'option "trajet".
Les procédures sont semblables : On peut Ajouter, Insérer ou Supprimer des arrêts, ainsi que modifier la limite de charge minimum ou la liste des arrêts vers lesquels le véhicule doit être dirigé.

A la fin, vous trouverez un exemple de l'utilisation des commandes qui sont décrites à suivre.

Aj. Arrêt : il sert à ajouter des arrêts à la fin d'une liste.
Ins. Arrêt : il insère un arrêt avant celui identifié par >>.
Sup. Arrêt : il sert à effacer un arrêt.
Fin : Quand vous avez terminé la définition de l'itinéraire de votre véhicule, l'appui sur ce bouton valide les modifications et ferme l'écran.
Charge mini (dans le haut de l'écran) : minimum de chargement que le véhicule doit atteindre pour continuer son chemin.
Il peut être 0, 20. 40. 60. 80 ou 100%. Zéro signifie que le véhicule va charger ce qu'il y aura dans la gare, et va partir immédiatement. Cent, le véhicule partira seulement quand il sera complet.

Attention donc, vous pouvez bloquer d'autres trains, tandis que celui-ci attendra pour atteindre sa capacité minimum.

Exemple : Un train doit porter le bois de la forêt 1 (F1) et de la forêt (F2) à Scierie 1 (S1) et à une papeterie (FP). Vous construisez vos voies et vos gares. Vous créez le dépôts ferroviaire et achetez le train. Cliquez sur 'Trajet''. Simutrans va vous ouvrir l'écran de gestion des trajets. On remarque tout de suite la charge mini: 0%. Et dans le bas, quatre boutons : 'Aj. Arrêt', 'Ins. Arrêt', 'Sup. Arrêt' et 'Fin'.

1. Pour créer l'itinéraire, cliquez sur 'Aj. Arrêt', commencez d'abord par le premier arrêt et ainsi de suite jusqu'au dernier du cycle. C'est très simple : le curseur va prendre l'aspect d'une pancarte où il est inscrit 'STOP', et il vous reste à cliquer sur les gares dans l'ordre désiré du passage.

2. Envisageons maintenant que le train ne doit pas quitter F1 avec moins de, disons, 20% de sa capacité, cliquez sur la gare de F1 dans le trajet, et après cela, dans la zone 'Charge mini' sur la flèche de droite pour augmenter la charge à 20% . L'autre flèche diminue la charge minimum.

3. Nous allons dire que vous voulez que le train passe par une autre scierie, la scierie 2 (S2), qui serait entre S1 et FP. Pour ceci, cliquez sur 'Ins. Arrêt' quand le trajet pointe sur la gare FP. Cliquez ensuite sur la gare S2. L'arrêt S2 sera entre S1 et FP. N'oubliez pas, qu'un raccordement doit exister entre la gare S2 et le circuit principal, et qu'il doit être électrifié, si votre train est électrique.

4. Disons que vous allez réaliser un changement de votre système et que vous êtes contraint de détruire une gare (S1 par exemple). Dans la définition du trajet, comme vous avez détruit la gare S1, vous voyez que l'itinéraire contient les coordonnées du segment de cette gare (x,y). Vous devrez lors supprimer cette ligne de l'itinéraire : sélectionnez la ligne en question et appuyez sur le bouton 'Sup. Arrêt'. Pour insérer la nouvelle gare, il suffit de suivre le procédé décrit dans le point 3.

5. Pour quelque raison, vous voulez que le train passe une certaine gare, ou simplement se rende immédiatement à une gare précise. Avec la programmation du train ouvert, cliquez sur l'arrêt souhaité.

6. Pour valider vos modifications, cliquez sur le bouton 'Fin'.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/shiptools.txt000066400000000000000000000026611520675460500217760ustar00rootroot00000000000000Aide maritime

L'écran de gestion maritime

Cet écran vous donne accès aux outils de construction de quais et gares maritime.

Dans le coin supérieur gauche se trouve le symbole du quai de chargement maritime. Cliquez dessus et votre curseur prendra la forme de ce symbole. Les quais permettent aux bateaux de charger et décharger leurs marchandises et passagers. Pour positionner un quai maritime cliquez sur la terre qui se trouve au bord de la mer, le quai est composé de deux parties : l'une sur la terre et l'autre en mer. Ils doivent être construits sur un littoral droit. Il est possible de construire de très grands quais, mais ils doivent toujours être adjacents les uns avec les autres. Pour diriger un bateau vers un quai, vous devrez l'envoyer vers un des carrés qui entoure le quai et pas le quai lui même. Attention, les quais ne peuvent pas encore être retirés proprement !

En bas, vous trouverez deux symboles pour construire une gare maritime, ils diffèrent seulement par le fait que l'un est orienté vers la gauche et l'autre vers la droite. Cliquez sur l'un des deux pour changer votre curseur à son effigie. Les gares maritimes servent à acheter les navires que vous désirez. Pour positionner une gare maritime cliquez dans l'eau à l'endroit où vous souhaitez construire votre gare maritime.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/sound.txt000066400000000000000000000017751520675460500211070ustar00rootroot00000000000000Aide sur les sons

Réglages sons et musiques

Dans cet écran vous pourrez ajuster les niveaux sonores de la musique et des sons joués dans Simutrans.

Les volume sont réglables via le curseur horizontal, pour augmenter déplacer le curseur vers la droite. Le volume sonore définit le niveau des effets sonores du jeu tels que les bruits des trains, etc. Le volume usical définit le niveau de la musique d'ambiance(fond sonore) du jeu.
Les derniers boutons au bas servent à définir le morceau de musique à jouer en tant que musique d'ambiance. Le bouton de gauche sert à faire défiler les morceaux de musique vers l'arrière et l'autre vers l'avant. S'il ne trouve aucun morceau de musique, c'est deux boutons n'auront aucun effet.
NB: La musique n'est pas fournie avec Simutrans, vous devrez l'ajouter manuellement. l'intérêt de cette aide ne concerne pas la procédure d'ajout de musique.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/fr/station.txt000066400000000000000000000027611520675460500214340ustar00rootroot00000000000000Les gares

L'écran de gestion des gares

Dans cet écran, vous pouvez trouver toute l'information sur la gare concernée. Vous pouvez voir les marchandises en attente de livraison, quelles marchandises sont attendues par les entreprises locales et quelles gares sont reliées à celle-ci.

Vous pouvez également modifier le nom d'une gare en changeant son nom dans la zone de texte en haut de l'écran. Cliquez simplement sur le nom et entrez le nouveau nom. Appuyez sur la touche 'Entrée' et votre modification est prise en compte.

Au bas de cet écran, sont affichées les marchandises en attente de livraison dans cette gare. Cette information est structurée pour afficher tous les mêmes types de marchandise ensemble. Par exemple:
402 voitures en attente
83 passagers en attente

Chacune de ces entêtes est éclatée en sous-entête, qui indiquent où les marchandises doivent aller, et leur nombre exact. Par exemple:

56 passagers en attente
- 25 passagers > Paris gare
- 29 passagers > Lyon gare via. Paris arrêt sud
- 2 passagers > Marseille gare

Dans le coin supérieur droit se trouve un bouton nommé 'Détails'. Cliquez dessus pour faire ouvrir une autre fenêtre indiquant les besoins en marchandises des industries locales, et les liaisons directes avec cette gare.

Les liaisons directes sont définies pour chaque type de marchandise.

Actualisé pour Simutrans 0.83.1.2
02/01/2003

simutrans-124.5/simutrans/text/gr.tab000066400000000000000000000520511520675460500177000ustar00rootroot00000000000000§Ellinika PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: gr Ellinika # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 3.04 2025 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable απενεργοποιημένο cl_btn_filter_enable ενεργοποιημένο cl_btn_filter_settings Ρυθμίσεις cl_btn_sort_asc με αύξουσα σειρά cl_btn_sort_desc με φθίνουσα σειρά cl_btn_sort_id εσωτερικό ID cl_btn_sort_income Εισόδημα cl_btn_sort_name Όνομα cl_btn_sort_type Είδος clf_btn_alle όλα clf_btn_keine κανένα gl_btn_sort_name ανά όνομα gl_btn_unsort αταξινόμητο hl_btn_filter_disable απενεργοποιημένο hl_btn_filter_enable ενεργοποιημένο hl_btn_filter_settings Ρυθμίσεις hl_btn_sort_asc ανιών hl_btn_sort_desc κατιών hl_btn_sort_name Όνομα hl_btn_sort_type Είδος hl_btn_sort_waiting Αναμονή hlf_btn_alle όλα hlf_btn_invers αντίστροφα hlf_btn_keine κανένα Scenario Πληροφορίες για το σενάριο #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic αρκτικό κλίμα desert κλίμα της ερήμου #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Cannot built depot here! Δεν μπορείτε να χτίσετε έναν χώρο στάθμευσης εδώ. Cannot create generic line!\nSelect line type by\nusing filter tabs. Δεν μπορείτε να φτιάξετε μια γραμμή γενικού τύπου!\nΕπιλέξτε ένα είδος γραμμής\nχρησιμοποιώντας φίλτρα καρτελών. Das Feld gehoert\neinem anderen Spieler\n Αυτό το τμήμα\nτου εδάφους ανήκει σε\nέναν άλλο παίκτη!\n Der Besitzer erlaubt das Entfernen nicht \nΟ ιδιοκτήτης δεν δίνει\nάδεια για να\nτο διαγράψετε!\n Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Δεν μπορείτε να χτίσετε\n\nκτίριο αεροδρομίου εδώ.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Δεν μπορείτε να τοποθετήσετε\nσήματα σιδηροδρόμων εδώ.\n Lost connection\nto server! Χαμένη σύνδεση\nμε τον διακομιστή! Lost synchronisation\nwith server. Χάθηκε ο συγχρονισμός\nμε τον διακομιστή Not enough money! Δεν έχεις αρκετά\nχρήματα για να το χτίσεις! Schiffhalt muss im\nWasser liegen!\n Το stop για τα πλοία μπορει\nνα τοποθετηθεί μόνο\nκοντά σε λιμάνι!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Show the owenership of infrastructure Εμφάνιση κυριότητας των υποδομών #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s τώρα\nπροσφέρει υπηρεσίες για λεωφορεία\nμεταξύ %s\nκαι δελεαστικές προτάσεις\n%s\nat (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s τώρα\nπροσφέρει υπηρεσίες για λεωφορεία\nμεταξύ %s\nκαι εργοστάσιο\n%s\nat (%i,%i).\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ LISTTOOLS Λίστες ROADTOOLS Εργαλεία για δρόμους SHIPTOOLS ναυτιλιακά εργαλεία #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s's έχει χτίσει ένα καινούριο διοικητήριο New %s now available:\n%s\n Καινούριο %s τώρα διαθέσιμο:\n%s\n\n Now %u clients connected. Τώρα ακριβώς %u οι πελάτες είναι συνδεδεμένοι. Remove vehicle from map. Use with care! Αφαίρεσε το όχημα από τον χάρτη #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (στον χώρο στάθμευσης οχημάτων) \nBauzeit bis μέχρι \nBauzeit von \nΕμφανίζεται από \nCan't open heightfield file.\n \nΑδυναμία φόρτωσης ύψους του χάρτη.\n\n \ndirection: \nκατευθύνσεις: \nelektrified \nμε ηλεκτρισμό\n \nHeightfield has wrong image type.\n \nΤο πεδίο ύψους έχει λανθασμένο\nτύπο εικόνας.\n \nis reserved by: \nδεσμευμένο από τρένο \nminimum speed: \nελάχιστη ταχύτητα: \nnot elektrified \nχωρίς ηλεκτρισμό\n \nRibi (masked) \nκατευθύνσεις\m (με απόκρυψη): \nRibi (unmasked) \nκατευθύνσεις\n (χωρίς απόκρυψη): \nsingle way \nΑπλή διαδρομή \nway1 reserved by διαδρομή 1 δεσμευμένη από\n \nway2 reserved by διαδρομή 2 δεσμευμένη από\n \nwith sign/signal\n \nμε σήμα/σήμα\n %d buildings\n %d κτίρια %d convois %d φάλαγγες οχημάτων %d Einzelfahrzeuge im Depot %d οχήματα σταθμευμένα εδώ. %i km/h (max. %ikm/h) %i χμ/ώ (μέγ. %iχμ/ώ) %s building %s %s %s %s %s %s city %d %s %s πόλη %d %s %s has entered a depot. %s έχει μπει στον χώρο στάθμευσης οχημάτων. %s land %d %s %s γη %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s \nέχτισε ένα\nκαινούριο δημαρχείο\nόταν αυξήθηκαν\n%i οι κάτοικοι. %s\nis crowded. %s\σταθμός γεμάτος. %s\nwas liquidated. %s\nέχει εξοφληθεί. <δεν υπάρχει γραμμή> 1 convoi 1 φάλαγγα οχημάτων 1 Einzelfahrzeug im Depot 1 όχημα πάρκαρε εδώ. 1LIGHT_CHOOSE Φωτεινότητα: 1WORLD_CHOOSE Ρυθμίσεις νέου παιχνιδιού: Apply Line Εφαρμογή γραμμής April Απρίλιος Arbeiter aus: Οι εργαζόμενοι ζουν σε: August Αύγουστος Autohalt muss auf\nStrasse liegen!\n Οι χώροι στάθμευσης αυτοκινήτων ή λεωφορείων\nπρέπει να τοποθετηθούν\nκοντά στον δρόμο. Available Διαθέσιμο Bahndepot Χώρος στάθμευσης τρένων Bankrott:\n\nDu bist bankrott.\n Χρεοκοπημένος:\n\nΕίστε χρεοκοπημένος!\n battery Μπαταρία bridge is too high for its type! Για τέτοιου τύπου γέφυρες, το ύψος αυτό είναι πολύ μεγάλο! Bridge is too long for this type!\n Για τέτοιου τύπου γέφυρες,\nτο άνοιγμα είναι πολύ μεγάλο.\n Bruecke Γέφυρα Bruecke muss an\neinfachem\nHang beginnen!\n Οι γέφυρες πρέπει\nνα ξεκινούν από μια\nεπιφάνεια με κλίση!\n Brueckenboden γέφυρα Build air depot Δεν υπάρχει! build choosesignals Δημιουργία πλατφόρμας, επιλογή σημάτων Build city market Χτίστε μια νέα αγορά στην κοντινότερη πόλη. Build drain Σταθμός μετασχηματιστών build HQ Χτίστε HQ Build land consumer Χτίστε ένα νέο σταθμό ενέργειας. Build maglev depot Χτίστε έναν χώρο στάθμευσης για αεροτρένα. Build monorail depot Χτίστε χώρο στάθμευσης αεροτρένων μονής τροχιάς. Build narrowgauge depot Χτίστε χώρο στάθμευσης για τρένα με στενές γραμμές. Build powerline Χτίστε γραμμή μεταφοράς ενέργειας. Build presignals Τοποθετήστε σήματα διπλού μπλοκ. Build road depot Χτίστε χώρο στάθμευσης αυτοκινήτων. Build ship depot Χτίστε ναυπηγείο. Build signals Τοποθετήστε σήματα. Build train depot Χτίστε χώρο στάθμευσης τρένων. Build tram depot Χτίστε χώρο στάθμευσης τραμ. Build truck depot Χτίστε χώρο στάθμευσης αυτοκινήτων. Building costs estimates Δεν υπάρχει! Buildings Δεν υπάρχει! Built artifical slopes Κατασκευάστε τεχνητές επιφάνειες. Built random attraction Κατασκευάστε τυχαία κάτι ελκυστικό. Bus_tab Λεωφορεία Can only move from halt to halt or waypoint to waypoint. Η κίνηση είναι δυνατή μόνο n\από στάση σε στάση ή \nαπό ένα ενδιάμεσο σημείο σε ένα άλλο. Cancel Άκυρο Capacity: %s\nLoad: %d (%d%%) Χωρητικότητα: %s\n? %d (%d%%) Cars are not available yet! Δεν υπάρχουν διαθέσιμα αυτοκίνητα ακόμη. cars.\nstate αυτοκίνητα\n Cash Υπόλοιπο λογαριασμού Change player Αλλάξτε παίκτη Chart Διάγραμμα Choose direction Δεν υπάρχει! Choose operation executed on clicking stored/new vehicles Επιλέξτε τη λειτουργία που θα εκτελεστεί με κλικ στα παλιά/καινούρια αυτοκίνητα. chooses a random map Επιλέξτε έναν τυχαίο χάρτη. citicens Πολίτες City attraction Δεν υπάρχει! City industries Αγορές στις πόλεις City list Κατάλογος πόλεων City size Μέγεθος πόλης city_road Δρόμος πόλης citybuilding builder Δεν υπάρχει! CityLimit Όρια πόλης cl_title Κατάλογος οχημάτων cl_txt_sort Ταξινόμηση κατά: clf_chk_aircrafts αεροπλάνα clf_chk_cars Λεωφορεία/Φορτηγά clf_chk_indepot μέσα σε χώρο στάθμευσης clf_chk_maglev Αεροτρένα clf_chk_monorail Αεροτρένα μονής τροχιάς clf_chk_name_filter Ονόματα φίλτρων: Copy Convoi Αντιγραφή φάλαγγας οχημάτων Copy the selected convoi and its schedule or line Αντιγράψτε την επιλεγμένη φάλαγγα οχημάτων και το χρονοδιάγραμμά της ή τη γραμμή Create a new line based on this schedule Κατασκευάστε μια νέα γραμμή βασισμένη σε αυτό το χρονοδιάγραμμα CURRENCY € Deccelerate time Χρόνος επιβράδυνσης December Δεκέμβριος Del Stop Αφαίρεση Delete Line Διαγραφή γραμμής Delete this file. Διαγραφή αυτού του αρχείου. Denkmal Μνημείο Depots Χώροι στάθμευσης Der Tunnel ist nicht frei!\n Η σήραγγα δεν είναι άδεια.\n Destination Προορισμός Details Λεπτομέρειες Die Bruecke ist nicht frei!\n Η γέφυρα δεν είναι ελεύθερη!\n diesel πετρελαιοκινητήρας Display settings Ρυθμίσεις εμφάνισης Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Έχετε %d μήνες για να εξοφλήσετε τις οφειλές σας. Filter: Φίλτρο: follow me Ακολουθώ Follow the convoi on the map. Ακολούθησε την αυτοκινητοπομπή στο χάρτη Forest Δάσος Found new city Βρες καινούρια πόλη Frame time: Χρονικό πλαίσιο Free Capacity Ελεύθερος χώρος freeplay mode ελεύθερος τρόπος παιχνιδιού Full load Ελάχιστο φορτίο GAME PAUSED Το παιχνίδι έχει διακοπή Game_msg Γενικός Gebaeude Κτίριο Gewicht Βάρος Gewinn Εισόδημα: Give the selected vehicle(s) an individual schedule Δώστε στο επιλεγμένο όχημα(τα) ένα ατομικό χρονοδιάγραμμα. gl_btn_sort_catg ανά κατηγορία gl_title Κατάλογος όλων των αγαθών go home Μετάβαση στον χώρο στάθμευσης Goods Εμπορεύματα Goods list Κατάλογος εμπορευμάτων Gross Profit Ρευστότητα επιχείρησης Groundobj Αντικείμενο Grow city Μεγέθυνση πόλης. Growth Ανάπτυξη πόλης H στάση Hangar Υπόστεγο Happy Ευτιχισμένος Haus kaufen Αγόρασε σπίτι Helligk. Εμφάνιση Help Βοήθεια Help text not found Λείπει το κείμενο βοήθειας. hide all building απόκρυψη όλων των κτιρίων hide city building απόκρυψη των κτιρίων της πόλης hide station names απόκρυψε τα ονόματα των σταθμών hide transparent διαφάνεια αντί απόκρυψης hide trees Απόκρυψη δέντρων Hier warten/lagern: Εμπορεύματα και επιβάτες σε αναμονή: hl_title Κατάλογος σταθμών hl_txt_filter Φίλτρο: hl_txt_sort Ταξινόμηση κατά: hlf_chk_airport Αεροδρόμια hlf_chk_anleger Αποβάθρα hlf_chk_bahnhof Σιδηροδρομικός σταθμός hlf_chk_bushalt Στάση λεωφορείου hlf_chk_frachthof Εξέδρα φόρτωσης hlf_chk_keine_verb δεν υπάρχει σύνδεση hlf_chk_maglevstop Σταθμός αεροτρένου hlf_chk_monorailstop Στάση αεροτρένου μονής τροχιάς hlf_chk_name_filter Ονόματα φίλτρων: hlf_chk_overflow Υπερπλήρης hlf_chk_spezial_filter Ειδικό φίλτρο: hlf_chk_type_filter Τύποι φίλτρων: Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Ο σταθμός δεν βρέθηκε.\nΠρέπει να στείλεις το \nόχημα εκεί χειροκίνητα Homeless Άστεγος industrial building Βιομηχανικά κτήρια Init map ... Αρχικοποίηση του χάρτη Input Είσοδος Ins Stop Εισάγω Intro. date: Εισαγωγή. ημερομηνία: January Ιανουάριος join game Παίξε στο ίντερνετ July Ιούλιος June Ιούνιος Kein Besitzer\n Χωρίς ιδιοκτήτη\n koord συντεταγμένες Laden Φόρτωσε καινούριο παιχνίδι LANG_CHOOSE\n Παρακαλώ επέλεξε την \n γλώσσα που προτιμάς:\n Last Month Προηγούμενος μήνας: Last Year Προηγούμενος χρόνος: Leistung Ισχύς letzen Monat: diesen Monat: προηγούμενος μήνας: αυτός ο μήνας: LKW_tab Φορτηγά Load scenario Φόρτωσε σενάριο loaded φορτωμένο Loading map ... Φόρτωση του χάρτη ... Mailbox Κέντρο μηνυμάτων Manual (Human) Εγχειρίδιο (Άνθρωπος) Max income: Μέγιστο. Εισόδιμα: Max. speed: Μέγιστη. ταχύτητα maximum length of rivers Μέγιστος. αριθμός ποταμών Maxspeed Μέγιστη ταχύτητα May Μάιος Meldung Μήνυμα Menge ποσό MessageOptionsText \nΚαινούριος χρόνος\n\nΝέα παιχτών\n\nΝέα της πόλης\n\nΚαμία διαδρομή\n\nΚαινούριοι προορισμού\n\nChat\n\nΚαινούρια οχήματα\n\nΟ σταθμός είναι πλήρης\n\nΠροβλήματα\n\nΚυκλοφοριακή συμφόρηση\n\nΣενάριο min Ελάχιστο minimum length of rivers Ελάχιστος αριθμός ποταμιών Missing pakfiles Τα αντικείμενα λείπουν Months Μήνες Mountain height Ύψος βουνού: Music playing disabled/not available Η μουσική είναι απενεργοποιημένη/μη διαθέσιμη mute sound Σίγαση Name Όνομα Neue Karte Καινούριο παιχνίδι Neue Welt Δημιούργησε καινούριο παιχνίδι New Vehicles Καινούρια οχήματα No Route Καμία διαδρομή No suitable ground! Όχι διαθέσιμο έδαφος! no tree Όχι δέντρα No. of Factories Εργοστάσια και μαγαζιά November Νοέμβριος Number of rivers Αριθμός των ποταμών Object Αντικείμενο Odometer: %s km Οδόμετρο. %s km Ok Εντάξει Oktober Οκτώβριος On this map, you are not\nallowed to change player!\n Αυτό το παιχνίδι είναι κλειδωμένο.\nΔεν είναι δυνατές αλλαγές από τον παίκτη.\n Only one transformer per factory! Μόνο ένας μεταφορέας για κάθε εργοστάσιο Optionen Επιλογές Passagiere Επιβάτες Password Κωδικός: player Παίχτης player 11 Παίχτης 11 player 12 Παίχτης 12 player 13 Παίχτης 13 player 2 Trikky Transport player 3 Meyer Moving Co. Problems_msg Προβλήματα Produktion Παραγωγή: q1 Άνοιξη q2 Καλοκαίρι q4 Χειμώνας Random age τυχαίος χρόνος Random map Τυχαίος χάρτης Reliefkarte Χάρτης Remove Διέγραψε την ετικέτα remove roads Αφαίρεσε τους δρόμους replace stop Αντικατέστησε το stop road δρόμος Rotate map Περιστροφή του χάρτη Routing Διαδρομή sack σάκοι Saving map ... Αποθήκευση χάρτη ... Scenario information Τρέχουσες λεπτομέρειες για το σενάριο Scenario Result Εξέλιξη Scenario Rules Κανόνες Schiff_tab Καράβια Seasons Εποχές Sehenswuerdigkeit Έλξη τουριστών Select a server to join: Παράθυρο πληροφοριών του διακομιστή Sell the selected vehicle(s) Πούλα το επιλεγμένο όχημα(οχήματα) sended Στείλε mail September Σεπτέμβριος Service Υπηρεσία Setting Ρυθμίσεις Ship Πλοίο shops and stores Μαγαζιά και γραφεία Show all Εμφάνιση όλων show all building Εμφάνισε όλα τα κτήρια Show legend Επιλογές Show schedules Εμφάνισε τα δρομολόγια show station names Εμφάνισε τα ονόματα των σταθμών show waiting bars Εμφάνισε την μπάρα αναμονής Show/hide statistics Εμφάνισε/Απόκριψε τα στατιστικά Sprache Γλώσσα Start Έναρξη Starte Spiel Έναρξη παιχνιδιού #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL Δεν υπάρχει! &1_CITY_SYLL πόλη &5_CITY_SYLL αστικό κέντρο &7_CITY_SYLL χωράφι &8_CITY_SYLL στόμα &9_CITY_SYLL κλειδώνω &A_CITY_SYLL διασταύρωση &D_CITY_SYLL Δεν υπάρχει! &E_CITY_SYLL Δεν υπάρχει! %0_CITY_SYLL Δεν υπάρχει! %2_CITY_SYLL πεζόδρομος %4_CITY_SYLL άνοιξη %A_CITY_SYLL Παλιό %B_CITY_SYLL Λόφος %C_CITY_SYLL Δεν υπάρχει! %D_CITY_SYLL Δεν υπάρχει! %E_CITY_SYLL Δεν υπάρχει! %F_CITY_SYLL Δεν υπάρχει! 1center %s %s 1suburb %s %s %s simutrans-124.5/simutrans/text/hr.tab000066400000000000000000001065761520675460500177150ustar00rootroot00000000000000§Hrvatski PROP_FONT_FILE prop-latin2.fnt ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: hr Hrvatski # # Encoding: UTF-8 # # Font: prop-latin2.fnt # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable onemoguceno cl_btn_filter_enable omoguceno cl_btn_filter_settings Postavke cl_btn_sort_asc rastuce cl_btn_sort_desc padajuce cl_btn_sort_id unutarnji ID cl_btn_sort_income Prihod cl_btn_sort_name Ime cl_btn_sort_type Tip clf_btn_alle sve clf_btn_invers inv. clf_btn_keine nijedno gl_btn_sort_bonus bonusom gl_btn_sort_name ime gl_btn_sort_revenue prihod gl_btn_unsort nesortirano hl_btn_filter_disable onemoguceno hl_btn_filter_enable omoguceno hl_btn_filter_settings Opcije hl_btn_sort_asc uzlazno hl_btn_sort_desc silazno hl_btn_sort_name Ime hl_btn_sort_type Tip hl_btn_sort_waiting Cekaju hlf_btn_alle sve hlf_btn_invers obrnuti hlf_btn_keine nista #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Arkticka klima desert pustinjska klima mediterran Mediteranska klima rocky alpska klima temperate umjerena klima tropic tropska klima #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Cannot built depot here! Depo se ne moze ovdje izgraditi. Cannot built this station/building\nin underground mode here. Zgrada se ne moze\n graditi podzemno.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. Nije moguce napraviti genericku liniju!\nOdbrati tip linije\nkoristeci filtere. Cannot create socket Nije moguce otvoriti socket Convoi handles exhausted! Dostignut najveci broj konvoja. Das Feld gehoert\neinem anderen Spieler\n Ovaj dio \nzemlje posjeduje \ndrugi igrac! Der Besitzer erlaubt das Entfernen nicht \nVlasnik odbija\ndozvolu za\nuklnjanje ovoga!\n Diese Zusammenstellung kann nicht fahren!\n Ova kombinacija\se ne moze kretati!\n Flugzeughalt muss auf\nRunway liegen!\n Stajanje zrakoplova mora\nbiti na voznoj stazi. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Ne mozete sagraditi\n\naerodromsku zgradu ovdje.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Ne mozete staviti\nzeljeznicki signal ovdje.\n Lost connection\nto server! Izgubljena veza\nna server! Lost synchronisation\nwith server. Izgubljena sinhronizacija\nna server. Maglevhalt muss auf\nMaglevschiene liegen!\n Meglev stajaliste se mora nalaziti na maglev tracnici! Monorailhalt muss auf\nMonorail liegen!\n Monorail stajalista moraju\nbiti na monorail tracnici. Monorails are not available yet! Monorail jos nije dostupan. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n stajaliste uskog kolosijeka be trebalo biti na uskom kolosijeku No through station here! Stanica se mora postaviti na krajevima\n puteva ili ravnim dijelovima bez krizanja. Not enough money! Nemas dovoljno\nnovaca za gradnju ovoga! On narrowgauge track only!\n Samo na uskom kolosijeu!\n Post muss neben\nHaltestelle\nliegen!\n Zgrada prosirenja stanice\n(npr. posta, skladiste)\nmora biti postavljena na slobodnu plocicu\nodmah do postojeceg stajalista/stanice.\n Protocoll error (expecting game) Greska protokola (ocekivana igra) Schiffhalt muss im\nWasser liegen!\n Brodsko stajaliste moze samo\nbiti postavljeno na vodu\nblizu doka!\n Terraforming not possible\nhere in underground view Oblikovanje zemlje nemoguce pod zemljom. Upgrade must have\na higher level Nadogradnja mora imati\nveci nivo Zughalt muss auf\nSchiene liegen!\n Zeljeznicke stanice moraju\nbiti postavljene na\ntracnice.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Keyboard Help\n

Keyboard Help

\n Tipkovnicka\n

Tipkovnicka Pomoc

\n

\nTipkovncika Pomoc prikazuje funkcije za razne pritiske tipaka na tipkovnici.\n

\n

\nTipkovnicka pomoc otvara se kada se stisne nedodijeljena tipka ili od Opca Pomoc.\n

\n

\nPritisci tipke ovise o malim i velikim slovima (koristiti [Shift] za velika slova).\n

\n

\nTipke sa pridodijeljenim funkcijama ukljucuju:\n

\n

\n[Strelice]: pomicanje pogleda igre u smjeru strelice.
\n[Backspace]: zatvranje svih prozora, alatnih traka i tekstova pomoci u pogledu igre.
\n[Delete], ili [Escape]: zatvoriti vrsni prozor, alatnu traku ili tekst pomoci u pogledu igre.
\n[Enter], ili [Return]: koristenje za potvrdu akcija.
\n[Page-Up], ili [>]: zoom-in pogled u igri.
\n[Page-Down], ili [ less than symbol]: zoom-out pogled u igri.\n[F1]: otvoriti Simutrans Pomoc.

\n

\n[1]: pomicanje pogleda igre juzno.
\n[2]: pomicanje pogleda igre jugoistocno.
\n[3]: pomicanje pogleda igre istocno.
\n[4]: pomicanje pogleda igre jugozapadno.
\n[6]: pomicanje pogleda igre sjeveroistocno.
\n[7]: pomicanje pogleda igre zapadno.
\n[8]: pomicanje pogleda igre sjevernozapadno.
\n[9]: pomicanje pogleda igre sjeverno.\n

\n

\n[Shift] + mis: koristen na Map za pregled poveznica u Lancu Opskrbe Industrije.
\n[CTRL] + tool: konstrukcija (signala & Stajalista) na gornjem nivou; ili gradnja sporijih cesta i tracnica preko brzih; ili gradnja ravnijih (direktnijih) ulica i tracnica.
\n[CTRL] + ([F2] do [F12]): postavlja odabir trenutnog alata za pritisak tipaka [F2] do [F12].\n

\n

#____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s sada\nnudi uslugu prijevoza busom\nizmedju %s\ni atrakcije\n%s\nna (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s sada\nnudi uslugu prijevoza busom\nizmedju %s\ni tvornice\n%s\nna (%i, %i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\sada ima\n%i kaimona izmedju\n%s na (%i,%i)\ni %s\na (%i, %i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\notvara novu zeljeznicu\nizmedju %s\nna (%i,%i) i\n%s\nna (%i,%i).\n Airline service by\n%s\nnow between\n%s \nand %s.\n Avionska sluzba od\n%s\nizmedju\n%s \ni %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Trajektni promet od\n%s\nsada izmedju\n%s \n i %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\notvara novi prekoselski\nautobus servis izmedju\n%s and\n%s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Alati za uredjivanje mape LISTTOOLS Liste MAGLEVTOOLS Maglev alati MONORAILTOOLS Monorail/maglev alati NARROWGAUGETOOLS Alati uskog kolosijeka RAILTOOLS Tracnicki alati ROADTOOLS Cestovni alati SHIPTOOLS Alati luke SLOPETOOLS Vertikalno dici/spustiti teren SPECIALTOOLS Specijalni alati konstrukcije TRAMTOOLS Alati Tramvaja/lakosinske zeljeznice #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s s\nsredisnjica sada\nna (%i,%i). Factory chain extended\nfor %s near\n%s built with\n%i factories. Ekonomija jaca:\n%s bilzu %s povecava lanac.\n%i nova tvornica osnovana. New %s now available:\n%s\n Novo %s sada dostupno:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Rast industrije:Novi lanac industrije\m za %s blizu\n%s sagradjeno sa\n%i tvornica. New vehicle now available:\n%s\n \n Novo vozilo je\n sada dostupno:\n\n\n -- %s --\n\n Remove vehicle from map. Use with care! Ukloniti vozilo sa mape. (Koristi pazljivo!) Screenshot\ngespeichert.\n Snimak ekrana\nspremljen.\n Sends the convoi to the last depot it departed from! Salje konvoj u zadnji depo iz kojega je posao. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. Sa velikim festivalom,\n %s sagradnjen\nnovi spomenik\n%i gradjana su se veselili. #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (u depou) \nBauzeit bis do \nBauzeit von \nDostupno od \nCan't open heightfield file.\n \nNemoguce otvoriti mapu visina.\n \ndirection: \nsmjerovi: \nelektrified \nelektrificirano\n \nHeightfield has wrong image type.\n \nPolje visine ima krivi\tip slike.\n \nis reserved by: \nrezervirano od vlaka \nminimum speed: \nnajmanja brzina: \nnot elektrified \nnije elektrificirano\n \nRibi (masked) \nsmjerovi\m (maskirano): \nRibi (unmasked) \nsmjerovi\n (nemaskirano): \nSet phases: \nnamjestiti duzinu ns/ew: \nsingle way \nJednosmjeran put \nway1 reserved by \nput 1 rezerviran od \nway2 reserved by \nput 2 rezerviran od \nwith sign/signal\n \nsa znakom/signalom\n %d buildings\n %d objekata %d convois %d konvoja %d Einzelfahrzeuge im Depot %d vozila ovdje pohranjeno. %s building %s %s %s %s %s %s city %d %s %s grad %d %s %s factory %s %s %s %s dvorista %s has entered a depot. %s je usao u depo. %s land %d %s %s %d izvan %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s je trosio\ntvoj novac na\nnovu vijecnicu\nkada je dosegao\n%i stanovnika. %s\nis crowded. %s\nje pretrpana %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\nbrzina %i\nmax brzina %i\ndx:%i dy:%i %s\nwas liquidated. %s\n je likvidiran. 1 convoi 1 konvoj 1 Einzelfahrzeug im Depot 1 vozilo ovdje spremljeno. 1LIGHT_CHOOSE Osvijetljenost: 1WORLD_CHOOSE Postavke za Novu Igru 2LIGHT_CHOOSE Boje: 2WORLD_CHOOSE Broj mape: 3LIGHT_CHOOSE Brzina pomicanja: 4LIGHT_CHOOSE Obrnuto pomicanje 5LIGHT_CHOOSE Pjesaci na stajalistima 5WORLD_CHOOSE Br. gradova: 6LIGHT_CHOOSE Pjesaci u gradovima 6WORLD_CHOOSE Gustoca prometa: 8WORLD_CHOOSE promjena dana i noci A bridge must start on a way! Ne posjedujete krajeve mosta,\n ili se nesto naslo na njegovom putu. Abfrage Istrazivacki alat Abnehmer Potrosac About O Abriss Uništi/Ukloni Absenken Snizi zemljište Accelerate time Vrijeme ubrzanja Active player only Samo aktivan igrač Add forest Dodaj šumu Add random citycar Dodaj slucajan gradski automobil Add Stop Dodaj postaju Add stops for backward travel Dodaj postaje za povratak air Pista aircraft_tab Teretni avioni airplane Avion Airport Aerodrom AIRTOOLS Alati za aerodrom All Sve all convoi tooltips Savjet za sve konvoje Allow city growth Dozvoli razvoj grada Allow player change Dopustiti promjenu igraca allowed climates:\n Dozvoljene klime Alters a schedule. Dodaj/ukloni postaje iz voznog reda Angenommene Waren Sirovine potrebne obliznjim industrijama anhaengen Dodaj Anhaenger_tab Prikolice Anheben Izdigni teren Appends stops at the end of the schedule Dodaj postaju na kraj linije Apply Line Primjeni liniju April Travanj Arbeiter aus: Radnici zive u: Arrived Opsrkba Assets Imovina Aufloesen Rastavi August Kolovoz Autohalt muss auf\nStrasse liegen!\n Autobusne postaje\nmoraju se\nnalazitina cesti. Available Dostupno Bahndepot Depo za vlakove. Bankrott:\n\nDu bist bankrott.\n Bankrot:\n\nBankrotirali ste!\n battery Akumulator Baum Drvo baum builder Posadi drvece Baustelle Gradiliste Bauzeit Vrijeme izgradnje Beenden Kraj Beginner mode Pocetnicki Mod Besonderes Gebaeude Turisticka atrakcija BF postaja bio bioloski Blockstrecke ist\nbelegt\n \nTracnicu koristi\ndrugi vlak\n Boden Zemljiste bridge is too high for its type! Most je previsok za svoj tip! Bridge is too long for this type!\n Raspon mosta\nje prevelik za\novaj tip mosta.\n Bruecke Most Bruecke muss an\neinfachem\nHang beginnen!\n Most mora\nnpoceti na\npravom nagibu!\n Brueckenboden most Build air depot Izgradi hangar build choosesignals Izgradi znak za odabir platforme Build city market Izgradi novu prodavaonicu u najblizem gradu. Build drain Trafostanica build HQ Izgradi sredisnjicu Build land consumer Izgradi novu elektricnu centralu Build maglev depot Izgradi depo za Maglev vlakove Build monorail depot Izgradi depo za Monorail vozove Build narrowgauge depot Izgradi depo uskotracne pruge Build powerline Izgradi dalekovod Build presignals Izgradi dvoblokni signal Build road depot Izgradi garazu Build ship depot Izgradi brodogradiliste Build signals Napravi semafor Build train depot Izgradi depo za vlakove Build tram depot Izgradi depo za tramvaje Build truck depot Izgradi depo za kamione Building costs estimates Procijenjena cijena izgradnje Buildings Broj gradjevina Built artifical slopes Izgradi umjetne nasipe Built random attraction Izgradi nasumicnu turisticku atrakciju Bus_tab Autobusi Can only move from halt to halt or waypoint to waypoint. Moze se kretati\nsamo od postaje do postaje\ili od tocke do tocke. Cancel Ponistiti Capacity: %d%s %s\n Kapacitet: %3d %s %s Capacity: %s\nLoad: %d (%d%%) Kapacitet: %s\n? %d (%d%%) Cars are not available yet! Automobili jos nisu dostupni. cars.\nstate automobili\n Cash Stanje racuna Change player Promjena igraca Chart Grafikon Choose direction Odabir smjera Choose operation executed on clicking stored/new vehicles Odabir operacije za izvrsenje pri kliku na spremljena/nova vozila chooses a random map Odabire nasumicnu mapu. citicens Gradjani City attraction Gradske atrakcije City industries Trznice u gradovima City list Lista gradova City size Velicina grada city_road Gradska ulica citybuilding builder Gradnja urbanih zgrada CityLimit Granice grada cl_title Lista vozila cl_txt_sort Sortirati: Clear block reservation Prikaz/reset reserviranih puteva clf_chk_aircrafts Zrakoplovi clf_chk_cars Autobusi/kamioni clf_chk_indepot u depou clf_chk_maglev Maglevi clf_chk_monorail Monorailovi clf_chk_name_filter Imena filtera: clf_chk_narrowgauge Vlakovi uskog kolosijeka clf_chk_noincome bez prihoda clf_chk_noline nema linije clf_chk_noroute nema rute clf_chk_noschedule nema rasporeda clf_chk_ships Brodovi clf_chk_spezial_filter Specijalni filter: clf_chk_stucked zaglavljeno clf_chk_trains Vlakovi clf_chk_trams Tramvaji clf_chk_type_filter Tipovi filtera: clf_chk_waren Filter tereta: clf_title Filter liste vozila Climate Control Postaviti klimu closed zatvoreno. COLOR_CHOOSE\n Molimo odabrati a\nboju iz tablice:\n Company bankrupt Kompanija bankrotirana Congratulation\nScenario was complete in\n%i months %i years. Cestitke!\nScenario je\nzavrsen unutar \n%i mjeseci i %i godina! Connected stops Spojena stajalista Constructed by Nacrtano od Constructed by %s Napravio %s Construction_Btn Troskovi gradnje convoi %d of %d Konvoj %d od %d convoi error tooltips Savjeti greske konvoja Convoi has been sent\nto the nearest depot\nof appropriate type.\n Konvoj je poslan\nu najblizi depo \nprikladne vrste.\n Convoi is sold when all wagons are empty. Konvoj je prodan kad su svi vagoni prazni. convoi mouseover tooltips Savjeti konvoja prelaska misem convoi passed last\nmonth %i\n \nkonvoja proslo zasnji\nmjesec: %i\n Convois Konvoj Convois: %d\nProfit: %s Vozila: %d\nPrihod: %s Convoys Konvoji Copy Convoi Kopiraj Konvoj Copy the selected convoi and its schedule or line Kopiraj odabrani konvoj i njegov vozni red ili liniju cost for removal Troskovi uklanjanja Costs Troskovi Create a new line based on this schedule Napravi novu liniju baziranu na ovom voznom redu curiosity builder Graditelj atrakcija curlist_title Lista atrakcija CURRENCY € Currently playing: Svira: Deccelerate time Vrijeme decelaracije December Prosinac decrease underground view level spustiti podzemni nivo pogleda Del Stop Ukloni Delete Line Obrisati liniju Delete the current stop Obrisati trenutno stajaliste Delete this file. Obrisati ovu datoteku. Denkmal Spomenik Departed Odaslan Depots Depoi Der Tunnel ist nicht frei!\n Tunel nije prazan.\n Destination Dolasci Details Detalji Die Bruecke ist nicht frei!\n Most nije slobodan!\n diesel dizel Direkt erreichbare Haltestellen Direktne rute odavde disable midi Zanijemiti MIDI glazbu Display settings Opcije prikaza Distance Udaljenost Dock dok Dock must be built on single slope! Dok mora biti sagradjen na poziciji s kosinom. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Imate %d mjeseca da otplatite svoj dug. Durchsatz Max. Economy Ekonomija i gradovi Eigenbesitz\n Javni posjed\n Ein %s\npasst hier nicht.\n %s\nne pripada ovdje!\n Einstellungen aendern Promijeni opcije:\n electric elektricni pogon Electricity Elekticitet Electricity producer\n\n Proizvodjac elektriciteta\n\n Electrics_tab Elektricne Electrify track Elektrificiraj prugu enlarge map Povecati mapu enter a value between %i and %i Unijeti vrijednost izmedju %i i %i Error Greska Erzeuge neue Karte.\n Molimo Vas, pricekajte dok se nova\nmapa ne kreira.\n\n (Ovo bi moglo\n potrajati nekoliko minuta\n za velike mape.)\n Es wird bereits\nein Fahrplan\neingegeben\n Raspored se\nplanira.\nPrvo zavrsi\n prije nego se ponovno napravi!\n Fabrikanschluss Povezane tvornice Fabrikname ime tvornice Factories Tvornice factory details Tvornicke veze factorybuilder Graditelj tvornica Fahrplan Vozni red Fahrtziel Cilj: Fahrzeuge koennen so nicht entfernt werden Vozila ne mogu biti\nuklonjena na ovaj nacin Fahrzeuge: Vozila: Farbe Boja igraca Fast forward Ubrzati February Veljaca Ferry_tab Trajekti Fertig Gotovo Filename Datoteka: Finances of %s Financije %s Finanzen Financije fl_title lista tvornica Flug_tab Putnicki Avioni follow me Slijedi me. Follow the convoi on the map. Slijedi konvoj na mapi. Forest Suma Found new city sagraditi novi grad Fracht Teret Frame time: Okvirno vrijeme: Free Capacity Slobodan kapac. freeplay mode Slobodni modus Friction: trenutni faktor trenja: fuel_cell goriva celija Full load Min. napunjenost: Fussgaenger Pjesaci GAME PAUSED Igra je zaustavljena Gear: Vucna snaga: Gebaeude Gradjevina General Opcenito Gewicht Tezina Gewinn Dohodak: Give the selected vehicle(s) an individual schedule Dati odabranom(nim) vozilu(ima) poseban vozni red. gl_btn_sort_catg Kategorija gl_title Lista svih dobara go home Idi u depo. Goods Teret Goods AI AI tereta Goods list Lista dobara Gross Profit Tok novca Groundobj Objekt Grow city Povecati grad Growth Rast grada H postaja Happy Sretni Haus kaufen Kupiti kucu Helligk. Prikaz Help Pomoc Help text not found Tekst s pomoci nedostaje hide all building Sakrij sve gradjevine hide city building Sakrij gradjevine u gradovima hide station names sakriti imena stanica hide transparent transparentno umjesto sakriveno hide trees Sakrij stabla Hier warten/lagern: Roba i putnici koji cekaju: hl_title Lista stanica hl_txt_filter Filter: hl_txt_sort Sortirano: hlf_chk_airport Aerodromi hlf_chk_anleger Dok hlf_chk_bahnhof Zeljezincka stanica hlf_chk_bushalt Autobusna postaja hlf_chk_frachthof Zona za ukrcavanje hlf_chk_keine_verb nema veze hlf_chk_maglevstop Maglev stanica hlf_chk_monorailstop Monorail stajaliste hlf_chk_name_filter Imena filtera: hlf_chk_narrowgaugestop Stanica uskog kolosijeka hlf_chk_overflow Iznad kapaciteta hlf_chk_spezial_filter Poseban filter: hlf_chk_tramstop Stajaliste tramvaja hlf_chk_type_filter Tipovi filtera: hlf_chk_waren_abgabe Proizvodnja: hlf_chk_waren_annahme Potrebna roba: hlf_title Filter liste stanica Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depo nije nadjen.\nMorate poslati\nvozilo tamo rucno. Homeless Beskucnik hydrogene Vodik Idle: U mirovanju: ignore climates Ignoriranje klima Increase Industry density Povecavanje gustoce industrije increase underground view level povecati nivo podzemnog pogleda industrial building Industrijske zgrade Init map ... Postavljanje mape ... Input ulaz Ins Stop Umetnuti Insert stop before the current stop Umetnuti prije trentnog stajalista Intercity road len: Duzina medjugradske ceste: Intro. date: Datum uvodjenja: invalid nedefinirano. Invalid coordinate Neispravan redoslijed isometric map Izometricni pogled January Sijecanj July Srpanj Jump to Skociti na June Lipanj Kann Spielstand\nnicht laden.\n Nije moguce ucitati spremljenu igru! Kann Spielstand\nnicht speichern.\n Nije moguce otvoriti\nciljnu datoteku\nza pisanje! Kein Besitzer\n Bez vlasnika\n keine nema Keine Einzelfahrzeuge im Depot Nema ovdje spremljenih vozila. Keyboard_Help\n Pomoc tipkovnice\n koord koordinate Kreuzung Krizanje labellist_title Lista markera Lade Relief Ucitati Reljef Laden Ucitati Land attraction Zemaljske atrakcije Land industries Industrijski lanci: LANG_CHOOSE\n Molimo odaberite vas\n zeljeni jezik:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Year Zadnja godina: Leaving depot! Napustanje depoa! leer prazno Legend Legedna mape Leistung Snaga Leistung: %d kW Snaga: %d kW Leitung Dalekovod letzen Monat: diesen Monat: zadnji mjesec: ovaj mjesec: Line Linija Line Management Upravljanje linijom Lines serving this stop Linije ovoga stajalista LKW_tab Kamioni Load game Ucitati igru load height data from file Ucitati podatci visine iz datoteke. Load scenario Ucitati scenarij loaded ukrcano loaded passenger/freight Sortiranje putnika/tereta po Loading (%i->%i%%)! Utovarivanje (%i->%i%%) Loading addon paks ... Ucitavanje dodatnih paketa ... Loading map ... Ucitavanje mape ... Loading paks ... Ucitavanje paketa ... Loading skins ... Ucitanje koza ... Lock game Zabraniti promjene igraca (zahtijeva potvrdu). Lokomotive_tab Lokomotive maglev vehicle Maglev vozilo maglev_track Maglev tracnica Maglevdepot Maglev depo Mailbox Centar poruka Mailbox Options Opcije centra poruka Maintenance Odrzavanje make stop public (or join with public stop next) costs %i per tile and level Naciti stajalista javnima (ili spojiti sa susjednim javinim stajalistima) kosta %i$ po plocici i nivou Manual (Human) Uputsvo (ljudsko) Manufactured: Stvoreno: Map roughness hrapavost mape: map zoom zumiranje March Ozujak Margin (%%) Margina Marker Naciti znak Max income: Najveci prihod: Max. speed: Najveca brzina: Maximum 254 stops\nin a schedule!\n Najvise 254 stajalista\nu rasporedu!\n maximum length of rivers Max. duzina rijeke Maximum tile height difference reached. Najveca visina\nrazlika izmedju\ndvije plocice \nje dostignuta! May svibanj Median Citizen per town Srednji gradjani po gradu: Meldung Poruka Menge Iznos MessageOptionsText \nNova Godina\n\nAI novosti\n\nGradske novosti\n\nNema rute\n\nNove industrije\n\n"chat"\n\nNova vozila\n\nStanica puna\n\nProblemi\n\nUpozorenja min min. minimum length of rivers Min. duzina rijeka Modify the selected line Promjena odabrane linije Monate alt Mjeseca starosti. monorail vehicle monorail vozilo monorail_track Monorail tracnica Monorailboden Uzdignut put Monoraildepot Depo Monoraila month wait time mjeseci cekanja Months Mjeseci Monument Spomenik Monuments Spomenici Mountain height Velicina planine: Movingobj pokretni objekt Music playing disabled/not available Glazba onemogucena/nedostupna. Music volume: Jacina glazbe: mute sound Zanijemiti Name Ime Narrowgauge Uski kolosijek Narrowgauge are not available yet! Uski kolosijek jos nije dostupan! narrowgauge vehicle vozilo uskog kolosijeka narrowgauge_track Uska tracnica Narrowgaugedepot Depo uske tracnice Net Wealth Ukupna Imovina Neue Karte Nova mapa Neue Welt Stvoriti novu igru new convoi Novi konvoj New Line Nova linija New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Nova linija stvorena!\nMozes pridruziti liniju sada\nodabiruci je sa\nodabiraca linija gore. New Vehicles Nova vozila no buildings hidden nijedna zgrada skrivena no convois nema konvoja No goods are loaded onto this convoi. Nikakav teret nije ukrcan u konvoj no goods waiting teret ne ceka no load Nema tereta No Route Nema rute No stop here! Alat se mora koristiti na plocicama stajalista. No suitable ground! Nije prigodna podloga! No terminal station here! Nije moguce graditi terminal\n ovdje! Krajnji komad \nod ravne ceste/pruge je potreban. no timeline sve epohe no tree Nema stabala Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n "Gdje ja idem?"\nMorate ovome konvoju postaviti raspored ili dodijeliti mu liniju prije nego ode po nalog. none nema nord Sjever nordost Sjeveroistok nordwest Sjeverozapad Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Raspored vozila\nse ne smije mijenjati\n prilikom trazenja rute. Not enough fields would remain. Ne bi dovoljno polja\n ostalo oko farme. November Studeni Now active as %s.\n Sada aktivan kao %s.\n Number of rivers Broj rijeka Object Objekt Ok U redu Oktober Listopad On loan since %i month(s) U kreditu vec %i mjesec(i) On this map, you are not\nallowed to change player!\n Ova igra je zakljucana.\nPromjene od igraca nisu moguce.\n Only city chains Samo lanci gradova Only land chains Samo lanci sela open otvoreno. Operation Operativni Troskovi Ops Profit Operativni profit Optionen Opcije Origin Podrijetlo ost Istok Output Izlaz paletten sanduk Pas_tab Putnicki vlakovi Passagiere Putnici Passagierrate Nivo putnika Passagierziele ciljevi putnika/poste Passenger AI AI putnika Passengers %d %c, %d %c, %d no route Putnici %d %c, %d %c, %d nemaju rutu Passengers %d %s, %d %s, %d no route Putnici %d %s, %d %s, %d nemaju rutu Pause Pauza PaxDest Odredista Percent Electricity Elektricitet (%% potraznje): Planes are not available yet! Zrakoplovi jos nisu dostupni. Plant tree Posaditi stabla. player igrac player -1 Covjek player 0 Javni servis player 1 Napik 128 AS player 10 igrac 10 player 11 Igrac 11 player 12 Igrac 12 player 13 Igrac 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Freight Forwarder VM player 5 H-Trans Ltd player 6 PSK & Co KG player 7 Igrac 7 player 8 Igrac 8 player 9 Igrac 9 Please choose vehicles first\n Molimo prvo odaberi vozila!\n Post Posta Postrate Nivo poste Power Snaga Power: Snaga: Powerlines Dalekovodi Production of %s has been stopped:\n%s\n Proizvodnja od %s je ukinuta:\n%s\n Produktion Produkcija promote to line Promovirati u liniju q1 Proljece q2 Ljeto q3 Jesen q4 Zima rail car Tracnicko vozilo random slucajno Random age slucajna godina Random map Slucajna Mapa Rathaus Vijecnica Rating Procjena ratio_pax Omjer putnika Reliefkarte Mapa Remove Ukloniti remove airstrips ukloniti vozne staze remove channels Ukloniti kanale remove interm. signals Ukloniti signale izmedju remove maglev tracks ukloniti maglav tracnice remove monorails ukloniti monorailove remove narrowgauge tracks ukloniti uski kolosijek remove powerlines ukloniti dalekovode remove roads ukloniti ceste remove tracks ukloniti tracnice Remove wayobj %s Ukloniti wayobj %s replace other signals Zamjena postojecih signala replace stop Ukloniti stajaliste request closing traziti zatvaranje residential house Residentalne kuce Restore natural slope Vratiti prirodni nagib. Restwert: Prodajna vrijednost: Retire. date: Datum umirovljenja: return ticket ponovo unazad Revenue Prihodi road cesta road vehicle cestovni znak Roadsign Cestovni Znak Rotate map Okretanje mape Rotation Okretanje Routing Rutiranje sack vrece sail vjetar Saving map ... Spremanje mape... Scenario complete: %i%% Scenarij zavrsen:%i%% Schedule changing! Raspored u promijeni! Schienentunnel Gradnja zeljeznickog tunela Schiff_tab Brodovi Schiffdepot Brodogradiliste Schleppkahn_tab Sleperi Screenshot Uzeti snimak ekrana. Seasons Godisnja doba Sehenswuerdigkeit Turisticka atrakcija Sell the selected vehicle(s) Prodati odabrano vozilo/la. sended Poslana posta SEP_FRACTION , SEP_THOUSAND , SEP_THOUSAND_EXPONENT 3 September Rujan Server did not respond! Server nije odgovorio! Serves Line: Posluzuje liniju: Service Servis set signal spacing Postavljanje razmaka signala Setting Postavke Ship Brod shops and stores Trgovine i biroi Show all Prikaz svih show all building Prikaz svih zgrada Show also vehicles no longer in production. Prikaz takodjer vozila koja se vise ne proizvode. Show also vehicles that do not match for current action. Prikaz i vozila koja se ne mogu koristiti odabranom akcijom. show grid Prikaz mreze Show industry Industrija Show legend Prikaz legende Show map scale Skala mape Show obsolete Prikaz zastarjelih Show schedules Prikaz rasporeda show station coverage Prikaz pokrivenosti stanice show station names Prikaz imena stanice show waiting bars prikaz motke cekanja Show/hide statistics Prikaz/Skrivanje Statistike Shows consumer/suppliers for factories Prikaz potrosaca/dobavljaca za tvornice Shows the currently selected schedule Prikaz trenutno prikazanog rasporeda Shrink city Smanjenje grada shuffle midis mijesanje mid-ova Sim: Simloop-ovi: Size (%d MB): Velicina (%d MB): sliced underground mode Prikaz Kriske Sloja slot empty isjecak prazan Sort by sortiranje prema Sort waiting list by Sortiranje liste cekanja prema Sound Zvuk Sound settings Postavke zvuka Sound volume: Jacina zvuka: special freight Specijalni teret Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Brzinski bonus\n cesta %i km/h, tracnice %i km/h\nbrodovi %i km/h, zrakoplovi %i km/h. Speedlimit Brzina Speichern Spremi Spieler Igrac Spieler(mz) Igraci Spielerliste Lista Igraca Spielstand wurde\ngeladen!\n \nIgra uspjesno ucitana.\n Spielstand wurde\ngespeichert!\n \n Igra uspjesno spremljena.\n Sprache Jezik Sprachen Jezici Stadtinformation Gradske statistike Start the selected vehicle(s) Poslati odabrano/na vozilo/la Starte Spiel Pokreni igru Station tiles: Plocice stanice: Status Status stajalista steam para Step timeline one year Pomaknuti za godinu dana unaprijed. Storage capacity Kapacitet spremanja Strassendepot Garaza Strassentunnel Gradnja cestovnog tunela street car ulicni auto sued Jug suedost Jugoistok suedwest Jugozapad Summer snowline Ljetna snjezna linija Suppliers Dobavljaci Tage alt dana staro. There are still vehicles\nstored in this depot!\n Jos ima vozila\nu ovom depou!\n This Month Ovaj mjesec This Year Ova Godina: Tile not empty. Obrisati plocicu prije\nkoristenja Alata za Nagibe. timeline slijediti epohu tl_title Lista svih Gradova To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Za privlacenje vise turista,\n%s je sagradio\n %s\s pomocu od\n%i placenih poreza. To heavy traffic\nresults in traffic jam.\n Preveliki promet\nprouzrocuje zastoj.\n Toggle day/night view Promjena dan/noc pogleda Toggle vehicle tooltips Promijena savjeta za vozila tonnen t Total inhabitants: Ukupno stanovnika: Tourist attractions Turisticke atrakcije: Tourists Znamenitosti Town: %s\n Grad od %s.\n Towns Gradovi track tracnica Tracks Tracnice Traffic Promet Train Vlak Trains are not available yet! Vlakovi jos nisu dostupni. Tram Tramvaj tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. tramvaj %i km/h, monorail %i km/h\nmaglev %i km/h, uski kolosijek %i km/h. tram_track tramvajske tracnice Tramdepot Tramvajski depo Trams are not available yet! Tramvaji jos nisu dostupni. Transformer only next to factory! Transformatori moraju biti na\npraznom ravnom tlu\nblizu tvornici! Translation Prijevod transparent station coverage transparentan prikaz pokrivanja Transported Putovanja TrolleyBus_tab trolejbusi Truck Kamion Tunnel muss an\neinfachem\nHang beginnen!\n Tuneli moraju\npocinjati na ravnom\nnagibu!\n Tunnel must start on single way! Tuneli moraju pocinjati na pojedinacnom putu. Tunnelboden Tunel underground mode podzemni pogled UNDO failed! Ponistenje vise nije moguce.\nMozete ponistiti konstrukciju\nrute samo toliko dugo dok\nnijedan signal/stanica/stajaliste ili \nnista drugo nije izgradjeno \nna putu Undo last ways construction Ponistiti zadnju konstrukciju puteva Unemployed Nezaposlen Unhappy Nesretno units/day /mjesec, Update Line Osvjeziti liniju upgrade HQ Nadogradnja HQ Use timeline start year Koristenje trake godina: Vehicle %s can't find a route! Vozilo %s\nne moze naci rutu! Vehicle %s is stucked! Vozilo %s je zaglavljeno! Vehicle details Detalji Vozila Verbrauch Potrosnja Vergroessere die Karte\n Povecanje mape.\n verkaufen Prodaje Verkehrsteilnehmer gradski auti Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Imas %d mjeseca\nza otplatu svojih dugova.\n via preko (detalj) via %s\n preko %s\n via Menge preko (kolicina) voranstellen Postaviti naprijed Waggon_tab vagoni waiting cekanje Waiting for clearance! Cekanje na odobrenje! Water Kanal Water level Nivo vode: water vehicle vodeno vozilo way %s cannot longer used:\n Putni tip %s se ne moze vise koristiti.\n way %s cannot longer used:\n%s\n tip puta %s se ne moze vise koristiti:\n way %s now available:\n Putni tip %s je sada dostupan.\n Ways not connected Putovi nisu spojeni Wegpunkt Putna tocka Wert Vrijednost west Zapad Winter snowline Zimska snjezna linija withdraw povuci Withdraw All Povuci Sve WRONGSAVE \nNekompatibilna spremljena igra.\nNemoguce ucitati datoteku.\n Year %i has started. Godina %i je zapocela. Years Godina Zielort Cilj zooming in uzumiravanje zooming out izumiravanje Zu nah am Kartenrand Ne mozes graditi\ntako blizu na\nrubu mape. #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Novi svjetski record za maglevove: %.1f km/h by %s. New world record for monorails: %.1f km/h by %s. Novi svjetski rekord za\nmonorailove: %.1f km/h - %s. New world record for motorcars: %.1f km/h by %s. Novi svjetski rekord za\nmotorna vozila:\n %.1f km/h - %s. New world record for narrowgauges: %.1f km/h by %s. Novi svjetski rekord za uski kolosijek: %.1f km/h - %s. New world record for planes: %.1f km/h by %s. Novi svjetski rekord za\nzrakoplove:\n %.1f km/h\n - %s. New world record for railways: %.1f km/h by %s. Novi svjetski rekord za \nvlakove:\n %.1f km/h - %s. New world record for ship: %.1f km/h by %s. Novi svjetski rekod za \nbrodove:\n %.1f km/h\n - %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ %D_CITY_SYLL Vrata %E_CITY_SYLL Zid %F_CITY_SYLL Hrast 1center %s %s 1extern %s ekspoziturna %s 1suburb %s %s %s 2extern %s vanjska %s 3center %s glavna %s 3extern %s agrarna %s 3suburb %s selo %s %s 4extern %s vanjska %s 5center %S u centru grada %s 6center %s cvorisna %s 7center %s grad %s Asuburb Ne postoji! Bcenter Ne postoji! Bextern Ne postoji! Bsuburb Ne postoji! Ccenter Ne postoji! Cextern Ne postoji! Csuburb Ne postoji! Dcenter Ne postoji! Dextern Ne postoji! Dsuburb Ne postoji! Ecenter Ne postoji! Eextern Ne postoji! Esuburb Ne postoji! Fcenter Ne postoji! Fextern Ne postoji! Fsuburb Ne postoji! Gcenter Ne postoji! Gextern Ne postoji! simutrans-124.5/simutrans/text/hr/000077500000000000000000000000001520675460500172065ustar00rootroot00000000000000simutrans-124.5/simutrans/text/hr/baum_build.txt000066400000000000000000000017351520675460500220600ustar00rootroot00000000000000Sadnja drveca

Sadnja drveca

Dijalog je podijeljen u cetiri odijeljka.
- gore lijevo je lista odabira dostupnih vrsta drveca
- dolje lijevo je prikazana slika odabranog drveta
- gore desno su postavke
- dolje desno je prikazana informacija o odabranom drvetu

Lista odabira

U odabranoj listu su navedi svi tipovi drveca koji postoje s postavkama zadanim gore desno. Postoje dvije kartice koje ce dozvoliti promjenu identifikacije drveca.
- Prijevod: Prikazuje ime drveta u zadanom jeziku. Ako ne postoji prijevod u zadanom jeziku, ime objekta ce se korisiti.
- Objekt: Prikazuje Simutrans interno ime objekta za tip drveta.

Postavke

- igniranje klima: Ova postavka onemogucuje ogranicenja klima koje su pridjeljenje raznim tipovima drveta.
- slucajna godina: Starost drveta koje ce se posaditi ce biti slucajno odabrana.

simutrans-124.5/simutrans/text/hr/citybuilding_build.txt000066400000000000000000000040051520675460500236130ustar00rootroot00000000000000Gradnja gradskih zgrada

Gradnja gradskih zgrada

Dijalog je podijeljen u cetiri odjeljka.
- gore lijevo je lista odabira dostupnih gradskih zgrada
- dolje lijevo je prikazana slika odbrane gradske zgrade
- gore desno su neke postavke
- dolje desno je dana informacija o odabranoj gradskoj zgradi

Lista odabira

U listi odabira se nalaze sve urbane zgrade koje su dostupne postavkama danim gore desno (Stambene kuće, Trgovine i ured, Industrijske zgrade). Postoje dva jahaca koji dozvoljavaju promjenu nacina na koji se urbane gradjevine identificiraju.
- Prijevod: Prikazuje ime zgrade u zadanom jeziku. Ako ne postoji prijevod u zadanom jeziku, koristiti ce se ime objekta.
- Objekt: Prikazuje Simutrans unutarnje ime za tip zgrade.

Boja teksta ime slijedece znacenje:
- plavi tekst naznacava zgradu za stanovanje.
- zeleni tekst nazncčava zgradu trgovine ili ureda.
- vrni tekst naznacava industrijsku zgradu.

Opcije

- ignoriranje klima: Ova postavka onemogucuje ogranicenje klime koje se odnosi na razne tipove zgrada.
- Koristiti vremensku traku: Lista odabira ce sadrzavati samo zgrade koje su dostupne u trenutnoj Simutrans godini.
- Takodjer prikaz zastarjelih: Prositiri listu odabira kako bi takodjer sadrzavala zgrade koje su zastarjele u trenutnoj Simutrans godini.
- Stambene kuce: Biti ce stambene kuce u listi odabira.
- Trgovine i uredi: Biti ce trgovine i uredi u listi odabira.
- Industrijske zgrade: Biti ce industrijske zgrade u listi odabira.
- Rotacija: Ako se odabrano zgrada moze prikazati iz drugacijih kutova, tada je moguce ovdje odabrati odredjeni pogled. Donja lijeva slika ce se promijeniti kako bi to odrazila. Ako je ova postavka na slucajno, koristi ce se slucajni pogled zgrade.

simutrans-124.5/simutrans/text/hr/citylist_filter.txt000066400000000000000000000035111520675460500231600ustar00rootroot00000000000000Pomoc Liste Gradova

Lista Gradova

Lista Gradova prikazuje detalje o urbanim podrucjima (selima, gradovima i velegradovima) i stanovnicima.

Za otvaranje: klik nba city-list-tool u Upravljanje Listom ili stisnuti [T].
Lista Gradova ima dva gumba postavki koji odredjuju redoslijed po kojemu se pokazuju urbana poručja (i njihovi stanovnici).
{Savjeti: Ako su stavke u listi djelomicno vidljive, tada promijeniti velicinu Lista Gradova ili pomicati se koristeci klizni pokazivac.}

Ukupno stanovnika: je potpun broj stanovnika u igri (potpuni stanovnici svih urbanih podrucja). Nedavni porast stanovnika se prikazuje u zagradama.

Sortiranje po: ima postavke za odredjivanje redoslijeda urbanih podrucja prikazanih u listi.
Kiliknuti na gumb postavki za kruzenje kroz postavke (mijenja ime gumba postavki):

- Ime prikazuje alfanumericki koristeci ASCC-code redoslijed (velika slova prije malih slova) dodijeljenih imena.
- Citizens slaze prema velicini stanovnika.
- Town growth slaze prema rastu (koji je ovisan o velicini urbanih podrucja i broju prevezenih putnika).

- rastuce / padajuce mijenja redoslijed liste.

Kilik na stavku prikazanu uLista Gradovaza pregled vise informacija o urvbanim podrucjima.
Stavke prikazane za svako urbano podrucje sadrze:

ime dodijeljenih urbanom podrucju (moze se promijeniti u Informaciji Gradova)..

broj stanovnika urbanog podrucja, u zagradama je nedavni porast stanovnika.

simutrans-124.5/simutrans/text/hr/citywindow.txt000066400000000000000000000110521520675460500221460ustar00rootroot00000000000000Pomoc Statistike Grada

Statistika Grada

Statistika Grada prikazuje statistike o oznacenom urbanom podrucju i moze se koristiti za promjenu imena. Urbano podrucje je grad, gradic ili selo; ispod se pojam grad koristi za bilo koje urbano podrucje.

Svaki grad ima vlastitu gradsku vijecnicu oko koje raste. Prijevoz putnika i poste poboljsava rast, što ukljucuje ulice koje se grade ili koje se preuzimaju od igraca javnog servisa; nove gradsje zgrade i industrije koje se pojavljuju; i granice grada se prosiruju.

Klik na vijecnicu u pogledu igre sa Alatom Inspekcije ili na grad koji je prikazan uListi Gradova za otvaranje Statistike Grada. Ovdje se moze vidjeti i promijeniti ime grada, neke statistike, dvije mini-map, i graf gdje je vidljiv razvoj tijekom vremena za razne faktore koji se odnose na grad.

Ime: pridruzeno ime grada, gradica ili sela se prikazuje u kucici na vrhu Statistike Grada.
Imena takvih urvanih podrucja se koriste za stvaranje imena za stajalista unutar granica grada. Za promjenu imena samo kliknuti na kucicu i upisati novo ime.
{Savjet: Koristiti [!] za promjenu prikaza imena grada iznad Vijecnice u pogledu igre. Pogledati readme_citylist.txt (u ...simutrans/text/) za vise informacija o tome kako se stvaraju imena grada}.

Pod kucicom imena su prikazane neke osnovne gradsje statistike:
Velicina Grada: je trenutan broj gradjana. Nedavni porast populacije je prikazan u zagradama.
Zgrade: boj gradskih zgrada (rezidencijalne/komercijalne/industrijske) u gradu.
Koordinate mape: prikazuje granicu grada (mijenjati ce se kako grad raste). Prvi par vrijednostiprikazuje gornji lijevi kut, a drugi donji desni kut. Kako gradovi rastu, granice gradova se mogu preklapati.

Trenutni broj nezaposlenih i beskucnih gradjana grada. Ovo ce rasti kako se grad siri u granicama, i smanjivati se kada niknu nove zgrade. Ravnoteza ovih brojaca odredjuje koji tip gradskih gradjevina (rezidencijalne/komercijalne/industrijske) ce se konstruirati.

Gumb Dozvoli rast grada omogucava promjenu rasta grada na ukljuceno i iskljuceno.

Mini-mape svijeta igre u Statistike Grada prikazuje odredista putnika i poste koji su stvoreni unutar grada kao obojene tocke.
Desna mini-mapa prikazuje vrijednosti trenutnog kalendarskjog mjeseca igre, a lijeva za zadnji kalendarski mjesec. Boja svake tocke oznacava moze li se odrediste dostici ili ne:
- zuta tocka: postoji ruta i nenatrpano stajaliste za pocetak putovanja (sretno lice prikazano u Informacijama Stajalista).
- narancasta tocka: nema rute na to odrediste.
- crvena tocka: postoji ruta na odrediste, ali stajaliste na pocetku putovanja je pretrpano (nesretno lice u Informacijama Stajalista).

Ispod mini-mapa postoji graf koji prikazuje statistike za grad.
Na y-osi se prikazuje kolicina a na x-osi vrijeme. Kilik na jahace indeksa grafa za promjenu skale x-osi:
Godine: prikazuje godisnje vrijednosti za zadnjih 12 kalendarskih godina igre.
Mjeseci: prikazuje mjesecne vrijednosti za zadnjih 12 mjeseci igre

Klikom na gumbe opcije grafa, aktivirate i deaktivirate funkciju za svaki zadani gumb. Kada je neki gumb odabran prikazan je utisnuto.
Funkcije odgovaraju boji odgovarajucih gumbi postavki:
Gradjani: Broj ljudi koji zive u gradu
Rast grada: Promjene u populaciji (ovise o velicini grada i prijevoznoj usluzi koja je pruzena za putnike i postu).
Br. zgrada: Koliko gradskih zgrada ima u gradu.
Gradskih automobila: Koliko gradskih automobila je stvoreno
Putovanja: Koliko putnika je uslo u prijevoznu mrezu (prikazano kao sretno lice u Informaciji Stajalista)
Putnici: Ukupni broj korisnika stvorenih u gradu.
Poslana posta: Posta koja je usla u mrezu
Posta: Potpuni broj poste stvorene od grada.
Dostava:Broj trenutaka kada je bilo robe u skladistima u gradskim tvornicama
Roba:Broj trenutaka kada je skladistenje gradskih tvrtki biko provjeravano.

{Savjet: Postavke koje se odnose na gradove mogu se promijeniti u simuconf.tab i cityrules.tab}.

simutrans-124.5/simutrans/text/hr/climates.txt000066400000000000000000000052641520675460500215570ustar00rootroot00000000000000Pomoc Postavljanja Klime

Postavljanje Klime

Prozor Postavljanje Klime omogućuje promjenu postavki za topografske i meteorološke aspekte svijeta igre. Otvara se skupa sa Stvaranje Nove Igre i Jezici prozori.

Za namjestanje svake postavke, klik tipke strelice sa strane svake postavke.

Opcije Mape

Razina Vode - Postavlja koliko je visoka voda na mapi. Više vrijednosti daju manje zemlje i više tijela vode.

Visina Planina - Postavlja najvecu visinu terena.

Grubost Mape - Postavlja grubost terena. Veći brojevi stvaraju više talasanja u obalnim područjima. Za stvaranjwe realističnijih linija obale, koristiti veću vrijednost.

Postavke Linije Snijega
Linija snjiega je visina na mapa na kojoj se pojavljuje snijeg.

ljetna linija snijega - Linija snijega tijekom ljeta. Ovo je najveća vrijednost terena odabrana u opcijama klime.

Zimska Linija Snijega - Postavlja visinu snježne linije u zimi. Kako bi se snijeg pojavljivao u većim područjima na mapi, držati ovu vrijednost niskom.

Postavke Klime
Postavlja razne postavke klime za novu mapu.
Vrijednost pokraj svakog tipa klime postavlja visinu u mapi terena gdje se pojavljuje klima. Za isključivanje određene klime, postaviti njegovu vrijednost na 0 (zero).

Klima Pustinje - Vrlo malo oborina, ekstremi u temperaturama. Ravan krajolik.

Tropska Klima - Tipicno za ne-bezvodna područja oko ekvatora, stalno visoka temperatura na razini mora i mala uzviđenja.

Mediterabska klima - Umjereno promjenjivo vlažno vrijeme

Umjerena Klima - Može se naći u područjima između tropske i polarnih kapa, promijenjivo vrijeme.

Tundra - Niske temperature, rubovi polarnih kapa

Alpska Klima - Suha klima, hladne zime.

Druge Postavke

Nema Drva - Klik za omogućenje. Omogučiti ovu postavku za sprečavanje Simutrans-ovog iscrtavanja drva. Drva opterećuju CPU vrijeme za iscrtavanje. Koristiti ovu postavku na PCima sa sporijim CPU ili malo RAM-a.

Broj Rijeka - Postavlja najveci broj rijeka koji se mogu stvoriti na mapi terena. Rijeke se ne mogu ploviti camcima ili brodovima bilo kojeg tipa.

Najmanja duzina rijeke - Postavlja najmanju duzinu rijeke na mapi u u pogledu kvadratica.

Najveca duzina rijeke - Postavlja najvecu duzinu rijeke na mapi u pogledu kvadratica.

simutrans-124.5/simutrans/text/hr/color.txt000066400000000000000000000014141520675460500210650ustar00rootroot00000000000000Pomoc boje igraca

Boja igraca

Boja igraca postavlja boju kompananije.
Vozila, zgrade i druge stvari prikazuju boju kompanije.

Boja igraca se otvara iz Opcije Igre.

Kilinuti na obojane kvadratice za promjenu boje kompanije.
Naslov od Boja Igraca se mijenja (ne na novu boju kompanije) kako bi se prikazala nova odabrana boja.

Uobicajena boja ljudskog igraca je lagano plava.
Neki predmeti nisu kodirani bojom i ne moraju prikazivati odabranu boju.

{Savjeti: Za promjenu AI boje igraca koristiti Lista Igraca ili P+ Promjena Igraca za odabir AI igraca}.

simutrans-124.5/simutrans/text/hr/display.txt000066400000000000000000000052301520675460500214140ustar00rootroot00000000000000Pomoc Postavki Prikaza

Postavke Prikaza

Postavke Prikazaima kontrole za izgled igre; & nudi informacije o performansama racunala.

Klik na Prikaz-gumb u Postavkama Igre za otvaranje Postavke Prikaza.

Klik na kvadrate-gumbe za odabir postavki (gumb je oznacen kada je postavka odabrana), ili koristiti tipke strelica za prilagodbu postavki:

promjena dana i noci: pogled vremena noci u igri.

Svjetlina:prikazuje koliko svjetlo/tamno se igra pojavljuje; manji brojevi za zatamnjenje; premale ili prebelike vrijednosti stvaraju probleme; koristiti [+] za povecanje vrijednosti preko 0.

Inverzni pomak: mijenja smjer pomicanja za pogled igre u ?Prozoru Igre.

Brzina Pomicanja: postavlja brzinu pomicanja za pogled igre u Prozoru Igre.

Pjesaci u gradovima: pogled pjesaka koji se pojavljuju u urbanim podrucjima.

Pjesaci na stajalistima: Pogled pjesaka koji nastaju kada vozilo dolazi na Stajaliste.

Gustoca prometa: postavlja broj privatnih auta u urbanim podrucjima.
Broj novih privatnih automobila koji nastaju kako urbana podrucja rastu ovisi o velicini urbanih podrucja i gustoci prometa. Vece vrijednosti stvaraju vise privatnog prometa, 0 ne stvara nista.

{Savjeti: Vise postavki i podrazumijevane postavke za Simutrans pokretanje se mogu promijeniti u simuconf.tab}.


Informacija Prikaza:

Ispod ovih postavki, informacija se prikazuje o racunalnim performansama pri izvrsavanju Simutrans-a.
Ako su brojevi (obicno bijeli) crveni ili ?zuti mozda je potrebno promijeniti vase postavke.
Promjene u stopi kako vrijeme prolazi u igri, T, (koristeci Fast Forward >> ikonu iznad pogleda igre, ili [,]/ [.]) moze promijeniti broj boje.

Vrijeme Okvira: ?Prvi broj je zamisljeno vrijeme izmedju okvira; drugi broj je stvarno vrijeme izmedju okvira.

Besposlenost: Ako je iznad 0, racunalo ima kapaciteta za izvrsavanje drugog software-a.

FPS: Vece vrijednosti znace kako se vozila pojavljuju gladje. Ako broj ostaje crven, racunalo je presporo za trenutne postavke(?pokusajte smanjiti velicinu prozora pogleda igre).

Simloopovi: Ako broj ostane crven, racunalo je presporo za treutne postavke - pokusajte sa manjom kartom sa manje urbanih podrucja.

simutrans-124.5/simutrans/text/hr/finances.txt000066400000000000000000000064441520675460500215450ustar00rootroot00000000000000Financijska Pomoc

Financije

Finances prikazuje pregled financijskog stanja tvrtke igraca.

Klik na ikon jezika gore unutar pogleda igre, ili stisnuti [f], ili kliknuti na ime igraca u Igraci, za otvaranje Financije.

Vrijednosti (u Hajo Kreditima) su prikazane za trenutnu i prethodne kalendarske godine.
Ova godina: trenutna kalendarska godina igre.
Prethodna godina: prethodna kalendarska godina igre.

Godišnje vrijednosti se također mogu vidjeti u grafikonu za zadnjih deset godina, ili mjesecne vrijednosti za zadnju godinu.
Klik na vrijednost ime-gumb za promjenu detalja na grafu: y-os je kolicina, x-os je vrijeme.
Klik na indeksnu karticu na grafu index-kartici za promjenu skale x-osi:
Godine: prikaz godisnjih vrijednosti za zadnjih deset kalendarskih godina.
Mjeseci: prikaz mjesecnih vrijednosti za zadnjih 12 mjeseci igre

Vrijednosti (klik na ime-gumb za prikaz detalja na grafu), sadrze:

Proceeds - ukupni prihod prijevoza putnika, robe, poste i od viskonaponskih prijenosa.

Operativni Troskovi - ukupni troskovi koji su se pojavili od svih Convoja kada su bili u prijevozu.
{Savjeti: Operativni troskovi po kilometru za vozila se mogu vidjeti u depou i iz Informacija Konvoja.}

Odrzavanje - ukupni troskovi koji su se pojavili odrzavanje prijevozne mreze.
{Savjeti: Troskovi po mjesecu za odrzavanje mreze (Odrzavanje (mjesecno) prikazano na desnom Financije) se odbija na kraju kalendarskog mjeseca igre.}

Operacijski Prihod - Prihod prijevoza robe, putnika i poste i visokonaposki prijenos (Postupa manje Operacijske Troskove i Odrzavanje).

Nova Vozila - Rashodi i prihodi primljeni od kupnje ili prodaje vozila.

Gradiliste - ukupni troskovi koji su se pojavili izgradnjom mreza prijevoza, oblikovanjem zemljista, visokonaponskim vodovima, i koristenjem Unisti/Ukloni i drugih alata uklanjanja puteva (tracnice, cesta, kanali i poletno/sletnig staza).

Tok Gotovine - ukupni prihod minus ukupni rashod (troskovi).

Stanje Racuna - trenutno dostupna gotovina za izgradnju ili kupnju vozila (takodjer prikazanano u sredini donje trake ispod pogleda igre.
{Savjeri: Ako igrac covjek ode u crveno (negativno stanje na racunu) u slijedna tri mjeseca, igrac je bankrotirao i igra je gotova}.

Imovina - vrijednost svih vozila tvrtke na kraju poslijednjeg kalendarskog mjeseca.
{Savjeti: svaki mjesec se vrijednost operativnih vozila lagano smanjuje, trenutni prodajna vrijednost operativnih vozila se moze vidjeti u Detalji Konvoja ili kada je vozilo u depou.}

Neto bogatstvo - Stanje na racunu plus imovina.

Margina - je omjer operativnog profita naspram prihoda.

Puto?vali - ukupni broj robe, putnika i prevezene poste.

{Savjeti: Neki troskovi, pocetno stanje i financije u igri se mogu promijeniti u simuconf.tab}

simutrans-124.5/simutrans/text/hr/general.txt000066400000000000000000000063621520675460500213730ustar00rootroot00000000000000Simutrans Pomoc

Opcenita Pomoc

- Dobrodosli
- Sucelje Igre
- Pomoc Za Misa (za napisati)
- Podzemlje
- Pomoc Za Tipkovnicu
Opcije Igre
- Jezik
- Boja Igraca
- Postavke Prikaza
- Zvuk & Glazba
- Igraci
- Ucitavanje Igre
- Spremanje Igre
- Nova Igra
- - Kontrola Klime
- - Ucitavanje Visinske Mape

Glavni Meni
- Postavke Igre
- Mapa
- Alat Istrazivanja
- Alati Za Oblikovanje
- Zeljeznicki/Vlakovni Alati
- Monorail Alati
- Tramvajski Alati
- Cestovni Alati
- Lucni Alati
- Aerodromski Alati
- Alati Posebne Izgradnje
- Alati Uredjivanja Igre
- Unistiti/Ukloniti

?Meni Upravljanja
- Upravljanje Linijama
- Liste
- - Lista Stajalista/Stanica
- - - Filter Liste Stajalista/Stanica
- - Lista Vozila
- - - Filter Liste Vozila
- - Lista Gradova
- - Lista Robe
- - Lista Industrije
- - Lista Atrakcija
- Centar Poruka
- Financije

Druge Postavke
- Spremiti Sliku Ekrana
- Pauza
- Brzo Unaprijed
- Pomoc

Drugi Dijalozi
-Kontrole Depoa
-Kontrole Rasporeda
- Informacija Stajalista
- - Detali Stajalista
- Informacija Konvoja/Vozia
- - Detalji Konvoja/Vozila
- ?Informacija Grada
- Informacija Industrije

Ako pomoc nije nadjena ovdje, pokusati
http://guide.simutrans.com, ili
http://wiki.simutrans.com, ili
http://simutrans-tips.com, ili
http://forum.simutrans.com.

simutrans-124.5/simutrans/text/hr/language.txt000066400000000000000000000017061520675460500215360ustar00rootroot00000000000000Pomoc o jezicima

Jezici

Prozor Jezici mijenja jezik koji se koristi u igri.

Otvara se iz Opcije Igre prozora i sa Stvoriti Novu Igru prozorima.

Uobicajeni jezik za Simutransa je Engleski. Za igranje igre u drugom jeziku, odabrati gumb pokraj jezika u kojemu zelite igrati.

Jezik se moze promjeniti bilo kada tijekom igre. Za promjenu jezika, odabrati Opcije Igre -> Jezik.

Promjena jezika se dogadja odmah nakon stvaranja nove igre i nakon zatvaranja Jezici prozora ako se mijenja usred igre.

Ako prijevod nije dostupan u odabranom jeziku, moze se pojaviti na engleskom.

Za pomoc oko prijevoda Simutransa u Vas jezik, posjetite https://translator.simutrans.com

Da biste pomogli u prevođenju Simutrana na vaš jezik,
posjetite https://translator.simutrans.com

simutrans-124.5/simutrans/text/hr/linemanagement.txt000066400000000000000000000171171520675460500227420ustar00rootroot00000000000000Upravljanja linijama

Upravljanje linijom

Upravljanje Linijomnudi informacije i kontrole za upravljanje linijama (rute i najmanja kolicina robe & putnika zahtjevani za Konvoj to proceed) za konvoj ili visestruke konvoje na istoj ruti.

Linije se sastoje od Stajalista gdje vozila utovaruju i istovaruju robu i putnike (vozila vodenog prijevoza mogu koristiti bilo koji kvadrat u podrucju pokrivenisti doka) i putne tocke (koristene za usmjeravanje vozila ako postoji vise od jedne opcije ili nude privremene tocke cilja).

Klik na mreznu-ikonu s gornje strane igre ili pritisak [w] za otvaranje Upravljanje Linijama, sto moze mijenjati velicinu (klik na strelicu dolje u alatnoj traci za povrataka na originalnu velicinu).

Lijeva strana od Upravljanja Linijama prikazuje postojece Linije (za pomicanje u listi: koristiti klizac s desne strane liste), koje se mogu filtrirati prema tipu prijevoza, klikom na indeks-jahac iznad liste za odabir:
Sve: lista Linija za sva vozila.
Vlak: lista Linija za sva tracnicka vozila.
Monorail: lista Linija za sva Monorail/maglev vozila.
Tramvaj: lista Linija za sve tramvaje.
Kamion: lista Linija za sva cestovna vozila.
Brod: lista Linija za sva vodena prijevozna vozila.
Zrak: lista Linija za sve zrakoplove.

Boja imena linije koja se pojavljuje u listi za prikaz slijedeceg:
bijela - nema vozila pridruzenih liniji
zuta - ?nisu u funkciji, ne stvaraju dobit ili gubitak
crna - ?stvaraju profit, ?visak kapaciteta, ?poboljsanje moguce
plava - ?stvaraju profit, malo dodatnog kapaciteta
crvena - linija stvara gubitak

Pod listom postojecih linija su gumbi opcija za upravljanje Linijama.
Klik na liniju za odabir (istice ime) i pregled informacija o Stajalistima, dobiti, i konvojima na odabranoj Liniji.
Ako su detalji o Stajalistima ili Konvojima samo djelomicnop vidljivi potrebno je promijeniti velicinu Upravljanje Linijama ili koristiti klizac za pomicanje informacija.

Opcijski gumbi, slijeva, sadrze (klik za odabir):

Nova Linija:opcija otvara kontrole za definiranje Rasporeda (ruta i najmanja kolicina robe ili putnika zahtjevanih od Convoja to proceed) za novu Liniju .
Dodati koliko je potrebno Stajalista ili putnih tocaka, postavka nivoa Cekanja na koliko je potrebno, zatim zatvoriti Kontrole Rasporeda za stvaranje Nove Linije (klik x u gornjem lijevom kutu kotrola ili koristiti tipkovnicu). Novoj Liniji ce se pridjeliti broj i prikazati se u Upravkljanju Linijama.
{Savjeti: Odabrati tip prijevoza koristeci index-jahac prije stvaranja nove linije. Vozila ne utrpavaju ili istrpavaju robu i putnike na putnim tockama.}

Osvjezavanje Linije: otvara kontrole za promjenu i upravljanje postojecim Rasporedom (ruta i najmanja kolicina robe i putnika zahtjavana od Konvoja za nastavak) Linije. Promjene ce se odraziti na vozila na Liniji, kada se zatvore Kontrole Rasporeda (klikk x u gornjem lijevom kutu).
Klik na ime Linije u listi za odabir (oznacava ime) tada klik na gumb opcije za otvaranje Kontrola Rasporeda.

Brisanje Linije: brisanje odabrane linije (ruta i najmanja kolicina robe i putnika zahtjevane od Konvoja za nastavak).
Klik na ime Linije u listi za odabir (oznacava ime), tada klik na gumb opcije za uklanjanje. Daljnja potvrda nece biti potrebna.

Kada je Linija odabrana, Stajalistza su prikazana gore lijevo u Upravljanje Linijom. Klik na Stajaliste u listi za otvaranje Informacija o Stajalistu.
Stavke prikazane za svako stajaliste sadrze:
status-traku-boje: Boje prikazuju operaciju od Stajalista s obzirom na popunjenost. Status-traka-boje je također koristi u Listi Stajalista i Informacija o Stakjalistu je iste boje prikazane u traci-boje iznad Stajalista u prikazu igre:
- zuto: nije u sluzbi.
- zeleno: nisu potrebna poboljsanja.
- narancasto: moguca poboljsanja.
- crveno: preporucena poboljsanja.
{Savjeti: Koristiti [!] za promjenu stupca-boje iznad Stajalista u pogledu igre.}
ime koje je pridruzeno Stajalistu.
broj Stajalista (Stajalista se numeriraju ako je ukljucena postavka u simuconf.tab).
ikona/e vozila prikaz koji tipovi vozila mogu kosristiti stajaliste (takodjer koristeno u Informaciji Stajalista i Listi Stanica). Ikone sadrze: autobus (za cestovna putnicka vozila), kamion (za cestovna vozila prijevoza robe), vlak, brod ili zrakoplov. Tramvaji su prikazani autobus-ikonom ili vlak-ikonom sto ovisi o tipu stanice.
ikona/e tereta prikaz koji predmeti (putnici, roba i posta) koje Stajaliste moze obraditi (takodjer koristeno u Informaciji Stajalista i Listi Stanica).
{Savjeti: dodavanje ispravnih dodataka moze promijeniti kategorije predmeta koje stajaliste moze obraditi. Postanski ured moze biti dodan Stajalistima za obradu poste}.
cekanje detalja o raznim robama i putnicima.

Sdesna od Upravljanja Linijama informacije na grafu, ime-kvadratic i Konvoj lista su prikazani za odabranu Liniju.

Klik na ime Linije u listi za odabir (oznacava ime). Klik na opcija-dugme za promjenu informacije, o odabranoj Liniji i vozilima pridruzenim istoj, na grafu (x-os je vrijeme u mjesecima):

- Slobodan Kapacitet - kolicina viska prostora od vozila za robu i putnike.

- Putova?li - kolicina robe i putnika koji koriste Liniju.

- Prihod - kolicina dobiti stvorena od vozila.

- Operativni Troskovi - nastali troskovi od vozila u tranzitu.

- Profit - stvoreni profit minus troškovi koji su nastali (Prihod minus Operativni Troskovi).

- Konvoji - broj Konvoja pridruzenih Liniji.

Za preimenovanje Linije: Klik na imne Linije u listi za odabir (oznacava ime), tada klik na ime-kvacicu (pod opcijama grafa, ispod ispisanih Konvoja) i upis novoga imena.

Kada je Linija odabrana (klik na ime Linije u listi za oznacavanje), Konvoji pridruzeni Liniji su prikazani ispod grafa.
Na gornjem dijelu liste vozila: ukupni broj Konvoja pridruzenih Liniji; prihod (prinos minus operacijski troskovi), kapacitet, i trenutna kolicina robe i putnika prevezenih (% punog kapaciteta u zagradama) svih Konvoja od Linije je prikazano.
Klik na prikazani predmet za prikaz informacija o tome Konvoju (koristiti klizac za pomicanje liste).
Predmeti prikazani za svaki Konvoj sadrze:
ime koje je pridruzeno (podrazumijevano je to proizvodjac prve jedinice vozila kupjene ili sastavljene za Konvoj).
dohodak prikazuje dobit (stvoreni prihod minus opecarijski troskovi).
Linija: prikazuje ime Linije prikazane za Konvoj i prikazuje je li u depou.
grafike prikazuje kompoziciju i trenutni nivo predmeta na ploci.

simutrans-124.5/simutrans/text/hr/list.txt000066400000000000000000000034741520675460500207320ustar00rootroot00000000000000Pomoc Listi

Liste

Liste ima postavke za otvaranje kontola za informacije o trenutnom statusu igre (i takodjer kontrole za namjestanje relativnog dohotka koji je dobiven prijevozom raznih roba, putnika & poste).

Informacija je dostupna za igraca u listama o: Stajalistima; Convojima; urbanim podrucjima; dohotcima dobivenim iz raznih roba, putnika i poste; industrije; i turistickih znamenitosti.

Klik na ikonu liste s gornje strane pogleda igre za otvaranje Lista.
Pomicanje kurzora misa, nakon otvaranja ili klikom na alatnu traku, preko postavke alata liste za prikaz imena.

Klik na postavku alata za otvaranje kontrola za informacije, slijeva na desno, o:

Lista Stanica: Stajalista, gdje vozila utovaruju ili izbacuju robu & putnike (ali ne Stajalista samo za vozila prijevoza vodom).

Lista Vozila: cestovna i tracnicka vozila, zrakoploci i brodovi.

Lista svih gradova: urbana podrucja (sela, gradici i gradovi).

Lista svih roba: dohotci dobijevi od raznih roba, putnika i poste (ukljucuje kontrole za namjestanje relativnog dohotka).

Lista Tvrtki: industrija (potrosaci i dobavljaci robe).

Lista Znamenitosti: touristicke znamenitosti.

simutrans-124.5/simutrans/text/hr/load.txt000066400000000000000000000027641520675460500206770ustar00rootroot00000000000000Ucitavanje Pomoci

Ucitavanje

Ucitavanje pomnovno pokrece spremljenu igru (za nastavak igranja) & moze se koristiti za brisanje tih igara. Igra koja je ponovno startana mijenja trenutni svijet koji je izgubljen.

Ucitavanje otvara iz Stvaranje Novoga Svijeta & Postavke Igre ili pritiskom [L]; & lista imena, datuma & vremena (of spremanja) dostuopnih igara.
Ako nisu vidljive sve igre: koristiti kliznik sa desne strane liste za pomicanje imena.

Klik na ime liste za ponovno pokretanje igre; ili unos imena u kucicu s gornje strane Ucitavanja: klik na kvacicu, tip imena, & stisnuti [Enter] ili [Return] ili klik na OK.
Neispravno ime ce izazvati poruku o gresci: zatvoriti poruku, ponovno pokusati.

UPOZORENJE: Klik na x-button s imenom za BRISANJE igre odmah & za stalno.
Ime se uklanja iz liste i Ucitavanje se zatvara. Koristiti funkciju oprezno.

BITNO: Nove distribucije Simutrans-a se javljaju redovito & igre iz prethodnih distribucija Simutrans-a mozda ne rade sa kasnijim inacicama; molimo procitajte biljeske distribucije za kasnije inacice kako bi se provjerila kompatibilnost starijih igara.

Klik na Odustajanje za zatvaranje Ucitavanja; ili x u gornjem lijevom kutu; ili koristiti tipkovnicu.

simutrans-124.5/simutrans/text/hr/options.txt000066400000000000000000000024451520675460500214470ustar00rootroot00000000000000Pomoc Postavki Igre

Postavke Igre

Postavke Igre otvara kontrole za operaciju & prezentacijzu igre; izlaz iz Simutrans-a iz Postavke Igre.

Kliknuti na ikonu diskete na gornjem dijelu pogleda igre za otvaranje Postavke Igre.

Kilik na gumb za otvaranje novih kontrola:

Jezik: postavljanje jezika koristenom u igri.

Boja Igraca: postavljanje boje Igraceve kompanije.

Prikaz: postavke kako se igra pjavljuje u Simutrans-u.

Zvuk: postavke za zvuk & glazbu u igri.

Igraci: otvara kontrole za razlicite kompanije igraca.

Ucitavanje: otvara kontrole za restart snimljene igre.

Spremanje: otvara kontrole za spremanje trenutne igte, kako ni se nastavila kasnije.

Nova ?Igra/Mapa: otvara kontrole za pocetak nove igre (zatvara ostale trake izbornika, tekstove & postavke u trenutnoj igri).

Izlaz:napusta Simutrans, trenutna igra se ne sprema.

simutrans-124.5/simutrans/text/hr/players.txt000066400000000000000000000036241520675460500214330ustar00rootroot00000000000000Pomoc Liste Igraca

Lista Igraca

Lista Igraca daje informacije i ima kontrole za razne tvrtke igraca.

Klik na Igraci-gumb u Postavkama Igre ili pritisak [k] za otvaranje Liste Igraca koja prikazuje imena igraca & Stanja Racuna.

Postoji osam igraca: covjek, javni servis i sest AI igraca. Kontrole omogucuju igranje igre kao drugaciji igrac ako je odabrano Dozvoliti promjenu igraca na pocetku igre.

Klik na gumb strelice za rad kao drugacija tvrtka igraca u trenutnoj igri. Poruka potvrde se pojavljuje kako je promijenjen igrac: klik na x u gornjem lijevom kutu poruke ili koristiti tipkovnicu za zatvaranje.

Klik na ime igraca za predgled njegovih financija.

Klik na cetvrtasti-gumb pokraj AI imena igraca za promjenu ukljucen/iskljucen igrac (gumb je podebljan kakda je igrac odabran).

AI igraci ne bankrotiraju i koriste vozila sa najvecom efikasnoacu.
Iskljucivanje AI igraca tijekom igre nece ukloniti prijevoznu mrezu.
Neki AI igraci mogu koristiti samo cestovna ili zeljeznicka vozila, neki mogu prevoziti samo robu ili putnike.

{Savjeti: Vozila ne mogu koristiti tracnice drugog igraca ili Stajalista (svejedno vozila svih igraca mogu koristiti predmete sagradjene od igraca javnog servisa).
Igraci ne mogu graditi predmete ili postavljati putne tocke na tracnice drugih igraca ili ceste, ili uklanjati predmete sagradjene od drugih igraca (svejedni neki predmeti gradjeni od javnog servisa mogu se unistiti koristeci Unisti/Ukloni).
Onemoguciti postavku promjene igraca i pristup posebnih alata kao igrac javnog servisa.}

simutrans-124.5/simutrans/text/hr/simutrans.txt000066400000000000000000000044451520675460500220030ustar00rootroot00000000000000 Uvod

Dobrodosli u Simutrans ...

Odaberite jednu, od vise tisuca Simutrans karata ili stvorite novu pomocu mape visina. Zatim, stavite sebe u cipele od mladog poduzetnika s nesto novca od vasih djeda i bake: nestrpljiv da bi ih ucinio ponosnima, s ambicijama osnivanja uspjesnog transportnog poduzeca, vas izazov je sada izgradnja uspjesnog pothvata.

Kako vrijeme prolazi, razvijajajte svoju tvrtku u prijevozno carstvo, vodjenjem doboroustaljene ekonomije isporukom tereta i zadovoljavanjem zahtjeva putovanja milijuna putnika svaki mjesec.
Prevozite putnike, postu i robu zeljeznicom, cestom, brodom i cak jos i zrakom.

Svaka mapa ima nesto drugo za ponuditi. Pogledajte dobro na svoju okolinu ... njezina buducnost lezi u rukama vas i vasih konkurenata.

U pocetku, moglo bi biti lakse jednostavno dobavljati elektranu sa potrebnim sirovinama. Tada slijedi povezivanje drugih tvornica, mozda cak i upotrebe centralnih cvorista gdje se roba stalno prekracava.
Nedostatak prijevoza samo robe je kako gradovi nece rasti, a nove industrije se nece pojaviti.

Umjesto toga, vi odmah mozete krenuti na prijevoz putnika (i poste). To je veci izazov, jer stanovnici ovog svijeta zele putovati u odredjena mjesta.
Neki trebaju ici u tvornice na rad, neki zele ici u kupovinu u veliki trgovacki centar u glavnom gradu, a drugi zele otici u razgledavanje.

Ako ne nudite veze na njihovo odrediste, oni uopce nece putovati sa vama. Trebati ce izgraditi prometnu mrezu gdje je moguce otici na sto je vise moguce odredista - barem na sve one popularne. Kada se vase mreze uspostave, morati cete ih nadograditi kako bi ih zadrzali na istom nivou potražnje. Dakle planirajte unaprijed.
Ukoliko se putnicima svidja vasa usluga, gradovi će rasti i nove industrije će biti uspostavljene i samo tada će gospodarstvo pokrenuti glatko.

Mozete igrati koliko god zelite. Pocetak u 1880 i kraj u 2050. Postojati ce nova vozila i zgrade tijekom vremena.
Igrajte protiv AI ili sami. Samo ako je vase neto bogatstvo negativano tri mjeseca za redom, bankotirati cete i igra je gotova. Provjerite svoje neto bogatstvo u prozoru financija.

Simutrans-Team i zajednica Vam zele odlican provod ovdje u Simutrans-u!

simutrans-124.5/simutrans/text/hr/sound.txt000066400000000000000000000014421520675460500211000ustar00rootroot00000000000000Pomoc Postavki Zvuka

Postavke Zvuka

Postavke Zvuka ima kontrole za zvucne efekte i glazbu.

Postavke Zvuka se otvara iz Opcije Igre.

Koristiti tipke strelica za prilagodbu postavki ili pomicnih traka za smanjivanje/povecavanje glasnoce:

Glasnoca zvuka: postavlja glasnocu zvucnih efekata u igri.

Glasnoca glazbe: postavlja glasnocu glazbe. Ako glazba ne postoji tada kontrole nece raditi.

Trenutno svira: changes music; tipke strelica kruze kroz dostupnu glazbu.

Molimo primjetiti: glazba se ne isporucuje sa Simutrans-om.
Pomognite javno dodati glazbu na http://wiki.simutrans.com

simutrans-124.5/simutrans/text/hr/station_details.txt000066400000000000000000000016101520675460500231330ustar00rootroot00000000000000Detaljnih Informacija Stajalista

Pomoc Detaljnih Informacija Stajalista

Spojene Tvornice: Sve tvornice u podrucju pokrivanja stajalista se prikazuju alfabetski. Podrucja pokrivanja se mogu prikazati koristeci tipku v.

Roba potrebna od okolnih industrija: Svi tipovi robe, koje su potrebne od stajalista spojenih tvornica, se prikazuju alfabetski.

Linije koje opsluzuju ovo stajaliste: Sve linije, koje opsluzuju stajaliste, prikazuju se alfabetski. Klik na strelicu otvara Upravljanje Linijom.

Direktne rute: Sva stajalista se prikazuju, koja se direktno opsluzuju preko linija sa stajalista. Imenu stajalista slijedi u slijedecem retku nakon crtice tip prijevoza. Preko strelice ispred imena stajalista moguce je direktno skociti na to stajaliste.

simutrans-124.5/simutrans/text/hr/underground.txt000066400000000000000000000043641520675460500223120ustar00rootroot00000000000000gradnja u podzemlju

Postoje 2 moguca pogleda podzemlja.
potpuni pogled podzemlja
Sa U se ulazi u potpuni pogled podzemlja. U ovome pogledu se iskljucuje prikaz svih objekata na povrsini.
Podzemni pogled kao nivo
Sa Ctrl + U se ukjlucuje podzemni pogled sa nivoima. U ovom pogledu se iskljucuje prikaz svih objekata, koji se nalaze iznad odabranog nivoa. To su svi objekti povrsine i takodjer svi objekti u podzemlju, koji se nalaze iznad odabrane razine.

Promjena nivoa se dogadaja sa + (vise) i - (nize) ili preko Postavki prikaza.

Pokazivac misa (Cursor)
puni pogled podzemlja
Kod punog pogleda podzemlja se prikazuje resetka povrsine. Na nesagradjenim poljima se pokazivac namjesta prema toj resetci. Na sagradjenim poljima se pokazivac namjesta na izgradjena polja.
Jer je namjestanje pokazivaca misa preko resetke povrsine poprilicno mukotrpno, trebalo bi se pri gradnji u podzemlju koristiti pogled nivoa.

Pogled podzemlja kao nivo
Kod pogleda nivoa se resetka prikazuje na aktivnom nivou. Pokazivac nisa se namjesta na tu resetku. Jer se pokazivac misa nalazi uvijek na aktivnom nivou, gradnja je puno laksa.

Gradnja rute

opcenito


Opcenito se rute u podzemlju grade alatima za tunele. Kako bi se u pozemlju moglo graditi, mora se sagraditi tunel ili ulaz u tunel (tipka Ctrl + klik sa alatom za tunele na polju nagiba).

Promjena nivoa
Kako bi se promijenio nivo u podzemlju, koriste se alati iz Smanjiti/povecati nivo iz menija Alati okolisa. Za promjenu nivoa, potrebno je jednostavno sa odgovarajucim alatom kliknuti na kraj rute.

Posebni slucaj tramvaj


Tracnice za tramvaj se takodjer u podzemlju grade sa normalnim alatima za tracnice na cestu.

Stanice i pratece zgrade
Samo vlastite Stanice se mogu graditi jedna preko druge.

Pratece zgradee se ne mogu izgraditi u podzemlju.

Signali
Mogu se koristiti kao na povrsini.

simutrans-124.5/simutrans/text/hr/window.txt000066400000000000000000000114161520675460500212610ustar00rootroot00000000000000Pomoc Koristenja Sucelja Igre

Sucelje Igre

Simutrans se igra preko Sucelja Igre koje daje informacije o trenutnoj igri; pristup trakama izbornika & kotrolama igre; i pogled trenutnoga svijeta igre.

naslov: lijevo na naslovu Sucelja Igre se prikazuje inacica i datum release-a Simutransa koji je trenutno pokrenut.
Desna strana naslova takodjer sadrzi kontrole za smanjivanje, povecavanje Sucelja Igre i takoder izlaz iz Simutrans-a.

Ikone: red ikona, koje se koriste za pristup trakama izbornika igre i kontrolama igre kada se igra Simutrans, nalazi se pravo ispod naslova. Ikone (klik za koristenje) su grupirane u tri seta:

- Glavni Meni ikone sadrze:
- disketa: otvara Postavke Igre
- mapa: otvara Mapu
- lupa odabire Alat Inspekcije
- ikona nagnuca tla: otvara traku alata oblikovanja tla
- vlak: otvara ?traku alata zeljeznica vlak
- monorail/maglev: monorail transport traka alata
- tramvaj: otvara tram transport traku alata
- kamion: otvara cestovni transport traka alata
- brod: otvara vodeni transport traka alata
- zrakoplov: otvara zracni transport traka alata
- crvena kapljica u zuto krugu (ikona krana u pak128): otvara traka alata posebnih konstrukcija
- crveni kriz (ikona buldozera u pak128): odabire Alat Unisti/Ukoloni

- Meni Vodjenja ikone sadrze:
- ikona mreze: otvara Vodnjenje Linije
- ikona lista: otvara Liste
- ikona postanskog sanducica: otvara Centar Poruka
- ikona novca: otvara Financije

- Druge Postavke ikone sadrze:
- ikon kamere: hvata trenutnu sliku od Sucelja Igre (bez naslova) i sprema kao sliku u direktorij/mapu ...simutrans/screeshot/
{Savjeti: Slika takodjer moze biti uhvacena sa pritiskom tipke [c].}
- ikona salice za kavu: pauza /odpauziranje igre
{Savjeti: Pritisak tipke [p] se takodjer moze koristiti za pauzu/odpauziranje igre}.
- >> ikona: odbire/odznacava Brzo Naprijed. (mijenja stopu po kojoj Vrijeme, T, prolazi u igri).
{Savjeti: Brzo Naprijed se takodjer more odabrati/odznaciti sa pritiskom tipke [W]
VAZNO: Igra mozda nece ici glatko;>Informacija Prikazaprikazuje moze li se racunalo nositi sa promjenom.}
- ?: otvara Simutrans Pomoc

prikaz igre dio trenutnog pogleda igre se prikazuje ispod Ikona.
Mozete zumirati i odzumirati pogled igre i takodjer se pomicati okolo u drugim dijelovima svijeta igre koristeci misa ili tipkovnicu.

privremeni scroll-bar pomicanja pojavljuje se i prikazuje poruke na dnu pogleda igre.
{Savjeti: Klik na poruku u centru poruka za centriranje pogleda igre na povezanoj poziciji.}

Donja traka koja prikazuje informacije o trenutnom svijetu igre se prikazuej ispod pogleda igre.
Pruzena informacija, slijeva premna desno u donjoj traci sadrzi:
datum & vrijeme: trenutan datum i vrijeme u svijetu igre, slijedi grafiku i ime godisnjeg doba.
stanje na racunu: trenutna dostupna gotovina za konstrukciju i drugi troskovi (nabavljanje vozila, odrzavanje infrastrukture prijevoza i troskovi koristenja vozila u pogonu).
koordinate: pokazuje poziciju kursora u pogledu igre. Tri lika predstavljaju X koordinate, Y koordinate i visinu.
T: prikazuje stopu prolaska vremena u igri.
{Savjeti: Koristiti [.] / [,] za ubrzanje/usporavanje vremena, T ; Prikaz Informacijapokazuje moze li se racunalo nositi sa promjenom.
Koristenje Brzo Naprijed mijenja T sa ">>" na donjoj traci.}
vremenska traka: prikazuje je li vremenska traka odabrana u trenutnoj igri.

simutrans-124.5/simutrans/text/hu.tab000066400000000000000000001264361520675460500177150ustar00rootroot00000000000000§Magyar PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: hu Magyar # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ AMBIENT_SOUND Háttérzajok CASH_SOUND Pénzcsörgés cl_btn_filter_disable tiltva cl_btn_filter_enable engedélyezve cl_btn_filter_settings Beállítások cl_btn_sort_asc növekvő cl_btn_sort_desc csökkenő cl_btn_sort_id azonosító szám szerint cl_btn_sort_income Bevétel cl_btn_sort_name Név cl_btn_sort_type Típus clf_btn_alle mind clf_btn_invers fordít clf_btn_keine semmi CROSSING_SOUND Vasúti átjárók FACTORY_SOUND Gyárak zaja gl_btn_sort_bonus Bónusz gl_btn_sort_name Név gl_btn_sort_revenue Bevétel gl_btn_unsort Deszortíroz hl_btn_filter_disable Tiltva hl_btn_filter_enable Engedve hl_btn_filter_settings Beállítások hl_btn_sort_asc Növekvő hl_btn_sort_desc Csökkenő hl_btn_sort_name Név hl_btn_sort_type Típus hl_btn_sort_waiting Várakozók hlf_btn_alle mind hlf_btn_invers fordít hlf_btn_keine semmi Install Telepít Lake Tó Lake height tavak max. magassága Networks Hálózat Num pad keys always move map A numerikus billentyűkkel mindig mozdul a térkép Open Sea Nyílt Tenger Queueing Várakozók rainfall páratartalom Reselect closes tools Újbóli kiválasztás bezárja az eszközt Road toll Útdíjak Scenario Forgatókönyv sea tengeri éghajlat Sound range: Hangterjedelem Start this as a server Kiszolgáló indítása ezzel a játékkal TOOL_SOUND Eszközök hangja TRAFFIC_SOUND Forgalom zaja Transfers Átszállások #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Sarkvidéki éghajlat desert Sivatagi éghajlat mediterran Mediterrán éghajlat rocky Sziklás/köves temperate Mérsékelt éghajlat tropic Trópusi éghajlat tundra Tundra éghajlat #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Forgatókönyv betöltése sikertelen Bridge blocked by way below or above. Valami útban van a tervezett híd alatt vagy felett. Can't buy obsolete vehicles! Elavult járművet nem vásárolhatsz! Cannot alter water Ezt a medencét nem lehet jobban leereszteni vagy feltölteni. Cannot build on a double slope! Ez csak enyhe emelkedőn vagy sík terepen építhető! Cannot built depot here! Ide nem építhetsz depót! Cannot built this station/building\nin underground mode here. Az épület metróépítő módban nem építhető. Cannot create generic line!\nSelect line type by\nusing filter tabs. Általános menetrendet nem lehet kialakítani! Egyszer válasz menetrend típust. Cannot create socket Nem hozható létre socket Cannot rotate this building! Ez az épület nem forgatható el! Client closed connection during transfer A kliens az átvitel közben lezárta a kapcsolatot Convoi handles exhausted! A szerelvények száma elérte a maximumot. Convoy already deleted! A szerelvényt már törölték! Could not open file Nem sikerült megnyitni a fájlt Das Feld gehoert\neinem anderen Spieler\n Ezt a területet más játékos birtokolja! Der Besitzer erlaubt das Entfernen nicht Az eltávolítást a tulajdonos nem engedélyezte! Diese Zusammenstellung kann nicht fahren!\n Rossz kombináció! Flugzeughalt muss auf\nRunway liegen!\n Légikikötő csakis kifutópályára építhető! Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Orsinak továbbítva!!! Hier kann kein\nSignal aufge-\nstellt werden!\n Vasúti forgalomjelző ide nem építhető! In order to lock the game, you have to protect the public player by password! Ahhoz, hogy lezárhasd a játékot, le kell védened a publikus játékost jelszóval! Loading a new game will end the current server session! Új játék betöltése befejezi a jelenlegi kiszolgáló-munkamenetet! Lost connection\nto server! A szerverrel való kapcsolat megszűnt! Lost synchronisation\nwith server. Szerverrel való szinkronizáció nem működik! Maglevhalt muss auf\nMaglevschiene liegen!\n Nyeregvasúti megálló\ncsakis nyeregvasútra\népíthető!\n Monorailhalt muss auf\nMonorail liegen!\n Megálló csakis nyeregvasútra építhető! Monorails are not available yet! Még nem áll rendelkezésre nyeregvasúti szerelvény! More than one possibility to build this dock found. Ez a kikötő egynél többféleképpen is megépíthető. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n A keskeny nyomtávú sínen keskeny nyomtávú megállónak kell lennie No bridges over runways! Kifutópálya fölé nem lehet hidat építeni! No suitable way on the ground! Megállót csak útra lehet építeni! No through station here! Ide nem lehet építeni megállót! Not enough bytes transferred Túl kevés adat érkezett Not enough clearance. Két út között legalább két egység magasságkülönbség szükséges. Not enough money! Nincs elég pénz az építéshez! On narrowgauge track only!\n Csakis kisvasútpályán lehet! Only public player can lock games! Csak publikus játékos zárhatja le a játékot! Only up and down movement in the underground! Földalatti mező csak a talajemelő és talajsüllyesztő eszközzel formálható! Out of funds Nincs elég pénzed erre! Post muss neben\nHaltestelle\nliegen!\n A posta/vezérlő/tároló egy megállóhoz/állomáshoz kell közel legyen! Protocoll error (expecting game) Protokoll hiba Schiffhalt muss im\nWasser liegen!\n Hajómegálló csak vízre a dokkok mellé tehető! Server busy Szerver elfoglalt Server version too new A kiszolgáló a program újabb verzióját használja. Terraforming not possible\nhere in underground view Terraformálás nem lehetséges metróépítő módban. This tunnel branches. You can try Control+Click to remove. Ennek az alagútnak kettőnél több bejárata van. Próbáld Ctrl+klikkel eltávolítani az alagutat. Upgrade must have\na higher level Fejleszteni csak nagyobbra lehet! Vehicle %s cannot choose because stop too short! %s járműnek túl rövid a következő megálló! Zughalt muss auf\nSchiene liegen!\n Vasúti megálló csak vasútra tehető! #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Súgó

Tartalom

A Simutransról

%1$s

Hogyan használd

%2$s

Hogyan kezdj új játékot

%4$s

Hogyan játszd

%5$s

Eszközök

%3$s

További ablakok

%6$s Color-coded terrain according to altitude. A területek magasságtól függő színezése Decrease water height Vízszint csökkentése Highlight railroad tracks Vasúti sínek jelölése a térképen Highlite depots Depók jelölése a térképen Highlite electrical transmission lines Elektromos hálózat jelölése Highlite factories Gyárak jelölése Highlite forests Fák jelölése a térképen Highlite tourist attraction Turistalátványosságok jelölése Increase water height Vízszint növelése Open station/stop details Állomás/megálló részleteinek megnyitása Overlay city limits Városhatárok mutatása a térképen Overlay passenger destinations when a town window is open Utasok célját jelöli, ha meg van nyitva a város infó ablak Overlay schedules/network Megjeleníti a vonalhálózatot Overlay town names Városok nevét mutatja Please click on the map to add\nwaypoints or stops to this\nschedule. Jelöld ki az érinteni kívánt\nmegállókat és pontokat\na térképre kattintással. Set tile climate %s A terület %s éghajlatúra alakítása Show capacity and if halt is overcrowded Megállók kapacitását és terheltségét mutatja Show how many convoi reach a station Megmutatja, mennyi jármű érinti a megállókat Show how many people/much is waiting at halts Megmutatja, mennyi utas/áru vár a megállóban Show initial passenger departure Utasok kiindulási helyét mutatja Show level of city buildings Épületek utasforgalmát jeleníti meg Show mail service coverage/mail network Postai lefedettség és postaszállító hálózat megjelenítése Show passenger coverage/passenger network Utas lefedettség és utasszállító hálózat megjelenítése Show speedlimit of ways Sebességhatárok megjelenítése Show the change of waiting at halts Várakozók számának változása a múlt hónaphoz képest. Show the owenership of infrastructure Infrastruktúra tulajdonosainak mutatása Show transported freight/freight network Áruszállítás és áruszállító hálózat megjelenítése Show usage of network A havi forgalom nagyságát mutatja minden mezőn Shows a listing with all industries on the map. Az összes gyártípust listázza Sum of departure/arrivals at halts Érkezők/távozók száma összesen #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s újabb buszjáratot indított %s és %s (%i,%i) között. %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s újabb buszjáratott indított %s és %s (%i,%i) között. %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). A(z) %s a(z) %i számú kamiont %s (%i,%i) és %s (%i,%i) között üzemelteti. %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s új vasutat avatott \n%s (%i,%i) és \n%s (%i,%i) között Airline service by\n%s\nnow between\n%s \nand %s.\n %s által\nüzemeltetett légikapcsolatt\njött létre\n%s és %s\nközött Ferry service by\n%s\nnow between\n%s \nand %s.\n A %s kompjárat\n%s és %s között közlekedik.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Az utasok mostmár\n%s\nbuszait használják\n%s és\n%s között. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Szerkesztő eszközök LISTTOOLS Listák MAGLEVTOOLS Mágnesvasúti kellékek MONORAILTOOLS Nyeregvasúti kellékek NARROWGAUGETOOLS Keskeny nyomközű vasút kellékei RAILTOOLS Vasúti kellékek ROADTOOLS Közúti kellékek SHIPTOOLS Hajózás SLOPETOOLS Földformázás SPECIALTOOLS Speciális eszközök TRAMTOOLS Villamos kellékei #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s-n\n felépítettek a (%i,%i) cégközpontot. Factory chain extended\nfor %s near\n%s built with\n%i factories. A gazdaság megerősődött:\nA %s mely %s van közel, fejlődik.\n%i új gyár lett alapítva. Net wealth less than 10%% of starting capital! A tőke a kezdőtőke 10%%-a alatt van! New %s now available:\n%s\n Új %s érhető el:\n%s.\n New factory chain\nfor %s near\n%s built with\n%i factories. Új termelési lánc épült %s, közel %s városához, %i kiszolgáló gyárral (termelési egységgel). New vehicle now available:\n%s\n Új jármű áll rendelkezésre:\n\n\n -- %s -- \n\n Now %u clients connected. %u kliens csatlakozott Remove vehicle from map. Use with care! A jármű eltávolítása a térképről. (Gondold meg!) Screenshot\ngespeichert.\n Képernyő kimentve. Sends the convoi to the last depot it departed from! Visszaküldi a szerelvényt az utolsó garázsba, ahonnan indult. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. Egy nagy fesztivál kíséretében %s új emlékművet épített. %i lakós igencsak elégedett az eredménnyel. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Vonal szűrő #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) depóban [CTRL] Ctrl [END] End [HOME] Home \nBauzeit bis \nEltűnik: \nBauzeit von \nMegjelenik: \nCan't open heightfield file.\n Domborzatfájlt nem lehet megnyitni. \ndirection: \nirány \nelektrified \nvillamosított\n \nHeightfield has wrong image type.\n A domborzatfájl inkompatibilis képeket tartalmaz. \nis reserved by: \nfoglalja: \nminimum speed: \nminimum sebesség: \nnot elektrified \nnincs villamosítva\n \nRibi (masked) \nberendezések (lefedett): \nRibi (unmasked) \nberendezések\n(összes): \nSet phases: \nFázisváltás ideje mp-ben\nÉszak-Dél Kelet-Nyugat: \nsingle way \negyirányú út \nwith sign/signal\n \njellel/táblával \n %d buildings\n %d épület\n %d convois %d szerelvény %d Einzelfahrzeuge im Depot %d van %i km/h (max. %ikm/h) %i km/ó (max. %i km/ó) %i years %i months old. Kora: %i év és %i hónap %s building %s %s %s épület %s %s %s city %d %s %s város %d %s %s factory %s %s %s%s épület %s has entered a depot. %s depóba/dokkba érkezett. %s land %d %s %s föld %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s többlet költséget jelent %i számú lakosnál. %s\nis crowded. %s\n túlterhelt. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\nseb. %i\nmax_seb. %i\ndx:%i dy:%i %s\nwas liquidated. %s\nfelszámolásra került. %u Client(s)\n %u klient\n %u Player (%u locked)\n %u játékos (%u lezárva)\n <új vonal készítése> 1 convoi 1 szerelvény 1 Einzelfahrzeug im Depot 1 jármű van 1LIGHT_CHOOSE Fényesség: 1WORLD_CHOOSE Új térkép beállításai: 2LIGHT_CHOOSE Színek száma: 2WORLD_CHOOSE Térképszám: 3LIGHT_CHOOSE Görgetés sebessége: 4LIGHT_CHOOSE Fordított görgetés 5LIGHT_CHOOSE Gyalogost megállóban mutat 5WORLD_CHOOSE Városok száma: 6LIGHT_CHOOSE Gyalogost városban mutat 6WORLD_CHOOSE Forgalom sűrűsége: 8WORLD_CHOOSE Éjszaka/nappal mód A bridge must start on a way! Híd csakis úton kezdődhet! Abfrage Infó eszköz Abnehmer Fogyasztók: About Infó About scenario Copyright Abriss Töröl/Eltávolít Absenken Talaj süllyesztése Abspanntransformator Transzformátorállomás Accelerate time Idő felgyorsítása Act. load: %u MW\n Terhelés: %u MW\n Active player only Csak az aktív játékos Add forest Erdő hozzáadása Add random citycar Véletlenszerű kocsi elhelyezése Add Stop Hozzáadás Add stops for backward travel Megállók hozzáadása a visszaúthoz Aircraft Légiközlekedés aircraft_tab Teherszállítók airplane repülőgép Airport Repülőtér AIRTOOLS Légi kellékek All Mind Allow city growth Város növekedés engedélyezve Allow player change Játékosváltás engedélyezése allowed climates:\n megengedett klímatípusok:\n Alters a schedule. Megváltoztatja a menetrendet. Angenommene Waren Közeli ipartelepek szükségletei anhaengen Vontatmány Anhaenger_tab Vontatmányok Anheben Talaj emelése Appends stops at the end of the schedule Megálló hozzáadása a menetrend végéhez Apply Line Útvonal alkalmaz April Április Arbeiter aus: Munkások: Arrivals from\n Érkező járatok\n Arrived Érkezett Assets Tárgyi eszközök Aufloesen Felbont August Augusztus Autohalt muss auf\nStrasse liegen!\n Megálló csakis útra építhető! Available Rendelkezésre állás Bahndepot Vasúti kocsiszin Bankrott:\n\nDu bist bankrott.\n Csődbe jutottál. battery akkumulátor Baum Fa baum builder Faültetés Baustelle Építési terület Bauzeit Építési idő Beenden Kilépés Beginner mode Kezdő színtű játékmód Besonderes Gebaeude Turistalátványosság BF p.u. bio biológiai Blockstrecke ist\nbelegt\n A vonalat már más vonat használja Boden Föld Bonusspeed: %i km/h Legnagyobb elérhető sebesség: %i km/h Boost (%%) Növekedés (%%) bridge is too high for its type! A híd túl magas a típusához képest! Bridge is too long for this type!\n Ilyen típusú hídat ilyen hosszúra nem lehet építeni! Bruecke Híd Bruecke muss an\neinfachem\nHang beginnen!\n A híd csakis egyenes emelkedőről indulhat! Brueckenboden hídalap Build air depot Légi depó építése build choosesignals Jelzőberendezés építése Build city market Fogyasztó/ipari egység építése Build drain Trafóállomás építése build HQ Székhely építése Build land consumer Hőerőmű építése Build maglev depot Mágnesvasúti depó építése Build monorail depot Nyeregvasút depó építése Build powerline Elektromos hálózat építése Build presignals Vasúti előjelző építése Build road depot Közúti depó építése Build ship depot Hajódepó építése Build signals Forgalomjelző építése Build train depot Vasúti depó építése Build tram depot Villamos depó építése Build truck depot Kocsiszín építése Building costs estimates Építkezés költségeinek becslése Buildings Épületek Built artifical slopes Mesterséges töltés építése Built random attraction Véletlenszerű turista attrakció építése Bus_tab Buszok Can only move from halt to halt or waypoint to waypoint. Eltávolítás csak megállótól megállóig vagy szakaszponttól szakaszpontig lehetséges. Cancel Mégsem Capacity: Kapacitás Capacity: %.0f MW Kapacitás: %.0f MW\n Capacity: %d%s %s\n Kapacitás: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Kapacitás: %s \nKihasználtság: %d (%d%%) Cars are not available yet! Még nem áll rendelkezésre gépkocsi! Cash Számlaegyenleg Change player Játékos váltása Chart Grafikon Choose operation executed on clicking stored/new vehicles A műveletet a járműre kattintással lép működésbe chooses a random map Véletlenszerű térkép választása citicens Lakók City attraction Városi látványosságok City industries Ipari egységek (városban) City list Városok listája City size Város mérete city_road Városi út citybuilding builder Városi épületek építése CityLimit Városhatár cl_title Járműlista cl_txt_sort Szortírozás: Clear block reservation Mutasd/rejtsd a lefoglalt utat clf_chk_aircrafts Repülőgépek clf_chk_cars Közúti járművek clf_chk_indepot Garázsban clf_chk_maglev Mágnesvasút clf_chk_monorail Nyeregvasút clf_chk_name_filter Szűrés névre: clf_chk_narrowgauge Keskeny nyomközű vasút clf_chk_noincome Veszteséges clf_chk_noline Nincs vonal clf_chk_noroute Eltévedt clf_chk_noschedule Nincs menetrend clf_chk_obsolete Elavult clf_chk_ships Hajók clf_chk_spezial_filter Speciális szűrő: clf_chk_stucked Elakadt clf_chk_trains Vonatok clf_chk_trams Villamosok clf_chk_type_filter Szűrés típusra: clf_chk_waren Szűrés árukra: clf_title Járműlista filter Climate Control Éghajlati beállítások COLOR_CHOOSE\n Válassz színt: Company bankrupt A cég csődbement! Company_msg Versenytársak Comparing pak files ... Pak fájlok összehasonlítása... Congratulation\nScenario was complete in\n%i months %i years. Gratulálunk!\n A forgatókönyv %i hónap\nés %i év alatt lett végigjátszva. Connected stops Csatlakozó megállók: Connections Kapcsolatok Constructed by Rajzolta: Constructed by %s Rajzolta: %s Construction_Btn Építkezések Consumed Felhasznált convoi %d of %d %d./%d szerelvény Convoi following mode Járműkövetés módja Convoi has been sent\nto the nearest depot\nof appropriate type.\n A szerelvényt\na legközelebbi alkalmas\ngarázsba küldtük. Convoi is sold when all wagons are empty. A járművet eladjuk, amint teljesen kiürült. convoi passed last\nmonth %i\n \nmúlt hónapban áthaladt\njárművek száma: %i\n Convois Szerelvények Convois: %d\nProfit: %s Járművek: %d Profit: %s Convoys Szerelvények Copy Convoi Szerelvény másolása Copy the selected convoi and its schedule or line Megkettőzi a kiválasztott szerelvényt és menetrendjét/vonalát Copy to clipboard Másolás a vágólapra cost for removal Törlés díja Cost: %8s (%.2f$/km)\n Ár: %8s (%.2f$/km)\n Costs Árak Create a new line based on this schedule Új vonal készítése a jelenlegi menetrend alapján curiosity builder Látványosság építése curlist_title Turistalátványosságok listája Currently playing: Most szól: Customers live in: Vásárlók itt élnek: Date format Dátumformátum Deccelerate time Idő lelassítása Del Stop Törlés Delete Line Törlés Delete the current stop A kiválasztott megálló törlése Delete the selected line (if without associated convois). A vonal törölhető (ha nem használja jármű) Delete this file. Fájl törlése. Delivered Szállítva Demand Igény Demand: %.0f MW Igény: %.0f MW\n Denkmal Látványosság Departed Távozott Departure board Menetrendi kijelző Departures to\n Induló járatok Depots Garázsok Der Tunnel ist nicht frei!\n Az alagút nem szabad!\n Destination Célállomás Details Részletek Die Bruecke ist nicht frei!\n A híd nem üres! diesel dízel Direkt erreichbare Haltestellen Közvetlenül elérhető célok disable midi MIDI zene kikapcsolása Display settings Képernyő beállítások Distance Távolság Dock Dokk Dock must be built on single slope! A dokk csak lejtős (ferde)\npart mentén építhető!\n dp_title Garázsok listája Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Még %d hónap maradt a tartozás viszafizetéséig! Durchsatz Max. Economy Gazdaság és városok Eigenbesitz\n Köztulajdon:\n Ein %s\npasst hier nicht.\n A '%s' nem fér ide! Einstellungen aendern Beállítások electric villamos Electricity Áram Electricity producer\n\n Villanytermelő\n\n Electrics_tab Elektromos járművek Electrify track Vágányok villamosítása enlarge map Térkép nagyítása enter a value between %i and %i adj egy értéket %i és %i között Enter Password Játékosnév és jelszó módosítása Error Hiba Erzeuge neue Karte.\n Kis türelmet míg az új \ntérkép elkészül. (Nagy \ntérképek esetén ez \npercekbe is telhet.) Es wird bereits\nein Fahrplan\neingegeben\n Egy menetrend meghatározása már folyamatban van. Fejezd be előbb azt! Fabrikanschluss Kapcsolódó ipar Fabrikname Gyárnév Factories Gyárak factory details Term./fogy. infó factorybuilder Gyárak építése Fahrplan Menetrend Fahrtziel Úticél: Fahrzeuge koennen so nicht entfernt werden A jármű nem távolítható el ilyen módon! Fahrzeuge: Járművek: Farbe Játékos színe Fast forward Időgyorsítás February Február Ferry_tab Kompok Fertig Kész Filename Fájlnév: Files from: Fájlok itt: Filter: Szűrés: Finances of %s %s pénzügyei Finanzen Pénzügyek find mismatch Pakok összevetése fl_title Termelőegységek listája Flug_tab Utasszállítók follow me Követés Follow the convoi on the map. Kövesd a szerelvényt a térképen font size Betűméret Forest Erdő Found new city Új város alapítása Fracht Rakomány Frame time: Képfrissítés: Free Capacity Szabad kapac. freeplay mode Szabad játékmód Friction: Pillanatnyi súrlódás: fuel_cell üzemanyagcella Full load Min. telítettség Fussgaenger gyalogos Game info Online játék GAME PAUSED Játék megállítva Game_msg Általános Gear: Áttétel: Gebaeude Épület General Általános Generated Keletkezett Generation: %.0f MW Termelés: %.0f MW\n Gewicht Súly Gewinn Bevétel: Give the selected vehicle(s) an individual schedule A járműhöz rendelt vonal vagy egyedi menetrend megadása gl_btn_sort_catg Kategória gl_title Áruk listája go home Garázsba Goods Áruk Goods list Áruk listája Gross Profit Végösszeg Grow city Város növelése Growth Növekedés GUI settings Kezelőfelület H megálló Hangar Hangár Happy Elégedett Haus kaufen Épület vásárlása Helligk. Képernyő Help Súgó Help text not found Súgó fájl nem található hide all building Összes épület elrejtése hide city building Városi épületek elrejtése hide station names Megállók nevének elrejtése hide transparent Átlátszó terep hide trees Fák elrejtése Hier warten/lagern: Várakozó áruk és utasok: Higher transport fees, crossconnect all factories Magasabb fuvardíjak, raktárkorlát kikapcsolása Highlite schedule Megállók kiemelése hl_title Állomások/megállók listája hl_txt_filter Filter: hl_txt_sort Rendezési elv: hlf_chk_airport Repülőtér hlf_chk_anleger Dokk hlf_chk_bahnhof Vasútállomás hlf_chk_bushalt Buszmegálló hlf_chk_frachthof Rakodóudvar hlf_chk_keine_verb Nincs kapcsolat hlf_chk_maglevstop Mágnesvasúti megálló hlf_chk_monorailstop Nyeregvasúti megálló hlf_chk_name_filter Szűrés névre: hlf_chk_narrowgaugestop Keskenyvasúti megálló hlf_chk_overflow Túlzsúfolt hlf_chk_spezial_filter Speciális szűrő: hlf_chk_tramstop Villamosmegálló hlf_chk_type_filter Szűrés típusra: hlf_chk_waren_abgabe Kimenő áruk: hlf_chk_waren_annahme Bemenő áruk: hlf_title Állomás/megálló filter Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depó nem található! A depót manuálisan kell kijelölni. Homeless Hajléktalan hydrogene hidrogén Idle: Üresjárat: ignore climates Klimaövezetek ignorálása In Transit Érkezik Increase Industry density Iparsűrűség növelése industrial building Ipari épület Industry overlay Gyárak állapota Init map ... Térkép előkészítése ... Input Bemenet Ins Stop Beszúrás Insert stop before the current stop Megálló beszúrása a kiválasztott elé Intercity road len: Városközi utak hossza: Intro. date: Megjelenés: Invalid coordinate Nem valós útvonal! isometric map izometrikus térkép January Január join game Online játék July Július Jump to Ugrás June Június Kann Spielstand\nnicht laden.\n Nem tudom megnyitni a fájlt! Kann Spielstand\nnicht speichern.\n Nem lehet felülírni a fájlt! Kein Besitzer\n Köztulajdon\n keine nincs Keine Einzelfahrzeuge im Depot Nincs jármű Keyboard_Help\n Billentyűzet segítség koord Koordináták Kreuzung keresztezés/kereszteződés labellist_title Jelzőtáblák listája Lade Relief Domborzat betöltése Laden Betöltés Land attraction Terep látványosságok Land industries Ipari egységek LANG_CHOOSE\n Válasz nyelvet: Last Month Múlt hónap: Last Year Múlt év: Leaving depot! Elhagyja a depót! leer üres Legend Jelmagyarázat Leistung Teljesítmény Leistung: %d kW Teljesítmény: %d kW Leitung vezeték letzen Monat: diesen Monat: múlt hónap: e hónap: Line Vonal Line Management Vonalak kezelése Lineless convoys serving this stop Vonal nélküli járművek Lines serving this stop Megállót kiszolgáló vonalak LKW_tab Teherautók Load game Játék betöltése load height data from file Terepadatok betöltése Load scenario Forg.könyv betölt loaded berakodva loaded passenger/freight Utas/áru rendezése eszerint: Loading (%i->%i%%)! Rakodás (%i->%i%%) Loading addon paks ... Pak fájl betöltése ... Loading map ... Térkép betöltése Loading paks ... Pakok betöltése... Loading skins ... Kinézetek betöltése... Lock game Játék zárolása Lokomotive_tab Mozdonyok Maglev Mágnesvasút maglev vehicle Mágnesvasúti jármű maglev_track Mágnesvasúti pálya Maglevdepot Mágnesvasúti depó Mailbox Postaláda Mailbox Options Üzenetek beállításai Maintenance Karbantartás Manufactured: Gyártási idő: Map roughness Terepdurvaság: map view Térkép map zoom méretarány March Március Margin (%%) Árrés (%%) Marker Jelzőtábla Max Boost (%%) Max növekedés(%%) Max income: Maximális bevétel: Max. speed: Max. sebesség: Maximum 254 stops\nin a schedule!\n Maximum 254 megálló engedélyezett! maximum length of rivers Folyók maximális hossza Maximum tile height difference reached. A két szomszédos \nterület \nmagasságkülönbsége \nnagyobb mint \na megengedett! Maxspeed Max. sebesség May Május Median Citizen per town Átlagos népesség városonként: Meldung Üzenet Menge Mennyiség MessageOptionsText \nÚj év\n\nJátékoshírek\n\nVárosi hírek\n\nNincs út\n\nÚj úticélok\n\nChat\n\nÚj járművek\n\nÁllomás tele\n\nProblémák\n\nDugók\n\nForg.könyv minimum length of rivers Folyók minimális hossza Missing pakfiles Elemek hiányoznak! Modify the selected line Kiválasztott útvonal módosítása Monate alt hónapos Monorail Nyeregvasút monorail vehicle Nyeregvasúti jármű monorail_track Nyeregvasúti pálya Monorailboden Nyeregvasút alap Monoraildepot Nyeregvasút depó month wait time Max. hó várakozás Months Hónap Monument Emlékmű Monuments Emlékművek Mountain height Hegymagasság: Music playing disabled/not available Zenelejátszás kikapcsolva Music volume: Zene hangerő: mute sound Hang némítása Name Név Narrowgauge Keskeny nyomközű vasút Narrowgauge are not available yet! A keskeny nyomtávú vasút nem érhető még el! narrowgauge_track Metro/ HÈV vágányok Narrowgaugedepot Depot HÉVnek/ Metronak Net ID: %p Azonosító: %p\n Net Wealth Tőke Net wealth near zero A tőke nullához közelít Neue Karte Új térkép Neue Welt Új térkép new convoi Új szerelvény New Line Új vonal New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Az új útvonal kész! Hozzárendelheted az útvonal szelektorral New Vehicles Új járművek Nickname: Becenév: no buildings hidden Épületek mutatása no convois Nincs szerelvény No goods are loaded onto this convoi. Nem rakodnak új árut a szerelvényre no goods waiting nincs várakozó áru/utas no load Csak kirakodás No Route Nincs útvonal No stop here! Csak megállókon használható! No suitable ground! Nem megfelelő talaj! No terminal station here! Ide nem lehet építeni megállót! no timeline Idővonal kikapcsolva no tree Fák elrejtése No. of Factories Gyárak és üzletek: Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Egyszer definiálj menetrendet! none semmi nord Észak nordost Északkelet nordwest Északnyugat Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Tilos! A menetrendet ilyenkor nem lehet megváltoztatni Próbáld később! Not enough fields would remain. A farm körül nem marad elég szabad terület! Now active as %s.\n Most "%s" játékosként folytatódik a játék. Number of rivers Folyók száma Object Objektum Odometer: %s km Megtett út: %s km Ok Rendben Oktober Október On loan since %i month(s) A kölcsön %i hónapja indult! On this map, you are not\nallowed to change player!\n Ezen a térképen a játékosváltás nem engedélyezett! Only city chains Csak városi láncolat! Only full Unicode fonts Csak teljes Unicode betűkészletek Only land chains Csak terep láncolat! Only one transformer per factory! Csak egy trafóállomást lehet építeni gyáranként! Operation Működési költségek Ops Profit Működési nyereség Optionen Beállítások Or enter a server manually: Vagy adj meg egyet kézzel: Origin Kiindulás ost Kelet Output Kimenet Ownership Tulajdonos paletten raklap Pas_tab Személyvonatok Passagiere Utas Passagierrate Utasforgalom Passagierziele Utas/levél célállomás Passengers %d %c, %d %c, %d no route Az utasoknak %d %c, %d %c, %d nincs útvonala Passengers %d %s, %d %s, %d no route Az utasoknak %d %s, %d %s, %d nincs útvonala Password Jelszó: Pause Megállítás/folytatás Pax <%i> Mail <%i> Utasok <%i> Levél <%i> PaxDest Célállomás Percent Electricity Százalék villamosság Planes are not available yet! Még nem áll rendelkezésre repülőgép! Plant tree Faültetés player Játékos player -1 Énmagam :-). player 0 Nullusz Kft. player 1 Amata Transz Kft. player 2 Sündörgő Speed Bt. player 3 Mávocska Rt. player 4 Pihe Transz Kft. player 5 Lubisz Speed Rt. player 6 Fakerék Transz Rt. Please choose vehicles first\n Egyszer válasz járművet! Post Küldemény Postrate Posta forgalom Power Teljesítmény Power (MW) Teljesítmény(MW) Power: Teljesítmény: Power: %4d kW\n Teljesítmény: %4d kW\n Powerlines Elektr. hálóz. Price Ár Problems_msg Problémák Produced Termelve Production of %s has been stopped:\n%s\n A %s gyártása leállt:\n%s.\n Production/Boost Termelés/Növekedés Produktion Termelés promote to line Új útvonal q1 Tavasz q2 Nyár q3 Ősz q4 Tél Query server Lekérdezés rail car Vasúti kocsi random Véletlenszerű Random age Véletlenszerű kor Random map Véletlen térkép Rathaus Városháza Rating Terheltség ratio_pax Utas %% Relevant Érdemleges Reliefkarte Térkép Remove Jelzőtábla törlése remove airstrips Kifutó-/gurulópálya elbontása remove channels Csatorna elbontása remove interm. signals Köztes jelzők törlése remove maglev tracks Mágnesvasút elbontása remove monorails Nyeregvasút elbontása remove narrowgauge tracks Keskeny nyomközű vágány elbontása remove powerlines Elektromos vezeték elbontása remove roads Út elbontása remove tracks Sín elbontása Remove wayobj %s Felsővezeték elbontása %sról replace other signals Többi jelző cseréje replace stop Megállócsere residential house Lakóház Restore natural slope Terepviszonyok visszaállítása Restwert: Eladási ár: Retire. date: Kifutás: return ticket Visszaút Revenue Árbevétel Revision: Revízió: road út road vehicle Közúti kocsi Roadsign közúti jelzőtábla Rotate map Térkép forgatása Rotation Forgatás sack zsák(ok) sail szél Saving map ... Térkép mentése... Scenario complete: %i%% Forgatóköny befejezve:%i%% Schedule changing! Menetrend megváltozik! Schienentunnel Vasúti alagút építése Schiff_tab Teherhajók Schiffdepot Hajó dokk Schleppkahn_tab Uszályok Screenshot Képernyőmentés Seasons Évszakok Sehenswuerdigkeit Látványosság Select a server to join: Válassz kiszolgálót, amihez csatlakoznál: Select a theme for display Megjelenési téma választása Select display font Betűtípus kiválasztása Sell the selected vehicle(s) A kiválasztott járművet/járműveket elad sended Küldemény SEP_FRACTION , September Szeptember Server did not respond! A kiszolgáló nem válaszolt! Serves Line: Kiszolg. vonal: Service Kiszolgálás set signal spacing Jelzőtávolság beállítása Setting Beállítások Ship Hajó shops and stores üzletek és irodák Show all Mindent mutat show all building Minden épületet mutat Show also vehicles no longer in production. A már nem gyártott járműveket is megjeleníti. Show also vehicles that do not match for current action. Mutassa azokat a járműveket is amelyek nem vesznek részt a műveletben. Show contour Domborzat mutatása Show finances for transport type Csak ezen járműtípus pénzügyei: show grid Tereprács mutatása Show industry Ipar mutatása Show legend Jelmagyarázat Show map scale Színkódok Show mismatched Eltérők mutatása Show networks Hálózatok mutatása Show obsolete Elavult járművek Show offline Offline lévők mutatása Show schedules Menetrend mutat Show servers that are offline Az épp nem kapcsolódó kiszolgálókat is mutatja Show servers where game version or pakset does not match your client Az olyan kiszolgálókat is mutatja, ahol a játék verziója vagy a pakset eltér a te kliensedétől show station coverage Állomás hatókör mutatása show station names Megállónevet mutat show waiting bars Terhelési csík mutatása Show/hide statistics Mutat/rejt statiszt. Shows consumer/suppliers for factories A fogyasztók / ellátók mutatása a termelő egységeknél Shows the color code for several selections. Számos kiválasztásnál alkalmazott színkódok mutatása. Shows the currently selected schedule Jelenlegi menetrend mutatása Shrink city Város csökkentése shuffle midis Véletlenszerű lejátszás signal spacing Jelzőtávolság Sim: Szimuláció: Similar view as the main window A főablakéhoz hasonló nézet Size (%d MB): Méret (%d MB): sliced underground mode Metróépítő mód rétegei slot empty üres hely Smart hide objects Épületek elrejtése Sort by Szortírozta Sort waiting list by Rendezés eszerint: Sound Hang Sound settings Hangbeállítások Sound volume: Effektus hangerő: special freight különleges áru Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Sebesség bónusz: \nközút %i km/ó, vasút %i km/ó \nhajó %i km/ó, repülő %i km/ó. Speedlimit Sebességkorlát Speichern Mentés Spieler Játékos Spieler(mz) Játékosok Spielerliste Játékosok listája Spielstand wurde\ngeladen!\n Játék sikeresen betöltve! Spielstand wurde\ngespeichert!\n Játék sikeresen mentve! Sprache Nyelv Sprachen Nyelv kiválasztás: Stadtinformation Város statisztikák Start the selected vehicle(s) A kiválasztott jármű(vek) indítása Starte Spiel Játék indítása Station tiles: Állomáshosszúság: Station_msg Megállók Status Státusz steam gőz Step timeline one year Egy év ugrás Stops Megállók Storage Tárolva Storage capacity Tároló kapacitás Strassendepot Kocsiszin Strassentunnel Közúti alagút építése street car Utcai kocsi sued Dél suedost Délkelet suedwest Délnyugat Summer snowline Nyári hóhatár Supplied: %.0f %% Ellátás: %.0f %% Suppliers Beszállítók: Tage alt napos Theme selector Témaválasztó There are still vehicles\nstored in this depot!\n Még van jármű a depóba! This Month Folyó hónap: This Year Folyó év: Tile not empty. A terep nem üres! Terep megváltoztatása nem lehetséges. timeline Idővonal bekapcsolva tl_title Városok listája To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Több turista kegyeinek\n elnyeréséért \n%s városa épített \negy %s nevű \nturista látványosságot \n%i lakosok segítségével. To heavy traffic\nresults in traffic jam.\n A nagy forgalom forgalmi dugót okoz. tonnen t Toolbar position: Eszköztár helye: Total inhabitants: Teljes népesség: Tourist attractions Turistalátványosságok: Tourists Turisták Town_msg Városok Town: %s\n Város: %s\n Towns Városok track vágány Tracks Vasutak Traffic Forgalom traffic settings Forgalom Train Vonat Trains are not available yet! Még nem áll rendelkezésre vonat! Tram Villamos tram_track villamos vágány Tramdepot Villamos kocsiszin Trams are not available yet! Még nem áll rendelkezésre villamos! Transformer only next to factory! Trafó csak termelőegység mellé telepíthető! Translation Fordítás transparencies Átlátszóság transparent station coverage Átlátszó állomás hatókör Transported Szállítmányok TrolleyBus_tab Trolibuszok Truck Közúti jármű tt_Other Egyebek Tunnel muss an\neinfachem\nHang beginnen!\n Az alagút csakis egyenes emelkedőn építhető! Tunnel must start on single way! Alagút csakis úton kezdődhet Tunnelboden Alagút underground mode Metróépítő mód UNDO failed! A visszavonás nem lehetséges! \nAz útépítés csak \naddig vonható vissza, \namíg nem építesz kapcsolódó \nlétesítményt (jelzőtáblát, \n állomást, megállót).\n Undo last ways construction Utolsó útvonal visszaállítása Unemployed Munkanélküli Unhappy Elégedetlen units/day /nap, Update Line Szerkesztés upgrade HQ Központ fejleszt. Usage: %.0f %% Felhasználva: %.0f %% Usage/Output Felhasz./kimenet Use beginner mode Kezdő mód használata Use timeline start year Idővonal be Kezdő év: Vehicle %s can't find a route! %s nem találja az útat! Vehicle %s is stucked! A %s \njármű dugóba \nkerült (beragadt).\n Vehicle details A jármű adatai vehicles stored Tárolt járművek Verbrauch Felhasználás: Vergroessere die Karte\n Térkép nagyítása Verkauf Eltüntetés verkaufen Eladható Verkehrsteilnehmer Magánautó Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Eladósodtál! %d hónapod van a visszafizetésre! vh_title Minden jármű listája via keresztül (részletesen) via %s\n át %s\n via Menge keresztül (összesen) voranstellen Vontató Waggon_tab Vagonok waiting várakozik Waiting for clearance! Várakozás a szabad útra! Walked Gyalog Warnings_msg Forgalom Water level Tengervíz szintje: water vehicle Vizi jármű way %s cannot longer used:\n %s típúsú út többé már nem elérhatő\n way %s cannot longer used:\n%s\n %s típúsú út többé már nem elérhatő:\n%s\n way %s now available:\n %s típúsú út már elérhatő:\n Ways not connected Az útvonal nincs összekötve waytype Úttípus Wegpunkt Várakozópont Weight: Súly: Wert Érték west Nyugat Winter snowline Téli hóhatár withdraw Selejtezés Withdraw All Összes eladása WRONGSAVE Inkompatibilis verzió Fájl betöltése nem lehetséges. Year %i has started. Az %i év elkezdődött Years Év Your primary color: Elsődleges szín Your secondary color: Másodlagos szín Zielort Célállomás zooming in Nagyítás zooming out Kicsinyítés Zu nah am Kartenrand A térkép széle túl\közel van ahhoz, hogy építeni lehessen! #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Lebegés mágneseken \n%.1f km/h-val, %s által:\nÚj világrekord! New world record for monorails: %.1f km/h by %s. Nyeregvasútak új sebességi világrekordja: %.1f km/h - %s. New world record for motorcars: %.1f km/h by %s. Gépkocsik új sebeségi világrekordja: %.1f km/h - %s. New world record for narrowgauges: %.1f km/h by %s. Új villágrekord metronak/HÉVnek: %.1f km/h by %s. New world record for planes: %.1f km/h by %s. Repülőgépek új sebességi világrekordja: %.1f km/h - %s. New world record for railways: %.1f km/h by %s. Vonatok új sebességi világrekordja: %.1f km/h - %s. New world record for ship: %.1f km/h by %s. Vizijárművek új sebességi világrekordja: %.1f km/h - %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &1_CITY_SYLL hátság &2_CITY_SYLL falva &3_CITY_SYLL szeg &4_CITY_SYLL varsány &5_CITY_SYLL keszi &6_CITY_SYLL füred &7_CITY_SYLL mező &8_CITY_SYLL hegyi &9_CITY_SYLL keresztúr &A_CITY_SYLL határ %1_CITY_SYLL Duna %2_CITY_SYLL Tisza %3_CITY_SYLL Maros %4_CITY_SYLL Dráva %5_CITY_SYLL Száva %6_CITY_SYLL Olt %7_CITY_SYLL Ipoly %8_CITY_SYLL Kőrös %9_CITY_SYLL Szamos %A_CITY_SYLL Ó %B_CITY_SYLL domb 1center %s %s 1extern %s elágazás %s 1suburb %s %s %s 2center %s központi %s 2extern %s felső %s 2suburb %s zöldmezők %s %s 3center %s fő %s 3extern %s határ %s 3suburb %s lakótelep %s %s 4center %s mellék %s 4extern %s külső %s 4suburb %s vidék % 5center %s átszálló %s 5suburb %s falu %s 6center %s központ %s 6extern %s alsó %s 6suburb %s külváros %s %s 7center %s város %s\n 7extern %s külterület %s 7suburb %s park %s 8center %s üzleti %s 8extern %s %s szélső %s 8suburb %s szatelit %s 9center %s tengely %s 9extern %s híd %s 9suburb %s közbülső %s Acenter %s sétány %s Bcenter %s belváros %s Ccenter %s centrum %s simutrans-124.5/simutrans/text/hu/000077500000000000000000000000001520675460500172115ustar00rootroot00000000000000simutrans-124.5/simutrans/text/hu/citylist_filter.txt000066400000000000000000000004121520675460500231600ustar00rootroot00000000000000Városok listája Sugó

Városok listája

Itt egy helyen jelennek meg a városok. Jelezve van a város mérete (lakosszámban) illetve, hogy épp mennyivel fejlõdõtt.

-> Város statisztikák

simutrans-124.5/simutrans/text/hu/citywindow.txt000066400000000000000000000010631520675460500221520ustar00rootroot00000000000000Város statisztikák Sugó

Város statisztikák

Itt lehet a városról infókat szerezni. Kijelzi a város méretét (lakosszámban kell érteni), munkanélkülieket, hajléktalanokat stb.

A két kis térképen nyomonkövetheted az e havi és múlt havi szállítmányozási célállomásokat.

A grafikon pedig segít követni a városlakok számának változását, a város növekedését stb. (értelemszerûen válaszd ki a grafikon alatt lávõ gombokat, aktiváld õket és voila meglesz a grafikon :) )

simutrans-124.5/simutrans/text/hu/color.txt000066400000000000000000000006151520675460500210720ustar00rootroot00000000000000Saját színed

Színválasztó

Itt tudod a saját színedet kiválasztani. Evvel lesznek az összes általad birtokolt javak, vonatok, kocsik, épületek stb. megjelenítve. De ne feledd nem minden épületnek és/vagy jármûnek változik meg a színe, ezt többnyire a tervezõ határozza meg (tudniillik ez egy több emberke álltal fejlesztett játék :-)).

simutrans-124.5/simutrans/text/hu/convoi.txt000066400000000000000000000013121520675460500212440ustar00rootroot00000000000000Jármûlista Sugó

Jármûlista

A következõ beállítások lehetségesek:

1.) Szortírozás:
Lehetõség van a kilistázott jármûvek szortírozására "Név", "Bevétel" vagy "Típus" szerint illetve az így kapott lista a jármûvek betétele függvényében növekvõ, vagy csökkenõ sorrendbe rakható.

2.) Filter:
A kilistázott jármûvekre filtereket is alkalmazhatsz. Vagyis a jármûvek egyedi kritériumok alapján is listázhatóak. A filtereket engedélyezni kell.

->Segédlet a filterek használatához

->Áltlános információk

simutrans-124.5/simutrans/text/hu/convoi_filter.txt000066400000000000000000000027121520675460500226160ustar00rootroot00000000000000Jármûlista filter Sugó

Jármûlista filter

Beállítási lehetõségek:

1.) Filter neve:
A filtereket el lehet nevezni.

2.) Filter tipusa:
Jármûcsoportonként lehet választani. A jármûcsoportok melleti kis gombocskát megnyomva lehet aktiválni a kívánt filtert.

3.) Speciális filter:
Itt lehet a speciális igényeket kiválasztani. Az eltéved jármûveket (vagy a depóban lévõket) itt nagyon könnyen meg tudod találni. Eltévedt jármû az a jármû amelyik nem kapja az útvonalát. Ennek több oka lehet: vagy az történt, hogy építkezéseid során egy kapcsolódási pontot megszüntettél, vagy pont egy olyan állomást töröltél ahova a kérdéses jármû épp indulni készült. Utóbbi esetben ellenõrizd le a jármû menetrendjét vagy a hozzá rendelt vonalat.

4.) Javak filtere:
Itt lehet még jobban szûkíteni a kersett jármûvek listáját. A javak kiválasztásával csak azok a jármûvek jelennek meg amelyek az általad kiválasztott javakat szállítják. A "mind" gombbal a teljes javak listája kiválasztódik, a "semmi" mindent töröl, a "ford." az aktuális kiválasztást megfordítja.

->Kis segédlet a konvojokról

->Általános információk

simutrans-124.5/simutrans/text/hu/depot.txt000066400000000000000000000066171520675460500210770ustar00rootroot00000000000000Közúti\vasúti\hajózási kocsiszin\dokk Sugó

Kocsiszin\dokk ablak

A kocsiszinben lehetõség van különbözõ típusú jármûvek vásárlására, eladására,menetrendek összeállítására.

A kocsiszin ablak megmutatja a vásárolt jármûveket, illetve, hogy miket lehet még vásárolni. A jármûvek különbözõ kategoriákban vannak osztva. Ezt a különbözõ füleken olvashatód le. Tulajdonképpen ez a depó lelke. Itt található az összes jármû. A felsõ részen ellenben a már kész összeállított konvojok között tudsz navigálni. Illetve ugyancsak a fenti legördülõ menük egyikében az útvonalak között barangolhatsz. A felsõ részen látható a már összeállított szerelvény. Több típus is összekapcsolható ha ez megengedett. Ha nem kapcsolható össze akkor azt egy vörös vonal jelzi a jármû alatt. A jobb alsó gomb segítségével állítható be a konvojok össszeállítási módja: "Hozzáad", "Elé rak", "Eladási mód"
Hozzáad - egymás után kapcsolódnak össze a jármûvek
Elé rak - az új jármû a régi elejébe kerül
Eladási mód - a depóban lévõ jármûveket így lehet eladni, tehát nem a konvojokat hanem a már szétbontott és depóban lévõ külön jármûveket. Az, hogy a depóban van-e jármû a bal alsó részen látható.

Az alsó részhez közel az aktuális jármûrõl találhatóak meg a legfontosabb paraméterek (ára, fentartási költsége, mit szállít, csúcssebesség, kapacitás stb.). A felsõ részen 8 menûgomb található.

A Start gombbal lehet a készen összeállított konvojt elindítani, de csak akkor ha a "Menetrend" vagy "Új vonal" menûgomb alatt már készítettél egy menetrendet vagy új vonalat. Az új vonalat "Útvonalat alkalmaz" gombal társítod a konvojhoz. A Menetrend és Vonal között az a különbség, hogy míg a Menetrend csak az adott jármûre érvényes, addig a Vonalat más jármû is használhatja párhuzamosan. Vagyis ugyanazt a vonalat nagyon sok jármû is használhatja és a Vonalon történõ változtatás minden az illetõ Vonalat használó jármûre kihat. A "Szétbont" gombbal az aktuális konvojt bontod fel, de a pénzedet nem kapod vissza. Ilyenkor ahhoz, hogy eladd a jármûveket, a jobb alsó gombot kell Eladási mód-ra változtatni majd a megfelelõ jármûre kattintva eladni. Az egész konvojt egyszerre eladni az "Elad" gombbal lehet.

Vásárláshoz elég ha a bal egérgombbal ráklikkelsz a megfelelõ jármûre. A konvoj tetszés szerintí összeállításához, a kiválasztott jármûveket sorba kell rakni. Nem akármelyék jármû rakható a másik után. Azt hogy elé/utána rakja-e a következõ jármûvet, a jobb alsó részhez közel lévõ kapcsológombbal határozod meg.Csakis egy konvojon dolgozhatsz egyszerre!

A már mûködõ konvoj újbóli kocsiszinbe küldéséhez, a jármû ablakában a "Depóba" gombra kell kattintani, vagy kézzel kell változtatni a menetrenden. Figyelem! A villanyal mûködõ jármûvek csakis a vágányok villamosítása után jelennek meg. Az Elavult jármûvek gombbal lehet szabályozni azt, hogy a depó mutassa-e vagy sem a régi jármûveket.

simutrans-124.5/simutrans/text/hu/display.txt000066400000000000000000000022401520675460500214150ustar00rootroot00000000000000Képernyõ beállítások Sugó

Képernyõ beállítások

Itt tudod a Simutrans megjelenítésével kapcsolatos beállításokat elvégezni.
A következõ dolgokat tudod állítani:

Fényesség - Sötétítí világosítja a képernyõt (- irányba sötétedik).
Színek száma - Nem mûködik
Görg. sebes. - A képernyõ görgetési sebességét lehet szabályozni (a nagyobb gyorsabb).

Fordított görgetés - A képernyõ görgetési irányának a megfordítása
Gyalogost megáll. mutat - Mutatja/elrejti a gyalogosokat a megállókban. Ha a géped egy kicsit lassú kapcsold ki.
Gyalogost városb. mutat - Mutatja/elrejti a gyalogosokat a városokban. Ha a géped egy kicsit lassú kapcsold ki.
Éjszaka/nappal mód - Mutatja a nappalt és az éjszakákat. Elég érdekes effektus, fõleg az éjszaka.

Az ablak alsó részén, a játék szempontjából lényegtelen információk találhatók. Ellenben ha valami hiba van és netán levelezni fogsz a fejlesztõkkel, akkor valoszínû errõl fognak egy pár dolgot kérdezni.

simutrans-124.5/simutrans/text/hu/edittools.txt000066400000000000000000000023401520675460500217570ustar00rootroot00000000000000Szerkesztõ eszközök Sugó

Szerkesztõ eszközök


Város növelése: egyszer kiválasztod a megfelelõ várost, megkeresed a Városházát majd ráklikkelsz. Egy klikkelés +100 lakost add hozzá a város aktuális lakósszámához.

Város csökkentése: egyszer kiválasztod a megfelelõ várost, megkeresed a Városházát majd ráklikkelsz. Egy klikkelés -100 lakost vesz el a város aktuális lakósszámából.

Városi út építése: 50 km/h sebességû utakat tudsz építeni.

Véletlenszerû turista attrakció építése: véletlenszerûen turista attrakciókat épít, így növelheted az utasforgalmat.

Hõerõmû építése: ha a meglévõ hõerõmûveid nem tudják kielégíteni az energia igényeidet akkor ezzel az eszközzel újabb hõerõmûveket építhetsz. A játék a hõerõmûhõz automatikusan szénbányákat is generál.

Fogyasztó/ipari egység építése: evvel az eszközzel tudsz új ipari egységeket építeni.

Játék zárolása: evvel az eszközzel megakadájozhatod azt, hogy a térképen módosítani lehessen.

simutrans-124.5/simutrans/text/hu/finances.txt000066400000000000000000000032731520675460500215450ustar00rootroot00000000000000Pénzügyek Sugó

Pénzügyek (f)

Itt tudod a gazdasági helyzetedet ellenõrizni. Leellenõrizhetõ a nyereség, veszteség a gazdasági hálózatod fentartási költsége és minden egyéb a játék szempotntjából hasznos információ.
Több kategoriában tudsz információkat kinyerni grafikus és számszerû adatok formájában. Fel vannak tüntetve az ez évi és múlt évi adatok is.

A következõ adatinformációk állank rendelkezésedre:

Árbevétel - Minden ami hasznot hoz.

Munkálatok - Minden költség amit építkezésre, hálozatfejlesztésre költöttél.

Karbantartás - Minden karbantartás igénylõ munkálat összegzése.

Üzleti eredmény - Bevételek összegzése.

Új jármû - Új jármûvekre költött pénzösszeg.

Építkezés - Építkezésre költött pénzösszegek összegzése.

Össz profit - Minden bevétel összegzése.

Készpénz - A pillanatnyi készpénz mennyiség (a képernyõ alján folyamatosan jelezve).

Vagyon - Az álltalad felépített vonalak, épületek, ipari egységek össz értéke.

Nettó jövedelem - A pillanatnyi pénzösszeg ami a kasszába befolyik.

Árrés - A pillanatnyi költség és árbevétel közötti különbség. Minnél nagyobb annál jobb.

Havi karbantartás - A közlekedési hálózatod fenttartási költsége.

Szállítmányok - A szállításra költött pénzmennyiség.

Az eredményeket évre, vagy hónapra lebontva lehet megjeleníteni.

simutrans-124.5/simutrans/text/hu/goods_filter.txt000066400000000000000000000017311520675460500224340ustar00rootroot00000000000000Javak listája

Javak listája

A különbözõ javaknak különbözõ színe van. Ez nagyon hasznos ha a kis térképet hasunálod. Minden egyes árú egységnek van egy értéke amely úgymond Simucentben van megadva. Ez azt jelenti, hogyha az árú a megfelelõ helyre ér, a jövedelmed ennyi centtel lesz több egységenként. Ugyanakkor a sebesség függvényében százalékban véve bónuszcenteket is kapsz. A bónusz az alap sebesség és az aktuális sebesség különbségébõl számolódik ki. Részleteket lásd a: http://128.simutrans.com/graphs.htm honlapon (angol nyelvû).

Rakomány Itt vannak felsorolva a különbözõ javak.

Ár: Egyes javak ára Simucentben.

Bonusz: Plusz pénz amit az alapsebbességhez viszonytott aktuális sebességbõl számolodik ki.

Típus: Az árú típusa.

Súly: Egy egység kg-ban vett súlya.

simutrans-124.5/simutrans/text/hu/haltlist.txt000066400000000000000000000014011520675460500215720ustar00rootroot00000000000000Állomás/megálló lista Sugó

Állomás/megálló lista

Az Állomás/megálló listalehetõvé teszi a kis világodban fellelhetõ megállok és állomásák kilistázását, különbözõ opciók szerint. Az állomások/megállók név, a várakozó utas vagy termék, típus és számozás szerint sorba állíthatóak, illetve az azonos kategoriák növekvõ vagy csökkenõ sorrendbe jeleníthetõek meg.

A Filterek segítségével tovább szûkithetõ a megjelenített állomások/megállók listája. A filterek hatása csak engedélyezés után jelenik meg. A Filterek tulajdonságait a Beállítások gombbal lehet beállítani.

simutrans-124.5/simutrans/text/hu/haltlist_filter.txt000066400000000000000000000031231520675460500231420ustar00rootroot00000000000000Állomás/megálló filter Sugó

Állomás/megálló filter

Az Állomás/megálló filter teszi lehetõvé az állomások/megállok további szortírozását.

Beállítási lehetõségek:

1.) Filter neve:
A filtereket (szûrõket) el lehet nevezni.

2.) Filter tipusa:
Megálló típuskont lehet választani. A kívánt filtert a kis kockák "benyomásával" lehet aktiválni.

3.) Speciális filter:
Itt lehet azokat a megállókat/állomásokat kilistázni melyek túlzsúfoltal vagy amit egyetlen jármû sem érint. AZt, hogy az állomást milyen vonalak szolgálják ki, vagy van-e direkt útvonal valamilyen más megállóhoz azt az állomás "Részletek" gombjára kattintva tudod megnézni.

4.) Igényelt nyersany.:
Itt lehet az árú vagy utas függvényében jobban szûkíteni a keresett megállók listáját. A "mind" gombbal minden kijelõlõdik, a "semmi" mindent töröl, a "ford." az aktuális kiválasztást megfordítja.

5.) Termelés:
Itt lehet azokat a megállókat/dokkokat kiszûrni melyek az álltalad kiválasztott árúkat termelik. A "mind" gombbal minden kijelõlõdik, a "semmi" mindent töröl, a "ford." az aktuális kiválasztást megfordítja.

->Megálló részletek

->Általános információk

simutrans-124.5/simutrans/text/hu/language.txt000066400000000000000000000023051520675460500215350ustar00rootroot00000000000000Nyelv választás Sugó

Nyelv ablak

A Simutrans sok nyelvre lett lefordítva. Többek között magyarra is. Az egyes nyelvek kiválasztásakor a játék összes szövege azonnal átvált.
A már létezõ városok neve nem fog megváltozni, hanem az új térkép generálása közben kapott néven marad. Ha azt akarod, hogy a városok egyértelmûen a kiválsztott nyelven jelenjenek meg, akkor az új térkép beindítása elõtt válaszd ki a nyelvet. A Simutrans alapvetõen két módszert használ a városneveknél, az elsõ egy szótagókból álló véletlenszerû névgenerálás a másik módszer pedig \text vagy \pak128\text vagy \pak könyvtárakban található városlistákból szedi ki véletlenszerûen a városok nevét.

Ha netán mégis hiányzik a kívánt fordítás akkor a
http://128.simutrans.com
http://forum.simutrans.com
http://www.simutrans.de

honlapokon kutakodhaszt tovább. Figyelem egyes fordítások nem biztos, hogy tartalmazzák a legutobbi fejlesztések leírását.

Hogy segítsen a Simutrans fordításában az Ön nyelvén,
látogasson el a https://translator.simutrans.com oldalra

simutrans-124.5/simutrans/text/hu/list.txt000066400000000000000000000007341520675460500207310ustar00rootroot00000000000000Szortírozás Sugó

Szortírozás

Különbözõ dolgok, infók filterek segítségével nagyon egyszerûen megtekinthetõek. Lásd a lenti link-eket.

Állomás/megálló lista
Jármûlista
Összes város listázása
Javak listázása
Termelõegységek listázása

simutrans-124.5/simutrans/text/hu/load.txt000066400000000000000000000013201520675460500206650ustar00rootroot00000000000000Betöltés Sugó

Lementett játékok betöltése

A "Betöltés" menûpont lehetõvé teszi a régebben lementett játékok folytatását. A felkínált gombok hasonlóak a "Mentés" menûpont alatti gombokkal.

Ha netán olyan játékot szertnél betölteni ami már nem létezik, akkor egyszerûen egy hibaüzenetet kapsz.

Fontos! Régebbi verzióban mentett játékok nem minden esetben kompatibilisek az új verziókkal. További információért kutakodj egy kicsit a neten. Például itt:http://forum.simutrans.com. Több mint valószínû a nagy verzióváltások lementett játékai nem kompatibilisek egymással.

simutrans-124.5/simutrans/text/hu/mailbox.txt000066400000000000000000000034131520675460500214060ustar00rootroot00000000000000Postaláda/Postaláda opciók Sugó

Postaláda/Postaláda opciók

A játék során felbukkanó összes üzenet itt mentõdik. Ugyanakkor itt tudod beállítani azt, hogy mirõl és hogyan kérsz figyelmeztetést.

Az Opciók gomb a "Postaláda opciók" nevû ablakot hozza fel. Itt lehet beállítani, hogy a különbözõ események üzenetei hogyan jelenjenek meg. Alapvetõen három módot lehet beállítani:
1. A kis bal oldali kocka aktiválása az jelenti, hogy az üzenetek futó szövegként a játék fõ ablakának alján jelennek meg (scroll text).
2. A közbelsõ kocka aktiválása azt jelenti, hogy az üzenetek egy felugró ablakban jelennek meg, amit csakis kézzel lehet lezárni.
3. A jobb kis kocka aktiválása azt jelenti, hogy az üzenetek egy felugró ablakban jelennek meg ami egy idõ után magától lezáródik.

From top to bottom, the following messages are adjustable:
-Új év: Üzenet küldése új év kezdetekor.
-AI (mesters. int.): Üzenet a számítógép álltal vezérelt játékos (AI) új vonaláról.
-Új város: Üzenet nagyobb városháza építésekor.
-Eltévedt jármû: Üzenet olyan jármûvekrõl, melyek nem találják az utat.
-Új ipari egység: Üzenet új ipari lánc kialakításáról.
-Turisták: Üzenet új turista atrakció vagy emlékmû építésérõl.
-Új jármû: Üzenet új jármû érkezésérõl.
-Tároló telítvel: Üzenet a túlterhelt megállókról.
-Egyéb problémák: Üzenet egyébb kritikus hibákról.

Minden egyes üzenetkor, az üzenetre vagy az üzenet melletti képre kattintva ráugrasz a problémás területre.

simutrans-124.5/simutrans/text/hu/map.txt000066400000000000000000000050021520675460500205240ustar00rootroot00000000000000Térkép ablak Sugó

Térkép ablak

Az egész "világról" mutat egy madártávlat típusú térképet. Alapállásban a térképen nincs feltüntetve semmi. Azt, hogy mi látszodjon a kis térképen azt a "Jelmagyarázat" ablakban tudod beállítani.

A "Jelmagyarázat" ablakban a következõ dolgokat lehet a kis térképre kihelyezni:
Városok - Megjeleníti a városok nevét illetve helyét.
Utas - Megjeleníti azokat a pontokat ahol utasok tartozkodnak. A terhelési színekbõl lehet következtetni (lásd az ablak felsõ részén a minimum és maximum színkódjait), hogy egy adott állomás vagy megálló mennyire zsúfolt.
Posta - Feltünteti a posták helyét.
Rakomány - Megjeleníti azokat a pontokat ahol rakomány van (legyen az árú, vagy nyersanyag, vagy posta).
Státusz - Megjeleníti azokat a helyeket, ahol túlterheltség vagy zsúfoltság van.
Szerviz - Jelzi azokat az útvonalakat és egységeket melyek szervizelést igényelnek.
Forgalom - A forgalom nagyságát jelzi utakon vasútakon.
Kiindulás - Kijelöli azokat az állomásokat vagy megállókat ahonnan az árúk, utasok kiindulnak illetve itt lehet követni azt, hogy honnan indul ki a legtöbb utas vagy árú.
Célállomás - Jelzi azokat a célállomásokat és megállókat ahova a legtöbb utas vagy árú tart.
Várakozik - Kijelöli azokat a megállókat vagy állomásokat ahol a legtöbb árú vagy utas várakozik.
Vonalak - Megjeleníti az összes vasúti és közúti vonalat.
Sebes.korlát. - Kijelzi azokat a szakaszokat ahol sebességkorlátozás van.
Elektr.hálóz. - Megjeleníti az egész elektromos hálózatot.
Turisták - Kijelzi azokat a helyeket ahol turisták illetve turistacélpontok vannak.
Gyárak - Megjeleníti az összes termelõ egységet (gyárakat, bányákat, iparegységeket).

Minden egyes termelési egység külön színnel van feltüntetve. A részletes színkódott a térkép alján találod.A minimum és maximum értéket is színkód jelzi (vagyis azt, hogy sok az utas vagy kevés, túlterhelt egy állomás vagy nem, nagy forgalom vagy kicsi stb.).

A madártávlat térképre kétfoklozatú nagyítás alkalmazható. Bármelyék részére az egér jobb gombjával, vagy a középsõ görgõvel ráklikkelve változtathatód a nagyítás mértékét.

simutrans-124.5/simutrans/text/hu/new_world.txt000066400000000000000000000057501520675460500217610ustar00rootroot00000000000000Új Térkép Sugó

Új térkép

Itt tudod az új létrehozandó térkép paramétereit beállítani. Mindegyik mellet egy rövid leírás található.

Térképszám - Itt határozod meg a térkép típusát. Egy kis elõnézeti térképet is láthatsz melyen a paraméterek változtatásának hatását lehet ellenõrizni.
Méret - A térkép MB vett méretét lehet meghatározni. Az alap a 20 MB. Fontos! Minnél nagyobb a MB vett térkép méret annál lassabban készül el az új térkép és több memoria szükségeltetik.

Városok száma - A városok számát tudod beállítani (min. 2 max. 64) Alapérték 16.

Forg. sûrûsége - A városban közlekedõ kocsik sûrûségét lehet meghatározni. Alapérték 9. Vízmennyiség - Az új térképen megjelenõ vízmennyiséget szabályozód. Minél kisebb az érték annál több föld lesz ill. annál kevesebb víz és fordítva. Alpérték 4.
Hegymagasság - A hegyek magasságát lehet beállítani. Minél kissebb annál lágyabb az átmenet a terepen. Alapérték 160.
Terepdurvaság - Mennyire legyen változatos a terep, mennyire legyen dimbes-dombos. Alapérték 2.

Ipari egys. (terepen) - Az ipari egységek sûrûségét szabályozod városon kívül. Nagyobb érték több ipari egységet jelent.
Ipari egys. (városban) - Az ipari egységek sûrûségét szabályozod a városokban vagy a városok közelében. Nagyobb érték több ipari egységet jelent.
Turista forgalom - Mennyi turista legyen a kis világodban. A nagyobb érték több turistaforgalmat jelent.

Átlag lakos per város - Itt lehet meghatározni az átlag lakósszámot városonként.

Véletlen térkép - Véletlen térkép generálása (nem befolyásol más beállítást).

Gyalogosokat mutat - Itt lehet beállítani, hogy mutassa-e vagy sem a városokban a gyalogosokat. A játék elindítása után a Képernyõ opciók dialogus ablakban módosítható.
Éjszaka/nappal mód - Itt állítható be, hogy legyen-e éjszaka és nappal mód. Ha ki van kapcsolva akkor "állandóan nappal van" :). A játék elindítása után a Képernyõ opciók dialogus ablakban módosítható.

Játék betöltés - Egy már lementett játék indítását teszi lehetõvé. (lásd az Opcióknál a Mentésmenüpontot.)
Játék indítása Az új a fenti paramétereket alkalmazó játék indítása.
Domborzat betölt. Itt lehet az álltalad készített térképfájlt, domborzatfájlt betölteni. Mikor nincs kedved a véletlenszerû térképekhez akkor készíts magadnak egy térképet. Azt, hogy hogyan kell ezt csinálni azt a neten lehet megtudni :)
Kilépés - Végleges kilépés a játékból.

simutrans-124.5/simutrans/text/hu/options.txt000066400000000000000000000016101520675460500214430ustar00rootroot00000000000000Játék beállítások Sugó

Játék beállítások

Itt lehet a mindenféle beállítási lehetõségeket kihasználni és alkalmazni

Nyelv - A játék nyelvének a megváltoztatása.
Játékos szín - Saját színed megváltoztatása.
Képernyõ - Képernyõvel kapcsolatos beállítások.
Hang - Hangokkal kapcsolatos beállítások.
Játékosok - Itt tudod a számítogépes ellenfeleidet (AI) kiválasztani.
Betöltés - Lementett játék betöltése
Mentés - Az aktuális játék lementése.
Új térkép - Új térkép létrehozása (figyelem a régi mentés nélkül lezárodik)
Kilépés - Kilépés a játékból (nincs figyelmeztetés és nem menti a játékot).

Részleteket az egyes opciók helyi sugójánál találsz.

simutrans-124.5/simutrans/text/hu/players.txt000066400000000000000000000010301520675460500214230ustar00rootroot00000000000000Játékosok Sugó Help

Játékosok

Itt tudod a számítógép állatl vezérelt ellenfeleidet (AI - Artificial intelligence) ki, illetve bekapcsolni.

A kikapcsolás a kis sima négyzet manipulálásával érhetõ el. Figyelem ha kikapcsolsz egy játékost lényegében csak annyit csinálsz, hogy "elaltatod" vagyis több fejlesztés nem lesz de a már felépített termelõegységek mûködni fognak.

Tájékoztatásképp az egyes játékosok pénzösszegét tudod egyelõre megnézni.

simutrans-124.5/simutrans/text/hu/railtools.txt000066400000000000000000000013701520675460500217630ustar00rootroot00000000000000Vasúti eszközök Sugó

Vasúti eszközök

Itt tudsz vasútat, mozdonyokat, jelzõlámpákat, alagútakat megállókat, depókat, felüljárókat építeni.

Többfajta vasúti sín létezik, válaszd ki a sebesség függvényében a neked megfelelõt.

A vasúti jelzõ építésekor mikor felépíted, akkor az mindkét irányba fog jelezni. Sorozatos klikkeléssel éred el azt, hogy a megfelelõ irányba mutasson.

Az alagút építésnél figyelembe kell vedd, hogy egyszer a hegyoldalra a síneket kell felépítsd és csak erre majd az alagútat. De erre a játék idejében figyelmeztet.

A Depóban tudod összeállítani a megfelelõ vasúti szerelvényt (mozdonyokat, vagonokat).

simutrans-124.5/simutrans/text/hu/roadtools.txt000066400000000000000000000005111520675460500217550ustar00rootroot00000000000000Közúti eszközök Sugó Help

Közúti eszközök

Itt található a közútak és kocsik építéséhez szükséges összes eszköz.

Többféle úttípus közül választhatsz. Válaszd ki a sebesség függvényében a neked épp megfelelõt, majd klikkeléssel helyezd a térképre.

simutrans-124.5/simutrans/text/hu/save.txt000066400000000000000000000026001520675460500207060ustar00rootroot00000000000000Mentés Sugó

Mentés

A megfelelõ fájlnév megadásával lehet kimenteni az aktuális játékmenetet, ami késõbb a "Betöltés" menûpontban bármikor visszatölthetõ.

Figyelem! Nagy verzióváltásoknál több mint valószínû a régebbi játékok nem tölthetõk be.

Menetrend Sugó

Menetrend ablak

Itt határozod meg. hogy konvojod milyen pontok között közlekedjen. Hat gombot találhatsz az ablak alsó részén:"Hozzáadás", "Beszúrás" "Törlés", "Kész","Új vonal", "Visszaút". A "Hozzáad" gombbal megállokat adsz hozzá a menetrendhez. A "Beszúrás" gombbal az aktuális megálló elé szúrsz be egy újabb megállót. Az aktuális megálló egy dupla ">>" jelezve van. A "Töröl" gombbal tudsz megállókat törölni, mégpedig úgy, hogy ha benyomod a "Töröl" gombot és utána a megfelelõ megállóra kattintasz a bal egérgombbal akkor az törlödik. Minden egyes megállóban meg tudod határozni, hogy a jármû kapacitásának, hány százaléka teljen fel mielõtt az elindulna. Tudni kell, hogy ha magasabb százalékra rakod és a termelõegység nem tudja egybõl megtölteni a jármûvet, akkor az addig marad a megállóban míg a megfelelõ százalékban fel nem telik. Az "Új vonal" új vonal tervezését teszi lehetõvé. A "Visszaút" pedig értelemszerû.

simutrans-124.5/simutrans/text/hu/schedule.txt000066400000000000000000000020521520675460500215450ustar00rootroot00000000000000Menetrend Sugó

Menetrend ablak

Itt határozod meg. hogy konvojod milyen pontok között közlekedjen. Hat gombot találhatsz az ablak alsó részén:"Hozzáadás", "Beszúrás" "Törlés", "Kész","Új vonal", "Visszaút". A "Hozzáad" gombbal megállokat adsz hozzá a menetrendhez. A "Beszúrás" gombbal az aktuális megálló elé szúrsz be egy újabb megállót. Az aktuális megálló egy dupla ">>" jelezve van. A "Töröl" gombbal tudsz megállókat törölni, mégpedig úgy, hogy ha benyomod a "Töröl" gombot és utána a megfelelõ megállóra kattintasz a bal egérgombbal akkor az törlödik. Minden egyes megállóban meg tudod határozni, hogy a jármû kapacitásának, hány százaléka teljen fel mielõtt az elindulna. Tudni kell, hogy ha magasabb százalékra rakod és a termelõegység nem tudja egybõl megtölteni a jármûvet, akkor az addig marad a megállóban míg a megfelelõ százalékban fel nem telik. Az "Új vonal" új vonal tervezését teszi lehetõvé. A "Visszaút" pedig értelemszerû.

simutrans-124.5/simutrans/text/hu/shiptools.txt000066400000000000000000000004671520675460500220050ustar00rootroot00000000000000Hajózási eszközõk Sugó

Hajózási eszközõk

Itt találhatód a hajók, dokkok építéséhez használatos eszközõket.

Megállókat a kiválasztott hely közvetlen közelébe kell elhelyezd, hogy azt a program a te álltalad igényelt állomásnak ismerje fel!

simutrans-124.5/simutrans/text/hu/sound.txt000066400000000000000000000010771520675460500211070ustar00rootroot00000000000000Hang beállítások Sugó

Hang beállítások

Itt tudod a játék hang- és zeneeffektusait beállítani.

Effektus hangerõ - A játék hangeffetusait szabályozod (pld. vonat fütyülése stb.)
Zene hangerõ: - A zene hangereját szabályozod. Figyelem a játékhoz nem minden esetben tartozik zene (attól függ honnan töltötted le). Ha nincsenek hangfájlok akkor ez a csuszka nem változtat semmin. Azt, hogy épp milyen hangfájl van használatban az két sorral lejjebb lehet látni.

simutrans-124.5/simutrans/text/hu/special.txt000066400000000000000000000031461520675460500213760ustar00rootroot00000000000000Speciális eszközök Sugó

Speciális eszközök

Itt találhatók azok az eszközök melyekkel a posta szállítási vonalakat felépítheted, illetve olyan eszközök melyekkel az egyes állomások, megállók/rakodóudvarok kapacitását megnövelheted.

Eszközök:

- Posta: több fajta postai épületet lehet építeni.

- Egy állomás kapacitását növelõ épületek: ezeket az épületeket egy már meglévõ megálló vagy állomás mellé kell építeni.

- Játékos váltás: nagy P betûvel válthatsz az AI vezérelte játékosok és magad álltal kontrolált játékos között. Azt, hogy épp melyék játékos aktív az a képernyõ alján látható. Illetve itt érhetõek el egyébb "Szerkesztõ" eszközök.

- Új város alapítása: ezt az eszközt kell alkalmazs, ha egy teljesen új várost akarsz építeni.

- Faültetés: minél több fát ültetsz annál nyereségesebb az egész rendszer.

- Elektromos hálózat építése: ezzel az eszközzel építheted ki az elektromos hálózatodat. A vezetékek a termelõegységekhez egy Trafóállomáson keresztül csatlakoznak.

- Trafóállomás építése: ez az eszköz szükséges a Trafóállomások felépítéséhez. A Trafóállomást közel kell építeni a termelõ egységekhez. A villamos vezetékek ide kell csatlakozzanak.

- Jelzõtábla: a térképen jelzõtáblákat helyezhetsz el a jobb tájékozódást elõsegítve.

simutrans-124.5/simutrans/text/hu/station.txt000066400000000000000000000003541520675460500214350ustar00rootroot00000000000000 Megálló/állomás infó Sugó

Megálló/állomás infó

Itt találhatod az aktuális állomás paramétereit. Pld. mennyi nyersanyag vagy utas várakozik, különbözõ statisztikai grafikonok stb.

simutrans-124.5/simutrans/text/hu/station_details.txt000066400000000000000000000003601520675460500231370ustar00rootroot00000000000000Részletek Sugó

Részletek

Itt megnézheted, hogy ehhez az egységhez milyen gyárak, termelõegységek kapcsolódnak.

Továbbá kijelzi a direkt útat, a tároló kapacitást és egyéb infót.

simutrans-124.5/simutrans/text/hu/tramtools.txt000066400000000000000000000013571520675460500220040ustar00rootroot00000000000000Villamos eszközök Sugó

Villamos eszközök

Itt tudsz villamos síneket, jelzõlámpákat, megállókat, depókat, felüljárókat építeni.

Többfajta villamos sín létezik, válaszd ki a sebesség függvényében a neked megfelelõt.

A villamos jelzõ építésekor mikor felépíted, akkor az mindkét irányba fog jelezni. Sorozatos klikkeléssel éred el azt, hogy a megfelelõ irányba mutasson.

Tudni kell, hogy villamos a rendes vasúti síneken is mûködik. Ezért ha hídra vagy alagútra van szükséget használd a "Vasúti eszközöknél" fellelhetõ kellékeket.

A Depóban tudod összeállítani a megfelelõ villamos szerelvényt.

simutrans-124.5/simutrans/text/id.tab000066400000000000000000001246231520675460500176710ustar00rootroot00000000000000Indonesia PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: id Indonesia # # Encoding: ISO-8859-1 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable Non-aktif cl_btn_filter_enable Aktif cl_btn_filter_settings Pengaturan cl_btn_sort_asc Kecil ke besar cl_btn_sort_desc Besar ke kecil cl_btn_sort_id ID internal cl_btn_sort_income Laba cl_btn_sort_name Nama cl_btn_sort_type Jenis clf_btn_alle Semua clf_btn_invers inv. clf_btn_keine tidak ada gl_btn_sort_bonus Bonus gl_btn_sort_name Nama gl_btn_sort_revenue Harga gl_btn_unsort Acak hl_btn_filter_disable Non-aktif hl_btn_filter_enable Aktif hl_btn_filter_settings Pengaturan hl_btn_sort_asc Kecil ke besar hl_btn_sort_desc Besar ke kecil hl_btn_sort_name Nama hl_btn_sort_type Jenis hl_btn_sort_waiting Menunggu hlf_btn_alle Semua hlf_btn_invers Balik hlf_btn_keine Tidak ada Networks Rute Queueing Tren menunggu Road toll Jalan Tol Scenario Info skenario Transfers Yang lewat #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Iklim Kutub desert Gurun mediterran Iklim Mediterrania rocky Iklim Alpen temperate iklim sedang tropic Iklim Tropis tundra Iklim Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Scenario script tidak dapat dibuka Can't buy obsolete vehicles! Kendaraan tua tidak dapat dibeli. Cannot alter water Air tidak dapat ditambah atau dikurangi lagi. Cannot built depot here! Depot tidak dapat dibangun di sini. Cannot built this station/building\nin underground mode here. Stasiun/bangunan ini tidak dapat\ndibangun di bawah tanah di sini. Cannot create generic line!\nSelect line type by\nusing filter tabs. Rute semua jenis tidak dapat dibuat.\nGunakan filter untuk\nmemilih jenis kendaraan. Cannot create socket Koneksi socket tidak dapat dilakukan Convoi handles exhausted! Konvoi sudah mencapai jumlah maksimum. Convoy already deleted! Konvoi sudah dihapus. Das Feld gehoert\neinem anderen Spieler\n Tanah ini\ndimiliki oleh\npemain lain.\n Der Besitzer erlaubt das Entfernen nicht Pemilik tidak\nmemberikan ijin\nuntuk menghapus.\n Diese Zusammenstellung kann nicht fahren!\n Salah Kombinasi\nKendaraan tdk dpt bergerak!\n Flugzeughalt muss auf\nRunway liegen!\n Tempat parkir pesawat\nharus di taxiway. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Bandara tidak dapat\n\ndibangun di sini.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Sinyal kereta tidak dapat\ndiletakkan di sini.\n In order to lock the game, you have to protect the public player by password! Untuk mengunci permainan, anda harus memberikan kata sandi untuk public player. Lost connection\nto server! Koneksi dengan\nserver terputus. Lost synchronisation\nwith server. Kehilangan sinkronisasi\ndengan server. Maglevhalt muss auf\nMaglevschiene liegen!\n Stasiun maglev harus dibuat di rel maglev Monorailhalt muss auf\nMonorail liegen!\n Stasiun Monorel\nharus dibuat di\njalur Monorel!\n Monorails are not available yet! Monorel belum dikembangkan! Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Stasiun rel sempit hanya dapat diletakkan di rel sempit. No suitable way on the ground! Pemberhentian hanya dapat dibangun\ndi jalurnya dan harus datar. No through station here! Pemberhentian hanya dapat dibangun di jalur lurus tanpa persimpangan. Not enough money! Uang Anda tidak cukup\nuntuk membangun ini. On narrowgauge track only!\n Hanya untuk rel sempit.\n Only public player can lock games! Hanya public player yang dapat mengunci permainan. Out of funds Uang Anda tidak cukup untuk melakukan hal ini. Post muss neben\nHaltestelle\nliegen!\n Bangunan tambahan\n(kantor pos,gudang)\nhanya dapat dibangun di tanah kosong\ndi sebelah pemberhentian yang sudah ada.\n Protocoll error (expecting game) Protocol error (expecting game) Schiffhalt muss im\nWasser liegen!\n Pemberhentian kapal harus\ndibangun di air\ndi dekat dermaga.\n Server busy Server sibuk. Terraforming not possible\nhere in underground view Di bawah tanah, tanah tidak dapat dinaikturunkan. This tunnel branches. You can try Control+Click to remove. Terowongan ini memiliki cabang. Ctrl+klik untuk membongkar. Upgrade must have\na higher level Hanya dapat diperbaharui\nke tingkat yang lebih tinggi. Vehicle %s cannot choose because stop too short! Kendaraan %s tidak dapat memilih karena pemberhentian terlalu pendek. Zughalt muss auf\nSchiene liegen!\n Stasiun kereta\nhanya dapat dibangun\ndi atas rel kereta.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Help

Help Index

About Simutrans

%1$s

How to use

%2$s

How to start a game

%4$s

How to play

%5$s

Tools

%3$s

Others windows

%6$s Decrease water height Menurunkan air sebanyak satu tingkat Highlight railroad tracks Menampilkan rel kereta Highlite depots Menampilkan depot Highlite electrical transmission lines Menampilkan kabel transmisi listrik Highlite factories Menampilkan pabrik Highlite forests Menampilkan hutan Highlite tourist attraction Menampilkan tempat wisata Increase water height Meninggikan air sebanyak satu tingkat Overlay city limits Menampilkan batas kota Overlay passenger destinations when a town window is open Menampilkan tujuan penumpang saat jendela informasi kota dibuka Overlay schedules/network Menampilkan jadwal/jaringan Overlay town names Menampilkan nama kota Please click on the map to add\nwaypoints or stops to this\nschedule. Klik di peta untuk menambah\nwaypoint atau pemberhentian\nke jadwal ini. Set tile climate %s Mengubah tanah ini menjadi iklim %s. Show capacity and if halt is overcrowded Menampilkan kapasitas dan keramaian pemberhentian Show how many convoi reach a station Menampilkan jumlah konvoi yang mencapai stasiun Show how many people/much is waiting at halts Menampilkan jumlah yang menunggu di pemberhentian Show initial passenger departure Menampilkan keberangkatan awal penumpang Show level of city buildings Menampilkan tingkat bangunan kota Show mail service coverage/mail network Menampilkan jangkauan layanan pos Show passenger coverage/passenger network Menampilkan jangkauan layanan penumpang Show speedlimit of ways Menampilkan batas kecepatan jalan Show the change of waiting at halts Menampilkan perubahan jumlah yang menunggu dibandingkan bulan lalu Show the owenership of infrastructure Menampilkan kepemilikan bangunan/fasilitas Show transported freight/freight network Menampilkan jaringan kargo yang diantarkan Show usage of network Menampilkan kendaraan setiap kotak setiap bulan. Shows a listing with all industries on the map. Menampilkan daftar semua industri di peta. Sum of departure/arrivals at halts Jumlah kedatangan/keberangkatan di pemberhentian #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s\nmenyediakan bus\nantara %s\ndan tempat wisata\n%s\ndi (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s\nmenyediakan bus\nantara %s\ndan pabrik\n%s\ndi (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nmengoperasikan\n%i truk antara\n%s di (%i,%i)\ndan %s\ndi (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\nmembuka jalur kereta\nantara %s\ndi (%i,%i)\ndan %s\ndi (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Layanan penerbangan oleh\n%s\nantara\n%s \ndan %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Layanan kapal feri oleh\n%s\nantara\n%s \ndan %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\nmembuka layanan\nbus antara\n%s dan\n%s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Infrastruktur Publik LISTTOOLS Data-Data MAGLEVTOOLS Infrastruktur Maglev MONORAILTOOLS Infrastruktur Monorel NARROWGAUGETOOLS Peralatan kereta rel sempit RAILTOOLS Infrastruktur Kereta ROADTOOLS Infrastruktur Jalan SHIPTOOLS Infrastruktur Kapal SLOPETOOLS Perbaiki Lahan SPECIALTOOLS Infrastruktur Khusus TRAMTOOLS Infrastruktur Trem #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s membangun kantor pusat baru. Factory chain extended\nfor %s near\n%s built with\n%i factories. Ekonomi meningkat:\n%s di dekat %s menambah pabrik.\n%i pabrik baru dibangun. New %s now available:\n%s\n %s baru tersedia:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Pertumbuhan industri: Pabrik baru\nuntuk %s di dekat\n%s dibangun beserta\n%i pabrik. New vehicle now available:\n%s\n \n Kendaraan baru\n tersedia:\n\n\n -- %s --\n\n Now %u clients connected. Saat ini %u orang tersambung. Remove vehicle from map. Use with care! Menghapus kendaraan. (Gunakan secara hati-hati) Screenshot\ngespeichert.\n Screenshot\ntersimpan.\n Sends the convoi to the last depot it departed from! Mengirim konvoi ke depot tempat konvoi ini berangkat. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s membangun\nmonumen baru.\n%i penduduk bersuka cita. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Filter rute #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (di depot) \nBauzeit bis - \nBauzeit von \nAda sejak \nCan't open heightfield file.\n \nPeta ketinggian tidak dapat dibuka.\n \ndirection: \narah: \nelektrified \nberlistrik\n \nHeightfield has wrong image type.\n \nTanah memiliki jenis\ngambar yang salah.\n \nis reserved by: \nDigunakan Kereta: \nminimum speed: \nKecepatan Minimum: \nnot elektrified \nTidak Terelektrifikasi\n \nRibi (masked) \narah\m (disembunyikan): \nRibi (unmasked) \narah\m (ditampilkan): \nSet phases: \nTenggang waktu\nUtara-Selatan Timur-Barat: \nsingle way \nSatu Arah \nway1 reserved by \njalur 1 digunakan oleh \nway2 reserved by \njalur 2 digunakan oleh \nwith sign/signal\n \ndengan rambu/sinyal\n %d buildings\n %d bangunan\n %d convois %d konvoi %d Einzelfahrzeuge im Depot %d kendaraan disimpan di sini. %i km/h (max. %ikm/h) %i km/h (maks. %i km/h) %i years %i months old. Usia: %i tahun %i bulan %s at (%i,%i) now public stop. %s menjadi pemberhentian umum. %s building %s %s %s %s %s %s city %d %s %s %d %s %s factory %s %s %s %s (pabrik) %s has entered a depot. %s telah kembali ke depot. %s land %d %s %s %d %s pinggir kota %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s \nmembangun\nbalai kota baru\nsaat mencapai\n%i penduduk. %s\nis crowded. %s\n Penuh. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nkecepatan maksimum %3$i km/h\n\nkecepatan saat ini %2$i km/h.\n\n %s\nwas liquidated. %s bangkrut dan\nsudah dikeluarkan.\nSeluruh aktifitas dihentikan\ndan semua aset telah dijual. %u Client(s)\n %u orang\n %u Player (%u locked)\n %u pemain (%u terkunci)\n 1 convoi 1 konvoi 1 Einzelfahrzeug im Depot 1 kendaraan disimpan di sini. 1LIGHT_CHOOSE Brightness 1WORLD_CHOOSE Peta 2LIGHT_CHOOSE Warna 2WORLD_CHOOSE No. Peta 3LIGHT_CHOOSE Scroll Speed 4LIGHT_CHOOSE Scroll Inverse 5LIGHT_CHOOSE Pederstrians at stops 5WORLD_CHOOSE Jumlah Kota 6LIGHT_CHOOSE Pedestrians in town 6WORLD_CHOOSE Traffic Density 8WORLD_CHOOSE Tampilkan Malam Hari A bridge must start on a way! Ujung jembatan tidak tersedia atau terdapat penghalang di antaranya. Abfrage Lihat Abnehmer Konsumen About Tentang About scenario Copyright Abriss Hapus Absenken Turunkan Daratan Abspanntransformator Gardu Listrik Accelerate time Percepat Waktu Act. load: %u MW\n Yang digunakan: %u MW\n Active player only Hanya pemain yang aktif Add forest Membuat Hutan Add random citycar Tambah kendaraan penduduk add server Tambah server Add Stop Tambah pemberhentian Add stops for backward travel Tambah pemberhentian untuk perjalanan balik. aircraft_tab Pesawat kargo airplane pesawat Airport Bandara AIRTOOLS Infrastruktur Pesawat All Semua all convoi tooltips semua keterangan konvoi Allow city growth Ijinkan kota untuk berkembang Allow player change Ijinkan ganti pemain allowed climates:\n Iklim yang diijinkan:\n Alters a schedule. Tambah/hapus pemberhentian dari/ke jadwal Angenommene Waren Barang yang dibutuhkan oleh industri dekat sini anhaengen Tambah di akhir Anhaenger_tab Peti kemas Anheben Naikkan Daratan Appends stops at the end of the schedule Tambah pemberhentian di akhir jadwal Apply Line Terapkan Arbeiter aus: Tempat Tinggal Pekerja: Arrivals from\n Kedatangan dari\n Arrived Datang Assets Aktiva Tetap Aufloesen Batalkan Aufspanntransformator Gardu Listrik August Agustus Autohalt muss auf\nStrasse liegen!\n Halte Bus\nharus dibuat\ndi jalan. Available Tersedia Bahndepot Depot kereta Bankrott:\n\nDu bist bankrott.\n Bangkrut:\n\nAnda bangkrut.\n battery Baterai Baum Pohon baum builder Tanami Pohon Baustelle Tempat pembangunan Bauzeit waktu pembangunan Beenden Keluar Beginner mode Untuk pemula Besonderes Gebaeude Venue BF Stasiun bio Biologis Blockstrecke ist\nbelegt\n \nJalur sedang digunakan kereta lainnya,\ntunggu hingga kereta lewat!\n Boden Tanah Bonusspeed: %i km/h Kecepatan maks. yang mungkin dicapai: %i km/h Boost (%%) Peningkatan produksi(%%) bridge is too high for its type! Melebihi tinggi maksimum jenis jembatan ini. Bridge is too long for this type!\n Melebihi\npanjang maksimum\njenis jembatan ini.\n Bruecke Jembatan Bruecke muss an\neinfachem\nHang beginnen!\n Jembatan harus\ndibuat ditanah\nyang lurus!\n Brueckenboden Jembatan Build air depot Hangar Pesawat build choosesignals Membuat sinyal yang menentukan peron yang digunakan Build city market Bangun Rangkaian Industri di sekitar kota ini. Build drain Gardu Listrik build HQ Bangun kantor pusat Build land consumer Bangun Pembangkit listrik Build maglev depot Bangun depot maglev Build monorail depot Bangun depot monorel Build narrowgauge depot Membangun depot rel sempit Build powerline Bangun SUTET Build presignals Membuat sinyal dua blok Build road depot Garasi Build ship depot Bangun depot kapal Build signals Membuat sinyal Build train depot Bangun depot kereta Build tram depot Bangun depot trem Build truck depot Garasi Building costs estimates Perkiraan Biaya Buildings Jumlah Bangunan Built artifical slopes Perbaiki bentuk tanah Built random attraction Bangun Venue apapun Bus_tab Bus Can only move from halt to halt or waypoint to waypoint. Hanya dapat memindah\npemberhentian ke pemberhentian\natau waypoint ke waypoint Cancel Batalkan Cannot connect to offline server! Tidak dapat menghubungi server offline. Capacity: Kapasitas: Capacity: %.0f MW Kapasitas: %.0f MW\n Capacity: %d%s %s\n Kapasitas: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Kapasitas: %s\n? %d (%d%%) Cars are not available yet! Mobil belum tersedia. cars.\nstate mobil\n Cash Kas Change player Ganti Pemain Chart Grafik Chat_msg Bincang Choose direction Pilih arah Choose operation executed on clicking stored/new vehicles Pemilihan arah dilakukan saat klik pada kendaraan baru/tersimpan chooses a random map Peta Acak citicens Penduduk City attraction Venue dalam kota City industries Industri City list Daftar Kota City size Jumlah Penduduk city_road Jalan Perkotaan citybuilding builder Bangun Rumah penduduk CityLimit Batas Kota cl_title Daftar kendaraan cl_txt_sort Urutkan: Clear block reservation Menampilkan jalur/jalan yang digunakan clf_chk_aircrafts Pesawat clf_chk_cars Bus/truk clf_chk_indepot di depot clf_chk_maglev Maglev clf_chk_monorail Monorel clf_chk_name_filter Nama filter: clf_chk_narrowgauge Kereta rel sempit clf_chk_noincome Tidak membuat untung clf_chk_noline Tidak ada rute clf_chk_noroute Tidak ada jalur/jalan clf_chk_noschedule Tidak ada jadwal clf_chk_obsolete Kuno/tua clf_chk_ships Kapal clf_chk_spezial_filter Filter khusus: clf_chk_stucked Tersangkut clf_chk_trains Kereta clf_chk_trams Trem clf_chk_type_filter Jenis filter: clf_chk_waren Filter barang: clf_title Filter daftar kendaraan Climate Control Pengaturan kondisi alam closed ditutup. COLOR_CHOOSE\n Pilih dua warna\ndari gambar\ndi kanan: Company bankrupt Perusahaan bangkrut Company_msg Lawan Comparing pak files ... Membandingkan berkas pak... Configure AI Mengatur lawan komputer Configure AI setttings Melakukan pengaturan lawan komputer Congratulation\nScenario was complete in\n%i months %i years. Selamat!\nSkenario sudah\nselesai dalam\n%i bulan dan %i tahun. Connected stops Pemberhentian yang terhubung: Connected with server Terhubung ke server Constructed by Digambar oleh Constructed by %s Digambar oleh %s construction speed Kecepatan pembangunan Construction_Btn Biaya pembangunan Consumed Dihabiskan convoi %d of %d Konvoi %d dari %d convoi error tooltips keterangan kesalahan konvoi Convoi has been sent\nto the nearest depot\nof appropriate type.\n Konvoi telah dikirim\nke depot terdekat\nsesuai dengan jenisnya.\n Convoi is sold when all wagons are empty. Kendaraan akan dijual setelah isinya kosong. convoi mouseover tooltips keterangan konvoi dengan mouse convoi passed last\nmonth %i\n \nkonvoi yang lewat\nbulan lalu: %i\n Convois Konvoi Convois: %d\nProfit: %s Kendaraan: %d\nKeuntungan: %s Convoys Konvoi Copy Convoi Salin konvoi Copy the selected convoi and its schedule or line Salin konvoi yang dipilih beserta rute/jadwalnya cost for removal Biaya untuk menghapus Cost: %8s (%.2f$/km %.2f$/m)\n Harga: %s (%.2f$/km %.2f$/m)\n Cost: %8s (%.2f$/km)\n Biaya: %8s (%.2f$/km)\n Costs Biaya Create a new line based on this schedule Buat rute baru berdasarkan jadwal ini curiosity builder Pembangun tempat wisata curlist_title Daftar tujuan turis Currently playing: Sedang bermain: Customers live in: Pelanggan tinggal di: deactivated in online mode Tidak aktif saat online Deccelerate time Perlambat waktu December Desember decrease underground view level kurangi tingkat tampilan bawah tanah Del Stop Hapus Delete Line Hapus rute Delete the current stop Hapus pemberhentian ini Delete the selected line (if without associated convois). Hapus rute yang dipilih (jika tidak ada konvoi di dalamnya) Delete this file. Hapus berkas ini. Delivered Terkirim Demand Dibutuhkan Demand: %.0f MW Dibutuhkan: %.0f MW\n Denkmal Monumen Departed Berangkat Departure board Waktu keberangkatan Departures to\n Keberangkatan ke\n Depots Depot Der Tunnel ist nicht frei!\n Terowongan tidak kosong.\n Destination Kedatangan Destroying map ... Menghapus peta yang lalu... Details Detil Die Bruecke ist nicht frei!\n Jembatan tidak kosong.\n Direkt erreichbare Haltestellen Rute langsung dari sini: disable midi Matikan Musik Display settings Pengaturan tampilan Distance Jarak Dock Galangan Kapal Dock must be built on single slope! Dermaga harus dibangun di tanah miring. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Anda memiliki %d bulan untuk membayar utang. Durchsatz Tingkat Produksi: Economy Ekonomi dan kota Eigenbesitz\n Fasilitas umum\n Ein %s\npasst hier nicht.\n '%s'\ntidak dapat diletakkan di sini.\n Einstellungen aendern Pilihan electric listrik Electricity Listrik Electricity producer\n\n Penyedia listrik\n\n Electrics_tab Listrik Electrify track Membuat rel listrik enlarge map Memperbesar peta enter a value between %i and %i masukkan nilai antara %i dan %i Enter address Masukkan alamat Enter Password Ganti nama pemain dan kata sandi Erzeuge neue Karte.\n Silakan tunggu.\nPeta sedang dibuat.\n\n(Proses dapat berlangsung beberapa\nmenit untuk peta yang besar)\n Es wird bereits\nein Fahrplan\neingegeben\n Jadwal sedang dibuat.\nSelesaikan terlebih\ndahulu sebelum\nmenjadwal ulang.\n Fabrikanschluss Pabrik yang terhubung Fabrikname nama pabrik Factories Pabrik factory details Detil pabrik factorybuilder Bangun pabrik Fahrplan Jadwal Fahrtziel Tujuan: Fahrzeuge koennen so nicht entfernt werden Kendaraan tidak dapat\ndihapus dengan cara ini.\n Fahrzeuge: Kendaraan: Farbe Pilihan Warna Fast forward Percepat waktu February Febuari Ferry_tab Feri Fertig Selesai Filename Nama berkas: Files from: Berkas dari: Finances of %s Keuangan %s Finanzen Kondisi Keuangan find mismatch Bandingkan pak fl_title Daftar pabrik Flug_tab Pesawat penumpang follow me Ikuti ini Follow the convoi on the map. Ikuti konvoi di peta Forest Hutan Found new city bangun kota yang baru Fracht Barang Frame time: Frame Time: Free Capacity Kapasitas kosong freeplay mode Permainan bebas Friction: Faktor gesekan: fuel_cell fuel cell Full load Muatan minimum: Fundament Pondasi Fussgaenger Pejalan kaki Game info Informasi permainan GAME PAUSED Permainan berhenti sementara Game_msg Umum Gear: Gigi: Gebaeude Bangunan General Umum Generated Dibuat Generation: %.0f MW Dihasilkan: %.0f MW\n Gewicht Berat Gewinn Pendapatan: Give the selected vehicle(s) an individual schedule Ubah rute/jadwal setiap kendaraan yang dipilih gl_btn_sort_catg Kategori gl_title Daftar barang go home Ke depot Goods Barang Goods AI Pemain komputer (barang) Goods list Daftar barang Gross Profit Cash Flow Groundobj Objek Grow city Perbesar kota Growth Pertumbuhan kota H halte Happy Senang Haus kaufen Beli rumah Helligk. Tampilan Help Bantuan Help text not found Teks bantuan tidak ada. hide all building Sembunyikan semua bangunan hide city building Sembunyikan bangunan kota hide objects under cursor Sembunyikan objek dekat pointer mouse hide station names Sembunyikan nama stasiun hide transparent Tembus pandang, bukan disembunyikan hide trees Sembunyikan pohon Hier warten/lagern: Barang/penumpang yang menunggu: Higher transport fees, crossconnect all factories Biaya angkut lebih tinggi, matikan just-in-time Highlite schedule Tampilan pemberhentian yang ada pada jadwal hl_title Daftar Stasiun hl_txt_filter Filter: hl_txt_sort Disusun menurut: hlf_chk_airport Bandara hlf_chk_anleger Pelabuhan hlf_chk_bahnhof Stasiun Kereta Api hlf_chk_bushalt Pemberhentian Bus hlf_chk_frachthof Tempat pengangkutan hlf_chk_keine_verb tidak ada jaringan hlf_chk_maglevstop Stasiun Maglev hlf_chk_monorailstop Stasiun monorel hlf_chk_name_filter Nama filter: hlf_chk_narrowgaugestop Stasiun rel sempit hlf_chk_overflow Terlalu ramai hlf_chk_spezial_filter Filter khusus: hlf_chk_tramstop Halte trem hlf_chk_type_filter Jenis filter: hlf_chk_waren_abgabe Barang keluar: hlf_chk_waren_annahme Barang masuk: hlf_title Filter daftar stasiun Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depot tidak ditemukan.\nKendaraan harus dikirim ke\ndepot secara manual. Homeless Tunawisma hydrogene Hidrogen Idle: Diam: ignore climates abaikan iklim In the industry legend show only currently existing factories Hanya menampilkan pabrik yang ada In Transit Sedang transit Increase Industry density Tambah kepadatan industri increase underground view level Tambah tingkat tampilan bawah tanah industrial building Bangunan industri Init map ... Menyiapkan peta... Input Kebutuhan Ins Stop Sisipkan Insert stop before the current stop Sisipkan pemberhentian sebelum pemberhentian yang dipilih Intercity road len: Panjang jalan antarkota: Intro. date: Tanggal kemunculan: invalid tidak benar. Invalid coordinate Urutan salah isometric map Tampilan isometrik January Januari join game Bermain online July Juli Jump to Pergi ke June Juni Kann Spielstand\nnicht laden.\n Data permainan tidak dapat dibuka. Kann Spielstand\nnicht speichern.\n Berkas yang dituju\ntidak dapat\ndiubah. Kein Besitzer\n Tanpa pemilik\n keine tidak ada Keine Einzelfahrzeuge im Depot Tidak ada kendaraan yang disimpan di sini Keyboard_Help\n Bantuan papan ketik\n koord kordinat Kreuzung Perlintasan labellist_title Daftar penanda Lade Relief Peta Buatan Laden Load Land attraction Tempat wisata luar kota Land industries Rantai industri: LANG_CHOOSE\n Pilih bahasa\nyang disukai:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Bulan lalu: Last Year Tahun lalu: Leaving depot! Meninggalkan depot leer kosong Legend Simbol peta Leistung Daya Leistung: %d kW Daya: %d kW Leitung Kabel listrik letzen Monat: diesen Monat: bulan lalu: bulan ini: Line Rute Line Management Pengelolaan rute Lineless convoys serving this stop Konvoi tanpa rute yang melayani pemberhentian ini Lines serving this stop Rute yang melayani pemberhentian ini LKW_tab Truk Load game Buka permainan load height data from file Memuat data ketinggian dari berkas. Load scenario Buka skenario loaded dimuat loaded passenger/freight Urutkan penumpang/kargo Loading (%i->%i%%)! Terisi (%i->%i%%) Loading addon paks ... Sedang Memuat Addons ... Loading map ... Sedang memuat peta Loading paks ... Sedang memuat file utama ... Loading skins ... Sedang memuat skin ... Lock game Mencegah ganti pemain (membutuhkan konfirmasi) Lokomotive_tab Lokomotif maglev vehicle Maglev maglev_track Rel Maglev Maglevdepot Depot maglev Mail Demand %d\n Kebutuhan pos %d\n Mailbox Pesan Mailbox Options Pilihan pesan Maintenance Perawatan make stop public (or join with public stop next) costs %i per tile and level menjadikan pemberhentian umum (atau bergabung dengan pemberhentian umum di sebelahnya) biaya %i$ setiap kotak dan tingkat Manual (Human) Manual (Manusia) Manufactured: Dibuat: Map roughness Kerumitan Peta: map zoom Perbesar Peta March Maret Margin (%%) Marjin Marker Meletakkan penanda max maks. Max Boost (%%) Peningkatan maks.(%%) Max income: Pendapatan Max: Max. speed: Kecepatan Max: Maximum 254 stops\nin a schedule!\n Paling banyak 254\npemberhentian dalam jadwal\n maximum length of rivers Panjang sungai maks. Maximum tile height difference reached. Perbedaan tinggi\nmaksimum antara\n2 kotak\ntelah tercapai. Maxspeed Kecepatan maksimum May Mei Median Citizen per town Penduduk kota rata-rata: Meldung Pesan Menge jumlah MessageOptionsText \nTahun baru\n\nBerita pemain\n\nBerita kota\n\nTidak ada jalan\n\nTujuan baru\n\nBincang\n\nKendaraan baru\n\nStasiun penuh\n\nMasalah\n\nKemacetan\n\nSkenario min min. minimum length of rivers Panjang sungai min. Missing pakfiles Objek tidak ditemukan. Modify the selected line Ubah rute yang dipilih Monate alt bulan (usia) Monorail Monorel monorail vehicle Kendaraan Monorel monorail_track Rel monorel Monorailboden Substruktur Monorel Monoraildepot Depot monorel month wait time waktu tunggu (bulan) Months Bulan Monument Monumen Monuments Monumen Mountain height Tinggi gunung: Movingobj objek bergerak Music playing disabled/not available Musik dimatikan/tidak tersedia Music volume: Tingkat suara musik: mute sound Matikan suara Name Nama Narrowgauge Rel sempit Narrowgauge are not available yet! Kereta rel sempit belum tersedia. narrowgauge vehicle Kereta rel sempit narrowgauge_track Rel untuk kereta rel sempit Narrowgaugedepot Depot rel sempit Net ID: %p ID jaringan: %p\n Net Wealth Ekuitas Net wealth near zero Kekayaan bersih mendekati nol Neue Karte Permainan baru Neue Welt Buat permainan baru new convoi Konvoi baru New Line Rute baru New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Rute baru telah dibuat.\nRute dapat digunakan dengan\nmemilihnya dari\npilihan rute di atas. New Vehicles Kendaraan baru Nickname: Nama panggilan: no buildings hidden tampilkan gedung no convois Tak ada iringan No goods are loaded onto this convoi. Kendaraan ini bukan untuk mengangkut barang jenis ini no goods waiting Tak ada penumpukan brg no load Tidak memuat No Route Tidak ada jalan ke tujuan No stop here! Harus ditempatkan di pemberhentian! No suitable ground! Tidak ada tanah yang sesuai. No terminal station here! Stasiun/terminal tidak dapat dibangun\ndi sini. Hanya dapat dibangun\ndi ujung rel/jalan. no timeline semua masa no tree Tanpa pohon No. of Factories Pabrik dan toko Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Konvoi ini harus memiliki jadwal atau rute perjalanan sebelum dapat dijalankan. nord Utara nordost Timur laut nordwest Barat laut Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Jadwal tidak dapat diubah\nsaat sedang mencari jalur.\nCoba sekali lagi. Not enough fields would remain. Lahan yang tersisa untuk\nladang ini tidak cukup. Now active as %s.\n Sekarang aktif sebagai %s.\n Number of rivers Jumlah sungai Object Objek Odometer: %s km Sudah ditempuh: %s km Ok OK On loan since %i month(s) Meminjam uang sejak %i bulan On this map, you are not\nallowed to change player!\n Permainan terkunci.\nSemua pemain tidak dapat mengubah apapun.\n Only city chains Bangun rantai industri dalam kota Only first %d differing paks reported. There are probably more. Hanya %d pak berbeda pertama yang dilaporkan. Mungkin masih ada lagi. Only land chains Bangun rantai industri luar kota Only one transformer per factory! Hanya satu transformer untuk setiap pabrik. Only show goods which are currently handled by factories Hanya menampilkan barang yang saat ini ditangani oleh pabrik open buka Operation Biaya operasional Ops Profit Keuntungan operasional Optionen Opsi Or enter a server manually: Atau masukkan server secara manual: Origin Asal ost Timur Output Hasil Ownership Kepemilikan Pak which may cause severe errors: Objek (pak) berikut tidak ditemukan, hal ini dapat menyebabkan masalah pada rantai transportasi: Pak which may cause visual errors: Objek (pak) berikut diganti dengan objek yang mirip/serupa: Pak(s) different: Pak yang berbeda: Pak(s) missing on client: Pak yang tidak Anda miliki: Pak(s) not on server: Pak yang tidak ada di server: Pakset differences Perbedaan pakset paletten peti/kotak Pas_tab Kereta penumpang Passagiere Penumpang Passagierrate Jumlah Pengunjung Passagierziele Tujuan penumpang/pos Passenger AI Pemain komputer (penumpang) Passenger Demand %d\n Permintaan penumpang %d\n Passengers %d %c, %d %c, %d no route Penumpang %d %c, %d %c, %d tidak bisa ke tujuan Passengers %d %s, %d %s, %d no route Penumpang %d %s, %d %s, %d tidak bisa ke tujuan Password Kata sandi: Pause Berhenti sementara Pax <%i> Mail <%i> Penumpang <%i> Pos <%i> PaxDest Tujuan Percent Electricity Listrik dihasilkan (%% dari permintaan): Planes are not available yet! Pesawat terbang belum tersedia. Plant tree Tanam pohon player Pemain player -1 Pemain manusia player 0 layanan umum player 1 pemain 1 player 10 pemain 10 player 11 pemain 11 player 12 pemain 12 player 13 pemain 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 pemain 4 player 5 pemain 5 player 6 pemain 6 player 7 pemain 7 player 8 pemain 8 player 9 pemain 9 Please choose vehicles first\n Pilih kendaraan terlebih dahulu\n Post Pos Postrate Jumlah Pos Power Daya Power (MW) Daya (MW) Power: Daya: Power: %4d kW\n Daya: %4d kW\n Powerlines Kabel listrik Problems_msg Masalah Produced Dibuat Production of %s has been stopped:\n%s\n Pembuatan %s telah dihentikan:\n%s\n Production/Boost Produksi/peningkatan Produktion Produksi Profit Keuntungan promote to line Masukkan ke rute q1 Musim semi q2 Musim panas q3 Musim gugur q4 Musim dingin Query server Panggil server rail car gerbong random acak Random age Tahun acak Random map Peta acak Rathaus Balai kota Rating Penilaian ratio_pax Perbandingan penumpang Relevant Yang diperlukan Reliefkarte Peta Remove Hapus remove airstrips Hapus landasan/jalan pesawat remove channels Hapus Kanal remove interm. signals Hapus sinyal di tengah remove maglev tracks Hapus rel maglev remove monorails Hapus monorel remove narrowgauge tracks Hapus rel sempit remove powerlines Hapus kabel listrik remove roads Hapus jalan remove tracks Hapus Rel Remove wayobj %s Hapus objek %s replace other signals Ganti sinyal lainnya replace stop Tukar pemberhentian request closing permintaan penutupan residential house Rumah penduduk Restore natural slope Kembalikan ke kemiringan alami Restwert: Nilai Jual: Retire. date: Tanggal tidak dibuat lagi: return ticket arah sebaliknya Revenue Laba Kendaraan Revision: Revisi road Jalanan road vehicle Kendaraan Roadsign Rambu-Rambu Rotate map Putar peta Rotation Perputaran Routing Pencarian arah sack bungkus/sak sail angin Saving map ... Menyimpan peta... Scenario complete: %i%% Skenario selesai: %i%% Scenario Debug Debug Scenario Error Log Kesalahan pada skrip Scenario Goal Tujuan skenario Scenario Info Keterangan singkat Scenario information Keterangan lengkap skenario Scenario Result Perkembangan Scenario Rules Peraturan Schedule changing! Perubahan jadwal Schienentunnel Bangun terowongan kereta Schiff_tab Kapal Schiffdepot Galangan kapal Schleppkahn_tab Kapal tongkang Screenshot Potret layar Seasons Musim Sehenswuerdigkeit Tempat wisata Select a server to join: Pilih server untuk bergabung: Select a theme for display Pilih tema untuk ditampilkan Sell the selected vehicle(s) Jual kendaraan yang dipilih. sended Pos terkirim SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 Server did not respond! Server tidak dapat dihubungi. Serves Line: Melayani rute: Service Layanan set signal spacing Atur jarak antarsinyal Setting Pengaturan Ship Kapal shops and stores Toko dan kantor Show all Tampilkan semua show all building Tampilkan semua bangunan Show also vehicles no longer in production. Tampilkan juga kendaraan yang sudah tidak dibuat Show also vehicles that do not match for current action. Tampilkan juga kendaraan yang tidak sesuai digunakan saat ini. Show even servers with wrong version or pakset Tampilkan juga server yang menggunakan versi/pakset yang berbeda. show grid Tampilkan kotak-kotak Show industry Daftar industri Show legend Pilihan Show map scale Kode warna Show mismatched Tampilkan yang tidak cocok Show obsolete Tampilkan yang tua/kuno Show offline Tampilkan yang offline Show only used Tampilkan hanya yang digunakan Show schedules Tampilkan jadwal Show servers that are offline Tampilkan server yang mati Show servers where game version or pakset does not match your client Tampilkan server yang pakset/versinya tidak cocok dengan yang Anda gunakan show station coverage Tampilkan jangkauan stasiun show station names Tampilkan nama stasiun show waiting bars Tampilkan jumlah tunggu show/hide block reservations Tampilkan blok jalur yang digunakan Show/hide estimated arrival times Menampilkan perkiraan waktu kedatangan show/hide object owner Tampilkan pemilik benda Show/hide statistics Tampilkan statistik Shows buttons on special topics. Tampilkan tombol simbol peta Shows consumer/suppliers for factories Tampilkan pelanggan dan pemasok pabrik Shows the color code for several selections. Tampilkan kode warna untuk banyak pilihan Shows the currently selected schedule Tampilkan jadwal yang dipilih Shrink city Perkecil kota shuffle midis acak urutan MIDI Signal Sinyal signal spacing Jarak antarsinyal Sim: Simloops: Similar view as the main window Tampilan mirip dengan jendela utama Size (%d MB): Luas Peta sliced underground mode Tampilan atas+bawah tanah slot empty kosong Smart hide objects Sembunyikan objek secara otomatis Sort by urutkan Sort waiting list by Urutkan daftar tunggu Sound Suara Sound settings Pengaturan suara Sound volume: Tingkat keras suara: special freight Kargo khusus Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Bonus kecepatan (persentase)\nkecepatan minimum yang dibutuhkan: jalan %i km/h,\nrel %i km/h, kapal %i km/h, pesawat %i km/h Speedlimit Batas kecepatan Speichern Save Spieler Pemain Spieler(mz) Pemain Spielerliste Daftar Pemain Spielstand wurde\ngeladen!\n \nPermainan berhasil dibuka.\n Spielstand wurde\ngespeichert!\n \nPermainan berhasil disimpan.\n Sprache Bahasa Sprachen Bahasa Stadtinformation Statistik kota Start Mulai Start the selected vehicle(s) Menjalankan kendaraan yang dipilih. Starte Spiel Mainkan! Station tiles: Kotak stasiun: Station_msg Stasiun Status Keadaan steam uap Step timeline one year Maju satu tahun Stops Pemberhentian Storage Penyimpanan Storage capacity Daya tampung penyimpanan Strassendepot Garasi Strassentunnel Bangun terowongan jalan street car mobil di jalan sued Selatan suedost Tenggara suedwest Barat daya Summer snowline Garis salju musim panas Supplied: %.0f %% Dipasok: %.0f %% Suppliers Pemasok Tage alt hari (usia). There are still vehicles\nstored in this depot!\n Masih ada kendaraan\ndi depot ini.\n This Month Bulan ini: This Year Tahun ini: Tile not empty. Kosongkan kotak sebelum\nmengubah kemiringan/ketinggian. timeline sesuai jaman tl_title Daftar semua kota To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Lalu lintas di %1$s meningkat.\n%3$i pembayar pajak membuat pembangunan\n%2$s menjadi kenyataan. To heavy traffic\nresults in traffic jam.\n Lalu lintas padat\nmenyebabkan kemacetan.\n Toggle day/night view Ubah tampilan siang/malam Toggle vehicle tooltips Tampilkan keterangan kendaraan saat disorot tonnen Ton Total inhabitants: Jumlah penghuni: Tourist attractions Tempat wisata: Tourists Tempat menarik Town_msg Tujuan baru Town: %s\n Kota %s.\n Towns Kota track rel Tracks Rel Traffic Lalu lintas Train Kereta Trains are not available yet! Kereta belum tersedia. Tram Trem tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. trem %i km/h, monorel %i km/h\nmaglev %i km/h, rel sempit %i km/h. tram_track rel trem Tramdepot Depot trem Trams are not available yet! Trem belum tersedia. Transferring game ... Memindahkan permainan... Transformer only next to factory! Transformer harus di\ntanah kosong dan rata\ndi sebelah pabrik. Translation Terjemahan transparent station coverage jangkauan stasiun tampil tembus pandang Transported Perjalanan TrolleyBus_tab Busway Truck Truk tt_Other Lain-lain Tunnel muss an\neinfachem\nHang beginnen!\n Terowongan harus\ndimulai di\nkemiringan lurus.\n Tunnel must start on single way! Terowongan harus dimulai di jalan tunggal. Tunnelboden Terowongan underground mode tampilan bawah tanah UNDO failed! Tidak dapat dibatalkan lagi.\nPembatalan hanya dapat\ndilakukan jika tidak ada\nsinyal/stasiun/pemberhentian\natau apapun yang dibangun\ndi jalan. Undo last ways construction Batalkan pembangunan jalan terakhir Unemployed Pengangguran Unhappy Tidak senang units/day per bulan\n Update Line Ubah rute upgrade HQ Perbaharui kantor pusat Usage: %.0f %% Pemakaian: %.0f %% Usage/Output Pemakaian/hasil Use beginner mode Menggunakan cara pemula Use timeline start year Mulai pada tahun: Vehicle %s can't find a route! Kendaraan %s tidak\nmenemukan jalan/jalur. Vehicle %s is stucked! Kendaraan %s tersangkut. Vehicle details Rincian kendaraan Verbrauch Konsumsi Vergroessere die Karte\n Perbesar peta.\n Verkauf Hapus verkaufen Jual Verkehrsteilnehmer Mobil kota Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Anda memiliki %d bulan\nuntuk membayar utang.\n via melewati(rincian) via %s\n lewat %s\n via Menge melewati(jumlah) voranstellen Taruh di depan Waggon_tab Gerbong waiting menunggu Waiting for clearance! Menunggu jalan kosong. Walked Berjalan kaki Warnings_msg Lalu lintas Wasser Air Water Kanal Water level Tinggi air: water vehicle kendaraan air way %s cannot longer used:\n Jenis jalan %s tidak dapat digunakan lagi.\n way %s cannot longer used:\n%s\n jenis jalan %s tidak dapat digunakan lagi:\n way %s now available:\n Jenis jalan %s tersedia sekarang.\n Way toll Tarif tol Ways not connected Jalan tidak terhubung Wegpunkt Titik arah Weight: Berat: Wert Nilai west Barat Winter snowline Garis salju musim dingin withdraw Tarik kembali Withdraw All Tarik kembali semua WRONGSAVE \nData permainan tidak sesuai.\nBerkas tidak dapat dimuat.\n Year %i has started. Tahun %i telah dimulai. Years Tahun Your primary color: Warna utama Anda: Your secondary color: Warna kedua Anda: Zielort tujuan zooming in lihat lebih dekat zooming out lihat lebih jauh Zu nah am Kartenrand Anda tidak dapat\nmembangun di dekat\npinggiran peta. #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Melayang di atas magnet dengan\n%.1f km/h oleh %s:\nRekor baru! New world record for monorails: %.1f km/h by %s. MHX NEWS: Rekor kecepatan di dunia Monorel terpecahkan dengan kecepatan: %.1f km/h oleh %s. Selamat kepada Human, silahkan hubungi forums.simutrans.com untuk berbagi kisah anda dalam bermain game ini. New world record for motorcars: %.1f km/h by %s. MHX NEWS: Rekor kecepatan di dunia Jalan Darat terpecahkan dengan kecepatan: %.1f km/h oleh %s. Selamat kepada Human, silahkan hubungi forums.simutrans.com untuk berbagi kisah anda dalam bermain game ini. New world record for narrowgauges: %.1f km/h by %s. Rekor baru untuk kereta rel sempit: %.1f km/h oleh %s. New world record for planes: %.1f km/h by %s. MHX NEWS: Rekor kecepatan di dunia Pesawat Terbang terpecahkan dengan kecepatan: %.1f km/h oleh %s. Selamat kepada Human, silahkan hubungi forums.simutrans.com untuk berbagi kisah anda dalam bermain game ini. New world record for railways: %.1f km/h by %s. MHX NEWS: Rekor kecepatan di dunia Kereta Api terpecahkan dengan kecepatan: %.1f km/h oleh %s. Selamat kepada Human, silahkan hubungi forums.simutrans.com untuk berbagi kisah anda dalam bermain game ini. New world record for ship: %.1f km/h by %s. MHX NEWS: Rekor kecepatan di dunia Kapal Laut terpecahkan dengan kecepatan: %.1f km/h oleh %s. Selamat kepada Human, silahkan hubungi forums.simutrans.com untuk berbagi kisah anda dalam bermain game ini. simutrans-124.5/simutrans/text/id/000077500000000000000000000000001520675460500171715ustar00rootroot00000000000000simutrans-124.5/simutrans/text/id/general.txt000066400000000000000000000061221520675460500213500ustar00rootroot00000000000000Simutrans Help

General Help

- Welcome
- Game Interface
- Mouse Help (to be written)
- Keyboard Help
Game Options
- Language
- Player Colour
- Display Settings
- Sound & Music
- Players
- Load Game
- Save Game
- New Game
- - Climate Control
- - Load Height Map

Main Menu
- Game Options
- Map
- Inspection Tool
- Landscaping Tools Tools
- Railroad/Train Tools
- Monorail Tools
- Tram Tools
- Road Tools
- Harbour Tools
- Airport Tools
- Special Construction Tools
- Game Edit Tools
- Destroy/Remove

?Management Menu
- Line Management
- Lists
- - Stops/Station List
- - - Stops/Station List Filter
- - Vehicle List
- - - Vehicle List Filter
- - City List
- - Goods List
- - Industry List
- - Attractions List
- Message Centre
- Finances

Other Options
- Save Screenshot
- Pause
- Fast Forward
- Help

Other Dialogs
-Depot Controls
-Schedule Controls
- Stop Information
- - Stop Details
- Convoy/Vehicle Information
- - Convoy/Vehicle Details
- City Information
- Industry Information

If no help is found here, try
http://guide.simutrans.com, or
http://wiki.simutrans.com, or
http://simutrans-tips.com, or
http://forum.simutrans.com.

simutrans-124.5/simutrans/text/id/simutrans.txt000066400000000000000000000047521520675460500217670ustar00rootroot00000000000000About Simutrans

Welcome to Simutrans!

First, select a map: either choose one of Simutrans' thousands of maps or create a new one from a height map. Then put yourself in the shoes of a young entrepreneur with some money from your grandparents: eager to make them proud, with ambitions of establishing a successful transport company, your challenge now is to build a prosperous transport empire.

As time passes, develop your company into a successful enterprise, whilst at the same time keeping the economy running smoothly by delivering freight & satisfying the travel requirements of millions of passengers. You can transport passengers, mail and goods by rail, road, ship and even air.

Every map has something different to offer. Take a good look at your surroundings: their future lies in the hands of you and your competitors.

You may find it easiest to begin by supplying a power station with the goods it needs. With the money you've made, gradually expand by supplying other factories - maybe even using central hubs where goods are reloaded. The disadvantage of only transporting goods is that towns and cities won't grow and new industries will not appear.

Alternatively, you may immediately go for passenger (and mail) transport. This is more challenging, as the inhabitants of the Simutrans world all wish to travel to specific destinations Some need to go to factories to work, some want to go shopping in the city centre, and others want to visit tourist attractions. But if you don't offer a connection to their desired destination, passengers will not travel with you at all.

You will need to build a transport network where as many popular destinations as possible are reachable. When your network is established, you will need to continuously upgrade it to keep abreast of demand, so plan ahead! If passengers like your service, towns will grow, new industries will be established, and only then will the economy run smoothly.

You can play as long as you like. Start in 1880 and finish in 2050, or set a start time anywhere between these dates. There will be new vehicles and buildings throughout this time. Play against the AI or on your own. But watch out: if your net wealth is negative for three months in a row, you will go bankrupt and the game is over. Check your net wealth in the Finances window.

The Simutrans Team and the community wish you an enjoyable time playing Simutrans!

simutrans-124.5/simutrans/text/it.tab000066400000000000000000001430441520675460500177070ustar00rootroot00000000000000§Italiano PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: it Italiano # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable Inattivo cl_btn_filter_enable Attivo cl_btn_filter_settings Proprietà cl_btn_sort_asc Ascend. cl_btn_sort_desc Discend. cl_btn_sort_id ID interno cl_btn_sort_income Incasso cl_btn_sort_name Nome cl_btn_sort_type Tipo clf_btn_alle tutti clf_btn_invers inv. clf_btn_keine ness. Find matching convois Trova convogli corrispondenti gl_btn_sort_bonus per bonus gl_btn_sort_name per nome gl_btn_sort_revenue per incassi gl_btn_unsort non ordinato hl_btn_filter_disable Inattivo hl_btn_filter_enable Attivo hl_btn_filter_settings Proprietà hl_btn_sort_asc Ascend. hl_btn_sort_desc Discend. hl_btn_sort_name Nome hl_btn_sort_type Tipo hl_btn_sort_waiting Attesa hlf_btn_alle tutti hlf_btn_invers inv. hlf_btn_keine ness. humidities umidità Lake Lago Maximize height levels Massimizza livelli altezza Networks Reti Open Sea Mare Aperto Queueing Accodamento rainfall pioggia Return to menu Torna al menù Road toll Casello stradale sea mare Start this as a server Avvia come server Transfers Trasferimenti #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic clima artico desert clima desertico mediterran clima mediterr. rocky roccioso temperate temperato tropic clima tropicale #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Caricamento dello scenario fallito Bridge blocked by way below or above. Ponte bloccato da strada sopra o sotto. Can't buy obsolete vehicles! Non puoi comprare veicoli obsoleti! Cannot alter water Non puoi modificare l'acqua Cannot build on a double slope! Non è possibile costruire su una doppia pendenza! Cannot built depot here! Impossibile costruire deposito qui. Cannot built this station/building\nin underground mode here. L'edificio non può\nessere costruito sotto terra.\n Cannot connect to the\ncenter of a double slope! Impossibile connettersi al\ncentro di una doppia pendenza! Cannot create generic line!\nSelect line type by\nusing filter tabs. Impossibile creare linea generia!\nSelezionare tipo linea\nusando i filtri. Cannot create socket Non si può creare il raccordo Cannot rotate this building! Impossibile ruotare questo edificio! Client closed connection during transfer Il client ha chiuso la connessione durante il caricamento Convoi handles exhausted! Massimo numero di convogli raggiunto Convoy already deleted! Convoglio già cancellato! Could not open file Impossibile aprire il file Das Feld gehoert\neinem anderen Spieler\n Questo terreno\nè di proprietà\ndi un altro giocatore!\n Der Besitzer erlaubt das Entfernen nicht Il proprietario non\nconcede il permesso\ndi rimozione.\n Diese Zusammenstellung kann nicht fahren!\n Questa combinazione\nnon può viaggiare!\n Flugzeughalt muss auf\nRunway liegen!\n I velivoli devono\nstare su taxiway. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Non puoi costruire\n\nun terminal qui.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Impossibile mettere QUI\ndei segnali ferroviari!\n In order to lock the game, you have to protect the public player by password! Per bloccare il cambio giocatore devi proteggere il giocatore pubblico con una password! Loading a new game will end the current server session! Caricare un nuovo gioco terminerà la sessione del server corrente! Lost connection\nto server! Connessione persa con il server! Lost synchronisation\nwith server. Persa la sincronizzazione con il\n\nserver! Maglevhalt muss auf\nMaglevschiene liegen!\n Una fermata per maglev deve essere su una linea maglev! Monorailhalt muss auf\nMonorail liegen!\n Le fermate devono\nstare su una rotaia. Monorails are not available yet! Morotaie non ancora disponibili. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n una fermata per scartamento ridotto deve stare su una linea a scartamento ridotto No bridges over runways! Impossibile costruire ponti sopra le piste di decollo! No curves on runways Curve non ammesse su piste di decollo No suitable crossing Nessun incrocio adatto No suitable way on the ground! Serve un percorso No through station here! Non c'è nessuna stazione passante qui! Not enough clearance. Spazio insufficiente. Not enough money! Non hai abbastanza soldi per costruirlo! On narrowgauge track only!\n Solo su linee a scartamento ridotto!\n Only public player can lock games! Solo il giocatore pubblico può bloccare i giochi! Out of funds Denaro esaurito Post muss neben\nHaltestelle\nliegen!\n Un ufficio postale\ndeve essere ubicato vicino\nad una fermata/stazione!\n Protocoll error (expecting game) Errore nel protocollo (aspettando il gioco) Schiffhalt muss im\nWasser liegen!\n Le fermate delle barche\npossono essere piazzate\nsolo sull'acqua\nvicino ad un attracco!\n Server busy Server sovraccarico Server version too new Aggiorna la versione del gioco, quella del server è avanzata. Terraforming not possible\nhere in underground view Il terraforming è impossibile sottoterra. This tunnel branches. You can try Control+Click to remove. Questo tunnel si ramifica. Puoi provare Control+click per rimuovere. Too far away to merge stations! Stazioni troppo lontane per unirle! Upgrade must have\na higher level L'aggiornamento deve essere a un livello superiore Vehicle %s cannot choose because stop too short! Il veicolo %s non può scegliere, fermata troppo corta! Zughalt muss auf\nSchiene liegen!\n Le fermate ferroviarie\npossono essere piazzate\nsolo sui binari!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Keyboard Help\n

Keyboard Help

\n Aiuto tastiera\n

Aiuto tastiera

\n

\nAiuto tastiera Mostra le funzioni per vari tasti premuti sulla tastiera\n

\n

\nAiuto tastiera si apre quando viene premuto un tasto non assegnato o da Aiuto generale.\n

\n

\nLe maiuscole sono diverse dalle minuscole (usare [Shift] per lettere maiuscole).\n

\n

\nTasti con le funzioni assegnate includono: \n

\n

\n[Tasti freccia]: muovi la vista nel senso della freccia.
\n[Backspace]: chiude tutte le finestre,barre e testi d'aiuto nella vista.
\n[Cancella], o [Esc]: chiude la finestra,barra o aiuto in primo piano sulla vista.
\n[Enter], or [Return]: usato per confermare azioni.
\n[Pagina su], o [>]: aumenta zoom.
\n[Pagina giù], o [simbolo "minore di"]: diminuisce zoom.\n[F1]: apre Aiuto Simutrans.

\n

\n[1]: muove la vista verso sud.
\n[2]: muove la vista verso sud-est.
\n[3]: muove la vista verso est.
\n[4]: muove la vista verso sud-ovest.
\n[6]: muove la vista verso nord-est.
\n[7]: muove la vista verso ovest.
\n[8]: muove la vista verso nord-ovest.
\n[9]: muove la vista verso nord.\n

\n

\n[Shift] + mouse: usato in Map per vedere collegamenti in Catena approvvigionamenti industrie.
\n[CTRL] + strumento: costruisce (segnali & fermate) al livello più alto; o costruisce strade più veloci sulle più lente; o più dritte (più dirette) strade e ferrovie.
\n[CTRL] + ([F2] fino a [F12]): seleziona lo strumento richiamato da [F2] a [F12].\n

\n

At least one stop is connected to the town. Almeno una fermata è collegata alla città. Decrease water height Diminuisci altezza acqua Highlight railroad tracks Evidenzia le ferrovie Highlite depots Evidenzia i depositi Highlite electrical transmission lines Evidenzia le linee elettriche Highlite factories Evidenzia le fabbriche Highlite forests Evidenzia le foreste Highlite tourist attraction Evidenzia le attrazioni turistiche Increase water height Aumenta altezza acqua Open station/stop details Apri dettagli stazioni/fermate Overlay city limits Sovrapponi i limiti delle città Overlay passenger destinations when a town window is open Sovrapponi le destinazioni dei passeggeri quando la finestra della città è aperta Overlay schedules/network Sovrapponi i percorsi/reti Overlay town names Sovrapponi i nomi delle città Please click on the map to add\nwaypoints or stops to this\nschedule. Click sulla mappa per aggiungere\npassaggi o fermate a questo\npercorso. Set tile climate %s Imposta il clima della casella %s Show capacity and if halt is overcrowded Mostra la capacità e se la fermata è sovraccarica Show how many convoi reach a station Mostra quanti convogli raggiungono una stazione Show how many people/much is waiting at halts Mostra quante persone e quanto tempo sono in attesa alle fermate Show initial passenger departure Mostra l'origine dei passeggeri Show level of city buildings Mostra il livello degli edifici cittadini Show mail service coverage/mail network Mostra la copertura del servizio postale / la rete postale Show passenger coverage/passenger network Mostra la copertura del servizio passeggeri / la rete passeggeri Show speedlimit of ways Mostra il limite di velocità delle strade Show the change of waiting at halts Mostra il cambio di attesa alle fermate Show the owenership of infrastructure Mostra il proprietario delle infrastrutture Show transported freight/freight network Mostra le merci trasportate / la rete merci Show usage of network Mostra l'utilizzo della rete Shows a listing with all industries on the map. Mostra la lista delle industrie sulla mappa Sum of departure/arrivals at halts Somma di partenze/arrivi alle fermate #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s ora\noffre servizio bus\ntra %s\ne l'attrazione\n%s\na (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s ora\noffre servizio bus\ntra %s\ne l'industria\n%s\na (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nora fa viaggiare\n%i camion tra\n%s a (%i,%i)\ne %s\na (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\ninaugura una nuova ferrovia\ntra %s\na (%i,%i) e\n%s\na (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Servizio aereo da\n%s\nadesso tra\n%s \ne %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Servizio navale tra\n%s\nadesso tra\n%s\ne%s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s apre un nuovo servizio bus tra %s e %s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Strumenti per modifica mappa LISTTOOLS Liste MAGLEVTOOLS Strumenti maglev MONORAILTOOLS Strumenti monorotaia/maglev NARROWGAUGETOOLS Strumenti linea a scartamento ridotto RAILTOOLS Strumenti ferrovia ROADTOOLS Strumenti strade SHIPTOOLS Strumenti porto SLOPETOOLS Movimenti terra SPECIALTOOLS Strumenti costruzione speciali TRAMTOOLS Strumenti tram/ferrovia leggera #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s ha costruito una nuova sede. Factory chain extended\nfor %s near\n%s built with\n%i factories. L'economia si sta allargando:\n%s vicino %s allarga la catena.\n%i nuove fabbriche sono state fondate Net wealth less than 10%% of starting capital! Ricchezza netta inferiore al 10%% del capitale iniziale! New %s now available:\n%s\n Nuovo %s ora disponibile:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Nuova rete d'industrie\nper %s vicino a\n%s costruita con\n%i industrie. New vehicle now available:\n%s\n \n Un nuovo veicolo è\n ora disponibile:\n\n\n -- %s --\n\n Now %u clients connected. Adesso sono connessi %i giocatori. overtaking sorpasso Remove vehicle from map. Use with care! Rimuovi veicolo dalla mappa.(Attenzione!) Screenshot\ngespeichert.\n Schermata\nsalvata.\n Sends the convoi to the last depot it departed from! Manda convoglio all'ultimo deposito da cui è partito Trees Alberi With a big festival\n%s built\na new monument.\n%i citicens rejoiced. Con una grande festa,\n%s ha costruito\nun nuovo monumento.\n%i cittadini festeggiano. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Filtro Linee #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (in deposito) /month /mese \nBauzeit bis \nfino al \nBauzeit von \nCompare dal \nCan't open heightfield file.\n \nFile dei rilievi non leggibile.\n \ndirection: \ndirezioni: \nelektrified \nelettrificata \nHeightfield has wrong image type.\n \nLa mappa dei rilievi è in un formato\ndi immagine errato.\n \nis reserved by: \nriservata da \nminimum speed: \nvelocità minima: \nnot elektrified \nnon elettrificata\n \nRibi (masked) \ndirezioni\m (mascherate): \nRibi (unmasked) \ndirezioni\n(visibili): \nSet phases: \nregola lunghezza ns/ew: \nsingle way \nSingola Via \nway1 reserved by via 1 risevata da\n \nway2 reserved by via 2 riservata da\n \nwith sign/signal\n \ncon cartello/segnale\n %d buildings\n %d palazzi\n %d convois %d convogli %d Einzelfahrzeuge im Depot %d veicoli in deposito %i car(s), %i auto, %i km/h (max. %ikm/h) %i km/h (max %ikm/h) %i years %i months old. ha %i anni e %i mesi. %s at (%i,%i) now public stop. %s (coordinate %i,%i) è divenuta fermata pubblica. %s building %s %s %s %s %s %s city %d %s %s città %d %s %s factory %s %s %s fabbrica %s %s %s has entered a depot. %s è in deposito. %s land %d %s %s terreno %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s ha speso\nsoldi per costruire un\nnuovo municipio\nquando ha raggiunto\n%i abitanti. %s\nis crowded. %s è congestionata! %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\nVelocità %i\nVelocità massima %i\ndx:%i dy:%i %s\nwas liquidated. %s\nè stato liquidato. %u Client(s)\n %i Connesso(i)\n %u Player (%u locked)\n %u Giocatore (%u bloccato)\n

Warning

addons for

Attenzione

addons per 1 convoi 1 convoglio 1 Einzelfahrzeug im Depot 1 veicolo in deposito 100 km/h = %i tiles/month 100 km/h = %i caselle/mese 1LIGHT_CHOOSE Luminosità: 1WORLD_CHOOSE Opzioni della nuova mappa: 2LIGHT_CHOOSE Colori: 2WORLD_CHOOSE Numero mappa: 3LIGHT_CHOOSE Vel. scrolling: 4LIGHT_CHOOSE Inverti scrolling 5LIGHT_CHOOSE Mostrare pedoni 5WORLD_CHOOSE Numero città: 6LIGHT_CHOOSE Pedoni in città 6WORLD_CHOOSE Densità traffico: 8WORLD_CHOOSE Modalità giorno e notte A bridge must end on a way! I ponti devono\nfinire su un\ntracciato esistente. A bridge must start on a way! I ponti devono\niniziare su un\ntracciato esistente. Abfrage Lente Abnehmer Clienti About Info About scenario Info scenario Abriss Distruggi/Rimuovi Absenken Abbassa terreno Abspanntransformator Trasformatore Accelerate time Accelera il tempo Act. load: %u MW\n Carico attuale:%u MW\n Active player only Solo giocatore attivo Add forest Aggiungi foresta Add random citycar Aggiungi auto casuale add server Aggiungi server Add Stop Aggiungi Add stops for backward travel Aggiungi fermate viaggio ritorno air pista di rullaggio Aircraft Aereo aircraft_tab Aerei merci airplane aeroplano Airport Aeroporto AIRTOOLS Strumenti aeroporto All Tutto all convoi tooltips Tutti i richiami dei convogli Allow city growth Permetti crescita città Allow player change Consenti cambio giocatore allowed climates:\n climi permessi Alters a schedule. Aggiungi/rimuovi fermate da percorso Angenommene Waren Prodotti necessari da impianti vicini anhaengen Aggiungi Anhaenger_tab Rimorchi Anheben Alza terreno Appends stops at the end of the schedule Aggiungi fermate alla fine del programma Apply Line Applica Linea April Aprile Arbeiter aus: Lavoratori da: Arrivals from\n Arrivi da\n Arrived Arrivati Aufloesen Disassembla Aufspanntransformator Trasformatore August Agosto Autohalt muss auf\nStrasse liegen!\n Le fermate dei veicoli\ndevono stare\nsu una strada. Available Disponibili Bahndepot Deposito treni Bankrott:\n\nDu bist bankrott.\n Bancarotta:\n\nSei fallito!\n battery Non esiste! Baum Albero baum builder Pianta alberi Baustelle Cantiere\nedile Bauzeit avanza nel tempo Beenden Esci Beginner mode Modalità principiante Besonderes Gebaeude Attrazione Turistica BF stazione bio biologico Blockstrecke ist\nbelegt\n Questo tratto di\nlinea è usato\nda un altro treno\n Boden Terreno Bonus Speed: %i km/h Bonus Velocità: %i km/h Bonusspeed: %i km/h Bonus velocità: %i km/h Boost (%%) Incremento (%%) bridge is too high for its type! Ponte troppo alto per il suo tipo Bridge is too long for this type!\n La campata è\ntroppo lunga per\nquesto tipo di ponte.\n Bruecke Ponte Bruecke muss an\neinfachem\nHang beginnen!\n Un ponte deve\niniziare su un\nterreno pianeggiante!\n Brueckenboden ponte Build air depot Costruisci deposito aerei build choosesignals Costruisci segnali di scelta Build city market Costruisci un mercato nella città più vicina. Build drain Trasformatore build HQ Costruisci sede Build land consumer Costruisci nuova stazione di potenza. Build maglev depot Costruisci deposito maglev Build monorail depot Costruisci deposito monorotaia Build narrowgauge depot Costruisci deposito per ferrovia scartamento ridotto Build powerline Costruisci linea elettrica Build presignals Costruisci presegnali Build road depot Costruisci deposito veicoli Build ship depot Costruisci deposito navale Build signals Costruisci segnale Build train depot Costruisci deposito treni Build tram depot Costruisci deposito tram Build truck depot Costruisci deposito veicoli Building costs estimates Stima costo costruzione Buildings Numero edifici Built artifical slopes Costruisci pendio artificiale Built random attraction Costruisci attrazione casuale Bus_tab Bus Can only move from halt to halt or waypoint to waypoint. Si può solo muovere\nda fermata a fermata o\n da punto a punto. Cancel Annulla Cannot connect to offline server! Impossibile connettersi, server offline! Capacity Capacità Capacity: Capacità: Capacity: %.0f MW Capacità: %.0f MW\n Capacity: %d%s %s\n Capacità: %3d %s %s\n Capacity: %s\nLoad: %d (%d%%) Capacità: %s\nCarico: %d (%d%%) Cars are not available yet! Automobili non ancora disponibili. cars.\nstate auto\n Cash Cassa Change player Cambia giocatore Chart Grafico Chat_msg Chat Choose direction Scegli direzione Choose operation executed on clicking stored/new vehicles Scegli operazione eseguita selezionando veicoli nuovi o in deposito chooses a random map Sceglie mappa casuale. citicens Cittadini City attraction Attrazione cittadina City industries Mercati in città City list Lista città City size Dimensioni city_road Strade urbane citybuilding builder Costruisci edifici urbani CityLimit Limiti città cl_title Lista veicoli cl_txt_sort Ordina per: Clear block reservation Mostra/nascondi vie riservate clf_chk_aircrafts Aereoplani clf_chk_cars Bus/Camion clf_chk_indepot in deposito clf_chk_maglev Maglev clf_chk_monorail Monorotaia clf_chk_name_filter Filtro per nome: clf_chk_narrowgauge Treni a scartamento ridotto clf_chk_noincome nessun incasso clf_chk_noline nessuna linea clf_chk_noroute nessuna rotta clf_chk_noschedule nessun percorso clf_chk_obsolete obsoleto clf_chk_ships Imbarcazioni clf_chk_spezial_filter Filtro speciale: clf_chk_stucked bloccato clf_chk_trains Treni clf_chk_trams Tram clf_chk_type_filter Filtro per tipo: clf_chk_waren Filtro per merci: clf_title Filtro Lista Veicoli Climate Clima Climate Control Controllo clima Climates Climi closed chiuso COLOR_CHOOSE\n Per favore scegli\nun colore dalla\ntabella:\n Company bankrupt Compagnia in bancarotta Company_msg Concorrenti Comparing pak files ... Comparazione pak files... Configure AI Configura AI Configure AI setttings Configura impostazioni AI Congratulation\nScenario was complete in\n%i months %i years. Congratulazioni!\nLo scenario è stato\ncompletato in\n%imesi e %i anni! Connected stops Fermate collegate Connected with server Connesso al server Connections Connessioni Constructed by Disegno di Constructed by %s Disegno di %s construction speed velocità di costruzione Construction_Btn Costi costruzione Consumed Consumato convoi %d of %d Convoglio %d di %d convoi error tooltips Richiami errori convogli Convoi following mode Modalità inseguimento convoglio Convoi has been sent\nto the nearest depot\nof appropriate type.\n Convoglio deviato al deposito più vicino Convoi is sold when all wagons are empty. Il convoglio viene venduto quando tutti i vagoni sono vuoti. convoi mouseover tooltips Richiami convogli con mouse sopra convoi passed last\nmonth %i\n \nconvogli nell'ultimo\nmese: %i\n Convois Convogli Convois: %d\nProfit: %s Veicoli: %d\nProfitto: %s Convoys Convogli Copy Convoi Copia Convoglio Copy the selected convoi and its schedule or line Copia convoglio selezionato con percorso o linea Cost Costo cost for removal Costi per rimozione Cost per unit Costo per unità Cost: %8s (%.2f$/km %.2f$/m)\n Costo: %8s (%.2f$/km %.2f$/m)\n Cost: %8s (%.2f$/km)\n Costo: %8s (%.2f$/km)\n Costs Costi Create a new line based on this schedule Crea nuova linea da questo percorso curiosity builder Costruzione curiosità curlist_title Lista attrazioni CURRENCY € Currently playing: Brano in esecuzione: Customers live in: Clienti abitano a: deactivated in online mode Non attivo online. Deccelerate time Rallenta tempo December Dicembre decrease underground view level Diminuisci livello visione sotterranea Del Stop Rimuovi Delete Line Cancella linea Delete the current stop Cancella la fermata corrente Delete the selected line (if without associated convois). Cancella la linea selezionata (se senza veicoli associati). Delete this file. Cancella questo file Delivered Consegnati Demand Richiesta Demand: %.0f MW Domanda %.0f MW\n Denkmal Monumento Departed Partiti Departs at Parte alle Departure after Partenza dopo Departure board Tabella partenze Departures per month Partenze al mese Departures to\n Partenze per\n Depot Deposito Depots Depositi Der Tunnel ist nicht frei!\n Galleria non vuota.\n Destination Destinazione Destroying map ... Distruzione della mappa... Details Dettagli Die Bruecke ist nicht frei!\n Il ponte non è libero!\n Direkt erreichbare Haltestellen Collegamenti diretti da qui disable midi Disabilita MIDI Display settings Modifica colori Distance Distanza distributing cities distribuzione città distributing factories distribuzione industrie Dock porto Dock must be built on single slope! I porti si costruiscono su unica livelletta. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Hai %d mesi per ripagare i debiti. Durchsatz Max. prod. Economy Economia e città Eigenbesitz\n Proprietà pubblica\n Ein %s\npasst hier nicht.\n Un '%s'\nnon va bene qui!\n Einstellungen Impostazioni Einstellungen aendern Cambia opzioni electric elettrico Electricity Elettricità Electricity producer\n\n Produttore elettricità\n\n Electrics_tab Veicoli elettrici Electrify track Elettrifica enlarge map Allarga mappa enter a value between %i and %i Inserisci un valore tra %i e %i Enter address Inserire l'indirizzo Enter Password Inserire Password Error Errore Erzeuge neue Karte.\n Attendere prego,\nsto creando una\nnuova mappa...\n\n(Per grandi mappe,\nquesta operazione\npuò richiedere anche\nalcuni minuti)\n Es wird bereits\nein Fahrplan\neingegeben\n Hai già iniziato\nad inserire un percorso!\n Fabrikanschluss Fabbriche connesse Fabrikname Nome fabbrica Factories Fabbriche factory details Dettagli fabbrica factorybuilder Costruisci fabbriche Fahrplan Percorso Fahrtziel Destinazione: Fahrzeuge koennen so nicht entfernt werden I veicoli non\npossono essere\nrimossi.\n Fahrzeuge: Veicoli: Farbe Colore giocat. Fast forward Tempo accelerato February Febbraio Ferry_tab Traghetti Fertig Fatto Filename Nome file: Files from: Files da: Filter: Filtro: Finances of %s Finanze di %s Finanzen Finanze find mismatch Compara il pak fl_title Lista fabbriche Flug_tab Aereo Passeggeri follow me Seguimi Follow the convoi on the map. Segui convoglio sulla mappa For questions and support please visit: Per domande e supporto visitare: Forest Foresta Found new city Fonda nuova città Fracht Merce Frame time: Tempo Frame: Free Capacity Capacità disp. freeplay mode Modalità gioco libero Friction: fattore frizione attuale: fuel_cell celle combustibile Full load Carico: Fundament Fondazione Fussgaenger Pedoni Game info Informazioni gioco GAME PAUSED GIOCO IN PAUSA Game_msg Generale Gear: Rapporti: Gebaeude Edificio General Generale Generated Generati Generation: %.0f MW Generazione: %.0f MW\n Gewicht Peso Gewinn Profitti: Give the selected vehicle(s) an individual schedule Dai al veicolo(i) selezionato un percorso individuale. gl_btn_sort_catg Categoria gl_title Lista di tutti i beni go home Al deposito Goods Merci Goods AI Merci IA Goods list Lista beni Gross Profit Utile lordo Groundobj Oggetto Grow city Ingrandisci Growth Crescita città H fermata Happy Felice Haus kaufen Compra casa Helligk. Grafica Help Aiuto Help text not found Testo della guida non trovato. hide all building Nascondi edifici hide city building Nascondi edifici città hide objects under cursor nascondi gli oggetti sotto il cursore hide station names nascondi nomi stazioni hide transparent Nascondi trasparenti hide trees Nascondi alberi Hier warten/lagern: Passeggeri/merci in attesa: Higher transport fees, crossconnect all factories Tasse di trasporto più alte, disabilita Just-in-time. Highlite schedule Evidenzia percorso hl_title Lista stazioni/fermate hl_txt_filter Filtro: hl_txt_sort Ordina per: hlf_chk_airport Aeroporti hlf_chk_anleger Porti hlf_chk_bahnhof Stazione ferrov. hlf_chk_bushalt Fermata bus hlf_chk_frachthof Piazz. di carico hlf_chk_keine_verb Senza conness. hlf_chk_maglevstop Stazione maglev hlf_chk_monorailstop Fermata monorail hlf_chk_name_filter Filtro per nome: hlf_chk_narrowgaugestop Stazione linea a scartamento ridotto hlf_chk_overflow Oltre Capacità hlf_chk_spezial_filter Filtro speciale: hlf_chk_tramstop Fermata tram hlf_chk_type_filter Filtro per tipo: hlf_chk_waren_abgabe Produzione: hlf_chk_waren_annahme Merci necessarie: hlf_title Filtro Lista Stazioni/Fermate Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Impossibile trovare deposito.\nManda il veicolo\nmanualmente. Homeless Senzatetto hydrogene Idrogeno Idle: Inattivi: ignore climates Ignora clima In the industry legend show only currently existing factories Nella legenda delle industrie mostra solo le fabbriche esistenti In Transit In Transito Increase Industry density Aumenta densità industrie increase underground view level Aumenta livello di visione sotterranea industrial building Edificio industriale Init map ... Costruzione mappa ... Ins Stop Inserisci Insert stop before the current stop Inserisci fermata prima della corrente Insufficient funds! Fondi insufficenti! Intercity road len: Lunghezza strade interurbane: Intro. date Data intro. Intro. date: Data intro.: invalid indefinito Invalid coordinate Ordine non valido Invert stops Inverti le fermate isometric map Mappa isometrica January Gennaio join game Gioca online! July Luglio Jump to Salta a June Giugno Kann Spielstand\nnicht laden.\n Impossibile leggere/aprire\nil file!\n Kann Spielstand\nnicht speichern.\n Impossibile aprire\nil file in\nscrittura!\n Kein Besitzer\n Nessun proprietario keine nessuno Keine Einzelfahrzeuge im Depot Nessun veicolo in deposito Keyboard_Help\n Aiuto tastiera\n koord Coordinate Kreuzung Attraversamento labellist_title Lista dei marcatori Lade Relief Carica Rilievo Laden Carica Land attraction Attrazioni della zona Land industries Industrie fuori città: LANG_CHOOSE\n Per favore scegli\nuna lingua:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Ultimo Mese Last used tools Ultimo strumento utilizzato Last Year Anno Scorso Leaving depot! Sto uscendo dal deposito! leer vuoto Legend Legenda Leistung Potenza Leistung: %d kW Potenza: %d kW Leitung Elettrodotto length: %d lunghezza: %d letzen Monat: diesen Monat: scorso mese: questo mese: Line Linea Line Management Gestione Linee Lineless convoys serving this stop Convogli senza linea che si fermano qui Lines serving this stop Linee passanti List of industries on the map Elenco industrie sulla mappa LKW_tab Camion Load game Apri file load height data from file Apri rilievo da file. Load scenario Carica scenario loaded caricato loaded passenger/freight Ordina passegeri/merci per Loading (%i->%i%%)! Caricamento (%i->%i%%) Loading (%i%%) departure %s! Caricando (%i%%) partenza %s! Loading (%i%%)! Caricando (%i%%)! Loading addon paks ... Caricamento pacchetti aggiuntivi... Loading map ... Caricamento mappa ... Loading paks ... Caricamento pacchetti... Loading skins ... Caricamento maschere... Loading time: Tempo di caricamento: Lock game Vieta cambio giocatore (richiede conferma). Lokomotive_tab Motrici m3 m³ maglev vehicle veicolo maglev maglev_track Linea maglev Maglevdepot Deposito maglev Mail Demand %d\n Richiesta di posta %d\n Mailbox Centro Messaggi Mailbox Options Opzioni Centro Messaggi Maintenance Manutenz. edifici make stop public (or join with public stop next) costs %i per tile and level far diventare la fermata pubblica (o collega a fermata pubblica vicina) costa %i$ per area e livello Make way or stop public (will join with neighbours), %i times maintainance Rendere pubblica fermata o strada (si unirà ai vicini), %i volte la manutenzione Manual (Human) Manuale (umano) Manufactured: Prodotti: Map roughness Asprezza terreno: map zoom Zoom March Marzo Margin (%%) Margine (%%) Marker Cartello Max Boost (%%) Incr. max. (%%) Max income: Guadagno max: Max. speed Velocità max. Max. speed: Velocità max.: Maximum 254 stops\nin a schedule!\n Al massimo 254 fermate\nin un percorso!\n maximum length of rivers Lunghezza max. fiumi Maximum tile height difference reached. Massimo dislivello\ntra due blocchi\ndi terreno\nraggiunto. Maxspeed Velocità massima May Maggio Median Citizen per town Media abitanti per città: Meldung Messaggio Menge ammontare merge stop unisci fermate MessageOptionsText \nNuovo Anno\n\nNotizie Avversari\n\nNotizie Città\n\nPercorsi Ostruiti\n\nNuove Fabbriche\n\nChat\n\nNuovi Veicoli\n\nStazioni Piene\n\nProblemi\n\nIngorghi Traffico min min. minimum length of rivers Lunghezza min. fiumi Missing pakfiles Nessun pak trovato Modify the selected line Modifica linea selezionata Monate alt mesi di età Monorail Monorotaia monorail vehicle Veicolo monorotaia monorail_track Linea monorail Monorailboden Suppporto Monorotaia Monoraildepot Deposito Monorotaia Month Mese month wait time Attesa mensile Monthly departures Partenze mensili Months Mesi Monument Monumento Monuments Monumenti Mountain height Altezza montagne: Movingobj Oggetto in movimento Music playing disabled/not available Musica disabilitata o non disponibile Music volume: Volume musica: mute sound Elimina suoni Name Nome Narrowgauge Linea a scartamento ridotto Narrowgauge are not available yet! Linea a scartamento ridotto non è ancora disponibile! narrowgauge vehicle Veicolo per linea a scartamento ridotto narrowgauge_track Linea a scartamento ridotto Narrowgaugedepot Deposito mezzi a scartamento ridotto Net ID: %p ID rete: %p\n Net Wealth Valore netto Net wealth near zero Valore netto prossimo a zero Neue Karte Nuova mappa Neue Welt Crea Nuovo Mondo new convoi Nuovo convoglio New Line Nuova linea New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Nuova linea creata!\nPuoi assegnare la linea\nselezionandola dal\nmenu sopra. New Vehicles Nuovi veicoli no buildings hidden Nessun edificio nascosto no convois nessun convoglio No goods are loaded onto this convoi. Nessun bene sarà caricato sul convoglio no goods waiting nessuna merce in attesa no load No carico No Route Senza percorso No stop here! Lo strumento deve essere usato nelle aree di fermata No suitable ground! Terreno non disponibile No terminal station here! Non c'è nessuna stazione terminale! no timeline no linea del tempo no tree Nessun albero No vehicles are available for purchase. Nessun veicolo è disponibile per l'acquisto. No. of Factories Numero di fabbriche Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Assegna un percorso\nal veicolo prima\ndi dargli l'ordine\ndi partire!\n none nessuno nord Nord nordost Nord-Est nordwest Nord-Ovest Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Il percorso del veicolo\nnon può esser cambiato\ndurante la ricerca del percorso. Not enough fields would remain. Non rimarranno abbastanza\nterreni intorno a questa fattoria November Novembre now adesso Now active as %s.\n Ora attivo come %s.\n Number of rivers Numero di fiumi Object Oggetto Odometer: %s km Tachimetro: %skm Oktober Ottobre On loan since %i month(s) In debito da %i mese/i On this map, you are not\nallowed to change player!\n Le impostazioni della partita\nnon permettono il cambio di giocatore.\n Only city chains Solo catene di città Only first %d differing paks reported. There are probably more. Solo i primi %d paks differenti sono segnalati. Probabilmente le differenze sono di più. Only land chains Solo catene di campagna Only one transformer per factory! Solo un trasformatore per fabbrica! Only show goods which are currently handled by factories Mostra solo i beni che sono attualmente gestiti dalle fabbriche open apri Operation Costi Operativi Ops Profit Profitti Optionen Opzioni Or enter a server manually: O inserire manualmente il server: Origin Origine ost Est Ownership Proprietà Pak which may cause severe errors: Pak che può causare errori gravi: Pak which may cause visual errors: Pak che può causare errori di visualizzazione: Pak(s) different: Pak(s) differente: Pak(s) missing on client: Pak(s) mancanti sul client: Pak(s) not on server: Pak(s) non sul server: Pakset differences Differenze del Pakset paletten casse Pas_tab Treni Passeggeri Passagiere Passeggeri Passagierrate Livello Passeggeri Passagierziele Destinazioni passeggeri/posta Passenger AI Passeggeri IA Passenger Demand %d\n Richiesta di passeggeri %d\n Passengers %d %c, %d %c, %d no route Passeggeri %d %c, %d %c, %d non serviti Passengers %d %s, %d %s, %d no route Passeggeri %d %s, %d %s, %d non serviti Password Password: Pause Pausa Pax <%i> Mail <%i> Passeggeri <%i> Posta <%i> PaxDest Destinazioni Percent Electricity Produzione elettricità(%% domanda) Planes are not available yet! Aerei non ancora disponibili. Plant tree Pianta alberi player Giocatore player -1 umano player 0 servizio pubblico player 1 Napik 128 AS player 10 giocatore 10 player 11 giocatore 11 player 12 giocatore 12 player 13 giocatore 13 player 2 Tricchi Trasporti player 3 Fratelli Bianchi Trasporti player 4 Società Anonima Trasporti player 5 Trasporti Veloci Spa player 6 PSK & Co KG player 7 giocatore 7 player 8 giocatore 8 player 9 giocatore 9 Please choose vehicles first\n Scegli prima i veicoli!\n Post Posta Postrate Livello Posta Power Potenza Power (MW) Potenza (MW) Power: Potenza: Power: %4d kW\n Potenza: %4d kW\n Powerlines Elettrodotti Price Prezzo Problems_msg Problemi Produced Prodotti Produces: %.1f units/minute Produce: %.1f/minuto Production of %s has been stopped:\n%s\n Produzione di %s è stata interrotta:\n%s\n Production/Boost Produzione/Incremento Produktion Produzione Profit Profitti promote to line Promuovi a linea q1 Primav. q2 Estate q3 Autunno q4 Inverno Query server Interroga server rail car treno random casuale Random age Anno casuale Random map Mappa casuale Rathaus Municipio Rating Valutazione ratio_pax Percentuale passeggeri Relevant Rilevante Reliefkarte Mappa Remove Rimuovi remove airstrips Rimuovi aeroporto remove channels rimuovi canali remove interm. signals Rimuovi segnali intermedi remove maglev tracks rimuovi linee maglev remove monorails rimuovi rotaia remove narrowgauge tracks rimuovi linee a scartamento ridotto remove powerlines rimuovi linee elettriche remove roads rimuovi strada remove signal rimuovi segnale remove tracks rimuovi binari Remove wayobj %s Rimuovi oggetto %s replace other signals Sostituisci altri segnali replace stop ripristina fermata request closing richiedi chiusura residential house Abitazione Restore natural slope Ripristina pendio naturale. Restwert: Prezzo di vendita: Retire date Data ritiro Retire. date: Data ritiro: return ticket Replica ritorno Revenue Entrate Revenue/unit/100 tiles Entrate/unità/100 caselle Revision: Revisione: road strada road vehicle Veicolo da strada Roadsign Cartello Stradale Rotate Building Ruota Edificio Rotate map Ruota mappa Rotation Rotazione sack sacchi sail vela Saving map ... Salvataggio mappa ... Scenario complete: %i%% Scenario completo: %i%% Schedule changing! Cambio programma! Schienentunnel Costruisci tunnel ferroviario Schiff_tab Imbarcazioni Schiffdepot Deposito imbarcazioni Schleppkahn_tab Chiatte Screenshot Cattura schermata Search: Cerca: Seasons stagioni Sehenswuerdigkeit Attrazione turistica Select a server to join: Scegli un server al quale aderire: Select a theme for display Scegli un tema Sell the selected vehicle(s) Vendi veicolo(i) selezionato. Selling of the program is forbidden. È vietata la vendita di questo programma. sended Spedito SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 September Settembre Served by Servito da Served by me Servito da me Server did not respond! Il server non risponde! Serves Line: Serve linee: Service Servizio set signal spacing Imposta distanza segnali Setting Opzioni Ship Imbarcazione shops and stores Negozi ed uffici Show all Mostra tutto show all building Visualizza tutti gli edifici Show also vehicles no longer in production. Mostra anche veicoli non più prodotti. Show also vehicles that do not match for current action. Mostra anche veicoli non adatti. Show even servers with wrong version or pakset Visualizza i server che forniscono una versione sbagliata del pakset. show grid Mostra griglia Show industry Mostra fabbriche Show legend Mostra legenda Show map scale Scala valori Show mismatched Mostra le mancate corrispondenze Show obsolete Mostra Veicoli Vecchi Show offline Mostra offline Show only used Mostra solo quelli utilizzati Show schedules Mostra programmi Show servers that are offline Mostra i server che sono offline Show servers where game version or pakset does not match your client Mostra i server dove il gioco o il pak non combaciano con la propria versione show station coverage Mostra copertura stazione show station names Mostra nomi stazioni show waiting bars Visualizza barre di attesa show/hide block reservations mostra/nascondi blocco prenotazioni Show/hide estimated arrival times mostra/nascondi tempi di arrivo stimati show/hide object owner mostra/nascondi possessore dell'oggetto Show/hide statistics Mostra/Nascondi Statistiche Shows buttons on special topics. Mostra i pulsanti relativi a particolari temi. Shows consumer/suppliers for factories Mostra clienti/fornitori per fabbrica Shows the color code for several selections. Mostra il codice colore per diverse selezioni. Shows the currently selected schedule Mostra la linea selezionata Shrink city Rimpicciolisci città shuffle midis MIDI casuali Signal Segnale signal spacing Distanza segnali Sim: CicliSim: Similar view as the main window Visualizza viste simili come finestra principale Size (%d MB): Dimens. (%d MB): sliced underground mode Vista per livelli slot empty Slot vuoto Smart hide objects Nascondi oggetti intelligente Sort by ordina per Sort by: Ordina per: Sort waiting list by Ordina lista di attesa per Sound Audio Sound settings Proprietà Audio Sound volume: Volume suono: special freight Carico Speciale Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Bonus velocità\nstrada %i km/h, ferrovia %i km/h\nnavi %i km/h, aerei %i km/h. Speedlimit Limite velocità Speichern Salva Spieler Giocatore Spieler(mz) Giocatori Spielerliste Lista giocatori Spielstand wurde\ngeladen!\n \nGioco caricato con successo!\n Spielstand wurde\ngespeichert!\n \nGioco salvato con successo!\n Sprache Lingua Sprachen Lingue Stadtinformation Statistiche della città Start Parti Start the selected vehicle(s) Fai partire veicolo(i) selezionato. Starte Spiel Inizia Gioco Station tiles: Area stazioni: Station_msg Stazioni Status Stato fermata steam vapore Step timeline one year Salta un anno. Stops Fermate Storage Accoglienza Storage capacity Capacità di accoglienza Strassendepot Deposito camion/bus Strassentunnel Costruisci tunnel stradale street car Auto sued Sud suedost Sud-Est suedwest Sud-Ovest Summer snowline 0°C estate Supplied: %.0f %% Forniti: %.0f %% Suppliers Fornitori Tage alt giorni di età There are still vehicles\nstored in this depot!\n Ci sono ancora\ndei veicoli\nin questo deposito!\n This Month Questo mese This Year Anno Corrente Tile not empty. Rimuovi tutto prima\ndi spostare la terra. timeline Usa linea del tempo tl_title Lista di tutte le Città To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Per attrarre più turisti,\n%s ha costruito\nun %s\ncon l'aiuto di\n%i contribuenti. To heavy traffic\nresults in traffic jam.\n Il troppo traffico\nha causato code.\n Toggle day/night view Cambia visione giorno/notte Toggle vehicle tooltips Cambia richiami veicoli tonnen t Total inhabitants: Abitanti totali: Tourist attractions Attrazioni turistiche Tourists Attrazioni Town_msg Nuove Destinazioni Town: %s\n Città di %s.\n Towns Città track linea Tracks Binari Traffic Traffico Train Treno Trains are not available yet! Treni non ancora disponibili. tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. Tram %i km/h, Monorotaia %i km/h\nMaglev %i km/h, Scartamento ridotto %i km/h tram_track Linea tram Tramdepot Deposito tram Trams are not available yet! Tram non ancora disponibili. Transferring game ... Trasferimento gioco... Transformer only next to factory! I trasformatori devono essere in\nuno spazio piano vuoto\nvicino alla fabbrica! Translation Traduzione transparencies trasparenze transparent background sfondo trasparente transparent station coverage Copertura stazione trasparente Transported Trasportati Trees disabled! Alberi disabilitati! TrolleyBus_tab filobus Truck Camion Tunnel muss an\neinfachem\nHang beginnen!\n Un tunnel deve\niniziare su\nlivelletta uniforme!\n Tunnel must start on single way! Le gallerie devono iniziare su una singola via. Tunnelboden Galleria under the Artistic License secondo la Licenza Artistica underground mode Modalità sotterranea UNDO failed! Impossibile annullare. \nPuoi annullare la costruzione di \ntracciati solo prima di aver messo\nsegnali/stazioni/fermate ecc. Undo last ways construction Annulla costruzione delle ultime linee Unemployed Disoccupati Unhappy Infelici units/day /mese, Unloading (%i%%)! Scaricando (%i%%)! Update Line Aggiorna linea upgrade HQ Migliora sede Usage: %.0f %% Utilizzo: %.0f %% Usage/Output Utilizzo/Produzione Use beginner mode Modalità principiante Use timeline start year Linea del tempo dall'anno: Vehicle %s can't find a route! Il veicolo %s\nnon trova il percorso! Vehicle %s is stucked! Il veicolo %s è bloccato! Vehicle count: Conteggio Veicoli: Vehicle details Dettagli Veicolo Vehicle Name Nome Veicolo Vehicle Power Potenza Veicolo vehicles stored veicoli in deposito Verbrauch Consumo Vergroessere die Karte\n Allarga mappa\n Verkauf Rimuovi convoglio verkaufen Vendi Verkehrsteilnehmer Utente stradale Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Sei in rosso!\n\nHai %d mese/i\nper rientrare.\n via via (dettaglio) via %s\n via %s\n via Menge via (ammontare) voranstellen Agg.in testa Waggon_tab Vagoni waiting in attesa Waiting for clearance! In attesa di permesso! Walked Camminato walking camminando Warnings_msg Traffico Wasser Acqua Water Acqua Water level Livello acqua: water vehicle Veicolo navale way %s cannot longer used:\n La via del tipo %s non può più esser usata.\n way %s cannot longer used:\n%s\n La via del tipo %s non può più essere usata:\n%s\n way %s now available:\n Via del tipo %s ora disponibile:\n Way toll Casello pedaggio Ways not connected Vie non connesse Wegpunkt Waypoint Weight Peso Weight: Peso: Weight/unit Peso/unità Welcome to Simutrans Benvenuti in Simutrans Wert Valore west Ovest Wind direction Direzione del vento Winter snowline 0°C inverno withdraw Ritira Withdraw All Ritira tutto WRONGSAVE Versione salvata\nnon compatibile.\n\nImpossibile leggere\nil file.\n Year Anno Year %i has started. L'anno %i è iniziato. Years Anni Your primary color: Il tuo colore primario: Your secondary color: Il tuo colore secondario: Zielort Destinazione zooming in Aumenta zoom zooming out Riduci zoom Zu nah am Kartenrand Troppo vicino al\nbordo della mappa\nper costruire qualcosa.\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nuovo record mondiale per maglev: %.1f km/h da %s. New world record for monorails: %.1f km/h by %s. Nuovo record mondiale per \nmonorotaie: %.1f km/h\nda %s. New world record for motorcars: %.1f km/h by %s. Nuovo record del mondo per\nveicoli:\n %.1f km/h\nda %s. New world record for narrowgauges: %.1f km/h by %s. Nuovo record del mondo per ferrovie a scartamento ridotto: %.1f km/h da %s. New world record for planes: %.1f km/h by %s. Nuovo record del mondo per\naeroplani:\n %.1f km/h\nda %s. New world record for railways: %.1f km/h by %s. Nuovo record del mondo per\nferrovie:\n %.1f km/h\nda %s. New world record for ship: %.1f km/h by %s. Nuovo record del mondo per\nnavi:\n %.1f km/h\nda %s. #________________________________script_ai_text_________________________________ #________________________________script_ai_text_________________________________ %s build additional convoy to line: %s %s costruisce un convoglio aggiuntivo sulla linea: %s %s build rail line from %s (%s) to %s (%s) %s costruisce linea ferroviaria da %s (%s) a %s (%s) %s build road line from %s (%s) to %s (%s) %s costruisce linea stradale da %s (%s) a %s (%s) %s build ship line from %s (%s) to %s (%s) %s costruisce linea navale da %s (%s) a %s (%s) %s extends the route from %s (%s) to %s (%s) %s estende il percorso da %s (%s) a %s (%s) %s optimize way line from %s (%s) to %s (%s) %s ottimizza percorso da %s (%s) a %s (%s) %s removes convoys from line: %s %s rimuove i convogli dalla linea: %s vehicles of the line %s were retired i veicoli della linea %s sono stati ritirati #_________________________________tooltip_text__________________________________ #_________________________________tooltip_text__________________________________ Add convois with similar schedule to this line. Aggiungi convogli con orari simili a questa linea. Enter intervall in days, hours, minutes Immettere l'intervallo in giorni, ore, minuti #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL villaggio &1_CITY_SYLL vecchio &2_CITY_SYLL nuovo &3_CITY_SYLL fiorito &4_CITY_SYLL bello &5_CITY_SYLL basso &6_CITY_SYLL bruno &7_CITY_SYLL verde &8_CITY_SYLL rosso &9_CITY_SYLL forte &A_CITY_SYLL corto &B_CITY_SYLL da &C_CITY_SYLL seppellire &D_CITY_SYLL vece &E_CITY_SYLL bene %0_CITY_SYLL pino %1_CITY_SYLL Borgo %2_CITY_SYLL Castel %3_CITY_SYLL Orto %4_CITY_SYLL Monte %5_CITY_SYLL Colle %6_CITY_SYLL Passo %7_CITY_SYLL Prato %8_CITY_SYLL Campo %9_CITY_SYLL Bosco %A_CITY_SYLL Sasso %B_CITY_SYLL Ponte %C_CITY_SYLL nuovo %D_CITY_SYLL Vuoto %F_CITY_SYLL Quercia 1center %s %s 1extern %s %s est. 1suburb %s %s %s 2center %s %s Centro 2extern %s %s P.ta Principe 2suburb %s Terre verdi %s %s 3center %s %s Via Dante 3extern %s %s P.ta Vecchia 3suburb %s villaggio %s %s 4center %s %s V.le Repubblica 4extern %s %s P.ta Nuova 4suburb %s rurale %s 5center %s %s C.so Roma 5extern % trasferimento %s 5suburb %s città %s 6center %s %s Hub 6extern %s scambio %s 6suburb %s periferia %s %s 7center %s %s Settimo 7extern %s pianure %s 7suburb %s parco %s 8center %s affari %s 8extern %s %s margine %s 8suburb %s satellite %s 9center %s asse %s 9extern %s bypass %s 9suburb %s mezzo %s simutrans-124.5/simutrans/text/it/000077500000000000000000000000001520675460500172115ustar00rootroot00000000000000simutrans-124.5/simutrans/text/it/airtools.txt000066400000000000000000000122021520675460500216030ustar00rootroot00000000000000Aiuto strumenti aeroporto

Strumenti aeroporto

Strumenti aeroporto contiene gli strumenti per costruire reti aeree. Gli strumenti possono costruire o rimuovere: piste di rullaggio o di atterraggio, spiazzi per carico/scarico (sia merci sia passeggeri), depositi per aerei (per comprare e gestire i mezzi) e vari edifici aeroportuali. Se giochi con la linea del tempo attivata, più il tempo passa e più strumenti ed edifici diventeranno disponibili.

Clicca sull'icona dell'aeroplano in cima alla vista di gioco per aprire la barra.
Posiziona il cursore sopra l'opzione o strumento voluto (dopo aver aperto o cliccato sulla barra) per vedere il nome e, dove previsto, il costo di costruzione, con quello di mantenimento tra parentesi, e la velocità massima consentita.

{Consigli: un semplice metodo per costruire un aeroporto è:
I) costruisci e collega piste di rullaggio e atterraggio;
II) piazza degli spiazzi alla fine delle piste di rullaggio;
III) aggiungi gli edifici richiesti.}

Gli strumenti possono includere, da sinistra a destra:

Pista di rullaggio: lo strumento costruisce delle aree di movimentazione per aerei per andare dal piazzale alle piste di atterraggio. Le piste di rullaggio sono costruite a livello del terreno.
IMPORTANTE: Se li piste di rullaggio sono connesse alla fine di una di atterraggio, gli aerei non atterreranno o decolleranno.
Come costruire una pista di rullaggio: clicca sullo strumento e seleziona (il cursore cambia su pista di atterraggio), poi clicca sul terreno per il punto di partenza (compare un bulldozer sul punto scelto) e sul punto finale.
{Consiglio: Usa Rimuovi per rimuovere parti individuali della pista di rullaggio. Annullare con [z] non rimborsa i costi di costruzione.}

Pista d'atterraggio: gli strumenti costruiscono una pista, usata dagli aerei per decollare e atterrare. Esse sono costruite a livello del terreno e si possono incrociare una all'altra.
Come costruire una pista d'atterraggio: clicca sullo strumento e seleziona (il cursore cambia su pista), poi clicca sul terreno per il punto iniziale (compare un bulldozer) e, infine, clicca sul punto finale.
{Consiglio: Lascia le estremità della pista non collegata, altrimenti gli aerei non partiranno o atterreranno. Usa Rimuovi per eliminare singoli pezzi di pista. Annullare con [z] non rimborsa i costi di costruzione.}

Rimuovere piste: lo strumento rimuove un segmento di pista di rullaggio o atterraggio, quando non ci sono aerei, tra due punti nella vista di gioco. L'uso dello strumento prevede un costo di costruzione.
Come rimuovere una pista: clicca sullo strumento (il cursore diventa una croce rossa), poi clicca sulla pista da rimuovere (seleziona il punto iniziale da rimuovere) e, infine, sul punto finale per rimuovere la porzione tra i due punti. {Consigli: Lascia gli estremi delle piste d'atterraggio non connessi, altrimenti gli aerei non atterreranno o decolleranno. Usa Rimuovi per eliminare singole parti di piste.}

Deposito aerei: lo strumento costruisce un hangar per comprare e gestire gli aerei. Gli hangar hanno un costo di mantenimento e devono essere costruiti alla fine di una pista di rullaggio.
Come costruire un hangar: clicca sullo strumento (il cursore cambia in un deposito) e poi alla fine di una pista di rullaggio.
{Consiglio: Rimuovi il deposito con Rimuovi.}

Piazzole di sosta e ponte di imbarco: gli strumenti costruiscono una fermata nella vista di gioco per caricare e scaricare merci e passeggeri.
Un piazzola non costruita a fianco un'altra Fermata ne creerà una nuova.
Le piazzole sono costruite alla fine di una pista di rullaggio, hanno costi di mantenimento e un bacino d'utenza per merci, passeggeri e posta. Un aereo atterrerà solo se c'è una piazzola libera.
Come costruire una fermata: clicca sullo strumento e poi su un'estremità di una pista di rullaggio.
{Consigli: elimina le piste con Rimuovi. Premi [v] per vedere/nascondere il bacino d'utenza per merci e passeggeri nella vista di gioco.}

Edifici aeroportuali: gli strumenti costruiscono estensioni per le Fermate e possono aumentare i costi di mantenimento, la capacità e il bacino d'utenza per merci e passeggeri. Nell'angolo di alcuni strumenti sono riportate le icone (usate in Lista Stazioni e Informazioni fermata) mostrano che tipo di bene l'estensione consente alla fermata di gestire.
Come costruire un'estensione: clicca sullo strumento per selezionare l'estensione (il cursore cambia nell'estensione), poi clicca sulla posizione richiesta, vicino a una fermata preesistente. La nuova estensione è ora considerata come parte della fermata.
{Consigli: rimuovere estensioni con Rimuovi. Premi [v] per visualizzare/nascondere i bacini di raccolta di merci e passeggeri nella vista di gioco.}

simutrans-124.5/simutrans/text/it/baum_build.txt000066400000000000000000000022621520675460500220570ustar00rootroot00000000000000Pianta alberi

Pianta alberi

La finestra di dialogo è divisa in quattro sezioni.
· Quella in alto a sinistra è una lista di selezione dei tipi di alberi a disposizione
· In fondo a sinistra a sinistra è presentata un'immagine dell'albero scelto
· In alto a destra possono essere selezionate alcune opzioni
· In fondo a destra ci sono le informazioni sull'albero selezionato

Lista di selezione

Nella lista di selezione sono elencati tutti i tipi di alberi che sono disponibili con le opzioni specificate in alto a destra. Ci sono due tab che ti permettono di cambiare come i tipi di albero sono identificati.
· Traduzioni: Presenta il nome dell'albero nella propria lingua. Se non è disponibile la traduzione, sarà utilizzato il nome dell'oggetto.
· Oggetto: Presenta il nome interno dell'oggetto in Simutrans per il tipo di albero.

Opzioni

· Ignora clima: Questa opzione disattiva le restrizioni climatiche associati i vari tipi di albero.
· Anno casuale: L'anno in cui l'albero sarà piantato è casuale.

simutrans-124.5/simutrans/text/it/citylist_filter.txt000066400000000000000000000036011520675460500231630ustar00rootroot00000000000000lista città

Lista città

Lista città visualizza i dettagli sulle aree urbane (villaggi, paesi e città) popolazioni.

Per aprire: clicca sullo strumento Lista città nella Gestione liste o premi [T].
Lista città ha due pulsanti opzione che determinano l'ordine in cui le aree urbane (e le loro popolazioni) sono presentate.
{Consiglio: Se le voci nella lista sono solo parzialmente visibili, modifica la dimensione di Lista città oppure muovi la lista con lo slider.}

Abitanti totali: è il numero totale di cittadini nel gioco (la popolazione di tutte le aree urbane). La variazione di popolazione è riportata tra parentesi.

Ordinati per: ha le opzioni per determinare l'ordine delle aree urbane elencate nella lista.
Clicca sul pulsante opzione per muoversi tra le opzioni (cambia il nome del bottone):

- Nome ordina alfanumericamente secondo il codice ASCII (le lettere maiuscole sono prima delle minuscole) inomi assegnati.
- Cittadini ordina secondo la popolazione.
- Crescita popolazione ordine secondo la crescita (che è dipendente dalle dimensioni dell'area urbana e dal numero numero di passeggeri trasportati).

- Ascendente / Discendente inverte l'ordine della lista.

Cliacca su un elemento dellaLista cittàper vedere più informazioni circa l'area urbana.
Per ogni area sono riportati:

nome assegnato all'area urbana (può essere cambiata nelle Informazioni città)..

numero di abitanti dell'area urbana, con l'aumento di popolazione tra parentesi.

simutrans-124.5/simutrans/text/it/citywindow.txt000066400000000000000000000120501520675460500221500ustar00rootroot00000000000000Aiuto statistiche città

Statistiche città

Statistiche città visualizza le statistiche dell'area urbana selezionata e può essere usata per cambiarne il nome. Un'area urbana è una città, un paese o un villaggio; tutte le aree urbane sono chiamate città.

Ogni città ha il suo Municipio e cresce attorno ad esso. Il trasporto di passeggeri e di posta aiuta la crescita, che provoca la costruzione di strade o l'assorbimento di quelle esistenti da parte del servizio pubblico; appaiono nuovi edifici cittadini e industrie e i limiti della città di espandono.

Clicca su un Municipio nella vista di gioco con lo Strumento ispezione o su una città elencata nella Lista città per aprire le Statistiche città. Qui puoi vedere e modificare il nome della città, alcune statistiche, due mini mappe e un grafico dove puoi vedere lo sviluppo nel tempo di certi fattori riguardanti la città.

Nome: il nome assegnato alla città appare in un riquadro nella parte alta di Statistiche città.
I nomi di queste aree urbane sono usati per creare quelli delle fermate nei limiti della città. Per cambiare il nomer cliccare semplicemente sul riquadro e inserirne uno nuovo.
{Suggerimento: Usa [!] per vedere il nome della città sopra il Municipio nella finestra di gioco. Guarda readme_citylist.txt (in ...simutrans/text/) per più informazioni su come sono generati i nomi delle città}.

Sotto il riquadro del nome sono presentate le statistiche base della città:
Dimensioni: numero di cittadini corrente. Il recente aumento di popolazione è riportato tra parentesi.
Edifici: numero di edifici (residenziali/commerciali e industriali) nella città.
Coordinate sulla mappa: indica i limiti della città (cambieranno con la crescita della città). Il primo paio di valori indica l'angolo in alto a sinistra e il secondo quello in basso a destra. Con la crescita delle città, i limiti si possono sovrapporre.

Numero corrente di disoccupati e senza tetto nella città. Essi aumenteranno quando la città espanderà i propri limiti e scenderanno quando saranno eretti nuovi edifici. L'equilibrio tra questi numeri determina che tipo di edifici (residenziale/commerciale/industriale) verrà costruito.

Il bottone Permetti crescita città ti permette di attivare o disattivare la crescita delle città.

Le Mini mappe del mondo nelle Statistiche città mostrano le destinazioni dei passeggeri e posta generati nella città come pallini colorati.
La mini mappa sulla destra riporta i dati dell'ultimo mese, e quella a sinistra del mese precedente. Il colore di ogni punto indica se la destinazione può essere raggiunta o meno:
- punto giallo: c'è un percorso e una fermata non troppo affollata per iniziare il viaggio (faccina felice nelle Informazioni sulla fermata).
- punto arancione: non c'è un tragitto per questa destinazione.
- punto rosso: c'è un tragitto fino alla destinazione, ma la fermata della partenza era troppo affollata (faccia triste nelle Informazioni sulla fermata).

Sotto le mini mappe c'è un grafico che mostra le statistiche per la città.
Sull'asse delle y c'è la quantità, mentre sulle x il tempo. Clicca su uno dei due tab sopra per cambiare la scala dell'asse delle x:
Anni: visualizza valori annuali per gli ultimi 12 anni.
Mesi: visualizza valori mensili per gli ultimi 12 mesi.

Cliccando sul bottone delle opzioni del grafico, attiverai e disattiverai le relative curve. Quando un bottone è selezionato, appare come premuto.
Le curve hanno lo stesso colore del loro bottone:
Popolazione: il numero di abitanti che vivono nella città.
Crescita città: aumento o calo di popolazione (dipendente dalle dimensioni della città e dal servizio di trasporto fornito a passeggeri e posta).
N° edifici: quanti edifici sono presenti nella città.
Auto da città: quante auto sono state generate.
Viaggi: quanti passeggeri sono entrati nella rete di trasporto (rappresentato da una faccina felice nelle informazioni sulla fermata)
Passeggeri: il numero totale di passeggeri generati dalla città.
Posta spedita: quantità di posta che è entrata nella rete di trasporti.
Posta: quantità totale di posta generata nella città.
Approvvigionamento: numero di volte che ci sono state merci nelle fabbriche della città.
Merci: numero di volte che il magazzino delle fabbriche in città è stato controllato.

{Suggerimento: le impostazioni relative alle città possono essere cambiate in simuconf.tab e cityrules.tab}.

simutrans-124.5/simutrans/text/it/climates.txt000066400000000000000000000052741520675460500215630ustar00rootroot00000000000000Aiuto imposta clima

Imposta clima

La finestra Imposta clima imposta le opzioni per gli aspetti topografici e meteorologici del mondo. Si può aprire dalle finestre Crea nuovo gioco e Lingue.

Per modificare ogni impostazione, cliccare sulle frecce a lato di ognuna di esse.

Opzioni mappa

Livello acqua - Imposta quant'è alto il livello dell'acqua nella mappa. Valori più alti riducono la terra ferma a favore dei mari.

Altezza montagne - Imposta l'altezza massima del terreno.

Asprezza mappa - Imposta l'asprezza del terreno. Un numero grande crea delle linee di costa più ondulate. Per creare una mappa realistica, impostare un valore alto.

Limite neve
È l'altezza oltre la quale appare la neve.

Limite neve estivo - È il limite della neve durante l'estate. È il valore più alto da impostare tra le opzioni del clima.

Limite neve invernale - Imposta il limite della neve per l'inverno. Per far apparire la neve in zone ampie della mappa, impostare un valore basso.

Opzioni clima
Impostare le differenti opzioni climatiche per la mappa.
Il valore dopo ogni opzione imposta le altezze del terreno dove appare quel dato clima. Per disattivarne uno in particolare, inserire 0 (zero).

Clima desertico - Pochissime precipitazioni e temperature estreme. Pochi alberi e paesaggio piatto

Clima tropicale - Tipico delle zone non aride intorno all'equatore, con temperature costantemente alte al livello del mare e a piccole elevazioni

Clima mediterraneo - Clima moderatamente variabile ed umido

Clima temperato - Presente nelle aree tra i topici e i poli. Tempo variabile

Tundra - Basse temperature, ai limiti dei poli

Clima alpino - Clima secco con inverni freddi

Altre opzioni

Nessun albero - Cliccare per abilitare. Attivare quest'opzione per evitare che Simutrans disegni alberi. Il loro disegno occupa la CPU. Usare quest'opzione su computer lenti o con poca RAM.

Numero di fiumi - Imposta il numero massimo di fiumi che possono essere generati e usati in una mappa. Essi non possono essere navigati da alcun tipo di barca o nave.

Lunghezza minima dei fiumi - Imposta la lunghezza minima dei fiumi in termini di numero di settori.

Lunghezza massima dei fiumi - Imposta la lunghezza massima dei fiumi in termini di numero di settori.

simutrans-124.5/simutrans/text/it/color.txt000066400000000000000000000003451520675460500210720ustar00rootroot00000000000000Aiuto - Scelta colori

Scelta colori

Con questo menù si può scegliere il colore che avranno tutti i propri veicoli, le proprie stazioni e le proprie fermate. Il colore di default è l'azzurro.

simutrans-124.5/simutrans/text/it/convoidetail.txt000066400000000000000000000036511520675460500224370ustar00rootroot00000000000000Aiuto dettagli del convoglio

Dettagli del convoglio

Dettagli del convoglio fornisce più informazioni riguardo un determinato convoglio (un veicolo o una combinazione di essi) e può essere usato per venderlo immediatamente.

Dettagli del convoglio si apre dal bottone Dettagli ne Informazioni sul convoglio.
Se tutti gli elementi non fossero visibili, ridimensiona Dettagli del convoglio o usa una barra di scorrimento per muoverli.

In cima a Dettagli del convoglio, le informazioni includono:

Potenza: del convoglio (combinazione della potenza di tutti gli elementi motorizzati in esso).

Valore di vendita: denaro ricevuto se il convoglio venisse venduto (prezzo d'acquisto meno tutte le perdite di valore).

ATTENZIONE: Clicca sull'opzione Modalità vendita per vendere il convoglio immediatamente. Nessuna conferma sarà richiesta. Esso sarà rimosso dal gioco e il bilancio del giocatore riceverà in credito il prezzo di vendita.

Dettagli convoglio elenca tutti gli elementi in esso.
Le informazioni presenti per tutti i veicoli includono:
- Immagine: una figura dell'unità (usata anche in Depositi).
- Costruito: mese e anno di costruzione e di introduzione se si sta giocando con la linea del tempo.
- Valore di rivendita: somma ricevuta se venduto.
- Potenza: potenza generata (dai veicoli motorizzati).
- Frizione: livello di frizione corrente.
- Guadagno massimo: guadagno dell'unità secondo il tipo di bene trasportato dal convoglio.
- Beni a bordo ammontare e destinazione dei beni trasportati dal veicolo.

simutrans-124.5/simutrans/text/it/convoiinfo.txt000066400000000000000000000153501520675460500221270ustar00rootroot00000000000000informazioni convoglio

Informazioni convoglio

Informazioni convoglio riporta le informazioni riguardanti un convoglio (un veicolo o una combinazione di essi) che è usato per trasportare merci o passeggeri.
Elenca quanto trasportato da un convoglio e ha la possibilità di accedere a maggiori informazioni e di venderlo, seguirlo nella vista di gioco, cambiargli nome e assegnargli una linea.

Un convoglio è composto da almento un veicolo motorizzato e da ogni rimorchio per merci o persone.
Esempi di convogli includono: un gruppo di cavalli che trainano un carro; una locomotiva attaccata a un tender e a dei vagoni; un bus o un camion attaccati a un rimorchio; tram collegati; una nave; un rimorchiatore che traina una chiatta; un aereo.
I convogli sono assemblati e comprati da un deposito a seconda del tipo di trasporto.
{Consigli: un convoglio ha un limite di unità a seconda del tipo: treni 24; veicoli stradali 4}

Clicca su un convoglio nella vista di gioco con lo strumento ispezione o clicca su un convoglio riportato nella Lista veicoli o Gestione linee per aprire le Informazioni convoglio
La barra del titolo delle Informazioni convoglio mostra un unico ID e nome del convoglio (per default è quello del primo veicolo acquistato o assemblato nel deposito).
Informazioni convoglio contiene una casella con il nome, una mini vista con informazioni sul convoglio, bottoni per le opzioni e una lisa di beni caricati al momento (se non sono visibili tutti ridimensiona Informazioni convoglio o usa gli slider per muoverli).

Le informazioni riportate in alto a Informazioni convoglio includono:

- ID interno: un unico ID per convoglio (assegnato al momento dell'acquisto o dell'assemblaggio in un deposito).

- nome: riporta il nome assegnato al convoglio (assegnato al momento dell'acquisto o dell'assemblaggio in un deposito).
Clicca sul nome e scrivine uno nuovo per cambiarlo.

- velocità: la velocità corrente è riportata in km/h.
Tra parentesi la velocità massima del convoglio (determinata dall'unità più lenta del convoglio).
Una barra verde indica la velocità corrente relativa alla massima consentita.

- guadagno: profitto generato dal veicolo (incasso meno costi operativi) nell'anno di gioco corrente.
Tra parentesi il costo operativo per quadrato di gioco.

- Peso: combinazione di peso del convoglio e delle merci in tonnellate.
Tra parentesi c'è il peso delle merci caricate.
Una barra del progresso verde indica la capacità utilizzata per caricare merci e passeggeri.
Una barra gialla indica In attesa del livello minimo (quantità minima di merci o persone richieste per partire) impostato nel Controllo linee.
{Consigli: per impostare o modificare i livelli minimi per tutti i convogli in una linea, utilizzare il bottone Aggiorna linea in Gestione linee o i Controlli nel deposito. Per impostarli solo per un certo convoglio, usare il bottone Imposta linea nelle Informazioni convoglio or Controlli deposito.}

- Destinazione: la prossima fermata o punto di passaggio nella linea.
Una barra del progresso verde indica la distanza alla prossima destinazione.

- Serve linea: indica ogni linea assegnata al convoglio.

- mini vista visualizza il convoglio.
Clicca sulla mini vista per vedere il convoglio sulla vista di gioco.

Clicca sui bottoni nelle Informazioni convoglio per aprire i controlli o selezionare le opzioni:

Linee: apre i controlli per cambiare percorso e la quantità minima di merci e passeggeri richiesta dal convoglio per procedere.

vai al deposito: manda il convoglio al deposito appropriato più vicino. Ogni elemento caricato è perduto e si riceve l'incasso per il trasporto al deposito.
Nel deposito il convoglio mantiene la propria Linea.

seguimi: bottone che muove la vista di gioco con il convoglio.
Per deselezionare l'opzione, cliccare ancora sul bottone o con il destro sulla Mappa.

grafico: clicca sul bottone per visualizzare il grafico (quando quest'ultimo è visibile, il bottone è evidenziato) nelle Informazioni convoglio.
Il grafico mostra le statistiche per gli ultimi 12 mesi (asse x) quando è selezionata un'opzione.
Clicca sui bottoni delle opzioni del grafico per avere delle informazioni su di esso (il bottone è evidenziato quando l'opzione è selezionata).
I colori e linee del grafico corrispondono a quelli dei bottoni:
- Capacità disponibile indica lo spazio inutilizzato del convoglio.
- Trasportati indica la quantità di merce, passeggeri e posta trasportati.
- Entrate indica i ricavi generati dal trasporto.
- Costi operativi indica i costi sostenuti durante il transito del convoglio.
- Profitto indica il profitto ricevuto dal trasporto (Entrate meno costi operativi).

Dettagli apre Dettagli convoglio che contiene più informazioni e opzioni per vendere un convoglio.

Ordina passeggeri/merci per: elenca quanto correntemente caricato su un convoglio.
Le informazioni riportate includono la quantità caricata e la capacità del convoglio, tipo di merce, destinazione finale e prossima fermata.
Il pulsante (cambia nome con la selezione) ordina gli elementi caricati in gruppi di passeggeri, posta e merci:
- destinazione: ordina gli elementi alfanumericamente secondo l'ordine ASCII (lettere maiuscole prima delle minuscole), secondo il nome della fermata di destinazione.
- via (dettaglio): ordina gli elementi alfanumericamente secondo l'ordine ASCII (lettere maiuscole prima delle minuscole), secondo il nome della prossima fermata.
- via (ammontare): ordina gli elementi trasportati alla prossima fermata secondo la quantità.
- ammontare: ordina gli elementi secondo la quantità in ordine decrescente.

simutrans-124.5/simutrans/text/it/depot.txt000066400000000000000000000057671520675460500211040ustar00rootroot00000000000000Deposito

Finestra di dialogo Deposito

Nel deposito si può comprare, vendere e definire il percorso di tutti i veicoli disponibili di un particolare tipo, ad es. autobus o camion se si è in un deposito di mezzi stradali.

Questa finestra di dialogo contiene informazioni sui veicoli che si possono comprare e su quelli già acquistati. Nelle tab sulla destra sono mostrati i veicoli acquistabili. Nella zona in alto a sinistra è mostrato il convoglio attualmente assemblato (ad es. un treno con i suoi vagoni). Nella zona di mezzo, sempre a sinistra, sono invece mostrati motrici e camion, mentre in basso a sinistra vengono mostrati vagoni e rimorchi.

Nella parte più in basso vengono visualizzate delle informazioni relative al veicolo selezionato. Al di sotto di questa zona, sono presenti tre bottoni.

Il più a sinistra è il pulsante "Parti", che serve per far partire un veicolo di cui si è già specificato il percorso. Il pulsante "Percorso" serve quindi proprio a definire una serie di punti in cui si vuole far passare il veicolo. Infine, il bottone "Vendi" svolge la ovvia funzione di vendere i veicoli in proprio possesso (questa funzione è attiva finchè non la si disattiva).

Per vedere le informazioni relative ad un particolare veicolo è sufficiente passare con il puntatore del mouse sopra uno di essi. Le informazioni compariranno nella parte bassa della finestra. Per comprare un veicolo basta selezionarlo con il tasto sinistro del mouse: comparirà nella parte sinistra della finestra. Il convoglio sul quale si sta lavorando apparirà nella zona superiore sinistra; ogni motrice/camion/nave addizionale viene visualizzata subito sotto, mentre ogni vagone/rimorchio addizionale ancora sotto, appena sopra le informazioni.

E' possibile formare un convoglio in due modi. Si possono comprare tutti i veicoli necessari uno dopo l'altro, iniziando dalla motrice/camion e quindi comprando ogni altro vagone/rimorchio; oppure si possono selezionare uno ad uno i veicoli già acquistati in precedenza e formare un convoglio con quelli.E' possibile lavorare su un solo convoglio alla volta. Per vendere un qualunque veicolo in proprio possesso, si seleziona il pulsante "Vendi" e quindi si clicca sul veicolo. Il prezzo di vendita appare nella zona delle informazioni.

Per definire il percorso di un veicolo, si preme il pulsante "Percorso" per far comparire la finestra di impostazione del percorso. Una volta definito il percorso, è possibile far partire il veicolo agendo sul bottone "Parti".

Per far tornare un convoglio al deposito, è necessario rimuovere tutte le fermate dal suo percorso e impostare come sua unica destinazione il deposito stesso. Una volta che il convoglio ha raggiunto il deposito, viene disassemblato e i veicoli di cui era composto diventano nuovamente disponibili nel deposito, pronti per ripartire, formare un altro convoglio o essere venduti.

simutrans-124.5/simutrans/text/it/display.txt000066400000000000000000000106061520675460500214220ustar00rootroot00000000000000Aiuto - Grafica

Grafica

Grafica permette di controllare l'aspetto grafico del gioco e fornisce informazioni sulle performance.

Click sul pulsante Grafica nel menù Opzioni di Gioco per aprire il menù Grafica.

Click sui bottoni quadrati permette di selezionare / deselezionare le opzioni, mentre si possono utilizzare i bottoni freccia per regolare i settaggi:

Mostra griglia: evidenzia i bordi dei quadrati in cui è suddivisa la mappa.

Modalità sotterranea: visualizza cosa si trova subito sotto al livello del suolo (utile per costruire tunnel, metropolitane, ferrovie sotterranee)

Sliced underground mode: seleziona il livello del terreno da visualizzare.
In questa modalità il terreno viene suddiviso "a fette" di pari livello, viene visualizzato il livello selezionato utilizzando i bottoni freccia.

Modalità giorno e notte: attiva / disattiva la variazione della luminosità in base all'ora del giorno.

Luminosità: permette di impostare la massima luminosità ad un determinato valore indicato utilizzando i bottoni freccia; 0 imposta la massimà luminosità, 9 la minima.

Inverti scrolling: inverte la direzione dello scroll nella finestra del gioco.
Tenendo premuto il tasto destro nella finestra di gioco è possibile trascinare o spostare il terreno.

Vel. scrolling: imposta la velocità dello scroll nella finestra del gioco.

Nascondi trasparenti: tutti gli oggetti nascosti (siano essi alberi od edifici) saranno resi trasparenti.

Nascondi alberi: selezionare questa opzione comporta che se "nascondi trasparenti" è selezionato gli alberi saranno resi trasparenti, altrimenti saranno miniaturizzati.

Copertura stazione trasparente: con questa opzione è possibile modificare l'aspetto grafico con cui viene indicata la copertura della stazione.

Mostra copertura stazione: visualizza un segnaposto, il cui aspetto è selezionabile da "copertura stazione trasparente", sulle caselle che possono usufruire dei servizi della stazione.

Mostra nomi stazioni: in prossimità di ogni stazione ne viene visualizzato il nome.

Visualizza barre di attesa: in prossimità di ogni stazione viene visualizzata una barra contenente informazioni su tutti i prodotti che vi sono in attesa.

Pedoni in città: abilita la visualizzazione dei pedoni che transitano sulle strade delle aree urbane.

Pedoni alle fermate: abilita la visualizzazione dei pedoni quando un veicolo arriva ad una fermata.

Densità di traffico: imposta il numero di auto private che circolano nelle aree urbane.
Il numero di auto private cresce al crescere della dimensione della città ed alla densità di traffico impostata. Impostando densità di traffico a 0 non verranno generate auto private.

{Consiglio: Maggiori opzioni e valori di default al caricamento di simutrans possono essere modificati in simuconf.tab}.


Display informativo:

Sotto alle impostazioni sono visualizzate delle informazioni sulle prestazioni di simutrans durante il suo funzionamento.
I numeri sono normalmente visualizzati in bianco, se sono gialli o rossi occorre modificare le impostazioni.
Modifiche alla velocità di scorrimento del gioco, T, (utilizzando l'icona Fast Forward >> in alto alla finestra di gioco, o [,]/ [.]) può modificare il colore dei numeri.

Tempo del frame: tempo tra tra un frame e il successivo.

Inattivi: quando è maggiore di 0 il computer può eseguire altri software.

FPS: valori elevati significano che i veicoli scorrono più fluidamenti. Se il numero rimane rosso significa che il computer è troppo lento per le attuali impostazioni, provate a ridurre la dimensione della finestra di gioco.

Sim: se il numero rimane rosso significa che il computer è troppo lento per la mappa attuale, provate con una mappa più piccola o con meno aree urbane.

simutrans-124.5/simutrans/text/it/finances.txt000066400000000000000000000025341520675460500215440ustar00rootroot00000000000000Aiuto - Finanze

Finanze

Attraverso questa finestra si può vedere lo stato delle proprie finanze, accessibile premendo "f" o cliccando sull'immagine sulla destra della toolbar.
Qui si trovano le informazioni relative alle finanze degli ultimi due anni, con entrate e uscite.

Le voci sono le seguenti:

Costruzioni - Qui c'è tutto ciò che è stato costruito durante l'anno, inclusi segnali, binari e uffici postali.

Nuovi veicoli - Qualunque veicolo comprato, di qualunque tipo.

Costi percorrenza veicoli - I costi di tutti i veicoli in movimento. I costi di ogni veicolo possono essere visti dalle finestre di dialogo dei depositi (fra parentesi, vicino al prezzo di acquisto).

Entrate - Entrate totali derivanti dai trasporti di merci e passeggeri.

Totale - Il totale delle entrate e delle uscite dell'anno. E' la somma di tutte le voci precedenti.

Saldo - Il bilancio totale della compagnia. E' il denaro disponibile per nuovi investimenti. Questa informazione si può trovare anche nella barra di stato sul bordo inferiore dello schermo.

simutrans-124.5/simutrans/text/it/haltlist.txt000066400000000000000000000112561520675460500216030ustar00rootroot00000000000000Elenco stazioni/fermate

Elenco delle stazioni/fermate

Elenco stazioni/fermate visualizza le informazioni relative alle stazioni/fermate dove è possibile caricare e scaricare passeggeri ed i beni, permette inoltre di filtrare i dati.

Per aprire: click sullo strumento Liste. Elenco stazioni/fermate ha quattro bottoni tra cui scegliere, due sono utilizzati per ordinare la lista di oggetti mentre gli altri due sono utilizzati per applicare dei filtri agli oggetti da visualizzare. Sotto ai bottoni si trova una lista di fermate selezionate secondo i criteri impostati nel filtro.
Click su uno degli elementi di Elenco stazioni/fermate per visualizzare maggiori informazioni riguardanti la fermata.
{Consiglio: se l'elenco di fermate è vuoto provate a modificare le opzioni del filtro. Se l'elenco è parziale, provate a ridimensionare Elenco stazioni/fermate o scorrete l'elenco con la barra di scorrimento.}

Premendo i tre bottoni a sinistra ci si muove all'interno delle possibili opzioni (il nome del bottone indica l'opzione attualmente selezionata), il bottone proprietà apre invece una maschera contenente ulteriori opzioni.

Ordina per: contiene due bottoni utilizzati per odrinare le fermate visualizzate nell'elenco:

Bottone di sinistra:
- Tipo: ordina in base al tipo di veicolo.
- Nome: ordina alfabeticamente utilizzando i codici ASCII (le lettere maiuscole si ordinano prima delle lettere minuscole ) dei nomi assegnati.
- Attesa: ordina in base alla quantità di passeggeri e beni presenti mostrando anche gli oggetti in attesa e il tipo di servizio offerto.
Bottone di destra:
- ascend. / discend.: inverte l'ordine dell'elenco.

Filtro: Due bottoni di opzioni, per abilitare / disabilitare i criteri di selezione e modificare i criteri di selezione degli oggetti.

Bottone di sinistra
- abilita / disabilita: abilita / disabilita l'utilizzo dei criteri del bottone proprietà.
Bottone di destra
- Proprietà: apre la finestra dei controlli per modificare i criteri si selezione.

Gli elementi elencati per ognuna delle fermate includono:

Barra con il colore di stato: i colori indicano quanto la fermata è sovraffollata. La stessa barra di colore è utilizzata nelle informazioni della fermata e Gestione linee e sono gli stessi colori visualizzati nella barra di colore in corrispondenza delle fermate nella finestra principale del gioco:
- giallo: fermata senza servizio, quindi non c'è nulla in attesa.
- verde: fermata non sovraffollata.
- arancio: fermata leggermente sovraffollata.
- rosso: fermata sovraffollata in cui si verifica una delle tre seguenti condizioni; sovraffollamento oltre 1.5 volte la capacità della fermata, fermata con più di 200 passeggeri malcontenti, fermata con fabbrica che ha bloccato la produzione a causa della congestione.
{Consiglio: Usa [!] per modificare l'aspetto delle barre di colore in corrispondenza delle fermate nella schermata principale del gioco.}

Nome: il nome assegnato alla fermata {Consiglio: In simuconf.tab si trovano delle opzioni per assegnare dei numeri alle fermate}.

Icone dei veicoli: indicano quali tipi di veicolo utilizzano la fermata (utilizzati anche nelle informazioni delle fermate e gestione linee).
Le icone icludono: bus (per veicoli passeggeri su strada), camion (per il trasporto di beni su strada), treni, navi ed aereoplani; i tram possono essere indicati dall'icona di un treno o di un bus a seconda del tipo di fermata.

Icone merci indicano quali elementi (passeggeri, posta, beni) la fermata può gestire (utilizzati anche nelle informazioni delle fermate e gestione linee).
{Consiglio: aggiungendo appositi edifici di estensione le categorie degli elementi gestiti dalla fermata possono aumentare; ad esempio gli Uffici postali possono essere aggiunti alla fermata per permetterle di gestire la posta}.

Attesa: dettaglio dei passeggeri e merci in attesa alla fermata.

simutrans-124.5/simutrans/text/it/language.txt000066400000000000000000000017261520675460500215430ustar00rootroot00000000000000Aiuto - Scelta lingua

Finestra di dialogo scelta lingua

Questa finestra di dialogo permette di scegliere la lingua di Simutrans. Se si seleziona la lingua prima di creare una nuova mappa, i nomi delle città saranno creati in base a quella lingua. Si può cambiare la lingua in ogni momento: i contenuti delle finestre aperte non verranno sempre aggiornati immediatamente, ma solo dopo la loro chiusura e riapertura. Molti testi sono invece aggiornati immediatamente.

Se si preferisce avere i nomi delle città nella propria lingua, è necessario selezionarla PRIMA di iniziare un nuovo gioco.

Se la propria lingua non è supportata, alcune ulteriori traduzioni sono disponibili al seguente indirizzo: http://www.simutrans.de/english/download/simu/download.html ma purtroppo potrebbero essere incomplete o non aggiornate.

Per aiutare a tradurre Simutrans nella tua lingua,
visita https://translator.simutrans.com

simutrans-124.5/simutrans/text/it/load.txt000066400000000000000000000004661520675460500206770ustar00rootroot00000000000000Aiuto - Caricamento

Caricare un gioco salvato

Quando si seleziona l'opzione di caricamento, appare una nuova finestra che mostra i nomi dei giochi salvati precedentemente. Facendo clic sul nome del file del gioco che si vuole caricare questo verrà caricato e fatto partire.

simutrans-124.5/simutrans/text/it/mailbox.txt000066400000000000000000000046441520675460500214150ustar00rootroot00000000000000Aiuto centro messaggi e sue opzioni

Centro messaggi

Il centro messaggi riporta i messaggi dal gioco e apre i controlli per selezionare quali sono visualizzati nel gioco.

Clicca sull'icona del centro messaggi in cima alla vista di gioco o premi [B] per aprire Message Centre, che elenca i messaggi dal gioco.
Clicca su un messaggio per muovere la vista di gioco sulla relativa posizione.

Il tasto Opzioni (clicca per usarlo) apre le Opzioni centro messaggi che controlla quali messaggi possono essere visualizzati.


Opzioni centro messaggi

Opzioni centro messaggi ha tre colonne di bottoni quadrati che stabiliscono come sono visualizzati i messaggi.

Da sinistra a destra, le colonne indicano come sono presentati i messaggi: come testo scorrevole in fondo alla vista di gioco, messaggio temporaneo o messaggio permanente.
{Consiglio: clicca sull'angolo in alto a sinistra del messaggio o usa la tastiera per chiudere i messaggi permanenti}

Clicca sui bottoni quadrati per selezionare le opzioni dei messaggi (i bottoni sono evidenziati quando selezionati) per i seguenti messaggi:

Anno nuovo: un nuovo anno è iniziato.

Notizie AI: un giocatore gestito dal computer ha aperto una nuova linea.

Notizie città: un'area urbana sta costruendo un municipio più grande.

Nessuna via: un veicolo non può trovare una via per la sua prossima destinazione (a Fermata o punto di transito).

Nuove industrie: una nuova catena industriale (un consumatore finale e ogni fornitore necessario) è stata costruita per la crescita della città.

Turismo: sono stati costruiti un'attrazione o un memorial.

Nuovi veicoli: un nuovo veicolo è disponibile o ritirato (se è utilizzata la linea temporale).

Stazione piena: una fermata è troppo piena.

Problemi: la società di un giocatore è in debito o altri messaggi di problemi.

simutrans-124.5/simutrans/text/it/map.txt000066400000000000000000000010471520675460500205310ustar00rootroot00000000000000Aiuto - Mappa

Finestra Mappa

La finestra mappa è visualizzabile sia premendo 'm' sia cliccando sul pulsante relativo nella toolbar.

La mappa permette di vedere il mondo in una vista ridotta e verificare la posizione di diversi elementi come ad es. fabbriche, ferrovie, strade e città.

E' possibile muoversi in ogni parte del mondo semplicemente cliccando nel punto della mappa corrispondente al luogo in cui si vuole andare; il gioco centrerà la vista generale ingrandita sul posto selezionato.

simutrans-124.5/simutrans/text/it/new_world.txt000066400000000000000000000053261520675460500217600ustar00rootroot00000000000000Aiuto - Nuovo Mondo

Finestra di dialogo Nuovo Mondo

Attraverso questa finestra di dialogo si possono cambiare i parametri che determinano le caratteristiche della mappa che si sta creando per iniziare un nuovo gioco. Inoltre, nell'angolo in alto a sinistra appare una finestra per la selezione della lingua di gioco.

Il primo parametro è il numero della mappa. Esso determina il 'layout' della mappa, ad es. quantità e dislocazione dell'acqua, delle colline, degli alberi, la posizione delle città e delle fabbriche. Viene mostrata un'anteprima per ogni mappa. Il numero di mappa di default è 33.

Le dimensioni della mappa determinano quanto grande essa sarà. E' possibile impostare un valore compreso fra 128x128 e 576x576 quadrati. Il default è 256x256.

La densità delle industrie determina quante fabbriche saranno inserite nella mappa. Più bassa è la densità e più difficile diveterà il gioco, poichè ci saranno meno possibilità di trasporto di beni. Il default è 125.

Il numero di città indica il numero di insediamenti urbani che verranno inseriti nella mappa e può variare da 2 a 64. Il default è 16.

La densità del traffico determina il numero di auto private che circoleranno nelle città. Il default è 8.

Il livello dell'acqua determina quanto acqua sarà presente nella mappa. Più basso è questo valore e più terreno sarà presente, e viceversa. Il default è 4.

L'altezza delle montagne indica l'altitudine della più alta montagna della mappa. Il default è 160. L'asprezza del terreno indica quanto pianeggiante sarà la mappa. Più questo valore è basso e meno colline e valli saranno presenti. il range di valori fra cui scegliere varia da 1 a 3. Il default è 2.

Il pulsante "Mappa casuale" selezionerà un numero di mappa a caso. Questa operazione non modifica qualsiasi altra opzione che si avesse selezionato precedentemente.

Il bottone "Mostra pedoni" determina se i pedoni verranno mostrati quando arrivano con un treno o un autobus. E' possibile cambiare questa impostazione in ogni momento anche durante il gioco attraverso la finestra di dialogoSchermo.

Il pulsante "Mod. giorno e notte" abilita o disabilita la funzione di simulazione dell'alternarsi di luce e buio. E' possibile cambiare questa impostazione in ogni momento anche durante il gioco attraverso la finestra di dialogoSchermo.

Il pulsante "Carica gioco" permette di scegliere un gioco da caricare. Il pulsante "Inizia gioco" fa iniziare un nuovo gioco creando una nuova mappa secondo i parametri indicati. Il pulsante "Esci" termina immediatamente l'esecuzione di Simutrans.

simutrans-124.5/simutrans/text/it/options.txt000066400000000000000000000016161520675460500214510ustar00rootroot00000000000000Aiuto - Opzioni di gioco

Opzioni di gioco

Questa finestra di dialogo è usata per modificare gran parte delle opzioni del gioco. Molte di queste sono raggruppate in sotto-finestre che possono essere aperte agendo sui pultanti relativi. Questa finestra è anche utilizzata per caricare, salvare e uscire dal gioco.

Lingue - Si sceglie la lingua di Simutrans
Colore giocatore - Si sceglie il colore della propria compagnia
Schermo - Si impostano le proprietà di visualizzazione
Audio - Si impostano le proprietà audio
Giocatori - Si imposta il numero di intelligenze artificiali (AI) nel gioco
Carica - Si carica un gioco salvato
Salva - Si salva il gioco in corso
Nuova mappa - Si crea una nuova mappa
Esci - Si esce dal gioco

Per ulteriori informazioni, si vedano le finestre di aiuto per ogni singola sotto-finestra.

simutrans-124.5/simutrans/text/it/players.txt000066400000000000000000000006501520675460500214320ustar00rootroot00000000000000Aiuto - Lista Giocatori

Lista Giocatori

Con questo menù si può vedere come stanno andando le AI concorrenti, con la possibilità di attivare o disattivare ognuna di esse.

Alla destra del colore di ogni concorrente è mostrato il relativo bilancio. Per attivare o disattivare i concorrenti, si selezioni il bottone relativo. Il concorrente attivo per default è quello turchese.

simutrans-124.5/simutrans/text/it/railtools.txt000066400000000000000000000073101520675460500217630ustar00rootroot00000000000000Ferrovie

Finestra Strumenti Ferrovie

Questa finestra contiene gli strumenti per costruire binari, segnali, tunnel, depositi, passaggi a livello e stazioni.

Nell'angolo in alto a sinistra c'è il tool per costruire i binari. Per costruire una ferrovia, prima si faccia clic sulla casella di partenza e quindi su quella di arrivo. Simutrans costruirà un binario fra le due caselle. Per costruire il binario verrano usate solo caselle libere: non verranno attraversate strade, costruiti ponti o tunnel. Se nessuna connessione è possibile, non accade nulla.

Il prossimo tool alla destra del precedente è quello per piazzare segnali ferroviari. I segnali sono usati per vincolare il movimento dei treni. E' possibile usarli per permettere a due treni di percorrere lo stesso tratto di binario, piazzandoli ogni poche caselle lungo la linea interessata. Per costruire un segnale si deve fare clic nel punto della linea in cui lo si vuole piazzare. I segnali appaiono ai lati delle caselle, ma in realtà viene messo un segnale su ogni bordo delle caselle adiacenti. Questo è importante in quanto una casella in cui è presente un segnale non può contenerne un altro. I segnali di senso unico possono essere usati per costringere un treno ad andare in una data direzione. Per costruire un segnale di senso unico basta piazzare un normale segnale ferroviario e fare clic su di esso. Con ulteriori click del mouse si cambierà la direzione e infine si ritornerà ad avere un normale segnale ferroviario. Attenzione: un segnale errato potrebbe impedire ai treni di muoversi come desiderato! Per rimuovere un segnale si deve usare il tool di rimozione sul segnale stesso o sui suoi bordi; questo rimuoverà il segnale lasciando intatti i binari.

L'ultimo simbolo della prima riga permette di costruire i tunnel. I tunnel sono utilizzati per creare una linea ferroviaria che attraversa una montagna, evitando quindi di doverla aggirare o costruire percorsi in salita. Per costruire un tunnel è necessario prima predisporre i binari ai lati della montagna, entrambi allo stesso livello. Quindi basta fare clic su una delle due estremità per connetterle con un tunnel.

Sotto il tool tunnel è presente quello per i passaggi a livello. Gli attraversamenti sono utilizzati per permettere intersezioni con eventuali strade. Per costruirne uno, è sufficiente fare clic nel punto della linea ferroviaria in cui si desidera crearlo.

Alla sinistra del precedente, c'è il simbolo per la costruzione di un deposito. I depositi sono usati per comprare e gestire i veicoli ferroviari, motrici e vagoni. Per costruire un deposito basta fare clic ad una estremità di una linea ferroviaria già esistente.

L'ultimo simbolo è quello delle stazioni. Le stazioni sono usate dai treni per caricare e scaricare merci e passeggeri e possono essere di qualsiasi forma e dimensione. Attualmente, le stazioni possono contenere un numero infinito di unità di merci, ma le fabbriche vi spediscono merci solo se sono già in attesa meno di 1000 unità. Le stazioni sono costituite da singoli segmenti. Per costruire un segmento si faccia clic sulla casella in cui lo si vuole mettere. Le stazioni possono essere costruite sopra binari già esistenti, ma non possono essere messe su colline, curve o intersezioni. Ogni segmento costruito adiacente ad una stazione già esistente diverrà parte integrante di quella stazione. Ogni segmento può ospitare un massimo di 2 veicoli ferroviari, quindi ci si assicuri che la lunghezza delle stazioni sia sufficiente a contenere il più lungo treno che vi passa: i vagoni che rimangono fuori da una stazione non vengono nè caricati nè scaricati!

simutrans-124.5/simutrans/text/it/removal_tool.txt000066400000000000000000000043331520675460500224570ustar00rootroot00000000000000Strumento rimozione

Strumento rimozione

Lo strumento rimozione è usato per distruggere e rimuovere gli elementi non voluti e gli ostacoli alle costruzioni sul singolo quadrato nella vista di gioco.

Utilizzo: clicca sull'icona con la croce rossa (bulldozer nel pak128) in cima alla vista di gioco o premi [a] per aprire lo strumento di rimozione (cambia il cursore in una croce rossa sopra una nuvola di polvere).
Posizionare il cursore sulla vista di gioco e un bordo giallo indicherà la posizione dove lo strumento farà effetto (le coordinate sulla mappa saranno riportate nella barra in fondo). Poi clicca sull'elemento nella vista di gioco per rimuovere.

Il costo dell'operazione è riportato nella vista di gioco sotto il sito di costruzione. Esso sarà uguale al costo di costruzione.
Il costo di distruzione degli edifici cittadini è riportato nella finestra di dialogo aperta con lo strumento ispezione. Per tagliare un albero sono necessari 100 crediti.

Strumento rimozione può essere usato per rimuovere pedoni e auto cittadine.

Gli elementi presenti nel singolo quadrato di gioco sono rimossi nell'ordine: pedoni o auto private, segnali, fermate o piattaforme, elettrificazioni, strade.

{Consigli: per rimuovere un veicolo di un giocare, venderlo da Dettagli convoglio.
Per rimuovere industrie, attrazioni o municipi passare prima al giocatore servizio pubblico. Nota: L'uso dello strumento rimozione su un municipio rimuove tutti gli edifici cittadini dall'area urbana.
Usa [Ctrl] per applicare lo strumento ad un livello superiore.
Per distruggere/rimuovere le cose di un altro giocatore, selezionarlo prima nella finestra giocatori.
Per rimuovere segmenti di binario o strada usare rimuovi binari (presente anche tra gli strumenti per tram) o rimuovi strade.}

simutrans-124.5/simutrans/text/it/roadtools.txt000066400000000000000000000044541520675460500217670ustar00rootroot00000000000000Aiuto - Strumenti Strade

Strumenti Strade

Con questa finestra di dialogo si può avere accesso a tutti gli strumenti necessari per costruire strade, fermate dell'autobus, tunnel, depositi, passaggi a livello e piazzali di carico.

Nell'angolo in alto a sinistra è presente il simbolo che identifica lo strumento per costruire le strade. Per iniziare a costruire una strada bisogna prima cliccare sul punto di inizio, quindi spostarsi sul punto in cui la si vuole far terminare e cliccare nuovamente con il tasto sinistro del mouse: verrà costruita una strada fra i due punti indicati.

Proseguendo a destra, troviamo il simbolo che contraddistingue lo strumento per la costruzione delle fermate dell'autobus. Queste fermate sono usate per permettere agli autobus di caricare i passeggeri in attesa. Ogni fermata ha la capacità di due autobus, una per ciascuna direzione. Per costruirne una basta cliccare nel punto in cui si desidera posizionarla. Possono essere costruite sulle strade ma non in curve o incroci.

L'ultimo simbolo della prima riga indica lo strumento per costruire tunnel. I tunnel vengono utilizzati per creare una strada diretta attraverso una montagna, evitando di doverla aggirare o scalare (le strade tortuose e/o in salita rallentano molto i veicoli). Per costruire un tunnel, per prima cosa occorre aver costruito le due strade ai lati della montagna (devono essere allo stesso livello). Cliccando su una delle due terminazioni il tunnel verrà automaticamente costruito.

Sotto questo simbolo c'è quello per il passaggio a livello. Viene usato per consentire ai treni di attraversare le strade. Per costruirne uno, è sufficiente cliccare nel punto di una strada già esistente in cui lo si desidera piazzare.

Alla sinistra di questo simbolo c'è quello per costruire i depositi. I depositi sono gli edifici in cui è possibile comprare tutti i veicoli stradali, come furgoni, bus, camion e rimorchi. Per posizionare un deposito basta cliccare sull'estremità di una strada già esistente.

L'ultimo simbolo presente è il piazzale di carico. Sono usati dai camion per caricare e scaricare le merci; sono in grado di ospitare un numero massimo di due veicoli. Per costruirne una il procedimento è lo stesso dei depositi (vedi sopra).

simutrans-124.5/simutrans/text/it/save.txt000066400000000000000000000010171520675460500207070ustar00rootroot00000000000000Aiuto - Salvataggio

Salvataggio di un gioco

Questa finestra elenca tutti i nomo di file dei giochi salvati precedentemente. Se si vuole sostituire un file esistente, si faccia clic sul bottone sul quale vi compare il nome: Simutrans salverà il gioco con quel nome.

Se si vuole invece salvare il gioco con un nuovo nome, si selezioni con il mouse la casella di inserimento e vi si scriva dentro il nuovo nome. Cliccando il bottone "Salva", il gioco verrà salvato con il nuovo nome.

simutrans-124.5/simutrans/text/it/scenario.txt000066400000000000000000000005751520675460500215640ustar00rootroot00000000000000Aiuto - Scenario

Scenari

Simutrans permette di giocare utilizzando scenari, la vincita negli scenari si ottiene raggiungendo determinati obbiettivi quali ad esempio: raggiungere una certa ricchezza entro un determinato lasso di tempo, erigere un quartier generale monumentale, connettere tra loro determinate fabbriche etc. etc.

simutrans-124.5/simutrans/text/it/shiptools.txt000066400000000000000000000026521520675460500220030ustar00rootroot00000000000000Aiuto - Strumenti Imbarcazioni

Finestra Strumenti Imbarcazioni

Questa finestra dà accesso a tutti i tool con i quali si possono costruire depositi di imbarcazioni e porti.

Il primo strumento a sinistra è quello per costruire un porto. Selezionandolo, il cursore si trasforma in un porto. I porti sono utilizzati per consentire alle imbarcazioni di caricare e scaricare merci e passeggeri provenienti da fabbriche e città vicine. Per costruire un porto, si faccia clic su un bordo (rettilineo) di costa: verrà creato un porto composto da due caselle. Si possono costruire porti anche molto grandi, tanto quanto la conformazione delle coste lo permette, ripetendo l'operazione precedente su porzioni di costa adiacenti. Per indirizzare una imbarcazione in un porto si deve mandarla in una casella adiacente al porto, non al porto stesso! Attenzione: in caso di errore, i porti non possono ancora essere rimossi del tutto!

Sull'ultima riga ci sono due simboli, entrambi usati per costruire depositi: la differenza fra i due sta nel fatto che quello a sinistra si affaccia sulla sinistra, l'altro sulla destra. Selezionando uno di essi, il cursore assume la forma di un deposito. I depositi sono usati per costruire tutti i possibili tipi di imbarcazioni che sono disponibili. Per posizionare un deposito è sufficiente fare clic sulla porzione d'acqua in cui lo si vuole mettere.

simutrans-124.5/simutrans/text/it/signal_spacing.txt000066400000000000000000000026661520675460500227450ustar00rootroot00000000000000Aiuto - Spaziatura automatica dei segnali

Spaziatura automatica dei segnali

Spaziatura automatica dei segnali permette di piazzare su di un percorso, in maniera automatica, una serie di segnali equispaziati tra loro.

Metodo di utilizzo:

-- Ctrl-click su un segnale fa apparire l'interfaccia.
-- Click-and-drag lungo il tracciato mostra la preview dei nuovi segnali, rilasciando il bottone i segnali diventeranno definitivi. I segnali saranno costruiti permettendo ai treni di muoversi lungo il percorso solo nella direzione che si è usata per trascinare i segnali.
-- Gioco in rete: per prima cosa ctrl-click sul tracciato e poi rilasciare il bottone del mouse. Premere il tasto e muovere il mouse per vedere l'anteprima. Rilasciare il bottone per costruire.

Parametri:

-- Spaziatura dei segnali: numero di piastrelle consecutive oltre le quali verrà piazzato il segnale (i percorsi in diagonale valgono 1/2 piastrella). Questo valore è visualizzato sull'icona selezionando il segnale.
-- Rimozione segnali intermedi: se selezionato tutti gli altri segnali sul percorso saranno cancellati.
-- Rimpiazza gli altri segnali: se selzionato i segnali in corrispondenza dei nuovi segnali saranno cancellati.

Questi parametri possono essere impostati per tutti i tipi di segnale in modo indipendente.

simutrans-124.5/simutrans/text/it/sound.txt000066400000000000000000000006651520675460500211110ustar00rootroot00000000000000Aiuto - Suono & Musica

Proprietà di Suono & Musica

Da questo menù è possibile impostare il volume dei suoni, della musica e quale colonna sonora ascoltare durante il gioco.

Per cambiare il volume dei suoni o della musica si può agire sul controllo a scorrimento (sinistra = volume più basso). Per cambiare la colonna sonora è invece sufficiente agire sulle frecce in fondo alla finestra.

simutrans-124.5/simutrans/text/it/station.txt000066400000000000000000000030031520675460500214270ustar00rootroot00000000000000Fermata/Stazione

Informazioni Fermata/Stazione

Attraverso questa finestra di dialogo si possono avere informazioni su una specifica fermata/stazione su cui si è cliccato. Si possono vedere le merci in attesa, quali merci sono necessarie alle fabbriche vicine e quali altre fermate e stazioni sono connesse direttamente a questa.

E' possibile anche cambiare il nome della fermata/stazione utilizzando il box vicino al top della finestra, dove il nome è visualizzato. Per farlo, si deve cliccare il box e editare il nome, premendo Invio quando si ha finito.

Al fondo della finestra di dialogo ci sono le informazioni relative ai beni in attesa alla fermata/stazione. Questi sono strutturati in modo che ci siano delle voci principali, una per ogni tipo di merce, per es.
402 auto in attesa
83 passeggeri in attesa

Quindi ognuna di queste voci è divisa in sotto-voci che indicano dove le merci sono dirette e l'esatto ammontare di ognuna, per es:

56 passeggeri in attesa
- 25 passeggeri > Stazione Collevecchio
- 29 passeggeri > Stazione Boscocorto via Pratofiorito ferm. est.
- 2 passeggeri > Castelforte ferm. C.so Roma

Nell'angolo in alto a destra della finestra c'è un bottone etichettato 'Dettagli'. Si clicchi su di esso per aprire una finestra separata che illustra le merci richieste da fabbriche vicine e tutte le rotte dirette a partire da questa stazione/fermata.

Le rotte dirette sono divise per tipo di merci.

simutrans-124.5/simutrans/text/it/station_details.txt000066400000000000000000000032461520675460500231450ustar00rootroot00000000000000Dettagli fermata

Dettagli fermata

Dettagli fermata fornisce più informazioni riguardo una fermata (dove i veicoli caricano e scaricano beni e passeggeri).

Dettagli fermata si apre dal bottone Dettagli nelle informazioni su di essa.
Se non sono visibili tutti gli elementi, ridimensiona Dettagli fermata o usa gli slider per muoverli.

Le informazioni riportate includono:
Fabbriche connesse: nomi e coordinate sulla mappa (tra parentesi) delle industrie nell'area circostante la fermata.
{Consigli: usa [v] per visualizzare le aree circostanti le fermate nel gioco.}

Merci necessarie alle fabbriche vicine: tipi di beni richiesti dalleindustrie nella zona circostante la stazione.

Linee che servono la fermata: Linee che usano la stazione.
Le linee sono elencate alfabeticamente nell'ordine dei caratteri ASCII (le maiuscole prima della minuscole).

Tragitti diretti da qui: elenca i nomi delle fermate che possono essere raggiunte senza cambiare linea e i tipi di beni (merci o passeggeri) che possono essere trasportati.
Le linee sono elencate alfabeticamente nell'ordine dei caratteri ASCII (le maiuscole prima della minuscole) secondo il tipo di trasporto, nome della fermata e tipo di bene che può essere trasportato (che è anche visualizzato per ogni stazione).

simutrans-124.5/simutrans/text/it/trafficlight_info.txt000066400000000000000000000004051520675460500234320ustar00rootroot00000000000000Aiuto - Semafori

Semafori

E' possibile controllare la durata del verde e del rosso dei semafori. I numeri che si possono impostare sono la durata del verde (in secondi) per le direzioni nord/sud ed est/ovest.

simutrans-124.5/simutrans/text/it/window.txt000066400000000000000000000114651520675460500212700ustar00rootroot00000000000000Aiuto uso interfaccia di gioco

Interfaccia di gioco

In Simutrans si gioca attraverso l'Interfaccia di gioco che contiene le informazioni sul gioco corrente, l'accesso alle barre degli strumenti e controlli di gioco e una vista della mappa.

Barra del titolo: nella parte sinistra della barra del titolo dell'Interfaccia di gioco è riportata la versione e la data di rilascio di Simutrans che sta girando.
La parte destra contiene i controlli per minimizzare e massimizzare l'Interfaccia di gioco e per uscire da Simutrans.

Icone: una riga di icone, usata per accedere alle barre degli strumenti e dei controlli mentre si gioca a Simutrans, è direttamente sotto la barra del titolo. Icone (cliccare per utilizzarle) sono raggruppate in tre set:

- Menù principale include:
- floppy-disc: apre Opzioni di gioco
- map: apre Mappa
- lente d'ingrandimento: seleziona Visualizza
- modifica terreno: apre la barra terreno
- treno: apre la barra ferrovie/treni
- monorotaia/maglev: apre la barra dei trasporti su monorotaia
- tram: apre la barra dei tram
- camion: apre la barra trasporti su strada
- nave: apre la barra trasporti su acqua
- aeroplano: apre la barra trasporti aerei
- pallino rosso dentro cerchio giallo (gru in pak128): apre la barra delle costruzioni speciali
- croce rossa (bulldozer in pak128): seleziona lo strumento di rimozione

- Menù gestione include:
- rete: apre la gestione linee
- lista: apre la lista
- cassetta delle lettere: apre il centro messaggi
- money-icon: opens Finances

- Altre opzioni include:
- macchina fotografica: cattura il contenuto dell'Interfaccia di gioco (senza la barra del titolo) e la salva come file bitmap nella cartella simutrans/screeshot/
{Consiglio: le immagini possono essere catturate premendo [c].}
- tazzina di caffè: mette o toglie dalla pausa il gioco.
{Consiglio: può essere usato anche il tasto [p]}.
- >>: seleziona e deseleziona l'accelerazione del tempo. (cambia la velocità con cui T passa nel gioco).
{Consiglio: può essere usato anche il tasto [W]
IMPORTANTE: il gioco non può essere rallentato;Display Informationindica se il computer può resistere al cambiamento.}
- ?: apre Aiuto Simutrans

La vista di gioco di una porzione della mappa corrente è rappresentata sotto le icone.
Puoi ingrandire o rimpicciolire la vista o muoverti per la mappa utilizzando il mouse o la tastiera.

Una barra di scorrimento temporanea appare in fondo e riporta i messaggi.
{Consigli: clicca sul messaggio per centrale la relativa posizione.}

Una barra in fondo riporta le informazioni riguardanti la mappa corrente.
Le informazioni riportate, da sinistra a destra, includono:
data e ora: data e ora correnti, a cui seguono un'icona e il nome della stagione.
bilancio: soldi disponibili per le costruzioni e altre spese (acquisto veicoli, mantenimento infrastrutture e costi operativi dei veicoli).
coordinate: indicano la posizione del cursore nella vista di gioco. Sono riportate le coordinate X e Y e l'altezza.
T: indica la velocità a cui passa il tempo nel gioco.
{Consigli: Usa [.] / [,] per accelerare/decelerare il tempo, T ; Visualizza informazioniindica se il computer può resistere al cambiamento.
L'uso di scorrimento veloce sostituisce T con ">>" on bottom-bar.}
linea del tempo: indica se la linea del tempo è stata selezionata nel gioco corrente.

simutrans-124.5/simutrans/text/ja.tab000066400000000000000000002036311520675460500176640ustar00rootroot00000000000000§Japanese PROP_FONT_FILE m+10r.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: ja Japanese # # Encoding: UTF-8 # # Font: m+10r.bdf # # Date Created: 18.04 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times 時刻を月の「日 時:分」形式で表示 AMBIENT_SOUND 環境音の音量 CASH_SOUND 売買の音量 cl_btn_filter_disable 無効 cl_btn_filter_enable 有効 cl_btn_filter_settings 設定 cl_btn_sort_asc 昇順 cl_btn_sort_desc 降順 cl_btn_sort_id 番号 cl_btn_sort_income 収益 cl_btn_sort_name 名前 cl_btn_sort_type タイプ clf_btn_alle 全選択 clf_btn_invers 反転 clf_btn_keine 全解除 climate area percentage 気候区分の割合 Continue Game 前回の続き CROSSING_SOUND 踏切の音量 FACTORY_SOUND 産業施設の音量 Find matching convois 一致する編成を検索 gl_btn_sort_bonus ボーナス順 gl_btn_sort_name 名前順 gl_btn_sort_revenue 収入順 gl_btn_unsort ソート無し height based 標高の気候 hl_btn_filter_disable 無効 hl_btn_filter_enable 有効 hl_btn_filter_settings 設定 hl_btn_sort_asc 昇順 hl_btn_sort_desc 降順 hl_btn_sort_name 名前順 hl_btn_sort_type タイプ hl_btn_sort_waiting 待機 hlf_btn_alle 全選択 hlf_btn_invers 反転 hlf_btn_keine 全解除 humidities 湿潤基準 Install インストール Lake 湖 Lake height 湖面水位 Maximize height levels 高さを最大化する moisture land 陸地の湿潤度 moisture water 川、池周辺の湿潤度 Networks 路線図 Num pad keys always move map Numlockを無視する Open Sea 海 Queueing 待機の増減 rainfall 降雨量 Reselect closes tools 選択済のツールボタンを押すと選択を解除する Return to menu メニューに戻る Road toll 通行料 Scenario シナリオ sea 海洋 Single GUI シングルGUI Sound range: 効果音の再生範囲 Start this as a server このゲームをサーバーとして開始します。 temperature borders 気候区分の気温基準 temperature-humidity based 温湿度 TOOL_SOUND ツールの音量 TRAFFIC_SOUND 乗り物の音量 Transfers 乗降数 Tutorial チュートリアル #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic 極寒気候 desert 砂漠気候 mediterran 地中海性気候 rocky 高山(岩場) temperate 温暖気候 tropic 熱帯気候 tundra ツンドラ気候 #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed シナリオスクリプトのロードに失敗しました A building blocks the construction 建物が建設の妨げになっています。 Bridge blocked by way below or above. 建設予定地の上または下に別の軌道があるため橋梁を建設できません Can't buy obsolete vehicles! 旧型車両は購入できません。 Cannot alter water この窪地にはこれ以上、水を満たすことも排出することもできません。 Cannot build on a double slope! この型の橋梁は緩やかな坂か平らな地面の上からのみ建設できます Cannot built depot here! ここに車庫を\n建てることはできません。 Cannot built this station/building\nin underground mode here. 駅ビル/駅舎は、地下には建設できません。 Cannot connect to the\ncenter of a double slope! ダブルスロープの中間の高さには橋を建設できません。 Cannot create generic line!\nSelect line type by\nusing filter tabs. 汎用の路線を作成することはできません。\nタブで路線のタイプを選んでください。 Cannot create socket ソケットを作成できません Cannot rotate this building! この建物の方向は変えられません。 Client closed connection during transfer データ転送中にクライアントとの接続が切れました。 Convoi handles exhausted! 編成数が上限に達しました。 Convoy already deleted! 編成は既に削除されています! Corrupt file ファイルが破損しているか、不完全です。 Could not open file ファイルを開けません Das Feld gehoert\neinem anderen Spieler\n この場所は\n他のプレイヤーによって\nすでに占拠されています。\n Depots cannot be built on runways! 格納庫を滑走路上に建設することはできません! Depots must be built on flat dead-end way tiles! 車庫は、平坦な道の端にのみ建設できます。 Der Besitzer erlaubt das Entfernen nicht 撤去できません。\nここは他社の所有地です。\n Diese Zusammenstellung kann nicht fahren!\n この編成では\n出発できません!\n Failed to write file ファイルを書き込めませんでした。\nアクセス権限を確認してください。\n Flugzeughalt muss auf\nRunway liegen!\n 駐機場は誘導路上にのみ設置できます! Forbidden by scenario シナリオによって禁止されています。 Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n ここには空港施設を\n建設できません!\n Hier kann kein\nSignal aufge-\nstellt werden!\n ここには信号機を\n設置できません!\n In order to lock the game, you have to protect the public player by password! ゲームをロックするには、まず公共事業にパスワードを設定してください。 Loading a new game will end the current server session! 新しいゲームをロードすると、現在のサーバーセッションは終了します! Lost connection\nto server! サーバーへの接続が切断されました。 Lost synchronisation\nwith server. サーバーとの同期が失われました。 Maglevhalt muss auf\nMaglevschiene liegen!\n リニアはリニア軌道上にのみ\n停車することができます。 Monorailhalt muss auf\nMonorail liegen!\n モノレールはモノレール軌道上にのみ\n停車することができます。 Monorails are not available yet! モノレールはまだ開発されていません! More than one possibility to build this dock found. この港を建設可能な候補が複数あります。 Narrowgaugehalt muss auf\nNarrowgauge liegen!\n ナローゲージの駅は\nナローゲージ軌道上に\n設置してください!\n No bridges over runways! 滑走路をまたぐ橋梁は建設できません。 No curves on runways 滑走路にカーブは作れません! No suitable crossing 適切な交差がありません!\n(最高速度が速すぎるのかもしれません) No suitable way on the ground! 駅は平坦で直線的な\n対応する道の上にしか\n建設できません! No through station here! ここに通り抜け可能な駅を建設できません! Not allowed to copy object. 建設できません!\n(理由: 他のプレイヤーの所有物、または旧式です) Not enough bytes transferred データ転送に失敗しました。 Not enough clearance. 上下に間隔を空ける必要があります。 Not enough empty space 空き容量が足りません。 Not enough money! 資金が不足しています。 Not initialized ライブラリを初期化できません。 On narrowgauge track only!\n ナローゲージ軌道上にのみ設置可能です! Only public player can lock games! 公共事業だけが「プレイヤー変更の禁止」とその解除ができます。\nまたロック解除には公共事業のパスワードが必要です。 Only up and down movement in the underground! 地下のタイルには斜面を作るスロープツールは利用できません\n地下鉄や地下道の高度を変えるには上げ下げするスロープツールを使用してください Out of funds 資金がありません Post muss neben\nHaltestelle\nliegen!\n 駅の拡張施設 (郵便局や倉庫など) は\n既存の駅に隣接した空き地に\n建設してください。\n Protocoll error (expecting game) プロトコルエラー\nProtocoll error (expecting game) Schiffhalt muss im\nWasser liegen!\n 船舶は港の周辺にのみ\n停泊することができます。\n Server busy サーバー使用中\nServer busy Server version too new サーバーで使用されているものより古いバージョンのプログラムです Ship depots must be built on water! 造船所は水上に建設する必要があります! Show all revenue messages 全てのプレイヤーの収益を表示 Show no revenue messages 全てのプレイヤーの収益を非表示 Show only player's revenue 現在のプレイヤーの収益のみ表示 Terraforming not possible\nhere in underground view スロープツールは、\n地下では使用できません。 This tunnel branches. You can try Control+Click to remove. このトンネルは枝分かれしています。Control+クリックで削除できます。 Too far away to merge stations! 駅を統合するには離れすぎています! Unknown error 不明なエラー Upgrade must have\na higher level この駅にはアップグレードできません\n\nよりレベルの高い駅を選択するか、\n一度撤去してから、置き換えてください Vehicle %s cannot choose because stop too short! %s が停車できません。\nホームが短すぎます! Watertable reached これ以上低くできません!\n水面に達しました。 Zughalt muss auf\nSchiene liegen!\n 列車は線路上にのみ\n停車することができます。\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s ヘルプ

総合ヘルプ

*:未訳(英文で表示)

Simutransについて

%1$s

使い方

%2$s

ゲームの開始

%4$s

遊び方

%5$s

ツール

%3$s

その他のダイアログ

%6$s Keyboard Help\n

Keyboard Help

\n キーボードショートカット\n\n

キーボードのヘルプ

\n\n

割り当てられていないキーを押すか、総合ヘルプからこの画面を開くことができます。

\n

アルファベットの大文字と小文字は区別されます。大文字を入力するには [Shift] キーを押しながらキーをを押します。日本語入力は解除してください。

\n

主要なキー設定

\n

\n· [↑, ↓, →, ←]: ゲーム画面を指定の方向にスクロールします。
· [Backspace]: すべてのウィンドウ、ツールバー、ヘルプを閉じます。
· [Delete] または [ESC]: 最前面のウィンドウ(またはツールバー、ヘルプ)を閉じます。
· [PageUp] または []: 画面をズームイン(拡大)します。
· [PageDown] または []: 画面をズームアウト(縮小)します。
· [Ctrl] + 道路/線路/トンネルツール:
- 直線的に道路/線路などを敷設します(既存の路を上書きして敷設する場合も含みます)。
- トンネルの入り口のみを建設します。
- 「情報の取得」や「削除」の際、選択の優先順位を逆にします。
· [Ctrl+V]:
クリップボードからテキストを貼り付けます。(Windows のみ)\n

以下のショートカットキーは、使用中の「(pakフォルダ)/config/menuconf.tab」ファイルで定義されています。
このファイルを編集することで、既存のキー割り当てを変更したり、別のツールに新しいショートカットキーを割り当てたりすることが可能です。

現在の操作キー一覧

At least one stop is connected to the town. 1つ以上の輸送施設が接続されている街だけを表示する At least one tile is connected to one stop. 1つ以上の輸送施設が接続されている産業施設だけを表示する Color according to transport capacity left 色は受け入れ可能な輸送能力の残量を表します。 Color-coded terrain according to altitude. 地形の等高線シェーディングの表示/非表示。 Decrease water height 水位を下げる Highlight railroad tracks 線路をハイライトする Highlite depots 倉庫をハイライトする Highlite electrical transmission lines 送電線をハイライトする Highlite factories 産業をハイライトする Highlite forests 森をハイライトする Highlite tourist attraction 名所をハイライトする Increase water height 海面水位を1上げる Open station/stop details 駅の詳細を表示 Overlay city limits 市域を表示します Overlay passenger destinations when a town window is open 都市ウィンドウが開いている場合、旅客の目的地を表示します Overlay schedules/network スケジュール/ネットワークを表示します Overlay town names 都市名を表示します Please click on the map to add\nwaypoints or stops to this\nschedule. マップをクリックして、中継点または\n駅をスケジュールに追加してください。 Remove double stops from schedule 連続する重複登録を削除します Set tile climate %s 地面タイルの気候を%sに変更します。 Show capacity and if halt is overcrowded 駅容量と過密状態を表示 Show how many convoi reach a station 駅への到着編成数を表示 Show how many people/much is waiting at halts 待機量を表示 Show initial passenger departure 旅客および郵便の発生源を表示 Show level of city buildings 市内建築物の旅客度/郵便度を表示 Show mail service coverage/mail network 郵便事業が行われている範囲を表示 Show passenger coverage/passenger network 旅客事業が行われている範囲を表示 Show speedlimit of ways 軌道/道路の制限速度を表示 Show the change of waiting at halts 先月と比べた待機数の増減を表示 Show the owenership of infrastructure 軌道/道路の所有者を表示 Show transported freight/freight network 輸送された貨物経路の表示 Show usage of network 線路/道路の交通量を表示 Shows a listing with all industries on the map. マップ上に存在する全ての産業のリスト Sum of departure/arrivals at halts 発着合計数を表示します #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s社は\n現在、バスの便を\n%sから\n名所旧跡\n「%s」(%i, %i) に\n提供しています。\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %sは現在、\n%sと工場\n%s (%i, %i) 間で\nバスの運行を行っています。\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %sは\n現在、%i台のトラックで\n%s(%i,%i)と\n%s(%i,%i)の間を\n運行しています。 %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %sは\n%s(%i,%i)と\n%s(%i,%i)の間で\n新たな鉄道を開通しました。 Airline service by\n%s\nnow between\n%s \nand %s.\n %s社は\n現在、航空便を\n%sと\n%s\nの間に提供しています。\n Ferry service by\n%s\nnow between\n%s \nand %s.\n %s社は\n現在、船便を\n%sと\n%s\nの間に提供しています。\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n 旅行者は現在、\n%s社が運行している\n%sと\n%sを結ぶ\nバスを利用しています。 #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS マップ編集ツール LISTTOOLS 一覧表 MAGLEVTOOLS リニアツール MONORAILTOOLS モノレール/リニアツール NARROWGAUGETOOLS ナローツール RAILTOOLS 鉄道ツール ROADTOOLS 道路ツール SHIPTOOLS 船舶ツール SLOPETOOLS スロープツール SPECIALTOOLS 特別建築物 TRAMTOOLS 路面電車/ライトレールツール #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s社は\n(%i, %i)に\n本社を建設しました。 Factory chain extended\nfor %s near\n%s built with\n%i factories. 「%s」は事業を拡大し、\n新たに%s近郊に\n%iの産業施設を建てました。 Net wealth less than 10%% of starting capital! 総資産が開始時の総資産の10%%未満になりました New %s now available:\n%s\n 新型%s\n「%s」が\n新たに使用可能になりました。 New factory chain\nfor %s near\n%s built with\n%i factories. 新しい産業チェーン\n「%s」は%s近郊に\n%iの産業施設を建てました。 New vehicle now available:\n%s\n 「%s」が\n新たに使用可能になりました。 Now %u clients connected. 現在、 %u 人がオンライン中です。 overtaking 車線 Remove vehicle from map. Use with care! ! 注意 ! この乗り物を撤去します ! 注意 ! Screenshot\ngespeichert.\n スクリーンショットを\n保存しました。\n Sends the convoi to the last depot it departed from! この編成を一番近い車庫に向かわせます。 Server preparing game ... サーバーがゲームの準備をしています... Trees 樹木 With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %sに\n新しい記念碑が建立され、\n歓喜に沸く%i人の住民が\n見守る中、盛大な除幕式が\n行われました。 #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter 路線フィルター #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ ... … (%.2f$/km %.2f$/m) (¥%.2f/km ¥%.2f/月) (%.2f$/km) (¥%.2f/km) (%i)- (%i) (in depot) (入庫中) [0] south-facing [0] 南向き [1] east-facing [1] 東向き [2] north-facing [2] 北向き [3] west-facing [3] 西向き [4] southeast corner [4] 南東の角 [5] northeast corner [5] 北東の角 [6] northwest corner [6] 北西の角 [7] southwest corner [7] 南西の角 [DELETE] [DEL] [SCROLLLOCK] [SCROLL LOCK] /month /月 \nBauzeit bis ~ \nBauzeit von \n登場する年: \nCan't open heightfield file.\n \nハイトマップを開けません。\n \ndirection: \n接続方向: \nelektrified \n電化 \nHeightfield has wrong image type.\n \nハイトマップの\n画像形式が正しくありません。\n \nis reserved by: \n予約車両: \nminimum speed: \n最低速度: \nnot elektrified \n非電化 \nRibi (masked) \n進行可能方向: \nRibi (unmasked) \n全接続方向 : \nSet phases: \n信号サイクル(秒)\n東西 / 南北 / オフセット\n切り替わり時間(秒)\n東西 / 南北 \nsingle way \n一方通行 \nway1 reserved by \n予約車両 (Way 1): \nway2 reserved by \n予約車両 (Way 2): \nwith sign/signal\n \n信号/標識あり\n %d buildings\n 建物数: %d棟\n %d convois %d 編成 %d Einzelfahrzeuge im Depot %d台/両が留置されています。 %d more convois 他 %d 編成 %i car(s), %i両、 %i km/h (max. %ikm/h) %i km/h (最高速度 %ikm/h) %i years %i months old. 樹齢: %i年 %iヶ月 %s at (%i,%i) now public stop. %s は公共の乗換駅になりました。 %s building %s %s %s %s前 %s %s city %d %s %s第%d%s %s factory %s %s %s %s前 %s %s has entered a depot. %s\nは車庫に着きました。 %s has left. %s がオフラインになりました。 %s land %d %s %s郊外第%d%s %s now known as %s. %s はプレイヤー名を %s に変更しました。 %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %sは、\n人口が%i人に達したので、\n公共事業の資金を浪費して\n新しい役場を建てました。 %s\nis crowded. %sが\n混雑しています。 %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\n最高速度: %3$ikm/h\n\n現在の速度: %2$ikm/h\n\n %s\nwas liquidated. %s は破産し、\n清算されました。\n全運行は停止され、\nすべての資産が売却されました。 %u Client(s)\n %u 名のクライアント\n %u days ago %u日前 %u hours ago %u時間前 %u minutes ago %u分前 %u Player (%u locked)\n %u のプレーヤー (%u はロック済み)\n %u years ago %u年前 スケジュールクリア <新しい路線を作成> %s - %s
\n [%s]: %s
\n

Error

エラー: メインpakディレクトリ内でオブジェクトが重複しています

発生箇所:

Warning

addons for

警告: アドオンディレクトリ内でオブジェクトが重複しています

対象のアドオン: <個別のスケジュール> <路線未設定> <スケジュールなし> <路線に登録> 1 convoi 1編成 1 Einzelfahrzeug im Depot 1台/両が留置されています。 1 minute ago 1分前 1 year ago 1年前 100 km/h = %i tiles/month 100 km/h = %i タイル/月 1LIGHT_CHOOSE 明るさ: 1WORLD_CHOOSE 新しいゲームの設定: 2LIGHT_CHOOSE カラー: 2WORLD_CHOOSE マップ番号: 3LIGHT_CHOOSE スクロールの速さ: 4LIGHT_CHOOSE スクロールの反転 5LIGHT_CHOOSE 乗降客を表示する 5WORLD_CHOOSE 都市数: 6LIGHT_CHOOSE 歩行者を表示する 6WORLD_CHOOSE 交通量: 8WORLD_CHOOSE 夜間は暗くする A bridge must end on a way! 橋の終端には道路/線路が必要です。 A bridge must start on a way! 橋の始点と終点は、\n同じ種類の道の端か\n何もない坂でなければなりません!\n Abfrage 調査ツール Abnehmer 需要先 About 解説 About scenario Copyright Abriss 撤去/取り壊し Absenken 陸地を低くする Abspanntransformator 変圧所 Accelerate time ゲームスピードを速くする Act. load: %u MW\n 使用電力: %uMW\n Active player only 自社のみ表示 Add forest 森を作成する Add random citycar ランダムな自家用車を配置 add server サーバーを追加 Add Stop 駅を追加 Add stops for backward travel 折り返して戻るための停車駅を追加します air 航空 Aircraft 航空機 aircraft_tab 貨物機 airplane 航空機 Airport 空港 AIRTOOLS 航空機ツール All すべて all convoi tooltips 常に編成の状態を表示 Allow city growth 街を発展させる Allow player change プレーヤーの変更を許可する allowed climates:\n 対応する気候\n Alters a schedule. 運行スケジュールの編集 Angenommene Waren 付近の産業で需要のある物資 anhaengen 追加 Anhaenger_tab トレーラー Anheben 陸地を高くする Appends stops at the end of the schedule スケジュールの最後に駅を追加 Apply Line 路線を適用 Apr. 4月 April 4月 Arbeiter aus: 労働者居住地: Arrivals from\n 到着予想時間\n Arrived 到着 Assets 資産 Aufloesen 編成解除 Aufspanntransformator 変圧所 Aug. 8月 August 8月 auto 自動 Autohalt muss auf\nStrasse liegen!\n 自動車は道路上にのみ\n停車することができます。\n Available 利用可能 Available at custom date 以下の年月で利用可能 Bahndepot 機関庫 Bankrott:\n\nDu bist bankrott.\n 破産:\n\n破産しました。\n battery 蓄電池 Baum 木 baum builder 植樹ツール Baustelle 建設場所 Bauzeit 建設時期 人口数 Beenden ゲーム終了 Beginner mode 初心者モード Besonderes Gebaeude 特殊建築物 BF 駅 bio 生物 Blockstrecke ist\nbelegt\n 閉塞区間に他の列車があります。\n Boden 地表 Bonus Multiplier: %i%% 速達ボーナス倍率: %i%% Bonus Speed: %i km/h ボーナス基準速度: %i km/h Bonusspeed: %i km/h ボーナス適用速度: %i km/h Boost (%%) 増産率 (%%) Borderless (disabled on fullscreen) 全画面表示 (ソフトウェアモード) bridge is too high for its type! この型の橋梁で架橋できる\n橋脚の高さ制限を超えています。 Bridge is too long for this type!\n この型の橋梁で架橋できる長さをこえています Bruecke 橋 Bruecke muss an\neinfachem\nHang beginnen!\n 橋梁は直線の坂に建ててください。\n Brueckenboden 橋上 Build air depot 格納庫の建設 build choosesignals 入線振分信号の建設 Build city market 最寄りの都市に新たな市場を誘致する Build drain 変圧所 build HQ 本社を建設する Build land consumer 新しい発電所を建設する Build maglev depot リニア車庫の建設 Build monorail depot モノレール基地の建設 Build narrowgauge depot ナローゲージ車庫の建設 Build powerline 高圧送電線を敷設 Build presignals プレシグナルを設置 Build road depot 車庫の建設 Build ship depot 造船所の建設 Build signals 信号の設置 Build train depot 機関庫の建設 Build tram depot 路面電車車庫の建設 Build truck depot 車庫の建設 Building costs estimates 建設費概算 Buildings 市街地 Built artifical slopes スロープ(人工斜面)の建設 Built random attraction 観光地の整備(ランダム) Bus_tab バス Can be overgrown 生い茂ることがあります Can only move from halt to halt or waypoint to waypoint. スケジュールの移動は、駅と駅または\n中継点と中継点の間でのみ可能です。\n駅から駅以外の場所(その逆も)\nへは移動できません。 Cancel キャンセル Cannot connect to offline server! オフラインのサーバーには接続出来ません。 Capacity 積載量 Capacity: 定員/最大積載量: Capacity: %.0f MW 送電容量: %.0fMW\n Capacity: %d%s %s\n 定員/最大積載量: %d%s %s\n Capacity: %s\nLoad: %d (%d%%) 定員/最大積載量: %s\n乗車/積載: %d (%d%%) Cars are not available yet! 自動車は・・・\nいえ、馬すら見あたりません・・・ cars.\nstate 車\n Cash 手許資金 Category カテゴリー Change label style ラベルのスタイルを変更する Change player プレイヤー変更 Chart グラフ Chat_msg チャット Choose direction 向きを選択してください Choose operation executed on clicking stored/new vehicles 車両選択時のアクションを選択してください chooses a random map マップ番号を無作為に選びます citicens 住民 Cities can only be built on flat empty ground! 市役所は平坦な空き地にのみ建設できます。 City attraction 都市内施設 City industries 産業チェーン数 City list 街一覧 City size 街の規模 city_road 市道 citybuilding builder 市内建築物建設ツール CityLimit 市域 cl_title 編成一覧 cl_txt_sort 並び替え: clamp 値の範囲を制限 Cleanup schedule 二重エントリを削除 Clear block reservation 線路占有解除ツール clf_chk_aircrafts 航空機 clf_chk_cars 自動車 clf_chk_indepot 入庫中 clf_chk_maglev リニア clf_chk_monorail モノレール clf_chk_name_filter 名前フィルター: clf_chk_narrowgauge ナローゲージ clf_chk_noincome 収入なし clf_chk_noline 路線なし clf_chk_noroute ルートなし clf_chk_noschedule スケジュールなし clf_chk_obsolete 旧型 clf_chk_ships 船舶 clf_chk_spezial_filter 特殊フィルター: clf_chk_stucked 立ち往生 clf_chk_trains 列車 clf_chk_trams 路面電車 clf_chk_type_filter タイプフィルター: clf_chk_waren 旅客/貨物フィルター: clf_title 編成一覧フィルター Click to open the finance dialog. 財務ダイアログを開くにはクリックしてください。 Climate 気候 Climate Control 気候の設定 Climates 気候 closed 遮断 Closes topmost line window when new line selected. 路線を選択したときに最前面の路線ウィンドウを閉じます COLOR_CHOOSE\n カラーを選んで下さい。\n Company bankrupt 破産しました company_chat 社内チャット Company_msg ライバル企業 Comparing pak files ... pakを比較しています ... Composed by 作曲: Configure AI AI指針設定 Configure AI setttings AI指針設定を編集します Congratulation\nScenario was complete in\n%i months %i years. ★☆★ おめでとうございます! ★☆★\n    シナリオ達成です!\n\n  所要時間: %2$i年 %1$iヶ月 Connect factory 産業施設を接続する Connected stops 接続している停車場: Connected with server サーバーと接続しました。 connection 路線 Connections 接続産業・路線 Constructed by 作者: Constructed by %s 作者: %s construction speed 増線頻度 Construction_Btn 建設費 Consumed 消費量 contains the following doubled objects:

以下のオブジェクトが重複しています:

convoi %d of %d 入庫中の編成 %d / %d convoi error tooltips 問題のある編成の状態を表示 Convoi following mode 編成追跡モード Convoi has been sent\nto the nearest depot\nof appropriate type.\n この編成は一番近い車庫に\n向かいました。\n Convoi is sold when all wagons are empty. この編成を回送し、空車時に売却します。 convoi mouseover tooltips マウスオーバーで編成の状態を表示 convoi passed last\nmonth %i\n \n先月の交通量: %i 編成\n―――――――――――――――――――――― Convois 編成数 Convois: %d\nProfit: %s 編成数: %d\n利益: %s Convoys 編成数 Copy Convoi 編成をコピー Copy the selected convoi and its schedule or line 選択した編成とそのスケジュール/路線を複製します Copy to clipboard クリップボードにコピー Cost 輸送費 cost for removal 撤去費用: Cost per unit 単位あたりの輸送費 Cost: %8s (%.2f$/km %.2f$/m)\n 費用: %s (¥%.2f/㎞, ¥%.2f/月)\n Cost: %8s (%.2f$/km)\n 費用: %8s (¥%.2f/km)\n Costs  費用  Create a new line based on this schedule このスケジュールを元に新しい路線を作ります curiosity builder ランドマーク建設ツール curlist_title 名所旧跡一覧 CURRENCY ¥ CURRENCY_LEFT 1 Current game 現在のゲーム Currently playing: 再生中の曲: Customers live in: 顧客居住地: Date format 日付と時刻の形式 DAY_SYMBOL 日 deactivated in online mode ネットゲームでは使用できません Dec. 12月 Deccelerate time ゲームスピードを遅くする December 12月 decrease underground view level ハイトカットレベルを下げる Del Stop 削除 Delete Line 路線削除 Delete the current stop 削除する駅名をクリックして削除 Delete the selected line (if without associated convois). 選択した路線を削除 (所属編成がない場合のみ) Delete this file. ファイルの削除 Delivered 出荷量 Demand 需要量 Demand: %.0f MW 需要電力: %.0fMW\n Denkmal モニュメント Departed 出発 Departs at 出発時間 (日 時:分) Departure after %%積載 または 発車間隔 (日 時:分) Departure board 発着時刻 Departures per month 月間出発回数 Departures to\n 出発予想時間\n Depots 車庫 Der Tunnel ist nicht frei!\n トンネル内が空ではないか、\n他社の所有物です。\n Destination 目的地 Destroying map ... 現在のマップを終了しています ... Details 詳細 Developed by the Simutrans Team 開発:Simutrans Team Die Bruecke ist nicht frei!\n 橋梁上が空ではないか、\n他社の所有物です。\n diesel ディーゼル direct_chat_to: ダイレクトメッセージ: directmail 広告郵便 Direkt erreichbare Haltestellen 直通駅 disable midi BGMオフ Display bars above factory to show the status 産業建築物の上部に在庫量を示すバーを表示します Display settings 表示設定 Distance 走行距離 distributing cities 都市を配置中 distributing factories 産業を配置中 Do not show 非表示 Dock 港 Dock must be built on single slope! 港は斜面沿いに造ってください! Downloading ダウンロード中 dp_title 車庫一覧 Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen あと %dヵ月以内に借金を返済してください。 Durchsatz 最大: Economy 経済と都市 Eigenbesitz\n 公共物\n\n Ein %s\npasst hier nicht.\n '%s' を\nここに建てることはできません。\n Einstellungen 設定 Einstellungen aendern ゲームオプション electric 電気 Electricity 電力 Electricity producer\n\n 電力供給施設\n\n Electrics_tab 電車 Electrify track 線路の電化 enlarge map マップの拡張 enter a value between %i and %i %i~%i Enter address (IPアドレス/ホスト名を入力) Enter Password プレーヤー名とパスワードを変更します。 Error エラー Erzeuge neue Karte.\n 新しいマップを\n作成しています。\nお待ちください。\n\n(大きいマップでは\n数分掛かる\nことがあります)\n Es wird bereits\nein Fahrplan\neingegeben\n すでにスケジュール\nダイアログが開いています。\nまず、すでにあるダイアログを\n閉じてください。\n Extract files ファイルを展開中 Fabrikanschluss 接続している産業 Fabrikname 産業名 Factories 産業 factory details 産業の需要先/供給元 factorybuilder 産業施設建設ツール Fahrplan スケジュール Fahrtziel 行先: Fahrzeuge koennen so nicht entfernt werden 乗り物をブルドーザーツール\nで撤去することはできません。\n Fahrzeuge: 車両数: Farbe プレーヤーの色 Fast forward 早送り Feb. 2月 February 2月 Ferry_tab フェリー Fertig 終了 Filename ファイル名: Files from: ファイルの送信元: Filter: フィルター: Finances of %s %s の財務 Finanzen 財務 find mismatch パックファイルを比較 fl_title 産業一覧 Flug_tab 旅客機 follow me 追跡 Follow the convoi on the map. この編成を追ってマップを移動します font size フォントサイズ For questions and support please visit: 質問・サポートは以下のサイトへアクセスしてください: Forest 森 Found new city 新しい街を作る Fracht 貨物 Frame time: 描画時間: Free Capacity 空き容量 freeplay mode フリープレイ モード Freight 貨物 Friction: 現在の摩擦係数: fuel_cell 燃料電池 Full load 積むまで待機 Fullscreen (changed after restart) 全画面表示 (ハードウェアモード、変更は再起動が必要) Fundament 土地 Fussgaenger 歩行者 Game info ゲームの情報 GAME PAUSED 一時停止 Game_msg ゲーム Gear: ギヤ比: Gebaeude 建築物 General 全般 generate script インフラ構築マクロを生成 Generated 発生 Generation: %.0f MW 発電量: %.0fMW\n Gewicht 重量 Gewinn 収益: Give the selected vehicle(s) an individual schedule 選択している編成に個別の移動計画を設定します gl_btn_sort_catg カテゴリー順 gl_title 貨物一覧 go home 車庫へ Goods 製品 Goods AI AI 貨物 Goods list 貨物一覧 Gross Profit 粗利益 Groundobj 景観物 groundobj builder 修景物設置ツール Grow city 都市の人口を増やす Growth 発展 GUI settings GUI設定 H 停 Hangar 格納庫 Happy 旅行成功 Has slope graphics 傾斜地設置対応 Has Snow 降雪対応 Haus kaufen 市内建築物を購入 Helligk. 表示設定 Help ヘルプ Help text not found 申し訳ありません。この項目のヘルプはまだありません hide all building 全ての建物を隠す hide city building 都市の建物を隠す Hide labels 駅名プレートを非表示にする hide objects under cursor カーソル周辺の樹木や建物を隠す hide station names 駅名を隠す hide transparent 隠す時はシルエットで表示する hide trees 樹木を隠す Hier warten/lagern: 待機中の旅客/貨物 Higher transport fees, crossconnect all factories 運賃収入が1.5倍に増え、ジャストインタイム生産が無効になります highlight single ways 交通ルートの終点/中断を表示します。 Highlite schedule 路線の経由地をハイライトする hl_title 駅一覧 hl_txt_filter フィルター: hl_txt_sort 並び替え: hlf_chk_airport 空港 hlf_chk_anleger 港 hlf_chk_bahnhof 鉄道駅 hlf_chk_bushalt バス停 hlf_chk_frachthof トラックターミナル hlf_chk_keine_verb 接続なし hlf_chk_maglevstop リニア駅 hlf_chk_monorailstop モノレール駅 hlf_chk_name_filter 名前フィルター: hlf_chk_narrowgaugestop ナローゲージ駅 hlf_chk_overflow 混雑中 hlf_chk_spezial_filter 特殊フィルター: hlf_chk_tramstop 電停 hlf_chk_type_filter タイプフィルター: hlf_chk_waren_abgabe 生産品: hlf_chk_waren_annahme 製品需要: hlf_title 駅一覧フィルター Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. 帰るべき車庫が確認できません。\n手動で入庫先を指定してください。 Homeless 浮浪者 hydrogene 水素 Icon size: アイコンサイズ: Idle: アイドル状態: ignore climates 気候条件を無視する In the industry legend show only currently existing factories 現在存在する産業が取り扱っている貨物のみを表示します。 In Transit 待機/輸送中 Increase Industry density 産業チェーン/発電所を増やす increase underground view level ハイトカットレベルを上げる industrial building 工場 Industry overlay 産業在庫量ツールチップ Infinite mouse scrolling マップスクロール時にポインタ位置を固定(マウス使用時推奨) Infinite scrolling using mouse windowモードではオフにした場合に画面の外にマウスポインタが移動することがあります。※タッチスクリーンでは動作しない可能性があります! Init map ... マップを初期化しています ... Input 原料 Ins Stop 駅を挿入 Insert stop before the current stop 選択している駅の直前に駅を挿入 Install graphics グラフィックセットの追加 Install paks Pakセットをダウンロードし、インストールします。 Insufficient funds! 資金が足りません! Intercity road len: 接続道路の長さ: Intro. date 開発年月 Intro. date: 登場年月: invalid 故障 Invalid coordinate ※無効な座標高度※ Invert stops 順序を反転 isometric map 回転して表示 Jan. 1月 January 1月 join game ゲームに参加 July 7月 Jump to 指定座標へ移動 June 6月 just now たった今 Kann Spielstand\nnicht laden.\n ゲームを読み込む\nことができません。\n Kann Spielstand\nnicht speichern.\n ゲームを保存できません。\n Kein Besitzer\n 所有者なし\n keine 無し Keine Einzelfahrzeuge im Depot 保管中の車両はありません。 Keyboard_Help\n キーボードヘルプ\n koord 位置順 Kreuzung 踏切 labellist_title マーカー一覧 Lade Relief 地形データを読み込む Laden ゲームのロード Land attraction 郊外型施設 Land industries 産業チェーン数: LANG_CHOOSE\n 言語を選んでください。\n LARGE_NUMBER_STRING 億 LARGE_NUMBER_VALUE 1e8 Last Month 先月 Last used tools 最後に使用したツール Last Year 前年度: Leaving depot! 出庫中 leer 空き Left button drags minimap too 左ボタンでミニマップもドラッグする legacy (large heights) レガシー (高さ 2) legacy (small heights) レガシー (高さ 1) Legend 凡例 Leistung 出力 Leistung: %d kW 出力: %dkW Leitung 送電線 length: %d 建設距離: %d letzen Monat: diesen Monat: 先月:         今月: Line 路線 Line Management 路線編集 line name mouseover tooltips マウスオーバーで路線名を表示 linear 線形 Lineless convoys serving this stop 路線に所属していない編成 Lines serving this stop 乗り入れている路線 List of industries on the map マップ上の産業施設一覧 LKW_tab トラック Load game ゲームの再開 load height data from file 地形データをファイルから読み込みます Load mode: ロードモード: Load scenario シナリオを読み込む Load script tool スクリプトツールを読み込む Load Scripted AI AIスクリプトを読み込む Load settings from 設定の読み込み元: loaded 乗車中 loaded passenger/freight 乗車中の旅客/貨物 Loading (%i->%i%%)! 積込待機 (%i->%i%%) Loading (%i%%) departure %s! 積載中 (%i%%)、出発: %s Loading (%i%%)! 積込中 (%i%%)! Loading addon paks ... アドオンを読み込み中 ... Loading map ... マップを読み込み中 Loading paks ... pakを読み込み中 ... Loading skins ... スキンを読み込み中 ... Loading time: 積載時間: Lock Control key Ctrlキーを固定する Lock game プレイヤー変更の禁止【解除不可】 Lokomotive_tab 機関車 m3 m³ Maglev リニア maglev vehicle リニア車両 maglev_track リニア Maglevdepot リニア車庫 Mail Demand %d\n 郵便需要: %d\n Mail level 郵便物のレベル Mailbox メッセージ表 Mailbox Options メッセージ設定 Maintenance 維持費 make stop public (or join with public stop next) costs %i per tile and level 公共駅に変更 駅上をドラッグで、実際の見積り費用を表示します (単価%i) Make way or stop public (will join with neighbours), %i times maintainance 駅または道路・軌道等を公共化して隣接する公共駅と統合します。維持費%iヶ月分が必要です。 Manual (Human) プレイヤー(人間) Manufactured: 製造年月: Map roughness マップの起伏: map view マップ表示設定 map zoom 拡大・縮小 Mar. 3月 March 3月 Margin (%%) 利益率 (%%) Marker マーカー max 高 Max Boost (%%) 増産能力 (%%) Max income: 最大収入: Max. speed 最高速度 Max. speed: 最高速度: Max. waiting time 最大待ち時間 (日 時:分) Maximum 254 stops\nin a schedule!\n スケジュールの登録数は\n最大254ヶ所までです! maximum length of rivers 河川の最大長: Maximum tile height difference reached. これ以上高く(低く)\n造成することはできません。\n\n隣接した土地の高低差は\n2段までです。\n Maxspeed 最高速度 May 5月 Median Citizen per town 都市の平均人口: Meldung メッセージ Menge 待機数 merge stop 駅の結合 MessageOptionsText \n新年\n\nプレイヤー情報\n\n都市\n\n経路なし\n\n新規目的地\n\n一般通知\n\n新型車両\n\n駅の混雑\n\n問題\n\n交通渋滞\n\nシナリオ\n\n全体チャット\n\n社内チャット\n\n個人チャット\n\n min 低 minimum length of rivers 河川の最短長: Minimum load 最低積載率 (%%) Missing pakfiles オブジェクトが不足しています! Modify the selected line 選択されている路線を編集します Monate alt 樹齢 (月) Monorail モノレール monorail vehicle モノレール/リニア車両 monorail_track モノレール Monorailboden 高架 Monoraildepot モノレール基地 Month 月 month wait time 月間最大待ち時間: Monthly departures 時刻表 Months 月 Monument モニュメント Monuments モニュメント Mountain height 山の高さ: Move the map マップの移動 Movingobj 動物 Music playing disabled/not available BGMは演奏できません。 Music volume: BGMの音量: mute sound 効果音オフ Name 名前 Narrowgauge ナローゲージ Narrowgauge are not available yet! ナローゲージは\nまだ利用できません。 narrowgauge vehicle ナローゲージ車両 narrowgauge_track ナローゲージ Narrowgaugedepot ナローゲージ車庫 Net ID: %p 電力網 ID: %p\n Net Wealth 総資産 Net wealth near zero 総資産が尽きかけています Neue Karte 新しいゲーム Neue Welt 新しいゲームを開始する new convoi 新しい編成 New Line 路線作成 New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. 新しい路線が作られました。\n選択すれば路線を割り当てることができます。 New Vehicles 購入費 Nickname: プレイヤー名: no buildings hidden すべての建物を表示 no convois 編成はありません No goods are loaded onto this convoi. この編成を降車(荷降ろし)専用にします。 no goods waiting 待機中の貨物はありません no load 回送 No player プレイヤーが存在しません No Route 経路無し No stop here! ここには駅がありません。\nキャンセルします。 No suitable ground! 適切な場所ではありません。\n\n気候条件が合わない/空地ではない\nまたは線路/道路上ではありません\nまた地下では線路/道路上を起点としてください。 No suitable townhall available for this climate! 気候に適合する役所建築物がありません! No terminal station here! 終端駅を建てるには、\n行き止まりの道路や線路が必要です。 no timeline 年代設定: 無効 no tree 木/森を作成しない No vehicles are available for purchase. 購入できる車両はありません。 No. of Factories 産業施設数 Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n 乗り物をスタートさせる前に\nスケジュールを設定してください。\n none 無し nord 北 nordost 北東 nordwest 北西 Not allowed to make publicly owned ways! 公共事業で軌道を建設することは許可されていません。 Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! 現在、この編成のスケジュールは\n変更できません。少し待ってから\n再度試してください。 Not enough fields would remain. これ以上は撤去できません Not online オフライン Nov. 11月 November 11月 now 現在時刻 Now active as %s.\n 現在、%sが\n操作できます nowhere どこにもない Number of rivers 河川の数: Object オブジェクト名 Oct. 10月 Odometer: %s km 総走行距離: %skm Ok OK Oktober 10月 On loan since %i month(s) 現在、負債があります。(%iヶ月間) On mouseover マウスオーバーで表示 on the 毎月1回目の出発日時 (日 時:分) On this map, you are not\nallowed to change player!\n このマップではプレイヤーの交代は認められ\nていません。 Only city chains 市内産業チェーンを建設 Only first %d differing paks reported. There are probably more. 違いが多いため、%d個以上は表示を省略しています。 Only full Unicode fonts Unicodeを網羅したフォントに限定する Only land chains 発電所/陸上産業チェーンを建設 Only one transformer per factory! 産業施設ごとに変圧所は一つのみ建設できます。 Only show goods which are currently handled by factories 現在存在する産業が取り扱っている貨物のみを表示します。 open 進入可 Operation 運行費 Ops Profit 運用利益 Optionen 設定 Or enter a server manually: 参加するサーバーを入力: ORDINAL_DAY_SYMBOL 日 Origin 出発地 ost 東 Output 生産 Ownership 所有者 Pak which may cause severe errors: 以下のオブジェクト(pak)が不足しているため、輸送に障害をもたらす可能性があります: Pak which may cause visual errors: 以下のオブジェクト(pak)は同等のオブジェクトに置換されました: Pak(s) different: pakの不一致: Pak(s) missing on client: クライアント側で不足しているpak: Pak(s) not on server: サーバー側に存在しないpak: Pakset differences Pakセットの差異 paletten パレット Pas_tab 旅客車 Passagiere 旅客 Passagierrate 旅客度 Passagierziele 旅客/郵便物の行き先 Passenger AI AI 旅客 Passenger Demand %d\n 旅客需要: %d\n Passengers %d %c, %d %c, %d no route 旅客の評価 %d %c, %d %c, %d 経路無し Passengers %d %s, %d %s, %d no route 旅客の評価 %d %s, %d %s, %d 経路無し Password パスワード: Pause 一時停止 Pax <%i> Mail <%i> 旅客 <%i> 郵便 <%i> Pax level 旅客レベル PaxDest 旅客/郵便目的地 Percent Electricity 電力供給率 (%%): personal_chat 個人チャット Pipette 選択したオブジェクトをコピーする Planes are not available yet! 飛行機はまだ開発されていません! Plant tree 木を植える player プレイヤー順 player -1 プレイヤー会社 player 0 公共事業 player 1 Napik 128 AS player 10 プレイヤー 10 player 11 プレイヤー 11 player 12 プレイヤー 12 player 13 プレイヤー 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Spedition VM player 5 H-Trans GmbH player 6 PSK & Co KG player 7 プレイヤー 7 player 8 プレイヤー 8 player 9 プレイヤー 9 Player ranking プレイヤーランキング Please choose vehicles first\n 最初に乗り物を選んでください\n Post 郵便 Postrate 郵便度 Power 電力 Power (MW) 電力 (MW) Power: 出力: Power: %4d kW\n 出力: %4dkW\n Powerlines 高圧線 Price 車両価格 Problems_msg 問題 Produced 生産量 Produces: %.1f units/minute 現在の生産量: %.1f単位/分 Production of %s has been stopped:\n%s\n %s「%s」は\n製造中止になりました。\n Production/Boost 生産能力/増産 Produktion 生産 Profit 利益 promote to line 路線作成 public_chat 全体チャット q1 春 q2 夏 q3 秋 q4 冬 Query server サーバ情報の表示 rail car 鉄道車両 random ランダム Random age ランダムな樹齢を使用 Random map ランダムマップ Rathaus 役所 Rating 評価 ratio_pax 旅客比率 (%%) Relevant 取扱い貨物のみ Reliefkarte マップ Remove 撤去する remove airstrips 滑走路・誘導路の撤去 remove channels 運河の撤去 remove interm. signals 途中の信号を削除 remove maglev tracks リニアの撤去 remove monorails モノレールの撤去 remove narrowgauge tracks ナローゲージの撤去 remove powerlines 送電線の撤去 remove roads 道路の撤去 remove signal 信号を削除します remove tracks 鉄道の撤去 Remove wayobj %s %s 架線の撤去 replace other signals 他の信号を置換 replace stop スケジュール移動ツール request closing 進入禁止 residential house 住宅 Restore natural slope スロープを戻す Restwert: 売却価格: Retire date 引退年月 Retire. date: 製造終了年月: return ticket 往復路線 Revenue 収入 Revenue/unit/100 tiles 単価 (100タイルあたり) Revert schedule スケジュールを戻す Revision: リビジョン: road 道路 road vehicle 自動車 Roadsign 道路標識 Rotate Building 建物の向き回転 Rotate map 90°回転する Rotation 向き Routing 経路 Running cost 維持費 sack 袋 sail 帆走 Save ゲームの保存 Save generated script 生成されたスクリプトを保存 Saving map ... マップを保存中 Scenario complete: %i%% シナリオ達成率: %i%% Scenario Debug デバッグ Scenario Error Log エラーログ Scenario Goal 目標 Scenario Info 情報 Scenario information シナリオの概要 Scenario Result 結果 Scenario Rules ルール Scenario_ シナリオ Schedule changing! ルート変更中 Schienentunnel 鉄道トンネルの建設 Schiff_tab 貨物船 Schiffdepot 造船所 Schleppkahn_tab バージ Screen scale: 表示スケール: Screenshot スクリーンショット Scroll threshold スクロールのしきい値 Search: 検索: Seasons 季節 Sehenswuerdigkeit 名所旧跡 Select a server to join: 参加するサーバーを選択してください: Select a theme for display 表示するテーマの選択 Select display font テキストのフォントを選択 Select one or more graphics to install (Ctrl+click): インストールするグラフィックを選択してください (Ctrl+クリックで複数選択): Select soundfont サウンドフォントの選択 Sell the selected vehicle(s) 選択している乗り物を売却します Selling of the program is forbidden. このプログラムの販売は禁止されています。 sended 配達実績 SEP_FRACTION . SEP_THOUSAND , SEP_THOUSAND_EXPONENT 3 Sept. 9月 September 9月 Served by サービス提供 Served by me 自社輸送網内のみ Server did not respond! サーバーの応答がありません。 Serves Line: 所属路線: Service 編成数 set signal spacing 信号間隔の設定 Setting 高度な設定 Ship 船舶 shops and stores 商店 Show all 全てを表示 show all building すべての建物を表示する Show also vehicles no longer in production. 生産を終了している乗り物も表示します。 Show also vehicles that do not match for current action. このボタンをオフにすると、選択/連結できない乗り物を隠します Show always 常に表示 Show climates 気候を表示 Show contour 等高線を表示 Show even servers with wrong version or pakset リビジョンや種類が不明または異なるサーバーも表示する Show finances for transport type 輸送形態 Show future 登場予定の乗り物 show grid グリッドを表示する Show industry 産業凡例 Show legend 表示項目 Show map scale カラースケール Show mismatched 不適合のサーバーを表示 Show networks 路線図 Show obsolete 旧型を表示 Show offline オフラインを表示 Show only used マップに存在のみ表示 Show outline インフラのみ Show schedules 路線経路の表示 Show servers that are offline オフラインのサーバーを表示します Show servers where game version or pakset does not match your client バージョンやpakセットが異なるサーバーを表示 show station coverage 駅の有効範囲を表示する show station names 駅名を表示する show waiting bars 待機状態・待機数グラフを表示する show/hide block reservations ルートの予約の表示/非表示 Show/hide estimated arrival times 到着予定時刻の表示/非表示 show/hide object owner 所有者の表示/非表示 Show/hide statistics 統計グラフを見る/隠す Shows buttons about networks overlay. 輸送ネットワーク表示設定を表示する。 Shows buttons on network overlay. 接続ネットワークをオーバーレイ表示するオプションを表示します。 Shows buttons on special topics. 路線の利用状況や産業などの表示項目を選択します。 Shows consumer/suppliers for factories カーソル下の工場の供給先を表示します(Shiftキー併用で供給元を表示)。 Shows the color code for several selections. カラースケールバーを表示します。 Shows the currently selected schedule 選択中のスケジュールを路線図に表示します。 Shrink city 都市の人口を減らす shuffle midis シャッフル再生 Signal 信号 signal spacing 信号/標識の間隔 Sim: 演算回数: Similar view as the main window メインウィンドウと同じ視点にします Single toolbar only ツールバーを1つに制限する Size (%d MB): サイズ (%d MB): Size (area) サイズ (面積) sliced underground mode ハイトカット表示モード slot empty <空き> Smart hide objects カーソル周辺を隠す Sort by 並び替え規則 Sort by: ソート: Sort waiting list by 待機表の並び変え Sound サウンド Sound settings サウンド設定 Sound volume: マスターボリューム Soundfonts are located in the music directory. サウンドフォントは、musicディレクトリにあります。 special freight 専用貨物 Speed Bonus 速度ボーナス Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. 速度ボーナスの基準速度\n道路 %ikm/h, 鉄道 %ikm/h,\n海路 %ikm/h, 空路 %ikm/h Speedlimit 制限速度 Speichern ゲームの保存 Spieler プレイヤー Spieler(mz) プレイヤー Spielerliste プレイヤー一覧 Spielstand wurde\ngeladen!\n \nゲームをロードしました。\n Spielstand wurde\ngespeichert!\n \nゲームを保存しました。\n Sprache 言語 Sprachen 言語 Stadtinformation 街の詳細 Start スタート Start the selected vehicle(s) 選択している編成を発進させます Starte Spiel この設定で始める station labels 駅ラベル表示設定 Station tiles: ホームの長さ: Station_msg 駅 Status 待合状態 steam 蒸気 Step timeline one year 1年進める Stops 駅 Storage 在庫量 Storage capacity 収容量 Strassendepot 車庫 Strassentunnel 道路トンネルの建設 street car 路面電車車両 sued 南 suedost 南東 suedwest 南西 Summer snowline 雪線 Supplied: %.0f %% 供給率: %.0f%% Suppliers 供給元 Tage alt 日経過 The following graphics are unmaintained: 以下のグラフィックはメンテナンスされていません: The generated script was saved!\n マクロが正常に保存されました。\nスクリプトツールで実行できます。 The gradient does not fit a tunnel 斜面がトンネルを建設するのに適していません The script cannot be saved!\n マクロを保存できませんでした。\n Theme selector テーマの選択 There are still vehicles\nstored in this depot!\n この車庫には、まだ乗り物が\n留置されています。\n This Month 今月 This Year 今年度 Tile not empty. ここは空き地ではありません。\n土地の造成をする前にすべての\n建造物を取り除いてください。 timeline 年代設定: 有効 tl_title 街一覧 To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. より多くの乗降客を\n%sに呼び込むため\n「%s」を\n%i人の住民の援助により\n建設しました。 To heavy traffic\nresults in traffic jam.\n 交通集中により渋滞発生中\n To: %s 宛先: %s Toggle day/night view 昼/夜を交互に切り替えます Toggle vehicle tooltips 車両ツールチップの切り替え tonnen t Toolbar position: ツールバーの表示位置: Total inhabitants: 総人口: Tourist attractions 名所旧跡 Tourists 名所 Town_msg 都市 Town: %s\n 所在地: %s\n Towns 都市 track 鉄道 Tracks 鉄道線 Traffic 交通量 traffic settings 交通設定 Train 列車 Trains are not available yet! 鉄道はまだ開発されていません! Tram 路面電車 tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. 路面電車 %ikm/h, モノレール %ikm/h\nリニア %ikm/h, ナローゲージ %ikm/h tram_track 路面軌道 Tramdepot 路面電車車庫 Trams are not available yet! 路面電車はまだ開発されていません! Transferring game ... ゲームを転送しています ... Transformer only next to factory! 変圧所は、産業施設に\n隣接して建設してください。 Translation 日本語名 transparencies 透明度設定 transparent background 透明な背景 transparent station coverage 駅の有効範囲を半透明色で表示する Transported 輸送実績 Trees disabled! 樹木は使用できません! TrolleyBus_tab トロリーバス Truck 自動車 tt_Other その他 Tunnel muss an\neinfachem\nHang beginnen!\n トンネルは直線の\n坂に作ってください。\n Tunnel must start on single way! トンネルはまっすぐな道路/線路上に建設してください。 Tunnelboden トンネル内部 under the Artistic License Artistic License の下で公開されています underground mode 地下表示モード UNDO failed! この操作を取り消すことはできません。\n路線の敷設を取り消せるのは、\nその区間に信号、駅、停留所、\nその他の構造物が\nまだ設置されていない場合だけです。 Undo last ways construction 直前に建設した道路/線路を取り壊す Unemployed 無職者 Unhappy 旅行失敗 units/day 単位/月  Unloading (%i%%)! 荷降ろし中 (%i%%)! Update Line 路線編集 upgrade HQ 本社を拡張移転する Usage: %.0f %% 稼動率: %.0f%% Usage/Output 使用量/発電量 Use beginner mode 初心者モード Use timeline start year 開始年を指定: Vehicle %s can't find a route! 「%s」の\n運行できる経路がありません Vehicle %s is stucked! 「%s」が\n立ち往生しています! Vehicle count: 車両数: Vehicle details 車両の詳細 Vehicle Name 車両名 Vehicle Power 出力 vehicles stored 保管車両順 Verbrauch 消費 Vergroessere die Karte\n マップを拡げています。\nしばらくお待ちください。\n Verkauf 売却 verkaufen 売却 Verkehrsteilnehmer 自家用車 Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n 現在借金があります。\n\nあと %d ヵ月以内に\n借金を返済してください。\n vh_title 乗り物一覧 via 経由地 via %s\n (%s経由)\n via Menge 経由地 (量) via owner 経由地 (所有者) voranstellen 先頭へ Waggon_tab 貨車 waiting 待機中 Waiting for clearance! 停止 Walked 徒歩 walking 徒歩圏内 Warnings_msg 交通 Wasser 水域 Water 沿岸 Water level 海面水位: water vehicle 船舶 way %s cannot longer used:\n この先、\n%sは\n建設できません。 way %s cannot longer used:\n%s\n この先、\n%sは\n建設できません。 way %s now available:\n %s が\n新しく建設可能になりました。\n\n Way toll 通行料 Ways not connected 道路/線路が繋がっていません。 waytype 経路の種類 Wegpunkt 中継点 Weight 重量 Weight: 重量: Weight/unit 重量 (1単位当たり) Welcome to Simutrans ようこそ、Simutransへ Welcome, %s! %s がゲームに接続しました。 Wert 価格 west 西 Wind direction 風向き Winter snowline 積雪高度 withdraw 引退 Withdraw All すべて引退 WRONGSAVE 互換性が無いため、\nロードできません。\n Year 年 Year %i has started. %i年になりました。 YEAR_SYMBOL 年 Years 年 Your primary color: 基調色 Your secondary color: 補助色 Zielort 目的地 zooming in ズームイン zooming out ズームアウト Zu nah am Kartenrand 地図の周辺部から離して\n設置してください\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. 最高速度記録(リニア)\n 時速 %.1f km\n「%s」が達成 New world record for monorails: %.1f km/h by %s. 最高速度記録(モノレール)\n 時速 %.1f km\n「%s」が達成 New world record for motorcars: %.1f km/h by %s. 最高速度記録(自動車)\n 時速 %.1f km\n「%s」が達成 New world record for narrowgauges: %.1f km/h by %s. 最高速度記録(ナローゲージ)\n 時速 %.1f km\n「%s」が達成 New world record for planes: %.1f km/h by %s. 最高速度記録(航空機)\n 時速 %.1f km\n「%s」が達成 New world record for railways: %.1f km/h by %s. 最高速度記録(鉄道)\n 時速 %.1f km\n「%s」が達成 New world record for ship: %.1f km/h by %s. 最高速度記録(船舶)\n 時速 %.1f km\n「%s」が達成 #________________________________script_ai_text_________________________________ #________________________________script_ai_text_________________________________ %s build additional convoy to line: %s %s が路線「%s」を増強しました %s build rail line from %s (%s) to %s (%s) %s は %s (%s) から %s (%s) までの鉄道路線を構築します %s build road line from %s (%s) to %s (%s) %s は、%s (%s) から %s (%s) までのバス・トラック路線を開設しました。 %s build ship line from %s (%s) to %s (%s) %s は %s (%s) から %s (%s) までの航路を構築します %s extends the route from %s (%s) to %s (%s) %s は %s (%s) から %s (%s) へとルートを伸張します %s optimize way line from %s (%s) to %s (%s) %s は %s (%s) から %s (%s) までの路線を最適化します %s removes convoys from line: %s %s が路線「%s」を縮小しました vehicles of the line %s were retired 路線 %s の編成が引退しました。 #_________________________________tooltip_text__________________________________ #_________________________________tooltip_text__________________________________ Add convois with similar schedule to this line. 同じスケジュールの編成をこの路線に追加します。 Attach current coordinate to the comment 現在の位置を地図上に記録し、その情報をメッセージに添付する。 Enter intervall in days, hours, minutes 運行間隔を日、時、分で入力してください。 Mirrors order of stops 停止順序を反転する Revert to original schedule すべての変更を破棄して戻す Tutorial not available for this pakset. Please try loading Pak128. このpakセットにはチュートリアルが用意されていません。(最新版のpak128を確認してみてください) #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL 坂 &1_CITY_SYLL 崎 &2_CITY_SYLL 田 &3_CITY_SYLL 野 &4_CITY_SYLL 橋 &5_CITY_SYLL 塚 &6_CITY_SYLL 島 &7_CITY_SYLL 沼 &8_CITY_SYLL 原 &9_CITY_SYLL 岡 &A_CITY_SYLL 谷 &B_CITY_SYLL 井 &C_CITY_SYLL 内 &D_CITY_SYLL 平 &E_CITY_SYLL 沢 %1_CITY_SYLL 森 %2_CITY_SYLL 山 %3_CITY_SYLL 櫻 %4_CITY_SYLL 大 %5_CITY_SYLL 松 %6_CITY_SYLL 堀 %7_CITY_SYLL 倉 %8_CITY_SYLL 竹 %9_CITY_SYLL 福 %A_CITY_SYLL 川 %B_CITY_SYLL 富士 %C_CITY_SYLL 白 %D_CITY_SYLL 石 %E_CITY_SYLL 古 %F_CITY_SYLL 神 1center %s%s 1extern 前%s%s 1suburb %s%s%s 2center %s中央%s 2extern 上%s%s 3center %s本町%s 3extern 下%s%s 4center 中%s%s 4extern %s外%s 5center 新%s%s 6center 元%s%s 7center %s大通り%s simutrans-124.5/simutrans/text/ja/000077500000000000000000000000001520675460500171675ustar00rootroot00000000000000simutrans-124.5/simutrans/text/ja/airtools.txt000066400000000000000000000150461520675460500215720ustar00rootroot00000000000000航空機ツール

航空機ツール

航空機ツールには、航空輸送ネットワークを構築するためのツールが含まれています。ツールを使用して、航空機用の誘導路や滑走路、エアストップ(貨物や乗客の搭乗や降機に使用)、航空機の格納庫(航空機の購入や管理)、さまざまな空港の建物などを建設または撤去することができます。年代設定を有効にしてプレイしている場合、Simutrans内で時間が経過するにつれて、より多くのツールオプションが表示されます。

ゲーム画面上部にある飛行機のアイコンをクリックすると、ツールバーを開きます。
(ツールバーを開くかクリックした後)ツールオプションの上にマウスカーソルを合わせると、名前と該当する場合は建設費、維持費の括弧書き、最高速度の制限が表示されます。

{ヒント: シンプルな空港を建設する手順の一例です:
i) 誘導路と滑走路を建設し、接続します。
ii) 誘導路の端にエアストップを設置します。
iii) 必要に応じて空港の建物を追加します。}

ツールは、左から順に以下のものが含まれます:

誘導路: 航空機がエアストップから滑走路へ移動するための地上移動エリアを建設するツールです。誘導路はゲーム画面の平地に建設されます。
重要: 誘導路が滑走路の端に接続されている場合、航空機は離着陸できません。
誘導路の建設方法: ツールをクリックして選択し(カーソルが誘導路に変わります)、次に始点となる地形をクリックし(ゲーム画面にブルドーザーが表示され、画面右下にマップ座標が表示されます)。最後に終点となる地形または滑走路をクリックします。
{ヒント: [削除/撤去] を使って誘導路の個々の区画を削除します。「元に戻す」[z] では建設費用は返還されません。}

滑走路: 航空機が離着陸する際に使用する滑走路を建設するツールです。滑走路はゲーム画面上の平地に作られ、互いに交差させることができます。
滑走路の建設方法: ツールをクリックして選択し(カーソルが道に変わります)、次に開始点となる地形をクリックし(ゲーム画面にブルドーザーが表示されます)。最後に終点となる地形をクリックします。
{ヒント: 滑走路の両端は誘導路を接続せずに開けておいてください。そうしないと航空機が離着陸できなくなる場合があります。個別の滑走路を撤去するには [削除/撤去] を使用してください。「元に戻す」[z] では建設費用は返還されません。}

滑走路/誘導路の撤去: 航空機が存在しない場合に、ゲーム画面内の2点間にある誘導路または滑走路の区間を撤去するツールです。このツールの使用には撤去費がかかります。
撤去方法: ツールをクリックし(カーソルが赤い十字に変わります)、次に削除したいストリップ(部分)をクリックし(ゲーム画面に赤い十字で削除の始点が示されます)。最後にストリップの別のポイントをクリックすると、最初の削除ポイントまでの区間が削除されます。
{ヒント:滑走路の両端は接続せずに開けておいてください。そうしないと飛行機が離着陸できなくなる場合があります。個々の区間を撤去するには [削除/撤去] ツールを使用してください。}

航空機格納庫: 航空機を購入・管理するための格納庫(ハンガー)を建設するツールです。格納庫には維持費がかかり、誘導路の端に建設されます。
格納庫の建設方法: ツールをクリックして選択し(カーソルが格納庫に変わります)、誘導路の端をクリックします。
{ヒント: 格納庫の撤去には [削除/撤去] ツール使用してください。}

駐機場&ボーディングブリッジ(搭乗橋): 航空機が乗客や貨物を積み降ろしするための停留所をゲーム画面に建設するツールです。
エアストップ(駐機場)は、既存のストップに隣接して建設されない場合、新たなストップとして建設されます。 エアストップは誘導路の端に建設され、維持費がかかり、貨物、乗客、郵便物の集客範囲を持ちます。空港に空きのある(占有されていない)エアストップがある場合にのみ、航空機は着陸します。
エアストップの建設方法: ツールをクリックして選択し、誘導路の端をクリックします。
{ヒント: エアストップの撤去には「削除/撤去」ツールを使用してください。[v]キーを押すと、ゲーム画面で貨物と旅客の集客範囲を表示/非表示に切り替えられます。}

空港施設等: 各ツールはエアストップの拡張施設を建設しますが、これにより貨物や郵便、旅客の収容力および集客範囲が拡大しますが、維持費が増加する場合があります。いくつかのツールのオプションの隅には、その拡張施設によってエアストップが取り扱えるようになる属性(旅客・郵便・貨物)が示されています(駅一覧表およびストップ情報で使用されるものと同じ)。
拡張施設の建設方法: ツールをクリックして拡張施設を選択し(カーソルが拡張施設に変わります)、ゲーム画面内の既存のエアストップに隣接する場所をクリックします。新しい施設は、ストップの一部とみなされます。
{ヒント: 追加した施設の撤去には「削除/撤去」ツールを使用してください。[v]キーを押すと、ゲーム画面で貨物と旅客の集客範囲を表示/非表示に切り替えられます。}

simutrans-124.5/simutrans/text/ja/baum_build.txt000066400000000000000000000040361520675460500220360ustar00rootroot00000000000000木を植える

木を植える

木を植えるウィンドウでは、個々の木を植えることができます。これは、プレーヤーを公共事業に切り替えたときに、マップ編集ツールバーから利用できるマップカスタマイズオプションです。
ヒント:このバーには、一度に森を追加するための森を追加するボタンもあります。

ダイアログは4つのセクションに分かれています。
· 左上には、植樹可能なすべての樹種の一覧(リスト)が表示されています。リストの中の項目をクリックすると、その木の詳細情報が表示されます。木を配置するには、リストから選択した後、ゲームワールドのウィンドウ内で希望の場所をクリックします。
· 左下には、選んだ木の画像が表示されます。
· 右上にはいくつかのオプションが設定されています。
· 選択肢の下には、選択した木についての情報が表示されます。

選択リスト

選択リストには2つのタブがあり、木のタイプの識別方法を変更することができます。
· 日本語名: パックセットで定義されているように、指定された言語でその木の名前を表示します。利用可能な翻訳がない場合は、元のオブジェクト名(原名)が使用されます。
· 原名: Simutransの内部オブジェクト名を表示します。

オプション

· 気候条件を無視する: このオプションは、様々な木のタイプに関連する気候制限を無効にします。
· 年代設定を使用: 植えられる木の年齢はランダムになります。通常、最も若い年齢で植えられ、成熟してから何年もかけて枯れていきます。

simutrans-124.5/simutrans/text/ja/citybuilding_build.txt000066400000000000000000000050071520675460500235770ustar00rootroot00000000000000市内建築物建築ツール

市内建築物建築ツール

ダイアログは4つのセクションに分かれています。
· 左上は、利用可能な都市の建物の選択一覧表です。
· 左下には、選択した都市の建物の画像が表示されます。
· 右上にはいくつかのオプションが設定されています。
· 右下は、選択された都市の建物に関する情報です。

選択リスト

選択リストには、右上に指定されたオプション(住宅商店やオフィス工場の建物)で利用可能な、すべての都市部の建物が表示されます。都市部の建物の識別方法を変更するために、2つのタブがあります。
· 日本語名: 指定された言語(日本語)での建物の名前を表示します。指定された言語で利用可能な翻訳がない場合は、原名(オブジェクト名)が使用されます。
。 - 原名: 建物の種類に対するSimutrans内部のオブジェクト名を表示します。

文字の色には次のような意味があります。
· 青の文字は住宅を表します。
· 緑の文字は、商店やオフィスビルを表します。
· 黒の文字は、工場の建物を表します。

オプション

· 気象条件を無視する: このオプションは、様々な建物タイプに関連付けられてる気候制限を無効にします。
· 年代設定を使用: 選択リストには、現在のSimutrans年度に利用可能な建物のみが含まれます。
· 旧型を表示: 選択リストを拡張して、現在のSimutransの年に廃止された建物も含めるようにします。
· 住宅: 選択リストには、住宅が含まれます。
· 商店とオフィスビル: 選択リストには、商店と工場が含まれます。
· 工場: 選択リストには、工場が含まれます。
· 向き: 選んだ建物がさまざまな角度から見られる場合は、ここで特定の向きを選ぶことができます。選択したビューは左下の画像に反映されます。このオプションがランダムに設定されている場合、建物の向きはランダムに使用されます。

simutrans-124.5/simutrans/text/ja/citylist_filter.txt000066400000000000000000000050111520675460500231360ustar00rootroot00000000000000街一覧

街一覧

街一覧では都市部(村、町、市)について人口などの詳細情報を得ることが
できます。また、マップ世界全体の統計情報をグラフで見ることができます。

開き方一覧表ツールの街一覧のアイコンをクリックするか、[T]キーを
押します。

操作: リストの都市の名前を左クリックすると、街の詳細ウィンドウを開
きます。右クリックまたは左端の矢印ボタンをクリックすると、ゲーム画面
を都市に移動します。

総人口:はゲーム内の全市民の合計数(全ての都市部の人口の合計)
です。最近の人口増加はカッコ付きで表示されています。

並び替え:: ボタンのクリックでリストの並び替え基準を切り替えます。

- 都市名 名前の文字コードを基準に並び替えます。
- 住民 人口規模によって並び替えます。
- 発展 発展度(によって並び替えます。

- 昇順 / 降順 並び順を反転させます。

都市名 その都市部の名前

人口 その都市部の人口。カッコ内は最近の発展度。

グラフ: グラフを開閉します。

住民: 住民の数

発展: 増加した住民の数

都市: 都市の数

産業: 産業施設の数

編成数: 全社の車両編成の総数

自家用車: 自家用車の総数

旅客輸送率(%): 旅行希望者のうち、目的地へと出発できた割合(%)

旅客: 旅行希望者の総数

配達実績: 配達希望郵便のうち、目的地へと出発できた割合(%)

郵便: 配達希望郵便の総数

到着: 最終消費地に必要な製品の種類うち、在庫が充足されている種類の割合(%)

製品: 最終消費地に必要な製品の種類の総数

旅客輸送率(%)、配達実績、到着の割合は、各都市の発展に大きく影響します。
割合が高いほど、より速く発展します。

simutrans-124.5/simutrans/text/ja/citywindow.txt000066400000000000000000000060621520675460500221340ustar00rootroot00000000000000街の詳細

街の詳細

開き方:街の詳細を開くには、ゲーム画面上で調査ツールを使って役所をクリックするか、街一覧ウィンドウでリストを左クリックします。

名前: 直接変更できます。長い名前は入力できません。
街の規模: 都市の人口。カッコ内は最近の発展度。
市街地: 建物の数(ブロック数)
市域: 都市の市域の境界座標
建物が建設されたり、市道が整備されるのは、この市域の範囲内だけです。
市域はマップウィンドウでも確認できます。

無職者: 商店や工場が増えると、無職者の数は減少します。
浮浪者: 住居の数が増えると、浮浪者の数は減少します。
通常、無職者と浮浪者との合計が100を超えると、新しい建物が建設されたり、増改築が行われます。
無職者と浮浪者との数がどんどん増え続けているような場合は、新しい建物を建設する適切な場所がなかったり、建物 が最大レベルに達している可能性があります。
適切な整地や道路整備で改善されるかもしれません。

街を発展させる: このボタンをオフにすると、この都市の発展を一時的に止めることができます。
このボタンの状態はセーブゲームに保存されます。

地図: 前月と今月の旅客と郵便の目的地を表示してします。
地図上を左クリックすると、でゲーム画面を移動できます。
マップウィンドウに表示させることもできます。
 橙: 目的地へ出発できた
 黄: 目的地へ出発できなかった

グラフ
住民: 住民の数
発展: 増加した住民の数
市街地: 建物の数(ブロック数)
自家用車: この都市で増えた自家用車の数
輸送実績: この都市の旅行希望者のうち、目的地へと出発できた数
旅客: この都市の旅行希望者の数
配達実績: この都市の配達希望郵便のうち、目的地へと出発できた数
郵便: この都市の配達希望郵便の数
到着: この都市から労働者が通う最終消費地に必要な製品の種類うち、在庫が充足されている種類の数
製品: この都市から労働者が通う最終消費地に必要な製品の種類の数

輸送実績/旅客、配達実績/郵便、到着/製品の割合は、都市の発展に大きく影響します。
割合が高いほど、より速く発展します。また、この割合が低いと自家用車が増えます。

simutrans-124.5/simutrans/text/ja/climates.txt000066400000000000000000000103041520675460500215270ustar00rootroot00000000000000気候の設定

気候の設定

「気候の設定」ウィンドウでは、ゲームワールドの地形や気象に関するオプションを設定します。このウィンドウは、「新しいマップの作成」ダイアログの項目にあるオプションの1つです。

各設定の調整は、各項目の横にある矢印ボタンをクリックするか、ボックスに数字を入力して行います。

マップのオプション

海面水位 - マップ上の水位の高さを設定します(標準値 -2)。 値が高いほど、より少ない土地とより多くの水域が提供されます。

起伏の高さ - 地形の最大の高さを設定します。

起伏の激しさ - 地形の激しさを設定します。数値が大きいほど、沿岸部の起伏が大きくなります(よりリアルに見えます)。非常に起伏のあるマップでは、意図したほど多くの都市、産業、道路を生成できない場合があります。

警告: 33以上の高さのレベルは避けるべきです(プレイヤーはアクセスできません)。必要に応じて、水位を下げることで、より多くの土地を生成することができます。

雪線オプション
雪線とは、雪が現れる地図上の高さのことです。年間を通じて、Simutransの雪線は次の設定に従って変化します。

夏季の雪線 - 極気候はここから始まります。夏の間の雪線(万年雪)。この値は、気候オプションで選択された最も高い地形の値です。

冬季の雪線 - 冬季の雪線の最小高さを設定します。マップ上のより広い範囲に雪を出現させたい場合は、この値を小さくします。

気候オプション
新しいマップに異なる気候タイプを設定します。

それぞれの気候タイプの隣にある値は、地形図の中でその気候が現れる場所までの高さを設定します。高い位置にある気候帯は、低い位置にある気候帯でおおわれています。特定の気候をオフにするには、その値をリストのさらに上の気候と同じ値に設定します(砂漠の場合は水位に設定します)。

注意:建物、産業、植物には、それぞれの作者によって特定の気候が割り当てられています。

砂漠気候 - 降水量が非常に少なく、気温が極端に高い。木がほとんどない。平坦な風景。

熱帯気候 - 赤道周辺の非乾燥地域の典型的な例で、海面や低地では常に高温。

地中海性気候 - 適度に変化のある湿気が多い天候。

温暖気候 - 熱帯と極地の間に位置し、天候を変化に富む天候。

ツンドラ気候 - 低温、極冠の周縁。

氷雪(高山)気候 - 乾燥した気候、冬は寒い。

その他のオプション

木/森を作成しない - このオプションを有効にすると、Simutransが樹木を描かなくなります。木は、描画するのにCPUで時間を要します。性能が低いCPUや容量の少ないRAMのPCではこのオプションを使用してください。

湖を作成する - このオプションを有効にすると、Simutransが地面のいくつかの凹みを水で満たしてマップ全体に湖を作ることができます。

河川の数 - 地形図に生成・使用できる河川の最大数を設定します。狭い川は、ボートや船が航行することができません。

河川の長さ 最短 - マップ上の河川の最小の長さを設定します(タイル単位で)。

河川の長さ 最長 - マップ上の河川の最大の長さを設定します(タイル単位で)。

simutrans-124.5/simutrans/text/ja/color.txt000066400000000000000000000017511520675460500210520ustar00rootroot00000000000000プレイヤーの色

プレイヤーの色

プレイヤーの色ダイアログではあなたの会社の色を選択することができます。
ここで選んだ色は、列車や建築物などの色となります。

プレイヤーの色ダイアログゲームオプションから開きます。

色のボタンをクリックするとその色を会社の色にします。
色の変更は直ちに反映されます。

プレイヤー会社の初期の色はライトブルーです。
全ての物がここで選んだ色になるわけではなく、色が変わらない物もあります。

{Tips: ライバル会社の色を変更する場合、最初に ライバル会社一覧またはP+(プレイヤーの変更)から 操作する会社を切り替える必要があります。}

simutrans-124.5/simutrans/text/ja/convoi.txt000066400000000000000000000100371520675460500212260ustar00rootroot00000000000000編成一覧

編成一覧

乗り物リストには情報が表示され、異なる車両をフィルタリングしてリストアップするための操作機能があります(車両は稼働中の車両または車両ユニットの組み合わせで構成されています)。

開くには、一覧表の乗り物一覧ツールをクリックするか、[V]を押します。乗り物一覧には4つのオプションボタンがあり、2つは一覧表の中のアイテムの順番を決めるのに使用し、2つは一覧に表示される内容の設定を変更するのに使用します。
下のボタンは、フィルターオプションの条件に合う車両のリストです。

{ヒント: 車両が表示されない場合は、フィルターオプションを変更してください。一覧のアイテムが一部しか表示されていない場合は、乗り物一覧re-size表示するか、スライダバーを使って一覧をスクロールしてください。}

オプションボタンをクリックすると、オプションが切り替わり(オプションボタンの名前が変わります)、フィルタ設定コントロールが開きます。

並び替え: 2つのオプションボタンで、一覧に表示される車両の順番を決定します。

- タイプ 輸送形態別の順番(道路車両、鉄道車両、船舶、航空機の昇順)
- 名前 割り当てられた名前で、英数字でASCIIコードの順番:大文字が小文字の前に並びます(標準では、車両用に購入または組立てた最初の車両ユニットのメーカーです)。
- 収益 利益(収入から営業費用を差し引いたもの)の順番。
- 識別番号 車両固有のID番号(標準では、車両が最初に購入されたとき、または車庫で組立てられたときに割り当てられ、車両情報のタイトルバーに表示されています)の順番。

- 昇順 / 降順 一覧の順序を逆にします。

フィルター: 2つのオプションボタンは、リストの選択基準のオン/オフを切り替え、基準を変更するためのアクセスコントロールを行います。

- 有効 / 無効 クリックすると、一覧表の車両のフィルタ条件が切り替わります。

- 設定 フィルタ条件を変更するコントロールが開きます。

記載されている項目をクリックすると、その車両の詳細情報が表示されます。

各車両に記載されている項目は以下の通りです。

識別番号: コンボイの固有のID番号(デフォルトでは、コンボイが最初に購入されたとき、または車庫で組立てられたときに割り当てられ、車両情報のタイトルバーに表示されます)。

名前 が割り当てられています(標準では、コンボイが購入または組み立てられたときの最初の車両のメーカーです)。

収益は、利益(発生した収入から発生した運営費を差し引いたもの)を示しています。

路線: 割り当てられた路線を示し、車両が車庫にあるかどうかを示します。

グラフィックスは、構成と現在の負荷レベルを示しています。

simutrans-124.5/simutrans/text/ja/convoi_filter.txt000066400000000000000000000054711520675460500226010ustar00rootroot00000000000000乗り物の一覧フィルター

乗り物の一覧フィルター

乗り物の一覧フィルターには、乗り物一覧に表示する車両を決定するオプションがあります。

乗り物一覧の設定をクリックすると、乗り物の一覧フィルターが表示されます。

オプションボタンは、フィルタオプションが有効なときに乗り物一覧に表示する車両を選択します。設定された条件に合致した車両のみが表示されます。

四角いボタンをクリックしてフィルタの条件を設定します(ボタンは選択されると押し込まれます)。

名前フィルター: 車両の名前で選択します。これを使うには、ボタンをクリックして選択し、名前ボックスをクリックして必要な名前を正確に入力します(オプションは大文字と小文字を区別します)。

タイプフィルター: 選択された場合、輸送手段の種類別に車両をリストアップします。これを使用するには、ボタンをクリックして選択し、輸送の種類(道路、鉄道、水路、空路)をクリックします。

特殊フィルター: selects Convoys to list by following criteria (click on button to select, then click on): 一覧に載せる車両を以下の条件で選択します(ボタンをクリックして選択し、以下の中からクリックしてください)。
- ルートなし - 次の目的地へのパスが見つからない。
- スケジュールなし - ルートが割り当てられていない。
- 収入なし - 利益が出ていない。
- 入庫中 - 車庫に保管中のもの。
- 路線なし - 路線が割り当てられていない。

旅客/貨物フィルター: 旅客/貨物フィルター: さまざまな商品や乗客を輸送する能力によって車両を選択します。名前の横にあるボタンをクリックしてアイテムを選択します。スライダーバーを使って一覧のアイテムをスクロールします。オプションは以下の通りです。
- 全選択 - すべてのアイテムを選択します。
- 全解除 - すべてのアイテムを選択しません。
- 反転 - 現在選択されている項目を反転させます。

simutrans-124.5/simutrans/text/ja/convoidetail.txt000066400000000000000000000042011520675460500224050ustar00rootroot00000000000000編成の詳細

編成の詳細

編成の詳細では、編成(稼働中の車両または連結された車両全体)に関する詳細情報を提供します。また、ここから編成を直ちに売却することもできます。

編成の詳細は、編成情報ウィンドウにある詳細ボタンから開くことができます。
すべての車両が表示されていない場合は、編成の詳細ウィンドウをリサイズするか、スクロールバーを使用してください。

編成の詳細の上部には、以下の情報が表示されます。

出力: 編成内の全動力車の出力の合計。

売却価格: 編成を売却した場合に受け取れる金額(編成の購入価格から減価償却費を差し引いた額)。

警告: 売却モードボタンをクリックすると、編成は直ちに売却されます。追加の確認画面は表示されません。編成はゲームから削除され、プレイヤーの資金に売却額が加算されます。

編成の詳細には、編成内のすべての車両がリスト表示されます。
各車両について表示される情報は以下の通りです。
- 車両のグラフィック: 車両の画像(車庫でも使用されます)
- 製造年月: 製造された月と年。年代設定を有効にしてプレイしている場合は登場年月
- 売却価格: 売却した際に受け取れる金額
- 出力: 発生出力(動力車の合計)
- 現在の摩擦係数: 現在の摩擦(走行抵抗)レベル
- 最大収入: この編成が輸送する貨物の種類に基づいた、この車両の最大可能収入
- 積載品: この車両ユニットに積載されている貨物の数量と目的地

simutrans-124.5/simutrans/text/ja/convoiinfo.txt000066400000000000000000000206011520675460500221000ustar00rootroot00000000000000編成情報

編成情報

編成情報は、旅客や貨物を輸送する編成(稼働中の車両または複数の車両ユニットの組み合わせ)に関する情報を表示します。
編成が積載している品目を一覧表示するほか、詳細情報の表示、編成の売却、ゲームビューでの追跡、編成名およびスケジュールの変更を行うための操作項目も備えています。

編成は、少なくとも1つの動力車両ユニットと、それに連結された付随車や客車/貨車で構成されます。
編成の例としては、馬車を引く馬のチーム、テンダー(炭水車)と客車または貨車を連結した機関車、バス、バン、トレーラーを牽引するトラック、連結された路面電車、船舶、バージを押すタグボート、航空機などが挙げられます。
編成は、各輸送手段に対応した車庫で組み立て、購入します。
{ヒント: 1つの編成に連結できる車両ユニットの最大数は、pakセットに依存します。鉄道車両は通常、24両に制限されています。}

ゲームビュー上で調査ツールを使って編成をクリックするか、編成一覧または路線編集に表示されている編成をクリックすると、編成情報ウィンドウが開きます。
編成情報のタイトルバーには、編成名と固有のID番号が表示されます(詳細は後述)。
編成情報には、名前入力欄、ミニビュー、編成に関する情報、オプションボタン、現在積載している品目の一覧が含まれています(すべての品目が表示されない場合は、編成情報ウィンドウをリサイズするか、スクロールバーを使用してください)。

編成情報の上部に表示される情報は以下の通りです。

固有のID: 編成に割り当てられた固有のID番号(車庫で編成が最初に購入または組み立てられた際に割り当てられます)。

- 名前: 編成に割り当てられた名前を表示します(デフォルトでは、車庫で最初に購入・組み立てられた車両ユニットの名前になります)。
名前入力欄をクリックして新しい名前を入力すると、編成名を変更できます。

- 速度: 現在の速度をkm/hで表示します。
カッコ内は編成の最高速度です(編成内で最も遅い車両ユニットによって決まります)。
緑色のプログレスバーは、最高速度に対する現在速度の割合を示します。

- 収益: 現在のゲーム内年度において、編成が生み出した利益(運賃収入から運行コストを差し引いた額)です。
カッコ内は、1マスあたりの運行コストを示します。

- 重量: 編成自体の重量と積載品の合計重量(トン)です。
カッコ内は積載品の現在重量を示します。
緑色のプログレスバーは、旅客や貨物の定員・容量に対する現在の使用率(積載率)を示します。
黄色いバーは、スケジュール設定で設定された最低積載レベル(編成が出発するために必要な旅客・貨物の最小量)を示します。
{ヒント: 路線上のすべての編成の積むまで待機の設定を変更するには、路線編集または車庫ウィンドウの「路線更新」ボタンを使用してください。同じ路線の他の編成には影響を与えず、対象編成のみの設定を変更するには、編成情報または車庫ウィンドウのスケジュールボタンを使用してください。}

- 行先: 編成のスケジュールにおける次の停車駅(停留所)または経由地です。
緑色のプログレスバーは、次の目的地までの進捗を示します。

- 所属路線: この編成に割り当てられた路線です。三角マークをクリックすると、路線編集ウィンドウでその路線が開きます(路線が割り当てられている場合のみ表示されます)。

- ミニビュー: この編成を表示します。
ミニビューをクリックすると、この車両をゲームビューの中央に表示します。

編成情報のボタンをクリックすると、ウィンドウが開くか、オプションを選択できます。

貨物: 編成が現在積載している品目を一覧表示します。
表示される情報には、積載数量、編成の積載容量、品目の種類、最終目的地、および次の乗り換えが含まれます。
オプションボタン(選択内容によって名称が変わります)で、積載品目(旅客、郵便、貨物)の一覧を並び替えます。
- 目的地: 最終目的地のの名前でASCIIコード順(大文字が小文字より先)に並び替えます。
- : 積載量の多い順(降順)に並び替えます。
- 経由地: 最初の乗り換えの名前でASCIIコード順(大文字が小文字より先)に並び替えます。
- 経由地 (量): 最初の乗り換えへ向かう品目の量順で並び替えます。
- 経由地 (所有者): 次のの所有者ごとに量順で並び替えます。

スケジュール: ルートや、編成が出発するために必要な旅客・貨物の最小量を変更するための操作画面を開きます。

グラフ: このオプションボタンをクリックすると、編成情報内のグラフの表示/非表示を切り替えます(グラフ表示中はボタンが押された状態になります)。
グラフオプションを選択すると、過去12ヶ月間(横軸)の統計が表示されます。
各グラフオプションボタンをクリックすると、選択した情報がグラフに表示されます(選択中はボタンが押された状態になります)。
グラフの線の色は、グラフオプションボタンの色に対応しています。
- 空き容量: 編成の未使用スペースを示します。
- 輸送量: 輸送した貨物、旅客、郵便の数を示します。
- 収入: 輸送によって発生した収入を示します。
- 運行経費: 運行中に発生したコストを示します。
- 利益: 輸送から得られた利益(収入から運行経費を差し引いた額)を示します。

車両の詳細: 編成の詳細を開きます。より詳しい情報と、編成をすぐに売却するオプションが含まれています。

simutrans-124.5/simutrans/text/ja/curiosity_build.txt000066400000000000000000000052551520675460500231500ustar00rootroot00000000000000ランドマーク(歴史的建築物)建設ツール

ランドマーク(歴史的建築物)建設ツール

このダイアログは4つのセクションに分かれています。
· 左上は、利用可能な建物の選択リストです。
· 左下には、選択した建物の画像が表示されます。
· 右上には、いくつかのオプションが設定されています。
· 右下には、選択した建物や史跡などに関する情報が表示されます。

選択リスト

選択リストには、右上で指定したオプション(都市型施設郊外型施設モニュメント)で利用できる、すべての特別な建物やランドマークが表示されます。
2つのタブがあり、呼び物の建物の識別方法を変更することができます。
· 日本語名: 指定された言語で建物の名前を表示します。指定した言語の翻訳がない場合は、オブジェクト名が使用されます。
· オブジェクト: 建物タイプのSimutrans内部オブジェクト名を表示します。

文字の色には次のような意味があります。
· 青い文字 都市型施設は、都市部に作らなければならない
· 緑の文字 郊外型施設は、田舎に作らなければならない
· 黒文字 モニュメントは、一度しか建設できない

オプション

· 気候条件を無視する: このオプションは、様々な建物タイプに関連する気候の制限を無効にします。
· 年代設定を使用: 選択一覧には、現在のSimutransの暦年に利用可能な建物のみが含まれます。
· 旧型を表示: 選択一覧を拡張して、現在のSimutransの暦年に廃止された建物も含むようにします。
· 都市型施設: 選択一覧に都市の名所旧跡(観光地)が表示されます。
· 郊外型施設: 選択一覧に地方の名所旧跡が表示されます。
· モニュメント: 選択一覧にモニュメント(記念碑)が表示されます。
· 向き: 選択した建物が様々な角度から見ることができる場合、ここで特定の向き(ビュー)を選択することができます。左下の画像はこれを反映して変化します。このオプションがランダムに設定されている場合は、建物の向きはランダムに使用されます。

simutrans-124.5/simutrans/text/ja/curiositylist_filter.txt000066400000000000000000000050701520675460500242250ustar00rootroot00000000000000名所旧跡一覧

名所旧跡一覧

名所旧跡一覧には、乗客や郵便物の目的地と出発地の両方になっているすべての観光名所の情報があります。

名所旧跡一覧を開くには、一覧表名所旧跡一覧をクリックします。
{ヒント: 一覧のアイテムが部分的にしか表示されていない場合は、名所旧跡一覧サイズを変更するか、スライダーバーを使って一覧をスクロールしてください。}

並び替え: 名所旧跡の表示順を決めるオプションがあります。
ボタンをクリックすると、オプションが切り替わります(オプションボタンの名前が変わります)。

- 名前: 名前のアルファベット順に表示されます。
- 旅客度 乗客や郵便物の目的地としての相対的な人気度で並びます。

- 昇順 / 降順 一覧の順序を逆にします。

名所旧跡一覧に掲載されている項目をクリックすると、その観光地についての詳細情報が表示されます。
{ヒント: 詳細情報の中の観光地の写真をクリックすると、その観光地にゲームビューが移動します。}

それぞれの名所旧跡に掲載されている項目は以下の通りです。

ステータス・カラーバー の色は、名所旧跡の評価を示します。
- 黄色: の集客エリアに入っていない。
- 緑色: 旅客用の駅があります。
- 青色: 郵便用の駅があります。
- 青緑: 旅客と郵便用の駅があります。
- 橙色: 関連する駅が混雑しています。
- 赤色: 関連するすべての停留所が混雑しています。

建物の画像 は、それがの名所旧跡であるかどうかを示します。

名前 名所旧跡の名前です。

括弧内の旅客度は、乗客や郵便の目的地としての相対的な人気を示す値です。

{ヒント: 最適なサービスを提供するためには、名所旧跡全体が1つまたは複数の停留所の集客エリア内にある必要があります。}

simutrans-124.5/simutrans/text/ja/depot.txt000066400000000000000000000063451520675460500210530ustar00rootroot00000000000000車庫

車庫ダイアログ

 車庫ダイアログでは、それぞれ列車、車両、船舶の購入やスケジュール設定
を行えます。ここでは列車の場合で解説しますが、車両や船舶においても操作
方法は同じです。

 ダイアログの最上部には、現在編成中の列車の情報が表示されます。テキス
トフィールドには列車名が表示されます。ここに文字を入力することで、列車
名を変更できます。 ォボタンと サボタンで、編成対象の列車を変更できます。
ここで「新しい編成」を選択すると、新規の列車の編成になります。

 その下には、編成中の列車を操作するための次のボタンがあります。

スタート - 編成中の列車の運行を開始します。運行を開始するためには、
あらかじめスケジュールが設定されていなければいけません。
編成解除 - 現在の編成を解体します。解体された編成の車両はストックされ、
他の編成に組み込むことが出来ます。
売却 - 現在の編成を構成している車両を全て売却します。現在の売却価格は
「売却価格」に示されており、購入時からの時間の経過に伴って減少していきます。

 ダイアログの中段部分には、購入できる車両の一覧が表示されています。車両は機関
車と客車・貨車に分かれており、「機関車」タブ、「貨車・客車」タブで表示を切り替
えられます。車両の画像にマウスカーソルを重ねると、その車両の情報がダイアログ下
部に表示されます。クリックすると、その車両を購入し、現在の編成に組み込みます。

 各車両の下にある色付きのバーは、その車両を現在の編成に組み込むことが
出来るかどうかを示しています。緑の車両は、現在の編成に組み込むことが出
来ます。赤の車両は、現在の編成には組み込むことが出来ません。緑と黄色の
混じった車両は、現在の編成に組み込むことが出来ますが、その編成を完成さ
せるためには、さらに他の車両を購入しなければならないことを示しています。
後部機関車が必要な列車などが、この表示になります。

 車両の左上にある数字は、その車両のストックがあることを示しています。
ストックのある車両は、クリックすることで現在の編成に組み込むことが出来
ます。

 右側にある「車両」ボタンでは、購入した車両を現在の編成のどの位置に組
み込むかを変更できます。「追加」の状態では、現在の編成の最後部に追加し
ます。「先頭へ」では、現在の編成の先頭に組み込みます。「売却」では、車
両の購入ではなく、ストックされている車両の売却モードになります

simutrans-124.5/simutrans/text/ja/display.txt000066400000000000000000000025671520675460500214070ustar00rootroot00000000000000表示オプション

表示オプション

 このダイアログでは、表示に関する様々な設定をすることが出来ます。
 オプションは次の通りです。

明るさ - 画面を明るくしたり、暗くしたりします。
色合い - 動作しません。
スクロールの早さ - この値が大きいほど、スクロールが早くなります。

スクロール方向の逆転 - マウスを動かす方向と、スクロールの方向の対応が
逆になります。例えば、右に動かすと左にスクロールします。
歩行者を表示する - 街に歩行者を生成して表示するかどうかを設定します。
コンピュータの処理能力が不足している場合は、このオプションをオフにして
ください。
夜は暗くする - 1日の昼と夜のサイクルによって画面の明るさを変えます。
オンにすると、夜には綺麗な夜景が現れます。

 ダイアログの下の部分は、コンピュータのパフォーマンスに関する情報を示
しています。
注意 - この情報は、バグ報告の際やパフォーマンス向上の為に、お聞きすることがあります。

simutrans-124.5/simutrans/text/ja/enlarge_map.txt000066400000000000000000000006161520675460500222050ustar00rootroot00000000000000マップ拡張

マップ拡張

マップは右か下に向けて拡張できます。もし左または上に拡張したい場合は、まずマップを回転させてから、拡張を行い、再び回転させて向きを戻します。
拡張されたマップのプレビューが、ダイアログウィンドウの右上に表示されます。dow.

simutrans-124.5/simutrans/text/ja/factorylist_filter.txt000066400000000000000000000054121520675460500236420ustar00rootroot00000000000000産業一覧

産業一覧

産業一覧には、現在のゲームに登場するすべての産業(商品の供給者と消費者)の情報が表示されます。

産業一覧を開くには、一覧表で産業一覧をクリックします。
{ヒント: 一覧のアイテムが一部しか表示されていない場合は、産業/工場リストのサイズを変更するか、スライダーバーを使って一覧をスクロールしてください。}

並び替え: リストアップされた産業の順序を決定するオプションがあります。
ボタンをクリックすると、オプションが切り替わります(オプションボタンの名前が変わります)。

- 産業名は、名前のアルファベット順に並びます。
- 供給 供給保管能力の容量順。
- 製品 製品保管能力の容量順。
- 生産 生産能力による産業順。
- Rating orders Industry by status-colour-bar (see below). ステータスカラーバーによる産業順(下記参照)。
- 電力 供給電力による産業順。

- 昇順 / 降順は一覧の順序を逆にします。

産業一覧に掲載されている項目をクリックすると、その産業の詳細が表示されます。
各産業には以下の項目があります。

ステータスカラーバー カラー(色)はその産業の稼働状況を示します(産業情報でも使われます)。
- 白色: 供給を必要としない。
- 黄色: 輸送手段でつながっているが、産業のサプライチェーンでは供給不足。
- 緑色: 最適な稼働状態。
- 橙色: 稼働中だが、改善の余地あり。
- 赤色: 稼働中だが、産業のサプライチェーンの一部が供給過剰。

赤色の稲妻マークは、変電所を使って電気を供給している産業の場合に表示されます。

産業名: その産業の名前。

括弧内は3つの値を表しています。
現在の供給量: 処理可能な状態で保管されている原料の在庫量。
現在の生産量: 出荷する準備ができている生産量。
生産能力: その産業の生産能力(1ヶ月に生産される製品の最大量)。

simutrans-124.5/simutrans/text/ja/finances.txt000066400000000000000000000113421520675460500215170ustar00rootroot00000000000000財務

財務

財務ウィンドウでは、プレーヤー会社の財務状況を確認することができます。

メインビューの上部にあるお金のアイコンをクリックするか、[f] を押すか、プレーヤーダイアログでプレーヤーの名前をクリックすると表示されます。

貨幣単位はHajoクレジットで、現在と過去の月・年ごとに表示されます。グラフの時間軸を変更するには、対応するタブをクリックしてください。
- :年間の値を表示します。
- :月単位の値を表示します。

グラフは、Y軸を量、X軸を時間として表示します。複数の曲線を同時に表示することができ、グラフ上の点にカーソルを重ねるとツールチップに数値が表示されます。

右上の本社を建設するボタンを押すと、会社の本社を建設したり、アップグレードしたりすることができます(グラフィックがパックセットで提供されている場合)。本社は高レベルのアトラクションとして機能し、いくつかのシナリオでは必要になるかもしれません。


値(名前のボタンをクリックするとグラフで詳細を表示)は、以下の通りです。

輸送実績(トリップ) - 移動したすべてのものの数(乗客、郵便、商品を数えます)。乗り換えは1区間につき1回カウントされます。

収入(車両収入):旅客、商品、郵便の輸送による総収入。

運行費(輸送コスト) - 輸送中にすべての輸送機関が被る総コスト。
{Tips: 車両の1kmあたりの運行コストは、車庫や車両情報で確認できます。}

維持費 - 輸送ネットワークの維持にかかる総費用。
{Tips: ネットワークを維持するための月ごとのコスト(財務の右に表示されている維持費(月))は、ゲームの暦月の終わりに差し引かれます。}

通行料(道路通行料) - 外国の道路を使って輸送車両を走行させる場合、コストが発生することがあります。他のプレイヤーの道路を使用している場合は、支払わなければなりません。しかし、あなたが非常に良い道路接続を提供している場合、あなたは他の人からも収入を得ることができます。

送電線 - 高出力の送電による電気の販売から得られる収入です。

運営利益 - 輸送、道路通行料、送電による利益から、維持費と運営費を差し引いたもの(上記すべての合計)。

購入費(新規車両) - 車両の購入および販売にかかる支出および収入。

建設費(建設現場)-交通網、造成、高電力送電線の建設、削除/取り壊しやその他の除去ツールを使用した経路(線路、道路、運河、滑走路)の建設にかかる総費用。

粗利益(キャッシュフロー) - 総収入(上記のすべてを合計したもの)。


財務ウィンドウ右半分の縦列には、4種類の数値が表示されます。これらは合計額(累積値)です。

現金 - 建設や車両購入に使用できる現金口座の残高です(ゲームビューのボトムバー中央にも表示されます):

資産 - カレンダーの前月末時点での、プレーヤー会社所有のすべての車両の総価額です。
{ヒント: 車両の現在の販売価格は車両の 車両の詳細 または車両が車庫(機関庫)にあるときに見ることができます。}

利益率 (%) - 売上高に対する営業利益の割合です。

純資産 - 現金残高に資産を加えたもの。会社の価額を表しています。純資産がマイナスになると、会社は倒産し、ゲームに負けてしまいます。

{ヒント: ゲーム内の一部の費用、ゲーム開始時の収益率や財務は、simuconf.tab で変更できます}。

シナリオ情報

シナリオをプレイする場合には、シナリオのゴールと進捗率が表示されます。

simutrans-124.5/simutrans/text/ja/general.txt000066400000000000000000000065011520675460500213470ustar00rootroot00000000000000Simutrans総合ヘルプ

総合ヘルプ

これは主なヘルプファイルの一覧です(すべてではありません)。
※印のものは長い間、更新されていません。現在の内容と大きく異なる場合があります。 英語のものは、日本語ヘルプがまだありません。

キーボード

ゲームオプション
※ゲームオプション
※言語
※プレイヤーの色
※表示設定
※サウンド
※ライバル会社
※ゲームの再開
※ゲームの保存
※新しいマップの作成
気候の設定
地形データ読み込み
高度な設定

メインメニュー
※設定を選ぶ
※マップ
調査ツール
※スロープツール
※鉄道ツール
※信号/標識の間隔設定
モノレール/リニアツール
市電/軽便鉄道ツール
※道路ツール
※船舶ツール
航空機ツール
特別建築物建設ツール
撤去/取り壊し
Game Edit Tools

一覧表
ライバル会社一覧
路線編集
一覧表
駅一覧
・・駅一覧フィルター
乗り物一覧
・・乗り物一覧フィルター
※街一覧
貨物一覧
産業一覧
名所旧跡一覧
※メッセージ表
財務(財務一覧)

その他のダイアログ
※ゲームの情報ウィンドウ
※パスワード設定ウィンドウ
車両の情報
車両の詳細
※車庫ダイアログ
※スケジュールダイアログ
※駅情報ダイアログ
※駅情報詳細ダイアログ
※街の詳細
産業の情報
交通信号機の情報
指定座標へ移動

simutrans-124.5/simutrans/text/ja/goods_filter.txt000066400000000000000000000075671520675460500224270ustar00rootroot00000000000000貨物一覧

貨物一覧

貨物一覧には、ゲーム内で輸送可能なさまざまなアイテムと、それらを輸送することで得られる収入の情報が含まれています。

開くには、一覧表で貨物一覧ツールをクリックするか、[G] を押します。

ゲーム内で輸送可能なアイテムのリストには、受け取った収入、速度ボーナス(%)、貨物のカテゴリー、重量が表示されます。
{ヒント: 一覧のアイテムが一部しか表示されていない場合は、貨物一覧サイズを変更するか、スライダーバーで一覧をスクロールしてください。}

速度ボーナス: 一覧に記載されている収益を得るために必要な、さまざまな輸送タイプの車両の平均最大速度が表示されます。平均最高速度が表示されている速度よりも速い場合、いくつかのアイテムはより多くの収益を得ることができますが、遅い場合は収益が少なくなります。
年代設定を有効にすると、時間の経過とともに速度の値が変化することがあります。この変更は通常、より高速または経済的な車両の導入に伴って行われます。
矢印ボタンを使って速度を一定の割合で調整し、表示されているすべての収益を更新することができます。車両の平均最高速度がボーナス速度に達していない場合の収益を、手動で計算することなく確認したい場合に便利です。

並び替え: アイテム一覧の表示順を示します。
オプションボタンをクリックすると、さまざまな並べ替えオプションが表示されます(オプションボタンの名前が変わります)。

- 名前順は、名前のアルファベット順に表示します。
- 収入順は、収入を得た順に表示します。
- ボーナス別は、速度ボーナスの % 別に並び替えます。

- 昇順 / 降順は、一覧の順序を逆にします。

各項目の詳細は以下の通りです。

色つき四角は、ゲームビューでストップの上にある貨物の色バーに表示されている色と同じ色で、輸送待ちのアイテムの量を示します。
{ヒント: [ ! ] を使うと、ゲームビューのストップ上の貨物の色バーが切り替わります。}

収入とは、現在の支払いモデルにおいて、1ユニットを1タイル分の距離だけ輸送するのに受け取る収入のことです。
これは {(基本収入) x (1 + ((平均最大速度) / (速度ボーナス) - 1) x (ボーナス%))} で計算されます。

ボーナス %は、車両の最大速度に比例して変化する収入の割合であり、 速度ボーナスの速度に応じて反比例します。

貨物カテゴリは、輸送に必要な車両の種類を示します。特殊な貨物は特定の車両を必要とします。
{ヒント: 車両の積載物は、車両がどのアイテムを輸送できるかを示しています}。

重量は、貨物 1ユニット当たりの重さをキログラム単位で示します。
この重量は輸送中の車両重量に加算されるので、空の時より車両を動かすのに多くの馬力が必要になります。

simutrans-124.5/simutrans/text/ja/haltlist.txt000066400000000000000000000117661520675460500215670ustar00rootroot00000000000000駅一覧

駅一覧

駅一覧には情報が表示され、車両が荷物や乗客を積んだり降ろしたりするさまざまなストップ(水上輸送専用の埠頭以外も含む)をフィルタリングしてリストアップする機能があります。

これを開くには、一覧表の駅一覧ツールをクリックします。駅一覧には4つのオプションボタンがあり、2つは一覧内のアイテムの順番を決めるため、2つは一覧に表示するアイテムの選択にオプションを適用するために使用します。ボタンの下には、フィルターオプションの条件に合うストップの一覧が表示されます。
{ヒント: ストップの一覧がない場合は、フィルターオプションを変更してください。一覧のアイテムが一部しか表示されていない場合は、駅一覧サイズを変更するか、スライダーバーで一覧をスクロールしてください。}

ボタンをクリックすると、オプションが切り替わり(オプションボタンの名前が変わります)、フィルタ設定コントロールが開きます。

並べ替え: 2つのオプションボタンで、一覧に表示されるストップの順番を決定します。

- タイプ: 受け入れ可能な車両のタイプ別に表示します。
- ランダム: ランダムな順序で表示します。
- 名前順: 割り当てられた名前のASCIIコード順の英数字で表示します(大文字が小文字の前にきます)。
- 待機数: 提供される輸送サービスに関連する、現に存在する商品と乗客の数量による順序。
- 昇順 / 降順: 一覧の順序を逆にします。

フィルター: 2つのオプションボタンは、一覧の選択基準をオン/オフし、基準を変更するためのアクセスコントロールを行います。

- 有効 / 無効: クリックすると、フィルタ基準が切り替わります。

- 設定: クリックすると、フィルター基準を変更するためのコントロールが表示されます。

ステーションリストに表示されている項目をクリックすると、そのストップに関する詳細情報が表示されます。
各ストップに表示される項目は以下の通りです。

ステータスカラーバー: 各色はストップの混雑度など、そのストップの運営状況を示します。このカラーバーは、ストップ情報と路線編集でも使用され、ゲームディスプレイのストップの上にあるカラーバーに表示される色と同じです。
- 黄色: 経路、何も待っていない状態。
- 緑色: 改善の必要なし。
- 橙色: ストップがやや混雑している(主に貨物が多い)。
- 赤色: 定員の1.5倍以上、または200人以上の不幸な待機客がいる、またはストップが混雑しているので工場は生産を停止しなければならない。
{ヒント: [ ! ] を使うと、ゲーム表示でストップの上のカラーバーが切り替わります。}

名前 割り当てられた名前。
割り当てられた名前{ヒント: simuconf.tabでストップ名に数字を割り当てるオプションがあります。}

車両アイコンは、どのタイプの車両がストップを使用できるかを示します(停留所情報とライン管理でも使用されます)。
アイコンには、バス(道路用の乗用車)、トラック(道路用の貨物車)、列車、船舶、航空機があります。市電(路面電車)は、停留所の種類に応じて、バスアイコンまたは列車アイコンで表示されます。

貨物アイコンは、そのストップがどのようなアイテム(旅客、貨物、郵便物)を扱うことができるかを示します(駅情報や路線編集でも使用されます)。
{ヒント: 適切な拡張施設を追加することで、ストップが扱えるアイテムのカテゴリーが変わります。郵便物の取り扱いを可能にするために、郵便局をストップに追加することもできます}。

待機中は、ストップで待機している色々な貨物や乗客の詳細です。

simutrans-124.5/simutrans/text/ja/haltlist_filter.txt000066400000000000000000000057041520675460500231270ustar00rootroot00000000000000駅一覧フィルター

駅一覧フィルター

駅一覧フィルターでは、乗客の乗り降りを行うストップ(駅・停留所)(水上輸送のための港も含む)を駅一覧に表示するかどうかを設定することができます。

駅一覧の設定をクリックすると、駅一覧フィルターが表示されます。

オプションボタンは、フィルターオプションが有効な場合に、駅一覧に表示するストップを選択します。設定された条件に合致するストップのみが一覧に表示されます。

ボタンをクリックすると、フィルタの条件を選択/解除することができます(ボタンを選択すると、押し込まれた状態になります)。

名前フィルター: ストップを名前で選択します。使用方法:ボタンをクリックして選択し、名前ボックスをクリックして必要な名前を正確に入力します(オプションは大文字と小文字を区別します)。

タイプフィルター: 以下の項目のうち、少なくとも1つ以上の項目を持つストップを選択します。使用するには、ボタンをクリックしてください。(水上輸送専用の埠頭は、駅一覧には表示されません。)

特殊フィルター:
- 混雑中は、使用できません。
- 接続なしは、輸送経路のないストップを選択します。

製品需要: 選択されたアイテムの最終目的地となるストップのことです。名前の横にあるボタンをクリックすると、アイテムが選択されます。スライダーバーでアイテムのリストをスクロールします。選択肢は以下の通りです。
- 全選択:すべてのアイテムを選択する。
- 全解除:アイテムを1つも選択しない。
- 反転:現在選択されているアイテムを反転する。

生産品: 選択されたアイテムの輸送を開始するストップを参照します。名前の横にあるボタンをクリックすると、アイテムが選択されます。スライダーバーを使ってアイテム一覧をスクロールします。オプションは以下の通りです。
- 全選択:すべてのアイテムを選択する。
- 全解除:アイテムを選択しない。
- 反転:現在選択されている項目を反転する。

simutrans-124.5/simutrans/text/ja/industry_info.txt000066400000000000000000000137301520675460500226300ustar00rootroot00000000000000産業情報

産業情報

産業情報 では、選択した産業(商品の供給会社または消費者)の詳細が表示され、ゲームビューをゲームワールドの関連する位置に移動することができます。

商品の供給者と消費者は、最終消費者に到達する産業界のサプライチェーン(流通プロセス)を提供します。ある産業は、消費者であると同時に供給者でもあり、産業サプライチェーンの中間に位置しています。初心者モードでなければ、消費者が供給過剰になった場合、産業は供給を停止します。
産業は、乗客や郵便の目的地や発信地になることもあります。車両は、停車場の集客エリアが産業の建物の一部をカバーしていて、停車場がそうした対象を扱える場合、停車場を経由して産業に商品や乗客を集めたり届けたりします。{ヒント: 沖合の埠頭/石油掘削所/養魚場/集散地/石油/郵便/旅客/停留所一覧に記載されていないもの}

新しいゲームでは、初期の産業サプライチェーンの数を設定することができます。都市部の成長に伴い、より多くの産業が出現する可能性があります。2000年頃から都市部の人口が倍増するたびに、新しい産業が出現します。
{ヒント。ゲーム編集ツールの最寄りの都市に新しい市場を作るツールを使って、都市部に産業を追加します。cityrules.tab で新しい産業が発生するタイミングを変更します(industry_increase_every = 0 では新しい産業が発生しません)}。

調査ツールでゲームビューに表示されている産業、または産業一覧に表示されている産業をクリックすると、産業情報が表示されます。
すべての項目が表示されていない場合は、スライダーバーを使って項目をスクロールします。

選択された産業の名前は、産業情報のタイトルバーに表示されます。
。 その他、選択された産業に関する情報やオプションは以下の通りです。

生産:産業が供給する(供給側の場合)、または消費する(最終消費者の場合)商品の最大レート(単位:月/日)。
{ヒント: 変電所を追加して、産業へ電気を供給できるよう追加すると、商品の生産/消費率が増加します}。

産業の画像は、産業の建物の外観を表しています。画像をクリックすると、ゲームビューが産業の建物に移動します。

画像の下のステータスバーの色は、産業の稼働状況を示しています(産業一覧表でも使用されています)。
- 白色: 供給は必要ありません。
- 黄色: 輸送でつながっていますが、産業サプライチェーンは供給力が弱い。
- 緑色: 最適な状態です。
- 橙色: 稼働していますが、改善の余地があります。
- 赤色: 稼動していますが、産業サプライチェーンの一部が供給過剰です。

需要先: 選択された産業で生産された商品の、産業のサプライチェーン上の目的地が記載されています。
目的地となる産業とその地図上の座標が矢印ボタンで表示されます。矢印ボタンをクリックすると、ゲームビューが目的地に移動します。

供給元(仕入先): 選択された産業が必要とする、産業サプライチェーンにおける商品の起源を示します。
供給元の産業とその地図座標は、矢印ボタンで表示されます。矢印ボタンをクリックすると、ゲームビューが供給元へ移動します。

労働者居住地: 従業員が近くのどの都市部に住んでいるのかが分かります。 これらの都市部からの乗客や郵便物は、選択された産業に到達する必要があります。
。 都市部とその地図の座標は矢印ボタンで表示されます。矢印ボタンをクリックすると、ゲームビューが供給元へ移動します。

旅客度: 乗客にとって目的地としての相対的な人気です。
郵便度: 郵便の宛先としての相対的な人気です。
{ヒント: 乗客と郵便のための最適なサービスを提供するためには、産業の建物の1マスだけが停車場の集容エリアに入る必要があります}。}

生産: 産業の生産品の詳細が分かります。
表示される情報は、アイテム(貨物)の名前、製品の最大保存容量および現在の保存量、アイテムのカテゴリー、供給に対する消費のレベルの割合などです。

消費: 産業の供給に関する詳細を提供します。
表示される情報は、アイテムの名前、アイテムの最大保存容量と比較した現在の保存量、アイテムのカテゴリー、供給に対する消費の割合レベルなどです。

産業に接続している駅や停留所は、産業情報ウィンドウのボトム(下部)に掲載されています。

simutrans-124.5/simutrans/text/ja/inspection_tool.txt000066400000000000000000000155261520675460500231510ustar00rootroot00000000000000調査ツール

調査ツール

調査ツールは、さまざまなアイテムの情報やオプションコントロールを提供するダイアログを開くために使用します。

ゲームビューの上部にある虫眼鏡のアイコンをクリックするか、[a] を押すと、調査ツールが開きます(カーソルが虫眼鏡に変わります)。

調査ツールを使うには、ゲームビューの拡大鏡カーソルを必要な位置に置き、クリックしてダイアログを開きます。必要な位置に複数のアイテムがある場合は、さらにクリックすると2つ目のダイアログが開きます。
{ヒント:simuconf.tabには、指定されたカーソルの位置で1つのダイアログまたはすべてのダイアログを開くオプションがあります。}

ゲームビュー上の以下のアイテムをツールでクリックすると、様々なダイアログが表示されます。

機関庫(車庫) - その機関庫で車両の購入と管理を行うためのコントロールを開きます。

ストップ(駅や停留所) - ストップ - そのストップについてのストップ情報を開きます。

車両 - その車両の車両/車両情報を開きます。

タウンホール(自治体の庁舎) - その市町村の街の情報が開きます。
{ヒント:タウンホールに関する情報を提供するダイアログを開くオプションは、simuconf.tabで利用できます}

産業 - 産業 - その産業についての産業情報を開きます。

建築物 その建物に関する情報を提供するダイアログが開きます(ダイアログのタイトルバーは「建物」です)。ダイアログに含まれる情報は以下の通りです。
- 説明 建物に関する情報を提供します。もし説明がない場合は、「住宅」、「産業用建物」、「ショップと商店」(商業施設の場合)のいずれかが表示されます。
- City of : その市街ビルがどの都市圏に属しているかを示します。
- 旅客度: 乗客の目的地としての相対的な人気度です。
- 郵便度: 郵便物の目的地としての相対的な人気を表します。
- 登場する年: 時系列で見た場合、建物が現れる年を示します。
- 価格: 撤去/取り除くツールで建物を除去した場合に発生する費用を示します。
- 作者: Simutransのために建物を描いた人の名前です。

モニュメント名所旧跡ダイアログでは、その名所に関する情報が表示されます(ダイアログのタイトルバーには、それがモニュメントか名所旧跡かが表示されます)。ダイアログに含まれる情報は以下の通りです。
- 公共物 これは、プレイヤーの会社が所有していないことを示します。
- 説明 名所旧跡に関する情報を提供します。
- 旅客度: 乗客の目的地としての相対的な人気度です。
- 郵便度: 郵便物の目的地としての相対的な人気を表します。
- 登場する年: 年代設定でプレイしている場合、建物の登場年を示します。
- 作者: Simutransのために名所旧跡を描いた人の名前です。

道路と線路 道路やトラックについての情報を提供するダイアログが開きます(ダイアログのタイトルバーには、その道路の種類が表示されます - 道路上に作られた路面電車の場合、タイトルバーには道路の種類が表示されます)。
ダイアログには、以下の情報が含まれます。
- 最大速度: 道路や線路で許容される最大速度。
- Ribi (unmasked): これはwayの方向を示す内部カウンターです。
- Ribi (masked): 信号や標識を考慮した上で、進行方向を示す内部カウンターです。
- with sign/signal 信号や標識がある場合に表示されます。
- 電化されているされていない は、軌道が電化されているかどうかを示します。
- 先月通過した車両 先月通過した車両は、前のゲームのカレンダー月にウェイを使用した車両の数を示します。
- reserved by は、どの車両が通過するまでトラックを使用するために予約したかを示します。
- wayslope もしあれば、傾斜の方向を示します。
- 座標 ボトムバーに表示されている地図の座標です。
道路上に建設された市電(路面電車)の場合、情報は2回表示され、最初は道路、次に市電が表示されます。

シグナル/Signs ダイアログを開き、ダイアログのタイトルバーには信号か道路標識かが表示されます。ダイアログには以下のような情報が含まれます。
- direction は、信号が適用される方向を示す内部カウンターです。

送電線 / 変電所 では、電力供給網に関する情報を提供するダイアログが開きます(ダイアログのタイトルバーは「送電線」)。表示される情報は現在の需要と供給です。

のダイアログが開きます(ダイアログのタイトルバーは「木」)。表示される情報は以下の通りです。
- 名前 樹木の種類を示します。
- 樹齢 樹齢を月単位で表示しています。

土地ダイアログボックスが表示されます(タイトルバーは「土地」)。表示される情報は以下の通りです。
- スロープ 樹木の種を提供します。
- マップ座標 ボトムバーに表示される地図上の座標。

{ヒント: 私道の利用者や歩行者に関するダイアログを開くオプションは、simunconf.tab で利用できます。}

simutrans-124.5/simutrans/text/ja/jump_frame.txt000066400000000000000000000013531520675460500220570ustar00rootroot00000000000000指定座標へ移動

指定座標へ移動

SHIFT + [ J ] キーでダイアログが開き、指定した座標へのジャンプが可能になります。これは、ゲームワールド上の特定の位置に素早く移動するのに便利です。

ゲームワールド上のすべてのタイルには2つの座標(x,y)があり、(0,0)はマップの左上の隅に位置しています。カーソルの座標は、ステータスバーに常に表示されます。
注意: マップの回転( SHIFT + [ R ] キーで実行)は、すべてのタイルの座標を回転させます。

simutrans-124.5/simutrans/text/ja/language.txt000066400000000000000000000017211520675460500215140ustar00rootroot00000000000000言語

言語ダイアログ

 Simutrans は複数の言語をサポートしています。このダイアログで使用したい言語を選択することで、ゲーム中のメッセージの言語を切り替えることが出来ます。現在開いているウィンドウの表示内容は変更されません。また、すでに存在する街の名称は変更されませんので、その言語らしい町名でプレイしたい場合には、ゲームを開始する前にあらかじめ言語を選択しておくと良いでしょう。

 この一覧で選択できる言語の他にも、http://www.simustation.de.vu や http://www.simutrans.de でいくつか公開されています(ただし不完全であったり最新版に対応していない可能性があります)。

Simutransをあなたの言語に翻訳するために、https://translator.simutrans.comにアクセスします

simutrans-124.5/simutrans/text/ja/linedetails.txt000066400000000000000000000131661520675460500222340ustar00rootroot00000000000000路線情報

路線情報

路線情報ウィンドウでは、単一の「路線」(同じスケジュールを共有する編成のグループ)のプロパティを表示・編集できます。このウィンドウは、車庫編成情報、または路線リストから開くことができます。

上部には路線名が表示されています。枠内をクリックすることで名前を変更できます。その下には、編成数、使用容量(積載率)、および利益が表示されます。その下には4つのタブがあります。

スケジュール: この路線に所属するすべての編成スケジュールを一括で変更できます。変更内容は、ウィンドウを閉じるか、他のタブに切り替えた時に適用されます。なお、車庫編成情報からこのウィンドウを開いた場合、このタブが自動的に選択されます。

グラフ: 過去12ヶ月間の路線の月別統計をグラフで表示します。グラフの色はスイッチの色に対応しています。
- 空き容量: 各運行区間における、全編成の未使用の積載容量の合計
- 輸送実績: 各運行区間で実際に輸送した荷物・旅客の合計
- 収入: この路線の全編成が稼いだ運賃収入の合計
- 運行費: この路線の全編成の運行にかかったコスト
- 通行料: 他のプレイヤーが所有する線路や道路を利用した際に発生した費用
- 利益: 収入から運行費を差し引いた純利益
- 編成数: この路線を走行している編成数
- 走行距離: 全編成の月間総走行距離
- 速度: 編成の平均速度

編成一覧: この路線に割り当てられているすべてのコンボイを表示します。もし特定のコンボイが見当たらない場合は、そのコンボイに手動で新しいスケジュールが割り当てられ、路線から外れてしまった可能性があります。

3つの管理ボタンがあります:
- 路線削除: この路線を削除し、このウィンドウを閉じます。接続されているすべての編成は、現在のスケジュール通りに運行を「路線なし」の状態で継続します。
- すべて引退: この路線の車庫外にいるすべての編成を撤退させます。編成はそれ以降、新規の荷積みを行わなくなり、積荷が空になった時点で売却されます。この機能は、この路線の編成を刷新したい場合に便利です。(この路線に接続している車庫で新しい編成を編成してこの路線に割り当てます。その後、すべて引退を押し、新しい編成を必要な数だけ複製し、すべての新しい編成を発車させます。
- 一致する編成を検索:

ボタンの下には編成の一覧が表示されます。項目をクリックすると、その編成の詳細情報が表示されます(リストをスクロールするにはスライダーを使用してください)。
各編成について表示される項目は以下の通りです:
名称: 割り当てられた名前(デフォルトでは購入・編成された最初の車両の名称になります)
収益: 利益(収入から運行費を差し引いた額)
行先:
路線: 編成に割り当てられた路線の名前が表示され、車庫内にあるかどうかも示されます。
グラフィック: 編成の内容と、現在の積載状況を視覚的に表示します。

駅一覧: 路線のを表示します。リスト内の駅名をクリックすると、その駅の情報が開きます。
各項目の内容は以下の通りです:
名称: 駅の名前(中継点は表示されません)。名前の色は、混雑具合や稼働状況を示しています。これはゲーム画面上の駅名バーの色とも連動しています:
- : 休止中(運行停止中)
- : 良好(改善の必要なし)
- オレンジ: 混雑(改善の余地あり)
- : 過密(改善を強く推奨)
名称の横に、その駅を利用できる車両の種類を示す車両アイコンが表示されます。アイコンには、バス、トラック、鉄道、船舶、飛行機があります。路面電車は、駅のタイプに応じてバスまたは鉄道のアイコンで表示されます。
次の行には、その駅が取り扱える種類(旅客、貨物、郵便)を示す貨物アイコンと、待機中の旅客・貨物数の概要が表示されます。
{ヒント: 適切な付属施設を追加することで、その駅で取り扱える種類を増やすことができます。例えば、郵便局を設置すると郵便の取り扱いが可能になります。}

simutrans-124.5/simutrans/text/ja/linemanagement.txt000066400000000000000000000066521520675460500227250ustar00rootroot00000000000000路線編集

路線編集

路線一覧 路線を管理します(同じルートを共有する車両のグループ)

路線には、荷物や乗客を乗せたり降ろしたりするストップ(係留場)(水上輸送では埠頭の集荷域内の水辺を利用できます)と、ウェイポイント(複数の選択肢がある場合に車両を誘導したり、暫定的な目的地を提供するために使用されます)があります。

ゲームビューの上部にあるネットワークアイコンをクリックするか、 [w] を押すと路線編集が表示され、このサイズは変更することもできます(タイトルバーの下向き矢印をクリックすると本来のサイズに戻ります)。

The 路線一覧 lists existing Lines (to scroll list: use slider-bar on right of list), which can be filtered by name or by transport type (click on a index-tab above list to select): ラインリストには、既存の路線がリストアップされています(一覧をスクロールするには、一覧の右にあるスライダーバーを使用)。この一覧は、名前や輸送タイプでフィルタリングできます(リスト上のインデックスタブをクリックして選択します)。
全てAll: すべての車両の経路がリストアップされます。
列車: すべての鉄道車両の路線がリストアップされます。
モノレール: すべてのモノレール/リニアモーターカー車両の路線をリストアップします。
市電: すべての路面電車の路線をリストアップします。
自動車: すべての道路交通車両の路線をリストアップします。
船舶: すべての水上交通機関の路線をリストアップします。
航空機: すべての航空機の路線をリストアップします。

一覧に表示される路線名の色は以下の通りです。
白色 - 路線に割り当てられた車両がない。
黄色 - 稼動しておらず、損益が出ていない。
黒色 - 利益を出している。
青色 - 旧式の車両がある。
赤色 - 損失を出しているライン。

路線の上にカーソルを置くと、さらに詳しい情報が表示されます。一覧の路線をクリックすると、その路線が表示され、その路線の停車所、収入、車両情報を確認したり、スケジュールを変更することができます。

路線編集には、次の2つのオプションがあります。

路線作成: 新しい路線ウィンドウを作成します。これには輸送タイプが選択されている必要があります。
路線削除: 現在の路線をゲームから削除します。その路線に属するすべての車両は、その路線と同様の個別のスケジュールで運転します。
Single GUI: 路線をクリックすると、一番上の路線ウィンドウが選択された路線で置き換えられます。

simutrans-124.5/simutrans/text/ja/list.txt000066400000000000000000000042301520675460500207020ustar00rootroot00000000000000一覧表

一覧表

一覧表には、ゲームの現在の状況に関する情報を得るためのコントロールを開くオプションがあります(また、さまざまな商品、乗客、郵便物の輸送から得られる相対的な収入を調整するコントロールもあります)。

プレイヤーは情報を一覧表で見ることができます。停留所車両都市部、さまざまな商品や乗客、郵便から得られる収入、産業、観光地などの情報が一覧表になっています。

ゲームビューの上部にある一覧表アイコンをクリックして、一覧表リストを開きます。
ツールバーを開くか、ツールバーをクリックした後、マウスカーソルを一覧表ツールのオプションに合わせると名前が表示されます。

ツールオプションをクリックすると、左から右の順に情報を表示するコントロールが開きます。

駅一覧: 、車両が荷物や乗客を乗せたり降ろしたりする場所(しかし、水上輸送の埠頭だけではありません)。

乗り物一覧: 道路・鉄道車両、航空機、船舶。

街一覧: 各都市 (村、町、市)。

貨物一覧: 異なる商品、乗客、郵便から受け取る収入(相対的な収入を調整するためのコントロールを含む)。

産業一覧: 産業 (商品の消費者と供給会社)

名所旧跡一覧: 観光名所。

simutrans-124.5/simutrans/text/ja/load.txt000066400000000000000000000007011520675460500206450ustar00rootroot00000000000000ロード

ロードダイアログ

 ロードダイアログでは以前保存したゲームをロードして続行することが出来ます。操作方法はセーブダイアログと同様です。

注意 ゲームを保存したSimutransのバージョンによってはファイルの互換性の問題により、ロードできない場合があります。

simutrans-124.5/simutrans/text/ja/load_relief.txt000066400000000000000000000037111520675460500221770ustar00rootroot00000000000000地形データ読み込み

地形データの読み込み

地形データを読み込む は新しいマップで始める際に自作の地形ファイルを使用するときや、これらの地形ファイルを削除する際に使います。

地形データを読み込む新しいマップの作成ダイアログから開くことができ、使用可能な地形データファイルの名称と保存日時が表示されます。

使用したい地形データファイルの名前をクリックするか、上段の入力スペースにファイル名を打ち込んで[Enter]キーか[Return]キーを押して下さい(もし存在しないファイル名を打ち込んだ場合は何も起こらないので、正しいファイル名を入力し直して下さい)。

なお、ファイル名の左にある×ボタンをクリックすると、その地形ファイルを削除することができます。警告:この際、削除の確認等は表示されず即座に削除されますので、誤って押したりしないように気をつけて下さい。

http://maps.simutrans.com に利用可能な地形データファイルがあります。
まt地形データファイルは.ppmか.bmp形式(RLE圧縮された256色のものが最良)でなければならず、simutrans/maps/と名前のついたパーソナルディレクトリ(またはフォルダ)に保存する必要があります。

地形データファイルの製作方法については、 http://wiki.simutrans.com や http://japanese.simutrans.com/index.php?FrontPage で見ることができます。

キャンセルをクリックするか、ダイアログ右上の×をクリックすれば地形データを読み込むを終了出来ます。

simutrans-124.5/simutrans/text/ja/mailbox.txt000066400000000000000000000047571520675460500214000ustar00rootroot00000000000000メッセージ表 メッセージ設定

メッセージ表 メッセージ設定

メッセージ表
ゲーム中のすべてのメッセージは、メッセージ表に表示されます。
左クリック: メッセージウィンドウを再表示します。
右クリック: メッセージの発生場所に移動します。
設定: メッセージ設定ウィンドウを開きます。

メッセージ設定
メッセージはメッセージ表のほか、メッセージウィンドウや画面最下部の
スクロールテキストにも表示できます。
メッセージ設定では、メッセージの種類ごとに、表示方法を設定できます。
この設定は、simutransの正常終了時に、settings.xmlに保存されます。

左端列のボタンをオフにすると、メッセージを完全に非表示にできます。
メッセージ表にも表示されません。

右側の3列は、左からスクロールテキスト、時限メッセージウィンドウ、
固定メッセージウィンドウの表示/非表示の設定です。

スクロールテキスト: 左クリックでメッセージの発生場所に移動 します。
時限メッセージウィンドウ: 一定時間後に消えます。
固定メッセージウィンドウ: クリックで閉じるまで消えません。

新年    新年が始まったことをお知らせします。
他社    他社が新しい路線を作ったことをお知らせします。
都市    都市に大きな建築物ができたことをお知らせします。
ルート不明 列車が次の駅に行くことができないことをお知らせします。
産業    新しい産業ができたことをお知らせします。
名所旧跡   新しい観光地ができたことをお知らせします。
車両登場  新しい車両が登場したことをお知らせします。
駅の混雑  駅がとても混雑していることをお知らせします。
問題    借金が大きいことをお知らせします。
警告    交通渋滞やプレイヤーの変更をお知らせします。

simutrans-124.5/simutrans/text/ja/map.txt000066400000000000000000000111451520675460500205070ustar00rootroot00000000000000マップ

マップ

マップ(地図)ウィンドウを開くには、ツールバーの左から2番目にあるマップのアイコンをクリックするか、
キーボードの[m]キーを押します。

基本マウス操作: マップ上では、以下の操作が使用できます。

左クリック: クリックした場所にゲーム画面を移動します。そのままドラッグ
すると、連続して移動します。
右ドラッグ: マップ画面をスクロールします。
右ダブルクリック: マップ画面をスクロール前の位置に戻ります。
マウスホイール: マップ画面を拡大/縮小します。

基本ボタン操作

拡大・縮小: マップ画面を拡大/縮小します。
回転して表示マップの表示を右に45度傾けます。ゲーム画面と上下左右が一致
します。
産業の需要崎/供給元
このボタンがオンの時は、産業施設にマウスポインタを合わせると、生産品の
需要先と白のラインで結ばれます。[Shift]キーを押しながら産業施設にマウス
ポインタを合わせると、原料の供給元と赤のラインで結ばれます。

路線経路の表示
このボタンがオンの時は、路線編集ウィンドウで路線が選択されている時や、
スケジュールウィンドウが開かれている時に、その経路の駅が直線で結ばれて
表示されます。
駅の上にマウスポインタを合わせると、駅名を表示します。

カラースケール

産業凡例

表示種別選択: マップの表示内容を切り替えます。
[Ctrl]キーが押されている間は、街の名前が一時的に表示されます。

都市: 都市の名前を表示します。デフォルトで選択されています。

旅客: 旅客取扱い駅の駅勢圏をプレイヤーカラーで表示します。

郵便: 郵便取扱い駅の駅勢圏をプレイヤーカラーで表示します。

貨物: 道路/線路の貨物量をカラースケールで表示します。

待合状態: 駅の混雑の状態を表示します。
駅一覧駅の情報で表示される色と同じです。

編成数: その駅に到着した乗り物の編成数の量をカラースケールで表示します。

交通量: 道路/線路の交通量をカラースケールで表示します

出発地: その駅を出発した旅客/貨物の量をカラースケールで表示します

目的地: その駅に到着した旅客/貨物の量をカラースケールで表示します

待機: 待機している旅客/貨物の量をカラースケールで表示します

鉄道線: 鉄道線を表示します。
 白: 非電化
 茶: 電化
 黄: 信号

制限速度: 道路/線路の制限速度をカラースケールで表示します

高圧線: 高圧線の電力供給率をカラースケールで表示します。
自動更新されません

名所: 名所旧跡を旅客度に基づいてカラースケールで表示します

産業: 産業施設を凡例の色で表示します

車庫: 自社の車庫・機関庫・造船所をハイライトします。
交通機関の種類ごとに、異なる色で表示されます。
 赤:  造船所
 黄:  自動車車庫
 橙:  機関庫
 臙脂: モノレール車庫
 桃:  航空機格納庫

: 樹木の多い場所を緑色の点で表示します。
市域: 都市の市域の境界線を、水色で表示します。
旅客/郵便目的地: 街の詳細ウィンドウが開かれている時、
その都市の旅客/郵便目的地をリアルタイムに表示します。
街の詳細ウィンドウの右上に表示されているものと同じです。
 オレンジ: 目的地へ出発できた
 黄色:   目的地へ出発できなかった

simutrans-124.5/simutrans/text/ja/monorailtools.txt000066400000000000000000000022741520675460500226360ustar00rootroot00000000000000モノレール/リニアツール

モノレール/リニアツール

このツールバーでは、モノレールやリニアの軌道や橋などの建設を行えます。Simutransを小さなウィンドウで実行している場合、ツールバーのアイコンが2行にわたって表示されることがあります。ツールを選択すると、マウスカーソルがそれぞれのアイコンに変わります。

モノレールは、通常の鉄道とは異なり、高架鉄道として機能するのが特徴です。1階建ての建物や道路の上を通り、都市の中心部まで直接軌道を敷設することが可能です。 高架の軌道と地上の軌道を接続するには、橋を建設してスロープを作ります。
注意: 高架軌道を建設・操作する際のマウスのポインタ位置は、その軌道が設置される場所の下のマス(地面)を基準にします。

その他の詳細な操作方法については、鉄道ツールのヘルプを参照してください。

simutrans-124.5/simutrans/text/ja/new_world.txt000066400000000000000000000062751520675460500217420ustar00rootroot00000000000000新しいマップの作成

新しいマップの作成

 新しいマップの作成ダイアログでは、新しいマップ(ゲーム)を作成するに
あたっての設定をすることが出来ます。マップのサイズや、産業の反映度など
が設定出来ます。

 以下は新しいマップに関する各オプションについての説明です。

マップ番号 - 地形や、都市の位置を決定します。番号を変更するたびにプ
レビューも更新されます(他のオプションを変更しても更新されます)。デフォ
ルトのマップ番号は33です。
マップサイズ - マップが何タイルで構成されるかを128×128から640×640
タイルの範囲で設定します。デフォルトは256です。
注意マップが大きければ大きいほど空きメモリが必要となります。プレイす
るにあたっては充分な空きメモリが確保されているか確認してください。

産業の反映度 - 油田や自動車工場など、どれくらいの産業を配置するか設
定します。この数値を低くすると、工場や生産地の間での輸送がしにくくなる
ので、ゲームが難しくなります。
街の数 - マップ上に街をいくつ配置するかを設定します。2から64の間で
設定が出来ます。デフォルトは16です。
交通量 - 自家用車の交通量を設定します。デフォルトは8です。

海面の水位 - 海面の高さを設定します。水位が低いほど陸地は広くなり、
高いほど狭くなります。デフォルトは4です。
起伏の高さ - 起伏の最高値を設定します。この数値が低いほど丘や谷の数
が少なくなります。0から160の範囲で設定できます。デフォルトは160です。
起伏の激しさ - 起伏の激しさを設定します。この数値が少ないほど自動車
や列車での輸送が楽になります。

ランダムマップ - マップ番号をランダムに選択します(他の設定には影響
しません)。

歩行者を表示する - 街中を通行している歩行者を表示するかどうかを設定
します。この設定はゲーム中でも表示オプション
ダイアログにて変更可能です。
夜は暗くする-1日の昼と夜のサイクルによって画面の明るさを変えます。こ
の設定はゲーム中でも表示オプションダイアログにて変更可能です。

ロード - 保存されているゲームをロードします(詳しくはロードダイアロ
グをご覧下さい)。
ゲームスタート - 各設定を用いてマップを作成します。大きいマップほど
時間が掛かります。
終了-直ちにSimutrans を終了します。

simutrans-124.5/simutrans/text/ja/options.txt000066400000000000000000000037271520675460500214340ustar00rootroot00000000000000ゲームオプション

ゲームオプション

ゲームオプションではSimutransのオプションの確認と変更ができます。ゲームの終了もここから行います。

ゲームオプションはツールバーの工具のアイコンから開くことができます。

ゲームオプションで設定できる項目:

言語: 使用する言語を設定します。

ライバル会社: ゲームに参加する(AIを含む)プレイヤーの設定がきます。公共事業もここに含まれます。

プレイヤーの色: プレイヤーの会社の色を設定します。

表示設定: 表示に関する設定を行います。

サウンド: 効果音とBGMの設定を行います。

新しいゲーム: 新規にゲームを始めるダイアログを表示します。

ゲームの再開: 保存(セーブ)したゲームデータを読み込みます。

ゲームの保存: 現在のゲームの状態を保存(セーブ)します。

シナリオを読み込む: ゲームシナリオを読み込むダイアログを開きます。シナリオは定められたゴール(条件)の達成を目指すプレーモードです。

シナリオ情報: 通常のプレーではこのボタンは無効になっています。シナリオプレー中のみこのボタンでシナリオの詳細を表示できます。

ゲーム終了: Simutransを終了します。現在のゲームは一時データとして保存されます。

simutrans-124.5/simutrans/text/ja/password.txt000066400000000000000000000031101520675460500215650ustar00rootroot00000000000000パスワード設定

パスワード設定ウィンドウ

 このダイアログでは、会社名の変更とパスワードの入力、変更、解除ができ
ます。入力内容は[Enter]キーで確定します。
パスワードは主に、オンラインゲームで使用されますが、プレイヤーの変更を
禁止したい場合にも、公共事業にパスワードを設定する必要があります。

このダイアログは、ライバル会社一覧ウィンドウから開くことができます。
また、ロックされているプレイヤーを操作しようとした時にも開かれます。

会社名の変更
プレイヤーがロックされていない場合、会社名を変更することができます。
この会社名はセーブゲームごとに保存され、後から変更することも可能です。

パスワードの入力
ロックされている場合
プレイヤーがロックされている場合、パスワードを入力してロックを解除し
ます。
ロックされていない場合
入力したパスワードが新規に設定され、またはパスワードが更新されます。
パスワードに何も指定せずに[Enter]キーを押すと、パスワード保護が解除されます。

注意:公共事業プレイヤーは、ロックされている他社のパスワードを強制的に変更
することができます。

simutrans-124.5/simutrans/text/ja/players.txt000066400000000000000000000107201520675460500214070ustar00rootroot00000000000000ライバル会社一覧

ライバル会社一覧

プレイヤー名
クリックすると、それぞれのプレイヤーの財務ウィンドウが開かれます。
プレイヤー名の右側には、各プレイヤーの現在の現金が示されています。

プレイヤーの色を変更したい場合は、右向き三角ボタンで変更したい
プレイヤーに切り替えてから、カラーダイアログで変更します。

プレイヤーの追加
■ボタンで、新しいプレイヤー会社を追加することができます。
一度追加したプレイヤーを、あとから取り除いたり、スロットを開放する
ことはできません。

プレイヤーの種類を、プレイヤー(人間)AI 貨物AI 旅客から選び、
左端の■ボタンをクリックして有効にします。

AIの有効/無効
AIプレイヤーが有効な場合、■ボタンでAIの有効/無効を切り替えること
ができます。
オフにすると、新規路線の開拓や資産の売却などの活動を停止します。
現在運行されているものは、そのまま運行を継続します。
経路が見つからない編成などの問題にも対処できなくなります。

プレイヤーの切り替え
右向き三角ボタンで操作できるプレイヤーを切り替えます。
プレイヤーの切り替えは、特殊建築物ツールの[プレイヤーの変更]で昇順
に切り替えることもできます。([Shift+P]
現在のプレイヤー名はステータスバーに表示されます。

ゲームのルールでプレイヤーの変更が禁止されている場合には、プレイヤー
を変更することは一切できません。
プレイヤーの変更の禁止は、新規マップ作成時に設定できるほか、
マップ編集ツールからもできます。
プレイヤーの変更の禁止を、後から元に戻すことはできません。

プレイヤーの種類
新しいマップの開始時には、プレイヤー会社公共事業のみが有効です。

プレイヤー会社: あなたの会社です

公共事業: 公共事業は、役所、産業施設、名所旧跡などを所有しています。
また、マップ編集ツールを使用して、それらを任意に建設することがで
きます。
公共事業は、すべてのプレイヤーの所有物を取り除くことができます。
公共事業が所有する道路/線路や駅などは、すべてのプレイヤーが利用
することができます。公共駅でのみ会社間での乗換ができます。

公共事業は車庫を建設することができませんので、運送によって直接収入を
得ることはできません。赤字でも問題ありませんので気にしないでください。

プレイヤー(人間): プレイヤーが操作します。破産しても勝手に清算されたり
はしません。

AI 貨物: 鉄道/道路/船舶を使用して、貨物輸送を行います。

AI 旅客: バス/船舶を使用して、旅客輸送を行います。資金が十分あるとき
には旅客機を使用することもあります。

フリープレイモード
通常、総資産がマイナスになった会社は破産となります。
あなたが破産した場合、ゲームは終了し、新しいマップの作成ウィンドウが
開かれます。
AIプレイヤーの場合は、すべての資産が売却され、活動を停止します。
破産した会社のスロットは使用できなくなります。

フリープレイモードをオンにすると、破産することなく、ゲームを続けること
ができます。この設定はセーブゲームに保存され、いつでも変更することが
できます。

simutrans-124.5/simutrans/text/ja/privatesign_info.txt000066400000000000000000000021471520675460500233020ustar00rootroot00000000000000ゲート

ゲート

ゲート(進入制限標識、私有道ゲート)は、設置した軌道や道路への進入をプレイヤーごとに制限するための標識です。この標識の設定ダイアログを使用することで、チェックを外したプレイヤーの車両がその先に進入することを禁止できます。

設定内容と挙動
- プレイヤーごとの許可: チェックが入っているプレイヤーの車両のみが、この標識を通過できます。
 · 公共事業の場合: 都市内を走る自家用車(city cars)の進入可否に影響します。

通行が許可されているプレイヤーにはゲートまたはバリアが開いた状態に、通行が禁止されているプレイヤーにはゲートまたはバリアが閉じた状態になります。

この交通標識は、各交通機関のツールバー(鉄道ツール道路ツールなど)を使用して設置します。

simutrans-124.5/simutrans/text/ja/railtools.txt000066400000000000000000000111411520675460500217360ustar00rootroot00000000000000鉄道ツール

鉄道ツール

 鉄道ツールからは、線路、信号、トンネル、駅、操車場、踏切を作ることが
出来ます。

 上段の一番左は、線路ツールです。マップの二点間に線路を敷くことが出来
ます。アイコンをクリックすると、マウスカーソルが線路の交差したような形
状に変化します。まず線路の起点でクリックし(その場所にブルドーザーのマー
クが表示されます)、次に線路の終点でクリックすると、選択した二点を結ぶ
ように線路が敷かれます。このとき、二点間は普通の線路だけで結ばれます。
踏み切りや橋、トンネルを自動的に作ることはありません。選択した二点を結
ぶ経路が見つからなかった場合は、何も起こりません。

 上段の左から2番目は、信号ツールです。信号を設置して閉塞区間を作るこ
とで、一本の線路に複数の列車を走らせることが出来ます。アイコンをクリッ
クすると、マウスカーソルが信号機に変化します。信号を設置したい場所でク
リックしてください。信号には、両方向信号と一方向信号があります。一方向
信号は、閉塞区間を作る効果に加えて、反対側からの列車の進入を禁止する効
果があります。一方向信号を設置するには、まず両方向信号を設置してから、
そこでもう一度クリックしてください。クリックするたびに、両方向信号 -
一方向信号 - 反対方向の一方向信号 - 両方向信号と、循環的に切り替わり
ます。信号を撤去する時は、撤去ツールを選択して信号のある場所をクリック
してください。このとき、信号機だけが撤去され、線路は残ります。

 上段の左から3番目は、トンネルツールです。トンネルは、同じ高さの直線
の線路を結びます。トンネルを設置する前に、トンネルで結ぶ線路の両端が、
それぞれ山の斜面に掛かっているようにしてください。アイコンをクリックす
ると、マウスカーソルがトンネルに変化します。トンネルの入り口でクリック
すると、向かい合った線路がトンネルで結ばれます。

 上段の一番右は、架線ツールです。電車を走らせる為には、線路を敷設した
あとに架線を張らなければいけません。アイコンをクリックすると、マウスカー
ソルが架線のグラフィックに変化します。架線を張りたい線路の上でクリック
すると、その場所を含む閉塞区間全体に架線が張られます。

 下段の一番左は、駅建設ツールです。列車は、駅で物資や旅客の積み下ろし
を行い、収益を得ます。アイコンをクリックすると、マウスカーソルが駅に変
化します。建設したい線路上でクリックすると、そこに駅が建設されます。駅
は、平坦な直線線路の上にしか作れません。また、1マスの駅には2両までの
列車しか入りません。駅に入らない部分の車両は、荷物の積み下ろしを行えま
せん。より長い列車を停車させる為には、大きな駅を作る必要があります。す
でに存在する駅に隣接するマスに駅を建設すると、それは同一の駅とみなされ
ます。駅の大きさや形状に制限はなく、繋がっている限りは全て一つの駅にな
ります。Simutrans の現在のバージョンでは、駅にはいくらでも物資を貯める
ことが出来ます(産業施設には1000tまでしか貯めることが出来ません)。

 下段の左から2番目は、操車場建設ツールです。列車の購入やスケジュール
設定を行うためには、操車場が必要です。アイコンをクリックすると、マウス
カーソルが操車場に変化します。操車場は線路の終端にしか建設できませんの
で、そのような場所を選んでクリックしてください。

 下段の左から3番目は、踏み切りツールです。アイコンをクリックすると、
マウスカーソルが踏み切りの形状に変化します。踏切を設置したい道路上でク
リックしてください。

simutrans-124.5/simutrans/text/ja/removal_tool.txt000066400000000000000000000052331520675460500224350ustar00rootroot00000000000000撤去ツール

撤去ツール

撤去ツール は、不要な建築物やアイテムを、取り壊して撤去するときに使います。

使い方: ゲーム画面の上にあるツールバーの赤い×印のアイコン(pak128ではブルドーザーのアイコン)をクリックするか、[r]のキーを押すと撤去ツールが起動し、カーソルが赤い×印に変わります。
ゲーム画面に表示される黄色の枠が、撤去ツールの作用する範囲を示します(座標は画面下のステータスバーの右方に表示されます)。カーソルの位置を目的の場所に合わせて左クリックすれば、撤去できます。

撤去費用は、撤去後にゲーム画面に表示されるとともに、建設費に加算されます。なお撤去費用は、撤去したアイテムをプレイヤーが設置する際にかかる費用と同じです。
また市内建築物の撤去にかかる費用は、調査ツール使用時にその撤去したい市内建築物をクリックして表示されるダイアログ中の価格と同じです。

なお、撤去ツールは歩行者や自家用車を取り除きたいときにも同じように使用出来ます。

同一のマスに複数のアイテムが存在する場合、1,歩行者または自家用車、2,標識または信号、3,停留所または駅のホーム、4,架線、5,橋またはトンネル、6,道路または線路(踏切があれば線路が先)の順に優先して撤去されます。

{Tips: プレイヤー会社の乗り物を撤去したいときは、乗り物詳細から乗り物を即座に売却することができます。
産業施設や名所旧跡、町役場を撤去したいときは、まずは操作する会社を公共事業に切り替えて下さい。要注意:町役場を撤去すると、その市域の全ての市内建築物が撤去されてしまいます。
ライバル会社のアイテムを撤去したいときは、まず操作する会社をそのライバル会社に切り替えてください。
線路や道路を標識や信号とまとめて撤去したいときは、鉄道の撤去(同様のツールは市電/軽便鉄道ツール内にもあります)や道路の撤去を使用して下さい。}

simutrans-124.5/simutrans/text/ja/roadtools.txt000066400000000000000000000061401520675460500217370ustar00rootroot00000000000000道路ツールダイアログ

道路ツールダイアログ

 道路ツールダイアログからは、道路、バス停、トンネル、トラック停車場、車庫、踏切を作ることが出来ます。

 上段の一番左は、道路敷設ツールです。マップの二点間に道路を敷くことが出来ます。アイコンをクリックすると、マウスカーソルが交差点のような形状に変化します。まず道路の起点でクリックし(その場所にブルドーザーのマークが表示されます)、次に道路の終点でクリックすると、選択した二点を結ぶように道路が敷かれます。このとき、二点間は普通の道路だけで結ばれます。踏切や橋、トンネルを自動的に作ることはありません。選択した二点を結ぶ経路が見つからなかった場合は、何も起こりません。

 上段の左から2番目は、バス停建設ツールです。バス停では、その付近の建物を利用する人が乗り降りします。アイコンをクリックすると、マウスカーソルがバス停に変化します。バス停を建設したい道路上でクリックしてください。バス停は、平坦な直線道路の上にしか作れません。1マスのバス停には、道路のそれぞれの車線に1台ずつのバスが停車できます。

 上段の左から3番目は、トンネルツールです。トンネルは、同じ高さの直線の道路を結びます。トンネルを設置する前に、トンネルで結ぶ道路の両端が、それぞれ山の斜面に掛かっているようにしてください。アイコンをクリックす
ると、マウスカーソルがトンネルに変化します。トンネルの入り口にしたいところでクリックすると、向かい合った道路がトンネルで結ばれます。

 下段の一番左は、トラック停車場建設ツールです。トラック停車場では、トラックが物資の積み下ろしを行います。アイコンをクリックすると、マウスカーソルがトラック停車場に変化します。トラック停車場は道路の終端にしか建設できませんので、そのような場所を選んでクリックして下さい。1マスのトラック停車場には、2台までのトラックが停車できます。

 下段の左から2番目は、車庫建設ツールです。バスやトラック、郵便配達車など、車両の購入やスケジュール設定を行うためには、車庫が必要です。アイコンをクリックすると、マウスカーソルが車庫に変化します。車庫は道路の終端にしか建設できませんので、そのような場所を選んでクリックしてください。

 下段の左から3番目は、踏切ツールです。アイコンをクリックすると、マウスカーソルが踏切の形状に変化します。踏切を設置したい線路上でクリックして下さい。

simutrans-124.5/simutrans/text/ja/save.txt000066400000000000000000000013751520675460500206740ustar00rootroot00000000000000セーブ

セーブ

 現在の状態を保存できます。

 新しいファイルに保存する場合は、テキストフィールドにファイル名を入力
してOKボタンを押してください。ファイル名には拡張子を含める必要はありません。

 既存のファイルに上書き保存する場合は、表示されているファイル名をクリッ
クしてください。上書きの確認はありませんので、注意してください。

 ファイルを削除したい場合は、そのファイル名の左側にある×ボタンをクリッ
クしてください。削除の確認はありませんので、注意してください。

simutrans-124.5/simutrans/text/ja/scenario.txt000066400000000000000000000004611520675460500215340ustar00rootroot00000000000000シナリオ

シナリオ

Simutransではシナリオを使って遊ぶことができます。工場をつなぐ、できるだけ早く収入を得る、本社を立てるといった目標を達成することでシナリオを完結させることができます。

simutrans-124.5/simutrans/text/ja/schedule.txt000066400000000000000000000033601520675460500215260ustar00rootroot00000000000000時刻表

時刻表ダイアログ

 時刻表ダイアログでは、車両・船舶の運行経路を設定することが出来ます。ちなみに時刻を設定しての運行は出来ません。

 運行経路を設定するには、まず追加ボタンをクリックします。次に、停車する駅などの上をクリックしてください。港を指定する場合は、港の周辺のマスをクリックしてください。リストに駅名・港名が追加されます。なお、線路上・道路上・海上で駅など出ない場所も中継点として設定できます。このようにして、運行経路の駅・港などを次々とクリックしていって下さい。最大16ヵ所まで指定できます。

 車両・船舶は、リストの上から順番に設定された箇所を回ります。リストの一番下まで来たら、また上まで戻って運行を続けます。番号の左のマークは現在向かっている箇所を示します。

 追加ボタンでは、新たに箇所をリストの一番下に追加します。挿入ボタンでは、リスト上の現在マークがある項目に、新たな箇所が挿入されます。リストの項目を削除するには、削除ボタンをクリックして、削除したい項目をクリックしてください。全ての設定が終わったら終了ボタンをクリックしてください。

 「積むまで待機」の数字は、その駅・港で、車両・船舶に貨物・旅客をこの割合積んだ状態になるまで、出発を待つと言う意味です。この数字を大きくすることで、無駄のない運行が出来ます。

simutrans-124.5/simutrans/text/ja/script_generator.txt000066400000000000000000000032231520675460500233020ustar00rootroot00000000000000スクリプトツールとして保存

スクリプトツールとして保存

この機能では、事前にドラッグして選択した範囲内にあるすべての建築物を、スクリプトとして保存します。 保存したスクリプトを後で実行すると、これらの建物やオブジェクトの建設が再現されます。

スクリプトを保存するには、まず名前を入力する必要があります。 または、下に表示されている既存のスクリプト一覧から一つを選択して、上書き保存することも可能です。
リスト内の名前の先頭にある X をクリックすると、不要になったスクリプトを削除できます。

OK を押すと、新しいスクリプトツールが保存されます。

保存されたツールは、セーブデータ(saveフォルダ)などがある個人用ディレクトリ simutrans 内に保存されます。

- Windowsの場合: 「ドキュメント」フォルダ内の Simutrans フォルダにあります。
- Linuxの場合: ホームディレクトリ内の /home/[ユーザー名]/.simutrans(隠しフォルダ)にあります。

詳細な保存先パスは以下の通りです:
simutrans/addons/[使用中のパックセット名]/tool/

保存されたスクリプトは、一般的なテキストエディタで閲覧・編集が可能です。スクリプトの構造は非常に単純で、容易に理解できるようになっています。

simutrans-124.5/simutrans/text/ja/script_tool.txt000066400000000000000000000006041520675460500222710ustar00rootroot00000000000000スクリプトツールのロード

このウィンドウには、Simutransの機能を拡張するSquirrelで作成されたツールが一覧表示されます。
ツールは、simutrans/[Graphics Set]/tool/またはsimutrans/addons/[Graphics Set]/tool/ディレクトリのサブディレクトリにあります。

simutrans-124.5/simutrans/text/ja/server.txt000066400000000000000000000034461520675460500212450ustar00rootroot00000000000000ゲーム情報 / サーバー

ゲーム情報 / サーバー

Game information / Server will show you the current state of the game running, including the date, map size, number of companies, towns, citizens, factories, vehicles running, and so on. ゲーム情報 / サーバーでは、日付、マップサイズ、会社数、街の数、人口、工場数、稼働中の車両数など、現在稼働中のゲームの状態が表示されます。

現在のゲームがまだオンラインゲームでない場合は、接続可能な他のサーバーがドロップダウンリストに表示されます。このリストが空の場合、お使いのパックバージョンやプログラムバージョンのサーバーがありません。すべて表示ボタンをクリックすると、既存のすべてのサーバーが表示されます。

サーバーを選択すると、そのサーバーの状態を示すゲーム情報が表示されます。(サーバーがダウンしている場合、確認に時間がかかることがあります。) また、サーバーのパックセットとプログラムバージョンが表示されます。両方とも黒であれば、あなたのパックセットとプログラムのバージョンがサーバーと一致しているので、Play online を押して参加することができます。

pakセットの不一致がある場合は、エントリーが赤になります。そのときは、パックを比較するボタンを使って、足りないパック、違うパック、余分なパックのレポートを得ることができます。(これには時間がかかる場合があります。)

simutrans-124.5/simutrans/text/ja/settings.txt000066400000000000000000000025221520675460500215710ustar00rootroot00000000000000高度な設定

高度な設定

高度な設定ウィンドウは新しいマップの作成ウィンドウから開きます。
ここでは、主に作成する新しいマップのルールを設定します。

 最上部のボタンはリセットボタンです。設定をリセットします。注意してく
ださい。(一部リセットされない項目もあります)
Simuconf.tab simuconf.tabの設定にリセットします。
Default.sve 最後に新規マップを作成した時の設定にリセットします。

設定項目には、セーブゲームに保存されるものと、保存されないものとがあり
ます。
セーブゲームに保存されるものは、新規に作成されるゲームにのみ反映されます。
ゲーム開始後に変更することはできません。

保存されない設定項目は、変更が即時に反映されます。ただし再起動後は元の
設定に戻ってしまいますので、変更を固定したい場合は、simuconf.tabや
cityrules.tabをテキストエディタで直接変更してください。

各設定項目の詳細は「日本語化wiki」を参照してください。

simutrans-124.5/simutrans/text/ja/shiptools.txt000066400000000000000000000026711520675460500217620ustar00rootroot00000000000000船舶ツール

船舶ツール

 船舶ツールでは、港とドックの建造が行えます。

 上段の一番左のボタンは、港建造ツールです。港では、船舶が貨物や旅客の
積み下ろしを行います。アイコンをクリックすると、マウスカーソルが港の形
状に変化します。海に
接した斜面でクリックすると、その斜面と隣接する
海との2マスを利用して、港が建造さ
れます。港を建造する斜面は、真っ
直ぐな斜面(線路や道路を敷設できるような斜面)出なければいけません。船
舶を停泊させるようにスケジュールを組むには、港のあるマスではなく、港に
隣接するマスに停泊するように指定して下さい。Simutrans の現在のバージョ
ンでは、港の撤去は正しく動作しません。

 下段の左の2つのボタンは、ドック建造ツールです。これら2つは画面上で
の向きが違うだけで、実質的には違いはありません。ドックは、船舶の購入や
スケジュール設定を行うために必要です。アイコンをクリックすると、マウス
カーソルがドックに変化します。ドックは海上に建造します。ドックを建造し
たい場所でクリックして下さい。

simutrans-124.5/simutrans/text/ja/signal_info.txt000066400000000000000000000021271520675460500222220ustar00rootroot00000000000000信号情報ウィンドウ

信号情報ウィンドウ

設置されている信号の詳細情報を表示します。
· 信号の名前
· 説明: 信号の解説(アドオン等で設定されている場合のみ表示されます)。
· 所有者: その信号を所有しているプレイヤー名
· 接続方向: 現在通行可能な方向です。これ以外の方向からの進入は禁止されます。
- 1 = 南(南西への分岐を含む)
- 2 = 西(北西への分岐を含む)
- 3 = 南北方向(斜め)
- 4 = 北(北東への分岐を含む)
- 5 = 南北方向
- 8 = 東(南東への分岐を含む)
- 9 = 東西方向(斜め)
- 10 = 東西方向
- 12 = 南北方向(斜め ※3の別パターン)
· 信号を撤去: この信号を撤去します。
· 作者: この信号の画像を作成した作者名

simutrans-124.5/simutrans/text/ja/signal_spacing.txt000066400000000000000000000016461520675460500227200ustar00rootroot00000000000000信号/標識の間隔設定

信号/標識の間隔設定

 信号、標識アイコンを[Ctrl]キーを押しながらクリックします。
 オプションは次の通りです。

信号/標識の間隔
設置間隔。間隔はツールアイコンにも表示されます。

中間の信号/標識を撤去する
 オン...設置間隔の中間にある信号・標識は撤去します。
 オフ...設置間隔の中間にある信号・標識は残します。

他の信号/標識を置き換える
 オン...設置予定場所に別の信号・標識があった場合、撤去し新しく設置し
ます。
 オフ...設置予定場所に別の信号・標識があった場合そのまま残し、それよ
り先に設置可能な場所を探し、そこに設置します。

simutrans-124.5/simutrans/text/ja/simutrans.txt000066400000000000000000000024041520675460500217550ustar00rootroot00000000000000Simutrans

Simutrans

Simutransは、鉄道・自動車・船舶・飛行機等で旅客の輸送ルートや産業物資を開拓し利益を上げる、フリーの経営シミュレーションゲームです。 初心者でもとっつきやすく、効率のよい交通システムの構築、都市の発展、混雑の解消、流通ルート開拓による産業の開発など、醍醐味は尽きません。 海外で開発が続けられているフリーウェア(無料)ですが、製品としても十分通用するほどの完成度の高さは、着実に国内のプレイヤーを増やしています。 大変楽しめる作品ですので、ぜひ一度プレイされることをお勧めします。Windows・MacOS X・Linux・FreeBSD・BeOS(Haiku OS)・AmigaOS・iPhone(iPod touch)に対応しています。  ※iPhone(iPod touch)対応は公式ビルドではありません。

Simutransについてよりインターネットで見つけることができます:
http://japanese.simutrans.com/
http://forum.japanese.simutrans.com/
http://forum.simutrans.com.

 日本語訳「フリーの経営シム"Simutrans"を語るスレ」の313さん+囲碁25級

simutrans-124.5/simutrans/text/ja/slopetools.txt000066400000000000000000000044731520675460500221430ustar00rootroot00000000000000スロープツール

スロープツール

スロープツールを使って、陸地を造成することができます。造成できる場所は何もない空地や海上に限られます。
[#]キー([shift+3])で、グリッドを表示させておくと便利です。

陸地を高くする
 陸地を現在より1レベル上の高さに盛り上げます。
陸地を低くする
 陸地を現在より1レベル下の高さに窪ませます。

以上の2つのツールは、左クリックまたは左ボタンを押したままドラッグして使用します。ドラッグの場合は、すべて同じ高さになります。
造成する体積に比例して、費用が高くなります。
樹木などの自然物は取り除かれてしまいます。

スロープの建設: 人工的な造成をします。

左の4つは傾斜地を、右の2つは垂直に陸地を盛り上げ/切り下げします。
人工的な造成では、周囲との段差を2段以上にすることはできません。
後者のツールは空地や海上以外に、線路/道路/運河(高架含む)や送電線の終端上でも使用できます。
また、飛行機の誘導路や滑走路に適用することはできません。

線路/道路の終端上での使用
この場合、主に右の2つのツールを使って、線路/道路/運河ごと傾斜させます。
向きが合えば左の4つのスロープも使えますが、あまり便利ではありません。
傾斜を元に戻す場合にも、これら2つのツールを使用します。
下りの傾斜を元に戻すには、盛り上げツールが、上りの傾斜を元に戻すには、切り下げツールが使用できます。
地下で線路/道路を傾斜させたい場合には、必要です。
地下で切り上げを行った後、線路/道路を延伸するには、表示設定より地下路線建設モードを使用してください。

スロープを戻す
人工的な造成地やその周囲を、できる限り自然な状態に戻します。
線路/道路の終端上では使用できません。

simutrans-124.5/simutrans/text/ja/sound.txt000066400000000000000000000043311520675460500210610ustar00rootroot00000000000000サウンド設定

サウンド設定

サウンド設定 ダイアログでは効果音とBGMの設定ができます。サウンド設定ゲームオプションメニューから開きます.
メモ: WindowsではSDLバージョンが音質に優れる

矢印ボタンかスライダーを使用して音量の調整ができます。

効果音の設定(上段)

ミュート: 全ての効果音を止めます。

効果音の音量: 効果音全体の音量を設定します。

効果音の再生範囲: 効果音が再生される範囲を設定します。設定値を大きくするほどマウスのカーソルから離れた所の音が聞こえるようになります。

ツールの音量: ツールを使って操作した時の音量(調査、建設、撤去等)を設定します。

乗り物の音量: 車、列車、船舶、航空機の効果音の音量を設定します。

環境音の音量: 環境音(鳥の鳴き声等)の音量を設定します。

産業施設の音量: 産業施設の効果音の音量を設定します。

踏切の音量: 踏切を列車が通過する時の効果音の音量を設定します。

売買の音量: 取引の際に発生する効果音の音量を設定します。

BGMの設定(下段)

ミュート: 全てのBGMを止めます。

BGMの音量: BGMの音量を設定します。(Windowsではボリュームコントロールのミキサーと連動しています)

演奏中の曲: 現在再生されているBGMが表示されます。矢印ボタンで変更できます。

シャッフル再生: ONにするとBGMをランダムな順番で再生します。OFFにすると順番に再生されます。

メモ: Simutransのコマンドライン起動オプションに「-nomidi」「-nosound」を指定して起動するとBGMと効果音を停止して起動できます。

simutrans-124.5/simutrans/text/ja/special.txt000066400000000000000000000211121520675460500213450ustar00rootroot00000000000000特別建築物ツール

特別建築物ツール

特別建築物ツール 特別建築物ツールは、交通に関係のないアイテムを建設するほか、ゲーム内であなたが別のプレイヤーとして操作することもできます。

ゲームビューの上部にある、黄色い丸の中に赤い点のアイコン(pak128ではクレーンのアイコン)をクリックすると、ツールバーが開きます。
ツールバーを開くかクリックした後、マウスカーソルをツールオプションに合わせると、名前と必要に応じて建設が表示されます。

特別建築物ツールには、左から右の順に以下のアイテムが含まれています。

郵便局: ストップ(駅や停留所など)が郵便を扱えるようにするために、郵便局を建設するツールです。郵便局は、ストップの収容量と集客エリアを増やしますが、維持費がかかります。
郵便局を作るには、ツールをクリックして選択し、ゲームビューで既存のストップのそばをクリックします。新しい郵便局はストップの一部とみなされます。
{ヒント 郵便局や一部の障害物は「撤去/取り壊し」で取り除きます。}

駅の拡張: ストップの拡張や増築を行うためのツールです。増築することで、貨物や乗客の収容力や集客エリアが増加し、維持費も増加します。いくつかのツールオプションの隅には、アイコン(これは駅一覧とストップ情報で使用される)が表示され、その拡張機能でストップが扱えるアイテムを示しています。
拡張部分を作るには、ツールをクリックして選択し(カーソルが拡張部分に変わる)、ゲームビューで既存のストップの横の必要な位置をクリックします。新しい拡張部分は、ストップの一部とみなされます。
{ヒント: 拡張部分の削除には、「撤去/取り壊し」を使用します。}

P+ プレイヤー変更: P+ プレイヤー変更:ゲーム開始時にプレイヤー変更を許可が選択されていた場合、別のプレイヤーとしてゲームをプレイすることができます。公共事業として追加のオプションが利用できます。
ツールをクリックして(または [P] を押して)プレーヤーを切り替えてください。プレイヤーが変更されたことを確認するメッセージが表示されます。メッセージの左上にある x をクリックするか、キーボードで閉じてください。

新しい街を作る: 新しい都市を作る:ツールは新しいを作ります。
新しい都市を建設するには、ツールをクリックするか、 [C] キーを押して選択し(カーソルが市町村役場に変わります)、ゲームビュー内の必要な位置をクリックします。新しい街の建設には500万クレジットが必要で、追加の確認なしに建設されます。
{ヒント: ゲームビュー上の役場を「撤去/取り壊し」でクリックすると、新しい街のすべての建物が削除されます。}

木を植える: ツールはゲームビューに木を配置します。
木を植えるには、ツールをクリックして選択し(カーソルが木に変わります)、次に必要な位置をクリックします。
{ヒント: 撤去/取り除く」で木々を取り除きます。}

送電線の建設: 送電線の建設:ツールは発電所の変電所と産業との間に電力を供給する送電線を建設します(生産性向上のため)。電力を供給することで収入が得られます。この送電線は、斜めの線路や道路を越えることができません。
高圧線を作るには、ツールをクリックするか、[l] キーを押して選択し(カーソルが送電線に変わります)、次に発電所の隣に置かれた変電所をクリックし、最後に産業の変電所をクリックして、電力を供給して生産量を増やします。

変電所 変電所ツールは、発電所や産業に変電所を建設し、高出力の送電線に接続するためのツールです。
変電所を作るには、ツールをクリックするか、 [g] を押して選択し(カーソルが黄色の中に赤色の点に変わります)、ゲームビューで産業や発電所の横にある必要な位置をクリックします。
発電所に作られた変電所は、産業に作られた変電所と送電線で結ばれていないと電気が供給されません。
電気が供給されていない場合、ゲームビュー上の変電所には黄色の稲妻マークが表示され、電気が供給されている場合は赤色の稲妻マークが表示されます。
{ヒント: 産業や発電所は、電気の供給量を増やすために複数の変電所を所有することができます。変電所は撤去/取り壊しで削除してください。}

マーカー(サインを作る): ツールを起動するとコントロールが開き、ゲームビューにプレイヤーが自分で作った文字(テキスト)を含むサイン(看板)を配置することができ、ゲームビューを別の地点に移動することもできます。
ツールをクリックして選択するか、[M] を押すとカーソルがテキストマーカーに変わるので、ゲームビュー内の必要な位置をクリックして「マーカー」コントロールを開きます。マーカーコントロールの上部には、選択した位置の地図上の座標と空のボックスが表示されるので、ボックスに必要なテキストを入力して(ボックスをクリックしてテキストを入力し、OK をクリックするか[Enter]あるいは[Return]を押して)サインを設置します。新しいサインはゲームビューに表示され、再度開くとマーカーコントロールにリストアップされます(地図上の座標も表示)。
ゲームビューをサインのある位置へ移動するには、マーカーコントロールを開き、一覧の中から必要な位置をクリックします。
マーカーを削除するにはマーカーツールをクリックするか、[M] を押して選択すると、カーソルがテキストマーカーに変わるので、ゲームビューでサインの座標をクリックし、マーカーコントロールで削除をクリックします。
マーカーコントロールを閉じるには、コントロールの左上にある x をクリックするか、キャンセルをクリックするか、またはキーボードを使用します。
{ヒント: ゲームビュー下部のバーの右側には、カーソルの地図座標が表示されます。ゲームビューでサインのテキストを切り替えるには [!] を使います。}

スケジュール移動ツール: このツールを使うと、そのストップ(駅や停留所など)を使用しているすべての車両と路線のスケジュールで、ストップを置き換えることができます。また、ストップ内のスケジュールタイルを変更することもできます(例えば、すべての路線を鉄道駅の別のプラットフォームに移動させる場合など)。ツールを選択した後、スケジュールから削除したいストップ(またはストップの一部)をクリックし、次に今後使用したいストップ(またはストップの一部)をクリックします。最初のストップを使用していた以前の全車両は、2番目のストップを使用するようになります。スケジュールの設定によっては、変更時に1つ目のストップに向かう車両に混乱が生じる可能性があります。

simutrans-124.5/simutrans/text/ja/station.txt000066400000000000000000000153331520675460500214160ustar00rootroot00000000000000駅情報

駅情報

駅情報ウィンドウでは、車両が貨物や旅客の積み降ろしを行う「駅」に関する詳細な情報を確認できます。このウィンドウから、名称の変更や詳細情報の確認を行うことも可能です。なお、ここでいう「駅」は、鉄道駅だけでなくバス停や港、空港なども含みます。


駅の構成と建設

駅は、運行便が積み降ろしを行う場所と、それに付随する拡張施設で構成されます 。 建設ツールは各輸送手段のツールバー(鉄道駅モノレール/磁気浮上式鉄道駅電停停車場空港など)に用意されています。

- 拡張: 既存の駅に隣接するマス(斜めを含む)に新しい駅要素や拡張施設を建設すると、それらは自動的に一つの駅として結合されます 。これにより、地下鉄、地上駅、高架線、港などを一か所に集約し、乗り換えを可能にできます。
- 建設条件: 駅を建設する経路(線路や道路)は、平坦で交差点がない場所である必要がありますが、橋やトンネル内にも建設可能です。ただし、一部の駅舎や拡張施設には、線路の端(終端)にしか建設できない、あるいはトンネル内には建設できないといった追加の制約がある場合があります。
- 所有権: 通常、駅はその所有者の運行便のみが利用できますが、公共事業が所有する駅は全プレイヤーが利用でき、相互に旅客・貨物をやり取りできます。


ウィンドウの見かたと機能

ゲーム画面上の駅を調査ツール(虫眼鏡)でクリックするか、駅リストから選択するとウィンドウが開きます。

名称と基本情報
- 名称: 上部の名前欄をクリックして変更できます。デフォルトでは近隣の都市産業施設の名前に基づいて自動付与されます。
- 駅の画像: ウィンドウ右側の画像をクリックすると、ゲーム画面の視点がその駅へ移動します。
- ステータスバー: 混雑状況を色で示します。
· : 稼働しておらず、待機中の荷物もありません。
· : 正常
· オレンジ: 混雑
· : 深刻な過密状態です。容量の150%を超過しているか、200名以上の不満な旅客がいる、あるいは過密により工場の生産が停止しています。
- アイコン: その駅が扱うカテゴリー(旅客・郵便・貨物)や、利用可能な輸送種別(バス、トラック、鉄道、船、飛行機など)を表示します。
- 収容量: 旅客や貨物をどれだけ収容できるかを示します。拡張施設を建てることで容量を増やせます。

旅客の満足度
その駅から出発しようとする旅客や郵便の状況が表示されます。
- 満足: 経路を見つけ、輸送を待っている旅客数
- 不満: 駅が過密だったために旅行を諦めた旅客数
- 経路なし: 目的地への経路が見つからなかったために立ち去った旅客数。乗り継ぎ回数が制限(simuconf.tabの max_transfers)を超えている場合もここに含まれます。

待機中の旅客/貨物
駅で輸送を待っている全アイテムがリストアップされます。
リスト上部のボタンで以下の順に並び替えが可能です:
- 目的地: 最終目的地の停留所名順(英数字順、大文字優先)
- 経由地: 次の乗り換え地点の名称順
- 経由地 (量): 次の乗り換え地点ごとの合計量順
- 経由地(所有者): 次の接続を所有するプレイヤー順
- 路線: 次の路線または運行便順

発着時刻

接続産業・路線
駅に関する詳細な情報を確認できます。
表示される情報は以下の通りです:
- 接続している産業: 駅の集荷範囲内にある産業施設の名称とマップ座標(カッコ内)が表示されます。
{ヒント: キーボードの [v] キーを押すと、ゲーム画面上で駅の集荷範囲の表示/非表示を切り替えることができます。}

- 付近の産業で需要のある物資: 集荷範囲内の産業施設が必要としている貨物の種類が表示されます。

- 乗り入れている路線: この駅を利用している路線の一覧です。
リストはASCIIコード順(英数字順。大文字が先、小文字が後)で並べられます。

- 路線に所属していない編成: 特定の路線に割り当てられていない、ここに停車する運行便が表示されます。

- 直通駅: 乗り換えなしで到達できる駅の名前と、配送可能な品目(旅客・郵便・貨物)の一覧です。
このリストは、配送可能な品目ごとに、ASCIIコード順(英数字順)で並べられます。

グラフ
過去12ヶ月の推移をグラフで表示します。旅客の満足度、到着・出発量、待機量、運行編成数などを確認できます。

マップ上での表示
表示設定やショートカットキーで、マップ上の表示を切り替えられます。
- [!] キー: 駅名、ステータスバー、待機量を示すカラーバーの表示を切り替えます。
- [v] キー: 駅の集荷範囲を表示します。旅客や貨物は、この範囲内にある建物や産業からのみ集荷されます。

simutrans-124.5/simutrans/text/ja/station_details.txt000066400000000000000000000036161520675460500231240ustar00rootroot00000000000000駅詳細

駅詳細

駅詳細では、貨物や旅客の積み降ろしを行う「駅」に関する詳細な情報を確認できます。

駅詳細ウィンドウは、駅情報ウィンドウにある詳細ボタンから開くことができます。
すべての項目が表示されない場合は、ウィンドウをリサイズするか、スクロールバーを使用して表示範囲を移動させてください。

表示される情報は以下の通りです:
接続されている産業: 駅の集荷範囲内にある産業施設の名称とマップ座標(カッコ内)が表示されます。
【ヒント】: キーボードの [v] キーを押すと、ゲーム画面上で駅の集荷範囲の表示/非表示を切り替えることができます。

付近の産業で需要のある物資: 集荷範囲内の産業施設が必要としている貨物の種類が表示されます。

乗り入れている路線: この駅を利用している路線の一覧です。
リストはASCIIコード順(英数字順。大文字が先、小文字が後)で並べられます。

路線に所属していない編成: 特定の路線に割り当てられていない、ここに停車する運行便が表示されます。

直通駅: 乗り換えなしで到達できる駅の名前と、配送可能な品目(旅客・郵便・貨物)の一覧です。
このリストは、配送可能な品目ごとに、ASCIIコード順(英数字順)で並べられます。

simutrans-124.5/simutrans/text/ja/trafficlight_info.txt000066400000000000000000000021451520675460500234130ustar00rootroot00000000000000交通信号機の制御

交通信号機の制御

このダイアログでは、信号機の赤信号と青信号が切り替わる周期を調整できます。入力する数値は、東西方向(1番目)および南北方向(2番目)それぞれの青信号の時間(秒)を指定するものです。

マップ上のすべての信号機は、デフォルトでは同じタイミング(同期)で切り替わります。3番目の入力欄にオフセット(秒)の値を設定することで、特定の信号の切り替わりタイミングを意図的にずらし、車両を停止させずに円滑に流すを実現できます。

下段の数値入力フィールドは、東西および南北方向のそれぞれの黄信号の時間(秒)(青から赤に変わる前の猶予時間)を指定するものです。

信号機は道路ツールから設置できます。

simutrans-124.5/simutrans/text/ja/tramtools.txt000066400000000000000000000031641520675460500217600ustar00rootroot00000000000000路面電車/ライトレールツール

路面電車/ライトレールツール

このツールバーでは、路面電車の軌道、架線、車庫の建設を行えます。ツールを選択すると、マウスカーソルがそれぞれのアイコンに変わります。

路面電車の軌道: 軌道の建設方法については、鉄道ツールのヘルプを参照してください。
路面電車の軌道は、通常の道路上、および道路トンネル内や道路橋の上にも敷設することが可能です。(トンネルや橋への敷設がうまくいかない場合は、[Ctrl] キーを押しながら建設を試みてください。)また、道路の脇に鉄道用線路を敷設して利用することも可能です。

架線と信号機: これらは鉄道ツールにあるものと同様の機能を持っています。

車庫: 車庫では路面電車の編成、路線の設定、およびスケジュール割り当て後に運行開始を行えます。
車庫は平坦な軌道の端(終端)にのみ設置できます。

路面電車停留場(電停): 電停の建設に関する詳細は、駅情報ウィンドウのヘルプを参照してください。
道路上に設置する場合は「バス停」を使用し、道路以外の場所(専用軌道など)では「駅(プラットフォーム)」を使用してください。

simutrans-124.5/simutrans/text/ja/underground.txt000066400000000000000000000054351520675460500222730ustar00rootroot00000000000000地下の建設

地下の建設

Simutransには2種類の地下表示モードがあり、表示設定ウィンドウ、またはキーボードショートカットから切り替えることができます。

地下表示 U
地下の全階層が一度に表示され、地上にあるすべてのオブジェクトが非表示になります。
デフォルトでは地表のグリッドが表示されますが、この表示では目的の場所にオブジェクトを配置するのが難しいため、地下路線建設には次の「ハイトカット表示」の使用を推奨します。

ハイトカット表示 Ctrl+U
選択した階層(高さ)より上にあるものをすべて非表示にします。
マウスカーソルが現在アクティブな階層のグリッドに固定されるため、地下路線建設にはこのモードを強く推奨します。

表示階層の切り替えは、キーボードの +(高く)/ -(低く)キー、または 表示設定ウィンドウから行えます。


路線の建設手順

1. 地下へのアクセス
地下路線は通常、トンネル建設ツールを使用して構築します。まず地上でトンネル、またはトンネルの入り口(トンネルツールを選択した状態で Ctrl キーを押しながら斜面をクリック)を作成する必要があります。

2. 地下での階層変更(昇降)
地下に別の層を構築するには、スロープツールメニューにある「陸地を盛り上げ/切り下げ」するツールを使用します。目的のツールで道路や線路の端をクリックするだけです。

3. 路面電車の敷設
路面電車の軌道は、地下でも通常の 路面電車/ライトレールツール を使用し、既存の道路上に敷設します。


駅や付属施設等に関する制約

· 駅・バス停: 自社の 駅やバス停 同士であれば、異なる階層に重ねて建設できます。
· 付属施設: 郵便局や待合室などの駅付属施設は地下には建設できません。
· 信号: 地上と同様に使用できます。

: 地下への掘削がこれ以上進まない場合は、地表に障害物がないか、またはマップの高度制限(上限・下限)に達していないかを確認してください。

simutrans-124.5/simutrans/text/ja/vehiclelist.txt000066400000000000000000000024011520675460500222400ustar00rootroot00000000000000全車両リスト

全車両リスト

ゲーム内に登場するすべての車両(動力車および貨車・客車)を一覧表示します。
デフォルトでは、現在利用可能な車両と未登場車両のすべて表示されます。

このリストの最大の特徴は、未登場の車両を事前に確認できる点にあります。
通常、車庫(デポ)の購入メニューには表示されない将来の新型車両を把握できるため、将来の輸送需要を見据えた、より正確な車両更新計画の策定に役立ちます。

未登場も表示: チェックを入れると、まだゲーム内に登場していない将来の車両を表示します。外すと現在購入可能な車両のみに絞り込まれます。
旧型を表示: 販売終了した旧型車両をリストに表示します。車庫(デポ)にある同機能と同じ役割を果たします。
貨物フィルター (すべて): ドロップダウンメニュー(初期状態は「すべて」)から旅客、郵便、または特定の貨物種別を選択し、その輸送に対応した車両のみを表示します。

simutrans-124.5/simutrans/text/ja/window.txt000066400000000000000000000277021520675460500212470ustar00rootroot00000000000000ゲーム・インターフェースの使い方

ゲーム・インターフェース

Simutransは、現在のゲームに関する情報、ツールバーやゲームコントロールへのアクセス、現在のゲームワールドのビューを提供するゲーム・インターフェースを通してプレイします。Simutransは、マウスキーボードで操作できます。キーボードレイアウトは、ヘルプメニューのキーボードバインディングで確認するか、 [F1]キーを押して確認することができます。また、機能のないキーを押すたびに表示されます。
重要: キーは大文字と小文字を区別します。

ゲームウインドウ

現在のゲームワールドの一部を表示します。マウスホイール、[PGUP]/[PGDN]キー、またはメインのツールバーにある2つの専用アイコンを使って、拡大/縮小することができます。マップのスクロールにはいくつかの方法があります。

· マウスの右ボタンを押したまま、地図を任意の方向にスクロールして、目的の位置に移動することができます。初期設定では、マウスの動きに合わせてマップがスクロールします。この動作は、表示設定ウィンドウで変更できます。
· テンキーを使います。
· マップウィンドウ上の適切なポイントを選択して、目的のマップセクションへ直接ジャンプ(移動)します。
· Jump to ダイアログ([J]キーを押す)を使って特定の座標へ移動します。

街、工場、駅、車両、輸送ラインなど、いくつかのオブジェクトの名前は編集可能です。編集するには、虫眼鏡(調査ツール)で開いたダイアログの名前欄をマウスでクリックします(下図)。編集した内容はすぐに有効になります。Returnキーを押してフィールドを終了します。

タイトルバー

タイトルバー(ページ上端の見出し)の左側には、現在プレイ中のSimutransのバージョンとリリース日が表示されます。タイトルバーの右側には、ゲーム・インターフェイスを最小化、最大化したり、Simutransを終了したりするコントロールがあります。

ゲームウィンドウの上部には、Simutransをプレイする際に、ゲームツールバーやゲームコントロールにアクセスするためのアイコン(メインツールバー)が並んでいます。アイコン(これを利用するにはクリック)は、いくつかのメニューに分類されます:

メインメニュー

オプション: フロッピーディスクのアイコンをクリックするとダイアログが表示され、そこからゲームのロード、セーブ、新規ゲームの開始ができます。

マップ: オブジェクトを含む世界のオーバービュー・マップは、プレイヤーの輸送機関の動きを示し、いくつかの統計情報を見ることができます。このマップ上の任意のポイントを選択すると、ゲームウィンドウはそれに該当する画像がある地点へ移動します。

調査ツール: 虫眼鏡アイコンは、調査ツールを象徴しています。これがアクティブな場合、カーソルも虫眼鏡になります。このアイコンでオブジェクト(建物、車両など)を選択すると、その上に情報ウィンドウが表示されます。特に削除ツールで作業したばかりの場合は、常に虫眼鏡に戻ることをお勧めします。そうすれば、オブジェクトの情報を呼び出す代わりに、誤ってオブジェクトを破壊してしまうことを避けることができます。

スロープツール: スロープツールのアイコンをクリックすると、ツールバーが表示され、各タイルの傾斜を変更して地形を変更することができます。

各種の建設用ツールバー: 交通機関の種類ごとに用意されたツールバーのセットを開きます。
· 鉄道ツール
· モノレール/リニアツール
· 市電/軽便鉄道ツール
· 道路ツール
· 船舶ツール
· 飛行機ツール

注意:一部のパックセットには、ナローゲージツールなど、追加的な建設用ツールバーが含まれている場合があります。
各ツールバーには車両基地(車庫)のアイコンがあります。車庫を建設すると、車庫ウィンドウに入って車両にアクセスできるようになります。

特別建築物: 黄色い丸印の中心に赤い点があるアイコン(pak128ではクレーンのアイコン)をクリックして開くツールバーには、交通ネットワークの構築を補完するさまざまな建設ツールが用意されています。

撤去/取り壊し: 赤いXのアイコン(pak128ではブルドーザーのアイコン)は、地図上の様々なオブジェクトを削除するために使用されます。

管理メニュー

路線編集: ここでは輸送経路(ネットワークアイコン)を作成し、変更・削除することができ、またその情報も閲覧することができます。

一覧表: 一覧表のアイコンをクリックすると、次の7種類の一覧表が表示されるサブメニューにつながります:
· 駅一覧
· 乗り物一覧
· 街一覧
· 貨物一覧
· 産業一覧
· 名所旧跡一覧
· マーカー一覧

メッセージ表: この郵便受けのアイコンをクリックすると、ゲーム開始時または前回のゲーム読み込み時以降に発生したすべてのメッセージが一覧表示されます。

財務: このお金のアイコンは、あなたの現在の財務状況について、知る価値のあるものすべてを表示するウィンドウを開きます。

その他のメニュー

スクリーンショット: カメラアイコンをクリックすると、現在のゲームウィンドウのスナップショット(ツールバーを含み、タイトルバーは含みません)が .png イメージとして simutrans/screenshot/ フォルダに保存されます。画像は、[c] キーでもキャプチャできます。

一時停止: 輸送会社の忙しい経営から離れて一息つきたいときは、コーヒーカップのアイコンをクリックして温かいお茶やコーヒーを注文してください。シムトランでは、もちろん無料でサービスを受けることができます!
[p] キーでもゲームの一時停止/一時停止解除ができます。

早送り: シムトランの時間経過が遅すぎる場合、この >> アイコンを押すことで、時間を早めることができます。早送りは、[W] キーでも選択/解除できます。
警告: 早送りモードでは、ゲームがスムーズに進行しない場合があります。表示設定ウィンドウの下部にあるディスプレイのパフォーマンス情報セクションは、コンピュータがこの変化に対応できるかどうかを示します。

ヘルプ: ?アイコンをクリックすると、ヘルプの索引を表示します。[F1] キーでも表示できます。

注意: プレイするパックセットによっては、メインツールバーに表示されるアイコンが増えることがあります。例えば次のアイコンです: ズームイン / ズームアウトグリッドを表示する90°回転する など。

ステータスバー

ゲームワールドのウィンドウ下部にあるステータスバーには、ゲームの進行に合わせて常に更新される情報があります。ステータスバーに表示される情報は、左から右の順に以下の通りです:
· 日付と時間: シムトランでは、1年を月、日、時間に分けています。日付は、「年代設定」(下記参照)を有効にしたときの車両や建物の利用可能性を制御します。日付の前には、気象の季節を示すアイコンが表示されます。一部の気候設定では、季節によって地面が変化することがあります。
· プレーヤー: 現在のプレイヤーの名前がプレイヤーの色で表示されます。
· 現金: 現在のプレイヤーの現金口座残高が表示され、常に更新されます。建設費やその他のコスト(車両の購入費、輸送インフラの維持費、車両の運行コスト)が考慮されています。
· 位置座標: 括弧内には、カーソルの x 座標と y 座標、現在位置するタイルの高さが表示されます。座標(0,0)はマップの左上に位置し、高さ0は水位またはそれより上に位置します(マップ作成時の水位設定によります)。
· ゲーム速度: 値(T=1.00)は現在のゲームの進行速度を示します。[,] / [.] キーで時間を少しずつ加速/減速することができます。ディスプレイ設定画面の下部にあるディスプレイパフォーマンス情報には、コンピュータがその変化に対応できるかどうかが表示されます。早送りモードでは、追加のアイコン >> が表示されます。
· 時代に従う: 現在のゲームで、年代設定が選択されているかどうかをカレンダーアイコンで表示します。

スクロールバー

ゲームワールドのウィンドウ下部にあるステータスバーに、メッセージが表示されることがあります。表示されるメッセージは、メッセージセンターオプションで定義できます。
ヒント: メッセージをクリックすると、ゲームビューが関連する位置にセンタリング(中央寄せ)されます。

simutrans-124.5/simutrans/text/ko.tab000066400000000000000000001334461520675460500177110ustar00rootroot00000000000000§Korean PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: ko Korean # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 4.04 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable 불가능 cl_btn_filter_enable 가능 cl_btn_filter_settings 설정 cl_btn_sort_asc 오름차순 cl_btn_sort_desc 내림차순 cl_btn_sort_id 번호(ID) cl_btn_sort_income 수입 cl_btn_sort_name 이름 cl_btn_sort_type 종류 clf_btn_alle 모두 선택 clf_btn_invers 반전 clf_btn_keine 모두 해제 gl_btn_sort_bonus 보너스 gl_btn_sort_name 이름 gl_btn_sort_revenue 수입 gl_btn_unsort 정렬되지않음 hl_btn_filter_disable 불가능 hl_btn_filter_enable 가능 hl_btn_filter_settings 설정 hl_btn_sort_asc 오름차순 hl_btn_sort_desc 내림차순 hl_btn_sort_name 이름 hl_btn_sort_type 종류 hl_btn_sort_waiting 대기량 hlf_btn_alle 모두 선택 hlf_btn_invers 반전 hlf_btn_keine 모두 해제 Networks 네트워크 Queueing 대기시간 Road toll 통행료 Scenario 시나리오 정보 Transfers 환승 #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic 북극권 기후 desert 사막 기후 mediterran 지중해성 기후 rocky 고산 기후 temperate 온대 기후 tropic 열대 기후 tundra 툰드라 기후 #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed 시나리오를 불러오는데에 실패했습니다. Can't buy obsolete vehicles! 생산 중단된 차량을 살 수 없습니다! Cannot alter water 더이상 물을 채우거나 뺄 수 없습니다. Cannot built depot here! 여기에 차고지를 건설할 수 없습니다. Cannot built this station/building\nin underground mode here. 이 건물/역은 지하에 건설할 수 없습니다.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. 일반 노선을 만들 수 없습니다!\n필터탭을 사용해서\n노선 타입을 선택하세요.\n Cannot create socket 소켓을 만들 수 없습니다 Convoi handles exhausted! 최대 차량 수에 도달했습니다.\n이 이상 차량을 생성할 수 없습니다. Convoy already deleted! 이미 차량이 제거되었습니다! Das Feld gehoert\neinem anderen Spieler\n 이 토지는\n다른 플레이어가\n소유하고 있습니다!\n Der Besitzer erlaubt das Entfernen nicht 주인이 \n없애는 것을\n허락하지 않습니다!\n Diese Zusammenstellung kann nicht fahren!\n 이 조합으로\n운행할 수 없습니다!\n Flugzeughalt muss auf\nRunway liegen!\n 비행기는 활주로에서만 착륙할 수 있습니다. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n 여기에\n공항을 건설할 수 없습니다.\n Hier kann kein\nSignal aufge-\nstellt werden!\n 여기에 열차 신호등을\n만들 수 없습니다.\n In order to lock the game, you have to protect the public player by password! 게임을 잠그기위해 공공 서비스에 비밀번호를 거세요! Lost connection\nto server! 서버와의 연결이 끊어졌습니다! Lost synchronisation\nwith server. 서버와의 동기화에 실패했습니다! Maglevhalt muss auf\nMaglevschiene liegen!\n 자기부상열차는 자기부상열차 철도 위에서만 운행할 수 있습니다. Monorailhalt muss auf\nMonorail liegen!\n 모노레일 열차는 모노레일 위에서만 운행할 수 있습니다.\n Monorails are not available yet! 모노레일은 아직 사용할 수 없습니다! Narrowgaugehalt muss auf\nNarrowgauge liegen!\n 협궤열차는 협궤철도 위에서만 운행할 수 있습니다. No through station here! 역은 선로의 끝부분이나 교차지점이 없는\n직선 선로 위에서만 건설할 수 있습니다. Not enough money! 돈이 부족합니다. On narrowgauge track only!\n 협궤철도에서만 이용할 수 있습니다!\n Only public player can lock games! 주인장만 게임을 잠글 수 있습니다! Out of funds 돈이 부족합니다! Post muss neben\nHaltestelle\nliegen!\n 역 확장 건물(예: 우체국, 창고)은\n정류장/역 옆의 빈 타일에 건설해야 합니다.\n\n Protocoll error (expecting game) 프로토콜 오류 (대기중인 게임에서) Schiffhalt muss im\nWasser liegen!\n 배는 항구 주변에서만 정박 할 수 있습니다!\n Server busy 서버가 복잡합니다. Terraforming not possible\nhere in underground view 지형변경은 지하에서 사용할 수 없습니다. This tunnel branches. You can try Control+Click to remove. 이 터널은 2개 이상의 출구가 있습니다. Crtl+클릭으로 철거하세요. Upgrade must have\na higher level 더 높은 레벨에서 업그레이드를 해야합니다. Vehicle %s cannot choose because stop too short! 차량 %s가 정차하기에는 승강장이 너무 짧습니다. Zughalt muss auf\nSchiene liegen!\n 기차는 철도 위에서만 운행할 수 있습니다.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s 도움말

도움말 목록

Simutrans에 대해

%1$s

사용법

%2$s

게임 시작

%4$s

게임하는 법

%5$s

도구

%3$s

기타

%6$s Keyboard Help\n

Keyboard Help

\n 단축키 도움말\n

단축키 도움말

\n

\n단축키 도움말은 단축키의 다양한 기능들을 보여줍니다.\n

\n

\n단축키 도움말은 할당되지 않은 단축키를 누를때나 일반 도움말을 열 때 나타납니다.\n

\n

\n키 입력은 대소문자를 구별합니다.\n (대문자 입력은 [Shift]를 사용)\n

\n

\n할당된 탄축키는 다음과 같습니다:\n

\n

\n[화살표키]: 게임 화면을 화살표 방향으로 이동합니다.
\n[Backspace]: 게임화면의 모든 창, 툴바, 도움말을 닫습니다.
\n[Delete], 또는 [Esc]: 게임화면의 맨위의 창, 툴바 또는 도움말을 닫습니다.
\n[Enter], or [Return]: 행동을 확정할때 사용합니다.
\n[Page-Up], 또는 [>]: 게임화면을 확대합니다.
\n[Page-Down], 또는 [<]: 게임화면을 축소합니다.\n[F1]: Simutrans 도움말을 엽니다.

\n

\n[1]: 왼쪽아래(남쪽) 방향으로 화면이 이동합니다.
\n[2]: 아래(동남쪽) 방향으로 화면이\n 이동합니다.
\n[3]: 오른쪽아래(동쪽) 방향으로 화면이 이동합니다.
\n[4]: 왼쪽(남서쪽) 방향으로 화면이 이동합니다.
\n[6]: 오른쪽(북동쪽) 방향으로 화면이 이동합니다.
\n[7]: 왼쪽위(서쪽) 방향으로 화면이 이동합니다.
\n[8]: 위(북서쪽) 방향으로 화면이 이동합니다.
\n[9]: 오른쪽위(북쪽) 방향으로 화면이 이동합니다.\n

\n

\n[Shift] + 마우스: 지도상에서 사용하면 산업 공급 체인의 연결들을 볼\n 수 있습니다.
\n[CTRL] + 도구버튼: 속도제한이 높은 도로나 선로 위에 속도제한이 낮은 도로나 선로를 덮어 씌울 때, 또는 똑바른 직선 도로나 선로를 건설할 때 사용합니다.\n

\n

\n\n Decrease water height 수면을 한 단계 내리기 Highlight railroad tracks 철도 선로 강조 Highlite depots 차고지 강조 Highlite electrical transmission lines 송전선 강조 Highlite factories 공장 강조 Highlite forests 숲 강조 Highlite tourist attraction 관광명소 강조 Increase water height 수면을 한 단계 올리기 Open station/stop details 역 세부정보 보기 Overlay city limits 도시 경계 보이기 Overlay passenger destinations when a town window is open 도시 창이 열려있는 경우 승객의 목적지를 표시합니다. Overlay schedules/network 스케줄/네트워크 보기 Overlay town names 도시 이름 표시 Please click on the map to add\nwaypoints or stops to this\nschedule. 지도를 클릭하여 웨이포인트 또는 역을 스케줄에 추가하세요. Set tile climate %s 이 땅을 %s 기후로 바꿉니다. Show speedlimit of ways 선로/도로의 제한속도 표시 Show the owenership of infrastructure 기반시설(선로/도로 등)의 소유자 표시 Show transported freight/freight network 운송된 화물의 경로 표시 Shows a listing with all industries on the map. 지도에 존재하는 모든 산업의 목록 #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s는 이제\n %s와 관광지 %s 사이에 버스 서비스를 제공합니다.\n(%i,%i)부터.\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s는 이제\n %s와 공장 %s 사이에 버스 서비스를 제공합니다.\n(%i,%i)부터.\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s는\n%i 트럭을 %s과 (%i,%i)부터\n %s\n사이에 (%i,%i)부터\n 개통했습니다.\n\n %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s는\n %s와 (%i,%i)부터\n%s사이에\n(%i,%i)부터\n새로운 철도를 개통했습니다. Airline service by\n%s\nnow between\n%s \nand %s.\n 항공서비스가 %s에 의해\n%s와 \n%s사이에 제공됩니다.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n 페리서비스가 %s에 의해\n%s와 \n%s사이에 제공됩니다.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s는 \n%s와\n%s 사이에\n새로운 버스서비스를 개통했습니다.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS 지도 편집 도구 LISTTOOLS 목록 MAGLEVTOOLS 자기부상열차 도구 MONORAILTOOLS 모노레일 도구 NARROWGAUGETOOLS 협궤철도 도구 RAILTOOLS 철도 도구 ROADTOOLS 도로 도구 SHIPTOOLS 선박 도구 SLOPETOOLS 지형/슬로프 도구 SPECIALTOOLS 특수 건설 도구 TRAMTOOLS 노면전차 도구 #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s는 새로운 본사를 건설했습니다. Factory chain extended\nfor %s near\n%s built with\n%i factories. 경제 성장:\n%s 근처의 %s는 체인을 확장합니다.\n%i에 새로운 공장이 건설됩니다.\n New %s now available:\n%s\n 새로운 %s를 사용할 수 있습니다.:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. 산업 성장:%s를 위한 %s근처에\n 새로운 공장 체인이\n %i 공장과 함께 건설됩니다. New vehicle now available:\n%s\n \n 새로운 차량을\n 사용할 수 있습니다:\n\n\n -- %s --\n\n Now %u clients connected. 플레이어 %u가 연결되었습니다. Remove vehicle from map. Use with care! 차량을 제거합니다. 조심하세요! Screenshot\ngespeichert.\n 스크린샷이\n저장되었습니다.\n Sends the convoi to the last depot it departed from! 가장 마지막으로 방문한 차고지로 보냅니다. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. 대형 축제와 함께,\n%s에 새로운\n기념물이 건설되었습니다.\n%i 시민들이 기뻐하고 있습니다. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter 노선 필터 #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (차고지 안에 있음) \nBauzeit bis 까지 \nBauzeit von \n에서 나타남 \nCan't open heightfield file.\n \nheightmap을 열 수 없습니다.\n \ndirection: \n방향: \nelektrified \n전철화 된\n \nHeightfield has wrong image type.\n \nHeightfield가 잘못된\n이미지 타입입니다.\n \nis reserved by: \n열차에 의해 예약됨 \nminimum speed: \n최저속력: \nnot elektrified \n전철화 되지 않은\n \nRibi (masked) \n방향\m (가려짐): \nRibi (unmasked) \n방향\n (드러남): \nSet phases: \n남북/동서 교통신호 길이 조절: \nsingle way \n단선 \nway1 reserved by \nway 1 예약됨 \nway2 reserved by \nway 2 예약됨 \nwith sign/signal\n \n표지판/신호등 포함\n %d buildings\n %d 건물 %d convois 차량 %d대 %d Einzelfahrzeuge im Depot %d 차량은 여기에 입고되어 있습니다. %i km/h (max. %ikm/h) %i km/h (최고속력 %ikm/h) %i years %i months old. 수령: %i 년 %i 개월 %s at (%i,%i) now public stop. %s는 공공 정류장이되었습니다. %s building %s %s %s %s %s %s city %d %s %s %d %s %s has entered a depot. %s가 차고로 들어왔습니다. %s land %d %s %s %d 외부의 %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s 주민수가 %i에 도달해, 새 시청을 지었습니다. %s\nis crowded. %s\n는 혼잡합니다.\n\n %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\n최고속력 %3$i km/h\n\n현재속력 %2$i km/h.\n\n %s\nwas liquidated. %s 가 파산했습니다.\n모든 사업은 중지되었고, 재산은 매각되었습니다. %u Client(s)\n %u 클라이언트\n %u Player (%u locked)\n %u 플레이어 (%u 잠김)\n <스케줄 취소> <새 노선 만들기> <개별 스케줄> <노선 없음> <스케줄 없음> <노선 만들기> 1 convoi 차량 1대 1 Einzelfahrzeug im Depot 여기에 차량 1대가 있습니다. 1LIGHT_CHOOSE 밝기: 1WORLD_CHOOSE 새 게임 설정: 2LIGHT_CHOOSE 색상: 2WORLD_CHOOSE 지도 번호: 3LIGHT_CHOOSE 스크롤 속도: 4LIGHT_CHOOSE 역방향 스크롤 5LIGHT_CHOOSE 정류장의 보행자 5WORLD_CHOOSE 도시의 수: 6LIGHT_CHOOSE 도시의 보행자 6WORLD_CHOOSE 교통량: 8WORLD_CHOOSE 낮/밤 변화 A bridge must start on a way! 다리 경로의 끝부분을 소유하고 있지 않거나, 다리 경로가 막혀있습니다. Abfrage 조사도구 Abnehmer 소비자 About 에 대해 About scenario Copyright Abriss 파괴/제거 Absenken 땅 낮추기 Abspanntransformator 변전소 Accelerate time 가속시간 Act. load: %u MW\n 송전량: %u MW\n Active player only 활동적인 플레이어만 Add forest 숲 추가 Add random citycar 랜덤 자가용 추가 add server 서버 추가 Add Stop 정류장 추가 Add stops for backward travel 여정 뒤에 정류장을 추가합니다. air 활주로/유도로 aircraft_tab 화물 수송기 airplane 항공기 Airport 공항 AIRTOOLS 비행기 도구 All 모두 all convoi tooltips 모든 차량 도구팁 Allow city growth 도시성장 허용 Allow player change 플레이어 교체 허용 allowed climates:\n 가능한 기후:\n Alters a schedule. 스케줄에서 정류장을 추가/제거 Angenommene Waren 근처 공장에 필요한 물품 anhaengen 추가 Anhaenger_tab 트레일러 Anheben 땅 올리기 Appends stops at the end of the schedule 스케줄 끝에 정류장 추가 Apply Line 노선 적용 April 4월 Arbeiter aus: 노동자 거주지: Arrived 도착 Assets 자산 Aufloesen 해체하다 Aufspanntransformator 변전소 August 8월 Autohalt muss auf\nStrasse liegen!\n 버스/자동차는 도로 위에서만 운행할 수 있습니다. Available 사용가능 Bahndepot 철도 차량기지 Bankrott:\n\nDu bist bankrott.\n 파산:\n\n당신은 파산했습니다!\n battery 배터리 Baum 나무 baum builder 나무 심기 Baustelle 공사 현장 Bauzeit 공사소요시간 Beenden 종료 Beginner mode 초보자 모드 Besonderes Gebaeude 관광 명소 BF 역 bio 생물학적인 Blockstrecke ist\nbelegt\n \n선로가 또다른 기차에\n의해 막혀있습니다!\n Boden 땅 Bonusspeed: %i km/h 가능한 최고속력: %i km/h Boost (%%) 증산률 (%%) bridge is too high for its type! 이 타입의 다리를 건설하기에는 너무 높습니다! Bridge is too long for this type!\n 이 타입의 다리를 건설하기에는 너무 깁니다!\n Bruecke 다리 Bruecke muss an\neinfachem\nHang beginnen!\n 다리는 곧은 경사 위에서 건설해야 합니다!\n Brueckenboden 다리 Build air depot 격납고 건설 build choosesignals 입선 배분 신호기 건설 Build city market 가장 가까운 도시에 새로운 시장 건설 Build drain 변전소 build HQ 본사 건설 Build land consumer 새 발전소 건설 Build maglev depot 자기부상열차 차량기지 건설 Build monorail depot 모노레일 차량기지 건설 Build narrowgauge depot 협궤철도 차량기지 건설 Build powerline 송전선 건설 Build presignals 2폐색 신호기 건설 Build road depot 차고지 건설 Build ship depot 조선소 건설 Build signals 신호등 건설 Build train depot 철도 차량기지 건설 Build tram depot 노면전차 차량기지 건설 Build truck depot 차고지 건설 Building costs estimates 예상 건설 비용 Buildings 도시 건물 Built artifical slopes 인공 경사면 건설 Built random attraction 관광지를 무작위로 건설 Bus_tab 버스 Can only move from halt to halt or waypoint to waypoint. 정류장부터 정류장 사이\n또는 웨이포인트부터 웨이포인트 사이에만\n이동할 수 있습니다. Cancel 취소 Cannot connect to offline server! 오프라인 서버에 연결할 수 없습니다. Capacity: 수송량: Capacity: %.0f MW 송전 용량: %.0f MW\n Capacity: %d%s %s\n 수송량: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) 수송량: %s\n적재량: %d (%d%%) Cars are not available yet! 자동차는 아직 사용할 수 없습니다. cars.\nstate 자동차\n Cash 잔액 Change player 플레이어 교체 Chart 차트 Chat_msg 채팅 Choose direction 방향 선택 Choose operation executed on clicking stored/new vehicles 입고된/새로운 차량을 클릭해서 작동여부를 선택 chooses a random map 랜덤 맵을 선택합니다. citicens 시민 City attraction 시내 관광명소 City industries 시내 시장 City list 도시 목록 City size 도시 크기 city_road 시내도로 citybuilding builder 도시 건물 건설 CityLimit 도시 규모 한계 cl_title 차량 목록 cl_txt_sort 분류: Clear block reservation 예약된 선로를 보임/리셋함 clf_chk_aircrafts 항공기 clf_chk_cars 버스/트럭 clf_chk_indepot 차고 안에 clf_chk_maglev 자기부상열차 clf_chk_monorail 모노레일 clf_chk_name_filter 필터 이름: clf_chk_narrowgauge 협궤철도 열차 clf_chk_noincome 수입 없음 clf_chk_noline 노선 없음 clf_chk_noroute 경로 없음 clf_chk_noschedule 스케줄 없음 clf_chk_ships 선박 clf_chk_spezial_filter 특수 필터: clf_chk_stucked 갇힘 clf_chk_trains 기차 clf_chk_trams 노면전차 clf_chk_type_filter 필터 타입: clf_chk_waren 필터 상품: clf_title 차량목록 필터 Climate Control 기후 설정 closed 닫음. COLOR_CHOOSE\n 색상을 선택하세요.:\n Company bankrupt 회사 파산 Company_msg 경쟁자 Comparing pak files ... 팩 비교 중... Congratulation\nScenario was complete in\n%i months %i years. 축하합니다!\n이 시나리오가 \n%i개월, %i년 만에\n완료되었습니다! Connected stops 연결된 정류장 Connected with server 서버에 연결되었습니다. Constructed by 제작자 Constructed by %s 제작 : %s construction speed 건설 속도 Construction_Btn 건설비용 Consumed 소비 convoi %d of %d %d번째 차량 %d대중 convoi error tooltips 차량 오류 도구팁 Convoi has been sent\nto the nearest depot\nof appropriate type.\n 차량을 적당한 타입의\n가까운 차고로 보냅니다.\n Convoi is sold when all wagons are empty. 차량이 비면 팔릴 것입니다. convoi mouseover tooltips 차량 마우스오버 도구팁 convoi passed last\nmonth %i\n \n지난달에 통과한\n차량: %i\n Convois 차량 Convois: %d\nProfit: %s 차량: %d\n이익: %s Convoys 차량 Copy Convoi 차량 복사 Copy the selected convoi and its schedule or line 선택된 차량과 스케줄 또는 노선을 복사 cost for removal 제거비용: Cost: %8s (%.2f$/km %.2f$/m)\n 비용: %s (%.2f$/km %.2f$달)\n Cost: %8s (%.2f$/km)\n 비용: %8s (%.2f$/km)\n Costs 비용 Create a new line based on this schedule 이 스케줄로 새 노선 만들기 curiosity builder 호기심있는 건설업자 curlist_title 관광명소 목록 CURRENCY ₩ Currently playing: 게임중: Customers live in: 소비자 거주지: deactivated in online mode 온라인에서는 사용할 수 없습니다. Deccelerate time 감속 시간 December 12월 decrease underground view level 지하시점 레벨 낮추기 Del Stop 제거 Delete Line 노선 삭제 Delete the current stop 현재의 정류장 삭제 Delete the selected line (if without associated convois). 선택한 노선 삭제 (노선에 차량이 없다면) Delete this file. 이 파일을 삭제합니다. Delivered 출하량 Demand 수요량 Demand: %.0f MW 수요량: %.0f MW\n Denkmal 기념물 Departed 출발 Departure board 출발 시간 Depots 차고지 Der Tunnel ist nicht frei!\n 터널이 비어있지 않습니다.\n Destination 도착 Details 세부사항 Die Bruecke ist nicht frei!\n 다리가 비어있지 않습니다!\n diesel 디젤 Direkt erreichbare Haltestellen 여기에서 바로가는 경로 disable midi 미디 음악 음소거 Display settings 디스플레이 설정 Distance 주행거리 Dock 항구 Dock must be built on single slope! 항구는 반드시 1개의 경사면 타일에 건설되어야 합니다. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen 부채 상환 기한이 %d개월 남았습니다. Durchsatz 최대 Economy 경제 Eigenbesitz\n 공공 재산\n Ein %s\npasst hier nicht.\n '%s'는 여기에서\n 맞지 않습니다!\n Einstellungen aendern 옵션 바꾸기 electric 전기 Electricity 전기 Electricity producer\n\n 전기 생산자\n\n Electrics_tab 전기 Electrify track 전기철도 선로 enlarge map 지도 넓히기 enter a value between %i and %i %i와 %i사이의 값을 입력 Enter address 서버 주소 입력 Enter Password 플레이어 이름과 비밀번호를 변경 Error 오류 Erzeuge neue Karte.\n 새 지도가 생성될 때까지\n기다리세요.\n\n(대형 지도는 시간이\n오래 걸립니다.)\n\n Es wird bereits\nein Fahrplan\neingegeben\n 이 스케줄은\n진행중입니다.\n스케줄을 새로 짜기 전에\n먼저 기존 스케줄을 끝내세요!\n Fabrikanschluss 연결된 공장 Fabrikname 공장 이름 Factories 공장 factory details 공장 연결 factorybuilder 공장 건설자 Fahrplan 스케줄 Fahrtziel 행선지: Fahrzeuge koennen so nicht entfernt werden 이 방법으로 차량을\n제거할 수 없습니다!\n Fahrzeuge: 차량: Farbe 플레이어 색상 Fast forward 빨리감기 February 2월 Ferry_tab 선박 Fertig 완료 Filename 파일명: Filter: 필터: Finances of %s %s의 재정 Finanzen 재정 find mismatch 팩을 비교하세요 fl_title 공장 목록 Flug_tab 여객기 follow me 따라가기 Follow the convoi on the map. 지도상의 차량을 따라감 Forest 숲 Found new city 신도시 건설 FPS: FPS: Fracht 화물 Frame time: 프레임 시간: Free Capacity 비어있음 freeplay mode 프리게임 모드 Friction: 현재 마찰 요소: fuel_cell 연료전지 Full load 최소 적하량: Fundament 기초 Fussgaenger 보행자 Game info 온라인 게임 정보 GAME PAUSED 게임 일시정지 Game_msg 일반 Gear: 기어비: Gebaeude 건물 General 일반 Generated 생산 Generation: %.0f MW 발전량: %.0f MW\n Gewicht 무게 Gewinn 수입: Give the selected vehicle(s) an individual schedule 선택한 차량의 스케줄 또는 노선을 수정 gl_btn_sort_catg 카테고리 gl_title 상품 목록 go home 차고지로 보내기 Goods 상품 Goods AI 상품 AI Goods list 상품 목록 Gross Profit 현금 흐름 Groundobj 오브젝트 Grow city 도시 키우기 Growth 도시 성장 H 정지 Hangar 격납고 Happy 행복 Haus kaufen 집 구매 Helligk. 디스플레이 Help 도움말 Help text not found 도움말이 아직 작성되지 않았습니다. hide all building 모든 건물 숨기기 hide city building 도시 건물 숨기기 hide objects under cursor 커서 밑의 오브젝트 숨기기 hide station names 역 이름 숨기기 hide transparent 반투명하게 숨기기 hide trees 나무 숨기기 Hier warten/lagern: 대기중인 상품과 승객: Highlite schedule 스케줄의 정류장 강조 hl_title 역 목록 hl_txt_filter 필터: hl_txt_sort 분류: hlf_chk_airport 공항 hlf_chk_anleger 항구 hlf_chk_bahnhof 철도역 hlf_chk_bushalt 버스정류장 hlf_chk_frachthof 적재 구획 hlf_chk_keine_verb 연결안됨 hlf_chk_maglevstop 자기부상열차역 hlf_chk_monorailstop 모노레일역 hlf_chk_name_filter 필터 이름: hlf_chk_narrowgaugestop 협궤철도역 hlf_chk_overflow 수용량 초과 hlf_chk_spezial_filter 특수 필터: hlf_chk_tramstop 노면전차 정류장 hlf_chk_type_filter 필터 타입: hlf_chk_waren_abgabe 상품 출고: hlf_chk_waren_annahme 상품 입고: hlf_title 역 목록 필터 Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. 차고지를 찾을 수 없습니다.\n수동으로 차량을\n차고지로 보내세요. Homeless 노숙자 hydrogene 수소 Idle: 일이없는: ignore climates 기후를 무시 In the industry legend show only currently existing factories 현존하는 공장에 대한 범례만 보여줍니다. In Transit 운송 중인 Increase Industry density 산업 밀도 증가 increase underground view level 지하시점 레벨 증가 industrial building 공장 건물 Init map ... 지도 초기화중... Input 공급 Ins Stop 삽입 Insert stop before the current stop 현재의 정류장 전단계에 정류장을 삽입 Intercity road len: 도시간 도로 길이: Intro. date: 도입 날짜: invalid 정의되지 않음. Invalid coordinate 잘못된 명령 isometric map 게임화면 시점 January 1월 join game 온라인에서 플레이 July 7월 Jump to 건너뛰기 June 6월 Kann Spielstand\nnicht laden.\n 게임을 불러올 수 없습니다! Kann Spielstand\nnicht speichern.\n 게임을 저장을 할 수 없습니다! Kein Besitzer\n 주인 없음\n keine 없음 Keine Einzelfahrzeuge im Depot 차량이 없습니다. Keyboard_Help\n 단축키 도움말\n koord 좌표 Kreuzung 교차 labellist_title 표시판 목록 Lade Relief Height Map 불러오기 Laden 새 게임 불러오기 Land attraction 지역 관광명소 Land industries 산업 체인: LANG_CHOOSE\n 원하는 언어를 선택해주세요:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month 지난달: Last used tools 마지막으로 사용한 도구 Last Year 지난 해: Leaving depot! 차고지를 떠남! leer 비어있는 Legend 지도 범례 Leistung 힘 Leistung: %d kW 전력: %d kW Leitung 송전선 letzen Monat: diesen Monat: 지난달: 이번달: Line 노선 Line Management 노선 관리 Lineless convoys serving this stop 노선이 할당되지 않은 차량 Lines serving this stop 이 정류장에서 운행하는 노선 LKW_tab 트럭 Load game 게임 불러오기 load height data from file 파일로부터 height 데이터 불러오기 Load scenario 시나리오 불러오기 loaded 적재된 loaded passenger/freight 승객/화물 분류 Loading (%i->%i%%)! 적재중 (%i->%i%%) Loading addon paks ... 추가 팩을 불러오는 중... Loading map ... 지도를 불러오는중.... Loading paks ... 팩을 불러오는 중... Loading skins ... 스킨을 불러오는 중... Lock game 플레이어 교체를 할 수 없습니다. (인증 필요) Lokomotive_tab 기관차 m3 m³ Maglev 자기부상열차 maglev vehicle 자기부상열차 차량 maglev_track 자기부상열차 선로 Maglevdepot 자기부상열차 차량기지 Mail Demand %d\n 편지 수요 %d\n Mailbox 메시지 센터 Mailbox Options 메시지 센터 옵션 Maintenance 유지 관리 make stop public (or join with public stop next) costs %i per tile and level 공용 정류장(또는 이웃하는 공용정류장과 통합)을 만드는데 타일과 높이당 %i$의 비용이 듭니다. Manual (Human) 수동 (사람) Manufactured: 제조: Map roughness 지도의 거칠음: map zoom 지도 줌 March 3월 Margin (%%) 총이익 Marker 표지판 만들기 max 최대 Max Boost (%%) 최대촉진률 (%%) Max income: 최대수입: Max. speed: 최고속력: Maximum 254 stops\nin a schedule!\n 스케줄에는 최대 254개의 정류장을\n할당할 수 있습니다.\n maximum length of rivers 강의 최대 길이 Maximum tile height difference reached. 두 타일 사이의 높이차가\n최대치에 도달했습니다.\n Maxspeed 최대속력 May 5월 Median Citizen per town 도시당 인구 평균 Meldung 메시지 Menge 수량 MessageOptionsText \n새해\n\nAI\n\n도시\n\n새 행선지\n\n채팅\n\n신규차량\n\n역 포화\n\n문제\n\n교통정체\n\n시나리오 min 최소 minimum length of rivers 강의 최소 길이 Missing pakfiles 오브젝트가 없습니다! Modify the selected line 선택한 노선 수정 Monate alt 개월 전 Monorail 모노레일 monorail vehicle 모노레일 차량 monorail_track 모노레일 선로 Monorailboden 올라가는 길 Monoraildepot 모노레일 차량기지 month wait time 개월 대기시간 Months 개월 Monument 기념물 Monuments 기념물 Mountain height 산의 높이: Movingobj 오브젝트 이동 Music playing disabled/not available 음악 사용 불가능. Music volume: 음악 볼륨: mute sound 음소거 Name 이름 Narrowgauge 협궤철도 Narrowgauge are not available yet! 협궤철도는 아직 사용할 수 없습니다! narrowgauge vehicle 협궤철도 차량 narrowgauge_track 협궤철도 선로 Narrowgaugedepot 협궤철도 차량기지 Net ID: %p 전력망 ID: %p\n Net Wealth 전 자산 Neue Karte 새 지도 Neue Welt 새 게임 만들기 new convoi 새 차량 New Line 새로운 노선 New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. 새로운 노선이 만들어졌습니다!\n위의 노선을 선택하면\n노선을 할당할 수 있습니다.\n\n New Vehicles 새로운 차량 Nickname: 이름: no buildings hidden 모든 건물 표시 no convois 차량 없음 No goods are loaded onto this convoi. 이 차량은 적재하고 있는 상품이 없습니다. no goods waiting 대기중인 상품 없음 no load 적재량 없음 No Route 경로 없음 No stop here! 이 도구는 정거장 타일에만 사용되어야 합니다. No suitable ground! 적합한 지형의 땅이 없습니다. No terminal station here! 여기에 터미널을 \n지을 수 없습니다! 평지에 위치한 \n도로/선로 끝부분에 지어야 합니다. no timeline 모든 시대 no tree 나무 없음 No. of Factories 공장과 상점 수 Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n 차량을 출발시키 전에 스케줄을 설정해주세요!\n none 아무것도 없음 nord 북쪽 nordost 북동쪽 nordwest 북서쪽 Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! 경로를 탐색하는 동안\n차량의 스케줄을\n바꿀 수 없습니다. Not enough fields would remain. 이 농장 주변에는\n충분한 평지가 없습니다. November 11월 Now active as %s.\n %s가 활성화됨.\n Number of rivers 강의 개수 Object 오브젝트 Odometer: %s km 주행기록: %s km Ok 확인 Oktober 10월 On loan since %i month(s) %i달 동안 대출 On this map, you are not\nallowed to change player!\n 이 게임에서는 플레이어를 교체할 수 없습니다.\n\n Only city chains 도시에 체인 만들기 Only land chains 지상 산업 체인 만들기 Only one transformer per factory! 한 공장에는 하나의 변전소만 건설할 수 있습니다! Only show goods which are currently handled by factories 현존하는 산업이 취급하는 화물만 표시합니다. open 열기. Operation 운용 비용. Ops Profit 운영 이익 Optionen 설정 Or enter a server manually: 또는 참여할 서버 직접 입력: Origin 출처 ost 동쪽 Output 생산 Ownership 소유자 Pak which may cause severe errors: 다음 오브젝트 (팩)이 없기 때문에, 운송 체인에 문제가 발생할 수 있습니다: Pak which may cause visual errors: 다음 오브젝트 (팩)은 비슷한 오브젝트로 대체되었습니다: Pak(s) different: 다른 오브젝트: Pak(s) missing on client: 다음 오브젝트 (팩)이 없습니다: Pak(s) not on server: 서버에 없는 오브젝트 (팩): Pakset differences 팩 차이 paletten 틀 Pas_tab 여객열차 Passagiere 승객 Passagierrate 승객 등급 Passagierziele 승객/편지 목적지 Passenger AI 승객 AI Passenger Demand %d\n 승객 수요 %d\n Passengers %d %c, %d %c, %d no route 승객 %d %c, %d %c, %d 경로없음 Passengers %d %s, %d %s, %d no route 승객 %d %s, %d %s, %d 경로없음 Password 비밀번호 Pause 일시정지 Pax <%i> Mail <%i> 여객 <%i> 편지 <%i> PaxDest 목적지 Percent Electricity 전기 생산량 (%%): Planes are not available yet! 비행기는 아직 사용할 수 없습니다! Plant tree 나무 심기 player 플레이어 player -1 인간 플레이어 player 0 공공 서비스 player 1 Napik 128 AS player 10 플레이어 10 player 11 플레이어 11 player 12 플레이어 12 player 13 플레이어 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Freight Forwarder VM player 5 H-Trans Ltd player 6 PSK & Co KG player 7 플레이어 7 player 8 플레이어 8 player 9 플레이어 9 Please choose vehicles first\n 먼저 차량을 선택하세요!\n Post 편지 Postrate 편지 등급 Power 힘 Power (MW) 전력 (MW) Power: 힘: Power: %4d kW\n 전력: %4d kW\n Powerlines 송전선 Problems_msg 문제 Produced 생산 Production of %s has been stopped:\n%s\n %s의 생산이 중지되었습니다:\n%s\n Production/Boost 생산/증산 Produktion 생산 Profit 이익 promote to line 노선 생성 q1 봄 q2 여름 q3 가을 q4 겨울 Query server 쿼리 서버 rail car 철도 차량 random 무작위 Random age 무작위 연도 Random map 무작위 지도 Rathaus 시청 Rating 비율 ratio_pax 승객 비율 Reliefkarte 지도 Remove 제거 remove airstrips 활주로/유도로 철거 remove channels 수로 철거 remove interm. signals 중간 신호기 제거 remove maglev tracks 자기부상열차 선로 철거 remove monorails 모노레일 선로 철거 remove narrowgauge tracks 협궤선로 철거 remove powerlines 송전선 철거 remove roads 도로 철거 remove tracks 선로 철거 Remove wayobj %s 도로 오브젝트 %s를 제거 replace other signals 다른 신호기로 교체 replace stop 정류장 교체 request closing 리퀘스트 종료 residential house 주택 Restore natural slope 자연 경사면 복구 Restwert: 재판매 가격: Retire. date: 퇴역 날짜: return ticket 왕복운행 노선 Revenue 수입 Revision: 수정: road 도로 road vehicle 자동차 Roadsign 도로 표지판 Rotate map 지도 회전 Rotation 회전 Routing 여정 sack 가방 sail 바람 Saving map ... 지도를 저장 중... Scenario complete: %i%% 시나리오 완료: %i%% Scenario Debug 디버그 Scenario Error Log 스크립트 오류 Scenario Goal 목표 Scenario Info 설명 Scenario information 시나리오 세부정보 Scenario Result 성과 Scenario Rules 규칙 Schedule changing! 스케줄 교체! Schienentunnel 철도터널 건설 Schiff_tab 선박 Schiffdepot 조선소 Schleppkahn_tab 바지선 Screenshot 스크린샷 Seasons 계절 Sehenswuerdigkeit 관광명소 Select a server to join: 참여할 서버 고르기: Select a theme for display 디스플레이 테마 선택하기 Sell the selected vehicle(s) 선택한 차량을 팝니다. sended 보낸 편지 SEP_FRACTION . SEP_THOUSAND , SEP_THOUSAND_EXPONENT 3 September 9월 Server did not respond! 서버가 응답하지 않습니다. Serves Line: 노선 운영: Service 서비스 set signal spacing 신호간격 설정 Setting 설정 Ship 선박 shops and stores 상점과 사무실 Show all 모두 보기 show all building 모든 빌딩 보기 Show also vehicles no longer in production. 더 이상 생산되지 않는 차량도 보기 Show also vehicles that do not match for current action. 선택한 행동에 사용할 수 없는 차량도 보기 Show even servers with wrong version or pakset 종류를 알 수 없는 버전이나 팩 서버도 표시 show grid 격자 보기 Show industry 산업 목록 Show legend 범례 보기 Show map scale 색상 코드 Show obsolete 폐기된 것도 표시 Show offline 오프라인 서버 보기 Show schedules 스케줄 보기 Show servers that are offline 오프라인 서버 보기 Show servers where game version or pakset does not match your client 게임 버전이나 팩이 호환되지 않는 서버 보기 show station coverage 역 범위 보기 show station names 역 이름 보기 show waiting bars 대기 막대그래프 보기 show/hide block reservations 경로 예약 보기/숨기기 Show/hide estimated arrival times 추정 도착 시간 보기/숨기기 show/hide object owner 오브젝트 주인 보기/숨기기 Show/hide statistics 통계 보기/숨기 Shows buttons on special topics. 특정한 주제들을 보여줍니다. Shows consumer/suppliers for factories 공장의 공급자/소비자 보기 Shows the currently selected schedule 현재 선택된 스케줄 보기 Shrink city 도시 축소 shuffle midis 셔플 재생 Signal 신호기 signal spacing 신호 간격 Sim: 심루프: Similar view as the main window 주 게임창처럼 보이게 합니다. Size (%d MB): 파일크기 (%d MB): sliced underground mode 층 시점 slot empty 빈 슬롯 Smart hide objects 커서 주변 숨기기 Sort by 승객/화물 분류 Sort waiting list by 대기목록 분류 Sound 소리 Sound settings 소리 설정 Sound volume: 소리 볼륨: special freight 특별화물 Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. 속도 보너스: 도로 %i km/h, 철도 %i km/h\n선박 %i km/h, 항공기 %i km/h. Speedlimit 제한속도 Speichern 저장 Spieler 플레이어 Spieler(mz) 플레이어 Spielerliste 플레이어 목록 Spielstand wurde\ngeladen!\n \n게임 불러왔습니다.\n Spielstand wurde\ngespeichert!\n \n게임 저장했습니다.\n Sprache 언어 Sprachen 언어 Stadtinformation 도시 통계 Start 시작 Start the selected vehicle(s) 선택한 차량을 내보냅니다. Starte Spiel 게임 시작 Station tiles: 역 타일: Station_msg 역 Status 정지 상태 steam 증기 Step timeline one year 연도별 타임라인 Stops 정류장 Storage 재고량 Storage capacity 창고 용량 Strassendepot 차고지 Strassentunnel 도로터널 건설 street car 노면전차 sued 남쪽 suedost 남동쪽 suedwest 남서쪽 Summer snowline 여름 만년설 높이 Supplied: %.0f %% 공급량: %.0f %% Suppliers 공급자: Tage alt 일 전. There are still vehicles\nstored in this depot!\n 차량이 차고지에서\n대기중입니다!\n This Month 이번 달 This Year 이번 해: Tile not empty. 타일을 비운 뒤 경사면 도구를 사용하세요. timeline 시대에 따름 tl_title 모든 도시 목록 To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. %1$s에서의 교통량 증가: %3$i 주민들이 %2$s 건설 To heavy traffic\nresults in traffic jam.\n 많은 교통량이\n 교통혼잡을 유발합니다.\n Toggle day/night view 낮/밤 시점 토글 Toggle vehicle tooltips 차량 도구팁 토글 tonnen 톤 Total inhabitants: 총 주민수: Tourist attractions 관광명소: Tourists 관광지 Town_msg 새 행선지 Town: %s\n %s 시.\n Towns 도시 track 선로 Tracks 선로 Traffic 교통량 Train 기차 Trains are not available yet! 기차는 아직 사용할 수 없습니다. Tram 노면전차 tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. 노면전차 %i km/h, 모노레일 %i km/h\n자기부상열차 %i km/h, 협궤열차 %i km/h. tram_track 노면전차 선로 Tramdepot 노면전차 차량기지 Trams are not available yet! 노면전차는 아직 사용할 수 없습니다. Transferring game ... 게임 전송중... Transformer only next to factory! 변전소는 공장 옆의 빈 평지에 건설해주세요! Translation 번역 transparent station coverage 역 범위 반투명으로 보기 Transported 수송실적 TrolleyBus_tab 트롤리 버스 Truck 트럭 tt_Other 기타 Tunnel muss an\neinfachem\nHang beginnen!\n 터널은 곧은 경사면에서\n 시작해야 합니다!\n Tunnel must start on single way! 터널은 직선 도로에서 시작해야 합니다. Tunnelboden 터널 underground mode 지하 시점 UNDO failed! 되돌리기를 더이상 사용할 수 없습니다.\n신호등/역/정류장 등의 \n선로 위에 세워진 \n구조물이 없을 때에만\n되돌리기를 사용할 수 있습니다. Undo last ways construction 마지막으로 건설한 도로를 되돌리기 Unemployed 실업 Unhappy 불행 units/day 단위/개월  Update Line 노선 수정 upgrade HQ 본사 업그레이드 Usage: %.0f %% 사용량: %.0f %% Usage/Output 사용량/발전량 Use beginner mode 초보자 Use timeline start year 타임라인 시작년도 사용: Vehicle %s can't find a route! 차량 %s이\n경로를 찾을 수 없음! Vehicle %s is stucked! 차량 %s이 갇힘! Vehicle details 차량 상세 Verbrauch 소비 Vergroessere die Karte\n 지도 키우기.\n Verkauf 지금 제거! verkaufen 판매 Verkehrsteilnehmer 자가용 Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n 부채 상환 기한이\n %d개월 남았습니다.\n via 경유(세부사항) via %s\n %s를 경유\n via Menge 경유 (수량) voranstellen 맨앞에 넣기 Waggon_tab 자동차 waiting 대기수량 Waiting for clearance! 정리를 위한 대기! Walked 도보 walking 도보 Warnings_msg 교통량 Wasser 물 Water 운하 Water level 수면 높이: water vehicle 선박 way %s cannot longer used:\n %s 길 타입은 더 이상 사용할 수 없습니다.\n way %s cannot longer used:\n%s\n %s 길 타입은 더 이상 사용할 수 없습니다:\n%s\n\n way %s now available:\n %s 길 타입을 사용할 수 있습니다.\n Way toll 통행료 Ways not connected 길이 연결되지 않았습니다. Wegpunkt 웨이포인트 Weight: 무게: Wert 가격 west 서쪽 Winter snowline 겨울 만년설 높이 withdraw 퇴역 Withdraw All 모두 퇴역 WRONGSAVE \n호환되지 않는 세이브입니다.\n불러올 수 없습니다.\n Year %i has started. %i년에 시작. Years 년 Your primary color: 주 색깔: Your secondary color: 부 색깔: Zielort 행선지 zooming in 화면 확대 zooming out 화면 축소 Zu nah am Kartenrand 지도의 가장자리와\n가까운 지역에\n건설할 수 없습니다. #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. 자기부상열차 %2$s가 %1$.1f km/h로 세계 신기록 달성! New world record for monorails: %.1f km/h by %s. 모노레일 %2$s가 %1$.1f km/h로 세계 신기록 달성! New world record for motorcars: %.1f km/h by %s. 자동차 %2$s가 %1$.1f km/h로 세계 신기록 달성! New world record for narrowgauges: %.1f km/h by %s. 협궤기차 %2$s가 %1$.1f km/h로 세계 신기록 달성! New world record for planes: %.1f km/h by %s. 비행기 %2$s가 %1$.1f km/h로 세계 신기록 달성! New world record for railways: %.1f km/h by %s. 기차 %2$s가 %1$.1f km/h로 세계 신기록 달성! New world record for ship: %.1f km/h by %s. 선박 %2$s가 %1$.1f km/h로 세계 신기록 달성! #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL 인 &1_CITY_SYLL 성 &2_CITY_SYLL 동 &3_CITY_SYLL 주 &4_CITY_SYLL 촌 &5_CITY_SYLL 읍 &6_CITY_SYLL 양 &7_CITY_SYLL 전 &8_CITY_SYLL 구 &9_CITY_SYLL 령 &A_CITY_SYLL 교 &B_CITY_SYLL 림 &C_CITY_SYLL 산 &D_CITY_SYLL 원 &E_CITY_SYLL 정 %0_CITY_SYLL 송 %1_CITY_SYLL 영 %2_CITY_SYLL 산 %3_CITY_SYLL 안 %4_CITY_SYLL 양 %5_CITY_SYLL 평 %6_CITY_SYLL 노 %7_CITY_SYLL 합 %8_CITY_SYLL 병 %9_CITY_SYLL 원 %A_CITY_SYLL 구 %B_CITY_SYLL 산 %C_CITY_SYLL 신 %D_CITY_SYLL 점 %E_CITY_SYLL 천 %F_CITY_SYLL 동 1center %s %s 1extern %s 지선 %s 1suburb %s %s %s 2center %s 중앙 %s 2extern %s 외곽 %s 2suburb %s 전원구역 %s %s 3center %s 메인 %s 3extern %s 토지 %s 3suburb %s 마을 %s %s 4center %s 내부 %s 4extern %s 외부 %s 4suburb %s 촌락 %s 5center %s 시내 %s 5extern %s 환승센터 %s 5suburb %s 마을 %s 6center %s 허브 %s 6extern %s 교환 %s 6suburb %s 변두리 %s %s 7center %s 도시 %s 7extern %s 평원 %s 7suburb %s 공원 %s 8center %s 사업 %s 8extern %s %s 마진 %s 8suburb %s 위성 %s 9center %s 중심축 %s 9extern %s 우회 %s 9suburb %s 중간 %s simutrans-124.5/simutrans/text/ko/000077500000000000000000000000001520675460500172065ustar00rootroot00000000000000simutrans-124.5/simutrans/text/ko/airtools.txt000066400000000000000000000141361520675460500216100ustar00rootroot00000000000000항공 관련 도구

항공 관련 도구

항공 관련 도구 항공망을 형성하기 위한 도구를 포함합니다.

도구들은 생성 및 제거 작업을 시행합니다: 유도로나 항공 활주로, 에어스탑 (비행기와 공항을 오가며 화물이나 승객을 실고 내리는 데에 사용되어 진다), 격납고(항공기체들을 보관하고 관리하기 위한 것입니다.) 그리고 그리고 다수의 항공 관련 건축물을 포함합니다.

플레이를 하며 타임라인이 생성된다면, 시간이 지나면 Simutrans에서 더 많은 도구-옵션이 나타날 수 있습니다

게임 뷰 맨 위에 있는 비행기 아이콘을 클릭하여 도구 모음을 엽니다.

도구 모음을 열거나 클릭한 후 마우스 커서를 도구 옵션에 가져가서 이름과 적절한 위치를 확인합니다: 건설 비용, 유지보수 비용과 최대 속도 제한.

{팁: 간단한 공항을 만드는 방법:

i) 유도로(들)와 활주로(들)을 구축 및 연결합니다.;

ii) 유도로(들)의 끝에 에어스탑을 설치합니다.;

iii) 필요에 따라 공항 건물을 추가합니다.}

도구에는 다음 항목이 포함될 수 있습니다:

유도로: 이 도구는 항공기가 활주로에서 활주로로 이동할 수 있는 지상 이동 영역을 구축합니다. 유도로는 게임 뷰 기준 평지에 건설됩니다.

주의: 활주로가 활주로 끝에 연결되어 있는 경우 항공기는 이륙 및 착륙을 할 수 없습니다.

유도로 건설법: 도구를 클릭하여 선택 (커서를 유도로 변경합니다.), 그런 다음 지형을 클릭하여 시작점을 찾습니다. (게임 뷰에 불도저를 표시하고 디스플레이 오른쪽 하단에 좌표를 표시합니다.), 그리고 연결할 지형 또는 활주로를 클릭합니다.

{팁: 파괴/제거를 사용하여 유도로의 개별적으로 제거합니다. 되돌리고나면 [z] 건축 비용을 환불하지 않습니다.}

활주로: 이 도구는 항공기가 이륙과 착륙을 위해 사용하는 활주로를 만듭니다. 활주로는 게임 뷰의 평평한 지면에 세워지며 서로 교차할 수 있습니다.
{팁: 활주로 끝을 연결하지 않은 채로 남겨둡니다. 그렇지 않으면 항공기가 이륙하거나 착륙할 수 없습니다. 파괴/제거를 사용하여 유도로의 개별적으로 제거합니다. 되돌리고나면 [z] 건설 비용을 환불하지 않습니다.}

활주로 제거: 이 도구는 게임 뷰의 두 점 사이에,비행기가 없을 때, 유도로 또는 활주로 부분을 제거합니다. 이 도구는 사용시 비용이 발생합니다.
활주로 제거법: 도구를 클릭합니다. (커서를 빨간색 X자로 변경합니다.); 삭제할 활주로를 클릭합니다. (게임 뷰에서 빨간색 X표로 표시된 삭제 지점 선택); 그리고 마지막으로 스트립의 다른 지점에서 클릭하여 첫 번째 삭제 지점까지 부분을 제거합니다. {팁: 활주로 끝을 연결하지 않은 상태로 유지하지 않으면 비행기가 이륙하지 않거나 착륙할 수 없습니다. 활주로를 개별적으로 제거하려면 파괴/제거 사용하시면됩니다.}

항공기 격납고(차고, 차고지, 차량기지): 항공기 구매 및 관리를 위한 격납고 구축 도구입니다. 격납고는 유지 관리 비용이 발생하며, 유도로의 끝에 제작이 가능합니다.
격납고 제작법: 도구를 클릭하여 선택 (커서를 창고 모양으로 변경), 그리고 나서 유도로의 끝 부분을 클릭합니다.
{팁: 파괴/제거로 격납고 제거}

에어스탑&탑승교: 이 도구는 게임 뷰 내에 항공기가 승객이나 화물을 픽업 및 내릴 수 있도록 하는 에어스탑(들)을 만듭니다.
만약 새롭게 만들어진 에어스탑이 기존에 존재하는 에어스탑과 연결되지 않는다면, 에어스탑 이라는 새로운 에어스탑이 만들어 집니다.
에어스톱은 유도로 타일의 끝에 건설됩니다.; 그리고 에어스탑은 물품, 승객, 우편물의 유지보수 비용과 유역 구역(모이는 곳)을 가지고 있습니다. 항공기는 무료로 이용가능한 에어스탑이 있는 공항에만 착륙할 수 있습니다.
에어스탑 제작법: 도구를 클릭하여 선택한 다음 유도로의 끝을 클릭합니다.
{팁: 파괴/제거로 에어스탑을 제거합니다. [v] 키를 눌러 게임 뷰 내의 화물과 승객들을 위한 유역 구역을 보이거나 숨길 수 있습니다.}

공항건물: 이 도구는 공항(역, 정류장)을 위해 증축공사를 합니다. 이는 유지보수 비용과 화물과 승객을 위한 유역 구역과 수용량을 증가시킵니다. 일부 도구 옵션에서 아이콘(공항(역) 목록과 공항 정보에서 사용된다.) 은 확장을 통해 중지 작업을 수행할 수 있는 항목을 표시합니다.
확장 하는 법: 확장 선택을 위한 도구를 클릭합니다. (커서를 '확장'으로 변경), 그런 다음 게임 뷰 내에서 기존 공항 주변에 있는 확장이 필요한 위치를 클릭합니다. 새로 확장된 부분은 이제 공항의 일부분으로 간주됩니다.
{팁: 확장된 부분은 파괴/제거로 제거합니다. [v]키를 눌러 게임 뷰 내의 화물과 승객들을 위한 유역 구역을 보이거나 숨길 수 있습니다.}

simutrans-124.5/simutrans/text/ko/baum_build.txt000066400000000000000000000035661520675460500220640ustar00rootroot00000000000000나무 심기 도움말

나무 심기

나무 심기 창은 나무를 개별적으로 심는 것을 가능하게 합니다. 이 기능은 맵 커스터마이즈 기능으로, 당신이 공용서버 유저로 전환하는 경우 지도 편집 도구모음을 이용해 사용이 가능합니다.
팁: 이 도구 모음에는 한번에 숲을 추가할 수 있는 '숲 추가' 라는 버튼도 있습니다.

정보를 네 부분으로 나누어 설명 드리겠습니다.
· 왼쪽 상당에는 심을 수 있는 모든 종의 목록이 있습니다. 특정 나무에 대한 자세한 정보를 보려면 목록에서 항목을 클릭하십면됩니다. 나무를 배치하려면 목록에서 선택한 다음, 원하는 게임 월드 창에서 원하는 위치를 클릭하시면됩니다.
· 왼쪽 아래에 선택한 나무의 그림이 표시됩니다.
· 오른쪽 상단에서 일부 옵션이 설정할 수 있습니다.
· 옵션의 아래부분은 선택한 나무에 대한 정보입니다.

선택 목록

선택 목록에는 나무 유형이 식별되는 방법을 변경할 수 있는 두 개의 탭이 있습니다.
· 대체명: Paksets에 정의된 대로 지정된 언어로 트리 이름을 표시합니다. 변경된 이름이 없으면, 일반적인 개체 이름이 사용됩니다.
· 객체 고유명: Simutrans 내부 개체 이름을 표시합니다.

옵션들

· 기후 무시: 이 옵션은 다양한 나무 유형들과 관련된 기후 제약 기능을 비활성화합니다..
· 나이 랜덤: 심을 나무의 나이가 랜덤화됩니다. 보통은 가장 어린 나이대로 심어져 자라며, 꾀 오랜 세월 동안 살아있습니다.

simutrans-124.5/simutrans/text/ko/citybuilding_build.txt000066400000000000000000000046051520675460500236210ustar00rootroot00000000000000도시 건물 건설

도시 건물 건설

네 부분으로 나눠서 설명합니다.
· 왼쪽 상단은 건설 가능한 도시 건물들의 선택 목록입니다.
· 왼쪽 하단에 선택한 도시 건물의 사진이 표시됩니다.
· 오른쪽 상단에서 일부 옵션을 설정할 수 있습니다.
· 오른쪽 하단은 선택한 도시 건물에 대한 정보입니다.

선택 목록

선택 목록은 사용 가능한 모든 도시 건물을 보이며, 오른쪽 상단에 사용 가능한 옵션도 표시합니다. (주택, 상점과 사무실, 공업용 건물). 도시 건물의 식별 방법을 변경할 수 있는 탭이 두 가지가 있습니다.
· 대체명: 설정된 언어로 건물 이름을 표시합니다. 설정된 언어에 대체할만한 것이 없는 경우, 개체 본연의 이름이 사용됩니다.
· 고유 개체명: Simutrans에서 기존에 설정한 건물 유형에 따른 고유 개체명을 표시합니다.

색상이 들어간 글은 각자 특별한 의미를 가집니다:
· 파란글 주택을 나타냅니다.
· 초록글 상점과 사무실을 나타냅니다.
· 검은색 글 공업용 건물을 나타냅니다.

옵션들

· 기후 무시: 이 옵션은 다양한 건물 유형과 관련된 기후 제약 기능을 비활성화합니다.
· 사용 연식: 선택 목록에는 현재 Simutrans의 연도를 사용할 수 있는 건물만 포함됩니다.
· 구형 항목 표시: 선택 목록을 확장하여 현재 Simutrans의 연도가 더 이상 사용되지 않는 건물도 포함합니다..
· 주택: 선택 목록에 거주지가 있을 것입니다.
· 상점과 사무실: 선택 목록에 상점과 사무실이 있을 것입니다.
· 공업용 건물: 선택 목록에 공업용 건물이 있을 것입니다.
· 회전: 선택한 건물을 각기 다른 각도에서 볼 수 있습니다. 그로인해 해당 위치에서 특정각도를 선택 가능합니다. 왼쪽 하단에 있는 이미지가 이를 반영하도록 변경됩니다. 이 옵션을 랜덤으로 설정하면 건물의 랜덤 보기가 사용됩니다.

simutrans-124.5/simutrans/text/ko/citylist_filter.txt000066400000000000000000000036241520675460500231650ustar00rootroot00000000000000도시 목록

도시 목록

도시 목록에서는 도시지역(시골, 마을, 도시)과 인구 등에 대한 정보를 볼 수 있습니다.

여는 방법: 목록 관리 안의 도시 목록 아이콘이나 [Shift+T]키를 누르면 됩니다.
도시 목록은 도시의 순서를 바꾸는 2개의 옵션 버튼이 있습니다.
{팁: 목록이 일부분만 보인다면, 슬라이드 바를 이용하여 도시 목록의 크기를재조정하거나 바꾸세요. }

총인구: 는 게임 내의 전체 인구(모든 도시지역의 총인구 수)입니다. 최근의 인구 증가는 괄호 안에 표시됩니다.

분류: 에는 도시지역의 순서를 결정하는 옵션이 있습니다.
옵션 버튼을 클릭하면 다른 옵션이 나옵니다.

- 이름은 도시이름을 아스키 코드(ASCII-code) 순서대로 배열합니다. (대문자가 소문자보다 먼저 나옴)
- 주민은 인구수 순서대로 배열합니다.
- 도시 성장은 발전속도 순서대로 배열합니다.(발전속도는 도시지역의 크기와 수송되는 승객수에 따라 결정됩니다.)
- 오름차순 / 내림차순 순서를 반전시킵니다.

도시 목록의 도시 이름을 클릭하면, 그 도시지역에 대한 더 많은 정보를 볼 수 있습니다.
목록에는 다음과 같은 정보가 표시됩니다:

도시명 그 도시지역의 이름 (도시 정보에서 바꿀 수 잇습니다)..

인구 그 도시지역의 인구. 괄호 안의 것은 최근의 발전속도.

simutrans-124.5/simutrans/text/ko/citywindow.txt000066400000000000000000000115521520675460500221530ustar00rootroot00000000000000도시 통계 도움말

도시 통계

도시 통계는 선택한 도시지역의 통계를 보여주며, 도시 통계를 이용하여 그 도시의 이름을 바꿀 수 있습니다. 도시지역은 도시, 마을 또는 시골입니다. 아래에 쓰인 도시라는 단어는 도시지역 3개 모두 해당합니다.

각 도시는 시청이 있으며, 그 주변에서 성장합니다. 승객과 우편의 이동으로 성장합니다. 도시의 성장은 도로의 건설, 공공 서비스 플레이어의 도로 인수, 새 도시 건물과 새 산업의 등장과 도시 경계 확장으로 나타납니다.

도시 통계는 시청을 점검 도구로 클릭하거나 도시 목록에서 도시를 클릭하면 나옵니다. 여기서 도시의 이름을 바꿀 수 있으며, 몇몇 통계, 두 개의 미니맵과 도시의 발전을 시간 경과에 따라 보여주는 그래프를 볼 수 있습니다.

이름: 은 도시에 부여된 이름이며, 도시 통계 제일 위 상자에 나타납니다.
도시지역의 이름은 도시 경계 내의 이름에 쓰입니다. 도시 이름을 보여주는 상자를 클릭하여 이름을 바꿀 수 있습니다.
{팁: [!] 버튼으로 시청 위에 나타나는 고시 이름의 표현 방식을 바꿀 수 있습니다. 도시 이름의 생성 방식에 대해 더 자세히 알고 싶으시다면 ...simutrans/text/ 안의 readme_citylist.txt를 참고하세요.}

이름 상자 밑에는 다음과 같은 기본적인 도시 통계가 있습니다.:
도시 크기: 도시의 인구입니다. 괄호 안의 숫자는 최근의 인구 증가를 의미합니다.
시가지: 도시 건물(주거/상업/공업)의 수
도시 좌표: 도시의 경계를 의미합니다. 이는 도시가 성장하면서 바뀝니다. 앞쪽의 좌표는 경계의 자상귀 좌표이며, 뒤쪽은 우하귀입니다. 여러 도시가 성장하면서 도시경계가 곂칠수도 있습니다.
실업자: 와 노숙자: 는 현재 도시에 살고 있는 실업자와 노숙자의 수입니다. 이들은 도시 경계가 커지면서 증가하며, 도시에 새 건물이 들어서면 감소합니다. 이들의 균형에 따라 어떤 유형의 도시 건물(주거/상업/공업)이 들어설지 결정됩니다.

도시성장 허용 버튼은 도시 성장을 가능하게 하거나 막게 할 수 있습니다.

도시 통계에서의 미니맵은 승객과 우편(색칠된 점으로 보여짐)의 목적지를 보여줍니다.
오른쪽 미니맵은 게임상 현재 달의 값을, 왼쪽은 저번 달의 값을 보여줍니다. 각 점들의 색깔은 그 승객 또는 우편이 목적지에 도달했는지, 안했는지를 나타냅니다.
- 노란색 점: 경로가 있으며, 붐비지 않는 (역 정보에서 행복한 얼굴로 표시됨)에서 여정을 시작함
- 오랜지색 점: 목적지로 가는 경로가 없음 - 빨간색 점: 목적지로 가는 경로가 있지만, 붐비는 역(역 정보에서 불행한 얼굴로 표시됨)에서 여정을 시작함

미니맵 아래에서는 도시의 통계를 보여주는 그래프가 있습니다.
y축(세로축)은 양을 x축(가로축)은 시간을 의미합니다. 그래프 위쪽의 버튼으로 x축의 단위를 바꿀 수 있습니다.:
년: 각 년도로 보여줌
달: 12개월 단위로 보여줌

그래프 옵션 버튼을 부르면, 각 버튼에 해당하는 통계가 그래프로 나타나며, 눌린 것으로 표시됩니다.
버튼의 색깔과 그에 해당하는 그래프의 색깔은 같습니다.:
주민: 도시에 살고 있는 주민의 수
성장: 인구 변화 (도시 크기와 승객/우편에 제공되는 교통 서비스에 따라 변화함)
건물 수: 도시에 있는 건물의 수
자가용: 도시에서 생성된 자가용의 수
통근: 교통 네트워크를 이용하는 승객의 수 (역 정보에서 행복한 얼굴로 표시됨)
승객: 도시에서 생성되는 승객의 수
보내진 우편물: 교통 네트워크를 이용한 우편물의 수
우편물: 도시에서 생성되는 우편물의 수
공급: 공장에 보관된 물품이 있었던 횟수
물품: 공장에서 생산한 물품 수

{팁: simuconf.tab와 cityrules.tab에서 도시와 관련된 설정을 바꿀 수 있습니다.}

simutrans-124.5/simutrans/text/ko/climates.txt000066400000000000000000000074031520675460500215540ustar00rootroot00000000000000풍경 설정 도움말

풍경 설정

풍경 설정 창은 게임 세계의 지형 및 기상 측면에 대한 옵션을 설정합니다. 새 게임 만들기 창과 함께 열립니다.

각 설정을 조정하려면 각 옵션의 옆에 있는 화살표 버튼을 클릭하거나 상자에 숫자를 입력하십시오.

지도 옵션

수위 - 지도에서 물의 높이를 설정하십시오 (기본값 -2). 값이 높을수록 육지가 적고 수역이 많아집니다.

산의 높이 - 지형의 최대 높이를 설정하십시오.

지도의 조도 - 지형의 조도를 설정하십시오. 숫자가 클수록 해안 지역에서 더 많은 기복을 만듭니다 (더 사실적으로 보입니다). 매우 거친지도는 의도했던만큼 많은 도시, 산업 또는 도로를 생성하지 못할 수도 있습니다.

경고: 높이 레벨이 33보다 커야합니다.(플레이어가 접근할 수 없습니다). 필요한 경우수위를 낮추면 더 많은 지면 레벨을 생성할 수 있습니다.

스노우 라인 옵션
스노우 라인은 눈이 나타나는 지도 상의 높이입니다. 1년 동안 Simutrans의 스노우 라인은 다음 설정에 따라 다릅니다.

여름 스노우 라인 - 여름철의 스노우 라인(영원한 눈). 이 값은 기후 옵션에서 선택된 가장 높은 지형 값입니다. 북극 기후는 여기에서 시작됩니다.

겨울 스노우 라인 - 겨울철 스노우 라인의 최소 높이를 설정하십시오. 지도 상의 넓은 지역에 눈이 나타나게 하려면 이 값을 낮게 유지하십시오.

기후 옵션
새 지도의 다른 기후 유형을 설정하십시오.

각 기후 유형 옆의 값은 지형지도의 높이를 기후가 나타나는 곳까지 설정합니다. 더 높은 기후대는 낮은 지역의 더 낮은 기후대로 덮여있습니다. 특정 기후를 끄려면 목록에서 더 높은 기후 (사막의 경우 수위)와 동일한 값을 설정하십시오.

참고: 건물, 산업 그리고 식물은 제작자에 의해 특정 기후를 할당받았습니다.

사막 기후 - 매우 적은 강수량, 극심한 기온. 매우 적은 나무. 평평한 풍경.

열대 기후 - 적도 부근의 건조하지 않은 지역의 전형적인 해수면의 일정한 고온 및 낮은 고도.

지중해성 기후 - 적당히 변하는 습한 날씨.

온화한 기후 - 열대와 극관 사이의 지역에서 발견되어 날씨가 변합니다.

툰드라 기후 - 낮은 기온, 극관의 가장자리.

고산(바위 투성이의) 기후 - 건조한 기후, 추운 겨울.

다른 옵션

나무가 없음 - simutrans가 나무를 그릴 수 없도록 하려면 이 옵션을 사용하십시오. 나무는 렌더링에 CPU 시간을 소비합니다. CPU 속도가 느리거나 RAM이 적은 PC에서는 이 옵션을 사용하십시오.

호수 - simutrans가 지표면의 일부 피트를 물로 채워 지도 전체에서 호수를 만드려면 이 옵션을 사용하십시오.

하천의 수 - 지형지도에서 생성되고 사용될 수 있는 하천의 최대 수를 설정하십시오.

최소. 하천의 길이 - 지도 상의 하천의 최소 길이를 설정하십시오.(타일을 통해서)

최대. 하천의 길이 - 지도 상의 하천의 최대 길이를 설정하십시오.(타일을 통해서)

simutrans-124.5/simutrans/text/ko/color.txt000066400000000000000000000017261520675460500210730ustar00rootroot00000000000000플레이어 색상 도움말

플레이어 색상

플레이어 색상에서는 여러분의 회사 색깔을 고를 수 있습니다.
차량, 건물 등에 회사 색깔로 색칠됩니다.

플레이어 색상게임 옵션에서 열 수 있습니다.

색상 버튼을 누르면, 회사 색깔이 바뀝니다.
새 색상을 골랐다는 것을 보여주기 위해, 제목 표시줄의 색깔도 바뀝니다.

기본 인간 플레이어의 색상은 밝은 파란색입니다.
몇몇 것들은 색상 코드가 없거나 선택한 색깔로 보여지지 않을 수 있습니다.

{팁: AI 플레이어의 색상을 바꾸려면, 플레이어 목록이나 [Shift+P]를 눌러 플레이어를 바꾼 뒤 플레이어 색상에서 바꾸시면 됩니다.}

simutrans-124.5/simutrans/text/ko/convoi.txt000066400000000000000000000071501520675460500212470ustar00rootroot00000000000000차량 목록

차량 목록

차량 목록은 정보를 보여주며 다른 차량 (수송수단은 운용가능한 차량 또는 차량 단위의 조합으로 구성됩니다)을 필터를 관리하고 나열합니다.

열려면: 목록 관리에서 차량 목록 도구를 클릭하거나 [V]를 누릅니다. 차량 목록에는 4개의 옵션 버튼이 있습니다: 두 개는 목록의 항목을 주문하는 데 사용되며 두 개는 목록에 표시된 항목의 설정을 변경하는 데 사용됩니다.
아래 버튼은 필터 옵션의 기준에 맞는 수송수단 목록입니다.
{팁: 만약 수송수단이 나열되지 않는다면 필터 옵션을 변경하십시오. 만약 목록에 있는 항목이 부분적으로만 보인다면 슬라이더 바를 사용하여 차량 목록 또는 스크롤 목록의 크기를 조정하십시오.}

옵션을 순환하거나(옵션 버튼의 이름 변경) 필터 설정 관리를 열려면 옵션 버튼을 클릭하십시오:

정렬 기준: 두 개의 옵션 버튼은 목록에 표시된 차량의 순서를 결정합니다.

- 유형 수송의 유형에 따른 순서(도로 차량, 철도 차량, 보트 및 항공기의 오름차순)
- 이름 ASCII 코드 순서 내에서 알파벳 순으로 할당된 이름에 따른 순서: 소문자 앞의 대문자(기본적으로 이것은 수송수단으로 매입 또는 조립된 최초의 차량 단위입니다).
- 수입 수익에 의한 순서(생성된 수입 - 운영 비용).
- 내부 아이디 수송수단의 고유 아이디 번호에 의한 순서(수송수단이 처음 매입됐거나 차고에서 조립됐을 때 기본적으로 할당되고 수송수단 정보의 제목 바에 표시됩니다).

- 오름차순 / 내림차순은 목록의 순서를 바꿉니다.

필터: 두 개의 옵션 버튼, 기준을 변경하기위한 목록 및 접근 관리에 대한 선택 기준을 켜고 끕니다.

- 활성화 / 비활성화 클릭해서 목록에서 수송수단에 대한 필터 기준을 전환합니다.

- 설정에서 관리 창을 열어 필터 기준을 변경합니다.

해당 차량에 대한 더 많은 정보를 보려면 나열된 항목을 클릭하십시오.

각 수송수단에 대해 나열된 항목은 포함합니다:

내부 아이디: 수송수단의 고유 아이디 번호(수송수단이 처음으로 매입됐거나 차고에서 조립됐을 때 기본적으로 할당되고 차량 정보의 제목 바에 표시됩니다).

할당된 이름(기본적으로 이것은 수송수단이 구매됐거나 조립됐을 때 첫번째 차량이 됩니다).

수입은 수익을 보여줍니다.(생성된 수입 - 발생한 운영 비용).

라인: 할당된 라인을 표시하고 수송수단이 차고에 있는지를 나타냅니다.

그래픽은 구성과 현재 부하 레벨을 보여줍니다.

simutrans-124.5/simutrans/text/ko/convoi_filter.txt000066400000000000000000000045451520675460500226210ustar00rootroot00000000000000차량 목록 필터

차량 목록 필터

차량 목록 필터에는 차량 목록에 표시된 수송수단을 결정하는 옵션이 있습니다.

차량 목록 필터를 열려면 차량 목록의 설정을 클릭하십시오.

옵션 버튼은 필터 옵션이 활성화됐을 때 차량 목록에 나열될 수송수단을 선택합니다. 모든 기준 세트에 맞는 수송수단만 나열됩니다.

필터 기준을 설정하려면 정사각형 버튼을 클릭하십시오 (선택됐을 때 버튼이 들여쓰기됩니다):

필터 이름: 이름으로 차량을 선택합니다. 사용하려면: 버튼을 클릭하여 선택하고 이름 상자를 클릭한 다음 필요한 이름을 정확하게 입력하십시오(옵션은 대소문자를 구분합니다).

필터 유형: 선택된다면 수송 유형에 따라 수송수단을 나열합니다. 사용하려면: 버튼을 클릭하여 선택하고, 운송 유형: (도로, 철도, 물 및 공기)을 클릭하십시오.

특수 필터: 차량을 선택하여 다음 기준에 따라 나열합니다(버튼을 클릭하여 선택하고 클릭합니다):
- 경로 없음 - 다음 목적지에 대한 경로를 찾을 수 없습니다.
- 일정 없음 - 경로가 할당되지 않았습니다.
- 수입 없음 - 수익을 생성하지 못합니다.
- 차고에 - 차고에 있는 것들
- 노선 없음 - 노선이 할당되지 않았습니다.

화물 필터: 다른 화물 및 승객을 수송할 수 있는 능력에 따라 수송수단을 선택합니다. 항목을 선택하려면 이름 옆에 버튼을 클릭하십시오. 목록에서 항목을 스크롤하려면 슬라이더 바를 사용하십시오. 옵션에는 다음이 포함됩니다:
- all은 모든 항목을 선택합니다.
- none은 어떠한 항목도 선택되지 않습니다.
- inv.은 현재 항목의 선택을 반대로 합니다.

simutrans-124.5/simutrans/text/ko/convoidetail.txt000066400000000000000000000042741520675460500224360ustar00rootroot00000000000000수송수단 세부 사항

차량 세부 사항

수송수단 세부 사항 수송수단 세부 정보는 수송수단(운용가능한 차량 또는 차량 단위의 조합)에 대한 더 많은 정보를 제공하고 즉시 수송수단을 판매하는 데 사용될 수 있습니다.

수송수단 세부 사항은 수송수단 정보의 세부 사항 옵션 버튼에서 열 수 있습니다.
만약 모든 차량 단위가 보이지 않으면 수송수단 세부 사항조정하거나 스크롤할 수 있도록 슬라이더 바를 사용하십시오.

수송수단 세부 사항의 맨 위에는 다음과 같은 정보가 포함됩니다:

동력: 수송수단의(수송수단의 모든 차량 단위의 결합된 동력)

재판매 가치: 수송수단이 판매될 경우 받을 금액(수송수단의 매입 가격에서 감가 견적을 제외한 금액)

경고: 수송수단을 즉시 판매하려면 판매모드 옵션 버튼을 클릭하십시오. 더이상의 확인은 필요하지 않습니다. 수송수단은 게임에서 제거되고 플레이어 잔액은 판매 가격에 맞게 적립됩니다.

수송수단 세부 사항은 수송수단의 모든 차량 단위를 나열합니다.
각 차량 단위에 대해 나열된 정보에는 다음이 포함됩니다:
- 차량 단위 그래픽: 차량 단위의 사진(차고에서도 사용됩니다.)
- 제조: 제조 년월, 타임라인을 사용하는 경우 소개 날짜
- 재판매 가치: 판매할 경우 받는 금액
- 동력: 생성된 힘(전동식의 차량 단위에 의해)
- 마찰: 현재 마찰 레벨
- 최대 수입: 이 수송수단으로 수송되는 항목의 유형에 기반을 둔 차량 단위의 최대 가능 수입
- 탑재된 항목 이 차량 단위에 의해 현재 운반되는 항목의 수량과 목적지

simutrans-124.5/simutrans/text/ko/convoiinfo.txt000066400000000000000000000202521520675460500221210ustar00rootroot00000000000000수송수단 정보

수송수단 정보

수송수단 정보는 화물과 승객을 수송하는데 사용되는 수송수단(운용가능한 차량 또는 차량 단위의 조합)에 대한 정보를 제공합니다.
여기에는 수송수단에 의해 운반된 항목들을 나열하며 추가 세부 사항에 접근해서 관리하고 수송수단을 판매할 수 있습니다; 게임 뷰에서 수송수단을 따라가십시오; 그리고 수송수단의 이름과 스케쥴을 변경하십시오.

수송수단은 적어도 하나의 동력(전동식의)을 이용한 차량 단위와 연결된 트레일러/객차로 구성되었습니다.
수송수단의 예에는 다음이 포함됩니다: 마차를 끄는 말의 무리; 또는 탄수차가 연결된 기관차와 객차; 또는 버스; 또는 밴; 또는 트레일러가 연결된 트럭; 또는 연결된 노면전차; 또는 배; 또는 바지선을 끄는 예인선; 또는 항공기.
수송수단은 수송 유형에 따라 차고에서 조립 및 구매됩니다.
{팁: 수송수단은 구성에 있어서 차량 단위의 수에 최대 한도가 있습니다: 기차, 24대; 도로 차량, 4대.}

점검 도구로 게임 보기에서 수송수단을 클릭하거나 수송수단 정보를 열려면 차량 목록 또는 라인 관리에 나열된 수송수단을 클릭하십시오.
수송수단 정보의 제목 바는 수송수단의 이름과 고유 아이디 번호를 보여줍니다(자세한 내용은 아래를 참고하십시오).
수송수단 정보에는 이름 상자가 있습니다; 작은 보기; 수송수단에 대한 정보; 옵션 버튼; 현재 운반된 모든 항목의 목록(만약 모든 항목이 보이지 않는다면 수송수단 정보재조정하거나 스크롤할 수 있도록 슬라이더 바를 사용하십시오).

수송수단 정보의 맨 위에 표시된 정보에는 다음이 포함됩니다:

내부 아이디: 수송수단에 대한 고유 아이디 번호(수송수단이 차고에서 처음 매입되거나 조립되었을 때 기본적으로 할당됩니다).

- 이름: 수송수단의 할당된 이름을 보여줍니다(기본적으로 이것은 수송수단을 위한 차고에서 매입 또는 조립된 최초의 차량 단위입니다).
수송수단의 이름을 변경하려면 이름 상자를 클릭하고 새로운 이름을 입력하십시오.

- 속도: 현재 속도는 km/h로 표시됩니다.
수송수단의 최대 가능 속도는 괄호 안에 표시됩니다(수송수단에서 가장 느린 차량 단위에 의해 결정됩니다).
초록색 진행 바는 최대 속도와 관련하여 현재 속도를 나타냅니다.

- 수입: 현재 게임의 일정의 연도에서 차량(수입으로 인해 운영 비용이 감소)에 의해 만들어진 수익.
게임 당 Hajo Credit에 포함된 운송수단의 운영 비용은 괄호 안에 표시됩니다.

- 중량: 수송수단의 현재 합산된 중량과 수송수단에 의해 운반된 항목(톤).
운반된 항목의 현재 중량은 괄호 안에 표시됩니다.
초록색 진행 바는 화물 및 승객을 운반하는 데 사용된 용량의 비율을 나타냅니다.
노란색 바는 일정 관리에 설정된 대기 레벨(운송수단이 진행하는데 필요한 화물 및 승객의 최소량)을 나타냅니다.
{팁: 라인의 모든 수송수단에 대한 대기 레벨을 설정하거나 변경하려면 라인 관리 또는 차고 관리에서 라인 업데이트 옵션 버튼을 사용하십시오. 수송수단에 대한 대기 레벨 및 할당된 라인에 다른 수송수단을 설정하거나 변경하려면 수송수단 정보 또는 차고 관리에서 일정 옵션 버튼을 사용하십시오.}

- 목적지: 수송수단의 일정에 있는 다음 정거장 또는 경유지.
초록색 진행 바는 다음 목적지까지에 대한 수송수단의 진행 상황을 나타냅니다.

- 서브 라인: 이 수송수단에 할당된 노선. 삼각형은 노선 관리창에서 라인을 엽니다. (노선이 할당된 경우에만 볼 수 있습니다.)

- 작은 보기는 이러한 수송수단을 보여줍니다. 이 수송수단의 게임 보기를 중앙에 배치하려면 작은 보기를 클릭하십시오.

관리 창을 열거나 옵션을 선택하려면 수송수단 정보의 버튼을 클릭하십시오:

일정: 경로와 수송수단의 진행에 필요한 화물 및 승객의 최소량을 변경하려면 관리 창을 엽니다.

차고로 수송: 수송수단을 가장 가까우면서 적절한 차고에 보냅니다. 차고로 수송하는 데에 대한 수입은 얻게되지만 운반되는 모든 항목은 분실됩니다.
차고에서 수송수단은 일정과 할당된 라인을 유지합니다.

다음을 따라하십시오: 들여쓰기됐을 때 버튼은 수송수단과 함께 게임 보기로 이동합니다.
이 옵션의 선택을 취소하려면, 버튼을 다시 클릭하거나 메인 지도 상에서 왼쪽 버튼을 클릭하십시오.

차트: 수송수단 정보에서 그래프(버튼은 그래프가 보이면 들여쓰기됩니다)를 전환하려면 옵션 버튼을 클릭하십시오.
그래프는 그래프 옵션이 선택되었을 때 지난 12개월(x축)에 대한 통계를 보여줍니다.
그래프에서 선택된 정보를 보려면 그래프 옵션 버튼을 클릭하십시오(버튼은 옵션이 선택되면 들여쓰기됩니다).
그래프에서 라인의 색상은 그래프 옵션 버튼의 색상과 일치합니다:
- 여유 용량은 수송수단에서 미사용 공간을 나타냅니다.
- 운송량은 화물, 승객 및 운반된 우편의 수를 나타냅니다.
- 이익은 수송에 의해 생성된 수입을 나타냅니다.
- 운영 비용은 수송 중에 수송수단에 의해 발생하는 비용을 나타냅니다.
- 수익은 수송으로 얻은 수익을 나타냅니다 (운영 비용 감소).

세부 사항은 즉시 수송수단을 판매하는 옵션과 추가 정보가 포함된 수송수단 세부사항을 엽니다.

승객/ 화물 정렬 기준: 수송수단에 의해 현재 운반된 항목을 나열합니다.
표시된 정보에는 운반된 양, 수송수단의 한도, 항목의 유형, 최종 목적지 및 다음 환승 정거장이 포함됩니다.
옵션 버튼(선택에 따라 이름이 변경됩니다)은 운반된 항목(승객, 우편 및 화물)의 목록을 정렬합니다:
- 목적지: ASCII 코드 순서에 따라 알파벳 순으로, 최종 목적지 정거장의 할당된 이름에 따라 항목을 정렬합니다 (소문자 앞의 대문자).
- 경유 (세부 사항): ASCII 코드 순서에 따라 알파벳 순으로, 첫번째 환승 정거장의 할당된 이름에 따라 운반된 항목을 정렬합니다 (소문자 앞의 대문자).
- 경유 (수량): 첫번째 환승 정거장으로 향하는 수량에 따라 운반된 항목을 정렬합니다.
- 수량: 내림차순으로 수량에 따라 운반된 항목을 정렬합니다.

simutrans-124.5/simutrans/text/ko/curiosity_build.txt000066400000000000000000000045021520675460500231610ustar00rootroot00000000000000매력적인 건물 건설

매력적인 건물 건설

다이얼로그는 4개의 부분으로 나뉩니다.
- 왼쪽 상단은 사용 가능한 건물의 선택 목록입니다
- 왼쪽 하단은 선택된 건물의 사진이 표시됩니다
- 오른쪽 상단은 몇 가지 옵션을 설정할 수 있습니다
- 오른쪽 하단은 선택된 도시 건물에 대한 정보입니다

선택 목록

선택 목록에는 오른쪽 상단에 명시된 옵션으로 사용 가능한 모든 특수 건물과 랜드마크가 제공됩니다. (도시 명소, 육지 명소, 기념물). 매력적인 건물을 식별하는 방법을 변경할 수 있는 2개의 탭이 있습니다.
- 번역: 주어진 언어로 건물의 이름을 보여줍니다. 만약 주어진 언어에서 번역할 수 없으면, 객체 이름이 사용됩니다.
- 객체: 건물 유형에 대한 Simutrans 내부 객체 이름을 보여줍니다.

텍스트의 색상은 다음과 같은 의미를 가집니다:
- 파란색 텍스트 명소는 도시에 건설되어야 합니다
- 초록색 텍스트 명소는 시골에 건설되어야 합니다
- 검정색 텍스트 기념물: 한 번만 건설할 수 있습니다

옵션

- 기후를 무시: 이 옵션은 다양한 건물 유형과 관련된 기후 제한을 비활성화합니다.
- 타임라인 연도 사용: 선택 목록에는 현재 Simutrans 연도에서 사용할 수 있는 건물만 포함됩니다.
- 쓸모없는 부분도 표시: 현재 simutrans 연도에서 쓸모없는 건물도 포함하려면 선택 목록을 확장하십시오.
- 도시 명소: 선택 목록에 도시 명소가 있습니다.
- 육지 명소: 선택 목록에 육지 명소가 있습니다.
- 기념물: 선택 목록에 기념물이 있습니다.
- 회전: 선택한 건물을 다른 각도에서 볼 수 있다면, 여기에서 특정한 보기를 선택할 수 있습니다. 이것을 반영하기 위해 왼쪽 하단의 이미지가 변경됩니다. 만약 이 옵션을 임의로 설정한다면 건물의 임의 보기가 사용됩니다.

simutrans-124.5/simutrans/text/ko/curiositylist_filter.txt000066400000000000000000000046661520675460500242560ustar00rootroot00000000000000명소 목록

명소 목록

명소 목록에는 승객과 우편의 목적지 및 출발지인 모든 관광 명소에 대한 정보가 있습니다.

명소 목록을 열려면: 목록 관리에서 명소 목록 도구를 클릭하십시오.
{팁: 만약 목록에 있는 항목이 부분적으로만 표시된다면 슬라이더 바를 사용해서 명소 목록 또는 스크롤 목록을 재조정하십시오.}

정렬 기준: 표시된 관광 명소의 순서를 결정할 수 있는 옵션이 있습니다.
옵션을 통해 순환하려면 버튼을 클릭하십시오 (옵션 버튼의 이름을 변경합니다):

- 이름 알파벳 순서에 의한 이름 순서.
- 승객 수준 승객과 우편에 대한 목적지로서 상대적 인기 순서.

- 오름차순 / 내림차순 목록의 순서를 뒤집습니다.

해당 관광 명소에 대한 자세한 정보를 보려면 명소 목록에 나열된 항목을 클릭하십시오.
{팁: 해당 명소로 게임 보기를 이동하려면 자세한 정보에서 관광 명소의 사진을 클릭하십시오.}

각 관광 명소에 대해 나열된 항목에는 다음이 포함됩니다:

색상-상태 바 색상은 명소의 상태를 나타냅니다:
- 노란색: 정거장(역)의 통행 가능 지역에서 내릴 수 없습니다.
- 초록색: 승객에 대한 정거장과 연관됐습니다.
- 파란색: 우편에 대한 정거장과 연관됐습니다.
- 청록색: 승객과 우편에 대한 정거장과 연관됐습니다.
- 주황색: 연관된 정거장은 혼잡합니다.
- 빨간색: 모든 연관된 정거장은 혼잡합니다.

건물 그래픽은 그 곳이 도시 명소인지를 나타냅니다.

관광 명소의 이름

대괄호 안에 승객 레벨은 승객과 우편에 대한 목적지로서의 상대적 인기를 나타내는 값입니다.

{팁: 최적의 서비스를 제공하기 위해서 전체 관광 명소는 하나 또는 여러 개의 정거장의 통행 가능 지역에 있어야 합니다.}

simutrans-124.5/simutrans/text/ko/depot.txt000066400000000000000000000115231520675460500210640ustar00rootroot00000000000000차고지, 차량기지, 조선소, 격납고 도움말

차고지

* 이하 차고지로 통일하여 서술합니다.

차고지에서는 차량을 구입하거나 관리할 수 있습니다. 서로다른 운송수단마다 각자 차고지가 있습니다. 전기 철도 차량의 경우에는 전기철도 차고지에서만 나타납니다. 차고지는 각 운송수단 툴바에서 찾을 수 있으며, 순서대로 철도 차량기지, 모노레일 차량기지, 트램 차량기지, 도로 차고지, 선박 정박소, 비행기 격납고입니다. 또한, 타임라인 시작년도 설정을 활성화한 경우, 차량의 등장년도가 되기 전에는 차량이 나타나지 않습니다.

게임 창에서 조사 도구로 차고지를 클릭하면, 차고지 관리 창이 뜨며, 여기서 차량들의 정보를 얻거나, 구입, 관리할 수 있습니다.
타이틀 바는 이 차고지가 어느 종류의 운송수단 것인지를 알려줍니다.
차고지 관리 창은 크기를 조절할 수 있으며, 타이틀바의 아래쪽 방향 화살표를 누르면 원래 크기로 돌아갑니다.
타이틀바의 왼쪽/오른쪽 화살표를 누르면, 같은 종류의, 다른 차고지로 갈 수 있습니다.

차고지 관리 창 윗 부분에는 다음과 같은 정보가 나타납니디.

차량 X 중 Y : 차고지에 유치된 차량 중 현재 차량(편성)의 순번을 나타냅니다.
차량 이름: 옆의 왼쪽/오른쪽 화살표를 누르면, 이 차량의 앞/뒤 차량으로 이동합니다. 이름 상자를 눌러 차량의 이름을 바꿀 수 있습니다.
노선 이름: 옆의 왼쪽/오른쪽 화살표를 눌러, 차량의 노선을 바꿀 수 있습니다. 또한 상자를 누르면, 노선들 목록이 뜹니다. 노선 이름을 바꾸는 방법은 차량 이름을 바꾸는 것과 같습니다.
차량 그림: 차량 그림 위에 마우스를 갖다대면, 해당 차량(1량)의 정보가 뜹니다. 그림을 누르면, 그 차량(1량)이 차량 묶음에서 빠집니다.

(번역중) 그 아래에는, 편성중의 열차를 조작하기 위한 다음의 버튼이 있습니다.

스타트 - 편성중의 열차의 운행을 개시합니다.운행을 개시하기 위해서는,
미리스케줄이 설정되어 있지 않으면 안됩니다.
편성 해제 - 현재의 편성을 해체합니다.해체된 편성의 차량은 스톡 되어
다른 편성에 짜넣을 수 있습니다.
매각 - 현재의 편성을 구성해 있는 차량을 모두 매각합니다.현재의 매각 가격은
「매각 가격」에 나타나고 있어 구입시부터의 시간의 경과에 수반해 감소해 갈 것입니다.

 다이얼로그의 중단 부분에는, 구입할 수 있는 차량의 일람이 표시되고 있습니다.차량은 기관
차와 객차·화차로 나누어져 있어 「기관차」탭, 「화차·객차」탭으로 표시를 완전히 교체되어
얻을 수 있습니다.차량의 화상에 마우스 커서를 거듭하면, 그 차량의 정보가 다이얼로그하
부에 표시됩니다.클릭하면, 그 차량을 구입해, 현재의 편성에 짜넣습니다.

 각 차량아래에 있는 색첨부의 바는, 그 차량을 현재의 편성에 짜넣는 것이
할 수 있을지를 나타내고 있습니다.초록의 차량은, 현재의 편성에 짜넣는 것이 출
옵니다.빨강의 차량은, 현재의 편성에는 짜넣을 수 없습니다.초록과 황색의
섞인 차량은, 현재의 편성에 짜넣을 수 있습니다만, 그 편성을 완성함
다투기 위해서는, 한층 더 다른 차량을 구입하지 않으면 안 되는 것을 나타내 보이고 있습니다.
후부 기관차가 필요한 열차등이, 이 표시가 됩니다.

 차량의 좌상에 있는 숫자는, 그 차량의 스톡이 있는 것을 나타내고 있습니다.
스톡이 있는 차량은, 클릭하는 것으로 현재의 편성에 짜넣는 것이 성과
.

 우측에 있는 「차량」버튼에서는, 구입한 차량을 현재의 편성의 어느 위치에 조
보는지 를 변경할 수 있습니다.「추가」 상태에서는, 현재의 편성의 최후부에 추가해
.「선두에」에서는, 현재의 편성의 선두에 짜넣습니다.「매각」에서는, 차
양의 구입이 아니고, 스톡 되고 있는 차량의 매각 모드가 됩니다

simutrans-124.5/simutrans/text/ko/display.txt000066400000000000000000000123001520675460500214100ustar00rootroot00000000000000디스플레이 설정 도움말

디스플레이 설정

디스플레이 설정에서는 게임이 보여지는 방식과 정보를 제공하는 방식을 조정할 수 있습니다.

디스플레이 설정게임 옵션의 디스플레이 버튼을 누르면 들어갈 수 있습니다. 옵션 버튼 왼쪽의 네모 버튼이나 화살표 버튼으로 조절할 수 있습니다.:

격자 보이기: 게임 화면에서 격자가 나타납니다. ([#]으로도 가능합니다.)
지하 시점: 지하를 볼 수 있습니다. 이를 이용하여 터널이나 지하철도를 건설할 수 있습니다. ([Shift+U]로도 가능합니다.)
층 시점: 오른쪽 숫자만큼에 해당하는 높이로 지도를 잘라서 보여줍니다. ([Crtl] + [u]로도 가능합니다. 이 때 [-][+]를 누르면 층 높이가 변합니다.)
낮/밤 변화: 활성화하면 시간에 따라 낮/밤이 변합니다.
밝기: 게임 화면의 밝기를 설정합니다. 값이 낮아지면 어두워지며, 너무 낮거나 높은 값은 문제를 일으킬 수 있습니다.
역방향 스크롤: 활성화하면 게임 화면에서 스크롤 방향이 반대가 됩니다. 예를 들면, 오른쪽으로 마우스를 드래그하면, 게임 화면이 왼쪽으로 움직입니다.
스크롤 속도: 게임 화면에서의 스크롤 속도를 정합니다. 값이 클수록, 스크롤이 빨리 됩니다.

반투명 모드: 활성화 하면 나무나 건물이 작아지지 않고 반투명하게 보입니다. 아래 두 설정에 영향을 미칩니다.
- 나무 최소화: 나무를 작게 보이게 합니다. 반투명 모드가 활성화되면 반투명하게 보입니다.
- 건물 디스플레이: 어떤 건물을 작게 보이게할지를 고를 수 있습니다.
- - 건물을 숨기지 않음: 모든 건물이 보입니다.
- - 도시건물 숨기기: 도시 건물만을 작게 보이게 합니다. 반투명 모드가 활성화되면 반투명하게 보입니다.
- - 모든 건물 숨기기: 모든 건물을 최작게 보이게 합니다. 반투명 모드가 활성화되면 반투명하게 보입니다.
- 포인터 주변 숨기기: 활성화 하면 오른쪽 숫자만큼의 영역 안의 나무와 건물을 작게 보이게 합니다. 반투명 모드가 활성화되면 반투명하게 보입니다.

역의 승객 유치 범위: 잘 모르겠습니다;;
역세권: 역세권 안에 있는 건물에서만 승객이나 화물을 수송할 수 있으며, 이 역세권을 게임 화면에 각 플레이어 색상으로 나타나게 합니다.
역 이름 보기: 게임 화면에 나타나는 역 이름 표시 방식을 바꾸는 것입니다.
대기 막대 보기: 활성화하면 역에서 수송하지 못한 승객이나 화물의 양을 역명 위에 막대그래프로 표시하게 합니다. 막대그래프의 색깔은 화물(또는 승객)의 색깔을 따릅니다.

도시의 보행자: 도시지역에 보행자가 나타납니다.
역의 보행자: 차량이 정차하는 역(정류장)에 보행자가 나타납니다.
교통량: 도시지역의 자가용의 수를 정합니다. 도시지역이 성장하면서 새로 생기는 자가용의 수는 도시지역의 크기와 교통량에 의해 결정됩니다. 교통량 값이 높을수록 자가용이 많아지며, 0이라면 생성되지 않습니다.
{팁: simuconf.tab에서 더 많은 옵션과 값을 바꿀 수 있습니다.}
차량 툴팁: 설명 추가 예정
스케줄 강조: 설명 추가 예정

디스플레이 정보:

위 설정 아래에는 Simutrans를 실행할 때의 컴퓨터 성능에 대한 정보가 표시됩니다.
숫자가 하얀색이면 모든 것이 제대로 돌아가는 중입니다. 하지만 빨간색이나 노란색 숫자가 있다면, 설정을 변경할 필요가 있습니다.
게임 상에서 시간이 흐르거나 빨리감기 중일 때에는 숫자 (또는 색깔)이 변할 수 있습니다.

프레임 시간: 은 두 프레임 사이의 실제 시간을 보여줍니다.
대기: 두 화면 업데이트 사이의 컴퓨터가 일시정지 된 시간을 보여줍니다.
FPS: 값이 높을수록 차량이 좀 더 부드럽게 움직입니다. 숫자 색깔이 빨간색이라면, 컴퓨터가 매우 느리다는 것을 의미하므로, 설정을 바꾸세요. (게임 화면 크기를 한번 줄여보세요.)
Simloop: 초당 시뮬레이션 루프의 수를 보여줍니다. 숫자 색깔이 빨간색이라면, 도시지역이 적은 좀 더 작은 지도로 플레이하세요.

simutrans-124.5/simutrans/text/ko/edittools.txt000066400000000000000000000152701520675460500217620ustar00rootroot00000000000000게임 및 지도 편집 도구 도움말

게임 및 지도 편집 도구

게임 편집 도구는 현재 게임 세계를 추가로 사용자 지정할 수 있도록 허용합니다.
도구를 통해 다음과 같이 할 수 있습니다: 도시 지역의 인구를 변화시킵니다; 도시의 도로, 명소, 발전소 및 산업단지를 건설합니다; 다른 플레이어 회사로서 운영하기 위한 옵션을 제거합니다; 게임을 1년을 앞당깁니다.

공용 서비스 플레이어로 작동하는 옵션이 특수 건설 도구(또는 키 누르기 [P])의 플레이어 또는 P+에서 선택되면 도구 바가 열립니다. 플레이어를 변경하는 옵션이 다시 사용되는 경우에도 도구 바는 열린 상태로 유지됩니다.
일부 도구 옵션에 대한 이름 및 자세한 정보를 보려면 도구 옵션 (도구 바를 열거나 클릭한 후) 위로 마우스 커서를 올리십시오.

도구 옵션은 왼쪽에서 오른쪽으로 다음을 포함합니다:

도시 성장: 도구는 도시 지역의 인구를 100명으로 늘리고 필요한 경우 새로운 도로와 건물을 건설합니다.
사용하려면: 도구를 클릭하여 선택하고 커서를 빨간색 위 화살표로 변경한 다음 도시 지역을 클릭하십시오.

도시 축소: 도구는 도시 지역의 인구를 100으로 줄입니다. 도시 지역은 노숙자와 실업자 수의 레벨이 증가하면 계속 성장할 것입니다.
사용하려면: 도구를 클릭하여 선택하고 커서를 빨간색 아래 화살표로 변경한 다음 도시 지역을 클릭하십시오.

도시 도로: 도구는 모든 플레이어가 사용할 수 있는 공용 서비스 플레이어로서 두 지점 사이에 도시 도로를 건설합니다. 도시 도로는 다른 건물이 차지하는 지역에는 건설되지 않을 수 있으며 거친 지형, 물 및 장애물을 가로 지르는 경로를 찾지 못할 수도 있습니다. 지점 사이에 건설된 도시 도로는 기존 도로를 경로로 사용할 수 있습니다.
공용 서비스 플레이어 의해 발생된 이름, 건설 및 유지 비용 그리고 최대 속도 한도를 보려면 도구 옵션 (도구 바를 열거나 클릭한 후) 위로 마우스 커서를 올리십시오. 도시 도로를 건설하려면: 도구를 클릭하여 선택한 (도시 도로로 커서를 변경합니다) 다음, 도로 (게임 보기에서 불도저를 표시하고 게임 보기 아래 하단 바의 오른쪽에 지도 좌표를 표시합니다)의 시작 지점의 게임 보기를 클릭하고, 마지막으로 도로의 끝 지점에 대한 지형을 클릭하십시오.
{팁: 다른 유형의 도로가 연결될 수 있습니다. 거친 지형을 가로 지르는 도로를 연결하거나 장애물을 피하려면 다리와 터널을 사용하십시오. 도로를 위한 경로를 사용할 수 있도록 지형을 변경하려면 지형변경 도구를 사용하십시오. 도시 도로 및 일부 장애물을 제거하려면 파괴/제거를 사용하십시오. 추가적인 기능을 위해서 동시에 [Ctrl]키를 사용하십시오.}

무작위로 명소를 건설하십시오: 도구는 승객들이 방문하고 싶어하는 명소를 건설합니다. 서로 다른 명소에서는 각각 다른 양의 승객과 우편이 발생합니다. 건설된 명소의 유형은 무작위이며 장애물이 없는 적절한 공간의 경우에만 나타납니다.
명소를 건설하려면: 도구를 클릭하여 선택한 (커서를 red-drop-in-yellow-circle 아이콘으로 변경합니다) 다음, 게임 보기에서 지형 상의 필수 위치를 클릭하십시오.
{팁: 공용 서비스 플레이어로 명소를 제거하려면 파괴/제거를 사용하십시오. 명소에 관한 적절한 장소를 생성하려면 파괴/제거 또는 풍경 도구를 사용하십시오.}

새 발전소를 건설하십시오: 도구는 발전소 (및 가능한 공급소)를 건설합니다. 건설된 발전소의 유형은 무작위이며 장애물이 없는 적절한 공간의 경우에만 나타납니다.
발전소를 건설하려면: 도구를 클릭하거나 [I]를 눌러서 선택한 (커서를 red-drop-in-yellow-circle 아이콘으로 변경합니다) 다음, 게임 보기에서 지형 상의 필수 위치를 클릭하십시오.
{팁: 공용 서비스 플레이어로 발전소를 제거하려면 파괴/제거를 사용하십시오. 발전소에 관한 적절한 장소를 생성하려면 파괴/제거 또는 풍경 도구를 사용하십시오.}

가장 가까운 도시에 새로운 시장을 건설하십시오: 도구는 도시 지역 (그리고 도시 지역에 공급자가 반드시 필요한 것은 아닙니다)에서 최종 소비자를 구성합니다. 건설된 산업 시설의 유형은 무작위이며 장애물이 없는 적절한 공간의 경우에만 나타납니다.
사용하려면: 도구를 클릭하여 선택한 (커서를 red-drop-in-yellow-circle 아이콘으로 변경합니다) 다음, 가장 가까운 도시 지역에서 소비자를 구축할 수 있도록 게임 보기를 클릭하십시오.
{팁: 공용 서비스 플레이어로 발전소를 제거하려면 파괴/제거를 사용하십시오. 산업 시설에 관한 적절한 장소를 생성하려면 파괴/제거 또는 테라포밍 도구를 사용하십시오.}

추가 플레이어 변경을 허용하지 마십시오: 도구는 다른 플레이어 회사로 작동하고 게임 (지도) 편집 도구를 열 수 있는 옵션을 제거합니다.
사용하려면: 도구를 클릭하여 선택한 (커서를 자물쇠 형상으로 변경합니다) 다음, 게임 보기의 아무 곳이나 클릭하면, 확인이 필요하지 않습니다.

1년 단위의 타임라인 단계: 게임의 일정을 1년 앞당깁니다. 만약 타임라인을 사용한다면, 시간이 지남에 따라 더 많은 수송 및 건설 옵션을 사용할 수 있게되고 느린 차량은 수익성(수입은 이용 가능한 수송차량의 평균 속도에 의해 계산됩니다)이 없어지게 됩니다.
사용하려면: 도구를 클릭하면, 더이상 확인할 필요가 없습니다 (게임 보기 아래 하단 바의 왼쪽에 날짜가 변경됩니다).

simutrans-124.5/simutrans/text/ko/enlarge_map.txt000066400000000000000000000005671520675460500222310ustar00rootroot00000000000000지도 확대 도움말

확대 지도

여기서, 지도를 오른쪽과 아래쪽으로 확장할 수 있습니다. 만약 왼쪽 / 위쪽으로 확장하려면, 먼저 지도를 회전하고 확대한 다음 뒤로 회전해야 합니다.
확대된 지도의 미리보기는 다이얼로그 창의 오른쪽 상단에 표시됩니다.

simutrans-124.5/simutrans/text/ko/factorylist_filter.txt000066400000000000000000000050071520675460500236610ustar00rootroot00000000000000 팩토리 목록

팩토리 목록

Factory List 은 현재 게임의 모든 factories(상품 공급업체 및 소비자)에 대한 정보를 보여줍니다.

공장 목록 열기: 목록 관리.
에서 공장 목록 클릭 {팁: 목록의 항목이 부분적으로만 표시되는 경우 재조정 산업/공장 목록 또는 스크롤은 슬라이더 막대를 사용하여 수행합니다.}

순서 지정: 에는 나열된 산업체의 순서를 결정하는 옵션이 있습니다.
옵션을 순환하려면 버튼을 클릭하십시오(옵션 버튼의 이름 변경).

- 공장이름은 이름으로 알파벳순으로 주문됩니다.
- 입력 용량별 주문 건수입니다.
- 출력 출력 스토리지 용량별 주문입니다.
- 생산은 생산 능력별로 업계를 주문합니다.
- 레이팅은 상태별 색상 표시줄로 업계를 주문합니다(아래 참조).
- 파워은 전기 공급으로 업계를 주문합니다.

- 스캔딩 / descending은 목록 순서를 반대로 합니다.

해당 업계에 대한 자세한 내용을 보려면 Factory List 에 나열된 항목을 클릭하십시오. 각 업종에 대한 목록에는 다음이 포함됩니다:

상태-색상-바 색상은 업계 운영을 나타냅니다(A에서도 사용됨):
- 흰색: 은 보충제가 필요하지 않습니다.
- 노란색:운송으로 연결되었지만, 산업 공급 체인의 공급은 부족합니다.
- 녹색: 최적상태에서 녹색입니다.
- 오렌지: 작동 가능하지만 개선이 가능합니다.
- 빨간색: 작동 중이지만 산업 공급망의 일부가 공급 과잉입니다.

업계가 변전소 를 통해 전력을 공급받는다면 빨간불-항목이 표시됩니다.

공장의 이름 공장 이름:

괄호 안의 3 값은 다음과 같습니다. 전류 입력: 처리 준비가 된 상품의 양입니다.
전류 출력: 운송 준비가 된 상품의 양입니다.
총생산: 공장의 생산율(월당 최대 생산량)

simutrans-124.5/simutrans/text/ko/finances.txt000066400000000000000000000023171520675460500215400ustar00rootroot00000000000000재정 도움말

재정

 재정 다이얼로그에서는, 작년도의 결산, 및 금년도의 현시점에서의 수지를 보는 와
(이)가 할 수 있습니다.각 항목은 이하의 내용을 나타내고 있습니다.

건설비 선로나 도로, 역 등, 건축물에 사용된 비용의 합계입니다.

차량/선박 구입비 열차, 트럭이나 버스, 선박 등, 탈 것의 구입에 사원
비용의 합계입니다.

차량/선박 운항 비용 탈 것의 운행 비용의 합계입니다(차량마다의 운행 비용은,차고 위
드우로 조사할 수 있습니다).

수입 여객 수송, 화물 수송에 의해서 얻을 수 있던 모든 수입의 합계입니다.

유지비 선로나 도로, 역등의 건축물을 유지하기 위해 걸린 비용의 합계 입니다.
(팁:이 항목은 다이얼로그아래의 부분에 있습니다)

수지 이상의 각 항목의 지출과 수입을 계산한 결과의 수지입니다.

잔금 현재의 자금 총액입니다.

simutrans-124.5/simutrans/text/ko/general.txt000066400000000000000000000063231520675460500213700ustar00rootroot00000000000000Simutrans 도움말

일반 도움말

- 환영합니다!
- 인터페이스
- 마우스 도움말
- 키보드 도움말

게임 옵션

- 언어
- 플레이어 색상
- 디스플레이 설정
- 소리와 음악
- 플레이어
- 게임 불러오기
- 게임 저장하기
- 새 게임
- - 기후 설절
- - Height Map 불러오기

주 메뉴

- 게임 옵션
- 지도
- 조사 도구
- 조경 도구
- 철도 도구
- 모노레일 도구
- 트램 도구
- 도로 도구
- 선박 도구
- 비행기 도구
- 특수 건설 도구
- 게임 편집 도구
- 파괴/제거

경영 메뉴

- 노선 관리
- 목록
- - 정류장/역 목록
- - - 정류장/역 목록 필터
- - 차량 목록
- - - 차량 목록 필터
- - 도시 목록
- - 상품 목록
- - 산업 목록
- - 관광명소 목록
- 메시지 센터
- 재정

기타 옵션

- 스크린샷
- 멈춤
- 빨리감기
- 도움말

기타 이야기

- 차고지 관리
- 스케줄 관리
- 정류장/역 정보

- - 정류장/역 상세 정보
- 차량 정보
- - 차량 상세 정보
- 도시 정보
- 산업 정보

찾으시는 도움말이 없다면 밑의 사이트들을 참고하세요.
- http://guide.simutrans.com
- http://wiki.simutrans.com
- http://simutrans-tips.com
- http://forum.simutrans.com

simutrans-124.5/simutrans/text/ko/goods_filter.txt000066400000000000000000000067211520675460500224350ustar00rootroot00000000000000 상품 목록

상품 목록

상품 목록 에는 게임 내에서 수송할 수 있는 여러 가지 항목과 이를 운반하는 데 필요한 수입의 양에 대한 정보가 포함되어 있습니다.

열려면: 목록 관리에서 상품 목록 도구를 클릭하거나 [G].를 누릅니다.

게임에서 운송이 가능한 품목의 목록은 수령한 수익, 속도 보너스 %, 상품 카테고리, 중량이다.
{팁: 목록의 항목이 부분적으로만 표시되는 경우 re-size 상품 목록 또는 스크롤은 슬라이더 막대를 사용하여 수행합니다.}

속도보너스 : 열거된 수익을 얻는 데 필요한 다양한 전송 유형의 차량평균 최대 속도를 보여줍니다. 평균 최대 속도가 나열된 속도보다 빠를 경우 일부 항목은 더 많이 벌 수 있지만 속도가 더 느릴 경우 더 적게 벌 수 있습니다.
타임라인 설정이 활성화되면 시간이 경과함에 따라 속도 값이 변경될 수 있습니다. 변화는 대개 더 빠르거나 더 경제적인 차량
의 도입에 따릅니다. 화살표 버튼을 사용하여 속도를 백분율로 조정하고 표시된 모든 수익을 업데이트할 수 있습니다. 이것은 차량의 평균 최대 속도가 수동으로 계산할 필요 없이 속도보너스 속도에 정확히 맞지 않을 때 차량이 얻는 수익을 알고 싶을 때 유용합니다.

순서 지정: 은 항목 목록이 표시되는 순서를 보여줍니다.
다양한 정렬 옵션(옵션 버튼의 이름 변경):

- 이름에 의한, 알파벳순으로 주문됩니다.
- 수익에 의한 소득별로 주문됩니다.
- 보너스에 의한 스피드보너스 비율(%)에 의해 주문됩니다.

- 오름차순 / 내림차순은 목록 순서를 반대로 합니다.

각 항목에 대한 세부 정보는 다음과 같습니다.

색상표는 A 위 게임 뷰에 상품-색상 바에서 운송 대기 중인 품목의 양을 나타내는 색상과 동일합니다.
{팁: [!]을(를) 사용하여 게임 뷰의 중지 위에 있는 상품-색상-바를 전환합니다.}

수익은 한 단위를 현재 지급 모델에서 한 개의 타일 거리로 운반하여 받은 소득이다.
{(기본 수익) * (1 + (평균 최대 속도) / (속도보너스) - 1) * (% 보너스)}에 의해 계산됩니다.

% 보너스은 속도보너스 속도를 중심으로 한 속도보너스와 반대로 최대 속도 Convoy와 선형적으로 매출 변화율 %입니다.

화물 카테고리은 운송에 필요한 차량의 종류를 나타냅니다. 특수 화물은 특정 차량을 필요로 합니다.
{팁: 차량capacity 에 운송할 수 있는 항목이 표시됩니다.

무게은 화물 한 단위의 무게가 얼마나 되는지 보여줍니다.
이는 운송 중 차량 무게에 추가되어 차량이 비어 있는 경우보다 더 많은 동력을 필요로 합니다.

simutrans-124.5/simutrans/text/ko/haltlist.txt000066400000000000000000000104221520675460500215720ustar00rootroot00000000000000역 목록

역 목록

역 목록 은 정보를 보여주며 다양한 정거장(물 운송 차량만 해당)을 필터링 및 나열할 수 있는 제어 장치를 갖추고 있으며, 차량이 픽업 및 드롭됩니다.

열기:목록 관리를 클릭하십시오. 역 목록 은 네 개의 옵션 버튼이 있으며, 두 개는 목록의 항목을 주문하는 데 사용되고 두 개는 목록에 표시할 항목을 선택하는 데 사용됩니다. 아래 버튼은 필터 옵션의 기준에 맞는 정류장 의 목록입니다.
{팁: 목록에 있는 정류장이 없으면 필터 옵션을 변경합니다. 목록의 항목이 부분적으로만 표시되는 경우 재조정 역 목록 또는 스크롤 목록(슬라이더 막대가 있는 경우)}

버튼을 클릭하여 옵션을 순환하거나(옵션 버튼의 이름 변경) 필터 설정 컨트롤을 엽니다.

정렬: 두 옵션 버튼들은 보여지는 리스트들에서 정류장의 순서 결정한다.

- 타입은 허가된 차량의 타입을 주문한다.
- 비정렬는 무작위로 순서를 정한다.
- 이름이름에 할당된 서비스 주문의 ASCII 코드(하위 사례 문자의 대문자) 내에서 영숫자 순서로 주문한다.
- 대기순서는 대기 중인 승객과 상품의 양만큼 제공됩니다.

- 오름차순 / 내립차순은 목록 순서를 반대로 합니다.

필터: 옵션 버튼 두 개, 목록 설정/해제 선택 기준을 설정하여 기준을 변경합니다.

- 가능 / 불가능 클릭해서 필터 기준을 전환합니다.

-설정 을 클릭하여 컨트롤을 열고 필터 기준을 변경합니다.

역 목록에 나열된 항목을 클릭하면 해당 정류장에 대한 자세한 정보 를 볼 수 있습니다.
각 정지에 대해 나열된 항목은 다음과 같습니다.

색상 상태 바: 색상은 과밀도와 관련하여 Stop의 작동을 나타냅니다. 색상 바는 정지 정보 및 노선 관리에서도 사용되며, 게임 디스플레이에서 정지 위의 색상 막대에 표시된 색상과 동일합니다. - 노란:은 아무 서비스도 받지 않고 기다리는 것도 없습니다.
- 녹색:은 개선할 필요가 없습니다.br - 주황:은 정류장 꽉찬 상태이다(대부분 환승 물품이 있는).
- 빨강: 정류장은 용량의 1.5배 이상, 또는 200명 이상의 불행한 승객을 수용하거나, 역이 과밀하기 때문에 공장에서 생산을 중단해야 합니다.
{팁: [!]를 사용하여 게임 디스플레이에서 중지 위의 색상 막대를 전환합니다.}

할당된 이름 {팁: simuconf.tab}에서 사용할 수 있는 중지 이름의 번호를 할당하는 옵션입니다.

차량 아이콘 정류장를 사용할 수 있는 차량 유형을 나타냅니다(정류장 정보와 노선 관리에서도 사용됨).
아이콘에는 버스(도로 승객용), 트럭(도로 화물용), 기차, 보트 및 비행기가 포함됩니다. 열차는 정지 유형에 따라 버스-아이콘 또는 열차-아이콘으로 표시될 수 있습니다.

화물 아이콘 정류장 이 처리할 수 있는 항목(승객, 상품 및/또는 메일)을 나타냅니다(정류장 정보와 노선 관리에서도 사용됨).
{팁: 적절한 확장을 추가하면 중지 시 처리할 수 있는 항목의 범주가 변경됩니다. 우체국을 정류장에 추가하여 메일을(를) 처리할 수 있습니다.}

정류장에 관련된 보도객들 상품들의 차이점의 대기 세부사항.

simutrans-124.5/simutrans/text/ko/haltlist_filter.txt000066400000000000000000000050311520675460500231370ustar00rootroot00000000000000역 목록 필터

역 목록 필터

역 목록 필터에는 차량이 승객을 태워가는 정류장 을 결정하는 옵션이 있습니다(물 운송 차량은 해당 안됨). 또한 역 목록에 표시됩니다.

스테이션 목록에서 설정를 클릭하여 역 목록 필터를 엽니다.

옵션 버튼은 정류장을 선택합니다. 필터 옵션이 가능 상태일 때 역 목록에 나열됨. 모든 기준 집합에 적합한 정류장만 나열됩니다.

버튼을 클릭하여 필터 기준을 선택하거나 선택 취소합니다(선택하면 버튼이 들여쓰기됨).

필터 이름:정류장을 선택합니다. 사용하려면 단추를 클릭하여 선택한 다음 이름 상자를 클릭하고 필요한 이름을 정확하게 입력합니다(옵션은 대소문자를 구분함).

필터 유형:에는 선택한 경우 다음 항목 중 하나 이상을 포함하는 정류장 이 나열됩니다. 사용하려면: 버튼을 클릭합니다. (수송 차량만 정지하면 역 목록에 없음)

특수 필터:
- 오버플로? 사용 중이 아닙니다.
- 비 연결은 운송 서비스가 없는 정류장을 선택합니다.

필요한 상품: 정류장을 의미합니다. 여기서 선택한 항목은 이동을 종료합니다. 이름 옆에 있는 버튼을 클릭하여 항목을 선택합니다. 항목 목록을 스크롤하려면 슬라이드 바 를 사용하십시오. 옵션은 다음과 같습니다:
- all은 모든 항목을 선택합니다.
- none 항목이 선택되지 않았습니다.
- inv.은 현재 선택한 항목보다 우선합니다.

생산:은 이동 경로를 반영한 정류장 을 나타냅니다. 이름 옆에 있는 버튼을 클릭하여 항목을 선택합니다. 슬라이더 바를 사용하여 항목 목록을 스크롤합니다. 옵션은 다음과 같습니다.
- all은 모든 항목을 선택합니다.
- none 항목이 선택되지 않았습니다.
- inv.은 현재 선택한 항목보다 우선합니다.

simutrans-124.5/simutrans/text/ko/industry_info.txt000066400000000000000000000124261520675460500226500ustar00rootroot00000000000000산업 정보

산업 정보

산업 정보는 선택된 산업(상품 공급업체 또는 소비자)에 대한 세부 정보를 표시하고 게임 세계의 관련 위치로 게임 보기를 이동할 수 있도록 합니다.

상품의 공급자와 소비자는 최종 소비자로 종료되는 산업 공급망을 제공하기 위해 연결 됩니다. 어떤 산업은 소비자와 공급자 둘 다이며 산업 공급망의 중간에 있다. 만약 초보자 모드에서 뛰지 않으면, 소비자는 공급 과잉을 중단하게 됩니다.
산업은 또한 승객과 우편물의 목적지일 수도 있다. 정차장의 유역 구역이 산업 건물의 어느 부분을 커버하고 정차장이 해당 품목을 처리할 수 있는 경우 차량은 정류장 을 통해 상품과 승객을 수집하여 산업으로 운송한다. {팁: 해상운반/오일선/어류 양식장/배치구역/오일/우편/승객/역 목록에 없음}

초기 산업 공급 체인 수는새 게임 만들기을 설정가능한 체인들을 공급합니다. 도시 지역가 성장할수록 더 많은 산업이 나타날 것입니다. 새로운 산업은 도시 지역의 인구가 2000년 이후 두 배가 될 때마다 나타난다.
{팁: 가까운 도시에 새로운 시장 건설을 사용하여 산업체를 도시 지역에 추가한다?게임 편집 도구. cityrules.tab에서 새 산업이 생성될 때 변경(산업체 성장률 = 0은 새로운 산업을 생성하지 않음)}.

검사 도구를 이용하거나 게임 뷰에 있는 업체를 클릭하거나 산업 목록에 나열된 산업체를 클릭하여 산업 정보를 엽니다. 슬라이더 바 를 사용하여 보이지 않는 모든 항목을 스크롤 할 수 있습니다.

선택한 산업의 이름은 산업 정보의 제목 표시줄에 표시됩니다.
선택한 산업 및 옵션에 대한 기타 정보는 다음과 같습니다.

생산: 업계가 공급(공급업체인 경우) 또는 소비(최종 소비자인 경우)의 최대 비율(월/일 단위).
{팁: 변압소 와 산업에 전기를 공급하는 것을 추가하면 생산/소비되는 상품의 비율이 증가한다.

산업은 산업건물을 보여줍니다. 게임 뷰를 산업 건물로 이동하려면 그림을 클릭하십시오.

그림 아래의 색상 상태 바 는 산업의 운영을 나타냅니다(또한 산업 목록)에서도 사용됨). - 흰색:은 입력하지 않아도 됩니다.
- 노란색:운송으로 연결되었지만, 산업 공급 체인의 공급은 부족합니다.
- 녹색: 최적의 상태.br - 주황색: 작동 가능하지만 개선이 가능합니다.
- 빨간색: 작동 중이지만 산업 공급망의 일부가 공급 과잉입니다.

소비자:은 선택된 산업계에서 생산된 제품에 대한 산업 공급망의 대상 목록을 보여줍니다.
목적지 산업과 그 지도 좌표는 화살표 버튼에 의해 나열되어 있다. 화살표 버튼을 클릭하여 게임 보기를 대상으로 이동합니다.

공급자:은 특정 산업이 요구하는 산업 공급망에서 상품의 출처를 보여줍니다.br 공급 업체 산업과 이들의 지도 좌표는 화살표 버튼별로 나열되어 있다. 화살표 단추를 클릭하여 게임 보기를 공급업체로 이동합니다.

노동자:은 직원들이 살고 있는 도시 지역 근처에 살고 있습니다. 이러한 도시 지역의 승객과 우편물은 선택된 산업에 도달해야 할 것이다.
도시 지역과 지도 좌표는 화살표 버튼에 의해 나열되어 있다. 화살표 단추를 클릭하여 게임 보기를 공급업체로 이동합니다.

승객 레벨:은 승객의 목적지로 상대적인 인기를 얻고 있습니다. 메일 레벨:은 메일의 목적지로 상대적인 인기입니다.
{팁: 승객과 메일에 최적의 서비스를 제공하기 위해 산업 건물 중 1평방미터만 정류장의 보관 구역에 떨어지면 됩니다.

프로덕션:은 산업 생산량에 대한 세부 정보를 제공합니다.
표시된 정보에는 품목 이름, 품목의 최대 저장 용량으로 현재 저장된 수량, 상품 카테고리, 입력 대비 출력 수준 등이 포함됩니다.

소비은 산업 입력에 대한 세부 정보를 제공합니다.br 표시된 정보에는 품목 이름, 품목의 최대 저장 용량으로 현재 저장된 수량, 상품 카테고리, 출력 대비 입력 비율 수준이 포함됩니다.

업계에 서비스를 제공하는 정류장산업 정보 하단에 나열되어 있습니다.

simutrans-124.5/simutrans/text/ko/inspection_tool.txt000066400000000000000000000137401520675460500231640ustar00rootroot00000000000000 검사 도구

검사 도구

검사 도구는 다양한 항목에 대한 정보 또는 옵션 제어를 제공하는 대화를 여는 데 사용됩니다.

게임 뷰 맨 위에 있는 돋보기-유리-아이콘을 클릭하거나 [a]를 눌러 검사 도구를 엽니다(커서가 돋보기 모양으로 변경).

검사 도구를 사용하려면: 필요한 위치에서 게임 뷰에 돋보기-유리-커서를 배치하고 대화 상자를 클릭하여 엽니다. 둘 이상의 항목이 필요한 위치에 있는 경우, 다시 클릭하면 두 번째 대화 상자가 열립니다. {팁: simuconf.tab}에서 사용할 수 있는 지정된 커서 위치에 대해 하나의 대화 또는 모든 대화 상자를 여는 옵션

다양한 대화를 열려면 툴과 함께 다음 항목을 클릭하십시오.

차고지 - 제어 창을 열어 해당 차고에서 차량을 구매하고 관리합니다.

정류장 - 해당 Stop에 대한 정류장 정보를 엽니다.

차량 - 해당 차량에 대한 수송수단/차량 정보를 엽니다.

시청 은 그 도시 지역을 위한 도시 정보 를 엽니다. {팁: simuconf.tab에서 사용 가능한 시청 정보를 제공하는 다이알로그를 여는 옵션입니다}

산업 - 해당 산업의 산업 정보를 엽니다.

도시 건물은 건물에 대한 정보를 제공하는 대화상자를 엽니다(대화 제목 표시줄은 '건물'). 대화 상자에 포함된 정보에는 다음이 포함됩니다. - 설명은 건물에 대한 정보를 제공합니다. 설명을 사용할 수 없는 경우 '주택', '산업 건물', '작업장 및 상점'(상업 건물의 경우) 중 하나가 표시됩니다.
- 도시: 도시 건물이 속한 도시 지역을 나타냅니다.
- 승객 레벨: 은 승객의 목적지로 상대적인 선호도입니다.
- 메일 레벨: 은 메일의 수신지로서 상대적인 선호도입니다.
- 시작 시기: 타임라인으로 플레이할 경우 건물이 나타나는 연도를 나타냅니다.
- 값: 파괴/제거 도구로 건물을 철거할 경우 발생하는 비용을 나타냅니다.
- 그린 이: 시뮤트렌스의 건물을 그린 사람의 이름

기념비 / 관광 명소는 관광 명소에 대한 정보를 제공하는 대화를 엽니다(대화 모음은 그것이 기념물인지 관광 명소인지를 나타냅니다). 다이알로그에 포함된 정보에는 다음이 포함됩니다. - 공개 자산은 해당 항목이 플레이어 회사에 의해 소유되지 않음을 나타냅니다.
- 설명은 매력에 대한 정보를 제공합니다.
- 승객 레벨:은 승객의 목적지로 상대적인 선호도입니다.
- 메일 레벨:은 메일의 수신지로서 상대적인 선호도입니다.
- 시작 시기:타임라인으로 플레이할 경우 나타나는 연도를 나타냅니다.
- 그린 이: 시뮤트랜스의 관광지를 그린 사람의 이름

도로와 선로은 도로 또는 선로에 대한 정보를 제공하는 다이알로그를 엽니다(대화 제목 표시줄은 도로 표제 트랙의 유형을 나타냅니다).
대화에 포함된 정보: - 최대 속도: 도로 또는 트랙에서 허용되는 최대 속도 제한.
- Ribi(마스크 없음) :
은 이동 방향을 나타내는 내부 카운터입니다.
- Ribi(마스크 적용): 신호나 신호를 고려하는 경로 방향을 나타내는 내부 카운터입니다.
- 신호 또는 기호가 있는 경우 - 기호/신호 이 표시됩니다.
- 전기화/ 비 전기화는 길이 전기화되었는지 여부를 나타냅니다.
- 지난 달 수송량는 이전 게임 일정 기간에 사용된 차량의 수를 나타냅니다.
- 예약은 어느 차량이 통과될 때까지 선로을 예약했는지 나타냅니다.
- 경사 로프은 슬로프 방향을 나타냅니다. - 맵 좌표은 아래쪽 막대 에 나온 것과 같이
- 도로에 설치된 트램 선로의 경우: 정보가 두 번 표시됩니다(처음에는 도로, 그 다음에는 트램 선로).

신호등/신호은 타이틀 바의 다이알로그가 신호인지 도로 표지판인지를 분별하는 다이알로그를 엽니다. 대화에 포함된 정보에는 다음이 포함됩니다. - 방향은 신호가 적용되는 방향을 나타내는 내부 카운터입니다.

전압선 / 변압소은 전기 공급 그리드에 대한 정보를 제공하는 대화를 엽니다. 표시된 정보에는 현재 수요와 공급이 포함됩니다.

나무를 누르면 다이알로그가 열립니다(타이틀 바는 '나무'임). 표시된 정보에는 다음이 포함됩니다. - 이름은 나무의 종류를 제공.
- 나이은 월별 나무 나이.

은 대화를 엽니다(제목표는 '땅'임). 표시된 정보에는 다음이 포함됩니다.
- 경사로는 나무의 종류를 제공합니다.
- 하단 바에 나온 것처럼 지도 좌표에 보여진다.

{팁: simunconf.tab에서 사용할 수 있는 개인 도로 사용자 및 보행자에 대한 대화를 여는 옵션입니다.}

simutrans-124.5/simutrans/text/ko/jump_frame.txt000066400000000000000000000012071520675460500220740ustar00rootroot00000000000000건너뛰기 도움말

건너뛰기

다이알로그는 [J]키와 건너뛰기 가능한 특정한 좌표와 함께 열립니다. 이것은 게임세계의 특정한 위치로 빠르게 움직이는데 유용합니다.

게임안의 모든타일은 (x,y)두가지 좌표를 가지며,(0,0)은 지도의 맨왼쪽구석에 위치합니다. 커서 좌표들은 영구적으로 상태 바에 보여집니다.
주의: 지도회전([R]키와 같이 수행 되어지는)은 모든 좌표타일들을 회전시킵니다.

simutrans-124.5/simutrans/text/ko/labellist_filter.txt000066400000000000000000000005101520675460500232630ustar00rootroot00000000000000신호 목록

플레이어 신호 목록

여기는 지도의 모든 신호들의 목록이 있다.
활동 사용자 전용 옵션은 활동하는 사용자의 신호들만 보여줍니다.

항목을 클릭하면 해당 텍스트를 편집할 수 있습니다. simutrans-124.5/simutrans/text/ko/language.txt000066400000000000000000000020641520675460500215340ustar00rootroot00000000000000언어 도움말

언어

언어 창에서는 게임에 사용되는 언어를 바꿀 수 있습니다.

게임 옵션에서 열 수 있으며, 새 게임 만들기 창과 함께 뜹니다.

Simutrans의 기본 언어는 영어입니다. 다른 언어로 설정하려면, 여러분이 하고 싶은 언어 옆 버튼을 누르세요.

언어는 게임하는 도중에도 바꿀 수 있습니다. 그 때에는 게임 옵션의 언어 창으로 들어가세요.

언어는 새 게임을 만들 때 즉시 바뀌며, 게임 도중에는 언어 창을 끄자마자 바뀝니다.

선택한 언어에서 번역되지 않은 말이 있다면, 그 말들은 영어로 나타납니다.

Simutrans 번역을 돕고 싶으시다면, https://translator.simutrans.com를 방문해주세요.

귀하의 언어로 Simutrans를 번역 할 수 있도록
https://translator.simutrans.com을 방문하십시오.

simutrans-124.5/simutrans/text/ko/linemanagement.txt000066400000000000000000000215051520675460500227360ustar00rootroot00000000000000노선 관리

노선 관리t

동일한 경로를 가진 운송수단이나 복수 운송수단에 대한 노선 관리 는(운송수단이 진행하기 위해 필요한 재화 및 승객의 경로 및 최소 수량 )들을 관리하기 위한 정보와 제어를 제공한다.

선은 차량 픽업 및 드롭 오프 상품들 및 승객들(물 운송 차량이 부두의 유역 구역에 있는 모든 정사각형 물을 사용하는) 과 경유지(두 개 이상의 옵션이 가능한 경우 차량을 직접 연결하거나 중간 목적지 지점을 제공하는 데 사용)로 구성됩니다.

게임 보기 맨 위에 있는 네트워크 아이콘을 클릭하거나 [w]를 눌러 노선 관리을 엽니다. 이 작업은 재조정 크기를 다시 조정할 수 있습니다(원래 크기에서 아래쪽 화살표 클릭).

노선 관리의 왼쪽에는 전송 유형으로 필터링할 수 있는 기존 라인이 나열되어 있습니다(목록 오른쪽에 있는 슬라이더 바 사용). 위 목록에서 index-tab를 클릭하여 다음 항목
을 선택합니다. 전부: 모든 차량에 관한 선을 나열합니다.
기차:모든 철도에 관한 선을 나열합니다.
모노레일:모든 모노레일/화거리차량에 관한 선을 나열합니다.
기차: 모든 전차에 관한 선을 나열합니다.
트럭: 모든 도로차량에 관한 선을 나열합니다.
: 모든 수송차량에 관한 선을 나열합니다.
항공: 모든 항공기에 관한 선을 나열합니다.

목록에 나타나는 선의 색상은 다음을 나타냅니다.:
하양 - 라인에 할당된 차량이없습니다.
노랑 - 운영되지 않고 이득과 손실을 발생시키지 않습니다.
검은 - 이득을 만듭니다.
파랑 - 쓸모없는 차량을 가지고 있습니다.
빨간 - 선이 손실을 만듭니다.

아래에 존재하는 선들의 나열은 선들을 관리하기위한 선택-버튼들이다.
라인 입력 목록을 클릭하여 선택(이름 강조)하고 정류장에 관한 정보,수입,그리고 선택된 선의 운송수단들을 볼수 있습니다.
정류장들 또는 운송수단들에 대한 세부 정보가 부분적으로만 보이는 경우, 노선 관리 크기를 조정하거나 슬라이더 막대를 사용하여 정보를 스크롤합니다.

왼쪽의 선택-버튼들은 (선택을 클릭)포함한다:

새 노선:옵션은 스케쥴(수송 수단 에서 진행하기 위해 요구하는 재화와 승객의 경로 및 최소 수량)을 정의하는 컨트롤이 열립니다.
필요한 수의 정류장 또는 경유지를 추가하고 필요에 따라 대기 레벨을 설정한 다음 스케줄 컨트롤을 닫아 새 라인을 만듭니다(제어판의 왼쪽 상단 모서리에서 x 클릭 또는 키보드 사용). 새 회선은 번호가 할당되고 노선 관리에 나열됩니다.
{팁: 새 라인을 생성하기 전에 색인 탭을 사용하여 전송 유형을 선택합니다. 차량들은 경유지에 있는 물품과 승객들을 픽업하거나 내려놓지 않는다.}

노선 갱신:은 기존 스케쥴(수송 수단가 진행하는 데 필요한 재화와 승객의 경로 및 최소 수량)을 변경하고 관리하기 위한 컨트롤을 엽니다. 변경 사항은 스케줄 제어를 닫은 후 온라인 차량에 영향을 미칩니다(왼쪽 상단 모서리에서 x 클릭).
목록에서 노선 이름을 클릭하여 선택(이름 강조)한 다음 옵션 버튼을 클릭하여 일람표 컨트롤을 엽니다.

노선 제거:은 선택한 라인(수송 수단가 진행하는데 필요한 재화와 승객의 경로 및 최소 수량)을 삭제합니다.
목록에서 노선 이름을 클릭하여 선택(이름 강조)한 다음 옵션 버튼을 클릭하여 제거합니다. 추가 확인은 필요하지 않습니다.

노선을 선택하면 정류장 라인 관리"의 왼쪽 하단에 표시됩니다. 목록 정류장를 클릭하여 정류장 정보를 엽니다.
각 정류장에 대해 나열된 항목에는 다음이 포함됩니다. 색상 상태 바: 색상은 과밀도와 관련하여 정류장 의 작동을 나타냅니다. 상태-색상은 또한 역 목록 및 정류장 정보에도 사용되며, 게임 디스플레이에서 정류장 위의 색상 표시줄에 표시되는 색상과 동일합니다:
- 노락색: 은 작동하지 않습니다.
- 초록색: 개선 불필요합니다.
- 주황색: 개선 가능.
- 빨간색: 개선 사항이 권장됩니다.
{팁: [!]을(를) 사용하여 게임 보기에서 정류장 위의 색상 막대를 전환합니다.}
정류장의 숫자(simuconf.tab에서 옵션을 선택한 경우 정류장 번호가 부여됩니다)입니다
차량 아이콘은 정류장를 사용할 수 있는 차량 유형을 나타냅니다(정류장 정보 및 역 목록에서도 사용됨). 아이콘에는 버스(도로 승객용), 트럭(도로 화물용), 기차, 보트 및 비행기가 포함됩니다. 열차는 역 종류에 따라 버스-아이콘이나 기차-아이콘으로 표시할 수 있다.
화물 아이콘은 정류장이 처리할 수 있는 항목(승객, 상품 및 메일)을 나타냅니다(정류장 정보 및 역 목록에서도 사용됨).
{팁: 적절한 확장을 추가하면 정류장에서 처리할 수 있는 항목의 범주를 변경할 수 있습니다. 우편물 취급을 위해 우체국을 추가할 수 있습니다.br 대기다양한 상품과 승객에 대한 세부 정보

그래프의 노선 관리 정보 오른쪽에 선택한 라인에 대한 이름 상자와 수송 수단 목록이 표시됩니다.

목록에서 노선명을 클릭하여 선택합니다(이름 강조). 옵션 버튼을 클릭하여 선택한 노선과 여기에 할당된 차량에 대한 정보를 그래프에서 전환합니다(x축은 월 단위 시간).

- 남은 수용량 - 상품과 승객을 위한 차량 내 여유 공간의 양

-이용량 - 노선을 사용한 상품과 승객의 양.

-수입 - 차량에서 발생한 소득의 양

- 운영 비용 - 운송 중 차량에 의해 발생하는 비용.

노선: 수송 수단이 이용하고 있는 노선명과 그것이 차고 있는지 보여준다.
그래프 보드 위 항목의 현재 레벨과 구성을 보여준다.

-이익- 소득으로 발생된 비용이 감소함(운영 비용 감소).

- 수송 수단- 라인에 할당된 운송 수단의 수입니다.

노선명 바꾸기: 목록에서 노선명을 클릭하여 선택(이름 강조)한 다음 이름 상자를 클릭하고(그래프 옵션에서 위에 표시된 수송 수단) 새 이름을 입력합니다.

노선을 선택하면(목록에서 노선명을 클릭하여 강조 표시) 라인에 할당된 수송 수단 가 그래프 아래에 나열됩니다.
차량 목록 상단에는 라인으로 배정된 총 수송 수단 수, 소득(운영 비용 감소), 수용량 및 운반한 재화와 승객의 현재 수량(총 수용량 중 괄호 안에 표시)이 있다.
나열된 항목을 클릭하면 해당 운송수단에 대한 자세한 정보를 볼 수 있습니다(슬라이더 바를 사용하여 목록을스크롤).
각 운송수단에 대해 나열된 품목은 다음과 같습니다. 이름(기본적으로 이것은 첫 번째 차량 장치를 구매 하거나 수송 수단를 위해 조립됨)의해 할당 되어진다.
수입은 수익을 나타냅니다(소득으로 인해 운영 비용이 절감됨).
노선: 수송 수단이 이용하고 있는 노선명과 그것이 차고 있는지 보여준다.
그래픽 보드 위 항목의 현재 레벨과 구성을 보여준다.

simutrans-124.5/simutrans/text/ko/list.txt000066400000000000000000000035731520675460500207320ustar00rootroot00000000000000목록 도움말

목록

목록에는 게임의 현재 상태에 대한 정보를 위한 컨트롤을 여는 옵션이 있습니다. (또한 다른 물품, 승객, 우편물의 운송에서 받는 상대적 소득을 조정하는 통제).

플레이어의 정보 목록: 정류장; 수송 수단; 도시 지역; 다른상품, 승객, 우편에서 받는 수입; 산업; 그리고 관광명소.

게임 뷰 상단에 있는 아이콘 목록을 클릭하여 엽니다. 목록.
도구 모음을 열거나 클릭한 후 목록-툴-옵션을 마우스로 가리키면 이름이 표시됩니다.

도구 옵션을 클릭하여 정보를 위한 컨트롤을 엽니다.:

역 목록: 정류장, 운송수단이 화물과 승객들을 픽업하거나 하차하는 장소입니다. (해양 운송 수단을 위한 경유지는 없습니다).

운송 수단 목록: 길과 철도 수송 수단, 항공기와 해양선박들.

모든 도시 목록: 도시 지역 (마을, (소)도시 그리고 (대)도시).

모든 상품 목록: 각각의 상품, 승객 및 우편에서 받은 소득(상대소득을 조정하는 통제 포함).

공장 목록: 공업 (상품의 소비자와 공급자).

관광지 목록: 관광 명소.

simutrans-124.5/simutrans/text/ko/load.txt000066400000000000000000000031431520675460500206670ustar00rootroot00000000000000도움말 불러오기

불러오기

불러오기저장된 게임을 다시 시작하고 (계속하려면) 게임을 삭제하는 데 사용할 수 있습니다. 다시 시작된 게임은 잃어버린 현재 세계를 대체합니다.

불러오기새로운 세계게임 옵션 만들기에서 또는 [L]을 눌러 열립니다. 그리고 사용 가능한 게임의 이름과 날짜 및 시간을 저장합니다.

게임을 다시 시작하려면 목록에 있는 이름을 클릭하십시오; 또는 불러오기의 상단 상자 안에 이름을 입력하십시오: 상자를 클릭하고 이름을 입력한 다음 [Enter] 또는 [Return]을 누르거나 확인을 클릭하십시오. 잘못된 이름은 오류 메시지를 수신합니다: 메시지를 닫고 다시 시도하십시오.

경고: 게임을 즉시 & 영구적으로 삭제하려면 이름에 따라 x 버튼을 클릭하십시오.
이 기능을 주의하여 사용하십시오.

중요사항: Simutrans의 새로운 릴리스는 정기적으로 출시되며 이전 버전의 Simutrans는 이후 버전에서 작동하지 않을 수 있습니다; 이전 게임이 호환되는지 확인하려면 최신 버전의 출시 노트를 읽어보십시오.

불러오기를 닫으려면 취소를 클릭하십시오; 또는 왼쪽 상단의 x를 클릭하십시오; 또는 키보드를 사용하십시오.

simutrans-124.5/simutrans/text/ko/load_relief.txt000066400000000000000000000035421520675460500222200ustar00rootroot00000000000000Height Map 도움말 불러오기

Height Map 불러오기

Height Map 불러오기 윈도우를 사용하여 사용자 지정 지형의 파일을 열고(새 게임을 만들 때 사용) 이러한 파일을 삭제할 수도 있습니다. 새 게임 생성 창에서 열리며 사용 가능한 사용자 정의 지역 파일의 이름, 날짜 및 시간(저장)이 나열됩니다. 일부 파일 이름이 보이지 않으면 목록 오른쪽에 있는 슬라이더 바를 사용하여 이름을 스크롤합니다.

목록에서 이름을 클릭하여 사용자 지정 영역 지도를 불러오거나 창 위쪽의 텍스트 상자에 이름을 입력합니다: 상자를 클릭하고 이름을 입력하고 [Enter] 또는 [Return]를 누르거나 확인을 클릭합니다 (잘못된 이름은 영향을 미치지 않습니다. 다시 시도해야합니다).

경고: 이름 왼쪽에 있는 x-버튼(simuconf.tab으로 설정된 경우)을 클릭하여 파일을 즉시 영구적으로 삭제합니다; 목록에서 파일 이름이 제거되고 Height Map 불러오기창이 닫힙니다. 이 기능은 주의해서 사용하셔야 합니다!

일부 커스텀-지형들은 http://maps.simutrans.com 에서 다운받아야 이용할 수 있습니다. 새 파일은 .ppm 또는 .bmp 형식(최상의 256색 RLE 압축)이어야 하며 simutrans/maps/ 디렉토리에 배치되어야 합니다.

팁: 커스텀 지형을 만드는 방법은 http://wiki.simutrans.com에서 볼 수 있습니다.

취소를 클릭하여 Height Map 불러오기창을 닫거나 좌측 상단의 x-버튼 또는 키보드를 이용하여 닫을 수 있다.

simutrans-124.5/simutrans/text/ko/mailbox.txt000066400000000000000000000050641520675460500214070ustar00rootroot00000000000000메시지 센터 & 메시지 센터 옵션 도움말

메시지 센터

메시지 센터는 게임의 메시지를 나열하고 게임에서 메시지가 표시되는 방식을 선택하는 관리 설정을 엽니다.

게임 시점 상단의 사서함 아이콘을 클릭하거나 [B]를 눌러 메시지 센터를 열면 게임의 메시지가 나열됩니다.
게임 시점을 관련 위치로 이동하려면 메시지를 클릭하십시오.

옵션 버튼 (사용하려면 클릭하십시오)은 메시지 표시 방법을 결정하는 컨트롤이 있는 메시지 센터 옵션을 엽니다.


메시지 센터 옵션

메시지 센터 옵션에는 메시지 표시 방법을 선택하는 세 개의 열이 있습니다.

왼쪽에서 오른쪽으로, 열은 메시지가 다음과 같이 표시될지 여부를 선택합니다: 게임 시점 하단의 스크롤 텍스트; 임시 메시지; 또는 영구 메시지.
{팁: 메시지의 왼쪽 상단 모서리에있는 x를 클릭하거나 키보드를 사용하여 영구 메시지를 닫습니다.}

다음 메시지에 대한 메시지 옵션 (단추를 들여쓰기할 때 선택)을 선택하려면 사각형 단추를 클릭하십시오:

신년: 신년이 시작되었습니다.

AI 뉴스: AI 플레이어가 새로운 노선을 열었습니다.

도시 뉴스: 도시 지역은 더 큰 타운홀을 만들고 있습니다.

경로 없음: 차량은 다음 목적지 (정류장 또는 경유지)까지의 경로를 찾을 수 없습니다.

새로운 산업: 도시 성장으로 인해 새로운 산업 체인 (최종 소비자 및 필요한 공급자)이 건설되었습니다.

관광: 도시 관광지나 기념탑이 세워졌습니다.

새로운 차량: 새 차량을 사용할 수 있게 되거나 차량이 폐기됩니다 (타임라인이 사용되는 경우).

역 혼잡: 은 매우 붐빕니다.

문제: 플레이어 회사에는 부채 및 기타 비판적인 메시지가 있습니다.

simutrans-124.5/simutrans/text/ko/map.txt000066400000000000000000000172011520675460500205250ustar00rootroot00000000000000지도 창 도움말

지도 창

지도는 지형, 중요한 위치 및 교통망에 대한 정보를 제공하는 현재 세계의 지도를 제공합니다. 또한 전 세계로 이동하는 방법을 제공합니다.

상단 메뉴에서 지도 아이콘을 클릭하거나 [m]을 눌러 지도를 열면 크기가 재조정되거나 최소화될 수 있습니다. 가장 위에 있는 3개의 버튼으로 추가 옵션을 사용할 수 있습니다.
 선택 항목에는 지도의 추가 정보를 덮어씌우는 다양한 버튼이 표시됩니다.
 색상 코드는 일부 옵션에 대한 활동 수준을 보여주는 최소-최대 색상 크기를 보여줍니다.
 산업 목록은 지도에 표시된 산업의 색상을 가진 테이블을 엽니다.

지도에는 다른 확대 / 축소 수준이 있습니다: 마우스 휠, 페이지 위/ 아래 또는 버튼을 사용하여 변경하십시오. 등각 투영지도 버튼을 사용하여 지도를 기울여 디스플레이와 일치시킬 수도 있습니다.

지도에서 물은 파란색이고 낮은 땅은 높은 땅보다 더 어둡습니다. 현재 화면에 보이는 세계의 일부는 노란색 사각형 윤곽으로 표시됩니다. 이 시야를 전세계로 이동하려면 원하는 위치에서 지도를 마우스 왼쪽 버튼으로 클릭하거나 노란색 사각형을 마우스 왼쪽 버튼으로 드래그합니다. 마우스 오른쪽 버튼을 클릭하여 스크롤 바를 사용하지 않고 지도 창에서 영역을 이동할 수 있습니다.

기본 지도에서 (옵션을 사용하지 않을 때) 도시 지역과 관광지는 회색, 전력선은 청록색, 터널은 갈색, 고가 도로는 밝은 갈색, 다리는 밝은 회색입니다. 지상의 길에는 고유의 색상이 있습니다: 도로는 거의 검은색으로 보입니다; 레일과 노면 전차 트랙은 어두운 갈색; 수로는 파란색; 활주로는 진한 노란색입니다. 다른 모든 방법 (좁은 게이지, 자기 부상 철도 또는 모노레일 트랙과 같은)은 밝은 빨간색입니다. 정류장은 빨간색입니다. 길에 있는 차량은 노란색입니다.

버튼 네트워크는 지도 상의 지하철 노선도와 같은 추상적인 노선 네트워크를 덮어씌웁니다. 노란색 버튼을 사용하여 승객, 우편물 또는 화물 연결만 표시하도록 제한할 수 있습니다.

선택 버튼을 사용하여 많은 추가 정보를 중첩할 수 있습니다. 이들은 세 그룹으로 나뉩니다. 흰색과 노란색 버튼은 독점적이며 다른 모든 흰색 옵션을 비활성화합니다. 녹색 버튼은 다른 버튼과 결합할 수 있는 중첩용 버튼입니다. 세 번째 그룹은 정류장을 위한 빨간 버튼입니다.

다음의 옵션이 존재합니다:

도시 지역도시 이름.
도시 한계은 도시의 한계를 보여줍니다; 새로운 주택과 도로는 도시의 한도 내에서만 지어집니다.
건물은 각 도시의 건물 레벨에 따라 색상이 지정됩니다. 레벨에 따라 승객 또는 우편물을 생성할 확률이 결정됩니다.
목적지로 도시가 선택된 경우, 이 옵션은 현재 승객의 목적지를 덮어씌웁니다 (도시 정보의 작은 지도와 유사.)
관광지는 관광 명소의 위치에 원을 그립니다. 색상과 크기는 이 관광 명소가 생성하는 교통량을 나타냅니다 (최소-최대 규모 사용).
공장: 산업 (소비자 및 공급자)을 강조 표시합니다. 이러한 중첩이 활성화되어 있고 마우스 포인터가 지도 상의 산업을 가리키는 경우, 고객을 연결하는 이름과 흰색 선이 표시됩니다. 공급자에게 빨간 선을 표시하려면 [Shift]를 누르십시오. 색상은 산업 목록에서 볼 수 있는 산업의 색상입니다.
승객은 각 소유자의 색상으로 승객을 위한 운행 권역을 정류장별 색상으로 표시합니다. 네트워크 중첩이 활성화되어 있는 경우, 승객 연결만 표시됩니다.
우편물은 각 소유자의 색상으로 우편물 배송 권역을 정류장별 색상으로 표시합니다. 네트워크 중첩이 활성화되어 있는 경우, 승객 연결만 표시됩니다.
모든 방법으로 운송되는 화물 및 승객의 화물 레벨 (최소-최대 규모 사용). 네트워크 중첩이 활성화되어 있는 경우, 화물 연결만 표시됩니다.
정류장 상태는 (및 이후의 모든 빨간색 옵션) 원을 기준으로한 정류장을 강조 표시합니다. 정류장 상태 색상은 정류장의 혼잡 상태를 나타냅니다. 역 목록의 상태-색상-바, 정류장 정보, 노선 관리, 마지막으로 정류장의 이름 위에 있는 색상-바에 동일한 색상이 표시됩니다. 크기는 승객에 대한 정류장의 수용량을 나타냅니다.
대기정류장에 운송 대기 중인 승객, 우편물 및 화물의 양을 표시합니다. 색상 및 크기는 대기 중인 양을 나타냅니다 (최소-최대 스케일 사용).
큐잉은 이번 달 안에 승객, 우편물 및 기다리는 화물의 양이 얼마나 많이 변경되었는지를 보여줍니다. 크기는 전체 변경을 나타내며 색상은 감소 시 녹색이고 증가시 빨간색입니다 (최소-최대 배율 사용).
서비스 색상과 크기는 이번 달에 역을 운영한 차량 수를 나타냅니다 (최소-최대 규모 사용).
전송 크기 및 색상은 역에서 얼마나 많은 사람들이 도착/ 출발했는지 나타냅니다 (최소-최대 규모 사용).
출발지 색상과 크기는 정류장에서 여행을 시작하려는 승객과 우편물의 수, 즉 행복하고 불행한 승객이없는 경우를 보여줍니다 (최소-최대 규모 사용).
교통량은 한 달 안에 지나가는 차량 수에 따라 색상을 바꿉니다 (최소-최대 스케일 사용).
선로레일 및 노면전차 트랙, 교량 및 터널은 흰색 (전기라면 빨간색)으로 표시됩니다. 신호는 노란색으로 표시됩니다.
속도 제한은 허용되는 최대 속도와 관련하여 모든 면에서 색상을 지정합니다 (최소-최대 스케일 사용).
차고차고를 강조 표시합니다. 색상은 운송 유형을 나타냅니다: 선박, 노란색; 도로, 주황색; 선로, 빨간색; 항공기, 녹색; 노면전차, 파란색; 그리고 모노레일, 회색.
전력선송전 선로에 부하를 나타냅니다.
은 밝은 녹색 숲으로 표시됩니다.
소유권은 지도에 있는 모든 것을 소유자의 색상으로 표시합니다 (소유자가 없는 경우 주황색).

simutrans-124.5/simutrans/text/ko/monorailtools.txt000066400000000000000000000207571520675460500226630ustar00rootroot00000000000000모노레일/ 자기부상 열차 도구 도움말

모노레일/ 자기부상 열차 도구

모노레일/ 자기부상 열차 도구는 모노레일/자기부상열차 수송망을 구축합니다. 이 도구는 건설 및 제거 작업을 수행합니다: 모노레일과 자기부상 열차의 트랙; 다리; 터널; 신호등; 역; 역 플랫폼; 그리고 확장합니다. 플레이하며 타임라인이 추가된다면, 시간이 지나면서 Simutrans에서 더 많은 도구 옵션이 나타날 수 있습니다.

게임 시점 맨 위에 있는 모노레일/자기부상 열차 - 아이콘을 클릭하여 도구 모음을 엽니다.
도구 모음을 열거나 클릭한 후 마우스 커서를 도구 옵션에 가져가서 이름 및 해당되는 경우 확인: 건설 비용 유지비, 최대 속도 제한 및 최대 길이.

모노레일/자기부상 열차 도구 옵션은 왼쪽에서 오른쪽으로 포함합니다:

모노레일과 자기부상 열차의 트랙: 이 도구는 선택된 두 지점에 모노레일과 자기부상 열차의 트랙을 건설합니다, 트랙은 경사면 방향으로만 경사로에 건설될 수 있으며, 거친 지형, 물 및 장애물을 가로 지르는 경로는 찾을 수 없습니다.
새 트랙은 경로에 기존 트랙을 사용할 수 있습니다.
트랙을 건설하려면: 도구를 클릭하여 트랙(커서를 트랙으로 변경)을 선택한 다음, 게임 시점의 시작점(마지막 클릭 게임 시점에 불도저 표시), 하단 바의 게임 오른쪽에 좌표를 표시합니다.
{팁: 서로 다른 유형의 트랙이 연결될 수 있습니다(그러나 일부 다른 플레이어들에 의해 구축되지 않은 경우).
지형 도구를 이용하여 지형을 트랙이 지나갈 수 있게 변경합니다.
개별적인 트랙의 일부분과 장애물을 제거하려면 파괴/ 제거를 사용하면됩니다.
[Ctrl] 을 사용하여 동시에 추가 기능을 수행할 수 있습니다.
마지막 경로 공사를 [z]키로 취소해도 공사 비용을 환불하지 않습니다.}

모노레일 제거: 이 도구는 차량이 없을 때 게임 시점의 두 지점 사이에 모노레일/ 자기 부상 열차 트랙을 제거합니다(플랫폼 및 경로 신호도 제거됩니다).
도구를 사용하면 건설 비용이 발생합니다.
트랙을 제거하려면: 도구 클릭(커서를 빨간색 X로 변경);
삭제할 트랙에서 암 클릭(게임 뷰에서 빨간색 X 표시 삭제 지점 선택);
그리고 마지막으로 트랙의 다른 지점을 클릭하여 첫 번째 삭제 지점부터 마지막 클릭 지점까지의 부분들을 제거합니다.
{팁: 트랙 제거를 위한 다른 플레이어처럼 행동합니다.}

경사로 그리고 교량: 이 도구는 모노레일/ 자기 부상 열차가 통과할 수 있도록 게임 시점의 모노레일/ 자기 부상 열차 트랙 두 개 사이에 직선 교량을 구축한다.
교량의 최대경간.
이 도구는 선로 끝에서 적절한 장소까지 제작합니다(다른 선로 끝 또는 약간 높은 지면, 범위 이내).
경사로와 교량을 제작하려면: 도구를 클릭한(교량으로 커서 변경) 다음, 구축할 트랙의 끝(교량의 시작점)을 클릭합니다.
교량 끝에 적합한 장소가 부족하거나 일부 방해물이 있다면 교량 시공이 막힙니다: 교량에 연결할 양쪽의 트랙을 배치하고 다시 시도하십시오.
{팁: 파괴/ 제거로 교량(교량 끝 클릭) 및 교량 건물에 대한 몇 가지 장애물을 제거하십시오. 트랙 도구를 사용하여 교량 끝자락을 트랙에 연결합니다.}

모노레일 신호등: 게임 시점 트랙에서 모노레일과 자기 부상 열차를 위한 신호등 제작 도구.
신호는 트랙 & 교량, 교차로 & 정류장 차량 픽업 및 하차)에서 차량의 흐름을 직접 조정하고 조절합니다.)
양방향 및 단방향 버전의 신호등을 작성할 수 있습니다.
트랙 위 양방향 신호등 제작하려면: 도구를 클릭하여 신호 선택(커서를 신호등으로 변경); 그리고 트랙을 클릭합니다.
단방향 신호등 제작하려면: 신호등 커서로 같은 지점에서 다시 클릭 하면 단방향 신호와 양방향 신호를 번갈아 표시합니다..
중요사항: 차량의 기본적인 주행은 우측 주행이며, 이를 무시한 체로 신호등을 설치하지 마십시오.(simuconf.tab에서 변경할 수 있음).
- 신호등: 차량은 운행시간표 (정류장 혹은 경유지)에 맞추어 다른 차량이 없는 경우, 다음 신호등까지나 목적지까지 이동합니다.
단방향 모드에서는 차량이 한 방향으로만 이동합니다.
- 선행 신호등: 전방의 트랙 영역이 다른 차량에 의해 점유되지 않을 경우 차량이 진행됩니다. (3개의 연속 신호등 또는 일정의 다음 목적지 사이에서)
단방향 모드에서는 차량이 한 방향으로만 통과할 수 있습니다.
- 신호등 선택: 차량을 여러 플랫폼 정류장에서 빈 역 플랫폼으로 유도: 이 신호등을 통과하는 차량은 일정에 할당된 플랫폼뿐만 아니라 다음 목적지에서 빈 플랫폼을 사용할 수 있습니다.
빈 플랫폼이나 다음 목적지로 가는 명확한 경로가 발견되지 않으면 차량은 신호등에서 대기합니다.
{팁: 파괴/ 제거로 신호등을 제거하십시오.
[Ctrl]을 눌러 하단 트랙 위에 있는 다리에 신호등을 배치합니다.}

모노레일 차고지: 이 도구는 모노레일/ 자기 부상 열차의 차고를 매입 및 관리하기 위해 사용됩니다. 차고지는 유지관리 비용이 발생하며 게임 시점의 모노레일/ 자기 부상 열차 트랙의 끝에 건설 가능합니다.
모노레일 차고지를 제작하려면: 도구를 클릭한 다음(커서를 차고지로 변경), 트랙의 끝을 클릭합니다.

열차-정류장: 이 도구는 모노레일/ 자기 부상 열차가 화물, 승객 및 우편물을 싣고 내리기 위한 역-플렛폼을 건설합니다.
기존 정류장 에 인접해 구축되지 않은 역-플랫폼은 새로운 정류장을 만듭니다.
열차-정류장에는 유지 관리 비용이 들고, 선로 위에 건설됩니다 (커브 구간과 교차로는 제외).
열차-정류장에는 화물, 승객, 우편물 운행 권역이 있습니다.
각각의 역 플랫폼은 화물, 승객 및 우편물에 대해 서로 다른 수용량을 가질 수 있습니다.
일부 도구 옵션의 코너에서 아이콘 (역 목록정류장 정보에서 사용) 은 역 플랫폼이 처리할 수 있는 항목을 표시합니다.
역-플렛폼 제작하려며: 도구를 클릭하여 선택(커서를 플랫폼으로 변경)한 다음 트랙을 클릭합니다.
{팁: 파괴/ 제거로 열차-정류장을 제거하십시오.
더 많은 열차들을 수용할수 있도록 역-플랫폼을 확장 하거나 여러 플랫폼 정류장을 건설합니다(인접 트랙에 더 많은 플랫폼 섹션 건설)
[v]를 눌러 게임 시점에 화물 및 승객의 운행 권역을 표시하거나 숨깁니다.
[Ctrl]을 눌러 교량 또는 하부 트랙 위에 고가 트랙을 구축합니다.}

모노레일 토대: 이 도구는 정류장에 대한 확장을 구성하며 정류장의 수용량을 증가시킨다.
모노레일 토대는 모노레일/ 자기 부상 열차의 트랙 끝에 건설되며, 유지 비용이 발생합니다.
모노레일 토대를 제작하려면: 도구를 클릭하여 선택한 다음 (커서를 토대로 변경), 트랙 끝에서 필요한 끝 위치를 클릭합니다.
{팁: 파괴/ 제거로 모노레일 토대를 제거하십시오.}

simutrans-124.5/simutrans/text/ko/new_world.txt000066400000000000000000000137401520675460500217540ustar00rootroot00000000000000새 게임 생성 도움말

새 게임 생성

새 게임 만들기 창은 새 게임에 대한 옵션을 설정합니다. 이렇게 하면 선택한 설정으로 새 지도가 만들어집니다. 지형의 크기, 도시 지역의 초기 수 및 산업의 수를 포함하여 많은 게임 변수를 구성할 수 있습니다. 선택한 지형의 미리보기가 새 게임 만들기 창에도 표시됩니다.

이 창을 열면 다른 모든 게임 옵션이 비활성화됩니다. 새 게임 만들기 창을 닫아 현재 게임으로 돌아가려면 제목 바에있는 x 버튼을 클릭하십시오.

화살표 버튼은 설정을 조정하고 버튼은 옵션을 켜거나 끄거나 새 컨트롤을 엽니다.

지도 번호 - 게임에 사용할 지형을 선택합니다.
지형을 순환하려면 숫자 상자를 클릭하고 필요한 지도 번호를 입력하거나 화살 버튼을 사용하십시오. 미리보기 영역에는 게임에 표시되는대로 선택된 지형이 표시됩니다 (물은 파란색, 낮은 땅은 높은 땅보다 어둡습니다).

크기 - 지도 크기를 W x H로 설정합니다. 상단에 있는 숫자 상자는 너비이고 그 아래에는 높이가 있습니다. 지도에 필요한 메모리는 대괄호 안에 표시됩니다.
참고: 큰 지도는 더 많은 메모리가 필요하며 생성에 더 많은 시간이 필요합니다. 게임 및 기타 그래픽은 pak64에서 추가 88MB, pak128에서 144MB가 필요합니다. 일반적으로 256MB RAM이 장착된 컴퓨터에서는 512 x 512보다 훨씬 큰 지도를 사용하지 마십시오.

랜덤 지도 - 랜덤으로 새로운 지형을 선택합니다. 다른 설정은 변경되지 않습니다.

Height Map 불러오기 - 커스텀 지형을 불러옵니다. Height Map 불러오기 창을 엽니다.

도시의 수 - 게임의 초기 도시 지역 수를 설정합니다. 지도가 너무 작거나 거친 경우, 도시가 적게 만들어집니다. 공용 플레이어로 전환하여 새로운 도시를 수동으로 추가할 수 있습니다.

도시 당 평균 시민 - 모든 도시 지역의 평균 초기 인구를 설정합니다. Simutrans는 대도시보다 작은 마을을 창출하려고 노력할 것입니다.

도시 간의 도로 길이 - 게임 시작 시 도시 지역 사이의 공용 도로의 ​​최대 길이를 설정합니다. 이 값을 더 높게 설정하면 도시 지역을 더 쉽게 연결할 수 있습니다.

공장과 가게 - 도시 지역 밖의 산업 공급망의 초기 수를 설정합니다. 도시 지역 인구가 증가함에 따라 더 많은 산업 체인이 자동으로 나타납니다.
참고: 지형에 언덕이 매우 많은 경우, 산업 체인을 만드는 데 적합한 평지의 공간이 없기 때문에 더 적은 수의 산업이 나타납니다.

관광 명소 - 승객과 우편물을 유치하는 도시 외 장소의 초기 개수를 설정합니다. 관광 명소는 게임 시작 시 새로운 도시나 도시에서 생성 된 관광 명소 외에 도시 외곽 지역에도 나타납니다.

연도별 타임 라인 사용 - 게임의 시작 연도를 설정합니다. 시간 기반 제약 조건을 적용하여 게임을 즐기려면 사각형 버튼을 클릭하십시오. 게임에서 시간이 지남에 따라 더 많은 차량, 건물 및 산업이 나타납니다.

이 옵션을 선택하지 않으면 게임에서 모든 차량, 건물 및 산업을 사용할 수 있습니다. 도시 지역은 모든 시대의 건물에 나타나고 느린 차량은 빠른 차량만큼 수익이 높지 않으며 수입은 운송 차량의 평균 속도로 계산됩니다.

입문자 모드 사용 - 게임 플레이에서 화물 및 승객을 운송하는 데 더 높은 수준의 수입을 받을 수있는 옵션을 설정합니다. 기본적으로 '입문자 모드'로 재생하면 정상 모드에서 재생할 때 받은 값의 150%를 받게됩니다. 또한, 소비자(s)가 초과 공급을 할 경우 산업계는 공급을 중단하지 않을 것입니다.

설정 - 확장 설정 창이 열립니다. simuconf.tab 파일에서도 사용할 수 있는 모든 매개 변수에 접근할 수 있습니다.

풍경 설정 - 다양한 지도 설정을 제어하는 ​​풍경 설정 창을 엽니다: 조도, 수위, 기후, 숲, 하천.

게임 불러오기 - 저장된 게임을 불러옵니다. 이전에 저장된 게임을 다시 시작할 수 있게 제어를 통해 게임 불러오기 창이 표시됩니다. 저장된 게임이 불러오기되면 확인 없이 현재 게임을 대체합니다.

시나리오 불러오기 - 시나리오를 불러옵니다. 즉, 특정 목표를 달성해야 하는 게임입니다. 이것은 일반적인 개방형 Simutrans 플레이 스타일의 대안입니다.
참고: 일부 팩셋에는 시나리오가 없을 수 있습니다.

게임 시작 - 선택한 설정을 사용하여 새로운 세계를 만들어 게임을 시작합니다. Simutrans 플레이를 시작하려면 이 버튼을 클릭하십시오. 행운을 빕니다. 게임을 즐기십시오!

나가기 - 게임을 종료하려면 이 버튼을 클릭하십시오.

팁: Simutrans를 시작할 때 기본 값을 변경하거나 다른 전역 속성을 변경하려면 simuconf.tab 파일에서 값을 수정하십시오.

simutrans-124.5/simutrans/text/ko/options.txt000066400000000000000000000030331520675460500214410ustar00rootroot00000000000000게임 옵션 도움말

게임 옵션

게임 옵션에서는 게임의 작동과 표시 방식을 조절할 수 있습니다; 또한 게임 옵션에서 저장, 불러오기, 게임 종료, 새 게임 시작 등을 할 수도 있습니다.

게임 시점 상단에 있는 플로피디스크 아이콘을 클릭하여 게임 옵션을 엽니다.

새로운 컨트롤을 열려면 버튼을 클릭하십시오:

언어: 사용하는 언어를 설정합니다.

플레이어: 플레이어들을 관리합니다.

플레이어 색상: 플레이어 회사의 색상을 설정합니다.

디스플레이:게임이 표시되는 방식을 설정합니다.

소리:효과음과 BGM을 설정합니다.

새 지도:현재의 게임에서의 도구 바를 모두 닫고, 게임 시작 창이 뜹니다.

불러오기:이전에 저장한 게임을 불러옵니다.

저장:현재 게임을 저장합니다.

시나리오 불러오기:저장된 시나리오를 불러옵니다.

나가기:Simutrans를 종료합니다. 게임이 저장되지 않습니다.

simutrans-124.5/simutrans/text/ko/password.txt000066400000000000000000000024231520675460500216120ustar00rootroot00000000000000 플레이어 이름 및 암호 변경 도움말

플레이어 이름 및 암호 변경

플레이어 이름 및 암호 변경에는 두가지 방법이 있습니다:

플레이어 목록에서
플레이어 이름 오른쪽에 있는 상자를 클릭하거나 암호로 보호된 플레이어를 변경하여 명령을 실행합니다.

플레이어 이름 오른쪽에 녹색 상자가 있으면 암호가 보호되지 않으며, 녹색 상자를 누르고 암호를 입력하여 권한 및 암호를 보호할 수 있습니다. 이름을 선택할 수 없는 경우 올바른 암호를 지정해야 합니다.

암호가 없거나 잠금 해제된 플레이어의 이름은 맨 위에 있는 텍스트를 편집하여 변경할 수 있습니다.

암호로 보호된 플레이어에 플레이어 목록의 이름 오른쪽에 빨간색 상자가 표시됩니다. 올바른 암호를 입력하면 상자가 녹색으로 바뀌고 해당 플레이어로 플레이할 수 있습니다. 입력한 잠금 해제 암호가 로컬에 유지되므로 프로그램을 다시 시작하지 않는 한 암호를 한 번만 입력하면 됩니다.

simutrans-124.5/simutrans/text/ko/players.txt000066400000000000000000000064411520675460500214330ustar00rootroot00000000000000플레이어 목록 도움말

플레이어 목록

플레이어 목록에서는 플레이어들의 정보와 다른 플레이어의 회사를 관리할 수 있습니다.

플레이어 목록게임 옵션의 플레이어 버튼이나 [k]버튼을 누르면 나타나고, 거기서 플레이어의 이름과 재정 상태가 표시됩니다.

총 14명의 플레이어가 있습니다: 인간, 공용 서비스와 12개의 빈 슬롯으로 구성되어 있습니다. 빈 슬롯에는 다른 인간 플레이어나 2종류의 AI로 채울 수 있습니다.

왼쪽의 네모 버튼을 누르면, 플레이어가 활성화됩니다. 그 플레이어가 AI라면, 그 버튼으로 멈추거나 계속 확장하게 할 수 있습니다. 게임 도중 AI를 끄더라도 AI의 교통 네트워크는 없어지지 않습니다.

화살표 버튼을 누르면 그 플레이어의 회사를 운영할 수 있습니다. 플레이어를 바꾸면 확인 메시지가 뜹니다: 메시지의 왼쪽 상단 모서리에 있는 x를 클릭하거나 키보드를 사용하여 닫을 수 있습니다.

자신의 재정 상태를 보려면 플레이어 이름을 클릭하십시오. 플레이어 슬롯이 지정되지 않은 경우 이 슬롯을 차지할 플레이어 종류를 선택할 수 있습니다. 그것을 활성화하려면 가장 왼쪽 상자를 사용하십시오.

암호로 플레이어를 보호 할 수 있습니다. 보호 된 플레이어로 게임을 하는 경우 많은 통계에 접근할 수 있지만 이 플레이어와 관련된 게임은 변경할 수 없습니다.
보호된 플레이어는 그의 이름 오른쪽에 빨간색 상자로 표시됩니다. 암호가 설정되어 있지 않거나 플레이어의 잠금이 해제된 경우이 상자는 녹색으로 표시됩니다. 암호를 입력하거나 설정할 수있는 암호 대화 상자를 열려면 상자를 클릭하십시오.

네트워크 게임의 경우 플레이어 간의 협력이 바람직할 수 있습니다. 당신은 대중들에게 공개할 수 있습니다. 모든 플레이어는 연결하거나 직접 운전하여 공용 정류장으로 이동할 수 있습니다. 또한 연결 타일을 공개함으로써 트랙을 공유할 수 있습니다. 이러한 트랙에서 어떤 회사를 운영될 수 있는지에 대한 통제는 게이트를 통해 수행될 수 있습니다.

플레이어는 아이템을 만들거나 다른 플레이어의 트랙이나 도로에 경유지를 배치하거나 다른 플레이어가 만든 아이템을 제거할 수 없습니다 (단, 공공 서비스 플레이어가 만든 일부 아이템은 파괴/ 제거를 사용하여 제거할 수 있습니다).

하단에는 자유플레이 모드를 활성화하는 옵션이 있습니다 (공개 플레이어가 잠겨 있지 않은 경우). 이 모드에서는 파산하지 않습니다. 그렇지 않으면 부정적인 회사 가치를 얻은 후 플레이어는 제거되고 1년 후에 슬롯이 해제됩니다.

simutrans-124.5/simutrans/text/ko/privatesign_info.txt000066400000000000000000000012351520675460500233160ustar00rootroot00000000000000개인용 입장 게이트

개인용 입장 게이트

이 설명글 읽고 선택 취소된 모든 플레이어가 자신의 차량으로 이 문을 통과해 진입하는 것을 거부할 수 있습니다. (이 설정은 도시의 모든 차에게 영향을 준다.) 통과가 허용된 플레이어는 열린 게이트나 장벽을 볼 수 있으며, 제외된 플레이어는 닫힌 게이트나 장벽을 볼 수 있습니다.

이 기호는 건물 도구 모음 중 하나를 통해 제작되었습니다. (철도 도구 or 도로 도구같은).

simutrans-124.5/simutrans/text/ko/railtools.txt000066400000000000000000000247571520675460500217760ustar00rootroot00000000000000철도 도구 도움말

철도/열차/선로 도구

철도 도구는 기차 운송 네트워크를 구성합니다. 도구가 생성될 수 있습니다: 선로 트랙 (그리고 그것을 전화 또는 제거); 터널; 신호; 차고지; 역 플랫폼; & 증축. 타임 라인을 가지고 플레이하는 경우, Simutrans에서 시간이 지남에 따라 더 많은 도구 옵션이 나타날 수 있습니다.

게임 시점의 상단에 있는 열차 아이콘을 클릭하여 도구 바를 엽니다. 마우스 커서를 도구 옵션 위에 올리거나 (도구 바를 열거나 클릭 한 후) 이름을 보고 적절한 위치에 표시하십시오: 건설 비용, 괄호 안의 유지 보수 비용, 최대 속도 제한 및 최대 길이.

철도 도구는 왼쪽에서 오른쪽으로 포함합니다:

선로 트랙: 도구는 두 지점 사이에서 철도 차량을 위한 트랙을 만듭니다. 트랙은 경사면 방향으로 경사면에만 건설할 수 있으며 거친 지형, 물 및 장애물을 가로 지르는 경로를 찾을 수 없습니다. 새 트랙이 만들어지면 경로의 기존 트랙을 사용할 수 있습니다.
트랙을 건설하려면: 도구를 클릭하여 트랙을 선택한 다음 (트랙으로 커서 변경), 트랙의 시작점에 대한 게임 시점 (게임 시점의 불도저 표시 및 게임 시점의 하단 막대 오른쪽에 있는 지도 좌표 표시)를 클릭합니다. 마지막으로 트랙의 종점에 대한 게임 시점을 클릭하십시오.
{팁: 다른 유형의 트랙이 연결될 수 있습니다 (그러나 다른 플레이어가 제작한 트랙은 연결할 수 없습니다). 거친 지형을 가로 질러 트랙을 연결하거나 장애물을 피하려면 다리 및 터널을 사용하십시오. 트랙의 경로를 사용하려면 풍경 도구를 사용하여 지형을 변경하십시오. 파괴/ 제거를 사용하여 개별 트랙 조각 및 일부 장애물을 제거하십시오. 추가 기능을 위해 [Ctrl]을 동시에 사용하십시오. 마지막으로 사용 된 트랙 도구를 선택하려면 [t]를 누르십시오.}

전화 트랙: 도구는 전기 자동차용 게임 시점에서 두 지점 간 트랙, 교량 및 터널에 전기를 공급합니다.
트랙에 전기를 통하게 하려면: 도구를 클릭하거나 [e] (전기 트랙 아이콘으로 커서 이동)를 눌러 선택한 다음 전화의 시작점에 대한 게임 시점에서 트랙을 클릭하고 (게임 시점에서 전기 트랙 아이콘 배치) 마지막으로 트랙의 두 번째 점을 클릭하여 시작점까지 트랙의 일부분을 표시합니다.
{팁: 파괴/ 제거를 사용하여 비 전기 트랙으로 다시 변경하십시오.}

트랙 제거: 도구는 게임 보기의 두 지점 사이에 차량이 없을 때 트랙 및 전기 트랙을 제거합니다 (참고하십시오: 경로에 있는 플랫폼, 신호, 터널 및 교량도 제거됩니다). 도구를 사용하면 건설 비용이 발생합니다.
트랙을 제거하려면: 도구를 클릭하십시오 (커서가 빨간색 십자 기호로 바뀝니다); 트랙을 클릭하십시오 (게임 시점에서 빨간색 십자가로 표시된 삭제 점을 선택하십시오); 마지막으로 트랙의 두 번째 점을 클릭하여 첫 번째 삭제 점까지 부분을 제거합니다.
{팁: 다른 플레이어로 작동하여 트랙을 제거하십시오.}

선로 교량: 도구는 철도 차량이 지나갈 수 있도록 게임 시점에서 두 개의 트랙 사이를 직선으로 연결합니다. 교량에는 최대 경간이 있습니다.
도구는 트랙 끝에서 적절한 위치 (경간 내 다른 트랙 끝 또는 약간 높은 쪽)에 다리를 만듭니다.
교량을 건설하려면: 도구를 클릭하여 선택한 다음 (커서를 교량으로 변경), 트랙 끝 (교량의의 시작점)을 클릭하여 건설하십시오.
교량 끝 부분에 장애물이 있거나 적절한 장소가 없는 경우, 교량이 막히는 것을 방지합니다: 교량의 양면에 트랙을 놓고 다시 시도하십시오.
{팁: 다리를 제거하려면 파괴/ 제거를 사용하십시오 (교량의 끝을 클릭하십시오). 건물을 연결하기위한 장애물이 있습니다. 트랙 도구를 사용하여 교량의 끝을 트랙에 연결합니다.}

선로 터널: 이 도구는 철도 차량이 지상을 통과할 수 있도록 직선 터널을 구축하여 게임 시점의 두 트랙 사이에 놓습니다. 터널은 기울어질 수 없습니다: 차량은 동일한 높이로 출입합니다.
이 도구는 경사면에 위치한 트랙 끝에 터널을 만듭니다. 터널을 건설하려면: 도구를 클릭한 다음 (커서를 터널로 변경), 경사면의 트랙 끝을 클릭하여 터널 입구를 찾습니다.
터널이 적당한 출구 (장애물이 없는 경사면)를 찾지 못하면, 그것은 건설되지 않을 것입니다. 연결될 양면에 트랙을 놓고 다시 시도하십시오.
{팁: 터널 출입구에 대해 적절한 장소를 생성하려면 풍경 도구 및 파괴/ 제거를 사용하십시오. 트랙 도구를 사용하여 터널 출구를 트랙에 연결하십시오.}

선로 신호: 도구는 게임 시점의 트랙에서 철도 차량 신호를 만듭니다. 신호는 트랙 및 교량, 교차점 및 정류장에서 차량의 흐름을 직접 제어합니다.
신호의 양방향 및 단방향 버전을 구축할 수 있습니다. 트랙에서 양방향 신호를 구축하려면: 도구를 클릭하여 신호를 선택하십시오 (커서를 신호로 변경); 트랙을 클릭하십시오. 단방향 신호를 구축하려면: 단방향 신호를 순환하고 양방향 신호로 돌아가려면 신호-커서를 사용하여 같은 지점에서 다시 클릭하십시오.
중요사항: 차량이 목적지에 도달하지 못하도록 단방향 신호를 보내지 않도록 주의하십시오. 기본적으로 차량은 오른쪽으로 주행합니다 (simuconf.tab에서 변경할 수 있습니다).
신호를 클릭할 때 제어 키를 누르고 있는 경우, 다중 신호 구축기가 활성화됩니다.
- 신호: 차량이 다음 신호 또는 목적지의 일정 (정류장 또는 중간 지점)까지 다른 차량에 의해 점유되지 않은 경우에만 진행됩니다. 단방향 모드에서 차량은 한 방향으로만 지나갑니다.
- 선로 닫힘: 차량이 신호 이상으로 진행하지 않습니다 (단방향 모드에서만 사용 가능).
- 선행 신호: 선행 신호 차량은 트랙 전방 (3개의 연속 신호 사이 또는 일정의 다음 목적지까지) 영역이 다른 차량에 의해 점유되지 않는 경우, 계속 진행됩니다. 단방향 모드에서는 차량이 한 방향으로만 지나가게 합니다.
- 선택 신호: 다중 플랫폼 정류장에서 빈 역 플랫폼으로 차량을 안내합니다: 이 신호를 통과하는 차량은 일정에 지정된 것뿐만 아니라 다음 목적지에서 빈 플랫폼을 사용할 수 있습니다. 빈 플랫폼이나 다음 목적지까지 가는 명확한 경로가 없는 경우, 차량은 신호를 기다립니다.
{팁: 파괴/ 제거로 신호를 제거하십시오. 다리 위의 신호를 더 낮은 트랙 위에 놓으려면 [Ctrl] 키를 길게 누르십시오.}

열차 차고지: 도구는 열차 및 객차 매입 및 관리를 위한 차고지를 건설합니다. 차고지는 유지 보수 비용이 필요하며 게임 시점에서 트랙의 끝 부분에 건설됩니다.
열차 차고지를 건설하려면: 도구를 클릭한 다음 (커서를 저장소로 변경), 트랙의 끝을 클릭하십시오.
{팁: 파괴/ 제거로 차고지를 제거하십시오. 차고지 전기 트랙에서만 전기 차량을 표시합니다.}

선로-정류장: 도구는 철도 차량이 화물, 승객 및 우편물을 싣고 내리는 데 사용하는 역 플랫폼을 건설합니다.
역 플랫폼은 기존의 정류장에 인접하지 않으면, 새로운 정류장을 만들 수 있습니다.
선로-정류장은 유지 보수 비용이 있으며 트랙에 건설되어 있습니다 (그러나 트랙의 굴곡 및 교차점에는 해당되지 않습니다).
선로-정류장에는 화물, 승객 및 우편물을 위한 운행 권역이 있습니다. 다른 역 플랫폼은 화물, 승객 및 우편물에 대해 서로 다른 수용량을 가질 수 있습니다. 일부 도구 옵션의 모서리에 아이콘 (역 목록 및 정류장 정보에 사용)은 역 플랫폼이 정류장를 처리할 수 있는 항목을 보여줍니다.
역 플랫폼을 건설하려면: 도구를 클릭하여 선택한 다음 (커서를 플랫폼으로 변경), 트랙을 클릭하십시오.
{팁: 파괴/ 제거로 선로-정류장을 제거하십시오. 더 길고 더 많은 차량을 수용하고 용량 및 운행 권역을 늘리려면 역 플랫폼을 증축하고 (인접 트랙에 더 많은 플랫폼 섹션을 건설하여) 다중 플랫폼 정류장을 건설하십시오. 게임 시점에서 화물 및 승객을 위한 운행 권역을 표시하거나 숨기려면 [v]를 누르십시오. 더 낮은 트랙 위에 교량을 건설하려면 [Ctrl]을 길게 누르십시오.}

증축: 도구는 용량, 운행 권역 및 유지 보수 비용을 증가시킬 수 있는 정류장을 위한 증축 및 건물을 건설합니다.
일부 도구 옵션의 모서리에 있는 아이콘 (역 목록 및 정류장 정보에서도 사용)은 정류장을 처리 ​​할 수 있는 증축 항목을 보여줍니다.
증축하려면: 도구를 클릭하여 선택한 다음 (커서를 내선 번호로 변경), 게임 시점에서 기존 정류장 옆의 필요한 위치를 클릭하십시오. 새로 증축된 부분은 정류장의 일부로 간주됩니다.
{팁: 파괴/ 제거로 증축된 부분을 제거하십시오.}

simutrans-124.5/simutrans/text/ko/removal_tool.txt000066400000000000000000000047661520675460500224660ustar00rootroot00000000000000제거 도구 도움말

파괴/ 제거 도구

제거 도구게임 시점단일 게임-타일에서 원치 않는 아이템과 구성 요소를 파괴하고 제거하기 위해 사용됩니다.

사용법: 게임 시점의 맨 위에 있는 빨간색 십자선(pak128의 bulldozer-icon)을 클릭하거나 [r] 키를 눌러 제거 도구를 엽니다(커서가 빨간색 십자-분진 구름 모양으로 변경됨). 게임 시점에서 커서 위치에 표시되는 노란색 윤곽은 도구가 작동할 위치를 나타냅니다(지도 좌표는 아래쪽 바에 표시됨). 그런 다음 게임 시점에서 개체를 클릭하여 제거합니다. 제거할 수 있는 권한이 있으면 경고 없이 해당 개체가 지도에서 제거됩니다.

발생된 비용은 게임 시점에 즉시 표시되며 건축 비용 항목에 표시되며, 이는 재정 상태 창에 속해있습니다. 제거 비용은 (선수 품목의 경우) 건설 비용과 동일합니다. 도시 건물 파괴 비용은 점검도구로 열린 설명창에서 보듯이 그 가치와 같습니다. 또한 나무를 파괴하는 비용도 듭니다.

제거 도구는 개인 보행자와 개인 차를 제거하는 데도 사용할 수 있습니다.

단일 도로/트랙 타일로부터 항목을 제거하는 순서: 보행자 또는 개인 도시 차량, 신호, 정지/정지 플랫폼, 경로 전기화, 교량 또는 터널, 경로 자체(도로에 있는 경우 먼저 추적).

팁:
플레이어 차량을 제거하려면 Convoy Details창에서 판매/소지를 해야합니다.
산업, 관광지 또는 시청을 제거하려면 먼저 공용 서비스 플레이어로 전환해야합니다.
경고: 시청 또는 마을회관을 철거할 때 그 도시 지역의 모든 도시 건물도 철거됩니다.
다른 플레이어 항목을 삭제/ 제거하려면 먼저 다른 플레이어로 재생하도록 선택합니다.
트랙 또는 도로의 세그먼트를 제거하려면 트랙 제거 도구 (트램 도구에도 있음) 또는 도로 제거 도구를 사용합니다.

simutrans-124.5/simutrans/text/ko/roadtools.txt000066400000000000000000000260341520675460500217620ustar00rootroot00000000000000도로 도구 도움말

도로 도구

도로 도구는 화물과 승객을 위한 도로 교통망을 구축합니다. 도구는 작성 또는 제거 될 수 있습니다: 도로, 교량, 터널, 신호, 차고지, 증축, 그리고 정류장. 타임 라인을 가지고 플레이하는 경우, Simutrans에서 시간이 지남에 따라 더 많은 도구 옵션이 나타날 수 있습니다.

도구 바를 열려면 게임 시점의 상단에 있는 트럭 아이콘을 클릭하십시오.
마우스 커서를 도구 옵션 위에 올리거나 (도구 바를 열거나 클릭 한 후) 이름을 보고 적절한 위치에 표시하십시오: 건설 비용, 괄호 안의 유지 보수 비용, 최대 속도 제한 및 최대 길이.

선로 도구는 왼쪽에서 오른쪽으로 포함합니다:

도로: 도구는 도로 차량을 위해 두 지점 사이에 도로를 건설합니다. 도로는 다른 건물이 차지하는 구역에는 건설될 수 없습니다. 도로는 경사면 방향으로 경사면에만 건설될 수 있으며 거친 지형이나 물을 가로 질러 건설될 수는 없습니다. 새로 건설된 도로는 기존 경로의 도로를 사용할 수 있습니다.
도로를 건설하려면: 도구를 클릭하여 도로 유형을 선택하고 (커서를 도로로 변경), 도로의 시작점에 대한 게임 시점에서 클릭하십시오 (게임보기의 불도저 표시 및 게임보기의 하단 막대 오른쪽에지도 좌표 표시 ), 마지막으로 도로 종점에 대한 게임 시점을 클릭하십시오.
{팁: 다른 유형의 도로는 다른 플레이어가 만든 경우에도 연결할 수 있습니다. 거친 지형을 가로 질러 도로를 연결하거나 장애물을 피하려면 교량과 터널을 사용하십시오. 지형을 변경하고 도로 경로를 사용하려면 테라포밍 도구를 사용하십시오. 개별 도로 및 장애물을 제거하려면 파괴/ 제거를 사용하십시오. 추가 기능을 위해 [Ctrl]을 동시에 사용하십시오. 실행 취소 [z]는 건축 비용을 환불하지 않습니다. 마지막 도로 도구를 선택하려면 [s]를 누르십시오.}

도로 제거: 이 도구는 차량이 없을 때 도로를 제거하여 게임 시점의 두 지점 (경로에 있는 도로-정류장, 신호, 교량의 터널 또한 제거됩니다) 사이에 있습니다. 도구를 사용하면 건설 비용이 발생합니다.
도로를 제거하려면: 도구를 클릭하십시오 (커서가 빨간색 십자 기호로 바뀝니다); 삭제할 트랙을 클릭하십시오 (게임 시점에서 적십자로 표시된 삭제점을 선택하십시오); 마지막으로 트랙의 다른 지점을 클릭하여 첫 번째 삭제점까지 트랙 부분을 제거합니다.
{팁: 도로를 제거하려면 다른 플레이어로 작동하십시오.}

도로 교량: 도구는 게임 시점에서 도로의 두 부분 사이를 통과하는 직선 교량을 만듭니다. 교량에는 최대 경간이 있습니다. 도구는 도로 끝에서부터 적당한 장소 (경간 내 다른 도로 끝 또는 약간 높은 땅)까지 교량을 만듭니다.
교량을 건설하려면: 도구를 클릭하여 교량을 선택하고 (커서를 교량으로 변경) 도로의 끝 (교량의 시작점)을 클릭하십시오. 교량 쪽 끝 부분에 장애물이나 적당한 장소가 없는 경우 교량이 막히는 것을 막을 수 있습니다: 교량이 될 양측 도로를 만들고 다시 시도하십시오.
{팁: 다리를 제거하려면 (다리 끝을 클릭하십시오) 파괴/ 제거를 사용하십시오. 건물을 연결하기 위한 장애물이 있습니다. 도로에 교량의 끝을 연결하려면 도로 도구를 사용하십시오.}

도로 터널: 이 도구는 차량이 육지를 통과할 수 있도록 직선 터널을 건설하여 게임 시점에서 두 개의 도로 사이에 둡니다. 터널은 기울어 질 수 없습니다: 차량은 동일한 높이로 출입합니다.
도구는 경사면에 위치한 도로 끝 부분에 터널을 만듭니다. 터널을 건설하려면: 도구를 클릭한 다음 (커서를 터널로 변경), 경사면의 도로 끝을 클릭하여 터널 입구를 찾습니다. 터널이 적당한 출구 (장애물이 없는 경사면)를 찾지 못하는 경우, 그것은 건설되지 않을 것입니다: 연결될 양쪽에 도로를 놓고 다시 시도하십시오.
{팁: 터널 출입구에 대해 적합한 장소를 생성하려면 풍경 도구 및 파괴/ 제거를 사용하십시오. 도로 도구를 사용하여 터널 출구를 도로에 연결하십시오.}

도로 신호 및 장벽: 도구는 도로 차량의 신호 및 장벽을 게임 시점에서 건설합니다. 신호 및 장애물은 도로 및 교량, 교차점 및 정류장 (화물 및 승객을 싣고 내린 곳)에서 차량의 흐름을 직접 통제합니다. 일부 신호의 양방향 및 단방향 버전을 구축할 수 있습니다.
중요사항: 주의 신호는 차량이 목적지에 도달하지 못하도록 막는 신호를 구축하지 않으며, 기본적으로 차량은 오른쪽으로 주행합니다 (simuconf.tab에서 변경할 수 있습니다).
- 교통 신호등: 도구는 게임 시점에서 신호를 구축하여 교차점에서 도로 차량의 흐름을 조절하고 진행시키거나 잠시 기다릴 수 있도록 합니다. 도구를 클릭하여 선택한 다음 (커서를 신호로 변경), 구축할 도로를 클릭하십시오. 신호 방향을 변경하려면 교차점에서 다시 클릭하십시오.
- 단일 장벽: 도구는 도로 한쪽을 사용하여 도로 차량을 막는 장벽을 만듭니다. 도구를 클릭하여 선택하고 (커서를 장벽으로 변경), 건설할 도로를 클릭한 다음, 동일한 위치에서 다시 클릭하여 장벽이 적용되는 도로의 측면을 변경하십시오.
- 도로 닫힘: 도구는 도로 차량의 진행을 방해하는 장벽을 만듭니다. 도구를 클릭하여 선택한 다음 (커서를 장벽으로 변경), 건설할 도로를 클릭하십시오.
- 출입 금지: 도구는 도로 차량이 도로의 한쪽에서 진행되는 것을 방지하기 위한 신호를 구축합니다 (단일 장벽과 유사). 도구를 클릭하여 선택한 다음 (커서를 신호로 변경), 건설할 도로를 클릭하고 동일한 위치에서 다시 클릭하여 신호가 있는 도로의 측면을 변경하십시오. 또한 특정 플레이어의 통행만을 허용하는 개인용 입구 게이트라는 변형이 있습니다.
- 최소 80km/h 최대 속도가 80km/h 미만인 경우, 도로 차량이 진행하지 못하도록 신호를 구축합니다. 도구를 클릭하여 선택하고 커서를 신호로 변경한 다음, 건설할 도로를 클릭하고 동일한 위치에서 다시 클릭하여 신호가 적용되는 도로의 측면을 변경합니다.
- 지점 선택: 도구는 도로 차량을 빈 도로-정류장으로 안내하여 여러 도로-정류장이있는 정류장에서 화물 및 승객을 싣고 내리는 신호를 구축합니다: 이 신호를 통과하는 차량은 일정에 할당된 차량뿐만 아니라 다음 목적지에서 무료 도로-정류장을 사용할 수 있습니다. 빈 도로-정류장 또는 다음 목적지까지 가는 명확한 경로가 없으면 차량은 신호를 기다립니다. 도구를 클릭하여 선택하고 커서를 신호로 변경한 다음 건설할 도로를 클릭하고 동일한 위치에서 다시 클릭하여 신호가 적용되는 도로의 측면을 변경합니다.
- 고속도로: (pak128에서만 사용 가능) 도구는 최대 속도 제한이 80km/h를 초과하는 경우, 차량이 한 방향으로 진행하는 것을 허용하는 신호를 구축합니다.
도구를 클릭하여 선택하고 (커서를 신호로 변경), 건설할 도로를 클릭하고 동일한 위치에서 다시 클릭하여 신호가 적용되는 도로의 방향을 변경하십시오.
{팁: 파괴/ 제거로 신호를 제거하십시오. 더 낮은 도로 위의 교량에 신호를 배치하려면 [Ctrl]을 사용하십시오.}

도로 차고지: 도구는 도로 차량 및 트레일러 매입 및 관리를 위한 차고지를 건설합니다. 차고지는 유지 보수 비용이 있으며 게임 시점에서 도로 끝 부분에 건설됩니다.
도로 차고지를 건설하려면: 도구를 클릭한 다음 (커서를 저장소로 변경), 도로의 끝을 클릭하십시오.
{팁: 파괴/ 제거로 차고지를 제거하십시오.}

주차: (pak 128에서만 이용 가능) 도구는 화물 및 승객의 수용량 및 운행 권역을 늘리고 유지 비용을 증가시키는 정류장의 확장을 구성합니다.
증축하려면: 도구를 클릭하여 증축할 부분을 선택하고 (커서를 확장 프로그램으로 변경), 게임 시점의 기존 정류장 옆에 있는 도로에서 필요한 위치를 클릭하십시오. 새로 증축한 부분은 이 정류장의 일부로 간주됩니다.
{팁: 파괴/ 제거로 증축된 부분을 제거하십시오.}

도로-정류장: 도구는 도로 차량이 화물 및 승객을 싣고 내릴 수 있는 장소를 건설합니다.
도로-정류장이 기존 정류장에 인접해서 건설되지 않으면 새로운 정류장을 만들 수 있습니다.
도로-정류장은 도로에 건설되고 화물, 승객 및 우편물에 대한 유지 비용 및 운행 권역이 있습니다. 일부 도구 옵션의 모서리에 아이콘 (역 목록 및 정류장 정보에 사용)은 정류장를 처리​​할 수 있는 도로-정류장의 항목을 보여줍니다. 다른 도로-정류장은 화물, 승객 및 우편물에 대해 서로 다른 수용량을 가질 수 있습니다.
도로-정류장을 건설하려면: 도구를 클릭하여 선택한 다음 (커서를 도로 정지까지 변경), 게임 시점에서 도로 상의 필요한 위치를 클릭하십시오.
{팁: 파괴/ 제거로 도로-정류장을 제거하십시오. 게임 시점에서 화물 및 승객의 운행 권역을 토글하려면 [v]를 누르십시오. 도로-정류장은 다른 플레이어가 만든 도로에는 설치할 수 없습니다. 더 낮은 도로 위에 교량을 건설하려면 [Ctrl]을 사용하십시오. 수용량 및 운행 권역을 증가시키려면 차량에 대해 여러 장소에 정류장을 만드십시오.}

simutrans-124.5/simutrans/text/ko/save.txt000066400000000000000000000043531520675460500207120ustar00rootroot00000000000000저장 도움말

저장

저장은 현재 게임을 파일에 저장하고 (나중에 재생하기 위해)이 파일을 삭제하는 데 사용할 수 있습니다.

저장게임 옵션 또는 [S]를 눌러 열 수 있습니다; 이전 게임의 이름과 날짜 및 시간을 나열합니다.
모든 게임이 보이지 않는 경우: 목록의 오른쪽에 있는 슬라이더 바를 사용하여 이름을 스크롤하십시오.

현재 게임을 새 파일이나 기존 게임에 저장할 수 있습니다.
게임을 새 파일로 저장하려면 저장 상단의 상자에 새 이름을 입력하십시오. 상자를 클릭하고 이름을 입력한 다음 [Enter] 또는 [Return] 키를 누르거나 확인을 클릭하십시오; 새 파일 이름이 나열됩니다.
중요사항: 나열된 파일 이름이 사용된 경우: 오래된 게임은 현재 게임에 따라 대체되고 손실됩니다.

기본적으로 현재 게임이 이 파일에 저장하기 위해 파일 이름 상자에 나타납니다: 확인을 클릭하십시오 (이전 데이터가 손실되어 현재 데이터로 대체됩니다).
이전 게임을 통해 파일로 게임을 저장하려면: 목록에서 이름을 클릭하십시오 (예전 게임은 현재 게임으로 대체됩니다).

파일 이름은 글자, 숫자 및 기호를 최대 57자까지 조합할 수 있습니다; 저장 후에는 저장불러오기에 20자 정도만 표시됩니다.

경고: 게임을 즉시 & 영구적으로 삭제하기 위하여 파일 이름에 의하여 x 버튼을 누르십시오, 파일 이름은 목록에서 제거된다. 이 기능을 주의하여 사용하십시오.

{팁: 불러오기에서 저장된 게임을 다시 시작하십시오.
simuconf.tab에서 자동 저장 (자동 게임 저장)을 변경하십시오}.

저장을 닫으려면 취소를 클릭하십시오. 또는 좌상단 부분의 x; 또는 키보드를 사용하십시오.

simutrans-124.5/simutrans/text/ko/scenario.txt000066400000000000000000000004431520675460500215530ustar00rootroot00000000000000시나리오 도움말

시나리오

당신은 simutrans에서 시나리오를 플레이 할 수 있습니다. 완수해야할 몇 가지 목표: 공장 연결시키기, 최단 시간으로 부자되기, 기념비적인 본부 세우기.

simutrans-124.5/simutrans/text/ko/schedule.txt000066400000000000000000000034531520675460500215500ustar00rootroot00000000000000스케줄 다이얼로그 도움말

스케줄 다이얼로그

 스케줄 다이얼로그에서는, 차량·선박의 운행 경로를 설정하는 것이 성과
.덧붙여서 시각을 설정한 운행은 할 수 없습니다.

 운행 경로를 설정하려면 , 우선 추가 버튼을 클릭합니다.다음에, 정차
역등의 위를 클릭해 주세요.항구를 지정하는 경우는, 항구의 주변의 매스
(을)를 클릭해 주세요.리스트에 역명·항명이 추가됩니다.덧붙여 선로상·
도로상·해상에서 역 등 나오지 않는 장소도 중계점으로서 설정할 수 있습니다.이와 같이 해
(이)라고, 운행 경로의 역·항등을 차례차례로 클릭하며 가 주세요.최대 16개소
그리고 지정할 수 있습니다.

 차량·선박은, 리스트 위로부터 차례로 설정된 개소를 돕니다.리스트의
맨 밑까지 오면, 또 위까지 돌아오고 운행을 계속합니다.번호의 왼쪽의 마크는 현
재 향하고 있는 개소를 나타냅니다.

 추가 버튼에서는, 새롭게 개소를 리스트의 맨 밑에 추가합니다.삽입 버튼으로
(은)는, 리스트상의 현재 마크가 있는 항목에, 새로운 개소가 삽입됩니다.리스트
의 항목을 삭제하려면 , 삭제 버튼을 클릭하고, 삭제하고 싶은 항목을 크리
쿠 해 주세요.모든 설정이 끝나면 종료 버튼을 클릭해 주세요.

 「쌓을 때까지 대기」의 숫자는, 그 역·항으로, 차량·선박에 화물·여객을 이
비율 쌓은 상태가 될 때까지, 출발을 기다린다고 하는 의미입니다.이 숫자를 대나무 쿠스
일로, 헛됨이 없는 운행을 할 수 있습니다.

simutrans-124.5/simutrans/text/ko/server.txt000066400000000000000000000025041520675460500212560ustar00rootroot00000000000000게임 정보 / 서버 도움말

게임 정보 / 서버

게임 정보 / 서버 는 시간대, 지도 크기, 회사 수, 마을, 시민, 공장, 운영중인 차량 등등의 실행중인 게임의 현재 상태를 보여줍니다.

현재 게임이 온라인이 아닌 경우, 연결가능한 서버 목록을 보여줍니다. 만약 목록이 비어있다면, 당신의 pak 버전이나 프로그램 버전에 맞는 서버가 없는 것입니다. 모두 보이기 버튼을 사용하여 존재하는 모든 서버를 확인할 수 있습니다.

서버를 선택하면 서버 상태에 대한 게임 정보 개요가 표시됩니다. (만약 서버가 다운되면, 이를 확인하는 데 약간의 시간이 소요됩니다.) 또한 서버의 pak set과 프로그램 버전이 표시됩니다. 둘 다 검은 색이라면, 당신의 pak set과 프로그램 버전이 서버와 일치하며 온라인 플레이 을 눌러 참가할 수 있습니다.

pak set이 일치하지 않으면, 항목이 빨간색으로 표시됩니다. 당신의 pak 비교 버튼을 사용하여 누락되었거나, 다르거나, 혹은 추가 paks들에 대한 보고서를 받아 볼 수 있습니다. (다소 시간이 걸릴 수 있습니다.)

simutrans-124.5/simutrans/text/ko/settings.txt000066400000000000000000000024311520675460500216070ustar00rootroot00000000000000고도의 설정 도움말

고도의 설정

고도의 설정윈도우는새로운 맵의 작성 윈도우로부터 엽니다.
여기에서는, 주로 작성하는 새로운 맵의 룰을 설정합니다.

 최상부의 버튼은 리셋 버튼입니다.설정을 리셋트 합니다.주의해 간다
(이)다 차이.(일부 리셋트 되지 않는 항목도 있습니다)
Simuconf.tab simuconf.tab의 설정에 리셋트 합니다.
Default.sve 마지막에 신규 맵을 작성했을 때의 설정에 리셋트 합니다.

설정 항목에는, 세이브 게임에 보존되는 것과 보존되지 않는 것이 있어
.
세이브 게임에 보존되는 것은, 신규에 작성되는 게임에게만 반영됩니다.
게임 개시 후로 변경할 수 없습니다.

보존되지 않는 설정 항목은, 변경이 즉시에 반영됩니다.다만 재기동 후는 원의
설정으로 돌아와 버리기 때문에, 변경을 고정하고 싶은 경우는, simuconf.tab나
cityrules.tab를 텍스트 문자 편집기로 직접 변경해 주세요.

각 설정 항목의 자세한 것은 「일본어화 wiki」를 참조해 주세요.

simutrans-124.5/simutrans/text/ko/shiptools.txt000066400000000000000000000027341520675460500220010ustar00rootroot00000000000000선박 툴 다이얼로그 도움말

선박 도구 다이얼로그

 선박 툴에서는, 항구와 도크의 건조 을 할 수가 있습니다.

 상단의 제일 왼쪽의 버튼은, 항구 건조 도구입니다.항구에서는, 선박이 화물이나 여객의
실음과 내림을 실시합니다.아이콘을 클릭하면, 마우스 커서가 항구의 형태
상태에 변화합니다.바다에
접한 경사면에서 클릭하면, 그 경사면과 인접한다
바다와의 2 매스를 이용하고, 항구가 건조함
.항구를 건조하는 경사면은, 진
곧경사면(선로나 도로를 부설할 수 있는 경사면) 나오지 않으면 안됩니다.선
박을 정박시키도록(듯이) 스케줄을 짜려면 , 항구가 있는 매스가 아니고, 항구에
인접하는 매스에 정박하도록(듯이) 지정해 주세요.Simutrans 의 현재의 바죠
에서는,항구의 철거는 올바르게 동작하지 않습니다.

 하단의 왼쪽의 2개의 버튼은, 도크 건조 도구입니다.이것들 2는 화면상에서
의 방향이 다른 것만으로, 실질적으로는 차이는 없습니다.도크는, 선박의 구입이나
스케줄 설정을 실시하기 위해서 필요합니다.아이콘을 클릭하면, 마우스
커서가 도크에 변화합니다.도크는 해상에 건조합니다.도크를 건조해
싶은 장소에서 클릭해 주세요.

simutrans-124.5/simutrans/text/ko/signal_spacing.txt000066400000000000000000000023741520675460500227360ustar00rootroot00000000000000신호등 배치 간격 도움말

일반적인 사용법:


-- 신호등 아이콘을 Ctrl-클릭하여 gui를 불러옵니다.
-- 트랙을 클릭하고 드래그 하면 만들어질 신호등의 미리보기가 나타나고 마우스 버튼을 놓으면 건설이 진행됩니다. 열차가 마우스로 드래그한 방향을 통과할 수 있어야만 신호등이 만들어집니다.
-- 온라인 게임에선: 먼저 트랙을 Ctrl-클릭하고 마우스 버튼을 놓습니다. 마우스를 누르고 있는동안 마우스를 움직이면 미리보기가 나타납니다. 버튼을 놓으면 만들어 집니다.

신호등 창의 인자들:

-- 신호등 배치 간격: 타일에 얼마나 많은 연속된 신호등을 배치할 수 있는지 나타냅니다 (대각선은 1/2 타일로 계산한다). 이 값은 신호등이 선택되면 표시됩니다.
-- 다른 신호등 제거: 선택되면 길의 다른 신호등은 삭제됩니다.
-- 다른 신호등 재배치: 선택되면, 만들려는 신호등 타일 위에 있는 기존의 신호등은 삭제됩니다.

이 인자들은 다른 종류의 신호등과는 독립적으로 설정됩니다.

simutrans-124.5/simutrans/text/ko/simutrans.txt000066400000000000000000000060761520675460500220050ustar00rootroot00000000000000Simutrans에 대하여

Welcome to Simutrans!

첫째로, 지도 고르기: 일단 Simutrans의 수천 개의 지도 중 하나를 선택하거나 높이 지도에서 새 지도를 만드십시오. 그런 뒤 조부모의 돈을 물려 받은 젊은 사업가가 되어보세요. 그들의 자부심이 되고. 성공적인 운송 회사를 세우려는 야망과 함께 번창하는 운송 제국을 만드는 일 모두 당신에게 달려있습니다.

시간이 지남에 따라, 수백만 승객의 여행 요구 사항을 만족시키고 화물을 운송하여 경제를 원활히 가속화 시키는 동안 당신의 회사를 성공적인 기업으로 발전십시오. 당신은 승객, 우편 및 화물을 철도, 도로, 선박, 심지어 항공으로도 운송 할 수 있습니다.

모든 지도는 각자 다른 요소를 제공합니다. 주변 환경을 잘 살펴보십시오: 그것들은 모두 당신과 경쟁자 손 위에 놓여있습니다.

발전소에 필요한 물품을 공급하는 것이 가장 쉽다는 것을 알 수 있을 겁니다. 당신이 번 돈을 점차적으로 공장에 투자해 확장시키십시오 - 어쩌면 화물을 다시 실을 수 있는 중앙 허브를 사용할 수도 있을 것입니다. 화물을 운송만 한다면 마을과 도시가 성장하지 않고 새로운 산업이 출현하지 않을 것입니다.

또는, 즉시 승객(및 우편) 수송을 할 수도 있습니다. Simutrans 월드의 주민들은 모두 특정 목적지로 여행하기를 원하기 때문에 이 방법은 더욱 어려울 수 있습니다. 몇몇은 공장에 일하로 가길 원하고, 몇몇은 시내에 쇼핑하길 원하며 또 다른이들은 관광 명소에 방문하길 원합니다. 만약 그들이 원하는 목적지와 연결되어 있지 않다면, 그들은 당신을 위해 여행하지 않을 것입니다.

당신은 가능한 많이 대중적인 목적지에 도달할 수 있는 운송 네트워크를 구축해야 합니다. 네트워크가 형성되면, 지속적인 수요를 유지하기 위해 지속적으로 업그레이드해야 할 것입니다, 그러니 미리 계획하십시오! 만약 승객들이 당신의 서비스에 만족한다면, 마을은 성장하고 새로운 산업체들이 설립될 것이며, 그럴 경우에만 경제가 원활히 운영될 것입니다.

당신은 최대한 오래 플레이하는 것을 좋아할 수 있습니다. 그렇다면 1880년에 시작해서 2050년에 끝내십시오, 혹은 이 사이 어떤 시대로든 시작 시간을 설정하십시오. 시간대에 맞는 차량과 빌딩이 존재합니다. AI를 상대하거나 당신을 위해 플레이 하십시오. 하지만 주의할 점은: 3개월 동안 마이너스 재산을 유지한다면, 당신은 파산할 것이고 게임은 끝나게 됩니다. 재정 창을 통해 자산을 확인하십시오.

Simutrans 팀 과 커뮤니티는 당신이 Simutrans 플레이하며 즐거운 시간을 보내시길 바랍니다!

simutrans-124.5/simutrans/text/ko/slopetools.txt000066400000000000000000000041421520675460500221530ustar00rootroot00000000000000슬로프 툴 도움말

슬로프 도구

슬로프 도구를 사용하고, 육지를 조성할 수 있습니다.조성할 수 있다
장소는 아무것도 없는 빈터나 해상에 한정됩니다.
[#]키([shift+3])로, 구라두를 표시 하게 하면 편리합니다.

육지를 비싸게 한다
 육지를 현재보다 1 레벨상의 높이에 북돋웁니다.
육지를 낮게 한다
 육지를 현재보다 1 레벨하의 높이에 움푹 팬 곳.

이상의 2개의 도구는, 왼쪽 클릭 또는 왼쪽 버튼을 누른 채로 드러그 해
사용합니다.드러그의 경우는, 모두 같은 높이가 됩니다.
조성하는 체적에 비례하고, 비용이 비싸집니다.
수목등의 자연물은 제거되어 버립니다.

슬로프의 건설: 인공적인 조성을 합니다.

왼쪽의 4개는 경사지를, 오른쪽의 2개는 수직에 육지를 분위기 고취/인하합니다.
인공적인 조성에서는, 주위와의 단차를 2단 이상으로 할 수 없습니다.
이러한 도구는 빈터나 해상 이외에, 선로/도로의 종단상에서도 사용할 수 있습니다.

선로/도로의 종단상에서의 사용
이 경우, 주로 오른쪽의 2개의 도구를 사용하고, 선로/도로마다 경사시킵니다.
방향이 맞으면 왼쪽의 4개의 슬로프도 사용할 수 있습니다만, 별로 편리하지는 않습니다.
경사를 바탕으로 되돌리는 경우에도, 이것들 2개의 도구를 사용합니다.
내리막의 경사를 바탕으로 되돌리려면 , 분위기 고취 도구가, 오름의 경사를 바탕으로 되돌리려면 ,
인하 도구를 사용할 수 있습니다.
지하에서 선로/도로를 경사시키고 싶은 경우에는, 필요합니다.

슬로프를 되돌린다
인공적인 조성지나 그 주위를, 가능한 한 자연스러운 상태에 되돌립니다.
선로/도로의 종단상에서는 사용할 수 없습니다.

simutrans-124.5/simutrans/text/ko/sound.txt000066400000000000000000000015771520675460500211110ustar00rootroot00000000000000소리 설정 도움말

소리 설정

소리 설정에서는 효과음과 BGM을 조절할 수 있습니다.

소리 설정게임 옵션에서 열 수 있습니다.

화살표 버튼이나 슬라이드 바를 이용하여 설정을 조절할 수 있습니다.

소리 크기: 효과음의 크기를 조절합니다.

음악 크기: BGM의 크기를 조절합니다. 음악이 없으면 작동하지 않습니다.

재생 중인 음악: BGM을 바꿉니다. 화살표 버튼으로 바꿀 수 있습니다.
임의 재생을 누르면, 노래가 임의의 순서로 재생됩니다.

온라인 음원을 추가하고 싶으시다면, http://wiki.simutrans.com를 참조하세요.

simutrans-124.5/simutrans/text/ko/special.txt000066400000000000000000000145421520675460500213750ustar00rootroot00000000000000특수 건설 도구 도움말

특수 건설 도구

특수 건설 도구 는 교통과 관련 없는 항목들로 구성되며 다른 플레이어도 작동시킬 수 있도록 허용됩니다.

도구 모음(toolbar)을 열기 위해 게임 뷰 상단에 위치한 red-drop-in-yellow-circle 아이콘 (pak128의 크레인 아이콘) 을 클릭하십시오. 마우스 커서를 도구 옵션 위에 올려 (도구 모음을 열거나 클릭한 후) 이름과 다음 항목을 확인: 건설비용.

특수 건설 도구 가 포함하는 도구들(좌측 부터):

우체국: 정거장 이 우편물을 처리 할 수 있게 해주는 우체국을 만드는 도구입니다. 우체국은 정거장의 수용 및 집수 용량을 늘리고 유지 보수 비용이 있습니다.
우체국 만들기: 선택한 도구를 클릭한 뒤, 게임 뷰의 기존 정거장을 클릭합니다. 새로운 만든 우체국은 이제 정거장의 일부가 됩니다.
{팁: 파괴/제거 도구로 우체국을 제거할 수 있습니다.}

역 확장: 정거장 의 건물들을 추가하거나 확장하기 위한 도구 입니다. 확장 도구는 물품과 승객들을 위한 집수 구역의 용량과 유지 비용을 증가 시킬 수 있습니다. 일부 도구 옵션 구석에 있는 아이콘 (역 목록 과 역 정보에서 사용되는) 은 확장도구로 정거장을 다룰 수 있는 항목들을 보여줍니다.
확장 시키기: 선택한 도구를 클릭한 뒤 (커서가 확장 아이콘으로 변경됨), 게임 뷰 내, 기존 정거장 옆에, 원하는 공간을 클릭하십시오.
{팁: 파괴/제거 도구로 확장시설을 제거할 수 있습니다.}

P+ 플레이어 교체: 만약 게임 시작 전 플레이어 교체 허용 이 체크되어 있다면 다른 플레이어 로 게임 플레이 하는 것을 허용합니다. 추가 옵션 으로 플레이어 공동 이용을 가능하게 할 수 있습니다.
플레이어를 바꾸기 위해 도구를 클릭하십시오 (혹은 [P] 를 누르거나). 플레이어 변경 사실을 알려주는 확인 메세지: 좌측 상단 x를 클릭하거나 키보드를 사용하여 끌 수 있습니다.

도시 건설: 새로운 도시 구역.
을 설립하는 도구입니다. 새 도시 만들기: 선택한 도구를 클릭하거나 [C] 키를 누른 뒤 (커서가 시청 아이콘으로 변경됨), 게임 뷰에서 원하는 지역을 클릭합니다. 새 도시는 추가 확인 없이 500만 credits 으로 건설되어집니다.
{팁: 파괴/제거 도구로 게임 뷰 내 시청을 클릭하여 새 도시를 제거할 수 있습니다.}

나무 심기: 게임 뷰 내에서 나무를 심는 도구입니다.
나무 심기: 선택한 도구를 클릭한 뒤 (커서가 나무 아이콘으로 변경됨), 원하는 공간을 클릭합니다.
{팁: 파괴/제거 도구로 나무를 제거할 수 있습니다.}

고압 전선 만들기: 발전소의 변전소와 산업체 간에 전력을 공급하기 위해 전압선을 구축 (생산성을 높이기 위해) 하는 도구입니다. 전력의 공급은 수입을 창출해냅니다. 이 전력선은 대각선 트랙과 도로에 교차시킬 수 없습니다.
고압 전선 만들기: 선택한 도구를 클릭하거나 [l]키를 누른 뒤 (커서가 전압선 아이콘으로 변경됨), 발전소 옆에 있는 변전소를 클릭하고, 마지막으로 산업체에서 변전소를 클릭하여 생산량을 증가시키는 전력을 공습하십시오.

변전소 고압 전선을 연결하기 위해 발전소나 산업체 에 필요한 변전소를 건설하기 위한 도구입니다.
변전소 만들기: 선택한 도구를 클릭하거나 [g] 키를 누른 뒤 (커서가 red-drop-in-yellow-circle 아이콘으로 변경됨), 게임 시점에서, 산업체나 발전소 옆 원하는 공간을 클릭합니다.
발전소에 설치된 변전소는 반드시 산업체의 변전소와 연결하여 고압 전선을 통해 전기를 공급 받도록 하여야 합니다.
전기가 공급되지 않는다면, 게임 시점에서 변전소는 노란 번개 모양으로 표시되고 전류가 공급 된다면 붉은 번개 모양으로 표시됩니다.
{팁: 산업체와 발전소는 전력 공급을 높이기 위해 여러 변전소를 가질 수 있습니다. 파괴/제거 도구로 변전소를 제거할 수 있습니다.}

표지판 만들기: 게임 시점에서 원하는 텍스트를 적을 수 있는 표지판을 배치하고 게임 뷰를 다른 지점으로 이동시키는 데 사용할 수 있는 도구입니다.
선택한 도구를 클릭하거나 [M] 키를 누른 뒤, 커서가 표지판 아이콘으로 변경됨, 게임 뷰에서 원하는 지점을 클릭하여 표지판 조작을 엽니다. 표지판 조작 상단에 선택한 위치의 좌표가 기입되고, 빈상자에 원하는 텍스트를 입력 (박스를 클릭하고 텍스트를 입력한 뒤 확인 을 클릭하거나 [Enter] 혹은 [Return]을 누르십시오) 한 후 표지판을 배치합니다. 게임 시점에서 새 표지판이 보여지고 다시 연다면 표지판 조작 (지도 좌표와 함께) 이 나열됩니다.
표지판으로 게임 시점 이동하기: 표지판 조작을 열고 목록에서 원하는 지점을 클릭합니다.
표지판 제거하기: 표지판 만들기 도구를 클릭하거나 [M] 을 누른 뒤, 커서가 표지판 아이콘으로 변경됨; 게임-시점에서 표지판의 좌표를 클릭한고; 표지판 조작에서 제거 를 클릭합니다.
표지판 조작 닫기: 좌측 상단의 x 를 클릭하거나; 취소 를 클릭; 혹은 키보드를 사용하십시오.
{팁: 커서의 좌표는 게임-시점 우측 하단에 표시됩니다. [!] 키를 사용하여 게임-시점에서 표지판의 텍스트 모양을 변경시킬 수 있습니다.}

simutrans-124.5/simutrans/text/ko/station.txt000066400000000000000000000021171520675460500214310ustar00rootroot00000000000000역 정보 다이얼로그 도움말

역 정보 다이얼로그

 역 정보 다이얼로그에는, 그 역에 관한 정보가 표시됩니다.또, 여기서
역명을 변경하는 일도 할 수 있습니다.

 다이얼로그 상부의 텍스트 필드에는, 역명이 표시되고 있습니다.여기에
문자를 입력하는 일로, 역명을 변경할 수 있습니다.

 우측에 있는 상세 버튼을 클릭하면, 그 역에 관한보다 상세한 정보를
조사할 수가 있습니다.

 역 정보 다이얼로그의 주요한 부분은, 하부에 있는 화물·승객 리스트입니다.여기
에는, 그 역에서 수송을 기다리고 있는 화물이나 승객의 일람이 표시됩니다.이 일람
정보는, 화물의 종류마다, 목적지 마다 표시됩니다.또, 여기에 표시되어
정보는 게임의 진행에 수반해 자동적으로 갱신되기 때문에, 최신의 정보를 얻는다
위해(때문에) 다이얼로그를 다물어 다시 열 필요는 없습니다.

simutrans-124.5/simutrans/text/ko/station_details.txt000066400000000000000000000014171520675460500231400ustar00rootroot00000000000000역 정보 상세 다이얼로그 헬프

역 정보 상세 다이얼로그 도움말

 역 상세 다이얼로그에서는, 이 역에 접속되고 있는 공장을 나타내고 있습니다.또,
이러한 공장이 어느 제품을 필요로 하고 있을지도 나타내 보이고 있습니다.

 이 접속 정보는, 환승을 하지 않고 직접 보낼 수 있는 목적지만을
나타내 보이고 있습니다.승객과 화물은, 최종적인 목적지에 겨우 도착하는데 필요하면 몇 번인가
의 환승을 시도합니다.

 역의 자세한 것은,역 정보 다이얼로그에 있는상세버튼을 누르는 것에 의해서 연다
일이 생깁니다.

simutrans-124.5/simutrans/text/ko/trafficlight_info.txt000066400000000000000000000012131520675460500234250ustar00rootroot00000000000000교통 신호등 제어

교통 신호등 제어

교통 신호등의 빨간불과 초록불의 빈도를 제어할 수 있습니다. 선택할 수 있는 것은 북/남 과 동/서 방향의 초록불 지속시간입니다 (초 단위).

모든 교통 신호등이 단계적으로 전환되므로, 세 번째 필드에 추가 오프셋을 작성하여, 후속 교통 신호등을 통해 특정 방향으로 장애물 없는 교통 흐름을 실현시킬 수 있습니다.

교통 신호등은 도로 건설 도구 모음 에서 만들 수 있습니다.

simutrans-124.5/simutrans/text/ko/tramtools.txt000066400000000000000000000231551520675460500220010ustar00rootroot00000000000000노면전차 도구 도움말

노면전차 도구

노면전차 도구 는 전차 수송망을 건설합니다. 이 도구로 만들 수 있는 것: 전차 트랙 (전기를 통하게 하거나 그것을 제거하거나), 신호들, 노면전차 차량기지, 노면전차 정류장. 타임 라인 으로 플레이 하는 경우 Simutrans에서 시간이 지남에 따라 더 많은 도구 옵션이 나타날 수 있습니다.

게임 뷰 상단의 전차 아이콘을 클릭하여 도구 모음(toolbar)를 엽니다.
도구 옵션 위에 마우스 커서를 가져 가면 (도구 모음(toolbar)를 열거나 클릭한 뒤) 이름과 다음 특징들을 볼 수 있습니다: 건설 비용, & 속도 제한.

노면전차 도구 가 만들 수 있는 것들 , 좌측부터:

전차 트랙: 두 지점 사이의 철도 차량이 이용하는 트랙을 만드는 도구이다.
전차 트랙은 도로와 도시 지역에 따라 지어질 수 있지만 건물을 가로질러서는 불가능 합니다. 트랙은 경사로에도 건설할 수 있는데, 거친 지형, 물이 있는 지형, 장애물이 없는 지형에만 건설 가능합니다. 새로 만든 트랙은 기존 트랙과 한 경로로 이을 수 있습니다.
트랙 만들기: 도구를 클릭하여 트랙을 선택하십시오 (커서가 트랙 아이콘으로 변경됨); 그 후 트랙의 시작점에 대한 게임 뷰를 클릭하십시오 (게임 뷰에 불도저가 나타나고 우측 하단 바에 좌표가 표시됨); 마지막으로 트랙의 종점에 대한 게임 뷰를 클릭하십시오.
{팁: 다른 유형의 트랙이 연결될 수 있습니다 (단, 다른 플레이어가 만든 경우는 제외). 거친 지형에서 트랙을 연결하거나 장애물을 피하려면 다리 및 터널을 사용하십시오. 파괴/제거 도구를 이용하여 개별 트랙 및 장애물을 제거합니다. [Ctrl] 키와 동시에 사용한다면 추가 기능을 사용할 수 있습니다. 실행 취소 [z]는 건설 비용을 환불하지 않습니다.}

전기 선로: 게임 뷰에서 두 장소를 있는 전기 선로, 다리 터널에 전기를 통하게 하여 전기로 운영되는 차량들을 이용할 수 있게 해줍니다.
트랙에 전기 통하게 하기: 도구를 클릭하거나 [e] (커서가 전기 선로 아이콘으로 변경됨) 키를 눌러 선택한 다음. 게임 뷰에서 전력이 시작 될 부분을 클릭하고 (게임 뷰에 전기 선로 아이콘을 위치시킴), 마지막으로 선로의 두 번째 점을 클릭하여 시작 점까지 전기를 공급합니다.
{팁: 파괴/제거 도구를 이용하여 전력이 흐르지 않는 트랙으로 되돌립니다.}

트랙 제거: 차량이 없는 경우 게임 뷰의 두 지점 사이의 트랙과 전기 선로를 제거하는 도구 (정거장, 정류장, 신호등, 터널 및 다리도 함께 제거됨). 이 도구를 사용하여도 건설 비용 이 지출됩니다.
트랙 제거하기: 도구를 누른 뒤 (커서가 적색 X자 아이콘으로 변경됨); 제거되길 원하는 트랙을 클릭하고 (게임 뷰에서 적색 X자로 표시된 삭제 포인트를 선택합니다.); 마지막으로 연결된 트랙의 두 번째 지점을 클릭하여 첫 지점까지의 트랙을 삭제합니다.
{팁: 플레이어들은 자신의 트랙만 삭제할 수 있도록 작동합니다.}

철도 신호등: 게임 뷰안의 트랙에서 철도 차량 신호등을 생성하는 도구입니다. 신호등은 트랙 및 다리, 교차점 과 정거장 (물품이나 승객들을 승,하차 시키는 곳)에서 차량의 흐름을 직접 통제합니다.
일방 통행 신호등, 양방향 신호등 모두 설치 가능합니다. 트랙에서 양방향 신호등 만들기: 신호등 도구를 클릭한 뒤(커서가 신호등 아이콘으로 변경됨); 트랙을 클릭합니다. 일방 통행 신호등 만들기: 신호등 커서로 같은 지점을 다시 클릭하면 일방 통행 신호등이 되었다가 또 클릭하면 양방향 신호등으로 돌아오는 순환구조 입니다.
중요사항: 차량이 목적지에 도달하지 못하게 단방향 신호를 보내지 않도록 주의 하십시오. 기본적으로 차량은 오른쪽으로 주행합니다 (simuconf.tab에서 변경 가능).
- 신호등: 차량은 다른 차량의 일정 에 포함되지 않은 목적지 (정거장 이나 중간지점) 나 다른 신호등이 있는 트랙까지 향하게 됩니다. 일방 통행 모드인 경우 차량은 오직 한 방향으로만 주행 가능합니다.
- 철도 폐쇄 표시: 차량이 신호를 무시하지 못하게 합니다 (일방 통행 모드에서만 가능합니다).
- 전 신호등: 트랙이 향하는 공간 (일정 에서의 다음 목적지나 연속된 세 신호 사이까지 포함)에 다른 차량이 있지않다면 차량은 계속해서 움직일 수 있습니다. 일방 통행 모드에서는 차량이 한 방향으로만 지나가게 합니다.
- 선택 신호등: 다중 플랫폼 정거장 에서 빈 플랫폼으로 향하도록 차량을 지시: 차량이 이 신호등을 통과한다면 다음 목적지는 빈 플랫폼이 된다. 이곳은 일정 에 지정되어 있지 않다. 빈 플랫폼이 없거나 다음 목적지에 대한 명확한 경로가 없다면 차량은 신호를 기다린다.
{팁: 파괴/제거 도구로 신호등을 제거하십시오. [Ctrl] 키를 길게 누르면 다리 위의 신호등을 더 낮은 트랙 에 배치할 수 있습니다.}

노면 전차 차고: 노면 전차를 구입하거나 관리하기 위해 차고를 짓는 도구.
차고들은 유지 보수 비용이 있으며 게임 뷰에서 트랙 끝 부분에 건설됩니다.
노면 전차 차고 만들기: 도구를 클릭하고 (커서가 차고 아이콘으로 변경됨), 트랙의 끝을 클릭한다.
{팁: 전기가 흐르는 트랙이라면 차고들은 전기로 운용가능한 차량들만 보여줍니다. 파괴/제거 도구로 차고를 제거할 수 있습니다.}

철도 정류장: 철도 차량이 물품, 승객 그리고 우편물을 승, 하차 시키는 데 사용하는 역 플랫폼을 건설하는 도구.
역 플랫폼이 정류장 에 인접하지 않다면 새로운 정류장을 만듭니다.
철도 정류장은 유지 보수 비용이 소요되며 트랙에 건설됩니다 (하지만 굴곡지거나 교차되는 트랙에는 건설되지 않습니다).
철도 정류장에는 물품, 승객 및 우편물을 수용할 수 있는 구역이 있습니다. 역 플랫폼마다 승객과 우편을 수용할 수 있는 용량이 다릅니다. 일부 도구 옵션의 모서리에있는 아이콘 (역 목록 과 정류장 정보에 사용됨) 은 역 플랫폼이 정류장을 다룰 수 있는 항목을 보여줍니다.
역 플랫폼 만들기: 선택한 도구를 클릭한 뒤 (커서가 플랫폼 아이콘으로 변경됨) 트랙을 클릭합니다.
{팁: 파괴/제거 도구로 철도 정류장을 제거 할 수 있습니다. 더 길고 더 많은 차량을 수용하고 적재 구역의 용량을 늘리기 위해서 역 플랫폼을 확장하거나 다중 플랫폼 정류장을 건설하십시오 (인접 트랙에 더많은 플랫폼을 건설함으로써). [v]를 눌러 물품과 승객을 수용하는 구역을 게임 뷰에서 보이거나/ 숨길 수 있습니다. [Ctrl] 키를 누른 채로 아래 트랙 위로 다리를 지을 수 있습니다.}

주차장: (pak128 버전에서만 가능) 정류장 을 확장하여 물품 및 승객의 수용 및 적재 구역, 유지 보수 비용을 높이는 데 사용하는 도구입니다.
확장시키기: 확장 도구를 클릭한 뒤 (커서가 확장 아이콘으로 변경됨), 게임뷰에서 정류장 옆에 원하는 도로를 클릭 하십시오. 이 확장구역은 이제 정류장의 일부분이 됩니다.
{팁: 파괴/제거 도구로 확장구역을 제거할 수 있습니다.}

?도로 정거장: 노면 전차나 도로 차량이 물품, 우편, 승객들을 승,하차 시킬 수 있는 공간을 만드는 도구.
기존 정거장 에 인접하여 건설하지 않을 경우 도로 정거장은 새로운 정거장을 만듭니다. 도로 정거장 도로 를 기반으로 지어야 하며 물품, 승객 및 우편물에 대한 집수 공간과 유지 관리 비용이 있습니다. 일부 도구 옵션 모서리에 위치한 아이콘 (정거장 목록과 정보에 사용됨) 은 도로 정거장에서 다룰 수 있는 항목을 보여줍니다. 각 도로 정거장마다 물품, 승객 및 우편물을 수용할 수 있는 용량이 다릅니다.
도로 정거장 건설하기: 선택한 도구를 클릭한 뒤 (커서가 도로 정거장 아이콘으로 변경됨) 게임 뷰 속 도로 중 원하는 지역을 클릭하십시오.
{팁: 파괴/제거 도구로 도로 정거장을 제거할 수 있습니다. [Ctrl] 키를 눌러 아래 도로 위로 다리를 지을 수 있습니다.}

simutrans-124.5/simutrans/text/ko/underground.txt000066400000000000000000000041041520675460500223020ustar00rootroot00000000000000지하 건설 도움말 Help

지하 건설

Simutrans에는 두 가지 지하시점이 있습니다, 두 가지 모두 디스플레이 설정 창에서 접근하거나 단축키를 사용하여 접근할 수 있습니다.

지하 시점: [U] 키를 눌러, 모든 지하 레벨이 공개되고 모든 지상 위의 물체가 숨겨집니다. 지표 그리드는 기본적으로 표시됩니다, 하지만 이 그리드 패턴을 사용하여 원하는 위치에 객체를 배치하는 것은 매우 어렵기 때문에 다음 지도 시점이 지하 건설에 권장됩니다.

층 시점: [Ctrl+U] 를 통해 Simutrans에서 단일 높이 레벨로 지하가 표시됩니다. 이 시점은 선택한 층 위에 있는 지하, 표면의 모든 객체를 숨깁니다. 현재 시점 레벨은 디스플레이 설정에서 조정하거나 pakset의 특정 를 통해조정할 수 있습니다. 활성화된 층의 그리드 패턴이 보여집니다. 마우스 포인터는 현재 레벨에서만 움직이므로 건설 작업에 더 용이합니다.

일반적으로, 지하 노선은 터널 건축 도구를 사용하여 건설됩니다. 지하에 짓기 위해서는 터널이나 터널 입구 (경사면을 터널 도구로 Ctrl + 클릭) 가 먼저 만들어져야 합니다.

지하에 또 다른 층을 만들려면, 풍경 도구 메뉴의 인공 경사 만들기 도구를 사용합니다. 이 도구로 도로/트랙의 끝만 클릭하면 됩니다.

노면전차 트랙은 일반 노면전차 도구 를 사용하여 지하에 설치할 수 있습니다.

오직 정거장/역 만이 서로의 위에 만들 수 있습니다. 역은 지하에 지을 수 없습니다.

참고: 더 지하를 만들 수 없다면 지상의 장애물을 확인하십시오.

simutrans-124.5/simutrans/text/ko/window.txt000066400000000000000000000234621520675460500212650ustar00rootroot00000000000000게임 인터페이스 사용법

게임 인터페이스

Simutrans는 현재 게임에 대한 정보, 도구 모음(toolbars) 및 게임 컨트롤에 대한 접근과 게임 월드 내 시야를 제공하는 게임 인터페이스를 통해 플레이 됩니다. Simutrans는 마우스키보드 를 통해 컨트롤 할 수 있습니다. 키보드 구성은 단축키 섹션의 도움말 메뉴에서 확인 가능하며, [F1] 키를 누르거나 할당되지 않은 키를 누를 때마다 확인 가능합니다.
중요사항: 키들은 대소문자를 구분합니다.

게임 창

게임 월드의 일부를 보여줍니다. 마우스 휠이나 [PgUp]/[PgDown] 키, 주 도구 모음(toolbar)에 있는 두 가지 전용 아이콘을 통해 확대와 축소를 할 수 있습니다. 지도를 스크롤하는 방법:
? 마우스 오른쪽 버튼을 누른 상태에서 원하는 위치를 향해 지도를 스크롤한다. 지도가 마우스의 움직임에 따라 스크롤되는 것이 기본 설정입니다. 디스플레이 설정 창에서 변경할 수 있습니다.
? 숫자 키패드를 사용한다.
? 지도 창에서 적절한 지점을 선택하여 원하는 지점으로 이동한다.
? 점프 다이알로그 ([J] 키)를 사용하여 특정 좌표로 이동한다.

마을, 공장, 역, 차량 및 수송선과 같은 여러 객체의 이름을 편집 할 수 있습니다. 편집하려면 조사 도구 (아래 참조)로 연 다이알로그의 이름 필드에서 마우스로 클릭하십시오. 수정 사항은 즉시 적용됩니다. [Return]을 누르면 필드에서 나갑니다.

타이틀 바

타이틀 바의 왼쪽 부분은 시뮤트랜스의 버전 및 업데이트 날짜를 표시합니다. 오른쪽 부분에서는 게임 인터페이스를 최소화, 최대화하거나 Simutrans를 종료할 수 있습니다.

게임 창 상단에는 Simutrans 게임을 할 때 게임 툴바 및 게임 컨트롤에 접근하는 데 사용되는 아이콘들 (주 도구 모음(toolbar))이 있습니다. 여러 메뉴들로 그룹화 되어있는 아이콘들 (클릭해서 사용):

주 메뉴

옵션: 플로피 디스크 아이콘은 새로운 게임을 로드, 저장 또는 시작할 수있는 다이알로그를 엽니다.

지도: 그 자체가 목적인 오버뷰 월드 지도는 플레이어의 차량 움직임을 보여주고 몇몇 통계 치를 볼 수 있게 해줍니다. 지도에서 한 지점을 선택하면 게임 창에서 해당 이미지 섹션으로 이동합니다.

조사 도구: 돋보기 아이콘은 검색 기능을 상징합니다. 활성화 된다면, 커서가 돋보기 모양이 됩니다. 건물, 차량 등의 객체를 선택하면 그 위에 정보 창이 나타납니다. 특히 제거 도구 로 작업 한 경우, 항상 돋보기로 돌아가는 것이 좋습니다. 객체의 정보를 조사하는 대신 실수로 객체를 제거하지 마십시오.

조경 도구: 경사로 아이콘은 각 타일의 기울기를 변경하여 지형을 변경할 수 있는 도구 모음(toolbar)을 엽니다.

건설 도구: 각 운송 유형의 도구 모음(toolbars)를 엽니다.
?철도 도구
?모노레일 도구
?노면전차 도구
?도로 도구
?선박 도구
?비행기 도구

참고: 일부 펙셋에는 협괘(광궤) 철도 도구 와 같은 추가 건설 도구가 포함될 수 있습니다.
각 도구 모음(toolbars)에는 차고 아이콘이 있습니다. 차고가 지어진다면, 차량에 접근하기 위해 차고 창 으로 들어갈 수 있습니다.

특수 건설 도구: red-drop-in-yellow-circle 아이콘 (pak128의 크레인 아이콘)을 클릭하면 열리는 도구 모음 (toolbar)에는 교통 네트워크 구축을 보완하는 다양한 구성 도구가 포함되어 있습니다.

파괴/제거 도구: 적색 X 아이콘 (pak128의 불도저 아이콘)은 지도에서 다양한 객체를 제거하는 데 사용됩니다.

관리 메뉴

노선 관리: 수송 노선을 여기 (네트워크 아이콘)에서 설정하거나, 변경하거나 삭제할 수 있을 뿐만 아니라 그 정보를 볼 수도 있습니다.

목록: 목록 아이콘에서 이어지는 7개의 하위 목록:
?역 목록
?차량 목록
?도시 목록
?상품 목록
?공장 목록
?관광 명소 목록
?표시판 목록

통신 센터: 사서함 아이콘을 클릭하면 게임을 시작한 이후, 혹은 마지막으로 로드한 이후 생성된 모든 메세지가 나열됩니다.

재정: 이 돈 아이콘은 현재 재무 상황에 대해 알만한 가치가 있는 모든 것을 보여주는 창을 열어줍니다.

다른 메뉴들

스크린샷: 카메라 아이콘 버튼을 누른다면, 현재 게임 창의 스냅 샷 (도구 모음(toolbars) 포함, 타이틀 바 제외)은 simutrans/screenshot/ 폴더에 .png 이미지로 저장됩니다. [c] 키를 사용하여 이미지를 캡처할 수도 있습니다.

일시정지: 당신이 운송 회사의 혼잡과 번잡에서 벗어나고 싶다면이 커피 머그 아이콘으로 차 또는 커피 한잔을 주문하십시오. 이 서비스는 물론 Simutrans에서 무료입니다!
이 키 [p] 는 게임 일시 중지 / 해제에도 사용할 수 있습니다.

빨리감기: Simutrans의 시간이 느리게 느껴진다면 이 >> 아이콘을 눌러 가속시킬 수 있습니다. 빨리감기는 [W] 키를 사용하여 선택 / 취소 할 수도 있습니다.
경고: 빨리감기 모드에선 게임이 원활하게 실행되지 않을 수 있습니다. 디스플레이 설정 창의 하단에 있는 디스플레이 성능 정보 섹션은 컴퓨터가 변경 사항을 처리 할 수 ??있는지 여부를 나타냅니다.

도움말: ? 아이콘은 [F1] 키와 함께 도움말 색인을 엽니다.

참고: 플레이 하는 pakset에 따라 주 도구 모음(toolbar)에 아이콘이 더 있을 수 있습니다: 확대/축소, 격자 보이기, 지도 회전, 등등

상태 바

게임 월드 창 하단에 있는 상태 바에는 게임이 진행함에 따라 지속적으로 업데이트 되는 정보가 포함되어 있습니다. 좌측에서 우측으로 정보가 제공되는 상태 바:
?날짜와 시간: Simutrans에서 1 년은 월, 일 및 시간으로 나뉩니다. 날짜는 '타임 라인'이 활성화 될 때 차량 및 건물이 사용 가능한지 제어합니다 (아래 참조). 날짜 앞에 기상 계절 아이콘이 표시됩니다. 일부 기후 설정 의 경우 시즌은 계절에 따라 변경될 수 있습니다.
?플레이어: 현재 플레이어의 이름이 플레이어의 색상으로 표시됩니다.
?잔액: 현재 플레이어의 잔액이 표시되고 지속적으로 업데이트됩니다. 건설 및 기타 비용 (차량 구매, 운송 인프라 유지 및 차량 운영 비용)을 고려합니다.
?위치 좌표: 괄호 안은 커서가 현재 위치한 타일의 xy 좌표와 높이 로 표시됩니다. 좌표 (0,0) 은 지도의 왼쪽 상단 모서리에 있고 높이 0은 해수면입니다. (맵의 수위 설정에 따라 다름)
?게임 속도: (T = 1.00)은 현재 게임 속도를 나타냅니다. [,]/[.] 키를 사용하여 약간의 시간 단위로 가속 / 감속시킬 수 있습니다. 디스플레이 설정 창의 하단에 있는 디스플레이 성능 정보 섹션은 컴퓨터가 변경 사항을 처리 할 수 ??있는지 여부를 나타냅니다. 빨리감기 모드에선 추가 아이콘 >>이 표시됩니다.
?연대 설정: 현재 게임에서 타임 라인 이 선택되었는지 달력 아이콘으로 나타냅니다.

스크롤 바

경우에 따라, 메시지가 게임 월드 창 하단, 상태 바 위에 표시됩니다. 표시 할 메시지는 통신 센터 옵션 에서 정의 할 수 있습니다.
팁: 메시지를 클릭하면 게임 시점을 관련 위치로 이동 시킵니다.

simutrans-124.5/simutrans/text/lt.tab000066400000000000000000001301601520675460500177050ustar00rootroot00000000000000§Lithuanian PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: lt Lithuanian # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable išjungtas cl_btn_filter_enable įjungtas cl_btn_filter_settings Nustatymai cl_btn_sort_asc didėjančiai cl_btn_sort_desc mažėjančiai cl_btn_sort_id numerį (ID) cl_btn_sort_income Pajamas cl_btn_sort_name Vardą cl_btn_sort_type Rūšį clf_btn_alle visi clf_btn_invers atv. clf_btn_keine joks gl_btn_sort_bonus pagal bonusą gl_btn_sort_name pagal vardą gl_btn_sort_revenue pagal pajamas gl_btn_unsort nesurikiuota hl_btn_filter_disable išjungti hl_btn_filter_enable įjungti hl_btn_filter_settings Nustatymai hl_btn_sort_asc didėjančiai hl_btn_sort_desc mažėjančiai hl_btn_sort_name Pavadinimą hl_btn_sort_type Rūšį hl_btn_sort_waiting Laukia hlf_btn_alle visi hlf_btn_invers atvirkščiai hlf_btn_keine joks Networks Tinklai Queueing Laukimo eilė Road toll Kelio mokestis Transfers Juda per #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic arktinis klimatas desert dykumų klimatas mediterran viduržemio j. klimatas rocky kalnų klimatas temperate vidutinis klimatas tropic tropikai #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Cannot built depot here! Negalima čia statyti depo. Cannot built this station/building\nin underground mode here. Pastatas negali būti\npastatytas po žeme.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. Negalima sukurtu bet kam linijos!\nPasirinkite tr. priemonės tipą\nviršutinėse lango auselėse. Cannot create socket Negalima sukurti lizdo Convoi handles exhausted! Pasiektas maksimalus transporto priemonių skaičius. Convoy already deleted! Konvojus jau ištrintas! Das Feld gehoert\neinem anderen Spieler\n Šitas žemės lopinėlis\nvaldomas\nkito žaidėjo!\n Der Besitzer erlaubt das Entfernen nicht Laukelio savininkas atsisako\nduoti leidimą\nsunaikinti šitą objektą!\n Diese Zusammenstellung kann nicht fahren!\n Šita kombinacija\nnevažiuoja!\n Flugzeughalt muss auf\nRunway liegen!\n Lėktuvo sustojimas\nturi būti ant orouosto dangos. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Jūs negalite čia statyti\n\naerousto pastatų.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Jūs čia negalite\nstatyti signalo.\n In order to lock the game, you have to protect the public player by password! Norėdami apriboti kitų žaidėjų prieigą prie žaidimo, uždėkite viešam (valstybės, public) žaidėjui slaptažodį. Lost connection\nto server! Prarastas ryšys\nsu serveriu! Lost synchronisation\nwith server. Prarasta sinchronizacija\nsu serveriu! Maglevhalt muss auf\nMaglevschiene liegen!\n M. levitacijos traukinio stotelė privalo būti ant atitinkamų bėgių! Monorailhalt muss auf\nMonorail liegen!\n Vienbėgio stotelės privalo\nbūti ant atitinkamų bėgių. Monorails are not available yet! Vienbėgiai dar neatsirado. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Siauruko sustojimas tik ant atitinkamų bėgių No through station here! Neįmanoma čia pastatyti stoties ji turi būti ant kelio galo arba sekcijose be sankryžos. Not enough money! Trūksta pinigų statyboms! On narrowgauge track only!\n Tik ant siauruko bėgių! Only public player can lock games! Tik viešas (valstybės, public) žaidėjas gali užrakinti žaidimą! Out of funds Trūksta pinigų! Post muss neben\nHaltestelle\nliegen!\n Stotelės išplėtimas\n(sandėlis, paštas)\nprivalo būti šalia\negzistuojančios stotelės.\n Protocoll error (expecting game) Protokolo klaida (tikimasi žaidimo) Schiffhalt muss im\nWasser liegen!\n Laivų sustojimas\n gali būti tik ant vandens\nšalia prieplaukos!\n Server busy Serveris užsiėmęs Terraforming not possible\nhere in underground view Neįmanoma po žeme tvarkyti aplinkos. Upgrade must have\na higher level Atnaujinimas turi būti\naukštesnio lygio Vehicle %s cannot choose because stop too short! Transportas %s negali sustoti stotyje, kadangi stoties platforma per trumpa! Zughalt muss auf\nSchiene liegen!\n Geležinkelio stotys\n turi būti padėtos ant bėgių.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Keyboard Help\n

Keyboard Help

\n Klaviat?ros pagalba\n

Klaviat?ros pagalba

\n

\nKlaviat?ros pagalba rodo ?vairi? mygtuk? funkcijas.\n

\n

\nKlaviat?ros pagalba atsirado tada kai paspaudžiana neegzistuojanti klaviš? kombinacija General Help.\n

\n

\nmygtukai skiria didžiasias ir mažąsias raides (naudoti [Shift] pakeisti raides(ne caps lock)).\n

\n

\nFunkcionuojantys mygtukai:\n

\n

\n[Rodykl?s]: Slenka žaidimo vaizdą.
\n[Backspace]: uždaro visus langus, ?rankines ir pagalbos tekstus.
\n[Delete], arba [Escape]: uždaro aktyv? langą, ?rankinę ar pagalbos tekstus žaidime.
\n[Enter], arba [Return]: patvirtina veiksmus.
\n[Page-Up], arba [>]: artina vaizdą.
\n[Page-Down], or [ less than symbol]: tolina vaizdą.\n[F1]: atveria Simutrans pagalbą.

\n

\n[1]: slenka vaizdą.
\n[2]: slenka vaizdą.
\n[3]: slenka vaizdą.
\n[4]: slenka vaizdą.
\n[6]: slenka vaizdą.
\n[7]: slenka vaizdą.
\n[8]: slenka vaizdą.
\n[9]: slenka vaizdą.\n

\n

\n[Shift] + pel?: naudojama Žem?lapyje rodyti jungtis tarp gamykl?tiekimo linij?.
\n[CTRL] + tool: construct (signals & Stops) on upper level; or build slower roads and tracks over faster ones; or build straighter (more direct) roads and tracks.
\n[CTRL] + ([F2] to [F12]): sets select current tool to keypresses [F2] to [F12].\n

\n

Highlight railroad tracks Paryškinti bėgius Highlite depots Paryškinti depus Highlite electrical transmission lines Paryškinti elektros linijas Highlite factories Paryškinti gamyklas Highlite forests Paryškinti miškus Highlite tourist attraction Paryškinti lankytinas vietas Overlay city limits Miesto ribų vaizdas Overlay passenger destinations when a town window is open Keleivių tikslų vaizdas Overlay schedules/network Tinklų/grafikų vaizdas Overlay town names Miestų pavadinimų vaizdas Please click on the map to add\nwaypoints or stops to this\nschedule. Spauskite ant žemelapio, norėdami \npridėti naujų stotelių ir krypčių\n į grafiką. Show capacity and if halt is overcrowded Rodyti stočių talpą ir perkrovimo būklę. Show how many convoi reach a station Rodyti kiek konvojų sugebės pasiekti stotį Show how many people/much is waiting at halts Rodyti kiek laukia Show initial passenger departure Rodyti pradinius kelionės tikslus Show level of city buildings Miesto pastatų lygių vaizdas Show mail service coverage/mail network Pašto paslaugų/pasiekiamumo vaizdas Show passenger coverage/passenger network Keleivių tinklo vaizdas Show speedlimit of ways Greičio apribojimų vaizdas Show the change of waiting at halts Keleivių ir prekių laukiančių stotyse pokyčio per mėnesį vaizdas Show the owenership of infrastructure Rodyti infrastruktūros savininką Show transported freight/freight network Pervežti kroviniai tinkle Show usage of network Transporto priemonės per mėnesį pravažiavusios laukelyje Shows a listing with all industries on the map. Rodo visų įmonių sarašą. Sum of departure/arrivals at halts Visi atvykimai/išvykimai stotyje #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s dabar\nsi?lo autobuso paslaugas\ntarp %s\nir lankytinos vietos\n%s\nat (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s dabar\nsi?lo autobuso paslaugas\ntarp %s\nir gamyklos\n%s\n (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\ndabar valdo\n%i sunkvežimius tarp\n%s esančios (%i,%i)\nir %s\n (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\natidar? naują geležinkel?\ntarp %s\n (%i,%i) ir\n%s\n (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Naujos oro linijos\n%s\ndabar tarp\n%s \nir %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Kompanijos\n%s\keltas veikia tarp\n%s \nir %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\natidarė naują\nautobusų liniją tarp\n%s ir\n%s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Žemėlapio redagavimo įrankiai LISTTOOLS Sąrašai MAGLEVTOOLS Maglev įrankiai MONORAILTOOLS Vienbėgiū traukinių statybos įrankiai NARROWGAUGETOOLS Siauruko stat. įrankiai RAILTOOLS Geležinkeliai ROADTOOLS Keliai SHIPTOOLS Uostų statybos įrankiai SLOPETOOLS Reljefo įrankiai SPECIALTOOLS Spec. statybos įrankiai TRAMTOOLS Tramvajų/lengvojo traukinio įrankiai #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s'as pastat? naują vadavietę. Factory chain extended\nfor %s near\n%s built with\n%i factories. Ekonomika pakilo: \n%s šalia %s praplėtė gamybą. \n%i pastatyta nauja gamykla. New %s now available:\n%s\n Naujas %s :\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Ekonomikos pakilimas: Nauja gamyklų linija\nskirta %s šalia\n%s sudaryta iš\n%i gamyklų. New vehicle now available:\n%s\n \n Atsirado nauja\n tr. priemonė:\n\n\n -- %s --\n\n Now %u clients connected. Dabar %i žaidėjų prisijungę. Remove vehicle from map. Use with care! Naikinti transportą. (Naudoti atsargiai!) Screenshot\ngespeichert.\n Ekrano fonas\nišsaugotas.\n Sends the convoi to the last depot it departed from! Siunčia konvojų į praeitą depą iš kurio išvyko With a big festival\n%s built\na new monument.\n%i citicens rejoiced. Švesdami,\n%s pastatė\nnaują paminklą.\n%i miestiečių dalyvavo ceremonijoje. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Linijos filtras #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (garaže) \nBauzeit bis iki \nBauzeit von \nAtsiranda nuo \nCan't open heightfield file.\n \nNe?manoma atverti aukščio žem?lapio.\n \ndirection: \nkryptys: \nelektrified \nelektrifikuotas\n \nHeightfield has wrong image type.\n \nNetinkamas failas ukščio žem?lapiui\n \nis reserved by: \nparuošta kitam traukiniui: \nminimum speed: \nmažiausias greitis: \nnot elektrified \nneelektrifikuota\n \nRibi (masked) \nkryptys (masked) \nRibi (unmasked) \nkryptys\n (unmasked): \nSet phases: \nustatyti ilgį ns/ew: \nsingle way \nViena kryptis \nway1 reserved by kelias 1 reservuotas \n \nway2 reserved by kelias 2 užimtas: \n \nwith sign/signal\n \nsu ženklu/signalu\n %d buildings\n %d pastatai\n %d convois %d konvojai %d Einzelfahrzeuge im Depot %d transporto priemon?s laikomos čia. %i years %i months old. %i metų %i mėnesių amžiaus. %s at (%i,%i) now public stop. %s tapo viešai prieinama. %s building %s %s %s %s %s %s city %d %s %s miestas %d %s %s factory %s %s %s %s aikštelė %s has entered a depot. %s pasiekė depą. %s land %d %s %s žem? %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s \npastatė\nnaują rotušę\nkada pasiekė\n%i gyventojus. %s\nis crowded. %s\n užimta. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nmaksimalus greitis %3$i km/h\n\nesamas greitis %2$i km/h.\n\n %s\nwas liquidated. %s įmonė bankrutavo\nir buvo likviduota.\nVisa veikla sustabdyta,\no turtas išparduotas. %u Client(s)\n %i klientas -ų\n %u Player (%u locked)\n %u žaidėjų (%u užrakinta)\n 1 convoi 1 konvojus 1 Einzelfahrzeug im Depot 1 transporto priemon? laikoma čia. 1LIGHT_CHOOSE Ryškumas: 1WORLD_CHOOSE Nustatymai naujam žaidimui: 2LIGHT_CHOOSE Spalvos: 2WORLD_CHOOSE Žemėlapio numeris: 3LIGHT_CHOOSE Lango slinkties greitis: 4LIGHT_CHOOSE Atvirkštine slinktis 5LIGHT_CHOOSE P?stieji stotel?se 5WORLD_CHOOSE Gyvenviečių skaičius: 6LIGHT_CHOOSE P?stieji gatv?se 6WORLD_CHOOSE Eismo intensyvumas: 8WORLD_CHOOSE dienos ir nakties kaita A bridge must start on a way! Tiltai privalo\nprasid?ti ant\negistuojančio kelio. Abfrage Tikrinimo ?rankis Abnehmer Vartotojas About Apie Abriss Sunaikinti/pašalinti Absenken Nuleisti žemę Abspanntransformator Transformatorius Accelerate time ?sib?g?jimo laikas Act. load: %u MW\n \nApkrova: Active player only Tik aktyvus žaidėjas Add forest Prid?ti mišką Add random citycar Pridėti bet kokį automobilį add server Pridėti serverį Add Stop Pastatyti stotelę Add stops for backward travel Prid?ti stot? atgaliniam keliavimui. air takas aircraft_tab Krovininiai l?ktuvai airplane L?ktuvas Airport Aerouostas AIRTOOLS Orousto statybos ?rankiai All Visi all convoi tooltips visi konvojų pranešimai Allow city growth Leisti miestui augti Allow player change Leisti žaid?jo apsikeitimą allowed climates:\n Leidžiami klimatai:\n Alters a schedule. Prideda/naikina stoteles iš grafiko Angenommene Waren Prek?s reikalingos kitoms gamykloms anhaengen Išardyti Anhaenger_tab Priekabos Anheben Kelti žemę Appends stops at the end of the schedule Prid?ti stotelę grafiko gale Apply Line Priskirti liniją April Balandis Arbeiter aus: Darbininkai gyvena: Arrived Atvykę Assets Turtas Aufloesen Keisti Aufspanntransformator Transformatorius August Rugpjūtis Autohalt muss auf\nStrasse liegen!\n Autobuso ar automobilio stotelės\nturi būti\nant kelio. Available Galimi Bahndepot Traukinių depas Bankrott:\n\nDu bist bankrott.\n Bankrotas:\n\nTU BANKRUTAVAI!\n battery Baterija Baum Medis baum builder Sodinti medžius Baustelle Statybvietė Bauzeit statymo laikas Beenden Išeiti Beginner mode Naujoko rėžimas Besonderes Gebaeude Lankoma vieta BF stotis bio biologinis Blockstrecke ist\nbelegt\n \nBėgių blokas naudojamas\nkito traukinio!\n Boden Žemė Bonusspeed: %i km/h Maks. greitis: %i km/h Boost (%%) Produktyvumas (%%) bridge is too high for its type! Tiltas per aukštas savo tipui! Bridge is too long for this type!\n Tiltas per ilgas\nnaudokite kitą\ntipą.\n Bruecke Tiltas Bruecke muss an\neinfachem\nHang beginnen!\n Tiltas privalo\nprasidėti ant\ntiesios nuokalnės!\n Brueckenboden tiltas Build air depot Statyti angarą build choosesignals Pastatyti "Rinkis platformą" signalus. Build city market Pastatyti naują prekybos centrą artimiausiame mieste. Build drain Transformatorinė build HQ Statyti būstinę Build land consumer Pastatyti elektrinę. Build maglev depot Statyti maglev depą Build monorail depot Pastatyti vienbėgio depą Build narrowgauge depot Statyti siauruko depą Build powerline Pastatyti elektros trasą Build presignals Pastatyti presignalus Build road depot Pastatyti garažą Build ship depot Pastatyti laivų statyklą Build signals Įrengti signalus Build train depot Pastatyti traukinių depą Build tram depot Pastatyti tramvajų depą Build truck depot Statyti garažą Building costs estimates Numatoma statybų kaina Buildings Pastatai Built artifical slopes Sukasti šlaitus Built random attraction Pastatyti atsitiktinę lankomą vietą Bus_tab Autobusai Can only move from halt to halt or waypoint to waypoint. Galima judėti\ntik nuo gairės iki gairės\nar nuo sustojimo iki sustojimo. Cancel Atšaukti Cannot connect to offline server! Negalima prisijungti prie serverio: serveris atsijungęs. Capacity: %.0f MW Pralaidumas:%.0f MW\n Capacity: %d%s %s\n Talpa: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Talpa: %s\n? %d (%d%%) Cars are not available yet! Mašinos dar neprieinamos. cars.\nstate automobiliai\n Cash Sąskaitos balansas Change player Pakeisti žaidėją Chart Diagrama Chat_msg Čatas Choose direction Rinkitės kryptį Choose operation executed on clicking stored/new vehicles Pasirenkamas veiksmas vykdomas paspaudus ant saugomų/naujų transporto priemonių chooses a random map Pasirenkamas atsitiktinis žemėlapis. citicens Gyventojai City attraction Lankoma vieta City industries Prekybos centrai miestuose City list Miestų sąrašas City size Miesto dydis city_road Miesto kelias citybuilding builder Miesto pastatų įrankis CityLimit Miesto ribos cl_title Tr. priemonių sąrašas cl_txt_sort Surušiuoti pagal: Clear block reservation Naikinti bloko rezervaciją clf_chk_aircrafts Lėktuvai clf_chk_cars Autobusai/sunkvežimiai clf_chk_indepot depe clf_chk_maglev Magnetiniai tr. clf_chk_monorail Vienbėgiai clf_chk_name_filter Filtrų pavadinimai: clf_chk_narrowgauge Siauruko traukiniai clf_chk_noincome nėra pajamų clf_chk_noline be linijos clf_chk_noroute nėra kelio clf_chk_noschedule nėra grafiko clf_chk_obsolete Pasenę clf_chk_ships Laivai clf_chk_spezial_filter Specialus filtras: clf_chk_stucked užstrigo clf_chk_trains Traukiniai clf_chk_trams Tramvajai clf_chk_type_filter Filtruoti tipus: clf_chk_waren Filtruoti pagal krovinius: clf_title Tr. priemonių sąrašo filtras Climate Control Nustatyti klimatą closed uždaryta COLOR_CHOOSE\n Prašome pasirinkti\nspalvą iš lentelės:\n Company bankrupt Kompanijos bankrotas Company_msg Konkurentai Comparing pak files ... Lyginami pak paketai... Configure AI Konfigūruoti DI Configure AI setttings Nustatyti Dirbtinio Intelekto nustatymus Congratulation\nScenario was complete in\n%i months %i years. Sveikiname!\njūs pabaigėte \nscenarijų per \n%i mėnesių ir %i metų! Connected stops Aptarnaujamas stotelių: Connected with server Prisijungta prie serverio Constructed by Nupiešė : Constructed by %s Nupiešė %s construction speed statymo greitis Construction_Btn Statybos/pirkimo kaina Consumed Suvartota convoi %d of %d Konvojus %d iš %d convoi error tooltips konvojaus pranešimai Convoi has been sent\nto the nearest depot\nof appropriate type.\n Konvojus buvo išsiųstas\nį artimiausia depą. \n Convoi is sold when all wagons are empty. Transporto priemonė bus perduota iškart kai bus tuščia. convoi mouseover tooltips konvojų pranešimai užvedus rodyklę convoi passed last\nmonth %i\n \nkonvojai pravažiavę praeitą\nmėnesį: %i\n Convois Konvojai Convois: %d\nProfit: %s Tr. priemonės: %d\nPelnas: %s Convoys Konvojai Copy Convoi Nukopijuoti mašiną Copy the selected convoi and its schedule or line Kopijuoti pasirinkta konvojų ir jo grafiką ar visą liniją cost for removal Pašalinimo išlaidos Costs Kainuoja Create a new line based on this schedule Sukurti linija paremta šiuo grafiku curiosity builder Įvairūs pastatai curlist_title Lankomų vietų sąrašas CURRENCY € Currently playing: Grojama: Customers live in: Klientai gyvena: deactivated in online mode Neveikia "online" rėžime Deccelerate time Sulėtinti laiką December Gruodis decrease underground view level nuleisti požeminį vaizdą Del Stop Pašalinti Delete Line Pašalinti liniją Delete the current stop Sugriauti stotelę Delete the selected line (if without associated convois). Ištrinti pasirinktą liniją (jei linija tusčia) Delete this file. Ištrinti šį failą Delivered Nusiųsta Demand Paklausa Demand: %.0f MW Paklausa: %.0f MW \n Denkmal Monumentas Departed Išvykę Depots Depai Der Tunnel ist nicht frei!\n Tunelis nėra tuščias.\n Destination Tikslas Destroying map ... Naikinamas žemėlapis ... Details Informacija Die Bruecke ist nicht frei!\n Tiltas užimtas \n diesel dyzelis Direkt erreichbare Haltestellen Tiesioginiai maršrutai iš šios vietos disable midi Išjungti MIDI muziką Display settings Vaizdo nustatymai Distance Atstumas Dock prieplauka Dock must be built on single slope! Dokai turi būti pastatyti ant lygios pakrantės. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Tu turi %d mėnesių gražinti skolą. Durchsatz Max. Economy Miestai ir ekonomika Eigenbesitz\n Vieša nuosavybė\n Ein %s\npasst hier nicht.\n '%s'\nčia netinka!\n Einstellungen aendern Pakeisti nustatymus electric elektrinis Electricity Elektra Electricity producer\n\n Elektros gamintojas\n\n Electrics_tab Elektriniai Electrify track Elektrifikuoti bėgius enlarge map Padidinti žemėlapį enter a value between %i and %i Įveskite reikšmę tarp %i ir %i Enter address Įvesti adresą Enter Password Keisti žaidėjo vardą ir slaptažodį Error Klaida Erzeuge neue Karte.\n Prašom palaukti kol naujas\nžemėlapis kuriamas.\n\n(Tai gali užtrukti kelias\nminutes dideliems žemėlapiams.) Es wird bereits\nein Fahrplan\neingegeben\n Grafikas\nsuplanuotas.\nPirmą jį pabaikite\nprieš pakeisdami jį iš naujo!\n Fabrikanschluss Sujungtos gamyklos Fabrikname gamyklos pavadinimas Factories Gamyklos factory details Gamyklos informacija factorybuilder Statyti gamyklą Fahrplan Grafikas Fahrtziel Tikslas: Fahrzeuge koennen so nicht entfernt werden mašinos negali būti\npašalintos tokiu būdu!\n Fahrzeuge: Transportas: Farbe Žaidėjo spalvos Fast forward Prasukti laiką February Vasaris Ferry_tab Keltai Fertig Atlikta Filename Failas: Filter: Filtras: Finances of %s Financai %s Finanzen Financai find mismatch Palyginti pak fl_title Gamyklų sąrašas Flug_tab Keleivinis lėktuvas follow me Sek mane! Follow the convoi on the map. Sekti konvojų žemėlapyje. Forest Miškas Found new city Įkurti naują miestą FPS: Kadrai/s: Fracht Krovinys Frame time: Kadro laikas: Free Capacity Laisva Talpa freeplay mode laisvas žaidimas Friction: Trinties faktorius: fuel_cell kuro elementas Full load Minimali apkrova: Fundament Pamatas Fussgaenger Pėstysis Game info Žaidimo info GAME PAUSED Žaidimas sustabdytas Game_msg Bendri Gear: Pavara: Gebaeude Pastatas General Pagrindiniai Generated Sugeneruotas Generation: %.0f MW Gamyba: %.0f MW\n Gewicht Svoris Gewinn Pelnas: Give the selected vehicle(s) an individual schedule Duoti transporto priemonei nuosavą grafiką. gl_btn_sort_catg pagal kategoriją gl_title Visas prekių sąrašas go home Į depą. Goods Prekės Goods AI Prekių DI Goods list Prekių sąrašas Gross Profit Pinigų prieaugis Groundobj Objektas Grow city Plėsti miestą. Growth Miesto augimas H stotelė Hangar Angaras Happy Laimingi Haus kaufen Nupirkti namą Helligk. Rodyti Help Pagalba Help text not found Trūksta pagalbinio teksto. hide all building slėpti pastatus hide city building slėpti miesto pastatus hide objects under cursor paslėpti objektus po kursoriu hide station names slėpti stotelių pavadinimus hide transparent permatomi užuot paslėpti hide trees Slėpti medžius Hier warten/lagern: Laukiantys keleiviai ir prekės: Higher transport fees, crossconnect all factories Didesni transporto mokesčiai, išjungti "Just-In-Time". Highlite schedule rodyti grafiko stoteles hl_title Stočių sąrašas hl_txt_filter Filtras: hl_txt_sort Sutrumpinta pagal: hlf_chk_airport Aerouostai hlf_chk_anleger Dokai hlf_chk_bahnhof Geležinkelio stotis hlf_chk_bushalt Autobuso stotelė hlf_chk_frachthof Krovos punktas hlf_chk_keine_verb Nesujungta hlf_chk_maglevstop Magnetinio tr. stotelė hlf_chk_monorailstop Vienbėgio stotelė hlf_chk_name_filter Filtro pavadinimas: hlf_chk_narrowgaugestop Siauruko stotelė hlf_chk_overflow Virš talpos hlf_chk_spezial_filter Specialus filtras: hlf_chk_tramstop Tramvajaus stotelė hlf_chk_type_filter Filtro tipai: hlf_chk_waren_abgabe Produkcija: hlf_chk_waren_annahme Reikalingos prekės: hlf_title Stočių sąrašo filtras Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depas nerastas.\nSiųskite transporto priemonę\nrankiniu būdu. Homeless "Benamių" hydrogene Vandenilis Idle: Laisvi: ignore climates nekreipti dėmesio į klimatą In the industry legend show only currently existing factories Rodyti tik egzistuojančias gamyklas Increase Industry density Padidinti gamyklų tinklo tankį increase underground view level pakelti požeminį vaizdą industrial building Pramoninis pastatas Init map ... Paledžiamas žemėlapis ... Input įvestis Ins Stop Įterpti Insert stop before the current stop Įterpia stotelę prieš pasirinktą Intercity road len: Tarpmiestinio kelio ilgis: Intro. date: Atsiradimo data: invalid nenustatyta. Invalid coordinate Netinkamas nurodymas isometric map Izometrinis žemėlapis January Sausis join game Prisijunk ! July Liepa Jump to Peršokti į June Birželis Kann Spielstand\nnicht laden.\n Neįmanoma užkrauti išsaugoto žaidimo! Kann Spielstand\nnicht speichern.\n Neįmanoma atverti\npasirinktos bylos\nįrašimui! Kein Besitzer\n Neturi savininko\n keine n?ra Keine Einzelfahrzeuge im Depot Čia nėra transporto priemonių. Keyboard_Help\n Klaviatūros pagalba\n koord koordinatės Kreuzung Pervaža labellist_title Žymeklių sąrašas Lade Relief Aukščių žem. Laden Užkrauti Land attraction Traukos vieta Land industries Įmonių grandinės LANG_CHOOSE\n Prašome pasirinkti kalbą:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Pask. mėnuo: Last Year Praeiti metai: Leaving depot! Palieką depą! leer tuščia Legend Sutartiniai ženklai Leistung Galia Leistung: %d kW Galia: %d kW Leitung Elektros linija letzen Monat: diesen Monat: Praeitą mėnesį: Šį mėnesį: Line Linija Line Management Linijos valdymas Lineless convoys serving this stop Konvojai be linijos aptarnauja šią stotelę Lines serving this stop Linijos aptarnaujančios šią stotelę LKW_tab Sunkvežimiai Load game Užkrauti žaidimą load height data from file Užkrauti aukščių duomenis iš failo. Load scenario Užkrauti scenarijų loaded užkrauta loaded passenger/freight Rikiuoti prekes/krovinius pagal Loading (%i->%i%%)! Krauna (%i->%i%%) Loading addon paks ... Krauna papildinius ... Loading map ... Kraunamas pasaulis... Loading paks ... Kraunami pak'ai... Loading skins ... Kraunamos temos ... Lock game Uždrausti žaidėjų apsikeitimą (reikia patvirtinimo). Lokomotive_tab Lokomotyvai m3 m³ Maglev Magnetinis tr. maglev vehicle magnetinis traukinys maglev_track Magnetinės levitacijos bėgiai Maglevdepot Magnetinio traukinio depas Mail Demand %d\n Pašto paklausa %d\n Mailbox Žinutės Mailbox Options Žinučių nustatymai Maintenance Išlaikymas make stop public (or join with public stop next) costs %i per tile and level Paversti stotelę vieša (ar sujungti su gretimomis viešomis stotelėmis) kainuoja %i$ per aukštį ir langelį Manual (Human) Rankinis (žmogus) Manufactured: Pagaminta: Map roughness Žemėlapio šiurkštumas: map zoom Priartinimas March Kovas Margin (%%) Margin'as Marker Pastatyti ženklą Max Boost (%%) Maks. bonusas (%%) Max income: Max pajamos: Max. speed: Max greitis: Maximum 254 stops\nin a schedule!\n Daugiausia 254 stotelės\ngrafike!\n maximum length of rivers Max. upių ilgis Maximum tile height difference reached. Didžiausias aukščių\nskirtumas tarp\ndviejų langelių\nbuvo pasiektas. Maxspeed Maks. greitis May Gegužė Median Citizen per town Vidutinis miesto gyventojų sk.: Meldung Žinutė Menge kiekis MessageOptionsText \nNauji metai\n\nDI Naujienos\n\nMiesto naujienos\n\nnėra kelio\n\nNaujos įmonės\n\n"chat"\n\nNaujos tr. priemonės\n\nStotelė pilna\n\nProblemos\n\nTraffic jam\n\nScenario min min. minimum length of rivers Min. upių ilgis Missing pakfiles Trūksta objektų Modify the selected line Modifikuoti pasirinktą liniją Monate alt mėnesių amžiaus. Monorail Vienbėgis monorail vehicle Vienbėgis traukinys monorail_track Vienbėgio bėgiai Monorailboden Vienbėgio kelio atrama Monoraildepot Vienbėgio depas month wait time laukimo laikas mėnesiais Months Mėnesiai Monument Statula Monuments Statulos Mountain height Kalnų aukštis: Movingobj judantis objektas Music playing disabled/not available Muzikos groti neįmanoma arba ji išjungta. Music volume: Muzikos garsas: mute sound Be garso Name Pavadinimas Narrowgauge Siaurukas Narrowgauge are not available yet! Siauruko dar negalima statyti. narrowgauge vehicle Siauruko traukinys narrowgauge_track Siauruko bėgiai Narrowgaugedepot Siauruko depas Net ID: %p Tinklo Nr: %p\n Net Wealth Turto vertė Net wealth near zero Bendra turto vertė beveik 0 Neue Karte Naujas žaidimas Neue Welt Paleisti naują žaidimą new convoi Naujas sąstatas New Line Nauja linija New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Nauja linija įkurta!\nJūs galite pridėti naujas \n tr. priemones į liniją. New Vehicles Naujas tr. Nickname: Vardas: no buildings hidden pastatai nepaslėpti no convois Nėra konvojų No goods are loaded onto this convoi. Šiame konvojuje nebus pakrauta krovinių. no goods waiting nėra krovinių no load Nepakrauti No Route Nėra kelio No stop here! Įrankį naudoti tik ant stotelių lagelių. No suitable ground! Netinkama žemė! No terminal station here! Neįmanoma pastatyti \nstoties čia! Reikia statytiti ant jau egzistuojančių \n kelių ar bėgių. no timeline visos eros no tree Nėra medžių Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Duokite tvarkaraštį!\nPrieš paleisdami konvojų! none joks nord Šiaurė nordost Šiaurės rytai nordwest Šiaurės vakarai Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Transporto priemonės\ngrafiko nekeisti\nper kelio paiešką. Not enough fields would remain. Per mažai laukų\nliks aplink šią fermą. November Lapkritis Now active as %s.\n Dabar žaidžiate kaip %s.\n Number of rivers Upių kiekis Object Objektas Odometer: %s km Kilometražas: %s km Ok Gerai Oktober Spalis On loan since %i month(s) Skoloje %i mėnesius On this map, you are not\nallowed to change player!\n Žaidimas užrakintas.\nŽaidėjo keitimas uždraustas.\n Only city chains Tik miesto įmonės Only first %d differing paks reported. There are probably more. Tik pirmi %d pak'ai rodomi. Galimai jų skaičius didesnis. Only land chains Tik sausumos įmonės. Only one transformer per factory! Leidžiamas tik vienas transformatorius gamyklai! Only show goods which are currently handled by factories Rodyti tik prekes, kurias gali apdirbti gamykla open atvertas. Operation Veikimo kaina Ops Profit Atneštas pelnas Optionen Parametrai Or enter a server manually: Arba įveskite serverį rankiniu būdu: Origin Kilmė ost Rytai Output Išeiga Ownership Savininkai Pak which may cause severe errors: Trūksta šių paketų objektų, kas gali sukelti problemų ir sugadinti gamybos grandines: Pak which may cause visual errors: Paketų objektai pakeisti analogais: Pak(s) different: Pak skirtumai: Pak(s) missing on client: Paketai trūkstami kliento pusėje: Pak(s) not on server: Paketai nesantys serveryje: Pakset differences Paketų skirtumai paletten dėžės Pas_tab Keleiviniai traukiniai Passagiere Keleiviai Passagierrate Keleivių kiekis Passagierziele Keleivių/ pašto tikslai Passenger AI Keleivių DI Passenger Demand %d\n Keleivių paklausa %d\n Passengers %d %c, %d %c, %d no route Keleiviai %d %c, %d %c, %d neturi kelio Passengers %d %s, %d %s, %d no route Keleiviai %d %s, %d %s, %d neturi kelio Password Slaptažodis: Pause Pauzė Pax <%i> Mail <%i> Keleiviai <%i> Paštas <%i> PaxDest Tikslai Percent Electricity Elektros gamyba (%%paklausos): Planes are not available yet! Lėktuvai dar neasirado. Plant tree Sodinti medžius player Žaidėjas player -1 žmogus player 0 valstybė player 1 Napik 128 AS player 10 UAB "Busturas" player 11 žaidėjas nr. 11 player 12 žaidėjas nr. 12 player 13 žaidėjas nr. 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Eksppedicija VM player 5 Hayo, the conqueror UAB player 6 PSK & Co KG player 7 LG, valstybinė įmonė player 8 Kauno šiukšlių ir keleivių pervežimo kompanija player 9 UAB "K-Busas" Please choose vehicles first\n Pirma pasirinkite automobilį!\n Post Paštas Postrate Pašto lygis Power Galia Power (MW) Galia (MW) Power: Galia: Powerlines Elektros linijos Problems_msg Bėdos Produced Pagaminta Production of %s has been stopped:\n%s\n Gamyba %s sustabdyta:\n%s\n Production/Boost Produktyvumas/Bonusas Produktion Gamyba Profit Pelnas promote to line Paversti į liniją q1 Pavasaris q2 Vasara q3 Ruduo q4 Žiema Query server Ieškoti serverių rail car vagonas random atsitiktinis Random age bet kokie metai Random map Bet koks žemėlapis Rathaus Rotušė Rating Reitingas ratio_pax Keleiv. koef. Relevant Atitinkami Reliefkarte Žemėlapis Remove Pašalinti užrašą remove airstrips Naikinti pakilimo takus remove channels Sunaikinti kanalus remove interm. signals Pašalinti tarpinius signalus remove maglev tracks Šalinti magnetinius bėgius remove monorails Šalinti vienbėgį remove narrowgauge tracks Naikinti siauruką remove powerlines Pašalinti el. linijas remove roads Griauti kelią remove tracks Ardyti bėgius Remove wayobj %s Pašalinti kelio objektus %s replace other signals Pakeisti kitais signals replace stop Keisti stotelę request closing prašyti uždarymo residential house Gyvenamasis namas Restore natural slope Sugražinti kalvos šlaitą Restwert: Perpardavimo vertė: Retire. date: Pasens: return ticket kopijuoti atv. Revenue Pajamos Revision: Versija: road kelias road vehicle automobilis Roadsign Ženklas Rotate map Sukti vaizdą Rotation Sukimas Routing Kelio paieška sack maišai sail vėjas Saving map ... Išsaugomas žemėlapis ... Scenario complete: %i%% Scenarijus baigtas: %i%% Schedule changing! Grafikas keičiasi! Schienentunnel Statyti geležinkelio tunelį Schiff_tab Laivai Schiffdepot Laivų statykla Schleppkahn_tab Baržos Screenshot Nufotograpuoti ekrano foną. Seasons Metų laikai Sehenswuerdigkeit Lankytina vieta Select a server to join: Pasirinkite serverį prisijungimui: Sell the selected vehicle(s) Parduoti transporto priemones sended Išsiųsta pašto SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 September Rugsėjis Server did not respond! Serveris neatsako! Serves Line: Aptarnaujama linija: Service Aptarnavimas set signal spacing Nustatyti atstumą tarp signalų Setting Nustatymai Ship Laivai shops and stores Parduotuvės ir biurai Show all Rodyti viską show all building Rodyti visus pastatus Show also vehicles no longer in production. Rodyti ir automobilius kurie daugiau nebegaminami Show also vehicles that do not match for current action. Taip pat parodyti tr. priemones, kurios negali būti panaudotos. Show even servers with wrong version or pakset Rodyti ir serverius skirtus kitam pak'ui ar versijai. show grid Rodyti tinklelį Show industry Rodyti įmones Show legend Rodyti legendą Show map scale Spalvų kodai Show mismatched Rodyti nesutapimus Show obsolete Rodyti pasenusius Show offline Rodyti atjungtus Show only used Rodyti naudojamus Show schedules Rodyti maršrutus Show servers that are offline Rodo atjungtus serverius Show servers where game version or pakset does not match your client Rodyti serverius su nesutampančia žaidimo ar paketo versijomis esančiomis kliento pusėje show station coverage Rodyti stotelių aprėptį show station names Rodyti stotelių pavadinimus show waiting bars rodyti laukimo juostas show/hide block reservations rodyti/slėpti blokų rezervacijas show/hide object owner rodyti objektų savininkus Show/hide statistics Rodyti/slėpti statistiką Shows buttons on special topics. Rodyti mygtukus spec. straipsniams Shows consumer/suppliers for factories Rodyti tiekėjus ir vartotojus gamyklose Shows the color code for several selections. Rodyti spalvas keliems pažymėjimams. Shows the currently selected schedule Rodyti esamą grafiką Shrink city Mažinti miestą shuffle midis išmaišyti muziką Signal Signalas signal spacing Tarpai tarp signalų Sim: Simuliacijos/min: Similar view as the main window Vienodas vaizdas kaip pagrindiniame lange Size (%d MB): Dydis (%d MB): sliced underground mode Sluoksniuotas vaizdas slot empty tuščia Smart hide objects Slėpti objektus automatiškai Sort by rikiuoti krovinius pagal Sort waiting list by Rikiuoti laukiančiuosius pagal Sound Garsas Sound settings Garso nustatymai Sound volume: Garso lygis: special freight Specialus krovinys Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Greičio bonusas\nkelias %i km/h, geležinkelis %i km/h\nlaivai %i km/h, lėktuvai %i km/h. Speedlimit Greičio apribojimas Speichern Išsaugoti Spieler Žaidėjas Spieler(mz) Žaidėjai Spielerliste Žaidėjų sąrašas Spielstand wurde\ngeladen!\n \nŽaidimas užkrautas.\n Spielstand wurde\ngespeichert!\n \nŽaidimas išsaugotas.\n Sprache Kalba Sprachen Kalbos Stadtinformation Miesto statistika Start Pradėti Start the selected vehicle(s) Paleidžia transporto priemones. Starte Spiel Paleisti žaidimą Station tiles: Stotelės laukeliai: Station_msg Stotys Status Stotelės statusas steam garinis Step timeline one year Praleisti 1 metus. Stops Sustojimai Storage Talpykla Storage capacity Saugyklos talpa Strassendepot Garažas Strassentunnel Statyti geležinkelio tunelį street car lengvasis automobilis sued Pietūs suedost Pietryčiai suedwest Pietvakariai Summer snowline Vasaros sniego juosta: Supplied: %.0f %% Tiekta: %.0f %% Suppliers Tiekėjai Tage alt dienų senumo. There are still vehicles\nstored in this depot!\n Šitame depe\nyra automobilių!\n This Month Šį mėnesį This Year Šiais metais: Tile not empty. Apvalykite laukelį\nprieš kasdami šlaitą. timeline su laiko tėkme tl_title Vaizduoti visus miestus To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Pritraukti turistams,\n%s pastatė %s\nsu pagalba n%i mokeščių mokėtojų. To heavy traffic\nresults in traffic jam.\n Didelis eismas\nsukėlė kamštį.\n Toggle day/night view Dienos/ nakties rėžimas Toggle vehicle tooltips Keisti transporto pranešimus tonnen t Total inhabitants: Gyventojai: Tourist attractions Turistų traukos centrai: Tourists Lankytinos vietos Town_msg Nauji tikslai Town: %s\n Miestas %s.\n Towns Miestai track bėgiai Tracks Bėgiai Traffic Eismas Train Traukinys Trains are not available yet! Traukinių dar negalima statyti. Tram Tramvajus tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. tramvajus %i km/h, vienbėgis %i km/h\nmaglev %i km/h, siaurukas %i km/h tram_track Tramvajaus bėgiai Tramdepot Tramvajus depas Trams are not available yet! Tramvajų dar negalima gaminti. Transferring game ... Perkeliamas žaidimas ... Transformer only next to factory! Transformatoriai privalo būti\nant laisvo žęmės ploto\nprie gamyklos! Translation Vertimas transparent station coverage permatomas stoties vaizdas Transported Nukeliavo TrolleyBus_tab troleibusai Truck Sunkvežimiai Tunnel muss an\neinfachem\nHang beginnen!\n Tuneliai\nprasideda ant\ntiesaus šlaito!\n Tunnel must start on single way! Tuneliai privalo prasidėti viename kelyje Tunnelboden Tunelis underground mode požeminis vaizdas UNDO failed! Undo nebeįmanomas.\n Undo veikia tik \n kai nėra ant kelio\n signalų, stotelių\n ir kitų daiktų. Undo last ways construction Atšaukti paskutinę konstrukciją Unemployed Bedarbiai Unhappy Nepatenkinti units/day /mėnesį, Update Line Atnaujinti liniją upgrade HQ Patobulinti būstinę Usage: %.0f %% Gener. naudojimas: %.0f %% Usage/Output Sunaudojimas/Išeiga Use beginner mode Naujoko rėžimas Use timeline start year Laikas teka, nuo: Vehicle %s can't find a route! Automobilis %s\nneranda kelio! Vehicle %s is stucked! Automobilis %s užstrigo! Vehicle details Transporto priemonės info Verbrauch Vartojimas Vergroessere die Karte\n Padidinti žemėlapį \n verkaufen Pardavimas Verkehrsteilnehmer miesto mašinos Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Tu turi %d mėnesių\nišmokėti skolą.\n via per (info) via %s\n per %s\n via Menge per (kiekis) voranstellen Perkelti priekin Waggon_tab Vagonai waiting laukia Waiting for clearance! Laukia laisvo kelio! Walked Išėjo Warnings_msg Eismas Wasser Vanduo Water Kanalas Water level Vandens lygis: water vehicle laivas way %s cannot longer used:\n Kelio %s daugiau nebegalima naudoti.\n way %s cannot longer used:\n%s\n Kelio rūšis %s negali būti naudijama:\n way %s now available:\n Nauja kelio rūšis : %s .\n Ways not connected Keliai nesujungti Wegpunkt Gairė Wert Vertė west Vakarai Winter snowline Sniego aukštis žiemą withdraw atstatydinti Withdraw All Atstatydinti visus WRONGSAVE \nIšsaugotas žaidimas ne tai versijai.\nNeįmanoma įkrauti failo.\n Year %i has started. Metai %i prasidėjo. Years Metai Your primary color: Pagrindinė spalva: Your secondary color: Papildoma spalva: Zielort tikslas zooming in pritraukiama zooming out atitraukiama Zu nah am Kartenrand Negalite statyti\nper arti \nžemėlapio krašto. #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Magnetinis traukinys pasiekė: %.1f km/h.\n Traukinys pasiekės rekordą buvo pavadintas %s. New world record for monorails: %.1f km/h by %s. Naujas vienbėgio pasaulio\n rekordas: %.1f km/h\npasiektas %s. New world record for motorcars: %.1f km/h by %s. Naujas pasaulio motoristų\nrekordas:\n %.1f km/h\n kurį pademonstravo %s. New world record for narrowgauges: %.1f km/h by %s. Naujas siauruko pasaulio rekordas:/n %.1f km/h pasiektas %s. New world record for planes: %.1f km/h by %s. Naujas lėktuvo pasaulio \nrekordas:\n %.1f km/h\npasiektas %s. New world record for railways: %.1f km/h by %s. Naujas traukinio pasaulio \nrekordas:\n %.1f km/h \npasiektas %s. New world record for ship: %.1f km/h by %s. Naujas laivų pasaulio rekordas:\n %.1f km/h\npasiektas %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL inė &1_CITY_SYLL as &2_CITY_SYLL miestis &3_CITY_SYLL vilis &4_CITY_SYLL iai &5_CITY_SYLL iūnai &6_CITY_SYLL ys &7_CITY_SYLL arė &8_CITY_SYLL onas &9_CITY_SYLL uojis &A_CITY_SYLL akiai &B_CITY_SYLL iuolė &C_CITY_SYLL ūkis &D_CITY_SYLL ėnai &E_CITY_SYLL ius %0_CITY_SYLL Puš %1_CITY_SYLL Appingas %2_CITY_SYLL Medž %3_CITY_SYLL Kaun %4_CITY_SYLL Kalabybišk %5_CITY_SYLL Mėsmal %6_CITY_SYLL Montvil %7_CITY_SYLL Narkon %8_CITY_SYLL Mork %9_CITY_SYLL Bil %A_CITY_SYLL Sen %B_CITY_SYLL Kaln %C_CITY_SYLL Panevėž %D_CITY_SYLL Mek %E_CITY_SYLL Tilž %F_CITY_SYLL Ties 0center %s Centras %s 0extern %s Priemiesčio %s 1center %s %s 1extern %s atšakos %s 1suburb %s %s %s 2center %s centrin? %s 2extern %s Išorin? %s 2suburb %s Greenfieldas %s %s 3center %s pagrindinis %s 3extern %s žem? %s 3suburb %s kaimas %s %s 4center %s vidin? %s 4extern %s išorin? %s 4suburb %s kaimo % 5center %s perk?la %s 5extern %s krova %s 5suburb % miestelio % 6center %s sankirta %s 6extern %s pamišk? %s 6suburb %s pakampių %s 7center %s miestas %s 7extern %s lygumų %s 7suburb %s parkas %s 8center %s transporto mazgo %s 8extern %s %s pamiškės %s 8suburb %s ateities %s 9center %s ašies %s 9extern %s apylankos %s 9suburb %s sodų %s simutrans-124.5/simutrans/text/nl.tab000066400000000000000000001244131520675460500177030ustar00rootroot00000000000000§Nederlands PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: nl Nederlands # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable uit cl_btn_filter_enable aan cl_btn_filter_settings Instellingen cl_btn_sort_asc oplopend cl_btn_sort_desc aflopend cl_btn_sort_id id-nummer cl_btn_sort_income Inkomsten cl_btn_sort_name Naam cl_btn_sort_type Type clf_btn_alle alles clf_btn_invers wissel clf_btn_keine geen Continue Game Speel verder gl_btn_sort_bonus naar bonus gl_btn_sort_name op naam gl_btn_sort_revenue op inkomsten gl_btn_unsort niet gesorteerd hl_btn_filter_disable uit hl_btn_filter_enable aan hl_btn_filter_settings Instellingen hl_btn_sort_asc oplopend hl_btn_sort_desc aflopend hl_btn_sort_name Naam hl_btn_sort_type Soort hl_btn_sort_waiting Wachtend hlf_btn_alle alles hlf_btn_invers wissel hlf_btn_keine geen Maximize height levels Meer hoogteverschillen Networks Transportnetwerk Queueing Wachtend Road toll Tol Scenario Scenario info Transfers Verwerkt #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic poolklimaat desert Woestijn mediterran mediterraans rocky Rotsachtig temperate Gematigd tropic Tropisch tundra Toendra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Scenario kan niet geladen worden! Can't buy obsolete vehicles! Verouderde voertuigen kunnen niet gekocht worden! Cannot alter water Je kan de waterstand van dit waterbekken niet aanpassen. Cannot built depot here! Hier kan geen depot gebouwd worden! Cannot built this station/building\nin underground mode here. Dit gebouw kan niet\nondergronds gebouwd worden.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. Kies eerst een voertuig voor\nde nieuwe lijn! Cannot create socket Kan netwerk niet initialiseren! Convoi handles exhausted! Maximum aantal lijnen bereikt! Convoy already deleted! Convooi reeds verwijderd Das Feld gehoert\neinem anderen Spieler\n Deze grond \nis van een \nandere Speler!\n\n Der Besitzer erlaubt das Entfernen nicht De eigenaar laat\nverwijderen\nniet toe\n Diese Zusammenstellung kann nicht fahren!\n Deze voertuigsamenstelling \nkan niet rijden! Flugzeughalt muss auf\nRunway liegen!\n Vliegtuigen kunnen alleen\nop een luchthaven landen. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Dit luchthavengebouw \nkan hier niet gebouwd \nworden! Hier kann kein\nSignal aufge-\nstellt werden!\n Hier kan geen\nsein staan!\n In order to lock the game, you have to protect the public player by password! Om het spel te blokkeren moet de overheid beschermd worden met een wachtwoord Lost connection\nto server! Verbinding met server\nverbroken! Lost synchronisation\nwith server. Verbinding verbroken\nLaad het spel opnieuw!\n Maglevhalt muss auf\nMaglevschiene liegen!\n Maglev-halte moet op magneetbaan liggen. Monorailhalt muss auf\nMonorail liegen!\n Een monorail station moet \nop een monorailbaan\ngeplaatst worden! Monorails are not available yet! Monorails zijn nog niet beschikbaar! Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Smalspoorhalte moet op smalspoor liggen No suitable way on the ground! Haltes kunnen alleen op rechte\nvlakke wegen gebouwd worden No through station here! Hier is geen (doorrij) station mogelijk! Not enough clearance. Tussen twee wegen moeten minstens 2 hoogtes zijn. Not enough money! Onvoldoende saldo! On narrowgauge track only!\n Alleen op smalspoor!\n Only public player can lock games! Alleen de overheid kan spellen blokkeren Out of funds Je hebt daar niet genoeg geld voor! Post muss neben\nHaltestelle\nliegen!\n Een postkantoor of ander \ngebouw moet geplaatst worden\nop een vrij veld naast een \nstopplaats/station! Protocoll error (expecting game) Protocolfout Schiffhalt muss im\nWasser liegen!\n Een scheepsstop\nmoet op het water\nworden geplaatst\n Server busy Server bezet. Terraforming not possible\nhere in underground view Actie niet mogelijk ondergronds This tunnel branches. You can try Control+Click to remove. Deze tunnel heeft meer dan 2 ingangen. Je kan deze tunnel verwijderen met ctrl+klik. Upgrade must have\na higher level Upgraden alleen naar hoger niveau Vehicle %s cannot choose because stop too short! Voertuig %s heeft een te kort station in zijn dienstregeling! Zughalt muss auf\nSchiene liegen!\n Een treinstop\nmoet op rails\ngeplaatst worden\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Hulp

Hulp Index

Over Simutrans

%1$s

Hoe te bedienen

%2$s

Hoe een spel te starten

%4$s

Hoe te spelen

%5$s

Gereedschappen

%3$s

Andere vensters

%6$s Decrease water height Waterstand 1 verlagen Highlight railroad tracks Benadruk spoorwegen Highlite depots Benadruk depots Highlite electrical transmission lines Benadruk elektriciteitsnet Highlite factories Benadruk fabrieken Highlite forests Benadruk bossen Highlite tourist attraction Benadruk toeristenattracties Increase water height Waterstand 1 verhogen Open station/stop details Toon stationdetails Overlay city limits Benadruk stadsgrenzen Overlay passenger destinations when a town window is open Benadruk passagiers- en post-bestemmingen van de stad als deze in een open venster staat Overlay schedules/network Laat alle verbindingen zien op de kaart Overlay town names Toon stadsnamen Please click on the map to add\nwaypoints or stops to this\nschedule. Klik op de kaart om haltes en routepunten toe te voegen Set tile climate %s Verander het klimaat van deze tegel naar %s Show capacity and if halt is overcrowded Toon capaciteit en status van halte Show how many convoi reach a station Toon hoeveel voertuigen aangekomen zijn Show how many people/much is waiting at halts Toon aantal wachtende mensen en goederen Show initial passenger departure Toon de oorsprong van de passagiers en post. Show level of city buildings Toon niveaus van stadsgebouwen Show mail service coverage/mail network Toon het bereik van de post Show passenger coverage/passenger network Toon bereik van het passagiersnetwerk Show speedlimit of ways Toont de maximale snelheid van wegen Show the change of waiting at halts Toon de geschiedenis van de wachttijd Show the owenership of infrastructure Toon de eigenaar van de infrastructuur Show transported freight/freight network Toon goederentransport en verbindingen Show usage of network Toon de drukte van de wegen Shows a listing with all industries on the map. Geeft een lijst van alle fabrieken Sum of departure/arrivals at halts Toon het aantal aangekomen en vertrokken voertuigen #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n Toeristen kunnen nu\nde buslijn van %s\ntussen %s en\nbezienswaardigheid %s\nbij (%i,%i) gebruiken. %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n Arbeiders kunnen nu\nde buslijn van %s\ntussen %s en\nfabriek %s\nbij (%i,%i) gebruiken. %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s heeft nu %i vrachtwagens,\ndie tussen %s (%i,%i)\nen %s (%i,%i)\nrijden. %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s voltooit\nnieuwe spoorwegen tussen\n%s (%i,%i) en\n%s (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n %s heeft\neen Luchtverbinding tussen\n%s en\n%s geopend. Ferry service by\n%s\nnow between\n%s \nand %s.\n %s\nopening van een nieuwe bootlijn\ntussen\n%s en \n%s.\n\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\nopent een nieuwe\nbusverbinding tussen\n%s en\n%s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Kaarteneditor LISTTOOLS Lijstenbeheer MAGLEVTOOLS Maglev MONORAILTOOLS Monorail/maglev werktuigen NARROWGAUGETOOLS Smalspoor RAILTOOLS Spoorwegen ROADTOOLS Autowegen SHIPTOOLS Scheepvaart SLOPETOOLS Land ophogen/verlagen SPECIALTOOLS Bijzondere werktuigen TRAMTOOLS Trams #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s's heeft een nieuw hoofdkantoor gebouwd. Factory chain extended\nfor %s near\n%s built with\n%i factories. Economie gaat goed\n voor %s bij %s zijn\n%i nieuwe fabrieken gebouwd. Net wealth less than 10%% of starting capital! Het nettovermogen bedraagt minder dan 10%% van het startkapitaal! New %s now available:\n%s\n %s '%s'\nis nu beschikbaar. New factory chain\nfor %s near\n%s built with\n%i factories. Er is een nieuwe productieketen\nvoor %s bij\n%s gebouwd,\nbestaande uit %i fabrieken. New vehicle now available:\n%s\n Nieuw voertuig is \nnu beschikbaar:\n\n» %s «\n Now %u clients connected. %i spelers verbonden. Remove vehicle from map. Use with care! Verwijder het voertuig direct. Screenshot\ngespeichert.\n Schermafdruk\nopgeslagen\n Sends the convoi to the last depot it departed from! Stuur het voertuig naar een nabijgelegen depot. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s heeft\neen nieuw monument\nopgericht. Dit wordt\ngevierd met festiviteiten.\n #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter lijn filter #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (in remise) \nBauzeit bis tot \nBauzeit von \ngebouwd vanaf \nCan't open heightfield file.\n \nKan hoogteveld niet openen.\n \ndirection: \nrichtingen: \nelektrified geëlektrificeerd \nHeightfield has wrong image type.\n \nHoogteveld heeft een onjuiste\nbestands opmaak.\n \nis reserved by: \nin gebruik door trein: \nminimum speed: \nminimumsnelheid: \nnot elektrified niet geëlektrificeerd \nRibi (masked) \nRichtingen (gemaskeerd) \nRibi (unmasked) \n\nrichtingen (alle): \nSet phases: \npas stoplicht aan NZ/OW: \nsingle way \neenrichtingsweg \nway1 reserved by Weg 1 gebruikt door\n \nway2 reserved by Weg 2 gebruikt door\n \nwith sign/signal\n \nmet bord/sein\n %d buildings\n %d Gebouwen\n %d convois %d voertuigen %d Einzelfahrzeuge im Depot %d voertuigen in het depot %i years %i months old. %i jaar en %i maanden oud. %s at (%i,%i) now public stop. %s is een publieke halte geworden. %s building %s %s %s %s %s %s city %d %s %s centrum %d %s %s factory %s %s %s %s fabriek %s has entered a depot. %s heeft een depot bereikt. %s land %d %s %s %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s heeft \neen nieuw stadhuis \ngebouwd toen de stad\n%i inwoners bereikte. %s\nis crowded. %s is overbelast. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nmaximale snelheid %3$i km/u\n\n\nhuidige snelheid %2$i km/u.\n\n %s\nwas liquidated. %s is bankroet gegaan\n. Het bedrijf is stilgelegd en alle bezittingen zijn verkocht. %u Client(s)\n %i spelers\n %u Player (%u locked)\n %u spelers (%u beveiligd)\n 1 convoi 1 voertuigsamenstelling 1 Einzelfahrzeug im Depot 1 voertuig in dit depot 1LIGHT_CHOOSE Helderheid: 1WORLD_CHOOSE Instellingen voor een nieuw spel: 2LIGHT_CHOOSE Contrast: 2WORLD_CHOOSE Kaartnummer: 3LIGHT_CHOOSE Scrollsnelheid: 4LIGHT_CHOOSE Scroll omdraaien 5LIGHT_CHOOSE voetgangers (halte) 5WORLD_CHOOSE Steden: 6LIGHT_CHOOSE voetgangers (stad) 6WORLD_CHOOSE Stadsverkeer: 8WORLD_CHOOSE Dag en Nacht Modus A bridge must start on a way! Een brug \nmoet op een weg beginnen! Abfrage Info Abnehmer Afnemer About Info About scenario Copyright Abriss Verwijder Absenken Verlagen Abspanntransformator Transformator Accelerate time Tijd versnellen Act. load: %u MW\n Verbruik: %u MW\n Active player only alleen actieve speler Add forest Bos aanplanten Add random citycar voeg willekeurige stadsauto toe add server Voeg server toe Add Stop Toevoegen Add stops for backward travel Toevoegen van de stops voor de terugweg air start/taxibaan aircraft_tab Vrachtvliegtuigen airplane vliegtuig Airport Luchthaven AIRTOOLS Luchtvaart \n All Alle all convoi tooltips alle tooltips voor voertuigen Allow city growth Stadsuitbreiding toestaan Allow player change Speleromschakeling toelaten allowed climates:\n Klimaatzones Alters a schedule. Dienstregeling wijzigen Angenommene Waren Vlakbij benodigde goederen anhaengen Toevoegen Anhaenger_tab Opleggers Anheben Ophogen Appends stops at the end of the schedule Stop toevoegen aan einde dienstregeling Apply Line Lijn toewijzen Arbeiter aus: Arbeiders uit: Arrivals from\n Aankomst van Arrived Aangekomen Assets Overige activa Aufloesen Opheffen Aufspanntransformator Transformator August Augustus Autohalt muss auf\nStrasse liegen!\n Een halte voor bussen of \nvrachtwagens moet op een \nweg geplaatst worden! Available Beschikbaar Bahndepot Depot Bankrott:\n\nDu bist bankrott.\n Bankroet:\n\nU bent falliet.\n battery batterij Baum Boom baum builder Plant bomen Baustelle Bouwplaats Bauzeit Bouwtijd Beenden Afsluiten Besonderes Gebaeude Bezienswaardigheid BF station bio biologisch Blockstrecke ist\nbelegt\n Andere trein op\ndit railblok\n Boden Land Bonusspeed: %i km/h Max. haalbare snelheid: %i km/h Boost (%%) verhoging met (%%) bridge is too high for its type! De brug is te hoog voor dit type! Bridge is too long for this type!\n Dit brugtype kan \nde overspanning niet halen!\n Bruecke Brug Bruecke muss an\neinfachem\nHang beginnen!\n Een brug moet op\neen rechte\nhelling beginnen!\n Brueckenboden brugdek Build air depot Bouw hangar build choosesignals Plaats keuzeseinen Build city market Bouw een nieuwe winkel in de dichtstbijzijnde stad Build drain Transformator build HQ Bouw Hoofdkantoor Build land consumer Bouw een nieuwe energiecentrale. Build maglev depot Bouw een Maglev depot Build monorail depot Bouw een monorail depot Build narrowgauge depot Bouw een smalspoor depot Build powerline Hoogspanningslijn Build presignals Plaats voorseinen Build road depot Bouw een garage Build ship depot Bouw een scheepswerf Build signals Plaats seinen Build train depot Bouw een trein depot Build tram depot Bouw een tram depot Build truck depot Bouw een garage Building costs estimates Geschatte bouwkosten Buildings gebouwen Built artifical slopes Kunstmatige glooiingen Built random attraction Nieuwe bezienswaardigheid Bus_tab Bussen Can only move from halt to halt or waypoint to waypoint. Kan alleen verplaatst worden \nvan halte naar halte of \nvan routepunt naar routepunt. Cancel Annuleren Cannot connect to offline server! Server is offline en niet bereikbaar. Capacity: Capaciteit Capacity: %.0f MW Capaciteit: %.0f MW\n Capacity: %d%s %s\n Capaciteit: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Capaciteit: %s\n» %d (%d%%) Cars are not available yet! Voertuigen zijn nog niet beschikbaar! cars.\nstate Voertuigen\n Cash Kas Change player Van speler wisselen Chart Grafiek Chat_msg Chat Choose direction Richting kiezen Choose operation executed on clicking stored/new vehicles Kies een actie voor de opgeslagen/nieuwe voertuigen. chooses a random map Kiest een willekeurige kaart citicens Inwoners City attraction Toeristische attractie City industries Aantal industrieën (stad) City list Stedenlijst City size Aantal inwoners city_road stadsweg citybuilding builder Stadsgebouwen bouwen CityLimit Gemeentegrens cl_title Voertuigen lijst cl_txt_sort Sorteren op: Clear block reservation Blokreservering tonen/wissen clf_chk_aircrafts Vliegtuigen clf_chk_cars Wegverkeer clf_chk_indepot in een depot clf_chk_maglev Maglev banen clf_chk_monorail Monorail banen clf_chk_name_filter Filter namen: clf_chk_narrowgauge Smalspoorbanen clf_chk_noincome geen inkomen clf_chk_noline geen lijn clf_chk_noroute geen route clf_chk_noschedule geen dienstregeling clf_chk_obsolete Verouderd clf_chk_ships Schepen clf_chk_spezial_filter Speciaal filter: clf_chk_stucked geblokkeerd clf_chk_trains Treinen clf_chk_trams Trams clf_chk_type_filter Filter type: clf_chk_waren Filter goederen: clf_title Voertuigen Filter Climate Control Klimaatinstellingen closed gesloten COLOR_CHOOSE\n Kies een kleur\nuit de tabel:\n Company bankrupt Onderneming is failliet! Company_msg Concurrenten Comparing pak files ... Bezig met het vergelijken van pak-bestanden ... Configure AI AI-instellingen Configure AI setttings AI (computer tegenstander) instellingen aanpassen Congratulation\nScenario was complete in\n%i months %i years. Gefeliciteerd!\nHet scenario is gerealiseerd\nin \n%i maanden en %i jaren! Connected stops Aangesloten haltes Connected with server Verbonden met server Constructed by Gemaakt door Constructed by %s Gemaakt door %s construction speed Bouwsnelheid Construction_Btn Bouwkosten Consumed verbruikt convoi %d of %d Voertuig %d van %d convoi error tooltips tooltips voor voertuig fouten Convoi has been sent\nto the nearest depot\nof appropriate type.\n Dit voertuig is naar een\nnabijgelegen depot gestuurd.\n Convoi is sold when all wagons are empty. Het voertuig of de samenstelling wordt verkocht zodra het helemaal leeg is. convoi mouseover tooltips convooi mouseover tooltips convoi passed last\nmonth %i\n Verkeer in de \nvoorafgegane maand: %i\n Convois Voertuigen Convois: %d\nProfit: %s Voertuigen: %d\nWinst: %s Convoys Voertuigen Copy Convoi Kopiëren Copy the selected convoi and its schedule or line Kopieer dit voertuig inclusief dienstregeling of lijn cost for removal Kosten voor verwijdering Cost: %8s (%.2f$/km %.2f$/m)\n Kosten: %s (%.2f$/km, %.2f$/m)\n Cost: %8s (%.2f$/km)\n Kosten: %8s (%.2f$/km)\n Costs Kosten Create a new line based on this schedule Maak van deze dienstregeling een nieuwe lijn curiosity builder Attractie bouwen curlist_title Bezienswaardigheden CURRENCY € Currently playing: Momenteel spelend: Customers live in: Klanten wonen in: deactivated in online mode Niet actief in online modus Deccelerate time Tijd vertragen decrease underground view level lager ondergronds niveau Del Stop Verwijderen Delete Line Lijn opheffen Delete the current stop Verwijder halte Delete the selected line (if without associated convois). Verwijder de geselecteerde lijn (mits zonder konvooien) Delete this file. Dit bestand verwijderen Delivered Geleverd Demand Vraag: Demand: %.0f MW Vraag: %.0f MW\n Denkmal Monument Departed Vertrokken Departure board Vertrektijden Departures to\n Gaat naar\n Der Tunnel ist nicht frei!\n De tunnel is niet vrij!\n Destination Bestemming Destroying map ... Oude kaart wordt ontruimd ... Die Bruecke ist nicht frei!\n De brug is niet leeg!\n directmail Direct Mail Direkt erreichbare Haltestellen Direct bereikbare stopplaatsen disable midi Midi-muziek uitschakelen Display settings Beeldscherm instellingen Distance Afstand Dock haven Dock must be built on single slope! Een haven moet op een enkelvoudige helling gebouwd worden! Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Je hebt %d maanden om je schulden af te lossen! Durchsatz Capaciteit Economy Economie en steden Eigenbesitz\n Openbaar bezit\n Ein %s\npasst hier nicht.\n Hier past geen\n%s.\n Einstellungen aendern Instellingen aanpassen electric elektrisch Electricity Stroom Electricity producer\n\n Energiecentrale \n\n Electrics_tab Elektrisch Electrify track Bovenleidingen enlarge map Vergroot kaartafmetingen enter a value between %i and %i Geef een waarde tussen %i en %i Enter address Serveradres Enter Password Naam en wachtwoord van speler wijzigen Error Fout Erzeuge neue Karte.\n Een ogenblik geduld,\neen nieuwe kaart\nwordt gemaakt ...\n\n(Bij een grote\nkaart kan dit even\nduren)\n Es wird bereits\nein Fahrplan\neingegeben\n Er wordt al\neen dienstregeling\ngemaakt.\nMaak deze eerst af!\n Fabrikanschluss Aangesloten industrie Fabrikname Fabrieksnaam Factories Fabrieken factory details Fabrieks details factorybuilder Fabrieken bouwen Fahrplan Dienstregeling Fahrtziel Bestemming: Fahrzeuge koennen so nicht entfernt werden Voertuigen kunnen\nzo niet verwijderd\nworden\n Fahrzeuge: Voertuigen: Farbe Kleur Fast forward Snel voorwaarts in tijd February Februari Ferry_tab Veerboten Fertig Klaar Filename Bestand: Files from: Bestanden uit: Finances of %s Financiën van %s Finanzen Financiën find mismatch Vergelijk je pak fl_title Lijst met fabrieken Flug_tab Passagier-vliegtuigen follow me volgen Follow the convoi on the map. Volg het voertuig op de kaart. Forest Bos Found new city Nieuwe Stad Fracht Vracht Frame time: Beeldfrequentie: Free Capacity Vrije capaciteit freeplay mode Freeplay, nooit bankroet Friction: Frictie: fuel_cell brandstofcel Full load Laden t/m Fundament Fundering Fussgaenger Voetgangers Game info Online multiplayer GAME PAUSED SPEL GEPAUZEERD Game_msg Algemeen Gear: Overbrenging: Gebaeude Gebouw General Algemeen Generated Gegenereerd Generation: %.0f MW Generatie: %.0f MW\n Gewinn Inkomsten Give the selected vehicle(s) an individual schedule Ken het geselecteerde voertuig een eigen dienstregeling toe gl_btn_sort_catg op categorie gl_title Goederenlijst go home naar depot Goods Goederen Goods AI Vracht-AI Goods list Goederenlijst Gross Profit Cash flow Groundobj Object Grow city Stad vergroten Growth Groei H bushalte Happy Tevreden Haus kaufen koop huis Helligk. Scherm Help Hulp Help text not found Hulp tekst niet gevonden. hide all building verberg alle gebouwen hide city building Verberg stadsgebouwen hide objects under cursor Verberg objecten onder de cursor. hide station names Verberg stationsnamen hide transparent Verberg objecten transparant hide trees Verberg bomen Hier warten/lagern: Wachtende passagiers/goederen: Higher transport fees, crossconnect all factories Voor meer winsten op transport, deactiveer Just-in-time. Highlite schedule Benadruk haltes dienstregeling hl_title Stations lijst hl_txt_filter Filter: hl_txt_sort Gesorteerd op: hlf_chk_airport Luchthaven hlf_chk_anleger Haven hlf_chk_bahnhof Station hlf_chk_bushalt Bushalte hlf_chk_frachthof Laadperron hlf_chk_keine_verb Geen verbinding hlf_chk_maglevstop Maglev station hlf_chk_monorailstop monorail halte hlf_chk_name_filter Filter namen: hlf_chk_narrowgaugestop Smalspoorstation hlf_chk_overflow overbeladen hlf_chk_spezial_filter Speciaal filter: hlf_chk_tramstop Tramhalte hlf_chk_type_filter Filter soort: hlf_chk_waren_abgabe Capaciteit: hlf_chk_waren_annahme Verbruik: hlf_title Station Filter Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depot niet gevonden!\nKies zelf een depot \n(met de dienstregeling). Homeless Dakloos hydrogene Waterstof Idle: Werkloos: ignore climates Negeer klimaatzones In the industry legend show only currently existing factories Toon enkel de gebruikte fabrieken In Transit Onderweg Increase Industry density Verhoog industriedichtheid increase underground view level hoger ondergronds niveau industrial building Fabrieksgebouw Init map ... Kaart inrichten... Input input Ins Stop Invoegen Insert stop before the current stop Stop invoegen voor de huidige Intercity road len: Lengte provinciale wegen Intro. date: Geïntroduceerd: invalid ongeldig Invalid coordinate Ongeldige coördinaat isometric map isometrische kaart January Januari join game Online spelen July Juli Jump to Ga naar June Juni Kann Spielstand\nnicht laden.\n Kan opgeslagen spelstand\nniet vinden/laden\n Kann Spielstand\nnicht speichern.\n Kan spelstand\nniet opslaan\n Kein Besitzer\n Geen eigenaar keine geen Keine Einzelfahrzeuge im Depot Het depot is leeg Keyboard_Help\n Toetsenbord help\n koord coördinaten Kreuzung Kruising labellist_title Label lijst Lade Relief Laad Hoogtekaart Land attraction Bezienswaardigheid Land industries Aantal industrieën (land) LANG_CHOOSE\n Maak de keuze uit\nde volgende talen:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Vorige maand: Last used tools Laatst gebruikte werktuigen Last Year Afgelopen Jaar Leaving depot! Verlaat depot! leer leeg Legend Legenda Leistung Vermogen Leistung: %d kW Vermogen: %d kW Leitung Hoogspanningsleiding letzen Monat: diesen Monat: Afgelopen maand: Deze maand: Line Lijn Line Management Lijnenbeheer Lineless convoys serving this stop Niet aan lijn gebonden voertuigen die hier stoppen: Lines serving this stop Aangesloten lijnen LKW_tab Vrachtverkeer Load game Spel laden load height data from file Bestand met reliëfinformatie laden Load scenario Laad scenario loaded geladen. loaded passenger/freight Sorteer passagiers/vracht op Loading (%i->%i%%)! Laden van (%i->%i%%) Loading addon paks ... Addon paks laden ... Loading map ... Bezig met het laden van de kaart... Loading paks ... Paks laden ... Loading skins ... Skins laden ... Lock game Blokkeer speleromschakeling. Lokomotive_tab Locomotieven m3 m³ Maglev Magneetbaan maglev vehicle Magneetbaanvoertuig maglev_track Maglev baan Maglevdepot Maglev depot Mail Demand %d\n Vraag naar post %d\n Mailbox Berichten Mailbox Options Berichtencentrum en opties Maintenance Onderhoud make stop public (or join with public stop next) costs %i per tile and level maak halte openbaar of verbind met openbare halte (%i$ per veld en niveau) Manual (Human) Mens Manufactured: Geproduceerd: Map roughness Ruigheid: map zoom Inzoomen March Maart Margin (%%) Marge (%%) Marker InfoBordje max max. Max Boost (%%) Maximale verhoging Max income: Max. inkomsten: Max. speed: Max. snelheid: Maximum 254 stops\nin a schedule!\n Niet meer dan 254 haltes\nin één dienstregeling!\n maximum length of rivers Maximale rivierlengte Maximum tile height difference reached. Het maximale hoogteverschil\ntussen twee grondvlakken \nis bereikt. Maxspeed Maximum snelheid May Mei Median Citizen per town Gem. aantal inwoners Meldung Melding Menge Hoeveelheid MessageOptionsText \nNieuw Jaar\n\nAI Nieuws\n\nStadnieuws\n\nGeen Route\n\nNieuwe industrie\n\nChat\n\nNieuwe Voertuigen\n\nStation overbeladen\n\nProblemen\n\nFiles\n\nScenario min min. minimum length of rivers Minimum rivierlengte Missing pakfiles Er ontbreken objecten! Modify the selected line Gekozen lijn veranderen Monate alt maanden oud Monorail monorail monorail vehicle Monorail voertuig monorail_track Monorail baan Monorailboden monorailfundering Monoraildepot monoraildepot month wait time wachttijd in maanden Months Maanden Monuments Monumenten Mountain height Berghoogte Movingobj Verschuiven Music playing disabled/not available Muziek uitgeschakeld/niet beschikbaar Music volume: Muziek volume: mute sound geluid uitschakelen Name Naam Narrowgauge Smalspoor Narrowgauge are not available yet! Smalspoor nog niet beschikbaar! narrowgauge vehicle Smalspoor voertuig narrowgauge_track Smalspoor Narrowgaugedepot Smalspoor depot Net ID: %p Net ID: %p\n Net Wealth Totale activa Net wealth near zero Er dreigt een negatief vermogen Neue Karte Nieuwe kaart Neue Welt Nieuwe wereld new convoi Nieuw voertuig New Line Nieuwe lijn New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Nieuwe lijn gecreëerd!\nJe kunt de lijn nu toewijzen\ndoor deze te kiezen uit\nde lijnen hierboven. New Vehicles Nieuwe voertuigen no buildings hidden geen verborgen gebouwen no convois geen voertuigsamenstellingen No goods are loaded onto this convoi. Het voertuig accepteert geen nieuwe lading. no goods waiting geen wachtende goederen no load Alleen lossen No Route Geen route No stop here! Dit kan alleen voor haltes! No suitable ground! Geen geschikt terrein! No terminal station here! Hier is geen eindstation mogelijk! no timeline Geen tijdlijn no tree zonder bomen No. of Factories Fabrieken en winkels Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Voertuig heeft\n een dienstregeling\nnodig voordat het\nkan vertrekken!\n none Geen nord Noord nordost Noord-Oost nordwest Noord-West Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Niet mogelijk!\nDe dienstregeling kan \nnu niet veranderd \nworden.\nProbeer later. Not enough fields would remain. Onvoldoende vrije velden. Now active as %s.\n Je bent nu actief \nals speler %s. Number of rivers Aantal rivieren Odometer: %s km Kilometerstand: %s Ok OK On loan since %i month(s) U heeft schulden sinds %i maand(en) On this map, you are not\nallowed to change player!\n Spelerswissel is niet mogelijk in dit spel.\nSpelerswissel is uitgeschakeld!\n Only city chains Alleen stadsfabrieken Only first %d differing paks reported. There are probably more. Enkel de eerste paks die verschillen zijn aangegeven. Waarschijnlijk zijn er nog meer. Only land chains Alleen plattelandsfabrieken Only one transformer per factory! Slecht één transformator per fabriek Only show goods which are currently handled by factories Toon enkel de goederen die momenteel verwerkt kunnen worden Operation Exploitatiekosten Ops Profit Resultaat Optionen Instellingen Or enter a server manually: Of voer manueel het serveradres in: Origin Herkomst ost Oost Output Productie Ownership Eigenaar Pak which may cause severe errors: De volgende ontbrekende objecten kunnen problemen veroorzaken in transportnetwerken: Pak which may cause visual errors: De volgende objecten werden vervangen door gelijkaardige objecten: Pak(s) different: Afwijkende objecten: Pak(s) missing on client: ontbrekende objecten: Pak(s) not on server: Deze paks doen niet mee: Pakset differences Verschillen in paksets: paletten Kisten Pas_tab Passagiertreinen Passagiere Passagiers Passagierrate Passagiersaanbod Passagierziele Passagiers/postbestemmingen Passenger AI Passagiers AI Passenger Demand %d\n Vraag naar passagiers: %d\n Passengers %d %c, %d %c, %d no route Passagiers %d %c, %d %c, %d zonder route Passengers %d %s, %d %s, %d no route Passagiers %d %s, %d %s, %d zonder route Password Wachtwoord: Pause Pauze Pax <%i> Mail <%i> Passagiers <%i> Post <%i> PaxDest Bestemmingen Percent Electricity Stroomproductie (%% vraag) Planes are not available yet! Vliegtuigen zijn nog niet\nbeschikbaar! Plant tree Plant een boom player speler player -1 Mens player 0 Overheid player 1 Napik 128 AS player 10 speler 10 player 11 speler 11 player 12 speler 12 player 13 speler 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Spedition VM player 5 H-Trans b.v. player 6 PSK & Co KG player 7 speler 7 player 8 speler 8 player 9 speler 9 Please choose vehicles first\n Selecteer eerst een voertuig!\n Post post Postrate Postaanbod Power Stroom Power (MW) Vermogen (MW) Power: Vermogen: Power: %4d kW\n Vermogen: %4d kW\n Powerlines HSP-leidingen Problems_msg Waarschuwingen Produced Geproduceerd Production of %s has been stopped:\n%s\n %s '%s'\nword niet meer geproduceerd. Production/Boost Productie/boost Produktion Produktie Profit Winst promote to line Maak nieuwe lijn q1 Lente q2 Zomer q3 Herfst q4 Winter Query server Server opvragen rail car spoorwagen random willekeurig Random age willekeurig jaar Random map Willekeurige kaart Rathaus Stadhuis Rating Beoordeling ratio_pax Verhouding Relevant Alleen benodigde Reliefkarte Kaart Remove Verwijder remove airstrips landingsbaan verwijderen remove channels kanaal verwijderen remove interm. signals verwijder tussenliggende signalen remove maglev tracks verwijder maglevbaan remove monorails monorail verwijderen remove narrowgauge tracks smalspoor verwijderen remove powerlines Verwijder HSP-leidingen remove roads weg verwijderen remove tracks spoorbaan verwijderen Remove wayobj %s Verwijder wayobj %s replace other signals Vervang andere signalen replace stop vervang halte request closing verzoek sluiten residential house woonhuis Restore natural slope Herstel natuurlijke glooiing Restwert: Verkoop waarde: Retire. date: Gebouwd tot: return ticket Retourtraject Revenue Inkomsten Revision: Revisie: road weg road vehicle Wegvoertuig Roadsign verkeersbord Rotate map Kaart draaien Rotation Draaiing Routing Verbindingen sack zakken sail Wind Saving map ... Bezig met het opslaan van de kaart... Scenario complete: %i%% doel voor %i%% bereikt Scenario Debug Debug Scenario Goal Doel Scenario information Details Scenario Result Voortgang Scenario Rules Regels Schedule changing! Wijziging in dienstregeling! Schienentunnel Spoorwegtunnel bouwen Schiff_tab Schepen Schiffdepot Scheepswerf Schleppkahn_tab Sleepboot Screenshot Schermafdruk Seasons Seizoenen Sehenswuerdigkeit Bezienswaardigheid Select a server to join: Kies een server Select a theme for display Verander het thema Sell the selected vehicle(s) Verkoop de gekozen voertuigen. sended verstuurd SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 Server did not respond! Server reageert niet Serves Line: Bedient lijn: set signal spacing Set afstand tussen signalen Setting Instellingen Ship Schip shops and stores Winkels en magazijnen Show all Toon alles show all building Toon alle gebouwen Show also vehicles no longer in production. Toon ook voertuigen die niet meer vervaardigd worden. Show also vehicles that do not match for current action. Toon ook voertuigen die niet passen bij de reeds geselecteerde voertuigen. Show even servers with wrong version or pakset Toon ook servers met verkeerde programmaversie of verkeerde pakset show grid Toon raster Show industry Toon industrie Show legend Toon legenda Show map scale Toon schaal Show mismatched Toon foute combinaties Show obsolete Verouderde tonen Show offline Toon offline servers Show only used Toon enkel gebruikte Show schedules toon dienstregelingen Show servers that are offline Toon offline servers Show servers where game version or pakset does not match your client Toon servers die niet overeenkomen met jouw versie of pakset show station coverage toon stations dekking show station names toon stationsnamen show waiting bars toon waiting bars show/hide block reservations Toon/verberg blokreservering Show/hide estimated arrival times Toon/verberg de geschatte aankomsttijden show/hide object owner Toon/verberg eigenaar Show/hide statistics Toon/verberg statistieken Shows buttons on special topics. Toon interactieve kaart Shows consumer/suppliers for factories Toon afnemers/leveranciers voor fabrieken Shows the color code for several selections. Toon de kleurcode bij de selecties Shows the currently selected schedule Toon de gekozen dienstregeling Shrink city Stad verkleinen shuffle midis shufflemode voor achtergrondmuziek Signal Sein signal spacing Signaal afstand Sim: Simloops: Similar view as the main window Zelfde oriëntatie als het hoofdscherm Size (%d MB): Omvang (%d MB): sliced underground mode "sliced" ondergrondse modus slot empty vrij Smart hide objects Verberg objecten rond de cursor Sort by Gesorteerd op Sort waiting list by Sorteer wachtenden op Sound Geluid Sound settings Geluidsinstellingen Sound volume: Geluidssterkte special freight Speciale vracht Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Snelheidsbonus\nstraat %i km/h, treinen %i km/h\nschepen %i km/h, vliegtuigen %i km/h.\n Speedlimit Max. snelheid Speichern Opslaan Spieler Speler Spieler(mz) Spelers Spielerliste Lijst met spelers Spielstand wurde\ngeladen!\n \nSpelstand is\ngeladen\n Spielstand wurde\ngespeichert!\n \nSpelstand is\nopgeslagen\n Sprache Taal Sprachen Talen Stadtinformation Stadsinformatie Start Starten Start the selected vehicle(s) Start het gekozen voertuig. Starte Spiel Start Spel Station tiles: min. stationslengte: Station_msg Overbezettingen Status Haltestatus steam Stoom Step timeline one year Een jaar naar voren springen Stops Haltes Storage voorraad Storage capacity Opslagcapaciteit Strassendepot Garage Strassentunnel Tunnel bouwen street car Wagen sued Zuid suedost Zuid-Oost suedwest Zuid-West Summer snowline Sneeuwgrens(z): Supplied: %.0f %% Geleverd: %.0f %% Suppliers Leveranciers Tage alt dagen oud Theme selector Thema selectie There are still vehicles\nstored in this depot!\n Er zijn nog\nvoertuigen in\ndit depot!\n This Month Deze maand This Year Dit jaar Tile not empty. Het veld is niet leeg! timeline Tijdlijn tl_title Lijst met steden To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. %s bouwt\neen %s,\nom meer toeristen aan te trekken. To heavy traffic\nresults in traffic jam.\n Te druk verkeer veroorzaakt\nopstoppingen. Toggle day/night view dag/nacht schakelen Toggle vehicle tooltips Voertuig info omschakelen tonnen t Total inhabitants: Aantal inwoners Tourist attractions Bezienswaardigheden Tourists Toerisme Town_msg Nieuwe bestemmingen Town: %s\n Stad: %s\n Towns Stadsnamen track spoor Tracks Spoorwegen Traffic Verkeer Train Trein Trains are not available yet! Treinen zijn nog niet beschikbaar! tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. tram %i km/h, monorail %i km/h\nmagneetbaan %i km/h, smalspoor %i km/h. tram_track tramrails Tramdepot Tramremise Trams are not available yet! Trams zijn nog niet beschikbaar! Transferring game ... Spel wordt overgehaald ... Transformer only next to factory! Een transformatorstation moet direct naast de fabriek geplaatst worden! Translation Vertaling transparent station coverage transparante stations dekking Transported Vervoerd TrolleyBus_tab Trolleybussen Truck Vrachtwagen tt_Other Andere Tunnel muss an\neinfachem\nHang beginnen!\n Een tunnel moet op\neen rechte\nhelling beginnen\n Tunnel must start on single way! Een tunnel moet op het einde van een straat beginnen! Tunnelboden Tunnelbodem underground mode ondergrondse mode UNDO failed! Ongedaan maken is niet meer mogelijk.\nJe kunt alleen de bouw van een weg\nongedaan maken zo lang je geen\nstations/haltes/seinen/borden/... \nhebt geplaatst op de weg. Undo last ways construction Maak laatste wegconstructie ongedaan Unemployed Werkeloos Unhappy Ontevreden units/day /dag, Update Line Lijn veranderen upgrade HQ Hoofdkwartier uitbreiden Usage: %.0f %% Gebruik: %.0f %% Usage/Output Verbruik/productie Use beginner mode Activeer beginnermode Use timeline start year Tijdlijn/Aanvangsjaar Vehicle %s can't find a route! Voertuig %s \nkan geen route vinden! Vehicle %s is stucked! Voertuig %s\nis geblokkeerd. Vehicle details Voertuigdetails Verbrauch Verbruik Vergroessere die Karte\n Vergroot kaart Verkauf Verkopen! verkaufen Verkopen Verkehrsteilnehmer Weggebruiker Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n U heeft schulden!\n\nU heeft %d maanden\nom uw schulden af\n te lossen!\n via %s\n via %s\n via Menge Aantal-via voranstellen Invoegen Waggon_tab Wagons waiting wachtend Waiting for clearance! Wacht op voorligger Walked Te voet walking Te voet Warnings_msg Verkeersmededelingen Wasser Water Water Kanaal Water level Waterpeil: water vehicle Watervoertuig way %s cannot longer used:\n %s kan niet langer worden gebruikt:\n way %s cannot longer used:\n%s\n %s kan niet langer worden gebruikt:\n%s\n way %s now available:\n %s is nu beschikbaar.\n Way toll Tol Ways not connected Wegen niet aangesloten Wegpunkt Routepunt Weight: Gewicht: Wert Waarde west West Winter snowline Sneeuwgrens(w): withdraw Terugtrekken Withdraw All Alles terugtrekken WRONGSAVE Opgeslagen spelversie\nniet compatibel. Bestand kan\nniet geladen worden.\n Year %i has started. Het jaar %i is begonnen. Years Jaren Your primary color: Eerste kleur Your secondary color: Tweede kleur Zielort Bestemming zooming in inzoomen zooming out uitzoomen Zu nah am Kartenrand Er kan hier niet\ngebouwd worden!\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nieuw wereldrecord voor\nmaglevs: %.1f km/h\nby %s. New world record for monorails: %.1f km/h by %s. Nieuw wereldrecord voor\nmonorails: %.1f km/h\nby %s. New world record for motorcars: %.1f km/h by %s. Nieuw wereldrecord voor\nmotorvoertuigen:\n %.1f km/h\nby %s. New world record for narrowgauges: %.1f km/h by %s. Nieuw wereldrecord voor\nsmalspoor: %.1f km/h\nby %s. New world record for planes: %.1f km/h by %s. Nieuw wereldrecord voor\nvliegtuigen: %.1f km/h\nby %s. New world record for railways: %.1f km/h by %s. Nieuw wereldrecord voor\nspoorwegen: %.1f km/\nby %s. New world record for ship: %.1f km/h by %s. Nieuw wereldrecord voor\nschepen: %.1f km/h\nby %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &1_CITY_SYLL dam &2_CITY_SYLL sum &3_CITY_SYLL hoven &4_CITY_SYLL tricht &5_CITY_SYLL drecht &6_CITY_SYLL hage &7_CITY_SYLL ham &8_CITY_SYLL mare &9_CITY_SYLL dijk &A_CITY_SYLL huizen &B_CITY_SYLL sel &C_CITY_SYLL burg &D_CITY_SYLL stad &E_CITY_SYLL bron %0_CITY_SYLL woud %1_CITY_SYLL Amster %2_CITY_SYLL Rotter %3_CITY_SYLL Hilver %4_CITY_SYLL Veen %5_CITY_SYLL Eind %6_CITY_SYLL Maas %7_CITY_SYLL Rijn %8_CITY_SYLL Nieuw %9_CITY_SYLL Delf %A_CITY_SYLL Oud %B_CITY_SYLL Volen %C_CITY_SYLL nieuw %D_CITY_SYLL top %F_CITY_SYLL park 1center %s %s 1extern %s buiten %s 1suburb %s %s %s 2center %s centraal %s 2extern %s extern %s 2suburb %s Velden %s %s 3center %s centrum %s 3extern %s land %s 3suburb %s dorp %s %s 4center %s binnen %s 4extern %s veld %s 4suburb %s Kolonie %s %s 5center %s brink %s 5extern %s Weidegrond %s 5suburb Ontwikkelingsgebied %s %s %s 6center %s Centraal %s 6extern %s Industrieterrein %s 7center %s Stad %s simutrans-124.5/simutrans/text/nl/000077500000000000000000000000001520675460500172065ustar00rootroot00000000000000simutrans-124.5/simutrans/text/nl/airtools.txt000066400000000000000000000142301520675460500216030ustar00rootroot00000000000000Luchtvaart Tools Help (Nederlands)

Luchtvaart Tools

Luchtvaart Tools bevatten tools om een luchtvaart infrastructuur te bouwen . Deze tools kunnen de volgende onderdelen bouwen of slopen: taxibanen en start- en landingsbanen voor vliegtuigen, pieren (gebruikt om passagiers en vracht te laden en te lossen), hangars (voor het beheren en kopen van vliegtuigen) en nog andere luchtvaart gebouwen. Indien de tijdlijn is geactiveerd in het spel, zal naar mate de tijd vordert in Simutrans het aantal beschikbare keuzes aan verandering onderhevig zijn.

Klik op het vliegtuig icoon bovenin het scherm om het menu te openen
Beweeg de muis aanwijzer over de beschikbare keuzes in het menu (het menu dient wel geactiveerd te zijn, anders even ergens in het menu klikken) om de naam en waar van toepassing: constructiekosten, onderhoudskosten tussen haken, en de snelheidslimiet.

{TIP: Een manier om een eenvoudige luchthaven te bouwen is:
i) leg taxiba(a)n(en) en start/landingsbanen aan en verbindt deze met elkaar;
ii) plaats een of meer pieren aan het einde van de taxiba(a)n(en);
iii) voeg overige luchthaven gebouwen toe indien nodig.}

In het menu kunnen de volgende tools voorkomen, van links naar rechts:

Taxibaan: cre-eert een taxibaan voor het verbinden van de pier met de start- en/of landingsbaan. Taxibanen dienen op een horizontale ondergrond te worden geplaatst.
LET OP: Als taxibanen aangesloten zijn op het begin of het einde van een start- en landingsbaan kan het voorkomen dat vliegtuigen niet kunnen stijgen of landen.

Hoe een taxibaan te bouwen: klik op de toolbar (het menu met iconen voor Luchtvaart) op het taxibaan icoon (verandert de cursor in een afbeelding van een taxibaan), klik op het terrein voor de beginpositie (cursor verandert in een bulldozer en laat de co-ordinaten zien rechtsonder op het scherm), en klik tot slot op het terrein voor het eindpunt van de taxibaan.

{TIP: Gebruik de verwijder functie om individuele stukken taxibaan te verwijderen. De undo functie [z] geeft het ge-investeerde bedrag niet terug.}

Start- en landingsbaan: cre-eert een start- (en landingsbaan) welke door vliegtuigen worden gebruikt om te stijgen en te landen. Startbanen worden gebouwd op horizontaal terrein en mogen elkaar kruisen.
Hoe een startbaan te bouwen: klik op de toolbar (het menu met iconen voor Luchtvaart) op het start- en landingsbaan icoon (verandert de cursor in een afbeelding van een start- en landingsbaan), klik op het terrein voor de beginpositie (cursor verandert in een bulldozer en laat de co-ordinaten zien rechtsonder op het scherm), en klik tot slot op het terrein voor het eindpunt van de start- en landingsbaan.

{TIP: Houdt het begin en einde van start- en landingsbanen vrij van connecties omdat het anders kan voorkomen dat vliegtuigen niet willen stijgen of landen. Gebruik de verwijder functie om individuele stukken start- en landingsbaan te verwijderen. De undo functie [z] geeft het ge-investeerde bedrag niet terug.}

Verwijder (start/landings/taxi)banen: verwijdert aaneengesloten delen van een taxibaan of start- en landingsbaan, als er zich geen vliegtuigen bevinden tussen het begin en het einde van de te verwijderen baan. Het gebruik van deze tool brengt constructiekostenmet zich mee.

Hoe een baan te verwijderen: klik op de toolbar (het menu met iconen voor Luchtvaart) op het rode kruis icoon (verandert de cursor in een afbeelding van een rood kruis), klik op het terrein voor de beginpositie (laat de co-ordinaten zien rechtsonder op het scherm), en klik tot slot op het terrein voor het eindpunt van de start- en landingsbaan.

{TIP: Houdt het begin en einde van start- en landingsbanen vrij van connecties omdat het anders kan voorkomen dat vliegtuigen niet willen stijgen of landen. Gebruik de verwijder functie om individuele stukken start- en landingsbaan te verwijderen.}

Hangar (Depot): bouwt een hangar om vliegtuigen te kopen en te beheren. Hangars hebben onderhoudskosten en worden gebouwd op een uiteinde van een taxibaan.
Om een hangar te bouwen (verandert de cursor in een hangar) en klik op een uiteinde van een taxibaan.

{TIP: Verwijder hangars met de verwijder tool.}

Pier: noodzakelijk om het in- en uitladen van passagiers en vracht mogelijk te maken.
Wanneer een pier niet in de buurt van een bestaand station of halte wordt gebouwd zal er een nieuw station worden gebouwd.
Een pier wordt op het einde van een stuk taxibaan geplaatst; een pier heeft onderhoudskosten en kan passagiers, post en vracht herbergen. Een vliegtuig zal alleen landen wanneer er een pier beschikbaar is voor laden en lossen.
Hoe een pier te plaatsen: klik op de toolbar (het menu met iconen voor Luchtvaart) op het pier icoon en klik vervolgens op een uiteinde van de taxibaan op het terrein.

{TIP: Verwijder pieren met de verwijder tool. Druk op [v] om het dekkingsgebied van een pier te tonen of te verbergen.}

Luchthaven gebouwen: bouwt uitbreidingen voor de luchthaven (station of halte)> die de capaciteit voor passagiers, post en vracht kunnen vergroten, het dekkingsgebied van het vliegveld kunnen vergroten maar ook onderhoudskosten kunnen verhogen. Indien er in de bovenhoek van de te kiezen uitbreiding een icoontje staat (ook gebruikt in stationslijst en stations en halte details) geeft dat aan welke capaciteit wordt uitgebreid.
Hoe een uitbreiding te plaatsen: klik op de toolbar (het menu met iconen voor Luchtvaart) op het rode kruis icoon (verandert de cursor in een afbeelding van de gekozen uitbreiding), en plaats deze naast een pier/station/halte. De nieuwe uitbreiding wordt nu geacht bij de pier/station/halte te horen.

{TIP: Verwijder uitbreidingen met de verwijder tool. Druk op [v] om het dekkingsgebied van een pier/station/halte te tonen of te verbergen.}

simutrans-124.5/simutrans/text/nl/citylist_filter.txt000066400000000000000000000036611520675460500231660ustar00rootroot00000000000000Lijst met steden

Lijst met steden

Lijst met steden toont details over dorpen en steden en inwoners.

Hoe te openen: klik op het Lijst met steden icoon in Lijstenbeheer of druk op toets [T].
Lijst met steden heeft twee knoppen die de volgorde bepalen waarop de dorpen en steden (en de inwoners) worden getoond.
{TIP: Als de items in de lijst slechts gedeeltelijk zichtbaar zijn, dan kun je het scherm vergroten Lijst met steden of door de lijst "scrollen" door middel van de scroll-bar.}

Totaal inwoners: is het totaal aantal bewoners in het spel (het totaal van de inwoners van alle dorpen en steden). De recente groei van de bevolking wordt getoond tussen haakjes.

Gesorteerd op: bepaalt de sortering van de lijst.
Klik meerdere malen op de knop om door de beschikbare sorteringen te bladeren (naam van de knop wijzigt ook):

- Naam toont de alfanumerieke lijst op de ASCII-code volgorde (hoofdletters gaan voor kleine letters) op de toegekende namen.
- Inwoners sorteert op het aantal inwoners.
- Groei sorteert op de mate van groei van een dorp of stad (welke afhankelijk is van de grootte van het dorp of stad het aantal reizigers).

- oplopend / aflopend draait de volgorde van de lijst om.

Klik op een dorp of stad in de Lijst met stedenom meer stadsinformatie over de beteffende plaats te zien.
Voor elke plaats wordt het volgende getoond:

Toegewezen naam van het gebied (kan worden gewijzigd in stadsinformatie scherm)..

aantal inwoners van het gebied, tussen haakjes staat de recente groei van de bevolking.

simutrans-124.5/simutrans/text/nl/citywindow.txt000066400000000000000000000105101520675460500221440ustar00rootroot00000000000000Stadsinformatie Help (Nederlands)

Stadsinformatie

Stadsinformatie toont statistieken over het geselecteerde dorp of stad en biedt de mogelijkheid de naam van de betreffende plaats te wijzigen.

Elk dorp of stad heeft een eigen stadhuis en zal groeien in de buurt van dat stadhuis. Transport van passagiers en post helpt om het dorp of de stad te laten groeien en dat heeft de volgende kenmerken: door de speler aangelegde wegen worden overgenomen door de overheidr; nieuwe gebouwen en industrie verschijnen op de kaart; en de grenzen van de stad worden uitgebreid.

Klik op het stadhuis met de info tool of op de naam van het dorp of de stad in lijst met steden om het scherm met stadsinformatiete bekijken, waarop het volgende is te zien: een vak met een naam; statistieken; twee kleine kaarten; en een grafiek voor de geselecteerde plaats.

naam: toegekende naam van de plaats verschijnt in het naam vak bovenin het scherm met stadsinformatie.
Namen van dorpen en steden worden gebruikt in de standaard namen voor haltes en stations.
Hoe een naam te wijzigen: klik op het naam vak en voer een nieuwe naam in.
{TIP: Gebruik [!] om de naam van een plaats boven het stadhuis zichtbaar en onzichtbaar te maken in het spel. Zie ook readme_citylist.txt (in ...simutrans/text/) voor hulp bij het genereren van namen van dorpen en steden}.

Onder het naam vak zijn statistieken te zien voor de geselecteerde plaats:
inwoners: het aantal inwoners van de plaats. Tussen haakjes is de recente groei in inwoneraantal te lezen.
gebouwen: het aantal gebouwen dat zich in de plaats bevindt.
co-ordinaten: geeft de grenzen aan van de plaats (verandert wanneer het dorp of de stad groeit).

Kengetallen zullen toenemen wanneer de plaats groeit en afnemen wanneer er nieuwe gebouwen worden gebouwd in het dorp of de stad:
werkloosheid: geeft aan hoeveel inwoners zonder werk zitten en welk type gebouw zou kunnen worden gebouwd (huizen, winkels, industrie).
dakloos: geeft aan hoeveel inwoners geen huis hebben en welk type gebouw zou kunnen worden gebouwd (huizen, winkels, industrie)..

kleine kaarten van de virtuele wereld in het stadsinformatie scherm toont bestemmingen voor passagiers en post.
Bestemmingen worden getoond door gekleurde punten en zijn geldig voor de maand waarin het spel zich bevindt (rechter kaart) en voor de afgelopen maand (linker kaart). De kleur van de punten geeft de details van de passagiers- en postbestemmingen aan:
- geel: er is een route gevonden met een rustige halte of station om de reis te beginnen (lachende smiley wordt getoond in de stationsinformatie).
- oranje: er is geen route gevonden naar de bestemming.
- rood: er is een route gevonden naar de bestemming maar de halte of station aan het begin van de route is overbezet (triest kijkende smiley in de stationsinformatie).

Grafiek toont statistieken voor de plaats.
De y-as (verticaal) is hoeveelheid, de x-as (horizontaal) is tijd.
Klik op de tabbladen om de schaal van de x-as te wijzigen:
Jaren: laat de jaarlijkse waardes zien voor de afgelopen tien jaren.
Maanden: toont de maandelijkse waardes voor de afgelopen twaalf maanden.

Klik op de knoppen onderin de grafiek om informatie in de grafiek aan of uit te zetten.
De kleuren van de lijn in de grafiek corresponderen met de kleuren van de knoppen:
Inwoners: bevolking.
Groei: wijziging in het aantal inwoners (afhankelijk van de grootte van de plaats en van het transport van passagiers en post).
Vervoerd: passagiers en post die zijn begonnen met een reis naar een bestemming (getoond als een lachende smiley in stations- en halte-informatie)
Passagiers: Totaal aantal passagiers en post dat beschikbaar is voor tansport in de geselecteerde plaats.

{TIP: Instellingen van dorpen en steden zijn te veranderen in de configuratiebestanden simuconf.tab en cityrules.tab}.

simutrans-124.5/simutrans/text/nl/climates.txt000066400000000000000000000027571520675460500215630ustar00rootroot00000000000000Klimaatinstellingen Help (Nederlands)

Klimaatinstellingen

Klimaatinstellingen biedt opties om topografische en meteorologische variabelen te veranderen in de virtuele spel wereld

Klimaatinstellingen wordt geactiveerd wanneer je Simutrans start of door te kiezen voor Hoofdmenu en vervolgens voor de optie Nieuwe kaart.

Pijltjes knoppen veranderen de instellingen:

opties:

Waterpeil: bepaalt op welk niveau het water zich bevindt in het nieuwe spel.

Berghoogte: bepaalt de maximum hoogte van het terrein.

Ruigheid: bepaalt de ruigheid van het terrein; een hoger getal zorgt voor extra glooingen.

Sneeuwgrens opties:

Sneeuwgrens(z): bepaalt de hoogte van de sneeuwgrens in de zomer

Sneeuwgrens(w): bepaalt de hoogte van de sneeuwgrens in de winter.

Klimaat opties:

Woestijn: weinig begroeing, extreme temperaturen

Tropisch: tropisch, erg hoge en constante temperatuur, regenwouden.

Subtropisch: gematigd wisselvallig vochtig weer.

Gematigd: wisselvallig weer, komt voor tussen de polen en de subtropische gebieden.

Toendra: lage temperatuur, grenst aan de polen

rotsachtig: maakt de kaart meer of minder rotsachtig.

simutrans-124.5/simutrans/text/nl/color.txt000066400000000000000000000020511520675460500210630ustar00rootroot00000000000000Kleur van de speler Help (Nederlands)

Kleur van de speler

Kleur van de spelers bepaalt de kleur van de speler.
Voertuigen, gebouwen en andere zaken krijgen de kleur van de speler.

Kleur van de speler wordt geactiveerd door de optie kleuren te kiezen vanuit het Hoofdmenu.

Klik op de knop met de kleur van je keuze om de kleur voor de speler te wijzigen.
De titelbalk van Kleur van de speler scherm zal van kleur veranderen (niet in de nieuw gekozen kleur) om aan te geven dat de instelling is gewijzigd.

Standaard kleur voor de mens is licht blauw.
Niet alle objecten zijn geschikt om in de kleur van de speler te worden getoond.

{TIP: Om de kleur van een AI (= kunstmatige intelligentie = computer) speler te wijzigen kies eerst de Lijst met spelers of P (net zo vaak totdat je de speler van je keuze bent) om van speler te wisselen}.

simutrans-124.5/simutrans/text/nl/convoi.txt000066400000000000000000000073471520675460500212570ustar00rootroot00000000000000Voertuigen Lijst

Voertuigen Lijst

Voertuigen Lijst toont informatie over voertuigen en kan een filter toepassen op de lijst met voertuigen (een voertuig is een enkel voertuig of een samenstelling van voertuigen).

Hoe te benaderen: klik op het voertuigen lijst icoontje in Lijstenbeheer of toets [V]. De voertuigen lijst heeft vier knoppen, de eerste twee knoppen bepalen de sortering van de lijst en de andere twee zijn voor het filter.
Onder de knoppen wordt een lijst met voertuigen getoond die (indien geactiveerd) voldoen aan de filter voorwaarden.
{TIP: indien er geen voertuigen te zien zijn kun je de filter instellingen aanpassen. Indien de lijst slechts voor een deel zichtbaar is, kun je de grootte van het scherm voertuigen lijst of scrollen door middel van de schuifbalk.}

Klik op de knoppen om door de opties te bladeren (verandert de naam van de betreffende knop in het op dat moment gekozen instelling) of opent een scherm voor de instellingen van het filter :

Gesorteerd op: Twee knoppen regelen de instelling van de volgorde waarop de voertuigen worden getoond in de lijst.

- Naam sorteert op de naam van het voertuig, alfanumeriek op de volgorde van ASCII-code order (hoofdletters voor kleine letters (de naam is standaard het merk en naam van het eerst gekozen voertuig van de samenstelling in een remise of hangar (depot)).
- Inkomsten sorteert de lijst op inkomsten (netto). - Type sorteert de lijst op het type transport (in oplopende volgorde wegverkeer, treinen, schepen en vliegtuigen)
- id nummer sorteert de lijst op het unieke id-nummer van het voertuig (wordt automatisch toegekend op het moment dat het voertuig in een remise of hangar (depot) wordt gekocht en wordt getoond in de titelbalk van het scherm voertuig informatie).

- oplopend / aflopend keert de volgorde van de lijst om.

Filter: Twee knoppen waarvan de eerste het filter in- en uitschakelt en de tweede een scherm opent met instellingen van het filter.

- aan / uit klik op de knop om het filter voor voertuig informatie aan of uit te zetten.

- Instellingen opent het scherm om de selectiecriteria voor het filter aan te passen.

Klik op een voertuig in de lijst om meer informatie te zien van het voertuig.

De volgende informatie wordt getoond voor elk voertuig:

id-nummer: een uniek nummer dat automatisch wordt toegekend op het moment dat het voertuig in een remise of hangar (depot) wordt gekocht en wordt getoond in de titelbalk van het scherm voertuig informatie).

naam naam van het voertuig (dit is standaard het merk en naam van het eerst gekozen voertuig van de samenstelling in een remise of hangar (depot)).

Inkomsten toont inkomsten (netto).

Lijn: toont de toegekende lijn en toont of een voertuig zich in een remise of hangar (depot).

plaatje toont een plaatje van het voertuig en een groene balk die weergeeft hoeveel lading het voertuig op dit moment vervoert ten opzichte van het maximum laadvermogen.

simutrans-124.5/simutrans/text/nl/convoi_filter.txt000066400000000000000000000047001520675460500226120ustar00rootroot00000000000000Voertuigen Lijst Filter

Voertuigen Lijst Filter

Voertuigen Lijst Filter biedt mogelijkheden om te bepalen welke Voertuigen worden getoond in de Voertuigen lijst.

Klik op de Instellingen knop in de voertuigen lijst om het Voertuigen Lijst Filter te benaderen.

De knoppen selecteren voertuigen die worden getoond in de voertuigen lijst als het filter aan is. Alleen voertuigen die aan alle selectie criteria voldoen worden getoond.

Klik op een knop om de selectie criteria te activeren of te deactiveren voor het filter:

Filter namen: selecteert voertuigen door middel van de naam van het voertuig. Hoe te gebruiken: klik op de knop om het filter te activeren, en klik vervolgens in het vak onder Filter namen en toets de naam precies in (hoofdlettergevoelig).

Filter type: selecteert voertuigen van het geselecteerde type indien geactiveerd. Hoe te gebruiken: klik op de knop naast Filter type om het filter te selecteren, klik vervolgens op het type transport dat je wilt selecteren: Wegverkeer, Treinen, Schepen en Vliegtuigen.

Speciaal filter: selecteert voertuigen indien deze voldoen aan de volgende criteria (klik op de knop naast Speciaal filter om te selecteren, klik vervolgens op):
- geen route - voertuig kan geen route vinden naar de volgende bestemming.
- geen planning - voertuigen die geen route toegewezen hebben gekregen.
- geen inkomen - voertuigen die geen winst maken.
- in een depot - voertuigen die in een hangar of remise (depot) staan.
- geen lijn - voertuigen die geen lijn toegewezen hebben gekregen .

Filter goederen: selecteert voertuigen die in staat zijn om de geselecteerde goederen en passagiers te vervoeren. Klik op de knop naast Filter goederen om het filter te selecteren een klik vervolgens op de knop naast de optie van je keuze om te selecteren. Gebruik de schuifbalk om te scrollen door de beschikbare goederen in de lijst. Opties bevatten:
- alles selecteert alles.
- geen zet alles uit.
- wissel draait de selectie om.

simutrans-124.5/simutrans/text/nl/convoidetail.txt000066400000000000000000000044161520675460500224340ustar00rootroot00000000000000Voertuig Details Help (Nederlands)

Voertuig details

Voertuig details geeft meer informatie over een voertuig (een in gebruik zijnd voertuig of samenstelling) en kan gebruikt worden om een voertuig onmiddelijk te kunnen verkopen.

Voertuig details worden zichtbaar door op de knop Details te klikken in het voertuig informatie scherm.
Indien niet alle items zichtbaar zijn kun je de grootte van het scherm wijzigen of de schuifbalken om door de items te scrollen.

Bovenin het scherm Voertuig details getoonde informatie bevat:

Vermogen: in kW van het voertuig (optelsom van het vermogen van alle gemotoriseerde voertuigen in de samenstelling).

Verkoop waarde: bedrag dat het voertuig nog oplevert indien het verkocht wordt op dat moment (is aanschafwaarde minus afschrijving).

LET OP: Klik op verkopen knop om het voertuig onmiddelijk te verkopen. Er wordt niet om bevestiging gevraagd. Het voertuig wordt uit het spel verwijderd en de speler krijgt de verkoop waarde bijgestort in de kas. Terugtrekken doet hetzelfde dan verkopen maar zal eerst de volledige lading afleveren alvorens verkocht te worden.

Voertuig details toont alle voertuigen uit de samenstelling.
Per voertuig wordt de volgende informatie getoond:
- afbeelding van het voertuig een afbeelding van het voertuig (wordt ook gebruikt in de hangar of remise (depots)).
- Geproduceerd: jaar en maand van productie van het voertuig en indien met de tijdlijn ingeschakeld wordt gespeeld wordt ook de datum van introductie van het voertuig vermeld.
- Verkoop waarde: bedrag dat wordt ontvangen indien het voertuig nu verkocht zou worden.
- Vermogen: vermogen in kW (in geval van gemotoriseerd voertuig).
- Frictie: huidig frictie niveau.
- Max inkomsten: het maximale bedrag dat het voertuig aan inkomsten heeft gegenereerd.
- Lading hoeveelheid en bestemming van de op dit moment aanwezige lading.

simutrans-124.5/simutrans/text/nl/convoiinfo.txt000066400000000000000000000174751520675460500221360ustar00rootroot00000000000000Voertuig Informatie

Voertuig Informatie

Voertuig informatie toont informatie over een voertuig (een enkel voertuig of een voertuig samenstelling) dat wordt gebruikt voor het vervoeren van vracht of passagiers.
In het scherm worden details getoond over de lading van het voertuig en tevens zijn er knoppen voor meer details, er wordt een mogelijkheid geboden het voertuig direct te verkopen, je kunt het voertuig volgen op de kaart, de naam van het voertuig wijzigen en een dienstregeling toewijzen of wijzigen van een voertuig.

Een voertuig is een samenstelling van minimaal een (1) aangedreven (gemotoriseerd) voertuig en een hoeveelheid aanhangers of wagons.
Voorbeelden van voertuigen zijn: een span paarden dat een kar trekt; een locomotief met tender en wagons; een bus; een bestelauto; een tram met wagons; een schip of een vliegtuig.
Voertuigen worden samengesteld en gekocht in een remise of hangar (depot) van toepassing voor het type voertuig.
{TIP: Een voertuig samenstelling heeft een maximum aantal voertuigen in een samenstelling: treinen, 24; wegverkeer, 4 .}

Klik op een voertuig in het spel met de info tool of klik op een voertuig in de lijst met voertuigen of lijnen beheer om het scherm met voertuig informatie te activeren.
De titelbalk van het scherm met voertuig informatie toont een uniek ID nummer en een naam van het voertuig (dit is standaard het merk en naam van het eerst gekozen voertuig van de samenstelling in een remise of hangar (depot)).
Voertuig informatie bestaat uit een vak met een naam; een mini kaart en informatie over het voertuig; knoppen met opties; en een opsomming van de op dat moment door het voertuig getransporteerde goederen of passagiers (indien niet alles zichtbaar is kun je de grootte van het scherm wijzigen of de schuifbalken gebruiken om te scrollen).

De volgende informatie wordt getoond in de titelbalk en bovenin van het scherm voertuig informatie :

Intern ID: een uniek ID nummer voor dat voertuig (toegekend wanneer het voertuig wordt gekocht in een remise of hangar (depot)).

- naam: dit is standaard het merk en naam van het eerst gekozen voertuig van de samenstelling in een remise of hangar (depot)).
Klik in het naam vak en toets een nieuwe naam om de toegewezen naam te wijzigen.

- snelheid: de huidige snelheid in km/u.
De maximum snelheid van het voertuig staat tussen haakjes vermeld (dit is de maximum snelheid van het langzaamste voertuig uit de samenstelling).
De snelheid ten opzichte van de maximum snelheid wordt weergegeven door een groene balk.

- Inkomsten: de netto inkomsten die de exploitatie van het voertuig dit kalenderjaar heeft opgeleverd. Verlies wordt voorafgegaan door een - en in het rood weergegeven.
De kostprijs per kilometer is vermeld tussen haakjes in Hajo valuta per kilometer.

- Gewicht: totaal gewicht van het voertuig inclusief lading op dit moment in ton.
Het gewicht van de lading staat tussen haakjes vermeld.
Een groene balk geeft aan hoe de werkelijke lading zich verhoudt tot de maximale lading voor het voertuig of samenstelling.
Een gele balk geeft aan dat het voertuig wacht op lading (zie laden t/m). Dit is in te stellen bij de dienstregeling van het voertuig en is een percentage van de maximale laadcapaciteit van het voertuig. Het voertuig zal niet eerder vertrekken totdat minstens de minimale lading is geladen.
{TIP: Om laden t/m in te stellen of te wijzigen voor alle voertuigen die dezelfde lijn bedienen, kun je de Lijn veranderen knop in Lijnenbeheer gebruiken of dezelfde knop in de remise of hangar (depot). Om alleen voor een (1) voertuig de laden t/m instelling te wijzigen kun je de knop dienstregeling in het scherm voertuig informatie of in de remise of hangar (depot).}

- Bestemming: De volgende halte of stopplaats volgens de dienstregeling van het voertuig.
De groene balk geeft de relatieve afstand aan die is afgelegd op weg naar de aangegeven bestemming.

- Bedient lijn: toont de lijn die het voertuig bedient indien er een lijn is toegewezen aan het voertuig. Een voertuig kan ook een aparte dienstregeling hebben.

- mini kaart toont het voertuig in aktie.
Klik op de mini kaart om het scherm te centreren rond het voertuig.

Klik op de knoppen in het scherm voertuig informatie om opties te activeren of een scherm te openen:

Dienstregeling: opent een scherm om de dienstregeling aan te passen of om laden t/m te wijzigen.

naar depot: stuurt het voertuig naar een dichtbij gelegen remise of hangar (depot). De aanwezige lading zal verloren gaan maar wel geld opbrengen voor het transport naar de remise of hangar.
Eenmaal in de remise of hangar zal het voertuig de dienstregeling en de toegewezen lijn behouden.

alleen lossen: zorgt ervoor dat er alleen lading wordt gelost en niet geladen.

volgen: als deze knop is geactiveerd zal het voertuig gevolgd worden op de kaart.
Nogmaals klikken of ergens op de overzichtskaart zal het volgen van het voertuig eindigen

Gesorteerd op: sorteert het overzicht van de aanwezige lading.
Het overziccht toont informatie over de hoeveelheid lading, de capaciteit van het voertuig, soort lading, de bestemming van de lading en het eerste overlaadstation.
De knop krijgt de naam van de actieve sortering. De mogelijkheden zijn:
- bestemming: sorteert de lading alfanumeriek op de volgorde van ASCII-code order (hoofdletters voor kleine letters), op de naam van de uiteindelijke bestemming van de lading.
- via: sorteert de lading alfanumeriek op de volgorde van ASCII-code order (hoofdletters voor kleine letters), op de naam van het eerste overlaadstation.
- aantal-via: sorteert de lading op hoeveelheid en op het eerste overlaadstation.
- aantal: sorteert de lading op hoeveelheid in aflopende volgorde.

Grafiek: klik op de knop om de grafiek aan of uit te zetten (knop is ingedrukt wanneer de grafiek zichtbaar is) in het voertuig informatie scherm.
De grafiek toont statistieken over de laatste 12 maanden (x-as) wanneer er een grafiek is geactiveerd door middel van de knoppen onder de grafiek.
Klik op de knoppen om grafieken aan of uit te zetten. De knop zal zijn ingedrukt wanneer een optie wordt getoond.
De kleur van de lijn in de grafiek correspondeert met de kleur van de knop voor de grafiek:
- Vrije capaciteit geeft informatie over onbenutte laadcapaciteit van het voertuig.
- Vervoerd geeft informatie over de hoeveelheid lading die is vervoerd.
- Inkomsten de inkomsten die het voertuig heeft gegenereerd.
- Exploitatiekosten geeft aan hoeveel kosten het voertuig heeft gemaakt.
- Winst het saldo van de twee bovenstaande waardes.

Details opent het voertuig details scherm met meer informatie en de opties om een voertuig terug te trekken of te verkopen.

simutrans-124.5/simutrans/text/nl/curiositylist_filter.txt000066400000000000000000000051651520675460500242510ustar00rootroot00000000000000Lijst met bezienswaardigheden

Lijst met bezienswaardigheden

Lijst met bezienswaardigheden toont informatie over alle bezienswaardigheden (dit zijn zowel vertrekpunten als bestemmingen voor passagiers en post).

Hoe het scherm lijst met bezienswaardigheden te openen: klik op het icoon voor lijst met bezienswaardigheden in het submenu met opties voor het lijstenbeheer.
{TIP: indien de bezienswaardigheden in de lijst ten dele zichtbaar zijn, kun je de lijst met bezienswaardighedenin grootte wijzigen of door de lijst scrollen door middel van de schuifbalk.}

Gesorteerd op bepaalt de volgorde waarop de bezienswaardigheden worden getoond.
Klik op de knoppen om door de verschillende mogelijkheden te bladeren (dit zal de naam van de knop wijzigen in de op dat moment actieve sortering):

- Naam sorteert alfabetisch op naam.
- Passagiersaanbod sorteert op het passagiers- en postaanbod dat een attractie genereert.

- oplopend / aflopend keert de volgorde van de lijst om.

Klik op een bezienswaardigheid in de lijst met bezienswaardigheden om meer informatie over de gekozen bezienswaardigheid te zien.
{TIP: klik op het plaatje in het detail scherm om naar de betreffende bezienswaardigheid op de kaart te gaan.}

Informatie welke wordt getoond van iedere bezienswaardigheid bestaat uit:

statusbalk kleuren geven een indicatie voor de status van de bezienswaardigheid:
- geel: heeft geen halte of station waar het passagiers en post verbonden.
- groen: heeft een station of halte verbonden voor passagiers.
- blauw: heeft een halte of station verbonden voor post.
- turquoise: heeft zowel een halte of station voor passagiers als voor post.
- oranje: een (1) verbonden station of halte is overbezet.
- rood: alle verbonden stations of haltes zijn overbezet.

gebouw icoon geeft aan dat de bezienswaardigheid zich in een stad of dorp bevindt.

naam van de bezienswaardigheid.

passagiersaanbod geeft tussen haakjes aan hoeveel passagiers en post de bezienswaardigheid genereert (in andere woorden: hoe populair het is).

{TIP: Om een optimale service voor passagiers en post te bieden kan het nodig zijn dat er meerdere haltes of stations zijn verbonden met een bezienswaardigheid.}

simutrans-124.5/simutrans/text/nl/depot.txt000066400000000000000000000254631520675460500210740ustar00rootroot00000000000000Depot (garage, hangar, remise)

Depot (garage, hangar, remise) Opties

In dit spel wordt de term depot gebruikt om een garage, remise, hangar of werf aan te duiden. Dit is afhankelijk van het type voertuig dat in het depot wordt geproduceerd. Zo zijn er depots voor wegtransport, railvervoer (treinen, monorails/maglevs en trams), luchtvaart (vliegtuigen en dergelijke) en scheepvaart (schepen en dergelijke). Al deze depots hebben een groot aantal overeenkomsten welke in deze helptekst worden behandeld.
Een depot wordt gebruikt om voertuigen aan te schaffen en te beheren. Een depot toont alleen voertuigen die ook kunnen bewegen op het type infrastructuur dat is verbonden met het depot: een depot voor treinen dat niet is verbonden met rails met bovenleiding zal geen elektrische treinen tonen.
Indien de optie tijdlijn is ingeschakeld, zal het depot alleen die nieuwe voertuigen tonen die op dat moment in productie zijn. Naar mate de tijd vordert zullen voertuigen worden toegevoegd dan wel worden verwijderd.

Gereedschappen om de verschillende types depots te maken zijn beschikbaar in de iconenbalk voor het betreffende type transport: Trein remise; Monorail remise; Tram remise; Garage; Scheepswerf ; en een Hangar.

Klik op een depot met de Info tool om het scherm met de Depot opties te openen. Het scherm geeft informatie over Voertuigen en voertuig samenstellingen en de mogelijkheid deze samen te stellen, te beheren en te verkopen.
De titelbalk van de Depot opties geeft aan welk type voertuigen er mee kunnen worden beheerd.

Het scherm Depot opties kan in grootte worden gewijzigd en door in de titelbalk op het pijltje naar beneden te klikken (nogmaals op het pijltje naar beneden klikken zal het scherm weer herstellen in de oorspronkelijke grootte).
De pijltjes naar links en naar rechts zullen naar een vorig of volgend depot van hetzelfde type navigeren en het spelscherm centreren op het geselecteerde depot.

Bovenin het Depot opties scherm wordt het aantal voertuigen getoond dat aanwezig is in het geselecteerde depot, en wanneer een (1) van deze voertuigen is geselecteerd zal ook de naam en de lijn worden getoond.

Voertuig naam: Klik op de pijltjes links of rechts van de naam om door de aanwezige voertuigen te bladeren.
Hoe de naam van een voertuig te wijzigen: klik in het veld waar de naam wordt getoond en pas de naam aan. De standaard naamgeving die het spel toepast is de naam van het eerst gekochte voertuig in de samenstelling van het voertuig.

Hoe een lijn toe te kennen: gebruik de pijltjes links en rechts om te bladeren in de beschikbare lijnen voor dit type voertuig of klik in het vak met daarin de aanduiding van de lijn (onder de naam van het voertuig) om een scherm met beschikbare lijnen te tonen. Selecteer vervolgens de lijn die je wilt en klik op de knop lijn toewijzen.
Hoe de naam van een lijn te wijzigen: klik in het vak waar de lijn wordt getoond en toets een nieuwe naam. Standaard wordt de naam Lijn + (nummer van de lijn) toegewezen.

Afbeelding van het voertuig: onder de velden voor naam en lijn van het voertuig vind je een afbeelding van het geselecteerde voertuig of samenstelling.
Beweeg de muis over de afbeelding van het voertuig of de samenstelling om details te zien over het deel van het voertuig waarnaar je wijst. (De details worden getoond onderin het scherm van de depot opties).
Klikken op het deel van het voertuig in de afbeelding zal dat betreffende deel verwijderen uit de samenstelling en deze weer plaatsen in de voorraad van het depot.

Onder de afbeelding van het voertuig of de samenstelling staat het aantal delen van het voertuig of de samenstelling vermeld en tevens de benodigde grootte van het station of de halte om het hele voertuig of de samenstelling volledig te kunnen laden en lossen, alsmede de lijn die het voertuig bedient.

De knoppen in het depot opties hebben de volgende functies (klik om de betreffende optie te selecteren):

Starten: het geselecteerde voertuig wordt gestart en zal het depot verlaten indien de toegewezen route of dienstregeling kan worden gestart (met andere woorden: indien er een route kan worden gevonden).

Dienstregeling: opent een scherm met de mogelijkheid om een dienstregeling toe te kennen of te wijzigen; tevens is per station of halte aan te geven hoeveel de minimaal aanwezige lading moet zijn om verder te gaan met de volgende halte of station in de dienstregeling.

Opheffen: plaatst het geselecteerde voertuig in de voorraad van het depot. Het betreffende voertuig zal niet langer een dienstregeling onderhouden.

Verkopen: verkoopt het geselecteerde voertuig, het kassaldo zal met de restwaarde van het voertuig worden verhoogd.

Nieuwe lijn: opent een scherm met de mogelijkheid om een lijn te defini-eren en eventueel te bepalen hoeveel lading het voertuig dient te hebben voordat het naar het volgende punt in de lijn vertrekt. Een lijn kan voor meerdere voertuigen worden gebruikt.

Lijn toewijzen: wijst de geselecteerde lijn toe aan het geselecteerde voertuig.

Lijn veranderen: opent een scherm waarin je de geselecteerde lijn kunt wijzigen (zowel de route als de minimale lading bij een halte of station). Dit heeft gevolgen voor alle voertuigen die de lijn bedienen.

Kopieren: maakt een kopie van het geselecteerde voertuig met dezelfde samenstelling en dienstregeling of lijn. Dit zal automatisch het benodigde aankoopbedrag in mindering brengen op het kassaldo.

Onder de knoppen is een overzicht van de beschikbare voertuigen met afbeeldingen te zien. Wat er zichtbaar is is afhankelijk van de staat waarin een samen te stellen voertuig zich bevindt en de geselecteerde opties onder de plaatjes. Dit kunnen alle voertuigen zijn, alleen de op dit moment nog in productie zijnde voertuigen of alleen die voertuigen welke relevant zijn voor de reeds geselecteerde voertuigen. Ook wordt er een aantal tabbladen getoond waarop verschillende soorten voertuigen zijn te selecteren. Dit is afhankelijk van het type depot wat wordt getoond.
afbeeldingen van voertuigen: Voertuigen worden verdeeld in categori-en die kunnen worden benaderd door op de betreffende tabblad te klikken. Tabbladen verdelen de voertuigen in soorten: voertuigen voor de transport van passagiers en post, overige aangedreven voertuigen en aanhangers en opleggers.
Een getal dat boven het voertuig staat (wit) geeft het aantal eenheden aan dat in voorraad is. Deze kan (kunnen) zonder kosten weer gebruikt worden in een nieuwe samenstelling.

Beweeg de muis over de afbeelding om meer informatie over het betreffende voertuig te krijgen (de volgorde is afhankelijk van het type voertuig):
Naam: en tussen haakjes het type motor dat wordt gebruikt in geval van aangedreven voertuigen.
Prijs: aanschafprijs en tussen haakjes de kostprijs per kilometer.
Vermogen: motorvermogen en maximum snelheid voor aangedreven voertuigen.
Gewicht: in ton.
Maximum snelheid: van niet aangedreven voertuigen.
Capaciteit: hoeveelheid en soort goederen dat het voertuig maximaal kan vervoeren.
Geintroduceerd: datum waarop een voertuig beschikbaar kwam indien de tijdlijn is geactiveerd.
Gebouwd tot: datum waarop het voertuig uit productie wordt genomen. (tijdlijn)
Gemaakt door: de ontwerper van het voertuig in Simutrans.
Overbrenging: versnelling.
Verkoopwaarde: het bedrag dat het voertuig op dit moment nog waard is wanneer het wordt verkocht.

Klik op het voertuig in het tabblad om deze (afhankelijk van de instelling van de knop rechts onderin het scherm) te kopen of te verkopen. Deze knop schakelt tussen drie opties:

Toevoegen: koopt het geselecteerde voertuig en plaatst deze achteraan in de geselecteerde voertuig of samenstelling of indien dit het eerste voertuig is zal dit een nieuw voertuig cre-eren.

Invoegen: koopt het geselecteerde voertuig en plaatst deze vooraan in het geselecteerde voertuig of samenstelling.

Verkopen: verkoopt het in voorraad zijnde voertuig (deze dient dan wel in voorraad te zijn).

gekleurde balken onder de afbeelding van het voertuig geeft aanwijzingen voor volgorde waarin een voertuig moet worden gebruikt en welke voertuigen aan een bestaande samenstelling kunnen worden toegevoegd om een werkende voertuig samenstelling te vormen (kleurenbalken kunnen veranderen door een andere instelling van de hiervoor beschreven knop).
De kleuren betekenen het volgende:
- groen: voertuig kan worden gebruikt.
- rood: voertuig kan niet worden gebruikt.
- geel: voertuig is pas beschikbaar indien er een ander voertuig voor wordt geplaatst.
- blauw: voertuig kan worden gebruikt maar is inmiddels uit productie genomen.
{TIP: een voertuig of samenstelling is bruikbaar wanneer er groene balken voor alle eenheden worden getoond. Indien er nog deels een gele balk wordt getoond zal er nog een aanvullend onderdeel moeten worden toegevoegd.}

Toon alles: deze optie zorgt ervoor dat alle beschikbare voertuigen worden getoond en niet alleen de relevante voor de reeds geselecteerde samenstelling. De optie is ingeschakeld wanneer de knop is ingedrukt. Nogmaals klikken zal de optie uitzetten.
Verouderde tonen: indien er wordt gespeeld met de tijdlijn ingeschakeld, zullen indien deze optie is gekozen ook de verouderde voertuigen worden getoond welke al uit productie zijn genomen.

simutrans-124.5/simutrans/text/nl/display.txt000066400000000000000000000107511520675460500214200ustar00rootroot00000000000000Beeldscherm instellingen help (Nederlands)

Beeldscherm instellingen

Beeldscherm instellingen bevat instellingen waarmee de wijze waarop het spel wordt getoond kunnen worden be-invloed; & geeft informatie over de prestaties van de computer.

Om het scherm beeldscherm instellingen te openen dien je op de Scherm knop klikken in het Spel instellingen scherm.

Klik op de kleine vierkante knoppen om een instelling te activeren of te deactiveren, of gebruik de horizontale pijltjes om de instelling aan te passen:

underground mode: zet het spel in ondergrondse modus (in 99.08.1 nog erg experimenteel)

Dag en Nacht Modules: zet het spel in dag en nacht modus (licht en donker).

Helderheid:bepaalt de helderheid van het scherm tijdens het spel; bij een lagere waarde neemt de helderheid af; te lage of hoge waardes kunnen problemen veroorzaken (dus wees voorzichtig met wijzigen van deze instelling); om de helderheid boven 0 te laten stijgen dien je de [+] toets te gebruiken. Met de muis is een waarde tot maximaal 0 in te stellen.

Scroll omdraaien: draait de richting om bij het scrollen in het spel in het spel scherm (wereld/kaart).

Scroll snelheid: bepaalt de snelheid van het scrollen tijden het spelen in het spel scherm (wereld/kaart).

Verberg objecten transparant: toont doorzichtige objecten met een kleurmarkering in het spel scherm wanneer betreffende objecten verborgen zijn (zie de instellingen hierna, verbergen kan ook met de ["] toets).

Verberg bomen: verbergt bomen in het spel, bomen worden nu of transparant (zie hierboven) of geminimaliseerd getoond wanneer deze instelling is gekozen.

Verberg stadsgebouwen: verbergt alle huizen, kantoren, industrie en winkels in een stad behalve speciale gebouwen.

Verberg alle gebouwen: verbergt alle gebouwen. Deze optie zet tevens de bovenliggende optie aan.

Transparante stations dekking: toont het gebied dat wordt verzorgd door een station als een doorzichtig gekleurd vlak in plaats van de markering met vierkante blokjes.

Toon stations dekking: om tijdens het spel de zichtbaarheid van de dekking van een station aan of uit te zetten kun je ook de [v] toets gebruiken

Voetgangers (stad): toont voetgangers in een dorp of stad.

Voetgangers (halte): toont voetgangers bij een halte of station.

Stadsverkeer: bepaalt de hoeveelheid particuliere voertuigen in steden en dorpen.
De hoeveelheid particuliere voertuigen is afhankelijk van de grootte van het dorp of de stad en deze instelling. Hoge waardes zorgen voor veel verkeer, 0 zet het genereren van stadsverkeer uit.

{TIP: Meer instellingen en de standaard waardes kunnen worden gezet in simuconf.tab}.


Prestaties van de computer:

Onder bovenstaande instellingen wordt informatie getoond over de prestaties van de computer tijdens het spelen van .
Indien de getallen (normaal gesproken wit gekleurd) in het rood of geel worden getoond kan het noodzakelijk zijn om de instellingen van de computer te wijzigen.
Wijzigingen in de snelheid van het spel, W, (of de Snel voorwaarts in tijd >> icoon, of [,]/ [.]) kunnen deze kleur be-invloeden.

Beeldfrequentie: geeft de snelheid van een enkel beeld aan.

Werkloos: Indien deze waarde groter is dan 0, heeft de computer tijd over voor andere taken.

FPS: Hogere waardes geven aan dat het systeem in staat om bijvoorbeeld de voertuigen in het spel vloeiend voort te bewegen. Indien deze waarde rood blijft is dat een indicatie voor het feit dat de computer te traag is om Simutrans goed te kunnen draaien met de huidige instellingen ( probeer eens een kleiner formaat voor het Simutrans scherm).

Simloops:Indien deze waarde rood blijft is dat een indicatie voor het feit dat de computer te traag is om Simutrans goed te kunnen draaien met de huidige instellingen ( probeer eens een kleiner formaat voor het Simutrans scherm).

simutrans-124.5/simutrans/text/nl/general.txt000066400000000000000000000062411520675460500213670ustar00rootroot00000000000000Simutrans Help (Nederlands)

Simutrans Help


Algemeen
- Welkom
- Speel scherm/interface
- Toetsenbord configuratie
- Muis bediening

Spel instellingen
- Taal
- Kleur
- Scherm
- Geluid
- Spelers
- Laden
- Opslaan
- Nieuwe wereld
-- Klimaat instellingen
-- Laad hoogtekaart

Menu - algemeen
- Spel instellingen
- Mini kaart
- Info Tool
- Landschap
- Spoorwegen
- Monorail/Maglev
- Trams
- Autowegen
- Scheepvaart
- Luchtvaart
- Bijzondere werktuigen
-- Kaarteditor
- Verwijder

Menu - management
- Lijnen administratie
- Lijsten administratie
-- Stations lijst
--- Station filter
-- Voertuigen lijst
--- Voertuigen filter
-- Lijst met steden
-- Goederen lijst
-- Industrie lijst
-- Bezienswaardigheden
- Berichtencentrum
- Financiën

Menu - overig
- Schermafdruk
- Pause
- Snel vooruit in tijd
- Hulp

Overige dialogen
-Depot/Garage/Werf informatie
-Dienstregeling informatie
- Station/Halte informatie
-- Station/Halte details
- Voertuigen informatie
-- Voertuigen details
- Stad informatie
- Industrie informatie

Aanvullende hulp is te vinden op de diverse Simutrans websites (in het Engels):
http://guide.simutrans.com
http://wiki.simutrans.com
http://simutrans-tips.com
http://forum.simutrans.com

simutrans-124.5/simutrans/text/nl/mailbox.txt000066400000000000000000000045561520675460500214140ustar00rootroot00000000000000Berichtencentrum & Opties

Berichtencentrum

Het Berichtencentrum toont berichten uit het spel en geeft toegang tot de opties die betrekking hebben op de wijze waarop de berichten getoont worden.

Klik op het mailbox icoon in het menu of druk op de [B] toets om het berichtencentrum te openen.

Het berichtencentrum toont de berichten uit het spel. Door op een bericht te klikken wordt de spel-scherm naar de gerelateerde positie verplaatst.

De knop [Opties] geeft toegang tot de opties voor het berichtencentrum. De opties bepalen hoe de diverse berichten worden getoond.



Berichtencentrum Opties

Het scherm Berichtencentrum Opties bevat drie kolommen met check-boxes die bepalen hoe de diverse berichten worden getoond.

Van links naar rechts bepalen de kolommenof berichten als:
a) scrollende tekst in de statusbalk; b) een tijdelijke popup; of c) een permanente popup
getoond worden. De tijdelijke popup verdwijnt weer na een aantal seconden van het scherm, de permanente popup moet door de speler (m.b.v. [x]) gesloten worden.

De volgende bericht-categorien zijn afzonderlijk in te stellen:

Nieuw Jaar: Een nieuw jaar is begonnen.

AI Nieuws: Berichten betreffende de simutrans spelers.

Stadnieuws: Berichten betreffende de steden en de stedelijke ontwikkelingen.

Geen Route: Een voertuig kan geen route vinden naar de volgende bestemming, station of routepunt.

Nieuwe Industrie: Berichten betreffende de industrie ketens van grondstof producerende industrieen tot de eind-gebruikers.

Tourisme: Berichten betreffende attracties en monumenten.

Nieuwe Voertuigen: een nieuw voertuig komt beschikbaar of is niet langer meer beschikbaar (als timeline wordt gebruikt).

Station vol: Een Station is overvol.

Problemen: Roodstaan en andere kritieke berichten.

simutrans-124.5/simutrans/text/nl/map.txt000066400000000000000000000112711520675460500205260ustar00rootroot00000000000000MiniKaart Help (Nederlands)

MiniKaart

De MiniKaart toont een overzicht van de simutrans wereld. Het geeft informatie over het terrein, belangrijke locaties en de transportinfrastructuur. Daarnaast geeft het de mogelijkheid het speel-scherm naar een andere positie van de simutrans wereld te verplaatsen.

Klik op het MiniKaart icoon in het menu of druk op de [m] toets om het MiniKaart scherm te openen. Met behulp van de rechts/links pijltjes kan de minikaart in 7 stappen in- en uitgezoomd worden. De knop [# ] isometric view toont de minikaart gelijk aan het het speel-scherm.

Het minikaartscherm heeft drie optieknoppen: toon legenda; toon schaal en toon industriën

Water is blauw op de kaart en het land groen. Het hoogte- (land) en diepte- (water) niveau wordt weergegeven in lichtere en donkere tinten. Het gedeelte van de simutranswereld dat zichtbaar is wordt op de minikaart aangegeven door een geel kader.

Links-klik, of links-klik en sleep het kader op de minikaart om het speel-scherm snel door de simutrans wereld te verplaatsen.

Als de muis-cursor op een industrie op de minikaart wordt geplaatst, wordt de naam getoond en worden witte lijntjes getoond naar afnemende industriën. Als eerst de [Shift] wordt ingedrukt wordt m.b.v. rode lijntjes de aanleverende industriën aangegeven.

Steden en dorpen, attracties, tunnels en kanalen worden donker grijs weergegeven; wegen zwart; spoor- en tramlijnen grijs; bruggen licht grijs; stations/haltes rood/geel; monorail- en maglevlijnen oranje; luchthavens wit en voertuigen geel.

Op de minikaart kan extra detailinformatie gegeven worden. Klik hiervoor op [toon legenda]. Er verschijnt nu een hele serie extra optie-knoppen waarmee de informatie die op de minikaart getoond wordt in- en uitgeschakeld kan worden. Een aantal opties maakt gebruik van een min-max schaal. De min-max schaal kan met behulp van de knop [toon schaal] zichtbaar/verborgen worden.

De volgende opties zijn beschikbaar:

Plaatsnamen: namen van steden en dorpen.
Passagiers: gebieden die voorzien zijn van stations en haltes voor passagiersvervoer.
Post: gebieden die voorzien zijn van stations en haltes voor postvervoer.
Vracht:aantallen passagiers en/of goederen op het spoor of de weg (min-max schaal).
Halte status: operationele status van de stations/haltes. De status wordt als volgt weergegeven:
geel: halte/station niet opgenomen in een route; groen: status OK; oranje: station/halte druk. Actie nemen wordt aangeraden; rood: station/halte overvol. Actie nemen noodzakelijk.
Service: aantallen passagiers en/of goederen dat gebruik maakt van een station of halte(min-max schaal).
Verkeer: aantallen voertuigen op het spoor of de weg (min-max schaal).
Herkomst: aantallen passagiers en/of goederen dat het station/halte als beginpunt heeft (min-max schaal).
Bestemming: aantallen passagiers en/of goederen dat het station/halte als eindpunt heeft (min-max schaal).
Wachtend: aantallen passagiers en/of goederen dat bij het station/halte wacht op vervoer (min-max schaal).
Spoorwegen: trein- en tramrails; -bruggen; -tunnels en -seinen (kleur: wit of rood indien geëlectrificeerd; seinen: geel).
Max snelheid: maximaal toegestane snelheid op spoorwegen en wegen (min-max schaal).
HSP leidingen: hoogspanningsleidingen in de simutranswereld. De kleur is een indicatie voor het vermogen dat getransporteerd wordt (min-max schaal).
Attracties: positie van toeristische attracties in de simutranswereld. Tevens toont het de aantallen passagiers dat gegenereerd wordt door attracties (min-max schaal).
Fabrieken: positie van industriën in de simutranswereld. De verschillende soorten industriën hebben verschillende kleuren. Klik voor de gedefinieerde kleuren op [toon industriën].
Depots: positie van depots in de simutranswereld. De kleuren duiden op verschillende typen. Schepen:geel; bus/truck:oranje; trein:rood; vliegtuigen:groen; trams:blauw; en monorail/maglev:grijs.

simutrans-124.5/simutrans/text/nl/monorailtools.txt000066400000000000000000000120761520675460500226560ustar00rootroot00000000000000Monorail/Maglev Tools Help (Nederlands)

Monorail/Maglev Tools

Het Monorail/Maglev Tools menu bevat tools om een monorail/maglev infrastructuur te bouwen. Deze tools kunnen de volgende onderdelen bouwen of slopen: monorail- en maglevbanen, seinen, bruggen, tunnels en stations. Indien de tijdlijn is geactiveerd in het spel, zal met de tijd het aantal beschikbare opties aan verandering onderhevig zijn.

Klik op het Monorail/Maglev icoon in het menu om het Monorail/Maglev Tools Menu te openen.

In het Monorail/Maglev Werktuigen Tools Menu kunnen, van links naar rechts, de volgende tools voorkomen:

Monorail/Maglev baan: bouwt de verschillende monorail-/maglevbaangedeeltes tussen twee punten. Een monorail- en maglevbaan kan alleen in in de richting van een helling gebouwd worden en kan niet door ruig terrein, door water en door andere obstakels gebouwd worden. Een nieuwe baan kan/mag gebruik maken van een al bestaand stuk baan dat eigendom is van dezelfde speler.
Klik om een baan te bouwen op een van de baanopties in het menu (de cursor verandert nu), klik dan in het spel-scherm op de plaats waar de baan moet beginnen en klik tenslotte in het spel-scherm op het eindpunt van de baan.
{TIPS: - verschillende baantypen kunnen gekoppeld worden door gebruik te maken van de koppelstukken uit het menu. - gebruik de Land ophogen/verlagen tool om het terrein aan te passen zodat het bouwen van een baan mogelijk wordt. - gebruik de Verwijder tool om obstakels te verwijderen.}

Verwijder monorails: verwijdert een, gedeelte van een, monorail-/maglevbaan. Er mogen geen voertuigen aanwezig zijn op het te verwijderen baangedeelte (stations en seinen op het baangedeelte worden wel verwijderd). Het gebruik van de tool kost geld.
Klik om een stuk baan te verwijderen op de Verwijder monorails tool; klik dan in het spel-scherm op de het eerste stuk baan dat verwijdert moet worden en tenslotte op het laatste stuk baan dat verwijderd moet worden. Het tussenliggende stuk baan (incl stations en seinen) wordt nu verwijderd.

Bruggen en koppelstukken: worden gebruit om bruggen/koppelingen te bouwen tussen twee monorail/maglevbanen. Bruggen hebben een maximum lengte, als de te overbruggen afstand te groot is zal geen brug gebouwd worden. Koppelingen worden gebruikt een standaard monorailbaan en een verhoogde monorailbaan met elkaar te verbinden.
Klik om een brug of koppelstuk te bouwen op de betreffende optie in het menu (de cursor verandert nu) en klik op een uiteinde van een monorail-/maglevbaan. Op dit punt wordt gestart met het bouwen van de brug/koppelstuk.
{TIPS: - gebruik de Verwijder tool om obstakels te verwijderen. - gebruik de monorail/maglevbaan opties om een los einde van een brug te verbinden met de monorail-/maglevbaan.}

Seinen worden gebruikt om de loop van monorail/maglevtreinen te controleren. Het werken met seinen wordt in uitgelegd in het hoofdstuk Seinen.

Remise (depot): bouwt een remise om monorail/maglev treinen te kopen en te beheren. Een remise heeft onderhoudskosten en wordt gebouwd op een uiteinde van een monorail-/maglevbaan.
Klik om een remise te bouwen op de remise optie in het menu (de cursor verandert in een remise) en klik op een uiteinde van een monorail-/maglevbaan.

Haltes: noodzakelijk om het in- en uitladen van passagiers, post en vracht mogelijk te maken. Er zijn verschillende haltes voor passagiers, post en vracht. Meestal wordt dit met behulp van kleine icoontjes aangegeven. De capaciteit voor passagiers, post en vracht is per halte verschillend. Een halte heeft onderhoudskosten en kan overal op de monorail-/maglevbaan gebouwd worden (behalve in bochten en kruisingen). Wanneer een halte niet direct naast een andere halte gebouwd wordt zal er een nieuwe halte gebouwd worden.
Klik om een halte te bouwen op de betreffende halte optie in het menu (de cursor verandert in een halte) en klik op de gewenste plaats op de monorail-/maglevbaan. Door meerdere haltes direct naast elkaar te plaatsen kan de capaciteit van de halte uitgebreid worden of kan de halte geschikt gemaakt worden voor meerdere soorten vervoer. Bijvoorbeeld passagiers en vracht.
{TIPS: - verwijder uitbreidingen met de verwijder tool. - druk op [v] om het dekkingsgebied van het station te tonen of te verbergen. - gebruik [Ctrl] om monorail-/maglevbanen te bouwen op bruggen of om verhoogde banen te bouwen boven een lage baan.}

Monorail fundament: cre-eert ?een uitbreiding voor een halte en vergroot de capaciteit van de halte. Monorail fundamenten worden gebouwd op het uiteinde van een monorail-/maglevbaan.
Klik om een fundament te bouwen op de fundament optie in het menu (de cursor verandert nu) en klik op een uiteinde van een monorail-/maglevbaan waar je het fundament wilt bouwen.

simutrans-124.5/simutrans/text/nl/mouse.txt000066400000000000000000000023741520675460500211050ustar00rootroot00000000000000Muisbediening Help (Nederlands)

Muisbediening Help

Muisbediening geeft informatie hoe met de muis om te gaan met schermen en het wijzigen van de grootte om lijsten beter in beeld te krijgen.

Hoe een schermgrootte te wijzigen in Simutrans: klik op rechteronderhoek van een scherm (gemarkeerd met horizontale strepen) waarvan je de grootte wilt wijzen en klik met de linkermuisknop en houdt deze ingedrukt. Sleep de muis over het scherm om de grootte aan te passen.

Richtingen:
Naar links: het scherm wordt smaller
Naar rechts: het scherm wordt breder
Naar boven: het scherm wordt kleiner, er worden minder regels getoond
Naar beneden: het scherm wordt groter, er worden meer regels getoond
Diagonaal: combineert de bovenstaande mogelijkheden, bijvoorbeeld diagonaal rechts naar beneden zal het scherm groter en breder maken.

Een aantal schermen heeft links en rechts van een bepaalde optie een horizontale pijl staan. Hiermee is het mogelijk om te bladeren in een lijst met waardes.
De linkerpijl zal naar de vorige of lagere waarde gaan
De rechterpijl zal naar de volgende of hogere waarde gaan

simutrans-124.5/simutrans/text/nl/signals.txt000066400000000000000000000054641520675460500214200ustar00rootroot00000000000000Seinen Help (Nederlands)

Seinen

Seinen worden gebruikt om de loop van treinen, trams en monorail/maglevs te controleren bij stations, kruisingen, bruggen etc. Ze verdelen het spoor in baanvakken. In een baanvak kan zich maar een trein tegelijk bevinden.

Er zijn verschillende soorten seinen:
- (normale) Seinen
- Voorseinen
- Keuzeseinen
Seinen kunnen in eenrichting- en tweerichtingmode gebouwd worden. In eenrichtingmode kan een sein maar in een richting gepasseerd worden. Let er bij het gebruik van eenrichtingseinen op dat de treinen hun bestemming wel kunnen bereiken. Houd in het begin een eenrichtingsbaanvak extra in de gaten.

Seinen: treinen passeren een normaal sein alleen als het baanvak tot het volgende sein of tot de bestemming (station/halte) niet bezet is door een andere trein.

Voorseinen: treinen passeren een voorsein alleen als de volgende twee baanvakken niet bezet zijn door een andere trein of als de volgende baanvakken tot de bestemming (station/halte) niet door een andere trein bezet zijn.

Keuzeseinen: sturen een trein naar een beschikbaar (leeg) perron. Bij een station met meerdere perrons mag een trein een willekeurig beschikbaar perron gebruiken. Als er geen beschikbaar perron is zal de trein bij het sein blijven wachten.

Bouw en positie: Seinen kunnen alleen op trein, tram of monorail/maglev banen gebouwd worden. Er kan maar een sein per veld zijn; het kortst mogelijke baanvak is dus twee velden lang. Neem voor het bepalen van de baanvakgrootte de lengte van de langste trein in velden en tel daar nog een veld bij op. Dit bevordert een soepele dienstregeling.

Klik om een sein te bouwen op de betreffende sein optie in het menu (de cursor verandert in een sein) en klik op de gewenste plaats op de baan. Er wordt nu aan beide zijden van de baan een sein geplaatst. Het sein kan nu in twee richtingen gepasseerd worden (tweerichtingenmode). Klik nogmaals op hetzelfde veld waar het sein geplaatst is en er zal een sein verdwijnen, en het sein kan alleen nog in de richting van het sein gepasseerd worden (eenrichtingmode). In het algemeen staan seinen in Simutrans aan rechter zijde van het spoor/baan. Bij de volgende klik wordt de toegestane richting omgedraaid; het sein staat aan de andere kant van het spoor/baan. Door hierna nog een keer te klikken kom je weer in de beginsituatie (tweerichtingenmode).

Werkzaamheden aan het spoor kun je door twee blokkerende seinen ongestoord uitvoeren. Na het aanpassen van een baanvak herstel je de seinen weer naar "normaal" bedrijf. Open dan de getroffen lijnen (via lijnManagement, Klik op "Klaar" waarna de route opnieuw wordt ingelezen en de treinen weer gaan rijden.

simutrans-124.5/simutrans/text/no.tab000066400000000000000000000321151520675460500177030ustar00rootroot00000000000000Norsk PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: no Norsk # # Encoding: ISO-8859-1 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable av cl_btn_filter_enable p cl_btn_filter_settings Innstillinger cl_btn_sort_asc Stigende cl_btn_sort_desc Synkende cl_btn_sort_id ID-nummer cl_btn_sort_income Inntekt cl_btn_sort_name Navn cl_btn_sort_type Type clf_btn_alle alle clf_btn_invers bytt clf_btn_keine ingen gl_btn_sort_bonus bonus hlf_btn_invers bytt hlf_btn_keine ingen Networks Nettverk #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic arktisk klima desert rkenklima mediterran middelhavsklima rocky alpint klima temperate temperert klima tropic tropisk klima tundra tundraklima #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Monorails are not available yet! Monorailer er ikke tilgjengelig enda! Not enough money! Ikke nok penger! Zughalt muss auf\nSchiene liegen!\n Togstasjoner maa\nplasseres paa togskinner.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Keyboard Help\n

Keyboard Help

\n Tastatur hjelp\n

Tastatur hjelp

\n

\nTastatur hjelp viser funksjonene til de forskjellige tastene p tastaturet.\n

\n

\nTastatur hjelp aapnes ved aa taste en ugyldig hurtigtast eller via Generell hjelp.\n

\n

\nStore og smaa bokstaver er forskjellige hurtigtaster (bruk [Shift] for store bokstaver).\n

\n

\nHurtigtaster inkluderer:\n

\n

\n[Piltaster]: flytter spillervindu i pilens retning.
\n[Backspace]: lukker alle vinduer, verktylinjer og hjelpetekster.
\n[Del], eller [Esc]: lukker oeverste windu, verktylinje eller hjelpetekst.
\n[Enter], eller [Return]: brukes for bekrefte handlinger.
\n[Pg-Up], eller [>]: zoom-inn spillervindu.
\n[Pg-Dn], eller [<]: zoom-ut spillervindu.\n[F1]: aapner Simutrans hjelp.

\n

\n[1]: flytter spillervindu mot soer.
\n[2]: flytter spillervindu mot soeroest.
\n[3]: flytter spillervindu mot oest.
\n[4]: flytter spillervindu mot soervest.
\n[6]: flytter spillervindu mot nordoest.
\n[7]: flytter spillervindu mot vest.
\n[8]: flytter spillervindu mot nordvest.
\n[9]: flytter spillervindu mot nord.\n

\n

\n[Shift] + mus: brukt paa Kart for aa se informasjon om Industriers leveranse-kjeder.
\n[CTRL] + verktoey: lager (signaler og Stasjoner) paa et hoere nivaa; eller bygger veier eller spor med lav hastighet over veier og spor med hy hastinghet; eller bygger rettere (mer direkte) veier og spor.
\n[CTRL] + ([F2] til [F12]): for aa legge til markert verktoey som hurtigtast p tastene [F2] til [F12].\n

\n

#____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s har naa\nbussforbindelse mellom\n%s og\nattraksjon\n%s\n(%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s har naa\nbussforbindelse mellom\n%s og\nfabrikk\n%s\n(%i,%i).\n Airline service by\n%s\nnow between\n%s \nand %s.\n N flyr %s mellom\n%s og %s!\n #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %ss har bygget nytt hovedkontor. #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (i depot) \nBauzeit bis til \nBauzeit von \nProduseres fra \nCan't open heightfield file.\n \nKan ikke aapne hoeydekart.\n \ndirection: \nretning: \nelektrified \nelektrifisert\n \nHeightfield has wrong image type.\n \nFeil filtype.\n \nis reserved by: \nreservert av tog: \nminimum speed: \nminimum hastighet: \nnot elektrified \nikke elektrifisert\n \nRibi (masked) \nretninger (tillatte) \nRibi (unmasked) \nretninger (alle): \nSet phases: \angi tidsintervall NS/V retning: \nsingle way \nenveisstrekning \nway1 reserved by \nvei 1 er reservert av \nway2 reserved by \nvei 2 er reservert av \nwith sign/signal\n \n med skilt/signal\n %d convois %d kjrety %d Einzelfahrzeuge im Depot %d kjoeretoey parkert her. %i km/h (max. %ikm/h) %i km/t (maks. %ikm/t) %i years %i months old. %i r %i mned(er) gammel. %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s\noppfoerte et nytt\nraadhus naar\ninnbyggertallet steg\n til over %i. %s\nis crowded. er overfyldt. 1WORLD_CHOOSE Innstillinger for Nytt spill: 2LIGHT_CHOOSE Farger: 2WORLD_CHOOSE Kart nummer: 5LIGHT_CHOOSE Fotgjengere p holdeplasser 5WORLD_CHOOSE Ant. byer: 6LIGHT_CHOOSE Fotgjengere i byer Abfrage Inspeksjonsverkty Abnehmer Forbruker About scenario Opphavsrett Abriss Rivningsverkty Absenken Senk jordniv Abspanntransformator Transformatorstasjon Accelerate time k spillhastighet Add forest Legg til skog add server Legg til server Add Stop Legg til Add stops for backward travel Legg til stopp p veien tilbake. aircraft_tab Frakt airplane fly Airport Flyplass AIRTOOLS Flyplassverkty All Alle Allow city growth Tillat ekspansjon av byer Allow player change Tillat spillerbytte allowed climates:\n Klimasoner: Alters a schedule. Endrer rute Angenommene Waren -- Varer som kan leveres anhaengen Heng p Anhaenger_tab Tilhengere Anheben Hev land Appends stops at the end of the schedule Legg til stopp p enden av ruta Apply Line Bruk rute Arbeiter aus: Arbeidere bor i: Arrivals from\n Ankommende fra\n Arrived Ankommet Assets vrige aktiva Aufspanntransformator Transformatorstasjon Autohalt muss auf\nStrasse liegen!\n Buss- og lastebilholdeplasser m ligge p en vei.\n Available Tilgjengelig Bahndepot Lokstall/Vognhall Bankrott:\n\nDu bist bankrott.\n Konkurs:\n\nDu er konkurs!\n battery Batteri Baum Tre baum builder Plant trr Baustelle Byggeplass Beenden Avslutte Beginner mode Begynnermodus Besonderes Gebaeude Turistattraksjon BF stasjon bio biologisk Blockstrecke ist\nbelegt\n Linjeblokk er\nopptatt\nav et annet tog\n Boden Land Bonusspeed: %i km/h Maks. mulige hastighet: %i km/t bridge is too high for its type! Dalen er for dyp for \ndenne type bro!\n Bridge is too long for this type!\n Spennet blir for\nlangt for denne brotypen!\n Bruecke Bro Bruecke muss an\neinfachem\nHang beginnen!\n Broer m\nbegynne p\nrette bakker!\n Brueckenboden bro Build air depot Bygg flyhangar build choosesignals Innfartssignaler Build city market Bygg ny butikk Build drain Bygg drenering build HQ Bygg hovedkontor Build land consumer Bygg kraftverk Build maglev depot Vognhall for Maglev Build monorail depot Monorailvognhall Build narrowgauge depot Vognhall for smalsporet jernbane Build powerline Bygg Kraftlinje Build road depot Verkstedhall Build ship depot Skipsverft Build signals Bygg signaler Build train depot Bygg lokstall/vognhall Build tram depot Bygg trikkestall/bybanevognhall Build truck depot Bygg verkstedhall Building costs estimates Estimert byggekostnad Buildings Ant. bygninger Built artifical slopes Lag bakker Built random attraction Bygg en vilkrlig turistattraksjon Bus_tab Busser Can only move from halt to halt or waypoint to waypoint. Kan bare flytte \nfra holdeplass til holdeplass eller \nfra veipunkt til veipunkt. Cancel Avbryt Capacity: Kapasitet: Capacity: %s\nLoad: %d (%d%%) Kapasitet: %s\n? %d (%d%%) Cash Penger Change player Bytt spiller chooses a random map Velg et tilfeldig kart. cl_txt_sort Sorter etter: clf_chk_aircrafts Fly clf_chk_noincome mangler inntekt clf_chk_ships Skip clf_chk_trains Tog closed Stengt Configure AI Konfigurer KI Constructed by Tegnet av Constructed by %s Tegnet av %s Construction_Btn Byggekostnader Costs Koster December Desember Destroying map ... delegger kart... Details Detaljer Dock Havn Dock must be built on single slope! Havnen m bli bygget p skr-bakke Electricity Elektrisitet Enter Password Skriv inn passord Error Feil Fabrikname Fabrikknavn follow me Flg meg. Found new city Lag en ny by go home Kjr til depot Help Hjelp January Januar join game Bli med i spill July Juli June Juni Kann Spielstand\nnicht laden.\n Kan ikke laste lagret spill! Kein Besitzer\n Eksisterer ikke! keine ingen Keyboard_Help\n Tastaturhjelp\n koord koordinater Lade Relief Last hydekart Last Year Siste r Legend Legende Line Linje Load game Last inn lagret spill Load scenario Last inn Scenario loaded lastet inn loaded passenger/freight Sorter pass./gods etter: Loading addon paks ... Laster tilleggspakke (add-on)... Loading map ... Kartet lastes... Loading paks ... Leser inn pak... Mailbox Mailboks max Max Months Mneder Mountain height Hyde p fjell: Name Navn Neue Karte Nytt kart Neue Welt Generer Nytt spill New Vehicles Nytt kjrety no load ingen last Number of rivers Antall elver Object Objekt open pen Optionen Innstillinger Ownership Eierskap Password Passord player spiller player 0 Spiller 0 player 1 Spiller 1 player 10 Spiller 10 player 11 Spiller 11 player 12 Spiller 12 player 13 Spiller 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 7 Spiller 7 player 8 Spiller 8 player 9 Spiller 9 Power Kraft random tilfeldig Random age tilfeldig aar Random map Tilfeldig kart Rathaus Raadhus Reliefkarte Kart Remove Fjern remove powerlines Fjerne kraftlinje remove roads fjerne veier road vei Rotate map Roter kart sail Seil Saving map ... Lagrer kart ... Schienentunnel Bygg togtunnel Schiff_tab Skip Schiffdepot Skipsverft Sehenswuerdigkeit Turistattraksjon SEP_FRACTION . SEP_THOUSAND Setting Innstillinger Ship Skip Show all Vis alle show all building Vis alle bygninger Show industry Vis industri show station coverage Vis dekningsomraade for stasjon show station names vis navn paa stasjoner Show/hide statistics Vis/skjul statistikk Size (%d MB): Stoerrelse (%d MB): Sound Lyd Sound settings Innstillinger for lyd Speichern Lagre Spieler Spiller Spieler(mz) Spillere Spielerliste Spillerliste Spielstand wurde\ngeladen!\n \nInnlasting av spill vellykket.\n Spielstand wurde\ngespeichert!\n \nLagring av spill vellykket.\n Sprache Sprk Sprachen Sprk Starte Spiel Start spill steam damp Storage capacity Lagringskapasitet sued Soer suedost Soeroest suedwest Soervest Suppliers Leverandrer Tage alt dager gammel. This Month Denne mnd This Year I r: tl_title Byoversikt Towns Byer Traffic Trafikk Train Tog Translation Oversettelse transparent station coverage gjennomsiktig dekningsomraade Unemployed Arbeidsloes units/day /mnd, upgrade HQ Oppgradere HK Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Gjeld maa tilbakebetales\n innen %d mnd.\n Waggon_tab Biler waiting venter Wert Verdi west Vest Year %i has started. Aar %i har startet. Years Aar #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL by &1_CITY_SYLL stad &4_CITY_SYLL berg &5_CITY_SYLL berg &6_CITY_SYLL dal &7_CITY_SYLL fjell &8_CITY_SYLL foss &9_CITY_SYLL sj &A_CITY_SYLL vann &B_CITY_SYLL sand &C_CITY_SYLL hei &D_CITY_SYLL vik &E_CITY_SYLL s %1_CITY_SYLL Lunde %2_CITY_SYLL Mos %3_CITY_SYLL Nord %4_CITY_SYLL Vest %5_CITY_SYLL Sr %6_CITY_SYLL st %7_CITY_SYLL Skog %8_CITY_SYLL Kristian %9_CITY_SYLL Haug %A_CITY_SYLL Viks %B_CITY_SYLL Ask %D_CITY_SYLL Fjell %E_CITY_SYLL Tran %F_CITY_SYLL Gran simutrans-124.5/simutrans/text/ol.tab000066400000000000000000001554471520675460500177170ustar00rootroot00000000000000§Livvinkarjal PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: olo Livvinkarjal # # Encoding: UTF-8 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Näytä kuun aijat dd:hh:mm-muvvos AMBIENT_SOUND Ymbaristön iänet: CASH_SOUND Rahan kilahtus: cl_btn_filter_disable Iäre piäl cl_btn_filter_enable Piäl cl_btn_filter_settings Laitokset cl_btn_sort_asc Nouzeva cl_btn_sort_desc Laskeva cl_btn_sort_id Syvainp. ID cl_btn_sort_income Tulot cl_btn_sort_name Nimi cl_btn_sort_type Tiippu clf_btn_alle kaikin clf_btn_invers inv. clf_btn_keine ei nimidä climate area percentage Ilmastopalstoin suurus: CROSSING_SOUND Tazorist. varoitusiänet: FACTORY_SOUND Zavodoin iänet: Find matching convois Eči luajivie kulgunevvoloi gl_btn_sort_bonus Bonus gl_btn_sort_name Nimi gl_btn_sort_revenue Bizn. oborottu gl_btn_unsort Ei nimidä height based Ilmaston mugaine hl_btn_filter_disable Iäre piäl hl_btn_filter_enable Piäl hl_btn_filter_settings Laitokset hl_btn_sort_asc Nouzeva hl_btn_sort_desc Laskeva hl_btn_sort_name Nimi hl_btn_sort_type Tiippu hl_btn_sort_waiting Vuottau hlf_btn_alle kaikin hlf_btn_invers inv. hlf_btn_keine ei nimidä humidities Kostevusrajat: Install Azeta pakiettusarjoi Lake Järvi Lake height Järven enimäiskorgevus: Maximize height levels Laita korgevustazot maksimumih moisture land Nosta muan yl: moisture water Nosta vien yl: Networks Liiniiverko Num pad keys always move map Älä käytä numpad-kluaviššoi kartan liikuttamizeh Open Sea Meri Queueing Vuottajien muutos rainfall Kostevuon mugah Reselect closes tools Ruadoinstr:n valličuksen iäreottuamine uuvessahvalličuksel Road toll Tiemaksut Scenario Tieduo skenuariospäi sea meri Single GUI Yksil. käyttöl. Sound range: Iänen aloveh: Start this as a server Pane serveru käymäh temperature borders Lämbörajat: temperature-humidity based Temperatuuran/kostevuon mugaine TOOL_SOUND Ruadoinstr. iänet: TRAFFIC_SOUND Likendehen iänet: Transfers Vaihtoi #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic arktilline desert kuivikko mediterran välimerelline rocky alppi-ilmasto temperate lauhkei tropic tropiičeskoi tundra tundru #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Skenuariošriftan täyttiämine ei mennyh hyvin. Bridge blocked by way below or above. Sildu on typitty ylähän- libo alahanpäi. Can't buy obsolete vehicles! Et voi ostua tuotandospäi otettuloi kulgunevvomalliloi. Cannot alter water Tädä basseinua ei voi tyhjendäy libo täyttäy enämbäh. Cannot build on a double slope! Objektan voi srojie vaiku yksittäismägeh libo tazazeh muah. Cannot built depot here! Depoa ei voi srojie täh. Cannot built this station/building\nin underground mode here. Tädä azemua/srojua ei voi srojie muan alle. Cannot create generic line!\nSelect line type by\nusing filter tabs. Ei voitu luadie ylehispäi liiniedy! Valličče liinin tiippu enzin. Cannot create socket Yhtevyön luadimine ei mennyh hyvin. (ei voitu luadie socketoa) Cannot rotate this building! Tädä srojua ei voi kiändäy. Client closed connection during transfer Klijentu katkai yhtevyön tiijonsiirron aiguna. Convoi handles exhausted! Kulgunevvoloin enimäismiäry tavattih. Convoy already deleted! Kulgunevvo on jo otettu iäre. Could not open file Tiijostuo ei voitu avata. Das Feld gehoert\neinem anderen Spieler\n Plitku on toizen kižuajan omistukses. Der Besitzer erlaubt das Entfernen nicht Omistai ei ole andanuh lubua objektan ottamizeh iäre. Diese Zusammenstellung kann nicht fahren!\n Tädä yhtistelmiä ei voi käyttäy. Flugzeughalt muss auf\nRunway liegen!\n Seizondupaikan tulou olla ruloamisdorogal. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Lendokentysrojua ei voi srojie täh. Hier kann kein\nSignal aufge-\nstellt werden!\n Likendehenohjavuslaitetta ei voi panna täh. In order to lock the game, you have to protect the public player by password! Sinun pidäy suojata julgine kižuaju peittosanal, ga voit lukita kižan. Loading a new game will end the current server session! Uvven kižan täyttiämine loppie nygözen serveruistundon, oletgo varmu? Lost connection\nto server! Yhtevys serverale menetettih. Lost synchronisation\nwith server. Sinhronizatsii serverale menetettih. Maglevhalt muss auf\nMaglevschiene liegen!\n Maglev-azetus tulou panna maglev-raudutiele. Monorailhalt muss auf\nMonorail liegen!\n Yksiraizivoazetus tulou panna yksiraizivotiele. Monorails are not available yet! Yksiraizivotiet ei ole vie suadavannu. More than one possibility to build this dock found. Portan srojimizeh löydittih muidagih taboi. Valličče taba valličemal portu uuvessah Ctrl+klikil. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Kaiduraizivoazetus tulou panna kaiduraizivotiele. No bridges over runways! Sildoi ei voi srojie lennätysdorogoin yli. No curves on runways Lennätysdorogat ei voi ruadua kiännälmyksii. No suitable crossing Sobijua ristavustu ei löytynyh. (enimäisvauhti liijan suuri?) No suitable way on the ground! Azetukset voi panna vaiku tazazele muale, oigien luaduzele da suorale tiele. No through station here! Azemat tulou panna tien piäteheh libo suorale vuitile, mis ei ole ristavuksii. Not enough bytes transferred Tavuloi ei siirretty tävveliste. Not enough clearance. Kahten tien välil pidäy olla vähimytteh kahten yksikön korgevusero. Not enough money! Sinul ei ole tävveliste rahua tämän ostamizeh. On narrowgauge track only!\n Tämä meneh hyvin vaiku kaiduraizivotiel. Only public player can lock games! Vaiku julgine kižuaju voi lukita kižoi. Only up and down movement in the underground! Muanalaistu plitkua voi vaiku heittäy ylembäkse libo alembakse tarvittavien ruadoinstrumentoin kel. Out of funds Sinul ei ole tävveliste rahua jällel täh. Post muss neben\nHaltestelle\nliegen!\n Levendämissroju (ezim. poštutoimisto, varasto) tulou srojie azetuksien/azemien rinnale. Protocoll error (expecting game) Protokološipku (vuotettih kižua) Schiffhalt muss im\nWasser liegen!\n Laivuazetuksen voi panna vaiku veteh, portan viereh. Server busy Serveru on tungokselline. Server version too new Serveru käyttäy uvvembua versieda kižas. Show all revenue messages Näytä kaikin oborottuviestit Show no revenue messages Älä näytä oborottuviestilöi Show only player's revenue Näytä vaiku kižuajan oborottu Terraforming not possible\nhere in underground view Muata ei voi ruadua muan al. This tunnel branches. You can try Control+Click to remove. Tunnelil on enämbän kui kaksi sydämehpiäzyä. Ottuakses tunnelin iäre, käytä Ctrl+klikkuaustu. Too far away to merge stations! Azemat on liijan loitton toizispäih. Upgrade must have\na higher level Päivityksen tulou olla korgiemboa tazoa. Vehicle %s cannot choose because stop too short! Kulgunevvo %s ei voi vallita pristania gu azetus on liijan lyhyt. Zughalt muss auf\nSchiene liegen!\n Raudutieazemat voi srojie vaiku raudutiele. #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Ohjavo

Opasluvettelo

Tieduo Simutransispäi

%1$s

Käyttö

%2$s

Kižan alguamine

%4$s

Kižuamine

%5$s

Ruadoinstrumentat

%3$s

Muut dialougat

%6$s Keyboard Help\n

Keyboard Help

\n Klaviatuuran käyttö

Klaviatuuran käyttö

Klaviatuuran käyttöohjavo avavuu painamal kluaviššua, mi ei ole muus käytös, libo ylehizen ohjavon kauti.

Kluaviššukomandat huomavoi kirjainsuuruon (käytä [Shift]-kluaviššua suajakses suuret kirjaimet).

Tulieloil kluaviššukomandil on miäritelty tietyt funksiet:

[nuolikluaviššut]: vieretä kižanägymiä nuolen suundah.
[askeltyöndin]: salbua kaikin ikkunat da ruadoinstrumentupalkat.
[Delete], libo [Escape]: salbua ylin ikkun/ruadoinstrumentupalkku.
[Page Up], libo [>]: lähendä kižanägymiä.
[Page Down], libo [<]: loitonda kižanägymiä.
[Ctrl] + ruadoinstumentu: sroji suorembii tielöi (daže boikoimbien tielöin yli) libo muuta valličuskundo inversiellizekse kyzelyn libo iäreottuamizen aiguna.

At least one stop is connected to the town. Yhtelläh yksi azetus on liitätty linnah. At least one tile is connected to one stop. Yhtelläh yksi plitku on liitätty yhteh azetukseh. Color according to transport capacity left Näytä värit jällel olijale kulletussijan miäräle Color-coded terrain according to altitude. Näytä alovehen muvvot Decrease water height Alenda vientazuo Highlight railroad tracks Näytä raudutiet Highlite depots Näytä depot Highlite electrical transmission lines Näytä sähkönsiirdoliiniit Highlite factories Näytä zavodat Highlite forests Näytä meččät Highlite tourist attraction Näytä turistunähtävykset Increase water height Nosta vientazuo Open station/stop details Näytä azeman/azetuksen tiijot Overlay city limits Näytä linnoin rajat Overlay passenger destinations when a town window is open Näytä passažiiroin piämiärät linnan tiedoikkunan olles avvoi Overlay schedules/network Näytä reitit da liinit Overlay town names Näytä linnoin nimet Please click on the map to add\nwaypoints or stops to this\nschedule. Klikkua kartua lizätäkses välietuappoi libo azetuksii reitile. Set tile climate %s Laita muan ilmastokse %s Show capacity and if halt is overcrowded Näytä azetuksen sijan miäry da tila Show how many convoi reach a station Näytä azetuksele piäzejjen kulgunevvoloin miäry Show how many people/much is waiting at halts Näytä vuottavien passažiiroin miäry Show initial passenger departure Näytä passažiiroin da poštan lähtöčökehet Show level of city buildings Näytä linnusrojuloin tazo Show mail service coverage/mail network Näytä poštupalvelan kattavus/verko Show passenger coverage/passenger network Näytä hengilölikendehen kattavus/verko Show speedlimit of ways Näytä tielöin enimäisvauhtit Show the change of waiting at halts Näytä vuottajien miärän muutos viime kuuh verduttuna Show the owenership of infrastructure Näytä infrastruktuuran omistavus Show transported freight/freight network Näytä vedolikendehverko Show usage of network Näytä kulgunevvoloin miäry per plitku da kuu Shows a listing with all industries on the map. Näytä luvettelo kaikispäi tevollizusaloispäi kartal Sum of departure/arrivals at halts Näytä tullejjen da lähtenejjen kulgunevvoloin miäry #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s ajeleh nygöi avtobussuliiniedy välil %s - %s (%i,%i). %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s ajeleh nygöi avtobussuliiniedy välil %s - %s (%i,%i). %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). Yhtivön %s %i rekkua huolehtii kulletuksispäi välil %s (%i,%i) - %s (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s avas uvven raudutien välile %s (%i,%i) - %s (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n %s lendäy nygöi välil %s - %s. Ferry service by\n%s\nnow between\n%s \nand %s.\n %s ajeleh nygöi lauttua välil %s - %s. Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s ajeleh nygöi loittolikendehen avtobussuliiniedy välil %s - %s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Kartan kohendelemine LISTTOOLS Luvettelot MAGLEVTOOLS Maglev-raudutiet MONORAILTOOLS Yksiraizivotiet NARROWGAUGETOOLS Kaiduraizivotiet RAILTOOLS Raudutiet ROADTOOLS Dorogat SHIPTOOLS Laivat SLOPETOOLS Muan kohendelemine SPECIALTOOLS Spetsial'noit ruadoinstrumentat TRAMTOOLS Tramvait #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s on srojinuh uvven piäkantoran. Factory chain extended\nfor %s near\n%s built with\n%i factories. Talovuskazvo raveneh: %s levendäy omua tuotanduo linnan %s alovehel. %i uuttu zavodua perustettih. Net wealth less than 10%% of starting capital! Nettovarakkahus on alle 10%% algupiäomazuospäi! New %s now available:\n%s\n Uuzi %s nygöi suadavannu:\n%s New factory chain\nfor %s near\n%s built with\n%i factories. Uuzi %s on välifirmoineh avuattu linnan %s lähäl. %i uuttu zavodua srojittih. New vehicle now available:\n%s\n Uuzi kulgunevvo nygöi suadavannu:\n%s Now %u clients connected. Täl kodvazel %u klijentua on yhtistetty. overtaking menöy siiriči Remove vehicle from map. Use with care! Ota kulgunevvo iäre kartalpäi. (Käytä vardoičen!) Screenshot\ngespeichert.\n Kuvakuabavus tallendettu. Sends the convoi to the last depot it departed from! Työnnä kulgunevvo viimezimbäh depoh mispäi se on lähtenyh. Server preparing game ... Serveru valmisteleh kižua... Trees Puut: With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s sroji uvven mustomerkin. %i eläjiä vietti vällypäiviä sen kunnivokse. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Liiniifiltru #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%.2f$/km %.2f$/m) (%.2f$/km %.2f$/kuu) (%.2f$/km) (%.2f$/km) (%i)- (%i) (in depot) (depos) [0] south-facing [0] suven suundaine [1] east-facing [1] päivännouzun suundaine [2] north-facing [2] pohjaizen suundaine [3] west-facing [3] päivänlaskun suundaine [4] southeast corner [4] liidehine kulmu [5] northeast corner [5] koilline kulmu [6] northwest corner [6] luodehine kulmu [7] southwest corner [7] lounaine kulmu \nBauzeit bis vuoteh \nBauzeit von \nValmistettu vuodespäi \nCan't open heightfield file.\n Korgevuskuvua ei voitu avata. \ndirection: \nSuunnat: \nelektrified \nSähköstetty\n \nHeightfield has wrong image type.\n Korgevuskuva on viäränluaduine. \nis reserved by: \nVuitil on juna \nminimum speed: \nVähimäisvauhti: \nnot elektrified \nEi sähköstetty\n \nRibi (masked) \nSuunnat\m (masku): \nRibi (unmasked) \nSuunnat\n (ei maskia): \nSet phases: \nReguliirui aiguvälilöi:\n p.-s. -> pl.-pn. -> siirros \nsingle way \nYksisuundaine \nway1 reserved by \nTiedy 1 käyttäy \nway2 reserved by \nTiedy 2 käyttäy \nwith sign/signal\n \nMerki-/valo-ozuttimel\n %d buildings\n %d srojua\n %d convois %d kulgunevvuo %d Einzelfahrzeuge im Depot %d kulgunevvuo depos %i car(s), %i vagonua, %i km/h (max. %ikm/h) %i km/h (max. %i km/h) %i years %i months old. Igä: %i vuottu da %i kuudu %s at (%i,%i) now public stop. Azetus %s on muuttunuh julgizekse vaihtopaikakse. %s building %s %s %s %s %s %s city %d %s %s %d %s %s factory %s %s %s %s kenty %s has entered a depot. Kulgunevvo %s on tulluh depoh. %s land %d %s %s %d kyläaloveh %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s sroji uvven linnantaloin, ku se tavatti %i eläjän rajan. %s\nis crowded. Azetus %s on tungokselline. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nYlen suuri vauhti %3$i km/h\n\nNygöine vauhti %2$i km/h.\n\n %s\nwas liquidated. %s on mennyh maksukyvyttomäkse. Kaikin ruado loppiettih, da yhtivön omazus myötih. %u Client(s)\n %u klijentu(a)\n %u Player (%u locked)\n %u kižuaj(u)a (%u lukittu(u))\n

Error

Ošipku: Monda yhtenluaduistu objektua pakiettusarjan piäeččomistos

Koht(i/a)s

Warning

addons for

Ošipku: Monda yhtenluaduistu objektua lizäozan eččomistos

Lizäozis koht(a/i)le 1 convoi 1 kulgunevvo 1 Einzelfahrzeug im Depot 1 kulgunevvo depos 100 km/h = %i tiles/month 100 km/h = %i plitkua/kuu 1LIGHT_CHOOSE Kirkavus: 1WORLD_CHOOSE Uvven kižan laitokset: 2LIGHT_CHOOSE Värit: 2WORLD_CHOOSE Kartan noumer: 3LIGHT_CHOOSE Vieretysvauhti: 4LIGHT_CHOOSE Inversiellizet vieretyssuunnat 5LIGHT_CHOOSE Kävelijöi azetuksil 5WORLD_CHOOSE Linnoin lugumiäry: 6LIGHT_CHOOSE Kävelijöi linnois 6WORLD_CHOOSE Likendehen tihevys: 8WORLD_CHOOSE Päivy/yö-kierdo A bridge must end on a way! Sillan tulou loppie tiele. A bridge must start on a way! Libo et omista sillan mollembii piälöi, libo reiti on typitty. Abfrage Tutki Abnehmer Kuluttai: About Tieduo About scenario Avtoranvallat Abriss Hävitä/ota iäre Absenken Heitä muata alembakse Abspanntransformator Transformuator Accelerate time Boikonda aigua Act. load: %u MW\n Akt. takku: %u MW\n Active player only Vaiku aktiivine kižuaju Add forest Liziä meččiä Add random citycar Liziä piävyndähine hengilöavto add server Liziä serveru Add Stop Liziä azetus Add stops for backward travel Liziä azetuksii tulomatkale. air Lennätys-/ruloamisdorogu Aircraft Lendoliinii aircraft_tab Gruuzulendokonehet airplane lendokoneh Airport lendokenty AIRTOOLS Lendokentät All Kaikin all convoi tooltips Kaikin kulgunevvon tiijot Allow city growth Anna luba linnan kazvole Allow player change Anna luba kižuajan vaihtamizele allowed climates:\n Luvallizet ilmastot:\n Alters a schedule. Muuta reitii Angenommene Waren Lähäl olijoin yrityksien tarvičemat tavarat anhaengen Liziä Anhaenger_tab Pritsepat Anheben Heitä muata ylembäkse Appends stops at the end of the schedule Liziä azetus reitin loppuh Apply Line Laita reitile Apr. sulakuu April sulakuu Arbeiter aus: Ruadajat eläh linnois: Arrivals from\n Tuliet:\n Arrived Tulluh Assets Kulgunevvoloin arvo Aufloesen Riiči Aufspanntransformator Transformuator Aug. elokuu August elokuu auto avtomuatilline Autohalt muss auf\nStrasse liegen!\n Avtobussu-/gruzoviekkuazetukset tulou panna dorogan piäle. Available Suadavannu Available at custom date Suadav. tietys pvm:s algajen Bahndepot Veduridepo Bankrott:\n\nDu bist bankrott.\n Yhtivösi on mennyh maksukyvyttömäkse. battery Akkumul'uatoru Baum Puu baum builder Istuta puuloi Baustelle Srojuruadopaikku Bauzeit Srojuamisaigu Beenden Lope Beginner mode Algaitila Besonderes Gebaeude Turistunähtävys BF azemu bio Biolougine Blockstrecke ist\nbelegt\n Raudutieoza on toizen junan käytös. Boden Mua Bonus Multiplier: %i%% Bonuskerduaju: %i%% Bonusspeed: %i km/h Korgein mahtolline vauhti: %i km/h Boost (%%) Lizävys (%%) Borderless (disabled on fullscreen) Reunatoi kogo näyttö (ei käytettävänny oigies tilas) bridge is too high for its type! Sildu on liijan korgei sen tiipule. Bridge is too long for this type!\n Tugiväli on liijan pitky sillan tiipule. Bruecke Sildu Bruecke muss an\neinfachem\nHang beginnen!\n Sillat tulou algua suoraspäi mäespäi. Brueckenboden sildu Build air depot Sroji hanguaru build choosesignals Sroji pristaninvalličusozutin Build city market Sroji uuzi kauppu lähimbäh linnah Build drain Transformuatorazemu build HQ Sroji piäkantor Build land consumer Sroji uuzi sähkölaitos Build maglev depot Sroji maglev-depo Build monorail depot Sroji yksiraizivodepo Build narrowgauge depot Sroji kaiduraizivodepo Build powerline Sroji sähkönsiirdoliinielöi Build presignals Sroji kahtenaine suojastusozutin Build road depot Sroji garažu Build ship depot Sroji dokku Build signals Sroji ozuttimii Build train depot Sroji veduridepo Build tram depot Sroji tramvaidepo Build truck depot Sroji garažu Building costs estimates hinnoitus Buildings Srojuloi Built artifical slopes Sroji keinotegozii rindehii Built random attraction Sroji piävyndähine turistunähtävys Bus_tab Avtobusat Can be overgrown Sua olla ylikazvanuh Can only move from halt to halt or waypoint to waypoint. Kohtu voijah siirdäy vaiku azetukselpäi libo välietuapalpäi toizele. Cancel Peruuta Cannot connect to offline server! Serveru on iäre piäl, eigä se ole muga tavattavannu. Capacity Tilavus Capacity: Tilavus: Capacity: %.0f MW Tilavus: %.0f MW\n Capacity: %d%s %s\n Tilavus: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Tilavus: %s\n\nGruuzu: %d (%d%%) Cars are not available yet! Avtot ei ole vie suadavannu. cars.\nstate kulgunevvuo\n Cash Čotan sal'do Change player Vaihta kižuajua Chart Statistiekat Chat_msg Čätty Choose direction Valličče suundu Choose operation executed on clicking stored/new vehicles Valličče funksii mi ajetah ku säilytettyy/uuttu kulgunevvuo klikatah. chooses a random map Luaji piävyndähine kartu citicens Eläilugu City attraction Linnunähtävys City industries Kaupat linnois City list Linnuluvettelo City size Eläjii city_road Piha citybuilding builder Sroji linnusrojuloi CityLimit Linnoin rajat cl_title Kulgunevvoluvettelo cl_txt_sort Lajitteluperusteh: clamp Rajoita arvoalovehta Cleanup schedule Ota toistuvat kohtat iäre Clear block reservation Näytä/nol'ua tielöin tilavuksii clf_chk_aircrafts Lendokonehet clf_chk_cars Avtobusat/gruzoviekat clf_chk_indepot Depos clf_chk_maglev Maglev-junat clf_chk_monorail Yksiraizivojunat clf_chk_name_filter Filtrui nimet: clf_chk_narrowgauge Kaiduraizivojunat clf_chk_noincome Luadiu hävivyö clf_chk_noline Ei liiniedy clf_chk_noroute Ei reitii clf_chk_noschedule Ei azetuksii clf_chk_obsolete Ei tuotandos clf_chk_ships Laivat clf_chk_spezial_filter Spetsial'noi filtru clf_chk_stucked Juuttunuh clf_chk_trains Junat clf_chk_trams Tramvait clf_chk_type_filter Filtrui tiiput: clf_chk_waren Filtrui tavarat clf_title Kulgunevvoluvettelon filtruičus Climate Ilmasto Climate Control Landšaftan laitokset closed salvattu. Closes topmost line window when new line selected. Salbuau ylimän ikkunan uuttu liiniedy vallites COLOR_CHOOSE\n Valličče väriteemu al\nolijois paletoispäi: Company bankrupt Yhtivö on häviendäs Company_msg Kilbailijat Comparing pak files ... Verdaillah pak-tiijostoloi... Configure AI Muuta luadivoälyy Configure AI setttings Muuta luadivoälyn laitoksii Congratulation\nScenario was complete in\n%i months %i years. Hyvittelyt! Piäzit läbi skenuariospäi %i kuus da %i vuvves! Connected stops Yhtistetyt azetukset: Connected with server Yhtistetty serverale Connections Yhtevykset Constructed by Mualuaju: Constructed by %s Mualuaju: %s construction speed Srojuamisvauhti Construction_Btn Srojuamismaksut Consumed Kulutettu contains the following doubled objects:

tuliet objektat toistuu vähimiten kerran:

convoi %d of %d Kulgunevvo %d/%d convoi error tooltips Kulgunevvoloin ošipkutiijot Convoi following mode Kulgunevvoloin tarkailutila: Convoi has been sent\nto the nearest depot\nof appropriate type.\n Kulgunevvo on työnnetty lähimbäh sobijah depoh. Convoi is sold when all wagons are empty. Kulgunevvo myvväh hedi, ku se on kogonah tyhjy. convoi mouseover tooltips Kulgunevvon tiijot hiirel convoi passed last\nmonth %i\n \nKulgunevvoloi viime kuus: %i\n Convois Kulgunevvot Convois: %d\nProfit: %s Kulgunevvoloi: %d\nTulos: %s Convoys Kulgunevvoloi Copy Convoi Kopiirui kulgunevvo Copy the selected convoi and its schedule or line Kopiirui vallittu kulgunevvo da sen reiti Copy to clipboard Kopiirui leikehstolale Cost Maksut cost for removal Iäreottuamismaksut Cost per unit Maksut per yksikkö Cost: %8s (%.2f$/km %.2f$/m)\n Maksut: %s (%.2f$/km %.2f$/kuu)\n Cost: %8s (%.2f$/km)\n Maksut: %8s (%.2f$/km)\n Costs Maksut Create a new line based on this schedule Luaji uuzi liinii, mi pohjavuu täh reitih curiosity builder Nähtävyssrojii curlist_title Nähtävysluvettelo Currently playing: Nygöi soi: Customers live in: Aziiniekat eläh linnois: Date format Päivymiärymuodo: deactivated in online mode Ei aktiivine nettukižas Dec. talvikuu Deccelerate time Hillendä aigua December talvikuu decrease underground view level Alenda muanalazen nägymän tazoa Del Stop Ota iäre Delete Line Ota liinii iäre Delete the current stop Ota nygöine azetus iäre Delete the selected line (if without associated convois). Ota vallittu liinii iäre (ku kulgunevvoloi ei ole liitätty) Delete this file. Ota tämä tiijosto iäre Delivered Työnnetty Demand Tarveh Demand: %.0f MW Tarveh: %.0f MW\n Denkmal Monumentu Departed Lähtenyh Departs at Lähtöy (dd:hh:mm): Departure after %-gruuzu libo vuottamisaigu (dd-hh-mm) Departure board Lähtöaijat Departures per month Lähtöt/kuu: Departures to\n Lähtevät:\n Depots Depot Der Tunnel ist nicht frei!\n Tunneli ei ole vie tyhjy. Destination Tuliet Destroying map ... Hävitetäh vahnua kartua... Details Lizätiijot Die Bruecke ist nicht frei!\n Sildu ei ole vie välly. diesel Diizeli directmail Lehtizet Direkt erreichbare Haltestellen Suorat yhtevykset tiä azemalpäi disable midi Nemoita muuzikku Display bars above factory to show the status Näytä zavodan piäl palkkoi, mit näyttäy sen tilan Display settings Näytön laitokset Distance Matku distributing cities Sijoitetah linnoi... distributing factories Sijoitetah zavodoi... Do not show Älä näytä Dock portu Dock must be built on single slope! Portat tulou srojie yhten yksikön korgevuoh mägeh. Downloading Täytetäh dp_title Depoluvettelo Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Sinul on %d kuudu aigua maksua velgas iäre. Durchsatz Max. Economy Talovus da linnat Eigenbesitz\n Julgine omazus\n Ein %s\npasst hier nicht.\n '%s' ei sovi täh. Einstellungen Laitokset Einstellungen aendern Laitokset electric Sähkö Electricity Sähkö Electricity producer\n\n Sähkönluoju\n\n Electrics_tab Sähkö Electrify track Sähköstä raudutie enlarge map Suurenda kartua enter a value between %i and %i Syötä arvo %i:n da %i:n välilpäi Enter address Syötä adressi Enter Password Muuta kižuajan nimie da peittosanua Error Ošipku Erzeuge neue Karte.\n Uuttu kartua luajitah, vuota kodvaine.\n\n(Tämä voi kestäy erähän minuutin suuril kartoil.) Es wird bereits\nein Fahrplan\neingegeben\n Reitii planiiruijah parahite. Suata se enzin loppuh enne kui luajit sen uvvessah. Extract files Purretah tiijostoloi... Fabrikanschluss Yhtistetyt zavodat Fabrikname Zavodan nimi Factories Zavodoi factory details Zavodan yhtevykset factorybuilder Zavodusrojii Fahrplan Reiti Fahrtziel Miäränpiä: Fahrzeuge koennen so nicht entfernt werden Kulgunevvuo ei voi ottua iäre nenga. Fahrzeuge: Kulgunevvot: Farbe Kižuajan värit Fast forward Boikonda aigua Feb. tuhukuu February tuhukuu Ferry_tab Lautat Fertig Valmis Filename Tiijosto: Files from: Tiijostot kohtas: Filter: Filtruičus: Finances of %s Yhtivön %s talovus Finanzen Talovus find mismatch Verdua pakiettusarjoi fl_title Zavoduluvettelo Flug_tab Passažiirulendokonehet follow me Tule jälles Follow the convoi on the map. Tule kulgunevvon jälles kartal font size Šriftusuurus (vaiku TTF- da OTF-šriftat): Forest Meččät Found new city Perusta uuzi linnu Fracht gruuzua Frame time: Klietkumyöh.: Free Capacity Välly sija freeplay mode Vällykiža Friction: Nygöine treeniikoefitsijentu: fuel_cell Poltokenno Full load Vuota vähimäisgruuzuu Fullscreen (changed after restart) Kogo näyttö (vuadiu uvvestahtyönnändän) Fundament Perustu Fussgaenger Kävelii Game info Nettukiža GAME PAUSED Pauzal Game_msg Ylehine Gear: Välitys: Gebaeude Sroju General Ylehine Generated Luajittu Generation: %.0f MW Tuotando: %.0f MW\n Gewicht Paino Gewinn Tulos: Give the selected vehicle(s) an individual schedule Muuta vallittuloin kulgunevvoloin reitii gl_btn_sort_catg Kategourii gl_title Tavarluvettelo go home Työnnä depoh Goods Tavarua Goods AI Tavarluadivoäly Goods list Tavaraluvettelo Gross Profit Bruttotulos Groundobj Objektu groundobj builder Landšaftuobjektoin srojii Grow city Kazvata linnua Growth Kazvo GUI settings Käyttöliittymy H azetus Hangar Hanguaru Happy Tyydyväzii Has slope graphics Pidäy sydämessäh rinnehgruafiekat Has Snow Pidäy sydämessäh lumigruafiekat Haus kaufen Osta taloi Helligk. Näyttö Help Ohjavo Help text not found Ohjavotekstua ei löytynyh. hide all building Peitä kaikin srojut hide city building Peitä linnusrojut hide objects under cursor Peitä kursoran al olijat objektat hide station names Peitä azetuksien nimet hide transparent Näytä peitetyt objektat läbinägyjinä hide trees Peitä puut Hier warten/lagern: Passažiiroi/tavaroi vuottamas Higher transport fees, crossconnect all factories Korgeambii kulletustuloloi, zavodan tuotando ei azetu varaston mennes tävvelleh Highlite schedule Näytä reitin azetukset hl_title Azetusluvettelo hl_txt_filter Filtruičus: hl_txt_sort Lajitteluperusteh: hlf_chk_airport Lendokenty hlf_chk_anleger Portu hlf_chk_bahnhof Raudutieazemu hlf_chk_bushalt Avtobussuazetus hlf_chk_frachthof Nakkavusaloveh hlf_chk_keine_verb Ei yhtevysty hlf_chk_maglevstop Maglev-azemu hlf_chk_monorailstop Yksiraizivoazetus hlf_chk_name_filter Filtrui nimet: hlf_chk_narrowgaugestop Kaiduraizivoazemu hlf_chk_overflow Ylipainunuh hlf_chk_spezial_filter Spetsial'noi filtru hlf_chk_tramstop Tramvaiazetus hlf_chk_type_filter Filtrui tiiput: hlf_chk_waren_abgabe Tavarat sydämeh hlf_chk_waren_annahme Tavarat ullos hlf_title Azetusluvettelon filtruičus Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depuo ei löyditty; sinun pidäy tuöndäy kulgunevvo depoh manuaalizesti. Homeless Kodittomii hydrogene Vodorodu Idle: Vällyna: ignore climates Älä huomavoi ilmastoloi In the industry legend show only currently existing factories Näytä vaiku olemas olijat zavodat tevollizuskohtien sellittehes In Transit Matkal Increase Industry density Kazvata tevollizuon miäryä increase underground view level Nosta muanalazen nägymän tazoa industrial building Tevollizussrojut Industry overlay Tevollizustiijot: Infinite mouse scrolling Rajatoi kartan vieretysaloveh Infinite scrolling using mouse Kursoru pyzyy paikallah kartua vierettäes. Tämä ei vältämättäh rua kosketusnäyttölöil. Init map ... Luajitah kartua... Input Sydämeh Ins Stop Sijoita Insert stop before the current stop Liziä azetus enne nygöisty azetustu Install graphics Azeta gruafiekkoi Install paks Täytetäh da azetetah pakiettusarjoi... Insufficient funds! Sinul ei ole tävvellizesti varoi käyttäy tädä. Intercity road len: Muantielöin piduhus: Intro. date Käyttöhottopvm. Intro. date: Käyttöhottopvm.: invalid ei miäritelty. Invalid coordinate Viäry jälletys Invert stops Inversielline jälletys isometric map Izometrine nägymy Jan. pakkaskuu January pakkaskuu join game Kižua netas July heinykuu Jump to Mene siiriči kohtah June kezäkuu Kann Spielstand\nnicht laden.\n Tallendustu ei voitu täyttäy. Kann Spielstand\nnicht speichern.\n Kižua ei voitu tallendau. Kein Besitzer\n Ei omistajua\n keine ei nimidä Keine Einzelfahrzeuge im Depot Ei kulgunevvoloi depos Keyboard_Help\n Pikakluaviššuohjavo\n koord Kordinuatat Kreuzung Ristavus labellist_title Merkiluvettelo Lade Relief Täytä korgevuskuva Laden Täytä uuzi kiža Land attraction Kyläalovehnähtävys Land industries Zavodučiepit: LANG_CHOOSE\n Valličče tahtomas\nkieli:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Viime kuus: Last used tools Viimekse käytetyt ruadoinstrumentat Last Year Viime vuvvel: Leaving depot! Lähtös depospäi leer tyhjy legacy (large heights) Vahnu tila (kaksinkerdazet korgevuot) legacy (small heights) Vahnu tila (puolitetut korgevuot) Legend Kartan selliteh Leistung Voimu Leistung: %d kW Voimu: %d kW Leitung Sähköliinii length: %d Piduhus: %d letzen Monat: diesen Monat: viime kuu: tämä kuu: Line Liinii Line Management Liinielöin haldivo line name mouseover tooltips Liinin nimi hiirel linear Linearine Lineless convoys serving this stop Azetuksel kävvät liinitömät kulgunevvot Lines serving this stop Azetuksel kävvät liinit List of industries on the map Luvettelo kaikispäi kartal olijoispäi zavodoispäi LKW_tab Gruzoviekat Load game Täytä kiža load height data from file Täytä korgevusdatua tiijostospäi Load mode: Täyttötaba: Load scenario Täytä skenuario loaded nakattu loaded passenger/freight Passažiiroin/gruuzun lajitteluperusteh: Loading (%i->%i%%)! Nakatah (%i->%i%%) Loading (%i%%)! Nakatah (%i%%) Loading addon paks ... Täytetäh lizäozii... Loading map ... Täytetäh kartua... Loading paks ... Täytetäh pakiettoi... Loading skins ... Täytetäh käyttöliittymän gruafiekkoi... Loading time: Täyttöaigu: Lock game Estä kižuajan vaihto (vuadiu varmendustu) Lokomotive_tab Vedurit maglev vehicle maglev-juna maglev_track Maglev-raudutie Maglevdepot Maglev-depo Mail Demand %d\n Poštan tarveh: %d\n Mail level Poštua Mailbox Viestit Mailbox Options Viestilöin laitokset Maintenance Hoidamine make stop public (or join with public stop next) costs %i per tile and level Muuta azetus julgizekse (libo yhtistä rinnal olijah julgizeh azetukseh), maksau %i$ per plitku da tazo Make way or stop public (will join with neighbours), %i times maintainance Muuta tie libo azetus julgizekse (yhtistetäh rinnal olijoih azetuksih), %i-kerdazil hoidomenoloil Manual (Human) Manualine (kižuaju) Manufactured: Valmistettu: Map roughness Kartan šarmakkoizus: map view Kartu map zoom zoom: Mar. kevätkuu March kevätkuu Margin (%%) Voitto (%%) Marker Sijoita merki max max. Max Boost (%%) Enimäislizävys (%) Max income: Enimäistulot: Max. speed Enimäisvauhti Max. speed: Enimäisvauhti: Max. waiting time Enimäine vuottamisaigu (dd:hh:mm): Maximum 254 stops\nin a schedule!\n Aiguluvettelos voi olla enimilläh vaiku 254 azetustu. maximum length of rivers Jogiloin enimäispiduhus: Maximum tile height difference reached. Suurin korgevusero kahten plitkan välil tavattih. Maxspeed Enimäisvauhti May oraskuu Median Citizen per town Eläjii keskimiärin per linnu: Meldung Viesti Menge miäry merge stop Yhtistä azetus MessageOptionsText \nUuzi vuozi\n\nKižuaju-uudizet\n\nLinnu-uudizet\n\nEi reitii\n\nUvvet miäränpiät\n\nČätty\n\nUvvet kulgunevvot\n\nAzemu täyzi\n\nOngelmat\n\nTungokset\n\nSkenuario min min. minimum length of rivers Jogiloin vähimäispiduhus: Minimum load Vähimäisgruuzu (%): Missing pakfiles Ei-olijoi objektoi Modify the selected line Muuta vallittuu liiniedy Monate alt kuudu vahnu. monorail vehicle yksiraizivojuna monorail_track Yksiraizivotie Monorailboden Nostettu raudutie Monoraildepot Yksiraizivodepo Month Kuu month wait time Vuotanduaigu (kuus) Monthly departures Plotnoit lähtöaijat Months Kuut Monument Monumentu Monuments Monumentat Mountain height Gorien korgevus: Move the map Liikuta kartua Movingobj Liikkui objektu Music playing disabled/not available Muuzikku on iäre piäl/ei suadavannu. Music volume: Muuzikan iänenkovus: mute sound Nemoita iänet Name Nimi Narrowgauge Kaiduraizivo Narrowgauge are not available yet! Kaiduraizivotiet ei ole vie suadavannu. narrowgauge vehicle kaiduraizivojuna narrowgauge_track Kaiduraizivotie Narrowgaugedepot Kaiduraizivodepo Net ID: %p Verkon tunnus: %p\n Net Wealth Nettovarakkahus Net wealth near zero Nettovarakkahus on käytetty läs kogonah. Neue Karte Uuzi kiža Neue Welt Luaji uuzi kiža new convoi Uuzi kulgunevvo New Line Uuzi liinii New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Uuzi liinii on luajittu. Voit nygöi liittäy liinin kulgunevvoh valličemal sen liiniivalikospäi ylähäl. New Vehicles Uvvet kulgunevvot Nickname: Kuččumanimi: no buildings hidden Ei peitettylöi srojii no convois Ei kulgunevvoloi No goods are loaded onto this convoi. Gruuzuu ei nakata täh kulgunevvoh no goods waiting Ei vuottavii tavaroi no load Vaiku purgavo No player Ei kižuajua No Route Ei reitii No stop here! Ruadoinstrumentua tulou käyttäy azetuksien kohtal. No suitable ground! Mua on ebäsobii. No terminal station here! Agjuazemua ei voi srojie täh. Tazazel mual olii tienpiäte tarvitah. no timeline Kaikin aiguvälit no tree Ei puuloi No. of Factories Zavodoi da kauppoi: Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n "Kunnepäi lähten?"\nSinun pidäy andua kulgunevvole azetuksii libo liinii enne kui voit työndäy sen. none Ei nimidä nord pohjaine nordost koilline nordwest luodeh Not allowed to make publicly owned ways! Julgizien tielöin srojuamine ei ole luvallistu. Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Kulgunevvon aiguluvetteluo ei sua muuttua tien ečon aiguna. Not enough fields would remain. Ferman ymbarile ei jiäs tarbehekse peldoloi. Nov. kylmykuu November kylmykuu now nygöi Now active as %s.\n Nygöi aktiivine kižuajana %s. nowhere ei nimis Number of rivers Jogiloin lugumiäry: Object Objektu Oct. ligakuu Odometer: %s km Matkumittari: %s km Ok OK Oktober ligakuu On loan since %i month(s) Velgois %i kuun aijan On mouseover Ku kursoru on piäl on the lähtö(y)/kuu, enzimäine lähtö (dd-hh-mm) On this map, you are not\nallowed to change player!\n Kiža on lukittu; kižuajua ei voi vaihtau. Only city chains Sroji linnutevollizusčieppi Only first %d differing paks reported. There are probably more. Vaiku enzimäzet %d eriluadustu pakiettua näytetäh. Tovenägözes nennii on enämbängi. Only full Unicode fonts Käytä vaiku tävvellizii Unicode-šriftoi Only land chains Sroji kyläalovehen tevollizusčieppi Only one transformer per factory! Zavodan viereh voi srojie vaiku yhten transformuatoran. Only show goods which are currently handled by factories Näytä vaiku tavarat, mit on täl kodvazel käzittelys zavodal open Avvoi Operation Ruadomenot Ops Profit Laukkuvoitto Optionen Laitokset Or enter a server manually: Libo syötä adressi manualizesti: Origin Alguperä ost päivännouzu Output Ullostulo Ownership Omistavus Pak which may cause severe errors: Tulieloi objektoi (*.pak) ei ole, mi voi nostattau likendehčieppilöin kuadumizen: Pak which may cause visual errors: Tuliet objektat (*.pak) korvattih samankaldazil objektoil: Pak(s) different: Eriluaduzet pakietat: Pak(s) missing on client: Pakietat, mit ei ole klijental: Pak(s) not on server: Pakietat, mit ei ole serveral: Pakset differences Objektusarjoin erot paletten juaššiekkua Pas_tab Passažiirujunat Passagiere passažiirua Passagierrate Passažiiroi Passagierziele Passažiiroin/poštan miäränpiät Passenger AI Passažiiruluadivoäly Passenger Demand %d\n Passažiiroin tarveh: %d\n Passengers %d %c, %d %c, %d no route Passažiiroi %d %c, %d %c, %d ilmai reitii Passengers %d %s, %d %s, %d no route Passažiiroi %d %s, %d %s, %d ilmai reitii Password Peittosana: Pause Pauzu Pax <%i> Mail <%i> Passažiiroi <%i> poštua <%i> Pax level Passažiiroi PaxDest Miäränpiät Percent Electricity Sähkön tuotando (% tarpehespäi): Planes are not available yet! Lendokonehet ei ole vie suadavannu. Plant tree Istuta puuloi player Kižuaju player -1 Ristikanzankižuaju player 0 Julgizet palvelut player 1 Tiger Trans Oy player 10 Kižuaju 10 player 11 Kižuaju 11 player 12 Kižuaju 12 player 13 Kižuaju 13 player 2 Kulletus Trikky player 3 Oy Simutransport Ab player 4 Kulletus Meyer Oy player 5 Net Shipping Oy player 6 Kulletus Kataimägi player 7 Kižuaju 7 player 8 Kižuaju 8 player 9 Kižuaju 9 Please choose vehicles first\n Valličče kulgunevvot enzin.\n Post poštua Postrate Poštua Power Voimu Power (MW) Voimu (MW) Power: Voimu: Power: %4d kW\n Voimu: %4d kW\n Powerlines Sähköliinit Price Hindu Problems_msg Ongelmat Produced Luajittu Produces: %.1f units/minute Luadiu %.1f yksikkyö/min Production of %s has been stopped:\n%s\n Tiipun %s kulgunevvon tuotando on loppiettu:\n%s Production/Boost Tuotando/lizävys Produktion Tuotando: Profit Tulos promote to line Muuta liinikse q1 Kevät q2 Kezä q3 Sygyzy q4 Talvi Query server Työnnä kyzely rail car junanvagon random Piävyndähine Random age Piävyndähine igä Random map Piävyndähine kartu Rathaus Linnutaloi Rating Luokittelu ratio_pax Matkoin vuitti Relevant Relevantnoi Reliefkarte Kartu Remove Ota merki iäre remove airstrips Ota lennätys-/ruloamisdorogoi iäre remove channels Ota kanualoi iäre remove interm. signals Ota välis olijoi ozuttimii iäre remove maglev tracks Ota maglev-raudutielöi iäre remove monorails Ota yksiraizivotielöi iäre remove narrowgauge tracks Ota kaiduraizivotielöi iäre remove powerlines Ota sähköliinielöi iäre remove roads Ota dorogoi iäre remove signal Ota ozuttimii iäre remove tracks Ota raudutielöi iäre Remove wayobj %s Ota tieobjektua %s iäre replace other signals Korvua muut ozutimet replace stop Korvua azetus request closing Pakiče salbavuo residential house Eländyhuonukset Restore natural slope Työnnä luonnolline rinneh järilleh Restwert: Myöndyarvo: Retire date Vahnavumispvm. Retire. date: Vahnavumispvm.: return ticket Kopiirui tulole Revenue Ruadotulot Revert schedule Kumua muutokset Revision: Kerävöversii: road dorogu road vehicle ajonevvo Roadsign Likendehmerki Rotate Building Kiännä srojua Rotate map Kiännä kartua Rotation Pyörindy Routing Reititys Running cost Käyttömenot sack säkkii sail tuuli Saving map ... Tallendetah kartua... Scenario complete: %i%% Skenuarion progressu: %i%% Scenario Debug Ošipkanhaldivo Scenario Error Log Šriftan ošipkat Scenario Goal Tavoiteh Scenario Info Tieduo lyhyes Scenario information Tieduo nygözespäi skenuariospäi Scenario Result Progressu Scenario Rules Siännöt Schedule changing! Reitii muutetah! Schienentunnel Sroji raudutietunneli Schiff_tab Laivat Schiffdepot Dokku Schleppkahn_tab Baržat Screenshot Ota kuvakuabavus Scroll threshold Vieretyskynnys Search: Eči: Seasons Vuvvenaijat Sehenswuerdigkeit Turistunähtävys Select a server to join: Valličče serveru mih tahtot yhtistäy: Select a theme for display Valličče näytettävä teemu Select display font Valličče näytettävä šriftu Select one or more graphics to install (Ctrl+click): Valličče vähimiten yksi gruafiekkupakiettu azetettavakse (Ctrl+klikkuaus): Select soundfont Valličče soundfont-tiijosto Sell the selected vehicle(s) Myö vallitut kulgunevvot sended Työn. poštu SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 Sept. syvyskuu September syvyskuu Served by Ajelii: Served by me Minun ajelema Server did not respond! Serveru ei vastannuh! Serves Line: Palveleh liinil: Service Kursiiruindu set signal spacing Laita ozutinväli Setting Laitokset Ship Laivu shops and stores Kaupat da laitokset Show all Näytä kaikin show all building Näytä kaikin srojut Show also vehicles no longer in production. Näytä myös tuotandospäi iäreotetut kulgunevvot. Show also vehicles that do not match for current action. Näytä myös kulgunevvot, midä ei voi käyttäy vallitul funksiel. Show always Näytä ainos Show climates Ilmastot Show contour Muanmuvvot Show even servers with wrong version or pakset Näytä myös serverat, mit taritah viäryä versieda libo objektusarjua. Show finances for transport type Näytä tiedoloi likendehmuvvole Show future Näytä tulevazus show grid Näytä kodazilokku Show industry Zavoduluvettelo Show legend Valličukset Show map scale Värikoodat Show mismatched Näytä ebäluadeivat Show networks Näytä verkot Show obsolete Näytä vahnavunnuot Show offline Näytä offline-serverat Show only used Näytä vaiku käytetyt Show outline Infrastruktuuru Show schedules Näytä reitit Show servers that are offline Näytä offline-tilas olijat serverat Show servers where game version or pakset does not match your client Näytä serverat kudamin kižan versii libo objektusarju ei luadihes klijentas kel. show station coverage Näytä azemien kattavus show station names Näytä azemien nimet show waiting bars Näytä paččahii vuottavispäi show/hide block reservations Näytä/peitä tielöin tilavukset Show/hide estimated arrival times Näytä/peitä laskietut tuloaijat show/hide object owner Näytä/peitä objektan omistai Show/hide statistics Näytä/peitä statistiekat Shows buttons about networks overlay. Näytä liiniiverkon kattokuvan luajindah liittyjät painoknopkat Shows buttons on network overlay. Näytä painoknopkat, mil luajitah kattokuva liiniiverkospäi Shows buttons on special topics. Näytä painoknopkii omaluaduzispäi peruksispäi Shows consumer/suppliers for factories Näytä zavodoin kuluttajat/tavaranruadajat Shows the color code for several selections. Näytä värilöin selliteh muudamile valličuksile Shows the currently selected schedule Näytä täl kodvazel vallittu reiti Shrink city Kučista linnua shuffle midis Sevoita ozutelmat Signal Ozutin signal spacing Ozutinväli Sim: Petlat: Similar view as the main window Samankaldaine nägymy kui piäikkunas Size (%d MB): Suurus (%d Mt): Size (area) Suurus (aloveh) sliced underground mode Viibalehnägymy slot empty Tyhjy Smart hide objects Peitä objektoi kursoran ymbärilpäi Sort by Lajitteluperusteh Sort by: Lajitteluperusteh: Sort waiting list by Vuottavien luvettelon lajitteluperusteh: Sound Iänet Sound settings Iänilaitokset Sound volume: Iänien kovus: Soundfonts are located in the music directory. Soundfontat on 'music'-eččomistas. special freight Spetsgruuzu Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Vuajittai vähimäisvauhti vauhtibonuksele (%%):\ndorogat %i km/h, raudutiet %i km/h,\nlaivat %i km/h, lendokonehet %i km/h Speedlimit Vauhtirajoitus Speichern Tallenda Spieler Kižuaju Spieler(mz) Kižuajat Spielerliste Kižuajuluvettelo Spielstand wurde\ngeladen!\n Kiža täytettih hyvin. Spielstand wurde\ngespeichert!\n Kiža tallendettih hyvin. Sprache Kieli Sprachen Kielet Stadtinformation Linnan statistiekat Start Pane käymäh Start the selected vehicle(s) Työnnä vallitut kulgunevvot likendeheh Starte Spiel Alla kiža station labels Azemat Station tiles: Azemuplitkat: Station_msg Azemat Status Azetuksen tila steam höyry Step timeline one year Siirry vuvvel edehpäi Stops Azetukset Storage Varasto Storage capacity Varastotilavus Strassendepot Garažu Strassentunnel Sroji dorogutunneli street car tramvai sued suvi suedost liideh suedwest lounas Summer snowline Lumiraja kezäl: Supplied: %.0f %% Ruattu: %.0f %% Suppliers Tavaranruadajat: Tage alt päiviä vahnu. The following graphics are unmaintained: Tulieloi gruafiekkupakiettoi ei hoijeta: The gradient does not fit a tunnel Täh mägeh ei synny tunneli. Theme selector Teeman valličus There are still vehicles\nstored in this depot!\n Depos on vie kulgunevvoloi. This Month Täs kuus: This Year Täl vuvvel: Tile not empty. Tyhjendä plitku enzin enne muankohendeluu. timeline Mene aiguvälii myöte tl_title Linnuluvettelo To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Linnan %1$s likendehmiärät kazvos: %3$i veronmaksajua luaji srojun %2$s srojimizespäi mahtollistu. To heavy traffic\nresults in traffic jam.\n Bohattu likendeh nostattau tungoksen. Toggle day/night view Vaihta päivy/yö-tilua Toggle vehicle tooltips Valličče, mibo kulgunevvovihjavukset näytetäh tonnen t Toolbar position: Ruadoinstrupalkan paikku: Total inhabitants: Eläjii: Tourist attractions Turistunähtävyksii: Tourists Nähtävyksii Town_msg Uvvet miäränpiät Town: %s\n Linnu (%s)\n Towns Linnat track raizivo Tracks Raudutiet Traffic Likendeh traffic settings Likendeh Train Juna Trains are not available yet! Junat ei ole vie suadavannu. Tram Tramvai tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. tramvaitiet %i km/h, yksiraizivo %i km/h, maglev %i km/h, kaiduraizivo %i km/h. tram_track Tramvaitie Tramdepot Tramvaidepo Trams are not available yet! Tramvait ei ole vie suadavannu. Transferring game ... Siirretäh kižua... Transformer only next to factory! Transformuatorat tulou olla tyhjäl, tazazel mual zavodan vieres. Translation Kiännös transparencies Läbinägyvys transparent station coverage Läbinägyi azemien kattavus Transported Matkoi Trees disabled! Puut on otettu iäre piäl. TrolleyBus_tab Troleibusat Truck Gruzoviekku tt_Other Muut Tunnel muss an\neinfachem\nHang beginnen!\n Tunnelit tulou algua suoraspäi mäespäi. Tunnel must start on single way! Tunnelit tulou algua tielpäi. Tunnelboden Tunneli underground mode Muanalaine nägymy UNDO failed! Kumuamine enämbiä ei ole mahtollistu. Reitin srojimizen voi kumata vain, ku reitile ei ole srojittu ozutimii/azemii/azetuksii libo nimidä muuta. Undo last ways construction Kumua viimezin tiensrojuamisruado Unemployed Ruavottomii Unhappy Tyydymättömii units/day /kuu, Unloading (%i%%)! Purretah (%i%%) Update Line Muuta liiniedy upgrade HQ Päivitä piäkantor Usage: %.0f %% Käyttö: %.0f %% Usage/Output Käyttö/luajindu Use beginner mode Algaitila Use timeline start year Käytä aiguvälii vuvvespäi: Vehicle %s can't find a route! Kulgunevvo %s ei lövvä reitii. Vehicle %s is stucked! Kulgunevvo %s on juuttunuh. Vehicle details Kulgunevvon tiijot Vehicle Name Nimi Vehicle Power Voimu vehicles stored Säilytetut kulgunevvot Verbrauch Kulutus: Vergroessere die Karte\n Suurenda kartua. Verkauf Ota iäre hedi verkaufen Myö Verkehrsteilnehmer Hengilöavtot Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Sinul on %d kuudu aigua maksau velgas iäre. vh_title Luvettelo kaikispäi kulgunevvoloispäi via kauti (podr.) via %s\n kauti %s\n via Menge kauti (miäry) voranstellen Laita edeh Waggon_tab Vagonat waiting vuottamas Waiting for clearance! Vuottau vuoroah Walked Kävelty walking Kävelöy Warnings_msg Likendeh Wasser Vezi Water Kanualu Water level Vientazo: water vehicle vezikulgunevvo way %s cannot longer used:\n Tietiippu %s enämbi ei ole käytettävänny.\n way %s cannot longer used:\n%s\n Tietiippu %s enämbi ei ole käytettävänny:\n way %s now available:\n Tietiippu %s on nygöi suadavannu.\n Way toll Tiemaksut Ways not connected Tielöi ei ole yhtistetty. waytype tietiippu Wegpunkt Välietuappu Weight Paino Weight: Paino: Wert Arvo west päivänlasku Wind direction Tuulensuundu: Winter snowline Lumiraja talvel: withdraw Lope Withdraw All Lope kaikin WRONGSAVE Tallendettu kiža ei ole yhtehsobii. Tiijostuo ei voitu täyttäy. Year Vuozi Year %i has started. Vuozi %i on alganuh. Years Vuvvet Your primary color: Enzisijahine väri: Your secondary color: Toizensijahine väri: Zielort miäränpiä zooming in Lähendä zooming out Loitonda Zu nah am Kartenrand Et voi srojie nenga lähäle kartan reunua. #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Levitoindii magniettoin piäl %.1f km/h:n vauhtil: kulgunevvolpäi %s uuzi muailmanrekordu! New world record for monorails: %.1f km/h by %s. Yksi raizivo luajittu vauhtile: tarkah %.1f km/h kulgunevvolpäi %s luadiu täs uvven muailmanrekordan! New world record for motorcars: %.1f km/h by %s. Vaiku junot jäi jällele kulgunevvon %2$s kahaittua-ajuamizespäi %1$.1f km/h:s piästäkseh uuteh muailmanrekordah! New world record for narrowgauges: %.1f km/h by %s. Jyristämisty kaidoin raizivoloin yli %.1f km/h:n vauhtil: %s luaji uvven kaiduraizivojunien muailmanrekordan. New world record for planes: %.1f km/h by %s. Piduhuksien kučistamistu myös: %2$s lendi %1$.1f km/h - uuzi muailmanrekordu! New world record for railways: %.1f km/h by %s. Jyristämisty raizivoloin yli %.1f km/h:n vauhtil: %s laittau uvven muailmanrekordan junile.\m New world record for ship: %.1f km/h by %s. Sinizen nuorazen kulleskeluspäi %.1f km/h:n vauhtil on voittanuh %s. #________________________________script_ai_text_________________________________ #________________________________script_ai_text_________________________________ %s build additional convoy to line: %s %s osti kulgunevvon liinile %s. %s build rail line from %s (%s) to %s (%s) %s sroji raudutieliinin välile %s (%s) - %s (%s). %s build road line from %s (%s) to %s (%s) %s sroji gruzoviekkuliinin välile %s (%s) - %s (%s). %s build ship line from %s (%s) to %s (%s) %s sroji laivuliinin välile %s (%s) - %s (%s). %s extends the route from %s (%s) to %s (%s) %s levendi reitii välil %s (%s) - %s (%s). %s optimize way line from %s (%s) to %s (%s) %s muutti liiniedy välil %s (%s) - %s (%s). %s removes convoys from line: %s %s otti kulgunevvoloi iäre liinilpäi %s. vehicles of the line %s were retired Kulgunevvoloi loppiettih liinilpäi %s. #_________________________________tooltip_text__________________________________ #_________________________________tooltip_text__________________________________ Add convois with similar schedule to this line. Liziä liinile muut samankaldaistu reitii ajajat kulgunevvot Enter intervall in days, hours, minutes Syötä aiguväli päivis, čuassulois da minuutilois Mirrors order of stops Muuta azetuksien kundo inversiellizekse Revert to original schedule Kumua kaikin muutokset aiguluvetteloh #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL järvi &1_CITY_SYLL linnu &2_CITY_SYLL kylä &3_CITY_SYLL jogi &5_CITY_SYLL meččä &6_CITY_SYLL levei &7_CITY_SYLL niittu &8_CITY_SYLL kulmu &9_CITY_SYLL koski &A_CITY_SYLL pohju &B_CITY_SYLL korbi &C_CITY_SYLL kallivo &D_CITY_SYLL rinneh &E_CITY_SYLL lambi %0_CITY_SYLL Raja %1_CITY_SYLL Uuzi %2_CITY_SYLL Vähä %3_CITY_SYLL Koivu %4_CITY_SYLL Leppä %5_CITY_SYLL Armon %6_CITY_SYLL Laaja %7_CITY_SYLL Oja %8_CITY_SYLL Lahti %9_CITY_SYLL Vahnu %A_CITY_SYLL Kivi %B_CITY_SYLL Loitto %C_CITY_SYLL Lapin %D_CITY_SYLL Kirikön %E_CITY_SYLL Riihi 1center %s %s 1extern %s šuaru %s 1suburb %s %s %s 2center %s keskus %s 2extern %s sivu %s 2suburb %s kylä %s %s 3center %s piäaloveh %s 3extern %s mua %s 3suburb %s niittu %s %s 4center %s syväinpuoline %s 4extern %s ulgoine %s 4suburb %s kyläläine %s 5center %s keskilinnu %s 5extern %s vaihto %s 5suburb %s pieni linnu %s 6center %s ristavus %s 6extern %s tazango %s 6suburb %s laidu %s %s 7center %s linnu %s 7suburb %s puusto %s 8center %s laukkukeskus %s 8extern %s %s reunu %s 9center %s akseli %s 9extern %s dorogu siiriči %s simutrans-124.5/simutrans/text/ol/000077500000000000000000000000001520675460500172075ustar00rootroot00000000000000simutrans-124.5/simutrans/text/ol/climates.txt000066400000000000000000000022731520675460500215550ustar00rootroot00000000000000Landšaftan laitokset

Landšaftan laitokset (ruan al)

Landšaftan laitokset -ikkun laittau kižamuailman topogruafellizii da meteorolougizii ominazuksii. Ikkun avavuu ühtes luaji uuzi kiža -ikkunan kel.

Muuttuakses jogahistu laitostu, n'apsahuta jogahizen valličuksen sivus olevii nuolipainoknopkii, libo süötä noumer juaššiekkah.

Kartan valličukset

Vientazo - Laittau vien korgevuon kartal (gipoutezu on -2). Korgeambat arvot tariččou vähembän muata da enämbi vezistölöi.

Gorien korgevus - Laittau alovehen enimäiskorgevuon.

Kartan šarmakkoizus - Laittau alovehen šarmakkoizuon. Suurembi noumer luadiu enämbi aldoiluu rannikkoalovehil (nenne on real'noimbii). Ülen šarmakkoizile kartoile ei vältämättäh voi luadie muga äijü linnoi, zavodoi libo dorogoi kui midä oli tarkoitus luadie.

Varoitus: korgevustazoloi, mit on suurembii kui 33, tulis vältüö (nenne ei ole kižuajan käütettävännü). Liziä muatazoloi voi luadie tarbehen mugah alendamal vientazoa.

simutrans-124.5/simutrans/text/ol/general.txt000066400000000000000000000075141520675460500213740ustar00rootroot00000000000000Simutransin ohjavo

Ülehine ohjavo

- Tulgua terveh
- Käüttöliittümü
- Hiiren käüttö
- Klaviatuuran käüttö

Kižan laitokset
- Kieli
- Kižuajan värit
- Näütön laitokset
- Iäni ja muuzikku
- Kižuajat
- Kižan täüttiämine
- Tallendamine
- Uuzi kiža
- - Landšaftan laitokset
- - Korgevuskuvan käüttö

Piävalikko
- Kižan laitokset
- Kartuikkuna
- Tutkimisruadoinstr.
- Muankohendeluruadoinstr.
- Raudutieruadoinstr.
- Üksiraizivoruadoinstr.
- Tramvairuadoinstr.
- Doroguruadoinstr.
- Porturuadoinstr.
- Lendokentüruadoinstr.
- Spetsial'noit srojimisruadoinstrumentat
- Kižankohendeluruadoinstr.
- Hävitä/ota iäre

- Haldivoiččemisvalikko
- Liinielöin haldivo
- Luvettelot
- - Azetusluvettelo
- - - Azetus-/azemuluvettelon filtruičus
- - Kulgunevvoluvettelo
- - - Kulgunevvoluvettelon filtruičus
- - Linnuluvettelo
- - Tavarluvettelo
- - Zavoduluvettelo
- - Nähtävüsluvettelo
- Viestit
- Talovus

Muut valličukset
- Kuvakuabavuksen otto
- Pauzu
- Boikonda aigua
- Ohjavo

Muut dialougat
- Depon haldivo
- Aiguluvettelon haldivo
- Azetuksen tiijot
- - Azetuksen lizätiijot
- Kulgunevvon tiijot
- - Kulgunevvon lizätiijot
- Linnan tiijot
- Zavodan tiijot

Abuu voi löudüö muuloil kielil
müös tulieloil sivustoloil:
https://simutrans-germany.com/wiki/wiki/Home/
https://forum.simutrans.com/
https://www.tips.simutrans.com/
libo tulielois ohjavokirjoispäi:
Simutrans Starter Guide
(ezim. http://simutrans.igoreliezer.com/docs/
Simutrans%20Starter%20Guide.pdf
)
Simutrans Reference Manual
(ezim. http://simutrans.igoreliezer.com/docs
/Simutrans%20Reference%20Manual.pdf
)

simutrans-124.5/simutrans/text/ol/load_relief.txt000066400000000000000000000034461520675460500222240ustar00rootroot00000000000000Täütä korgevuskuva

Täütä korgevuskuva

Täütä korgevuskuva -ikkunua käütetäh avuamah tiijostuo, mis on mugavutettu aloveh (käütettäväkse uvven kižan luajindas), da sidä voi müös käüttäü nämien iäreottuamizeh. Se avavuu luaji uuzi kiža -ikkunaspäi, da se näüttäü tiijostoloin nimet, päivümiärät da tallendusaijat luvettelos. Ku kaikin tiijostonimet ei nävü, käütä luvettelon oigienpuolistu vieretüspalkkuo vierettäkses nimien läbi.

N'apsahuta nimie luvettelospäi täüttiäkses mugavutetun kartan, libo süötä nimi ikkunan üläreunas olevah tekstujuaššiekkah: n'apsahuta juaššiekkua, kirjuta nimi da paina [Enter]- libo [Return]-kluaviššua libo n'apsahuta OK-painoknopkua (viärü nimi ei rua nimidä, üritä uvvessah).

N'apsahuta nimen hural puolel olevua x-painoknopkua (ku on laitettu piäle simuconf.tab:s) ottuakses mainitun tiijoston iäre tiijostosistiemaspäi. Tiijostonimi lähtöü iäre luvettelospäi da ikkun salbavuu.
Varoitus: tiijosto otetah iäre välittömästi da lujasti. Käütä funksiedu hüvin ruatusti!

Alovehii on suadavannu netispäi, ezim. sivustol https://maps.simutrans.com libo Steam Workshopispäi. Uvven tiijoston tuleh olla PPM- libo BMP-muvvos, mis jälgimäistü pidetäh tugie rajoitetusti. Tiijostot tuleh laittau kižan käüttäieččomistoh "simutrans/maps/".

Vihjavus: Tieduo omien alovehien luadimizespäi on suadavannu Simutransin wikis.

N'apsahuta Peruuta-painoknopkua libo üläoigies reunas olevua x-painoknopkua libo käütä kluaviššua salbuakses ikkunan.

simutrans-124.5/simutrans/text/ol/new_world.txt000066400000000000000000000127421520675460500217560ustar00rootroot00000000000000Luaji uuzi kiža

Luaji uuzi kiža

Luaji uuzi kiža -ikkunas laitetah uvven kižan ominazuksii. Täl luajitah uuzi kartu vallittuloil laitoksil. Voit muuttau monii eri kižan muuttujii, ezim. kartan suuruttu, eloikeskuksien miäryä kižan allus da zavodoin miäryä. Vallituspäi alovehespäi näytetäh myös ezikačeluversii.

Muut laitosikkunat ei ole käytettävis, ku tämä ikkun on avvoi. Tullakses järilleh nygözeh kižah, salbua ikkun n'apsahuttamal nimipalkis olevua x-painoknopkua.

Nuolipainoknopkat muuttau laitoksii, da valličusklietkat panou valličuksii piäle/pois piälpäi libo avuau uuzii ohjavusikkunoi.

Kartan noumer - Valličeh kižas käytettävän alovehen.
Klikkua noumerjuaššiekkua da syötä kartan noumer, libo käytä nuolipainoknopkii siirdyäkses eri alovehvaihtoehtoloin välil. Ezikačelunägymy näyttäy vallitun alovehen moizena kui se nägys kižas (vezistöt on sinizii, madalembi mua on mustembi kui korgeambi mua).

Suurus - Laittau kartan suuruon L x K (levevys x korgevus) -muvvos. Ylembi arvo on levevys, da alembi on korgevus. Vuajittava keskusmuston miäry näytetäh salbavomerkien sydämes.
Huom.: Suuremmat kartat vuadiu enämbän mustuo, da nennien generoindas meneh enämbän aigua. Kiža da muut gruafiekat vuadiu lizäkse 88 Mt mustuo pak64:s da 144 Mt pak128:s. Ylehizenä piäsiändönä älä yritä luadiu kartua mi on suurembi kui 512 x 512 plitkua tiedokonehel, mis on 256 Mt mustuo.

Piävyndähine kartu - Valličeh alovehen piävyndähizesti. Muut laitokset ei muutu.

Täytä korgevuskuva - Täyttäy mugavutetun alovehen. Avuau mainitun ikkunan.

Linnoin lugumiäry - Laittau eloikeskuksien miärän kižan allus. Ku kartu on liijan pieni libo liijan karhei, linnoi luajitah vähembän. Liziä uuzii linnoi voi panna manualizesti vaihtamal julgizeh kižuajah.

Eläjii keskimiärin per linnu - Laittau kaikkien eloikeskuksien keskimiaräzen eläiluvun. Simutrans yrittäy luadie enämbän pienii kylii kui suurii linnoi.

Muantielöin piduhus - Laittau eloikeskuksien keskeizien julgizien dorogoin enimäispiduhuon kižan allus. Laita täh suurembi arvo yhtistiäkses eloikeskuksii, mit on loitombana toizispäih.

Zavodoi da kauppoi - Laittau eloikeskuksien ulgopuolizien tevollizusruandučieppilöin miärän kižan allus. Liziä tevollizusčieppilöi ilmestyy avtomuatillizesti eloikeskuksen eläiluvun kazvaes.
Huom.: Ku aloveh on ylen mägine, sobijua tazamuata ei vältämättäh löydy tevollizusčieppilöin luadimizeh, sikse vähembän tevollizuttu ilmestyy.

Turistunähtävyksii - Laittau eloikeskuksien ulgopuolizien passažiiroi da poštua muanittelijoin mua-aloin miärän kižan allus. Turistunähtävykset ilmestyy eloikeskuksien ulgopuolele uuzien kylien libo linnoin kel luajittuloin nähtävyksien lizäkse kartan luajindan jälgeh.

Käytä aiguvälii algajen vuvves - Laittau kižan allatusvuvven. N'apsahuta valličusklietkua kižatakses aiguperustehizil rajoituksil. Ku aigu kižas kuluu, liziä kulgunevvoi, srojii da zavodoi ilmestyy.

Ku funksiedu ei ole vallittu, kaikin kulgunevvot, srojut da zavodat on suadavannu da käytös kižas. Eloikeskukset ilmestyy eri aiguvälielpäi tulieloin srojien kel, da hil'l'ambat kulgunevvot ei ole muga tuloksekkahii kui boikoimbat, ku tulos laskietah käytetyn kulgunevvon keskivauhtin mugah.

Algaitila - Laittau valličuksen, mi andau luvan korgeambien tuloloin suandah kižas tavaroin da passažiiroin kullettamizespäi. Gipoutezuarvozesti, ku kižatah algaitilas, suat 150% arvospäi kui ku sais 100% tavallizes tilas. Lizäkse zavodat ei lope tuotanduo ku kuluttajil on ylitaričustu.

Laitokset - Avuau levendetyt laitokset -ikkunan. Tiäpäi piäzeh käzikse kaikkih parametroih mit on käytettävänny myös simuconf.tab -tiijostos.

Landšaftan laitokset - Avuau landšaftan laitokset -ikkunan, mi ohjuau erilazii kartan laitoksii: šarmakkoizuttu, vientazoa, ilmastoloi, meččäy da jogiloi.

Täytä kiža - Täyttäy tallendetun kižan. Näyttäy Täytä kiža -ikkunan, mispäi voi jatkau aijembah tallendettuu kižua. Ku tallendettu kiža on täytetty, se korvuau nygözen kižan ilmai varmendustu.

Täytä skenuario - Täyttäy skenuarion, toizin sanoen kižan, mis tietty tavoiteh pidäy tavoittau. Tämä on vaihtoehto tavallizele, välläle Simutransin kižaluavule.
Huom.: Mistah pakiettusarjois ei ole ni yhty skenuarioloi.

Alla kiža - Algau kižan luajimal uvven muailman vallituloil laitoksil. N'apsahuta painoknopkua kižatakses Simutransii. Ozua da hyvii kižakodvazii!

Lope - N'apsahuta painoknopkua lähtiekses iäre kižas.

Vihjavus: muuttuakses Simutransii käymäh laittaes täytettävii gipoutezuarvoloi libo muuloi globualoi muuttujii, muuta arvoloi simuconf.tab -tiijostos.

simutrans-124.5/simutrans/text/pl.tab000066400000000000000000001537231520675460500177130ustar00rootroot00000000000000§Polski PROP_FONT_FILE prop-latin2.fnt ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: pl Polski # # Encoding: UTF-8 # # Font: prop-latin2.fnt # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Czas bezwzględny AMBIENT_SOUND Dźwięk otoczenia CASH_SOUND Dźwięk pieniędzy cl_btn_filter_disable Wyłącz cl_btn_filter_enable Włącz cl_btn_filter_settings Ustaw cl_btn_sort_asc Rosnąco cl_btn_sort_desc Malejąco cl_btn_sort_id Wewnętrzne ID cl_btn_sort_income Przychód cl_btn_sort_name Nazwa cl_btn_sort_type Typ clf_btn_alle Wszystkie clf_btn_invers Odwróć clf_btn_keine Brak climate area percentage Procent obszaru klimatu CROSSING_SOUND Dźwięk przejścia FACTORY_SOUND Dźwięk fabryk Find matching convois Znajdź pasujące konwoje gl_btn_sort_bonus w/g premii gl_btn_sort_name w/g nazwy gl_btn_sort_revenue w/g dochodu gl_btn_unsort nieposortowane height based Na podstawie wysokości hl_btn_filter_disable Wyłącz hl_btn_filter_enable Włącz hl_btn_filter_settings Ustaw hl_btn_sort_asc Rosnąco hl_btn_sort_desc Malejąco hl_btn_sort_name Nazwa hl_btn_sort_type Typ hl_btn_sort_waiting Oczekiwanie hlf_btn_alle Wszystkie hlf_btn_invers Odwróć hlf_btn_keine Brak humidities Wilgotność Install Instalacja paczek Lake Jezioro Lake height Wysokość jezior Maximize height levels Maksymalizuj poziomy wysokości moisture land Wilgotność nad lądem moisture water Wilgotność nad wodą Networks Sieci transportowe Num pad keys always move map Klawisze numeryczne zawsze przesuwają mapę Open Sea Otwarte morze Queueing Czas oczekiwania rainfall Opady deszczu Reselect closes tools Ponowny wybór narzędzia powoduje jego zamknięcie Road toll Opłata drogowa Scenario Sceneria sea Morze Single GUI Pojedynczy interf. graf. Sound range: Zakres dźwięku: Start this as a server Uruchom jako serwer temperature borders Granice temperatur temperature-humidity based Na podstawie temperatury i wilgotności TOOL_SOUND Dźwięk narzędzi TRAFFIC_SOUND Dźwięk ruchu Transfers Przepustowość #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Klim. arktyczny desert Klim. pustynny mediterran Klim. śródziemny rocky Ter. skaliste temperate Klim. umiarkowany tropic Klim. tropikalny tundra Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Wczytanie scenariusza nie powiodło się! Bridge blocked by way below or above. Most zablokowany poniżej lub powyżej. Can't buy obsolete vehicles! Nie można zakupić przestarzałych pojazdów! Cannot alter water Zbiornika tego nie można dalej opróżniać ani napełniać. Cannot build on a double slope! Można budować tylko na jednym zboczu lub terenie płaskim. Cannot built depot here! Nie można zbudować tu zajezdni. Cannot built this station/building\nin underground mode here. Budynek można wybudować tylko na powierzchni.\n Cannot connect to the\ncenter of a double slope! Nie można połączyć się ze środkiem podwójnego zbocza! Cannot create generic line!\nSelect line type by\nusing filter tabs. Nie mogę utworzyć linii \nnie znając jej typu! \n\n\nWybierz typ linii za pomocą zakładek. Cannot create socket Nie można zainicjować połączenia! Cannot rotate this building! Nie można obrócić tego budynku. Client closed connection during transfer Klient zamknął połączenie podczas transferu. Convoi handles exhausted! Maksymalna ilość pojazdów (konwojów) osiągnięta. Convoy already deleted! Skład już usunięto! Could not open file Nie można otworzyć pliku. Das Feld gehoert\neinem anderen Spieler\n Te tereny\nsą własnoscią\ninnego gracza!\n Der Besitzer erlaubt das Entfernen nicht \nWłaściciel nie wyraża\nzgody na likwidację\nobiektu!\n Diese Zusammenstellung kann nicht fahren!\n Taki zestaw\nnie pojedzie! Flugzeughalt muss auf\nRunway liegen!\n Przystanek musi\nleżeć na pasie\nstartowym! Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Tego budynku lotniska nie można tutaj zbudować. Hier kann kein\nSignal aufge-\nstellt werden!\n Nie możesz\nzbudować tu\nsemaforu!\n In order to lock the game, you have to protect the public player by password! W celu zapisania gry trzeba ustawić hasło! Loading a new game will end the current server session! Uruchomienie nowej gry zakończy bieżącą sesję serwera Lost connection\nto server! Serwer \nnieodpowiada!\nGrasz offline! Lost synchronisation\nwith server. Utracono synchronizację\nz serwerem. Maglevhalt muss auf\nMaglevschiene liegen!\n Przystanek kolei magnetycznej może znajdować się tylko na szynach magnetycznych! Monorailhalt muss auf\nMonorail liegen!\n Przystanek jednoszynowy\nmusi być na szynie. Monorails are not available yet! Kolej jednoszynowa nie jest jeszcze dostępna. More than one possibility to build this dock found. Znaleziono więcej niż jedną możliwość zbudowania tego doku. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Przystanek kolei wąskotorowej może zostać zbudowany tylko na szynach wąskotorowych! No bridges over runways! Nie można zbudować mostu nad pasem startowym. No curves on runways Nie można budować zakrętu pasa startowego. No suitable crossing Brak odpowiedniej trasy. No suitable way on the ground! Peron można wybudować tylko na płaskiej powierzchni! No through station here! Nie możesz tu wybudować \nprzystanku! Not enough bytes transferred Serwer nie wysłał wystarczającej liczby bajtów podczas transmisji gry. Not enough clearance. Za mało miejsca nad lub pod. Not enough money! Masz za mało pieniędzy aby to zbudować! On narrowgauge track only!\n Tylko na torach! Only public player can lock games! Tylko zarejestrowany gracz może zamknąć grę! Only up and down movement in the underground! W podziemiach możesz poruszać się w górę i w dół wyłącznie za pomocą narzędzi góra/dół! Out of funds Brak funduszy. Post muss neben\nHaltestelle\nliegen!\n Poczta musi\nznajdować się\nprzy stacji/przystanku!\n Protocoll error (expecting game) Błąd protokołu Schiffhalt muss im\nWasser liegen!\n Porty można budować\ntylko na wodzie!\n Server busy Serwer zajęty! Server version too new Wersja serwera jest nowsza od twojej. Show all revenue messages Pokaż zarobki wszystkich graczy Show no revenue messages Ukryj zarobki wszystkich graczy Show only player's revenue Pokaż tylko przychody graczy Terraforming not possible\nhere in underground view Terraformowanie jest pod ziemią niemożliwe. This tunnel branches. You can try Control+Click to remove. Ten tunel rozgałęzia się. Możesz spróbować ctrl+klik, aby usunąć. Too far away to merge stations! Zbyt daleko, aby połączyć stacje! Upgrade must have\na higher level Unowocześnić można tylko\nna wyższy poziom! Vehicle %s cannot choose because stop too short! Pojazd %s nie mógł wybrać drogi. Przystanek za krótki! Zughalt muss auf\nSchiene liegen!\n Stacje kolejowe\nmożna budować\ntylko na\ntorach kolejowych!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Pomoc

Spis pomocy

O Simutrans

%1$s

Jak używać

%2$s

Jak zacząć grę

%4$s

Jak grać

%5$s

Narzędzia

%3$s

Inne okna

%6$s Keyboard Help\n

Keyboard Help

\n Polecenia klawiaturowe\n

Polecenia klawiaturowe

\nSimutrans zna następujące polecenia klawiaturowe:

\nCtrl(< em>Ctrl) + kliknięcie - ukrywanie okna informacyjnego (tylko w niektórych miejscach)
\nCtrl(Ctrl) + narzędzie - działanie na wyższym poziomie lub .buduj tylko proste ścieżki.
\n\nEsc, Usuń: zamknij górne okno
\nBackspace >: Zamknięcie wszystkich okien
\nStrona w górę/Strona w dół, </>: Powiększ mniejszy/większy< br>\nKlawisze kursora i klawiatura numeryczna przesuwają mapę\n

\nDowolnie można przypisywać następujące polecenia:
\n At least one stop is connected to the town. Przynajmniej jeden przystanek jest połączony z miastem. At least one tile is connected to one stop. Co najmniej jeden budynek jest połączony z przystankiem. Color according to transport capacity left Kolor w zależności od pozostałej pojemności transportowej Color-coded terrain according to altitude. Pokaż/ukryj cieniowanie konturu terenu. Decrease water height Zmniejsz poziom wody Highlight railroad tracks Podświetl tory Highlite depots Podświetl magazyny Highlite electrical transmission lines Podświetl linie wysokiego napięcia Highlite factories Podświetl fabryki Highlite forests Podświetl lasy Highlite tourist attraction Podświetl atrakcje turystyczne Increase water height Zwiększ poziom wody Open station/stop details Pokaż szczegóły przystanku Overlay city limits Pokaż granice miast Overlay passenger destinations when a town window is open Przy otwartym oknie miasta: Pokaż cele pasażerów/poczty Overlay schedules/network Pokaż na mapie wszystkie istniejące połączenia Overlay town names Pokaż nazwy miast Please click on the map to add\nwaypoints or stops to this\nschedule. Kliknij na mapie aby dodać do tego rozkładu punkty przejazdowe albo przystanki. Remove double stops from schedule Usuń powtarzające się przystanki z rozkładu Set tile climate %s Zmień klimat na %s Show capacity and if halt is overcrowded Pokaż pojemność przystanku oraz stan przeciążenia. Show how many convoi reach a station Pokaż, ile pojazdów zatrzymuje się na tej stacji. Show how many people/much is waiting at halts Pokaż liczbę oczekujących Show initial passenger departure Pokaż, skąd przjeżdżają pasażerowie. Show level of city buildings Pokaż poziom budynków miejskich. Show mail service coverage/mail network Pokaż zasięg usług poczty Show passenger coverage/passenger network Pokaż zasięg/sieć pasażerów Show speedlimit of ways Pokaż maksymalne prędkości na drogach. Show the change of waiting at halts Pokaż historię czasu oczekiwania Show the owenership of infrastructure Pokaż właściciela infrastruktury. Show transported freight/freight network Pokaż transport towarowy i połączenia Show usage of network Pokaż wykorzystanie sieci. Shows a listing with all industries on the map. Pokaż listę wszystkich fabryk na mapie. Sum of departure/arrivals at halts Liczba przybyłych i odprawionych towarów (pasażerów, poczty) na tej stacji. #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s prowadzi linię\nautobusową pomiędzy %s\na atrakcją %s\nna (%i,%i) %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s otworzył linię autobusową\npomiędzy %s\na fabryką %s\nna (%i,%i) %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nteraz korzysta z\n%i pojazdów pomiędzy\n%s w (%i,%i)\ni %s w (%i,%i).\n %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s \notworzył nowe połączenie \nkolejowe między \n%s w (%i,%i) a %s \nw (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Firma %s utworzyła połączenie\nlotnicze pomiędzy %s a %s. Ferry service by\n%s\nnow between\n%s \nand %s.\n Firma %s uruchomiła połączenie promowe\npomiędzy %s a %s. Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Podróżni mogą teraz korzystać z połączenia\nautobusowego firmy %s pomiędzy\n%s a %s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Narzędzia edycji mapy LISTTOOLS Listy MAGLEVTOOLS Buduj kolej magnetyczną MONORAILTOOLS Buduj kolej jednoszynową NARROWGAUGETOOLS Buduj kolejkę wąskotorową RAILTOOLS Buduj koleje ROADTOOLS Buduj drogi SHIPTOOLS Buduj porty SLOPETOOLS Narzędzia zbocza SPECIALTOOLS Buduj budynki specjalne TRAMTOOLS Buduj tramwaje #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s ma teraz siedzibę w (%i,%i). Factory chain extended\nfor %s near\n%s built with\n%i factories. Gospodarka rośnie:\n%s blisko %s rozszerza łańcuch.\n%i założono nową fabrykę. Net wealth less than 10%% of starting capital! Majątek netto poniżej 10%% kapitału początkowego! New %s now available:\n%s\n Nowy %s jest dostępny:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Nowy łańcuch fabryk\ndla %s powstał\nniedaleko %s z %i fabrykami. New vehicle now available:\n%s\n Dostepny nowy pojazd:\n%s\n Now %u clients connected. Obecnie na serwerze jest %u graczy. overtaking wyprzedzanie Remove vehicle from map. Use with care! Usuń pojazd z gry. Używać ostrożnie! Screenshot\ngespeichert.\n Zapisano\nzrzut ekranu.\n Sends the convoi to the last depot it departed from! Wyślij pojazd do najbliższej bazy. Server preparing game ... Serwer przygotowuje grę... Trees Drzewa With a big festival\n%s built\na new monument.\n%i citicens rejoiced. Dzięki dużemu festiwalowi,\n\nw %s zbudowano\n\nnowy monument powodując\n\nradość obywateli.\n #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Filtr liniowy #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%.2f$/km %.2f$/m) (%.2f$/km %.2f$/mies.) (%i)- (%i) (in depot) (w zajezdni) [0] south-facing [0] zwrócone na południe [1] east-facing [1] zwrócone na wschód [2] north-facing [2] zwrócone na północ [3] west-facing [3] zwrócone na zachód [4] southeast corner [4] narożnik południowo-wschodni [5] northeast corner [5] narożnik północno-wschodni [6] northwest corner [6] narożnik północno-zachodni [7] southwest corner [4] narożnik południowo-zachodni [END] End [HOME] Home \nBauzeit bis do \nBauzeit von \nPojawia się od \nCan't open heightfield file.\n \nNie mogę odczytać pliku mapy.\n \ndirection: \nkierunek: \nelektrified \nzelektryfikowane \nHeightfield has wrong image type.\n \nZły typ obrazu\ndla mapy!\n(.ppm)\n \nis reserved by: \nzarezerwowany przez: \nminimum speed: \nprędkość minimalna: \nnot elektrified \nnie zelektryfikowane \nRibi (masked) \n\nKierunki\n\n(maskowane): \nRibi (unmasked) \nKierunki\n(niezamaskowane): \nSet phases: \nCzas zielonego światła: \nsingle way \npojedyncza droga \nway1 reserved by \nTrasa 1 zarezerwowana przez \nway2 reserved by \nTrasa 2 zarezerwowana przez \nwith sign/signal\n \n\nZe znakiem/semaforem\n %d buildings\n Budynków: %d %d convois Składów: %d %d Einzelfahrzeuge im Depot Pojazdów w zajezdni: %d %i car(s), %i pojazd(ów), %i km/h (max. %ikm/h) %i km/h (max. %i km/h) %i years %i months old. Wiek: %i lat i %i miesięcy %s at (%i,%i) now public stop. %s stał się węzłem publicznym. %s building %s %s %s - %s %s %s city %d %s %s miasto %d %s %s factory %s %s %s fabryka %s %s %s has entered a depot. %s\nPojazd czeka w bazie %s land %d %s %s teren %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s zbudowało nowy ratusz gdy ilość\n\nmieszkańców wyniosła\n\n%i. %s\nis crowded. %s\njest przepełniony/a. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nPrędkość maksymalna: %3$i km/h\n\nAktualna prędkość: %2$i km/h.\n %s\nwas liquidated. %s zbankrutował\n. Firma została zamknięta, a wszystkie aktywa sprzedane. %u Client(s)\n %u Klient(ci)\n %u Player (%u locked)\n %u Gracz (%u zablokowany)\n %s - %s
\n [%s]: %s
\n

Error

Błąd: Podwójne obiekty w głównym katalogu pak

W

Warning

addons for

Ostrzeżenie: zdublowane obiekty w katalogu dodatków

Dodatki dla 1 convoi 1 skład 1 Einzelfahrzeug im Depot 1 pojazd w zajezdni 100 km/h = %i tiles/month 100 km/h = %i pól/miesiąc 1LIGHT_CHOOSE Jasność: 1WORLD_CHOOSE Opcje nowej mapy: 2LIGHT_CHOOSE Kolor: 2WORLD_CHOOSE Numer mapy: 3LIGHT_CHOOSE Prędk. przewijania: 4LIGHT_CHOOSE Odwróć przewijanie 5LIGHT_CHOOSE Pokaż przechodniów 5WORLD_CHOOSE Ilość miast: 6LIGHT_CHOOSE Piesi w miastach 6WORLD_CHOOSE Ruch drogowy: 8WORLD_CHOOSE Tryb dzień/noc A bridge must end on a way! Most musi kończyć się na drodze! A bridge must start on a way! Most musi zaczynać\nsię na drodze! Abfrage Zobacz... Abnehmer Odbiorcy About Informacje About scenario O scenariuszu Abriss Zniszcz/Usuń Absenken Obniż poziom Abspanntransformator Stacja transformatorowa Accelerate time Przyśpiesz Act. load: %u MW\n Aktualne zużycie: %u MW\n Active player only Tylko Aktywny Gracz Add forest Zasadź las Add random citycar Dodaj losowy samochód add server Dodaj serwer Add Stop Dodaj Add stops for backward travel Dodaj stacje drogi powrotnej air Pas startowy Aircraft Samolot aircraft_tab Transportowe airplane samolot Airport Lotnisko AIRTOOLS Buduj lotniska All Wszystkie all convoi tooltips wszystkie podpowiedzi dotyczące pojazdów Allow city growth Pozwól miastu się rozrastać Allow player change Zezwól na zmianę gracza allowed climates:\n Dozwolone klimaty:\n Alters a schedule. Dodaje/usuwa przystanki z rozkładu and arranged by aranżacja przez Angenommene Waren Tutejszy przemysł potrzebuje: anhaengen Dodaj Anhaenger_tab Przyczepy Anheben Podnieś poziom Appends stops at the end of the schedule Dodaj przystanki na końcu rozkładu Apply Line Przypisz linię Apr. Kwiecień April Kwiecień Arbeiter aus: Robotnicy mieszkają w: Arrivals from\n Przybycie z\n Arrived Przybyłych Assets Aktywa Aufloesen Rozłóż Aufspanntransformator Stacja transformatorowa Aug. Sierpień August Sierpień auto Automatyczny Autohalt muss auf\nStrasse liegen!\n Przystanek musi\nbyć postawiony\nna drodze. Available Dostępny Available at custom date Dostępne w niestandardowym terminie Bahndepot Lokomotywownia Bankrott:\n\nDu bist bankrott.\n Bankructwo:\n\nStraciłeś cały majątek.\n battery akumulatorowy Baum Drzewo baum builder Posadź drzewa Baustelle Konstrukcja Bauzeit Czas budowy Beenden Koniec gry Beginner mode Tryb Początkujący Besonderes Gebaeude Atrakcja turystyczna BF Stacja bio Biologiczny Blockstrecke ist\nbelegt\n Tor używany\nprzez inny pociąg\n Boden Ląd Bonus Multiplier: %i%% Mnożnik premii: %i%% Bonus Speed: %i km/h Premia prędkości: %i km/h Bonusspeed: %i km/h Maks. możliwa prędkość: %i km/h Boost (%%) Wzrost (%%) Borderless (disabled on fullscreen) Bez obramowania (wyłączone na pełnym ekranie) bridge is too high for its type! Ten typ mostu nie może być tak wysoki! Bridge is too long for this type!\n Przęsło zbyt długie\ndla tego typu mostu!\n Bruecke Most Bruecke muss an\neinfachem\nHang beginnen!\n Most musi\nzaczynać się na\nrównym zboczu!\n Brueckenboden most Build air depot Zbuduj hangar lotniczy build choosesignals Semafory wyboru peronu Build city market Zbuduj market w najbliższym mieście Build drain Stacja transformatorowa build HQ Zbuduj siedzibę Build land consumer Stacja zasilania Build maglev depot Baza kolei magnetycznej Build monorail depot Lokomotywownia Build narrowgauge depot Baza kolejowa Build powerline Linia wysokiego napięcia Build presignals Semafor wyprzedzający Build road depot Zbuduj garaż Build ship depot Stocznia Build signals Semafor Build train depot Lokomotywownia Build tram depot Zajezdnia tramwajowa Build truck depot Baza Building costs estimates Koszt budowy wyniesie Buildings Ilość budynków Built artifical slopes Zbuduj sztuczne zbocza Built random attraction Buduj losową atrakcję Bus_tab Autobusy Can be overgrown Może być obrośnięty Can only move from halt to halt or waypoint to waypoint. Może się przemieszczać jedynie\nod przystanku do przystanku lub\nod punktu do punktu. Cancel Anuluj Cannot connect to offline server! Nie można połączyć się z serwerem offline! Capacity Pojemność Capacity: Pojemność: Capacity: %.0f MW Pojemność: %.0f MW Capacity: %d%s %s\n Pojemność: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Pojemność: %s\nŁadunek: %d (%d%%) Cars are not available yet! Samochody nie są jeszcze dostępne. cars.\nstate pojazdów\n Cash Gotówka Change player Zmień gracza Chart Wykres Chat_msg Czat Choose direction Wybierz kierunek Choose operation executed on clicking stored/new vehicles Wybierz operację wykonywaną podczas kliknięcia na pojazdy chooses a random map Wybiera losową mapę citicens Obywateli City attraction Atrakcja turystyczna City industries Marketów w miastach: City list Lista miast City size Ludność city_road Droga miejska citybuilding builder Rozbudowa urbanizacji CityLimit Zasięg miast cl_title Lista pojazdów cl_txt_sort Sortuj po: clamp Ogranicz zakres Cleanup schedule Wyczyść podwójne rozkłady Clear block reservation Pokaż/usuń rezerwacje tras clf_chk_aircrafts Samoloty clf_chk_cars Pojazdy drogowe clf_chk_indepot W bazie clf_chk_maglev Kolej magnetyczna clf_chk_monorail Jednotorówka clf_chk_name_filter Nazwa filtru: clf_chk_narrowgauge Kolej wąskotorowa clf_chk_noincome Bez dochodu clf_chk_noline Bez linii clf_chk_noroute Bez połączenia clf_chk_noschedule Bez rozkładu clf_chk_obsolete przestarzałe clf_chk_ships Statki clf_chk_spezial_filter Filtr specjalny: clf_chk_stucked Zablokowane clf_chk_trains Pociągi clf_chk_trams Tramwaje clf_chk_type_filter Typ filtru: clf_chk_waren Filtruj towary: clf_title Filtruj listę pojazdów Climate Klimat Climate Control Kontrola klimatu Climates Klimaty closed Zamknięte Closes topmost line window when new line selected. Zamyka najwyższe okno linii po wybraniu nowej linii. COLOR_CHOOSE\n Wybierz kolor\n z tabeli:\n Company bankrupt Firma zbankrutowała! Company_msg Konkurencja Comparing pak files ... Porównywanie plików... Composed by Skomponowany przez Configure AI Skonfiguruj AI Configure AI setttings Skonfiguruj ustawienia AI Congratulation\nScenario was complete in\n%i months %i years. Gratulacje!\nScenariusz został ukończony \nw %i mięsiecy i %i lat! Connected stops Połączony z: Connected with server Połącz z serwerem Connections Połączenia Constructed by Autor Constructed by %s Autor: %s construction speed Szybkość budowy Construction_Btn Koszty konstrukcji Consumed zużyto contains the following doubled objects:

zawiera następujące podwójne obiekty:

convoi %d of %d Skład %d z %d convoi error tooltips podpowiedzi dotyczące błędów pojazdów Convoi following mode Tryb śledzenia składu Convoi has been sent\nto the nearest depot\nof appropriate type.\n Pojazd został\nwysłany do najbliższej\nbazy. Convoi is sold when all wagons are empty. Pojazdy zostaną sprzedane gdy wszystkie wagony będą puste. convoi mouseover tooltips podpowiedzi po najechaniu myszą na pojazd convoi passed last\nmonth %i\n \nskłady przej. przez\nostatni miesiąc: %i\n Convois Konwoje Convois: %d\nProfit: %s Pojazdów: %d\nZysk: %s Convoys Pojazdy Copy Convoi Kopiuj Copy the selected convoi and its schedule or line Skopiuj zaznaczone składy i ich rozkład jazdy Copy to clipboard Skopiuj do schowka Cost Koszt cost for removal Koszty usunięcia Cost per unit Koszt jednostkowy Cost: %8s (%.2f$/km %.2f$/m)\n Koszt: %8s (%.2f$/km %.2f$/m)\n Cost: %8s (%.2f$/km)\n Koszt: %8s (%.2f$/km)\n Costs Koszty Create a new line based on this schedule Utwórz nową linię na bazie tego rozkładu curiosity builder Budowanie atrakcji curlist_title Lista atrakcji Currently playing: Teraz odtwarzane: Customers live in: Klienci mieszkają w: Date format Format daty deactivated in online mode Dezaktywowany w trybie online Dec. Grudzień Deccelerate time Zwolnij czas December Grudzień decrease underground view level zmniejsz poziom widoku pod ziemią Del Stop Usuń Delete Line Usuń linię Delete the current stop Usuń bieżący przystanek Delete the selected line (if without associated convois). Usuń wybraną linię (jeśli nie ma powiązanych składów). Delete this file. Skasuj ten plik Delivered Dostarczono Demand Popyt Demand: %.0f MW Popyt: %.0f MW\n Denkmal Pomnik Departed Wysłanych Departs at Odjazd o Departure after Wyjazd po Departure board Tablica odlotów Departures per month Odjazdów na miesiąc Departures to\n Odjazdy do\n Depot Baza Depots Bazy Der Tunnel ist nicht frei!\n Tunel nie jest pusty.\n Destination Przeznaczenie Destroying map ... Niszczenie mapy Details Szczegóły Die Bruecke ist nicht frei!\n Most nie jest pusty.\n diesel spalinowy directmail Poczta bezpośrednia Direkt erreichbare Haltestellen Bezpośrednie połączenie z disable midi Wyłącz muzykę Midi Display bars above factory to show the status Wyświetl paski nad fabryką, aby pokazać status Display settings Ustawienie wyświetlania Distance Odległość distributing cities Dystrybucja miast distributing factories Dystrybucja fabryk Do not show Nie pokazuj Dock Port Dock must be built on single slope! Port musi być budowany na prostym zboczu. Downloading Pobieranie dp_title Lista baz Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Masz %d miesięcy na spłatę zadłużenia. Durchsatz Max. prod. Economy Ekonomia Eigenbesitz\n Obiekt publiczny\n Ein %s\npasst hier nicht.\n '%s'\nie pasuje\nw tym miejscu!\n Einstellungen Ustawienia Einstellungen aendern Zmień opcje electric elektryczny Electricity Elektryczność Electricity producer\n\n Producent energii elektrycznej\n\n Electrics_tab Trakcja elektryczna Electrify track Elektryfikuj trasę enlarge map Powiększ mapę enter a value between %i and %i wprowadź wartość pomiędzy %i - %i Enter address Wpisz adres IP Enter Password Zmień nazwę gracza i hasło Error Błąd Erzeuge neue Karte.\n Proszę czekać.\nTworzę mapę.\n\n (Może to potrwać\n kilka minut)\n\n\n Es wird bereits\nein Fahrplan\neingegeben\n Rozkład już\nzaplanowano.\nMusisz najpierw\nusunąć stary rozkład!\n Extract files Rozpakuj pliki Fabrikanschluss Pobliskie zakłady Fabrikname nazwa fabryki Factories Fabryki factory details Szczegóły fabryk factorybuilder Buduj fabryki Fahrplan Rozkład jazdy Fahrtziel Przeznaczenie: Fahrzeuge koennen so nicht entfernt werden Pojazdy nie mogą\nbyć cofnięte\ntą drogą!\n Fahrzeuge: Pojazdy: Farbe Kolor gracza Fast forward Przyspiesz Feb. Luty February Luty Ferry_tab Pasażerskie Fertig Gotowe Filename Nazwa pliku: Files from: Pliki z: Filter: Filtr: Finances of %s Finanse %s Finanzen Finanse find mismatch Sprawdź pliki fl_title Lista fabryk Flug_tab Pasażerskie follow me Śledź Follow the convoi on the map. Podążaj za pojazdem na mapie font size Rozmiar czcionki Forest Lasy Found new city Załóż nowe miasto Fracht Pojemności magazynowej Frame time: Czas klatki: Free Capacity Pusta ład. freeplay mode Bez bankructwa Freight Transport Friction: Tarcie: fuel_cell Ogniwa paliwowe Full load Ładuj do: Fullscreen (changed after restart) Pełny ekran (wymagany restart) Fussgaenger Pieszy Game info Multiplayer GAME PAUSED GRA WSTRZYMANA Game_msg Ogólne Gear: Przełożenie: Gebaeude Budynek General Ogólne Generated Wytworzono Generation: %.0f MW Wytwarzanie: %.0f MW Gewicht Masa Gewinn Dochód: Give the selected vehicle(s) an individual schedule Nadaj wybranemu pojazdowi indywidulany rozkład. gl_btn_sort_catg Kategoria gl_title Lista towarów go home Do bazy Goods Towary Goods AI Towary AI Goods list Lista towarów Gross Profit Przychód całkowity Groundobj Objekt groundobj builder Budowa obiektów naziemnych Grow city Rozwój miasta. Growth Rozwój miasta GUI settings Ustawienia graficznego interfejsu użytkownika H Przystanek Happy Zadowoleni Has slope graphics Posiada grafikę nachylenia Has Snow Ma śnieg Haus kaufen Kup dom Helligk. Grafika Help Pomoc Help text not found Nie znaleziono pomocy hide all building Schowaj wszystkie budynki hide city building Schowaj budynki miejskie hide objects under cursor Ukryj obiekty pod kursorem hide station names Ukryj nazwy przystanków hide transparent Schowaj przezroczyste hide trees Schowaj drzewa Hier warten/lagern: Lista czekajacych ludzi/towarów: Higher transport fees, crossconnect all factories Aby uzyskać więcej zysków z transportu, dezaktywuj Just-in-time. Highlite schedule Harmonogram najważniejszych wydarzeń hl_title Lista stacji hl_txt_filter Filtr: hl_txt_sort Sortuj wg: hlf_chk_airport Lotniska hlf_chk_anleger Porty hlf_chk_bahnhof Dworce kolejowe hlf_chk_bushalt Przystanki hlf_chk_frachthof Składy towarów hlf_chk_keine_verb Bez połączenia hlf_chk_maglevstop Przystanek kolei magnetycznej hlf_chk_monorailstop Przystanek jednotorówki hlf_chk_name_filter Nazwa filtru: hlf_chk_narrowgaugestop Przystanek wąskotorówki hlf_chk_overflow Ponad rozmiar hlf_chk_spezial_filter Filtr specialny: hlf_chk_tramstop Przystanek tramwajowy hlf_chk_type_filter Typ filtru: hlf_chk_waren_abgabe Produkcja: hlf_chk_waren_annahme Zapotrzebowanie: hlf_title Filtruj listę stacji Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Baza nie może zostać\nodnaleziona. Musisz\nwysłać ręcznie. Homeless Bezdomni hydrogene Wodór Idle: Bezczynni: ignore climates Ignoruj klimat In the industry legend show only currently existing factories W legendzie branżowej pokazuj tylko aktualnie istniejące fabryki In Transit W drodze Increase Industry density Zwiększ gęstość przemysłu increase underground view level zwiększ poziom widoku pod ziemią industrial building Budynek przemysłowy Industry overlay Interfejs fabryki Infinite mouse scrolling Nieskończone przewijanie mapy Infinite scrolling using mouse Nieskończone przewijanie za pomocą myszy Init map ... Inicjalizacja mapy...\n\n Input Wejście Ins Stop Wstaw Insert stop before the current stop Wstaw przystanek przed bieżącym przystankiem Install graphics Zainstaluj grafikę Install paks Zainstaluj paczki Insufficient funds! Brak wystarczających funduszy! Intercity road len: Długość tras międzymiast.: Intro. date Data wprowadzenia Intro. date: Wprowadzony: invalid nieważny Invalid coordinate Błędne współrzędne Invert stops Odwróć przystanki isometric map Mapa izometryczna Jan. Styczeń January Styczeń join game Graj online July Lipiec Jump to Skocz do June Czerwiec Kann Spielstand\nnicht laden.\n Nie mogę odczytać\nzapisanej gry!\n Kann Spielstand\nnicht speichern.\n Nie mogę otworzyć\n gry do zapisu!\n Kein Besitzer\n Brak właściciela keine brak Keine Einzelfahrzeuge im Depot Żadnych pojazdów w zajezdni Keyboard_Help\n Pomoc dotycząca klawiatury\n koord koordynaty Kreuzung Skrzyżowanie labellist_title Lista etykiet Lade Relief Wczytaj mapę Laden Wczytaj Land attraction Atrakcja turystyczna Land industries Fabryk poza miastem: LANG_CHOOSE\n Wybierz\njęzyk:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month Zeszły miesiąc: Last used tools Ostatnio użyte narzędzie Last Year Zeszły rok: Leaving depot! Opuszczam zajezdnię! leer pusty legacy (large heights) Dziedzictwo (duży wzrost) legacy (small heights) Dziedzictwo (mały wzrost) Legend Legenda Leistung Moc Leistung: %d kW Moc: %d kW Leitung Linie elektryczne length: %d Długość: %d letzen Monat: diesen Monat: zeszły miesiąc: ten miesiac: Line Linia Line Management Zarządzanie liniami line name mouseover tooltips Informacje o nazwie linii po najechaniu kursorem linear Liniowy Lineless convoys serving this stop Pojazdy obsługujące ten przystanek bez przypisanej linii Lines serving this stop Line obsługujące ten przystanek List of industries on the map Lista fabryk na mapie LKW_tab Ciężarówki Load game Załaduj grę load height data from file Wczytuje plik z mapą. Load mode: Tryb ładowania: Load scenario Scenariusz Load script tool Załaduj narzędzie skryptu Load Scripted AI Załaduj AI ze skryptu loaded załadowane loaded passenger/freight Sortuj pas./tow. wg: Loading (%i->%i%%)! Ładowanie (%i->%i%%) Loading (%i%%) departure %s! Ładowanie (%i%%) odjazd %s! Loading (%i%%)! Ładowanie (%i%%)! Loading addon paks ... Ładowanie paczek addon ... Loading map ... Wczytuję mapę... Loading paks ... Ładowanie paczek Loading skins ... Ładowanie skórek ... Loading time: Czas ładowania: Lock game Zablokuj zmianę gracza (wymaga potwierdzenia) Lokomotive_tab Lokomotywy maglev vehicle Pojazd kolei magnetycznej maglev_track Tory Maglev Maglevdepot Lokomotywownia (Maglev) Mail Demand %d\n Popyt poczty %d\n Mail level Poziom poczty Mailbox Skrzynka pocztowa Mailbox Options Opcje skrzynki pocztowej Maintenance Obsługa make stop public (or join with public stop next) costs %i per tile and level Utwórz/połącz z sąsiednim publicznym przystankiem (kosztuje %i$ za pole i stopień) Make way or stop public (will join with neighbours), %i times maintainance Ustąp drogi lub przestań być publiczny (dołączy do sąsiadów), %i-krotność utrzymania Manual (Human) Nowy gracz Manufactured: Wyprodukowane: Map roughness Nierówn. mapy: map view Widok mapy map zoom Skala mapy Mar. Marzec March Marzec Margin (%%) Margines zysku Marker Etykietka max Max Max Boost (%%) Maksymalny wzrost (%%) Max income: Maks. dochód: Max. speed Maks. prędkość Max. speed: Prędkość max: Max. waiting time Maksymalny czas oczekiwania Maximum 254 stops\nin a schedule!\n Maksymalnie 254\nprzystanki na trasie!\n maximum length of rivers Max długość rzek Maximum tile height difference reached. Maksymalna różnica\nwysokości pomiędzy\ndwoma polami\nzostała osiągnięta. Maxspeed Prędkość maksymalna May Maj Median Citizen per town Średnia mieszkańców/miasto: Meldung Komunikat Menge Ilość merge stop Przystanek transferowy MessageOptionsText \nNowy rok\n\niadomości AI\n\nWiadomości miasta\n\nBrak trasy\n\nNowe Fabryki\n\nChat\n\nNowe pojazdy\n\nStacja pełna\n\nProblemy\n\nKorki\n\nScenario min Min. minimum length of rivers Min długość rzek Minimum load Minimalny załadunek (%%) Missing pakfiles Brakujące pliki! Modify the selected line Edytuj wybraną linię Monate alt miesięcy. Monorail Jednoszyn. monorail vehicle Pojazd jednoszyn. monorail_track Tor kolei jednoszynowej Monorailboden Kolej jednoszynowa (wspornik) Monoraildepot Zajezdnia jednoszynowa Month Miesiąc month wait time miesięczny czas oczekiwania Monthly departures Stałe godziny odjazdów Months Miesiące Monuments Monumenty Mountain height Wysokość gór: Move the map Przesuń mapę Movingobj Przesuń objekt Music playing disabled/not available Muzyka wył/niedostępna. Music volume: Głośność muzyki: mute sound wycisz Name Nazwa Narrowgauge Kolej wąskotorowa Narrowgauge are not available yet! Kolej wąskotorowa nie jest jeszcze dostępna! narrowgauge vehicle Pojazd kolei wąskotorowej narrowgauge_track Tory o wąskim prześwicie Narrowgaugedepot Lokomotywownia kolei wąskotorowej Net ID: %p Identyfikator sieciowy: %p\n Net Wealth Wartość całkowita Net wealth near zero Majątek netto bliski zeru Neue Karte Nowa mapa Neue Welt Utworz nowy świat new convoi Nowy skład New Line Nowa linia New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Utworzono nową linię!\nMożesz przypisac linię\npoprzez wybranie jej z\n listy wyboru powyżej. New Vehicles Nowe pojazdy Nickname: Nazwa gracza: no buildings hidden Bez ukrytych budynków no convois Brak pojazdów No goods are loaded onto this convoi. Pojazdy nie podlegają załadunkowi. no goods waiting Pusty no load bez ładowania No player Brak gracza No Route Bez połączenia No stop here! Tu nie ma przystanku No suitable ground! Złe podłoże! No terminal station here! Nie ma tutaj stacji! no timeline Bez osi czasu no tree bez drzew No vehicles are available for purchase. Brak możliwości zakupu pojazdów No. of Factories Liczba fabryk Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Najpierw ustal\nrozkład jazdy!\n none Nic nord Północ nordost Północny wschód nordwest Północny zachód Not allowed to make publicly owned ways! Niedozwolone jest tworzenie dróg publicznych! Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Rozkład jazdy tego\npojazdu nie może być zmieniany\n w czasie znajdywania trasy. Not enough fields would remain. To pole obsiane zbożem jest\nwłasnością prywatną, \na właściciel nie wyraża zgody \nna jego sprzedaż. Nie możesz \nwięc zrównać go z ziemią. Nov. Listopad November Listopad now Teraz Now active as %s.\n Aktywny teraz jako %s.\n nowhere Nigdzie Number of rivers Ilość rzek Object Objekt Oct. Październik Odometer: %s km Przebieg Ok OK Oktober Październik On loan since %i month(s) Na debecie od %i miesiąc/miesięcy. On mouseover Po najechaniu kursorem myszy on the wyjazd/miesiąc pierwszy o (dd-gg-mm) On this map, you are not\nallowed to change player!\n Ta gra jest zablokowana.\nNie można zmieniać gracza.\n Only city chains Tylko sieci miejskie Only first %d differing paks reported. There are probably more. Zgłoszono tylko pierwsze %d różnych pakietów. Prawdopodobnie jest ich więcej. Only full Unicode fonts Tylko pełne czcionki Unicode Only land chains Tylko sieci lądowe Only one transformer per factory! Tylko jeden transformator na jedną fabrykę! Only show goods which are currently handled by factories Pokazuj tylko towary, które są obecnie obsługiwane przez fabryki open otwarty Operation Koszty ruchu Ops Profit Przychody Optionen Opcje Or enter a server manually: Lub połącz się ręcznie z serwerem: Origin Pochodzenie ost wschód Output Wyjście Ownership Posiadacz Pak which may cause severe errors: Pak może powodować poważne błędy: Pak which may cause visual errors: Pak może powodować błędy wizualne: Pak(s) different: Różnice w plikach Pak(s) missing on client: Brakujące pliki: Pak(s) not on server: Zbędne pliki: Pakset differences Różnice w pakietach paletten Palet Pas_tab Pociągi pasażerskie Passagiere Pasażerowie Passagierrate Poziom pasażerów Passagierziele Cele pas./pocz. Passenger AI SI przewozów pasażerskich Passenger Demand %d\n Popyt pas. %d\n Passengers %d %c, %d %c, %d no route Pasażerowie %d %c, %d %c, %d brak trasy Passengers %d %s, %d %s, %d no route Pasażerowie %d %s, %d %s, %d brak trasy Password Hasło Pause Pauza/Wznów Pax <%i> Mail <%i> Pasażerowie <%i> Poczta <%i> Pax level Liczba pasażerów PaxDest Cel podróży Percent Electricity Procent zelektryfikowania Planes are not available yet! Samoloty jeszcze nie są dostępne. Plant tree Zasadź drzewa. player Gracz player -1 Człowiek player 0 Transport Publiczny player 1 Napik 128 AS player 10 Gracz 10 player 11 Gracz 11 player 12 Gracz 12 player 13 Gracz 13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Spedition VM player 5 H-Trans Ltd player 6 PSK & Co KG player 7 Gracz 7 player 8 Gracz 8 player 9 Gracz 9 Please choose vehicles first\n Najpierw wybierz pojazd.\n Post Poczta Postrate Poziom poczty Power Moc Power (MW) Moc (MW) Power: Moc: Power: %4d kW\n Moc: %4d kW\n Powerlines Linie elektr. Price Cena Problems_msg Problemy Produced Wyprodukowano Produces: %.1f units/minute Produkuje: %1f jednostek na minutę Production of %s has been stopped:\n%s\n Produkcja %s została zatrzymana:\n%s\n Production/Boost Produkcja/wzrost Produktion Wydobycie/Produkcja Profit Zysk promote to line Utwórz linię q1 Wiosna q2 Lato q3 Jesień q4 Zima Query server Pokaż informacje o serwerze rail car wagon kolejowy random losowy Random age losowy rok Random map Losuj mapę Rathaus Ratusz Rating Ocena ratio_pax Proporcja Relevant Niezbędne Reliefkarte Mapa Remove Usuń remove airstrips Usuń pas startowy remove channels Usuń kanały remove interm. signals Usuń sygnały pośrednie remove maglev tracks Usuń tory remove monorails Usuń szynę remove narrowgauge tracks Usuń tory remove powerlines Usuń linię wysokiego napięcia remove roads Usuń drogę remove signal Usuń sygnał remove tracks Usuń tory Remove wayobj %s Usuń %s replace other signals Zamień inne sygnały replace stop Przenieś przystanek request closing żądanie zamknięcia residential house Dom Restore natural slope Przywróć naturalne zbocze. Restwert: Cena sprzedaży: Retire date Data wycofania Retire. date: Data wycofania: return ticket Trasa powrotna Revenue Przychody Revert schedule Przywróć rozkład jazdy Revision: Rewizja: road droga road vehicle pojazd drogowy Roadsign Znak drogowy Rotate Building Obróć budynek Rotate map Obróć mapę o 90° Rotation Obrót Routing Obliczanie trasy Running cost Koszty ekploatacji sack Paczek sail Siła wiatru Save Zapisz Saving map ... Zapisuję mapę... Scenario complete: %i%% Scenariusz kompletny w %i%%. Scenario Debug Debugowanie scenariusza Scenario Error Log Log błędów scenariusza Scenario Goal Cel scenariusza Scenario Info Informacje o scenariuszu Scenario information Informacje o scenariuszu Scenario Result Rezultaty scenariusza Scenario Rules Reguły scenariusza Scenario_ Scenariusz Schedule changing! Zmieniono rozkład jazdy! Schienentunnel Zbuduj tunel kolejowy Schiff_tab Statki Schiffdepot Stocznia Schleppkahn_tab Barki Screen scale: Skala ekranu Screenshot Zrzut ekranu. Scroll threshold Próg przemieszczenia Search: Szukaj: Seasons Pory roku Sehenswuerdigkeit Atrakcja turystyczna Select a server to join: Wybierz serwer żeby dołączyć: Select a theme for display Wybierz motyw do wyświetlenia Select display font Wybierz czcionkę do wyświetlenia Select one or more graphics to install (Ctrl+click): Wybierz jedną lub więcej grafik do zainstalowania (Ctrl+klik): Select soundfont Wybierz ścieżkę dźwiękową Sell the selected vehicle(s) Sprzedaj wybrany pojazd sended Wysłanych SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 Sept. Wrzesień September Wrzesień Served by Obsługiwany przez Served by me Obsługiwane przeze mnie Server did not respond! Serwer nie odpowiada! Serves Line: Obsługuje linię: Service Obsługa set signal spacing Ustal odległość sygnałów Setting Ustawienia Ship Wodne shops and stores Sklepy i biura Show all Pokaż wszystko show all building Pokaż wszystkie budynki Show also vehicles no longer in production. Pokaż również pojazdy wycofane z produkcji. Show also vehicles that do not match for current action. Pokaż również pojazdy nie pasujące. Show always Zawsze pokazuj Show climates Pokaż klimaty Show contour Pokaż kontury Show even servers with wrong version or pakset Pokaż nawet serwery z niewłaściwą wersją lub zestawem pakietów Show finances for transport type Pokaż finanse dla rodzaju transportu Show future Pokaż przyszłe pojazdy show grid Pokaż siatkę Show industry Pokaż fabryki Show legend Pokaż legendę Show map scale Pokaż skalę Show mismatched Pokaż niedopasowane Show networks Sieci Show obsolete Pokaż zużyte pojazdy Show offline Pokaż offline Show only used pokaż tylko używane Show outline Pokaż zarys Show schedules Pokaż linie Show servers that are offline Pokaż serwery offline Show servers where game version or pakset does not match your client Pokaż serwery, na których wersja gry lub zestaw pakietów nie pasuje do Twojego klienta show station coverage Pokaż zasięg stacji show station names Pokaż nazwy stacji show waiting bars Pokaż wykresy czasu czekania show/hide block reservations Pokaż/ukryj rezerwację bloku Show/hide estimated arrival times Pokaż/ukryj szacowany czas przybycia show/hide object owner Pokaż/ukryj właściciela obiektu Show/hide statistics Pokaż/ukryj statystyki Shows buttons about networks overlay. Pokaż przyciski nakładki sieci Shows buttons on network overlay. Pokaż opcje nałożenia sieci połączeń. Shows buttons on special topics. Pokaż interaktywną mapę Shows consumer/suppliers for factories Pokazuje połączenia dostawcy-odbiorcy dla fabryk Shows the color code for several selections. Pokazuje kod koloru dla kilku opcji. Shows the currently selected schedule Pokazuje aktualnie wybraną linię Shrink city Zmniejsz miasto shuffle midis Odtwarzaj losowo Signal Semafor signal spacing Odstęp sygnału Sim: Simpętle: Similar view as the main window Widok podobny jak w oknie głównym Single toolbar only Tylko pojedynczy pasek narzędzi Size (%d MB): Wielkość (%d MB): Size (area) Rozmiar (terenu) sliced underground mode Płaszczyzna mapy slot empty pusty Smart hide objects Inteligentne ukrywanie obiektów Sort by Sortuj wg Sort by: Sortuj według: Sort waiting list by Posortuj listę według Sound Dźwięk Sound settings Ustawienia dźwięku Sound volume: Poziom dźwięku: Soundfonts are located in the music directory. Ścieżki dźwiękowe znajdują się w katalogu muzycznym. special freight Towar specjalny Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Bonus za szybkość\ndroga %i km/h, kolej %i km/h\nstatki %i km/h, samoloty %i km/h Speedlimit Prędkość maks. Speichern Zapisz Spieler Gracz Spieler(mz) Gracze Spielerliste Lista graczy Spielstand wurde\ngeladen!\n \nGrę załadowano!\n Spielstand wurde\ngespeichert!\n \nGra została zapisana!\n Sprache Język Sprachen Języki Stadtinformation Statystyka miasta Start the selected vehicle(s) Uruchom wybrany pojazd Starte Spiel Rozpocznij grę station labels Etykiety przystanków Station tiles: Wielkość stacji: Station_msg Przepełnienia Status Stan steam parowy Step timeline one year Przesuń czas o rok do przodu Stops Stacje Storage Magazyn Storage capacity Pojemność magazynowa Strassendepot Zajezdnia Strassentunnel Zbuduj tunel drogowy street car Samochód sued Południe suedost Południowy wschód suedwest Południowy zachód Summer snowline Śnieg w lecie (linia) Supplied: %.0f %% Dostarczone: %.0f %% Suppliers Dostawcy Tage alt dniowe The following graphics are unmaintained: Poniższe grafiki są nie obsługiwane: The gradient does not fit a tunnel Nachylenie nie pasuje do tunelu Theme selector Wybór motywu There are still vehicles\nstored in this depot!\n W zajezdni/lokomotywowni\nnadal znajduje się\npojazd!\n This Month W tym miesiącu: This Year Bieżący rok: Tile not empty. Wyczyść pole zanim\nużyjesz Narzędzi zbocza. timeline Użyj osi czasu tl_title Lista miast To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Aby zachęcić turystów\n\nw %s wybudowano\n\n%s\n\nz pomocą %i\n\npodatników. To heavy traffic\nresults in traffic jam.\n Duży ruch\n\nspowodował korki. Toggle day/night view Przełącz widok dzień/noc Toggle vehicle tooltips Przełącz podpowiedzi dotyczące pojazdów tonnen t Toolbar position: Pozycja paska narzędzi Total inhabitants: Mieszkańców: Tourist attractions Atrakcji turystycznych: Tourists Atrakcje Town_msg Rozwój miasta Town: %s\n Miasto %s.\n Towns Miasta track trasa Tracks Tory Traffic Ruch traffic settings Ustawienia ruchu Train Kol. Trains are not available yet! Pociągi jeszcze nie dostępne. Tram Tramwaj tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. Trolejbus %i km/h, jednotorówka %i km/h\nkolej magnetyczna %i km/h, wąskotorówka %i hm/h. tram_track Trasa trolejbusa Tramdepot Zajezdnia tramwajowa Trams are not available yet! Tramwaje jeszcze nie dostępne. Transferring game ... Przesyłanie gry ... Transformer only next to factory! Stacja transformatorowa musi być postawiona przy fabryce! Translation Tłumaczenie transparencies Przezroczystość transparent background Przezroczyste tło transparent station coverage Przezr. zasięg stacji Transported Przewiezione Trees disabled! Drzewa nieaktywne! TrolleyBus_tab Trolejbusy Truck Drogowe tt_Other Inny Tunnel muss an\neinfachem\nHang beginnen!\n Tunel musi\nzaczynać się na \nrównym zboczu!\n Tunnel must start on single way! Tunel musi zaczynać się na pojedynczej drodze. Tunnelboden Tunel underground mode Tryb podziemi UNDO failed! Nie można cofnąć.\n\nCofać trasę można do czasu\n\npostawienia semaforów\n\nstacji/przystanków. Undo last ways construction Cofnij ostatnią budowę trasy. Unemployed Bezrobotni Unhappy Niezadowoleni units/day /dzień, Unloading (%i%%)! Rozładowywanie (%i%%)! Update Line Zmień linię upgrade HQ Rozbuduj siedzibę Usage: %.0f %% Użycie: %.0f %% Usage/Output Zużycie/produkcja prądu Use beginner mode Tryb początkującego Use timeline start year Rozpocznij od roku: Vehicle %s can't find a route! Pojazd %s\nnie może znaleźć drogi! Vehicle %s is stucked! Pojazd %s utknął! Vehicle count: Liczba pojazdów: Vehicle details Szczegóły pojazdu Vehicle Name Nazwa pojazdu Vehicle Power Moz pojazdu vehicles stored Przechowywane pojazdy Verbrauch Zużycie Vergroessere die Karte\n Powiększ mapę.\n Verkauf Sprzedano! verkaufen Sprzedaj Verkehrsteilnehmer Użytkownik drogi Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Masz dlugi!\n\nDostałeś %d miesięcy\nna spłatę długu!\n vh_title Lista wszystkich pojazdów via Przez via %s\n przez %s\n via Menge przez (ilość) voranstellen Dostaw z przodu Waggon_tab Wagony waiting Czekających Waiting for clearance! Czekam na wolną drogę! Walked pieszo walking Pieszo Warnings_msg Wiadomość o korkach Wasser Woda Water Kanał Water level Poziom wody: water vehicle Pojazd wodny way %s cannot longer used:\n Droga typu %s nie może być dalej używana:\n way %s cannot longer used:\n%s\n Droga typu %s nie może być dalej używana:\n\n%s\n way %s now available:\n Droga %s jest dostępna:\n Way toll Cło Ways not connected Trasa nie jest połączona!\n waytype Rodzaj drogi Wegpunkt Punkt Weight Waga Weight: Waga: Wert Wartość west Zachód Wind direction Kierunek wiatru Winter snowline Śnieg w zimie (linia) withdraw Wycofaj Withdraw All Wycofaj wszystko WRONGSAVE Nie można odczytać\npliku z poprzedniej\nwersji gry.\n Year Rok Year %i has started. Rozpoczął się rok %i. YEAR_SYMBOL r. Years Lata Your primary color: Główny kolor Your secondary color: Podrzędny kolor Zielort Przeznaczenie zooming in Powiększ zooming out Oddal Zu nah am Kartenrand Za blisko brzegu mapy\naby cokolwiek\nzbudować!\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nowy rekord prędkości kolei magnetycznej: %.1f km/h osiągnięty przez %s. New world record for monorails: %.1f km/h by %s. Nowy rekord prędkości kolei jednoszynowej: %.1f km/h osiągnięty przez %s. New world record for motorcars: %.1f km/h by %s. Nowy rekord prędkości samochodów: %.1f km/h osiągnięty przez %s. New world record for narrowgauges: %.1f km/h by %s. Nowy rekord prędkości kolejki wąskotorowej: %.1f km/h osiągnięty przez %s. New world record for planes: %.1f km/h by %s. Nowy rekord prędkości samolotów: %.1f km/h osiągnięty przez %s. New world record for railways: %.1f km/h by %s. Nowy rekord prędkości kolei: %.1f km/h osiągnięty przez %s. New world record for ship: %.1f km/h by %s. Nowy rekord prędkości statków: %.1f km/h osiągnięty przez %s. #________________________________script_ai_text_________________________________ #________________________________script_ai_text_________________________________ %s build additional convoy to line: %s %s Stwórz dodatkowy pojazd do linii: %s %s build rail line from %s (%s) to %s (%s) %s Stwórz linię kolejową z %s (%s) do %s (%s) %s build road line from %s (%s) to %s (%s) %s Stwórz linię drogową z %s (%s) do %s (%s) %s build ship line from %s (%s) to %s (%s) %s Stwórz linię wodną z %s (%s) do %s (%s) %s extends the route from %s (%s) to %s (%s) %s wydłuża trasę z %s (%s) do %s (%s) %s optimize way line from %s (%s) to %s (%s) %s optymalizuje trasę z %s (%s) do %s (%s) %s removes convoys from line: %s %s usuwa pojazdy z linii: %s vehicles of the line %s were retired Pojazdy linii %s zostały wycofane #_________________________________tooltip_text__________________________________ #_________________________________tooltip_text__________________________________ Add convois with similar schedule to this line. Dodaj do tej linii pojazdy o podobnym harmonogramie. Enter intervall in days, hours, minutes Wprowadź odstęp w dniach, godzinach, minutach Mirrors order of stops Odzwierciedla kolejność przystanków Revert to original schedule Przywróć oryginalny harmonogram #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL bryg &1_CITY_SYLL in &2_CITY_SYLL ów &3_CITY_SYLL inów &4_CITY_SYLL iny &5_CITY_SYLL in &6_CITY_SYLL owo &7_CITY_SYLL owice &8_CITY_SYLL ice &9_CITY_SYLL iska &A_CITY_SYLL ica %1_CITY_SYLL Grab %2_CITY_SYLL Ryp %3_CITY_SYLL Koz %4_CITY_SYLL Biel %5_CITY_SYLL Os %6_CITY_SYLL Wierzb %7_CITY_SYLL Kon %8_CITY_SYLL Dęb %9_CITY_SYLL Lip %A_CITY_SYLL Wol %B_CITY_SYLL Rudź %Z_CITY_SYLL Zim 1center %s %s 1extern %s %s Zamieście 2center %s %s Centralny 2extern %s %s Kopalnia 3center %s %s Główny 3extern %s %s Fabryczny 4center %s Śródmieście 4extern %s %s Zewnętrzny 5center %s %s Robotniczy 6center %s Centrum %s 7center %s miasto %s simutrans-124.5/simutrans/text/pl/000077500000000000000000000000001520675460500172105ustar00rootroot00000000000000simutrans-124.5/simutrans/text/pl/color.txt000066400000000000000000000004571520675460500210750ustar00rootroot00000000000000POMOC: wybór koloru

Wybór koloru

Mozesz wybrac dla siebie odpowiedni kolor. Tym kolorem beda zaznaczone Twoje posiadlosci (budynki, tory itp.) Zwróc uwage, ze nie wszystkie nalezace do Ciebie budynki/pojazdy beda w tym kolorze (tak zadecydowali autorzy rysunków).

simutrans-124.5/simutrans/text/pl/depot.txt000066400000000000000000000050431520675460500210660ustar00rootroot00000000000000Zajezdnia i Lokomotywownia

Zajezdnia samochodowa & Lokomotywownia

W zajezdni mozesz kupic, sprzedac i ustalic rozklad jazdy dla wszystkich pojazdów kolowych, do niej nalezacych.

Menu zajezdni zawiera informacje o pojazdach które mozesz kupic i tych które wlasnie kupiles. W górnej czesci okna, po prawej, wyswietlone sa wszystkie pojazdy które mozesz kupic. Na glównej zakladce znajduja sie samochody, autobusy i lokomotywy a na drugiej zakladce przyczepy i wagony. (W zaleznosci w zaleznosci od tego czy jest to Zajezdnia czy Lokomotywownia) U góry z lewej znajduje sie wizerunek wlasnie zestawionych pojazdów (samochód+przyczepa lub lokomotywa+wagony).

Nizej znajduja sie wszystkie informacje o danym pojezdzie a na samym dole przyciski:

'Start' wlacza dany pojazd do ruchu. Wczesniej jednak pojazd musi miec ustalony 'Rozklad jazdy'. Dzieki opcji 'Sprzedaj' mozesz sprzedac dowolny pojazd, przyczepy czy wagony.

Po najechaniu kursorem na wizerunek dowolnego pojazdu bedziesz mógl odczytac wszystkie wazne informacje o tym pojezdzie. Aby kupić wybrany pojazd wystarczy na nim kliknac. Pojawi sie on wtedy w lewej czesci okna na górze. Po kupieniu nastepnego pojazdu pojawi sie on na górze, a wczesniej kupiony nieco nizej. Jesli pojazd posiada wagony/przyczepy to pojawia sie one pod wszystkimi pojazdami.

Aby zestawic pojazd z wagonami/przyczepami najpierw nalezy kupic pojazd, a nastepnie przejsc na zakladke 'Przyczepy' lub 'Wagony' i wybrac odpowiednie wagony/przyczepy. Mozna tez dokupic wagony/przyczepy do pojazdu ktory ich nie posiadal. W jednym czasie mozesz pracowac tylko nad jednym pojazdem. Aby sprzedac pojazd wybierz 'Sprzedaj' w menu zajezdni/lokomotywowni a nastepnie kliknij na pojazd który zamierzasz sprzedac. Na ekranie podana jest cena jaka mozesz uzyskac ze sprzedarzy. Cena ta wraz z uplywem czasu jest coraz nizsza. Uwaga! pojazd który chcesz sprzedac musi znajdowac sie w zajezdni/lokomotywowni.

W celu ustalenia rozkladu jazdy wybierz przycisk'Rozklad jazdy'. Pojawi sie okno rozkladu jazdy. Kiedy juz ustalisz przebieg trasy wystarczy kliknac na przycisk 'Start' - pojazd wyruszy w droge.

Aby pojazd skierowac do zajezdni/lokomotywoni musisz usunac z rozkladu jazdy wszystkie przystanki a nastepnie wstawic jako miejsce przystanku odpowiednia zajezdnie/lokomotywownie. Po dotarciu danego pojazdu do zajezdni/lokomotywowni bedzisz mógl go sprzedac lub zmienic ilosc wagonów/przyczep.

simutrans-124.5/simutrans/text/pl/display.txt000066400000000000000000000020141520675460500214130ustar00rootroot00000000000000POMOC: Opcje wyswietlania

Opcje wyswietlanie

Opcje te pozwalaja zmieniac wyglad i dzialanie gry Simutrans.
Opis opcji:

Jasnosc - rozjasnienie/przyciemnienie ekranu gry.
Kolory - bez efektu
Predkosc przewijania - im wyzsza liczba tym szybciej bedzie przewijany ekran.

Odwróc przewijanie - Zmiana kieruku przewijania na przeciwny (Ekran przewija sie trzymajac wcisniety prawy przycisk myszki)
Pokaz pasazerów - Mozna wlaczyc lub wylaczyc przedstawianie przechodniów w grze (Jesli masz slaby komputer wylacz pokazywanie pasazerów
Tryb dzien/noc - Kiedy usawisz ten tryb cyklicznie bedzie nastepowala noc (Ciemny ekran z bardzo ciekawymi efektami!).

Dolna czesc okna zawiera niektóre informacje zwiazane z praca komputera. Opcje te sa wazne jedynie dla prawdziwych 'komputerowców'.
Wazne: - Jesli bedziesz zglaszal problemy lub bledy mozesz byc poproszony o podanie tych informacji.

simutrans-124.5/simutrans/text/pl/finances.txt000066400000000000000000000017161520675460500215440ustar00rootroot00000000000000POMOC: Finanse

Finanse (f)

W tym oknie mozesz obserwowac stan swoich finansów w danym roku i w roku poprzednim.

W oknie znajduja sie ponizsze inforamcje:

Infrastruktura - wszystko co zbudowales: koleje, drogi, budynki, sygnalizacje itp.

Nowe pojazdy - Wszystkie zakupione w danym roku pojazdy/pociagi/statki.

Koszty przejazdów - Koszty zwiazane z utrzymaniem wszystkich pojazdów. (Koszty te dostepne sa w oknie 'Zajezdnia')

Dochód - Twoje dochody zwiazane z dzialalnoscia transportowa.

Utrzymanie - Koszty zwiazane z utrzymaniem budynków, dróg, mostów itp. (Na dole okna znajduja sie miesieczne koszty utrzymania)

Razem - Suma powyzszych kwot, zyski/straty w danym roku.

Balans - Pieniadze które posiadasz. Jest to kwota która mozesz inwestowac i wydawac.

simutrans-124.5/simutrans/text/pl/language.txt000066400000000000000000000020211520675460500215270ustar00rootroot00000000000000POMOC: Języki

Języki

Okno języków pozwala zmienić język używany w grze.

Można je otworzyć za pomocą opcji gry, wyświetla się również przy tworzeniu nowego świata.

Domyślnym językiem gry jest język angielski. Jeśli chcesz grać w innym języku, zaznacz odpowiednie pole przy nazwie języka.

Język może być dowolnie zmieniany podczas gry. Aby go zmienić, wejdź w Opcje gry i kliknij Język.

Zmiana następuje natychmiastowo gdy tworzysz nowy świat lub przy zamykaniu okienka Języki jeśli grasz na wcześniej stworzonej mapie.

Jeżeli tłumaczenie zawiera błędy, niektóre teksty mogą wyświetlać się w języku angielskim. Jeśli tak jest, zgłoś to!

Jeżeli chcesz pomóc w tłumaczeniu gry, odwiedź stronę SimuTranslator - https://translator.simutrans.com
Pamiętaj, każda pomoc się przyda!

simutrans-124.5/simutrans/text/pl/load.txt000066400000000000000000000010471520675460500206720ustar00rootroot00000000000000POMOC: Wczytaj gre

Wczytywanie zapisanej gry

Dzieki tej opcji mozesz kontynuowac rozgrywania wczesniej i zapisana gre. To menu podobne jest do menu 'Zapisz gre'.

Jesli bedziesz chcial wczytac gre której nie ma na dysku otrzymasz inforamacje o bledzie.

WAZNE Gry zapisane w starszych wersjach SimuTrans moga byc niekompatybilne z nowszymi wersjami!!! Szczególowe informacje o mozliwosci uruchomienia starszych gier znajdziesz w dodatkowej dokumentacji na stronie gry.

simutrans-124.5/simutrans/text/pl/map.txt000066400000000000000000000006431520675460500205310ustar00rootroot00000000000000POMOC: Mapa

Okno mapy

Mapa pokazuje wyglad utworzonego swiata z zaznaczeniem miast, rozmieszczenia przemyslu, kolei itp.

Kazdy rodzaj przemyslu oznaczony jest innym kolorem (Legenda kolorów znajduje sie u dolu mapy)

Jesli chcesz powiekszyc poglad mapy kliknij na niej prawym przyciskiem myszy. W ten sposób mozesz wybierac korzystac z dwuch stopni powiekszenia.

simutrans-124.5/simutrans/text/pl/new_world.txt000066400000000000000000000046501520675460500217560ustar00rootroot00000000000000POMOC: Tworzenie nowego swiata

Tworzenie nowego swiata

W tym oknie masz mozliwosc utworzenia nowej mapy. Mozesz przy tym korzystac z wielu opcji (wielkosc, gestosc przemyslu itp.)

Ponizej znajduje sie lista opcji dostepnych przy tworzeniu swiata wraz z ich krotkim objasnienim .

Numer mapy - Określa wygląd świata wraz z iloscia miast, obszarem i polozeniem wody, wzniesien itp. Dla kazdej mapy dostepny jest podglad (Podglad zmienia sie jesli ustawisz opcje dodatkowe). Domyslnie ustawiona jest mapa nr 33.
Wielkosc mapy - okresla wielkosc mapy w kwadratach w zakresi od 128*128 do 640*640. Domyslna wielkoscia jest 256.
Uwaga: Duze mapy wymagaja wiecej pamieci od komputera!

Osrodki przemyslowe - Wielkosc ta oznacza ilosc zakladów przemyslowych/kopaln na mapie. Mala gestosc oznacza trudniejsza gre (trudniej jest ustalic trasy przejazdu, trasy te sa czesto bardzo dlugi) Domyslna gestosc osrodków przemyslowych wynosi 125.
Ilosc miast - Pozwala ustalic ilosc miast na naszej mapie (od 2 do 64 miast). Domyslnie ustawiono 16 miast.
Transport osobowy - Okresla ilosc poruszajacych sie pojazdów prywatnych. Domyslna wielkoscia jest 8.

Poziomo wody - Okresla wysokosc (w kwadratach) poziomu wody. Mniejsza liczba oznacza odsloniecie wiekszej ilosci ladów. Domyslny poziom wody to 4.
Wysokosc gór - Okresla wysokosc najwiekszych gór na mapie. Mniejsza liczba oznacza mniej malych gór i wawozów. Dostepny zakres ustawien 0-160. Domyslna wilkoscia jest 160.
Wygladzanie mapy - Dzieki tej opcji wzniesienia moga byc lagodniejsze (zakres ustawien: 1-3).

Mapa losowa - Losowy wybór mapy (Bez mozliwosci zmian innych efektów).

Pokaz pasazerów - Wlacza/wylacza pokazywanie ludzi poruszajacych sie w miastach. Opcje ta mozna zmienic w czasie gry wybierajac 'Opcje wyswietlania'.
Tryb dzien/noc - Pozwala wybrac cykl dnia/nocy. Opcje te mozna zmienic podczas gry w menu 'Opcje wyswietlania'.

Wczytaj gre - Wczytanie zapisanej wczesniej gry (zobacz tez 'Wczytaj gre')
Rozpocznij gre Tworzy nowa mape na podstawie ustawien tego okna. Wygenerowanie duzej mapy moze zajac troche czasu.
Wyjscie - Wyjscie z gry.

simutrans-124.5/simutrans/text/pl/options.txt000066400000000000000000000014771520675460500214550ustar00rootroot00000000000000POMOC: opcje gry

Opcje gry

W tym oknie mozesz dokonac szczególowych ustawien gry. Ponizej znajduje sie lista ustawien z krótkim opisem.

Jezyk - Umozliwia wybór wersji jezykowych gry.
Kolory graczy - Pozwala wyrac/zmienic kolor twojej firmy.
Wyswietlanie - Opcje wyswietlania, np. tryb dzien/noc
Dzwiek - Ustawienia dzwieku i muzyki.
Gracze - Ustawienia dla graczy CPU.
Wczytaj - Wczytanie zapisanej gry
Zapisz - Zapisuje gre na dysk.
Nowa mapa - Rozpoczecie gry na nowej mapie
Koniec gry - Zakonczenie gry bez jej zapisania.

simutrans-124.5/simutrans/text/pl/players.txt000066400000000000000000000006601520675460500214320ustar00rootroot00000000000000POMOC: gracze

Gracze CPU

W tym oknie masz mozliwosc manipulacji AI graczy oraz ich wlaczania i wylaczania.

Aby wl/wyl gracza CPU wystarczy kliknac na kwadracik obok kolorów graczy. Prosze zauwazyc, ze raz wlaczony gracz CPU bedzie obecny nawet jesli zostanie wylaczony (zostanie tylko 'uspiony').

W oknie tym mozna tez sprawdzic stan funduszy graczy CPU (w tej wersji tylko balans).

simutrans-124.5/simutrans/text/pl/railtools.txt000066400000000000000000000057661520675460500217770ustar00rootroot00000000000000Budowa kolei

Budowa kolei

To okno zawiera narzedzia do budowy torów, sygnalów, tuneli, lokomotywowni, skrzyzowan i stacji. Po wybraniu danego narzedzia, obok kursora pojawi sie jego wizerunek.

W lewym górnym rogu znajduje sie narzedzie do budowy torów. Z jego pomoca mozna budowac tory miedzy dwoma kwadratami. Chcac zbudowac tory kliknij na kwadrat poczatkowy - kursor zamieni sie w buldozer - a nastepnie na kwadrat docelowy. Miedzy tymi kwadratami powstana tory (pola miedzy kwadratami powinny byc wolne). Jesli bedziesz chcial przeciac ulice zastosuj skrzyzowanie, most, lub tunel. Jesli nie bedzi polaczenia miedzy kwadratem poczatkowym i koncowym tory nie zostana wybudowane.

Nastepna ikona pozwala budowac sygnalizacje kolejowe. Sygnaly pozwalaja ustalic ruch pociagów. Dzieki nim dwa pociagi moga poruszac sie po jednym torze. Syganal jednokierunkowy nakazuje pociagowi jazde wyznaczonym torem. Sygnaly jednokierunkowe budujemy w ten sposób, ze stawiamy normalny sygnal i klikamy na niego - wtedy zamieni sie w syganl jednokierunkowy. Kolejne kliknieci spowoduje ze zmieni sie kierunek syganalu, a nastepne zamieni sygnal jednokierunkowy w dwukierunkowy (Sygnaly mozna zmieniac przy pomocy ikony 'sygnaly'). Nalezy uwazac aby zle ustawione sygnaly nie zablokowaly ruchu pociagów. Aby zbudowac sygnal kliknij lewym przyciskiem myszy na torach w miejscu gdzie ma stanac syganl. Sygnaly umieszczane sa na kwadratowym polu. Na jednym polu moze stac tylko jeden sygnal. Aby usunac sygnal uzyj narzedzia 'zniszcz'. Po kliknieciu na niepotrzebny sygnal zostanie on zlikwidowany (tory pozostana nienaruszone).

Ostatnia ikona zawiera narzedzia do budowy tuneli. Tunele sa uzywane do budowy krótkich polaczen przez wysokie góry. Przed skonstruowaniem tunelu najpierw musisz wybudowac tory. Po obu stronach góry w wybranym przez Ciebie miejscu musisz wybudowac zakonczenie torów (koniecznie na tej samej wysokosci po obu stronach!). Nastepnie wybuduj tunel klikjac na narzedzie 'Tunel' i przeciagajac linie pomiedzy utworzonymi zakonczeniami torów.

Kolejna ikona sa 'Skrzyzowania'. Dzieki 'Skrzyzowaniom' tory moga sie nawzajem przecinac. Skrzyzowania budujemy przez klikniecie na torach w miejscu gdzie chcemy je utworzyc.

Nastepnym narzedziem jest 'Lokomotywownia'. W lokomotywowni kupujemy, naprawiamy i sprzedajemy lokomotywy i wagony. Lokomotywnie mozna zbudowac tylko na koncu torów.

Ostatnia ikona to 'Stacja'. Stacje uzywane sa do zaladunku/wyladunku towarów i przewozu ludzi. Stacje moaga miec dowolny ksztalt i rozmiar. Na stacjach mozna przechowywac niograniczona liczbe towarów, ale fabryki przyjmuja towary jesli oczekuje mniej niz 1000 jednostek towaru. Stacje budujemy na skraju torów, ale nie moga powstac na pochylosciach skrzyzowaniach i zakretach. Kazdy segment stacji moze przyjac do 2 wagonów, dlatego upewnij sie czy dany pociag zmiesci sie w calosci na stacji. Wagony nie mieszczace sie na stacji nie moga transportowac towarów/ludzi.

simutrans-124.5/simutrans/text/pl/roadtools.txt000066400000000000000000000033361520675460500217640ustar00rootroot00000000000000POMOC: Budowa dróg

Budowa dróg

W tym oknie znajduja sie narzedzia do budowy dróg, przystanków, tuneli, punktów załadunku, zajezdni itp. Po kliknieciu na dane narzedzie kursor zmieni sie w ikonke obrazujaca to narzedzie.

W lewym górnym rogu znajduje sie ikona budowy dróg. Klikamy w miejscu gdzie droga ma sie rozpoczac (ikona zmieni sie w buldozer) a nastepnie zaznaczmy punkt, gdzie droga ma sie zakonczyc - droga zostanie zbudowana automatycznie.

Nastepna ikona to 'Przystanek'. Na przystankach gromadza sie ludzie z najblizszej strefy i mozemy ich przewozic do innych przystanków. Na przystanku moga sie zmiescic dwa autobusy. Przystanki mozna budowac na drogach z wylaczeniem skrzyzowan i zakretów.

Kolejne narzedzie sluzy do budowy tuneli. Dzieki nim mozemy skrócic czas podrózy poprzez duze góry. Przed wybudowaniem tunelu najpierw z obu stron góry musisz zbudowac drogi. Zakonczenie tych dróg musi znajdowac sie na tej samej wysokosci. Nastepnie wybieramy ikonke 'Tunel' i przeciagamy linie poprzez góry laczac konce dróg.

Ponizej znajduje sie narzedzie 'Skrzyzowanie'. Jest ono uzywane w celu przeprowadzenia przez droge linii kolejowych. Skrzyzowanie tworzymy klikajac w miejscu gdzie ma ono powstac.

Na lewo widnieje symbol 'Zajezdnia'. W zajezdni kupujemy i sprzedajemy wszystkie dostepne pojazdy i przyczepy. Zajezdnie budujemy na segmentach konczacych droge.

Ostatnie narzedzie to sluzy do budowy punktów przeladunku towarów. Punkty takie budujemy w poblizu kopaln i fabryk aby móc transportowac towary. W jednym punkcie moze stacjonowac równoczensnie do dwóch pojazdów. Punkty przeladunków budujemy na segmentach konczacych drogi.

simutrans-124.5/simutrans/text/pl/save.txt000066400000000000000000000014211520675460500207050ustar00rootroot00000000000000POMOC: zapis gry

Zapisywanie stanu gry

Jesli zamierzasz kontynuowac gre po wyjsciu z Simutrans bedziesz musial zapisac stan gry. Mozesz zapisac wszystkie gre w dowolnym momencie aby pózniej do niej powrócic.

W tym menu mozesz zapisac gre w nowym pliku, nadpisac istniejacy plik, lub usunac zapisana gre. Aby zapisac gre w nowym pliku musisz podac nazwe pliku i wcisnac przycisk 'zapisz' - za chwile pojawi sie komunikta potwierdzajacy zapis gry. Jesli chcesz nadpisac istniejacy plik kliknij na nazwie tego pliku.

Jesli chcesz usunac plik musisz sie dobrze zastanowic, poniewaz usunietego pliku nie da sie juz odzyskc (nawet z kosza w systemie Windows). W celu usuniecia pliku kliknij na przycisk 'X' obok nazwy danego pliku.

simutrans-124.5/simutrans/text/pl/schedule.txt000066400000000000000000000017121520675460500215460ustar00rootroot00000000000000Rozklad jazdy

Rozklad jazdy

Po zakupie pojazdu w oknie zajezdni/lokomotywowni wybierz przycisk 'Rozklad jazdy'. Przy kursorze pojawi sie zólta tabliczka. Teraz mozesz zaznaczyc gdzie bedzie sie zatrzymywal pojazd. Zwykle beda to przystanki i stacje. Dodatkowo masz mozliwosc ustawienia czy pojazd ma od razu odjezdzac z przystanku czy czekac az zostanie zaladowany okreslaona liczbe procent (zaznaczamy to u góry okna). Po ustaleniu trasy kliknij na 'wlacz' zniknie wtedy okno 'Rozklad jazdy' i ponownie pojawie sie okno zajezdni/lokomotywowni. Teraz wystarczy ze klikniesz na start i pojazd wyruszy w droge!

W oknie 'Rozklad jazdy' mozesz równiez dodawac lub zmieniac wczesnie zaplanowana trase. Po wcisnieciu 'Dodaj' wybierz dodatkowy przystanek na trasie. Jesli chcesz dany przystanek usuna kliknij 'Usun' a nastepnie zaznacz nazwe przystanku w oknie. Zaistniale zmiany zatwierdzamy wciskajac przycisk 'Gotowe'. C.D.N.

simutrans-124.5/simutrans/text/pl/shiptools.txt000066400000000000000000000015461520675460500220030ustar00rootroot00000000000000POMOC: transport morski

Transport morski

W tym oknie dostepne sa narzedzia do budowy stoczni, statków i portów.

W górnym lewym rogu znajduje sie ikona budowy portów. Po kliknieciu kursor zamieni sie w rysunek portu. Dzieki portom mozliwy jest transport ludzi i towarów pomiedzy obszarami przedzielonymi woda lub na wyspy. Po kliknieciu na brzegu morza zostanie zbudowany port. Mozna zbudowac port nawet duzych rozmiarów. Aby przydzielic statek do portu podczas ustalania rozkladu musisz wybrac pole sasiadujace z portem.Uwaga! Podczas usuwania portu moga wystapic bledy!

W menu znajduja sie dwie ikony budowy stoczni. Jedyna róznica jest kierunek wyplywania gotowych statków. Stocznie uzywane sa do produkcji statków. Stocznie budujemy klikajac w wybranym miejscu na wodzie w poblizu brzegu.

simutrans-124.5/simutrans/text/pl/sound.txt000066400000000000000000000012631520675460500211030ustar00rootroot00000000000000POMOC: Ustawienia dzwieku i muzyki

Ustawienia dzwieku i muzyki

W tym oknie masz mozliwosc ustawienia dzwieku i muzyki dla gry SimuTrans.

Poziom dzwieku utawiamy suwakiem. Suwak efektów ustala glosnosc dla pociagów, samochodów itp., natomiast suwak 'Muzyka' ustala glosnosc melodii odgrywanych w tle.
Istnieje mozliwosc odgrywania muzyki w petli do przodu i do tylu (dwa odrebne przyciski). Jesli muzyka jest nie dostepna przyciski te sa nieaktywne.
Uwaga: Gra nie zawiera zadnych melodii. Nalezy je dolaczyc samodzielnie i ustalic kolejnosc odgrywania. Jak tego dokonac? Odpowiedz znajdziesz w pliku readme w katalogu 'Music'.

simutrans-124.5/simutrans/text/pl/station.txt000066400000000000000000000012361520675460500214340ustar00rootroot00000000000000Infomacje o stacjach i przystankach

Informacje o stacjach i przystankach

Informacje te zawieraja niezbedne dane o wszystkich stacjach na mapie. Mozliwa jest zmiana nazw stacji.

U góry okna znajduje sie nazwa stacji, która mozna zmienic klikajac na niej i wpisujac nowa.

W tym oknie znajdziesz liste towarów i pasazerów danej stacji (ilosc i miejsce docelowe).

W szczególowych informacjach znajdziesz takie dane jak: - dokad nalezy przewozic towary/ludzi ze stacji - jakich towarów potrzebuje miejscowy przemysl (co nalezy tu przywozic)
Uwaga: Lista uaktualniana w czasie rzeczywistym.

simutrans-124.5/simutrans/text/pt.tab000066400000000000000000001353301520675460500177150ustar00rootroot00000000000000Portugues PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: pt Portugues # # Encoding: ISO-8859-1 # # Font: Prop-Latin1.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable Desligado cl_btn_filter_enable Ligado cl_btn_filter_settings Opes filtro cl_btn_sort_asc Crescente cl_btn_sort_desc Decrescente cl_btn_sort_id N criao cl_btn_sort_income Renda cl_btn_sort_name Nome cl_btn_sort_type Tipo clf_btn_alle Tudo clf_btn_invers Inv. clf_btn_keine Nada gl_btn_sort_bonus Bnus gl_btn_sort_name Nome gl_btn_sort_revenue Valor gl_btn_unsort Sem ordem hl_btn_filter_disable Desligado hl_btn_filter_enable Ligado hl_btn_filter_settings Opes filtro hl_btn_sort_asc Crescente hl_btn_sort_desc Decrescente hl_btn_sort_name Nome hl_btn_sort_type Tipo de carga hl_btn_sort_waiting Esperando hlf_btn_alle Tudo hlf_btn_invers Inv. hlf_btn_keine Nada Networks Esquemtico Queueing Em fila Road toll Pedgio rodovirio Scenario Cenrio Transfers Transferncias #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic rtico desert Deserto mediterran Mediterrneo rocky Alpino temperate Temperado tropic Tropical tundra Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed No foi possvel carregar o script do cenrio! Can't buy obsolete vehicles! No se pode comprar veculos obsoletos! Cannot alter water No possvel alterar a gua Cannot built depot here! No possvel construir um depsito neste local! Cannot built this station/building\nin underground mode here. O edifcio no pode\nser construdo no subterrneo.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. No possvel criar uma linha\nna aba Geral. Selecione\no tipo de linha pelas\noutras abas. Cannot create socket No pde criar conexo Convoi handles exhausted! Nmero mximo de comboios atingido. Convoy already deleted! Veculo j deletado! Das Feld gehoert\neinem anderen Spieler\n Voc no tem permisso para usar ou modificar!\n\nIsto no lhe pertence! Der Besitzer erlaubt das Entfernen nicht Voc no tem permisso para usar ou modificar!\n\nIsto no lhe pertence! Diese Zusammenstellung kann nicht fahren!\n Esta combinao de veculos no funciona! Flugzeughalt muss auf\nRunway liegen!\n Paradas aerovirias s\npodem ser colocadas\nem pistas de\ntaxeamento.\n Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n No se pode construir\nessa dependncia do\naeroporto a!\n Hier kann kein\nSignal aufge-\nstellt werden!\n O sinal no pode ser instalado neste local! In order to lock the game, you have to protect the public player by password! Para proibir a mudana de jogador o Poder pblico deve ter uma senha! Lost connection\nto server! Conexo ao servidor foi perdida! Lost synchronisation\nwith server. A sincronizao com o servidor foi perdida. Maglevhalt muss auf\nMaglevschiene liegen!\n Paradas de maglev devem\nficar em vias magnticas! Monorailhalt muss auf\nMonorail liegen!\n A programao para os veculos de monotrilho deve ser sobre monotrilhos! Monorails are not available yet! Monotrilhos ainda no\nesto disponveis!\n Narrowgaugehalt muss auf\nNarrowgauge liegen!\n A estao deve estar num trilho de bitola estreita! No suitable way on the ground! As estaes devem ser construdas sobre uma superfcie plana! No through station here! No possvel colocar uma parada neste local! Not enough clearance. Deve existir uma altura mnima de dois entre duas vias! Not enough money! Voc no tem bastante dinheiro para construir isto! On narrowgauge track only!\n Somente sobre trilhos de bitola estreita!\n Only public player can lock games! Somente o Poder pblico pode bloquear jogos! Out of funds Voc no tem dinheiro suficiente para isto! Post muss neben\nHaltestelle\nliegen!\n Extenses de estao e\nagncias postais devem\nser colocadas numa rea\nlivre e ao lado de uma\nparada/estao.\n\n Protocoll error (expecting game) Erro de protocolo (esperando jogo) Schiffhalt muss im\nWasser liegen!\n A programao para embarcaes deve ser sobre rios e mares! Server busy Servidor cheio Terraforming not possible\nhere in underground view No possvel modificar\no terreno na viso subterrnea! This tunnel branches. You can try Control+Click to remove. Este tnel se ramifica. Tente utilizar CTRL+Clique para remover esta entrada. Upgrade must have\na higher level Atualizao tem que ter um nvel mais alto! Vehicle %s cannot choose because stop too short! Veculo %s no pde se dirigir porque a parada curta demais. Zughalt muss auf\nSchiene liegen!\n A programao para este tipo de veculo deve ser sobre trilhos! #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Ajuda

ndice

Sobre Simutrans

%1$s

Como usar

%2$s

Como iniciar um jogo

%4$s

Como jogar

%5$s

Ferramentas

%3$s

Outras janelas

%6$s Keyboard Help\n

Keyboard Help

\n Teclas de Atalho\n

Teclas de Atalho

\n

\nAs teclas de atalho apresentam teclas do teclado que acionam diversas funes.\n

\n

\nEsta ajuda aparece quando uma tecla sem funo pressionada ou pela Ajuda do Simutrans.\n

\n

\nO Simutrans faz diferenciao entre letras maisculas e minsculas, ento use [Shift] para usar letras maisculas.\n

\n

\nAs teclas com funes atribudas so:\n

\n

\n[Setas do teclado]: Move a tela para a direo da seta.
\n[Backspace]: Fecha todas as janelas abertas.
\n[Delete] ou [Esc]: Fecha apenas a janela em uso.
\n[Enter] ou [Return]: Confirma aes.
\n[Page-Up] ou [>]: Aumenta zoom do jogo.
\n[Page-Down] ou [<]: Diminui zoom do jogo.
\n[F1]: Abre a Ajuda do Simutrans.\n

\n

\nUse com a parte numrica do teclado:

\n

\n[1]: Move a tela para a direo sul.
\n[2]: Move a tela para a direo sudeste.
\n[3]: Move a tela para a direo leste.
\n[4]: Move a tela para a direo sudoeste.
\n[6]: Move a tela para a direo nordeste.
\n[7]: Move a tela para a direo oeste.
\n[8]: Move a tela para a direo noroeste.
\n[9]: Move a tela para a direo norte.\n

\n

\n[Ctrl] pode ser utilizado para trocar as vias por uma mais lenta.\n

\n

\nAtalhos para ferramentas e opes:\n

\n Decrease water height Reduzir nvel da gua Highlight railroad tracks Realar ferrovias Highlite depots Realar depsitos Highlite electrical transmission lines Realar linhas de transmisso Highlite factories Realar fbricas Highlite forests Realar florestas Highlite tourist attraction Realar atraes tursticas Increase water height Elevar nvel da gua Overlay city limits Sobrepor limites das cidades Overlay passenger destinations when a town window is open Sobrepor destinaes de passageiros quando a janela Estatsticas da Cidade estiver aberta Overlay schedules/network Sobrepe esquema de rede de transportes no mapa Overlay town names Sobrepor nomes de cidades Please click on the map to add\nwaypoints or stops to this\nschedule. Por favor clique no mapa para adicionar\npontos de passagem ou paradas a esta\nprogramao. Set tile climate %s Clima do quadro %s Show capacity and if halt is overcrowded Mostrar capacidade e se a parada est lotada Show how many convoi reach a station Mostrar quantos veculos atendem a estao Show how many people/much is waiting at halts Mostrar quantos passageiros h e quanto tempo ficam esperando nas paradas Show initial passenger departure Mostrar origem dos passageiros Show level of city buildings Mostrar o nvel dos edifcios urbanos Show mail service coverage/mail network Mostrar a cobertura da rede de transporte de correios Show passenger coverage/passenger network Mostrar a cobertura da rede de transporte de passageiros Show speedlimit of ways Mostrar o limite de velocidade das vias Show the change of waiting at halts Mostrar evoluo da espera nas paradas desde o ms anterior Show the owenership of infrastructure Mostrar propriedade sobre a infra-estrutura Show transported freight/freight network Mostrar a rede de transporte de cargas/bens Show usage of network Mostrar uso da rede de transportes Shows a listing with all industries on the map. Lista de indstrias no mapa Sum of departure/arrivals at halts Soma de partidas e chegadas nas paradas #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s comeou uma linha\nde nibus entre %s e a\natrao %s em (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s comeou uma linha\nde nibus entre %s e a\nfbrica %s em (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s comeou a operar\n%i caminhes entre %s\nem (%i,%i) e %s em\n(%i,%i).\n %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s abriu novos\ntrilhos entre %s em\n(%i,%i) e %s em\n(%i,%i).\n Airline service by\n%s\nnow between\n%s \nand %s.\n %s\niniciou nova linha\narea entre \n%s \ne %s.\n\n Ferry service by\n%s\nnow between\n%s \nand %s.\n %s\n iniciou nova linha\nde barcas entre\n%s e \n%s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s abriu uma nova linha de nibus entre %s e %s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Editor do mapa LISTTOOLS Listas MAGLEVTOOLS Ferramentas de MagLev MONORAILTOOLS Monotrilhos NARROWGAUGETOOLS Bitola Estreita RAILTOOLS Ferrovias ROADTOOLS Estradas SHIPTOOLS Navegao SLOPETOOLS Editor de terrenos SPECIALTOOLS Ferramentas especiais TRAMTOOLS Bondes #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). A empresa %s construiu seu centro administrativo. Factory chain extended\nfor %s near\n%s built with\n%i factories. Economia em crescimento!\nA %s, perto de %s, ampliou sua cadeia de produo.\n%i nova(s) fbrica(s) fundada(s). Net wealth less than 10%% of starting capital! Riqueza lquida inferior a 10%% do capital inicial! New %s now available:\n%s\n Novo %s disponvel:\n%s New factory chain\nfor %s near\n%s built with\n%i factories. Nova cadeia de\nproduo de %s\nprximo a %s\nfoi construda com\n%i fbricas. New vehicle now available:\n%s\n Novo veculo disponvel:\n%s\n Now %u clients connected. Agora %u clientes conectados. Remove vehicle from map. Use with care! Vende o veculo e toda sua carga perdida Screenshot\ngespeichert.\n \nImagem salva!\n Sends the convoi to the last depot it departed from! Envia o veculo para o depsito mais prximo With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s inaugurou um monumento.\n%i pessoas participaram. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Busca rpida #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (no depsito) \nBauzeit bis at \nBauzeit von \nAno de Disponibilidade: \nCan't open heightfield file.\n No foi possvel abrir o arquivo de relevo. \ndirection: \nBits de direo: \nelektrified \nEletrificado\n \nHeightfield has wrong image type.\n O arquivo de relevo est\nem um formato incorreto.\n \nis reserved by: Sendo usada por:\n \nminimum speed: \nVelocidade Mnima: \nnot elektrified \nNo eletrificado\n \nRibi (masked) \nBits direo (oculto): \nRibi (unmasked) \nBits direo (visvel): \nSet phases: \nTempo do Semforo\nNorte-Sul: Leste-Oeste: \nsingle way Mo nica \nway1 reserved by \nVia 1 sendo usada por \nway2 reserved by veculos.\nVia 2 sendo usada por \nwith sign/signal\n \ncom sinal\n %d buildings\n %d edifcios\n %d convois %d veculos %d Einzelfahrzeuge im Depot Armazenados: %d %i km/h (max. %ikm/h) %i km/h (mx. %ikm/h) %i years %i months old. Idade: %i anos e %i meses %s at (%i,%i) now public stop. %s se tornou em parada pblica. %s building %s %s %s - Parada %s %s city %d %s %s - %d.a %s urbana %s factory %s %s %s - Parada %s %s has entered a depot. O veculo\n%s\ninforma que\nchegou ao\ndepsito %s land %d %s %s - %d.a %s rural %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s construiu uma nova prefeitura quando atingiu %i habitantes. %s\nis crowded. %s est lotada. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nVelocidade mx. %3$i km/h\n\nVelocidade atual %2$i km/h.\n %s\nwas liquidated. %s faliu e foi liquidado. Todas as operaes foram paralizadas, e todos os ativos foram vendidos. %u Client(s)\n %u cliente(s)\n %u Player (%u locked)\n %u jogadores (%u com senha)\n 1 convoi 1 veculo 1 Einzelfahrzeug im Depot Armazenados: 1 1LIGHT_CHOOSE Luminosidade 1WORLD_CHOOSE Criando novo jogo: 2LIGHT_CHOOSE Cores: 2WORLD_CHOOSE Nmero do mapa 3LIGHT_CHOOSE Velocidade boto direito 4LIGHT_CHOOSE Inverter direo boto direito 5LIGHT_CHOOSE Pedestres descem nas paradas 5WORLD_CHOOSE Nmero de cidades 6LIGHT_CHOOSE Pedestres nas cidades 6WORLD_CHOOSE Densidade do trfego 8WORLD_CHOOSE Alternar dia e noite A bridge must start on a way! Uma ponte deve comear onde termina uma via e ter seu final livre! Abfrage Consultar Abnehmer Consumidores About Sobre About scenario Autoria Abriss Remover Absenken Rebaixar terreno Abspanntransformator Transformador industrial Accelerate time Acelerar tempo Act. load: %u MW\n Carga ativa: %u MW\n Active player only Apenas deste jogador Add forest Adicionar floresta Add random citycar Adicionar veculos urbanos add server Criar servidor Add Stop Adicionar Add stops for backward travel Adiciona as mesmas paradas no sentido contrrio air Avies aircraft_tab Cargueiros airplane veculo areo Airport Aeroporto AIRTOOLS Aeroportos All Todos all convoi tooltips Status sobre os veculos Allow city growth Permitir crescimento Allow player change Permitir mudana de jogador allowed climates:\n Climas permitidos:\n Alters a schedule. Modifica a programao do veculo Angenommene Waren Indstrias prximas precisam de: anhaengen A reboque Anhaenger_tab Reboques Anheben Elevar terreno Appends stops at the end of the schedule Adiciona paradas ao fim da programao Apply Line Associar April Abril Arbeiter aus: Trabalhadores e correio de: Arrivals from\n Chegadas de\n Arrived Chegaram Assets Valor dos veculos Aufloesen Desmontar Aufspanntransformator Transformador de usina August Agosto Autohalt muss auf\nStrasse liegen!\n A programao para veculos rodovirios\ndeve ser sobre estradas! Available Disponvel Bahndepot Depsito ferrovirio Bankrott:\n\nDu bist bankrott.\n Fim de Jogo\n\nVoc ficou muito tempo com a conta negativa e faliu!\n\nSe prefere no usar dinheiro, na lista de jogadores h a opo\nModo Jogo-livre. battery bateria Baum rvore baum builder Plantar rvore Baustelle Canteiro de obras Bauzeit poca: Beenden Sair Beginner mode Modo iniciante Besonderes Gebaeude Atrao turstica BF Estao bio Animal Blockstrecke ist\nbelegt\n Esta seo est em uso por outro veculo. Boden Terra Bonusspeed: %i km/h Vel. mxima possvel: %i km/h Boost (%%) Reforo (%%) bridge is too high for its type! O local muito alto para este modelo de ponte! Bridge is too long for this type!\n A distncia da ponte muito extensa para este modelo! Bruecke Ponte Bruecke muss an\neinfachem\nHang beginnen!\n A ponte deve\ncomear em um\ntalude reto!\n Brueckenboden Ponte Build air depot Hangar build choosesignals Colocar sinais de opo de parada Build city market Construir mercado Build drain Construir transformador build HQ Construir empresa Build land consumer Construir usina de energia Build maglev depot Depsito de Maglev Build monorail depot Depsito de monotrilho Build narrowgauge depot Depsito de bitola estreita Build powerline Construir linha eltrica Build presignals Colocar sinal intermedirio\n Build road depot Garagem Build ship depot Estaleiro Build signals Colocar sinais Build train depot Depsito ferrovirio Build tram depot Depsito de bonde Build truck depot Garagem Building costs estimates Estimativa de custo Buildings Edifcios Built artifical slopes Construir muro de arrimo Built random attraction Construir atrao aleatria Bus_tab nibus Can only move from halt to halt or waypoint to waypoint. S pode mover de parada\npara parada ou de ponto\ndepassagem para ponto\nde passagem. Cancel Cancelar Cannot connect to offline server! No possvel se conectar a um servidor desligado! Capacity: Capacidade: Capacity: %.0f MW Capacidade: %.0f MW\n Capacity: %d%s %s\n Capacidade: %3d%s %s Capacity: %s\nLoad: %d (%d%%) Capacidade: %s\n? %d (%d%%) Cars are not available yet! Carros ainda no esto\ndisponveis!\n cars.\nstate veculos.\nCancela: Cash Balano Change player Mudar jogador Chart Grfico Chat_msg Chat Choose direction Escolher direo Choose operation executed on clicking stored/new vehicles Opes para vender ou adicionar veculos por uma ordem chooses a random map Escolher um mapa aleatrio citicens Habitantes City attraction Atraes urbanas City industries Indstrias urbanas City list Lista de cidades City size Habitantes city_road Via urbana citybuilding builder Construtor de edifcios urbanos CityLimit Limites cidade cl_title Lista de veculos cl_txt_sort Classificao por: Clear block reservation Mostrar/limpar vias reservadas clf_chk_aircrafts Avies clf_chk_cars Automveis clf_chk_indepot No depsito clf_chk_maglev Maglev clf_chk_monorail Monotrilhos clf_chk_name_filter Filtro por nome: clf_chk_narrowgauge Bitola estreita clf_chk_noincome sem renda clf_chk_noline Sem linha clf_chk_noroute Sem rota clf_chk_noschedule Sem programao clf_chk_obsolete obsoleto clf_chk_ships Barcos clf_chk_spezial_filter Filtro especial: clf_chk_stucked Preso clf_chk_trains Trens clf_chk_trams Bondes clf_chk_type_filter Filtro por veculo: clf_chk_waren Filtro por carga: clf_title Filtro da lista de veculos Climate Control Controle do clima closed Fechada COLOR_CHOOSE\n Escolha duas cores\npara sua companhia: Company bankrupt Falido! Company_msg Concorrncia Comparing pak files ... Comparando arquivos... Configure AI Configurar IA Configure AI setttings Defina configuraes IA Congratulation\nScenario was complete in\n%i months %i years. Parabns! \nO cenrio foi \ncompletado em \n%i meses e%i anos! Connected stops Paradas conectadas: Connected with server Conectado com servidor Constructed by Pintado por Constructed by %s Desenhado por %s construction speed velocidade de construo Construction_Btn Gasto em obras Consumed Consumido convoi %d of %d Veculo %d de %d convoi error tooltips Apenas status de erro Convoi has been sent\nto the nearest depot\nof appropriate type.\n O veculo foi enviado ao depsito do seu tipo mais prximo. Convoi is sold when all wagons are empty. Os veculos sero vendidos assim que estiverem descarregados convoi mouseover tooltips Status veculos com mouse convoi passed last\nmonth %i\n Trfego no ltimo ms: %i veculos\n Convois Comboios Convois: %d\nProfit: %s Comboios: %d\nLucro: %s Convoys Vec. jogador Copy Convoi Copiar Copy the selected convoi and its schedule or line Compra um veculo igual e com programao igual cost for removal \nCusto de remoo: Cost: %8s (%.2f$/km %.2f$/m)\n Custo: %8s (%.2f$/km %.2f$/m)\n Cost: %8s (%.2f$/km)\n Custo: %8s (%.2f$/km)\n Costs Custos Create a new line based on this schedule Cria uma nova linha baseada na programao deste veculo curiosity builder Construtor de atraes tursticas curlist_title Lista de atraes tursticas CURRENCY R$ Currently playing: Tocando: Customers live in: Consumidores vivem em: deactivated in online mode No ativo enquanto online Deccelerate time Desacelerar tempo December Dezembro decrease underground view level Baixar nvel de viso subterrnea Del Stop Apagar Delete Line Apagar Delete the current stop Selecione e depois clique sobre a parada que deseja apagar Delete the selected line (if without associated convois). Apagar linha selecionada (se no houver veculos nela) Delete this file. Apagar este arquivo Delivered Entregue Demand Demandado Demand: %.0f MW Demanda: %.0f MW\n Denkmal Monumento Departed Partiram Departure board Tempo viagem Departures to\n Partidas para\n Depots Depsitos Der Tunnel ist nicht frei!\n O tnel est sendo utilizado ou ele no\nlhe pertence! Destination Destino Destroying map ... Destruindo mapa antigo... Details Detalhes Die Bruecke ist nicht frei!\n A ponte est sendo utilizada ou ela no\nlhe pertence! diesel Desel directmail folhetos Direkt erreichbare Haltestellen Paradas conectadas diretamente: disable midi Mudo Display settings Opes de vdeo Distance Distncia Dock Doca Dock must be built on single slope! A doca deve ser construida sobre\na margem! Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Faltam %d meses para pagar a dvida! Durchsatz Produo mx. de Economy Economia Eigenbesitz\n Propriedade pblica Ein %s\npasst hier nicht.\n No se pode usar um\n'%s' aqui.\n Einstellungen aendern Mudar opes electric Eletricidade Electricity Eletricidade Electricity producer\n\n Produtor de eletricidade\n\n Electrics_tab Trleibus Electrify track Eletrificar linha enlarge map Expandir mapa enter a value between %i and %i Digite um valor entre %i e %i Enter address Insira o endereo Enter Password Nome do jogador e senha Error Erro Erzeuge neue Karte.\n Criando mapa...\n\nPor favor,\naguarde enquanto um novo mapa criado...\n\nIsso pode levar alguns minutos e fazer o programa\nno responder! Es wird bereits\nein Fahrplan\neingegeben\n J tem uma rota\nsendo programada.\nTermine-a antes\nde programar outra!\n Fabrikanschluss Indstrias conectadas: Fabrikname Nome Factories Fbricas factory details Visualizar cadeias factorybuilder Construtor de fbricas Fahrplan Programar Fahrtziel Destino: Fahrzeuge koennen so nicht entfernt werden O veculo ou edifcio est em uso e no pode ser eliminado! Fahrzeuge: Veculos: Farbe Cores Fast forward Acelerar tempo February Fevereiro Ferry_tab Barcos Fertig Pronto Filename Arquivo: Files from: Arquivos de: Filter: Filtro: Finances of %s Finanas de %s Finanzen Finanas find mismatch Comparar seu pak fl_title Lista de fbricas Flug_tab Avies de passageiros follow me Siga-me Follow the convoi on the map. Segue o veculo no mapa Forest Florestas Found new city Fundar nova cidade (Shift+C) Fracht Produtos Frame time: Tempo por quadro: Free Capacity Capacidade freeplay mode Modo jogo-livre Friction: Fator de atrito atual: fuel_cell Clula combustvel Full load Carga mnima (%%) Fundament Arrimo Fussgaenger Pedestre Game info Jogo Online GAME PAUSED JOGO PAUSADO Game_msg Geral Gear: Cmbio: Gebaeude Edifcio General Geral Generated Gerado Generation: %.0f MW Gerao: %.0f MW\n Gewicht Peso Gewinn Renda: Give the selected vehicle(s) an individual schedule Cria uma programao individual gl_btn_sort_catg Tipo de carga gl_title Lista de cargas go home Ir ao depsito Goods Bens Goods AI IA Produtos Goods list Informaes das cargas Gross Profit Lucro lquido Groundobj Objeto-cho Grow city Aumentar habitantes da cidade Growth Crescimento H - Parada Happy Contentes Haus kaufen Comprar edifcio Helligk. Vdeo Help Ajuda Help text not found Texto de ajuda no encontrado\n hide all building Todos os edifcios ocultos hide city building Edifcios urbanos ocultos hide objects under cursor Oculta dinamicamente objetos ao redor do cursor. Tamanho ajustvel. hide station names Ocultar nomes das paradas hide transparent Usar transparncia ao ocultar hide trees Ocultar rvores Hier warten/lagern: Passageiros/carga esperando: Higher transport fees, crossconnect all factories Taxas de transporte maiores, Fbricas podem enviar produtos para qualquer outra Highlite schedule Mostra as paradas da programao hl_title Lista de paradas hl_txt_filter Filtro: hl_txt_sort Classificao por: hlf_chk_airport Aeroportos hlf_chk_anleger Docas hlf_chk_bahnhof Estaes de trem hlf_chk_bushalt Pontos de nibus hlf_chk_frachthof Ptios de carga hlf_chk_keine_verb no conectado hlf_chk_maglevstop Est. de maglev hlf_chk_monorailstop Est. de monotrilho hlf_chk_name_filter Filtro por nome: hlf_chk_narrowgaugestop Est. bitola estreita hlf_chk_overflow Lotadas hlf_chk_spezial_filter Filtro especial: hlf_chk_tramstop Paradas de bonde hlf_chk_type_filter Filtro por tipo: hlf_chk_waren_abgabe Enviam: hlf_chk_waren_annahme Recebem: hlf_title Filtro da lista de paradas Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. No foi encontrado nenhum depsito na rede!\n\nVerifique se existem vias at ele, ou envie o veculo manualmente. Homeless Sem-teto hydrogene Hidrognio Idle: Livre: ignore climates Ignorar climas In the industry legend show only currently existing factories Na legenda de indstria, s mostre fbricas existentes agora no mapa. In Transit Em trnsito Increase Industry density Aumentar densidade das indstrias increase underground view level Elevar nvel de viso subterrnea industrial building Edifcio industrial Init map ... Iniciando mapa... Input Entrada\n Ins Stop Inserir Insert stop before the current stop Insere paradas antes da parada selecionada Intercity road len: Extenso vias interurbanas Intro. date: Incio produo: invalid invlido. Invalid coordinate Coordenada invlida isometric map Mapa isomtrico January Janeiro join game Jogar online July Julho Jump to Ir para June Junho Kann Spielstand\nnicht laden.\n Impossvel ler/abrir\no arquivo salvo!\n Kann Spielstand\nnicht speichern.\n Impossvel abrir\no arquivo para\nsalvar!\n Kein Besitzer\n Disponvel para compra\n keine nenhum(a) Keine Einzelfahrzeuge im Depot Armazenados: 0 Keyboard_Help\n Ajuda do teclado koord Coordenadas Kreuzung Cruzamento labellist_title Lista de marcadores Lade Relief Carregar terreno Laden Carregar Land attraction Atraes rurais Land industries Indstrias rurais LANG_CHOOSE\n Por Favor,\nescolha um idioma: LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month ltimo ms: Last Year ltimo ano: Leaving depot! Partindo do depsito leer vazio Legend Legenda Leistung Potncia Leistung: %d kW Potncia: %d kW Leitung Linha de transmisso letzen Monat: diesen Monat: ltimo ms: este ms: Line Linha Line Management Gerenciador de linhas Lineless convoys serving this stop Comboios sem linha servindo esta parada: Lines serving this stop Linhas que servem esta parada: LKW_tab Caminhes Load game Carregar jogo load height data from file Carrega relevo a partir de um arquivo Load scenario Carregar cenrio loaded carregado loaded passenger/freight Passageiros/carga carregada: Loading (%i->%i%%)! Carregando (%i->%i%%) Loading addon paks ... Carregando add-ons... Loading map ... Carregando mapa... Loading paks ... Carregando pakset... Loading skins ... Carregando temas... Lock game Proibir mudana de jogador (Impossvel desfazer) Lokomotive_tab Locomotivas m3 m maglev vehicle Veculo Maglev maglev_track Trilho de Maglev Maglevdepot Depsito de Maglev Mail Demand %d\n Demada por correio %d\n Mailbox Mensagens Mailbox Options Opes das mensagens Maintenance Manuteno make stop public (or join with public stop next) costs %i per tile and level Converter parada ao Poder pblico, %i$ por quadro e nvel Manual (Human) Humano Manufactured: Comprado: Map roughness Aspereza map zoom Zoom March Maro Margin (%%) Margem de lucro Marker Marcador max mx. Max Boost (%%) Reforo mx.(%%) Max income: Faturamento mximo: Max. speed: Velocidade mx.: Maximum 254 stops\nin a schedule!\n Mximo de 254\nparadas em uma\nprogramao de linha!\n maximum length of rivers Compr. mx. dos rios Maximum tile height difference reached. No se pode ter mais do que 2 nveis de altura entre dois quadros adjacentes. Maxspeed vel. mxima May Maio Median Citizen per town Mdia moradores por cidade Meldung Mensagem Menge Quantidade MessageOptionsText \nAno novo\n\nConcorrncia\n\nNotcias cidades\n\nSem rota\n\nNovas rotas\n\nChat\n\nNotcias veculos\n\nEstao lotada\n\nProblemas\n\nCongestionamento\n\nCenrio min mn. minimum length of rivers Compr. mn. dos rios Missing pakfiles Faltam objetos! Modify the selected line Abre opes para modificar a linha selecionada Monate alt meses de idade Monorail Monotrilhos monorail vehicle Veculo para monotrilho monorail_track Trilho de monotrilho Monorailboden Monotrilho Monoraildepot Depsito de monotrilho month wait time Espera mx. (ms) Months Meses Monument Monumentos Monuments Monumentos Mountain height Desnvel do relevo Movingobj Objeto mvel Music playing disabled/not available Sem msica para tocar Music volume: Volume da msica mute sound Mudo Name Nome Narrowgauge Bitola estreita Narrowgauge are not available yet! Bitola estreita no est disponvel ainda! narrowgauge vehicle Veculo de bilota estreita narrowgauge_track trilho de bitola estreita Narrowgaugedepot Depsito de trem de bitola estreita Net ID: %p Rede n.o: %p\n Net Wealth Ativos Net wealth near zero Ativos prximo de zero Neue Karte Novo jogo Neue Welt Criar novo jogo new convoi Novo veculo New Line Criar linha New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Criada uma nova linha!\nVoc pode associar essa linha\nselecionando-a\nno seletor de linhas acima. New Vehicles Compra de veculos Nickname: Seu nome: no buildings hidden Nenhum edifcio oculto no convois Sem veculos No goods are loaded onto this convoi. O veculo no ser mais carregado e sua carga atual ainda ser descarregada nas paradas corretas no goods waiting Sem cargas esperando no load No carregar No Route Sem rota No stop here! Esta ferramenta deve ser usada em quadros de uma parada. No suitable ground! No h local adequado! No terminal station here! No possvel colocar\num terminal neste local! no timeline Sem linha do tempo no tree Mapa sem rvores No. of Factories Fbricas e comrcios Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Programe o veculo antes de mand-lo partir! none nenhum nord Norte nordost Nordeste nordwest Noroeste Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! A programao no pode ser modificada agora.\n\nTente mais tarde ou verifique se ela j no est sendo modificada! Not enough fields would remain. Um nmero insuficiente de\nquadros continuaro em volta da fazenda. November Novembro Now active as %s.\n Jogando como %s. Number of rivers Nmero de rios Object Objeto Odometer: %s km Odmetro: %s km Oktober Outubro On loan since %i month(s) Em dbito h %i ms(es) On this map, you are not\nallowed to change player!\n Neste jogo no permitido mudar de jogador! Only city chains Somente comrcios/servios Only first %d differing paks reported. There are probably more. Apenas os %d primeiros arquivos foram reportados. Outros podem existir. Only land chains Somente usinas eltricas Only one transformer per factory! Cada fbrica pode ter s um transformador! Only show goods which are currently handled by factories S mostre cargas que esto sendo processadas pelas indstrias open Aberta Operation Operao Ops Profit Lucro operacional Optionen Opes Or enter a server manually: Ou digite o endereo de um servidor: Origin Origem ost Leste Output Sada Ownership Propriedade Pak which may cause severe errors: Os seguintes objetos (paks) esto faltando, isso pode causar falhas na rede de transportes: Pak which may cause visual errors: Os seguintes objetos (paks) foram substitudos por objetos similares: Pak(s) different: Arquivo(s) com diferenas: Pak(s) missing on client: Arquivo(s) faltando em seu pakset: Pak(s) not on server: Arquivo(s) faltando no servidor: Pakset differences Diferenas no pakset paletten caixas Pas_tab Trens de passageiros Passagiere Passageiros Passagierrate ndice viagens Passagierziele Destino dos passageiros/correio Passenger AI IA Passageiros Passenger Demand %d\n Demanda por passageiros %d\n Passengers %d %c, %d %c, %d no route Passageiros %d %c, %d %c, %d sem caminho Passengers %d %s, %d %s, %d no route Passageiros %d %s, %d %s, %d sem caminho Password Senha: Pause Pausar Pax <%i> Mail <%i> Passageiro <%i> Correio <%i> PaxDest Destinos Percent Electricity Percentual de usinas Planes are not available yet! Avies ainda no esto\ndisponveis!\n Plant tree Plantar rvore aleatria player Jogador player -1 Jogador humano player 0 Poder pblico player 1 Napik 128 SA player 10 Jogador 10 player 11 Jogador 11 player 12 Jogador 12 player 13 Jogador 13 player 2 Trikky Transport SA player 3 Cia. Transp. Meyer player 4 Expedio VM player 5 H-Trans Ltda player 6 PSK & Co S.A. player 7 Jogador 7 player 8 Jogador 8 player 9 Jogador 9 Please choose vehicles first\n Por favor, escolha os veculos\nantes de programar. Post Correios Postrate ndice correios Power Potncia Power (MW) Potncia (MW) Power: Potncia: Power: %4d kW\n Potncia: %4d kW\n Powerlines Linha eltrica Problems_msg Problemas Produced Produzido Production of %s has been stopped:\n%s\n A produo do %s %s foi encerrada. Production/Boost Produo/Reforo Produktion Produo Profit Lucro promote to line Criar linha q1 Primavera q2 Vero q3 Outono q4 Inverno Query server Examinar rail car veculo ferrovirio random Aleat. Random age poca aleatria Random map Mapa aleatrio Rathaus Prefeitura Rating Taxa ratio_pax Taxa de passageiros (%%) Relevant Relevante Reliefkarte Mapa Remove Remover remove airstrips Remover trecho de pistas remove channels Remover trecho de canais remove interm. signals Remover sinais intermedirios remove maglev tracks Remover trecho de trilhos de Maglevs remove monorails Remover trecho de monotrilho remove narrowgauge tracks Remover trecho de trilhos de bitola estreita remove powerlines Remover linhas de transmisso remove roads Remover trecho de estrada remove tracks Remover trecho de trilhos Remove wayobj %s Remover objeto de via %s replace other signals Substituir outros sinais replace stop Transferir paradas request closing a se fechar. residential house Edifcio residencial Restore natural slope Remover muro de arrimo Restwert: Valor de venda: Retire. date: Fim da produo: return ticket Espelhar linha Revenue Faturamento Revision: Reviso: road estrada road vehicle veculo rodovirio Roadsign Sinal Rotate map Girar mapa Rotation Rotao Routing Rotas sack sacos sail Ventos Saving map ... Salvando mapa... Scenario complete: %i%% Cenrio completo: %i%% Scenario Debug Depurar Scenario Error Log Erros de script Scenario Goal Metas Scenario Info Informao Scenario information Informao Scenario Result Resultado Scenario Rules Regras Schedule changing! Alterando Programao Schienentunnel Tnel ferrovirio Schiff_tab Cargueiros Schiffdepot Estaleiro Schleppkahn_tab Barcas Screenshot Capturar imagem da tela Seasons Estaes do ano Sehenswuerdigkeit Atrao turstica Select a server to join: Selecione um servidor para entrar: Select a theme for display Tema para interface Sell the selected vehicle(s) Vende o veculo selecionado sended Enviados SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 September Setembro Server did not respond! O servidor no respondeu! Serves Line: Serve linha: Service Servio set signal spacing Espaamento entre sinais Setting Configurao Ship Navios shops and stores Edifcio comercial Show all Mostrar todos show all building Mostrar todos os edifcios Show also vehicles no longer in production. Mostra veculos que no so mais produzidos Show also vehicles that do not match for current action. Mostrar tambm os veculos inaptos para essa ao Show even servers with wrong version or pakset Mostra inclusive servidores com paksets e verses incompatveis show grid Mostrar grade no terreno Show industry Indstrias Show legend Legenda Show map scale Escala Show mismatched Mostrar incompatveis Show obsolete Mostrar obsoletos Show offline Mostrar desligados Show only used S fbricas existentes Show schedules Mostrar programao Show servers that are offline Mostrar servidores que esto desligados Show servers where game version or pakset does not match your client Mostrar servidores cuja verso do jogo e pakset so incompatveis show station coverage rea de captura das paradas show station names Mostrar nomes das paradas show waiting bars Mostrar barras de espera show/hide block reservations Mostrar/ocultar reservao de via Show/hide estimated arrival times Exibir os tempos de chegada e partida show/hide object owner Mostrar/ocultar objetos do jogador Show/hide statistics Mostra grficos estatsticos Shows buttons on special topics. Mostra botes dos temas da legenda do mapa. Shows consumer/suppliers for factories Mostra as ligaes dos fornecedores aos consumidores Shows the color code for several selections. Mostra a escala de cores para o tema ativo da legenda. Shows the currently selected schedule Mostra a programao de veculo ou linha agora selecionada Shrink city Diminuir habitantes da cidade shuffle midis Ordem aleatria Signal Sinal signal spacing Espaamento dos sinais Sim: Simloops: Similar view as the main window Vista semelhante da janela do jogo Size (%d MB): Tamanho (%d MB) sliced underground mode Viso sub. por altura slot empty --------------- Smart hide objects Auto-ocultar sob cursor Sort by Organizar por Sort waiting list by Ordenar lista por Sound Som Sound settings Opes de som Sound volume: Volume dos efeitos special freight carga especial Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Bnus a partir de: Variar limiar em:\nRodovia: %i km/h, Ferrovia: %i km/h\nHidrovia: %i km/h, Tr. Areo: %i km/h\n Speedlimit Vel. mxima Speichern Salvar Spieler Jogador Spieler(mz) Jogadores Spielerliste Lista de jogadores Spielstand wurde\ngeladen!\n \nJogo carregado!\n Spielstand wurde\ngespeichert!\n \nJogo salvo!\n Sprache Idioma Sprachen Idioma Stadtinformation Estatsticas da cidade Start Partir Start the selected vehicle(s) Manda o veculo selecionado comear a servir sua programao Starte Spiel Criar jogo Station tiles: Tamanho da estao: Station_msg Estaes Status Status paradas steam Carvo Step timeline one year Avanar tempo em um ano Stops Paradas Storage Armazenado Storage capacity Capacidade Strassendepot Garagem Strassentunnel Tnel rodovirio street car bonde/elctrico sued Sul suedost Sudeste suedwest Sudoeste Summer snowline Alt. neve no vero Supplied: %.0f %% Fornecido: %.0f %% Suppliers Fornecedores Tage alt dia(s) Theme selector Seleo de Tema There are still vehicles\nstored in this depot!\n Voc no pode destruir!\n\nVenda ou mova os veculos armazenados neste depsito para poder destruir. This Month Este ms: This Year Este ano: Tile not empty. O local no est vazio.\nPor favor remova o\nque o ocupa antes de\nmodificar o relevo\ncom muros de arrimo.\n timeline Com linha do tempo tl_title Lista de cidades To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Para atrair mais turistas,\n%s construiu\num %s\ncom a ajuda de\n%i contribuintes. To heavy traffic\nresults in traffic jam.\n Trfego muito intenso resulta em congestionamento!\n Toggle day/night view Alternar viso diurna/noturna Toggle vehicle tooltips Ativa/desativa dicas de veculos tonnen t Total inhabitants: Total de Habitantes: Tourist attractions Atraes tursticas Tourists Turistas Town_msg Cidades Town: %s\n Cidade de %s\n Towns Cidades track trilho Tracks Trilhos Traffic Trfego Train Trens Trains are not available yet! Trens ainda no esto disponveis!\n Tram Bondes tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. Bonde: %i km/h, Monotrilho: %i km/h\nMaglev: %i km/h, Bitola estreita: %i km/h. tram_track Trilho de bonde Tramdepot Depsito de bondes Trams are not available yet! Bondes ainda no\nesto disponveis!\n Transferring game ... Transferindo jogo... Transformer only next to factory! No pode haver transformadore\ndescolados de uma fbrica! Translation Traduo transparent station coverage rea de captura transparente Transported Transportado TrolleyBus_tab Trleibus Truck Veculos rodovirios tt_Other Outros Tunnel muss an\neinfachem\nHang beginnen!\n Um tnel deve comear no final de uma via que esteja sobre um terreno elevado e inclinado! Tunnel must start on single way! Um tnel deve comear onde termina uma via e\nter sua sada livre! Tunnelboden Tnel underground mode Viso subterrnea UNDO failed! No mais possvel desfazer\na via. Isto s possvel se\nnada foi construdo sobre a\nvia. Undo last ways construction Desfaz a ltima construo de via Unemployed Desempregados Unhappy Descontentes units/day /dia, Update Line Modificar upgrade HQ Ampliar empresa Usage: %.0f %% Uso: %.0f %% Usage/Output Usado/Sada Use beginner mode Usar modo iniciante Use timeline start year Usar linha do tempo Vehicle %s can't find a route! O veculo %s no consegue encontrar sua rota programada! Vehicle %s is stucked! O veculo %s est parado/preso em um congestionamento. Vehicle details Detalhes do veculo Verbrauch Consumo Vergroessere die Karte\n Expandindo mapa...\n\nPor favor, aguarde enquanto o mapa expandido...\n\nIsso pode levar alguns minutos e fazer o programa\nno responder. Verkauf Remover agora! verkaufen Vender Verkehrsteilnehmer Vec. urbanos Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Voc tem uma dvida!\n\nFaltam %d meses\npara pag-la!\n via Via (detalhe) via %s\n (via %s)\n via Menge Via (quant.) voranstellen frente Waggon_tab Vages waiting esperando Waiting for clearance! Aguardando liberao Walked Caminhado walking Andar a p Warnings_msg Avisos Wasser gua Water gua Water level Nvel d'gua water vehicle veculo aqutico way %s cannot longer used:\n Via " %s " no pode ser utilizada:\n way %s cannot longer used:\n%s\n Via " %s " no pode ser utilizada:\n%s\n way %s now available:\n A via " %s " agora est disponvel:\n Way toll Pedgio Ways not connected Vias no conectadas Wegpunkt Ponto de passagem Weight: Peso: Wert Valor west Oeste Winter snowline Alt. neve no inverno withdraw Encerrar Withdraw All Encerrar todos WRONGSAVE Verso salva\nincompatvel. Impossvel ler\no arquivo.\n Year %i has started. Feliz Ano Novo!\n\nEst comeando o ano de %i. Years Anos Your primary color: Sua cor primria: Your secondary color: Sua cor secundria: Zielort Destino zooming in Aumentar zoom zooming out Diminuir zoom Zu nah am Kartenrand Voc no pode construir to prximo da borda do mapa. #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Novo recorde mundial para maglevs: %.1f km/h por %s. New world record for monorails: %.1f km/h by %s. Novo recorde mundial para monotrilhos:\n %.1f km/h por %s. New world record for motorcars: %.1f km/h by %s. Novo recorde mundial para automveis:\n %.1f km/h por %s. New world record for narrowgauges: %.1f km/h by %s. Novo recorde mundial para trens de bitola estreita: %.1f km/h por %s. New world record for planes: %.1f km/h by %s. Novo recorde mundial para avies:\n %.1f km/h por %s. New world record for railways: %.1f km/h by %s. Novo recorde mundial para trens:\n %.1f km/h por %s. New world record for ship: %.1f km/h by %s. Novo recorde mundial para navios:\n %.1f km/h por %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL do Leste &1_CITY_SYLL do Sul &2_CITY_SYLL do Norte &3_CITY_SYLL do Oeste &4_CITY_SYLL da Barra &5_CITY_SYLL da Serra &6_CITY_SYLL d'Oeste &7_CITY_SYLL da Imperatriz &8_CITY_SYLL da Luz &9_CITY_SYLL do Alto &A_CITY_SYLL dos Campos &B_CITY_SYLL do Vale &C_CITY_SYLL da Mata &D_CITY_SYLL das Misses &E_CITY_SYLL do Planalto %0_CITY_SYLL Rancho %1_CITY_SYLL So Pedro %2_CITY_SYLL Riacho %3_CITY_SYLL So Jos %4_CITY_SYLL Jacaragu %5_CITY_SYLL Rio %6_CITY_SYLL Estrada %7_CITY_SYLL Vale %8_CITY_SYLL Salto %9_CITY_SYLL Santa Laura %A_CITY_SYLL So Jorge %B_CITY_SYLL Santa Rita %C_CITY_SYLL Macar %D_CITY_SYLL So Pedro %E_CITY_SYLL Cruzeiro %F_CITY_SYLL Morro 0center %s %s Centro Velho 0extern %s %s rural 0suburb %s %s suburbana 1center %s %s Centro Novo 1extern %s %s rural da Fazendinha 1suburb %s %s do %s 2center %s %s Centro Novo 2 2extern %s %s rural da Fazendinha 2 2suburb %s %s suburbana do %s 3center %s %s principal 3extern %s %s Descampado 3suburb %s %s suburbana de %s 4center %s %s do Centro Expandido 4extern %s %s rural de Retiro 4suburb %s %s suburbana de Vila Nova 5center %s %s central 5extern %s %s rural de baldeao 5suburb %s %s suburbana de Vila Nova 6center %s %s central de transferncia 6extern %s %s rural de transferncia 6suburb %s %s Jardins 7center %s %s central D. Pedro I 7extern %s %s rural de Descampado 7suburb %s %s Parque das Naes 8center %s %s central do Comrcio 8extern %s %s rural %s do Bairro Bela Vista 8suburb %s %s Bairro Recanto 9center %s %s central do Eixo Urbano 9extern %s %s rural de passagem 9suburb %s %s de interligao Acenter %s %s central Repblica Aextern %s %s rural do Bairro Bela Vista Asuburb %s %s Vila das Flores Bcenter %s %s central Liberdade Bextern %s %s rural Bairro Bem-te-vi Bsuburb %s %s Vila do Solrio Ccenter No! simutrans-124.5/simutrans/text/pt/000077500000000000000000000000001520675460500172205ustar00rootroot00000000000000simutrans-124.5/simutrans/text/pt/airtools.txt000066400000000000000000000134571520675460500216270ustar00rootroot00000000000000 Ajuda - Aeroportos

Aeroportos

A Barra Aeroportos contm ferramentas para construir uma rede de transportes areos. As ferramentas podem construir: pista de taxiamento e pistas de pouso para aeronaves (ou remov-las), hangares (usados para compra e manuteno dos veculos), Paradas de Aeronaves (usados para carga e descarga de bens e passageiros) e vrios edifcios aeroporturios. Se jogar com a Linha do Tempo ativa, com o passar do tempo no jogo, outras opes de ferramentas podero aparecer.

Clique no cone do avio ao topo da tela do jogo para abrir a barra de ferramentas.
Passe o cursor do mouse por cima dos botes para poder ver o nome e, onde for o caso, o custo de construo, custo de manuteno (dentro dos parnteses) e limite de velocidade mxima.

As ferramentas podem incluir, da esquerda para direita:

Pista de taxiamento: A ferramenta constri pistas de taxiamento (reas de trfego terrestre para as aeronaves irem das Paradas de Aeronaves para uma pista de decolagem). As pista de taxiamento so construdas sobre o solo na tela do jogo.
IMPORTANTE: NO conecte as pistas de taxiamento s extremidades de uma pista de decolagem (cabeceiras), seno as aeronaves no podero decolar e pousar.

Para construir uma pista de taxiamento: clique sobre a ferramenta para selecion-la (o cursor torna-se uma pista de taxiamento), ento clique sobre o terreno para o ponto inicial (uma escavadora aparece sobre o cursor na tela do jogo e as coordenadas do mapa no canto inferior esquerdo da tela), e clique sobre o terreno ou sobre uma pista de decolagem para ponto final.

Pista de Pouso e Decolagem: A ferramenta constri uma pista usada pelas aeronaves para decolar e pousar. As pistas so construdas sobre o nvel do solo na tela do jogo e podem cruzar entre si.

Para construir uma pista de pouso e decolagem: clique sobre a ferramenta para selecion-la (o cursor torna-se uma pista), ento clique sobre o terreno para fixar o ponto inicial da pista de pouso e decolagem (uma escavadora aparece sobre o cursor na tela do jogo), e finalmente clique sobre o terreno para fixar o ponto final da pista.

Remover Pistas de Pouso e Decolagem: a ferramenta que remove um segmento de uma pista de taxiamento ou pista de pouso e decolagem, quando nenhuma aeronave est presente, entre dois pontos na tela do jogo. O custo do uso desta ferramenta igual ao custo de construo.

Para remover pistas de pouso e decolagem: clique sobre o boto Remover Pistas (o cursor torna-se um X vermelho); ento clique sobre o ponto inicial do segmento da pista a ser removida (um X vermelho fixado neste ponto na tela do jogo) e finalmente clique no ponto final do segmento da pista. Todo o segmento entre os pontos marcados ser removido.

Depsito de aeronaves ou hangar: A ferramenta constri hangares para compra e gerenciamento das aeronaves. Os hangares tm um custo de manuteno e so construdos sobre o fim de uma pista de taxiamento.

Para construir hangares: clique no boto (o cursor torna-se um depsito de aeronaves), ento clique sobre o fim de uma pista de taxiamento.

Paradas de Aeronaves e Ponte de Embarque: A ferramenta constri Paradas de Aeronaves para carga e descarga de bens e passageiros na tela de jogo.
Uma Parada de Aeronaves, quando no for construda adjacentemente a uma j existente, far com que seja criada uma nova Parada independente.
As Paradas de Aeronaves so construdas sobre o final de um trecho de uma pista de taxiamento e tem uma rea de captura para bens, passageiros e correio. Uma aeronave somente ir pousar em um aeroporto se houver uma Parada de Aeronaves vazia disponvel.

Para construir uma Parada de aeronaves: Clique sobre o boto para selecionar a ferramenta Parada de Aeronaves e ento sobre clique sobre a extremidade de uma pista de taxiamento.

{Dica: Pressione [v] para exibir ou ocultar rea de captura para bens e passageiros na tela do jogo.}

Edifcios Aeroporturios: A ferramenta constri extenses e edifcios para Paradas de Aeronaves os quais podem incrementar a capacidade e a rea de captura para bens e passageiros, assim como custo de manuteno. No canto superior esquerdo de alguns dos botes existe um pequeno cone (usado tambm em Lista de Paradas e Informaes da Parada) que indica quais os itens (bens, passageiros ou correio) a extenso habilita a Parada de Aeronaves a manipular.

Para construir uma extenso: Clique sobre o boto para selecionar uma extenso (o cursor torna-se a extenso selecionada), ento clique na posio requerida, prxima a uma Parada de Aeronaves existente na tela do jogo. A nova extenso agora passa a ser parte da Parada de Aeronaves existente.

{Dica: Um modo de construir um aeroporto simples :
i) construa uma pista de pouso e conecte a ela uma pista de taxiamento;
ii) coloque uma parada de aeronaves no fim da pista de taxiamento;
iii) adicione mais edifcios aeroporturios se desejar.}
{Dica: Use o boto Remover [r] para remover partes individuais das pistas de taxiamento, pouso, remover hangares e demais edifcios. O Desfazer [z] no reembolsa os custos das construes.
Pressione [v] para exibir ou ocultar rea de captura para bens e passageiros na tela do jogo.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/baum_build.txt000066400000000000000000000022151520675460500220640ustar00rootroot00000000000000Ajuda - Plantao de rvores

Plantao de rvores

A caixa de dilogo dividida em quatro sees:
superior-esquerda, uma lista de seleo de tipos de rvore disponveis
inferior-esquerda, mostrada uma figura da rvore escolhida
superior-direto, esto algumas opes podem ser definidas
inferior-direito, esto informaes sobre a rvore escolhida

Lista de seleo

Na lista de seleo esto dados todos os tipos de rvores que esto disponveis com as opes especificadas no canto superior-direito. H duas abas que lhe permitiro mudar como os tipos de rvore so identificados.
Traduo: Mostra o nome da rvore em determinado idioma. Se no houver nenhuma traduo disponvel para o determinado idioma, o nome do objeto ser usado.
Objeto: Mostra o nome de objeto interno do Simutrans para o tipo de rvore.

Opes

Ignorar climas: Esta opo desativa as restries de clima associadas a vrios tipos de rvore.
poca aleatria: A idade da rvore a ser plantada ser aleatria.

simutrans-124.5/simutrans/text/pt/citybuilding_build.txt000066400000000000000000000043601520675460500236310ustar00rootroot00000000000000Ajuda - Construo de edifcios urbanos

Construo de edifcios urbanos

A caixa de dilogo dividida em quatro sees:
superior-esquerda, uma lista de seleo de edifcios urbanos disponveis
inferior-esquerda, mostrado uma figura do edifcio urbano escolhido
superior-direto, esto algumas opes podem ser definidas
inferior-direito, esto informaes sobre o edifcio urbano escolhido

Lista de seleo

Na lista de seleo esto dados todos os edifcios urbanos que esto disponveos com as opes especificadas no canto superior-direito. (Residcia, Comrcio, Edifcio industrial). H duas abas que lhe permitiro mudar como os edifcios urbanos so identificados.
Traduo: Mostra o nome do edifcio urbano em determinado idioma. Se no houver nenhuma traduo disponvel para o determinado idioma, o nome do objeto ser usado.
Objeto: Mostra o nome de objeto interno do Simutrans para o tipo de edifcio urbano.

As cores de texto tm os respectivos significados:
azul indica que uma residncia.
verde indica que um comrcio ou um edifcio de escritrios.
preto indica que um edifcio industrial.

Opes

Ignorar climas: Esta opo desativa as restries de clima associadas varios tipos de edifcios.
Linha do tempo a partir de: A lista de seleo listar s edifcios que esto disponveis para o ano corrente do Simutrans.
Mostrar obsoletos: Expande a lista de seleo para concluir tambm edifcios que esto obsoletos para o ano do Simutrans corrente.
Residcia: Haver residncias na lista de seleo.
Comrcio Haver comrcios e edifcios de escritrios na lista de seleo.
Edifcio industrial: Haver edifcios industriais na lista de seleo.
Rotao: Se o edifcio escolhido pode ser mostrado de ngulos diferentes, ento possvel escolher uma vista especfica dele aqui. A parte inferior-esquerda da imagem mudar para indicar isto. Se esta opo est defina em aleatria, uma vista aleatria do edifcio ser usada.

simutrans-124.5/simutrans/text/pt/citylist_filter.txt000066400000000000000000000040321520675460500231710ustar00rootroot00000000000000Lista das Cidades

Lista das Cidades

Lista das Cidades apresenta detalhes sobre as reas urbanas (cidades) e suas populaes.

Para abrir: clique sobre o boto de Lista das Cidades em Listas ou tecle [T].
Lista das Cidades tem dois botes de opes que determinam a ordem em que as reas urbanas (e suas populaes) sero exibidas.
{Dica: Se os itens na lista esto parcialmente visveis, ento redimensione a Lista das Cidades ou desloque a lista usando a barra de rolagem.}

Habitantes: o nmero total de habitantes do jogo (de todas as cidades). O mais recente acrscimo da populao mostrado entre parnteses.

Classificadas por: tem opes para determinar a ordem de como as cidades so apresentadas na lista.
Clique sobre os botes de opes para alternar as opes (o que faz mudar o nome do boto):

- Nome lista ordenada alfanumericamente de acordo com a ordem do cdigo ASCII (letras maisculas antes das minsculas) dos nomes atribudos.
- Moradores ordena pelo nmero de habitantes.
- Crescimento ordena por de taxa de crescimento (que dependente do tamanho da cidade e do nmero de passageiros transportados).

- Crescente / Decrescente inverte a ordem da lista.

Clique sobre um item listado na Lista das Cidades para ver mais informaes sobre aquela cidade.
Itens listados para cada cidade incluem:

Nome atribudo cidade (que pode ser alterado em Estatsticas da Cidade).

Populao da rea urbana, e entre parnteses est o recente acrscimo na populao.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/citywindow.txt000066400000000000000000000104551520675460500221660ustar00rootroot00000000000000 Ajuda - Estatsticas da Cidade

Estatsticas da Cidade

Estatsticas da Cidade mostra estatsticas sobre a rea urbana (cidade) selecionada e pode ser usada para alterar o seu nome.

Cada cidade tem sua prpria Prefeitura, ao redor da qual ela se desenvolve. O transporte de passageiros e de correio ajuda o seu crescimento, de forma que novas estradas so construdas e administradas pelo Jogador Poder Pblico, novos prdios aparecem na cidade, indstrias so criadas e os limites da cidade se expandem.

Clique em uma Prefeitura na tela do jogo com a Ferramenta Consultar ou em uma cidade listada em Lista das Cidades para abrir Informaes das Cidades, a qual contm: um campo para o nome, estatsticas, dois mini-mapas e um grfico informativo da rea urbana.

Nome: o nome atribudo cidade aparece num campo de nome no topo das Estatsticas da Cidade. Este nome usado nos nomes dados s Paradas criadas.
Para alterar o nome: clique no campo do nome e digite o novo nome.
{Dica: Use [!] para alternar a visualizao do nome da rea urbana sobre a Prefeitura e os marcadores das diversas paradas criadas, na tela do jogo. Veja readme_citylist.txt (em ...simutrans/text/) para maior ajuda na criao de nomes para as cidades}.

Algumas estatsticas da rea urbana selecionada so mostradas logo abaixo do campo do nome:
Populao: o nmero de habitantes. O mais recente aumento da populao mostrado entre parnteses.
Edifcios: o nmero de edifcios da cidade.
Coordenadas no mapa: indica os limites da cidade (so alterados conforme a cidade cresce).

As seguintes informaes aumentam com o crescimento da cidade e diminuem com a construo de novos prdios:
Desempregados: o seu valor determina qual o tipo de construo (comercial/industrial) pode ser construda.
Sem-teto: a sua existncia determina que residncias podem ser construdas.

Mini-mapas abrangendo toda a rea de jogo em Estatsticas da Cidade mostram o destino de passageiros e do correio.
Os destinos so mostrados por pontos coloridos referentes ao corrente ms do jogo (mini-mapa da direita) e ao ms anterior (mini-mapa da esquerda). A cor dos pontos indicam os seguintes detalhes sobre passageiros e correio:
- amarelo: foi encontrada uma rota e uma Parada descongestionada para incio do deslocamento (rosto feliz mostrado nas Informaes da Parada).
- laranja: no foi encontrada nenhuma rota para seu destino desejado.
- vermelho: foi encontrada uma rota porm a Parada estava super-congestionada (rosto zangado mostrado nas Informaes da Parada).

Grfico mostra estatsticas da cidade.
O eixo Y (vertical) indica a quantidade, o eixo X (horizontal) indica o tempo.
Clique nas abas superiores do grfico para alterar a escala do eixo X:
Anos: mostra os valores anuais para os ltimos 12 anos de jogo.
Meses: mostra os valores para os ltimos 12 meses de jogo.

Clique nos botes de opes abaixo do grfico para ver mais informaes (o boto fica pressionado quando selecionado).
A cores das linhas no grfico correspondem s cores dos botes de opes:
Populao: nmero de habitantes da cidade.
Crescimento: variao da populao (depende do tamanho da cidade e do servio de transporte de passageiros e correio providenciado).
Vol. Transportado: passageiros e correio que entraram no sistema de transporte (indicado como o rosto feliz na Informao das Paradas)
Passageiros: Nmero total de passageiros e correio gerados na cidade.

{Dicas: Configuraes relacioandas s cidades podem ser alteradas no arquivo simuconf.tab e cityrules.tab}.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/climates.txt000066400000000000000000000033441520675460500215660ustar00rootroot00000000000000Ajuda - Controle do Clima

Controle do Clima

Controle do Clima ajusta diversas opes de aspectos topogrficos e meteorolgicos do jogo.

Controle do Clima abre com Criando um Novo Mundo.

Os botes de setas ajustam os parmetros:

Opes:

Nvel d'gua: ajusta qual a profundidade que se encontra a gua no mapa.

Desnvel: ajusta a altura mxima atingida no terreno.

Aspereza: ajusta as ondulaes do terreno; quanto maior o nmero, mais irregular ser o terreno.

Opes da linha de neve:

Linha de neve no vero: ajusta a altitude que comea a aparecer a neve permanente no vero.

Linha de neve no inverno: ajusta a altitude que comea a aparecer a neve permanente no inverno.

Opes de clima:

Clima desrtico: ajusta a que altitude acima do nvel do mar o clima desrtico comea a aparecer.

Clima tropical: ajusta a que altitude acima do nvel do mar o clima tropical comea a aparecer.

Clima mediterrneo: ajusta a que altitude acima do nvel do mar o clima mediterneo comea a aparecer.

Clima temperado: ajusta a que altitude acima do nvel do mar o clima temperado comea a aparecer.

Tundra: ajusta a que altitude acima do nvel do mar a tundra comea a aparecer.

Clima alpino: ajusta a que altitude acima do nvel do mar o clima alpino comea a aparecer.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/color.txt000066400000000000000000000016421520675460500211020ustar00rootroot00000000000000Ajuda - Cor

Cor

Neste menu voc pode alterar a cor de sua companhia de transporte.
Veculos, prdios e outros itens mostram a cor da sua companhia.

A janela Cor aberta atravs das Preferncias.

Clique nas cores mostradas para mudar a cor da companhia.
A barra de ttulo da janela Cor muda para indicar que a nova cor est selecionada.

A cor-padro do jogador humano o azul claro.
Alguns itens no esto programados para mudar de cor e portanto permanecero inalterados.

{Dica: para mudar a cor dos outros jogadores use a Lista de jogadores ou Mudar Jogador (P+) para selecion-lo}.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/convoi.txt000066400000000000000000000071571520675460500212700ustar00rootroot00000000000000Lista de Veculos

Lista de Veculos

A Lista de Veculos mostra informaes e tem controles para filtrar e listar diferentes Comboios (um Comboio formado por um veculo operacional ou uma combinao de veculos).

Para abrir: clique na ferramenta de Lista de Veculos em Listas ou aperte a tecla [V]. A Lista de Veculos tem quatro botes de opes, dois so utilizados para ordenar itens em uma lista e dois so usados para aplicar as opes para selecionar os itens a serem mostrados na lista.
Abaixo dos botes existe uma lista de Comboios que se ajustam aos critrios de opes de filtros.
{Dica: Se nenhum Comboio est listado altere as opes de Filtro. Se os itens na lista esto apenas parcialmente visveis, ento reajuste a Lista de Veculos ou desa a lista usando a barra lateral.}

Clique nos botes de opes para mud-los de acordo com as opes (o nome do boto de opes muda) ou abra os controles de Configurao de Filtros :

Classificado por: Dois botes de opes determinam a ordem dos Comboios mostrados na lista. O primeiro boto altera a ordem da lista em:

- Tipo organiza por tipo de veculo (na ordem crescente de veculo rodovirio, veculo ferrovirio, navio e avio)
- Nome organiza por nome designado, alfanumericamente de acordo com a ordem do cdigo ASCII: letras maisculas antes das letras minsculas (por padro isso feito no primeiro veculo comprado ou montado ao Comboio).
- Lucro organiza por lucro (renda gerada menos os custos operacionais).
- ID Interno organiza pelo cdigo de identificao nico (ID) do comboio (designado por padro quando o Comboio comprado ou construdo pela primeira vez em um Depsito e mostrado na barra de ttulo da Informao do Comboio).

E o segundo boto, altera a ordem da lista em:
- crescente / decrescente inverte a ordem da lista.

Filtro: Dois botes de opes, ligam/desligam os critrios de seleo da lista e acessa os controles para alterar os critrios.

- Ligado / Desligado clique para alterar os critrios para os Comboios na lista.

- Parmetros abre os controles para alterar os critrios de filtragem.

Clique no item listado para ver mais informaes sobre o Comboio.

Itens listados para cada Comboio incluem:

ID Interno: um nmero de identificao nico (designado por padro quando um Comboio comprado ou montado pela primeira vez em um depsito e mostrado na barra de ttulo da Informao do Comboio).

Nome que foi designado (por padro isso j feito no primeiro veculo quando o Comboio comprado ou montado).

Lucro: mostra o lucro (renda gerada menos os custos operacionais enquanto trafega).

Linha: mostra a Linha designada e indica se o Comboio est em um depsito.

Grficos: mostra a composio e o nvel atual de carregamento.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/convoi_filter.txt000066400000000000000000000045631520675460500226330ustar00rootroot00000000000000Filtro da Lista de Veculos

Filtro da Lista de Veculos

O Filtro da Lista de Veculos tem opes para determinar quais Comboios so mostrados na Lista de Veculos.

Clique em Parmetros na Lista de Veculos para abrir o Filtro da Lista de Veculos.

Os botes de opes selecionam os Comboios a serem listados na Lista de Veculos quando a opo de filtro est ligada. Apenas os Comboios que esto dentro de todos os critrios selecionados so listados.

Clique em uma caixa de seleo para selecionar o critrio para filtrar (o boto est em baixo-relevo quando selecionado):

Filtro por nome: seleciona o Comboio pelo nome. Para usar: clique no boto para selecionar, ento clique na caixa de texto e digite o nome desejado exatamente (a opo diferencia letras maisculas de minsculas, portanto preste muita ateno quanto a isso).

Filtro por tipo: listar os Comboios por tipo de transporte se selecionado. Para us-lo: clique no boto para selecionar, ento clique no(s) tipo(s) de transporte: Caminhes, Trens, Barcos e Avies.

Filtro especial: seleciona os Comboios a serem listados pelos seguintes critrios (clique no boto para selecionar, ento clique):
- sem rota - no pode encontrar um caminho ao prximo destino.
- sem programa - sem uma rota atribuda.
- sem renda - os veculos que no esto gerando lucros.
- em depsito - os veculos que esto em um depsito.
- sem linha - sem uma Linha atribuda.

Filtro por carga: seleciona os Comboios pela habilidade para transportar diferentes mercadorias e passageiros. Clique no boto ao lado do nome para selecionar o(s) item(ns). Use a barra lateral para ver os item(ns) na lista. As opes incluem:
- tudo seleciona todos os itens.
- nada nenhum item selecionado.
- inv. inverte a seleo atual de itens.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/convoidetail.txt000066400000000000000000000042241520675460500224430ustar00rootroot00000000000000Ajuda - Detalhes do Comboio

Detalhes do Comboio

Os Detalhes do Comboio do informaes sobre um Comboio (um veculo operacional ou combinao de veculos) e pode ser usado para vender o Comboio imediatamente.

As informaes mostradas na janela dos Detalhes do Comboio incluem:

Potncia do Comboio (potncia combinada de todos os veculos motorizados no Comboio).

Valor de Venda: dinheiro recebido se o Comboio for vendido (valor de compra do Comboio menos qualquer depreciao).

AVISO:

Clique no boto Vender para que o Comboio seja vendido imediatamente. Alm disso, nenhuma confirmao pedida. O Comboio removido do jogo e o jogador recebe o valor de venda.

Detalhes do Comboio lista todos os veculos no Comboio.
As informaes listadas para todos os veculos incluem:
- cone do veculo: uma imagem do veculo (tambm usado em depsitos).
- Manufaturado: ms e ano da manufatura e data de introduo do veculo se estiver jogando com a Linha do Tempo.
- Valor de venda: quantidade de dinheiro recebida se o veculo for vendido.
- Potncia: potncia gerada (por veculos motorizados).
- Fator de atrito atual: nvel de atrito atual.
- Faturamento mximo: lucro do veculo pelo tipo de item transportado pelo Comboio.
- Itens carregados: quantidade e destino dos itens atualmente carregados pelo veculo.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/convoiinfo.txt000066400000000000000000000167341520675460500221450ustar00rootroot00000000000000Informaes do Comboio

Informaes do Comboio

A janela Informaes do Comboio oferece informaes sobre um Comboio (um veculo operacional ou uma combinao de veculos) o qual utilizado para transportar mercadorias e passageiros.
Ela lista os itens carregados por um Comboio e tambm possui os controles para acessar os detalhes e vender o Comboio; seguir o Comboio na viso do jogo; e alterar o nome e a Programao de um Comboio.

Um Comboio composto de pelo menos um veculo com fora (motorizado) e qualquer tipo de reboque, como um vago.
Exemplos de Comboios incluem: um grupo de cavalos puxando uma carruagem; ou uma locomotiva ligada a um tender e a vages; ou um nibus; ou uma van; ou um caminho ligado a um reboque; ou bondes conectados entre si; ou um navio; ou um rebocador puxando uma barcaa; ou um avio.
Os Comboios so montados e comprados do depsito para o seu respectivo tipo de transporte.
{Dica: Um Comboio tem um nmero limite mximo de veculos em sua composio: trens, 24; veculos rodovirios, 4 .}

Clique em um Comboio enquanto estiver na viso do jogo com a ferramenta Consultar ou clique no Comboio listado na Lista de Veculos ou no Gerenciador de Linhas para abrir a janela Informaes do Comboio
A barra de ttulo da janela Informaes do Comboio mostra um nmero de identificao nico e o nome do Comboio (por padro isso feito quando o primeiro veculo comprado ou montado em um depsito).
A janela Informaes do Comboio contm uma caixa do nome; uma mini-viso e a informao sobre o Comboio; botes de opes; e uma lista de todos os itens atualmente sendo carregados (se todos os itens no esto visveis reajuste a janela de Informaes do Comboio ou use as barras laterais para ver os outros itens).

A informao mostrada na janela de Informaes do Comboio inclui:

ID Interno: um nmero nico de identificao para o Comboio (designado por padro quando o Comboio comprado ou montado pela primeira vez em um depsito).

- Nome: mostra o nome designado do Comboio (por padro isso j feito ao primeiro veculo comprado ou montado no Comboio em um depsito).
Clique na caixa de texto e digite um novo para alterar o nome do Comboio.

- Velocidade: velocidade atual mostrada em km/h.
Entre parnteses est localizada a velocidade mxima que pode ser atingida pelo Comboio (determinada pelo veculo mais lento no Comboio).
Uma barra de progresso verde indica a velocidade atual relativa ao limite de velocidade mxima.

- Renda: lucro gerado por veculo (renda gerada menos os custos operacionais) de acordo com o ano atual do calendrio do jogo.
Entre parnteses est o custo operacional do Comboio em Crditos Hajo.

- Peso: peso combinado atual do Comboio e itens carregados em toneladas.
Entre parnteses est o peso atual dos veculos carregados.
Uma barra de progresso verde indica a capacidade usada, para carregar mercadorias e passageiros.
Uma barra amarela indica o nvel de Espera (quantidade mnima de mercadorias e passageiros necessrios para o Comboio prosseguir) ajustado nas opes da Linha.
{Dica: Para ajustar os nveis de Espera para todos os Comboios de uma Linha, use o boto Atualizar em Gerenciador de Linhas ou em Controles do Depsito. Para ajustar o nvel de Espera para apenas um Comboio e mais nenhum outro em uma Linha atribuda, use o boto Programar na janela Informaes do Comboio ou Controles do Depsito.}

- Destino: A prxima Parada ou ponto de passagem na Programao do Comboio.
Uma barra de progresso verde indica o progresso do Comboio at o prximo Destino.

- Serve a linha: indica qualquer Linha atribuda ao Comboio.

- Mini-vista mostra o Comboio.
Clique na mini-vista para centralizar a viso do jogo no Comboio.

Clique nos botes da janela Informaes do Comboio para abrir os controles ou selecionar as opes:

Programar: abre os controles para modificar a rota e a quantidade mnima de mercadorias e passageiros necessrios para o Comboio prosseguir.

Recolher: manda o Comboio ao depsito apropriado mais prximo. Quaisquer itens carregados sero perdidos, apesar de que o faturamento recebido pelo transporte ao depsito.
No depsto, o Comboio mantm sua Programao e sua Linha atribuda.

Siga-me: boto que quando ativado move a viso do jogo junto com o Comboio.
Para desativar esta opo, clique no boto novamente ou clique com o boto direito no Mapa.

Grfico: clique nesse boto para ativar/desativar o grfico (o boto fica pressionado quando o grfico est visvel) em Informaes do Comboio.
O grfico mostra as estatsticas para os ltimos 12 meses (eixo horizontal) quando uma opo do grfico est selecionada.
Clique no boto de opo de grfico para ver a informao no grfico (o boto fica em baixo-relevo quando a opo est selecionada).
As cores das linhas no grfico correspondem s cores dos botes de opo do grfico:
- Capacidade indica o espao sem uso no Comboio.
- Vol. Transportado indica o nmero de mercadorias, passageiros e correio transportados.
- Faturamento indica o lucro gerado pelo transporte.
- Operao indica os custos do Comboio enquanto est trafegando.
- Lucro indica o lucro recebido pelo transporte (Lucro Bruto menos os custos operacionais).

Detalhes abre os Detalhes do Comboio que contm mais informaes e a opo para vender o Comboio.

Passageiros/carga carregada: lista os itens carregados atualmente pelo Comboio.
A informao mostrada inclui a quantidade carregada e a capacidade do Comboio, o tipo do item, o destino final e a primeira Parada.
O boto de opo (o nome varia de acordo com a seleo) organiza os itens carregados em grupos de passageiros, correio e mercadorias por:
- Destino: organiza os itens carregados alfanumericamente de acordo com a ordem do cdigo ASCII (letras maisculas antes das letras minsculas), pelo nome designado da Parada final.
- Via (detalhe): organiza os itens carregados alfanumericamente de acordo com a ordem do cdigo ASCII (letras maisculas antes das letras minsculas), pelo nome designado da primeira Parada de transferncia.
- Via (quant.): organiza os itens carregados pela quantidade levada primeira Parada de transferncia.
- Quantidade: organiza os itens carregados pela quantidade em ordem decrescente.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/curiosity_build.txt000066400000000000000000000043211520675460500231720ustar00rootroot00000000000000Construtora de atraes tursticas

Construtora de atraes tursticas

Esta caixa de dilogo dividida em quatro sees:
acima esquerda est uma lista de seleo de edifcios disponveis
bottom left is shown a picture of the chosen building
top right some options may be set
bottom right is information about the chosen city building given

Lista de seleo

In the selection list are given all special buildings and landmarks that are available with the options specified top right (City attractions, Land attractions, Monumentos). There are two tabs that will allow you to change how the attraction buildings are identified.
Translation: Shows the name of the building in the given language. If there is no translation available in the given language, the object name will be used.
Object: Shows the Simutrans internal object name for the building type.

The color of the text has the following meaning:
texto em azul a atrao tem que ser construda nas cidades
texto em verde a atrao tem que ser construda nas zonas rurais
texto em preto monumentos: podem ser construdos uma vez.

Opes

Ignorar climas: This option deactivates the climate restrictions associated with various building types.
Usar linha do tempo: The selection list will only contain buildings that are available the current Simutrans year.
Show obsolete too: Expand the selection list to also contain buildings that are obsolete the current Simutrans year.
City attraction: There will be city attractions in the selection list.
Land attraction: There will be land attractions in the selection list.
Monumentos: There will be monuments in the selection list.
Rotation: If the chosen building can be shown from different angles, then it is possible to chose a specific view of it here. The image bottom left will change to reflect this. If this option is set to random a random view of the building will be used.

simutrans-124.5/simutrans/text/pt/curiositylist_filter.txt000066400000000000000000000044601520675460500242600ustar00rootroot00000000000000Lista de Atraes

Lista de Atraes

A Lista de Atraes mostra informaes sobre todas as atraes tursticas que so um destino frequente de pessoas e correio.

Para abrir a Lista de Atraes: clique no boto de atraes tursticas em Listas.
{OBS: Se os itens s forem parcialmente visveis, tente redimensionar a Lista de Atraes ou role a barra de rolagem.}

Classificadas por: tm a opo de determinar a ordem das atraes tursticas.
Clique nos botes para alternar ordem (o nome do boto muda ao clicar):

- Nome lista de atraes em ordem alfabtica.
- Gerao de viagens lista de atraes em ordem de popularidade ou destinos de passageiros ou correio.

- Crescente / Decrescente reverte a ordem da lista.

Clique no item listado na Lista de Atraes para abrir uma nova janela com informaes sobre o local.
{OBS: clique na imagem que aparece na janela de informaes da atrao para ser levado automaticamente at ela.}

Os items listados para cada atrao turstica incluem:

Barra de status a cor indica a popularidade da atrao:
- amarelo: no h paradas ou no h veculos para poder carregar passageiros ou correio.
- verde: a atrao contm uma parada associada com passageiros.
- azul: a atrao contm uma parada associada somente a correio.
- turquesa: a atrao contm uma parada associada com passageiros e correio.
- laranja: a atrao contm uma parada quase lotada.
- vermelho: todas as paradas da atrao esto lotadas.

cone de prdio, se presente, indica que uma atrao da cidade.

Nome da atrao turstica.

Gerao de viagens um valor que indica a popularidade do destino para passageiros e correios.

{OBS: Para obter um melhor servio, as atraes tursticas devem estar inteiramente dentro da rea de captura de uma ou mais paradas.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/depot.txt000066400000000000000000000201221520675460500210710ustar00rootroot00000000000000Depsitos

Depsitos

Depsitos so usados para comprar e gerenciar veculos. Tipos diferentes de transporte possuem seus prprios depsitos. Somente aparecero veculos eltricos no depsito se eles estiverem em vias eletrificadas.
Se estiver jogando com linha do tempo, ento conforme o tempo passa no Simutrans mais veculos podem aparecer.

Ferramentas para a construo de depsitos esto disponveis na barra de ferramentas de cada tipo de transporte: depsitos ferrovirios; depsitos de monotrilhos; depsitos de bondes; garagens; estaleiros e hangares.

Clique em um depsito construdo na rea de jogo com a ferramenta Consultar para abrir a janela de controles do depsito, que fornecem informaes e permitem a compra e o gerenciamento dos comboios (um veculo operacional ou a combinao de mais de uma unidade veicular).
A barra de ttulo da janela do depsito indica para que tipo de veculo o depsito serve.

A janela do depsito pode ser redimensionada (clique na seta para baixo da barra de ttulo para retornar ao tamanho original).
Clique nas setas para a direita/esquerda na barra de ttulo para percorrer os depsitos do mesmo tipo de transporte (a rea de jogo ficar centralizada no depsito).

No topo da janela, so mostrados o nmero de comboios no depsito, o nome e a linha.

Nome do comboio: clique nas setas existentes nos lados da caixa de nome do comboio para percorrer os comboios armazenados no depsito.
Para mudar o nome do comboio: clique na caixa de nome do comboio e digite o novo nome (por padro isso feito na primeira unidade veicular comprada ou montada para um novo comboio).

Nome da linha: clique nas setas existentes nos lados do nome da linha para selecionar as linhas daquele tipo de transporte, ou clique na caixa de nome para abrir uma lista das linhas existentes (clique em uma delas para selecion-la).
Para mudar o nome da linha: clique na caixa de nome da linha e digite o novo nome (por padro quando uma nova linha criada ela identificada por um nmero).

Grfico: abaixo das caixas de nome, mostrado o grfico do comboio selecionado.
Passe o cursor do mouse por cima do grfico das unidades veiculares do comboio selecionado para ver detalhes sobre a unidade veicular (mostrados na parte inferior da janela do depsito).
Clique em uma unidade veicular para remov-la do comboio selecionado e armazen-la no depsito.

Abaixo do grfico so mostrados o nmero de unidades veiculares do comboio selecionado, bem como a quantidade de quadros de estao precisos (todas as unidades veiculares devem caber em uma parada para poder recolher e entregar mercadorias e passageiros), e a linha a que serve.

Os botes de opo (logo abaixo, clique para usar) da janela do depsito incluem:

Partir: Coloca o Comboio em funcionamento se uma rota foi programada e/ou encontrada.

Programar: abre controles para mudar a rota e quantidade de itens que um comboio esperar em uma parada.

Desmontar: armazena no depsito a(s) unidade(s) veicular(es) do comboio selecionado.

Vender: vende o comboio selecionado. O Valor de venda ser creditado no balano.

Nova linha: abre controles para definir uma programao (uma rota e quantidade mnima de itens que um comboio esperar em uma parada), que pode ser usada por mltiplos comboios.

Associar: associa a linha mostrada na caixa de nome da linha no topo da janela do depsito ao comboio selecionado.

Atualizar: abre controles para mudar a programao da linha selecionada.

Copiar comboio: compra e monta outro comboio com a mesma composio, programao e linha do comboio selecionado.

Abaixo dos botes so mostrados grficos de todas as unidades veiculares disponveis para compra.
Grficos das unidades veiculares: unidades veiculares so divididas em categorias (unidades para passageiros e correio, unidades motorizadas, e aquelas que servem como reboque), que podem ser exibidas clicando nas respectivas abas.
Um nmero branco em cima do grfico da unidade veicular indica a quantidade armazenada se um ou mais comboios foram desmontados. Unidades armazenadas podem ser vendidas ou usadas na montagem de um novo comboio.

Passe o cursor do mouse sobre o grfico de uma unidade veicular para ver as seguintes informaes:
Nome: e entre parnteses o tipo de energia utilizado pelas unidades motorizadas.
Preo: valor para compra e entre parnteses o custo de operao por quilmetro.
Potncia: potncia do motor e velocidade mxima para unidades motorizadas.
Capacidade: quantidade e nome da mercadoria suportados pela unidade veicular.
Peso: em toneladas.
Velocidade mxima: para unidades veiculares no motorizadas.
Introduo: data em que a unidade veicular se torna disponvel quando se joga com linha do tempo.
Fim da produo: data em que a unidade veicular se torna obsoleta.
Construdo por: pessoa que criou o veculo para o Simutrans.
Cmbio: relao de fora.
Valor de venda: quantidade de dinheiro recebida se a unidade veicular anteriormente comprada for vendida.

Clique em um dos veculos abaixo das categorias para compr-los ou vend-los dependendo do estado do boto encontrado no canto inferior direito dos controles do depsito. O boto alterna entre as trs seguintes opes:
A reboque: compra uma nova unidade veicular e a adiciona ao comboio selecionado, ou cria um novo comboio.
frente: compra uma nova unidade veicular e a adiciona frente do comboio selecionado, ou cria um novo comboio.
Armazenar: vende os veculos contidos no depsito.

Barras de cores abaixo dos grficos das unidades veiculares indicam quais e em qual ordem as unidades veiculares podem ser montadas, para formar um comboio operacional (as cores podem mudar dependendo dos diferentes estados do boto do canto inferior direito e do comboio seleceionado).
Significado das cores:
- verde: a unidade veicular pode ser usada.
- vermelho: a unidade veicular no pode ser usada.
- amarelo: outra unidade veicular precisa ser conectada para montar um veculo operacional.
- azul: a unidade veicular pode ser usada, mas est obsoleta.
{Dicas: um comboio operacional se ele mostra barras verdes em todas as unidades veiculares; se houver alguma barra amarela ento necessria outra unidade veicular.}

Mostrar obsoletos: jogando com linha do tempo, quando o boto pressionado (clique para usar) mostra as unidade veiculares que tiveram sua produo terminada.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/display.txt000066400000000000000000000077171520675460500214420ustar00rootroot00000000000000Ajuda - Vdeo

Vdeo

A janela Ajustes de Vdeo possui controles para a aparncia do jogo e fornece informaes da performance do computador.

Clique no boto 'Vdeo' na janela Preferncias para abrir os Ajustes de vdeo.

Clique nos botes quadrados para selecionar as opes (o boto aparece pressionado quando a opo selecionada), ou use as setas para ajustar as configuraes:

Mostrar grade: Quando ativado, mostra a terra na tela de jogo dividida em quadros para facilitar a construo. Pode ser ativada pelo atalho [#].

Vista subterrnea: Ativa a viso subterrnea, construes debaixo da terra. Pode ser ativada pelo atalho [U].

Viso subterrnea em nveis: Alterna entre os nveis de profundidade de viso subterrnea.

Alternar dia e noite: ver ciclos de dia e noite no jogo.

Luminosidade: regula o quo claro/escuro o jogo aparecer. Valores muito distantes de zero produzem resultados estranhos; use [+] e [-] para aumentar/diminuir o valor.

Inverter deslocamento do mouse: inverte o sentido do deslocamento da tela do jogo com o mouse.

Velocidade de deslocamento: regula a velocidade do deslocamento pela tela do jogo.

Usar transparncia ao ocultar: Ao ativar, quando se oculta um objeto, mostra-o transparente em vez de escond-lo totalmente.

Ocultar rvores: Oculta as rvores da tela de jogo.

Captura transparente das paradas: Mostra uma captura transparente das paradas. Quando desativado, a captura das paradas representada por pontos distribudos pela tela de jogo.

Mostrar captura das paradas: Mostra/oculta a rea de captura das paradas. Pode ser ativada pelo atalho [v].

Mostrar nomes das paradas: Mostra o nome das paradas. Pode ser ativado/desativado pelo atalho [!].

Mostrar barras de espera: Mostra a barra de espera das paradas. Pode ser ativado/desativado em alternncia com o item anterior pelo atalho [!].

Pedestres nas cidades: permite a simulao de pedestres caminhando pelas reas urbanas.

Ver pedestres: visualizao dos pedestres que desembarcam dos veculos em paradas.

Densidade de trfego: regula o nmero de carros particulares em reas urbanas.
O nmero de carros novos que aparecem conforme a rea urbana cresce depende do seu tamanho e da densidade de trfego. Valores maiores que zero geram trfego nas cidades.

{Dica: mais opes e valores padro para quando o Simutrans inicia podem ser mudados no arquivo simuconf.tab}.


Informaes de vdeo:

Abaixo dessas configuraes, so mostradas informaes do desempenho do computador rodando o Simutrans.
Se os nmeros (geralmente brancos) estiverem vermelhos ou amarelos talvez voc precise mudar suas configuraes.
Mudanas na velocidade que o tempo passa no jogo, (usando o Avano Rpido(>>) no topo da rea de jogo, ou [,]/ [.]) podem mudar a cor do nmero.

Tempo por quadro: quantidade de tempo entre os quadros.

Livre: quando superior a zero, o computador tem capacidade de rodar outro software.

Q/s: taxa de quadros por segundo. Valores altos fazem os veculos parecerem se mover mais naturalmente. Se o nmero continuar vermelho, tente reduzir o tamanho da tela de jogo.

Simloops: se o nmero continuar vermelho, significa que o computador fica lento com as configuraes atuais - tente um mapa menor com menos reas urbanas.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/edittools.txt000066400000000000000000000134421520675460500217730ustar00rootroot00000000000000Ajuda - Editor de mapa

Editor de mapa

O Editor de mapa permite a personalizao avanada do mundo em que se est jogando.
As ferramentas podem: aumentar/diminuir a populao das cidades; construir vias urbanas, atraes, usinas de energia e indstrias; proibir a mudana de jogador e tambm avanar alguns anos na linha do tempo.

Esta barra de ferramentas abre quando se joga como o Poder Pblico, selecionado atravs da Lista de jogadores ou Mudar jogador (P+) nas ferramentas especiais (ou pressionando [P]). A barra ir se fechar ao mudar de jogador novamente.
Passe o cursor do mouse em cima das opes (aps ter aberto ou clicado na barra de ferramentas) para ver o nome e mais informaes de algumas delas.

As opes incluem, da esquerda para a direita:

Aumentar cidade: aumenta a populao de uma rea urbana em 100 habitantes, construindo novas estradas e prdios, se necessrio.
Para aumentar a cidade: clique na ferramenta para ativ-la (o cursor mudar para uma seta vermelha apontando para cima), e clique na rea urbana.

Diminuir a cidade: reduz a populao de uma rea urbana em 100 habitantes. reas urbanas somente continuaro a crescer se a quantidade de sem-tetos e desempregados aumentar.
Para diminuir a cidade: clique na ferramenta para ativ-la (o cursor mudar para uma seta vermelha apontando para baixo), e clique na rea urbana.

Via urbana: constri uma via urbana entre dois pontos, como o Poder pblico, que qualquer jogador pode usar. Vias urbanas podem no ser construdas em reas ocupadas por outros prdios, e podem no encontrar um caminho atravs de terreno acidentado, gua e obstculos. A via urbana construda entre dois pontos pode usar estradas existentes em seu caminho.
Passe o cursor do mouse em cima das opes (aps ter aberto ou clicado na barra de ferramentas) para ver o nome, custo de manuteno e construo para o Poder pblico e o limite mximo de velocidade. Para construir uma via urbana: clique na ferramenta para ativ-la (o cursor mudar para um trecho de via urbana), e clique na rea de jogo no ponto de incio da via (mostra um trator na rea de jogo e as coordenadas do mapa no lado direito da barra inferior da rea de jogo), e finalmente clique no local desejado para o ponto final da via.
{Dicas: tipos diferentes de estradas podem ser conectadas. Use pontes e tneis para conectar estradas atravs de terreno acidentado, ou para desviar de obstculos. Use o Editor de terrenos para aplainar o relevo, possibilitando caminhos para as estradas. Use a ferramenta Remover para remover estradas e obstculos. Use [Ctrl] ao mesmo tempo para algumas funes adicionais.}

Construir atrao aleatria: constri uma atrao turstica que passageiros vo querer visitar. Diferentes atraes geram quantidades diferentes de passageiros e correio. O tipo de atrao construda aleatrio e somente aparece se houver espao suficiente livre de obstculos.
Para construir uma atrao: clique na ferramenta para ativ-la (o cursor mudar para uma gota vermelha em um crculo amarelo), e clique na posio desejada na rea de jogo.
{Dicas: use a ferramenta Remover jogando como Poder pblico para remover atraes. Use a ferramenta Remover e/ou o Editor de terrenos para criar lugares propcios para receber atraes.}

Construir usina de energia: constri uma usina de energia eltrica (e possveis fornecedores). O tipo de usina de energia construdo aleatrio e somente aparece se houver espao suficiente livre de obstculos.
Para construir uma usina: clique na ferramenta ou pressione [I] para ativ-la (o cursor mudar para uma gota vermelha em um crculo amarelo), e clique na posio desejada na rea de jogo.
{Dicas: use a ferramenta Remover jogando como Poder pblico para remover as usinas de energia. Use a ferramenta Remover e/ou o Editor de terrenos para criar lugares propcios para receber usinas.}

Construir mercado: constri um consumidor-final de mercadorias em uma rea urbana (e possveis fornecedores, no necessariamente na rea urbana). O tipo de indstria construda aleatrio e somente aparece se houver espao suficiente livre de obstculos.
Para construir um mercado: clique na ferramenta para ativ-la (o cursor mudar para uma gota vermelha em um crculo amarelo), e clique na rea de jogo para construir um mercado na rea urbana mais prxima.
{Dicas: use a ferramenta Remover jogando como Poder pblico para remover os mercados. Use a ferramenta Remover e/ou o Editor de terrenos para criar lugares propcios para receber mercados.}

Proibir mudana de jogador: remove a opo de atuar como outro jogador e abrir as ferramentas de Editor de mapa.
Para us-la: clique na ferramenta para ativ-la (o cursor mudar para um cadeado), e clique em qualquer lugar na rea de jogo, no pedida confirmao ao jogador.

Pular linha do tempo em um ano: avana a data no jogo em um ano. Se estiver jogando com linha do tempo, conforme o tempo passa mais opes de transporte e construo aparecem e veculos lentos deixam de ser to lucrativos (a receita calculada pela velocidade mdia do transporte disponvel).
Para avanar no tempo: clique na ferramenta, no pedida confirmao ao jogador (a data muda no lado direito da barra inferior da tela do jogo).

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/enlarge_map.txt000066400000000000000000000005331520675460500222340ustar00rootroot00000000000000Expandir mapa

Expandir mapa

Aqui, o jogador pode expandir o mapa direita e para baixo. Se o jogador quiser estender esquerda e para cima, necessrio girar o mapa antes, ento expande-se o mapa, e gira-se de volta.
Uma pr-visualizao do mapa expandido mostrada acima direita da janela de dilogo.

simutrans-124.5/simutrans/text/pt/factorylist_filter.txt000066400000000000000000000051131520675460500236710ustar00rootroot00000000000000Lista das fbridas

Lista das fbricas

Lista das fbricas tem informaes sobre todas as indstrias (fornecedoras e consumidoras) durante o curso do jogo.

Para abrir a Lista das fbricas: clique no boto da lista de fbricas em Listas.
{OBS: Se os itens s forem parcialmente visveis, tente redimensionar a Listas de fbricas ou role a barra de barra de rolagem.}

Classificadas por: tm a opo de determinar a ordem das indstrias.
Clique nos botes para alternar ordem (o nome do boto muda ao clicar):

- Nome da fbrica lista de indstrias em ordem alfabtica.
- Entrada ordenar por entrada de matria-prima.
- Sada ordenar por sada de suplimentos.
- Produo ordenar por capacidade de produo.
- Taxa ordenar por barra de status (ver abaixo).
- Potncia ordenar por suprimento de energia.

- Crescente / Decrescente reverte a ordem da lista.

Clique em um dos itens listados na Lista das fbricas para maiores informaes da indstria.
Os itens listados sobre a indstria incluem:

Barra de status a cor indica a operao da indstria (tambm usada na Informaes das indstrias):
- branco: no requer matria-prima.
- amarelo: a indstria est conectada rede de transporte, mas com fornecimento precrio.
- verde: indstria em timo estado.
- laranja: operacional, mas pode ser melhorado.
- vermelho: operacional, mas parte da cadeia industrial tem suprimentos em excesso.

cone de relmpago vermelho, se presente, mostra que a indstria contm um suprimento de energia nos seus transformadores.

Nome da indstria: mostra o nome da indstria.

Entre parnteses, existem 3 valores que representam informaes de entrada e sada:
produtos entrando: quantidade de matrias em estoque prontas a ser processadas.
produtos saindo: quantidade de produtos que j foram produzidos aguardando transporte.
produo total: mostra o quanto a indstria consegue produzir por dia.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/finances.txt000066400000000000000000000073321520675460500215540ustar00rootroot00000000000000Ajuda - Finanas

Finanas

A janela de Finanas mostra como est o fluxo de caixa de sua empresa, mostra o quanto voc gastou com veiculos, tranpostes e etc.

Clique no cone de dinheiro na parte superior de seu monitor, ou tecle [f], ou clique no nome de um Jogador, para abrir Finaas.

Os valores esto em Hajo Credits, nessa janela voc pode ver seu fluxo deste ano e do ano anterior.
Este Ano: ano atual do calendrio do jogo (o ano mostrado na parte inferior a esquerda do seu monitor).
Ano passado: ano anterior do calendrio do jogo.

Os valores anuais podem tambm ser vistos em um grfico para os ltimos doze anos, ou valores mensais para o ano passado.
Clique sobre o valor ou no boto de nome para mostrar o valor: O eixo vertical a quantidade, o eixo horizontal o tempo.
Clique nas abas para alterar os valores do grfico
ltimos 12 Anos: mostra os valores anuais dos ltimos doze anos do jogo.
ltimos 12 Meses: mostra os valores mensais dos ltimos doze meses do jogo.

Para ver os detalhes dos valores, clique nos botes dos valores para ver os detalhes no grfico. Os valores incluem:

Faturamento e L. Eltricas - renda total do transporte de passageiros, bens, correio e de transmisso de energia.

Operao - custo total de todas as operaes dos Comboios quando em trnsito.
{OBS: A operao de quanto o comboio gastou por quilmetro pode se vista em depsitos ou em Informaes de Comboios.}

Manuteno - custo total para manter a rede de transporte.
{OBS: O custo por o ms para manter a rede (Manuteno (ms) mostrado na direita de Finanas) deduzido no fim do ms de calendrio do jogo.}

Lucro Operacional - lucro com o transporte dos bens, passageiros o correio e a transmisso de energia (lucros menos custos de manuteno e operao).

Veculos Novos - despesa e renda recebida da compra e da venda dos veculos.

Construo - custo total gasto com construo de linhas da transmisso, redes do transporte, reflorestao, alta-voltagem, e de usar a ferramenta Destruir/Remover e outras ferramentas para remoo prpria (canais, estradas, aeroporto, monotrilhos).

Fluxo de Caixa - renda total menos despesa total (custos).

Vol. Transportado - o nmero total dos bens, dos passageiros e de correio transportados.

Balano - o dinheiro atual disponvel para a construo ou compra de veculos novos (mostrada tambm no centro da barra na parte inferior do monitor).
{OBS: Se o jogador humano est no vermelho (balano negativo) por trs meses seguidos, o jogado ir falncia e o jogo acaba}.

Valor dos Veculos - o valor de todos os veculos da companhia no fim do ltimo ms do calendrio.
{OBS: a cada ms, os valores dos veiculos caem ligeiramente, o valor de venda atual para os veculos pode ser visto dentro de Detalhes do Comboio ou quando o veiculo est no depsito.}

Margem (%) - a relao do lucro operacional aos rendimentos.

Ativos - a soma entre Balano e Valor dos Veculos.

{OBS: Alguns custos, valor inicial do balano e as finanas no jogo podem ser mudados dentro do simuconf.tab}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/general.txt000066400000000000000000000066751520675460500214140ustar00rootroot00000000000000Ajuda do Simutrans

Ajuda geral

- Bem-vindo
- Interface/janela do jogo
- Mouse/Rato
- Configurao do teclado

Preferncias
- Idioma
- Cor do jogador
- Vdeo
- Ajustes de som
- Jogadores
- Carregar jogo
- Salvar jogo
- Novo jogo
- - Carregar mapa

Menu principal
- Preferncias
- Mapa
- Ferramenta de consulta
- Editor de terrenos
- Ferrovias
- Monotrilhos e Maglevs
- Bondes
- Estradas
- Navegao
- Aeroportos
- Ferramentas especiais
- - Editor de mapa
- Ferramenta Remover

Opes de Gerenciamento
- Gerenciador de linhas
- Listas
- - Lista de paradas
- - - Filtro da lista de paradas
- - Lista de veculos
- - - Filtro da lista de veculos
- - Lista das cidades
- - Lista das cargas
- - Lista das fbricas
- - Lista das atraes
- Mensagens
- Finanas

Outras opes
- Salvar imagem da tela
- Pausa
- Avano rpido
- Ajuda

Outros dilogos
-Depsitos
-Programao de veculos
- Informaes das estaes
- - Detalhes da parada
- Informaes do comboio
- - Detalhes de comboio/veculo
- Estatsticas da cidade
- Informaes das indstrias

Se voc no encontrou aqui a ajuda que procura, ento tente:
http://guide.simutrans.com, or
http://wiki.simutrans.com, or
http://simutrans-tips.com, or
http://forum.simutrans.com.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008, atualizado em 07/02/2008.

Equipe de traduo do Simutrans PT-SubFrum

Quaquer problema, dvida ou sugesto em nossas tradues, contacte-nos pelo http://pt.simutrans.com.

simutrans-124.5/simutrans/text/pt/goods_filter.txt000066400000000000000000000073421520675460500224470ustar00rootroot00000000000000Lista das cargas

Lista das cargas

A lista de Lista das cargas contm informaes sobre itens variados que podem ser transportados no jogo e tem controles para mudar o lucro recebido do transporte das cargas.

Para abrir: clique no boto das Lista das cargas nas Listas ou aperte a tecla [G].

Uma lista de itens disponveis para transporte no jogo indica: renda recebida, bnus de velocidade (medido em %), categoria das cargas e peso.
{Dica: Se os itens na lista so apenas parcialmente visveis, ento reajuste o tamanho da janela Lista das cargas ou desa a lista usando a barra de rolagem.}

Bnus a partir de: mostra a velocidade necessria para os diferentes tipos de transportes, os quais um Comboio precisa ser capaz de alcanar para receber renda extra pelo transporte rpido de mercadorias e passageiros (ou em alguns casos uma penalidade em dinheiro, se for muito devagar).
Se estiver jogando com a linha do tempo ativada, com o passar do tempo no Simutrans essas velocidades necessrias mudam com a introduo de novos veculos.
Os botes-setas ajustam o lucro recebido e a velocidade que o Comboio precisa alcanar para receber o bnus (ou receber uma penalidade caso o Comboio seja muito lento).

Classificadas por: oferece opes para determinar a ordem dos itens mostrados na lista.
Clique nos botes abaixo para escolher uma das opes (a opo ativada no momento tem o seu nome mostrado no boto):

- Sem ordem classifica em uma ordem aleatria.
- Nome classifica em ordem alfabtica.
- Valor classifica pelo lucro recebido pelo transporte de uma unidade das cargas por um quadrado do jogo.
- Bnus classifica pela porcentagem de bnus de velocidade (a porcentagem de lucro extra recebida se o Comboio capaz de alcanar a velocidade necessria para ter direito a essa porcentagem).

- Crescente / Decrescente inverte a ordem na lista.

Os detalhes listados para cada item incluem:

A cor do quadrado a mesma cor mostrada nas barras coloridas das cargas na viso do jogo sobre uma Parada para indicar a quantidade de itens que aguardam pelo transporte.
{Dica: Pressione [!] para ativar ou desativar as barras coloridas das cargas sobre uma Parada na viso do jogo.}

O valor a renda recebida pelo transporte de uma unidade daquela carga por um quadrado do jogo.
{Dica: Use o modo iniciante para aumentar os lucros em 150% (pode ser alterado no simuconf.tab)}

A porcentagem a porcentagem de lucro extra recebido caso o Comboio utilizado seja capaz de transportar a carga na velocidade necessria para ganhar o bnus (ou em alguns casos em penalidade, caso o Comboio seja muito lento).
A frmula usada para calcular o lucro muito complexa e considera a velocidade mxima de um Comboio com a mdia da velocidade mxima dos outros veculos (e no apenas do veculo visto no jogo).

A categoria da carga indica o tipo de veculo necessrio para o transporte daquela carga. Cargas especiais precisam de veculos especficos.
{Dica: A capacidade dos veculos mostra quais itens um veculo pode transportar}.

O peso mostrado de uma unidade da carga em quilogramas.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 07/02/2008.

simutrans-124.5/simutrans/text/pt/haltlist.txt000066400000000000000000000074251520675460500216150ustar00rootroot00000000000000Lista de paradas

Lista de paradas

A Lista de paradas mostra todas as estaes, pontos de nibus e terminais de carga do jogo, alm de ter controles para filtrar essas paradas. NOTA: as paradas que so somente aquticas, como as plataformas de petrleo, no so listadas aqui!

Para abrir: clique no boto "Listas", e na barra que se abrir, no boto "Lista de Paradas". A Lista de paradas tem quatro botes: dois so utilizados para ordenar os itens e dois so utilizados na filtragem deles. Abaixo desses botes, vem a lista de paradas propriamente dita, com os itens que obedeam aos critrios de filtragem j estabelecidos. (Dicas: Se nenhuma parada estiver listada, modifique as opes de filtragem.. Caso os itens apaream parcialmente, ento use o mouse/rato para redimensionar a Lista de Paradas ou para deslocar a lista com a barra de rolagem.)

Ao clicar nos botes, vo-se modificando as opes que eles contm:

O boto mais esquerda diz o critrio de ordenamento da lista:
- Nome: ordena segundo o cdigo ASCII: primeiro todas as letras maisculas, depois todas as minsculas, em seguida as acentuadas.
- Espera: Ordena pela quantidade de passageiros ou mercadorias aguardando ser transportada
- Tipo: Ordena pelo tipo de veculo que aceito pela parada
- Sem ordem:Ordena aleatoriamente.

O segundo boto define se a classificao crescente ou decrescente.
O terceiro boto define se o filtro est ligado ou desligado.
O quarto abre a janela do Filtro da Lista de Paradas.

Clicando em um item na Lista de paradas, obtm-se maiores informaes sobre aquela parada.
Os dados listados por parada so:

Estado: Barra colorida onde cada cor indica o estado da parada, com relao lotao. A mesma cor exibida nas Informaes da Parada, no Gerenciador de Linhas e na barra acima do nome da parada que se v no jogo:
- amarelo: parada sem atendimento; nenhum veculo a utiliza.
- verde: no necessrio melhorar o atendimento; a parada no lota.
- laranja: alguma melhoria necessria.
- vermelho: necessrio melhorar o atendimento urgentemente.
(Dica: para ver as cores do estado do servio no jogo, clique em (!) at que aparea.)

Nome da parada. (Dica: possvel auto-numerar as paradas utilizando uma opo no arquivo simuconf.tab)

cones de veculos que indicam quais so os veculos aceitos na parada: nibus/autocarro, caminho/camio, trem/comboio, barco/navio e avio. Bondes/elctricos so ora representados pelo smbolo de nibus/autocarro, ora pelo de trem/comboio, conforme o tipo original da parada.

cones de carga que indicam o tipo de carga que a parada aceita. Podem ser passageiros, carga propriamente dita e correio. (Dica: h alguns tipos de plataforma que no aceitam nenhum tipo de carga por si ss, sendo necessrio acoplar ou um quadro de um tipo de plataforma que aceite a carga desejada, ou uma extenso que aceite a carga. Para adicionar um novo tipo de carga parada, necessrio acoplar um quadro de plataforma ou de extenso que aceite a caraga desejada. Para transportar correio, acople uma Agncia Postal.)

Na segunda linha vm os detalhes das diversas cargas esperando na parada.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/haltlist_filter.txt000066400000000000000000000051071520675460500231550ustar00rootroot00000000000000Filtro da Lista de Paradas

Filtro da Lista de Paradas

O Filtro da Lista de Paradas tem as opes para determinar quais paradas, onde os veculos acolhem e deixam passageiros e mercadorias, so mostradas na Lista de Paradas. (Isso s vale para paradas que no sejam exclusivas de veculos de transporte na gua).

Para acessar o filtro, clique sobre o boto Parmetros na Lista de Paradas para abrir o Filtro da Lista de Paradas.

Os botes de opo limitam as paradas que sero listadas na Lista de Paradas conforme o filtro desejado. Somente sero listadas as paradas que caibam em todos os critrios selecionados.

Clique sobre um boto para selecionar critrios para a filtragem (o boto fica afundado quando selecionado):

Filtro por nome: seleciona paradas pelo nome. Para us-lo, clique sobre o boto para ativar o filtro, e clique ento sobre a caixa de nome e digite o nome da parada exatamente. (a opo sensvel a maisculas e minsculas (ou caixa alta e baixa) ).

Filtro por tipo: listar as paradas que atendem a pelo menos um dos tipos de veculos selecionados. Para us-lo, clicar sobre os botes com os nomes do tipos de veculo. (Apenas as paradas exclusivas para veculos de transporte na gua no so listados na lista da paradas)

Filtro especial:
- lotadas: no est funcionando.
- no conectadas seleciona paradas com nenhum servio de transporte.

Necessidade: Seleciona as paradas de acordo com as necessidades das indstrias prximas. Use a barra de rolagem para rolar pela lista dos artigos. As opes incluem:
- tudo seleciona todos os artigos.
- nada desfaz todas as selees.
- inv. inverte a seleo atual.

Produo: Seleciona as paradas de acordo com os produtos das indtrias prximas. Use a barra de rolagem para rolar pela lista dos artigos. As opes incluem:
- tudo seleciona todos os artigos.
- nada desfaz todas as selees.
- inv. inverte a seleo atual.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/industry_info.txt000066400000000000000000000121311520675460500226530ustar00rootroot00000000000000Informaes das indstrias

Informaes das indstrias

A janela de Informaes das indstrias mostra detalhes sobre uma indstria selecionada (fornecedora ou consumidora de mercadorias) e permite se deslocar na rea de jogo at o local onde ela se encontra.

Fornecedoras e consumidoras de mercadorias so interdependentes, criando uma cadeia produtiva que termina em um consumidor-final. Algumas indstrias so ao mesmo tempo consumidoras e fornecedoras e se encontram no meio da cadeia produtiva. Se no se estiver jogando no modo iniciante, a indstria interromper o fornecimento enquanto sua consumidora estiver com excesso de mercadorias.
Indstrias tambm podem ser um destino ou origem de passageiros e correio. Veculos recolhem e entregam mercadorias e passageiros nas indstrias atravs de paradas se a rea de captura da parada abranger qualquer parte do prdio da indstria e se a parada for capaz de receber o item. {Dica: a rea de captura das paradas em alto-mar/plataformas de petrleo/fazendas marinhas no aparecem na lista de paradas}

O nmero inicial de cadeias produtivas pode ser alterado quando se cria um novo mundo. Conforme as reas urbanas crescem, mais indstrias podem aparecer. Novas indstrias aparecem toda vez que a populao da rea urbana dobra acima de 2000 habitantes.
{Dicas: adicione indstrias a uma rea urbana usando a ferramenta Construir mercado nas ferramentas de edio do mapa. Mude quando novas indstrias so geradas no arquivo cityrules.tab (industry_increase_every = 0) o valor zero no gera nenhuma indstria nova}.

Clique em uma indstria na rea de jogo com a ferramenta Consultar ou em uma indstria listada na lista das fbricas para abrir as informaes das indstrias.
Use a barra de rolagem para percorrer os itens que no estiverem visveis.

O nome da indstria selecionada mostrado na barra de ttulo das informaes das indstrias.
Outras informaes e opes sobre a indstria selecionada incluem:

Produo: quantidade mxima de mercadorias que a indstria fornece (se for uma fornecedora) ou consome (se for um consumidor-final) em unidades.
{Dica: colocando transformadores e suprindo a indstria com eletricidade faz aumentar a quantidade mxima de mercadorias consumidas/produzidas}.

Imagem da indstria: mostra uma imagem da indstria. Clique na imagem para ir at a indstria.

As cores da barra de status abaixo da imagem indicam a operao da indstria (tambm usada no Filtro da lista de indstrias):
- branco: a indstria no necessita de mercadorias.
- amarelo: a indstria est conectada rede de transporte, mas com fornecimento precrio.
- verde: indstria em timo estado.
- laranja: operacional, mas pode ser melhorado.
- vermelho: operacional, mas parte da cadeia industrial tem suprimentos em excesso.

Consumidor: mostra os destinos na cadeia produtiva para os bens produzidos na indstria selecionada.
Indstrias consumidoras e suas coordenadas no mapa so listadas ao lado de um boto em forma de seta. Clique nele para ir at a indstria.

Fornecedores: mostra a origem dos bens que devem ser fornecidos indstria selecionada.
Indstrias fornecedoras e suas coordenadas no mapa so listadas ao lado de um boto em forma de seta. Clique nele para ir at a indstria.

Trabalhadores de: mostra em quais reas urbanas prximas os empregados moram. Passageiros e correio dessas reas urbanas precisaro se deslocar at a indstria.
reas Urbanas e suas coordenadas no mapa so listadas ao lado de um boto em forma de seta. Clique nele para ir at a cidade.

Gerao de viagens: a popularidade relativa como destino para passageiros.
Gerao de correios: a popularidade relativa como destino para correio.
{Dica: somente uma parte da indstria precisa estar na rea de captura da estao/parada para ser destino de passageiros ou correio}.

Produo: d detalhes sobre a produo da indstria.
A informao mostrada inclui: nome do item; quantidade atualmente armazenada e quantidade mxima armazenvel do item; categoria; e porcentagem da sada comparada com a entrada.

Consumo: d detalhes sobre os bens fornecidos indstria.
A informao mostrada inclui: nome do item; quantidade atualmente armazenada e quantidade mxima armazenvel do item; categoria; e porcentagem da entrada comparada com a sada.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/inspection_tool.txt000066400000000000000000000135431520675460500231770ustar00rootroot00000000000000Consultar

Consultar

A ferramenta Consultar usada para abrir caixas de dilogo que fornecem informaes ou controles para vrios itens.

Clique na lupa no topo da rea de jogo ou pressione [a] para abrir a ferramenta Consultar (o cursor mudar para uma lupa).

Para usar: posicione o cursor em forma de lupa no local desejado na rea de jogo e clique para abrir a caixa de dilogo. Se mais de um item estiver no local desejado, outro clique abrir uma segunda caixa de dilogo. {Dica: a opo de abrir uma ou todas as caixas de dilogo do local est disponvel no arquivo simuconf.tab}

Clique nos seguintes itens na rea de jogo com a ferramenta para abrir as caixas de dilogo:

Depsitos - abre controles para comprar e gerenciar veculos naquele depsito.

Paradas - abre as informaes daquela parada.

Veculos - abre as informaes daquele veculo.

Prefeitura - abre as informaes da populao daquela rea urbana. {Dica: a opo para abrir a caixa de dilogo fornecendo informaes sobre o prdio da prefeitura est disponvel no arquivo simuconf.tab}

Indstrias - abre as informaes daquela indstria.

Edifcios abrem uma caixa de dilogo que fornece informaes sobre a construo (a barra de ttulo mostra 'Edifcio'). A informao contida inclui:
- Descrio: fornece informaes sobre o edifcio. Se no houver informao disponvel ento 'residncia', ou 'edifcio industrial', ou 'comrcio e lojas' (no caso de edifcios comerciais) mostrado.
- Cidade de: indica a qual rea urbana que o edifcio faz parte.
- Gerao de viagens: a popularidade relativa como destino para passageiros.
- Gerao de correios: a popularidade relativa como destino para correio.
- Construdo a partir de: indica o ano que o edifcio aparece quando se joga com Linha do tempo.
- Valor: indica o custo para remover o edifcio com a ferramenta Remover.
- Construdo por: nome da pessoa que criou o edifcio para o Simutrans.

Monumentos / Atraes tursticas abrem uma caixa de dilogo que fornece informaes sobre a atrao (a barra de ttulo mostra se um monumento ou uma atrao turstica). A informao contida inclui:
- Propriedade pblica: indica que o item no pertence a nenhuma companhia de transporte.
- Descrio: fornece informaes sobre a atrao.
- Gerao de viagens: a popularidade relativa como destino para passageiros.
- Gerao de correios: a popularidade relativa como destino para correio.
- Construdo a partir de: indica o ano que o edifcio aparece quando se joga com Linha do tempo.
- Construdo por: nome da pessoa que criou a atrao para o Simutrans.

Estradas e trilhos abrem uma caixa de dilogo que fornece informaes sobre a estrada ou trilho (a barra de ttulo mostra qual o tipo de via que se est usando - no caso de trilhos de bonde construdos em estradas a barra de ttulo indica o tipo da estrada). A informao contida inclui:
- Vel. Mxima: velocidade mxima permitida na estrada ou trilho.
- Ribi (unmasked): um contador interno que indica a direo da via.
- Ribi (masked): um contador interno que indica a direo da via levando em conta sinais e/ou placas.
- com sinal: mostrado se h um sinal ou placa presente.
- eletrificado / no eletrificado: indica se a via est eletrificada.
- comboios passados no ltimo ms: indica o nmero de veculos que usaram a via no ltimo ms do jogo.
- est reservado para um trem: indica qual veculo reservou o trecho de trilho para usar at ter passado.
- wayslope [aguardando definio]
- coordenadas: mostradas na barra inferior da rea de jogo
Caso haja trilhos de bonde construdos em estradas as informaes aparecem duas vezes, pimeiro para a estrada e depois para o trilho de bonde.

Sinais / Placas abrem uma caixa de dilogo que fornece informaes sobre o sinal ou placa (a barra de ttulo mostra se um sinal ou uma placa). A informao contida inclui:
- direo: um contador interno que indica a direo a que o sinal se aplica.

Linhas eltricas / Transformadores abrem uma caixa de dilogo que fornece informaes sobre a distribuio de eletricidade (a barra de ttulo nostra 'Linha de transmisso'). A informao contida inclui:
- Potncia: indica a quantidade de energia fornecida.
- Disponvel: o valor aparece nos transformadores das indstrias e indica a quantidade relativa de energia fornecida.
- Net: o nmero de identificao da rede de distribuio. Dois pontos com o mesmo nmero esto conectados.

rvores abrem uma caixa de dilogo (a barra de ttulo mostra 'rvore'). A informao contida inclui:
- Nome: mostra a espcie da rvore.
- Meses de idade: idade da rvore mostrada em meses.

{Dicas: opo de abrir caixas de dilogo em estradas privadas e pedestras est disponvel no arquivo simuconf.tab}.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/language.txt000066400000000000000000000017301520675460500215450ustar00rootroot00000000000000Ajuda - Lngua

Linguagem

Lngua altera o idioma utilizado no jogo.

Lngua abre a partir de Preferncias ou a partir de Novo Mundo.

Para jogar com outro idioma: clique no boto ao lado do idioma desejado para selecionar este idioma; para que os lugares tenham os nomes no novo idioma, faa isso antes de comear um novo mapa.

O novo idioma pode apenas aparecer em novas ou reabertas barras de ferramentas, opes e textos no jogo.
Se uma traduo no est disponvel no idioma selecionado, ele pode abrir em ingls.

Visite http://translator.simutrans.com para ajuda com tradues.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

Para ajudar na traduo do Simutrans para o seu idioma,
visite https://translator.simutrans.com

simutrans-124.5/simutrans/text/pt/linemanagement.txt000066400000000000000000000165511520675460500227550ustar00rootroot00000000000000Gerenciador de Linhas

Gerenciador de Linhas

O Gerenciador de Linhas fornece informaes e ferramentas para o gerenciamento de linhas (caminhos e quantidades mnimas de carregamento requeridas por uma composio para partir) para uma composio ou mais com a mesma rota.

Linhas consistem em Paradas onde os veculos pegam e deixam mercadorias e passageiros (veculos aquticos podem usar qualquer quadro de gua na rea de captura de uma doca) e pontos de passagem (usados para forar os veculos a irem por um caminho).

Clique no cone que (parece uma rede) na barra do alto da tela ou pressione [w] para abrir a janela do Gerenciador de Linhas, que pode ser redimensionada (clique no boto com uma seta para baixo na barra do ttulo para retornar ao tamanho original).

O lado esquerdo do Gerenciador de Linhas lista as linhas existentes (para rolar pela lista: use a barra do lado direito da lista), que pode ser filtrado por tipo de transporte, clicando numa das abas que ficam acima da lista pra selecionar:
Todos: lista as linhas de todos os tipos de veculos.
Trem: lista as linhas para os veculos ferrovirios.
Monotrilho: lista as linhas para os monotrilhos ou maglevs (trens magnticos).
Tram: lista as linhas dos bondes/elctricos.
Caminhes: lista as linhas para os veculos rodovirios.
Navios: lista as linhas para os veculos aquticos.
Ar: lista as linhas para os veculos areos.

A cor dos nomes das linhas significam o seguinte:
branco - no h nenhum veculo associado linha
amarelo - no operacional, ou no est dando lucro nem prejuzo
negro - linha lucrativa, com capacidade ociosa, sem necessidade de melhoria
azul - linha lucrativa, com pouca capacidade ociosa, ou com veculos obsoletos
vermelho - linha com perdas

Sob a lista das linhas existentes h alguns botes, que incluem:

Nova Linha: abre controles para definir uma Programao (caminhos e quantidades mnimas de carregamento requeridas por uma composio para partir) para uma nova linha.
Adicione tantas paradas ou pontos de passagem quanto necessrios, estabelea as quantidades de espera necessrias, e ento feche a janela para criar a nova linha, clicando no boto "x" na barra de ttulo ou com o teclado). A nova linha ter associado um nmero e ser listada na lista do Gerenciador de Linhas.
{Dicas: Selecione o tipo de transporte nas abas antes de criar uma nova linha. Os veculos no pegam nem deixam mercadorias e passageiros nos pontos de passagem.}

Atualizar: abre os controles para modificar uma Programao existente. As mudanas afetaro todos os veculos associados linha, assim que a janela desses controles for fechada.
Para us-lo, necessrio selecionar uma linha na lista antes de clicar nele e editar a linha.

Apagar: apaga a linha selecionada. Para us-lo, necessrio selecionar uma linha na lista antes de clicar nele e apagar a linha.

Quando uma linha selecionada, ele fica destacada das outras, e as paradas que esto associadas linha aparecem embaixo dos botes "Nova Linha", "Atualizar" e "Apagar". Clique numa parada para abrir suas informaes.
Os itens listados para cada parada incluem:
barra da cor de status: as cores indicam a situao da parada com relao sua lotao. a mesma barra mostrada na Lista de Paradas, na janela Informaes da Parada e na barra que fica acima da parada no jogo:
- amarelo: fora de servio.
- verde: nenhuma melhoria necessria.
- laranja: melhorias possveis.
- vermelho: melhorias necessrias.
{Dica: Use [!] para fazer aparecer a barra da cor de status acima da parada na tela do jogo.}
nome da parada.
nmero da parada (elas pode ser numeradas se a opo for escolhida no simuconf.tab).
cone de veculos(s) indicam quais tipos de veculo podem usar a parada. Os cones incluem: nibus/autocarro (para veculos rodovirios de transporte de passageiros), cami(nh)o (para veculos rodovirios de transporte de cargas), trem/comboio, barco e avio. Bondes/elctricos podem ser indicados por um nibus ou trem conforme o tipo de via da parada.
cone de carga(s) indica com quais itens (passageiros, cargas ou correio) uma parada pode lidar (tambm so usados nas Informaes das Paradas e na Lista de Paradas.
{Dica: adicionar extenses apropriadas pode modificar as categorias de itens que uma parada pode lidar. Agncias de correio podem ser adicionadas s paradas para o transporte de correspondncias}.
espera detalhes das diversas mercadorias e passageiros.

No lado direito do Gerenciador de Linhas so exibidos um grfico com dados da linha selecionada, uma caixa de nome e uma lista das composies associadas linha.

Clique no nome da linha pra selecion-la. Ele ficar destacado. Clique nos botes para ver as informaes sobre a linha, sendo que o eixo horizontal representa os ltimos 12 meses:

- Capacidade - quantidade de mercadorias ou passageiros que puderam ser transportadas no perodo.

- Vol. Transportado - quantidade de mercadorias ou passageiros que realmente foram transportados.

- Faturamento - total recebido pelo transporte de mercadorias ou passageiros.

- Operao - custo da operao dos veculos, ou seja, de eles estarem rodando.

- Lucro - diferena entre o faturamento e a operao.

- Veculos - nmero de veculos associados linha.

Para modificar o nome da linha: clique na caixa de nome, abaixo do grfico, onde aparece o nome da linha, e escreva o nome desejado.

Abaixo da caixa de nomes aparece a lista dos veculos associados a ela.
No topo da lista dos veculos vem a quantidade de veculos associados a ela, a renda da linha (o lucro operacional, melhor dizendo), a capacidade e a quantidade transportada no momento, com a porcentagem da capacidade entre parnteses).
Clique num item listado para ver mais informaes sobre o veculo selecionado (use a barra de rolagem para ver os outros veculos da linha).
So listados, para cada veculo:
nome nome dado do veculo, que normalmente o do primeiro componente comprado ou montado na composio).
renda o lucro operacional do veculo: faturamento menos custo operacional.
linha: mostra o nome da linha e se o veculo est num depsito.
grfico mostra o veculo e a capacidade utilizada do veculo no momento.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/list.txt000066400000000000000000000037771520675460500207520ustar00rootroot00000000000000Ajuda - Listas

Listas

Listas tem opes para abrir controles para informao sobre o estado atual do jogo (e tambm um controle para ajustar renda relativa recebida de diferentes bens, passageiros e correio).

A informao est disponvel para um jogador em listas sobre: Paradas; Veculos; reas urbanas; rendas recebidas de diferentes bens, passageiros e correio; Fbricas; e atraes tursticas.

Clique sobre o boto Listas no topo da tela do jogo para abrir Listas.
Mova o cursor do mouse, depois de abrir ou clicar na barra de ferramentas, por cima dos botes da barra para ver o nome.

Clique sobre o boto para abrir os controles de informao, da esquerda para direita, sobre:

Lista de Paradas: Paradas, onde os veculos carregam e descarregam objetos e passageiros (paradas s martimas no so listadas).

Lista de Veculos: veculos rodovirios e ferrovirios, aeronaves e barcos.

Lista das cidades: reas urbanas (aldeias, vilas e cidades).

Lista das cargas: rendas, recepo de diferentes bens, passageiros e correio (est includo um controle para ajustar renda relativa).

Lista das Fbricas: Fbricas (consumidoras e fornecedoras de bens).

Lista das atraes: atraes tursticas.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008, atualizado em 07/02/2008.

simutrans-124.5/simutrans/text/pt/load.txt000066400000000000000000000031001520675460500206720ustar00rootroot00000000000000Ajuda - Carregar jogo

Carregar jogo

A janela Carregar reabre um jogo salvo (para continuar a jogar de onde parou) e pode ser usado para apag-los. Um jogo reaberto por cima do mundo atual, que perdido.

Carregar abre de Novo Mundo e Preferncias ou pressionando [L], e mostra os nomes e data e hora de salvamento dos jogos disponveis.
Se todos os jogos no estiverem visveis: use a barra de rolagem no lado direito da lista para percorrer os nomes.

Clique em um nome da lista para reabrir o jogo, ou digite o nome na caixa de texto do topo da janela: clique na caixa, digite o nome e pressione [Enter] ou [Return] ou clique em OK.
Um nome incorreto mostra uma mensagem de erro: feche a mensagem e tente novamente.

AVISO: clique no X ao lado do nome para DELETAR o jogo imediata e permanentemente.
O nome removido da lista e a janela se fecha. Use com cuidado.

IMPORTANTE: novas verses do Simutrans surgem regularmente e jogos de verses anteriores podem no funcionar com verses mais novas. Consulte as notas da nova verso para saber se jogos antigos so compatveis.

Clique em Cancelar para fechar a janela Carregar, ou no X do canto superior esquerdo, ou use o teclado.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/load_relief.txt000066400000000000000000000030221520675460500222230ustar00rootroot00000000000000Ajuda - Carregar terreno

Carregar terreno

A janela Carregar terreno usada para abrir um arquivo de mapa personalizado (para ser usado em um novo jogo) e pode ser usada para deletar esses arquivos.

Carregar terreno abre a partir da janela de Novo mundo e lista nomes, data e hora de salvamento dos mapas disponveis.
Se todos os arquivos no estiverem visveis: use a barra de rolagem no lado direito da lista para percorrer os nomes.

Clique em um nome da lista para usar; ou digite o nome na caixa de nome no topo da janela: clique na caixa, digite o nome e pressione [Enter] ou [Return], ou clique em OK (um nome incorreto fechar a janela, tente novamente).

AVISO: clique no X ao lado do nome para DELETAR o mapa imediata e permanentemente. O nome removido da lista e a janela Carregar terreno se fecha. Use com cuidado.

Alguns mapas personalizados e informaes de como criar um esto disponveis na Internet em http://maps.simutrans.com.
O arquivo de mapa dever conter a extenso .ppm e colocado no diretrio (ou pasta) chamada ...simutrans/save/

Clique em Cancelar para fechar a janela Carregar terreno, ou no x do canto superior esquerdo, ou use o teclado.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/mailbox.txt000066400000000000000000000057521520675460500214250ustar00rootroot00000000000000Ajuda - Mensagens & Op;es das Mensagens

Mensagens

A janela Mensagens lista mensagens que o jogo lhe d e abre controles para selecionar como so mostradas.

Para abrir a janela de mensagens, clique no cone da caixa de correio na barra superior do jogo ou pressione [B].
Clique sobre a mensagem para abrir uma janela com a mensagem e a imagem do local dela. Clique no boto com uma flecha no canto esquerdo da mensagem para ir pro local relacionado a mensagem.

O boto Opes abre a Opes das mensagens para determinar como so exibidas as mensagens.

Opes das mensagens

A janela Opes das mensagens tem quatro colunas com botes quadrados que selecionam como so exibidas as mensagens.

Da esquerda para a direita, as colunas selecionam se as mensagens so mostradas como:
- Desativa as mensagens daquele tipo;
- Texto rolando na barra inferior do jogo;
- Janela temporria (aparece por alguns segundo); ou
- Janela permanente (fica na tela at voc fechar).
{Dica: clique no X no canto superior esquedo da mensagem ou use o teclado para fechar janelas permanentes.}

Segue o tipo de mensagens exibidas por linha:

Ano novo: Mostra uma mensagem todo comeo de ano avisando sobre o novo ano.

Concorrncia: Mostra mensagens sobre outros jogadores.

Notcias cidades: Mostra mensagens das cidades, como construo de monumentos e novas prefeituras.

Sem rota: Mostra uma mensagem toda vez que um veiculo no pode achar uma rota para seu prximo destino (uma parada ou ponto de passagem).

Novas rotas: Toda vez que uma nova rota ficar disponvel, com uma nova cadeia empresarial aparecer (um consumidor final e os fornecedores nescessrios) devido ao crecimentos de uma cidade.

Chat: Mostra textos de conversas de outros usurios quando estiver jogando online.

Notcias veculos: Mostra mensagens quando um novo veculo ficar disponvel, ou terminar a produo de um. (s aparecem se a opo linha do tempo estiver ligada).

Estao lotada: Se uma estao ou parada ficar cheia, aparecer uma mensagem.

Problemas: Mensagens de quaisquer problema com sua empresa de transporte, como dvidas.

Congestionamento: Quando um veculo seu ficar parado em um congestionamento, uma mensagem vai aparecer.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 11/08/2011.
Acesse a wiki para mais informaes e ajudas: http://wiki.simutrans-germany.com/

simutrans-124.5/simutrans/text/pt/map.txt000066400000000000000000000123551520675460500205440ustar00rootroot00000000000000Ajuda - Mapa

Mapa

Mapa fornece um mapa do mundo atual oferecendo informaes sobre terreno, lugares importantes e redes de transporte. Ele tambm fornece um modo para mover a viso do jogo pelo mundo.

Clique no cone do mapa no topo da viso do jogo ou pressione [m] para abrir o Mapa, cujo tamanho pode ser ajustado ou minimizado. Os trs botes abaixo da barra de ttulo abrem trs legendas: opes do mapa(explicadas mais abaixo), escala de cor mn./mx. e a legenda das indstrias.

O mapa pode ser visto em vrias tamanhos: aponte com o cursor do mouse e use o scroll, ou clique nas setas para a direita e para a esquerda na parte superior da janela para visualizar as escalas. Se a parte do mundo que deseja ver no estiver visvel, mude a escala do mapa, redimensione-o, ou mova-o: enquanto clica com o boto direito do mouse no mapa, arraste-o (ou use as barras deslizantes nas laterais).

No mapa, a gua azul e as terras mais baixas so mais escuras que as terras mais altas. A parte do mundo na viso do jogo mostrada por um quadrado com contorno amarelo no Mapa: o topo da viso do jogo corresponde ao lado superior esquerdo do retngulo amarelo (o Norte est no topo do mapa, enquanto na viso do jogo est no canto superior direito). Se o retngulo amarelo sair da rea do mapa, gua ser vista na viso do jogo na rea correspondente.

Para mover a viso do jogo por distncias grandes no mundo, clique com o boto esquerdo no ponto desejado ou clique com o boto esquerdo do mouse e ao mesmo tempo arraste o retngulo amarelo.

Aponte o cursor do mouse em uma indstria no mapa, para ver o seu nome. Se a opo "detalhes da fbrica" estiver ativada, aparecer uma linha branca ligando a indstria ao(s) seu(s) consumidor(es); pressionando [Shift] aparecer uma linha vermelha ligando a indstria ao(s) seu(s) fornecedor(es).

No mapa (quando as opes no esto sendo usadas): reas urbanas, atraes e tneis aparecem em cinza escuro; canais, azul; estradas, preto; ferrovias e trilhos de bonde, marrom escuro; pontes, cinza-claro; Paradas,vermelho; pistas de monotrilho/maglev, laranja; reas de aeroporto, bege; e veculos, em amarelo, normalmente em movimento.

Clique nos botes de opes para mostrar/ocultar detalhes no mapa (para os botes aparecerem, clique no boto Legenda):

Cidades: nomes das reas urbanas.

Passageiros: rea de captura para passageiros por Paradas.

Correio: rea de captura para correio por Paradas.

Mercadoria: nvel de mercadorias e passageiros em estradas e outros tipos de vias (usa a escala de cor mn./mx.).

Qualificao: as cores indicam a operao da Parada em relao a quo lotada ela est no momento. A mesma cor mostrada nas barras de status na Lista de Paradas e Informaes das Paradas e Gerenciador de Linhas e a mesma cor mostrada na barra de cor acima do nome de uma Parada na tela do jogo.

Servio: nvel de mercadorias e passageiros usando uma Parada (usa a escala de cor mn./mx.).

Trfego: nvel de veculos em estradas e outros tipos de vias (usa a escala de cor mn./mx.).

Origem: nvel de mercadorias e passageiros que comeam viagem de uma Parada (usa a escala de cor mn./mx.).

Destino: nvel de mercadorias e passageiros terminando uma viagem em uma Parada (usa a escala de cor mn./mx.).

Espera: nvel de mercadorias e passageiros esperando por transporte em uma Parada (usa a escala de cor mn./mx.).

Vias: ferrovias e trilhos de bonde e placas; trilhos, pontes e tneis so mostrados com a cor branca (vermelha se for eletrificado) e placas so mostradas com a cor amarela.

Vel. Mxima: estradas e pistas so mostradas pela velocidade mxima permitida (usa a escala de cor mn./mx.).

L. Eltricas: linhas eltricas (usa a escala de cor mn./mx. para visualizar o nvel de abastecimento do momento em que o Mapa foi aberto).

Turistas: nvel de passageiros que as atraes tursticas geram (usa a escala de cor mn./mx.); esta opo tambm mostra as atraes tursticas no mapa.

Fbricas: enfatiza as indstrias; destaca os consumidores e fornecedores no mapa.

Depsitos: enfatiza os depsitos. As cores indicam o tipo de transporte: navios, vermelho; estradas, amarelo; ferrovias, laranja; avies, lils; bondes, laranja escuro; e monotrilhos, marrom escuro.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/monorailtools.txt000066400000000000000000000203721520675460500226660ustar00rootroot00000000000000Ajuda - Monotrilhos e Maglevs

Monotrilhos e Maglevs

A Barra Monotrilhos e Maglevs mostra as ferramentas para construir uma rede de transporte de monotrilhos ou Maglevs. As ferramentas podem construir ou remover: trilhos de monotrilhos e Maglevs; pontes; tneis; sinais; depsitos; estaes-plataforma e extenses. Se jogar com a Linha do Tempo ativa, com a passagem do tempo no Simutrans, novas opes podem aparecer.

Clique no cone do monotrilho na barra de ferramentas. Mova o cursor do mouse sobre a barra de opes (aps abrir ou clicar na barra de ferramentas) para ver o nome e, onde for o caso, o custo da construo, custo de manuteno (em parnteses) e limites de velocidade mxima e comprimento mximo.

A Barra Monotrilhos e Maglevs inclui, da esquerda para a direita:

Trilho de monotrilho/Maglev: ferramenta que constri trilhos, para veculos monotrilhos e Maglevs entre dois pontos. Os trilhos s podem ser construdos em barrancos na direo dos mesmos e no podem encontrar um caminho atravs de terrenos acidentados, gua ou obstculos. Novos trilhos construdos entre dois pontos podem usar outros trilhos existentes em seu caminho.
Para construir trilhos: clique no boto para selecionar o trilho desejado (para monotrilho ou Maglev; ento o cursor do mouse muda para o formato de um trilho), e ento clicar no terreno para construir o ponto inicial para os trilhos (mostra um trator na tela do jogo e mostra as coordenadas no canto direito da barra inferior na tela) e finalmente clique em um outro ponto no terreno para construir o ponto final do trilho.
{Dica: Diferentes tipos de trilhos podem conectar (no podero quando construdos por outras companhias). Use o Editor de terrenos para modificar o terreno, para abrir caminhos para os trilhos. Use o boto Remover para remover trilhos individuais e alguns obstculos. Pressione [Ctrl] ao mesmo tempo para acessar funes extras. Para desfazer algo j feito aperte [z], mas isso no o reembolsar dos custos de construo.}

Remover trecho de monotrilho: essa ferramenta remove trilhos de monotrilhos e Maglevs, quando no h nenhum veculo no momento obstrundo, entre dois pontos na tela do jogo (platformas and sinais no caminho tambm so removidos).
O uso dessa ferramenta implica em custos de construo.
Para remover um trilho: clique neste boto (o cursor torna-se num X vermelho); e ento clique no trilho a ser destrudo (nesse mesmo ponto fixado um X vermelho); e finalmente clique em um outro ponto nos trilhos, para remover o trecho entre estes dois pontos.
{Dica: Altere o jogador para remover os trilhos de cada companhia.}

Rampas e Pontes: ferramentas que constroem pontes retas, para veculos de monotrilho/maglev passarem, entre dois treos de trilho de monotrilho/maglev na viso do jogo. As pontes tm um comprimento mximo. A ferramenta contri pontes de uma ponta de trilho at um outro lugar adequado (outra ponta de trilho ou um barranco mais alto, dentro do limite de comprimento.
Para construir uma rampa ou ponte: clique no boto para selecion-lo (o cursor torna-se numa ponte), e ento clique no fim de um trilho (ponto inicial da ponte) para constru-la. Certas obstrues ou a falta de um local adequado para o fim da ponte evitar a construo da ponte: coloque trilhos em ambos os lados a serem conectados pela ponte, e tente novamente.
{Dica: Use o boto Remover para remover pontes (clique no fim da ponte) e alguns obstculos construo da ponte. Use as ferramentas para trilhos para conectar o fim das pontes aos trilhos.}

Sinais: essa ferramenta constri sinais para monotrilhos e Maglevs num trilho na viso do jogo. Os sinais direcionam e regulam o fluxo de veculos em trilhos e pontes, cruzamentos e Paradas (onde os passageiros embarcam e desembarcam nos veculos).
Verses de sinais de mo-dupla e nica podem ser construdas. Para construir um sinal de mo-dupla em um trilho: clique em um dos botes de sinais para escolher um desejado (muda-se o cursor para um sinal); e ento clique no trilho. Para construir um sinal de mo-nica: clique de novo no mesmo ponto, com o cursor com a forma de um sinal, para escolher entre os sinais de mo-nica e dupla.
IMPORTANTE: Tome cuidado para no colocar sinais de mo-nica que impeam os veculos de chegar aos seus destinos, por padro os veculos dirigem pela direita (isso pode ser alterado no simuconf.tab).
- Semforo de monotrilho: os veculos prosseguem somente se o trilho frente at o prximo semforo ou destino na Programao (a Parada ou ponto de passagem) no estiver ocupado por um outro veculo. No modo de mo-nica os veculos passam apenas em uma nica direo.
- PreSignals: os veculos prosseguem se a seo do trilho frente (ou entre trs sinais consecutivos ou at o prximo destino na Programao) no estiver ocupada por um outro veculo. No modo de mo-nica eles deixam os veculos passarem em apenas uma direo.
- Direcionador de monotrilho para vaga livre: direciona os veculos a uma estao vazia, em uma Parada multi-plataforma: um veculo que passa por esse sinal pode usar qualquer plataforma vazia no seu prximo destino, e no apenas aquelas que foram definidas em sua Programao. Se no for encontrada uma plataforma vazia ou um caminho vazio at o prximo destino, ento os veculos aguardaro nesse sinal.
{Dica: Remova sinais com o boto Remover. Pressione [Ctrl] para colocar sinais em pontes sobre trilhos em nvel inferior.}

Depsito de monotrilhos: essa ferramenta constri um depsito para comprar e gerenciar monotrilhos e Maglevs, tanto veculos como seus vages. Os depsitos possuem um custo de manuteno e so construdos no fim de um trilho de monotrilho/Maglev.
Para construir um depsito de monotrilhos: clique no boto (o cursor torna-se num depsito), ento clique no fim de um trilho.

Estao: essa ferramenta constri estaes, utilizadas por monotrilhos e Maglevs para carregar e descarregar passageiros e correio.
Uma seo de estao quando no construda adjacente a uma Parada j existente criar uma nova Parada.
As estaes possuem um custo de manuteno e so construdos sobre trilhos (mas no em curvas e cruzamentos).
As estaes possuem uma rea de captura para mercadorias, passageiros e correio. Diferentes estaes podem ter diferentes capacidades para mercadorias, passageiros e correio. No canto de alguns botes um cone (utilizado em Lista de Paradas e Informaes das Paradas) mostra quais itens a estao aceita.
Para construir uma estao: clique neste boto para selecionar (o cursor torna-se numa estao), e ento clique em um trilho.
{Dica: Remova as estaes com o boto Remover. Estender e criar estaes multi-plataformas (construindo mais plataformas nos trilhos) servem para acomodar veculos mais longos e em maior nmero, aumentar a capacidade e rea de captura. Aperte [v] para mostrar/ocultar a rea de captura de mercadorias e passageiros na tela do jogo. Pressione [Ctrl] para construir em cima de pontes ou trilhos elevados sobre trilhos em nvel inferior.}

Suporte para monotrilho:essa ferramenta constri uma extenso a uma Parada e aumenta a capacidade da mesma. Fundaes de monotrilhos so construdas no final de um trilho de monotrilho/Maglev e possuem um custo de manuteno.
Para construir uma fundao de monotrilho: clique neste boto para selecionar (o cursor torna-se numa fundao), ento clique na posio desejada no fim de um trilho.
{Dica: Remova as fundaes de monotrilho com a ferramenta Remover.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 07/02/2008.

simutrans-124.5/simutrans/text/pt/mouse.txt000066400000000000000000000025331520675460500211140ustar00rootroot00000000000000Ajuda - Mouse

Mouse

Com o mouse voc pode quase comandar todo o Simutrans sem o teclado.
Com ele voc pode adicionar vrias ferramentas nas diversas categorias: Trilhos, Estradas, Navegao, Aviao, Transporte Terrestre, Edio do Terreno, Excluir, Mais Ajuda, Finanas, Foto, Pausa, Ferramentas Especiais, Configuraes, Novo Mapa, Salvar, Carregar, Alterar Cor da Companhia, Adicionar Jogadores, Mudar Jogador, Comprar e Vender Veculos, Criar novas Linhas de Transporte, Ver Informaes de Edifcios e muitas outras.

As funes dos botes:

Boto Esquerdo: aplica todas as aes ou objetos que voc seleciona nos menus variados, como por exemplo criar uma rua, ver informaes de um determinado edifcio, modificar ruas ou at adicionar um veculo.

Boto Scroll: (em apenas alguns mouses) tem como servio aumentar ou reduzir o zoom do jogo, girando-o para trs voc reduz o zoom e girando-o para frente, voc o aumenta.

Boto Direito: clicando, segurando e arrastando ao mesmo tempo, voc movimenta a tela do jogo para todos os lados at local que voc deseja sem precisar do teclado ou mapa.

Escrito pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 07/02/2008.

simutrans-124.5/simutrans/text/pt/new_world.txt000066400000000000000000000131031520675460500217570ustar00rootroot00000000000000Ajuda - Criar novo jogo

Comear um Novo Jogo

Criar novo jogo permite modificar as opes para um novo jogo, com um novo mapa e configuraes.
As variveis incluem: tamanho do terreno, nmero inicial de reas urbanas e indstrias.
Uma prvia do terreno, com os ajustes, mostrada.

Criar novo jogo abre com Lngua e Controle do Clima quando o Simutrans iniciado (outras opes do jogo esto desligadas) e tambm abre do boto Preferncias (quaisquer barras de ferramentas, textos e opes no jogo atual so fechados).
Os atalhos do teclado para abrir a janela Criar novo jogo incluem apertar [Q] e [X].

Para retornar ao jogo atual, feche a janela Criar novo jogo: clicando no x no canto superior esquerdo da janela; ou voc pode utilizar o teclado.

Os botes em forma de seta permitem ajustar as configuraes e os botes que podem ser "apertados" (baixo/alto relevo) ligam/desligam opes ou abrem novos controles:

Nmero: seleciona o terreno a ser utilizado no jogo.
Clique na caixa de texto com o nmero e digite o nmero desejado ou use os botes em forma de seta para selecionar um entre vrios terrenos.
A prvia mostra o terreno selecionado (a gua azul, e quanto mais baixo o nvel da terra, mais escura fica a cor).

Dimenso: ajusta o tamanho do mapa (o nmero superior largura, o inferior altura).
O nmero em colchetes a memria requerida pelo mapa.
IMPORTANTE: Mapas maiores precisam de mais memria e precisam de mais tempo para serem criados. O jogo e outros grficos precisam de aproximadamente 88MB no pak64 e 144MB no pak128. Geralmente um computador com 256MB de memria consegue operar mapas com o tamanho de at 512 x 512 quadros.

Aleatrio: seleciona um novo terreno com um nmero do mapa aleatrio, mas as outras opes no so alteradas.

Carregar terreno: abre os controles para utilizar os mapas disponveis personalizados.

Cidades: ajusta o nmero inicial de reas urbanas no jogo.

Moradores por cidade: ajusta a populao inicial das reas urbanas.
Essa configurao um valor mdio, o Simutrans tentar gerar mais cidades pequenas do que cidades grandes.

Ext. vias interurbanas: ajusta a extenso inicial das estradas, entre reas urbanas, para um novo jogo.

Dens. de trfego: ajusta o nmero de carros privados nas estradas.
Valores maiores criam mais trfego, 0 (zero) gera nenhum. Novos carros aparecero medida que as reas urbanas crescem.

Indstrias rurais: ajusta o nmero inicial de cadeias industriais fornecedoras fora das reas urbanas.
Se o terreno muito irregular o espao adequado pode no ser encontrado e menos indstrias aparecero.

Indstrias urbanas: ajusta o nmero inicial de consumidores finais de mercadorias nas reas urbanas, como lojas e postos de gasolina.
Novos consumidores (e possivelmente fornecedores, sem ser necessariamente em reas urbanas) aparecem cada vez que a populao aumenta em aproximadamente 2000 habitantes.

Atraes tursticas: ajusta o nmero inicial de construes fora das reas urbanas que atraem passageiros e correio.
Essas atraes aparecem fora das reas urbanas e em somam-se com quaisquer atraes geradas em novas vilas ou cidades no comeo do jogo. Essas atraes tambm geram passageiros e correio.

Linha do tempo a partir de: permite alterar o ano em que o jogo comea. Quando o boto em forma de quadrado est "apertado", essa opo est selecionada e, ao passar do tempo, mais veculos e opes de construo podero aparecer.
Quando o boto em forma de quadrado no est "apertado": o jogo ainda iniciar no ano escolhido, mas todos os veculos e opes de contruo aparecero imediatamente; as reas urbanas aparecero com prdios de todas as pocas; e veculos mais lentos no so to lucrativos (j que o lucro calculado pela velocidade mdia do transporte disponvel).

Permitir mudana de jogador: essa opo lhe deixa jogar como um jogador dono das empresas que so comandadas pela IA ou como o servio pblico (esta opo no pode ser acessada aps o jogo ser iniciado).

Modo iniciante: essa opo faz com que o jogador receba um lucro maior no jogo por transportar mercadorias e passageiros (padro: 150% dos valores recebidos no modo normal). Alm disso, as Indstrias no pararo de produzir quando o(s) consumidor(es) j tiver(em) excesso de mercadorias.

Carregar jogo: abre os controles para jogar um jogo salvo previamente, substituindo o mundo atual que perdido ao selecionar um jogo salvo.

Criar jogo: cria um novo mundo, usando as configuraes mostradas na tela. Clique nesse boto para comear a jogar - Boa Sorte e Curta o Jogo!

{Dica: Os valores usados por padro quando o Simutrans iniciado e outras configuraes podem ser alterados no arquivo simuconf.tab}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008. Atualizado em 18/12/2010.

simutrans-124.5/simutrans/text/pt/options.txt000066400000000000000000000027041520675460500214570ustar00rootroot00000000000000Ajuda - Opes

Opes

Opes abre controles para a operao e apresentao do jogo; e sair do Simutrans a partir da janela Opes.

Clique sobre o cone do disquete no topo da tela do jogo para abrir as Opes.

Clique sobre um boto para abrir novos controles:

Idioma: define um idioma a ser usado no jogo.

Cores: define a cor da companhia do Jogador.

Vdeo: cofiguraes de como o jogo se aparentar visualmente no Simutrans.

Som: configuraes para som e msica no jogo.

Jogadores: abre controles para diferentes companhias dos jogadores.

Carregar: abre controles para continuar um jogo salvo.

Salvar: abre controles para gravar o atual jogo, para continuar jogar depois.

Novo jogo: abre controles para comenar um novo jogo (isto fecha as barras de ferramentas, textos e opes do jogo atual).

Sair: fecha o Simutrans, e o jogo atual no salvo.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008. Atualizado em 20/12/2010.

simutrans-124.5/simutrans/text/pt/players.txt000066400000000000000000000044731520675460500214500ustar00rootroot00000000000000Ajuda - Lista de jogadores

Lista de jogadores

A Lista de jogadores fornece informao e possui controles para as diferentes companhias dos jogadores.

Clique em 'Jogadores' na janela Preferncias ou pressione [k] para abrir a Lista de jogadores, que mostra o nome das companhias e seus balanos.

H oito jogadores: jogador humano, Poder pblico e seis jogadores controlados por inteligncia artificial (IA). Os controles permitem jogar como outra companhia de transporte se Permitir mudana de jogador foi selecionado na criao do jogo.

Clique no boto em forma de seta para atuar como aquela companhia no jogo atual. Uma mensagem de confirmao aparece quando se trocado o jogador: clique no x do canto superior esquerdo da mensagem ou use o teclado para fech-la.

Clique no nome do jogador para ver suas Finanas.

Clique no boto quadrado ao lado de um jogador de IA para ativ-lo/desativ-lo (o boto aparece pressionado quando a opo selecionada).

Jogadores de IA no vo bancarrota e gerenciam seus veculos com mxima eficincia.
Desativando um jogador de IA durante o jogo no remover sua rede de transporte.
Alguns jogadores de IA podem usar somente veculos de estradas ou ferrovias, alguns podem somente transportar mercadorias ou passageiros.

{Dicas: veculos no podem usar trilhos de outro jogador ou estaes (de qualquer modo, veculos de todos os jogadores podem usar itens construdos pelo Poder pblico).
Jogadores no podem construir itens ou colocar marcadores nos trilhos ou estradas de outro jogador, ou remover itens construdos por outro jogador (de qualquer modo, alguns itens construdos pelo Poder pblico podem ser removidos usando a ferramenta Remover).
Desabilitar a mudana de jogador tambm restringe as ferramentas extras como Poder pblico.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/railtools.txt000066400000000000000000000235771520675460500220070ustar00rootroot00000000000000Ferrovias

Ferrovias

A Barra Ferrovias mostra as ferramentas para construir uma rede de transporte sobre trilhos. As ferramentas podem construir os trilhos (tambm eletrificar e demolir), pontes, tneis, sinais, depsitos, estaes, plataformas e extenses. Se jogar com a Linha do Tempo ativa, com a passagem do tempo no Simutrans, novas ferramentas podem aparecer.

Clique no cone de trem na barra de ferramentas. Mova o cursor do mouse por cima do boto, depois de abrir ou clicar na barra de ferramentas, para ver o nome e, onde for o caso, o custo da construo, custo de manuteno (dentro dos parnteses), limite de velocidade mxima e comprimento mximo.

A Barra Ferrovias inclui, da esquerda para a direita:

Trilhos: ferramenta de construir trilhos, para trens, entre dois pontos na viso do jogo. Os trilhos s podem ser construdos sobre barrancos na direo dos mesmos e no podem encontrar caminhos atravs de terreno acidentado ou sobre a gua (s se utilizar pontes). Um trilho construdo entre dois pontos pode usar um trilho existente em seu caminho.
Para construir um trilho: clique na ferramenta para selecionar o trilho (o cursor torna-se num trilho), em seguida, clique no terreno para dar o ponto de partida do trilho (mostra um trator na viso do jogo e exibe as coordenadas no mapa inferior direito do ecr), e finalmente clique no terreno para o ponto final do trilho.
{Dica: Diferentes tipos de trilhos podem ser ligados (porm no quando construdos por diferentescompanhias). Voc pode utilizar tambm pontes e tneis para desviar de montanhas e obstrues. Use Editor de terrenos para mudar terreno, a fim de permitir caminhos para trilhos. Use a ferramenta Remover para remover pistas individuais e algumas obstrues. Use [Ctrl] ao mesmo tempo para funes extras. Utilize o [z] para desfazer algo que j foi feito (custos no so reembolsados). Pressione [t] para ativar a ferramenta de construo do ultimo trilho utilizado.}

Eletrificar Trilhos: esta ferramenta eletrifica trilhos, pontes, depsitos e tneis, para uso dos veculos eltricos.
Clique na ferramenta ou pressione [e] (o cursor torna-se num par de postes de eletrificao), em seguida, clique no trilho na viso do jogo para dar o ponto inicial da eletrificao (o mesmo cone do cursor fixado neste ponto), e finalmente clique no segundo ponto no trilho, para eletrificar o trecho entre os dois pontos.
{Dica: Use a ferramenta Remover para remover a eletrificao dos trilhos.}

Remover trecho de trilhos: esta ferramenta remove trilhos eletrificados e no eletrificados, quando no h veculos entre dois pontos na viso do jogo (Nota: plataformas, sinais, tneis e pontes do trecho tambm so removidos). O uso da ferramenta associada ao custo da construo.
Para remover um trecho de trilho: clique na ferramenta (cursor se muda numa cruz vermelha), depois clique no ponto inicial do trecho a ser removido (neste mesmo ponto fixada uma cruz vermelha); e finalmente clique em outro ponto do trilho, para remover o trecho entre estes dois pontos.
{Dica: Altere o jogador para remover os trilhos de cada companhia.}

Pontes: ferramenta para construir pontes retas, para os veculos ferrovirios passarem, entre duas sees de trilhos na viso do jogo. As pontes tm um comprimento mximo. A ferramenta constri pontes de uma ponta de trilho at um outro lugar adequado (outra ponta de trilho ou um barranco mais alto, dentro do limite de comprimento).
Para construir uma ponte: clique num boto de ponte desejada (o cursor torna-se numa ponte), e clique no final de um trilho (ponto inicial da ponte) para construir. Alguns obstculos podem ser passados, mas montanhas altas ou falta de um local adequado para a ponte finalizar podem parar a sua construo, ento deve-se colocar o trilho com as pontas alinhadas em ambos os lados do local onde se estar sendo construda a ponte, e tente novamente.
{Dicas: Use a ferramenta Remover para remover pontes (clique sobre o ponto final da ponte) e alguns obstculos frente, como um edifcio alto, por exemplo. Use as ferramentas para trilhos para conectar os fins da ponte aos trilhos.}

Tnel para trem: esta ferramenta constri um tnel reto, para os veculos ferrovirios poderem atravessar pelo subsolo, entre dois trilhos alinhados na viso do jogo. Tneis no podem ser inclinados: os veculos entram e saem na mesma altura. A ferramenta cria um tnel no fim de um trilho, sendo colocado em um barranco.
Para construir um tnel: clique na ferramenta (cursor torna-se num tnel) e, em seguida, clique no final do trilho em um baranco para a entrada do tnel. Se um tnel no encontrar uma sada adequada (um barranco livre de obstrues), ele no ser construdo: coloque o trilho em ambos os lados que devem ser conectados, e tente novamente.
{Dica: Use a ferramenta de Edio de terrenos e a ferramenta Remover para criar locais adequados para entradas e sadas de um tnel. Use as ferramentas para trilhos para conectar sadas de tneis aos trilhos.}

Sinais para Trilhos:constri sinais para os veculos ferrovirios em um trilho na viso do jogo. Sinais direcionam e regulam o fluxo de veculos em trilhos e pontes, e em cruzamentos e estaes. Sinais bidirecionais e de mo-nica tambm podem ser construdos.
Para construir um sinal bidirecional no trilho: clique na ferramenta para selecionar sinal (cursor torna-se num par de sinais), depois clique no trilho. Para construir um sinal de mo-nica: clique novamente no mesmo ponto, com o cursor de sinais, e clique novamente para voltar a ter um sinal para os dois sentidos.
IMPORTANTE: Tome cuidado ao colocar sinais de mo nica, que impede alguns veculos chegarem ao destino; por padro veculos so conduzidos direita (pode ser alterado em simuconf.tab).
-Semforo ferrovirio: os veculos prosseguem somente se o trilho frente at o prximo semforo ou at o prximo destino da Programao (uma Estao ou ponto de passagem) no est ocupado por nenhum outro veculo. No modo mo-nica, estes sinais deixam passar veculos em uma nica direo.
- Sentido nico: veculos no prosseguem alm do sinal (disponvel s em modo mo-nica).
-Pr-semforo ferrovirio: os veculos proseguem se a seo do trilho frente (ou entre trs consecutivos sinais ou at o prximo destino da Programao) no est ocupado por nenhum outro veculo. No modo mo-nica, estes sinais deixam passar veculos em uma nica direo.
-Direcionador ferrovirio para vaga livre: direciona os veculos a uma plataforma vazia, em uma estao multi-plataforma: um veculo passando este sinal pode usar qualquer plataforma vazia em seu destino, e no apenas aquelas definidas em sua Programao. Se nenhuma plataforma ou rota para o prximo destino vazia encontrada, os veculos iro esperar no sinal.
{Dica: Remova os sinais com a ferramenta Remover. Mantenha pressionada [Ctrl] para colocar sinais sobre pontes ferrovirias acima de trilhos em nvel inferior.}

Depsitos ferrovirios: esta ferramenta cria um depsito para compra e gerenciamento de comboios e composies. Depsitos tm um custo e manuteno e so construdos no final do trilho ferrovirio na viso do jogo.
Para construir um depsito: clique na ferramenta (cursor torna-se num depsito), em seguida, clique no final do trilho.
{Dica: Para remover depsitos use a ferramenta Remover. Depsitos somente mostram veculos eltricos se estiver num trilho eletrificado.}

Estaes: a ferramenta constri plataformas, utilizadas pelos veculos para carregar e descarregar mercadorias e passageiros. Plataformas tm um custo e manuteno e so construdas no trilho (mas no em curvas e cruzamentos).
A plataforma tem uma rea de captura para mercadorias, passageiros e correio. Plataformas diferentes podem ter diferentes capacidades de mercadorias e passageiros. No canto de alguns botes, um cone (utilizado em Lista de Paradas e Informaes das Paradas) mostra quais itens so tratados pela plataforma.
Para construir uma plataforma: clique na ferramenta para selecionar plataforma (cursor torna-se numa plataforma) e, em seguida, clique no trilho.
{Dica: Para remover plataformas da estao utilize a ferrementa Remover. Estender e criar estaes multi-plataformas (construindo mais plataformas nos trilho) servem para acomodar mais veculos, aumentando a capacidade de carregamento. Pressione [v] para mostrar/esconder a rea de captura de mercadorias e passageiros na viso do jogo. Mantenha pressionada[Ctrl] para colocar estaes sobre pontes ferrovirias acima de trilhos em nvel inferior.}

Extenses: esta ferramenta constri extenses e edifcios para estaes, de modo que se possa aumentar a capacidade, rea de captura para passageiros e mercadorias, e o custo de manuteno. No canto de alguns botes, um cone (usado na Lista de Paradas e Informaes das Paradas), que mostra itens que so tratados pela extenso.
Para construir uma extenso: clique na ferramenta para selecionar (cursor torna-se numa estenso) e, em seguida, clique na posio exigida, ao lado de uma estao, na viso do jogo. A nova extenso agora considerada como sendo parte da estao.
{Dica: Remova as extenses utilizando a ferramenta Remover.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/removal_tool.txt000066400000000000000000000047231520675460500224710ustar00rootroot00000000000000Ferramenta Remover

Ferramenta Remover

Ferramenta Remover usada para destruir e remover os itens e obstculos no desejados construo em um nico quadro do terreno na tela do jogo.

Para usar: Clique no cone com uma cruz vermelha (ou escavadora, no pak128) no topo da tela do jogo ou pressione [a] para abrir a Ferramenta Remover (o cursor torna-se numa cruz vermelha numa nuvem de poeira).
Posicione o cursor na tela dojogo, um retngulo amarelo indica a posio em que a ferramenta ir operar (as coordenadas do mapa so indicadas na barra cinza abaixo). Ento clique sobre o objeto na tela do jogo para remover.

O custo do uso da ferramenta indicado na tela do jogo e aparece bem onde estava o item eliminado. O custo da remoo equivalente ao custo da construo para os itens do jogador.
O custo de destruir edifcios da cidade o mesmo valor apresentado na caixa de dilogo deste edifcio aberta pela Ferramenta de consula. O custo de destruir rvores de 100 Hajo Credits.

Ferramenta de remoo pode ser usada para remover os pedestres individuais e os carros privados.

A ordem de remoo dos itens em um quadro do terreno: carros e pedestres, sinais, paradas ou plataforma, eletrificao das vias, ponte ou tnel, vias (trilhos primeiramente se sobre estradas).

{Dicas: Para remover um veculo do jogador, venda o veculo a partir de Detalhes do Comboio.
Para remover indstrias, atraes tursticas e a prefeitura, primeiro altere para jogador Servio Pblico. Por favor ateno: o uso da ferramenta de remoo em uma prefeitura remover todos os edifcios da cidade da rea urbana.
Use [Ctrl] para usar a ferramenta em objetos no nvel superior.
Para destruir/remover itens de outros jogadores, primeiro faa a mudana de jogador em Lista de Jogadores.
Para remover trechos de um trilho ou estrada, use Remover trecho de trilhos tambm encontrado em Bondes) ou Remover trecho de estradas.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/roadtools.txt000066400000000000000000000257311520675460500217770ustar00rootroot00000000000000Ajuda - Estradas

Estradas

A Barra Estradas possibilita a construo de uma rede de transportes rodoviria para mercadorias e passageiros. As ferramentas nesta barra podem construir ou remover: estradas, pontes, tneis, sinais, depsitos, extenses e Paradas. Caso esteja jogando com a linha do tempo ativada, ento com o passar do tempo mais opes podero aparecer.

Clique no cone do caminho na barra de ferramentas. Mova o cursor do mouse sobre a barra de opes (aps abrir ou clicar na barra de ferramentas) para ver o nome e, onde for o caso, o custo da construo, custo de manuteno (em parnteses), velocidade mxima permitida e o comprimento mximo.

A Barra Estradas inclui, da esquerda para a direita:

Estradas: ferramenta a qual constri uma estrada entre dois pontos para veculos rodovirios. As estradas no podem ser construdas em reas ocupadas por outras construes, e podem apenas ser construdas em barrancos na direo dos mesmos e no podem encontrar um caminho atravs de terrenos acidentados ou gua. As novas estradas construdas podem se conectar com outras j existentes. Existem atualmente diversos tipos de estradas, com diferentes velocidades mximas permitidas e custos de construo e manuteno.
Para construir uma estrada: clique em um dos botes com a imagem de uma estrada para escolher um tipo (o cursor torna-se uma estrada), ento clique na viso do jogo em um local para ser o ponto inicial da estrada (mostra um trator na viso do jogo e as coordenadas do local na barra inferior da tela no canto direito), e ento clique em um outro ponto na viso do jogo para ser o ponto final da estrada.
{Dica: Diferentes tipos de estradas podem se conectar mesmo se forem construdas por companhias diferentes. Use pontes e tneis para conectar as estradas passando por terreno acidentado, ou para evitar obstculos. Use o Editor de terrenos para alterar o terreno, abrindo caminhos para as estradas. Use o boto Remover para remover sees individuais de estradas e alguns obstculos. Aperte [Ctrl] ao mesmo tempo para funes extras. Desfazendo alteraes apertando [z] no o reembolsa dos custos da construo. Aperte [s] para selecionar a ltima ferramenta para construo de estradas usada.}

Remover trecho de estrada: essa ferramenta remove estradas, quando no h nenhum veculo passando, entre dois pontos na viso do jogo (qualquer Parada rodoviria, sinal, tnel ou ponte no caminho removido junto). O uso dessa ferramenta implica em custos de construo.
Para remover um trecho de estrada: clique nessa ferramenta (muda o cursor para um X vermelho); ento clique no trecho a ser destrudo (nesse mesmo ponto fixado um X vermelho); e finalmente clique em um outro ponto na estrada, para remover o trecho entre os dois pontos.
{Dica: Jogue como uma outra companhia para remover suas respectivas estradas.}

Ponte rodoviria: possibilita a construo de pontes retas, para que os veculos possam passar entre dois trechos de estrada na viso do jogo. As pontes tm uma extenso mxima. Essa ferramenta constri as pontes a partir do fim de uma estrada at um local adequado (outro fim de estrada ou um local com uma altitude levemente superior, dentro dos limites do comprimento mximo permitido).
Para construir uma ponte: clique em uma ferramenta de construo de ponte rodoviria para selecionar um tipo (muda o cursor para uma ponte), e ento clique no fim de uma estrada (ponto inicial da ponte). Certos obstculos ou a falta de um local adequado para o fim da ponte pode cancelar a construo da ponte: construa estradas em ambos os lados a serem conectados pela ponte, e tente novamente.
{Dica: Use o boto Remover para remover pontes (clique no fim da ponte) e tambm certos obstculos construo de pontes. Use os botes de construo de Estradas para conectar as extremidades das pontes s estradas.}

Tnel rodovirio: possibilita a construo de um tnel reto, para que os veculos possam passar atravs da terra, entre dois trechos de estrada na viso do jogo. Os tneis no podem ser inclinados: os veculos entram e saem dos tneis a uma mesma altitude.
Essa ferramenta constri um tnel, a partir do fim de uma estrada colocada em uma inclinao. Para construir um tnel: clique nessa ferramenta (muda o cursor para um tnel), e ento clique em um fim de estrada em um uma inclinao para construir a entrada do tnel. Se o tnel no puder encontrar uma sada adequada (uma inclinao livre de obstculos), ele no ser construdo: ento coloque um fim de estrada em ambos os lados que sero conectados, e tente novamente.
{Dica: Use o Editor de terrenos e o boto Remover para criar locais adequados para as entradas e sadas de tneis. Use os botes de construo de estradas para conectar as sadas dos tneis s estradas.}

Sinais e bloqueadores rodovirios: constrem sinais e barreiras rodovirias para os veculos que ali transitam, na viso do jogo. Esses sinais direcionam e regulam o fluxo de veculos em estradas e pontes, e em cruzamentos e Paradas (onde passageiros e mercadorias so carregados e descarregados). Verses de mo-nica e mo-dupla de alguns sinais podem ser construdos.
IMPORTANTE: Tome cuidado para no colocar sinais que evitam os veculos a chegarem aos seus destinos, por padro os veculos dirigem pelo lado direito (isso pode ser alterado no simuconf.tab).
- Semforo: essa ferramenta constri semforos que controlam o fluxo de veculos rodovirios em cruzamentos, tanto deixando-os passarem ou fazendo-os aguardarem por um tempo. Clique nessa ferramenta para selecion-la (muda o cursor para um semforo), e ento clique na estrada para colocar um semforo ali. Clique novamente nos cruzamentos para alterar a orientao dos semforos.
- Bloqueio: (disponvel s em pak128) essa ferramenta constri uma barreira para impedir veculos rodovirios de prosseguirem. Clique na ferramenta para selecionar (o cursor torna-se num bloqueio), ento clique na estrada para construir.
- Sentido nico: essa ferramenta constri um sinal que evita os veculos passarem daquele ponto, usando um lado da estrada. Clique nesse boto para selecion-lo (o cursor muda para essa placa), e ento clique em um trecho na estrada para colocar a placa ali; clique novamente no mesmo lugar para mudar o lado da estrada o qual a placa est.
- Velocidade mnima de 80km/h essa ferramenta constri uma placa que evita os veculos com velocidade mxima inferior a 80 km/h de continuar naquela estrada. Clique nesse boto para selecion-lo (o cursor muda para essa placa), e ento clique em um trecho na estrada para colocar a placa ali; clique novamente no mesmo lugar para mudar o lado da estrada o qual a placa est, alterando o lado da estrada o qual o efeito da placa ser aplicado.
- Direcionador rodovirio para vaga livre: essa ferramenta constri uma placa a qual direciona um veculo rodovirio a uma seo livre de uma Parada com vrias sees para carregar/descarregar mercadorias e passageiros: um veculo que passa por este sinal pode usar qualquer seo de uma Parada livre que esteja em seu prximo destino, e no apenas a um determinado pela sua Programao. Se no houver nenhuma vaga livre ou caminho livre ao seu prximo destino, o veculo aguardar na placa. Clique nessa ferramenta para selecion-la (o cursor muda para essa placa), e ento clique em um trecho na estrada para colocar ali a placa, e clique novamente no mesmo trecho para alterar o lado da estrada o qual a placa est, alterando o lado o qual os efeitos da placa sero aplicados.
- Vias expressas: (disponvel s em pak128) ferramentas que constroem um sinal que s permite veculos proceder numa direo em uma estrada se a velocidade mxima deles for mais de 80km/h.
Clique em um dos botes acima para selecionar um deles (o cursor muda para o respectivo sinal), e ento clique em um trecho na estrada para colocar ali o sinal, e clique novamente no mesmo local para alterar o lado da estrada o qual o sinal est, alterando tambm o lado o qual os efeitos do mesmo sero aplicados.
{Dica: Remova sinais com o boto Remover. Pressione [Ctrl] para colocar sinais em pontes sobre estradas construdas em um nvel inferior.}

Garagem: essa ferramenta constri uma garagem para a compra e gerenciar veculos rodovirios e seus reboques. As garagens possuem custos de manuteno e so construdos no fim de uma estrada.
Para construir uma garagem: clique neste boto (muda o cursor para uma garagem), e ento clique no fim de uma estrada.
{Dica: Use o boto Remover para destruir Garagens.}

Estacionamento: (disponvel s em pak 128) ferramentas que constroem extenses para Paradas, as quais aumentam capacidade e rea de captura para bens e passageiros, e o custo de manuteno.
Para construir uma extenso: clique numa ferramenta para selecionar uma extenso (cursor torna-se numa extenso), ento clique na posio desejada numa estrada, ao lado de uma Parada existente na viso do jogo. A nova extenso agora passa a ser considerada parte da Parada.
{Dicas: Remova extenses com a ferramenta Remover.}

Paradas rodovirias: essas ferramentas possibilitam a construo de locais para que os veculos rodovirios possam carregar/descarregar mercadorias e passageiros.
Quando uma seo de uma Parada rodoviria no construda ao lado de uma Parada j existente, uma nova Parada ser criada.
Paradas rodovirias so construdas sobre estradas j existentes e possuem um custo de manuteno e uma rea de captura de mercadorias, passageiros e correio. No canto de certos botes um cone (utilizado na Lista de Paradas e na Informao da Parada) mostra quais itens a Parada aceita. Diferentes Paradas podem possuir capacidades diferentes para mercadorias, passageiros e correio.
Para construir uma Parada: clique em um dos botes de construo de um tipo de Parada (muda o cursor para a Parada correspondente), e ento clique na posio desejada em uma estrada.
{Dica: Remova as Paradas com o boto Remover. Pressione [v] para mostrar/ocultar a rea de captura de mercadorias e passageiros na tela do jogo. As Paradas rodovirias no podem ser colocadas em uma estrada construda por outras companhias. Pressione [Ctrl] para construir em pontes sobre rodovias em um nvel inferior. Crie Paradas com mltiplas vagas para veculos para aumentar a capacidade e a rea de captura.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 07/02/2008.

simutrans-124.5/simutrans/text/pt/save.txt000066400000000000000000000041731520675460500207240ustar00rootroot00000000000000Ajuda - Salvar

Salvar

Salvar armazena o jogo atual a um arquivo (para continuar o jogo mais tarde) e pode ser usado para apagar esses arquivos.

Salvar abre-se de Opes de Jogo ou prosionando a tecla [S]; tambm lista nome, data e hora dos jogos precedentes.
Se todos os nomes no estiverem visveis: use a barra de rolagem no lado direito para rolar os nomes.

O jogo atual pode ser salvo em um novo arquivo ou em cima de um jogo j existente.
Para salvar em um novo arquivo, entre com o novo nome no campo de preenchimento "Arquivo" no topo de Salvar: clique no campo "Arquivo", digite um nome e pressione [Enter] ou [Backspace] ou clique em OK; o novo nome de arquivo listado.
IMPORTANTE: Se um nome de arquivo listado usado: o jogo velho substitudo, pelo jogo atual e apagado.

Por padro o jogo atual aparece no campo "Arquivo", para salvar este arquivo: clique em OK (os dados velhos sero apagados e substitudos pelos novos).
Para salvar o jogo em cima de um arquivo velho: clique em um nome da lista (o jogo velho substitudo pelo jogo atual).

Os nome de arquivos podem ser combinaes de letras, nmeros e simbolos com o comprimento mximo de 57 caracteres; depois de salvar, s 20 (aprox.) caracteres so mostrados em Salvar e Carregar.

AVISO: Apertando o boto X na frente do nome de um arquivo ele ser APAGADO imediatamente e pemanentemente, o arquivo ser retirado da lista e o Salvarser fechado. Use esta funo cuidadosamente.

{Dica: Recomece um jogo salvo a partir do Carregar.
Mude o autosave (salvamento automtico do jogo) no simuconf.tab}.

Clique em Cancelar para fechar Salvar; ou no X no canto superior esquerdo da janela; ou use o teclado.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/scenario.txt000066400000000000000000000004651520675460500215710ustar00rootroot00000000000000Ajuda - Carregar cenrio

Carregar cenrio

Voc pode jogar com cenrios prontos no Simutrans. Aqui voc tem que cumprir certas metas, tais como: conectar uma fbrica, fiquar rico o mais rpido possvel, ou erigir uma monumental sede empresarial.

simutrans-124.5/simutrans/text/pt/schedule.txt000066400000000000000000000113111520675460500215520ustar00rootroot00000000000000Programao

Programao

A programao usada para configurar a rota e a quantidade mnima de mercadorias e passageiros para um comboio ou uma linha.
Ela lista as paradas (onde veculos recolhem e entregam mercadorias e passageiros) e pontos de passagem (que podem ser usados para direcionar os comboios por um caminho especfico caso haja mais de um disponvel ou fornecer destinos temporrios) com suas coordenadas do mapa.

A janela Programar abre do boto Programar nas Informaes do comboio e nos depsitos.
Controles para a programao de uma linha abrem dos botes Nova Linha e Atualizar no Gerenciador de linhas e nos depsitos.

Quando a janela Programar aberta atravs das informaes do comboio, o nome da linha utilizada por ele (se houver) mostrado no topo da janela.
Serve linha: clique nos botes em forma de seta nos dois lados do nome da linha para escolher uma linha ou clique na caixa de nome para abrir uma lista suspensa (clique na linha para selecion-la).
Clicando na caixa de texto, possvel digitar e alterar o nome da linha.

Carga mnima (%): configura a quantidade mnima de mercadorias ou passageiros que o comboio deve esperar at prosseguir. A porcentagem mostrada se refere capacidade total do comboio e no a um tipo individual de item carregado.
O comboio esperar na parada at estar carregado com a porcentagem requerida.
Para usar: clique em uma parada na lista para selecion-la (o > antes do nome da parada mostra que mesma est selecionada), e use os botes em forma de seta para definir a porcentagem.
{Dicas: para incluir ou alterar porcentagens para todos os comboios de uma linha, use o boto Atualizar no Gerenciador de Linhas ou no depsito. Para incluir ou alterar porcentagens para somente um comboio e no para todos da linha, use o boto Programar nas Informaes do combio ou no depsito.}

Os botes de opo (clique para usar, o boto aparece pressionado quando ativo) incluem:

Adicionar: adiciona uma parada onde veculos recolhem e entregam mercadorias e passageiros (veculos aquticos podem usar qualquer quadro da rea de captura de uma doca) ou ponto de passagem na rota.
Para adicionar: clique na opo para selecion-la (o cursor mudar para um sinal escrito "HALT"), e clique no ponto desejado na rea de jogo (pode ser feito em estradas, trilhos ou gua, dependendo do tipo de veculo). A parada ou ponto de passagem agora aparece na programao.
{Dica: veculos no recolhem ou entregam mercadorias e passageiros em pontos de passagem.}

Inserir: insere uma parada ou ponto de passagem na rota, antes de uma parada ou ponto de passagem existente.
Para inserir: clique em uma parada ou ponto de passagem da lista para selecion-lo (o > antes do nome da parada mostra que mesma est selecionada), e clique no boto inserir (o cursor mudar para um sinal escrito "HALT"), e clique no ponto desejado na rea de jogo (pode ser feito em estradas, trilhos ou gua, dependendo do tipo de veculo).
A nova parada ou ponto de passagem inserido na programao antes da parada ou ponto de passagem anteriormente selecionado.
{Dica: veculos no recolhem ou entregam mercadorias e passageiros em pontos de passagem.}

Eliminar: essa opo deleta uma parada ou ponto de passagem da rota.
Clique no boto para ativ-lo, e clique em um item da lista para remover.

Espelhar linha: adiciona as paradas e pontos de passagem existentes em ordem reversa na rota. Os valores de Carga mnima (%) tambm so replicados. Clique no boto para us-lo: a nova rota aparecer na programao.

Promover a linha: (somente disponvel quando a janela Programar for aberta pelas Informaes do comboio) cria uma nova linha utilizando a atual programao. A nova linha identificada por um nmero e listada no Gerenciador de Linhas.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/shiptools.txt000066400000000000000000000142521520675460500220110ustar00rootroot00000000000000Ajuda - Navegao

Ferramentas de navegao

As Ferramentas de navegao constrem uma rede de transporte por mares e canais. As ferramentas podem construir ou remover: canais, eclusas, vrios tipos de doca e estaleiros. Se estiver jogando com linha do tempo, ento conforme o tempo passa no Simutrans mais opes de ferramentas podem aparecer.

Clique no cone de um barco no topo da rea de jogo para abrir a barra de ferramentas.
Passe o cursor do mouse em cima das opes (aps ter aberto ou clicado na barra de ferramentas) para ver o nome, o custo de construo, custo de manuteno (entre parnteses), e o limite mximo de velocidade.

As Ferramentas de navegao incluem, da esquerda para a direita:

Canal: constri hidrovias para veculos aquticos, entre dois pontos na rea de jogo.
Canais podem somente ser construdos em terra firme e podem no encontrar um caminho atravs de terrenos acidentados. Canais construdos entre dois pontos podem usar trechos de canais existentes em seu caminho.
Para construir um canal: clique na ferramenta para ativ-la (o cursor mudar para um canal), e clique na rea de jogo no ponto inicial do canal (mostra um trator na rea de jogo e as coordenadas do mapa no lado direito da barra inferior do jogo), e finalmente clique na rea de jogo no ponto final.
{Dicas: canais construdos por jogadores diferentes podem no se conectar. Use o Editor de terrenos para modificar o terreno, possibilitando caminhos para os canais. Use a ferrramenta Remover para remover partes individuais do canal e obstculos. O desfazer [z] no reembolsa os custos da construo.}

Remover canais: remove partes do canal, quando no houver veculos presentes, entre dois pontos na rea de jogo (Obs.: cais de canal no caminho so removidos tambm). O uso da ferramenta tem um custo de construo.
Para remover canais: clique na ferramenta para ativ-la (o cursor mudar para um X vermelho), e clique no canal que ser removido (o ponto selecionado mostrado por um X vermelho na rea de jogo), e finalmente clique no segundo ponto em um canal conectado, para remover o trecho de canais desde o ponto inicial selecionado.

Canal elevado: constri eclusas e uma ponte de canal para elevar/rebaixar o canal em um nvel na rea de jogo. A ponte possui um comprimento mximo e construda no fim de um canal at um ponto disponvel (outro final de canal ou um trecho de terra mais alto, dentro do limite).
Para construir um canal elevado: clique na ferramenta para ativ-la (o cursor mudar para um canal elevado), e clique na rea de jogo no fim de um canal (ele ser o ponto inicial da ponte). Obstculos ou a falta de um lugar propcio para o fim da ponte podem interromper a construo: construa canais dos dois lados que devem ser ligados pela ponte e tente novamente.
{Dicas: use a ferramenta Remover para remover pontes (clique no comeo dela) ou obstculos para a construo. Use canais para conectar pontes com hidrovias.}.

Cais de canal: constri uma doca em um canal na rea de jogo para navios e barcaas recolherem e entregarem mercadorias, passageiros e correio.
Quando uma doca no for construda ao lado de uma parada existente, ser criada uma nova.
Uma doca possui uma rea de captura para mercadorias, passageiros e correio. No canto superior esquerdo um cone (usado na Lista de paradas e nas Informaes das estaes) mostra que tipos de itens uma parada suporta.
Para construir uma doca: clique na ferramenta para ativ-la (o cursor mudar para um cais de canal); e clique em um ponto do canal na rea de jogo.
{Dicas: remova as docas com a ferramenta Remover. Pressione [v] para mostrar/ocultar a rea de captura para mercadorias, passageiros e correio na rea de jogo.
Paradas somente de veculos aquticos no sero listadas na Lista de paradas/estaes}.

Paradas marinhas: os vrios itens a seguir constrem docas na rea de jogo para veculos aquticos.
Docas so construdas em uma poro reta da costa.
Quando uma doca no for construda ao lado de uma parada existente, ser criada uma nova.
Uma doca possui uma rea de captura para mercadorias, passageiros e correio. No canto superior esquerdo um cone (usado na Lista de paradas e nas Informaes das Estaes) mostra que tipos de itens uma parada suporta. Diferentes docas podem ter diferentes capacidades para mercadorias, passageiros e correio.
Para construir uma doca: clique na ferramenta para ativ-la (o cursor mudar para uma doca) e ento clique em um quadro adjacente ao mar.
{Dicas: remova docas com a ferramenta Remover.
O tamanho das docas pode ser incrementado construindo nos quadros adjacentes. Veculos de transporte aqutico podem usar qualquer quadro de gua na rea de captura da doca.
Paradas somente de veculos aquticos no sero listadas na Lista de paradas/estaes}. Indstrias em alto-mar como plataformas de petrleo no precisam de docas. Veculos de transporte aqutico podem usar qualquer quadro da rea de captura da indstria.
Pressione [v] para mostrar/ocultar a rea de captura para mercadorias, passageiros e correio na rea de jogo.

Construir estaleiro: constri um depsito para a compra e gerenciamento de navios, barcos e barcaas. Depsitos possuem um custo de manuteno e so construdos dentro d'gua na rea de jogo.
Para construir um estaleiro: clique na ferramenta para ativ-la (o cursor mudar para um estaleiro), e clique na gua na rea de jogo.
{Dicas: remova os depsitos com a ferramenta Remover.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/simutrans.txt000066400000000000000000000050211520675460500220040ustar00rootroot00000000000000Simutrans

Bem-vindo ao Simutrans...

Escolha um, entre alguns milhares, dos mundos do Simutrans. Ento, ponha-se no lugar de um jovem empreendedor com um pouco de dinheiro dado por seus avs: ansioso para deix-los orgulhosos, com a ambio de estabelecer uma companhia de transporte de sucesso, seu desafio a partir de agora construir uma empresa vitoriosa.

Com o passar do tempo, transforme a sua pequena companhia em um grande imprio de transporte, mantendo as finanas equilibradas entregando cargas e satisfazendo o desejo de viajar de milhes de passageiros todos os meses.
Transporte passageiros, correio e mercadorias por estradas, ferrovias, navios e at pelo ar.

Cada mundo possui algo diferente a oferecer. D uma boa olhada ao seu redor... o futuro do lugar est em suas mos e nas de seus concorrentes.

No comeo, pode ser mais fcil de se comear suprindo uma usina de gerao de energia com aquilo que ela precisa. A seguir, conecte outras indstrias, talvez usando estaes de distribuio onde as mercadorias so recarregadas.
A desvantagem de somente transportar mercadorias que as cidades no iro crescer e novas indstrias no aparecero.

Ou ento, voc pode comear imediatamente a transportar passageiros (e correio). Este mais desafiador, j que os habitantes deste mundo querem viajar para os locais de suas preferncias.
Alguns precisam ir trabalhar nas indstrias, outros querem fazer compras no Shopping Center da cidade grande, enquanto outros decidem visitar pontos tursticos.

Se voc no oferecer uma conexo at seus destinos, os passageiros no viajaro com voc para sempre. Voc precisar construir redes. Quando suas redes estiverem prontas, ser necessrio aumentar suas capacidades para absorver a demanda. Ento, continue com o planejamento.
Se eles gostarem de seus servios, as cidades crescero, atraindo novas indstrias, e assim a economia ser prspera.

Voc pode jogar com o tempo que voc quiser. Comece em 1880 e termine em 2050. Haver novos veculos e edifcios durante todo esse tempo.
Jogue contra a IA ou do seu prprio jeito. Somente se ficar devendo por trs meses, ir falir e ser o fim de seu jogo.

O Simutrans-Team e toda a comunidade te desejam muitas horas de diverso jogando Simutrans!

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008. Veja a lista dos tradutores na Ajuda ou teclando F1.

simutrans-124.5/simutrans/text/pt/slopetools.txt000066400000000000000000000040751520675460500221720ustar00rootroot00000000000000Ajuda - Editor de terrenos

Editor de terrenos

As ferramentas do Editor de terrenos elevam e rebaixam o terreno, e podem ser usadas para obter locais viveis para construo.

Clique no cone de terreno que est no topo da rea de jogo para abrir a barra de ferramentas.
Depois de abrir ou clicar sobre a barra de ferramentas, passe o cursor do mouse por cima das opes para ver o nome e o custo de construo.

A ferramenta funciona em reas livres de obstculos (exemplos: estradas, trilhos, edificaes, paradas, estaes etc).

Clique em um dos botes para ativar (o cursor muda-se de aparncia), ento posicione o cursor sobre o terreno na tela do jogo e clique para usar:

Elevar terreno/Abaixar terreno: eleva/abaixa o terreno.
O cursor mudar para uma seta para baixo ou para cima.
A ferramenta tambm ativada pelas teclas [u] e [d].

Barrancos: cria um barranco na direo indicada pelo boto, podendo criar juntamente muros de arrimo verticais nas laterais
O cursor mudar para um cubo amarelo vazio que posiciona o barranco.

Abaixar quadro/Elevar quadro: abaixa/eleva um quadro especfico do terreno, podendo juntamente criar muros de arrimo verticais em suas laterais.
O cursor mudar para um cubo amarelo vazio que modifica o quadro.

Restaurar relevo natural: retorna a forma natural do terreno, removendo juntamente os muros de arrimo.
O cursor mudar para um cubo amarelo vazio que restaura o terreno no quadro.

{Dica: O Editor de terreno fica mais fcil de se usar com a grade. Pressione [#] para exibir/ocultar a grade sobre o terreno.
Use a ferramenta Remover para remover obstculos.
Dependendo do local do mapa, as ferramentas podem elevar ou rebaixar terreno dentro da gua.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/sound.txt000066400000000000000000000016541520675460500211170ustar00rootroot00000000000000Ajuda - Ajustes de Som

Ajustes de Som

Ajustes de Som tem controles para efeitos de som e msica.

Ajustes de Som abrem de Opes de Jogo.

Use as setas ou a barra de rolagem para aumentar ou diminuir o volume:

Volume dos efeitos: ajusta o volume de efeitos sonoros do jogo.

Volume da msica: ajusta o volume da msica. Se nenhuma msica estiver disponvel ento os controles no funcionaro.

Tocando: muda a msica; as setas alternam a execuo das msicas disponiveis.

Nota: As msicas no so fornecidas com o Simutrans.
Ajuda para adicionar msicas est disponivel on line em http://wiki.simutrans.com

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/special.txt000066400000000000000000000145141520675460500214060ustar00rootroot00000000000000Ajuda - Ferramentas Especiais

Ferramentas Especiais

As Ferramentas Especiais constrem itens no-relacionados com trfego e tambm permitem a voc atuar como um de seus concorrentes dentro do jogo.

Clique no cone representado por uma gota vermelha em um crculo amarelo (ou no de um guindaste no pak128) no topo da rea de jogo para abrir a barra de ferramentas.
Passe o cursor do mouse em cima das opes (aps ter aberto ou clicado na barra de ferramentas) para ver o nome e o custo de construo.

As Ferramentas Especiais incluem, da esquerda para a direita:

Agncia dos Correios: esta ferramenta constri uma agncia de correios que permite a uma estao ou parada receber correspondncia. Agncias de correios aumentam a capacidade e a rea de coberura da estao e possuem um custo de manuteno.
Para construir uma agncia: clique na ferramenta para ativ-la, e na rea do jogo clique ao lado de alguma estao existente. A nova agncia agora far parte da estao.
{Dica: remova as agncias e os obstculos com a ferramenta Remover.}

Extenses para estaes: ferramentas que constrem prdios ou extenses para as estaes. Extenses podem aumentar a capacidade e a rea de coberura para cargas e passageiros, e tambm o custo de manuteno. No canto de algumas das opes um cone (usado em Lista de Paradas e em Informaes das Paradas) mostra quais itens a extenso permite que a parada receba.
Para construir uma extenso: clique na ferramenta para ativ-la (o cursor mudar para o desenho da extenso selecionada), e clique no local desejado, ao lado de uma estao existente, na rea de jogo. A nova extenso agora far parte da estao.
{Dica: remova as extenses com a ferramenta Remover.}

Mudar jogador (P+): permite atuar como outro jogador se a opo Permitir mudana de jogador tiver sido selecionada no incio do jogo. Opes adicionais esto disponveis se jogando como o Poder pblico.
Clique no boto (ou pressione [P]) para alternar entre os jogadores. Uma mensagem de confirmao aparece quando se muda o jogador: clique no [X] do canto superior esquerdo da mensagem ou use o teclado para fech-la.

Criar nova cidade: constri uma nova rea urbana.
Para construir uma nova cidade: clique na ferramenta ou pressione [C] para ativ-la (o cursor mudar para uma pequena prefeitura), e clique no local desejado, na rea do jogo. A nova cidade custa 5.000.000,00 de crditos e ser construda sem pedir confirmao ao jogador.
{Dica: clique na prefeitura da cidade na rea de jogo com a ferramenta Remover para demolir as construes da nova cidade.}

Plantar rvore: planta uma rvore na rea de jogo.
Para plantar uma rvore: clique na ferramenta para ativ-la (o cursor mudar para uma rvore), e clique no lugar desejado.
{Dica: remova as rvores com a ferramenta Remover.}

Construir linha eltrica: constri linhas eltricas que transportam eletricidade dos transformadores das usinas de gerao de energia at as indstrias (para aumentar suas produtividades). O fornecimento de energia eltrica gera lucro. As linhas eltricas no podem cruzar trilhos e estradas na diagonal.
Para construir uma linha eltrica: clique na ferramenta ou pressione [l] para ativ-la (o cursor mudar para uma linha eltrica), e clique em um transformador situado em uma usina, e finalmente clique em um transformador situado em uma indstria.
{Dicas: linhas eltricas podem ser construdas em partes, clique nos pontos desejados na rea de jogo. Remova as linhas eltricas com a ferramenta Remover.}

Construir transformador: constri um transformador nas usinas geradoras de energia ou nas indstrias para conexo com as linhas eltricas.
Para construir um transformador: clique na ferramenta ou pressione [g] para ativ-la (o cursor mudar para uma gota vermelha em um crculo amarelo), e clique na posio desejada ao lado de uma indstria ou de uma usina de energia.
Transformadores construdos nas usinas de energia devem ser conectados com transformadores das indstrias por meio das linhas eltricas.
Se no houver eletricidade sendo fornecida, o transformador mostrar um raio de cor amarela. Quando houver fornecimento, mostrar um raio na cor vermelha.
{Dicas: indstrias e usinas de energia podem ter mltiplos transformadores para aumentar o fornecimento de eletricidade. Remova os transformadores com a ferramenta Remover.}

Marcador: abre controles para a colocao de um sinal com texto personalizado na rea de jogo e pode ser usado para ir de um lugar a outro na rea de jogo.
Clique na ferramenta para ativ-la ou pressione [M] (o cursor mudar para um marcador), e clique no lugar desejado para abrir a janela de controles do marcador. No topo da janela esto as coordenadas no mapa da posio selecionada e uma caixa de texto vazia, digite o texto desejado nela (clique na caixa, digite o texto e clique em OK ou pressione [Enter] ou [Return]) para colocar o marcador. O novo marcador mostrado na rea de jogo e mostrado na janela de controles do marcador (com as coordenadas do mapa) quando reaberta.
Para se deslocar at um marcador: abra a janela de controles do marcador e clique na posio desejada na lista.
Para remover um marcador: clique na ferramenta marcador ou pressione [M] para ativ-la (o cursor mudar para um marcador), e clique nas coordenadas do marcador na rea de jogo; logo aps, clique em Remover na janela de controles do marcador.
Para fechar os controles do marcador: clique no X do canto superior esquerdo da janela; ou clique em Cancelar; ou use o teclado.
{Dicas: as coordenadas da posio do cursor podem ser vistas no lado direito da barra inferior do jogo. Use [!] para mostrar/esconder o texto dos marcadores na rea de jogo.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/station.txt000066400000000000000000000173661520675460500214570ustar00rootroot00000000000000Informaes das Paradas

Informaes das Paradas

As Paradas so os locais onde os veculos apanham e deixam passageiros ou cargas. A janela que se abre ao clicar em uma parada d acesso a diversos detalhes da parada alm de permitir mudar o seu nome.

Alm de ser o local onde os veculos param, uma parada pode ser composta de extenses, que adicionam capacidade de armazenamento ou permitem que a parada aceite outros tipos de carga alm dos originais.

As ferramentas para a construo de paradas esto disponveis nas respectivas barras de cada tipo de transporte:
- plataformas de trem/comboio;
- plataformas de monotrilho;
- paradas de bonde/elctrico;
- paradas de nibus/autocarro ou caminho/camio;
- docas;
- e aeroportos.
(Dicas: Veculos aquticos podem usar qualquer quadro de gua na rea de captura de uma doca para pegar e deixar cargas e passageiros. Parada maiores podem ser construdas simplesmente adicionando mais quadros de parada. Uma parada pode aceitar mais de um tipo de veculo adicionando-se os quadros adequados nas vias prprias; ou seja, uma estao de trem pode ser colocada ao lado de uma parada de nibus, desde que haja um trilho passando ao lado da rua onde est a parada.)

Clique numa parada na tela do jogo com a ferramenta Consultar ou numa parada listada naLista de paradas ou Gerenciador de Linhas para abrir as informaes das paradas que mostra o nome da parada em sua barra de ttulo.
A janela das paradas contm uma caixa de nome, uma vista da parada, informaes sobre a parada, botes de opo e uma lista de todos os bens e passageiros prontos para serem transportados. Se no for possvel ver todos os bens, ou todas as informaes sobre eles, redimensione a janela da parada ou use as barras de rolagem para mostrar os itens:

Nome: O nome da parada mostrado na caixa que fica em cima na janela de informaes das Paradas.
Por padro, um nome atribudo automaticamente parada quando ela construda e depende da rea urbana onde inserida ou das indstrias prximas, dentro da rea de captura da parada.
Para alterar o nome da parada, basta clicar na caixa do nome e escrever o novo nome da parada.
(dicas: Use [!] para alterar a visualizao dos nomes das paradas sobre elas. Use [v] para alterar a viso da rea de captura da parada. Opo para numerar as paradas est disponvel no arquivo simuconf.tab [numbered_stops=])

Barra de estado Suas cores indicam a qualidade da operao da parada. Elas tambm so usadas na Lista das Paradas e no Gerenciador de Linhas, e so as mesmas exibidas acima da parada na tela do jogo:
- amarelo: parada sem atendimento; nenhum veculo a utiliza.
- verde: no necessrio melhorar o atendimento; a parada no lota.
- laranja: alguma melhoria necessria.
- vermelho: necessrio melhorar o atendimento urgentemente.
(Dicas: Use [!] para alternar a visualizao das barras de estado sobre as paradas na tela do jogo.)

Tambm h cones que representam:
- carga(s) indicam quais itens so aceitos na parada: (passageiros, carga ou correio).
- veculo(s) indicam quais tipos de veculo podem usar a parada. Eles incluem: nibus/autocarro, caminho/camio, trem/comboio, barco/navio e avio. Bondes/elctricos so ora representados pelo smbolo de nibus/autocarro, ora pelo de trem/comboio, conforme o tipo original da parada.

Capacidade de armazenamento: o quanto de cada carga a parada pode acomodar.

Imagem da parada: uma viso da parada e quaisquer veculos visveis.
Clique na imagem para centralizar a viso do jogo sobre a parada.

Passageiros os nmeros indicam a quantidade de passageiros que querem iniciar sua viagem pela parada em foco:
- rosto feliz indica a quantidade de passageiros que chegaram parada e encontraram-na sem estar lotada e encontram uma rota para o seu destino.
- rosto triste ou bravo indica a quantidade de passageiros que chegaram parada, mas encontraram-na lotada, o que os fez desistir de viajar.
- sem caminho indica a quantidade de passageiros que chegaram parada, mas no encontraram uma rota para chegar ao seu destino.
(Dicas: Manter os passageiros felizes ajuda a aumentar a populao das cidades. possvel alterar o nmero mximo de transferncias no arquivo simuconf.tab}.

Passageiros/mercadoria esperando: lista os itens que esperam por transporte na parada em grupos de: passageiros; correio; e tipo de mercadorias.
A informao mostrada de cada item inclui quantidade, tipo, destino final e primeira parada de transferncia.
O boto de opes (que muda seu nome conforme se clica nele) organiza os itens das seguintes maneiras:
- destino: organiza os itens alfanumericamente, de acordo com a codificao ASCII (letra maisculas antes das minsculas e estas, antes das acentuadas), pelo nome da parada do destino final.
- via (detalhe): organiza os itens alfanumericamente, de acordo com a codificao ASCII (letra maisculas antes das minsculas e estas, antes das acentuadas), pelo nome da primeira parada de transferncia.
- via (quantidade): organiza os itens pela quantidade que vai ser transferida numa parada imediatamente posterior.
- quantidade: organiza por ordem decrescente da quantidade do item em espera.
(Dicas: as barras coloridas sobre uma parada indicam a quantidade de mercadoria ou de passageiros esperando para ser transportada e so as mesmas que se v na Lista das cargas.)

Grfico: Clique no boto para acionar os grficos da janela de Informaes da Parada (quando a rea do grfico est visvel, o boto fica afundado).
O grfico mostra estatsticas para os ltimos 12 meses (eixo horizontal) para o(s) dado(s) selecionado(s).
As cores das linha do grfico correspondem s dos botes sobre ele:
- Contentes indica a quantidade de passageiros que chegaram parada e encontraram-na sem estar lotada e encontram uma rota para o seu destino.
- Descontentes indica a quantidade de passageiros que chegaram parada, mas encontraram-na lotada, o que os fez desistir de viajar.
- Sem rota indica a quantidade de passageiros que chegaram parada, mas no encontraram uma rota para chegar ao seu destino.
- Espera: quantidade de passageiros e mercadorias espera de transporte.
- Chegados: quantidade de passageiros e mercadorias que chegaram parada atravs de veculos, no pela rea de captura.
- Sados: quantidade de passageiros e mercadorias que saram da parada atravs de veculos.
- Veculos: quantidade de veculos que partiram da parada.

Detalhes: boto que abre a janela de Detalhes da Parada que fornece outras caractersticas da parada, no disponveis na janela principal.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008, atualizado em 07/02/2008.

simutrans-124.5/simutrans/text/pt/station_details.txt000066400000000000000000000033441520675460500231530ustar00rootroot00000000000000Detalhes da Parada

Detalhes da Parada

A janela Detalhes da Parada providencia mais informaes sobre a estao/parada (onde veculos recolhem e entregam mercadorias e passageiros).

A janela Detalhes da Parada abre do boto Detalhes na janela de Informaes das Paradas.
Se todos os itens no estiverem visveis redimensione a janela Detalhes da Parada ou use as barras de rolagem para percorrer os itens.

As informaes listadas incluem:
Indstrias conectadas: nomes e coordenadas no mapa (em parnteses) das indstrias na rea de captura da parada.
{Dica: use [v] para mostrar/exibir a rea de captura de uma parada.}

Indstrias prximas precisam: lista as mercadorias requeridas pelas indstrias na rea de captura da parada.

Linhas que servem esta parada: mostra as linhas que usam a parada.
Linhas so listadas pela ordem do cdigo ASCII (letras maisculas antes de minsculas).

Paradas conectadas diretamente: lista o nome das paradas que podem ser alcanadas e o tipo de itens (mercadorias e passageiros) que podem ser entregues.
Paradas so listadas pela ordem do cdigo ASCII (letras maisculas antes de minsculas) pelo tipo de transporte, nome da parada, e tipo de item que pode ser entregue (que tambm mostrado por cada parada).

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/trafficlight_info.txt000066400000000000000000000004221520675460500234400ustar00rootroot00000000000000Semforo

Semforo

Voc pode controlar a freqncia de fases vermelhas e verdes dos semforos. Os nmeros que voc pode escolher aqui so a durao da fase verde em segundos para direes norte/sul e leste/oeste.

simutrans-124.5/simutrans/text/pt/tramtools.txt000066400000000000000000000220111520675460500220010ustar00rootroot00000000000000Ajuda - Bondes

Bondes

As ferramentas para Bondes constrem uma rede de transporte por bondes. Elas podem construir trilhos para bondes (e eletrific-los ou remov-los), sinais, depsitos e paradas. Se voc estiver jogando com linha do tempo, ento conforme o tempo passa no Simutrans mais opes de ferramentas podem aparecer.

Clique no cone de um bonde no topo da rea de jogo para abrir a barra de ferramentas.
Passe o cursor do mouse em cima das opes (aps ter aberto ou clicado na barra de ferramentas) para ver o nome, o custo de construo e o limite mximo de velocidade.

As ferramentas para Bondes incluem, da esquerda para a direita:

Trilho de bonde: constri trilhos de bondes, para veculos de linha frrea entre dois pontos.
Trilhos de bonde podem ser construdos por cima das estradas e em reas urbanas, mas no em reas ocupadas por outros edifcios. Trilhos podem ser construdos em declives somente na direo dos mesmos e no podem ser construdos em terreno acidentado, sobre a gua ou obstculos. O novo trilho construdo pode usar outros trilhos existentes em seu caminho.
Para construir um trilho: clique na ferramenta para ativ-la (o cursor mudar para um trilho), e clique na rea de jogo no ponto inicial do trilho (mostra um trator na rea de jogo e as coordenadas do mapa no lado direito da barra inferior do jogo), e finalmente clique na rea de jogo no ponto final para o trilho.
{Dicas: tipos diferentes de trilhos podem se conectar (porm no quando construdos por jogadores diferentes). Use pontes e tneis para conectar trilhos atravs de terreno acidentado ou para desviar de obstculos. Use a ferramenta Remover para remover pedaos individuais dos trilhos e algumas obstrues. Use [Ctrl] ao mesmo tempo para funes extra. O desfazer [z] no reembolsa os custos da construo.}

Rede area de fora: ferramentas para eletrificar trilhos, pontes ou tneis entre dois pontos na rea de jogo, para o uso de veculos eltricos.
Para eletrificar os trilhos: clique na ferramenta ou pressione [e] (o cursor mudar para um trilho eletrificado) para ativ-la, e clique no trilho no ponto de incio da eletrificao (um cone de rede area se fixa marcando o ponto), e finalmente clique em um segundo ponto do trilho, para eletrificar o trecho desde o ponto inicial selecionado.
{Dica: use a ferramenta Remover para voltar a usar trilhos no-eletrificados.}

Remover trecho de trilhos: remove trilhos (eletrificados ou no), quando no houver veculos presentes, entre dois pontos na rea de jogo (plataformas de estaes, paradas em estradas, sinais, tneis e pontes do caminho so removidos tambm). O uso da ferramenta tem um custo de construo.
Para remover um trecho de trilhos: clique na ferramenta para ativ-la (o cursor mudar para um X vermelho), e clique no trilho que ser removido (o ponto selecionado marcado por um X vermelho na rea de jogo), e finalmente clique no segundo ponto em um trilho conectado, para remover o trecho de trilhos desde o ponto inicial selecionado.
{Dica: jogue como outro jogador para remover seus trilhos.}

Sinais: coloca sinais para os veculos em um trilho na rea de jogo. Sinais regulam e direcionam o fluxo de veculos em trilhos, pontes, cruzamentos e estaes (onde veculos recolhem e entregam mercadorias e passageiros).
Podem ser construdos sinais para vias de mo dupla ou mo nica. Para construir um sinal de mo dupla nos trilhos: clique na ferramenta para ativ-la (o cursor mudar para um sinal), e clique no trilho. Para construir um sinal de mo nica: clique novamente no mesmo ponto, com o cursor em forma de sinal, para alternar entre os sinais de cada lado da via, e de volta para um sinal de mo dupla.
IMPORTANTE: tome cuidado para no colocar sinais de mo nica que impeam os veculos de chegar a seu destino, por padro os veculos andam direita (pode ser mudado no arquivo simuconf.tab).
- Sinais: veculos prosseguem somente se o trecho de trilho at o prximo sinal ou destino na Programao (uma estao ou ponto de passagem) no estiver ocupado por outro veculo. Em sinais de mo nica veculos passam em apenas uma direo.
- Sentido nico: veculos no prosseguem alm do sinal (disponvel somente em sinais de mo nica).
- PreSignals: veculos prosseguem se o trecho de trilho frente (qualquer entre trs sinais consecutivos ou para o prximo destino na Programao) no estiver ocupado por outro veculo. Em sinais de mo nica eles permitem que veculos trafeguem em somente uma direo.
- Direcionador ferrovirio para vaga livre: direciona veculos para uma plataforma livre da estao, em uma estao com mltiplas plataformas: um veculo passando este sinal pode usar qualquer plataforma livre at seu prximo destino, e no somente aquela descrita em sua Programao. Se no houver plataforma livre ou caminho livre at o prximo destino, o veculo esperar no sinal.
{Dicas: remova os sinais com a ferramenta Remover. Mantenha pressionada a tecla [Ctrl] para colocar sinais em pontes acima de outros trilhos.}

Construir depsito de bonde: constri um depsito para a compra e o gerenciamento de bondes.
Depsitos possuem um custo de manuteno e so construdos em fins de trilhos na rea de jogo.
Para construir um depsito de bonde: clique na ferramenta para ativ-la (o cursor mudar para um depsito), e clique no fim de um trilho.
{Dicas: depsitos somente mostraro veculos eltricos se estiverem em um trilho eletrificado. Remova os depsitos com a ferramenta Remover.}

Paradas de bonde em ferrovias: constrem plataformas de estao, usadas por veculos ferrovirios para recolher e entregar mercadorias, passageiros e correio.
Uma plataforma de estao quando no for construda adjacente a uma parada existente, criar uma nova estao.
Paradas em ferrovias possuem um custo de manuteno e so construdos em trilhos (menos em curvas e cruzamentos de ferrovias).
Elas possuem uma rea de captura para mercadorias, passageiros e correio. Plataformas diferentes podem ter diferentes capacidades para passageiros e correio. No canto superior de algumas opes um cone (usado na Lista de Paradas e Informaes das paradas) mostra quais itens a plataforma permite estao receber.
Para construir uma plataforma: clique na ferramenta para ativ-la (o cursor mudar para uma plataforma), e clique no trilho.
{Dicas: remova as paradas com a ferramenta Remover. Estenda as plataformas de estao e construa estaes maiores (construindo mais sees de plataformas nos trilhos adjacentes) para acomodar veculos longos, bem como maior quantidade deles, aumentar a capacidade e a rea de captura da estao. Pressione [v] para mostrar/esconder a rea de captura da estao. Mantenha pressionada a tecla [Ctrl] para construir em pontes acima de outros trilhos.}

Estacionamento: (disponvel somente no pak128) constri extenses para estaes que aumentam a capacidade e a rea de captura para mercadorias e passageiros, e o custo de manuteno.
Para construir uma extenso: clique na ferramenta para ativ-la (o cursor mudar para uma extenso), e clique na posio desejada em uma estrada, ao lado de uma estao existente. A nova extenso agora far parte da estao.
{Dica: remova extenses com a ferramenta Remover.}

Paradas de bonde em estradas: constrem pontos onde bondes e veculos de estrada possam recolher e entregar mercadoria, correio e passageiros.
Uma parada em estrada, quando no for construda adjacente a uma parada existente, criar uma nova parada. Elas so construdas em estradas e possuem um custo de manuteno e uma rea de captura para mercadorias, passageiros e correio. No canto superior de algumas opes um cone (usado na Lista de paradas e Informaes das paradas) mostra quais itens a plataforma permite estao receber. Diferentes paradas podem ter diferentes capacidades para mercadorias, passageiros e correio.
Para construir uma parada de bonde em estrada: clique na ferramenta para ativ-la (o cursor mudar para uma estao), e clique no local desejado da estrada na rea de jogo.
{Dicas: remova as paradas com a ferramenta Remover. Mantenha pressionada a tecla [Ctrl] para construir em pontes acima de outras estradas.}

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 17/01/2008.

simutrans-124.5/simutrans/text/pt/window.txt000066400000000000000000000125621520675460500212760ustar00rootroot00000000000000Ajuda - Interface/Janela do Jogo

Interface/Janela do Jogo

O Simutrans jogado atravs da Interface/Janela do Jogo, que fornece informaes sobre o jogo atual; acesso s barras de ferramentas e controles do jogo, e uma viso do jogo atual.

Barra de ttulo: o lado esquerdo da barra de ttulo da Interface/Janela do Jogo mostra a verso e a data de lanamento do Simutrans que est sendo jogado no momento.
O lado direito da barra de ttulo oferece controles para minimizar, maximizar a Interface/Janela do Jogo e tambm para sair do Simutrans.

cones: uma fileira de cones, utilizada para acessar as barras de ferramentas e os controles do jogo enquanto ele est sendo jogado, est diretamente abaixo da barra de ttulo. Os cones (clique para usar) esto agrupados em trs conjuntos:

- Os cones do Menu Principal incluem:
- Disquete: abre as Preferncias
- Mapa: abre o Mapa
- Lupa seleciona a Ferramenta Consultar
- Barranco: abre o Editor de terrenos
- Locomotiva: abre a barra de ferramentas Ferrovias
- Monotrilho/Maglev: barra de ferramentas Monotrilhos e Maglevs
- Bonde: abre a barra de ferramentas Bondes
- Caminho: abre a barra de ferramentas Estradas
- Barco: abre a barra de ferramentas Navegao
- Avio: abre a barra de ferramentas Aeroportos
- Crculo amarelo com uma bola vermelha dentro (um guindaste no pak128): abre a barra de Ferramentas Especiais
- X vermelho (um trator no pak128): seleciona a ferramenta Remover

- Os cones do Menu de Gerenciamento incluem:
- Rede de transportes: abre o Gerenciador de Linhas
- Lista: abre as Listas
- Caixa de correio: abre as Mensagens
- Cdula de dinheiro: abre as Finanas

- Os cones das Outras Opes incluem:
- Cmera fotogrfica: captura uma imagem do que est acontecendo no momento na Interface/Janela do Jogo (sem a barra de ttulo) e salva a imagem como um arquivo bitmap (.bmp) na pasta ...simutrans/screenshot/
{Dica: as imagens tambm podem ser capturadas pressionando a tecla [c].}
- Duas barras verticais amarelas: pausa/volta ao jogo .
{Dica: Pressionando-se [p] tambm pausa/volta ao jogo}.
- cone >>: ativa/desativa a funo Avano Rpido (muda a velocidade com a qual o Tempo, T, passa no jogo).
{Dica: A funo Avano Rpido tambm pode ser ativada/desativada pressionando-se [W]
IMPORTANTE: O jogo pode no rodar perfeitamente; a janela Vdeo, acessvel a partir de Preferncias, indica se o computador aceita essa funo sem problemas.}
- ?: abre a Ajuda do jogo.

A viso do jogo de uma poro do mundo (jogo) atual mostrada abaixo dos cones.
Voc pode aumentar ou diminuir o zoom na viso do jogo e tambm visualizar as outras partes do mundo atual usando o mouse ou o teclado.

Uma barra deslizante temporria aparece e mostra mensagens na parte inferior da viso do jogo.
{Dica: Clique em uma mensagem para centralizar a viso do jogo para a posio informada na mensagem.}

Uma barra inferior a qual mostra informaes sobre o jogo atual mostrada na viso do jogo.
A informao fornecida, da esquerda para a direita na barra inferior inclui:
- Data e hora: a data e hora atuais do jogo carregado, seguido por um cone e o nome da estao.
- Caixa atual: o dinheiro atualmente disponvel para construo e outros custos (compra de veculos, manuteno da infra-estrutura para transportes e custos operacionais para os veculos que esto em funcionamento).
- Coordenadas: indica a posio do cursor do mouse na viso do jogo. Os nmeros representam as coordenadas X, Y, e a altitude.
- T: indica a velocidade com a qual o tempo passa no jogo.
{Dica: Pressione [.] / [,] para acelerar/desacelerar o Tempo, (T); o menu Vdeo indica se o computador suporta esta mudana.
O uso da funo Avano substitui T por ">>" na barra inferior.}
- Linha do tempo: indica se a Linha do tempo foi ativada para o jogo atual.

Traduzido pela Equipe de traduo do Simutrans PT-SubFrum (http://pt.simutrans.com), 16/02/2008.

simutrans-124.5/simutrans/text/readme_citylist.txt000066400000000000000000000022501520675460500225160ustar00rootroot00000000000000 Simutrans city name generation help ----------------------------------- Simutrans can create city names two ways: 1) If there is a citylist_xx.txt file for the chosen language city names are read from this file. 2) Otherwise random city names are created from the syllables given in xx.tab Currently there is only one citylist per language used. I.e. for english, citylist_en.txt is used. A citylist in pak/text/ will overlay a citylist in text/. So different scenarios can have different citylists. But there are some additional citylist files supplied, i.e. citylist_en_au.txt for australian city names and citylist_de_at.tx for austrian city names. You can copy the citylist either in the folder text/ or to your pak-files e.g. pak/text/. To use them, you need to rename the files. I.e. to use the austrian city names, rename citylist_de.txt to citylist_de_de.txt and rename citylist_de_at.txt to citylist_de.tx If you don't want Simutrans to use the city name lists, delete them. Simutrans will then use the old (pre 0.82.15.5exp) scheme of random city names. written by Hansjrg Malthaner, 07-Dec-03 change by Markus Pristovsek, 14-Jan-04 simutrans-124.5/simutrans/text/ro.tab000066400000000000000000000630641520675460500177160ustar00rootroot00000000000000§Romana PROP_FONT_FILE prop-latin2.fnt ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: ro Romana # # Encoding: UTF-8 # # Font: prop-latin2.fnt # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable dezactivat cl_btn_filter_enable activat cl_btn_filter_settings Setari cl_btn_sort_asc crescator cl_btn_sort_desc descrescator cl_btn_sort_id ID intern cl_btn_sort_income Venit cl_btn_sort_name Nume cl_btn_sort_type Tip clf_btn_alle tot clf_btn_invers inv. clf_btn_keine nimic gl_btn_sort_bonus Bonus gl_btn_sort_name Nume gl_btn_sort_revenue Venit gl_btn_unsort Nesortat hl_btn_filter_disable dezactivat hl_btn_filter_enable activat hl_btn_filter_settings Setari hl_btn_sort_asc crescator hl_btn_sort_desc descrescator hl_btn_sort_name Nume hl_btn_sort_type Tip hl_btn_sort_waiting Asteapta hlf_btn_alle tot hlf_btn_invers inv. hlf_btn_keine nimic #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic clima arctica desert clima desertica mediterran clima mediteraneana rocky clima alpina temperate clima temperata tropic clima tropicala #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Cannot built depot here! Nu pot construi depou aici. Cannot built this station/building\nin underground mode here. Cladirea nu poate fi\nconstruita sub pamant.\n Cannot create generic line!\nSelect line type by\nusing filter tabs. Nu pot creea linie generica!\nAlege tipul de linie\nfolosind filtre. Cannot create socket Nu pot creea socket Convoi handles exhausted! Numar maxim de convoaie atins. Das Feld gehoert\neinem anderen Spieler\n Aceasta parte a insulei\neste detinuta de\nun alt jucator!\n Der Besitzer erlaubt das Entfernen nicht \nNu ai permsiunea!\n Diese Zusammenstellung kann nicht fahren!\n Combinatia\nnu functioneaza!\n Flugzeughalt muss auf\nRunway liegen!\n Trebuie sa fie\n pe taxiway. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Nu poti construi\n\naeroport aici.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Aici nu se poate\nconstrui semafor!\n Lost connection\nto server! Nu exista! Lost synchronisation\nwith server. Nu exista! Maglevhalt muss auf\nMaglevschiene liegen!\n Un semafor maglev trebuie plasat pe linie maglev! Monorailhalt muss auf\nMonorail liegen!\n Statiile monorail trebuie\npuse pe linii monorail. Monorails are not available yet! Linie monorail indisponibila. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n acest stop trebuie construit pe linia specifica No through station here! Statiile trebuie plasate la capatul liniilor sau pe sectiuni in line dreapta. Not enough money! Nu ai suficienti bani\npentru a construi! On narrowgauge track only!\n Doar pe ecartament ingust!\n Post muss neben\nHaltestelle\nliegen!\n Extensia (posta, depozit)\n trebuie plasat pe loc liber\nlanga o statie existenta.\n Protocoll error (expecting game) Eroare protocol (ceva s-a stricat) Schiffhalt muss im\nWasser liegen!\n O nava poate fi\n plasata doar inapa\nlanga un doc!\n Terraforming not possible\nhere in underground view Teraformarea imposibila sub pamant. Upgrade must have\na higher level Upgrade-ul se face\ndoar cu un nivel in plus. Zughalt muss auf\nSchiene liegen!\n Statiile trebuiesc\npuse pe\nsina normala.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Keyboard Help\n

Keyboard Help

\n Ajutor tastatura\n

Ajutor tastatura

\n

\nAjutor tastatura arata diferitele actiuni peare le poti face folosindu-te de taste.\n

\n

\nAjutor tastatura se deschide cand este apasata o tasta fara corespondenta sau din Ajutor general.\n

\n

\nScurtaturile (shortcuts) sunt case-sensitive (foloseste [Shift] pentru litere mari).\n

\n

\nFunctiile incluse contin:\n

\n

\n[Sageti]: muta camera in directia dorita.
\n[Backspace]: inchide toate ferestrele, texte ajutor si meniuri.
\n[Delete] sau [Escape]: inchide fereastra de sus, meniu sau text ajutor .
\n[Enter] sau [Return]: folosit pentru a confirma actiuni.
\n[Page-Up] sau [>]: magnificare.
\n[Page-Down] sau ["<"]: zoom-out.\n[F1]: deschide Ajutor general.

\n

\n[1]: scroll game-view south-wards.
\n[2]: scroll game-view south-east-wards.
\n[3]: scroll game-view east-wards.
\n[4]: scroll game-view south-west-wards.
\n[6]: scroll game-view north-east-wards.
\n[7]: scroll game-view west-wards.
\n[8]: scroll game-view north-west-wards.
\n[9]: scroll game-view north-wards.\n

\n

\n[Shift] + mouse: used on Map to see links in Industry Supply Chain.
\n[CTRL] + tool: construct (signals & Stops) on upper level; or build slower roads and tracks over faster ones; or build straighter (more direct) roads and tracks.
\n[CTRL] + ([F2] to [F12]): sets select current tool to keypresses [F2] to [F12].\n

\n

#____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s acum\noofera transport autobuz\nintre %s\nsi atractia\n%s\n la (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s acum\nofera transport autobuz\nintre %s\nsi fabrica\n%s\nla (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nopereaza acum\n%i camioane\n%s intre (%i,%i)\nsi %s\nla (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\na deschis o noua linie\nintre %s\nla (%i,%i) si\n%s\nla (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Transport aerian\n%s\nacum intre\n%s \nsi %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Bac oferit de\n%s acum intre\n%s \nsi %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\ndeschide un nou sistem\nde transport intre\n%s si\n%s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Unelte editare harta LISTTOOLS Liste MAGLEVTOOLS Unelte maglev MONORAILTOOLS Unelte monorail/maglev NARROWGAUGETOOLS Unelte ecartament ingust RAILTOOLS Unelte trenuri ROADTOOLS Unelte automobile SHIPTOOLS Unelte nave SLOPETOOLS Unelte pamant SPECIALTOOLS Unelte speciale TRAMTOOLS Unelte tramvai #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s's a construit un nou sediu. Factory chain extended\nfor %s near\n%s built with\n%i factories. Economie puternica:\n%s aproape de %s se extinde.\n%i are o fabrica noua. New %s now available:\n%s\n Noi %s acum disponibil:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Crestere: Nou lant industrial\npentru %s langa\n%s, constructie cu \n%i fabrici. New vehicle now available:\n%s\n \n Un nou vehicol este\nacum disponibil:\n\n\n -- %s --\n\n Screenshot\ngespeichert.\n Imaginea \ns-a salvat.\n Sends the convoi to the last depot it departed from! Vehicolul este trimis în depoul de unde provine! #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (in depou) \nBauzeit bis pana \nBauzeit von \nApare din \nCan't open heightfield file.\n \nRelieful nu poate fi deschis.\n \ndirection: \ndirectii: \nelektrified \nelectrificat\n \nHeightfield has wrong image type.\n \nRelieful con?ine imagini\nincompatibile.\n \nnot elektrified \nneelectrificata\n %d convois %d convoi %d Einzelfahrzeuge im Depot %d sunt %s has entered a depot. %s a intrat in depou. %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s \na construit o\n noua primarie\n cand a ajuns la\n%i locuitori. 1 Einzelfahrzeug im Depot 1 vehicol 1LIGHT_CHOOSE Luminozitate: 1WORLD_CHOOSE Setari pentru joc nou: 2LIGHT_CHOOSE Nr. de culori: 2WORLD_CHOOSE Numar harta: 3LIGHT_CHOOSE Viteza de scrol: 4LIGHT_CHOOSE Scrol invers 5LIGHT_CHOOSE Pietoni in statii 5WORLD_CHOOSE Nr. de orase: 6LIGHT_CHOOSE Pietoni in orase 6WORLD_CHOOSE Densit. traficului: 8WORLD_CHOOSE Mod zi/noapte A bridge must start on a way! Nu detii capetele podului sau acestea sunt obstructionate. Abfrage Unealta informatii Abnehmer Consumator About Despre Abriss Distruge/Sterge Absenken Coboara nivel Accelerate time Accelereaza timpul Active player only Doar jucător activ Add forest Adaugă pădure Add random citycar Adaugă ma?ină de ora? aleator Add Stop Adaugă Stop Add stops for backward travel Adaugă stopuri pentru circula?ie inversă air pistă aircraft_tab Avioane de transport airplane avion Airport Aeroport AIRTOOLS Unelte Aeroport All Tot all convoi tooltips toate uneltele pentru convoi Allow city growth Permite dezvoltarea orasului Allow player change Permite schimbarea jucatorului allowed climates:\n permite clime Alters a schedule. Adauga/sterge opriri in/din program Angenommene Waren Nevoile uzinelor adiacente anhaengen Adauga Anhaenger_tab Remorci Anheben Ridica nivel Appends stops at the end of the schedule Adauga opriri la finalul programului Apply Line Aplica linie April Aprilie Arbeiter aus: Muncitorii traiesc in Arrived Sosit Assets Bunuri Aufloesen Dezansambleaza Autohalt muss auf\nStrasse liegen!\n Statiile trebuie\npuse pe drum. Available Disponibil Bahndepot Depou trenuri Bankrott:\n\nDu bist bankrott.\n Faliment:\n\nAi dat faliment!\n battery Baterie Baum Copac baum builder Planteaza copaci Baustelle Santier Bauzeit timp constructie Beenden Terminare joc Beginner mode Mod Incepator Besonderes Gebaeude Atractii turistice BF statie bio biologic Blockstrecke ist\nbelegt\n \nBlocaj folosit\nde un alt tren!\n Boden Uscat bridge is too high for its type! Podul este prea inalt! Bridge is too long for this type!\n Podul este prea lung. Bruecke Pod Bruecke muss an\neinfachem\nHang beginnen!\n Podurile incep\npe zona plata!\n Brueckenboden pod Build air depot Construieste hangar build choosesignals Contruieste platforma alege semnale Build city market Construieste piata in cel mai apropiat oras. Build drain Transformator build HQ Construieste sediu Build land consumer Construieste centrala electrica Build maglev depot Construieste depou maglev Build monorail depot Depou monorail Build narrowgauge depot Depou linie-ingusta Build powerline Statie transformator Build presignals Semnal dublu Build road depot Garaj Build ship depot Port Build signals Semnale Build train depot Depou tren Build tram depot Depou tramvai Build truck depot Garaj Building costs estimates Cost estimativ Buildings Numar cladiri Built artifical slopes Panta artificiala Built random attraction Atractie aleatoare Bus_tab Autobuze Can only move from halt to halt or waypoint to waypoint. Poti muta doar \ndin halta in halta sau \nde de la waypoint la waypoint. Cancel Anuleaza Capacity: %d%s %s\n Capacitate: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Capacitate: %s\n? %d (%d%%) Cars are not available yet! Masinile nu sunt inca disponibile cars.\nstate masini\n Cash Balanta cont Change player Schimba jucator Chart Grafic Choose direction Alege directie Choose operation executed on clicking stored/new vehicles Alege mod pentru vehicole noi/in garaj chooses a random map Alege o harta aleatoare. citicens Cetateni City attraction Atractii oras City industries Piete in oras City list Lista orase City size Dimensiune oras city_road Drum orasenesc citybuilding builder Cladiri urbane CityLimit Limite oras cl_title Lista vehicole cl_txt_sort Sorteaza dupa: Clear block reservation Arata/reseteaza cai rezervate clf_chk_aircrafts Avioane clf_chk_cars Autobuze/Camioane clf_chk_indepot intr-un depou clf_chk_maglev Maglev-uri clf_chk_monorail Monorail-uri clf_chk_name_filter Nume filtre: clf_chk_narrowgauge Trenuri ecartament mic clf_chk_noincome venit zero clf_chk_noline nicio linie clf_chk_noroute nicio ruta clf_chk_noschedule niciun program clf_chk_ships Nave clf_chk_spezial_filter Filtru special: clf_chk_stucked blocat clf_chk_trains Trenuri clf_chk_trams Tramvaie clf_chk_type_filter Tip filtru: clf_chk_waren Filtru bunuri: clf_title Filtru vehicole Climate Control Control clima closed oprit. COLOR_CHOOSE\n Alege culoarea:\n Company bankrupt Companie falimentara Congratulation\nScenario was complete in\n%i months %i years. Felicitari!\nScenariul a fost\nterminat in\n%i luni si %i ani! Connected stops Conecteaza statii Constructed by Pictat de Constructed by %s Pictat de %s Construction_Btn Costuri constructii convoi %d of %d convoi din %d %d convoi error tooltips ajutor erori convoi Convoi has been sent\nto the nearest depot\nof appropriate type.\n Convoiul a fost trimis\nin depoul cel mai\napropiat.\n Convoi is sold when all wagons are empty. Vehicolul va fi vandut imediat ce va fi descarcat. convoi mouseover tooltips ajutor convoi mouseover convoi passed last\nmonth %i\n \nconvoaie in ultima\nluna: %i\n Convois Convoaie Convois: %d\nProfit: %s Vehicole: %d\nProfit: %s Convoys Convoaie Copy Convoi Duplicare convoi Copy the selected convoi and its schedule or line Copiaza convoiul si programul/linia aferenta cost for removal Cost eliminare Costs Costuri Create a new line based on this schedule Creeaza linie noua dupa acest program curiosity builder Constructie curiozitate curlist_title Lista atractii Currently playing: Joaca: Deccelerate time Decelereaza timp December Decembrie decrease underground view level nivel superior Del Stop Elimina Delete Line Sterge linie Delete the current stop Sterge stop Delete this file. Sterge fila. Denkmal Monument Departed Plecat Depots Depouri Der Tunnel ist nicht frei!\n Tunelul nu este liber.\n Destination Sosiri Details Detalii Die Bruecke ist nicht frei!\n Podul nu este liber!\n Direkt erreichbare Haltestellen Traseu direct de aici disable midi Opreste muzica MIDI Display settings Setare monitor Distance Distanta Dock Doc Dock must be built on single slope! Docurile pot fi construite doar pe o panta. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Ai %d luni sa platesti datoria. Durchsatz Max. Economy Economie si orase Eigenbesitz\n Proprietate publica\n Ein %s\npasst hier nicht.\n '%s'\nnu se potriveste aici!\n Einstellungen aendern Schimba optiuni Electricity Electricitate Electricity producer\n\n Producator electricitate\n\n Electrics_tab Electrice Electrify track Electrifica linia enlarge map Mareste harta enter a value between %i and %i introdu o valoarea intre %i si %i Error Eroare Erzeuge neue Karte.\n Te rog asteapta pana cand\n noua harta este creata.\n\n(S-ar putea sa dureze\nceva timp pt. harti imense)\n Es wird bereits\nein Fahrplan\neingegeben\n Lucrezi deja la\nun program.\nTermina-l inainte de a\nincepe unul nou.\n Fabrikanschluss Uzine conectate Fabrikname nume fabrica Factories Fabrici factory details Legaturi fabrica factorybuilder Constructor fabrica Fahrplan Program Fahrtziel Destinatie: Fahrzeuge koennen so nicht entfernt werden Vehicolele nu pot\nfi eliminate asa!\n Fahrzeuge: Vehicole Farbe Culoare jucator Fast forward Accelerare timp February Februarie Ferry_tab Bacuri Fertig Gata Filename Nume fisier: Filter: Filtru: Finances of %s Finantele %s Finanzen Finante fl_title lista fabrici Flug_tab Avion pasageri follow me Urmareste-ma. Follow the convoi on the map. Urmareste acest convoi pe harta. Forest Padure Found new city cladeste oras nou Fracht Marfa Frame time: Timp frame: Free Capacity Spatiu ramas freeplay mode joc liber Friction: indice frecare: fuel_cell combustibil Full load Incarcatura minima: Fussgaenger Pieton GAME PAUSED Pus pe pauza Gear: Viteza: Gebaeude Cladire Gewicht Greutate Gewinn Venit: Give the selected vehicle(s) an individual schedule Da vehicolului selectat un program. gl_btn_sort_catg Categorie gl_title Lista bunuri go home La depou. Goods Bunuri Goods AI Bunuri AI Goods list Lista bunuri Gross Profit Flux bani Groundobj Obiect Grow city Creste oras. Growth Crestere oras H stop Happy Bucuros Haus kaufen Cumpara casa Helligk. Monitor Help Ajutor Help text not found Nu exista text ajutor. hide all building ascunde toate cladirile hide city building ascunde toate cladirile hide station names ascunde nume statii hide transparent transparent in loc de ascuns hide trees Ascunde copacii Hier warten/lagern: Bunuri si pasageri care asteapta: hl_title Lista statii hl_txt_filter Filtru: hl_txt_sort Sortat dupa: hlf_chk_airport Aeroporturi hlf_chk_anleger Doc hlf_chk_bahnhof Statie tren hlf_chk_bushalt Statie autobuz hlf_chk_frachthof Depou hlf_chk_keine_verb Nicio legatura hlf_chk_maglevstop Statie Maglev hlf_chk_monorailstop Statie Monorail hlf_chk_name_filter Nume filtru: hlf_chk_narrowgaugestop Statie ecartament ingust hlf_chk_overflow Peste capacitate hlf_chk_spezial_filter Filtru special: hlf_chk_tramstop Statie tramvai hlf_chk_type_filter Tip filtru: hlf_chk_waren_abgabe Bunuri iesite: hlf_chk_waren_annahme Bunuri primite: hlf_title Filtru lista statii Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depou inexistent!\nDepoul trebuie desemnat\nmanual. Homeless Cersetor hydrogene Hidrogen Idle: Inactiv: ignore climates ignora clime Increase Industry density Mareste densitate industrii increase underground view level nivel suplimentar industrial building Cladiri industriale Init map ... Creez harta ... Input input Ins Stop Insereaza Insert stop before the current stop Adauga stop inainte Intercity road len: Lungime drum intercity: Intro. date: Intro. data: invalid nedefinit Invalid coordinate Ordin invalid isometric map Vedere izometrica January Ianuarie July Iulie Jump to Sari la June Iunie Kann Spielstand\nnicht laden.\n Nu pot incarca salvarile! Kann Spielstand\nnicht speichern.\n Nu pot deschide\n fila pentru\n scriere! Kein Besitzer\n Fara proprietar\n keine nimic Keine Einzelfahrzeuge im Depot Niciun vehicol aici. Keyboard_Help\n Ajutor tastatura\n koord coordonate Kreuzung Intersectie labellist_title Lista markere Lade Relief Incarca height map Laden Incarca Land attraction Atractie Land industries Lanturi industriale: LANG_CHOOSE\n Alege limba:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE Ie6 Last Year Anul trecut: Leaving depot! Paraseste depoul! leer gol Legend Legenda harta Leistung Putere Leistung: %d kW Putere: %d kW Leitung Linie electrica letzen Monat: diesen Monat: luna trecuta: luna curenta: Line Traseu Line Management Management trasee Lines serving this stop Trasee pentru aceasta oprire LKW_tab Camioane Load game Incarca joc load height data from file Incarca inaltime din fisier. Load scenario Incarca scenariu loaded incarcat loaded passenger/freight Sorteaza pasageri/marfa dupa Loading (%i->%i%%)! Se incarca (%i->%i%%) Loading addon paks ... Incarc pakuri suplimentare ... Loading map ... Incarc harta ... Loading paks ... Incarc pakuri ... Loading skins ... Incarc skinuri ... Lock game Refuza schimbarile jucatorului (necesita confirmare). Lokomotive_tab Locomotive m3 mÂ? maglev vehicle vehicol maglev maglev_track sina maglev Maglevdepot depou maglev Mailbox Centru mesaje Mailbox Options Optiuni centru mesaje Maintenance Mentenanta make stop public (or join with public stop next) costs %i per tile and level fa statia publica (sau conecteaza la cele adiacente publice) costuri %i$ pe dala si nivel Manual (Human) Manual (Uman) Manufactured: Fabricat: Map roughness Dificultate teren: map zoom zoom March Martie Margin (%%) Margine Marker Indicator Max income: Venit maxim: Max. speed: Viteza max.: Maximum 254 stops\nin a schedule!\n Maxim 254 statii\nintr-un program!\n maximum length of rivers Lungima maxima Maximum tile height difference reached. Diferenta maxima\n intre doua dale\n a fost atinsa. May Mai Median Citizen per town Numar mediu de locuitori: Meldung Mesaj Menge cantitate MessageOptionsText \nAn nou\n\nStiri AI\n\nStiri Oras\n\nNicio ruta\n\nIndustrii noi\n\n"chat"\n\nMasini noi\n\nStatii pline\n\nProbleme\n\nAvertizari\n\nScenario min min. minimum length of rivers Lungime minima Modify the selected line Modifica traseul Monate alt luni vechime. monorail vehicle vehicol monorail monorail_track Sina monorail Monorailboden Cale elevata Monoraildepot Depou monorail month wait time luni de asteptare Months Luni Monuments Monumente Mountain height Inaltime munte Movingobj mutare obiect Music playing disabled/not available Muzica dezactivata/inexistenta. Music volume: Volum muzica: mute sound Mut Name Nume Narrowgauge Ecartament ingust Narrowgauge are not available yet! Indisponibil momentan! narrowgauge vehicle vehicol ecartament ingust narrowgauge_track linie ecartament ingust Narrowgaugedepot depou ecartament ingust Net Wealth Avere Neue Karte Harta noua Neue Welt Creeaza un joc nou new convoi Convoi nou New Line Traseu nou New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Traseul nou este gata!\nAtribuirea se face cu\nselectorul de traseu.\n New Vehicles Vehicol nou no buildings hidden nicio cladire ascunsa no convois nu este conv. No goods are loaded onto this convoi. Acest convoi nu va fi incarcat no tree fara copaci Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n "Unde ma duc?"\nTrebuie sa-i dai convoiului un program sau o linie inainte de alte ordine. none nimic nord Nord nordost Nord-est nordwest Nord-vest Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Programul nu poate\n fi schimbat\n in acest moment. November Noiembrie Number of rivers Numar de rauri Oktober Octombrie On this map, you are not\nallowed to change player!\n Jocul este oprit.\nNicio modificare nu este posobila.\n Ops Profit Profit ops Optionen Optiuni Origin Origine paletten palete Passagiere pasageri Passagierrate Trafic pasageri Percent Electricity Electricitate (%% cerinta) Planes are not available yet! Avioanele nu sunt inca disponibile. player 2 Trikky Transport player 3 Meyer Moving Co. Post Post? Power Putere promote to line Traseu nou Random map Harta aleatoare return ticket Retur Revenue Venit sack sac(i) Schienentunnel Tunel feroviar Schiff_tab Nave Schiffdepot Depou nave Schleppkahn_tab Balize Sell the selected vehicle(s) Vehicolul selectat este vândut SEP_FRACTION , September Septembrie Server did not respond! Serverul nu a raspuns! Size (%d MB): Dimensiune (%d MB): Sound Sunet Sound settings Setare sunet Sound volume: Volum efecte: Spielstand wurde\ngespeichert!\n \nSalvarea jocului s-a terminat!\n Sprache Limba Sprachen Limbi Starte Spiel Incepe Jocul Status Stare steam aburi Strassentunnel Tunel Suppliers Parteneri: Tage alt zile There are still vehicles\nstored in this depot!\n Mai este vehicol\nîn depou!\n This Year Anul actual: tonnen t Tracks Trasee Traffic Trafic Train Tren Truck Camion units/day /zi, Update Line Actualizare traseu Verbrauch Consum verkaufen Vânzare Verkehrsteilnehmer Automobil via %s\n via %s\n Waggon_tab Vagoane Wert Valoare Years An #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &1_CITY_SYLL câmpie &2_CITY_SYLL sat &4_CITY_SYLL vad &6_CITY_SYLL dumbrava &7_CITY_SYLL vârf &9_CITY_SYLL cruci &A_CITY_SYLL hotar %5_CITY_SYLL Râmnicu %6_CITY_SYLL Slatina %7_CITY_SYLL Vama %8_CITY_SYLL Întorsura %B_CITY_SYLL deal 1center %s %s 2center %s centru %s 2extern %s de sus %s 3center %s principal %s 3extern %s de jos %s 4center %s secundar %s 4extern %s extern %s 5center %s transfer %s Acenter Nu există! Aextern Nu există! Asuburb Nu exista! Bcenter Nu exista! Bextern Nu exista! Bsuburb Nu exista! Ccenter Nu exista! Cextern Nu exista! Csuburb Nu exista! Dcenter Nu exista! Dextern Nu exista! Dsuburb Nu exista! Ecenter Nu exista! Eextern Nu exista! Esuburb Nu exista! Fcenter Nu exista! Fextern Nu exista! Fsuburb Nu exista! Gcenter Nu exista! Gextern Nu exista! Gsuburb Nu exista! Hcenter Nu exista! Hextern Nu exista! Hsuburb Nu exista! Icenter Nu exista! Iextern Nu exista! Isuburb Nu exista! Jcenter Nu exista! Jextern Nu exista! Jsuburb Nu exista! Kcenter Nu exista! Kextern Nu exista! Ksuburb Nu exista! Lcenter Nu exista! Lextern Nu exista! Lsuburb Nu exista! Mcenter Nu exista! Mextern Nu exista! Msuburb Nu exista! Ncenter Nu exista! simutrans-124.5/simutrans/text/ro/000077500000000000000000000000001520675460500172155ustar00rootroot00000000000000simutrans-124.5/simutrans/text/ro/airtools.txt000066400000000000000000000120031520675460500216060ustar00rootroot00000000000000Ajutor unele aeroport

Unelte aeroport

Unelte aeroport contine unelte care te ajuta sa construiesti o retea de trafic aerian. Acestea pot construi sau elimina : piste de rulare si piste de decolare/aterizare, terminale (pentru pasageri sau marfa) si alte cladiri specifice unui aeroport. Daca joci cu timeline, noi unelte apar cu trecerea timpului.

Apasa pe iconita avionului in partea de sus a meniului pentru a deschide bara de unelte.
Tine cursorul deasupra uneltelor (dupa deschiderea barei) pentru a vedea numele si costul constructiei, pretul mentenantei in paranteze si limita de viteza.

{Sfat: Un mod usor de a construi un aeroport:
i) construieste si conecteaza pistele de rulare si decolare;
ii) plaseaza terminale la capatul pistelor de rulare;
iii) adauga cladiri necesare.}

Uneltele pot include, de la stanga la dreapta:

Pista rulare: este o pista folosita de avioane pentru a traversa de la terminal la pista de decolare. Pistele de taxi sunt construite la nivelul zero (nu pot fi construite sub pamant sau suspendate).
IMPORTANT: Daca pistele de rulare sunt conectate la capatul pistelor de aterizare/decolare, avioanle nu vor putea ateriza sau decola.
Pentru a construi o pista de rulare: click pe iconita (cursorul se va schimba), click pe teren pentru un punct de pornire (arata un buldozer iar in partea din dreapta jos coordonate) iar la final click in locul in care vrei sa se termine aceasta pista.
{Sfat: Foloseste Distruge/Elimina pentru a sterge bucati din pista de rulare. Functia undo, [z] nu va returna banii.)

Pista principala: construieste o pista pentru aterizarea si decolarea avioanelor. Aceste piste sunt construite la nivelul zero (deci nu pot fi construite sub pamant sau suspendate) dar se pot suprapune.
Pentru a construi o pista principala: click pe iconita (care se va schimba), click pe teren pentru a seta inceputul pistei (se va schimba in iconita unui buldozer) iar la final click in locul in care vrei ca pista sa se termine.
{Sfat: Capetele pistei principale trebuie sa fie goale, altfel avioanele nu vor putea decola sau ateriza. Foloseste Distruge/Elimina pentru a sterge bucati din pista de rulare. Functia undo, [z] nu va returna banii.}

Eliminarea pistelor: aceasta unealta va sterge un segment din pista de rulare sau aterizare, doar atunci cand nu este folosita de un avion. Aceasta unealta necesita anumite costuri.
Pentru a sterge pista: click pe iconita (se va schimba intr-o crucie rosie), click pe inceputul pistei de care nu mai ai nevoie (iconita se va schimba din nou) iar la final click pe capatul zonei ce urmeaza a fi distrusa. {Sfat: Capetele pistei principale trebuie sa fie goale, altfel avioanele nu vor putea decola sau ateriza. Foloseste Distruge/Elimina pentru a sterge bucati din pista de rulare.}

Hangarul: este folosit pentru a cumpara si a inventaria avioane. Hangarele au costuri de mentenanta si sunt construite la capatul unei piste de rulare.
Pentru a construi un Hangar: click pe unealta (se va schimba in iconita unui hangar) si apoi click pe capatul unei piste de rulare.
{Sfat: Elimina hangare cu unealta de Distruge/Sterge.}

Terminal: aceata unealta construieste statii pentru avioane, folosite pentru imbarcarea si debarcarea pasagerilor, incarcareea si descarcarea de marfa.
Un Terminal, cand nu este construit langa un alt Terminal va actiona de sine statator (nu va imparti marfa sau pasageri cu celalalt terminal).
Terminalele sunt construite la capatul pistelor de rulare si au costuri de mentenanta. Mai mult, acestea au zone (arie de efect) care determina numarul de pasageri, posta si marfuri ce vor tranzita aeroportul. Un avion va ateriza numai la un aeroport care are un Terminal liber.
Pentru a construi un Terminal : click pe unealta si apoi click pe capatul unei piste de rulare.
{Sfat: Elimina Terminale cu unealta de Distruge/Sterge. Apasa [v] pentru a arata/ascunde aria de efect penru marfuri si pasageri.}

Cladiri aeroport: aceasta unealta permite constructia de extensii pentru Terminale care pot creste costul de mententanta, capacitatea si aria de efect a acestuia, atat pentru pasageri cat si pentru marfa. In coltul unor unelte, o unealta (folosita in Lista statii si Terminale) arata care elemente din extensie pot functiona cu terminalul.
Pentru a construi o extensie: click pe unealta pentru a selecta extensia dorita (cursorul se va schimba in forma unei extensii) iar apoi click pe zona potrivita, langa un Terminal. Noua extensie construita este acum parte din Terminal.
{Sfat: Sterge extenstiile cu unealta de Distruge/Sterge. Apasa [v] pentru a arata/ascunde aria de efect pentru marfa si pasageri.}

simutrans-124.5/simutrans/text/ro/color.txt000066400000000000000000000004711520675460500210760ustar00rootroot00000000000000Help culori

Selectare culoare

Aici poţi să-ţi alegi propria culoare. Cu culoarea aleasă sunt marcate toate proprietăţile şi vechicolele tale. Nu toate obiectele sunt marcate cu culoarea aleasă, ci depinde cum a fost obiectul implantat în joc de cel care l-a proiectat.

simutrans-124.5/simutrans/text/ro/depot.txt000066400000000000000000000024301520675460500210700ustar00rootroot00000000000000Depou

Depou

Aici poţi procura vechiculele dorite şi defineşti trasee.

Poţi găsi toate vechicolele ce pot fi cumpărate la un anumit moment. Într-u tab sunt puse angrenajele (locomotivele respectiv camioanele ), iar în altul sunt puse vagoanele sau remorcile. Nu toate combinaţiile sunt permise, acesta este semnalat printr-o linie verde (permis) sau roşie (nepermis).

În partea de jos se găsesc parametrii vehicolului dorit (preţ, putere, capacitate etc.)

Cu butonul "Start" porneşti noul convoi, însă numai atunci îl poţi porni după ce la punctul "Traseu/rută" defineşti un traseu. Cu butonul "Dezleagă" dezlegi convoiul actual fără să-ţi primeşti înapoi banii. Preţul vehicolului este returnat când alegi butonul "Vinde". Preţul scade odată cu trecerea timpului.

Achiziţionarea se face cu un click stâng pe vehicolul dorit. În partea dreaptă mai în jos poţi găsi butonul cu care alegi dacă noul vehicol este pus în faţă sau în spate. Poţi lucra deodată numai la un singur convoi!

În timpul jocului dacă vrei ca convoiul să meargă înpoi în depou insertează depoul, în traseul convoiului, sau apasă pe butonul "înapoi depou".

simutrans-124.5/simutrans/text/ro/display.txt000066400000000000000000000013111520675460500214170ustar00rootroot00000000000000Help Setare monitor

Setare monitor

Aici poţi defini setările dorite pt. monitor.

Luminozitate - Schimbă lumina.
Nr. de culori - Nefuncţional încă
Viteza de scrol - Poţi seta viteza de scrol a mapei.

Scrol invers - Defilarea mapei se schimbă
Arată pietonii - Arată sau nu pietonii din oraş.
Mod zi/noapte - Arată sau nu trecerea zile şi a nopţilor.

Îm partea de jos sunt unele informaţii nesemnificative în timpul jocului. Însă dacă ai ceva probleme cu jocul şi vrei să vorbeşti cu autorii probabil unele informaţii afişate aici vor fi foarte importante.

simutrans-124.5/simutrans/text/ro/finances.txt000066400000000000000000000016441520675460500215510ustar00rootroot00000000000000Help Finanţe

Finanţe (f)

Aici poţi afla amănunte despre starea economiei tale.Sunt afişate cele mai importante parametre care pot fi analizate anual s-au lunar.

Sunt afişate:

Construcţii - Suma cheltuită pe construcţii noi.

Vehicole noi - Suma cheltuită pt. achiziţionarea de noi vehicole.

Cost transport - Suma ce este cheltuită în timpul transporturilor.

Întreţinere - Suma necesară întreţinerii vehicolelor şi traseelor.

Venit - Venitul realizat (este semnalat şi în partea de jos a jocului)

Total - Suma consturilor şi veniturilor sus menţionate.

Balanţa - Balanţa actuală.

Întraţinere lunară - Costul înterţinerii lunare.

În partea dreaptă, sus poţi afişa grafic alte parametrii.

simutrans-124.5/simutrans/text/ro/language.txt000066400000000000000000000011621520675460500215410ustar00rootroot00000000000000Help Limba

Limba

Simutrans a fost tradus în mai multe limbi printre care şi româna. Print selectarea limbajului dorit toate inscripţiile jocului se schimbă în limbajul selectat.
Însă numele oraşelor de pe harta odată creată nu se schimbă. Pt. a folosi oraşe româneşti, prima dată trebuie selectat limba română, după care trebuie creat o nouă hartă.

Pt. eventuale alte traduceri vizitaţi: www.simutrans.de sau 128.simutrans.com

Pentru a ajuta la traducerea Simutrans în limba dvs.,
vizitati https://translator.simutrans.com

simutrans-124.5/simutrans/text/ro/load.txt000066400000000000000000000007241520675460500207000ustar00rootroot00000000000000Help Încarcă

Încărcarea jocurilor salvate:

Sub opţiunea "Încarcă" poţi găsi jocurele deja salvate. Butoanele au aceeaşi semnificaţie ca şi sub meniul "Salvare"

Dacă se încarcă un joc care deja nu mai există va apărea un mesaj de eroare.

Important! Jocurile salvate în versiunile mai vechi ale Simutrans probabil nu pot fi utilizate sub versiunile noi ale jocului.

simutrans-124.5/simutrans/text/ro/map.txt000066400000000000000000000005601520675460500205340ustar00rootroot00000000000000Help Hartă

Hartă

Este afişat o hartă în miniatură a întegului joc. Sunt afişate şi unele informaţii adiacente jocului.

Fiecare tip de unitate de producţie are propria culoare. Legenda culorilor este afişat sub hartă.

Harta poate fi mărită. Asta se face cu un click drept pe orice parte a hărţii.

simutrans-124.5/simutrans/text/ro/new_world.txt000066400000000000000000000035731520675460500217660ustar00rootroot00000000000000Help Hartă nouă

Hartă nouă

Aici se poate ajusta harta nou creată.

Nr. hartă - Aici se poate alege harta dorită. Un mic preview este afişat în partea dreaptă.
Mărime - Mărimea hărţii. Valoare iniţială este de 256.
Important!Cu cât harta e mai mare cu atât creşte şi nevoia de putere de calcul şi multă memorie.

Densit. industriei - Se poate seta densitatea unităţilor industriale. Valoarea iniţială este de 125.
Nr. oraşelor - SE poate seta nr. oraşelor (min. 2 max. 64) Valoare iniţială 16.
Densit. traficului - Se poate regla densitatea traficului din oraşe. Valoarea mai mică poate accelera jocul.

Adâncime apă - Aici se poate ajusta cantitatea de apă afişat pe hartă. Valoare mai mare cantitate mai mare. Valoare iniţială 4.
Înălţime munţi - Se poate ajusta nivelul munţilor. Valorile mai mari cauzează un teren cu mai multe culmi. Valoarea iniţială este de 160.
Dificultate teren - Se poate ajusta dificultatea terenului

Hartă aleatorie - Generare de hartă aleatorie.

Arată pietonii - Se poate seta să afişeze sau nu pietonii din oraşe. După pornirea unei noi hărţi valoarea se poate modifica in meniul "Monitor"
Mod zi/noapte - Aici se poate seta ca jocul să arate trecerea timpului sub formă de zi şi noapte. Neactivat înseamnă că întotdeauna este ziua.După pornirea unei noi hărţi valoarea se poate modifica in meniul "Monitor"

Încarcă joc - Încarcă un joc salvat. (vezi la Opţiuni meniul "Salvare".)
Porneşte joc Pornirea jocului.
Terminare joc - Ieşire din joc (fără confirmare).

simutrans-124.5/simutrans/text/ro/options.txt000066400000000000000000000010201520675460500214420ustar00rootroot00000000000000Help Opţiuni

Opţiuni

Limba - Selectarea limbii
Culoare jucător - Selectarea culorii.
Monitor - Setări monitor.
Sunet - Setări sunete.
Jucători - Selectare adversari.
Încarcă - Încarcă joc salvat
Salvează - Salvare joc.
Hartă nouă - Crearea unei noi hărţi
Terminare joc - Ieşire din joc (jocul ne se salvează automat)

Detalii în help-ul meniurilor.

simutrans-124.5/simutrans/text/ro/players.txt000066400000000000000000000006271520675460500214420ustar00rootroot00000000000000Help Jucători

Jucători

Aici poţi activa sau deactiva jucătorii manipulati de calculator.

Deactivarea unui jucător înseamnă defatp "hibernarea" acestuia, adică unităţile de producţie controlate de jucătorul respectiv vor produce în continuare, însă nu se vor înfiinţa noi legături şi industrii.

Sunt afişate banii deţinuţi de jucători.

simutrans-124.5/simutrans/text/ro/railtools.txt000066400000000000000000000012031520675460500217620ustar00rootroot00000000000000Scule pt. trenuri

Scule pt. trenuri

De aici se poate procura tren, vagoane, semafoare etc.

Sunt mai multe tipuri de cale ferată, fiecare permiţând o anumită viteză maximă.

Semafoarele la început pot semnala în ambele direcţii. Pt. a impune o anumită direcţie trebuie făcut click pe semafor, până ce se obţine direcţia dorită.

La construirea tunelului trebuie luat în considerare faptul, că şinele trebuie urcate pe pantă. În caz contrar programul nu permite construirea tunelezului.

Convoiul dorit se poate asambla în combinaţia dorită în depou.

simutrans-124.5/simutrans/text/ro/roadtools.txt000066400000000000000000000004571520675460500217720ustar00rootroot00000000000000Help Scule pt. automobile

Scule pt. automobile

Aici poţi găsii toate uneltele necesare pt. managementul automobilelor procurate în depou.

Fiecare tip de şosea permite o anumită viteză maximă.

Traversă se poate construi numai pe cale ferată.

simutrans-124.5/simutrans/text/ro/save.txt000066400000000000000000000001311520675460500207070ustar00rootroot00000000000000Help Salvare

Salvare

Aici se poate salva jocul actual.

simutrans-124.5/simutrans/text/ro/schedule.txt000066400000000000000000000005731520675460500215570ustar00rootroot00000000000000Traseu/rută

Traseu/rută

Aici defineşti trasee pt. convoaiele dorite. În fiecare staţie poţi defini cât la sută din capacitatea de transport să fie utilizat. Însă trebuie luat în considerare că dacă unitatea de producţie nu poate să umple vehicolul, atunci acesta va sta în staţie până ce se va umple la capacitatea setată.

simutrans-124.5/simutrans/text/ro/shiptools.txt000066400000000000000000000003431520675460500220020ustar00rootroot00000000000000Help Scule pt. nave

Scule pt. nave

Aici găseşti toate uneltele necesare pt. construire de nave şi docuri maritime.

Locul de ancorare trebuie ales cat mai aproape de staţia dorita.

simutrans-124.5/simutrans/text/ro/sound.txt000066400000000000000000000006301520675460500211050ustar00rootroot00000000000000Help Setare sunet

Setare sunet

Aici poţi selcta efectele dorite.

Volum efecte: - Ajustarea efectelor (ex. claxonarea etc.)
Volum muzică: - Ajustarea muzicii. Atenţie în unele cazuri jocul este instalat fără muzică, în acest caz potenţiometrul nu are nici un efect. Melodia jucată este afişată cu un rând mai jos

simutrans-124.5/simutrans/text/ro/station.txt000066400000000000000000000002451520675460500214400ustar00rootroot00000000000000Help Staţii

Staţii

Sunt afişate parametrii staţiei selectate (câţi pasageri sunt, câtă materie primă staţionează etc.)

simutrans-124.5/simutrans/text/ro/station_details.txt000066400000000000000000000002521520675460500231430ustar00rootroot00000000000000Detalii staţii

Detalii staţii

Poţi analiza toate legăturile staţiei, precum şi legăturile directe cu unităţile de producţie.

simutrans-124.5/simutrans/text/ru.tab000066400000000000000000002212641520675460500177220ustar00rootroot00000000000000§Russian PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: ru Russian # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Отобразить время как "день чч:мм месяц" AMBIENT_SOUND Окружающие звуки CASH_SOUND Денежные звуки cl_btn_filter_disable Отключено cl_btn_filter_enable Включено cl_btn_filter_settings Настройки cl_btn_sort_asc По возрастанию cl_btn_sort_desc По убыванию cl_btn_sort_id Внутренний ID cl_btn_sort_income Доход cl_btn_sort_name Название cl_btn_sort_type Тип clf_btn_alle Все clf_btn_invers Обратно clf_btn_keine Нет climate area percentage Площадь климата Continue Game Продолжить игру CROSSING_SOUND Предупреждающие звуки FACTORY_SOUND Производственные шумы Find matching convois Поиск подходящих т/с gl_btn_sort_bonus Бонус gl_btn_sort_name Название gl_btn_sort_revenue Выручка gl_btn_unsort Не отсортировано height based Климат по уровням высоты hl_btn_filter_disable Отключено hl_btn_filter_enable Включено hl_btn_filter_settings Настройки hl_btn_sort_asc По возрастанию hl_btn_sort_desc По убыванию hl_btn_sort_name Название hl_btn_sort_type Тип hl_btn_sort_waiting Ожидание hlf_btn_alle Все hlf_btn_invers Обратно hlf_btn_keine Нет humidities Граница влажности Install Наборы пакетов Lake Озёра Lake height Макс. высота озёр Maximize height levels Макс. уровни высот moisture land Увеличение по суше moisture water Увеличение по воде Networks Сеть Num pad keys always move map Клавиши цифровой клавиатуры всегда перемещают карту Open Sea Открытое море Queueing Тенденция ожидания rainfall влажность Reselect closes tools Повторный выбор инструментов закрывает их Return to menu Вернуться в меню Road toll Дорожный сбор Scenario Информация о сценарии sea Морской климат Single GUI Единый GUI Sound range: Диапазон звука: Start this as a server Запустите сервер с этой игрой temperature borders Границы температур temperature-humidity based Климат по погоде TOOL_SOUND Звуки инструментов TRAFFIC_SOUND Транспортные шумы Transfers Трансфер Tutorial Обучение #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Арктический desert Пустынный mediterran Средиземный rocky Альпийский (скалистый) temperate Умеренный tropic Тропический tundra Субарктический #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Ошибка загрузки скрипта сценария Bridge blocked by way below or above. Высота моста не достаточна для прокладки путей под ним. Can't buy obsolete vehicles! Устаревшие транспортные средства покупать нельзя! Cannot alter water Этот водоем не может быть больше осушен или заполнен. Cannot build on a double slope! Может быть построено только на одиночном спуске или плоскости! Cannot built depot here! Здесь нельзя построить депо! Cannot built this station/building\nin underground mode here. Невозможно построить эту станцию/здание\nв подземном режиме. Cannot create generic line!\nSelect line type by\nusing filter tabs. Нельзя создать общую линию!\nВыберите тип линии,\nиспользуя фильтр. Cannot create socket Невозможно создать соединение Cannot rotate this building! Невозможно вращать это строение! Client closed connection during transfer Клиент закрыл соединение при загрузке. Convoi handles exhausted! Достигнут максимум т/с. Convoy already deleted! Т/с уже удалено! Could not open file Невозможно открыть файл. Das Feld gehoert\neinem anderen Spieler\n Этим участком\nземли владеет\nдругой игрок!\n Der Besitzer erlaubt das Entfernen nicht Права владельца\nзапрещают удаление!\n Diese Zusammenstellung kann nicht fahren!\n Эта комбинация\nне может двигаться!\n Flugzeughalt muss auf\nRunway liegen!\n Посадочный терминал\nдолжен быть на дороге!\n Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Это здание аэропорта\nне может быть\nпостроено здесь!\n Hier kann kein\nSignal aufge-\nstellt werden!\n Семафор не может\nбыть установлен здесь!\n In order to lock the game, you have to protect the public player by password! Для блокировки игры, необходимо защитить текущего игрока паролем! Loading a new game will end the current server session! Загрузка новой игры закроет текущую сессию сервера! Lost connection\nto server! Нет соединения\nс сервером! Lost synchronisation\nwith server. Нет синхронизации\nс сервером. Maglevhalt muss auf\nMaglevschiene liegen!\n Остановка маглева\nдолжна находиться\nна путях маглева! Monorailhalt muss auf\nMonorail liegen!\n Монорельсовая остановка\nдолжна находиться\nна монорельсе! Monorails are not available yet! Монорельс ещё недоступен! More than one possibility to build this dock found. Обнаружено несколько возможностей построить этот док. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Остановка узкоколеных т/с\nдолжна находиться\nна узкоколейке! No bridges over runways! Мосты над шоссе запрещены! No curves on runways Взлётно-посадочная полоса не имеет повороты! No suitable crossing Нет подходящего пересечения! (максимальная скорость слишком быстрая?) No suitable way on the ground! Остановку можно построить только на\nпрямом участке трассы. No through station here! Станции должны располагаться на тупиковых путях или прямых участках, не имеющих пересечений. Not enough bytes transferred Передано недостаточно байтов Not enough clearance. Между двумя клеткам для постройки пути разница высот должна составлять минимум 2 уровня. Not enough money! У вас недостаточно денег! On narrowgauge track only!\n Только на узкоколейном пути!\n Only public player can lock games! Только публичный игрок может блокировать игру! Out of funds Не достаточно средств! Post muss neben\nHaltestelle\nliegen!\n Станционные строения\n должны размещаться\nна свободной клетке\nрядом с существующей\nстанцией или остановкой!\n Protocoll error (expecting game) Ошибка протокола (ожидание игры) Schiffhalt muss im\nWasser liegen!\n Остановка судна\nможет быть только\nоколо дока!\n Server busy Сервер занят Server version too new Сервер использует более новую версию программы. Show all revenue messages Показать все сообщения о доходах Show no revenue messages Не показывать сообщения о доходах Show only player's revenue Показывать только доход игрока Terraforming not possible\nhere in underground view Изменение ландшафта в подземном режиме не возможно! This tunnel branches. You can try Control+Click to remove. Этот тоннель разветвляется. Вы можете нажать Ctrl+клик для удаления. Too far away to merge stations! Слишком далеко для объединения станций! Upgrade must have\na higher level Усовершенствование должно иметь\nболее высокий уровень Vehicle %s cannot choose because stop too short! Транспортное средство %s не может выбрать путь, т.к. остановка слишком коротка! Zughalt muss auf\nSchiene liegen!\n Ж/д станции\nдолжны строиться\nтолько на ж/д путях!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Справка

Содержание

Про Simutrans

%1$s

Общие данные

%2$s

Начало игры

%4$s

Игровой процесс

%5$s

Инструменты

%3$s

Разное

%6$s Keyboard Help\n

Keyboard Help

\n Горячие клавиши\n

Справка по клавишам управления

\n

\nСправка по клавишам управления показывает функции основных клавиш на клавиатуре..\n

\n

\nСтраница справки по клавишам управления открывается, когда была нажата неверная клавиша или из страницы Главной справки.\n

\n

\nKey presses are case sensitive (use [Shift] for upper-case letters).\n

\n

\nKeys with assigned functions include:\n

\n

\n[Arrow Keys]: scroll game-view in direction of arrow.
\n[Backspace]: close all windows, toolbars and help-texts in game-view.
\n[Delete], or [Escape]: close top window, toolbar or help-text in game-view.
\n[Enter], or [Return]: used to confirm actions.
\n[Page-Up], or [>]: zoom-in game-view.
\n[Page-Down], or [ less than symbol]: zoom-out game-view.\n[F1]: open Simutrans Help.

\n

\n[1]: scroll game-view south-wards.
\n[2]: scroll game-view south-east-wards.
\n[3]: scroll game-view east-wards.
\n[4]: scroll game-view south-west-wards.
\n[6]: scroll game-view north-east-wards.
\n[7]: scroll game-view west-wards.
\n[8]: scroll game-view north-west-wards.
\n[9]: scroll game-view north-wards.\n

\n

\n[Shift] + mouse: used on Map to see links in Industry Supply Chain.
\n[CTRL] + tool: build slower roads and tracks over faster ones; or build straighter (more direct) roads and tracks.
\n

At least one stop is connected to the town. Как минимум одна остановка соединена с городом. At least one tile is connected to one stop. Как минимум одна плитка соединена с одной остановкой Color according to transport capacity left Цвет соответствует свободному месту транспорта Color-coded terrain according to altitude. Показать/скрыть затенение контура рельефа. Decrease water height Уменьшить уровень воды Highlight railroad tracks Выделить железнодорожные пути Highlite depots Выделить депо Highlite electrical transmission lines Выделить линии электропередачи Highlite factories Выделить предприятия Highlite forests Выделить леса Highlite tourist attraction Выделить достопримечательности Increase water height Увеличить уровень воды Open station/stop details Показать информацию о станции/остановке Overlay city limits Отображать границы городов. Overlay passenger destinations when a town window is open Отображать точки назначения пассажиров, когда открыто окно информации города. Overlay schedules/network Отображать всю транспортную сеть или только выбранный маршрут. Overlay town names Отображать названия городов. Please click on the map to add\nwaypoints or stops to this\nschedule. Укажите на карте, какие точки или остановки\nнеобходимо добавить к этому расписанию. Remove double stops from schedule Убрать из расписания повторяющиеся остановки Set tile climate %s Преобразовать плитку в зону %s Show capacity and if halt is overcrowded Отображать емкость складов станции и их переполнение грузами. Show how many convoi reach a station Отображать сколько т/с обслуживается на станциях. Show how many people/much is waiting at halts Отображать сколько пассажиров на станциях и как долго они ожидают свой т/с. Show initial passenger departure Отобразать начальные точки отправления пассажиров. Show level of city buildings Отображать уровни городских зданий. Show mail service coverage/mail network Отображать зону покрытия станциями почты или почтовые линии. Show passenger coverage/passenger network Отображать зону покрытия станциями пассажиров или пассажирские линии. Show speedlimit of ways Отобразить ограничения скорости. Show the change of waiting at halts Отображать как изменилось ожидание на станциях с прошлого месяца. Show the owenership of infrastructure Отобразить владельцев инфраструктуры. Show transported freight/freight network Отображать транспортируемые грузы или грузовые линии. Show usage of network Отобразить загруженность линий. Shows a listing with all industries on the map. Отобразить список фабрик, расположенных на карте. Sum of departure/arrivals at halts Отображать сколько происходит отправлений/прибытий из/на станциях. #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s теперь\nпредлагает поездки на автобусе\nиз %s\n до \n%s\nв (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s теперь\nперевозит рабочих из\n%s на\n%s\nв (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nзапустил %i грузовиков между\n%s в (%i,%i)\nи %s\nв (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\nоткрыл новую ж/д\nсообщением %s\nв (%i,%i) -\n%s\nв (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Компания %s\nуже обслуживает линию между\n%sи %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Компания \n%s\nуже обслуживает паромную линию между\n%s и %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Компания %s\n уже открыла автобусный маршрут между\n%s и %s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Редактор карты LISTTOOLS Списки MAGLEVTOOLS Маглев MONORAILTOOLS Монорельс NARROWGAUGETOOLS Узкоколейная железная дорога RAILTOOLS Железная дорога ROADTOOLS Автодорога SHIPTOOLS Судоходство SLOPETOOLS Редактор ландшафта SPECIALTOOLS Особые постройки TRAMTOOLS Трамваи/легкорельсовые дороги #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s построил новый головной офис (%i,%i) Factory chain extended\nfor %s near\n%s built with\n%i factories. Рост экономики:\n%s около %s расширяет цепочку поставок. \n%i основано новое предприятие. Net wealth less than 10%% of starting capital! Чистых активов меньше 10%% от стартового капитала! Это может привести к банкротству. New %s now available:\n%s\n Новый %s теперь доступен:\n%s\n New factory chain\nfor %s near\n%s built with\n%i factories. Рост промышленности: новая производственная цепочка\n для %s вблизи %s построена с\n%i предприятий. New vehicle now available:\n%s\n \n Теперь доступен новый\nтранспорт:\n\n\n -- %s --\n\n Now %u clients connected. Сейчас подключено клиентов: %u. overtaking обгон Remove vehicle from map. Use with care! Убрать т/с с карты. Используйте осторожно! Screenshot\ngespeichert.\n Скриншот\nсохранён.\n Sends the convoi to the last depot it departed from! Послать т/с в последнее посещённое депо. Server preparing game ... Сервер сохраняет игру... Trees Деревья With a big festival\n%s built\na new monument.\n%i citicens rejoiced. В честь открытия нового монумента\nв %s организованы большие празднования.\n%i горожан отмечают это событие. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Фильтр линий #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%.2f$/km %.2f$/m) (%.2f$/км %.2f$/месяц) (%.2f$/km) (%.2f$/км) (%i)- (%i) (in depot) (в депо) [0] south-facing [0] на юг [1] east-facing [1] на восток [2] north-facing [2] на север [3] west-facing [3] на запад [4] southeast corner [4] на юго-восток [5] northeast corner [5] на северо-восток [6] northwest corner [6] на северо-запад [7] southwest corner [7] на юго-запад \nBauzeit bis до \nBauzeit von \nДоступно в период:\n \nCan't open heightfield file.\n \nНе могу открыть карту высот.\n \ndirection: \nнаправления: \nelektrified \nэлектрифицировано \nHeightfield has wrong image type.\n \nКарта высот имеет\nнекорректный формат\nизображения.\n \nis reserved by: \nзанято поездом \nminimum speed: \nминимальная скорость: \nnot elektrified \nне электрифицировано\n \nRibi (masked) \nнаправления\n (перекрыты): \nRibi (unmasked) \nНаправления\n(открыты): \nSet phases: \nУстановка времени фаз\nСевер-Юг Восток-Запад: \nsingle way \nодностороннее движение \nway1 reserved by \nПуть 1 заразервирован \nway2 reserved by \nПуть 2 зарезервирован \nwith sign/signal\n \nсо знаком/сигналом\n %d buildings\n %d зданий\n %d convois %d т/с %d Einzelfahrzeuge im Depot %d т/с хранится здесь %i car(s), %i машин, %i km/h (max. %ikm/h) %i км/ч (макс. %i км/ч) %i years %i months old. Возраст:\n%i лет и %i месяцев. %s at (%i,%i) now public stop. %s в (%i,%i) теперь общедоступна. %s building %s %s здание %s %s %s %s city %d %s Город %s %d %s %s factory %s %s Предприятие %s %s %s %s has entered a depot. %s прибыл в депо. %s land %d %s %s область %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. В %s была построена новая мэрия, когда население достигло %i. %s\nis crowded. %s переполнена. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nмакс. скор.: %3$i км/ч\n\nтек. скор.: %2$i км/ч.\n\n %s\nwas liquidated. Компания %s обанкротилась и ликвидирована.\nВсе операции остановлены,\nвсе активы проданы. %u Client(s)\n %u клиент(-ов)\n %u Player (%u locked)\n %u игроков (%u заблокировано)\n <очистить расписание> <создать линию> %s - %s
\n [%s]: %s
\n

Error

Ошибка: Дублирование объектов в папке пак-сета

В

Warning

addons for

Предупреждение: Дублирование объектов в папке дополнений

Дополнения для <индивидуальное расписание> <не назначен на линию> <нет расписания> <новая линия> 1 convoi 1 т/с 1 Einzelfahrzeug im Depot 1 т/с хранится здесь 100 km/h = %i tiles/month 100 км/ч = %i клеток/месяц 1LIGHT_CHOOSE Яркость: 1WORLD_CHOOSE Установки новой игры: 2LIGHT_CHOOSE Цвета: 2WORLD_CHOOSE Номер карты: 3LIGHT_CHOOSE Скорость прокрутки: 4LIGHT_CHOOSE Обратная прокрутка 5LIGHT_CHOOSE Пешеходы на остановках 5WORLD_CHOOSE Количество городов 6LIGHT_CHOOSE Пешеходы в городах 6WORLD_CHOOSE Плотность движения: 8WORLD_CHOOSE Смена дня и ночи A bridge must end on a way! Мост должен заканчиваться на пути! A bridge must start on a way! Мост должен начинаться в конце существующего пути. Abfrage Режим просмотра Abnehmer Потребитель About Про About scenario Описание Abriss Разрушить/Удалить Absenken Опустить землю Abspanntransformator Трансформатор Accelerate time Ускорить время Act. load: %u MW\n Факт. нагр.: %u МВ\n Active player only Только активный игрок Add forest Посадить лес Add random citycar Добавить городское т/с add server Добавить сервер Add Stop Добавить Add stops for backward travel Добавить остановки для обратного пути air рулежная дорожка/взлетная полоса Aircraft Авиалиния aircraft_tab Грузовые самолёты airplane самолёт Airport Аэропорт AIRTOOLS Воздушный транспорт All Все all convoi tooltips Все значки над т/с Allow city growth Разрешить рост города Allow player change Разрешить смену игрока allowed climates:\n Разрешённые климаты:\n Alters a schedule. Добавить/удалить остановки в/из расписания. and arranged by и аранжировка Angenommene Waren Товары, необходимые близлежащим предприятиям anhaengen Добавить Anhaenger_tab Прицепы Anheben Поднять землю Appends stops at the end of the schedule Добавить остановки в конце расписания. Apply Line Назначить линию Apr. Апр. April Апрель Arbeiter aus: Рабочие живут в: Arrivals from\n Отправился из:\n Arrived Доставлено Assets Фонды Aufloesen Разобрать Aufspanntransformator Трансформатор Aug. Авг. August Август auto авто Autohalt muss auf\nStrasse liegen!\n Остановка должна размещаться на дороге. Available Доступно Available at custom date Доступно в выбранную дату Bahndepot Железнодорожное депо Bankrott:\n\nDu bist bankrott.\n Банкротство:\n\nВы банкрот.\n battery Батарея Baum Дерево baum builder Посадить дерево Baustelle Место строительства Bauzeit время строительства Beenden Выйти Beginner mode Режим обучения Besonderes Gebaeude Достопримечательность BF станция bio гужевой Blockstrecke ist\nbelegt\n Ж/д блок уже занят\nдругим поездом!\n Boden Земля Bonus Multiplier: %i%% Множитель бонуса: %i%% Bonus Speed: %i km/h Бонусная скорость: %i км/ч Bonusspeed: %i km/h Макс. возможная скорость: %i км/ч Boost (%%) Ускорение (%%) Borderless (disabled on fullscreen) Без рамки (отключено в полноэкранном режиме) bridge is too high for its type! Слишком высоко для моста данного типа! Bridge is too long for this type!\n Слишком большой пролёт\nдля данного типа мостов!\n Bruecke Мост Bruecke muss an\neinfachem\nHang beginnen!\n Мосты должны\nначинаться на\nпрямом склоне!\n Brueckenboden Мост Build air depot Самолётный ангар build choosesignals Построить семафор выбора платформы(choosesignal) Build city market Построить новый торговый центр в ближайшем городе Build drain Трансформаторная подстанция build HQ Построить штаб Build land consumer Построить новую электростанцию Build maglev depot Построить депо для маглева Build monorail depot Построить депо монорельса Build narrowgauge depot Пстроить депо для узкоколейки Build powerline Строить ЛЭП Build presignals Построить двусторонний семафор Build road depot Построить гараж Build ship depot Построить корабельный док Build signals Построить семафор Build train depot Железнодорожное депо Build tram depot Трамвайное депо Build truck depot Гараж Building costs estimates Расчётная стоимость строительства Buildings Здания Built artifical slopes Построить искусственные склоны Built random attraction Построить случайную достопримечательность Bus_tab Автобусы Can be overgrown Может быть заросшим Can only move from halt to halt or waypoint to waypoint. Может перемещаться от\n остановки к остановке, или\n или точки до точки. Cancel Отмена Cannot connect to offline server! Не возможно соединиться с отключенным сервером! Capacity Ёмкость Capacity: Вместимость: Capacity: %.0f MW Вместимость: %.0f МВт\n Capacity: %d%s %s\n Вместимость: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Вместимость: %s\nЗагружено: %d (%d%%) Cars are not available yet! Автомобили еще недоступны! cars.\nstate машин\n Cash На счету Change label style Изменить стиль надписи Change player Сменить игрока Chart График Chat_msg Чат Choose direction Выберите направление Choose operation executed on clicking stored/new vehicles Выбрать операцию, выполняемую при щелчке по новому/сохранённому т/с chooses a random map Выбрать случайную карту citicens Население City attraction Достопр. в городах City industries Промышл. в городах City list Список городов City size Размер city_road Городская дорога citybuilding builder Построить городские здания CityLimit Границы cl_title Список т/с cl_txt_sort Сортировка: clamp хомут Clear block reservation Отобразить/скрыть зарезервированные пути clf_chk_aircrafts Самолёты clf_chk_cars Автотранспорт clf_chk_indepot в депо clf_chk_maglev Маглев clf_chk_monorail Монорельс clf_chk_name_filter Фильтр по имени: clf_chk_narrowgauge Узкоколейка clf_chk_noincome нет дохода clf_chk_noline нет линии clf_chk_noroute нет пути clf_chk_noschedule нет расписания clf_chk_obsolete Устарело clf_chk_ships Корабли clf_chk_spezial_filter Спец. фильтр: clf_chk_stucked закреплено clf_chk_trains Поезда clf_chk_trams Трамваи clf_chk_type_filter Фильтр по типу: clf_chk_waren Фильтр по товару: clf_title Фильтр списка т/с Climate Климат Climate Control Настройки ландшафта Climates Климат closed закрыто. COLOR_CHOOSE\n Пожалуйста,\nвыберите цвет\nиз таблицы:\n Company bankrupt Банкротство компании Company_msg Конкуренты Comparing pak files ... Сравнение графических пакетов ... Composed by Композитор Configure AI Настроить ИИ Configure AI setttings Настроить ИИ Congratulation\nScenario was complete in\n%i months %i years. Поздравляем!\nВы прошли сценарий за\n%i месяцев и %i лет! Connected stops Соединённые остановки Connected with server Соединение с сервером Connections Соединения Constructed by Нарисовано: Constructed by %s Нарисовано: %s\n---------------------------------------- construction speed Скорость строительства Construction_Btn Постройки Consumed Использовано contains the following doubled objects:

содержит следующие объекты более одного раза:

convoi %d of %d т/с %d из %d convoi error tooltips Значки ошибок над т/с Convoi has been sent\nto the nearest depot\nof appropriate type.\n Т/с было послано\nв ближайшее депо\nподходящего типа. Convoi is sold when all wagons are empty. Т/с будет продано как только будет полностью освобождено convoi mouseover tooltips Значки над т/с у курсора convoi passed last\nmonth %i\n За прошлый месяц\nпрошло %i т/с\n Convois Трансп. ед. Convois: %d\nProfit: %s Т/с: %d\nПрибыль: %s Convoys Трансп. ед. Copy Convoi Скопировать т/с Copy the selected convoi and its schedule or line Копировать выбранное т/с и его расписание или линию\n\n Copy to clipboard Скопировать в буфер обмена Cost Стоимость cost for removal Стоимость ликвидации Cost per unit Стоимость за единицу Cost: %8s (%.2f$/km %.2f$/m)\n Стоимость: %s (%.2f$/км %.2f$/месяц)\n Cost: %8s (%.2f$/km)\n Стоимость: %8s (%.2f$/км)\n Costs Затраты Create a new line based on this schedule Создать новую линию, основываясь на этом расписании curiosity builder Построить достопримечательность curlist_title Список достопримечательностей CURRENCY ₽ CURRENCY_LEFT Символ валюты Currently playing: Сейчас звучит: Customers live in: Посетители живут в: Date format Формат даты deactivated in online mode Не активен в сети Dec. Дек. Deccelerate time Замедлить время December Декабрь decrease underground view level уменьшить уровень подземного режима Del Stop Удалить Delete Line Удалить линию Delete the current stop Удалить данную остановку Delete the selected line (if without associated convois). Удалить выбранную линию (если она не обслуживается ни одним т/с) Delete this file. Удалить этот файл. Delivered Отправлено Demand Потребность Demand: %.0f MW Нужно: %.0f МВт\n Denkmal Монумент Departed Отправлено Departs at Отправляется в (dd:hh:mm) Departure board Время отправления Departures per month Отправления за месяц Departures to\n Отправление в\n Depots Депо Der Tunnel ist nicht frei!\n Тоннель не пуст!\n\n\n Destination Прибытие Destroying map ... Очистка карты... Details Детали Die Bruecke ist nicht frei!\n Мост несвободен!\n diesel дизельный привод directmail Почтовая рассылка Direkt erreichbare Haltestellen Прямые маршруты отсюда: disable midi Выключить музыку Display settings Настройка графики Distance Расстояние distributing cities распределение городов distributing factories распределение фабрик Do not show Не показывать Dock Порт Dock must be built on single slope! Порт должен строиться на ровном склоне. Downloading Загрузка dp_title Список депо Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Для выплаты долга у Вас осталось %d месяцев. Durchsatz Макс. Economy Экономика и города Eigenbesitz\n Муниципальная\nсобственность\n Ein %s\npasst hier nicht.\n "%s"\nне поместится здесь!\n Einstellungen Настройки Einstellungen aendern Настройки игры electric электро Electricity Электричество Electricity producer\n\n Производитель электричества\n\n Electrics_tab Т/c на электротяге Electrify track Электрифицировать путь enlarge map Расширить карту enter a value between %i and %i Введите значение между %i и %i Enter address Введите адрес Enter Password Ввод пароля Error Ошибка Erzeuge neue Karte.\n Пожалуйста, подождите,\nсоздаётся новая карта.\n\n (Это может занять\n несколько минут\n для больших карт)\n Es wird bereits\nein Fahrplan\neingegeben\n Создается\nрасписание.\nЗакончите его\nперед упорядочиванием!\n Fabrikanschluss Соединенные предприятия Fabrikname назв. фабрики Factories Фабрики factory details Информация о предприятии factorybuilder Построить предприятие Fahrplan Расписание Fahrtziel Прибытие: Fahrzeuge koennen so nicht entfernt werden Транспортное средство\nне может быть убрано\nтаким образом!\n Fahrzeuge: Т/с: Farbe Цвет игрока Fast forward Ускорить время Feb. Фев. February Февраль Ferry_tab Паромы Fertig Готово Filename Игра: Files from: Файлы из: Filter: Фильтр: Finances of %s Финансы %s Finanzen Финансы find mismatch Найти несоответствия fl_title Список фабрик Flug_tab Пассажирские самолеты follow me Следовать за т/с Follow the convoi on the map. Следовать за т/с на карте. font size размер шрифта Forest Лес Found new city Основать новый город FPS: Кадров в сек: Fracht Грузы Frame time: Время кадра: Free Capacity Простой freeplay mode Играть без банкротства Freight Груз Friction: Текущий фактор трения: fuel_cell элемент питания Full load Ожидание минимальной загрузки Fullscreen (changed after restart) Полноэкранный (изменяется после перезапуска) Fundament Фундамент Fussgaenger Пешеход Game info Информация о игре GAME PAUSED ИГРА ПРИОСТАНОВЛЕНА Game_msg Общее Gear: Оснастка: Gebaeude Строение General Общие Generated Сгенерировано Generation: %.0f MW Производство: %.0f МВ\n Gewicht Вес Gewinn Доход: Give the selected vehicle(s) an individual schedule Назначить выбранным т/с индивидуальное расписание gl_btn_sort_catg по категории gl_title Список грузов go home В депо Goods Грузы Goods AI ИИ грузы Goods list Список товаров Gross Profit Валовая прибыль Groundobj Наземный объект Grow city Увеличить город Growth Рост города GUI settings Графика H остановка Hangar Ангар Happy Счастливы Haus kaufen Купить здание Helligk. Графика Help Справка по игре Help text not found Текст справки не найден hide all building Скрыть все здания hide city building Скрыть городские здания Hide labels Скрывать надписи hide objects under cursor Скрыть объекты вокруг курсора радиусом: hide station names скрыть названия остановок hide transparent Прозрачность вместо скрытия hide trees Скрыть деревья Hier warten/lagern: Ожидание грузов и пассажиров Higher transport fees, crossconnect all factories Высшие транспортные сборы, выключенные вовремя. Highlite schedule Подсвечивать маршрут hl_title Список станций hl_txt_filter Фильтр: hl_txt_sort Сортировать по: hlf_chk_airport Аэропорты hlf_chk_anleger Док hlf_chk_bahnhof Ж/д станция hlf_chk_bushalt Автобусная остановка hlf_chk_frachthof Погрузочная платформа hlf_chk_keine_verb нет связи hlf_chk_maglevstop Остановка маглева hlf_chk_monorailstop Остановка монорельса hlf_chk_name_filter Фильтр по имени: hlf_chk_narrowgaugestop Остановка узкоколейки hlf_chk_overflow Сверх вместимости hlf_chk_spezial_filter Спец. фильтр: hlf_chk_tramstop Трамвайная остановка hlf_chk_type_filter Типы фильтров: hlf_chk_waren_abgabe Производство: hlf_chk_waren_annahme Требуются товары: hlf_title Фильтр списка станций Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Исходное депо не найдено!\nВы должны послать т/с\nв депо вручную. Homeless Бездомные hydrogene водородный привод Icon size: Размер иконок: Idle: Простой: ignore climates Игнорировать климат In the industry legend show only currently existing factories Во вкладке "Производство" отобразятся только текущие на карте предприятия. In Transit В пути Increase Industry density Увеличить плотность промышленности increase underground view level увеличить уровень в подземном режиме industrial building Промышленное строение Industry overlay Названия предприятий Infinite mouse scrolling Бесконечная прокрутка мышью Init map ... Подготовка карты ... Input Потребл. Ins Stop Вставить Insert stop before the current stop Вставить остановку перед текущей Insufficient funds! Недостаточно средств! Intercity road len: Длина междугородних дорог Intro. date Начало произв. Intro. date: Начало экспл.: invalid неопределенный. Invalid coordinate Неверные координаты isometric map Изометрический вид карты January Январь join game Интернет July Июль Jump to Перейти к June Июнь Kann Spielstand\nnicht laden.\n Не могу загрузить\nсохранённую игру!\n Kann Spielstand\nnicht speichern.\n Не могу открыть для записи\nфайл с сохранением игры!\n Kein Besitzer\n Нет владельца\n keine нет Keine Einzelfahrzeuge im Depot Т/с не хранятся здесь Keyboard_Help\n Горячие клавиши\n koord координаты Kreuzung Перекрёсток labellist_title Список меток Lade Relief Загрузить рельеф Laden Загрузить Land attraction Загородная достопримечательность Land industries Произв. цепочки: LANG_CHOOSE\n Пожалуйста, выберите\nВаш язык:\n LARGE_NUMBER_STRING M LARGE_NUMBER_VALUE 1e6 Last Month В прош. месяце Last used tools Последние использованные инструменты Last Year Прошл. год: Leaving depot! Покидает депо! leer пустой Left button drags minimap too Левая кнопка мыши перетаскивает мини-карту Legend обознач.\n\n Leistung Мощность Leistung: %d kW Мощность: %d кВт Leitung ЛЭП length: %d длина: %d letzen Monat: diesen Monat: Пред. месяц: этот месяц: Line Линия Line Management Управление линиями Lineless convoys serving this stop Т/с без линий, обслуживающие эту остановку: Lines serving this stop Линии, обслуживающие эту остановку: List of industries on the map Список предприятий на карте LKW_tab Грузовики Load game Загрузить load height data from file Загрузить данные рельефа из файла Load scenario Сценарий loaded загружено loaded passenger/freight Сортировка пассажиров/грузов: Loading (%i->%i%%)! Погрузка (%i->%i%%) Loading addon paks ... Загрузка дополнений паков ... Loading map ... Загрузка карты ... Loading paks ... Загрузка графических пакетов ... Loading skins ... Загрузка скинов ... Lock game Запретить изменение игрока (нужно подтверждение) Lokomotive_tab Локомотивы m3 м³ Maglev Маглев maglev vehicle поезд маглев maglev_track Путь для маглева Maglevdepot Депо для маглева Mail Demand %d\n Спрос почты %d\n Mailbox Центр сообщений Mailbox Options Настройки оповещения Maintenance Содержание make stop public (or join with public stop next) costs %i per tile and level создать публичную остановку (или присоединить к соседней публичной остановке), стоимостью %i$ за клетку и уровень Make way or stop public (will join with neighbours), %i times maintainance Сделать путь или остановку публичной (объединяет с соседними) %i обслуживание Manual (Human) Ручной (Человек) Manufactured: Построен: Map roughness Неровность рельефа: map view Карта map zoom Масштаб March Март Margin (%%) Маржа Marker Метка max макс. Max Boost (%%) Макс. ускорение (%%) Max income: Макс. доход: Max. speed Макс. скор. Max. speed: Макс. скорость: Maximum 254 stops\nin a schedule!\n Максимум 254 остановки\nв расписании!\n maximum length of rivers Макс. длина рек Maximum tile height difference reached. Разница высоты между двумя клетками не может превышать 2 уровня Maxspeed Макс. скорость May Май Median Citizen per town Ср. кол-во жителей на город Meldung Сообщение Menge количество MessageOptionsText \nНовый год\n\nНовости ИИ\n\nГородские новости\n\nБез маршрута\n\nНовые производства\n\nболтать\n\nНовые т/с\n\nЗаполение станций\n\nПроблемы\n\nTraffic jam\n\nScenario min мин. minimum length of rivers Мин. длина рек Minimum load Минимальная загрузка Missing pakfiles Объекты отсутствуют! Modify the selected line Изменить выбранную линию Monate alt месяцев Monorail Монорельс monorail vehicle Монорельсовое т/с monorail_track Монорельс Monorailboden Основание монорельса Monoraildepot Депо монорельса month wait time Ждать, времени: Months Месяцы Monument Монумент Monuments Монументы Mountain height Высота гор: Movingobj перемещение объекта Music playing disabled/not available Музыка отключена или недоступна Music volume: Громкость музыки: mute sound Выключить звук Name Название Narrowgauge Узкоколейка Narrowgauge are not available yet! Узкоколейка еще недоступна! narrowgauge vehicle узкоколейные т/с narrowgauge_track Узкоколейка Narrowgaugedepot Депо для узкоколейки Net ID: %p ID сети: %p\n Net Wealth Чистые активы Net wealth near zero Чистые активы близки к нулю Neue Karte Новая игра Neue Welt Создание новой игры new convoi Новое т/с New Line Добавить New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Создана новая линия!\nТеперь Вы можете назначить линию,\nвыбирая её из представленного\nниже списка. New Vehicles Новые т/с Nickname: Имя: no buildings hidden Не скрывать здания no convois Нет т/с No goods are loaded onto this convoi. Грузы не будут грузиться на это т/с no goods waiting Нет ожидающих грузов no load Не грузить No Route Нет пути No stop here! Инструмент используется только на остановках. No suitable ground! Нет подходящей земли! No terminal station here! Здесь нет терминала! no timeline без линии времени no tree Без деревьев No. of Factories Кол-во фабрик и магазинов Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Установите расписание \nт/с перед отправкой!\n none нет nord Север nordost Северо-восток nordwest Северо-запад Not allowed to make publicly owned ways! Запрещено делать общедоступными собственные пути! Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Расписание нельзя \nпоменять во время \nпоиска пути Not enough fields would remain. Вокруг фермы осталось слишком мало полей November Ноябрь now сейчас Now active as %s.\n Теперь играем за %s.\n nowhere нигде Number of rivers Количество рек Object Объект Odometer: %s km Одометр: %s км Ok OK Oktober Октябрь On loan since %i month(s) Кредит в течении %i месяца(-ев) On mouseover При наведении мыши On this map, you are not\nallowed to change player!\n Эта игра заблокирована!\nСмена игрока невозможна!\n\n\n Only city chains Только городские цепочки Only first %d differing paks reported. There are probably more. Указаны только первые %d различающиеся наборы. Вероятно, их несколько больше. Only full Unicode fonts Шрифты только с полным Юникодом Only land chains Только загородные цепочки Only one transformer per factory! Только один трансформатор на фабрику! Only show goods which are currently handled by factories Отобразить грузы, обработанные фабриками open открыть. Operation Эксплуатация Ops Profit Оп. прибыль Optionen Настройки Or enter a server manually: Или введите сервер вручную: Origin Исходно ost Восток Output Произв. Ownership Владелец Pak which may cause severe errors: Следующие объекты (наборы) отсутствуют (может привести к разрыву транспортных связей): Pak which may cause visual errors: Следующие объекты (наборы) были заменены на схожие: Pak(s) different: Различия пакета(-ов): Pak(s) missing on client: Набор(-ы), отсутствующий в клиенте: Pak(s) not on server: Набор(-ы), отсутствующий на сервере: Pakset differences Различия между наборами paletten ящ. Pas_tab Пассажирские поезда Passagiere Пассажиров Passagierrate Наличие пассажиров Passagierziele Назначение пасс./почты Passenger AI ИИ пассажиры Passenger Demand %d\n Спрос на пассажиров %d\n Passengers %d %c, %d %c, %d no route Пассажиры %d %c, %d %c, %d без маршрута Passengers %d %s, %d %s, %d no route Пассажиры %d %s, %d %s, %d без маршрута Password Пароль: Pause Поставить игру на паузу Pax <%i> Mail <%i> Пассажиры <%i> Почта <%i> PaxDest Направления Percent Electricity Произв. электричества (%% от спроса): Planes are not available yet! Авиация ещё недоступна! Plant tree Посадить дерево player Игрок player -1 Игрок 1 player 0 Государство player 1 Напик 128 АС player 10 Игрок 10 player 11 Игрок 11 player 12 Игрок 12 player 13 Игрок 13 player 2 Трикки Транспорт player 3 Мейер Мувинг К° player 4 ВМ Экспедиция player 5 ООО "Аш-Транс" player 6 ПСК и Ко КГ player 7 Игрок 7 player 8 Игрок 8 player 9 Игрок 9 Please choose vehicles first\n Сначала выберите т/с\n Post Почта Postrate Наличие почты Power Мощность Power (MW) Энергия (МВт) Power: Мощность: Power: %4d kW\n Энергия: %4d КВт\n Powerlines ЛЭП Price Цена Problems_msg Проблемы Produced Произведено Produces: %.1f units/minute Производит: %.1f ед./мин. Production of %s has been stopped:\n%s\n Производство %s было прекращено:\n%s\n Production/Boost Производство/Рост Produktion Производство Profit Прибыль promote to line Новая линия q1 Весна q2 Лето q3 Осень q4 Зима Query server Запрос сервера rail car Рельсомобиль random случайно Random age Случайный возраст Random map Случайная карта Rathaus Ратуша Rating Рейтинг ratio_pax Соотношение Relevant актуальные Reliefkarte Карта Remove Удалить метку remove airstrips Удалить полосу remove channels Удалить каналы remove interm. signals Удалить промежуточные remove maglev tracks Удалить пути маглева remove monorails Удалить пути монорельса remove narrowgauge tracks Удалить пути узкоколейки remove powerlines Удалить ЛЭП remove roads Удалить дорогу remove tracks Удалить ж/д пути Remove wayobj %s Удалить объект %s replace other signals заменить другие семафоры replace stop Заменить остановку request closing закрытие запроса residential house Жилая постройка Restore natural slope Восстановить естественный склон Restwert: \nСтоимость продажи: Retire date Дата устарев. Retire. date: Конец экспл.: return ticket Путь назад Revenue Выручка Revision: Версия: road дорога road vehicle Автомобиль Roadsign Дорожный знак Rotate map Повернуть карту Rotation Вращение Routing Прокладка маршрута sack мешков sail ветер Save Сохранить игру Saving map ... Сохранение карты ... Scenario complete: %i%% Сценарий завершен на %i%% Scenario Debug Ошибки Scenario Error Log Ошибки кода Scenario Goal Цель Scenario Info Информация Scenario information Описание Scenario Result Результаты Scenario Rules Правила Schedule changing! Изменение расписания! Schienentunnel Построить ж/д туннель Schiff_tab Корабли Schiffdepot Корабельное депо Schleppkahn_tab Баржи Screen scale: Масштаб экрана: Screenshot Сделать скриншот Scroll threshold Порог прокрутки Search: Поиск: Seasons Время года Sehenswuerdigkeit Достопримечательность Select a server to join: Выберите сервер для подключения: Select a theme for display Выберите тему игрового экрана Select display font Выбор шрифта Sell the selected vehicle(s) Продать выбранные т/с sended Отправлено SEP_FRACTION , SEP_THOUSAND , SEP_THOUSAND_EXPONENT 3 September Сентябрь Served by me Обслуживаются мной Server did not respond! Сервер не отвечает Serves Line: Обсл. линию: Service Обслуживание set signal spacing Авторасстановка семафоров Setting Настройки Ship Корабль shops and stores Магазины и офисы Show all Показать всё show all building Показать все здания Show also vehicles no longer in production. Показать также т/с, которые больше не производятся. Show also vehicles that do not match for current action. Показывать также т/с, которые не могут быть использованы для выбранного действия Show always Показывать всегда Show even servers with wrong version or pakset Также отображать сервера с иной версией или наборами. show grid Отобразить сетку Show industry Производство Show legend Легенда Show map scale Шкала высот Show mismatched Показать отличия Show networks Показать сети Show obsolete Показать устаревшие\n\n Show offline Показать отключенные Show only used Показать текущие предприятия Show schedules Показать расписания Show servers that are offline Показать сервера, отключенные от сети, в данный момент. Show servers where game version or pakset does not match your client Показать сервера, версия которых или состав наборов отличатся от вашего клиента. show station coverage Показать покрытие станций show station names Названия станций/остановок show waiting bars Индикатор ожидающих show/hide block reservations Вкл/выкл показ резервирования блоков Show/hide estimated arrival times Вкл/выкл ожидаемое время прибытия show/hide object owner Вкл/выкл показ владельца объекта Show/hide statistics Показать/скрыть статистику Shows buttons about networks overlay. Отображает кнопки о наложении сетей Shows buttons on special topics. Отобразить кнопки условных обозаначений. Shows consumer/suppliers for factories Показать потребителей/поставщиков для предприятий Shows the color code for several selections. Отобразить цветовую шкалу высот. Shows the currently selected schedule Показать текущее расписание Shrink city Уменьшить город shuffle midis Играть в случайном порядке Signal Сигнал signal spacing Расстояние между сигналами Sim: Сим-циклы: Similar view as the main window Отображать карту так же, как отображается игровое окно. Single toolbar only Одна панель инструментов Size (%d MB): Размер (%d Мб): sliced underground mode Z-уровень slot empty Пустой слот Smart hide objects Частичное сокрытие Sort by Сортировать пассажиров/грузы по: Sort by: Сорт. по: Sort waiting list by Сортировка списка ожидания: Sound Звук Sound settings Настройка звука Sound volume: Громкость звука: special freight особый груз Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Ускорение:\nдорога %i км/ч, ж/д %i км/ч\nкорабли %i км/ч, самолёты %i км/ч. Speedlimit Огр. скорости Speichern Сохранить Spieler Игрок Spieler(mz) Игроки Spielerliste Список игроков Spielstand wurde\ngeladen!\n \nИгра успешно загружена!\n Spielstand wurde\ngespeichert!\n \nИгра успешно сохранена!\n Sprache Язык Sprachen Языки Stadtinformation Городская статистика Start Пуск Start the selected vehicle(s) Отправить на линию выбранные т/с Starte Spiel Начать игру station labels Станции Station tiles: Секции cтанции: Station_msg Станции Status Статус steam паровая тяга Step timeline one year Перевести время на год вперёд Stops Остановки Storage Склад Storage capacity Вместимость склада Strassendepot Гараж Strassentunnel Автомобильный туннель street car Частное авто sued Юг suedost Юго-восток suedwest Юго-запад Summer snowline Летняя линия снега Supplied: %.0f %% Доставлено: %.0f %% Suppliers Поставщики Tage alt дней Theme selector Выбор темы There are still vehicles\nstored in this depot!\n Остались транспортные средства,\nразмещённые в депо!\n This Month В этом месяце This Year В этом году: Tile not empty. Участок не пуст.\nПожалуйста, удалите всё\nперед постройкой\n искусственного уклона. timeline Использовать линию времени tl_title Список городов To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Чтобы привлечь больше\nтуристов в %s, \nбыл построен %s \nпо достижении %i налогоплательщиков. To heavy traffic\nresults in traffic jam.\n Слишком плотное движение\nвызвало пробку на дороге.\n Toggle day/night view Вкл/выкл цикл дня и ночи Toggle vehicle tooltips Вкл/выкл подсказки по т/с tonnen т Toolbar position: Положение иконок: Total inhabitants: Общее население: Tourist attractions Достопримечательности Tourists Достоприм. Town_msg Новые назначения Town: %s\n Город %s\n Towns Города track путь Tracks Ж/д пути Traffic Движение traffic settings Трафик Train Поезд Trains are not available yet! Поезда ещё недоступны! Tram Трамвай tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. трамвай %i км/ч, монорельс %i км/ч\nмаглев %i км/ч, узкоколейка %i км/ч. tram_track трамвайный путь Tramdepot Трамвайное депо Trams are not available yet! Трамваи ещё недоступны! Transferring game ... Передача игры... Transformer only next to factory! Трансформаторная подстанция должна быть около предприятия Translation Перевод transparencies Прозрачность transparent station coverage Прозрачная зона покрытия Transported Перевезено Trees disabled! Деревья запрещены! TrolleyBus_tab троллейбусы Truck Грузовик tt_Other Другое Tunnel muss an\neinfachem\nHang beginnen!\n Туннели должны начинаться\nна прямом склоне!\n Tunnel must start on single way! Туннели должны начинаться на одиночном пути! Tunnelboden Туннель underground mode Подземный режим UNDO failed! Отмена больше недоступна.\nВы можете отменить создание \nмаршрута только если на нём \nне будет построено сигналов /\nстанций / остановок или ещё \nчего-нибудь. Undo last ways construction Отменить последнюю постройку пути Unemployed Безработные Unhappy Несчастны units/day /месяц, Update Line Изменить upgrade HQ Улучшить офис Usage: %.0f %% Использование: %.0f %% Usage/Output Потребление/производство Use beginner mode Режим "новичок" Use timeline start year Режим течения времени Vehicle %s can't find a route! %s\nне может найти путь! Vehicle %s is stucked! %s в пробке! Vehicle details Подробнее Vehicle Name Название Vehicle Power Мощность Verbrauch Потребление Vergroessere die Karte\n Расширить карту.\n Verkauf Уничтожить verkaufen Продажа Verkehrsteilnehmer Гор. трансп. Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n У Вас образовалась задолженность!\n\nОсталось %d месяц(а,ев)\nчтобы выплататить долг!\n vh_title Список всех транспортных средств via через (детали) via %s\n через %s\n via Menge через (кол-во) voranstellen Вывести Waggon_tab Вагоны waiting ожидает Waiting for clearance! Путь занят! Walked Пешеходы walking Пешеходы Warnings_msg Трафик Wasser Вода Water Канал Water level Уровень воды: water vehicle Водное т/с way %s cannot longer used:\n Дороги типа %s не могут больше использоваться.\n way %s cannot longer used:\n%s\n Дороги типа %s не могут больше использоваться.\n%s\n way %s now available:\n Теперь доступны дороги типа %s.\n Way toll Дорожный налог Ways not connected Дороги не соединены Wegpunkt Точка пути Weight Вес Weight: Масса: Wert Стоимость west Запад Wind direction Направление ветра Winter snowline Зимняя линия снега withdraw Продать Withdraw All Продать все WRONGSAVE \nНесовместимая версия\nсохранения. Не могу загрузить\nэтот файл.\n Year %i has started. Начался %i год. Years Годы Your primary color: Основной цвет: Your secondary color: Вторичный цвет: Zielort пункт назначения zooming in Приблизить камеру zooming out Отдалить камеру Zu nah am Kartenrand Слишком близко\nк краю карты,\nчтобы что-то строить!\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Новый мировой рекорд скорости маглева: %.1f км/ч, %s. New world record for monorails: %.1f km/h by %s. Установлен новый мировой \nрекорд скорости для \nмонорельсовых т/с %.1f км/ч -\n%s New world record for motorcars: %.1f km/h by %s. Установлен новый мировой \nрекорд скорости для \nавтомобилей %.1f км/ч -\n%s New world record for narrowgauges: %.1f km/h by %s. Новый мировой рекорд скорости для узкоколейных т/с: %.1f км/ч, %s. New world record for planes: %.1f km/h by %s. Установлен новый мировой \nрекорд скорости для \nавиации %.1f км/ч -\n%s New world record for railways: %.1f km/h by %s. Установлен новый мировой \nрекорд скорости на железных\nдорогах %.1f км/ч -\n%s New world record for ship: %.1f km/h by %s. Установлен новый мировой \nрекорд скорости для водного\nтранспорта %.1f км/ч -\n%s #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL деревня &1_CITY_SYLL каменск &2_CITY_SYLL водск &3_CITY_SYLL дар &4_CITY_SYLL перекопск &5_CITY_SYLL реченск &6_CITY_SYLL озёрск &7_CITY_SYLL полье &8_CITY_SYLL горск &9_CITY_SYLL лодск &A_CITY_SYLL братск &B_CITY_SYLL по &C_CITY_SYLL закапывать %1_CITY_SYLL Северо %2_CITY_SYLL Мало %3_CITY_SYLL Бело %4_CITY_SYLL Дальне %5_CITY_SYLL Пяти %6_CITY_SYLL Светло %7_CITY_SYLL Нижне %8_CITY_SYLL Верхне %9_CITY_SYLL Ново %A_CITY_SYLL Старо %B_CITY_SYLL Высоко 0center %s центр %s 0extern %s село %s 0suburb %s пригород %s 1center %s %s 1extern %s %s ветка 1suburb %s, %s%s станция 2center %s %s центральная 2extern %s %s ближняя 2suburb %s Поля %s %s 3center %s %s главная 3extern %s %s область 3suburb %s деревня %s %s 4center %s %s внутренняя 4extern %s %s дальняя 4suburb %s деревенский %s 5center %s %s грузовая 5extern %s пересадочная %s 5suburb %s город %s 6center %s Связь %s 6extern %s Биржа %s 6suburb %s окраина %s %s 7center %s город %s 7extern %s простая %s 7suburb %s парк %s 8center %s деловая %s 8extern %s %s запасная %s 8suburb %s пригород %s 9center %s осевая %s 9extern %s объездная %s 9suburb %s промежуточная %s Acenter %s центр %s Bcenter %s центр %s Ccenter %s центр %s Dcenter %s центр %s Ecenter %s центр %s Fcenter %s центр %s Gcenter %s центр %s Hcenter %s центр %s Icenter %s центр %s Jcenter %s центр %s Kcenter %s центр %s Lcenter %s центр %s Mcenter %s центр %s Ncenter %s центр %s Ocenter %s центр %s Pcenter %s центр %s Qcenter %s центр %s Rcenter %s центр %s Scenter %s центр %s Tcenter %s центр %s Ucenter %s центр %s Vcenter %s центр %s Wcenter %s центр %s Xcenter %s центр %s Ycenter %s центр %s Zcenter %s центр %s simutrans-124.5/simutrans/text/ru/000077500000000000000000000000001520675460500172235ustar00rootroot00000000000000simutrans-124.5/simutrans/text/ru/airtools.txt000066400000000000000000000224771520675460500216340ustar00rootroot00000000000000Авиация

Авиация

Меню авиации содержит инструменты для постройки всех элементов сети воздушного транспорта: постройка и удаление рулежных и взлетно-посадочных полос; станций, терминалов, складов и погрузочных платформ; самолетных ангаров и других зданий. При игре в режиме течения времени, возможно появление других возможностей.

Нажмите на иконку самолета на главной панели инструментов, чтобы открыть меню авиации.
Задерживая курсор мышки над какой-либо иконкой, Вы можете видеть название объекта, цену постройки, стоимость обслуживания (в круглых скобках) в месяц, максимально допустимую скорость и максимально возможный вес т/с для передвижения по объекту (если это рулежные или взлетно-посадочные полосы)cost.

Подсказка: Одним из вариантов постройки простого аэропорта:
1) постройте взлетно-посадочную полосу;
2) сбоку пристройте к ней рулежную полосу;
3) на свободном конце рулежной полосы разместите аэровокзал или другой погрузочно-разгрузочный терминал;
4) на свободном конце рулежной полосы разместите ангар для самолетов;
5) достройте другие здания, если это необходимо.

Меню может состоять из следующих кнопок (слева направо):

Рулежные полосы: необходимы для перемещения самолетов по земле между взлетно-посадочными полосами, анагарами и погрузочно-разгрузочными терминалами. Они строятся на плоской земле в главном игровом экране.
ВАЖНО: Если рулежную полосу присоединить к концу взлетно-посадочной, то самолеты не смогут пользоваться взлетно-посадочной полосой.
Для постройки нажмите на соответствующую иконку (курсор мыши должен изменить свой вид) и протяните полосу в нужном месте. В сплывающем окошке будет указана стоимость строительства.
Подсказка: Используйте инструмент Разрушить/Убрать для удаления не нужного участка рулежной полосы. Отмена последнего действия [z] не вернет Вам деньги, потраченные на строительство.

Взлетно-посадочные полосы (ВПП): необходимы для взлета и посадки самолетов. они строятся на плоской земле в главном игровом экране и могут пересекаться.
Для постройки нажмите на соответствующую иконку (курсор мыши должен изменить свой вид) и протяните полосу в нужном месте. В сплывающем окошке будет указана стоимость строительства.
Подсказка: Используйте инструмент Разрушить/Убрать для удаления не нужного участка ВПП. Отмена последнего действия [z] не вернет Вам деньги, потраченные на строительство.

Удалить полосу: удалит участок рулежной или взлетно-посадочной полосы, если на них нет самолета. Используйте это для возмещения стоимости постройки.
Для удаления нажмите на соответствующую иконку (курсор мыши должен изменить свой вид), затем нажмите на удаляемый элемент полосы, протяните для удаления нескольких участков и отпустите кнопку мыши.
Подсказка: Удалив соединение ВПП с аэровокзалом, будьте готовы к тому, что эта полоса не сможет принимать/отправлять самолеты. Используйте инструмент Разрушить/Убрать для удаления одиночного участка полосы.

Самолетное депо (ангар): необходимо для покупки и управления самолетами. Их можно строить только на концах рулежных полос. Ангары имеют ежемесячную стоимость обслуживания.
Для постройки нажмите на соответствующую иконку (курсор мыши должен изменить свой вид) и постройте ангар в нужном месте.
Подсказка: Используйте инструмент Разрушить/Убрать для удаления не нужного ангара. Отмена последнего действия [z] не вернет Вам деньги, потраченные на строительство.

Аэровокзалы и посадочные рукава: нужны для погрузки и разгрузки грузов и пассажиров в/из самолеты.
Если Вы построите здание не рядом с уже существующей станцией, то получите новую станцию.
Их можно строить только на концах рулежных полос. Аэровокзалы и терминалы имеют ежемесячную стоимость обслуживания и область охвата для приема грузов и пассажиров от окружающих фабрик и зданий. Самолет приземлиться только если есть свободный погрузочно-разгрузочный терминал.
Для постройки нажмите на соответствующую иконку (курсор мыши должен изменить свой вид) и постройте здание в нужном месте поверх рулежной полосы.
Подсказка: Используйте инструмент Разрушить/Убрать для удаления не нужного здания. Отмена последнего действия [z] не вернет Вам деньги, потраченные на строительство.

Другие здания: тут перечислены дополнительные здания, которые расширяют функционал уже построенных станций: увеличение емкости складов, площади охвата. Они также могут иметь ежемесячную стоимость обслуживания. На иконках таких зданий часто указывается, на какие грузы оно расчитано (пассажиры, почта или грузы).
Для постройки нажмите на соответствующую иконку (курсор мыши должен изменить свой вид) и постройте здание в нужном месте возле существующего аэровокзала или терминала.
Подсказка: Используйте инструмент Разрушить/Убрать для удаления не нужного здания. Отмена последнего действия [z] не вернет Вам деньги, потраченные на строительство.

simutrans-124.5/simutrans/text/ru/baum_build.txt000066400000000000000000000046021520675460500220710ustar00rootroot00000000000000Посадка деревьев

Посадка деревьев

Данное меню позволяет вам высаживать в игре отдельные деревья. Эта возможность носит чисто декоративный характер, т.к. никако пользы от этого действия нет (кроме эстетической).

В левой части представлен список всех существующих в игре деревьев, которые можно высадить. Две вкладки, Перевод и Объект позволяют выбрать вариант отображения объекта - по названию дерева (Перевод) или по техническому его обозначению (Объект). Выберите какое-либо дерево и вы увидите больше информации о нем. Чтобы посадить дерево достаточно выбрать его в списке и разместить на игровой области экрана.

В правой части перечислены несколько параметров:
Игнорировать климат: возможность рассаживать деревья в любом климате.
Случайный возраст: обычно, свежепосаженное дерево является саженцем с нулевым возрастом. Затем, с течением времени, дерево постепенно растет. Эта настройка позволяет рассаживать деревья случайного возраста - от саженцев до старых больших деревьев.
Вращение: если в графике дерева доступны несколько вариантов его внешнего вида, эта настройка позволит выбрать, какого именно внешнего вида будет размещаемое дерево. Также можно выбрать параметр "случайно", чтобы внешний вид дерева определялся случайным образом.

simutrans-124.5/simutrans/text/ru/citybuilding_build.txt000066400000000000000000000067231520675460500236410ustar00rootroot00000000000000Постройка городских зданий

Постройка городских зданий

Данный инструмент дает возможность строить городские здания в любом месте, где Вы захотите. Данная возможность не является обязательной, а более эстетической.

В левой части представлен список всех доступных городских задний, которые можно постройить. Две вкладки, Перевод и Объект позволяют выбрать вариант отображения объекта - по названию здания (Перевод) или по техническому его обозначению (Объект). Выберите какое-либо здание и вы увидите больше информации о нем. Чтобы построить здание достаточно выбрать его в списке и разместить на игровой области экрана.

В правой части перечислены несколько параметров:
Игнорировать климат: возможность строить здания в любом климате.
Режим течения времени: показать только здания, доступные в данный момент времени.
Показать устаревшие: показать здания, которые были доступны в прошлом.
Жилые здания: показать только здания, в которых проживают люди. Эти здания обычно самопроизвольно строятся, когда город расширяется при увеличении населения.
Магазины и офисы: показать только коммерческие здания. Эти здания предоставляют рабочие места и ведут торговлю (в том числе и с игроками), они обычно строятся при большом количестве безработных жителей в городе.
Промышленные здания: показать только фабрики. Эти здания предоставляют рабочие места и ведут торговлю (в том числе и с игроками). Однако, в этот список не входят большие промышленные фабрики (например, Электростанция или Угольная шахта), которые размещаются за городом и с которыми сотрудничает игрок.
Вращение: если в графике здания доступны несколько вариантов его внешнего вида, эта настройка позволит выбрать, какого именно внешнего вида будет размещаемое здание. Также можно выбрать параметр "случайно", чтобы внешний вид здания определялся случайным образом.

simutrans-124.5/simutrans/text/ru/color.txt000066400000000000000000000030221520675460500210770ustar00rootroot00000000000000Выбор цвета игрока

Выбор цвета игрока

Здесь Вы можете выбрать цвет компании игрока, который будет влиять на цвет названий остановок, заголовки диалоговых окон, цвет текста бегущей строки новостей в нижней части экрана и объекты, принадлежащие игроку.

Выбор цвета игрока можно найти в игровом меню.

Просто выберите, интересующий Вас, цвет в таблице цветов.

Заголовок диалогового окна выбора цвет игрока изменится на выбранный Вами.

По умолчанию, цвет игрока - светло-голубой.

Некоторые объекты не изменят свой цвет - это зависит от, выбранного Вами, набора графики.

Подсказка: Чтобы изменить цвет компьютерного игрока используйте список игроков или кнопку P+ (сменить игрока) на панели спецсредств, чтобы выбрать другого игрока.

simutrans-124.5/simutrans/text/ru/language.txt000066400000000000000000000022741520675460500215540ustar00rootroot00000000000000Языки

Окно « Языки » меняет язык, используемый в игре. Он открывается из окна «Параметры игры» и из окон «Создать новую игру» . Языком по умолчанию для Simutrans является английский. Чтобы играть в игру на другом языке, нажмите кнопку рядом с языком, на котором вы хотите играть. Язык можно изменить в любой момент во время игры. Чтобы изменить язык, выберите «Параметры игры» -> «Язык». Смена языка происходит сразу при создании новой игры и при закрытии окна «Языки» при смене игры в середине игры.

Если перевод недоступен на выбранном языке, он может появиться на английском языке. Чтобы помочь в переводе Simutrans на ваш язык, посетите https://translator.simutrans.com/.

simutrans-124.5/simutrans/text/sk.tab000066400000000000000000000776311520675460500177200ustar00rootroot00000000000000§Slovencina PROP_FONT_FILE prop-latin2.fnt ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: sk Slovencina # # Encoding: UTF-8 # # Font: prop-latin2.fnt # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ Absolute times Zobraz čas ako den:hh:mm mesiaca AMBIENT_SOUND zvuky okolia CASH_SOUND zvuk pokladne cl_btn_filter_disable Vypnutý cl_btn_filter_enable Zapnutý cl_btn_filter_settings Nastavenia cl_btn_sort_asc Vzostupne cl_btn_sort_desc Zostupne cl_btn_sort_id Interné ID cl_btn_sort_income Príjem cl_btn_sort_name Názov cl_btn_sort_type Typ clf_btn_alle všetko clf_btn_invers opak clf_btn_keine nič climate area percentage veľkosť jedného podnebia Continue Game Pokračovať v hre CROSSING_SOUND zvuk výstrah na križovatkách FACTORY_SOUND Zvuky tovární Find matching convois Nájdi konvoje ktoré sa zhodujú gl_btn_sort_bonus podľa príplatku gl_btn_sort_name podľa názvu gl_btn_sort_revenue podľa tržby gl_btn_unsort nezoradené height based Podnebie podľa výšky hl_btn_filter_disable Vypnutý hl_btn_filter_enable Zapnutý hl_btn_filter_settings Nastavenia hl_btn_sort_asc Vzostupne hl_btn_sort_desc Zostupne hl_btn_sort_name Názov hl_btn_sort_type Typ hl_btn_sort_waiting Čakanie hlf_btn_alle všetko hlf_btn_invers opak hlf_btn_keine nič humidities hranice vlhkosti Install Inštalovať Lake Jazero Lake height max. výška jazier Maximize height levels zvýšiť maximálnu výšku moisture land vzrast nad zemou moisture water vzrast nad vodou Networks Schéma siete Num pad keys always move map Numlock je vždy vypnutý Open Sea Oceán Queueing trend čakania rainfall vlhkosť Reselect closes tools vybranie nástroja znovu ho odloží Return to menu Späť do menu Road toll Mýto Scenario Info scenára sea námorné podnebie Single GUI jedno GUI Transfers Prestupy #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic arktické podnebie desert suché podnebie mediterran Subtropické podn. rocky alpská klíma temperate Mierne podnebie tropic Tropické podnebie tundra Polárne podnebie #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Nahranie scenára sa nepodarilo Cannot built depot here! Tu nemôžeš postaviť depo. Cannot create generic line!\nSelect line type by\nusing filter tabs. Linka nebola vygenerovaná !\nVyberte typ linky s použitím\nfiltra. Convoi handles exhausted! Počet konvojov dosiahol maximálnu hodnotu. Das Feld gehoert\neinem anderen Spieler\n Toto políčko patrí inému hráčovi !\n Der Besitzer erlaubt das Entfernen nicht Majiteľ Ti to nedovolí\nzbúrat.\n Flugzeughalt muss auf\nRunway liegen!\n Stojánka musí byť\nna rolovacej ploche. Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Tu nemôžeš postaviť\nletištnú budovu.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Tu nemôžeš postavit'\nžiadny signalizátor !\n Monorailhalt muss auf\nMonorail liegen!\n Stanica visutej železnice\nmusí byť na trati. Monorails are not available yet! Visutá železnica ešte neexistuje. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Stanica úzkokoľajky musí ležať na trati. No suitable way on the ground! Nevhodný úsek pre stavbu stanice alebo zastávky! No through station here! Tu nie je možné postaviť stanicu/zastávku. Not enough money! Nedostatok peňazí\nna stavbu. On narrowgauge track only!\n Len na úzkokoľajke!\n Only public player can lock games! Iba hráč 'Verejnej služby' môže uzamknúť hru. Post muss neben\nHaltestelle\nliegen!\n Pošta musí ležat \nvedľa zastávky !\n Schiffhalt muss im\nWasser liegen!\n Závory musia stát'\nna vode!\n Upgrade must have\na higher level Nová stanica musí byť vyššej úrovne Vehicle %s cannot choose because stop too short! Vozidlo %s nemôže použiť zastávku, pretože je príliš krátka! Zughalt muss auf\nSchiene liegen!\n Stanica musí stáť\nna koľajniciach!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________ Sum of departure/arrivals at halts Súčet množstva prijatého a odbaveného tovaru (cestujícich, pošty) na zastávke. #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s vytvoril\npre turistov novú\nautobusovú linku\nmedzi \n%s a \n%s\n(na %i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s\nteraz prepravuje\nzamestancov z/zo \n%s\ndo závodu \n%s\n(na %i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nteraz používa\n%i nákladné vozidlá\nna prepravu medzi \n%s (na %i,%i) a \n%s (na %i,%i).\n %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\notvára novú\nželezničnú trat' medzi \n%s\n(na %i,%i) a \n%s\n(na %i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Spoločnosť %s\notvára leteckú linku\nmedzi mestami \n%s \na %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s\nvytvoril novú\nautobusovú linku\nmedzi \n%s a %s\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Nástroje pre tvorbu mapy LISTTOOLS Prehľady MAGLEVTOOLS Nástroje pre maglev MONORAILTOOLS Visutá železnica NARROWGAUGETOOLS Nástroje pre úzkokoľajku. RAILTOOLS Železničná doprava ROADTOOLS Cestná doprava SHIPTOOLS Lodná doprava SLOPETOOLS Zošikmenie terénu SPECIALTOOLS Špeciálne nástroje TRAMTOOLS Električková doprava #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ New %s now available:\n%s\n Bola zahájená sériová výroba %s\n'%s'\n New factory chain\nfor %s near\n%s built with\n%i factories. Bolo postavené nové odvetvie\npriemyslu na %s\npri %s,\ncelkovo %i tovární\n New vehicle now available:\n%s\n Je dostupné nové vozidlo:\n>> %s <<\n Now %u clients connected. Práve je připojených %u hráčov. Remove vehicle from map. Use with care! Odstránenie vozidla z mapy. Používať opatrne! Screenshot\ngespeichert.\n Screenshot uložený.\n Sends the convoi to the last depot it departed from! Poslať celý konvoj do nabližšieho depa ! With a big festival\n%s built\na new monument.\n%i citicens rejoiced. S veľkou parádou\nodhalilo mesto %s\nnovú atrakciu.\n%i obyvateľov oslavuje. #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (v depe) \nBauzeit bis do \nBauzeit von \nVýstavba od \ndirection: \nsmery: \nelektrified \nelektrifikované \nis reserved by: \nobsadené vlakom \nminimum speed: \nnajnižšia povolená rýchlosť \nnot elektrified \nneelektrifikované \nRibi (unmasked) \nSmery\n (skryté): \nsingle way \nJednosmerka %d buildings\n %d budov\n %d convois %d súprav %d Einzelfahrzeuge im Depot %d zoznam vozidiel v depe %i km/h (max. %ikm/h) %i km/h (max. %i km/h) %i years %i months old. %i rokov a %i mesiacov. %s at (%i,%i) now public stop. %s je teraz verejnou zastávkou. %s building %s %s %s %s %s %s city %d %s %s mesto %d %s %s has entered a depot. Vozidlo ,,%s'' prišlo do depa. %s land %d %s %s pozemok %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. Mesto %s\npostavilo\nnovú radnicu,\nkeď dosiahlo\n%i obyvateľov. %s\nis crowded. Zastávka ,,%s'' je pret'ažená <žiadna linka> 1 convoi 1 súprava 1 Einzelfahrzeug im Depot 1 vozidlo v depe 1LIGHT_CHOOSE Svetlost': 1WORLD_CHOOSE Nastavenia pre novú mapu: 2LIGHT_CHOOSE Farebnost': 2WORLD_CHOOSE Číslo mapy: 3LIGHT_CHOOSE Rolovanie: 4LIGHT_CHOOSE Inverzné rolovanie 5LIGHT_CHOOSE Ukázat' chodcov 5WORLD_CHOOSE Počet miest: 6LIGHT_CHOOSE Chodci v mestách 6WORLD_CHOOSE Hustota dopravy: 8WORLD_CHOOSE Striedat' noc a deň A bridge must start on a way! Mosty musia\nzačínať na\n existujúcej ceste. Abfrage Informácie Abnehmer Odberateľ Abriss Búranie Absenken Terén znížit' Accelerate time Zrýchliť čas Add forest Vysadiť les Add Stop Pridat' Add stops for backward travel Rovnaká spiatočná trasa aircraft_tab Nákladné lietadlá Airport Letisko AIRTOOLS Letecká doprava All Všetky Allow city growth Povoliť rast mesta Allow player change Povoliť zmenu hráča Alters a schedule. Pridať/odstrániť zastávky do/z cestovného poriadku Angenommene Waren Tovar potrebný pre ďalší priemysel anhaengen Pripojiť Anhaenger_tab Prívesy Anheben Terén zvýšit' Apply Line Použit' linku April Apríl Arbeiter aus: Zamestnanci z: Arrived Privezené Assets Majetok Aufloesen Rozpojiť Autohalt muss auf\nStrasse liegen!\n Zastávka musí byť\numiestnená na ceste. Available K dispozícii Bahndepot Železničné depo Bankrott:\n\nDu bist bankrott.\n Bankrot:\n\nZbankrotoval si.\n Baum Strom baum builder Záhradník Baustelle Stavenisko Beenden Skončit' Beginner mode Začiatočnícky mód Besonderes Gebaeude Turistický cieľ BF stanica Blockstrecke ist\nbelegt\n Blok je obsadený !\n Boden Krajina bridge is too high for its type! Tento typ mostu nemôže stáť tak vysoko. Bridge is too long for this type!\n Tento typ mostu\nnemôže byť tak dlhý.\n Bruecke Most Bruecke muss an\neinfachem\nHang beginnen!\n Most môže byt' postavený\nlen na rovnom kopci !\n Brueckenboden most build choosesignals Postaviť hradlo Build city market Postavit' Supermarket Build drain Postavit' elektrickú rozvodňu build HQ Postaviť centrálu Build land consumer Postavit' veternú elektráreň Build monorail depot Postaviť depo Alwegu Build powerline Postavit' elektrické vedenie Build presignals Stavba predsignalizácie Build road depot Postaviť vozovňu Build ship depot Stavba lodného depa Build signals Stavba signalizácie Build train depot Stavba železničného depa Build tram depot Stavba električkového depa Build truck depot Stavba automobilového depa Buildings Budovy Built artifical slopes Upraviť svah Built random attraction Postaviť náhodnú turistickú atrakciu Bus_tab Autobusy Cancel Návrat Capacity: %d%s %s\n Kapacita : %3d%s %s\n Cars are not available yet! Vozidlá sa zatiaľ nevyrábajú. Cash Peniaze Change player Zmeniť hráča Chart Grafy Choose operation executed on clicking stored/new vehicles Zvolit' vykonávanú operáciu pri kliknutí na zaparkované/nové vozidlá v depe chooses a random map Vybrať náhodnú mapu citicens Obyvatelia City attraction Mestské pamiatky City industries Mestský priemysel City list Zoznam miest City size Veľkosť city_road Mestská cesta citybuilding builder Postaviť mestskú budovu CityLimit Hranice mesta cl_title Zoznam vozidiel cl_txt_sort Triedenie: clf_chk_aircrafts lietadlá clf_chk_cars Busy/Nákl.autá clf_chk_indepot depá clf_chk_name_filter Filter názvov: clf_chk_noincome žiadny príjem clf_chk_noline bez trasy clf_chk_noroute žiadna cesta clf_chk_noschedule žiadny cest.por. clf_chk_ships Lode clf_chk_spezial_filter Špeciálny filter: clf_chk_stucked Uviaznuté clf_chk_trains Vlaky clf_chk_trams Električky clf_chk_type_filter Filter typov: clf_chk_waren Filter cien: clf_title Filter vozidiel: Climate Control Nastaviť podnebie COLOR_CHOOSE\n Prosím zvoľ\nTvoju farbu\nkliknutím:\n Company bankrupt Spoločnosť skrachovala Company_msg Konkurenti Comparing pak files ... Porovnávanie *.pak súborov ... Configure AI Nastaviť umelú inteligenciu Congratulation\nScenario was complete in\n%i months %i years. Gratulácia!\n\nScenár bol úspešne uhraný za %i mesiace(ov) a %i rok(y) Connected stops Zastávky v dosahu: Connected with server Spojený so serverom Constructed by Vytvoril: Constructed by %s Vytvoril: %s construction speed rýchlosť výstavby Construction_Btn Stavby Consumed Spotrebované convoi %d of %d konvoj %d z %d convoi error tooltips Zobraziť len varovné nápovedy Convoi has been sent\nto the nearest depot\nof appropriate type.\n Konvoj bol poslaný do\nnajbližšieho depa príslušného\ntypu. Convoi is sold when all wagons are empty. Konvoj bude vyradený, keď budú všetky jeho vozidlá prázdne. convoi passed last\nmonth %i\n \nkonvojov za posledný mesiac: %i\n Convois Konvoje Convois: %d\nProfit: %s Vozidiel: %d\nPZisk: %s Convoys Konvoje Copy Convoi Duplikovať Copy the selected convoi and its schedule or line Skopírovať zvolený konvoj s cest. poriadkom alebo linku Create a new line based on this schedule Vytvoriť novú linku podľa tohto cestovného poriadku curiosity builder Postaviť kuriozitu curlist_title Zoznam atrakcií Currently playing: Zmena prehrávania : Deccelerate time Spomaliť hru Del Stop Zmazat' Delete Line Zrušit' linku Denkmal Pomník Departed Odvezené Depots Depá Der Tunnel ist nicht frei!\n Tunel nie je voľný ! Destination Cieľ určenia Details Detaily Direkt erreichbare Haltestellen Priame spojenia Display settings Obrazovka : Dock Dok Dock must be built on single slope! Dok musí byť postavený na rovnom svahu. dp_title Zoznam dep Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Máš %d mesiace na splatenie dlhu. Durchsatz Max. Eigenbesitz\n Verejný majetok\n Ein %s\npasst hier nicht.\n '%s'\nsem nepasuje.\n Einstellungen aendern Zmena nastavení electric elektro Electrics_tab Elektrické vlaky Electrify track Elektrifikovaná trat' Error Chyba Erzeuge neue Karte.\n \nProsím čakaj,\nvytváram novú mapu...\n\n(Pri veľkých mapách\nto môže pár minút\ntrvat'...)\n Es wird bereits\nein Fahrplan\neingegeben\n Už bol jeden\ncestovný poriadok\nzadaný!\n Fabrikanschluss Spojenie s priemyslom Fabrikname názov fabriky Factories Fabriky factorybuilder Postaviť fabriku Fahrplan Cest. poriadok Fahrtziel Smer: Fahrzeuge koennen so nicht entfernt werden Vozidlá nemôžu byt'\ntakto zrušené.\n Fahrzeuge: Vozidlá: Farbe Farby Fast forward Zrýchliť hru February Február Ferry_tab Trajekty Fertig Hotovo Filename Súbor : Filter: cl_btn_filter_enable Finances of %s Financie spoločnosti %s Finanzen Financie fl_title Zoznam fabrík Flug_tab Dopravné liatadlo follow me Sledovanie Follow the convoi on the map. Sleduj súpravu na mape Forest les Found new city Vybudovat' nové mesto (5.000.000$) Fracht Náklad Free Capacity Voľné miesto freeplay mode voľná hra Full load Vytaženie : Fussgaenger Chodec GAME PAUSED HRA POZASTAVENÁ Gear: Príslušenstvo: Gebaeude Budova Gewicht Hmotnost' Gewinn Príjem Give the selected vehicle(s) an individual schedule Zadat' vybranému(ým) vozidlu(ám) osobitný cest. poriadok gl_title Zoznam služieb go home Do depa Goods Tovar Goods list Prehľady služieb Gross Profit Cash Flow groundobj builder Postaviť objekt v krajine Grow city Zväčsiť mesto Growth Prírastok obyv. H Hangar Hangár Happy Uspokojené Haus kaufen Kúpiť dom Helligk. Obraz Help Nápoveda Help text not found Text nápovedy chýba. hide all building Skryť všetky budovy hide city building Skryť mestské budovy hide transparent Priehľadné (neskrývať) hide trees Skryť stromy Hier warten/lagern: Čakajúci pasažieri/tovar: hl_title Zoznam zastávok hl_txt_filter Filter: hl_txt_sort Triedenie: hlf_chk_airport Letiská hlf_chk_anleger Dok hlf_chk_bahnhof Žel.stanica hlf_chk_bushalt Zastávka hlf_chk_frachthof Nákl.rampa hlf_chk_keine_verb bez spojenia hlf_chk_name_filter Filter názvov: hlf_chk_spezial_filter Speciálny filter: hlf_chk_type_filter Filter typov: hlf_chk_waren_abgabe Produkcia: hlf_chk_waren_annahme Akcept.cena: hlf_title Filter staníc: Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Depo nebolo nájdené!\nKonvoj treba poslat'\ndo depa manuálne. Homeless Bývanie hľadá hydrogene Vodík ignore climates Ignorovať podnebie industrial building priemyselná budova Init map ... Vytváram mapu Ins Stop Vložit' Intercity road len: Dĺžka ciest: Intro. date: Dostupné od: Invalid coordinate -neplatné súradnice- isometric map Izometrické zobrazenie January Január join game On-line hra July Júl June Jún Kann Spielstand\nnicht laden.\n Nemôžem načítat' hru !\n Kann Spielstand\nnicht speichern.\n Nemôžem uložit' hru !\n Kein Besitzer\n Bez majiteľa\n keine žiadny Keine Einzelfahrzeuge im Depot V depe nie je žiadne vozidlo Kreuzung Križovatka labellist_title Zoznam značiek Lade Relief Načítat' reliéf Laden Načítat' Land industries Mimomestský priemysel: LANG_CHOOSE\n Prosím zvoľ Tvoj jazyk:\n Last Month Minulý mesiac: Last used tools Nedávno použité nástroje Last Year Minulý rok: leer prázdne Legend Vysvetlivky Leistung Výkon Leistung: %d kW Výkon: %d kW Leitung Elektrické vedenie letzen Monat: diesen Monat: posledný mesiac: tento mesiac: Line Linka Line Management Manažment liniek Lines serving this stop Linky obsluhujúce túto stanicu LKW_tab Nákl. autá Load game Uložené hry Load scenario Scenáre loaded naložené loaded passenger/freight Zoradiť náklad podľa Loading (%i->%i%%)! Naložené %i z %i%% Loading map ... Nahrávam mapu ... Lokomotive_tab Lokomotívy m3 m? Mail Demand %d\n Dopyt po pošte %d\n Mailbox Archív správ Mailbox Options Nastavenie správ Maintenance Údržba make stop public (or join with public stop next) costs %i per tile and level Sprístupnenie zastávky (prepojenie so susednými verejnými zastávkami) stojí %i$ za každé políčko a podlažie Manual (Human) Človek Manufactured: Vyrobené: Map roughness Členitosť mapy map zoom mierka mapy March Marec Margin (%%) Rentabilita tržieb Marker Značkovač Max Boost (%%) Max. nárast (%%) Max income: Max. tržba: Max. speed: Max. rýchlosť: Maximum 254 stops\nin a schedule!\n Maximum\n254 staníc\nv cestovnom poriadku!\n maximum length of rivers Max. dĺžka riek Maximum tile height difference reached. Maximálný výškový rozdiel\nmedzi dvomi políčkami\nbol presiahnutý. Maxspeed Najvyššia rýchlosť May Máj Median Citizen per town Priem. počet obyvateľov: Meldung Oznámenie Menge Množstvo merge stop Spojiť zastávky MessageOptionsText \nNový rok\n\nKonkurencia\n\nMesto\n\nBez trasy\n\nPriemysel\n\n"chat"\n\nNové vozidlá\n\nPlné zastávky\n\nProblémy\n\nTraffic jams\n\nScenario minimum length of rivers Min. dĺžka riek Missing pakfiles Chýbajú súbory grafickej sady! Modify the selected line Upraviť zvolenú linku Monate alt mesiacov starý. Monorail Visutá železnica monorail vehicle vozidlo visutej železnice monorail_track Trať visutej železnice Monorailboden Nosník visutej železnice Monoraildepot Depo month wait time Max. čakanie (mes.) Months Mesiace Monument Pamätník Monuments Pamätníky Mountain height Výškový rozdiel Movingobj pohyblivý objekt Music playing disabled/not available Hudba nedostupná/vypnutá Music volume: Hlasitosť hudby: mute sound Vypnúť zvuk Name Názov Narrowgauge Úzkokoľajka Narrowgauge are not available yet! Úzkokoľajka ešte neexistuje! narrowgauge vehicle Úzkorozchodné vozidlo narrowgauge_track Úzkorozchodná trať Narrowgaugedepot Úzkorozchodné depo Net ID: %p Sieť: %p\n Net Wealth Vlastné imanie Net wealth near zero Hodnota Vašej spoločnosti sa blíži nule. Neue Karte Nová mapa Neue Welt Nový svet new convoi nový konvoj New Line Nová linka New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Nová linka bola vytvorená.\nMôžete ju teraz priradiť\njej zvolením\nv hornej časti okna.\n New Vehicles Nové vozidlá Nickname: Prezývka no buildings hidden Ukázať budovy no convois žiadny konvoj No goods are loaded onto this convoi. Do konvoja nebude naložený žiadny tovar no goods waiting Nečaká žiadny tovar no load Nenakladať No Route Bez trasy No stop here! Nástroj je prístupný len na staniciach! No suitable ground! Nevhodný terén! No terminal station here! Tu nie je možné postaviť\nkoncovú zastávku.\nMusí stáť na rovine\nna konci cesty. no timeline všetky obdobia no tree bez stromov No. of Factories Počet tovární a obchodov: Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Nemáš žiadny\ncestovný poriadok!\n none nič nord Sever nordost Severovýchod nordwest Severozápad Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Odopreté!\nCestovný poriadok nemôže\nbyt' teraz zmenený.\nVyskúšajte neskôr ! Not enough fields would remain. Okolo tejto farmy zostáva\nmálo políčok. Now active as %s.\n Teraz aktivny ako %s.\n Number of rivers Počet riek Object Podľa ID Odometer: %s km Najazdené: %s km Ok OK Oktober Október On loan since %i month(s) Počet mesiacov na splatenie dlhov: %i On this map, you are not\nallowed to change player!\n V tejto hre\nnemôžete hrať\nza iného hráča!\n Only city chains Postaviť mestský priemysel Only first %d differing paks reported. There are probably more. Ohlásených je len %d odlišných súborov. Ale je ich asi viac. Only land chains Postaviť mimomestský priemysel Only one transformer per factory! Továreň môže mať len jednu trafostanicu! Operation Prevádzka Ops Profit Zisk EBITDA Optionen Nastavenia Origin Zdroj ost Východ paletten paliet Pas_tab Osobné vlaky Passagiere cestujúcich Passagierrate Pasažieri Passagierziele Pasažierske/Poštové ciele Passengers %d %c, %d %c, %d no route Cestujúci %d %c, %d %c, %d bez spojenia Passengers %d %s, %d %s, %d no route Cestujúci %d %s, %d %s, %d bez spojenia Pause Pauza Pax <%i> Mail <%i> Cestujúci <%i> Pošta <%i> Planes are not available yet! Dopravné lietadla ešte neboli vyvinuté. Plant tree Zasadit' strom player -1 človek player 0 verejná služba player 1 Napik 128 AS player 2 Trikky Transport player 3 Meyer Moving Co. player 4 Spedition VM player 5 H-Trans GmbH player 6 PSK & Co KG Please choose vehicles first\n \nNajprv prosím vyber vozidlo ! Post pošty Postrate Pošta Power Energia Power: Výkon: Powerlines El. vedenie Production of %s has been stopped:\n%s\n Výroba %s\n'%s'\nbola ukončená.\n Produktion Produkcia Profit Zisk promote to line Linka podľa trasy q1 Jar q2 Leto q3 Jesen q4 Zima rail car železničného vozidla Random map Náhodná mapa Rathaus Radnica Rating Hodnotenie Reliefkarte Mapa reliéfu Remove Odstránit' remove airstrips Odstrániť dráhu remove channels Zrušiť kanál remove monorails Demontovať trať remove roads Odstrániť vozovku remove tracks Demontovať trať replace stop Vymeniť zastávky residential house Obytný dom Restore natural slope Obnoviť pôvodný terén Restwert: Predajná hodnota: Retire. date: Koniec výroby: return ticket Spiatoč.jazda Revenue Tržby road vehicle cestného vozidla Roadsign Dopravná značka Rotate Building Otočiť budovu (nástupište) Rotate map Otočiť mapu Rotation Otočenie sack vriec sail vietor Save Uložiť hru Saving map ... Ukladám mapu ... Schienentunnel Stavba žel. tunela Schiff_tab Lode Schiffdepot Lodné depo Schleppkahn_tab Tahače Screenshot Urobiť screenshot. Sehenswuerdigkeit Turistická destinácia Sell the selected vehicle(s) Predat' vybrané vozidlo(á) sended Odosl. pošta SEP_FRACTION , Serves Line: Obsluha linky: Service Obsluha Setting Konfigurovať Ship Lode shops and stores Obchody a kancelárie Show all Zobraziť všetko show grid Zobraziť mriežku Show industry Priemysel Show legend Vysvetlivky Show map scale Nadm. výška Show obsolete Zobraziť zastaralé Show schedules Zobraziť trasu linky show station coverage Zobraziť spádové uzemie stanice Show/hide statistics Ukázat'/Skryt' štatistiku Shrink city Zmenšiť mesto Size (%d MB): Veľkost mapy: Sort by Zoradiť podľa Sort waiting list by Triedit' podľa čakania Sound Hudba Sound settings Nastavenia zvukov Sound volume: Hlasitost zvukov: special freight Špec. preprava Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Expresný príplatok\ncesty %i km/h, železnica %i km/h\nlode %i km/h, lietadlá %i km/h. Speedlimit Max. rýchlost' Speichern Uložit' Spieler Hráč Spieler(mz) Konkurencia Spielerliste Zoznam hráčov Spielstand wurde\ngeladen!\n Hra bola načítaná !\n Spielstand wurde\ngespeichert!\n Hra bola uložená !\n Sprache Jazyk Sprachen Jazyky Stadtinformation Informácie o meste Start Štart Start the selected vehicle(s) Vyjst' s vybraným(i) vozidlom(i) Starte Spiel Nová hra Station tiles: Rozloha stanice: Station_msg Stanice Status Stav steam para Step timeline one year Pridaj jeden rok Stops Zastávky Storage Zásoby Storage capacity Ložná kapacita Strassendepot Cestné depo Strassentunnel Postaviť tunel street car električka sued Juh suedost Juhovýchod suedwest Juhozápad Summer snowline Letná hr. snehu Supplied: %.0f %% Dodané: %.0f %% Suppliers Dodávatelia Tage alt dní staré There are still vehicles\nstored in this depot!\n V tomto depe\nsú ešte vozidlá !\n This Month Tento mesiac: This Year Tento rok: timeline Zodpovedajúce dobe tl_title Zoznam miest To heavy traffic\nresults in traffic jam.\n Hustá doprava\nspôsobila zápchu.\n tonnen t Total inhabitants: Počet obyvateľov: Tourist attractions Počet turistických atrakcií: Tourists Turisti Town: %s\n Mesto %s.\n Towns Mestá Tracks Trasy Traffic Premávka Train Vlaky Trains are not available yet! Vlaky ešte neexistujú! Tram Električka tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. električky %i km/h, visutá železnica %i km/h\nmaglev %i km/h, úzkokoľajka %i km/h. tram_track električková trať Tramdepot Električková vozovňa Trams are not available yet! Električky zatiaľ neexistujú. Transferring game ... Prevádzam hru... Transformer only next to factory! Trafostanice sú dostupné len\nna prázdnych a rovných políčkach\nsusediacich s továrňou! Translation Preklad transparent station coverage Priehľadné sp. územie stanice Transported Odvezení TrolleyBus_tab Trolejbusy Truck Automobily Tunnel muss an\neinfachem\nHang beginnen!\n Tunel musí začínat'\nna úpätí kopca !\n Tunnel must start on single way! Tunel musí začínať\nna voľnom konci cesty!\n Tunnelboden Tunel underground mode Pohľad do podzemia UNDO failed! Nie je možné vziať späť. Undo last ways construction Odvolať posledný krok stavby cesty Unemployed Prácu hľadá Unhappy Neuspokojené units/day /mes, Update Line Upraviť linku upgrade HQ Rozšíriť centrálu Usage: %.0f %% Využitie: %.0f %% Usage/Output Spotreba/Výroba Use beginner mode Začiatočnícky mód Use timeline start year Začať od roku Vehicle %s can't find a route! Vozidlo %s nemôže nájsť cestu ! Vehicle %s is stucked! Vozidlo %s uviazlo!\n Vehicle details Detaily vozidla Verbrauch Spotreba Vergroessere die Karte\n Zväčšiť mapu.\n Verkauf Odstrániť verkaufen Vyradiť Verkehrsteilnehmer Osobné autá Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Máš %d mesiace\nna splatenie dlhov.\n vh_title Katalóg vozidiel via cez via %s\n cez %s\n via Menge cez (počet) voranstellen Na začiatok Waggon_tab Vagóny waiting Čaká Waiting for clearance! Čaká na voľnú cestu! Warnings_msg Doprava Wasser Voda/More Water voda Water level Úroveň vody water vehicle plavidlo way %s cannot longer used:\n Trať/cesta %s sa nemôže ďalej používať.\n Ways not connected Možné len na spojených cestách! Wegpunkt Prejazd cez Weight: Hmotnosť: Wert Hodnota west Západ Winter snowline Zimná hr. snehu withdraw Vyradiť Withdraw All Vyradiť všetky! WRONGSAVE Nekomatibilná verzia\nuloženej hry. Hra nemôže\nbyt' načítaná.\n Year %i has started. Začal rok %i! Years Roky Your primary color: Vaša hlavná farba: Your secondary color: Vaša vedľajšia farba: Zielort cieľ zooming in priblížiť zooming out vzdialiť Zu nah am Kartenrand Si príliš blízko\nk okraju mapy,\naby si mohol\nniečo stavat'.\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nový rýchlostný rekord visutej zeleznice dosiahol rýchlosťou %.1f km/h\nvlak %s. New world record for monorails: %.1f km/h by %s. Nový rýchlostný rekord\nnvisutej železnice dosiahol\nrýchlosťou %.1f km/h\nvlak %s. New world record for motorcars: %.1f km/h by %s. Nový rýchlostný rekord\nna ceste dosiahlo\nrýchlosťou %.1f km/h\nvozidlo %s. New world record for narrowgauges: %.1f km/h by %s. Nový rýchlostný rekord\nna úzkorozchodnej železnici\ndosiahol rýchlosťou %.1f km/h\nvlak %s. New world record for planes: %.1f km/h by %s. Nový rýchlostný rekord\nv leteckej doprave dosiahlo\nrýchlosťou %.1f km/h\nlietadlo %s. New world record for railways: %.1f km/h by %s. Nový rýchlostný rekord\nna železnici dosiahol\nrýchlosťou %.1f km/h\nvlak %s. New world record for ship: %.1f km/h by %s. Nový rýchlostný rekord\nna vode dosiahla\nrýchlosťou %.1f km/h\nloď %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &1_CITY_SYLL ské mesto &2_CITY_SYLL ská Dedina &3_CITY_SYLL sko &4_CITY_SYLL ský Zrub &5_CITY_SYLL ská Kopanica &6_CITY_SYLL ský Tatran &7_CITY_SYLL ská Koliba &8_CITY_SYLL ovce &9_CITY_SYLL ská župa &A_CITY_SYLL ské vrchy %1_CITY_SYLL Košic %2_CITY_SYLL Poprad %3_CITY_SYLL Kežmar %4_CITY_SYLL Martin %5_CITY_SYLL Žilin %6_CITY_SYLL Trenčín %7_CITY_SYLL Trnav %8_CITY_SYLL Zvolen %9_CITY_SYLL Svitov %A_CITY_SYLL Ostrav %B_CITY_SYLL Prešov %D_CITY_SYLL Lev 0center %s Námestie 0extern %s Pod lesom 0suburb %s Predmestie 1center %s Obchodná 1extern %s Na okraji 1suburb %s Nová Doba 2center %s Dolná 2extern %s Záhradky 2suburb Kolónia 3center %s Horná 3extern %s Vidiek 3suburb %s Pod Viškovkou 4center %s Kapitulská 4extern %s Chatky 4suburb %s Továrenská 5center %s Zámocká 5extern %s Rázcestie 5suburb %s Hronov 6center %s Kapitulská 7center %s Národná 8center %s Štefánikova 9center %s Masarykova Acenter %s Štúrova Bcenter %s U samaritánov Ccenter %s Cintorínska Dcenter %s Kafkova Ecenter %s Národná Fcenter %s Hodžova Fsuburb %s Hurbanova Gcenter %s Blumentálska Gsuburb %s Františkánska Hcenter %s Alžbetínska Hextern %s Hsuburb Icenter %s Poštová Isuburb %s Dobšinského Jcenter %s Tajovského Jsuburb %s Mateja Bela Kcenter %s Moyzesova Ksuburb %s Nová Doba Lcenter %s Satinského Mcenter %s Havlova Mextern %s Dolná Msuburb %s predmestie Ncenter %s SNP Nextern %s Mlynská Nsuburb %s Predmestie Ocenter %s Benešova Oextern %s Záhradná Pcenter %s Národná Qcenter %s Šoltésovej Rcenter %s Židovská Scenter %s Sokolská Tcenter %s Kapitulská Tsuburb %s Mýtna Ucenter %s Divadelná Uextern %s Tatranská Usuburb %s Dolnozemská Vcenter %s Trojičná Vextern %s Stodolná Vsuburb %s Lipová Wcenter %s Murgašova Wextern %s Lužná Wsuburb %s Záhumienok Xcenter %s Medená Xextern %s Školská Xsuburb %s Stráne Ycenter %s Zámocká Yextern %s Na pokraji Ysuburb %s Kolónia Zcenter %s Havlova Zextern %s Újazd Zsuburb %s Sedliacka simutrans-124.5/simutrans/text/sk/000077500000000000000000000000001520675460500172125ustar00rootroot00000000000000simutrans-124.5/simutrans/text/sk/bridges.txt000066400000000000000000000027461520675460500214030ustar00rootroot00000000000000Nástroj na stavbu mosta - pomoc

Stavba mosta

Most sa stavia podobne ako tunel.
Cestný most
Musíš mat' už vytvorené cesty, ktoré spojíš mostom. Obe musia presne lícovat' (pasovat' proti sebe). Myšou zvoľ nástroj cestného mosta. Prepojenie vytvoríš kliknutím na jeden z koncov prepájaných ciest. Most môzeš stavat' aj tak, že jeden jeho koniec bude na kopci (do výšky 1-nej pozície). Pokiaľ by sa Ti stalo, že cesta nie je prepojená, skús potiahnut' cestu ešte o jednu pozíciu pod most.

Železničný most
Musíš mat' vytvorené železničné trate, ktoré spojíš mostom. Obe musia presne lícovat' (pasovat' proti sebe). Myšou zvoľ nástroj niektorého zo železničných mostov. Prepojenie železničných tratí vytvoríš kliknutím na jeden z koncov. Most s plným dnom (bez bočnej konštrukcie) má tú výhodu, ze môžeš naň umiestnit' signalizáciu. Most môzeš postavit' aj tak, ze jeden koniec bude na kopci (do výšky 1-nej pozície). Pokiaľ by sa Ti stalo, ze železničná trat' nie je prepojená, skús potiahnut' koľajnice ešte o jednu pozíciu pod most.

V oboch prípadoch platí, ze stavat' mosty s jedným koncom na kopci (príp. obidvomi) je možné, len ak sú steny kopca zarovnané do výšky 1-nej pozície. Mosty môzeš stavat' aj tak, že preklenieš jamu (roklinu). Obe steny jamy (rokliny) však musia mat' opät zarovnané steny do hĺbky 1-nej pozície.

simutrans-124.5/simutrans/text/sk/color.txt000066400000000000000000000005251520675460500210730ustar00rootroot00000000000000Voľba farby - pomoc

Farby

V tomto menu si môžeš zvoliť farbu Tvojej spoločnosti. Bude spoločná pre všetky Tvoje dopravné prostriedky, stanice atd.

Zvoľ svoju svoju farbu jednoduchým kliknutím na niektorú zo zobrazených farieb.

Štandardne je nastavená svetlomodrá farba.

simutrans-124.5/simutrans/text/sk/convoi.txt000066400000000000000000000010351520675460500212470ustar00rootroot00000000000000Prehľad vozidiel - pomoc Prehľad ponúka nasledovné možnosti:

1.) Triedenie :
Klikaním na prvé tlačidlo zľava meníš kritéria, t.j. podľa čoho bude zoznam triedený : názvu, príjmu alebo typu.
Druhým tlačidlom zľava si nastavuješ vzostupné alebo zostupné triedenie.

2.) Filter :
Klikaním na tretie tlačidlo zľava zapínaš alebo vypínaš filter.
Posledným tlačidlom ,,Nastavenia'' zobrazíš podrobný zoznam všetkých kritérií a možností filtrovania.

simutrans-124.5/simutrans/text/sk/depot.txt000066400000000000000000000067721520675460500211020ustar00rootroot00000000000000Depo

Depo

V depe môžeš kupovat' a predávat' vozidlá, vytvárat' cestovné poriadky pre kúpené dopravné prostriedky príslušného typu, t.j. dopravné prostriedky v cestnom, želežničnom alebo lodnom depe.

Depo obsahuje informáciu o dopravnom prostriedku, ktorý môžeš kúpit' (aj ktorý už vlastníš). Cenu a parametre vozidiel zobrazíš nabehnutím myši na príslušné vozidlo. Kliknutím na vedľajšiu záložku ,,Prívesy'' alebo ,,Vagóny'' zobrazíš zoznam prívesov alebo vagónov. Nad informačnou zónou (s cenou a parametrami vozidla) nájdeš skupinu tlačidiel. Prvú skupinu tvoria :
,,Štart'',,,Cestovný poriadok'', ,,Rozdelit' '' a ,,Predat' ''.

Tlačidlom ,,Štart'' vypustíš vozidlo so zadaným cestovným poriadkom do premávky. Tlačidlom ,,Cestovný poriadok'' zadefinuješ, odkiaľ a kam bude vozidlo premávat' a ako bude chodit vyt'ažené. Tlačidlom ,,Rozpojit' '' dosiahneš rozpojenie zostavy vozidla s prívesom alebo vlaku. Pod nápisom ,,Vozidlá'' je umiestnené tlačidlo módu. Klikaním nadobúda tri stavy ,,Pridávanie'' ,,Zostavovanie'' a ,,Predávanie''. V móde predávania si zvolíš, ktorý vlastnený dopravný prostriedok predat' (za zostatkovú cenu). Predávanie ukončíš prepnutím tohto tlačidla. Informácie o hodnote a parametroch budú zobrazené v informačnej zóne (tesne pod tlačidlami).
Predat' okamžite nejaký Tvoj privolaný dopravný prostriedok môžeš aj tlačidlom ,,Predat' ''.
Pri kúpe : Ak Ti vyhovuje cena a parametre, kúpu vozidla vykonáš kliknutím na vozidlo. Kúpu prívesu alebo vagónov dosiahneš obdobným spôsobom. Kúpené vozidlá sa objavia v prvom a ďalších poliach. Z nich si klikaním v logickom slede poskladaj dopravný prostriedok. Poskladaný dopravný prostriedok sa objaví v prvých (vrchných) poliach.

Teraz môžeš pristúpit' k zadaniu cestovného poriadku. Musíš mat' už vytvorené stanice, zastávky alebo nákladné rampy. Zobrazeným žltým značkovačom pooznačuj stanice, zastávky alebo nákladné rampy po ktorých bude premávat' dopravný prostriedok.

Klasicky zadávaš cestovný poriadok každému vozidlu zvlášt'. Táto verzia hry však umožňuje vytvorit' cestovný poriadok pre celú skupinu vozidiel - konvoj tlačidlami ,,Nová linka'', ,,Použit' linku'', ,,Upravit' linku'' a ,,Zrušit' linku''. Stačí kúpit' dopravný prostriedok, priradit' k niektorej zo zadefinovaných liniek a vypustit' do premávky.
Pre lepší prehľad o linkách má hra zabudovaný tzv. Manažment liniek - kláves ,,W''.

Ak chceš poslat' dopravný prostriedok spät' do depa neskôr, do cestovného poriadku ho vlož úplne na koniec. V depe môžeš dopravný prostriedok porozkladat' (napr. oddelit' príves alebo vagóny), preusporiadat' a prípadne znova po skontrolovaní (príp. zmene) cestovného poriadku vypustit' do premávky.

Pozn. prekladateľa : Cestovný poriadok vozidla môzeš menit' aj počas jazdy. Myšou klikni na idúci dopravný prostriedok a vykonaj v jeho cestovnom poriadku úpravy. Po ukončení úprav potvrď tlačidlo ,,Hotovo''. Počas týchto úprav bude dopravný prostriedok stát'. Zaujímavost'ou je, že do cestovného poriadku môžeš zadat' aj ,,Voľný bod trasy''. Vozidlo takto nebude zarábat'. Pri úpravách staníc, zastávok alebo nákladných rámp si treba na toto dat' pozor !

simutrans-124.5/simutrans/text/sk/display.txt000066400000000000000000000030201520675460500214130ustar00rootroot00000000000000Nastavenia obrazu

Obraz - pomoc

Tu si nastavuješ vlastnosti rolovania, zapínaš alebo vypínaš zobrazenie chodcov a zapínaš alebo vypínaš efekt, ktorý napodobňuje striedanie dňa a noci.

Rolovanie:
Pri rolovaní môzeš klikaním na šípky nastavit' citlivost' rolovania, t.j. o akú veľkú vzdialenost' sa Ti bude posúvat' zobrazenie. Čím je hodnota väčšia, o tým väčšiu vzdialenost' (gradovanie) sa budeš presúvat' z jedného miesta na druhé už pri nepatrnom pohybe myšou alebo povelom z klávesnice.
Ak ponecháš hodnotu rolovania na 1, môžeš dosiahnut' jemnejšieho presúvania sa po krajine.

Inverzné rolovanie:
Ak ti nevyhovuje posun po krajine voči pohybu myšou a si zvyknutý na opačný pohyb krajiny, zapni si túto funkciu.

Ukázat' chodcov:
Zapnutím tejto funkcie dosiahneš mimoriadne efektného zobrazenia pasažierov, ktorý vystupujú z vlaku, autobusu alebo lode. Čím bude viac vystupujúcich pasažierov, tým bude efekt viditeľnejsí (skoro ako mravenisko).

Striedat' noc a deň:
Táto funkcia umožňuje napodobnit' striedanie dňa a noci. Odporúčam ju zapnút' iba v prípade, keď nemáš v pláne nijaké väčšie budovanie Tvojej dopravy a chceš iba také ,,oddychové'' hranie a pozeranie. Akonáhle sa pustíš do väčšieho budovania, tento efekt Ťa môze rušit'. Vtedy si ho vypni.

Slovenský popis: Ján Krnáč
Simutrans (c) Hj. Malthaner

simutrans-124.5/simutrans/text/sk/finances.txt000066400000000000000000000024031520675460500215400ustar00rootroot00000000000000Financie - pomoc

Financie

Stav Tvojich penažných prostriedkov môžes sprístupnit' stlačením klávesu ,,f'' alebo voľbou ikony ,,Financie'' na hornej lište.
Zobrazená bude informácia o Tvojich financiách za posledné dva roky : príjmov a výdavkov.

Položky sú nasledujúce:

Výdaje na stavbu - to je čokoľvek, čo si postavil v priebehu roka vrátane signalizácie, železnicných tratí, pôšt a pod.

Nové vozidlá - výdaje na akýkoľvek dopravný prostriedok, ktorý pribudol do Tvojho vozového parku v ktoromkoľvek depe.

Údržba vozidiel - náklady na údržbu všetkých Tvojich dopravných prostriedkov.

Príjem - súhrn Tvojich príjmov za všetok prepravený tovar a pasažierov.

Celkom - súhrn Tvojich príjmov/výdajov za rok. Toto je súhrn za všetky položky.

Zostatok - celkové obchodné saldo Tvojej spoločnosti. To znamená celkový stav Tvojich peňazí. Ak je kladný, môzeš ho pouzit' na ďalsie investovanie. Táto informácia je tiež zobrazená v stavovej (dolnej) lište obrazovky.

Podpora (mesacne) - bonifikácia, dotácie a pod. Tvojich financií (mesačne).

simutrans-124.5/simutrans/text/sk/general.txt000066400000000000000000000014371520675460500213750ustar00rootroot00000000000000Simutrans - pomoc

Klikni na témy :

Ovládacie klávesy
Spustenie hry s parametrami

Nastavenia hry
Nástroje na stavbu ciest
Nástroje na stavbu železnice
Nástroje na stavbu lodnej dopravy
Nástroje na stavbu mostov
Stanice a zastávky
Cestovný poriadok
Prehľad zastávok
Prehľad vozidlového parku
Prehľad financií

Slovenský popis : Ján Krnáč
Simutrans (c) Hj. Malthaner

simutrans-124.5/simutrans/text/sk/haltlist.txt000066400000000000000000000011601520675460500215750ustar00rootroot00000000000000Prehľad zastávok a staníc

Prehľad Tvojich zastávok, staníc a nakladacích rámp

Prehľad ponúka nasledovné možnosti:
1.) Triedenie :
Klikaním na prvé tlačidlo zľava meníš kritéria, t.j. podľa čoho bude zoznam triedený : názvu, typu, čakania alebo čísla.
Druhým tlačidlom zľava si nastavuješ vzostupné alebo zostupné triedenie.

2.) Filter :
Klikaním na tretie tlačidlo zľava zapínaš alebo vypínaš filter.
Posledným tlačidlom ,,Nastavenia'' zobrazíš podrobný zoznam všetkých kritérií a možností filtrovania.

simutrans-124.5/simutrans/text/sk/language.txt000066400000000000000000000020331520675460500215340ustar00rootroot00000000000000Nastavenie jazyka - pomoc

Nastavenie jazyka

Spôsob ako zmenit nastavenie jazyka v hre Simutrans je voľbou Tvojho jazyka. Ak zvolíš jazyk predtým, ako spustíš novú hru, názvy miest už budú vytvorené podľa tohto jazyka.

Nastavenie jazyka môzeš kedykoľvek zmenit. Obsah otvorených okien nemôže byt aktualizovaný ihneď, ale až po ich znovuotvorení.

Teda ak chceš mat názvy miest v Tvojom jazyku, najprv vyber svoj jazyk a až potom spusti novú hru.

Ak chýba Tvoj požadovaný jazyk, skús pohľadať ďalšie preklady na:
http://www.simutrans.de
http://www.simutrans.cjb.net
v sekcii ,,Download''.

České stránky venované tejto hre nájdeš na:
http://www.simutrans.wz.cz

Pozn. : Ale môžu to byt' staršie verzie prekladov. Väčšina textov vsak býva bezprostredne aktualizovaná.

Ak chcete pomôcť s prekladom Simutranov do vášho jazyka,
navštívte stránku https://translator.simutrans.com

simutrans-124.5/simutrans/text/sk/load.txt000066400000000000000000000005011520675460500206660ustar00rootroot00000000000000Načítanie - pomoc

Načítat' uloženú hru

Napíš názov uloženej hry, alebo ju vyber zo zoznamu uložených hier. Pokračuj kliknutím na jej názov. Týmto vybranú hru spustíš.

Hru vymažeš kliknutím na značku ,,X'' vedľa názvu (pozor na neopatrné kliknutie !).

simutrans-124.5/simutrans/text/sk/map.txt000066400000000000000000000007231520675460500205320ustar00rootroot00000000000000Mapa - pomoc

Okno mapy

Okno mapy môzeš zobrazit stlačením klávesu ,,m'', alebo voľbou ikony ,,Mapa'' na hornej lište.

Na mape máš zobrazený svet miniatúrne s farebným rozlíšením rôznych bodov ako sú priemyselné odvetvia, želežničné trate, cesty a mestá.

Ak chceš zobrazit' aj konkrétny detail mapy, jednoducho klikni na príslušný bod, a hra hneď zobrazí túto pozíciu na obrazovke.

simutrans-124.5/simutrans/text/sk/new_world.txt000066400000000000000000000052321520675460500217550ustar00rootroot00000000000000Nový svet - pomoc

Nový svet

Tento režim ponúka nastavenie parametrov, ktoré budú určovat' charakter mapy vytvorenej pre novú hru. V ľavom hornom rohu obrazovky sa zároveň zobrazí ponuka na výber jazyka.

Číslo mapy - číslo ,,schémy'' mapy, vrátane množsta a umiestnenia vody, kopcov, osadenia stromov, umiestnenia miest a priemyslu. Predbežné zobrazenie (náhľad) je zobrazený pri každej mape. Štandardne je nastavená mapa číslo 33.
Veľkost mapy - akú veľkú mapu chceš hrat'. Rozmedzie je 128x128 - 576x576 stvorcekov. Štandardné nastavenie je 256x256 štvorčekov.
Hustota priemyslu - index určuje, ako husto bude priemysel rozmiestnený na mape. Čím je hustota priemyslu nižšia, tým sa stáva hra tažšou a tým sú možnosti prepravy surovín alebo tovaru menšie. Štandardné nastavenie je 125.
Počet miest - koľko miest celkom bude rozmiestnených na mape. Rozmedzie je 2 - 64 miest. Štandardné nastavenie je 16 miest.
Hustota dopravy - index určuje, aká bude hustota premávky súkromných áut na cestách. Štandardné nastavenie je 8.
Hladina vody - index určuje, aké množstvo vody budeš mat' na mape. Čím bude hodnota väcsia, tým bude menej pevniny a naopak. Štandardné nastavenie je 4.
Výškový rozdiel určuje rozdiel medzi hladinou vody a najvyšším vrchom, t.j. maximálnu nadmorskú výšku na mape. Štandardné nastavenie je 160.
Členitost' mapy - index určuje, či pôjde o prevažne rovinu, pahorkatinu alebo horskú krajinu. Rovine zodpovedá najnižšie číslo a horskej krajine najvyššie císlo. Rozmedzie je 1 - 3. Štandardné nastavenie je 2.

Tlačidlá :
Náhodná mapa - tlačidlom si vyberáš mapu, ktorú hra náhodne vyberie a ponúkne. Tým sa nemenia iné nastavenia parametrov mapy.
Ukázat' chodcov - zapnutím bude hra zobrazovat' chodcov na ulici alebo vystupujúcich z vlaku alebo autobusu. Toto nastavenie môžeš menit' aj neskôr v nastaveniach obrazu - Obraz.
Striedat' noc a deň - zapnutím bude bude hra napodobnovat' chovanie sa dňa a noci. Toto nastavenie môžeš menit' aj neskôr v nastaveniach obrazu Obraz.
Načítat' hru - umožní Ti vybrat' a spustit' niektorú z uložených hier.
Načítat' reliéf - umožní Ti vybrat' alternatívnu krajinu.
Spustit' hru - spustenie novej hry s tými parametrami mapy, aké máš momentálne nastavené.
Skoncit' - ukoncíš hru Simutrans.

simutrans-124.5/simutrans/text/sk/options.txt000066400000000000000000000017311520675460500214500ustar00rootroot00000000000000Nastavenia hry - pomoc

Nastavenia hry

Dialógový režim voľbou niektorej z možností ponúkne ešte ďalšie podúrovne nastavení. Potom sa zobrazí štandardný dialógový režim. Slúžia spravidla na zmenu vlastností, načítanie, uloženie alebo ukončenie hry.

Jazyk - zvoľ jazyk, ktorý budeš pouzívat' v hre Simutrans
Farby - ponúka možnost' zmenit' farbu Tvojej spoločnosti
Obraz - nastavenie vlastností obrazu
Zvuky - nastavenie hlasitosti hudby a zvukov
Konkurencia - voľba konkurenčných hráčov UMELEJ INTELIGENCIE
Nacítat' - načítanie uloženej hry
Ulozit' - uloženie rozohranej hry
Nová mapa - reštart hry a ponuka novej mapy
Skoncit' - ukončenie hry

simutrans-124.5/simutrans/text/sk/players.txt000066400000000000000000000011721520675460500214330ustar00rootroot00000000000000Konkurenční hráči - pomoc

Zoznam hráčov

V tomto menu si schopný vidiet' konkurenciu - UMELÚ INTELIGENCIU s možnost'ou bud vypnutia alebo zapnutia každého z nich.

Bankový zostatok - každý konkurent je slovne zobrazený aj so zostatkom jeho účtu. Zapnút' alebo vypnút' ktoréhokoľvek z nich môžeš kliknutím na príslušné políčko vľavo. Štandardne je zapnutý jeden konkurent tyrkysovej farby.

Pozn. prekladateľa : Vypnutím konkurentov deaktivuješ u nich len moznost' nového investovania. Ich dovtedajšie investičné aktivity však bežia naďalej.

simutrans-124.5/simutrans/text/sk/railtools.txt000066400000000000000000000107441520675460500217710ustar00rootroot00000000000000Nástroje na stavbu železnice

Stavba koľajníc - nástrojom staviaš železničnú trat' medzi dvoma ľubovolne vzdialenými bodmi (štvorčekmi), alebo pripájaš koľajnice čast' po časti. Kliknutím na zvolené miesto vytvorených koľajníc zase vytváraš križovatky. Postup : Klikni myšou na symbol koľajníc a pokračuj kliknutím na bod, z ktorého chceš viest' koľajnice. Kurzor bude zobrazený ako malý buldozér. Premiestni sa na koniec zamýšľanej trate a klikni tam myšou. Simutrans vytvorí spojenie medzi týmito dvomi bodmi. Pozn. prekladateľa : Pokiaľ by sa Ti trat' zdala príliš kľukatá, máš možnost' urobit' korekcie (zbúrat' tú čast', kde budeš robit' úpravy a potom pridávat' koľajnice čast' po časti. Stavbu koľajníc po uhlopriečkach je vhodnejšie robit' čast' po časti. Výsledok je lepší ;-) ). Na stavbu železnice môzeš pouzit' len voľné pozície (štvorceky). Ak nechceš križovat' inú trat', použi stavbu mosta alebo tunela. Ak nie je možné úspešne pokracovat' v budovaní trate, skús najprv vykonat' terénne úpravy.
Signalizácia - nástroj na stavbu signalizácie. Kliknutím naň bude kurzor bude zobrazený vo podobe dvoch semafórov (obojsmerný). Signalizácia slúži na organizáciu pohybu vlakov. Semafóry umiestniš kliknutím na koľajnice napr. pred križovatku nejakého spoločného jednokoľajového úseku alebo na koľajovú križovatku. Opakovaným klikaním na tú istú pozíciu sa Ti zobrazuje postupnost' ponúkaných typov semafórov. Akonáhle prejde jeden vlak cez semafór, ostané semafóry budú blokovat' ďalsie vlaky, až kým sa trat' opät' neuvoľní. Semafóry potom budú púšt'at' vlaky podľa poradia, v akom prišli k semafórom.
Tunel - kliknutím na tento nástroj bude kurzor zobrazený symbolom tunela. Aby si tunel mohol úspešne vytvorit', musia mat' kopce rovnú stenu na oboch stranách. Pomôž si úpravou terénu. Potiahni koľajnice ešte o 1-nu pozíciu na kopec a tak isto aj na druhej strane. Myšou klikni na nástroj tunela. Pokračuj kliknutím na jeden z koncov koľajníc (na kopci). Ak Ti všetko presne lícuje (t.j. trafil si sa), Simutrans urobí tunel a odpočíta Ti náklady na stavbu. Nadmorská výška tratí, ktoré spájaš tunelom musí byt' rovnaká. Inak sa Ti tunel nevytvorí. Pozn. prekladateľa : Pokiaľ zabudneš na druhej strane urobit' ukončenie koľajníc, alebo ich tam ani nemáš, Simutrans vie aj v tomto prípade urobit' tunel. Koľajnice potom pripojíš (po navolení koľajníc) k tomuto voľnému koncu kliknutím na vchod do tunela. ;-)
Most - klikni na text : Mosty.
Stanica - nástroj slúži na stavbu železničnej stanice (zvlášt' osobnej a nákladnej). Osobná stanica je zastrešená. Kliknutím myšou na tento symbol sa Ti kurzor zobrazí v tvare jedného segmentu stanice. Samotnú stanicu s požadovanou dĺžkou potom staviaš klikaním na voľné kolajnice. Stanica ale musí ležat' na rovnom teréne. V takejto stanici môžeš nakladat' a vykladat' suroviny, tovar (nákladaná) alebo pasažierov (osobná). Množstvo čakajúcich surovín, tovaru alebo počet pasiažierov pritom nezávisí od dĺžky stanice (pozn. prekladateľa : ale čím je stanica dlhšia, tým viac toho môžeš odniest' alebo priniest' ;-) ). Jeden takýto segment dokáže obslúžit' max. 2 vozidlá (pozn. prekladateľa : prvý segment obslúži 1 vagón - s lokomotívou a ďalšie vždy po 2 vagónoch). Ak je dĺžka vlaku väčšia, presahujúce vagóny nebudú obslúžené. Ak z nejakých dôvodov nebudeš mat' dostatok miesta pre dlhšiu stanicu, máš možnost' postavit' ,,rohovú'' stanicu. Najprv vybuduj úsek, pokiaľ sa dá. Potom pripoj koľajnice zboku klinutím na posledný segment a pokračuj potom pripájaním ďalších segmentov.
Depo - nástrojom môžeš postavit' depo na konci koľajníc, príp. urobit' odbočku a umiestnit' ho na jej koniec. Myšou klikni na nástroj depa a klinutím ho umiestni na žiadané miesto. Všetky ostatné informácie o nákupe vozidiel sa dozvieš kliknutím na vybudované depo a kliknutím na otáznik (pomoc) vedľa nadpisu.
Železničné priecestie - touto voľbou staviaš železničné priecestie. Kliknutím na tento symbol sa kurzor zobrazí ako križovatka železnice s cestou. Naraz môžeš urobit' priecestie cez jednu koľaj. Ak ich tam máš viac, musíš kliknút' na každú jednu osobitne.

simutrans-124.5/simutrans/text/sk/roadtools.txt000066400000000000000000000077021520675460500217670ustar00rootroot00000000000000Nástroje na stavbu cesty - pomoc

Stavba cesty

Tu máš prístup ku všetkým nástrojom, ktoré budeš potrebovat' na stavbu cesty, autobusových zastávok, cestných tunelov, depa a nákladných rámp.

Cesta - nástrojom staviaš cestu medzi dvoma ľubovolne vzdialenými bodmi (štvorčekmi), alebo cestu čast' po časti. Kliknutím na zvolené miesto vytvorenej cesty zase vytváraš križovatky. Postup : Klikni myšou na nástroj cesty a pokračuj kliknutím na bod, z ktorého chceš viest' cestu. Kurzor bude zobrazený ako malý buldozér. Premiestni sa na koniec zamýšľanej cesty a klikni tam myšou. Simutrans vytvorí spojenie medzi týmito dvomi bodmi. Pozn. prekladateľa : pokiaľ by sa Ti cesta zdala príliš kľukatá, máš možnost' urobit' korekcie (zbúrat' tú čast', kde budeš robit' úpravy a potom pridávat' cestu čast' po časti. Stavbu cesty po uhlopriečkach je vhodnejšie robit' čast' po časti. Výsledok je lepší ;-) ). Na stavbu cesty môžeš použit' len voľné pozície (štvorčeky). Ak nechceš križovat' inú cestu, použi stavbu mosta alebo tunela. Ak nie je možné úspešne pokračovat' v budovaní cesty, skús najprv vykonat' terénne úpravy.
Autobusová zastávka - nástrojom staviaš obojsmerné autobusové zastávky. Myšou klikni na nástroj a umiestni ju na cestu. Autobusová zastávka Ti tak umožní prijímat' a vysadzovat' pasiažierov v oboch smeroch. Zastávky je možné budovat' aj za sebou. Autobus tak vždy zastaví na tej, ktorú si mu v cestovnom poriadku označil. Autobusové zastávky nemôžeš budovat' na koľajniciach, v zákrutách alebo priamo na križovatkách.
Tunel - voľbou tohto nástroja bude kurzor zobrazený symbolom tunela. Aby si tunel mohol úspešne vytvorit', musia mat' kopce rovnú stenu na oboch stranách. Pomôž si úpravou terénu. Potiahni cestu ešte o 1-nu pozíciu na kopec a tak isto aj na druhej strane. Myšou klikni na nástroj tunela. Pokračuj kliknutím na jeden z koncov cesty (na kopci). Ak Ti všetko presne lícuje (t.j. trafil si sa), Simutrans urobí tunel a odpočíta Ti náklady na stavbu. Nadmorská výška ciest, ktoré spájaš tunelom musí byt' rovnaká. Inak sa Ti tunel nevytvorí. Pozn. prekladateľa : Pokiaľ zabudneš na druhej strane urobit' ukončenie cesty, alebo ju tam ani nemáš, Simutrans vie aj v tomto prípade urobit' tunel. Cestu potom pripojíš (po navolení cesty) k tomuto voľnému koncu kliknutím na vchod do tunela ;-).
Cestný most - klikni na text : Mosty.
Nákladná rampa - nástroj slúži na stavbu nákladnej rampy. Kliknutím myšou na tento symbol sa Ti kurzor zobrazí v tvare nákladnej rampy. Nákladná rampa musí ležat' na konci cesty alebo konci odbočky a musí byt' na rovnom teréne. Môžeš postavit aj niekolko rámp vedľa seba. Tu môžeš nakladat' a vykladat' suroviny aj tovar. Nákladné autá budú chodit' iba na tie rampy, ktoré im označíš v cestovnom poriadku. Množstvo čakajúcich surovín alebo tovaru pritom nezávisí od počtu takýchto rámp vedľa seba (pozn. prekladateľa : ale čím je viac rámp vedľa seba, tým viac toho môžeš naraz odniest' alebo priniest' ;-) ). Jedna takáto rampa dokáže obslúžit' max. 2 vozidlá (vrátane prívesov).
Depo - nástrojom môžeš postavit' depo na konci cesty, príp. urobit' odbočku a umiestnit' ho na jej koniec. Myšou klikni na nástroj depa a klinutím ho umiestni na žiadané miesto. Všetky ostatné informácie o nákupe vozidiel sa dozvieš kliknutím na vybudované depo a potom kliknutím na otáznik (pomoc) vedľa nadpisu.
Železničné priecestie - touto voľbou staviaš železničné priecestie. Kliknutím na tento symbol sa kurzor zobrazí ako križovatka železnice s cestou. Naraz môžeš urobit' priecestie cez jednu koľaj. Ak ich tam máš viac, musíš kliknút' na každú koľaj osobitne.

simutrans-124.5/simutrans/text/sk/save.txt000066400000000000000000000010261520675460500207100ustar00rootroot00000000000000Uložit' hru - pomoc

Uložit' hru

V dialógovom okne sa zobrazí zoznam názvov všetkých dovtedy uložených súborov. Ak chceš prepísat' existujúci súbor, klikni na príšlusný názov (identifikátor) súboru. Simutrans uloží Tvoju hru s týmto názvom.

Ak chceš ulozit' hru s novým názvom, klikni na horné textové pole a zadaj svoj názov súboru. Kliknutím na ,,OK'' bude nový súbor so zadaným názvom uložený. Hru vymazeš kliknutím na značku ,,X'' vedľa názvu.

simutrans-124.5/simutrans/text/sk/schedule.txt000066400000000000000000000043371520675460500215560ustar00rootroot00000000000000Cestovný poriadok - pomoc

Cestovný poriadok

Popis tlačidiel :

Pridat' - toto tlačidlo je aktívne hneď po zobrazení. Namiesto kurzora s lupou by mal byt' žltý značkovač, ktorým pooznačuj v logickom slede všetky zastávky, stanice alebo nákladné rampy.
U každej zastávky, stanice alebo nákladnej rampy môžeš určit' s akým minimálnym vyt'ažením bude dopravný prostriedok odchádzat' (u nákladných dopravných prostriedkov odporúčam pre nakladanie 80-100%, u autobusov je lepšie ostat' na 0%). Dĺžku vlakov odporúčam väčšiu ako u konkurencie.
Ak by si vykonával nejaké úpravy terénu, cesty alebo železnice počas zadávania cestovného poriadku a vrátil sa spät' k zadávaniu, musíš kliknút' napr. na tlačidlo ,,Vložit' '' a následne na ,,Pridat' ''. Tým obnovíš zobrazenie žltého značkovača a môžeš pokračovat' v zadávaní.
Maximálny počet zastávok alebo staníc v cestovnom poriadku môže byt' 16 ! (Obmedzenie ich fyzického počtu na mape som zatiaľ nezistil).
Tip : Ak máš zapnutú konkurenciu, konkurencia môže (ale nemusí) mat' vyt'aženie menšie ako 100% a chodit' tak poloprázdna až takmer prázdna. Vykazuje tým nižší zisk. Pri odporúčanom nastavení vyt'aženia nákladných dopravných prostriedkov môžeš takto konkurenciu predbehnút' v zisku. Ak prevážaš suroviny alebo tovar, kde je aj konkurencia, tam je lepšie nastavit' minimálne vyt'aženie na 100% (=plne naložit') ;-).

Vložit' - zapnutím tohto tlačidla môžeš vložit' pred označenú zastávku alebo stanicu ďalšiu (napr. po rozšírení siete zastávok alebo staníc). Opät' pozor na maximálny počet zastávok alebo staníc.

Zmazat' - zapnutím tohto tlačidla môzeš vymazat' ľubovolnú zastávku, stanicu alebo nákladnú rampu z cestovného poriadku (zastávka, stanica alebo nákladná rampa ale fyzicky zostáva, až kým ju nezbúraš)

Hotovo - potvrdením ukončíš zadávanie cestovného poriadku.

Pozn. prekladateľa : Pri zadávaní cestovného poriadku musíš kalkulovat' s trasou tak, aby si neprekročil maximálny počet zastávok.

simutrans-124.5/simutrans/text/sk/shiptools.txt000066400000000000000000000024571520675460500220070ustar00rootroot00000000000000Lodná doprava - pomoc

Nástroje lodnej dopravy

Tu máš prístup k všetkým pomôckam, ktoré potrebuješ na vybudovanie lodného parku a dokov.

Ako prvý nástroj je prístavné mólo. Prístavné mólo musí stát' na okraji pevniny. Móla môžeš ukladat' vedľa seba a tým zväčšovat' ich obslužnú kapacitu. Mólo vložíš tak, ze klikneš myšou na symbol ,,dlaždica'' a umiestniš ho na požadované miesto, najčastejšie v blízkosti nejakého priemyselného odvetvia. Ak si ho umiestnil správne, ostane tam. Ďalšou pomôckou je stavba doku.
Dok postavíš podobným spôsobom ako prístavné mólo, ale dok musí stát' na vode ! Môžeš si vybrat' medzi ľavým a pravým dokom. Akonáhle máš dok postavený, môžeš kupovat' lode kliknutím na dok, zadat' cestovný poriadok - čo, odkiaľ a kam prevážat'.
Pamätaj na to, že lode budú pri väčších mólach zastavovat' na tom úseku (mieste), na ktorom si im označil zastávku pri zadávaní cestovného poriadku.

Pozor, doky nemôžu byt' vzdialené od móla !
Musia stát' vedľa móla. Kvôli terénu sú preto k dispozícii ľavý a pravý dok. Inak sú obe zhodné a ponúkajú nákup rovnakých typov lodí.

simutrans-124.5/simutrans/text/sk/sound.txt000066400000000000000000000007321520675460500211050ustar00rootroot00000000000000Zvuky a hudba - pomoc

Nastavenia zvukov a hudby

V tomto menu nastavuješ hlasitost' zvukov, hlasitost' hudby a ktorú skladbu chceš aktuálne prehrat'.

Pre zmenu hlasitosti zvukov a hudby uchop myšou jazdec posuvníka a posuň ho do želanej polohy. Posunom vľavo uberáš a posunom vpravo pridávaš hlasitost'. Na zmenu hlasitosti a zmenu prehrávanej skladby môžeš pouzit' aj klikanie na príslušné šípky posuvníkov.

simutrans-124.5/simutrans/text/sk/station.txt000066400000000000000000000030271520675460500214360ustar00rootroot00000000000000 Stanica, zastávka

Stanica, zastávka a nákladná rampa

Tu sa dozvieš informácie o stanici, zastávke, alebo nákladnej rampe, ktorú si zvolil. Prvou informáciou je čakajúci tovar, ktorý je potrebný pre blízko ležiace priemyselné odvetvia a na ktoré stanice alebo zastávky sú priame spojenia.

Môžeš tiež premenovat' svoju stanicu alebo zastávku - klikni na textové pole s názvom svojej stanice alebo zastávky. Objaví sa kurzor, čo znamená, ze môžeš upravovat' názov. Po úpravách názvu stlač kláves Enter.

Informácia o zastávke zobrazuje, ktorý tovar alebo pasažieri čakajú na stanici. Informácie sú usporiadané tak, že každá má svoje záhlavie pre rozdielny typ tovaru a iné, napr.
402 áut čaká
83 pasažierov čaká

a každá informácia z tohoto záhlavia ešte môže obsahovat' podrobnejšie údaje (podriadené). Tieto udávajú cieľ, kam má tovar íst' a presné množstvo tovaru alebo pasažierov napr.

56 pasažierov čaká
- 25 pasažierov > Prešovská Kopanica BF
- 29 pasažierov > Trnavské Mesto cez Svitovská Dedina východ
- 2 pasažieri > Košické Mesto Hlavné H

Úplne vpravo nájdeš tlačidlo ,,Detaily''. Obsahuje informácie o tovare potrebnom pre ďalší priemysel, priamych spojeniach s ďalšími stanicami alebo zastávkami a tovar, ktorý sa sem priváža alebo odtiaľto odváža preč. Všetko je usporiadané tak, že okamžite máš prehľad odkiaľ, čo a kam ide.

simutrans-124.5/simutrans/text/sv.tab000066400000000000000000001156521520675460500177270ustar00rootroot00000000000000Svenska PROP_FONT_FILE Prop-Latin1.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: sv Svenska # # Encoding: ISO-8859-1 # # Font: Prop-Latin1.bdf # # Date Created: 2.06 2024 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ AMBIENT_SOUND omgivningsljud CASH_SOUND kassakling cl_btn_filter_disable av cl_btn_filter_enable p cl_btn_filter_settings instllningar cl_btn_sort_asc stigande cl_btn_sort_desc fallande cl_btn_sort_id ID-nummer cl_btn_sort_income intkter cl_btn_sort_name beteckning cl_btn_sort_type typ clf_btn_alle alla clf_btn_invers byt clf_btn_keine inga gl_btn_sort_bonus bonus gl_btn_sort_name namn gl_btn_sort_revenue intkter gl_btn_unsort osorterade hl_btn_filter_disable av hl_btn_filter_enable p hl_btn_filter_settings instllningar hl_btn_sort_asc stigande hl_btn_sort_desc fallande hl_btn_sort_name beteckning hl_btn_sort_type typ hl_btn_sort_waiting Vntande hlf_btn_alle alla hlf_btn_invers byt hlf_btn_keine inga Networks Linjent Queueing ktrend Road toll Vgtull Scenario Scenario info #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Polarklimat desert kenklimat mediterran Medelhavsklimat rocky Fjllklimat temperate Tempererat klimat tropic Tropiskt klimat tundra Tundra #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Laddningen av scenario skriptet (.nut) misslyckades. Can't buy obsolete vehicles! Du kan inte kpa utgngna fordonsmodeller! Cannot built depot here! Fordonshall kan inte byggas hr! Cannot built this station/building\nin underground mode here. Byggnaden kan enbart\nbyggas ovan jord.\n\n Cannot create generic line!\nSelect line type by\nusing filter tabs. Det gr inte att skapa en linje av generell typ.\nVlj linjetyp med hjlp av filtreringsflikarna. Cannot create socket Kan inte skapa sockel Convoi handles exhausted! Strsta tilltna antal konvojer har ntts. Convoy already deleted! konvojen har redan raderats! Das Feld gehoert\neinem anderen Spieler\n Den hr mark-\nbiten gs av\nen annan spelare!\n Der Besitzer erlaubt das Entfernen nicht garen tillter\ninte att\ndetta tas bort!\n Diese Zusammenstellung kann nicht fahren!\n Denna kombination\nkan ej kra!\n Flugzeughalt muss auf\nRunway liegen!\n Flygplan kan bara\nlanda p flygplatser!\n Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Hr kan man inte\nbygga den hr flygplats-\nbyggnaden.\n Hier kann kein\nSignal aufge-\nstellt werden!\n Hr kan man\ninte bygga\nngon signal! In order to lock the game, you have to protect the public player by password! Fr att kunna lsa spelet krvs det att den allmnna spelare r skyddad med lsenord! Lost connection\nto server! Tappat anslutningen\till servern! Lost synchronisation\nwith server. Ej lngre\nsynkroniserad\nmed servern! Maglevhalt muss auf\nMaglevschiene liegen!\n En magnetsprsstation mste placeras p magnetspr! Monorailhalt muss auf\nMonorail liegen!\n Monorailhllplatser\nmste placeras p monorail. Monorails are not available yet! Det gr nnu inte att bygga monorail. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Smalsprsstation mste\nligga p ett smalspr!\n No through station here! Hr kan inte byggas ngon hllplats. Not enough money! Pengarna rcker int\ntill fr att bygga detta! On narrowgauge track only!\n En smalsprshllplats mste ligga p smalspr. Out of funds Du har inte tillrckligt med pengar fr detta! Post muss neben\nHaltestelle\nliegen!\n En stationstillbyggnad (t ex\n\npostkontor, lager)\n\nmste byggas p en fri\n\nyta i direkt anslutning\n\ntill en hllplats! Protocoll error (expecting game) Protokollfel (spel frvntades) Schiffhalt muss im\nWasser liegen!\n Fartygshllplatser\nmste placeras\ni vatten!\n Server busy Servern upptagen Terraforming not possible\nhere in underground view Det gr inte att frndra\nlandskapet p detta stt under jord.\n Upgrade must have\na higher level Kan enbart uppgraderas till hgre niv Vehicle %s cannot choose because stop too short! %s kan inte vlja plattform eftersom alla r fr korta! Zughalt muss auf\nSchiene liegen!\n Tghllplatser\nmste placeras\np jrnvgsspr!\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Hjlp

Hjlp Index

Om Simutrans

%1$s

Att anvnda

%2$s

Att starta ett spel

%4$s

Hur man spelar

%5$s

Verktyg

%3$s

vriga fnster

%6$s Keyboard Help\n

Keyboard Help

\n Tangentbordshjlp\n\n

Tangentbordshjlp

\n

\nTangentbordshjlp visar vilka funktioner som aktiveras av tangentbordets olika tangenter.\n

\n

\nTangentbordshjlp ppnas nr en tangent som ej r kopplad till ngon funktion trycks ner eller frnAllmn hjlp.\n

\n

\nTangentnedtryckningar r skiftlgesknsliga (anvnd [Skift] fr stora bokstver).\n

\n

\nExempel p tangenter som r kopplade till en viss funktion r:\n

\n

\n[Piltangenterna]: rulla spelplanen i tangentens riktning.
\n[Baktsteg]: stng alla fnster, verktygsmenyer och hjlptexter som finns ppna p spelplanen.
\n[Delete] eller [Escape]: stng det versta fnstret, verktygsmenyn eller hjlpfnster som finns ppet p spelplanen.
\n[Enter] eller [Return]: anvnds fr att bekrfta handlingar.
\n[Page-Up] eller [>]: zooma in p spelplanen.
\n[Page-Down] eller [ mindre n-symbolen]: zooma ut p spelplanen.\n[F1]: ppna Simutrans-hjlpen.

\n

\n[1]: rulla spelplanen mot sder.
\n[2]: rulla spelplanen mot sydst.
\n[3]: rulla spelplanen mot ster.
\n[4]: rulla spelplanen mot sydvst.
\n[6]: rulla spelplanen mot nordst.
\n[7]: rulla spelplanen mot vster.
\n[8]: rulla spelplanen mot nordvst.
\n[9]: rulla spelplanen mot norr.\n

\n

\n[Skift] + mouse: anvnd p kartan fr att se lnkar i industrinfrsrjningskedjan.
\n[CTRL] + verktyg: bygg (signaler & Stationer) p den vre nivn; eller bygg lngsammare vgar and jrnvgar ver existerande; eller bygg rakare vgar och jrnvgar.
\n[CTRL] + ([ Overlay city limits Visa stadsgrnser Overlay passenger destinations when a town window is open Visa resml fr passagerare i den stad vars informations fnster r ppet Overlay town names Visa stadsnamn Show capacity and if halt is overcrowded Visa kapacitet och status Show speedlimit of ways Visa vgarnas hastighetsbegrnsningar #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n Nu kan turisterna\nka i %s regi\nmellan %s\noch %s p (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s kr nu arbetare\nfrn %s\ntill fabriken %s\n p (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nhar nu\n%i lastbilar i trafik mellan\n%s vid (%i,%i)\noch %s\nvid (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\nppnade en ny jrnvg\nmellan %s\nvid (%i,%i) och\n%s\nvid (%i,%i).\n\n Airline service by\n%s\nnow between\n%s \nand %s.\n Nu flyger %s mellan\n%s och %s!\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Skepp ohoj!\n%s kryssningsfartyg\ntar nu passagerare frn\s%s till %s. Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Det finns nu en ny\nfrbindelse i\n%ss\nregi mellan %s\noch %s. #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Kartredigerare LISTTOOLS Listor MAGLEVTOOLS Magnetsprsverktyg MONORAILTOOLS Monorailverktyg NARROWGAUGETOOLS Verktyg fr smalspr RAILTOOLS Jrnvgsverktyg ROADTOOLS Vgverktyg SHIPTOOLS Kanaler, hamnar och varv SLOPETOOLS Markhjdsverktyg SPECIALTOOLS Specialbyggnader TRAMTOOLS Sprvgsverktyg #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). Ett nytt huvudkontor har byggts av %s. Factory chain extended\nfor %s near\n%s built with\n%i factories. Ekonomin r stark:\n%s nra %s utvidgar.\n%i nya fabriker har ppnat. New %s now available:\n%s\n %s av typen \n'%s'\nhar brjat tillverkas.\n New factory chain\nfor %s near\n%s built with\n%i factories. Industritillvxt: Ny fabrikskedja\ntill %s nra\n%s byggd med\n%i fabriker. New vehicle now available:\n%s\n \nEtt nytt fordon r\n nu tillgngligt:\n\n\n -- %s --\n\n Now %u clients connected. Just nu %i spelare anslutna. Remove vehicle from map. Use with care! Fordonet tas bort. Frsljningsvrdet tillgodoskrivs som vinst. Screenshot\ngespeichert.\n Skrmbild\nsparad.\n Sends the convoi to the last depot it departed from! Snder konvojen till fordonshallen den startade frn. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. I %s\ninvigs ett nytt\nmonument.\n%i skdare deltar\ni festligheterna. #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (i fordonshall) \nBauzeit bis t o m \nBauzeit von \nKan byggas fr o m \nCan't open heightfield file.\n \nKan inte ppna hjdkartefilen.\n \ndirection: \nRiktningar: \nelektrified \nelektrifierad\n \nHeightfield has wrong image type.\n \nFel filtyp.\n \nis reserved by: \ntgvg r lagd fr: \n \nminimum speed: \nminsta tilltna hastighet: \nnot elektrified \nej elektrifierad\n \nRibi (masked) \nriktningar (dolda): \nRibi (unmasked) \nriktningar (alla): \nSet phases: \ange tidsintervall NS/V riktning: \nsingle way \nEnkelriktat \nway1 reserved by \nvg 1 r reserverad av \nway2 reserved by \nvg 2 r reserverad av \nwith sign/signal\n \n med skylt/signal\n %d buildings\n %d byggnader\n %d convois %d konvojer %d Einzelfahrzeuge im Depot Hr frvaras %d fordon %i km/h (max. %ikm/h) %i km/t (max %i km/t) %i years %i months old. %i r %i mnad(er) gammal. %s building %s %s %s %s %s %s city %d %s %s %d stad %s %s factory %s %s %s %s gods%s %s has entered a depot. %s\nhar ntt fordonshall %s land %d %s %s %d yttre %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. Borgmstaren vgrar frysa mer:\nNytt rdhus byggt i %s\n nr staden blev %i invnare stor.\n %s\nis crowded. %s r verfull. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\nhastighet %i\nhgsta hastighet %i\ndx:%i dy:%i %s\nwas liquidated. %s r bankrutt och\nhar blivit likviderad.\nAll verksamhet har upphrt\noch alla tillgngar slts. %u Client(s)\n %i klient(er)\n %u Player (%u locked)\n %i Spelarplatser (%i lsenordsskyddade)\n < Ny linje > 1 convoi 1 konvoj 1 Einzelfahrzeug im Depot Hr frvaras 1 fordon 1LIGHT_CHOOSE Ljusstyrka: 1WORLD_CHOOSE Instllningar fr nytt spel: 2LIGHT_CHOOSE Frger: 2WORLD_CHOOSE Kartnummer: 3LIGHT_CHOOSE Rullningsfart: 4LIGHT_CHOOSE Omvnd rullning 5LIGHT_CHOOSE Fotgngare vid stationer 5WORLD_CHOOSE Antal stder: 6LIGHT_CHOOSE Fotgngare i stder 6WORLD_CHOOSE Trafiktthet: 8WORLD_CHOOSE Skilj p dag och natt A bridge must start on a way! Broar mste brja/sluta\ni nden av en befintlig \nvg eller p en tom \nsluttning! Abfrage Granskningsverktyg Abnehmer Konsumenter About Om About scenario Copyright Abriss Rivningsverktyg Absenken Snk marknivn Abspanntransformator Transformatorstation Accelerate time Accelerera tidsfrlopp Act. load: %u MW\n Frbrukning: %u MW\n Active player only Endast aktiv spelare Add forest Lgg till skog Add random citycar Lgg till slumpmssig stadsbil add server Lgg till server Add Stop Lgg till Add stops for backward travel Lgg till stopp p vgen tillbaka. air flyg aircraft_tab Fraktflygplan airplane Flygplan Airport Flygplats AIRTOOLS Flygplatsverktyg All Alla all convoi tooltips alla konvojtips alltid Allow city growth Tillt stadstillvxt Allow player change Tillt spelarbyte allowed climates:\n Klimatzoner: Alters a schedule. ndrar en tidtabell Angenommene Waren --Varor som kan levereras hit anhaengen Hng p Anhaenger_tab Slpvagnar Anheben Hj marknivn Appends stops at the end of the schedule Lgg till stopp i slutet av tidtabellen Apply Line Anvnd linje Arbeiter aus: Arbetarna bor i: Arrivals from\n Ankommande frn\n Arrived Varuleverans Assets Fordonsvrde Aufloesen Uppls Aufspanntransformator Transformatorstation August Augusti Autohalt muss auf\nStrasse liegen!\n Buss- och lastbilshllplatser mste ligga p en vg.\n Available Tillgnglig Bahndepot Lokstall/vagnhall Bankrott:\n\nDu bist bankrott.\n Bankrutt:\n\nDu r bankrutt.\n battery Batteri Baum Trd baum builder Plantera trd Baustelle Byggplats Bauzeit Byggnadstid Beenden Avsluta Beginner mode Nybrjarlge Besonderes Gebaeude Turistattraktion BF station bio biologisk Blockstrecke ist\nbelegt\n Sprblocket r\nupptaget\nav ett annat tg\n Boden Mark Bonusspeed: %i km/h Hgsta mjliga hastighet: %i km/h bridge is too high for its type! Dalen r fr djup fr\ndenna typ av bro!\n Bridge is too long for this type!\n Spannet skulle bli fr\nlngt fr denna typ av bro!\n Bruecke Bro Bruecke muss an\neinfachem\nHang beginnen!\n Broar mste\nbrja p\nraka sluttningar!\n Brueckenboden Bro Build air depot Hangar build choosesignals Infartssignaler Build city market Ny affrsbyggnad Build drain Transformatorstation build HQ Huvudkontor Build land consumer En ny industrikedja kommer att byggas Build maglev depot Maglevfordonshall Build monorail depot Monorailfordonshall Build narrowgauge depot Smalsprsfordonshall Build powerline Kraftledning Build presignals Tvblockssignaler Build road depot Garage Build ship depot Bygg skeppsvarv Build signals Bygg signaler Build train depot Bygg lokstall/vagnhall Build tram depot Bygg sprvagnshall Build truck depot Bygg garage Building costs estimates Uppskattad byggkostnad Buildings Antal hus Built artifical slopes Bygg konstgjorda sluttningar Built random attraction Ny sevrdhet Bus_tab Bussar Can only move from halt to halt or waypoint to waypoint. Kan endast flytta \nfrn hllplats till hllplats eller \nfrn vgpunkt till vgpunkt. Cancel Avbryt Cannot connect to offline server! Kan inte ansluta till server som r offline! Capacity Kapacitet Capacity: Kapacitet: Capacity: %.0f MW Kapacitet: %.0f MW\n Capacity: %d%s %s\n Kapacitet: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Kapacitet: %s\n? %d (%d%%) Cars are not available yet! Det finns inte ens hstar n!\nBrja frn frsta knallen... cars.\nstate fordon\n Cash Konto Change player Byt spelare Chart Statistik Chat_msg Chatt Choose direction Vlj riktning Choose operation executed on clicking stored/new vehicles Vlj vad som skall hnda nr du klickar p nya fordon eller fordon i fordonshall. chooses a random map Vljer slumpkarta. citicens Medborgare City attraction Stadsattraktion City industries Affrer i stder City list Stadslista City size Storlek city_road Gata citybuilding builder Bygg stadsbyggnader CityLimit Stadsgrnser cl_title Fordonslista cl_txt_sort Sortera efter: Clear block reservation Visa/ls upp lagda tgvgar clf_chk_aircrafts Flygplan clf_chk_cars Landsvgsfordon clf_chk_indepot I fordonshall clf_chk_maglev Maglevtg clf_chk_monorail Monorailtg clf_chk_name_filter Filternamn: clf_chk_narrowgauge Smalsprstg clf_chk_noincome Inga intkter clf_chk_noline Ingen linje clf_chk_noroute Ingen rutt clf_chk_noschedule Ingen tidtabell clf_chk_obsolete frldrad clf_chk_ships Fartyg clf_chk_spezial_filter Specialfilter: clf_chk_stucked Blockerad clf_chk_trains Tg clf_chk_trams Sprvagnar clf_chk_type_filter Filtrera typ: clf_chk_waren Varufilter: clf_title Fordonslista - instllningar Climate Control Vlj klimat closed stngd COLOR_CHOOSE\n Vlj en frg\nfrn paletten:\n Company bankrupt Fretaget r satt i konkurs! Company_msg Konkurrenter Comparing pak files ... Jmfr PAK filer ... Configure AI Konfigurera AI Configure AI setttings Justera parametrar fr datormotstndare Congratulation\nScenario was complete in\n%i months %i years. Gratulationer!\nDetta scenario har \nspelats klart p \n%i mnader och %i r! Connected stops Hllplatser (med anslutning till denna fabrik) Connected with server Anslut till server Constructed by Skapad av Constructed by %s Skapad av %s Construction_Btn Byggkostnader Consumed Frbrukat convoi %d of %d Konvoj %d frn %d convoi error tooltips alltid konvojfel Convoi has been sent\nto the nearest depot\nof appropriate type.\n Fordonet har skickats\ntill nrmaste fordons-\nhall av lmplig typ. Convoi is sold when all wagons are empty. Konvojen kommer att tas bort s fort som den r tmd. Frsljningsvrdet kommer att tillgodogras kassan. convoi mouseover tooltips konvojstatus vid muspekning convoi passed last\nmonth %i\n \nkonvojer som passerade\nfrra mnaden: %i\n Convois Konvojer Convois: %d\nProfit: %s Fordon: %d\nVinst: %s Convoys Konvojer Copy Convoi Kopiera konvoj Copy the selected convoi and its schedule or line Gr en kopia av konvojen med dess tidtabell eller linje Cost Kostnad cost for removal Kostnad fr att ta bort Costs Kostnader Create a new line based on this schedule Skapa en ny linje baserad p denna tidtabell. curiosity builder Bygg sevrdheter curlist_title Turistml Currently playing: Spelar just nu: Deccelerate time Snk spelhastigheten decrease underground view level Snk kartans utsnittshjd. Del Stop Ta bort Delete Line Ta bort linje Delete the current stop Ta bort nuvarande hllplats Delete this file. Ta bort denna fil. Delivered Levererat Demand Efterfrgan Demand: %.0f MW Efterfrgan: %.0f MW\n Denkmal Minnesmrke Departed Avgtt Depots Fordonshallar Der Tunnel ist nicht frei!\n Tunneln r inte tom.\n Details Detaljer Die Bruecke ist nicht frei!\n Bron r inte tom!\n Direkt erreichbare Haltestellen --direkta trafikfrbindelser: disable midi Tysta MIDI-musiken Display settings Visning: Distance Frdstrcka Dock Hamn Dock must be built on single slope! Hamnar mste byggas\np en rak sluttning! Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Du har %d mnader p dig att betala dina skulder. Durchsatz Max. Economy Ekonomi och stder Eigenbesitz\n Allmn egendom\n Ein %s\npasst hier nicht.\n En '%s'\npassar inte hr.\n Einstellungen aendern ndra alternativ electric elektrisk Electricity Strmfrsrjning Electricity producer\n\n Elektricitetsproducent\n\n Electrics_tab Elektricitet Electrify track Elektrifiera spret enlarge map Gr kartan strre enter a value between %i and %i Vlj ett vrde mellan %i och %i. Enter address Server adress Enter Password Ange lsenord Error Ett fel har uppsttt... Erzeuge neue Karte.\n Vnta,\nskapar en\nny karta...\n\n(Det hr kan ta\nngra minuter\np stora kartor.)\n Es wird bereits\nein Fahrplan\neingegeben\n Du hller redan p att\nsammanstlla en tidtabell! Fabrikanschluss --Fabriker som har anslutning till denna hllplats Fabrikname Fabriksnamn Factories Fabriker factory details Fabriksinformation factorybuilder Bygg industrianlggningar Fahrplan Tidtabell Fahrtziel Frdml: Fahrzeuge koennen so nicht entfernt werden Det gr inte att ta bort\nfordon/fartyg p det hr sttet!\n Fahrzeuge: Fordon/fartyg: Farbe Spelarfrg Fast forward Snabbspola February Februari Ferry_tab Frjor Fertig Klar Filename Filnamn: Finances of %s Finanser fr %s Finanzen Finanser fl_title Fabrikslista Flug_tab Passagerarflygplan follow me Flj mig Follow the convoi on the map. Flj denna konvoj. Forest Skog Found new city Grunda ny stad FPS: Bildhastighet (fps): Fracht Frakt Frame time: Bildfrekvens: Free Capacity Fri kapacitet freeplay mode ingen bankrutt Friction: Nuvarande friktionsfaktor: fuel_cell brnslecell Full load Minimilast: Fussgaenger Fotgngare GAME PAUSED Spelet r pausat Gear: Utvxling: Gebaeude Byggnader General Allmnt Generation: %.0f MW Produktion: %.0f MW\n Gewicht Vikt Gewinn Vinst: Give the selected vehicle(s) an individual schedule Tilldela de valda fordonen en egen tidtabell. gl_btn_sort_catg typ gl_title Varulista go home Till fordonshall Goods Varor Goods AI Frakt-AI Goods list Varuversikt Gross Profit Bruttovinst Groundobj Freml Grow city Utka invnarantal Growth Tillvxt Happy Tillfreds Haus kaufen Kp hus Helligk. Visning Help Hjlp Help text not found Det gr bara att\nvisa hjlptexter som\nredan finns. Stll\ndin frga till\nett av Simutrans-forumen. hide all building Krymp alla byggnader hide city building Krymp stadsbyggnader hide station names Dlj stationsnamn hide transparent Genomskinlig istllet fr krympt hide trees Krymp trd Hier warten/lagern: Vntande varor och passagerare: hl_title Stationslista hl_txt_filter Filter: hl_txt_sort Sortera efter: hlf_chk_airport Flyg hlf_chk_anleger Sjfart hlf_chk_bahnhof Jrnvg hlf_chk_bushalt Buss hlf_chk_frachthof Lastbil hlf_chk_keine_verb Otrafikerad hlf_chk_maglevstop Maglev hlf_chk_monorailstop Monorail hlf_chk_name_filter Filternamn: hlf_chk_narrowgaugestop Smalsprsstation hlf_chk_overflow verfull hlf_chk_spezial_filter Specialfilter: hlf_chk_tramstop Sprvagn hlf_chk_type_filter Filtrera typ: hlf_chk_waren_abgabe Utgende varor: hlf_chk_waren_annahme Inkommande varor: hlf_title Stationslista - instllningar Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Vet inte var fordonet \nhr hemma! Lgg\nmanuellt till\ndetta i frdplanen. Homeless Hemlsa hydrogene vtgas Idle: Tomgng: ignore climates Alla klimatzoner In Transit Under frd Increase Industry density Hj industridensiteten increase underground view level ka kartans utsnittshjd. industrial building Industribyggnader Init map ... Kartan lses in... Input tillfrd Ins Stop Infoga Insert stop before the current stop Infoga stopp fre den valda hllplatsen Intercity road len: Lngd p vgar mellan orter: Intro. date: Introduktion: invalid odefinierad Invalid coordinate Ogiltig order isometric map Isometrisk karta January Januari join game Anslut till spel! July Juli Jump to Centrera runt June Juni Kann Spielstand\nnicht laden.\n Kan inte ppna\ndet sparade spelet!\n Kann Spielstand\nnicht speichern.\n Kan inte spara\nspelet!\n Kein Besitzer\n Ingen gare\n keine inga Keine Einzelfahrzeuge im Depot Hr frvaras inga fordon Keyboard_Help\n Tangentsbordshjlp\n koord koordinater Kreuzung Jrnvgskorsning labellist_title Lista ver skyltar Lade Relief Ls in hjdkarta Laden Ls in Land attraction Utflyktsml Land industries Industrintverk LANG_CHOOSE\n Vlj ditt sprk:\n Last Year Frra ret Leaving depot! Lmnar fordonshall! leer tom Legend Kartinfo Leistung Effekt Leistung: %d kW Effekt: %d kW Leitung Hgspnningsledningar letzen Monat: diesen Monat: Frra mnaden: Den hr mnaden: Line Linje Line Management Linjehanteraren Lineless convoys serving this stop Ej linjeanknutna konvojer som betjnar denna hllplats Lines serving this stop --Linjer som trafikerar denna hllplats LKW_tab Lastbilar Load game Ladda spel load height data from file Ls in hjddata frn fil. Load scenario Ladda scenario loaded lastad loaded passenger/freight Sortera pass./varor efter Loading (%i->%i%%)! Lastar (%i->%i%%) Loading addon paks ... Lser in tillggspaket (addon paks) Loading map ... Kartan laddas... Loading paks ... Lser in paket (pak) ... Loading skins ... Lser in skinn ... Lock game Tillt inte spelarbyte under spelets gng (krver konfirmation). Lokomotive_tab Lokomotiv m3 m Maglev Magnetspr maglev vehicle Magnetsprsfordon maglev_track Magnetrls Maglevdepot Magnetsprsvagnhall Mailbox Meddelanden Mailbox Options Meddelandealternativ Maintenance Underhll make stop public (or join with public stop next) costs %i per tile and level Skapa eller anknyt till existerande allmn hllplats (%i$ per ruta och niv) Manual (Human) Mnniska (utan AI) Manufactured: Tillverkad: Map roughness Bergighet map zoom zoom March Mars Margin (%%) Marginal Marker Skapa en skylt Max income: Max. inkomst: Max. speed: maxhastighet: Maximum 254 stops\nin a schedule!\n Det fr inte finnas mer n\n254 stopp i en tidtabell!\n maximum length of rivers Lngsta flodlngd Maximum tile height difference reached. Hjdskillnaden\nmellan tv rutor\nfr inte verstiga\ntv steg. Maxspeed Hgsta hatstighet May Maj Median Citizen per town Medianstorlek fr stder: Meldung Meddelande Menge antal MessageOptionsText \nNytt r\n\nAI:n bygger\n\nStadsnyheter\n\nRutt saknas\n\nNy industri\n\n"chat"\n\nNy fordonstyp\n\nFulla stationer\n\nGenerella problem\n\nVarningar\n\nScenario min min. minimum length of rivers Minsta flodlngd Modify the selected line ndra in den valda linjen Monate alt mnader gammal. monorail vehicle Monorailfordon monorail_track Monorailrls Monorailboden Upphjd vgstrcka Monoraildepot Monorailvagnhall month wait time Mnaders vntetid Months Mnader Monuments Monument Mountain height Bergshjd Movingobj Rrliga Music playing disabled/not available Musik avstngd/ej tillgnglig Music volume: Musikvolym: mute sound Stng av allt ljud Name Namn Narrowgauge Smalspr Narrowgauge are not available yet! Smalspr finns nnu inte tillgngligt! narrowgauge vehicle Smalsprsfordon narrowgauge_track Smalspr Narrowgaugedepot Smalsprsvagnhall Net ID: %p Nt-ID: %p\n Net Wealth Nettotillgngar Neue Karte Ny karta Neue Welt Skapa ny vrld new convoi Ny konvoj New Line Ny linje New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Ny linje skapad!\nDu kan nu vlja att\nknyta konvojen\ntill den ovan. New Vehicles Nya fordon no buildings hidden Inga byggnader krympta no convois Inga konvojer No goods are loaded onto this convoi. Lasta inte p mer. no goods waiting inga varor vntar no load Ingen last No Route Ingen rutt No stop here! Verktyget gr enbart att anvnda p hllplatser! No suitable ground! Olmplig mark! No terminal station here! En ndstation kan\nendast byggas vid slutet\nav en led! no timeline utan tidslinje no tree Inga trd Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Det finns nnu inte ngon\ntidtabell att kra efter! none ingen nord Norra nordost Nordstra nordwest Nordvstra Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Detta gr inte!\nFordonets tidtabell kan\nfr nrvarande inte ndras.\nFrsk igen senare! Not enough fields would remain. Det mste finnas ett\nvisst antal flt\nkvar runt varje bondgrd. Now active as %s.\n Du r nu spelare:%s.\n Number of rivers Antal floder Object Objekt Odometer: %s km Frdmtare: %s km Ok OK On loan since %i month(s) Kontot vertrasserat i %i mnad(er) On this map, you are not\nallowed to change player!\n Detta spel r lst.\nInga spelarbyten r tilltna\np denna karta.\n Only city chains Endast stadsntverk Only land chains Endast fabriker utanfr stder Only one transformer per factory! Endast en transformatorstation vid varje fabrik! open ppnad Operation Driftskostnader Ops Profit Driftsvinster Optionen Alternativ Origin Avgngsort ost stra Output resultat paletten lastpallar Pas_tab Passagerartg Passagiere Passagerare Passagierrate Passagerartal Passagierziele Destinationer fr pass. och post Passenger AI Passagerar-AI Passengers %d %c, %d %c, %d no route Passagerare %d %c, %d %c, %d utan rutt Passengers %d %s, %d %s, %d no route Passagerare %d %s, %d %s, %d utan rutt Password Lsenord Pause Pausa PaxDest Resml Percent Electricity Andel kraftverk (%%) Planes are not available yet! Flygplan har nnu inte uppfunnits! Plant tree Trd player Spelare player -1 Simutrans-spelare player 0 samhllsservice player 1 Napik 128 HB player 10 spelare 10 player 11 spelare 11 player 12 Spelare 12 player 13 Spelare 13 player 2 Trikky Transport player 3 Meyers Flytt AB player 4 Transport AB player 5 H-Trans AB player 6 PSK & Son AB player 7 Spelare 7 player 8 Spelare 8 player 9 Spelare 9 Please choose vehicles first\n Vlj en konvoj frst! Postrate Posttal Power Effekt Power (MW) Effekt (MW) Power: Effekt: Power: %4d kW\n Effekt: %4d kW\n Powerlines Kraftledningar Price Pris Problems_msg Problem Production of %s has been stopped:\n%s\n %s av typen \n'%s'\ntillverkas inte lngre.\n Profit Vinst promote to line Ny linje q1 Vr q2 Sommar q3 Hst q4 Vinter Query server Kontaktar servern rail car Rlsfordon random slumpad Random age Slumpr Random map Slumpkarta Rathaus Rdhus Rating Rang ratio_pax Proportion Relevant Relevanta Reliefkarte versiktskarta Remove Ta bort etikett remove airstrips Ta bort rullbana remove channels Fyll igen kanalstrcka remove interm. signals Ta bort mellanl. signaler remove maglev tracks Ta bort magnetsprsstrcka remove monorails Ta bort monorailstrcka remove narrowgauge tracks Ta bort smalsprsstrcka remove powerlines Ta bort kraftledningar remove roads Ta bort vgstrcka remove tracks Ta bort jrnvgsstrcka Remove wayobj %s Ta bort vgobjekt %s replace other signals Erstt vr. signaler replace stop Erstt hllplats request closing frbered nedstngning residential house Bostadshus Restore natural slope terstll naturlig sluttning Restwert: Frsljningsvrde: Retire. date: Slopning: return ticket Spegla frdplan Revenue Inkomster road vg road vehicle Landsvgsfordon Roadsign Vgskylt Rotate map Vrid spelplanen 90 Routing Transportleder sack sckar sail vind Saving map ... Sparar karta... Scenario complete: %i%% Scenariomlen uppndda till %i%% Scenario Debug Debug Scenario Error Log Fel Scenario Goal Ml Scenario Info Bakgrund Scenario Result Progress Scenario Rules Regler Schedule changing! Tidtabellen ndras! Schienentunnel Jrnvgstunnel Schiff_tab Fartyg Schiffdepot Fartygsvarv Schleppkahn_tab Kanalprmar Screenshot Skrmbild Seasons rstider Sehenswuerdigkeit Sevrdhet Select a server to join: Vlj server Sell the selected vehicle(s) Slj det valda fordonet. sended Skickad post SEP_FRACTION , SEP_THOUSAND SEP_THOUSAND_EXPONENT 3 Server did not respond! Servern svarade inte! Serves Line: Trafikerar linje: Service Tillfartsvgar set signal spacing Ange signalavstnd Setting Instllningar Ship Fartyg shops and stores Affrsbyggnader Show all Visa allt show all building Visa alla byggnader Show also vehicles no longer in production. Visa ven fordon som inte lgre r i produktion. Show also vehicles that do not match for current action. Visa ven fordon som ej kan anvndas med vald handling. Show even servers with wrong version or pakset Visa ven servrar som har fel version eller objektmodul show grid Visa rutnt Show industry Industrilista Show legend Information Show map scale Kartskala Show obsolete Visa ven frldrade Show offline Visa offline Show only used Visa enbart anvnda Show schedules Tidtabeller Show servers where game version or pakset does not match your client Visa servrar som inte r kompatibla med din version eller pakset show station coverage Visa stationstckning show station names Visa stationsnamn show waiting bars visa staplar fr vntande gods show/hide block reservations Visa/dlj blockresrvation Show/hide statistics Visa/dlj statistik Shows consumer/suppliers for factories Visar kunder (eller med skift nedtryckt: leverantrer) till fabriken under muspekaren Shows the currently selected schedule Visa aktuell tidtabell som en bild. Shrink city Minska invnarantal shuffle midis Slumpval signal spacing Signalavstnd Sim: Simloopar: Size (%d MB): Kartstorlek (%d MB): sliced underground mode Kartutsnitt slot empty tom Sort by sortera efter Sort waiting list by Sortera vntelistan efter Sound Ljud Sound settings Ljudinstllningar Sound volume: Ljudvolym: special freight Specialfrakt Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Hastighetsbonus\np vg %i km/t, jrnvg %i km/t\nfartyg %i km/t, flygplan %i km/t. Speedlimit Maxhastighet Speichern Spara Spieler Spelare Spieler(mz) Spelare Spielerliste Spelarlista Spielstand wurde\ngeladen!\n \nSpelet har lsts in!\n Spielstand wurde\ngespeichert!\n \nSpelet har sparats!\n Sprache Sprk Sprachen Sprkval Stadtinformation Stadsinformation Start Kr Start the selected vehicle(s) Skicka ut det valda fordonet. Starte Spiel Starta spel Station tiles: Stationsrutor: Status Hllplatsstatus steam nga Step timeline one year Hoppa ver ett r Storage Lager Storage capacity Frvaringskapacitet Strassendepot Garage Strassentunnel Bygg vgtunnel street car Sprvgsfordon sued Sdra suedost Sydstra suedwest Sydvstra Summer snowline Sommarsngrns Suppliers Leverantrer Tage alt dagar. There are still vehicles\nstored in this depot!\n Det finns fort-\nfarande fordon hr!\n This Month Denna mnad This Year Hittills i r Tile not empty. Rutan r upptagen\noch kan fr nrvarande\ninte ndras.\n timeline med tidslinje tl_title Stadslista To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Fr att attrahera fler turister har \n%s byggt\n%s\nfr %i:- av skattebetalarnas pengar. To heavy traffic\nresults in traffic jam.\n Mycket trafik\norsakar trafikkaos.\n Toggle day/night view Vxla mellan dag och natt Toggle vehicle tooltips Vxla visning av verktygstips fr fordon tonnen t Total inhabitants: Totalt antal invnare: Tourist attractions Turistattraktioner Tourists Sevrdheter Town_msg Nya resml Town: %s\n Ligger i staden %s\n Towns Stder track rls Tracks Spr Traffic Trafik Train Tg Trains are not available yet! Det finns nnu inte tg! Tram Sprvagn tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. sprvagn %i km/t, monorail %i km/t\nmaglev %i km/t, smalspr %i km/t. tram_track sprvgsrls Tramdepot Sprvagnshall Trams are not available yet! Sprvagnar r nnu inte tillgngliga. Transferring game ... verfr spel... Transformer only next to factory! Transformatorer kan endast byggas\n p platt mark i anslutning\n till en industri! Translation versttning transparent station coverage Genomskinlig stationstckning Transported Antal resor TrolleyBus_tab Trdbuss Truck Landsvgsfordon Tunnel muss an\neinfachem\nHang beginnen!\n Tunnlar mste\nbrja p raka\nsluttningar!\n Tunnel must start on single way! Tunnel mste starta p en befintlig vg. Tunnelboden Tunnel underground mode Underjordslge UNDO failed! Detta gr inte lngre\natt ngra! Man kan enbart\nngra direkt efter byggnation! Undo last ways construction ngra senast byggda vg. Unemployed Arbetslsa Unhappy Missnjda units/day enheter/mnad Update Line ndra linje upgrade HQ Uppgradera huvudkontor Usage: %.0f %% Anvndning: %.0f %% Use beginner mode Nybrjarlge Use timeline start year Flj tidslinje: Vehicle %s can't find a route! %s\nkan inte lngre\nhitta ngon vg! Vehicle %s is stucked! Fordon %s sitter fast! Vehicle details Fordonsdetaljer Verbrauch Konsumtion Vergroessere die Karte\n Frstora kartan\n verkaufen Slj Verkehrsteilnehmer Stadsbilar Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n Du str i skuld!\n\nDu har %d mnader p dig\natt terbetala din skuld.\n via %s\n via %s\n via Menge via (antal) voranstellen Stll frst Waggon_tab Vagnar waiting vntar Waiting for clearance! Vntar p fri vg! Warnings_msg Trafikrapport Wasser Vatten Water vatten Water level Vattenniv water vehicle Fartyg way %s cannot longer used:\n Vgtyp %s\nkan inte byggas lngre!\n way %s cannot longer used:\n%s\n %s finns inte lngre tillgnglig:\n way %s now available:\n Vgtyp %s kan\Nframver byggas.\n Way toll Vgtull Ways not connected Vgarna hnger inte ihop!\n Wegpunkt Vgpunkt Weight: Vikt: Wert Vrde west Vstra Winter snowline Vintersngrns withdraw terkalla Withdraw All terkalla alla WRONGSAVE Ej kompatibel spel-\nversion. Kan inte lsa\nin den hr filen.\n Year %i has started. r %i har brjat. Years r Your primary color: Primrfrg Your secondary color: Sekundrfrg Zielort destination zooming in Zoomar in zooming out Zoomar ut Zu nah am Kartenrand Fr nra\nkanten p spelplanen fr att kunna\nbygga ngonting.\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Nytt vrldsrekord fr magnetsprstg med: %.1f km/t av %s. New world record for monorails: %.1f km/h by %s. Ett nytt vrldsrekord\nfr monorail: %.1f km/t\nav %s. New world record for motorcars: %.1f km/h by %s. Bengt Blyfot nr\n%.1fkm/t med\n%s.\nNytt vrldsrekord! New world record for narrowgauges: %.1f km/h by %s. Ett nytt vrldsrekord fr\nsmalsprtg med %.1f km/t\nav %s. New world record for planes: %.1f km/h by %s. Lilienthals arvingar rapporterar\nnytt vrldsrekord:\nflg i %.f km/t\n%s. New world record for railways: %.1f km/h by %s. Dr flg vevstaken\nsin vg. Lokfrare\nCederlund krde i %.f km/t med \n%s. New world record for ship: %.1f km/h by %s. Ett nytt vrldsrekord fr\nfartyg med: %.1f km/t\np %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL by &1_CITY_SYLL stad &2_CITY_SYLL by &3_CITY_SYLL bro &4_CITY_SYLL brunn &5_CITY_SYLL berg &6_CITY_SYLL dal &7_CITY_SYLL flt &8_CITY_SYLL fors &9_CITY_SYLL sluss &A_CITY_SYLL vad &B_CITY_SYLL kping &C_CITY_SYLL hult &D_CITY_SYLL vik &E_CITY_SYLL borg %1_CITY_SYLL Lunds %2_CITY_SYLL Malm %3_CITY_SYLL Norr %4_CITY_SYLL Vster %5_CITY_SYLL Sder %6_CITY_SYLL ster %7_CITY_SYLL Skog %8_CITY_SYLL Marie %9_CITY_SYLL Karls %A_CITY_SYLL Oskars %B_CITY_SYLL Alm %C_CITY_SYLL Bke %D_CITY_SYLL Gran %E_CITY_SYLL Tall %F_CITY_SYLL Ek 1center %s %s 1extern %s filial %s 1suburb %s %s %s 2center %s central %s 2extern %s annex %s 2suburb %s Nya %s %s 3center %s huvud%s 3extern %s landsbygd %s 3suburb %s by %s %s 4center %s inre %s 4extern %s yttre %s 4suburb %s koloni %s %s 6center %s centrum %s 6extern %s industriomrde %s 7center %s stad %s simutrans-124.5/simutrans/text/sv/000077500000000000000000000000001520675460500172255ustar00rootroot00000000000000simutrans-124.5/simutrans/text/sv/airtools.txt000066400000000000000000000121321520675460500216210ustar00rootroot00000000000000Hjlp fr flygplatsverktyg

Flygplatsverktyg

Flygplatsverktyg innehller verktyg fr att bygga upp ett flygtransportntverk. Verktygen kan bygga eller ta bort taxibanor och landningsbanor fr flygplan, flyghllplatser (fr att hmta och lmna gods och passagerare), hangarer (fr handel med och underhll av flygfarkoster) och olika flygplatsbyggnader. Om du spelar med tidslinje aktiv s kan fler verktyg bli tillgngliga efterhand som tiden frflyter i Simutrans.

Klicka p flygplansikonen ovanfr kartan fr att ppna verktygsraden.
Lt muspekaren vila ver ett verktygsalternativ (efter att du ppnat eller klickat p verktygsraden) fr att se dess namn och dr s tillmpligt: byggkostnader, underhllskostnad i parenteser, och hastighetsbegrnsning.

{Tips: Ett stt att bygga en enkel flygplats r:
i) bygg och bind samman taxibanor och landningsbanor;
ii) placera ut flyghllplatser i nden av taxibanorna;
iii) lgg till de flygplatsbyggnader som krvs.}

Bland verktygen kan finnas, frn vnster till hger:

Taxibana: verktyget bygger markomrden dr flygplatser kan ta sig frn en flyghllplats till en landningsbana. Taxibanor byggs p jmn mark p kartan.
VIKTIGT: Om taxibanor ansluter till nden av landningsbanan, s kan inte flygplan landa och lyfta.
Fr att bygga en taxibana: klicka p verktyget fr att vlja (pekaren ndras till en taxibana), klicka sedan p terrngen fr att ange startpunkt (visar en bulldozer p kartan och visar kartkoordinater lngst nere till hger i fnstret), och klicka slutligen p marken eller en landningsbana fr att ange slutpunkt.
{Tips: Anvnd Frstr/Ta bort fr att avlgsna enstaka bitar av en taxibana. ngra [z] betalar inte tillbaka byggkostnader.}

Landningsbana: verktyget bygger en landningsbana dr flygplan kan landa och lyfta. Landningsbanor byggs p jmn mark p kartan, och kan korsa varandra.
Fr att bygga en landningsbana: klicka p verktyget fr att vlja (pekaren ndras till en vg) och klicka sedan p marken fr att ange startpunkt (visar en bulldozer p kartan) och klicka slutligen p marken fr att ange slutpunkt.
{Tips: Lt ndarna av landningbanor vara fria annars s kan inte flygplan landa eller lyfta. Anvnd Frstr/Ta bort fr att avlgsna enstaka bitar av en taxibana. ngra [z] betalar inte tillbaka byggkostnader.}

Ta bort landnings- och taxibanor: verktyget tar bort ett segment mellan tv punkter p kartan av en taxibana eller landningsbana nr det inte finns ngra flygplan dr. Det medfr byggkostnader att anvnda verktyget.
Fr att ta bort en landnings- eller taxibana: klicka p verktyget (pekaren ndras till ett rtt kors); klicka sedan p den bana som skall tas bort (vljer borttagningspunkt som visas som ett rtt kors p kartan); klicka slutligen p en annan punkt p samma bana fr att avlgsna strckan mellan denna och den frst angivna borttagningspunkten. {Tips: Lt ndarna av landningbanor vara fria annars s kan inte flygplan landa eller lyfta. Anvnd Frstr/Ta bort fr att avlgsna enstaka bitar av en taxi- eller landningsbana.}

Hangarer: verktyg fr att bygga hangarer fr att kpa eller administrera flygplan. Hangarer kostar underhll och byggs i slutet av en bit taxibana.
{Tips: Ta bort hangarer med Frstr/Ta bort.}

Flyghllplats&embarkeringsbrygga: verktyget bygger hllplatser p kartan dr flygplan kan hmta och lmna gods eller passagerare.
En flyghllplats som inte byggs i anslutning till en existerande hllplats skapar en ny hllplats.
Flyghllplatser byggs i nden av en taxibana; kostar underhll, har ett upptagningsomrde fr gods, passagerare och post. Ett flygplan landar enbart p en flygplats om det finns en ledig flyghllplats.
Fr att bygga en flyghllplats: klicka p verktyget fr att vlja det och klicka sedan p nden av en taxibana.
{Tips: Ta bort flyghllplatser med Frstr/Ta bort. Tryck [v] fr att visa/dlja upptagningsomrdet fr gods och passagerare p kartan.}

Flygplatsbyggnader: verktyget bygger tillbyggnader till hllplatser som kan medfra kade underhllskostnader och utkat upptagningsomrde eller strre lagringskapacitet fr gods och passagerare. I hrnet av ngra verktygsalternativ visas en ikon (anvnds i stationslistan & hllplatsinformation) som anger vad som tillbyggnaden mjliggr fr hllplatsen att hantera.
Fr att bygga en tillbyggnad: klicka p verktyget fr att vlja tillbyggnad (pekaren ndras till tillbyggnaden) och klicka sedan p nskad plats i anslutning till en existerande hllplats p kartan. Den nya tillbyggnaden anses nu vara en del av hllplatsen..
{Tips: Ta bort tillbyggnader med Frstr/Ta bort. Tryck [v] fr att visa/dlja upptagningsomrdet fr gods och passagerare p kartan.}

simutrans-124.5/simutrans/text/sv/baum_build.txt000066400000000000000000000017661520675460500221030ustar00rootroot00000000000000Plantera trd

Plantera trd

Dialogen r indelad i fyra sektioner.
uppe till vnster finns en urvalslista ver de trdtyper som r tillgngliga med angivna alternativ
nere till vnster visas en bild ver valt trd
uppe till hger finns ett par alternativknappar
nere till hger ges information om valt trd

Urvalslista

I urvalslistan ges alla tillgngliga trdtyper. Det finns tv flikar som lter dig vlja hur dessa trdtyper presenteras.
versttning: Visar trdnamnet med versatt till valt sprk. Om det inte finns ngon versttning s anvnds objektnamnet i stllet.
Objekt: Visar det Simutrans-interna objektnamnet p fr trdtypen.

Alternativ

Alla klimatzoner: Detta alternativ avaktiverar de klimatbegrnsningar som finns associerade med varje trdtyp.
Slumpr: ldern p det trd som skall planteras ges ett slumpvrde.

simutrans-124.5/simutrans/text/sv/citybuilding_build.txt000066400000000000000000000033111520675460500236310ustar00rootroot00000000000000Bygg stadsbyggnader

Bygg stadsbyggnader

Dialogen r indelad i fyra sektioner.
uppe till vnster finns en urvalslista ver de stadsbyggnader som r tillgngliga med angivna alternativ
nere till vnster visas en bild ver vald stadsbyggnad
uppe till hger finns ett par alternativknappar
nere till hger ges information om vald stadsbyggnad

Urvalslista

I urvalslistan ges alla tillgngliga stadsbyggnader. Det finns tv flikar som lter dig vlja hur dessa stadsbyggnader presenteras.
versttning: Visar stadsbyggnadens namn versatt till valt sprk. Om det inte finns ngon versttning s anvnds objektnamnet i stllet.
Objekt: Visar det Simutrans-interna objektnamnet fr stadsbyggnaden.

Alternativ

Alla klimatzoner: Detta alternativ avaktiverar de klimatbegrnsningar som finns associerade med varje byggnadstyp.
Flj tidslinje: Urvalslistan innehller enbart de byggnader som r tillgngliga innevarande Simutrans-r.
Visa ven frldrade: Utka urvalslistan med byggnader som r frldrade innevarande Simutrans-r.
Bostadshus: Urvalslistan innehller bostadshus.
Affrsbyggnader: Urvalslistan innehller affrsbyggnader.
Industribyggnader: Urvalslistan innehller industribyggnader.
Rotation: Om vald byggnad kan visas frn olika vinklar, s r det hr mjligt att ange vilken vinkel som skall anvndas. Bilden p byggnaden nere till vnster ndras och avspeglar den valda vinkeln. Om alternativet ges vrdet slumpad anvnds en slumpmssig vinkel av byggnaden.

simutrans-124.5/simutrans/text/sv/citywindow.txt000066400000000000000000000114121520675460500221650ustar00rootroot00000000000000Hjlp om Stadsinformation

Stadsinformation

Stadsinformation visar statistik om vald ttort och kan anvndas fr att ndra dess namn. En ttort kan vara en stad, smstad eller by; nedan anvnds beteckningen stad fr alla typer av ttorter.

Varje stad har ett rdhus runt vilket det vxer. Tillvxten befrmjas av att passagerare och post transporteras. Tillvxten visar sig genom att gator byggs eller tas ver av samhllsservice (visas genom att vgarna kantas av trottoarer p huvudkartan) och genom att nya stadsbyggnader och industrier dyker upp, samt genom att stadens grnser flyttas utt.

Genom att klicka p ett rdhus p huvudkartan med granskningsverktyget eller p en stad i stadslistan ppnas Stadsinformation. Hr kan du se och ndra stadens namn, basuppgifter om staden, tv mini-kartor samt statistiska kurvor ver stadens utveckling ver tid.

Namn: det namn som staden, smstaden eller byn br visas verst i Stadsinformation i en textruta.
Detta namn anvnds bl a nr namn fr hllplatser inom stadsgrnserna skall skapas. Fr att ndra namnet, klicka helt enkelt i textrutan och skriv in ett nytt.
{Tips: Anvnd [!] fr att vxla om stadens namn visas ovanfr rdhuset p huvudkartan. Ls readme_citylist.txt (finns i ...simutrans/text/) fr mer information om hur stadsnamn skapas}.

Under namnrutan ges lite grundlggande information om staden:
Storlek: aktuellt invnarantal. Den senaste utvecklingen visas inom parenteser..
byggnader: antal stadsbyggnader (bostadshus/affrsbyggnader/industribyggnader) inom stadens grnser.
Kartkoordinater: anger hur vitt stadens grnser strcker sig (dessa kommer att ndras efterhand som staden vxer). Det frsta vrdeparet anger vre vnstra hrnet och det andra nedre hgra hrnet. Nr stder vxer kan deras stadomrden komma att verlappa.

Nuvarande antal arbetslsa och hemlsa invnare i staden. Dessa tal kar nr staden utkar sina grnser och minskar nr stadsbyggnader byggs. Frhllandet mellan dessa vrden avgr vilken typ av stadsbyggnad som byggs/renoveras nr staden bygger till.

Kryssrutan Tillt stadstillvxt tillter dig att aktivera eller avaktivera om staden fr vxa eller ej.

Minikartorna av spelvrlden i Stadsinformation visar som frgade punkter vart passagerare och post som genererats inom stadens grnser har fr destination.
Den hgra minikartan visar vrden fr innevarande spelkalendermnad, och den till vnster fr fregende spelkalendermnad. Frgen p punkterna anger om destinationen kan ns eller ej:
- gul punkt: det finns en rutt och en hllplats som inte r verfull dr resan kan ta sin brjan (motsvarande glatt ansikte i hllplatsinformationsfnstret).
- orange punkt: det finns inte ngon rutt till resmlet.
- rd punkt: det finns en rutt till resmlet, men den hllplats dr resan skulle pbrjas r verfull (motsvarande ledset ansikte i hllplatsinformationsfnstret.

Under minikartorna finns ett kurvdiagram som visar statistik fr staden.
P y-axeln visas kvantitet och p x-axeln tid. Klicka p tabbarna ovanfr fnstret fr att ndra tidsskalan p x-axeln:
r: visar rliga vrden fr de senaste 12 spelkalenderren.
Mnader: visar mnatliga vrden fr de senaste 12 spelkalendermnaderna

Genom att klicka p knapparna under kurvfnstret aktiverar eller avaktiverar du kurvan i frga. En aktiverad knapp visas nedsnkt.
Kurvorna visas i samma frg som frgen p motsvarande knapp:
Medborgare: Antal mnniskor som bor i staden
Tillvxt: Befolkningsfrndringar.
Antal hus: Hur mnga stadsbyggnader det finns i staden.
Stadsbilar: Hur mnga stadsbilar som genererats.
Antal resor: Hur mnga passagerare som givit sig i vg p sin resa (visas som glatt ansikte i hllplatsinformationsfnstret)
Passagerare:Det sammanlagda antalet passagerare som genererats i staden.
Skickad post: Hur mycket post som har pbrjat sin frd mot sin destination
Mail: Den sammanlagda mngden post som genererats i staden.
Varor i lager:Antal gnger som kontroll fann varor i lager hos stadens fabriker
Varor:Antal gnger som lagerstatusen hos stadsfabrikerna kontrollerats.

{Tips: Instllningar som rr stder kan ndras i simuconf.tab och cityrules.tab}.

simutrans-124.5/simutrans/text/sv/depot.txt000066400000000000000000000200241520675460500210770ustar00rootroot00000000000000Fordonshallsverktyg

Fordonshallsverktyg

Fordonshallar - garage, vagnhallar, bthus och hangarer - anvnds fr att kpa och administrera fordon och fartyg. Olika transportmedel har sina egna typer av fordonshallar. Fordonshallar visar enbart elektrifierade fordonstyper om de finns p elektrifierade spr.
Om du spelar med en tidslinje s kan fler verktyg bli tillgngliga efterhand som tiden passerar i Simutrans.

Verktyg fr att bygga de olika typerna av fordonshallar finns p verktygsraden fr respektive fordonsslag: lokstall/vagnhall; monorailvagnhall; sprvagnshall; garage; bthus och hangar.

Klicka p en fordonshall p kartan med granskningsverktyget fr att ppna fordonshallsverktygen som ger dig information om och lter dig kpa och administrera konvojer (ett eller en grupp av fordon/fartyg i trafik).
Titelraden av fordonshallsverktygen anger vilken typ av transportmedel som fordonshallen betjnar.

Fordonshallsverktygen kan ndras storlek p (klicka p nerpilen p titelraden fr att terg till ursprunglig storlek).
Klicka p vnster/hgerpilar p titelraden fr att stega igenom fordonshallar fr samma transportslag (kartan centreras runt fordonshallen).

Hgst upp i fordonshallsverktygen anges antalet konvojer i fordonshallen, konvojnamn och trafikeringslinje visas.

Konvojnamn: Klicka p vnster-/hgerpilsknappar bredvid konvojnamnet fr att stega igenom de konvojer som finns i fordonshallen.
Att byta konvojnamn: klicka p rutan fr konvojnamnet och skriv in ett nytt namn (som frval r detta mrket p frsta komponent som kptes eller tilldelades en ny konvoj).

Trafikeringslinjenamn: Klicka p vnster-/hgerpilsknappar vid namn fr att vlja trafikeringslinjer fr fordonshallens transporttyp, eller klicka p namnrutan fr trafikeringslinjen fr att ppna upp en rullgardinsmeny ver trafikeringslinjer (klicka p en trafikeringslinje i rullgardinslistan fr att vlja denna).
Att ndra namn p trafikeringslinje: klicka p namnrutan fr trafikeringslinjen och skriv in ett nytt namn (som frval tilldelas trafikeringslinjen ett nummer nr den skapas).

konvoj-bild Under namnrutorna fr konvoj- och trafikeringslinjenamn visas en bild av den valda konvojen.
Lt musen vila ver bilden av en komponent av konvojen fr att f mer detaljer om just denna komponent (visas i botten av fordonshallsverktygen).
Klicka p komponent av konvojbilden fr att ta bort denna frn den valda konvojen och frvara den i fordonshallen.

Nedanfr bilden visas antalet komponenter i vald konvoj, lngden p konvojen i rutor (alla komponenter mste f plats p en lmplig hllplats fr att gods och passagerare skall kunna hmtas och lmnas dr) samt vilken trafikeringslinje konvojen r knuten till.

Bland alternativknappar (klicka fr att anvnda) fr Fordonshallsverktyg finns:

Kr: skickar ivg vald konvoj om en rutt kan hittas.

Tidtabell: ppnar verktyg fr att ndra rutt och hur lnge en konvoj vntar (till hur fylld) vid en station.

Uppls: parkerar de fordonsenheter som den valda konvojen bestr av.

Slj: sljer nuvarande konvoj, frsljningsvrdet stts in p ditt konto.

Ny linje: ppnar verktyg fr att sammanstlla en tidtabell (en rutt och en minsta mngd varor/passagerare som konvojen vntar p vid varje stopp), som kan anvndas fr flera konvojer.

Tilldela linje: knyter en linje som visas i linjenamnsrutan verst i fordonshallsverktygen till en vald konvoj.

ndra linje: ppnar verktyg fr att ndra tidtabellen (den rutt och en minsta mngd varor/passagerare som konvojen vntar p vid varje stopp) fr vald linje.

Kopiera konvoj: kper och stter samman en konvoj med samma sammansttning, tidtabell och linje som vald konvoj.

Nedanfr alternativknapparna visas bilder av alla fordon och fordonskomponenter som det gr att kpa.
bilder av fordons-enheter: fordonsenheter r indelade i typer som kan ses genom att klicka p indextabbarna. Indextabbarna delar in fordonsenheter i fljande grupper: fordons-enheter fr passagerare och post, andra motordrivna fordonsenheter, samt andra slp/vagnar.
En vit siffra ver bilden av fordonsenheten visar hur mnga sdana enheter som finns i lager efter att en konvoj har upplsts. Lagrade fordonsenheter kan sljas eller anvndas i en ny konvoj.

Lt muspekaren vila ver en bild av en fordonsenhet fr att se mer information om denna:
Namn: och i hakparentes framdrivningstyp fr motorfordon.
Kostnad: inkpspris och i hakparenteser driftskostnad per kilometer
Kraft: motorstyrka och maxhastighet fr motoriserade fordon.
Kapacitet: mngd och typ av varor som kan fraktas i fordonsenheten.
Vikt: i ton.
Maxhastighet: fr icke-motoriserade fordonsenheter.
Introduktion: datum nr fordonsenheten blir tillgnglig om tidslinje r aktiverad.
Tillbakadragning: datum nr en fordonsenhet blir frldrad.
Skapad av: den person som skapade fordonsenheten fr Simutrans.
Utvxling: ? power ratio.
Andrahandsvrde: den summa pengar som erhlls om en fordonsenhet sljs igen.

Klicka p en bild av en fordonsenhet under indextabbarna fr att antingen kpa eller slja en sdan beroende p de instllningar som gjorts med fordonsalternativknappen lngst ner till vnster i fordonshallsverktygen Denna knapp har tre lgen:
Lgg till: kper en fordonsenhet och lgger till densamma i den konvoj som r vald alternativt skapar en ny konvoj om ingen r vald.
Stll frmst: kper en fordonsenhet och placerar den frst i den konvoj som r vald alternativt skapar en ny konvoj om ingen r vald.
Frsljningslge: sljer lagrade fordonsenheter.

frgstaplar under bilderna av fordonsenheter visar vilka och i vilken ordning fordonsenheter kan sammanfogas fr att bilda en funktionell konvoj (frgstaplarna kan ndras beroende p instllning av fordonsalternativknappen).
Frgerna anger:
- grn: fordonsenheten kan anvndas.
- rd: fordonsenheten kan inte anvndas
- gul en annan fordonsenhet mste placeras fre eller efter fr att konvojen skall kunna fungera.
- bl: fordonsenheten kan anvndas men r frldrad.
{Tips: en konvoj r funktionell om den visar grna staplar fr alla fordonsenheter; om det finns kvar en gul stapel, s saknas en ndvndig fordonsenhet.}

Visa frldrade: om du spelar med en tidslinje, visas ven fordonsenheter som dragits tillbaka om den fyrkantiga knappen r nedtryckt.

simutrans-124.5/simutrans/text/sv/players.txt000066400000000000000000000031141520675460500214440ustar00rootroot00000000000000Hjlp om spelarlista

Spelarlista

I spelarlistan visas alla spelare.
Allra verst terfinns den Simutrans-spelare som styrs av en mnniska.
Drefter finns samhllsservice. Samhllsservice skter utbyggnad av stder och industrier samt sevrdheter och minnesmrken.

Du kan ta kontroll ver en annan spelare genom att klicka p pilen framfr dess namn; detta frutsatt att spelarbyte inte avaktiverats.

Hrefter visas alla ett antal flt fr vriga aktiva spelare; som frval finns det inte ngon mer aktiverad spelare, och flten visas drfr gra med texten tom. Nr du valt spelartyp (mnniska, AI fr passagerartrafik, AI fr godstrafik) visas en kryssruta frst p raden med hjlp av vilken du kan aktivera eller avaktivera spelaren i frga.

Aktiverade datorspelare (AI-spelare) bygger ett transportnt och transporterar antingen varor eller passagerare. Om du avaktiverar en datorspelare, s slutar denna spelares fretag att expandera, men existerande ntverk och frbindelser fortstter att fungera.

AI-spelare: Namnet p existerande spelares fretag visas i listan med fretagets frg som bakgrund. Genom att klicka p detta namn ppnas finansfnstret fr fretaget i frga upp.

Lngst ner i fnstret under listan ver spelare finns en alternativruta dr du kan aktivera eller avaktivera ingen bankrutt. Om du aktiverar detta alternativ kan inte ngon spelare (inklusive du sjlv) g i bankrutt ven om fretagets tillgngar r negativa.

simutrans-124.5/simutrans/text/sv/signal_spacing.txt000066400000000000000000000021651520675460500227530ustar00rootroot00000000000000Hjlp om signalintervall

Allmnt bruk:


-- Ctrl-klicka p signalikonen fr att ta fram denna dialog.
-- Klicka-och-dra lngs spr ger en frhandsvisning av de signaler som kommer att byggas; slpp knappen fr att genomfra byggnationen. Signalerna kommer att byggas s att tg kan frdas lngs spret endast i den riktning som du drog musen.
-- I ntverksspel: Ctrl-klicka frst p spret och slpp musknappen. Nr du hller ner musknappen och flyttar musen fr du en frhandsvisning. terigen slpper du knappen fr att genomfra byggnationen.

Parametrar i denna dialog:

-- Avstnd: hur mnga rutor som skall skilja intilliggande signaler t (diagonal rknas som 1/2 ruta). Detta vrde visas p knappen om signalverktyget r valt.
-- Ta bort mellanliggande signaler: om aktiverad s kommer redan existerande signaler tas bort frn strckan.
-- Erstt andra signaler: om aktiverad, s kommer existerande signaler p de rutor dr nya signaler skall byggas att tas bort.

Dessa parametrar stlls in fr varje signaltyp separat.

simutrans-124.5/simutrans/text/sv/simutrans.txt000066400000000000000000000054401520675460500220160ustar00rootroot00000000000000Simutrans

Vlkommen till Simutrans

Du r en ung entreprenr med drmmar om att bygga ett transportntverk. Du brjar med ett litet men tillrckligt startkapital. Vlj en av de tusentals Simutrans-kartor som finns tillgngliga, eller skapa en ny baserad p en hjdkarta. Samma karta kan anvndas fr flera olika spel och ndras d baserat p de instllningar du gjort.

Se dig omkring p kartan och gr en bedmning av situationen. Det ligger i dina hnder hur denna lilla vrld kommer att utvecklas. Du kan t ex vlja att frakta varor (rvaror, slutprodukter) fr industrierna i en industrikedja. Bygg de transportleder som behvs; vlj mellan rls, landsvg, kanaler eller kanske flygrutter. Sjlvklart kan varor lastas om eller passagerare byta fordon p mellanstationer.

Frdelen med att brja med godstransport r att du p detta stt kan f god avkastning fr liten investering. Nackdelen r att godstransport inte bidrar till din vrlds tillvxt.

En annan strategi r att frn brjan stimulera stadstillvxten och att bygga eller tminstone planera fr den strre kapacitet som d s smningom kommer att krvas. Om invnarna i stderna kan komma till sina nskade resml, kan de f utlopp fr sin reslust. Varje potentiell resenr bryr sig dock enbart om det finns en resvg dit denne vill, s frsk att ansluta s mnga destinationer som mjligt till ditt transportntverk; arbetsplatser och turistattraktioner som ruiner och gamla slott attraherar stora mngder av resenrer. Om du ven transporterar den post som stadsbefolkningen vill skicka s stimuleras tillvxten av staden n mer.

God service lnar sig. Ju fler resenrer som r njda med din service och ju mer post du transporterar, desto mer vxer staden i frga. Nya bostder samt affrs- och industribyggnader byggs i staden. Kompletta industrikedjor dyker upp utanfr stderna. Detta ppnar nya mjligheter fr expansiva transportfretag och erfarna logistiker.

Genom att noggrannt planera dina transportleder, kan du utnyttja dina jrnvgs- och landsvgsstrckor maximalt. Trafikera inte en linje med fler fordon n vad trafikunderlaget krver; men snla inte heller i ondan. En konvoj som stndigt kr halvfull gr inte med vinst.

Det finns inte ngra tidsramar fr hur lnge du kan spela. Spela tills du ntt dina ml. Du kan konkurrera mot upp till 6 AI-spelare. Stjl dina konkurrenters passagerare eller frakt frn rakt under deras nsa! Kanske spelar du helst helt utan konkurrenter? Det r ditt val. Expandera inte fr fort i inledningsfasen; ditt transportfretag gr i konkurs om du inte har positivt netto.

Simutrans-teamet och communityn runt Simutrans nskar dig mycket nje och gnuggande av de sm gr nr du bygger upp din vrld.

simutrans-124.5/simutrans/text/sv/underground.txt000066400000000000000000000040321520675460500223210ustar00rootroot00000000000000Att bygga under jord

Det finns tv stt att se vad som finns under jorden i Simutrans.
underjordslge
Med U vljler du underjordslge. D dljs alla freml som finns ovan mark.
kartusnitt
Med Ctrl + U vljer du att visa en hjdniv av vad som finns under jord i Simutrans. D dljs alla freml som finns p en hgre niv, oavsett om dessa r under eller ovan jord.

Du byter utsnittsniv med + (hgre) och - (djupare) eller med hjlp av Visningsdialogen.

Muspekaren
underjordslge
I underjordslge visas rutmnstret frn marknivn. Fr obebyggda rutor placerar sig pekaren enligt detta rutmnster, men fr bebyggda rutor p rutan.
Det r ganska svrt att placera pekaren rtt m h a rutmnstret, och drfr br kartutsnitt anvndas nr man bygger saker under jord.

kartutsnitt
Vid kartutsnitt lggs rutmnstret enbart ver aktuell niv och muspekaren kan ltt placeras p rtt stlle i detta rutmnster. Dvs muspekaren kan enbart flyttas inom aktuell niv vilket i hg grad underlttar byggnadsarbete.

Vgbygge

Generellt


I allmnhet byggs vgstrckor under jord m h a tunnelverktygen. Fr att kunna bygga under jord, mste en tunnel eller tunnelingng (Ctrl + klick med tunnelverktyget p en sluttning) byggas.

Nivbyte
Fr att ndra tunnelns niv under jord anvnds verktyget Hj/Snk markniv p menyn Markhjdsverktyg. Klicka helt enkelt p vgstrckans nde med nskat verktyg.

Speciellt om sprvgar


Sprvgsrls lggs under jord med de vanliga byggverktygen.

Stationer och tillbyggnader
Endast egna stationer kan byggas ovanp varandra.

Tillbyggnader kan inte byggas under jord.

Signaler
Kan anvndas som ovan jord.

simutrans-124.5/simutrans/text/th.tab000066400000000000000000000131601520675460500177010ustar00rootroot00000000000000§Thai PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: th Thai # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 2.06 2024 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_sort_income รายรับ cl_btn_sort_name รายการ cl_btn_sort_type ลักษณะ #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic ภูมิอากาศหนาว #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ allowed climates:\n ภูมิอากาศที่อนุญาต:\n Alters a schedule. ปรับปรุงตารางเวลา Angenommene Waren สินค้าที่จำเป็นสำหรับอุตสาหกรรมในบริเวณใกล้เคียง anhaengen ส่วนต่อท้าย Anhaenger_tab รถพ่วง Anheben ยกระดับพื้นดิน Appends stops at the end of the schedule เพิ่มจุดรับส่งในส่วนท้ายของตาราง Apply Line ใช้งานสายนี้ April เมษายน Arbeiter aus: แรงงานอาศัยอยู่ใน: Arrived ถึงจุดหมายแล้ว Assets สินทรัพย์ Aufspanntransformator สถานีจ่ายไฟฟ้าย่อย August สิงหาคม Autohalt muss auf\nStrasse liegen!\n จุดรับส่งผู้โดยสารนี้\nต้องสร้างบนถนนเท่านั้น Available หาได้ Bahndepot โรงซ่อมรถไฟ Bankrott:\n\nDu bist bankrott.\n ล้มละลาย:\n\nคุณล้มละลายแล้ว!\n\n battery แบตเตอร์รี่ Baum ต้นไม้\n baum builder ปลูกต้นไม้ Baustelle เขตก่อสร้าง Bauzeit ระยะเวลาก่อสร้าง Beenden ออก Beginner mode โหมดสำหรับผู้เริ่มเล่น Besonderes Gebaeude สถานที่ท่องเที่ยว BF สถานี bio ชีวภาพ Blockstrecke ist\nbelegt\n \nทางวิ่งนี้ถูกใช้งาน\nจากขบวนรถคันอื่น!\n Boden พื้นที่ Bonusspeed: %i km/h ใช้ความเร็วสูงสุด: %i km/h bridge is too high for its type! สะพานสูงเกินไป Bridge is too long for this type!\n ไม่สามารถสร้างสะพานได้\nระยะทางไกลเกินไป Bruecke สะพาน Bruecke muss an\neinfachem\nHang beginnen!\n สร้างสะพานด้วยแนวตรงเท่านั้น! Brueckenboden สะพาน Build air depot สร้างโรงซ่อมบำรุงเครื่องบิน build choosesignals สร้างสัญญาณไฟเลือกชานชาลา Build city market สร้างร้านค้าใหม่ในพื้นที่ใกล้เคียง Build drain สถานีแปลงระบบไฟฟ้า build HQ สร้างสำนักงานใหญ่ HQ Build land consumer สร้างโรงไฟฟ้า Build maglev depot สร้างโรงซ่อมบำรุงรถไฟแมกเลฟ Build monorail depot สร้างโรงซ่อมบำรุงรถไฟโมโนเรล Build narrowgauge depot สร้างโรงซ่อมบำรุงรถรางขนาดเล็ก Build powerline ไฟฟ้าความเร็วสูง Build presignals สร้างสัญญาณไฟคู่ Build road depot สร้างอู่รถยนต์ Build ship depot สร้างอู่เรือ Build signals สร้างสัญญาณ Build train depot สร้างอู่รถไฟ Build tram depot สร้างอู่รถราง Build truck depot สร้างอู่ซ่อมรถ Building costs estimates ค่าก่อสร้างโดยประมาณ Buildings จำนวนสิ่งก่อสร้าง Neue Karte การ์ดใหม่ Sprachen ภาษา Stadtinformation ข้อมูลของเมือง Start เริ่ม simutrans-124.5/simutrans/text/tr.tab000066400000000000000000000025061520675460500177150ustar00rootroot00000000000000§Turkish PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: tr Turkish # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 3.06 2024 # # # ################################################################################ #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ Neue Karte Yeni kart simutrans-124.5/simutrans/text/tr/000077500000000000000000000000001520675460500172225ustar00rootroot00000000000000simutrans-124.5/simutrans/text/tr/airtools.txt000066400000000000000000000114741520675460500216260ustar00rootroot00000000000000Hava Araçları

Havaalanı Araçlar hava ulaşım ağı oluşturmak için araçlar içerir. Araçlar inşa veya kaldırın: taxiways ve uçak için piste, Airstops (seçmek-up & drop-off, kargo ve yolcu) kullanılan uçak depoları (satın alma ve hava ulaşım araçları yönetmek) ve çeşitli hava binalar. Zaman Simutrans daha fazla araç geçerse Eğer zaman çizelgesi ile birlikte oynarken seçenekleri görünebilir. Tıklayın uçak-Oyunun başında icon-araç çubuğunu açmak için görüntüleyebilirsiniz.

Araç üzerinde Hover fare imleci seçeneği (açılış veya araç çubuğu üzerine tıklayarak) sonra parantez içinde ve maksimum hız sınırı adı ve nerede uygun: inşaat maliyeti, bakım maliyeti görmek için.
İpuçları: basit bir havaalanı inşa etmek için bir yolu:
i) inşa ve (ler) ve Pist (ler) hızlanma pisti bağlamak;
ii) yer airstop (lar) ucunda (lar) hızlanma pisti ve (lar);
iii) havaalanı binaları olarak gerekli ekleyin.

Araçlar, sağa soldan içerebilir:
Hızlanma pisti: araç bir Airstop bir Pist gitmek için uçak zemin hareket alanları oluşturur. Taxiways oyun seviyesi zemin görünümü oluşur.
ÖNEMLİ: Taxiways piste sonuna kadar olmayabilir uçak kapatma ve arazi bağlıdır.

Bir hızlanma pisti kurmak için: tıklayın aletine hızlanma pisti için (değişiklikler imleç) seçmek için tıklayın başlatmak için arazi üzerinde noktaya (eş-harita ekranın alt sağda ordinates), ve nihayet tıklayın oyun bir buldozer-görünümü ve gösterir arazi veya bitirmek için pist noktası.
İpuçları: Kullanım Destroy / Kaldır hızlanma pisti tek tek parça kaldırın. Geri Al [z] inşaatların maliyetleri iade etmez.

Pist: araçlar bir uçak pisti, uçak yapmak için kullanılan kapalı ve toprak yapacağız. Piste oyun seviyesi zemin görünümü ve dahili birbiriyle çapraz Mayıs

Bir Pist oluşturmak için: tıklayın aletine yoludur (değişiklikler imleç seçmek için), sonra başlamak için arazi üzerinde nokta (), ve bitiş için arazi üzerinde son tıklayın noktası oyun görünümünde bir buldozer gösterir. (İpuçları: olmayabilir başka uçak-off veya arazi piste tutarsız ve biter bırakın. Kullanım Destroy / Kaldır Pist tek tek parça kaldırın. Geri Al [z] inşaatların maliyetleri iade etmez.) Airstrips Kaldır: araç, iki nokta arasında oyunda hızlanma pisti veya Pist, hiç uçak mevcut, bir parça kaldırır-görüntüleyebilirsiniz. Araç kullanımı bir inşaat maliyetini düşürebilir. Bir uçak pisti kaldırmak için: tıklayın aletine (değişiklikler imleç kırmızı-geçmeye); tıklatın şerit üzerinde silinmek üzere olduğunu () silme noktaya oyun görünümünde bir kırmızı haç ile gösterilir seçer; ve nihayet tıklayın başka bir noktada şerit, ilk silme-noktasına kısmını kaldırın. (İpuçları: olmayabilir piste başka uçaklar ayrı sonu bırakın kapatma veya arazi. Kullanım Destroy / Kaldır airstrips bireysel adet kaldırmak için.) Hava Depo: araçlar satın alma ve uçak yönetmek için hangarlar inşa. Hangarlar ve bakım maliyetini bir parça hızlanma pisti sonuna üzerine kurulmuş bulunmaktadır. Bir Hangar oluşturmak için: tıklayın aletine (değişiklikler imleç bir depo için), ardından bir hızlanma pisti bir ucunda. (İpuçları: Destroy ile depoları Kaldır / Kaldır.) Airstops & binmeden köprü: araçlar oyun Durdurur inşa görüntülemek, uçak için seçmek için yukarı ve açılan yolcu veya kargo kapatın. Bir Airstop bir mevcut Dur yanında yeni bir Dur oluşturur yapılmış değil. Airstops bir parça hızlanma pisti sonuna üzerine kurulmuş ise; ve eşya, yolcu ve posta için bir bakım maliyeti ve havza. Eğer bir serbest Airstop kullanılabilir bir uçak sadece havaalanında girerler. Bir Airstop oluşturmak için: tıklayın aletine seçin ve ardından bir hızlanma pisti bir ucunda. (İpuçları: Destroy ile Airstops Kaldır / çıkarın. Medya [v] göstermek için mal ve yolcu oyun için havza gizlemek görüntülemek.) Havaalanı binaları: araçlar hangi mal ve yolcu için bakım maliyetleri ve kapasitesi ve havza artırabilir Durdurur için uzantıları inşa. Bazı aracın köşesinde seçenekleri bir simge olarak (İstasyon Listesi & Dur Bilgiler) kullanılan bu öğeler uzantısı gösterir ele Dur sağlar. Bir uzantısı oluşturmak için: tıklayın aletine uzantısı için (değişiklikler imleç uzantısı seçmek için), ardından gerekli konum, oyun mevcut bir Dur-görmek için gelecek. Yeni uzantısı artık Dur bir parçası olarak kabul edilir. (Ipuçları: the Destroy ile / Kaldır uzantıları çıkarın. Medya [v] göstermek için mal ve yolcu oyun için havza gizlemek görüntülemek.) simutrans-124.5/simutrans/text/translate_users.txt000066400000000000000000000047041520675460500225610ustar00rootroot00000000000000################################################################################ # User list: all authors and contributors to Simutrans translation # # https://translator.simutrans.com # # DO NOT EDIT THIS FILE. It will be created automatically # ################################################################################ 209CATrus Ampersand An_dz note: Verified where all texts are shown Aureus - Aureus BJ99 BarthVader - Bartlomiej Weclewski Citarofahrer Daikon - Daikon Ocelot Duke Dwachs Eps-Omo FLN - Francisco Flemmbrav Gauthier - Gauthier NOTTRET Green HyperSim IgorEliezer JHLee - J. H. Lee Jaroslaw - Jaroslaw JulioCesarBR - Julio Cesar Miranda Rodrigues note: Member who helps translate in Portuguese language! Kozinsky - Evgen Leartin LeitnerI MRBK note: KHOMKRIT BUTSARAKHAM (พี) Makie - Martin note: Maintainer PAK128.german & Translator Matthew NNW - Daniel Nazalassa - Nazalassa Pestr - Mikhail Pestrechikhin Phystam - T. Tomai note: Simutrans Extended player pak256-Ex developer Pumuckl999 Randonn - Maico Randon Ranran Roboron - Roberto Michán Sánchez Tajia TcatalunyaT Tjoeker Umoxfo VaclavMacurek - Václav Macůrek Ves - Victor Sørensen Vladki - Vladislav Kurz Woratiklis - Woratiklis Yona-TYT - Yonatn Mejia albert.yuste - Albert Ruiz note: Albert Ruiz - Zeno alekcxjo - Alexandre Raymond cousjath gebeine note: maintainer PAK128.german gwalch iGi jamespetts - James E. Petts note: Maintainer for Simutrans-Experimental. Also works on Pak128.Britain. jk271 - Honza Kobel locidux lr19 margaret milko paintree pasichna pierohd pika polix - Pawel Rudzinski prissi sojo - Sven Osada note: maintainer pak96.comic splithor1zon stepan - Stepan stev0312 - steve0312 uci vonjo vsys - Vladimír Slávik vzrenggamani - Rengga Prakoso Nugroho note: Riyantama Wira - Vesa khrisna - Satria naail - Mikhael Theofilus - Thomas Septian whoami - Michael Olbricht wlindley - William Lindley woodman - Maxim Filippow yurrriiiy z8w38 simutrans-124.5/simutrans/text/uk.tab000066400000000000000000001774141520675460500177220ustar00rootroot00000000000000§Ukrainian PROP_FONT_FILE cyr.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: uk Ukrainian # # Encoding: UTF-8 # # Font: cyr.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable Вимкнено cl_btn_filter_enable Увімкнено cl_btn_filter_settings Налаштування cl_btn_sort_asc За зростанням cl_btn_sort_desc За спаданням cl_btn_sort_id Внутрішній ІД cl_btn_sort_income Прибуток cl_btn_sort_name Назва cl_btn_sort_type Тип clf_btn_alle усі clf_btn_invers оберн. clf_btn_keine нічого gl_btn_sort_bonus за бонусом gl_btn_sort_name за назвою gl_btn_sort_revenue за прибутком gl_btn_unsort без сортування hl_btn_filter_disable вимкн. hl_btn_filter_enable увімкн. hl_btn_filter_settings Налаштування hl_btn_sort_asc за зростанням hl_btn_sort_desc за спаданням hl_btn_sort_name за назвою hl_btn_sort_type за типом hl_btn_sort_waiting Очікування hlf_btn_alle усе hlf_btn_invers обернути hlf_btn_keine нічого Networks Лінії Queueing Черга Road toll Дорожнє мито Scenario Дані про сценарій Transfers Транзит #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic Арктичний клімат desert Пустельний клімат mediterran субтропічний клімат rocky гірський клімат temperate помірний клімат tropic тропічний клімат tundra тундра #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed Помилка завантаження коду сценарію. Can't buy obsolete vehicles! Не можна купляти застарілі засоби! Cannot alter water Ця водойма не може бути згодом осушена або заповнена. Cannot built depot here! Тут не можна побудувати депо. Cannot built this station/building\nin underground mode here. Тут не можна побудувати цю станцію/будівлю \nу підземному режимі. Cannot create generic line!\nSelect line type by\nusing filter tabs. Не можна створити загальну лінію!\nВиберіть тип лінії,\nвикористовуючи фільтр. Cannot create socket Неможливо створити з'єднання Convoi handles exhausted! Досягнуто максимальноо числа засобів! Convoy already deleted! Колону вже вилучено! Das Feld gehoert\neinem anderen Spieler\n Цією ділянкою\nземлі володіє\nінший гравець!\n Der Besitzer erlaubt das Entfernen nicht Права власника\n забороняють видалення!\n Diese Zusammenstellung kann nicht fahren!\n Ця комбінація\n не може рухатися!\n Flugzeughalt muss auf\nRunway liegen!\n Посадковий термінал\n має бути на дорозі!\n Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n Ця будівля аеропорту\nне може бути\nпобудована тут!\n Hier kann kein\nSignal aufge-\nstellt werden!\n Семафор не може\nбути встановлений тут!\n In order to lock the game, you have to protect the public player by password! Для блокування гри вам потрібно захистити відкритого гравця паролем! Lost connection\nto server! Немає з'єднання\n із сервером! Lost synchronisation\nwith server. Немає синхронізації\nіз сервером. Maglevhalt muss auf\nMaglevschiene liegen!\n Зупинка поїзда на магнітній\nподушці має бути\n на відповідній колії! Monorailhalt muss auf\nMonorail liegen!\n Зупинка монорейки \n має перебувати\nна відповідній колії! Monorails are not available yet! Монорейки наразі недоступні. Narrowgaugehalt muss auf\nNarrowgauge liegen!\n Зупинка вузькоколійок\nмає перебувати\nна відповідній колії! No suitable way on the ground! Перон можна побудувати тільки на\n прямій ділянці траси. No through station here! Тут немає транзитної станції! Not enough clearance. Між двома клітинками для побудови шляху різниця висоти має становити принаймні 2 рівні. Not enough money! У вас недостатньо грошей! On narrowgauge track only!\n Тільки на вузькоколійній трасі!\n Only public player can lock games! Тільки відкритий гравець може заблокувати гру! Out of funds Обмаль коштів! Post muss neben\nHaltestelle\nliegen!\n Станційні будівлі \n повинні розміщуватися \n на вільній клітці \n поруч з існуючою \n станцією або зупинкою!\n Protocoll error (expecting game) Вада протоколу Schiffhalt muss im\nWasser liegen!\n Зупинка судна\nможет бути тільки\nбіля доку!\n Server busy Сервер зайнятий Terraforming not possible\nhere in underground view Формування рельєфу під землею неможливе! This tunnel branches. You can try Control+Click to remove. Цей тунель розгалужується. Ви можете натиснути Ctrl+клік для вилучення. Upgrade must have\na higher level Модернізація можлива тільки на\n вищому рівні Vehicle %s cannot choose because stop too short! Транспортний засіб %s не може вибрати, бо зупинка надто коротка! Zughalt muss auf\nSchiene liegen!\n Залізничні станції повинні\nбути розміщені лише на\nзалізничних коліях.\n #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s Довідка

Зміст

Про Simutrans

%1$s

Загальні дані

%2$s

Початок гри

%4$s

Ігровий процес

%5$s

Засоби

%3$s

Інші вікна

%6$s Keyboard Help\n

Keyboard Help

\n Прив'язка клавіш\n

Довідка по клавіатурі

\n

\nДовідка по клавіатурі показує функції, прив'язані до різноманітних натискань комбінацій клавіш на клавіатурі.\n

\n

\nДовідка по клавіатурі відкривається, коли натиснута нерозпізнана клавіша або із Загальної довідки.\n

\n

\nНатискання клавіш чутливе до регістру (вживайте [Shift] для великих букв).\n

\n

\nДо клавіш із призначеними функціями відносяться:\n

\n

\n[Стрілки]: прокручують екран гри у напрямку стрілки.
\n[Backspace]: закриває усі вікна, панелі інструментів та довідкові тексти на екрані гри.
\n[Delete] або [Escape]: закриває верхнє вікно, панель інструментів або довідковий текст на екрані гри.
\n[Enter] або [Return]: вживається для підтвердження дій.
\n[Page-Up] чи [>]: зменшення масштабу мапи.
\n[Page-Down] чи [ символ менше]: збільшення масштабу мапи.\n[F1]: відкриває Довідку Simutrans.

\n

\n[1]: прокручує екран гри на південь.
\n[2]: прокручує екран гри на південний схід.
\n[3]: прокручує екран гри на схід.
\n[4]: прокручує екран гри на південний захід.
\n[6]: прокручує екран гри на північний схід.
\n[7]: прокручує екран гри на захід.
\n[8]: прокручує екран гри на північний захід.
\n[9]: прокручує екран гри на північ.\n

\n

\n[Shift] + мишка: вживається на мапі для показу посилань у Ланцюгу промислових поставок.
\n[CTRL] + інструмент: будує повільніше дороги та колії; або будує випрямленіші дороги і колії.
\n

Decrease water height Зменшити рівень води Highlight railroad tracks Підсвітити залізничні колії Highlite depots Підсвітити депо Highlite electrical transmission lines Підсвітити лінії електропередач Highlite factories Підсвітити фабрики Highlite forests Підсвітити ліси Highlite tourist attraction Підсвітити пам'ятки Increase water height Збільшити рівень води Overlay city limits Показувати межі міст Overlay passenger destinations when a town window is open Відображати точки призначення пасажирів, коли відкрито вікно інформації міста. Overlay schedules/network Відображати усю транспортну мережу або тільки вибраний маршрут. Overlay town names Відображати назви міст Please click on the map to add\nwaypoints or stops to this\nschedule. Натисніть на карті, щоб додати\n до цього розкладу транзитні точки або зупинки. Set tile climate %s Задати клімат для цієї клітинки як %s Show capacity and if halt is overcrowded Показати місткість складів і їх переповнення вантажами Show how many convoi reach a station Показати число т/з, які обслуговуються на станції Show how many people/much is waiting at halts Показати число пасажирів та тривалість їхнього очікування на станції Show initial passenger departure Показати початкове відправлення пасажирів Show level of city buildings Показати рівні міських споруд Show mail service coverage/mail network Показати зону охоплення поштової служби та її мережу Show passenger coverage/passenger network Показати зону охоплення станціями пасажирів та їхню мережу Show speedlimit of ways Показати обмеження швидкості на дорогах Show the change of waiting at halts Показати зміну очікування з минулого місяця на станціях Show the owenership of infrastructure Показати власників інфраструктури Show transported freight/freight network Показати вантажоперевезення та їхню мережу Show usage of network Показати завантаженість ліній Shows a listing with all industries on the map. Показати перелік усіх виробництв на мапі Sum of departure/arrivals at halts Число відправлень/прибуттів на станції #____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s тепер\nпропонує поїздки на автобусі\nіз %s\n до \n%s\nо (%i,%i).\n %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s тепер\nперевозить робітників із\n%s на\n%s\nо (%i,%i).\n %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s\nзапустив %i вантажівок між\n%s о (%i,%i)\nта %s\nо (%i,%i). %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s\nвідкрив нову залізницю\n між %s\nо (%i,%i) та \n%s\nо (%i,%i). Airline service by\n%s\nnow between\n%s \nand %s.\n Авіаційне сполучення від фірми\n%s\nвже діє між\n%s \nта %s.\n Ferry service by\n%s\nnow between\n%s \nand %s.\n Поромне сполучення від фірми\n%s\nвже діє між\n%s \nта %s.\n Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n Мандрівники можуть\nвже користуватися автобусним сполученням від фірми %s\n між \n%s \nта %s.\n #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS Засоби зміни мапи LISTTOOLS Списки MAGLEVTOOLS Поїзд на магнітній подушці MONORAILTOOLS Монорейка NARROWGAUGETOOLS Вузькоколійка RAILTOOLS Залізниця ROADTOOLS Автошляхи SHIPTOOLS Судноплавство SLOPETOOLS Рельєф SPECIALTOOLS Додаткові засоби TRAMTOOLS Трамвай #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s спорудив новий офіс у (%i,%i). Factory chain extended\nfor %s near\n%s built with\n%i factories. Промисловість зростає:\n%s біля %s збільшує ланцюжок.\n%i нових підприємств засновано. Net wealth less than 10%% of starting capital! Чистих активів менше 10%% від стартового капіталу! New %s now available:\n%s\n Доступний новий %s:\n%s New factory chain\nfor %s near\n%s built with\n%i factories. Промислове зростання: новий ланцюжок\nдля %s\nбіля %s\nстворено ще %i фабрик\n. New vehicle now available:\n%s\n З'явився новий транспортний засіб:\n%s\n Now %u clients connected. Наразі вірно %u клієнтів під'єднано. Remove vehicle from map. Use with care! Прибрати т/з з карти. Використовуйте обережно! Screenshot\ngespeichert.\n Знімок екрану\n збережено.\n Sends the convoi to the last depot it departed from! Надіслати т/з у останнє відвідане депо. With a big festival\n%s built\na new monument.\n%i citicens rejoiced. На честь відкриття нового монумента\n %s організовані великі святкування.\n%i городян відзначають цю подію. #___________________________________obsolete____________________________________ #___________________________________obsolete____________________________________ Line Filter Фільтр ліній #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (у депо) \nBauzeit bis до \nBauzeit von \nДоступно від \nCan't open heightfield file.\n \nНеможливо відкрити карту висот.\n \ndirection: \nнапрямки: \nelektrified \nелектрифіковано \nHeightfield has wrong image type.\n \n Карта висот має\n некоректний формат\n зображення.\n \nis reserved by: \nзайнято поїздом \nminimum speed: \nнайменша швидкість: \nnot elektrified \nнеелектрифіковано \nRibi (masked) \nнапрямки\n (перекриті): \nRibi (unmasked) \nНапрямки\n(відкриті): \nSet phases: \nУстановити зеленого світла\nПівніч-Південь Схід-Захід: \nsingle way \nОдносторонній рух \nway1 reserved by \nШлях 1 заразервований \nway2 reserved by \nШлях 2 заразервований \nwith sign/signal\n \nпід знаком/сигналом\n %d buildings\n %d будівель\n %d convois %d колон %d Einzelfahrzeuge im Depot %d т/с зберігається тут %i km/h (max. %ikm/h) %i км/год (макс. %i км/год) %i years %i months old. Вік:\n%i років та %i місяців. %s at (%i,%i) now public stop. %s вже став громадською розв'язкою. %s building %s %s %s %s %s %s city %d %s Місто %s %d %s %s factory %s %s %s фабрика %s %s %s has entered a depot. %s прибув у депо. %s land %d %s %s область %d %s %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. Місто %s\nчерез збільшення\nнаселення до %i\nспорудило тепер нову ратушу. %s\nis crowded. Станція %s переповнена. %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %1$s\n\n\nмаксимальна швидкість %3$i км/год\n\nпоточна швидкість %2$i км/год.\n\n %s\nwas liquidated. Компанія %s стала банкрутом та\nвже ліквідована.\nУсі операції припинені\n та усі активи вже продані. %u Client(s)\n %u клієнт(ів)\n %u Player (%u locked)\n %u гравців (%u заблоковано)\n <очистити розклад> <створити нову лінію> <індивідуальний розклад> <немає лінії> <немає розкладу> <нова лінія> 1 convoi 1 конвой 1 Einzelfahrzeug im Depot 1 тр. засіб у депо 1LIGHT_CHOOSE Яскравість: 1WORLD_CHOOSE Установки для нової гри: 2LIGHT_CHOOSE Кольори: 2WORLD_CHOOSE Номер мапи: 3LIGHT_CHOOSE Швидкість прокрутки: 4LIGHT_CHOOSE Зворотня прокрутка 5LIGHT_CHOOSE Пішоходи на зупинках 5WORLD_CHOOSE Число міст: 6LIGHT_CHOOSE Пішоходи у містах 6WORLD_CHOOSE Щільність руху: 8WORLD_CHOOSE зміна дня і ночі A bridge must start on a way! Міст має починатися в кінці наявного шляху!\n Abfrage Засіб інспекції Abnehmer Споживач: About Про About scenario Автор сценарію Abriss Знищити/усунути Absenken Понизити терен Abspanntransformator Трансформатор Accelerate time Прискорити час Act. load: %u MW\n Факт. навантаж.: %u МВ\n Active player only Лише активний гравець Add forest Додати ліс Add random citycar Додати випадкову міську машину add server Додати сервер Add Stop Додати зупинку Add stops for backward travel Додати зупинки для зворотньої дороги. air Літаки aircraft_tab Вантажні літаки airplane літак Airport Аеропорт AIRTOOLS Авіація All Усе all convoi tooltips Усі поради на т/з Allow city growth Дозволити зростання міста Allow player change Дозволити зміну гравця allowed climates:\n Дозволений клімат:\n Alters a schedule. Додати/видалити зупинки до/із розкладу. Angenommene Waren Товари, які потребують близько розміщеної промисловості anhaengen Додати Anhaenger_tab Прицепи Anheben Підняти терен Appends stops at the end of the schedule Додати зупинку у кінці розкладу Apply Line Призначити лінію April Квітень Arbeiter aus: Працівники живуть у: Arrivals from\n Прибуття від\n Arrived Прибув вантаж Assets Активи Aufloesen Розібрати Aufspanntransformator Трансформатор August Серпень Autohalt muss auf\nStrasse liegen!\n Зупинки автобуса чи автомобіля\nповинні бути\nрозміщені на дорозі. Available Доступно Bahndepot Залізничне депо Bankrott:\n\nDu bist bankrott.\n Банкрутство:\n\nВи - банкрут.\n battery Батарея Baum Дерево baum builder Посадити дерева Baustelle Місце будівництва Bauzeit час будівництва Beenden Вийти Beginner mode Режим навчання Besonderes Gebaeude Пам'ятка BF станція bio гужовий Blockstrecke ist\nbelegt\n \nЗалізн. блок вже зайнято\nіншим поїздом!\n Boden Земля Bonusspeed: %i km/h Найб. можлива швидкість: %i км/год Boost (%%) Прискорення (%%) bridge is too high for its type! Міст зависокий для цього типу! Bridge is too long for this type!\n Занадто довгий проліт\nдля цього типу мостів!\n Bruecke Міст Bruecke muss an\neinfachem\nHang beginnen!\n Мости повинні\nпочинатися на\nпрямому схилі!\n Brueckenboden міст Build air depot Будувати ангар для літаків build choosesignals Будувати семафор вибору перону Build city market Будувати нову крамницю у найближчому місті. Build drain Трансформаторна підстанція build HQ Будувати офіс Build land consumer Побудувати нову електростанцію Build maglev depot Побудувати депо для поїздів на магнітній підвісці Build monorail depot Побудувати депо для монорейок Build narrowgauge depot Побудувати депо для вузькоколійки Build powerline Побудувати лінію електропередачі Build presignals Побудувати двосторонній семафор Build road depot Побудувати гараж Build ship depot Побудувати корабельний док Build signals Побудувати семафори Build train depot Побудувати залізничне депо Build tram depot Побудувати трамвайне депо Build truck depot Побудувати гараж для вантажівок Building costs estimates Орієнтовна вартість будівництва Buildings Міські споруди Built artifical slopes Побудувати штучні нахили Built random attraction Побудувати випадкову пам'ятку Bus_tab Автобуси Can only move from halt to halt or waypoint to waypoint. Може переміщатися тільки\n від зупинки до зупинки, або\n від точки до точки. Cancel Скасувати Cannot connect to offline server! Не можна з'єднатися із сервером поза мережею! Capacity: Місткість: Capacity: %.0f MW Місткість: %.0f МВт\n Capacity: %d%s %s\n Місткість: %3d%s %s\n Capacity: %s\nLoad: %d (%d%%) Місткість: %s\nЗавантажено: %d (%d%%) Cars are not available yet! Автомобілі ще не доступні. cars.\nstate машини\n Cash Баланс рахунку Change player Змінити гравця Chart Діаграма Chat_msg Балачка Choose direction Вибрати напрямок Choose operation executed on clicking stored/new vehicles Вибрати операцію, виконувану при клацанні по новому/збереженому т/з chooses a random map Вибирає випадкову мапу. citicens Жителі City attraction Міські пам'ятки City industries Міська промисловість City list Список міст City size Розмір міста city_road Міська дорога citybuilding builder Будувати міські споруди CityLimit Межі міста cl_title Перелік засобів cl_txt_sort Сортування: Clear block reservation Показати/усунути зарезервовані шляхи clf_chk_aircrafts Літаки clf_chk_cars Автотранспорт clf_chk_indepot у депо clf_chk_maglev Поїзд на магнітній підвісці clf_chk_monorail Монорейковий поїзд clf_chk_name_filter Фільтр за назвою: clf_chk_narrowgauge Вузьколійка clf_chk_noincome Без прибутку clf_chk_noline Без лінії clf_chk_noroute Без маршруту clf_chk_noschedule Нема розкладу clf_chk_obsolete Застаріло clf_chk_ships Кораблі clf_chk_spezial_filter Особливий фільтр: clf_chk_stucked Застрягнуто clf_chk_trains Поїзди clf_chk_trams Трамваї clf_chk_type_filter Типи фільрів: clf_chk_waren Фільр за товаром: clf_title Фільтр списку засобів Climate Control Налаштування ландшафту closed закрито. COLOR_CHOOSE\n Будь ласка, виберіть два\nкольори із\nзображення праворуч: Company bankrupt Банкрутство компанії Company_msg Конкуренти Comparing pak files ... Порівняння графічних пакетів... Configure AI Налаштувати параметри ШІ Configure AI setttings Налаштувати параметри ШІ Congratulation\nScenario was complete in\n%i months %i years. Вітаємо!\nВи завершили сценарій за\n%i місяців та %i років! Connected stops Сполучені зупинки Connected with server З'єднано із сервером Constructed by Створено: Constructed by %s Намальовано: %s\n construction speed швидкість будівництва Construction_Btn Вартість будівництва Consumed Спожито convoi %d of %d Потяг %d із %d convoi error tooltips підказки про помилки над потягом Convoi has been sent\nto the nearest depot\nof appropriate type.\n Колону засобів надіслано\nу найближче депо\nвідповідного типу. Convoi is sold when all wagons are empty. Транспортний засіб буде продано, як тільки він повністю звільниться. convoi mouseover tooltips Підказки над курсором у колоні convoi passed last\nmonth %i\n За минулий місяць \nпройшло %i т/з\n Convois Конвої Convois: %d\nProfit: %s Т/з: %d\nПрибуток: %s Convoys Конвої Copy Convoi Копіювати конвой Copy the selected convoi and its schedule or line Копіювати обраний конвой та його розклад або лінію\n\n cost for removal Вартість вилучення Cost: %8s (%.2f$/km %.2f$/m)\n Вартість: %s (%.2f$/км %.2f$/m)\n Cost: %8s (%.2f$/km)\n Вартість: %8s (%.2f$/км)\n Costs Видатки Create a new line based on this schedule На основі цього розкладу створити нову лінію curiosity builder Творець дивини curlist_title Список визначних місць Currently playing: Наразі звучить: Customers live in: Відвідувачі живуть у: deactivated in online mode Не активний поки не у мережі Deccelerate time Уповільнити час December Грудень decrease underground view level зменшити рівень підземного огляду Del Stop Вилучити Delete Line Вилучити лінію Delete the current stop Вилучити поточну зупинку Delete the selected line (if without associated convois). Вилучити обрану лінію (якщо відсутні прив'язані до неї засоби) Delete this file. Вилучити цей файл. Delivered Доставлено Demand Попит Demand: %.0f MW Попит: %.0f MW\n Denkmal Пам'ятник Departed Відправлено Departure board Час відправлення Departures to\n Відправлення до\n Depots Депо Der Tunnel ist nicht frei!\n Тунель не порожній!\n Destination Призначення Destroying map ... Вилучення старої мапи… Details Подробиці Die Bruecke ist nicht frei!\n Міст не вільний!\n diesel Дизель directmail пряма пошта Direkt erreichbare Haltestellen Прямі маршрути звідсіля disable midi Вимкнути музику MIDI Display settings Параметри графіки Distance Відстань Dock Док Dock must be built on single slope! Порт повинен будуватися на рівній ділянці. Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen Для виплати боргу у Вас залишилося %d місяців. Durchsatz Макс. Economy Економіка та міста Eigenbesitz\n Муніципальна власність\n Ein %s\npasst hier nicht.\n "%s"\nне поміститься тут!\n Einstellungen aendern Параметри electric Електропривід Electricity Електрика Electricity producer\n\n Виробник електрики\n\n Electrics_tab Електричні т/з Electrify track Електрифікація enlarge map Збільшити карту enter a value between %i and %i Введіть значення між %i і %i Enter address Введіть адресу Enter Password Введіть пароль Error Помилка Erzeuge neue Karte.\n Будь ласка, почекайте,\n створюється нова карта.\n\n (Це може зайняти\n кілька хвилин\n для великих карт)\n Es wird bereits\nein Fahrplan\neingegeben\n Створюється\n розклад.\n Завершіть його\n перед упорядкуванням!\n Fabrikanschluss З'єднані фабрики Fabrikname Назва фабрики Factories Фабрики factory details Подробиці про фабрику factorybuilder Конструктор фабрики Fahrplan Розклад Fahrtziel Призначення: Fahrzeuge koennen so nicht entfernt werden Транспортний засіб\nне може бути прибрано\nтаким чином!\n Fahrzeuge: Трансп. засоби: Farbe Колір гравця Fast forward Прискорити час February Лютий Ferry_tab Пороми Fertig Готово Filename Назва файлу: Files from: Файли із: Filter: Фільтр: Finances of %s Фінанси %s Finanzen Фінанси find mismatch знайти невідповідність fl_title Список фабрик Flug_tab Пасажирські літаки follow me Слідувати Follow the convoi on the map. Слідувати за конвоєм на мапі. Forest Ліс Found new city заснувати нове місто FPS: Кадр/с: Fracht Вантажі Frame time: Час кадру: Free Capacity Вільно freeplay mode Без банкрутства Friction: поточний фактор тертя: fuel_cell елемент живлення Full load Мінімальне завантаження: Fundament Фундамент Fussgaenger Пішохід Game info Дані про гру GAME PAUSED ГРА - ПРИЗУПИНЕНА Game_msg Загальне Gear: Спорядження: Gebaeude Будівля General Загальне Generated Згенеровано Generation: %.0f MW Виробництво: %.0f МВт\n Gewicht Маса Gewinn Прибуток: Give the selected vehicle(s) an individual schedule Призначити обраним т/з індивідуальний розклад gl_btn_sort_catg за категорією gl_title Список вантажів go home У депо Goods Вантажі Goods AI ШІ торговець Goods list Список товарів Gross Profit Валовий прибуток Groundobj Наземний об'єкт Grow city Збільшити місто Growth Зростання міста H зупинка Hangar Ангар Happy Щасливі Haus kaufen Купити дім Helligk. Графіка Help Довідка Help text not found Текст довідки не знайдено. hide all building приховати усі будівлі hide city building приховати міські будівлі hide objects under cursor приховати об'єкти під курсором hide station names приховати назви станцій hide transparent прозорість замість приховування hide trees Приховати дерева Hier warten/lagern: Товари і пасажири, які очікують: Higher transport fees, crossconnect all factories Вищі транспортні збори, вимкнути вчасно Highlite schedule Підсвітити маршрут hl_title Список станцій hl_txt_filter Фільтр: hl_txt_sort Упорядкувати за: hlf_chk_airport Аеропорти hlf_chk_anleger Док hlf_chk_bahnhof Залізнична станція hlf_chk_bushalt Автобусна зупинка hlf_chk_frachthof Навантажувальна платформа hlf_chk_keine_verb без з'єднання hlf_chk_maglevstop Станція магн. підвіски hlf_chk_monorailstop Зупинка монорейки hlf_chk_name_filter Фільтрувати назви: hlf_chk_narrowgaugestop Станція вузькоколійки hlf_chk_overflow Переповнення hlf_chk_spezial_filter Особливий фільтр: hlf_chk_tramstop Трамвайна зупинка hlf_chk_type_filter Типи фільтрів: hlf_chk_waren_abgabe Виробництво: hlf_chk_waren_annahme Потреби: hlf_title Фільтр списку станцій Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. Початкове депо не знайдено!\n Ви повинні послати засіб\n у депо вручну. Homeless Бездомні hydrogene Гідроген Idle: Простій: ignore climates ігнорувати клімат In the industry legend show only currently existing factories Відображати список наявних у даний момент фабрик на карті. In Transit У дорозі Increase Industry density Збільшити щільність промисловості increase underground view level збільшити підземний рівень огляду industrial building Промислові будівлі Init map ... Створення мапи… Input Споживання Ins Stop Вставити Insert stop before the current stop Вставити зупинку перед поточною зупинкою Intercity road len: Довжина міжміських доріг: Intro. date: Початок експл.: invalid невизначений Invalid coordinate Хибні координати isometric map Ізометричний вигляд January Січень join game Грати у мережі July Липень Jump to Перестрибнути на June Червень Kann Spielstand\nnicht laden.\n Не можу завантажити\nзбережену гру!\n Kann Spielstand\nnicht speichern.\n Не можу відкрити для запису\nфайл із збереженням гри!\n Kein Besitzer\n Без власника\n keine нічого Keine Einzelfahrzeuge im Depot Т/з не зберігаються тут. Keyboard_Help\n Довідка з клавіатури\n koord Координати Kreuzung Перехрестя labellist_title Список маркерів Lade Relief Завантажити карту висот Laden Завантажити нову гру Land attraction Заміська пам'ятка Land industries Виробничі ланцюжки: LANG_CHOOSE\n Будь ласка, виберіть\nвашу бажану мову:\n LARGE_NUMBER_STRING М LARGE_NUMBER_VALUE 1е6 Last Month Останній місяць: Last Year Торік: Leaving depot! Залишає депо! leer порожній Legend Леґенда мапи Leistung Потужність Leistung: %d kW Потужність: %d кВт Leitung Лінія електропередачі letzen Monat: diesen Monat: минулий місяць: цей місяць: Line Лінія Line Management Управління лініями Lineless convoys serving this stop Т/з без ліній, які обслуговують цю зупинку Lines serving this stop Лінії, які обслуговують цю зупинку LKW_tab Вантажівки Load game Завантажити гру load height data from file Завантажити дані про висоти із файлу. Load scenario Завантажити сценарій loaded завантажено loaded passenger/freight Упорядкувати пасажирів/товари за Loading (%i->%i%%)! Навантаження (%i->%i%%) Loading addon paks ... Завантаження пакетів доповнень… Loading map ... Завантаження мапи… Loading paks ... Завантаження пакетів… Loading skins ... Завантаження оболонок… Lock game Не дозволяти зміну гравця (потрібне підтвердження). Lokomotive_tab Локомотиви m3 м³ Maglev Магн. підвіска maglev vehicle Поїзд на магн. підвісці maglev_track Колія для магн. підвіски Maglevdepot Депо для магн. підвіски Mail Demand %d\n Потреба на пошту %d\n Mailbox Поштова скринька Mailbox Options Параметри центру повідомлень Maintenance Утримання make stop public (or join with public stop next) costs %i per tile and level створити публічну зупинку (або приєднати до сусідньої публічної зупинки) вартістю %i$ за клітинку та рівень Manual (Human) Підручник (людина) Manufactured: Вироблено: Map roughness Нерівність мапи: map zoom масштаб March Березень Margin (%%) Запас Marker Знак max макс Max Boost (%%) Макс. прискорення (%%) Max income: Макс. дохід: Max. speed: Макс. швидкість: Maximum 254 stops\nin a schedule!\n Досягнуто максимуму 254 зупинок\nу розкладі!\n maximum length of rivers Найбільша довжина річок Maximum tile height difference reached. Найбільша висота\nрізниця між двома\nклітинками вже\nдосягнута. Maxspeed Найбільша швидкість May Травень Median Citizen per town Середнє число жителів на місто: Meldung Повідомлення Menge кількість MessageOptionsText \nНовий рік\n\nНовини гравців\n\nМіські новини\n\nБез маршруту\n\nНові призначення\n\nБалачка\n\nНові т/з\n\nЗаповнення станцій\n\nПроблеми\n\nТранспортні затори\n\nСценарій min мін. minimum length of rivers Найменша довжина річок Missing pakfiles Загублені об'єкти! Modify the selected line Змінити вибрану лінію Monate alt місяців. Monorail Монорейка monorail vehicle монорейковий т/з monorail_track Монорейкова колія Monorailboden Основа монорейки Monoraildepot Монорейкове депо month wait time час очікування за місяць Months Місяці Monument Пам'ятник Monuments Пам'ятники Mountain height Висота гір: Movingobj переміщення об'єкту Music playing disabled/not available Музика вимкнена/недоступна. Music volume: Гучність музики: mute sound Без звуку Name Назва Narrowgauge Вузькоколійка Narrowgauge are not available yet! Вузькоколійка ще недоступна! narrowgauge vehicle вузькоколійний т/з narrowgauge_track вузькоколійний шлях Narrowgaugedepot Вузькоколійне депо Net ID: %p ІД мережі: %p\n Net Wealth Чисті активи Net wealth near zero Чисті активи близькі до нуля Neue Karte Нова гра Neue Welt Створити нову гру new convoi Новий конвой New Line Нова лінія New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. Створена нова лінія!\nТепер Ви можете призначити лінію,\nвибираючи її з поданого\nнижче списку. New Vehicles Нові т/з: Nickname: Прізвисько: no buildings hidden не приховувати будівлі no convois нема т/з No goods are loaded onto this convoi. Вантажі не будуть завантажуватися на цей т/з. no goods waiting Нема товарів, які очікують no load Лише розвантажувати No Route Нема шляху No stop here! Не зупинятися тут! No suitable ground! Нема придатної землі! No terminal station here! Тут немає терміналу! no timeline без часової шкали no tree Без дерев No. of Factories Число фабрик Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n Встановіть розклад \nт/з перед відправкою!\n none нема nord Північ nordost Північний схід nordwest Північний захід Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! Розклад не можна \n поміняти під час \n пошуку шляху. Not enough fields would remain. Навколо ферми залишилося занадто мало полів. November Листопад Now active as %s.\n Тепер граємо за %s.\n Number of rivers Число річок Object Об'єкт Odometer: %s km Одометр: %s км Ok Гаразд Oktober Жовтень On loan since %i month(s) На кредиті від %i місяця(ів) On this map, you are not\nallowed to change player!\n Ця гра заблокована!\n Зміна гравця неможлива!\n\n\n Only city chains Будувати ланцюжки міської промисловості Only first %d differing paks reported. There are probably more. Повідомили лише перші %d окремі пакунки. Але їх є, вірогідно, більше. Only land chains Лише заміські ланцюжки Only one transformer per factory! Тільки один трансформатор на фабрику! Only show goods which are currently handled by factories Показати тільки товари, що наразі оброблені фабриками open відкрити Operation Операційні витрати Ops Profit Операційний прибуток Optionen Параметри Or enter a server manually: Або введіть сервер вручну: Origin Походження ost Схід Output Вироблено Ownership Власник Pak which may cause severe errors: Наступні об'єкти (пакети) відсутні, що може призвести до відмови транспортних ланцюжків: Pak which may cause visual errors: Наступні об'єкти (пакети) були замінені подібними об'єктами: Pak(s) different: Пакет(и) різний(і): Pak(s) missing on client: Пакет(и) загублені на клієнті: Pak(s) not on server: Пакет(и) не на сервері: Pakset differences Відмінності між наборами пакетів paletten ящики Pas_tab Пасажирські поїзди Passagiere Пасажири Passagierrate Наявність пасажирів Passagierziele Призначення пасажирів/пошти Passenger AI ШІ пасажирів Passenger Demand %d\n Попит на пасажирів %d\n Passengers %d %c, %d %c, %d no route Пассажири %d %c, %d %c, %d без маршруту Passengers %d %s, %d %s, %d no route Пассажиры %d %s, %d %s, %d без маршруту Password Пароль: Pause Пауза Pax <%i> Mail <%i> Пасажирська <%i> пошта <%i> PaxDest Цілі подорожі Percent Electricity Виробництво електри (%% від потреби): Planes are not available yet! Літаки ще недоступні! Plant tree Посадити дерева player Гравець player -1 Людина-гравець player 0 державна служба player 1 Напік 128 АС player 10 Гравець 10 player 11 Гравець 11 player 12 Гравець 12 player 13 Гравець 13 player 2 Тріккі транспорт player 3 Меєр мувінґ Ко. player 4 ВМ Експедитор player 5 ТОВ „Супертранс“ player 6 ПСК і Ко КҐ player 7 Гравець 7 player 8 Гравець 8 player 9 Гравець 9 Please choose vehicles first\n Спершу виберіть транспортні засоби!\n Post Пошта Postrate Наявність пошти Power Потужність Power (MW) Потужність (МВт) Power: Потужність: Power: %4d kW\n Потужність: %4d кВт\n Powerlines Лінії електропередач Problems_msg Проблеми Produced Вироблено Production of %s has been stopped:\n%s\n Виробництво %s було припинено:\n%s\n Production/Boost Виробництво/Зростання Produktion Виробництво: Profit Прибуток promote to line Нова лінія q1 Весна q2 Літо q3 Осінь q4 Зима Query server Сервер запиту rail car Рейкова машина random випадково Random age випадковий рік Random map Випадкова мапа Rathaus Ратуша Rating Оцінка ratio_pax Співвідношення Relevant Відповідні Reliefkarte Мапа Remove Вилучити мітку remove airstrips Вилучити смугу remove channels Вилучити канали remove interm. signals Вилучити вбудовані семафори remove maglev tracks Вилучити колії на магн. подвісці remove monorails Вилучити монорейки remove narrowgauge tracks Вилучити вузькоколійки remove powerlines Вилучити ЛЕП remove roads Вилучити дороги remove tracks Вилучити колії Remove wayobj %s Вилучити дорожній об'єкт %s replace other signals замінити інші семафори replace stop Замінити зупинку request closing закриття запиту residential house Житлові будинки Restore natural slope Відновити природний схил. Restwert: Вартість продажу: Retire. date: Кінець експл.: return ticket квиток назад Revenue Виручка Revision: Версія: road дорога road vehicle траспортний засіб Roadsign Дорожний знак Rotate map Повернути карту Rotation Поворот Routing Прокладка маршруту sack мішок(ів) sail вітер Saving map ... Збереження мапи… Scenario complete: %i%% Перебіг сценарію: %i%% Scenario Debug Зневадження Scenario Error Log Помилки коду Scenario Goal Ціль сценарію Scenario Info Анотація Scenario information Дані про поточний сценарій Scenario Result Перебіг Scenario Rules Правила Schedule changing! Змінюється розклад! Schienentunnel Побудувати залізничний тунель Schiff_tab Кораблі Schiffdepot Верф Schleppkahn_tab Баржі Screenshot Знімок екрану Seasons Пори року Sehenswuerdigkeit Туристична пам'ятка Select a server to join: Виберіть сервер, аби приєднатися: Select a theme for display Виберіть тему для дисплею Sell the selected vehicle(s) Продати вибрані т/з. sended Надіслати листа SEP_FRACTION , SEP_THOUSAND . SEP_THOUSAND_EXPONENT 3 September Вересень Server did not respond! Сервер не відповідав Serves Line: На лінії: Service Послуга set signal spacing Задати відстань між сигналами Setting Налаштування Ship Корабель shops and stores Магазини та офіси Show all Показати усе show all building Показати усі будівлі Show also vehicles no longer in production. Показати також т/з, які більше не виробляються. Show also vehicles that do not match for current action. Показати також т/з, які не можуть бути використані для вибраної дії Show even servers with wrong version or pakset Також показувати сервери з невірною версією або пакетом show grid Показати сітку Show industry Перелік підприємств Show legend Показати леґенду Show map scale Показати шкалу висот Show mismatched Показати невідповідність Show obsolete Показати застаріле Show offline Показати поза мережею Show only used Показати лише використане Show schedules Показати розклади Show servers that are offline Показати сервери поза мережею Show servers where game version or pakset does not match your client Показати сервери, де версія гри чи набору не збігається з версією вашого клієнта show station coverage Показати покриття станцій show station names Показати назви станцій show waiting bars Показати смуги очікування show/hide block reservations Показати/приховати блокування резервувань Show/hide estimated arrival times Показати/приховати очікуваний час прибуття show/hide object owner Показати/приховати власника об'єкта Show/hide statistics Показати/приховати статистику Shows buttons on special topics. Показати кнопки на спеціальних темах. Shows consumer/suppliers for factories Показати споживачів/постачальників для фабрик Shows the color code for several selections. Показати код кольору для окремих виділень. Shows the currently selected schedule Показати поточний обраний розклад Shrink city Зменшити місто shuffle midis перемішати музику Signal Семафор signal spacing Відстань між семафорами Sim: Sim-петлі: Similar view as the main window Відображати карту так само, як відображається ігрове вікно. Size (%d MB): Розмір (%d МБ): sliced underground mode Огляд шарів карти slot empty слот порожній Smart hide objects Кмітливо ховати об'єкти Sort by Сортувати за Sort waiting list by Сортувати список очікування за Sound Звук Sound settings Параметри звуку Sound volume: Гучність звуку: special freight Особливий вантаж Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. Зростання швидкості:\nдорога %i км/год, залізниця %i км/год\nкораблі %i км/год, літаки %i км/год. Speedlimit Обмеження швидкості Speichern Зберегти Spieler Гравець Spieler(mz) Гравці Spielerliste Список гравців Spielstand wurde\ngeladen!\n \nГра успішно завантажена!\n Spielstand wurde\ngespeichert!\n \nГра успішно збережена!\n Sprache Мова Sprachen Мови Stadtinformation Міська статистика Start Старт Start the selected vehicle(s) Відправити на лінію обрані т/з. Starte Spiel Розпочати гру Station tiles: Секції станції: Station_msg Станції Status Стан steam пара́ Step timeline one year Перевести час на рік уперед. Stops Зупинки Storage Склад Storage capacity Місткість складу Strassendepot Гараж Strassentunnel Автомобільний тунель street car трамвай sued Південь suedost Південний схід suedwest Південний захід Summer snowline Літня лінія снігу Supplied: %.0f %% Поставлено: %.0f %% Suppliers Постачальники: Tage alt днів тому. Theme selector Вибирач тем There are still vehicles\nstored in this depot!\n Наявні транспортні засоби\nу цьому депо!\n This Month Цього місяця: This Year Цього року: Tile not empty. Ділянка не порожня.\nБудь ласка, видаліть усе\nперед будівництвом\n штучного нахилу. timeline дотримуватися часової осі tl_title Перелік усіх міст To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. Потік транспорту у %1$s на піднесенні:\n%3$i платників податків зробили реальною побудову\nof %2$s. To heavy traffic\nresults in traffic jam.\n Надто щільний рух\nспричинив пробку на дорозі.\n Toggle day/night view Перемкнути день/ніч Toggle vehicle tooltips Перемкнути підказки для засобів tonnen т Total inhabitants: Всього жителів: Tourist attractions Туристичні принади: Tourists Пам'ятки Town_msg Нові призначення Town: %s\n Місто: %s\n Towns Міста track шлях Tracks Колії Traffic Рух Train Поїзд Trains are not available yet! Поїзди ще недоступні! Tram Трамвай tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. трамвай %i км/год, монорельс %i км/год\nмагнітна підвіска %i км/год, вузькоколійка %i км/год. tram_track Трамвайна колія Tramdepot Трамвайне депо Trams are not available yet! Трамваї ще не доступні! Transferring game ... Передається гра… Transformer only next to factory! Трансформаторна підстанція повинна бути біля підприємства! Translation Переклад transparent station coverage прозора зона покриття Transported Перевезено TrolleyBus_tab тролейбуси Truck Вантажівка tt_Other Інше Tunnel muss an\neinfachem\nHang beginnen!\n Тунелі мають починатися\nна прямому схилі!\n Tunnel must start on single way! Тунелі повинні починатися на одному шляху! Tunnelboden Тунель underground mode підземний режим UNDO failed! Скасування більше недоступне.\nВи можете скасувати створення \nмаршруту тільки, якщо на ньому \nне буде побудовано сигналів /\nстанцій / зупинок або ще \nщось. Undo last ways construction Скасувати останнє будівництво траси Unemployed Безробітні Unhappy Нещасливі units/day /місяць, Update Line Змінити лінію upgrade HQ Поліпшити офіс Usage: %.0f %% Використання: %.0f %% Usage/Output Споживання/виробництво Use beginner mode Використати режим початківця Use timeline start year Використати рік часової шкали: Vehicle %s can't find a route! %s не може знайти шлях! Vehicle %s is stucked! %s у пробці! Vehicle details Дані про засіб Verbrauch Споживання: Vergroessere die Karte\n Збільшити мапу.\n Verkauf Вилучити зараз! verkaufen Продати Verkehrsteilnehmer Міський транспорт Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n У Вас утворилася заборгованість!\n\n Залишилося %d місяць(а,ів)\n щоб виплатити борг!\n via через (деталі) via %s\n через %s\n via Menge через (кількість) voranstellen Поставити спереду Waggon_tab Вагони waiting очікують Waiting for clearance! Очікують звільнення шляху! Walked Піші walking ходьба Warnings_msg Трафік Wasser Вода Water Море Water level Рівень води: water vehicle Водний т/з way %s cannot longer used:\n Шляхи типу %s не можуть бвльше використовуватися.\n way %s cannot longer used:\n%s\n Дороги типу %s не можуть більше використовуватися.\n%s\n way %s now available:\n Тепер доступні дороги типу %s.\n Way toll Дорожнє мито Ways not connected Дороги не з'єднані Wegpunkt Дорожня точка Weight: Вага: Wert Значення west Захід Winter snowline Зимова лінія снігу withdraw Виключити з обігу Withdraw All Виключити усе з обігу WRONGSAVE \n Несумісна версія \n збереження. Не можу завантажити \n цей файл.\n Year %i has started. Розпочався %i рік. Years Роки Your primary color: Основний колір: Your secondary color: Вторинний колір: Zielort призначення zooming in збільшення zooming out зменшення Zu nah am Kartenrand Занадто близько\n до краю карти,\nщоби щось будувати!\n #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. Новий світовий рекорд швидкості поїзда на магнітній подушці: %.1f км/год, %s. New world record for monorails: %.1f km/h by %s. Новий світовий рекорд швидкості монорейкових поїздів: %.1f км/год, %s. New world record for motorcars: %.1f km/h by %s. Новий світовий рекорд швидкості автомобілів: %.1f км/год, %s. New world record for narrowgauges: %.1f km/h by %s. Новий світовий рекорд швидкості вузькоколійних поїздів: %.1f км/год, %s. New world record for planes: %.1f km/h by %s. Новий світовий рекорд швидкості літаків: %.1f км/год, %s. New world record for railways: %.1f km/h by %s. Новий світовий рекорд швидкості поїздів: %.1f км/год, %s. New world record for ship: %.1f km/h by %s. Новий світовий рекорд швидкості кораблів: %.1f км/год, %s. #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ 0center середмістя 0extern село 0suburb передмістя 1center %s %s 1extern %s %s околиця 1suburb %s %s %s 2center %s %s головна 2extern %s %s зовнішня 2suburb %s Рівнини %s %s 3center %s основна %s 3extern %s сільська %s 3suburb %s сільська %s %s 4center %s середмістя %s 4extern %s передмістя %s 4suburb %s приміська %s 5center %s ділова %s 5extern %s %s транзитна 5suburb %s місто %s 6center %s вузол %s 6extern %s біржа %s 6suburb %s окраїна %s %s 7center %s місто %s 7extern %s рівнини %s 7suburb %s парк %s 8center %s ділова %s 8extern %s %s запасна %s 8suburb %s супутник %s 9center %s осьова %s 9extern %s об'їзна %s 9suburb %s проміжна %s simutrans-124.5/simutrans/text/uk/000077500000000000000000000000001520675460500172145ustar00rootroot00000000000000simutrans-124.5/simutrans/text/uk/airtools.txt000066400000000000000000000223311520675460500216120ustar00rootroot00000000000000Авіація

Авіація


Меню авіації містить інструменти для побудови всіх елементів мережі повітряного транспорту: будівництво та видалення руліжних доріжок і злітно-посадкових смуг; станцій, терміналів, складів та вантажних платформ; літакових ангарів та інших будівель. При грі в режимі плину часу можлива поява інших можливостей.

Натисніть на піктограму літака на головній панелі інструментів, щоб відкрити меню авіації.

Затримуючи курсор миші над будь-якою піктограмою, Ви можете бачити назву об'єкта, ціну спорудження, вартість обслуговування (у круглих дужках) на місяць, максимально допустиму швидкість і максимально можливу вагу т/з для пересування по об'єкту (якщо це руліжні доріжки або злітно-посадкові смуги).

Підказка: Один з варіантів побудови простого аеропорту:

1) побудуйте злітно-посадкову смугу;

2) збоку прибудуйте до неї рубіжну смугу;

3) на вільному кінці руліжної доріжки розмістіть аеровокзал або інший вантажно-розвантажувальний термінал;

4) на вільному кінці руліжної доріжки розмістіть ангар для літаків;

5) добудуйте інші будівлі, якщо це необхідно.

Меню може складатися з наступних кнопок (зліва направо):

Руліжні доріжки: необхідні для переміщення літаків по землі між злітно-посадковими смугами, ангарами і вантажно-розвантажувальними терміналами. Вони будуються на плоскій землі в головному ігровому екрані.

УВАГА: Якщо руліжну доріжку долучити до кінця злітно-посадкової, то літаки не зможуть користуватися злітно-посадковою смугою.

Для побудови натисніть на відповідну піктограму (курсор повинен змінити свій вигляд) і простягніть доріжку в потрібному місці. У спливному віконці буде вказана вартість будівництва.

Підказка: Використовуйте інструмент Зруйнувати/Прибрати для видалення непотрібної ділянки руліжної доріжки. Скасування останньої дії [z] не поверне Вам гроші, витрачені на будівництво.

Злітно-посадкові смуги (ЗПС): необхідні для зльоту і посадки літаків. Вони будуються на плоскій землі в головному ігровому екрані і можуть перетинатися.

Для побудови натисніть на відповідну піктограму (курсор повинен змінити свій вигляд) і простягніть смугу в потрібному місці. У спливному віконці буде вказана вартість будівництва.

Підказка: Використовуйте інструмент Зруйнувати/Прибрати для видалення непотрібної ділянки ЗПС. Скасування останньої дії [z] не поверне Вам гроші, витрачені на будівництво.

Видалити смугу: видалить ділянку руліжної доріжки або злітно-посадкової смуги, якщо на них немає літака. Використовуйте це для відшкодування вартості споруди.

Для видалення натисніть на відповідну піктограму (курсор повинен змінити свій вигляд), а потім натисніть на знищуваний елемент смуги, простягніть для видалення декількох ділянок і відпустіть кнопку миші.

Підказка: Видаливши з'єднання ЗПС з аеровокзалом, будьте готові до того, що ця смуга не зможе відправляти/приймати літаки. Використовуйте інструмент Зруйнувати/Прибрати для видалення однієї ділянки смуги.

Літакове депо (ангар): необхідне для купівлі та управління літаками. Їх можна будувати лише на кінцях руліжних доріжок. Ангари мають щомісячну вартість обслуговування.

Для побудови натисніть на відповідну піктограму (курсор повинен змінити свій вигляд) і побудуйте ангар в потрібному місці.

Підказка: Використовуйте інструмент Зруйнувати/Прибрати для видалення непотрібного ангара. Скасування останньої дії [z] не поверне Вам гроші, витрачені на будівництво.

Аеровокзали та посадкові рукави: потрібні для навантаження і розвантаження вантажів і пасажирів у літаки та з них.

Якщо Ви побудуєте будинок не поруч з вже існуючою станцією, то отримаєте нову станцію.

Їх можна будувати лише на кінцях руліжних доріжок. Аеровокзали та термінали мають щомісячну вартість обслуговування й область охоплення для прийому вантажів і пасажирів від навколишніх фабрик і будівель. Літак приземлиться тільки, якщо є вільний навантажувально-розвантажувальний термінал.

Для побудови натисніть на відповідну іконку (курсор повинен змінити свій вигляд) і побудуйте будинок в потрібному місці поверх руліжної доріжки.

Підказка: Використовуйте інструмент Зруйнувати/Прибрати для видалення непотрібного будинку. Скасування останньої дії [z] не поверне Вам гроші, витрачені на будівництво.

Інші будівлі: тут перераховані додаткові будівлі, які розширюють функціонал вже побудованих станцій: збільшення місткості складів, площі охоплення. Вони також можуть мати щомісячну вартість обслуговування. На піктограмах таких будинків часто вказується, на які перевезення вони розраховані (пасажири, пошта або вантажі).

Для побудови натисніть відповідну піктограму (курсор повинен змінити свій вигляд) і побудуйте будинок в потрібному місці біля існуючого аеровокзалу або терміналу.

Підказка: Використовуйте інструмент Зруйнувати/Прибрати для видалення непотрібного будинку. Скасування останньої дії [z] не поверне Вам гроші, витрачені на будівництво.

simutrans-124.5/simutrans/text/uk/baum_build.txt000066400000000000000000000043711520675460500220650ustar00rootroot00000000000000Посадка дерев

Посадка дерев

Дане меню дозволяє вам висаджувати в грі окремі дерева. Ця можливість носить чисто декоративний характер, оскільки жодною ді користі від цього немає (крім естетичної).

У лівій частині представлений список всіх існуючих в грі дерев, які можна висадити. Дві вкладки Переклад та Об'єкт дозволяють вибрати варіант відображення об'єкта - за назвою дерева (Переклад) або по технічному його позначенню (Об'єкт). Оберіть будь-яке дерево і ви побачите більше інформації про нього. Щоб посадити дерево досить вибрати його в списку і розмістити на ігровій області екрану.

У правій частині перераховані кілька параметрів:

Ігнорувати клімат: можливість розсаджувати дерева в будь-якому кліматі.

Випадковий вік: зазвичай щойно посаджене дерево є саджанцем з нульовим віком. Потім, з плином часу, дерево поступово зростає. Ця установка дозволяє розсаджувати дерева випадкового віку - від саджанців до старих великих дерев.

Обертання: якщо у графіку дерева доступні кілька варіантів його зовнішнього вигляду, ця настройка дозволить вибрати, якого саме вигляду буде розміщений дерево. Можна також вибрати параметр "випадково", щоб зовнішній вигляд дерева визначався випадковим чином.

simutrans-124.5/simutrans/text/uk/bridges.txt000066400000000000000000000041511520675460500213750ustar00rootroot00000000000000Інструмент для будівництва мосту - довідка

Будівництво моста

Міст будується так само, як і тунель.

Автодорожній міст

Ви повинні спершу створити шлях, який з'єднається мостом. Обидва шляхи повинні точно підходити (розташуватися один навпроти іншого). Мишкою виберіть інструмент автомобільний міст. Створіть посилання, натиснувши на один з кінців з'єднуваних доріг. Міст ви можете спорудити навіть так, що один його кінець буде на гірці (до висоти 1-це положення). Якщо трапилося, що шлях не під'єднаний, спробуйте перетягнути шлях на одну позицію нижче моста.

Залізничний міст

Маєте створити залізничну колію, яку потім з'єднаєте мостом. Обидві ділянки колії повинні точно підходити (розташуватися один навпроти іншого). Мишкою виберіть інструмент залізничний міст. Створіть посилання, натиснувши на один з кінців з'єднуваних колій. Міст з повним дном (без бічних конструкцій) має ту перевагу, що ви можете розмістити на ньому семафори. Міст ви можете спорудити навіть так, що один його кінець буде на гірці (до висоти 1-це положення). Якщо трапилося, що колія не під'єднана, то спробуйте перетягнути її на одну позицію нижче моста.

simutrans-124.5/simutrans/text/uk/citybuilding_build.txt000066400000000000000000000065661520675460500236370ustar00rootroot00000000000000Будівництво міських будинків

Будівництво міських будинків

Даний інструмент дає можливість будувати міські будівлі в будь-якому місці, де Ви захочете. Дана можливість не є обов'язковою, а більш естетичною.

У лівій частині представлений список всіх доступних міських будівель, які можна побудувати. Дві вкладки Переклад та Об'єкт дозволяють вибрати варіант відображення об'єкта - за назвою будівлі (Переклад) або по технічному його позначенню (Об'єкт). Виберіть яку-небудь будівлю і ви побачите більше інформації про неї. Щоб побудувати будинок, досить вибрати його в списку і розмістити на ігровій області екрану.

У правій частині перераховані кілька параметрів:

Ігнорувати клімат: можливість будувати будівлі в будь-якому кліматі.

Режим плину часу: показати тільки будівлі, доступні в даний момент часу.

Показати застарілі: показати будівлі, які були доступні в минулому.

Житлові будівлі: показати тільки будівлі, в яких проживають люди. Ці будівлі зазвичай мимовільно будуються, коли місто розширюється при збільшенні населення.

Магазини та офіси: показати тільки комерційні будівлі. Ці будівлі надають робочі місця і ведуть торгівлю (у тому числі і з гравцями), вони зазвичай будуються при великій кількості безробітних жителів у місті.

Промислові будівлі: показати тільки фабрики. Ці будівлі надають робочі місця і ведуть торгівлю (у тому числі і з гравцями). Однак, в цей список не входять великі промислові фабрики (наприклад, Електростанція або Вугільна шахта), які розміщуються за містом і з якими співпрацює гравець.

Обертання: якщо у малюнку будівлі доступні кілька варіантів її зовнішнього вигляду, то ця установка дозволить вибрати, якого саме вигляду будинок буде розміщений. Можна також вибрати параметр "випадково", щоб зовнішній вигляд будівлі визначався випадковим чином.

simutrans-124.5/simutrans/text/zh.tab000066400000000000000000001127241520675460500177150ustar00rootroot00000000000000§T. Chinese PROP_FONT_FILE wenquanyi_9pt.bdf ################################################################################ # DO NOT EDIT THIS FILE. # # USE https://translator.simutrans.com FOR YOUR CHANGES # # AND DOWNLOAD THIS FILE AGAIN WITH YOUR SUGGESTIONS # ################################################################################ # # # Simutrans Base Translation File # # Scenario: Base texts # # Language: zh T. Chinese # # Encoding: UTF-8 # # Font: wenquanyi_9pt.bdf # # Date Created: 11.03 2026 # # # ################################################################################ #__________________________________button_text__________________________________ #__________________________________button_text__________________________________ cl_btn_filter_disable 禁用 cl_btn_filter_enable 啓用 cl_btn_filter_settings 設置 cl_btn_sort_asc 升序 cl_btn_sort_desc 降序 cl_btn_sort_id 内部ID cl_btn_sort_income 收入 cl_btn_sort_name 名稱 cl_btn_sort_type 類型 clf_btn_alle 全選 clf_btn_invers 反選 clf_btn_keine 取消選擇 gl_btn_sort_bonus 紅利 gl_btn_sort_name 名稱 gl_btn_sort_revenue 收益 gl_btn_unsort 未排序 hl_btn_filter_disable 禁用 hl_btn_filter_enable 啓用 hl_btn_filter_settings 設置 hl_btn_sort_asc 升序 hl_btn_sort_desc 降序 hl_btn_sort_name 名稱 hl_btn_sort_type 類型 hl_btn_sort_waiting 等候 hlf_btn_alle 全選 hlf_btn_invers 反選 hlf_btn_keine 取消選擇 Networks 交通網絡 Road toll 路費 Scenario 場景信息 #_________________________________climates_text_________________________________ #_________________________________climates_text_________________________________ arctic 極地氣候 desert 沙漠氣候 mediterran 地中海氣候 rocky 高山氣候 temperate 溫帶氣候 tropic 熱帶氣候 tundra 苔原氣候 #__________________________________error_text___________________________________ #__________________________________error_text___________________________________ Loading scenario script failed 載入場景腳本失敗 Cannot built depot here! 無法在這裡建造車庫 Cannot built this station/building\nin underground mode here. 該建築不能建於地底 Cannot create generic line!\nSelect line type by\nusing filter tabs. 不能創建「全部」類型的路線\n請選擇合適的類型,再創建路線 Cannot create socket 未能建立通訊 Convoi handles exhausted! 車輛總數已達到最大 Convoy already deleted! 車輛已移除! Das Feld gehoert\neinem anderen Spieler\n 該陸地屬於其他玩家 Der Besitzer erlaubt das Entfernen nicht 建築物擁有者拒絕移除建築\n汽車或行人在路上,無法移除 Diese Zusammenstellung kann nicht fahren!\n 非法組合 Flugzeughalt muss auf\nRunway liegen!\n 飛機必須放置在滑行道上 Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n 無法在這裡建造飛機場 Hier kann kein\nSignal aufge-\nstellt werden!\n 不能在這裡放置道路標誌 In order to lock the game, you have to protect the public player by password! 想要鎖定遊戲,必須設置密碼以防其他玩家繼續遊戲 Lost connection\nto server! 與服務器失去連接 Lost synchronisation\nwith server. 與服務器失去同步 Maglevhalt muss auf\nMaglevschiene liegen!\n 磁懸浮車站必須建造在磁懸浮軌道上 Monorailhalt muss auf\nMonorail liegen!\n 單軌車站必須建造在單軌軌道上 Monorails are not available yet! 單軌鐵路還不可用 Narrowgaugehalt muss auf\nNarrowgauge liegen!\n 窄軌車站必須建造在窄軌軌道上 No through station here! 車站必須建造在路的盡頭或道路直綫段上 Not enough money! 資金不足 On narrowgauge track only!\n 只適用於窄軌軌道!\n Only public player can lock games! 只有公共服務可以鎖上遊戲 Post muss neben\nHaltestelle\nliegen!\n 車站擴展建築必須建設在車站或碼頭附近 Protocoll error (expecting game) 傳輸協定錯誤(等侍遊戲) Schiffhalt muss im\nWasser liegen!\n 輪船停靠點只能建造在靠近碼頭的水面上 Server busy 伺服器繁忙! Terraforming not possible\nhere in underground view 無法在地底修整地形 Upgrade must have\na higher level 需要更高級別才能升級 Zughalt muss auf\nSchiene liegen!\n 火車車站必須建造在鐵路軌道上 #___________________________________help_text___________________________________ #___________________________________help_text___________________________________

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s 幫助索引\n關於模擬交通(simutrans)\n\n%1$s\n如何使用\n\n%2$s\n如何開始遊戲\n\n%4$s\n如何操作遊戲\n\n%5$s\n\n工具\n%3$s\n其他窗口\n\n%6$s Keyboard Help\n

Keyboard Help

\n 鍵盤幫助\n

鍵盤幫助

\n

\n鍵盤幫助 顯示快捷鍵功能\n

\n

\n鍵盤幫助 當某未定義快捷鍵按下時或從一般幫助打開\n

\n

\n快捷鍵區分大小寫 (使用[Shift]轉換大小寫)\n

\n

\n被引用的鍵包括:\n

\n

\n[方向鍵]: 遊戲地圖按其方向移動
\n[Backspace]: 關閉遊戲界面中所有的窗口、工具欄和幫助文檔
\n[Delete]或[Escape]: 關閉遊戲界面中最上層的窗口、工具欄或幫助文檔
\n[Enter]或[Return]: 確認操作
\n[Page-Up]或[>]: 放大遊戲地圖
\n[Page-Down]或[<]: 縮小遊戲地圖
\n[F1]: 打開Simutrans幫助

\n

\n[1]: 朝南移動遊戲地圖
\n[2]: 朝東南方向移動遊戲地圖
\n[3]: 朝東移動遊戲地圖
\n[4]: 朝西南方向移動遊戲地圖
\n[6]: 朝東北方向移動遊戲地圖
\n[7]: 朝西移動遊戲地圖
\n[8]: 朝西北方向移動遊戲地圖
\n[9]: 朝北移動遊戲地圖\n

\n

\n[Shift] + 鼠標: 在地圖中打開工業產業鏈
\n[CTRL] + 工具: 建設慢速公路、鐵軌或更直的公路、鐵軌
\n

#____________________________________ki_text____________________________________ #____________________________________ki_text____________________________________ %s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n %s 現在在\n%s 和景點 %s(%i,%i)\n之間提供巴士服務 %s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n %s 現在在\n%s 和工廠 %s(%i,%i)\n之間提供巴士服務 %s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). %s 現在運營 %i 貨車往返于\n%s(%i,%i) 和 %s (%i,%i) 之間 %s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). %s 在\n%s(%i,%i) 和 %s(%i,%i)\n之間開設了一條新鐵路綫 Airline service by\n%s\nnow between\n%s \nand %s.\n %s 現在在\n%s 和 %s\n之間提供航空服務 Ferry service by\n%s\nnow between\n%s \nand %s.\n %s 現在在\n%s 和 %s\n之間提供船運服務 Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n %s 現在在\n%s 和 %s\n之間開設了一條新巴士綫 #___________________________________menu_text___________________________________ #___________________________________menu_text___________________________________ EDITTOOLS 地圖編輯工具 LISTTOOLS 列表管理 MAGLEVTOOLS 磁懸浮鐵路工具 MONORAILTOOLS 單軌/磁懸浮鐵路工具 NARROWGAUGETOOLS 窄軌鐵路工具 RAILTOOLS 鐵路工具 ROADTOOLS 道路工具 SHIPTOOLS 船運工具 SLOPETOOLS 地形工具 SPECIALTOOLS 其他工具 TRAMTOOLS 電車/輕軌工具 #_________________________________message_text__________________________________ #_________________________________message_text__________________________________ %s s\nheadquarter now\nat (%i,%i). %s 興建了一個新總部 Factory chain extended\nfor %s near\n%s built with\n%i factories. 經濟逐漸繁榮\n%s 擴大產業, 於 %s 附近建設了 %i 間工廠 New %s now available:\n%s\n 新 %s 現在可用:\n%s New factory chain\nfor %s near\n%s built with\n%i factories. 工業發展: %s 新工業鏈\n於 %s 附近建立了 %i 工廠 New vehicle now available:\n%s\n 新交通工具現在可用\n\n -- %s -- Now %u clients connected. 現在有 %u 個用戶已連接 Remove vehicle from map. Use with care! 移除該運輸工具(請小心使用!) Screenshot\ngespeichert.\n 遊戲截圖已保存 Sends the convoi to the last depot it departed from! 將運輸隊調回最後的發車庫 With a big festival\n%s built\na new monument.\n%i citicens rejoiced. %s 為某盛大的節日建立了紀念館\n%i 個市民感到十分高興 #_________________________________program_text__________________________________ #_________________________________program_text__________________________________ (%i)- (%i) (in depot) (在車廠) \nBauzeit bis 直到 \nBauzeit von 出現於 \nCan't open heightfield file.\n 無法使用高度圖 \ndirection: 方向 \nelektrified 電氣化 \nHeightfield has wrong image type.\n 高度圖圖像格式不正確 \nis reserved by: 已被火車佔用 \nminimum speed: 最低時速: \nnot elektrified 未電氣化 \nRibi (masked) 通行方向(右): \nRibi (unmasked) 通行方向(左): \nSet phases: 調節南北/東西的綠燈持續秒數: \nsingle way 單向道路 \nway1 reserved by 道路1被佔用: \nway2 reserved by 道路2被佔用: \nwith sign/signal\n 標誌/信號 %d buildings\n %d 建築 %d convois %d 運輸隊 %d Einzelfahrzeuge im Depot %d 車輛存放在這 %i km/h (max. %ikm/h) %ikm/h(最高 %ikm/h) %i years %i months old. 樹齡為 %i 年 %i 個月 %s at (%i,%i) now public stop. %s 已成為公共運輸交匯處 %s building %s %s %s %s %s %s city %d %s %s %d %s %s factory %s %s %s %s %s has entered a depot. %s 已回到車庫 %s land %d %s %s %d %s 外 %s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. %s 的居民達到 %i\n因此興建了一個新市政廳 %s\nis crowded. %s 過度擁擠 %s\nspeed %i\nmax_speed %i\ndx:%i dy:%i %s\n時速 %i\n最高時速 %i\ndx:%i dy:%i %s\nwas liquidated. %s 已清盤 %u Client(s)\n %u 玩家\n %u Player (%u locked)\n %u 個玩家 (%u 個已鎖定)\n <未指定路線> 1 convoi 1 運輸隊 1 Einzelfahrzeug im Depot 1輛車存放在這 1LIGHT_CHOOSE 亮度: 1WORLD_CHOOSE 新遊戲的設置: 2LIGHT_CHOOSE 顔色: 2WORLD_CHOOSE 地圖編號: 3LIGHT_CHOOSE 鼠標卷動速度: 4LIGHT_CHOOSE 鼠標逆向卷動 5LIGHT_CHOOSE 車站的行人 5WORLD_CHOOSE 城市總數: 6LIGHT_CHOOSE 鎮上的行人 6WORLD_CHOOSE 交通密度: 8WORLD_CHOOSE 日夜模式 A bridge must start on a way! 這橋的兩端只能建于自己擁有的平地上且需在一條綫上 Abfrage 視察工具 Abnehmer 消費者 About 關於 Abriss 摧毀/移除 Absenken 降低地形 Abspanntransformator 變壓站 Accelerate time 加快遊戲速度 Act. load: %u MW\n 實際負荷: %u MW Active player only 僅限活躍玩家 Add forest 擴大森林 Add random citycar 隨機新增私家車輛 add server 新增服務器 Add Stop 新建車站 Add stops for backward travel 為回程路段增設車站 aircraft_tab 貨機 airplane 飛機 Airport 機場 AIRTOOLS 航空工具 All 全部 all convoi tooltips 顯示所有交通工具的提示 Allow city growth 允許城市增長 Allow player change 允許切換玩家 allowed climates:\n 允許的氣候 Alters a schedule. 調整路線資料 Angenommene Waren 鄰近工廠可輸送的物資 anhaengen 添加 Anhaenger_tab 貨車拖架 Anheben 提高地形 Appends stops at the end of the schedule 新增車站於路線的末端 Apply Line 應用路線 April 4月 Arbeiter aus: 工人居住在: Arrived 到達 Assets 資產 Aufloesen 解散 Aufspanntransformator 變壓站 August 8月 Autohalt muss auf\nStrasse liegen!\n 巴士、車輛停靠站必須建在道路上 Available 可用 Bahndepot 鐵路車廠 Bankrott:\n\nDu bist bankrott.\n 破產:\n\n你已經破產, 遊戲結束 battery 電池 Baum 樹木 baum builder 栽種樹木 Baustelle 建築中 Bauzeit 建築時間: Beenden 結束遊戲 Beginner mode 新手模式 Besonderes Gebaeude 旅遊景點 BF 站 bio 動物 Blockstrecke ist\nbelegt\n \n其他列車正使用此段鐵路。 Boden 陸地 bridge is too high for its type! 無法在這裏建造\n因為離地面的高度超出了其橋腳的高度限制 Bridge is too long for this type!\n 橋梁跨度太大\n請選擇其它橋梁或分段建造 Bruecke 橋樑 Bruecke muss an\neinfachem\nHang beginnen!\n 這橋必須建在斜坡上 Brueckenboden 橋梁 Build air depot 興建飛機庫 build choosesignals 興建月台選擇信號 Build city market 在城市附近興建新市場 Build drain 興建變壓站 build HQ 建立總部 Build land consumer 興建發電站 Build maglev depot 興建磁浮列車廠 Build monorail depot 興建單軌列車廠 Build narrowgauge depot 興建窄軌列車廠 Build powerline 興建電纜 Build presignals 架設信號 Build road depot 與建道路車廠 Build ship depot 與建船廠 Build signals 架設信號 Build train depot 與建鐵路車廠 Build tram depot 與建電車車廠 Build truck depot 與建道路車廠 Building costs estimates 預計建築成本 Buildings 建築物數量 Built artifical slopes 興建人造斜坡 Built random attraction 隨機興建景點 Bus_tab 巴士 Can only move from halt to halt or waypoint to waypoint. 只能從一個車站轉移到另一個車站,或從一個途經點轉移到另一個途經點。 Cancel 取消 Capacity: %.0f MW 負載量: %.0f MW\n Capacity: %d%s %s\n 運載量: %3d%s %s Capacity: %s\nLoad: %d (%d%%) 容量: %s\n 負載: %d (%d%%) Cars are not available yet! 仍未可製造該車輛。 cars.\nstate 車輛\n Cash 現金 Change player 切換玩家 Chart 運送圖表 Chat_msg 聊天 Choose direction 選擇方向 Choose operation executed on clicking stored/new vehicles 請點擊在庫或新購入的運輸工具,並選擇執行指定操作。 chooses a random map 選擇隨機地圖 citicens 居住量 City attraction 市內景點 City industries 城市工業 City list 城市列表 City size 城市大小 city_road 城市道路 citybuilding builder 城市建築物工具 CityLimit 城市限制 cl_title 車輛列表 cl_txt_sort 排序: Clear block reservation 顯示/重置佔用路段 clf_chk_aircrafts 飛機 clf_chk_cars 巴士/卡車 clf_chk_indepot 在車廠內 clf_chk_maglev 磁懸浮列車 clf_chk_monorail 單軌鐵路 clf_chk_name_filter 篩選器名稱: clf_chk_narrowgauge 窄軌鐵路 clf_chk_noincome 無收入 clf_chk_noline 無指定路線 clf_chk_noroute 無適當路線 clf_chk_noschedule 無行程表 clf_chk_ships 船 clf_chk_spezial_filter 特殊篩選器: clf_chk_stucked 困住 clf_chk_trains 列車 clf_chk_trams 電車 clf_chk_type_filter 類別: clf_chk_waren 物資: clf_title 車輛/船隻清單分類 Climate Control 氣候/地形控制 closed 欄截 COLOR_CHOOSE\n 請選擇閣下的色系。\n Company bankrupt 您的公司已經破產! Company_msg 競爭對手 Comparing pak files ... 正在比對遊戲資料檔案... Congratulation\nScenario was complete in\n%i months %i years. 恭喜!\n歷經 %i 月 %i 年, 此遊戲場景已完成! Connected stops 可供使用的站: Connected with server 已連接到伺服器 Constructed by 繪畫者: Constructed by %s 繪畫者:%s Construction_Btn 建設開支 convoi %d of %d 運輸工具 %d / %d convoi error tooltips 顯示交通工具的錯誤提示 Convoi has been sent\nto the nearest depot\nof appropriate type.\n 該運輸工具已召回\n至鄰近合適廠房。 Convoi is sold when all wagons are empty. 所有拖架空載時,運輸工具將會售出。 convoi mouseover tooltips 滑鼠指標下的交通工具顯示提示 convoi passed last\nmonth %i\n \n上月流量: %i\n Convois 交通工具 Convois: %d\nProfit: %s 車輛:%d\n利潤:%s Convoys 運輸工具 Copy Convoi 複製編成 Copy the selected convoi and its schedule or line 重複購買所選運輸工具並複製路線資料。 cost for removal 清除費用 Costs 成本 Create a new line based on this schedule 開設基於此路線資料的新路線。 curiosity builder 景點建設工具 curlist_title 景點列表 CURRENCY ¥ Currently playing: 進行中: Customers live in: 乘客居住於: deactivated in online mode 不適用於連線遊戲 Deccelerate time 減慢遊戲速度 December 12月 decrease underground view level 將切面推落一層 Del Stop 刪除 Delete Line 刪除路線 Delete the current stop 清除此站 Delete this file. 刪除此檔案 Demand: %.0f MW 需求: %.0f MW\n Denkmal 古蹟 Departed 已出發 Depots 車廠 Der Tunnel ist nicht frei!\n 隧道内仍有車輛在行駛 Destination 到達 Destroying map ... 正在刪除地圖... Details 詳情 Die Bruecke ist nicht frei!\n 橋上存在其他玩家的所有物 diesel 柴油 Direkt erreichbare Haltestellen 從這裡開始的直達路綫 disable midi 關閉背景音樂 Display settings 顯示設置 Distance 行走距離 Dock 碼頭 Dock must be built on single slope! 碼頭只能建築在沿海單面斜坡上 Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen 僅餘 %d 個月還清債務 Durchsatz 最大 Economy 經濟及城市 Eigenbesitz\n 公共財產 Ein %s\npasst hier nicht.\n 空間不足, 無法建造 %s Einstellungen aendern 變更選項 electric 電 Electricity 電力 Electricity producer\n\n 電力供應商 Electrics_tab 電氣化 Electrify track 電氣化路軌 enlarge map 擴大地圖 enter a value between %i and %i 請輸入介乎於%i與%i之間的數值 Enter address 請輸入地址: Enter Password 更改玩家名稱及密碼 Error 錯誤 Erzeuge neue Karte.\n 新地圖創建中, 請稍候\n(大地圖耗時會稍久一些) Es wird bereits\nein Fahrplan\neingegeben\n 正在安排車輛計劃表\n請在重新安排前先完成設定 Fabrikanschluss 相關廠商 Fabrikname 工廠名稱 Factories 工廠 factory details 工廠詳情 factorybuilder 工廠工具 Fahrplan 車輛計劃表 Fahrtziel 目的地: Fahrzeuge koennen so nicht entfernt werden 非法移除運輸工具 Fahrzeuge: 車卡: Farbe 玩家顔色 Fast forward 快速進行 February 2月 Ferry_tab 渡輪 Fertig 已完成 Filename 檔案名 Filter: 篩選: Finances of %s %s 的資產 Finanzen 資產 find mismatch 比對遊戲資料檔 fl_title 工廠列表 Flug_tab 客機 follow me 跟隨 Follow the convoi on the map. 在地圖上跟隨 Forest 森林 Found new city 開發新城市 Fracht 貨物 Frame time: 幀時 Free Capacity 空載量 freeplay mode 自由模式 Friction: 當前摩擦係數 fuel_cell 燃料箱 Full load 最小負載 Fundament 地基 Fussgaenger 行人 Game info 遊戲資料 GAME PAUSED 遊戲已暫停 Game_msg 一般 Gear: 齒輪 Gebaeude 建築 General 一般 Generation: %.0f MW 發電量: %.0f MW\n Gewicht 重量 Gewinn 收入 Give the selected vehicle(s) an individual schedule 給所選的車輛制定獨立的車輛計劃表 gl_btn_sort_catg 類別 gl_title 商品列表 go home 回車庫 Goods 貨物 Goods AI 電腦玩家-貨運 Goods list 貨物清單 Gross Profit 資金流動 Groundobj 目标 Grow city 發展城市 Growth 城市發展 H 車站 Hangar 飛機庫 Happy 高興 Haus kaufen 購買房屋 Helligk. 顯示 Help 幫助 Help text not found 幫助文檔不存在 hide all building 隱藏所有建築 hide city building 隱藏城市建築 hide station names 隱藏車站名稱 hide transparent 透明化隱藏建築 hide trees 隱藏樹木 Hier warten/lagern: 待運乘客/貨物 Higher transport fees, crossconnect all factories 提高運輸費用, 關閉及時制度 hl_title 車站列表 hl_txt_filter 分類: hl_txt_sort 排序方法 hlf_chk_airport 機場 hlf_chk_anleger 碼頭 hlf_chk_bahnhof 鐵路車站 hlf_chk_bushalt 巴士站 hlf_chk_frachthof 上落客貨區 hlf_chk_keine_verb 未連接 hlf_chk_maglevstop 磁浮列車站 hlf_chk_monorailstop 單軌鐵路車站 hlf_chk_name_filter 名稱: hlf_chk_narrowgaugestop 窄軌鐵路車站 hlf_chk_overflow 容量超荷 hlf_chk_spezial_filter 特別: hlf_chk_tramstop 電車站 hlf_chk_type_filter 類別: hlf_chk_waren_abgabe 生產: hlf_chk_waren_annahme 物資 hlf_title 車站列表篩選 Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. 找不到車庫\n請手動設置車輛行程表令其回到車庫 Homeless 流浪者 hydrogene 氫氣 Idle: 空閒 ignore climates 忽略氣候限制 Increase Industry density 增加工業 increase underground view level 將切面推上一層 industrial building 工業建築 Init map ... 初始化地圖中... Input 原料 Ins Stop 插入 Insert stop before the current stop 在此車站前增加車站 Intercity road len: 城市道路長度 Intro. date: 開發日期 invalid 未定義 Invalid coordinate 指令無效 isometric map 等距視圖 January 1月 join game 連線遊戲 July 7月 Jump to 跳至 June 6月 Kann Spielstand\nnicht laden.\n 無法載入存儲的遊戲 Kann Spielstand\nnicht speichern.\n 無法打開目標文件 Kein Besitzer\n 無物主 keine 沒有 Keine Einzelfahrzeuge im Depot 無車輛停留在此 Keyboard_Help\n 快捷键帮助 koord 座標 Kreuzung 十字路口 labellist_title 玩家標記列表 Lade Relief 載入高度圖 Laden 載入 Land attraction 郊外景點 Land industries 工業鏈 LANG_CHOOSE\n 請選擇界面語言 LARGE_NUMBER_STRING 億 LARGE_NUMBER_VALUE 百万 Last Year 上年度 Leaving depot! 正離開車廠! leer 空的 Legend 圖例 Leistung 功率 Leistung: %d kW 功率: %d kW Leitung 電線 letzen Monat: diesen Monat: 上月 本月 Line 路線 Line Management 路線管理 Lineless convoys serving this stop 途經本站的車輛 Lines serving this stop 途經本站的路線 LKW_tab 貨車 Load game 載入遊戲 load height data from file 從文檔載入高度圖 Load scenario 載入場景 loaded 承載 loaded passenger/freight 排列乘客/貨運,按 Loading (%i->%i%%)! 載入中 (%i->%i%%) Loading addon paks ... 正在載入附加的遊戲資料檔... Loading map ... 載入地圖中... Loading paks ... 正在載入遊戲資料檔... Loading skins ... 正在載入樣式... Lock game 禁止切換玩家 (需確認) Lokomotive_tab 機車 m3 立方米 Maglev 磁浮鐵路 maglev vehicle 磁浮鐵路車輛 maglev_track 磁浮路軌 Maglevdepot 磁浮列車廠 Mail Demand %d\n 運郵需求 %d\n Mailbox 消息中心 Mailbox Options 消息中心選項 Maintenance 維修 make stop public (or join with public stop next) costs %i per tile and level 公有化車站(或與鄰接的公共車站合併)每格每等級的成本為$%i Manual (Human) 人類玩家 Manufactured: 製造商 Map roughness 地圖粗糙度 map zoom 縮放 March 3月 Margin (%%) 利潤 Marker 設定標記 max 最高 Max income: 最高收入 Max. speed: 最高速度: Maximum 254 stops\nin a schedule!\n 一個車輛計劃表最多能容納254個途徑點 maximum length of rivers 河流最長長度 Maximum tile height difference reached. 兩個地形格之間的高度差已達到最大 May 5月 Median Citizen per town 城市人口中位數 Meldung 消息 Menge 總數 MessageOptionsText \n新年度\n\n玩家消息\n\n城市消息\n\n沒有路線\n\n新目標\n\n聊天\n\n新車輛\n\n車站爆滿\n\n問題\n\n交通擁擠 min 最低 minimum length of rivers 河流最短長度 Modify the selected line 修改所選路線 Monate alt 個月 Monorail 單軌鐵路 monorail vehicle 單軌列車 monorail_track 單軌路軌 Monorailboden 高架道路 Monoraildepot 單軌列車廠 month wait time 最大等候時間(月) Months 月 Monument 紀念碑 Monuments 紀念碑 Mountain height 山勢高度: Movingobj 移動物體 Music playing disabled/not available 背景音樂意外中止播放。 Music volume: 背景音樂音量: mute sound 靜音 Name 城市名稱 Narrowgauge 窄軌 Narrowgauge are not available yet! 窄軌鐵路還不可用 narrowgauge vehicle 窄軌鐵路車輛 narrowgauge_track 窄軌鐵路 Narrowgaugedepot 窄軌鐵路車廠 Net ID: %p 電網編號: %p\n Net Wealth 總資產 Neue Karte 新地圖 Neue Welt 建立新地圖 new convoi 購入運輸工具 New Line 開設路線 New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. 已開設新路線!\n請在路線列表中選擇該路線,\n並修改路線資料。 New Vehicles 購車開支 no buildings hidden 顯示所有建築 no convois 無運輸工具 No goods are loaded onto this convoi. 運輸隊無載貨 no goods waiting 沒有貨物等候 no load 取消承載 No Route 無路線 No stop here! 此工具只能應用於車站 No suitable ground! 地點不合適! No terminal station here! 不能在此建設總站!請檢查建\n設地點是否在對應路軌或鐵路\n的末端。 no timeline 不使用時間軸 no tree 沒有樹林 Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n "我該去哪裏?"\n在派出車輛前,必須先給其制定行程表或者安排到已有的行程表 none 無 nord 北 nordost 東北 nordwest 西北 Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! 尋找路綫期間,無法修改車輛路線表 Not enough fields would remain. 此農場預留空間不足 November 11月 Now active as %s.\n 當前玩家為%s\n Number of rivers 河流數量 Object 物件 Odometer: %s km 里程計: %s 公里 Ok 確定 Oktober 10月 On loan since %i month(s) 負債狀況已經維持了%i個月 On this map, you are not\nallowed to change player!\n 遊戲已鎖定, 無法切換玩家\n Only city chains 只顯示城市的工業鏈 Only first %d differing paks reported. There are probably more. 只顯示首%d個不相同的遊戲資料檔。可能會有更多。 Only land chains 只顯示郊外的工業鏈 Only one transformer per factory! 一間工廠只能設置一個變壓器 open 通行 Operation 運營費用 Ops Profit 運營利潤 Optionen 選項 Origin 出發地 ost 東 Output 輸出 Pak(s) different: 不相同的遊戲資料檔: Pak(s) missing on client: 用戶端所欠缺的遊戲資料檔: Pak(s) not on server: 伺服器上所沒有的遊戲資料檔: Pakset differences 遊戲資料集之間的差別 paletten 貨箱 Pas_tab 客運火車 Passagiere 乘客 Passagierrate 乘客量 Passagierziele 乘客/郵件目的地 Passenger AI 電腦玩家-客運 Passengers %d %c, %d %c, %d no route 乘客 %d %c, %d %c, %d 無路綫 Passengers %d %s, %d %s, %d no route 乘客 %d %s, %d %s, %d 無路綫 Password 密碼 Pause 暫停 PaxDest 目的地 Percent Electricity 供電率(%%需求) Planes are not available yet! 飛機還不可用 Plant tree 種植樹木 player 玩家 player -1 人 player 0 公共服務 player 1 Napik 128 AS player 10 玩家10 player 11 玩家11 player 12 玩家12 player 13 玩家13 player 2 Trikky Transport player 3 Meyer Moving Co. player 4 貨運代理人 VM player 5 H-Trans公司 player 6 PSK & Co KG player 7 玩家7 player 8 玩家8 player 9 玩家9 Please choose vehicles first\n 請先選擇車輛 Post 郵件 Postrate 郵件量 Power 電力 Power: 功率 Powerlines 電纜 Problems_msg 問題 Production of %s has been stopped:\n%s\n %s 已停產\n%s Produktion 生產 Profit 利潤 promote to line 改進路綫 q1 春季 q2 夏季 q3 秋季 q4 冬季 rail car 火車 random 隨機 Random age 隨機樹齡 Random map 隨機地圖 Rathaus 市政府 Rating 評價 ratio_pax 乘客運載率(%%) Reliefkarte 地圖 Remove 移除標簽 remove airstrips 拆除飛機跑道和滑行道 remove channels 移除運河 remove interm. signals 拆除中間的信號 remove maglev tracks 拆除磁浮路軌 remove monorails 拆除單軌鐵路 remove narrowgauge tracks 拆除窄軌鐵路 remove powerlines 拆除電纜 remove roads 拆除道路 remove tracks 拆除路軌 Remove wayobj %s 拆除道路 %s replace other signals 置換其他標誌 replace stop 更換車站 request closing 請求拒絕 residential house 居民房 Restore natural slope 還原原來的斜坡 Restwert: 轉售價格 Retire. date: 退休日期 return ticket 複製此回程 Revenue 收入 Revision: 修正 road 道路 road vehicle 道路車輛 Roadsign 路牌 Rotate map 旋轉地圖 Rotation 旋轉 Routing 尋找路線中 sack 袋 sail 風 Saving map ... 地圖存儲中... Scenario complete: %i%% 場景已完成: %i%% Schedule changing! 正在更新路線表! Schienentunnel 建造鐵路隧道 Schiff_tab 船 Schiffdepot 造船廠 Schleppkahn_tab 駁船 Screenshot 截圖 Seasons 季節 Sehenswuerdigkeit 旅遊勝地 Sell the selected vehicle(s) 賣出所選車輛 sended 郵件已發送 SEP_FRACTION . SEP_THOUSAND , SEP_THOUSAND_EXPONENT 3 September 9月 Server did not respond! 服務器無回應 Serves Line: 服務路線: Service 服務 set signal spacing 設定信號間距 Setting 設置 Ship 船 shops and stores 商店和辦公樓 Show all 全部顯示 show all building 顯示所有建築 Show also vehicles no longer in production. 同時顯示不再生產的車輛 Show also vehicles that do not match for current action. 同時顯示不符合所選指令的車輛 Show even servers with wrong version or pakset 同時顯示使用錯誤遊戲版本或資料集的服務器 show grid 顯示網格 Show industry 顯示工業 Show legend 顯示圖例 Show map scale 顯示地圖比例 Show obsolete 顯示已淘汰的 Show schedules 顯示路綫表 show station coverage 顯示車站服務範圍 show station names 顯示車站名稱 show waiting bars 顯示等候圖表 Show/hide statistics 顯示/隱藏數據統計 Shows consumer/suppliers for factories 顯示工廠的客戶和供應商 Shows the currently selected schedule 顯示當前所選的路綫表 Shrink city 縮小城市 shuffle midis 隨機音樂 Signal 信號 signal spacing 信號間距 Sim: 模擬週期 Size (%d MB): 大小 (%d MB): sliced underground mode 切面地圖模式 slot empty 尚未使用 Sort by 排列按 Sort waiting list by 排列等候列表,按 Sound 音效 Sound settings 音效設置 Sound volume: 音效音量 special freight 特殊貨物 Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. 速度加成\n道路 %i km/h 鐵路 %i km/h\n船運 %i km/h 空運 %i km/h Speedlimit 速度限制 Speichern 保存 Spieler 玩家 Spieler(mz) 玩家 Spielerliste 玩家列表 Spielstand wurde\ngeladen!\n 遊戲載入成功 Spielstand wurde\ngespeichert!\n 遊戲存儲成功 Sprache 語言 Sprachen 語言 Stadtinformation 城市統計數據 Start 開始 Start the selected vehicle(s) 派遣所選車輛 Starte Spiel 開始遊戲 Station tiles: 車站長度: Station_msg 車站 Status 車站狀態 steam 蒸汽 Step timeline one year 跳至下一年 Stops 車站 Storage capacity 容量 Strassendepot 車庫 Strassentunnel 建造行車隧道 street car 電車 sued 南 suedost 東南 suedwest 西南 Summer snowline 夏季雪綫 Suppliers 供應商 Tage alt 天 There are still vehicles\nstored in this depot!\n 車廠内還有車輛!\n This Month 本月 This Year 本年度 Tile not empty. 在使用斜坡工具前, 請先清除地面物件 timeline 遵從時代 tl_title 城市列表 To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. %s 爲了吸引更多的旅客\n興建了 %s\n(在 %i 納稅人的支持下) To heavy traffic\nresults in traffic jam.\n 交通過於繁忙導致交通阻塞 Toggle day/night view 切換日/夜視圖 Toggle vehicle tooltips 切換車輛工具提示 tonnen 噸 Total inhabitants: 居民總數 Tourist attractions 旅遊景點 Tourists 景點 Town_msg 新目的地 Town: %s\n %s 市 Towns 城市 track 路轨 Tracks 路軌 Traffic 交通 Train 火車 Trains are not available yet! 火車還不可用 Tram 電車 tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. 電車 %i km/h, 單軌列車 %i km/h\n磁懸浮列車 %i km/h, 窄軌列車 %i km/h tram_track 電車路軌 Tramdepot 電車車庫 Trams are not available yet! 電車還未可使用 Transferring game ... 正在傳送遊戲... Transformer only next to factory! 變壓站必須建造在與工廠毗連的平坦空地上 Translation 翻譯 transparent station coverage 透明顯示車站覆蓋範圍 Transported 路程 TrolleyBus_tab 無軌電車 Truck 貨車 Tunnel muss an\neinfachem\nHang beginnen!\n 隧道入口必須建造在斜坡上 Tunnel must start on single way! 隧道入口必須連接道路或鐵路 Tunnelboden 隧道 underground mode 地底模式 UNDO failed! 無法撤銷上次操作\n僅當某路段上沒有信號標誌、車站或其他建築物時, 才能使用撤銷道路建設操作 Undo last ways construction 取消上次道路建設 Unemployed 失業 Unhappy 不高興 units/day 件/月、  Update Line 更新路綫 upgrade HQ 升級總部 Usage: %.0f %% 使用率: %.0f %% Use beginner mode 正在使用新手模式 Use timeline start year 使用時間軸: 从 Vehicle %s can't find a route! 車輛 %s 無法找到路綫 Vehicle %s is stucked! 車輛 %s 被卡住了 Vehicle details 車輛詳情 Verbrauch 消耗量 Vergroessere die Karte\n 擴大地圖 verkaufen 販售模式 Verkehrsteilnehmer 城市車輛 Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n 僅餘 %d 個月還清債務 via 途經站(細節) via %s\n 途經 %s\n via Menge 途經站(數量) voranstellen 前置 Waggon_tab 車輛 waiting 等待中 Waiting for clearance! 等待清理中 Warnings_msg 交通狀況 Wasser 水 Water 運河 Water level 水平面 water vehicle 船隻 way %s cannot longer used:\n 無法再建造 %s 路 way %s cannot longer used:\n%s\n 無法再建造 %s 路 way %s now available:\n 現在可用%s 路了\n Ways not connected 道路未連接 Wegpunkt 途經點 Wert 價格 west 西 Winter snowline 冬季雪綫 withdraw 撤消 Withdraw All 全部撤消 WRONGSAVE 檔案可能已損壞, 無法載入遊戲 Year %i has started. %i 年己開始 Years 年 Zielort 目的地 zooming in 放大 zooming out 縮小 Zu nah am Kartenrand 過於靠近地圖邊緣, 無法建造 #__________________________________record_text__________________________________ #__________________________________record_text__________________________________ New world record for maglevs: %.1f km/h by %s. %2$s 创造了新磁悬浮列车世界紀錄: %1$.1f km/h New world record for monorails: %.1f km/h by %s. %2$s 创造了新單軌世界紀錄: %1$.1f km/h New world record for motorcars: %.1f km/h by %s. %2$s 创造了新車輛世界紀錄: %1$.1f km/h New world record for narrowgauges: %.1f km/h by %s. %2$s 创造了新窄軌世界紀錄: %1$.1f km/h New world record for planes: %.1f km/h by %s. %2$s 创造了新航空世界紀錄: %1$.1f km/h New world record for railways: %.1f km/h by %s. %2$s 创造了新鐵路世界紀錄: %1$.1f km/h New world record for ship: %.1f km/h by %s. %2$s 創造了船運新世界紀錄:\n%1$.1f km/h #_______________________________unnecessary_text________________________________ #_______________________________unnecessary_text________________________________ &0_CITY_SYLL 油繩 &1_CITY_SYLL 切斯特 &2_CITY_SYLL 噸 &3_CITY_SYLL 市 &4_CITY_SYLL 集聚地 &5_CITY_SYLL 行政區 &6_CITY_SYLL 火腿 &7_CITY_SYLL 域 &8_CITY_SYLL 口 &9_CITY_SYLL 鎖 &A_CITY_SYLL 穿過 &B_CITY_SYLL 由 &C_CITY_SYLL 埋藏 &D_CITY_SYLL 用處 &E_CITY_SYLL 井 %0_CITY_SYLL 松樹 %1_CITY_SYLL 艾坪 %2_CITY_SYLL 馬利 %3_CITY_SYLL 櫻 %4_CITY_SYLL 泉 %5_CITY_SYLL 大選 %6_CITY_SYLL 巴靈 %7_CITY_SYLL 赫普 %8_CITY_SYLL 布倫特 %9_CITY_SYLL 仁恩 %A_CITY_SYLL 舊 %B_CITY_SYLL 山丘 %C_CITY_SYLL 新 %D_CITY_SYLL 岩山 %E_CITY_SYLL 沃尔 %F_CITY_SYLL 橡樹 0center 不存在! 0extern 不存在! 0suburb 不存在 1center %s %s 1extern %s 分支 %s 1suburb %s %s %s 2center %s 中心 %s 2extern %s 外 %s 2suburb %s 草地 %s %s 3center %s 主 %s 3extern %s 土地 %s 3suburb %s 村莊 %s %s 4center %s 里 %s 4extern %s 外圍 %s 4suburb %s 農村 %s 5center %s 市中心 %s 5extern %s 转换 %s 5suburb %s 鎮 %s 6center %s中繼%s 6extern %s 交換 %s 6suburb %s 郊外 %s %s 7center %s 城市 %s 7extern %s 平原 %s 7suburb %s 公園 %s 8center %s 商业 %s 8extern %s %s 邊 %s 8suburb %s 衛星 %s 9center %s 軸 %s 9extern %s 旁道 %s 9suburb %s 中部 %s Acenter 不存在 Aextern 不存在 Asuburb 不存在 Bcenter 不存在 Bextern 不存在 Ccenter 不存在 Cextern 不存在 Csuburb 不存在! Dcenter 不存在 Dextern 不存在 Dsuburb 不存在 Ecenter 不存在 Eextern 不存在 Esuburb 不存在 Fcenter 不存在 Fextern 不存在 Fsuburb 不存在 Gcenter 不存在 Gextern 不存在 Gsuburb 不存在 Hcenter 不存在 Hextern 不存在 Hsuburb 不存在 Icenter 不存在 Iextern 不存在 Isuburb 不存在 Jcenter 不存在 Jextern 不存在 Jsuburb 不存在 Kcenter 不存在 Kextern 不存在 Ksuburb 不存在 Lcenter 不存在 Lextern 不存在 Lsuburb 不存在 Mcenter 不存在 Mextern 不存在 Msuburb 不存在 Ncenter 不存在 Nextern 不存在 Nsuburb 不存在 Ocenter 不存在 Oextern 不存在 Osuburb 不存在 Pcenter 不存在 Pextern 不存在 Psuburb 不存在 Qcenter 不存在 Qextern 不存在 Qsuburb 不存在 Rcenter 不存在 Rextern 不存在 Rsuburb 不存在 Scenter 不存在 Sextern 不存在 Ssuburb 不存在 Tcenter 不存在 Textern 不存在 Tsuburb 不存在 Ucenter 不存在 Uextern 不存在 Usuburb 不存在 Vcenter 不存在 Vextern 不存在 Vsuburb 不存在 Wcenter 不存在 Wextern 不存在 Wsuburb 不存在 Xcenter 不存在! Xextern 不存在! Xsuburb 不存在! Ycenter 不存在! Yextern 不存在! Ysuburb 不存在! Zcenter 不存在! Zextern 不存在! Zsuburb 不存在! simutrans-124.5/simutrans/text/zh/000077500000000000000000000000001520675460500172165ustar00rootroot00000000000000simutrans-124.5/simutrans/text/zh/baum_build.txt000066400000000000000000000015751520675460500220720ustar00rootroot00000000000000植樹

植樹

此問題可分為4各部分

·左上角是可供選擇的樹的類型的列表

·左下角是顯示被選擇的樹的圖片

·右上角是一些可以被設置的選項

·右下角是顯示被選擇的樹的相關信息

選擇列表

選擇列表提供了所有樹的類型,通過右上角的選項可以獲得。有兩個標籤可以被用來改變樹的類型是如何被區別的。

· 翻譯: 使用當前語言顯示樹的名稱,如果沒有被翻譯成當前語言,對象的名稱將被使用

· 對象: 樹的類型用模擬交通內部對象的名字

選項

· 忽視天氣: 此選項使得天氣對不同樹的類型的限制被解除

· 隨機年份: 將要被種植的樹的年齡是隨機的

simutrans-124.5/simutrans/text/zh/color.txt000066400000000000000000000013301520675460500210720ustar00rootroot00000000000000

玩家顏色

玩家顏色 設置公司的顏色

交通工具,建築物和其他項目顯示公司的顏色

玩家顏色 使用遊戲選項打開

點擊顏色條來修改公司的顏色

玩家顏色的名稱條的改變(新的公司的名稱條的顏色不會改變)意味著新的顏色被選出

默認的人類玩家的顏色是淡藍色

一些項目沒有為顏色編碼&這些項目不需要選擇顏色

(提示:使用玩家列表或者P+ Change Player改變電腦玩家的顏色之前先選擇電腦玩家)。

simutrans-124.5/simutrans/text/zh/enlarge_map.txt000066400000000000000000000004061520675460500222310ustar00rootroot00000000000000地圖擴展幫助

擴展地圖>

可以向右和向下擴展地圖. 如果想要向左或向上擴展地圖, 必須先將地圖旋轉, 在擴展, 最後再旋轉回去.
地圖擴展預覽在對話框窗口的右上顯示.

simutrans-124.5/simutrans/text/zh/labellist_filter.txt000066400000000000000000000003231520675460500232750ustar00rootroot00000000000000標記列表幫助

玩家標記列表

這裡列出了地圖上所有的標記.
僅顯示活躍玩家選項 只顯示活躍玩家設置的標記.

simutrans-124.5/simutrans/text/zh/save.txt000066400000000000000000000035061520675460500207210ustar00rootroot00000000000000<strong>存檔</strong>

存檔:把當前遊戲記錄到文件中(為了可以在以後繼續進行遊戲)& 這些文件可以被刪除。

存檔打開遊戲選項或者按[S]鍵; & 列出遊戲的名稱,日期&之前遊戲的時間。

如果所有的遊戲都看不見;使用右邊的滾動條將名字滾出。

當前遊戲可以被儲存打一個新的文件或者覆蓋一個已經存在的遊戲文件。

為了儲存遊戲到一個新的文件,請在存檔鍵上方的盒子裡填寫保存文件的名稱,輸入名稱&按回車鍵或者返回鍵或者點擊OK< /em>; 新的文件名稱被列出來。

注意:如果列出的文件名稱是使用過的:以前的文件內容被當前的遊戲文件內容替代,&以前的內容丟失。

默認當前遊戲出現在文件名稱盒子裡,點擊OK 保存這個文件(以前的數據被當前數據替代被丟失)

為了將以前的遊戲有當前的遊戲文件覆蓋,點擊文件列表中的文件名(以前的遊戲將要被當前的遊戲文件替代)

文件名可以是一個由字母,數字,符號組成的最大不超過57個字符。保存後(最多)只有20個字符可以被現實在保存文件&裝載文件中。

警告:通過文件名點擊X鍵遊戲將被立即有就刪除,文件名將被移除列表。使用此功能要小心。

{提示:從載入遊戲中重新開始一個已經被保存過的遊戲,

改變自動保存{自動遊戲保存}到simuconf.tab文件中}。

點擊取消鍵用來關閉存檔鍵;或者點擊左上角的X;或者使用鍵盤關閉。

simutrans-124.5/simutrans/text/zh/scenario.txt000066400000000000000000000003221520675460500215570ustar00rootroot00000000000000劇本幫助

劇本

Simutrans支持劇本模式. 在劇本中, 必須完成一定的目標: 連接某個工廠, 迅速變富, 或建造一個不朽的總部.

simutrans-124.5/simutrans/text/zh/settings.txt000066400000000000000000000010541520675460500216170ustar00rootroot00000000000000擴展設置幫助

擴展設置

擴展設置允許改變在遊戲引擎中每一個參數(幾乎), 如同修改中心配置文件simuconf.tab. 因此所有參數的命名與他們在simuconf.tab文件中相應參數的命名相同.

如果修改某個正在進行的遊戲, 你需要重新載入遊戲以便應用設置. 某些設置(比如工廠連接)只會影響新創建的項目.

注意:
不適儅的參數設置機易損壞遊戲!

simutrans-124.5/simutrans/text/zh/trafficlight_info.txt000066400000000000000000000003001520675460500234310ustar00rootroot00000000000000交通燈控制幫助

交通燈控制

控制紅綠燈的變換頻率. 其可選的數字是綠燈在北/南和東/西方向持續的秒數.

simutrans-124.5/simutrans/thanks.txt000066400000000000000000000134321520675460500176450ustar00rootroot00000000000000Credits ======= Many people helped me with various contributions to Simutrans. This file tries to list all contributors to Simutrans. Additional programming ---------------------- * Markus Pristovsek: from 2005 team leader markus@pristovsek.de * Andreas Rver: small patches andreas_roever@yahoo.com * Rainer Bertram: Small patches hellmade@hellmade.de * Niels Roest: Many additional UI routines Simutrans' proportional font npr@hetnet.nl * Pasi Ruhanen: Help with BeOS port duck@neutech.fi * Tillman Raatz: Bugfixes for vehicle movement code mrtlee@gmx.de * Tomas Kubes: Forest creation code, new darkening, Patches Coordination of 128x128 pixel tile set creation t_kubes@yahoo.com * Markus Weber: Additional UI coding preml@gmx.net * Owen Rudge: MIDI playing routines for Windows version Finance window Command line arguments for reading custom daten.pak files Many small patches owen@osplus.co.uk * Volker Meyer: Better bridges and tunnels Rewrites of many data structures Binary save game format Many big patches volker.meyer@thales-is.com * Hendrik Siegeln: Line management Charts/Statistics Bugfixes "Master of the scrollbars" hsiegeln@tibco.com * Kieron Green: Textures Multiple freight graphics * Christoph Mallon: C-backends, Makefile, hosting SVN Help with game design --------------------- * Peter J. Dobrovka dobrovka@t-online.de Additional artwork ------------------ * Georg "MHz" Moers: Lots and lots of highest quality images * Flor Wauters: trucks, trains cars, industries and many other images many animations Artist trademark (vehicles): WF Ultd. flor.wauters@pandora.be * Tomas Kubes: Many high quality images t_kubes@yahoo.com * Jens Emmel: ships, trucks Artist trademark (vehicles): JET jens.emmel@hansenet.de * Manfred Tomaschitz: train waggons Artist trademark (vehicles): VT Vienna manfredtomaschitz@netscape.net * Oskari Saarekas: locomotives, waggons, ships and buildings Artist trademark (vehicles): TPI lopomo@mbnet.fi * Peter J. Dobrovka: buildings, some trees dobrovka@t-online.de * Rick McGreal: town buildings and factories Artist trademark (vehicles): Trikky trikky@mynock.com * Sergiu Croitor: busses and trucks Artist trademark (vehicles): Serj sheff.cib@usa.net * Seth C. Triggs: town buildings and factories sethtriggs@yahoo.com * Dirk Burkholz: cars and ships Artist trademark (vehicles): DIBU dirk@burkholz.de * Tuomas Suikkanen: old bookshop image tsuippa@yahoo.com * Daniel Tpel: improved powerline images, transformer station image toepel@talknet.de * Marcal Guardiola: town buildings margs@retemail.es * Frans van Nispen: some very nicely rendered high-level buildings xlr8@tref.nl * Paul Szczepanek: many high quality buildings * Stefan "MIP" Wuttich: cobblestone road s.wuttich@web.de * Patrick Nesbitt: lots of images * James Starr: high quality factories * Georg "MHZ" Moers: buildings and vehicles * sanchimaru: big harbour crane * timeshock: many small graphics * erin: many busses for the german set *singaporkid: german steamers, different station graphics * Timothy Baldock: lots of improved buildings, maglevs Translation ----------- * George Dragon: Hungarian translation dragon@idg.hu * Marcal Guardiola: Spanish translation margs@retemail.es * Niels Roest: Dutch translation npr@hetnet.nl * Rmi Denis-Courmont: French translation rdenis@multimania.com * Pasi Ruhanen: Finnish translation duck@neutech.fi * Ville Koskinen: Finnish translation bm@linja.net * Peter Molnar: Hungarian translation * Antti Louko: Finnish translation alou@mbnet.fi * Bram Schoenmakers: Dutch translation bram@schnmkrs.myweb.nl * Alberto Beccaria: Italian translation a.beccaria@inwind.it * Ivo Strasil: Czech translation ivo.strasil@centrum.cz * Kamil "FastIkarus" Ondrk: Czech translation fastikarus@centrum.cz * Ondrej Machulda: Czech translation translation@simutrans.wz.cz * Piotr Pietrzak: Polish translation pyotr@wp.pl * Jaroslav Pitok: Slovak translation jaro.pitonak@seznam.cz * Jn Krnc: Slovak translation Krnac@UZIS.Sk * Julius Grantn: Swedish translation ge_klav@hotmail.com * Eric C. Olivera: Portuguese translation ecostaoli@yahoo.com.br * Finn Rosenbech Jensen: Danish translation finnrj@ofir.dk * Ferencz Szekely: Hungary and Romanian translation phritzy74@freemail.hu * Aleks Revo: Russian translation deep-dream@narod.ru * Danilo Schneberg: Russian BDF-font champcarracer@hotmail.com User manual, documentation and in-game texts -------------------------------------------- * Henrik Wolff: User manual extensions eMail@Henrik-Wolff.de * Seth C. Triggs: Maintaining the bug list 2000-2002 sethtriggs@yahoo.com * Dirk Burkholz: Help with german manual Webmaster of the german Simutrans forum dirk@burkholz.de * Adam Barclay: Many english in-game help texts Maintaining the bug list 2002-2003 echobeatz666@aol.com * Stefan Heinze: german in-game help texts masato@gmx.de * Joachim Baum: german tree descriptions * Isaak Eiland-Hall: hosting the Simutrans Wiki * Gotthardlok: Many german/english/french help texts Help with assembly coding -------------------------- * Frans van Nispen xlr8@tref.nl Additional thanks go to ----------------------- * Bernhard Mecl: Very first beta tester bernhard.mecl@gmx.de * Gerhard Hafner: Railway informations * Richard Tockey: Vehicle editor for Windows version darkfyre@phoenixdsl.com (The vehicle editor is no longer maintained as far as I know) * Veikka Kemppainen: Testing and suggestions and many other things vkem@kolumbus.fi Written by Hansjrg Malthaner, October 2000 added by Markus Pristovsek Last update: 6-Jul-2005 - markus@pristovsek.de simutrans-124.5/simutrans/themes/000077500000000000000000000000001520675460500170765ustar00rootroot00000000000000simutrans-124.5/simutrans/themes/aero.tab000066400000000000000000000131071520675460500205160ustar00rootroot00000000000000# # name of the theme (will be shown with file selector) name=Aero skin (standard size) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = #AFEEEE # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 # windows drop a little shadow (default off) # gui_drop_shadows = 1 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 #cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # Use the default height #gui_button_height = 22 # larger icons too icon_width = 32 #################################window stuff################################# # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like Windows) # window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #1E90FF # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window # window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) bottom_window_darkness = 20 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #C0C0C0 # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #A7D1EA #################################colour stuff################################# # color used for the default titlebars default_window_title_color = #338FCC # gui_color_text = #000000 gui_color_text_highlight = #005C9E gui_color_text_shadow = #BDD3DF gui_color_text_title = #338FCC gui_color_text_strong = #E46A0B gui_color_obsolete = #2878ff gui_color_empty = #868c86 gui_color_edit_text = #000000 gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #666666 gui_color_edit_background_selected = #72B2DD gui_color_edit_beam = #1E90FF gui_color_chart_background = #D6EBF7 gui_color_chart_lines_zero = #005C9E gui_color_chart_lines_odd = #FFFFFF gui_color_chart_lines_even = #336699 gui_color_list_text_selected_focus = #FAFAFA gui_color_list_text_selected_nofocus = #FAFAFA gui_color_list_background_selected_focus = #72B2DD gui_color_list_background_selected_nofocus = #72B2DD gui_color_button_text = #000000 gui_color_button_text_disabled = #666666 gui_color_button_text_selected = #FAFAFA gui_color_colored_button_text = #FAFAFA gui_color_colored_button_text_selected = #FAFAFA # gui_color_checkbox_text = #000000 gui_color_checkbox_text_disabled = #666666 gui_color_ticker_background = #D6ECF7 gui_color_ticker_divider = #6095B8 # gui_color_statusbar_text = #000000 gui_color_statusbar_background = #72B2DD gui_color_statusbar_divider = #6095B8 gui_highlight_color = #FAFAFA gui_shadow_color = #CCDDFF gui_color_text_minus = #C00000 gui_color_text_plus = #000000 gui_color_text_unused = #E46A0B # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #ff0000 # Colo loadbar, save game, load game, server... gui_color_loadingbar_progress = #6495ED gui_color_loadingbar_inner = #D6ECF7 # Color player brigth gui_player_color_dark = 1 gui_player_color_bright = 3 ##################################size stuff################################## # force main menu icon size icon_width = 32 # titlebar height gui_titlebar_height = 22 # safe margin around windows # gui_frame_left = 10 # gui_frame_top = 10 # gui_frame_right = 10 # gui_frame_bottom = 10 # horizontal and vertical margins between objects # gui_hspace = 4 # gui_vspace = 4 # tab minimum sizes gui_tab_header_vsize = 18 # input boxes height # minimum height is the height of the position button graphic # gui_edit_height = 22 # checkbox height, default sizes come from the checkbox graphic # gui_checkbox_width = 10 # gui_checkbox_height = 10 # sum of the top and bottom margins for divider lines # default is twice the size of gui_vspace # gui_divider_vsize = 8 # buttons sizes gui_button_height = 16 # for buttons with fixed widths gui_button_width = 100 # text margins inside the buttons (left, top, right) gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 icon_scaling = 100 ###################################pakstuff################################### # pak with the images for this theme themeimages=aerotheme.pak simutrans-124.5/simutrans/themes/aerotheme.pak000066400000000000000000001700161520675460500215510ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1$$IMG^RRJVZkRJ^wwVZswwsN9gswwsNVZkswsIMG.@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkIMGVRcsNwsNww9gRJwssZR{o{o{ocsNIMGlsNZcZksNZcZksNZcZksNZcZksNZcZksNZcZksNZcZkIMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMGlZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNIMG=^{o{o{o1FcZkZk{o{oVsN{osswRJ9gww{ZsNsR1F9gZ1FsNZIMG:@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGv{oZkZkcR{o{oZkZkRsssZk1FwwsRR{^RJ{VRJ1FsNcIMG`B919J)1RZ5J)RJRV1F)%9sNRR1)%9sNsNRJIMG.@@)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGX5RJ1Z1FJ)VsN9J)RsN1F)%J)RsN1Fk-!1FIMGl)%5B1F)%5B1F)%5B1F)%5B1F)%5B1F)%5B1F)%5B1FIMG@@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGl1FB5)%1FB5)%1FB5)%1FB5)%1FB5)%1FB5)%1FB5)%IMGtk-5=1FRJRJk-9BRJRJsN=51FsNVV5=RVZ11F^c5=RRJ=IMG:@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@================================================================IMG|RJB==5J)sNRJ1F=1k-ZRsN=k-R^VRJBcR99^9B=V^IMG^ssw{swsw{s{{{{ss{{{{IMG.@@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@@@@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{IMGVsws{s{{s{{{ws{{{wsIMGl{{{{{{{{{{{{{{{{{{{{{{{{{{{{IMG@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{IMGl{{{{{{{{{{{{{{{{{{{{{{{{{{{{IMGZkww{{{9gw{{{{ss{{{{{ws{s{o{w{oswIMG:@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@@@@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMGv{{{ws{{{{s{{{{{o{ssw{os{o{oswIMG^RRJVZkRJ^wwVZswwsN9gswwsNVZkswsIMG.@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkIMGVRcsNwsNww9gRJwssZR{o{o{ocsNIMGlsNZcZksNZcZksNZcZksNZcZksNZcZksNZcZksNZcZkIMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMGlZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNIMG=^{o{o{o1FcZkZk{o{oVsN{osswRJ9gww{ZsNsR1F9gZ1FsNZIMG:@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGv{oZkZkcR{o{oZkZkRsssZk1FwwsRR{^RJ{VRJ1FsNcMENUTEXT EditfieldTEXTIMG1  IMG^RRJVZkRJ^wwVZsww9gswwVZkswsIMG.@@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssIMGVRcsNwsNww9gRJwssZR{o{o{ocsNIMGv=^{o{o{o5cZkZk{o{o=^{o{o{o5cZkZk{o{o=^{o{o{o5cZkZk{o{oIMG.@@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssIMGj{oZkZkcR{o{oZkZkR{oZkZkcR{o{oZkZkR{oZkZkcR{o{oZkZkRIMG^VsN{osswRJ9gww{ZsNsR1F9gZ1FZIMG.@@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGVsssZk1F{wsRR{^RJ{VRJ1FsNcMENUTEXTListboxTEXTIMG1  IMGJIMGJJIMG@@JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIMGJJJJJJJJJJJJJJJJJJJJIMGkkkkkkkkkkkkkkkkkkkkIMG @@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[IMG~[IMGIMG@@~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[~[@MENUTEXT RoundbuttonTEXTIMG1IMG^RRJVZkRJ^wwVZswwsN9gswwsNVZkswsIMG.@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkIMGVRcsNwsNww9gRJwssZR{o{o{ocsNIMGlsNZcZksNZcZksNZcZksNZcZksNZcZksNZcZksNZcZkIMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMGlZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNZkcZsNIMG=^{o{o{o1FcZkZk{o{oVsN{osswRJ9gww{ZsNsR1F9gZ1FsNZIMG:@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGv{oZkZkcR{o{oZkZkRsssZk1FwwsRR{^RJ{VRJ1FsNcIMG`B919J)1RZ5J)RJRV1F)%9sNRR1)%9sNsNRJIMG.@@)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%)%@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGX5RJ1Z1FJ)VsN9J)RsN1F)%J)RsN1Fk-!1FIMGl)%5B1F)%5B1F)%5B1F)%5B1F)%5B1F)%5B1F)%5B1FIMG@@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGl1FB5)%1FB5)%1FB5)%1FB5)%1FB5)%1FB5)%1FB5)%IMGtk-5=1FRJRJk-9BRJRJsN=51FsNVV5=RVZ11F^c5=RRJ=IMG:@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@================================================================IMG|RJB==5J)sNRJ1F=1k-ZRsN=k-R^VRJBcR99^9B=V^IMG^ssw{swsw{s{{{{ss{{{{IMG.@@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@@@@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{IMGVsws{s{{s{{{ws{{{wsIMGl{{{{{{{{{{{{{{{{{{{{{{{{{{{{IMG@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{IMGl{{{{{{{{{{{{{{{{{{{{{{{{{{{{IMGZkww{{{9gw{{{{ss{{{{{ws{s{o{w{oswIMG:@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@@@@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMGv{{{ws{{{{s{{{{{o{ssw{os{o{oswMENUTEXT CheckbuttonTEXTIMG1IMG  5999991 Rw{{{{{wwc Vswwwwwwwc Z{osssssssc ^{o{o{o{o{oc csswwwwww9g 9g{{{{{{{{{o ^{o sN^cccccccRRJ======1FIMG   J]_]_]_]_Vd , FJJJJ Cx:6 Wz6 >52")>w \"" e \&<" .2pC9*2. <:?> o?C:\" <"GOOC~KOOG\& ){G______{G21F555551FIMG  Zk9g9g9g9g9g9gc {o{{{{{{Zk{o{o {o{{{{{{o9gw{o {owZkZk{w9gs{ swwc{o9gZkwws s{{sc9gw{{s w{{{Zks{{{w w{{{{{{{{w s{{{{{{{{wwsssssswMENUTEXT PosbuttonTEXTIMG1IMGRJ1FRRJZkcsNRJZkwsNRJZkww9gRJRJZkwssZRRJZk{o{o{ocsNRJZk{oZkZkcRRJZk{o{oZkZkRRJZksssZk1FRJZk{wsRRRJZk{^RJRJZk{VRJRJZk1FsNRJ1FcIMGZS>=ZR>zJ9ZR>WzJ-ZR>OJ96)ZR>JF:-ZR>JF:uNZS>:6{.[*1ZS>>:6|.9ZR>?GC:|.)ZZR>_KC:2SJZR>OC562BZR>]O3>SJZR>RBZZS>9gIMG{o{os{o{{s{o{{s{o{{s{o{{ws{o{{{{ws{o{{{{ws{o{{{{{s{o{{{{o{o{ss{o{w{o{o{s{o{{os{o{owMENUTEXT ScrollbarTEXTIMG1IMGZ  V^^^^^^^^R V9gZkZkZkZkZkZkZkZkZ {{Z VwwRZ VZk-)%V ZZkZk9)%)%)%{{V ^ZkV!!!!!sssN c{osc1!!!wwR 9gswwwVJ)!wwV 9gw{{{{wRJ{{Z 9gswwwwwwwwZ ^^^^^^^^^^^IMGZ  1155595555k- k-BBBBBBBBB1 1RVVVVVVVV 1sNVVVVRJ)VV1 1RJVVVBVV1 1FV1FVV1 91F1RJRJJ) 9RJ9c sNsNk- =sNRRsNk-RRk- BsNVVVVsN!VV5 =sNRRRRRRRR1 99999999999IMGZ  wwwwwwwwwws s{{{{{{{{{w ww wsw w{wc^w w{{Zk^^^s w{sZZZZZ{{s w{w9g^^^s {sc^s {{ow {w wwwwwwwwwwwIMGZ  R^^^^^^^^V ZZkZkZkZkZkZkZkZk9gV ^{{{{{{{{w^ ZRwwV V)%k-ZV V{{)%)%)%9ZkZkZ sNss!!!!!VZk^ Rww!!!1cs{oc Vww!J)Vwwws9g Z{{RJw{{{{w9g Zwwwwwwwws9g ^^^^^^^^^^^IMGZ  k-5555955511 1BBBBBBBBBk- 9RRRRRRRRR9 1VVJ)RVVVVsN1 1VVBVVVRJ1 1VV1FV1F J)RJRJ11F9 k-sNsNc 9RJ9 k-RRk-sNRRsN= 5VV!sNVVVVsNB 1RRRRRRRRsN= 99999999999IMGZ  swwwwwwwwww w{{{{{{{{{s ww wsw w^cw{w s^^^Zk{{w s{{ZZZZZs{w s^^^9gw{w s^cs{ w{o{ w{ wwwwwwwwwwwIMGRRJVZkRJ^wwVZsww9gswwVZksws=^{o{o{o5cZkZk{o{oVsN{osswRJ9gww{ZsNsR1F9gZ1FZIMG^@@@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGRcsNwsNww9gRJwssZR{o{o{ocsN{oZkZkcR{o{oZkZkRsssZk1F{wsRR{^RJ{VRJ1FsNcIMG XJ=5B15RW9)FJNvJ9*BJJ1Z*BF>%:&6::)[&2:>>SB*6>GG:z2??G_K5:_KO>u>GVRFIMG^@@!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!@]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c]c@=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[=[@OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO@KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::>@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K@OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO@SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS@WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW@R>R>R>S>S>R>R>R>R>R>S>S>R>R>R>R>R>S>S>R>R>R>R>R>R>S>S>R>R>R>R>R>S>S>R>R>R>R>R>S>S>R>R>R>R>R>S>R>R>R>R>R>R>S>R>R>R>R>R>R>S>R>S>S>@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZIMG=zJ9WzJ-OJ96)JF:-JF:uN:6{.[*1>:6|.9?GC:|.)Z_KC:2SJOC562B]O3>SJRBZ9gIMGr  VV^VVZ^c9g9g^ 9gw{wZkZk{oss^ ZZk{sw{w^ ZZk{Vsw{w^ ^Zk{Zk!cw{w^ ^Zk{9!1w{w^ ^Zk{Z)%!!V{w^ ^Zk{wk-)%!!J)ww^ ^Zk{R)%)%!!!RJw^ ^Zk{{sww{w^ ^Zk{{swww^ RZ^ZZVVsNRVZ^IMGr  wswwwwwww{{w w{{{{{w w{{w w{sw w{{Zww w{ZkZ9gw w{w^Z^sw w{c^Z^cw w{s^^Z^^{ow w{{w w{{w swwwwwsssswwIMGr  1k-911199==9 1BRRsNRJ1F1FRJsNsN9 5BRVVVRJsNsNRsN9 BRVVV1RVsN9 5BRVV1F9RVR9 5BRVVc sNVR9 5BRVk-VR9 5BRRBsNR9 9BRJ)!R9 5BRVVVRJsNRVR9 5BRVVVVRJsNRR9 k-19111J)k-k-19IMGr  RZZZVVsNRVZZ^ ^Zk{sww{w^ ^Zk{sww{w^ ^ZkR)%)%!!!RJw^ ^Zkwk-)%!!J)ww^ ^ZkZ)%!!V{w^ ^Zk9!1w{w^ ^ZkZk!cw{w^ ^Zk{Vsw{w^ ^Zksw{w^ 9g{wZkZk{osws^ VVVVZ^c9g9g9g^IMGr  swwwwsssswww w{{w w{{w w{s^^Z^^{ow w{c^Z^cw w{w^Z^sw w{ZkZ9gw w{{Zww w{sw w{{w w{{{{{w wswwwwww{{{wIMGr  k-1111J)k-k-519 5BVVVVRJsNRVR9 5BVVVVRJsNRVR9 5BVJ)!R9 5BVRBsNR9 9BVVk-VR9 5BVVVc sNVR9 5BVVV1F9RVR9 5BVVVV1RVR9 5BVVVVRJsNsNRR9 1BRsNRJ1F1FRJsNsNsN9 1k-11199=B=9IMGV5=ZRJsNc^VV RsN9g{oZkZk9gZRJ Z1FswsZk{osss^RJ 1F9gws{o{owwwwVZ{w{o{oswwwZkRIMG@RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{RJZk{ws{oswww{IMGc1F{{s{o{o{owwwcR sNV{ws{oZk{oswsN RJ^ssZkZk{os9gsNRJRZkZkccZRJR1FRRsNRIMGSB)%1vJ:*[&9 5z262:&Z*9*)1 >:?>:6BBF5= Vu>_K?GG>:FJJR59gRFGO_KG>:>JNWBXJIMG@ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZR>WSO_KB:BKO=[]c!ZS>WSO_KB:BKO=[]c!ZS>WSO_KB>BKO=[]c!IMG9gRB]OO_K?G>:JJOWzJ= Z3>CCC:6FFJzJ9 SJ56::6{.::96-2B2|.|.[*)SJ)-Z91uNMENUTEXTBackTEXTIMG1  IMG IMG"@@@@@IMG IMG @IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG IMG IMG IMG MENUTEXTGadgetTEXTIMG1  IMGBB%=RNRN=% cJ5sV9k{ss9ksVJ5c ,MsfnnnnSfM, c$DTI]aaaaI]TDd$$8PX\a(a(aa\XL8(B HX`(ejiiiji(e`XHB$B,PaIimquuqqIiePB,4TIeqyz0~0~zyqIiT8 Liu~Q~r~r~Q~~umP @JazQ~~~~~Q~zje@ Dmrvz~~zrzmHIAU/frnr0fUJEkA(=(AEIMGsJB1BRSJuRg~s]o^ER UNb^s~ssss>oVTJ F^>o^s^s_s_s^s^okRV^9Rk>o^"n1?o>of6NVUNbo?okbg=_s?oj}ZBJfk?o_s_s_s9?s?ojf1Jn?so?s?sWV"^o?snf14Nn?s?s_w_wE{_w?so^1FSJf?s_w{{l1j{_w_woWRV Jo_w{m5j{{?wj3J xZ_w{b{o4NZ^w]wSNsNV^^VRJIMG BB $1ERRRRE1  c JAs^9o{s{s9os^JAd  $<]n6wWwVw6wn]@(A0gUfhnrrrrhnfgU8A0EanBrdrevrdrBrna%M0b4YnBvzzzzzzbv#nYb46c.  %*:>JOkSkS(GC:%* g!D.BIKkS[[kSIKCD.!>#*GkSW_c[kSG#*> %>lW_gkgcWB) **K_gookckO.2B-Wcc.WB(6PF :22*:PFIMG9BB9!9RJRJ9! dj)N9gooYgNk-d '-6NV_wcV_N-6'=G.:BB:G.=!e"c*2::6c*$"e! c.6:GGC6c.  %*:>(GkSkSJOC:%* g!D.BIKkS[[kSIKCD.!>#*GkSWc_[kSG#*> %>lW_gkgcWB) **K_gookckO.2B-Wcc.WB(6PF :22*:PFIMG!dd!.)X:X:)N !F\coo\cF!  O72R<_<_<_<_R76p)!*72X6X:X:W672*! ).O%%%)..)%%%q.!%%.W6X:X:X67.%%! %*72x>BBFBx>76.% p%76x>BJJJJBx>W6%q*!X:BJNRRNJBX:!* q72FNRWWRNF72q pW6NR[Nx:p%x:JJx>%!7.!pp!7.IMG!dd!.)X:X:)N !F\coo\cF!  O72R<_<_<_<_R76p)!*72X6X:X:W672*! ).O%%%)..)%%%q.!%%.W6X:X:X67.%%! %*72x>BBFBx>76.% p%76x>BJJJJBx>W6%qp%76x>BJJJJBx>W6%qp%76x>BJJJJBx>W6%qp%76x>BJJJJBx>W6%qp%76x>BJJJJBx>W6%qp%76x>BJJJJBx>W6%qIMG   {o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o  {o{o{o{o{o{o{o{o{o{o IMGBB%=RNRN=% cJ5sV9k{ss9ksVJ5c ,MsfnnnnSfM, c$DTI]aaaaI]TDd$$8PX\a(a(aa\XL8(B HX`(e(edUiji(e`XHB$B,PaIimdU44qqIiePB,4TIeqdU44$yyqIiT8 LidU44$Q~Q~~umP @dU44$~~~Q~zje@ dU4$rvz~~zrzmH dU4$IAU/frnr0fUJECI4kA(=(AEMENUTEXTDividerTEXTIMG1IMG IMG@@5555555555555555555555555555555555555555555555555555555555555555@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG simutrans-124.5/simutrans/themes/classic-large.tab000066400000000000000000000111021520675460500222720ustar00rootroot00000000000000# Default simutrans theme # # name of the theme (will be shown with file selector) name=Simutrans classic (large size) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 tooltip_text_color = #000000 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 # larger icons too icon_scaling = 150 # use the larger Button height gui_button_height = 22 # titlebar height gui_titlebar_height = 22 # width of the window elements gui_gadget_width = 22 # arrows and scrollbars gui_horizontal_arrow_height = 22 gui_horizontal_arrow_width = 22 gui_vertical_arrow_height = 22 gui_vertical_arrow_width = 22 gui_scrollbar_width = 22 gui_scrollbar_height = 22 # and a matching edit field height gui_edit_height = 22 # request larger font size (the resulting spacing is about 15% larger due to decent) font_size = 16 #################################colour stuff################################# gui_color_text = #000000 gui_color_text_highlight = #F8FCF8 gui_color_text_shadow = #000000 gui_color_text_title = #C0FC60 gui_color_text_strong = #C00000 gui_color_text_minus = #C00000 gui_color_text_plus = #000000 gui_color_text_unused = #F8FC00 gui_color_edit_text = #F8FCF8 gui_color_edit_text_selected = #A8ACA8 gui_color_edit_text_disabled = #606060 gui_color_edit_background_selected = #404040 gui_color_edit_beam = #F8FCF8 gui_color_chart_background = #989898 gui_color_chart_lines_zero = #D8DCD8 gui_color_chart_lines_odd = #F8FCF8 gui_color_chart_lines_even = #686868 gui_color_list_text_selected_focus = #F8FCF8 gui_color_list_text_selected_nofocus = #C8C8C8 gui_color_list_background_selected_focus = #0000F8 gui_color_list_background_selected_nofocus = #60A0F8 gui_color_obsolete = #0000F8 gui_color_empty = #F8FCF8 gui_color_button_text = #000000 gui_color_button_text_disabled = #686868 gui_color_button_text_selected = #000000 gui_color_colored_button_text = #000000 gui_color_colored_button_text_selected = #F8FCF8 gui_color_checkbox_text = #000000 gui_color_checkbox_text_disabled = #686868 gui_color_ticker_background = #B0B0B0 gui_color_ticker_divider = #000000 gui_color_statusbar_text = #000000 gui_color_statusbar_background = #989898 gui_color_statusbar_divider = #D8DCD8 gui_highlight_color = #D8DCD8 gui_shadow_color = #686868 gui_color_loadingbar_inner = #A8ACA8 gui_color_loadingbar_progress = #0000F8 cursor_overlay_color = #F88000 tooltip_background_color = #7094B8 tooltip_text_color = #000000 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #D74700 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window #window_frame_active = 0 # second: draw the title with a dark overlay (0=none, 100=full black) #bottom_window_darkness = 25 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #DDDDDD # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 4 gui_player_color_dark = 1 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #EEEEEE ##################################pakstuff################################## # pak with the images for this theme themeimages=classic.pak simutrans-124.5/simutrans/themes/classic.pak000066400000000000000000001343061520675460500212230ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1$$IMGB{o{o{o{o{oIMG"@@@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@@IMGB{o5IMGl{o{o{o{o{o{o{oIMG@@@@@@@@IMGlsN5sN5sN5sN5sN5sN5sN5IMG49g9g9g9gsNIMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@5555555555555555555555555555555555555555555555555555555555555555@IMG45555IMG455555555IMG@@5555555555555555555555555555555555555555555555555555555555555555@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMG(55sNsNIMGl55sN55sN55sN55sN55sN55sN55sNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGPsNZsNZsNZsNZsNZsNZsNZIMGB5sNsN5sNsN5sNZ{o{o{o{oIMG"@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG2sNZsNZZZ{o{oIMGBwwwwsswssIMG"@@@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssIMGB9wss9sss9ssR9IMGlwsswsswsswsswsswsswssIMG@@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssIMGlscR9scR9scR9scR9scR9scR9scR9IMG4sssscR9999IMG@@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@9999999999999999999999999999999999999999999999999999999999999999IMG4ccR9RRR99999IMG9g9g9g9gIMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMG,{o9g9g9g9g9g9g9gIMGP9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMGB9g9g9g9g9g9g9gIMG"9g9g9g9gsN5IMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMGsNsNMENUTEXT RoundbuttonTEXTIMG1IMGB{o{o{o{o9g9g{o9g9gIMG"@@@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMGB{o9g9g9g9g9g9g9g5IMGl{o9g9g{o9g9g{o9g9g{o9g9g{o9g9g{o9g9g{o9g9gIMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMGl9gsN59gsN59gsN59gsN59gsN59gsN59gsN5IMG49g9g9g9gsN5IMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@5555555555555555555555555555555555555555555555555555555555555555@IMG4sNsN5555IMG455555555IMG@@5555555555555555555555555555555555555555555555555555555555555555@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMG(55sNsNIMGl55sN55sN55sN55sN55sN55sN55sNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGPsNZsNZsNZsNZsNZsNZsNZIMGB5sNsN5sNsN5sNZ{o{o{o{oIMG"@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG2sNZsNZZZ{o{oIMGBwwwwsswssIMG@@@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIMGB9wss9sss9ssR9IMGPwwwwwwwIMG IMGlscR9scR9scR9scR9scR9scR9scR9IMG4sssscR9999IMG@@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@9999999999999999999999999999999999999999999999999999999999999999IMG4ccR9RRR99999MENUTEXT EditfieldTEXTIMG1  IMG5IMG@@5555555555555555555555555555555555555555555555555555555555555555IMG55IMG55555555555555555555IMG @@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMG{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG{oIMG@@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG{oMENUTEXTListboxTEXTIMG1  IMGIMG@@IMGIMGIMG @@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMG{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG9gIMG@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@IMGMENUTEXTBackTEXTIMG1  IMG IMG IMG IMG @{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG!@@@VVb^ZZ^ZZ8gRb^rRZZV^ZVZb^ZbrRZrR^R^gVVb^ZZ^ZZ8gRb^rRZZV^ZVZb^ZbrRZrR^R^g@VbZZZ^ZZgrRbZRZZV^ZrRZ^ZZ^VVR^rR^8kVVbZZZ^ZZgrRbZRZZV^ZrRZ^ZZ^VVR^rR^8kV@^Zb^^ZZcrRbZrRV^VZ^rRZ^ZZ^VZV^V^8gVrR^Zb^^ZZcrRbZrRV^VZ^rRZ^ZZ^VZV^V^8gVrR@Zbb^VZbQNbZVVbVZZrRZZZ^^VZV^V^gZRZZbb^VZbQNbZVVbVZZrRZZZ^^VZV^V^gZRZ@cg^VVbrRbZVZbVZZVZZZ^ZZ^V^V^bZVZVcg^VVbrRbZVZbVZZVZZZ^ZZ^V^V^bZVZV@c^V^ZrRZZVZbZZZV^ZZ^ZZ^Z^VZZ^V^Vcc^V^ZrRZZVZbZZZV^ZZ^ZZ^Z^VZZ^V^Vc@^VZZQNVZVVbZVZV^ZZ^ZZbZ^VZZ^VZVbb^VZZQNVZVVbZVZV^ZZ^ZZbZ^VZZ^VZVbb@ZZ^QNV^VVbZVVZ^ZZ^ZZbZ^RZVbZZVbb^ZZ^QNV^VVbZVVZ^ZZ^ZZbZ^RZVbZZVbb^@^^rRV^rRVbZVVZb^Z^ZZ^Z^RZRcZ^Vb^Z^^^rRV^rRVbZVVZb^Z^ZZ^Z^RZRcZ^Vb^Z^@^rRV^rR^g^VVZb^Z^Z^^ZZVZrRgZZRb^ZbZ^rRV^rR^g^VVZb^Z^Z^^ZZVZrRgZZRb^ZbZ@VVZrR^gZrRrRZb^Z^Z^Z^ZVZrR8gZ^R^ZZcZ^VVZrR^gZrRrRZb^Z^Z^Z^ZVZrR8gZ^R^ZZcZ^@VZrR^gZrRRZg^Z^^^Z^VZZrRgZ^rR^ZZgZ^VVZrR^gZrRRZg^Z^^^Z^VZZrRgZ^rR^ZZgZ^V@ZV^bZrRVZbbZ^^bZbVZZrRgZZV^ZZ8k^^ZVZV^bZrRVZbbZ^^bZbVZZrRgZZV^ZZ8k^^ZV@VZbZRVb^ZV^^bZbV^ZRbV^V^ZZYoZ^ZVZVZbZRVb^ZV^^bZbV^ZRbV^V^ZZYoZ^ZVZ@ZbZVrR^bZZb^bVbrR^ZVcV^V^ZZYkZ^ZVZZZbZVrR^bZZb^bVbrR^ZVcV^V^ZZYkZ^ZVZZ@^ZVVb^ZZ^ZbVgrRbZVbR^V^ZZYoZZ^VZZZ^ZVVb^ZZ^ZbVgrRbZVbR^V^ZZYoZZ^VZZZ@ZZV^^Zb^Z^VgrRbZVbrR^V^ZZ9kVZ^VZZZZZZV^^Zb^Z^VgrRbZVbrR^V^ZZ9kVZ^VZZZZ@ZV^^ZbVVZVgrRbZV^rR^V^ZZYkrRZ^VZZZZZZV^^ZbVVZVgrRbZV^rR^V^ZZYkrRZ^VZZZZZ@V^^ZbVZZVgrR^ZZ^qR^Z^ZZ8krRZ^ZZZ^ZZZV^^ZbVZZVgrR^ZZ^qR^Z^ZZ8krRZ^ZZZ^ZZZ@ZZZbV^VVgrR^b^ZrR^Z^ZZ8kQNZbZVZ^ZZZVZZZbV^VVgrR^b^ZrR^Z^ZZ8kQNZbZVZ^ZZZV@ZZcR^VZgrRZb^ZqR^Z^ZZ8g0J^bZVZbZZZV^ZZcR^VZgrRZb^ZqR^Z^ZZ8g0J^bZVZbZZZV^@ZgrRbZZbrRZb^ZqR^Z^ZZ8gFcbZVZbZVZZ^ZZgrRbZZbrRZb^ZqR^Z^ZZ8gFcbZVZbZVZZ^Z@gRcZZcrRVb^ZqR^Z^ZZgFcbZVZcZRZZ^ZZgRcZZcrRVb^ZqR^Z^ZZgFcbZVZcZRZZ^ZZ@VbZZbQNbgbZQN^ZZVZg0JgbZRZgZrRZZbZZcVbZZbQNbgbZQN^ZZVZg0JgbZRZgZrRZZbZZc@bZZbrRb^b^rR^^ZZZgQNg^ZVR8gZrRZZbZZcVbZZbrRb^b^rR^^ZZZgQNg^ZVR8gZrRZZbZZcV@VZZrRbbbZrRbbVZVgQNg^ZVVbZQNZ^bZZcZ^VZZrRbbbZrRbbVZVgQNg^ZVVbZQNZ^bZZcZ^@ZZV^b^^rRbbVrRZgrRg^ZVZZZQNZ^cZZbZ^VZZV^b^^rRbbVrRZgrRg^ZVZZZQNZ^cZZbZ^V@ZZZbcV^bbrRrRZgrR8g^VZZZZqR^^gZZbZ^ZZZZZbcV^bbrRrRZgrR8g^VZZZZqR^^gZZbZ^ZZ@ZVZgqR^b^rRrRZbVgZVZRZZrRbZcZZb^ZZZZZVZgqR^b^rRrRZbVgZVZRZZrRbZcZZb^ZZZZ@V^cR^cZrRrR^bZ8gZbVVVZVb^rRV^gZbZZR^V^cR^cZrRrR^bZ8gZbVVVZVb^rRV^gZbZZR^@^grRZbZRVb^ZYkVbZVVZVc^rRZb^ZbVZrR^V^grRZbZRVb^ZYkVbZVVZVc^rRZb^ZbVZrR^V@gRZb^RZbZZ8kVbZVZZV^^ZZb^ZbVZrR^V^gRZb^RZbZZ8kVbZVZZV^^ZZb^ZbVZrR^V^@VVb^ZZ^ZZ8gRb^rRZZV^ZVZb^ZbrRZrR^R^gVVb^ZZ^ZZ8gRb^rRZZV^ZVZb^ZbrRZrR^R^g@VbZZZ^ZZgrRbZRZZV^ZrRZ^ZZ^VVR^rR^8kVVbZZZ^ZZgrRbZRZZV^ZrRZ^ZZ^VVR^rR^8kV@^Zb^^ZZcrRbZrRV^VZ^rRZ^ZZ^VZV^V^8gVrR^Zb^^ZZcrRbZrRV^VZ^rRZ^ZZ^VZV^V^8gVrR@Zbb^VZbQNbZVVbVZZrRZZZ^^VZV^V^gZRZZbb^VZbQNbZVVbVZZrRZZZ^^VZV^V^gZRZ@cg^VVbrRbZVZbVZZVZZZ^ZZ^V^V^bZVZVcg^VVbrRbZVZbVZZVZZZ^ZZ^V^V^bZVZV@c^V^ZrRZZVZbZZZV^ZZ^ZZ^Z^VZZ^V^Vcc^V^ZrRZZVZbZZZV^ZZ^ZZ^Z^VZZ^V^Vc@^VZZQNVZVVbZVZV^ZZ^ZZbZ^VZZ^VZVbb^VZZQNVZVVbZVZV^ZZ^ZZbZ^VZZ^VZVbb@ZZ^QNV^VVbZVVZ^ZZ^ZZbZ^RZVbZZVbb^ZZ^QNV^VVbZVVZ^ZZ^ZZbZ^RZVbZZVbb^@^^rRV^rRVbZVVZb^Z^ZZ^Z^RZRcZ^Vb^Z^^^rRV^rRVbZVVZb^Z^ZZ^Z^RZRcZ^Vb^Z^@^rRV^rR^g^VVZb^Z^Z^^ZZVZrRgZZRb^ZbZ^rRV^rR^g^VVZb^Z^Z^^ZZVZrRgZZRb^ZbZ@VVZrR^gZrRrRZb^Z^Z^Z^ZVZrR8gZ^R^ZZcZ^VVZrR^gZrRrRZb^Z^Z^Z^ZVZrR8gZ^R^ZZcZ^@VZrR^gZrRRZg^Z^^^Z^VZZrRgZ^rR^ZZgZ^VVZrR^gZrRRZg^Z^^^Z^VZZrRgZ^rR^ZZgZ^V@ZV^bZrRVZbbZ^^bZbVZZrRgZZV^ZZ8k^^ZVZV^bZrRVZbbZ^^bZbVZZrRgZZV^ZZ8k^^ZV@VZbZRVb^ZV^^bZbV^ZRbV^V^ZZYoZ^ZVZVZbZRVb^ZV^^bZbV^ZRbV^V^ZZYoZ^ZVZ@ZbZVrR^bZZb^bVbrR^ZVcV^V^ZZYkZ^ZVZZZbZVrR^bZZb^bVbrR^ZVcV^V^ZZYkZ^ZVZZ@^ZVVb^ZZ^ZbVgrRbZVbR^V^ZZYoZZ^VZZZ^ZVVb^ZZ^ZbVgrRbZVbR^V^ZZYoZZ^VZZZ@ZZV^^Zb^Z^VgrRbZVbrR^V^ZZ9kVZ^VZZZZZZV^^Zb^Z^VgrRbZVbrR^V^ZZ9kVZ^VZZZZ@ZV^^ZbVVZVgrRbZV^rR^V^ZZYkrRZ^VZZZZZZV^^ZbVVZVgrRbZV^rR^V^ZZYkrRZ^VZZZZZ@V^^ZbVZZVgrR^ZZ^qR^Z^ZZ8krRZ^ZZZ^ZZZV^^ZbVZZVgrR^ZZ^qR^Z^ZZ8krRZ^ZZZ^ZZZ@ZZZbV^VVgrR^b^ZrR^Z^ZZ8kQNZbZVZ^ZZZVZZZbV^VVgrR^b^ZrR^Z^ZZ8kQNZbZVZ^ZZZV@ZZcR^VZgrRZb^ZqR^Z^ZZ8g0J^bZVZbZZZV^ZZcR^VZgrRZb^ZqR^Z^ZZ8g0J^bZVZbZZZV^@ZgrRbZZbrRZb^ZqR^Z^ZZ8gFcbZVZbZVZZ^ZZgrRbZZbrRZb^ZqR^Z^ZZ8gFcbZVZbZVZZ^Z@gRcZZcrRVb^ZqR^Z^ZZgFcbZVZcZRZZ^ZZgRcZZcrRVb^ZqR^Z^ZZgFcbZVZcZRZZ^ZZ@VbZZbQNbgbZQN^ZZVZg0JgbZRZgZrRZZbZZcVbZZbQNbgbZQN^ZZVZg0JgbZRZgZrRZZbZZc@bZZbrRb^b^rR^^ZZZgQNg^ZVR8gZrRZZbZZcVbZZbrRb^b^rR^^ZZZgQNg^ZVR8gZrRZZbZZcV@VZZrRbbbZrRbbVZVgQNg^ZVVbZQNZ^bZZcZ^VZZrRbbbZrRbbVZVgQNg^ZVVbZQNZ^bZZcZ^@ZZV^b^^rRbbVrRZgrRg^ZVZZZQNZ^cZZbZ^VZZV^b^^rRbbVrRZgrRg^ZVZZZQNZ^cZZbZ^V@ZZZbcV^bbrRrRZgrR8g^VZZZZqR^^gZZbZ^ZZZZZbcV^bbrRrRZgrR8g^VZZZZqR^^gZZbZ^ZZ@ZVZgqR^b^rRrRZbVgZVZRZZrRbZcZZb^ZZZZZVZgqR^b^rRrRZbVgZVZRZZrRbZcZZb^ZZZZ@V^cR^cZrRrR^bZ8gZbVVVZVb^rRV^gZbZZR^V^cR^cZrRrR^bZ8gZbVVVZVb^rRV^gZbZZR^@^grRZbZRVb^ZYkVbZVVZVc^rRZb^ZbVZrR^V^grRZbZRVb^ZYkVbZVVZVc^rRZb^ZbVZrR^V@gRZb^RZbZZ8kVbZVZZV^^ZZb^ZbVZrR^V^gRZb^RZbZZ8kVbZVZZV^^ZZb^ZbVZrR^V^IMG @5555555555555555555555555555555555555555555555555555555555555555IMG5IMG@@5555555555555555555555555555555555555555555555555555555555555555IMG5MENUTEXT CheckbuttonTEXTIMG1IMG   {o{o{o{o{oZZZ {o9g9g9g9g9g9g5 {o9g9g9g9g9g9g5 {o9g9g9g9g9g9g5 {o9g9g9g9g9gsN5 Z9g9g9g9g9gsN5 ZZZZsNsNsN5 Z5555555 IMG  5555555555 5 5555sNsNsNsN 55sNsNsNsNsNsN 55sNsNsNsNsNZ 5sNsNsNsNsNsNZ 5sNsNsNsNsNsNZ 5sNsNsNsNsNsNZ 5sNsNZZZZZ IMG  555555555555555555 MENUTEXT PosbuttonTEXTIMG1IMG  {o{o{o9g{o{o {o9g9g9g{o{o {o9g9g9g9g9g{osN {o9g9g9g9gsN55 {o9g9gsN55{osN5555IMG  5555555sN55 5sNsNsN55 5sNsNsNsNsN 5sNsNsNsNsNsNZ 5sNsNsNsNZ5sNsNZ5sNZIMG  55555555555555555MENUTEXT ScrollbarTEXTIMG1IMG  {o{o{o{o9gsN {o{o9g9g9gsN {o{o9g9g9g9g9gsN 5sN9g9g9g9g9gsN 5sN9g9g9gsN5sN9gsN5sNIMG  55555sN5555 5555sNsN 555sNsNsNsN sNsNsNsNsNsNsNsNsN sNsNsNsNsNsNsNsNsNsNsNsNIMG  55555sN555555IMG  {o{o{o9g{o{o {o9g9g9g{o{o {o9g9g9g9g9g{osN {o9g9g9g9gsN55 {o9g9gsN55{osN5555IMG  5555555sN55 5sNsNsN55 5sNsNsNsNsN 5sNsNsNsNsNsNZ 5sNsNsNsNZ5sNsNZ5sNZIMG  55555555555555555IMG IMG^@@@@@@@@@@@@@@@IMG IMG{o{o{o{o9g9g{o9g9g{o9g9g{o9g9g{o9g9g{o9g9g{o9g9g{o9g9g{o9g9g9g9g9g9gsN5IMG^@@@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@5555555555555555555555555555555555555555555555555555555555555555@IMG{o9g9g9g9g9g9g9g59gsN59gsN59gsN59gsN59gsN59gsN59gsN5sNsN5555IMG  {o5{o5{o9g9g5{o9g9g5{o9g9g9g9g5{o9g9g9g9g5 9g9g9g9g9g9g9g5 9g5555555 IMG  5sN5sN5sN55sNsN55sNsN55sNsNsNsN55sNsNsNsN 55sNsNsNsNsNsN 55sNsNsNsNsNsN sNIMG  555555555 sNIMG   {o{o{o{o{o{o{osN {o9g9g9g9g9gsN5{o9g9g9g9g5{o9g9g9gsN59g9g9g59g9gsN59g5sN5IMG  5 55555555 55sNsNsNsNsNsN55sNsNsNsN55sNsNsNsN55sNsN55sNsN5sN5sNsNIMG  555555555IMG IMG@IMG IMG{o{o{o{o{o{o{o{o{o{o9g9g{o9g9g9g9g9g9g9g9g9g9gsN{o9g9g9g9g9g9g9g9g9g9g5IMG@{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5{o9g9g9g9g9g9g9g9g9g9g5IMG{o9g9g9g9g9g9g9g9g9gsN59g9g9gsNsNsNsNsNsNsNsN59g9g5555555555MENUTEXTGadgetTEXTIMG1  IMG  VZsN^ 5cRJ1Fc=1VZ^ZkVRJsNRJRsN RZ91ZZ=B51FIMG  sN^Zc1F Vc1F=cR=1FRRJBZ1FRsNV^=1F^9gBRJIMGz  =ZZZ^^^BZkZk^91RZ519IMG B1FVBcV1F{oR1ZcR1R5sN5IMG BsNRsNcRJRJ9gZksNRJ^c1FR9B=5IMG  == RR1R cZk1999c ZkZkZkcZkZkcZkB c91191c =11911IMG  B =RcZkc9119c119Zk91c1RZkZk91 =RcZkc=1 ZkIMG   {o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o  {o{o{o{o{o{o{o{o{o{o IMG tRRVtR sNsNZ^ tRRVVVVZZ^SN^ZZVRF RRJ1F= sNF9=B=99MENUTEXTDividerTEXTIMG1IMG IMG@@5555555555555555555555555555555555555555555555555555555555555555@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG simutrans-124.5/simutrans/themes/classic.tab000066400000000000000000000100641520675460500212100ustar00rootroot00000000000000# Default simutrans theme # # name of the theme (will be shown with file selector) name=Simutrans classic # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 tooltip_text_color = #000000 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 # size of tools (negative number: scaling factor in %) icon_scaling = 100 #################################colour stuff################################# gui_color_text = #000000 gui_color_text_highlight = #F8FCF8 gui_color_text_shadow = #000000 gui_color_text_title = #C0FC60 gui_color_text_strong = #C00000 gui_color_text_minus = #C00000 gui_color_text_plus = #000000 gui_color_text_unused = #F8FC00 gui_color_edit_text = #F8FCF8 gui_color_edit_text_selected = #A8ACA8 gui_color_edit_text_disabled = #606060 gui_color_edit_background_selected = #404040 gui_color_edit_beam = #F8FCF8 gui_color_chart_background = #989898 gui_color_chart_lines_zero = #D8DCD8 gui_color_chart_lines_odd = #F8FCF8 gui_color_chart_lines_even = #686868 gui_color_list_text_selected_focus = #F8FCF8 gui_color_list_text_selected_nofocus = #C8C8C8 gui_color_list_background_selected_focus = #0000F8 gui_color_list_background_selected_nofocus = #60A0F8 gui_color_obsolete = #0000F8 gui_color_empty = #F8FCF8 gui_color_button_text = #000000 gui_color_button_text_disabled = #686868 gui_color_button_text_selected = #000000 gui_color_colored_button_text = #000000 gui_color_colored_button_text_selected = #F8FCF8 gui_color_checkbox_text = #000000 gui_color_checkbox_text_disabled = #686868 gui_color_ticker_background = #B0B0B0 gui_color_ticker_divider = #000000 gui_color_statusbar_text = #000000 gui_color_statusbar_background = #989898 gui_color_statusbar_divider = #D8DCD8 gui_highlight_color = #D8DCD8 gui_shadow_color = #686868 gui_color_loadingbar_inner = #A8ACA8 gui_color_loadingbar_progress = #0000F8 cursor_overlay_color = #F88000 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #D74700 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window #window_frame_active = 0 # second: draw the title with a dark overlay (0=none, 100=full black) #bottom_window_darkness = 25 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #DDDDDD # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 4 gui_player_color_dark = 1 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #EEEEEE ##################################pakstuff################################## # pak with the images for this theme themeimages=classic.pak simutrans-124.5/simutrans/themes/flat-skin-large.tab000066400000000000000000000101731520675460500225500ustar00rootroot00000000000000# Flat Skin theme # Created by Phystam # # name of the theme (will be shown with file selector) name=Flat (large size) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 250 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 10000 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 #cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # in default theme white text gui_text_highlight= #FFFFFF # Use the default height #gui_button_height = 22 # larger icons too icon_width = 32 # windows drop a little shadow (default off) # gui_drop_shadows = 1 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) # Should be rather a user setting, if the theme does not absolutely require it #window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) # Should be rather a user setting, if the theme does not absolutely require it #second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #7b7b7b # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window #window_frame_active = 0 # second: draw the title with a dark overlay (0=none, 100=full black) bottom_window_darkness = 25 # third (best together with 2nd):use different text color for bar front_window_text_color = #FFFFFF bottom_window_text_color = #DDDDDD # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #CFCFCF #################################colour stuff################################# gui_color_text = #101010 gui_color_text_highlight = #fafafa gui_color_text_shadow = #202020 gui_color_text_title = #fafafa gui_color_text_strong = #ef4026 gui_color_obsolete = #00004a gui_color_empty = #FFFFFF gui_color_edit_text = #fafafa gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #333333 gui_color_edit_background_selected = #333333 gui_color_edit_beam = #333333 gui_color_chart_background = #a3a3a3 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #333333 gui_color_chart_lines_even = #646464 gui_color_button_text = #101010 gui_color_button_text_disabled = #333333 gui_color_button_text_selected = #FAFAFA gui_color_loadingbar_progress = #4d4d4d gui_color_loadingbar_inner = #cfcfcf ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 1 (to not occupy so much screen space) icon_scaling = 150 gui_titlebar_height = 22 gui_gadget_width = 22 gui_tab_header_vsize = 26 gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 toolbar_max_width = 0 toolbar_max_height = 0 gui_button_height = 24 gui_button_width = 100 ##################################pakstuff################################## # pak with the images for this theme themeimages=flat.pak simutrans-124.5/simutrans/themes/flat-skin-touch.tab000066400000000000000000000101751520675460500226020ustar00rootroot00000000000000# Flat Skin theme # Created by Phystam # # name of the theme (will be shown with file selector) name=Flat (touch) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 250 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 10000 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 #cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # in default theme white text gui_text_highlight= #FFFFFF # Use the default height #gui_button_height = 22 # larger icons too icon_width = 32 # windows drop a little shadow (default off) # gui_drop_shadows = 1 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) # Should be rather a user setting, if the theme does not absolutely require it #window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) # Should be rather a user setting, if the theme does not absolutely require it #second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #7b7b7b # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window #window_frame_active = 0 # second: draw the title with a dark overlay (0=none, 100=full black) bottom_window_darkness = 25 # third (best together with 2nd):use different text color for bar front_window_text_color = #FFFFFF bottom_window_text_color = #DDDDDD # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #CFCFCF #################################colour stuff################################# gui_color_text = #101010 gui_color_text_highlight = #fafafa gui_color_text_shadow = #202020 gui_color_text_title = #fafafa gui_color_text_strong = #ef4026 gui_color_obsolete = #00004a gui_color_empty = #FFFFFF gui_color_edit_text = #fafafa gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #333333 gui_color_edit_background_selected = #333333 gui_color_edit_beam = #333333 gui_color_chart_background = #a3a3a3 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #333333 gui_color_chart_lines_even = #646464 gui_color_button_text = #101010 gui_color_button_text_disabled = #333333 gui_color_button_text_selected = #FAFAFA gui_color_loadingbar_progress = #4d4d4d gui_color_loadingbar_inner = #cfcfcf ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears #toolbar_max_width = 0 toolbar_max_height = 1 (to not occupy so much screen space) icon_scaling = 200 gui_titlebar_height = 28 gui_gadget_width = 28 gui_tab_header_vsize = 26 gui_button_text_offset = 7,0,7 gui_color_button_text_offset = 7,0,7 toolbar_max_width = 0 toolbar_max_height = 0 gui_button_height = 28 gui_button_width = 100 gui_scrollbar_width = 28 gui_scrollbar_height = 28 ##################################pakstuff################################## # pak with the images for this theme themeimages=flat-touch.pak simutrans-124.5/simutrans/themes/flat-skin.tab000066400000000000000000000077471520675460500214750ustar00rootroot00000000000000# Flat Skin theme # Created by Phystam # # name of the theme (will be shown with file selector) name=Flat # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 250 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 10000 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 #cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # in default theme white text gui_text_highlight= #FFFFFF # Use the default height #gui_button_height = 22 # larger icons too icon_width = 32 # windows drop a little shadow (default off) # gui_drop_shadows = 1 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) # Should be rather a user setting, if the theme does not absolutely require it #window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) # Should be rather a user setting, if the theme does not absolutely require it #second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #7b7b7b # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window #window_frame_active = 0 # second: draw the title with a dark overlay (0=none, 100=full black) bottom_window_darkness = 25 # third (best together with 2nd):use different text color for bar front_window_text_color = #FFFFFF bottom_window_text_color = #DDDDDD # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #CFCFCF #################################colour stuff################################# gui_color_text = #101010 gui_color_text_highlight = #fafafa gui_color_text_shadow = #202020 gui_color_text_title = #fafafa gui_color_text_strong = #ef4026 gui_color_obsolete = #00004a gui_color_empty = #FFFFFF gui_color_edit_text = #fafafa gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #333333 gui_color_edit_background_selected = #333333 gui_color_edit_beam = #333333 gui_color_chart_background = #a3a3a3 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #333333 gui_color_chart_lines_even = #646464 gui_color_button_text = #101010 gui_color_button_text_disabled = #333333 gui_color_button_text_selected = #FAFAFA gui_color_loadingbar_progress = #4d4d4d gui_color_loadingbar_inner = #cfcfcf ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears icon_scaling = 100 # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 ##################################pakstuff################################## # pak with the images for this theme themeimages=flat.pak simutrans-124.5/simutrans/themes/flat-touch.pak000066400000000000000000003152001520675460500216420ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1$$IMGv===========IMG.@@================================================================@@@@@IMGv===========IMGl=======IMG@@@@@@@@IMGl=======IMGv===========IMG.@@@@@@@================================================================IMGv===========IMGv====================================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGv====================================IMGl============================IMG@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGl============================IMGv====================================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGv====================================IMGv=========================IMG.@@@================================================================@================================================================@================================================================@================================================================@================================================================IMGv=========================IMGl=====================IMG@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGl=====================IMGv=========================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@IMGv=========================IMGv===========IMG.@@================================================================@@@@@IMGv===========IMGl=======IMG@@@@@@@@IMGl=======IMGv===========IMG.@@@@@@@================================================================IMGv===========MENUTEXT RoundbuttonTEXTIMG1IMGv===========IMG.@@================================================================@@@@@IMGv===========IMGl=======IMG@@@@@@@@IMGl=======IMGv===========IMG.@@@@@@@================================================================IMGv===========IMGv====================================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGv====================================IMGl============================IMG@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGl============================IMGv====================================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGv====================================IMGv=========================IMG.@@@================================================================@================================================================@================================================================@================================================================@================================================================IMGv=========================IMGl=====================IMG@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGl=====================IMGv=========================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@IMGv=========================MENUTEXT EditfieldTEXTIMG1  IMG=IMG@@================================================================IMG=IMG====================IMG @@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMG====================IMG=IMG@@================================================================IMG=MENUTEXTListboxTEXTIMG1  IMG=IMG@@================================================================IMG==IMG====================IMG @@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssIMG====================IMG9gIMG@@================================================================IMG=MENUTEXTBackTEXTIMG1  IMG=IMG@@================================================================IMG=IMG @================================================================IMG!@@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMG @================================================================IMG=IMG@@================================================================IMG=MENUTEXT TitlebarTEXTIMG1IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG==IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@================================================================IMG==IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG IMG@@================================================================IMG MENUTEXT GadgetBackTEXTIMG166IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG==IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@================================================================IMG==IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG IMG@@================================================================IMG IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG==IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@================================================================IMG==IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP=====RRR=RRR=RRR=RRRIMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG IMG@@================================================================IMG IMGP=====RRR=RRR=RRR=RRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG==IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@================================================================IMG==IMGP====RRR=RRR=RRR=RRR=IMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG IMG@@================================================================IMG MENUTEXT CheckbuttonTEXTIMG1IMG  ========== == == == == == == == == ==========IMG  ========== == == == == == == == == ==========IMG   ======== ======== ======== ======== ======== ======== ======== ======== MENUTEXT PosbuttonTEXTIMG1IMGT  ============ == =RR= =RR= =RR= =RR= =RR= =RR= =RR= == ============IMGT  ============ ============ ===RR====== ===RR===== ===RR==== ===RR=== ===RR==== ===RR===== ===RR====== ============ ============IMGT   1111111111 11)%)%11111 11)%)%1111 11)%)%111 11)%)%11 11)%)%111 11)%)%1111 11)%)%11111 1111111111 MENUTEXT ScrollbarTEXTIMG1IMG=======================================1==111==1111==11111==111111==1111111==11111111==111111111==1111111111==111111111==11111111==1111111==111111==11111==1111==111==1=====================================IMGBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBIMG===================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================IMG=====================================1==111==1111==11111==111111==1111111==11111111==111111111==1111111111==111111111==11111111==1111111==111111==11111==1111==111==1=======================================IMGBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBIMG===================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================IMG IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMG IMG============================IMG@@================================================================@@@@@@@@@@@@@@@@@@@@@@@@@@@================================================================IMG============================IMG=============================================1==111==11111==1111111==111111111==11111111111==1111111111111==111111111111111==11111111111111111==111111111111111=============================================IMG===================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================IMGBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBIMG=============================================111111111111111==11111111111111111==111111111111111==1111111111111==11111111111==111111111==1111111==11111==111==1=============================================IMG===================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================IMGBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBIMG IMG@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMG IMGH============================IMG@================================================================================================================================IMGH============================MENUTEXTGadgetTEXTIMG1  IMG11111111111111111111111111111111111111111111111111111111111111111111111111 11111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111IMG 11111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111IMG 11111111111111111111111111111111111111111111111111111111111111111111111 111111111 1111111 111111111  IMG   1111111111111 111111 1111111 11111111 111111111 1111111111 111111111 11111111 1111111 1111111111111111111  IMG 1111111111111 111111 1111111 11111111 111111111 1111111111 111111111 11111111 1111111 1111111111111111111IMG  11111111111111111111111111111111111111111111111111111111111111111111BB1111111111111111BB1111111111111111111111111111111111111111111111111111  IMG111111111111111111111111111111111111111111111111 111111 111111 111111 1111111111111111111111111111111111111111111111111111111111IMG{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o   {o{o{o{o{o{o{o{o{o{o{o{o {o{o{o{o{o{o{o{o{o{o{o{o {o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o {o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o  {o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG  11111111111111111111111111111111111111111111111111 111111111111 111111111111 111111111111 1111111111111111==111111==111199==111199==1111111111111111  MENUTEXTDividerTEXTIMG1IMG IMG@@================================================================IMG simutrans-124.5/simutrans/themes/flat.pak000066400000000000000000002251721520675460500205320ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1$$IMGv===========IMG.@@================================================================@@@@@IMGv===========IMGl=======IMG@@@@@@@@IMGl=======IMGv===========IMG.@@@@@@@================================================================IMGv===========IMGv====================================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGv====================================IMGl============================IMG@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGl============================IMGv====================================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGv====================================IMGv=========================IMG.@@@================================================================@================================================================@================================================================@================================================================@================================================================IMGv=========================IMGl=====================IMG@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGl=====================IMGv=========================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@IMGv=========================IMGv===========IMG.@@================================================================@@@@@IMGv===========IMGl=======IMG@@@@@@@@IMGl=======IMGv===========IMG.@@@@@@@================================================================IMGv===========MENUTEXT RoundbuttonTEXTIMG1IMGv===========IMG.@@================================================================@@@@@IMGv===========IMGl=======IMG@@@@@@@@IMGl=======IMGv===========IMG.@@@@@@@================================================================IMGv===========IMGv====================================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGv====================================IMGl============================IMG@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGl============================IMGv====================================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGv====================================IMGv=========================IMG.@@@================================================================@================================================================@================================================================@================================================================@================================================================IMGv=========================IMGl=====================IMG@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGl=====================IMGv=========================IMG.@@================================================================@================================================================@================================================================@================================================================@================================================================@IMGv=========================MENUTEXT EditfieldTEXTIMG1  IMG=IMG@@================================================================IMG=IMG====================IMG @@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMG====================IMG=IMG@@================================================================IMG=MENUTEXTListboxTEXTIMG1  IMG=IMG@@================================================================IMG==IMG====================IMG @@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssIMG====================IMG9gIMG@@================================================================IMG=MENUTEXTBackTEXTIMG1  IMG=IMG@@================================================================IMG=IMG @================================================================IMG!@@@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g@9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9g9gIMG @================================================================IMG=IMG@@================================================================IMG=MENUTEXT TitlebarTEXTIMG1IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG==IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@================================================================IMG==IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG IMG@@================================================================IMG MENUTEXT GadgetBackTEXTIMG166IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG==IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@================================================================IMG==IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG IMG@@================================================================IMG IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG==IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@================================================================IMG==IMGZ======RRRR=RRRR=RRRR=RRRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP=====RRR=RRR=RRR=RRRIMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG IMG@@================================================================IMG IMGP=====RRR=RRR=RRR=RRRIMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG==IMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@================================================================IMG==IMGP====RRR=RRR=RRR=RRR=IMG@@================================================================@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGP====RRR=RRR=RRR=RRR=IMGl=RRR=RRR=RRR=RRR=RRR=RRR=RRRIMG@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGlRRR=RRR=RRR=RRR=RRR=RRR=RRR=IMG IMG@@================================================================IMG MENUTEXT CheckbuttonTEXTIMG1IMG  ========== == == == == == == == == ==========IMG  ========== == == == == == == == == ==========IMG   ======== ======== ======== ======== ======== ======== ======== ======== MENUTEXT PosbuttonTEXTIMG1IMGT  ============ == =RR= =RR= =RR= =RR= =RR= =RR= =RR= == ============IMGT  ============ ============ ===RR====== ===RR===== ===RR==== ===RR=== ===RR==== ===RR===== ===RR====== ============ ============IMGT   1111111111 11)%)%11111 11)%)%1111 11)%)%111 11)%)%11 11)%)%111 11)%)%1111 11)%)%11111 1111111111 MENUTEXT ScrollbarTEXTIMG1IMG  ============ == == =RR= =RR= =RR= =RR= =RR= =RR= =RR= == == ============IMG  ============ ============ ============ ======RR=== =====RR=== ====RR=== ===RR=== ====RR=== =====RR=== ======RR=== ============ ============ ============IMG   1111111111 1111111111 11111)%)%11 1111)%)%11 111)%)%11 11)%)%11 111)%)%11 1111)%)%11 11111)%)%11 1111111111 1111111111 IMG  ============ == == =RR= =RR= =RR= =RR= =RR= =RR= =RR= == == ============IMG  ============ ============ ============ ===RR====== ===RR===== ===RR==== ===RR=== ===RR==== ===RR===== ===RR====== ============ ============ ============IMG   1111111111 1111111111 11)%)%11111 11)%)%1111 11)%)%111 11)%)%11 11)%)%111 11)%)%1111 11)%)%11111 1111111111 1111111111 IMG IMG@ @RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMG IMGr =============IMG@ @================================================================@@@@@@@@@@@@================================================================IMGr =============IMG  ============= == == =R= =RR= =RR= =RR= == =RRRRRRR= == == =============IMG   =========== =========== =====)%===== ====)%)%==== ===)%)%=== ==)%)%== ==== ==)%)%)%)%)%)%)%== =========== =========== IMG  ============= ============= ============= ======R====== =====RR===== ====RR==== ===RR=== ====== ===RRRRRRR=== ============= ============= =============IMG  ============= == == =RRRRRRR= == =RR= =RR= =RR= =R= == == =============IMG   =========== =========== ==)%)%)%)%)%)%)%== ==== ==)%)%== ===)%)%=== ====)%)%==== =====)%===== =========== =========== IMG  ============= ============= ============= ===RRRRRRR=== ====== ===RR=== ====RR==== =====RR===== ======R====== ============= ============= =============IMG IMG  @ RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRR RRRRRRRRRRRRRIMG IMG*  =============IMG  @ == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == ==IMG*  =============MENUTEXTGadgetTEXTIMG1  IMG  !!!!!!!!! !!!! !!!!!!!!!!!!!! !!IMG !!! !!!!!!!!!IMG2!!!!!!!!!!IMG!!!!!!!!!!!!!!!!!!!!IMG!!!!!!!!!!!!!!!!!!!!IMG  !!!!1!!!1!!!! !!!!!!!!B!!11!1!!11!11IMG  B!!!!!!11!!11!!!1!1!!!!1!!!!!!1!IMGD{o{o{o{o{oIMGj    !!!!!!!! ! !MENUTEXTDividerTEXTIMG1IMG IMG@@================================================================IMG simutrans-124.5/simutrans/themes/high-contrast-large.tab000066400000000000000000000122221520675460500234270ustar00rootroot00000000000000# # # Read the wiki article for better explanation and full parameters list # https://wiki.simutrans.com/en_themeDef # # name of the theme (will be shown with file selector) name=High Contrast (large size) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = #000000 # tooltip text color (240=black, 215=white) tooltip_text_color = 215 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 # windows drop a little shadow (default off) # gui_drop_shadows = 1 #################################window stuff################################# # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like Windows) # window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window # window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) front_window_bar_color = 1 bottom_window_bar_color = 3 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #C0C0C0 # make the chat window transparent in network games gui_chat_window_network_transparency = 13 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #000000 #################################colour stuff################################# default_window_title_color = #194766 gui_color_text = #c98926 gui_color_text_highlight = #FAFAFA gui_color_text_shadow = #868c86 gui_color_text_title = #bee6c1 #gui_color_text_strong = #E46A0B gui_color_empty = #FAFAFA gui_color_obsolete = #2878ff gui_color_edit_text = #c98926 gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #868c86 gui_color_edit_background_selected = #5686a6 gui_color_edit_beam = #ffffff gui_color_chart_background = #333333 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #39a1e6 gui_color_chart_lines_even = #ffffff gui_color_list_text_selected_focus = #FAFAFA gui_color_list_text_selected_nofocus = #FAFAFA gui_color_list_background_selected_focus = #5686a6 gui_color_list_background_selected_nofocus = #5686a6 gui_color_button_text = #FAFAFA gui_color_button_text_disabled = #868c86 gui_color_button_text_selected = #b3b3b3 gui_color_colored_button_text = #FAFAFA gui_color_colored_button_text_selected = #b3b3b3 gui_color_checkbox_text = #c98926 gui_color_checkbox_text_disabled = #868c86 gui_color_ticker_background = #000000 gui_color_ticker_divider = #ffffff gui_color_statusbar_text = #c98926 gui_color_statusbar_background = #000000 gui_color_statusbar_divider = #ffffff gui_highlight_color = #ffffff gui_shadow_color = #ffffff # gui_color_text_minus = #C00000 gui_color_text_plus = #c98926 # gui_color_text_unused = #E46A0B # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #000000 # Color of load bar in loag game, load serve etc gui_color_loadingbar_progress = #000000 gui_color_loadingbar_inner = #868c86 # Transparency color in factories boost icons (mail, energy, passengers) gui_color_image_transparency = #ffffff gui_player_color_dark = 5 gui_player_color_bright = 5 ##################################size stuff################################## # force main menu icon size icon_scaling = 150 # titlebar height gui_titlebar_height = 22 gui_gadget_width = 22 # safe margin around windows # gui_frame_left = 10 # gui_frame_top = 10 # gui_frame_right = 10 # gui_frame_bottom = 10 # horizontal and vertical margins between objects # gui_hspace = 4 # gui_vspace = 4 # tab minimum sizes gui_tab_header_vsize = 26 # gui_label_size = 24 # input boxes height # minimum height is the height of the position button graphic # gui_edit_height = 24 # checkbox height, default sizes come from the checkbox graphic # gui_checkbox_width = 10 # gui_checkbox_height = 10 # sum of the top and bottom margins for divider lines # default is twice the size of gui_vspace # gui_divider_vsize = 8 # buttons sizes gui_button_height = 24 # for buttons with fixed widths gui_button_width = 100 # text margins inside the buttons (left, top, right) gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 ###################################pakstuff################################### # pak with the images for this theme themeimages=highcontrast-large.pak simutrans-124.5/simutrans/themes/high-contrast.tab000066400000000000000000000120751520675460500223450ustar00rootroot00000000000000# # # Read the wiki article for better explanation and full parameters list # https://wiki.simutrans.com/en_themeDef # # name of the theme (will be shown with file selector) name=High Contrast # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = #000000 # tooltip text color (240=black, 215=white) tooltip_text_color = 215 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 # windows drop a little shadow (default off) # gui_drop_shadows = 1 #################################window stuff################################# # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like Windows) # window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window # window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) front_window_bar_color = 1 bottom_window_bar_color = 3 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #C0C0C0 # make the chat window transparent in network games gui_chat_window_network_transparency = 13 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #000000 #################################colour stuff################################# default_window_title_color = #194766 gui_color_text = #c98926 gui_color_text_highlight = #FAFAFA gui_color_text_shadow = #868c86 gui_color_text_title = #bee6c1 #gui_color_text_strong = #E46A0B gui_color_empty = #FAFAFA gui_color_obsolete = #2878ff gui_color_edit_text = #c98926 gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #868c86 gui_color_edit_background_selected = #5686a6 gui_color_edit_beam = #ffffff gui_color_chart_background = #333333 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #39a1e6 gui_color_chart_lines_even = #ffffff gui_color_list_text_selected_focus = #FAFAFA gui_color_list_text_selected_nofocus = #FAFAFA gui_color_list_background_selected_focus = #5686a6 gui_color_list_background_selected_nofocus = #5686a6 gui_color_button_text = #FAFAFA gui_color_button_text_disabled = #868c86 gui_color_button_text_selected = #b3b3b3 gui_color_colored_button_text = #FAFAFA gui_color_colored_button_text_selected = #b3b3b3 gui_color_checkbox_text = #c98926 gui_color_checkbox_text_disabled = #868c86 gui_color_ticker_background = #000000 gui_color_ticker_divider = #ffffff gui_color_statusbar_text = #c98926 gui_color_statusbar_background = #000000 gui_color_statusbar_divider = #ffffff gui_highlight_color = #ffffff gui_shadow_color = #ffffff # gui_color_text_minus = #C00000 gui_color_text_plus = #c98926 # gui_color_text_unused = #E46A0B # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #000000 # Color of load bar in loag game, load serve etc gui_color_loadingbar_progress = #000000 gui_color_loadingbar_inner = #868c86 # Transparency color in factories boost icons (mail, energy, passengers) gui_color_image_transparency = #ffffff gui_player_color_dark = 5 gui_player_color_bright = 5 ##################################size stuff################################## # force main menu icon size icon_scaling = 100 # titlebar height gui_titlebar_height = 15 # safe margin around windows # gui_frame_left = 10 # gui_frame_top = 10 # gui_frame_right = 10 # gui_frame_bottom = 10 # horizontal and vertical margins between objects # gui_hspace = 4 # gui_vspace = 4 # tab minimum sizes gui_tab_header_vsize = 22 # gui_label_size = 24 # input boxes height # minimum height is the height of the position button graphic # gui_edit_height = 22 # checkbox height, default sizes come from the checkbox graphic # gui_checkbox_width = 10 # gui_checkbox_height = 10 # sum of the top and bottom margins for divider lines # default is twice the size of gui_vspace # gui_divider_vsize = 8 # buttons sizes gui_button_height = 16 # for buttons with fixed widths gui_button_width = 100 # text margins inside the buttons (left, top, right) gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 ###################################pakstuff################################### # pak with the images for this theme themeimages=highcontrast.pak simutrans-124.5/simutrans/themes/highcontrast-large.pak000066400000000000000000002662421520675460500233740ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1$$IMG  ============= ============= == ==! == == == == == == == ==IMGR@ @================================================================@================================================================@@@@@@@@@@IMG ========================IMG*  ==IMG@@IMG==IMGJ  ============= =============IMG@@================================================================@================================================================IMG====IMG  ============= ============= ==c c ==!Zk! ==RJ! ==1!J)B ==B^!c ^ ==!^ss! ==Bc ==B! ==99 ==IMGR@ @================================================================@================================================================@@@@@@@@@@IMG ========================IMG*  ==IMG@@IMG==IMGJ  ============= =============IMG@@================================================================@================================================================IMG====IMG  ============= ============= == ==J)!!J) ==ZZkZkZ ==Bs{sB ==!! ==99! =={{{ ==cssc ==9J)J)9 ==!!!!IMGR@ @================================================================@================================================================@@@@@@@@@@IMG ========================IMG*  ==IMG@@IMG==IMGJ  ============= =============IMG@@================================================================@================================================================IMG====IMG  ||||||||||||| |||||||||||||||||||||||||||||||||IMG@@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||IMG ||||||||||||||||||||||||IMG||IMG IMG||IMGJ  ||||||||||||| |||||||||||||IMG@@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||IMG||||MENUTEXT RoundbuttonTEXTIMG1IMGsNsNsNsNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGsNsNsNsNIMGsNsNsNsNIMG@@@IMGsNsNsNsNIMGsNsNsNsNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGsNsNsNsNIMGsNsNsNsNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGsNsNsNsNIMGsNsNsNsNIMG@@@IMGsNsNsNsNIMGsNsNsNsNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGsNsNsNsNIMGsNsNsNsNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGsNsNsNsNIMGsNsNsNsNIMG@@@IMGsNsNsNsNIMGsNsNsNsNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGsNsNsNsNMENUTEXT EditfieldTEXTIMG1  IMGIMG@@@IMGIMG@IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG@IMGIMG@@@IMGMENUTEXTListboxTEXTIMG1  IMGIMG@@@IMGIMG@IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG@IMGIMG@@@IMGMENUTEXTBackTEXTIMG1  IMGIMG@@@IMGIMG@IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG@IMGIMG@@@IMGMENUTEXT CheckbuttonTEXTIMG1IMGB{{BBB{BB{!!{BB{BBB{{BIMGB{{BBB{BB{!BB!c !!19g9gR!!c{=!Rsw9!c 1Fwc)%!J)s=c !=1F!!!Z{1Fk-BZks^)%!c RwZRJZ^Bc 5w{c)%k-1FwsRBRZk{o)%!!^{{{o5!c 9sw9BBZkwsNc =s{R!B9gww^!!VV1B!)%1F1F)%!{BB{BBB{{BIMGc J)sNRRRRRRRRRRRRRRRRRRRRsNJ)c J)RRRRRRRRRRRRRRRRRRRRRRRRJ)sNRJ)J)RsNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR!RRRRRRRRRRRRRRRRRRRRRRRRRRsNRJ)J)RsNJ)RRRRRRRRRRRRRRRRRRRRRRRRJ)c J)sNRRRRRRRRRRRRRRRRRRRRsNJ)c MENUTEXT PosbuttonTEXTIMG1IMG:  IMG:wwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwIMG:sNsNsNsNsNsNsNsNsNsNsNsN sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNMENUTEXT ScrollbarTEXTIMG1IMG1ZcJ)R{cRJsc9scB1ZkZkRJ!Z9!{RJ)B!{RJ)B!Z9B1ZkZkRJ9scRJscJ)R{c1ZcIMG@)`NV% F@kssVBgsssssV` 1gssssssssV @)^ssssssss^Bsssssss`N1` sss@k F% sss@k F% sssssss`N1`  @)^ssssssss^B` 1gssssssssVBgsssssV% F@kssV@)`NVIMG591RJsNsN9c J)BsNsNsNsNsN9B!BsNsNsNsNsNsNsNsN9!=sNsNsNsNsNsNsNsN=J)c sNsNsNsNsNsNsN5!BsNsNsNRJ1!sNsNsNRJ1!sNsNsNsNsNsNsN5!B!=sNsNsNsNsNsNsNsN=J)c B!BsNsNsNsNsNsNsNsN9c J)BsNsNsNsNsN91RJsNsN959IMGcZ1c{RJ)csRJcs9RJZkZk1B9Z!BJ)R{!BJ)R{!9Z!RJZkZk1Bcs9csRJc{RJ)cZ1IMGV`N@)Vss@k F%VsssssgBVssssssssg1` B^ssssssss^@) ` 1`Nsssssss % F@ksss % F@ksss` 1`NsssssssB^ssssssss^@) Vssssssssg1` VsssssgBVss@k F%V`N@)IMG959sNsNRJ19sNsNsNsNsNBJ)c 9sNsNsNsNsNsNsNsNB!Bc J)=sNsNsNsNsNsNsNsN=!B!5sNsNsNsNsNsNsN!1RJsNsNsN!1RJsNsNsNB!5sNsNsNsNsNsNsNc J)=sNsNsNsNsNsNsNsN=!9sNsNsNsNsNsNsNsNB!B9sNsNsNsNsNBJ)c 9sNsNRJ195IMG IMG @@@@@@@@@@@@@@@@@@@@@@@@@IMG IMGIMG @@@@@@@@@@@@@@@@@@@@@@@@@IMGIMGB!!!!B11ZkZk9{{9sRRsJ)J)RJBBRJsZZsJ)99J)RR{ZkZk{1RJRJ1ZZccccccccIMG!!sNsNsNsN=sNsNsNsN=BsNsNsNsNsNsNB!sNsNRJRJsNsN!BsNsN11sNsNBc sNsNsNsNsNsNc J)sNsNsN!!sNsNsNJ)BsNsN55sNsNBsNsNsN!!sNsNsN1sNsNsNBBsNsNsN1RJsNsN==sNsNRJsNsNsNJ)J)sNsNsN5sNsNsNc c sNsNsN599999999IMG  @)ssss@)^ssss^` ssssss` 1ss@k@kss1gss F Fssgsss%%sssBsss  sssBgss`N`Nssg%sss11sss% Fsss` ` sss F@kss^^ss@k@)sssBBsss@)`Nssssss`NVVVVVVVVIMGccccccccZZ1RJRJ1{ZkZk{RRJ)99J)sZZsRJBBRJJ)J)sRRs9{{9ZkZk11B!!!!BIMG999999995sNsNsNc c sNsNsN5sNsNsNJ)J)sNsNsNRJsNsN==sNsNRJ1sNsNsNBBsNsNsN1sNsNsN!!sNsNsNBsNsN55sNsNBJ)sNsNsN!!sNsNsNJ)c sNsNsNsNsNsNc BsNsN11sNsNB!sNsNRJRJsNsN!BsNsNsNsNsNsNB=sNsNsNsN=sNsNsNsN!!IMGVVVVVVVV`Nssssss`N@)sssBBsss@)@kss^^ss@k Fsss` ` sss F%sss11sss%gss`N`NssgBsss  sssBsss%%sssgss F Fssg1ss@k@kss1` ssssss` ^ssss^@)ssss@)  IMG IMG @IMG IMGvIMG @IMGvMENUTEXTGadgetTEXTIMG1  IMGJ)cccZZcccJ)BsBBsB!{{!RJZZRJBssB!!RJRJZZJ)J){{{{cBBc9ss9{!!{ZkZZZk!!!!BB!!!!IMGJ)RRRR1BwsJ)J)wZ{ZRRw1^RRJZkJ)^^{Z1s1J)J)ZkZkZkJ)RwRs1BRJ)IMGZkZkZkZkZkZkZk^RJJ)=ZB{s{Z=^J)^RJ=w{wRJJ)ZZ=J)ss^wB{RJsBB1111IMGRBBZkRZ{RBsZkBJ)^{wRs{^1{RJ{^11^{wRBsBZ{{BBRRwRIMG1BBc1B{RJB1cZk1BBs{R!R{ZkB9{!R{Bs{R!1ZZk1B1{{RJBZ1BsBIMG9g!99VBJ)ZZk!s!BZkRs1Rk-ss{5^{o^wss{oV{Zk5)%V!k-k-9sRJs{sNwsNZk1FssNk-IMGk-ssNZk1FwsNs{sNk-k-9sRJ)%V!s{oV{Zk5^ws^{oss{5Rk-BZkRs1ZZk!s!9VBJ)!99gIMGz          IMGk-RJRRRJk-sNwwsN1wwww19{^^{91{{{sc BBc s{{{1{o{wwVc s{oBVww{{o9g{ww{Rc {o{oBR{ww{9g!wwsw{oc c Bc {owsww!)%{owswZZwsw{o)%ZwswZkRRZkwswZ1csw{{wsc1!!=1F1F=!!MENUTEXTDividerTEXTIMG1IMG IMG@@IMG simutrans-124.5/simutrans/themes/highcontrast.pak000066400000000000000000002027361520675460500223020ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1$$IMG  ============= ============= == ==! == == == == == == == ==IMGR@ @================================================================@================================================================@@@@@@@@@@IMG ========================IMG*  ==IMG@@IMG==IMGJ  ============= =============IMG@@================================================================@================================================================IMG====IMG  ============= ============= ==c c ==!Zk! ==RJ! ==1!J)B ==B^!c ^ ==!^ss! ==Bc ==B! ==99 ==IMGR@ @================================================================@================================================================@@@@@@@@@@IMG ========================IMG*  ==IMG@@IMG==IMGJ  ============= =============IMG@@================================================================@================================================================IMG====IMG  ============= ============= == ==J)!!J) ==ZZkZkZ ==Bs{sB ==!! ==99! =={{{ ==cssc ==9J)J)9 ==!!!!IMGR@ @================================================================@================================================================@@@@@@@@@@IMG ========================IMG*  ==IMG@@IMG==IMGJ  ============= =============IMG@@================================================================@================================================================IMG====IMG  ||||||||||||| |||||||||||||||||||||||||||||||||IMG@@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||IMG ||||||||||||||||||||||||IMG||IMG IMG||IMGJ  ||||||||||||| |||||||||||||IMG@@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||IMG||||MENUTEXT RoundbuttonTEXTIMG1IMGIMG@@IMGIMGIMG@@IMGIMGIMG@@IMGIMGsIMG@@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssIMGsIMGsIMG@@IMGsIMGsIMG@@ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssIMGsIMGsNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGsNIMGsNIMG@@IMGsNIMGsNIMG@@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMGsNMENUTEXT EditfieldTEXTIMG1  IMGIMG@@IMGIMG @IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG @IMGIMG@@IMGMENUTEXTListboxTEXTIMG1  IMGIMG@@IMGIMG @IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG @IMGIMG@@IMGMENUTEXTBackTEXTIMG1  IMGIMG@@IMGIMG @IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG @IMGIMG@@IMGMENUTEXT CheckbuttonTEXTIMG1IMG     B  B B      IMG     B1 V1 BB9sN sN9gB9g 19gwwB sN sN1 1F1F  IMG  VVVVVVVVVVVVV VV VV VBV VV VBV VBV VV VV VV VV VV VVVVVVVVVVVVVMENUTEXT PosbuttonTEXTIMG1IMG$ k-   k-IMG$ w5wwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwww wwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwww5wwwIMG$ sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN sNsNsNsNsNsNsNsNsNsNsN sNsNsNsNsNsNsNsNsNsNsNsN sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNMENUTEXT ScrollbarTEXTIMG1IMGjZBRJsRBRR{w^=ZksRJ)ZksRJ)R{w^=BsRRJsRZBIMGjR= F kww@J=wwwwww@J@Jswwww`oZ9cww k@J %cww k@J %@Jswwww`oZ9=@kwwwww@J F kww@JR=IMGj5J)Bk-BsNsN1BJ)ZsNsNsNsNsN11RJsNsNsNsN1F9)%BsNsNB1BsNsNB11RJsNsNsNsN1F9)%BJ)1FsNsNsNsNsN1Bk-BsNsN15J)IMGjBZRsRJRsB=^w{RJ)RsZkJ)RsZk=^w{RRBRsRJBZIMGj=R@Jww k F@Jwwwww@k=9Z`owwwws@J %@J kwwc %@J kwwc9Z`owwwws@J@Jwwwwww=@Jww k F=RIMGjJ)51sNsNBk-B1sNsNsNsNsNZJ)B)%91FsNsNsNsNRJ11BsNsNB1BsNsNB)%91FsNsNsNsNRJ11sNsNsNsNsN1FJ)B1sNsNBk-BJ)5IMG IMGj@@@@@@@@@@@@@@@@@IMG IMGIMGj@@@@@@@@@@@@@@@@@IMGIMGjRZkZkR{{BBsssRRRJJ)J)RJswws^^Z==ZBRRRRBIMGj1BB1BRJsNsNRJBJ)sNsNsNsNJ)ZsNBBsN1FBsNsN11sNsNBk-sNsNsNsNk-BsN1F1FsNBsNsN99sNsN5sNsN)%)%sNsN5J)1111J)IMGj@Jcc@Jswws=wwww=ww k kw@kww@J@Jww Fww % %ww F kw`o`ow kwwZZwwRww99wwR=@J@J@J@J=IMGjBRRRRBZ==Z^^swwsRJJ)J)RJRRsssBB{{RZkZkRIMGjJ)1111J)5sNsN)%)%sNsN5sNsN99sNsNBsN1F1FsNBk-sNsNsNsNk-BsNsN11sNsNBZsNBBsN1FJ)sNsNsNsNJ)BRJsNsNRJB1BB1IMGj=@J@J@J@J=Rww99wwRwwZZww kw`o`ow k Fww % %ww Fww@J@Jwwww k kw@k=wwww=swws@Jcc@JIMG IMG @IMG IMG0IMG @IMG0MENUTEXTGadgetTEXTIMG1  IMGjBccBZkJ)J)ZkssBB{{99{ss{cJ)J)cBccBBccccccIMGjB^ZkZkZ1s=R{^^ZkBsZk^ZkZk1RZkJ)ZkwBRBJ)^J){ZkBwJ)IMGjBRRRR1Z=B=^{sw{^RJJ)=1RZwswZBJ){sRZkZkBIMGjZZ9sZkc1F1Fs{oJ)1FsR1FsRRwZkJ)J)c{1F1FsZkZZIMGj11BB{R!Zks9BRJsR!J)R!J)R!BsZJ)!cs9BR!11BIMGjBBss5sV5sVsVsssssVssssc BBss5sss5IMGj5sssBBss5Vssssc ssssVs5sVss5sVsBBIMGz          IMGjJ)sNccsNJ)c{o{ocw99w9g{{cB=c{{9g^{{c==c{{^c{w15w{cBRJs{cc{sRJBBVVBMENUTEXTDividerTEXTIMG1IMG IMG@@IMG simutrans-124.5/simutrans/themes/menu.pak192comic.pak000066400000000000000000001761061520675460500225730ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1--IMGX J='-'-9H1EFA99JJIMG@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@J9E9A=EJ9AA=AJA99=9==9AAAA99JE9EJA999J9AJ=59JA9A9E59EA9A9=E999JJIMG@H5$9(IMGZ  H1Ei5H5(-)( i9= G1)( i9(1 $'1$&- )i5 $( '19$'1-  '1A G1)- '1 i9A$($ =H1)($-'1 )=$H5H5$ )A-'1'1 -9$ G5H5$ '19(G5-$$ H1A&-'1(-&- -'1 $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1Aabbb)Ab$&-$((bb A'1@ @ $$A '1H5 b$b bIMGX bAA abbab@ IMG@@bA aAAbA bAAabA bbA A bb@ @ A bbA bA a@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMG@abbIMGX bbbbaA bIMG@@@@ babAbA A bbabbaAAbAA bbAA AbbbbAAbA@ baA bbabaAbb@aA bA bbA @ @ bbA A bbA baAAbA bAAaA bIMG@bAbIMGZ  aA'1)( bbh=,^h= bh=ywf,^h= b,^f,^,^h= h=,^,^h=h= h=h=h='1 bb($ A $-'1 @ @ '1H5H5$ A$$'1'1 '1 G5H5$ b H5-$$ b$(-&- b $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMGH5i5EH1G1 =i9 (1i9 i5)'1$9'1G1 A'1$Ai9()H1=$=)-A)$9-G5(9'1'1&-AH1 '1- IMGX (99E9J$H59i5'-'-IMG@@JJ999E=9A9AE95E9A9AJ95=JA9J999AJE9EJ99AAAA9==9=99AJA=AA9JE=A9E9J@H19'-'-9H1i5j9A'-==i5=AA9='-i5'-H1A'1H1'-i5'-j9H1i5=i5H5AH1H1=H1H19'-9AH1H1'-9A9H19'-9=H1Ai5=H19i5'-'-@IMG@AFE=JIMGX J='-'-9H1EFA-'1IMG@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1IMG@H5$9(IMGZ  H1'1i5H5(-)( i9&- G1)( i9( $'1$&- ) $( '1$'1-  '1 G1)- '1 i9)$($ =$)($-'1 ) $H5H5$ )$-'1'1 - $ G5H5$ '1 (G5-$$ H1 &-'1(-&- i5'1 $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1a$b b)'1b$&-$$'1((b (A'1$@ $$&- '1)H5 $ '1bIMGX bAG1$-bbab@ IMG@@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMG@abbIMGX J='-'-9H1EFA-'1IMG@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1IMG@H5$9(IMGZ  H1'1i5H5(-)( i9&- G1)( i9( $'1$&- ) $( '1$'1-  '1 G1)- '1 i9)$($ =$)($-'1 ) $H5H5$ )$-'1'1 - $ G5H5$ '1 (G5-$$ H1 &-'1(-&- i5'1 $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1a$b b)'1b$&-$$'1((b (A'1$@ $$&- '1)H5 $ '1bIMGX bAG1$-bbab@ IMG@@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMG@abbIMGX J='-'-9H1EFA-'1IMG@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1IMG@H5$9(IMG  H1'1i5)(i9&-i9)'1'1i9) =$)-'1 ) $H5H5$ )$-'1'1 - $ G5H5$ '1 (G5-$$ H1 &-'1(-&- i5'1 $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1a$b b)'1b$&-$$'1((b (A'1$@ $$&- '1)H5 $ '1bIMGX bAG1$-bbab@ IMG@@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMG@abbMENUTEXT RoundbuttonTEXTIMG1IMGPJ=EFA=0NqREIMG"@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@J9E9A=EJ9AA=AJA99=9==9AAAA99JE9EJA999J9AJ=59JA9A9E59EA9A9=E999JJ@'1-$ - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ (-H5-$$ '1&1$-( (&-(IMGPH5$9( abIMGH1Ei5H5i9= G1i9(1 )i5 '19$'1'1A G1i9A$=H1)()=$)A--9$'19(G5H1A&-'1 -'1 IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1Aabbb)Ab$&-$((bb A'1@ @ $$A '1H5 b$b bIMGP$ abAbIMG"@@-$G1&-G1$H5'1$(H5$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)G1$&-G1-'1@bA aAAbA bAAabA bbA A bb@ @ A bbA bA a@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMGP$bbabbIMGPbbabb$IMG"@@@@ babAbA A bbabbaAAbAA bbAA AbbbbAAbA@ baA bbabaAbb@aA bA bbA @ @ bbA A bbA baAAbA bAAaA b@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-IMGPbAba $IMGaA'1 bbbbA)&-$$bb((A @ @ '1A$$'1 b H5b$b IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMGH5i5EH1G1 =i9 (1i9 i5)'1$9'1G1 A'1$Ai9()H1=$=)-A)$9-G5(9'1'1&-AH1 '1- IMGPba (9$H5IMG"@@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1@JJ999E=9A9AE95E9A9AJ95=JA9J999AJE9EJ99AAAA9==9=99AJA=AA9JE=A9E9J@H19'-'-9H1i5j9A'-==i5=AA9='-i5'-H1A'1H1'-i5'-j9H1i5=i5H5AH1H1=H1H19'-9AH1H1'-9A9H19'-9=H1Ai5=H19i5'-'-@IMGPEqR0N=AFE=JIMGPJ=EFA=0NqREIMG"@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1@'1-$ - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ (-H5-$$ '1&1$-( (&-(IMGPH5$9( abIMGH1'1i5H5i9&- G1i9( ) '1$'1'1 G1i9)$=$)() $)$-- $'1 (G5H1 &-'1 i5'1 IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1a$b b)'1b$&-$$'1((b (A'1$@ $$&- '1)H5 $ '1bIMGP$ abAbIMG"@@-$G1&-G1$H5'1$(H5$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)G1$&-G1-'1@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMGP$bbabbMENUTEXT EditfieldTEXTIMG1  IMG  h=IMG@@ @h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=IMG  fIMG h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h=IMG @@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^IMGywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfIMG h=h=fIMG@@ywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywyw@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMGffffMENUTEXTListboxTEXTIMG1  IMG  h=IMG@@ @h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=IMG  fIMG h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h=IMG @@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMGywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfIMG h=h=fIMG@@ywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywyw@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMGffffMENUTEXTBackTEXTIMG1  IMGffffIMG@@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywIMGfh=h= IMG@fywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywIMG!@@@,^,^,^,^L^,^,^,^L^L^L^L^L^,^,^,^L^L^,^,^,^L^L^L^,^,^,^,^,^,^,^,^+^+^+^+^+^ Z+^+^+^+^+^ Z Z+^+^+^+^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^ Z+^+^ Z+^+^+^+^ Z Z Z+^+^ Z+^+^+^+^+^+^+^+^,^,^,^,^,^+^+^+^+^+^+^+^,^,^,^+^+^+^,^,^,^,^,^+^,^,^@+^,^+^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^+^+^+^+^+^+^+^ Z Z Z Z Z Z Z+^+^+^+^,^,^,^,^,^,^,^,^+^+^,^+^,^,^,^,^,^,^,^,^,^+^+^+^@+^+^+^+^+^,^+^+^+^ Z+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^L^L^L^,^,^L^L^L^,^,^,^,^,^,^,^,^+^+^+^@L^L^L^L^L^L^,^L^L^L^L^,^,^,^,^,^,^,^,^+^,^+^+^ Z+^ Z Z Z Z Z Z Z+^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^,^,^,^L^L^L^L^L^L^L^L^@,^+^+^+^,^+^+^+^+^ Z+^ Z+^ Z+^+^ Z+^+^+^+^+^+^+^+^+^,^,^,^,^+^,^+^+^,^,^,^,^,^+^+^,^+^,^,^,^,^+^+^,^+^,^,^,^,^,^,^+^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^ Z Z Z Z+^+^,^+^+^+^+^+^,^,^,^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^+^,^,^+^+^+^+^+^ Z Z+^+^+^+^+^+^+^+^+^+^+^,^+^,^,^,^,^,^,^,^,^,^,^@,^,^+^+^,^,^,^,^,^,^,^,^+^,^+^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^+^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@+^+^ Z+^+^,^,^,^+^+^+^+^+^+^+^ Z Z Z Z+^+^+^+^,^+^+^+^ Z Z Z Z Z+^+^+^+^,^,^,^,^,^+^+^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^@ Z+^+^+^+^,^,^+^+^+^+^+^+^+^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^ Z+^+^+^+^+^+^+^ Z Z Z Z Z Z Z Z@L^,^,^L^L^L^L^L^,^,^,^,^,^,^,^,^+^+^+^ Z+^ Z+^+^+^+^+^+^+^+^ Z+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^,^,^L^L^,^L^@,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^+^+^+^+^+^,^,^,^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^L^L^L^L^,^,^@,^+^+^+^+^,^,^,^,^+^+^+^,^,^,^,^,^,^,^,^,^,^L^L^L^L^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^ Z+^+^ Z+^+^+^+^+^+^+^+^+^+^,^+^,^,^@,^+^+^ Z+^+^+^ Z+^+^ Z+^+^+^+^+^+^+^,^,^,^+^+^+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^+^+^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^+^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^,^L^,^L^L^L^,^,^,^,^,^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^+^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^,^,^,^,^+^+^+^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^L^,^,^L^L^,^,^,^,^,^,^,^,^,^+^,^,^+^+^,^,^,^,^,^+^+^,^,^,^,^,^@,^,^,^,^,^,^,^,^+^+^+^+^ Z+^+^+^+^+^+^+^+^+^ Z Z+^+^,^+^+^+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^,^+^+^+^+^+^+^,^,^,^+^+^+^,^,^+^+^,^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^,^,^,^,^,^L^L^L^L^L^L^,^,^,^,^,^,^@,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^L^,^L^,^,^,^,^L^L^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^+^,^,^+^+^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^+^,^,^,^+^+^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^L^,^L^L^L^L^L^,^,^,^L^L^L^L^,^,^,^,^,^,^,^,^,^+^,^+^+^+^+^+^+^+^+^+^,^,^,^,^,^@,^,^,^,^,^+^+^+^+^+^+^+^+^,^+^+^+^ Z+^+^+^+^+^+^,^,^,^,^+^,^,^,^+^+^+^,^,^+^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^L^L^L^,^,^L^L^L^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^+^+^+^,^,^,^+^,^+^,^+^+^+^+^+^,^,^,^,^,^+^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^ Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^ Z Z Z Z+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^@+^+^+^+^+^+^ Z Z+^+^+^+^,^,^,^,^,^,^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^ Z+^+^,^,^,^+^@+^+^+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^,^,^,^,^+^@,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^+^ Z Z Z Z+^ Z Z Z Z+^+^+^+^ Z+^ Z+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@+^+^+^,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^+^+^+^+^+^+^+^ Z Z+^+^+^+^,^,^,^,^,^+^+^+^+^+^+^+^,^,^,^@L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^L^@,^,^,^+^+^ Z+^+^ Z+^+^ Z Z Z Z Z Z+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^,^L^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^+^+^+^,^,^,^+^,^+^,^+^+^+^+^+^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^+^+^+^,^+^,^,^,^+^+^+^+^,^,^,^,^+^+^+^,^,^,^,^,^,^,^,^,^,^,^@,^,^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^,^,^,^,^L^,^L^,^L^,^L^L^L^L^L^,^,^,^@,^,^,^,^,^,^+^+^+^+^,^+^,^,^,^,^,^,^,^+^+^,^,^+^+^+^+^+^+^+^+^ Z Z Z Z+^+^+^+^+^+^+^+^ Z Z+^+^+^+^,^,^,^,^,^,^,^,^+^+^,^+^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^ Z+^+^+^+^+^+^+^+^+^ Z+^+^+^+^+^+^+^+^,^,^,^,^,^+^+^+^+^+^+^+^,^,^,^+^+^+^,^,^,^,^,^+^,^,^,^,^,^@+^+^ Z+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^ Z Z Z Z Z Z Z Z Z Z+^+^+^+^ Z Z+^+^+^+^+^+^,^+^+^ Z+^+^+^@ Z Z Z Z+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^,^+^+^+^ Z@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^,^,^,^+^+^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^+^,^,^+^+^+^+^+^+^+^,^+^+^ Z+^+^+^+^+^ Z+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^@,^+^,^,^,^,^,^,^,^+^+^+^,^+^+^,^,^+^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^,^+^+^,^+^,^,^,^,^+^+^,^+^,^,^,^,^,^@ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^ Z Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^+^,^,^,^+^+^+^+^+^+^ Z Z Z Z@,^,^,^,^,^,^,^,^,^L^L^L^L^,^,^,^,^,^L^,^,^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^,^+^+^+^+^+^+^,^+^+^,^,^,^,^@L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^+^+^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^@,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@ Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^+^+^+^,^+^+^+^+^ Z Z Z Z Z@,^,^,^,^,^,^L^L^L^L^L^L^L^,^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^L^,^L^,^,^,^,^,^@+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^+^+^+^ Z+^+^+^+^+^ Z Z+^+^+^+^,^@,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^+^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^@+^+^,^,^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^ Z+^+^ Z+^+^ Z Z Z Z Z Z+^@ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^ Z Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^+^,^,^,^+^+^+^+^+^+^ Z Z Z Z@,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^+^,^+^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^,^@L^L^L^,^,^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^@,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^L^,^L^L^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^L^,^L^L^L^,^,^,^,^,^,^,^,^,^,^+^+^,^,^+^+^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^+^,^+^+^+^ Z+^ Z Z+^ Z+^+^+^+^+^+^+^,^+^,^,^,^,^,^,^,^@L^L^L^L^L^L^L^,^,^,^,^+^+^,^+^+^+^,^+^+^+^+^ Z Z Z Z+^+^+^+^,^,^,^,^+^,^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^IMG@h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= IMGf  IMG@@h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=@ IMGh=  MENUTEXT CheckbuttonTEXTIMG1IMG6   E=9AJJA9G1 Ai5(-='1'-&-) Ah5i9H1('1('1) E-'1$H5'1(- AH5h5)H5=9i9 =-'-i9H5'1i9- AH59G1)='-- J$9-=9-( h5% $  IMG6    $ %h5 (-9$J -h=,^h=H5A h=ywf,^h== ,^f,^,^h=A h=,^,^h=h=E )'1h=h=h=h5A )&-'-(i5A G19AJJA9=E IMG6    $ %h5 (-9=-9$J -'-=)G19H5A -i9'1H5i9'--= i99=H5)h5H5A -('1H5$'1-E )'1('1(H1i9h5A )&-'-'1=-(i5A G19AJJA9=E MENUTEXT PosbuttonTEXTIMG1IMG6   qVJAA/NJ=/Ni9 N-H5i9i9 =ywfi9( NywywywfH5 0Nywywywywyw$ EywywywfH5$ IywfA() A===H5) -)$%  IMG6   ) )))i9 $'1-h9(A ywfi9h5J )ywywywf=E ywywywywyw/N ywywywf(E ywf-=J '1=='1/N $/N/N=0NE/N0NQR IMG6   i9)-=h5h5(A&1 =-'1()9 =,^h=)=i9 A,^,^,^h='19 h9,^,^,^,^,^( =,^,^,^h=&-'1 9,^h=9'1( (9-&-='1 h5i9G1h5)($H5H5 MENUTEXT ScrollbarTEXTIMG1IMG6   rVAJAJEJ='1 J=9h9= J==fyw AH5fywywyw$ 0Nywywywywyw AH5fywywyw$ EH59fyw) A-'1-'1 ( ) %   IMG6   ) $$ '1 (((G1A i9'1fywA -fywywywE $ywywywywywJ =fywywyw= '1G1fyw/N -=((/N =NEJ==JE0N IMG6   h9i9G1=='1'1-- H5-i9i9(H5 H5&-=h=,^= -=h=,^,^,^= =,^,^,^,^,^'1 '1'1h=,^,^,^( '1h5=h=,^i9 9)i9(H5&- =(&-(=-i9(i9 IMG6   qVJAA/NJ=/Ni9 N-H5i9i9 =ywfi9( NywywywfH5 0Nywywywywyw$ EywywywfH5$ IywfA() A===H5) -)$%  IMG6   ) )))i9 $'1-h9(A ywfi9h5J )ywywywf=E ywywywywyw/N ywywywf(E ywf-=J '1=='1/N $/N/N=0NE/N0NQR IMG6   i9)-=h5h5(A&1 =-'1()9 =,^h=)=i9 A,^,^,^h='19 h9,^,^,^,^,^( =,^,^,^h=&-'1 9,^h=9'1( (9-&-='1 h5i9G1h5)($H5H5 IMG  h=h=h=h=h=h=h=h=h=h= h= h= h=QN9J1 h=9$$$$$ h=J1c h= h= h=ywywywywywywywywyw h=ffffffffffIMG@ @ @h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@ywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywyw@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMG  h= h=h=h=h=h=h=h=h=h=h=f ywfywfQN9J1ywf$$$$9$ywfJ1cywfywf ff ywywywywywywywywfff fffffffffffIMG E=9Ai5(Ah5i9E-'1AH5h5=-'-AH59J$9h5%IMG@ @@AJJEJEEJJA/NAE/N0NPREAA/NJJJAPREAEJJEEIJPRAAEEPRPREPRPREJ/N/NNAJAJJJEEEPR/NAE=9@-='1H5H5H5=)=('1=H5AH5H5H5-G5(&-G1'1==i9='1h9i9i9='1&-h5H5)9'1G1H5i9'1(G1=9=G1-H5(i9G1===9(G1='1h5$@H1('1=h9&-h9-===-=i9H5=(9i9h9i9G1'1='1i9'1H5H5G1G1i9--'1H5(&1i9H5(-G1(-'1(9&-H5-&1-&1=&-=-&-=i9(i9-@$H5'1(-(A=$i9((=&-h9-H5=(i9H5)(=-=)'1-i9(==(-(i9G5h59h9===G5=G5(&-9&1&-i9(i9'1-='1-h9$-)@)H5=G5&-(=-9i9=H5&-=h9i9G5h5'1H5)=i9&-'1-==-(-H59H5=((=9'1(=&-G1(G5i9=h5-9(9('1=&-H5-'1i9i9H5i9@i9H5'1'1-G5=i9G1(9G1(H5H59H5-9i9='1G1H5'1--=h5)(H5=-'1-)9)=G1h5H59(i9G5h5=G1$(-'19=i9h9i9=&1H5)-@G1)=H5&-=-=(9('1'1h5='1'19H5i9&-h5h5G1(&1i9H5-&1&-i9=H5'1(G59(H5G5===&-9(H5G1-i9=-='1h5G1H5=G199=(@-=9='1=)9h9&-(H5=-G1h9'1'1=='1=-)(--h5-=)h5=G5=i9-((-=--G1-=i9--=-G1(-h5G1&1i9=-i9)-H1@ $  ))%$ $  )) )) )$)))) )$$) $$ $$@IMG A9G1'-&-)('1)(- 9i9i9-'---($ IMG6   0N=JAE/N=A'1 Ah5'1G1='1h9$ J-=yw=H5 AH5fywf'1$ PRH5ywywyw= Jfywywywf) Aywywywywyw E$ - $$ ) IMG6   h9i9'1='1G1H5-h5 (==H5'1'1== G1H5i9,^h5(i9 h9i9h=,^h=H5) &--,^,^,^(- i9h=,^,^,^h=&- =,^,^,^,^,^= -'1 )=G1&-=G1=)i9 IMG6    )  h5 -(H5i9H5h5/N '1H5ywH5(E -fywfh5I ))ywywyw(E $fywywywfJ ywywywywywE )= H5J=AJJA/NQR IMG6   QRAJ/NEAE0N- E$ 0Nywywywywyw /Nfywywywf PR(ywywyw( Ai9fywfi9$ /N&-'1ywh9&- A'1=h5h9'1= H5)$)  IMG6   ==H5i9'1=9-H5 )H5 (,^,^,^,^,^= -h=,^,^,^h=&1 (H5,^,^,^=- H5'1h=,^h==( H5(i9,^)(( (9H5H5=)&-H5 h9i9(((H5)-H5 IMG6   ) $$ - 0N )ywywywywyw/N )fywywywf/N )h5ywywyw'1= h5fywf=A -h5yw-=N =H5==-(E i9AAPRN/NPR=qR IMG@  h= h=h=h=h=h=h=h=h=h=f h=ywf h=ywf h=QN9J1ywf h=9$ywf h=J1cywf h=$ywf h=$ywf h=$ywf h=$ywfIMG  @ h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywf h=$ywfIMG@   h=$ywf h=$ywf h=$ywf h=$ywf h=QN9J1ywf h=9$ywf h=J1cywf h=ywf h=ff h=ywywywywywywfff h=ffffffffffIMGv  EAAEA=AJh5 =i5h5-H5-H5$% 9(i9'1h5'-99IMG  @ 9$-)i9-(H1$ =h5i9-H5)=- E'1($i9H59)$ A=i9h9i9&19i9 /NG1=-'1=G1- PR(&-'1-i9==$ E9-=H5h9H5i9 E==-&-i9G1&1$ E=&-'1==h5G1 J==i9'19'1h5 JG1&1(('1=- Ji9-i99--( A(&1&-((=G1 JH5-&19$i9-) A-H59-G1-= NG1&-&-h5=G1- /N=9(=h5H5-$ /N9(G5i9G5(i9$ J='1=G5i99=) EG1-G5((&-- PR((=G19=G1) PR'1G1=&-H5=- Ei9-==h5=- PRH5(h9(G1G5=) PRG1H59'1=H5- E'1i9h59)(() E9&1G5=99( A)(i9()G5-) AH5H5((-(i9$ PRh5'1-='1'1=) J&--(H5-H5G5 I'1-=9===) E=i9=H5H5i9h5 Ei9G1(-(&-) Ji9G1i9()&1=) Jh9H5--h5-- E'1H5'1==H5h5) A='1)=-i9-) Ei9i9=--&1- PR='1-'1'1(( A===&-H5G1) J'1'1(i9G1h5-$ JG1G1)='1h5= J&-i9H5)=&-'1 /N(h9i9H5i9i9= AG5i9('19H5=$ A-9=h5-9'1 EH5(H5G5H5'1'1% PRH5=-i99'1h9 0NH5H5h9h9H5=G1 /NAi9&-=H5h5-) EH5==&-('1=) A=-(H5G1'1H5 /N'1=(=9(( A(=i9i9(9&- J==$9G1(h9 J)-=-i9=9 E=h9A==-) EH5&-((G5== JH5h9-&--&-'1$ EH5=(G5'1H5= J'1'1'1='1=9 J=(H5H5H5)= A-H1$)i9G1- IMGv  A'-((9i9'--$ 9&-'1-i9--( G1))  MENUTEXTGadgetTEXTIMG1  IMG  ^fOZjAnfOZjI AnNV RnIh=bjjAOZ6wb-ROZNV^OZ p^jEh=jfp^jMAjfIMA-RIMG  OZjfn-R bo RIop^I Rp^fNVMf Rp^OZbjI Rj5sMNVIMGz  MfjjjjjMAf6wVwjEh=^fAh=EIMG  N-RbMo^-RoW{p^h=jop^h=p^OZAOZAIMG  NOZp^OZo-RNV5s6wOZNVjnA-Rp^E NMAIMG  MM p^p^h=p^ oVwh=EEEo VwVwVwoVwVwoVwB oEh=h=Eh=o Mh=h=Eh=h=IMG  B Mp^oVwoEh=h=Eoh=h=EVwEh=oh=p^VwVwEh= Mp^oVwoMh= VwIMG       IMG p^p^oo op^oVw oMp^VwVwMoVwMoop^Vwp^h=p^ p^Mh=M p^p^h=MMh=h=h=MENUTEXTDividerTEXTIMG1IMG IMG@@h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=@ @ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMG simutrans-124.5/simutrans/themes/menu.pak192comicxxl.pak000066400000000000000000002401441520675460500233210ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1$$IMGX J='-'-9H1EFA99JJIMG@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@J9E9A=EJ9AA=AJA99=9==9AAAA99JE9EJA999J9AJ=59JA9A9E59EA9A9=E999JJIMG@H5$9(IMGZ  H1Ei5H5(-)( i9= G1)( i9(1 $'1$&- )i5 $( '19$'1-  '1A G1)- '1 i9A$($ =H1)($-'1 )=$H5H5$ )A-'1'1 -9$ G5H5$ '19(G5-$$ H1A&-'1(-&- -'1 $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1Aabbb)Ab$&-$((bb A'1@ @ $$A '1H5 b$b bIMGX bAA abbab@ IMG@@bA aAAbA bAAabA bbA A bb@ @ A bbA bA a@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMG@abbIMGX bbbbaA bIMG@@@@ babAbA A bbabbaAAbAA bbAA AbbbbAAbA@ baA bbabaAbb@aA bA bbA @ @ bbA A bbA baAAbA bAAaA bIMG@bAbIMGZ  aA'1)( bbh=,^h= bh=ywf,^h= b,^f,^,^h= h=,^,^h=h= h=h=h='1 bb($ A $-'1 @ @ '1H5H5$ A$$'1'1 '1 G5H5$ b H5-$$ b$(-&- b $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMGH5i5EH1G1 =i9 (1i9 i5)'1$9'1G1 A'1$Ai9()H1=$=)-A)$9-G5(9'1'1&-AH1 '1- IMGX (99E9J$H59i5'-'-IMG@@JJ999E=9A9AE95E9A9AJ95=JA9J999AJE9EJ99AAAA9==9=99AJA=AA9JE=A9E9J@H19'-'-9H1i5j9A'-==i5=AA9='-i5'-H1A'1H1'-i5'-j9H1i5=i5H5AH1H1=H1H19'-9AH1H1'-9A9H19'-9=H1Ai5=H19i5'-'-@IMG@AFE=JIMGX J='-'-9H1EFA-'1IMG@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1IMG@H5$9(IMGZ  H1'1i5H5(-)( i9&- G1)( i9( $'1$&- ) $( '1$'1-  '1 G1)- '1 i9)$($ =$)($-'1 ) $H5H5$ )$-'1'1 - $ G5H5$ '1 (G5-$$ H1 &-'1(-&- i5'1 $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1a$b b)'1b$&-$$'1((b (A'1$@ $$&- '1)H5 $ '1bIMGX bAG1$-bbab@ IMG@@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMG@abbIMGX J='-'-9H1EFA-'1IMG@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1IMG@H5$9(IMGZ  H1'1i5H5(-)( i9&- G1)( i9( $'1$&- ) $( '1$'1-  '1 G1)- '1 i9)$($ =$)($-'1 ) $H5H5$ )$-'1'1 - $ G5H5$ '1 (G5-$$ H1 &-'1(-&- i5'1 $(IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1a$b b)'1b$&-$$'1((b (A'1$@ $$&- '1)H5 $ '1bIMGX bAG1$-bbab@ IMG@@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMG@abbMENUTEXT RoundbuttonTEXTIMG1IMGPJ=EFA=0NqREIMG"@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@J9E9A=EJ9AA=AJA99=9==9AAAA99JE9EJA999J9AJ=59JA9A9E59EA9A9=E999JJ@'1-$ - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ (-H5-$$ '1&1$-( (&-(IMGPH5$9( abIMGH1Ei5H5i9= G1i9(1 )i5 '19$'1'1A G1i9A$=H1)()=$)A--9$'19(G5H1A&-'1 -'1 IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1Aabbb)Ab$&-$((bb A'1@ @ $$A '1H5 b$b bIMGP$ abAbIMG"@@-$G1&-G1$H5'1$(H5$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)G1$&-G1-'1@bA aAAbA bAAabA bbA A bb@ @ A bbA bA a@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMGP$bbabbIMGPbbabb$IMG"@@@@ babAbA A bbabbaAAbAA bbAA AbbbbAAbA@ baA bbabaAbb@aA bA bbA @ @ bbA A bbA baAAbA bAAaA b@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-IMGPbAba $IMGaA'1 bbbbA)&-$$bb((A @ @ '1A$$'1 b H5b$b IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMGH5i5EH1G1 =i9 (1i9 i5)'1$9'1G1 A'1$Ai9()H1=$=)-A)$9-G5(9'1'1&-AH1 '1- IMGPba (9$H5IMG"@@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1@JJ999E=9A9AE95E9A9AJ95=JA9J999AJE9EJ99AAAA9==9=99AJA=AA9JE=A9E9J@H19'-'-9H1i5j9A'-==i5=AA9='-i5'-H1A'1H1'-i5'-j9H1i5=i5H5AH1H1=H1H19'-9AH1H1'-9A9H19'-9=H1Ai5=H19i5'-'-@IMGPEqR0N=AFE=JIMGPJ=EFA=0NqREIMG"@@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@(&-( (-$&1'1 $$-H5-( $'1G1'1((G5H5-H5)($--$$ &-$($&-)$  H5'1 G1 - $-'1@'1-$ - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ (-H5-$$ '1&1$-( (&-(IMGPH5$9( abIMGH1'1i5H5i9&- G1i9( ) '1$'1'1 G1i9)$=$)() $)$-- $'1 (G5H1 &-'1 i5'1 IMG^@@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(IMG '1a$b b)'1b$&-$$'1((b (A'1$@ $$&- '1)H5 $ '1bIMGP$ abAbIMG"@@-$G1&-G1$H5'1$(H5$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)G1$&-G1-'1@'1-G1&-$G1)$$$$)$-'1 H5( $- G5$()) ( --'1 -$H5($'1H5$G1&-G1$-@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMGP$bbabbMENUTEXT EditfieldTEXTIMG1  IMG  h=IMG@@ @h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=IMG  fIMG h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h=IMG @@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^IMGywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfIMG h=h=fIMG@@ywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywyw@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMGffffMENUTEXTListboxTEXTIMG1  IMG  h=IMG@@ @h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=IMG  fIMG h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h=IMG @@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMGywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfIMG h=h=fIMG@@ywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywyw@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMGffffMENUTEXTBackTEXTIMG1  IMGffffIMG@@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff@ywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywIMGfh=h= IMG@fywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywfywIMG!@@@,^,^,^,^L^,^,^,^L^L^L^L^L^,^,^,^L^L^,^,^,^L^L^L^,^,^,^,^,^,^,^,^+^+^+^+^+^ Z+^+^+^+^+^ Z Z+^+^+^+^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^ Z+^+^ Z+^+^+^+^ Z Z Z+^+^ Z+^+^+^+^+^+^+^+^,^,^,^,^,^+^+^+^+^+^+^+^,^,^,^+^+^+^,^,^,^,^,^+^,^,^@+^,^+^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^+^+^+^+^+^+^+^ Z Z Z Z Z Z Z+^+^+^+^,^,^,^,^,^,^,^,^+^+^,^+^,^,^,^,^,^,^,^,^,^+^+^+^@+^+^+^+^+^,^+^+^+^ Z+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^L^L^L^,^,^L^L^L^,^,^,^,^,^,^,^,^+^+^+^@L^L^L^L^L^L^,^L^L^L^L^,^,^,^,^,^,^,^,^+^,^+^+^ Z+^ Z Z Z Z Z Z Z+^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^,^,^,^L^L^L^L^L^L^L^L^@,^+^+^+^,^+^+^+^+^ Z+^ Z+^ Z+^+^ Z+^+^+^+^+^+^+^+^+^,^,^,^,^+^,^+^+^,^,^,^,^,^+^+^,^+^,^,^,^,^+^+^,^+^,^,^,^,^,^,^+^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^ Z Z Z Z+^+^,^+^+^+^+^+^,^,^,^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^+^,^,^+^+^+^+^+^ Z Z+^+^+^+^+^+^+^+^+^+^+^,^+^,^,^,^,^,^,^,^,^,^,^@,^,^+^+^,^,^,^,^,^,^,^,^+^,^+^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^+^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@+^+^ Z+^+^,^,^,^+^+^+^+^+^+^+^ Z Z Z Z+^+^+^+^,^+^+^+^ Z Z Z Z Z+^+^+^+^,^,^,^,^,^+^+^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^@ Z+^+^+^+^,^,^+^+^+^+^+^+^+^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^ Z+^+^+^+^+^+^+^ Z Z Z Z Z Z Z Z@L^,^,^L^L^L^L^L^,^,^,^,^,^,^,^,^+^+^+^ Z+^ Z+^+^+^+^+^+^+^+^ Z+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^,^,^L^L^,^L^@,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^+^+^+^+^+^,^,^,^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^L^L^L^L^,^,^@,^+^+^+^+^,^,^,^,^+^+^+^,^,^,^,^,^,^,^,^,^,^L^L^L^L^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^ Z+^+^ Z+^+^+^+^+^+^+^+^+^+^,^+^,^,^@,^+^+^ Z+^+^+^ Z+^+^ Z+^+^+^+^+^+^+^,^,^,^+^+^+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^+^+^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^+^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^,^L^,^L^L^L^,^,^,^,^,^@L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^@+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^+^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^,^,^,^,^+^+^+^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^L^,^,^L^L^,^,^,^,^,^,^,^,^,^+^,^,^+^+^,^,^,^,^,^+^+^,^,^,^,^,^@,^,^,^,^,^,^,^,^+^+^+^+^ Z+^+^+^+^+^+^+^+^+^ Z Z+^+^,^+^+^+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^,^+^+^+^+^+^+^,^,^,^+^+^+^,^,^+^+^,^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^,^,^,^,^,^L^L^L^L^L^L^,^,^,^,^,^,^@,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^L^,^L^,^,^,^,^L^L^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^+^,^,^+^+^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^+^,^,^,^+^+^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^L^,^L^L^L^L^L^,^,^,^L^L^L^L^,^,^,^,^,^,^,^,^,^+^,^+^+^+^+^+^+^+^+^+^,^,^,^,^,^@,^,^,^,^,^+^+^+^+^+^+^+^+^,^+^+^+^ Z+^+^+^+^+^+^,^,^,^,^+^,^,^,^+^+^+^,^,^+^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^L^L^L^,^,^L^L^L^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^+^+^+^,^,^,^+^,^+^,^+^+^+^+^+^,^,^,^,^,^+^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^ Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^ Z Z Z Z+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^@+^+^+^+^+^+^ Z Z+^+^+^+^,^,^,^,^,^,^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^ Z+^+^,^,^,^+^@+^+^+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^,^,^,^,^+^@,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^+^ Z Z Z Z+^ Z Z Z Z+^+^+^+^ Z+^ Z+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@+^+^+^,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^+^+^+^+^+^+^+^ Z Z+^+^+^+^,^,^,^,^,^+^+^+^+^+^+^+^,^,^,^@L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^L^@,^,^,^+^+^ Z+^+^ Z+^+^ Z Z Z Z Z Z+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^,^L^,^,^,^,^,^,^,^,^,^,^,^,^@,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^+^+^+^,^,^,^+^,^+^,^+^+^+^+^+^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^+^+^+^,^+^,^,^,^+^+^+^+^,^,^,^,^+^+^+^,^,^,^,^,^,^,^,^,^,^,^@,^,^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^,^,^,^,^L^,^L^,^L^,^L^L^L^L^L^,^,^,^@,^,^,^,^,^,^+^+^+^+^,^+^,^,^,^,^,^,^,^+^+^,^,^+^+^+^+^+^+^+^+^ Z Z Z Z+^+^+^+^+^+^+^+^ Z Z+^+^+^+^,^,^,^,^,^,^,^,^+^+^,^+^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^ Z+^+^+^+^+^+^+^+^+^ Z+^+^+^+^+^+^+^+^,^,^,^,^,^+^+^+^+^+^+^+^,^,^,^+^+^+^,^,^,^,^,^+^,^,^,^,^,^@+^+^ Z+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^ Z Z Z Z Z Z Z Z Z Z+^+^+^+^ Z Z+^+^+^+^+^+^,^+^+^ Z+^+^+^@ Z Z Z Z+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^,^+^+^+^ Z@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^,^,^,^+^+^,^,^,^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^+^,^,^+^+^+^+^+^+^+^,^+^+^ Z+^+^+^+^+^ Z+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^@,^+^,^,^,^,^,^,^,^+^+^+^,^+^+^,^,^+^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^,^+^+^,^+^,^,^,^,^+^+^,^+^,^,^,^,^,^@ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^ Z Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^+^,^,^,^+^+^+^+^+^+^ Z Z Z Z@,^,^,^,^,^,^,^,^,^L^L^L^L^,^,^,^,^,^L^,^,^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^,^+^+^+^+^+^+^,^+^+^,^,^,^,^@L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^+^+^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^@,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^@ Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^+^+^+^,^+^+^+^+^ Z Z Z Z Z@,^,^,^,^,^,^L^L^L^L^L^L^L^,^L^L^L^L^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^L^,^L^,^,^,^,^,^@+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^+^+^+^ Z+^+^+^+^+^ Z Z+^+^+^+^,^@,^,^+^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^+^,^,^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^,^,^L^L^L^L^L^L^L^L^L^L^L^,^,^,^,^@+^+^,^,^+^+^+^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^ Z+^+^ Z+^+^ Z Z Z Z Z Z+^@ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^ Z Z Z Z Z Z Z Z Z Z Z Z+^+^+^+^+^+^+^+^+^+^,^,^,^+^+^+^+^+^+^ Z Z Z Z@,^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^+^+^+^+^+^+^,^+^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^,^@L^L^L^,^,^L^L^L^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^+^,^,^,^+^,^,^,^,^,^,^,^,^,^,^,^L^,^,^,^@,^,^,^,^,^+^,^,^,^,^,^,^,^,^,^L^,^L^L^,^,^,^,^,^,^,^,^,^,^L^,^,^,^,^,^,^,^,^,^,^,^,^L^,^L^L^L^,^,^,^,^,^,^,^,^,^,^+^+^,^,^+^+^,^@,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^,^+^,^,^+^,^+^+^+^ Z+^ Z Z+^ Z+^+^+^+^+^+^+^,^+^,^,^,^,^,^,^,^@L^L^L^L^L^L^L^,^,^,^,^+^+^,^+^+^+^,^+^+^+^+^ Z Z Z Z+^+^+^+^,^,^,^,^+^,^+^+^+^+^+^+^+^,^,^,^,^,^,^,^,^,^,^,^L^L^L^L^L^L^L^L^L^L^IMG@h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= h= IMGf  IMG@@h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=@ IMGh=  MENUTEXT CheckbuttonTEXTIMG1IMG&=(a ()(($A=H1i9$'1$&-$H5 $$h5i9)FPNQNE'1-$ -( (&-( AbAQRI5H5(G1 &1 (-)( -AaJ/N G1($$-('1))(bbJA G1H5&-'1&- $'1$&-b=E $G1 '1()$( )AbAJ$'1H5$H5'1-G5$- $&-APN G1 $$($)- '1$JQR$$$$H5$ H5H5($((bbJE)(&-(G1$ $-$-'1 A=0N$'1$ &1) H5H5$'1@ @ =PR-$$H5 $H5$'1'1$$A=J$G1$$(H5 G5H5$ '1AJ(G5 '1'1'1'1 G1-$$H5 bA0N&-'1(-$($ ('1(-&-$bi5Ai5 ($ -$'1 $( bH5$--$G1&-G1$&-G1-'1$bb AbA aAbA aabbbbbAbAbab@ IMG&bb@ @ bbA A A A AbA A AA bAA AA $Ab-R-R$ -( (-R-R(&-A-Rp^p^-R &1 (-Rp^j-R9-Rp^jp^-R('1)-Rp^jp^-RAA -Rp^jp^-R-Rp^jp^-R0NAAa -Rp^jp^-R-Rp^jp^-R)EAbb$'1-Rp^jNV-R-Rp^jp^-R$&-/NJb G1-Rp^jNVp^jp^-R'1$/JJ$$$-Rp^jjp^-R$((JAA)(&-(-Rp^jjp^-R-'1 PN=A$'1-Rp^jp^p^jp^-R$'1A9A--Rp^jOZ-R-Rp^jp^-R$$EJbb-Rp^jp^-R-Rp^jp^-R'1PREbb-Rp^jp^-R G1-Rp^jp^-RJJb-Rp^jp^-R$ ('1-Rp^jp^-REA -Rp^p^-R -$'1 -Rp^p^-REb-R-RG1&-G1$&-G1-R-R0N0Nb) '1($'1- $H5EJ $'1 (( $ ('1EIMG&($$(-)) )( H5 -( '1 G1H5&-'1&- ( G1'1-$ -( (&-( )( -H5(G1 &1 (-)( '1 $'1- G1($$-('1))(($'1 G1H5&-'1&- $'1$&-&-$ $G1 '1()$( ) ($$$'1H5$H5'1-G5$- $&--G5('1 G1 $$($)- '1$)'1'1H5$$$$H5$ H5H5($((((H5 )(&-(G1$ $-$-'1 '1  &-$'1$ &1) H5H5$'1 G1-$$H5 $H5$'1'1$$ '1$$$G1$$(H5 G5H5$ '1G1-(((G5 '1'1'1'1 G1-$$H5 $H5('1&-'1(-$($ ('1(-&-$$--G5 ($ -$'1 $( ()$'1(&1-$G1&-G1$&-G1-'1'1$$-$G5 )$-$ )( '1'1 ($G5($H5$)) $ ($(-G5MENUTEXT PosbuttonTEXTIMG1IMGffffywfff fywywywfff fywfywywywffffywfL^fywywywffffywfL^L^L^fywywywffffywfL^L^L^L^L^fywywywffffywfL^L^L^L^L^L^L^fywywywffffywfL^L^L^L^L^L^L^L^L^fywywywffffywfL^L^L^L^L^L^L^L^L^L^L^fywywywffh=fywfL^L^L^L^L^L^L^L^L^L^L^L^ Z Zh=h= fywfL^L^L^L^L^L^L^L^L^L^ Z Zh=h= fywfL^L^L^L^L^L^L^L^ Z Zh=h= fywfL^L^L^L^L^L^ Z Zh=h= fywfL^L^L^L^ Z Zh=h= fywfL^L^ Z Zh=h= ffL^ Z Zh=h= f Z Zh=h= L^h=h= h= IMG  h=h= h= Zh=h= h= Z Z Zh=h=  h= ZL^L^ Z Zh=h=  h= ZL^L^L^L^ Z Zh=h=  h= ZL^L^L^L^L^L^ Z Zh=h=  h= ZL^L^L^L^L^L^L^L^ Z Zh=h=  h= ZL^L^L^L^L^L^L^L^L^L^ Z Zh=h=  h= ZL^L^L^L^L^L^L^L^L^L^L^L^ Z Zh=h=  h= ZL^L^L^L^L^L^L^L^L^L^L^fywywywffh= h= ZL^L^L^L^L^L^L^L^L^fywywywfff h= ZL^L^L^L^L^L^L^fywywywfff h= ZL^L^L^L^L^fywywywfff h= ZL^L^L^fywywywfff h= ZL^fywywywfff h= Zywywywfff Zywywfffh=ywffffffIMGffffffff ffL^ffff ffL^L^L^ffffffL^L^L^L^L^ffffffL^L^L^L^L^L^L^ffffffL^L^L^L^L^L^L^L^L^ffffffL^L^L^L^L^L^L^L^L^L^L^ffffffL^L^L^L^L^L^L^L^L^L^L^L^L^ffffffL^L^L^L^L^L^L^L^L^L^L^L^L^L^L^ffL^L^ffL^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^L^h=h=ffL^L^L^L^L^L^L^L^L^L^L^L^L^h=h=h=h=ffL^L^L^L^L^L^L^L^L^L^L^h=h=h=h=ffL^L^L^L^L^L^L^L^L^h=h=h=h=ffL^L^L^L^L^L^L^h=h=h=h=ffL^L^L^L^L^h=h=h=h= ffL^L^L^h=h=h=h= ffL^h=h=h=h=fL^h=h=h=L^h=h=MENUTEXT ScrollbarTEXTIMG1IMG&J='-'-i59H1=H19'-'-9H1H5$EFAJ9E9A=E999JJ9(=0NqRE'1-$ -( (&-( abH1Ei5H5(G1 &1 (-'1Aai9= G1($$-('1)bbi9(1 G1H5&-'1&-b)i5 $G1)Ab'19$'1H5$&-'1A G1$i9A$(bb=H1)A)=$'1@ @ )A-$$A-9$G1$$'1'19(G5 '1'1'1'1  bH1A&-'1(-$($ ('1$b -'1 ($ -$'1 $b$ a-$G1&-G1$&-G1-'1$bbbAbA aAbA aabbbbbAbAbab@ IMG&bb@ @ bbA A A A AbA A AAA bAA AAAba$'1-$ -( (&-( a AaH5(G1 &1 (-'1$$ G1($$-('1)9'-A G1H5&-'1&-='-Aa $G1)H5'1bb$'1H5$&-=9b G1$9i9$(9'1A)A(A$'1'-$A-$$H1i9bb$G1$$'1AH5bb(G5 '1'1'1'1  i5=b&-'1(-$($ ('1$i9i5A G1 ($ -$'1 $=Ei5b$-$G1&-G1$&-G1-'1EqRJE)'-A=9E=9AA9=EEE0N/J'-H1i5Ai599H1i5H1i9i59'-APNIMG&($$(-)) )( H5 -( '1 G1H5&-'1&- ( G1'1-$ -( (&-( )( -H5(G1 &1 (-'1 $'1- G1($$-('1)BB($'1 G1H5&-'1&-BBBB&-$ $G1BBBBBB) ($$$'1H5$BBBBBBBB&--G5('1 G1BBBBBBBBBB$)'1'1H5$BBBBBBBBBBBB(((H5 )BBBBBBBBBBBB'1  &-$BBBBBBBBBB'1 G1-$BBBBBBBB$ '1$$$G1$$BBBBBB'1G1-(((G5 '1'1'1'1 BBBB $H5('1&-'1(-$($ ('1BB$$--G5 ($ -$'1 $()$'1(&1-$G1&-G1$&-G1-'1'1$$-$G5 )$-$ )( '1'1 ($G5($H5$)) $ ($(-G5IMG&J='-'-i59H1=H19'-'-9H1H5$EFAJ9E9A=E999JJ9(=0NqRE'1-$ -( (&-( abH1Ei5 &1 (-)( '1Aai9= ('1))(bbi9(1  $'1$&-b)i5 $( )Ab'19$ $&-'1A $i9A$(bb=H1)A)='1@ @ )A'1$$A-9 G5H5$ '1'19(G1-$$H5 bH1A&-$ ('1(-&-$b -'1$ -$'1 $( b$ a-$G1&-G1$&-G1-'1$bbbAbA aAbA aabbbbbAbAbab@ IMG&bb@ @ bbA A A A AbA A AAA bAA AAAba$'1-$ -( (&-( a Aa &1 (-)( '1$$ ('1))(9'-A  $'1$&-='-Aa $( )H5'1bb$ $&-=9b $9i9$(9'1A)A(A'1'-$A'1$$H1i9bb G5H5$ '1AH5bb(G1-$$H5 i5=b&-$ ('1(-&-$i9i5A G1$ -$'1 $( =Ei5b$-$G1&-G1$&-G1-'1EqRJE)'-A=9E=9AA9=EEE0N/J'-H1i5Ai599H1i5H1i9i59'-APNIMG&($$(-)) )( H5 -( '1 G1H5&-'1&- ( G1'1-$ -( (&-( )( - &1 (-)( '1 $'1- BB('1))(($'1 BBBB $'1$&-&-$ BBBBBB$( ) ($$$BBBBBBBB $&--G5('1 BBBBBBBBBB$)'1'1H5$BBBBBBBBBBBB(((H5 )BBBBBBBBBBBB'1  &-BBBBBBBBBB'1 G1BBBBBBBB'1$$ '1$$BBBBBB G5H5$ '1G1-(((BBBBG1-$$H5 $H5('1&-BB$ ('1(-&-$$--G5$ -$'1 $( ()$'1(&1-$G1&-G1$&-G1-'1'1$$-$G5 )$-$ )( '1'1 ($G5($H5$)) $ ($(-G5IMG   h=h=h=h=h=h=h=h= h= h= h= h= h= h= h= h=QN99J1 h=9$$$$$ h=9$$$$$ h=J1c h= h= fh=fh=ywfywfyw fywywywywywywywywffffffffIMG@@ @h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@@@@ywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywywyw@ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMG  h=h=h=h=h=h=h=h= h= h= fh= fh= ywf ywfywfQN99J1ywf$$$9$$ywf$$$9$$ywfJ1cywfywf ywf ywf ywf ywf ywfywf ywywywywywywywywfffffffffIMGRJ=EFA=0NqREH1Ei5H5i9= G1i9(1 )i5 '19$'1'1A G1i9A$=H1)()=$)A--9$'19(G5H1A&-'1 -'1 $ abAbIMG @@@'-'-i59H1=i5AH1=9'-9H19A9'-H1H1A9'-9H1H1=H1H1AH5i5=i5H1j9'-i5'-H1'1AH1'-i5'-=9AA=i5=='-Aj9i5H19'-'-9H1@J9E9A=EJ9AA=AJA99=9==9AAAA99JE9EJA999J9AJ=59JA9A9E59EA9A9=E999JJ@'1-$ - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ (-H5-$$ '1&1$-( (&-(@(G1 &1('1'1$( )( ($('1( '1 ($H5 --$) H5 -$(&-$-$G5$$ -G1G5( (-)(@($$-('1-$ &-$-- G1'1'1(- &1(&-($$ G1(- &-G5$$ ((H5'1 '1($$ &- '1))(@G1H5&-'1&-(G1'1$'1H5$-'1 &-$ G5 G1 ( &1)( '1$)$$H5$( $-(&-( -((( $&- $'1$&-@$G1 '1'1 -((( $$ H5 -G1'1H5)'1 $(-$-H5G1G5)$ - )'1'1)$)'1'1G1()$( @H5$H5'1-G5 '1$ -$((G1$)G1$G1H5 G5- '1'1 $$ ( '1($)G1&1H5 &1('1$$G1$- @ $$$$'1 -H5)$$$G1$-$($- H5 $ --)$(( &--G1&1( ))- ($)- '1@$$$H5$ -$( '1- -$$ )&- -'1-($) $$ &- H5G5&1- -&-'1$$H5$$$$ H5H5($@&-(G1$ $)&-&-($&- '1$ -$(H5 )(H5)$H5-'1H5$&-$(((-H5( G5-&-G1--$-'1@'1$ &1)$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)--G1&1 )&-) &1( H5H5$@$$H5 $  (-G1-- - G1 '1H5  $)&-$($&- $$--$()H5-H5G5(('1G1'1$ H5$'1'1@G1$$( (&-'1$$$('1&-&1 ()-$) &-($-G5$H5'1$$$$ (G5&-'1 G5$)G5-)H5 G5H5$@ '1'1'1'1  G5(- - '1 $'1$($ G5 $&-)G1H5&-$-'1'1($-$(G5 &--$G1-$$@(-$($ $ -- $)H5-&-&-'1H5$$ ($$((-$ )'1($H5'1)(&1$G1(-- ('1(-&-@($ - G1( G1-'1$'1()G1G5$$ $ )-$(G1'1(&---)($$))(H5 '1&-- ($$'1 $(@-$G1&-G1$H5'1$(H5$- '1-- ( ))($G5 -$ (H5 '1-$)$$$$)G1$&-G1-'1@bA aAAbA bAAabA bbA A bb@ @ A bbA bA a@bbAababbA ab@ AbAAbbbbAA AbbA AbAAabbabbA A bAbab@ @IMGRH5$9( ab '1Aabbb)Ab$&-$((bb A'1@ @ $$A '1H5 b$b b$bbabbIMG&J='-'-i59H1=H19'-'-9H1H5$EFAJ9E9A=E999JJ9(=0NqRE'1-$ -( (&-( abH1Ei5H5(G1 &1 (-)( '1Aai9= G1($$-)(bbi9(1 G1H5&-'1$'1$&-b)i5 $G1( )Ab'19$'1H5$H5 $&-'1A G1  '1$i9A$$$$((bb=H1)(&-'1 A)=$'1$'1@ @ )A-$$A-9$ '1'19( bH1A&-$b -'1b$ a-$G1&-G1$&-G1-'1$bbbAbA aAbA aabbbbbAbAbab@ IMG&($$(-)) )( H5 -( '1 G1H5&-'1&- ( G1'1-$ -( (&-( )( -H5(G1 &1 (-)( '1 $'1- G1($$-BB)(($'1 G1H5&-'1BB$'1$&-&-$ $G1BBBB( ) ($$$'1H5$H5BBBB $&--G5('1 G1 BBBBBB '1$)'1'1H5$$$BBBBBB$((((H5 )(&-BBBBBBBB'1 '1  &-$'1BBBBBBBB$'1 G1-BBBBBBBBBB$$ '1$$$BBBBBBBBBB '1G1-(((BBBBBBBBBBBB $H5('1&-BBBBBBBBBBBB$$--G5()$'1(&1-$G1&-G1$&-G1-'1'1$$-$G5 )$-$ )( '1'1 ($G5($H5$)) $ ($(-G5IMG&bb@ @ bbA A A A AbA A AAA bAA AAAba$'1-$ -( (&-( a AaH5(G1 &1 (-)( '1$$ G1($$-)(9'-A G1H5&-'1$'1$&-='-Aa $G1( )H5'1bb$'1H5$H5 $&-=9b G1  '1$9i9$$$$((9'1A)(&-'1 A(A$'1$'1'-$A-$$H1i9bb$ '1AH5bb( i5=b&-$i9i5A G1=Ei5b$-$G1&-G1$&-G1-'1EqRJE)'-A=9E=9AA9=EEE0N/J'-H1i5Ai599H1i5H1i9i59'-APNIMG&J='-'-i59H1=H19'-'-9H1H5$EFAJ9E9A=E999JJ9(=0NqRE'1-$ -( (&-( abH1Ei5'1Aai9= bbi9(1 b)i5 )Ab'19$'1$&-'1A G1'1$i9A$$((bb=H1)(&-(-'1 A)=$'1$'1@ @ )A-$$'1$$A-9$G1$$G5H5$ '1'19(G5 '1'1'1-$$H5 bH1A&-'1(-$((-&-$b -'1 ($ '1 $( b$ a-$G1&-G1$&-G1-'1$bbbAbA aAbA aabbbbbAbAbab@ IMG&($$(-)) )( H5 -( '1 G1H5&-'1&- ( G1'1-$ -( (&-( )( -'1 $'1- BBBBBBBBBBBB($'1 BBBBBBBBBBBB&-$ BBBBBBBBBB) ($$$'1BBBBBBBBBB$&--G5('1 G1BBBBBBBB'1$)'1'1H5$$BBBBBBBB((((H5 )(&-(BBBBBB-'1 '1  &-$'1BBBBBB$'1 G1-$$BBBB'1$$ '1$$$G1$$BBBBG5H5$ '1G1-(((G5 '1'1'1BB-$$H5 $H5('1&-'1(-$(BB(-&-$$--G5 ($ '1 $( ()$'1(&1-$G1&-G1$&-G1-'1'1$$-$G5 )$-$ )( '1'1 ($G5($H5$)) $ ($(-G5IMG&bb@ @ bbA A A A AbA A AAA bAA AAAba$'1-$ -( (&-( a Aa'1$$ 9'-A ='-Aa )H5'1bb$'1$&-=9b G1'1$9i9$$((9'1A)(&-(-'1 A(A$'1$'1'-$A-$$'1$$H1i9bb$G1$$G5H5$ '1AH5bb(G5 '1'1'1-$$H5 i5=b&-'1(-$((-&-$i9i5A G1 ($ '1 $( =Ei5b$-$G1&-G1$&-G1-'1EqRJE)'-A=9E=9AA9=EEE0N/J'-H1i5Ai599H1i5H1i9i59'-APNIMGl  h=h=f h=h=h=h=h=h=h=h=h=h=h=h=ffywywf h=ywf h=ywf h=QN99J1ywf h=9$$ywf h=9$$ywf h=J1cywf h=$$ywf h=$$ywf h=$$ywfIMG@ h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywf h=$$ywfIMGl  h=$$ywf h=$$ywf h=$$ywf h=QN99J1ywf h=9$$ywf h=9$$ywf h=J1cywf h=ywf h=ywf h=ffywywywywywywywywywywywywywf h=h=fffffffffffffIMGJ='-'-i59H1=H19'-'-9H1H5$EFAJ9E9A=E999JJ9(=0NqRE'1-$ -( (&-( abH1Ei5H5(G1 &1 (-)( '1AaIMG @i9= G1($$-('1))(bbi9(1 G1H5&-'1&- $'1$&-b)i5 $G1 '1()$( )Ab'19$'1H5$H5'1-G5$- $&-'1A G1 $$($)- '1$i9A$$$$H5$ H5H5($((bbH5= $ $)$'1-(-bi5=G1 -&--$ ($ -AG1 ( &-$'1'1G1 '1G1A A'-=$ G5&- ( $-'1 '1 b-H1H5(-('1('1$(- ($&-$'1i9='1 -$--&- H5-('1$(H5b@ '1= -$G1 '1$H5-bG19$ $--'1-)($- A(9G1) ( $ $(- )bbi9H1$-H5-'1- -$( '1G1( A 99('1-  ( $G1$ '1 A-AH5$&- &-- -$$$$&-'1 AA H1H5$'1&-'1&1 )$ )(($A A$i5-('1 G1)(G1 $-$)bA)i5 )H5$( ()G1H5 (&-bH19 G1$'1)'1$H5&-$$ G5&1'1$Ab'1='1G5$$-H5G5 G1- ((bA )=-$ $)-H5G1&- ()H1$() ('1- G1 ('1$A'1=-( H5- ($&-bA'-A $$ -)($G5'1 $ A G1i5 $&- $$$(H5&1 ($A A9A($( ( H5)-))G1$$AG19 $$ - $ '1((H5-)A))(G5-) $- --A ai9A)-- &-('1$ '1 '1 $b(A($G5$H5 '1$$ -)=$($$$H5&-H5 ()-(bH19G5G1 ( $ $&-$)A A -A'1)&-H5$'1&- $$-$G5)H5Ab=E ('1)'1&-$H5 $$H5 -A A $H1 &-G1 -- -$$H5H5b)H1--(H5$$$(G5-(H5($ G5A A i5=$-&-$$)(&1) G1 -(bAH5= )$$$-$(-G5$(A '1= (-$-$ $'1)$ (bH19 $'1$$--(($-(&-'1@ A)A($H5 $($ (($G1AA =AH5)'1((H5&- )-&-H5-'1b)i9)'1(G5))'1&-('1$$b@ h5= ($&-H5--G1 G5 A -='1H5)-'1--($G1&1 -'1$(bH1'- (H5G1$&1H5)($-A i5A-'1&1G5&1( '1($H5'-A$&-$$ ( H5 ($-@ H5'-)G1(G5()$'1($Ab-H1$(G5$&- &1) 9j9$- )'1)G5$)($&-$'-9$-&-G1 -$)'1) - A A -A$ --G5'1&1&-$-$'1$ G1'1b$9(-$G1$'1 G5&1A b$(1)$ $) (- G1G1&-'1($Ab=H1)(&-(G1$ $-$-'1 A)=$'1$ &1) H5H5$'1@ @ )A-$$H5 $H5$'1'1$$A-9$G1$$(H5 G5H5$ '1'19(G5 '1'1'1'1 G1-$$H5 bH1A&-'1(-$($ ('1(-&-$bIMG -'1 ($ -$'1 $( b$ a-$G1&-G1$&-G1-'1$bbbAbA aAbA aabbbbbAbAbab@ MENUTEXTGadgetTEXTIMG1  IMGv h=h=h=h=h=h=-R-R-R-Rh=h= h=h=-Rp^p^-R -Rp^j-Rh=h= h=h=-Rp^jp^-R -Rp^jp^-Rh=h=h=h=-Rp^jp^-R-Rp^jp^-Rh=h=h=h=-Rp^jp^-R-Rp^jp^-Rh=h=h=h=-Rp^jNV-R-Rp^jp^-Rh=h=h=h=-Rp^jNVp^jp^-Rh=h=h=h=-Rp^jjp^-Rh=h= h=-Rp^jjp^-Rh= -Rp^jp^p^jp^-R-Rp^jOZ-R-Rp^jp^-R-Rp^jp^-Rh=h=-Rp^jp^-R-Rp^jp^-Rh=h=h=h=-Rp^jp^-R-Rp^jp^-Rh=h=h=h=-Rp^jp^-R-Rp^jp^-Rh=h=h=h=-Rp^jp^-R h=-Rp^p^-Rh=h= h=h=-Rp^p^-Rh= h=h=-R-Rh=h= h=h=-R-Rh=h=h=h=h=h=h=h=h=h=h=h=h=h= IMG -Rp^p^p^p^p^p^p^p^p^-Rp^jjjjjjjjjjp^-R-Rp^jjjp^p^-R-R-Rp^jjjp^-Rp^jjjp^-Rh=h=h=h=-Rp^jjp^-R-Rp^jjp^ Rh=h=h=-Rp^jp^-Rh= h=-Rp^p^-Rh=h=h=-Rp^p^-Rh=h=h=-R-Rh=h=h=-Rp^-Rh=h=h=h=h=h=h=h=h=-Rp^-Rh=h=h=h=h=h=h=-Rp^-Rh=h= h=-Rp^ Rh=h=h= h=-Rp^-Rh=h=h= h=-Rp^p^-Rh=h=-Rp^p^-Rh=h=h=-Rp^p^-Rh=-Rp^p^-Rh=h=h=-Rp^p^ Rh=h=h= R Rh=h=h=h=h=h=p^p^-Rh=p^jjp^-Rh=p^jjp^-Rh=-Rp^p^-R-Rh=h=-R-R-Rh=h=h=h=h=h=IMG  h=-R-R-R-R-R-R-R-R-R-R-R-Rh=h=h=h=-R-Rjjjjjjjjp^p^-R-Rh=h=h=h=-Rjp^p^p^p^p^p^p^p^p^-R-R-Rh=h=h=h=-R-Rp^p^p^p^p^p^p^-R-R-Rh=h=h=h=-R-Rp^p^p^p^p^-R-R-Rh=h=h=h=-R-Rp^p^p^-R-R-Rh=h=h= h=-R-Rp^-R-R-Rh=h=h= h=-R-R-R-Rh=h=h=h=-R-Rh=h=h=h=h=h=h=h=h= IMG   h=h=h=-Rjh=h=-Rjp^-Rh=-Rjp^p^-R h=-Rjp^p^p^-R h=-Rjp^p^p^p^-R h=-Rjp^p^p^p^p^-R h=-Rjp^p^p^p^p^p^-R h=h=-R-Rp^p^p^p^p^p^-R h=h=h=-R-Rp^p^p^p^p^-R h=h=h=-R-Rp^p^p^p^-R h=h=h=-R-Rp^p^p^-R h=h=h=-R-Rp^p^-R h=h=h=-R-Rp^-Rh=h=h=-R-Rh=h=h=h=h=h=h=h=h=h=h=h= IMG  h=h=h=-R-Rh=-R-Rj-Rh=-Rjp^-R-Rh= -Rjp^p^-R-Rh= -Rjp^p^p^-R-Rh= -Rjp^p^p^p^-R-Rh= -Rjp^p^p^p^p^-R-Rh= -Rjp^p^p^p^-R-R-Rh=h= -Rjp^p^p^-R-R-Rh=h=h= -Rjp^p^-R-R-Rh=h=h= -Rp^p^-R-R-Rh=h=h= -Rp^-R-R-Rh=h=h= -R-R-R-Rh=h=h=h=-R-Rh=h=h=h=h=h=h=h=h=h=h=h=h=h=IMG  h= h= h=h=h=h=h=h= h=p^p^h=h=p^p^h= h=jjp^h=h=p^jjh= h=p^p^p^h=p^p^p^p^p^p^h=jp^p^h=h=p^p^p^-Rjjjjjjp^p^p^p^h=p^p^p^p^h=-Rp^p^-Rp^p^p^p^p^p^jp^p^-Rh=p^p^p^jjjjh=-R-R-R-R-R-R-R-R-R-Rjp^-R-Rh=Bh=-R-R-R-R-R-Rh=-R-R-Rh=-R-R-R-R-R-Rjp^-R-Rh=Bh=h=h=h=h=-R-R-Rh=-R-R-R-R-R-Rj-R-R-Rh=h=-R-Rh=h=h=h=h=h=h=h=-Rh=-R-Rh= h=-Rh=h=h=h=h=h=h=h=h=h=h=h=-Rh= h=h=h=h=h=h=h=h=h=h= h=h=h=h=h=h=h=h= h=h=h=h=h=h= h= h= IMGh=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=p^jp^p^-R-R-R-R-R-Rh=h=h=h=p^jp^p^p^-R-R-R-Rh=h=h=h=h=p^p^p^p^-R-R-Rh=h=h=h= h=h=-R-R-Rh=h=h=h=h= p^jp^-R-R-Rh=h= p^jp^-R-R-Rh=h= p^jp^-R-R-Rh=h= p^jp^-R-R-Rh=h= p^jp^-R-R-Rh=h= p^jp^-R-R-Rh=h= h=h=p^jjjj-Rh=h=h=p^jp^p^p^p^-Rh=h=h=h=h=p^jp^p^p^-R-R-R-Rh=h=h=h=h=p^jp^p^-R-R-R-R-R-Rh=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=BBp^j-Rh=p^j-Rh=p^j-Rh=p^j-Rh=p^-Rp^-Rp^h=IMGh=fh=-R-Rh=h=h=h=h=-R-R-Rh=h=ffff-Rh=-R-R-R-R-R-Rh= h=h=h=h=h=h=h=h= -R-R-R-R-R-R-Rh=h= ffffffff-Rh= -R-R-R-R-R-R-R-R-R-Rh= h=h=h=h=h=h=h=h=h=h=h=h=-R-R-R-R-R-R-R-R-R-R-Rh=h=ffffffffffff-Rh=-R-R-R-R-R-R-R-R-R-R-R-R-R-Rh=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=-R-R-R-R-R-R-R-R-R-R-R-R-R-R-Rh=h=ffffffffffffffff-Rh=-R-R-R-R-R-R-R-R-R-R-R-R-R-R-R-R-R-Rh=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=IMG  -R-R-R-R-R-R-R-R-Rjjjjjjjjj-R-R-Rjp^-Rh=h=p^p^p^j-R-R-Rj-Rh=-Rjj-Rp^p^j-R-Rjh=-Rjp^p^-Rp^p^-Rp^j-R-Rjh= -Rjp^-Rp^-Rp^j-R-Rjh=-Rjp^jp^-Rh=p^j-R-Rjp^-Rjp^jjp^-Rh=p^j-R-Rp^-R-Rp^-Rp^-Rh=p^jh=-Rp^-R-R-Rp^p^-Rh=-Rjh=-Rjp^h=-R-Rp^p^-R-Rh=-Rp^h=-Rjp^ h=-R-Rp^p^p^p^-R-Rh=-Rp^h=-Rjp^ h=h=-R-R-R-Rh=h=-Rp^h=-Rjp^h=h=h=h=-Rp^h=-Rjp^p^-Rp^p^h=-R-Rjp^p^p^-R-Rp^p^h=h=-R-Rp^p^p^p^p^p^p^p^h=h= h=h=h=h=h=h=h=h= MENUTEXTDividerTEXTIMG1IMG IMG@@h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=h=@ @ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffIMG simutrans-124.5/simutrans/themes/menu.pak64german.pak000066400000000000000000004550211520675460500226640ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT-MENUTEXTBackTEXTIMG1  IMG IMG IMG IMG @{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG!@@@ccccZccZZcZcZZZZVZVVZVVVVVVVVVZccc}k\g}k\g}k\g\g\g\g\g\g\g\g\gc\g\gc\gc\gcc\gc\gccc\g@cccccZcZcZZZZZZZVZZZVZVVVVVVcccccccc}k}k\g\g}k\g\g\g\g\g\g\g\g\gc\g\gc\gc\gccccccc@cZccZcZcZZcZZZZZZZZVZVVVZVcc\gc\gccccccZ}k\g\g\g\g\g\g\gc\gc\g\gc\g\gc\gc\gcc\gc\gc@cccccZcZcZZcZZZZZZVZVVZVcc\gccccccccccZcc}k\g\g\g\g\g\g\g\g\gc\gc\g\gc\gcc\gcccc@\gcZcZccZZcZZZZZZVZVZVZc\gc\gc\gcc\gccccccccZcZ\g\g\g\g\g\gc\g\gc\gc\gc\gc\gccccc@cccccZcZcZZcZZZZVZZV\gc\gc\gccc\gccccccccZccZcZc\g\g\g\g\g\gc\g\gc\gc\gccc\gc\gc@ccccZccZZcZZZZZZZZc\g\gc\gc\gc\gccc\gccccccccZcZcZZZ\g\g\gc\g\gc\gc\gc\gc\gcccc@cZcZcZcZcZZcZZZV\gc\gc\g\gc\gc\gc\gcccccccccccZcZcZZcZZ\g\g\gc\g\gcc\gccc\gcc\g@ccccccZcZcZZZV\g\g\g\g\gc\gc\gc\gccc\gc\gccccccZcZcZcZZZZZZZc\g\gc\g\gcc\gccccc@\gccZccZcZZZZ\g\g\gc\gc\g\g\gc\gc\gc\gcccccccccccccZcZcZcZZZZVZc\gc\gc\gc\gcccc@cccccZcZZZ\g\g\g\g\g\g\g\g\gc\gc\g\gc\gccc\gcccccccZcZcZcZZZZZZZZVZV\gc\gccc\gc\gc@ccccZcZc\g}k\g\g\g\g\g\g\gc\g\gc\gc\gcc\gc\gcc\gcccccccZcZZcZZZZZZZZVZZV\gc\gccccc@cccZcc\g\g\g\g\g\g\g\g\gc\g\g\gc\gc\gc\gc\gccccccccccZccZcZcZZZZZZVZZZVVZVcc\gccc@cccc\g\g}k\g\g\g\g\g\g\g\g\g\g\gc\g\gc\gc\gcc\gc\gcccccccccZcZcZZcZZZZZVZVZVVVVVcccc@cc\g}k}k\g}k\g}k\g\g\g\g\g\g\gc\g\gc\gc\gc\gc\gcccccccccccZcZcZcZZZZZZVZZVZVVVZVVVcc@}k}k}k\g\g}k\g\g\g\g\g\g\g\g\g\g\g\gc\g\gc\gc\gccc\gcc\gcccccZccZcZZcZZZZZZVZVZVZVVVVVVV@cc\g}k}k\g\g}k\g\g\g\g\g\gc\g\gc\gc\g\gc\gc\gc\gcccccccccccZcZZcZZZZZZVZVZZVVVVVVVcc@cccZ}k}k\g\g\g\g\g\g\g\g\g\gc\g\gc\gc\g\gcc\gcc\gcccccccZccZcZZcZZZZZVZZVZVVVVVcccc@cccccZ}k\g\g\g\g\g\g\g\g\g\g\gc\g\gc\gc\gccc\gccccccccccZcZcZZZZZZZZZZVVVZVcc\gc\gc@cccccccZ\g}k\g\g\g\g\gc\g\g\gc\gc\gc\gc\gcccc\gcccccZccZcZcZcZZZZVZVZZVc\gc\gcccc@cccccZcZcZ\g\g\g\g\g\g\g\gc\g\gc\gc\gccc\gccccccccccZcZZcZZZZZZZVZZc\gc\gccccc\g@ccccZccccZcZ\g\g\g\gc\g\gc\g\gcc\gc\gcccc\gcccccZccZcZZZcZZZZZV\gc\gc\gc\gcc\g\gc@\gccccccZcZZcZZ\g\g\g\gc\g\gc\g\gc\gcc\gccccccccccZcZcZcZZZZZc\g\g\gc\gc\gc\gcccc@ccccZcZccZZZcZZZ\g\g\gcc\gc\gcc\gccc\gccccccZccZcZZZZZZ\g\g\gc\gc\gc\gccc\gccc@ccccccccZcZcZZZZZV\g\g\gc\gc\gcc\gcccccccccccZcZcZZZ\g\gc\gc\gc\g\gc\gc\gccccc@ccccZcZccZZZcZZZZZVZ\gc\gc\gc\gc\gccccccccZcccZZZ\g\g\g\g\g\gc\gc\gc\gcc\gc\gc\gc@ccccccccZcZcZZZZZZZVZV\gc\gcccccc\gccccccZcZZ\g\g\g\g\g\gc\g\g\gc\gc\gc\gccc\gcc@\gccccZcccZZZcZZZZZVZVZVV\gc\gc\gZcccccccZcZ\g\g\g\g\g\g\gc\g\gc\g\gc\gc\gc\gcccc\g@cccZcccZcZcZZcZZZZZVZVZZVZccc\gcccccccZ}k\g}k\g\g\g\g\g\g\g\g\gc\g\gc\gc\gcc\gcccc@cccccZccZcZZcZZZZZZZZVZVVVVVcccccccZ}k\g\g\g\g\g\g\g\g\g\gc\g\g\gc\gc\gc\gcccc\gcc@cccccccZcZcZZZZZZZVZZVZVVVVVVVcccZ}k\g}k\g}k\g}k\g\g\g\g\g\g\gc\g\gc\gc\gc\gccc\gc\gc@ccccccccZcZZZZZZZZVZVVZVZVVVVVVZ\g}k}k\g}k\g}k\g\g\g\g\g\g\g\g\g\gc\gc\gc\gcccc\g\gccc@cccccZcZcZcZZcZZZZVZZZVZVVVVVVcccc}k}k\g}k\g}k\g\g\g\g\g\g\g\gc\gc\g\gc\gc\gc\gccc\gc@cccZccccZcZcZZZZZZVZZVZVVVVVc\gcccccc\g}k\g\g\g\g\g\g\g\g\g\g\g\g\gc\gc\gccccc\gccc@\gccccZcccZZZcZZZZZZZVVZVVVc\gcccccccccc}k\g\g\g\g\g\g\g\gc\g\g\gc\gc\gc\gcccccc\g@ccccccZcZcZcZZZZZZZVZZVZc\gc\gccc\gcccccZcc\g\g\g\g\g\g\g\g\g\gc\gc\g\gc\gc\gc\gccc@ccccZcccZcZZcZZZZZVZVV\gc\gcccc\gccccccccZccZ\g\g\g\g\gc\gc\g\g\gc\gcc\gcccc\gc@cccccccZcZZcZZZZZZZV\g\gc\gc\gc\gccccc\gcccZcZcZcZ\g\g\g\g\g\g\gcc\gc\g\gccc\gccc@cccccZccZcZZcZZZZZc\g\gc\gc\gccc\gccccccccccccZZcZZ\g\g\gc\g\g\gc\gc\gc\gcccc\g@ccccccZccZcZZZZZc\g\gc\gc\gc\gc\gcc\gccccccZccZcZcZZZZZ\g\g\gc\gc\gc\gcc\gcccc@cccccccZcZcZZZ\g\g\g\gc\gc\gc\gc\gcc\gcccccccccZccZZcZcZZZZc\g\gc\gc\gc\gccc\gc@\gccccZcccZZZ\g\g\g\gc\g\g\gc\gc\gcc\gcc\gcc\gcccZccZcZcZZZZZZZZV\gc\gcc\gcc\gccc@cccccccZZZ}k\g\g\g\g\g\g\gc\g\gc\gc\gcc\gccccccccccZccZZcZZZZZZZVZV\g\gcc\gccccc@cccccZcZ}k\g\g\g\g\gc\g\gc\g\gc\g\gc\gc\gc\gc\gcccccZcccZcZcZZZZZZZZZZVZc\gc\gcccc@cccccZ}k\g\g\g\g\g\g\g\g\g\g\gc\g\gc\gc\gcccccccccccccZccZZZZcZZZZVZVZVVVVcc\gc\gc@cccZ}k\g}k\g\g\g\g\g\g\g\g\gc\g\gc\gc\gc\gc\gc\gcc\gccccZccZcZccZZZZVZZVZVZVVVVVcccc@cc\g}k\g}k\g\g}k\g\g\g\g\g\gc\g\gc\g\gc\gc\gcccccccccccccccZcZZZcZZZZVZZVZVZVVVVVcc@}k}k}k\g}k\g}k\g\g\g\g\g\g\g\g\g\g\g\gc\gc\gc\gc\gc\gccccccccZcZcZZcZZZZZZZVZZZVVVVVVVVV@cc}k}k\g\g}k\g}k\g\g\g\g\g\g\gc\gc\g\gc\gc\gccccc\gccccccccZcZcZZcZZZZVZZVVVVVVVVVcc@cccZ}k}k\g\g\g\g\g\g\g\g\g\g\g\g\gc\gc\gc\gc\gc\gccccccccZcccZZcZZZZZZZZVZVZVVVV\gccc@cZcZcZ}k\g\g\g\g\g\g\g\g\gc\gc\g\gc\gc\gccccccccccccccZcZcZZcZZZZZVZVZVVV\gcc\g\gc@cccccZcc\g\g\g\g\g\gc\g\g\g\gc\gc\gc\gc\gc\gc\gcccccccZcZcZcZcZZZZVZZVZVc\gc\gcccc@cccZccZcZZ\g\g\g\g\g\gc\g\gc\gc\gc\gcccccccccccccccZcZZZZZZZZVZVV\gc\gc\gc\gccc@\gccccZcZcZZZ\g\g\g\g\g\gc\g\gc\gc\gc\gc\gcc\gcccccZcZcZZcZcZZZZZV\g\gc\g\gc\gcc\gc\g@cccZccZcZZcZZZ\g\gc\g\gc\gc\gc\gccccccccccccccZcZcZZZZZZZ\g\gc\g\gc\gc\gc\gccc@cccccZcZcZZZZZZZ\g\gc\g\g\gc\gc\gc\gccccccccccZccZcZcZZZc\gc\g\gc\gc\gc\gccc\gc@cZcZccZcZZcZZZZZZZc\gc\gc\gcc\gcc\gccccccZccZcZZZZZ\g\g\g\g\g\gc\g\gc\gc\gc\gccc@cccccZcZcZZZZZZZZVZV\gc\gc\gcc\gcccccccccccZcZcZ\g\g\g\g\g\gc\g\gc\gc\gc\gcc\gcc@\gccZccZcZcZcZZZVZZZVZV\gc\gccc\gcccccccZcZcZc\g\g\g\g\g\g\gc\g\gc\gc\gc\gc\gcccc@ccccZccZcZZZZZZZZVZZZVZV\gc\gccc\gccccccccc}k\g\g\g\g\g\g\g\g\g\g\gc\g\gc\gc\gcccc\g@cZcZcZcZZZcZZZZZVZVVVVVVVVcc\gccccccccc\g}k\g}k\g\g\g\g\g\g\g\g\gc\gc\g\gc\gc\gc\gcc@cccccZccZcZZcZZZZZZZZVZVVVVVcccccccZ}k\g\g\g\g\g\g\g\g\g\gc\g\g\gc\gc\gc\gcccc\gcc@cccccccZcZcZZZZZZZVZZVZVVVVVVVcccZ}k\g}k\g}k\g}k\g\g\g\g\g\g\gc\g\gc\gc\gc\gccc\gc\gc@ccccccccZcZZZZZZZZVZVVZVZVVVVVVZ\g}k}k\g}k\g}k\g\g\g\g\g\g\g\g\g\gc\gc\gc\gcccc\g\gcccIMG @5555555555555555555555555555555555555555555555555555555555555555IMG5IMG@@5555555555555555555555555555555555555555555555555555555555555555IMG5MENUTEXTButtonTEXTIMG1$$IMG6VVVVVVRRRRIMG"@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMG6VVRJVRJ1FRRJ1FBIMGzRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMG:@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRIMGzRRJ1FBRRJ1FBRRJ1FBRRJ1FBRRJ1FBRRJ1FBRRJ1FBRRJ1FBIMG6RRRRRJRJRJ1F1FBIMG"@@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBIMG6RJ1FBB1FBBBBBIMG61111151159IMG"@@1111111111111111111111111111111111111111111111111111111111111111@5555555555555555555555555555555555555555555555555555555555555555@9999999999999999999999999999999999999999999999999999999999999999@================================================================IMG6155999====IMGz159=159=159=159=159=159=159=159=IMG:@@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================@================================================================IMGz================================IMG6159=59B9BBIMG"@@================================================================@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBIMG6====BBBBBBIMG6^^^^^^^^^^IMG"@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^IMG6^^Z^ZZ^ZZVIMGz^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^IMG:@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^IMGz^ZZV^ZZV^ZZV^ZZV^ZZV^ZZV^ZZV^ZZVIMG6^^^^ZZZZZVIMG"@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVIMG6ZZVVZVVVVVIMG6IMG"@@@@@IMG6IMGzIMG:@@@@@@@@@IMGzIMG6IMG"@@@@@IMG6MENUTEXT CheckbuttonTEXTIMG1IMGZVRRRV ZVRsNsNRJRJRJ1FsN ZVRsNsNRJRJRJ1FBBRJZVRsNsNRJRJRJ1FBBB=1FVRsNsNRJRJRJ1FBBB=95ZRsNsNRJRJRJ1FBBB=955RVsNsNRJRJRJ1FBBB=95551FRsNRJRJRJ1FBBB=955519sNRJRJRJ1FBBB=9555111RRJRJ1FBBB=955511k-9VRJ1FBBB=955511k-J)1FZ1FBBB=955511k-J)J)VsNBB=955511k-J)J)9 RJ=955511k-J)J)9 1F55511k-J)J)9B519RJIMGV[uWsOsOSO5W v[WSsOsOrKQGQG0CG v[WSsOsOrKQGQG0C/?;Gv[WSsOsOrKQGQG0C/?;; 7>WSsOsOrKQGQG0C/?;; 72.6[SsOsOrKQGQG0C/?;; 72..NUWsOsOrKQGQG0C/?;; 72..*>TSsOrKQGQG0C/?;; 72..*&2sOrKQGQG0C/?;; 72..*&&&SOQGQG0C/?;; 72..*&&".4SQG0C/?;; 72..*&&">[0C/?;; 72..*&&"RG;; 72..*&&". G 72..*&&". >..*&&".J6*&2BIMGsNV^^R VccccccccR VccccccccccRVccccccccccccRccccccccccccccsNccccccccccccccsNRccccccccccccccVccccccccccccccV^cccccccccccccc^VccccccccccccccRccccccccccccccRJRccccccccccccccRRccccccccccccR RccccccccccR RccccccccRsNRZZRRJMENUTEXTDisplayFactoryLabelTEXTIMG1  IMG^^{^{ s{ s sIMG.@@@@@ s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s^@ s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s@ sIMGL s s^IMG@ s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s sIMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG@ s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s sIMG^^ s s s s^IMG.@@@ s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s@ s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s@@@IMG^ s s^ s s^MENUTEXTDisplayMarkerLabelTEXTIMG1  IMG^^ZkZk^ZkZkIMG.@@@@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk^@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@IMGLZkZk^IMG@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkIMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkIMG^^ZkZkZkZk^IMG.@@@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@ZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk@@@IMG^ZkZk^ZkZk^IMGv ))#   MENUTEXTDisplayStationLabelTEXTIMG1  IMGBIMG"@@@@@IMGBIMG@IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG@IMGBIMG"@@@@@IMGBMENUTEXTDisplayTextLabelTEXTIMG1  IMGBIMG"@@@@@IMGBIMG@IMG!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IMG@IMGBIMG"@@@@@IMGBMENUTEXTDividerTEXTIMG1IMG IMG@@5555555555555555555555555555555555555555555555555555555555555555@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG MENUTEXT EditfieldTEXTIMG1  IMG5IMG@@5555555555555555555555555555555555555555555555555555555555555555IMG55IMG55555555555555555555IMG @@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsN@sNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNsNIMG{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG{oIMG@@{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG{oMENUTEXTGadgetTEXTIMG1  IMGfwswswswswswswsgwswswswsgwswswsgwswswswsgwswswswsgwswswswsgwsws wswsgwswswswsgwswswswsgwswsgwswswswsggwswswswsggwswswswsgwswsgwsws wswsgwswswswsgwswswswsgwswswswsgwswswswsgwswswswsgwswswsgwswswswsgwswswswswswswsIMGP wswswswswswsws wswswswsgggwswswsws wsgggwswswswswsgggwswsgggwswsgggwswswswswswswsgggwswsggggwswsggggwswsggwswswswsgggwswsgggwswsgggwswswswswsws wswswswsgggwswsgggwswswswsIMGwswswswswswswswswswswswswswswswsggwswswswswswsggwsws wswsggwswswswsggwsws wswsggwswsggwswswswsgwswsgwswswswsggwswswsggwswswsIMG wswswswswswswsgwswswsggwswswsggwswswswsggwswswswsggwswswswswswsggwswswswswswswsggwswswswswsggwswswswsggwswswsgwswswswswswsIMGwswswswswswsgwswswsggwswswswsggwswswswswsggwswswswswswswsggwswswswswswsggwswswswsggwswswswsggwswswsggwswswsgwswswswswswswsIMG wswsws wswswsgws wswswswswswsggwswswswswswsggggggggws99999gggggggggggwswswswswswsggggggggws wswswswswswsggws wswswsgwswswswsIMG  wswswswswswswswsws wsgggggggws wswsgggggwswswswsgggwswswswsgggwswswsgggwswsgggwswsgggwswsgggwswsgwswsgwswsgwsws9wsws9ws999IMGT9 xsxs 999 xsxsxsxs 99999 xsxsxsxsxsxs9999999xsxsxsxsxsxsxsxs 999999999 xsxsxsxsxsxsxsxsxsxs 99999999999 xsxsxsxsxsxsxsxsxsxsxsxs 9999999999999xsxsxsxsxsxsxsxsxsxsxsxsxsxs999999999999999IMGx wswswswswswswsws wswsVVVVVVVVwswswsVVwswswswswswswswsVVwswsVwswswswsA99wswswswswsVwswsVwswswswsws9999wswswsVwswsVwswswsws999999wswswswsVwswsVwswswsws9999wswswswsVwswsVwswswswswswswswswswswswsVwswsVVwswswswswswswswsVVws wswsVVVVVVVVwswswswswswswswswswsMENUTEXTListboxTEXTIMG1  IMGIMG@@IMGIMGIMG @@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZIMG{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{o{oIMG9gIMG@@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@IMGMENUTEXT PosbuttonTEXTIMG1IMG: ^Z^ZZVVRVZ ZVRRsNsNRJ1FsNV VRRsNRJ1F1F1FB==1FRRsNsNRJ1F1FBB=995119RJsNRJRJ1F1FB==99511k-J)9RJ1F1F1FB==951111B 1F1F1FB=995115RJ 1FBB=9951=RB==9551F=999sN=BRIMG: J)=J)9sN !!)%J)J)1=RJ !)%)%J)J)k-1159=RJ!)%)%J)k-111599=B1FBRJ)%J)J)k-11559==B1F1F1F1FJ)J)k-11599==B1F1F1F k-k-11599=B1FBRJ 11559==B1FsN1599==B599=1F9=sNIMG: VsNRJZZZZRsNR ZZZZZZZVsNR ZZZZZZZZZZVRsNZZZZZZZZZZZZZZRsNZZZZZZZZZZZZZZZRZZZZZZZZZZZZZR ZZZZZZZZZZVsN ZZZZZZZZRRZZZZZZRZZZVsNVRRMENUTEXT RoundbuttonTEXTIMG1IMG6z:y:y:y:y:y:y:y:y:y:IMG"@@z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:z:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:IMG6z:y:86y:862y:8621IMGzy:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:IMG:@@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:@y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:y:IMGzX:8621X:8621X:8621X:8621X:8621X:8621X:8621X:8621IMG6X:X:X:X:868686221IMG"@@X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:@86868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686@2222222222222222222222222222222222222222222222222222222222222222@1111111111111111111111111111111111111111111111111111111111111111IMG686211211111IMG6%%%%%%%%%%IMG"@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%IMG6%%!%!!%!!tIMGz%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%IMG:@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%IMGz%!!t%!!t%!!t%!!t%!!t%!!t%!!t%!!tIMG6%%%%!!!!!tIMG"@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!@!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!@ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttIMG6!!tt!tttttIMG6>FR;!hY>B9ldhY>yB6!hlhFY>yBN3lllY>Y>yByB0!hlllY>Y>yByBV-llll"hFY>Y>yByBF*"hlllllX>Y>Y>yByByB(llllllX>Y>Y>yByByBN$F>CdllllLaFX>Y>Y>yByByBF"8:8:X>6B"hllh 8:X>Y>Y>yByByBF[WG yB8:8:X>Y>Y>F!hl 8:X>Y>Y>yByByBFJ wGU;S 868:8:X>Y>Y>yBBI]c F8:X>Y>Y>yByByBFJ 57U;V? y>868:8:X>Y>Y>yBBBN 8:8:X>Y>Y>yByByBFJRV?57U;V?yO <_6868:8:X>Y>Y>yBBBF <_8:8:X>Y>Y>yByByBFJJ4357U;V?WC X>6868:8:X>Y>Y>yBBBF yB8:8:X>Y>Y>yByByBFJJU74357U;V?WCxO <_26868:8:X>Y>Y>yBBBF 868:8:X>Y>Y>yByByBFJJN_3/4357U;V?WCWGyB26868:8:X>Y>Y>yBBBFJ <_868:8:X>Y>Y>yByByBFJJJT73/4357U;V?WCWGyS<_226868:8:X>Y>Y>yBBBFJ %yB868:8:X>Y>Y>yByByBFJJJNvG4357U;V?WCzWX>226868:8:X>Y>Y>yBBBFJ $6868:8:X>Y>Y>yByByBFJJJN435;U;WCK1226868:8:X>Y>Y>yBBBFJR #<[6868:8:X>Y>Y>yByByBFJJJN_ySW:1226868:8:X>Y>Y>yBBBFJN!Y>6868:8:X>Y>Y>yByByBFJJJN7:1226868:8:X>Y>Y>yBBBFJJ26868:8:X>Y>Y>yByByBFJJJ8:226868:8:X>Y>Y>yBBBFJJ<_26868:8:X>Y>Y>yByByBFJJX>26868:8:X>Y>Y>yBBBFJJNyB26868:8:X>Y>Y>yByByBFJX>6868:8:X>Y>Y>yBBBFJJJ226868:8:X>Y>Y>yByByBFX>868:8:X>Y>Y>yBBBFJJJ<_226868:8:X>Y>Y>yByBfff8:X>Y>Y>yBBBFJJJ3+T7_ySX>226868:8:X>Y>fMf+f b b,ffY>yBBBFJJJNvG3/3/U7"xOWGWGzW1226868:8:WB,f baaaa bfBBFJJJN434343434343V? &yOWCWCWCWCWCO1226868:f baYaaYaMfBFJJJN;W5;57575757575757wG )SV?V?V?V?V?V?V?WCW:NNNJJMf baaaaaa+bfJJJNRySU;U;U;U;U;U;U;U;U;WG )WGU;U;U;U;U;U;U;U;U;ySRNJJJMf baaaaaa+bfJJNNNW:WCV?V?V?V?V?V?V?S &wG575757575757575;57NJJJFof baYaaYaMf8:8:866221KWCWCWCWCWCyO "V?4343434343zSNJJJFBF,faaaaa bfX>8:8:866221zWWGWGxOU73/3/vGNJJJFBByBf,b b b b+bofY>Y>X>8:8:86622X>yS_T7NJJJFBByBY>Y>X>fofoffByBY>Y>X>8:8:86622<_JJJFBByBY>Y>X>8:8:86X>FBByBY>Y>X>8:8:86622JJJFBByBY>Y>X>8:8:866X>JFBByBY>Y>X>8:8:8662yBNJJFBByBY>Y>X>8:8:8662X>JJFBByBY>Y>X>8:8:8662<_JJFBByBY>Y>X>8:8:866228:JJJFBByBY>Y>X>8:8:8662!JJFBByBY>Y>X>8:8:8662217:NJJJFBByBY>Y>X>8:8:866X>#NJFBByBY>Y>X>8:8:866221W:yS57NJJJFBByBY>Y>X>8:8:866<_ $RJFBByBY>Y>X>8:8:866221KWCU;5;zSNJJJFBByBY>Y>X>8:8:866 %JFBByBY>Y>X>8:8:86622X>zWWCV?U;5743vGNJJJFBByBY>Y>X>8:8:86y> JFBByBY>Y>X>8:8:86622<_ySWGWCV?U;57433/T7JJJFBByBY>Y>X>8:8:86<_ JFBByBY>Y>X>8:8:8662yBWGWCV?U;57433/_ NJJFBByBY>Y>X>8:8:86 [FBByBY>Y>X>8:8:8662<_xOWCV?U;5743U7 JJFBByBY>Y>X>8:8:yB FBByBY>Y>X>8:8:866X>WCV?U;5743 NJFBByBY>Y>X>8:8:\_ FBByBY>Y>X>8:8:866<_yOV?U;57V? RJFBByBY>Y>X>8:8: NBByBY>Y>X>8:8:86y> V?U;57 JFBByBY>Y>X>8:F BByBY>Y>X>8:8:86<_ SU;wG JFBByBY>Y>X>8: BByBY>Y>X>8:8:yBWG [FBByBY>Y>X>8: FByBY>Y>X>8:8:\_!FBByBY>Y>X>FVByBY>Y>X>8:F$NBByBY>Y>X>ByBY>Y>X>8:(BByBY>Y>X>ByBY>Y>X>F*FByBY>Y>FNyBY>Y>X>-VByBY>Y>yBY>Y>F0ByBY>Y>yBY>Y>3NyBY>FBY>F6yBY>RY>9BY>F;RFIMG>>FR;FY>B9Y>RY>yB6FY>BFY>yBN3Y>Y>yBY>Y>yBB0FY>Y>yBY>Y>yBBV-X>Y>Y>yBNFY>Y>yBBF*FX>Y>Y>yByBX>Y>Y>yBBB(8:X>Y>Y>yByBX>Y>Y>yBBBN$F8:X>Y>Y>yByBVFX>Y>Y>yBBBF! <_8:8:X>Y>Y>yByBF 8:X>Y>Y>yBBBF[WG yB8:8:X>Y>Y>yByByB 8:X>Y>Y>yBBBFJ wGU;S <_868:8:X>Y>Y>yByByB F8:X>Y>Y>yBBBFJ 57U;V? yB868:8:X>Y>Y>yByByBN 8:8:X>Y>Y>yBBBFJRV?57U;V?yO <[6868:8:X>Y>Y>yByByBF \_8:8:X>Y>Y>yBBBFJN4357U;V?WC Y>6868:8:X>Y>Y>yByByBF yB8:8:X>Y>Y>yBBBFJJU74357U;V?WCxO<_26868:8:X>Y>Y>yByByBF[ 868:8:X>Y>Y>yBBBFJJN_3/4357U;V?WCWGyB26868:8:X>Y>Y>yByByBFJ <_868:8:X>Y>Y>yBBBFJJJT73/4357U;V?WCWGyS<_226868:8:X>Y>Y>yByByBFJ %y>868:8:X>Y>Y>yBBBFJJJNvG4357U;V?WCzWX>226868:8:X>Y>Y>yByByBFJ $6868:8:X>Y>Y>yBBBFJJJNzS5;U;WCO1226868:8:X>Y>Y>yByByBFJR #<_6868:8:X>Y>Y>yBBBFJJJN57ySW:1226868:8:X>Y>Y>yByByBFJJ!X>6868:8:X>Y>Y>yBBBFJJJNRN226868:8:X>Y>Y>yByByBFJJ26868:8:X>Y>Y>yBBBFJJJNN26868:8:X>Y>Y>yByByBFJJ<_26868:8:X>Y>Y>yBBBFJJJN6868:8:X>Y>Y>yByByBFJJNyB26868:8:X>Y>Y>yBBBFJJJ868:8:X>Y>Y>yByByBFJJJ226868:8:X>Y>Y>yBBBFJJ8:8:X>Y>Y>yByByBFJJJ<_226868:8:X>Y>Y>yBJofMfMffWBY>Y>yByByBFJJJNT7_ySX>226868:8:X>Y>f,f b b b b,ffyByByBFJJJNvG3/3/U7"xOWGWGzW1226868:8:X>,baaaaa bMfyByBFJJJN434343434343V? &yOWCWCWCWCWCK1226868:f baYaaYa+ffFJJJN_5;57575757575757wG )SV?V?V?V?V?V?V?WCW:122686of baaaaaa bfX>X>X>8:7:ySU;U;U;U;U;U;U;U;U;WG )WGU;U;U;U;U;U;U;U;U;yS7:8:X>X>X>of baaaaaa bf866221W:WCV?V?V?V?V?V?V?S &wG575757575757575;57NJJJFf+baYaaYa,f8:8:866221KWCWCWCWCWCyO "V?4343434343zSNJJJFBBof baaaa bfX>8:8:866221zWWGWGxOU73/3/vGNJJJFBByBY>fMf+b+bMffY>Y>X>8:8:86622X>yS_T7NJJJFBByBY>Y>X>8:ffBByBY>Y>X>8:8:86622<_JJJFBByBY>Y>X>8:8:JJFBByBY>Y>X>8:8:86622JJJFBByBY>Y>X>8:8:86JJJFBByBY>Y>X>8:8:8662yBNJJFBByBY>Y>X>8:8:866NJJJFBByBY>Y>X>8:8:8662<_JJFBByBY>Y>X>8:8:8662NNJJJFBByBY>Y>X>8:8:8662!JJFBByBY>Y>X>8:8:86622NRNJJJFBByBY>Y>X>8:8:866X>#NJFBByBY>Y>X>8:8:866221W:yS;WNJJJFBByBY>Y>X>8:8:866<_ $RJFBByBY>Y>X>8:8:866221KWCU;5;43NJJJFBByBY>Y>X>8:8:866 %JFBByBY>Y>X>8:8:86622X>zWWCV?U;5743vG3+JJJFBByBY>Y>X>8:8:86y> JFBByBY>Y>X>8:8:86622<_ySWGWCV?U;57433/T7 JJJFBByBY>Y>X>8:8:86 JFBByBY>Y>X>8:8:8662yBWGWCV?U;57433/_ NJJFBByBY>Y>X>8:8:86 [FBByBY>Y>X>8:8:8662<_xOWCV?U;5743U7 JJFBByBY>Y>X>8:8:yB FBByBY>Y>X>8:8:866X>WCV?U;5743 NJFBByBY>Y>X>8:8: FBByBY>Y>X>8:8:866<_yOV?U;57V? RJFBByBY>Y>X>8:8: NBByBY>Y>X>8:8:86y> V?U;57 JFBByBY>Y>X>8:F BByBY>Y>X>8:8:86<_ SU;wG JFBByBY>Y>X>> BByBY>Y>X>8:8:yBWGFBByBY>6BCdl FByBY>Y>X>8:8:\_!FBBF"hll"hVByBY>Y>X>8:F$NI!hllllByBY>Y>X>8:']cllllllByBY>Y>X>F*hllll!hNyBY>Y>X>-LallllyBY>Y>F0llllyBY>Y>3"hll!hBY>F6llRY>9hlF;dh!hIMG>>FR;FY>B9Y>RY>yB6FY>BFY>yBN3Y>Y>yBY>Y>yBB0FY>Y>yBY>Y>yBBV-X>Y>Y>yBNFY>Y>yBBF*FX>Y>Y>yBBX>Y>Y>yBBB(8:X>Y>Y>yBBX>Y>Y>yBBBN$F8:X>Y>Y>yBBVFX>Y>Y>yBBBF! \_8:8:X>Y>Y>yBBF 8:X>Y>Y>yBBBF[WG yB8:8:X>Y>Y>yBBB 8:X>Y>Y>yBBBFJ wGU;S <_868:8:X>Y>Y>yBBB F8:X>Y>Y>yBBBFJ 57U;V? y>868:8:X>Y>Y>yBBBN 8:8:X>Y>Y>yBBBFJRV?57U;V?yO <_6868:8:X>Y>Y>yBBBF \_8:8:X>Y>Y>yBBBFJN4357U;V?WC X>6868:8:X>Y>Y>yBBBF yB8:8:X>Y>Y>yBBBFJJU74357U;V?WCxO<_26868:8:X>Y>Y>yBBBF[ 868:8:X>Y>Y>yBBBFJJN_3/4357U;V?WCWGyB26868:8:X>Y>Y>yBBBFJ <_868:8:X>Y>Y>yBBBFJJJT73/4357U;V?WCWGyS<_226868:8:X>Y>Y>yBBBFJ %y>868:8:X>Y>Y>yBBBFJJJNvG4357U;V?WCzWX>226868:8:X>Y>Y>yBBBFJ $6868:8:X>Y>Y>yBBBFJJJNzS5;U;WCK1226868:8:X>Y>Y>yBBBFJR #<_6868:8:X>Y>Y>yBBBFJJJN57ySW:1226868:8:X>Y>Y>yBBBFJN!X>6868:8:X>Y>Y>yBBBFJJJN7:1226868:8:X>Y>Y>yBBBFJJ26868:8:X>Y>Y>yBBBFJJJ8:226868:8:X>Y>Y>yBBBFJJ<_26868:8:X>Y>Y>yBBBFJJX>26868:8:X>Y>Y>yBBBFJJNyB26868:8:X>Y>Y>yBBBFJX>6868:8:X>Y>Y>yBBBFJJJ226868:8:X>Y>Y>yBBBFX>868:8:X>Y>Y>yBBBFJJJ<_226868:8:X>Y>Y>yBBfofoffX>Y>Y>yBBBFJJJNT7_ySX>226868:8:X>Y>Y>of+b b b b,bfyBBBFJJJNvG3/3/U7"xOWGWGzW1226868:8:X>f baaaaa,fJBFJJJNzS4343434343V? &yOWCWCWCWCWCK1226868:8:MfaYaaYa bofFJJJN575;57575757575757wG )SV?V?V?V?V?V?V?WCW:NNNJJf+baaaaaa bMfJJJNRySU;U;U;U;U;U;U;U;U;WG )WGU;U;U;U;U;U;U;U;U;ySRNJJJf+baaaaaa bMfJJNNNW:WCV?V?V?V?V?V?V?S &wG575757575757575;;WNJJJFBMfaYaaYa bf8:866221OWCWCWCWCWCyO "V?434343434343NJJJFBBf baaaa b,fWB8:8:866221zWWGWGxOU73/3/vGNJJJFBByBY>f,f b b+fMffY>X>8:8:86622X>yS_T73+JJJFBByBY>Y>X>8:fffyByBY>Y>X>8:8:86622<_JJJFBByBY>Y>X>8:8:86X>FyByByBY>Y>X>8:8:86622JJJFBByBY>Y>X>8:8:866X>JFyByByBY>Y>X>8:8:8662yBNJJFBByBY>Y>X>8:8:8662X>JJFyByByBY>Y>X>8:8:8662<_JJFBByBY>Y>X>8:8:866228:JJJFyByByBY>Y>X>8:8:8662!JJFBByBY>Y>X>8:8:8662217:NJJJFyByByBY>Y>X>8:8:866Y>#NJFBByBY>Y>X>8:8:866221W:yS_NJJJFyByByBY>Y>X>8:8:866<[ $RJFBByBY>Y>X>8:8:866221KWCU;5;43NJJJFyByByBY>Y>X>8:8:866 %JFBByBY>Y>X>8:8:86622X>zWWCV?U;5743vGNJJJFyByByBY>Y>X>8:8:86yB JFBByBY>Y>X>8:8:86622<_ySWGWCV?U;57433/T7JJJFyByByBY>Y>X>8:8:86<_ JFBByBY>Y>X>8:8:8662yBWGWCV?U;57433/_ NJJFyByByBY>Y>X>8:8:86 FBByBY>Y>X>8:8:8662<_xOWCV?U;5743U7 JJFyByByBY>Y>X>8:8:yB FBByBY>Y>X>8:8:866X>WCV?U;5743 JJFyByByBY>Y>X>8:8:<_ FBByBY>Y>X>8:8:866<_yOV?U;57V? RJFyByByBY>Y>X>8:8: NBByBY>Y>X>8:8:86y> V?U;57 JFyByByBY>Y>X>8:F ]cIByBY>Y>X>8:8:86 SU;wG JFyByByBY>Y>X>8: l!hFY>Y>X>8:8:yBWG [FyByByBY>Y>X>8:hll"h6BX>8:8:"FyByByBY>Y>X>FLallllCd>F$NyByByBY>Y>X>llllll(yByByBY>Y>X>lllll"h*FyByBY>Y>F"hllll-VyByBY>Y>lll!h0yByBY>Y>lll3NyBY>Fhl!h6yBY>dhl9BY>!h;RFIMG>>!hdh;Flh9Y>Rll6FY>B!hll"h3Y>Y>yBllll0FY>Y>yBllllLa-X>Y>Y>yBN!hllllh*FX>Y>Y>yBBllllll]c'8:X>Y>Y>yBBllll!hIN$F8:X>Y>Y>yBBV"hll"hFBBF! \_8:8:X>Y>Y>yBBFlCd6BY>yBBBFWG yB8:8:X>Y>Y>yBBB >X>Y>Y>yBBBFJ wGU;S <_868:8:X>Y>Y>yBBB F8:X>Y>Y>yBBBFJ 57U;V? y>868:8:X>Y>Y>yBBBN 8:8:X>Y>Y>yBBBFJRV?57U;V?yO <_6868:8:X>Y>Y>yBBBF 8:8:X>Y>Y>yBBBFJN4357U;V?WC X>6868:8:X>Y>Y>yBBBF yB8:8:X>Y>Y>yBBBFJJU74357U;V?WCxO<_26868:8:X>Y>Y>yBBBF[ 868:8:X>Y>Y>yBBBFJJN_3/4357U;V?WCWGyB26868:8:X>Y>Y>yBBBFJ 868:8:X>Y>Y>yBBBFJJJT73/4357U;V?WCWGyS<_226868:8:X>Y>Y>yBBBFJ %y>868:8:X>Y>Y>yBBBFJJJ3+vG4357U;V?WCzWX>226868:8:X>Y>Y>yBBBFJ $6868:8:X>Y>Y>yBBBFJJJN435;U;WCK1226868:8:X>Y>Y>yBBBFJR #<_6868:8:X>Y>Y>yBBBFJJJN;WySW:1226868:8:X>Y>Y>yBBBFJN!X>6868:8:X>Y>Y>yBBBFJJJNRN226868:8:X>Y>Y>yBBBFJJ26868:8:X>Y>Y>yBBBFJJJNN26868:8:X>Y>Y>yBBBFJJ<_26868:8:X>Y>Y>yBBBFJJJN6868:8:X>Y>Y>yBBBFJJNyB26868:8:X>Y>Y>yBBBFJJJ868:8:X>Y>Y>yBBBFJJJ226868:8:X>Y>Y>yBBBFJJ8:8:X>Y>Y>yBBBFJJJ<_226868:8:X>Y>Y>yBBBff8:X>Y>Y>yBBBFJJJNT7_ySX>226868:8:X>Y>Y>fMf+b+bMffY>yBBBFJJJNvG3/3/U7"xOWGWGzW1226868:8:X>f baaaa bofBBFJJJNzS4343434343V? &yOWCWCWCWCWCK1226868:8:,faYaaYa+bRFJJJN575;57575757575757wG )SV?V?V?V?V?V?V?WCW:122686f baaaaaa bofX>X>X>8:7:ySU;U;U;U;U;U;U;U;U;WG )WGU;U;U;U;U;U;U;U;U;yS7:8:X>X>X>f baaaaaa bof866221W:WCV?V?V?V?V?V?V?S &wG575757575757575;_NJJJFf+faYaaYa bf8:866221KWCWCWCWCWCyO "V?434343434343NJJJFyByBMf baaaaa,bX>8:8:866221zWWGWGxOU73/3/vGNJJJFyByByBf,f b b b b,ffY>X>8:8:86622X>yS_T7NJJJFyByByBY>Y>X>fMfMfofRyBY>Y>X>8:8:86622<_JJJFyByByBY>Y>X>8:8:JJFBByBY>Y>X>8:8:86622JJJFyByByBY>Y>X>8:8:86JJJFBByBY>Y>X>8:8:8662yBNJJFyByByBY>Y>X>8:8:866NJJJFBByBY>Y>X>8:8:8662<_JJFyByByBY>Y>X>8:8:8662NNJJJFBByBY>Y>X>8:8:8662!JJFyByByBY>Y>X>8:8:86622NRNJJJFBByBY>Y>X>8:8:866X>#JJFyByByBY>Y>X>8:8:866221W:yS57NJJJFBByBY>Y>X>8:8:866<_ $RJFyByByBY>Y>X>8:8:866221OWCU;5;zSNJJJFBByBY>Y>X>8:8:866 %JFyByByBY>Y>X>8:8:86622X>zWWCV?U;5743vGNJJJFBByBY>Y>X>8:8:86y> JFyByByBY>Y>X>8:8:86622<_ySWGWCV?U;57433/T7JJJFBByBY>Y>X>8:8:86<_ JFyByByBY>Y>X>8:8:8662yBWGWCV?U;57433/_ NJJFBByBY>Y>X>8:8:86 [FyByByBY>Y>X>8:8:8662<_xOWCV?U;5743U7 JJFBByBY>Y>X>8:8:yB FyByByBY>Y>X>8:8:866Y>WCV?U;5743 NJFBByBY>Y>X>8:8:\_ FyByByBY>Y>X>8:8:866<[yOV?U;57V? RJFBByBY>Y>X>8:8: NyByByBY>Y>X>8:8:86yB V?U;57 JFBByBY>Y>X>8:F yByByBY>Y>X>8:8:86<_ SU;wG JFBByBY>Y>X>8: yByByBY>Y>X>8:8:yBWG [FBByBY>Y>X>8: FyByBY>Y>X>8:8:<_!FBByBY>Y>X>FVyByBY>Y>X>8:F$NBByBY>Y>X>yByBY>Y>X>8:(BByBY>Y>X>yByBY>Y>X>F*FByBY>Y>FNyBY>Y>X>-VByBY>Y>yBY>Y>F0ByBY>Y>yBY>Y>3NyBY>FBY>F6yBY>RY>9BY>F;RFSYMBTEXT CompassMapTEXTprissiIMG1IMGJ IMG            IMGJ IMG          IMG IMG IMG IMG SYMBTEXT ElectricityTEXTIMG1IMGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxSYMBTEXTFlagsTEXTIMG1IMG -0 55RJ55555 ^wRuNRJ*5*5*55 55)%)%"5R^R4R]]]!%5RwuNRJ5#)%5^^nuj]]]A4 )%)%5555RJRRRR^^RRJ55#)%5Rs^ujujCqCq]A4*55EU4RuZ^^s^^RJRJ)#)%RJ^w^]]CqCq]"Q!5 CqCq]]ujnwn^^R5$)%RJR^sne]CqCqCq]&A*5CqCqCq]Cqe]jww^R55$)%RJRJR^nCqCqCqCqCqCq&A&A]CqCqCqCq]]san^uNRJRJ$)%55R^nCqCqCqCqCqCq]H"Q]CqCqCqCqCqCqCqws^^5)%$)%55R^nCqCqCqCqCqCq]]!5]CqCqCqCqCqCqCqujsws^^5)%$)5R^nCqCqCqCqCqCqCq]HCqCqCqCqCqCqCqljujns^^5)%$)5R^nCqCqCqCqCqCqCq]b(HCqCqCqCqCqCqCqCqCqCqwuNRJ5()%5R^nljCqCqCqCqCq]&A b(b(b(&A&ACqCqCqCqCqCqwRRJRJ5)5R^nnlj]]]]&A)%)%)%]]CqCqCqCqw^uNRJ)%*5RJRw^R)%)%)%)%)%)%)%)%]CqCqCqCqs^uN5)%)%*!%5*5R^s)%)%)%*)%)%)%)%!%**5&Ab(b(b(RRJRJ5)% +5R^sb()%)%)%!%)%)%)%555)%!%,)%RJR^ws)%)%)%)%)%)%b(b(!%!%)%)%)%)%)%)%)%)%5,55RJ^ws)%)%)%)%*)%)%)%)%)%,55RJ^ws)%)%)%*5!5b()%)%)%)%)%!%-)%RJR^ws)%)%*5HHHA4A4A4!%)%)%)%)%)%)%5-)%5RJ^wsb(b(HHH]]HHA4A4b(b()%)%)%)%)%)%!%5-)%5RJ^wsA4H]]]Cq]HHHA4A4b()%)%)%)%)%)%!%b(5-)RJ^wsb(H]CqCqCqCqCq]HHA4A4b(b(!%b(b()%)%)%)%)%)%)%A4A4HHA4A4A4A45-5RJ^^RJb(A4HH]]CqCqCq]HHHA4A4A4b(A4A4HA4A4"Q&AA4HHHHHA4A4A4A4-5RJRRRJb(b(A4HHH]CqCqCq]]HHA4A4A4b(A4A4HHH]HH]]HHHA4A4A4A4-)%5)%RJRJb(A4A4HHH]CqCqCqCqCq]HA4A4A4A4A4H]CqCqCqCqCqCq]HHHA4A4A4)%)%&)%)%b(A4A4HHH]CqCqCq]]CqCq]HA4b(A4A4H]]CqCqCqCqCq]HHHA4A4A4$b(A4A4HHH]CqCqCqCq]uuu]A4b(A4A4HH]CqCqCqCqCq]HHHA4A4A4$b(A4A4A4A4H]Cque{e{e{e{e{ZCje!%A4A4H]CqCqCqCqCqCq]HHHA4HH$b(A4HH]eCje{e{e{e{e{e{e{Cju"A&A&A]]]CqCqCqCqCqCqCq]]"Q]H$A4H]ejDke{e{e{e{e{e{e{jue&A)%ee]CqCqCqCqCqCqueeee"Q$A4]uue{e{e{e{e{e{e{e{ue{e{u!5ZCjuuuuuuue{juuue$]uuuCje{e{e{e{e{e{e{e{e{Cju!5 LJje{e{e{e{e{e{e{e{juuue $b(uuuuuCje{e{e{e{e{DkDkCjuuHb(5je{e{e{e{e{e{e{e{juuue $b(uuuue{e{e{DkZZLJ5b( *5"Q"Q"Q!%eue{e{e{e{e{e{e{juuue $b(uuuujDkCj!5!%!%5E"Q]]!5!5!59je{e{e{e{e{e{e{e{e{e{uu"Q5 $b(uuuuCV#F"Qb(!%EuCj])%EDke{e{e{e{e{e{e{e{e{e{u"Q"A5 b(uuuub(5RJCj"Q*Aue{e{ue{e{e{e{e{e{juE b(ueQb(95!%!%b("AeujDkjE!%!%!%!%!%!%b( uE!%5RJ"AeeCV#F#F 5 uNuNuNuN      SYMBTEXTHappyTEXTIMG1IMGsx{{{5s / ~T{t{~ a~0s.JpNRf~v ~~lIZJjIl~~BE~~GjC-f=eZg~~BIY~Fg&~a]~jh~jQfv)K{vf$~rk0on( Yl-sbq{Qwf.o&V &V~o{jjjjkOwJ&V &V oj&V &V&VJJf^&V&V$-(&VSYMBTEXTInTownTEXTIMG1IMGd4H1H1vuuvuuuub uuuub r^R^R^R^R^r^ 9!  !9 b!^!^^!^!bb^UjnjssjnjU^bu4{un)JqJq)nu4{u NH1b!rb!k}k}!rb!bH1 N NH1b!rb!k}k}!rb!bH1 N NH1b!sb!!sb!bH1 NbEb!fIIf!bEbSYMBTEXTLogoTEXTIMG1IMG 2,^^*^^9g9g9g9g( ^^9g9g9g9g^^RJRJ& ^^9g9g9g9g^^RJRJBB$^^9g9g9g9g^^RJRJJ)J)BB"^^9g9g9g9g^^RJRJJ)J)RJB ^^9g9g9g9g^^RJRJJ)J)RJB^^9g9g9g9g^^RJRJJ)J)VVRJ)%^^VBVV^^RJRJJ)J)VV)%)%^^9g9g9g9gVVVVBBVV)%)%^^9g9g9g9g^^RJRJBVBBVV)%)%^^9g9g9g9g^^RJRJBBBVBRJVV)%)%^^9g9g9g9g^^RJRJJ)J)BBBVBRJ)%)%^^9g9g9g9g^^RJRJJ)J)RJBBV)%RJ^^9g9g9g9g^^RJRJJ)J)RJB)%)% RJ)%^^9g9g9g9g^^RJRJJ)J)VVRJ)% RJ)%)%)%)%)%VV^^RJRJJ)J)VV)%)% )%)%)%)%)%RJ^VVBBVV)%)%)%RJ^55BVBBVV)%)%)%)%55BVBRJVV)%)%c5^)%)%5555BVBRJ)%)%5)%)%)%55^RJRJ55BV)%RJ5^)%55^5RJ555)%)%)%)%)%)%55^55)%55)%)%5c)%55^55)%)%)%)%cc)%^55)%)%55)%)%)%)%)%)%55k-k- )%c)%)%)%55)%)% k-)%)%)%)%55k-k-k-k-c)%k-SYMBTEXT MaglevStopTEXTIMG1IMG . SYMBTEXTMessageTEXTIMG1IMG -,HHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHssHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHHHHHH HHHHH55HHHH HHHHH55HHHHHHHHHRJRJHHHHHHHHHRJRJHHHHHHHHHssHHHHHHHHHssHHHH!HHHHHHHHH!HHHHHHHHH#HHHHHHHHH#HHHHHHHHH%HHHHHssHHHH%HHHHHHHHH'HHHHHHHHH'HHHHHssHHHH)HHHHHHHHH)HHHHHHHHH+HHHHHHHHH+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHSYMBTEXTMessageOptionsTEXTIMG1IMG #$$$$$###$$$$$######$###$###$###$#$$###$$$$$###$$$$###$###$###$###$##$$$$$###$$$$$######$###$###$###$#&)%##$$$$$###$$$$ 1$###$###$###$#V^==^^^^=^V^^^^^^^^^^^^^^^^^^^^^^^^^^^IMG ||| Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&|Q&Q& Q&Q& Q&Q& Q&Q& Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&IMG  Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q& Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q& Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q& Q&Q& Q&Q& Q&Q& Q&Q& Q&Q& Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&SYMBTEXT MonorailStopTEXTIMG1IMG. SYMBTEXTNarrowgaugeStopTEXTIMG1IMGd. SYMBTEXTNewYearTEXTIMG1IMG!@@@!BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB1@!AB BBF1EF1=mV+N9=nR+ND1 J N JKRElV^KR^JlZJV= JAe9=$5ED5A=D5Ee9E5E=E5f5$A!@!A $&-bf5+N9+JLR+J= JmVEI R*RlVElVf^bJVbbJR JA=E9=ED5AAD5A9=A=9f5))! !@!!!!$F-E$9+N%-LNLRI NVbQK^k^KVM^fkVvsfbbfImVe9D5AEe==Ef==A9A=A99Ab!@!b b$9LRf5=A- J^K^l^jbnfl^kZ^fbUoToff2oKVmZEeAA REEIIAEe9A=AA) )! !@!B A &)g5F1%-f5LRE J=E5KZjnRws0{rnjbkkRsfkvwlVf R*RQI VUQUUMIAD5=Ef5-=! !@!$)AE9f5 J*N+RQUswS{S{{Q{r{2wjRsswkRsoTsfbf*V*bY b b]]Lf+bUeA$5EE1$1E)! !@!! A 9 FmR JA=LVKZlbnu{Q{{wr{Qw1wRwSssjjfjKf bJnn*n jrn]IIf9D5=9)aA!@!! $%-9F1= JLVIfnr{{{{Q{s{{{Rwjnknvv*jKrvkrlrvvmj]MAf=A$1$-b!@!!A F19e55E1f5Ib ZrQ{v{S{{{owtwnrQ{zv)n{krvzzv bUAA$1== !!@!B$)e5LRmVA%1f= ^jv{r{2{s1w2wR{{Q{{z*nv)nJvzvlr bMEe=E=$A!@!B (f5AA= FLR VKfnrrv{{s{wv1{{wvkv)rrkvkrKrJn]LbMAe9AE5b!@!!g9 JlZmV^lVEML^Lblnr3{{QwT{{vvjvkvvJvzvvkrrYfAE+RAe5b!@!! F1ALR^bb^fnS{T{vkrr{{{r{q{s{s{1{vzzzvzvzzv Z N+RLRE%1! !@!!B,9 J+NZ*NE Zn3wv{S{2{{O{/{r{{{S{zzz{vv{1{v*^ N*NLRE)!@!! b,f=EKRl^jlVKZjsT{t{2{2{{{R{{{v{w{{T{{1{R{1{R{bImVKRA)!!@!" -EKVZZfmb*VKfv4w{{{{z{r{T{rnrr1wvR{z{{jLVA=9)b!!@!!"!B -f=E+ZlZlZ,Vlbn2w{{{s{{{{t{t{{rrrrvR{{S{{twm^mZMVE5f5E1A!@!!Bb$((9E*VblZIQnr1{t{u{Q{{Q{{S{v3{T{{{wnnjwv{2{{wnm^klV^mV!!!@!!B Ac,IMUQ RYKf+ZU*nv1{t{{1{t{r{vS{3wt{{wwjfrnwrv{U{rjYM*R^EF1&-!!@!! !!Bb0b 0%UQM ^K^*fe*bjrv3{u{3{t{T{z4{{{{{sfMbnooLb+^n,ZU+V RKRA NF5a!! !@!!! ! BB b$@M%I%MQ V+blbfmffnr3{1{2{2{4w3{6{{U{x{3w5sr,bnrMbb+^ ZLVMfA Nf9%-1!!! !!@!!! ! ABBB b,b,08EUU Z b+bfjnLnvu{zt{w3{{x{w{ssPjrWsfnnnjb,V^YnZ%9A%A$!!! !!@!! AABB BB$<rsqsP6q>3[QkQk3[p:JsWQkQkWsFNQkN1c1cNQkNN{NFQoQoFN{NNPoV[J1c1cJ[VQgNNpw[1cpw1gRR1cpw1g[PsNNpsW1cp{p{QgWPoNN[_p{{1cWNFRk1_{1cRgoB0.u_{{c0. !%%%%%%%%%%%%! SYMBTEXTSeasonsTEXTIMG1IMG*   s`````````````` `````` `` s```` s `` ```````````````````` sIMG   hA hA44 444#I #IdYdY4 4 hA444 44  #IdYdYdY 4#I44 444 44#I4hA hA#IdY 4#I44 44 #I4#I4 #I 4#I4  4 hA4 4hAIMGb  cccc cccccccc cccc cccccc cccccc  cccccc cccccc cccc ccccccccccccIMGx    B  ,k ,k,k B,k,k,kBB ,k,k ,k,k,k  BBBBSYMBTEXT ShipStopTEXTIMG1IMGz0    SYMBTEXT TrainStopTEXTIMG1IMG,   SYMBTEXT TramStopTEXTIMG1IMG)   SYMBTEXTUnhappyTEXTIMG1IMGhlllh |tllllpx| xplllllllpx |xpllllllltxl `@ptplllptd8txkHIJk\ttlttjHJIhl|kpe-J0kptk`)$IvFdt||klv~SI2kXkpH!s/yp||klpukx|}gpp|`tJl)p t)lI8j0)H)p p)lklxhH |plhj8Pk$k4lltp hxpdDtxdHlp|gD lxtpplpptx\ `xtplpxtiP\||tTSYMBTEXTWarenTEXTIMG1IMG$(( @@,,,  (,00LH0 (0044440 (H44LL84 (@,L88 88 Q @,DL4L8 F**D0H4L8 r =7=7D0L8 H433=7>7N * =3=37N * ^7>7=7=7 N N * _7^7^7 N N /;^7N N N ;8N N kSYMBTEXTfastforwardsymTEXTIMG1IMG"  ))`n)`n)`nr)`nr)`nrr)`nrr)`nrrr)`nrrr) `nrrrr)`nrrrr) `nrrrrE`nrrrrE`nrrrE`nrrrE`nrrE`nrrE`nrE`nrE`nE`nEEESYMBTEXT networksymTEXTIMG1IMG@  BBBBBBB> B8gwwwwwZkB Bw!y:y:y:!wB Bw!y:.!!wB BBBBBBB!!!wB B8gwwwwwZkB!twB Bw!y:y:y:!wBwwZkB Bw!y:.!!wBBBB Bw!!!!!wB Bwt!!!twB BZkwwwwwZkBBBBBBBBSYMBTEXT pausesymTEXTIMG1IMGB  )))~)))~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~`nrr~EEEEEEEESYMBTEXT station_typeTEXTIMG1  IMG IMG IMG   ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~ ~~~~~~~~~~~IMG IMG  ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~ ~~~~~~~~~~~IMG IMG IMG  ~~~~~~~~~~~~ ~~ ~~ ~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~ ~ ~~~~~~~~~~~IMG SYMBTEXT timelinesymTEXTIMG1IMG.  +M 55 MVcccccc* MW2.*"w5N &:O::*:6  U*{o[_{k{o{o{o:_ >{o{o{o+jiYo|o: t6{o{o|oiiwo %swwrbe{{B v.22{{{{{{s3. 69gu6{|cBt>V+ [gcZ^ZkN +:::ooo+simutrans-124.5/simutrans/themes/menu.pak64german_large.pak000066400000000000000000000001051520675460500240230ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOTsimutrans-124.5/simutrans/themes/modern-large.pak000066400000000000000000001503461520675460500221600ustar00rootroot00000000000000Simutrans object file Compiled with SimObjects 0.1.3exp ROOT MENUTEXTButtonTEXTIMG1$$IMGLŻлW26:Ż6::л:::IMG"@@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::IMGLлŻ:6W2::6Ļ:::лIMGBW2:::W2:::W2:::W2:::IMG"@@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::IMGB:::W2:::W2:::W2:::W2IMGLл:::Ż6::W26:ŻлIMG"@@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2IMGL:::л::6Ļ:6W2лŻIMGLL!%!%%%%%IMG"@@LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%IMGL%!L%%!%%%IMGBL%%%L%%%L%%%L%%%IMG"@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%IMGB%%%L%%%L%%%L%%%LIMGL%%%!%%L!%IMG"@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIMGL%%%%%!%!LIMGL&1RJRZ&RZZ1ZZZIMG"@@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZIMGL1&ZRRJZZR%ZZZ1IMGBRJZZZRJZZZRJZZZRJZZZIMG"@@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZIMGBZZZRJZZZRJZZZRJZZZRJIMGL1ZZZ&RZZRJRZ&1IMG"@@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGLZZZ1ZZR%ZRRJ1&IMGHIMG"@@@@@IMGHIMGBIMG"@@@@@IMGBIMGHIMG"@@@@@IMGHMENUTEXT RoundbuttonTEXTIMG1IMGLŻлW26:Ż6::л:::IMG"@@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::IMGLлŻ:6W2::6Ļ:::лIMGBW2:::W2:::W2:::W2:::IMG"@@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::IMGB:::W2:::W2:::W2:::W2IMGLл:::Ż6::W26:ŻлIMG"@@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2IMGL:::л::6Ļ:6W2лŻIMGLL!%!%%%%%IMG"@@LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%IMGL%!L%%!%%%IMGBL%%%L%%%L%%%L%%%IMG"@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%IMGB%%%L%%%L%%%L%%%LIMGL%%%!%%L!%IMG"@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIMGL%%%%%!%!LIMGL&1RJRZ&RZZ1ZZZIMG"@@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZIMGL1&ZRRJZZR%ZZZ1IMGBRJZZZRJZZZRJZZZRJZZZIMG"@@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZIMGBZZZRJZZZRJZZZRJZZZRJIMGL1ZZZ&RZZRJRZ&1IMG"@@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJIMGLZZZ1ZZR%ZRRJ1&MENUTEXT EditfieldTEXTIMG1  IMG"˻˻oIMG@@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2@IMG"˻o˻IMGW2W2IMG@@@IMGW2W2IMG"˻o˻IMG@@@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2IMG"o˻˻MENUTEXTListboxTEXTIMG1  IMG"˻˻oIMG@@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2@IMG"˻o˻IMGW2W2IMG@@@IMGW2W2IMG"˻o˻IMG@@@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2IMG"o˻˻MENUTEXTBackTEXTIMG1  IMG IMG IMG IMG@W2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkIMG!@@@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkIMG@kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2kkkW2IMGH6.gkkŻSkkW2SgŻлIMG"@@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2IMGLkkgлkkOĻgSW2лŻMENUTEXT CheckbuttonTEXTIMG1IMGŻлW2W2W2W2W2W2W2W2лŻW2\_{{\_W2Ż\_;[Ļл{{лW2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2л{{лŻ\_;[ĻW2\_{{\_W2ŻлW2W2W2W2W2W2W2W2лŻIMGŻлW2W2W2W2W2W2W2W2лŻW2\_{{\_W2Ż\_;[Ļл{{лW2kw:W2W2W2W2w:kW2W2w:W2W2W2W2W2W2w:W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2w:W2W2W2W2W2W2w6W2W2kw6W2W2W2W2:kW2л{{лŻ\_;[ĻW2\_{{\_W2ŻлW2W2W2W2W2W2W2W2лŻIMG&1RJRJRJRJRJRJRJRJ1&RJRZZZZZZZZZZRRJ&RZZZZZZZZZZZZR%1ZZZZZZZZZZZZZZ1RJZZZZZZZZZZZZZZRJRJZZZZZZZZZZZZZZRJRJZZZZZZZZZZZZZZRJRJZZZZZZZZZZZZZZRJRJZZZZZZZZZZZZZZRJRJZZZZZZZZZZZZZZRJRJZZZZZZZZZZZZZZRJRJZZZZZZZZZZZZZZRJ1ZZZZZZZZZZZZZZ1&RZZZZZZZZZZZZR%RJRZZZZZZZZZZRRJ&1RJRJRJRJRJRJRJRJ1&MENUTEXT PosbuttonTEXTIMG1IMGŻлW2W2W2W2W2W2W2W2лŻW2\_{{\_W2Ż\_;[Ļл{{лW2FsW2W2W2W2S{W2W2W2W2W2w6\_W2W2W2W2W2W2W2>kW2W2W2W2W2W2W2>kW2W2W2W2W2w6\_W2W2W2W2S{W2W2FsW2л{{лŻ\_;[ĻW2\_{{\_W2ŻлW2W2W2W2W2W2W2W2лŻIMGŻлW2W2W2W2W2W2W2W2лŻW26::::::::::6W2Ż6::::::::::::6Ļл::::::::::::::лW2::::kF::::::::W2W2::::~c>::::::W2W2::::{]W:::::W2W2::::w::::::W2W2::::kF::::::::W2л::::::::::::::лŻ6::::::::::::6ĻW26::::::::::6W2ŻлW2W2W2W2W2W2W2W2лŻIMG&1RJRJRJRJRJRJRJRJ1&RJRZZZZZZZZZZRRJ&RZZZZZZZZZZZZR%1ZZZZZZZZZZZZZZ1RJZZZZsNVZZZZZZZZRJRJZZZZRJRJRZZZZZZZRJRJZZZZRJRJRJRJRZZZZZRJRJZZZZRJRJRJRJRJsNVZZZRJRJZZZZRJRJRJRJRJsNVZZZRJRJZZZZRJRJRJRJRZZZZZRJRJZZZZRJRJRZZZZZZZRJRJZZZZsNVZZZZZZZZRJ1ZZZZZZZZZZZZZZ1&RZZZZZZZZZZZZR%RJRZZZZZZZZZZRRJ&1RJRJRJRJRJRJRJRJ1&MENUTEXT ScrollbarTEXTIMG1IMGŻлW2W2W2W2W2W2W2W2лŻW2\_{{\_W2Ļ;[\_Żл{{лW2sFW2W2{SW2W2W2W2\_w6W2W2W2W2W2k>W2W2W2W2W2W2W2k>W2W2W2W2W2W2W2\_w6W2W2W2W2W2{SW2W2W2W2sFW2л{{лĻ;[\_ŻW2\_{{\_W2ŻлW2W2W2W2W2W2W2W2лŻIMGŻлW2W2W2W2W2W2W2W2лŻW26::::::::::6W2Ļ6::::::::::::6Żл::::::::::::::лW2::::::::Fk::::W2W2::::::>~c::::W2W2:::::]W{::::W2W2:::~c::::W2W2::::::::Fk::::W2л::::::::::::::лĻ6::::::::::::6ŻW26::::::::::6W2ŻлW2W2W2W2W2W2W2W2лŻIMG&1RJRJRJRJRJRJRJRJ1&RJRZZZZZZZZZZRRJ%RZZZZZZZZZZZZR&1ZZZZZZZZZZZZZZ1RJZZZZZZZZVsNZZZZRJRJZZZZZZZRRJRJZZZZRJRJZZZZZRRJRJRJRJZZZZRJRJZZZVsNRJRJRJRJRJZZZZRJRJZZZVsNRJRJRJRJRJZZZZRJRJZZZZZRRJRJRJRJZZZZRJRJZZZZZZZRRJRJZZZZRJRJZZZZZZZZVsNZZZZRJ1ZZZZZZZZZZZZZZ1%RZZZZZZZZZZZZR&RJRZZZZZZZZZZRRJ&1RJRJRJRJRJRJRJRJ1&IMGŻлW2W2W2W2W2W2W2W2лŻW2\_{{\_W2Ż\_;[Ļл{{лW2FsW2W2W2W2S{W2W2W2W2W2w6\_W2W2W2W2W2W2W2>kW2W2W2W2W2W2W2>kW2W2W2W2W2w6\_W2W2W2W2S{W2W2FsW2л{{лŻ\_;[ĻW2\_{{\_W2ŻлW2W2W2W2W2W2W2W2лŻIMGŻлW2W2W2W2W2W2W2W2лŻW26::::::::::6W2Ż6::::::::::::6Ļл::::::::::::::лW2::::kF::::::::W2W2::::~c>::::::W2W2::::{]W:::::W2W2::::w::::::W2W2::::kF::::::::W2л::::::::::::::лŻ6::::::::::::6ĻW26::::::::::6W2ŻлW2W2W2W2W2W2W2W2лŻIMG&1RJRJRJRJRJRJRJRJ1&RJRZZZZZZZZZZRRJ&RZZZZZZZZZZZZR%1ZZZZZZZZZZZZZZ1RJZZZZsNVZZZZZZZZRJRJZZZZRJRJRZZZZZZZRJRJZZZZRJRJRJRJRZZZZZRJRJZZZZRJRJRJRJRJsNVZZZRJRJZZZZRJRJRJRJRJsNVZZZRJRJZZZZRJRJRJRJRZZZZZRJRJZZZZRJRJRZZZZZZZRJRJZZZZsNVZZZZZZZZRJ1ZZZZZZZZZZZZZZ1&RZZZZZZZZZZZZR%RJRZZZZZZZZZZRRJ&1RJRJRJRJRJRJRJRJ1&IMG IMGj@@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk@kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkIMG IMGĻлW2;[{Ż\_л{W2W2W2W2W2W2W2W2л{Ż\_W2;[{ĻлIMGj@@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2@@@@@@@@@@@@@@@W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2W2IMGлĻ{;[W2\_Ż{лW2W2W2W2W2W2W2W2{л\_Ż{;[W2лĻIMGĻлW2W2W2W2W2W2W2W2лĻW2;[{{;[W2Ż\_\_Żл{{лW2kkW2W2>>W2W2\_W2W2\_W2W2{w6W2W2w6{W2W2SW2W2W2W2SW2W2sW2W2W2W2W2W2sW2W2FW2W2W2W2W2W2FW2W2W2л{{лŻ\_\_ŻW2;[{{;[W2ĻлW2W2W2W2W2W2W2W2лĻIMGĻлW2W2W2W2W2W2W2W2лĻW26::::::::::6W2Ż6::::::::::::6Żл::::::::::::::лW2::::::{{>::::W2W2::::~c~c::::W2W2:::FF:::W2W2:::kk:::W2W2::::::::::::::W2л::::::::::::::лŻ6::::::::::::6ŻW26::::::::::6W2ĻлW2W2W2W2W2W2W2W2лĻIMG%1RJRJRJRJRJRJRJRJ1%RJRZZZZZZZZZZRRJ&RZZZZZZZZZZZZR&1ZZZZZZZZZZZZZZ1RJZZZZZZVVZZZZZZRJRJZZZZZZsNsNZZZZZZRJRJZZZZZRRJRJRZZZZZRJRJZZZZZRJRJRJRJZZZZZRJRJZZZZRRJRJRJRJRZZZZRJRJZZZVRJRJRJRJRJRJVZZZRJRJZZZsNRJRJRJRJRJRJsNZZZRJRJZZZZZZZZZZZZZZRJ1ZZZZZZZZZZZZZZ1&RZZZZZZZZZZZZR&RJRZZZZZZZZZZRRJ%1RJRJRJRJRJRJRJRJ1%IMGĻлW2W2W2W2W2W2W2W2лĻW2;[{{;[W2Ż\_\_Żл{{лW2W2W2FW2W2W2W2W2W2FW2W2sW2W2W2W2W2W2sW2W2SW2W2W2W2SW2W2{w6W2W2w6{W2W2\_W2W2\_W2W2>>W2W2kkW2л{{лŻ\_\_ŻW2;[{{;[W2ĻлW2W2W2W2W2W2W2W2лĻIMGĻлW2W2W2W2W2W2W2W2лĻW26::::::::::6W2Ż6::::::::::::6Żл::::::::::::::лW2::::::::::::::W2W2:::kk:::W2W2:::FF:::W2W2::::~c~c::::W2W2::::>{{>::::W2W2:::::]W]W:::::W2W2::::::ww::::::W2W2:::::: int main() { return puts(VERSION_NUMBER) < 0; } simutrans-124.5/src/OSX/osx.mk000066400000000000000000000042411520675460500161670ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # Makefile to build bundle applications for Mac OS X # bundle makes a bundle designed to work on the system it was build on # bundle_dist makes a bundle, which should work on all supported systems (custom config needed) # Variables: # CXXHOST CXX for host system. Only used when crosscompiling. # BINARY_PPC path to the binary for PPC # BINARY_i386 path to the binary for i386 (default $(PROGDIR)/$(PROG)) # SDL_PPC path to SDL.framwork for PPC # SDL_i386 path to SDL.framwork for i386 (default ../Frameworks/SDL.framework) # The name of the project is simutrans => thus we want to build the default bundle under simutrans PROGNAME = $(PROG) ifeq ($(PROGNAME),sim) PROGNAME := "simutrans" endif CXXHOST ?= $(CXX) BINARY_i386 ?= "$(PROGDIR)/$(PROG).app/Contents/MacOS/$(PROG)" SDL_i386 ?= "../Frameworks/SDL.framework" .PHONY: bundle bundle_dist "OSX/getversion": OSX/getversion.cc simversion.h @echo "===> Building $@" $(Q)$(CXXHOST) "OSX/getversion.cc" -o $@ bundle: all "OSX/getversion" @echo "===> Building bundle" $(Q)rm -fr "$(PROGDIR)/$(PROG).app" $(Q)mkdir -p "$(PROGDIR)/$(PROG).app/Contents/MacOS" $(Q)mkdir -p "$(PROGDIR)/$(PROG).app/Contents/Resources" $(Q)cp "$(PROGDIR)/$(PROG)" "$(PROGDIR)/$(PROG).app/Contents/MacOS/$(PROG)" $(Q)cp "OSX/simutrans.icns" "$(PROGDIR)/$(PROG).app/Contents/Resources/$(PROG).icns" $(Q)echo "APPL????" > "$(PROGDIR)/$(PROG).app/Contents/PkgInfo" $(Q)OSX/plistgen.sh "$(PROGDIR)/$(PROG).app" "$(PROG)" bundle_dist: bundle @echo "===> Changing bundle to be distributable" $(Q)mkdir -p "$(PROGDIR)/$(PROG).app/Contents/Frameworks" $(Q)cp "$(BINARY_i386)" "$(PROGDIR)/$(PROG).app/Contents/MacOS/$(PROG).i386" $(Q)cp "$(BINARY_PPC)" "$(PROGDIR)/$(PROG).app/Contents/MacOS/$(PROG).ppc" $(Q)cp "OSX/binary_picker.sh" "$(PROGDIR)/$(PROG).app/Contents/MacOS/$(PROG)" $(Q)cp -r "$(SDL_PPC)" "$(PROGDIR)/$(PROG).app/Contents/Frameworks/SDL-ppc.framework" $(Q)cp -r "$(SDL_i386)" "$(PROGDIR)/$(PROG).app/Contents/Frameworks/SDL-i386.framework" simutrans-124.5/src/OSX/plistgen.sh000077500000000000000000000030031520675460500172040ustar00rootroot00000000000000#!/bin/sh # # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # date=`date +%Y` PROG="$2" COPYRIGHT="Copyright 1997-${date} by the simutrans team" if [ -z "$3" ]; then VERSION="`../OSX/getversion`" else VERSION=$3 fi echo "Executable $PROG" echo "$VERSION" echo " CFBundleDevelopmentRegion English CFBundleDisplayName ${PROG} CFBundleExecutable ${PROG} CFBundleGetInfoString ${VERSION}, ${COPYRIGHT} CFBundleIconFile ${PROG}.icns CFBundleIdentifier org.${PROG}.${PROG} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PROG} CFBundlePackageType APPL CFBundleShortVersionString ${VERSION} CFBundleVersion ${VERSION} NSHumanReadableCopyright ${COPYRIGHT} NSPrincipalClass NSApplication " > "$1"/Contents/Info.plist simutrans-124.5/src/OSX/simutrans.icns000066400000000000000000005207501520675460500177400ustar00rootroot00000000000000icnsis32sokhgt c_ZVSOKaWRLGB=949RKD>82.)$%z#Vc~yY<221.R̬oLF z}rR"{nd]YY^dgiX"XFGRX]acegjor,S;YāWUROLJGDB?=;975420CGσTRNKHEB@=:741/,*('$4pSPMJGDA>;741.+(&# 4$‚TRNKGCA>:8641/,*('&'&HVJWTPVkx`H:765322123) [_{ͺaB=>==<=<УfIGHHI3 :*Δ[RSJ&.brlihio{Сc\?1</wutsrpnjeacwwomb`_J J}xph`YUSSUVZ]adfhcK`vk^TMIHKPV\aeghijlnprtb'AP?>?CLTZ^``_`behkmmnprss{52<>@IYZZ]`ceeca_^^_`bfjsy=<>?FY[YVSRQRSUWZ\^`bdef7C#7l;=?ADEFHKMOQSUWY[]_acefei[!;=?ACEGIKMPRSUWY[]_acegikl',65543224:CNUWY[]_acegiknA\&%$&,=SXZ\^`bdfhjnR(%:VZ\^`acegilY  % ރXmYfՂZ[Ya[[ZZ ±vZZYWT\ƀҀ ɴ]XWUSQLfIJjWTRPNLaf`OMKJFdÿ}||RJIGEC?tſznfccfimpsuYwwlSDB@?=8n`XUW[bhlprtuvxz}wS=<:9grJHIMV^dhijjkmqtvxy{}xV762WFHKSabdfilnonmnprtxyw=0/)[FHJQacb`_^_`begjloqtvxqT,*DTEGJLPQSUX[]`begjlnqsvxyrvs2%@EHJMOQSVX[^`bdgilnqsuxz}F$#J==<<;:8679AL[dgiknpsuwz|\&&;s&&%$&.Eaikmpruwz|~k)%F%@fkmoqsvx{}p,&$@%Qуw0&łwT ) e ހ  4ހC08iN|48ɶ Q~xwy|~f,N fʷ|rmmptx|~|6 HVeccflquxyzz|}}~: 8acdjstuwy{}~~z{ Babdhsuttsstuwy{}yF (;o`bdfhikmoqsuwy{|~|x&aceghjkmoqtvwxz|~71QPMKHEB??BKZmxz|~W !`'&%%$&1Pt|}l ,%&Hy}~u%%l8mkih32 ~}|{zyxvv|{zyxvvttsrqpomnvutrqponmlkjihhgedtponlkjihgedccba`__]\pkjhgedcb`_^]\[ZYXXWVTUzfeca`_]\[YXWVTSRQQPONNKXa`^\[YXVUSRQONMLKJIHGGFEEzܒ]\ZXVUSQPNMKJIGFECBA@@?>>;[ZXVTRPOMKJHFECB@?=<;:987764FkWUSQOMKIHFDB@?=;986542100/.-9@&USQOMKIGECA?=;976420.-+*)(''%/R TRPNLJGECA?=;97531/-+)'%#" ,6(TRPNLJGECA?=;97531/-+)'%#" !!3F{"USQOMKIGDB@><;975310.,+)(''&&'('HxWUSQOLJMSWVPG?:998653210/./0,<YXVSSgbG;::987766567"h]ZZlϸ\B>?>>=>6 bq͠fGDEED!НaKLM8 ;þdžWSTL 1# f{pkhghjoyӀ ڧfZ[Y-% (fղ}tnmmlkjihgfeelɾ`bT@W0( ywwvutsrrqpomkgc^^kf`^_`_aafX!~}}|zwrle_ZVTSUUVXYZ\]^`acbR, #xof]UPMLMNOQRTX[_cgjmnoonljfHY-|qdXOIFEFGHJLPU\cimopnljihijloswxpTHn;cA>>?@BCEJPXahkkgb^ZY[^bgkoqsttsppotj#-!<=>@AK\cfe`YURSX^elprrpmkhffginuxjy(ʀ<=>@AJTPNRX_glnmje`\Y[\^_abcehgJ5;=>?@I_ghgc^XTQOPQRUVWYZ[]^_abcef`zk E;<>?@ENKIGHIJLNOPRSTVWXZ[\^_`bcdfeaswj>![;<>?@BBDEGHJKLNOPRSTVWXZ[\^M_`bcdfggccl`J;<=?@ACDEGHIKLMOPQSTUWXY[\]_`acdeghiklm-T;<=?@ACDDE#FHJMPRSTUVXYZ\]^`abdefhijloId4-3210/-,+**+-07ALTUVXYZ\]^`abdefhijln]  4#&%$$% $$%*8LVWYZ[]^_abcefgijkmgP%$+DWYZ[]^_abcefgijkml#7%)IXYZ[]^_abcefgijkl*!% ĀĀ  a X]'`[Yx(Y[[Z_[ZeЄiZ[Y|ǽ\[ZXVՀ fZ[ZYXWUTVdz |ZZYXVUTRQO^сӀDz_XWVUSRQONKHp ہտkUVTSQPONLNh^I1ž̷evYQPNMLKIHŜ½*»ONMKJIHFDH |tpnoqsx~YKKJHGFECB@Iă$zogccdfhikmnprsuvp]JFEDCA@?>;OˀBĽpd]ZZ[]_adfjmqtvyz{||{{zjMBA@>=<;92WtdYSPQSUWZ]bhmruxwv]xz|nJ==;:97l_MHGIKMOQU[bjprrpmjhhilpswz|~~~wwuL98763JFGIKLVdjmlhd`_`djouxz{yxwuwy|nC4431.MҀ=EGIJLT]ZY\bhosuurolihhijlnoqstvxyl_110.-)UEGHJKSgmonkgc`^&`bdfgijlnoqstvwylzu=-,+01$_EFHJKPYVTVWZ\]_`bdegijlmoqrtvwyxiswu\+)'ArEFHIKMNPRSUVXZ[]_`bceghjlmoprtuwyzzrrw4&&%dDFHIKLNPQSUVXZ[]^`bceghjkmoprsuwxz|~K$&&e$lDGHJKLNOPQPQRTW[^abcefhikmnprsuvxz{}c'&&%#z;;:976421/-,,-/4=JXcefhiklnpqsuvxy{}~t/%&.PA&%$%$%,?Yfgiklnoqstvxy{|~}9%&)h%$-Nfijlnoqrtvwy{|~C$&&$S%&&%*Uiiklnpqstvxy{}~G#&#A%sK \ @ P%CF pރ6߁ނq'ā߀ނ݁" D݂D:-lz8a!l׵Úy ǵ, "о~ywwy{}~p@ &#ȴ~uppqrtvxz{~c"  3ѿsliijlnprvy| o% , Os@hicabdfgjlpuy}~~|{zz{}}{o*  'abcdfltyzzxvtstvz~qz! .ˀ`bcdekponqtx|~|{|}pN  ;`acdejvz|{zwutsstvwxy{|}~uzs  I`acdehmmkklnoprstuwxyz|}~ptx}T &_`abdefgijklnopqstuvxyz{}~K}}~O`abcefghjklnopqrtuvwyz{}~=X`bcdefgfUilptvwwyz{|~a gOMJGDA>:741//03:FWjwyz{|}z8'&&%$%/Giz{|}~$ S%$$/Zz|}~2;%&%,c||}~:&%h8mk it32[j    }|{~}|{yx~~~}|{zyxvtx~}}|{zyyxwvutrqz~~}}|{{zyxxwvutsrqqnr}|{{zzyyxwwvutssrqponlk{zzyyxxwvvuttsrrqponmlkjgqxxwwvvuttssrqqponnmlkjihgdp vvuuttssrrqppoonmmlkjihgfedbl ttssrrqqppoonmmllkjjihggfedcba_srqpo nnmmllkkjjihhgfeedccba`_][upoonnmmllkkjjiihhgffeddcbaa`_^]\Z\nmmllkkjjiihhggffeedccbba``_^]\[ZYXV`lkkjjiihhggffeeddccbba``__^]\\[ZYXWVURmjiihhggffeeddccbbaa``__^^]]\[[ZYYXWVUTSPUhggffeeddcbbaa``__^^]]\\[[ZYYXWWVUUTSRQPOMpffeedccbbaa`__^^]]\\[[ZZYYXXWVVUUTTSRQPONMKTedccbbaa`__^^]]\[[ZZYYXXWWVVUUTTSRRQQPOONMLKJII{۹#cbba``__^]]\\[[ZZYXXWWVVUUTTSSRRQQPPONNMLLKJIHGD^$aa`__^^]\\[ZZYYXXWVVUUTTSRRQQPPOONNMMLKKJIHGFEDBJ&`_^^]\\[[ZYYXWWVVUTTSSRRQQPOONNMMLLKKJJIHHGFFEDCBA@?{*^^]\[[ZZYXXWWVUUTSSRRQPPOONMMLLKKJJIIHHGGFFEDDCCBA@?>>=<;:O~-[[ZYYXWWVUUTSSRQQPOONNMLLKJJIIHGGFFEDDCCBBAA@@?>>==<;:96Hę1ZYYXWWVUUTSSRQQPOONMMLKKJIIHGGFFEDDCCBBA@@??>>==<<;::987648"a5YYXWVVUTTSRQQPOONMMLKKJIIHGGFEEDCCBAA@@??>==<<;;::99887654327pΘ4XWWVUTTSRRQPPONMMLKKJIHHGFFEDDCBBA@@?>>==<<;::998776654432102vw8WWVUTSSRQQPOONMLKKJIIHGGFEEDCBBA@@?>>=<<;::9887765544332210/.-.-+p^"՗:VVUTSSRQQPONNMLKKJIHHGFEEDCCBAA@?>>=<<;:99887665443321100//.--,+)nv>VUTTSRQQPONNMLKKJIHHGFEEDCCBA@??>==<;::988765544322100/..--,,++*))(&kF Ж?UUTSRQQPONNMLKKJIIGGFEDDCBAA@??>=<;;:99876654432110//.--,,+**)(('&%&$i `AUTSSRQPOONMLLKJIIHGFEEDCBBA@??>=<<;:9987655433210//.--,++*))(''&%%$#"#$$!{DCUTSRQQPOONMLKKJIHGGFEECCBAA@?>==<;:9987765433210//.--,+**)(''&%%$##"! !!""' WGTTSRQQPONMMLKJJIHGGFEDCCBA@??>=<<;:9987655432110/..-,++*)(''&%$$#"!!  !%1 GTTSRQQPONMMLKJJIHGGFEDCCBA@??>=<<;:9987655432110/..-,++*)(''&%$##"!!   !!""/0ܓEUTSRQQPOONMLKKJIHGGFEECCBAA@?>==<;:9987765433210//.--,+**)(''&%%$##"!!  !!"##$#10qCUTSSRQPOONMLLKJIIHGFEEDCBBA@??>=<<;:9987655433210//.--,++*))(''&%%$$#"# $$%%&$DAUUTSRQQPONNMLKKJIHGGFEDDCBAA@??>=<;;:99876654432110//.--,++**)((''&%& ''((&\)B>VUTTSRQQPONNMLKKJIHHGFEEDCBBA@??>==<;::988765544322100/..--,,++*))()**++(uiw>=<<;:99887665443321100//..-,+,-.)WWVUTSSRQQPOONMLKKJIIHGFEDCBBAA@?>==<<;::98877655443322100/.-./005ߏ!XWWVUTTSRRQPPONMMLJIHJNSW[]]ZVPJC@>==<<;::9887766554332101223* d5YXXWVVUTTSRQQPONLO\o|iUE>==>==<<;;::99887655434 ZYYXWWVUUTSSRPP]{ʀ­rVC==>>==<<;::987678+ [[ZYYXWWVUUSSeāŧ~[E>>??>>==<;:989:8;0]\[[ZYYXWVVfˬ|SA?@??>=<;<=* d4^^]\\[ZYXfęhH@ABA@?>?: `_^^]\[g(ӭuNBCDCBAB%8aa`_^hظRDEFEDCDE9=/cbajۀںRFHGFGEj0dkہٷyOIJIJ4  0ۃ֪lMLMLDɇ&ۄϗ]NON'F΀ۅܽ{TQRQS= t$Ӂ ۆ՟aSTUM &-wrmjihilotzۇܻvW. @̥xE2߄ sjfedccba``_afn}ۈ΋\YZY[B  !" lئb- fukiijiihggfedcbba`_ahvۉםa\ ]S/KT緃؃ ~pmllkjjihhgfed ccbado݇܀ۤe_ ]*9U`a6wqpqqppoonmmlkjihgfeedcfỳ݀ АabB>ZcP~vtssrqqponnmlkjihggff{xurpqtl`aabcd_I[fe`$)Њ{wxxwwvuutssrqponmlk jige`cska[XYYZ[\\]]__``aabdefggh>C߉{zzyyxwwvuttsrqpo*nnljfc_[YWVVUWWYYZZ[[\\]]^^__``aabbcceg\^~}}|{{zyyxwvutsrqokgb]YVT%UUVVWWXXYYZZ[[\\]]^^__``aabbccddbM-u~~}||{zzyxw7vtqmga[WSRQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddefeS/  ~}|{>ywsmg_YTQNONPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeefgfP$}ztme]VPMLKLMNNOPQQRRS TUUVVWWXXYYZ[\]^_`abcddefghc@'υ|ulcZRMIIHIJKLMN OOPPQQRRSSTUVVWWXYZ[\]`abcefeddceeffghiT =$}tk`VOIGEFFHHIIJJKKLLMMNNOOPPQQRRSSTUUWX\^cgmrwz~}wqlgfegghi`.^ }th\SJFCDEFFGGHHIIJJKKLLMMNNOOPPQRSVY^dkrz{yutrrqstw{}rjghhjf:  #}rdYNGB@@AACCDDEEFFGGHHIIJJKKLLMMNOPSW^enw|vpkfc`^\]\]]^^__``abcdhltzlhiji?  3a5E`TJB?=>>@@AABBCCDDEEFFGGHHIIJJKKLKLLOSZblw|tle`\YWXWXXYZ[[\\]]^^_``aaccdcdeisylfcc= MŒ\'9ف;;<=>>??@@AABBCCDDEEFFGGHHIJMS[ep{zpg`[VTSTTUVWWXXYYZ[[]^`bdfijklljihfdeegqjVdnj; e<<==>??@ABCDEFGKPYdq}|sh_XTQPQQRSSTUV WY[^bflqw|yqjfefidW{Xp;<<==>>??@@AABBCCDHMVbo{ymbYSONMNOPPQRSRSSUVY^djrz~zwtqpsuz}phcPqe[;<<==>>??@@AABABEKS`mzvj_VOLJKKMMNNOOPOPPRTY^emu}|vqlgd`_]^]^^__``aaccfkteRV;;<<==>>??@@AACUkxwj]SLIHHIJKKLLMLMMOQV\dmw~wphc^[YZZ\\]]^^__``aabbccdfl{qW/5;<<==>>??@@AABPlwj\RJGEFFHHIIJIJJLNSYbku~{rjc^YWV!WXYYZZ[[\\]]^^__``aabbccddeeffmeVa  Å[;<<==>>??@@AABACDCCDEFFGFGGIKPW_it}ypg_ZUSRSSTUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffg_Q7ք;<<==>>??@@AABBCDDEHMT]gr|wmd\VRPOPPRSTTUUVWXXYYZZ[\]]^^_`aabbccdeffgcOoAE;<==>>?@ AABBEJQ[fq|~ukaYSOMLLMNOOPPQRSSTTUVWX YYZZ[[\\]]^^__``aabbccddeeffgVX`"];;<<==>>??@@AAFdz|rh^VOLI>??@@AABRqpe[SLHFDGHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgg[Oqp`i""M- ;;<<==>>??@@AABACCDEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffg WOhyWgoL Ω}K Āh;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghfZNTbh`Q]mmh(€$ܰxAP;;<<==>>??@@AABBCCDDEEFFGGHHIJKKLLMMNNOOPPQQRSTTUUVWXXYYZZ[[\\]]^^_`aabbccddeeffggh bXQPT`kmmoD'΃;;<<=>>?@AABBCDDEEFGHHIJJKKLMMNNOPQQRSSTTUVWXYZ[\]]^^__`abbccdeffgghhiihhjkllmnaA܇:;;<=b>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmn/Bކi:;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmpQ[j:;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmngej:;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmno1j:;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnpL :;;<<=>??@@AABCDDEEFG HHIHIIJJLLMMNOPQQRRSTUUVVWWXYZ[\]^_``aabccdeffgghi jjkkllmnoa::;;<=>??@@A@@>=<;97642101012469=AEJNPQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``abccddeeffgghhiijjkkllmmnl#:9987654310.-,*)(''&%$ %%&(+08AIORS7TTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmno7 3׀,+*)(('&&%$$%)0=IRT5UUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnpK vBK%$%)4EQU4VVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmno[  جvB%:$&2ETUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnoed%$'7MVVWWXXYYZZ[[\\]]^_``aabcddeefgghiijjklmmnnk?ʃ% $-FVVWWXYYZ[[\\]^__``abccddefgghhiijjkkllmmnn+"%4(@UWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnp6%3'@VWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnp=b%1'DWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnpCA%&'&.OWWXXYXYYZZ[[\\]]^^__` aabbccddeef gghhiijlE<% Ǽ˃ʃɃȃ ȃDŽƃǃƃńăƃŃăÄ łĄÂ…Ă‚ՔbՙiWYZcژa[[YX[[Zhߗ`[X~Y[Ziݖ\[YY[aѕ[YpY[\Y[ZqӀrZ[XZ[ZaɒnZ[Y~X[Z`[Z_Ɛ{[Yuc[ZZW Ҁ wZ[ZZYYXXY Ղց؁·`[ ZZYYXXWWUbЍԂՁւ׀؀ɻrZ[ ZZYYXXWWVVURtρԂՁցׁ؀ـ ˻][ZZYYXXWVVUUTTQӂԂՁցׁ؁ـŴkZ[ZZYYXXWWVVUUTTSSRRS΀ӂԁՂցׁ؁فڀ̹[ZZYYXXWWVVUUTTSSRRQQO]ϊ΂ӂԂՁցׁ؁فڀۀϼd[ZZYYXXWVVUUTTSSRRQQPPOLr҃ӂԂՁւׁ؀قڀہмxZ[[ZZYYXXWWVVUUTSR QQPPOONMK҄ӂԂՁւׁ؁فځۂк^ZZYYXWWVVUUTTSSRRQQPPOONNMMLLŃ҃ӃԂՁցׂ؁فځۄ˶kYXXWWVUUTTSSRRQQPPOONNMMLLKKIYՇ҄ӃԁՂւׁ؁فڂۅűYYXXWWVVUUTTSRRQQPPOONNMMLLKKJJIGt҄ӂԃՂւׁ؂فځۈҼ^XXWWVVUUTTSSRRQQPPOONMLJIHGF҅Ӄԁڀفځۈȳ mVWVVUUTTSRRQQPPOONNMMLLKKJ^dQHEEFFEL҅Ӄ ÿڂۊѻ WVVUTUTSSRRQQPOONNMMLLKKJJHp׹rVFB@cӁ ܀ۋ]UTT[qrTRRQQPPOONNMLLKKJJIIHFຆ_EӀýڍȴiSUdlPQQPPOONNMMLLKJIIHGFGƜ߃؊ʶ {UhRPPOONMMLLKKJJIIHHGGFFEDNþӿk[ONNMLKJJIIHHGGFFEEDDAQ̋*ŷrNONNMMLLKKJIH GGFFEEDDCCB@Z؊?xpkihijkllmnnoopqqsv|PNNMMLLKKJJIIHHGGFFEEDDCCBBA>mÄ}rjffeggijjkklmmnooppqrrs ttx^LMLLKKJIHGGFFEEDDCCBBAA@@??;‚6wmfcbcdefgghhijjkklmmnooppqrrsstuuvuvwm_PKKJJIIHHGFEDDCCBBAA@@??>>=<ĂƒB~qhb__`abccdeefgghhijjkklmmnnoppqqrsstuuvvwxyyq_NIIHHGGFFEEDDCBBAA@@??>>==<;BłăÃUwkb]\[]^_``abbccdeeffghhiijkklmmnnoppqqrrsttuvvwwxyzzoXIGGFFEEDDCCBBAA@@??>>==<<;8GƃŃIqe]YXXZ[\]]^^_``aabccddeffghhiijkkllmmnoopqqrrsttuuvwwxxyz{yfLEDCBBAA@@??>>==<<;;::996Rׅǂŀ*yj_XUTVWXYYZ[[\\]^^__`aabccddeffgghhi jllmnpqrstuvuvuvwwxxyzz{|qTDCCBA@??>>==<<;;::998874b+rcYSQQSSUVVWWXYYZZ[\\]^^__`aabbcdeghkmptw{~~|yyzz{{}x[DA@@?>=<<;;::987664{'ǿ{j]SOMNPQRRSTTUVVWWXYYZZ[\\]]^__`abdgkptz~}}| }~{!|}{aC@@??>>==<<;;::9987BL;423445#ƽtcVNKJKMNOOPQQRRSTTUVVWWXYYZZ[\]_aeiov}}yvrpomnpoqqrsstuuwx|}||~~dC>>=<<;;::998875Zš~Z=1.>‚~l[OIGGIIKLLMMNOOPQQRRSTTUUVWWXXY=Z\`elt|{upljhghghijkklmmnnoppqqrrsttuvvwwx{~xst_@=<;;::998877665nΤyMZ߁0CDFFHHIJJKLLMMNOOPPQRRSSTUUVUVXZ_fnvwqlgedceffghhiijjkklmnoqrsuwxxyzyyxwxyyznZeooU=;;::998 77665541ׁ4EFGGHHIJJKKLMMNNOPPQQRSRSTW\clwyqjea_^_`abccddefgijmosw{~{yzz|jV{fA988776655443323EFFGHHIIJKKLLMNNOPPSY`ju}tkd_\\[\^^__`aabcdfhkpu{~|}~{tSqo?88776655443322104 EFFGHHIIJKKLLMPU]gr~{qh`[YXXYZ[\\]^_acfjpv|~zwtrpopqrsstuuvw{oSf86554433221100.8EFFGHHIIJKKLLN^p|zpf]XUVWXYYZ[[]_chnu|~xsplkjklmmnnoppqqrsstuuvvwwx}rWL55432 1100//.,?Æ4EFFGGHIIJJKLLMYqzpe\USQRSTUUVWVWWY\_eks{ytnkhf!hhiijkklmmnnoppqqrssttuvvwwxyy~lUl6433221100//..-*C˅EEFGGHHIJJKLLMLNPNOPQRRSTUX[aipy~wpkfcab(ddeffghhiijkkllmnnoopqqrssttuvvwwxyy{lQ?21100//..--,,++(VۄEEFGGHHIJJKKLMMNNOGPQTX^fow|tmgb_^]^_`aabccddeffghhiijkkllmnnoopqqrrsttuvvwwxyyztRoU41100//..--,,++**)%`"EEFFGHHIIJKKLMMNPU[cluyrjd_[Z6[\]^^__`aabccddeffghhiijjkllmmnoopqqrrsttuuvwwxxyzz_WjH/..- ,,++**))(''s\EEFFGHHIIJKKLLPk}wog`[XVVWWYYZZ[\\]^^__`aabbcddeefgghhijjkllmmnooppqrrsstuuvvwxxyz{qPiki2..--,,++**(&&'&^DEFFGHHIIJKKLLM[vulc\WTRRSSUVVWWXYYZZ[\\]]^__``abbccdeefgghhijjkklmmnnoppqqrsstuuvvwxxyyz{eOqpkF,-,,++**)'AfK5(##$%/gDEFFGHHIIJJKLLMMNONOPQRRSTTUUVWWXXYZZ[[\]]^__``abbccdeeffghhiijkklmmnnoppqqrsstuuvvwxxyyz{y`OhyYwg/+**))(':մd>*!:̀mDEEFGGHHIJJKLLMMNOOPPQRRSSTUUVVWXXYZZ[[\]]^^_``aabccdeeffghhiijkklmmnnoppqqrsstuuvvwxxyyzz{zdOSbh_Ri~C)*))(('$EˀvẊZgDEEFGGHHIJJKKLMMNNOPPQRRSSTUUVVWXXYZZ[[\]]^^_``aabccddeffghhiijkklmmnnoopqqrrsttuvvwwxyyzz{||r`VSZna*)((''&#EԃmDEEFGGHHIJJKKLMMNNOPPQRRSSTUUVVWXXYZZ[[\]]^^__`aabccddeffghhiijjkllmmnoopqqrrsttuuvwwxxyzz{{|~}{{}x5'&#[iDEEFGGHHIJJKKLMMNNOPPQQRSSTUUVVWXXYYZZ[\\]^^__`aabccddeefgghhijjkllmmnooppqrrsstuuvvwxxyzz{{|}}~N%&#\iDDEFFGHHIIJKKLLMNNOOPQQRRSTTUVVWWXYYZZ[\\]]^__``abbccdeefgghhijjkklmmnnoppqrrsstuuvvwxxyzz{{|}}~~j*&#qjDDEFFGHHIIJJKLLMMNOOPQQRRSTTUUVWWXXYZZ[[\]]^__``abbccdeefgghhijjkklmmnnoppqrrsstuuvvwxxyzz{{|}}~~|9%&'zjDDEEFGGHHIJJKLLMMNOOPPQRRSTTUUVWWXXYZZ[[\]]^__``abbccdeefgghhijjkklmmnnoppqqrsstuuvvwxxyyzz{||}~~N%&jDDEEFGGHHIJJKLLMMNOOPPQRRSTTUUVWWXXYZZ[[\]]^__``abbccdeeffghhiijkklmmnnoppqqrssttuvvwwxyyzz{||}~~f(&%.DDEEFGGHHIJJKLLMMNOOPPRRSTTUVWWXZZ\\]@^^__`aabccddeffghhiijjkllmmnoopqqrrsttuuvwwxxyzz{{|}}~x3%&%1 CDEEFGGHIJJKL KJJHGDCA><:7545 79;@EKPW\^`;aabccddeefgghhijjkllmmnooppqrrsttuuvwwxxyzz{{|}}~C$&%&$;ǁCBAA?><:86420.,+)('&%$ %&(-4=JU]ab7cddeefgghhijjkllmmnooppqrrsttuuvwwxxyzz{{|}}~S%&,5'##%#Oۀ /.-+*)('&&%$%$>&*4EU`ccddeefgghhijjkllmmnooppqrrsstuuvvwxxyzz{{|}}~~d(&%6\;) d$$%=$%*9O`ddeefgghhijjkklmmnnoppqqrsstuuvvwxxyyzz{||}~~s.%& %8ݷ[%:$'6Pbeeffghhiijkklmmnnoppqqrssttuvvwwxyyzz{||}~~|6%&%)y%$(=Zf1ghhiijkkllmnnoopqqrrsttuvvwwxyyzz{||}}~?%&%%YЃ%6$/Pefgghiijjkllmmnoopqqrrsttuuvwwxyyzz{||}}~I$&#@%5$)Iegghiijjkllmmnoopqqrrsttuuvwwxxyzz{{|}}~S%&$/%4$'Ifghhijjkllmmnooppqrrsstuuvvwxxyzz{{|}}~~Y%&%&x%2$(Mghhijjkklmmnnoppqqrsstuuvvwxxyyz{{||}~~^&$[%&'('1]gfgghiijkkllmnnonoppqqrsst uuvvwxxyzz{ ||}}^$#$$%&$W% #Ǚ.ix&И"~f-֗|O-ҖhN!•_<:ݓ;x!4Lu~,z@,k߂i߃ވ#(ÍފWD߈ފ݁k߈ދ݂Bߊމ݄'Ê߈ފ݆-F߈މ݈dq߇ފ݉߈މ݋?(ˇ߉ވ݋u M߉ވݍ"zވݍH /6  ނݎ Hͨ}L' :ݏ##VZ rکh5lߎ A7xK躇ڃތ k@ Gڀ+ $&ɽӨZ 1ъ ~|}~ J!пzxzz{||}}~~4 dʸ|wvvxyzz{ ||}}~~ jB zİwtstuwwxxyzz{{||}}~~ rD  ϼysqqrstuuvvwwxxy zz{{||}}~~m6  ɵ~uonnpprrssttuuvvw xxyyzz{{||}}~~X  .Ѕ xplmooppqqrrssttuuvwwxxyyzz{{||}}~~ r1  D κ~sliijklmmnnooppqqrrssttuuvwwxxyyzz{|~B   cDZxmhfghijkkl mmnnooppqqrssttuuvvwxz|~ P  '   ӿqhdfghiijjkkllmmnnooppqqr sstuxz~ X  9e; wkdaabceeffggh iijjkkllmmnnoop qrtw{}{{z{{||}}~~R RǕ`,>ځ ^_`abccddeeffg hhiijjkkllmmnmnpsw{~|yxwx-yyzz{{||}}~~q\fpqC j`aabbccdde ffgghhiijjklnpu{~zwvtuuvwwxxyyzz{{|}~oV{Z  t`aabbccddeffgghhihjknsyzvtrssttuuv wwxxyz}Uqg  `aabbcddeeffggikqw~}xtpooppqqrssttuuwx{~ wSY  `aabbcddeefgp||vqnlmlnnooppq$rrtvy|}}|}}~~ rW4  `abbccddeeffn}|uplj.kllmmnnoopqtwz~|zyyzz{{||}}~~qUc &ą ``aabbccddeeffghgiijjkmnqty}~{xwvwwxyyzz{{||}}~~wQ# <ׄ ``aabbccddeefgghjknqv{{xvtsttuuvvwxxyyzz{{||}}~~ ToI I ``aabbccdde ffghkotz~yvsqpqqrrssttuuv wwxxyyzz{{||}}~ dWm1 a``aabbcddeefhy{wspnmnmooppqqrrstu vvwwxxyyzz{{|}~  }Rirg  ``aabbcddeeffoytpmkkjkllmmnnooppqqrrstu vvwwxxyyzz{||}}~~mOqps1 'Q2 ``aabccddeeffgghghiijk llmmnnooppqrrssttuuvvwwxxyyzz{ ||}}~~fOhy[e  ϫO$  ƀ` aabbccddeeffghhiijjkkllmmnnooppq rrssttuuvvwwxxyzz{{||}}~~lQSah_Sp. ,Āݱ{DT_``aabbccddeefgghhiijjkkllmmnno ppqqrrssttuuvvwxxyyzz{{||}}~~ }fXV]w\ ,σ_``aabbccddeef gghhiijjkkllmnnooppqqrrssttuuvvw xxyyzz{{||}}~~ E݇_``aabbccdeeffgghhiijjkkllm nnooppqqrrssttuvvwwxxyyzz{{||}}~~ AF߆_``aabbccd eeffgghhiijjkkl mmnnooppqqrrstu vvwwxxyyzz{||}}~~ l^_``aabccddeeffgghhiijjk llmmnnooppqrrssttuuvvwwxxyyzz{ ||}}~~ $ i_` aabbccddeeffgghiijjkkllmmnnooppq rrssttuuvvwwxxyzz{{||}}~~ C __``aabbccddeeffggh iijjkkllmmnnoopqqrrssttuuvvwwxxy zz{{||}}~~ e  __``aabbccddeef gghijjkkllmlmopqrrsststtuuvvw xxyyzz{{||}}~~ __``aaccdde5feeddba^\YVROKGC@=<<;<>BEKRZbjptuvuuvvwwxxyyzz{{||}}~~ 2!_^]\\YXUSOMIEB>;741.,*('&%$%&*0:GWgqvwvwwxxyyzz{{|}}~~ J 7؀ 9641.,*('&%%$%$ &,:Pftxwxxyyzz{ ||}}~~c  yF! O$% $%,@]syxyyz{{||}}~~ x ٮyF%$(<^vzyz {{||}}~~  g%$)Dkzyzz{{||}}~~,Cʃ% $2]xzz{{||}}~~ ;&%$*Tyz{{|}~  J%$(Ty{{|}~ S f%$)Y{||}}~~ YE%&'( 5nzyzz{{||}~~ [ A%t8mk@ic08 jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2d#Creator: JasPer Version 1.900.1R \@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP ߂xss7d*l2F-:|"ay_z!%,.|;F(F66|C߂'Lܳ[87s4`Zm OEjb?f%ow?AV,S v GϿ߂'s.ӈqC[dy pF]~Q.`yD i9ԒƖ:%"횳sWtўlYx6Z#k$]cGX`#y Mif&--4lI|G|(Fc%dt-unC}pi9+UB?GiAܫ_\b~aJܕݯV% W4q<29?n T~+ kXPQGP/D0k_6Ÿ(J͂`ю.f l4|:]޺i;wNܒHfJZ?>Ө .`$W'5^h 央5]\TT%\;YU YY3!}& 3khŪbށ[^ ?<$b..d cW)<4PJTݺk?uMMo =PGן>v&\-΋“?:}QMSro \& fU8ɱb ^(پQP ȏ`T`\E2W)Q MIz1p.NʆH>hy8-W@xtA49kjfC`fNE̙S؈gؠ MH2Fk̽>4:ǻ,_i˞]<52T n$6coZڐSi?zFp,R]芽;y^>KtTX<`/нAw7^Y8t>i fSF@S&(-U߉%&0# l{B'aͥ%d{[c^:nUZ[D6#~FŒg0nعO (XgX^2%u>u?:h"=건qOL;=݊n?~CćJC$ vv"5R?pud0`!݃Pr?K?tey{dдaA D9@S>iK )&a1Qgb5y[vd肊!ȷmZ%T|~+cKIVh(7:w+<6%:j Pv  `tgh_t;~ݿXQ Xk[ʛt9)7dSĭw|ܼBϡcAiReJ킪aU?lS9@ŒrqPA֬J9^_I7ˀ8l/ؗ)c/2(F㉑!OU>RaH%ld1iNL5X[~0} 5خ>pc Vn#}6 b%sוã.J|{ib3=>~Qx6,>Y؞GȠJ˰4H_ ?>hy7S`BO9__tUq{7NZReF{r DUA<.=`-& uRS6>BC?>GPLh6.߭y;BF2bL[\7 v-!t4GACفō&{tجiBU`G C+ wH A@ ZU2xb.]y ˅I ]t VkQV<<ښ1TFB u~m*gqp&Qky% 0rnοr~51u56 ! sCc_{5=g,>4tDyŊi=]ϢyM|fp<<QPM9ḛ$lAʱ`uͯp:fl&ӛT07=U(:[jZ(jE )Am_bt%Ѱʆy^B C rUdCf&NbKPK1ƶ7 +nO?DHeB$eTmJ{6Om}= .fN,.<&#6wTO~5̍yVfZÛ=Swn\<>0jQwI2 [A9]Yn`eڪ@W!Q' 2ḧ/zq",xs%T)J~* #;̈́xo-KY^xO~U026 #qE-t4,Qx)" -TD*H9S0+;]z*pJeJQxh! /ׯ32Z\ 8Xf,|J[ks\%@z3H r[7;T#!iAILp@/R-/T!og.4}N P2ea:J 1:dIݳ}(w|Lo\O7dET=GZثQ<#}ݎy }P3cf(I5hNEÞPqY"8#$wy F 'W0JeZ8l lcsi%ۃ<602UV]Fٵ4aO#˘ gobmV%lT?dWo*RǨ cR90ݭqF4EWԊWZ)j'2!i3>pT#[=`geYvfQQ(l<uUd]كe'fO :^ bC 9KAYUS,+ Z خE%WXT~P+czW{<Эt&d KE}o+[x/NJDx"F uKg= ' ('JE ơ,}V` P PG.)iR\OJe+q5bչI5b6bcK.h]mhrG|(Xo3dO yФyz l0CXfW+Zxmiп  TGrZ{p&\)oW76H?f׮#/Q1#IN41Y!X@E;Gt2y4AdftvDswܙ͐G.Wm8O$>OrQ%&t&> ^~ω2DW4w>b&b r( ^Eϝ%R 7݌ܠس?bpIܛ=۝OV(Bf\c20'aАmaΔu)l, 6Ek[rn=CWD1ڧ Wfxe[łsY,UP>6q=1zo?ĀN,{KxVz=W' Q>2 f[144^b:ǎR3Da`gd!@Yjx$(1cNhdn@p#ZF sݟX]>e;NH1*lgl@`(>^d.7 67; ,Y2M߬ ɒkv~1xh?b}rǩH6Gx]ڒmm^0p**'~*"[@acQFWg;w8hZ;㡬>kp|( DoBJ(3WZdp"r:QΔkr D%݇sP-/рN^@È:g}TdXnB8@K[ t/Iyu4[QߖØj-)AhNk4U1R"C= my# NRe _lKCas쎴P5w4@!c* UNTޅ+Xk8 UG=dľ?tuC[K]Ig, DZ ~3{T1r9v QM|Tu3+B57k X0DH :?hj{R`]d}]}+E<#fI#NEB&L]-m\a!BgdzQ[1a$1v ApSڧ6%i/qӼv].ZQ|s`2hVt*aAhP&ٛWDB~]L'V]M8&ʼJc(Gn@ͫ }|xO~U0ԉm4!X7g>=Qm[$Q#n_kS#`f zےADl&ȑ'wcgFdv2O}+; 3;Rـ>$\c( )jI8$b{#O<2GS0pDq!*?D̠q]y-5.I_#P_C&F0n t+/*|3|8ķ"(~;oM @@u#HC߲dO5!{;ܩn`Xf =6DZ_6Ą;ypjk}3[b&3+uuqR`(Lο@L%b;٨2b'U%, ow^fB!z!`cǶX;<-uOߟAe9@t,T 5276\7yn{A`*ODCɸm_4c39f@ۍ. <ދW{$5[1A&E?WEFuٍR?,č(V/ߐ :~-:z8`͓TsU\,֬ZsGsɘdKl:FKڬ.U@)"_o_- w^|+ mb9eU{z\ux|V{ pe H=pЛ(oIrļ/ ̍O>""AwGX]8'&'TK{W}7;8Ssf,5h+^RbOu8 YxH .)OnHZ309N%r7j}s{q9茶K a_r:$@>O HJ0IYJH Kb>a& xB_~%@QúzG<ܵFʞV^#d{4wf XA]0í;[''-dJKl(.Rѧb.L:BUz^o;.0h/hZtc*h[dV}yB{|v)NMP{h [gʬKW4m aŠE ir7?>b5J@[ٵ?w'_i+|A FɟuTVG|eK[N tK)q,a 2qx&HgѢiZ㷵%-0Y ^V5|1S2?^ǂlş֩ڍOCB~5}ZOaCg!Um@6#BBD0)8lDea1 g2KDfV#{D m7=rpwze7I`EC&x0*L OyoS9պ+Y4rۖkR,βE'gM22F5֫O1wIշA9)̨F%/4|g" Y;\Mڟ SUɔ^0%l&ƖT[~.C '|b3dEtPW(iSΞ?x{O h3DǸRތzrz,ƶ'<; 糨Тntlݖ`8-*b5of" BMn UP{X)"lfu*oCS[)r31׾a;4f5rQLo:[Tuta Dx@]qX,Yauf"e"!PSKGS𗱉H8qPNQ0ʩ)`1JUiaV>Ga&i>SSjjcNEҲOb)/=-"d`b<EҔՂ_:rIWד娿Hqe5d\'ݫ\OݾiPNJ)FLN0^U+l F!ZWdeP笡gn=@m[6^LNRuϰVa2M+-[ gLb~SVsU͊b5zhT߫oܽ۟d悻y2+fնŌ bc3kdQ( `J>Z%&i}BɎr+<Z^q /^ ihqxAZWS >BrAfx D;$\t%)@Gwe*P&YʊqqCEx(3h"肚XTFɁR<2L fT'U13dqAORY jYDvWOC"W?Gvry3ðt*5hcla-1^6/[knĻUj*zҋe7c(owE{;U:CvaA|MZ|%?;o %B.<▛?v%bɠԐWfiHë_2X =ÄX O*9ry,/%*n$?kbk XȣLB3lz>,V'= B/1mک'U=cꑨbfs`+$VN+ippGȺ^:)u{c+%" P.#T`Xq 0E&2 ^39F^QBې^l.GۓMe$~q7 Fy)uv.r;]CN/l`O>4+:} wsYgJ/E7P}&J#wzNuZ(K`ox)ɓTu=xd1E;ou ģ˶$Gw F,DYp)tKJY&ղv]҉rn ]OT R#{΅qû2QF/'D"3MX MYC~vk\52_x@Z:'0l@b9MZvrErUY; ,# aXY@S²+ z678s`7b) ٠ztSͣU|K>()ZiA_=ȚZr2ۑ}nգk ѻ<{hE>3BLc-jm[< Cx3?-*jd-i~3豬R?o&׀ qӎ_$4+9~axy< ΍jUmOM<~x*E^ pp;n?7,4Kz&*ZZ>2}* `5)(&XjUhkk[흎|ed*+ tߌ =S6&ӷseK}֫l'3.:9xw(#g^\]6voZ[ t ^xnNYJU$.ؠ Vş髀 3-h1"Lns/rKfX c8ypN),|6ƆG@aO9\ ^a0jؿGnkN-gY<3uFe#)hsB?91d2,Psu:<'M,;bS0-='YTswLQB%F~R7ʭb S7O qIaٽ!Z|г]}8;t:2#60Sj5y)<)=EEkzW{Sy>ޫ`Ւ1Q¤wB #_ pZ̐uaTǏ prR}t}e+Iq;Zۻũ @uV]6 -#*]5wSyC}!{;kFZ'';s1کkLlPGYV{Ovɪ಍.| бQΆ'q" u~v@Q[1efyX ʿ9R45R&4@AkQ[W{6x'""{zc?n5,3"rL=&E̷`j(Yq4҂Ck~=֝X-^}@8CK':pUj_ gtN^Ƚ#yw1ҘrVl$r)G.Ȭ X"ptpضM$aw/%m9 v\:ðzOGP$tt`XV;^UO% R)t583SsvMʮ'Hyh3񿷒ngWu]8FfYH*?Dp'c$Qݛ" '='@HAcpB%^P6ygK6UQofEsAY(\fYcHcF5(naĺDhF̌YW5`0`hrjtp|CjOJO_WDsޞ*M񘗁#ܨn əF"9 q a.~cGscg>Vz D +lHuGMn)Bta-q?Q:l.@QA2;iO ͷucI tWҲu>>v;Z "k,d=:U8i0bț55 CLi~`Yε[m MaБ=u 怬(zfQ.81T p!5b<?QKjP4j4&ۚaպiB R{NrJ9`z(:=4m5sH8 j 8 F%(JHo|22Baq ust3 ٰڃjCjX.t,|.)c<E aG*(m_֜jВm;2i!"[}}UwpH"oGDVM ~+yi=Pf~] #8i-֐b7qRKe`'b_t-"Ӑ, .w7xL~$t6+?#fCb`GEiD$S!'f6u-M0>9ZhR~Pf !͇O7'w,{,89΂nvҍ/^GkdN^c 62AHjkczc2x_ozJs)Z_5kif5vs` MH4P:.!)PRnE}m3L398f:嶈hI[UQ ,vyDÂT1*LWj|Ce$<`gB04:CȐ⏛CVYd{נf_ [gŎ _ WidD}"iHWVĠ&\{mL~ހ P"k$7# I.VG_FKBۇWza*S>"93M1 plG=3<=^6R7ȥ:*e<>=[PQZnޅ bY!8ϰg]yn6%RbC8}a4@ q>&| jyaE")-#ANqjv.0YؙRX$z]*ݡ,s`Ȏ%h΢sSN6Qh jj3[h|>~Ypp񜼑!M@C}kݦ2xh0No\ b?`LkP4E8 m$#6WMıK]h^dRUTnqJ7n(ўWsٔ+y}-+dv/?s~2v\QHdX;%@9HtZ/)&f]VU0#$ϳ5}+Ņ*t23x~fQg4CNgbPHݨE@?y 0`1"KaHӕ0V@$iUsN9|6HAk(wsnt‚N/}gk|CZ" JcD Ñ!2R,l~ -;H~I ׊Ფ_ yL~rMlG!.8o,R YҌ^t Tl!oS%IgؓC)K!qf@љeoDŬ惗l`P/rP1eɽ罷LF $#^?~ڔʟ;kHKda@:*< JD3d@?P^;||od7ib}[}|Zԓ#T*})M n\fX 49J.>-8f F|;)*Y\xODGSy0.Nj7t*_rBbWQF\'28lQ4fI[N#bϻ¨XURm?I!x'>{JI2[f4B­7P~85{C dT萗Q, -[fEx]<(|y*: AyJmR]fstbͩ'w"4R$yrI\lPX̂-|l21>$ڃ2ڭeτa2-Nh_"vɸ cZrvԆKs/%h0^l_UconJ0_l7Fv꯰ns|‰s=٪1,x6<$Exbɏko3?g^8UO/Q11ayo Jm);:tRb~ܫR' JZ@cow Z׍̴ #L2'\iFW/<)6RWtl")7/ Qm0;w-ݼ4= ΏiZ*ٴ~"\+tW3UW%DD^j^ X몕k-_Eswߧ!G#펲K+8ԞD}:Yyx!BXsT#ӂj@Ft'~=|ǚ윰w|\ۯo),k*,$FCC,{iŽJ|foʕ!a(-Cݿl[*@bt/fd@xT7ȭvyU\ju Ձr:X*GϏ?OqKYItDO:7ix=d;imTi{ENx5Z)~|8/rGw;Cwy P,rB`ub&YW,ؼ{ѯH(MwaNMY*>` 'NV?4DNwI$|ݕQҖюJU `xۢ5qޢNgR[g7s>(riC_kea+O$k<:rzc[^}?I.ѕpMR \z1@ rs I Ov#?$- ΆSE顓 .g՛;U 4ߺ 3dMG bHu ڱ&Z~]wYpZn\ ]R\=a8we=9+){wr$dYHzTCG"DWXZ(nhVgi'a7LF ,!~ QL-U J1XW >V䀣;Fm:;DW測 ]T2jqy3V] T*l;\K;IIw3 } PQFAֱ[ FyEBZ@ cҒvbs>.qݬ5}0r;Q=׋ioB63ͻ_,r\P!Ii4 )&r^vس|%=dsDKu&&e rt<}$p*'vY s)@FR+2(W}nwvGdȻ}>s.ɪTkC_XM}c@e"eK{]gSf:.pc͕UNm@uv5sbkpC{(R\(nni@xMHR鰝tyI\wBy'>CfthTB;tl]1w@?`+_V$;FmGN$lupl`Y ׺frDre31ˑ6QY!7YXۑSXXLMOq|e:ĎQ͟ Jll.X,vD HY.~^OoMQ(fd yBS9מ"Ow( 'G-05aٳʹ_;=%dP^_R.d5${ LM]%伳v" [ jm%>angb +9{sB[cHMe|UkVʇj봍u.?p[ 4jBۘ~㨂cd4rX*sI0xeڨqjPyV (r2,7O$:}0!-XFW-ijENTOM~ xEZOFutKԪY4fCn"Q%G}uz[[R]Y1j/nW~hUQ_ WDe#W1 W7_ȨBCiRl$L@o Tmt6 wC\DYD;=SHa08SO2]v#H ?G+RC3bZQәEa@@\ЊIYK`\SıN%1'$imkۭuᜰL$)Z5w((}\Ϭj?,$uGxkkpY>R\g>c\.Jݳt*n6 M_K\F*ii0!=fmiݫ' /Xj3چf5fעn,.'Tx7d` +3RTIhTH2SHa'WՊe'{D⚜$"*g~`\lz~71l?X(Td4-;r]*eŪc*9=gJ5*ĄV?znTEBx ,"Fa* #Q^`|*zvrXiY,:{U)eԜ7|.4Pv6-<vFNk8eDžhTS` \k;;H*;h瀯2fo|XܢL T~IbҎq~:K'ig\!B>Cz&3$4pE`碏5rW%;3L.@]A3[ d֐6; o1"s˶X|pPshMX\\|ft$  *B <.g r-|ݕi]H=6ţ~ =r7߳J23FJQWꟵ)'Uqgbtx `RXT}{zb?Z3/cY {2 f1gi" bơKQFAQAb΂e>>Nwyta`yJ4Ep.lhE}Yq Hjmv*|i1&,C喉[kh;/ϓJ{LR|ν{Vzכ ½2|+jr&"ݢ ZIU%Δin*?j>kmZ^ کѐ%a9\ _ܚmD<*#l{̀H!5E4{ [j0y0Sx>؂(B: %TVYd*['Uf:) W-s.vg$ƮȕVИ"ih]gӲ!Iǔޭ[ fkR;њ - *'(6+3fD"ݕWa.CU͉K<\<;lQMao4j6 %<.v}r!_tQ]~JZZ. X;e]t…"4wur&€KJ0pH:*EnLؿ8F:o׀Lti/ U'ɫcݾ~~|ߏy0-2S _ xz&󂸼Q}@}/r}htG., x9vb-/` iDՈyus*^4EyiWd2)F.pEa֌%zdx5+A+ 򻖷̢Amp@4kN6@r{hm nf焒8// Ny4!twa?RH4 (} Y+ӣF(o9m! #I?$oLّ\Bnܾ|0$3U*eU/ќٵjf/4&d?q @֋("82DCW8oպD[!C6GF[:_F%a v9M3xM)#y㋪ 3s@B.|޶ѠG;[`*QHeٯG<#tPS!L[ %ՙkӈ{LRÚ`3 0Q0cBgh[,}W*v^B_oEy3u)[ ᎝!/VJ[{q99:s)/C;k`taC`/=:گ45ƛ;ji?Ml]T .kӯXϽQ7K6‹9{f/Σ\8OU螐`ѦB~b W"WZȂҲI%`Wϩ77mNW]Mye>iܑԞ1焲igPZ Ǐ%?ݔ z ]E3_x JPA(\>8p- u r\> Z՛W]]? pZV~i8aP-F*pyX]4e_KV E4|WEmRq&6TSe vMI1M ԦB O vjHh Nn2o]+:7/z!?~y*Iƭc4e/kfcTD;MZԛ\cB*'W! dCwn^ak)+[мe r/d<~$?rQ9Js/Nq_0u}0R~GpEw+t2NJ4ltv]#HЫO|AD>eI,ԓ3JxKA)1l#6 j-%ČR9H e/1iv@ JJhp=Ф|EKm _m%Ar A j5V̽jt}pN5NkR<ਟ\m:AF(%? U(\h7ҚfJjhۓ[okfV:.nZ|؉ve ދ-o4b˯i֠vY=0XB oND2 "sjc-iW>\ Ep*>뜀Q MW@= mJ2(l [FXU/~̽4qq|>OE߷ &WlߖկqLԓ#:aI5qIw[L7.NjEViVVb}\a?!-0W'G?w@rus݅ f̖JDb*NCq' 2pRxX[=QΉ4딻{PC+͡d͠aՒ[AyJhM{'K(ji]ydM]m>5?hNP-ځleuEv|st2!!!-kqFsk/kebrBfH0=D "@O?~%yH3ŊTQ͝J\J8aazI[c}gBtrܖ"ePM<sXh^<7Nrmz|KLcn/?6Թbrfb=AV 򐔹$$cz:Cs'~h{>H,+h4 }yS`?aӂwB8ߘ䪄3GˊezbeFo gN{sOc<цۭT82J ;gGJqނlGDP2{4(T)}QyW9 {-hƩQD a$/44B P-vIqK !!QBT."_LP4E$(ω¶IfЅ !ӟw=%k W= `A3_&FF IoxCM+A>ٓ yV7_ Y>}Zߨ>6ba%o}*)a拘r߇JxT|AF۱ߟ >AA[jz"A^αCO㺤6(LXsl )lQv Թ]sNKS fgdϔ%\'Wݻ6̨Iz y|K70C5pa{uF wT4L,3gnd푨HJo`b*#ɬ>UX.C,P>|6Jf|CnlUe5L}$$tEdLev3<+o(}_cayYOE0pv{0u)׀W@_+nj庐p[ '!, e_-A79tɥRF^b!{u>qNe?R\e9rr֍ 9́%[+2+kI>>E^R0m-KQ≞+(B)VGe#Wh̑lk=?}C)#qEawyN1(nhS恎`K_6N.ИEjhʔi i)O \0$::P,8Zw_fvS l^tƼ9]_a];N. rtQ~|Ql pezx)Rv]M Rԓz.e%D"0h=FyiWSC,9LSodF4b!;]uW?^ʃ;"p7y}ʃ gW)6-) WSފMY:"wD<>W|VUiWw=1TS*GMxҜҥ^?(nns݇TAo1K vV7\`nS_Odvqq_ Č:=Ĵ߃r=*>"`==ַ:-2#K+PBPmك+41Lǀ˿s.ro֔ C\Cy@"TC>D \:R`;h4`*Y` szd#_9c"ֳ IG9w8mϖ17qYWޝުÞ7`G( :'L!{&@x5JSb߃ gnX ލ\K(XsNy~!,[i)PH]/W]:%\;]L̋#f{yCHzl3,r^ 3TlO?25n@l`FK])i[sܶ.*r%3ǛO eA$am[R!zdY6h`CDMqvJ[ߜlbʬ8廊V.S>|A Ae^R7vwv)Y|zq{HOȑ~qVb,EaoTK:A'9r\cb`];B &f W~f &ZCd9hg=v)I|Df]6ZΧjDN$\f ggC.a/bFkL~:c_ >;Z97! f5Y%% A-Uh򞥕nԉ?ʙ3b;8U扤$e\Fd㫭7^1jq&Ґ&'@j5F1=8E( >>lʋ{<pɂJ,YI6٩8|= zLtk\1SXu*F57y`m`[)=6> _#K&5 }8Ļnhgl}jqbe= {y~q"8.,j3dU% Iޭuթҥ}MһᩂxgCĈ-E?%X\y <@|Ɵxd5Ys g~w:mGzi=)æu<)bGơr,!Y.0+`!o/3m$VX8ϗ%1KbS|TRpҚ$jaj[p"~@J \{I?6Km!_jgW"b5&@ކXy:n*nCII#1==ܱT.Y8" e]Kx ۣ+zrW<ǚCz ΁sYWqBTF3<'. ִ0̇m;^v5]JψmW%zU˜(k[_&E1C i%c/"acg0[)|p@BףּM?x@W0}Z ~[Gp9NW!@L;qEu$v5>7]"1Zw@}T3tճE'#~5%iqlJL& .NCm`_ipiΪџaC䵏]@<8zF@vV6n€ZCphdѺ(օ1-m_\*_? )e*/#Afxʪ9jSR~cLp_c$Wy)&"0Su/\PDOWؓ;Q|fanS?0?G1*N.!Q%0ӵĞ:1(za\jr:w9llNuՇII7xoKA!MfaI%=/<@bc8r|ER!0EnEx(/:`3v#w%AȐbHQʝoނI+$ y%]S0HDvBl9B]9e% !MRTfb[U@[ރt>7䕾GAF~> 1']:O+gd[O}.k7s*ЬBQti.մ34fQxVHNiQ.sHd(>9;py}>clEyz'c!ҾjP vSWFpoڌgo~υ(jcnƓ$C 빖Z̖:d#SP|bٙXf=B.Ю Vy"LmFm(BV >n钯f/u/p/ fMS!ar?#qoi1cĶ-j/%7YjJjn#um1#0_˝DHd-ROL/JkT6$aSx\~l \^[1(ceQ7}E+U B*:.p88+鲘<q/G+8*Usbsj!©*7!ڬ,͆>omryرtNƣǨ-tbZqZCE9 nnЌkQW:[ewIs.U)\Fi!l7eL;X /ɖpL^lUkɇ9OdV/Xi?&Uu>a0ĵOz kOl3w#ϨF0RSAnk .f 2YA~|6gJG,'QZN:QdwQOc棫@{Rclb`_Vs:bnn!(&vVEqE2}90:~9opԌ;wc%jg{ P!B,7+c;GRӝ^-?먢E ݰKn.Nd[n {),eO#u~vRX||+p^ 1؂]tBHL.:~uִ۝Dd[3ݗ;v$ @+RW.mzAF1J_wEA1Y`ueTM|"QJxC0V2_dnAe=V2u/;#a9 jݛ|f s.f.;egb} )J/*ś2a%SNT{ t[k2 L.-u5U@#j=gl,źh KOE'w:`l)WN1sF"i.=cfVH.L쀉79<ĔP 2{2LJ.Eۀt8]2EdA[HǙ+'BӞ*dI >'= @ RREqީ:ٗ iu;:L B;Z1P--h.t{0g-Tq"jw]KIp%[n}S31.8P?w@egsX_Z)6,>`ZpfI"#;x[ݓO9To딥egRWe49{SD:@/,+51#hմOSNCI\T/-W $Ą G&Y8u1c@ՊNwIA`~ghw-ZFۜ㫝?d4 Oʾn3ESz2 conpWNAIGq,P($ѫc.La I(u}N i)夢x~*خi 3吺h,S2sOŁG|k?O{?Տîpԓ#:a%dtߵGH t75FYm"Ⲑl;o& -2,\؎x-&O>zk{&( |USn㘺 QZ9ra?XIa?C`Ȼі?9 1, LrCg5_8Tu`/ y56c]n@b+f8J>%1_KEVF6IQw^E(}9۰vx&Wcro"oY&xJKʡ+ɾrU(zmM)bnWzi$JTf'_l'?@*zjc";3Y0oȪ)Rcчu~n8 -_$GN  ,LtV92/# yHnmEO6HH+l a-+öCʮ6CJј(a!RcGx03aVz!tqE32ѫQߘ&TVtnbD* T2 x3ۺTbZk6tDљt[70ĶKR̮ I ʨ 0 ewRy,b?-z>޶֦ƹRYIK WW!v,ae!=XG|<u4ʗM >mp8LCtC?Anr_* +,u?< guHz ؎2Y1xMU']b8h z_/*=&uELP[Bg uEXۿ.:M`ϭQYGDxQJYs巵lS"K2dæ=ԨSHWl, `ΌQ=~ F@b#L A(QitEiSw3$V$@{hm<O1#>2' ?2}U0guɇaErqzbix bfuGTuD cK>r(Ltk0~!n6"y*堧'X9;3Mvh&E ~Gӓj+U7Ic-T%Ħ䖎 L9ؔd @U/\ M!*$tM57RtAI'lED|ǘRФTύ5_(nh) rLA+q!C4HD?3QPW*xޓ-Cw*ABMe5EڰX%O.<o&];Š&NjaS#K)',]Qt6y4k(nns݇U 7l4Fל"5٧cƎM&EQ:I2?Hߏ|:8mEwODkg,퇇B$vnӓiE3n0y2 yKeXTS!{S?LГW7PLO[b Xbwyہzm˭:)-Wc=<+(\JSۄ&> 5`$`X%RۚyFsY^tE~OV5nXV,*T@R̟nBӄ?;W8ɛL/OM^{FHMHlM'{!0}5Q,1Յ݀&@Y/&Pi"A7aU=Y{њg+Oܿ#૎p"{XpQ/8v>zg"aQ kOxȿ#}S ̘"]jO{z`Ի͐V=r&:*Z!ėWhWَls͹MA| h|?7-N t#PD k;,_F ^0ʹZ!eqo5V<.j[=MH]c؟; u=hJ@Nh'{ҁ3qc :wuc}=@9_h{2S -#7]:ph]YH,1HW|q5|O26n_(8;<_{&P#?v[0ĭvͱtR:5g{;{< \%<,K$n:"#<6cǶc>qw;Lrшp H(`5Pј(c6028xM߸L` |$J0-.㱍׼4ޥ9}GA|I֧/5'|Z8_l؊Ic$ѯjmgIKǹYB\Bt'fI LDdQiy9#ӺWQk݅lpdٻrm $Z*uNq8jhvJm4W_0Nܒs=r(wYEpkK#b|Fj<"p. lW8DE/P*V#4Vr'K=M2r 'qW5YP;'Ć-"!8K|N :Np;c9HͶs'NoeyYne\N)T ۮ@HbwڽiJ.O.H&`?;4 DV%ZNrG|L͠7,:ljyܑ7oqe$;BOwM꧌7e\c_ة ʇGy4*`\ʚ@Mhi/ZWc0OG܈S6*()cj;>\`Ǝ%q*u1W͇0аw5"Ja}*s.7S5*=r稽Үp1><F%_BbAH'TS2Ka 5}dN Cdi [5N B׈t"8 wx40'v!|_w""rT(y vm-^"\}J#q4 cU@D#DP?iT(n(nPT?mۧqOO0ic09Qj jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2d#Creator: JasPer Version 1.900.1R \@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP P)߅&ef5:m '\nC |BT5sCT]+['=R)P|Nzx>Q=2zP>VfevH yx'` E1_բ}~bĤ7(=shv~ΔL cAO-D$9$XNZG1<7͔q[0xLi}`f~ϧ`tƚ$2R ?toH0wp8.3m)߅&ef5:n( >z;S*[cAכͼo 4f>h݃p33> g89ylt_RV'̳)ϨEkuD{:גO1_[1nrl5ozz X;7Խ#dʁOb4;?]ij:1O4Pt\~z}'Z;6Z|"YNJ-=TK咡:.7EJYxFܜ}YFOϸV#5"A?yn^Ij3{2E'L4meR3m"Y>G]}FcW`̻A8,N9YVGJ$EU=$5 `T0VpY0#ck޹N_iCtܮ/=u6`Tfڣ%$^];e[)‚w?:t"=건qOmB5ELvS7  k @d|p%;02w49$+nݲ}K^Fn}nUD ޓ >h ZɊ"It3l)+(<}T= 1S4Ey3Dܱ%t\}c3+#fl)n oU9onmRU {kZդʅS,ͦ yM\o'ʹQpzrd+Zj-;J|1y_w|ܠ=¯^(xEe ȠŠQ3گD`<1 s6;<{UTGgM6_ڔJ]n6:-{=NGwϏ׾#:q=Ԩl@w"6&o~PtE Y{ߦ2ju~Ȉt4 $h^ҩdʫK @]hy7S`&~~s3 Lk1{ʥCTC;Z0TtuyП>RlZj# S ^7va 3@>JrIhU&5:&Fc2eӇi,qߓeJdI"҆m ?eӟ`;޾T 2J;zȞ]k[f=zq mQ(jmmĸ^VY.+D>=ƦLIã>J/o[ …]1Ad0\՛Ő8xj~,>4tE@a `/ϢHM|fp<|(IXOP -0,飷EJ& 3Sq 8, in>wWu4jIlT5h8t "9i>Zf ?0BG8˖W#/*bٺMb18</hșkZqpmG=h&2u} QS}knH(PɬU04 nT :"[Gh1XQWX+~Ow%gaDlյk顾ƕ=S)͜ުo7fᅨ.}R`$pv_)<@d}Q a@ZK} Ȼjw?\ӫHCBÖ׃0b+>bW 6n'jQ vȔ@N?"Ά'8LCX1S 3Ti0ѨL:x'7usKnF]3&P5eψp\<9hC]R^pkSuXpP=Wrj#lkHx߅6[OWR6/=4p4|w2Aev&"{ díJTɷ1a/\(3y!5~^[q︞A>!}-`2t\'ݠDyMğʚZT H+K:W?-8s՟ႚn/R(.8|c$;y %MC;O璉_Yj=c#t/SLLOv78^4 jG@GGA:zZ?R珺k ,/UHcj/#P\W԰/@ϳ%{Riµ]ggZp c~6#Pc!ezN 1dv{ K|d*x|xx_~0@:.She6ܾƌq/}P r$=a`p/\ VدOTZhH@^ipb -G'aI,>e ff|1hG{ .vf?[i9VuU괄!Wp%0)Fgv%BDH)¤(R"y >mց5O$&ֿXl.G,&Ciޮ_46=mmS#S#Cԟu;Ō`0#]6Sx'vn-?lMu+Kj]1;t'K"ӁOd]Yld:1KP4۷=Re!јVZ1Qju]n5UkԺ+e9v*ߝQX>7 ?QNNg] G0j}JT$`J)3/PE^4@R^hGF$Qp00-w<":G)۵|<'>4x9%.`/Wf 3f|V LKVr>OrQ' L8QE[/1 u>s釣=֖`d\"RتyIK.}qҺ%ب^0ZHnbu[` V*rr[fgs? 6UJ0&"V/|g@M|T -ԟ~J 5ǟl,RN9rx/-[.#Sfg;:>r|EYVZ3oCn eC:؀SQW:[H#)mInRͥ\gS3Wߜw"\` i,1|mE.A-޶/j9ˍڵxJJ <,~IW.7XujGVx+*!ǍbE#/mz$%װy=ְu |]L)( 9pb!TԳ!WɊ.10&wky6N薿 0}C@y4|V9_QJHБ)Pga˲Cv+Ъ .~ p [GVX{Av2j&US[bٻBFLH4A ;3>Rvƙd'̧0!yJ(v2+Bц }D6g@ t Mz*ۭᒂy^J0CA3?/go"EȌ)!<6ƪ$p{Jk(7I ν"Ɵ(LA5oH&H*,ZS0XM_}[ێd訆ܥ3:.*ST_d%BݫV|/Z1&_ٹ:gJٰM,WonR *a.y1`1rz1!((k7 uhV p+ۚԆ *!%ސf:ł_‘:9r9ʼ+3Ӻì^[=[WuD!L)[M:zsh3fh3@ks-<2{%aA2RisɵYxb 6! D4m,Vrq1nP qI|Wϴ޴ӝ voV6qԗ4w2GC wwz{yE3'9B)Bƣ.x?u,}A]]Q˦.dVWcp? `adY1<nEd 5c6Mm'L#=]]{ DI6a 4r,91q.~)hyZ.WKqo7r r aFk +T{I%G0 7' (s+?Q~zIǎ;E:y5KbDfD߀{CB_6Ħ?Z|B,W8mL=Mg|,֙Nt-Ge'_ ;x|˜J︪=|%.BS`V@eyvH'8V>Ϙ[䮂{rΘd=O7I|nF#_Dl5 !&Nu^P?PcY0]r|,A{JSł_|D"HTE_n~DOlyJBXma?_l#Θf4: L]s`tvĎ3׻(ҵVRtfg׿A"*jRi7 $:@Pt\q?\( G$iG'5+@^1dgEZ^oy3WP,  rGټwFT?;ǟQe)@_e`/<B@x{Q;/ߘ/BS-ꄱք7p'9 ga n]}h cnC_~XB^ R:UМ7ۊF2꓍(j2-QBɔQEM^RJEƚ0Tfsc "j @߈48M2'֕C<.C76elQVRR [=.trTl&.rVʫpa 3WD\D9 l틸'^S8$ ԰keUk͇ܤ9crÚ%]XX2ުEcg ̦xƄwJMu/)c4M9luwbԽm\MT%bUA}\pB+R+:1URB* B' QD6 E? -ֶ(u)?lp rЋ9z[RoLy&/>e%ʅW0e}qB=(MG0ؑP2 _ ( ng>uQ,joS8҅^$wėX8޲Oh/ֽԟq |04ơӼ>gD]_\tE57GLirÒpX"ܷ::iV{f< pӇ;訓fBHv\B哰slAc;ڸq@2䫝>:eNr8N_[션87Gpk7xx ЧF6g6΢]XPru,銚zf^ 7qxRǞD3y5̀9wb3 A KƲe+)xŷ8sek'Թ QI|F0pw0c8<=#ŏ]YP@=01XUt- O"p<^pN|O(MI_ʩB$/#nžS"Ax_*#p)BΫ@€(q+',759VyUf!K̔ 9VdIͫIbUH|h/t3<I/mYBt+<*7(vc 9q6{7Lw$Abjwa9O/S\ j,9T$; vMtJf!QΙ`ӻUr0x=ߧ2mO!e #78w!ePn{Q\%-ۧVSF7$-gP?9NH`3mhŭZTMqt* :β3µt .ѥ$zGd nQh%&./D߬a:Ҵiqi #Ynvq*W`Fzwphf$3-WYrǀUf&\UL"QpcNdgF͖C۬.ю4P~̡FBqw`/·Zovr^WWVNjp !ݸ6Q`-?mѷZYEf .ֳgJp^nV##8`ô⎯\~S-  ( ;<&zc9WKX|c7Ep1]^'$HB:2>JA |3]/V@o6x"=$CS_K߾T8!;=.Uw3zM8 - l;ɦL^2 QDŽp@Њ5ilJ Dmm=mJWg?!`%B&unTuMMz! "ldOKCPA"Am3O͛8"QtM|ʃ_{gd S^&8F?O t9{C‹0m$pͣe#LhdߪȊ݌-yvq$UbY}a ךTMr&Cp9KG+n >R䇿t ؊VH}?c'Dw 0?i?ꋖ:%A)J[n8YHipNs .΢Q^< W Y7V@we '8tCoؾ\klF} 8[P"(˻MSkq=d.<`~d0`{JF}uf{5(U*=CC,z|rҵ6qe7~RT/NLOGyUm{}?tRL.9o2饗ս),,ƕWM`:7`DPCtuxDFûYzG~6gXK$+6E%6# 5Bڵ?wWNB.󞜯肝lCd̘Mh! je]QAK;XЪohBT![.?ׄ<ϡm҅l[<U̍OL[P]Y:% 1ЦH-Si@|(=+,g'Э!.KFv{[:`"b6DI6yR]H6KYLJxd 럣=7j9 F0hI~Р*ɯ J令 M}Rw]/X*j ps?X'.zx*ߠ5Q_x(O5N`r!еTFpd-x8jpYR{Y\݈ZU,r;_\n5zÎ0[5JE b-|J&f|SΙyjr(%#*t4YSaiҵt,l Ktmn 6 KPL0/V1:j}|i-g&*yָGv c#...|@NQZDwػRd\;Tu"G곙ҕlD^Ϊ'!hA &^)$}ܭqpU)T'TB?o݇ (b2WhsCIHQMpIm"ҧj']^*0§Q+zWߟ gu~37s Ia1]|dQ֣b[>_(n/7:|&p~TK|1Lk'$*"Q]l:A,zsfj*'aM @ UdB(F+ (zwftrT [-e0;SpƆ7e2ѹ~raMUYD7\f~ ٜږ HսRݦ*AEWHAËww.ْTVS˯RFg=ݟ^|#ڙ3:zXa\'fM&No逥pכumU?\JSaGnӋOv` S$l8uT#[~52 |"+w 6ӿp5uꮵR(pɝbY0I+\.34X*tRqmc,b{ө טϿ/|.@U%b 9eYͻ* H!ܽ/Y<h%4ORP0jFO&W5⋸ ZeM&J1AW2cbP8l>6a+IZTzii ;mxA'} 80={r NKz8xbX0ZןI cX`suHL~z3=[aO w5hZ)VFu|ƙ\#j*ǷE*!7://{XFH."+h^2L驾EcOz> FտpHeO<%~iGE*īNn_99 @$B,(o!y5 F>?IJ* uI fzS֤wo1ɝuV=p1pxzԉ~ NA̔LEVMF'{CrOd? |Vl%0m6ު3*fNZ5,x L)hb&X©CD*.*263\|aj瞹48~%^u:\Tg|Ѱ0x{`PG:^Av{eiwPN}80xSg $or3Bٟ<(,_k֞M:Zruh^Qt6!Y#H'lMy-S!ٵ%oI@66+.IChþrn=](ҹz5"dHWeio{n0 Fm'2`m#rhSd㞆";6 |]곯&Fb)Y2F#U8ְ _}1x&~u+`Hy,D[sRA6 \>$WC^Z ni߿[ȖFŢ*5̆*^e",\@ũO/+!)6-3XZxx̂F1);BloNS MɊޑ4 B!ځ27], Zشff27ttcT ?Z'ϫ;;ڹPUPaS77.szPlj~\LEXs! W`!Wq_OC$cWm@KhQAܫ'}6w8 +Uɸk 5Ky ~AtfnIWh0:fTGױr&UT6H6\lY ]8gK}Vf&b=UhK9IrQCbEmNI٦8˅쳔kbCl@(+Z*@&`M`&sBLR,*H?R慕aC [\pQCduy|ʩs~p_8Zmm dg W9se=+AH/B+=ዜH ~zyJoqHv/OҒ )2JwtvWgh-.jc0 CI|.L?ô zaGhǎ;vs¼[?8;#EM[hz&1}{Kp;~HZEOA$4Qb!ŌȌujgMT~ePf h]|uol4^V'C{{~Sr `';i&95ylI=?TFš!3W klx\?%IUqf~M -O"9cMckCZ])#dL.K.cWZJfV/Eˢ_cީP`!Wfks [ M1A)潤GAk2GGҡw1(7z8@T4-lإQ4ޓ!GL;1v0adxq͏MBGуJIX[gD2?uٛd5ץ%/3i bS6*NvJűX`\QPYaR>&cjNW<""#a3&9M\xT jr/{o{n$/n q?`6bzos2^%M:pnay1^h&g!)8Mr nȷ8n=`T/wXR|w7l|M}2ٟ.JmA:f`c p.df&hf[dɵY=ϰSft9ppPV.{8?cv= ?r$^(`O|/g3fѮTW4v~ ^,2Du–ɕ`c rTZ::LNd~!̑mM^Ǫ֩9^)wOb ukɉ0Hu=#3?(W;=Қ 5l/1xX4=wSX3J<*2vSmz=Y˰qPlB8k&WfFROӐb,pf8%|]ZO4+q''l۫jvFM-~|&Kl"̡\U f|z͹;R \vTtQf`i }r$@d/(E000C=_ O{ѺE=>G40dĪ/)ej8c_.r}VHH vj5.04,j#A>gc)I~uxa̍4B˵ձA%^d>|B`-Cya sI<(™N1(THr"90΁[~kn8ڜP^+!]#]cD(:U )f8LHź&FGr m83"(~ϱmN&҃gxƛβMk|u=nuAOf5vZJȭ ׄ('[z3ZG >@+}jb@Jq7̟N ^fZ&}Kd~9'*#w(!ҡ ~p̈D&{Ovx A]4g$J3:msY_:%8,ô: FN\*mi42WEbj.Ly+L56sB;[#Gnc>#&`=y81 yfBZMPDW]Pɸ *љ=iK'/ nI{bTrzn'fpoղۀe9}[9`oWp)ZD ##)Cw't3O61J4c}AutHa {F[]Ipp)Yq.z ;*jEMpm.m)ÀN2,@\ G/P1j6Ў 37Pd'k/OK>>ZYe!1q`808ɆLGiyX4Tmm 0fH0̈́ ڡ M/@R_O&PKK+DߑMh^1)3"5υ)r5,UvqVN뮆Zn ^f8&QI.Lnl]uUfT/4/X7r#g_V2' tP"T.Or%tHڊN拕׈2 BSو_$i&Kmk:?̕CL!i^qfXV"MO/! հ 1Z(~\981N 7XH-P3NcD~OR8IP> ߓ"3l̪UՈ]gP6wn1 ,|"/Jځuuh)b)mFHɝt'rl^E3U$Fn>YPns9kϰSFJ{{q< S#> _bk F)шIlԐ随<~(.pELY(Kr]jsa0$K2 [4SKt]:G1E+L+Ui{)7 5exeƨEU31m5R:=Ʊۖ ,d;;G/O€P/c?ya!/ G! $%aJy]KW/l6F*C(3TPBcY?_oY7ۮ= :}q`19gA;} rdj߁ }`~6VYL l߬,$T,FQa+Akqc(,a=HpL=z|V?t 9Z*y0I0Z8n*.y};Qva(V-S0AbicTť8Kkk0UVf@i1(eJ!^J&7/{)7i+!^.fdcZ{,(#FD? Y uǦG}{i=~MIɢ;FK]琁w""Il7^ãK[AU:s3ɔb~.8^|Bu(~=梎n7]v㫞CQe^mE2atq'e $/MC4!>sz姯sB\[)\9gG_ LdMA-61ȻWl?S" hA4m{2P&6ό o%`3 %!QHNLOAw28Bw@ }}2}0hT+d7~Z^kKaVa拉?_2RŔ'+e6<uI]-#'qCٱNxH}涎B"_wDP50+(:)UH V}F9/nóY*Z {ʏJķ!?Oạ0nKE_}Gf"vC *ؓ@?Y/p&/;{XDX^@Rw.2q巅DOh+ ]uXb$Oug# < w SUꮵLBW׆w;`DGϩJ+]:\N/ĘS<3eh?G/2/"i#rM uxJWa*φD*7k]{(c.|&P}1z˻ o _bm@H9]3oSDqqGvJ4I|8&]HD }1D">KRoM4-JT{֋{lե90 23x_Tk7cm@$m3>^ CraI[(.F `: 4m H齞eJx6k.5A;4'˷=V1X o&%ua0V(GI ;qHVѰBD^7B> -L>P (8O?׎'3dJJ8u"*{7V ZM®pMx |Hs߃dh#9MRb;JS7C^?wY;Bݝ[}Fv~F2xUj iICdgKG7q,)jMN0zvNS4Fip='ptC_$HwCy6(ul!)r?>C{DO}r}Hb1,jX\(IYGu5#ާN(+&85 ؉8=AJH 0߳ ;F6V"4I5T{`x5cvIDf~!_i\ѿȭ}&}6[+=j #PL?=b0q%l2sGE/o'' '3Iڣ)~{Y<$4/㪠.ƛ,?O?F*Ț?\{MfXe<`_k.sǧX)v[(.Qgk;0tS_IlH#ͪ:цf2jXlcvـˑg&Nb[ Ĕ~ uH+@ء8I66]JhlFpbTiPzO6]o`m4$.]8/ =ب4-+}5-;ՇLV͘~m%F$RuʖYNƧp: $B23|u:kޤ Wcvyr׮`)i?V%=nsqf=SU` q*PUK5[.jPf$nA_>a+i*=,V>Sr(n}Z]JәJem㈕FUUʒQG$vD滶aG_5]{KMj7Ze`UH'Aab iI6izPl#c-׀.<[ Lyaˡa)L?0Eț@%`nұܝw,U蘓{p]v`Iqug=6\y2 d^FfS-m n(mB'<ZԊ1"u!J]{^ Zͳ7؄Y=JߕJF cGzTqr+,|yu|24 cBwFbZ4nkS'!SWV9'ɶ^eqTޔIݦEqK> J*E%7a7y,\o!гn*%65(?LwC}aT" W0*>_8W.[bZz|H![&Jc[XLRzjTA ;:|Z97\_ .$mDZ{a˯BOϬl.wG2o!"HJ $¦QE/ߤ(LZTK#,%/mՙfȚT)eSkbilz SP"&7~w3LxAk/ ۃ[u?]q) (n˫6mPܑ'5QSU' q/2!WI ~2/ű1wj&OL S=}Mix|zxpJkĮݥmͯ|O|3yh/}[jMχW}`dY`#rحsAz3x|\b!#e7)j, L@|끉Y=n\;t {i?Ԇ&vZq%&R~Hʋj钡oIfMn߫ 脾̓0$K~Jޭ0?z%"faLEO$mm)Yia1Ehj[ڛ9a%l3wmZӨH'2ěW5\ 8PL Mm5`y2cxa4!HW2b}+n)3қks2\3/ʪK(9qǹ=B<MKA exL;z,3bt _Ēɜ^ |nɅBڟk}aʚGPbSrE;Ezqtݪ$H@]K%t?3H 6Ou Uz`󎎇9o]JH/ӡŐ쵪/#hGr#1+l?[;S}Y b"(7{[vVۺ+'x:C[$~IDn`5q{Z#OxIjXNi>6I|I 7U%Y /åD}睁W7u UbAk Ò²gXb!fIr7 ^V\7\[N=Y8+@dZqaoդ_o)[:dsJI7]͘CDDT6T{)]-׆3DDz&{ƱL7}Nzo9A>I1%Ep]̞E'AO&u2~Yf\u H'JmU`^(!dn)q=h;Vl5D$ۋzqC'tM{ɒY-vm7`7!<8HG*dGl+BxȲlamX 'WMŁP &=U],RԹΜMyےǏG]Vnx {rI܆9mA6s#>- gvE$ Zo4J$h3ĒA⊯#9=5UaR`\!W.ZK8gԂф`Y 'm^3t n[J-M9&29<ؼBw"Ë d_FgHުqļˊ@ !fuH0;_H+H'C'>}Jp” ȰrVDXKnqr ey\&jom ; WBNfܔ=-.~IT ,} ZSydJtgZ)]KYX"9y\l sN;_vgG BuiQ-vSVk$n8ęr+-<\+D~'`z Ti_&PvT7!rY FoHX_,[a*A2vGe]Na j #df}q,~ajOo/̜ROY0S{M}Fh̥7/ל?rEفAA+;zjq r! mO7ܭ 7 jp`\֪\qde .X*!) x$_|tʍ8u {V5R/ ؆:ϻd'$@l@ji$C˫%DZGcm^j੏JǤpIB[rgn( 8XڼTTp3w=m/錵+V.=4lr {#m[bxV5E&-h.Z:MOl6{x--hqYg?J,fS.}1Nec#L1Sp ^[@j! ^xH2~ĤWʘ,Kb fig]pxGrg$\&-z2``ybURsZP#@Jle)Vʏ'9۬|[ /`m<|xt' ^e ;exvمo~wa:M5?mX ۬m *c+˪х(-Ķ@$fvҽ[Ҿ}TFĿySG pv1yRrPW yfyNJCHAVk&P_4k#G.FGWQi֑[s X)CT`e"! qA`pHoL8tBӢU?JXEѭ3QTE,"R b ]l!MY$ DVû `J W9Tu:BCg7nB6wdc JX0eyU U(!:GZ(SYǻ{#.& :EL,WuI>;}jJk"j V`x,W>.u1KEVJy%&DBL#[r nF"G/f\#ao;Ğ5? #5Suus.QݘVt;Gi9bA^P =\&Q#l_G"vƓr=!eAvRHC8Ql[oE#v)Z܏<⭢ȖZ`lIhZdoڋ8 ~Yzy,Fn;KAC1u: ƺhǬlpv{{צ)="+8},ͪȞ6kuÚǞ|}KzI&yLBAAmPPAX_[wP'Պj` "a\%tC-_4ZxbTYz-[iLg63˨ED\;%(vU`Ǔ<~]Vga ^X`#kq@؈VI͇,MCD2 L`enN5UV;RCIk*$A;k8K2b~Dxyٞ|5ЋmL?c:cĮBu[ݦcW  | /X3f 6dv;hH1c$_&;_&ݹwtn'ǐwG_9b=ˤZIBX/d{K_J/ΛH8eaZYıKvpcvլgs q?V <SЗ4T2t?bZkN?v2ȃ(s|a=6[5 se5: (_B"85*|q\/ Gqu鿸Ɖ5/`#/;6LNMЋY4~ZuB[q Q6*AVVI1]iOb,H$#kLeGe=AZ-A$8qG/v;1"RW2QM[T#R \b8)?h2t_XE\ +1nRB`[YQ~G&T\TI~ j,M}92,;gCED+v31W[y)wEϲ>GG^fX[RTSt F&j䬆c]S!,TS\XM՛TKbvڧE)6fO)CBMD@MBp r~Q$=C'tI o? ,:柍+ƞ䐖C gU~]W*xq|D)>9^ )MJ:ԳO"¹` ,7. d`AWQFCl?PPy:[v٣UWmW Ak2P[OWECmնH - ۧ]Wٿm:P;̉x{ #2BjY\ . "VtF>O/ GzBbv|4Z}]O19gA;q}gTwf@%y}d: >D`+#ZĢ$z\zK'8tyfm_&Gz0-Iz4y#GvxnN1L0$SkAG9qN闦 w6Mqh364ӄ0!Z@Qlw:Z zbE99B?p$N&a6dX8zFY4,z8Pq: ψJo[L܃)! ?6ngRp䨦=Y3<C2㽿<$S䎟.˒U\ c-M^8RD3觝J *cP\-&J4%Q(O5 aaBmϗ?q!QsF?Yԓԇtൠퟔ7DUX^]KϮ7/r("M\&7Zw%'=,P2  @-ppTj~zk飮cZr|ґQ*'k4:`*77ye?Ct4[}! s"<$26y6<asZ _NC_$>nj(U9wQ;u%6ߍ@[k>?RFK~ab\CK$yi?(BxGDzvn.D. {%TT>+XþrLGD43]BT R!B/JR$+AD}DIf-^q 2Q Y+D"%,\kA/ ++@t<&Oj^R*d;%BMHf@s=nͭ"/J6.tEh6GX:hk${7QYQJݗjHVi+v (_TBb 5f{YZKd/Dl>t&+PsؕRU?QuMlr Oc$Mp hkׇ(m=<;&HQ@4Yۍόhl񆌑G7?@w1 ^!Q<P.e͜;E٦DC1y `~Pu((2O6 YLDY /v t^O_C!iMvlnk hSKUzVJVunUҼ};SU50N̏JO#YGևo¿?tY1¶H }9Cv?EzraMs9ϖ) oYJw"-K3 DGt97#EO8]7&GN"Bu%Uq#DoG`h0#ms9Dn1vw$45D.ͥ0`S RSk]S"^r'<7xB'=@lfao#FC" 3@BQV^kP6 2BgO)ުNE.0MOgz5Fu]cjHi89ܝi6Cvie hk/EZH aܾ(n}Z.:!kqY"wB61" m!j7nRwAR.c8ǿBAqj d?EQO'J%M8[GtuWf1X]5&T?U?\5l}faIԁ-2Y}#i8z^V{_3ŁNُ]W_/|ʺ t69Vxs sPX"^K`BpGY kl.2`nPzd}?=\:~A , ;Sf4"/2>HjUIq* -#{tZ31l]im)Ru8Ll H|[mc8NZ 0Oq!I#~*%Sǎl=;<7шB+r4M ?|ٕrp)VuQ} çF( !t2+ XAM1 O6d> Wf4Xi}gaZe3f +Q0-?#(a#LSZ!U~![(7l%o~k,ϰg['\34|L(Q췋%i&AwkqLӳo_v6<ֿ/F7()o*c0| d%@i1/@Fϝs@3BoP\>e7npIvx "NQX^-t̽ʾ}cb]W[0Yq˜ 5w&O`fV06JT'x"~ebp`AM`6 Joi+>)lHÆ`]n[BZ[>D(}dZU>`qGJ-C+Z+ L~|2=HvXUKsWlw+1G6v{GiM4B7O"u٢3$'47T%afiHelٕ*K{wo]W_Ql ,3X|ơݣGCoku5f[>ƓQJt}]]O-ʡtm l Rn?$^?$ҹà˥"~}ɸ/'ZearGSUQ+Wf1nRp\zGF pT"/S@r9*iboblgϤlK*ߣ(% ntrok':/^\`]sP:U`@bn&!LR(!..Yi#Jg޴ d uPQ[z)M JBMgr:{A9M&RAut41wg*z>{8X5D'sʤʽW;+ߴzL! h |A$'V=c2 Ag X KqC1//03-c1L13ݦDj6ݮ4KvH=O+΄ P*LY]IIh e"MqFܤ9x4N2!؁f1׉.nT7^v.VU\A}V׻Pˡ_>V J+aN|㘋uvjxڠHTU`WGH p^JpP+fEL:Vլ"IE=!]BL(J e,+kW+oݩ8{f@R~w@M$1Gu"OOh|bcI\9D@JpQYtVAW&Ѷ9b]Tl0XHJ_ci R4 I]υi\Y qߧ`êPD,0yaٷhd:Y,ޮ{\E k=v0L Q{/if7iP3x_* T'x o@ B[%NW+Ô]ܚ]M\ΦS`뮻2 QYFc[ !#Jz gŸ,<,3YW'aUK%Q 2!F$ti$S`v<`\$Mܪ$;IJ ^ev sKx'Fn! hC˄R"z ܶXFCH3/.!,.ˏ=d&+]x[a/w7#ÜfW\"3:l?:%2v@Ψ_IW\56Ae; l'^(~V, 0CJR#D,Lv2l~#6-+:Pr=(YLIt9B<cl.B.;`a &b5ߊo'ǝnsťX0D4f4LqMZS4 bpܰq[!5CʯTeFvB&VIu(jah`}@÷v1-3u2UJE2v`kqx|zLdKA,DලtlY"/Qᆛl%mAaZA;KLث&5/JiG~&#@O+D69i֞aFZ21S(s66Tfk\u=F6M3B[(cj1{- ET9d"ezFb ?RF UݠeV3}"(h C.q ^ܧVª`"&J dya6Pk~ǬMU 5D IO1m Z /n#ZfT5+ yF ?UWMLPRө3r?d:VkX͚wi/YY| ᛌ 3Qp0gn.%P*ۀJe _+ckflC=AYP-Y"*>U)~3]U_ܯVf9&k!XLtuC#Dڄ'V6Qc MHM0N'KhgZƖ")+R?Ӥt7Q߹|6x^d(RP ~;J#\Q!x0/ ~dޭKź:9zz0`Az'ϊ +>Vx~ݯ<50߇Lj$4@A?VAqÒ+FKeጉ\Puň<AqWO'v ‰\E^ڂv嵴H.(qi I]F}R1PTro,l5g-×^ČY Zw5r/ +wR 1/$'rx6y;9cI{)& $Q#t%rzԄ@(Ke~lw+ÙOyC]/^V] 1dA $wp#L*Y5qW9N8BeYSmШz֋Or1Kc QRmzzyM{M^*s Wu|qct Q65=# /5a P@gB*Ħs{ 'n԰R {6@.XCˋ[*Z,+v ɄRՔ-wKU|&TMqZ&ʺIp=ӀOMws-YTP+,2Lb2"ADmJo)v'l c=*wk@=̲xP%zv賬aTT<Ӽ ٽe@G/([*Q'Dv;/~w3tZeZC:?Lי!آ~ƪ} ^g]]~`"v~buaP`垨mWǙ SJՠ?"툹#&V:d?{hC{~4(|:({^fGĀ"5SP ], |ˍ_5fbܪGoE` G>):+g.}m!^d'7VYBc1ıJac(Y>T!Ɋ+/F*X*'Ls.zmeEW;7J)Touw-5ˮ߳M|IfRgg,וH| A9+&}tAб 2=,k 6|BocCyN33"}=:#HMʡjmMt * p 9bi<ȏ(ȳh{T{)Z:!^Z;3 9y` G0>x?|ڼ3vpֺDEr5A9%k.Cmȳ˜?dF%jFбUѐI3qJd(֫:tiSܝ!; vz$#B(n}ZK|u©̇pB}rh.*ҋ z''xMw& ޙ!#()Q7{1-8GSr_ ui(A]_07Ё2];7`ؙQMmlRB xuN`#n"!ܰ^mDnt㌡L]=Jc?1zxPAwm4S@v֗ TԼvhCJ (V^; 5Ejd%WՑٞj5 }W *dw~2B8DJe[C0&RAgDP1j\[n"Ր.Qb4uQ*9pTeD:s8o]|xgdJײ8^?4MKk&!BނLD!82r=9Tڨbd}mnk[Ucop6y* QGF,7F ZDͭ7,JJnv< WN< M}nŻ@(ǯ.?]6؛ZYW*b)\oݥ8Eي[G\PhzS=Xbg|ZF_ʪn喋c) :/GjG;|Am80Mϭ>K a-VY+Qpǖ .+j_8Uzes{ȳ Xcw]bߎ~{zL V;|obi 5zѺḘ-IҞD^YXegu9<S_`hF A8%\ /hl?6-+t¥tFs?%f-ďw3A0̡G *ϖgVL7KHqaM SlE)&UiE*٠ka+(먳4gXE*Q4jTOc^[`VNYG |T8Qܱ {AZJ-_bt*X8\>BઍJC:i6_)n3Rڏ~ m $9SVP:i4cƅ!F Ap]̞E'AOw32:9&:'xCW+`ZqԀ!S|c^BI#^1ώHGuLtNZ͟u96xrE/KVzan;)xZ)&8UmJ]N* BNDg$D">'8^6\͗-u%4ɻ>\Rt׸Cj=Bdt,u?:>/PNQ{b$O-xy{apP)~E-E;{䏤'}VΩӺ$wCCR;t2D^7j]ܓ#ٹH w.q?g[*=|Jՙfӊ{Ì6Q+qO?mH " bOXy9zM/?oN|+{pKUl_y贑EQ. i˓Oș$mDCVܸωhE,GG ػgT[=wS6][G~I [`݅h}*]mB|. { Z_:Ok鯻mY0 N:YM4B2k}*Za7B6QEoLhwi{猳^,R3QcT$"ft$_Zd1TE$>e P*LYFýXs߄zKa=M@ ad4Da/y(]%cy6$}CO3FݛiWpWфBB3ӳSq/W O,K{2{6'gFd5u ҆wJMh IgC]K4v+5钕Ww6"{p!sg%[jxSښ5d[,.dS҆!yOoTXM@e`M,:##9k5zQ >cTa8[ ĥ7PPmdwF7?dP9bGkPE+0q6W6c=kMk<=剷OEmz_\<3yQ$*٣_2OT}gݚr[7F졙_!ҷqDezg{Ÿ!=`ЫN ,yixJ:2{OHaEZu1-#Ph:s*9Tv[Pctǡ~; Y+|͋?TD4$iIޯPlx0jQT]i(ʲ%-wj.6% 7VJ C8Hۀ|mЯo1E`#W%V[AO(+cʷ\Bػe}SuCӃ'Nx^I-m1Xsk^^pJL|# 9@ULӎn3WCCCIڳ ']Ghs7qy<+~f_h7D?8`PX'(np>@pd9(np>@pd9Q/nM`ulUǡ'$ïd TP/T)^qNe(t0PL߀0@Oo3d_WBڟOb=G|3|Nϟ_noB'վWo޿Qgv?ϫ֟ճ/mdFC7ɿgϰo@҇ ;MK78Mv//C牯Kq NӍ)}"%@]"iV*bTӍVHJ1}~wn :nj=zGkV6MWBkn:SE+~XSZ:CP-kyl9n.qھ_K'ni[C$N>\ƃiKP8ZnRZ+gғ-[gQ{́+K }>n";t1$c8f_&FV$y*=[^)ExĿQAͅ^LFB.\\8YGlB偠A[?j{1ԫױVu+ )O a}` (w ò r| 3r.g5|+o=0sx; D@D~S☛G;! #h:m4W8H?MBEŎ]5ԑt &nmW]gC8*Fk%'fΡ̣UEUr'>t% X%1IZrzO2nckBsm+vZyh5Ax|OsM2& })rFqemTZtԢ6rS~i&S.[l+Z*x}ia-CdGN(v?*A"&dBRMԦ.m#y2IMɵHK7& q$Z XQWDgSv8,fLW龜1Z.5Ϙuu`"Ok#Bѳq(Op؜EGCY6飧]¹ w=0ŵ吞ehAni}H(/4Rer9#Sca窸u5̖&KC⽼qDX+P1(e݌0dp\(/nj0AujCUUuQf 3^:*:P֥IӀRiJ3⼖|V\/èFQeE!<E:r .ֿj-mo˿Yzd=+'|F]=iE4jSD˜>XHFX hkmtBy2Ł>7i7Kм1{8 EֽbyBk+wle bk?5!Wo^DE{P"mI*dǻPU7NAsl%j+ߢ%t#~6}á6#p*-OIJ ̛Hu+襄 ѥ >b`E?ӪDj.53ۆߌc͙@5WW[Y(]6@C-@%}1,*Y2W- 5l|ҸS<$q):>N OBF@R߬a nV0y[zDmQEngkvrj> #B$q["˧r0Y- 2&X|'XAZbBQwTrUi#KS]QiY]Eis}%8д=:AnhT;lfcȘED84!|'=CSH")0#+4d6ɍ,*P($\r(")m-X&^6CxQ`ӓ|HեnwnVbR8QiBX+hY=ȱnqS'{%bXm0fz׸-ҡb\5;e3g hl~.QG,yɫzA6q%Qpgf՝쩮7w܏E[H߳N4gPШM҉.kXGrG[J'QZD}T/szq 55٬.>ӵxZ4c6  R*8̴ȇp{qqM\G†':+AҀWfoshYwp啥̙́Z[xE}<'4qն~" 2bԿՠ,`+?Ds]J;+G#kاuF\IXr6u"~7shVNGUuasJv%Sj0ۙg2>)#|7 j3ADW@3f]#ijufAa\O;fImZC'N6V`L!wrE*K(?*~5o2mUKz @JqݑsC{@F~3Y,*n_]HuK H-,~VEA}wTs* zZ qUa6!PokOΕZ}?yOQq."vŔ1e'=֮s8L+)8V o샷^pVKi +)oYg>n<_r ,rvkq xz  7m"e9iגGPꡓcۧHN۵`ʡ+/CL9 (G!vW)'pN~Wi77x9+@*F5H܆ȹT9{+F Xj#Jy)<p?_voЅ9pBB:50xrUWk3(6N,9jsw9Q9Ś?6%~PKس]mC,`XtH=1o*GR.  $ZpH?ۡ/[-dc7F::hXO3ZJ)9ܠU>y!xV@۷uj,3^0:nJ.NZͱMd(I2/O4}kCl{x&Pb:x :y}N&_QIegU4F-G`dH[XU4=x2 ҏ!pw_<+Shwr\[1h)[KTŀGDM!ftя 1RU((K|P"{P)D={5V- ~͘br.8I8/YV$fR-pVOו^缨PnKKX5iˆ\t"T%##Pw'mO‹h%a a\j3rg- 3|u2fs<"D~I6٭og3Jc) -YT[S ?ڻrYKq+#o5hDV.M݀K ፅVT6;V!_CaX/\9GM5rRpzDc?9Hjv{S9v!F4Fe2V={kAwHJUow8.}38P+Z5ƻhFjO#lB/]\%sy;+F _Qn0y5{쌬FYRBrPn~-7:Ayca}7-ZmneObQ Xt?\9|FC¤Qԗ\aYԶO#yڧ1esL:2N#MuB0l~-IJ |Xt/A? Nj@Nϼ%& GyiɗqegfŮp1.#e;W$YU-BDGitLabEԒ[V`}DO~w_l9rQ(F`CҚ^?|aIFԕ wBƮ7=(vqҷP\OW$aS~S%P{B־+A«(TD;ïc.yFFi= Tܭ)Zi$6㭊 9[<|< RlQIUmC(Rw;Gܻ~F A`tӲm2~wk_k1a~+ÒPnT&AGi{E|uts_\d퀊p8J 3&x.Gi%>6 NF JEͶ^y[-Nc*:iBuUWf[%KvQl͛poPp@2ֲû"v4kuΜȍQ&>J/F`LWY ?_zY~I77RT-4luH Y+˺(ؽVH،cş 9N](M#h]|Dqkn(,@2~:Y\[R 84(Pd3 <M' DN] "R)~Ya(>h03a$`12;dJtftF!"QGU9G0܀FhV,\H僔G.c'yHLۃ5Zec;щSEST24JPbXlGޢ1TsNT3αZbjxRkKxO~ #r!o;iv}+ "1PNh۱ [05.PxVzVz H^ S 4"0M"?2yb>0= 6uy`Utߕ ɸL%w`j•wlxY"qmA5h,h 9*,PݍB罣= gu"5f`=0$EIfkt{:1?h=}?s}cM3WW\pP{̊?J;z;A:)=av ׊O>QGm|F_t{]}pɎj@gI!WZ~: ;ânbz;Qy hJ$MZ,c ;/OT S$7}KFޠԊs#U*JW{[ lɘjǷ,DcaDRs ,7wIxrK?>D:u2ް#yib2W8Qzk fj҆-\O޹fkZ)95RnEJR]z|y2xyq(Z".I2`3㹶P N',ìj3r4,adm{iʇ_^`suY:Xbd\!b5zA0ӊDh5%zxDê6%L;=O\)Y$q ":}e n|OBN=J,)zʼn CݑX**dʗ.Vna;~m]kP[5+ʏRRmW$Zb{x >4oVn{oᯊ`)&\4*0i|Ԫ:ף;%3fZ(n*sW`eqM[H%($y;L̴M/yGcLg0ס~i:1$hx?F{O_ޭ2z!r{KŧD3 ցr"f@ cG@|:3+ .>Pf;n _ZeWY($:Ε9XajFjXDGW¹jSu@@l[BL 1kLHZJ!Q_V3:u֟iTՊ/YBXzR?_b PpZ."<Χt 9lD"NA@\럚hEN`)5C_m-UG{3C*9* $yxZƤ* Lq{`}$9SY`'m2|V_(}mtKba%Zm=E )?c\`گ)آh30 8⮷ߑPW>gטXluM i{ϓˮ6q KXN"~ɔXuH*)s(LJ mX_ʫV`0k_v9eܕ{JfZ|3VkEwfBIPZ($ah9wj`ԆQ z6۬ 5MQ'. y&:4b{ +$՗we|{m(ըϨcHkɂ>3NW}@.(fnמ @7Z6 mAq b[,<*9XeKͮw[rN= yۉ}JOSQ|_̉رNAA0 LեиN3E%D6Ň-vf"¸|߱ xuS*w!F:,TTqj馁^`Qw mVkHk͙ !+q\{Goi_&?/'L<Ƥ i!;x9q)pm̋'2k)-:x15w%nw&pIoQtTN1a!{HSNhЅ$Umw XWmB[|\m8:,_1+_/K@ܽ! .yl(9v{USE`ZϠR΂rmGֺˆyL]y?^ͦP&n )4ՎΡMj ,ަZZc$_d6Q_>]𲱟wAŪ ОMTL ܏Н﫯Rx^8˅/SB |7! Fw[SID??l25i;=b-:B(,ӳ-)8Ni0E^nXq23s ̂ Ἥ˔_ݒ1%sKYTBJWN@#}CɦnK KK)cLz[Ig>λ4[W 1`fSd9i°NES9@;IMn4o_'wYk?C\?_)g қm.HEvzg@!oilva Uǯ.xzzc+rZYֳz#)Lr8 'L\o8OCd;p+PI;h@=hL ,誈].[_7z %S0"4;߶;0lXl O]ގSTE9ڝj)59!esQd򉘿XmZzlO4rL9_06@6{kn)4^޺Pd7GL->ZBi#Esᨾ/9HO-?CZ%+/0Mo }u):oG@nqHo0>Üj@2zu"Ue]Ӭ?\ݰ_S$eN ypV$ MsNks)ftp4})ZAy0> 8htܶ-s USc@z0!t_+p|,|zV_>:f4sԏWDdL#iO[1Ma^Gr]#f3VYb9|FUڄr\憉 [vպ q5&kkL'C=3BvTHcBxa S;lxE!Z̫gi@N6*~Bu \şt9DUe=!8uy]km S4ZJ qc MgЩ \<[t}O"#Z^B ).J?E)LR%RlG'Fbhw 5KZ$U%&&]RuKחFmTaQ bwn v,ORN ~}Q|ˮQ?~6#@=;Ӟȶ!:OThkp~ 2o j TLP^T +S/ k)*Y`zRrf_. b]"Ct8̘}pFT6,!VoOG6=*t@;MR\)Dcd/M9`y"P"y4,.*Y.e7r+BC;jN5RQ|8OBpp8q +ݧMš~DGKoVt8+0lyɍGisDA ƼPj}t?21 ɓ Ƙ^lYqlp}=jJ uwU f@$V)kV!GB6ަBB{0yGɋ3aC)Sel|HsjlpsJ%tkc)mxNDrB1&=6?dI[Y߶8wUc8["{EMؒS{qhUpbfCVS"B>>Jt0S& ."26k WK . sR̷$d'C>/ ߡoaڅ:~$6duoAX0EJ.}QMv;[ :3sf4{}p+tc0D\ ^14a; 5p7`qiq7o/{`\mbJT@r[pǫ՜ku FEJ{E$z8-.RVO12@2]>~ʱ d}Ͳ2g+Ty#.-Υ Į0TlI!/U!yki\Y&U 4_}LɄA~% nA ; P4y4 zg>Sp+#8CSQڔkGJD{,^ ZiZN%  O}Jv 0]G0OVBk@'>$j%ݢۿHU+R!ҿA/߀yekkez[AQoN j38cP͟# ]0 ۖ ڍuGE=8!+iEn3'bKg'"Bp %N&V)g!\uE31ߌϯamq\Zf 8 6Y,/#O:MּoC$<`)[nH E&_Px yK` HC`lq` UCXr.bոvfkwme !׹LӴw_ٷҺ:Y1w!_Oݐk?s0=o3v:h$Ȥd]咝J#DJ]S>D2̈́k<9!7<rn#Kj#'[/}%?~Hg/\=IUc:LCUr;ɑq(j"wy 5b& 2.۔Gy9ָ+&'Y-!b -[p&_Jw+Y6Q! EE(ZpbI٭rۘh":.n=Y~̡R-2ΒИ:ڏڙJj$/ׇ3*!KyxưYXI[G(Wj%~ݯ\[ R&轃HI!0c#G7fY8 Yښ$Y!?X#y~ |M\/.)P8au g:jD<YQA29o&otw0Cr<9 hinӐFVL0zЄt}%*YcBO+94>1m~w87K !"rT'JoN@fU'+WYY惪0MG֏2z.;'$bF&E}27bW.}'8_nnQ$eX#N̿>wyrge{%NXC킗_fEb_N-"ׇ@v_<# en=3**HOAlF{Z3EDGE T&NanV.Wyz8):ef=7lgQC$n.EJY\Ӡoe(eڊ*q8>PM o'BBsEƐ՘rV${F<{ yllM2ڻiϝ-xYt $]5S=ef):Xs4v^'ePD+x*%wY #`B]W_`KK&Y֣&B!fV]Q?swSt@>Jc ) _bAȅos'`s$Zal`dsYʣEn$<|%>sKz+%X1%(dQci i2P΃>U3Fw8;Wa K ŸfbǢİ/w?֗;Cj$c .e+*Neb=NDE^۩|?u|F?ToVmϿhG ?۴7Ruϫ}ZS: ۤ_۬Evs?Ow_}ϟh~ޖ҇ ;Mn9B~W t2c>S=:4}ΈngRw< uAJ;She=^(`gxu$b6i6S-EYߒ4JaA?@{o96oG_^ެ^ e,!W,Xo@" ʅHB7T&JZvSAYbB^GԩZwzpU 06:MpհxG =]6Cf%̿ف=1w*1$2k8)8Q'c,\g%E7C(ێ?l WSγǻ>T}7W5AПYCUUȻSd[d mOeUwSbl?/!]#CnC[K41/L:$ÓET N~NyPܻs478d~M{gCƷ !|'G,c ] N¥-:Du1';Ѳ?¨Cu?`z֪ŚK9_Q%ȬĢoC{5sY7c; X.m4$81->Xpevnٮi4fW5vg4AfM3S>@nQ[~A@mBe`TIs@+֝\2ku S^?x0 r)&慷6'(J#6V3d ]5Qk\'&NG㝃p.M`Iiˉ,G苢M JN+CD20T-kq0ߚ^o<ݒ%S/׍'&4}) M?V#{W$`iSyhGM 5GM_!Yv=UWxSxYg@%,hHrm@Ǿ 2kL' MAp 0Ye{pSȒ ^^4Eo}̣,enORæb %;C!3FM] dl[CE(cMMV57-L .%ԊW`?dNl0>13D@+C& Qk>$8q( T]1z~^ʲKdbzxpY&{d'(G%+A0%dAOO٬EՖE>H7VL I,jsQ<}fVh򷐈֤AX#]o#&Z~L~%2{j.T.;*'YP<>YWD&(_;oo)䬾p[juk A3kQle&s;ϳ٠-ʁ ,9Q[Xal=e7 Ż/o+`]lNnSN%S{f#8-;f1}'SJ`;5Y˦6[*8u:㔔t.qf PcbB`vv|$[뺓qp53nN鷌v%Sk)P#<ұ#L1A @blT؀$~0E*=(o89Vfn~@8e4ukKL.Crl`FO٬h9J3wbg B>Q[%b\ܞ-(j)}BUTZe%x^M-lTgKXբ,։~`QP-?a孙x%CvfKqw1?fhB zJy&\V^M )l Nզ1FD$&]?bXd/=Nd3/a>{K%f$UuB1Em>m>܌·L8(;*W.9u)VH9_?\cB`O<˿ۋG u63Gx3Аq` wی(Po _ȇgk+8Tf}Gl'8׽l9+U]8_e@ +УWr|WQ~\'q]Oy៶"EB>+;Vu#"H~KÎ->g\mP4ԧEZQt{0R8Ӕfr{LڭȎZ}W*<dk\@X tLTZZfTχ̿%=_侩B4tY0kR8B8WDMLVt;z2M=;?=HCa ^Q;!aGk4v?t#q% Vؔj,,iTe\1Dwk7 ۻ-c7wKJ[AY,70cFgN.Twm!# :q"!a I^Te2 N|]/,orHB(0â=H/T/+ #,Y%N`CA?簡~ZBOrұ(b{wQ*U'k T8xBGJV6A6o0`zY}C51a6R{_ze0׌}Ks9BlN<&xP#v&ROU ZQ9yй0B~Z Ȫo3I҉L%jPA*Em*ћp}dt̷XuOӴR?*ԀA ӸMz^E(,Ul)Rrk/N'4ɓ Cv *5(ckCOl'Sm ccu9ʖoicBg ͒5kft9h i{8tڃ{)@Tv4ka՛òC,3S^{/imś%&叫hdV`intXI[,Q> `dbX*- /UlSfQݰvwYEWdDn-{tA )VxV-Av1Ę4×>/;_?,|Um&}ah>:ӂFܪƻeX=2 'fR=bodٌ cebb >%z^8dxjFybwVzVzǘd_OE /|pF=`ȴpv:zy©.eN%`VNrPI@Y~TT㷪G>z#i rGΑfcԜg*8+y [d"!f̾ghB6d͗cUt;1[dn*1id %iֵSY.MaZ ]{$9=H Z%Gd5y?j|1'g+JKYRP) =Y&-迟%1Р,oְP/X{: 8KwS:8q,}2w k7bV4 ?>%MLy<u3N$&XRoOlEH8&fV\hw+H[s^Dm "$L>t{ oPH9U)qHPu#VY3t)U20cUؙ2d2~WIK !  $X[mT [}vDP֍'cSZؖds,p/2 v~=psܘ?dg10fϿ|ǹ#ph 9 i/Cb귱b DOkY]Dz},g9"\c@Gj y=0AC⚋Su|FyntȞp6p]l6_|<1xA$:A}g.4]0,|TD: ,!2g'@^UzX UZ]} pNk33U$X,Hsυcz+ ։;Eru$LaY{F7<O)ВrXBDT[8Bwt9HNAꇙRv@h;)9S>[ڌ;8 a=dUS8ǀ~jېVE۫FAn@9Ņ`#kW0Si/ނNBy_<QGdHF@*+ɳL@Ò rvMM^JKq A8a lb VCl7RujѶK^viixɁMѡZf"-Qd+&?zs?I ItD,n _=Fܺd뮠PڪGpiC.y2X_@x K00/{l`q: ƶnƗi4fP`X'I%R!G&SLo8c_^"1 mrL- ** XA\4%[5vc#%C~cAv*-o!J;nzyFBcͅzrKvU\V98;/A9^׸<$%c[Za{?ixW؛HUDb1J]hd({/› G+B8Ã.rsqo=ń{阚A1 u*LgrG._ !fʅLcOG=&ܖoAqNW}Z<]ózbytE"ٸ#:ԟ(FoPJM͖/a4#rԞ| 0(O41iUc#G\+T AOqְg!!_5+ Կbr t1>jYGʊj }f5_lΎ4 '>ы/C9 G9QPii]mʮUD1D9"eɀF|H'擓09ı}f"'i{tW5?3}\Jy34>[L|7WAf#{lQM]+l"jұ)cw%kxQ 2TGv츞 +~:9 ~ͧ.VT (8.?έMDxr&/EFY]W ZAGj6@EEF!h1|z,#Xp-ʐ{S`hRO_ȃGJM d&> 6TΈ|wSMK2Hiu ͉]1Q?*6}W]4|{v۾J| LkD-Ub͆F/5-J Z78xJ}KP8|1q[@}ȋ$j4;3qX%-)l{98M֢l}#[1I6@ɬ)y*{s5`Bp߳dw@:M@b^n4Ȋ0Fr%N'1]v _!XxڢI,~­̆PtBy87&3/r[qxndy.~KH(EFwfjh\I3@NLYgyI*%$>}1@qfP]Cqx2Th txտO&l\㷏v2 _q" +[Ej׌}薀Fz+5VW(__}״O(-EaO U~M%$ڵ" tNZs@=B3ج+鷲.]:usSQNCv7J8)K |3|K%8Z$l'E o %ՆUY޽axIfSa{B0F`j5A[V3,)< %z9 %yFW֪49CRCA!Sɛ.% Jp3qΈNI\׀l-N4% 롉wKVv ],UNJm_v5jdaܽ!ٕױSٕYXAG.44cwZjڬlzy% c#NWM^xfze#F\JmHhPW)J([;6)G9(lX'i?F@-Р|ʤǨؗ \lyV2oe{M=4Hw,-1NNͼ &"=y<XͱZh{(P%:N#=)<rqرwv0i4%86Eo B1͏=B2֢`0L "jxi(=G@_i4y#r WGit )ar^Zwm&ى5[煜=d{-.HJܮ*ZU]El@shPJl[}Voi_ku8&8CKN!Zgs!`󀐀 lĺ-GzN SYdd[Pj'Ja+ħBl2rQŮpWΔoozxOSAJdCOYRM&rj}麏bmCʊ[˖1㓳}^ `6Ẇ!!0|4QDChHˑ>ߥUsj(BTn_?0@Z#~a ;R$%$FlA"94KZ.hF)հ\Vvh澖yoTY;a u䅰9"^wImu'Air7hAPXݹ1rbDSH`,ڀY ,:T W}eFx; ]䎿<]*u> *b%dZŜ .Ik%RppF:~I1V-fh߫ l?~Z눳G^Ǻ.OOX0TgTT1]Ax5dOz0iH\~9sy8j#1 H(ta ;\92%ۺ6HN4ʌ\UH"0rj| Lҷ7o>%)Ŷd?$<#r^FeM 9Dv*0kpY@n?gw::Q𢌜|1XD=I QqqO`ve:GaS왈%}ذ#/gNVsQ^ ܋jH rg[*_pcw0:bd6)yL g3HLv M4l@.Z> *5տ1q5FcZ\G #5ޡғy26(lPMF갈 zi<܈4kLƯA/߀yekkez[B7aTAUG$25}LndΓŹ[ :';b< =܌ ԒGVzjEiMIÄ;8|BF2y*քl"vZg.L^] Bha7_FT+81bej_yLN&7 &E-e+Aqg5eF&>"aṴI?/vVgup5* Mz3~-IMBí#rYպ0^B8Ng5JON G˻unJQg z(.{g˸<@+H/9 $g͟ }mMfE28nNDޱ]}{'vZJw|Y-,FH)"}c.qJ@]X(u#WG޴()sڔA;89PEJ;7Y[8c@u*$'M$w4֔[ت:*% %%Қ=>&"ygOm%3<"AtX>M=ٹÄpÇJN''.X9E۱$Z{@( @DH>QżFӪJ t E魀EJ%)O߾+w¶o]uq/r%8-P/4׈H F8}Com.(H:K߹^f?d"RtLK&w%BȬNy3&DT 0W<æax~P]y"{Rz2tΤm{YM6֮h h\{(-]8b1)ذ[:LJ4N~$SuyWӂ{YRjQV*z}w>|t10 "`5 |ܔB-ʕ}Es>u(a㷭6y"KX#q,o:&E-VIj@dQg;ӵ_M%XWM W4LTVF§  P_?B);Sz`Z(F^d::RY%ViI~-̷Et[О`<sj ~g,w.{,"KAy f +H*O=^BIJ Z 9B6k!ba`d7iMR_0eH#qC8ӪQKYt6"gJ!2^ WhiC3~Cݘ{UƸюZ^8rPTc6 ]֓<4m׽\qKp+vDZEXuEѿx0Ӄ婡:_^GtyV$,)`IB7^ϗgi=Tf` ةWd3IZ-J)"xgaM 3ѫ/P61S'Lr搜U0*w+=#W 8=S]@xY|;<U0 [s\tm0U˦#AFK*m$jz}{ٯAya*+iχ}Íy {Usl8ṴR~U($:uL i L[lRzv_VysPM}_$N^Eބ|~%%_p;^y?fhFp@=H'p]>84g͸.E@vF֔sϮ<[%duOrOcKm,@ iSy M0|ɵY-w~z1^pw}G;7jH!^]/ϵ: qJĚf)0;g+Zڞ>Fi!gm'OhĎ<o@c xc>͗$8tqq́>6 Κ i,Ew1emd#\/ brk^txϤKUQS)APn9'CC\# xE=lPU:B2XGQfhC:5٢X&;}@r$|^q<7 kxw6~~LF%U)^< $? 4۹Z~7xBޟv$~p/H:*Wnhk__-zQ+c:g ӫWAʼnJI)W+; #t9k0g+33cGDl\ }f"ڿTyn}/_oAձ3?ﶔ;~=/}'En?ϱvOYމ@҇ ;M~WL 9n֐f ,s=udXdDmf $G0s ]zf`+ eX3X ˕Q.Dy)@FbF% F)7IF6{6*ɨ`BP9용$[Sta Jƺ;*jXuA>0u6 *=,,_x02{N|\E~f?OgMxCպT:5OΜ3W2s@۟ ̂?zͼL:#Q'~ƮV\!oM~B-1FgH0Zۃ79 ûDzΠM{EP :zU{ā^{>| DQ=1)4SxToSڰqԗO ]t Rj,/>櫒61J:=[j?觚xR! ]Q&2AY'4[?&xDyLf* ;a|2[I~KC>ެ)km5IpR RڣLaYVV&bB,{ -CUb8 7*zv]4e/G/FGbڂyoBp>*y5MTǼVSI/iaΫ>MYfELjR%bl"/MQͬ|X8H*Z˧Rl!SɜZw/ikxZ5{JKJix\n:pHjyʎ.ֿjVN7h_bʡ r^ktWta&J X5_w|[}3AݣY0EA܉gvxrb%~xYkߺj&4و1e%ͯoz{iCr;uJ/ރQQ(> >6:+Pq%o * 0jh.K {X漒F]g#rRp0WCAEqHލA 6AnGt zI̕lWXj^k㤧5Y>.}.Tx mP-uCDRPu!bOsczrR I! =2e4,kJЛ|?T/( @` `<{?9BI"WAgc{'/gPcvX{ܕ={م) ks="Z:!LGU8|;pzPH'4oZ 1v@Ǘ4)=, *BTMBl̻;2!*0$cX]Y{[C櫽6ϟҎ6!V[Klk#ϊf-&Q 9o&%~UH[~-mo5#R$ &oM@2VL =cz꯮&癙r3Z6rP仴'# XM-V=_6|NEAq xEܔ=m.ddJ%L@t õ1q3]Čz < Q?K?Dv$18 Tɯ B8ԂkȮ :?M9'iS'A{L`.M T& QoNsb6ՆiW6 0[7s_{m}_K) N!2S\lV*O(~{ѧhRUiݾ_]~n/ө@nNۗBgTOsdyn-8-w3F%toIio v\/!GoVU~?$L&k7AkhG)Q4z&Ш\F)zڀ HZS w|evOtzB\혯\fMHBOoS_4} ȧK,LWd~k0kg:D_Df@蹴Mך:J%៮@~|fkm*u eVęq&chքTP(?Fдۊ1YC}.Gw߬TOqvpiBmhQy(}np7Xq 'nb7*Oko>CюLIX*}8k:7 brF_P4̻iv ]?hJv&υt=!+G'xDѰ ms7Ooj.pV6>+/;+UrE!Qd|f1Z̓1|Cx`@MI!r"ҹT8Ps[L( ݽ>f`wmy!:xZF;9|o䂑qKPЃHFs'K M@PZɗ _D샟 ;'I-^{gr=ƤKT$}KGu䉼-P9f/j;NRHN~"Ql6e@-HDdWO$pty"' lDSfNcEG8jQ({!%M'89FË=Ưo 9[mݍiJx8r =Z; ;js`yivkSpv";\F؋HYLj}qmA. B'3e1,Twǯ%ڙnm7 t3lj]bc3Jo]@}RvGE6*^ [#ze/(dطL%,D?Z:)9@WD4L[5`;:5W="YS}3mJDP&4"+[JmzvZ)4LO㭊 9[<|< RlQIUm=LodS^=D&سط47:EBi0'. 0d <$73d\ʿȢ&'z,ۇ1cb,#-vꪃ6+68j--JƄ2_1w6L1ܵ0$-7|̞kP 0{E f[?d2E9_Tsɹ:T* {?^0bToÆzD㿋cnGUWu_*v75ú<)b'-m&܋;08\,K@]k;묏Qmq)OCV;6qwQ 4A)Gk -K$J4U:qrd\K{f8 3>U NjǁToCXv \6!S;J$#٩s|;>l-dQuѶ FnpՁֽ"%Mfӡ +<l4?鉇m 'ͣA?h gYDSrM0;~r?G:4{uKpO`DzNqyOJAHq`Ī -`~O:qu)(KSn-Lu7t1z!M1Ph{K4x,SL+BR'VGE|"Ioe[/0jt8xꀓϏTѤX4ZB}QɈ]Ƅp5s6t9AeOoaȶfZS?)f6DVP(:#f9q?V`imoh'kSdnT= ݀෮blCQ&#^J'gg f>}5V6尅?RɅ EbWۡ W4eW0b+# WPmdk jύZT~hϒnqOXEs\R26)* ) P2dVzVz tK+@/ -e+HdNBdLv #S4,UUum_K˙3V>KM'kCKS t{|1H>ӻS[FK%i$aϯP4QY97 ̿Sdlu3 МV{ή̯U@]bqE*+z>dQws+Lˤ})?ۗ+}ާbZGP(vtMqYTFf3+ Z5pFFXy"h/ox=MR*OiYk7 ԐtBdU U5+L;̷M GA#wՂQ)>y̐DR2LgV>֭0/E͡6OͮI% %_ufūsXq ;&56dy̫^ }O9 %KZh0/2:{yUo1^'Lx!M1zy]PӏuNZCÕYfs צEȾ?"x[ E=1%l_ھseeӷGܯocJq>ScUڨXPMUrJɠ?=6ǿ/dqV,'N ͢;_K>Q0??e@m;_=(B|a 5ko)O> 5=to Y 2<01ywŷ/U%ds ۱nd&ծ=st@op (%TtT'_`LU9̬]Nje &˒;{b'K?@ߪpT /}LAa 1ՓIƵG&v8#|t>A|Z1*(VޜB}K! V r [)-1W+qGX(G Fim|y:I0'?f92X$87V+KZ\.S}~wC׊-0~2Tsg0քCyZ42T jpLG bS02p"}sNG^YNֶqyk&nlCb;h7s5m<1xLS뻔-3_@ɫDa#p 2fe;ݏ\\/?RnqYA_Qx87 R)f;ݬ.J6._!d~T3jޢx=Șȃ:|)ˢM]SB̘&7aa O^{L*7GoW'bs->IPJ8,簪JO %s9rX{+0 # PQU+YK 1&.:nNƫSWYȅbroϡh/US`É 8GC,;Y?Ihl6нAP3{0 W6:{أW$߁k<Bj"IwFGʡmQuPPkUJęw#իf],xX9--?lLP02Ou3 OS}A+ vufIm#kPE3K[Q< 64y76sB}kcɶzif!i昺 lmokK24h9x=g^gt] 7oߜ#@Pn?h^COy}ib٥U܌!J(Ő ]R 1}uӱUJ=sp`ɀaCn)A)75\ك.ۢ bA|rUgy3c":Ǐ?d--!քD4W,r@1ޝҾqQY'3Lqb~svزΉAF-b/[G÷e"( \RŌK]q9HUBi.` LތGR!+)xaܙU䒑 Y/v YY0ק簌:|yߔ{| mb><-,'o~8"Y~PWL̗djX/cs\9XȆ%. 37&SBy;I|nxJh)|FNvqf+aimM_ԋeZW WRJ;B9Ip瀿L9HYyiB?9ӮZiMOzxPtg[^݀04|0O\.NZjɢɊf}LrW_ACarjSz9rR\#2+[5d6=h, R[#xWoJaRQxV@cJl j?*a% ,J$}t"?Hq:N7Ir2@$#V[oN#$)\"\}֙(fkhl}/L-iޏ6('q8&S+mw.2NDΦ\KVq?; pӯijuOroJ,,癒o22M:8v9:+H wʸI*qo9'aFR]+G Уrf'O|:d |WcӲ1>s*z$nJ5On98~-9#ƃgR}!gZ,%wi֋*HVS89 Ax]FsװEt]$gPj7Pdb܂fhw3V]D%3?gu@1QM$ V N${`o@<\_IԊ~ Hc{j_F׾ԷH6V@1wܤG -Q9{YE|H)J~)!;APLB _k8Po]fQڋrm<(U]׫tADKIHR}eބ;= ]V/J'M=X_ZpyJA]n^6KL$iY2ߍC±V`8.4xl(WN˗ ـKСҎlB&˩ʐ6>MӲ7Ekzvc+p{l~ G 65z/h# 0 ~Cm"5DJono_b[g(]f9ۢ[AY[jsp^7bI$.Q {=DQo B&41Q_2𡆍AvTY0}Q;'# ɓҠ7LU$vk p `fXسxi9t*+B'ܑM-XrB98~:Da;}4X՘a^RM{:ƅ1]%)k} p84wY< `Bwrʾ|r7ї.e]JyTL?M!snut/ۚP?ߢ36 \FȅEi 1R1tU:I}spY"ni\^NFut$@c Xg6 l:f^^茯A,ɞm6|ɃT-E{PҜ<%;j,n9lc!B=\2epevT(P%=Cˏ0y$FZ> ^)PdlcjR|x<58,Qu_~}8NA޴"Fd9VC9](̯EPZZu>S׿@ٝЋ_ RuNcw (ym[v}9x96`X >i3"BDJQk_pȕvdՉ'݄Z 6kd)<*Q?\颰pO?fNx)_;BoCZWk2[eñ V2+=2˂>JTy꒜gX5R;J'Q y%]3mʥy3\~$cT5]˘ j`Wuqm*#%i+sbGffs!XPa½In=;/ɑD* H+uY'WqQo#_ D['<߀Ih4b>X+P:9hQDW/?tŖ}Sc ^u87@rJ{6_l jM;n4y17JKغEY*/EZUЭ"뺏_"22GR9w=lYGq%Z9?7H=P~J #)vM-* xIAe߼i•ڡ>hzH_܎VMG4S {?ӎؙK?ۖglK';}^H;+u-9~ۚKMտٌəm0'9\Qm槙 3hk2&~go\B߬@>r*;urܲ 2p*9H tZ(ܺKDW &%yNp\D˜ /)>&iwzxmdU]RTy^pn ve]mgtIR>Иq_XJS?U\:XהBa,WS䇂pOpT|c%ǕI}5Ggm9X˺']dt|1vѣa{S=]{HpriVE8xo(M^䮆P ^\KԺ"sMG\| MPJmpb}v\h- u<D]բՔj`PAc`CZla{}TOAya'-Nz3-VU*f-vtOWSAX 5:Db}eU2f]LUK+/ţ&+gappHH.nc2QqJu&KKw{n_&%G7 LQ1Ygy\=( 7FQ[`b8X\ iSa|¼0T\s ȚS;yVo߽?xR[ϓ6$?z78ȅŏ5#0eX܈0p7C{@负]nȹ>A@I0~c~:3cF5;4d?] 쏬o?U9Gqy g:Z[va47~LŻ[G'x+q/_zti\3fr|CN T09GF:1VOGK~8MLV tBx?>1RgVꇽ4_)X&("Bh(]7Q\H֛H"iBO-;8zt+6~z/\]ZeTÝ+ !b'2,,ꢄqw Yft =^b}Ld:pcj`(4i~5TPj W7 yْ ~ІNO6ɪz몕ossكzEN +dtBoR+{>6GZ 1Du 5Ve.(&[ RkQ|&I!O="pfPtG'0cX-c.?ȇ~ЏhG ?BB/B$CA@OVzVzVzVzQ&8o@~O`O`O`Osimutrans-124.5/src/OSX/simutrans.sh000077500000000000000000000003441520675460500174110ustar00rootroot00000000000000#!/bin/sh # # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # "${0}.`uname -p`" if [ "$?" != "0" ] then /usr/bin/osascript <<-EOF tell application "Console" to activate EOF fi simutrans-124.5/src/Windows/000077500000000000000000000000001520675460500160055ustar00rootroot00000000000000simutrans-124.5/src/Windows/MSI/000077500000000000000000000000001520675460500164355ustar00rootroot00000000000000simutrans-124.5/src/Windows/MSI/ProgramDataClear.js000066400000000000000000000001661520675460500221460ustar00rootroot00000000000000 var fso = new ActiveXObject("Scripting.FileSystemObject"); fso.DeleteFolder("C:\\ProgramData\\Simutrans",true); simutrans-124.5/src/Windows/MSI/Simutrans SDL2.vdproj000066400000000000000000032014761520675460500223520ustar00rootroot00000000000000"DeployProject" { "VSVersion" = "3:800" "ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}" "IsWebType" = "8:FALSE" "ProjectName" = "8:Simutrans SDL2 MSI" "LanguageId" = "3:0" "CodePage" = "3:1252" "UILanguageId" = "3:0" "SccProjectName" = "8:" "SccLocalPath" = "8:" "SccAuxPath" = "8:" "SccProvider" = "8:" "Hierarchy" { "Entry" { "MsmKey" = "8:_003EF520411A46E6AE484162BAEDF263" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_005ECAB11B074C5787B1F8129ACD3DB0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_00978D4D6A334BA58E0D8F5EBDCE7F18" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_00B608AD6C5F4140B9EF5D465B3E947B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_010C9071ED524D8A8DC6A171F492F08E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_01D6A70C8D5048A9BF9BED777F584437" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_029552218B4E868F9CDB0806BE6E9ED2" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_02C7691523EE4C7AAF2C42A5F56C522E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_03030B54657549EEB3FBE9CDD8F47144" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_03294D718D6D4CC89C9CC669C9E3AC45" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_03333F46557E4AA09C3D2EDA7DABEAF8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0387841035034B988BC7744F512624A4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_03C3084D08E640D7A56876BE20147A37" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_05C99C5CC13C41A9885ED5CAAC4E241C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_060F8BEC80784113B5C4D0565C7B9B1F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_06B81764E3B7488D838810FAE42C1829" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0715F183A84541F5BF5D7BA3BE88E0A3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_07484E10F2A34831B3E6D9052C6B64CE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_079B392C00696F9EFD81A8F4C6CD66E5" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_07DE11554B2143DB96B5DA78A0720AB9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0819F586738B42CAB30947A5AD8337AA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_081BCF65CEA34B1093E83B88818BB39A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_09BA1EC65CC548038440E1CCEF260625" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_09FE9501B9E54975B984B8C110C56D54" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0ACF34CBE28DD32DC79D67B59FA64626" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0AD370E81C404873BBA9F6C080DE9986" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0B4C01938497473C97E1330B603EDC0F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0B96ED126C9941A0B5862E17EBEAECB3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0C86FF1A217F45FE9BD380453833B87C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0CC8C3B5975849E7A0674BC5B24FD640" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0D3020DE4E8ED5CC4AC9098B65B48DE7" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0DF1600FFBCC494BB4B3A3B41FBD4C45" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0E07641E8CC4496389374E089BCA52E4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0E90A6C737F1457096CEA2E4EBF7F70D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0E9BE20E59D54B6EA446FB9399D2BB1F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0ED6E2E3EA9748478583F98377A57988" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0F28D5341A894A28A785BEF0499CC2B6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0F9A37FCBB244ABC8A8149D9DF2B8563" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_0FCA3DB968B942E4BF29F988842AC875" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_101DBD56305847DA96CE37AF80AC07A7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1105F13F49EB413D8862437DEC6BFD64" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_11193214718A449C82F62145CD8B2C02" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_11DBDBB9ACE64BEEB7549FDAE6CF5500" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1237F66E84B34262BFEDBDB63174DC1E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1237FFECE674421784292D366BC64FA0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1247C8B3EBBD4CF5B1EB76A31014553D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_128F2CA87D9A4682AFD96DF5D4A04141" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_12AB4359C4B54FCBAC46DA8D834EB09C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_12F07DD4BB194D0891E2C35D9E2AAE00" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1339688690AA43C3A985A46189829DFE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_133D00B493E44429BEA58682323425F9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_13506C78AA494DA6AD4BD5AB7BB66DFD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_13637737D70917BC3CEE9C780E1C321C" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_139DA0EE1A894318BFBD62365131B07D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_13CA5808023C4A98B16E6F84BA0007BA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_13EF44563D4C4ADEBB10945AE080BF57" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1440778F3A70472595A747394B138D8C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_159A716BFFBE4242A25D18DA7DF569BE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_15AD3E75D8494309B7E9D38B67792845" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_166AAB6C447F4F14987682E2AE9E49C1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1674AA7C5F1C48E5980265AC6A9165B6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_16B9E22B4A6A439993F94972FB73C067" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_17B8C2EA02764F9CB743E2C2035BF903" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_181A6C90E6B7458EAB2292B8BE635C8F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_186AA79D5784439EAD1C5E15124C7F7A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_18A83AC11E07457D9E0B0E13BFCC3E79" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_18BABDE15592474D9AF3C78276D8449D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_18BADB004C1441909DA8DE66D75CDEBC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_18C933E7D69946F5B947EC8273AEB559" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1918ADD2CE144D709D7289D49A2CAA38" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1965A5D324AA4723A6D357265F9AFFC6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_19A98DC8BB0F44B5B90253ABAAFA14C4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_19BD0EDF53564D459C8A659AD26B22E2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1A3B66C3F3284C54AFDC39383717874E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1A52BAD08FFA4CF7A52704515E265E0A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1AAFF255E23343BBB16D3EBC42EFB21C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1ABB22A9F34A4486B4D0F7D6A8ED75C9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1B742E6F5F794339B7B00E5090AE41EE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1BC8A4B2533D4A9B81A8D906E75CC892" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1D713659992B4EEB89199A782FF15A97" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1DC828055A404A2C90A33E0062965415" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1DF38A11E5C74DB9A741FC9C10A3634A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1E30A8FD30C344B29F0C9B8BADAF36B4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1E43FD6B874142DCB7A19BFB907DC4E6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1ECF0A79127649D4A462C15FE8EBDD52" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1F78A7C6DB6849A1A3352CC3B935FBBA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1FF22B3CBBD34BDDA475ABCE7E8458FB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_20AF94701C3B4F31BE18E4C18593C65F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_216D641690CC4689BADF94365FA2B97A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_219E1B7730EC46EAA280143D520AE19C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_21AA11BC689E42B6B6697A5FC318E096" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_21AED397D928464F813731B7A635A8E8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_21C491A79E964383BBEBAFB16263BCC0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_22571262D0E1464AB1C4D661D729A7B2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_226BCA5FDA2B4579857D5F4C3AF3A482" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_230AC91992494E47953EE25EC0732E52" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2310BE29D51D4698A0E4D57439C7CF4D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_24C7BC64F7BD4CE4B1EC004A5BF37584" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_24D43BA152AF47719E58C99043546C39" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_25565CC3AFA2488FA09185A4D3536AF2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_257608DA64F7495383410A50CD623626" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2580742A58474754BB9F6E8DAF113625" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_25E373020643480BA2C6C4FE1967DF39" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2688604BC1004159AC65DA8026AAF404" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2692FE4659B3459FACB55B2ABABCEF3B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_26F725E6689A4B47B5225B9F9A6C4CB2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_280DE28E23F3432A8CDB55AB067712D2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2820718EDD1A4C0A998A555B73E7941F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_289828740CCF4491AEAE9923ABC9E630" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_28D87BD82DD242048CE3AAB274E4F224" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_28E16D55A93A44CAA25DB060E24E199F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_290A6460DE03485A9ECA056C13AB547A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_29ACEC7A06E24A1DAC101B2DBFFD8054" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_29CD1E15907F4CE2B442E61E72296DE1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_29E0B719E1D143A3B435F746D294CA48" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_29E3ABF203C04D2E8FF9FB6AE98967C1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_29E5E12EC22646BAAEDE11AE02DC2C61" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2A7AE2771A914BA7BBDEA452C9DCF2F3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2B5B075BE23041988B08F61EF1DE74AA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2B6156C249D14E63834D6BB3CAB3FF16" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2BB109D12B614A65BEE199658898234C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2D6E5C7AE3F64AC7ACE938CB8ACD252D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2D94FF39A27543EB95167C67429CF990" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2DAFA0293AE04710AD9F7E863F3BDCD4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2E231200DCCF424DBB14BCE8FE6D93BA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2E8428F464504F4CB245EFD3D16D3342" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2F1E496588C3496D8E4F6B8FEC90DF9A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2F4938DE7AA34B87B0D997F896311B0D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2F8D3E0ECE694AEDA82C5432F24C854E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_2FB0C916FA6E4D568517572F31975178" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_301A99763E6F471B96D9FCB200CE960B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3048CD4420B04A098D932C7011C16F75" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3067D12B03BD409DBED5BBA78761E959" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3075C6BC47214728B90B6AFC317EED62" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_311BBAC6B2C346929B78EDDCEAA25F52" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_314549B8854B4E04A7B69C5A1151519B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3156DFEDDFFA4625949A11A1B0F470A6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3179D684E9B144B78C3AF7E94102521C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_318FE10CEE38456B8FC8241021019358" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3194957A2F144B9DBB90D04D1202A0D5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_31BAA09A108D4F8D9457940B5856273C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_31D813A3C334427AA33385173447DCE6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_31DEDBED22EE4A4DA099BD028FB246A3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_31F6E25224B744DCBFC0141E79D10A60" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_323064989CF245F393B3FFED1FE07884" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3233EF6351004F93B9715A0A7AE4FA74" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_327F373C1E7413799B0AED3AF56713C3" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_33296DAD659B4BD39798C705F02C7834" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_33BD05ADB1C64EC8966999E905C33C9F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3487ABD4F8B94084913A128F4AB191A1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_34A049B6090F498E8A74F7782E6AC542" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_34BB38CFC48E42209DA5AF5500F6D2BA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_34DE603A33A04466AC350B9AC8D6A0A9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_34FE77A5FD59404B8CDD5D21FB0AC148" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3519CCFB2AEC4E349CCCFD738C500238" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_352D3120C5B04626B12EE20AC398F144" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3530E1B1A1844BDB81256561E663927F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_354005FEB66444AB84E610AE09EF0FE8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_364C50343A8945B59522EB0EA95F7370" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_36993815822640F6AFC910D125044DD4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3718F392451E49C8A03D48167AFFC98B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3721799D17B548DD91B4A37F71B2EA78" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_37605F6B6AC74871B93AF782EC31EA68" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_37666D0AB6CD430A955269F7E1CB6676" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_37D5EE6E06794EFDBED006661DB3CE03" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_37F86EBD13864C7986A4D05594E9C7E2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_38252595EA444B1DA8102684564FF3EE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_382A9A025A4C40BFBA389633BCC7465F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_38690161FBE14CB8B378D3A573CCF35F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_38935C206D864F57B6900428D9DB774F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_39EE9887C474434CB0593872C88614C0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3A3A98EBEADD4475AE7E4F2154758DFA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3A45C9EEAB5F4D41A9EF96EB0F70FE98" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3A4F1A9658FA4B0E805DFEAFA9B5C2EB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3B4AB35D4B814E49B374573FD9B41D9E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3BB85C849F4A45ED984521EF7EC00FF1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3BF8D681CD3B4B87B032DCF3C00FF926" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3C1CB9531BCD4293980FEBED7E010E28" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3C5211F08FDE4E4490BD5AF66C9A0EEF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3CA70B6D5F48488CA38489C0EC76229E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3CACB35ECE044CABAF73B3AF522C618B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3D42F41E95F74301B3DA30722D7F4D74" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3D6D0B1545684FB685D35EE557CBB335" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3D996E275F02498B99D4B122817672CD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3DCFF913F03C4FD48D362C46015CBA63" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3E2C5CEFA60D4DF59BC3A743C2073EE6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3E51AC108118479CA3F94EE04D6F01C0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3E5E32603E444A4BB9392F2DBAA25BF9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3EE1A722BEDA4ED7A2ACC4BEC7F4C4B8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3EF0959343750F1BB1E3683C3BB95052" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3F165DBD8D454BECB94AB3737DBE82C7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3F16D51E7A964A99A35D575CB10B1C2B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3F1AC2D8D7DD4F49B318E45AD0000E4E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3F3BE766613F4BF29D52703C4C0D0427" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3FD7E53C07E241C3BCD00B81C59CC66E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4057B27E9E494E8CBB7D1FC8FA3EACA2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_407A3BB3FFB14445B01632174D68B032" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_40A0BE0449894903AA520D9BB5D5EA6F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_40BBCF7041234B0E91AAC36E71B548FF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_40F016A1CD6F4489B117B3264DD98F90" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4147D6D1F8A245C4AAC7DA2E72741900" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_41600A65D8804208854E48546CE03C31" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_41DFF372121244008F06B6CFE66D65DC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_422F124DFC564135884EE030F8A75A7E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4263D1383F6141CC8FB85D9C89A37DB8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_433B0BB603EE49AFBE61E4A95B62F025" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_43B4F064B408462B9CFF09DAF89256DA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_43E4EBE3CF4C483F9B9C3B22FBF18EE7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_448B7A877DFE4EE7BCCC10E684A9F9B6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_45907458AB0F4F9BB5167B3C22912220" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_45C20A6E3143435AA6855758F2D9E540" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_45F7C42A57FE478FBEC1428DE3C17C67" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_46264B5F868B4521855C85BD897A8A84" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_46456885516B4B789AE75E836C9C05E8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_465C2BCA0E3B44E98A0C2EA624307C51" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_467B5D314942411D8AE3142D49E698C0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_467BE99BEC444EB28162927D6652266A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_46B75EC9AE974FA390E7CAC4D35F7919" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_46F68A58A67F2A54B4325EB94650EE7D" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_47293CF4BAF54E8CA44A9AB83AB3CB19" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_47CFC2CBCF804F13AD453C25372C8730" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_482768525BE34C6BBB46DB7ADBB779B0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_487490D85D3845FDA7F0C9B3FAB37393" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_490DF7497FD3469186609337C624758B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_49B9EB5E83934E4B8E4B0B9339FB5FAD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_49CD0D1D118547D3BE550596B2889432" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_49E3C49389E64767AEF91244E450BCF5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4A24E208D93D4E83B56C55A98B427CA4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4A85838D1D85427897D4F69693279715" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4A8CB7B7A27B416E8D5F16F16414DAB5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4AD08276500247AB8FAC3EF4B77F267D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4AD33819C37B42B1B0B339CE718E5CDE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4AF573D713344BED9E783ECD4C1284E9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4B6BF260CCD642F6AC9015FD883DC989" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4BB786F99376461988413CF1A585AED9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4BDEF13001284819BC75A57512F7E7CE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4BED95EEB2484DBD9257998035AE6209" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4CC13851778543C889504D03B5927C2E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4D3B4F373E4548D6882E71F29ADD2774" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4D4A63AD27BF461C917A7C7A92F3A084" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4D611D172DB94EF8B7C138B581301576" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4DB844664FE94090BEFB466C6763B36D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4F58BA38FA2541B58E8C52828A5272CA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4F774CD205E24CF4BE96227A527C8539" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_502FAC792C1D8614FE7926131D186D21" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5078896E10A44B0E8322B5B2DD9ABDB2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_50DE5B28CD594DE7B5F13CFEE25C43E8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_50E96DDE2E454243BFC25EE2D771B7F7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_512BCD1EA02040ED9BEEC446FC50AF1C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5140C10690D54348A4368EE02676B691" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_519DFBFE15F34747A61A95E2B9B10BA8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_51B4379EA7764F718E23148EC7F180AB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_51EDC323F02149719E5DF86E69F1BE72" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_51EFF26BEAF246629A4975AEA01A1470" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_522CEDAB5F5347408C153DCEC08D1B91" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5241ACC17B5C411280903A620FE31CDE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5264B3188A1F41E895A034ACBCE23609" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_529822798EDE4A6EB66A8FBC79D28466" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_529ED68FA8504A2596F83E3452215DF3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_52EB158E737145259F9D9B99E3A0DC77" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5358F97ED18A0C27CFFFA550621C549F" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_539D40508C6E4E75A11EFBBB82E0CF8C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_54BDE901C9354D4294895144769BACD8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_54C84BAA19B04F939D0E0367B7F17DB5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_55455C91BDFA4715B97E7CE7754928E9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_55A356B6F2BD48BAB0EE1DC4089CCC6A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_55B15CA598B04AC8AA57ABA39B5737D6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_55F557FF2D5F4FF8B4B841480F8E02EC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_561E7344F2D3457184C0231FFA8E0D63" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5649E800E882443DBFA060EF7C6D0C54" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5713DA04E2164ED6A58DF2872113A1E1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5722A6A9D28D4A048CA3FF4C5C399AA9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_575341B1DC7743458EEA436605782806" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_57969C68E2AC43A8A53EA72F3B6933F2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_58059A5FD7D842D4A019A4BDB501153B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_583AF0963D3F49819D420B80764536CA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_584B82F1837D4263B9287A526B252157" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_585E548884E1A0024245C4DBE90F6893" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_58ADEBE3020643D893F8EAF29C412F0E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_59052B74FB614B939DD6A4414515B755" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5977CE44BF2F4CE3B66EBC2E9BF91AA5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_59E2DDBD34F64EFCA5911828555E70E6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_59F7612898734E4DBD36DF9E76F2C256" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5A61100119004DE0A914F075B53697B3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5A8A8FF3CC8D4B3595A9DAE1150F703D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5ADFCBEBD3DA466287FAE7D8B02A2907" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5AFC05EFA72649698E65D01DC3A85278" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5B4BF2BFF84142708C9EA8FC67DB3216" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5B73D425BB464648BD3F6C58AE543014" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5C6AC9E04FE740A4B6E9ECBA0CB93F7F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5C6F45BB13064E52829F43B6FFE00220" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5C9630709CED41D2929492FD463827D1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5CD15C85FBF94FBB9AD95AA7490A45F0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5D9D568EFEE54B5A8A2A88644C400E6E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5D9F5328272845C3B2E2857D1BF93E74" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5DB6AF2EEFA24BABAB9B45BCEE62029D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5E76DC3AD0814F04A545A862CD5E7DCF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5E82840C0671457C8E5FECF7032D190B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5ECEAB174780441CB6657A851C694B5A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5FEE0DDA9A2847CB9FBD5EA59EECF525" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_604DDCD3A6294FEC9A6F72485DC08DAE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_61DEAD687F5A42A9A3CE05CF43194861" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6216B709025B45618D3CE7D60D744328" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_623197F7D11B48FEB04D649E84458841" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_62697F60BD184B3F85EF352882FF0FC6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_630392339F2C48718E8D505AE3524178" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_63090592FEF84404865D0F30FF65D3D0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_63254E53870241DEBABF85CC64023D1B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6335879001C8412CA64A127760C570A0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6336B3E3BB544A79B2748CFB446ECFB9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_639BCC27C4264E149D85E7E705C52710" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_63A60E70A6274498A49DD55849187C7F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_63AB0309FFF146E5B9A8BA431BEDB581" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_641F8E28A18740EE9672DF14F4322517" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_644E9138F1B04425AF2CA49C8C1029A9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_647D7C02726B43339F93308CE7419FC1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_65634BC992FA494598EA6E6C025512A5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_65CB95D1DE44480EA45158A755AE64BD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_65F46B0D45B646C4BDBB84B11FC74881" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_66A93844669E4AB8AFD7960F553D67E3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6702CB4A485E4C3C9B1970AC9D77FFD0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_67073B82D78742E5B9F3A6D364AF649D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6723BFD2968F488A8B21778707DD42A0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6734839905EF4389AC62192A95B835D0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_675B1F5C00FF4877883A37F4EBE1E3F5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_677C07C6147A43449F9AF59CAA3DE045" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_678A6CC7CDBE4341A42A0D8FA05D8A80" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_67C77B7A81734DCD93763CAFDB731F3D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_692C781C1C4944E3918865C3016F9231" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_695F9E0D19A94E9080FCF6161EBF07D9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_698D6BCBB25E43D6B7AA5EAA8898E4C0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_69A7859F453A4A91B8E1B6F8EE0E3623" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6A45AA5600B54C83989EF933E7D81F00" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6A709C49B7F943C691E029483C259726" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6A7B7E930C944000864ED2DC074B8944" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6A8F2A7B2D0E46B7BD05ED75ABEC07C0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6AC4A1E5DC494E24B5BCEF7E92D36816" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6AE881EFF6E147EF97DE619B22172E27" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6B365598BC624617B2B10FB965E239DE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6B5218A02D434B9BA80C49C643184341" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6B7612CAB6554AF286B04C35DA865321" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6C3CF89286EC42DAB9EF964252360D33" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6C7F90DDF17D4C6293C647A90E00E279" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6C945BE71CFF4E82B4F0DFB72C292BFA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6D01F4293A064FF2A6A6CC0F0398D328" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6D5F0504040C42649AAF5C80A6C93AF3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6D66B32FEBB94455B6BFEE30846CF33A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6D7ECB4FFD0B48E7A13074F90688F225" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6DCA48436F4847F5B3F327EE350E3AB1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6DCDE0BC21E54EEFBF9CFE05BEACF621" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6E5DAC8084104079B655A2FCAFAB3CC2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6EA80C01AAEF49228558822219AF7ECD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6EAD4002E15B498AA04B7DFB144B3917" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6EC63EB0D6364EC391551F261C4E34AF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6EEAFD65E9A14B65A1FD6C694AAEF48D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6F75DDB2EDC24C53BFFB2A4CBDCE7D32" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_707C1BE3EF9242BE9746771AB5B6E60A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_71225218E6B24F409FAF954309A5F4FE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_71C350E8A9B4499694F7F9CCF037A2F7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7243A246D6454771A7F203730142ECF8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_72ADF54DF3BB4CB495E986114E1E2DF4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_72B1848015A8405BB280F12F7FE44143" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_74126279572F497EAE3334BC10BD9420" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_74CF407282A64494B3743E844FA0B38A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_74ED0456E40947CA92C62824E4125894" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_751BF54EFC91407790B42AD4A75C158E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_75982E571A6F4F2A8349D07115881603" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_75DB0F22A99B487FBCBF4492787CF16F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_760A7DFAB6A846738F456A2D9B75DF42" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_761869FA0B4342EE977F57311DB8CBCB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7627C3286ECB4901B7FF66783A56EAA0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_76DB0BE795FC4B5BB8D0D0A037368A67" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_76E349042CB043F2809E4858A2D9A63C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_776CD7D0F5E84BBAB5A8563DE24D69BB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_777C23F3C78F43E196CF3E5471716A92" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_77AA9368DC924035A400D7A741A3B134" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_782A3C3B9FD34371BE3AEA0EEBFECC02" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7879F46087224474AEC649E7FB71EAEE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_78FC1DE773244F7AB6CEEE32A6997506" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_791BD97D0CD441D4AB0F2291EA64362D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7A4B68C765954A77AEBA8D340DE2310D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7A873BCA0E874A5C9AA964C4EED19DBC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7AD1BF4E17F04C1485967C5A664A4E0F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7B480D9F16C7409B8547DF976679CE18" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7BC9AACD90A04AD8BA6AECA487E9DCFB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7C175A1D24E94CD485688A2AE69E37F7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7C4ED6D1255C4087B67B059046DE362C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7D2FFB8DE5D246819C5152D280523C44" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7DE009E5FC2F487FA3A2EB121E49D0F9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7E1AB3235A184CDEA7ED5DAEF4C8CF96" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7E2C2656F54F4DBCAE3C5FD9AB651368" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7E5C15BD3D26417698877173773F3DBC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7F01371C7C884AA79D7B332DFE9CFB87" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7F01AB5E57414138829BC412322FD334" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7F1DC34C3220499EA78996207F8A9F18" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7F275CAC05284C2BAF4B234E294A98AE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7F473696C8894F5C827D4B1AF4EC61DA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7F5E16592EE74DB2AD2B8D96120CA325" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7F86529E598F451A91EDB4A9DF245431" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7FB25388690E4F35810256E3806D7AC7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_802286B2E6AD4D5EA5F7582550D0192B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_80718F22412647C49DC0084C574E72BB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8079B5223A464AD483F0B286A55B7CA1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_808B71735EB742128850F708235AEBB0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_80B5D3C38CAC465BA8C2550F7914F614" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_80E9BB86DA4D40C29497E5182B73AE5C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_81301366B3184A6FB5E40F909CF6AD26" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_813B3C3F7F724EB4BA5EAE500DBD41C9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8183844A6C6347D9BE1A8F17A5EDC446" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_81A24FF94CDC4E40852AA1F5B3802088" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_822E7AE56ADE4E36A4E2E5B00138756D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_825A92662B9B4407950D37C0CDB8F119" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_82C60047E4034EDE9C5AD03F08662F10" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_83189152D1FE45048CECC4A9991BFE56" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_835E8828F7DF417BF90CE0E5B3ED79A8" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_83A75CB51DCC4F87B8B9A3F8A3A215F6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_83C4B947AB2D43A09248D150A192DBF5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_846C24B81FCA43DC933E9BFA18FAE15E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_84CC521B95224F7383B53352F9151899" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_84CEE7DB1C0B407EABB654B361525004" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_85337AECC7CC496DAE6643D82A1E3F60" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_85CE5B3A65CD4D95AB6E6665907FB16B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_861479A1366940A2A130D6D3735A7D16" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_863731CD1BFC40B393D50C392C7916BD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8749AF65C301480F9E7A596349DD1367" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_879DA4798F1F4B11B5C99669556CC5B5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_87AA9DF095B146D986B7BED6B55017BE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_87E970659D104ACDA574ABA4AE56A153" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_882138DA00E9460F86B35552856298D2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_88A3256281AC4F25AAE598C1481FA990" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_88C0513F233B4CB4BB2CB756347E75F2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_88CEDD14A00441AFABAAA66D3806D822" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_89563EA02AAD48DCBD9CCFC5B5EB7A95" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8959E24FC0CD434A8A27A073FDEC71E2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_899749C5350C4225A5C30A5D6948EFDD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_899F7FB3E2F84E6A99128E5AABF039C2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_89A41FCA30A346A3A5D38B4DE2892768" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_89B354D3105949B1BF654E7853FC7F29" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8A0AA6D0C786436E93BD790C69FC2AFF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8A35DF083E7D481F94A22CB3A0CF9A71" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8AE1E9F65C8340A4B7B931321BC783FA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8B1C110612BFB297E730E7DC8AC24F63" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8B37931EDEBB4DBB82D4D794F46F4C4A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8B88D692BFF14DE5890C554BD2597CC1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8B98EC4380664715AFED6BEF0777940C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8C55EDABC5B542B88FF7074E4E99A08C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8CA32EF48C9640E9B151FC50FD50DA58" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8CEB3078E8C9419588372AA47D36A472" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8D341EE4712F4242AF051C6559BAD35A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8D9FE449D76947338601A9EF5EFEFC94" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8DC1AD7B4B434F15B3326D669B5925A5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8E07E467482344D8AC909E3F8F08B953" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8E1348361EB141EB80B348E2BA434EC1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8E2B0BCBB33945F9AD9D4A62B729FCBF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8E77D1CDBDC548E9BC92AC3E05BF156E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8F60949CD512455AB188852FB8723732" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8FC094660CBC4042BFA3CE7638801FCD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_90088DD7694749FD97D2DDE8DDC76779" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9012A62701DB4225BA55AC67F1B5A4D5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_903B35BF02DC4724854FBA8A693E7BDD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_907BB93A2A584C53B4A2808085BE7D63" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9093B505D80E44A88C5B422CF39C3F6B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_90F5B235E42C4301A5E0BE03B752C5D6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9196381DF8F04BECB29CB3A4EB358118" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_91CCEF23F60240989740162A20FFF3F6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_93076B17FB6342D9A680D2DB5CB88911" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_93553371BA0D4BA7AF534632AB28AF1C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_935E39838EEA4F03A10A26790A4CCE11" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_939311F03E484FD2894C70B30E575E58" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_93E45578B9A84ACF886B916820BC4D5C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_93FF28B992E344FDA54C83E61D5DE45B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9413D0887CEA4CEF8E077401A528CF8E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9494DEF39F564D128A2A1D77C94B41B3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_94C3E9F789844176B1A29257B7F54F25" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9512546582D04B0BA8B1EB90C25F266C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_95DBA15EEC0F44B98B81C3ACFA4A5A78" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_97AC8B571A7744588E1B5F4DD29926FB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_97B9A460977F4B43A3F262ED3C9AF963" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_97D429291A1A4B448DEDAC39AC789CF6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_982663C4E3A74AF98D1614ECF1222641" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_996EB12093BD4A92A9C8C5D7A3938DED" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_99F1C1D67C9F422685E2764F4D5E13E3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9A1181B0105A4B8F839A9E88FFA0580E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9A214C6687884ED5AE39CB9809B715B0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9A88343020324D73A521C5512E5801C7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9B24FA4B5DF145758D44C5A386E1DFFF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9B45AF680D7B453183740CCC5186F98C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9B669C48C940449882D6A2D67A5396ED" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9B97A924FE8546D488A68B2055F6DEAB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9BAE51D35D6A4608B5DEDE105547B06D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9C3079C5132A47CA8F7095A52DE30568" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9C8885FA8EFF45EA9CA498CAAAF4D24B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9D2AE5121BA64CFE91973505C47CF36C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9E3176E32D8A4094A57AB6B64B17DD9B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9E747D5708814DCA9BFAD71B7650A582" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9F43E9CB3E4642B384FAE24279AB317C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9F5B93603D5E4002AB90693F485FDD9E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9F82245E13B84479A19ABB0517AFAE74" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9FD82999B19B434586CA9147504F6B69" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A00BAB251762400FBFDB3F5F9DC7CC7F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A054E4EACE3C4ED0A06A7A05E9EBC6CD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A0774C119AFD4A54AB9DEFCD892EF3EA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A0EF7ECB8D1A450790401A38455F2FBC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A110420164074F6F80B1CCA1970AAAAF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A12A3E89B73A4D3FB21673D41DA88E76" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A13E8EA5CEB54C88BB30C6725F581FFF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A1A46558136D4B6E95B80C55A3FA5629" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A1C5832E423B46709ED06E8882B8DBD9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A1F00F4A0643448DAC8E66CF0727F09E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A2611C5487D241FF8B561E52D56B160C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A2A0A247DEBE4C00A67DE0C78793BDF3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A2B299E62D304A90BC01839FD9982CEA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A2C5C7638B7B423796BDDE1B37CC54DB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A3F9D5A01ECF47CF818A94DB7E3F30BD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A40DB08E389B4ED2A1FCD744FB4D5FC0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A438B857F43A4B91BADAD3D54FE069CF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A43ADD711918462F861281E4F45A3ED1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A46752F3A99348CCBCF9C33416BE5DDA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A4B5359306A34023A1ACFCD33C9B6B16" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A4C415F986314D75820B3ADD15F324DC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A4D653ABEC4646799283098724AAADB0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A51036CBD78E4C6CBB96191773FE82EE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A588D7D5733E64B36617EBE50FB31F3F" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A6644507B63B49799F05083DA240574B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A6A9C9BDA5A64E4DAF67877AAFA1F7E7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A6B3B56E517742D49D7AC896AB575EEA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A6EF70389C70472A8E4E4813A6CE44D3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A6FB275BE7D44AE2A24847C98A708103" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A766C5646A994F18811DC5D915ED99E1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A779E5A5C75B4577A215A6927870BB62" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A77D989F8A3F4AA52521E174DDF6EDFD" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A7B5A28B66FC487D9ACCAA1A42521224" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A7F06B6F1E2744E8B7803429C9B12AFC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A83F1194CD164AAA847A140EE8589B87" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A8778490948047EA9E74C817CFBE23C4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A9C9120ABBFE446E8CCA925945456C1F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AA1E6DC2B3FC4067ADCECCDA4B09C94B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AA7A085BD36949179AF2196EB06CF7DF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AB7122076A2045E9A16367FAA2603934" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ABACE35D95144B839016535CE2C649AB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ABF7BAE834ED471AAC122B3BA17D4C73" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AC22050E398241C29F0A0AA66AD69C26" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AD63432954AB432DA360A092F3ABAFD4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ADC52D4B09A046849FEC19C6ECFAA4A5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AE10E8B948F646FC960CEA7D158BE1A3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AE3624817F3F4EC2BC9ECCC528D96588" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AE43098A1E4D43D29FDAEF89B96A61AF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AEE0681E6B8448B4988AB3F8BDB95FD4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AF1BDC5479324EDEB012F7C64C02297C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_AF20A26E81F541948829FB5AFDE66541" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B0115B9715024672A9C1FA4CF0465956" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B0174050546944A19CF028E1DE44908C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B038F7BAA7974FE0A200AF565B96E33F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B0F23AB72E7A44AE854EE09E4BEFBC66" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B1094D229AD5442EAB76FC665782E076" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B12A038D1C9049108A3667AE89A7D7F7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B18F2A9BB0514708B11E2E262F7D8610" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B1AA4D786277439581E192B8982152A6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B1E1D7D3CD6D49689CED7E882E2FB5F5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B257E8B4219C4B298174488F5BCB7653" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B2EB2EFA27E54709B879C952BC42BB4B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B30E29F10A116D760074368E5BEB7A1F" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B316F19F34FE4A51BE7EC51035DB90B0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B371DAE4A0264666830E52D9514DA2D7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B3E5650F97D842528A636AD1DE430393" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B4B6DAC3BCDD42A99F77E9098980726B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B4E6E7EB7B9B42DABD1B411335B4429B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B566F21CC84A433381E8CE406761ECB7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B5C875785B0F4C9D9E76B3EFD305CF39" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B5DB74F3CBC44655AB3DE56EA147E4BD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B639E4ABD1444D78A8951A35EDE48EF1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B679464E4EAF4C549CB3B9BF93D9358E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B69073812BD04A2BA4FE0B6A8728F192" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B6A96A7B568E4FEF8FA8064518106BC2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B7CC8D077EFE42F9A0BC18F9A542E5C5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B7CC93E185B646E894689577E08EB81E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B7E5A578391A4BE3B786CDEBB4E7C262" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B7F6FB8254814AFE85BA148D583519C8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B82AEAEF414049BBA22FCF7C4281EBCF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B83FC3C677774402A75A2F6D613EC218" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B842BDA6FB9C4FF1B282CEF21A64CCC4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B875C187F9CA41C1AB77763828610A7A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B89E145011D34C80A50B7E8FF22318B8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B8A836BEA9F740858A2C1F1FFF645C71" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B8FCCA127BE24285B276CB6B88CA12B6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B9BAC184AB844A96839578235935D13F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BA92F69D198449BEB1A743C8E95A3F14" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BAA758A15315551B88F9AF92A6E67840" "OwnerKey" = "8:_B02CDD54271647E084EBF14CA126EEFB" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BAC6972F9BC24F169F0B59391778F185" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BAF3091B84044FF092312FDC3C441130" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BB25C1A581B2468CA01A37B787637CCD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BB51F76CF0614FFA817E9E28765936F4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BBD31A3743C64DAAAC66C73DBC395184" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BBFE3D0B7BFB49009DA1BFF118B2CF66" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BC104A0952934C1FB24C7C72FB22ECD2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BC5B445C4A87461784901F2D6717A9C2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BD8A0A9482644575AB7586E6526EC50E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BD9E0D72052E4EF7904B45E1247AF92C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BDA2160B8AD742CE91A5BA58C603C55E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BDAB03CEB8A34A48AE15DF20C239DDB6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BE0F9941F59E4C458CB67EC2AE92F547" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BE7A191C730B4BC3AE367292A126B060" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BF735304ABC245BBB4251519F60E5828" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BFEBF932AC564E48B0AF7256A973C278" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C0478D73D2AF4187949270E6FD38389B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C08F3403BDCD48AD83F04C9427530612" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C09716430EA54D868FD696C002FF54ED" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C0CEAECDB8F8480684B11DD07E961667" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C1ED0648FF6146B0A5C52AD0740588B6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C232E233F5334677A44AD9480882E1A4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C25D46C642324693951E9A826BAD34F4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C292DECF838E4A338B1E9381FE88862C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C2BCF04AE0144DB7809933F09C0D799B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C2E91044BE604F148A304860DFB0BAAA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C32B257727534BA496BEB71041ADF2B4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C367477479AC402B95F1AD058F17DF0A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C38EEA378E554DDC9B7516B8DDED70F7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C3C23BE233074FD6B456C2C05C5B4C15" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C45EA6EA27E24554B7E1A208734911EC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C476765DB8F94213A45B04C05F8658CD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C4DE63ED67994259861CC33EC3C8BAB3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C4E281AEA3BE44368B8891E3DD4FB96C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C4FC973F4611411EBBCDD999F5028D43" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C52BC200EEF6472E848B8CA666DC1B2C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C53EED194470430B8048AC19F45B8991" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C556B896643D459C81B361121C73C2C3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C5C315A416D34418A1655DBE35B025B3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C5FB56A197624E3C90766B9BC6F3D773" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C645106B875C414D919C4B6EF4F649BC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C66A118A06614510B5E13491D6C91EC5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C6BDDA3D38EB4DB8BACF5DA31D983E82" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C714818F8D754E9A8A9DC40733B30135" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C71AC36C9E1948349CD116DFBC84AB1E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C769223702A14E0AAB81FBF9D60B6CA7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C76E4317C8FD444E80C157DEC0DAD612" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C771425A4D4143728EDCE8CBCC90C4F3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C78E7F7C98154034B15F02B9BC53FC3F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C809E4861D6D46E78D6A32FE8124E73E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C81BAC2BED284DE786B2D681EB8E3E1F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C839E0301CEB47E1BA350AE6CDE807BB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C8DB858DCBE4427CA19FC95E46D103C6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C950F7538D1A402185228383AEE60C82" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_C98C522FC01B44D5970EBC31B95FCFE4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CA02543373D447F8B3E2C544CA97B122" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CB3347D71ED640DD9C8071E8BE50693B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CB65E5CC799546B7955219DAAE0AA9DC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CB6C6FA3B7D54809BF6148B3741134EF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CB9D6377113B44CF8E2345AC6BF1DE90" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CC048A88EB9D4ABC849A93F48E36C952" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CC991A94AFBB47B08D13C709894DCF90" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CCD22597FC2B42F3AC34751411580764" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CD0D1300C88B487CAD7E04E2A7E34C43" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CE23C05B12F5489D80D49B11021A68CD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CE49530C7C05497EAA89F5CCFE934216" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CF71964739C84F05A16D5207060275EB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CFE643C379A64FC3AE2CD23E2D2F69F5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CFF251B9A0E448EF9DC1F492C1854571" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D00F15F4EEF340309EA04799A0DB388C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D06F117B79164DADA51936E2405D9AE0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D10559A1107F43718503FAED41A57EE6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D11CEFCB96714000B0DA55A5870D6287" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D1FF0706D277464DA9F9FD5BE0519C10" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D216427BA6A345C990A281EC393FDB6C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D2EA924E189C4134B3F6738898223CEF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D2F40D27D4D5482E9EF3866E49DBA52F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D34B18B929744F4ABE62200C7D45CFA6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D36E8BDC47C649FD9617D0A6D7AC6BEE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D37D32CEF4AB40BA8B574271FE31AC5B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D38CF487E50547C4A7BA4E1505094FE4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D3A18E1A4D144D70A6F2241EBFFEC2C2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D45DDB9BC95E46DD99772BBB7E9D9560" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D48F15ED15E74449A5E39ED62B3CDFFB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D4AC3EDE729D4D8C9CE79F3BBF4A0C64" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D4C9319AB3AA4AF3BEE5F92FBD5A4E45" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D5360EBC9ED241A2A644B6CF6529B04B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D558CB15135F433D88F6035244DCEB68" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D58F1D37A97E4C708CAAD0AA4835AD3C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D5A25B4F622C4C4FAC8096B7EEFA5F15" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D5CED9D947C04A19B2F0AE853A4E3A1E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D66C55C5935F4AB79B03B4C00AD0F796" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D6A3CA57C8B54054BC881B683F7DA82B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D70EE948D30C4F86AF2B40C54035E892" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D717157FF96A429EBD907E570F4F2BC0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D773A4CCDBBD4AF5BFE18BC2DC1AC87B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D7F83D314A5247539D7E8246AB6F6A86" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D8763B920D6D46E3AD1B6911EF951B15" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D93F09ED33D649DD84751A2F14898DA7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D94AD6C7B4804825BF99854482A4159F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D970E48380BB47F99441E94A56F62A6F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D97B480C07D844B39CAD227C1EF4B211" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D9DB1DDB4B344A11887DF8AB3C579A37" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DA31A7B03687476B988805E3DFB3C5DC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DA44B31AF21F443D9C263A16FDA69946" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DB23FA2C15D24D49B624E736F061F420" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DB74BC92EE834DC29A1FC5FBF31A484F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DBB821B3793A46BB81E2CF2B9A19B6AB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DBC90AA5A0FD42C8A89D134FBCF0B15F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DBF5793FB8FE4AC0AEB966FDC7DEE88A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DC405B0582F34181944B53E63C15687F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DCC1BBCD7ED54FF8971DC872AE55C7B3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DCDDCA4564C04930948BD2CB44067A3E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DCF636D15E164C79AE8B74AD2919CC37" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DDD56428DD73444F974D61F9825040B5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DE1DF054CBB64310807E5A20C2D2467C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DE58A3BDE0904A95911A189BEFC5B524" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DE66DD7DDF4F4F96ABD6C752FEF96FE2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DE7DEFD90AA142F982CC61E7D208D90C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DF35D4CBA48347EC8527CB78B44D97EA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DF443B32252641F688BF2BB51DA32B91" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_DF977ED1A9D84B27B29191B1E1047B4D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E00F0E6EB6714730B47ADA6C126DD397" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E036E1DCACD844D9B32566942128B2F1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E04B04B7A7A9412B959C5912CF5BD600" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E0626408DAE04212B7A5010F753DBEF3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E0D736AF86EA4D5FBF643F5A8F351B4C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E0F743C6A6394781852F6CE29CDC99BE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E11660ECA02941C091C88F1BEBF4B631" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E12B0D5FE8814778B773D37F3D3E6F7E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E1DDAA09A5FD465AB8577C4655CCB033" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E217CA4F66774BA5A09314963FBD6CCA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E26EBAC5B8A0472E82DC9E74A440688D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E2BB419D3B7F4E39AE594DF418F07C45" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E2CFE015582543699346EC288B70E41D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E31F68CF189145C1B8FFFD1148F95198" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E4C946F4DF9444609A09E13D9072C3C6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E4FAC1F2D0C842C3A5B6560FD284FC1B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E579728367CD44EBA04AAF7008C46384" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E58DF4184FB145939D80A4AAC090F421" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E5BFBEC26C6B41DA9D85C4D678B637BF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E5F10C067FFC4F318F8CB2D219EE87F1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E620874F18094CED984F4503FABAF303" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E622F110BE7948158581ABC21B6520E0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E627F06E09E94890A8657BDA806DC45D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E691274B91684CD2864B82DF108BEBB4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E6AD10132AC04F27ACD79BBC321F30EC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E6CF33E4349C478D8C1B381C31A2E09B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E746B80C52C54F2F9B17C580432EEFBA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E75A3F6B76624E7AB13ECB7AD38250DC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E7864D4A83C04FFE8C5C06B03E11DEDD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E7CFF4924DC546198491DD71E7C5C9A2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E84D0915BD9B403582CBFCFE8B95B646" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E86C074859BF4775A00BD84487CF1275" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E889A14C0D074B379C16DB8CB052A0AF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E89BAA16F29B43F3B85DF8FF73598785" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E8D6C5BCE19D480C8686AA7FF5366EC2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E8E1EB3034AF439B81FC505E88623678" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E969A510B3D54FCB9AC85993634A5792" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E9E737ACC2A846EAB4DA8B8247E4F9B8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E9E893DFCBCE4770B1A667EB553233C3" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EA0871A0A6C94CE8B4E5C8256BD07B56" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EB211D0E657248F2A72935961F75758D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EB78E5C4CE8044F1A14092FAE05D6DA0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EC1ADB1DC1104F78A7F2F5520DB23B01" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EC5DA89F78B84B1D8A90F9ED53AD36D0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EC9766A95CC74083A11EAEBF44A688A1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ECC9F626519A414B921A8E234EF0A441" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ECDBD057B2934DB1BD42A1B33E89C357" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ECDF05EBC846419B808DA15EE0F2DAFC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ED718DE30C0D43B1AB252B3C48074814" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ED891169792044308BA72FF3909914AE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ED8EB9CCE7D64495894EE5DCBE629CE2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EE6428DBC5BC4BE88A90FC65A1745A29" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EE64372F00AC4267BEEAFCC9C6B58E18" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EE72B3867FE6440CA771E3550E38E396" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EEB9D46E7BE943D7AB48509C26A5C0EF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_EF47B6A3727849DAB2FE32F460BE8FF9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F07AC979B87A44A6971AE4CBB5D149D6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F09566EC6FCE4F82A31A9B0E54E7EDB1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F0DA6488EAB24633A1F6D3634BC3769A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F0E01DD09B8D47149A4A175506C86B78" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F1140351BFBC4E629BDAF8CE39EDE591" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F11D733F5F124EA3AB8410692487A78B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F12FA89FB33848218ED0FB80A64790DE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F130429E58C6468287355F1E01AFCC4B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F2202E85444340B1B58243D1CE5AE6DF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F231DE5A2FD2486399D07279FE9E9977" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F31DAAA2A03645A7A6D5A86256991A95" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F35AEED69FF14D85906771EF9BE1E9B0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F3AC087C60844BA4988566DB48C908C6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F3CD1BD309E34B28AEF60F9FAB278B9E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F40CE2B4AF78406B95630523AC31FFD2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F42AAC8BCDDA4AAEB61AC38E9E441CCF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F45743E0EE0C453B977771467658A7A0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F46B417F426B4A7FA4E56EE2126FC51F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F49FFDC689AB470F896FDB210760C4A6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F4A14191893942AAA5EA1A541CEF3E18" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F519563E51B94B8D91BDFE594EF272A6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F588BEAFF08146F780E0D1410A51FA5B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F58EA264ED0A47CAB5F5B47DEB36D2C4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F5E2A267D3AA495B8296333CCE8171B0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F61055B7AFA94AC0A725D6E7AD0725D0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F61682BC67C44421BC5B219801D9BCEB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F64F538AB0E1481BB9AC860C911171A6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F663097F2CCD4CA4B17CC05519AC8C4B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F6BC807749F54132AFC98F6827B8F7BF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F6F178E2FE184A6D8A42C619EA54979F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F75C5CADB5554982A153E0967CD5ECE2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F7619C72DBF2409EB37294746CCCE580" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F7CC215F650944B3A8A5DF21A79CAC48" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F84089B117984EACB3BB401593BD1B6D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F84258958801470AB39943310ED08AA4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F8D528CC3D51483DBDACE3CB9902880F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F92D7AC2DD14413384E88CE4B30F09B2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F95A613A31BA451D99930AB79F14614C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F9E9C90BDD0F45F6BB9E2735A05B5D20" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F9F8DEA95B1448F683EE58097C23CDAD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FA6D869BE6E943D0AA0C1306C8234A82" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FAB73043E4B44747B9DB2A6C7BDE20C7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FACA7D96D56C46339D48D794AD2B266E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FB33BA37B5664323A82F52A0BAB31996" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FB87560927AC4EF9994BF2E6033BFC37" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FB9702887DD5457782E571E44FEE852A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FBD3810511FC4EE8BEEC600F211EC31D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FBDC94140B404C93B5E6991A4013AA96" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FBEB80FA3394476993339C5BB381BD71" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FC637EBDEAA44A81B8A7DC3B86E7B34A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FD47CC396F5A4FCEBC4BC3A74BB463DD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FDB3F3C4DD4B4757B915C4CDCD5CA134" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FDE16188C8F84DD4AA0718B240A13F1B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FE391EA9A2D741FF8537B937A3757277" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FE449B821B53468E9AEBCFAFA2411C17" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FEA32CC3E67F43218123289C8920DD92" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FEF5A316BA1640CDAD18C911C28FD23D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FF0FF8FD90F14B63A9CC7CC4D25EC5BF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FF336FD387BD4B03B2C2120EC5C37AD9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FF97C0E77765407A8E9B2EBDAA0C3378" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FFA020A1D53A4B4E9658D0BDB393F0F0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FFAAEFAB7D684963A4C7532968E26CEE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } } "Configurations" { "Debug" { "DisplayName" = "8:Debug" "IsDebugOnly" = "11:TRUE" "IsReleaseOnly" = "11:FALSE" "OutputFilename" = "8:Simutrans.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" "Compression" = "3:3" "SignOutput" = "11:FALSE" "CertificateFile" = "8:" "PrivateKeyFile" = "8:" "TimeStampServer" = "8:" "InstallerBootstrapper" = "3:2" "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" { "Enabled" = "11:TRUE" "PromptEnabled" = "11:TRUE" "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" "Items" { } } } "Release" { "DisplayName" = "8:Release" "IsDebugOnly" = "11:FALSE" "IsReleaseOnly" = "11:TRUE" "OutputFilename" = "8:Simutrans.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" "Compression" = "3:2" "SignOutput" = "11:FALSE" "CertificateFile" = "8:" "PrivateKeyFile" = "8:" "TimeStampServer" = "8:" "InstallerBootstrapper" = "3:2" "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" { "Enabled" = "11:TRUE" "PromptEnabled" = "11:TRUE" "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" "Items" { "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" { "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" "ProductCode" = "8:.NETFramework,Version=v4.7.2" } } } } } "Deployable" { "CustomAction" { "{4AA51A2D-7D85-4A59-BA75-B0809FC8B380}:_DCC2599046C94669AED6D40C55F3B615" { "Name" = "8:ProgramDataClear.js" "Condition" = "8:" "Object" = "8:_EF47B6A3727849DAB2FE32F460BE8FF9" "FileType" = "3:3" "InstallAction" = "3:4" "Arguments" = "8:" "EntryPoint" = "8:" "Sequence" = "3:1" "Identifier" = "8:_87FDDA06_4822_4A69_A489_437AD55DCF94" "InstallerClass" = "11:FALSE" "CustomActionData" = "8:" } } "DefaultFeature" { "Name" = "8:DefaultFeature" "Title" = "8:" "Description" = "8:" } "ExternalPersistence" { "LaunchCondition" { } } "File" { "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_003EF520411A46E6AE484162BAEDF263" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_005ECAB11B074C5787B1F8129ACD3DB0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_00978D4D6A334BA58E0D8F5EBDCE7F18" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_00B608AD6C5F4140B9EF5D465B3E947B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\linemanagement.txt" "TargetName" = "8:linemanagement.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_010C9071ED524D8A8DC6A171F492F08E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_01D6A70C8D5048A9BF9BED777F584437" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_029552218B4E868F9CDB0806BE6E9ED2" { "SourcePath" = "8:VCRUNTIME140.dll" "TargetName" = "8:VCRUNTIME140.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_02C7691523EE4C7AAF2C42A5F56C522E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_03030B54657549EEB3FBE9CDD8F47144" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_03294D718D6D4CC89C9CC669C9E3AC45" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_03333F46557E4AA09C3D2EDA7DABEAF8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\slopetools.txt" "TargetName" = "8:slopetools.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0387841035034B988BC7744F512624A4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\edittools.txt" "TargetName" = "8:edittools.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_03C3084D08E640D7A56876BE20147A37" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_05C99C5CC13C41A9885ED5CAAC4E241C" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\09-Simupolitan-Swing.mid" "TargetName" = "8:09-Simupolitan-Swing.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_060F8BEC80784113B5C4D0565C7B9B1F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_06B81764E3B7488D838810FAE42C1829" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\high-contrast-large.tab" "TargetName" = "8:high-contrast-large.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0715F183A84541F5BF5D7BA3BE88E0A3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en.tab" "TargetName" = "8:en.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_07484E10F2A34831B3E6D9052C6B64CE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_079B392C00696F9EFD81A8F4C6CD66E5" { "SourcePath" = "8:api-ms-win-crt-filesystem-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-filesystem-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_07DE11554B2143DB96B5DA78A0720AB9" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\46-House-in-the-station.mid" "TargetName" = "8:46-House-in-the-station.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0819F586738B42CAB30947A5AD8337AA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\edittools.txt" "TargetName" = "8:edittools.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_081BCF65CEA34B1093E83B88818BB39A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\settings.txt" "TargetName" = "8:settings.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_09BA1EC65CC548038440E1CCEF260625" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\removal_tool.txt" "TargetName" = "8:removal_tool.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_09FE9501B9E54975B984B8C110C56D54" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\15-The-Wayside-Blues.mid" "TargetName" = "8:15-The-Wayside-Blues.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0ACF34CBE28DD32DC79D67B59FA64626" { "SourcePath" = "8:api-ms-win-crt-math-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-math-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0AD370E81C404873BBA9F6C080DE9986" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\10-Easy-driving.mid" "TargetName" = "8:10-Easy-driving.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0B4C01938497473C97E1330B603EDC0F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\linemanagement.txt" "TargetName" = "8:linemanagement.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0B96ED126C9941A0B5862E17EBEAECB3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv.tab" "TargetName" = "8:sv.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0C86FF1A217F45FE9BD380453833B87C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\settings.txt" "TargetName" = "8:settings.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0CC8C3B5975849E7A0674BC5B24FD640" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0D3020DE4E8ED5CC4AC9098B65B48DE7" { "SourcePath" = "8:IMM32.dll" "TargetName" = "8:IMM32.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0DF1600FFBCC494BB4B3A3B41FBD4C45" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\slopetools.txt" "TargetName" = "8:slopetools.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0E07641E8CC4496389374E089BCA52E4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0E90A6C737F1457096CEA2E4EBF7F70D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0E9BE20E59D54B6EA446FB9399D2BB1F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0ED6E2E3EA9748478583F98377A57988" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\factory_build.txt" "TargetName" = "8:factory_build.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0F28D5341A894A28A785BEF0499CC2B6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0F9A37FCBB244ABC8A8149D9DF2B8563" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\linedetails.txt" "TargetName" = "8:linedetails.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0FCA3DB968B942E4BF29F988842AC875" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_101DBD56305847DA96CE37AF80AC07A7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn.tab" "TargetName" = "8:cn.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1105F13F49EB413D8862437DEC6BFD64" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\17-The-Benevolent-Dictators-March.mid" "TargetName" = "8:17-The-Benevolent-Dictators-March.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_11193214718A449C82F62145CD8B2C02" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_11DBDBB9ACE64BEEB7549FDAE6CF5500" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1237F66E84B34262BFEDBDB63174DC1E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1237FFECE674421784292D366BC64FA0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1247C8B3EBBD4CF5B1EB76A31014553D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_128F2CA87D9A4682AFD96DF5D4A04141" { "SourcePath" = "8:..\\..\\..\\simutrans\\readme.txt" "TargetName" = "8:readme.txt" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_12AB4359C4B54FCBAC46DA8D834EB09C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\curiositylist_filter.txt" "TargetName" = "8:curiositylist_filter.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_12F07DD4BB194D0891E2C35D9E2AAE00" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1339688690AA43C3A985A46189829DFE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_133D00B493E44429BEA58682323425F9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_13506C78AA494DA6AD4BD5AB7BB66DFD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_13637737D70917BC3CEE9C780E1C321C" { "SourcePath" = "8:api-ms-win-crt-stdio-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-stdio-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_139DA0EE1A894318BFBD62365131B07D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_13CA5808023C4A98B16E6F84BA0007BA" { "SourcePath" = "8:..\\..\\..\\simutrans\\problem_report.txt" "TargetName" = "8:problem_report.txt" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_13EF44563D4C4ADEBB10945AE080BF57" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\goods_filter.txt" "TargetName" = "8:goods_filter.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1440778F3A70472595A747394B138D8C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_159A716BFFBE4242A25D18DA7DF569BE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_15AD3E75D8494309B7E9D38B67792845" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_166AAB6C447F4F14987682E2AE9E49C1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_A9D6B01652F44EEEBB95F40DFA558141" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1674AA7C5F1C48E5980265AC6A9165B6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_16B9E22B4A6A439993F94972FB73C067" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\mouse.txt" "TargetName" = "8:mouse.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_17B8C2EA02764F9CB743E2C2035BF903" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\flat.pak" "TargetName" = "8:flat.pak" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_181A6C90E6B7458EAB2292B8BE635C8F" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\prototyper.nut" "TargetName" = "8:prototyper.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_186AA79D5784439EAD1C5E15124C7F7A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\monorailtools.txt" "TargetName" = "8:monorailtools.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_18A83AC11E07457D9E0B0E13BFCC3E79" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\server.txt" "TargetName" = "8:server.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_18BABDE15592474D9AF3C78276D8449D" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\combined_connections.nut" "TargetName" = "8:combined_connections.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_18BADB004C1441909DA8DE66D75CDEBC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_18C933E7D69946F5B947EC8273AEB559" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\citygrowth.txt" "TargetName" = "8:citygrowth.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1918ADD2CE144D709D7289D49A2CAA38" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1965A5D324AA4723A6D357265F9AFFC6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_19A98DC8BB0F44B5B90253ABAAFA14C4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_19BD0EDF53564D459C8A659AD26B22E2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\vehiclelist.txt" "TargetName" = "8:vehiclelist.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1A3B66C3F3284C54AFDC39383717874E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1A52BAD08FFA4CF7A52704515E265E0A" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\41-Libertador.mid" "TargetName" = "8:41-Libertador.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1AAFF255E23343BBB16D3EBC42EFB21C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\privatesign_info.txt" "TargetName" = "8:privatesign_info.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1ABB22A9F34A4486B4D0F7D6A8ED75C9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1B742E6F5F794339B7B00E5090AE41EE" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\27-March-Winds.mid" "TargetName" = "8:27-March-Winds.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1BC8A4B2533D4A9B81A8D906E75CC892" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1D713659992B4EEB89199A782FF15A97" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\password.txt" "TargetName" = "8:password.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1DC828055A404A2C90A33E0062965415" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\industry_manager.nut" "TargetName" = "8:industry_manager.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1DF38A11E5C74DB9A741FC9C10A3634A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1E30A8FD30C344B29F0C9B8BADAF36B4" { "SourcePath" = "8:..\\..\\..\\simutrans\\script\\new_scenario_template.nut" "TargetName" = "8:new_scenario_template.nut" "Tag" = "8:" "Folder" = "8:_589146AE31A1480C89E8F37240599615" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1E43FD6B874142DCB7A19BFB907DC4E6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1ECF0A79127649D4A462C15FE8EBDD52" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\industry_connection_planner.nut" "TargetName" = "8:industry_connection_planner.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1F78A7C6DB6849A1A3352CC3B935FBBA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1FF22B3CBBD34BDDA475ABCE7E8458FB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\signal_spacing.txt" "TargetName" = "8:signal_spacing.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_20AF94701C3B4F31BE18E4C18593C65F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_216D641690CC4689BADF94365FA2B97A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_219E1B7730EC46EAA280143D520AE19C" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\28-Road-to-Warm-Places.mid" "TargetName" = "8:28-Road-to-Warm-Places.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_21AA11BC689E42B6B6697A5FC318E096" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\et.tab" "TargetName" = "8:et.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_21AED397D928464F813731B7A635A8E8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\removal_tool.txt" "TargetName" = "8:removal_tool.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_21C491A79E964383BBEBAFB16263BCC0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_22571262D0E1464AB1C4D661D729A7B2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_226BCA5FDA2B4579857D5F4C3AF3A482" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt.tab" "TargetName" = "8:pt.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_230AC91992494E47953EE25EC0732E52" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\signal_spacing.txt" "TargetName" = "8:signal_spacing.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2310BE29D51D4698A0E4D57439C7CF4D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\inspection_tool.txt" "TargetName" = "8:inspection_tool.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_24C7BC64F7BD4CE4B1EC004A5BF37584" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_24D43BA152AF47719E58C99043546C39" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_25565CC3AFA2488FA09185A4D3536AF2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_257608DA64F7495383410A50CD623626" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2580742A58474754BB9F6E8DAF113625" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\industry_connection_planner.nut" "TargetName" = "8:industry_connection_planner.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_25E373020643480BA2C6C4FE1967DF39" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\special.txt" "TargetName" = "8:special.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2688604BC1004159AC65DA8026AAF404" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2692FE4659B3459FACB55B2ABABCEF3B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_26F725E6689A4B47B5225B9F9A6C4CB2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_280DE28E23F3432A8CDB55AB067712D2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2820718EDD1A4C0A998A555B73E7941F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_289828740CCF4491AEAE9923ABC9E630" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_28D87BD82DD242048CE3AAB274E4F224" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\station_manager.nut" "TargetName" = "8:station_manager.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_28E16D55A93A44CAA25DB060E24E199F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\password.txt" "TargetName" = "8:password.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_290A6460DE03485A9ECA056C13AB547A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_29ACEC7A06E24A1DAC101B2DBFFD8054" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_29CD1E15907F4CE2B442E61E72296DE1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\signals.txt" "TargetName" = "8:signals.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_29E0B719E1D143A3B435F746D294CA48" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_29E3ABF203C04D2E8FF9FB6AE98967C1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_29E5E12EC22646BAAEDE11AE02DC2C61" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\industry_info.txt" "TargetName" = "8:industry_info.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2A7AE2771A914BA7BBDEA452C9DCF2F3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\signal_spacing.txt" "TargetName" = "8:signal_spacing.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2B5B075BE23041988B08F61EF1DE74AA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2B6156C249D14E63834D6BB3CAB3FF16" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu.tab" "TargetName" = "8:hu.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2BB109D12B614A65BEE199658898234C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\monorailtools.txt" "TargetName" = "8:monorailtools.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2D6E5C7AE3F64AC7ACE938CB8ACD252D" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\52-Dreamy-Oriental-Nights.mid" "TargetName" = "8:52-Dreamy-Oriental-Nights.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2D94FF39A27543EB95167C67429CF990" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2DAFA0293AE04710AD9F7E863F3BDCD4" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\38-positive-thrill.mid" "TargetName" = "8:38-positive-thrill.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2E231200DCCF424DBB14BCE8FE6D93BA" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\43-Driving-on-the-midnight-highway.mid" "TargetName" = "8:43-Driving-on-the-midnight-highway.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2E8428F464504F4CB245EFD3D16D3342" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\privatesign_info.txt" "TargetName" = "8:privatesign_info.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2F1E496588C3496D8E4F6B8FEC90DF9A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2F4938DE7AA34B87B0D997F896311B0D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2F8D3E0ECE694AEDA82C5432F24C854E" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\music.tab" "TargetName" = "8:music.tab" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2FB0C916FA6E4D568517572F31975178" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\slopetools.txt" "TargetName" = "8:slopetools.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_301A99763E6F471B96D9FCB200CE960B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\lt.tab" "TargetName" = "8:lt.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3048CD4420B04A098D932C7011C16F75" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3067D12B03BD409DBED5BBA78761E959" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\11-Stucked-Convoi.mid" "TargetName" = "8:11-Stucked-Convoi.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3075C6BC47214728B90B6AFC317EED62" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_311BBAC6B2C346929B78EDDCEAA25F52" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_314549B8854B4E04A7B69C5A1151519B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3156DFEDDFFA4625949A11A1B0F470A6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3179D684E9B144B78C3AF7E94102521C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_318FE10CEE38456B8FC8241021019358" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\road_connector.nut" "TargetName" = "8:road_connector.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3194957A2F144B9DBB90D04D1202A0D5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\monorailtools.txt" "TargetName" = "8:monorailtools.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_31BAA09A108D4F8D9457940B5856273C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\underground.txt" "TargetName" = "8:underground.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_31D813A3C334427AA33385173447DCE6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro.tab" "TargetName" = "8:ro.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_31DEDBED22EE4A4DA099BD028FB246A3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_31F6E25224B744DCBFC0141E79D10A60" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_323064989CF245F393B3FFED1FE07884" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\slopetools.txt" "TargetName" = "8:slopetools.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3233EF6351004F93B9715A0A7AE4FA74" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_327F373C1E7413799B0AED3AF56713C3" { "SourcePath" = "8:api-ms-win-crt-string-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-string-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_33296DAD659B4BD39798C705F02C7834" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\44-Above-the-sky.mid" "TargetName" = "8:44-Above-the-sky.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_33BD05ADB1C64EC8966999E905C33C9F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\special.txt" "TargetName" = "8:special.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3487ABD4F8B94084913A128F4AB191A1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_34A049B6090F498E8A74F7782E6AC542" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\underground.txt" "TargetName" = "8:underground.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_34BB38CFC48E42209DA5AF5500F6D2BA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_34DE603A33A04466AC350B9AC8D6A0A9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_34FE77A5FD59404B8CDD5D21FB0AC148" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\load_relief.txt" "TargetName" = "8:load_relief.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3519CCFB2AEC4E349CCCFD738C500238" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it.tab" "TargetName" = "8:it.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_352D3120C5B04626B12EE20AC398F144" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh.tab" "TargetName" = "8:zh.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3530E1B1A1844BDB81256561E663927F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_354005FEB66444AB84E610AE09EF0FE8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_364C50343A8945B59522EB0EA95F7370" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_36993815822640F6AFC910D125044DD4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3718F392451E49C8A03D48167AFFC98B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3721799D17B548DD91B4A37F71B2EA78" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh\\enlarge_map.txt" "TargetName" = "8:enlarge_map.txt" "Tag" = "8:" "Folder" = "8:_A9D6B01652F44EEEBB95F40DFA558141" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_37605F6B6AC74871B93AF782EC31EA68" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_A9D6B01652F44EEEBB95F40DFA558141" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_37666D0AB6CD430A955269F7E1CB6676" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de.tab" "TargetName" = "8:de.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_37D5EE6E06794EFDBED006661DB3CE03" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_37F86EBD13864C7986A4D05594E9C7E2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_38252595EA444B1DA8102684564FF3EE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_382A9A025A4C40BFBA389633BCC7465F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_38690161FBE14CB8B378D3A573CCF35F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_38935C206D864F57B6900428D9DB774F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_39EE9887C474434CB0593872C88614C0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\signals.txt" "TargetName" = "8:signals.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3A3A98EBEADD4475AE7E4F2154758DFA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ru\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_6AA7E18DFDC74EFF82A4CC967B14F3F2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3A45C9EEAB5F4D41A9EF96EB0F70FE98" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3A4F1A9658FA4B0E805DFEAFA9B5C2EB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3B4AB35D4B814E49B374573FD9B41D9E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3BB85C849F4A45ED984521EF7EC00FF1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3BF8D681CD3B4B87B032DCF3C00FF926" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\underground.txt" "TargetName" = "8:underground.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3C1CB9531BCD4293980FEBED7E010E28" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3C5211F08FDE4E4490BD5AF66C9A0EEF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\monorailtools.txt" "TargetName" = "8:monorailtools.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3CA70B6D5F48488CA38489C0EC76229E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ru\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_6AA7E18DFDC74EFF82A4CC967B14F3F2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3CACB35ECE044CABAF73B3AF522C618B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\special.txt" "TargetName" = "8:special.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3D42F41E95F74301B3DA30722D7F4D74" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3D6D0B1545684FB685D35EE557CBB335" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3D996E275F02498B99D4B122817672CD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\uk\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_789838269269425EB5ACB92A794015C0" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3DCFF913F03C4FD48D362C46015CBA63" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3E2C5CEFA60D4DF59BC3A743C2073EE6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3E51AC108118479CA3F94EE04D6F01C0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3E5E32603E444A4BB9392F2DBAA25BF9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3EE1A722BEDA4ED7A2ACC4BEC7F4C4B8" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\05-Boring-afternoon.mid" "TargetName" = "8:05-Boring-afternoon.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3EF0959343750F1BB1E3683C3BB95052" { "SourcePath" = "8:IPHLPAPI.DLL" "TargetName" = "8:IPHLPAPI.DLL" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3F165DBD8D454BECB94AB3737DBE82C7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3F16D51E7A964A99A35D575CB10B1C2B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3F1AC2D8D7DD4F49B318E45AD0000E4E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3F3BE766613F4BF29D52703C4C0D0427" { "SourcePath" = "8:..\\..\\..\\simutrans\\script\\ai_base.nut" "TargetName" = "8:ai_base.nut" "Tag" = "8:" "Folder" = "8:_589146AE31A1480C89E8F37240599615" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3FD7E53C07E241C3BCD00B81C59CC66E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4057B27E9E494E8CBB7D1FC8FA3EACA2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr.tab" "TargetName" = "8:hr.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_407A3BB3FFB14445B01632174D68B032" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_40A0BE0449894903AA520D9BB5D5EA6F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\signal_spacing.txt" "TargetName" = "8:signal_spacing.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_40BBCF7041234B0E91AAC36E71B548FF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\slopetools.txt" "TargetName" = "8:slopetools.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_40F016A1CD6F4489B117B3264DD98F90" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4147D6D1F8A245C4AAC7DA2E72741900" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_41600A65D8804208854E48546CE03C31" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_41DFF372121244008F06B6CFE66D65DC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_422F124DFC564135884EE030F8A75A7E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_A9D6B01652F44EEEBB95F40DFA558141" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4263D1383F6141CC8FB85D9C89A37DB8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_433B0BB603EE49AFBE61E4A95B62F025" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_43B4F064B408462B9CFF09DAF89256DA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_43E4EBE3CF4C483F9B9C3B22FBF18EE7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_448B7A877DFE4EE7BCCC10E684A9F9B6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\settings.txt" "TargetName" = "8:settings.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_45907458AB0F4F9BB5167B3C22912220" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\13-Stephenson-blues.mid" "TargetName" = "8:13-Stephenson-blues.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_45C20A6E3143435AA6855758F2D9E540" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_45F7C42A57FE478FBEC1428DE3C17C67" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\high-contrast.tab" "TargetName" = "8:high-contrast.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_46264B5F868B4521855C85BD897A8A84" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\monorailtools.txt" "TargetName" = "8:monorailtools.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_46456885516B4B789AE75E836C9C05E8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_465C2BCA0E3B44E98A0C2EA624307C51" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\astar.nut" "TargetName" = "8:astar.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_467B5D314942411D8AE3142D49E698C0" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\placefinder.nut" "TargetName" = "8:placefinder.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_467BE99BEC444EB28162927D6652266A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\goods_filter.txt" "TargetName" = "8:goods_filter.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_46B75EC9AE974FA390E7CAC4D35F7919" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_46F68A58A67F2A54B4325EB94650EE7D" { "SourcePath" = "8:api-ms-win-crt-locale-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-locale-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_47293CF4BAF54E8CA44A9AB83AB3CB19" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\be.tab" "TargetName" = "8:be.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_47CFC2CBCF804F13AD453C25372C8730" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\translate_users.txt" "TargetName" = "8:translate_users.txt" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_482768525BE34C6BBB46DB7ADBB779B0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\enlarge_map.txt" "TargetName" = "8:enlarge_map.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_487490D85D3845FDA7F0C9B3FAB37393" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\tramtools.txt" "TargetName" = "8:tramtools.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_490DF7497FD3469186609337C624758B" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\20-Last-Trip.mid" "TargetName" = "8:20-Last-Trip.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_49B9EB5E83934E4B8E4B0B9339FB5FAD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_49CD0D1D118547D3BE550596B2889432" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\monorailtools.txt" "TargetName" = "8:monorailtools.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_49E3C49389E64767AEF91244E450BCF5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4A24E208D93D4E83B56C55A98B427CA4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4A85838D1D85427897D4F69693279715" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4A8CB7B7A27B416E8D5F16F16414DAB5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4AD08276500247AB8FAC3EF4B77F267D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\industry_info.txt" "TargetName" = "8:industry_info.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4AD33819C37B42B1B0B339CE718E5CDE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4AF573D713344BED9E783ECD4C1284E9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\bridges.txt" "TargetName" = "8:bridges.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4B6BF260CCD642F6AC9015FD883DC989" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\factorylist_filter.txt" "TargetName" = "8:factorylist_filter.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4BB786F99376461988413CF1A585AED9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4BDEF13001284819BC75A57512F7E7CE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4BED95EEB2484DBD9257998035AE6209" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4CC13851778543C889504D03B5927C2E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4D3B4F373E4548D6882E71F29ADD2774" { "SourcePath" = "8:..\\..\\..\\simutrans\\change_request.txt" "TargetName" = "8:change_request.txt" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4D4A63AD27BF461C917A7C7A92F3A084" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4D611D172DB94EF8B7C138B581301576" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\settings.txt" "TargetName" = "8:settings.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4DB844664FE94090BEFB466C6763B36D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\linemanagement.txt" "TargetName" = "8:linemanagement.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4F58BA38FA2541B58E8C52828A5272CA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4F774CD205E24CF4BE96227A527C8539" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\id.tab" "TargetName" = "8:id.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_502FAC792C1D8614FE7926131D186D21" { "SourcePath" = "8:api-ms-win-crt-environment-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-environment-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5078896E10A44B0E8322B5B2DD9ABDB2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_50DE5B28CD594DE7B5F13CFEE25C43E8" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\ship_connector.nut" "TargetName" = "8:ship_connector.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_50E96DDE2E454243BFC25EE2D771B7F7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_512BCD1EA02040ED9BEEC446FC50AF1C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5140C10690D54348A4368EE02676B691" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_519DFBFE15F34747A61A95E2B9B10BA8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\enlarge_map.txt" "TargetName" = "8:enlarge_map.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_51B4379EA7764F718E23148EC7F180AB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\enlarge_map.txt" "TargetName" = "8:enlarge_map.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_51EDC323F02149719E5DF86E69F1BE72" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_51EFF26BEAF246629A4975AEA01A1470" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_522CEDAB5F5347408C153DCEC08D1B91" { "SourcePath" = "8:..\\..\\..\\simutrans\\font\\prop-latin2.fnt" "TargetName" = "8:prop-latin2.fnt" "Tag" = "8:" "Folder" = "8:_8BAAB00D55944276B3A666BC02AD26FA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5241ACC17B5C411280903A620FE31CDE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5264B3188A1F41E895A034ACBCE23609" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\08-The-journey-home.mid" "TargetName" = "8:08-The-journey-home.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_529822798EDE4A6EB66A8FBC79D28466" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl.tab" "TargetName" = "8:nl.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_529ED68FA8504A2596F83E3452215DF3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_52EB158E737145259F9D9B99E3A0DC77" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5358F97ED18A0C27CFFFA550621C549F" { "SourcePath" = "8:SETUPAPI.dll" "TargetName" = "8:SETUPAPI.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_539D40508C6E4E75A11EFBBB82E0CF8C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_54BDE901C9354D4294895144769BACD8" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\16-Midnight-Express2.mid" "TargetName" = "8:16-Midnight-Express2.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_54C84BAA19B04F939D0E0367B7F17DB5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko.tab" "TargetName" = "8:ko.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_55455C91BDFA4715B97E7CE7754928E9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_55A356B6F2BD48BAB0EE1DC4089CCC6A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_55B15CA598B04AC8AA57ABA39B5737D6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\industry_info.txt" "TargetName" = "8:industry_info.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_55F557FF2D5F4FF8B4B841480F8E02EC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\signal_spacing.txt" "TargetName" = "8:signal_spacing.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_561E7344F2D3457184C0231FFA8E0D63" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk.tab" "TargetName" = "8:dk.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5649E800E882443DBFA060EF7C6D0C54" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5713DA04E2164ED6A58DF2872113A1E1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5722A6A9D28D4A048CA3FF4C5C399AA9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_575341B1DC7743458EEA436605782806" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_57969C68E2AC43A8A53EA72F3B6933F2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\load_relief.txt" "TargetName" = "8:load_relief.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_58059A5FD7D842D4A019A4BDB501153B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_583AF0963D3F49819D420B80764536CA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_584B82F1837D4263B9287A526B252157" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_585E548884E1A0024245C4DBE90F6893" { "SourcePath" = "8:MSVCP140.dll" "TargetName" = "8:MSVCP140.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_58ADEBE3020643D893F8EAF29C412F0E" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\29-Runaway.mid" "TargetName" = "8:29-Runaway.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_59052B74FB614B939DD6A4414515B755" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\ai.nut" "TargetName" = "8:ai.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5977CE44BF2F4CE3B66EBC2E9BF91AA5" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\32-incidental-skies.mid" "TargetName" = "8:32-incidental-skies.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_59E2DDBD34F64EFCA5911828555E70E6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_59F7612898734E4DBD36DF9E76F2C256" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5A61100119004DE0A914F075B53697B3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5A8A8FF3CC8D4B3595A9DAE1150F703D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5ADFCBEBD3DA466287FAE7D8B02A2907" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\linemanagement.txt" "TargetName" = "8:linemanagement.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5AFC05EFA72649698E65D01DC3A85278" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\readme" "TargetName" = "8:readme" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5B4BF2BFF84142708C9EA8FC67DB3216" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5B73D425BB464648BD3F6C58AE543014" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5C6AC9E04FE740A4B6E9ECBA0CB93F7F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\goods_filter.txt" "TargetName" = "8:goods_filter.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5C6F45BB13064E52829F43B6FFE00220" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\24-needlessly-striking.mid" "TargetName" = "8:24-needlessly-striking.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5C9630709CED41D2929492FD463827D1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5CD15C85FBF94FBB9AD95AA7490A45F0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh\\settings.txt" "TargetName" = "8:settings.txt" "Tag" = "8:" "Folder" = "8:_A9D6B01652F44EEEBB95F40DFA558141" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5D9D568EFEE54B5A8A2A88644C400E6E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5D9F5328272845C3B2E2857D1BF93E74" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\curiositylist_filter.txt" "TargetName" = "8:curiositylist_filter.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5DB6AF2EEFA24BABAB9B45BCEE62029D" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\36-faded-things.mid" "TargetName" = "8:36-faded-things.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5E76DC3AD0814F04A545A862CD5E7DCF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\tramtools.txt" "TargetName" = "8:tramtools.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5E82840C0671457C8E5FECF7032D190B" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\40-alternative.mid" "TargetName" = "8:40-alternative.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5ECEAB174780441CB6657A851C694B5A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5FEE0DDA9A2847CB9FBD5EA59EECF525" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\placefinder.nut" "TargetName" = "8:placefinder.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_604DDCD3A6294FEC9A6F72485DC08DAE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\curiositylist_filter.txt" "TargetName" = "8:curiositylist_filter.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_61DEAD687F5A42A9A3CE05CF43194861" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6216B709025B45618D3CE7D60D744328" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_623197F7D11B48FEB04D649E84458841" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_62697F60BD184B3F85EF352882FF0FC6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_630392339F2C48718E8D505AE3524178" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\enlarge_map.txt" "TargetName" = "8:enlarge_map.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_63090592FEF84404865D0F30FF65D3D0" { "SourcePath" = "8:..\\..\\..\\simutrans\\history.txt" "TargetName" = "8:history.txt" "Tag" = "8:" "Folder" = "8:_A5BC29A726E64EB18DA057FAEE2DD996" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_63254E53870241DEBABF85CC64023D1B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6335879001C8412CA64A127760C570A0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\load_relief.txt" "TargetName" = "8:load_relief.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6336B3E3BB544A79B2748CFB446ECFB9" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\12-Steamin-across-the-prairies.mid" "TargetName" = "8:12-Steamin-across-the-prairies.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_639BCC27C4264E149D85E7E705C52710" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\03-Sunday-drivers.mid" "TargetName" = "8:03-Sunday-drivers.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_63A60E70A6274498A49DD55849187C7F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_63AB0309FFF146E5B9A8BA431BEDB581" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\curiositylist_filter.txt" "TargetName" = "8:curiositylist_filter.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_641F8E28A18740EE9672DF14F4322517" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\basic.nut" "TargetName" = "8:basic.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_644E9138F1B04425AF2CA49C8C1029A9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\tr\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_DD9701F195AA4B3D96975B01836C5F7F" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_647D7C02726B43339F93308CE7419FC1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_65634BC992FA494598EA6E6C025512A5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_65CB95D1DE44480EA45158A755AE64BD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_65F46B0D45B646C4BDBB84B11FC74881" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_66A93844669E4AB8AFD7960F553D67E3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6702CB4A485E4C3C9B1970AC9D77FFD0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_67073B82D78742E5B9F3A6D364AF649D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\uk.tab" "TargetName" = "8:uk.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6723BFD2968F488A8B21778707DD42A0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6734839905EF4389AC62192A95B835D0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_675B1F5C00FF4877883A37F4EBE1E3F5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_677C07C6147A43449F9AF59CAA3DE045" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_678A6CC7CDBE4341A42A0D8FA05D8A80" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_67C77B7A81734DCD93763CAFDB731F3D" { "SourcePath" = "8:..\\..\\..\\simutrans\\font\\Prop-Latin1.bdf" "TargetName" = "8:Prop-Latin1.bdf" "Tag" = "8:" "Folder" = "8:_8BAAB00D55944276B3A666BC02AD26FA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_692C781C1C4944E3918865C3016F9231" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_695F9E0D19A94E9080FCF6161EBF07D9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_698D6BCBB25E43D6B7AA5EAA8898E4C0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\mainmenu.txt" "TargetName" = "8:mainmenu.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_69A7859F453A4A91B8E1B6F8EE0E3623" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6A45AA5600B54C83989EF933E7D81F00" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6A709C49B7F943C691E029483C259726" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6A7B7E930C944000864ED2DC074B8944" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\linemanagement.txt" "TargetName" = "8:linemanagement.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6A8F2A7B2D0E46B7BD05ED75ABEC07C0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6AC4A1E5DC494E24B5BCEF7E92D36816" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6AE881EFF6E147EF97DE619B22172E27" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6B365598BC624617B2B10FB965E239DE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6B5218A02D434B9BA80C49C643184341" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6B7612CAB6554AF286B04C35DA865321" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\server.txt" "TargetName" = "8:server.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6C3CF89286EC42DAB9EF964252360D33" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6C7F90DDF17D4C6293C647A90E00E279" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\combined_connections.nut" "TargetName" = "8:combined_connections.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6C945BE71CFF4E82B4F0DFB72C292BFA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\no.tab" "TargetName" = "8:no.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6D01F4293A064FF2A6A6CC0F0398D328" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6D5F0504040C42649AAF5C80A6C93AF3" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\48-Techno-movement.mid" "TargetName" = "8:48-Techno-movement.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6D66B32FEBB94455B6BFEE30846CF33A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6D7ECB4FFD0B48E7A13074F90688F225" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\ai.nut" "TargetName" = "8:ai.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6DCA48436F4847F5B3F327EE350E3AB1" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\23-Something-for-Silver-Sand.mid" "TargetName" = "8:23-Something-for-Silver-Sand.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6DCDE0BC21E54EEFBF9CFE05BEACF621" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6E5DAC8084104079B655A2FCAFAB3CC2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6EA80C01AAEF49228558822219AF7ECD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk.tab" "TargetName" = "8:sk.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6EAD4002E15B498AA04B7DFB144B3917" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\31-Courtenay-Bridge.mid" "TargetName" = "8:31-Courtenay-Bridge.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6EC63EB0D6364EC391551F261C4E34AF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ru\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_6AA7E18DFDC74EFF82A4CC967B14F3F2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6EEAFD65E9A14B65A1FD6C694AAEF48D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6F75DDB2EDC24C53BFFB2A4CBDCE7D32" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\inspection_tool.txt" "TargetName" = "8:inspection_tool.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_707C1BE3EF9242BE9746771AB5B6E60A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_71225218E6B24F409FAF954309A5F4FE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_71C350E8A9B4499694F7F9CCF037A2F7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7243A246D6454771A7F203730142ECF8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\password.txt" "TargetName" = "8:password.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_72ADF54DF3BB4CB495E986114E1E2DF4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_72B1848015A8405BB280F12F7FE44143" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\server.txt" "TargetName" = "8:server.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_74126279572F497EAE3334BC10BD9420" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_74CF407282A64494B3743E844FA0B38A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\edittools.txt" "TargetName" = "8:edittools.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_74ED0456E40947CA92C62824E4125894" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\26-Tantalizingly-Unusual.mid" "TargetName" = "8:26-Tantalizingly-Unusual.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_751BF54EFC91407790B42AD4A75C158E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_75982E571A6F4F2A8349D07115881603" { "SourcePath" = "8:..\\..\\..\\simutrans\\script\\tool_base.nut" "TargetName" = "8:tool_base.nut" "Tag" = "8:" "Folder" = "8:_589146AE31A1480C89E8F37240599615" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_75DB0F22A99B487FBCBF4492787CF16F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_760A7DFAB6A846738F456A2D9B75DF42" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_761869FA0B4342EE977F57311DB8CBCB" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\50-Snowy-Road.mid" "TargetName" = "8:50-Snowy-Road.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7627C3286ECB4901B7FF66783A56EAA0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\factorylist_filter.txt" "TargetName" = "8:factorylist_filter.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_76DB0BE795FC4B5BB8D0D0A037368A67" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_76E349042CB043F2809E4858A2D9A63C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_776CD7D0F5E84BBAB5A8563DE24D69BB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_777C23F3C78F43E196CF3E5471716A92" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_77AA9368DC924035A400D7A741A3B134" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ru.tab" "TargetName" = "8:ru.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_782A3C3B9FD34371BE3AEA0EEBFECC02" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7879F46087224474AEC649E7FB71EAEE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_78FC1DE773244F7AB6CEEE32A6997506" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_791BD97D0CD441D4AB0F2291EA64362D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7A4B68C765954A77AEBA8D340DE2310D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\enlarge_map.txt" "TargetName" = "8:enlarge_map.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7A873BCA0E874A5C9AA964C4EED19DBC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7AD1BF4E17F04C1485967C5A664A4E0F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\linemanagement.txt" "TargetName" = "8:linemanagement.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7B480D9F16C7409B8547DF976679CE18" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7BC9AACD90A04AD8BA6AECA487E9DCFB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7C175A1D24E94CD485688A2AE69E37F7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\curiosity_build.txt" "TargetName" = "8:curiosity_build.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7C4ED6D1255C4087B67B059046DE362C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7D2FFB8DE5D246819C5152D280523C44" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\underground.txt" "TargetName" = "8:underground.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7DE009E5FC2F487FA3A2EB121E49D0F9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7E1AB3235A184CDEA7ED5DAEF4C8CF96" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7E2C2656F54F4DBCAE3C5FD9AB651368" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\edittools.txt" "TargetName" = "8:edittools.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7E5C15BD3D26417698877173773F3DBC" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\51-Summer-Intersection.mid" "TargetName" = "8:51-Summer-Intersection.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7F01371C7C884AA79D7B332DFE9CFB87" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7F01AB5E57414138829BC412322FD334" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\inspection_tool.txt" "TargetName" = "8:inspection_tool.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7F1DC34C3220499EA78996207F8A9F18" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7F275CAC05284C2BAF4B234E294A98AE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7F473696C8894F5C827D4B1AF4EC61DA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7F5E16592EE74DB2AD2B8D96120CA325" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7F86529E598F451A91EDB4A9DF245431" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\curiositylist_filter.txt" "TargetName" = "8:curiositylist_filter.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7FB25388690E4F35810256E3806D7AC7" { "SourcePath" = "8:..\\..\\..\\simutrans\\font\\m+10r.bdf" "TargetName" = "8:m+10r.bdf" "Tag" = "8:" "Folder" = "8:_8BAAB00D55944276B3A666BC02AD26FA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_802286B2E6AD4D5EA5F7582550D0192B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_80718F22412647C49DC0084C574E72BB" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\themes.tab" "TargetName" = "8:themes.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8079B5223A464AD483F0B286A55B7CA1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_808B71735EB742128850F708235AEBB0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\factorylist_filter.txt" "TargetName" = "8:factorylist_filter.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_80B5D3C38CAC465BA8C2550F7914F614" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_80E9BB86DA4D40C29497E5182B73AE5C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_81301366B3184A6FB5E40F909CF6AD26" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_813B3C3F7F724EB4BA5EAE500DBD41C9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8183844A6C6347D9BE1A8F17A5EDC446" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_81A24FF94CDC4E40852AA1F5B3802088" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\monorailtools.txt" "TargetName" = "8:monorailtools.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_822E7AE56ADE4E36A4E2E5B00138756D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_825A92662B9B4407950D37C0CDB8F119" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_82C60047E4034EDE9C5AD03F08662F10" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_83189152D1FE45048CECC4A9991BFE56" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_835E8828F7DF417BF90CE0E5B3ED79A8" { "SourcePath" = "8:api-ms-win-crt-heap-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-heap-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_83A75CB51DCC4F87B8B9A3F8A3A215F6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_83C4B947AB2D43A09248D150A192DBF5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_846C24B81FCA43DC933E9BFA18FAE15E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_84CC521B95224F7383B53352F9151899" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_84CEE7DB1C0B407EABB654B361525004" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_85337AECC7CC496DAE6643D82A1E3F60" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\uk\\bridges.txt" "TargetName" = "8:bridges.txt" "Tag" = "8:" "Folder" = "8:_789838269269425EB5ACB92A794015C0" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_85CE5B3A65CD4D95AB6E6665907FB16B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_861479A1366940A2A130D6D3735A7D16" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\industry_info.txt" "TargetName" = "8:industry_info.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_863731CD1BFC40B393D50C392C7916BD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8749AF65C301480F9E7A596349DD1367" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh\\labellist_filter.txt" "TargetName" = "8:labellist_filter.txt" "Tag" = "8:" "Folder" = "8:_A9D6B01652F44EEEBB95F40DFA558141" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_879DA4798F1F4B11B5C99669556CC5B5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_87AA9DF095B146D986B7BED6B55017BE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_87E970659D104ACDA574ABA4AE56A153" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_882138DA00E9460F86B35552856298D2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_88A3256281AC4F25AAE598C1481FA990" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_88C0513F233B4CB4BB2CB756347E75F2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\edittools.txt" "TargetName" = "8:edittools.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_88CEDD14A00441AFABAAA66D3806D822" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\slopetools.txt" "TargetName" = "8:slopetools.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_89563EA02AAD48DCBD9CCFC5B5EB7A95" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\factory_build.txt" "TargetName" = "8:factory_build.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8959E24FC0CD434A8A27A073FDEC71E2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja.tab" "TargetName" = "8:ja.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_899749C5350C4225A5C30A5D6948EFDD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_899F7FB3E2F84E6A99128E5AABF039C2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\removal_tool.txt" "TargetName" = "8:removal_tool.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_89A41FCA30A346A3A5D38B4DE2892768" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\inspection_tool.txt" "TargetName" = "8:inspection_tool.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_89B354D3105949B1BF654E7853FC7F29" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8A0AA6D0C786436E93BD790C69FC2AFF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8A35DF083E7D481F94A22CB3A0CF9A71" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8AE1E9F65C8340A4B7B931321BC783FA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\underground.txt" "TargetName" = "8:underground.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8B1C110612BFB297E730E7DC8AC24F63" { "SourcePath" = "8:VERSION.dll" "TargetName" = "8:VERSION.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8B37931EDEBB4DBB82D4D794F46F4C4A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\linemanagement.txt" "TargetName" = "8:linemanagement.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8B88D692BFF14DE5890C554BD2597CC1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\industry_info.txt" "TargetName" = "8:industry_info.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8B98EC4380664715AFED6BEF0777940C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8C55EDABC5B542B88FF7074E4E99A08C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8CA32EF48C9640E9B151FC50FD50DA58" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8CEB3078E8C9419588372AA47D36A472" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\factorylist_filter.txt" "TargetName" = "8:factorylist_filter.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8D341EE4712F4242AF051C6559BAD35A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8D9FE449D76947338601A9EF5EFEFC94" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8DC1AD7B4B434F15B3326D669B5925A5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8E07E467482344D8AC909E3F8F08B953" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8E1348361EB141EB80B348E2BA434EC1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_A9D6B01652F44EEEBB95F40DFA558141" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8E2B0BCBB33945F9AD9D4A62B729FCBF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8E77D1CDBDC548E9BC92AC3E05BF156E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8F60949CD512455AB188852FB8723732" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\mainmenu.txt" "TargetName" = "8:mainmenu.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8FC094660CBC4042BFA3CE7638801FCD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_90088DD7694749FD97D2DDE8DDC76779" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9012A62701DB4225BA55AC67F1B5A4D5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\labellist_filter.txt" "TargetName" = "8:labellist_filter.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_903B35BF02DC4724854FBA8A693E7BDD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_907BB93A2A584C53B4A2808085BE7D63" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9093B505D80E44A88C5B422CF39C3F6B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_90F5B235E42C4301A5E0BE03B752C5D6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9196381DF8F04BECB29CB3A4EB358118" { "SourcePath" = "8:..\\..\\..\\simutrans\\thanks.txt" "TargetName" = "8:thanks.txt" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_91CCEF23F60240989740162A20FFF3F6" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\37-inevitably-engrossed.mid" "TargetName" = "8:37-inevitably-engrossed.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_93076B17FB6342D9A680D2DB5CB88911" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\tramtools.txt" "TargetName" = "8:tramtools.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_93553371BA0D4BA7AF534632AB28AF1C" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\18-Ride-that-train.mid" "TargetName" = "8:18-Ride-that-train.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_935E39838EEA4F03A10A26790A4CCE11" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\classic.tab" "TargetName" = "8:classic.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_939311F03E484FD2894C70B30E575E58" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ru\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_6AA7E18DFDC74EFF82A4CC967B14F3F2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_93E45578B9A84ACF886B916820BC4D5C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_93FF28B992E344FDA54C83E61D5DE45B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9413D0887CEA4CEF8E077401A528CF8E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9494DEF39F564D128A2A1D77C94B41B3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_94C3E9F789844176B1A29257B7F54F25" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9512546582D04B0BA8B1EB90C25F266C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_95DBA15EEC0F44B98B81C3ACFA4A5A78" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_97AC8B571A7744588E1B5F4DD29926FB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_97B9A460977F4B43A3F262ED3C9AF963" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_97D429291A1A4B448DEDAC39AC789CF6" { "SourcePath" = "8:..\\..\\..\\simutrans\\license.txt" "TargetName" = "8:license.txt" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_982663C4E3A74AF98D1614ECF1222641" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_996EB12093BD4A92A9C8C5D7A3938DED" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\settings.txt" "TargetName" = "8:settings.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_99F1C1D67C9F422685E2764F4D5E13E3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9A1181B0105A4B8F839A9E88FFA0580E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\zh\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_A9D6B01652F44EEEBB95F40DFA558141" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9A214C6687884ED5AE39CB9809B715B0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ca.tab" "TargetName" = "8:ca.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9A88343020324D73A521C5512E5801C7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9B24FA4B5DF145758D44C5A386E1DFFF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\labellist_filter.txt" "TargetName" = "8:labellist_filter.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9B45AF680D7B453183740CCC5186F98C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9B669C48C940449882D6A2D67A5396ED" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\tramtools.txt" "TargetName" = "8:tramtools.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9B97A924FE8546D488A68B2055F6DEAB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9BAE51D35D6A4608B5DEDE105547B06D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl.tab" "TargetName" = "8:pl.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9C3079C5132A47CA8F7095A52DE30568" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9C8885FA8EFF45EA9CA498CAAAF4D24B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9D2AE5121BA64CFE91973505C47CF36C" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\readme.txt" "TargetName" = "8:readme.txt" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9E3176E32D8A4094A57AB6B64B17DD9B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9E747D5708814DCA9BFAD71B7650A582" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9F43E9CB3E4642B384FAE24279AB317C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9F5B93603D5E4002AB90693F485FDD9E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\tramtools.txt" "TargetName" = "8:tramtools.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9F82245E13B84479A19ABB0517AFAE74" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\goods_filter.txt" "TargetName" = "8:goods_filter.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9FD82999B19B434586CA9147504F6B69" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A00BAB251762400FBFDB3F5F9DC7CC7F" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\prototyper.nut" "TargetName" = "8:prototyper.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A054E4EACE3C4ED0A06A7A05E9EBC6CD" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\aero.tab" "TargetName" = "8:aero.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A0774C119AFD4A54AB9DEFCD892EF3EA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A0EF7ECB8D1A450790401A38455F2FBC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A110420164074F6F80B1CCA1970AAAAF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\vehiclelist.txt" "TargetName" = "8:vehiclelist.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A12A3E89B73A4D3FB21673D41DA88E76" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A13E8EA5CEB54C88BB30C6725F581FFF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A1A46558136D4B6E95B80C55A3FA5629" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\special.txt" "TargetName" = "8:special.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A1C5832E423B46709ED06E8882B8DBD9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\removal_tool.txt" "TargetName" = "8:removal_tool.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A1F00F4A0643448DAC8E66CF0727F09E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A2611C5487D241FF8B561E52D56B160C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A2A0A247DEBE4C00A67DE0C78793BDF3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\id\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_A26968E6884145E79C521F92E05DD14C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A2B299E62D304A90BC01839FD9982CEA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A2C5C7638B7B423796BDDE1B37CC54DB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fi.tab" "TargetName" = "8:fi.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A3F9D5A01ECF47CF818A94DB7E3F30BD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A40DB08E389B4ED2A1FCD744FB4D5FC0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\signal_spacing.txt" "TargetName" = "8:signal_spacing.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A438B857F43A4B91BADAD3D54FE069CF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\th.tab" "TargetName" = "8:th.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A43ADD711918462F861281E4F45A3ED1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A46752F3A99348CCBCF9C33416BE5DDA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A4B5359306A34023A1ACFCD33C9B6B16" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A4C415F986314D75820B3ADD15F324DC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A4D653ABEC4646799283098724AAADB0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\inspection_tool.txt" "TargetName" = "8:inspection_tool.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A51036CBD78E4C6CBB96191773FE82EE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\citygrowth.txt" "TargetName" = "8:citygrowth.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A588D7D5733E64B36617EBE50FB31F3F" { "SourcePath" = "8:api-ms-win-crt-convert-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-convert-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A6644507B63B49799F05083DA240574B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\load_relief.txt" "TargetName" = "8:load_relief.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A6A9C9BDA5A64E4DAF67877AAFA1F7E7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\factorylist_filter.txt" "TargetName" = "8:factorylist_filter.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A6B3B56E517742D49D7AC896AB575EEA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A6EF70389C70472A8E4E4813A6CE44D3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A6FB275BE7D44AE2A24847C98A708103" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A766C5646A994F18811DC5D915ED99E1" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\modern-large.pak" "TargetName" = "8:modern-large.pak" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A779E5A5C75B4577A215A6927870BB62" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A77D989F8A3F4AA52521E174DDF6EDFD" { "SourcePath" = "8:api-ms-win-crt-runtime-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-runtime-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A7B5A28B66FC487D9ACCAA1A42521224" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A7F06B6F1E2744E8B7803429C9B12AFC" { "SourcePath" = "8:..\\..\\..\\simutrans\\script\\scenario_base.nut" "TargetName" = "8:scenario_base.nut" "Tag" = "8:" "Folder" = "8:_589146AE31A1480C89E8F37240599615" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A83F1194CD164AAA847A140EE8589B87" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A8778490948047EA9E74C817CFBE23C4" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\53-Where-Thomassons-Lie.mid" "TargetName" = "8:53-Where-Thomassons-Lie.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A9C9120ABBFE446E8CCA925945456C1F" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\classic-large.tab" "TargetName" = "8:classic-large.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AA1E6DC2B3FC4067ADCECCDA4B09C94B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\id\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_A26968E6884145E79C521F92E05DD14C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AA7A085BD36949179AF2196EB06CF7DF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AB7122076A2045E9A16367FAA2603934" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ABACE35D95144B839016535CE2C649AB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ABF7BAE834ED471AAC122B3BA17D4C73" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AC22050E398241C29F0A0AA66AD69C26" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AD63432954AB432DA360A092F3ABAFD4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\mouse.txt" "TargetName" = "8:mouse.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ADC52D4B09A046849FEC19C6ECFAA4A5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AE10E8B948F646FC960CEA7D158BE1A3" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\theme-large.tab" "TargetName" = "8:theme-large.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AE3624817F3F4EC2BC9ECCC528D96588" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AE43098A1E4D43D29FDAEF89B96A61AF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\bridges.txt" "TargetName" = "8:bridges.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AEE0681E6B8448B4988AB3F8BDB95FD4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AF1BDC5479324EDEB012F7C64C02297C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\load_relief.txt" "TargetName" = "8:load_relief.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AF20A26E81F541948829FB5AFDE66541" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\flat-skin-large.tab" "TargetName" = "8:flat-skin-large.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B0115B9715024672A9C1FA4CF0465956" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B0174050546944A19CF028E1DE44908C" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\classic.pak" "TargetName" = "8:classic.pak" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B038F7BAA7974FE0A200AF565B96E33F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B0F23AB72E7A44AE854EE09E4BEFBC66" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B1094D229AD5442EAB76FC665782E076" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\33-Journey-to-times-gone-by.mid" "TargetName" = "8:33-Journey-to-times-gone-by.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B12A038D1C9049108A3667AE89A7D7F7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\industry_info.txt" "TargetName" = "8:industry_info.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B18F2A9BB0514708B11E2E262F7D8610" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B1AA4D786277439581E192B8982152A6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B1E1D7D3CD6D49689CED7E882E2FB5F5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\curiosity_build.txt" "TargetName" = "8:curiosity_build.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B257E8B4219C4B298174488F5BCB7653" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B2EB2EFA27E54709B879C952BC42BB4B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B30E29F10A116D760074368E5BEB7A1F" { "SourcePath" = "8:api-ms-win-crt-time-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-time-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B316F19F34FE4A51BE7EC51035DB90B0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B371DAE4A0264666830E52D9514DA2D7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B3E5650F97D842528A636AD1DE430393" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B4B6DAC3BCDD42A99F77E9098980726B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B4E6E7EB7B9B42DABD1B411335B4429B" { "SourcePath" = "8:..\\..\\..\\simutrans\\script\\script_compat.nut" "TargetName" = "8:script_compat.nut" "Tag" = "8:" "Folder" = "8:_589146AE31A1480C89E8F37240599615" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B566F21CC84A433381E8CE406761ECB7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\convoiinfo.txt" "TargetName" = "8:convoiinfo.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B5C875785B0F4C9D9E76B3EFD305CF39" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B5DB74F3CBC44655AB3DE56EA147E4BD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\special.txt" "TargetName" = "8:special.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B639E4ABD1444D78A8951A35EDE48EF1" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\34-flyingaway.mid" "TargetName" = "8:34-flyingaway.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B679464E4EAF4C549CB3B9BF93D9358E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B69073812BD04A2BA4FE0B6A8728F192" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B6A96A7B568E4FEF8FA8064518106BC2" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\07-Transport-chaos.mid" "TargetName" = "8:07-Transport-chaos.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B7CC8D077EFE42F9A0BC18F9A542E5C5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\removal_tool.txt" "TargetName" = "8:removal_tool.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B7CC93E185B646E894689577E08EB81E" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\06-A-busy-day-at-the-depot.mid" "TargetName" = "8:06-A-busy-day-at-the-depot.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B7E5A578391A4BE3B786CDEBB4E7C262" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\readme_citylist.txt" "TargetName" = "8:readme_citylist.txt" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B7F6FB8254814AFE85BA148D583519C8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B82AEAEF414049BBA22FCF7C4281EBCF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B83FC3C677774402A75A2F6D613EC218" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B842BDA6FB9C4FF1B282CEF21A64CCC4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B875C187F9CA41C1AB77763828610A7A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B89E145011D34C80A50B7E8FF22318B8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B8A836BEA9F740858A2C1F1FFF645C71" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\tramtools.txt" "TargetName" = "8:tramtools.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B8FCCA127BE24285B276CB6B88CA12B6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B9BAC184AB844A96839578235935D13F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BA92F69D198449BEB1A743C8E95A3F14" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\curiosity_build.txt" "TargetName" = "8:curiosity_build.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BAA758A15315551B88F9AF92A6E67840" { "SourcePath" = "8:api-ms-win-crt-utility-l1-1-0.dll" "TargetName" = "8:api-ms-win-crt-utility-l1-1-0.dll" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BAC6972F9BC24F169F0B59391778F185" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BAF3091B84044FF092312FDC3C441130" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BB25C1A581B2468CA01A37B787637CCD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\curiosity_build.txt" "TargetName" = "8:curiosity_build.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BB51F76CF0614FFA817E9E28765936F4" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\45-Misty-Forest.mid" "TargetName" = "8:45-Misty-Forest.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BBD31A3743C64DAAAC66C73DBC395184" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\22-Variable-Journeys.mid" "TargetName" = "8:22-Variable-Journeys.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BBFE3D0B7BFB49009DA1BFF118B2CF66" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\vehicle_constructor.nut" "TargetName" = "8:vehicle_constructor.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BC104A0952934C1FB24C7C72FB22ECD2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BC5B445C4A87461784901F2D6717A9C2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\underground.txt" "TargetName" = "8:underground.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BD8A0A9482644575AB7586E6526EC50E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BD9E0D72052E4EF7904B45E1247AF92C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\mouse.txt" "TargetName" = "8:mouse.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BDA2160B8AD742CE91A5BA58C603C55E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BDAB03CEB8A34A48AE15DF20C239DDB6" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\save.nut" "TargetName" = "8:save.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BE0F9941F59E4C458CB67EC2AE92F547" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BE7A191C730B4BC3AE367292A126B060" { "SourcePath" = "8:..\\..\\..\\simutrans\\script\\script_base.nut" "TargetName" = "8:script_base.nut" "Tag" = "8:" "Folder" = "8:_589146AE31A1480C89E8F37240599615" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BF735304ABC245BBB4251519F60E5828" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BFEBF932AC564E48B0AF7256A973C278" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C0478D73D2AF4187949270E6FD38389B" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\factorysearcher.nut" "TargetName" = "8:factorysearcher.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C08F3403BDCD48AD83F04C9427530612" { "SourcePath" = "8:..\\nsis\\download-paksets.exe" "TargetName" = "8:download-paksets.exe" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C09716430EA54D868FD696C002FF54ED" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C0CEAECDB8F8480684B11DD07E961667" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C1ED0648FF6146B0A5C52AD0740588B6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C232E233F5334677A44AD9480882E1A4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\enlarge_map.txt" "TargetName" = "8:enlarge_map.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C25D46C642324693951E9A826BAD34F4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C292DECF838E4A338B1E9381FE88862C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C2BCF04AE0144DB7809933F09C0D799B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C2E91044BE604F148A304860DFB0BAAA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C32B257727534BA496BEB71041ADF2B4" { "SourcePath" = "8:..\\..\\..\\simutrans\\config\\simuconf.tab" "TargetName" = "8:simuconf.tab" "Tag" = "8:" "Folder" = "8:_397ACEE3EE1C46FE983BCD5397F6F592" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C367477479AC402B95F1AD058F17DF0A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C38EEA378E554DDC9B7516B8DDED70F7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C3C23BE233074FD6B456C2C05C5B4C15" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C45EA6EA27E24554B7E1A208734911EC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C476765DB8F94213A45B04C05F8658CD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C4DE63ED67994259861CC33EC3C8BAB3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\removal_tool.txt" "TargetName" = "8:removal_tool.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C4E281AEA3BE44368B8891E3DD4FB96C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C4FC973F4611411EBBCDD999F5028D43" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\labellist_filter.txt" "TargetName" = "8:labellist_filter.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C52BC200EEF6472E848B8CA666DC1B2C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C53EED194470430B8048AC19F45B8991" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\password.txt" "TargetName" = "8:password.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C556B896643D459C81B361121C73C2C3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\industry_info.txt" "TargetName" = "8:industry_info.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C5C315A416D34418A1655DBE35B025B3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C5FB56A197624E3C90766B9BC6F3D773" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\display.txt" "TargetName" = "8:display.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C645106B875C414D919C4B6EF4F649BC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C66A118A06614510B5E13491D6C91EC5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C6BDDA3D38EB4DB8BACF5DA31D983E82" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\bg.tab" "TargetName" = "8:bg.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C714818F8D754E9A8A9DC40733B30135" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C71AC36C9E1948349CD116DFBC84AB1E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\special.txt" "TargetName" = "8:special.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C769223702A14E0AAB81FBF9D60B6CA7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C76E4317C8FD444E80C157DEC0DAD612" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\station_details.txt" "TargetName" = "8:station_details.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C771425A4D4143728EDCE8CBCC90C4F3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C78E7F7C98154034B15F02B9BC53FC3F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\mailbox.txt" "TargetName" = "8:mailbox.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C809E4861D6D46E78D6A32FE8124E73E" { "SourcePath" = "8:..\\..\\..\\simutrans\\font\\cyr.bdf" "TargetName" = "8:cyr.bdf" "Tag" = "8:" "Folder" = "8:_8BAAB00D55944276B3A666BC02AD26FA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C81BAC2BED284DE786B2D681EB8E3E1F" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\21-Dusty-Eyes.mid" "TargetName" = "8:21-Dusty-Eyes.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C839E0301CEB47E1BA350AE6CDE807BB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C8DB858DCBE4427CA19FC95E46D103C6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\load_relief.txt" "TargetName" = "8:load_relief.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C950F7538D1A402185228383AEE60C82" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\curiositylist_filter.txt" "TargetName" = "8:curiositylist_filter.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C98C522FC01B44D5970EBC31B95FCFE4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\enlarge_map.txt" "TargetName" = "8:enlarge_map.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CA02543373D447F8B3E2C544CA97B122" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CB3347D71ED640DD9C8071E8BE50693B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\server.txt" "TargetName" = "8:server.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CB65E5CC799546B7955219DAAE0AA9DC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\jump_frame.txt" "TargetName" = "8:jump_frame.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CB6C6FA3B7D54809BF6148B3741134EF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CB9D6377113B44CF8E2345AC6BF1DE90" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CC048A88EB9D4ABC849A93F48E36C952" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CC991A94AFBB47B08D13C709894DCF90" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CCD22597FC2B42F3AC34751411580764" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\inspection_tool.txt" "TargetName" = "8:inspection_tool.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CD0D1300C88B487CAD7E04E2A7E34C43" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\edittools.txt" "TargetName" = "8:edittools.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CE23C05B12F5489D80D49B11021A68CD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CE49530C7C05497EAA89F5CCFE934216" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\goods_filter.txt" "TargetName" = "8:goods_filter.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CF71964739C84F05A16D5207060275EB" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\14-Last-journey-of-the-Niagara.mid" "TargetName" = "8:14-Last-journey-of-the-Niagara.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CFE643C379A64FC3AE2CD23E2D2F69F5" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\basic.nut" "TargetName" = "8:basic.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CFF251B9A0E448EF9DC1F492C1854571" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\special.txt" "TargetName" = "8:special.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D00F15F4EEF340309EA04799A0DB388C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D06F117B79164DADA51936E2405D9AE0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D10559A1107F43718503FAED41A57EE6" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\49-Last-Sunday.mid" "TargetName" = "8:49-Last-Sunday.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D11CEFCB96714000B0DA55A5870D6287" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\privatesign_info.txt" "TargetName" = "8:privatesign_info.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D1FF0706D277464DA9F9FD5BE0519C10" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D216427BA6A345C990A281EC393FDB6C" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\highcontrast-large.pak" "TargetName" = "8:highcontrast-large.pak" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D2EA924E189C4134B3F6738898223CEF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D2F40D27D4D5482E9EF3866E49DBA52F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\load.txt" "TargetName" = "8:load.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D34B18B929744F4ABE62200C7D45CFA6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D36E8BDC47C649FD9617D0A6D7AC6BEE" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\road_connector.nut" "TargetName" = "8:road_connector.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D37D32CEF4AB40BA8B574271FE31AC5B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D38CF487E50547C4A7BA4E1505094FE4" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\42-Stranger-Echoes.mid" "TargetName" = "8:42-Stranger-Echoes.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D3A18E1A4D144D70A6F2241EBFFEC2C2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\gr.tab" "TargetName" = "8:gr.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D45DDB9BC95E46DD99772BBB7E9D9560" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\ship_connector.nut" "TargetName" = "8:ship_connector.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D48F15ED15E74449A5E39ED62B3CDFFB" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\39-bangin-mover.mid" "TargetName" = "8:39-bangin-mover.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D4AC3EDE729D4D8C9CE79F3BBF4A0C64" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D4C9319AB3AA4AF3BEE5F92FBD5A4E45" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D5360EBC9ED241A2A644B6CF6529B04B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es.tab" "TargetName" = "8:es.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D558CB15135F433D88F6035244DCEB68" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D58F1D37A97E4C708CAAD0AA4835AD3C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D5A25B4F622C4C4FAC8096B7EEFA5F15" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D5CED9D947C04A19B2F0AE853A4E3A1E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D66C55C5935F4AB79B03B4C00AD0F796" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\info.txt" "TargetName" = "8:info.txt" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D6A3CA57C8B54054BC881B683F7DA82B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D70EE948D30C4F86AF2B40C54035E892" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\signal_spacing.txt" "TargetName" = "8:signal_spacing.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D717157FF96A429EBD907E570F4F2BC0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D773A4CCDBBD4AF5BFE18BC2DC1AC87B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D7F83D314A5247539D7E8246AB6F6A86" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\scenario.txt" "TargetName" = "8:scenario.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D8763B920D6D46E3AD1B6911EF951B15" { "SourcePath" = "8:..\\..\\..\\simutrans\\copyright.txt" "TargetName" = "8:copyright.txt" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D93F09ED33D649DD84751A2F14898DA7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D94AD6C7B4804825BF99854482A4159F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D970E48380BB47F99441E94A56F62A6F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\eo.tab" "TargetName" = "8:eo.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D97B480C07D844B39CAD227C1EF4B211" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D9DB1DDB4B344A11887DF8AB3C579A37" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\01-Simutrans-Main-Theme.mid" "TargetName" = "8:01-Simutrans-Main-Theme.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DA31A7B03687476B988805E3DFB3C5DC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DA44B31AF21F443D9C263A16FDA69946" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DB23FA2C15D24D49B624E736F061F420" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\factorysearcher.nut" "TargetName" = "8:factorysearcher.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DB74BC92EE834DC29A1FC5FBF31A484F" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\30-On-the-waterfront.mid" "TargetName" = "8:30-On-the-waterfront.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DBB821B3793A46BB81E2CF2B9A19B6AB" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DBC90AA5A0FD42C8A89D134FBCF0B15F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DBF5793FB8FE4AC0AEB966FDC7DEE88A" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\modern.pak" "TargetName" = "8:modern.pak" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DC405B0582F34181944B53E63C15687F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\color.txt" "TargetName" = "8:color.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DCC1BBCD7ED54FF8971DC872AE55C7B3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DCDDCA4564C04930948BD2CB44067A3E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\factorylist_filter.txt" "TargetName" = "8:factorylist_filter.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DCF636D15E164C79AE8B74AD2919CC37" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DDD56428DD73444F974D61F9825040B5" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DE1DF054CBB64310807E5A20C2D2467C" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\35-deep-ride.mid" "TargetName" = "8:35-deep-ride.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DE58A3BDE0904A95911A189BEFC5B524" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DE66DD7DDF4F4F96ABD6C752FEF96FE2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\privatesign_info.txt" "TargetName" = "8:privatesign_info.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DE7DEFD90AA142F982CC61E7D208D90C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DF35D4CBA48347EC8527CB78B44D97EA" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai\\station_manager.nut" "TargetName" = "8:station_manager.nut" "Tag" = "8:" "Folder" = "8:_4F550A6D8B9549C28254FE6C48EE9E9E" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DF443B32252641F688BF2BB51DA32B91" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DF977ED1A9D84B27B29191B1E1047B4D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E00F0E6EB6714730B47ADA6C126DD397" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\tramtools.txt" "TargetName" = "8:tramtools.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E036E1DCACD844D9B32566942128B2F1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E04B04B7A7A9412B959C5912CF5BD600" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E0626408DAE04212B7A5010F753DBEF3" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\rail_connector.nut" "TargetName" = "8:rail_connector.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E0D736AF86EA4D5FBF643F5A8F351B4C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E0F743C6A6394781852F6CE29CDC99BE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sv\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_F11D2625D9744C6CA3057A6D8C74A7E6" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E11660ECA02941C091C88F1BEBF4B631" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E12B0D5FE8814778B773D37F3D3E6F7E" { "SourcePath" = "8:..\\..\\..\\simutrans\\license_squirrel.txt" "TargetName" = "8:license_squirrel.txt" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E1DDAA09A5FD465AB8577C4655CCB033" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\aerotheme.pak" "TargetName" = "8:aerotheme.pak" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E217CA4F66774BA5A09314963FBD6CCA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\edittools.txt" "TargetName" = "8:edittools.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E26EBAC5B8A0472E82DC9E74A440688D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\simutrans.txt" "TargetName" = "8:simutrans.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E2BB419D3B7F4E39AE594DF418F07C45" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\curiosity_build.txt" "TargetName" = "8:curiosity_build.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E2CFE015582543699346EC288B70E41D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E31F68CF189145C1B8FFFD1148F95198" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\privatesign_info.txt" "TargetName" = "8:privatesign_info.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E4C946F4DF9444609A09E13D9072C3C6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E4FAC1F2D0C842C3A5B6560FD284FC1B" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\47-Salty-Breeze.mid" "TargetName" = "8:47-Salty-Breeze.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E579728367CD44EBA04AAF7008C46384" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\special.txt" "TargetName" = "8:special.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E58DF4184FB145939D80A4AAC090F421" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E5BFBEC26C6B41DA9D85C4D678B637BF" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\vehicle_constructor.nut" "TargetName" = "8:vehicle_constructor.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E5F10C067FFC4F318F8CB2D219EE87F1" { "SourcePath" = "8:..\\..\\..\\simutrans\\font\\prop.fnt" "TargetName" = "8:prop.fnt" "Tag" = "8:" "Folder" = "8:_8BAAB00D55944276B3A666BC02AD26FA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E620874F18094CED984F4503FABAF303" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\uk\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_789838269269425EB5ACB92A794015C0" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E622F110BE7948158581ABC21B6520E0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E627F06E09E94890A8657BDA806DC45D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E691274B91684CD2864B82DF108BEBB4" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\save.nut" "TargetName" = "8:save.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E6AD10132AC04F27ACD79BBC321F30EC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E6CF33E4349C478D8C1B381C31A2E09B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E746B80C52C54F2F9B17C580432EEFBA" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E75A3F6B76624E7AB13ECB7AD38250DC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E7864D4A83C04FFE8C5C06B03E11DEDD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E7CFF4924DC546198491DD71E7C5C9A2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\curiositylist_filter.txt" "TargetName" = "8:curiositylist_filter.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E84D0915BD9B403582CBFCFE8B95B646" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E86C074859BF4775A00BD84487CF1275" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E889A14C0D074B379C16DB8CB052A0AF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\labellist_filter.txt" "TargetName" = "8:labellist_filter.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E89BAA16F29B43F3B85DF8FF73598785" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cn\\labellist_filter.txt" "TargetName" = "8:labellist_filter.txt" "Tag" = "8:" "Folder" = "8:_50DDEE80C09B4936B9F3BD188441B081" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E8D6C5BCE19D480C8686AA7FF5366EC2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pl\\shiptools.txt" "TargetName" = "8:shiptools.txt" "Tag" = "8:" "Folder" = "8:_0E185265549642E88542EE4FC1116E88" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E8E1EB3034AF439B81FC505E88623678" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\readme.txt" "TargetName" = "8:readme.txt" "Tag" = "8:" "Folder" = "8:_095792EE20E44A23A57816A778D0137B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E969A510B3D54FCB9AC85993634A5792" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\schedule.txt" "TargetName" = "8:schedule.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E9E737ACC2A846EAB4DA8B8247E4F9B8" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E9E893DFCBCE4770B1A667EB553233C3" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EA0871A0A6C94CE8B4E5C8256BD07B56" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\linemanagement.txt" "TargetName" = "8:linemanagement.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EB211D0E657248F2A72935961F75758D" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\02-Gotta-catch-that-train.mid" "TargetName" = "8:02-Gotta-catch-that-train.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EB78E5C4CE8044F1A14092FAE05D6DA0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EC1ADB1DC1104F78A7F2F5520DB23B01" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EC5DA89F78B84B1D8A90F9ED53AD36D0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\readme.html" "TargetName" = "8:readme.html" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EC9766A95CC74083A11EAEBF44A688A1" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\flat-skin.tab" "TargetName" = "8:flat-skin.tab" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ECC9F626519A414B921A8E234EF0A441" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ECDBD057B2934DB1BD42A1B33E89C357" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\trafficlight_info.txt" "TargetName" = "8:trafficlight_info.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ECDF05EBC846419B808DA15EE0F2DAFC" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\airtools.txt" "TargetName" = "8:airtools.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ED718DE30C0D43B1AB252B3C48074814" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ED891169792044308BA72FF3909914AE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\monorailtools.txt" "TargetName" = "8:monorailtools.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ED8EB9CCE7D64495894EE5DCBE629CE2" { "SourcePath" = "8:..\\..\\..\\simutrans\\themes\\highcontrast.pak" "TargetName" = "8:highcontrast.pak" "Tag" = "8:" "Folder" = "8:_9DC00CDC1F814439B7D247D84096B391" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EE6428DBC5BC4BE88A90FC65A1745A29" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\finances.txt" "TargetName" = "8:finances.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EE64372F00AC4267BEEAFCC9C6B58E18" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\railtools.txt" "TargetName" = "8:railtools.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EE72B3867FE6440CA771E3550E38E396" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EEB9D46E7BE943D7AB48509C26A5C0EF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\load_relief.txt" "TargetName" = "8:load_relief.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EF47B6A3727849DAB2FE32F460BE8FF9" { "SourcePath" = "8:ProgramDataClear.js" "TargetName" = "8:ProgramDataClear.js" "Tag" = "8:" "Folder" = "8:_589146AE31A1480C89E8F37240599615" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F07AC979B87A44A6971AE4CBB5D149D6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F09566EC6FCE4F82A31A9B0E54E7EDB1" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fi\\new_world.txt" "TargetName" = "8:new_world.txt" "Tag" = "8:" "Folder" = "8:_6675A84858B7478194E7F18868F647F2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F0DA6488EAB24633A1F6D3634BC3769A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\map.txt" "TargetName" = "8:map.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F0E01DD09B8D47149A4A175506C86B78" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\general.txt" "TargetName" = "8:general.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F1140351BFBC4E629BDAF8CE39EDE591" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\slopetools.txt" "TargetName" = "8:slopetools.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F11D733F5F124EA3AB8410692487A78B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fp.tab" "TargetName" = "8:fp.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F12FA89FB33848218ED0FB80A64790DE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hr\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_2D7B5643FA674C72801393320CEF6786" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F130429E58C6468287355F1E01AFCC4B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\goods_filter.txt" "TargetName" = "8:goods_filter.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F2202E85444340B1B58243D1CE5AE6DF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\citywindow.txt" "TargetName" = "8:citywindow.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F231DE5A2FD2486399D07279FE9E9977" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\25-Float-on-by.mid" "TargetName" = "8:25-Float-on-by.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F31DAAA2A03645A7A6D5A86256991A95" { "SourcePath" = "8:..\\..\\..\\simutrans\\history.txt" "TargetName" = "8:history.txt" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F35AEED69FF14D85906771EF9BE1E9B0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz.tab" "TargetName" = "8:cz.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F3AC087C60844BA4988566DB48C908C6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\uk\\citybuilding_build.txt" "TargetName" = "8:citybuilding_build.txt" "Tag" = "8:" "Folder" = "8:_789838269269425EB5ACB92A794015C0" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F3CD1BD309E34B28AEF60F9FAB278B9E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\server.txt" "TargetName" = "8:server.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F40CE2B4AF78406B95630523AC31FFD2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\factorylist_filter.txt" "TargetName" = "8:factorylist_filter.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F42AAC8BCDDA4AAEB61AC38E9E441CCF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ro\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_457530314F624CEFA48B24EC3A1E4DC7" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F45743E0EE0C453B977771467658A7A0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\curiosity_build.txt" "TargetName" = "8:curiosity_build.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F46B417F426B4A7FA4E56EE2126FC51F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\linedetails.txt" "TargetName" = "8:linedetails.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F49FFDC689AB470F896FDB210760C4A6" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\04-Simutrans-B-Theme.mid" "TargetName" = "8:04-Simutrans-B-Theme.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F4A14191893942AAA5EA1A541CEF3E18" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\nl\\citylist_filter.txt" "TargetName" = "8:citylist_filter.txt" "Tag" = "8:" "Folder" = "8:_ADEA92C80F784C009BC3F52129DE69ED" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F519563E51B94B8D91BDFE594EF272A6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\jump_frame.txt" "TargetName" = "8:jump_frame.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F588BEAFF08146F780E0D1410A51FA5B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F58EA264ED0A47CAB5F5B47DEB36D2C4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F5E2A267D3AA495B8296333CCE8171B0" { "SourcePath" = "8:..\\..\\..\\simutrans\\music\\19-Rockin-trucker.mid" "TargetName" = "8:19-Rockin-trucker.mid" "Tag" = "8:" "Folder" = "8:_B780B35D6F7A4448A293E4643747F033" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F61055B7AFA94AC0A725D6E7AD0725D0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F61682BC67C44421BC5B219801D9BCEB" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\astar.nut" "TargetName" = "8:astar.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F64F538AB0E1481BB9AC860C911171A6" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F663097F2CCD4CA4B17CC05519AC8C4B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\window.txt" "TargetName" = "8:window.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F6BC807749F54132AFC98F6827B8F7BF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\language.txt" "TargetName" = "8:language.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F6F178E2FE184A6D8A42C619EA54979F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\station.txt" "TargetName" = "8:station.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F75C5CADB5554982A153E0967CD5ECE2" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F7619C72DBF2409EB37294746CCCE580" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\baum_build.txt" "TargetName" = "8:baum_build.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F7CC215F650944B3A8A5DF21A79CAC48" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F84089B117984EACB3BB401593BD1B6D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F84258958801470AB39943310ED08AA4" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr.tab" "TargetName" = "8:fr.tab" "Tag" = "8:" "Folder" = "8:_E5FFBD7F0C89420D9553CA08E709A59C" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F8D528CC3D51483DBDACE3CB9902880F" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\goods_filter.txt" "TargetName" = "8:goods_filter.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F92D7AC2DD14413384E88CE4B30F09B2" { "SourcePath" = "8:..\\..\\..\\simutrans\\font\\Prop-Latin2.bdf" "TargetName" = "8:Prop-Latin2.bdf" "Tag" = "8:" "Folder" = "8:_8BAAB00D55944276B3A666BC02AD26FA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F95A613A31BA451D99930AB79F14614C" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\fr\\depot.txt" "TargetName" = "8:depot.txt" "Tag" = "8:" "Folder" = "8:_D80AA159986C49BB9A580BD344005969" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F9E9C90BDD0F45F6BB9E2735A05B5D20" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F9F8DEA95B1448F683EE58097C23CDAD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\convoidetail.txt" "TargetName" = "8:convoidetail.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FA6D869BE6E943D0AA0C1306C8234A82" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ko\\goods_filter.txt" "TargetName" = "8:goods_filter.txt" "Tag" = "8:" "Folder" = "8:_1DA44BC586394673AA54DD4DB6B03B41" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FAB73043E4B44747B9DB2A6C7BDE20C7" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\players.txt" "TargetName" = "8:players.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FACA7D96D56C46339D48D794AD2B266E" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\it\\roadtools.txt" "TargetName" = "8:roadtools.txt" "Tag" = "8:" "Folder" = "8:_15A07DA91FBA4E07B1CA74A40BE82AD2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FB33BA37B5664323A82F52A0BAB31996" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\climates.txt" "TargetName" = "8:climates.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FB87560927AC4EF9994BF2E6033BFC37" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\removal_tool.txt" "TargetName" = "8:removal_tool.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FB9702887DD5457782E571E44FEE852A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\jump_frame.txt" "TargetName" = "8:jump_frame.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FBD3810511FC4EE8BEEC600F211EC31D" { "SourcePath" = "8:..\\..\\..\\simutrans\\font\\ro_font.fnt" "TargetName" = "8:ro_font.fnt" "Tag" = "8:" "Folder" = "8:_8BAAB00D55944276B3A666BC02AD26FA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FBDC94140B404C93B5E6991A4013AA96" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\es\\sound.txt" "TargetName" = "8:sound.txt" "Tag" = "8:" "Folder" = "8:_9C6031A6730340FF81DE6E2DB6382F19" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FBEB80FA3394476993339C5BB381BD71" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\tramtools.txt" "TargetName" = "8:tramtools.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FC637EBDEAA44A81B8A7DC3B86E7B34A" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\haltlist.txt" "TargetName" = "8:haltlist.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FD47CC396F5A4FCEBC4BC3A74BB463DD" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\en\\list.txt" "TargetName" = "8:list.txt" "Tag" = "8:" "Folder" = "8:_6A48B18D268B4269BC306957F57C5602" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FDB3F3C4DD4B4757B915C4CDCD5CA134" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\settings.txt" "TargetName" = "8:settings.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FDE16188C8F84DD4AA0718B240A13F1B" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FE391EA9A2D741FF8537B937A3757277" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\password.txt" "TargetName" = "8:password.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FE449B821B53468E9AEBCFAFA2411C17" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\cz\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_FE9928D85D6D4839856B1A55E82FF3C1" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FEA32CC3E67F43218123289C8920DD92" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\ja\\save.txt" "TargetName" = "8:save.txt" "Tag" = "8:" "Folder" = "8:_853C7D6775444583B2E5CF8612FF2482" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FEF5A316BA1640CDAD18C911C28FD23D" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\pt\\curiositylist_filter.txt" "TargetName" = "8:curiositylist_filter.txt" "Tag" = "8:" "Folder" = "8:_64307D5F4D9F47A0AAC6223079A17655" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FF0FF8FD90F14B63A9CC7CC4D25EC5BF" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\de\\haltlist_filter.txt" "TargetName" = "8:haltlist_filter.txt" "Tag" = "8:" "Folder" = "8:_5C21685B773242648E0ACAED2ED24872" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FF336FD387BD4B03B2C2120EC5C37AD9" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\sk\\options.txt" "TargetName" = "8:options.txt" "Tag" = "8:" "Folder" = "8:_499E4D5D60A446ADBAD61455C007ACDE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FF97C0E77765407A8E9B2EBDAA0C3378" { "SourcePath" = "8:..\\..\\..\\simutrans\\ai\\sqai_rail\\industry_manager.nut" "TargetName" = "8:industry_manager.nut" "Tag" = "8:" "Folder" = "8:_2A45754220784BF98E5F500D384BDF6B" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FFA020A1D53A4B4E9658D0BDB393F0F0" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\dk\\convoi_filter.txt" "TargetName" = "8:convoi_filter.txt" "Tag" = "8:" "Folder" = "8:_04068C80229D4FFA8D9C962E9E0CB785" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FFAAEFAB7D684963A4C7532968E26CEE" { "SourcePath" = "8:..\\..\\..\\simutrans\\text\\hu\\convoi.txt" "TargetName" = "8:convoi.txt" "Tag" = "8:" "Folder" = "8:_0678692909684C3B8B9C71F487C68494" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } } "FileType" { } "Folder" { "{3C67513D-01DD-4637-8A68-80971EB9504F}:_52BA1BC52411470C9B09D5CD28B62FAE" { "DefaultLocation" = "8:[ProgramFilesFolder]\\Simutrans" "Name" = "8:#1925" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:TARGETDIR" "Folders" { "{9EF0B969-E518-4E46-987F-47570745A589}:_095792EE20E44A23A57816A778D0137B" { "Name" = "8:ai" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_7DC2A6D314B1423892DD86B361D88A8A" "Folders" { "{9EF0B969-E518-4E46-987F-47570745A589}:_2A45754220784BF98E5F500D384BDF6B" { "Name" = "8:sqai_rail" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_0BB203E0D58444B291F18C85CD5B32FC" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_4F550A6D8B9549C28254FE6C48EE9E9E" { "Name" = "8:sqai" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_F353E86449B14ACFBAE2F81AA18A6347" "Folders" { } } } } "{9EF0B969-E518-4E46-987F-47570745A589}:_397ACEE3EE1C46FE983BCD5397F6F592" { "Name" = "8:config" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_F65526EBAB454E488E7B62FB82D6AE8D" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_589146AE31A1480C89E8F37240599615" { "Name" = "8:script" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_E989ADEF3BE84A72A921BCD3FD4C3F49" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_8BAAB00D55944276B3A666BC02AD26FA" { "Name" = "8:font" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_504578582BF646E2ACF35BA50CC7AAC1" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_9DC00CDC1F814439B7D247D84096B391" { "Name" = "8:themes" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_EFDD01C448E84B8DA0AB11B4516FBC5A" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_B780B35D6F7A4448A293E4643747F033" { "Name" = "8:music" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_C5DFCC98652B4A9D98DE2932EF966655" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_E5FFBD7F0C89420D9553CA08E709A59C" { "Name" = "8:text" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_76E06520267546CCA412081ED5C9F122" "Folders" { "{9EF0B969-E518-4E46-987F-47570745A589}:_04068C80229D4FFA8D9C962E9E0CB785" { "Name" = "8:dk" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_70FAFE45E3774FDC8E10E9B6A60FA112" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_0678692909684C3B8B9C71F487C68494" { "Name" = "8:hu" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_18560D4B3A1E4A759776D6052D8B2D24" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_0DBD521BD66F4570A9CB7EDF17565613" { "Name" = "8:et" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_E499D02EA37B497F9E270521879396BE" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_0E185265549642E88542EE4FC1116E88" { "Name" = "8:pl" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_18DFD4B112274FE5BD29AD4DDA64A72F" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_15A07DA91FBA4E07B1CA74A40BE82AD2" { "Name" = "8:it" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_464389FF2590495D94C28F793B3DEBD0" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_1DA44BC586394673AA54DD4DB6B03B41" { "Name" = "8:ko" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_14BA639FCBFB449B8D5AA5C09DA21DA8" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_2D12A68841DE4C4F8AC1AE3E21C2F247" { "Name" = "8:gr" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_EB61F116DF8347658ACE6C640416FD01" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_2D7B5643FA674C72801393320CEF6786" { "Name" = "8:hr" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_A712CB08976A4A9E924B31923313925D" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_3F50924EBCAB44658C46AD13CB6404F7" { "Name" = "8:eo" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_B6CA6BC70B2C41B9BC643DCEE84ADD1A" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_457530314F624CEFA48B24EC3A1E4DC7" { "Name" = "8:ro" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_D91D42A99639450CB00C1957920147AB" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_499E4D5D60A446ADBAD61455C007ACDE" { "Name" = "8:sk" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_4067FCAEA27C4F6680622B27E8325397" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_50DDEE80C09B4936B9F3BD188441B081" { "Name" = "8:cn" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_CBE82BC167A44840836980650F6ED979" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_5C21685B773242648E0ACAED2ED24872" { "Name" = "8:de" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_3C16A0C063FE4BD1B508584F204239E5" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_64307D5F4D9F47A0AAC6223079A17655" { "Name" = "8:pt" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_5CD220DC0E20426BB6CF82E8DBB3A1CC" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_6675A84858B7478194E7F18868F647F2" { "Name" = "8:fi" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_A44086DF416A414DB097C1D989ACAD44" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_6A48B18D268B4269BC306957F57C5602" { "Name" = "8:en" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_BDE71A2F2FAF4D4EB4E5AFD75DD556C2" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_6AA7E18DFDC74EFF82A4CC967B14F3F2" { "Name" = "8:ru" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_6CBDE0A9E25848479BB626B0C9B9700D" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_6AFBEC71F0134B3EBE7092E5A9145B47" { "Name" = "8:no" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_40BE261FAB7644D79271EF6A0D59A6CD" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_729ABE1ACF0C4027AA858EED022538F2" { "Name" = "8:ca" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_02B97B1711904ED2A725CC0F97759065" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_789838269269425EB5ACB92A794015C0" { "Name" = "8:uk" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_EA312CB051D747B3A729729E48F31B40" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_853C7D6775444583B2E5CF8612FF2482" { "Name" = "8:ja" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_3589CA3B32FC4E24BCEFEF8B15473CBD" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_94B2E4229B2B44E895D6A98B1129B099" { "Name" = "8:bg" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_ECD5FF10ED1948FD8F8CE4A3057F857D" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_9C6031A6730340FF81DE6E2DB6382F19" { "Name" = "8:es" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_5C86A757B4094458A3B33DCB065E0052" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_A26968E6884145E79C521F92E05DD14C" { "Name" = "8:id" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_E0E3D318740145EBAC8EFE9AF2A6EA2D" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_A45D7C790E0C4752A4422E62C74568D6" { "Name" = "8:th" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_CF444B84B9934979911BEEBEBC97383B" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_A9D6B01652F44EEEBB95F40DFA558141" { "Name" = "8:zh" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_2BFAFDBE9A4B48F6AE8A01C57F6D7453" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_ADEA92C80F784C009BC3F52129DE69ED" { "Name" = "8:nl" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_BE262E9E92F649C58572A07000825AB1" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_BDE6662E864E49F7A98B835D4D8BF670" { "Name" = "8:fp" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_81D579FA3DC14881B0977A5B902ABE3C" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_D7C6A03AB90244D2A922A31ACEFB0F86" { "Name" = "8:lt" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_F397646C44694CAE96FC44176185BA2B" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_D80AA159986C49BB9A580BD344005969" { "Name" = "8:fr" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_4773E10ACBDA42C08F31FA2D59B20EFB" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_DA247FB0226342E8A6A415C4572F638A" { "Name" = "8:ce" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_817D0EB0EB174A478D2ED24CC9EF4BBF" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_DD9701F195AA4B3D96975B01836C5F7F" { "Name" = "8:tr" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_3E21B28F08824A1FAB617E29D9C34A1D" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_E161028A40244DC890C6DF3E5C6188BA" { "Name" = "8:be" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_B1586F75B21C43BABC78DD6EFEA2C87D" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_F11D2625D9744C6CA3057A6D8C74A7E6" { "Name" = "8:sv" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_68C4677596AE44228605D620787B5C21" "Folders" { } } "{9EF0B969-E518-4E46-987F-47570745A589}:_FE9928D85D6D4839856B1A55E82FF3C1" { "Name" = "8:cz" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_F2918120A3D34918A733CFEC430011F4" "Folders" { } } } } } } "{1525181F-901A-416C-8A58-119130FE478E}:_8286C1434FAF4646BE9D651967A10096" { "Name" = "8:#1919" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:ProgramMenuFolder" "Folders" { } } "{994432C3-9487-495D-8656-3E829A8DBDDE}:_A5BC29A726E64EB18DA057FAEE2DD996" { "DefaultLocation" = "8:[CommonAppDataFolder]\\Simutrans" "Name" = "8:Simutrans" "AlwaysCreate" = "11:TRUE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:NEWPROPERTY1" "Folders" { } } "{1525181F-901A-416C-8A58-119130FE478E}:_BCC58B92BBCB4EE89F2D7B1B590A6613" { "Name" = "8:#1916" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:DesktopFolder" "Folders" { } } } "LaunchCondition" { } "Locator" { } "MsiBootstrapper" { "LangId" = "3:0" "RequiresElevation" = "11:FALSE" } "Product" { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:Simutrans" "ProductCode" = "8:{9349912D-403F-4640-B356-0A6A248E5506}" "PackageCode" = "8:{1DF947DD-F67D-40F4-ADCA-53DF95F79A53}" "UpgradeCode" = "8:{04747FE4-03F3-4A59-8E96-C2D0DBAF2BE7}" "AspNetVersion" = "8:2.0.50727.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:FALSE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:FALSE" "ProductVersion" = "8:123.0.1" "Manufacturer" = "8:Simutrans Team" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" "Title" = "8:Simutrans" "Subject" = "8:" "ARPCONTACT" = "8:Simutrans Team" "Keywords" = "8:" "ARPCOMMENTS" = "8:Installs the Simutrans Transport Simulator Game in SDL2 32 Bit version" "ARPURLINFOABOUT" = "8:" "ARPPRODUCTICON" = "8:" "ARPIconIndex" = "3:0" "SearchPath" = "8:" "UseSystemSearchPath" = "11:TRUE" "TargetPlatform" = "3:0" "PreBuildEvent" = "8:" "PostBuildEvent" = "8:" "RunPostBuildEvent" = "3:0" } "Registry" { "HKLM" { "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_4DB56EE5F1714EB9A4200EF264D68316" { "Name" = "8:Software" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_2535A6E556814867AED8BCE9C1683C6A" { "Name" = "8:[Manufacturer]" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { } "Values" { } } } "Values" { } } } } "HKCU" { "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_157F22C6E58740ABAA53BEEBAB22C873" { "Name" = "8:Software" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_C45B8D428A1C44C9831FEB92A08B4520" { "Name" = "8:[Manufacturer]" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { } "Values" { } } } "Values" { } } } } "HKCR" { "Keys" { } } "HKU" { "Keys" { } } "HKPU" { "Keys" { } } } "Sequences" { } "Shortcut" { "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_F57E5DD1DBBE473D8BB002FE2766588E" { "Name" = "8:Simutrans" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" "IconIndex" = "3:100" "Transitive" = "11:FALSE" "Target" = "8:_B02CDD54271647E084EBF14CA126EEFB" "Folder" = "8:_8286C1434FAF4646BE9D651967A10096" "WorkingFolder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Icon" = "8:_B02CDD54271647E084EBF14CA126EEFB" "Feature" = "8:" } } "UserInterface" { "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_146261DCA8F042788377A07566B20D57" { "Name" = "8:#1902" "Sequence" = "3:1" "Attributes" = "3:3" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_163F870C006A493B98CA78FAD364D09D" { "Sequence" = "3:100" "DisplayName" = "8:Finished" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdFinishedDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "UpdateText" { "Name" = "8:UpdateText" "DisplayName" = "8:#1058" "Description" = "8:#1158" "Type" = "3:15" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1258" "DefaultValue" = "8:#1258" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_23B57DE408C64ECEB6BBDF2F4051DA10" { "Name" = "8:#1902" "Sequence" = "3:2" "Attributes" = "3:3" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_EBE035DE8391403C956031FD63B478E3" { "Sequence" = "3:100" "DisplayName" = "8:Finished" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminFinishedDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_4AED4AA98F09409A8092136212DFD2D6" { "Name" = "8:#1900" "Sequence" = "3:2" "Attributes" = "3:1" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_BED56C62870A449CBCDB91658BDD0FC8" { "Sequence" = "3:100" "DisplayName" = "8:Welcome" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminWelcomeDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "CopyrightWarning" { "Name" = "8:CopyrightWarning" "DisplayName" = "8:#1002" "Description" = "8:#1102" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1202" "DefaultValue" = "8:#1202" "UsePlugInResources" = "11:TRUE" } "Welcome" { "Name" = "8:Welcome" "DisplayName" = "8:#1003" "Description" = "8:#1103" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1203" "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_D882F877F4424D6783C52EEBC04ED6BC" { "Sequence" = "3:300" "DisplayName" = "8:Confirm Installation" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminConfirmDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_F2E8FF8E279544DC8401A5EA502A1738" { "Sequence" = "3:200" "DisplayName" = "8:Installation Folder" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminFolderDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_807B77E05FC84ED0ADB496BED79BC654" { "UseDynamicProperties" = "11:FALSE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdUserInterface.wim" } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_985FF174696B4FE79C7B71E3BCCE57C3" { "Name" = "8:#1901" "Sequence" = "3:2" "Attributes" = "3:2" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_BA112D1532CA4844AD4436C923BB4382" { "Sequence" = "3:100" "DisplayName" = "8:Progress" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminProgressDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "ShowProgress" { "Name" = "8:ShowProgress" "DisplayName" = "8:#1009" "Description" = "8:#1109" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_98DE520552624BE185C29C0CFA506D13" { "UseDynamicProperties" = "11:FALSE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdBasicDialogs.wim" } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_A122B1B3CC12485A93B254821F99C458" { "Name" = "8:#1900" "Sequence" = "3:1" "Attributes" = "3:1" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_92139CD85F1B4416BD64CF2857F9C28F" { "Sequence" = "3:300" "DisplayName" = "8:Confirm Installation" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdConfirmDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_BAAD3C448E334CEF95EE2092B2AA29C3" { "Sequence" = "3:200" "DisplayName" = "8:Installation Folder" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdFolderDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "InstallAllUsersVisible" { "Name" = "8:InstallAllUsersVisible" "DisplayName" = "8:#1059" "Description" = "8:#1159" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_BB7A9B597C014906A2014E13D4DFF287" { "Sequence" = "3:100" "DisplayName" = "8:Welcome" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdWelcomeDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "CopyrightWarning" { "Name" = "8:CopyrightWarning" "DisplayName" = "8:#1002" "Description" = "8:#1102" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1202" "DefaultValue" = "8:#1202" "UsePlugInResources" = "11:TRUE" } "Welcome" { "Name" = "8:Welcome" "DisplayName" = "8:#1003" "Description" = "8:#1103" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1203" "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_A922C2038B4D4533BCB32D48FD557BA6" { "Name" = "8:#1901" "Sequence" = "3:1" "Attributes" = "3:2" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_3BF20F731F1F4AAAAF1EC1D836B194DC" { "Sequence" = "3:100" "DisplayName" = "8:Progress" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdProgressDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "ShowProgress" { "Name" = "8:ShowProgress" "DisplayName" = "8:#1009" "Description" = "8:#1109" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } } "MergeModule" { } "ProjectOutput" { "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_B02CDD54271647E084EBF14CA126EEFB" { "SourcePath" = "8:..\\..\\..\\build\\SDL2\\Simutrans SDL2 Nightly.exe" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_52BA1BC52411470C9B09D5CD28B62FAE" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" "ProjectOutputGroupRegister" = "3:1" "OutputConfiguration" = "8:" "OutputGroupCanonicalName" = "8:Built" "OutputProjectGuid" = "8:{E74757E8-C2FD-44AD-87BD-3D55F4709484}" "ShowKeyOutput" = "11:TRUE" "ExcludeFilters" { } } } } } simutrans-124.5/src/Windows/Simutrans.manifest000066400000000000000000000016361520675460500215300ustar00rootroot00000000000000 true/PM simutrans-124.5/src/Windows/nsis/000077500000000000000000000000001520675460500167615ustar00rootroot00000000000000simutrans-124.5/src/Windows/nsis/CC-BY-SA.txt000066400000000000000000000377161520675460500206360ustar00rootroot00000000000000Simutrans is copyright (C) 1997-2004 by Hansjoerg Malthaner. (c) 2004-2007 Simutrans Team >>>>>>>>>>> NO WARRANTIES, EXPRESS OR IMPLIED. <<<<<<<<<<< Have lots of fun playing Simutrans! The following pak is under CC-BY-SA: - pak192 comic - pak48.bitlit ----------------------------------------------------------------------------- Creative Commons Attribution-ShareAlike 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. k. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. l. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. m. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. b. ShareAlike. In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. simutrans-124.5/src/Windows/nsis/GPL.txt000066400000000000000000001052431520675460500201510ustar00rootroot00000000000000Simutrans is copyright (C) 1997-2004 by Hansjoerg Malthaner. (c) 2004-2007 Simutrans Team >>>>>>>>>>> NO WARRANTIES, EXPRESS OR IMPLIED. <<<<<<<<<<< Have lots of fun playing Simutrans! The following paks are under GPL: - pak64 HO-scale (lost) - pak64 contrast ----------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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, either version 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . simutrans-124.5/src/Windows/nsis/installericon256.bmp000066400000000000000000000100221520675460500225570ustar00rootroot00000000000000BM6(9 !!)19BBJRZZ!Z!c9199!9BJBkks)19B1!9!B!)9B9c!!!)!!1!!9!!B!!J!!))!1)!9)!B)!J)!cs!!!)))1))9))B))J))91)B1)J1)R1)1J))111911R11Z11B91J91R91Z91ZB1111999RB9ZB9cB9cJ9kJ999cBBZJBcJBkJBkRBsRBZkBBBBBBJJJcRJkRJkZJsZJccJ{cJJZRRkZRsZR{ZRscR{cRkRsRRRRkcZscZ{kZkZssZ{sZsZsZ{ZZZΥZ֭ZޭZZ{kc{scsc{c{ccccƜcΥcޭccckkk{sksksk{k{kkkkkkss{s{ssss֭ssss{{{{{{{{{έ{{{{޵ƄƄ罌ƌƌƌΌΌֵƔΔεƜΜ֜ƭƥΥΥ֥֥έέ֭֭ޭƵεֵֵ޵ƽνֽֽֽ޽޽$*V#]qrru $*O#qrru H==3& %**>#*qrru3H =3&&'&&++*.#I qruu&=H=33&'-889BB98332*#Ih qruus&3H< 223,,--889BBBCJ LLLRJIA@=21 #=Qfhhh qrruu ߹P&3H <1 #*2379BBBCJLLLRT^_`ffhhh qrru f 3=H <1#7CJLLRT^_`ffhhh qrru Ύ'3=H <1#8CJLLLRT^_`fh qrru a ܥC'32<<<1 #8CJLLLR^__`fhhh qruu ݱU'-,321 #9CJLLLR^_`fhhh qruu o\\o پr-'-883*#9CJLLLR^_`ffhhh qrruu d ܺg-8897*#BCJLLLR e~f^_`ffhhh qrruu ֻv9889BBBCJLLRT^ phffhhh qrru\  ȿS999BBBCJLLR^p^ _``q qrru\ bCJLLLR Tfp__`fhhh~rrru\   vJLLLR^ p~ffhhhq :  ^LRT^ _```pqrruu w^RT^_`ffhhh~  ϴ b^_`ffhhh q  k`ffhhh qrruV  ϳ whqrrrut   xrqrruuz   ¸yzW|!    |   ɟ  l .   lE   l   X   X     X NXN  l N  l N FNN  lĝXNF  lXN F;;;F  lXN F;  lXNF; /;;  lXNF;/;;  lXN F ;   lXNF ! lX N F  " l XN  # l X N   % lX &l X-'l.(l0* l1+ 3- 4/ 61 83 :6 =8?< C@ Gsimutrans-124.5/src/Windows/nsis/languages.nsh000066400000000000000000000023741520675460500214470ustar00rootroot00000000000000 !insertmacro MUI_LANGUAGE "English" ;first language is the default language !insertmacro MUI_LANGUAGE "French" !insertmacro MUI_LANGUAGE "German" !insertmacro MUI_LANGUAGE "Spanish" !insertmacro MUI_LANGUAGE "SpanishInternational" !insertmacro MUI_LANGUAGE "SimpChinese" !insertmacro MUI_LANGUAGE "TradChinese" !insertmacro MUI_LANGUAGE "Japanese" !insertmacro MUI_LANGUAGE "Italian" !insertmacro MUI_LANGUAGE "Dutch" !insertmacro MUI_LANGUAGE "Danish" !insertmacro MUI_LANGUAGE "Swedish" !insertmacro MUI_LANGUAGE "Norwegian" !insertmacro MUI_LANGUAGE "NorwegianNynorsk" !insertmacro MUI_LANGUAGE "Finnish" !insertmacro MUI_LANGUAGE "Greek" !insertmacro MUI_LANGUAGE "Russian" !insertmacro MUI_LANGUAGE "Portuguese" !insertmacro MUI_LANGUAGE "PortugueseBR" !insertmacro MUI_LANGUAGE "Polish" !insertmacro MUI_LANGUAGE "Czech" !insertmacro MUI_LANGUAGE "Slovak" !insertmacro MUI_LANGUAGE "Croatian" !insertmacro MUI_LANGUAGE "Bulgarian" !insertmacro MUI_LANGUAGE "Hungarian" !insertmacro MUI_LANGUAGE "Romanian" !insertmacro MUI_LANGUAGE "Latvian" !insertmacro MUI_LANGUAGE "Estonian" !insertmacro MUI_LANGUAGE "Turkish" !insertmacro MUI_LANGUAGE "Lithuanian" !insertmacro MUI_LANGUAGE "Indonesian" !insertmacro MUI_LANGUAGE "Belarusian" !insertmacro MUI_LANGUAGE "Catalan" simutrans-124.5/src/Windows/nsis/license.rtf000066400000000000000000000224711520675460500211260ustar00rootroot00000000000000{\rtf1\ansi\ansicpg1252\uc1 \deff0\deflang1033\deflangfe1031{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} {\f17\fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}MS Mincho;}{\f91\froman\fcharset238\fprq2 Times New Roman CE;}{\f92\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f94\froman\fcharset161\fprq2 Times New Roman Greek;} {\f95\froman\fcharset162\fprq2 Times New Roman Tur;}{\f96\froman\fcharset186\fprq2 Times New Roman Baltic;}{\f97\fswiss\fcharset238\fprq2 Arial CE;}{\f98\fswiss\fcharset204\fprq2 Arial Cyr;}{\f100\fswiss\fcharset161\fprq2 Arial Greek;} {\f101\fswiss\fcharset162\fprq2 Arial Tur;}{\f102\fswiss\fcharset186\fprq2 Arial Baltic;}{\f195\fmodern\fcharset0\fprq1 MS Mincho;}{\f193\fmodern\fcharset238\fprq1 MS Mincho CE;}{\f194\fmodern\fcharset204\fprq1 MS Mincho Cyr;} {\f196\fmodern\fcharset161\fprq1 MS Mincho Greek;}{\f197\fmodern\fcharset162\fprq1 MS Mincho Tur;}{\f198\fmodern\fcharset186\fprq1 MS Mincho Baltic;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0; \red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128; \red192\green192\blue192;}{\stylesheet{\nowidctlpar\adjustright \lang1031 \snext0 Normal;}{\s1\sb240\sa120\keepn\nowidctlpar\adjustright \b\fs32\lang1031 \sbasedon15 \snext16 heading 1;}{\s2\sb240\sa120\keepn\nowidctlpar\adjustright \b\i\fs28\lang1031 \sbasedon15 \snext16 heading 2;}{\*\cs10 \additive Default Paragraph Font;}{\s15\sb240\sa120\keepn\nowidctlpar\adjustright \fs28\lang1031 \sbasedon0 \snext16 Heading;}{\s16\sa120\nowidctlpar\adjustright \lang1031 \sbasedon0 \snext16 Body Text;}{ \s17\sa120\nowidctlpar\adjustright \lang1031 \sbasedon16 \snext17 List;}{\s18\sb120\sa120\nowidctlpar\adjustright \i\lang1031 \sbasedon0 \snext18 caption;}{\s19\nowidctlpar\adjustright \lang1031 \sbasedon0 \snext19 Index;}{ \s20\sa120\nowidctlpar\adjustright \lang1031 \sbasedon16 \snext20 Frame contents;}}{\info{\author Markus Pristovsek}{\operator Markus Pristovsek}{\creatim\yr2009\mo9\dy12\hr23\min14}{\revtim\yr2009\mo9\dy12\hr23\min20}{\version2}{\edmins0}{\nofpages3} {\nofwords786}{\nofchars4482}{\*\company }{\nofcharsws5504}{\vern73}}\paperw11905\paperh16837\margl1134\margr1134\margt1134\margb1134 \deftab709\widowctrl\ftnbj\aenddoc\hyphhotz425\hyphcaps0\viewkind4\viewscale100 \fet0\sectd \sbknone\linex0\headery709\footery709\colsx709\sectdefaultcl {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl4 \pnlcltr\pnstart1\pnindent720\pnhang{\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (} {\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}\pard\plain \nowidctlpar\adjustright \lang1031 {This is the Simutrans source code. \par (c) 1997-2003 Hans Joerg Malthaner \par (c) since 2004 The Simutrans Team \par \par Simutrans source code is provided by the current Simutrans team (team64@simutrans.com) as copyright holder under the Artistic License. \par \par This includes the translations from: \par http://translator.simutrans.com/ \par the midi files and the fonts with the exception of the m+10r.bdf, which is taken from the M+ font project: \par http://mplus-fonts.sourceforge.jp/ \par \par \par \par }\pard\plain \s1\sb240\sa120\keepn\nowidctlpar\outlinelevel0\adjustright \b\fs32\lang1031 {\hich\af1\dbch\af17\loch\f1 The Artistic License \par }\pard\plain \nowidctlpar\adjustright \lang1031 { \par }\pard\plain \s2\fi-578\li578\sb240\sa120\keepn\nowidctlpar\outlinelevel1\adjustright \b\i\fs28\lang1031 {\hich\af1\dbch\af17\loch\f1 Preamble \par }\pard\plain \nowidctlpar\adjustright \lang1031 { \par The inten t of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. \par \par Definitions: \par \par * "Package" refers to the collection of files distributed by the \par Copyright Holder, and derivatives of that collection of files \par created through textual modification. \par * "Standard Version" refers to such a Package if it has not been \par modified, or has been modified in accordance with the wishes \par of the Copyright Holder. \par * "Copyright Holder" is whoever is named in the copyright or \par copyrights for the package. \par * "You" is you, if you're thinking about copying or distributing \par this Package. \par * "Reasonable copying fee" is whatever you can justify on the \par basis of media cost, duplication charges, time of people involved, \par and so on. (You will not be required to justify it to the \par Copyright Holder, but only to the computing community at large \par as a market that must bear the fee.) \par * "Freely Available" means that no fee is charged for the item \par itself, though there may be fees involved in handling the item. \par It also means that recipients of the item may redistribute it \par under the same conditions they received it. \par \par 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. \par \par 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. \par \par 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: \par \par a) place your modifications in the Public Domain or otherwise make them \par Freely Available, such as by posting said modifications to Usenet or \par an equivalent medium, or placing the modifications on a major archive \par site such as ftp.uu.net, or by allowing the Copyright Holder to include \par your modifications in the Standard Version of the Package. \par \par b) use the modified Package only within your corporation or organization. \par \par c) rename any non-standard executables so the names do not conflict \par with standard executables, which must also be provided, and provide \par a separate manual page for each non-standard executable that clearly \par documents how it differs from the Standard Version. \par \par d) make other distribution arrangements with the Copyright Holder. \par \par 4. You may distribute the programs of this Package in object code or \par executable form, provided that you do at least ONE of the following: \par \par a) distribute a Standard Version of the executables and library files, \par together with instructions (in the manual page or equivalent) on where \par to get the Standard Version. \par \par b) accompany the distribution with the machine-readable source of \par the Package with your modifications. \par \par c) accompany any non-standard executables with their corresponding \par Standard Version executables, giving the non-standard executables \par non-standard names, and clearly documenting the differences in manual \par pages (or equivalent), together with instructions on where to get \par the Standard Version. \par \par d) make other distribution arrangements with the Copyright Holder. \par \par 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly comm ercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. \par \par 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. \par \par 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. \par \par 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. \par \par 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. \par \par }} simutrans-124.5/src/Windows/nsis/onlineupgrade.nsi000066400000000000000000000063561520675460500223420ustar00rootroot00000000000000; ; This file is part of the Simutrans project under the Artistic License. ; (see LICENSE.txt) ; ; ************************************* Pakset downloader for simutrans ********************************************* ; needs the following plugins: ; nsisunz ; inetc ; CabX ; untgz ; ShellLink Unicode true !define PAKSETINSTALL 1 !include "preparation-functions.nsh" !include "languages.nsh" RequestExecutionLevel user !define MULTIUSER_INSTALLMODE_INSTDIR "Simutrans" !define MULTIUSER_EXECUTIONLEVEL Standard Name "Simutrans Pakset Installer for 124.0" OutFile "download-paksets.exe" ; The default installation directory InstallDir $PROGRAMFILES\Simutrans !include "paksets.nsh" ;************************** from here on other helper stuff ***************** !include "other-functions.nsh" ;********************* from here on special own helper functions ************ ; make sure, at least one executable is installed Function .onSelChange ; Make sure at least some pak is selected SectionGetFlags ${pak} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak64.german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakHAJO} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak.japan} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak.nippon} $R0 IntCmp $R0 ${SF_SELECTED} show_not ; SectionGetFlags ${pak64.ho-scale} $R0 ; IntOp $R0 $R0 & ${SF_SELECTED} ; IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakcontrast} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak64.scifi} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak96.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakHD} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.japan} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.britain} $R0 IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${PAK128.german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.CS} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak192.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak144.Excentrique} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak48.Excentrique} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak32} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakTTD} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak48.bitlit} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not ; not pak selected! MessageBox MB_OK|MB_ICONSTOP "At least on pak set must be selected!" /SD IDOK SetErrorLevel 4 show_not: FunctionEnd simutrans-124.5/src/Windows/nsis/other-functions.nsh000066400000000000000000000453021520675460500226260ustar00rootroot00000000000000; ; This file is part of the Simutrans project under the Artistic License. ; (see LICENSE.txt) ; ;***************************************************** from here on come licence ***************************************** !include "TextFunc.nsh" ; Usage ... ; Push "|" ;divider char ; Push "string1|string2|string3|string4|string5" ;input string ; Call SplitFirstStrPart ; Pop $R0 ;1st part ["string1"] ; Pop $R1 ;rest ["string2|string3|string4|string5"] Function SplitFirstStrPart Exch $R0 Exch Exch $R1 Push $R2 Push $R3 StrCpy $R3 $R1 StrLen $R1 $R0 IntOp $R1 $R1 + 1 loop: IntOp $R1 $R1 - 1 StrCpy $R2 $R0 1 -$R1 StrCmp $R1 0 exit0 StrCmp $R2 $R3 exit1 loop exit0: StrCpy $R1 "" Goto exit2 exit1: IntOp $R1 $R1 - 1 StrCmp $R1 0 0 +3 StrCpy $R2 "" Goto +2 StrCpy $R2 $R0 "" -$R1 IntOp $R1 $R1 + 1 StrCpy $R0 $R0 -$R1 StrCpy $R1 $R2 exit2: Pop $R3 Pop $R2 Exch $R1 ;rest Exch Exch $R0 ;first FunctionEnd ; we just ask for Artistic licences, the other are only asked for certain paks PageEx license LicenseData "license.rtf" PageExEnd ; If not installed to program dir, ask for a portable installation Function CheckForPortableInstall ; defaults in progdir, and ending with simutrans StrCpy $installinsimutransfolder "1" StrCpy $multiuserinstall "1" ; if the destination directory is the program dir, we must use use own documents directory for data StrCmp $INSTDIR $PROGRAMFILES\simutrans FinishSetPortable StrCmp $INSTDIR $PROGRAMFILES64\simutrans FinishSetPortable StrCmp $INSTDIR $USERDIR FinishSetPortable StrCpy $multiuserinstall "0" ; check whether we already have a simuconf.tab, to get state from file IfFileExists "$INSTDIR\config\simuconf.tab" 0 PortableUnknown ; now we have a config. Without single_user install, it will be multiuser StrCpy $multiuserinstall "1" ${ConfigRead} "$INSTDIR\config\simuconf.tab" "singleuser_install" $R0 IfErrors PortableUnknown ; skip a leading space PortableSpaceSkip: StrCpy $1 $R0 1 StrCmp " " $1 0 +3 StrCpy $R0 $R0 100 1 Goto PortableSpaceSkip ; skip the equal char StrCpy $R0 $R0 10 1 IntOp $multiuserinstall $R0 ^ 1 Goto FinishSetPortable PortableUnknown: ; ask whether this is a portable installation ; check if there are paksets in the current folder FindFirst $R0 $R1 "$INSTDIR\pak*" IfErrors 0 YesPortable MessageBox MB_YESNO|MB_ICONINFORMATION "Should this be a portable installation?" /SD IDNO IDYES YesPortable StrCpy $multiuserinstall "1" Goto FinishSetPortable YesPortable: ; Messagebox MB_OK "singleuser" StrCpy $PAKDIR $INSTDIR StrCpy $multiuserinstall "0" Goto FinishSetPortable NoPortable: Messagebox MB_OK "Multiuser" FinishSetPortable: ; now check, whether the path ends with "simutrans" StrCpy $0 $INSTDIR 9 -9 StrCmp $0 simutrans +2 StrCpy $installinsimutransfolder "0" ; here everything is ok ; MessageBox MB_OK "$INSTDIR $PAKDIR" FunctionEnd !ifndef PAKSETINSTALL PageEx directory PageCallbacks "" "" CheckForPortableInstall PageExEnd !endif Page custom MovePre MoveLeave "Moving paks to ProgramData" ; only show this page, if there are old paks to move ... Function MovePre StrCmp $PAKDIR $INSTDIR 0 +2 Abort StrCmp $multiuserinstall "1" +2 Abort SetShellVarContext all StrCmp $PAKDIR "$LOCALAPPDATA\simutrans" +3 SetShellVarContext current Abort SetShellVarContext current ; find at least on pak to move? nsDialogs::Create 1018 Pop $0 ${NSD_CreateLabel} 0 0 100% 12u "Moving paks to ProgramData (slow)" Pop $0 IntOp $9 0 + 13 FindFirst $R0 $R1 "$INSTDIR\pak*" IfErrors 0 LoopIt Abort LoopIt: ${NSD_CreateLabel} 0 $9 100% 12u "$R1" POP $0 IntOp $9 $9 + 13 ; Messagebox MB_OK "Do some processing on $R1" FindNext $R0 $R1 IfErrors 0 LoopIt nsDialogs::Show FunctionEnd Function MoveLeave StrCmp $PAKDIR $INSTDIR0 0 +2 Abort StrCmp $multiuserinstall "1" +2 Abort FindFirst $R0 $R1 "$INSTDIR\pak*" IfErrors 0 MoveIt Abort MoveIt: CreateDirectory "$PAKDIR\$R1" CopyFiles "$INSTDIR\$R1" "$PAKDIR" RMdir /r "$INSTDIR\$R1" FindNext $R0 $R1 IfErrors 0 MoveIt ExecWait 'Icacls "$PAKDIR" /grant Users:(OI)(CI)M' FunctionEnd PageEx components PageCallbacks componentsPre PageExEnd ; If for the pak in this section exists, it will be preselected Function EnableSectionIfThere Exch $R0 Push $R1 SectionGetText $R0 $R1 ; now only use the part until the space ... Push " " Push $R1 Call SplitFirstStrPart Pop $R1 IfFileExists "$PAKDIR\$R1\ground.Outside.pak" 0 NotExistingPakNotSelected SectionGetFlags $R0 $R1 IntOp $R1 $R1 | ${SF_SELECTED} SectionSetFlags $R0 $R1 NotExistingPakNotSelected: Pop $R1 Exch $R0 FunctionEnd ; set pak for update/skip if installed Function componentsPre Push ${pak} Call EnableSectionIfThere Push ${pak64.german} Call EnableSectionIfThere Push ${pak.japan} Call EnableSectionIfThere Push ${pak.nippon} Call EnableSectionIfThere ; Push ${pak64.ho-scale} ; Call EnableSectionIfThere Push ${pakHAJO} Call EnableSectionIfThere Push ${pakcontrast} Call EnableSectionIfThere Push ${pak64.scifi} Call EnableSectionIfThere Push ${pak96.comic} Call EnableSectionIfThere Push ${pakHD} Call EnableSectionIfThere Push ${pak128} Call EnableSectionIfThere Push ${pak128.Britain} Call EnableSectionIfThere Push ${PAK128.german} Call EnableSectionIfThere Push ${pak128.japan} Call EnableSectionIfThere Push ${pak128.CS} Call EnableSectionIfThere Push ${pak144.Excentrique} Call EnableSectionIfThere Push ${pak192.comic} Call EnableSectionIfThere Push ${pak64.scifi} Call EnableSectionIfThere Push ${pak48.Excentrique} Call EnableSectionIfThere Push ${pak32} Call EnableSectionIfThere Push ${pakTTD} Call EnableSectionIfThere Push ${pak48.bitlit} Call EnableSectionIfThere FunctionEnd ; Some paksets don't have an open source license, so we have to show additional licences Function CheckForClosedSource SectionGetFlags ${pak64.german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showFW SectionGetFlags ${pakHAJO} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showFW SectionGetFlags ${pak.nippon} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showFW SectionGetFlags ${pak96.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showFW SectionGetFlags ${pakHD} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showFW SectionGetFlags ${pak128.japan} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showFW SectionGetFlags ${PAK128.german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showFW SectionGetFlags ${pak192.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showFW Abort showFW: ; here is ok FunctionEnd PageEx License LicenseData "pak128.txt" PageCallbacks CheckForClosedSource "" "" PageExEnd ; Some packs are GPL Function CheckForGPL ; SectionGetFlags ${pak64.ho-scale} $R0 ; IntOp $R0 $R0 & ${SF_SELECTED} ; IntCmp $R0 ${SF_SELECTED} showGPL SectionGetFlags ${pakcontrast} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showGPL Abort showGPL: ; here is ok FunctionEnd PageEx License LicenseData "GPL.txt" PageCallbacks CheckForGPL "" "" PageExEnd ; Some pak192.comic is CC Function CheckForCC SectionGetFlags ${pak192.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showCC SectionGetFlags ${pak48.bitlit} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} showCC Abort showCC: ; here is ok FunctionEnd PageEx License LicenseData "CC-BY-SA.txt" PageCallbacks CheckForCC "" "" PageExEnd PageEx instfiles PageExEnd ; ******************************** From here on Functions *************************** Function .oninit StrCpy $USERDIR "$LOCALAPPDATA\simutrans" SetShellVarContext all StrCpy $PAKDIR "$LOCALAPPDATA\simutrans" SetShellVarContext current !insertmacro MULTIUSER_INIT StrCpy $9 ${SDLexe} ;The default for radiobutton InitPluginsDir StrCpy $multiuserinstall "1" ; avoids two instance at the same time ... System::Call 'kernel32::CreateMutexA(i 0, i 0, t "SimutransMutex") i .r1 ?e' Pop $R0 StrCmp $R0 0 +3 MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." /SD IDOK Abort ; now find out, whether there is an old installation IfFileExists "$SMPROGRAMS\Simutrans\Simutrans.lnk" 0 no_previous_menu ShellLink::GetShortCutTarget "$SMPROGRAMS\Simutrans\Simutrans.lnk" Pop $0 StrCpy $INSTDIR $0 -14 ; now check for portable or not goto init_path_ok no_previous_menu: ;# call userInfo plugin to get user info. The plugin puts the result in the stack userInfo::getAccountType pop $0 ; compare the result with the string "Admin" to see if the user is admin. ; If match, jump 3 lines down. strCmp $0 "Admin" +3 ; we are not admin: default install in a different dir StrCpy $INSTDIR $USERDIR ; ok, we are admin init_path_ok: !ifdef PAKSETINSTALL Call CheckForPortableInstall !endif FunctionEnd ; ConnectInternet (uses Dialer plug-in) ; Written by Joost Verburg ; ; This function attempts to make a connection to the internet if there is no ; connection available. If you are not sure that a system using the installer ; has an active internet connection, call this function before downloading ; files with NSISdl. ; ; The function requires Internet Explorer 3, but asks to connect manually if ; IE3 is not installed. Function ConnectInternet Push $R0 ClearErrors Dialer::AttemptConnect IfErrors noie3 Pop $R0 StrCmp $R0 "online" connected MessageBox MB_OK|MB_ICONSTOP "Cannot connect to the internet." /SD IDOK Quit ;This will quit the installer. You might want to add your own error handling. noie3: ; IE3 not installed MessageBox MB_OK|MB_ICONINFORMATION "Please connect to the internet now." /SD IDOK connected: Pop $R0 FunctionEnd Function IsPakInstalledAndCurrent IntOp $R0 0 + 0 IfFileExists "$PAKDIR\$downloadname\ground.Outside.pak" +1 PakNotThere IntOp $R0 1 | 1 ; now we have outside.pak and it should have a valid number FileOpen $0 "$PAKDIR\$downloadname\ground.Outside.pak" r FileSeek $0 101 FileRead $0 $R1 FileClose $0 StrCmp $VersionString $R1 0 PakThereButOld ; now we are current IntOp $R0 2 | 2 goto PakNotThere PakThereButOld: DetailPrint "Old pak has version $R1 but current is $VersionString" PakNotThere: FunctionEnd ; $downloadlink is then name of the link, $downloadname the name of the pak for error messages Function DownloadInstallZip Call IsPakInstalledAndCurrent IntCmp $R0 2 DownloadInstallZipSkipped IntCmp $R0 0 DownloadInstallZipDo RMdir /r "$downloadname.old" Rename "$downloadname" "$downloadname.old" DetailPrint "Old $downloadname renamed to $downloadname.old" DownloadInstallZipDo: ; ok old directory rename Call ConnectInternet RMdir /r "tmp" ; since https fails with builtin NSISdl ... ; NSISdl::download $downloadlink "$TEMP\$archievename" ; Pop $R0 ;Get the return value ; StrCmp $R0 "success" +3 ; MessageBox MB_OK "Download of $archievename failed: $R0" ; Quit ; /WEAKSECURITY no working without admin ... inetc::get $downloadlink "$TEMP\$archievename" /END Pop $0 StrCmp $0 "OK" +3 MessageBox MB_OK "Download of $archievename failed: $R0" /SD IDOK Quit ; remove all old files before! RMdir /r "$OUTDIR\$downloadname" ; we need the magic with temporary copy only if the folder does not end with simutrans ... StrCmp $installinsimutransfolder "0" +4 nsisunz::Unzip "$TEMP\$archievename" "$OUTDIR\.." goto +2 nsisunz::Unzip "$TEMP\$archievename" "$TEMP" Pop $R0 ;Get the return value StrCmp $R0 "success" +4 MessageBox MB_OK|MB_ICONINFORMATION "$R0" /SD IDOK RMdir /r "$TEMP\simutrans" Quit Delete "$Temp\$archievename" StrCmp $installinsimutransfolder "1" +3 CreateDirectory "$OUTDIR" CopyFiles /silent "$TEMP\Simutrans\*.*" "$OUTDIR" RMdir /r "$TEMP\simutrans" DownloadInstallZipSkipped: FunctionEnd ; $downloadlink is then name of the link, $downloadname the name of the pak for error messages Function DownloadInstallNoRemoveZip Call ConnectInternet RMdir /r "$TEMP\simutrans" ; since https fails with builtin NSISdl ... ; NSISdl::download $downloadlink "$TEMP\$archievename" ; Pop $R0 ;Get the return value ; StrCmp $R0 "success" +3 ; MessageBox MB_OK "Download of $archievename failed: $R0" ; Quit inetc::get $downloadlink "$Temp\$archievename" /END Pop $0 StrCmp $0 "OK" +4 MessageBox MB_OK "Download of $archievename failed: $R0" /SD IDOK SetErrorLevel 3 Quit ; we need the magic with temporary copy only if the folder does not end with simutrans ... StrCmp $installinsimutransfolder "0" +4 nsisunz::Unzip "$TEMP\$archievename" "$OUTDIR\.." goto +2 nsisunz::Unzip "$TEMP\$archievename" "$TEMP" Pop $R0 ;Get the return value StrCmp $R0 "success" +5 MessageBox MB_OK|MB_ICONINFORMATION "$R0" /SD IDOK RMdir /r "$TEMP\simutrans" SetErrorLevel 5 Quit Delete "$Temp\$archievename" StrCmp $installinsimutransfolder "1" +3 CreateDirectory "$OUTDIR" CopyFiles /silent "$TEMP\Simutrans\*.*" "$OUTDIR" RMdir /r "$TEMP\simutrans" FunctionEnd ; $downloadlink is then name of the link, $downloadname the name of the pak for error messages Function DownloadInstallAddonZip # DetailPrint "Download of $downloadname from\n$downloadlink to $archievename" Call ConnectInternet RMdir /r "$TEMP\simutrans" ; since https fails with builtin NSISdl ... ; NSISdl::download $downloadlink "$TEMP\$archievename" ; Pop $R0 ;Get the return value ; StrCmp $R0 "success" +3 ; MessageBox MB_OK "Download of $archievename failed: $R0" ; Quit inetc::get $downloadlink "$Temp\$archievename" /END Pop $0 StrCmp $0 "OK" +4 MessageBox MB_OK "Download of $archievename failed: $R0" /SD IDOK SetErrorLevel 3 Quit nsisunz::Unzip "$TEMP\$archievename" "$DOCUMENTS" Pop $R0 ;Get the return value StrCmp $R0 "success" +5 DetailPrint "$0" ;print error message to log Delete "$TEMP\$archievename" SetErrorLevel 5 Quit Delete "$Temp\$archievename" FunctionEnd ; $downloadlink is then name of the link, $downloadname the name of the pak for error messages Function DownloadInstallAddonZipPortable # DetailPrint "Download of $downloadname from\n$downloadlink to $archievename" Call ConnectInternet RMdir /r "$TEMP\simutrans" ; since https fails with builtin NSISdl ... ; NSISdl::download $downloadlink "$TEMP\$archievename" ; Pop $R0 ;Get the return value ; StrCmp $R0 "success" +3 ; MessageBox MB_OK "Download of $archievename failed: $R0" ; Quit inetc::get $downloadlink "$Temp\$archievename" /END Pop $0 StrCmp $0 "OK" +4 MessageBox MB_OK "Download of $archievename failed: $R0" /SD IDOK SetErrorLevel 3 Quit nsisunz::Unzip "$TEMP\$archievename" "$OUTDIR\.." Pop $R0 ;Get the return value StrCmp $R0 "success" +5 DetailPrint "$0" ;print error message to log Delete "$TEMP\$archievename" SetErrorLevel 5 Quit Delete "$Temp\$archievename" FunctionEnd ; $downloadlink is then name of the link, $downloadname the name of the pak for error messages Function DownloadInstallZipWithoutSimutrans Call IsPakInstalledAndCurrent IntCmp $R0 2 DownloadInstallZipWithoutSimutransSkip IntCmp $R0 0 DownloadInstallZipWithoutSimutransDo SetOutPath $OUTDIR RMdir /r "$downloadname.old" Rename "$downloadname" "$downloadname.old" DetailPrint "Old $downloadname renamed to $downloadname.old" DownloadInstallZipWithoutSimutransDo: ; ok, now install DetailPrint "Download of $downloadname from\n$downloadlink to $archievename" Call ConnectInternet RMdir /r "$TEMP\simutrans" CreateDirectory "$TEMP\simutrans" # since we also want to download from addons ... inetc::get $downloadlink "$Temp\simutrans\$archievename" /END POP $0 StrCmp $0 "OK" +4 MessageBox MB_OK "Download of $archievename failed: $R0" /SD IDOK SetErrorLevel 3 Quit ; remove all old files before! RMdir /r "$OUTDIR\$downloadname" CreateDirectory "$OUTDIR" DetailPrint "Unzip $archievename to $OUTDIR" nsisunz::Unzip "$TEMP\simutrans\$archievename" "$OUTDIR" Pop $R0 StrCmp $R0 "success" +5 Delete "$Temp\simutrans\$archievename" DetailPrint "$0" ;print error message to log SetErrorLevel 5 Quit RMdir /r "$TEMP\simutrans" DownloadInstallZipWithoutSimutransSkip: FunctionEnd Function DownloadInstallCabWithoutSimutrans DetailPrint "Download of $downloadname from $downloadlink to $archievename" Call IsPakInstalledAndCurrent IntCmp $R0 2 DownloadInstallCabWithoutSimutransSkip IntCmp $R0 0 DownloadInstallCabWithoutSimutransDo SetOutPath $OUTDIR RMdir /r "$downloadname.old" Rename "$downloadname" "$downloadname.old" DetailPrint "Old $downloadname renamed to $downloadname.old" DownloadInstallCabWithoutSimutransDo: ; ok, needs update RMdir /r "$TEMP\simutrans" CreateDirectory "$TEMP\simutrans" Call ConnectInternet inetc::get $downloadlink "$Temp\simutrans\$archievename" /END POP $0 StrCmp $0 "OK" +5 DetailPrint "Download error $R0" ;print error message to log MessageBox MB_OK "Download of $archievename failed: $R0" /SD IDOK SetErrorLevel 3 Quit ; not supported with Unicode ;CabDLL::CabView "$TEMP\$archievename" DetailPrint "Install of $archievename to $OUTDIR" CreateDirectory "$OUTDIR\$downloadname" CreateDirectory "$OUTDIR\$downloadname\config" CreateDirectory "$OUTDIR\$downloadname\sound" CreateDirectory "$OUTDIR\$downloadname\text" CabX::FromFile "" "$TEMP\simutrans\$archievename" "$OUTDIR" ; for ANSI installer ;CabDLL::CabExtractAll "$TEMP\$archievename" "$OUTDIR" StrCmp $R0 "0" +6 DetailPrint "$0" ;print error message to log RMdir /r "$TEMP\simutrans" MessageBox MB_OK "Could not extract $TEMP\simutrans\$archievename" SetErrorLevel 5 Quit RMdir /r "$TEMP\simutrans" DownloadInstallCabWithoutSimutransSkip: FunctionEnd Function DownloadInstallTgzWithoutSimutrans # DetailPrint "Download of $downloadname from\n$downloadlink to $archievename" Call ConnectInternet RMdir /r "$TEMP\simutrans" CreateDirectory "$TEMP\simutrans" inetc::get $downloadlink "$Temp\simutrans\$archievename" /END POP $0 StrCmp $0 "OK" +5 DetailPrint "Download error $R0" ;print error message to log MessageBox MB_OK "Download of $archievename failed: $R0" /SD IDOK SetErrorLevel 3 Quit CreateDirectory "$OUTDIR" ; remove all old files before! RMdir /r "$OUTDIR\$downloadname" untgz::extract -d "$OUTDIR" "$TEMP\simutrans\$archievename" StrCmp $R0 "success" +4 RMdir /r "$TEMP\simutrans" MessageBox MB_OK "Extraction of $archievename failed: $R0" /SD IDOK SetErrorLevel 5 Quit RMdir /r "$TEMP\simutrans" FunctionEnd simutrans-124.5/src/Windows/nsis/pak128.txt000066400000000000000000000006761520675460500205410ustar00rootroot00000000000000Simutrans is copyright (C) 1997-2004 by Hansjoerg Malthaner. (c) 2004-2007 Simutrans Team >>>>>>>>>>> NO WARRANTIES, EXPRESS OR IMPLIED. <<<<<<<<<<< Have lots of fun playing Simutrans! The following paks are not OpenSource but Freeware. - pak64.german - pak64.HAJO - pak96.comic - pak128.german - pak192.comic - pak128 japan (has also sources available.) Commercial distribution without written permission of the author(s) is not permitted. simutrans-124.5/src/Windows/nsis/paksets.nsh000066400000000000000000000176371520675460500211630ustar00rootroot00000000000000; Generated by "get_pak.sh -generate_h". DO NOT EDIT. SectionGroup /e "Pak64: main and addons" pak64group Section /o "pak" pak AddSize 16464 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak64/124-4/simupak64-124-4.zip" SetOutPath $PAKDIR StrCpy $archievename "simupak64-124-4.zip" StrCpy $downloadname "pak" StrCpy $VersionString "pak64 124.4 r2223" Call DownloadInstallZip SectionEnd Section /o "pak64 Food addon" AddSize 228 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak64/121-0/simupak64-addon-food-120-4.zip" StrCpy $archievename "simupak64-addon-food-120-4.zip" StrCpy $downloadname "pak" StrCpy $VersionString "" StrCmp $multiuserinstall "1" +3 ; no multiuser => install in normal directory Call DownloadInstallAddonZipPortable goto +2 Call DownloadInstallAddonZip SectionEnd SectionGroupEnd Section /o "pak128" pak128 AddSize 415900 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak128/pak128%20for%20ST%20124.4up%20%282.10.1%29/simupak128-2-10-1-for124-4up.zip" SetOutPath $PAKDIR StrCpy $archievename "simupak128-2-10-1-for124-4up.zip" StrCpy $downloadname "pak128" StrCpy $VersionString "pak128 2.10.1 for 124.4 git r699 hash 6dccf5d4" Call DownloadInstallZip SectionEnd Section /o "pak192.comic" pak192.comic AddSize 909748 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak192.comic/pak192.comic%20V0.7.2/pak192-comic.zip" SetOutPath $PAKDIR StrCpy $archievename "pak192-comic.zip" StrCpy $downloadname "pak192.comic" StrCpy $VersionString "Pak192.Comic V0.7.2 Rev 1296" Call DownloadInstallZipWithoutSimutrans SectionEnd Section /o "pak64.german" pak64.german AddSize 29232 StrCpy $downloadlink "http://simutrans-germany.com/pak.german/pak64.german_0-124-4-0-1_full.zip" SetOutPath $PAKDIR StrCpy $archievename "pak64.german_0-124-4-0-1_full.zip" StrCpy $downloadname "pak64.german" StrCpy $VersionString "pak64.german 0.124.4.0.1" Call DownloadInstallZip SectionEnd Section /o "PAK128.german" PAK128.german AddSize 576132 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/PAK128.german/PAK128.german_2.4_for_ST_124.3.1/PAK128.german_2.4_for_ST_124.3.1.zip" SetOutPath $PAKDIR StrCpy $archievename "PAK128.german_2.4_for_ST_124.3.1.zip" StrCpy $downloadname "PAK128.german" StrCpy $VersionString "Pak128.german VS 2.4 (Rev. 547)" Call DownloadInstallZip SectionEnd Section /o "pak.japan" pak.japan AddSize 10175 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak64.japan/123-0/simupak64.japan-123-0.zip" SetOutPath $PAKDIR StrCpy $archievename "simupak64.japan-123-0.zip" StrCpy $downloadname "pak.japan" StrCpy $VersionString "pak64.japan 123 r2101" Call DownloadInstallZip SectionEnd Section /o "pak.nippon" pak.nippon AddSize 50198 StrCpy $downloadlink "https://github.com/wa-st/pak-nippon/releases/download/v0.6.2/pak.nippon-v0.6.2.zip" SetOutPath $PAKDIR StrCpy $archievename "pak.nippon-v0.6.2.zip" StrCpy $downloadname "pak.nippon" StrCpy $VersionString "pak.nippon v0.6.2" Call DownloadInstallZipWithoutSimutrans SectionEnd Section /o "pak128.CS" pak128.CS AddSize 78628 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/Pak128.CS/nightly%20builds/pak128.CS-r2096.zip" SetOutPath $PAKDIR StrCpy $archievename "pak128.CS-r2096.zip" StrCpy $downloadname "pak128.CS" StrCpy $VersionString "Pak128.CS 0.3.0 r2096" Call DownloadInstallZipWithoutSimutrans SectionEnd Section /o "pak128.Britain" pak128.Britain AddSize 241715 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak128.britain/pak128.Britain%20for%20120-3/pak128.Britain.1.18-120-3.zip" SetOutPath $PAKDIR StrCpy $archievename "pak128.Britain.1.18-120-3.zip" StrCpy $downloadname "pak128.Britain" StrCpy $VersionString "pak128.Britain 1.18 120.3 r1991" Call DownloadInstallZip SectionEnd Section /o "pak144.Excentrique" pak144.Excentrique AddSize 7621 StrCpy $downloadlink "https://github.com/Varkalandar/pak144.Excentrique/releases/download/r0.08/pak144.Excentrique_v008.zip" SetOutPath $PAKDIR StrCpy $archievename "pak144.Excentrique_v008.zip" StrCpy $downloadname "pak144.Excentrique" StrCpy $VersionString "pak144.Excentrique v0.08" Call DownloadInstallZipWithoutSimutrans SectionEnd Section /o "pakTTD" pakTTD AddSize 1995 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pakTTD/simupakTTD-124-0.zip" SetOutPath $PAKDIR StrCpy $archievename "simupakTTD-124-0.zip" StrCpy $downloadname "pakTTD" StrCpy $VersionString "pak64 120.4.1 r2146" Call DownloadInstallZip SectionEnd Section /o "pak48.bitlit" pak48.bitlit AddSize 1762 StrCpy $downloadlink "http://codeberg.org/Nazalassa/pak48.bitlit/releases/download/0.1d/pak48.bitlit_0.1d.zip" SetOutPath $PAKDIR StrCpy $archievename "pak48.bitlit_0.1d.zip" StrCpy $downloadname "pak48.bitlit" StrCpy $VersionString "pak48.bitlit v0.1d" Call DownloadInstallZipWithoutSimutrans SectionEnd ; OBSOLETE PAKS from here SectionGroup /e "Not currently developed" slowPakgroup Section /o "pak96.comic" pak96.comic AddSize 32526 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak96.comic/pak96.comic%20for%20111-3/pak96.comic-0.4.10-plus.zip" SetOutPath $PAKDIR StrCpy $archievename "pak96.comic-0.4.10-plus.zip" StrCpy $downloadname "pak96.comic" StrCpy $VersionString "pak96.comic V4.1 plus" Call DownloadInstallZip SectionEnd Section /o "pak128.japan" pak128.japan AddSize 27780 StrCpy $downloadlink "http://pak128.jpn.org/souko/pak128.japan.120.0.cab" SetOutPath $PAKDIR StrCpy $archievename "pak128.japan.120.0.cab" StrCpy $downloadname "pak128.japan" StrCpy $VersionString "Pak128.Japan 120.0" Call DownloadInstallCabWithoutSimutrans SectionEnd Section /o "pak32" pak32 AddSize 3420 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak32.comic/pak32.comic%20for%20102-0/pak32.comic_102-0.zip" SetOutPath $PAKDIR StrCpy $archievename "pak32.comic_102-0.zip" StrCpy $downloadname "pak32" StrCpy $VersionString "" Call DownloadInstallZip SectionEnd Section /o "pak48.Excentrique" pak48.Excentrique AddSize 2016 StrCpy $downloadlink "https://github.com/Varkalandar/pak48.Excentrique/releases/download/v0.19_RC3/pak48.excentrique_v019rc3.zip" SetOutPath $PAKDIR StrCpy $archievename "pak48.excentrique_v019rc3.zip" StrCpy $downloadname "pak48.Excentrique" StrCpy $VersionString "pak48.Excentrique v0.19" Call DownloadInstallZipWithoutSimutrans SectionEnd Section /o "pakcontrast" pakcontrast AddSize 1440 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak64.contrast/pak64.Contrast_910.zip" SetOutPath $PAKDIR StrCpy $archievename "pak64.Contrast_910.zip" StrCpy $downloadname "pakcontrast" StrCpy $VersionString "" Call DownloadInstallZipWithoutSimutrans SectionEnd Section /o "pakHD" pakHD AddSize 12511 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/OldFiles/PakHD_v04B_100-0.zip" SetOutPath $PAKDIR StrCpy $archievename "PakHD_v04B_100-0.zip" StrCpy $downloadname "pakHD" StrCpy $VersionString "Martin" Call DownloadInstallZip SectionEnd Section /o "pakHAJO" pakHAJO AddSize 7460 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pakHAJO/pakHAJO_102-2-2/pakHAJO_0-102-2-2.zip" SetOutPath $PAKDIR StrCpy $archievename "pakHAJO_0-102-2-2.zip" StrCpy $downloadname "pakHAJO" StrCpy $VersionString "" Call DownloadInstallZip SectionEnd Section /o "pak64.scifi" pak64.scifi AddSize 3293 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/pak64.scifi/pak64.scifi_112.x_v0.2.zip" SetOutPath $PAKDIR StrCpy $archievename "pak64.scifi_112.x_v0.2.zip" StrCpy $downloadname "pak64.scifi" StrCpy $VersionString "pak64.SciFi V0.2" Call DownloadInstallZip SectionEnd SectionGroupEnd simutrans-124.5/src/Windows/nsis/preparation-functions.nsh000066400000000000000000000013511520675460500240250ustar00rootroot00000000000000; ; This file is part of the Simutrans project under the Artistic License. ; (see LICENSE.txt) ; ; ************************ Preparations for installer / downloader ************************** !include "MUI2.nsh" !include "Sections.nsh" VAR /Global PAKDIR VAR /Global USERDIR ; needs the following plugins: ; nsisunz ; inetc ; CabDll ; untgz ; ShellLink ; Parameter for functions var downloadlink var downloadname var archievename var VersionString var multiuserinstall var installinsimutransfolder XPStyle on !define MUI_ICON "..\stormoog.ico" !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "simutranssmall.bmp" !define MUI_BGCOLOR 000000 !define MULTIUSER_MUI !define MULTIUSER_INSTALLMODE_COMMANDLINE !include MultiUser.nsh simutrans-124.5/src/Windows/nsis/simutrans-noadmin.nsi000066400000000000000000000155111520675460500231470ustar00rootroot00000000000000; ; This file is part of the Simutrans project under the Artistic License. ; (see LICENSE.txt) ; ; ************************************* Pakset downloader for simutrans ********************************************* ; needs the following plugins: ; nsisunz ; inetc ; CabX ; untgz ; ShellLink !define VERSION "0.124.5.0" RequestExecutionLevel user !define MULTIUSER_EXECUTIONLEVEL user VIProductVersion "${VERSION}" VIFileVersion "${VERSION}" VIAddVersionKey "ProductName" "Simutrans" ;VIAddVersionKey "Comments" "A test comment" VIAddVersionKey "CompanyName" "Simutrans Team" VIAddVersionKey "LegalCopyright" "Artistic License" VIAddVersionKey "FileDescription" "Simutrans installer (local user)" VIAddVersionKey "FileVersion" "${VERSION}" Unicode true var group1 Name "Simutrans Transport Simulator" OutFile "simutrans-online-install-noadmin.exe" InstallDir $LOCALAPPDATA\Simutrans !define MUI_UNICON "..\stormoog.ico" !include "preparation-functions.nsh" !include "languages.nsh" SectionGroup /e !Simutrans Function PostExeInstall WriteUninstaller $INSTDIR\uninstall.exe StrCmp $multiuserinstall "1" NotPortable ; just change to simuconf.tab "singleuser_install = 1" FileOpen $0 "$INSTDIR\config\simuconf.tab" a FileSeek $0 903 FileWrite $0 "singleuser_install = 1 " FileClose $0 goto finishGDIexe NotPortable: WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "DisplayName" "Simutrans - A transport simulator" WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "DisplayIcon" "$\"$INSTDIR\uninstall.exe$\"" ; make start menu entries CreateDirectory "$SMPROGRAMS\Simutrans" CreateShortCut "$SMPROGRAMS\Simutrans\Simutrans.lnk" "$INSTDIR\Simutrans.exe" "" CreateShortCut "$SMPROGRAMS\Simutrans\Simutrans (Debug).lnk" "$INSTDIR\Simutrans.exe" "-log 1 -debug 3" finishGDIexe: FunctionEnd Section /o "Executable (GDI)" GDIexe AddSize 19908 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-5/simuwin-124-5.zip" StrCpy $archievename "simuwin-124-5.zip" StrCpy $downloadname "nodelete" SetOutPath $INSTDIR Call DownloadInstallZip Call PostExeInstall SectionEnd Section "Executable (SDL2)" SDLexe AddSize 22280 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-5/simuwin-sdl-124-5.zip" StrCpy $archievename "simuwin-sdl-124-5.zip" StrCpy $downloadname "nodelete" SetOutPath $INSTDIR Call DownloadInstallZip Call PostExeInstall SectionEnd Section /o "Executable (GDI 64bit)" GDI64exe AddSize 19208 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-5/simuwin-x64-124-5.zip" StrCpy $archievename "simuwin-x64-124-5.zip" StrCpy $downloadname "nodelete" SetOutPath $INSTDIR Call DownloadInstallZip Call PostExeInstall SectionEnd Section /o "Executable (SDL2 64bit)" SDL64exe AddSize 21404 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-5/simuwin-x64-sdl-124-5.zip" StrCpy $archievename "simuwin-x64-sdl-124-5.zip" StrCpy $downloadname "nodelete" SetOutPath $INSTDIR Call DownloadInstallZip Call PostExeInstall SectionEnd SectionGroupEnd Section "Uninstall" Delete $INSTDIR\Uninstall.exe ; delete self (see explanation below why this works) Delete "$SMPROGRAMS\Simutrans\Simutrans.lnk" Delete "$SMPROGRAMS\Simutrans\Simutrans (Debug).lnk" Delete $INSTDIR\Uninst.exe ; delete self (see explanation below why this works) RMDir /r $INSTDIR SetShellVarContext all ; to query ProgramData StrCpy $PAKDIR "$LOCALAPPDATA\simutrans" SetShellVarContext current DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" MessageBox MB_YESNO "Remove global paksets from $PAKDIR?" /SD IDYES IDNO +2 RMDir /r $PAKDIR SectionEnd !include "paksets.nsh" ;************************** from here on other helper stuff ***************** !include "other-functions.nsh" ;********************* from here on special own helper functions ************ ; make sure, at least one executable is installed Function .onSelChange !insertmacro StartRadioButtons $9 !insertmacro RadioButton ${GDIexe} !insertmacro RadioButton ${SDLexe} !insertmacro RadioButton ${GDI64exe} !insertmacro RadioButton ${SDL64exe} !insertmacro EndRadioButtons test_for_pak: ; save last state of SDLexe selection SectionGetFlags ${SDLexe} $R0 IntOp $group1 $R0 & ${SF_SELECTED} ; Make sure at least some pak is selected SectionGetFlags ${pak} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak64.german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakHAJO} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak.japan} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak.nippon} $R0 IntCmp $R0 ${SF_SELECTED} show_not ; SectionGetFlags ${pak64.ho-scale} $R0 ; IntOp $R0 $R0 & ${SF_SELECTED} ; IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakcontrast} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak64.scifi} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak96.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakHD} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.japan} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.britain} $R0 IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${PAK128.german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.CS} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak192.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak144.Excentrique} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak48.Excentrique} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak32} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakTTD} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak48.bitlit} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not ; not pak selected! MessageBox MB_OK|MB_ICONSTOP "At least 1 pak set must be selected!" /SD IDOK show_not: FunctionEnd simutrans-124.5/src/Windows/nsis/simutrans-offline.nsi000066400000000000000000000265031520675460500231470ustar00rootroot00000000000000; ; This file is part of the Simutrans project under the Artistic License. ; (see LICENSE.txt) ; ; Section define/macro header file ; See this header file for more info !include "MUI2.nsh" !include "Sections.nsh" !include "zipdll.nsh" ; Parameter for functions var downloadlink var downloadname var archievename var group1 var multiuserinstall ;******** This script assumes, you have installed all the paks and GDI exe to this directory ******* ;******** There should be also a folder SDL containing the SDL exe and DLL ******* ;******** There should be also a folder pak64addon containing the food chain addon ******* ;******** There should be also a folder pak64addon/font with the chinese font ******* !define SP_PATH "C:\Programme\simutrans" Name "Simutrans Transport Simulator" OutFile "simutrans-offline-install.exe" ; The default installation directory InstallDir $PROGRAMFILES\Simutrans XPStyle on ; Request application privileges for Windows Vista RequestExecutionLevel admin !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "simutranssmall.bmp" !define MUI_BGCOLOR 000000 !insertmacro MUI_LANGUAGE "English" ;first language is the default language !insertmacro MUI_LANGUAGE "French" !insertmacro MUI_LANGUAGE "German" !insertmacro MUI_LANGUAGE "Spanish" !insertmacro MUI_LANGUAGE "SpanishInternational" !insertmacro MUI_LANGUAGE "SimpChinese" !insertmacro MUI_LANGUAGE "TradChinese" !insertmacro MUI_LANGUAGE "Japanese" !insertmacro MUI_LANGUAGE "Italian" !insertmacro MUI_LANGUAGE "Dutch" !insertmacro MUI_LANGUAGE "Danish" !insertmacro MUI_LANGUAGE "Swedish" !insertmacro MUI_LANGUAGE "Norwegian" !insertmacro MUI_LANGUAGE "NorwegianNynorsk" !insertmacro MUI_LANGUAGE "Finnish" !insertmacro MUI_LANGUAGE "Greek" !insertmacro MUI_LANGUAGE "Russian" !insertmacro MUI_LANGUAGE "Portuguese" !insertmacro MUI_LANGUAGE "PortugueseBR" !insertmacro MUI_LANGUAGE "Polish" !insertmacro MUI_LANGUAGE "Czech" !insertmacro MUI_LANGUAGE "Slovak" !insertmacro MUI_LANGUAGE "Croatian" !insertmacro MUI_LANGUAGE "Bulgarian" !insertmacro MUI_LANGUAGE "Hungarian" !insertmacro MUI_LANGUAGE "Romanian" !insertmacro MUI_LANGUAGE "Latvian" !insertmacro MUI_LANGUAGE "Estonian" !insertmacro MUI_LANGUAGE "Turkish" !insertmacro MUI_LANGUAGE "Lithuanian" !insertmacro MUI_LANGUAGE "Indonesian" !insertmacro MUI_LANGUAGE "Belarusian" !insertmacro MUI_LANGUAGE "Catalan" ; we need to start with sections first for the second licence SectionGroup !Simutrans Function PostExeInstall StrCmp $multiuserinstall "1" NotPortable ; just change to simuconf.tab "singleuser_install = 1" FileOpen $0 "$INSTDIR\config\simuconf.tab" a FileSeek $0 924 FileWrite $0 "singleuser_install = 1 " FileClose $0 goto finishGDIexe NotPortable: ; make start menu entries CreateDirectory "$SMPROGRAMS\Simutrans" CreateShortCut "$SMPROGRAMS\Simutrans\Simutrans.lnk" "$INSTDIR\Simutrans.exe" "-log 1 -debug 3" finishGDIexe: ; uninstaller not working yet ;WriteUninstaller $INSTDIR\uninstaller.exe FunctionEnd Section "Executable (GDI, Unicode)" GDIexe SetOutPath $INSTDIR File "${SP_PATH}\simutrans.exe" File "${SP_PATH}\*.txt" File /r "${SP_PATH}\text" File /r "${SP_PATH}\font" File /r "${SP_PATH}\skin" File /r "${SP_PATH}\music" File /r "${SP_PATH}\config\simuconf.tab" SectionEnd Section /o "Executable (SDL, better sound)" SDLexe SetOutPath $INSTDIR File "${SP_PATH}\SDL\*.*" File "${SP_PATH}\*.txt" File /r "${SP_PATH}\text" File /r "${SP_PATH}\font" File /r "${SP_PATH}\skin" File /r "${SP_PATH}\music" File /r "${SP_PATH}\config\simuconf.tab" SectionEnd Section /o "Chinese Font" wenquanyi_font SetOutPath $INSTDIR File /r "${SP_PATH}\pak64addon\font" SectionEnd SectionGroupEnd SectionGroup "Pak64: main and addons" pak64group Section "!pak64 (standard)" pak64 SetOutPath $INSTDIR File /r "${SP_PATH}\pak" SectionEnd Section /o "pak64 Food addon 102.2.1" StrCmp $multiuserinstall "1" InstallInUserDir ; no multiuser => install in normal directory SetOutPath $INSTDIR goto FinishFood64 InstallInUserDir: SetOutPath $DOCUMENTS\simutrans FinishFood64: File /r "${SP_PATH}\pak64addon\pak" SectionEnd SectionGroupEnd Section /o "pak64.german (Freeware)" pak64german SetOutPath $INSTDIR File /r "${SP_PATH}\pak64.german" SectionEnd Section /o "pak64.japan" pak64japan SetOutPath $INSTDIR File /r "${SP_PATH}\pak.japan" SectionEnd Section /o "pak64.Nippon" pak64nippon SetOutPath $INSTDIR File /r "${SP_PATH}\pak.nippon" SectionEnd Section /o "pak64.HO (GPL)" pak64HO SetOutPath $INSTDIR File /r "${SP_PATH}\pak64.HO" SectionEnd Section /o "pak64.SciFi" pak64SF SetOutPath $INSTDIR File /r "${SP_PATH}\pak64.ScfFi" SectionEnd Section /o "pak64 HAJO (Freeware)" pak64HAJO SetOutPath $INSTDIR File /r "${SP_PATH}\pak.HAJO" SectionEnd Section /o "pak64 contrast (GPL)" pak64contrast SetOutPath $INSTDIR File /r "${SP_PATH}\pak.HAJO" SectionEnd Section /o "pak96 Comic (beta, Freeware)" pak96comic SetOutPath $INSTDIR File /r "${SP_PATH}\pak96.comic" SectionEnd Section /o "pak96 hand drawn" pak96HD SetOutPath $INSTDIR File /r "${SP_PATH}\pakHD" SectionEnd Section /o "pak128" pak128 SetOutPath $INSTDIR File /r "${SP_PATH}\pak128" SectionEnd Section /o "pak128 German" pak128german SetOutPath $INSTDIR File /r "${SP_PATH}\pak128.german" SectionEnd Section /o "pak128 Japan" pak128japan SetOutPath $INSTDIR File /r "${SP_PATH}\pak128.japan" SectionEnd Section /o "pak128 Britain" pak128britain SetOutPath $INSTDIR File /r "${SP_PATH}\pak128.britain" SectionEnd Section /o "pak192 Comic (Freeware)" pak192comic SetOutPath $INSTDIR File /r "${SP_PATH}\pak192.comic" SectionEnd Section /o "pak32 Comic (alpha)" pak32comic SetOutPath $INSTDIR File /r "${SP_PATH}\pak32.comic" SectionEnd # create a section to define what the uninstaller does. # the section will always be named "Uninstall" section "Uninstall" Delete $INSTDIR\uninstaller.exe RMDir /r $INSTDIR sectionEnd ;***************************************************** from here on come sections ***************************************** ; we just ask for Artistic licences, the other re only asked for certain paks PageEx license LicenseData "license.rtf" PageExEnd ; If not installed to program dir, ask for a portable installation Function CheckForPortableInstall StrCpy $multiuserinstall "1" StrCmp $INSTDIR $PROGRAMFILES\Simutrans NonPortable MessageBox MB_YESNO|MB_ICONINFORMATION "Should this be a portable installation?" IDYES Portable IDNO NonPortable Portable: StrCpy $multiuserinstall "0" NonPortable: FunctionEnd PageEx directory PageCallbacks "" "" CheckForPortableInstall PageExEnd PageEx components PageExEnd ; Some paksets don't have an open source license, so we have to show additional licences Function CheckForClosedSource SectionGetFlags ${pak64german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show SectionGetFlags ${pak64HAJO} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show SectionGetFlags ${pak96comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show SectionGetFlags ${pak128} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show SectionGetFlags ${pak192comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show Abort show: ; here is ok FunctionEnd PageEx License LicenseData "pak128.txt" PageCallbacks CheckForClosedSource "" "" PageExEnd PageEx instfiles PageExEnd ; ******************************** From here on Functions *************************** Function .oninit StrCpy $multiuserinstall "1" ; activates GDI by default StrCpy $group1 ${GDIexe} ; Group 1 - Option 1 is selected by default ; avoids two instance at the same time ... System::Call 'kernel32::CreateMutexA(i 0, i 0, t "SimutransMutex") i .r1 ?e' Pop $R0 StrCmp $R0 0 +3 MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." Abort FunctionEnd ; ConnectInternet (uses Dialer plug-in) ; Written by Joost Verburg ; ; This function attempts to make a connection to the internet if there is no ; connection available. If you are not sure that a system using the installer ; has an active internet connection, call this function before downloading ; files with NSISdl. ; ; The function requires Internet Explorer 3, but asks to connect manually if ; IE3 is not installed. Function ConnectInternet Push $R0 ClearErrors Dialer::AttemptConnect IfErrors noie3 Pop $R0 StrCmp $R0 "online" connected MessageBox MB_OK|MB_ICONSTOP "Cannot connect to the internet." Quit ;This will quit the installer. You might want to add your own error handling. noie3: ; IE3 not installed MessageBox MB_OK|MB_ICONINFORMATION "Please connect to the internet now." connected: Pop $R0 FunctionEnd ; make sure, at least one executable is installed Function .onSelChange !insertmacro StartRadioButtons $group1 !insertmacro RadioButton ${GDIexe} !insertmacro RadioButton ${SDLexe} !insertmacro EndRadioButtons ; make sure GDI is installed when chinese is selected SectionGetFlags ${wenquanyi_font} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} +1 test_for_pak ; force selection of GDI exe SectionGetFlags ${GDIexe} $R0 IntOp $R0 $R0 | ${SF_SELECTED} SectionSetFlags ${GDIexe} $R0 SectionGetFlags ${SDLexe} $R0 IntOp $R0 $R0 & ${SECTION_OFF} SectionSetFlags ${SDLexe} $R0 test_for_pak: ; Make sure at least some pak is selected SectionGetFlags ${pak64} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak64german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak64HAJO} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak96comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128japan} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128britain} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak192comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak32comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not ; not pak selected! MessageBox MB_OK|MB_ICONSTOP "At least on pak set must be selected!" show_not: FunctionEnd ; $downloadlink is then name of the link, $downloadanme the name of the pak for error messages Function DownloadInstall ; MessageBox MB_OK|MB_ICONINFORMATION "Download of $downloadname from\n$downloadlink$archievename" Call ConnectInternet RMdir /r "$TEMP\simutrans" NSISdl::download $downloadlink$archievename "$Temp\$archievename" Pop $R0 ;Get the return value StrCmp $R0 "success" +3 MessageBox MB_OK "Download of $archievename failed: $R0" Quit ZipDLL::extractall "$TEMP\$archievename" "$TEMP" Pop $0 StrCmp $0 "success" exeok DetailPrint "$0" ;print error message to log RMdir /r "$TEMP\simutrans" Quit exeok: CreateDirectory "$INSTDIR" CopyFiles "$TEMP\Simutrans\*.*" "$INSTDIR" RMdir /r "$TEMP\simutrans" FunctionEnd simutrans-124.5/src/Windows/nsis/simutrans-store.nsi000066400000000000000000000060501520675460500226540ustar00rootroot00000000000000; ; This file is part of the Simutrans project under the Artistic License. ; (see LICENSE.txt) ; ; ************************************* Pakset downloader for simutrans ********************************************* ; needs the following plugins: ; nsisunz ; inetc ; CabX ; untgz ; ShellLink !define VERSION "0.124.0.0" RequestExecutionLevel user !define MULTIUSER_EXECUTIONLEVEL user VIProductVersion "${VERSION}" VIFileVersion "${VERSION}" VIAddVersionKey "ProductName" "Simutrans" ;VIAddVersionKey "Comments" "A test comment" VIAddVersionKey "CompanyName" "Simutrans Team" VIAddVersionKey "LegalCopyright" "Artistic License" VIAddVersionKey "FileDescription" "Simutrans installer (local user)" VIAddVersionKey "FileVersion" "${VERSION}" !define INSTALLSIZE 21412 !define HELPURL "http://forum.simutrans.com/" # "Support Information" link SilentInstall silent Unicode true var group1 Name "Simutrans Transport Simulator" OutFile "simutrans-store.exe" InstallDir $LOCALAPPDATA\Simutrans !define MUI_UNICON "..\stormoog.ico" !include "preparation-functions.nsh" !include "languages.nsh" Function PostExeInstall WriteUninstaller $INSTDIR\uninstall.exe WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "DisplayName" "Simutrans - A transport simulator" WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "DisplayIcon" "$\"$INSTDIR\uninstall.exe$\"" ; make start menu entries CreateDirectory "$SMPROGRAMS\Simutrans" CreateShortCut "$SMPROGRAMS\Simutrans\Simutrans.lnk" "$INSTDIR\Simutrans.exe" "" FunctionEnd Section "Executable (SDL2)" SDLexe SetOutPath $INSTDIR File /r simuwin-sdl-124-0\Simutrans\*.* ; AddSize 21412 ; StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-0/simuwin-sdl-124-0.zip" ; StrCpy $archievename "simuwin-sdl-124-0.zip" ; StrCpy $downloadname "Simutrans Executable (SDL2)" ; SetOutPath $INSTDIR ; Call DownloadInstallZip Call PostExeInstall SectionEnd Section "Uninstall" Delete $INSTDIR\Uninstall.exe ; delete self (see explanation below why this works) Delete "$SMPROGRAMS\Simutrans\Simutrans.lnk" Delete "$SMPROGRAMS\Simutrans\Simutrans (Debug).lnk" Delete $INSTDIR\Uninst.exe ; delete self (see explanation below why this works) RMDir /r $INSTDIR SetShellVarContext all StrCpy $PAKDIR "$LOCALAPPDATA\simutrans" DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" SetShellVarContext current MessageBox MB_YESNO "Remove global paksets from $PAKDIR?" /SD IDYES IDNO +2 RMDir /r $PAKDIR SectionEnd ; ******************************** From here on Functions *************************** Function .oninit StrCpy $USERDIR "$LOCALAPPDATA\simutrans" SetShellVarContext all StrCpy $PAKDIR "$LOCALAPPDATA\simutrans" ; SectionSetFlags SDLexe SetShellVarContext current FunctionEnd ;********************* from here on special own helper functions ************ simutrans-124.5/src/Windows/nsis/simutrans.nsi000066400000000000000000000160121520675460500215210ustar00rootroot00000000000000; ; This file is part of the Simutrans project under the Artistic License. ; (see LICENSE.txt) ; ; ************************************* Pakset downloader for simutrans ********************************************* ; needs the following plugins: ; nsisunz ; inetc ; CabX ; untgz ; ShellLink !define VERSION "0.124.5.0" VIProductVersion "${VERSION}" VIFileVersion "${VERSION}" VIAddVersionKey "ProductName" "Simutrans" ;VIAddVersionKey "Comments" "A test comment" VIAddVersionKey "CompanyName" "Simutrans Team" VIAddVersionKey "LegalCopyright" "Artistic License" VIAddVersionKey "FileDescription" "Simutrans installer (all users)" VIAddVersionKey "FileVersion" "${VERSION}" Unicode true ; Request application privileges for Windows Vista ;RequestExecutionLevel highest !define MULTIUSER_INSTALLMODE_INSTDIR "Simutrans" !define MULTIUSER_EXECUTIONLEVEL Highest var group1 Name "Simutrans Transport Simulator" OutFile "simutrans-online-install.exe" InstallDir $PROGRAMFILES\Simutrans !define MUI_UNICON "..\stormoog.ico" !include "preparation-functions.nsh" !insertmacro MULTIUSER_PAGE_INSTALLMODE !include "languages.nsh" SectionGroup /e !Simutrans Function PostExeInstall StrCmp $multiuserinstall "1" NotPortable ; just change to simuconf.tab "singleuser_install = 1" FileOpen $0 "$INSTDIR\config\simuconf.tab" a FileSeek $0 903 FileWrite $0 "singleuser_install = 1 " FileClose $0 goto finishGDIexe NotPortable: ; make start menu entries SetShellVarContext all CreateDirectory "$SMPROGRAMS\Simutrans" CreateShortCut "$SMPROGRAMS\Simutrans\Simutrans.lnk" "$INSTDIR\Simutrans.exe" "" CreateShortCut "$SMPROGRAMS\Simutrans\Simutrans (Debug).lnk" "$INSTDIR\Simutrans.exe" "-log 1 -debug 3" ExecWait 'Icacls "$PAKDIR" /grant Users:(OI)(CI)M' WriteUninstaller $INSTDIR\uninstall.exe WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "DisplayName" "Simutrans Game" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "DisplayIcon" "$\"$INSTDIR\uninstall.exe$\"" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" finishGDIexe: FunctionEnd Section /o "Executable (GDI)" GDIexe AddSize 19908 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-5/simuwin-124-5.zip" StrCpy $archievename "simuwin-124-5.zip" StrCpy $downloadname "nodelete" SetOutPath $INSTDIR Call DownloadInstallZip Call PostExeInstall SectionEnd Section "Executable (SDL2)" SDLexe AddSize 22280 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-5/simuwin-sdl-124-5.zip" StrCpy $archievename "simuwin-sdl-124-5.zip" StrCpy $downloadname "nodelete" SetOutPath $INSTDIR Call DownloadInstallZip Call PostExeInstall SectionEnd Section /o "Executable (GDI 64bit)" GDI64exe AddSize 19208 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-5/simuwin-x64-124-5.zip" StrCpy $archievename "simuwin-x64-124-5.zip" StrCpy $downloadname "nodelete" SetOutPath $INSTDIR Call DownloadInstallZip Call PostExeInstall SectionEnd Section /o "Executable (SDL2 64bit)" SDL64exe AddSize 21404 StrCpy $downloadlink "http://downloads.sourceforge.net/project/simutrans/simutrans/124-5/simuwin-x64-sdl-124-5.zip" StrCpy $archievename "simuwin-x64-sdl-124-5.zip" StrCpy $downloadname "nodelete" SetOutPath $INSTDIR Call DownloadInstallZip Call PostExeInstall SectionEnd SectionGroupEnd Section "Uninstall" SetShellVarContext all Delete $INSTDIR\Uninst.exe ; delete self (see explanation below why this works) Delete "$SMPROGRAMS\Simutrans\Simutrans.lnk" Delete "$SMPROGRAMS\Simutrans\Simutrans (Debug).lnk" Delete $INSTDIR\Uninst.exe ; delete self (see explanation below why this works) RMDir /r $INSTDIR SetShellVarContext all StrCpy $PAKDIR "$LOCALAPPDATA\simutrans" SetShellVarContext current DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Simutrans" MessageBox MB_YESNO "Remove global paksets from $PAKDIR?" /SD IDYES IDNO +2 RMDir /r $PAKDIR SectionEnd !include "paksets.nsh" ;************************** from here on other helper stuff ***************** !include "other-functions.nsh" ;********************* from here on special own helper functions ************ ; make sure, at least one executable is installed Function .onSelChange !insertmacro StartRadioButtons $9 !insertmacro RadioButton ${GDIexe} !insertmacro RadioButton ${SDLexe} !insertmacro RadioButton ${GDI64exe} !insertmacro RadioButton ${SDL64exe} !insertmacro EndRadioButtons test_for_pak: ; save last state of SDLexe selection SectionGetFlags ${SDLexe} $R0 IntOp $group1 $R0 & ${SF_SELECTED} ; Make sure at least some pak is selected SectionGetFlags ${pak} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak64.german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakHAJO} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak.japan} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak.nippon} $R0 IntCmp $R0 ${SF_SELECTED} show_not ; SectionGetFlags ${pak64.ho-scale} $R0 ; IntOp $R0 $R0 & ${SF_SELECTED} ; IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakcontrast} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak64.scifi} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak96.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakHD} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.japan} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.britain} $R0 IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${PAK128.german} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak128.CS} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak192.comic} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak144.Excentrique} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak48.Excentrique} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak32} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pakTTD} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not SectionGetFlags ${pak48.bitlit} $R0 IntOp $R0 $R0 & ${SF_SELECTED} IntCmp $R0 ${SF_SELECTED} show_not ; not pak selected! MessageBox MB_OK|MB_ICONSTOP "At least 1 pak set must be selected!" /SD IDOK show_not: FunctionEnd simutrans-124.5/src/Windows/nsis/simutranssmall.bmp000066400000000000000000000645161520675460500225530ustar00rootroot00000000000000BMNi6(9i:9630-) &#"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%bSI{hX|iY}jZ~kZ~l[m\n]o^p_q`satbucvdwexezf{g|h}i~jklmn&&&&&&&&&&>;;;;;;;9741-* '""%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%zgW{hX|iY}jZ~k[~l[m\n]o^p_r`satbucvdweyfzf{g|h}i~jklmn&&&&&&&&&&;99;;;;;;;;;;;;;;;:77:0/:(': :;;;;;;;;;;;;8741 -!!)#"%$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%('&yfVzgW{hX|iY}jZ~k[~l[m\n]o^q_r`satbucvdxeyfzg{h|i}jjklmn&&&&&&&&>-,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;77:..:&&; ;;;<=>?@ABBC D!E"F#G$ G%!G%!D'#?'$;'$6'%1'&,&%&%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%QF>yeUyfVzgW{hX|iY}jZ~l[~l[m\n]p^q_r`satbucwdxeyfzg{h|i~jkllmn&&&&&&&;"!;00;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:55;/.=)(?$"B D D!E"F#G$ H%!H'"I(#J)$K*%L+&M,'N-(O/)P0*Q1+Q2,R3-S5.T6/U70R60L4/E2-?/+8-*1+)*'&%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%TG?wbSxdTyeUzfVzgW{hX|iY}kZ~l[l[m\o]p^q_r`satbvcwdxeyfzg{h}i~jklmntpX&&&&&&&& ';;;;;;:!!:.-;::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::9998887776665554443332221111/.6-+;+)@*'G*&L+&M,'N.(O/)P0*Q1+R2,S4-S5.T6/U70V81W92X:3Y<4Z=5[>5\?6\@7]A8^C9_D:`E;aF]E6\?7]@8]B9^C9_D:`E;aFcI?dJ@eLAfMBgNBhOCiPDjQEkRFkTGlUHmVInWJoXKpZLq[Mr\Ns]Os^Pt_Qu`RvbSwcTxdUyeVzfW{gW|iX}jY}kZ~l[m\n]o^p_q`ratbubvcwdxeyf{g|h}i~jklmn&&&&&&&&&w[V;;;;;;;;;::)):55;;;;;;;;;;;;:::999888777666555444333333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!! @/*X;3Y<4Z=5[>6\?7]A8^B9^C:_D;`EcI?dK@eLAfMBgNChODiPEjQEkSFlTGlUHmVInWJoYKpZLq[Mr\Ns]Ot^Pu_QvaRvbSwcTxdUyeVzfW{hX|iY}jZ~kZl[m\n]o^p_q`satbucvdwexezf{g|h}i~jklmn&&&&&&&&ŶqiE ;;;;;;;<==<)(:32888777666555444333333222111000///...---,,,+++***)))(((&&&%%%$$$###"""!!! C0*X;3Y<4Z=5[?6\@7]A8^B9_C:`D;`EcH>dJ?eK@eLAfMBgNChODiQEjRFkSGlTHmUInVInWJoYKpZLq[Mr\Ns]Ot_Pu`QvaRwbSwcTxdUyeVzgW{hX|iY}jZ~k[l\m\n]o^q_r`satbucvdweyfzg{g|h}ijklmefL&&&&&&&ġ{Y72;;<=>?@AABA!<('7.-410111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!! G1,X;3Y<4Z>5[?6\@7]A8^B9_C:`D;aFcI>dJ?eK@fLAfMBgNChPDiQEjRFkSGlTHmUInVJoXKoYLpZMq[Mr\Ns^Ot_Pu`QvaRwbSxcTxdUyfVzgW{hX|iY}jZ~k[m\m\n]p^q_r`satbucvdxeyfzg{h|i}jjklm *('&&&&ɶpPG?@ABCC D!E"F#G$ G%!H&"C)%:+(0,++++***)))((('''&&&%%%$$$###"""!!! K3-Y;3Z=4Z>5[?6\@7]A8^B9_C:`E;aFcI>dJ?eK@fLAgMBgOChPDiQEjRFkSGlTHmVInWJoXKpYLpZMq[Nr]Os^Ot_Pu`QvaRwbSxcTyeUyfVzgW{hX|iY}jZ~l[m\m\o]p^q_r`satbvcwdxeyfzg{hhaXLLLLLLicZmm + *))(' &&&˳i\H$!E!E#F# G$ H%!I'"I(#J)$K*%L+&M,'N.(I-(<+(.'&%%%$$$###"""!!! N5/Y<4Z=4[>5[?6\@7]A8^C9_D:`E;aFcI>dJ?eK@fLAgOCkTIhPDiQEjRFkSGlUHmVInWJoXKpYLqZMr\Nr]Os^Pt_Qu`RvaRwcSxdTyeUzfV{gW{hX|iY}kZ~l[m\n]o]p^q_r`saubvcwdxeyfzgZVRyyym$> - , , + *(('&&&߼޿Ÿz]SI'"J(#J)$K*%L+&M-'N.(O/)P0*Q1+R2,S4-S5.O4-@.*.&$S81Y<4Z=5[>6\?7\@7]B8^C9_D:`E;aFcI?dJ@eKAfMAo[Q}ql~{ngq\PnWJoXKpYLqZMr\Ns]Os^Pt_Qu`RvbSwcTxdUyeVzfV{gW|hX|jY}kZ~l[m\n]o^p_q_r`taubvcwdxeyfrcmmmve 0 0 / . -+ + *)('߼߻޺޹޸ݷݶܶھѶs^T5-N.(O/)P0*Q1+R3,S4-T5.T6/U70V81W:2X;3Y<4Z=5[>6\?7]A8^B9^C9_D:`E;aFcI?dJ@eLAfMBgNChODiPEjQEkSFlTGtbXvpskvbVt^Pu_QuaRvbSwcTxdUyeVzfW{hX|iY}jY~kZ~l[m\n]o^p_q`satbubvcwdxezfONM> 3 2 1 0 . . - , + *߻޺޹޸ݷݶܴܵ۳۲۱ڱڲڿڿٿٿٿײש֨ƜljK>S4-T5.U6/V70V91W:2X;3Y<4Z=5[>6\@7]A8^B9_C:_D;`EdI?dK@eLAiRH{xtbZjRFkSGlTGmUHmVInWJoYKpZLq[Myi^ztxp{j]yeVzgW{hX|iY}jZ~k[l\m\n]o^p_r`satbucvdweyfqLLL 6 5 4 3 1 1 0 / . -߼߻޹޸ݷݶܴܵ۳۲ڱڰٯٮٮحج׫׮ؽؽؽ׽ؽַԣӢӢӡҠϝv`LX:2X;3Y<4Z=5[?6\@7]A8^B9_C:`D;aFcI?dJ?eK@eLAfMBgNChODr_Vyu|xve]nXJoYKpZLq[Mr\Ns]Ot_Pu`QvaRwbS|maztywmqbn]n]o^q_r`satbucvd}yLKK@ 8 7 6 5 4 3 2 1 0 0߻ߺ޹޸ݷݶܴܵ۳۱ڰڰٮ٭جثתת֩֨էզեի׻ֻ׻ֻֻֻҨќМЛϚϙϘΗΖ˩}sY^B8]A8^B9_C:`E;aFcI?dJ?eK@fLAfMBgNChPDiQEjRFkSGlTHmUIwg^{||nes_Pt_Pu`QvaRwbSxcTyeUyfVzgW{hX|iY}jZsf}tzhyfzg|simmm0H, ; : 9 8 7 6 5 4 3 3߼ߺ޹޸ݷݶܵ۳۲ڱڰٯٮحج׫ת֩֨զզեԤԣӢӡҠҟҨչչչչպպӱἨ͖͕͔̒̑ːːʏʎܷboSDbG=cH>dI?dJ?eK@fLAgMBhOChPDiQEjRFkSGlTHmVInWJoXKpYLqZMq[NweZwp~yqeyeUzfVzgW{hX|iY}kZ~l[m\n]o]p^q_r`satbvcwdxe~o}}iueG> = < ; 9 9 8 7 7 6 3 2 1߼߻޺޸ݷݶܴܵ۳ڱڰٯٮحجתש֧֨զեԤԣӢӡҠҟўѝМЛКϙѦԸԸԸԸԹԹԹΡːʏʎɍɌɋȊȊȉLjLJLJƆѫv|[jPCgNBhOCiPDiQEjRFkSGlUHmVInWJoXKpYLqZMr\Nr]Os^Pt_Qu`RvaSwcT}m`xo~w|rynwkufsbvgzmt~tzg|h}iHA@?> < < ; ; : 9 8 7 6 5 4߻ߺ޹޸ݷܴܵ۳۲ڱگٮحج׫ת֧֨զեԤԣӢӠҟҟўќМКϙϘΗΖ͕͔̓ЦӶӷӷӷӸӸӹѲɎljLjLJƆƅƄńŃĂāĀĀÀ~}va}aMkTGlUHmVInWJoXKpZLq[Mr\Ns]Os^Pt_Qu`RvbSwcTxdUyeVzfV{gW|iX}jY}kZ~l[m\p`sdwjxlvhveubvcwdxeyf{g{gEDCBA@?? > = < ; : 9 8 7߻޺޹ݷݶܴܵ۲ڱڰٯٮجثת֩֨զեԤԣӢӠҟҞѝМЛКϙϘΗΖ͕͔̒̑ːˏʎʍϦӶҶӶҷӸҸӸӹ̠ŃĂāÀ~~}||{zyyxwwvvtdqUpZLq[Mr\Ns]Ot^Pu_QvaRvbSwcTxdUyeVzfW{hX|iY}jZ~kZl[m\o]o^p_q`satbucvdwexezf^iDHGFDDCBBA@? > = < ; :޹ݷݶܵ۳۲ڱڰٮحج׫ש֧֨զԤԣӢӡҠҞѝМЛϚϘϘἨ͖͔̒ˑːʏʎɍɌȊȊLjLJΧҵҶҶҷҷҸҹҹѷŊ|{zyxxwvvutssrqqpponnm̢eYvaQvaRwbSwcTxdUyeVzgW{hX|iY}jZ~k[l\n]o^o^q_r`satbucvdwexsYLKJIHGGFEDCBA@?> =ܵ۳۲ڱڰٮحجתש֨էեԤԣӢӠҟўѝЛКϙϘΖ͕͕͔̒ˑːʏʎɍɋȊȉLjLJƆƅńŃĂΩѶѶѷҷҸҹҹҺһ̤vutssrqpponmmlkkjjihhhggfڪc\mWzgW{hX|iY}jZ~k[m\n]o^p^q_r`satbuc›dЩXONMLKJJIGGFEDCBA@?۲ڱڰٮحجתש֨էեԤԣӡҠҟўќЛϚϘΗΖ͕͔̒̒ːˏʎɍɌȊȉLjLJƆƅńłāĀ~}|άѶѶѷѸҹҹҺһҼҼÉonnmlkjjihggfeeddccbbaa`v½~tk^΢^^F߯Z__>ePONNMLKJIHGFEDCBٯٮج׫ת֧֨զԤԣӢҠҟўќЛϚϘΗΖ͔̓̒ˑˏʏʎɌɋȊljLJƆƅńŃāĀ~}|zyxwwίѶѷѸѹѹѺһҼҽӾӾËhhgfeddcbaa`__^^]]\ēYYYYY$] %\ WZRRQPONMLKJIHGFFحثת֧֩զԤԣӢӠҟўќЛϚϘΗΖ͔̓̒ˑˏʎɍɌɋȊLjLJƆƄŃĂāÀ~}|{zxwvutrqrϲѷѸѹѺѺһҽҾҾӿϳuba`_^^]\[[ZY϶uTTTTTTTWVVew$TSRQPONMLKJJIFEDש֨էեԤӢӡҠҞѝМКϙΘΖ͕͔̒ˑːʎɍɌȊȊLjLJƆƄŃĂĀ~}{zyxvutsrponmlqеиѹѺѻҼҽҾҿѼиPOOOOOOOO6YXWVUTSRQPONMMLKJIHG֨զեԣӢӡҟўќЛϚϘἨ͖͔̑ːʏʍɌȋȉLjLJƆƄŃĂĀ~}{zywvutrqpomlkjhgfoиѺѻҼҽҾҿ㿉JJJIIIIIIJJ&a ZYXWVUTSRQQPONMLKJIԤԣӢҠҟѝМЛϙϘΖ͕͔̒ˑˏʎɍɋȊLjLJƆƅńłāÀ~}|zyxvutrqpnmljihgfdcbapѺѼҽҽҾFEEEDDDDDDDDEE[[ZYXWVUUTSRQPONMLԤӢӡҠўѝЛϚϘἨ͖͔̑ːʎʍɌȊȉLjƆƅńŃāÀ}|zyxvutrqpnmljihgedcba_^qѼѼҽҿҿFCCBBBBBAAABBBB^ [[ZYXWWVUTSRQPONMӡҠўѝЛϚϘἨ͖͔ˑːʎɍɌȊljLJƆƄŃĂāÀ~}{zxwvtsrponlkjhgedcb`_^\[ZYu˪ҾѾ@??>>===<<<<<<===;[[[[[ZYXWVUTSRQPҞѝЛКϘἨ͖͔̑ːʎɍɌȊljLJƆńŃĂā~|{zxwutsqpnmljigfecba_^][ZYWVUTTβ⿊=<;::998877777777889Fk[[[[[[ZYXWVUTSѝМКϙΗΖ͔̓̒ːʏʍɌȊȉLJƆƄŃĂĀ~|{zxwutrqpnmkjhgfdca`_]\[YXVUTRRPOQȣƝ<:9877654433222222223344[[[[[[[[ZYXWWVЛϚϘΗ͕͔̒ˑˏʎɌɋȉLjƆƅńĂā~}|zxwvtsqpnmljigfdca`_]\ZYWVUSRPPNMKJII{ǟ̮ϸ̮ǢvE;:9875432110/..--------../0jv[[[[[[[[[[ZYϙΗΖ͔̓̒ːʏʍɌȊljLJƆńŃāÀ~~|{yxvusrpomljihfecb`_]\[YXVUSRPONMKJHGFDCA@?=<:98654210/.-++*)((('''(())*+[[[[[[[[[[[Ἠ͖͔ˑːʎɍɋȊLjLJƅńĂā~}|zywvtsqpnmkjhgedca`^][ZXWUTRQONMLJIGFDCA@?=<:9765320/-,+)('&%$##"""##%%'[[[[[[[[[[͕͔̒ˑˏʎɌɋȊLjƆƅńĂā~}{zxwvtrqpnmkjhgedba_^\[YXVUSRQOMMKJHGEDBA?>=;9875421/.,+)(&%#"! "#%[[[[[[[[[͕͔̒ˑːʎɌɋȊLjLJƅńĂā~}|zxwvtsqpnmkjhgedba_^\[YXWUSRQONMKJIGEDCA@>=;:875421/.-+*('%$#!  !#$[[[[[[[[͔̓̑ːʎɍɋȊLjLJƅńłāÀ~}|zywvtsrpnmljigfdca`^][ZYWVTSQPNNLKIHFECBA?><;98754210.-,+)('&&%%$$%%%&[[[[[[̓̒ːʏʍɌȊȉLJƆƄŃĂÀ~|{zxvutrqonlkihfedba_^\[YXWUTRQOOMLKIHFECBA?>=;:97654310/.-,,+********[[[[[[̒ˑːʎɍɋȊLjLJƅńłāÀ}|zyxvusrpomljihfecba_^\[YXWUTSQQONLKJHGFDCBA?>=<:987654322100///////[[[[̒ːʏʎɌȋȉLjƆƅńĂāÀ~}|zywvtsrpomljihfedba_^][ZYWVUSSQPOMLKIHGFECBA@?>=<;:98876655544444[[[[̒ːʏʎɌȋȉLjƆƅńĂāÀ~}|zyxvusrponlkjhgfdca`_]\[ZXWVUTSQPONMKJIHGFEDCBA@?>>=<<;;:::9999[[ːʏʎɌȋȉLjƆƅńŃĂĀ}|{yxvutrqpnmljihfedca`_]\[ZYXWVTSRQPONMLKJIHGFEEDCBBAA@@??????[ˑˏʎɌɋȊLjLJƆƅńĂā~}{zywvusrqpnmljihfedcba_^]\[ZYXWVUSSQQPONMLKKJIHHGGFFEEEDDDˑːʎʍɌȊȉLjLJƆńŃĂĀ~|{zxwvusrqpnmlkjhgfedca`__]][ZYXWWUUTSRQQPONNMMLKKKJJJII̒ˑˏʎɍɋȊȉLjLJƅńŃĂĀ~|{zywvutrqponmljihgfedcba`_^]\[[ZYXWWVUTTSSRQQQPPOOO̒ːʏʎɌɌȋȉLjLJƆńŃĂĀ~}|zyxwvtsrqponmlkjhhffedcbaa`_^]]\[ZZYYXWWVVVUUUT̓̒ːʏʎʍɌɋȊLjLJƆƅńĂāÀ~|{zyxwvutrrponmlljjihgffedccba``__^]]\\[[[ZZ̓̒̑ːʏʎɍɋȊȉLjLJƆƄŃĂāÀ~||zyxwvutsrqpponmllkjihhgffeedccbbaaa``_͔̓̒ˑːʏʍɌɋȊȉLjLJƆƄŃĂāĀ~}|{zyxwvvutsrrqponnmllkjjiihhggffee͕͔̓̒ˑːʏʎɌɋȊȉLjLJƆƅńŃĂāÀ~}||{zyxwwvuttsrrqppoonnmlllkkΖ͕͔̓̒ˑːʏʎɍɌɋȊȉLjLJƆƅńŃłĂāÀ~}||{zzyxxwvvuuttsrrrqqΗΖ͕͔̓̒ˑːʏʎɍɌɋȊȉLjLjLJƆƅńŃłĂāĀÀ~~}||{{zzyxxwwϙΘΗΖ͕͔̓̒ˑːʏʎʍɍɌɋȊȉLjLjLJƆƆƅƄńŃłĂāāÀ~~}}ϙϘΗΖ͕͔̓̓̒ˑːˏʎʎɍɌɌɋȊȉȉLjLjLJLJƆƅƅńńϙϙϘΗΖ͕͔͔̓̒̒ˑːˏʏʎʎʍɌɌɋɋsimutrans-124.5/src/Windows/old.ico000066400000000000000000000056661520675460500172740ustar00rootroot00000000000000 & ( @ffffoffffffoffHf`{ff{ff@{뻻vffH6;fvff`8fffffffffvfkfȈff{;fkwcwxfkx???( @@ @ @@@@@`@```@@@@ @ @ @ @@ @ @ ` @` ` ` @ @ @ @ @@@@@ @@ @ @ @@@@@@@@@@`@@`@`@`@@@@@@@@@@@@@@@@@@@@@`@``` `@ ` ` `@`@@`@`@```@```````@````@````@````@```@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@ II&I$$I)))$Iw))))Mw;;))))&Iw;w)))))&I$$;*))w))))))$IOO+**))))))))))MOO+**+O+*))))w))))**+OOOO*+*))))wԐOOO*OOO+*)));OOOOOۗ+*)OOۗOO**)OOOOOOOOOOO???simutrans-124.5/src/Windows/simres.rc000066400000000000000000000017631520675460500176440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* ICON and Version info for Simutrans Windows */ #include #include "../simutrans/simversion.h" 100 ICON "stormoog.ico" 101 ICON "simutrans.ico" 102 ICON "old.ico" 1 VERSIONINFO FILEVERSION RES_VERSION_NUMBER PRODUCTVERSION RES_VERSION_NUMBER { BLOCK "StringFileInfo" { BLOCK "040904E4" { VALUE "CompanyName", "Simutrans Team" VALUE "FileDescription", "Simutrans" VALUE "FileVersion", VERSION_NUMBER VALUE "InternalName", "SIMUTRANS" VALUE "LegalCopyright", "\251 Hj. Malthaner '97-'04 Simutrans Team '05-'13" VALUE "OriginalFilename", "SIMUTRANS.EXE" VALUE "ProductName", "Simutrans" VALUE "ProductVersion", VERSION_NUMBER } } BLOCK "VarFileInfo" { VALUE "Translation", 2057, 1252, 1031, 1252, 1035, 1252, 1036, 1252, 1034, 1252, 1038, 1252, 1043, 1252 } } CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "Simutrans.manifest" simutrans-124.5/src/Windows/simutrans.ico000066400000000000000000000042761520675460500205370ustar00rootroot00000000000000 ( @@ @ @@@@@`@```@@@@ @ @ @ @@ @ @ ` @` ` ` @ @ @ @ @@@@@ @@ @ @ @@@@@@@@@@`@@`@`@`@@@@@@@@@@@@@@@@@@@@@`@``` `@ ` ` `@`@@`@`@```@```````@````@````@````@```@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwmwwwwmmmwwwwwwmmwwwwwwwwmmmےmwwwwwwwwmmmmmmwwwwwwww`mmmmmmwwwwwwĄmmmmmmmmwwwwĄ`mmmmmmmےwwwwwĄm$mmmmےmmwwwwwwwmm$mmےmmwwwwwwwwwwwwwmmm$mmmmwwwwwwwwwwwwwwm$mmmmwwwwwwwwwwwwwwwm$$mmmmmmwwwwwmwww wwwwmmmmmmmwwwwmmm wwww$$mmwwwww$$ wwwww$wwwww$$$$wwwwwwwwwwwwwwww$wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww?>simutrans-124.5/src/Windows/stormoog.ico000066400000000000000000000173661520675460500203670ustar00rootroot00000000000000@@(& N(@@ @ @@@@@`@```@@@@ @ @ @ @@ @ @ ` @` ` ` @ @ @ @ @@@@@ @@ @ @ @@@@@@@@@@`@@`@`@`@@@@@@@@@@@@@@@@@@@@@`@``` `@ ` ` `@`@@`@`@```@```````@````@````@````@```@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@$$$ $ $$$$$ $$ $  $$$$$ -nmmnmn-nqnmnqnrmrmrnnrnrn$$ $$$ $$ $$ $$ $$$$  -mm-n-nmnm.mrnmnmrnnrmrnqnr $ $$$ $$$$$ $$ $  $$-m.mnmnm.mnmnnmnnrnmrmnrnrnr$$$ $$$ $$ $$  $ $$$$m-mnm-n-nmrmnqnqnqnmrnnrnqnrn$$$$$$ $ $$$$$$$$$ $$%m-nm.m-nmnmnmnmnmnnnmrnmrmrnrnq%%%%-%-e%-$%$%$$%$$$%$m-ne.mm.mmnmn-nm.mnmrmrmrnqnrnrnqnm%-%-e%-%-%m%-e--e-.e-n-m-m-m.mm.m.mnmnmn1nmnmnnmnrnqnrnr$%%-%%-e-e-%m%-e-m%m-m-e.m.m.mm.mnmm.mnmnmnnmrnqnqnmrnqnr%-%%-%-%-%m-%m-e--m-e.m-mm-mn-mmn-nmnm2mnrmrnmnrnnrnrnrm%%m%-e-%m%-e--%-e.e-m-m-m.mn-nm.mmn-nmnmnmnmnmrnmrmnmrn$%-%-%%%-%-e-%m%m-m-n%m.en-m-mm.mm.mnmnmnmrmrnrnqnmmmmnm%%%%m%-mmm-%m-m-%m%m-m-m-mmn-nm.mnmnm.mrmnnmnmnn-mqm-%-%-%-%mmnmnmnmm-m-m-n-n-n-mm.mmn-nmnm.nmrmrnqmmm%%-e-%m%-%-e-mnmrrnmmm-mmn-nmn-nmn-nqnmrmnnmn-r,%-%%%-%mmm-e-m-e-m.mnqrmrmnm-mnmnmnmnmnmnnqnqm%%-%m%m%-mnmrmmm-e-m.m.mnmnqrmnqnmmnmnnqre-%-%-%-%-e--e-%mnnrnmm-mm.mnm-nmnqnnrqnrnmqmrmsn-e%-%m%-e-%m--m-e-nmrrmnm-nm-nmnmrmnmnnmrmnmn,{{ssn--e-%m-%m%m-n%m-n-nmnqqnqnmnmrmrrrnrn,{{{{ssrn.e-m--e-m.m-mn-nmn-nmnqnrmrnrnnmrmrm,  {s{{{{{{srnn-m-m-n-m-m.mm.mnmnmrmnmrmrnnm {{{s{{s{{s{{s{sssn-m-nmnm.mnm.mnmnmrnmnnm      {{s{s{{{s{{s{{s{{s{{s{{ssr.m-mn-nmnm.mnnmr-,    {{{{s{{s{{{s{{s{{s{s{s{s{{Ӓrnrnrs-2s3      ۛۛ{s{s{{s{s{s{s{s{s{s{{s{s{ .-     ߛ{s{s{s{3{s{s{s{s{3{33        ۛۛߛۛ{ۛ3{3s;s;-       ۛ{3s;s;s3s3       ۛۛۛۛ3{3{3s3{3{32    {ۛs333333;333333       s{sۛ3333;33;333;33;32  {3s{s{3;33;33333333333333     s{3{3s{ۛ{33333333333333333333332 {3{3{3{3{s{{s3;33;33333333333333333333   s;s3s;s33s;3;3;3333333333333333333333333333  {3{3{3s;s;3s33s333;3333333333333333333333   3s3{3s;33s33;33;3333333333333333333333  {3{3s;s3{3;3s33333333333333333333333333  s;s3{3;s33s3;333;33;333333333333333333 s;s;s3s;3s;33s;3s3333;33333333333333333333  ;ss3{3{3s;3s;3333;33333333333333333333333 s{3{s;s3{3s;3s3;s333{33;3333333333333333 {3{s;ss;s3{3s;s333{3333s33;33;333333;33 s{3{s{3{3{3s;s;s;s33;s3;3s333s33;33333{s{3{3{s{s3{s3s;s3{3s3;s3;3{33;s33{33{s{s{s{3{3{s;s{3s;s;s;s3{3s3s;s3;s3;s{s{s{s{s{s;s{3{3{s3{s3{s3{3{3s;s3{{s{{s{s{{s{s{s{s;s{3{3{3{s;s3{3{{s{{s{{s{s{{s{s{s{s{s{s{s;s{3{s{{s{s{s{s{{s{s{{{s{{s{s{s{s{s{{s{{{{{{{s{s{{s{s{{s{{{s{{s{{{{s{{s{{{{s{s{{s{s{s{{s{s{{{{{{s{{{{s{{{s{{{{{s{s{s{{s{s{{{{{{{{{{{{{{{{{{{{{{{{?g????( @@ @ @@@@@`@```@@@@ @ @ @ @@ @ @ ` @` ` ` @ @ @ @ @@@@@ @@ @ @ @@@@@@@@@@`@@`@`@`@@@@@@@@@@@@@@@@@@@@@`@``` `@ ` ` `@`@@`@`@```@```````@````@````@````@```@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@@ @ @@@@@`@```@@@@$ $$$$$$$$$$$mnmn-nmmnqnq-$$ $$$$$$-nm.mnqnnrnrn,$$$$$$$$$%%-mn-mnmnmnqnmrn$%-e-e-m%m-mm.mmnmnqnrnnrnr%-%-%m%-m.m.mm.mn-nmnmrmr--%m-m--m-m-mm.mnmnmrnrmnm,%%-emmnmnnmm.mnmnmnmnmm%mmmmnmmm-mnqnqnrmrnqn$.%-%--e-nmnnmnmnmnnqnrm,{snn-m-m-mmnmnqnqnrm {{{{{{sssn.m.mnmnmnm-  {{s{s{s{s{s{ۓss3s   ۛ{{s{{3{3 -   {3;s;2   {3333333     s{ۛ{3333333333  3{3{s{{3333333333333   {333{333;333333333 3s;s333333333333333  s;s;33{333333333333 s;ss3{33;333;33333 {s{3{3s;s3{3s3;s3 s{s{s{s{s;s;s;s3{{{{s{{s{s{s{s{{s{s{{s{s{s{s{{{s{{{{{{{{{{ ???simutrans-124.5/src/android/000077500000000000000000000000001520675460500157735ustar00rootroot00000000000000simutrans-124.5/src/android/AndroidAppSettings.cfg.in000066400000000000000000000443201520675460500226260ustar00rootroot00000000000000# The application settings for Android libSDL port # Specify application name (e.x. My Application) AppName="Simutrans" # Specify reversed site name of application (e.x. com.mysite.myapp) AppFullName=com.simutrans # Application version code (integer) AppVersionCode=0@ver_major@@ver_minor@ # Application user-visible version name (string) AppVersionName="@ver_major@.@ver_minor@.@ver_patch@@nightly_suffix@" # Specify path to download application data in zip archive in the form "Description|URL|MirrorURL^Description2|URL2|MirrorURL2^..." # If you'll start Description with '!' symbol it will be enabled by default, '!!' will also hide the entry from the menu, so it cannot be disabled # If the URL in in the form ':dir/file.dat:http://URL/' it will be downloaded as binary BLOB to the application dir and not unzipped # If the URL does not contain 'http://' or 'https://', it is treated as file from 'project/jni/application/src/AndroidData' dir - # these files are put inside .apk package by the build system # You can specify Google Play expansion files in the form 'obb:main.12345' or 'obb:patch.12345' where 12345 is the app version for the obb file # You can mount expansion files created with jobb tool if you put 'mnt:main.12345' or 'mnt:patch.12345' # The mount directory will be returned by calling getenv("ANDROID_OBB_MOUNT_DIR") # Android app bundles do not support .obb files, they use asset packs instead. # This app project includes one pre-configured install-time asset pack. # To put your data into asset pack, copy it to the directory AndroidData/assetpack # and run changeAppSettings.sh. The asset pack zip archive will be returned by # getenv("ANDROID_ASSET_PACK_PATH"), this call will return NULL if the asset pack is not installed. # You can put "assetpack" keyword to AppDataDownloadUrl, the code will check # if the asset pack is installed and will not download the data from other URLs. # You can extract files from the asset pack the same way you extract files from the app assets. # You can use .zip.xz archives for better compression, but you need to add 'lzma' to CompiledLibraries # Generate .zip.xz files like this: zip -0 -r data.zip your-data/* ; xz -8 data.zip AppDataDownloadUrl="!!Data pak|data.zip^!!MIDI data|:TimGM6mb.sf2:TimGM6mb.sf2" # Reset SDL config when updating application to the new version (y) / (n) ResetSdlConfigForThisVersion=n # Delete application data files when upgrading (specify file/dir paths separated by spaces) DeleteFilesOnUpgrade="libsdl-DownloadFinished-0.flag" # Here you may type readme text, which will be shown during startup. Format is: # Text in English, use \\\\n to separate lines (that's four backslashes)^de:Text in Deutsch^ru:Text in Russian^button:Button that will open some URL:http://url-to-open/ ReadmeText='' # libSDL version to use (1.2/1.3/2.0) LibSdlVersion=2.0 # Specify screen orientation: (v)ertical/(p)ortrait or (h)orizontal/(l)andscape ScreenOrientation=h # Video color depth - 16 BPP is the fastest and supported for all modes, 24 bpp is supported only # with SwVideoMode=y, SDL_OPENGL mode supports everything. (16)/(24)/(32) VideoDepthBpp=16 # Enable OpenGL depth buffer (needed only for 3-d applications, small speed decrease) (y) or (n) NeedDepthBuffer=n # Enable OpenGL stencil buffer (needed only for 3-d applications, small speed decrease) (y) or (n) NeedStencilBuffer=n # Use GLES 2.x context # you need this option only if you're developing 3-d app (y) or (n) NeedGles2=n # Use GLES 3.x context # you need this option only if you're developing 3-d app (y) or (n) NeedGles3=n # Use gl4es library for provide OpenGL 1.x functionality to OpenGL ES accelerated cards (y) or (n) UseGl4es= # Application uses software video buffer - you're calling SDL_SetVideoMode() without SDL_HWSURFACE and without SDL_OPENGL, # this will allow small speed optimization. Enable this even when you're using SDL_HWSURFACE. (y) or (n) SwVideoMode=y # Application video output will be resized to fit into native device screen (y)/(n) SdlVideoResize=n # Application resizing will keep 4:3 aspect ratio, with black bars at sides (y)/(n) SdlVideoResizeKeepAspect=n # Do not allow device to sleep when the application is in foreground, set this for video players or apps which use accelerometer InhibitSuspend=n # Create Android service, so the app is less likely to be killed while in background CreateService= # Application does not call SDL_Flip() or SDL_UpdateRects() appropriately, or draws from non-main thread - # enabling the compatibility mode will force screen update every 100 milliseconds, which is laggy and inefficient (y) or (n) CompatibilityHacksForceScreenUpdate=n # Application does not call SDL_Flip() or SDL_UpdateRects() after mouse click (ScummVM and all Amiga emulators do that) - # force screen update by moving mouse cursor a little after each click (y) or (n) CompatibilityHacksForceScreenUpdateMouseClick=n # Application initializes SDL audio/video inside static constructors (which is bad, you won't be able to run ndk-gdb) (y)/(n) CompatibilityHacksStaticInit=n # On-screen Android soft text input emulates hardware keyboard, this will only work with Hackers Keyboard app (y)/(n) CompatibilityHacksTextInputEmulatesHwKeyboard=n # Built-in text input keyboards with custom layouts for emulators, requires CompatibilityHacksTextInputEmulatesHwKeyboard=y # 0 or empty - standard Android keyboard # 1 - Simple QWERTY keyboard, no function keys, no arrow keys # 2 - Commodore 64 keyboard # 3 - Amiga keyboard # 4 - Atari800 keyboard TextInputKeyboard= # Hack for broken devices: prevent audio chopping, by sleeping a bit after pushing each audio chunk (y)/(n) CompatibilityHacksPreventAudioChopping=n # Hack for broken apps: application ignores audio buffer size returned by SDL (y)/(n) CompatibilityHacksAppIgnoresAudioBufferSize=n # Hack for VCMI: preload additional shared libraries before aplication start CompatibilityHacksAdditionalPreloadedSharedLibraries="" # Hack for Free Heroes 2, which redraws the screen inside SDL_PumpEvents(): slow and compatible SDL event queue - # do not use it with accelerometer/gyroscope, or your app may freeze at random (y)/(n) CompatibilityHacksSlowCompatibleEventQueue=n # Save and restore OpenGL state when drawing on-screen keyboard for apps that use SDL_OPENGL CompatibilityHacksTouchscreenKeyboardSaveRestoreOpenGLState= # Application uses SDL_UpdateRects() properly, and does not draw in any region outside those rects. # This improves drawing speed, but I know only one application that does that, and it's written by me (y)/(n) CompatibilityHacksProperUsageOfSDL_UpdateRects= # Application uses mouse (y) or (n), this will show mouse emulation dialog to the user AppUsesMouse=y # Application needs two-button mouse, will also enable advanced point-and-click features (y) or (n) AppNeedsTwoButtonMouse=n # Right mouse button can do long-press/drag&drop action, necessary for some games (y) or (n) # If you disable it, swiping with two fingers will send mouse wheel events RightMouseButtonLongPress= # Show SDL mouse cursor, for applications that do not draw cursor at all (y) or (n) ShowMouseCursor=y # Screen follows mouse cursor, when it's covered by soft keyboard, this works only in software video mode (y) or (n) ScreenFollowsMouse= # Generate more touch events, by default SDL generates one event per one video frame, this is useful for drawing apps (y) or (n) GenerateSubframeTouchEvents= # Force relative (laptop) mouse movement mode, useful when both on-screen keyboard and mouse are needed (y) or (n) ForceRelativeMouseMode=n # Show on-screen dpad/joystick, that will act as arrow keys (y) or (n) AppNeedsArrowKeys=n # On-screen dpad/joystick will appear under finger when it touches the screen (y) or (n) # Joystick always follows finger, so moving mouse requires touching the screen with other finger FloatingScreenJoystick= # Application needs text input (y) or (n), enables button for text input on screen AppNeedsTextInput=y # Application uses joystick (y) or (n), the on-screen DPAD will be used as joystick 0 axes 0-1 # This will disable AppNeedsArrowKeys option AppUsesJoystick=n # Application uses second on-screen joystick, as SDL joystick 0 axes 2-3 (y)/(n) AppUsesSecondJoystick=n # Application uses third on-screen joystick, as SDL joystick 0 axes 20-21 (y)/(n) AppUsesThirdJoystick= # Application uses accelerometer (y) or (n), the accelerometer will be used as joystick 1 axes 0-1 and 5-7 AppUsesAccelerometer=n # Application uses gyroscope (y) or (n), the gyroscope will be used as joystick 1 axes 2-4 AppUsesGyroscope=n # Application uses orientation sensor (y) or (n), reported as joystick 1 axes 8-10 AppUsesOrientationSensor= # Use gyroscope to move mouse cursor (y) or (n), it eats battery, and can be disabled in settings, do not use with AppUsesGyroscope setting MoveMouseWithGyroscope= # Application uses multitouch (y) or (n), multitouch events are passed as SDL_JOYBALLMOTION events for the joystick 0 AppUsesMultitouch=n # Application records audio (it will use any available source, such a s microphone) # API is defined in file SDL_android.h: int SDL_ANDROID_OpenAudioRecording(SDL_AudioSpec *spec); void SDL_ANDROID_CloseAudioRecording(void); # This option will add additional permission to Android manifest (y)/(n) AppRecordsAudio=n # Application needs read/write access SD card. Always disable it, unless you want to access user photos and downloads. (y) / (n) AccessSdCard=y # Application needs to read it's own OBB file. Enable this if you are using Play Store expansion files. (y) / (n) ReadObbFile=n # Application needs Internet access. If you disable it, you'll have to bundle all your data files inside .apk (y) / (n) AccessInternet=y # Immersive mode - Android will hide on-screen Home/Back keys. Looks bad if you invoke Android keyboard. (y) / (n) ImmersiveMode=y # Draw in the display cutout area. (y) / (n) DrawInDisplayCutout= # Hide Android system mouse cursor image when USB mouse is attached (y) or (n) - the app must draw it's own mouse cursor HideSystemMousePointer=y # Application implements Android-specific routines to put to background, and will not draw anything to screen # between SDL_ACTIVEEVENT lost / gained notifications - you should check for them # rigth after SDL_Flip(), if (n) then SDL_Flip() will block till app in background (y) or (n) # This option is reported to be buggy, sometimes failing to restore video state NonBlockingSwapBuffers=n # Redefine common hardware keys to SDL keysyms # BACK hardware key is available on all devices, MENU is available on pre-ICS devices, other keys may be absent # SEARCH and CALL by default return same keycode as DPAD_CENTER - one of those keys is available on most devices # Use word NO_REMAP if you want to preserve native functionality for certain key (volume keys are 3-rd and 4-th) # Keys: TOUCHSCREEN (works only when AppUsesMouse=n), DPAD_CENTER/SEARCH, VOLUMEUP, VOLUMEDOWN, MENU, BACK, CAMERA RedefinedKeys="LALT RETURN NO_REMAP NO_REMAP SPACE DELETE" # Number of virtual keyboard keys - currently 12 keys is the maximum AppTouchscreenKeyboardKeysAmount=0 # Define SDL keysyms for multitouch gestures - pinch-zoom in, pinch-zoom out, rotate left, rotate right RedefinedKeysScreenGestures="KP_PLUS KP_MINUS 1 2 " # Redefine on-screen keyboard keys to SDL keysyms - currently 12 keys is the maximum RedefinedKeysScreenKb="LALT RETURN KP_PLUS KP_MINUS SPACE DELETE " # Names for on-screen keyboard keys, such as Fire, Jump, Run etc, separated by spaces, they are used in SDL config menu RedefinedKeysScreenKbNames="LALT RETURN KP_PLUS KP_MINUS SPACE DELETE KP_PLUS KP_MINUS 1 2" # On-screen keys theme # 0 = Ultimate Droid by Sean Stieber (green, with cross joystick) # 1 = Simple Theme by Beholder (white, with cross joystick) # 2 = Sun by Sirea (yellow, with round joystick) # 3 = Keen by Gerstrong (multicolor, with round joystick) # 4 = Retro by Santiago Radeff (red/white, with cross joystick) # 5 = GameBoy from RetroArch # 6 = PlayStation from RetroArch # 7 = SuperNintendo from RetroArch # 8 = DualShock from RetroArch # 9 = Nintendo64 from RetroArch TouchscreenKeysTheme=2 # Redefine gamepad keys to SDL keysyms, button order is: # A B X Y L1 R1 L2 R2 LThumb RThumb Start Select Up Down Left Right LThumbUp LThumbDown LThumbLeft LThumbRight RThumbUp RThumbDown RThumbLeft RThumbRight RedefinedKeysGamepad="LALT RETURN KP_PLUS KP_MINUS SPACE DELETE KP_PLUS KP_MINUS 1 2" # Redefine keys for the second gamepad, same as the first gamepad if not set: RedefinedKeysSecondGamepad="" # Redefine keys for the third gamepad, same as the first gamepad if not set: RedefinedKeysThirdGamepad="" # Redefine keys for the fourth gamepad, same as the first gamepad if not set: RedefinedKeysFourthGamepad="" # How long to show startup menu button, in msec, 0 to disable startup menu StartupMenuButtonTimeout=3000 # Menu items to hide from startup menu, available menu items: # SettingsMenu.OkButton SettingsMenu.DummyMenu SettingsMenu.MainMenu SettingsMenuMisc.DownloadConfig SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMisc.AudioConfig SettingsMenuMisc.VideoSettingsConfig SettingsMenuMisc.ShowReadme SettingsMenuMisc.GyroscopeCalibration SettingsMenuMisc.StorageAccessConfig SettingsMenuMisc.CommandlineConfig SettingsMenuMisc.ResetToDefaultsConfig SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.DisplaySizeConfig SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.JoystickMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuMouse.CalibrateTouchscreenMenu SettingsMenuKeyboard.KeyboardConfigMainMenu SettingsMenuKeyboard.ScreenKeyboardSizeConfig SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig SettingsMenuKeyboard.ScreenKeyboardThemeConfig SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig SettingsMenuKeyboard.RemapHwKeysConfig SettingsMenuKeyboard.RemapScreenKbConfig SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout SettingsMenuKeyboard.ScreenKeyboardAdvanced HiddenMenuOptions='SettingsMenuMouse.DisplaySizeConfig' # Menu items to show at startup - this is Java code snippet, leave empty for default # new SettingsMenuMisc.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new SettingsMenuMouse.DisplaySizeConfig(true) : new SettingsMenu.DummyMenu()), new SettingsMenuMisc.OptionalDownloadConfig(true), new SettingsMenuMisc.GyroscopeCalibration() # Available menu items: # SettingsMenu.OkButton SettingsMenu.DummyMenu SettingsMenu.MainMenu SettingsMenuMisc.DownloadConfig SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMisc.AudioConfig SettingsMenuMisc.VideoSettingsConfig SettingsMenuMisc.ShowReadme SettingsMenuMisc.GyroscopeCalibration SettingsMenuMisc.StorageAccessConfig SettingsMenuMisc.CommandlineConfig SettingsMenuMisc.ResetToDefaultsConfig SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.DisplaySizeConfig SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.JoystickMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuMouse.CalibrateTouchscreenMenu SettingsMenuKeyboard.KeyboardConfigMainMenu SettingsMenuKeyboard.ScreenKeyboardSizeConfig SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig SettingsMenuKeyboard.ScreenKeyboardThemeConfig SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig SettingsMenuKeyboard.RemapHwKeysConfig SettingsMenuKeyboard.RemapScreenKbConfig SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout SettingsMenuKeyboard.ScreenKeyboardAdvanced FirstStartMenuOptions='' # Minimum amount of RAM application requires, in Mb, SDL will print warning to user if it's lower AppMinimumRAM=0 # GCC version, or 'clang' for CLANG NDK_TOOLCHAIN_VERSION= # Android platform version. # android-16 = Android 4.1, the earliest supported version in NDK r18. # android-18 = Android 4.3, the first version supporting GLES3. # android-21 = Android 5.1, the first version with SO_REUSEPORT defined. APP_PLATFORM= # Specify architectures to compile, 'all' or 'y' to compile for all architectures. # Available architectures: armeabi-v7a arm64-v8a x86 x86_64 MultiABI='armeabi-v7a arm64-v8a x86 x86_64' # Optional shared libraries to compile - removing some of them will save space # MP3 patents are expired, but libmad license is GPL, not LGPL # Available libraries: mad (GPL-ed!) sdl_mixer sdl_image sdl_ttf sdl_net sdl_blitpool sdl_gfx sdl_sound intl xml2 lua jpeg png ogg flac tremor vorbis freetype xerces curl theora fluidsynth lzma lzo2 mikmod openal timidity zzip bzip2 yaml-cpp python boost_date_time boost_filesystem boost_iostreams boost_program_options boost_regex boost_signals boost_system boost_thread glu avcodec avdevice avfilter avformat avresample avutil swscale swresample bzip2 #CompiledLibraries="png freetype bzip2 ssl crypto curl zip fluidsynth c++_shared" CompiledLibraries="png freetype bzip2 fluidsynth c++_shared" # Application uses custom build script AndroidBuild.sh instead of Android.mk (y) or (n) CustomBuildScript=y # Aditional CFLAGS for application AppCflags='' # Aditional C++-specific compiler flags for application, added after AppCflags AppCppflags='' # Additional LDFLAGS for application AppLdflags='' # If application has headers with the same name as system headers, this option tries to fix compiler flags to make it compilable AppOverlapsSystemHeaders= # Build only following subdirs (empty will build all dirs, ignored with custom script) AppSubdirsBuild='' # Exclude these files from build AppBuildExclude='' # Application command line parameters, including app name as 0-th param AppCmdline='simutrans -fullscreen -autodpi -debug 2 -syslog -tag com.simutrans' # Screen size is used by Google Play to prevent an app to be installed on devices with smaller screens # Minimum screen size that application supports: (s)mall / (m)edium / (l)arge MinimumScreenSize=m # Your AdMob Publisher ID, (n) if you don't want advertisements AdmobPublisherId=n # Your AdMob test device ID, to receive a test ad AdmobTestDeviceId= # Your AdMob banner size (BANNER/FULL_BANNER/LEADERBOARD/MEDIUM_RECTANGLE/SMART_BANNER/WIDE_SKYSCRAPER/FULL_WIDTH:Height/Width:AUTO_HEIGHT/Width:Height) AdmobBannerSize= # Google Play Game Services application ID, required for cloud saves to work GooglePlayGameServicesId= # The app will open files with following extension, file path will be added to commandline params AppOpenFileExtension='' simutrans-124.5/src/android/AndroidBuild.sh000077500000000000000000000017521520675460500206770ustar00rootroot00000000000000#!/bin/sh cd simutrans ln -sf libbzip2.so ../../../../obj/local/$1/libbz2.so rm -f config.$1.txt echo VERBOSE=1 >> config.$1.txt echo OPTIMIZE=1 >> config.$1.txt echo OSTYPE=linux >> config.$1.txt echo COLOUR_DEPTH=16 >> config.$1.txt echo BACKEND=sdl2 >> config.$1.txt echo USE_SOFTPOINTER=1 >> config.$1.txt echo USE_FLUIDSYNTH_MIDI=1 >> config.$1.txt echo WITH_REVISION=1 >> config.$1.txt echo MSG_LEVEL=3 >> config.$1.txt echo DEBUG=1 >> config.$1.txt echo FLAGS ?= -DUSE_OWN_PAKINSTALL cmake -E copy_if_different config.$1.txt config.$1 echo "#define REVISION `sh tools/get_revision.sh`" > revision.h.txt cmake -E copy_if_different revision.h.txt revision.h env CFLAGS="-fpermissive -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections" \ LDFLAGS="-L`pwd`/../../../../obj/local/$1 --Wl,-z,max-page-size=16384 -s " \ PATH=`pwd`/../..:$PATH \ ../../setEnvironment-$1.sh sh -c " \ make -j8 CFG=$1 && \ cp -f build/$1/sim ../libapplication-$1.so" || exit 1 simutrans-124.5/src/android/AndroidPreBuild.sh000077500000000000000000000027371520675460500213520ustar00rootroot00000000000000#!/bin/sh cd simutrans || exit 1 echo "Updating translations" tools/get_lang_files.sh || exit 1 cd simutrans echo "Get pak64" ../tools/get_pak.sh pak64 || exit 1 #echo "Get pak64.german" #../tools/get_pak.sh pak.german || exit 1 echo "Get pak64.japan" ../tools/get_pak.sh pak64.japan || exit 1 cd .. echo "Adding assets" download_with_retry() { echo 'START ' $1 RESULT=-1 RETRY=5 while [ $RESULT -ne 0 ] && [ $RETRY -ne 0 ] do wget --show-progress $1 -O $2 RESULT=$? RETRY=$((RETRY-1)) done if [ $RETRY -eq 0 ] then exit 1 fi } echo "`pwd`" echo "`ls -l`" echo "`ls -l *`" mkdir -p simutrans/music [ -e simutrans/music/TimGM6mb.sf2 ] || (download_with_retry https://sourceforge.net/p/mscore/code/HEAD/tree/trunk/mscore/share/sound/TimGM6mb.sf2?format=raw TimGM6mb.sf2 && cp ./TimGM6mb.sf2 simutrans/music/TimGM6mb.sf2) || exit 1 #[ -e ../simutrans/font/RobotoCondensed-Regular.ttf ] || (download_with_retry https://fonts.google.com/download?family=Roboto%20Condensed Roboto_Condensed.zip && unzip -n Roboto_Condensed.zip -d ../simutrans/font) || exit 1 #[ -e simutrans/font/Roboto-Regular.ttf ] || (download_with_retry https://github.com/googlefonts/roboto/releases/download/v2.138/roboto-android.zip Roboto.zip && unzip -n Roboto.zip Roboto-Regular.ttf -d simutrans/font) || exit 1 rm -rf simutrans/font [ -e simutrans/get_pak.sh ] || cp src/android/unpak.sh simutrans/get_pak.sh; chmod 755 simutrans/get_pak.sh || exit 1 echo 'Done adding assets'simutrans-124.5/src/android/Simutrans-upload.keystore000066400000000000000000000051031520675460500230300ustar00rootroot000000000000000 ?0  *H   0 0} *H nj0f0b *H  00) *H  0227{5[a asPM"h,`Z.~֞d5}7D24(xFPݜc.RwP$;SlydDB&UaC7TQ8:d& lGbƴ '~fdverM кQ酓q(<26Dgn]L). ,W3j $7渤(pUm7}2M4*g ya4¥yJk%E:7SKx;3 Oak@iͳ o[A4m}QL5Ui/}ԈT(+b9$MPS+Py2iRz$R)m.d$*/ _y֪8z{C+ 3MǑ v+;H {Pz.'pd;_8UA9BN/.~[DIJa'Ŏ77.(X'{:hq4vq-/xp~ P6tXbDMFapg{Ѯn+t@<2:E=:L} L;M{''8ǙBn!x'&jln(+ͪEYkGJoy: OvyI-#?z# S !imi;p8sZ;?㱐\ y 5+Eu Hf"/wȥ(su)a%{\H@(!_] p0# :0Sg0kȭm=zVUJrUǛ^TZiDMEqr\0dmռ_~MdTU %~H {Kp!KY81 5 a@MVDTlڽ3kya}9ָ̏Jbx(aM c %#rnt'p"ۻVo[ᮎ z]AƃVi6ݾ)BQ@}z\0ц̓mp쾍R 53l >>93}2޶{î*пoZ*#|>mݬL~eu=,'J h}كvQ<9|`r"/'@@0!0 +F˧ȉ|=_$Gd- WOsimutrans-124.5/src/android/android.cc000066400000000000000000000051531520675460500177260ustar00rootroot00000000000000#include "../simutrans/sys/simsys.h" #include "../simutrans/simdebug.h" #include #include #include #include "android.h" // Function to find the class and method ID (Must call this once on init because otherwise not found) => cache the results! // for some reason (maybe because main SDL runs in other thread) this works only for static functions static struct MethodIDs { jmethodID downloadfile; jclass myClass; } methodIDs; bool getMethodIDs(JNIEnv *env, jobject thisClass) { // Find the Java class jclass cls = env->FindClass("com/simutrans/Simutrans"); // Use fully qualified name if (cls == nullptr) { dbg->fatal("getMethodIDs()","Class not found"); return false; // Class not found } methodIDs.myClass = static_cast(env->NewGlobalRef(cls)); // Prevent GC // Find the instance method methodIDs.downloadfile = env->GetStaticMethodID(methodIDs.myClass, "downloadFile", "(Ljava/lang/String;Ljava/lang/String;)V"); if (methodIDs.downloadfile == nullptr) { env->ExceptionClear(); // Clear the exception if the method is not found __android_log_print(ANDROID_LOG_ERROR, "Simutrans", "methodIDs.downloadfile not found!"); return false; // Method not found } __android_log_print(ANDROID_LOG_INFO, "Simutrans", "methodIDs.downloadfile %p", methodIDs.downloadfile); return true; } extern "C" { // get the version but JNIEXPORT jstring JNICALL Java_com_simutrans_Simutrans_getVersion(JNIEnv* env, jobject thisClass) { // now we have a valid class reference => use this to get the static memberIDs ... getMethodIDs(env,thisClass); return env->NewStringUTF(get_version()); } } #include const char *download_file(const char *url, const char *filename) { const char * error_msg = NULL; JNIEnv *env = (JNIEnv *)SDL_AndroidGetJNIEnv(); jstring jurl = env->NewStringUTF(url); jstring jfilename = env->NewStringUTF(filename); // Call the Java method env->CallStaticVoidMethod(methodIDs.myClass, methodIDs.downloadfile, jurl, jfilename); // Check for exceptions if (env->ExceptionCheck()) { jthrowable exception = env->ExceptionOccurred(); env->ExceptionDescribe(); env->ExceptionClear(); jclass exceptionCls = env->GetObjectClass(exception); jmethodID getMessage = env->GetMethodID(exceptionCls, "getMessage", "()Ljava/lang/String;"); jstring message = (jstring)env->CallObjectMethod(exception, getMessage); error_msg = env->GetStringUTFChars(message, nullptr); __android_log_print(ANDROID_LOG_INFO, "Simutrans", "downloadfile from %s failed with %s", url, error_msg); } env->DeleteLocalRef(jurl); env->DeleteLocalRef(jfilename); return error_msg; } simutrans-124.5/src/android/android.h000066400000000000000000000002171520675460500175640ustar00rootroot00000000000000#include extern "C" { JNIEXPORT jstring JNICALL Java_com_simutrans_simutrans_Simutrans_getVersion(JNIEnv* env, jobject thisObject); }simutrans-124.5/src/android/debug.bat000066400000000000000000000005541520675460500175550ustar00rootroot00000000000000rem My devices, edit as needed rem G0W0T8058344F2B5 rem emulator-5554 set "tablet=emulator-5554" cd C:\temp\platform-tools adb -s %tablet% shell stop adb -s %tablet% shell setprop log.redirect-stdio true adb -s %tablet% shell am start com.simutrans/.MainActivity --es args "'-log -debug 3"' adb -s %tablet% shell start adb -s %tablet% logcat com.simutrans:V *:S simutrans-124.5/src/android/icon.png000066400000000000000000002245161520675460500174430ustar00rootroot00000000000000PNG  IHDR{C)IDATximuknv9A,ˎ)?BbOb#?;@~؁c@B,(Ѣ(q&$B T/L(LCNyb(i()ǁ2/M2j-V /^(XN(U4ʵZo#V[)J3YU4SUa )NT5L*eSTP:5e 44˪ c׌e#jUa %3[9KCt&R"cV[HK[ eҧ\sOGʣRSR֜NBY*d^2La);e*1GY3@qJ㾩B0)IW~U +vDTP Ԃ$s*+N!VRONS_i=eʩS,%YUӑR+b5jfR)l"CFCi-,1U嵗~<-p W"x=Y| XpC섀 Đ|K]W&d+;"WBeYH4":LG#+n$lRSMY3p ƭ<6,WY(Oggii 6SXұVm y/=bmO:Py#{ـ[őHam*H0JS1)u!GԈaEmګ<5"J<4c$[|c@ 8TQbc 酗E⮫ * nz>}꣕t )-GWp*(&O1SY{B>n#.TY0a  pq%^Wu hZk# Q8&31X{҆aRUHJٹjTRBPF1xވ(*d+- +Dgk@^Ԗ~Iy40|yvRXPOϒIa݇ SRJMS-:p\ J;fPSRȰ r^Y6SWNX]9r АT -J%!%$@(B`$%;+Y7U)2α>'֋ :$vdQl|ĆeJD1VWJGbE8Ĵ*»1CC\SwT @|قܠc8.2OT@߄z8'j#8 ܯrGҤL)m딱-qT- H^장1Y`7DY ;`LK1`&ebfJԨ<*aNR7QDC̼nu X`+a%QJq *R-|*)nG)1@ډad<"R(q cky|fǔIv:OYM~uh R_VKYPeh0u ʐ;ș5VAVQQy")uTcO:Ăv%4zb%*P #_ pBB@LL)NUMg>eDYj4} O򵫫O%]Kg'뾇NQEDg4/܈ .0Ğr)1:2k2کVp= JB_J呻f "zG ?2KД9̥d ¦AX481PFV8Tدҏ5=7p2@(1F1eYj#5xq1TUklUNVlֻ;PNѷPȁL*LĞ4x  WFC)cS5:@Ds~BykGʪ|U֨ݎc2:9 ׯiB5cvdKLSd DCnAkI-~ * M&(ϡp ]C(bBtWfwAf2\lUes-SҐ]3l@Ń suz2nJ*AYAXaGJG*W 1hk,I FYǀtGn#IAe]Zb'D8;\ p%~eVڈ8K5Sc>tEU&5)L6vsڀȩ,' :6^~Ep447Z1fLәW" be<HA2L9 d Gن(x"%F7*H u X0.TzyCccri %wk4;R43!,UH8'+VݝcUةoz~k b[#b ˸2 }f v⚽-?][ٙ`j5FU9hP05zXG#牫S|L D1PQv = tڡUl,+Ν#ՈHjOw De'^3)U@`@0+'16@(_Aڈ8Ki2>aRWdgҩ=g̤(? TZ9urr ONl.;Y)(v4< QmWo{65( kV6%G'ʡ/ k[*0"ߋQ(D&o!|NcPNAiDerVb-ej~r7 lT8K8͌ao"l> $.bw#bP5Ą9x ,8߬ l-m(`s)гz.:XP(gP.tBq:!KdcIWZR"1z$0wRc'DT })^] eCKwr#0WPxȲ@F )f*>=0%j1%xH8{+[ 'Q"r6w8kơH2O7T#2ǀ)?< ҈:y2y:~cLUZGxŊb{TLcSx])nO,2eEyK;p₩\ρJau0pȅiBJ#J r w_q u%v $H@p-5jyX$^1\BI!, J@$>8vΓKxӉt8ۅ"څ\SL-_(yt +~fw^ *R^ iƣnLCv!Pld#לF;w6J% c1ܯxqGѸ ʈPDC*{:* /STv$Gnrvjyl,k9x81*Rv*U (|FNN) Ɯ aĮC9З5`OF b;w4B)1^ DKI ޒ3)Tx>ţ1l] l]G=9@Pчup;!C &a]5\9q'iCSp dvrp ԯ-bҀz:ӯa휾 ;B@'N`YbgJ+I>Jyn&H&&__(;ɳJTX:p ȩ[|(B=/̰ U8ѭv懀8Ǔ)Schho_7e%؇@Q^9U% @ LDcc~q?93kSZ9J`Qt_iz 1-TP^?͢ɖz&T8Er57&[fW5gA4fp3kp 7<4BF= `r .xؓ`[v~c (1&318,@@crF)Kt؏U24uc("R>}Pɶ(h3@,3&%*1<#5Ӏy4ffM]ɘAI\66)d偠1IU W,@Peg  :b@~eq5DVp ўD1Jr Ā uY*?i|WBW:XeJݥdJ-A;g@t5tӷzE#5"'c 1&Sy:0q ( d fPnc"Htn 0 jNdmJ92[2V.>{Q=FNə1[H y |ĸ=2co DH^82_r #>K*[M>)aN#SN=4Y5L*M_ՖB'hS6HHk"N'!`aEKRpDUeX|N?`؏(7 #/P1+ﭜ| I6SU+{{4>UJr @aGUsx!.v9.v'i'0R5]ih7W.vM%"4N *3@LEE:߀Ku/ @6Ɩnb$q~A(vJeeK\4RnQ<!E}lwDl .SHs9 4H=:l9pJbC#B&+ Q@c@"q-->,2bSD(ӬOAS/*-,ؗcmqSk)1?XJm汐A| ԓ = ODP@(1Q2c29@f  r7d Q  ̥c]= aD(%_PHh\YPvi[=v_*)v>F6bBHtmA`o^ QLmx Fe+/@r$8|S,(Lř_ѲhQBTҀPe5 NCGOJ7K@p$pFToh0ӄ2.q )#"n/G'wRN"XO,:iBYT,׶N;yʣ[äQ\hCUxʀ*;YKhrlbAMYdߑʯ\ڣD]J05{{授T@Ӂpfb@X~WVPjVگЙK(wd8 y!UC$A`twm4UV}*P!7!"ߣA;cGƖEUՕ5u'+]cG"ibtld3."AVv n J@xdʐcm_%M"y,18<ܜ֠#xhW :B@P,$<4e(_9 $WYh/8KȘ۟++qciSDiP0eS4AA9So/fqx-m)00pK9 :+dM̎˻oՁ@&Ȳ_ 0'VK[11ɎaRy "U$L5_^ՀHc':oЊk38la_4 ;Nф(ۀi_ ө H --~;feZӃ<6D(6Iy; +TNE_XS]Y1d3ɔL17cPHdȮ}<h%V넾 ivp0PdBU*%ˁi DbP@եf88'1`u SKGa+)T46 1 swU@'N9 kJd[9.g%@Й b6|kRKjT1:8A_p'yl9'g"X[?Hĸ ƴ P0=9@(cI(͞<'A2%#zXX 7TtĎU*0e\eړR*~yDк54BARbOluQHW&oDUP9ys̤|e9?:@ĕ?O)% `Uuv4?C*73Үr B)BJ%d z4"^"FX* t4N[@vUNdʕP"B%CJDy7m,jI:B'q}tdW>Op@787hrnjJa";q jiDHS~V,1jMyJI@,`pWOC!sC)>"PZ ­ۯ*HgK"B&<<ݷf=ۊ_OŖ̤0xGvP9 G;]CGL5! MlÞ)Fv+[ś-zQ<i2lY҉G=h @TKٳ Y)oO?uDā(v@Բ !zL@(ZiR1"Hi=cDBpǗ"lSd;"l"8SsI&kWN Fm.¼^b. _FO $?4wNFۘ<;Clf1BO/5܊7ۼ$= TR"K<ZI,dT`槢dPh"I1 \oUZ*TXCÛ"&cnbFcsw1 afҶdS{hJp2m qDI:1ɏTWSyP<;}r THK񄃍vš/To)?@7YU( zBd Jiab6S;R D]m]B3*J@0 (q [BnOLi'Ƕd QE21;{/lS9f?Q#~pihD;P [D @"n{ni 2t޴p HjXWdX_|)%O ;*RLQCwYր@`ŪVeKt5(p ;|;SKT7vD)2$I$Iü{EM06˒u0%r;MA a2cN+#JWW2@0Y 9,b2ā G9@G@h}/=w-2 X+s*6A\t Uy[% ]F[΂y?ծ:D(fEva8@t5N*;ǴD޽wL͜^xSl';W,(sMuc{ 8P˳79Cz67E3\u -UP!JhA;c)Î$#1/7@ҍn~ XY u٧3:@`"C@Xqmo,}e>οME@pbOp Xv)I vSHV@N2 xgOr)~] Y\o}nSGMDN3dAowչ͕o*S>y  ̜ͬXOPH9kyȻm/@Gw8MU?:gL|5 Χ$@ ,.RJ.Hl(u8[2ӀF̕B1mK"l=ؽ6Zk PR a12%1;-A^ >R?+vj }STN4\B[_|fs&w8yj)5ɞM7^XΦ;4Ġ7Rm<>x<6JW(3EaDzIyw6ת11+`&QK pC@9i|$89R Dz+_ĝh!"/TM۾|Ο{8s?:ɑ' Dٯӯcp!<"h! ǨcPsvd \ْ 9S~5ͰuSruWmەꭿzqvbemN (t[[N~hum~o1r1Kܚԣoxb+sTgTv=&B9_kFcVP\k-Y@P8wWŊw kىx%~cW ˼= "Z >*5do/B+W@?E:s]e=/J,v7( 㦰YD>;[ʢ0.sm"~1m0 O"uwu&H@f$Z*?_\[<^jpk`gŬYitťDK8ŗ' ^a~SxE}>QY ,B$ ~8@*Dn;;J²؈pf QF FeǕߗ|`&ik;>oJl6 T/ >FV#e [CӈKX:0c*>NRZ\ RZd&W\ o`flf}5:fԐ+L: D9B%<1RcaDZGkW ]3 {hd MD  }QeMHN!U@`TCM𹍎c`(;Ny|~mAJB҉WGItaF~os}k?L1uowo\+Rb@%++"K3 Ak7.DHP~U>W67RRZ$_qkͱ0@&Iv`gĠ{Ӊѹ<̪pϔng)'*ƀ ‚OyAX<]+iKD*} '8N]Ry(t ЯЊ%W.ܯ^G"G2UJU"A_)LY<; @,~p䱾~ 1Z_b*rUfD$ \/2WvPevU( \e0Tq0UOU LJkEǀP8s0W6cz;z@t4g;//pI> լXWklhoaŕȵ5l /Y-8Gǀp%ƇTQ 01"G<iB S8 7Z>$R@3]މNB*{ v$Ѳ4["W]$kB{>Io=q~97jB1q@ĴD|„e8 !i =ȈNdNcH]+]+5\Psޣ6 _zil?ub_Ŭ}DS(%3Dcxb2ϳB9(S9$([l jLDOzQJJ쐭4p5 ā@4GҞg6 DAR|h҅JMsP=%G({0PVrD͕Hk?Zkřs[]|qFNL=4̈R1E%¿Y%YfX\]|܃o7hdbLv '?No7k2pg>f@cd[ʧم"V}dمuB *kvY,J %J4 0!^ogeӿO?߻>#"uF=BQ =;vr%)HቤDE Tp(OaR)^]N;֗m2SJ)" ɍRT`jysLŗw 91<%<\>ZĖ-n3BRo`cDv˜ERos@v~c@SW ;/2B没/}:kWJZB%/}Dxo ,V&滃>Ψ /.CBW?ӯ|܇8$M(RCD6OFy]zWWe<1 ;9t_)o63N8d\y86"01>s2WFyQF_'{8Q:PI)< GPIKK9{ʱžۯ䥀 ӯ]lQaI+RRRKU拍:ʩ9"̗RM )ZSr pȫQX"F_,js'UgPsV })v D\쎁 ɡof1IZ*5 cARpY/]H3)a=: SNrv]jss"*H9++$QŽ'kRy_1(S' a%}1 |cVNl_6pgQeh3mpb.gFD>w ɅIC@uCF)Ŭ2ʬ"9hIx#m%s)׃lp%.!Q_(3 QBqU\=(<ӊF % .4W("@dأ XBi@(@xZR5YJI*X*_~s H^V@ad?3ޛJ{7 =<"٦JAYO@Vs|qEvnΈ cQ@X%"d_uk7SD5UBexwWvI.rS%ǔZZ6Ixπ3mpW^|͔G\ޣ(->*+=dH*@ȲM,aF`@Q;UE >dUqq>&FY )4^ DMֻWm¸Ey' P2YJ DwIG[?Z_h7޴LiBY8F|"5p Mg8F5wH œ>5*;! }2_egQV1RwNx|C [5;}y$>" ~5=W!iO z[??}o:^i_;''&g @SI~(!Á zBWvDh0/l'!1gNHkx&yJSͱ34 [xi_,M˫ .|5u_emF=-LU[ߐM9XL?eQl^fo;,+f2}VD6T"Q! 8OTL+ nf*d'C̀"6"L'—זJůnPJĜ>0e]>r dH޴&+eDNGpyS5}Y:ˤRSxk .*3"bw­ջnݾ`*J {{̒ԘbbF_;OA]٣3;l_vSYBP" zq d 9'JiaӯPkՀ ^:\" @p` Dp0w`I'oJ7jp@DUs2m(/H̫itnzkR>tr.-S {m[[$Y[|po>$)41s?)MDW_|͓f>DS.W]kz\Lkge) )A F )7'T)5qFKeXV 5K6Яί DuRN~ ZQu??wOf\HntJ{t\ x$ W@׍NDpߴZQ@YNMYL/;SJyw٩la=8Zy2w2[IPW~ӘG,$w;ElEUm:ګ=5Jd34[TE9`PTxa'j$|0f 3m'ɚϏ&މ&lJ.+`KD{ ۯ@p Q;6~WHG7D0cGDbBٔbcxT$sZXSX姄QXݹ~K~|lq"@rY8x訧Hx])T80=N"Q*^ЗH<j_?& 1";͔vJ"f_Y'Ml ggWNM3%/^^1@.~}s-k_w2>)Nl~UbJk 0?M+ =>ʉ:42׶GO*uφ)ldUC;ZoEͥkʦ- *n{.Okʙcě2()G[ ^%%"so*W2an6XRyeQJ C\=D9 s|!1d .dea JG@FFE1Sy@,|eg7W|)| e'1g.%58(.#=2GYu"e6Wg'7{} ombu^9Ekj 4E6պxV9jsb;ڣbQ̛ dmCeL`&$S\̎|`>uǰly [—mأ+ %+_21O/$ue;ϡ_)PCnx;oʎ̖!LkRΔǎSðLAu۫Zo-|r ˆ(e6̽9VgӀ"t򴗙Bcm +F.e&%P)i@`QՏZ5\6HگP0i@BpPcC|D@pCLKߛsO=t?7DYAH+7 ea*Gf ,`+8иo:[3v}ɱgX~]:*#1F J5:@h}t|+?\ܠ3iJNR9n† JNrydj<TH#幣Y`E|P'?vŽgPT\q?[zĩ@gRalrDs5G(R4HdkqThWԇxySeIVy_{ aӱ1"dCY[]8DpL@ gl,>wJٙ^Rmq۟BN˗skw6\3Yp4n+>,}GC Sf6PPrGIW+p/Sr#/1+ݑ "amD ٰկD ~p7 =0Tu dS=+LP_1` KdzLOm?S _Z[Ơt ]^ Bw_z-z]8lۇraNH*3ǐƺzhʖCVS")"[W߫﷩܍mo={4s ib Hq(.m<)[TM1#Xbhz˯'dx ()O-():#;Ė._)R.._-H! ~tt|:amCKong@|_گNhpw2:ieҩūyo}L3$Ěv9J_}ikNj?t@`r6;/Op}-3JO<!FD`^R|BfNfQ&Ey;g@Ao΍??EIosFaW@ *Ѡ9(bߍSKL L̨Jf drgi&4,&g Y@xޤ<CsV+/ɖd!C)`٦;6;_O)L/ o cK6ܯ&oD_ {P`$7!$Ns7(ivҔ`]#۷>?U䥃{^~.4 lFW0 _5 ~eG"̞*f0n L?8f>m@ B)UݗaΥ`\'l~JjAUA™Aq KCш@@8B`oisiN/?33F~E1ʤ]95 X޴SWTY T+c6+U)])տ~]L:{7? p`'GfX:SK1 en blj=Z2q8@g9g KWUVmP^ ={UĤE.+[dRABATU60!*2_3@ӯ8 lI֪}i;|HUE lѕx/8RB֕;7>|Ji\z?D}dDpia~nU"_IoZ𡏿m7oZHYL1dtt (WϏTn۽wc{[缓i]'^tgYމ,g̓B_5~ ZY*eew XJLunsfc ϏI hGuR#f(u6 5yBp @Et@CcP#H0xR+fՀqPJyqpGk+?Z4<`onKK(qW֧ȮWѫ y"KX0"P%JZ^$ Rީ] ڧS)Ã[W?WO?ZZJ:ahk&JmL4j/ dPnKgH'KeM̞cQpZ,hO '-Y 0ĸk@T }Qc"T6Q0 @ F'Jg5½gc2`XD>|.-`F=sqVv?"W;PB+4TC#2/8C](oQ_n6羴k֦5 k\_Z2*r,>L1\(MY¢6SDU28?PF_E 7 c.; h,^1~Js?+0xz{o뇄]Ȫu$<44&R++?]r g)W>u5o}Û0TȔJ,$S8"ߴ수ɛBpc;2(oeǡgonߜ4Qӑw.ڳVS`uD݈sLpjdj>eՂ/e!698/@c7._4iDfcC_Re 6S@'W"Ea5!J* !uRRlo4wC/G~h'%xh38S(.I1H!:+ʏ:5)Q֩TukTbW&2?3>~tX븛-w~pE*3~Umވu9QC9ԏ<&`Gi;h\.dW^P[/T!m`\H D;ߙ9p6蝙:"ވNeb5̄7Xx1֖8M;L*uk]K7ݽp\9Upεmm<8"T̈LXS\y|!*,~!UGb las}Fiep5eؒOI@t52(Thr;WBabğ%=_VQz(GFfA٦1"8FՎF"~/Ywnx,C^Nz;[R|Fpn;ӯC7LI@J;Ns\D7hۛ/]ϧljoD8\M(z:m'P4h|!yruJer 7F|%y@&ʉhXlaV{8\S'T 1jJBIĮ۹+nv2.}ε?aubIlNO+,E WeȰ$~Dp/|*sxS߆(t^vFپ>!a4gt_HW= (@JD˂{LKᅇ{#G~UDŽE^M pQ \QDfFD+w+u_R'FJ.1vX j@{_&}֕s{j#*3asfYf4f- npW (#a`9H=@d ١Ċ* `Uߩh80_Ȏ[X9`"1@Pi;E)q>. n~dc텳OzûgphB&{`dD(e=h0=@YQkcʙuim1haR5΂Im+B=GX\TbyD|epy Ð%~cTO8u=&dd"6(0 kئҵPc3M`\\"1 t4!Ʊř9S]W EdPc@Н9v ٜؕ(&@r h <41e:vP̫Y=Яe߻yC/Yj-{s7?t72F߯L}_U9…qWmB|ȾĀ4>+ ߀)?gVu -G]N/)lޥ(v K}PX6|BqFٔ;'{be 5d \ /< PJE 9z.@7>j:m,/}[ M^{ŋoz s'YxDxUo&_ZZ*^F+-!%|pgI,ZaHOnbRkh:@՝;wݹg kgg*uT}5< +%`}mǶ[EG*7^W,Hpa XDqN䀐=Lf|Kv{nQB<(+Rh#b=" 4#7ci[pƕ6VxƟ/[mcGDy 2S-!.ϛ!qM[:5I)]i͚Nɞj;`}텹O{>pNRӬ1 ]H eYW$EKӡv7[4B:҆_ahw+eb7kH5؊oS Ql{:~<x[?=+t @:44?t R:8X[pƞn_}C;33M&*mS~0#Bvpڒs7Hi-<"6ɷcV =V1έOO4iݻZ{ﻮ߯o5ߓCwfSGc3ԱRm)Ʋ\ knN rpYDyܨR<ߑQ6o".Ʀ< *)\Zq:R`79~啴,jW>Jy-G=瞸v{mC RD0c Xʃ@SA e˅ll[2"% s_ΤŻO]|ϽͿ}na2llׯ9w~ɳG))M-L%;cbB|N Q}ʫ;woOÊU$w Z;f]U82s p.D ǫ͊M%Msz몷 Q | -alE&150;([# DYEq7*% e'!p((oDP6eEz;ׯ 8KK[/^cf%DTL~5Ѷ޴ek2JQCQ>Nreʹm -ܼT8 _@T3oY/EQI&볓OG.Y0@p+->Y_+I15"rͷBr::uO~pRg9:Xs#nγN'#{ +3TLSMudr:5޻s3[[&Yl?_}}K]mb׻L;G*VV@`dU6ְi=ʝAp2UXb^L2$1v< <R}cPpِ*EB!ǔw3)W2: Dg~4dܾJUDE3Gvn޾uW-_!u"fG_#1@V>i[^9][͍wn;T1xIo=oo]XQ"gi ~!&1Y/c,Z<:`{A|G!![pI nQ%@([\ *YqqEGv (?XssYMWUfY@*:8pu/<__zu¸_B~5X}0y-ٶcMvirٶ6)w>7T\zNiYܭܟ Mfr٣AsT8Y4il ~O^0,Gm. =d`R`˼!Rђ3%B_,|KE bܯMhygV~5\-_x`+b Z[JL Vq>$8:\[ m+fhrG*+ ^J+vTsZFaQCt-.*Z[O-/hBbRCs6H[Ys{s8P`jH+Dz0[9J( s cfyV 1ɗR,!Uv DҁȜ::Y]d+f*&9R`d+Rx?pڟS2 @gRy9z(}<; ߫&}/zokp'[avDDOߴ p4Fb)^ tڔj:+K[@tCS^|hOSC:!zu֕loOjjDBw+\"z6WF/JZYn}{"h¼yϿ ՃkG1H<"%GQ| ʋ .or˗@2ah(>L_dhEs %a r˶u<`>+\|@MJIn~`s=oGDN+bt*dϰVۜک%EV`gb'W7N ~pǧ&g *6k;-4uomh_xPր/j$ u Zώ*ܺR)d+asLT+T l)i iBt 3@]]•$.W^y̛V1QO̙8~6+eغ֯^ʳ<3FS۟{z5G,J,u(-IZk{ eU&\+)E^yY1ݫ2X[~O* ZvEF vMN Վ, dW DEUmڶSaZƝ; u ma"r4S wH>TStmD!;ھ}/>7?7ro5!|GMSjZ^oPw7>9<򳹷ʎ+; 7_Í%-i%1Su( 6S1J-_4RDDL-l a☹æ:4Ls]v@ GU@A@ȼ) HPRvT,;׃2 $ zn=-7.cc=1*@_S~.`0'ms&F϶*TIz3E$ 7.xw1Fŋ IE3]R\}gfu 5wSDة5k?u3GGb,5rBB'to{KO h+e;LEPUvWR~a`#gfQJQ3)ȜOrGnލjC )w/{msPeHdHxHQ:{'# o=sM1?z,܊x5vOMMqohޗ.\|;ϝ(,]zӦ\>ZF_)Ñ3ka* Zr%`ʕσ D;c"JݺiP]J@iPn LNK p+š3'E 7+i59$R[D7 s7?:w޾'bFo~D̀YY{wٙZiU0E~BS |B 9 $, y>b3jr*sQ럹^9A8q h]J] ()PiWEErXJ xȬ|#~%3ЮIamMiߦ F(bB W{oߞQ0ԉ*#dʶYN@0C66{3fp%4Z*.aCɶ (6D )ov(- (+J1I;B U|]b+`]$AyVrr?y1S)}LM,/ZrM1QIjSk/{⍿9Aai:]N6HoVko=ʨIjS}: t&s @xi{N?O_lȤ ;WlRmqDX4KQR*`as@f/nÇIMبNl#81Rd#($@$Ayʨ1@@ٯHg3J08U$V&$ 6֯> z{3-t:)~ !Nfqm#׶7\ٿ۹gɉeod2!v e&][cVʏ}_fXt!mՕ_GuGDq2Տ)Fى'r?1h7">o黐Jb Gd5Nz @q`_pAY9SC2ҏ  ՈKW}])_1@ӛNLk>}Ds/:&w2Nty@-n\-Qu;Q9Lc`nMaD0V5%SU{8.1&fWCFԺSWKP6ZwýN|2Ix1tk1c&F)[#B-I jR涄9A(M^jeI\ JJ+,i+0 fǀ-FpduQ W˄, 3v|f3ԣܺMeǠM泒 ߃C\e} w7_~G<3s^ZX^TQޢ B 68O"Nnmݺr/o3EO3YU&wMPf'ހ٣9c`T^xK  j8aAˎQrkx *2< gUxQH.Ffoo%N ^؃qn% \I+'Í?c2_jzh{+rv[𶌗.|'GۆTVBѷ8#dz/ 'B渓b?6N|f픇&IHwo~3U^@O4.½;_})oΆ6f G ?>̤:ހ[mf=pm }a)%/Vc@ɖðC%CXģuQ#y@i5 p93ܼq'66ȩ=@ʲųpoԆ|DUy&nId'%VtG;s7'͹/8"P/>~Tt$}"yjSl_YןZXc\yFOdj6q߾K=^MBC1F #=:Yir1G㯩*c_[ezk,1 yv ®nh HcprA`~;7n<;ڣ*:XUSxٳ*|&n4WH~6.ڒ'Xxc5q]Qtt ;nދɟ-`;!P=n}l^-Eɤ(mA.q2$NJ-v6\v6֯ @09f ;N /ch%;y=}&UF40Wpq%G$p#W9MLx-@R{J,Cٻy Y7FT| }@mʇ~񡹼 8 }eɵG 1H,&xC{y0u xgP%o33U߹_]Y\`8I N惓:3GZ ü9$#aLg]Kƍܼ链*X!m+Zn *X8 v v;_pwCO1_o@Kc^ٹt49,|p2776GMH,"k1Ot #Kn =MFT;y ̴]ki]3!e j(3^|[^q~|">q1~@>Qpn~CkϞK1mE%4Z݄-ӯp均ן1_;YNXcՀ_}?ܼ56>NC5ڽkpuo~?`[l*OҮyV8vvzftr @ˊevPT)D5paB)(P{'qAU}n0-Ġܕg@Ȏ  :R }q6 (_}FHdq[ k$B-`zȐ*r8{{w^oJ0#EFD#^yxNvov2S}KNQX1q3W|xf VfWi|j>MI%[޾O-冝Fʝ ILW /o_nN]541`ZR0!v5hPlSZEcvǽdk(p; йJ&N1:="3iۻfc9!y|#TD> m><:ؘ\z˛O;aP 6_LmW}p{-k|~`\VRZޯBb*l<û@`oK;^'Ud ʅ _߸vGׯj[,;k(϶̷dGF<׺,#k !bznv> л[/Koy_miwDqVW>b_txak8N>SնWNPM4S;Nt,Rb/=ӎ]"UQEfZeh/BJ U*5?P##ALc3Z<2PvG#듗jƟ^+q\A#fL\AaYYzJbѝ;_w"THۘ %uj}bXY9l) vy }B:33vwXֈNx*cK{F򴯇No`R#omzWoce[Mxԧe<6:H`px狯ym ;z47f&|hb}1j-&q p+A޻`cDOw _~cyo$zhV;e`SƋ$5#35?4v}> t]_`& o#Dk%b ]h񬁞|lT%6]إso#~ȰJ3~:AgI,I86Fov~DM)Vj5ɪO;w6m0ƶd4[/}c{41Gg>͵:8N*̌ÏqR enFfwc(W^_J TЅ+#=h+Є0pPk9'ʏC>S[c7'`:wq71e:[d߾t{ZgIX=TJY twqvvQ΁?i>ARM)[HvUj{߸;Wٹ7Bλ'O|oQNio\m&S [b5wϫ.B5K1@SpaZ_{QfJ#? bqj.x3 dg{EZ+{a',v1x6TOƫ;_}_Jݸ؅XIaE훝fΙb-8ru E@庼i8\SX 3gun +K M181nTW&t|fxZpՂ25,@(tl}$L*>Eel8 X'5TuyCw #d.O5釱 :u )M\q?7OMFjoi^[&Bʮ?0 E l @D@*61}Y+ Yn0W? ^\]U,_w(YVd=K72P^_<^=Jq^o]bh,+ PQP?8P9ZFg L2>\KOdR-@X,r9~c1e6HGm6O> Yz`Sw/WDd&0 2hhz$ɐwyj|g,Kz@~Y7lT37p8._7 tK瓩$ lɲ SH-Cw}WHAd|1@st;O93J6P!DE('ކh62E%D|SU.l;Mg.^|GߧX3Ul@`(@Q8@Oz;=Tf+c܆Aw_10 oO]aE8R(P SiNszRlbK'Iu쁃~.qflg`ͳdJ \lטښ?;Ig"ѫ1u7OzDY3( )lt Զ?_dϤ~z1v!^KA6p<~%+bpWl2ׂy[ Yd]^1Yfg&aWS֕|i]Nj҂`Y&__MKj>)6< ]Nxǀ%`?]ΟOil7pT'gon\usW82MaSx+[=5t]U+ vO|RYH־II[,]}l>Jq oSϮR5@))Йw<]_)+Sh58$QkV!3KeD&^d̨!d#[RW͠dٵO_IQiC+t{}m[8tȐa'?!KQJPHLO4^L༴O}>1CvS$d)3%ڥڥx&9Oere&K h.ǀ Sh9q>Yh "ª_{_6NOL|ru{&Apb+ lo?WڝQ^q߸ٿcۃ`m:h6?[Y:a\Oŧrl*].0N˻bĎeShee_m+\FaÎ?u TFaTC1HFD鷎}"#4@S@v7-Duth vftGֵ,bSV;$x5 Am὇}zu95=]c3t|B*v9˦9~%v+reGsej- |i—>𖴁|Vp9|sgO&D EvGC#!XX\nX^U&&ڈ0rn죧VWfu2>^ Tf)L&ӱ)NcvWbkTdj7 uJV+̍HS,!ϯC jCH=Xȉ~arBaۘG--4ѱP ހ*8N<.~|/Lx ځG@Ѓ@>8PqoC -FqGƛ wOyIk%Ϥ^Mq6͍@7썶G{mv/piOөEl]- O ,mڢÒ7ۦYn08H<-ڀMZ$JyYU 3WMU20#D ĕ\I(JSߘxeqIX\p}(DޮJQK_!bM@ajLZjd$<p{%#Bf!I"ʔ"r,]i:T,SR񥌄nH8Tx6 5@$oA=>B?v ɤ^?7C΃P8'oԄM%μ ,z7r}n->r/vIPeG =KӶ$+D~c R:D+_(P##ߛh I{$2美;]^OU0W9IQc0>!zZ$-ADv(1 2/UwS-KH*3:eqʪJ}7xJHBȞ=ĥ uIѽއC`" CBfaĥW:DM%W.I3~Q/@X+݇*_Mҕ1@Hu.WA`\ 愻;m 9*@yuS#aqԙ3ϥ7ގ{ Gv=k#^`O-/+dl"b2R74x#ߡ;IUjrj& ITֵNqEr 1PJEP(-|<,1@Ȥ**P% ?zvey/Rk{{o ( Tn>MW~43k.jTƊ-%֒댉Et>1;}!]e['0۩b"%Mp^_N|YW%FxJA¡s/;I@o`{zz]fH[׮0Mu -ウ~ HkhƵ5EP0]﮶-R|H…̀1@TcwI!EwlBU쑟+d JEkޡ?}r:U7d%Tp KO=jTNq*VsL[=A( G& j&>6PUp?(v}D3 6B2Gh!⑐f2[ SmklW{1\ vjw= [>ܙ~0\% Y|Z \fiKҾ}菥 AZi$l2[ -Bk"b{#ʄ@5'g5Qp M}|}[v_Ŝi3%:q3>TfhI.kE 0W?QV~Sﰚ "vyG7ՅCE$ls- H6!vз.C2*UNqZNl M.57vo#;% ZϹ0O.]?+vМ0$j @_n$@)2P@adt{ XQI"P!Z{D+p)>d EPbv:.6 d􍓒w@4I99 V]Ox|ãeDYtU#Sފu$Ϛ*TOҁE Xܞ3d-rt8s#7ᆃ̗;\YȎ= %ZH<@aBw uBJN:5&50DD;!"<V1bu2cO,νI**Dh!"l2B; /Yuc^h,BF($Vg $YZUP¢a1 @PɍEFSTqf21$J ɍT R7xduIզA(H^lu}&4(ǑPLbb?L`B/u.msFIeyh4sÕǿɬlR&(\zv8~"oԈ0 UDx}ؗUr (WP@p_he%(Eb] Z `M/tՕKoJ ʺꬷNPණ DA[iiuD_PY5, EʮG֗/IM+%\(V*#BB@96BL' Jxk5L P.8j5%7`<ͯ|Ξ~{ܛ F"ī8/[3aJK*G%b&0g񔛂B 6z(^aas$EvR /!VݨqT,w_6*c F 3ҽ/'lb"4 _- Tq$ gv`NYIKZV2x@>z?1eH$irXHݔ~}o9+a˔X S(i>@ ,,+l l-9'59+{89."E")$EJBJ$[)N9NR%r=D~q#JT^<8N!UyPIlEQIHݙٹqNsF70v ;ht`~nu*4H[$t )[}(ansGS4p%{f%H\ ɩ0E\V1<H.e> mV50tX-gfmć:$ H[}6+zbhhZ)Z%QP ,:[;XM7΄wn9#BD8Q":#z*5 ԩ T{Zp)E!Io $)Vg "`x@8M ,e ]|^m<6r F|h4j]4Ԫ jE5$2 h8N'؇?Su6$e='^oQ P~(!Du~L@D]ztb^4іAд޲,zshp/KR~CʗZLՄ:6 <<' )B||d!$W_8kN:"܅ %Hd\^,xV `po} U u<+E~U)m2(催tS[3GLCB̙"hWb|W%>x1< "W $2hR^VDD.1@A:2uKYr~jš) l|Y*bbPPaNH<$I@=%vPaʁGpf"GiL _]U3k!}G#۷[  RjF%A$B YV7r $aD }gty''O}orAv3a~!O sEmD(l@hJ .}ýdKAߒHm-ULEqUTk.*hBgS{*g:&)@tP[yx„n6-"\_uK'ȃ JJOr:FE3124qa>F|ۃ}UL@IJ`Y*Dk5P^7D(jHa[LPI}:=r_pq 2k!rh/2SL EsB"D(e3gϟt:D-MduqrQs[σiLxW}Rb!KjrY.`2PtZ[Έ+2RaH$5zV08+;2u{S// OJn\g+jzh$~f(o']k#zDK[}vގh (#}l9lU1QkZ7jJIjk_8 M(Č=tţg_~`H+ QrAarSH^dUd pČrv0-CA_~e{хe[STC.k תF]6Iu1CGz}/3:>}Hi%@I%.5 Iz8LmVs{|'?X\Q˺ 2 P[JUU֭B j9_oR"e]B@[I EX'9'":GkZ&\d[DW l(d8"X#)SB Y2+ ɒR*{. =''|u;\\ϭWQ["FemF39g|id|6s"Z2@R]?ӊ(Aَ`M ANj(FMbн+h /IlXzm @`P S;=C /eSJz>{ȳg-E ͉D6xLW1[#۔c.|ʍ FZCl4$ I2|IG(ʉA%Q쬡 1dcrx»6.+&9@Opw1# Hpi"@o=3n)M@|t'_7x/J QRTͿp=$Ul Ֆ j 4!p46F',.f-<ꡅ8^KYMFpD qy@H-OQ 0''2j^ٸ/,"#Jq>pJmbGUTZ[Z.Kz=.9-B́|?y`Pӷ+*bUn_֨9+U "}QVU\-JT6ޟ[L0 4J[,Un}hDhzTenSxr<;)>UHu='0kj&,S=&zRR%I |D ÑVcڳЙP5K/L<)s0H9;J~Tz~c'/34H4R?rmZJVJm9bu}lT&٩j:ѝ|R%`ᭊОH2bUp֐L:kjĮu'~aك}}I\GrxDQVYAn6f=$n"|Iw43{z;||| nǽmVp˹[Jz/'g3 eO9x} =%0Ī{sPwYڑ:\^(V5 n,ypZbfwq"W:`-SKś/Bz1z;}#ԁ{7G'vyxx=:&E{s3JvX* )ǣ5w|$2} .>gC.f} 2{C luM"?\5z0%32?xNUp'W#g#勏~(n،|gtڼ']+K媪a7L"sHF@Oo=ՑNw#0u9VhU<!W!~H4Pln r@H:|=CJ1V lAHa2r@"a=@,xS|V;=3q\uu5DhYe1^PF>R5Áৣ00}QZfM)z.]O߳6N ^m {t Ttp)C14_x}(`gt+LYeǧˀO.aKЄ Z:p@@@`ZdjXʉAقolԖ6n˒DCǣ.v:Gp--h:_$? ]8h>h8{lFV˭sJ-,r(s@O`@9L@?o,r0 1/HoqwX̬M=0k3OЩk٣oV(:q *sڀo2xsw"~NZ~7f/ɀGdN2QvThYԡ>w~ ʶ*Ce]‰]箼wmw-9zxPCc.v?rw dg{'0P` Ksw!  8bA]H%pGa .'όLcvFpwK9aL[D= ABg샊?bg' Awv~o]tTb;2]7ViAఈxRqXX6jd PvaGT nࢍЋļM0\| [)~lCܾ{;p(qg}Y}t ~#M"EW+_xh@6r/"NDD.֤Ⲙ_+cJMU y"R2brǡ`e=y*Ψ@" ;g BNwм.4H, 95{ot'Dz&JD&/Zy2=̷2tVKRaY5 dX4@xdzΗc1 U1בY3RI֦S{Ny-b>qcRDe(t?Gf.2>}"{9:Ȼ{z##F^24MUn0ȥUSSZ5ƎGg|Aк=SuNwWL=jLDQ}MLAuN=\muX(7͛^}%eZ$J't\O3 XFWU5 zOy~f|K5<@w[o,Je U':vF'~132v*@+p!|@NЊ4`R۷=<}Sʺq#w! `Din{j e=?`DC8NMI_[_w]gKʌLd'%uhԥb_cVo s_rgO|V*=?vqb1݇lXam5]:\- ]h3F]t_X9f1婩$ @;PRR h;cb7RҷA_| RiTZV(hj jZu/4O .ԻqLze?Ovh3o`u1 -4ʒp.44TIUq#ge% sUv&WfG50L#>=r)FkhTApn(+䞇ܩZ-`R-IhINCgn/U{vfP.ɚ`&O?dz٧8tR/iTVXjTUM0Ȧ!c+ew;BT6L|kYAHv!mل(A #@muMIkRiՙ Q?J<9m#SGX<4'Bhоig19:5SmKzSA&^@p"Ԣvnmckw|\@` ӳ{ǾɎhJUʷ Z%lF-| ;Y}u Yv{N(iZQ1P+U (XɥuMv=ӠQ׫pУ`$wǷz Җޭ4$pXhByʩrxJ MN N)]zEe!L@a+ %Ge.nw풘z_ozvoaH2DDhTWP/ݪnK Shh"ȡb)T:g{3;?{#q{fvvK,VMT(%!%EbKMAI  G8$ @@@䚗}C.y(e H|LRUٙU9@EkǙGzصΨ7&=bIP#v @rB*R2aUU$T̯\PB 7i[S( L~B t-}?s> ay`y HmϚt2`{w$kOc@^ Ɛ\lg^'O\~]kAbb߳^N:h?KyvarU7k_x?}[50'3V Ňeȵǎ9r͡5;x8&uȾE029!~ZO0! U[A "nM~RX@4 RdG!敷Mŀ=6ȗ7[YrT/r}830\ֈoCXg?MsKR soX}2?Y :czÇN8%TN_m&'3P}kصQg>h\LBvOwYK1n k`,$O]7{ SLg@x\ HJ&l,H$5Pd<Ą#" $AwԺq[7]Sek&_8W_e@EฆpᰳFȫq0࿎ g_zOp@^tZ1}{4]6{-{ec RR^2 -NH90O@g~**d]A&"P?Bb H)՚MK(D=3)\"AЎ%8fˀqA3ʀg^ywW:#f&3^u7ݍqkv^_e*O2-=֬ނ}soX[tS+%7U2@xIzRLB,6Bvch?C==|Cd'I?xz2I+#|v`>T$TAp/0@I\l@ ڒ6С"|2Vc2m[sWݕ _՚UV/-"ռ$J%sMYD樷9?t>+pbkVe1fG}|^EvJIձLrn˞CLRXȅZ5⓿O#Hu=Uؑ g@ZeδtJZCsJPWY. t3,l" f >0{76M4B϶ɕ篼/\Ɔ|"ȜFM,aw#G-=DĠDM0ww`;Q;.I_Z 8F#(j w8Pz:40(0qgHs' &J 㷉U 6題JR^A+*:Pd PPP P+ M`&Eb",fƃ|]E`۩箾^˦c+;'^ є"c1cyCG$f+{I/\`5a<uGָ{@\.QMW]d;dnTb.* uV`_;{ GCtjL1"՞p"~9<[=PSzE80H€H*!bV$zxˀٹߟn0 ,N>vٗק%+)ޘ cXL,C8LU2c5buOR2toXc|)zr`\ `Z'e )疍gKtά8ecLqNb=f1ȮZ>x;PmDƘG>"͂XgT5>%m%1DPk7l8%.xroJ/&CK(3e7AvgyT "An9i~.@Zj.~dI$c\11?9IKDfg1 Ȁz 8:4a{X巆={ط-+DB Y? 1 ܢ\.XyݥpHͯZEy׏p?qG].4V`EZ+/#`ȿC.XFFy.OM 5t cr/2}0`C0rɀqek:}?wפ!VmFRRBq6B`QeaSKf0|k]&C9G}2EJU섀QsZk_֓0)xA(d(rrZ6_VV?k=d(5 ֩jO@hZ-hgAXOX#,g Xl۴_@!gdZ I ؇RQk=d, X fr^}+RHXxASGX74ɍ5P2Cc*}<2v+.#?kܞ.M!CT@Z ̾e)w'BL 6{k z6I0ڗ+q\g$zMfTWXHI,A5X34F8>jWgsHI?^H#QADXoj>9N&\i`|\=}Q̖1A0HQfRZzݔzf71z&ȑ(q/ɓõJr!4׮ф>>:N%~!2Ѐ7u;iK<1o#$C1d |wS} ]MNx@&6|3ZPz`2=9sMG >SiYQjȕ8~aua54@U"Np %HVAP Ay'snz^a&iAt|X.HV, #;/_7nwS鞊1e ה8-s ֠{{o:4kK|y~C`@LC<9'&G. p!~Mbљ!'R+tBSAY*'"JJyOZUG|XC ipf t>ߟS?E $t _yO5  w9D21  :- xv ۶G':lS!H]2,Ju)*Nt8WכTǻpfOY.}>qp m&y; fS ȀRD)Ag T:|@^0ТNLleݹm= pa:qt?&ͥS;ٟlOvǙ jnڱ/}^zuZ>#X"\ ,Bcvv6a8\bvEDŽT{ +m *rm ǖ 2o{p8AӧiI  ,nZ-4\Z>r>0޹q~˸;m~_<{yAIPLa砵v9﹁IZ*T?e?e>Ĉ?_ӛu}`R@onAj Z /OXB \.A>ʐ3<:tv!dQhNEda.:a؉'?{)~*U\nokmzs}DAT(h=*@U3Mcs+?ѲMctLǷ<2xtRIB0Bdda5G-OAU^CEnE4 AfaLfs9 8kͷPǏfktʷ:OLQnkAooΦcQ@S ? 8(9[ijy^Ǫބ<K둚O$K"pktN{T.*7[yT9$RJa F9Ƌprdm @Xq b^4?I+ $y/*Ē{XFVϑ5~+ĂVl,hɷCK675/[~̿#y}ŎUȍ<8Yrk$}g̦Su!ėoٹ0;od[,  b1B=bKX]_͑=8L.--=3V;ovKWȦ$ݢ :WE֐a%&w/G[%v[;o{okIE̻}g±d2P&)aÐaA6aHـd˰O0 ih1%2l={^̼ۗˉyW=ݛy2ĉGw 0UǹLK. ҴuD1yt9J 5F[MtDxK+(Oa8`_b *0ii H^CSFayU%ܵR KcB=Iimjkqq62]}7fY<ϻqFt-}{'a_V4x4C9=zWq뵀P Y z  ;e?6߁dF YmXg:ߦH|w1WO~j $WgvxjAW{}VG!$-^Fhp' G $TO?r`< }?P=`I=x|Gxq^}_|裏Nd[p0Ǵ __q>Hż1UTǩ LD].^i7kje >q1(wK^zypV.] w} ћ80,yr@2㒺nիz:[Mɞ&r\ȕBz\~1륛sPDz8ºFrsሚ',L)9S>+p2lZW\t:ַW.oVwޟD~0㫖w~hq=O~!m1ZN zP2]>g\rܑ G+n&xV+8bwㇱ50qDՐAP!+u-sU3lf|pr(Y0g(' uNnSexA!0.\̮:oh5Hymx50c~`:eO5?N$OAF9 _RpMOcm$6}.$$d;˘'g_sVa- |F\ ] īAqx8<$8 O%RRkf)-Zq0[zX /n^}hr W8mST.)MoZ6{r8(:pk 𛲦3\Ok:XoD'ax|0߆i6Ud$1% )I :j}@ C=7ز |rAuԷmۛ~Pco"Kř 9 'e8Sy _Nį` $ <4C :fE bú6xT %V2IRG ZF')VX'O yW^L&}xl2\/jLOwVg9O|LmlVBi)H`#$Hx%}6:bNӫ͛jyaxإ3J|; 63lOK;{jZ}+\Z-$CNrSBSS}z͹j#,+ɩg9hQzq 1re>"jVf.d/?' c&Nz اb΄aBcnB᪥j1przr8:? oE~]ox##`!"D-H .,$j٣mI׭ /:$PUNwǀ#LOa_srߧhw'\>ݧBޑ-:lݯfb oL%>~DHA֏}DHǑULxt{b"~'MQfkW8?ovxJ3L 笜Õ{}yous;@ Q`SV9sҎ=Hb'n:~3Vj~s>>0Ҭ`~ P ̏SK{m҄ԫ8;sxvoԓ}Д>C,)seeL%:ӍC4R3[ѓIDd( 3.ɬ|3T,q,gfV 2jB v/Jj gR!-K;Ic};;boWS&a4Dc?}4 (z5L 4Q.E51/S# Plp0Oc0ffrfVaǝNXOBFd@|9[nVcD98o; "ʀ`tBOIS{^*,V7Z;&h 1pqH6kgk5`j˯$CiUMYy>i^kqII8(2T{"e|{8ÅIȄAyp C#'(sL vGnGʖs,[ͳo|[xj IlU|JUՁoiG3ffNo7Z~zmi0v;JOZw ez@JUQGr6 |ݠz9éjtU*\i">B{ r}YР@5`Ɣ9]BFK¡h {~q)P Iԟ_h!9x52B_"{`O&'@I]K׫_r.+:Bhj,Hxyy2Q]nfMwZMNf2?)gl\衤>笀_l{(N7bgNUxcMB*.Jpp: z431V'ߙ OA<?; rw?(RBg)Ӄn}]%_IA]r"'UH V`\*>8بݧ>Ĭ9GUEN&N%bE3 1D/9{oOԏgMy}wS?_JZ@Ftg#04iu S (}? {  ђLC0:%Ж/Oq3*h&T*H=*kB`dB8,̔?}߉@3-FϽlnn,-|rÏZ</$((Cn Ht70oXvGT-vG:񣝃dea^]l[cUTASN <\}7v$e6?xfNTi V݇QցYIVWhT84V x_qH1ć{[^shtr2kuIKhoo?bvKoF@^AO*B:Jq-+RDu_ӴdŔ'y;Ns/i'&8,sz8IJs /HeBAΐqndPf1jC!H0 T'XŖ$H E@& ]Q Ɣ.t0IofwtR֍n> i@<#OK; k?zmn 1ަ(b1ʖ (ZHAqrx2^]l/6:N6SK]\H=A(DJh,%VW }U p]3 6}fg[e5kf^QG{'IvG,m H֠kdQUh9z,5Q󕱅ZA"=X؏bk-':}ynwyiy3-l2P* =~dڭK@&S2mvx*vؙ'f YOIE8PC"}R @n5^WB';j%k=wkilwcR>^`|[վ w( $+7f "N 0reϹjhfD=CR\NU#zh1cc)Df&~vȱ9pЋD^8 Ÿ 67667o xWBЃPUk?D~'W^bmr=;(]Sf Y9UcoQ_9$ 0?8~馼L5F=-ulj?'Nyib~HOG#dxl9YS3NEu04;Yθd25u&<|ݸ~}ky;?f ~&H%m DȾOBb /$EA 8Liΐ3BQhu5NM?iL9~{rq NRcqXeA[!El5"Ŋ} '` EihTdi'}=X= g=H G2 V'X$%\{cxgS7HdQ?/~ٸ-`u@bD$k=V;~wP F+62cCiV76$Mݧg ǟn]t}_]5z$l㎵2<JsaA/&)kOwW>ޜ`SaOoV6nBlJg*I ǯL=H4>W"WhmA*v৉: Ǐ3s ~iGQq`=Xv &49[cay,)Q 2Lim`tB5EC $Br0HS ȗ䉈b #wDcldr^{h,--=~x_|shtO c(I7= ͮD E3^ӓ3rCYR7A[u,uqf' k~8> ޵ۥl>/^y{xef "‡x՟[Kk/ f1v>  _jPVQx啈cTm-ڝd؃:_t=:wĥ=Sn{d 6BhGkqN'0ӝ`gԀO,>NWm•Ud\m/p 9pPc[сp5q,iuZX3s_zf05?ppG ѽwmvW`0*y!5~r=Qo9ৼ~z)Q( )Sd)VzԭS k=Vsˌ0$ЍM %Hk @@\da{d+&?:k'yar0&:0`z/^ut瑧Icܿ?k_Eo*&>Ns4^)$:H |Mwk'8@2 eO3CK&):ݧbt8;4>^;Ai~ ,x7i7!}˹vǰD P-&=BC8X!{XleǐbD9qW1P1/ DzIt j?O4ȀńYù%Zp)oVgl!_x<~Lv^}>?ڮRWgg㏾^OK;*.m#栀KKu;n/99 '3V8c`hjZB 8Kr|Z*YBGI28d֐!D(H2ӊ9(N, 8'gU9OQ#'ST#U94K! NȁIqժʇ5q$H/K#zOj汥;\a' 1nEx_JNXsĕ B+Ljy"s6H@Iwis7XzǸ }֗>~tOu;B ҍN]v,g.`d/rڝUxę^=yrHMFbvϘ>c~61lCBlա[c1ّuV9k$h-.;GC!ٳg/_>w p=oiq37~VHgHpy3PW!L],)> GD\6Q0.i.t^[6~gT08a㣉=͔d}i3*d)䖴}T+!Aփa9(  .%/WƈVf < !:80sx C@jh @ᵎWGG~ppv]ӥf/Q0avz x󕯼ub9pLF'_,}xf)][KfSpQ$ >Sca7wΡ5Lv֙ZXhתK]!)U'֮H'O\t@Dabz?E//{1,G,Ag '(R)`fc})K &}f ~G~?qGAr(1IoT+Rց%H@ Jl@ir o(_@S3[*ɂ% 1_85A{f.HP 8q DAכY# bҔ1Q8" 6]54Bk9=-zKp)3^z &N?sA"ps_Q:p -<٣Fy 7gOO= 繽{un׎vsa&ẢoE3ʞ<D¸![~t?"(~S c T&epfʐ)ä>FEpoz߈NpBљ-sXPTBMɞTnD7AM1nsa$Xy!3'W SG{O?<=|Mgr{9+^ɾ ( =K\B7Z Z?2tt~Fe;M[* *OQI8?1TcT4!:,mm[.hxv|75I8)Cd=1娉D\ĉ̬dփA/3̄/`iIi/<~_>/# 1ge{`N}>zoOH"U=A5߱R@ !,ٗSlJx,fݧ~u/njh4Victq~ڊ`tx;O}0 ( _*N$WXPW([>c|zZ6z^vciJϩ䩖ӴKJuvg8:HEh!j= \Ca4@,u#a[n'Gj::W# V[usחQ u'oKXpT~wnV۰X1jq jc7v~3:88޾v;|T8d2}X4\$s?EÝ'2a{a0I?f!yP" nlakVsj>&9h>I:(c5 YFRV1ƹh YjTy̱U ^\cdA充'=bdxF:/rDpܕ͛s&}0>{ 'm~gQ/F{We&ZOGwuG'Cˋ f-ۨMqv?A}'b4o | *Q%6Xˬϔ7U-\P Z#Df,6.H $ I`B$C@ÁLOfC潓S ɢmh-CT`<ب-uW/ҟ s\pQ{t$wynjTtMbkW^c~Hrn,>bQ? LF;~H$H3I8yRsKˀԇYlMlَ0Ъ3UlB}>SJz6@y\fފ,ݧ۳eU,C҇ڣ4MxEH^պy&{62`2ߙqzl6#_w'mDdǟ A0ɨHHNHyͼbՂÓqwㅕ+k1 )O\ñn p xOŢПVea A_kv8~P&G\Mb>p|!#'fU* خ> c/W| W^wRSEI3Yz <|⮿*@=WV7o$.@w}ÃO<%M.ijāEЫRu@;=_m15>\`4fx+|9艈Z$2M iV&QSOi6N1?U߳@toDkFͽ,{xbAk\ KTp_.:4kdZH6AoF.AWLM}H6`LNr@H#gx7Z+ZThp{kWaKF?iȞ֧Ps ,Z&ZA#\LtSI]0637ǂ)H"3Z.*Z+;2stKM  imH͉d|޽{GR0xd2yAZn X ga 9Wm0DRV @$ 8_\`99zLxik1b 4ͱTt'3r[x%e`_w;Wֆf#۰wt hwN(^(.A3?0|Up+ G]tz&0I#Q.t3tFrVY3;Yj\[3,޽˭r: ל&F~oiTi!jzP)W;Nr,s@{)Fy-#2A[p/6{txi0bTR֯y%O aBm>>0n`!u$АQ{mv"G c٣ш /)CA0GGGy @k-n-*C(G';K""MTDWC/7!M`,lwڄOgg f/Hⴵw='b:K}7 $kV2Fliixĥ41; a0}?ͲQٳ;;;<`&..]~*U',nt3ApA`1{G i g#a[Mc H*eX6H\Y-jQ3? .#X\QXՓj 9M-1a; W;~);#>HY?S?~\x~p/W*C|so7۝%rŽZu4 Jt݃^j* OSR>HLu$z=hV4֓ꔙ]B;:Mcnul†qXT0 pG 0!1&ewwI qmq3ۂ҈M'_Ǔqkފˁ`P)ȐJk;dLR1&x ^ZZVE. \>ֆ-iPb0YwT\tR >~ڽnspɌ&u݅^l6[IImq$Y* >w/VW[D 8K4\ϋ{[z$[$"VV] @zkJ#-dmn@#l ‚1 kZ I(lWp=& #e,If1^Ͼ0oo]IE |Vka jiWyte^|lB;NuT{xn'Z| l` s iYZGb&=xQ7"^ Bǐ;UVҐ Ol@D1!va/_|zGH2v>y``yŕ+f ~p[ve :}I/ܸan0- `,2][uH AF4:H*nTb|4¼n_dM-ahkwS1#Y.n~gڱN;`r{pʕHDU"6 WQdchK4<` I|4"%V8@lL@@fm9P2f M#~C#wʌ6A2 82pvIT4wW7/Фky;1Nmm\XyP W>>" )&' 1&2<25`~Y2٨uhm@EjeHþ< h5^,N|HTZXYܼYZgօv0#Y'@(=Xr{q2Ms0=Y@pU^d yp2LcR wK}kAVPM 0N#뛟@}e>4՘.8ƫb6[#(Ph-,֯-_mġ( Noqի8Q[Mx#؛8p]G[||Ng 7TIjBMLp`1,_5א[.Zx-Տq'1Pdcɀ-Uqx":9AZPjvUuh@gu؅(f@Pl.^Y^\Zm?#5Alu V?;LLi- zyu _0'1e lb5ͣ K'V|0Aj@@A|bumr7.V3X˃ ”<`+i_bs vݿ6Z[slPx08fw1~-b)f_44 ( tpqgauϤ(ݶ(=Q; Hx"Z!19!Hce$ca;i0 g0#a)\*HCB8־[,H7kvWQ4 mxc{7昰m}[\߸#8hۋg =qeY0kRBD!ynxZ0AzٸO-LAzP}vk㡱׿8آցzk3HFiXѳtA\ m}|e:ߩ0*̀0aT?pS=5i@pm ~V@j(2BiᲵ cpP6B\*AȴI<>#bX4Zp+b;`wׯmW!^#Jڽ~ [ ,&v>WbSd A97(`J )ZJ/p+cD?:tQM`l[P `MS0'p-)sj-pFʥ#ԭ! Jn"bGD&ZxוupD8Wb}DU "<5JH;<lw~ڭ/]qu?TOZ[rlHd^`!]^Ѽ++7 z ĈmK$mk+J rsUV) zLH#}DXz4݃'c>},/a0y/.op1b(f76Xf6KxZ/adGnxlXH{X{PgGV[$)ōL zuia_/|e\0z=  !h)^> >IeQbhR=j,7!qڷ!:{p*YmGxr,fN^ 2gf,fq'GT9]^?^Z~v#ٛZphB̗@f ajDUV(0%`ujbI~q_Ci N:#,ޅ Z=9@tȱ{T|D3d᳃G~:r2?Wipg| Rۚ\W OMkszJ`[6 !5n.EZƏVb*8T%t lX ESQ2!AKaA>絞<3\=nq5kuݛ-@H:, @W4sr Լ`UJDC@Ki"j밳> ݤ7FtVA-ߪCu00nvd_a2`ڕW]i "53UYA)޸ז6cE"CY1nbH;9q+6`+/. ZlGhVmrx~#8!doUG9}`_ }]X]luO'" ߯b-cCsV@l#Ǻ3`eili5pUG 8i!T4 I,fH,( +?XDkپd`W&6.rm30_4agYw-$}y/AVZOk=K+ ݨ^.6He^C[cvNAh(ҠpcZA5_6I3Ɣ9/ǯQghm<ٽs'%dt۽K_- אP |6N(1 iV3ʾA-.;:cA|GhвGρ1{S*óu/즎/90Ј_`k>&T*d-^[rZ:ܛ}IxmMX[hBi#|Cw`RJƍ/.H}km. [naO,M.K2эk,$j.C/~ynw¨ʆ??zlvVoT bFm+-$RjK˼]IVU1aKԵXZ[K@mt?S:H0 ~A gM(sזXmbR/^5N&߭K]Ư*0ɀ6"%LኵP=XgZRϬMC0[uQh]06@0rؑ pA^=:A; x_"}=2CNsĖ.lļ k_:Se|faKd"p]Xz洪ڎX\fW,kK`Yj3"gc6l\x1$A@/*?URkZ·E!7V6^e3}{3ݕ rղ뜵uc5mDeK@e<%uhFu kk)Goh:w>HJ˃l|xfa=vi'w1>",/-zzڑ!× p B)7 ֻȭå7y 7%:<P*~Ay@^z!+/bDk-ձ47ϝ yjNcEyoA ao!y†I9ChCistHl9=:}vͽmruVb b +VL Q\O0ַ6!jqؖÁj} s7x ;8Xy[ ~"i.DC1aá-CHtk _N5F+s.,ؙW{b[hn~U%W.wG:MLM_3rؖYzjә簮Y]kq/udwףnbMnX}pP^}#b5Zm<{gY-s GAUM$>'! {uĬYa<1uͰzf!~ \X}°/E"&db8|2 q- h}C*,C2*kըpz6Ե|sgjikevCM|vdݗ_| -ЖlߘnB=h{Ǻ!GBƍIf' .蠗tl&l D˦x0Kkhb!O0Vۀ hxtvJ+t[x.YCڮQ3 5FآS߆+1m(@) UǦV? }܆W'^@Dn-{3nL &V)]iMAF K kD+k[lu=5SQ4*4C̈ r@'HuĜ2cU$?ݻ? 1~:<QjJYM0%d>9WgЈ?eLKnv}NL>SC_] 7P[جM!Hu(iA﷕"69%Xv@ /(F>d[/#4L84۽omFÝQX0%jnxuYp&cmpx. Q q;79jGZ@R!Ծz0 ݰw  N+Ax+AmJnnfq^y|5G?{~䃽 J NuX:ʱ&[97JeNEhTxTr8LQq ԩ6 ”'\K SNhZ LB5F;ɵ_qPRz*~Ic\u ᰤ2$qmpJ8PtdZ|X*Q_nv;ϕ.Q:kHC`rPLEvkB 'rhD˛L?l={Ý{a0!R1n/g ^ Ϩ/?ǃ`xv^}Hw9Mׯ޾W0`"axwϮ8?JG+y!;a lm~Tp#ReWb5 |LCv`A#llՈ[C ١ ڟކxg}{w9lx+-a?:i A N$(A2*v GewH[W z½x[1B ʔJ܆LO ѩ"d֠Wliwҥ?'gwyݷG荆$38%gQldूɿNHpAnWA~T iBR `q:7u4AZj*N 7'=+"vĵPV$P1lY b < 5 m^$8e0/(`ԠXCe!"R↾A'OMG*A"NC(+)`xJq=f*ot]\t/aEACǬZO wsJjީkMkCw_9sz sf\(EV2`tL]dzVnj׋ʊI?` w'Ӽ PEl>Ȣ\Lf6,($dZEVLrNw)Ҕ,>g'`H) RJ;-J@|!H@jqTmB_HdMq[<6W̷E@9v Qwߖx(iu `(#f[6s*ɇQa?堆44 eG7};&=aIm06?v׎V*@=Xa`ӵznx7;3* svbLZw~tA^A$hvX{~?vw횛x"^ /47 Ͷ6~cj 9ȀWD g&O|s1O4摿,UX0KuKc|:X_iȧoӝWn;V]Eu[_YKL2#, L `:D[G̘JoR(0_ɇwewn!䓿&o/"ZX_lijm#Ggc EYxZ4h`Mњ4#XK:P+~t;{OyFC ;HƂ>gyu8!}(W>M\.ca@c.F)˞~ F_␆:i4o}c_w_oY ## teRޡkeR`)n|6ye6׻:UYL# R)2Ef_SRٹ_s_No9e7}ris̫h@W{yRLQ:U ǶiMK~8NS: XoZtʾcvXr>[ԍ3Y #?fD4<:f'e,D :iY"kV^$@S 'UbR!h sjd fJ 5ṛgS2owlMoJ*ې 臺6@J$wjRnsK<౔Zpji-46;o/;qf;VjS o|s%"LOj߄_QPʫI$N.&K!CJm{"ވ%S3vxB5jڐo`uStbM p||Fn# .7~׶~Gr?exŻM@9H*9ג8%idz SXX!r^GT$AxhsSWp>b2k M@T|~0$!-ibg%R(b;A6/mZD$,:ikIlU<\\9 :bq䍳M].KaS[)Wc' d?ں/̏6 xÓFb?sfE(j?OhŖR,Af}I@L6 ldXM ȴjRT 8įZ:?y"9y*a4*t LHCX֞8D$<"Q4NsE$s'I᠗]:Mu9j@8n{s~I]&>߼zfINRf09 &0 FQ8 ,_ dt:</ IZ"gj8§ՏR,ȅn\!5(Z#ll178*k>"%1f:L}ٱhh޿?vֽfWq766E(Wxp48~6<= ORa|4z!X}G_c|X7* Hxa@,]]FIн*ڬcы A0Lڽ3mؐgw:Gno5v/Ey 0͂`$8$ * 7l3R6@@U05 U]U@t2 8@c a20汹ه)?[SFwqz,7f;B\約x<<Odc*a[@ξP[a,,iٮbԻ+蚖*_լ{@P3L"*7M^'bk)VђLJ>txh{0Ejɇ;[0{a2d(ylƒt2: AEm$ึvpV}|6P[Ġ訵hi, *1:`?2>D`!ڽ{h٬99|;l*ˋ+w!.ʧPG2H;R5]T:` Z]PV["4"~ lX[cl-D9|tx^x{;"lt!.ʧL40WJ㒇`ut@1A0- f5S&gʘc U;?ڊfރ/EA0~ݖcb% BA@ gcxM_8Px}@ҠD^fF]MX A޳Ga6]G'[]n墜q> N'ŵf$>,`%V7)R˲ĂWij/.-]e|va7!nkQ a\[:LeV'3^T 5Hwc )dΎU5eSj⠹nF*X.,Uh N`ʕ$2関UU ]Y6f/τ *NPYFHYEMS+)tTm弱 اJɵmZ.SP"*J'$2 x q\c+ d+CB`H*5l(?%[M#^5^zxk/akj%@S-H`*|zHJ1M*˅pQ~ vO6_l݃q`)W ^BP>wV-"eV fΊ#(DT^hhx=g%L*jQS// d(M^ јU.z4=nJRA](P,5 ΐxޙwӽFkFJrQIo_+ 6H=! qgv2cMcy/~c+tp+.H4"2ɣgT8&/E(2oFJX^mWi$(VV>DX[&H_ ^CVP6IENDB`simutrans-124.5/src/android/signBundle.sh000077500000000000000000000034311520675460500204250ustar00rootroot00000000000000#!/bin/sh # Set path to your Android keystore and your keystore alias here, or put them in your environment APPNAME=`grep AppName AndroidAppSettings.cfg | sed 's/.*=//' | tr -d '"' | tr " '/" '---'` APPVER=`grep AppVersionName AndroidAppSettings.cfg | sed 's/.*=//' | tr -d '"' | tr " '/" '---'` # Fix Gradle compilation error [ -z "$ANDROID_NDK_HOME" ] && export ANDROID_NDK_HOME="`which ndk-build | sed 's@/ndk-build@@'`" cd project ./gradlew bundleRelease || exit 1 ../copyAssets.sh pack-binaries-bundle app/build/outputs/bundle/release/app-release.aab cd app/build/outputs/bundle/release || exit 1 #[[ ! -z "$STORE_PASS" ]] && jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 -keystore /android-sdl/project/jni/application/simutrans/simutrans/src/android/Simutrans-upload.keystore --storepass $STORE_PASS app-release.aab $STORE_ALIAS wget https://github.com/google/bundletool/releases/download/1.8.2/bundletool-all-1.8.2.jar mv bundletool-all-1.8.2.jar bundletool.jar if [ -z "$STORE_PASS" ] then # no key defined java -jar bundletool.jar build-apks --bundle=app-release.aab --output=simutrans-multi.apks --mode=universal else java -jar bundletool.jar build-apks --bundle=app-release.aab --output=simutrans-multi.apks --mode=universal --ks=/android-sdl/project/jni/application/simutrans/simutrans/src/android/Simutrans-upload.keystore --ks-pass=pass:$STORE_PASS --ks-key-alias=$STORE_ALIAS fi unzip -p simutrans-multi.apks universal.apk > simuandroid-multi-nightly.apk mv app-release.aab simutrans-nightly.aab # prepare for Play Store upload to beta track cp simutrans-nightly.aab simutrans-nightly-r`cd /android-sdl/project/jni/application/simutrans/simutrans/;tools/get_revision.sh`.aab mkdir whatsNewDirectory echo `git log --pretty=format:'%s' -1` > whatsNewDirectory/whatsnew-en-US simutrans-124.5/src/android/unpak.sh000077500000000000000000000004731520675460500174540ustar00rootroot00000000000000#!/bin/sh URL=$1 pakzippath="${URL##http*\/}" rm -rf $pakzippath wget $URL unzip -l "$pakzippath" | grep "simutrans/" if [ $? -eq 1 ]; then # Not matched - We only have a simutrans/ directory unzip -qo "$pakzippath" else unzip -qo "$pakzippath" cp -RF simutrans/* . rm -rf simutrans fi rm -rf "$pakzippath" simutrans-124.5/src/base.tab000066400000000000000000002244531520675460500157670ustar00rootroot00000000000000# text group = 'message_text' # text group = 'button_text' # text group = 'climates_text' # text group = 'error_text' # text group = 'help_text' # text group = 'menu_text' # text group = 'program_text' # text group = 'record_text' # text group = 'ki_text' # text group = 'unnecessary_text' #obj= <-> text group #name= <-> object name #note= <-> description for object #- #obj=program_text #name=!0_STATION_CROWDED #- #obj=program_text #name=!1_DEPOT_REACHED #- obj=unnecessary_text name=%0_CITY_SYLL - obj=unnecessary_text name=%1_CITY_SYLL - obj=unnecessary_text name=%2_CITY_SYLL - obj=unnecessary_text name=%3_CITY_SYLL - obj=unnecessary_text name=%4_CITY_SYLL - obj=unnecessary_text name=%5_CITY_SYLL - obj=unnecessary_text name=%6_CITY_SYLL - obj=unnecessary_text name=%7_CITY_SYLL - obj=unnecessary_text name=%8_CITY_SYLL - obj=unnecessary_text name=%9_CITY_SYLL - obj=unnecessary_text name=%A_CITY_SYLL - obj=unnecessary_text name=%B_CITY_SYLL - obj=unnecessary_text name=%C_CITY_SYLL - obj=unnecessary_text name=%D_CITY_SYLL - obj=unnecessary_text name=%E_CITY_SYLL - obj=unnecessary_text name=%F_CITY_SYLL - obj=program_text name=%d buildings\n - obj=program_text name=%d convois - obj=program_text name=%d Einzelfahrzeuge im Depot - obj=program_text name=%i km/h (max. %ikm/h) - obj=program_text name=%s building %s %s - obj=program_text name=%s city %d %s - obj=program_text name=%s land %d %s - obj=program_text name=%s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants. - obj=program_text name=%s\nspeed %i\nmax_speed %i\ndx:%i dy:%i - obj=program_text name=%s\nwas liquidated. - obj=unnecessary_text name=&1_CITY_SYLL - obj=unnecessary_text name=&2_CITY_SYLL - obj=unnecessary_text name=&3_CITY_SYLL - obj=unnecessary_text name=&4_CITY_SYLL - obj=unnecessary_text name=&5_CITY_SYLL - obj=unnecessary_text name=&6_CITY_SYLL - obj=unnecessary_text name=&7_CITY_SYLL - obj=unnecessary_text name=&8_CITY_SYLL - obj=unnecessary_text name=&9_CITY_SYLL - obj=unnecessary_text name=&A_CITY_SYLL - obj=program_text name=(%i)- - obj=program_text name=(in depot) - obj=program_text name=1 Einzelfahrzeug im Depot - obj=unnecessary_text name=1center - obj=unnecessary_text name=1extern - obj=program_text name=1LIGHT_CHOOSE - obj=unnecessary_text name=1suburb - obj=unnecessary_text name=2center - obj=unnecessary_text name=2extern - obj=program_text name=2LIGHT_CHOOSE - obj=unnecessary_text name=2suburb - obj=program_text name=2WORLD_CHOOSE - obj=unnecessary_text name=3center - obj=unnecessary_text name=3extern - obj=program_text name=3LIGHT_CHOOSE - obj=unnecessary_text name=3suburb - #obj=program_text #name=3WORLD_CHOOSE #- obj=unnecessary_text name=4center - obj=unnecessary_text name=4extern - obj=program_text name=4LIGHT_CHOOSE - obj=unnecessary_text name=4suburb - obj=unnecessary_text name=5center - obj=unnecessary_text name=5extern - obj=program_text name=5LIGHT_CHOOSE - obj=unnecessary_text name=5suburb - obj=program_text name=5WORLD_CHOOSE - obj=unnecessary_text name=6center - obj=unnecessary_text name=6extern - obj=program_text name=6LIGHT_CHOOSE - obj=program_text name=6WORLD_CHOOSE - obj=unnecessary_text name=7center - obj=unnecessary_text name=8center - obj=program_text name=8WORLD_CHOOSE - obj=unnecessary_text name=9center - obj=program_text name=%C - %s
\n - obj=program_text name= - obj=help_text name=Keyboard Help\n

Keyboard Help

\n - #obj=program_text #name=A bridge must end on a way! #- #obj=program_text #name=A bridge must start on a way! - obj=program_text name=Abfrage - obj=program_text name=Abnehmer - obj=program_text name=About - obj=program_text name=Abriss - obj=program_text name=Absenken - obj=program_text name=Accelerate time - obj=program_text name=Add Stop - obj=program_text name=Add stops for backward travel - obj=program_text name=aircraft_tab - obj=program_text name=airplane - obj=program_text name=air note=waytype for run/taxiways - obj=program_text name=Airport - obj=program_text name=AIRTOOLS - obj=program_text name=All - obj=program_text name=Allow player change - obj=program_text name=allowed climates:\n - obj=program_text name=Alters a schedule. - obj=program_text name=Angenommene Waren - obj=program_text name=anhaengen - obj=program_text name=Anhaenger_tab - obj=program_text name=Anheben - obsolete=program_text name=Appends stops at the end of the schedule - obj=program_text name=Apply Line - obj=program_text name=April - obj=program_text name=Arbeiter aus: - obj=climates_text name=arctic - obj=program_text name=Arrived - obj=program_text name=Assets - obj=program_text name=August - obj=program_text name=Autohalt muss auf\nStrasse liegen!\n - obj=program_text name=Available - obj=program_text name=Bahndepot - obj=program_text name=Bankrott:\n\nDu bist bankrott.\n - obj=program_text name=battery - obj=program_text name=Baum - obj=program_text name=baum builder - obj=program_text name=Baustelle - obj=program_text name=Bauzeit - obj=program_text name=Beenden - obj=program_text name=Beginner mode - obj=program_text name=Transport fees: + note=new world window - obj=program_text name=Besonderes Gebaeude - obj=program_text name=BF - obj=program_text name=bio - obj=program_text name=Blockstrecke ist\nbelegt\n - obj=program_text name=Boden - obj=program_text name=bridge is too high for its type! - obj=program_text name=Bridge is too long for this type!\n - obj=program_text name=Bruecke - obj=program_text name=Bruecke muss an\neinfachem\nHang beginnen!\n - obj=program_text name=Brueckenboden - obj=program_text name=build choosesignals - obj=program_text name=Build city market - obj=program_text name=Build drain - obj=program_text name=build HQ note=finance window - obj=program_text name=Build land consumer - obj=program_text name=Build monorail depot - obj=program_text name=Build powerline - obj=program_text name=Build presignals - obj=program_text name=Build road depot - obj=program_text name=Build ship depot - obj=program_text name=Build signals - obj=program_text name=Build train depot - obj=program_text name=Build tram depot - obj=program_text name=Build truck depot - obj=program_text name=Building costs estimates - obj=program_text name=Buildings - obj=program_text name=Built artifical slopes - obj=program_text name=Built random attraction - obj=program_text name=Bus_tab - obj=program_text name=Can only move from halt to halt or waypoint to waypoint. - obj=program_text name=Cancel - obj=program_text name=Capacity: %s\nLoad: %d (%d%%) - obj=program_text name=Cars are not available yet! - obj=program_text name=Cash - obj=program_text name=Change player - obj=program_text name=Chart - obj=program_text name=Choose operation executed on clicking stored/new vehicles - obj=program_text name=chooses a random map - obj=program_text name=citicens - obj=program_text name=Cities can only be built on flat empty ground! note=Error message when building city manually - obj=program_text name=City attraction - obj=program_text name=City industries - obj=program_text name=City list - obj=program_text name=City size - obj=program_text name=citybuilding builder - obj=program_text name=CityLimit - obj=program_text name=city_road - obj=program_text name=Clear block reservation - obj=button_text name=clf_btn_alle - obj=button_text name=clf_btn_invers - obj=button_text name=clf_btn_keine - obj=program_text name=clf_chk_indepot - obj=program_text name=clf_chk_noincome - obj=program_text name=clf_chk_noline - obj=program_text name=clf_chk_noroute - obj=program_text name=clf_chk_noschedule - obj=program_text name=clf_chk_spezial_filter - obj=program_text name=clf_chk_stucked - - obj=program_text name=clf_chk_type_filter - obj=program_text name=clf_chk_waren - obj=program_text name=clf_title - obj=program_text name=Climate Control - obj=button_text name=cl_btn_filter_settings - obsolete=button_text name=cl_btn_sort_asc - obsolete=button_text name=cl_btn_sort_desc - obj=button_text name=cl_btn_sort_id - obj=button_text name=cl_btn_sort_income - obj=button_text name=cl_btn_sort_name - obj=button_text name=cl_btn_sort_type - obj=program_text name=cl_title - obj=program_text name=Filter: - obj=program_text name=cl_txt_sort - obj=program_text name=COLOR_CHOOSE\n - obj=program_text name=Company bankrupt - obj=program_text name=Congratulation\nScenario was complete in\n%i months %i years. - obj=program_text name=Connected stops - obj=program_text name=Constructed by - obj=program_text name=Constructed by %s - obj=program_text name=Construction_Btn - obj=program_text name=convoi %d of %d - obj=program_text name=Convoi has been sent\nto the nearest depot\nof appropriate type.\n - obj=program_text name=Convoi is sold when all wagons are empty. - obj=program_text name=convoi passed last\nmonth %i\n - obj=program_text name=Convois - obj=program_text name=Convois: %d\nProfit: %s - obj=program_text name=Convoys - obj=program_text name=Copy Convoi - obj=program_text name=Copy the selected convoi and its schedule or line - obj=program_text name=cost for removal - obj=program_text name=Create a new line based on this schedule - obj=program_text name=curiosity builder - obj=program_text name=curlist_title - obj=program_text name=Currently playing: - obj=error_text name=Das Feld gehoert\neinem anderen Spieler\n - obj=program_text name=Deccelerate time - obj=program_text name=December - obsolete=program_text name=Del Stop - obj=program_text name=Delete Line - obj=program_text name=Delete the current stop - obj=program_text name=Delete this file. - obj=program_text name=Denkmal - obj=program_text name=Departed - obj=program_text name=Depots - obj=error_text name=Depots must be built on flat dead-end way tiles! note=Error text when building depot fails - obj=error_text name=Depots cannot be built on runways! note=Error text when building depot fails - obj=error_text name=Ship depots must be built on water! note=Error text when building depot fails - obj=program_text name=Depot - obj=error_text name=Der Besitzer erlaubt das Entfernen nicht - obj=program_text name=Der Tunnel ist nicht frei!\n - obj=climates_text name=desert - obj=program_text name=Destination - obj=program_text name=Details - obj=error_text name=Diese Zusammenstellung kann nicht fahren!\n - obj=program_text name=diesel - obj=program_text name=Direkt erreichbare Haltestellen - obj=program_text name=Dock - obj=program_text name=Dock must be built on single slope! - obj=program_text name=Du hast %d Monate Zeit, deine Schulden zurueckzuzahlen - obj=program_text name=Durchsatz - obj=menu_text name=EDITTOOLS - obj=program_text name=Eigenbesitz\n - obj=program_text name=Ein %s\npasst hier nicht.\n - obj=program_text name=Einstellungen - obj=program_text name=Einstellungen aendern - obj=program_text name=electric - obj=program_text name=Electricity - obj=program_text name=Electricity producer\n\n - obj=program_text name=Electrify track - obj=program_text name=Error - obj=program_text name=Erzeuge neue Karte.\n - obj=program_text name=Es wird bereits\nein Fahrplan\neingegeben\n - obj=program_text name=Fabrikanschluss - obj=program_text name=Fabrikname - obj=program_text name=Factories - obj=message_text name=Factory chain extended\nfor %s near\n%s built with\n%i factories. - obj=program_text name=factory details - obj=program_text name=factorybuilder - obj=program_text name=Fahrplan - obj=program_text name=Fahrtziel - obj=program_text name=Fahrzeuge koennen so nicht entfernt werden - obj=program_text name=Fahrzeuge: - obj=program_text name=Farbe - obj=program_text name=Fast forward - obj=program_text name=February - obj=program_text name=Ferry_tab - obj=program_text name=Fertig - obj=program_text name=Filename - obj=program_text name=Finances of %s - obj=program_text name=Finanzen - obj=error_text name=Flugzeughalt muss auf\nRunway liegen!\n - obj=program_text name=Flug_tab - obj=program_text name=fl_title - obj=program_text name=follow me - obj=program_text name=Follow the convoi on the map. - obj=program_text name=Forest - obj=program_text name=Found new city - obj=program_text name=FPS: - obj=program_text name=Fracht - obj=program_text name=Frame time: - obj=program_text name=Free Capacity - obj=program_text name=Friction: - obj=program_text name=fuel_cell - obj=program_text name=Full load - obj=program_text name=Fussgaenger - obj=program_text name=GAME PAUSED - obj=program_text name=Gear: - obj=program_text name=Gebaeude - obj=program_text name=Gewicht - obj=program_text name=Gewinn - obj=program_text name=Give the selected vehicle(s) an individual schedule - obj=button_text name=gl_btn_sort_bonus - obj=button_text name=gl_btn_sort_name - obj=button_text name=gl_btn_sort_revenue - obj=button_text name=gl_btn_unsort - obj=program_text name=gl_title - obj=program_text name=go home - obj=program_text name=Goods - obj=program_text name=Goods list - obj=program_text name=Gross Profit - obj=program_text name=Groundobj - obj=program_text name=Grow city - obj=program_text name=Growth - obj=program_text name=H - obj=program_text name=Hangar - obj=program_text name=Happy - obj=program_text name=Display settings - obj=program_text name=Help - obj=program_text name=Help text not found - obj=program_text name=hide all building - obj=program_text name=hide city building - obj=program_text name=hide station names - obj=program_text name=hide transparent - obj=program_text name=hide trees - obj=error_text name=Hier kann dieses\nFlughafengebaeude nicht\ngebaut werden!\n - obj=error_text name=Hier kann kein\nSignal aufge-\nstellt werden!\n - obj=program_text name=Hier warten/lagern: - obj=button_text name=hlf_btn_alle - obj=button_text name=hlf_btn_invers - obj=button_text name=hlf_btn_keine - obj=program_text name=hlf_chk_airport - obj=program_text name=hlf_chk_anleger - obj=program_text name=hlf_chk_bahnhof - obj=program_text name=hlf_chk_bushalt - obj=program_text name=hlf_chk_frachthof - obj=program_text name=hlf_chk_keine_verb - obj=program_text name=hlf_chk_name_filter - obj=program_text name=hlf_chk_overflow - obj=program_text name=hlf_chk_spezial_filter - obj=program_text name=hlf_chk_type_filter - obj=program_text name=hlf_chk_waren_abgabe - obj=program_text name=hlf_chk_waren_annahme - obj=program_text name=hlf_title - obj=button_text name=hl_btn_filter_disable - obj=button_text name=hl_btn_filter_enable - obj=button_text name=hl_btn_filter_settings - obj=button_text name=hl_btn_sort_asc - obj=button_text name=hl_btn_sort_desc - obj=button_text name=hl_btn_sort_name - obj=button_text name=hl_btn_sort_type - obj=button_text name=hl_btn_sort_waiting - obj=program_text name=hl_title - obj=program_text name=hl_txt_filter - obj=program_text name=hl_txt_sort - obj=program_text name=Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually. - obj=program_text name=Homeless - obj=program_text name=hydrogene - obj=program_text name=Idle: - obj=program_text name=ignore climates - obj=program_text name=Increase Industry density - obj=program_text name=industrial building - obj=program_text name=Init map ... - obj=program_text name=Input - obj=program_text name=Ins Stop - obsolete=program_text name=Insert stop before the current stop - obj=program_text name=Intercity road len: - obj=program_text name=Intro. date: - obj=program_text name=Invalid coordinate - obj=program_text name=isometric map - obj=program_text name=January - obj=program_text name=July - obj=program_text name=Jump to - obj=program_text name=June - obj=program_text name=Kann Spielstand\nnicht laden.\n - obj=program_text name=Kann Spielstand\nnicht speichern.\n - obj=program_text name=keine - obj=program_text name=Keine Einzelfahrzeuge im Depot - obj=program_text name=Keyboard_Help\n - obj=program_text name=Kreuzung - obj=program_text name=Lade Relief - obj=program_text name=Laden - obj=program_text name=Land attraction - - obj=program_text name=LANG_CHOOSE\n - obj=program_text name=Last Year - obj=program_text name=leer - obj=program_text name=Legend - obj=program_text name=Leistung - obj=program_text name=Leistung: %d kW - obj=program_text name=Leitung - obj=program_text name=letzen Monat: diesen Monat: - obj=program_text name=Line - obj=program_text name=Line Management - obj=program_text name=Lines serving this stop - obj=menu_text name=LISTTOOLS - obj=program_text name=LKW_tab - obj=program_text name=Load game - obj=program_text name=load height data from file - obj=program_text name=Load scenario - obj=program_text name=loaded - obj=program_text name=loaded passenger/freight - obj=program_text name=Loading map ... - obj=program_text name=Lock game - obj=program_text name=Lokomotive_tab - obj=program_text name=m3 - obj=error_text name=Maglevhalt muss auf\nMaglevschiene liegen!\n - obj=program_text name=Mailbox - obj=program_text name=Mailbox Options - obj=program_text name=Maintenance - obj=program_text name=Manufactured: - obj=program_text name=Map roughness - obj=program_text name=map zoom - obj=program_text name=March - obj=program_text name=Margin (%%) - obj=program_text name=Marker - obj=program_text name=max - obj=program_text name=Max income: - obj=program_text name=Max. speed: - obj=program_text name=Maximum 254 stops\nin a schedule!\n - obj=program_text name=Maximum tile height difference reached. - obj=program_text name=May - obj=program_text name=Median Citizen per town - obj=climates_text name=mediterran - obj=program_text name=Meldung - obj=program_text name=Menge - obj=program_text name=MessageOptionsText - obj=program_text name=min - obj=program_text name=Modify the selected line - obj=program_text name=Monorail - obj=program_text name=monorail vehicle - obj=program_text name=Monorailboden - obj=program_text name=Monoraildepot - obj=error_text name=Monorailhalt muss auf\nMonorail liegen!\n - obj=error_text name=Monorails are not available yet! - obj=menu_text name=MONORAILTOOLS - obsolete=program_text name=month wait time - obj=program_text name=Months - obj=program_text name=Monument - obj=program_text name=Monuments - obj=program_text name=Mountain height - obj=program_text name=Movingobj - obj=program_text name=Music playing disabled/not available - obj=program_text name=Music volume: - obj=program_text name=mute sound - obj=program_text name=Name - obj=program_text name=Net Wealth - obj=program_text name=Neue Karte - obj=program_text name=Neue Welt - obj=message_text name=New %s now available:\n%s\n - obj=program_text name=new convoi - obj=message_text name=New factory chain\nfor %s near\n%s built with\n%i factories. - obj=program_text name=New Line - obj=program_text name=New line created!\nYou can assign the line now\nby selecting it from the\nline selector above. - obj=message_text name=New vehicle now available:\n%s\n - obj=program_text name=New Vehicles - obj=record_text name=New world record for monorails: %.1f km/h by %s. - obj=record_text name=New world record for motorcars: %.1f km/h by %s. - obj=record_text name=New world record for planes: %.1f km/h by %s. - obj=record_text name=New world record for railways: %.1f km/h by %s. - obj=record_text name=New world record for ship: %.1f km/h by %s. - obj=record_text name=New world record for narrowgauges: %.1f km/h by %s. - obj=record_text name=New world record for maglevs: %.1f km/h by %s. - obj=program_text name=no convois - obj=program_text name=No goods are loaded onto this convoi. - obj=program_text name=no goods waiting - obj=program_text name=no load - obj=program_text name=No Route - obj=program_text name=No suitable ground! - obj=program_text name=No suitable townhall available for this climate! note=Error message when building city manually - obj=program_text name=No terminal station here! - obj=error_text name=No through station here! - obj=program_text name=no timeline - obj=program_text name=no tree - obj=program_text name=Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n - obj=program_text name=none - obj=program_text name=nord - obj=program_text name=nordost - obj=program_text name=nordwest - obj=program_text name=Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later! - obj=program_text name=Not enough fields would remain. - obj=program_text name=November - obj=program_text name=Now active as %s.\n - obj=program_text name=Object - obj=program_text name=Ok - obj=program_text name=Oktober - obj=program_text name=On loan since %i month(s) - obj=error_text name=On narrowgauge track only!\n - obj=program_text name=On this map, you are not\nallowed to change player!\n - obj=program_text name=Only city chains - obj=program_text name=Only land chains - obj=program_text name=Operation - obj=program_text name=Ops Profit - obj=program_text name=Optionen - obj=program_text name=Origin - obj=program_text name=ost - obj=program_text name=Output - obj=program_text name=paletten - obj=program_text name=Passagiere - obj=program_text name=Passagierrate - obj=program_text name=Passengers %d %c, %d %c, %d no route - obj=program_text name=Pas_tab - obj=program_text name=Pause - obj=program_text name=Percent Electricity - obj=program_text name=Planes are not available yet! - obj=program_text name=Plant tree - obj=program_text name=player -1 - obj=program_text name=player 0 - obj=program_text name=player 1 - obj=program_text name=player 2 - obj=program_text name=player 3 - obj=program_text name=player 4 - obj=program_text name=player 5 - obj=program_text name=player 6 - obj=program_text name=player 7 - obj=program_text name=player 8 - obj=program_text name=player 9 - obj=program_text name=player 10 - obj=program_text name=player 11 - obj=program_text name=player 12 - obj=program_text name=player 13 - obj=program_text name=Please choose vehicles first\n - obj=program_text name=Post - obj=error_text name=Post muss neben\nHaltestelle\nliegen!\n - obj=program_text name=Postrate - obj=program_text name=Power - obj=program_text name=Power: - obj=program_text name=Powerlines - obj=program_text name=Produktion - obj=program_text name=Profit - obj=program_text name=promote to line - obj=program_text name=q1 - obj=program_text name=q2 - obj=program_text name=q3 - obj=program_text name=q4 - obj=program_text name=rail car - obj=menu_text name=RAILTOOLS - obj=program_text name=random - obj=program_text name=Random age - obj=program_text name=Random map - obj=program_text name=Rathaus - obj=program_text name=Rating - obj=program_text name=ratio_pax - obj=program_text name=Reliefkarte - obj=program_text name=remove airstrips - obj=program_text name=remove channels - obj=program_text name=remove monorails - obj=program_text name=remove roads - obj=program_text name=remove tracks - obj=program_text name=remove powerlines - obj=message_text name=Remove vehicle from map. Use with care! - obj=program_text name=replace stop - obj=program_text name=residential house - obj=program_text name=Restore natural slope - obj=program_text name=Restwert: - obj=program_text name=Retire. date: - obj=program_text name=return ticket - obj=program_text name=Revenue - obj=program_text name=road vehicle - obj=program_text name=Roadsign - obj=menu_text name=ROADTOOLS - obj=climates_text name=rocky - obj=program_text name=Rotate map - obj=program_text name=Rotation - obj=program_text name=sack - obj=program_text name=sail - obj=program_text name=Saving map ... - obj=program_text name=Scenario complete: %i%% - obj=program_text name=Schienentunnel - obj=program_text name=Schiffdepot - obj=error_text name=Schiffhalt muss im\nWasser liegen!\n - obj=program_text name=Schiff_tab - obj=program_text name=Schleppkahn_tab - obj=program_text name=Screenshot - obj=message_text name=Screenshot\ngespeichert.\n - obj=program_text name=Sehenswuerdigkeit - obj=program_text name=Sell the selected vehicle(s) - obj=program_text name=sended - obj=message_text name=Sends the convoi to the last depot it departed from! - obj=program_text name=September - obj=program_text name=SEP_FRACTION - obj=program_text name=SEP_THOUSAND - obj=program_text name=Serves Line: - obj=program_text name=Service - obj=program_text name=Ship - obj=menu_text name=SHIPTOOLS - obj=program_text name=shops and stores - obj=program_text name=Show all - obj=program_text name=show all building - obj=program_text name=Show also vehicles no longer in production. - obj=program_text name=Show also vehicles that do not match for current action. - obj=program_text name=show grid - obj=program_text name=show HQ note=finance window - obj=program_text name=Show industry - obj=program_text name=Show legend - obj=program_text name=Show map scale - obj=program_text name=Show obsolete - obj=program_text name=Show schedules - obj=program_text name=show station coverage - obj=program_text name=show waiting bars - obj=program_text name=Show/hide statistics - obj=program_text name=Shows consumer/suppliers for factories - obj=program_text name=Shows the currently selected schedule - obj=program_text name=Shrink city - obj=program_text name=shuffle midis - obj=program_text name=Sim: - obj=menu_text name=SLOPETOOLS - obj=program_text name=Sort by - obj=program_text name=Sort waiting list by - obj=program_text name=Sound - obj=program_text name=Sound settings - obj=program_text name=Sound volume: - obj=program_text name=special freight - obj=menu_text name=SPECIALTOOLS - obj=program_text name=Speedlimit - obj=program_text name=Save note=button and window title - obj=program_text name=Spieler - obj=program_text name=Spieler(mz) - obj=program_text name=Spielerliste - obj=program_text name=Spielstand wurde\ngeladen!\n - obj=program_text name=Spielstand wurde\ngespeichert!\n - obj=program_text name=Sprache - obj=program_text name=Sprachen - obj=program_text name=Stadtinformation - obj=program_text name=Start - obj=program_text name=Start the selected vehicle(s) - obj=program_text name=Starte Spiel - obj=program_text name=Station tiles: - obj=program_text name=Status - obj=program_text name=steam - obj=program_text name=Step timeline one year - obj=program_text name=Storage capacity - obj=program_text name=Strassendepot - obj=program_text name=Strassentunnel - obj=program_text name=street car - obj=program_text name=sued - obj=program_text name=suedost - obj=program_text name=suedwest - obj=program_text name=Summer snowline - obj=program_text name=Suppliers - obj=program_text name=Tage alt - obj=climates_text name=temperate - obj=program_text name=There are still vehicles\nstored in this depot!\n - obj=program_text name=This Month - obj=program_text name=This Year - obj=program_text name=Tile not empty. - obj=program_text name=timeline - obj=program_text name=tl_title - obj=program_text name=To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers. - obj=program_text name=To heavy traffic\nresults in traffic jam.\n - obj=program_text name=tonnen - obj=program_text name=Total inhabitants: - obj=program_text name=Tourist attractions - obj=program_text name=Tourists - obj=program_text name=Town: %s\n - obj=program_text name=Towns - obj=program_text name=Tracks - obj=program_text name=Traffic - obj=program_text name=Train - obj=program_text name=Trains are not available yet! - obj=program_text name=Tram - obj=program_text name=Tramdepot - obj=program_text name=Trams are not available yet! - obj=menu_text name=TRAMTOOLS - obj=program_text name=Transformer only next to factory! - obj=program_text name=Transformer only on flat bare land! - obj=program_text name=Transformers cannot connect to factories on water. - obj=program_text name=Translation - obj=program_text name=transparent station coverage - obj=program_text name=Transported - obj=climates_text name=tropic - obj=program_text name=Truck - obj=climates_text name=tundra - obj=program_text name=Tunnel muss an\neinfachem\nHang beginnen!\n - obj=program_text name=Tunnel must start on single way! - obj=program_text name=Tunnelboden - obj=program_text name=underground mode - obj=program_text name=UNDO failed! - obj=program_text name=Undo last ways construction - obj=program_text name=Unemployed - obj=program_text name=Unhappy - obj=program_text name=units/day - obj=program_text name=Update Line - obj=program_text name=upgrade HQ note=HQ info window - obj=program_text name=Use timeline start year - obj=program_text name=Verbrauch - obj=program_text note=button in convoi dialogue to remove a convoi completely name=Verkauf - obj=program_text name=verkaufen - obj=program_text name=Verkehrsteilnehmer - obj=program_text name=Verschuldet:\n\nDu hast %d Monate Zeit,\ndie Schulden zurueckzuzahlen.\n - obj=program_text name=via - obj=program_text name=via %s\n - obj=program_text name=via Menge - obj=program_text name=voranstellen - obj=program_text name=Waggon_tab - obj=program_text name=waiting - obj=program_text name=Water level - obj=program_text name=water vehicle - obj=program_text name=way %s cannot longer used:\n - obj=program_text name=way %s cannot longer used:\n%s\n - obj=program_text name=way %s now available:\n - obj=program_text name=Ways not connected - obj=program_text name=Wegpunkt - obj=program_text name=Wert - obj=program_text name=west - obj=program_text name=Winter snowline - obj=message_text name=With a big festival\n%s built\na new monument.\n%i citicens rejoiced. - obj=program_text name=withdraw - obj=program_text name=WRONGSAVE - obj=program_text name=Year %i has started. - obj=program_text name=Years - obj=program_text name=Zielort - obj=program_text name=zooming in - obj=program_text name=zooming out - obj=program_text name=Zu nah am Kartenrand - obj=error_text name=Zughalt muss auf\nSchiene liegen!\n - obj=program_text name=\nBauzeit bis - obj=program_text name=\nBauzeit von - obj=program_text name=\nCan't open heightfield file.\n - obj=program_text name=\ndirection: - obj=program_text name=\nelektrified - obj=program_text name=\nHeightfield has wrong image type.\n - obj=program_text name=\nis reserved by: - obj=program_text name=\nminimum speed: - obj=program_text name=\nnot elektrified - obj=program_text name=\nRibi (masked) - obj=program_text name=\nsingle way - obj=program_text name=\nwith sign/signal\n - obj=program_text name=Passagierziele - obj=program_text name=Production of %s has been stopped:\n%s\n - obj=unnecessary_text name=6suburb - obj=unnecessary_text name=7extern - obj=unnecessary_text name=7suburb - obj=unnecessary_text name=8extern - obj=unnecessary_text name=8suburb - obj=unnecessary_text name=9extern - obj=ki_text name=%s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n - obj=ki_text name=%s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n - obj=ki_text name=%s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i). - obj=ki_text name=%s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i). - obj=ki_text name=Airline service by\n%s\nnow between\n%s \nand %s.\n -- obj=program_text name=Add forest -- obj=program_text name=Build narrowgauge depot - obj=menu_text name=NARROWGAUGETOOLS - obj=program_text name=remove narrowgauge tracks - obj=program_text name=Narrowgauge - obj=program_text name=narrowgauge vehicle - obj=program_text name=Narrowgaugedepot - obj=error_text name=Narrowgaugehalt muss auf\nNarrowgauge liegen!\n - obj=program_text name=Narrowgauge are not available yet! - obj=program_text name=Speedbonus\nroad %i km/h, rail %i km/h\nships %i km/h, planes %i km/h. - obj=program_text name=tram %i km/h, monorail %i km/h\nmaglev %i km/h, narrowgauge %i km/h. - obj=program_text name=\nRibi (unmasked) - obj=program_text name=\nway1 reserved by - obj=program_text name=\nway2 reserved by - obj=message_text name=%s s\nheadquarter now\nat (%i,%i). - obj=program_text name=Build air depot - obj=program_text name=Build maglev depot - obj=program_text name=cars.\nstate - obj=program_text name=clf_chk_monorail - obj=program_text name=clf_chk_narrowgauge - obj=program_text name=closed - obj=program_text name=disable midi - obj=program_text name=Electrics_tab - obj=program_text name=enlarge map - obj=program_text name=enter a value between %i and %i - obj=ki_text name=Ferry service by\n%s\nnow between\n%s \nand %s.\n - obj=program_text name=freeplay mode - obj=program_text name=Goods AI - obj=program_text name=hlf_chk_maglevstop - obj=program_text name=hlf_chk_monorailstop - obj=program_text name=hlf_chk_narrowgaugestop - obj=program_text name=hlf_chk_tramstop - obj=program_text name=invalid - obj=program_text name=LARGE_NUMBER_STRING - obj=program_text name=LARGE_NUMBER_VALUE - obj=program_text name=Make way or stop public (will join with neighbours), %i times maintainance - obj=program_text name=Manual (Human) - obj=program_text name=No stop here! - obj=program_text name=open - obj=program_text name=Passenger AI - obj=program_text name=Remove - obj=program_text name=remove maglev tracks - obj=program_text name=request closing - obj=program_text name=slot empty - obj=program_text name=Toggle day/night view - obj=ki_text name=Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n - obj=program_text name=TrolleyBus_tab - obj=program_text name=Vergroessere die Karte\n - obj=program_text name=clf_chk_maglev -- obj=unnecessary_text name=&0_CITY_SYLL -- obj=unnecessary_text name=&B_CITY_SYLL -- obj=unnecessary_text name=&C_CITY_SYLL -- obj=unnecessary_text name=&D_CITY_SYLL -- obj=unnecessary_text name=&E_CITY_SYLL -- obj=unnecessary_text name=&F_CITY_SYLL -- obj=unnecessary_text name=%G_CITY_SYLL - obj=unnecessary_text name=%H_CITY_SYLL - obj=unnecessary_text name=%I_CITY_SYLL - obj=unnecessary_text name=%J_CITY_SYLL - obj=unnecessary_text name=%K_CITY_SYLL - obj=unnecessary_text name=%L_CITY_SYLL - obj=unnecessary_text name=%M_CITY_SYLL - obj=unnecessary_text name=%N_CITY_SYLL - obj=unnecessary_text name=%O_CITY_SYLL - obj=unnecessary_text name=%P_CITY_SYLL - obj=unnecessary_text name=%Q_CITY_SYLL - obj=unnecessary_text name=%R_CITY_SYLL - obj=unnecessary_text name=%S_CITY_SYLL - obj=unnecessary_text name=%T_CITY_SYLL - obj=unnecessary_text name=%U_CITY_SYLL - obj=unnecessary_text name=%V_CITY_SYLL - obj=unnecessary_text name=%W_CITY_SYLL - obj=unnecessary_text name=%X_CITY_SYLL - obj=unnecessary_text name=%Y_CITY_SYLL - obj=unnecessary_text name=%Z_CITY_SYLL - obj=unnecessary_text name=&G_CITY_SYLL - obj=unnecessary_text name=&H_CITY_SYLL - obj=unnecessary_text name=&I_CITY_SYLL - obj=unnecessary_text name=&J_CITY_SYLL - obj=unnecessary_text name=&K_CITY_SYLL - obj=unnecessary_text name=&L_CITY_SYLL - obj=unnecessary_text name=&M_CITY_SYLL - obj=unnecessary_text name=&N_CITY_SYLL - obj=unnecessary_text name=&O_CITY_SYLL - obj=unnecessary_text name=&P_CITY_SYLL - obj=unnecessary_text name=&Q_CITY_SYLL - obj=unnecessary_text name=&R_CITY_SYLL - obj=unnecessary_text name=&S_CITY_SYLL - obj=unnecessary_text name=&T_CITY_SYLL - obj=unnecessary_text name=&U_CITY_SYLL - obj=unnecessary_text name=&V_CITY_SYLL - obj=unnecessary_text name=&W_CITY_SYLL - obj=unnecessary_text name=&X_CITY_SYLL - obj=unnecessary_text name=&Y_CITY_SYLL - obj=unnecessary_text name=&Z_CITY_SYLL - obj=unnecessary_text name=0center -- obj=unnecessary_text name=0extern -- obj=unnecessary_text name=0suburb -- obj=program_text name=Die Bruecke ist nicht frei!\n -- obj=program_text name=Maglev -- obj=program_text name=Maglevdepot -- obj=program_text name=Number of rivers -- obj=program_text name=minimum length of rivers -- obj=program_text name=maximum length of rivers -- obj=program_text name=maglev vehicle -- obj=program_text name=labellist_title -- obj=program_text name=Active player only -- obj=program_text name=koord note=sort option in halt and depot list -- obj=program_text name=gl_btn_sort_catg note=sort option for goods_list window -- obj=program_text name=Leaving depot! -- obj=program_text name=Schedule changing! -- obj=program_text name=Waiting for clearance! -- obj=program_text name=Withdraw All -- obj=program_text name=sliced underground mode -- obj=program_text name=increase underground view level -- obj=program_text name=decrease underground view level -- obj=error_text name=Cannot built this station/building\nin underground mode here. -- obj=error_text name=Terraforming not possible\nhere in underground view -- obj=program_text name=Remove wayobj %s - obj=program_text name=[HOME] - obj=program_text name=[END] - obj=program_text name=[CTRL] - obj=program_text name=[SHIFT] - obj=program_text name=[ESC] - obj=program_text name=[DELETE] - obj=program_text name=[SCROLLLOCK] - obj=error_text name=Upgrade must have\na higher level - obj=error_text name=Not enough money! - obj=program_text name=Allow city growth - obj=program_text name=Setting - obj=program_text name=General - obj=program_text name=Economy - obj=program_text name=Routing - obj=program_text name=Costs - obj=error_text name=Cannot create socket - obj=error_text name=Protocoll error (expecting game) - obj=program_text name=Distance - obj=program_text name=Odometer: %s km - obj=error_text name=Lost synchronisation\nwith server. - obj=error_text name=Lost connection\nto server! - obj=program_text name=\nSet phases: - obj=program_text name=%s factory %s %s - obj=unnecessary_text name=Asuburb - obj=unnecessary_text name=Bsuburb - obj=unnecessary_text name=Csuburb - obj=unnecessary_text name=Dsuburb - obj=unnecessary_text name=Esuburb - obj=unnecessary_text name=Fsuburb - obj=unnecessary_text name=Gsuburb - obj=unnecessary_text name=Hsuburb - obj=unnecessary_text name=Isuburb - obj=unnecessary_text name=Jsuburb - obj=unnecessary_text name=Ksuburb - obj=unnecessary_text name=Lsuburb - obj=unnecessary_text name=Msuburb - obj=unnecessary_text name=Nsuburb - obj=unnecessary_text name=Osuburb - obj=unnecessary_text name=Psuburb - obj=unnecessary_text name=Qsuburb - obj=unnecessary_text name=Rsuburb - obj=unnecessary_text name=Ssuburb - obj=unnecessary_text name=Tsuburb - obj=unnecessary_text name=Usuburb - obj=unnecessary_text name=Vsuburb - obj=unnecessary_text name=Wsuburb - obj=unnecessary_text name=Xsuburb - obj=unnecessary_text name=Ysuburb - obj=unnecessary_text name=Zsuburb - obj=unnecessary_text name=Acenter - obj=unnecessary_text name=Bcenter - obj=unnecessary_text name=Ccenter - obj=unnecessary_text name=Dcenter - obj=unnecessary_text name=Ecenter - obj=unnecessary_text name=Fcenter - obj=unnecessary_text name=Gcenter - obj=unnecessary_text name=Hcenter - obj=unnecessary_text name=Icenter - obj=unnecessary_text name=Jcenter - obj=unnecessary_text name=Kcenter - obj=unnecessary_text name=Lcenter - obj=unnecessary_text name=Mcenter - obj=unnecessary_text name=Ncenter - obj=unnecessary_text name=Ocenter - obj=unnecessary_text name=Pcenter - obj=unnecessary_text name=Qcenter - obj=unnecessary_text name=Rcenter - obj=unnecessary_text name=Scenter - obj=unnecessary_text name=Tcenter - obj=unnecessary_text name=Ucenter - obj=unnecessary_text name=Vcenter - obj=unnecessary_text name=Wcenter - obj=unnecessary_text name=Xcenter - obj=unnecessary_text name=Ycenter - obj=unnecessary_text name=Zcenter - obj=unnecessary_text name=Aextern - obj=unnecessary_text name=Bextern - obj=unnecessary_text name=Cextern - obj=unnecessary_text name=Dextern - obj=unnecessary_text name=Eextern - obj=unnecessary_text name=Fextern - obj=unnecessary_text name=Gextern - obj=unnecessary_text name=Hextern - obj=unnecessary_text name=Iextern - obj=unnecessary_text name=Jextern - obj=unnecessary_text name=Kextern - obj=unnecessary_text name=Lextern - obj=unnecessary_text name=Mextern - obj=unnecessary_text name=Nextern - obj=unnecessary_text name=Oextern - obj=unnecessary_text name=Pextern - obj=unnecessary_text name=Qextern - obj=unnecessary_text name=Rextern - obj=unnecessary_text name=Sextern - obj=unnecessary_text name=Textern - obj=unnecessary_text name=Uextern - obj=unnecessary_text name=Vextern - obj=unnecessary_text name=Wextern - obj=unnecessary_text name=Xextern - obj=unnecessary_text name=Yextern - obj=unnecessary_text name=Zextern - obj=program_text name=SEP_THOUSAND_EXPONENT - obj=program_text name=Transferring game ... note=transfer game over network - obj=program_text name=Lineless convoys serving this stop note=halt detail window - obj=program_text name=Net ID: %p note=powernet info window - obj=program_text name=Capacity: %.0f MW note=powernet info window - obj=program_text name=Demand: %.0f MW note=powernet info window - obj=program_text name=Generation: %.0f MW note=powernet info window - obj=program_text name=Usage: %.0f %% note=powernet info window - obj=program_text name=Supplied: %.0f %% note=powernet info window - obj=program_text name=%i years %i months old. note=tree info dialog - obj=program_text name=Only one transformer per factory! - obj=message_text name=Now %u clients connected. - obj=program_text name=Revision: - obj=program_text name=%u Player (%u locked)\n - obj=program_text name=%u Client(s)\n - obj=program_text name=join game - obj=program_text name=Show even servers with wrong version or pakset - obj=program_text name=add server - obj=program_text name=set signal spacing - obj=program_text name=signal spacing - obj=program_text name=remove interm. signals - obj=program_text name=replace other signals - obj=program_text name=Connected with server - obj=program_text name=Chat_msg - obj=program_text name=Problems_msg - obj=program_text name=Warnings_msg - obj=program_text name=Station_msg - obj=program_text name=Town_msg - obj=program_text name=Company_msg - obj=program_text name=Game_msg - obj=program_text name=Signal note=title of signal info window --- obj=program_text name=Game info note=title of play-online window --- obj=program_text name=find mismatch note=play-online window: button to compare paksets of client and server --- obj=program_text name=Aircraft note=tooltip in linemanagement window --- obj=program_text name=Wasser note=title of tile-info window of water tiles --- obj=program_text name=Comparing pak files ... --- obj=program_text name=Pakset differences --- obj=program_text name=Pak(s) not on server: --- obj=program_text name=Pak(s) different: --- obj=program_text name=Pak(s) missing on client: --- obj=program_text name=Only first %d differing paks reported. There are probably more. --- obj=program_text name=Enter Password note=title of cmpany name/password dialoge --- obj=program_text name=Password --- obj=program_text name=%s at (%i,%i) now public stop. --- obj=program_text name=deactivated in online mode --- obj=program_text name=Use beginner mode --- obj=program_text name=Higher transport fees, crossconnect all factories --- obj=button_text name=Road toll --- obj=program_text name=Pax <%i> Mail <%i> -- obj=program_text name=Production/Boost -- obj=program_text name=Boost (%%) -- obj=program_text name=Max Boost (%%) -- obj=program_text name=Demand -- obj=program_text name=Power (MW) -- obj=program_text name=Storage -- obj=program_text name=Consumed -- obj=program_text name=Produced -- obj=program_text name=Generated -- obj=error_text name=Server busy -- obj=error_text name=Only public player can lock games! -- obj=program_text name=Destroying map ... -- obj=program_text name=Passenger Demand %d\n -- obj=program_text name=Mail Demand %d\n -- obj=program_text name=Customers live in: -- obj=program_text name=Enter address -- obj=program_text name=show/hide block reservations -- obj=program_text name=show/hide object owner -- obj=program_text name=Walked -- obj=program_text name=Configure AI setttings -- obj=program_text name=Configure AI -- obj=program_text name=construction speed -- obj=program_text name=Smart hide objects -- obj=program_text name=hide objects under cursor -- obj=program_text name=Show only used -- obj=program_text name=In the industry legend show only currently existing factories -- obj=program_text name=Only show goods which are currently handled by factories -- obj=program_text name=clf_chk_obsolete -- obj=program_text name=Relevant note=depot window: show vehicles that transport goods produced by factories in game -- obj=program_text name=Missing pakfiles -- obj=program_text name=Pak which may cause severe errors: -- obj=program_text name=Pak which may cause visual errors: -- obj=program_text name=Bonusspeed: %i km/h note=maximum speed possible on a route. This is used for bonus calculation -- obj=program_text name=Maxspeed note=button with statistics of possible maxspeed of a convoi during all trips -- obj=program_text name=Ownership note=button in minimap to show owner -- obj=program_text name=player note=button in marker list sorting -- obj=help_text name=Please click on the map to add\nwaypoints or stops to this\nschedule. note=Shown when a schedule is empty. -- obj=program_text name=Highlite schedule note=button in visual setting dialog -- obj=program_text name=Similar view as the main window note=tooltip in minimap -- #obj=program_text #name=List of industries on the map #note=tooltip in minimap -- obj=program_text name=Shows the color code for several selections. note=tooltip in minimap -- obj=program_text name=Shows buttons on special topics. note=tooltip in minimap -- obj=program_text name=Delete the selected line (if without associated convois). note=tooltip in line schedule -- obj=obsolete name=Line Filter note=text in lin window for searching lines containing -- obj=program_text name=Last Month note=text in finance window -- obj=program_text name=Your primary color: note=text in player color choose dialog -- obj=program_text name=Your secondary color: note=text in player color choose dialog -- name=Show servers that are offline obj=program_text note=Tooltip in server information window --- name=Show mismatched obj=program_text note=Option in server information window --- name=Show offline obj=program_text note=Option in server information window --- name=Server did not respond! obj=program_text note=Server information window --- name=Query server obj=program_text note=Server information window --- name=Cannot connect to offline server! obj=program_text note=Server information window --- name=Nickname: obj=program_text --- name=Or enter a server manually: obj=program_text note=Server information window --- name=Show servers where game version or pakset does not match your client obj=program_text note=Tooltip in server information window --- name=Select a server to join: obj=program_text note=Server information window --- obj=error_text name=Vehicle %s cannot choose because stop too short! note=message at choose signs when the first stop is too short --- obj=help_text name=

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s note=string of which the index will be built -- obj=program_text name=Net wealth near zero note=text when net wealth nearly consumed -- obj=button_text name=Networks note=show network overlay in minimap -- obj=button_text name=Queueing note=show station waiting number change overlay in minimap -- obj=button_text name=Transfers note=show station arrival+departure overlay in minimap -- obj=help_text name=Overlay town names note=tooltip in minimap -- obj=help_text name=Overlay city limits note=tooltip in minimap -- obj=help_text name=Show level of city buildings note=tooltip in minimap -- obj=help_text name=Overlay passenger destinations when a town window is open note=tooltip in minimap -- obj=help_text name=Highlite tourist attraction note=tooltip in minimap -- obj=help_text name=Highlite factories note=tooltip in minimap -- obj=help_text name=Overlay schedules/network note=tooltip in minimap -- obj=help_text name=Show passenger coverage/passenger network note=tooltip in minimap -- obj=help_text name=Show mail service coverage/mail network note=tooltip in minimap -- obj=help_text name=Show transported freight/freight network note=tooltip in minimap -- obj=help_text name=Show capacity and if halt is overcrowded note=tooltip in minimap -- obj=help_text name=Show how many people/much is waiting at halts note=tooltip in minimap -- obj=help_text name=Show the change of waiting at halts note=tooltip in minimap -- obj=help_text name=Show how many convoi reach a station note=tooltip in minimap -- obj=help_text name=Sum of departure/arrivals at halts note=tooltip in minimap -- obj=help_text name=Show initial passenger departure note=tooltip in minimap -- obj=help_text name=Show usage of network note=tooltip in minimap -- obj=help_text name=Show speedlimit of ways note=tooltip in minimap -- obj=help_text name=Highlight railroad tracks note=tooltip in minimap -- obj=help_text name=Highlite depots note=tooltip in minimap -- obj=help_text name=Highlite electrical transmission lines note=tooltip in minimap -- obj=help_text name=Highlite forests note=tooltip in minimap -- obj=help_text name=Show the owenership of infrastructure note=tooltip in minimap -- obj=help_text name=Shows a listing with all industries on the map. note=tooltip in minimap -- obj=error_text name=Out of funds note=Shown when an action is forbidden because of negative net wealth -- obj=error_text name=No suitable way on the ground! note=show when building stations without ways/on slopes/on tunnels -- obj=button_text name=Scenario - obj=error_text name= Loading scenario script failed - obj=program_text note=tab in scenario info window name=Scenario Info - obj=program_text note=tab in scenario info window name=Scenario Goal - obj=program_text note=tab in scenario info window name=Scenario Rules - obj=program_text note=tab in scenario info window name=Scenario Result - obj=program_text note=tab in scenario info window name=About scenario - obj=program_text note=tab in scenario info window name=Scenario Error Log - obj=program_text note=tab in scenario info window name=Scenario Debug - obj=program_text note=Title of scenario info dialogue name=Scenario information - obj=program_text note=button in map creation dialoge to set number of factories name=No. of Factories - obj=program_text note=item in depot line selector name= - obj=program_text note=item in depot line selector name= - obj=program_text note=item in depot line selector name= - obj=program_text note=item in depot line selector name= - obj=program_text note=item in depot line selector name= - obj=error_text name=Can't buy obsolete vehicles! - obj=program_text note=info in depot name=Cost: %8s (%.2f$/km)\n - obj=program_text note=info in depot name=Power: %4d kW\n - obj=program_text note=info in depot name=Weight: - obj=program_text note=info in depot name=Capacity: - obj=program_text note=info in depot name=1 convoi - obj=program_text note=factory chart for goods in transit name=In Transit - obj=program_text name=Files from: note=open savegame window - obj=program_text name=distributing factories note=for progress bar - obj=program_text name=distributing cities note=for progress bar - obj=program_text note=finance window: misc. transport type name=tt_Other - obj=program_text note=halt window, departure board name=Departure board - obj=program_text note=halt window, departure board name=Show/hide estimated arrival times - obj=program_text note=halt window, departure board name=Departures to\n - obj=program_text note=halt window, departure board name=Arrivals from\n - obj=program_text note=halt window, departure board name=now - obj=program_text note=Way toll button in charts name=Way toll - obj=program_text note=Char(s) to indicate truncated text name=... - obj=program_text note=title/tooltip of thememanager name=Select a theme for display - obj=help_text note=tooltext to indicate climate conversion of a tile name=Set tile climate %s - obj=help_text note=tooltext for filling a bassin name=Increase water height - obj=help_text note=tooltext for draining a bassin name=Decrease water height - obj=error_text note=Failure or drainign/filling a bassin name=Cannot alter water - obj=program_text name=Cost: %8s (%.2f$/km %.2f$/m)\n note=Text in depot for convoi with monthly fixed cost - obj=error_text name=Cannot connect to the\ncenter of a double slope! note=Bridges ending in the middle of a slope - obj=error_text name=Cannot build on a double slope! - obj=error_text name=No bridges over runways! - obj=program_text name=Water note=Climate name of water areas - obj=error_text name=This tunnel branches. You can try Control+Click to remove. - obj=program_text name=nowhere - obj=help_text name=Color according to transport capacity left note=tooltip in minimap window - obj=error_text name=Not enough clearance. note=Error when building a way with not enough clearance above or below. -- obj=message_text name=Net wealth less than 10%% of starting capital! -- obj=message_text name=Server preparing game ... note=Shown while server is saving the game -- obj=program_text name=Theme selector note=Loading a GUI theme dialog -- obj=program_text name=Last used tools note=Toolbar with the last used tools -- obj=help_text name=Open station/stop details note=Tooltip -- obj=program_text name=Insufficient funds! note=text for when not enough funds are owned to use a tool - obj=program_text name=Trees disabled! note=text for when a tool fails due to trees being disabled - obj=program_text name=Not allowed to make publicly owned ways! note=text for when trying to make a public way when public ways are disabled - obj=error_text name=Server version too new note=Error message when questioning future server versions - obj=program_text name=Produces: %.1f units/minute note=Factory production per minute text - obj=program_text name=%s %.0f%% : %.0f/%.0f @ %.1f%% note=JIT2 factory output formater - obj=program_text name=%s %.0f%% : %.0f+%u/%.0f @ %.1f%% note=JIT2 factory input formater - obj=program_text name=%i car(s), note=length of assembled convoi in depot frame, only shown if more than one - obj=error_text name=Loading a new game will end the current server session! - obj=button_text name=Start this as a server note=used in loading games/scenario dialoge - obj=error_text name=More than one possibility to build this dock found. - obj=error_text name=Bridge blocked by way below or above. note=height clearance not enough for a bridge at this position - obj=program_text name=Select display font - obj=program_text name=font size - obj=program_text name=Only full Unicode fonts - obj=error_text name=Cannot rotate this building! - obj=error_text name=Not enough bytes transferred note=server did not send enough bytes when transmitting a game - obj=error_text name=Client closed connection during transfer - obj=error_text name=Show all revenue messages - obj=error_text name=Show only player's revenue - obj=error_text name=Show no revenue messages - obj=program_text name=length: %d - obj=program_text name=

Error

- obj=program_text name=contains the following doubled objects:

- obj=program_text name=

Warning

addons for - obj=program_text name=Connections note=Tab in factory window/station dialog - obj=program_text name=Freight note=Tab in convoy-info window - obj=program_text name=Scenario_ note=in finance window - obj=program_text name=Show finances for transport type note=in finance window - obj=program_text name=Size (%d MB): note=in enlarge map and new map window - obj=error_text name=Too far away to merge stations! - obj=program_text name=Bonus Speed: %i km/h note=Goods frame current average speed of line type - obj=program_text name=Bonus Multiplier: %i%% note=Goods frame speed bonus multiplier in percent - obj=program_text name=No vehicles are available for purchase. note=Goods frame displayed if no vehicles of the selected line type are available for purchase - obj=program_text name=GUI settings note=tab in display setting - obj=program_text name=map view note=tab in display setting - obj=program_text name=transparencies note=tab in display setting - obj=program_text name=station labels note=tab in display setting - obj=program_text name=traffic settings note=tab in display setting - obj=program_text name=Convoi following mode note=label in trafic display setting - obj=button_text name=TOOL_SOUND note=label in sound frame - obj=button_text name=TRAFFIC_SOUND note=label in sound frame - obj=button_text name=AMBIENT_SOUND note=label in sound frame - obj=button_text name=FACTORY_SOUND note=label in sound frame - obj=button_text name=CROSSING_SOUND note=label in sound frame - obj=button_text name=CASH_SOUND note=label in sound frame - obj=button_text name=Sound range: note=label in sound frame - obj=program_text name=dp_title note=list of depots - obj=program_text name=waytype - obj=program_text name=vehicles stored note=sort option in depot list - obj=program_text name=vh_title note=vehicle list title - obj=program_text name=Vehicle Name note=vehicle sort option - obj=program_text name=Capacity note=vehicle sort option - obj=program_text name=Price note=vehicle sort option - obj=program_text name=Cost note=vehicle sort option - obj=program_text name=Cost per unit note=vehicle sort option - obj=program_text name=Max. speed note=vehicle sort option - obj=program_text name=Vehicle Power note=vehicle sort option - obj=program_text name=Weight note=vehicle sort option - obj=program_text name=Intro. date note=vehicle sort option - obj=program_text name=Retire date note=vehicle sort option - obj=program_text name=Show future note=vehicle list option - obj=program_text name=Copy to clipboard note=In chat window - obj=error_text name=Only up and down movement in the underground! note=Tried to use a directional slope in the underground - obj=program_text name=Show contour note=Option in minimap - obj=help_text name=Color-coded terrain according to altitude. note=Option in minimap - obj=program_text name=line name mouseover tooltips note=option in display settings - obj=help_text name=At least one stop is connected to the town. note=tooltip in citylist - obj=program_text name=Served by note=option in citylist - obj=help_text name=At least one tile is connected to one stop. note=tooltip in factorylist - obj=program_text name=(%.2f$/km %.2f$/m) note=info for cars with running and fixed cost - obj=program_text name=(%.2f$/km) note=info for cars with running cost only - obj=button_text name=height based note=tab in climate dialog: Climate by heigh level - obj=button_text name=temperature-humidity based note=tab in climate dialog: climates by temperature and humidity - obj=button_text name=climate area percentage note=text in climate dialog, sizeof climate patches - obj=button_text name=moisture land note=text in climate dialog - obj=button_text name=moisture water note=text in climate dialog - obj=button_text name=humidities note=text in climate dialog - obj=button_text name=temperature borders note=text in climate dialog - obj=button_text name=rainfall note=text in climate dialog, trees vial simulated rainfall - obj=button_text name=Lake height note=text in climate dialog, maximum height of lakes - obj=button_text name=Num pad keys always move map note=Numloock is ignored with this - obj=program_text name=Move the map note=name of tool to move map view - obj=button_text name=Find matching convois info=line management window - obj=tooltip_text name=Add convois with similar schedule to this line. info=line management window - obj=program_text name=legacy (small heights) info=Heightmap window - obj=program_text name=legacy (large heights) info=Heightmap window - obj=program_text name=linear info=Heightmap window - obj=program_text name=clamp info=Heightmap window - obj=program_text name=Load mode: info=Heightmap window - obj=program_text name=Install graphics info=title of pakset installer - obj=program_text name=Wind direction info=Climate window - obj=program_text name=Select one or more graphics to install (Ctrl+click): note=pakinstaller window - obj=program_text name=The following graphics are unmaintained: note=pakinstaller window - obj=program_text name=groundobj builder note=groundobj edit window - obj=program_text name=Has Snow note=groundobj edit window - obj=program_text name=Has slope graphics note=groundobj edit window - obj=program_text name=Can be overgrown note=groundobj edit window - obj=program_text name=[0] south-facing note=Extended edit option - obj=program_text name=[1] east-facing note=Extended edit option - obj=program_text name=[2] north-facing note=Extended edit option - obj=program_text name=[3] west-facing note=Extended edit option - obj=program_text name=[4] southeast corner note=Extended edit option - obj=program_text name=[5] northeast corner note=Extended edit option - obj=program_text name=[6] northwest corner note=Extended edit option - obj=program_text name=[7] southwest corner note=Extended edit option - obj=program_text name=Pax level note=Extended edit option - obj=program_text name=Mail level note=Extended edit option - obj=program_text name=Size (area) note=Extended edit option - obj=program_text name=Available at custom date note=Extended edit option - obj=program_text name=Loading (%i->%i%%)! -- obj=program_text name=Loading (%i%%) departure %s! note=tooltip for convoi state scheduled loading - obj=program_text name=Loading (%i%%)! note=tooltip for convoi state scheduled loading without minimum load - obj=program_text name=Unloading (%i%%)! note=tooltip for convoi state scheduled unloading - obj=program_text name=Toolbar position: note=display setting option - obj=program_text name=1th note=ordinal number en - obj=program_text name=2th note=ordinal number en - obj=program_text name=3th note=ordinal number en - obj=program_text name=21th note=ordinal number en - obj=program_text name=22th note=ordinal number en - obj=program_text name=23th note=ordinal number en - obj=program_text name=31th note=ordinal number en - obj=program_text name=ORDINAL_DAY_SYMBOL note=ordinal day symbol (English th, german . frensh e - obj=program_text name=Monthly departures note=absolute departure time - obj=program_text name=Departure after note=relative departure time on loading - obj=program_text name=on the note=absolute departure time after the repetion box - obj=button_text name=Single GUI note=Button in line lines, please short translations! -- obj=program_text name=Closes topmost line window when new line selected. note=tooltip for button in line list enforcing single window gui - obj=button_text name=Absolute times note=absolute departure time instead relative in departure board - obj=program_text name=The gradient does not fit a tunnel note=Shown when building a tunnel in invalid slope - obj=program_text name=Do not show note=option for factory tooltips -- obj=program_text name=On mouseover note=option for factory tooltips -- obj=program_text name=Served by me note=option for factory tooltips -- obj=program_text name=Show always note=option for factory tooltips -- obj=program_text name=Industry overlay note=label for factory tooltips -- obj=program_text name=Display bars above factory to show the status note=tooltip help for factory tooltips -- obj=program_text name=Date format note=label for date format combobox -- obj=program_text name=Fullscreen (changed after restart) note=button in display setting -- obj=program_text name=Borderless (disabled on fullscreen) note=button in display setting -- obj=program_text name=Screen scale: note=button in display setting -- obj=button_text name=Lake note=Name of water above groundlevel -- obj=button_text name=Open Sea note=Name of water at groundlevel -- obj=button_text name=sea note=climate of water -- obj=program_text name=Climates -- obj=button_text name=Reselect closes tools note=display option -- obj=button_text name=Install note=Option to install new pak sets -- obj=program_text name=Show climates note=minimap display setting -- obj=program_text name=Show outline note=minimap display setting -- obj=program_text name=Month note=in exted edit dialogues -- obj=program_text name=Year note=in exted edit dialogues -- obj=program_text name=auto note=in exted edit dialogues -- obj=program_text name=Departures per month note=label in schedule edit -- obj=program_text name=Departs at note=label in schedule edit -- obj=program_text name=Minimum load note=label in schedule edit -- obj=program_text name=Max. waiting time note=label in schedule edit -- obj=program_text name=No player note=text to indicate to use for served by no one -- obj=program_text name=Cleanup schedule note=Appear in schedule if there are double entries -- obj=help_text name=Remove double stops from schedule note=toolytip for button that appears in schedule if there are double entries -- obj=program_text name=Infinite mouse scrolling note=button in display options -- obj=program_text name=Infinite scrolling using mouse note=tooltip in display options -- obj=program_text name=Scroll threshold note=distance moved before starting map scrolling when a general tool is selected -- obj=program_text name=Downloading note=loadingbar during http download of content -- obj=program_text name=Extract files note=loadingbar during zip file extrating of paks -- obj=program_text name=Install paks note=loadingbar during paks download and install -- obj=program_text name=remove signal note=button/tool tooltip on removing signals -- obj=program_text name=transparent background -- obj=program_text name=Show networks -- obj=program_text name=Shows buttons on network overlay. note=tooltip to Show networks -- obj=program_text name=Loading time: note=Time for full load -- obj=error_text name=No suitable crossing note=Displayed when a route search failed as one possible cause -- obj=error_text name=No curves on runways note=Displayed when a route search failed as one possible cause -- obj=error_text name=A building blocks the construction note=Displayed when a route search failed as one possible cause -- obj=tooltip_text name=Mirrors order of stops note=tooltip for mirror stop order button -- obj=program_text name=Revert schedule note=button to restore original schedule -- obj=tooltip_text name=Revert to original schedule note=tooltip to restore original schedule -- obj=program_text name=Invert stops note=button to invert stop order -- obj=tooltip_text name=Enter intervall in days, hours, minutes note=tooltip for timeinput gui element -- obj=program_text name=Single toolbar only note=button in display setting -- obj=program_text name=Vehicle count: note=number of convoi vehicles in the convoi details -- obj=program_text name=Climate note=dialoque to build object -- obj=program_text name=Composed by note=music credits -- obj=program_text name=and arranged by note=music credits -- obj=program_text name=Load Scripted AI note=script Ai text player list and load dialoque -- obj=program_text name=Load script tool note=mouseover and dialogue title -- obj=button_text name=Tutorial note=Button in the banner window -- obj=tooltip_text name=Tutorial not available for this pakset. Please try loading Pak128. note=Tooltip text for the button in the banner window -- obj=button_text name=Continue Game note=Button in the banner window -- obj=program_text name=Load '%s' note=Tooltip for Continue Game button in banner window -- obj=button_text name=Return to menu note=Button to return to banner menu -- obj=program_text name=Welcome to Simutrans note=Text in banner window. It may be followed by a version string (i.e. "Nightly") -- obj=program_text name=Developed by the Simutrans Team note=Text in banner window -- obj=program_text name=under the Artistic License note=Text in banner window -- obj=program_text name=based on Simutrans 84.22.1 note=Text in banner window -- obj=program_text name=Selling of the program is forbidden. note=Text in banner window -- obj=program_text name=For questions and support please visit: note=Text in banner window -- obj=program_text name=/month note=Text in tooltip for way and building tool icons -- obj=program_text name=Revenue/unit/100 tiles note=Column header in goods list -- obj=program_text name=Speed Bonus note=Column header in goods list -- obj=program_text name=Category note=Column header in goods list -- obj=program_text name=Weight/unit note=Column header in goods list -- obj=program_text name=Hide labels note=Option in display settings/stations -- obj=program_text name=Change label style note=Option in display settings/stations -- obj=program_text name=%s now known as %s. note=Shown as message if a client renames himself. -- obj=program_text name=Welcome, %s! note=Shown as message if a client connects -- obj=program_text name=%s has left. note=Shown as message if a client leaves -- obj=program_text name=Lock Control key note=Tool button tooltip -- obj=program_text name=1 year ago note=chat window -- obj=program_text name=%u years ago note=chat window -- obj=program_text name=%u days ago note=chat window -- obj=program_text name=%u hours ago note=chat window -- obj=program_text name=1 minute ago note=chat window -- obj=program_text name=%u minutes ago note=chat window -- obj=program_text name=just now note=chat window -- obj=tooltip_text name=Attach current coordinate to the comment note=chat window -- obj=program_text name=%u Client(s)\n note=chat window -- obj=program_text name=direct_chat_to: note=chat window -- obj=program_text name=To: %s note=chat window -- obj=program_text name=public_chat note=tab in chat window -- obj=program_text name=company_chat note=tab in chat window -- obj=program_text name=Player ranking note=player ranking window -- obj=program_text name=Click to open the finance dialog. note=player ranking window -- obj=program_text name=Not online note=message when offline in chat window -- obj=program_text name=Current game note=loading default in extended settings -- obj=program_text name=Load settings from note=loading setting in extended settings -- obj=program_text name=Left button drags minimap too note=in display settings -- obj=program_text name=The script cannot be saved!\n note=Script generator -- obj=program_text name=The generated script was saved!\n note=Script generator -- obj=error_text name=Not allowed to copy object. note=Pipette tool -- obj=program_text name=generate script note= tooltip -- obj=program_text name=Pipette note= tooltip -- obj=error_text name=Watertable reached note=too deep lowering failed -- obj=program_text name=Icon size: note=text in diplay options -- obj=error_text name=Forbidden by scenario note=error on trying to select and execute a forbidden tool -- obj=error_text name=Could not open file note=Whenever one cannot access a file -- obj=error_text name=Not initialized note=Could not initilise extrenal library -- obj=error_text name=Unknown error note=General error occured -- obj=error_text name=Not enough empty space note=not written as much bytes as requested => disk full or device busy -- obj=error_text name=Failed to write file note=write operation could not finish or did not even start (permissions?) -- obj=error_text name=Corrupt file note=reading failed due to Corrupt or incomplete file -- obj=program_text name=CURRENCY note=local currency (no longer than 2 charcters for right side) -- obj=program_text name=CURRENCY_LEFT note=local currency to the left of the number if the text is "1" -- obj=program_text name=via owner note=sort label for Freight for next stop owner -- obj=program_text name=connection note=sort label for sorting display by lines/convoi -- obj=program_text name=%d more convois note=message when additional convois serve that stop for sorting display -- obj=button_text name=Server message note=button shown if there is a server motd in the network dialog -- obj=help_text name=Compile a report what pak files are wrong (can be slow) note=tooltip for compare paks -- obj=button_text name=allow reverse passage note=button in signal info -- obj=program_text name=Stopping permissions note=button in signal info -- #obj=help_text #name=Lower values mean more local sounds #note=tooltip in sound frame #-- #obj=program_text #name= #- # text group = 'message_text' # text group = 'button_text' # text group = 'climates_text' # text group = 'error_text' # text group = 'help_text' # text group = 'menu_text' # text group = 'program_text' # text group = 'record_text' # text group = 'ki_text' #obj= <-> text group #name= <-> objecr name #note= <-> description for object #*************** Zuviel/falsch/obsolet ********************** #- #obj=program_text #name=Land industries #- #obj=error_text #name=Cannot create generic line!\nSelect line type by\nusing filter tabs. #- # Obsolete vor 102.2 #obj=program_text #name=Vehicle %s is stucked! #-- #obj=program_text #name=Vehicle %s can't find a route! #-- #obj=program_text #name=%s's\nheadquarter now\nat (%i,%i). #-- #obj=program_text #name=1nord #-- #obj=program_text #name=1nordost #-- #obj=program_text #name=1nordwest #-- #obj=program_text #name=1ost #-- #obj=program_text #name=1sued #-- #obj=program_text #name=1suedost #-- #obj=program_text #name=1suedwest #-- #obj=program_text #name=1west #-- #obj=program_text #name=Ambiguous signal combination.\nTry removing by clicking the\nother side of the signal.\n #-- #obj=ki_text #name=Ferry service by %s now between %s nand %s. #-- #obj=program_text #name=Monorailhalt #- #obj=program_text #name=Narrowgaugehalt #- #obj=program_text #name=Narrowgaugeboden #- #obj=program_text #name=Passengers %d %c, %d %c, %d no route\n\nCapacity: %i\n #- #obj=message_text #name=Production of vehicle has been stopped:\n%s\n #- #obj=error_text #name=Station already\nhas a post office!\n #- #obj=ki_text #name=Travellers now use %s's busses between %s nand %s. #- #obj=program_text #name=Tunnelmuendung #- #obj=program_text #name=Wagen im Block #- #obj=program_text #name=Zeiger #- #obj=program_text #name=Zu nah an einem\nanderen Signal!\n #- #obj=program_text #name=\nRail block #- #obj=program_text #name=\n\nRibi (unmasked) #- #obj=program_text #name=attractionbuilder #-- #obj=program_text #name=housebuilder #-- #obj=program_text #name=treebuilder #-- #obj=record_text #name=New world record for maglev: %.1f km/h by %s. #-- #obj=record_text #name=New world record for narrowgauge: %.1f km/h by %s. #-- #obj=program_text #name=Tunnel must end on single way! #-- #obj=program_text #name=Headquartern and %li trains #- #obj=program_text #name=Monate alt #- #obj=program_text #name=Einstellungsfenster #- #obj=program_text #name=Move the selected vehicle(s) back to the depot #- #obj=program_text #name=Lines are used to manage groups of vehicles #- #obj=program_text #name=Add the selected vehicle(s) to the selected line #- #obj=program_text #name=Aufloesen #- #obj=program_text #name=LOCO_CAP #- #obj=program_text #name=LOCO_INFO #- #obj=program_text #name=WAGGON_INFO #- #obj=program_text #name=Cost: %6d$ (%.2f$/km)\n #- #obj=program_text #name=Vehicle details #- #obj=program_text #name=%i years %i months old #-- #obj=program_text #name=Stucked! #-- #obj=program_text #name=Can't find a route! #-- #obj=program_text #name=Not loading! #-- #obj=program_text #name=Going to depot! #-- #obj=button_text #name=Maximize height levels #note=Indicates that the heightmap should be stretched over more height levels #-- #obj=program_text #name=clf_chk_name_filter #- #obj=program_text #name=clf_chk_ships #- #obj=program_text #name=clf_chk_trains #- #obj=program_text #name=clf_chk_trams #- #obj=program_text #name=clf_chk_aircrafts #- #obj=program_text #name=clf_chk_cars #- #obj=button_text #name=cl_btn_filter_disable #- #obj=button_text #name=cl_btn_filter_enable #- #obj=button_text #name=Select soundfont #- #obj=program_text #name=Soundfonts are located in the music directory. #- #obj=program_text #name=Soundfont not found. Please select a soundfont below. #- #obj=error_text #name=No soundfont found!\n\nMusic won't play until you load a soundfont from the sound options menu. #- #obj=program_text #name=Helligk. #- #obj=program_text #name=\nAct. Load: %u MW\n #note=powernet info window (depricated) #- #obj=program_text #name=show station names #- #obj=program_text #name=Account above %s #name=Supply %s at (%i,%i) #name=Connect factory #name=Headquarter and %li trains #name=Transport %li passengers #note=old scenarion descriptions simutrans-124.5/src/external/000077500000000000000000000000001520675460500161755ustar00rootroot00000000000000simutrans-124.5/src/external/miniz.h000066400000000000000000014130061520675460500175010ustar00rootroot00000000000000#ifndef MINIZ_EXPORT #define MINIZ_EXPORT #endif /* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateReset/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #pragma once /* Defines to completely disable specific portions of miniz.c: If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on * stdio for file I/O. */ /*#define MINIZ_NO_STDIO */ /* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able * to get the current time, or */ /* get/set file times, and the C run-time funcs that get/set times won't be * called. */ /* The current downside is the times written to your archives will be from 1979. */ /*#define MINIZ_NO_TIME */ /* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ /*#define MINIZ_NO_DEFLATE_APIS */ /* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ /*#define MINIZ_NO_INFLATE_APIS */ /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_APIS */ /* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP * archive API's. */ /*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ /* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression * API's. */ /*#define MINIZ_NO_ZLIB_APIS */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent * conflicts against stock zlib. */ /*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ /* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ #ifdef MINIZ_NO_INFLATE_APIS #define MINIZ_NO_ARCHIVE_APIS #endif #ifdef MINIZ_NO_DEFLATE_APIS #define MINIZ_NO_ARCHIVE_WRITING_APIS #endif #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc * on Linux */ #define MINIZ_NO_TIME #endif #include #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ defined(__i386) || defined(__i486__) || defined(__i486) || \ defined(i386) || defined(__ia64__) || defined(__x86_64__) /* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ #define MINIZ_X86_OR_X64_CPU 1 #else #define MINIZ_X86_OR_X64_CPU 0 #endif /* Set MINIZ_LITTLE_ENDIAN only if not set */ #if !defined(MINIZ_LITTLE_ENDIAN) #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #else #if MINIZ_X86_OR_X64_CPU #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #endif #endif /* Using unaligned loads and stores causes errors when using UBSan */ #if defined(__has_feature) #if __has_feature(undefined_behavior_sanitizer) #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #endif /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if MINIZ_X86_OR_X64_CPU /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient * integer loads and stores from unaligned addresses. */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #define MINIZ_UNALIGNED_USE_MEMCPY #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || \ defined(_LP64) || defined(__LP64__) || defined(__ia64__) || \ defined(__x86_64__) /* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are * reasonably fast (and don't involve compiler generated calls to helper * functions). */ #define MINIZ_HAS_64BIT_REGISTERS 1 #else #define MINIZ_HAS_64BIT_REGISTERS 0 #endif #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API Definitions. */ /* For more compatibility with zlib, miniz.c uses unsigned long for some * parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ typedef unsigned long mz_ulong; /* mz_free() internally uses the MZ_FREE() macro (which by default calls free() * unless you've modified the MZ_MALLOC macro) to release a block allocated from * the heap. */ MINIZ_EXPORT void mz_free(void *p); #define MZ_ADLER32_INIT (1) /* mz_adler32() returns the initial adler-32 value to use when called with * ptr==NULL. */ MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) /* mz_crc32() returns the initial CRC-32 value to use when called with * ptr==NULL. */ MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); /* Compression strategies. */ enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; /* Method */ #define MZ_DEFLATED 8 /* Heap allocation callbacks. Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); /* Compression levels: 0-9 are the standard zlib-style levels, 10 is best * possible compression (not zlib compatible, and may be very slow), * MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; #define MZ_VERSION "11.0.2" #define MZ_VERNUM 0xB002 #define MZ_VER_MAJOR 11 #define MZ_VER_MINOR 2 #define MZ_VER_REVISION 0 #define MZ_VER_SUBREVISION 0 #ifndef MINIZ_NO_ZLIB_APIS /* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The * other values are for advanced use (refer to the zlib docs). */ enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; /* Return status codes. MZ_PARAM_ERROR is non-standard. */ enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; /* Window bits */ #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; /* Compression/decompression stream struct. */ typedef struct mz_stream_s { const unsigned char *next_in; /* pointer to next byte to read */ unsigned int avail_in; /* number of bytes available at next_in */ mz_ulong total_in; /* total number of bytes consumed so far */ unsigned char *next_out; /* pointer to next byte to write */ unsigned int avail_out; /* number of bytes that can be written to next_out */ mz_ulong total_out; /* total number of bytes produced so far */ char *msg; /* error msg (unused) */ struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ mz_free_func zfree; /* optional heap free function (defaults to free) */ void *opaque; /* heap alloc function user pointer */ int data_type; /* data_type (unused) */ mz_ulong adler; /* adler32 of the source or uncompressed data */ mz_ulong reserved; /* not used */ } mz_stream; typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ MINIZ_EXPORT const char *mz_version(void); #ifndef MINIZ_NO_DEFLATE_APIS /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ /* pStream must point to an initialized mz_stream struct. */ /* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ /* level 1 enables a specially optimized compression function that's been * optimized purely for performance, not ratio. */ /* (This special func. is currently only enabled when * MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if the input parameters are bogus. */ /* MZ_MEM_ERROR on out of memory. */ MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); /* mz_deflateInit2() is like mz_deflate(), except with more control: */ /* Additional parameters: */ /* method must be MZ_DEFLATED */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with * zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no * header or footer) */ /* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); /* Quickly resets a compressor without having to reallocate anything. Same as * calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); /* mz_deflate() compresses the input to output, consuming as much of the input * and producing as much output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update * the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or * MZ_FINISH. */ /* Return values: */ /* MZ_OK on success (when flushing, or if more input is needed but not * available, and/or there's more output to be written but the output buffer is * full). */ /* MZ_STREAM_END if all input has been consumed and all output bytes have been * written. Don't call mz_deflate() on the stream anymore. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input and/or * output buffers are empty. (Fill up the input buffer or free up some output * space and try again.) */ MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); /* mz_deflateEnd() deinitializes a compressor: */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); /* mz_deflateBound() returns a (very) conservative upper bound on the amount of * data that could be generated by deflate(), assuming flush is set to only * MZ_NO_FLUSH or MZ_FINISH. */ MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); /* Single-call compression functions mz_compress() and mz_compress2(): */ /* Returns MZ_OK on success, or one of the error codes from mz_deflate() on * failure. */ MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); /* mz_compressBound() returns a (very) conservative upper bound on the amount of * data that could be generated by calling mz_compress(). */ MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS /* Initializes a decompressor. */ MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); /* mz_inflateInit2() is like mz_inflateInit() with an additional option that * controls the window size and whether or not the stream has been wrapped with * a zlib header/footer: */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or * -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); /* Quickly resets a compressor without having to reallocate anything. Same as * calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); /* Decompresses the input stream to the output, consuming only as much of the * input as needed, and writing as much to the output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update * the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ /* On the first call, if flush is MZ_FINISH it's assumed the input and output * buffers are both sized large enough to decompress the entire stream in a * single call (this is slightly faster). */ /* MZ_FINISH implies that there are no more source bytes available beside * what's already in the input buffer, and that the output buffer is large * enough to hold the rest of the decompressed data. */ /* Return values: */ /* MZ_OK on success. Either more input is needed but not available, and/or * there's more output to be written but the output buffer is full. */ /* MZ_STREAM_END if all needed input has been consumed and all output bytes * have been written. For zlib streams, the adler-32 of the decompressed data * has also been verified. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_DATA_ERROR if the deflate stream is invalid. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input buffer is * empty but the inflater needs more input to continue, or if the output buffer * is not large enough. Call mz_inflate() again */ /* with more input data, or with more room in the output buffer (except when * using single call decompression, described above). */ MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); /* Deinitializes a decompressor. */ MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); /* Single-call decompression. */ /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on * failure. */ MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /* Returns a string description of the specified error code, or NULL if the * error code is invalid. */ MINIZ_EXPORT const char *mz_error(int err); /* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used * as a drop-in replacement for the subset of zlib that miniz.c supports. */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you * use zlib in the same project. */ #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #ifndef MINIZ_NO_DEFLATE_APIS #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflateReset mz_inflateReset #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define uncompress2 mz_uncompress2 #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ #endif /* MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif #pragma once #include #include #include #include /* ------------------- Types and macros */ typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef int64_t mz_int64; typedef uint64_t mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) /* Works around MSVC's spammy "warning C4127: conditional expression is * constant" message. */ #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #define MZ_FILE FILE #endif /* #ifdef MINIZ_NO_STDIO */ #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { mz_uint32 m_dummy1; mz_uint32 m_dummy2; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else #define MZ_TIME_T time_t #endif #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) #define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) \ ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) \ ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | \ ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | \ ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #define MZ_READ_LE64(p) \ (((mz_uint64)MZ_READ_LE32(p)) | \ (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) \ << 32U)) #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); #define MZ_UINT16_MAX (0xFFFFU) #define MZ_UINT32_MAX (0xFFFFFFFFU) #ifdef __cplusplus } #endif #pragma once #ifndef MINIZ_NO_DEFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression API Definitions */ /* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly * slower, and raw/dynamic blocks will be output more frequently). */ #define TDEFL_LESS_MEMORY 0 /* tdefl_init() compression flags logically OR'd together (low 12 bits contain * the max. number of probes per dictionary search): */ /* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes * per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap * compression), 4095=Huffman+LZ (slowest/best compression). */ enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; /* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before * the deflate data, and the Adler-32 of the source data at the end. Otherwise, * you'll get raw deflate data. */ /* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even * when not writing zlib headers). */ /* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more * efficient lazy parsing. */ /* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's * initialization time to the minimum, but the output may vary from run to run * given the same input (depending on the contents of memory). */ /* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ /* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ /* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ /* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ /* The low 12 bits are reserved to control the max # of hash probes per * dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; /* High level compression functions: */ /* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block * allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ /* flags: The max match finder probes (default is 128) logically OR'd against * the above flags. Higher probes are slower but improve compression. */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pOut_len will be set to the compressed data's size, which could be larger * than src_buf_len on uncompressible data. */ /* The caller must free() the returned block when it's no longer needed. */ MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tdefl_compress_mem_to_mem() compresses a block in memory to another block in * memory. */ /* Returns 0 on failure. */ MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* Compresses an image to a compressed PNG file in memory. */ /* On entry: */ /* pImage, w, h, and num_chans describe the image to compress. num_chans may be * 1, 2, 3, or 4. */ /* The image pitch in bytes per scanline will be w*num_chans. The leftmost * pixel on the top scanline is stored first in memory. */ /* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, * MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ /* If flip is true, the image will be flipped on the Y axis (useful for OpenGL * apps). */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pLen_out will be set to the size of the PNG image file. */ /* The caller must mz_free() the returned heap block (which will typically be * larger than *pLen_out) when it's no longer needed. */ MINIZ_EXPORT void * tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); /* Output stream interface. The compressor uses this interface to write * compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); /* tdefl_compress_mem_to_output() compresses a block to an output stream. The * above helpers use this function internally. */ MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output( const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; /* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed * output block (using static/fixed Huffman codes). */ #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif /* The low-level tdefl functions below may be used directly if the above helper * functions aren't flexible enough. The low-level functions don't make any heap * allocations, unlike the above helper functions. */ typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1 } tdefl_status; /* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; /* tdefl's compression state structure. */ typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; /* Initializes the compressor. */ /* There is no corresponding deinit() function because the tdefl API's do not * dynamically allocate memory. */ /* pBut_buf_func: If NULL, output data will be supplied to the specified * callback. In this case, the user should call the tdefl_compress_buffer() API * for compression. */ /* If pBut_buf_func is NULL the user should always call the tdefl_compress() * API. */ /* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, * etc.) */ MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); /* Compresses a block of data, consuming as much of the specified input buffer * as possible, and writing as much compressed data to the specified output * buffer as possible. */ MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); /* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a * non-NULL tdefl_put_buf_func_ptr. */ /* tdefl_compress_buffer() always consumes the entire input buffer. */ MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); /* Create tdefl_compress() flags given zlib-style compression parameters. */ /* level may range from [0,10] (where 10 is absolute max compression, but may be * much slower on some files) */ /* window_bits may be -15 (raw deflate) or 15 (zlib) */ /* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, * MZ_RLE, or MZ_FIXED */ MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor structure in C so that */ /* non-C language bindings to tdefl_ API don't need to worry about */ /* structure size and allocation mechanism. */ MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #pragma once /* ------------------- Low-level Decompression API Definitions */ #ifndef MINIZ_NO_INFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* Decompression flags used by tinfl_decompress(). */ /* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and * ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the * input is a raw deflate stream. */ /* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available * beyond the end of the supplied input buffer. If clear, the input buffer * contains all remaining input. */ /* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large * enough to hold the entire decompressed stream. If clear, the output buffer is * at least the size of the dictionary (typically 32KB). */ /* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the * decompressed bytes. */ enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; /* High level decompression functions: */ /* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block * allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data * to decompress. */ /* On return: */ /* Function returns a pointer to the decompressed data, or NULL on failure. */ /* *pOut_len will be set to the decompressed data's size, which could be larger * than src_buf_len on uncompressible data. */ /* The caller must call mz_free() on the returned block when it's no longer * needed. */ MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block * in memory. */ /* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes * written on success. */ #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* tinfl_decompress_mem_to_callback() decompresses a block in memory to an * internal 32KB buffer, and a user provided callback function will be called to * flush the buffer. */ /* Returns 1 on success or 0 on failure. */ typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; #ifndef MINIZ_NO_MALLOC /* Allocate the tinfl_decompressor structure in C so that */ /* non-C language bindings to tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); #endif /* Max size of LZ dictionary. */ #define TINFL_LZ_DICT_SIZE 32768 /* Return status. */ typedef enum { /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ TINFL_STATUS_BAD_PARAM = -3, /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ TINFL_STATUS_ADLER32_MISMATCH = -2, /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ TINFL_STATUS_FAILED = -1, /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ TINFL_STATUS_DONE = 0, /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ TINFL_STATUS_NEEDS_MORE_INPUT = 1, /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ /* so I may need to add some code to address this. */ TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; /* Initializes the decompressor to its initial state. */ #define tinfl_init(r) \ do { \ (r)->m_state = 0; \ } \ MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 /* Main low-level decompressor coroutine function. This is the only function * actually needed for decompression. All the other functions are just * high-level helpers for improved usability. */ /* This is a universal API, i.e. it can be used as a building block to build any * desired higher level decompression API. In the limit case, it can be called * once per every byte input or output. */ MINIZ_EXPORT tinfl_status tinfl_decompress( tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); /* Internal/private bits follow. */ enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else #define TINFL_USE_64BIT_BITBUF 0 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ #pragma once /* ------------------- ZIP archive reading/writing */ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif enum { /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 }; typedef struct { /* Central directory file index. */ mz_uint32 m_file_index; /* Byte offset of this entry in the archive's central directory. Note we * currently only support up to UINT_MAX or less bytes in the central dir. */ mz_uint64 m_central_dir_ofs; /* These fields are copied directly from the zip's central dir. */ mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; /* CRC-32 of uncompressed data. */ mz_uint32 m_crc32; /* File's compressed size. */ mz_uint64 m_comp_size; /* File's uncompressed size. Note, I've seen some old archives where directory * entries had 512 bytes for their uncompressed sizes, but when you try to * unpack them you actually get 0 bytes. */ mz_uint64 m_uncomp_size; /* Zip internal and external file attributes. */ mz_uint16 m_internal_attr; mz_uint32 m_external_attr; /* Entry's local header file offset in bytes. */ mz_uint64 m_local_header_ofs; /* Size of comment in bytes. */ mz_uint32 m_comment_size; /* MZ_TRUE if the entry appears to be a directory. */ mz_bool m_is_directory; /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip * doesn't support) */ mz_bool m_is_encrypted; /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a * compression method we support. */ mz_bool m_is_supported; /* Filename. If string ends in '/' it's a subdirectory entry. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; /* Comment field. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; #ifdef MINIZ_NO_TIME MZ_TIME_T m_padding; #else MZ_TIME_T m_time; #endif } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, /*After adding a compressed file, seek back to local file header and set the correct sizes*/ MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 } mz_zip_flags; typedef enum { MZ_ZIP_TYPE_INVALID = 0, MZ_ZIP_TYPE_USER, MZ_ZIP_TYPE_MEMORY, MZ_ZIP_TYPE_HEAP, MZ_ZIP_TYPE_FILE, MZ_ZIP_TYPE_CFILE, MZ_ZIP_TOTAL_TYPES } mz_zip_type; /* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or * modify this enum. */ typedef enum { MZ_ZIP_NO_ERROR = 0, MZ_ZIP_UNDEFINED_ERROR, MZ_ZIP_TOO_MANY_FILES, MZ_ZIP_FILE_TOO_LARGE, MZ_ZIP_UNSUPPORTED_METHOD, MZ_ZIP_UNSUPPORTED_ENCRYPTION, MZ_ZIP_UNSUPPORTED_FEATURE, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, MZ_ZIP_NOT_AN_ARCHIVE, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, MZ_ZIP_UNSUPPORTED_MULTIDISK, MZ_ZIP_DECOMPRESSION_FAILED, MZ_ZIP_COMPRESSION_FAILED, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, MZ_ZIP_CRC_CHECK_FAILED, MZ_ZIP_UNSUPPORTED_CDIR_SIZE, MZ_ZIP_ALLOC_FAILED, MZ_ZIP_FILE_OPEN_FAILED, MZ_ZIP_FILE_CREATE_FAILED, MZ_ZIP_FILE_WRITE_FAILED, MZ_ZIP_FILE_READ_FAILED, MZ_ZIP_FILE_CLOSE_FAILED, MZ_ZIP_FILE_SEEK_FAILED, MZ_ZIP_FILE_STAT_FAILED, MZ_ZIP_INVALID_PARAMETER, MZ_ZIP_INVALID_FILENAME, MZ_ZIP_BUF_TOO_SMALL, MZ_ZIP_INTERNAL_ERROR, MZ_ZIP_FILE_NOT_FOUND, MZ_ZIP_ARCHIVE_TOO_LARGE, MZ_ZIP_VALIDATION_FAILED, MZ_ZIP_WRITE_CALLBACK_FAILED, MZ_ZIP_TOTAL_ERRORS } mz_zip_error; typedef struct { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; /* We only support up to UINT32_MAX files in zip64 mode. */ mz_uint32 m_total_files; mz_zip_mode m_zip_mode; mz_zip_type m_zip_type; mz_zip_error m_last_error; mz_uint64 m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; mz_file_needs_keepalive m_pNeeds_keepalive; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef struct { mz_zip_archive *pZip; mz_uint flags; int status; mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf; void *pWrite_buf; size_t out_blk_remain; tinfl_decompressor inflator; #ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint padding; #else mz_uint file_crc32; #endif } mz_zip_reader_extract_iter_state; /* -------- ZIP reading */ /* Inits a ZIP archive reader. */ /* These functions read and validate the archive's central directory. */ MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); #ifndef MINIZ_NO_STDIO /* Read a archive from a disk file. */ /* file_start_ofs is the file offset where the archive actually begins, or 0. */ /* actual_archive_size is the true total size of the archive, which may be * smaller than the file's actual size on disk. If zero the entire file is * treated as the archive. */ MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2_rpb(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); /* Read an archive from an already opened FILE, beginning at the current file * position. */ /* The archive is assumed to be archive_size bytes long. If archive_size is 0, * then the entire rest of the file is assumed to contain the archive. */ /* The FILE will NOT be closed when mz_zip_reader_end() is called. */ MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); #endif /* Ends archive reading, freeing all allocations, and closing the input archive * file if mz_zip_reader_init_file() was used. */ MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); /* -------- ZIP reading or writing */ /* Clears a mz_zip_archive struct to all zeros. */ /* Important: This must be done before passing the struct to any mz_zip * functions. */ MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); /* Returns the total number of files in the archive. */ MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); /* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); /* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. * These functions retrieve/manipulate this field. */ /* Note that the m_last_error functionality is not thread safe. */ MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); /* MZ_TRUE if the archive file entry is a directory entry. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the file is encrypted/strong encrypted. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the compression method is supported, and the file is not * encrypted, and the file is not a compressed patch file. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); /* Retrieves the filename of an archive file entry. */ /* Returns the number of bytes written to pFilename, or if filename_buf_size is * 0 this function returns the number of bytes needed to fully store the * filename. */ MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); /* Attempts to locates a file in the archive's central directory. */ /* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ /* Returns -1 if the file cannot be found. */ MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); /* Returns detailed information about an archive file entry. */ MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); /* MZ_TRUE if the file is in zip64 format. */ /* A file is considered zip64 if it contained a zip64 end of central directory * marker, or if it contained any zip64 extended file information fields in the * central directory. */ MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); /* Returns the total central directory size in bytes. */ /* The current max supported size is <= MZ_UINT32_MAX. */ MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); /* Extracts a archive file to a memory buffer using no memory allocation. */ /* There must be at least enough room on the stack to store the inflator's state * (~34KB or so). */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc( mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); /* Extracts a archive file to a memory buffer. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); /* Extracts a archive file to a dynamically allocated heap buffer. */ /* The memory will be allocated via the mz_zip_archive's alloc/realloc * functions. */ /* Returns NULL and sets the last error on failure. */ MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); /* Extracts a archive file using a callback function to output the file's data. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback( mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback( mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); /* Extract a file iteratively */ MINIZ_EXPORT mz_zip_reader_extract_iter_state * mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); MINIZ_EXPORT mz_zip_reader_extract_iter_state * mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read( mz_zip_reader_extract_iter_state *pState, void *pvBuf, size_t buf_size); MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state *pState); #ifndef MINIZ_NO_STDIO /* Extracts a archive file to a disk file and sets its last accessed and * modified times. */ /* This function only extracts files, not archive directory records. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file( mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); /* Extracts a archive file starting at the current position in the destination * FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile( mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); #endif #if 0 /* TODO */ typedef void *mz_zip_streaming_extract_state_ptr; mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif /* This function compares the archive's local headers, the optional local zip64 * extended information block, and the optional descriptor following the * compressed data vs. the data in the central directory. */ /* It also validates that each file can be successfully uncompressed unless the * MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); /* Validates an entire archive by calling mz_zip_validate_file() on each file. */ MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); /* Misc utils/helpers, valid for ZIP reading or writing */ MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); #ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); #endif /* Universal end function - calls either mz_zip_reader_end() or * mz_zip_writer_end(). */ MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); /* -------- ZIP writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS /* Inits a ZIP archive writer. */ /*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init * or mz_zip_writer_init_v2*/ /*The output is streamable, i.e. file_ofs in mz_file_write_func always increases * only by n*/ MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_heap( mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2( mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); #ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2( mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); #endif /* Converts a ZIP archive reader object into a writer object, to allow efficient * in-place file appends to occur on an existing archive. */ /* For archives opened using mz_zip_reader_init_file, pFilename must be the * archive's filename so it can be reopened for writing. If the file can't be * reopened, mz_zip_reader_end() will be called. */ /* For archives opened using mz_zip_reader_init_mem, the memory block must be * growable using the realloc callback (which defaults to realloc unless you've * overridden it). */ /* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's * user provided m_pWrite function cannot be NULL. */ /* Note: In-place archive modification is not recommended unless you know what * you're doing, because if execution stops or something goes wrong before */ /* the archive is finalized the file's central directory will be hosed. */ MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2_noreopen( mz_zip_archive *pZip, const char *pFilename, mz_uint flags); /* Adds the contents of a memory buffer to an archive. These functions record * the current local time into the archive. */ /* To add a directory entry, call this method with an archive name ending in a * forwardslash with an empty buffer. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, * MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or * just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_mem(), except you can specify a file comment field, * and optionally supply the function with already compressed data. */ /* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA * flag is specified. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex( mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2( mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); /* Adds the contents of a file to an archive. This function also records the * disk file's modified time into the archive. */ /* File data is supplied via a read callback function. User * mz_zip_writer_add_(c)file to add a file directly.*/ MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback( mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void *callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #ifndef MINIZ_NO_STDIO /* Adds the contents of a disk file to an archive. This function also records * the disk file's modified time into the archive. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, * MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or * just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_file( mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes); /* Like mz_zip_writer_add_file(), except the file data is read from the * specified FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile( mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #endif /* Adds a file to an archive by fully cloning the data from another archive. */ /* This function fully clones the source file's compressed data (no * recompression), along with its full filename, extra data (it may add or * modify the zip64 local header extra data field), and the optional descriptor * following the compressed data. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader( mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); /* Finalizes the archive by writing the central directory records followed by * the end of central directory record. */ /* After an archive is finalized, the only valid call on the mz_zip_archive * struct is mz_zip_writer_end(). */ /* An archive must be manually finalized by calling this function for it to be * valid. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); /* Finalizes a heap archive, returning a pointer to the heap block and its size. */ /* The heap block will be allocated using the mz_zip_archive's alloc/realloc * callbacks. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); /* Ends archive writing, freeing all allocations, and closing the output file if * mz_zip_writer_init_file() was used. */ /* Note for the archive to be valid, it *must* have been finalized before ending * (this function will not do it for you). */ MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); /* -------- Misc. high-level helper functions: */ /* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) * appends a memory blob to a ZIP archive. */ /* Note this is NOT a fully safe operation. If it crashes or dies in some way * your archive can be left in a screwed up state (without a central directory). */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, * MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or * just set to MZ_DEFAULT_COMPRESSION. */ /* TODO: Perhaps add an option to leave the existing central dir in place in * case the add dies? We could then truncate the file (so the old central dir * would be at the end) if something goes wrong. */ MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place( const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2( const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); #ifndef MINIZ_NO_STDIO /* Reads a single file from an archive into a heap block. */ /* If pComment is not NULL, only the file with the specified comment will be * extracted. */ /* Returns NULL on failure. */ MINIZ_EXPORT void * mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2( const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); #endif #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifdef __cplusplus } #endif #endif /* MINIZ_NO_ARCHIVE_APIS */ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API's */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } /* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C * implementation that balances processor cache usage against speed": * http://www.geocities.com/malbrain/ */ #if 0 mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } #elif defined(USE_EXTERNAL_MZCRC) /* If USE_EXTERNAL_CRC is defined, an external module will export the * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. * Depending on the impl, it may be necessary to ~ the input/output crc values. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); #else /* Faster, but larger CPU cache footprint. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; while (buf_len >= 4) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; pByte_buf += 4; buf_len -= 4; } while (buf_len) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; ++pByte_buf; --buf_len; } return ~crc32; } #endif void mz_free(void *p) { MZ_FREE(p); } MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } #ifndef MINIZ_NO_ZLIB_APIS #ifndef MINIZ_NO_DEFLATE_APIS int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for (;;) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; /* Can't make forward progress without some input. */ } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; /* This is really over conservative. (And lame, but it's actually pretty * tricky to compute a true upper bound given the way tdefl's blocking works.) */ return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong pSource_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__WATCOMC__) /* In case mz_ulong is 64-bits (argh I hate longs). */ #else if ((mz_uint64)(pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; #endif stream.next_in = pSource; stream.avail_in = (mz_uint32)pSource_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflateReset(mz_streamp pStream) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; pDecomp = (inflate_state *)pStream->state; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; /* pDecomp->m_window_bits = window_bits */; return MZ_OK; } int mz_inflate(mz_streamp pStream, int flush) { inflate_state *pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state *)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { /* MZ_FINISH on the first call implies that the input and output buffers are * large enough to hold the entire compressed/decompressed file. */ decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } /* flush != MZ_FINISH then we must assume there's more input. */ if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for (;;) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress( &pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ else if (flush == MZ_FINISH) { /* The output buffer MUST be large to hold the remaining uncompressed data * when flush==MZ_FINISH. */ if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's * at least 1 more byte on the way. If there's no more room left in the * output buffer then something is wrong. */ else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__WATCOMC__) /* In case mz_ulong is 64-bits (argh I hate longs). */ #else if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; #endif stream.next_in = pSource; stream.avail_in = (mz_uint32)*pSource_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); *pSource_len = *pSource_len - stream.avail_in; if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_uncompress2(pDest, pDest_len, pSource, &source_len); } #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = {{MZ_OK, ""}, {MZ_STREAM_END, "stream end"}, {MZ_NEED_DICT, "need dictionary"}, {MZ_ERRNO, "file error"}, {MZ_STREAM_ERROR, "stream error"}, {MZ_DATA_ERROR, "data error"}, {MZ_MEM_ERROR, "out of memory"}, {MZ_BUF_ERROR, "buf error"}, {MZ_VERSION_ERROR, "version error"}, {MZ_PARAM_ERROR, "parameter error"}}; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif /*MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifndef MINIZ_NO_DEFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression (independent from all decompression * API's) */ /* Purposely making these tables static for faster init and thread safety. */ static const mz_uint16 s_tdefl_len_sym[256] = { 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285}; static const mz_uint8 s_tdefl_len_extra[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0}; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17}; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; /* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted * values. */ typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_ARR(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32 *pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq *t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } /* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, * alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next = 1; next < n - 1; next++) { if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key; if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); } A[n - 2].m_key = 0; for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; avbl = 1; used = dpth = 0; root = n - 2; next = n - 1; while (avbl > 0) { while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; } while (avbl > used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2 * used; dpth++; used = 0; } } /* Limits canonical Huffman code table's max code size. */ enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_ARR(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); MZ_CLEAR_ARR(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) \ do { \ mz_uint bits = b; \ mz_uint len = l; \ MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); \ d->m_bits_in += len; \ while (d->m_bits_in >= 8) { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } \ MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() \ { \ if (rle_repeat_count) { \ if (rle_repeat_count < 3) { \ d->m_huff_count[2][prev_code_size] = \ (mz_uint16)(d->m_huff_count[2][prev_code_size] + \ rle_repeat_count); \ while (rle_repeat_count--) \ packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } else { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 16; \ packed_code_sizes[num_packed_code_sizes++] = \ (mz_uint8)(rle_repeat_count - 3); \ } \ rle_repeat_count = 0; \ } \ } #define TDEFL_RLE_ZERO_CODE_SIZE() \ { \ if (rle_z_count) { \ if (rle_z_count < 3) { \ d->m_huff_count[2][0] = \ (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ while (rle_z_count--) \ packed_code_sizes[num_packed_code_sizes++] = 0; \ } else if (rle_z_count <= 10) { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 17; \ packed_code_sizes[num_packed_code_sizes++] = \ (mz_uint8)(rle_z_count - 3); \ } else { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 18; \ packed_code_sizes[num_packed_code_sizes++] = \ (mz_uint8)(rle_z_count - 11); \ } \ rle_z_count = 0; \ } \ } static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes [2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS( d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && \ MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) \ { \ bit_buffer |= (((mz_uint64)(b)) << bits_in); \ bits_in += (l); \ } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0]; mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && \ MINIZ_HAS_64BIT_REGISTERS */ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static const mz_uint s_tdefl_num_probes[11] = {0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { const mz_uint8 cmf = 0x78; mz_uint8 flg, flevel = 3; mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); /* Determine compression level by reversing the process in * tdefl_create_comp_flags_from_zip_params() */ for (i = 0; i < mz_un; i++) if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; if (i < 2) flevel = 0; else if (i < 6) flevel = 1; else if (i == 6) flevel = 2; header = cmf << 8 | (flevel << 6); header += 31 - (header % 31); flg = header & 0xFF; TDEFL_PUT_BITS(cmf, 8); TDEFL_PUT_BITS(flg, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); /* If the block gets expanded, forget the current contents of the output * buffer and send a raw block instead. */ if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS( d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN( (size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #ifdef MINIZ_UNALIGNED_USE_MEMCPY static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8 *p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16 *p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) #define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) #endif static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || \ ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16 *)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) continue; p = s; probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || \ ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && \ (d->m_dict[probe_pos + match_len - 1] == c1)) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #ifdef MINIZ_UNALIGNED_USE_MEMCPY static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8 *p) { mz_uint32 ret; memcpy(&ret, p, sizeof(mz_uint32)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) #endif static mz_bool tdefl_compress_fast(tdefl_compressor *d) { /* Faster, minimally featured LZRW1-style match+parse loop with better * register utilization. Intended for applications where raw throughput is * valued more highly than ratio. */ mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32( d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); #ifdef MINIZ_UNALIGNED_USE_MEMCPY memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); #else *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; #endif pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; /* Update dictionary and hash chains. Keeps the lookahead size equal to * TDEFL_MAX_MATCH_LEN. */ if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; /* Simple lazy/greedy parsing state machine. */ len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } /* Move the lookahead forward by len_to_move bytes. */ d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); /* Check if it's time to flush the current LZ codes to the internal output * buffer. */ if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_ARR(d->m_hash); MZ_CLEAR_ARR(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_ARR(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; *d->m_pLZ_flags = 0; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_ARR(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output( pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8 *)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output( pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } /* level may actually range from [0,10] (10 is a "hidden" max level, where we * want a bit more compression and it's fine if throughput to fall off a cliff * on some files). */ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4204) /* nonstandard extension used : non-constant \ aggregate initializer (also supported by \ GNU C and C99, so no big deal) */ #endif /* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was * defined. */ static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } /* write dummy header */ for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); /* compress image data */ tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } /* write real header */ *pLen_out = out_buf.m_size - 41; { static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; mz_uint8 pnghdr[41] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x44, 0x41, 0x54}; pnghdr[18] = (mz_uint8)(w >> 8); pnghdr[19] = (mz_uint8)w; pnghdr[22] = (mz_uint8)(h >> 8); pnghdr[23] = (mz_uint8)h; pnghdr[25] = chans[num_chans]; pnghdr[33] = (mz_uint8)(*pLen_out >> 24); pnghdr[34] = (mz_uint8)(*pLen_out >> 16); pnghdr[35] = (mz_uint8)(*pLen_out >> 8); pnghdr[36] = (mz_uint8)*pLen_out; c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); for (i = 0; i < 4; ++i, c <<= 8) ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); memcpy(out_buf.m_pBuf, pnghdr, 41); } /* write footer (IDAT CRC-32, followed by IEND chunk) */ if (!tdefl_output_buffer_putter( "\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); for (i = 0; i < 4; ++i, c <<= 8) (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); /* compute final size of file, grab compressed data buffer and return */ *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we * can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's * where #defined out) */ return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tdefl_compressor *tdefl_compressor_alloc(void) { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } void tdefl_compressor_free(tdefl_compressor *pComp) { MZ_FREE(pComp); } #endif #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifndef MINIZ_NO_INFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Decompression (completely independent from all * compression API's) */ #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN \ switch (r->m_state) { \ case 0: #define TINFL_CR_RETURN(state_index, result) \ do { \ status = result; \ r->m_state = state_index; \ goto common_exit; \ case state_index:; \ } \ MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) \ do { \ for (;;) { \ TINFL_CR_RETURN(state_index, result); \ } \ } \ MZ_MACRO_END #define TINFL_CR_FINISH } #define TINFL_GET_BYTE(state_index, c) \ do { \ while (pIn_buf_cur >= pIn_buf_end) { \ TINFL_CR_RETURN(state_index, \ (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) \ ? TINFL_STATUS_NEEDS_MORE_INPUT \ : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ } \ c = *pIn_buf_cur++; \ } \ MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) \ do { \ mz_uint c; \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) \ do { \ if (num_bits < (mz_uint)(n)) { \ TINFL_NEED_BITS(state_index, n); \ } \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) \ do { \ if (num_bits < (mz_uint)(n)) { \ TINFL_NEED_BITS(state_index, n); \ } \ b = bit_buf & ((1 << (n)) - 1); \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END /* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes * remaining in the input buffer falls below 2. */ /* It reads just enough bytes from the input stream that are needed to decode * the next Huffman code (and absolutely no more). It works by trying to fully * decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. * If this fails, it reads another byte, and tries again until it succeeds or * until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ #define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ do { \ temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ } \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < 15); /* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex * than you would initially expect because the zlib API expects the decompressor * to never read */ /* beyond the final byte of the deflate stream. (In other words, when this macro * wants to read another byte from the input, it REALLY needs another byte in * order to fully */ /* decode the next Huffman code.) Handling this properly is particularly * important on raw deflate (non-zlib) streams, which aren't followed by a byte * aligned adler-32. */ /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in * input buffer, but we also need to handle the case where the user passes in * 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't * kick in here on this code. This is much trickier. */ #define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ do { \ int temp; \ mz_uint code_len, c; \ if (num_bits < 15) { \ if ((pIn_buf_end - pIn_buf_cur) < 2) { \ TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ } else { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | \ (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ pIn_buf_cur += 2; \ num_bits += 16; \ } \ } \ if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ bit_buf >>= code_len; \ num_bits -= code_len; \ } \ MZ_MACRO_END static void tinfl_clear_tree(tinfl_decompressor *r) { if (r->m_type == 0) MZ_CLEAR_ARR(r->m_tree_0); else if (r->m_type == 1) MZ_CLEAR_ARR(r->m_tree_1); else MZ_CLEAR_ARR(r->m_tree_2); } tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const mz_uint8 s_length_extra[31] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0}; static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; static const mz_uint16 s_min_table_sizes[3] = {257, 1, 4}; mz_int16 *pTrees[3]; mz_uint8 *pCode_sizes[3]; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer * is large enough to hold the entire output file (in which case it doesn't * matter). */ if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } pTrees[0] = r->m_tree_0; pTrees[1] = r->m_tree_1; pTrees[2] = r->m_tree_2; pCode_sizes[0] = r->m_code_size_0; pCode_sizes[1] = r->m_code_size_1; pCode_sizes[2] = r->m_code_size_2; num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_code_size_0; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_code_size_1, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_ARR(r->m_code_size_2); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; mz_int16 *pLookUp; mz_int16 *pTree; mz_uint8 *pCode_size; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pLookUp = r->m_look_up[r->m_type]; pTree = pTrees[r->m_type]; pCode_size = pCode_sizes[r->m_type]; MZ_CLEAR_ARR(total_syms); TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); tinfl_clear_tree(r); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pCode_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pLookUp[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTree[-tree_cur - 1]) { pTree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) { mz_uint8 *pSrc; for (;;) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { #ifdef MINIZ_UNALIGNED_USE_MEMCPY memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32) * 2); #else ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; #endif pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif while (counter > 2) { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; counter -= 3; } if (counter > 0) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); /* Ensure byte alignment and put back any bytes from the bitbuf if we've * looked ahead too far on gzip, or other Deflate streams followed by * arbitrary data. */ /* I'm being super conservative here. A number of simplifications can be made * to the byte alignment part, and the Adler32 check shouldn't ever need to * worry about reading from the bitbuf now. */ TINFL_SKIP_BITS(32, num_bits & 7); while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: /* As long as we aren't telling the caller that we NEED more input to make * forward progress: */ /* Put back any bytes from the bitbuf in case we've looked ahead too far on * gzip, or other Deflate streams followed by arbitrary data. */ /* We need to be very careful here to NOT push back any bytes we definitely * know we need to make forward progress, though, or we'll lock the caller up * into an inf loop. */ if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) { while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } } r->m_num_bits = num_bits; r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } /* Higher level helper functions. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for (;;) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress( &decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; memset(pDict, 0, TINFL_LZ_DICT_SIZE); tinfl_init(&decomp); for (;;) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } #ifndef MINIZ_NO_MALLOC tinfl_decompressor *tinfl_decompressor_alloc(void) { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) tinfl_init(pDecomp); return pDecomp; } void tinfl_decompressor_free(tinfl_decompressor *pDecomp) { MZ_FREE(pDecomp); } #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * Copyright 2016 Martin Raiber * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- .ZIP archive reading */ #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #if defined(_MSC_VER) || defined(__MINGW64__) #define WIN32_LEAN_AND_MEAN #include static WCHAR *mz_utf8z_to_widechar(const char *str) { int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); WCHAR *wStr = (WCHAR *)malloc(reqChars * sizeof(WCHAR)); MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, reqChars); return wStr; } static FILE *mz_fopen(const char *pFilename, const char *pMode) { WCHAR *wFilename = mz_utf8z_to_widechar(pFilename); WCHAR *wMode = mz_utf8z_to_widechar(pMode); FILE *pFile = NULL; #ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN pFile = _wfopen(wFilename, wMode); #else errno_t err = _wfopen_s(&pFile, wFilename, wMode); #endif free(wFilename); free(wMode); #ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN return pFile; #else return err ? NULL : pFile; #endif } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { WCHAR *wPath = mz_utf8z_to_widechar(pPath); WCHAR *wMode = mz_utf8z_to_widechar(pMode); FILE *pFile = NULL; #ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN pFile = _wfreopen(wPath, wMode, pStream); #else errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); #endif free(wPath); free(wMode); #ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN return pFile; #else return err ? NULL : pFile; #endif } static int mz_stat64(const char *path, struct __stat64 *buffer) { WCHAR *wPath = mz_utf8z_to_widechar(path); int res = _wstat64(wPath, buffer); free(wPath); return res; } static int mz_mkdir(const char *pDirname) { WCHAR *wDirname = mz_utf8z_to_widechar(pDirname); int res = _wmkdir(wDirname); free(wDirname); return res; } #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat64 #define MZ_FILE_STAT mz_stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) mz_mkdir(d) #elif defined(__MINGW32__) || defined(__WATCOMC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) _mkdir(d) #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #if defined(_WIN32) || defined(_WIN64) #define MZ_MKDIR(d) _mkdir(d) #else #define MZ_MKDIR(d) mkdir(d, 0755) #endif #elif defined(__USE_LARGEFILE64) /* gcc, clang */ #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) mkdir(d, 0755) #elif defined(__APPLE__) || defined(__FreeBSD__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen(p, m, s) #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) mkdir(d, 0755) #else #pragma message( \ "Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #ifdef __STRICT_ANSI__ #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #else #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #endif #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) mkdir(d, 0755) #endif /* #ifdef _MSC_VER */ #endif /* #ifdef MINIZ_NO_STDIO */ #ifndef CHMOD // Upon successful completion, a value of 0 is returned. // Otherwise, a value of -1 is returned and errno is set to indicate the error. // int chmod(const char *path, mode_t mode); #define CHMOD(f, m) chmod(f, m) #endif #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) /* Various ZIP archive enums. To completely avoid cross platform compiler * alignment and platform endian issues, miniz.c doesn't use structs for any of * this stuff. */ enum { /* ZIP archive identifiers and record sizes */ MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, /* ZIP64 archive identifier and record sizes */ MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, /* Central directory header record offsets */ MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, /* Local directory header offsets */ MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, /* End of central directory offsets */ MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, /* ZIP64 End of central directory locator offsets */ MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ /* ZIP64 End of central directory header offsets */ MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; /* The flags passed in when the archive is initially opened. */ mz_uint32 m_init_flags; /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ mz_bool m_zip64; /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 * will also be slammed to true too, even if we didn't find a zip64 end of * central dir header, etc.) */ mz_bool m_zip64_has_extended_info_fields; /* These fields are used by the file, FILE, memory, and memory/heap read/write * helpers. */ MZ_FILE *m_pFile; mz_uint64 m_file_archive_start_ofs; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) \ (array_ptr)->m_element_size = element_size #if defined(DEBUG) || defined(_DEBUG) static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) { MZ_ASSERT(index < pArray->m_size); return index; } #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ ((element_type *)((array_ptr) \ ->m_p))[mz_zip_array_range_check(array_ptr, index)] #else #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ ((element_type *)((array_ptr)->m_p))[index] #endif static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) { memset(pArray, 0, sizeof(mz_zip_array)); pArray->m_element_size = element_size; } static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; if (n > 0) memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif /* #ifdef _MSC_VER */ *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifndef MINIZ_NO_STDIO #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) { struct MZ_FILE_STAT_STRUCT file_stat; /* On Linux with x86 glibc, this call will fail on large files (I think >= * 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; *pTime = file_stat.st_mtime; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) { struct utimbuf t; memset(&t, 0, sizeof(t)); t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_TIME */ static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) { if (pZip) pZip->m_last_error = err_num; return MZ_FALSE; } static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; pZip->m_last_error = MZ_ZIP_NO_ERROR; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_init_flags = flags; pZip->m_pState->m_zip64 = MZ_FALSE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; pZip->m_zip_mode = MZ_ZIP_MODE_READING; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT( pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) \ do { \ mz_uint32 t = a; \ a = b; \ b = t; \ } \ MZ_MACRO_END /* Heap sort of lowercased filenames, used to help accelerate plain central * directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), * but it could allocate memory.) */ static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices; mz_uint32 start, end; const mz_uint32 size = pZip->m_total_files; if (size <= 1U) return; pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); start = (size - 2U) >> 1U; for (;;) { mz_uint64 child, root = start; for (;;) { if ((child = (root << 1U) + 1U) >= size) break; child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } if (!start) break; start--; } end = size - 1; while (end > 0) { mz_uint64 child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for (;;) { if ((child = (root << 1U) + 1U) >= end) break; child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) { mz_int64 cur_file_ofs; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; /* Basic sanity checks - reject files which are too small */ if (pZip->m_archive_size < record_size) return MZ_FALSE; /* Find the record by scanning the file from the end towards the beginning. */ cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for (;;) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) { mz_uint s = MZ_READ_LE32(pBuf + i); if (s == record_sig) { if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) break; } } if (i >= 0) { cur_file_ofs += i; break; } /* Give up if we've searched the entire file, or we've gone back "too far" * (~64kb) */ if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } *pOfs = cur_file_ofs; return MZ_TRUE; } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) { mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; mz_uint64 cdir_ofs = 0; mz_int64 cur_file_ofs = 0; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); mz_uint32 zip64_end_of_central_dir_locator_u32 [(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; mz_uint32 zip64_end_of_central_dir_header_u32 [(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; mz_uint64 zip64_end_of_central_dir_ofs = 0; /* Basic sanity checks - reject files which are too small, and check the first * 4 bytes of the file to make sure a local header is there. */ if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_locate_header_sig( pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); /* Read and verify the end of central directory record. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { zip64_end_of_central_dir_ofs = MZ_READ_LE64( pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { pZip->m_pState->m_zip64 = MZ_TRUE; } } } } } pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if (pZip->m_pState->m_zip64) { mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64( pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64( pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64( pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (zip64_total_num_of_disks != 1U) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); /* Check for miniz's practical limits */ if (zip64_cdir_total_entries > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; /* Check for miniz's current practical limits (sorry, this should be enough * for millions of files) */ if (zip64_size_of_central_directory > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); cdir_size = (mz_uint32)zip64_size_of_central_directory; num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); } if (pZip->m_total_files != cdir_entries_on_this_disk) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; /* Read the entire central directory into a heap block, and allocate another * heap block to hold the unsorted central dir file record offsets, and * possibly another to hold the sorted indices. */ if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); /* Now create an index into the central directory file records, do some * basic sanity checking on each record */ p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; mz_uint64 comp_size, decomp_size, local_header_ofs; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && (ext_data_size) && (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) { /* Attempt to find zip64 extended information field in the entry's extra * data */ mz_uint32 extra_size_remaining = ext_data_size; if (extra_size_remaining) { const mz_uint8 *pExtra_data; void *buf = NULL; if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) { buf = MZ_MALLOC(ext_data_size); if (buf == NULL) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } pExtra_data = (mz_uint8 *)buf; } else { pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; } do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { /* Ok, the archive didn't have any zip64 headers but it uses a * zip64 extended information field so mark it as zip64 anyway * (this can occur with infozip's zip util when it reads * compresses files from stdin). */ pZip->m_pState->m_zip64 = MZ_TRUE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); MZ_FREE(buf); } } /* I've seen archives that aren't marked as zip64 that uses zip64 ext * data, argh */ if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (comp_size != MZ_UINT32_MAX) { if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } void mz_zip_zero_struct(mz_zip_archive *pZip) { if (pZip) MZ_CLEAR_PTR(pZip); } static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_bool status = MZ_TRUE; if (!pZip) return MZ_FALSE; if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) { if (set_last_error) pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { return mz_zip_reader_end_internal(pZip, MZ_TRUE); } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) { if ((!pZip) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) { if (!pMem) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pNeeds_keepalive = NULL; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); } mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) { mz_uint64 file_size; MZ_FILE *pFile; if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pFile = MZ_FOPEN(pFilename, "rb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); file_size = archive_size; if (!file_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); } file_size = MZ_FTELL64(pFile); } /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_zip_type = MZ_ZIP_TYPE_FILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_init_file_v2_rpb(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) { mz_uint64 file_size; MZ_FILE *pFile; if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pFile = MZ_FOPEN(pFilename, "r+b"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); file_size = archive_size; if (!file_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); } file_size = MZ_FTELL64(pFile); } /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_zip_type = MZ_ZIP_TYPE_FILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) { mz_uint64 cur_file_ofs; if ((!pZip) || (!pFile)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); cur_file_ofs = MZ_FTELL64(pFile); if (!archive_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); archive_size = MZ_FTELL64(pFile) - cur_file_ofs; if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = archive_size; pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; } mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) { mz_uint bit_flag; mz_uint method; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if ((method != 0) && (method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); return MZ_FALSE; } if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); return MZ_FALSE; } if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, attribute_mapping_id, external_attr; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } /* Bugfix: This code was also checking if the internal attribute was non-zero, * which wasn't correct. */ /* Most/all zip writers (hopefully) set DOS file/directory attributes in the * low 16-bits, so check for the DOS directory flag and ignore the source OS * ID in the created by field. */ /* FIXME: Remove this check? Is it necessary - we already check the filename. */ attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; (void)attribute_mapping_id; external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) { return MZ_TRUE; } return MZ_FALSE; } static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) { mz_uint n; const mz_uint8 *p = pCentral_dir_header; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_FALSE; if ((!p) || (!pStat)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Extract fields from the central directory record. */ pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); /* Copy as much of the filename and comment as possible. */ n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; /* Set some flags for convienance */ pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); /* See if we need to read any zip64 extended information fields. */ /* Confusingly, these zip64 fields can be present even on non-zip64 archives * (Debian zip on a huge files from stdin piped to stdout creates them). */ if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) { /* Attempt to find zip64 extended information field in the entry's extra * data */ mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if (extra_size_remaining) { const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; mz_uint32 field_data_remaining = field_data_size; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_TRUE; if (pStat->m_uncomp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_uncomp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_comp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_comp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_local_header_ofs == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); } } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT( &pState->m_sorted_central_dir_offsets, mz_uint32, 0); const mz_uint32 size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); if (pIndex) *pIndex = 0; if (size) { /* yes I could use uint32_t's, but then we would have to add some special * case checks in the loop, argh, and */ /* honestly the major expense here on 32-bit CPU's will still be the * filename compare */ mz_int64 l = 0, h = (mz_int64)size - 1; while (l <= h) { mz_int64 m = l + ((h - l) >> 1); mz_uint32 file_index = pIndices[(mz_uint32)m]; int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } else if (comp < 0) l = m + 1; else h = m - 1; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint32 index; if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) return -1; else return (int)index; } mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) { mz_uint file_index; size_t name_len, comment_len; if (pIndex) *pIndex = 0; if ((!pZip) || (!pZip->m_pState) || (!pName)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* See if we can use a binary search */ if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) { return mz_zip_locate_file_binary_search(pZip, pName, pIndex); } /* Locate the entry by scanning the entire central directory */ name_len = strlen(pName); if (name_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); comment_len = pComment ? strlen(pComment) : 0; if (comment_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } static mz_bool mz_zip_reader_extract_to_mem_no_alloc1( mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (st) { file_stat = *st; } else if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Ensure supplied output buffer is large enough. */ needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); /* Read and parse the local directory entry. */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) { if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); } #endif return MZ_TRUE; } /* Decompress the file either directly from memory or from a file input * buffer. */ tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { /* Read directly from the archive in memory. */ pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { /* Use a user provided read buffer. */ if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { /* Temporarily allocate a read buffer. */ read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { /* The size_t cast here should be OK because we've verified that the output * buffer is >= file_stat.m_uncomp_size above */ size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress( &inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); status = TINFL_STATUS_FAILED; } #endif } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_zip_archive_file_stat file_stat; mz_uint64 alloc_size; void *pBuf; if (pSize) *pSize = 0; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return NULL; alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); return NULL; } if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint file_crc32 = MZ_CRC32_INIT; #endif mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Read and do some minimal validation of the local directory entry (this * doesn't crack the zip64 stuff, which we already have from the central dir) */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); /* Decompress the file either directly from memory or from a file input * buffer. */ if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pState->m_pMem) { if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; } else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); #endif } cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { file_crc32 = (mz_uint32)mz_crc32( file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); } #endif if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); status = TINFL_STATUS_FAILED; } else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress( &inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); #endif if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (file_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; } #endif } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } mz_zip_reader_extract_iter_state * mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_reader_extract_iter_state *pState; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; /* Argument sanity check */ if ((!pZip) || (!pZip->m_pState)) return NULL; /* Allocate an iterator status structure */ pState = (mz_zip_reader_extract_iter_state *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); if (!pState) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } /* Fetch file details */ if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Encryption and patch files are not supported. */ if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Init state - save args */ pState->pZip = pZip; pState->flags = flags; /* Init state - reset variables to defaults */ pState->status = TINFL_STATUS_DONE; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS pState->file_crc32 = MZ_CRC32_INIT; #endif pState->read_buf_ofs = 0; pState->out_buf_ofs = 0; pState->pRead_buf = NULL; pState->pWrite_buf = NULL; pState->out_blk_remain = 0; /* Read and parse the local directory entry. */ pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Decompress the file either directly from memory or from a file input * buffer. */ if (pZip->m_pState->m_pMem) { pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; pState->comp_remaining = pState->file_stat.m_comp_size; } else { if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, therefore intermediate read buffer required */ pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } else { /* Decompression not required - we will be reading directly into user * buffer, no temp buf required */ pState->read_buf_size = 0; } pState->read_buf_avail = 0; pState->comp_remaining = pState->file_stat.m_comp_size; } if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, init decompressor */ tinfl_init(&pState->inflator); /* Allocate write buffer */ if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pState->pRead_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } return pState; } mz_zip_reader_extract_iter_state * mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_uint32 file_index; /* Locate file index by name */ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return NULL; /* Construct iterator */ return mz_zip_reader_extract_iter_new(pZip, file_index, flags); } size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state *pState, void *pvBuf, size_t buf_size) { size_t copied_to_caller = 0; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) return 0; if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data, calc * amount to return. */ copied_to_caller = (size_t)MZ_MIN(buf_size, pState->comp_remaining); /* Zip is in memory....or requires reading from a file? */ if (pState->pZip->m_pState->m_pMem) { /* Copy data to caller's buffer */ memcpy(pvBuf, pState->pRead_buf, copied_to_caller); pState->pRead_buf = ((mz_uint8 *)pState->pRead_buf) + copied_to_caller; } else { /* Read directly into caller's buffer */ if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) { /* Failed to read all that was asked for, flag failure and alert user */ mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; copied_to_caller = 0; } } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Compute CRC if not returning compressed data only */ if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) pState->file_crc32 = (mz_uint32)mz_crc32( pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); #endif /* Advance offsets, dec counters */ pState->cur_file_ofs += copied_to_caller; pState->out_buf_ofs += copied_to_caller; pState->comp_remaining -= copied_to_caller; } else { do { /* Calc ptr to write buffer - given current output pos and block size */ mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); /* Calc max output size - given current output pos and block size */ size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if (!pState->out_blk_remain) { /* Read more data from file if none available (and reading from file) */ if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) { /* Calc read size */ pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) { mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Advance offsets, dec counters */ pState->cur_file_ofs += pState->read_buf_avail; pState->comp_remaining -= pState->read_buf_avail; pState->read_buf_ofs = 0; } /* Perform decompression */ in_buf_size = (size_t)pState->read_buf_avail; pState->status = tinfl_decompress( &pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); pState->read_buf_avail -= in_buf_size; pState->read_buf_ofs += in_buf_size; /* Update current output block size remaining */ pState->out_blk_remain = out_buf_size; } if (pState->out_blk_remain) { /* Calc amount to return. */ size_t to_copy = MZ_MIN((buf_size - copied_to_caller), pState->out_blk_remain); /* Copy data to caller's buffer */ memcpy((mz_uint8 *)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Perform CRC */ pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); #endif /* Decrement data consumed from block */ pState->out_blk_remain -= to_copy; /* Inc output offset, while performing sanity check */ if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Increment counter of data copied to caller */ copied_to_caller += to_copy; } } while ((copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT))); } /* Return how many bytes were copied into user buffer */ return copied_to_caller; } mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state *pState) { int status; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) return MZ_FALSE; /* Was decompression completed and requested? */ if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); pState->status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (pState->file_crc32 != pState->file_stat.m_crc32) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; } #endif } /* Free buffers */ if (!pState->pZip->m_pState->m_pMem) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); if (pState->pWrite_buf) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); /* Save status */ status = pState->status; /* Free context */ pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); return status == TINFL_STATUS_DONE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); status = mz_zip_reader_extract_to_callback( pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) { if (status) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) { mz_zip_archive_file_stat file_stat; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return mz_zip_reader_extract_to_callback( pZip, file_index, mz_zip_file_write_callback, pFile, flags); } mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); } #endif /* #ifndef MINIZ_NO_STDIO */ static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_uint32 *p = (mz_uint32 *)pOpaque; (void)file_ofs; *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); return n; } mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_archive_file_stat file_stat; mz_zip_internal_state *pState; const mz_uint8 *pCentral_dir_header; mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint64 local_header_ofs = 0; mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; mz_bool has_data_descriptor; mz_uint32 local_header_bit_flags; mz_zip_array file_data_array; mz_zip_array_init(&file_data_array, 1); if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (file_index > pZip->m_total_files) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_is_encrypted) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports stored and deflate. */ if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); if (!file_stat.m_is_supported) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); /* Read and parse the local directory entry. */ local_header_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); has_data_descriptor = (local_header_bit_flags & 8) != 0; if (local_header_filename_len != strlen(file_stat.m_filename)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (!mz_zip_array_resize( pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); goto handle_failure; } if (local_header_filename_len) { if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } /* I've seen 1 archive that had the same pathname, but used backslashes in * the local dir and forward slashes in the central dir. Do we care about * this? For now, this case will fail validation. */ if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_uint32 extra_size_remaining = local_header_extra_len; const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } /* TODO: parse local header extra data when local_header_comp_size is * 0xFFFFFFFF! (big_descriptor.zip) */ /* I've seen zips in the wild with the data descriptor bit set, but proper * local header values and bogus data descriptors */ if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) { mz_uint8 descriptor_buf[32]; mz_bool has_id; const mz_uint8 *pSrc; mz_uint32 file_crc32; mz_uint64 comp_size = 0, uncomp_size = 0; mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; file_crc32 = MZ_READ_LE32(pSrc); if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); } else { comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); } if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } else { if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } mz_zip_array_clear(pZip, &file_data_array); if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) { if (!mz_zip_reader_extract_to_callback( pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) return MZ_FALSE; /* 1 more check to be sure, although the extract checks too. */ if (uncomp_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); return MZ_FALSE; } } return MZ_TRUE; handle_failure: mz_zip_array_clear(pZip, &file_data_array); return MZ_FALSE; } mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) { mz_zip_internal_state *pState; mz_uint32 i; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Basic sanity checks */ if (!pState->m_zip64) { if (pZip->m_total_files > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (pZip->m_archive_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } else { if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } for (i = 0; i < pZip->m_total_files; i++) { if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) { mz_uint32 found_index; mz_zip_archive_file_stat stat; if (!mz_zip_reader_file_stat(pZip, i, &stat)) return MZ_FALSE; if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) return MZ_FALSE; /* This check can fail if there are duplicate filenames in the archive * (which we don't check for when writing - that's up to the user) */ if (found_index != i) return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); } if (!mz_zip_validate_file(pZip, i, flags)) return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if ((!pMem) || (!size)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if (!pFilename) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #endif /* #ifndef MINIZ_NO_STDIO */ /* ------------------- .ZIP archive writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) { mz_write_le32(p, (mz_uint32)v); mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) #define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); if (!n) return 0; /* An allocation this big is likely to just fail on 32-bit systems, so don't * even go there. */ if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); return 0; } if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc( pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return 0; } pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) { mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) { if (!pZip->m_pRead) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (pZip->m_file_offset_alignment) { /* Ensure user specified file offset alignment is a power of 2. */ if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_zip64 = zip64; pZip->m_pState->m_zip64_has_extended_info_fields = zip64; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { return mz_zip_writer_init_v2(pZip, existing_size, 0); } mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) { mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); return 0; } return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); } mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN( pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } pZip->m_pState->m_pFile = pFile; pZip->m_zip_type = MZ_ZIP_TYPE_FILE; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_ARR(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) { pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, 0, flags)) return MZ_FALSE; pZip->m_pState->m_pFile = pFile; pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { /* We don't support converting a non-zip64 file to zip64 - this seems like * more trouble than it's worth. (What about the existing 32-bit data * descriptors that could follow the compressed data?) */ if (!pZip->m_pState->m_zip64) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* No sense in trying to write to an archive that's already at the support max * size */ if (pZip->m_pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO (void)pFilename; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); #else if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (!pFilename) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Archive is being read from stdio and was originally opened only for * reading. Try to reopen as writable. */ if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { /* The mz_zip_archive is now in a bogus state because pState->m_pFile is * NULL, so just close it. */ mz_zip_reader_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } } pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; #endif /* #ifdef MINIZ_NO_STDIO */ } else if (pState->m_pMem) { /* Archive lives in a memory block. Assume it's from the heap that we can * resize using the realloc callback. */ if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; } /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ else if (!pZip->m_pWrite) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Start writing new files at the archive's current central directory * location. */ /* TODO: We could add a flag that lets the user start writing immediately * AFTER the existing central dir - this would be safer. */ pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_central_directory_file_ofs = 0; /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ /* Even though we're now in write mode, files can still be extracted and * verified, but file locates will be slow. */ /* TODO: We could easily maintain the sorted central directory offsets. */ mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init_from_reader_v2_noreopen(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { /* We don't support converting a non-zip64 file to zip64 - this seems like * more trouble than it's worth. (What about the existing 32-bit data * descriptors that could follow the compressed data?) */ if (!pZip->m_pState->m_zip64) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* No sense in trying to write to an archive that's already at the support max * size */ if (pZip->m_pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO (void)pFilename; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); #else if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (!pFilename) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; #endif /* #ifdef MINIZ_NO_STDIO */ } else if (pState->m_pMem) { /* Archive lives in a memory block. Assume it's from the heap that we can * resize using the realloc callback. */ if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; } /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ else if (!pZip->m_pWrite) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Start writing new files at the archive's current central directory * location. */ /* TODO: We could add a flag that lets the user start writing immediately * AFTER the existing central dir - this would be safer. */ pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_central_directory_file_ofs = 0; /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ /* Even though we're now in write mode, files can still be extracted and * verified, but file locates will be slow. */ /* TODO: We could easily maintain the sorted central directory offsets. */ mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); } /* TODO: pArchive_name is a terrible name here! */ mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } #define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE \ (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) #define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE \ (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) { mz_uint8 *pDst = pBuf; mz_uint32 field_size = 0; MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); MZ_WRITE_LE16(pDst + 2, 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { MZ_WRITE_LE64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pComp_size) { MZ_WRITE_LE64(pDst, *pComp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pLocal_header_ofs) { MZ_WRITE_LE64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } MZ_WRITE_LE16(pBuf + 2, field_size); return (mz_uint32)(pDst - pBuf); } static mz_bool mz_zip_writer_create_local_dir_header( mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header( mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir( mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; if (!pZip->m_pState->m_zip64) { if (local_header_ofs > 0xFFFFFFFF) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!mz_zip_writer_create_central_dir_header( pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { /* Try to resize the central directory array back into its original state. */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { /* Basic ZIP archive filename validity checks: Valid filenames cannot start * with a forward slash, cannot contain a drive letter, and cannot use * DOS-style backward slashes. */ if (*pArchive_name == '/') return MZ_FALSE; /* Making sure the name does not contain drive letters or DOS style backward * slashes is the responsibility of the program using miniz*/ return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { return mz_zip_writer_add_mem_ex_v2( pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); } mz_bool mz_zip_writer_add_mem_ex_v2( mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint16 bit_flags = 0; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); #ifndef MINIZ_NO_TIME if (last_modified != NULL) { mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); } else { MZ_TIME_T cur_time; time(&cur_time); mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); } #endif /* #ifndef MINIZ_NO_TIME */ if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { /* Set DOS Subdirectory attribute bit. */ ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; /* Subdirectories cannot contain data. */ if ((buf_size) || (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* Try to do any allocations before writing to the archive, so if an * allocation fails the file remains unmodified. (A good idea if we're doing * an in-place modification.) */ if ((!mz_zip_array_ensure_room( pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes; MZ_CLEAR_ARR(local_dir_header); if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { method = MZ_DEFLATED; } if (pState->m_zip64) { if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pExtra_data != NULL) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += buf_size; comp_size = buf_size; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params( level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; if (uncomp_size) { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir( pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_add_read_buf_callback( mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void *callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 gen_flags; mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_zip_internal_state *pState; mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) { /* Source file is too large for non-zip64 */ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ pState->m_zip64 = MZ_TRUE; } /* We could support this, but why? */ if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } #ifndef MINIZ_NO_TIME if (pFile_time) { mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); } #endif if (max_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_archive_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (max_size && level) { method = MZ_DEFLATED; } MZ_CLEAR_ARR(local_dir_header); if (pState->m_zip64) { if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); else extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, NULL, NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (max_size) { void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!level) { while (1) { size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); if (n == 0) break; if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } file_ofs += n; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); cur_archive_file_ofs += n; } uncomp_size = file_ofs; comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params( level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); } for (;;) { tdefl_status status; tdefl_flush flush = TDEFL_NO_FLUSH; size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); break; } file_ofs += n; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) flush = TDEFL_FULL_FLUSH; if (n == 0) flush = TDEFL_FINISH; status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) { mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); break; } } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return MZ_FALSE; } uncomp_size = file_ofs; comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) { if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); cur_archive_header_file_ofs = local_dir_header_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); if (pExtra_data != NULL) { cur_archive_header_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_header_file_ofs += archive_name_size; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_header_file_ofs += extra_size; } } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir( pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pSrc_file); } mz_bool mz_zip_writer_add_cfile( mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { return mz_zip_writer_add_read_buf_callback( pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, ext_attributes, user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); } mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes) { MZ_FILE *pSrc_file = NULL; mz_uint64 uncomp_size = 0; MZ_TIME_T file_modified_time; MZ_TIME_T *pFile_time = NULL; mz_bool status; memset(&file_modified_time, 0, sizeof(file_modified_time)); #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) pFile_time = &file_modified_time; if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); #endif pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); status = mz_zip_writer_add_cfile( pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, ext_attributes, NULL, 0, NULL, 0); MZ_FCLOSE(pSrc_file); return status; } #endif /* #ifndef MINIZ_NO_STDIO */ static mz_bool mz_zip_writer_update_zip64_extension_block( mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { /* + 64 should be enough for any new zip64 data */ if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) { mz_uint8 new_ext_block[64]; mz_uint8 *pDst = new_ext_block; mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); mz_write_le16(pDst + sizeof(mz_uint16), 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { mz_write_le64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); } if (pComp_size) { mz_write_le64(pDst, *pComp_size); pDst += sizeof(mz_uint64); } if (pLocal_header_ofs) { mz_write_le64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); } if (pDisk_start) { mz_write_le32(pDst, *pDisk_start); pDst += sizeof(mz_uint32); } mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if ((pExt) && (ext_len)) { mz_uint32 extra_size_remaining = ext_len; const mz_uint8 *pExtra_data = pExt; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } return MZ_TRUE; } /* TODO: This func is now pretty freakin complex due to zip64, split it up? */ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; mz_zip_archive_file_stat src_file_stat; mz_uint32 src_filename_len, src_comment_len, src_ext_len; mz_uint32 local_header_filename_size, local_header_extra_len; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Don't support copying files from zip64 archives to non-zip64, even though * in some cases this is possible */ if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Get pointer to the source central dir header and crack it */ if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 * fudge factor in case we need to add more extra data) */ if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); if (!pState->m_zip64) { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { /* TODO: Our zip64 support still has some 32-bit limits that may not be * worth fixing. */ if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) return MZ_FALSE; cur_src_file_ofs = src_file_stat.m_local_header_ofs; cur_dst_file_ofs = pZip->m_archive_size; /* Read the source archive's local dir header */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Compute the total size we need to copy (filename+extra data+compressed * data) */ local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; /* Try to find a zip64 extended information field */ if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_zip_array file_data_array; const mz_uint8 *pExtra_data; mz_uint32 extra_size_remaining = local_header_extra_len; mz_zip_array_init(&file_data_array, 1); if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } pExtra_data = (const mz_uint8 *)file_data_array.m_p; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64( pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); mz_zip_array_clear(pZip, &file_data_array); } if (!pState->m_zip64) { /* Try to detect if the new archive will most likely wind up too big and * bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which * could be present, +64 is a fudge factor). */ /* We also check when the archive is finalized so this doesn't need to be * perfect. */ mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; if (approx_new_archive_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } /* Write dest archive padding */ if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } /* The original zip's local header+ext block doesn't change, even with zip64, * so we can just copy it over to the dest zip */ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Copy over the source archive bytes to the dest archive, also ensure we have * enough buf space to handle optional data descriptor */ if (NULL == (pBuf = pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); while (src_archive_bytes_remaining) { n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_dst_file_ofs += n; src_archive_bytes_remaining -= n; } /* Now deal with the optional data descriptor */ bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { /* Copy data descriptor */ if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { /* src is zip64, dest must be zip64 */ /* name uint32_t's */ /* id 1 (optional in zip64?) */ /* crc 1 */ /* comp_size 2 */ /* uncomp_size 2 */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); } else { /* src is NOT zip64 */ mz_bool has_id; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); if (pZip->m_pState->m_zip64) { /* dest is zip64, so upgrade the data descriptor */ const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor); const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32)); const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2 * sizeof(mz_uint32)); mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); n = sizeof(mz_uint32) * 6; } else { /* dest is NOT zip64, just copy it as-is */ n = sizeof(mz_uint32) * (has_id ? 4 : 3); } } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); /* Finally, add the new central dir header */ orig_central_dir_size = pState->m_central_dir.m_size; memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); if (pState->m_zip64) { /* This is the painful part: We need to write a new central dir header + ext * block with updated zip64 fields, and ensure the old fields (if any) are * not included. */ const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; mz_zip_array new_ext_block; mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); if (!mz_zip_writer_update_zip64_extension_block( &new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) { mz_zip_array_clear(pZip, &new_ext_block); return MZ_FALSE; } MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) { mz_zip_array_clear(pZip, &new_ext_block); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } mz_zip_array_clear(pZip, &new_ext_block); } else { /* sanity checks */ if (cur_dst_file_ofs > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (local_dir_header_ofs >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } } /* This shouldn't trigger unless we screwed up during the initial sanity * checks */ if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) { /* TODO: Support central dirs >= 32-bits in size */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); } n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[256]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { /* Write central directory */ central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += central_dir_size; } if (pState->m_zip64) { /* Write zip64 end of central directory header */ mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; /* Write zip64 end of central directory locator */ MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; } /* Write end of central directory record */ MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) { if ((!ppBuf) || (!pSize)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); *ppBuf = NULL; *pSize = 0; if ((!pZip) || (!pZip->m_pState)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_pWrite != mz_zip_heap_write_func) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *ppBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { return mz_zip_writer_end_internal(pZip, MZ_TRUE); } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place( const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { return mz_zip_add_mem_to_archive_file_in_place_v2( pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); } mz_bool mz_zip_add_mem_to_archive_file_in_place_v2( const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; mz_zip_zero_struct(&zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (!mz_zip_writer_validate_archive_name(pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_FILENAME; return MZ_FALSE; } /* Important: The regular non-64 bit version of stat() can fail here if the * file is very large, which could cause the archive to be overwritten. */ /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { /* Create a new archive. */ if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } created_new_archive = MZ_TRUE; } else { /* Append to an existing archive. */ if (!mz_zip_reader_init_file_v2( &zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); actual_err = zip_archive.m_last_error; /* Always finalize, even if adding failed for some reason, so we have a valid * central directory. (This may not always succeed, but we can try.) */ if (!mz_zip_writer_finalize_archive(&zip_archive)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if (!mz_zip_writer_end_internal(&zip_archive, status)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if ((!status) && (created_new_archive)) { /* It's a new archive and something went wrong, so just delete it. */ int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } if (pErr) *pErr = actual_err; return status; } void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) { mz_uint32 file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return NULL; } mz_zip_zero_struct(&zip_archive); if (!mz_zip_reader_init_file_v2( &zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return NULL; } if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) { p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); } mz_zip_reader_end_internal(&zip_archive, p != NULL); if (pErr) *pErr = zip_archive.m_last_error; return p; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ /* ------------------- Misc utils */ mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; } mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; } mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = err_num; return prev_err; } mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) { if (!pZip) return MZ_ZIP_INVALID_PARAMETER; return pZip->m_last_error; } mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) { return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); } mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = MZ_ZIP_NO_ERROR; return prev_err; } const char *mz_zip_get_error_string(mz_zip_error mz_err) { switch (mz_err) { case MZ_ZIP_NO_ERROR: return "no error"; case MZ_ZIP_UNDEFINED_ERROR: return "undefined error"; case MZ_ZIP_TOO_MANY_FILES: return "too many files"; case MZ_ZIP_FILE_TOO_LARGE: return "file too large"; case MZ_ZIP_UNSUPPORTED_METHOD: return "unsupported method"; case MZ_ZIP_UNSUPPORTED_ENCRYPTION: return "unsupported encryption"; case MZ_ZIP_UNSUPPORTED_FEATURE: return "unsupported feature"; case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: return "failed finding central directory"; case MZ_ZIP_NOT_AN_ARCHIVE: return "not a ZIP archive"; case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: return "invalid header or archive is corrupted"; case MZ_ZIP_UNSUPPORTED_MULTIDISK: return "unsupported multidisk archive"; case MZ_ZIP_DECOMPRESSION_FAILED: return "decompression failed or archive is corrupted"; case MZ_ZIP_COMPRESSION_FAILED: return "compression failed"; case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: return "unexpected decompressed size"; case MZ_ZIP_CRC_CHECK_FAILED: return "CRC-32 check failed"; case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: return "unsupported central directory size"; case MZ_ZIP_ALLOC_FAILED: return "allocation failed"; case MZ_ZIP_FILE_OPEN_FAILED: return "file open failed"; case MZ_ZIP_FILE_CREATE_FAILED: return "file create failed"; case MZ_ZIP_FILE_WRITE_FAILED: return "file write failed"; case MZ_ZIP_FILE_READ_FAILED: return "file read failed"; case MZ_ZIP_FILE_CLOSE_FAILED: return "file close failed"; case MZ_ZIP_FILE_SEEK_FAILED: return "file seek failed"; case MZ_ZIP_FILE_STAT_FAILED: return "file stat failed"; case MZ_ZIP_INVALID_PARAMETER: return "invalid parameter"; case MZ_ZIP_INVALID_FILENAME: return "invalid filename"; case MZ_ZIP_BUF_TOO_SMALL: return "buffer too small"; case MZ_ZIP_INTERNAL_ERROR: return "internal error"; case MZ_ZIP_FILE_NOT_FOUND: return "file not found"; case MZ_ZIP_ARCHIVE_TOO_LARGE: return "archive is too large"; case MZ_ZIP_VALIDATION_FAILED: return "validation failed"; case MZ_ZIP_WRITE_CALLBACK_FAILED: return "write callback failed"; case MZ_ZIP_TOTAL_ERRORS: return "total errors"; default: break; } return "unknown error"; } /* Note: Just because the archive is not zip64 doesn't necessarily mean it * doesn't have Zip64 extended information extra field, argh. */ mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return MZ_FALSE; return pZip->m_pState->m_zip64; } size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_central_dir.m_size; } mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) { if (!pZip) return 0; return pZip->m_archive_size; } mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_file_archive_start_ofs; } MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_pFile; } size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { return mz_zip_file_stat_internal( pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); } mz_bool mz_zip_end(mz_zip_archive *pZip) { if (!pZip) return MZ_FALSE; if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) return mz_zip_reader_end(pZip); #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) return mz_zip_writer_end(pZip); #endif return MZ_FALSE; } #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ simutrans-124.5/src/external/readme.txt000066400000000000000000000001031520675460500201650ustar00rootroot00000000000000These files are from https://github.com/kuba--/zip/tree/master/src simutrans-124.5/src/external/refresh.sh000077500000000000000000000003131520675460500201670ustar00rootroot00000000000000#!/bin/sh wget "https://github.com/kuba--/zip/zipball/master/" -O master.zip subdir="$(unzip -lqq master.zip | grep -o -e "kuba---zip-[^/]*" | uniq)" unzip -j master.zip "$subdir/src/*" rm -f master.zip simutrans-124.5/src/external/zip.c000066400000000000000000001516101520675460500171470ustar00rootroot00000000000000/* * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #define __STDC_WANT_LIB_EXT1__ 1 #include #include #include #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) /* Win32, DOS, MSVC, MSVS */ #include #define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL) #define HAS_DEVICE(P) \ ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ (P)[1] == ':') #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) #else #include // needed for symlink() #define STRCLONE(STR) ((STR) ? strdup(STR) : NULL) #endif #ifdef __MINGW32__ #include #include #endif #include "miniz.h" #include "zip.h" #ifdef _MSC_VER #include #define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0)) #define fileno _fileno #endif #if defined(__TINYC__) && (defined(_WIN32) || defined(_WIN64)) #include #define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0)) #define fileno _fileno #endif #ifndef HAS_DEVICE #define HAS_DEVICE(P) 0 #endif #ifndef FILESYSTEM_PREFIX_LEN #define FILESYSTEM_PREFIX_LEN(P) 0 #endif #ifndef ISSLASH #define ISSLASH(C) ((C) == '/' || (C) == '\\') #endif #define CLEANUP(ptr) \ do { \ if (ptr) { \ free((void *)ptr); \ ptr = NULL; \ } \ } while (0) #define UNX_IFDIR 0040000 /* Unix directory */ #define UNX_IFREG 0100000 /* Unix regular file */ #define UNX_IFSOCK 0140000 /* Unix socket (BSD, not SysV or Amiga) */ #define UNX_IFLNK 0120000 /* Unix symbolic link (not SysV, Amiga) */ #define UNX_IFBLK 0060000 /* Unix block special (not Amiga) */ #define UNX_IFCHR 0020000 /* Unix character special (not Amiga) */ #define UNX_IFIFO 0010000 /* Unix fifo (BCC, not MSC or Amiga) */ struct zip_entry_t { ssize_t index; char *name; mz_uint64 uncomp_size; mz_uint64 comp_size; mz_uint32 uncomp_crc32; mz_uint64 dir_offset; mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; mz_uint64 header_offset; mz_uint16 method; mz_zip_writer_add_state state; tdefl_compressor comp; mz_uint32 external_attr; time_t m_time; }; struct zip_t { mz_zip_archive archive; mz_uint level; struct zip_entry_t entry; }; enum zip_modify_t { MZ_KEEP = 0, MZ_DELETE = 1, MZ_MOVE = 2, }; struct zip_entry_mark_t { ssize_t file_index; enum zip_modify_t type; mz_uint64 m_local_header_ofs; size_t lf_length; }; static const char *const zip_errlist[33] = { NULL, "not initialized\0", "invalid entry name\0", "entry not found\0", "invalid zip mode\0", "invalid compression level\0", "no zip 64 support\0", "memset error\0", "cannot write data to entry\0", "cannot initialize tdefl compressor\0", "invalid index\0", "header not found\0", "cannot flush tdefl buffer\0", "cannot write entry header\0", "cannot create entry header\0", "cannot write to central dir\0", "cannot open file\0", "invalid entry type\0", "extracting data using no memory allocation\0", "file not found\0", "no permission\0", "out of memory\0", "invalid zip archive name\0", "make dir error\0", "symlink error\0", "close archive error\0", "capacity size too small\0", "fseek error\0", "fread error\0", "fwrite error\0", "cannot initialize reader\0", "cannot initialize writer\0", "cannot initialize writer from reader\0", }; const char *zip_strerror(int errnum) { errnum = -errnum; if (errnum <= 0 || errnum >= 33) { return NULL; } return zip_errlist[errnum]; } static const char *zip_basename(const char *name) { char const *p; char const *base = name += FILESYSTEM_PREFIX_LEN(name); int all_slashes = 1; for (p = name; *p; p++) { if (ISSLASH(*p)) base = p + 1; else all_slashes = 0; } /* If NAME is all slashes, arrange to return `/'. */ if (*base == '\0' && ISSLASH(*name) && all_slashes) --base; return base; } static int zip_mkpath(char *path) { char *p; char npath[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1]; int len = 0; int has_device = HAS_DEVICE(path); memset(npath, 0, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1); if (has_device) { // only on windows npath[0] = path[0]; npath[1] = path[1]; len = 2; } for (p = path + len; *p && len < MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE; p++) { if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) { #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) #else if ('\\' == *p) { *p = '/'; } #endif if (MZ_MKDIR(npath) == -1) { if (errno != EEXIST) { return ZIP_EMKDIR; } } } npath[len++] = *p; } return 0; } static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) { char c; size_t i; char *rpl = (char *)calloc((1 + n), sizeof(char)); char *begin = rpl; if (!rpl) { return NULL; } for (i = 0; (i < n) && (c = *str++); ++i) { if (c == oldchar) { c = newchar; } *rpl++ = c; } return begin; } static char *zip_name_normalize(char *name, char *const nname, size_t len) { size_t offn = 0; size_t offnn = 0, ncpy = 0; if (name == NULL || nname == NULL || len <= 0) { return NULL; } // skip trailing '/' while (ISSLASH(*name)) name++; for (; offn < len; offn++) { if (ISSLASH(name[offn])) { if (ncpy > 0 && strcmp(&nname[offnn], ".\0") && strcmp(&nname[offnn], "..\0")) { offnn += ncpy; nname[offnn++] = name[offn]; // append '/' } ncpy = 0; } else { nname[offnn + ncpy] = name[offn]; ncpy++; } } // at the end, extra check what we've already copied if (ncpy == 0 || !strcmp(&nname[offnn], ".\0") || !strcmp(&nname[offnn], "..\0")) { nname[offnn] = 0; } return nname; } static mz_bool zip_name_match(const char *name1, const char *name2) { char *nname2 = NULL; #ifdef ZIP_RAW_ENTRYNAME nname2 = STRCLONE(name2); #else nname2 = zip_strrpl(name2, strlen(name2), '\\', '/'); #endif if (!nname2) { return MZ_FALSE; } mz_bool res = (strcmp(name1, nname2) == 0) ? MZ_TRUE : MZ_FALSE; CLEANUP(nname2); return res; } static int zip_archive_truncate(mz_zip_archive *pzip) { mz_zip_internal_state *pState = pzip->m_pState; mz_uint64 file_size = pzip->m_archive_size; if ((pzip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { return 0; } if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { if (pState->m_pFile) { int fd = fileno(pState->m_pFile); return ftruncate(fd, file_size); } } return 0; } static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { int err = 0; mz_uint i, n; char path[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1]; char symlink_to[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1]; mz_zip_archive_file_stat info; size_t dirlen = 0, filename_size = MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE; mz_uint32 xattr = 0; memset(path, 0, sizeof(path)); memset(symlink_to, 0, sizeof(symlink_to)); dirlen = strlen(dir); if (dirlen + 1 > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE) { return ZIP_EINVENTNAME; } memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); #if defined(_MSC_VER) strcpy_s(path, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, dir); #else strncpy(path, dir, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE); #endif if (!ISSLASH(path[dirlen - 1])) { #if defined(_WIN32) || defined(__WIN32__) path[dirlen] = '\\'; #else path[dirlen] = '/'; #endif ++dirlen; } if (filename_size > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - dirlen) { filename_size = MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - dirlen; } // Get and print information about each file in the archive. n = mz_zip_reader_get_num_files(zip_archive); for (i = 0; i < n; ++i) { if (!mz_zip_reader_file_stat(zip_archive, i, &info)) { // Cannot get information about zip archive; err = ZIP_ENOENT; goto out; } if (!zip_name_normalize(info.m_filename, info.m_filename, strlen(info.m_filename))) { // Cannot normalize file name; err = ZIP_EINVENTNAME; goto out; } #if defined(_MSC_VER) strncpy_s(&path[dirlen], filename_size, info.m_filename, filename_size); #else strncpy(&path[dirlen], info.m_filename, filename_size); #endif err = zip_mkpath(path); if (err < 0) { // Cannot make a path goto out; } if ((((info.m_version_made_by >> 8) == 3) || ((info.m_version_made_by >> 8) == 19)) // if zip is produced on Unix or macOS (3 and 19 from // section 4.4.2.2 of zip standard) && info.m_external_attr & (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 // is directory) #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) #else if (info.m_uncomp_size > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE || !mz_zip_reader_extract_to_mem_no_alloc( zip_archive, i, symlink_to, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, 0, NULL, 0)) { err = ZIP_EMEMNOALLOC; goto out; } symlink_to[info.m_uncomp_size] = '\0'; if (symlink(symlink_to, path) != 0) { err = ZIP_ESYMLINK; goto out; } #endif } else { if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) { if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) { // Cannot extract zip archive to file err = ZIP_ENOFILE; goto out; } } #if defined(_MSC_VER) || defined(PS4) (void)xattr; // unused #else xattr = (info.m_external_attr >> 16) & 0xFFFF; if (xattr > 0 && xattr <= MZ_UINT16_MAX) { if (CHMOD(path, (mode_t)xattr) < 0) { err = ZIP_ENOPERM; goto out; } } #endif } if (on_extract) { if (on_extract(path, arg) < 0) { goto out; } } } out: // Close the archive, freeing any resources it was using if (!mz_zip_reader_end(zip_archive)) { // Cannot end zip reader err = ZIP_ECLSZIP; } return err; } static inline void zip_archive_finalize(mz_zip_archive *pzip) { mz_zip_writer_finalize_archive(pzip); zip_archive_truncate(pzip); } static ssize_t zip_entry_mark(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, const ssize_t n, char *const entries[], const size_t len) { ssize_t i = 0; ssize_t err = 0; if (!zip || !entry_mark || !entries) { return ZIP_ENOINIT; } mz_zip_archive_file_stat file_stat; mz_uint64 d_pos = UINT64_MAX; for (i = 0; i < n; ++i) { if ((err = zip_entry_openbyindex(zip, i))) { return (ssize_t)err; } mz_bool name_matches = MZ_FALSE; { size_t j; for (j = 0; j < len; ++j) { if (zip_name_match(zip->entry.name, entries[j])) { name_matches = MZ_TRUE; break; } } } if (name_matches) { entry_mark[i].type = MZ_DELETE; } else { entry_mark[i].type = MZ_KEEP; } if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) { return ZIP_ENOENT; } zip_entry_close(zip); entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs; entry_mark[i].file_index = (ssize_t)-1; entry_mark[i].lf_length = 0; if ((entry_mark[i].type) == MZ_DELETE && (d_pos > entry_mark[i].m_local_header_ofs)) { d_pos = entry_mark[i].m_local_header_ofs; } } for (i = 0; i < n; ++i) { if ((entry_mark[i].m_local_header_ofs > d_pos) && (entry_mark[i].type != MZ_DELETE)) { entry_mark[i].type = MZ_MOVE; } } return err; } static ssize_t zip_entry_markbyindex(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, const ssize_t n, size_t entries[], const size_t len) { ssize_t i = 0; ssize_t err = 0; if (!zip || !entry_mark || !entries) { return ZIP_ENOINIT; } mz_zip_archive_file_stat file_stat; mz_uint64 d_pos = UINT64_MAX; for (i = 0; i < n; ++i) { if ((err = zip_entry_openbyindex(zip, i))) { return (ssize_t)err; } mz_bool matches = MZ_FALSE; { size_t j; for (j = 0; j < len; ++j) { if ((size_t)i == entries[j]) { matches = MZ_TRUE; break; } } } if (matches) { entry_mark[i].type = MZ_DELETE; } else { entry_mark[i].type = MZ_KEEP; } if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) { return ZIP_ENOENT; } zip_entry_close(zip); entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs; entry_mark[i].file_index = (ssize_t)-1; entry_mark[i].lf_length = 0; if ((entry_mark[i].type) == MZ_DELETE && (d_pos > entry_mark[i].m_local_header_ofs)) { d_pos = entry_mark[i].m_local_header_ofs; } } for (i = 0; i < n; ++i) { if ((entry_mark[i].m_local_header_ofs > d_pos) && (entry_mark[i].type != MZ_DELETE)) { entry_mark[i].type = MZ_MOVE; } } return err; } static ssize_t zip_index_next(mz_uint64 *local_header_ofs_array, ssize_t cur_index) { ssize_t new_index = 0, i; for (i = cur_index - 1; i >= 0; --i) { if (local_header_ofs_array[cur_index] > local_header_ofs_array[i]) { new_index = i + 1; return new_index; } } return new_index; } static ssize_t zip_sort(mz_uint64 *local_header_ofs_array, ssize_t cur_index) { ssize_t nxt_index = zip_index_next(local_header_ofs_array, cur_index); if (nxt_index != cur_index) { mz_uint64 temp = local_header_ofs_array[cur_index]; ssize_t i; for (i = cur_index; i > nxt_index; i--) { local_header_ofs_array[i] = local_header_ofs_array[i - 1]; } local_header_ofs_array[nxt_index] = temp; } return nxt_index; } static int zip_index_update(struct zip_entry_mark_t *entry_mark, ssize_t last_index, ssize_t nxt_index) { ssize_t j; for (j = 0; j < last_index; j++) { if (entry_mark[j].file_index >= nxt_index) { entry_mark[j].file_index += 1; } } entry_mark[nxt_index].file_index = last_index; return 0; } static int zip_entry_finalize(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, const ssize_t n) { ssize_t i = 0; mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); if (!local_header_ofs_array) { return ZIP_EOOMEM; } for (i = 0; i < n; ++i) { local_header_ofs_array[i] = entry_mark[i].m_local_header_ofs; ssize_t index = zip_sort(local_header_ofs_array, i); if (index != i) { zip_index_update(entry_mark, i, index); } entry_mark[i].file_index = index; } size_t *length = (size_t *)calloc(n, sizeof(size_t)); if (!length) { CLEANUP(local_header_ofs_array); return ZIP_EOOMEM; } for (i = 0; i < n - 1; i++) { length[i] = (size_t)(local_header_ofs_array[i + 1] - local_header_ofs_array[i]); } length[n - 1] = (size_t)(zip->archive.m_archive_size - local_header_ofs_array[n - 1]); for (i = 0; i < n; i++) { entry_mark[i].lf_length = length[entry_mark[i].file_index]; } CLEANUP(length); CLEANUP(local_header_ofs_array); return 0; } static ssize_t zip_entry_set(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, ssize_t n, char *const entries[], const size_t len) { ssize_t err = 0; if ((err = zip_entry_mark(zip, entry_mark, n, entries, len)) < 0) { return err; } if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) { return err; } return 0; } static ssize_t zip_entry_setbyindex(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, ssize_t n, size_t entries[], const size_t len) { ssize_t err = 0; if ((err = zip_entry_markbyindex(zip, entry_mark, n, entries, len)) < 0) { return err; } if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) { return err; } return 0; } static ssize_t zip_mem_move(void *pBuf, size_t bufSize, const mz_uint64 to, const mz_uint64 from, const size_t length) { uint8_t *dst = NULL, *src = NULL, *end = NULL; if(!pBuf) { return ZIP_EINVIDX; } end = (uint8_t *)pBuf + bufSize; if(to > bufSize) { return ZIP_EINVIDX; } if(from > bufSize) { return ZIP_EINVIDX; } dst = (uint8_t *)pBuf + to; src = (uint8_t *)pBuf + from; if(((dst + length) > end) || ((src + length) > end)) { return ZIP_EINVIDX; } memmove(dst, src, length); return length; } static ssize_t zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to, const mz_uint64 from, const size_t length, mz_uint8 *move_buf, const size_t capacity_size) { if (length > capacity_size) { return ZIP_ECAPSIZE; } if (MZ_FSEEK64(m_pFile, from, SEEK_SET)) { return ZIP_EFSEEK; } if (fread(move_buf, 1, length, m_pFile) != length) { return ZIP_EFREAD; } if (MZ_FSEEK64(m_pFile, to, SEEK_SET)) { return ZIP_EFSEEK; } if (fwrite(move_buf, 1, length, m_pFile) != length) { return ZIP_EFWRITE; } return (ssize_t)length; } static ssize_t zip_files_move(struct zip_t *zip, mz_uint64 writen_num, mz_uint64 read_num, size_t length) { ssize_t n = 0; const size_t page_size = 1 << 12; // 4K mz_zip_internal_state *pState = zip->archive.m_pState; mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size); if (!move_buf) { return ZIP_EOOMEM; } ssize_t moved_length = 0; ssize_t move_count = 0; while ((mz_int64)length > 0) { move_count = (length >= page_size) ? page_size : length; if(pState->m_pFile) { n = zip_file_move(pState->m_pFile, writen_num, read_num, move_count, move_buf, page_size); } else if(pState->m_pMem) { n = zip_mem_move(pState->m_pMem, pState->m_mem_size, writen_num, read_num, move_count); } else { return ZIP_ENOFILE; } if (n < 0) { moved_length = n; goto cleanup; } if (n != move_count) { goto cleanup; } writen_num += move_count; read_num += move_count; length -= move_count; moved_length += move_count; } cleanup: CLEANUP(move_buf); return moved_length; } static int zip_central_dir_move(mz_zip_internal_state *pState, int begin, int end, int entry_num) { if (begin == entry_num) { return 0; } size_t l_size = 0; size_t r_size = 0; mz_uint32 d_size = 0; mz_uint8 *next = NULL; mz_uint8 *deleted = &MZ_ZIP_ARRAY_ELEMENT( &pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin)); l_size = (size_t)(deleted - (mz_uint8 *)(pState->m_central_dir.m_p)); if (end == entry_num) { r_size = 0; } else { next = &MZ_ZIP_ARRAY_ELEMENT( &pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, end)); r_size = pState->m_central_dir.m_size - (mz_uint32)(next - (mz_uint8 *)(pState->m_central_dir.m_p)); d_size = (mz_uint32)(next - deleted); } if (next && l_size == 0) { memmove(pState->m_central_dir.m_p, next, r_size); pState->m_central_dir.m_p = MZ_REALLOC(pState->m_central_dir.m_p, r_size); { int i; for (i = end; i < entry_num; i++) { MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -= d_size; } } } if (next && l_size * r_size != 0) { memmove(deleted, next, r_size); { int i; for (i = end; i < entry_num; i++) { MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -= d_size; } } } pState->m_central_dir.m_size = l_size + r_size; return 0; } static int zip_central_dir_delete(mz_zip_internal_state *pState, int *deleted_entry_index_array, int entry_num) { int i = 0; int begin = 0; int end = 0; int d_num = 0; while (i < entry_num) { while ((i < entry_num) && (!deleted_entry_index_array[i])) { i++; } begin = i; while ((i < entry_num) && (deleted_entry_index_array[i])) { i++; } end = i; zip_central_dir_move(pState, begin, end, entry_num); } i = 0; while (i < entry_num) { while ((i < entry_num) && (!deleted_entry_index_array[i])) { i++; } begin = i; if (begin == entry_num) { break; } while ((i < entry_num) && (deleted_entry_index_array[i])) { i++; } end = i; int k = 0, j; for (j = end; j < entry_num; j++) { MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin + k) = (mz_uint32)MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, j); k++; } d_num += end - begin; } pState->m_central_dir_offsets.m_size = sizeof(mz_uint32) * (entry_num - d_num); return 0; } static ssize_t zip_entries_delete_mark(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, int entry_num) { mz_uint64 writen_num = 0; mz_uint64 read_num = 0; size_t deleted_length = 0; size_t move_length = 0; int i = 0; size_t deleted_entry_num = 0; ssize_t n = 0; mz_bool *deleted_entry_flag_array = (mz_bool *)calloc(entry_num, sizeof(mz_bool)); if (deleted_entry_flag_array == NULL) { return ZIP_EOOMEM; } mz_zip_internal_state *pState = zip->archive.m_pState; zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING; if(pState->m_pFile) { if (MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) { CLEANUP(deleted_entry_flag_array); return ZIP_ENOENT; } } while (i < entry_num) { while ((i < entry_num) && (entry_mark[i].type == MZ_KEEP)) { writen_num += entry_mark[i].lf_length; read_num = writen_num; i++; } while ((i < entry_num) && (entry_mark[i].type == MZ_DELETE)) { deleted_entry_flag_array[i] = MZ_TRUE; read_num += entry_mark[i].lf_length; deleted_length += entry_mark[i].lf_length; i++; deleted_entry_num++; } while ((i < entry_num) && (entry_mark[i].type == MZ_MOVE)) { move_length += entry_mark[i].lf_length; mz_uint8 *p = &MZ_ZIP_ARRAY_ELEMENT( &pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i)); if (!p) { CLEANUP(deleted_entry_flag_array); return ZIP_ENOENT; } mz_uint32 offset = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); offset -= (mz_uint32)deleted_length; MZ_WRITE_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS, offset); i++; } n = zip_files_move(zip, writen_num, read_num, move_length); if (n != (ssize_t)move_length) { CLEANUP(deleted_entry_flag_array); return n; } writen_num += move_length; read_num += move_length; } zip->archive.m_archive_size -= (mz_uint64)deleted_length; zip->archive.m_total_files = (mz_uint32)entry_num - (mz_uint32)deleted_entry_num; zip_central_dir_delete(pState, deleted_entry_flag_array, entry_num); CLEANUP(deleted_entry_flag_array); return (ssize_t)deleted_entry_num; } struct zip_t *zip_open(const char *zipname, int level, char mode) { int errnum = 0; return zip_openwitherror(zipname, level, mode, &errnum); } struct zip_t *zip_openwitherror(const char *zipname, int level, char mode, int *errnum) { struct zip_t *zip = NULL; *errnum = 0; if (!zipname || strlen(zipname) < 1) { // zip_t archive name is empty or NULL *errnum = ZIP_EINVZIPNAME; goto cleanup; } if (level < 0) level = MZ_DEFAULT_LEVEL; if ((level & 0xF) > MZ_UBER_COMPRESSION) { // Wrong compression level *errnum = ZIP_EINVLVL; goto cleanup; } zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); if (!zip) { // out of memory *errnum = ZIP_EOOMEM; goto cleanup; } zip->level = (mz_uint)level; switch (mode) { case 'w': // Create a new archive. if (!mz_zip_writer_init_file_v2(&(zip->archive), zipname, 0, MZ_ZIP_FLAG_WRITE_ZIP64)) { // Cannot initialize zip_archive writer *errnum = ZIP_EWINIT; goto cleanup; } break; case 'r': if (!mz_zip_reader_init_file_v2( &(zip->archive), zipname, zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { // An archive file does not exist or cannot initialize // zip_archive reader *errnum = ZIP_ERINIT; goto cleanup; } break; case 'a': case 'd': if (!mz_zip_reader_init_file_v2_rpb( &(zip->archive), zipname, zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { // An archive file does not exist or cannot initialize // zip_archive reader *errnum = ZIP_ERINIT; goto cleanup; } if ((mode == 'a' || mode == 'd')) { if (!mz_zip_writer_init_from_reader_v2_noreopen(&(zip->archive), zipname, 0)) { *errnum = ZIP_EWRINIT; mz_zip_reader_end(&(zip->archive)); goto cleanup; } } break; default: *errnum = ZIP_EINVMODE; goto cleanup; } return zip; cleanup: CLEANUP(zip); return NULL; } void zip_close(struct zip_t *zip) { if (zip) { mz_zip_archive *pZip = &(zip->archive); // Always finalize, even if adding failed for some reason, so we have a // valid central directory. if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) { mz_zip_writer_finalize_archive(pZip); } if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING || pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { zip_archive_truncate(pZip); mz_zip_writer_end(pZip); } if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) { mz_zip_reader_end(pZip); } CLEANUP(zip); } } int zip_is64(struct zip_t *zip) { if (!zip || !zip->archive.m_pState) { // zip_t handler or zip state is not initialized return ZIP_ENOINIT; } return (int)zip->archive.m_pState->m_zip64; } static int _zip_entry_open(struct zip_t *zip, const char *entryname, int case_sensitive) { size_t entrylen = 0; mz_zip_archive *pzip = NULL; mz_uint num_alignment_padding_bytes, level; mz_zip_archive_file_stat stats; int err = 0; mz_uint16 dos_time = 0, dos_date = 0; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint64 local_dir_header_ofs = 0; if (!zip) { return ZIP_ENOINIT; } local_dir_header_ofs = zip->archive.m_archive_size; if (!entryname) { return ZIP_EINVENTNAME; } entrylen = strlen(entryname); if (entrylen == 0) { return ZIP_EINVENTNAME; } /* .ZIP File Format Specification Version: 6.3.3 4.4.17.1 The name of the file, with optional relative path. The path stored MUST not contain a drive or device letter, or a leading slash. All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' for compatibility with Amiga and UNIX file systems etc. If input came from standard input, there is no file name field. */ if (zip->entry.name) { CLEANUP(zip->entry.name); } #ifdef ZIP_RAW_ENTRYNAME zip->entry.name = STRCLONE(entryname); #else zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/'); #endif if (!zip->entry.name) { // Cannot parse zip entry name return ZIP_EINVENTNAME; } pzip = &(zip->archive); if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { zip->entry.index = (ssize_t)mz_zip_reader_locate_file( pzip, zip->entry.name, NULL, case_sensitive ? MZ_ZIP_FLAG_CASE_SENSITIVE : 0); if (zip->entry.index < (ssize_t)0) { err = ZIP_ENOENT; goto cleanup; } if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) { err = ZIP_ENOENT; goto cleanup; } zip->entry.comp_size = stats.m_comp_size; zip->entry.uncomp_size = stats.m_uncomp_size; zip->entry.uncomp_crc32 = stats.m_crc32; zip->entry.dir_offset = stats.m_central_dir_ofs; zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; zip->entry.external_attr = stats.m_external_attr; #ifndef MINIZ_NO_TIME zip->entry.m_time = stats.m_time; #endif return 0; } level = zip->level & 0xF; zip->entry.index = (ssize_t)zip->archive.m_total_files; zip->entry.comp_size = 0; zip->entry.uncomp_size = 0; zip->entry.uncomp_crc32 = MZ_CRC32_INIT; zip->entry.dir_offset = zip->archive.m_archive_size; zip->entry.header_offset = zip->archive.m_archive_size; memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); zip->entry.method = level ? MZ_DEFLATED : 0; // UNIX or APPLE #if MZ_PLATFORM == 3 || MZ_PLATFORM == 19 // regular file with rw-r--r-- permissions zip->entry.external_attr = (mz_uint32)(0100644) << 16; #else zip->entry.external_attr = 0; #endif num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { // Invalid zip mode err = ZIP_EINVMODE; goto cleanup; } if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { // Invalid zip compression level err = ZIP_EINVLVL; goto cleanup; } if (!mz_zip_writer_write_zeros(pzip, zip->entry.dir_offset, num_alignment_padding_bytes)) { // Cannot memset zip entry header err = ZIP_EMEMSET; goto cleanup; } local_dir_header_ofs += num_alignment_padding_bytes; zip->entry.m_time = time(NULL); #ifndef MINIZ_NO_TIME mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); #endif // ZIP64 header with NULL sizes (sizes will be in the data descriptor, just // after file data) extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, NULL, NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); if (!mz_zip_writer_create_local_dir_header( pzip, zip->entry.header, entrylen, (mz_uint16)extra_size, 0, 0, 0, zip->entry.method, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 | MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR, dos_time, dos_date)) { // Cannot create zip entry header err = ZIP_EMEMSET; goto cleanup; } zip->entry.header_offset = zip->entry.dir_offset + num_alignment_padding_bytes; if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, zip->entry.header, sizeof(zip->entry.header)) != sizeof(zip->entry.header)) { // Cannot write zip entry header err = ZIP_EMEMSET; goto cleanup; } if (pzip->m_file_offset_alignment) { MZ_ASSERT( (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0); } zip->entry.dir_offset += num_alignment_padding_bytes + sizeof(zip->entry.header); if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, zip->entry.name, entrylen) != entrylen) { // Cannot write data to zip entry err = ZIP_EWRTENT; goto cleanup; } zip->entry.dir_offset += entrylen; if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, extra_data, extra_size) != extra_size) { // Cannot write ZIP64 data to zip entry err = ZIP_EWRTENT; goto cleanup; } zip->entry.dir_offset += extra_size; if (level) { zip->entry.state.m_pZip = pzip; zip->entry.state.m_cur_archive_file_ofs = zip->entry.dir_offset; zip->entry.state.m_comp_size = 0; if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, &(zip->entry.state), (int)tdefl_create_comp_flags_from_zip_params( (int)level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { // Cannot initialize the zip compressor err = ZIP_ETDEFLINIT; goto cleanup; } } return 0; cleanup: CLEANUP(zip->entry.name); return err; } int zip_entry_open(struct zip_t *zip, const char *entryname) { return _zip_entry_open(zip, entryname, 0); } int zip_entry_opencasesensitive(struct zip_t *zip, const char *entryname) { return _zip_entry_open(zip, entryname, 1); } int zip_entry_openbyindex(struct zip_t *zip, size_t index) { mz_zip_archive *pZip = NULL; mz_zip_archive_file_stat stats; mz_uint namelen; const mz_uint8 *pHeader; const char *pFilename; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } pZip = &(zip->archive); if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) { // open by index requires readonly mode return ZIP_EINVMODE; } if (index >= (size_t)pZip->m_total_files) { // index out of range return ZIP_EINVIDX; } if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, index)))) { // cannot find header in central directory return ZIP_ENOHDR; } namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; /* .ZIP File Format Specification Version: 6.3.3 4.4.17.1 The name of the file, with optional relative path. The path stored MUST not contain a drive or device letter, or a leading slash. All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' for compatibility with Amiga and UNIX file systems etc. If input came from standard input, there is no file name field. */ if (zip->entry.name) { CLEANUP(zip->entry.name); } #ifdef ZIP_RAW_ENTRYNAME zip->entry.name = STRCLONE(pFilename); #else zip->entry.name = zip_strrpl(pFilename, namelen, '\\', '/'); #endif if (!zip->entry.name) { // local entry name is NULL return ZIP_EINVENTNAME; } if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) { return ZIP_ENOENT; } zip->entry.index = (ssize_t)index; zip->entry.comp_size = stats.m_comp_size; zip->entry.uncomp_size = stats.m_uncomp_size; zip->entry.uncomp_crc32 = stats.m_crc32; zip->entry.dir_offset = stats.m_central_dir_ofs; zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; zip->entry.external_attr = stats.m_external_attr; #ifndef MINIZ_NO_TIME zip->entry.m_time = stats.m_time; #endif return 0; } int zip_entry_close(struct zip_t *zip) { mz_zip_archive *pzip = NULL; mz_uint level; tdefl_status done; mz_uint16 entrylen; mz_uint16 dos_time = 0, dos_date = 0; int err = 0; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; if (!zip) { // zip_t handler is not initialized err = ZIP_ENOINIT; goto cleanup; } pzip = &(zip->archive); if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { goto cleanup; } level = zip->level & 0xF; if (level) { done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { // Cannot flush compressed buffer err = ZIP_ETDEFLBUF; goto cleanup; } zip->entry.comp_size = zip->entry.state.m_comp_size; zip->entry.dir_offset = zip->entry.state.m_cur_archive_file_ofs; zip->entry.method = MZ_DEFLATED; } entrylen = (mz_uint16)strlen(zip->entry.name); #ifndef MINIZ_NO_TIME mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); #endif MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, zip->entry.uncomp_crc32); MZ_WRITE_LE64(local_dir_footer + 8, zip->entry.comp_size); MZ_WRITE_LE64(local_dir_footer + 16, zip->entry.uncomp_size); if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) { // Cannot write zip entry header err = ZIP_EWRTHDR; goto cleanup; } zip->entry.dir_offset += local_dir_footer_size; pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (zip->entry.uncomp_size >= MZ_UINT32_MAX) ? &zip->entry.uncomp_size : NULL, (zip->entry.comp_size >= MZ_UINT32_MAX) ? &zip->entry.comp_size : NULL, (zip->entry.header_offset >= MZ_UINT32_MAX) ? &zip->entry.header_offset : NULL); if ((entrylen) && (zip->entry.name[entrylen - 1] == '/') && !zip->entry.uncomp_size) { /* Set DOS Subdirectory attribute bit. */ zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; } if (!mz_zip_writer_add_to_central_dir( pzip, zip->entry.name, entrylen, pExtra_data, (mz_uint16)extra_size, "", 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 | MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR, dos_time, dos_date, zip->entry.header_offset, zip->entry.external_attr, NULL, 0)) { // Cannot write to zip central dir err = ZIP_EWRTDIR; goto cleanup; } pzip->m_total_files++; pzip->m_archive_size = zip->entry.dir_offset; cleanup: if (zip) { zip->entry.m_time = 0; CLEANUP(zip->entry.name); } return err; } const char *zip_entry_name(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized return NULL; } return zip->entry.name; } ssize_t zip_entry_index(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized return (ssize_t)ZIP_ENOINIT; } return zip->entry.index; } int zip_entry_isdir(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } if (zip->entry.index < (ssize_t)0) { // zip entry is not opened return ZIP_EINVIDX; } return (int)mz_zip_reader_is_file_a_directory(&zip->archive, (mz_uint)zip->entry.index); } unsigned long long zip_entry_size(struct zip_t *zip) { return zip_entry_uncomp_size(zip); } unsigned long long zip_entry_uncomp_size(struct zip_t *zip) { return zip ? zip->entry.uncomp_size : 0; } unsigned long long zip_entry_comp_size(struct zip_t *zip) { return zip ? zip->entry.comp_size : 0; } unsigned int zip_entry_crc32(struct zip_t *zip) { return zip ? zip->entry.uncomp_crc32 : 0; } unsigned long long zip_entry_dir_offset(struct zip_t *zip) { return zip ? zip->entry.dir_offset : 0; } unsigned long long zip_entry_header_offset(struct zip_t *zip) { return zip ? zip->entry.header_offset : 0; } int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { mz_uint level; mz_zip_archive *pzip = NULL; tdefl_status status; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } pzip = &(zip->archive); if (buf && bufsize > 0) { zip->entry.uncomp_size += bufsize; zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32( zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize); level = zip->level & 0xF; if (!level) { if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, buf, bufsize) != bufsize)) { // Cannot write buffer return ZIP_EWRTENT; } zip->entry.dir_offset += bufsize; zip->entry.comp_size += bufsize; } else { status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, TDEFL_NO_FLUSH); if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) { // Cannot compress buffer return ZIP_ETDEFLBUF; } } } return 0; } int zip_entry_fwrite(struct zip_t *zip, const char *filename) { int err = 0; size_t n = 0; MZ_FILE *stream = NULL; mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE]; struct MZ_FILE_STAT_STRUCT file_stat; mz_uint16 modes; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE); memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT)); if (MZ_FILE_STAT(filename, &file_stat) != 0) { // problem getting information - check errno return ZIP_ENOENT; } #if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP) (void)modes; // unused #else /* Initialize with permission bits--which are not implementation-optional */ modes = file_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); if (S_ISDIR(file_stat.st_mode)) modes |= UNX_IFDIR; if (S_ISREG(file_stat.st_mode)) modes |= UNX_IFREG; if (S_ISLNK(file_stat.st_mode)) modes |= UNX_IFLNK; if (S_ISBLK(file_stat.st_mode)) modes |= UNX_IFBLK; if (S_ISCHR(file_stat.st_mode)) modes |= UNX_IFCHR; if (S_ISFIFO(file_stat.st_mode)) modes |= UNX_IFIFO; if (S_ISSOCK(file_stat.st_mode)) modes |= UNX_IFSOCK; zip->entry.external_attr = (modes << 16) | !(file_stat.st_mode & S_IWUSR); if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; } #endif zip->entry.m_time = file_stat.st_mtime; if (!(stream = MZ_FOPEN(filename, "rb"))) { // Cannot open filename return ZIP_EOPNFILE; } while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > 0) { if (zip_entry_write(zip, buf, n) < 0) { err = ZIP_EWRTENT; break; } } fclose(stream); return err; } ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { mz_zip_archive *pzip = NULL; mz_uint idx; size_t size = 0; if (!zip) { // zip_t handler is not initialized return (ssize_t)ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < (ssize_t)0) { // the entry is not found or we do not have read access return (ssize_t)ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; if (mz_zip_reader_is_file_a_directory(pzip, idx)) { // the entry is a directory return (ssize_t)ZIP_EINVENTTYPE; } *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0); if (*buf && bufsize) { *bufsize = size; } return (ssize_t)size; } ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { mz_zip_archive *pzip = NULL; if (!zip) { // zip_t handler is not initialized return (ssize_t)ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < (ssize_t)0) { // the entry is not found or we do not have read access return (ssize_t)ZIP_ENOENT; } if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index, buf, bufsize, 0, NULL, 0)) { return (ssize_t)ZIP_EMEMNOALLOC; } return (ssize_t)zip->entry.uncomp_size; } int zip_entry_fread(struct zip_t *zip, const char *filename) { mz_zip_archive *pzip = NULL; mz_uint idx; mz_uint32 xattr = 0; mz_zip_archive_file_stat info; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < (ssize_t)0) { // the entry is not found or we do not have read access return ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; if (mz_zip_reader_is_file_a_directory(pzip, idx)) { // the entry is a directory return ZIP_EINVENTTYPE; } if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) { return ZIP_ENOFILE; } #if defined(_MSC_VER) || defined(PS4) (void)xattr; // unused #else if (!mz_zip_reader_file_stat(pzip, idx, &info)) { // Cannot get information about zip archive; return ZIP_ENOFILE; } xattr = (info.m_external_attr >> 16) & 0xFFFF; if (xattr > 0 && xattr <= MZ_UINT16_MAX) { if (CHMOD(filename, (mode_t)xattr) < 0) { return ZIP_ENOPERM; } } #endif return 0; } int zip_entry_extract(struct zip_t *zip, size_t (*on_extract)(void *arg, uint64_t offset, const void *buf, size_t bufsize), void *arg) { mz_zip_archive *pzip = NULL; mz_uint idx; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < (ssize_t)0) { // the entry is not found or we do not have read access return ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) ? 0 : ZIP_EINVIDX; } ssize_t zip_entries_total(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } return (ssize_t)zip->archive.m_total_files; } ssize_t zip_entries_delete(struct zip_t *zip, char *const entries[], size_t len) { ssize_t n = 0; ssize_t err = 0; struct zip_entry_mark_t *entry_mark = NULL; if (zip == NULL || (entries == NULL && len != 0)) { return ZIP_ENOINIT; } if (entries == NULL && len == 0) { return 0; } n = zip_entries_total(zip); entry_mark = (struct zip_entry_mark_t *)calloc( (size_t)n, sizeof(struct zip_entry_mark_t)); if (!entry_mark) { return ZIP_EOOMEM; } zip->archive.m_zip_mode = MZ_ZIP_MODE_READING; err = zip_entry_set(zip, entry_mark, n, entries, len); if (err < 0) { CLEANUP(entry_mark); return err; } err = zip_entries_delete_mark(zip, entry_mark, (int)n); CLEANUP(entry_mark); return err; } ssize_t zip_entries_deletebyindex(struct zip_t *zip, size_t entries[], size_t len) { ssize_t n = 0; ssize_t err = 0; struct zip_entry_mark_t *entry_mark = NULL; if (zip == NULL || (entries == NULL && len != 0)) { return ZIP_ENOINIT; } if (entries == NULL && len == 0) { return 0; } n = zip_entries_total(zip); entry_mark = (struct zip_entry_mark_t *)calloc( (size_t)n, sizeof(struct zip_entry_mark_t)); if (!entry_mark) { return ZIP_EOOMEM; } zip->archive.m_zip_mode = MZ_ZIP_MODE_READING; err = zip_entry_setbyindex(zip, entry_mark, n, entries, len); if (err < 0) { CLEANUP(entry_mark); return err; } err = zip_entries_delete_mark(zip, entry_mark, (int)n); CLEANUP(entry_mark); return err; } int zip_stream_extract(const char *stream, size_t size, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { mz_zip_archive zip_archive; if (!stream || !dir) { // Cannot parse zip archive stream return ZIP_ENOINIT; } if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { // Cannot memset zip archive return ZIP_EMEMSET; } if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) { // Cannot initialize zip_archive reader return ZIP_ENOINIT; } return zip_archive_extract(&zip_archive, dir, on_extract, arg); } struct zip_t *zip_stream_open(const char *stream, size_t size, int level, char mode) { int errnum = 0; return zip_stream_openwitherror(stream, size, level, mode, &errnum); } struct zip_t *zip_stream_openwitherror(const char *stream, size_t size, int level, char mode, int *errnum) { struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); if (!zip) { // out of memory *errnum = ZIP_EOOMEM; return NULL; } if (level < 0) { level = MZ_DEFAULT_LEVEL; } if ((level & 0xF) > MZ_UBER_COMPRESSION) { // Wrong compression level *errnum = ZIP_EINVLVL; goto cleanup; } zip->level = (mz_uint)level; if ((stream != NULL) && (size > 0) && (mode == 'r')) { if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) { *errnum = ZIP_ERINIT; goto cleanup; } } else if ((stream == NULL) && (size == 0) && (mode == 'w')) { // Create a new archive. if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) { // Cannot initialize zip_archive writer *errnum = ZIP_EWINIT; goto cleanup; } } else { *errnum = ZIP_EINVMODE; goto cleanup; } *errnum = 0; return zip; cleanup: CLEANUP(zip); return NULL; } ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize) { size_t n; if (!zip) { return (ssize_t)ZIP_ENOINIT; } zip_archive_finalize(&(zip->archive)); n = (size_t)zip->archive.m_archive_size; if (bufsize != NULL) { *bufsize = n; } *buf = calloc(sizeof(unsigned char), n); memcpy(*buf, zip->archive.m_pState->m_pMem, n); return (ssize_t)n; } void zip_stream_close(struct zip_t *zip) { if (zip) { mz_zip_writer_end(&(zip->archive)); mz_zip_reader_end(&(zip->archive)); CLEANUP(zip); } } int zip_create(const char *zipname, const char *filenames[], size_t len) { int err = 0; size_t i; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; mz_uint32 ext_attributes = 0; mz_uint16 modes; if (!zipname || strlen(zipname) < 1) { // zip_t archive name is empty or NULL return ZIP_EINVZIPNAME; } // Create a new archive. if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { // Cannot memset zip archive return ZIP_EMEMSET; } if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) { // Cannot initialize zip_archive writer return ZIP_ENOINIT; } if (!memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT))) { return ZIP_EMEMSET; } for (i = 0; i < len; ++i) { const char *name = filenames[i]; if (!name) { err = ZIP_EINVENTNAME; break; } if (MZ_FILE_STAT(name, &file_stat) != 0) { // problem getting information - check errno err = ZIP_ENOFILE; break; } #if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP) (void)modes; // unused #else /* Initialize with permission bits--which are not implementation-optional */ modes = file_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); if (S_ISDIR(file_stat.st_mode)) modes |= UNX_IFDIR; if (S_ISREG(file_stat.st_mode)) modes |= UNX_IFREG; if (S_ISLNK(file_stat.st_mode)) modes |= UNX_IFLNK; if (S_ISBLK(file_stat.st_mode)) modes |= UNX_IFBLK; if (S_ISCHR(file_stat.st_mode)) modes |= UNX_IFCHR; if (S_ISFIFO(file_stat.st_mode)) modes |= UNX_IFIFO; if (S_ISSOCK(file_stat.st_mode)) modes |= UNX_IFSOCK; ext_attributes = (modes << 16) | !(file_stat.st_mode & S_IWUSR); if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; } #endif if (!mz_zip_writer_add_file(&zip_archive, zip_basename(name), name, "", 0, ZIP_DEFAULT_COMPRESSION_LEVEL, ext_attributes)) { // Cannot add file to zip_archive err = ZIP_ENOFILE; break; } } mz_zip_writer_finalize_archive(&zip_archive); mz_zip_writer_end(&zip_archive); return err; } int zip_extract(const char *zipname, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { mz_zip_archive zip_archive; if (!zipname || !dir) { // Cannot parse zip archive name return ZIP_EINVZIPNAME; } if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { // Cannot memset zip archive return ZIP_EMEMSET; } // Now try to open the archive. if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { // Cannot initialize zip_archive reader return ZIP_ENOINIT; } return zip_archive_extract(&zip_archive, dir, on_extract, arg); } simutrans-124.5/src/external/zip.h000066400000000000000000000423501520675460500171540ustar00rootroot00000000000000/* * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #ifndef ZIP_H #define ZIP_H #include #include #include #ifndef ZIP_SHARED #define ZIP_EXPORT #else #ifdef _WIN32 #ifdef ZIP_BUILD_SHARED #define ZIP_EXPORT __declspec(dllexport) #else #define ZIP_EXPORT __declspec(dllimport) #endif #else #define ZIP_EXPORT __attribute__((visibility("default"))) #endif #endif #ifdef __cplusplus extern "C" { #endif #if !defined(_POSIX_C_SOURCE) && defined(_MSC_VER) // 64-bit Windows is the only mainstream platform // where sizeof(long) != sizeof(void*) #ifdef _WIN64 typedef long long ssize_t; /* byte count or error */ #else typedef long ssize_t; /* byte count or error */ #endif #endif /** * @mainpage * * Documentation for @ref zip. */ /** * @addtogroup zip * @{ */ /** * Default zip compression level. */ #define ZIP_DEFAULT_COMPRESSION_LEVEL 6 /** * Error codes */ #define ZIP_ENOINIT -1 // not initialized #define ZIP_EINVENTNAME -2 // invalid entry name #define ZIP_ENOENT -3 // entry not found #define ZIP_EINVMODE -4 // invalid zip mode #define ZIP_EINVLVL -5 // invalid compression level #define ZIP_ENOSUP64 -6 // no zip 64 support #define ZIP_EMEMSET -7 // memset error #define ZIP_EWRTENT -8 // cannot write data to entry #define ZIP_ETDEFLINIT -9 // cannot initialize tdefl compressor #define ZIP_EINVIDX -10 // invalid index #define ZIP_ENOHDR -11 // header not found #define ZIP_ETDEFLBUF -12 // cannot flush tdefl buffer #define ZIP_ECRTHDR -13 // cannot create entry header #define ZIP_EWRTHDR -14 // cannot write entry header #define ZIP_EWRTDIR -15 // cannot write to central dir #define ZIP_EOPNFILE -16 // cannot open file #define ZIP_EINVENTTYPE -17 // invalid entry type #define ZIP_EMEMNOALLOC -18 // extracting data using no memory allocation #define ZIP_ENOFILE -19 // file not found #define ZIP_ENOPERM -20 // no permission #define ZIP_EOOMEM -21 // out of memory #define ZIP_EINVZIPNAME -22 // invalid zip archive name #define ZIP_EMKDIR -23 // make dir error #define ZIP_ESYMLINK -24 // symlink error #define ZIP_ECLSZIP -25 // close archive error #define ZIP_ECAPSIZE -26 // capacity size too small #define ZIP_EFSEEK -27 // fseek error #define ZIP_EFREAD -28 // fread error #define ZIP_EFWRITE -29 // fwrite error #define ZIP_ERINIT -30 // cannot initialize reader #define ZIP_EWINIT -31 // cannot initialize writer #define ZIP_EWRINIT -32 // cannot initialize writer from reader /** * Looks up the error message string corresponding to an error number. * @param errnum error number * @return error message string corresponding to errnum or NULL if error is not * found. */ extern ZIP_EXPORT const char *zip_strerror(int errnum); /** * @struct zip_t * * This data structure is used throughout the library to represent zip archive - * forward declaration. */ struct zip_t; /** * Opens zip archive with compression level using the given mode. * * @param zipname zip archive file name. * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t *zip_open(const char *zipname, int level, char mode); /** * Opens zip archive with compression level using the given mode. * The function additionally returns @param errnum - * * @param zipname zip archive file name. * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * @param errnum 0 on success, negative number (< 0) on error. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t * zip_openwitherror(const char *zipname, int level, char mode, int *errnum); /** * Closes the zip archive, releases resources - always finalize. * * @param zip zip archive handler. */ extern ZIP_EXPORT void zip_close(struct zip_t *zip); /** * Determines if the archive has a zip64 end of central directory headers. * * @param zip zip archive handler. * * @return the return code - 1 (true), 0 (false), negative number (< 0) on * error. */ extern ZIP_EXPORT int zip_is64(struct zip_t *zip); /** * Opens an entry by name in the zip archive. * * For zip archive opened in 'w' or 'a' mode the function will append * a new entry. In readonly mode the function tries to locate the entry * in global dictionary. * * @param zip zip archive handler. * @param entryname an entry name in local dictionary. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_open(struct zip_t *zip, const char *entryname); /** * Opens an entry by name in the zip archive. * * For zip archive opened in 'w' or 'a' mode the function will append * a new entry. In readonly mode the function tries to locate the entry * in global dictionary (case sensitive). * * @param zip zip archive handler. * @param entryname an entry name in local dictionary (case sensitive). * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_opencasesensitive(struct zip_t *zip, const char *entryname); /** * Opens a new entry by index in the zip archive. * * This function is only valid if zip archive was opened in 'r' (readonly) mode. * * @param zip zip archive handler. * @param index index in local dictionary. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_openbyindex(struct zip_t *zip, size_t index); /** * Closes a zip entry, flushes buffer and releases resources. * * @param zip zip archive handler. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_close(struct zip_t *zip); /** * Returns a local name of the current zip entry. * * The main difference between user's entry name and local entry name * is optional relative path. * Following .ZIP File Format Specification - the path stored MUST not contain * a drive or device letter, or a leading slash. * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' * for compatibility with Amiga and UNIX file systems etc. * * @param zip: zip archive handler. * * @return the pointer to the current zip entry name, or NULL on error. */ extern ZIP_EXPORT const char *zip_entry_name(struct zip_t *zip); /** * Returns an index of the current zip entry. * * @param zip zip archive handler. * * @return the index on success, negative number (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entry_index(struct zip_t *zip); /** * Determines if the current zip entry is a directory entry. * * @param zip zip archive handler. * * @return the return code - 1 (true), 0 (false), negative number (< 0) on * error. */ extern ZIP_EXPORT int zip_entry_isdir(struct zip_t *zip); /** * Returns the uncompressed size of the current zip entry. * Alias for zip_entry_uncomp_size (for backward compatibility). * * @param zip zip archive handler. * * @return the uncompressed size in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_size(struct zip_t *zip); /** * Returns the uncompressed size of the current zip entry. * * @param zip zip archive handler. * * @return the uncompressed size in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_uncomp_size(struct zip_t *zip); /** * Returns the compressed size of the current zip entry. * * @param zip zip archive handler. * * @return the compressed size in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_comp_size(struct zip_t *zip); /** * Returns CRC-32 checksum of the current zip entry. * * @param zip zip archive handler. * * @return the CRC-32 checksum. */ extern ZIP_EXPORT unsigned int zip_entry_crc32(struct zip_t *zip); /** * Returns byte offset of the current zip entry * in the archive's central directory. * * @param zip zip archive handler. * * @return the offset in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_dir_offset(struct zip_t *zip); /** * Returns the current zip entry's local header file offset in bytes. * * @param zip zip archive handler. * * @return the entry's local header file offset in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_header_offset(struct zip_t *zip); /** * Compresses an input buffer for the current zip entry. * * @param zip zip archive handler. * @param buf input buffer. * @param bufsize input buffer size (in bytes). * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize); /** * Compresses a file for the current zip entry. * * @param zip zip archive handler. * @param filename input file. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_fwrite(struct zip_t *zip, const char *filename); /** * Extracts the current zip entry into output buffer. * * The function allocates sufficient memory for a output buffer. * * @param zip zip archive handler. * @param buf output buffer. * @param bufsize output buffer size (in bytes). * * @note remember to release memory allocated for a output buffer. * for large entries, please take a look at zip_entry_extract function. * * @return the return code - the number of bytes actually read on success. * Otherwise a negative number (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); /** * Extracts the current zip entry into a memory buffer using no memory * allocation. * * @param zip zip archive handler. * @param buf preallocated output buffer. * @param bufsize output buffer size (in bytes). * * @note ensure supplied output buffer is large enough. * zip_entry_size function (returns uncompressed size for the current * entry) can be handy to estimate how big buffer is needed. * For large entries, please take a look at zip_entry_extract function. * * @return the return code - the number of bytes actually read on success. * Otherwise a negative number (< 0) on error (e.g. bufsize is not large * enough). */ extern ZIP_EXPORT ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize); /** * Extracts the current zip entry into output file. * * @param zip zip archive handler. * @param filename output file. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_fread(struct zip_t *zip, const char *filename); /** * Extracts the current zip entry using a callback function (on_extract). * * @param zip zip archive handler. * @param on_extract callback function. * @param arg opaque pointer (optional argument, which you can pass to the * on_extract callback) * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_extract(struct zip_t *zip, size_t (*on_extract)(void *arg, uint64_t offset, const void *data, size_t size), void *arg); /** * Returns the number of all entries (files and directories) in the zip archive. * * @param zip zip archive handler. * * @return the return code - the number of entries on success, negative number * (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entries_total(struct zip_t *zip); /** * Deletes zip archive entries. * * @param zip zip archive handler. * @param entries array of zip archive entries to be deleted. * @param len the number of entries to be deleted. * @return the number of deleted entries, or negative number (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entries_delete(struct zip_t *zip, char *const entries[], size_t len); /** * Deletes zip archive entries. * * @param zip zip archive handler. * @param entries array of zip archive entries indices to be deleted. * @param len the number of entries to be deleted. * @return the number of deleted entries, or negative number (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entries_deletebyindex(struct zip_t *zip, size_t entries[], size_t len); /** * Extracts a zip archive stream into directory. * * If on_extract is not NULL, the callback will be called after * successfully extracted each zip entry. * Returning a negative value from the callback will cause abort and return an * error. The last argument (void *arg) is optional, which you can use to pass * data to the on_extract callback. * * @param stream zip archive stream. * @param size stream size. * @param dir output directory. * @param on_extract on extract callback. * @param arg opaque pointer. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_stream_extract(const char *stream, size_t size, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg); /** * Opens zip archive stream into memory. * * @param stream zip archive stream. * @param size stream size. * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t *zip_stream_open(const char *stream, size_t size, int level, char mode); /** * Opens zip archive stream into memory. * The function additionally returns @param errnum - * * @param stream zip archive stream. * @param size stream size.* * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * @param errnum 0 on success, negative number (< 0) on error. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t *zip_stream_openwitherror(const char *stream, size_t size, int level, char mode, int *errnum); /** * Copy zip archive stream output buffer. * * @param zip zip archive handler. * @param buf output buffer. User should free buf. * @param bufsize output buffer size (in bytes). * * @return copy size */ extern ZIP_EXPORT ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize); /** * Close zip archive releases resources. * * @param zip zip archive handler. * * @return */ extern ZIP_EXPORT void zip_stream_close(struct zip_t *zip); /** * Creates a new archive and puts files into a single zip archive. * * @param zipname zip archive file. * @param filenames input files. * @param len: number of input files. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_create(const char *zipname, const char *filenames[], size_t len); /** * Extracts a zip archive file into directory. * * If on_extract_entry is not NULL, the callback will be called after * successfully extracted each zip entry. * Returning a negative value from the callback will cause abort and return an * error. The last argument (void *arg) is optional, which you can use to pass * data to the on_extract_entry callback. * * @param zipname zip archive file. * @param dir output directory. * @param on_extract_entry on extract callback. * @param arg opaque pointer. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_extract(const char *zipname, const char *dir, int (*on_extract_entry)(const char *filename, void *arg), void *arg); /** @} */ #ifdef __cplusplus } #endif #endif simutrans-124.5/src/linux/000077500000000000000000000000001520675460500155125ustar00rootroot00000000000000simutrans-124.5/src/linux/com.simutrans.Simutrans.metainfo.xml.in000066400000000000000000000033151520675460500252320ustar00rootroot00000000000000 com.simutrans.Simutrans CC0-1.0 Artistic-1.0 Simutrans

Open source transportation simulation game

Simutrans is a freeware and open-source transportation simulator. Your goal is to establish a successful transport company. Transport passengers, mail and goods by rail, road, ship, and even air. Interconnect districts, cities, public buildings, industries and tourist attractions.

simutrans.desktop https://simutrans-germany.com/wiki/wiki/display858 https://simutrans-germany.com/wiki/wiki/display864 https://simutrans-germany.com/wiki/wiki/display837 https://simutrans-germany.com/wiki/wiki/display1687 https://www.simutrans.com/ The Simutrans Team simutrans mild moderate simutrans-124.5/src/linux/simutrans.desktop000066400000000000000000000004271520675460500211350ustar00rootroot00000000000000[Desktop Entry] Name=Simutrans GenericName=Transportation Simulation Game Comment=Transportation simulator Exec=simutrans Icon=simutrans Terminal=false Type=Application Categories=Game;Simulation; X-Purism-FormFactor=Workstation;Mobile; X-KDE-FormFactors=desktop;tablet;handset; simutrans-124.5/src/makeobj/000077500000000000000000000000001520675460500157635ustar00rootroot00000000000000simutrans-124.5/src/makeobj/CMakeLists.txt000066400000000000000000000053241520675460500205270ustar00rootroot00000000000000# # This file is part of the Simutrans project under the artistic licence. # (see licence.txt) # add_executable(makeobj makeobj.cc ) target_compile_options(makeobj PRIVATE ${SIMUTRANS_COMMON_COMPILE_OPTIONS}) target_compile_definitions(makeobj PRIVATE MAKEOBJ=1 COLOUR_DEPTH=0) target_compile_definitions(makeobj PRIVATE MSG_LEVEL=${SIMUTRANS_MSG_LEVEL}) if (SIMUTRANS_USE_SYSLOG) target_compile_definitions(simutrans PRIVATE SYSLOG=1) endif () if (NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND SIMUTRANS_BUILD_32BIT) target_compile_options(makeobj PRIVATE -m32) set_target_properties(makeobj PROPERTIES LINK_FLAGS "-m32") endif () target_link_libraries(makeobj PNG::PNG ) # These source files are unique to makeobj target_sources(makeobj PRIVATE ../simutrans/descriptor/writer/bridge_writer.cc ../simutrans/descriptor/writer/building_writer.cc ../simutrans/descriptor/writer/citycar_writer.cc ../simutrans/descriptor/writer/crossing_writer.cc ../simutrans/descriptor/writer/factory_writer.cc ../simutrans/descriptor/writer/get_climate.cc ../simutrans/descriptor/writer/get_waytype.cc ../simutrans/descriptor/writer/good_writer.cc ../simutrans/descriptor/writer/groundobj_writer.cc ../simutrans/descriptor/writer/ground_writer.cc ../simutrans/descriptor/writer/image_writer.cc ../simutrans/descriptor/writer/imagelist2d_writer.cc ../simutrans/descriptor/writer/imagelist_writer.cc ../simutrans/descriptor/writer/obj_node.cc ../simutrans/descriptor/writer/obj_writer.cc ../simutrans/descriptor/writer/pedestrian_writer.cc ../simutrans/descriptor/writer/roadsign_writer.cc ../simutrans/descriptor/writer/root_writer.cc ../simutrans/descriptor/writer/sim_writer.cc ../simutrans/descriptor/writer/skin_writer.cc ../simutrans/descriptor/writer/sound_writer.cc ../simutrans/descriptor/writer/text_writer.cc ../simutrans/descriptor/writer/tree_writer.cc ../simutrans/descriptor/writer/tunnel_writer.cc ../simutrans/descriptor/writer/vehicle_writer.cc ../simutrans/descriptor/writer/way_writer.cc ../simutrans/descriptor/writer/way_obj_writer.cc ../simutrans/descriptor/writer/xref_writer.cc ) # These source files produce the same object code in makeobj and simutrans target_sources(makeobj PRIVATE ../simutrans/descriptor/image.cc ../simutrans/dataobj/freelist.cc ../simutrans/io/raw_image.cc ../simutrans/simio.cc ../simutrans/simdebug.cc ../simutrans/simmem.cc ../simutrans/utils/simstring.cc ../simutrans/utils/searchfolder.cc ) # These source files produce different object code in makeobj and simutrans target_sources(makeobj PRIVATE ../simutrans/dataobj/tabfile.cc ../simutrans/io/classify_file.cc ../simutrans/io/raw_image_bmp.cc ../simutrans/io/raw_image_png.cc ../simutrans/io/raw_image_ppm.cc ../simutrans/utils/log.cc ) simutrans-124.5/src/makeobj/Makefile000066400000000000000000000114711520675460500174270ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # CFG ?= default -include ../../config.$(CFG) OSTYPES = beos cygwin freebsd haiku linux mingw mac ifeq ($(findstring $(OSTYPE), $(OSTYPES)),) $(error Unkown OSTYPE "$(OSTYPE)", must be one of "$(OSTYPES)") endif PNG_CONFIG ?= pkg-config libpng ifneq ($(PNG_CONFIG),) PNG_CFLAGS := $(shell $(PNG_CONFIG) --cflags) PNG_LDFLAGS := $(shell $(PNG_CONFIG) --libs) endif CFLAGS += $(PNG_CFLAGS) LDFLAGS += $(PNG_LDFLAGS) # Absolutely essential CXXFLAGS += -DMAKEOBJ STD_LIBS += -lpng ifeq ($(OSTYPE),cygwin) OS_INC ?= -I/usr/include/mingw OS_OPT ?= -mwin32 STD_LIBS += -lwinmm endif ifeq ($(OSTYPE),mingw) OS_OPT ?= -DPNG_STATIC -DZLIB_STATIC -march=pentium LDFLAGS += -static-libgcc -static-libstdc++ -static # we need the libraries EXACTLY in this order to link STD_LIBS = -lmingw32 -lpng -lz endif ifeq ($(OSTYPE),mac) CXXFLAGS += -DUSE_HW -DUSE_C endif ifeq ($(OSTYPE),freebsd) CXXFLAGS += -I/usr/local/include endif ifdef OPTIMISE ifeq ($(shell expr $(OPTIMISE) \>= 1), 1) CXXFLAGS += -O3 # clang does not support fno-schedule-insns ifeq ($(findstring clang, $(CXX)),) CXXFLAGS += -fno-schedule-insns endif endif else CXXFLAGS += -O endif ifdef DEBUG ifeq ($(shell expr $(DEBUG) \>= 1), 1) CXXFLAGS += -g -DDEBUG endif ifeq ($(shell expr $(DEBUG) \>= 2), 1) CXXFLAGS += -fno-inline endif ifeq ($(shell expr $(DEBUG) \>= 3), 1) CXXFLAGS += -O0 endif else CXXFLAGS += -DNDEBUG endif ifdef PROFILE ifeq ($(shell expr $(PROFILE) \>= 1), 1) CXXFLAGS += -pg -DPROFILE -fno-inline LDFLAGS += -pg endif endif CXXFLAGS += -DREVISION -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align $(OS_INC) $(OS_OPT) $(FLAGS) # SOLO_SOURCES contains files which are unique to makeobj; # SHARED_SOURCES contains those with the exact same object code in makeobj and simutrans; # VARIANT_SOURCES contains those which need different .o files for makeobj and simutrans. # At the moment they're all treated identically, of course. SOLO_SOURCES += makeobj.cc SOLO_SOURCES += ../simutrans/descriptor/writer/bridge_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/building_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/citycar_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/crossing_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/factory_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/get_climate.cc SOLO_SOURCES += ../simutrans/descriptor/writer/get_waytype.cc SOLO_SOURCES += ../simutrans/descriptor/writer/good_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/groundobj_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/ground_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/image_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/imagelist2d_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/imagelist_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/obj_node.cc SOLO_SOURCES += ../simutrans/descriptor/writer/obj_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/pedestrian_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/roadsign_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/root_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/sim_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/skin_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/sound_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/text_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/tree_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/tunnel_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/vehicle_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/way_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/way_obj_writer.cc SOLO_SOURCES += ../simutrans/descriptor/writer/xref_writer.cc SHARED_SOURCES += ../simutrans/descriptor/image.cc SHARED_SOURCES += ../simutrans/dataobj/freelist.cc SHARED_SOURCES += ../simutrans/io/raw_image.cc SHARED_SOURCES += ../simutrans/simio.h SHARED_SOURCES += ../simutrans/simdebug.cc SHARED_SOURCES += ../simutrans/simmem.cc SHARED_SOURCES += ../simutrans/utils/simstring.cc SHARED_SOURCES += ../simutrans/utils/searchfolder.cc VARIANT_SOURCES += ../simutrans/dataobj/tabfile.cc VARIANT_SOURCES += ../simutrans/io/classify_file.cc VARIANT_SOURCES += ../simutrans/io/raw_image_bmp.cc VARIANT_SOURCES += ../simutrans/io/raw_image_png.cc VARIANT_SOURCES += ../simutrans/io/raw_image_ppm.cc VARIANT_SOURCES += ../simutrans/utils/log.cc SOURCES ?= $(SOLO_SOURCES) $(SHARED_SOURCES) $(VARIANT_SOURCES) BUILDDIR ?= build/$(CFG) TOOL = makeobj PROG ?= makeobj ifeq ($(origin MAKEOBJ_PROGDIR), undefined) MAKEOBJ_PROGDIR := ../../$(BUILDDIR)/$(TOOL) endif BUILDDIR := ../../$(BUILDDIR) TOOL_PROGDIR = $(MAKEOBJ_PROGDIR) include ../../uncommon.mk simutrans-124.5/src/makeobj/Makeobj.sln000066400000000000000000000020651520675460500200540ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29102.190 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Makeobj", "Makeobj.vcxproj", "{9944405A-74A3-443D-83A6-F4B9DBE23DB2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9944405A-74A3-443D-83A6-F4B9DBE23DB2}.Debug|x86.ActiveCfg = Debug|Win32 {9944405A-74A3-443D-83A6-F4B9DBE23DB2}.Debug|x86.Build.0 = Debug|Win32 {9944405A-74A3-443D-83A6-F4B9DBE23DB2}.Release|x86.ActiveCfg = Release|Win32 {9944405A-74A3-443D-83A6-F4B9DBE23DB2}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {179303F5-62B6-4464-85DF-782B62356351} EndGlobalSection EndGlobal simutrans-124.5/src/makeobj/Makeobj.vcxproj000066400000000000000000000316341520675460500207570ustar00rootroot00000000000000 Debug Win32 Release Win32 {9944405A-74A3-443D-83A6-F4B9DBE23DB2} Makeobj Application $(DefaultPlatformToolset) true <_ProjectFileVersion>16.0.28916.169 $(SolutionDir)..\build\makeobj\$(Configuration)\ $(SolutionDir)..\build\makeobj\$(Configuration)\ $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) true x86-windows-static true x86-windows-static true Level3 true true 4250;4373;4800;4996 true Console MachineX86 ProgramDatabase Disabled MAKEOBJ;DEBUG=3 MultiThreadedDebug EnableFastChecks true false MaxSpeed true Speed MAKEOBJ;NDEBUG MultiThreaded false true simutrans-124.5/src/makeobj/Makeobj.vcxproj.filters000066400000000000000000000015041520675460500224170ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms simutrans-124.5/src/makeobj/RightClickCompileDat2PakSystem.reg000066400000000000000000000075161520675460500244030ustar00rootroot00000000000000Windows Registry Editor Version 5.00 ; This file will include two context menu options for compiling DAT files to PAK ; YOU MUST EDIT IT TO REFLECT THE CURRENT MAKEOBJ LOCATION AND PAK SIZE! [HKEY_CLASSES_ROOT\SystemFileAssociations\.dat] ; This sets the default action to run when opening a dat file, uncomment to make it compile the pak automatically, change to makeobjCustom to make it ask for a size. ; @="makeobj" [HKEY_CLASSES_ROOT\SystemFileAssociations\.dat\shell] ; This and the next entry add a command that will automatically compile the DAT to a PAK [HKEY_CLASSES_ROOT\SystemFileAssociations\.dat\shell\makeobj] ; Text that will show in the context menu @="Compile to pakNUMBER" ; Icon that will show in the context menu, remove if you don't want an icon ; Change DIR to the location of the icon, notice that backslashes must be double "Icon"="DIR\\makeobj.ico" [HKEY_CLASSES_ROOT\SystemFileAssociations\.dat\shell\makeobj\Command] ; Change NUMBER to the pakset size you want to compile automatically ; Change PATH to your makeobj executable ; e.g. C:\\Users\\Me\\Documents\\makeobj.exe @="cmd /v:on /c set file=%1&set file=!file:%W\\=!&PATH pakNUMBER ./ \"!file!\"" ; These two last entries add an option that will ask you to enter the pakset size to compile, useful to compile other sizes you don't work all the time [HKEY_CLASSES_ROOT\SystemFileAssociations\.dat\shell\makeobjCustom] ; Text that will show in the context menu @="Compile to pak" ; Icon that will show in the context menu, remove if you don't want an icon ; Change DIR to the location of the icon, notice that backslashes must be double "Icon"="DIR\\makeobj.ico" [HKEY_CLASSES_ROOT\SystemFileAssociations\.dat\shell\makeobjCustom\Command] ; Change PATH to your makeobj executable ; e.g. C:\\Users\\Me\\Documents\\makeobj.exe @="cmd /v:on /c set file=%1&set file=!file:%W\\=!&set /p size=\"Enter size: \"&PATH pak!size! ./ \"!file!\"" simutrans-124.5/src/makeobj/makeobj.cc000066400000000000000000000127151520675460500177100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "../simutrans/utils/log.h" log_t::level_t debuglevel = log_t::LEVEL_WARN; #include "../simutrans/simdebug.h" #include "../simutrans/simtypes.h" #include "../simutrans/simversion.h" #include "../simutrans/utils/simstring.h" #include "../simutrans/descriptor/writer/obj_pak_exception.h" #include "../simutrans/descriptor/writer/root_writer.h" #include "../simutrans/descriptor/writer/image_writer.h" // Needed to avoid linking problems uint32 dr_time(void) { return 0; } int main(int argc, char* argv[]) { argv++; argc--; init_logging("stderr", true, true, "", "makeobj"); debuglevel = log_t::LEVEL_WARN; // only warnings and errors while( argc && ( !STRICMP(argv[0], "quiet") || !STRICMP(argv[0], "verbose") || !STRICMP(argv[0], "debug") ) ) { if (argc && !STRICMP(argv[0], "debug")) { argv++; argc--; debuglevel = log_t::LEVEL_DEBUG; // everything } else if (argc && !STRICMP(argv[0], "verbose")) { argv++; argc--; debuglevel = log_t::LEVEL_MSG; // only messages errors } else if (argc && !STRICMP(argv[0], "quiet")) { argv++; argc--; debuglevel = log_t::LEVEL_ERROR; // only fatal errors } } if( debuglevel>=log_t::LEVEL_WARN ) { puts( "Makeobj version " MAKEOBJ_VERSION " for Simutrans " VERSION_NUMBER " and higher" ); puts( "(c) 2002-2012 V. Meyer, Hj. Malthaner, M. Pristovsek & Simutrans development team\n" ); } if (argc && !STRICMP(argv[0], "capabilities")) { argv++; argc--; root_writer_t::instance()->capabilites(); return 0; } if (argc && !STRICMP(argv[0], "pak")) { argv++; argc--; try { const char* dest; if (argc) { dest = argv[0]; argv++; argc--; } else { dest = "./"; } root_writer_t::instance()->write(dest, argc, argv); } catch (const obj_pak_exception_t& e) { dbg->error( e.get_class(), e.get_info() ); return 1; } return 0; } if (argc && STRNICMP(argv[0], "pak", 3) == 0) { const int img_size = atoi(argv[0] + 3); if (img_size >= 16 && img_size < 32766) { dbg->message( "Image size", "Now set to %dx%d", img_size, img_size ); obj_writer_t::set_img_size(img_size); argv++; argc--; try { const char* dest; if (argc) { dest = argv[0]; argv++; argc--; } else { dest = "./"; } root_writer_t::instance()->write(dest, argc, argv); } catch (const obj_pak_exception_t& e) { dbg->error( e.get_class(), e.get_info() ); return 1; } return 0; } } if (argc && !STRICMP(argv[0], "expand")) { argv++; argc--; try { const char* dest; if (argc) { dest = argv[0]; argv++; argc--; } else { dest = "./"; } root_writer_t::instance()->expand_dat(dest, argc, argv); } catch (const obj_pak_exception_t& e) { dbg->error( e.get_class(), e.get_info() ); return 1; } return 0; } if (argc > 1) { if (!STRICMP(argv[0], "dump")) { argv++; argc--; root_writer_t::instance()->dump(argc, argv); return 0; } if (!STRICMP(argv[0], "list")) { argv++; argc--; root_writer_t::instance()->list(argc, argv); return 0; } if (!STRICMP(argv[0], "extract")) { argv++; argc--; root_writer_t::instance()->uncopy(argv[0]); return 0; } if (!STRICMP(argv[0], "merge")) { argv++; argc--; try { const char* dest = argv[0]; argv++; argc--; root_writer_t::instance()->copy(dest, argc, argv); } catch (const obj_pak_exception_t& e) { dbg->error( e.get_class(), e.get_info() ); return 1; } return 0; } } puts( "\n Usage: MakeObj [QUIET|VERBOSE|DEBUG] \n" "\n" " MakeObj CAPABILITIES\n" " Gives the list of objects, this program can read\n" " MakeObj PAK \n" " Creates a ready to use pak file for Simutrans from the dat files\n" " MakeObj pak128 \n" " Creates a special pak file for with 128x128 images\n" " Works with PAK16 up to PAK32767 but only up to 255 are tested\n" " MakeObj LIST \n" " Lists the contents of the given pak files\n" " MakeObj DUMP \n" " List the internal nodes of a file\n" " MakeObj MERGE \n" " Merges multiple pak files into one new pak file library\n" " MakeObj EXPAND \n" " Expands minified notation to complete notation on dat file(s)\n" " MakeObj EXTRACT \n" " Creates single files from a pak file library\n" "\n" " with a trailing slash a directory is searched rather than a file\n" " default for PAK is PAK ./ ./\n" "\n" " with QUIET as first arg status and copyright messages are omitted\n" "\n" " with VERBOSE as first arg also unused lines\n" " and unassigned entries are printed\n" "\n" " DEBUG dumps extended information about the pak process.\n" " Source: interpreted line from .dat file\n" " Image: .png file name\n" " X: X start position in .png to pack\n" " Y: Y start position in .png to pack\n" " Off X: X offset in image\n" " Off Y: Y offset in image\n" " Width: image width\n" " Width: image height\n" " Zoom: If image is zoomable or not\n" ); return 3; } simutrans-124.5/src/makeobj/vcpkg.json000066400000000000000000000002431520675460500177670ustar00rootroot00000000000000{ "name": "makeobj", "version-string": "1.0.0", "dependencies": [ "libpng" ], "builtin-baseline": "3c76dc55f8bd2b7f4824bcd860055094bfbbb9ea" }simutrans-124.5/src/nettool/000077500000000000000000000000001520675460500160375ustar00rootroot00000000000000simutrans-124.5/src/nettool/CMakeLists.txt000066400000000000000000000027751520675460500206120ustar00rootroot00000000000000# # This file is part of the Simutrans project under the artistic licence. # (see licence.txt) # add_executable(nettool nettool.cc ) target_compile_options(nettool PRIVATE ${SIMUTRANS_COMMON_COMPILE_OPTIONS}) target_compile_definitions(nettool PRIVATE NETTOOL=1 COLOUR_DEPTH=0) target_compile_definitions(nettool PRIVATE MSG_LEVEL=${SIMUTRANS_MSG_LEVEL}) if (SIMUTRANS_USE_SYSLOG) target_compile_definitions(nettool PRIVATE SYSLOG=1) endif () if (SIMUTRANS_USE_IP4_ONLY) target_compile_definitions(nettool PRIVATE USE_IP4_ONLY=1) endif () if (NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND SIMUTRANS_BUILD_32BIT) target_compile_options(nettool PRIVATE -m32) set_target_properties(nettool PROPERTIES LINK_FLAGS "-m32") endif () # these source files produce the same object code in nettool and simutrans target_sources(nettool PRIVATE ../simutrans/dataobj/freelist.cc ../simutrans/network/memory_rw.cc ../simutrans/network/network_address.cc ../simutrans/network/network_cmd.cc ../simutrans/network/network_packet.cc ../simutrans/network/network_socket_list.cc ../simutrans/simdebug.cc ../simutrans/simmem.cc ../simutrans/utils/simstring.cc ../simutrans/utils/fetchopt.cc ../simutrans/utils/sha1.cc ../simutrans/utils/sha1_hash.cc ) # these source files produce different object code in nettool and simutrans target_sources(nettool PRIVATE ../simutrans/utils/log.cc ../simutrans/network/network.cc ../simutrans/network/network_file_transfer.cc ) if (WIN32) target_link_libraries(nettool PRIVATE ws2_32) endif (WIN32) simutrans-124.5/src/nettool/Makefile000066400000000000000000000055111520675460500175010ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # CFG ?= default -include ../../config.$(CFG) OSTYPES = beos cygwin freebsd haiku linux mingw mac ifeq ($(findstring $(OSTYPE), $(OSTYPES)),) $(error Unkown OSTYPE "$(OSTYPE)", must be one of "$(OSTYPES)") endif # Absolutely essential CXXFLAGS += -DNETTOOL ifeq ($(OSTYPE),cygwin) OS_INC ?= -I/usr/include/mingw OS_OPT ?= -mwin32 STD_LIBS += -lwinmm -lcurses endif ifeq ($(OSTYPE),mingw) CC ?= gcc OS_OPT ?= -march=pentium # we need the libraries EXACTLY in this order to link STD_LIBS = -lmingw32 -lstdc++ -lws2_32 endif ifeq ($(OSTYPE),mac) CXXFLAGS += -DUSE_HW -DUSE_C endif ifdef OPTIMISE ifeq ($(shell expr $(OPTIMISE) \>= 1), 1) CXXFLAGS += -O3 # clang does not support fno-schedule-insns ifeq ($(findstring clang, $(CXX)),) CXXFLAGS += -fno-schedule-insns endif endif else CXXFLAGS += -O endif ifdef DEBUG ifeq ($(shell expr $(DEBUG) \>= 1), 1) CXXFLAGS += -g -DDEBUG endif ifeq ($(shell expr $(DEBUG) \>= 2), 1) CXXFLAGS += -fno-inline endif ifeq ($(shell expr $(DEBUG) \>= 3), 1) CXXFLAGS += -O0 endif else CXXFLAGS += -DNDEBUG endif ifdef PROFILE ifeq ($(shell expr $(PROFILE) \>= 1), 1) CXXFLAGS += -pg -DPROFILE -fno-inline LDFLAGS += -pg endif endif CXXFLAGS += -DREVISION -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align $(OS_INC) $(OS_OPT) $(FLAGS) # SOLO_SOURCES contains files which are unique to nettool; # SHARED_SOURCES contains those with the exact same object code in nettool and simutrans; # VARIANT_SOURCES contains those which need different .o files for nettool and simutrans. # At the moment they're all treated identically, of course. SOLO_SOURCES += nettool.cc SHARED_SOURCES += ../simutrans/dataobj/freelist.cc SHARED_SOURCES += ../simutrans/network/memory_rw.cc SHARED_SOURCES += ../simutrans/network/network_address.cc SHARED_SOURCES += ../simutrans/network/network_cmd.cc SHARED_SOURCES += ../simutrans/network/network_packet.cc SHARED_SOURCES += ../simutrans/network/network_socket_list.cc SHARED_SOURCES += ../simutrans/simdebug.cc SHARED_SOURCES += ../simutrans/simmem.cc SHARED_SOURCES += ../simutrans/utils/simstring.cc SHARED_SOURCES += ../simutrans/utils/fetchopt.cc SHARED_SOURCES += ../simutrans/utils/sha1.cc SHARED_SOURCES += ../simutrans/utils/sha1_hash.cc VARIANT_SOURCES += ../simutrans/utils/log.cc VARIANT_SOURCES += ../simutrans/network/network.cc VARIANT_SOURCES += ../simutrans/network/network_file_transfer.cc SOURCES ?= $(SOLO_SOURCES) $(SHARED_SOURCES) $(VARIANT_SOURCES) BUILDDIR ?= build/$(CFG) TOOL = nettool PROG ?= nettool ifeq ($(origin NETTOOL_PROGDIR), undefined) NETTOOL_PROGDIR := ../../$(BUILDDIR)/$(TOOL) endif BUILDDIR := ../../$(BUILDDIR) TOOL_PROGDIR = $(NETTOOL_PROGDIR) include ../../uncommon.mk simutrans-124.5/src/nettool/Nettool.sln000066400000000000000000000020651520675460500202040ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29102.190 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Nettool", "Nettool.vcxproj", "{6E485CA6-1FC1-45D2-97DC-0FD5197BF746}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6E485CA6-1FC1-45D2-97DC-0FD5197BF746}.Debug|x86.ActiveCfg = Debug|Win32 {6E485CA6-1FC1-45D2-97DC-0FD5197BF746}.Debug|x86.Build.0 = Debug|Win32 {6E485CA6-1FC1-45D2-97DC-0FD5197BF746}.Release|x86.ActiveCfg = Release|Win32 {6E485CA6-1FC1-45D2-97DC-0FD5197BF746}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BDF946B2-9B0A-44E7-974A-39C03A80EC45} EndGlobalSection EndGlobal simutrans-124.5/src/nettool/Nettool.vcxproj000066400000000000000000000176221520675460500211100ustar00rootroot00000000000000 Debug Win32 Release Win32 {6E485CA6-1FC1-45D2-97DC-0FD5197BF746} Nettool Application $(DefaultPlatformToolset) true <_ProjectFileVersion>16.0.28916.169 $(SolutionDir)..\build\nettool\$(Configuration)\ $(SolutionDir)..\build\nettool\$(Configuration)\ $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) $(SimIncludePath);$(IncludePath) $(SimLibraryPath);$(LibraryPath) Level3 true true 4250;4373;4800;4996 zlibstat.lib;Ws2_32.lib;wsock32.lib true Console MachineX86 ProgramDatabase Disabled NETTOOL;DEBUG=3;NOMINMAX MultiThreadedDebug EnableFastChecks true false MaxSpeed true Speed NETTOOL;NDEBUG;NOMINMAX MultiThreaded false true simutrans-124.5/src/nettool/nettool.cc000066400000000000000000000431131520675460500200340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* * Network server control tool for Simutrans */ #include #include #include // Needed for interaction with console // also needed, but this is included by networking code #ifndef _WIN32 #include #endif #include "../simutrans/network/network.h" #include "../simutrans/network/network_cmd.h" #include "../simutrans/network/network_packet.h" #include "../simutrans/network/network_socket_list.h" #include "../simutrans/simmem.h" #include "../simutrans/simtypes.h" #include "../simutrans/simversion.h" #include "../simutrans/utils/simstring.h" #include "../simutrans/utils/fetchopt.h" #include "../simutrans/utils/sha1.h" // dummy implementation // only receive nwc_service_t here // called from network_check_activity network_command_t* network_command_t::read_from_packet(packet_t *p) { // check data if (p==NULL || p->has_failed() || !p->check_version()) { delete p; dbg->warning("network_command_t::read_from_packet", "error in packet"); return NULL; } network_command_t* nwc = NULL; switch (p->get_id()) { case NWC_SERVICE: nwc = new nwc_service_t(); break; default: dbg->warning("network_command_t::read_from_socket", "received unknown packet id %d", p->get_id()); } if (nwc == NULL) { // Unknown / unsupported wire id: free p before returning so a // compromised server can't drive the admin tool out of memory // by replying with junk-id packets (the caller in nettool's // network_check_activity drops its packet pointer). Same // shape as the network_cmd_ingame.cc version. delete p; return NULL; } if (!nwc->receive(p) || p->has_failed()) { dbg->warning("network_command_t::read_from_packet", "error while reading cmd from packet"); delete nwc; nwc = NULL; } return nwc; } network_command_t* network_receive_command(uint16 id) { // wait for command with id, ignore other commands for(uint8 i=0; i<5; i++) { network_command_t* nwc = network_check_activity( 10000 ); if (nwc && nwc->get_id() == id) { return nwc; } delete nwc; } dbg->warning("network_receive_command", "no command with id=%d received", id); return NULL; } // echo() used to set whether terminal should echo user input or not // Used for more secure password entry #ifdef _WIN32 void echo(bool on) { DWORD mode; HANDLE hConIn = GetStdHandle(STD_INPUT_HANDLE); GetConsoleMode(hConIn, &mode); mode = on ? (mode | ENABLE_ECHO_INPUT) : (mode & ~(ENABLE_ECHO_INPUT)); SetConsoleMode(hConIn, mode); } #else // Should be good for POSIX platforms void echo(bool on) { struct termios settings; tcgetattr(STDIN_FILENO, &settings); settings.c_lflag = on ? (settings.c_lflag | ECHO) : (settings.c_lflag & ~(ECHO)); tcsetattr(STDIN_FILENO, TCSANOW, &settings); } #endif /** * Sets nwcs.flag = command_id, text to argv[1], number to argv[0]. * @returns true upon success */ bool fill_nwc_service(nwc_service_t &nwcs, uint32 command_id, int argc, char **argv) { nwcs.flag = command_id; switch (argc) { case 2: // second argument is a string if (argv[1]) { nwcs.text = strdup( argv[1] ); } case 1: // first argument is a positive number { int _n = atoi(argv[0]); if (_n >=0) { nwcs.number = _n; } else { return false; } } default: break; } return true; } // Simple commands specify only a command ID to perform an action on the server int simple_command(SOCKET socket, uint32 command_id, int argc, char **argv) { nwc_service_t nwcs; if ( !fill_nwc_service(nwcs, command_id, argc, argv) ) { return 3; } if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } return 0; } // Simple command to send command ID to server and receive and print a text buffer int simple_gettext_command(SOCKET socket, uint32 command_id, int argc, char **argv) { nwc_service_t nwcs; if ( !fill_nwc_service(nwcs, command_id, argc, argv) ) { return 3; } if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } nwc_service_t *nws = (nwc_service_t*)network_receive_command(NWC_SERVICE); if (nws==NULL) { return 3; } if (nws->flag != command_id) { delete nws; return 3; } if (nws->text) { printf("%s", nws->text); } else { printf("Nothing received.\n"); } delete nws; return 0; } int get_client_list(SOCKET socket, uint32 command_id, int, char **) { nwc_service_t nwcs; nwcs.flag = command_id; if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } nwc_service_t *nws = (nwc_service_t*)network_receive_command(NWC_SERVICE); if (nws==NULL) { return 3; } if (nws->flag != command_id || nws->socket_info==NULL) { delete nws; return 3; } // now get client list from packet vector_tpl &list = *(nws->socket_info); bool head = false; for(uint32 i=0; istate==socket_info_t::playing) { if (!head) { printf("List of playing clients:\n"); head = true; } uint32 ip = list[i]->address.ip; printf(" [%3d] .. %02d.%02d.%02d.%02d\n", i, (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); } } if (!head) { printf("No playing clients.\n"); } delete nws; return 0; } int kick_client(SOCKET socket, uint32 command_id, int, char **argv) { int client_nr = 0; client_nr = atoi(argv[0]); if (client_nr<=0) { return 3; } nwc_service_t nwcs; nwcs.flag = command_id; nwcs.number = client_nr; if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } return 0; } int ban_client(SOCKET socket, uint32 command_id, int, char **argv) { int client_nr = 0; client_nr = atoi(argv[0]); if (client_nr<=0) { return 3; } nwc_service_t nwcs; nwcs.flag = command_id; nwcs.number = client_nr; if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } return 0; } int get_blacklist(SOCKET socket, uint32 command_id, int, char **) { nwc_service_t nwcs; nwcs.flag = command_id; if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } nwc_service_t *nws = (nwc_service_t*)network_receive_command(NWC_SERVICE); if (nws==NULL) { return 3; } if (nws->flag != command_id || nws->address_list==NULL) { delete nws; return 3; } // now get list from packet address_list_t &list = *(nws->address_list); if (list.empty()) { printf("Blacklist empty\n"); } else { printf("List of banned addresses:\n"); } for(uint32 i=0; i=0; j-=8) { uint32 m = (list[i].mask >> j) & 0xff; if (m) { printf("%02d", (list[i].ip >> j) & 0xff); if ( m != 0xff) { printf("[%02x]", m); } if (j > 0) { printf("."); } } else { break; } } printf("\n"); } delete nws; return 0; } int ban_ip(SOCKET socket, uint32 command_id, int, char **argv) { net_address_t address(argv[0]); if (address.ip) { nwc_service_t nwcs; nwcs.flag = command_id; nwcs.text = strdup(argv[0]); if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } } return 0; } int unban_ip(SOCKET socket, uint32 command_id, int, char **argv) { net_address_t address(argv[0]); if (address.ip) { nwc_service_t nwcs; nwcs.flag = command_id; nwcs.text = strdup(argv[0]); if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } } return 0; } int say(SOCKET socket, uint32 command_id, int argc, char **argv) { nwc_service_t nwcs; nwcs.flag = command_id; // Cat all arguments together into message to send const int maxlen = 513; int remaining = maxlen - 1; char msg[maxlen]; int ind = 0; tstrncpy(msg, argv[ind], remaining); remaining -= strlen(argv[ind]); ind++; while (ind < argc && remaining > 1) { strcat(msg, " "); remaining -= 1; strncat(msg, argv[ind], remaining); remaining -= strlen(argv[ind]); ind++; } nwcs.text = strdup(msg); if (!nwcs.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } return 0; } int lock_company(SOCKET socket, uint32, int argc, char **argv) { // player number int _n = atoi(argv[0]); if (_n < 0 || _n >= 15 /*PLAYER_UNOWNED*/ ) { return 3; } uint8 player_nr = _n; // password source bool pwd_from_cl = strcmp(argv[1], "-F") != 0; if (!pwd_from_cl && argc<3) { return 3; } const char* password = NULL; // password if (pwd_from_cl) { password = argv[1]; } else { if (strcmp(argv[2], "-")==0) { // Read password from stdin char* password_in = MALLOCN(char, 256); fprintf(stderr, "Password: "); echo(false); scanf("%255s", password_in); echo(true); printf("\n"); password = password_in; } else if (FILE* const fd = fopen(argv[2], "r")) { // malloc ok here as utility is short-lived so no need to free() size_t const size = 256; char* password_in = MALLOCN(char, size); fgets(password_in, size, fd); password_in[strcspn(password_in, "\n")] = '\0'; fclose(fd); password = password_in; } else { return 3; } } printf("new password: %s\n",password); pwd_hash_t pwd_hash; { SHA1 sha1; sha1.Input(password, strlen(password)); sha1.Result(pwd_hash); } // now send nwc_auth_t command nwc_auth_player_t nwca(player_nr, pwd_hash); if (!nwca.send(socket)) { fprintf(stderr, "Could not send request!\n"); return 2; } return 0; } // Print usage and exit void usage() { fprintf(stderr, "nettool for Simutrans " VERSION_NUMBER " and higher\n" "\n" " Usage:\n" "\n" " nettool [options] [command argument]\n" "\n" " Options:\n" " -s : Specify server to connect to (default is localhost:13353)\n" " -p : Set password on command line\n" " -P : Read password from file specified (use '-' to read from stdin)\n" " -q : Quiet mode, copyright message will be omitted\n" "\n" " Commands:\n" " announce\n" " Request server announce itself to the central listing server\n" "\n" " clients\n" " Receive list of playing clients from server\n" "\n" " companies\n" " Receive list of running companies from server\n" "\n" " info-company \n" " Show detailed info for company\n" "\n" " lock-company \n" " lock-company -F \n" " Set password, read from file if specified (use '-' to read from stdin)\n" "\n" " unlock-company \n" " Clear password of company, effectively unlocking it for all clients\n" "\n" " remove-company \n" " Immediately remove company and all its belongings\n" "\n" " kick-client \n" " ban-client \n" " Kick / ban client (use clients command to get client number)\n" "\n" " ban-ip \n" " unban-ip \n" " Ban / unban ip address\n" "\n" " blacklist\n" " Display list of banned clients\n" "\n" " say \n" " Send admin message to all clients (maximum of 512 characters)\n" "\n" " shutdown\n" " Shut down server\n" "\n" " force-sync\n" " Force server to send sync command in order to save & reload the game\n" "\n" " Return codes:\n" " 0 .. success\n" " 1 .. server not reachable\n" " 2 .. could not send message to server\n" " 3 .. misc errors\n" "\n" ); exit(3); } // For each command, set: // name - char* - name of command as specified on command line // needs_auth - bool - specifies whether authentication is required for command // command_id - int - nwc_service_t command ID for the command in question // arguments - int - does this command require arguments, and if so how many? // function - function* - function pointer to function to evaluate for this command struct command_t { const char *name; bool needs_auth; uint32 command_id; int numargs; int (*func)(SOCKET, uint32, int, char**); }; int main(int argc, char* argv[]) { init_logging("stderr", true, true, NULL, "nettool"); // Use Fetchopt to parse option flags Fetchopt_t fetchopt(argc, argv, "hp:P:qs:"); bool opt_q = false; char default_server_address[] = "localhost:13353"; char *server_address = default_server_address; char *password = NULL; int ch; while ((ch = fetchopt.next()) != -1) { switch (ch) { case 'p': // Password specified on command line password = fetchopt.get_optarg(); break; case 'P': // Read password in from file specified // if filename is '-', read in from stdin if (!strcmp(fetchopt.get_optarg(), "-")) { // Password will be asked for later } else if (FILE* const fd = fopen(fetchopt.get_optarg(), "r")) { // malloc ok here as utility is short-lived so no need to free() size_t const size = 256; password = MALLOCN(char, size); fgets(password, size, fd); password[strcspn(password, "\n")] = '\0'; fclose(fd); } else { // Failure, file empty fprintf(stderr, "Unable to open file \"%s\" to read password\n", fetchopt.get_optarg()); } break; case 'q': opt_q = true; break; case 's': server_address = fetchopt.get_optarg(); break; case '?': case 'h': default: usage(); /* NOTREACHED */ } } // argc is length of argv, so argv[0] is first arg (name of program) // and argv[argc-1] is the last argument // After parsing arguments fetchopt.get_optind() will return the index // of the next argv after the last option // If this is equal to argc then there are no more arguments, this means // that the command has been omitted (which is a failure condition so usage // should be printed) // For commands which should take an argument, check that fetchopt.get_optind()+1 // is less than argc as well if (fetchopt.get_optind() >= argc) { usage(); } command_t commands[] = { {"announce", false, nwc_service_t::SRVC_ANNOUNCE_SERVER, 0, &simple_command}, {"clients", true, nwc_service_t::SRVC_GET_CLIENT_LIST, 0, &get_client_list}, {"kick-client", true, nwc_service_t::SRVC_KICK_CLIENT, 1, &kick_client}, {"ban-client", true, nwc_service_t::SRVC_BAN_CLIENT, 1, &ban_client}, {"blacklist", true, nwc_service_t::SRVC_GET_BLACK_LIST, 0, &get_blacklist}, {"ban-ip", true, nwc_service_t::SRVC_BAN_IP, 1, &ban_ip}, {"unban-ip", true, nwc_service_t::SRVC_UNBAN_IP, 1, &unban_ip}, {"say", true, nwc_service_t::SRVC_ADMIN_MSG, 1, &say}, {"shutdown", true, nwc_service_t::SRVC_SHUTDOWN, 0, &simple_command}, {"force-sync", true, nwc_service_t::SRVC_FORCE_SYNC, 0, &simple_command}, {"companies", true, nwc_service_t::SRVC_GET_COMPANY_LIST, 0, &simple_gettext_command}, {"info-company", true, nwc_service_t::SRVC_GET_COMPANY_INFO, 1, &simple_gettext_command}, {"unlock-company", true, nwc_service_t::SRVC_UNLOCK_COMPANY, 1, &simple_command}, {"remove-company", true, nwc_service_t::SRVC_REMOVE_COMPANY, 1, &simple_command}, {"lock-company", true, nwc_service_t::SRVC_LOCK_COMPANY, 2, &lock_company} }; int numcommands = lengthof(commands); // Determine which command we're running & validate any arguments int cmdindex; for (cmdindex = 0; cmdindex < numcommands; cmdindex++) { if (strcmp(commands[cmdindex].name, argv[fetchopt.get_optind()]) == 0) { // Validate number of parameters if (fetchopt.get_optind() + commands[cmdindex].numargs >= argc) { usage(); } break; } } // Unknown command if (cmdindex == numcommands) { usage(); } // Print copyright notice unless quiet flag set if (!opt_q) { fprintf(stderr, "nettool for Simutrans " VERSION_NUMBER " and higher\n" ); } // If command requires authentication and password is not set then // ask for password from stdin (interactive) if( commands[cmdindex].needs_auth && password == NULL ) { // Read password from stdin // malloc ok here as utility is short-lived so no need to free() password = MALLOCN(char, 256); fprintf(stderr, "Password: "); echo(false); scanf("%255s", password); echo(true); printf("\n"); } // This is done whether we're executing a password protected command or not... const char *error = NULL; SOCKET const socket = network_open_address(server_address, error); if (error) { fprintf(stderr, "Could not connect to server at %s: %s\n", server_address, error); return 1; } // now we are connected socket_list_t::add_client(socket); // If authentication required, perform authentication if( commands[cmdindex].needs_auth ) { // try to authenticate us nwc_service_t nwcs; nwcs.flag = nwc_service_t::SRVC_LOGIN_ADMIN; nwcs.text = strdup(password); if (!nwcs.send(socket)) { fprintf(stderr, "Could not send login data!\n"); return 2; } // wait for acknowledgement nwc_service_t *nws = (nwc_service_t*)network_receive_command(NWC_SERVICE); if( nws==NULL || nws->flag != nwc_service_t::SRVC_LOGIN_ADMIN ) { fprintf(stderr, "Authentication failed!\n"); delete nws; return 3; } if( nws->number == 0 ) { fprintf(stderr, "Wrong password!\n"); delete nws; return 3; } network_set_client_id( nws->number ); delete nws; } // Execute command function and exit with return code const int first_arg = fetchopt.get_optind() + 1; char **argv_in; int argc_in; if (first_arg >= argc) { argv_in = NULL; argc_in = 0; } else { argv_in = argv + first_arg; argc_in = argc - first_arg; } return commands[cmdindex].func(socket, commands[cmdindex].command_id, argc_in, argv_in); } simutrans-124.5/src/paksetinfo.h000066400000000000000000000060731520675460500166750ustar00rootroot00000000000000// Generated by "get_pak.sh -generate_h". DO NOT EDIT. #define PAKSET_COUNT 20 #define OBSOLETE_FROM (12) paksetinfo_t pakinfo[PAKSET_COUNT] = { { "http://downloads.sourceforge.net/project/simutrans/pak64/124-4/simupak64-124-4.zip", "pak", "pak64 124.4 r2222M", 16464 }, { "http://downloads.sourceforge.net/project/simutrans/pak128/pak128%20for%20ST%20124.4up%20%282.10.1%29/simupak128-2-10-1-for124-4up.zip", "pak128", "pak128 2.10.1 for 124.4 git r699 hash 6dccf5d4", 415900 }, { "http://downloads.sourceforge.net/project/simutrans/pak192.comic/pak192.comic%20V0.7.2/pak192-comic.zip", "pak192.comic", "Pak192.Comic V0.7.2 Rev 1296", 909748 }, { "http://simutrans-germany.com/pak.german/pak64.german_0-124-4-0-1_full.zip", "pak64.german", "pak64.german 0.124.4.0.1", 29232 }, { "http://downloads.sourceforge.net/project/simutrans/PAK128.german/PAK128.german_2.4_for_ST_124.3.1/PAK128.german_2.4_for_ST_124.3.1.zip", "PAK128.german", "Pak128.german VS 2.4 (Rev. 547)", 576132 }, { "http://downloads.sourceforge.net/project/simutrans/pak64.japan/123-0/simupak64.japan-123-0.zip", "pak.japan", "pak64.japan 123 r2101", 10175 }, { "https://github.com/wa-st/pak-nippon/releases/download/v0.6.2/pak.nippon-v0.6.2.zip", "pak.nippon", "pak.nippon v0.6.2", 50198 }, { "http://downloads.sourceforge.net/project/simutrans/Pak128.CS/nightly%20builds/pak128.CS-r2096.zip", "pak128.CS", "Pak128.CS 0.3.0 r2096", 78628 }, { "http://downloads.sourceforge.net/project/simutrans/pak128.britain/pak128.Britain%20for%20120-3/pak128.Britain.1.18-120-3.zip", "pak128.Britain", "pak128.Britain 1.18 120.3 r1991", 241715 }, { "https://github.com/Varkalandar/pak144.Excentrique/releases/download/r0.08/pak144.Excentrique_v008.zip", "pak144.Excentrique", "pak144.Excentrique v0.08", 7621 }, { "http://downloads.sourceforge.net/project/simutrans/pakTTD/simupakTTD-124-0.zip", "pakTTD", "pak64 120.4.1 r2146", 1995 }, { "http://codeberg.org/Nazalassa/pak48.bitlit/releases/download/0.1d/pak48.bitlit_0.1d.zip", "pak48.bitlit", "pak48.bitlit v0.1d", 1762 }, { "http://downloads.sourceforge.net/project/simutrans/pak96.comic/pak96.comic%20for%20111-3/pak96.comic-0.4.10-plus.zip", "pak96.comic", "pak96.comic V4.1 plus", 32526 }, { "http://pak128.jpn.org/souko/pak128.japan.120.0.cab", "pak128.japan", "Pak128.Japan 120.0", 27780 }, { "http://downloads.sourceforge.net/project/simutrans/pak32.comic/pak32.comic%20for%20102-0/pak32.comic_102-0.zip", "pak32", "", 3420 }, { "https://github.com/Varkalandar/pak48.Excentrique/releases/download/v0.19_RC3/pak48.excentrique_v019rc3.zip", "pak48.Excentrique", "pak48.Excentrique v0.19", 2016 }, { "http://downloads.sourceforge.net/project/simutrans/pak64.contrast/pak64.Contrast_910.zip", "pakcontrast", "", 1440 }, { "http://downloads.sourceforge.net/project/simutrans/OldFiles/PakHD_v04B_100-0.zip", "pakHD", "Martin", 12511 }, { "http://downloads.sourceforge.net/project/simutrans/pakHAJO/pakHAJO_102-2-2/pakHAJO_0-102-2-2.zip", "pakHAJO", "", 7460 }, { "http://downloads.sourceforge.net/project/simutrans/pak64.scifi/pak64.scifi_112.x_v0.2.zip", "pak64.scifi", "pak64.SciFi V0.2", 3293 }, }; simutrans-124.5/src/patches/000077500000000000000000000000001520675460500160025ustar00rootroot00000000000000simutrans-124.5/src/patches/dump-png.diff000066400000000000000000000134121520675460500203640ustar00rootroot00000000000000Index: descriptor/image.cc =================================================================== --- descriptor/image.cc (revision 10146) +++ descriptor/image.cc (working copy) @@ -189,3 +189,46 @@ } return target_image; } + + +/** + * decodes an image into a 32 bit bitmap (needed to dump images only) + */ +void image_t::decode_img(sint16 , sint16 , uint32 *target, uint32 target_width, uint32 target_height ) const +{ + if( h > 0 && w > 0 ) { + + // now: unpack the image + uint16 const *src = data; + for( sint32 yy = y; yy < h+y; yy++ ) { + uint16 runlen; + sint16 max_w = x; + uint8* p = ((uint8*)target) + x * 3 + yy * target_width * 3; + + // decode line + runlen = *src++; + do { + // clear run + p += runlen*3; + // color pixel + runlen = *src++; + while (runlen--) { + // get rgb components + uint16 s = *src++; + if( s>=0x8000 ) { + // special color + *p++ = rgbtab[s&0x001F]>>16; + *p++ = rgbtab[s&0x001F]>>8; + *p++ = rgbtab[s&0x001F]; + } + else { + *p++ = ((s>>10) & 0x001F)<<3; + *p++ = ((s>>5) & 0x001F)<<3; + *p++ = ((s) & 0x001F)<<3; + } + } + runlen = *src++; + } while( runlen!=0); + } + } +} Index: descriptor/image.h =================================================================== --- descriptor/image.h (revision 10146) +++ descriptor/image.h (working copy) @@ -80,6 +80,8 @@ void register_image() { ::register_image(this); } + void decode_img(sint16 xoff, sint16 yoff, uint32* target, uint32 target_width, uint32 target_height) const; + private: friend class image_reader_t; }; Index: descriptor/reader/image_reader.cc =================================================================== --- descriptor/reader/image_reader.cc (revision 10146) +++ descriptor/reader/image_reader.cc (working copy) @@ -19,6 +19,11 @@ #include "../../tpl/array_tpl.h" +#ifdef MAKEOBJ +#undef COLOUR_DEPTH +#define COLOUR_DEPTH 16 +#endif + // if without graphics backend, do not copy any pixel #if COLOUR_DEPTH != 0 #define skip_reading_pixels_if_no_graphics @@ -186,6 +191,7 @@ } } +#if !defined(MAKEOBJ) if (desc->len != 0) { // get the adler hash (since we have zlib on board anyway ... ) bool do_register_image = true; @@ -221,6 +227,7 @@ desc = same; } } +#endif return desc; } Index: descriptor/reader/image_reader.h =================================================================== --- descriptor/reader/image_reader.h (revision 10146) +++ descriptor/reader/image_reader.h (working copy) @@ -17,6 +17,13 @@ { OBJ_READER_DEF(image_reader_t, obj_image, "image"); +#ifdef MAKEOBJ +#undef COLOUR_DEPTH +#define COLOUR_DEPTH 16 + + void register_reader() {} +#endif + public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; Index: descriptor/writer/image_writer.cc =================================================================== --- descriptor/writer/image_writer.cc (revision 10146) +++ descriptor/writer/image_writer.cc (working copy) @@ -31,7 +31,41 @@ int ymax; }; +#include "../reader/image_reader.h" +#include +static image_reader_t ir; + +static int image_nr = 0; + + +void image_writer_t::dump_node(FILE* infp, const obj_node_info_t& node) +{ + image_t *desc = (image_t *)ir.read_node( infp, (obj_node_info_t& )node ); + + /* now we can dump the image as we like it ... */ + uint16 width = min( 64, desc->w+desc->x ); + uint16 height = min( 64, desc->h+desc->y ); + + raw_image_t rawimg(width, height, raw_image_t::FMT_RGB888); + uint8* target = rawimg.access_pixel(0, 0); + + for( uint32 i=0; idecode_img( 0, 0, (uint32 *)rawimg.access_pixel(0, 0), rawimg.get_width(), height ); + + // PNG + char fname[256]; + sprintf( fname, "image%i.png", image_nr++ ); + rawimg.write_png(fname); +} + + + std::string image_writer_t::last_img_file; raw_image_t image_writer_t::input_img; Index: descriptor/writer/image_writer.h =================================================================== --- descriptor/writer/image_writer.h (revision 10146) +++ descriptor/writer/image_writer.h (working copy) @@ -37,6 +37,8 @@ static void set_img_size(int _img_size) { img_size = _img_size; } obj_type get_type() const OVERRIDE { return obj_image; } + virtual void dump_node(FILE* infp, const obj_node_info_t& node); + const char* get_type_name() const OVERRIDE { return "image"; } void write_obj(FILE* fp, obj_node_t& parent, std::string imagekey, uint32 index); Index: makeobj/Makefile =================================================================== --- makeobj/Makefile (revision 10146) +++ makeobj/Makefile (working copy) @@ -118,6 +118,7 @@ SOLO_SOURCES += ../descriptor/writer/way_obj_writer.cc SOLO_SOURCES += ../descriptor/writer/xref_writer.cc SHARED_SOURCES += ../descriptor/image.cc +SHARED_SOURCES += ../descriptor/reader/image_reader.cc SHARED_SOURCES += ../dataobj/freelist.cc SHARED_SOURCES += ../io/raw_image.cc SHARED_SOURCES += ../simio.h Index: makeobj/Makeobj.vcxproj =================================================================== --- makeobj/Makeobj.vcxproj (revision 10146) +++ makeobj/Makeobj.vcxproj (working copy) @@ -88,6 +88,7 @@ + @@ -133,6 +134,7 @@ + simutrans-124.5/src/patches/experimental-hierachical-patchfinder.diff000066400000000000000000000264651520675460500260650ustar00rootroot00000000000000Index: gui/karte.cc =================================================================== --- gui/karte.cc (revision 1232) +++ gui/karte.cc (working copy) @@ -233,6 +233,7 @@ #endif color = calc_hoehe_farbe((welt->lookup_hgt(gr->gib_pos().gib_2d())/Z_TILE_STEP)+height, welt->gib_grundwasser()/Z_TILE_STEP); //color = COL_BLUE; // water with boat? + color = route_t::add_to_minimap(welt,gr); } else { color = dt->get_fabrik()->gib_kennfarbe(); Index: dataobj/route.cc =================================================================== --- dataobj/route.cc (revision 1232) +++ dataobj/route.cc (working copy) @@ -19,8 +19,187 @@ #include "umgebung.h" +// sorted heap, since we only need insert and pop +#include "../tpl/array2d_tpl.h" // fastest +#include "../tpl/array_tpl.h" // fastest +#include "../tpl/minivec_tpl.h" // fastest + +class region_node { +public: + koord center; + minivec_tpl conn; + uint8 marked; +}; + +static array_tpl connections; +static array2d_tpl *water_regions = NULL; +static int REGION_SIZE = 16; +static uint8 current_mark = 1; +#define REGION_MASK (~(REGION_SIZE-1)) + + +static void fill_region(karte_t *welt, int x, int y, uint16 region_nr ) +{ + const uint16 replace_nr = water_regions->at(x,y); + const int xmin = (x & REGION_MASK); + const int xmax = min( welt->gib_groesse_x(), xmin+REGION_SIZE ); + // fill one line + int xl, xr; + xl = xr = x; + while(xl>=x && water_regions->at(xl,y)==replace_nr ) xl--; + if(xlat(xr,y)==replace_nr ) xr++; + if(xr==xmax) xr--; + // first fill with color + for( x=xl; x<=xr; x++ ) { + water_regions->at(x,y) = region_nr; + } + // and then recursion + const int ymin = (y & REGION_MASK); + const int ymax = min( welt->gib_groesse_y(), ymin+REGION_SIZE ); + y--; + if(y>=ymin) { + for( x=xl; xat(x,y)==replace_nr) { + fill_region( welt, x, y, region_nr ); + } + } + } + y += 2; + if(yat(x,y)==replace_nr) { + fill_region( welt, x, y, region_nr ); + } + } + } +} + + +static void init_regions(karte_t *welt) +{ + if(water_regions) { + delete water_regions; + water_regions = NULL; + } + const int xw = welt->gib_groesse_x(); + const int yw = welt->gib_groesse_y(); + // what region size? + REGION_SIZE = 1; + while( max(xw,yw)/REGION_SIZE>=256 ) { + REGION_SIZE <<= 1; + } + if(REGION_SIZE<4) { + REGION_SIZE = 0; + return; + } + water_regions = new array2d_tpl( xw, yw ); + // init land with zero, water with 1 + memset( (void *)(water_regions->to_array()), 0, sizeof(uint16)*xw*yw ); + for( int y=0; ylookup_kartenboden(koord(x,y))->gib_weg_ribi(water_wt)) { + water_regions->at(x,y) = 1; + } + } + } + /* now finde the regions + * we go from top to bottom and from left to right + */ + int region = 2; + for( int y=0; yat(xoff,yoff)==1) { + fill_region( welt, xoff, yoff, region++ ); + } + } + } + } + } + // finally: init new nodelist + connections.resize(region); + for( int i=0; iat(x,yh)>1 && water_regions->at(x,yh+1)>1) { + connections[water_regions->at(x,yh)].conn.append_unique(water_regions->at(x,yh+1)); + connections[water_regions->at(x,yh)].center = center; + } + } + } + const int ymax = min(yw,yh); + if(xb+1at(xb,y)>1 && water_regions->at(xb+1,y)>1) { + connections[water_regions->at(xb,y)].conn.append_unique(water_regions->at(xb+1,y)); + connections[water_regions->at(xb,y)].center = center; + } + } + } + int y = yh & REGION_MASK; + if(y>0) { + for( int x=xb-(REGION_SIZE-1); xat(x,y)>1 && water_regions->at(x,y-1)>1) { + connections[water_regions->at(x,y)].conn.append_unique(water_regions->at(x,y-1)); + connections[water_regions->at(x,y)].center = center; + } + } + } + int x = xb & REGION_MASK; + if(x>0) { + for( int y=yh-(REGION_SIZE-1); yat(x,y)>1 && water_regions->at(x-1,y)>1) { + connections[water_regions->at(x,y)].conn.append_unique(water_regions->at(x-1,y)); + connections[water_regions->at(x,y)].center = center; + } + } + } + } + } + // now, in principle, we can free the region tile array ... + + // debug + for( int i=2; i%i",i-2,connections[i].conn[j]-2); + } + } +} + +// debug +uint8 route_t::add_to_minimap(karte_t *welt, const grund_t *gr) +{ + if(water_regions==NULL) init_regions(welt); + if(REGION_SIZE==0) { + return 3; + } + if(water_regions->get_width()!=welt->gib_groesse_x() || water_regions->get_height()!=welt->gib_groesse_y()) init_regions(welt); + uint16 node = water_regions->at( gr->gib_pos().x, gr->gib_pos().y ); + return connections[node].marked==current_mark ? 15 : ((node*241)%242)+16; +} + + + + // if defined, print some profiling informations into the file -//#define DEBUG_ROUTES +#define DEBUG_ROUTES // this is WAY slower than the sorted_heap! (also not always finding best routes, but was the original algorithms) //#include "../tpl/prioqueue_tpl.h" // ~100-250% slower @@ -36,6 +215,10 @@ // sorted heap, since we only need insert and pop #include "../tpl/binary_heap_tpl.h" // fastest +#ifdef DEBUG_ROUTES +#include "../gui/karte.h" +#include "../simsys.h" +#endif void @@ -324,9 +507,145 @@ -bool route_t::intern_calc_route(karte_t *welt, const koord3d ziel, const koord3d start, fahrer_t *fahr, const uint32 max_speed, const uint32 max_cost) +#define NODE_TO_KOORD(i) koord((i)%welt->gib_groesse_x(),(i)/welt->gib_groesse_x()) +#define GRUND_TO_NODE(i) ((i)->gib_pos().x+(i)->gib_pos().y*welt->gib_groesse_x()) +#define NODE_DISTANCE(i,j) (abs_distance(connections[i].center,connections[j].center)+((i)!=(j))) + +// ships may use a hierachical pathfinder +bool route_t::intern_calc_route_water(karte_t *welt, const koord3d ziel, const koord3d start, fahrer_t *fahr, const uint32 max_speed, const uint32 max_cost) { + // first, find a route in the node list + uint16 start_node = water_regions->at(start.x,start.y); + uint16 end_node = water_regions->at(ziel.x,ziel.y); + + uint16 node=0; + bool ziel_erreicht = false; + + // memory in static list ... + if(nodes==NULL) { + MAX_STEP = umgebung_t::max_route_steps; // may need very much memory => configurable + nodes = new ANode[MAX_STEP + 4 + 2]; + } + /* now find the shortest route beteen them with A* */ + + // there are several variant for maintaining the open list + // however, only binary heap and HOT queue with binary heap are worth considering +#if defined(tpl_HOT_queue_tpl_h) + static HOT_queue_tpl queue; +#elif defined(tpl_binary_heap_tpl_h) + static binary_heap_tpl queue; +#elif defined(tpl_sorted_heap_tpl_h) + static sorted_heap_tpl queue; +#else + static prioqueue_tpl queue; +#endif + GET_NODE(); + + // use different mark + current_mark ++; + + // clear the queue (should be empty anyhow) + route.clear(); + queue.clear(); + + uint32 step = 0; + ANode* tmp = &nodes[step]; + step ++; + + tmp->parent = NULL; + tmp->gr = welt->lookup_kartenboden(NODE_TO_KOORD(start_node)); + tmp->f = NODE_DISTANCE(start_node,end_node); + tmp->g = 0; + tmp->dir = 0; + tmp->count = 0; + + queue.insert(tmp); + + uint32 beat=1; + do { + // Hajo: this is too expensive to be called each step + if((beat++ & 255) == 0) { + INT_CHECK("route 161"); + } + + ANode *test_tmp = queue.pop(); + uint16 test_node = GRUND_TO_NODE(test_tmp->gr); + + if(connections[test_node].marked==current_mark) { + // we were already here on a faster route, thus ignore this branch + // (trading speed against memory consumption) + continue; + } + + // we took the target pos out of the closed list + if(end_node==node) { + ziel_erreicht = true; + break; + } + + tmp = test_tmp; + node = test_node; + connections[node].marked = current_mark; + + for(int n=0; ng + 1; + + // no check for curves ... + + const uint32 new_f = new_g + NODE_DISTANCE(to_node,end_node); + + // add new + ANode* k = &nodes[step]; + step ++; + + k->parent = tmp; + k->gr = welt->lookup_kartenboden(NODE_TO_KOORD(to_node)); + k->g = new_g; + k->f = new_f; + k->count = tmp->count+1; + + queue.insert( k ); + } + } + + } while (!queue.empty() && !ziel_erreicht && stepgg,max_cost); + //reliefkarte_t::gib_karte()->calc_map(); +#endif + vector_tplnodes(tmp->count+16); + + // target reached? + if(!ziel_erreicht || step >= MAX_STEP || tmp->parent==NULL) { + dbg->warning("route_t::intern_calc_route()","Too many steps (%i>=max %i) in route (too long/complex)",step,MAX_STEP); + return false; + } + else { + // reached => construct route + current_mark ++; + while(tmp != NULL) { + uint16 n = GRUND_TO_NODE(tmp->gr); + nodes.append( n ); + connections[n].marked = current_mark; + tmp = tmp->parent; + } + } + + RELEASE_NODE(); + + return false; +} + + + +bool route_t::intern_calc_route(karte_t *welt, const koord3d ziel, const koord3d start, fahrer_t *fahr, const uint32 max_speed, const uint32 max_cost) +{ bool ok = false; // check for existing koordinates Index: dataobj/route.h =================================================================== --- dataobj/route.h (revision 1232) +++ dataobj/route.h (working copy) @@ -28,6 +28,12 @@ { private: /** + * an experimental hierachical pathfinder + * @author Hj. Malthaner + */ + bool intern_calc_route_water(karte_t *w, koord3d start, koord3d ziel, fahrer_t *fahr, const uint32 max_kmh, const uint32 max_cost); + + /** * Die eigentliche Routensuche * @author Hj. Malthaner */ @@ -36,6 +42,8 @@ vector_tpl route; // Die Koordinaten fuer die Fahrtroute public: + static uint8 add_to_minimap(karte_t *welt, const grund_t *gr); + // this class save the nodes during route search class ANode { public: simutrans-124.5/src/patches/sun.diff000077500000000000000000000035011520675460500174430ustar00rootroot00000000000000Index: simworld.cc =================================================================== --- simworld.cc (revision 2029) +++ simworld.cc (working copy) @@ -2032,6 +2032,8 @@ if(letzter_monat>11) { letzter_monat = 0; } + view->update_darkness( 52, letzter_monat ); + DBG_MESSAGE("karte_t::neuer_monat()","Month %d has started", letzter_monat); DBG_MESSAGE("karte_t::neuer_monat()","sync_step %u objects", sync_list.count() ); Index: simview.cc =================================================================== --- simview.cc (revision 2031) +++ simview.cc (working copy) @@ -5,6 +5,7 @@ */ #include +#include #include "simworld.h" #include "simview.h" @@ -22,13 +23,17 @@ #include "dataobj/umgebung.h" #include "dings/zeiger.h" +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + karte_ansicht_t::karte_ansicht_t(karte_t *welt) { this->welt = welt; } -static const sint8 hours2night[] = +static sint8 hours2night[48] = { 4,4,4,4,4,4,4,4, 4,4,4,4,3,2,1,0, @@ -40,6 +45,20 @@ +void karte_ansicht_t::update_darkness(double latitude, int month) +{ + // first we need the approxiamate day + const double day = 10.0 + (30.4*month) +182.5; + const double delta = (-23.45*M_PI/180.0) * cos(2.0*M_PI/365.0*day); + for( int i=0; i<48; i++ ) { + const double tau = i*M_PI/24.0; + double arcsin_h = cos(delta)*cos(tau)*cos(latitude*M_PI/180.0) + sin(delta)*sin(latitude*M_PI/180.0); + hours2night[ i ] = (int)((1.0+arcsin_h)*3.0); + } +} + + + void karte_ansicht_t::display(bool force_dirty) { Index: simview.h =================================================================== --- simview.h (revision 2029) +++ simview.h (working copy) @@ -21,6 +21,7 @@ public: karte_ansicht_t(karte_t *welt); + void update_darkness(double latitude, int month); void display(bool dirty); }; simutrans-124.5/src/script_ai_text.dat000066400000000000000000000014661520675460500200750ustar00rootroot00000000000000obj=script_ai_text name=%s build road line from %s (%s) to %s (%s) - obj=script_ai_text name=%s build ship line from %s (%s) to %s (%s) - obj=script_ai_text name=%s build rail line from %s (%s) to %s (%s) - obj=script_ai_text name=%s optimize way line from %s (%s) to %s (%s) - obj=script_ai_text name=%s extends the route from %s (%s) to %s (%s) - obj=script_ai_text name=%s build additional convoy to line: %s - obj=script_ai_text name=%s removes convoys from line: %s - obj=script_ai_text name=vehicles of the line %s were retired - obj=script_ai_text name=%s electrified the line %s - obj=script_ai_text name=%s removes line: %s - obj=script_ai_text name=%s expand the station %s (%s) and station %s (%s) - obj=script_ai_text name=%s - last month: operating profit %d net wealth %d -simutrans-124.5/src/simutrans/000077500000000000000000000000001520675460500164005ustar00rootroot00000000000000simutrans-124.5/src/simutrans/Simutrans-full.svg000066400000000000000000000223251520675460500220520ustar00rootroot00000000000000 image/svg+xml simutrans-124.5/src/simutrans/builder/000077500000000000000000000000001520675460500200265ustar00rootroot00000000000000simutrans-124.5/src/simutrans/builder/brueckenbauer.cc000066400000000000000000001240741520675460500231620ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../dataobj/crossing_logic.h" #include "../dataobj/marker.h" #include "../dataobj/scenario.h" #include "../descriptor/bridge_desc.h" #include "../ground/boden.h" #include "../ground/brueckenboden.h" #include "../gui/minimap.h" #include "../gui/tool_selector.h" #include "../obj/bruecke.h" #include "../obj/depot.h" #include "../obj/leitung2.h" #include "../obj/pillar.h" #include "../obj/signal.h" #include "../obj/wayobj.h" #include "../player/simplay.h" #include "../tool/simtool.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/vector_tpl.h" #include "../world/simworld.h" #include "../simachievements.h" #include "../simdebug.h" #include "../simhalt.h" #include "../simtypes.h" #include "brueckenbauer.h" #include "wegbauer.h" karte_ptr_t bridge_builder_t::welt; /// All bridges hashed by name static stringhashtable_tpl desc_table; void bridge_builder_t::register_desc(bridge_desc_t *desc) { // avoid duplicates with same name if( const bridge_desc_t *old_desc = desc_table.remove(desc->get_name()) ) { tool_t::general_tool.remove( old_desc->get_builder() ); delete old_desc->get_builder(); delete old_desc; } // add the tool tool_build_bridge_t *tool = new tool_build_bridge_t(); tool->set_icon( desc->get_cursor()->get_image_id(1) ); tool->cursor = desc->get_cursor()->get_image_id(0); tool->set_default_param(desc->get_name()); tool_t::general_tool.append( tool ); desc->set_builder( tool ); desc_table.put(desc->get_name(), desc); } const bridge_desc_t *bridge_builder_t::get_desc(const char *name) { return (name ? desc_table.get(name) : NULL); } const bridge_desc_t *bridge_builder_t::find_bridge(const waytype_t wtyp, const sint32 min_speed, const uint16 time) { const bridge_desc_t *find_desc=NULL; for(auto const& i : desc_table) { bridge_desc_t const* const desc = i.value; if( desc->get_waytype()==wtyp && desc->is_available(time) ) { if( find_desc==NULL || (find_desc->get_topspeed()get_topspeed()get_topspeed()) || (desc->get_topspeed()>=min_speed && desc->get_maintenance()get_maintenance()) ) { find_desc = desc; } } } return find_desc; } /** * Compares the maximum speed of two bridges. * @param a the first bridge. * @param b the second bridge. * @return true, if the speed of the second bridge is greater. */ static bool compare_bridges(const bridge_desc_t* a, const bridge_desc_t* b) { return a->get_topspeed() < b->get_topspeed(); } void bridge_builder_t::fill_menu(tool_selector_t *tool_selector, const waytype_t wtyp, sint16 /*sound_ok*/) { // check if scenario forbids this if (!welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_BRIDGE | GENERAL_TOOL, wtyp)) { return; } bool enable = welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_BRIDGE | GENERAL_TOOL, wtyp, 0); const uint16 time = welt->get_timeline_year_month(); vector_tpl matching(desc_table.get_count()); // list of matching types (sorted by speed) for(auto const& i : desc_table) { bridge_desc_t const* const b = i.value; if ( b->get_waytype()==wtyp && b->is_available(time) ) { if (welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_BRIDGE | GENERAL_TOOL, wtyp,b->get_name())) { matching.insert_ordered(b, compare_bridges); } } } // now sorted ... for(bridge_desc_t const *i : matching) { i->get_builder()->enabled = enable && welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_BRIDGE | GENERAL_TOOL, wtyp, i->get_name()); tool_selector->add_tool_selector(i->get_builder()); } } const vector_tpl& bridge_builder_t::get_available_bridges(const waytype_t wtyp) { static vector_tpl dummy; dummy.clear(); const uint16 time = welt->get_timeline_year_month(); for(auto const& i : desc_table) { bridge_desc_t const* const b = i.value; if (b->get_waytype()==wtyp && b->is_available(time) && b->get_builder()) { dummy.append(b); } } return dummy; } inline bool ribi_check( ribi_t::ribi ribi, ribi_t::ribi check_ribi ) { // either check for single (if nothing given) otherwise ensure exact match return check_ribi ? ribi == check_ribi : ribi_t::is_single( ribi ); } /** * if check_ribi==0, then any single tile is ok * @returns either (1) error_message (must abort bridge here) (2) "" if it could end here or (3) NULL if it must end here */ const char *check_tile( const grund_t *gr, const player_t *player, waytype_t wt, ribi_t::ribi check_ribi ) { static karte_ptr_t welt; // not overbuilt transformers if( gr->find()!=NULL || gr->find()!=NULL ) { return "A bridge must start on a way!"; } if( !slope_t::is_way(gr->get_weg_hang()) ) { // it's not a straight slope return "Bruecke muss an\neinfachem\nHang beginnen!\n"; } if( gr->is_halt() || gr->get_depot() ) { // something in the way return "Tile not empty."; } if( wt != powerline_wt && gr->get_leitung() ) { return "Tile not empty."; } // we can build a ramp when there is one (or with tram two) way in our direction and no stations/depot etc. if( weg_t *w = gr->get_weg_nr(0) ) { if( w->get_removal_error(player) != NULL ) { // not our way return "Das Feld gehoert\neinem anderen Spieler\n"; } // now check for direction ribi_t::ribi ribi = w->get_ribi_unmasked(); if( weg_t *w2 = gr->get_weg_nr(1) ) { if( w2->get_removal_error(player) != NULL ) { // not our way return "Das Feld gehoert\neinem anderen Spieler\n"; } if( !gr->get_crossing() ) { // If road and tram, we have to check both ribis. ribi = gr->get_weg_nr(0)->get_ribi_unmasked() | gr->get_weg_nr(1)->get_ribi_unmasked(); } else { // else only the straight ones ribi = gr->get_weg_ribi_unmasked(wt); } // same waytype, same direction, no stop or depot or any other stuff */ if( gr->get_weg(wt) && ribi_check(ribi, check_ribi) ) { // ok too return NULL; } return "A bridge must start on a way!"; } if( w->get_waytype() != wt ) { // now check for perpendicular and crossing if( (ribi_t::doubles(ribi) ^ ribi_t::doubles(check_ribi) ) == ribi_t::all && crossing_logic_t::get_crossing(wt, w->get_waytype(), 0, 0, welt->get_timeline_year_month() ) ) { return NULL; } return "A bridge must start on a way!"; } // same waytype, check direction if( w->get_waytype() == wt && ribi_check(ribi, check_ribi ) ) { // ok too return NULL; } // one or two non-matching ways where I cannot built a crossing return "A bridge must start on a way!"; } else if( wt == powerline_wt ) { if( leitung_t *lt = gr->get_leitung() ) { if( lt->get_removal_error(player) == NULL && ribi_check( lt->get_ribi(), check_ribi ) ) { // matching powerline return NULL; } return "A bridge must start on a way!"; } } // something here which we cannot remove => fail too if( obj_t *obj=gr->obj_bei(0) ) { if( const char *err_msg = obj->get_removal_error(player) ) { return err_msg; } } return ""; // could end here but need not end here } bool bridge_builder_t::is_blocked(koord3d pos, ribi_t::ribi check_ribi, const char *&error_msg) { /* can't build directly above or below a way if height clearance == 2 */ // take slopes on grounds below into accout const sint8 clearance = welt->get_settings().get_way_height_clearance()-1; for(int dz = -clearance -2; dz <= clearance; dz++) { grund_t *gr2 = NULL; if (dz != 0 && (gr2 = welt->lookup(pos + koord3d(0,0,dz)))) { const slope_t::type slope = gr2->get_weg_hang(); if (dz < -clearance) { if (dz + slope_t::max_diff(slope) + gr2->get_weg_yoff() / TILE_HEIGHT_STEP < -clearance ) { // too far below continue; } } if (dz + slope_t::max_diff(slope) == 0 && gr2->ist_karten_boden() && ribi_type(gr2->get_grund_hang())==check_ribi) { // potentially connect to this slope continue; } weg_t *w = gr2->get_weg_nr(0); if (w && w->get_max_speed() > 0) { error_msg = "Bridge blocked by way below or above."; return true; } } } // first check for elevated ground exactly in our height if( grund_t *gr2 = welt->lookup( pos ) ) { if( gr2->get_typ() != grund_t::boden && gr2->get_typ() != grund_t::tunnelboden ) { // no through bridges // monorail tiles will be checked in is_monorail_junction error_msg = "Tile not empty."; return true; } } return false; } // return true if bridge can be connected to monorail tile bool bridge_builder_t::is_monorail_junction(koord3d pos, player_t *player, const bridge_desc_t *desc, const char *&error_msg) { // first check for elevated ground exactly in our height if( grund_t *gr2 = welt->lookup( pos ) ) { if( gr2->get_typ() == grund_t::monorailboden ) { // now check if our way if( obj_t *o = gr2->obj_bei(0) ) { if( !player_t::check_owner(o->get_owner(),player) ) { // not our way error_msg = "Das Feld gehoert\neinem anderen Spieler\n"; return false; } if( o->get_waytype() == desc->get_waytype() ) { // ok, all fine return true; } } error_msg = "Tile not empty."; } } return false; } /** * checks if a bridge can start (or end) on this tile in priciple * @returns either an error_message (must abort bridge building) or NULL */ const char* bridge_builder_t::check_start_tile(const player_t* player, const grund_t* gr, ribi_t::ribi bridge_ribi, const bridge_desc_t* desc) { const char* err = "A bridge must start on a way!"; if (!gr) { return err; } if (gr->get_typ() != grund_t::boden && gr->get_typ() != grund_t::monorailboden) { return err; } // not overbuilt transformers bridges or tunnel if (obj_t* obj=gr->obj_bei(0)) { switch (obj->get_typ()) { case obj_t::bruecke: case obj_t::tunnel: case obj_t::pumpe: case obj_t::senke: return err; default: break; } } if (slope_t::type sl = gr->get_weg_hang()) { if (!slope_t::is_way(sl)) { // it's not a straight slope return "Bruecke muss an\neinfachem\nHang beginnen!\n"; } if (slope_t::max_diff(gr->get_weg_hang()) == 2 && !desc->has_double_start()) { // cannot handle double slope return "Bruecke muss an\neinfachem\nHang beginnen!\n"; } if (bridge_ribi && ribi_type(sl) != bridge_ribi) { // wrong slope return "Bruecke muss an\neinfachem\nHang beginnen!\n"; } } if (gr->is_halt() || gr->get_depot()) { // something in the way return "Tile not empty."; } if (const char* err = gr->kann_alle_obj_entfernen(player)) { // therse is something not from us blocking construction return err; } if (desc->get_waytype() == powerline_wt) { if (gr->hat_wege()) { // no pylons on roads please return "Tile not empty."; } if (leitung_t* lt = gr->get_leitung()) { ribi_t::ribi ribi = lt->get_ribi(); if (ribi_t::is_single(ribi)) { // matching powerline return NULL; } return "A bridge must start on a way!"; } } // we can build a ramp when there is one (or with tram two) way in our direction and no stations/depot etc. else if (gr->hat_wege() && gr->get_typ()!=grund_t::monorailboden) { weg_t* w = gr->get_weg(desc->get_waytype()); if (!w) { // another way type is blocking us return "A bridge must start on a way!"; } // now check for direction ribi_t::ribi ribi = w->get_ribi_unmasked(); if (gr->get_weg_nr(1)) { if (gr->get_crossing()) { // cannot start on a crossing return "A bridge must start on a way!"; } // we have to check both ribis. ribi = gr->get_weg_nr(0)->get_ribi_unmasked() | gr->get_weg_nr(1)->get_ribi_unmasked(); } if (ribi_t::is_single(ribi) || ribi_t::is_straight(ribi | bridge_ribi)) { // directions return NULL; } // one or two non-matching ways where I cannot built a crossing return "A bridge must start on a way!"; } // test scenario conditions if (const char* error_msg = welt->get_scenario()->is_work_allowed_here(player, TOOL_BUILD_BRIDGE | GENERAL_TOOL, desc->get_finance_waytype(), desc->get_name(), gr->get_pos())) { return error_msg; } return NULL; } // check the bridge span for clearance in the given height const char *bridge_builder_t::can_span_bridge(const player_t* player, koord3d start_pos, koord3d end_pos, sint8 height, const bridge_desc_t* desc) { sint16 length = koord_distance(end_pos,start_pos); const sint8 clearance = welt->get_settings().get_way_height_clearance(); if (length == 1 && end_pos.z == start_pos.z && height == 0) { return ""; // just two tiles next to each other on flat ground => no bridge needed! } // test max length if (desc->get_max_length() > 0 && length-1 > desc->get_max_length()) { return "Bridge is too long for this type!\n"; } if (start_pos.z < height) { // ramp on start tile needs clearance; also we are on ground then planquadrat_t* pl = welt->access(start_pos.x,start_pos.y); if (weg_t* w = pl->get_boden_in_hoehe(start_pos.z)->get_weg(desc->get_waytype())) { ribi_t::ribi r = w->get_ribi_unmasked(); if (!ribi_t::is_single(r) || ribi_t::is_bend(r|ribi_type(start_pos-end_pos)) ) { // not a single way here => cannot start return "A bridge must start on a way!"; } } for (sint8 z = start_pos.z + 1; z < height + clearance; z++) { if (pl->get_boden_in_hoehe(z)) { // something is the way return ""; } } } if (end_pos.z < height) { // ramp on end tile needs clearance; also we are on ground then planquadrat_t* pl = welt->access(end_pos.x, end_pos.y); if (weg_t* w = pl->get_boden_in_hoehe(end_pos.z)->get_weg(desc->get_waytype())) { ribi_t::ribi r = w->get_ribi_unmasked(); if (!ribi_t::is_single(r) || ribi_t::is_bend(r | ribi_type(start_pos - end_pos))) { // not a single way here => cannot end here return "A bridge must start on a way!"; } } for (sint8 z = end_pos.z + 1; z < height + clearance; z++) { if (pl->get_boden_in_hoehe(z)) { // something is the way return ""; } } } koord zv = (end_pos - start_pos).get_2d() / length; for (sint16 i = 1; i < length; i++) { koord pos = start_pos.get_2d() + zv * i; planquadrat_t* pl = welt->access(pos.x, pos.y); if (!pl) { // outside of map return ""; } // test scenario conditions if (const char* error_msg = welt->get_scenario()->is_work_allowed_here(player, TOOL_BUILD_BRIDGE | GENERAL_TOOL, desc->get_finance_waytype(), desc->get_name(), koord3d(pos,height))) { return error_msg; } for (int i = 0; i < clearance; i++) { if (pl->get_boden_in_hoehe(height + i)) { // something is the way return ""; } } if (pl->get_boden_in_hoehe(height)) { // something at this level => stop return ""; } // check for height grund_t* gr = pl->get_kartenboden(); if (desc->get_max_height() && height - gr->get_hoehe() > desc->get_max_height()) { return "bridge is too high for its type!"; } if (gr->hat_weg(air_wt) && gr->get_styp(air_wt) == type_runway) { return "No bridges over runways!"; } for (int h = -clearance - 2; h <= -1; h++) { // check for slope and way clearance if (grund_t* grcheck = pl->get_boden_in_hoehe(height + h)) { sint8 delta_h = slope_t::max_diff(gr->get_grund_hang()); if (grcheck->hat_wege()) { if (delta_h == 0) { delta_h += slope_t::max_diff(gr->get_weg_hang()); } delta_h += clearance-1; } if (h + delta_h >= 0) { // not enough clearance return ""; } } } } return NULL; } // checks if we can build a bridge here const char* bridge_builder_t::can_build_bridge(const player_t* pl, koord3d start_pos, koord3d end_pos, sint8 &height, const bridge_desc_t* desc) { const char* err = "A bridge must start on a way!"; minivec_tplheights; koord delta = end_pos.get_2d() - start_pos.get_2d(); if (delta.x && delta.y) { // no diagonal bridges return ""; } else if (delta.x+delta.y == 0) { // no bridge return ""; } delta = delta / abs(delta.x + delta.y); // normalised step difference ribi_t::ribi bridge_ribi = ribi_type(delta); grund_t* start = welt->lookup(start_pos); if (const char* error_msg = check_start_tile(pl, start, ribi_t::reverse_single(bridge_ribi), desc)) { return error_msg; } grund_t* end = welt->lookup(end_pos); if (const char* error_msg = check_start_tile(pl, end, bridge_ribi, desc)) { return error_msg; } // now the tiles are in priciple ok = find out the bridge height const sint8 max_ramp = 1 + desc->has_double_ramp(); // on flat tile const sint8 clearance = welt->get_settings().get_way_height_clearance(); if (max(start_pos.z, end_pos.z)-min(start_pos.z, end_pos.z) > max_ramp) { // too big height difference return err; } slope_t::type start_sl = start->get_grund_hang(); if (start->get_typ() == grund_t::monorailboden) { if (start->get_grund_hang()) { // no ramps on elevated ways ... return ""; } heights.append(start_pos.z); } else if (start_sl != slope_t::flat) { sint8 start_dz = slope_t::max_diff(start_sl); heights.append(start_pos.z + start_dz); // test this first } else { // start on flat tile => may need a ramp => need clearance sint8 max_z = max_ramp+clearance; while(welt->lookup(start_pos + koord3d(0, 0, max_z))) { if (max_z == clearance) { break; } max_z--; } max_z -= clearance; // maximum possible height for a ramp for (sint8 i = 0; i <= max_z; i++) { heights.append(start_pos.z+i); } } slope_t::type end_sl = end->get_grund_hang(); if (end->get_typ() == grund_t::monorailboden) { if (!heights.is_contained(end_pos.z)) { // not matching heights return ""; } heights.clear(); heights.append(end_pos.z); } else if (end_sl != slope_t::flat) { sint8 end_dz = slope_t::max_diff(end_sl); if(!heights.is_contained(end_pos.z + end_dz)) { // mismatched heights return ""; } heights.clear(); heights.append(end_pos.z + end_dz); } else { // start on flat tile => may need a ramp => need clearance sint8 max_z = max_ramp + clearance; while (welt->lookup(start_pos + koord3d(0, 0, max_z))) { if (max_z == clearance) { break; } max_z--; } max_z -= clearance; // maximum possible height for a ramp // end of flat tile => may need a ramp for (int i = heights.get_count()-1; i >= 0; i--) { if (heights[i] > end_pos.z+max_z || heights[i] < end_pos.z) { // not matching height heights.remove_at(i); } } } // if no heights are left => there is nothing to build for us ... if (heights.empty()) { return ""; } // as default, we fail construction const char* error_msg = ""; if (koord_distance(start_pos, end_pos) > 0) { // we always try to build the lowest bridge possible with the least slopes for (int i = 0; i < heights.get_count(); i++) { sint8 h = heights[i]; error_msg = can_span_bridge(pl, start_pos, end_pos, h, desc); if (!error_msg) { // found valid height height = h; return NULL; } } } return error_msg ? error_msg: ""; } // returns the shortest possible bridge with a length of 1 ... min_length const char *bridge_builder_t::find_end_pos(player_t* player, koord3d &pos, const koord zv, sint8& bridge_height, const bridge_desc_t* desc, uint32 min_length, uint32 max_length, bool also_flat_ends) { grund_t* start = welt->lookup(pos); if (check_start_tile(player, start, ribi_t::backward(ribi_type(zv)), desc)) { return ""; } const char* error = "Bridge is too long for this type!\n"; sint8 finish_height = pos.z + slope_t::max_diff(start->get_grund_hang()); // first find slope in my direction for (uint8 i = max(1,min_length); i <= max_length && i <= welt->get_settings().way_max_bridge_len; i++) { const grund_t* gr_end = welt->lookup_kartenboden(pos.get_2d() + zv * i); if (!gr_end) { // not on map any more max_length = i-1; // try shorter bridge then break; } if (gr_end->get_hoehe() + slope_t::max_diff(gr_end->get_grund_hang()) == finish_height) { // try to connect to a slope or flat tile ending here koord3d end = gr_end->get_pos(); error = bridge_builder_t::can_build_bridge(player, pos, end, bridge_height, desc); if (!error) { pos = end; return NULL; } max_length = i-1; // try shorter bridge then break; } } if(also_flat_ends) { // now try shorter bridge for (uint8 i = min_length; i <= max_length; i++) { const grund_t* gr_end = welt->lookup_kartenboden(pos.get_2d() + zv * i); // must succeed, we tested above koord3d end = gr_end->get_pos(); if (const char* err = bridge_builder_t::can_build_bridge(player, pos, end, bridge_height, desc)) { if (err && *err) { error = err; } } else { pos = end; return NULL; } } } return (error && *error) ? error : "Bridge is too long for this type!\n"; } bool bridge_builder_t::is_start_of_bridge( const grund_t *gr ) { if( gr->ist_bruecke() ) { if( gr->ist_karten_boden() ) { // ramp => true return true; } // now check for end of rampless bridges ribi_t::ribi ribi = gr->get_weg_ribi_unmasked( gr->get_leitung() ? powerline_wt : gr->get_weg_nr(0)->get_waytype() ); for( int i=0; i<4; i++ ) { if( ribi_t::nesw[i] & ribi ) { grund_t *to = welt->lookup( gr->get_pos()+koord(ribi_t::nesw[i]) ); if( to && !to->ist_bruecke() ) { return true; } } } } return false; } void bridge_builder_t::build_bridge(player_t *player, const koord3d start, const koord3d end, koord zv, sint8 bridge_height, const bridge_desc_t *desc, const way_desc_t *way_desc) { ribi_t::ribi ribi = ribi_type(zv); DBG_MESSAGE("bridge_builder_t::build()", "build from %s", start.get_str() ); grund_t *start_gr = welt->lookup( start ); const slope_t::type slope = start_gr->get_weg_hang(); if( slope!=slope_t::flat || bridge_height > start.z ) { // needs a ramp to start on ground build_ramp( player, start, ribi, slope ? 0 : slope_type(zv) * (bridge_height - start.z), desc ); if( desc->get_waytype() != powerline_wt ) { ribi = welt->lookup(start)->get_weg_ribi_unmasked(desc->get_waytype()); } } else { // start at cliff if( desc->get_waytype() == powerline_wt ) { leitung_t *lt = start_gr->get_leitung(); if(!lt) { lt = new leitung_t(start_gr->get_pos(), player); lt->set_desc( way_desc ); start_gr->obj_add( lt ); lt->finish_rd(); } } else if( !start_gr->weg_erweitern( desc->get_waytype(), ribi ) ) { // builds new way weg_t * const weg = weg_t::alloc( desc->get_waytype() ); weg->set_desc( way_desc ); player_t::book_construction_costs(player, -start_gr->neuen_weg_bauen(weg, ribi, player) - weg->get_desc()->get_price(), end.get_2d(), weg->get_waytype()); } start_gr->calc_image(); } koord3d pos = koord3d(start.get_2d()+zv, bridge_height); // update limits if( welt->min_height > pos.z ) { welt->min_height = pos.z; } else if( welt->max_height < pos.z ) { welt->max_height = pos.z; } while( pos.get_2d() != end.get_2d() ) { brueckenboden_t *bruecke = new brueckenboden_t( pos, slope_t::flat, slope_t::flat ); welt->access(pos.get_2d())->boden_hinzufuegen(bruecke); if( desc->get_waytype() != powerline_wt ) { weg_t * const weg = weg_t::alloc(desc->get_waytype()); weg->set_desc(way_desc); bruecke->neuen_weg_bauen(weg, ribi_t::doubles(ribi), 0); weg->set_owner(player); } else { leitung_t *lt = new leitung_t(bruecke->get_pos(), player); bruecke->obj_add( lt ); lt->finish_rd(); } grund_t *gr = welt->lookup_kartenboden(pos.get_2d()); sint16 height = pos.z - gr->get_pos().z; bruecke_t *br = new bruecke_t(bruecke->get_pos(), player, desc, desc->get_straight(ribi,height-slope_t::max_diff(gr->get_grund_hang()))); bruecke->obj_add(br); bruecke->calc_image(); br->finish_rd(); //DBG_MESSAGE("bool bridge_builder_t::build_bridge()","at (%i,%i)",pos.x,pos.y); if(desc->get_pillar()>0) { // make a new pillar here if(desc->get_pillar()==1 || (pos.x*zv.x+pos.y*zv.y)%desc->get_pillar()==0) { //DBG_MESSAGE("bool bridge_builder_t::build_bridge()","h1=%i, h2=%i",pos.z,gr->get_pos().z); while(height-->0) { if( TILE_HEIGHT_STEP*height <= 127) { // eventual more than one part needed, if it is too high ... gr->obj_add( new pillar_t(gr->get_pos(),player,desc,desc->get_pillar(ribi), TILE_HEIGHT_STEP*height) ); } } } } pos = pos + zv; } // must determine end tile: on a slope => likely need auffahrt grund_t* gr = welt->lookup(end); if(bridge_height > end.z) { // ending at different height => needs ramp build_ramp(player, end, ribi_type(-zv), gr->get_weg_hang()?0:slope_type(-zv)*(bridge_height-end.z), desc); } else { // ending on a flat cliff or elevated way if(desc->get_waytype() != powerline_wt) { // just connect to existing way ribi = ribi_t::backward( ribi_type(zv) ); if( !gr->weg_erweitern( desc->get_waytype(), ribi ) ) { // builds new way weg_t * const weg = weg_t::alloc( desc->get_waytype() ); weg->set_desc( way_desc ); player_t::book_construction_costs(player, -gr->neuen_weg_bauen(weg, ribi, player) - weg->get_desc()->get_price(), end.get_2d(), weg->get_waytype()); } gr->calc_image(); } else { leitung_t *lt = gr->get_leitung(); if( lt==NULL ) { lt = new leitung_t(end, player ); player_t::book_construction_costs(player, -way_desc->get_price(), gr->get_pos().get_2d(), powerline_wt); gr->obj_add(lt); lt->set_desc(way_desc); lt->finish_rd(); } lt->calc_neighbourhood(); } } // if start or end are single way, and next tile is not, try to connect if (desc->get_waytype() != powerline_wt && player) { koord3d endtiles[] = { start, end }; for (uint i = 0; i < lengthof(endtiles); i++) { koord3d pos = endtiles[i]; if (grund_t* gr = welt->lookup(pos)) { ribi_t::ribi ribi = gr->get_weg_ribi_unmasked(desc->get_waytype()); grund_t* to = NULL; if (ribi_t::is_single(ribi) && gr->get_neighbour(to, invalid_wt, ribi_t::backward(ribi))) { // connect to open sea, calc_image will recompute ribi at to. if (desc->get_waytype() == water_wt && to->is_water()) { gr->weg_erweitern(water_wt, ribi_t::backward(ribi)); to->calc_image(); continue; } // only single tile under bridge => try to connect to next tile way_builder_t bauigel(player); bauigel.set_keep_existing_ways(true); bauigel.set_keep_city_roads(true); bauigel.set_maximum(20); bauigel.init_builder((way_builder_t::bautyp_t)desc->get_waytype(), way_desc, NULL, NULL); bauigel.calc_route(pos, to->get_pos()); if (bauigel.get_count() == 2) { bauigel.build(); } } } } } } void bridge_builder_t::build_ramp(player_t* player, koord3d end, ribi_t::ribi ribi_neu, const slope_t::type weg_hang, const bridge_desc_t* desc) { assert(weg_hang <= slope_t::max_number); grund_t* alter_boden = welt->lookup(end); brueckenboden_t* bruecke; slope_t::type grund_hang = alter_boden->get_grund_hang(); bridge_desc_t::img_t img; bruecke = new brueckenboden_t(end, grund_hang, weg_hang); // add the ramp img = desc->get_end(bruecke->get_grund_hang(), grund_hang, weg_hang); weg_t* weg = NULL; if (desc->get_waytype() != powerline_wt) { weg = alter_boden->get_weg(desc->get_waytype()); } // take care of everything on that tile ... bruecke->take_obj_from(alter_boden); welt->access(end.get_2d())->kartenboden_setzen(bruecke); if (desc->get_waytype() != powerline_wt) { if (!bruecke->weg_erweitern(desc->get_waytype(), ribi_neu)) { // needs still one weg = weg_t::alloc(desc->get_waytype()); bruecke->neuen_weg_bauen(weg, ribi_neu, 0); weg->set_owner(player); } weg->set_max_speed(desc->get_topspeed()); } else { leitung_t* lt = bruecke->get_leitung(); if (!lt) { lt = new leitung_t(bruecke->get_pos(), player); bruecke->obj_add(lt); } else { // remove maintenance - it will be added in leitung_t::finish_rd player_t::add_maintenance(player, -lt->get_desc()->get_maintenance(), powerline_wt); } // connect to neighbor tiles and networks, add maintenance lt->finish_rd(); } bruecke_t* br = new bruecke_t(end, player, desc, img); bruecke->obj_add(br); br->finish_rd(); bruecke->calc_image(); } const char* bridge_builder_t::renovate(player_t* player, koord3d pos_start, waytype_t wegtyp, const bridge_desc_t* desc) { if (wegtyp != desc->get_waytype()) { return ""; } slist_tpl part_list; slist_tpl end_list; grund_t* start = welt->lookup(pos_start); if (!start) { // no ground??? return "A bridge must start on a way!"; } if (!start->find()) { // need to click on a bridge return "A bridge must start on a way!"; } waytype_t check_wegtyp = wegtyp == powerline_wt ? invalid_wt : wegtyp; if (start->ist_karten_boden()) { end_list.append(start); } else { part_list.append(start); } if (!player->is_public_service()) { bruecke_t* br = start->find(); if (br->get_owner_nr() != player->get_player_nr()) { // since bridges are fully owned by one player, check only one tile return "Das Feld gehoert\neinem anderen Spieler\n"; } if (br->get_desc() == desc) { // same bridge already ... return NULL; } } // one direction grund_t* from = start; while (1) { ribi_t::ribi r = wegtyp == powerline_wt ? from->get_leitung()->get_ribi() : from->get_weg_nr(0)->get_ribi_unmasked(); ribi_t::ribi dir = r & ribi_t::northeast; grund_t* to; if (from->get_neighbour(to, check_wegtyp, dir) && to->ist_bruecke()) { if (to->ist_karten_boden()) { end_list.append(to); break; } else { part_list.append(to); } from = to; } else { break; } } // other direction from = start; while (1) { ribi_t::ribi r = wegtyp == powerline_wt ? from->get_leitung()->get_ribi() : from->get_weg_nr(0)->get_ribi_unmasked(); ribi_t::ribi dir = r & ribi_t::southwest; grund_t* to; if (from->get_neighbour(to, check_wegtyp, dir) && to->ist_bruecke()) { if (to->ist_karten_boden()) { end_list.append(to); break; } else { part_list.insert(part_list.begin(), to); } from = to; } else { break; } } // Check whether we can replace the ends for (grund_t *& gr : end_list) { const slope_t::type slope = gr->get_grund_hang(); if (desc->get_end(slope, slope, gr->get_weg_hang()) == (bridge_desc_t::img_t)IMG_EMPTY) { // wrong starting slope return "bridge is too high for its type!"; } } // Check whether the bridge would be too long if (desc->get_max_length() > 0 && part_list.get_count() > desc->get_max_length()) { return "Bridge is too long for this type!\n"; } // Check whether the bridge would be too tall if (desc->get_max_height() > 0) { for (grund_t *& bg : part_list) { // check for height grund_t* gr = welt->lookup_kartenboden(bg->get_pos().get_2d()); if (gr && bg->get_hoehe() - gr->get_hoehe() > desc->get_max_height()) { return "bridge is too high for its type!"; } } } part_list.append_list(end_list); const way_desc_t* way_desc = way_builder_t::weg_search(desc->get_waytype(), desc->get_topspeed(), welt->get_timeline_year_month(), type_flat); for(grund_t *&gr : part_list) { bruecke_t *br = gr->find(); const bridge_desc_t *br_desc = br->get_desc(); if (wegtyp != powerline_wt) { weg_t* weg = gr->get_weg(wegtyp); weg->set_owner(player); weg->set_desc(way_desc); weg->set_max_speed(desc->get_topspeed()); // respect max speed of catenary wayobj_t const* const wo = gr->get_wayobj(desc->get_wtyp()); if (wo && wo->get_desc()->get_topspeed() < desc->get_topspeed()) { weg->set_max_speed(wo->get_desc()->get_topspeed()); } } player_t::add_maintenance(br->get_owner(), -br_desc->get_maintenance(), desc->get_finance_waytype()); br->set_owner(player); br->set_desc(desc); player_t::add_maintenance(player, desc->get_maintenance(), desc->get_finance_waytype()); player_t::book_construction_costs(player, -desc->get_price(), br->get_pos().get_2d(), desc->get_waytype()); gr->calc_image(); // remove old pillars grund_t* gr_bottom = welt->lookup_kartenboden(gr->get_pos().get_2d()); while (obj_t* const p = gr_bottom->find()) { p->cleanup(p->get_owner()); delete p; } // Now for new pillars sint16 height = gr->get_hoehe() - gr_bottom->get_pos().z; ribi_t::ribi ribi = gr->get_weg_ribi_unmasked(wegtyp); if (desc->get_pillar() > 0 && !gr->ist_karten_boden()) { koord zv((ribi_t::ribi)(ribi & ribi_t::northeast)); // make a new pillar here koord3d pos = gr->get_pos(); if (desc->get_pillar() == 1 || (pos.x * zv.x + pos.y * zv.y) % desc->get_pillar() == 0) { //DBG_MESSAGE("bool bridge_builder_t::renovate()","h1=%i, h2=%i",pos.z,gr->get_pos().z); while (height-- > 0) { if (TILE_HEIGHT_STEP * height <= 127) { // eventual more than one part needed, if it is too high ... gr_bottom->obj_add(new pillar_t(gr_bottom->get_pos(), player, desc, desc->get_pillar(ribi), TILE_HEIGHT_STEP * height)); } } } } } welt->set_dirty(); return NULL; } const char *bridge_builder_t::remove(player_t *player, koord3d pos_start, waytype_t wegtyp) { marker_t& marker = marker_t::instance(welt->get_size().x, welt->get_size().y); slist_tpl end_list; slist_tpl part_list; slist_tpl tmp_list; const char *msg; tmp_list.insert(pos_start); marker.mark(welt->lookup(pos_start)); waytype_t delete_wegtyp = wegtyp==powerline_wt ? invalid_wt : wegtyp; koord3d pos; do { pos = tmp_list.remove_first(); // weg_position_t changed to grund_t::get_neighbour() grund_t *from = welt->lookup(pos); grund_t *to; koord zv = koord::invalid; if( pos != pos_start && from->ist_karten_boden() ) { // gr is start/end - test only one direction if( from->get_grund_hang() != slope_t::flat ) { zv = -koord(from->get_grund_hang()); } else if( from->get_weg_hang() != slope_t::flat ) { zv = koord(from->get_weg_hang()); } end_list.insert(pos); } else if( pos == pos_start ) { if( from->ist_karten_boden() ) { // gr is start/end - test only one direction if( from->get_grund_hang() != slope_t::flat ) { zv = -koord(from->get_grund_hang()); } else if( from->get_weg_hang() != slope_t::flat ) { zv = koord(from->get_weg_hang()); } end_list.insert(pos); } else { ribi_t::ribi r = wegtyp==powerline_wt ? from->get_leitung()->get_ribi() : from->get_weg_nr(0)->get_ribi_unmasked(); ribi_t::ribi dir1 = r & ribi_t::northeast; ribi_t::ribi dir2 = r & ribi_t::southwest; grund_t *to; // test if we are at the end of a bridge: // 1. test direction must be single or zero bool is_end1 = (dir1 == ribi_t::none); // 2. if single direction: test for neighbor in that direction // there must be no neighbor or no bridge if (ribi_t::is_single(dir1)) { is_end1 = !from->get_neighbour(to, delete_wegtyp, dir1) || !to->ist_bruecke(); } // now do the same for the reverse direction bool is_end2 = (dir2 == ribi_t::none); if (ribi_t::is_single(dir2)) { is_end2 = !from->get_neighbour(to, delete_wegtyp, dir2) || !to->ist_bruecke(); } if(!is_end1 && !is_end2) { return "Cannot delete a bridge from its centre"; } // if one is an end then go towards the other direction zv = koord(is_end1 ? dir2 : dir1); part_list.insert(pos); } } else if(from->ist_bruecke()) { part_list.insert(pos); } // can we delete everything there? msg = from->kann_alle_obj_entfernen(player); if(msg != NULL || (from->get_halt().is_bound() && from->get_halt()->get_owner()!=player)) { simachievements_t::set_achievement(ACH_TOOL_REMOVE_BUSY_BRIDGE); return "Die Bruecke ist nicht frei!\n"; } // search neighbors for(int r = 0; r < 4; r++) { if( (zv == koord::invalid || zv == koord::nesw[r]) && from->get_neighbour(to, delete_wegtyp, ribi_t::nesw[r]) && !marker.is_marked(to) && to->ist_bruecke() ) { if( wegtyp != powerline_wt || (to->find() && to->find()->get_desc()->get_waytype() == powerline_wt) ) { tmp_list.insert(to->get_pos()); marker.mark(to); } } } } while (!tmp_list.empty()); // now delete the bridge bool first = true; while (!part_list.empty()) { pos = part_list.remove_first(); grund_t *gr = welt->lookup(pos); if (weg_t* w = gr->get_weg_nr(0)) { w->set_owner(NULL); // no cost for ways on bridges } // the following code will check if this is the first of last tile, end then tries to remove the superflous ribis from it if( first || end_list.empty() ) { // starts on slope or elevated way, or it consist only of the ramp ribi_t::ribi bridge_ribi = gr->get_weg_ribi_unmasked( wegtyp ); for( uint i = 0; i < 4; i++ ) { if( bridge_ribi & ribi_t::nesw[i] ) { grund_t *prev; // if we have a ramp, then only check the higher end! if( gr->get_neighbour( prev, wegtyp, ribi_t::nesw[i]) && !prev->ist_bruecke() && !end_list.is_contained(prev->get_pos()) ) { if( weg_t *w = prev->get_weg( wegtyp ) ) { // now remove ribi (or full way) w->set_ribi( (~ribi_t::backward( ribi_t::nesw[i] )) & w->get_ribi_unmasked() ); if( w->get_ribi_unmasked() == 0 ) { // nowthing left => then remove completel prev->remove_everything_from_way( player, wegtyp, bridge_ribi ); // removes stop and signals correctly prev->weg_entfernen( wegtyp, true ); if( prev->get_typ() == grund_t::monorailboden ) { welt->access( prev->get_pos().get_2d() )->boden_entfernen( prev ); delete prev; } } else { w->calc_image(); } } } } } } first = false; // first: remove bridge bruecke_t *br = gr->find(); br->cleanup(player); delete br; gr->remove_everything_from_way(player,wegtyp,ribi_t::none); // removes stop and signals correctly // remove also the second way on the bridge if(gr->get_weg_nr(0)!=0) { gr->remove_everything_from_way(player,gr->get_weg_nr(0)->get_waytype(),ribi_t::none); } // we may have a second way/powerline here ... gr->obj_loesche_alle(player); gr->mark_image_dirty(); welt->access(pos.get_2d())->boden_entfernen(gr); delete gr; // finally delete all pillars (if there) gr = welt->lookup_kartenboden(pos.get_2d()); while (obj_t* const p = gr->find()) { p->cleanup(p->get_owner()); delete p; } // refresh map minimap_t::get_instance()->calc_map_pixel(pos.get_2d()); } // finally delete the bridge ends (all are kartenboden) while( !end_list.empty() ) { pos = end_list.remove_first(); grund_t *gr = welt->lookup(pos); if (weg_t* w = gr->get_weg_nr(0)) { w->set_owner(NULL); // no cost for ways on bridges } // starts on slope or elevated way, or it consist only of the ramp ribi_t::ribi bridge_ribi = gr->get_weg_ribi_unmasked( wegtyp ); for( uint i = 0; i < 4; i++ ) { if( bridge_ribi & ribi_t::nesw[i] ) { grund_t *prev; // if we have a ramp, then only check the higher end! if( gr->get_neighbour( prev, wegtyp, ribi_t::nesw[i]) && prev->get_hoehe() > gr->get_hoehe() ) { if( weg_t *w = prev->get_weg( wegtyp ) ) { // now remove ribi (or full way) w->set_ribi( (~ribi_t::backward( ribi_t::nesw[i] )) & w->get_ribi_unmasked() ); if( w->get_ribi_unmasked() == 0 ) { // nothing left => then remove completly prev->remove_everything_from_way( player, wegtyp, bridge_ribi ); // removes stop and signals correctly prev->weg_entfernen( wegtyp, true ); if( prev->get_typ() == grund_t::monorailboden ) { welt->access( prev->get_pos().get_2d() )->boden_entfernen( prev ); delete prev; } else if( prev->get_typ() == grund_t::tunnelboden && !prev->hat_wege() ) { grund_t* gr_new = new boden_t(prev->get_pos(), prev->get_grund_hang()); welt->access(prev->get_pos().get_2d())->kartenboden_setzen(gr_new); } } else { w->calc_image(); } } } } } if(wegtyp==powerline_wt) { while (obj_t* const br = gr->find()) { br->cleanup(player); delete br; } leitung_t *lt = gr->get_leitung(); if (lt) { player_t *old_owner = lt->get_owner(); // first delete powerline to decouple from the bridge powernet lt->cleanup(old_owner); delete lt; // .. now create powerline to create new powernet lt = new leitung_t(gr->get_pos(), old_owner); lt->finish_rd(); gr->obj_add(lt); } } else { ribi_t::ribi ribi = gr->get_weg_ribi_unmasked(wegtyp); ribi_t::ribi bridge_ribi; if(gr->get_weg_hang() != slope_t::flat) { bridge_ribi = ~ribi_t::backward(ribi_type(gr->get_weg_hang())); } else { bridge_ribi = ~ribi_type(gr->get_weg_hang()); } bridge_ribi &= ~(ribi_t::backward(~bridge_ribi)); ribi &= bridge_ribi; bruecke_t *br = gr->find(); br->cleanup(player); delete br; // stops on flag bridges ends with road + track on it // are not correctly deleted if ways are kept if (gr->is_halt()) { haltestelle_t::remove(player, gr->get_pos()); } // depots at bridge ends needs to be deleted as well if (depot_t *dep = gr->get_depot()) { dep->cleanup(player); delete dep; } // removes single signals, bridge head, pedestrians, stops, changes catenary etc weg_t *weg=gr->get_weg_nr(1); if(weg) { gr->remove_everything_from_way(player,weg->get_waytype(),bridge_ribi); } gr->remove_everything_from_way(player,wegtyp,bridge_ribi); // removes stop and signals correctly // corrects the ways weg = gr->get_weg(wegtyp); if( weg ) { // needs checks, since this fails if it was the last tile weg->set_desc( weg->get_desc() ); weg->set_ribi( ribi ); if( slope_t::max_diff(gr->get_weg_hang())>=2 && !weg->get_desc()->has_double_slopes() ) { // remove the way totally, if is is on a double slope gr->weg_entfernen( weg->get_waytype(), true ); } } } // then add the new ground, copy everything and replace the old one grund_t *gr_new = new boden_t(pos, gr->get_grund_hang()); gr_new->take_obj_from( gr ); welt->access(pos.get_2d())->kartenboden_setzen( gr_new ); if( wegtyp == powerline_wt ) { gr_new->get_leitung()->calc_neighbourhood(); // Recalc the image. calc_image() doesn't do the right job... } } welt->set_dirty(); return NULL; } simutrans-124.5/src/simutrans/builder/brueckenbauer.h000066400000000000000000000136031520675460500230170ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef BUILDER_BRUECKENBAUER_H #define BUILDER_BRUECKENBAUER_H #include "../simtypes.h" #include "../dataobj/koord.h" #include "../dataobj/koord3d.h" #include "../tpl/slist_tpl.h" class bridge_desc_t; class grund_t; class karte_ptr_t; class player_t; class way_desc_t; class tool_selector_t; /** * Bridge management: builds bridges, manages list of available bridge types * Bridges should not be built directly but always by calling bridge_builder_t::build(). */ class bridge_builder_t { private: bridge_builder_t() {} ///< private -> no instance please static karte_ptr_t welt; // allowed owner? static bool check_owner(const player_t *owner, const player_t *test); static bool is_blocked(koord3d pos, ribi_t::ribi check_ribi, const char *&error_msg); static bool is_monorail_junction(koord3d pos, player_t *player, const bridge_desc_t *desc, const char *&error_msg); public: /** * checks if a bridge can start (or end) on this tile in priciple * @return NULL on success or an error message */ static const char* check_start_tile(const player_t* player, const grund_t* gr, ribi_t::ribi bridge_ribi, const bridge_desc_t* desc); /** * check the bridge span for clearance in the given height * @return NULL on success or an error message */ static const char* can_span_bridge(const player_t* player, koord3d start_pos, koord3d end_pos, sint8 height, const bridge_desc_t* desc); /** * checks if we can build a bridge between start and end positions * @return NULL on success or an error message * on success h contains the height of the bridge */ static const char* can_build_bridge(const player_t* pl, koord3d start_pos, koord3d end_pos, sint8& height, const bridge_desc_t* desc); /** * Finds the position of the end of the bridge. Does all kind of checks. * Uses two methods: * -# If ai_bridge==false then looks for end location at a sloped tile. * -# If ai_bridge==true returns the first location (taking min_length into account) * for the bridge end (including flat tiles). * @param player active player, needed to check scenario conditions * @param pos the position of the start of the bridge * @param zv desired direction of the bridge * @param desc the description of the bridge * @param error_msg an error message when the search fails. * @param bridge_height on success, the height of the bridge that we can build * @param ai_bridge if this bridge is being built by an AI * @param min_length the minimum length of the bridge. * @return if NULL then the position of the other end of the bridge in pos. Otherwise error is the reason of failure */ static const char *find_end_pos(player_t *player, koord3d &pos, const koord zv, sint8 &bridge_height, const bridge_desc_t* desc, uint32 min_length, uint32 max_length, bool also_flat_ends); /** * Checks if a bridge starts on @p gr also for rampless bridges * * @param gr the ground to check. * @return true, if bridge ends/starts here */ static bool is_start_of_bridge( const grund_t *gr ); /** * Build a bridge ramp. * * @param player the player wanting to build the bridge * @param end the position of the ramp * @param ribi_neu * @param weg_hang * @param desc the bridge description. */ static void build_ramp(player_t *player, koord3d end, ribi_t::ribi ribi_neu, const slope_t::type weg_hang, const bridge_desc_t *desc); /** * Actually builds the bridge without checks. * Therefore checks should be done before in * bridge_builder_t::build(). * * @param player the master builder of the bridge. * @param start start position. * @param end end position * @param zv direction the bridge will face * @param bridge_height the height above start.z that the bridge will have * @param desc bridge description. * @param way_desc description of the way to be built on the bridge */ static void build_bridge(player_t *player, const koord3d start, const koord3d end, koord zv, sint8 bridge_height, const bridge_desc_t *desc, const way_desc_t *way_desc); /** * Registers a new bridge type and adds it to the list of build tools. * * @param desc Description of the bridge to register. */ static void register_desc(bridge_desc_t *desc); /** * Method to retrieve bridge descriptor * @param name name of the bridge * @return bridge descriptor or NULL if not found */ static const bridge_desc_t *get_desc(const char *name); /** * Renovates a bridge * @param player the renovator and owner of the bridge * @param pos position anywhere on a bridge * @param wegtyp way type of the bridge * @param desc descriptor for the new bridge * @return An error message if the bridge could not be renovated, NULL otherwise */ static const char *renovate(player_t *player, koord3d pos, waytype_t wegtyp, const bridge_desc_t *desc); /** * Removes a bridge * @param player the demolisher and owner of the bridge * @param pos position anywhere on a bridge * @param wegtyp way type of the bridge * @return An error message if the bridge could not be removed, NULL otherwise */ static const char *remove(player_t *player, koord3d pos, waytype_t wegtyp); /** * Find a matching bridge. * @param wtyp way type * @param min_speed desired lower bound for speed limit * @param time current in-game time * @return bridge descriptor or NULL */ static const bridge_desc_t *find_bridge(const waytype_t wtyp, const sint32 min_speed,const uint16 time); /** * Fill menu with icons for all ways of the given waytype * * @param tool_selector gui object of the toolbar * @param wtyp way type * @param sound_ok */ static void fill_menu(tool_selector_t *tool_selector, const waytype_t wtyp, sint16 sound_ok); /** * Returns a list with available bridge types. */ static const vector_tpl& get_available_bridges(const waytype_t wtyp); }; #endif simutrans-124.5/src/simutrans/builder/fabrikbauer.cc000066400000000000000000001154241520675460500226210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "fabrikbauer.h" #include "hausbauer.h" #include "../world/simworld.h" #include "../simintr.h" #include "../simfab.h" #include "../simmesg.h" #include "../utils/simrandom.h" #include "../world/simcity.h" #include "../simhalt.h" #include "../player/simplay.h" #include "../ground/grund.h" #include "../dataobj/settings.h" #include "../dataobj/environment.h" #include "../network/pakset_info.h" #include "../dataobj/translator.h" #include "../tpl/vector_tpl.h" #include "../tpl/array_tpl.h" #include "../world/building_placefinder.h" #include "../utils/cbuffer.h" #include "../descriptor/objversion.h" #include "../gui/minimap.h" // to update map after construction of new industry karte_ptr_t factory_builder_t::welt; /// Default factory spacing static int DISTANCE = 40; /// all factories and their exclusion areas static array_tpl fab_map; /// width of the factory map static sint32 fab_map_w=0; /** * Marks factories and their exclusion region in the position map. */ static void add_factory_to_fab_map(karte_t const* const welt, fabrik_t const* const fab) { koord3d const& pos = fab->get_pos(); sint16 const spacing = welt->get_settings().get_min_factory_spacing(); building_desc_t const& bdsc = *fab->get_desc()->get_building(); uint8 const rotate = fab->get_rotate(); sint16 const start_y = max(0, pos.y - spacing); sint16 const start_x = max(0, pos.x - spacing); sint16 const end_y = min(welt->get_size().y - 1, pos.y + bdsc.get_y(rotate) + spacing); sint16 const end_x = min(welt->get_size().x - 1, pos.x + bdsc.get_x(rotate) + spacing); for (sint16 y = start_y; y < end_y; ++y) { for (sint16 x = start_x; x < end_x; ++x) { fab_map[fab_map_w * y + x / 8] |= 1 << (x % 8); } } } /** * Creates map with all factories and exclusion area. */ void init_fab_map( karte_t *welt ) { fab_map_w = ((welt->get_size().x+7)/8); fab_map.resize( fab_map_w*welt->get_size().y ); for( int i=0; iget_size().y; i++ ) { fab_map[i] = 0; } for(fabrik_t* const f : welt->get_fab_list()) { add_factory_to_fab_map(welt, f); } if( welt->get_settings().get_max_factory_spacing_percent() > 0 ) { DISTANCE = (welt->get_size_max() * welt->get_settings().get_max_factory_spacing_percent()) / 100l; } else { DISTANCE = welt->get_settings().get_max_factory_spacing(); } } /** * @param x,y world position, needs to be valid coordinates * @returns true, if factory coordinate */ inline bool is_factory_at(koord p) { uint32 idx = (fab_map_w*p.y)+(p.x/8); return idx < fab_map.get_count() && (fab_map[idx]&(1<<(p.x%8)))!=0; } void factory_builder_t::new_world() { init_fab_map( welt ); } /** * Searches for a suitable building site using suche_platz(). * The site is chosen so that there is a street next to the building site. */ class factory_site_searcher_t: public building_placefinder_t { factory_desc_t::site_t site; public: factory_site_searcher_t(karte_t* welt, factory_desc_t::site_t site_) : building_placefinder_t(welt), site(site_) {} bool is_area_ok(koord pos, sint16 w, sint16 h, climate_bits cl) const OVERRIDE { // do not build too close to an existing factory if (is_factory_at(pos) || is_factory_at(pos+koord(w,h))) { return false; } if( !building_placefinder_t::is_area_ok(pos, w, h, cl) ) { return false; } uint16 mincond = 0; uint16 condmet = 0; switch(site) { case factory_desc_t::forest: mincond = w*h+w+h; // at least w*h+w+h trees, i.e. few tiles with more than one tree break; case factory_desc_t::shore: case factory_desc_t::river: case factory_desc_t::City: default: mincond = 1; } // needs to run one tile wider than the factory on all sides for (sint16 y = -1; y < h; y++) { for (sint16 x = -1; x < w; x++) { koord k(pos + koord(x,y)); grund_t *gr = welt->lookup_kartenboden(k); if (!gr) { return false; } if( 0 <= x && x < w && 0 <= y && y < h ) { // actual factory tile: is there something top like elevated monorails? if( gr->get_leitung()!=NULL || welt->lookup(gr->get_pos()+koord3d(0,0,1) )!=NULL) { // something on top (monorail or power lines) return false; } // check for trees unless the map was generated without trees if( site==factory_desc_t::forest && welt->get_settings().get_tree_distribution()!=settings_t::TREE_DIST_NONE && condmet < mincond ) { for(uint8 i=0; i< gr->obj_count(); i++) { if (gr->obj_bei(i)->get_typ() == obj_t::baum) { condmet++; } } } } else { // border tile: check for road, shore, river if( condmet < mincond && (-1==x || x==w) && (-1==y || y==h) ) { switch (site) { case factory_desc_t::City: condmet += gr->hat_weg(road_wt); break; case factory_desc_t::shore: condmet += welt->get_climate(k)==water_climate; break; case factory_desc_t::river: if( welt->get_settings().get_river_number() >0 ) { weg_t* river = gr->get_weg(water_wt); if (river && river->get_desc()->get_styp()==type_river) { condmet++; DBG_DEBUG("factory_site_searcher_t::is_area_ok()", "Found river near %s", pos.get_str()); } break; } else { // always succeeds on maps without river ... condmet++; break; } default: ; } } } } } if( site==factory_desc_t::forest && condmet>=3 ) { dbg->message("", "found %d at %s", condmet, pos.get_str() ); } return condmet >= mincond; } }; stringhashtable_tpl factory_builder_t::desc_table; /** * Compares factory descriptors by their name. * Used in get_random_consumer(). * @return true, if @p a < @p b, false otherwise. */ static bool compare_fabrik_desc(const factory_desc_t* a, const factory_desc_t* b) { const int diff = strcmp( a->get_name(), b->get_name() ); return diff < 0; } // returns a random consumer const factory_desc_t *factory_builder_t::get_random_consumer(bool electric, climate_bits cl, uint16 timeline ) { // get a random city factory weighted_vector_tpl consumer; for(auto const& i : desc_table) { factory_desc_t const* const current = i.value; // only insert end consumers if ( current->is_consumer_only() && current->get_building()->is_allowed_climate_bits(cl) && (electric ^ !current->is_electricity_producer()) && current->get_building()->is_available(timeline) ) { consumer.insert_unique_ordered(current, current->get_distribution_weight(), compare_fabrik_desc); } } // no consumer installed? if (consumer.empty()) { DBG_MESSAGE("factory_builder_t::get_random_consumer()","No suitable consumer found"); return NULL; } // now find a random one factory_desc_t const* const fd = pick_any_weighted(consumer); DBG_MESSAGE("factory_builder_t::get_random_consumer()", "consumer %s found.", fd->get_name()); return fd; } const factory_desc_t *factory_builder_t::get_desc(const char *factory_name) { return desc_table.get(factory_name); } void factory_builder_t::register_desc(factory_desc_t *desc) { uint16 p=desc->get_productivity(); if(p&0x8000) { koord k=desc->get_building()->get_size(); // to be compatible with old factories, since new code only steps once per factory, not per tile desc->set_productivity( (p&0x7FFF)*k.x*k.y ); DBG_DEBUG("factory_builder_t::register_desc()","Correction for old factory: Increase production from %i by %i",p&0x7FFF,k.x*k.y); } if( const factory_desc_t *old_desc = desc_table.remove(desc->get_name()) ) { delete old_desc; } desc_table.put(desc->get_name(), desc); } bool factory_builder_t::successfully_loaded() { for(auto const& i : desc_table) { factory_desc_t const* const current = i.value; if( field_group_desc_t * fg = const_cast(current->get_field_group()) ) { // initialize weighted vector for the field class indices fg->init_field_class_indices(); } // check for crossconnects for( int j=0; j < current->get_supplier_count(); j++ ) { weighted_vector_tplproducer; find_producer( producer, current->get_supplier(j)->get_input_type(), 0 ); if( producer.is_contained(current) ) { // must not happen else dbg->fatal( "factory_builder_t::successfully_loaded()", "Factory %s output %s cannot be its own input!", i.key, current->get_supplier(j)->get_input_type()->get_name() ); } } checksum_t *chk = new checksum_t(); current->calc_checksum(chk); pakset_info_t::append(current->get_name(), "factory", chk); } return true; } int factory_builder_t::count_producers(const goods_desc_t *ware, uint16 timeline) { int count=0; // iterate over all factories and check if they produce this good... for(auto const& t : desc_table) { factory_desc_t const* const tmp = t.value; for (uint i = 0; i < tmp->get_product_count(); i++) { const factory_product_desc_t *product = tmp->get_product(i); if( product->get_output_type()==ware && tmp->get_distribution_weight()>0 && tmp->get_building()->is_available(timeline) ) { count++; } } } DBG_MESSAGE("factory_builder_t::count_producers()","%i producer for good '%s' found.", count, translator::translate(ware->get_name())); return count; } /* * Finds a random producer producing @p ware. * @param timeline the current time(months) */ void factory_builder_t::find_producer(weighted_vector_tpl &producer, const goods_desc_t *ware, uint16 timeline ) { // find all producers producer.clear(); for(auto const& t : desc_table) { factory_desc_t const* const tmp = t.value; if ( tmp->get_distribution_weight()>0 && tmp->get_building()->is_available(timeline) ) { for( uint i=0; iget_product_count(); i++ ) { const factory_product_desc_t *product = tmp->get_product(i); if( product->get_output_type()==ware ) { producer.insert_unique_ordered(tmp, tmp->get_distribution_weight(), compare_fabrik_desc); break; } } } } // no producer installed? if( producer.empty() ) { dbg->error("factory_builder_t::find_producer()", "no producer for good '%s' was found.", translator::translate(ware->get_name())); } DBG_MESSAGE("factory_builder_t::find_producer()", "%i producer for good '%s' found.", producer.get_count(), translator::translate(ware->get_name()) ); } bool factory_builder_t::check_construction_site(koord pos, koord size, factory_desc_t::site_t site, bool is_factory, climate_bits cl) { // do not build too close or on an existing factory if (!welt->is_within_limits(pos) || is_factory_at(pos) || is_factory_at(pos+size)) { return false; } // check for water (no shore in sight!) if(site==factory_desc_t::Water) { // we want at least 3 tiles of empty water around a water factory for (int y = -3; y < size.y + 3; y++) { for (int x = -3; x < size.x + 3; x++) { const grund_t* gr = welt->lookup_kartenboden(pos + koord(x, y)); // need to check for water buildings (e.g. fisheries) if (gr == NULL || !gr->is_water() || gr->get_grund_hang() != slope_t::flat || gr->find()) { return false; } } } } else { if (is_factory && site!=factory_desc_t::Land) { return factory_site_searcher_t(welt, site).is_area_ok(pos, size.x, size.y, cl); } else { return welt->square_is_free(pos, size.x, size.y, NULL, cl); } } return true; } koord3d factory_builder_t::find_random_construction_site(koord pos, int radius, koord size, factory_desc_t::site_t site, const building_desc_t *desc, bool ignore_climates, uint32 max_iterations) { bool is_factory = desc->get_type()==building_desc_t::factory; climate_bits climates = !ignore_climates ? desc->get_allowed_climate_bits() : ALL_CLIMATES; if (ignore_climates && site != factory_desc_t::Water) { //site = factory_desc_t::Land; } uint32 diam = 2*radius + 1; uint32 area = diam * diam; uint32 index = simrand(area); koord k; max_iterations = min( area/(size.x*size.y)+1, max_iterations ); const uint32 a = diam+1; const uint32 c = 37; // very unlikely to have this as a factor in somewhere ... // in order to stop on the first occurence, one has to iterate over all tiles in a reproducable but random enough manner for( uint32 i = 0; iget_name(), pos.get_str(), max_iterations); } return koord3d::invalid; finish: return welt->lookup_kartenboden(k)->get_pos(); } // Create a certain number of tourist attractions void factory_builder_t::distribute_attractions(int max_number) { // current number of tourist attractions constructed int current_number=0; // select without timeline disappearing dates if(hausbauer_t::get_random_attraction(welt->get_timeline_year_month(),true,temperate_climate)==NULL) { // nothing at all? return; } // very fast, so we do not bother updating progress bar dbg->message("factory_builder_t::distribute_attractions()", "Distributing %i tourist attractions", max_number); int retrys = max_number*4; while(current_number0) { koord3d pos=koord3d( koord::koord_random(welt->get_size().x,welt->get_size().y),1); const building_desc_t *attraction=hausbauer_t::get_random_attraction(welt->get_timeline_year_month(),true,(climate)simrand((int)arctic_climate+1)); // no attractions for that climate or too new if(attraction==NULL || (welt->use_timeline() && attraction->get_intro_year_month()>welt->get_current_month()) ) { continue; } int rotation=simrand(attraction->get_all_layouts()-1); pos = find_random_construction_site(pos.get_2d(), 20, attraction->get_size(rotation), factory_desc_t::Land, attraction, false, 0x0FFFFFFF); // so far -> land only if(welt->lookup(pos)) { // space found, build attraction hausbauer_t::build(welt->get_public_player(), pos.get_2d(), rotation, attraction); current_number ++; retrys = max_number*4; } } // update an open map minimap_t::get_instance()->calc_map_size(); } /** * Compares cities by their distance to an origin. */ class RelativeDistanceOrdering { private: const koord m_origin; public: RelativeDistanceOrdering(const koord& origin) : m_origin(origin) { /* nothing */ } /// @returns true if @p a is closer to the origin than @p b, otherwise false. bool operator()(const stadt_t *a, const stadt_t *b) const { return koord_distance(m_origin, a->get_pos()) < koord_distance(m_origin, b->get_pos()); } }; /* * Builds a single new factory. */ fabrik_t* factory_builder_t::build_factory(koord3d* parent, const factory_desc_t* info, sint32 initial_prod_base, int rotate, koord3d pos, player_t* owner) { fabrik_t * fab = new fabrik_t(pos, owner, info, initial_prod_base); // now build factory fab->build(rotate, true /*add fields*/, initial_prod_base != -1 /* force initial prodbase ? */); welt->add_fab(fab); add_factory_to_fab_map(welt, fab); if(parent) { fab->add_consumer(parent->get_2d()); } // make all water station if(info->get_placement() == factory_desc_t::Water) { const building_desc_t *desc = info->get_building(); koord dim = desc->get_size(rotate); // create water halt halthandle_t halt = haltestelle_t::create(pos.get_2d(), welt->get_public_player()); if(halt.is_bound()) { // add all other tiles of the factory to the halt for( int x=pos.x; xis_within_limits(x,y)) { grund_t *gr = welt->lookup_kartenboden(x,y); // build the halt on factory tiles on open water only, // since the halt can't be removed otherwise if(gr->is_water() && !(gr->hat_weg(water_wt)) && gr->find()) { halt->add_grund( gr ); } } } } halt->set_name( translator::translate(info->get_name()) ); halt->recalc_station_type(); } } else { // connect factory to stations // search for nearby stations and connect factory to them koord dim = info->get_building()->get_size(rotate); for( int x=pos.x; xaccess(x,y); const halthandle_t *halt_list = plan->get_haltlist(); for( unsigned h=0; hget_haltlist_count(); h++ ) { if (halt_list[h]->connect_factory(fab)) { if (x != pos.x || y != pos.y) { // factory halt list is already correct at pos fab->link_halt(halt_list[h]); } } // cannot call halt_list[h]->verbinde_fabriken() here, // as it modifies halt_list in its calls to fabrik_t::unlink_halt and fabrik_t::link_halt } } } } // add passenger to pax>0, (so no suicide diver at the fish swarm) if(info->get_pax_level()>0) { const weighted_vector_tpl& staedte = welt->get_cities(); vector_tpldistance_stadt( staedte.get_count() ); for(stadt_t* const i : staedte) { distance_stadt.insert_ordered(i, RelativeDistanceOrdering(fab->get_pos().get_2d())); } settings_t const& s = welt->get_settings(); for(stadt_t* const i : distance_stadt) { uint32 const ntgt = fab->get_target_cities().get_count(); if (ntgt >= s.get_factory_worker_maximum_towns()) break; if (ntgt < s.get_factory_worker_minimum_towns() || koord_distance(fab->get_pos(), i->get_pos()) < s.get_factory_worker_radius()) { fab->add_target_city(i); } } } return fab; } // Check if we have to rotate the factories before building this tree bool factory_builder_t::can_factory_tree_rotate( const factory_desc_t *desc ) { // we are finished: we cannot rotate if(!desc->get_building()->can_rotate()) { return false; } // now check all suppliers if they can rotate for( int i=0; iget_supplier_count(); i++ ) { const goods_desc_t *ware = desc->get_supplier(i)->get_input_type(); // unfortunately, for every for iteration we have to check all factories ... for(auto const& t : desc_table) { factory_desc_t const* const tmp = t.value; // now check if we produce this good... for (uint i = 0; i < tmp->get_product_count(); i++) { if(tmp->get_product(i)->get_output_type()==ware && tmp->get_distribution_weight()>0) { if(!can_factory_tree_rotate( tmp )) { return false; } // check next factory break; } } } } // all good -> true; return true; } /* * Builds a new full chain of factories. Precondition before calling this function: * @p pos is suitable for factory construction and number of chains * is the maximum number of good types for which suppliers chains are built * (meaning there are no unfinished factory chains). */ int factory_builder_t::build_link(koord3d* parent, const factory_desc_t* info, sint32 initial_prod_base, int rotate, koord3d* pos, player_t* player, int number_of_chains, bool ignore_climates) { int n = 1; int org_rotation = -1; if(info==NULL) { // no industry found return 0; } // no cities at all? if (info->get_placement() == factory_desc_t::City && welt->get_cities().empty()) { return 0; } // if a factory is not rotate-able, rotate the world until we can save it if(welt->cannot_save() && parent==NULL && !can_factory_tree_rotate(info) ) { org_rotation = welt->get_settings().get_rotation(); for( int i=0; i<3 && welt->cannot_save(); i++ ) { pos->rotate90( welt->get_size().y-info->get_building()->get_y(rotate) ); welt->rotate90(); } assert( !welt->cannot_save() ); } // in town we need a different place search if (info->get_placement() == factory_desc_t::City) { koord size=info->get_building()->get_size(0); // build consumer (factory) in town stadt_t *city = welt->find_nearest_city(pos->get_2d()); climate_bits cl = ignore_climates ? ALL_CLIMATES : info->get_building()->get_allowed_climate_bits(); /* Three variants: * A: * A building site, preferably close to the town hall with a street next to it. * This could be a temporary problem, if a city has no such site and the search * continues to the next city. * Otherwise seems to me the most realistic. */ bool is_rotate=info->get_building()->get_all_layouts()>1 && size.x!=size.y && info->get_building()->can_rotate(); // first try with standard orientation koord k = factory_site_searcher_t(welt, factory_desc_t::City).find_place(city->get_pos(), size.x, size.y, cl ); // second try: rotated koord k1 = koord::invalid; if (is_rotate && (k == koord::invalid || simrand(256)<128)) { k1 = factory_site_searcher_t(welt, factory_desc_t::City).find_place(city->get_pos(), size.y, size.x, cl ); } rotate = simrand( info->get_building()->get_all_layouts() ); if (k1 == koord::invalid) { if (size.x != size.y) { rotate &= 2; // rotation must be even number } } else { k = k1; rotate |= 1; // rotation must be odd number } if (!info->get_building()->can_rotate()) { rotate = 0; } INT_CHECK( "fabrikbauer 588" ); /* B: * Also good. The final factories stand possibly somewhat outside of the city however not far away. * (does not obey climates though!) */ #if 0 k = find_random_construction_site(welt, welt->lookup(city->get_pos())->get_boden()->get_pos(), 3, land_bau.dim).get_2d(); #endif /* C: * A building site, as near as possible to the city hall. * If several final factories land in one city, they are often * often hidden behind a row of houses, cut off from roads. */ #if 0 k = building_placefinder_t(welt).find_place(city->get_pos(), land_bau.dim.x, land_bau.dim.y, info->get_building()->get_allowed_climate_bits(), &is_rotate); #endif if(k != koord::invalid) { *pos = welt->lookup_kartenboden(k)->get_pos(); } else { return 0; } } DBG_MESSAGE("factory_builder_t::build_link","Construction of %s at (%i,%i).",info->get_name(),pos->x,pos->y); INT_CHECK("fabrikbauer 594"); const fabrik_t *our_fab=build_factory(parent, info, initial_prod_base, rotate, *pos, player); INT_CHECK("fabrikbauer 596"); // now build supply chains for all products for(int i=0; iget_supplier_count() && i update map if needed if(parent==NULL) { DBG_MESSAGE("factory_builder_t::build_link()","update karte"); // update the map if needed minimap_t::get_instance()->calc_map_size(); INT_CHECK( "fabrikbauer 730" ); // must rotate back? if(org_rotation>=0) { for (int i = 0; i < 4 && welt->get_settings().get_rotation() != org_rotation; ++i) { pos->rotate90( welt->get_size().y-1 ); welt->rotate90(); } welt->update_map(); } } return n; } int factory_builder_t::build_chain_link(const fabrik_t* our_fab, const factory_desc_t* info, int supplier_nr, player_t* player) { int n = 0; // number of additional factories /* first we try to connect to existing factories and will do some * cross-connect (if wanted) * We must take care to add capacity for cross-connected factories! */ const factory_supplier_desc_t *supplier = info->get_supplier(supplier_nr); const goods_desc_t *ware = supplier->get_input_type(); const int producer_count=count_producers( ware, welt->get_timeline_year_month() ); if(producer_count==0) { dbg->error("factory_builder_t::build_chain_link()","No producer for %s found, chain incomplete!",ware->get_name() ); return 0; } // how much do we need? sint32 consumption = our_fab->get_base_production()*supplier->get_consumption(); slist_tpl factories_to_correct; slist_tpl new_factories; // since the cross-correction must be done later slist_tpl crossconnected_supplier; // also done after the construction of new chains int lcount = supplier->get_supplier_count(); int lfound = 0; // number of found producers DBG_MESSAGE("factory_builder_t::build_chain_link","supplier_count %i, lcount %i (need %i of %s)",info->get_supplier_count(),lcount,consumption,ware->get_name()); // search if there already is one or two (cross-connect everything if possible) for(fabrik_t* const fab : welt->get_fab_list()) { // Try to find matching factories for this consumption, but don't find more than two times number of factories requested. if ( (lcount != 0 || consumption <= 0) && lcount < lfound + 1 ) break; // connect to an existing one if this is a producer if( fab->get_output_stock(ware) > -1 ) { const int distance = koord_distance(fab->get_pos(),our_fab->get_pos()); if( distance > welt->get_settings().get_min_factory_spacing() && distance <= DISTANCE ) { // ok, this would match but can she supply enough? // now guess how much this factory can supply const factory_desc_t* const fd = fab->get_desc(); for (uint gg = 0; gg < fd->get_product_count(); gg++) { if (fd->get_product(gg)->get_output_type() == ware && fab->get_consumer().get_count() < 10) { // does not make sense to split into more ... sint32 production_left = fab->get_base_production() * fd->get_product(gg)->get_factor(); const vector_tpl & consumer = fab->get_consumer(); // decrease remaining production by supplier demand for(koord const& i : consumer) { if (production_left <= 0) break; fabrik_t* const zfab = fabrik_t::get_fab(i); for(int zz=0; zzget_desc()->get_supplier_count(); zz++) { if(zfab->get_desc()->get_supplier(zz)->get_input_type()==ware) { production_left -= zfab->get_base_production()*zfab->get_desc()->get_supplier(zz)->get_consumption(); break; } } } // here is actually capacity left (or sometimes just connect anyway)! if (production_left > 0 || simrand(100) < (uint32)welt->get_settings().get_crossconnect_factor()) { if(production_left>0) { consumption -= production_left; fab->add_consumer(our_fab->get_pos().get_2d()); DBG_MESSAGE("factory_builder_t::build_chain_link","supplier %s can supply approx %i of %s to us", fd->get_name(), production_left, ware->get_name()); } else { /* we steal something; however the total capacity * needed is the same. Therefore, we just keep book * from whose factories from how many we stole */ crossconnected_supplier.append(fab); for(koord const& t : consumer) { fabrik_t* zfab = fabrik_t::get_fab(t); for(int zz=0; zzget_desc()->get_supplier_count(); zz++) { if(zfab->get_desc()->get_supplier(zz)->get_input_type()==ware) { slist_tpl::iterator i = std::find(factories_to_correct.begin(), factories_to_correct.end(), factories_to_crossconnect_t(zfab, 0)); if (i == factories_to_correct.end()) { factories_to_correct.append(factories_to_crossconnect_t(zfab, 1)); } else { i->demand += 1; } } } } // the needed production to be built does not change! } lfound ++; } break; // since we found the product ... } } } } } INT_CHECK( "fabrikbauer 670" ); /* try to add all types of factories until demand is satisfied */ weighted_vector_tplproducer; find_producer( producer, ware, welt->get_timeline_year_month() ); if( producer.empty() ) { // can happen with timeline if(!info->is_consumer_only()) { dbg->error( "factory_builder_t::build_chain_link()", "no producer for %s!", ware->get_name() ); return 0; } else { // only consumer: Will do with partly covered chains dbg->error( "factory_builder_t::build_chain_link()", "no producer for %s!", ware->get_name() ); } } bool ignore_climates = false; // ignore climates after some retrys int retry=25; // and not more than 25 (happens mostly in towns) while( (lcount>lfound || lcount==0) && consumption>0 && retry>0 ) { const factory_desc_t *producer_d = pick_any_weighted( producer ); int rotate = simrand(producer_d->get_building()->get_all_layouts()-1); koord3d parent_pos = our_fab->get_pos(); // ignore climates after 40 tries // if climates are ignored, then placement as well ... factory_desc_t::site_t placement = ignore_climates ? (producer_d->get_placement() == factory_desc_t::City ? factory_desc_t::City : factory_desc_t::Land) : producer_d->get_placement(); INT_CHECK("fabrikbauer 697"); koord3d k = find_random_construction_site( our_fab->get_pos().get_2d(), DISTANCE, producer_d->get_building()->get_size(rotate), placement, producer_d->get_building(), ignore_climates, 20000 ); if( k == koord3d::invalid ) { // this factory cannot buuild in the desired vincinity producer.remove( producer_d ); if( producer.empty() ) { if( ignore_climates ) { // absolutely no place to construct something here break; } find_producer( producer, ware, welt->get_timeline_year_month() ); ignore_climates = true; } continue; } INT_CHECK("fabrikbauer 697"); DBG_MESSAGE("factory_builder_t::build_chain_link","Try to built supplier %s at (%i,%i) r=%i for %s.",producer_d->get_name(),k.x,k.y,rotate,info->get_name()); n += build_link(&parent_pos, producer_d, -1 /*random prodbase */, rotate, &k, player, 10000, ignore_climates); lfound ++; INT_CHECK( "fabrikbauer 702" ); // now subtract current supplier fabrik_t *fab = fabrik_t::get_fab(k.get_2d() ); if(fab==NULL) { DBG_MESSAGE( "factory_builder_t::build_chain_link", "Failed to build at %s", k.get_str() ); retry --; continue; } new_factories.append(fab); // connect new supplier to us const factory_desc_t* const fd = fab->get_desc(); for (uint gg = 0; gg < fab->get_desc()->get_product_count(); gg++) { if (fd->get_product(gg)->get_output_type() == ware) { sint32 production = fab->get_base_production() * fd->get_product(gg)->get_factor(); // the take care of how much this factory could supply consumption -= production; DBG_MESSAGE("factory_builder_t::build_chain_link", "new supplier %s can supply approx %i of %s to us", fd->get_name(), production, ware->get_name()); break; } } } // now we add us to all cross-connected factories for(fabrik_t* const i : crossconnected_supplier) { i->add_consumer(our_fab->get_pos().get_2d()); } /* now the cross-connect part: * connect also the factories we stole from before ... */ for(fabrik_t* const fab : new_factories) { for (slist_tpl::iterator i = factories_to_correct.begin(), end = factories_to_correct.end(); i != end;) { i->demand -= 1; fab->add_consumer(i->fab->get_pos().get_2d()); i->fab->add_supplier(fab->get_pos().get_2d()); if (i->demand < 0) { i = factories_to_correct.erase(i); } else { ++i; } } } INT_CHECK( "fabrikbauer 723" ); return n; } /* * This function is called whenever it is time for industry growth. * If there is still a pending consumer, this method will first complete another chain for it. * If not, it will decide to either build a power station (if power is needed) * or build a new consumer near the indicated position. * @return number of factories built */ int factory_builder_t::increase_industry_density( bool tell_me ) { int nr = 0; fabrik_t *last_built_consumer = NULL; #if MSG_LEVEL >= 3 const goods_desc_t *last_built_consumer_ware = NULL; #endif // find last consumer minivec_tplware_needed; if(!welt->get_fab_list().empty()) { for(fabrik_t* const fab : welt->get_fab_list()) { if (fab->get_desc()->is_consumer_only()) { last_built_consumer = fab; break; } } // ok, found consumer if( last_built_consumer ) { ware_needed.clear(); ware_needed.resize( last_built_consumer->get_desc()->get_supplier_count() ); for( int i=0; i < last_built_consumer->get_desc()->get_supplier_count(); i++ ) { ware_needed.append_unique( last_built_consumer->get_desc()->get_supplier(i)->get_input_type() ); } for(koord const& j : last_built_consumer->get_suppliers()) { factory_desc_t const* const fd = fabrik_t::get_fab(j)->get_desc(); for (uint32 k = 0; k < fd->get_product_count(); k++) { ware_needed.remove( fd->get_product(k)->get_output_type() ); } } } } // first: do we have to continue unfinished factory chains? if( !ware_needed.empty() && last_built_consumer ) { int org_rotation = -1; // rotate until we can save it if one of the factories is non-rotate-able ... if(welt->cannot_save() && !can_factory_tree_rotate(last_built_consumer->get_desc()) ) { org_rotation = welt->get_settings().get_rotation(); for( int i=0; i<3 && welt->cannot_save(); i++ ) { welt->rotate90(); } assert( !welt->cannot_save() ); } uint32 last_suppliers = last_built_consumer->get_suppliers().get_count(); do { // we have the ware but not the supplier for it ... int last_built_consumer_missing_supplier = 99999; for( int i=0; i < last_built_consumer->get_desc()->get_supplier_count(); i++ ) { if( last_built_consumer->get_desc()->get_supplier(i)->get_input_type() == ware_needed[0] ) { last_built_consumer_missing_supplier = i; break; } } nr += build_chain_link( last_built_consumer, last_built_consumer->get_desc(), last_built_consumer_missing_supplier, welt->get_public_player() ); #if MSG_LEVEL >=3 last_built_consumer_ware = ware_needed[ 0 ]; #endif ware_needed.remove_at( 0 ); } while( !ware_needed.empty() && last_built_consumer->get_suppliers().get_count()==last_suppliers ); // must rotate back? if(org_rotation>=0) { for (int i = 0; i < 4 && welt->get_settings().get_rotation() != org_rotation; ++i) { welt->rotate90(); } welt->update_map(); } // only return if successful if( last_built_consumer->get_suppliers().get_count() > last_suppliers ) { #if MSG_LEVEL >= 3 DBG_MESSAGE( "factory_builder_t::increase_industry_density()", "added ware %s to factory %s", last_built_consumer_ware->get_name(), last_built_consumer->get_name() ); #endif // tell the player if(tell_me) { stadt_t *s = welt->find_nearest_city( last_built_consumer->get_pos().get_2d() ); const char *stadt_name = s ? s->get_name() : translator::translate("nowhere"); cbuffer_t buf; buf.printf( translator::translate("Factory chain extended\nfor %s near\n%s built with\n%i factories."), translator::translate(last_built_consumer->get_name()), stadt_name, nr ); welt->get_message()->add_message(buf, last_built_consumer->get_pos(), message_t::industry, CITY_KI, last_built_consumer->get_desc()->get_building()->get_tile(0)->get_background(0, 0, 0)); } minimap_t::get_instance()->calc_map(); return nr; } else { #if MSG_LEVEL >= 3 DBG_MESSAGE( "factory_builder_t::increase_industry_density()", "failed to add ware %s to factory %s", last_built_consumer_ware->get_name(), last_built_consumer->get_name() ); #endif } } // ok, no chains to finish, thus we must start anew // first determine electric supply and demand uint32 electric_supply = 0; uint32 electric_demand = 1; for(fabrik_t* const fab : welt->get_fab_list()) { if( fab->get_desc()->is_electricity_producer() ) { electric_supply += fab->get_scaled_electric_demand() / PRODUCTION_DELTA_T; } else { electric_demand += fab->get_scaled_electric_demand() / PRODUCTION_DELTA_T; } } // now decide producer of electricity or normal ... sint32 promille = (electric_supply*1000l)/electric_demand; int no_electric = promille > welt->get_settings().get_electric_promille(); // =1 -> build normal factories only DBG_MESSAGE( "factory_builder_t::increase_industry_density()", "production of electricity/total production is %i/%i (%i o/oo)", electric_supply, electric_demand, promille ); while( no_electric<2 ) { bool ignore_climates = false; for(int retries=20; retries>0; retries-- ) { const factory_desc_t *fab=get_random_consumer( no_electric==0, ALL_CLIMATES, welt->get_timeline_year_month() ); if(fab) { const bool in_city = fab->get_placement() == factory_desc_t::City; if (in_city && welt->get_cities().empty()) { // we cannot build this factory here continue; } koord3d pos; int rotation = simrand( fab->get_building()->get_all_layouts() ); if(!in_city) { // find somewhere on the map pos = find_random_construction_site( koord(welt->get_size().x/2,welt->get_size().y/2), welt->get_size_max()/2, fab->get_building()->get_size(rotation),fab->get_placement(),fab->get_building(),ignore_climates,10000); } else { // or within the city limit const stadt_t *city = pick_any_weighted(welt->get_cities()); koord diff = city->get_rechtsunten()-city->get_linksoben(); pos = find_random_construction_site( city->get_center(), max(diff.x,diff.y)/2, fab->get_building()->get_size(rotation),fab->get_placement(),fab->get_building(),ignore_climates, 1000); } if(welt->lookup(pos)) { // Space found... nr += build_link(NULL, fab, -1 /* random prodbase */, rotation, &pos, welt->get_public_player(), 1, ignore_climates); if(nr>0) { fabrik_t *our_fab = fabrik_t::get_fab( pos.get_2d() ); minimap_t::get_instance()->calc_map_size(); // tell the player if(tell_me) { stadt_t* s = welt->find_nearest_city(our_fab->get_pos().get_2d()); const char* stadt_name = s ? s->get_name() : translator::translate("nowhere"); cbuffer_t buf; buf.printf( translator::translate("New factory chain\nfor %s near\n%s built with\n%i factories."), translator::translate(our_fab->get_name()), stadt_name, nr ); welt->get_message()->add_message(buf, pos, message_t::industry, CITY_KI, our_fab->get_desc()->get_building()->get_tile(0)->get_background(0, 0, 0)); } return nr; } } else if( retries==1 && !ignore_climates ) { // from now on, we will ignore climates to avoid broken chains ignore_climates = true; retries = 20; } } } // try building normal factories if building power plants was not successful no_electric++; } // we should not reach here, because it means neither land nor city industries exist ... dbg->warning( "factory_builder_t::increase_industry_density()", "No suitable city industry found => pak missing something?" ); return 0; } simutrans-124.5/src/simutrans/builder/fabrikbauer.h000066400000000000000000000130451520675460500224570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef BUILDER_FABRIKBAUER_H #define BUILDER_FABRIKBAUER_H #include "../tpl/stringhashtable_tpl.h" #include "../tpl/weighted_vector_tpl.h" #include "../dataobj/koord3d.h" #include "../descriptor/factory_desc.h" class stadt_t; class karte_ptr_t; class player_t; class fabrik_t; /** * This class builds factories. Never construct factories directly * but always by calling factory_builder_t::build() (for a single factory) * or factory_builder_t::baue_hierachie() (for a full chain of suppliers). */ class factory_builder_t { private: static karte_ptr_t welt; /** * @class factories_to_crossconnect_t * Used for cross-connection checks between factories. * This is necessary for finding producers for factory supply. */ class factories_to_crossconnect_t { public: fabrik_t *fab; ///< The factory sint32 demand; ///< To how many factories this factory needs to connect to factories_to_crossconnect_t() { fab = NULL; demand = 0; } factories_to_crossconnect_t(fabrik_t *f, sint32 d) { fab = f; demand = d; } bool operator == (const factories_to_crossconnect_t& x) const { return fab == x.fab; } bool operator != (const factories_to_crossconnect_t& x) const { return fab != x.fab; } }; /// Table of all factories that can be built static stringhashtable_tpl desc_table; /// @returns the number of producers producing @p ware static int count_producers(const goods_desc_t *ware, uint16 timeline); /** * Finds a random producer producing @p ware. * * @param producer * @param ware * @param timeline the current time (months) */ static void find_producer(weighted_vector_tpl &producer, const goods_desc_t *ware, uint16 timeline ); public: /// Registers the factory description so the factory can be built in-game. static void register_desc(factory_desc_t *desc); /** * Initializes weighted vector for farm field class indices. * @returns true */ static bool successfully_loaded(); /** * Tells the factory builder a new map is being loaded or generated. * In this case the list of all factory positions must be reinitialized. */ static void new_world(); /// Creates a certain number of tourist attractions. static void distribute_attractions(int max_number); /// @returns a factory description for a factory name static const factory_desc_t *get_desc(const char *factory_name); /// @returns the table containing all factory descriptions static const stringhashtable_tpl& get_factory_table() { return desc_table; } /** * @param electric true to limit search to electricity producers only * @param cl allowed climates * @param timeline * @returns a random consumer */ static const factory_desc_t *get_random_consumer(bool electric, climate_bits cl, uint16 timeline ); /** * Builds a single new factory. * * @param parent location of the parent factory * @param info Description for the new factory * @param initial_prod_base Initial base production (-1 to disable) * @param rotate building rotation (0..3) * @param pos * @param owner * @returns The newly constructed factory. */ static fabrik_t* build_factory(koord3d* parent, const factory_desc_t* info, sint32 initial_prod_base, int rotate, koord3d pos, player_t* owner); /** * Builds a new full chain of factories. Precondition before calling this function: * @p pos is suitable for factory construction and number of chains * is the maximum number of good types for which suppliers chains are built * (meaning there are no unfinished factory chains). * @returns number of factories built */ static int build_link(koord3d* parent, const factory_desc_t* info, sint32 initial_prod_base, int rotate, koord3d* pos, player_t* player, int number_of_chains, bool ignore_climates ); /** * Helper function for baue_hierachie(): builds the connections (chain) for one single product) * @returns number of factories built */ static int build_chain_link(const fabrik_t* our_fab, const factory_desc_t* info, int supplier_nr, player_t* player); /** * This function is called whenever it is time for industry growth. * If there is still a pending consumer, this method will first complete another chain for it. * If not, it will decide to either build a power station (if power is needed) * or build a new consumer near the indicated position. * @returns number of factories built */ static int increase_industry_density( bool tell_me ); private: /** * Checks if the site at @p pos is suitable for construction. * * @param pos * @param size Size of the building site * @param site * @param is_factory * @param cl allowed climates */ static bool check_construction_site(koord pos, koord size, factory_desc_t::site_t site, bool is_factory, climate_bits cl); /** * Find a random site to place a factory. * * @param pos * @param radius Radius of the search circle around @p pos * @param size size of the building site * @param site * @param desc * @param ignore_climates * @param max_iterations */ static koord3d find_random_construction_site(koord pos, int radius, koord size, factory_desc_t::site_t site, const building_desc_t *desc, bool ignore_climates, uint32 max_iterations); /** * Checks if all factories in this factory tree can be rotated. * This method is called recursively on all potential suppliers. * @returns true if all factories in this tree can be rotated. */ static bool can_factory_tree_rotate( const factory_desc_t *desc ); }; #endif simutrans-124.5/src/simutrans/builder/goods_manager.cc000066400000000000000000000117731520675460500231530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "../descriptor/goods_desc.h" #include "../descriptor/spezial_obj_tpl.h" #include "../simware.h" #include "../simcolor.h" #include "goods_manager.h" #include "../dataobj/translator.h" stringhashtable_tpl goods_manager_t::desc_table; vector_tpl goods_manager_t::goods; uint8 goods_manager_t::max_catg_index = 0; const goods_desc_t *goods_manager_t::passengers = NULL; const goods_desc_t *goods_manager_t::mail = NULL; const goods_desc_t *goods_manager_t::none = NULL; goods_desc_t *goods_manager_t::load_passengers = NULL; goods_desc_t *goods_manager_t::load_mail = NULL; goods_desc_t *goods_manager_t::load_none = NULL; static special_obj_tpl const special_objects[] = { { &goods_manager_t::passengers, "Passagiere" }, { &goods_manager_t::mail, "Post" }, { &goods_manager_t::none, "None" }, { NULL, NULL } }; bool goods_manager_t::successfully_loaded() { if(!::successfully_loaded(special_objects)) { return false; } // Put special items in front goods.insert_at(0,load_none); goods.insert_at(0,load_mail); goods.insert_at(0,load_passengers); if(goods.get_count()>=255) { dbg->fatal("goods_manager_t::successfully_loaded()","Too many different goods %i>255",goods.get_count()-1 ); } // assign indexes for( uint8 i=3; igoods_index = i; } // now assign unique category indexes for unique categories max_catg_index = 0; // first assign special freight (which always needs an own category) for(goods_desc_t* const i : goods) { if (i->get_catg() == 0) { i->catg_index = max_catg_index++; } } // mapping of waren_t::catg to catg_index, map[catg] = catg_index uint8 map[255] = {0}; for(goods_desc_t* const i : goods) { uint8 const catg = i->get_catg(); if( catg > 0 ) { if( map[catg] == 0 ) { // We didn't found this category yet -> just create new index. map[catg] = max_catg_index++; } i->catg_index = map[catg]; } } // init the lookup table in ware_t for( unsigned i=0; i<256; i++ ) { if(i>=goods.get_count()) { // these entries will be never looked at; // however, if then this will generate an error ware_t::index_to_desc[i] = NULL; } else { assert(goods[i]->get_index()==i); ware_t::index_to_desc[i] = goods[i]; if(goods[i]->color==255) { goods[i]->color = 16+4+((i-2)*8)%207; } } } // passenger and mail colors if(goods[0]->color==255) { goods[0]->color = COL_GREY3; } if(goods[1]->color==255) { goods[1]->color = COL_YELLOW; } // none should never be loaded to something ... // however, some place do need the dummy ... ware_t::index_to_desc[2] = NULL; DBG_MESSAGE("goods_manager_t::successfully_loaded()","total goods %i, different kind of categories %i", goods.get_count(), max_catg_index ); return true; } static bool compare_ware_desc(const goods_desc_t* a, const goods_desc_t* b) { int diff = strcmp(a->get_name(), b->get_name()); return diff < 0; } bool goods_manager_t::register_desc(goods_desc_t *desc) { desc->value = desc->base_value; ::register_desc(special_objects, desc); // avoid duplicates with same name if( const goods_desc_t *old_desc = desc_table.remove(desc->get_name()) ) { goods.remove( const_cast(old_desc) ); delete old_desc; } desc_table.put(desc->get_name(), desc); if(desc==passengers) { desc->goods_index = INDEX_PAS; load_passengers = desc; } else if(desc==mail) { desc->goods_index = INDEX_MAIL; load_mail = desc; } else if(desc != none) { goods.insert_ordered(desc,compare_ware_desc); } else { load_none = desc; desc->goods_index = INDEX_NONE; } return true; } const goods_desc_t *goods_manager_t::get_info(const char* name) { const goods_desc_t *ware = desc_table.get(name); if( ware==NULL ) { ware = desc_table.get(translator::compatibility_name(name)); } if( ware == NULL ) { // to avoid crashed with NULL pointer in skripts return good NONE dbg->warning( "goods_manager_t::get_info()", "No desc for %s", name ); ware = goods_manager_t::none; } return ware; } const goods_desc_t *goods_manager_t::get_info_catg(const uint8 catg) { if(catg>0) { for(unsigned i=0; icatg==catg) { return goods[i]; } } } dbg->warning("goods_manager_t::get_info()", "No info for good catg %d available, set to passengers", catg); return goods[0]; } const goods_desc_t *goods_manager_t::get_info_catg_index(const uint8 catg_index) { for(unsigned i=0; iget_catg_index()==catg_index) { return goods[i]; } } // return none as default return goods[2]; } // adjuster for dummies ... void goods_manager_t::set_multiplier(sint32 multiplier) { //DBG_MESSAGE("goods_manager_t::set_multiplier()","new factor %i",multiplier); for(unsigned i=0; ivalue = (goods[i]->base_value * (sint64)multiplier) / INT64_C(1000); } } simutrans-124.5/src/simutrans/builder/goods_manager.h000066400000000000000000000031241520675460500230040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef BUILDER_GOODS_MANAGER_H #define BUILDER_GOODS_MANAGER_H #include "../tpl/vector_tpl.h" #include "../tpl/stringhashtable_tpl.h" class goods_desc_t; /** * Factory-Class for Goods. */ class goods_manager_t { private: static stringhashtable_tpl desc_table; static vector_tpl goods; static goods_desc_t *load_passengers; static goods_desc_t *load_mail; static goods_desc_t *load_none; // number of different good classes; static uint8 max_catg_index; public: enum { INDEX_PAS = 0, INDEX_MAIL = 1, INDEX_NONE = 2 }; static const goods_desc_t *passengers; static const goods_desc_t *mail; static const goods_desc_t *none; static bool successfully_loaded(); static bool register_desc(goods_desc_t *desc); static uint8 get_max_catg_index() { return max_catg_index; } /** * Search the good 'name' information and return * its description. Return NULL when the Good is * unknown. * * @param name the non-translated good name */ static const goods_desc_t *get_info(const char* name); static const goods_desc_t *get_info(uint16 idx) { return goods[idx]; } static uint8 get_count() { return (uint8)goods.get_count(); } // good by catg static const goods_desc_t *get_info_catg(const uint8 catg); // good by catg_index static const goods_desc_t *get_info_catg_index(const uint8 catg_index); /* * allow to multiply all prices, 1000=1.0 * used for the beginner mode */ static void set_multiplier(sint32 multiplier); }; #endif simutrans-124.5/src/simutrans/builder/hausbauer.cc000066400000000000000000001010211520675460500223070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../descriptor/building_desc.h" #include "../descriptor/skin_desc.h" #include "../descriptor/spezial_obj_tpl.h" #include "../ground/boden.h" #include "../ground/wasser.h" #include "../ground/fundament.h" #include "../dataobj/ribi.h" #include "../dataobj/scenario.h" #include "../obj/leitung2.h" #include "../obj/tunnel.h" #include "../obj/zeiger.h" #include "../gui/minimap.h" #include "../gui/tool_selector.h" #include "../world/simcity.h" #include "../simdebug.h" #include "../obj/depot.h" #include "../simfab.h" #include "../simhalt.h" #include "../utils/simrandom.h" #include "../tool/simtool.h" #include "../world/simworld.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/weighted_vector_tpl.h" #include "../tpl/vector_tpl.h" #include "hausbauer.h" karte_ptr_t hausbauer_t::welt; /* * The different building groups are sorted into separate lists */ static vector_tpl city_residential; ///< residential buildings (res) static vector_tpl city_commercial; ///< commercial buildings (com) static vector_tpl city_industry; ///< industrial buildings (ind) vector_tpl hausbauer_t::attractions_land; vector_tpl hausbauer_t::attractions_city; vector_tpl hausbauer_t::townhalls; vector_tpl hausbauer_t::monuments; vector_tpl hausbauer_t::unbuilt_monuments; /** * List of all registered house descriptors. * Allows searching for a desc by its name */ static stringhashtable_tpl desc_table; const building_desc_t *hausbauer_t::elevated_foundation_desc = NULL; vector_tpl hausbauer_t::station_building; vector_tpl hausbauer_t::headquarters; /// special objects directly needed by the program static special_obj_tpl const special_objects[] = { { &hausbauer_t::elevated_foundation_desc, "MonorailGround" }, { NULL, NULL } }; sint16 hausbauer_t::largest_city_building_area = 0; /** * Compares house descriptors. * Order of comparison: * -# by level * -# by name * @return true if @p a < @p b */ static bool compare_building_desc(const building_desc_t* a, const building_desc_t* b) { int diff = a->get_level() - b->get_level(); if (diff == 0) { /* As a last resort, sort by name to avoid ambiguity */ diff = strcmp(a->get_name(), b->get_name()); } return diff < 0; } /** * Compares headquarters. * Order of comparison: * -# by level * -# by name * @return true if @p a < @p b */ static bool compare_hq_desc(const building_desc_t* a, const building_desc_t* b) { // the headquarters level is in the extra-variable int diff = a->get_extra() - b->get_extra(); if (diff == 0) { diff = a->get_level() - b->get_level(); } if (diff == 0) { /* As a last resort, sort by name to avoid ambiguity */ diff = strcmp(a->get_name(), b->get_name()); } return diff < 0; } /** * Compares stations. * Order of comparison: * -# by good category * -# by capacity * -# by level * -# by name * @return true if @p a < @p b */ static bool compare_station_desc(const building_desc_t* a, const building_desc_t* b) { int diff = a->get_enabled() - b->get_enabled(); if( diff == 0 ) { diff = a->get_capacity() - b->get_capacity(); } if( diff == 0 ) { diff = a->get_level() - b->get_level(); } if( diff == 0 ) { /* As a last resort, sort by name to avoid ambiguity */ diff = strcmp(a->get_name(), b->get_name()); } return diff < 0; } bool hausbauer_t::successfully_loaded() { for(auto const& i : desc_table) { building_desc_t const* const desc = i.value; if (desc->is_city_building()) { // find city build sizes if (desc->get_size().x > 3 || desc->get_size().y > 3) { dbg->fatal("hausbauer_t::successfully_loaded()", "maximum city building size (3x3) but %s is (%ix%i)", desc->get_name(), desc->get_x(), desc->get_y()); } if (largest_city_building_area < desc->get_area()) { dbg->message("hausbauer_t::successfully_loaded()", "so far largest city building %s is (%ix%i)", desc->get_name(), desc->get_x(), desc->get_y()); largest_city_building_area = desc->get_area(); } } // now insert the desc into the correct list. switch(desc->get_type()) { case building_desc_t::city_res: city_residential.insert_ordered(desc,compare_building_desc); break; case building_desc_t::city_ind: city_industry.insert_ordered(desc,compare_building_desc); break; case building_desc_t::city_com: city_commercial.insert_ordered(desc,compare_building_desc); break; case building_desc_t::monument: monuments.insert_ordered(desc,compare_building_desc); break; case building_desc_t::attraction_land: attractions_land.insert_ordered(desc,compare_building_desc); break; case building_desc_t::headquarters: headquarters.insert_ordered(desc,compare_hq_desc); break; case building_desc_t::townhall: townhalls.insert_ordered(desc,compare_building_desc); break; case building_desc_t::attraction_city: attractions_city.insert_ordered(desc,compare_building_desc); break; case building_desc_t::factory: break; case building_desc_t::dock: case building_desc_t::flat_dock: case building_desc_t::depot: case building_desc_t::generic_stop: case building_desc_t::generic_extension: station_building.insert_ordered(desc,compare_station_desc); break; case building_desc_t::others: if(strcmp(desc->get_name(),"MonorailGround")==0) { // foundation for elevated ways elevated_foundation_desc = desc; break; } /* FALLTHROUGH */ default: // obsolete object, usually such pak set will not load properly anyway (old objects should be caught before!) dbg->error("hausbauer_t::successfully_loaded()","unknown subtype %i of \"%s\" ignored",desc->get_type(), desc->get_name()); } } // now sort them according level warn_missing_objects(special_objects); return true; } bool hausbauer_t::register_desc(building_desc_t *desc) { ::register_desc(special_objects, desc); // avoid duplicates with same name const building_desc_t *old_desc = desc_table.get(desc->get_name()); if(old_desc) { // do not overlay existing factories if the new one is not a factory if (old_desc->is_factory() && !desc->is_factory()) { dbg->error( "hausbauer_t::register_desc()", "Object %s could not be registered since it would overlay an existing factory building!", desc->get_name() ); delete desc; return false; } desc_table.remove(desc->get_name()); tool_t::general_tool.remove( old_desc->get_builder() ); delete old_desc->get_builder(); delete old_desc; } // probably needs a tool if it has a cursor const skin_desc_t *sd = desc->get_cursor(); if( sd && sd->get_image_id(1)!=IMG_EMPTY) { tool_t *tool; if( desc->get_type()==building_desc_t::depot ) { tool = new tool_build_depot_t(); } else if( desc->get_type()==building_desc_t::headquarters ) { tool = new tool_headquarter_t(); } else { // station if( desc->get_type()==building_desc_t::flat_dock || desc->get_type()==building_desc_t::dock ) { if( desc->get_size().x>1 && desc->get_size().y>1 ) { // no multile docks in both directions => do not register dbg->warning( "hausbauer_t::register_desc()", "Multitile dock %s ignored!", desc->get_name() ); return false; } } else if( desc->get_type()==building_desc_t::generic_stop && desc->get_size().x+desc->get_size().y > 2 ) { // no multile docks in both directions => do not register dbg->warning( "hausbauer_t::register_desc()", "Way stops must be single tile! %s ignored!", desc->get_name() ); return false; } tool = new tool_build_station_t(); } tool->set_icon( desc->get_cursor()->get_image_id(1) ); tool->cursor = desc->get_cursor()->get_image_id(0); tool->set_default_param(desc->get_name()); tool_t::general_tool.append( tool ); desc->set_builder( tool ); } else { desc->set_builder( NULL ); } desc_table.put(desc->get_name(), desc); /* Supply the tiles with a pointer back to the matching description. * This is necessary since each building consists of separate tiles, * even if it is part of the same description (building_desc_t) */ const int max_index = desc->get_all_layouts()*desc->get_size().x*desc->get_size().y; for( int i=0; i(desc->get_tile(i))->set_desc(desc); } return true; } void hausbauer_t::fill_menu(tool_selector_t* tool_selector, building_desc_t::btype btype, waytype_t wt, sint16 /*sound_ok*/) { // check if scenario forbids this uint16 toolnr = 0; switch(btype) { case building_desc_t::depot: toolnr = TOOL_BUILD_DEPOT | GENERAL_TOOL; break; case building_desc_t::dock: case building_desc_t::flat_dock: case building_desc_t::generic_stop: case building_desc_t::generic_extension: toolnr = TOOL_BUILD_STATION | GENERAL_TOOL; break; default: break; } if( toolnr > 0 && !welt->get_scenario()->is_tool_allowed(welt->get_active_player(), toolnr, wt) ) { return; } bool enable = welt->get_scenario()->is_tool_enabled(welt->get_active_player(), toolnr | GENERAL_TOOL, wt, 0); const uint16 time = welt->get_timeline_year_month(); DBG_DEBUG("hausbauer_t::fill_menu()","maximum %i",station_building.get_count()); for(building_desc_t const* const desc : station_building ) { // DBG_DEBUG("hausbauer_t::fill_menu()", "try to add %s (%p)", desc->get_name(), desc); if( desc->get_type()==btype && desc->get_builder() && (btype==building_desc_t::headquarters || desc->get_extra()==(uint16)wt) ) { if( desc->is_available(time) ) { if (welt->get_scenario()->is_tool_allowed(welt->get_active_player(), toolnr, wt, desc->get_name())) { desc->get_builder()->enabled = enable && welt->get_scenario()->is_tool_enabled(welt->get_active_player(), toolnr | GENERAL_TOOL, wt, desc->get_name()); tool_selector->add_tool_selector( desc->get_builder() ); } } } } } void hausbauer_t::new_world() { unbuilt_monuments.clear(); for(building_desc_t const* const i : monuments) { unbuilt_monuments.append(i); } } void hausbauer_t::remove( player_t *player, gebaeude_t *gb ) { const building_tile_desc_t *tile = gb->get_tile(); if( tile->get_desc()->get_type() == building_desc_t::headquarters ) { gb->get_owner()->add_headquarter( 0, koord::invalid ); } if(tile->get_desc()->get_type()==building_desc_t::monument) { unbuilt_monuments.append_unique(tile->get_desc()); } // iterate over all places to check if there is already an open window static vector_tpl gb_tiles; gb->get_tile_list( gb_tiles ); // then remove factory if(fabrik_t* fab = gb->get_fabrik()) { for(grund_t* gr : gb_tiles ) { const koord3d pos = gr->get_pos(); planquadrat_t *plan = welt->access( pos.get_2d() ); gebaeude_t* gb_part = gr->find(); gb_part->set_fab( NULL ); for (size_t i = plan->get_haltlist_count(); i-- != 0;) { halthandle_t halt = plan->get_haltlist()[i]; halt->remove_fabriken( fab ); plan->remove_from_haltlist( halt ); } } // tell players of the deletion for(uint8 i=0; iget_player(i); if (player) { player->notify_factory(player_t::notify_delete, fab); } } // remove all transformers while(1) { vector_tplconst& trans = fab->get_transformers(); if( trans.empty() ) { break; } leitung_t* sk = trans[0]; sk->mark_image_dirty( sk->get_image(), 0 ); delete sk; } // cleared transformers successfully, now remove factory. welt->rem_fab(fab); } vector_tplrecalc_boden; // delete our house only for(grund_t* gr : gb_tiles ) { const koord3d pos = gr->get_pos(); gebaeude_t* gb_part = gr->find(); gb_part->cleanup( player ); delete gb_part; // if this was a station building: delete ground if(gr->get_halt().is_bound()) { haltestelle_t::remove(player, pos); } // and maybe restore land below if(gr->get_typ()==grund_t::fundament) { const koord newk = pos.get_2d(); sint8 new_hgt; slope_t::type new_slope; welt->get_height_slope_from_grid(newk, new_hgt, new_slope); // test for ground at new height const grund_t *gr2 = welt->lookup(koord3d(newk,new_hgt)); if( (gr2==NULL || gr2==gr) && new_slope!=slope_t::flat ) { // and for ground above new sloped tile gr2 = welt->lookup(koord3d(newk, new_hgt+1)); if( gr==0 && slope_t::max_diff(new_slope) == 2 ) { gr2 = welt->lookup(koord3d(newk, new_hgt + 2)); } } if( gr2 && gr2!=gr ) { // there is another ground below or above // => do not change height, keep foundation welt->access(newk)->kartenboden_setzen( new boden_t( pos, slope_t::flat ) ); } else { boden_t* bd = new boden_t(koord3d(newk, new_hgt), new_slope); welt->access(newk)->kartenboden_setzen(bd); recalc_boden.append(bd); } } else if (wasser_t* sea = dynamic_cast(gr)) { sea->recalc_water_neighbours(); } } for(boden_t * gr : recalc_boden) { // the grid height might not be fully appropriate, so now we checlk for superflous walls const koord newk = gr->get_pos().get_2d(); sint8 new_hgt = gr->get_pos().z; const uint8 new_slope = welt->recalc_natural_slope(newk, new_hgt); if (new_hgt < welt->get_water_hgt(newk) || (new_hgt == welt->get_water_hgt(newk) && new_slope == slope_t::flat)) { wasser_t* sea = new wasser_t(koord3d(newk, new_hgt)); welt->access(newk)->kartenboden_setzen(sea); welt->calc_climate(newk, true); sea->recalc_water_neighbours(); } else { boden_t* bd = new boden_t(koord3d(newk, new_hgt), new_slope); welt->access(newk)->kartenboden_setzen(bd); // climate is stored in planquadrat, and hence automatically preserved } // there might be walls from foundations left => thus some tiles may need to be redrawn if (grund_t* gr = welt->lookup_kartenboden(newk + koord::east)) { gr->calc_image(); } if (grund_t* gr = welt->lookup_kartenboden(newk + koord::south)) { gr->calc_image(); } welt->set_grid_hgt_nocheck(newk, new_hgt + corner_nw(new_slope)); } } gebaeude_t* hausbauer_t::build(player_t* player, koord pos, int org_layout, const building_desc_t* desc, void* param) { gebaeude_t* first_building = NULL; koord k; koord dim; uint8 layout = desc->adjust_layout(org_layout); dim = desc->get_size(org_layout); bool needs_ground_recalc = false; if (desc->is_city_building()) { grund_t* gr = welt->lookup_kartenboden(pos); sint8 base_height = gr->get_hoehe() + slope_t::max_diff(gr->get_grund_hang()); stadt_t* city = static_cast(param); for (k.y = 0; k.y < dim.y; k.y++) { for (k.x = 0; k.x < dim.x; k.x++) { if (!city->check_ground_tile_for_house(welt->lookup_kartenboden(pos + k),base_height)) { dbg->error("hausbauer_t::build", "check_ground_tile_for_house failed at (%s)", (pos + k).get_str()); // cannot build here! return NULL; } } } // now build it vector_tplex; return city->build_city_house(koord3d(pos, base_height), desc, layout, 0, &ex); } sint8 base_h = -128; if(desc->get_area() > 1) { for( k.y = 0; k.y < dim.y; k.y++ ) { for( k.x = 0; k.x < dim.x; k.x++ ) { if( grund_t* gr = welt->lookup_kartenboden( pos + k ) ) { base_h = max( base_h, gr->get_hoehe() ); if( gr->get_hoehe()!=base_h && welt->lookup( koord3d( pos+k, base_h ) ) ) { // there is already a ground here! dbg->error("hausbauer_t::build","Fail to create new ground at (%s): There is already a ground above!", (pos+k).get_str() ); return NULL; } } else { dbg->error("hausbauer_t::build", "Not on the map at %s", (pos+k).get_str()); return NULL; } } } } else { // single tile grund_t* gr = welt->lookup_kartenboden( pos + k ); base_h = gr->get_hoehe() + +slope_t::max_diff( gr->get_grund_hang() ); } // now we must raise all grounds to base_h during construction for(k.y = 0; k.y < dim.y; k.y ++) { for(k.x = 0; k.x < dim.x; k.x ++) { //DBG_DEBUG("hausbauer_t::build()","get_tile() at %i,%i",k.x,k.y); const building_tile_desc_t *tile = desc->get_tile(layout, k.x, k.y); // skip empty tiles if (tile == NULL || ( !(desc->get_type() == building_desc_t::dock || desc->get_type() == building_desc_t::flat_dock) && tile->get_background(0, 0, 0) == IMG_EMPTY && tile->get_foreground(0, 0) == IMG_EMPTY )) { // may have a rotation that is not recoverable DBG_MESSAGE("hausbauer_t::build()","get_tile() empty at %i,%i",k.x,k.y); continue; } gebaeude_t *gb = new gebaeude_t(koord3d(pos + k,base_h), player, tile); if (first_building == NULL) { first_building = gb; } if(desc->is_factory()) { gb->set_fab((fabrik_t *)param); } // try to fake old building else if(welt->get_ticks() < 2) { // after staring a new map, build fake old buildings gb->add_alter(10000); } grund_t *gr = welt->lookup_kartenboden(pos + k); if(gr->is_water()) { gr->obj_add(gb); } else if( desc->get_type() == building_desc_t::dock || desc->get_type() == building_desc_t::flat_dock ) { // it's a dock! gr->obj_add(gb); } else { if (gr->get_typ() == grund_t::fundament) { // here is an old building gebaeude_t* old_gb = gr->find(); DBG_MESSAGE("hausbauer_t::build", "Destroy old building at %s", old_gb->get_pos().get_str()); hausbauer_t::remove(player, old_gb); gr = welt->lookup_kartenboden(pos + k); // since it is normal ground right now // in principle, we could just update the tile on 1x1 buildings (and do some additional housekeeping) but this is safer } // mostly remove everything vector_tpl keptobjs; if(!gr->hat_wege()) { // save certain object types for( uint8 i = 0; i < gr->obj_count(); i++ ) { obj_t *const obj = gr->obj_bei(i); obj_t::typ const objtype = obj->get_typ(); if( objtype == obj_t::leitung || objtype == obj_t::pillar || objtype==obj_t::air_vehicle ) { keptobjs.append(obj); } } for( size_t i = 0; i < keptobjs.get_count(); i++ ) { gr->obj_remove(keptobjs[i]); } // delete everything except vehicles gr->obj_loesche_alle(player); } // build new foundation needs_ground_recalc |= gr->get_grund_hang()!=slope_t::flat || gr->get_hoehe()!=base_h; grund_t *gr2 = new fundament_t( koord3d(pos+k,base_h), slope_t::flat); welt->access(pos+k)->boden_ersetzen(gr, gr2); gr = gr2; //DBG_DEBUG("hausbauer_t::build()","ground count now %i",gr->obj_count()); gr->obj_add( gb ); // restore saved objects for( size_t i = 0; i < keptobjs.get_count(); i++ ) { gr->obj_add(keptobjs[i]); } if(needs_ground_recalc && welt->is_within_limits(pos+k+koord(1,1)) && (k.y+1==dim.y || k.x+1==dim.x)) { welt->lookup_kartenboden(pos+k+koord(1,0))->calc_image(); welt->lookup_kartenboden(pos+k+koord(0,1))->calc_image(); welt->lookup_kartenboden(pos+k+koord(1,1))->calc_image(); } } gb->set_pos( gr->get_pos() ); if(desc->is_attraction()) { welt->add_attraction( gb ); } if (!desc->is_city_building()) { if(station_building.is_contained(desc)) { (*static_cast(param))->add_grund(gr); } if( desc->get_type() == building_desc_t::dock || desc->get_type() == building_desc_t::flat_dock ) { // its a dock! gb->set_yoff(0); if (wasser_t* sea = dynamic_cast(gr)) { sea->recalc_water_neighbours(); } } } gr->calc_image(); minimap_t::get_instance()->calc_map_pixel(gr->get_pos().get_2d()); } } // remove only once ... if(desc->get_type()==building_desc_t::monument) { monument_erected(desc); } return first_building; } gebaeude_t *hausbauer_t::build_station_extension_depot(player_t *player, koord3d pos, int built_layout, const building_desc_t *desc, void *param) { uint8 corner_layout = 6; // assume single building (for more than 4 layouts) // adjust layout of neighbouring building if(desc->is_transport_building() && desc->get_all_layouts()>1) { int layout = built_layout & 9; // detect if we are connected at far (north/west) end sint8 offset = welt->lookup( pos )->get_weg_yoff()/TILE_HEIGHT_STEP; koord3d checkpos = pos+koord3d( (layout & 1 ? koord::east : koord::south), offset); grund_t * gr = welt->lookup( checkpos ); if(!gr) { // check whether bridge end tile grund_t * gr_tmp = welt->lookup( pos+koord3d( (layout & 1 ? koord::east : koord::south),offset - 1) ); if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 1) { gr = gr_tmp; } else { gr_tmp = welt->lookup( pos+koord3d( (layout & 1 ? koord::east : koord::south),offset - 2) ); if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 2) { gr = gr_tmp; } } } if(gr) { gebaeude_t* gb = gr->find(); if(gb==NULL) { // no building on same level, check other levels const planquadrat_t *pl = welt->access(checkpos.get_2d()); if (pl) { for( uint8 i=0; iget_boden_count(); i++ ) { gr = pl->get_boden_bei(i); if(gr->is_halt() && gr->get_halt().is_bound() ) { break; } } } gb = gr->find(); } if( gb && gb->get_tile()->get_desc()->is_transport_building() ) { corner_layout &= ~2; // clear near bit if(gb->get_tile()->get_desc()->get_all_layouts()>4) { koord xy = gb->get_tile()->get_offset(); uint8 layoutbase = gb->get_tile()->get_layout(); if((layoutbase & 1) == (layout & 1)) { layoutbase &= 0xb; // clear near bit on neighbour gb->set_tile( gb->get_tile()->get_desc()->get_tile(layoutbase, xy.x, xy.y), false ); } } } } // detect if near (south/east) end gr = welt->lookup( pos+koord3d( (layout & 1 ? koord::west : koord::north), offset) ); if(!gr) { // check whether bridge end tile grund_t * gr_tmp = welt->lookup( pos+koord3d( (layout & 1 ? koord::west : koord::north),offset - 1) ); if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 1) { gr = gr_tmp; } else { gr_tmp = welt->lookup( pos+koord3d( (layout & 1 ? koord::west : koord::north),offset - 2) ); if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 2) { gr = gr_tmp; } } } if(gr) { gebaeude_t* gb = gr->find(); if(gb && gb->get_tile()->get_desc()->is_transport_building()) { corner_layout &= ~4; // clear far bit if(gb->get_tile()->get_desc()->get_all_layouts()>4) { koord xy = gb->get_tile()->get_offset(); uint8 layoutbase = gb->get_tile()->get_layout(); if((layoutbase & 1) == (layout & 1)) { layoutbase &= 0xd; // clear far bit on neighbour gb->set_tile( gb->get_tile()->get_desc()->get_tile(layoutbase, xy.x, xy.y), false ); } } } } } // adjust layouts of the new building if(desc->get_all_layouts()>4) { built_layout = (corner_layout | (built_layout&9) ) % desc->get_all_layouts(); } const building_tile_desc_t *tile = desc->get_tile(built_layout, 0, 0); gebaeude_t *gb; if( desc->get_type() == building_desc_t::depot ) { switch( desc->get_extra() ) { case track_wt: gb = new bahndepot_t(pos, player, tile); break; case tram_wt: gb = new tramdepot_t(pos, player, tile); break; case monorail_wt: gb = new monoraildepot_t(pos, player, tile); break; case maglev_wt: gb = new maglevdepot_t(pos, player, tile); break; case narrowgauge_wt: gb = new narrowgaugedepot_t(pos, player, tile); break; case road_wt: gb = new strassendepot_t(pos, player, tile); break; case water_wt: gb = new schiffdepot_t(pos, player, tile); break; case air_wt: gb = new airdepot_t(pos, player, tile); break; default: dbg->fatal("hausbauer_t::build_station_extension_depot()","waytpe %i has no depots!", desc->get_extra() ); } } else { gb = new gebaeude_t(pos, player, tile); } //DBG_MESSAGE("hausbauer_t::build_station_extension_depot()","building stop pri=%i",pri); // remove pointer grund_t *gr = welt->lookup(pos); zeiger_t* zeiger = gr->find(); if( zeiger ) { gr->obj_remove(zeiger); zeiger->set_flag(obj_t::not_on_map); } gr->obj_add(gb); if( station_building.is_contained(desc) && desc->get_type()!=building_desc_t::depot ) { // is a station/bus stop (*static_cast(param))->add_grund(gr); gr->calc_image(); } else { gb->calc_image(); } if(desc->is_attraction()) { welt->add_attraction( gb ); } // update minimap minimap_t::get_instance()->calc_map_pixel(gb->get_pos().get_2d()); return gb; } const building_tile_desc_t *hausbauer_t::find_tile(const char *name, int org_idx) { if (org_idx < 0) { return NULL; } const building_desc_t *desc = desc_table.get(name); if(!desc) { // DBG_MESSAGE("hausbauer_t::find_tile()","\"%s\" not in hashtable",name); return NULL; } const int size = desc->get_y()*desc->get_x(); int idx = org_idx; if( idx >= desc->get_all_layouts()*size ) { idx %= desc->get_all_layouts()*size; DBG_MESSAGE("gebaeude_t::rdwr()","%s using tile %i instead of %i",name,idx,org_idx); } return desc->get_tile(idx); } const building_desc_t* hausbauer_t::get_desc(const char *name) { return desc_table.get(name); } const building_desc_t* hausbauer_t::get_random_station(const building_desc_t::btype type, const waytype_t wt, const uint16 time, const uint8 enables) { weighted_vector_tpl stops; if( wt < 0 ) { return NULL; } for(building_desc_t const* const desc : station_building) { if( desc->get_type()==type && desc->get_extra()==(uint32)wt && (enables==0 || (desc->get_enabled()&enables)!=0) ) { // skip underground stations if( !desc->can_be_built_aboveground() ) { continue; } // ok, now check timeline if( desc->is_available(time) ) { stops.append(desc,max(1,16-desc->get_level()*desc->get_x()*desc->get_y())); } } } return stops.empty() ? 0 : pick_any_weighted(stops); } const building_desc_t* hausbauer_t::get_special(uint32 bev, building_desc_t::btype type, uint16 time, bool ignore_retire, climate cl) { weighted_vector_tpl auswahl(16); vector_tpl *list = NULL; switch(type) { case building_desc_t::townhall: list = &townhalls; break; case building_desc_t::attraction_city: list = &attractions_city; break; default: return NULL; } for(building_desc_t const* const desc : *list) { // extra data contains number of inhabitants for building if( desc->get_extra()==bev ) { if( cl==MAX_CLIMATES || desc->is_allowed_climate(cl) ) { // ok, now check timeline if( time==0 || (desc->get_intro_year_month()<=time && (ignore_retire || desc->get_retire_year_month() > time) ) ) { auswahl.append(desc, desc->get_distribution_weight()); } } } } if (auswahl.empty()) { return 0; } else if(auswahl.get_count()==1) { return auswahl.front(); } // now there is something to choose return pick_any_weighted(auswahl); } /** * Tries to find a matching house desc from @p list. * This method will never return NULL if there is at least one valid entry in the list. * @param start_level the minimum level of the house/station * @param cl allowed climates */ static const building_desc_t* get_city_building_from_list(const vector_tpl& list, int start_level, uint16 time, climate cl, uint32 clusters, sint16 minsize, sint16 maxsize, vector_tpl* exclude ) { weighted_vector_tpl selections(16); int level = start_level; // DBG_MESSAGE("hausbauer_t::get_aus_liste()","target level %i", level ); const building_desc_t *desc_at_least=NULL; for(building_desc_t const* const desc : list) { const int thislevel = desc->get_level(); if( thislevel > level ) { if (selections.empty()) { // Nothing of the correct level. Continue with search on a higher level. level = thislevel; } else { // We already found something of the correct or an higher level; stop break; } } if( (desc->is_allowed_climate(cl) || cl==MAX_CLIMATES ) && desc->get_distribution_weight() > 0 && desc->is_available(time) && // size check (desc->get_area()>=minsize && desc->get_area() <= maxsize) && (!exclude || !exclude->is_contained(desc)) ) { desc_at_least = desc; if( thislevel == level ) { // DBG_MESSAGE("hausbauer_t::get_city_building_from_list()","appended %s at %i", desc->get_name(), thislevel ); /* Level, time period, and climate are all OK. * Now modify the chance rating by a factor based on the clusters. */ // FIXME: the factor should be configurable by the pakset/ int chance = desc->get_distribution_weight(); if( clusters ) { uint32 my_clusters = desc->get_clusters(); if( my_clusters & clusters ) { chance *= stadt_t::get_cluster_factor(); } else { chance /= stadt_t::get_cluster_factor(); } } selections.append(desc, chance); } } } if(selections.get_sum_weight()==0) { // this is some level below, but at least it is something return desc_at_least; } if(selections.get_count()==1) { return selections.front(); } // now there is something to choose return pick_any_weighted(selections); } const building_desc_t* hausbauer_t::get_commercial(int level, uint16 time, climate cl, uint32 clusters, sint16 minsize, sint16 maxsize, vector_tpl* exclude) { return get_city_building_from_list(city_commercial, level, time, cl, clusters, minsize, maxsize, exclude ); } const building_desc_t* hausbauer_t::get_industrial(int level, uint16 time, climate cl, uint32 clusters, sint16 minsize, sint16 maxsize, vector_tpl* exclude) { return get_city_building_from_list(city_industry, level, time, cl, clusters, minsize, maxsize, exclude ); } const building_desc_t* hausbauer_t::get_residential(int level, uint16 time, climate cl, uint32 clusters, sint16 minsize, sint16 maxsize, vector_tpl *exclude) { return get_city_building_from_list(city_residential, level, time, cl, clusters, minsize, maxsize, exclude ); } const building_desc_t* hausbauer_t::get_headquarters(int level, uint16 time) { if( level<0 ) { return NULL; } for(building_desc_t const* const desc : hausbauer_t::headquarters) { if( desc->get_extra()==(uint32)level && desc->is_available(time) ) { return desc; } } return NULL; } const building_desc_t *hausbauer_t::get_random_desc(vector_tpl &list, uint16 time, bool ignore_retire, climate cl) { if (!list.empty()) { // previously just returned a random object; however, now we look at the chance entry weighted_vector_tpl auswahl(16); for(building_desc_t const* const desc : list) { if((cl==MAX_CLIMATES || desc->is_allowed_climate(cl)) && desc->get_distribution_weight()>0 && (time==0 || (desc->get_intro_year_month()<=time && (ignore_retire || desc->get_retire_year_month()>time) ) ) ) { // DBG_MESSAGE("hausbauer_t::get_random_desc()","appended %s at %i", desc->get_name(), thislevel ); auswahl.append(desc, desc->get_distribution_weight()); } } // now look what we have got ... if(auswahl.get_sum_weight()==0) { return NULL; } if(auswahl.get_count()==1) { return auswahl.front(); } // now there is something to choose return pick_any_weighted(auswahl); } return NULL; } const vector_tpl* hausbauer_t::get_list(const building_desc_t::btype typ) { switch (typ) { case building_desc_t::monument: return &unbuilt_monuments; case building_desc_t::attraction_land: return &attractions_land; case building_desc_t::headquarters: return &headquarters; case building_desc_t::townhall: return &townhalls; case building_desc_t::attraction_city: return &attractions_city; case building_desc_t::dock: case building_desc_t::flat_dock: case building_desc_t::depot: case building_desc_t::generic_stop: case building_desc_t::generic_extension: return &station_building; default: return NULL; } } const vector_tpl* hausbauer_t::get_citybuilding_list(const building_desc_t::btype typ) { switch (typ) { case building_desc_t::city_res: return &city_residential; case building_desc_t::city_com: return &city_commercial; case building_desc_t::city_ind: return &city_industry; default: return NULL; } } simutrans-124.5/src/simutrans/builder/hausbauer.h000066400000000000000000000154701520675460500221650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef BUILDER_HAUSBAUER_H #define BUILDER_HAUSBAUER_H #include "../descriptor/building_desc.h" #include "../dataobj/koord3d.h" #include "../simtypes.h" #include "../tpl/vector_tpl.h" class gebaeude_t; class karte_ptr_t; class player_t; class tool_selector_t; /** * This class deals with building single- and multi-tile buildings. It knows the descriptions * of (nearly) all buildings as regards type, height, size, images and animations. * To be able to build a new house the house description must be registered by register_desc(). * * To ensure all monuments are only present once per map, there is a list * which monuments have been built and which not. * There's no need to construct an instance since everything is static here. */ class hausbauer_t { private: static sint16 largest_city_building_area; static vector_tpl attractions_land; ///< Sights outside of cities static vector_tpl attractions_city; ///< Sights within cities static vector_tpl townhalls; ///< Town halls static vector_tpl monuments; ///< All monuments static vector_tpl unbuilt_monuments; ///< All unbuilt monuments static vector_tpl headquarters; ///< Company headquarters static vector_tpl station_building; ///< All station buildings /// @returns a random entry from @p list static const building_desc_t* get_random_desc(vector_tpl& list, uint16 time, bool ignore_retire, climate cl); /// our game world static karte_ptr_t welt; public: static sint16 get_largest_city_building_area() { return largest_city_building_area; } /// description for elevated monorail (mandatory description) static const building_desc_t* elevated_foundation_desc; /** * Finds a station building enabling pax/mail/goods for the AI. * If @p time == 0 the timeline will be ignored. * * @param utype * @param wt * @param time * @param enables station enabled flags (see haltestelle_t::station_flags) * @returns a random station that can be built above ground. */ static const building_desc_t* get_random_station(const building_desc_t::btype utype, const waytype_t wt, const uint16 time, const uint8 enables); /// Finds and returns the tile at position @p idx static const building_tile_desc_t* find_tile(const char* name, int idx); /// @returns the house description with name @p name static const building_desc_t* get_desc(const char *name); /** * Registers the house description so the house can be built in-game. * @returns true */ static bool register_desc(building_desc_t *desc); /// Sorts all house descriptions into their respective lists. static bool successfully_loaded(); /** * Fills menu with icons of buildings of a given waytype. * This is needed for station extensions and headquarters. */ static void fill_menu(tool_selector_t* tool_selector, building_desc_t::btype, waytype_t wt, sint16 sound_ok); /// @returns a random commercial building matching the requirements. static const building_desc_t* get_commercial(int level, uint16 time, climate c, uint32 clusters, sint16 minsize, sint16 maxsize, vector_tpl* exclude = NULL); /// @returns a random industrial building matching the requirements. static const building_desc_t* get_industrial(int level, uint16 time, climate cl, uint32 clusters, sint16 minsize, sint16 maxsize, vector_tpl* exclude = NULL); /// @returns a random residential building matching the requirements. static const building_desc_t* get_residential(int level, uint16 time, climate cl, uint32 clusters, sint16 minsize, sint16 maxsize, vector_tpl* exclude = NULL); /// @returns headquarters with level @p level (takes the first matching one) static const building_desc_t* get_headquarters(int level, uint16 time); /// @returns a random tourist attraction matching the requirements. static const building_desc_t* get_random_attraction(uint16 time, bool ignore_retire, climate cl) { return get_random_desc(attractions_land, time, ignore_retire, cl); } /// @returns a random unbuilt monument. static const building_desc_t* get_random_monument(uint16 time = 0) { return get_random_desc(unbuilt_monuments, time, false, MAX_CLIMATES); } /** * Tells the house builder a new map is being loaded or generated. * In this case the list of unbuilt monuments must be refilled * to ensure each monument is only present once per map. */ static void new_world(); /// @returns true if this monument has not yet been built. static bool is_valid_monument(const building_desc_t* desc) { return unbuilt_monuments.is_contained(desc); } /// Tells the house builder a monument has been built. static void monument_erected(const building_desc_t* desc) { unbuilt_monuments.remove(desc); } /// Called for a city attraction or a town hall with a certain number of inhabitants (bev). static const building_desc_t* get_special(uint32 bev, building_desc_t::btype utype, uint16 time, bool ignore_retire, climate cl); /** * Removes an arbitrary building. * It will also take care of factories and foundations. * * @param player the player wanting to remove the building. * @param gb */ static void remove(player_t *player, gebaeude_t *gb); /** * Main function to build all non-traffic buildings, including factories. * Building size can be larger than 1x1. * Also the underlying ground will be changed to foundation. * * @param player * @param pos * @param layout * @param desc * @param param if building a factory, pointer to the factory, * if building a stop, pointer to the halt handle. * Can only build houses on map ground! * * @return The first built part of the building. Usually at @p pos, if this * building tile is not empty. */ static gebaeude_t* build(player_t* player, koord pos, int layout, const building_desc_t* desc, void* param = NULL); /** * Build all kind of stops and depots. The building size must be 1x1. * Stations with layout>4 may change the layout of neighbouring buildings. (->end of rail platforms) * * @param player * @param pos * @param layout * @param desc * @param param if building a stop, pointer to the halt handle */ static gebaeude_t* build_station_extension_depot(player_t* player, koord3d pos, int layout, const building_desc_t* desc, void* param = NULL); /// @returns house list of type @p typ static const vector_tpl *get_list(building_desc_t::btype typ); /// @returns city building list of type @p typ (res/com/ind) static const vector_tpl *get_citybuilding_list(building_desc_t::btype typ); }; #endif simutrans-124.5/src/simutrans/builder/tree_builder.cc000066400000000000000000000253401520675460500230060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "tree_builder.h" #include "../dataobj/settings.h" #include "../obj/baum.h" #include "../obj/groundobj.h" #include "../world/simworld.h" #include "../utils/simrandom.h" #include static karte_ptr_t welt; static const uint8 tree_age_index[(baum_t::AGE_LIMIT >> 6) + 1] = { 0,1,2,3,3,3,3,3,3,4,4,4 }; vector_tpl tree_builder_t::tree_list(0); weighted_vector_tpl tree_builder_t::tree_list_per_climate[MAX_CLIMATES]; stringhashtable_tpl tree_builder_t::desc_table; vector_tpl tree_builder_t::loaded_tree_names; /// Quick lookup of an image, assuring always five seasons and five ages. /// Missing images just have identical entries. /// Seasons are: 0=summer, 1=autumn, 2=winter, 3=spring, 4=snow /// Snow image is used if tree is above snow line, or for arctic climate static image_id tree_id_to_image[256][5*5]; image_id tree_builder_t::get_tree_image(uint8 tree_id, uint32 age, uint8 season) { const uint8 tree_age_idx = tree_age_index[min(age>>6, 11u)]; assert(tree_age_idx < 5); return tree_id_to_image[ tree_id ][ season*5 + tree_age_idx ]; } const tree_desc_t *tree_builder_t::find_tree(const char *tree_name) { return tree_list.empty() ? NULL : desc_table.get(tree_name); } uint8 tree_builder_t::plant_tree_on_coordinate(koord pos, const uint8 maximum_count, const uint8 count) { grund_t *gr = welt->lookup_kartenboden(pos); if( gr ) { if( has_trees_for_climate( welt->get_climate(pos) ) && gr->ist_natur() && gr->obj_count() < maximum_count ) { obj_t *obj = gr->obj_bei(0); if(obj) { switch(obj->get_typ()) { case obj_t::cloud: case obj_t::air_vehicle: case obj_t::baum: case obj_t::leitung: case obj_t::label: case obj_t::zeiger: // ok to build here break; case obj_t::groundobj: if(((groundobj_t *)obj)->get_desc()->can_build_trees_here()) { break; } /* FALLTHROUGH */ // leave these (and all other empty) default: return 0; } } const uint8 count_planted = min( maximum_count - gr->obj_count(), count); for (uint8 i=0; iobj_add( new baum_t(gr->get_pos()) ); //plants the tree(s) } return count_planted; } } return 0; } bool tree_builder_t::plant_tree_on_coordinate(koord pos, const tree_desc_t *desc, const bool check_climate, const bool random_age) { // none there if( desc_table.empty() ) { return false; } grund_t *gr = welt->lookup_kartenboden(pos); if( gr ) { if( gr->ist_natur() && gr->obj_count() < welt->get_settings().get_max_no_of_trees_on_square() && (!check_climate || desc->is_allowed_climate( welt->get_climate(pos) )) ) { if( gr->obj_count() > 0 ) { switch(gr->obj_bei(0)->get_typ()) { case obj_t::cloud: case obj_t::air_vehicle: case obj_t::baum: case obj_t::leitung: case obj_t::label: case obj_t::zeiger: // ok to built here break; case obj_t::groundobj: if(((groundobj_t *)(gr->obj_bei(0)))->get_desc()->can_build_trees_here()) { break; } /* FALLTHROUGH */ // leave these (and all other empty) default: return false; } } baum_t *b = new baum_t(gr->get_pos(), desc); //plants the tree if( random_age ) { b->geburt = welt->get_current_month() - simrand(baum_t::AGE_LIMIT-1); b->calc_off( welt->lookup( b->get_pos() )->get_grund_hang() ); } gr->obj_add( b ); return true; //tree was planted - currently unused value is not checked } } return false; } uint32 tree_builder_t::create_forest(koord new_center, koord wh, sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom) { // none there if( desc_table.empty() ) { return 0; } const sint16 xpos_f = new_center.x; const sint16 ypos_f = new_center.y; uint32 number_of_new_trees = 0; for( sint16 j = 0; j < wh.x; j++) { for( sint16 i = 0; i < wh.y; i++) { const sint32 x_tree_pos = (j-(wh.x>>1)); const sint32 y_tree_pos = (i-(wh.y>>1)); if( xtop > (xpos_f + x_tree_pos) || (xpos_f + x_tree_pos) >= xbottom ) { continue; } if( ytop > (ypos_f + y_tree_pos) || (ypos_f + y_tree_pos) >= ybottom ) { continue; } const uint64 distance = 1 + sqrt_i64( ((uint64)x_tree_pos*x_tree_pos*(wh.y*wh.y) + (uint64)y_tree_pos*y_tree_pos*(wh.x*wh.x))); const uint32 tree_probability = (uint32)( ( 8 * (uint32)((wh.x*wh.x)+(wh.y*wh.y)) ) / distance ); if (tree_probability < 38) { continue; } uint8 number_to_plant = 0; uint8 const max_trees_here = min(welt->get_settings().get_max_no_of_trees_on_square(), (tree_probability - 38 + 1) / 2); for (uint8 c2 = 0 ; c2get_settings().get_max_no_of_trees_on_square(); number_of_new_trees += plant_tree_on_coordinate(tree_pos, max_trees_per_square, number_to_plant); } } return number_of_new_trees; } void tree_builder_t::fill_trees(int dichte, sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom ) { // none there if( desc_table.empty() ) { return; } DBG_MESSAGE("tree_builder_t::fill_trees", "distributing single trees"); koord pos; for( pos.y=ytop; pos.ylookup_kartenboden(pos); if(gr->obj_count() == 0 && gr->get_typ() == grund_t::boden) { // plant spare trees, (those with low preffered density) or in an entirely tree climate const uint16 cl = 1 << welt->get_climate(pos); const settings_t &s = welt->get_settings(); if ((cl & s.get_no_tree_climates()) == 0 && ((cl & s.get_tree_climates()) != 0 || simrand(s.get_forest_inverse_spare_tree_density() * dichte) < 100)) { plant_tree_on_coordinate(pos, 1, 1); } } } } } static bool compare_tree_desc(const tree_desc_t *a, const tree_desc_t *b) { // same level - we do an artificial but unique sorting by (untranslated) name return strcmp(a->get_name(), b->get_name())<0; } bool tree_builder_t::successfully_loaded() { if( desc_table.empty() ) { DBG_MESSAGE("tree_builder_t::successfully_loaded", "No trees found - feature disabled"); } for(auto const& i : desc_table) { tree_list.insert_ordered(i.value, compare_tree_desc); if( tree_list.get_count()==255 ) { dbg->error( "tree_builder_t::successfully_loaded", "Maximum tree count exceeded! (%u > 255)", desc_table.get_count() ); break; } } tree_list.append( NULL ); // clear cache memset( tree_id_to_image, -1, sizeof(tree_id_to_image) ); // now register all trees for all fitting climates for( uint32 typ=0; typis_allowed_climate((climate)j) ) { assert(typ <= 255); tree_list_per_climate[j].append((uint8)typ, tree_list[typ]->get_distribution_weight()); } } // create cache images for( uint8 season = 0; season < 5; season++ ) { uint8 use_season = 0; const sint16 seasons = tree_list[typ]->get_seasons(); if( seasons > 1 ) { use_season = season; // three possibilities if( seasons < 4 ) { // only summer and winter => season 4 with winter image use_season = (season == 4); } else if( seasons == 4 ) { // all there, but the snowy special image missing if( season == 4 ) { // take spring image (gave best results with pak64, pak.german) ////// but season 2 is winter???? use_season = 2; } } } for( uint8 age_idx = 0; age_idx < 5; age_idx++ ) { tree_id_to_image[typ][season * 5 + age_idx] = tree_list[typ]->get_image_id( use_season, age_idx ); } } } return true; } bool tree_builder_t::register_desc(const tree_desc_t *desc) { // avoid duplicates with same name if(const tree_desc_t *old = desc_table.remove(desc->get_name()) ) { delete old; } desc_table.put(desc->get_name(), desc ); return true; } void tree_builder_t::distribute_trees(int dichte, sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom) { // now we can proceed to tree planting routine itself // best forests results are produced if forest size is tied to map size - // but there is some nonlinearity to ensure good forests on small maps settings_t const& s = welt->get_settings(); sint32 const x = welt->get_size().x; sint32 const y = welt->get_size().y; unsigned const t_forest_size = (uint32)pow(((double)x * (double)y), 0.25) * s.get_forest_base_size() / 11 + (x + y) / (2 * s.get_forest_map_size_divisor()); uint32 const c_forest_count = (uint32)pow(((double)x * (double)y), 0.5) / s.get_forest_count_divisor(); DBG_MESSAGE("tree_builder_t::distribute_trees", "Creating %i forests", c_forest_count); for (uint32 c1 = 0; c1 < c_forest_count ; c1++) { // to have same execution order for simrand koord const start = koord::koord_random(x, y); koord const size = koord(t_forest_size,t_forest_size) + koord::koord_random(t_forest_size, t_forest_size); create_forest( start, size, xtop, ytop, xbottom, ybottom ); } fill_trees( dichte, xtop, ytop, xbottom, ybottom ); } const tree_desc_t *tree_builder_t::random_tree_for_climate(climate cl) { const uint16 b = random_tree_id_for_climate(cl); return b!=0xFFFF ? tree_list[b] : NULL; } uint16 tree_builder_t::random_tree_id_for_climate(climate cl) { // now weight their distribution weighted_vector_tpl const &t = tree_list_per_climate[cl]; return t.empty() ? 0xFFFF : pick_any_weighted(t); } bool tree_builder_t::spawn_tree_near(const baum_t *tree, int radius) { // to have same execution order for simrand const sint16 sx = simrand(2*radius + 1)-radius; const sint16 sy = simrand(2*radius + 1)-radius; const koord k = tree->get_pos().get_2d() + koord(sx,sy); return plant_tree_on_coordinate(k, tree->get_desc(), true, false); } void tree_builder_t::rdwr_tree_ids(loadsave_t *file) { xml_tag_t tag(file, "tree_ids"); uint8 num_trees = tree_list.get_count()-1; file->rdwr_byte(num_trees); if (file->is_loading()) { loaded_tree_names.clear(); plainstring str; for (uint8 i = 0; i < num_trees; ++i) { file->rdwr_str(str); loaded_tree_names.append(str); } } else { for (uint8 i = 0; i < num_trees; ++i) { plainstring str = tree_list[i]->get_name(); file->rdwr_str(str); } } } const char *tree_builder_t::get_loaded_desc_name(uint8 loaded_id) { return (loaded_id < loaded_tree_names.get_count()) ? loaded_tree_names[loaded_id].c_str() : NULL; } simutrans-124.5/src/simutrans/builder/tree_builder.h000066400000000000000000000070111520675460500226430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef BUILDER_TREE_BUILDER_H #define BUILDER_TREE_BUILDER_H #include "../tpl/stringhashtable_tpl.h" #include "../tpl/weighted_vector_tpl.h" #include "../tpl/vector_tpl.h" #include "../utils/plainstring.h" #include "../dataobj/koord.h" #include "../display/simimg.h" class tree_desc_t; class baum_t; /// Handles desc management and distribution of trees. class tree_builder_t { private: static stringhashtable_tpl desc_table; ///< Mapping desc_name -> desc static vector_tpl tree_list; ///< Mapping tree_id -> desc static weighted_vector_tpl tree_list_per_climate[MAX_CLIMATES]; ///< index vector into tree_list, accessible per climate static vector_tpl loaded_tree_names; ///< Maps loaded_id -> loaded_desc_name public: static void rdwr_tree_ids(loadsave_t *file); /// Tree IDs never change after loading paks, so when loading a save /// which was saved with a different set of tree paks the tree ID from the save /// has to be translated to the one used in the current game. /// This is done by mapping /// loaded_id -> loaded_desc_name -> real_desc_name -> real_id /// This function does the first mapping based on the data stored in the save file. /// Returns NULL if no desc name could be found. /// @sa boden_t::boden_t static const char *get_loaded_desc_name(uint8 loaded_id); static const tree_desc_t *get_desc_by_name(const char * tree_name) { return desc_table.get(tree_name); } static const tree_desc_t *get_desc_by_id(uint8 tree_id) { return tree_id < get_num_trees() ? tree_list[tree_id] : NULL; } static uint8 get_id_by_desc(const tree_desc_t *desc) { return tree_list.index_of(desc); } static image_id get_tree_image(uint8 idx, uint32 age, uint8 season); static bool has_trees() { return get_num_trees() > 0; } static bool has_trees_for_climate(climate cl) { return !tree_list_per_climate[cl].empty(); } static sint32 get_num_trees() { return tree_list.get_count()-1; } /// Fill rectangular region with trees. static void fill_trees(int dichte, sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom); static bool register_desc(const tree_desc_t *desc); static bool successfully_loaded(); /// @returns list of all registered descriptors static const vector_tpl &get_all_desc() { return tree_list; } /// Plant a new tree in a (2n+1) * (2n+1) square around @p tree. static bool spawn_tree_near(const baum_t *tree, int radius = 3); static uint32 create_forest(koord center, koord size, sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom); public: /// distributes trees in a rectangular region of the map static void distribute_trees(int dichte, sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom); /// tree planting function - it takes care of checking suitability of area static bool plant_tree_on_coordinate(koord pos, const tree_desc_t *desc, const bool check_climate, const bool random_age); /// tree planting function - it takes care of checking suitability of area static uint8 plant_tree_on_coordinate(koord pos, const uint8 maximum_count, const uint8 count); static const tree_desc_t *find_tree( const char *tree_name ); static const tree_desc_t *random_tree_for_climate(climate cl); /// also checks for distribution values /// @returns the tree_id, or 0xFFFF if no trees exist for the requested climate. static uint16 random_tree_id_for_climate(climate cl); }; #endif simutrans-124.5/src/simutrans/builder/tunnelbauer.cc000066400000000000000000000621051520675460500226650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "tunnelbauer.h" #include "../gui/minimap.h" #include "../world/simworld.h" #include "../player/simplay.h" #include "../tool/simtool.h" #include "../descriptor/tunnel_desc.h" #include "../ground/tunnelboden.h" #include "../dataobj/scenario.h" #include "../dataobj/environment.h" #include "../dataobj/marker.h" #include "../obj/tunnel.h" #include "../obj/leitung2.h" #include "../obj/signal.h" #include "../gui/messagebox.h" #include "../gui/tool_selector.h" #include "wegbauer.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/vector_tpl.h" #include "../world/terraformer.h" karte_ptr_t tunnel_builder_t::welt; static stringhashtable_tpl tunnel_by_name; void tunnel_builder_t::register_desc(tunnel_desc_t *desc) { // avoid duplicates with same name if( const tunnel_desc_t *old_desc = tunnel_by_name.remove(desc->get_name()) ) { tool_t::general_tool.remove( old_desc->get_builder() ); delete old_desc->get_builder(); // we cannot delete old_desc, since then xref-resolving will crash } // add the tool tool_build_tunnel_t *tool = new tool_build_tunnel_t(); tool->set_icon( desc->get_cursor()->get_image_id(1) ); tool->cursor = desc->get_cursor()->get_image_id(0); tool->set_default_param( desc->get_name() ); tool_t::general_tool.append( tool ); desc->set_builder( tool ); tunnel_by_name.put(desc->get_name(), desc); } const tunnel_desc_t *tunnel_builder_t::get_desc(const char *name) { return (name ? tunnel_by_name.get(name) : NULL); } /** * Find a matching tunnel */ const tunnel_desc_t *tunnel_builder_t::get_tunnel_desc(const waytype_t wtyp, const sint32 min_speed, const uint16 time) { const tunnel_desc_t *find_desc = NULL; for(auto const& i : tunnel_by_name) { tunnel_desc_t* const desc = i.value; if( desc->get_waytype()==wtyp ) { if( desc->is_available(time) ) { if( find_desc==NULL || (find_desc->get_topspeed()get_topspeed()get_topspeed()) || (desc->get_topspeed()>=min_speed && desc->get_maintenance()get_maintenance()) ) { find_desc = desc; } } } } return find_desc; } static bool compare_tunnels(const tunnel_desc_t* a, const tunnel_desc_t* b) { int cmp = a->get_topspeed() - b->get_topspeed(); if(cmp==0) { cmp = (int)a->get_intro_year_month() - (int)b->get_intro_year_month(); } if(cmp==0) { cmp = strcmp(a->get_name(), b->get_name()); } return cmp<0; } /** * Fill menu with icons of given waytype */ void tunnel_builder_t::fill_menu(tool_selector_t* tool_selector, const waytype_t wtyp, sint16 /*sound_ok*/) { // check if scenario forbids this if (!welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_TUNNEL | GENERAL_TOOL, wtyp, 0)) { return; } bool enable = welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_TUNNEL | GENERAL_TOOL, wtyp, 0); const uint16 time=welt->get_timeline_year_month(); vector_tpl matching(tunnel_by_name.get_count()); for(auto const& i : tunnel_by_name) { tunnel_desc_t* const desc = i.value; if( desc->get_waytype()==wtyp && desc->is_available(time) ) { if (welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_TUNNEL | GENERAL_TOOL, wtyp, desc->get_name())) { matching.insert_ordered(desc, compare_tunnels); } } } // now sorted ... for(tunnel_desc_t const* const i : matching) { i->get_builder()->enabled = enable && welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_TUNNEL | GENERAL_TOOL, wtyp, i->get_name()); tool_selector->add_tool_selector(i->get_builder()); } } const vector_tpl& tunnel_builder_t::get_available_tunnels(const waytype_t wtyp) { static vector_tpl dummy; dummy.clear(); const uint16 time = welt->get_timeline_year_month(); for(auto const& i : tunnel_by_name) { tunnel_desc_t const* const b = i.value; if (b->get_waytype()==wtyp && b->is_available(time) && b->get_builder()) { dummy.append(b); } } return dummy; } /* now construction stuff */ koord3d tunnel_builder_t::find_end_pos(player_t *player, koord3d pos, koord zv, const tunnel_desc_t *desc, bool full_tunnel, const char** msg) { const grund_t *gr; leitung_t *lt; waytype_t wegtyp = desc->get_waytype(); // use the is_allowed_step routine of way_builder_t, needs an instance way_builder_t bauigel(player); bauigel.init_builder( way_builder_t::tunnel_flag | (way_builder_t::bautyp_t)wegtyp, way_builder_t::weg_search( wegtyp, 1, 0, type_flat ), desc); sint32 dummy; while(true) { pos = pos + zv; if(!welt->is_within_limits(pos.get_2d())) { return koord3d::invalid; } // check if ground is below tunnel level gr = welt->lookup_kartenboden(pos.get_2d()); // steep slopes and we are appearing at the top of one if( gr->get_hoehe() == pos.z-1 && welt->get_settings().get_way_height_clearance()==1 ) { const slope_t::type new_slope = slope_type(-zv); sint8 hsw = pos.z + corner_sw(new_slope); sint8 hse = pos.z + corner_se(new_slope); sint8 hne = pos.z + corner_ne(new_slope); sint8 hnw = pos.z + corner_nw(new_slope); terraformer_t raise(terraformer_t::raise, welt); raise.add_node(pos.x, pos.y, hsw, hse, hne, hnw); raise.generate_affected_tile_list(); if (raise.can_raise_all(player)) { // returned true therefore error reported return koord3d::invalid; } // if we can adjust height here we can build an entrance so don't need checks below return pos; } if( gr->get_hoehe() < pos.z ){ return koord3d::invalid; } // check water level if (gr->is_water() && welt->lookup_hgt(pos.get_2d()) <= pos.z) { return koord3d::invalid; } if (const char* err = welt->get_scenario()->is_work_allowed_here(player, TOOL_BUILD_TUNNEL|GENERAL_TOOL, desc->get_finance_waytype(), desc->get_name(), pos)) { if (msg) { *msg = err; } return koord3d::invalid; } // next tile gr = welt->lookup(pos); if( gr == NULL ) { // check for slope down ... gr = welt->lookup(pos + koord3d(0,0,-1)); if( !gr ) { gr = welt->lookup(pos + koord3d(0,0,-2)); } if( gr && gr->get_weg_hang() == slope_t::flat ) { // Don't care about _flat_ tunnels below. gr = NULL; } if( !gr && welt->get_settings().get_way_height_clearance()==2 ) { // check for one above gr = welt->lookup(pos + koord3d(0,0,1)); } } if(gr) { // if there is a tunnel try to connect if( gr->ist_tunnel() ) { if( gr->get_vmove(ribi_type(-zv))!=pos.z) { // wrong slope return koord3d::invalid; } // fake tunnel tile tunnelboden_t from(pos - zv, slope_t::flat); if (bauigel.is_allowed_step(&from, gr, &dummy)) { return gr->get_pos(); } else { return koord3d::invalid; } } const uint8 slope = gr->get_grund_hang(); const slope_t::type new_slope = slope_type(-zv) * welt->get_settings().get_way_height_clearance(); if( gr->ist_karten_boden() && ( slope!=new_slope || pos.z!=gr->get_pos().z ) ) { // lower terrain to match - most of time shouldn't need to raise // however player might have manually altered terrain so check this anyway sint8 hsw = pos.z + corner_sw(new_slope); sint8 hse = pos.z + corner_se(new_slope); sint8 hne = pos.z + corner_ne(new_slope); sint8 hnw = pos.z + corner_nw(new_slope); terraformer_t raise(terraformer_t::raise, welt); terraformer_t lower(terraformer_t::lower, welt); raise.add_node(pos.x, pos.y, hsw, hse, hne, hnw); lower.add_node(pos.x, pos.y, hsw, hse, hne, hnw); raise.generate_affected_tile_list(); lower.generate_affected_tile_list(); if (raise.can_raise_all(player)!= NULL || lower.can_lower_all(player)!=NULL) { // returned non-null therefore error reported return koord3d::invalid; } // if we can adjust height here we can build an entrance so don't need checks below return pos; } if( gr->get_typ() != grund_t::boden || slope != new_slope || gr->is_halt() || ((wegtyp != powerline_wt) ? gr->get_leitung() != NULL : gr->hat_wege()) ) { // must end on boden_t and correct slope and not on halts // ways cannot end on powerlines, powerlines cannot end on ways return koord3d::invalid; } if( gr->has_two_ways() && wegtyp != road_wt ) { // Only road tunnels allowed here. return koord3d::invalid; } ribi_t::ribi ribi = 0; if(wegtyp != powerline_wt) { ribi = gr->get_weg_ribi_unmasked(wegtyp); } else { if(gr->get_leitung()) { ribi = gr->get_leitung()->get_ribi(); } } if( ribi && koord(ribi) == zv ) { // There is already a way (with correct ribi) return pos; } if( !ribi ) { // End of the slope - Missing end rail or has no ribis // we still consider if we interfere with a way if(wegtyp != powerline_wt) { if( !gr->hat_wege() || gr->hat_weg(wegtyp) ) { return pos; } } else { lt = gr->find(); if(!gr->hat_wege() || lt) { return pos; } } } return koord3d::invalid; // Was im Weg (slope hillside or so) } // stop if we only want to check tile behind tunnel mouth if (!full_tunnel) { return pos; } // All free - keep looking } } const char *tunnel_builder_t::build( player_t *player, koord pos, const tunnel_desc_t *desc, bool full_tunnel ) { assert( desc ); const grund_t *gr = welt->lookup_kartenboden(pos); if(gr==NULL) { return "Tunnel must start on single way!"; } koord zv; const waytype_t wegtyp = desc->get_waytype(); const slope_t::type slope = gr->get_grund_hang(); if( wegtyp != powerline_wt ) { const weg_t *weg = gr->get_weg(wegtyp); if( gr->get_typ() != grund_t::boden || gr->is_halt() || gr->get_leitung()) { return "Tunnel must start on single way!"; } // If there is a way on this tile, it must have the right ribis. if( weg && (weg->get_ribi_unmasked() & ~ribi_t::backward( ribi_type(slope) )) ) { return "Tunnel must start on single way!"; } } else { leitung_t *lt = gr->find(); if( gr->get_typ() != grund_t::boden || gr->hat_wege() ) { return "Tunnel must start on single way!"; } if( lt && (lt->get_ribi() & ~ribi_t::backward( ribi_type(slope) )) ) { return "Tunnel must start on single way!"; } } if( !slope_t::is_single(slope) ) { return "Tunnel muss an\neinfachem\nHang beginnen!\n"; } /************************************** FIX ME *************************************************** ********************** THIS MUST BE RATHER A PROPERTY OF THE TUNNEL IN QUESTION ! ****************/ // for conversion factor 1, must be single height, for conversion factor 2, must be double if( (welt->get_settings().get_way_height_clearance() == 1 && !is_one_high(slope)) || (welt->get_settings().get_way_height_clearance() == 2 && is_one_high(slope)) ) { return "Tunnel muss an\neinfachem\nHang beginnen!\n"; } if( gr->has_two_ways() && wegtyp != road_wt ) { return "Tunnel must start on single way!"; } zv = koord(slope); if (const char* err = welt->get_scenario()->is_work_allowed_here(player, TOOL_BUILD_TUNNEL | GENERAL_TOOL, desc->get_finance_waytype(), desc->get_name(), gr->get_pos())) { return err; } // Search tunnel end and check intermediate tiles const char *err = NULL; koord3d end = koord3d::invalid; if (!full_tunnel) { // if there is no tunnel behind set end to start position const grund_t *gr_end = welt->lookup(end); if (gr_end == NULL || !gr_end->ist_tunnel()) { end = gr->get_pos(); } } else { end = find_end_pos(player, gr->get_pos(), zv, desc, full_tunnel, &err); if (err) { return err; } } if(!welt->is_within_limits(end.get_2d())) { return "Tunnel must start on single way!"; } // check ownership const grund_t *end_gr = welt->lookup(end); if (end_gr) { if (weg_t *weg_end = end_gr->get_weg(wegtyp)) { if (weg_end->get_removal_error(player)!=NULL) { return "Das Feld gehoert\neinem anderen Spieler\n"; } if( full_tunnel && end_gr->get_typ() == grund_t::tunnelboden ) { full_tunnel = false; } } } // Begin and end found, we can build slope_t::type end_slope = slope_type(-zv) * welt->get_settings().get_way_height_clearance(); if( full_tunnel && (!end_gr || end_gr->get_grund_hang()!=end_slope) ) { // end slope not at correct height - we have already checked in find_end_pos that we can change this sint8 hsw = end.z + corner_sw(end_slope); sint8 hse = end.z + corner_se(end_slope); sint8 hne = end.z + corner_ne(end_slope); sint8 hnw = end.z + corner_nw(end_slope); terraformer_t raise(terraformer_t::raise, welt); terraformer_t lower(terraformer_t::lower, welt); raise.add_node(end.x, end.y, hsw, hse, hne, hnw); lower.add_node(end.x, end.y, hsw, hse, hne, hnw); raise.generate_affected_tile_list(); lower.generate_affected_tile_list(); err = raise.can_raise_all(player); if (!err) err = lower.can_lower_all(player); if (err) return 0; // TODO: this is rather hackish as 4 seems to come from nowhere but works most of the time // feel free to change if you have a better idea! const int n = (raise.apply() + lower.apply()) / 4; player_t::book_construction_costs(player, welt->get_settings().cst_alter_land * n, end.get_2d(), desc->get_waytype()); } if(!build_tunnel(player, gr->get_pos(), end, zv, desc)) { return "Ways not connected"; } return NULL; } bool tunnel_builder_t::build_tunnel(player_t *player, koord3d start, koord3d end, koord zv, const tunnel_desc_t *desc) { ribi_t::ribi ribi = 0; weg_t *weg = NULL; leitung_t *lt = NULL; koord3d pos = start; sint64 cost = 0; const way_desc_t *way_desc; waytype_t wegtyp = desc->get_waytype(); DBG_MESSAGE("tunnel_builder_t::build()","build from (%d,%d,%d) to (%d,%d,%d) ", pos.x, pos.y, pos.z, end.x, end.y, end.z ); // now we search a matching way for the tunnels top speed way_desc = desc->get_way_desc(); if(way_desc==NULL) { // ignore timeline to get consistent results way_desc = way_builder_t::weg_search( wegtyp, desc->get_topspeed(), 0, type_flat ); } build_tunnel_portal(player, pos, zv, desc, way_desc, cost, start != end); ribi = ribi_type(-zv); // move on pos = pos + zv; // calc back image to remove wall blocking tunnel portal for active underground view if(grund_t::underground_mode) { grund_t *gr = welt->lookup_kartenboden(pos.get_2d()); gr->calc_image(); gr->set_flag(grund_t::dirty); } if( end == start ) { // already finished return true; } // Now we build the invisible part while(pos.get_2d()!=end.get_2d()) { tunnelboden_t *tunnel = new tunnelboden_t(pos, slope_t::flat); welt->access(pos.get_2d())->boden_hinzufuegen(tunnel); if(wegtyp != powerline_wt) { weg = weg_t::alloc(desc->get_waytype()); weg->set_desc(way_desc); weg->set_max_speed(desc->get_topspeed()); tunnel->neuen_weg_bauen(weg, ribi_t::doubles(ribi), player); player_t::add_maintenance( player, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype() ); } else { lt = new leitung_t(tunnel->get_pos(), player); lt->set_desc(way_desc); tunnel->obj_add( lt ); lt->finish_rd(); } tunnel->obj_add(new tunnel_t(pos, player, desc)); tunnel->calc_image(); tunnel->set_flag(grund_t::dirty); assert(!tunnel->ist_karten_boden()); player_t::add_maintenance( player, desc->get_maintenance(), desc->get_finance_waytype() ); cost += desc->get_price(); pos = pos + zv; } // if end is tunnel then connect grund_t *gr_end = welt->lookup(end); if (gr_end) { if (gr_end->ist_tunnel()) { gr_end->weg_erweitern(desc->get_waytype(), ribi); } else if (gr_end->ist_karten_boden()) { // if end is above ground construct an exit build_tunnel_portal(player, pos, -zv, desc, way_desc, cost, true); gr_end = NULL; // invalid - replaced by tunnel ground // calc new back image for the ground if (end!=start && grund_t::underground_mode) { grund_t *gr = welt->lookup_kartenboden(pos.get_2d()-zv); gr->calc_image(); gr->set_flag(grund_t::dirty); } } else { // good luck assert(0); } } else { // construct end tunnel tile tunnelboden_t *tunnel = new tunnelboden_t(pos, slope_t::flat); welt->access(pos.get_2d())->boden_hinzufuegen(tunnel); if(wegtyp != powerline_wt) { weg = weg_t::alloc(desc->get_waytype()); weg->set_desc(way_desc); weg->set_max_speed(desc->get_topspeed()); tunnel->neuen_weg_bauen(weg, ribi, player); player_t::add_maintenance( player, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype() ); } else { lt = new leitung_t(tunnel->get_pos(), player); lt->set_desc(way_desc); tunnel->obj_add( lt ); lt->finish_rd(); } tunnel->obj_add(new tunnel_t(pos, player, desc)); tunnel->calc_image(); tunnel->set_flag(grund_t::dirty); assert(!tunnel->ist_karten_boden()); player_t::add_maintenance( player, desc->get_maintenance(), desc->get_finance_waytype() ); cost += desc->get_price(); } player_t::book_construction_costs(player, -cost, start.get_2d(), desc->get_waytype()); return true; } void tunnel_builder_t::build_tunnel_portal(player_t *player, koord3d end, koord zv, const tunnel_desc_t *desc, const way_desc_t *way_desc, sint64 &cost, bool connect_inside) { grund_t *alter_boden = welt->lookup(end); ribi_t::ribi ribi = 0; if(desc->get_waytype()!=powerline_wt) { ribi = alter_boden->get_weg_ribi_unmasked(desc->get_waytype()); } if (connect_inside) { ribi |= ribi_type(zv); } tunnelboden_t *tunnel = new tunnelboden_t( end, alter_boden->get_grund_hang()); tunnel->obj_add(new tunnel_t(end, player, desc)); weg_t *weg = NULL; if(desc->get_waytype()!=powerline_wt) { weg = alter_boden->get_weg( desc->get_waytype() ); } // take care of everything on that tile ... tunnel->take_obj_from( alter_boden ); welt->access(end.get_2d())->kartenboden_setzen( tunnel ); if(desc->get_waytype() != powerline_wt) { if(weg) { // has already a way tunnel->weg_erweitern(desc->get_waytype(), ribi); } else { // needs still one weg = weg_t::alloc( desc->get_waytype() ); if( way_desc ) { weg->set_desc( way_desc ); } tunnel->neuen_weg_bauen( weg, ribi, player ); } player_t::add_maintenance( player, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype() ); weg->set_max_speed( desc->get_topspeed() ); } else { leitung_t *lt = tunnel->get_leitung(); if(!lt) { lt = new leitung_t(tunnel->get_pos(), player); lt->set_desc(way_desc); tunnel->obj_add( lt ); } else { // subtract maintenance once since leitung_t::finish_rd will add it again player_t::add_maintenance( player, -1*lt->get_desc()->get_maintenance(), powerline_wt ); } lt->finish_rd(); } // remove sidewalk weg_t *str = tunnel->get_weg( road_wt ); if( str && str->hat_gehweg()) { str->set_gehweg(false); } tunnel->calc_image(); tunnel->set_flag(grund_t::dirty); // Auto-connect to a way outside the new tunnel mouth grund_t *ground_outside = welt->lookup(end-zv); if (!ground_outside) { ground_outside = welt->lookup_kartenboden(end.get_2d() - zv); if (ground_outside) { if (!ground_outside->hat_weg(desc->get_waytype()) || ground_outside->get_weg_hang() != slope_t::flat || (end.z - ground_outside->get_pos().z) != slope_t::max_diff(ground_outside->get_grund_hang()) ) { // Not the correct slope/height ground_outside = NULL; } } } // check if we can connect if( ground_outside) { weg_t *way_outside = ground_outside->get_weg( desc->get_waytype() ); if( way_outside ) { // use the check_owner routine of way_builder_t (not player_t!), needs an instance way_builder_t bauigel(player); bauigel.init_builder( (way_builder_t::bautyp_t)desc->get_waytype(), way_outside->get_desc()); sint32 dummy; if(bauigel.is_allowed_step(tunnel, ground_outside, &dummy)) { tunnel->weg_erweitern(desc->get_waytype(), ribi_type(-zv)); ground_outside->weg_erweitern(desc->get_waytype(), ribi_type(zv)); } } if (desc->get_waytype()==water_wt && ground_outside->is_water()) { // connect to the sea tunnel->weg_erweitern(desc->get_waytype(), ribi_type(-zv)); ground_outside->calc_image(); // to recalculate ribis } } if(player!=NULL) { player_t::add_maintenance( player, desc->get_maintenance(), desc->get_finance_waytype() ); } cost += desc->get_price(); } const char *tunnel_builder_t::remove(player_t *player, koord3d start, waytype_t wegtyp, bool remove_all ) { marker_t& marker = marker_t::instance(welt->get_size().x, welt->get_size().y); slist_tpl end_list; slist_tpl part_list; slist_tpl tmp_list; koord3d pos = start; // First check if all tunnel parts can be removed tmp_list.insert(pos); grund_t *from = welt->lookup(pos); marker.mark(from); waytype_t delete_wegtyp = wegtyp==powerline_wt ? invalid_wt : wegtyp; do { pos = tmp_list.remove_first(); grund_t *from = welt->lookup(pos); grund_t *to; koord zv = koord::invalid; if(from->ist_karten_boden()) { // Der Grund ist Tunnelanfang/-ende - hier darf nur in // eine Richtung getestet werden. zv = koord(from->get_grund_hang()); end_list.insert(pos); } else { part_list.insert(pos); } if( from->kann_alle_obj_entfernen(player) ) { return "Der Tunnel ist nicht frei!\n"; } ribi_t::ribi waytype_ribi = ribi_t::none; if( wegtyp == powerline_wt ) { if( from->get_leitung() ) { waytype_ribi = from->get_leitung()->get_ribi(); } } else { waytype_ribi = from->get_weg_ribi_unmasked(delete_wegtyp); } if( !remove_all && ribi_t::is_threeway(waytype_ribi) ) { return "This tunnel branches. You can try Control+Click to remove."; } // Nachbarn raussuchen for(int r = 0; r < 4; r++) { if((zv == koord::invalid || zv == koord::nesw[r]) && from->get_neighbour(to, delete_wegtyp, ribi_t::nesw[r]) && !marker.is_marked(to) && (wegtyp != powerline_wt || to->get_leitung())) { tmp_list.insert(to->get_pos()); marker.mark(to); } } } while (!tmp_list.empty()); // Now we can delete the tunnel grounds while (!part_list.empty()) { pos = part_list.remove_first(); grund_t *gr = welt->lookup(pos); // remove the second way first in the tunnel if(gr->get_weg_nr(1)) { gr->remove_everything_from_way(player,gr->get_weg_nr(1)->get_waytype(),ribi_t::none); } gr->remove_everything_from_way(player,wegtyp,ribi_t::none); // removes stop and signals correctly // remove everything else gr->obj_loesche_alle(player); gr->mark_image_dirty(); welt->access(pos.get_2d())->boden_entfernen(gr); delete gr; minimap_t::get_instance()->calc_map_pixel( pos.get_2d() ); } // And now we can delete the tunnel ends while (!end_list.empty()) { pos = end_list.remove_first(); grund_t *gr = welt->lookup(pos); if(wegtyp == powerline_wt) { // remove tunnel portals tunnel_t *t = gr->find(); if(t) { t->cleanup(player); delete t; } if (leitung_t *lt = gr->get_leitung()) { // remove single powerlines if ( (lt->get_ribi() & ~ribi_type(gr->get_grund_hang())) == ribi_t::none ) { lt->cleanup(player); delete lt; } } } else { ribi_t::ribi mask = gr->get_grund_hang()!=slope_t::flat ? ~ribi_type(gr->get_grund_hang()) : ~ribi_type(slope_t::opposite(gr->get_weg_hang())); // remove the second way first in the tunnel if(gr->get_weg_nr(1)) { gr->remove_everything_from_way(player,gr->get_weg_nr(1)->get_waytype(),gr->get_weg_nr(1)->get_ribi_unmasked() & mask); } // removes single signals, bridge head, pedestrians, stops, changes catenary etc ribi_t::ribi ribi = gr->get_weg_ribi_unmasked(wegtyp) & mask; tunnel_t *t = gr->find(); uint8 broad_type = t->get_broad_type(); gr->remove_everything_from_way(player,wegtyp,ribi); // removes stop and signals correctly // remove tunnel portals t = gr->find(); if(t) { t->cleanup(player); delete t; } if( broad_type ) { slope_t::type hang = gr->get_grund_hang(); ribi_t::ribi dir = ribi_t::rotate90( ribi_type( hang ) ); if( broad_type & 1 ) { const grund_t *gr_l = welt->lookup(pos + dir); tunnel_t* tunnel_l = gr_l ? gr_l->find() : NULL; if( tunnel_l ) { tunnel_l->calc_image(); } } if( broad_type & 2 ) { const grund_t *gr_r = welt->lookup(pos - dir); tunnel_t* tunnel_r = gr_r ? gr_r->find() : NULL; if( tunnel_r ) { tunnel_r->calc_image(); } } } // corrects the ways weg_t *weg=gr->get_weg_nr(0); if(weg) { // fails if it was previously the last ribi weg->set_desc(weg->get_desc()); weg->set_ribi( ribi ); if(gr->get_weg_nr(1)) { gr->get_weg_nr(1)->set_ribi( ribi ); } } } // then add the new ground, copy everything and replace the old one grund_t *gr_new = new boden_t(pos, gr->get_grund_hang()); welt->access(pos.get_2d())->kartenboden_setzen(gr_new); if(gr_new->get_leitung()) { gr_new->get_leitung()->finish_rd(); } // recalc image of ground grund_t *kb = welt->access(pos.get_2d()+koord(gr_new->get_grund_hang()))->get_kartenboden(); kb->calc_image(); kb->set_flag(grund_t::dirty); } return NULL; } simutrans-124.5/src/simutrans/builder/tunnelbauer.h000066400000000000000000000033161520675460500225260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef BUILDER_TUNNELBAUER_H #define BUILDER_TUNNELBAUER_H #include "../simtypes.h" #include "../dataobj/koord.h" #include "../dataobj/koord3d.h" class karte_ptr_t; class player_t; class tunnel_desc_t; class way_desc_t; class tool_selector_t; /** * Baut Tunnel. Tunnel sollten nicht direkt instanziiert werden * sondern immer vom tunnel_builder_t erzeugt werden. * * Es gibt keine Instanz - nur statische Methoden. */ class tunnel_builder_t { private: static karte_ptr_t welt; static bool build_tunnel(player_t *player, koord3d pos, koord3d end, koord zv, const tunnel_desc_t *desc); static void build_tunnel_portal(player_t *player, koord3d end, koord zv, const tunnel_desc_t *desc, const way_desc_t *way_desc, sint64 &cost, bool connect_inside); tunnel_builder_t() {} // private -> no instance please public: static koord3d find_end_pos(player_t *player, koord3d pos, koord zv, const tunnel_desc_t *desc, bool full_tunnel=true, const char** msg=NULL); static void register_desc(tunnel_desc_t *desc); static const tunnel_desc_t *get_desc(const char *); static const tunnel_desc_t *get_tunnel_desc(const waytype_t wtyp, const sint32 min_speed,const uint16 time); static void fill_menu(tool_selector_t *tool_selector, const waytype_t wtyp, sint16 sound_ok); /** * Returns a list with available tunnel types. */ static const vector_tpl& get_available_tunnels(const waytype_t wtyp); static const char *build( player_t *player, koord pos, const tunnel_desc_t *desc, bool full_tunnel ); static const char *remove(player_t *player, koord3d pos, waytype_t wegtyp, bool all); }; #endif simutrans-124.5/src/simutrans/builder/vehikelbauer.cc000066400000000000000000000500441520675460500230060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../player/simplay.h" #include "../simdebug.h" #include "../utils/simrandom.h" #include "../simtypes.h" #include "../dataobj/environment.h" #include "../dataobj/tabfile.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../descriptor/vehicle_desc.h" #include "../gui/depot_frame.h" #include "vehikelbauer.h" #include "../tpl/stringhashtable_tpl.h" #include "../vehicle/air_vehicle.h" #include "../vehicle/rail_vehicle.h" #include "../vehicle/road_vehicle.h" #include "../vehicle/water_vehicle.h" const char* vehicle_builder_t::engine_type_names[9] = { "unknown", "steam", "diesel", "electric", "bio", "sail", "fuel_cell", "hydrogene", "battery" }; const char *vehicle_builder_t::vehicle_sort_by[vehicle_builder_t::sb_length] = { "Fracht", "Vehicle Name", "Capacity", "Price", "Cost", "Cost per unit", "Max. speed", "Vehicle Power", "Weight", "Intro. date", "Retire date" }; static stringhashtable_tpl name_fahrzeuge; // index 0 aur, 1...8 at normal waytype index #define GET_WAYTYPE_INDEX(wt) ((int)(wt)>8 ? 0 : (wt)) static slist_tpl typ_fahrzeuge[vehicle_builder_t::sb_length][9]; static uint8 tmp_sort_idx; class bonus_record_t { public: sint32 year; sint32 speed; bonus_record_t( sint32 y=0, sint32 kmh=0 ) { year = y*12; speed = kmh; } void rdwr(loadsave_t *file) { file->rdwr_long(year); file->rdwr_long(speed); } }; // speed bonus static vector_tplspeedbonus[8]; static sint32 default_speedbonus[8] = { 60, // road 80, // track 35, // water 350, // air 80, // monorail 200, // maglev 60, // tram 60 // narrowgauge }; bool vehicle_builder_t::speedbonus_init() { tabfile_t bonusconf; // first take user data, then user global data if (!bonusconf.open((env_t::pak_dir+"config/speedbonus.tab").c_str())) { dbg->warning("vehicle_builder_t::speedbonus_init()", "Can't read speedbonus.tab" ); return false; } tabfileobj_t contents; bonusconf.read(contents); /* init the values from line with the form year, speed, year, speed * must be increasing order! */ for( int j=0; j<8; j++ ) { vector_tpl tracks = contents.get_ints(weg_t::waytype_to_string(j==3?air_wt:(waytype_t)(j+1))); if((tracks.get_count() % 2) != 0) { dbg->warning( "vehicle_builder_t::speedbonus_init()", "Ill formed line in speedbonus.tab\nFormat is year,speed[,year,speed]!" ); tracks.pop_back(); } speedbonus[j].reserve( tracks.get_count()/2 ); for( uint32 i=0; i const& b = speedbonus[typ]; if (!b.empty()) { uint i=0; while (i < b.get_count() && monthyear >= b[i].year) { i++; } if (i == b.get_count()) { // maxspeed already? return b[i - 1].speed; } else if(i==0) { // minspeed below return b[0].speed; } else { // interpolate linear sint32 const delta_speed = b[i].speed - b[i - 1].speed; sint32 const delta_years = b[i].year - b[i - 1].year; return delta_speed * (monthyear - b[i - 1].year) / delta_years + b[i - 1].speed; } } else { sint32 speed_sum = 0; sint32 num_averages = 0; // needs to do it the old way => iterate over all vehicles with this type ... for(vehicle_desc_t const* const info : typ_fahrzeuge[0][GET_WAYTYPE_INDEX(wt)]) { if( info->get_power()>0 && info->is_available(monthyear) ) { speed_sum += info->get_topspeed(); num_averages ++; } } if( num_averages>0 ) { return speed_sum / num_averages; } } // no vehicles => return default return default_speedbonus[typ]; } void vehicle_builder_t::rdwr_speedbonus(loadsave_t *file) { for( int j=0; j<8; j++ ) { uint32 count = speedbonus[j].get_count(); file->rdwr_long(count); if (file->is_loading()) { speedbonus[j].clear(); speedbonus[j].reserve(count); } for(uint32 i=0; iis_loading()) { speedbonus[j].append(bonus_record_t()); } speedbonus[j][i].rdwr(file); } } } vehicle_t* vehicle_builder_t::build(koord3d k, player_t* player, convoi_t* cnv, const vehicle_desc_t* vb ) { vehicle_t* v; switch (vb->get_waytype()) { case road_wt: v = new road_vehicle_t( k, vb, player, cnv); break; case monorail_wt: v = new monorail_vehicle_t(k, vb, player, cnv); break; case track_wt: case tram_wt: v = new rail_vehicle_t( k, vb, player, cnv); break; case water_wt: v = new water_vehicle_t( k, vb, player, cnv); break; case air_wt: v = new air_vehicle_t( k, vb, player, cnv); break; case maglev_wt: v = new maglev_vehicle_t( k, vb, player, cnv); break; case narrowgauge_wt:v = new narrowgauge_vehicle_t(k, vb, player, cnv); break; default: dbg->fatal("vehicle_builder_t::build()", "cannot built a vehicle with waytype %i", vb->get_waytype()); } player->book_new_vehicle(-vb->get_price(), k.get_2d(), vb->get_waytype() ); return v; } bool vehicle_builder_t::register_desc(const vehicle_desc_t *desc) { // register waytype list const int wt_idx = GET_WAYTYPE_INDEX( desc->get_waytype() ); // first hashtable vehicle_desc_t const *old_desc = name_fahrzeuge.get( desc->get_name() ); if( old_desc ) { name_fahrzeuge.remove( desc->get_name() ); } name_fahrzeuge.put(desc->get_name(), desc); // now add it to sorter (may be more than once!) for( int sort_idx = 0; sort_idx < vehicle_builder_t::sb_length; sort_idx++ ) { if( old_desc ) { typ_fahrzeuge[sort_idx][wt_idx].remove(old_desc); } typ_fahrzeuge[sort_idx][wt_idx].append(desc); } // we cannot delete old_desc, since then xref-resolving will crash return true; } // compare funcions to sort vehicle in the list static int compare_freight(const vehicle_desc_t* a, const vehicle_desc_t* b) { int cmp = a->get_freight_type()->get_catg() - b->get_freight_type()->get_catg(); if (cmp != 0) return cmp; if (a->get_freight_type()->get_catg() == 0) { cmp = a->get_freight_type()->get_index() - b->get_freight_type()->get_index(); } return cmp; } static int compare_capacity(const vehicle_desc_t* a, const vehicle_desc_t* b) { return a->get_capacity() - b->get_capacity(); } static int compare_engine(const vehicle_desc_t* a, const vehicle_desc_t* b) { return (a->get_capacity() + a->get_power() == 0 ? (uint8)vehicle_desc_t::steam : a->get_engine_type()) - (b->get_capacity() + b->get_power() == 0 ? (uint8)vehicle_desc_t::steam : b->get_engine_type()); } static int compare_price(const vehicle_desc_t* a, const vehicle_desc_t* b) { return sgn(a->get_price() - b->get_price()); } static int compare_cost(const vehicle_desc_t* a, const vehicle_desc_t* b) { return sgn(a->get_running_cost() - b->get_running_cost()); } static int compare_cost_per_unit(const vehicle_desc_t* a, const vehicle_desc_t* b) { return sgn(a->get_running_cost()*b->get_capacity() - b->get_running_cost()*a->get_capacity()); } static int compare_topspeed(const vehicle_desc_t* a, const vehicle_desc_t* b) {return a->get_topspeed() - b->get_topspeed();} static int compare_power(const vehicle_desc_t* a, const vehicle_desc_t* b) {return (a->get_power() == 0 ? 0x7FFFFFF : a->get_power()) - (b->get_power() == 0 ? 0x7FFFFFF : b->get_power());} static int compare_weight(const vehicle_desc_t* a, const vehicle_desc_t* b) {return a->get_weight() - b->get_weight();} static int compare_intro_year_month(const vehicle_desc_t* a, const vehicle_desc_t* b) {return a->get_intro_year_month() - b->get_intro_year_month();} static int compare_retire_year_month(const vehicle_desc_t* a, const vehicle_desc_t* b) {return a->get_retire_year_month() - b->get_retire_year_month();} // default compare function with mode parameter bool vehicle_builder_t::compare_vehicles(const vehicle_desc_t* a, const vehicle_desc_t* b, sort_mode_t mode ) { int cmp = 0; switch (mode) { case sb_freight: cmp = compare_freight(a, b); if (cmp != 0) return cmp < 0; break; case sb_name: cmp = strcmp(translator::translate(a->get_name()), translator::translate(b->get_name())); if (cmp != 0) return cmp < 0; break; case sb_capacity: cmp = compare_capacity(a, b); if (cmp != 0) return cmp < 0; break; case sb_price: cmp = compare_price(a, b); if (cmp != 0) return cmp < 0; // fall-through case sb_cost: cmp = compare_cost(a, b); if (cmp != 0) return cmp < 0; cmp = compare_price(a, b); if (cmp != 0) return cmp < 0; break; case sb_cost_per_unit: cmp = compare_cost_per_unit(a,b); if (cmp != 0) return cmp < 0; break; case sb_speed: cmp = compare_topspeed(a, b); if (cmp != 0) return cmp < 0; break; case sb_weight: cmp = compare_weight(a, b); if (cmp != 0) return cmp < 0; break; case sb_power: cmp = compare_power(a, b); if (cmp != 0) return cmp < 0; break; case sb_intro_date: cmp = compare_intro_year_month(a, b); if (cmp != 0) return cmp < 0; // fall-through case sb_retire_date: cmp = compare_retire_year_month(a, b); if (cmp != 0) return cmp < 0; cmp = compare_intro_year_month(a, b); if (cmp != 0) return cmp < 0; break; default: ; } // Sort by: // 1. cargo category // 2. cargo (if special freight) // 3. engine_type // 4. speed // 5. power // 6. intro date // 7. name cmp = compare_freight(a, b); if (cmp != 0) return cmp < 0; cmp = compare_capacity(a, b); if (cmp != 0) return cmp < 0; cmp = compare_engine(a, b); if (cmp != 0) return cmp < 0; cmp = compare_topspeed(a, b); if (cmp != 0) return cmp < 0; cmp = compare_power(a, b); if (cmp != 0) return cmp < 0; cmp = compare_intro_year_month(a, b); if (cmp != 0) return cmp < 0; cmp = strcmp(translator::translate(a->get_name()), translator::translate(b->get_name())); return cmp < 0; } static bool compare( const vehicle_desc_t* a, const vehicle_desc_t* b ) { return vehicle_builder_t::compare_vehicles( a, b, (vehicle_builder_t::sort_mode_t)tmp_sort_idx ); } bool vehicle_builder_t::successfully_loaded() { // first: check for bonus tables DBG_MESSAGE("vehicle_builder_t::sort_lists()","called"); for ( int sort_idx = 0; sort_idx < vehicle_builder_t::sb_length; sort_idx++ ) { for( int wt_idx=0; wt_idx<9; wt_idx++ ) { tmp_sort_idx = sort_idx; slist_tpl& typ_liste = typ_fahrzeuge[sort_idx][wt_idx]; uint count = typ_liste.get_count(); if (count == 0) { continue; } const vehicle_desc_t** const tmp = new const vehicle_desc_t*[count]; const vehicle_desc_t** const tmp_end = tmp + count; for( const vehicle_desc_t** tmpptr = tmp; tmpptr != tmp_end; tmpptr++ ) { *tmpptr = typ_liste.remove_first(); } std::sort(tmp, tmp_end, compare); for( const vehicle_desc_t** tmpptr = tmp; tmpptr != tmp_end; tmpptr++ ) { typ_liste.append(*tmpptr); } delete [] tmp; } } return true; } const vehicle_desc_t *vehicle_builder_t::get_info(const char *name) { return name_fahrzeuge.get(name); } slist_tpl const & vehicle_builder_t::get_info(waytype_t const typ, uint8 sortkey) { return typ_fahrzeuge[sortkey][GET_WAYTYPE_INDEX(typ)]; } /** * extended search for vehicles for AI * checks also timeline and constraints * tries to get best with but adds a little random action */ const vehicle_desc_t *vehicle_builder_t::vehicle_search( waytype_t wt, const uint16 month_now, const uint32 target_weight, const sint32 target_speed, const goods_desc_t * target_freight, bool include_electric, bool not_obsolete ) { const vehicle_desc_t *desc = NULL; sint32 desc_index = -100000; if( target_freight==NULL && target_weight==0 ) { // no power, no freight => no vehicle to search return NULL; } for(vehicle_desc_t const* const test_desc : typ_fahrzeuge[0][GET_WAYTYPE_INDEX(wt)]) { // no constricts allow for rail vehicles concerning following engines if(wt==track_wt && !test_desc->can_follow_any() ) { continue; } // do not buy incomplete vehicles if(wt==road_wt && !test_desc->can_lead(NULL)) { continue; } // engine, but not allowed to lead a convoi, or no power at all or no electrics allowed if(target_weight) { if(test_desc->get_power()==0 || !test_desc->can_follow(NULL) || (!include_electric && test_desc->get_engine_type()==vehicle_desc_t::electric) ) { continue; } } // check for waytype too if(test_desc->get_waytype()!=wt || test_desc->is_future(month_now) ) { continue; } if( not_obsolete && test_desc->is_retired(month_now) ) { // not using vintage cars here! continue; } uint32 power = (test_desc->get_power()*test_desc->get_gear())/64; if(target_freight) { // this is either a railcar/trailer or a truck/boat/plane if( test_desc->get_capacity()==0 || !test_desc->get_freight_type()->is_interchangeable(target_freight) ) { continue; } sint32 difference=0; // smaller is better // assign this vehicle if we have not found one yet, or we only found one too weak if( desc!=NULL ) { // is it cheaper to run? (this is most important) difference += (desc->get_capacity()*1000)/(1+desc->get_running_cost()) < (test_desc->get_capacity()*1000)/(1+test_desc->get_running_cost()) ? -20 : 20; if( target_weight>0 ) { // is it stronger? difference += (desc->get_power()*desc->get_gear())/64 < power ? -10 : 10; } // is it faster? (although we support only up to 120km/h for goods) difference += (desc->get_topspeed() < test_desc->get_topspeed())? -10 : 10; // is it cheaper? (not so important) difference += (desc->get_price() > test_desc->get_price())? -5 : 5; // add some malus for obsolete vehicles if(test_desc->is_retired(month_now)) { difference += 5; } } // ok, final check if( desc==NULL || difference<(int)simrand(25) ) { // then we want this vehicle! desc = test_desc; DBG_MESSAGE( "vehicle_builder_t::vehicle_search","Found car %s",desc->get_name()); } } else { // engine/tugboat/truck for trailer if( test_desc->get_capacity()!=0 || !test_desc->can_follow(NULL) ) { continue; } // finally, we might be able to use this vehicle sint32 speed = test_desc->get_topspeed(); uint32 max_weight = power/( (speed*speed)/2500 + 1 ); // we found a useful engine sint32 current_index = (power * 100) / (1 + test_desc->get_running_cost()) + test_desc->get_topspeed() - test_desc->get_weight() / 1000 - (sint32)(test_desc->get_price() / 25000); // too slow? if(speed < target_speed) { current_index -= 250; } // too weak to reach full speed? if( max_weight < target_weight+test_desc->get_weight()/1000 ) { current_index += max_weight - (sint32)(target_weight+test_desc->get_weight()/1000); } current_index += simrand(100); if( current_index > desc_index ) { // then we want this vehicle! desc = test_desc; desc_index = current_index; DBG_MESSAGE( "vehicle_builder_t::vehicle_search","Found engine %s",desc->get_name()); } } } // no vehicle found! if( desc==NULL ) { DBG_MESSAGE( "vehicle_builder_t::vehicle_search()","could not find a suitable vehicle! (speed %i, weight %i)",target_speed,target_weight); } return desc; } /** * extended search for vehicles for replacement on load time * tries to get best match (no random action) * if prev_desc==NULL, then the convoi must be able to lead a convoi */ const vehicle_desc_t *vehicle_builder_t::get_best_matching( waytype_t wt, const uint16 month_now, const uint32 target_weight, const uint32 target_power, const sint32 target_speed, const goods_desc_t * target_freight, bool not_obsolete, const vehicle_desc_t *prev_veh, bool is_last ) { const vehicle_desc_t *desc = NULL; sint32 desc_index = -100000; for(vehicle_desc_t const* const test_desc : typ_fahrzeuge[0][GET_WAYTYPE_INDEX(wt)]) { if(target_power>0 && test_desc->get_power()==0) { continue; } // will test for first (prev_veh==NULL) or matching following vehicle if(!test_desc->can_follow(prev_veh)) { continue; } // not allowed as last vehicle if(is_last && test_desc->get_trailer_count()>0 && test_desc->get_trailer(0)!=NULL ) { continue; } // not allowed as non-last vehicle if(!is_last && test_desc->get_trailer_count()==1 && test_desc->get_trailer(0)==NULL ) { continue; } // check for waytype too if(test_desc->get_waytype()!=wt || test_desc->is_future(month_now) ) { continue; } // ignore vehicles that need electrification if(test_desc->get_power()>0 && test_desc->get_engine_type()==vehicle_desc_t::electric) { continue; } // likely tender => replace with some engine ... if(target_freight==0 && target_weight==0) { if( test_desc->get_capacity()!=0 ) { continue; } } if( not_obsolete && test_desc->is_retired(month_now) ) { // not using vintage cars here! continue; } uint32 power = (test_desc->get_power()*test_desc->get_gear())/64; if(target_freight) { // this is either a railcar/trailer or a truck/boat/plane if( test_desc->get_capacity()==0 || !test_desc->get_freight_type()->is_interchangeable(target_freight) ) { continue; } sint32 difference=0; // smaller is better // assign this vehicle if we have not found one yet, or we only found one too weak if( desc!=NULL ) { // is it cheaper to run? (this is most important) difference += (desc->get_capacity()*1000)/(1+desc->get_running_cost()) < (test_desc->get_capacity()*1000)/(1+test_desc->get_running_cost()) ? -20 : 20; if( target_weight>0 ) { // is it stronger? difference += (desc->get_power()*desc->get_gear())/64 < power ? -10 : 10; } // is it faster? (although we support only up to 120km/h for goods) difference += (desc->get_topspeed() < test_desc->get_topspeed())? -10 : 10; // is it cheaper? (not so important) difference += (desc->get_price() > test_desc->get_price())? -5 : 5; // add some malus for obsolete vehicles if(test_desc->is_retired(month_now)) { difference += 5; } } // ok, final check if( desc==NULL || difference<12 ) { // then we want this vehicle! desc = test_desc; DBG_MESSAGE( "vehicle_builder_t::get_best_matching","Found car %s",desc->get_name()); } } else { // finally, we might be able to use this vehicle sint32 speed = test_desc->get_topspeed(); uint32 max_weight = power/( (speed*speed)/2500 + 1 ); // we found a useful engine sint32 current_index = (power * 100) / (1 + test_desc->get_running_cost()) + test_desc->get_topspeed() - (sint16)test_desc->get_weight() / 1000 - (sint32)(test_desc->get_price() / 25000); // too slow? if(speed < target_speed) { current_index -= 250; } // too weak to reach full speed? if( max_weight < target_weight+test_desc->get_weight()/1000 ) { current_index += max_weight - (sint32)(target_weight+test_desc->get_weight()/1000); } current_index += 50; if( current_index > desc_index ) { // then we want this vehicle! desc = test_desc; desc_index = current_index; DBG_MESSAGE( "vehicle_builder_t::get_best_matching","Found engine %s",desc->get_name()); } } } // no vehicle found! if( desc==NULL ) { DBG_MESSAGE( "vehicle_builder_t::get_best_matching()","could not find a suitable vehicle! (speed %i, weight %i)",target_speed,target_weight); } return desc; } sint32 vehicle_builder_t::get_fastest_vehicle_speed(waytype_t wt, uint16 const month_now, bool const use_timeline, bool const allow_obsolete) { sint32 fastest_speed = 0; for(vehicle_desc_t const* const vehicle_descriptor : typ_fahrzeuge[0][GET_WAYTYPE_INDEX(wt)]) { if (vehicle_descriptor->get_power() == 0 || (use_timeline && ( vehicle_descriptor->is_future(month_now) || (!allow_obsolete && vehicle_descriptor->is_retired(month_now))))) { continue; } sint32 const speed = vehicle_descriptor->get_topspeed(); if (fastest_speed < speed) { fastest_speed = speed; } } return fastest_speed; } simutrans-124.5/src/simutrans/builder/vehikelbauer.h000066400000000000000000000047701520675460500226550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef BUILDER_VEHIKELBAUER_H #define BUILDER_VEHIKELBAUER_H #include "../dataobj/koord3d.h" #include "../simtypes.h" #include class vehicle_t; class player_t; class convoi_t; class vehicle_desc_t; class goods_desc_t; template class slist_tpl; /** * Baut Fahrzeuge. Fahrzeuge sollten nicht direct instanziiert werden * sondern immer von einem vehicle_builder_t erzeugt werden. */ class vehicle_builder_t { public: // sorting categories enum sort_mode_t { sb_freight, sb_name, sb_capacity, sb_price, sb_cost, sb_cost_per_unit, sb_speed, sb_power, sb_weight, sb_intro_date, sb_retire_date, sb_length }; static const char *vehicle_sort_by[sb_length]; // default compare function static bool compare_vehicles( const vehicle_desc_t* a, const vehicle_desc_t* b, sort_mode_t mode ); static const char *engine_type_names[9]; static bool speedbonus_init(); static sint32 get_speedbonus( sint32 monthyear, waytype_t wt ); static void rdwr_speedbonus(loadsave_t *file); static bool register_desc(const vehicle_desc_t *desc); static bool successfully_loaded(); static vehicle_t* build(koord3d k, player_t* player, convoi_t* cnv, const vehicle_desc_t* vb ); static const vehicle_desc_t * get_info(const char *name); static slist_tpl const& get_info(waytype_t, uint8 sortkey = vehicle_builder_t::sb_name); /** extended search for vehicles for AI */ static const vehicle_desc_t *vehicle_search(waytype_t typ,const uint16 month_now,const uint32 target_power,const sint32 target_speed, const goods_desc_t * target_freight, bool include_electric, bool not_obsolete ); /* for replacement during load time * prev_veh==NULL equals leading of convoi */ static const vehicle_desc_t *get_best_matching( waytype_t wt, const uint16 month_now, const uint32 target_weight, const uint32 target_power, const sint32 target_speed, const goods_desc_t * target_freight, bool not_obsolete, const vehicle_desc_t *prev_veh, bool is_last ); /* Resolves the fastest achieveable speed in km/h for the specified way type. * This is the speed of the fastest powered vehicle for the way type that is available within the time constraints. * This does not factor in limits applied by logically dependant vehicles or buildable ways. */ static sint32 get_fastest_vehicle_speed(waytype_t wt, uint16 const month_now, bool const use_timeline, bool const allow_obsolete); }; #endif simutrans-124.5/src/simutrans/builder/wegbauer.cc000066400000000000000000003101251520675460500221400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../world/simworld.h" #include "../tool/simtool.h" #include "../simhalt.h" #include "../simmesg.h" #include "../simintr.h" #include "../player/simplay.h" #include "../world/simplan.h" #include "../obj/depot.h" #include "wegbauer.h" #include "brueckenbauer.h" #include "tunnelbauer.h" #include "../descriptor/way_desc.h" #include "../descriptor/tunnel_desc.h" #include "../descriptor/building_desc.h" #include "../descriptor/crossing_desc.h" #include "../obj/way/strasse.h" #include "../obj/way/schiene.h" #include "../obj/way/monorail.h" #include "../obj/way/maglev.h" #include "../obj/way/narrowgauge.h" #include "../obj/way/kanal.h" #include "../obj/way/runway.h" #include "../ground/brueckenboden.h" #include "../ground/monorailboden.h" #include "../ground/tunnelboden.h" #include "../ground/grund.h" #include "../ground/wasser.h" #include "../dataobj/environment.h" #include "../dataobj/route.h" #include "../dataobj/marker.h" #include "../dataobj/translator.h" #include "../dataobj/scenario.h" #include "../utils/simrandom.h" // binary heap, since we only need insert and pop #include "../tpl/binary_heap_tpl.h" // fastest #include "../obj/field.h" #include "../obj/gebaeude.h" #include "../obj/bruecke.h" #include "../obj/tunnel.h" #include "../obj/crossing.h" #include "../obj/leitung2.h" #include "../obj/groundobj.h" #include "../obj/wayobj.h" #include "../vehicle/simtestdriver.h" #include "../tpl/array_tpl.h" #include "../tpl/stringhashtable_tpl.h" #include "../gui/minimap.h" // for debugging #include "../gui/tool_selector.h" #include "../gui/messagebox.h" #ifdef DEBUG_ROUTES #include "../sys/simsys.h" #endif // #define AUTOMATIC_BRIDGES // build bridges automatically // #define AUTOMATIC_TUNNELS // build tunnels automatically #define REVERSE_CALC_ROUTE_TOO // lookup also return route and take the better of the two karte_ptr_t way_builder_t::welt; const way_desc_t *way_builder_t::leitung_desc = NULL; static stringhashtable_tpl desc_table; static void set_default(way_desc_t const*& def, waytype_t const wtyp, systemtype_t const system_type = type_flat, sint32 const speed_limit = 1) { def = way_builder_t::weg_search(wtyp, speed_limit, 0, system_type); if (def == NULL) { def = way_builder_t::weg_search(wtyp, 1, 0, type_all); } } bool way_builder_t::successfully_loaded() { // some defaults to avoid hardcoded values set_default(strasse_t::default_strasse, road_wt, type_flat, 50); if( strasse_t::default_strasse == NULL ) { dbg->fatal( "way_builder_t::successfully_loaded()", "No road found at all!" ); } set_default(schiene_t::default_schiene, track_wt, type_flat, 80); set_default(monorail_t::default_monorail, monorail_wt, type_elevated); // Only elevated? set_default(maglev_t::default_maglev, maglev_wt, type_elevated); // Only elevated? set_default(narrowgauge_t::default_narrowgauge, narrowgauge_wt); set_default(kanal_t::default_kanal, water_wt, type_all); // Also find hidden rivers. set_default(runway_t::default_runway, air_wt, type_runway); set_default(way_builder_t::leitung_desc, powerline_wt); return true; } bool way_builder_t::register_desc(way_desc_t *desc) { if( const way_desc_t *old_desc = desc_table.remove(desc->get_name()) ) { tool_t::general_tool.remove( old_desc->get_builder() ); delete old_desc->get_builder(); delete old_desc; } if( desc->get_cursor()->get_image_id(1)!=IMG_EMPTY ) { // add the tool tool_build_way_t *tool = new tool_build_way_t(); tool->set_icon( desc->get_cursor()->get_image_id(1) ); tool->cursor = desc->get_cursor()->get_image_id(0); tool->set_default_param(desc->get_name()); tool_t::general_tool.append( tool ); desc->set_builder( tool ); } else { desc->set_builder( NULL ); } desc_table.put(desc->get_name(), desc); return true; } const vector_tpl& way_builder_t::get_way_list(const waytype_t wtyp, systemtype_t styp) { static vector_tpl dummy; dummy.clear(); const uint16 time = welt->get_timeline_year_month(); for(auto const& i : desc_table) { way_desc_t const* const test = i.value; if (test->get_wtyp()==wtyp && test->get_styp()== styp && test->is_available(time) && test->get_builder()) { dummy.append(test); } } return dummy; } /** * Finds a way with a given speed limit for a given waytype * It finds: * - the slowest way, as fast as speed limit * - if no way faster than speed limit, the fastest way. * The timeline is also respected. */ const way_desc_t* way_builder_t::weg_search(const waytype_t wtyp, const sint32 speed_limit, const uint16 time, const systemtype_t system_type) { const way_desc_t* best = NULL; bool best_allowed = false; // Does the best way fulfil the timeline? for(auto const& iter : desc_table) { way_desc_t const* const test = iter.value; if( ((test->get_wtyp()==wtyp && (test->get_styp()==system_type || system_type==type_all)) || (test->get_wtyp()==track_wt && test->get_styp()==type_tram && wtyp==tram_wt)) && test->get_cursor()->get_image_id(1)!=IMG_EMPTY ) { const bool test_allowed = test->get_intro_year_month()<=time && timeget_retire_year_month(); if( !best_allowed || time==0 || test_allowed ) { if( best==NULL || ( best->get_topspeed() < test->get_topspeed() && test->get_topspeed() <= speed_limit ) || // closer to desired speed (from the low end) ( speed_limit < best->get_topspeed() && test->get_topspeed() < best->get_topspeed()) || // respects speed_limit better ( time!=0 && !best_allowed && test_allowed) // current choice is actually not really allowed, timewise ) { best = test; best_allowed = test_allowed; } } } } return best; } const way_desc_t *way_builder_t::get_earliest_way(const waytype_t wtyp) { const way_desc_t *desc = NULL; for(auto const &iter : desc_table) { way_desc_t const *const test = iter.value; if( test->get_wtyp()==wtyp && (desc==NULL || test->get_intro_year_month()get_intro_year_month()) ) { desc = test; } } return desc; } const way_desc_t *way_builder_t::get_latest_way(const waytype_t wtyp) { const way_desc_t *desc = NULL; for(auto const& iter : desc_table) { way_desc_t const* const test = iter.value; if( test->get_wtyp()==wtyp && (desc==NULL || test->get_retire_year_month()>desc->get_retire_year_month()) ) { desc = test; } } return desc; } // ture if the way is available with timely bool way_builder_t::waytype_available( const waytype_t wtyp, uint16 time ) { if( time==0 ) { return true; } for(auto const& i : desc_table) { way_desc_t const* const test = i.value; if( test->get_wtyp()==wtyp && test->get_intro_year_month()<=time && test->get_retire_year_month()>time ) { return true; } } return false; } const way_desc_t *way_builder_t::get_desc(const char * way_name, const uint16 time) { //DBG_MESSAGE("way_builder_t::get_desc","return desc for %s in (%i)",way_name, time/12); const way_desc_t *desc = desc_table.get(way_name); if( desc && desc->is_available(time) ) { return desc; } return NULL; } // generates timeline message void way_builder_t::new_month() { const uint16 current_month = welt->get_timeline_year_month(); if(current_month!=0) { // check, what changed slist_tpl matching; for(auto const& i : desc_table) { way_desc_t const* const desc = i.value; cbuffer_t buf; const uint16 intro_month = desc->get_intro_year_month(); if(intro_month == current_month) { buf.printf( translator::translate("way %s now available:\n"), translator::translate(desc->get_name()) ); welt->get_message()->add_message(buf,koord3d::invalid,message_t::new_vehicle,NEW_VEHICLE,desc->get_image_id(5,0)); } const uint16 retire_month = desc->get_retire_year_month(); if(retire_month == current_month) { buf.printf( translator::translate("way %s cannot longer used:\n"), translator::translate(desc->get_name()) ); welt->get_message()->add_message(buf,koord3d::invalid,message_t::new_vehicle,NEW_VEHICLE,desc->get_image_id(5,0)); } } } } static bool compare_ways(const way_desc_t* a, const way_desc_t* b) { int cmp = a->get_topspeed() - b->get_topspeed(); if(cmp==0) { cmp = (int)a->get_intro_year_month() - (int)b->get_intro_year_month(); } if(cmp==0) { cmp = strcmp(a->get_name(), b->get_name()); } return cmp<0; } /** * Fill menu with icons of given waytype, return number of added entries */ void way_builder_t::fill_menu(tool_selector_t *tool_selector, const waytype_t wtyp, const systemtype_t styp, sint16 /*ok_sound*/) { // check if scenario forbids this const waytype_t rwtyp = wtyp!=track_wt || styp!=type_tram ? wtyp : tram_wt; if (!welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_WAY | GENERAL_TOOL, rwtyp, 0)) { return; } bool enable = welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_WAY | GENERAL_TOOL, rwtyp, 0); const uint16 time = welt->get_timeline_year_month(); // list of matching types (sorted by speed) vector_tpl matching; for(auto const &iter : desc_table) { way_desc_t const* const desc = iter.value; if ( desc->get_styp()==styp && desc->get_wtyp()==wtyp && desc->get_builder() && desc->is_available(time) ) { if (welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_WAY | GENERAL_TOOL, rwtyp, desc->get_name())) { matching.append(desc); } } } std::sort(matching.begin(), matching.end(), compare_ways); // now add sorted ways for(way_desc_t const* const i : matching) { i->get_builder()->enabled = enable && welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_WAY | GENERAL_TOOL, rwtyp, i->get_name()); tool_selector->add_tool_selector(i->get_builder()); } } /// allow for railroad crossing bool way_builder_t::check_crossing(const koord zv, const grund_t *bd, const way_desc_t *desc, const player_t *player) const { const waytype_t wtyp = desc->get_waytype(); // nothing to cross here if (!bd->hat_wege()) { return true; } // no triple crossings please if (bd->has_two_ways() && !bd->hat_weg(wtyp)) { return false; } const weg_t *w = bd->get_weg_nr(0); const uint8 iwtyp = w->get_waytype() != wtyp; // index of our wtype at the tile (must exist due to triple-crossing-check above) // get the other way if(iwtyp==0) { w = bd->get_weg_nr(1); // no other way here if (w==NULL) { return true; } } // special case: tram track on road if(!w->needs_crossing(desc)) { return true; } // right owner of the other way // exception: allow if we want to build road and road already exists, since this is passable for us if(!check_owner(w->get_owner(),player) && ! (wtyp==road_wt && bd->has_two_ways()) ) { return false; } // check for existing crossing crossing_t *cr = bd->find(); if (cr) { // index of the waytype in ns-direction at the crossing const uint8 ns_way = cr->get_dir(); // only cross with the right direction return (ns_way==iwtyp ? ribi_t::is_straight_ns(ribi_type(zv)) : ribi_t::is_straight_ew(ribi_type(zv))); } // no crossings in tunnels if((bautyp & tunnel_flag)!=0 || bd->ist_tunnel()) { return false; } // no crossings on elevated ways if((bautyp & elevated_flag)!=0 || bd->get_typ()==grund_t::monorailboden) { return false; } // crossing available and ribis ok bool has_logic = crossing_logic_t::get_crossing(wtyp, w->get_waytype(), desc ? desc->get_topspeed() : 0, w->get_max_speed(), welt->get_timeline_year_month()); if (!has_logic && w->get_waytype()==water_wt && (!player || player->is_public_service())) { has_logic = crossing_logic_t::get_crossing(wtyp, w->get_waytype(), 25, w->get_max_speed(), welt->get_timeline_year_month()); } if(has_logic) { const ribi_t::ribi w_ribi = w->get_ribi_unmasked(); // it is our way we want to cross: can we build a crossing here? // both ways must be straight and no ends return ribi_t::is_straight(w_ribi) && !ribi_t::is_single(w_ribi) && ribi_t::is_straight(ribi_type(zv)) && (w_ribi&ribi_type(zv))==0; } // cannot build crossing here return false; } /** crossing of powerlines, or no powerline */ bool way_builder_t::check_powerline(const koord zv, const grund_t *bd) const { if(zv==koord(0,0)) { return true; } leitung_t* lt = bd->find(); if(lt!=NULL) { ribi_t::ribi lt_ribi = lt->get_ribi(); // it is our way we want to cross: can we build a crossing here? // both ways must be straight and no ends return ribi_t::is_straight(lt_ribi) && !ribi_t::is_single(lt_ribi) && ribi_t::is_straight(ribi_type(zv)) && (lt_ribi&ribi_type(zv))==0 && !bd->ist_tunnel(); } // check for transformer if (bd->find() != NULL || bd->find() != NULL) { return false; } // ok, there is not high power transmission stuff going on here return true; } // allowed slope? bool way_builder_t::check_slope( const grund_t *from, const grund_t *to ) { const koord from_pos=from->get_pos().get_2d(); const koord to_pos=to->get_pos().get_2d(); const koord zv=to_pos-from_pos; if( !desc->has_double_slopes() && ( (from->get_weg_hang() && !is_one_high(from->get_weg_hang())) || (to->get_weg_hang() && !is_one_high(to->get_weg_hang())) ) ) { return false; } if(from==to) { if(!slope_t::is_way(from->get_weg_hang())) { return false; } } else { if(from->get_weg_hang()!=slope_t::flat && ribi_t::doubles(ribi_type(from->get_weg_hang()))!=ribi_t::doubles(ribi_type(zv))) { return false; } if(to->get_weg_hang()!=slope_t::flat && ribi_t::doubles(ribi_type(to->get_weg_hang()))!=ribi_t::doubles(ribi_type(zv))) { return false; } } return true; } // allowed owner? bool way_builder_t::check_owner( const player_t *player1, const player_t *player2 ) const { // unowned, mine or public property or superuser ... ? return player1==NULL || player1==player2 || player1==welt->get_public_player() || player2==welt->get_public_player(); } /// do not go through depots, station buildings etc. /// direction results from layout bool way_builder_t::check_building( const grund_t *to, const koord dir ) const { if( dir==koord(0,0) || to->first_no_way_obj()==0 ) { return true; } // first find all kind of buildings gebaeude_t *building = to->find(); if(building==NULL) { // but depots might be overlooked ... depot_t* depot = to->get_depot(); // no road to tram depot and vice-versa if (depot) { if ( (waytype_t)(bautyp&bautyp_mask) != depot->get_waytype() ) { return false; } } building = depot; } // check, if we may enter if(building) { // now check for directions uint8 layouts = building->get_tile()->get_desc()->get_all_layouts(); uint8 layout = building->get_tile()->get_layout(); ribi_t::ribi r = ribi_type(dir); if( layouts&1 ) { return false; } if( layouts==4 ) { return r == ribi_t::layout_to_ribi[layout]; } return ribi_t::is_straight( r | ribi_t::doubles(ribi_t::layout_to_ribi[layout&1]) ); } return true; } // helper function for building parallel ways bool way_builder_t::has_neighbour_with_way(koord3d pos, waytype_t wt) const { for (int i = 0; i < 8; i++) { if (grund_t* gr = welt->lookup(pos + koord::neighbours[i])) { if (gr->get_weg(wt)) { return true; } } } return false; } /** * This is the core routine for the way search * it will check * A) allowed step * B) if allowed, calculate the cost for the step from from to to */ bool way_builder_t::is_allowed_step(const grund_t *from, const grund_t *to, sint32 *costs, bool is_upperlayer ) { const koord from_pos = from->get_pos().get_2d(); const koord to_pos = to->get_pos().get_2d(); const koord zv = to_pos-from_pos; // fake empty elevated tiles static monorailboden_t from_dummy(koord3d::invalid, slope_t::flat); static monorailboden_t to_dummy(koord3d::invalid, slope_t::flat); if (desc == NULL) { return false; } if(bautyp==luft && (from->get_grund_hang()+to->get_grund_hang()!=0 || (from->hat_wege() && from->hat_weg(air_wt)==0) || (to->hat_wege() && to->hat_weg(air_wt)==0))) { // absolutely no slopes for runways, neither other ways return false; } bool to_flat = false; // to tile will be flattened if(from==to) { if((bautyp&tunnel_flag) && !slope_t::is_way(from->get_weg_hang())) { return false; } } else { // check slopes bool ok_slope = from->get_weg_hang() == slope_t::flat || ribi_t::doubles(ribi_type(from->get_weg_hang()))==ribi_t::doubles(ribi_type(zv)); ok_slope &= to->get_weg_hang() == slope_t::flat || ribi_t::doubles(ribi_type(to->get_weg_hang()))==ribi_t::doubles(ribi_type(zv)); // try terraforming if (!ok_slope) { uint8 dummy,to_slope; if ( (bautyp & terraform_flag) != 0 && from->ist_natur() && to->ist_natur() && check_terraforming(from,to,&dummy,&to_slope) ) { to_flat = to_slope == slope_t::flat; } else { // slopes not ok and no terraforming possible return false; } } } // ok, slopes are ok bool ok = true; // check scenario conditions if (welt->get_scenario()->is_work_allowed_here(player_builder, (bautyp&tunnel_flag ? TOOL_BUILD_TUNNEL : TOOL_BUILD_WAY)|GENERAL_TOOL, bautyp&bautyp_mask, desc->get_name(), to->get_pos()) != NULL) { return false; } // universal check for elevated things ... if(bautyp & elevated_flag) { if( is_upperlayer ) { // check if both are elevated grounds with the same waytype if( (to->get_typ() != grund_t::monorailboden || to->obj_count() == 0 || to->obj_bei(0)->get_waytype() != desc->get_wtyp() || !check_owner(to->obj_bei(0)->get_owner(),player_builder) ) || (from->get_typ() != grund_t::monorailboden || from->obj_count() == 0 || from->obj_bei(0)->get_waytype() != desc->get_wtyp() || !check_owner(from->obj_bei(0)->get_owner(),player_builder) ) ) { return false; } } else { if( to->hat_weg(air_wt) || welt->lookup_hgt( to_pos ) < welt->get_water_hgt( to_pos ) || !check_powerline( zv, to ) || (!to->ist_karten_boden() && to->get_typ() != grund_t::monorailboden) || to->get_typ() == grund_t::brueckenboden || to->get_typ() == grund_t::tunnelboden ) { // no suitable ground below! return false; } gebaeude_t *gb = to->find(); if(gb==NULL) { // but depots might be overlooked ... gb = to->get_depot(); } if(gb) { // no halt => citybuilding => do not touch // also check for too high buildings ... if(!check_owner(gb->get_owner(),player_builder) || gb->get_tile()->get_background(0,1,0) != IMG_EMPTY) { return false; } // building above houses is expensive ... avoid it! *costs += 4; } // absolutely nothing allowed here for set which want double clearance if( welt->get_settings().get_way_height_clearance() == 2 && welt->lookup( to->get_pos()+koord3d(0,0,1) ) ) { return false; } // up to now 'to' and 'from' referred to the ground one height step below the elevated way // now get the grounds at the right height koord3d pos = to->get_pos() + koord3d( 0, 0, welt->get_settings().get_way_height_clearance() ); grund_t *to2 = welt->lookup(pos); if(to2) { if(to2->get_weg_nr(0)) { // already an elevated ground here => it will have always a way object, that indicates ownership ok = to2->get_typ() == grund_t::monorailboden && check_owner(to2->obj_bei(0)->get_owner(),player_builder); ok &= to2->get_weg_nr(0)->get_desc()->get_wtyp() == desc->get_wtyp(); } else { ok = to2->find()==NULL; } if (!ok) { return false; } to = to2; } else { // simulate empty elevated tile to_dummy.set_pos(pos); to_dummy.set_grund_hang(to->get_grund_hang()); to = &to_dummy; } pos = from->get_pos() + koord3d( 0, 0, welt->get_settings().get_way_height_clearance() ); grund_t *from2 = welt->lookup(pos); if(from2) { from = from2; } else { // simulate empty elevated tile from_dummy.set_pos(pos); from_dummy.set_grund_hang(from->get_grund_hang()); from = &from_dummy; } // now 'from' and 'to' point to grounds at the right height } } if( welt->get_settings().get_way_height_clearance()==2 ) { // cannot build if conversion factor 2, we aren't powerline and way with maximum speed > 0 or powerline 1 tile below grund_t *to2 = welt->lookup( to->get_pos() + koord3d(0, 0, -1) ); if( to2 && (((bautyp&bautyp_mask)!=leitung && to2->get_weg_nr(0) && to2->get_weg_nr(0)->get_desc()->get_topspeed()>0) || to2->get_leitung()) ) { return false; } // tiles directly above cannot have way unless it is a powerline bridge to2 = welt->lookup( to->get_pos() + koord3d(0, 0, 1) ); if( to2 && (to2->find() == NULL || to2->get_typ() != grund_t::brueckenboden) ) { return false; } } if ((zv.x | zv.y) != 0) { // the forrowing checks make only sense if we actually connect two tiles // universal check for depots/stops/... if( !check_building( from, zv ) || !check_building( to, -zv ) ) { warn_fail = translator::translate("A building blocks the construction"); return false; } // universal check for bridges: enter bridges in bridge direction if( from->get_typ()==grund_t::brueckenboden ) { if (weg_t *w = from->get_weg((waytype_t)(bautyp_mask & bautyp))) { if (ribi_t::doubles(ribi_t::ribi(ribi_type(zv))) != ribi_t::doubles(w->get_ribi_unmasked())) { return false; } } if ((bautyp_mask & bautyp)==leitung && from->get_leitung()) { if (ribi_t::doubles(ribi_t::ribi(ribi_type(zv))) != ribi_t::doubles(from->get_leitung()->get_ribi())) { return false; } } } if( to->get_typ()==grund_t::brueckenboden ) { if (weg_t* w = to->get_weg((waytype_t)(bautyp_mask & bautyp))) { if (ribi_t::doubles(ribi_t::ribi(ribi_type(zv))) != ribi_t::doubles(w->get_ribi_unmasked())) { return false; } } if ((bautyp_mask & bautyp) == leitung && to->get_leitung()) { if (ribi_t::doubles(ribi_t::ribi(ribi_type(zv))) != ribi_t::doubles(to->get_leitung()->get_ribi())) { return false; } } } } // universal check: do not switch to tunnel through cliffs! if( from->get_typ() == grund_t::tunnelboden && to->get_typ() != grund_t::tunnelboden && !from->ist_karten_boden() ) { return false; } if( to->get_typ() == grund_t::tunnelboden && from->get_typ() != grund_t::tunnelboden && !to->ist_karten_boden() ) { return false; } // do not connect to the side of a sloped elevated way if the ground is flat if (from->get_typ() == grund_t::monorailboden && to->get_typ() != grund_t::monorailboden && !from->ist_karten_boden()) { // we try to connect to an elevated way. For bridges, only allowed, if both are parallel if (to->get_typ() == grund_t::brueckenboden) { weg_t* w = to->get_weg(desc->get_wtyp()); if (!w || ribi_t::doubles(ribi_t::ribi(ribi_type(zv))) != ribi_t::doubles(w->get_ribi_unmasked()) ) { // we are not allowed to connect here return false; } } } if (to->get_typ() == grund_t::monorailboden && from->get_typ() != grund_t::monorailboden && !to->ist_karten_boden()) { // we try to connect to an elevated way. For bridges, only allowed, if both are parallel if (from->get_typ() == grund_t::brueckenboden) { weg_t* w = to->get_weg(desc->get_wtyp()); if (!w || ribi_t::doubles(ribi_t::ribi(ribi_type(zv))) != ribi_t::doubles(w->get_ribi_unmasked())) { // we are not allowed to connect here return false; } } } // universal check for crossings if (to!=from && (bautyp&bautyp_mask)!=leitung) { if(!check_crossing(zv,to,desc,player_builder) || !check_crossing(-zv,from, desc,player_builder)) { warn_fail = translator::translate("No suitable crossing"); return false; } } // universal check for building under powerlines if ((bautyp&bautyp_mask)!=leitung) { if (!check_powerline(zv,to) || !check_powerline(-zv,from)) { return false; } } warn_fail = NULL; bool fundament = to->get_typ()==grund_t::fundament; // now check way specific stuff settings_t const& s = welt->get_settings(); switch(bautyp&bautyp_mask) { case strasse: { const weg_t *str=to->get_weg(road_wt); // we allow connection to any road ok = (str || !fundament) && !to->is_water(); if(!ok) { return false; } // check for end/start of bridge or tunnel // fail if no proper way exists, or the way's ribi are not 0 and are not matching the slope type ribi_t::ribi test_ribi = (str ? str->get_ribi_unmasked() : 0) | ribi_type(zv); if(to->get_weg_hang()!=to->get_grund_hang() && (str==NULL || !(ribi_t::is_straight(test_ribi) || test_ribi==0 ))) { return false; } // test if we are next to a way if (!str && prefer_parallel) { if (to->get_weg(road_wt)) { // we have to join? make it expensive *costs = s.way_count_leaving_way; } else if (has_neighbour_with_way(from->get_pos(), road_wt)) { // we are parallel => make it cheap *costs = s.way_count_straight; } else { // new way not parallel to an existing *costs = s.way_count_no_way; } } else { // calculate costs *costs = (!str ^ prefer_parallel) ? s.way_count_no_way : s.way_count_straight; } if((!str && to->hat_wege()) || (str && to->has_two_ways())) { *costs += s.way_count_avoid_crossings; // avoid crossings } if (to->get_weg_hang() != 0 && !to_flat) { *costs += s.way_count_slope; } } break; case schiene: default: { waytype_t wt = desc->get_wtyp(); const weg_t *sch=to->get_weg(wt); // extra check for AI construction (not adding to existing tracks!) if((bautyp&bot_flag)!=0 && (sch || to->get_halt().is_bound())) { return false; } // ok, regular construction here // if no way there: check for right ground type, otherwise check owner ok = sch==NULL ? (!fundament && !to->is_water()) : check_owner(sch->get_owner(),player_builder); if(!ok) { return false; } // check for end/start of bridge or tunnel // fail if no proper way exists, or the way's ribi are not 0 and are not matching the slope type ribi_t::ribi test_ribi = (sch ? sch->get_ribi_unmasked() : 0) | ribi_type(zv); if(to->get_weg_hang()!=to->get_grund_hang() && (sch==NULL || !(ribi_t::is_straight(test_ribi) || test_ribi==0 ))) { return false; } // test if we are next to a way if (!sch && prefer_parallel) { if (to->get_weg(wt)) { // we have to join? make it expensive *costs = s.way_count_leaving_way; } else if (has_neighbour_with_way(from->get_pos(), wt)) { // we are parallel => make it cheap *costs = s.way_count_straight; } else { // new way not parallel to an existing *costs = s.way_count_no_way; } } else { // calculate costs *costs = (!sch ^ prefer_parallel) ? s.way_count_no_way : s.way_count_straight; } if((sch && to->has_two_ways()) || (!sch && to->hat_wege())) { *costs += s.way_count_avoid_crossings; // avoid crossings } if(to->get_weg_hang()!=0 && !to_flat) { *costs += s.way_count_slope; } } break; case schiene_tram: // Dario: Tramway { const weg_t *sch=to->get_weg(track_wt); // roads are checked in check_crossing // if no way there: check for right ground type, otherwise check owner ok = sch==NULL ? (!fundament && !to->is_water()) : check_owner(sch->get_owner(),player_builder); if(ok) { // calculate costs *costs += sch ? s.way_count_straight : s.way_count_no_way; // prefer own track a little more if(to->hat_weg(road_wt)) { *costs += s.way_count_straight; } if(to->get_weg_hang()!=0 && !to_flat) { *costs += s.way_count_slope; } } } break; case leitung: ok = !to->is_water() && (to->get_weg(air_wt)==NULL); ok &= !(to->ist_tunnel() && to->hat_wege()); if(to->get_weg_nr(0)!=NULL) { // only 90 deg crossings, only a single way ribi_t::ribi w_ribi= to->get_weg_nr(0)->get_ribi_unmasked(); ok &= ribi_t::is_straight(w_ribi) && !ribi_t::is_single(w_ribi) && ribi_t::is_straight(ribi_type(zv)) && (w_ribi&ribi_type(zv))==0; } if(to->has_two_ways()) { // only 90 deg crossings, only for trams ... ribi_t::ribi w_ribi= to->get_weg_nr(1)->get_ribi_unmasked(); ok &= ribi_t::is_straight(w_ribi) && !ribi_t::is_single(w_ribi) && ribi_t::is_straight(ribi_type(zv)) && (w_ribi&ribi_type(zv))==0; } // do not connect to other powerlines { leitung_t *lt = to->get_leitung(); ok &= (lt==NULL) || check_owner(player_builder, lt->get_owner()); } if(to->get_typ()!=grund_t::tunnelboden) { // only fields are allowed if(to->get_typ()==grund_t::fundament) { ok &= to->find()!=NULL; } // no bridges and monorails here in the air apart from another powerline bridge if (grund_t* to2 = welt->lookup(to->get_pos() + koord3d(0,0,1))) { ok &= to2->find() && to2->get_typ() == grund_t::brueckenboden; } } // calculate costs if(ok) { *costs = s.way_count_straight; // extra malus for not following an existing line if( from->get_leitung() && !to->get_leitung() ) { *costs += s.way_count_leaving_way; } // avoid crossings if (to->hat_wege()) { *costs += s.way_count_avoid_crossings; } } break; case wasser: { const weg_t *canal = to->get_weg(water_wt); // if no way there: check for right ground type, otherwise check owner ok = canal || !fundament; // calculate costs if(ok) { if (to->is_water()) { *costs = 0; // prefer water very much } else if (canal) { *costs = s.way_count_straight; // next prefer existing canal } else { *costs = s.way_count_leaving_way; // new canal very expensive } if(to->get_weg_hang()!=0 && !to_flat) { *costs += s.way_count_slope * 2; } } break; } case river: if( to->is_water() ) { ok = true; // do not care while in ocean *costs = 1; } else { // only downstream ok = from->get_pos().z>=to->get_pos().z && (to->hat_weg(water_wt) || !to->hat_wege()); // calculate costs if(ok) { // prefer existing rivers: *costs = to->hat_weg(water_wt) ? 10 : 10+simrand(s.way_count_90_curve); if(to->get_weg_hang()!=0 && !to_flat) { *costs += s.way_count_slope * 10; } } } break; case luft: // runway { const weg_t *w = to->get_weg(air_wt); if( w && w->get_desc()->get_styp()==type_runway && desc->get_styp()!=type_runway && ribi_t::is_single(w->get_ribi_unmasked()) ) { // cannot go over the end of a runway with a taxiway return false; } ok = !to->is_water() && (w || !to->hat_wege()) && to->find()==NULL && !fundament; // calculate costs *costs = s.way_count_straight; } break; } return ok; } bool way_builder_t::check_terraforming( const grund_t *from, const grund_t *to, uint8* new_from_slope, uint8* new_to_slope) const { // only for normal green tiles const slope_t::type from_slope = from->get_weg_hang(); const slope_t::type to_slope = to->get_weg_hang(); const sint8 from_hgt = from->get_hoehe(); const sint8 to_hgt = to->get_hoehe(); // we may change slope of a tile if it is sloped already if( (from_slope == slope_t::flat || from->get_hoehe() == welt->get_water_hgt( from->get_pos().get_2d() )) && (to_slope == slope_t::flat || to->get_hoehe() == welt->get_water_hgt( to->get_pos().get_2d() )) ) { return false; } else if( abs( from_hgt - to_hgt ) <= (desc->has_double_slopes() ? 2 : 1) ) { // extra check for double heights if( abs( from_hgt - to_hgt) == 2 && (welt->lookup( from->get_pos() - koord3d(0,0,2) ) != NULL || welt->lookup( from->get_pos() + koord3d(0, 0, 2) ) != NULL || welt->lookup( to->get_pos() - koord3d(0, 0, 2) ) != NULL || welt->lookup( to->get_pos() + koord3d(0, 0, 2) ) != NULL) ) { return false; } // monorail above / tunnel below if (welt->lookup(from->get_pos() - koord3d(0,0,1))!=NULL || welt->lookup(from->get_pos() + koord3d(0,0,1))!=NULL || welt->lookup(to->get_pos() - koord3d(0,0,1))!=NULL || welt->lookup(to->get_pos() + koord3d(0,0,1))!=NULL) { return false; } // can safely change slope of at least one of the tiles if (new_from_slope == NULL) { return true; } // now calculate new slopes assert(new_from_slope); assert(new_to_slope); // direction of way const koord dir = (to->get_pos() - from->get_pos()).get_2d(); sint8 start = from_hgt * 2; sint8 middle = from_hgt * 2; sint8 end = to_hgt * 2; // get 3 heights - start (min start from, but should be same), middle (average end from/average start to), end (min end to) if( dir == koord::north ) { start += corner_sw(from_slope) + corner_se(from_slope); middle += corner_ne(from_slope) + corner_nw(from_slope); end += corner_ne(to_slope) + corner_nw(to_slope); } else if( dir == koord::east ) { start += corner_sw(from_slope) + corner_nw(from_slope); middle += corner_se(from_slope) + corner_ne(from_slope); end += corner_se(to_slope) + corner_ne(to_slope); } else if( dir == koord::south ) { start += corner_ne(from_slope) + corner_nw(from_slope); middle += corner_sw(from_slope) + corner_se(from_slope); end += corner_sw(to_slope) + corner_se(to_slope); } else if( dir == koord::west ) { start += corner_se(from_slope) + corner_ne(from_slope); middle += corner_sw(from_slope) + corner_nw(from_slope); end += corner_sw(to_slope) + corner_nw(to_slope); } // work out intermediate height: if( end == start ) { middle = start; } else { // end < start to ist wegbar?assert from ist_wegbar:middle = end + 1 // end > start to ist wegbar?assert from ist_wegbar:middle = end - 1 if( !slope_t::is_way( to_slope ) ) { middle = (start + end) / 2; } } // prevent middle being invalid if( middle >> 1 > from_hgt + 2 ) { middle = (from_hgt + 2) * 2; } if( middle >> 1 > to_hgt + 2 ) { middle = (to_hgt + 2) * 2; } if( middle >> 1 < from_hgt ) { middle = from_hgt * 2; } if( middle >> 1 < to_hgt ) { middle = to_hgt * 2; } const uint8 m_from = (middle >> 1) - from_hgt; const uint8 m_to = (middle >> 1) - to_hgt; // write middle heights if( dir == koord::north ) { *new_from_slope = encode_corners(corner_sw(from_slope), corner_se(from_slope), m_from, m_from); *new_to_slope = encode_corners(m_to, m_to, corner_ne(to_slope), corner_nw(to_slope)); } else if( dir == koord::east ) { *new_from_slope = encode_corners(corner_sw(from_slope), m_from, m_from, corner_nw(from_slope)); *new_to_slope = encode_corners(m_to, corner_se(to_slope), corner_ne(to_slope), m_to); } else if( dir == koord::south ) { *new_from_slope = encode_corners(m_from, m_from, corner_ne(from_slope), corner_nw(from_slope)); *new_to_slope = encode_corners(corner_sw(to_slope), corner_se(to_slope), m_to, m_to); } else if( dir == koord::west ) { *new_from_slope = encode_corners(m_from, corner_se(from_slope), corner_ne(from_slope), m_from); *new_to_slope = encode_corners(corner_sw(to_slope), m_to, m_to, corner_nw(to_slope)); } return true; } return false; } void way_builder_t::do_terraforming() { uint32 last_terraformed = terraform_index.get_count(); for(uint32 const i : terraform_index) { // index in route grund_t *from = welt->lookup(route[i]); uint8 from_slope = from->get_grund_hang(); grund_t *to = welt->lookup(route[i+1]); uint8 to_slope = to->get_grund_hang(); // calculate new slopes check_terraforming(from, to, &from_slope, &to_slope); bool changed = false; // change slope of from if( from_slope != from->get_grund_hang() ) { if( from_slope == slope_t::all_up_two ) { from->set_hoehe( from->get_hoehe() + 2 ); from->set_grund_hang( slope_t::flat ); route[i].z = from->get_hoehe(); } else if( from_slope != slope_t::all_up_one ) { // bit of a hack to recognise single height slopes shifted up 1 if( from_slope > slope_t::all_up_one && slope_t::is_single( from_slope-slope_t::all_up_one ) ) { from->set_hoehe( from->get_hoehe() + 1 ); from_slope -= slope_t::all_up_one; route[i].z = from->get_hoehe(); } from->set_grund_hang( from_slope ); } else { from->set_hoehe( from->get_hoehe() + 1 ); from->set_grund_hang( slope_t::flat ); route[i].z = from->get_hoehe(); } changed = true; if (last_terraformed != i) { // charge player player_t::book_construction_costs(player_builder, welt->get_settings().cst_set_slope, from->get_pos().get_2d(), ignore_wt); } } // change slope of to if( to_slope != to->get_grund_hang() ) { if( to_slope == slope_t::all_up_two ) { to->set_hoehe( to->get_hoehe() + 2 ); to->set_grund_hang( slope_t::flat ); route[i + 1].z = to->get_hoehe(); } else if( to_slope != slope_t::all_up_one ) { // bit of a hack to recognise single height slopes shifted up 1 if( to_slope > slope_t::all_up_one && slope_t::is_single( to_slope-slope_t::all_up_one ) ) { to->set_hoehe( to->get_hoehe() + 1 ); to_slope -= slope_t::all_up_one; route[i + 1].z = to->get_hoehe(); } to->set_grund_hang(to_slope); } else { to->set_hoehe( to->get_hoehe() + 1); to->set_grund_hang(slope_t::flat); route[i+1].z = to->get_hoehe(); } changed = true; // charge player player_t::book_construction_costs(player_builder, welt->get_settings().cst_set_slope, to->get_pos().get_2d(), ignore_wt); last_terraformed = i+1; // do not pay twice for terraforming one tile } // recalc slope image of neighbors if (changed) { for(uint8 j=0; j<2; j++) { for(uint8 x=0; x<2; x++) { for(uint8 y=0; y<2; y++) { grund_t *gr = welt->lookup_kartenboden(route[i+j].get_2d()+koord(x,y)); if (gr) { gr->calc_image(); } } } } } } } void way_builder_t::check_for_bridge(const grund_t* parent_from, const grund_t* from, const vector_tpl &ziel) { // wrong starting slope or tile already occupied with a way ... if (!slope_t::is_way(from->get_grund_hang())) { return; } /* * now check existing ways: * no tunnels/bridges at crossings and no track tunnels/bridges on roads (but road tunnels/bridges on tram are allowed). * (keep in mind, that our waytype isn't currently on the tile and will be built later) */ const weg_t *way0 = from->get_weg_nr(0); const weg_t *way1 = from->get_weg_nr(1); if( way0 ) { switch( bautyp&bautyp_mask ) { case schiene_tram: case strasse: { const weg_t *other = way1; if ( way0->get_waytype() != desc->get_wtyp() ) { if ( way1 ) { // two different ways return; } other = way0; } if ( other ) { if ( (bautyp&bautyp_mask) == strasse ) { if ( other->get_waytype() != track_wt || other->get_desc()->get_styp()!=type_tram ) { // road only on tram return; } } else { if ( other->get_waytype() != road_wt ) { // tram only on road return; } } } } /* FALLTHROUGH */ default: if (way0->get_waytype()!=desc->get_wtyp() || way1!=NULL) { // no other ways allowed return; } } } const koord zv=from->get_pos().get_2d()-parent_from->get_pos().get_2d(); const ribi_t::ribi ribi = ribi_type(zv); // now check ribis of existing ways const ribi_t::ribi wayribi = way0 ? way0->get_ribi_unmasked() | (way1 ? way1->get_ribi_unmasked() : (ribi_t::ribi)ribi_t::none) : (ribi_t::ribi)ribi_t::none; if ( wayribi & (~ribi) ) { // curves at bridge start return; } // ok, so now we do a closer investigation if( bridge_desc && ( ribi_type(from->get_grund_hang()) == ribi_t::backward(ribi_type(zv)) || from->get_grund_hang() == 0 )) { // Try a bridge. const sint32 cost_difference = desc->get_maintenance() > 0 ? (bridge_desc->get_maintenance() * 4l + 3l) / desc->get_maintenance() : 16; // try eight possible lengths .. uint32 min_length = 1; for (uint8 i = 0; i < 8 && min_length <= welt->get_settings().way_max_bridge_len; ++i) { sint8 bridge_height; const grund_t* gr_end = welt->lookup_kartenboden(from->get_pos().get_2d() + zv * i); if (!gr_end) { // not on map any more break; } koord3d end = gr_end->get_pos(); const char *error = bridge_builder_t::can_build_bridge( player_builder, from->get_pos(), end, bridge_height, bridge_desc); if(error) { // no valid end point found min_length++; continue; } uint32 length = koord_distance(from->get_pos(), end); if(!ziel.is_contained(end) && bridge_builder_t::check_start_tile(player_builder, gr_end, ribi_type(-zv), bridge_desc)) { // If there is a slope on the starting tile, it's taken into account in is_allowed_step, but a bridge will be flat! sint8 num_slopes = (from->get_grund_hang() == slope_t::flat) ? 1 : -1; // On the end tile, we haven't to subtract way_count_slope, since is_allowed_step isn't called with this tile. num_slopes += (gr_end->get_grund_hang() == slope_t::flat) ? 1 : 0; next_gr.append(next_gr_t(welt->lookup(end), length * cost_difference + num_slopes*welt->get_settings().way_count_slope, build_straight | build_tunnel_bridge)); min_length = length+1; } else { break; } } return; } if( tunnel_desc && ribi_type(from->get_grund_hang()) == ribi_type(zv) ) { // uphill hang ... may be tunnel? const sint32 cost_difference = desc->get_maintenance() > 0 ? (tunnel_desc->get_maintenance() * 4l + 3l) / desc->get_maintenance() : 16; koord3d end = tunnel_builder_t::find_end_pos( player_builder, from->get_pos(), zv, tunnel_desc); if( end != koord3d::invalid && !ziel.is_contained(end) ) { uint32 length = koord_distance(from->get_pos(), end); next_gr.append(next_gr_t(welt->lookup(end), length * cost_difference, build_straight | build_tunnel_bridge )); return; } } } way_builder_t::way_builder_t(player_t* player) : next_gr(32) , player_builder(player) , bautyp(strasse) // kann mit init_builder() gesetzt werden , keep_existing_ways(false) , keep_existing_faster_ways(false) , keep_existing_city_roads(false) , prefer_parallel(false) , build_sidewalk(false) { maximum = welt->get_settings().way_count_maximum; // building cost, (curves etc.) } /** * If a way is built on top of another way, should the type * of the former way be kept or replaced (true == keep) */ void way_builder_t::set_keep_existing_ways(bool yesno) { keep_existing_ways = yesno; keep_existing_faster_ways = false; } void way_builder_t::set_keep_existing_faster_ways(bool yesno) { keep_existing_ways = false; keep_existing_faster_ways = yesno; } void way_builder_t::init_builder(bautyp_t wt, const way_desc_t *b, const tunnel_desc_t *tunnel, const bridge_desc_t *br) { bautyp = wt; desc = b; bridge_desc = br; tunnel_desc = tunnel; if(wt&tunnel_flag && tunnel==NULL) { dbg->fatal("way_builder_t::init_builder()","needs a tunnel description for an underground route!"); } if((wt&bautyp_mask)==luft) { wt &= bautyp_mask | bot_flag; } if(player_builder==NULL) { bridge_desc = NULL; tunnel_desc = NULL; } else if( bautyp != river ) { #ifdef AUTOMATIC_BRIDGES if(!bridge_desc) { bridge_desc = bridge_builder_t::find_bridge(b->get_wtyp(), 25, welt->get_timeline_year_month()); } #endif #ifdef AUTOMATIC_TUNNELS if(!tunnel_desc) { tunnel_desc = tunnel_builder_t::get_tunnel_desc(b->get_wtyp(), 25, welt->get_timeline_year_month()); } #endif } DBG_MESSAGE("way_builder_t::init_builder()", "setting way type to %d, desc=%s, bridge_desc=%s, tunnel_desc=%s", bautyp, desc ? desc->get_name() : "NULL", bridge_desc ? bridge_desc->get_name() : "NULL", tunnel_desc ? tunnel_desc->get_name() : "NULL" ); } static void get_mini_maxi( const vector_tpl &ziel, koord3d &mini, koord3d &maxi ) { mini = maxi = ziel[0]; for(koord3d const& current : ziel) { if ( current.x < mini.x ) { mini.x = current.x; } else if( current.x > maxi.x ) { maxi.x = current.x; } if ( current.y < mini.y ) { mini.y = current.y; } else if( current.y > maxi.y ) { maxi.y = current.y; } if ( current.z < mini.z ) { mini.z = current.z; } else if( current.z > maxi.z ) { maxi.z = current.z; } } } /** * this routine uses A* to calculate the best route * beware: change the cost and you will mess up the system! * (but you can try, look at simuconf.tab) */ sint32 way_builder_t::intern_calc_route(const vector_tpl &start, const vector_tpl &ziel) { assert((get_random_mode() & SYNC_STEP_RANDOM) == 0); // we clear it here probably twice: does not hurt ... route.clear(); terraform_index.clear(); // check for existing koordinates bool has_target_ground = false; for(koord3d const& i : ziel) { has_target_ground |= welt->lookup(i) != 0; } if( !has_target_ground ) { return -1; } // check scenario conditions for start and endpoint for (koord3d const& pos : start) { if (welt->get_scenario()->is_work_allowed_here(player_builder, TOOL_BUILD_WAY | GENERAL_TOOL, bautyp & bautyp_mask, desc->get_name(), pos) != NULL) { return -1; } } for (koord3d const& pos : ziel) { if (welt->get_scenario()->is_work_allowed_here(player_builder, TOOL_BUILD_WAY | GENERAL_TOOL, bautyp & bautyp_mask, desc->get_name(), pos) != NULL) { return -1; } } // calculate the minimal cuboid containing 'ziel' koord3d mini, maxi; get_mini_maxi( ziel, mini, maxi ); // memory in static list ... if(route_t::nodes==NULL) { route_t::MAX_STEP = welt->get_settings().get_max_route_steps(); // may need very much memory => configurable route_t::nodes = new route_t::ANode[route_t::MAX_STEP+4+1]; } static binary_heap_tpl queue; // initialize marker field marker_t& marker = marker_t::instance(welt->get_size().x, welt->get_size().y); // clear the queue (should be empty anyhow) queue.clear(); // some thing for the search grund_t *to; koord3d gr_pos; // just the last valid pos ... route_t::ANode *tmp=NULL; uint32 step = 0; const grund_t* gr=NULL; for(koord3d const& i : start) { gr = welt->lookup(i); // is valid ground? sint32 dummy; if( !gr || !is_allowed_step(gr,gr,&dummy) ) { // DBG_MESSAGE("way_builder_t::intern_calc_route()","cannot start on (%i,%i,%i)",start.x,start.y,start.z); continue; } tmp = &(route_t::nodes[step]); step ++; tmp->parent = NULL; tmp->gr = gr; tmp->f = calc_distance(i, mini, maxi); tmp->g = 0; tmp->dir = 0; tmp->count = 0; queue.insert(tmp); } if( queue.empty() ) { // no valid ground to start. return -1; } INT_CHECK("wegbauer 347"); // get exclusively the tile list route_t::GET_NODE(); // to speed up search, but may not find all shortest ways uint32 min_dist = 99999999; //DBG_MESSAGE("route_t::itern_calc_route()","calc route from %d,%d,%d to %d,%d,%d",ziel.x, ziel.y, ziel.z, start.x, start.y, start.z); do { route_t::ANode *test_tmp = queue.pop(); if(marker.test_and_mark(test_tmp->gr)) { // we were already here on a faster route, thus ignore this branch // (trading speed against memory consumption) continue; } tmp = test_tmp; gr = tmp->gr; gr_pos = gr->get_pos(); #ifdef DEBUG_ROUTES DBG_DEBUG("insert to close","(%i,%i,%i) f=%i",gr->get_pos().x,gr->get_pos().y,gr->get_pos().z,tmp->f); #endif // already there? if (ziel.is_contained(gr_pos)) { // we added a target to the closed list: we are finished warn_fail = NULL; break; } // route costs too high => abort if (tmp->g > maximum) { warn_fail = "Route cost exceeded max_route_steps"; break; } // the four possible directions plus any additional stuff due to already existing brides plus new ones ... next_gr.clear(); // only one direction allowed ... const ribi_t::ribi straight_dir = tmp->parent!=NULL ? ribi_type(gr->get_pos() - tmp->parent->gr->get_pos()) : (ribi_t::ribi)ribi_t::all; // test directions // .. use only those that are allowed by current slope // .. do not go backward const ribi_t::ribi slope_dir = (slope_t::is_way_ns(gr->get_weg_hang()) ? ribi_t::northsouth : ribi_t::none) | (slope_t::is_way_ew(gr->get_weg_hang()) ? ribi_t::eastwest : ribi_t::none); const ribi_t::ribi test_dir = (tmp->count & build_straight)==0 ? slope_dir & ~ribi_t::backward(straight_dir) : straight_dir; // testing all four possible directions for(ribi_t::ribi r=1; (r&16)==0; r<<=1) { if((r & test_dir)==0) { // not allowed to go this direction continue; } bool do_terraform = false; const koord zv(r); if(!gr->get_neighbour(to,invalid_wt,r) || !check_slope(gr, to)) { // slopes do not match // terraforming enabled? if (bautyp==river || (bautyp & terraform_flag) == 0) { continue; } // check terraforming (but not in curves) if (gr->get_grund_hang()==0 || (tmp->parent!=NULL && tmp->parent->parent!=NULL && r==straight_dir)) { to = welt->lookup_kartenboden(gr->get_pos().get_2d() + zv); if (to==NULL || (check_slope(gr, to) && gr->get_vmove(r)!=to->get_vmove(ribi_t::backward(r)))) { continue; } else { do_terraform = true; } } else { continue; } } // something valid? if(marker.is_marked(to)) { continue; } sint32 new_cost = 0; bool is_ok = is_allowed_step(gr,to,&new_cost); if(is_ok) { // now add it to the array ... next_gr.append(next_gr_t(to, new_cost, do_terraform ? build_straight | terraform : 0)); } else if(tmp->parent!=NULL && r==straight_dir && (tmp->count & build_tunnel_bridge)==0) { // try to build a bridge or tunnel here, since we cannot go here ... check_for_bridge(tmp->parent->gr,gr,ziel); } } // now check all valid ones ... for(next_gr_t const& r : next_gr) { to = r.gr; if( to==NULL) { continue; } // new values for cost g uint32 new_g = tmp->g + r.cost; settings_t const& s = welt->get_settings(); // check for curves (usually, one would need the lastlast and the last; // if not there, then we could just take the last uint8 current_dir; if(tmp->parent!=NULL) { current_dir = ribi_type( tmp->parent->gr->get_pos(), to->get_pos() ); if(tmp->dir!=current_dir) { new_g += s.way_count_curve; if(tmp->parent->dir!=tmp->dir) { // discourage double turns new_g += s.way_count_double_curve; } else if(ribi_t::is_perpendicular(tmp->dir,current_dir)) { // discourage v turns heavily new_g += s.way_count_90_curve; } } else if(bautyp==leitung && ribi_t::is_bend(current_dir)) { new_g += s.way_count_double_curve; } // extra malus leave an existing road after only one tile waytype_t const wt = desc->get_wtyp(); if (tmp->parent->gr->hat_weg(wt) && !gr->hat_weg(wt) && to->hat_weg(wt)) { // but only if not straight track if(!ribi_t::is_straight(tmp->dir)) { new_g += s.way_count_leaving_way; } } } else { current_dir = ribi_type( gr->get_pos(), to->get_pos() ); } const uint32 new_dist = calc_distance( to->get_pos(), mini, maxi ); // special check for kinks at the end if(new_dist==0 && current_dir!=tmp->dir) { // discourage turn on last tile new_g += s.way_count_double_curve; } if (new_dist == 0 && r.flag & terraform) { // no terraforming near target continue; } if(new_distmin_dist+50) { // skip, if too far from current minimum tile // will not find some ways, but will be much faster ... // also it will avoid too big detours, which is probably also not the way, the builder intended continue; } const uint32 new_f = new_g+new_dist; if((step&0x03)==0) { INT_CHECK( "wegbauer 1347" ); #ifdef DEBUG_ROUTES if((step&1023)==0) {minimap_t::get_instance()->calc_map();} #endif } // not in there or taken out => add new route_t::ANode *k=&(route_t::nodes[step]); step++; k->parent = tmp; k->gr = to; k->g = new_g; k->f = new_f; k->dir = current_dir; // count is unused here, use it as flag-variable instead k->count = r.flag; queue.insert( k ); #ifdef DEBUG_ROUTES DBG_DEBUG("insert to open","(%i,%i,%i) f=%i",to->get_pos().x,to->get_pos().y,to->get_pos().z,k->f); #endif } } while (!queue.empty() && step < route_t::MAX_STEP); #ifdef DEBUG_ROUTES DBG_DEBUG("way_builder_t::intern_calc_route()","steps=%i (max %i) in route, open %i, cost %u",step,route_t::MAX_STEP,queue.get_count(),tmp->g); #endif INT_CHECK("wegbauer 194"); route_t::RELEASE_NODE(); // target reached? if( !ziel.is_contained(gr->get_pos()) || step>=route_t::MAX_STEP || tmp->parent==NULL || tmp->g > maximum ) { if (step>=route_t::MAX_STEP) { dbg->warning("way_builder_t::intern_calc_route()","Too many steps (%i>=max %i) in route (too long/complex)",step,route_t::MAX_STEP); } return -1; } else { const sint32 cost = tmp->g; // reached => construct route while(tmp != NULL) { route.append(tmp->gr->get_pos()); if (tmp->count & terraform) { terraform_index.append(route.get_count()-1); } tmp = tmp->parent; } return cost; } } void way_builder_t::intern_calc_straight_route(const koord3d start, const koord3d ziel) { const koord3d koordup(0, 0, welt->get_settings().get_way_height_clearance()); sint32 dummy_cost; const grund_t *test_bd = welt->lookup(start); bool ok = false; if (test_bd && is_allowed_step(test_bd,test_bd,&dummy_cost) ) { //there is a legal ground at the start ok = true; } if (ok && (bautyp&tunnel_flag) && !test_bd->ist_tunnel()) { return; // start tunnelbuilding in tunnels } if (bautyp&elevated_flag) { test_bd = welt->lookup(start + koordup); if (test_bd && is_allowed_step(test_bd,test_bd,&dummy_cost, true) ) { //there is a legal way at the upper layer of start ok = true; } } if (!ok) { //target is not suitable return; } test_bd = welt->lookup(ziel); // we have to reach target height if no tunnel building or (target ground does not exists or is underground). // in full underground mode if there is no tunnel under cursor, kartenboden gets selected const bool target_3d = (bautyp&tunnel_flag)==0 || test_bd==NULL || test_bd->ist_karten_boden(); if((bautyp&tunnel_flag)==0) { //same thing to the target point ok = false; if (test_bd && is_allowed_step(test_bd,test_bd,&dummy_cost) ) { //there is a legal ground at the target ok = true; } if (bautyp&elevated_flag) { test_bd = welt->lookup(ziel + koordup); if (test_bd && is_allowed_step(test_bd,test_bd,&dummy_cost, true) ) { //there is a legal way at the upper layer of the target ok = true; } } if(!ok) { return; } } koord3d pos=start; route.clear(); route.append(start); terraform_index.clear(); bool check_terraform = start.x==ziel.x || start.y==ziel.y; while(pos.get_2d()!=ziel.get_2d() && ok) { bool do_terraform = false; // shortest way ribi_t::ribi diff; if(abs(pos.x-ziel.x)>=abs(pos.y-ziel.y)) { diff = (pos.x>ziel.x) ? ribi_t::west : ribi_t::east; } else { diff = (pos.y>ziel.y) ? ribi_t::north : ribi_t::south; } if(bautyp&tunnel_flag) { // create fake tunnel grounds if needed bool bd_von_new = false, bd_nach_new = false; grund_t *bd_von = welt->lookup(pos); if( bd_von == NULL ) { bd_von = new tunnelboden_t(pos, slope_t::flat); bd_von_new = true; } // take care of slopes pos.z = bd_von->get_vmove(diff); // check next tile grund_t *bd_nach = welt->lookup(pos + diff); if( !bd_nach ) { // check for slope down ... bd_nach = welt->lookup(pos + diff + koord3d(0,0,-1)); bool is_two_levels_deep = false; if( !bd_nach ) { bd_nach = welt->lookup(pos + diff + koord3d(0,0,-2)); is_two_levels_deep = true; } if( bd_nach && (bd_nach->get_weg_hang() == slope_t::flat || (is_two_levels_deep && slope_t::max_diff(bd_nach->get_weg_hang()) <= 1)) ) { // Don't care about _flat_ tunnels below. // Also don't care about sloped tunnels, if they do not reach our height. bd_nach = NULL; } } if( bd_nach == NULL ){ bd_nach = new tunnelboden_t(pos + diff, slope_t::flat); bd_nach_new = true; } // check for tunnel and right slope ok = ok && bd_nach->ist_tunnel() && bd_nach->get_vmove(ribi_t::backward(diff))==pos.z; // all other checks are done here (crossings, stations etc) ok = ok && is_allowed_step(bd_von, bd_nach, &dummy_cost); // advance position pos = bd_nach->get_pos(); // check new tile: ground must be above tunnel and below sea grund_t *gr = welt->lookup_kartenboden(pos.get_2d()); ok = ok && (gr->get_hoehe() > pos.z) && (!gr->is_water() || (welt->lookup_hgt(pos.get_2d()) > pos.z) ); if (bd_von_new) { delete bd_von; } if (bd_nach_new) { delete bd_nach; } } else { grund_t *bd_von = welt->lookup(pos); ok = false; grund_t *bd_nach = NULL; if ( bd_von ) { if (bd_von->get_neighbour(bd_nach, invalid_wt, diff) && check_slope(bd_von, bd_nach)) { ok = true; } else { // slopes do not match // terraforming enabled? or able to follow upper layer? if ((bautyp==river || (bautyp & terraform_flag) == 0) && (bautyp&elevated_flag) == 0 ) { break; } // check terraforming (but not in curves) if (check_terraform) { bd_nach = welt->lookup_kartenboden(bd_von->get_pos().get_2d() + diff); if (bd_nach==NULL || (check_slope(bd_von, bd_nach) && bd_von->get_vmove(diff)!=bd_nach->get_vmove(ribi_t::backward(diff)))) { ok = false; } else { do_terraform = true; ok = true; } } } // allowed ground? ok = ok && bd_nach && is_allowed_step(bd_von,bd_nach,&dummy_cost); if (ok) { pos = bd_nach->get_pos(); } } // if failed if (!ok && bautyp&elevated_flag) { //search following the upper layer bd_von = welt->lookup(pos + koordup); if(bd_von && bd_von->get_neighbour(bd_nach, invalid_wt, diff) && check_slope(bd_von, bd_nach) && is_allowed_step(bd_von, bd_nach, &dummy_cost, true) ) { ok = true; pos = bd_nach->get_pos() - koordup; } } check_terraform = pos.x==ziel.x || pos.y==ziel.y; } route.append(pos); if (do_terraform) { terraform_index.append(route.get_count()-2); } DBG_MESSAGE("way_builder_t::calc_straight_route()","step %s = %i",koord(diff).get_str(),ok); } ok = ok && ( target_3d ? pos==ziel : pos.get_2d()==ziel.get_2d() ); // we can build a straight route? if(ok) { DBG_MESSAGE("way_builder_t::intern_calc_straight_route()","found straight route max_n=%i",get_count()-1); } else { route.clear(); terraform_index.clear(); } } /* this routine uses A* to calculate the best route * beware: change the cost and you will mess up the system! * (but you can try, look at simuconf.tab) */ /* It should not be river/airport/tunnel/bridge/terraformable */ sint32 way_builder_t::intern_calc_route_elevated(const koord3d start, const koord3d ziel) { // we clear it here probably twice: does not hurt ... route.clear(); terraform_index.clear(); const koord3d koordup(0, 0, welt->get_settings().get_way_height_clearance()); // check for existing koordinates const bool has_target_ground = welt->lookup(ziel) || welt->lookup(ziel + koordup); if( !has_target_ground ) { return -1; } // memory in static list ... if(route_t::nodes==NULL) { route_t::MAX_STEP = welt->get_settings().get_max_route_steps(); // may need very much memory => configurable route_t::nodes = new route_t::ANode[route_t::MAX_STEP+4+1]; } static binary_heap_tpl queue; // initialize marker field marker_t& markerbelow = marker_t::instance(welt->get_size().x, welt->get_size().y); marker_t& markerabove = marker_t::instance_second(welt->get_size().x, welt->get_size().y); // clear the queue (should be empty anyhow) queue.clear(); // some thing for the search grund_t *to; koord3d gr_pos; // just the last valid pos ... route_t::ANode *tmp=NULL; uint32 step = 0; const grund_t *gr=NULL, *gu = NULL; gr = welt->lookup(start); // is valid ground? sint32 dummy; if( gr && is_allowed_step(gr,gr,&dummy) ) { // DBG_MESSAGE("way_builder_t::intern_calc_route()","cannot start on (%i,%i,%i)",start.x,start.y,start.z); tmp = &(route_t::nodes[step]); step ++; tmp->parent = NULL; tmp->gr = gr; tmp->f = calc_distance(start, ziel, ziel); tmp->g = 0; tmp->dir = 0; tmp->count = 0; queue.insert(tmp); } gu = welt->lookup(start + koordup); if( gu && is_allowed_step(gu,gu,&dummy, true) ) { // DBG_MESSAGE("way_builder_t::intern_calc_route()","cannot start on (%i,%i,%i)",start.x,start.y,start.z); tmp = &(route_t::nodes[step]); step ++; tmp->parent = NULL; tmp->gr = gu; tmp->f = calc_distance(start, ziel, ziel); tmp->g = 0; tmp->dir = 0; tmp->count = is_upperlayer; queue.insert(tmp); } if( queue.empty() ) { // no valid ground to start. return -1; } INT_CHECK("wegbauer 347"); // get exclusively the tile list route_t::GET_NODE(); // to speed up search, but may not find all shortest ways uint32 min_dist = 99999999; //DBG_MESSAGE("route_t::itern_calc_route()","calc route from %d,%d,%d to %d,%d,%d",ziel.x, ziel.y, ziel.z, start.x, start.y, start.z); do { route_t::ANode *test_tmp = queue.pop(); if( (test_tmp->count&is_upperlayer?markerabove:markerbelow).test_and_mark(test_tmp->gr) ) { // we were already here on a faster route, thus ignore this branch // (trading speed against memory consumption) continue; } tmp = test_tmp; if(test_tmp->count & is_upperlayer) { gu = tmp->gr; gr_pos = gu->get_pos() - koordup; gr = welt->lookup(gr_pos); } else { gr = tmp->gr; gr_pos = gr->get_pos(); gu = welt->lookup(gr_pos + koordup); } #ifdef DEBUG_ROUTES DBG_DEBUG("insert to close","(%i,%i,%i) f=%i",gr->get_pos().x,gr->get_pos().y,gr->get_pos().z,tmp->f); #endif // already there if( ziel == gr_pos || tmp->g>maximum) { // we added a target to the closed list: we are finished break; } // the four possible directions plus any additional stuff due to already existing brides plus new ones ... next_gr.clear(); //search following the lower layer if(gr) { // only one direction allowed ... const ribi_t::ribi straight_dir = tmp->parent!=NULL ? ribi_type(gr->get_pos() - tmp->parent->gr->get_pos()) : (ribi_t::ribi)ribi_t::all; // test directions // .. use only those that are allowed by current slope // .. do not go backward const ribi_t::ribi slope_dir = (slope_t::is_way_ns(gr->get_weg_hang()) ? ribi_t::northsouth : ribi_t::none) | (slope_t::is_way_ew(gr->get_weg_hang()) ? ribi_t::eastwest : ribi_t::none); const ribi_t::ribi test_dir = (tmp->count & build_straight)==0 ? slope_dir & ~ribi_t::backward(straight_dir) : straight_dir; // testing all four possible directions for(ribi_t::ribi r=1; (r&16)==0; r<<=1) { if((r & test_dir)==0) { // not allowed to go this direction continue; } const koord zv(r); if(!gr->get_neighbour(to,invalid_wt,r) || !check_slope(gr, to)) { // slopes do not match continue; } // something valid? if(markerbelow.is_marked(to)) { continue; } sint32 new_cost = 0; bool is_ok = is_allowed_step(gr,to,&new_cost); if(is_ok) { // now add it to the array ... next_gr.append(next_gr_t(to, new_cost, 0)); } } } //search following the upper layer if(gu) { // only one direction allowed ... const ribi_t::ribi straight_dir = tmp->parent!=NULL ? ribi_type(gu->get_pos() - tmp->parent->gr->get_pos()) : (ribi_t::ribi)ribi_t::all; // test directions // .. use only those that are allowed by current slope // .. do not go backward const ribi_t::ribi slope_dir = (slope_t::is_way_ns(gu->get_weg_hang()) ? ribi_t::northsouth : ribi_t::none) | (slope_t::is_way_ew(gu->get_weg_hang()) ? ribi_t::eastwest : ribi_t::none); const ribi_t::ribi test_dir = (tmp->count & build_straight)==0 ? slope_dir & ~ribi_t::backward(straight_dir) : straight_dir; // testing all four possible directions for(ribi_t::ribi r=1; (r&16)==0; r<<=1) { if((r & test_dir)==0) { // not allowed to go this direction continue; } const koord zv(r); if(!gu->get_neighbour(to,invalid_wt,r) || !check_slope(gu, to)) { // slopes do not match continue; } // something valid? if(markerabove.is_marked(to)) { continue; } sint32 new_cost = 0; bool is_ok = is_allowed_step(gu,to,&new_cost, true); if(is_ok) { // now add it to the array ... next_gr.append(next_gr_t(to, new_cost, is_upperlayer)); } } } // now check all valid ones ... for(next_gr_t const& r : next_gr) { to = r.gr; if( to==NULL) { continue; } // new values for cost g uint32 new_g = tmp->g + r.cost; settings_t const& s = welt->get_settings(); // check for curves (usually, one would need the lastlast and the last; // if not there, then we could just take the last uint8 current_dir; if(tmp->parent!=NULL) { current_dir = ribi_type( tmp->parent->gr->get_pos(), to->get_pos() ); if(tmp->dir!=current_dir) { new_g += s.way_count_curve; if(tmp->parent->dir!=tmp->dir) { // discourage double turns new_g += s.way_count_double_curve; } else if(ribi_t::is_perpendicular(tmp->dir,current_dir)) { // discourage v turns heavily new_g += s.way_count_90_curve; } } else if(bautyp==leitung && ribi_t::is_bend(current_dir)) { new_g += s.way_count_double_curve; } // extra malus leave an existing road after only one tile waytype_t const wt = desc->get_wtyp(); if (tmp->parent->gr->hat_weg(wt) && !(gr? gr: gu)->hat_weg(wt) && to->hat_weg(wt)) { // but only if not straight track if(!ribi_t::is_straight(tmp->dir)) { new_g += s.way_count_leaving_way; } } } else { current_dir = ribi_type( gr_pos, to->get_pos() ); } const uint32 new_dist = calc_distance( to->get_pos(), ziel, ziel ); // special check for kinks at the end if(new_dist==0 && current_dir!=tmp->dir) { // discourage turn on last tile new_g += s.way_count_double_curve; } if(new_distmin_dist+50) { // skip, if too far from current minimum tile // will not find some ways, but will be much faster ... // also it will avoid too big detours, which is probably also not the way, the builder intended continue; } const uint32 new_f = new_g+new_dist; if((step&0x03)==0) { INT_CHECK( "wegbauer 1347" ); #ifdef DEBUG_ROUTES if((step&1023)==0) {minimap_t::get_instance()->calc_map();} #endif } // not in there or taken out => add new route_t::ANode *k=&(route_t::nodes[step]); step++; k->parent = tmp; k->gr = to; k->g = new_g; k->f = new_f; k->dir = current_dir; // count is unused here, use it as flag-variable instead k->count = r.flag; queue.insert( k ); #ifdef DEBUG_ROUTES DBG_DEBUG("insert to open","(%i,%i,%i) f=%i",to->get_pos().x,to->get_pos().y,to->get_pos().z,k->f); #endif } } while (!queue.empty() && step < route_t::MAX_STEP); #ifdef DEBUG_ROUTES DBG_DEBUG("way_builder_t::intern_calc_route()","steps=%i (max %i) in route, open %i, cost %u",step,route_t::MAX_STEP,queue.get_count(),tmp->g); #endif INT_CHECK("wegbauer 194"); route_t::RELEASE_NODE(); // target reached? if( !(ziel == gr_pos) || step>=route_t::MAX_STEP || tmp->parent==NULL || tmp->g > maximum ) { if (step>=route_t::MAX_STEP) { dbg->warning("way_builder_t::intern_calc_route()","Too many steps (%i>=max %i) in route (too long/complex)",step,route_t::MAX_STEP); } return -1; } else { const sint32 cost = tmp->g; // reached => construct route while(tmp != NULL) { if(tmp->count & is_upperlayer) { route.append(tmp->gr->get_pos() - koordup); } else { route.append(tmp->gr->get_pos() ); } tmp = tmp->parent; } return cost; } } // special for starting/landing runways bool way_builder_t::intern_calc_route_runways(koord3d start3d, const koord3d ziel3d) { route.clear(); terraform_index.clear(); const koord start=start3d.get_2d(); const koord ziel=ziel3d.get_2d(); // check for straight line! const ribi_t::ribi ribi = ribi_type( start, ziel ); if( !ribi_t::is_straight(ribi) ) { // only straight runways! warn_fail = "No curves on runways"; return false; } const ribi_t::ribi ribi_straight = ribi_t::doubles(ribi); // not too close to the border? if( !(welt->is_within_limits(start-koord(5,5)) && welt->is_within_limits(start+koord(5,5))) || !(welt->is_within_limits(ziel-koord(5,5)) && welt->is_within_limits(ziel+koord(5,5))) ) { if(player_builder==welt->get_active_player()) { create_win( new news_img("Zu nah am Kartenrand"), w_time_delete, magic_none); return false; } } // now try begin and endpoint const koord zv(ribi); // end start const grund_t *gr = welt->lookup_kartenboden(start); const weg_t *weg = gr->get_weg(air_wt); if(weg && !ribi_t::is_straight(weg->get_ribi()|ribi_straight) ) { // cannot connect with curve at the end return false; } if( weg && weg->get_desc()->get_styp()==type_flat ) { // could not continue taxiway with runway return false; } // check end gr = welt->lookup_kartenboden(ziel); weg = gr->get_weg(air_wt); if(weg && !ribi_t::is_straight(weg->get_ribi()|ribi_straight) ) { // cannot connect with curve at the end return false; } if( weg && weg->get_desc()->get_styp()==type_flat ) { // could not continue taxiway with runway return false; } // now try a straight line with no crossings and no curves at the end const int dist=koord_distance( ziel, start ); grund_t *from = welt->lookup_kartenboden(start); for( int i=0; i<=dist; i++ ) { grund_t *to = welt->lookup_kartenboden(start+zv*i); sint32 dummy; if (!is_allowed_step(from, to, &dummy)) { return false; } weg = to->get_weg(air_wt); if( weg && weg->get_desc()->get_styp()==type_runway && (ribi_t::is_threeway(weg->get_ribi_unmasked()|ribi_straight)) && (weg->get_ribi_unmasked()|ribi_straight)!=ribi_t::all ) { // only fourway crossings of runways allowed, no threeways => fail return false; } from = to; } // now we can build here route.clear(); terraform_index.clear(); route.reserve(dist + 2); for( int i=0; i<=dist; i++ ) { route.append(welt->lookup_kartenboden(start + zv * i)->get_pos()); } return true; } /* * calc_straight_route (maximum one curve, including diagonals) */ const char *way_builder_t::calc_straight_route(koord3d start, const koord3d ziel) { warn_fail = 0; DBG_MESSAGE("way_builder_t::calc_straight_route()","from %d,%d,%d to %d,%d,%d",start.x,start.y,start.z, ziel.x,ziel.y,ziel.z ); if(bautyp==luft && desc->get_styp()==type_runway) { // these are straight anyway ... intern_calc_route_runways(start, ziel); } else { intern_calc_straight_route(start,ziel); if (route.empty()) { intern_calc_straight_route(ziel,start); } } return warn_fail; } const char *way_builder_t::calc_route(const koord3d &start, const koord3d &ziel) { vector_tpl start_vec(1), ziel_vec(1); warn_fail = 0; start_vec.append(start); ziel_vec.append(ziel); calc_route(start_vec, ziel_vec); return warn_fail; } const char *way_builder_t::calc_route(const vector_tpl &start, const vector_tpl &ziel) { #ifdef DEBUG_ROUTES uint32 ms = dr_time(); #endif INT_CHECK("simbau 740"); warn_fail = 0; if(bautyp==luft && desc->get_styp()==type_runway) { assert( start.get_count() == 1 && ziel.get_count() == 1 ); intern_calc_route_runways(start[0], ziel[0]); } else if(bautyp==river) { assert( start.get_count() == 1 && ziel.get_count() == 1 ); // river only go downwards => start and end are clear ... if( start[0].z > ziel[0].z ) { intern_calc_route( start, ziel ); } else { intern_calc_route( ziel, start ); } while (route.get_count()>1 && welt->lookup(route[0])->get_grund_hang() == slope_t::flat && welt->lookup(route[1])->is_water()) { // remove leading water ... route.remove_at(0); } } else { keep_existing_city_roads |= (bautyp&bot_flag)!=0; sint32 cost2; if(desc->get_styp() == type_elevated) { cost2 = intern_calc_route_elevated(start[0], ziel[0]); INT_CHECK("wegbauer 1165"); if(cost2 < 0) { intern_calc_route_elevated(ziel[0], start[0]); return warn_fail; } } else { cost2 = intern_calc_route( start, ziel ); INT_CHECK("wegbauer 1165"); if(cost2 < 0) { intern_calc_route( ziel, start ); return warn_fail; } } #ifdef REVERSE_CALC_ROUTE_TOO vector_tpl route2(0); vector_tpl terraform_index2(0); swap(route, route2); swap(terraform_index, terraform_index2); sint32 cost; if(desc->get_styp() == type_elevated) { cost = intern_calc_route_elevated(start[0], ziel[0]); } else { cost = intern_calc_route( start, ziel ); } INT_CHECK("wegbauer 1165"); // the cheaper will survive ... if( cost2 < cost || cost < 0 ) { swap(route, route2); swap(terraform_index, terraform_index2); } #endif } INT_CHECK("wegbauer 778"); #ifdef DEBUG_ROUTES DBG_MESSAGE("calc_route::calc_route", "took %u ms", dr_time() - ms ); #endif return 0; } void way_builder_t::build_tunnel_and_bridges() { if(bridge_desc==NULL && tunnel_desc==NULL) { return; } // check for bridges and tunnels (no tunnel/bridge at last/first tile) for(uint32 i=1; i1) { DBG_MESSAGE("way_builder_t::build_tunnel_and_bridges", "Built bridge %p between %s and %s", bridge_desc, route[i].get_str(), route[i + 1].get_str()); const grund_t* start = welt->lookup(route[i]); const grund_t* end = welt->lookup(route[i + 1]); if(start->get_weg_hang()!=start->get_grund_hang()) { // already a bridge/tunnel there ... continue; } if(end->get_weg_hang()!=end->get_grund_hang()) { // already a bridge/tunnel there ... continue; } if(start->get_grund_hang()==slope_t::flat || start->get_grund_hang()==slope_type(zv*(-1)) || start->get_grund_hang()==2*slope_type(zv*(-1))) { sint8 bridge_height = 0; const char* error = bridge_builder_t::can_build_bridge(player_builder, route[i], route[i + 1], bridge_height, bridge_desc); if (error) { dbg->error("way_builder_t::build_tunnel_and_bridges()", "Cannot build a bridge between %s (n=%i, max_n=%i) and %s", route[i].get_str(), i, get_count() - 1, route[i + 1].get_str()); continue; } bridge_builder_t::build_bridge( player_builder, route[i], route[i+1], zv, bridge_height, bridge_desc, way_builder_t::weg_search(bridge_desc->get_waytype(), bridge_desc->get_topspeed(), welt->get_timeline_year_month(), type_flat)); } else { // tunnel tunnel_builder_t::build( player_builder, route[i].get_2d(), tunnel_desc, true ); } INT_CHECK( "wegbauer 1584" ); } // Don't build short tunnels/bridges if they block a bridge/tunnel behind! else if( bautyp != leitung && koord_distance(route[i + 2], route[i + 1]) == 1 ) { grund_t *gr_i = welt->lookup(route[i]); grund_t *gr_i1 = welt->lookup(route[i+1]); if( gr_i->get_weg_hang() != gr_i->get_grund_hang() || gr_i1->get_weg_hang() != gr_i1->get_grund_hang() ) { // Here is already a tunnel or a bridge. continue; } slope_t::type h = gr_i->get_weg_hang(); waytype_t const wt = desc->get_wtyp(); if(h!=slope_t::flat && slope_t::opposite(h)==gr_i1->get_weg_hang()) { // either a short mountain or a short dip ... // now: check ownership weg_t *wi = gr_i->get_weg(wt); weg_t *wi1 = gr_i1->get_weg(wt); if(wi->get_owner()==player_builder && wi1->get_owner()==player_builder) { // we are the owner if( h != slope_type(zv) ) { // its a bridge if( bridge_desc ) { sint8 bridge_height = 0; const char* error = bridge_builder_t::can_build_bridge(player_builder, route[i], route[i + 1], bridge_height, bridge_desc); if (!error) { bridge_builder_t::build_bridge(player_builder, route[i], route[i + 1], zv, bridge_height, bridge_desc, way_builder_t::weg_search(bridge_desc->get_waytype(), bridge_desc->get_topspeed(), welt->get_timeline_year_month(), type_flat)); } } } else if( tunnel_desc ) { // make a short tunnel wi->set_ribi(ribi_type(slope_t::opposite(h))); wi1->set_ribi(ribi_type(h)); tunnel_builder_t::build( player_builder, route[i].get_2d(), tunnel_desc, true ); } INT_CHECK( "wegbauer 1584" ); } } } } } /* * returns the amount needed to build this way */ sint64 way_builder_t::calc_costs() { sint64 costs=0; koord3d offset = koord3d( 0, 0, bautyp & elevated_flag ? welt->get_settings().get_way_height_clearance() : 0 ); sint64 single_cost; sint32 new_speedlimit; if( bautyp&tunnel_flag ) { assert( tunnel_desc ); single_cost = tunnel_desc->get_price(); new_speedlimit = tunnel_desc->get_topspeed(); } else { single_cost = desc->get_price(); new_speedlimit = desc->get_topspeed(); } // calculate costs for terraforming uint32 last_terraformed = terraform_index.get_count(); for(uint32 const i : terraform_index) { // index in route grund_t *from = welt->lookup(route[i]); uint8 from_slope = from->get_grund_hang(); grund_t *to = welt->lookup(route[i+1]); uint8 to_slope = to->get_grund_hang(); // calculate new slopes check_terraforming(from, to, &from_slope, &to_slope); // change slope of from if (from_slope != from->get_grund_hang()) { if (last_terraformed != i) { costs -= welt->get_settings().cst_set_slope; } } // change slope of to if (to_slope != to->get_grund_hang()) { costs -= welt->get_settings().cst_set_slope; last_terraformed = i+1; // do not pay twice for terraforming one tile } } for(uint32 i=0; ilookup(route[i] + offset); if( gr ) { if( bautyp&tunnel_flag ) { const tunnel_t *tunnel = gr->find(); assert( tunnel ); if( tunnel->get_desc() == tunnel_desc ) { continue; // Nothing to pay on this tile. } old_speedlimit = tunnel->get_desc()->get_topspeed(); } else { if( desc->get_wtyp() == powerline_wt ) { if( leitung_t *lt=gr->get_leitung() ) { old_speedlimit = lt->get_desc()->get_topspeed(); } } else { if (weg_t const* const weg = gr->get_weg(desc->get_wtyp())) { replace_cost = weg->get_desc()->get_price(); if( weg->get_desc() == desc ) { continue; // Nothing to pay on this tile. } if( desc->get_styp() == type_flat && weg->get_desc()->get_styp() == type_tram && gr->get_weg_nr(0)->get_waytype() == road_wt ) { // Don't replace a tram on a road with a normal track. continue; } old_speedlimit = weg->get_desc()->get_topspeed(); } else if (desc->get_wtyp()==water_wt && gr->is_water()) { old_speedlimit = new_speedlimit; } } } // eventually we have to remove trees for( uint8 i=0; iobj_count(); i++ ) { obj_t *obj = gr->obj_bei(i); switch(obj->get_typ()) { case obj_t::baum: costs -= welt->get_settings().cst_remove_tree; break; case obj_t::groundobj: costs += ((groundobj_t *)obj)->get_desc()->get_price(); break; default: break; } } } if( !keep_existing_faster_ways || old_speedlimit < new_speedlimit ) { costs += std::max(single_cost, replace_cost); } // last tile cannot be start of tunnel/bridge if(i+1 either bridge or tunnel if(d.x > 1 || d.y > 1 || d.x < -1 || d.y < -1) { koord zv = koord (sgn(d.x), sgn(d.y)); const grund_t* start = welt->lookup(route[i]); const grund_t* end = welt->lookup(route[i + 1]); if(start->get_weg_hang()!=start->get_grund_hang()) { // already a bridge/tunnel there ... continue; } if(end->get_weg_hang()!=end->get_grund_hang()) { // already a bridge/tunnel there ... continue; } if(start->get_grund_hang()==0 || start->get_grund_hang()==slope_type(zv*(-1))) { // bridge costs += bridge_desc->get_price()*(sint64)(koord_distance(route[i], route[i+1])+1); continue; } else { // tunnel costs += tunnel_desc->get_price()*(sint64)(koord_distance(route[i], route[i+1])+1); continue; } } } } DBG_MESSAGE("way_builder_t::calc_costs()","construction estimate: %f",costs/100.0); return costs; } // adds the ground before underground construction bool way_builder_t::build_tunnel_tile() { sint64 cost = 0; for(uint32 i=0; ilookup(route[i]); const way_desc_t *wb = tunnel_desc->get_way_desc(); if(wb==NULL) { // now we search a matching way for the tunnels top speed // ignore timeline to get consistent results wb = way_builder_t::weg_search( tunnel_desc->get_waytype(), tunnel_desc->get_topspeed(), 0, type_flat ); } if(gr==NULL) { // make new tunnelboden tunnelboden_t* tunnel = new tunnelboden_t(route[i], slope_t::flat); welt->access(route[i].get_2d())->boden_hinzufuegen(tunnel); if( tunnel_desc->get_waytype() != powerline_wt ) { weg_t *weg = weg_t::alloc(tunnel_desc->get_waytype()); weg->set_desc( wb ); tunnel->neuen_weg_bauen(weg, route.get_ribi(i), player_builder); tunnel->obj_add(new tunnel_t(route[i], player_builder, tunnel_desc)); weg->set_max_speed(tunnel_desc->get_topspeed()); player_t::add_maintenance( player_builder, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype()); } else { tunnel->obj_add(new tunnel_t(route[i], player_builder, tunnel_desc)); leitung_t *lt = new leitung_t(tunnel->get_pos(), player_builder); lt->set_desc( wb ); tunnel->obj_add( lt ); lt->finish_rd(); } tunnel->calc_image(); cost -= tunnel_desc->get_price(); player_t::add_maintenance( player_builder, tunnel_desc->get_maintenance(), tunnel_desc->get_finance_waytype() ); } else if( gr->get_typ() == grund_t::tunnelboden ) { // check for extension only ... if( tunnel_desc->get_waytype() != powerline_wt ) { gr->weg_erweitern( tunnel_desc->get_waytype(), route.get_ribi(i) ); tunnel_t *tunnel = gr->find(); assert( tunnel ); // take the faster way if( !keep_existing_faster_ways || (tunnel->get_desc()->get_topspeed() < tunnel_desc->get_topspeed()) ) { player_t::add_maintenance(player_builder, -tunnel->get_desc()->get_maintenance(), tunnel->get_desc()->get_finance_waytype()); player_t::add_maintenance(player_builder, tunnel_desc->get_maintenance(), tunnel->get_desc()->get_finance_waytype() ); tunnel->set_desc(tunnel_desc); weg_t *weg = gr->get_weg(tunnel_desc->get_waytype()); weg->set_desc(wb); weg->set_max_speed(tunnel_desc->get_topspeed()); // respect max speed of catenary wayobj_t const* const wo = gr->get_wayobj(tunnel_desc->get_waytype()); if (wo && wo->get_desc()->get_topspeed() < weg->get_max_speed()) { weg->set_max_speed( wo->get_desc()->get_topspeed() ); } gr->calc_image(); // respect speed limit of crossing weg->count_sign(); cost -= tunnel_desc->get_price(); } } else { leitung_t *lt = gr->get_leitung(); if(!lt) { lt = new leitung_t(gr->get_pos(), player_builder); lt->set_desc( wb ); gr->obj_add( lt ); } } } } player_t::book_construction_costs(player_builder, cost, route[0].get_2d(), tunnel_desc->get_waytype()); return true; } void way_builder_t::build_elevated() { for(koord3d & i : route) { planquadrat_t* const plan = welt->access(i.get_2d()); grund_t* const gr0 = plan->get_boden_in_hoehe(i.z); i.z += welt->get_settings().get_way_height_clearance(); grund_t* const gr = plan->get_boden_in_hoehe(i.z); if(gr==NULL) { slope_t::type hang = gr0 ? gr0->get_grund_hang() : 0; // add new elevated ground monorailboden_t* const monorail = new monorailboden_t(i, hang); plan->boden_hinzufuegen(monorail); monorail->calc_image(); } } } void way_builder_t::build_road() { // only public player or cities (sp==NULL) can build cityroads with sidewalk bool add_sidewalk = build_sidewalk && (player_builder==NULL || player_builder->is_public_service()); if(add_sidewalk) { player_builder = NULL; } // init undo if(player_builder!=NULL) { // intercity roads have no owner, so we must check for an owner player_builder->init_undo(road_wt,get_count()); } for( uint32 i=0; ilookup(route[i]); sint64 cost = 0; bool extend = gr->weg_erweitern(road_wt, route.get_short_ribi(i)); // bridges/tunnels have their own track type and must not upgrade if(gr->get_typ()==grund_t::brueckenboden || gr->get_typ()==grund_t::tunnelboden) { continue; } if(extend) { weg_t * weg = gr->get_weg(road_wt); // keep faster ways or if it is the same way, or it is the way under the halt of an existing player if(weg->get_desc()==desc || keep_existing_ways || (keep_existing_city_roads && weg->hat_gehweg()) || (keep_existing_faster_ways && weg->get_desc()->get_topspeed()>desc->get_topspeed()) || (player_builder!=NULL && weg->get_removal_error(player_builder)!=NULL) || (gr->get_typ()==grund_t::monorailboden && (bautyp&elevated_flag)==0) || (gr->has_two_ways() && gr->get_weg_nr(1)->get_removal_error(player_builder)!=NULL) // do not replace public roads crossing rails of other players || (gr->get_halt().is_bound() && !check_owner(gr->get_halt()->get_owner(), player_builder)) ) { //nothing to be done } else { // we take ownership => we take care to maintain the roads completely ... player_t *s = weg->get_owner(); player_t::add_maintenance(s, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype()); // cost is the more expensive one, so downgrading is between removing and new building cost -= max( weg->get_desc()->get_price(), desc->get_price() ); weg->set_gehweg(add_sidewalk); weg->set_desc(desc); // respect max speed of catenary wayobj_t const* const wo = gr->get_wayobj(desc->get_wtyp()); if (wo && wo->get_desc()->get_topspeed() < weg->get_max_speed()) { weg->set_max_speed( wo->get_desc()->get_topspeed() ); } player_t::add_maintenance( player_builder, weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype()); weg->set_owner(player_builder); if (crossing_t* crossing = gr->get_crossing()) { crossing->finish_rd(); } } } else { // make new way strasse_t * str = new strasse_t(); str->set_gehweg(add_sidewalk); str->set_desc(desc); cost = -gr->neuen_weg_bauen(str, route.get_short_ribi(i), player_builder)-desc->get_price(); // into UNDO-list, so we can remove it later if(player_builder!=NULL) { // intercity roads have no owner, so we must check for an owner player_builder->add_undo( route[i] ); } } gr->calc_image(); // because it may be a crossing ... minimap_t::get_instance()->calc_map_pixel(k); player_t::book_construction_costs(player_builder, cost, k, road_wt); } // for } void way_builder_t::build_track() { if (get_count() <= 1) { return; } // init undo player_builder->init_undo(desc->get_wtyp(), get_count()); // build tracks for( uint32 i=0; ilookup(route[i]); ribi_t::ribi ribi = route.get_short_ribi(i); if(gr->get_typ()==grund_t::wasser) { // not building on the sea ... continue; } const bool extend = gr->weg_erweitern(desc->get_wtyp(), ribi); // bridges/tunnels have their own track type and must not upgrade if((gr->get_typ()==grund_t::brueckenboden || gr->get_typ()==grund_t::tunnelboden) && gr->get_weg_nr(0)->get_waytype()==desc->get_wtyp()) { continue; } if(extend) { weg_t* const weg = gr->get_weg(desc->get_wtyp()); bool change_desc = true; // do not touch fences, tram way etc. if there is already same way with different type // keep faster ways or if it is the same way if (weg->get_desc() == desc || (desc->get_styp() == 0 && weg->get_desc()->get_styp() == type_tram && gr->has_two_ways()) || keep_existing_ways || (keep_existing_faster_ways && weg->get_desc()->get_topspeed() > desc->get_topspeed()) || (gr->get_typ() == grund_t::monorailboden && !(bautyp & elevated_flag) && gr->get_weg_nr(0)->get_waytype()==desc->get_wtyp())) { //nothing to be done change_desc = false; } // build tram track over crossing -> remove crossing if( gr->has_two_ways() && (desc->get_styp() == type_tram || weg->get_desc()->get_styp() == type_tram) ) { if( crossing_t *cr = gr->get_crossing() ) { // change to tram track cr->mark_image_dirty( cr->get_image(), 0); cr->cleanup(player_builder); delete cr; change_desc = true; // tell way we have no crossing any more, restore speed limit gr->get_weg_nr(0)->clear_crossing(); gr->get_weg_nr(0)->set_desc( gr->get_weg_nr(0)->get_desc() ); gr->get_weg_nr(1)->clear_crossing(); } } if( change_desc ) { // we take ownership => we take care to maintain the roads completely ... player_t *s = weg->get_owner(); player_t::add_maintenance( s, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype()); // cost is the more expensive one, so downgrading is between removing and new buidling cost -= max( weg->get_desc()->get_price(), desc->get_price() ); weg->set_desc(desc); // respect max speed of catenary wayobj_t const* const wo = gr->get_wayobj(desc->get_wtyp()); if (wo && wo->get_desc()->get_topspeed() < weg->get_max_speed()) { weg->set_max_speed( wo->get_desc()->get_topspeed() ); } player_t::add_maintenance( player_builder, weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype()); weg->set_owner(player_builder); // respect speed limit of crossing weg->count_sign(); if (crossing_t* crossing = gr->get_crossing()) { crossing->finish_rd(); } } } else { weg_t* const sch = weg_t::alloc(desc->get_wtyp()); sch->set_desc(desc); cost = -gr->neuen_weg_bauen(sch, ribi, player_builder)-desc->get_price(); // connect canals to sea if( desc->get_wtyp() == water_wt ) { // do not connect across slopes ribi_t::ribi slope_ribi = ribi_t::all; if (gr->get_grund_hang()!=slope_t::flat) { slope_ribi = ribi_t::doubles( ribi_type(gr->get_weg_hang()) ); } for( int j = 0; j < 4; j++ ) { if (ribi_t::nesw[j] & slope_ribi) { grund_t *sea = NULL; if (gr->get_neighbour(sea, invalid_wt, ribi_t::nesw[j]) && sea->is_water() ) { gr->weg_erweitern( water_wt, ribi_t::nesw[j] ); sea->calc_image(); } } } } // into UNDO-list, so we can remove it later player_builder->add_undo( route[i] ); } gr->calc_image(); minimap_t::get_instance()->calc_map_pixel( gr->get_pos().get_2d() ); player_t::book_construction_costs(player_builder, cost, gr->get_pos().get_2d(), desc->get_finance_waytype()); if((i&3)==0) { INT_CHECK( "wegbauer 1584" ); } } } void way_builder_t::build_powerline() { if( get_count() < 1 ) { return; } // no undo player_builder->init_undo(powerline_wt,get_count()); for( uint32 i=0; ilookup(route[i]); leitung_t* lt = gr->get_leitung(); bool build_powerline = false; // ok, really no lt here ... if(lt==NULL) { if(gr->ist_natur()) { // remove trees etc. sint64 cost = gr->remove_trees(); player_t::book_construction_costs(player_builder, -cost, gr->get_pos().get_2d(), powerline_wt); } lt = new leitung_t(route[i], player_builder ); gr->obj_add(lt); // into UNDO-list, so we can remove it later player_builder->add_undo( route[i] ); build_powerline = true; } else { // modernize the network if(lt->get_typ() == obj_t::leitung && (!keep_existing_faster_ways || lt->get_desc()->get_topspeed() < desc->get_topspeed()) ) { build_powerline = true; player_t::add_maintenance( lt->get_owner(), -lt->get_maintenance(), powerline_wt ); } } if (build_powerline) { lt->set_desc(desc); player_t::book_construction_costs(player_builder, -desc->get_price(), gr->get_pos().get_2d(), powerline_wt); // this adds maintenance lt->leitung_t::finish_rd(); minimap_t::get_instance()->calc_map_pixel( gr->get_pos().get_2d() ); } if((i&3)==0) { INT_CHECK( "wegbauer 1584" ); } } } // this can drive any river, even a river that has max_speed=0 class fluss_test_driver_t : public test_driver_t { bool check_next_tile(const grund_t* gr) const OVERRIDE { return gr->get_weg_ribi_unmasked(water_wt)!=0; } ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return gr->get_weg_ribi_unmasked(water_wt); } waytype_t get_waytype() const OVERRIDE { return ignore_wt; } int get_cost(const grund_t *, const weg_t *, const sint32, ribi_t::ribi) const OVERRIDE { return 1; } bool is_target(const grund_t *cur,const grund_t *) const OVERRIDE { return cur->is_water() && cur->get_hoehe()<=world()->get_groundwater(); } }; // make a river void way_builder_t::build_river() { /* since the constraints of the wayfinder ensures that a river flows always downwards * we can assume that the first tiles are the ocean. * Usually the wayfinder would find either direction! * route.front() tile at the ocean, route.back() the spring of the river */ uint32 start_n = 0; uint32 end_n = get_count(); // first find coast ... for( uint32 idx=start_n; idxlookup(route[idx])->get_hoehe() == welt->get_water_hgt(route[idx].get_2d()) ) { start_n = idx; } else { break; } } // Do we join an other river? for( uint32 idx=start_n; idxlookup( route[idx] )->get_weg(water_wt) ) { // river or channel start_n = idx; } } // No spring on a slope while( end_n>start_n ) { if( welt->lookup( route[end_n-1] )->get_grund_hang() == slope_t::flat ) { break; } end_n --; } if( start_n == get_count()-1 ) { // completely joined another river => nothing to do return; } // first check then lower riverbed sint8 start_h = max(route[start_n].z,welt->get_groundwater()); vector_tpl lower_tile( end_n ); // This contains the tiles which can be lowered. vector_tpl lower_tile_h( end_n ); // to this value // first: find possible valley for( uint32 i=0; i < end_n; i++ ) { bool can_lower = true; if( route[i].z < start_h ) { // skip all tiles below last sea level can_lower = false; } grund_t *gr = welt->lookup( route[i] ); if( gr->is_water() ) { // not lowering the sea or lakes start_h = welt->get_water_hgt( route[i].get_2d() ); can_lower = false; } if( gr->hat_weg(water_wt) ) { // not lowering exiting rivers start_h = route[i].z; can_lower = false; } // check if( can_lower ) { can_lower = welt->can_flatten_tile( NULL, route[i].get_2d(), max( route[i].z-1, start_h ) ); } lower_tile.append( can_lower ); lower_tile_h.append( start_h ); sint8 current_h = route[i].z+slope_t::max_diff(gr->get_grund_hang()); if( current_h > start_h + 1 ) { start_h++; } } // now lower all tiles for( uint32 i=start_n; iflatten_tile( NULL, route[i].get_2d(), lower_tile_h[i] ) ) { // illegal slope encountered, give up ... return; } } } // and raise all tiles that resulted in slopes on curves for( uint32 i = max(end_n, 2) - 2; i > start_n+1; i-- ) { grund_t *gr = welt->lookup_kartenboden( route[i].get_2d() ); if( !gr->get_weg_ribi( water_wt ) && gr->get_grund_hang()!=slope_t::flat ) { if( !ribi_t::is_straight(ribi_type(route[i+1].get_2d()-route[i-1].get_2d())) ) { welt->flatten_tile( NULL, route[i].get_2d(), gr->get_hoehe()+1 ); DBG_MESSAGE( "wrong river slope", route[i].get_str() ); } } } // now build the river uint32 last_common_water_tile = start_n; grund_t *gr_first = NULL; for( uint32 i=start_n; ilookup_kartenboden(route[i].get_2d()); if( gr_first == NULL) { gr_first = gr; } if( gr->get_typ()!=grund_t::wasser ) { // get direction ribi_t::ribi ribi = iweg_erweitern(water_wt, ribi); if( !extend ) { weg_t *river=weg_t::alloc(water_wt); river->set_desc(desc); gr->neuen_weg_bauen(river, ribi, NULL); } else { last_common_water_tile = i; } } else { dynamic_cast(gr)->recalc_water_neighbours(); last_common_water_tile = i; } } gr_first->calc_image(); // to calculate ribi of water tiles // we will make rivers gradually larger by stepping up their width // Since we cannot quickly find out if a lake has another influx, we just assume all rivers after a lake are navigable if( env_t::river_types>1 ) { // now all rivers will end in the sea (or a lake) and thus there must be a valid route! // unless weare the first and there is no lake on the way. route_t to_the_sea; fluss_test_driver_t river_tester; if (to_the_sea.calc_route(welt, welt->lookup_kartenboden(route[last_common_water_tile].get_2d())->get_pos(), welt->lookup_kartenboden(route[0].get_2d())->get_pos(), &river_tester, 0, 0x7FFFFFFF)) { for(koord3d const& entry : to_the_sea.get_route()) { if (weg_t* const w = welt->lookup(entry)->get_weg(water_wt)) { int type; for( type=env_t::river_types-1; type>0; type-- ) { // lookup type if( w->get_desc()==desc_table.get(env_t::river_type[type]) ) { break; } } // still room to expand if( type>0 ) { // thus we enlarge w->set_desc( desc_table.get(env_t::river_type[type-1]) ); w->calc_image(); } } } } } } void way_builder_t::build() { if(get_count()<2 || get_count() > (uint32)maximum) { DBG_MESSAGE("way_builder_t::build()","called, but no valid route."); // no valid route here ... return; } DBG_MESSAGE("way_builder_t::build()", "type=%d max_n=%d start=%d,%d end=%d,%d", bautyp, get_count() - 1, route.front().x, route.front().y, route.back().x, route.back().y); #ifdef DEBUG_ROUTES uint32 ms = dr_time(); #endif if ( (bautyp&terraform_flag)!=0 && (bautyp&(tunnel_flag|elevated_flag))==0 && bautyp!=river) { // do the terraforming do_terraforming(); } // first add all new underground tiles ... (and finished if successful) if(bautyp&tunnel_flag) { build_tunnel_tile(); return; } // add elevated ground for elevated tracks if(bautyp&elevated_flag) { build_elevated(); } INT_CHECK("simbau 1072"); switch(bautyp&bautyp_mask) { case wasser: case schiene: case schiene_tram: // Dario: Tramway case monorail: case maglev: case narrowgauge: case luft: DBG_MESSAGE("way_builder_t::build", "schiene"); build_track(); break; case strasse: build_road(); DBG_MESSAGE("way_builder_t::build", "strasse"); break; case leitung: build_powerline(); break; case river: build_river(); break; default: break; } INT_CHECK("simbau 1087"); build_tunnel_and_bridges(); INT_CHECK("simbau 1087"); #ifdef DEBUG_ROUTES DBG_MESSAGE("way_builder_t::build", "took %u ms", dr_time() - ms ); #endif } uint32 way_builder_t::calc_distance( const koord3d &pos, const koord3d &mini, const koord3d &maxi ) { uint32 dist = 0; if ( pos.x < mini.x ) { dist += mini.x - pos.x; } else if( pos.x > maxi.x ) { dist += pos.x - maxi.x; } if ( pos.y < mini.y ) { dist += mini.y - pos.y; } else if( pos.y > maxi.y ) { dist += pos.y - maxi.y; } settings_t const& settings = welt->get_settings(); dist *= settings.way_count_straight; if ( pos.z < mini.z ) { dist += (mini.z - pos.z) * settings.way_count_slope; } else if( pos.z > maxi.z ) { dist += (pos.z - maxi.z) * settings.way_count_slope; } return dist; } simutrans-124.5/src/simutrans/builder/wegbauer.h000066400000000000000000000155311520675460500220050ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef BUILDER_WEGBAUER_H #define BUILDER_WEGBAUER_H #include "../simtypes.h" #include "../dataobj/koord3d.h" #include "../tpl/vector_tpl.h" class way_desc_t; class bridge_desc_t; class tunnel_desc_t; class karte_ptr_t; class player_t; class grund_t; class tool_selector_t; /** * way building class with its own route finding */ class way_builder_t { static karte_ptr_t welt; public: static const way_desc_t *leitung_desc; static bool register_desc(way_desc_t *desc); static bool successfully_loaded(); // generates timeline message static void new_month(); /** * Finds a way with a given speed limit for a given waytype */ static const way_desc_t *weg_search(const waytype_t wtyp,const sint32 speed_limit, const uint16 time, const systemtype_t system_type); static const way_desc_t *get_desc(const char *way_name,const uint16 time=0); static const way_desc_t *get_earliest_way(const waytype_t wtyp); static const way_desc_t *get_latest_way(const waytype_t wtyp); static bool waytype_available( const waytype_t wtyp, uint16 time ); static const vector_tpl& get_way_list(waytype_t, systemtype_t system_type); /** * Fill menu with icons of given waytype */ static void fill_menu(tool_selector_t *tool_selector, const waytype_t wtyp, const systemtype_t styp, sint16 ok_sound); enum bautyp_t { strasse = road_wt, schiene = track_wt, schiene_tram = tram_wt, monorail = monorail_wt, maglev = maglev_wt, wasser = water_wt, luft = air_wt, narrowgauge = narrowgauge_wt, leitung = powerline_wt, river = 0x7F, bautyp_mask = 0xFF, bot_flag = 1 << 8, ///< do not connect to other ways elevated_flag = 1 << 9, ///< elevated structure terraform_flag = 1 << 10, tunnel_flag = 1 << 11, ///< underground structure }; private: /// flags used in intern_calc_route, saved in the otherwise unused route_t::ANode->count enum build_type_t { build_straight = 1 << 0, ///< next step has to be straight terraform = 1 << 1, ///< terraform this tile build_tunnel_bridge = 1 << 2, ///< bridge/tunnel ends here is_upperlayer = 1 << 3 ///< only used when elevated true:upperlayer }; struct next_gr_t { next_gr_t() {} next_gr_t(grund_t* gr_, sint32 cost_, uint8 flag_=0) : gr(gr_), cost(cost_), flag(flag_) {} grund_t* gr; sint32 cost; uint8 flag; }; vector_tpl next_gr; player_t *player_builder; /// Type of building operation bautyp_t bautyp; /// Which way to build const way_desc_t *desc; /// Type of bridge to build (null => no bridges) const bridge_desc_t *bridge_desc; /// Type of tunnel to build (null => no bridges) const tunnel_desc_t *tunnel_desc; /** * If a way is built on top of another way, should the type * of the former way be kept or replaced (true == keep) */ bool keep_existing_ways; bool keep_existing_faster_ways; bool keep_existing_city_roads; // try to keep a way close to existing ways bool prefer_parallel; bool build_sidewalk; sint32 maximum; // highest cost koord3d_vector_t route; // index in route with terraformed tiles vector_tpl terraform_index; // has a warning message, why the route may have failed (could be wrong!) const char *warn_fail; public: /** * This is the core routine for the way search * it will check * A) allowed step * B) if allowed, calculate the cost for the step from @p from to @p to */ bool is_allowed_step(const grund_t *from, const grund_t *to, sint32 *costs, bool is_upperlayer = false ); private: bool has_neighbour_with_way(koord3d pos, waytype_t wt) const; // checks, if we can build a bridge here ... // may modify next_gr array! void check_for_bridge(const grund_t* parent_from, const grund_t* from, const vector_tpl &ziel); sint32 intern_calc_route(const vector_tpl &start, const vector_tpl &ziel); void intern_calc_straight_route(const koord3d start, const koord3d ziel); sint32 intern_calc_route_elevated(const koord3d start, const koord3d ziel); // runways need to meet some special conditions enforced here bool intern_calc_route_runways(koord3d start, const koord3d ziel); void build_tunnel_and_bridges(); // adds the ground before underground construction (always called before the following construction routines) bool build_tunnel_tile(); // adds the grounds for elevated tracks void build_elevated(); void build_road(); void build_track(); void build_powerline(); void build_river(); /** * This function calculates the distance of pos to the cuboid * spanned up by mini and maxi. * The result is already weighted according to * welt->get_settings().get_way_count_{straight,slope}(). */ uint32 calc_distance( const koord3d &pos, const koord3d &mini, const koord3d &maxi ); public: const koord3d_vector_t &get_route() const { return route; } uint32 get_count() const { return route.get_count(); } void set_prefer_parallel(bool yesno) { prefer_parallel = yesno; } /** * If a way is built on top of another way, should the type * of the former way be kept or replaced (true == keep) */ void set_keep_existing_ways(bool yesno); /** * If a way is built on top of another way, should the type * of the former way be kept or replaced, if the current way is faster (true == keep) */ void set_keep_existing_faster_ways(bool yesno); /** * Always keep city roads (for AI) */ void set_keep_city_roads(bool yesno) { keep_existing_city_roads = yesno; } void set_build_sidewalk(bool yesno) { build_sidewalk = yesno; } void init_builder(bautyp_t wt, const way_desc_t * desc, const tunnel_desc_t *tunnel_desc=NULL, const bridge_desc_t *bridge_desc=NULL); void set_maximum(uint32 n) { maximum = n; } way_builder_t(player_t *player); const char *calc_straight_route(const koord3d start, const koord3d ziel); const char *calc_route(const koord3d &start3d, const koord3d &ziel); const char *calc_route(const vector_tpl &start3d, const vector_tpl &ziel); /// returns the amount needed to build this way sint64 calc_costs(); bool check_crossing(const koord zv, const grund_t *bd, const way_desc_t *desc, const player_t *player) const; bool check_powerline(const koord zv, const grund_t *bd) const; // allowed owner? bool check_owner( const player_t *player1, const player_t *player2 ) const; // checks whether buildings on the tile allow to leave in direction dir bool check_building( const grund_t *to, const koord dir ) const; // allowed slope? bool check_slope( const grund_t *from, const grund_t *to ); bool check_terraforming( const grund_t *from, const grund_t *to, uint8* new_from_slope=NULL, uint8* new_to_slope=NULL) const; void do_terraforming(); void build(); }; ENUM_BITSET(way_builder_t::bautyp_t); #endif simutrans-124.5/src/simutrans/convoihandle.h000066400000000000000000000004021520675460500212160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef CONVOIHANDLE_H #define CONVOIHANDLE_H #include "tpl/quickstone_tpl.h" class convoi_t; typedef quickstone_tpl convoihandle_t; #endif simutrans-124.5/src/simutrans/dataobj/000077500000000000000000000000001520675460500200045ustar00rootroot00000000000000simutrans-124.5/src/simutrans/dataobj/crossing_logic.cc000066400000000000000000000253251520675460500233260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../vehicle/vehicle_base.h" #include "../world/simworld.h" #include "../simsound.h" #include "translator.h" #include "../descriptor/crossing_desc.h" #include "../utils/cbuffer.h" #include "../tpl/slist_tpl.h" #include "crossing_logic.h" #include "../obj/crossing.h" karte_ptr_t crossing_logic_t::welt; crossing_logic_t::crossing_logic_t( const crossing_desc_t *desc ) { state = CROSSING_INVALID; this->desc = desc; request_close = NULL; } /** * @param[out] buf string; currently unused but useful for debugging */ void crossing_logic_t::info(cbuffer_t & buf) const { static char const* const state_str[NUM_CROSSING_STATES] = { "invalid", "open", "request closing", "closed" }; buf.printf("%s%u%s%u%s%s\n", translator::translate("\nway1 reserved by"), on_way1.get_count(), translator::translate("\nway2 reserved by"), on_way2.get_count(), translator::translate("cars.\nstate"), translator::translate(state_str[state]) ); } // after merging or splitting two crossings ... void crossing_logic_t::recalc_state() { if( !crossings.empty() ) { on_way1.clear(); on_way2.clear(); for(crossing_t* const i : crossings) { // add vehicles already there if (grund_t* const gr = welt->lookup(i->get_pos())) { for( uint8 i=3; iobj_count(); i++ ) { if( vehicle_base_t const* const v = obj_cast(gr->obj_bei(i)) ) { add_to_crossing( v ); } } } } } request_close = NULL; if(state==CROSSING_INVALID) { // now just set the state, if needed if(on_way2.empty()) { set_state( CROSSING_OPEN ); } else { set_state( CROSSING_CLOSED ); } } } // request permission to pass crossing bool crossing_logic_t::request_crossing( const vehicle_base_t *v ) { if(v->get_waytype()==desc->get_waytype(0)) { if(on_way2.empty() && state == CROSSING_OPEN) { // way2 is empty ... return true; } // passage denied, since there are vehicle on way2 // which has priority // => ok only if I am already crossing return on_way1.is_contained(v); } else if(v->get_waytype()==desc->get_waytype(1)) { // vehicle from way2 arrives if(on_way1.get_count()) { // sorry, still things on the crossing, but we will prepare set_state( CROSSING_REQUEST_CLOSE ); return false; } else { request_close = v; set_state( CROSSING_CLOSED ); return true; } } // likely an airplane ... return true; } // request permission to pass crossing void crossing_logic_t::add_to_crossing( const vehicle_base_t *v ) { if( v->get_typ()!=obj_t::pedestrian ) { if(v->get_waytype()==desc->get_waytype(0)) { on_way1.append_unique(v); } else if (v->get_waytype() == desc->get_waytype(1)) { // add it and close crossing on_way2.append_unique(v); if( request_close==v ) { request_close = NULL; } set_state( CROSSING_CLOSED ); } } } // called after passing of the last vehicle (in a convoi) // or of a city car; releases the crossing which may switch state void crossing_logic_t::release_crossing( const vehicle_base_t *v ) { if( v->get_waytype() == desc->get_waytype(0) ) { on_way1.remove(v); if( state == CROSSING_REQUEST_CLOSE && on_way1.empty() ) { set_state( CROSSING_CLOSED ); } } else { on_way2.remove(v); if( request_close == v ) { request_close = NULL; } if( on_way2.empty() && request_close == NULL ) { set_state( CROSSING_OPEN ); } } } // change state; mark dirty and plays sound void crossing_logic_t::set_state( crossing_state_t new_state ) { // play sound (if there and closing) if(new_state==CROSSING_CLOSED && desc->get_sound()>=0 && !welt->is_fast_forward()) { welt->play_sound_area_clipped(crossings[0]->get_pos().get_2d(), desc->get_sound(), CROSSING_SOUND ); } if(new_state!=state) { state = new_state; for(crossing_t* const i : crossings) { i->state_changed(); } } } /* static stuff from here on ... */ /** * nothing can cross airways, so waytype 0..7 is enough * only save this entries: * way0 way1 * 0 .. 1 2 3 4 5 6 7 8 * 1 .. 2 3 4 5 6 7 8 * 2 .. 3 4 5 6 7 8 * .. ... */ minivec_tpl crossing_logic_t::can_cross_array[36]; /** * compare crossings for the same waytype-combinations */ int compare_crossing(const crossing_desc_t *c0, const crossing_desc_t *c1) { // sort descending wrt maxspeed int diff = c1->get_maxspeed(0) - c0->get_maxspeed(0); if (diff==0) { diff = c1->get_maxspeed(1) - c0->get_maxspeed(1); } if (diff==0) { diff = strcmp(c0->get_name(), c1->get_name()); } return diff; } void crossing_logic_t::register_desc(crossing_desc_t *desc) { // mark if crossing possible const waytype_t way0 = (waytype_t)min(desc->get_waytype(0), desc->get_waytype(1)); const waytype_t way1 = (waytype_t)max(desc->get_waytype(0), desc->get_waytype(1)); if (way1 == way0) { dbg->error("crossing_logic_t::register_desc()", "Crossing %s has waytype %s for both ways! (Crossing ignored)", desc->get_name(), weg_t::waytype_to_string(way0)); return; } if( way0<8 && way1<9 ) { uint8 index = way0 * 9 + way1 - ((way0+2)*(way0+1))/2; // max index = 7*9 + 8 - 9*4 = 71-36 = 35 // .. overwrite double entries minivec_tpl &vec = can_cross_array[index]; // first check for existing crossing with the same name for(uint8 i=0; iget_name(), desc->get_name())==0) { delete vec[i]; vec.remove_at(i); } } DBG_DEBUG( "crossing_logic_t::register_desc()","%s", desc->get_name() ); // .. then make sorted insert for(uint8 i=0; iis_available(timeline_year_month) ) { continue; } // match maxspeed of first way uint8 const swap_way = i->get_waytype(0) != way0; sint32 const imax0 = i->get_maxspeed(swap_way); sint32 const bmax0 = best ? best->get_maxspeed(swap_way) : 9999; if( imax0 >= speed0 && imax0 <= bmax0 ) { // match maxspeed of second way sint32 const imax1 = i->get_maxspeed(!swap_way); sint32 const bmax1 = best ? best->get_maxspeed(!swap_way) : 9999; if( imax1 >= speed1 && imax1 <= bmax1 ) { best = i; } } } } return best; } /** * compare crossings for the same waytype-combinations */ bool have_crossings_same_wt(const crossing_desc_t *c0, const crossing_desc_t *c1) { return c0->get_waytype(0) == c1->get_waytype(0) && c0->get_waytype(1) == c1->get_waytype(1); } // returns a new or an existing crossing_logic_t object // new, of no matching crossings are next to it void crossing_logic_t::add( crossing_t *start_cr, crossing_state_t state ) { koord3d pos = start_cr->get_pos(); const koord zv = start_cr->get_dir() ? koord::west : koord::north; slist_tplcrossings; minivec_tplcrossings_logics; crossings.append_unique( start_cr ); if (crossing_logic_t *start_logic = start_cr->get_logic() ) { crossings_logics.append(start_logic); } // go north/west while(1) { pos += zv; grund_t *gr = welt->lookup( pos ); if(gr==NULL) { break; } crossing_t *found_cr = gr->find(); if(found_cr==NULL || !have_crossings_same_wt(found_cr->get_desc(),start_cr->get_desc()) || start_cr->get_dir() != found_cr->get_dir()) { break; } crossings.append( found_cr ); if( found_cr->get_logic()!=NULL ) { crossings_logics.append_unique( found_cr->get_logic() ); } } // go east/south pos = start_cr->get_pos(); while(1) { pos -= zv; grund_t *gr = welt->lookup( pos ); if(gr==NULL) { break; } crossing_t *found_cr = gr->find(); if(found_cr==NULL || !have_crossings_same_wt(found_cr->get_desc(),start_cr->get_desc()) || start_cr->get_dir() != found_cr->get_dir()) { break; } crossings.append( found_cr ); if( found_cr->get_logic()!=NULL ) { crossings_logics.append_unique( found_cr->get_logic() ); } } // remove all old crossing logics crossing_logic_t *found_logic = NULL; if( crossings_logics.get_count()>=1 ) { // leave one logic to be used further while( crossings_logics.get_count()>1 ) { crossing_logic_t *cl = crossings_logics[0]; crossings_logics.remove_at(0); delete cl; } found_logic = crossings_logics[0]; } // no old logic there create a new one if( found_logic == NULL ) { found_logic = new crossing_logic_t( start_cr->get_desc() ); } // set new crossing logic to all for(crossing_t* const cr : crossings) { cr->set_logic( found_logic ); found_logic->append_crossing( cr ); } found_logic->set_state( state ); found_logic->recalc_state(); } // removes a crossing logic, if all crossings are removed void crossing_logic_t::remove( crossing_t *cr ) { crossings.remove( cr ); if( crossings.empty() ) { delete this; } else { // check for a crossing to the east/south koord3d pos = cr->get_pos(); const koord zv = cr->get_dir() ? koord::west : koord::north; const grund_t *gr = welt->lookup( pos-zv ); if( gr ) { crossing_t *found_cr = gr->find(); if( found_cr && have_crossings_same_wt(found_cr->get_desc(),cr->get_desc()) ) { // crossing to the east/south so split logic from any found to the north/west crossing_logic_t *split_logic = NULL; while(1) { pos += zv; gr = welt->lookup( pos ); if( gr == NULL ) { break; } found_cr = gr->find(); if( found_cr == NULL || !have_crossings_same_wt(found_cr->get_desc(),cr->get_desc()) ) { break; } assert(this==found_cr->get_logic()); if( !split_logic ) { split_logic = new crossing_logic_t( cr->get_desc() ); } crossings.remove( found_cr ); found_cr->set_logic( split_logic ); split_logic->append_crossing( found_cr ); } if( split_logic ) { split_logic->set_state( CROSSING_INVALID ); split_logic->recalc_state(); } } } set_state( CROSSING_INVALID ); recalc_state(); } } simutrans-124.5/src/simutrans/dataobj/crossing_logic.h000066400000000000000000000050551520675460500231660ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_CROSSING_LOGIC_H #define DATAOBJ_CROSSING_LOGIC_H #include "../simtypes.h" #include "../tpl/minivec_tpl.h" #include "../tpl/slist_tpl.h" class cbuffer_t; class crossing_t; class karte_ptr_t; class crossing_desc_t; class vehicle_base_t; /** * road sign for traffic (one way minimum speed, traffic lights) */ class crossing_logic_t { public: enum crossing_state_t { CROSSING_INVALID = 0, CROSSING_OPEN, CROSSING_REQUEST_CLOSE, CROSSING_CLOSED, NUM_CROSSING_STATES }; protected: static karte_ptr_t welt; // the last vehicle that requested a closing const vehicle_base_t *request_close; crossing_state_t state; const crossing_desc_t *desc; minivec_tplcrossings; void set_state( crossing_state_t new_state ); public: minivec_tplon_way1; minivec_tplon_way2; public: crossing_logic_t( const crossing_desc_t *desc ); /** * @param[out] buf string (only used for debug at the moment) */ void info(cbuffer_t & buf) const; // recalcs the current state void recalc_state(); // returns true, if the crossing can be passed by this vehicle bool request_crossing( const vehicle_base_t * ); // adds to crossing void add_to_crossing( const vehicle_base_t *v ); // removes the vehicle from the crossing void release_crossing( const vehicle_base_t * ); /* states of the crossing; * since way2 has priority over way1 there is a third state, during a closing request */ crossing_state_t get_state() { return state; } void append_crossing( crossing_t *cr ) { crossings.append_unique(cr); } // static routines from here private: static slist_tpl list; // save all desc' only for waytype0 < waytype1 static minivec_tpl can_cross_array[36]; public: static void register_desc(crossing_desc_t *desc); /** * returns descriptor for crossing wrt timeline * tries to match max-speeds: * (1) find crossings with maxspeed close to requested maxspeed * (2) prefer crossings with maxspeed larger than requested */ static const crossing_desc_t *get_crossing(const waytype_t ns, const waytype_t ow, sint32 way_0_speed, sint32 way_1_speed, uint16 timeline_year_month); // returns a new or an existing crossing_logic_t object // new, of no matching crossings are next to it static void add( crossing_t *cr, crossing_logic_t::crossing_state_t state ); // remove logic from crossing(s) void remove( crossing_t *cr ); }; #endif simutrans-124.5/src/simutrans/dataobj/environment.cc000066400000000000000000000456721520675460500226750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "environment.h" #include "loadsave.h" #include "../pathes.h" #include "../simversion.h" #include "../simconst.h" #include "../simtypes.h" #include "../sys/simsys.h" #include "../simmesg.h" #include "../utils/simrandom.h" void rdwr_win_settings(loadsave_t *file); // simwin char env_t::base_dir[PATH_MAX]; char env_t::install_dir[PATH_MAX]; char env_t::user_dir[PATH_MAX]; std::string env_t::pak_dir; std::string env_t::pak_name; #ifndef __ANDROID__ sint16 env_t::menupos = MENU_TOP; bool env_t::single_toolbar_mode = false; bool env_t::stack_toolbars = true; sint16 env_t::dpi_scale = 100; bool env_t::single_info = 1; #else sint16 env_t::menupos = MENU_BOTTOM; bool env_t::single_toolbar_mode = true; bool env_t::stack_toolbars = false; sint16 env_t::dpi_scale = -1; bool env_t::single_info = 0; #endif sint16 env_t::fullscreen = WINDOWED; sint16 env_t::display_scale_percent = 100; bool env_t::reselect_closes_tool = true; sint8 env_t::pak_tile_height_step = 16; sint8 env_t::pak_height_conversion_factor = 1; env_t::height_conversion_mode env_t::height_conv_mode = env_t::HEIGHT_CONV_LINEAR; bool env_t::simple_drawing = false; bool env_t::simple_drawing_fast_forward = true; sint16 env_t::simple_drawing_normal = 4; sint16 env_t::simple_drawing_default = 24; uint8 env_t::follow_convoi_underground = 2; bool env_t::random_pedestrians = true; bool env_t::stop_pedestrians = true; char env_t::currency_symbol[16] = ""; bool env_t::currency_left = false; plainstring env_t::default_theme; const char *env_t::savegame_version_str = SAVEGAME_VER_NR; bool env_t::straight_way_without_control = false; bool env_t::networkmode = false; bool env_t::restore_UI = false; extern uint16 network_server_port; uint16 const &env_t::server = network_server_port; uint8 env_t::just_in_time = 1; // Disable announce by default uint32 env_t::server_announce = 0; bool env_t::easy_server = false; // Minimum is every 60 seconds, default is every 15 minutes (900 seconds), maximum is 86400 (1 day) sint32 env_t::server_announce_interval = 900; int env_t::server_port = env_t::server ? env_t::server : 13353; std::string env_t::server_dns; std::string env_t::server_alt_dns; // for dualstack systems std::string env_t::server_name; std::string env_t::server_comments; std::string env_t::server_email; std::string env_t::server_pakurl; std::string env_t::server_infurl; std::string env_t::server_admin_pw; std::string env_t::server_motd_filename; uint8 env_t::chat_unread_public = 0; uint8 env_t::chat_unread_company = 0; uint8 env_t::chat_unread_whisper = 0; vector_tpl env_t::listen; bool env_t::server_save_game_on_quit = false; bool env_t::reload_and_save_on_quit = true; uint8 env_t::network_heavy_mode = 0; sint32 env_t::server_frames_ahead = 4; sint32 env_t::additional_client_frames_behind = 4; sint32 env_t::network_frames_per_step = 4; uint32 env_t::server_sync_steps_between_checks = 24; bool env_t::pause_server_no_clients = false; std::string env_t::nickname = ""; // this is explicitly and interactively set by user => we do not touch it on init const char* env_t::language_iso = "en"; const char* env_t::language_names_iso = NULL; sint16 env_t::scroll_multi = -1; // start with same scrool as mouse as nowadays standard bool env_t::scroll_infinite = false; // since it fails with touch devices uint16 env_t::scroll_threshold = 8; sint16 env_t::global_volume = 127; uint32 env_t::sound_distance_scaling; sint16 env_t::midi_volume = 127; uint16 env_t::specific_volume[MAX_SOUND_TYPES]; std::string env_t::soundfont_filename = ""; std::string env_t::pakset_tutorial_dir = "tutorial"; bool env_t::global_mute_sound = false; bool env_t::mute_midi = false; bool env_t::shuffle_midi = true; sint16 env_t::window_snap_distance = 8; sint16 env_t::iconscaling=100; scr_size env_t::iconsize( 32, 32 ); uint8 env_t::chat_window_transparency = 75; bool env_t::hide_rail_return_ticket = true; bool env_t::numpad_always_moves_map = true; bool env_t::leftdrag_in_minimap = true; // only used internally => do not touch further bool env_t::quit_simutrans = false; // default settings for new games settings_t env_t::default_settings; // what finances are shown? (default bank balance) bool env_t::player_finance_display_account = true; // the following initialisation is not important; set values in init()! bool env_t::night_shift; bool env_t::hide_with_transparency; bool env_t::hide_trees; uint8 env_t::hide_buildings; bool env_t::hide_under_cursor; uint16 env_t::cursor_hide_range; bool env_t::show_single_ways; bool env_t::use_transparency_station_coverage; uint8 env_t::station_coverage_show; sint32 env_t::show_names; sint32 env_t::message_flags[4]; uint32 env_t::water_animation; uint32 env_t::ground_object_probability; uint32 env_t::moving_object_probability; bool env_t::road_user_info; bool env_t::tree_info; bool env_t::ground_info; uint8 env_t::show_factory_storage_bar; bool env_t::townhall_info; bool env_t::single_line_gui; bool env_t::window_buttons_right; bool env_t::second_open_closes_win; bool env_t::remember_window_positions; bool env_t::window_frame_active; log_t::level_t env_t::verbose_debug; bool env_t::pakset_debug = false; uint8 env_t::default_sortmode; uint32 env_t::default_mapmode; uint8 env_t::show_month; sint32 env_t::intercity_road_length; plainstring env_t::river_type[10]; uint8 env_t::river_types; sint32 env_t::autosave; uint32 env_t::fps; uint32 env_t::ff_fps; sint16 env_t::max_acceleration; uint8 env_t::num_threads; bool env_t::show_tooltips; rgb888_t env_t::tooltip_color_rgb; PIXVAL env_t::tooltip_color; rgb888_t env_t::tooltip_textcolor_rgb; PIXVAL env_t::tooltip_textcolor; sint8 env_t::toolbar_max_width; sint8 env_t::toolbar_max_height; rgb888_t env_t::cursor_overlay_color_rgb; PIXVAL env_t::cursor_overlay_color; rgb888_t env_t::background_color_rgb; PIXVAL env_t::background_color; bool env_t::draw_earth_border; bool env_t::draw_outside_tile; uint8 env_t::show_vehicle_states; bool env_t::visualize_schedule; sint8 env_t::daynight_level; bool env_t::left_to_right_graphs; uint32 env_t::tooltip_delay; uint32 env_t::tooltip_duration; sint8 env_t::show_money_message; uint8 env_t::gui_player_color_dark = 1; uint8 env_t::gui_player_color_bright = 4; #ifdef __ANDROID__ uint8 env_t::fontsize = 17; #else uint8 env_t::fontsize = 11; #endif std::string env_t::fontname; rgb888_t env_t::front_window_text_color_rgb; PIXVAL env_t::front_window_text_color; rgb888_t env_t::bottom_window_text_color_rgb; PIXVAL env_t::bottom_window_text_color; rgb888_t env_t::default_window_title_color_rgb; PIXVAL env_t::default_window_title_color; uint8 env_t::bottom_window_darkness; uint16 env_t::compass_map_position; uint16 env_t::compass_screen_position; uint32 env_t::default_ai_construction_speed; #ifdef __ANDROID__ // autoshow keyboard on textinput bool env_t::hide_keyboard = true; #else bool env_t::hide_keyboard = false; #endif // Define default settings. void env_t::init() { fontname = dr_get_system_font(); // settings for messages message_flags[0] = 0x017F; message_flags[1] = 0x0108; message_flags[2] = 0x0080; message_flags[3] = 0; night_shift = true; hide_with_transparency = true; hide_trees = false; hide_buildings = env_t::NOT_HIDE; hide_under_cursor = false; cursor_hide_range = 5; scroll_infinite = false; scroll_threshold = 16; visualize_schedule = true; /* station stuff */ use_transparency_station_coverage = true; station_coverage_show = 0; show_names = 3; player_finance_display_account = true; water_animation = 250; // 250ms per wave stage ground_object_probability = 10; // every n-th tile moving_object_probability = 1000; // every n-th tile follow_convoi_underground = 2; // slice through map road_user_info = false; tree_info = true; ground_info = false; townhall_info = false; single_info = true; single_line_gui = false; show_single_ways = false; random_pedestrians = true; stop_pedestrians = true; window_buttons_right = false; window_frame_active = false; second_open_closes_win = false; remember_window_positions = true; // debug level (0: only fatal, 1: error, 2: warning, 3: all verbose_debug = log_t::LEVEL_FATAL; default_sortmode = 1; // sort by amount default_mapmode = 0; // show cities savegame_version_str = SAVEGAME_VER_NR; show_month = DATE_FMT_US; show_factory_storage_bar = 0; intercity_road_length = 200; river_types = 0; // autosave every x months (0=off) autosave = 0; reload_and_save_on_quit = true; // default: make 25 frames per second (if possible) and 10 for faster fast forward fps = 25; ff_fps = 10; // maximum speedup set to 1000 (effectively no limit) max_acceleration=50; #ifdef MULTI_THREAD num_threads = min(MAX_THREADS,dr_get_max_threads()); #else num_threads = 1; #endif sound_distance_scaling = 10; show_tooltips = true; tooltip_color_rgb = { 0x39, 0x64, 0xD0 }; // COL_SOFT_BLUE tooltip_textcolor_rgb = { 0x00, 0x00, 0x00 }; // COL_BLACK toolbar_max_width = 0; toolbar_max_height = 0; cursor_overlay_color_rgb = { 0xFF, 0x80, 0x00 }; // COL_ORANGE background_color_rgb = { 0x40, 0x40, 0x40 }; // COL_GREY2 draw_earth_border = true; draw_outside_tile = false; show_vehicle_states = 1; daynight_level = 0; // midi/sound option global_volume = 127; midi_volume = 127; global_mute_sound = false; mute_midi = false; shuffle_midi = true; for( int i = 0; i < MAX_SOUND_TYPES; i++ ) { specific_volume[ i ] = 255; } left_to_right_graphs = false; tooltip_delay = 500; tooltip_duration = 5000; front_window_text_color_rgb = { 0xFF, 0xFF, 0xFF }; // COL_WHITE bottom_window_text_color_rgb = { 0xDD, 0xDD, 0xDD }; default_window_title_color_rgb = { 0xD7, 0x6B, 0x00 }; bottom_window_darkness = 25; default_ai_construction_speed = 8000; // upper right compass_map_position = ALIGN_RIGHT|ALIGN_TOP; // lower right compass_screen_position = 0; // disbale, other could be ALIGN_RIGHT|ALIGN_BOTTOM; // Listen on all addresses by default listen.append_unique("::"); listen.append_unique("0.0.0.0"); show_money_message = 0; language_names_iso = NULL; #ifndef __ANDROID__ menupos = MENU_TOP; single_toolbar_mode = false; stack_toolbars = true; dpi_scale = 100; single_info = 1; hide_keyboard = false; #else // here for Android menupos = MENU_BOTTOM; single_toolbar_mode = true; stack_toolbars = false; dpi_scale = -1; single_info = 0; // autoshow keyboard on textinput hide_keyboard = true; #endif } // save/restore environment void env_t::rdwr(loadsave_t *file) { // env_t used to be called umgebung_t - keep old name when saving and loading for compatibility xml_tag_t u( file, "umgebung_t" ); file->rdwr_short( scroll_multi ); file->rdwr_bool( night_shift ); file->rdwr_byte( daynight_level ); file->rdwr_long( water_animation ); if( file->is_version_less(110, 7) ) { bool dummy_b = 0; file->rdwr_bool( dummy_b ); } file->rdwr_byte( show_month ); file->rdwr_bool( use_transparency_station_coverage ); file->rdwr_byte( station_coverage_show ); file->rdwr_long( show_names ); file->rdwr_bool( hide_with_transparency ); file->rdwr_byte( hide_buildings ); file->rdwr_bool( hide_trees ); file->rdwr_long( message_flags[0] ); file->rdwr_long( message_flags[1] ); file->rdwr_long( message_flags[2] ); file->rdwr_long( message_flags[3] ); if ( file->is_loading() ) { if( file->is_version_less(110, 0) ) { // did not know about chat message, so we enable it message_flags[0] |= (1 << message_t::chat); // ticker message_flags[1] &= ~(1 << message_t::chat); // permanent window off message_flags[2] &= ~(1 << message_t::chat); // timed window off message_flags[3] &= ~(1 << message_t::chat); // do not ignore completely } if( file->is_version_less(112, 3) ) { // did not know about scenario message, so we enable it message_flags[0] &= ~(1 << message_t::scenario); // ticker off message_flags[1] |= (1 << message_t::scenario); // permanent window on message_flags[2] &= ~(1 << message_t::scenario); // timed window off message_flags[3] &= ~(1 << message_t::scenario); // do not ignore completely } } file->rdwr_bool( show_tooltips ); if ( file->is_version_less(120, 5) ) { uint8 color_idx = COL_SOFT_BLUE; file->rdwr_byte( color_idx ); env_t::tooltip_color_rgb = gfx->get_color_rgb(color_idx); color_idx = COL_BLACK; file->rdwr_byte( color_idx ); env_t::tooltip_textcolor_rgb = gfx->get_color_rgb(color_idx); } file->rdwr_long( autosave ); file->rdwr_long( fps ); if (file->is_version_atleast(121, 1)) { file->rdwr_long(ff_fps); } file->rdwr_short( max_acceleration ); file->rdwr_bool( road_user_info ); file->rdwr_bool( tree_info ); file->rdwr_bool( ground_info ); file->rdwr_bool( townhall_info ); file->rdwr_bool( single_info ); file->rdwr_byte( default_sortmode ); if( file->is_version_less(111, 4) ) { sint8 mode = log2(env_t::default_mapmode)-1; file->rdwr_byte( mode ); env_t::default_mapmode = mode>=0 ? 1 << mode : 0; } else { file->rdwr_long( env_t::default_mapmode ); } file->rdwr_bool( window_buttons_right ); file->rdwr_bool( window_frame_active ); if( file->is_version_less(112, 1) ) { // set by command-line, it does not make sense to save it. uint8 v = verbose_debug; file->rdwr_byte( v ); } file->rdwr_long( intercity_road_length ); if( file->is_version_less(102, 3) ) { bool no_tree = false; file->rdwr_bool( no_tree ); } file->rdwr_long( ground_object_probability ); file->rdwr_long( moving_object_probability ); if( file->is_loading() ) { // these three bytes will be lost ... const char *c = NULL; file->rdwr_str( c ); language_iso = c; } else { file->rdwr_str( language_iso ); } if (file->is_version_atleast(124, 5)) { if (file->is_loading()) { // these three bytes will be lost ... const char* c = NULL; file->rdwr_str(c); language_names_iso = c; } else { file->rdwr_str(language_names_iso); } } else if (file->is_loading()) { // default: same as GUI ... language_names_iso = NULL; } file->rdwr_short( global_volume ); file->rdwr_short( midi_volume ); file->rdwr_bool( global_mute_sound ); if( file->is_version_atleast( 121, 1 ) ) { for( int i = 0; i <= 5; i++ ) { file->rdwr_short( specific_volume[ i ] ); } } file->rdwr_bool( mute_midi ); file->rdwr_bool( shuffle_midi ); if( file->is_version_atleast(102, 2) ) { file->rdwr_byte( show_vehicle_states ); file->rdwr_bool( left_to_right_graphs ); } if( file->is_version_atleast(102, 3) ) { file->rdwr_long( tooltip_delay ); file->rdwr_long( tooltip_duration ); if ( file->is_version_less(120, 5) ) { uint8 color = COL_WHITE; file->rdwr_byte( color ); // to skip old parameter front_window_bar_color file->rdwr_byte( color ); env_t::front_window_text_color_rgb = gfx->get_color_rgb(color); file->rdwr_byte( color ); // to skip old parameter bottom_window_bar_color color = 209; // CITY_KI file->rdwr_byte( color ); env_t::bottom_window_text_color_rgb = gfx->get_color_rgb(color); } } if( file->is_version_atleast(110, 0) ) { bool dummy = false; file->rdwr_bool(dummy); //was add_player_name_to_message file->rdwr_short( window_snap_distance ); } if( file->is_version_atleast(111, 1) ) { file->rdwr_bool( hide_under_cursor ); file->rdwr_short( cursor_hide_range ); } if( file->is_version_atleast(111, 2) ) { file->rdwr_bool( visualize_schedule ); } if( file->is_version_atleast(111, 3) ) { plainstring str = nickname.c_str(); file->rdwr_str(str); if (file->is_loading()) { nickname = str ? str.c_str() : ""; } } if( file->is_version_atleast(112, 6) ) { if( file->is_version_less(120, 5) ) { uint8 color = COL_GREY2; file->rdwr_byte( color ); env_t::background_color_rgb = gfx->get_color_rgb(color); } file->rdwr_bool( draw_earth_border ); file->rdwr_bool( draw_outside_tile ); } if( file->is_version_atleast(112, 7) ) { file->rdwr_bool( second_open_closes_win ); file->rdwr_bool( remember_window_positions ); } if( file->is_version_atleast(112, 8) && file->is_version_less(123, 2) ) { bool show_delete_buttons = false; file->rdwr_bool( show_delete_buttons ); } if( file->is_version_atleast(120, 1) ) { file->rdwr_str( default_theme ); } if( file->is_version_atleast(120, 2) ) { if( file->is_version_atleast(122, 1)) { sint32 conv_mode = height_conv_mode; file->rdwr_long( conv_mode ); if (file->is_loading()) { height_conv_mode = (env_t::height_conversion_mode)::clamp(conv_mode, 0, (int)env_t::NUM_HEIGHT_CONV_MODES-1); } } else { bool new_convert = height_conv_mode != env_t::HEIGHT_CONV_LEGACY_SMALL; file->rdwr_bool( new_convert ); height_conv_mode = new_convert ? env_t::HEIGHT_CONV_LEGACY_LARGE : env_t::HEIGHT_CONV_LEGACY_SMALL; } } if( file->is_version_atleast(120, 5) ) { file->rdwr_color( background_color_rgb ); file->rdwr_color( tooltip_color_rgb ); file->rdwr_color( tooltip_textcolor_rgb ); file->rdwr_color( default_window_title_color_rgb ); file->rdwr_color( front_window_text_color_rgb ); file->rdwr_color( bottom_window_text_color_rgb ); file->rdwr_byte( bottom_window_darkness ); } if( file->is_version_atleast(120, 6) ) { plainstring str = fontname.c_str(); file->rdwr_str( str ); if (file->is_loading()) { fontname = str ? str.c_str() : ""; } file->rdwr_byte( fontsize ); } if( file->is_version_atleast(120, 7) ) { file->rdwr_byte(show_money_message); } if (file->is_version_atleast(120, 8)) { rdwr_win_settings(file); } if (file->is_version_atleast(120, 9)) { file->rdwr_byte(follow_convoi_underground); } if (file->is_version_atleast(121, 1)) { file->rdwr_long(sound_distance_scaling); file->rdwr_byte( gui_player_color_dark ); file->rdwr_byte( gui_player_color_bright ); } if( file->is_version_atleast( 122, 1 ) ) { file->rdwr_bool( env_t::numpad_always_moves_map ); plainstring str = soundfont_filename.c_str(); file->rdwr_str( str ); if( file->is_loading() ) { soundfont_filename = str ? str.c_str() : ""; } file->rdwr_short( menupos ); menupos &= 3; file->rdwr_bool( reselect_closes_tool ); file->rdwr_bool( single_line_gui ); file->rdwr_byte( show_factory_storage_bar ); file->rdwr_short( fullscreen ); } if( file->is_version_atleast(123, 1) ) { file->rdwr_short(display_scale_percent); file->rdwr_bool(scroll_infinite); } if( file->is_version_atleast(123, 2) ) { file->rdwr_short(scroll_threshold); file->rdwr_bool(single_toolbar_mode); file->rdwr_short(dpi_scale); if( file->is_loading() ) { dr_set_screen_scale(dpi_scale); } file->rdwr_bool(random_pedestrians); file->rdwr_bool(stop_pedestrians); } if (file->is_version_atleast(124, 2)) { file->rdwr_bool(leftdrag_in_minimap); } if (file->is_version_atleast(124, 3)) { file->rdwr_bool(currency_left); file->rdwr_str(currency_symbol,lengthof(currency_symbol)); } if (file->is_version_atleast(124, 4)) { file->rdwr_bool(show_single_ways); file->rdwr_bool(stack_toolbars); } // server settings are not saved, since they are server specific // and could be different on different servers on the same computers } simutrans-124.5/src/simutrans/dataobj/environment.h000066400000000000000000000351151520675460500225260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_ENVIRONMENT_H #define DATAOBJ_ENVIRONMENT_H #include #include "../simtypes.h" #include "../simconst.h" #include "../simcolor.h" #include "settings.h" #include "../display/scr_coord.h" #include "../tpl/vector_tpl.h" #include "../utils/plainstring.h" #include "../utils/log.h" #define TILE_HEIGHT_STEP (env_t::pak_tile_height_step) enum { MENU_LEFT, MENU_TOP, MENU_RIGHT, MENU_BOTTOM }; /** * Class to save all environment parameters, ie everything that changes * the look and feel of the game. Most of them can be changed by command-line * parameters or simuconf.tab files. */ class env_t { public: /// Points to the simutrans data directory which hosts translation theme etc. /// This directory may be write protected or only accesible for certain users /// May contain pak, scripts and addons static char base_dir[PATH_MAX]; /// Points to a global writable directory, where downloaded content content is stored /// This directory is writable to all users to store global content (like paksets) static char install_dir[PATH_MAX]; /// Points to a the user's writable directory /// This directory writable to the local user static char user_dir[PATH_MAX]; /// the selected pak (whole path, can be write protected) static std::string pak_dir; /// the folder name of the selected pak (with trailing path separator) static std::string pak_name; static sint16 menupos; static bool single_toolbar_mode; static bool stack_toolbars; static sint16 fullscreen; static sint16 dpi_scale; /// Controls size of the virtual display static sint16 display_scale_percent; static bool reselect_closes_tool; /// version for which the savegames should be created static const char *savegame_version_str; /// this the the preferred GUI theme at startup static plainstring default_theme; static char currency_symbol[16]; static bool currency_left; /** * @name Network-related settings */ /// @{ /// true, if we are in networkmode static bool networkmode; /// number of simulation frames server runs ahead of clients static sint32 server_frames_ahead; /// additional number of frames client is behind server static sint32 additional_client_frames_behind; /// number of sync_steps before one step /// @see karte_t::interactive() static sint32 network_frames_per_step; /// server sends information to clients for checking synchronization /// after this number of sync_steps /// @see karte_t::interactive() static uint32 server_sync_steps_between_checks; /// when true, restore the windows from a savegame static bool restore_UI; /// if we are the server, we are at this port /// @see network_init_server() static const uint16 &server; /// enable/disable server announcement static uint32 server_announce; static uint8 chat_unread_public; static uint8 chat_unread_company; static uint8 chat_unread_whisper; /// number of seconds between announcements static sint32 server_announce_interval; static uint8 chat_window_transparency; /// if true a kill event will save the game under recovery#portnr#.sve static bool server_save_game_on_quit; /// if true save game under autosave-#paksetname#.sve and reload it upon startup static bool reload_and_save_on_quit; static uint8 network_heavy_mode; /// @} end of Network-related settings /** * @name Information about server which is send to list-server */ /// @{ /// If set, we are in easy server mode, assuming the IP can change any moment and thus query it before announce) static bool easy_server; /// Default port to start a new server static int server_port; /// DNS name or IP address clients should use to connect to server static std::string server_dns; /// second DNS name or more liekly IP address (for a dualstack machine) to connect to our server static std::string server_alt_dns; /// Name of server for display on list server static std::string server_name; /// Comments about server for display on list server static std::string server_comments; /// Email address of server maintainer static std::string server_email; /// Download location for pakset needed to play on server static std::string server_pakurl; /// Link to further information about server static std::string server_infurl; /// Text to be show on startup; can be formatted like helpfiles static std::string server_motd_filename; /// @} end of Information about server /** * @name Network-related settings */ /// @{ /// Server admin password (for use with nettool) static std::string server_admin_pw; /// IP addresses to listen on/send announcements on static vector_tpl listen; /// pause server if no client connected static bool pause_server_no_clients; /// nickname of player static std::string nickname; /// @} end of Network-related settings /** * @name GUI settings and windows behavior */ /// @{ /// default GUI language static const char* language_iso; /// current name language (for towns, stops, ...) /// if empty, the same as for the main language static const char* language_names_iso; /// controls scrolling speed and scrolling direction static sint16 scroll_multi; /// enables infinite scrolling with trackball or mouse, by may fail with sytlus static bool scroll_infinite; /// scrolling with general tool (like building stops or setting halts) after dragging a threshold static uint16 scroll_threshold; /// converts numpad keys to arrows no matter of numlock state static bool numpad_always_moves_map; /// the leftbutton grags the minimap, not to real location static bool leftdrag_in_minimap; /// open info windows for pedestrian and private cars static bool road_user_info; /// open info windows for trees static bool tree_info; /// open info windows for ground tiles static bool ground_info; /// open info windows for townhalls static bool townhall_info; /// open only one info window per click on a map-square static bool single_info; /// pedestrians in cities static bool random_pedestrians; /// pedestrians at stops static bool stop_pedestrians; /// linelist enforcing single line GUI static bool single_line_gui; /// for schedules with rails hide the back ticket button static bool hide_rail_return_ticket; /// how to sort destination of goods /// @see freight_list_sorter_t::sort_mode_t static uint8 default_sortmode; /// default behavior of the map-window static uint32 default_mapmode; /// cut through the map when following convois? static uint8 follow_convoi_underground; ///which messages to display where? /** * message_flags[i] is bitfield, where bit is set if message should be show at location i, * where 0 = show message in ticker, 1 = open auto-close window, 2 = open persistent window, 3 = ignore message * @see message_option_t */ static sint32 message_flags[4]; static bool left_to_right_graphs; /** * window button at right corner (like Windows) */ static bool window_buttons_right; static bool second_open_closes_win; static bool remember_window_positions; static sint16 window_snap_distance; static sint16 iconscaling; static scr_size iconsize; /// customize your tooltips static bool show_tooltips; static rgb888_t tooltip_color_rgb; static PIXVAL tooltip_color; static rgb888_t tooltip_textcolor_rgb; static PIXVAL tooltip_textcolor; static uint32 tooltip_delay; static uint32 tooltip_duration; /// limit width and height of menu toolbars static sint8 toolbar_max_width; static sint8 toolbar_max_height; // how to highlight topped (untopped windows) static bool window_frame_active; static rgb888_t front_window_text_color_rgb; static PIXVAL front_window_text_color; static rgb888_t bottom_window_text_color_rgb; static PIXVAL bottom_window_text_color; static rgb888_t default_window_title_color_rgb; static PIXVAL default_window_title_color; static uint8 bottom_window_darkness; static uint8 gui_player_color_dark; static uint8 gui_player_color_bright; // default font name and -size static std::string fontname; static uint8 fontsize; // display compass static uint16 compass_map_position; static uint16 compass_screen_position; // what finances are shown? (default bank balance) static bool player_finance_display_account; /// @} end of GUI settings /** * @name Settings to control display of game world */ /// @{ /// show day-night cycle static bool night_shift; /// fixed day/night view level static sint8 daynight_level; /// show error/info tooltips over the vehicles static uint8 show_vehicle_states; /// display station coverage by transparent overlay /// (otherwise by colored squares) static bool show_single_ways; /// show station coverage indicators static uint8 station_coverage_show; /// display station coverage by transparent overlay /// (otherwise by colored squares) static bool use_transparency_station_coverage; /// use transparency to hide buildings and trees static bool hide_with_transparency; /// which is the deafult economy? static uint8 just_in_time; /// Three states to control hiding of building enum hide_buildings_states { NOT_HIDE = 0, ///< show all buildings SOME_HIDDEN_BUILDING, ///< hide city buildings ALL_HIDDEN_BUILDING ///< hide all buildings }; /// hide buildings if this is not NOT_HIDE static uint8 hide_buildings; /** * Set to true to hide all trees. "Hiding" is implemented by showing the * first pic which should be very small. */ static bool hide_trees; /// If hide_under_cursor is true then /// buildings and trees near mouse cursor will be hidden. static bool hide_under_cursor; /// Hide buildings and trees within range of mouse cursor static uint16 cursor_hide_range; /// color used for cursor overlay blending static rgb888_t cursor_overlay_color_rgb; static PIXVAL cursor_overlay_color; static sint8 show_money_message; /// color used for solid background draw static rgb888_t background_color_rgb; static PIXVAL background_color; /// true if the border shut be shown as cut through the earth static bool draw_earth_border; /// true if the outside tiles should be shown static bool draw_outside_tile; /** * Show labels (city and station names, ...) * and waiting indicator bar for stations * @see grund_t::display_overlay */ static sint32 show_names; /// Show factory storage bar static uint8 show_factory_storage_bar; /// if a schedule is open, show tiles which are used by it static bool visualize_schedule; /// time per water animation frame (0=off) static uint32 water_animation; /// how many internal pixel per height step (default 16) static sint8 pak_tile_height_step; /// new height for old slopes after conversion - 1=single height, 2=double height /// Only use during loading of old games! static sint8 pak_height_conversion_factor; enum height_conversion_mode { HEIGHT_CONV_LEGACY_SMALL, ///< Old (fixed) height conversion, small height difference HEIGHT_CONV_LEGACY_LARGE, ///< Old (fixed) height conversion, larger height difference HEIGHT_CONV_LINEAR, ///< linear interpolation between min_/max_allowed_height HEIGHT_CONV_CLAMP, ///< Use 1 height level per 1 greyscale level, clamp to allowed height (cut off mountains) NUM_HEIGHT_CONV_MODES }; static height_conversion_mode height_conv_mode; /// use the faster drawing routine (and allow for clipping errors) static bool simple_drawing; /// if tile-size is less than this value (will be updated automatically) static sint16 simple_drawing_normal; /// if tile-size is less than this value (set by simuconf.tab) static sint16 simple_drawing_default; /// always use fast drawing in fast forward static bool simple_drawing_fast_forward; /// format in which date is shown enum date_fmt { DATE_FMT_SEASON = 0, DATE_FMT_MONTH = 1, DATE_FMT_JAPANESE = 2, DATE_FMT_US = 3, DATE_FMT_GERMAN = 4, DATE_FMT_JAPANESE_NO_SEASON = 5, DATE_FMT_US_NO_SEASON = 6, DATE_FMT_GERMAN_NO_SEASON = 7 }; /** * show month in date? */ static uint8 show_month; /// @} end of Settings to control display of game world /** * @name Settings to control the simulation (to some extent) */ /// @{ static uint32 fps; ///< target frame rate static uint32 ff_fps; ///< target fps during fast forward static const uint32 min_fps = 5; ///< minimum target fps (actual fps may be lower for large zoom out on slow machines) static const uint32 max_fps = 100; /// maximum acceleration with fast forward static sint16 max_acceleration; /// number of threads to use (if MULTI_THREAD defined) static uint8 num_threads; /// false to quit the programs static bool quit_simutrans; /// @} end of Settings to control the simulation /** * @name Settings used at world creation */ /// @{ /// probability for ground objects (if exists) static uint32 ground_object_probability; /// probability for moving objects (if there) static uint32 moving_object_probability; /// maximum length of city connections static sint32 intercity_road_length; // AI construction speed for new games (default 8000) static uint32 default_ai_construction_speed; /** * Name of rivers; first the river with the lowest number */ static plainstring river_type[10]; /// number of different river types static uint8 river_types; /// @} /// Produce more debug info: /// can be set by command-line switch '-debug' static log_t::level_t verbose_debug; /// if set, dump pakset details static bool pakset_debug; /// do autosave every month? static sint32 autosave; /** * @name Midi/sound options */ /// @{ static sint16 global_volume, midi_volume; static bool mute_midi, shuffle_midi; static bool global_mute_sound; static uint16 specific_volume[MAX_SOUND_TYPES]; /// how dast are distant sounds fading (1: very fast 25: very little) static uint32 sound_distance_scaling; // FluidSynth MIDI parameters static std::string soundfont_filename; /// @} /// if true this will show a softkeyboard only when editing text /// default is off static bool hide_keyboard; /// default settings /// read in simmain.cc from various tab files /// @see simmain.cc static settings_t default_settings; /// construct always straight ways /// as if ctrl-key permanently pressed /// cannot be used in network mode static bool straight_way_without_control; /// initialize with default values static void init(); /** * load/saving these from file (settings.xml) * @see simmain.cc */ static void rdwr(loadsave_t *file); /// Name of tutorial scenario for the current pakset. /// Defaults to "tutorial". static std::string pakset_tutorial_dir; }; #endif simutrans-124.5/src/simutrans/dataobj/freelist.cc000066400000000000000000000027121520675460500221320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "../simtypes.h" #include "../simmem.h" #include "freelist.h" #include "../tpl/freelist_tpl.h" // for 64 bit, set this to 128 #define MAX_LIST_INDEX (128) // list for nodes size 8...64 #define NUM_LIST ((MAX_LIST_INDEX/4)+1) static freelist_size_t* all_lists[NUM_LIST] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #ifdef MULTI_THREAD static pthread_mutex_t freelist_mutex_create = PTHREAD_MUTEX_INITIALIZER;; #endif void* freelist_t::gimme_node(size_t size) { size_t idx = (size + 3) / 4; if (idx > NUM_LIST) { return xmalloc(size); } if (all_lists[idx] == NULL) { #ifdef MULTI_THREAD pthread_mutex_lock(&freelist_mutex_create); #endif all_lists[idx] = new freelist_size_t(idx * 4); #ifdef MULTI_THREAD pthread_mutex_unlock(&freelist_mutex_create); #endif } return all_lists[idx]->gimme_node(); } void freelist_t::putback_node(size_t size, void* p) { size = (size + 3) / 4; if (size > NUM_LIST) { free(p); } else { all_lists[size]->putback_node(p); } } void free_all_nodes() { for (int size = 0; size < NUM_LIST; size++) { if (all_lists[size]) { delete all_lists[size]; all_lists[size] = NULL; } } } simutrans-124.5/src/simutrans/dataobj/freelist.h000066400000000000000000000006421520675460500217740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_FREELIST_H #define DATAOBJ_FREELIST_H /** * Helper class to organize small memory objects i.e. nodes for linked lists * and such. */ class freelist_t { public: static void *gimme_node( size_t size ); static void putback_node( size_t size, void *p ); static void free_all_nodes(); }; #endif simutrans-124.5/src/simutrans/dataobj/gameinfo.cc000066400000000000000000000123511520675460500221020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gameinfo.h" #include "../network/network.h" #include "../network/network_socket_list.h" #include "settings.h" #include "translator.h" #include "environment.h" #include "../simdebug.h" #include "../world/simworld.h" #include "../world/simcity.h" #include "../simhalt.h" #include "../descriptor/ground_desc.h" #include "../player/simplay.h" #include "../gui/minimap.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "loadsave.h" #include "../network/pakset_info.h" #include "../simversion.h" #define MINIMAP_SIZE (64) gameinfo_t::gameinfo_t(karte_t *welt) : map_pixval(MINIMAP_SIZE,MINIMAP_SIZE), game_comment(""), file_name(""), pak_name("") { size_x = welt->get_size().x; size_y = welt->get_size().y; industries = welt->get_fab_list().get_count(); tourist_attractions = welt->get_attractions().get_count(); city_count = welt->get_cities().get_count(); citizen_count = 0; for(stadt_t* const i : welt->get_cities()) { citizen_count += i->get_einwohner(); } // make minimap const int gr_x = welt->get_size().x; const int gr_y = welt->get_size().y; for( uint16 i = 0; i < MINIMAP_SIZE; i++ ) { for( uint16 j = 0; j < MINIMAP_SIZE; j++ ) { const koord pos(i * gr_x / MINIMAP_SIZE, j * gr_y / MINIMAP_SIZE); const grund_t* gr = welt->lookup_kartenboden(pos); map_pixval.at(i,j) = minimap_t::calc_ground_color(gr); } } total_pass_transported = welt->get_finance_history_month(1,karte_t::WORLD_PAS_RATIO); total_mail_transported = welt->get_finance_history_month(1,karte_t::WORLD_MAIL_RATIO); total_goods_transported = welt->get_finance_history_month(1,karte_t::WORLD_GOODS_RATIO); convoi_count = welt->convoys().get_count(); for( int i=0; iget_player(i) ) { player_type[i] = player->get_ai_id(); if( !player->access_password_hash().empty() ) { player_type[i] |= player_t::PASSWORD_PROTECTED; } } } clients = socket_list_t::get_playing_clients(); halt_count = haltestelle_t::get_alle_haltestellen().get_count(); settings_t const& s = welt->get_settings(); freeplay = s.is_freeplay(); use_timeline = welt->get_timeline_year_month()!=0; current_starting_money = s.get_starting_money(welt->get_last_year()); current_year_month = welt->get_current_month(); bits_per_month = s.get_bits_per_month(); // names of the stations ... tstrncpy(language_code_names, translator::get_langs()[s.get_name_language_id()].iso, lengthof(language_code_names)); // will contain server-IP/name for network games file_name = s.get_filename(); // comment currently not used char const* const copyright = ground_desc_t::outside->get_copyright(); if (copyright && STRICMP("none", copyright) != 0) { // construct from outside object copyright string pak_name = copyright; } else { // construct from pak name pak_name = env_t::pak_name; pak_name.erase( pak_name.length()-1 ); } #ifdef REVISION game_engine_revision = atol( QUOTEME(REVISION) ); #else game_engine_revision = 0; #endif pakset_checksum = *(pakset_info_t::get_checksum()); motd = welt->get_settings().motd; } gameinfo_t::gameinfo_t(loadsave_t *file) : map_pixval(MINIMAP_SIZE,MINIMAP_SIZE), game_comment(""), file_name(""), pak_name("") { motd = ""; rdwr( file ); } void gameinfo_t::rdwr(loadsave_t *file) { xml_tag_t e( file, "gameinfo_t" ); file->rdwr_long( size_x ); file->rdwr_long( size_y ); for( int y=0; yrdwr_short( map_pixval.at(x,y) ); } } file->rdwr_long( industries ); file->rdwr_long( tourist_attractions ); file->rdwr_long( city_count ); file->rdwr_long( citizen_count ); file->rdwr_short( convoi_count ); file->rdwr_short( halt_count ); file->rdwr_longlong( total_pass_transported ); file->rdwr_longlong( total_mail_transported ); file->rdwr_longlong( total_goods_transported ); file->rdwr_longlong( total_goods_transported ); file->rdwr_bool( freeplay ); file->rdwr_bool( use_timeline ); file->rdwr_longlong( current_starting_money ); file->rdwr_long( current_year_month ); file->rdwr_short( bits_per_month ); file->rdwr_str(language_code_names, lengthof(language_code_names) ); char temp[PATH_MAX]; tstrncpy( temp, game_comment.c_str(), lengthof(temp) ); file->rdwr_str( temp, lengthof(temp) ); // game_comment if( file->is_loading() ) { game_comment = temp; } tstrncpy( temp, file_name.c_str(), lengthof(temp) ); file->rdwr_str( temp, lengthof(temp) ); // file_name if( file->is_loading() ) { file_name = temp; } tstrncpy( temp, pak_name.c_str(), lengthof(temp) ); file->rdwr_str( temp, lengthof(temp) ); // pak_name if( file->is_loading() ) { pak_name = temp; } if (file->is_version_atleast(124, 4)) { // get motd from server (to keep paket small, max message is truncated to 1024 bytes) tstrncpy(temp, motd.c_str(), lengthof(temp)); file->rdwr_str(temp, lengthof(temp)); // motd for server if (file->is_loading()) { motd = temp; } } file->rdwr_long(game_engine_revision); for( int i=0; i<16; i++ ) { file->rdwr_byte( player_type[i] ); } file->rdwr_byte( clients ); pakset_checksum.rdwr(file); } simutrans-124.5/src/simutrans/dataobj/gameinfo.h000066400000000000000000000052031520675460500217420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_GAMEINFO_H #define DATAOBJ_GAMEINFO_H #include #include "../simtypes.h" #include "../simconst.h" #include "../simcolor.h" #include "../tpl/array2d_tpl.h" #include "../network/checksum.h" class karte_t; class loadsave_t; /** * Info about the current game */ class gameinfo_t { private: sint32 size_x, size_y; /** * Two pixel arrays: one with indexed colors (sent by servers), * one with rgb colors (will be displayed) */ array2d_tpl map_pixval; sint32 industries; sint32 tourist_attractions; sint32 city_count; sint32 citizen_count; uint16 convoi_count; uint16 halt_count; sint64 total_pass_transported; sint64 total_mail_transported; sint64 total_goods_transported; bool freeplay; bool use_timeline; sint64 current_starting_money; uint32 current_year_month; sint16 bits_per_month; // names of the stations ... char language_code_names[4]; std::string game_comment; std::string file_name; std::string pak_name; uint32 game_engine_revision; checksum_t pakset_checksum; // 0 = empty, otherwise some value from simplay uint8 player_type[MAX_PLAYER_COUNT]; uint8 clients; // currently connected players public: std::string motd; gameinfo_t( karte_t *welt ); gameinfo_t( loadsave_t *file ); void rdwr( loadsave_t *file ); sint32 get_size_x() const {return size_x;} sint32 get_size_y() const {return size_y;} const array2d_tpl *get_map() const { return &map_pixval; } sint32 get_industries() const {return industries;} sint32 get_tourist_attractions() const {return tourist_attractions;} sint32 get_city_count() const {return city_count;} sint32 get_citizen_count() const {return citizen_count;} sint32 get_convoi_count() const {return convoi_count;} sint32 get_halt_count() const {return halt_count;} uint8 get_use_timeline() const {return use_timeline;} sint16 get_current_year() const {return current_year_month/12;} sint16 get_current_month() const {return (current_year_month%12);} sint16 get_bits_per_month() const {return bits_per_month;} bool is_freeplay() const { return freeplay; } sint64 get_current_starting_money() const {return current_starting_money;} uint32 get_game_engine_revision() const { return game_engine_revision; } const char *get_name_language_iso() const { return language_code_names; } const char *get_pak_name() const { return pak_name.c_str(); } uint8 get_player_type(uint8 i) const { return player_type[i]; } uint8 get_clients() const { return clients; } const checksum_t & get_pakset_checksum() const { return pakset_checksum; } }; #endif simutrans-124.5/src/simutrans/dataobj/height_map_loader.cc000066400000000000000000000056331520675460500237550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "height_map_loader.h" #include "environment.h" #include "../simio.h" #include "../sys/simsys.h" #include "../simmem.h" #include "../macros.h" #include "../io/raw_image.h" #include #include #include height_map_loader_t::height_map_loader_t(sint8 min_height, sint8 max_height, env_t::height_conversion_mode mode) : min_allowed_height(min_height), max_allowed_height(max_height), conv_mode(mode) { } // read height data from bmp or ppm files bool height_map_loader_t::get_height_data_from_file( const char *filename, sint8 groundwater, sint8 *&hfield, sint16 &ww, sint16 &hh, bool update_only_values ) { hfield = NULL; raw_image_t heightmap_img; if (!heightmap_img.read_from_file(filename)) { dbg->error("height_map_loader_t::get_height_data_from_file", "Cannot read heightmap file '%s'", filename); return false; } // report only values if( update_only_values ) { ww = heightmap_img.get_width(); hh = heightmap_img.get_height(); return true; } // ok, now read them in hfield = MALLOCN(sint8, heightmap_img.get_width()*heightmap_img.get_height()); if (!hfield) { dbg->error("height_map_loader_t::get_height_data_from_file", "Not enough memory"); return false; } memset( hfield, groundwater, heightmap_img.get_width()*heightmap_img.get_height() ); for (uint32 y=0; y(h0/32 - 28, min_allowed_height, max_allowed_height); // -28..19 } else { return ::clamp(h0/64 - 14, min_allowed_height, max_allowed_height); // -14..9 } } case env_t::HEIGHT_CONV_LEGACY_LARGE: { // new style, heights more spread out if( env_t::pak_height_conversion_factor == 2 ) { return ::clamp(h0/24 - 34, min_allowed_height, max_allowed_height); // -34..29 } else { return ::clamp(h0/48 - 18, min_allowed_height, max_allowed_height); // -18..13 } } case env_t::HEIGHT_CONV_LINEAR: { return min_allowed_height + (h0*(max_allowed_height-min_allowed_height)) / 0x5FA; } case env_t::HEIGHT_CONV_CLAMP: { return ::clamp((sint8)(((h0 * 0xFF) / 0x5FA) - 128), min_allowed_height, max_allowed_height); } default: dbg->fatal("height_map_loader_t::rgb_to_height", "Unhandled height conversion mode %d", conv_mode); } } simutrans-124.5/src/simutrans/dataobj/height_map_loader.h000066400000000000000000000027341520675460500236160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_HEIGHT_MAP_LOADER_H #define DATAOBJ_HEIGHT_MAP_LOADER_H #include "../simtypes.h" #include "environment.h" /** * Loads height data from BMP or PPM files. */ class height_map_loader_t { public: height_map_loader_t(sint8 min_allowed_height, sint8 max_allowed_height, env_t::height_conversion_mode conv_mode); /** * Reads height data from 8 or 24 bit bmp or ppm files. * * @param filename the file to load the height data from. * @param groundwater * @param update_only_values When true, do not allocate the height field; instead, only update @p ww and @p hh * @param[out] hfield 2d array of height values. * @param[out] ww width of @p hfield. On failure, this value is undefined. * @param[out] hh height of @p hfield. On failure, this value is undefined. * * @return true on success, false on failure. On success, @p hfield contains * the height field data (must be free()'d by the caller unless @p update_only_values is set). * On failure, @p hfield is set to NULL (Does not need to be free()'d) */ bool get_height_data_from_file( const char *filename, sint8 groundwater, sint8 *&hfield, sint16 &ww, sint16 &hh, bool update_only_values ); private: sint8 rgb_to_height( const int r, const int g, const int b ); private: const sint8 min_allowed_height; const sint8 max_allowed_height; const env_t::height_conversion_mode conv_mode; }; #endif simutrans-124.5/src/simutrans/dataobj/koord.cc000066400000000000000000000104121520675460500214270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "koord.h" #include "loadsave.h" #include "../display/scr_coord.h" #include "../utils/simrandom.h" #include "../simconst.h" // default: close and far away does not matter uint32 koord::locality_factor = 10000; const scr_coord scr_coord::invalid(-1, -1); const scr_size scr_size::invalid(-1, -1); const scr_size scr_size::inf(0x7fffffff, 0x7fffffff); const koord koord::invalid(-1, -1); const koord koord::north( 0, -1); const koord koord::east( 1, 0); const koord koord::south( 0, 1); const koord koord::west( -1, 0); const koord koord::nesw[] = { koord( 0, -1), koord( 1, 0), koord( 0, 1), koord(-1, 0) }; const koord koord::neighbours[] = { koord( -1, -1), koord( -1, 0 ), koord( -1, 1 ), koord( 0, 1 ), koord( 1, 1 ), koord( 1, 0 ), koord( 1, -1 ), koord( 0, -1 ) }; const koord koord::from_ribi[] = { koord( 0, 0), // none koord( 0, -1), // north (1) koord( 1, 0), // east (2) koord( 1, -1), // north-east (3) koord( 0, 1), // south (4) koord( 0, 0), // north-south (5) koord( 1, 1), // south-east (6) koord( 1, 0), // north-south-east (7) koord(-1, 0), // west (8) koord(-1, -1), // north-west (9) koord( 0, 0), // east-west (10) koord( 0, -1), // north-east-west (11) koord(-1, 1), // south-west (12) koord(-1, 0), // north-south-west (13) koord( 0, 1), // south-east-west (14) koord( 0, 0) // all }; const koord koord::from_hang[] = { koord( 0, 0), // 0:flat koord( 0, 0), // 1: koord( 0, 0), // 2: koord( 0, 0), // 3: koord( 0, 1), // 4:north single height slope koord( 0, 0), // 5: koord( 0, 0), // 6: koord( 0, 0), // 7: koord( 0, 1), // 8:north double height slope koord( 0, 0), // 9: koord( 0, 0), // 10: koord( 0, 0), // 11: koord( 1, 0), // 12:west single height slope koord( 0, 0), // 13: koord( 0, 0), // 14: koord( 0, 0), // 15: koord( 0, 0), // 16: koord( 0, 0), // 17: koord( 0, 0), // 18: koord( 0, 0), // 19: koord( 0, 0), // 20: koord( 0, 0), // 21: koord( 0, 0), // 22: koord( 0, 0), // 23: koord( 1, 0), // 24:west double height slope koord( 0, 0), // 25: koord( 0, 0), // 26: koord( 0, 0), // 27: koord(-1, 0), // 28:east single height slope koord( 0, 0), // 29: koord( 0, 0), // 30: koord( 0, 0), // 31: koord( 0, 0), // 32: koord( 0, 0), // 33: koord( 0, 0), // 34: koord( 0, 0), // 35: koord( 0, -1), // 36:south single height slope koord( 0, 0), // 37: koord( 0, 0), // 38: koord( 0, 0), // 39: koord( 0, 0), // 40: koord( 0, 0), // 41: koord( 0, 0), // 42: koord( 0, 0), // 43: koord( 0, 0), // 44: koord( 0, 0), // 45: koord( 0, 0), // 46: koord( 0, 0), // 47: koord( 0, 0), // 48: koord( 0, 0), // 49: koord( 0, 0), // 50: koord( 0, 0), // 51: koord( 0, 0), // 52: koord( 0, 0), // 53: koord( 0, 0), // 54: koord( 0, 0), // 55: koord(-1, 0), // 56:east double height slope koord( 0, 0), // 57: koord( 0, 0), // 58: koord( 0, 0), // 59: koord( 0, 0), // 60: koord( 0, 0), // 61: koord( 0, 0), // 62: koord( 0, 0), // 63: koord( 0, 0), // 64: koord( 0, 0), // 65: koord( 0, 0), // 66: koord( 0, 0), // 67: koord( 0, 0), // 68: koord( 0, 0), // 69: koord( 0, 0), // 70: koord( 0, 0), // 71: koord( 0, -1), // 72:south double height slope koord( 0, 0), // 73: koord( 0, 0), // 74: koord( 0, 0), // 75: koord( 0, 0), // 76: koord( 0, 0), // 77: koord( 0, 0), // 78: koord( 0, 0), // 79: koord( 0, 0) // 80: }; void koord::rdwr(loadsave_t *file) { xml_tag_t k( file, "koord" ); file->rdwr_short(x); file->rdwr_short(y); } // for debug messages... const char *koord::get_str() const { static char pos_str[32]; if(x==-1 && y==-1) { return "koord invalid"; } sprintf( pos_str, "%i,%i", x, y ); return pos_str; } const char *koord::get_fullstr() const { static char pos_str[32]; if(x==-1 && y==-1) { return "koord invalid"; } sprintf( pos_str, "(%i,%i)", x, y ); return pos_str; } // obey order of simrand among different compilers koord koord::koord_random( uint16 xrange, uint16 yrange ) { koord ret; ret.x = simrand(xrange); ret.y = simrand(yrange); return ret; } simutrans-124.5/src/simutrans/dataobj/koord.h000066400000000000000000000066361520675460500213060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_KOORD_H #define DATAOBJ_KOORD_H #include "ribi.h" #include "../simtypes.h" #include class loadsave_t; /** * 2D Coordinates */ class koord { public: // this is set by einstelugen_t static uint32 locality_factor; sint16 x; sint16 y; koord() : x(0), y(0) {} koord(sint16 xp, sint16 yp) : x(xp), y(yp) {} koord(ribi_t::ribi ribi) { *this = from_ribi[ribi]; } koord(slope_t::type slope) { *this = from_hang[slope]; } // use this instead of koord(simrand(x),simrand(y)) to avoid // different order on different compilers static koord koord_random(uint16 xrange, uint16 yrange); void rdwr(loadsave_t *file); const char *get_str() const; const char *get_fullstr() const; // including brackets const koord& operator += (const koord & k) { x += k.x; y += k.y; return *this; } const koord& operator -= (const koord & k) { x -= k.x; y -= k.y; return *this; } void rotate90( sint16 y_size ) { if( (x&y)<0 ) { // do not rotate illegal coordinates return; } sint16 new_x = y_size-y; y = x; x = new_x; } inline void clip_min( koord k_min ) { if (x < k_min.x) { x = k_min.x; } if (y < k_min.y) { y = k_min.y; } } inline void clip_max( koord k_max ) { if (x > k_max.x) { x = k_max.x; } if (y > k_max.y) { y = k_max.y; } } static const koord invalid; static const koord north; static const koord south; static const koord east; static const koord west; // the 4 basic directions as an Array static const koord nesw[4]; // 8 next neighbours static const koord neighbours[8]; private: static const koord from_ribi[16]; static const koord from_hang[81]; }; static inline uint32 koord_distance(const koord &a, const koord &b) { return abs(a.x - b.x) + abs(a.y - b.y); } // shortest distance in cardinal (N, E, S, W) and ordinal (NE, SE, SW, NW) directions static inline uint32 shortest_distance(const koord &a, const koord &b) { const uint32 x_offset = abs(a.x - b.x); const uint32 y_offset = abs(a.y - b.y); // square root of 2 is estimated by 181/128; 64 is for rounding if( x_offset>=y_offset ) { return (x_offset - y_offset) + ( ((y_offset * 181u) + 64u) >> 7 ); } else { return (y_offset - x_offset) + ( ((x_offset * 181u) + 64u) >> 7 ); } } // multiply the value by the distance weight static inline uint32 weight_by_distance(const sint32 value, const uint32 distance) { return value<=0 ? 0 : 1+(uint32)( ( ((sint64)value<<8) * koord::locality_factor ) / ( (sint64)koord::locality_factor + (sint64)(distance < 4u ? 4u : distance) ) ); } static inline koord operator * (const koord &k, const sint16 m) { return koord(k.x * m, k.y * m); } static inline koord operator / (const koord &k, const sint16 m) { return koord(k.x / m, k.y / m); } static inline bool operator == (const koord &a, const koord &b) { // only this works with O3 optimisation! return ((a.x-b.x)|(a.y-b.y))==0; } static inline bool operator != (const koord &a, const koord &b) { // only this works with O3 optimisation! return ((a.x-b.x)|(a.y-b.y))!=0; } static inline koord operator + (const koord &a, const koord &b) { return koord(a.x + b.x, a.y + b.y); } static inline koord operator - (const koord &a, const koord &b) { return koord(a.x - b.x, a.y - b.y); } static inline koord operator - (const koord &a) { return koord(-a.x, -a.y); } #endif simutrans-124.5/src/simutrans/dataobj/koord3d.cc000066400000000000000000000043371520675460500216670ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "koord3d.h" #include "../dataobj/loadsave.h" #include "../dataobj/environment.h" const koord3d koord3d::invalid(-1, -1, -1); void koord3d::rotate90( sint16 y_diff ) { sint16 new_x = y_diff-y; y = x; x = new_x; } void koord3d::rdwr(loadsave_t *file) { xml_tag_t k( file, "koord3d" ); sint16 v16; v16 = x; file->rdwr_short(v16); x = v16; v16 = y; file->rdwr_short(v16); y = v16; if(file->is_version_less(99, 5)) { file->rdwr_short(v16); if(v16!=-1) { z = (v16/16); } else { // keep invalid position z = -1; } } else { sint8 v8=z; file->rdwr_byte(v8); z = v8; } if( file->is_loading() && file->is_version_less(112, 7) && x != -1 && y != -1 ) { // convert heights from old single height saved game z *= env_t::pak_height_conversion_factor; } } // for debug messages... const char *koord3d::get_str() const { static char pos_str[32]; if(x==-1 && y==-1 && z==-1) { return "koord3d invalid"; } sprintf( pos_str, "%i,%i,%i", x, y, z ); return pos_str; } // for debug messages... const char *koord3d::get_fullstr() const { static char pos_str[32]; if(x==-1 && y==-1 && z==-1) { return "koord3d invalid"; } sprintf( pos_str, "(%i,%i,%i)", x, y, z ); return pos_str; } ribi_t::ribi koord3d_vector_t::get_ribi( uint32 index ) const { ribi_t::ribi ribi = ribi_t::none; koord3d pos = operator[](index); if( index > 0 ) { ribi |= ribi_type( operator[](index-1) - pos ); } if( index+1 < get_count() ) { ribi |= ribi_type( operator[](index+1) - pos ); } return ribi; } ribi_t::ribi koord3d_vector_t::get_short_ribi( uint32 index ) const { ribi_t::ribi ribi = ribi_t::none; const koord pos = operator[](index).get_2d(); if( index > 0 ) { const koord pos2 = operator[](index-1).get_2d(); if (koord_distance(pos,pos2)<=1) { ribi |= ribi_type( pos2-pos ); } } if( index+1 < get_count() ) { const koord pos2 = operator[](index+1).get_2d(); if (koord_distance(pos,pos2)<=1) { ribi |= ribi_type( pos2-pos ); } } return ribi; } void koord3d_vector_t::rotate90( sint16 y_size ) { for(koord3d & i : *this) { i.rotate90(y_size); } } simutrans-124.5/src/simutrans/dataobj/koord3d.h000066400000000000000000000053161520675460500215270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_KOORD3D_H #define DATAOBJ_KOORD3D_H #include "koord.h" #include "ribi.h" #include "../simtypes.h" #include "../tpl/vector_tpl.h" /** * 3D Coordinates */ class koord3d { public: sint16 x; sint16 y; sint8 z; koord3d() : x(0), y(0), z(0) {} koord3d(sint16 xp, sint16 yp, sint8 zp) : x(xp), y(yp), z(zp) {} koord3d(koord xyp, sint8 zp) : x(xyp.x), y(xyp.y), z(zp) {} const char *get_str() const; const char *get_fullstr() const; // including brackets void rotate90( sint16 y_diff ); void rdwr(loadsave_t* file); static const koord3d invalid; koord get_2d() const { return koord(x, y); } const koord3d& operator += (const koord3d& a) { x += a.x; y += a.y; z += a.z; return *this; } const koord3d& operator -= (const koord3d& a) { x -= a.x; y -= a.y; z -= a.z; return *this; } const koord3d& operator += (const koord& a) { x += a.x; y += a.y; return *this; } const koord3d& operator -= (const koord& a) { x -= a.x; y -= a.y; return *this; } } GCC_PACKED; static inline koord3d operator + (const koord3d& a, const koord3d& b) { return koord3d(a.x + b.x, a.y + b.y, a.z + b.z); } static inline koord3d operator - (const koord3d& a, const koord3d& b) { return koord3d(a.x - b.x, a.y - b.y, a.z - b.z); } static inline bool operator == (const koord3d& a, const koord3d& b) { // return a.x == b.x && a.y == b.y && a.z == b.z; return ((a.x-b.x)|(a.y-b.y)|(a.z-b.z))==0; } static inline bool operator != (const koord3d& a, const koord3d& b) { // return a.x != b.x || a.y != b.y || a.z != b.z; return ((a.x-b.x)|(a.y-b.y)|(a.z-b.z))!=0; } static inline koord3d operator - (const koord3d& a) { return koord3d(-a.x, -a.y, -a.z); } static inline koord3d operator + (const koord3d& a, const koord& b) { return koord3d(a.x + b.x, a.y + b.y, a.z); } static inline koord3d operator - (const koord3d& a, const koord& b) { return koord3d(a.x - b.x, a.y - b.y, a.z); } static inline uint32 koord_distance(koord3d a, koord b) { return abs(a.x - b.x) + abs(a.y - b.y); } static inline uint32 koord_distance(koord a, koord3d b) { return abs(a.x - b.x) + abs(a.y - b.y); } static inline uint32 koord_distance(koord3d a, koord3d b) { return abs(a.x - b.x) + abs(a.y - b.y); } /** * This class defines a vector_tpl with some * helper functions */ class koord3d_vector_t : public vector_tpl< koord3d > { public: // computes ribi at position i ribi_t::ribi get_ribi( uint32 index ) const; // computes ribi at position i only if distance to previous/next is not larger than 1 ribi_t::ribi get_short_ribi( uint32 index ) const; void rotate90( sint16 ); }; #endif simutrans-124.5/src/simutrans/dataobj/loadsave.cc000066400000000000000000000766731520675460500221340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include #include #include "../sys/simsys.h" #include "../simtypes.h" #include "../macros.h" #include "../simversion.h" #include "../simmem.h" #include "../simdebug.h" #include "../utils/plainstring.h" #include "../utils/simstring.h" #include "loadsave.h" #include "translator.h" #include "../io/rdwr/bzip2_file_rdwr_stream.h" #include "../io/rdwr/raw_file_rdwr_stream.h" #include "../io/rdwr/zlib_file_rdwr_stream.h" #if USE_ZSTD #include "../io/rdwr/zstd_file_rdwr_stream.h" #endif #include "../io/rdwr/compare_file_rd_stream.h" #define INVALID_RDWR_ID (-1) //#undef MULTI_THREAD // buffer size for read/write - bzip2 gains up to 8M for non-threaded, 1M for threaded. binary, zipped ok with 256K or smaller. #define LS_BUF_SIZE (1 << 20) // 1 MiB #define LS_MAX_STRING_LEN (0x7FFF) #define LS_STRING_BUF_SIZE (0x8000) // includes the trailing 0 #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_t ls_thread; static simthread_barrier_t loadsave_barrier; static pthread_mutex_t loadsave_mutex; static pthread_mutex_t readdata_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t readdata_cond = PTHREAD_COND_INITIALIZER; static int readdata_flag = 0; // > 0 read more, < 0 no data needed/error while reading // parameters passed starting a thread typedef struct{ loadsave_t *loadsave_routine; } loadsave_param_t; static loadsave_param_t ls; /* * Multi-threaded loading: * more complicated synchronization due to different sources of errors * - less data available than needed (noticed within load_thread) * - more data available than needed (main thread finishes reading but load_thread still waits) * * Communication of error by variable readdata_flag, protected by readdata_mutex * * Intended program flow: * * main load_thread * (data processing) (finalize) fill_buffer * < -- thread_barrier_wait ------------------------------------------------> * (error handling) pthread_cond_wait * if readdata_flag < 0 * load_thread already exited * not enough data -> fatal error * * readdata_flag = 1 readdata_flag = -1 * pthread_cond_broadcast ---------------------------------------------------> * repeat (error handling) * if readdata_flag < 0 * <------ end thread * if error occurred during previous fill_buffer * readdata_flag = -1 * (join threads) <------ end thread * repeat */ void *load_thread( void *ptr ) { loadsave_param_t *lsp = reinterpret_cast(ptr); int buf = 1; while(true) { const size_t bytes_read = lsp->loadsave_routine->fill_buffer(buf); // always wait to sync with main thread before filling the next buffer pthread_mutex_lock(&readdata_mutex); simthread_barrier_wait(&loadsave_barrier); while( readdata_flag == 0 ) { pthread_cond_wait(&readdata_cond, &readdata_mutex); } if (readdata_flag < 0) { // leave if no more data needed pthread_mutex_unlock(&readdata_mutex); break; } if (bytes_read == 0) { // nothing read into buffer, or error occurred // flag error to main thread readdata_flag = -1; pthread_mutex_unlock(&readdata_mutex); break; } readdata_flag = 0; pthread_mutex_unlock(&readdata_mutex); // switch buffer buf = (buf+1)&1; } return ptr; } void loading_trigger_fill_buffer() { // sync with other thread, tell to read more data simthread_barrier_wait(&loadsave_barrier); pthread_mutex_lock(&readdata_mutex); if (readdata_flag < 0) { pthread_mutex_unlock(&readdata_mutex); // reading thread exited due to error dbg->fatal("loadsave_t::read", "savegame corrupt, not enough data"); } readdata_flag = 1; // more data please pthread_cond_broadcast(&readdata_cond); pthread_mutex_unlock(&readdata_mutex); } void loading_finalize() { simthread_barrier_wait(&loadsave_barrier); // reader thread waits, signal end of loadingdata pthread_mutex_lock(&readdata_mutex); readdata_flag = -1; // no more data pthread_cond_broadcast(&readdata_cond); pthread_mutex_unlock(&readdata_mutex); } /* * Multi-threaded saving: * * - synchronization is done with barriers * - end-of-saving is signaled to thread with get_buf_pos(buf)==0, * which is protected by loadsave_mutex */ void *save_thread( void *ptr ) { loadsave_param_t *lsp = reinterpret_cast(ptr); int buf = 1; while(true) { // wait to sync with main thread before flushing the buffer simthread_barrier_wait(&loadsave_barrier); buf = (buf+1)&1; if( lsp->loadsave_routine->get_buf_pos(buf)==0 ) { // empty buffer after sync - signal to exit break; } lsp->loadsave_routine->flush_buffer(buf); } return ptr; } void saving_trigger_flush() { // sync with thread to flush the buffer simthread_barrier_wait(&loadsave_barrier); } void saving_finalize() { // first sync with thread causes buffer to be flushed simthread_barrier_wait(&loadsave_barrier); // second sync with empty buffer signals thread to exit simthread_barrier_wait(&loadsave_barrier); } #endif loadsave_t::mode_t loadsave_t::save_mode = bzip2; // default to use for saving loadsave_t::mode_t loadsave_t::autosave_mode = zipped; // default to use for autosaving int loadsave_t::save_level = 6; int loadsave_t::autosave_level = 1; void NORETURN loadsave_t::fatal(const char* who, const char* format, ...) { va_list argptr; va_start(argptr, format); static char formatbuffer[512]; const char* fn = filename.c_str(); sprintf(formatbuffer, "FATAL ERROR during reading of \"%s\"\n" "The file has been renamed to \"%s-error\"\n\n" "%s: %s\n" "\n" "Aborting program execution ...\n" "Please try to restart Simutrans again!\n", fn, fn, who, format); static char buffer[8192]; vsprintf(buffer, formatbuffer, argptr); va_end(argptr); close(); std::string fn_new = filename + "-error"; dr_remove(fn_new.c_str()); dr_rename(fn, fn_new.c_str()); dbg->custom_fatal(buffer); } loadsave_t::loadsave_t() : mode(binary), buffered(false), stream(NULL) { curr_buff = 0; } loadsave_t::~loadsave_t() { set_buffered(false); const bool saving = is_saving(); const char *errmsg = close(); if( errmsg ) { dbg->error( "loadsave_t::~loadsave_t", "Could not %s save file: %s", (saving ? "save" : "load"), errmsg ); } } void loadsave_t::set_buffered(bool enable) { if( enable ) { if( !buffered ) { buffered = true; curr_buff = 0; buff[0].pos = buff[1].pos = 0; buff[0].len = buff[1].len = 0; buff[0].buf = new char[LS_BUF_SIZE]; #ifdef MULTI_THREAD buff[1].buf = new char[LS_BUF_SIZE]; // second buffer only when multithreaded simthread_barrier_init(&loadsave_barrier, NULL, 2); pthread_mutex_init(&loadsave_mutex, NULL); pthread_mutex_init(&readdata_mutex, NULL); readdata_flag = 0; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); ls.loadsave_routine = this; pthread_create(&ls_thread, &attr, is_saving() ? save_thread : load_thread, (void *)&ls); pthread_attr_destroy(&attr); #endif } } else { if( buffered ) { if( is_saving() && buff[curr_buff].pos>0 ) { #ifdef MULTI_THREAD saving_finalize(); #else flush_buffer(curr_buff); #endif } #ifdef MULTI_THREAD if( !is_saving() ) { loading_finalize(); } pthread_join(ls_thread,NULL); pthread_mutex_destroy(&loadsave_mutex); pthread_mutex_destroy(&readdata_mutex); simthread_barrier_destroy(&loadsave_barrier); delete[] buff[1].buf; // second buffer only when multithreaded #endif delete[] buff[0].buf; buffered = false; } } } loadsave_t::file_status_t loadsave_t::rd_open(const char *filename_utf8) { close(); const file_classify_status_t cl_status = classify_save_file(filename_utf8, &finfo); if (cl_status != FILE_CLASSIFY_OK) { // file likely does not exist dbg->warning("loadsave_t::rd_open", "File '%s' does not exist or is not accessible", filename_utf8); return FILE_STATUS_ERR_INACCESSIBLE; } else if( finfo.version == INVALID_FILE_VERSION ) { return FILE_STATUS_ERR_NO_VERSION; } else if( finfo.version > (SIM_VERSION_MAJOR*1000 + SIM_SERVER_MINOR) ) { /* * Reading future versions will almost certainly lead to exceptions; so we close here. * It would be nice to give a detailed message what failed (like the fatal error does) * But this error may happening also in regular installations after running a nighly * so we just record the failure. */ return FILE_STATUS_ERR_FUTURE_VERSION; } // now open the file assert(stream == NULL); mode = 0; switch (finfo.file_type) { case file_info_t::TYPE_XML_ZSTD: mode = xml; // fallthrough case file_info_t::TYPE_ZSTD: mode |= zstd; #if USE_ZSTD stream = new zstd_file_rdwr_stream_t(filename_utf8, false, 0); break; #else dbg->warning("loadsave_t::rd_open", "Cannot read from '%s': Unsupported save file compression 'zstd'", filename_utf8); return FILE_STATUS_ERR_UNSUPPORTED_COMPRESSION; #endif case file_info_t::TYPE_XML_BZIP2: mode = xml; // fallthrough case file_info_t::TYPE_BZIP2: mode |= bzip2; stream = new bzip2_file_rdwr_stream_t(filename_utf8, false); break; case file_info_t::TYPE_XML_ZIPPED: mode = xml; // fallthrough case file_info_t::TYPE_ZIPPED: mode |= zipped; stream = new zlib_file_rdwr_stream_t(filename_utf8, false, 0); break; case file_info_t::TYPE_XML: mode = xml; // fallthrough default: stream = new raw_file_rdwr_stream_t(filename_utf8, false); break; } if (!stream || stream->get_status() != rdwr_stream_t::STATUS_OK) { const char *errmsg = close(); // file likely does not exist any longer if (errmsg) { dbg->warning("loadsave_t::rd_open", "Cannot read from '%s': %s", filename_utf8, errmsg); } else { dbg->warning("loadsave_t::rd_open", "Cannot read from '%s'", filename_utf8); } return FILE_STATUS_ERR_INACCESSIBLE; } // skip header size_t header_size = finfo.header_size; while (header_size != 0) { char buf[128]; const size_t sz = min(header_size, 128); stream->read(buf, sz); header_size -= sz; } filename = filename_utf8; return FILE_STATUS_OK; } loadsave_t::file_status_t loadsave_t::wr_open( const char *filename_utf8, mode_t m, int level, const char *pak_extension, const char *savegame_version ) { mode = m; close(); #if !USE_ZSTD if( mode & zstd ) { mode &= ~zstd; mode |= bzip2; dbg->warning( "loadsave_t::wr_open", "Compiled without zstd support, using bzip2!" ); } #endif assert(stream == NULL); switch (mode & ~xml) { #if USE_ZSTD case zstd: stream = new zstd_file_rdwr_stream_t(filename_utf8, true, level); break; #endif case bzip2: stream = new bzip2_file_rdwr_stream_t(filename_utf8, true); break; case zipped: stream = new zlib_file_rdwr_stream_t(filename_utf8, true, level); break; case binary: stream = new raw_file_rdwr_stream_t(filename_utf8, true); break; default: dbg->error("loadsave_t::wr_open", "Unsupported save file compression"); return FILE_STATUS_ERR_UNSUPPORTED_COMPRESSION; } if (stream->get_status() != rdwr_stream_t::STATUS_OK) { dbg->error("loadsave_t::wr_open", "Cannot open '%s' for writing!", filename_utf8); return (stream->get_status() == rdwr_stream_t::STATUS_ERR_FILE_INACCESSIBLE) ? FILE_STATUS_ERR_INACCESSIBLE : FILE_STATUS_ERR_CORRUPT; } set_buffered( true ); // get the right extension const char *start = pak_extension; const char *end = pak_extension + strlen(pak_extension)-1; const char *c = pak_extension; // find the start while(*c<*end) { if(*c==':' || *c=='\\' || *c=='/') { start = c+1; } c++; } assert(start\n\n", savegame_version, finfo.pak_extension ); write( str, n ); indent = 1; } return FILE_STATUS_OK; } const char *loadsave_t::close() { if (!stream) { return NULL; } if( is_xml() && stream->is_writing() && stream->get_status() == rdwr_stream_t::STATUS_OK) { // only write when close and no error occurred const char *end = "\n\n"; write( end, strlen(end) ); } if (buffered) { // flush buffers set_buffered(false); } const char *errmsg = NULL; switch (stream->get_status()) { case rdwr_stream_t::STATUS_OK: case rdwr_stream_t::STATUS_EOF: errmsg = NULL; break; case rdwr_stream_t::STATUS_ERR_NOT_INITIALIZED: errmsg = "Not initialized"; break; case rdwr_stream_t::STATUS_ERR_GENERIC_ERROR: errmsg = "Unknown error"; break; case rdwr_stream_t::STATUS_ERR_FILE_INACCESSIBLE: errmsg = "Could not open file"; break; case rdwr_stream_t::STATUS_ERR_FULL: errmsg = "Not enough empty space"; break; case rdwr_stream_t::STATUS_ERR_WRITEFAILURE: errmsg = "Failed to write file"; break; case rdwr_stream_t::STATUS_ERR_NO_VERSION: errmsg = "Unversioned save file"; break; // unused case rdwr_stream_t::STATUS_ERR_FUTURE_VERSION: errmsg = "Save file version too new"; break; // unused case rdwr_stream_t::STATUS_ERR_OBSOLETE_VERSION: errmsg = "Save file version too old"; break; // unused case rdwr_stream_t::STATUS_ERR_CORRUPT: errmsg = "Corrupt file"; break; } delete stream; stream = NULL; return errmsg ? translator::translate(errmsg) : NULL; } /************* from here on the actual data in/out routines ****************/ /** * Checks end-of-file */ bool loadsave_t::is_eof() { #ifdef MULTI_THREAD if (buffered) { pthread_mutex_lock( &loadsave_mutex ); } #endif const bool eof = (!buffered || (buff[0].pos >= buff[0].len && buff[1].pos >= buff[1].len)) && stream->get_status() == rdwr_stream_t::STATUS_EOF; #ifdef MULTI_THREAD if (buffered) { pthread_mutex_unlock(&loadsave_mutex); } #endif return eof; } void loadsave_t::lsputc(int c) { uint8 ch = c; write( &ch, 1 ); } int loadsave_t::lsgetc() { uint8 c[2]; if( read(c,1) ) { return c[0]; } return -1; } size_t loadsave_t::write(const void *buf, size_t len) { if (!buffered) { return stream->write(buf, len); } if( buff[curr_buff].pos+len<=LS_BUF_SIZE ) { // room in the buffer, copy it all for( unsigned i=0; iget_status() == rdwr_stream_t::STATUS_OK && buff[buf_num].pos > 0) { stream->write(buff[buf_num].buf, buff[buf_num].pos); } buff[buf_num].pos = 0; #ifdef MULTI_THREAD pthread_mutex_unlock(&loadsave_mutex); #endif } void loadsave_t::write_indent() { static const int max_indent = 64; static const char spaces[max_indent] = " "; write(spaces, min(indent, max_indent) ); } size_t loadsave_t::read(void *buf, size_t len) { if (!buffered) { return stream->read( buf, len); } if( len>=LS_BUF_SIZE*2 ) { dbg->fatal("loadsave_t::read()","Request for %d too long", len); } if( buff[curr_buff].pos+len<=buff[curr_buff].len ) { // room in the buffer, copy it all for( unsigned i=0; i0 ) { const unsigned left = buff[curr_buff].len-buff[curr_buff].pos; while( ibuff[curr_buff].len ) { dbg->fatal("loadsave_t::read","savegame corrupt, not enough data"); } // copy the rest while( iread(buff[ buf_num ].buf, LS_BUF_SIZE); #ifdef MULTI_THREAD pthread_mutex_lock(&loadsave_mutex); #endif const rdwr_stream_t::status_t status = stream->get_status(); const bool stream_ok = (status == rdwr_stream_t::STATUS_EOF || status == rdwr_stream_t::STATUS_OK); assert((status == rdwr_stream_t::STATUS_OK) == (sz == LS_BUF_SIZE)); buff[buf_num].pos = 0; buff[buf_num].len = stream_ok ? sz : 0; // buf_len is unsigned, set to zero in case of error #ifdef MULTI_THREAD pthread_mutex_unlock(&loadsave_mutex); #endif return sz; } /*************** High level routines to read/write data types ************* * (check also for Intel/Motorola) etc */ void loadsave_t::rdwr_byte(sint8 &c) { if(!is_xml()) { if(is_saving()) { lsputc(c); } else { c = (sint8)lsgetc(); } } else { sint64 ll = c; rdwr_xml_number( ll, "i8" ); c = (sint8)ll; } } void loadsave_t::rdwr_byte(uint8 &c) { sint8 cc=c; rdwr_byte(cc); c = (uint8)cc; } void loadsave_t::rdwr_short(sint16 &i) { if(!is_xml()) { if (is_saving()) { #ifdef SIM_BIG_ENDIAN sint16 ii = endian(i); write(&ii, sizeof(sint16)); #else write(&i, sizeof(sint16)); #endif } else { #ifdef SIM_BIG_ENDIAN uint16 ii; read(&ii, sizeof(sint16)); i = endian(ii); #else read(&i, sizeof(sint16)); #endif } } else { sint64 ll = i; rdwr_xml_number( ll, "i16" ); i = (sint16)ll; } } void loadsave_t::rdwr_short(uint16 &i) { sint16 ii=i; rdwr_short(ii); i = (uint16)ii; } void loadsave_t::rdwr_long(sint32 &l) { if(!is_xml()) { if (is_saving()) { #ifdef SIM_BIG_ENDIAN uint32 ii = endian(l); write(&ii, sizeof(uint32)); #else write(&l, sizeof(sint32)); #endif } else { #ifdef SIM_BIG_ENDIAN uint32 ii; read(&ii, sizeof(uint32)); l = endian(ii); #else read(&l, sizeof(sint32)); #endif } } else { sint64 ll = l; rdwr_xml_number( ll, "i32" ); l = (sint32)ll; } } void loadsave_t::rdwr_long(uint32 &l) { sint32 ll=l; rdwr_long(ll); l = (uint32)ll; } void loadsave_t::rdwr_color(rgb888_t &col) { uint32 v = col.r<<16 | col.g<<8 | col.b; rdwr_long(v); col.r = v >> 16; col.g = v >> 8; col.b = v >> 0; } void loadsave_t::rdwr_longlong(sint64 &ll) { if(!is_xml()) { if (is_saving()) { #ifdef SIM_BIG_ENDIAN sint64 ii = endian(ll); write(&ii, sizeof(sint64)); #else write(&ll, sizeof(sint64)); #endif } else { #ifdef SIM_BIG_ENDIAN uint64 ii; read(&ii, sizeof(sint64)); ll = endian(ii); #else read(&ll, sizeof(sint64)); #endif } } else { rdwr_xml_number( ll, "i64" ); } } void loadsave_t::rdwr_double(double &dbl) { if(!is_xml()) { if(is_saving()) { write(&dbl, sizeof(double)); } else { read(&dbl, sizeof(double)); } } else { // so far only with 3 digit precision, but this is ok for only two locations used sint64 ll= (sint64)((dbl*1000.0)+0.5); rdwr_xml_number( ll, "d1000" ); dbl = (((double)ll)+0.000001)/1000.0; } } void loadsave_t::rdwr_bool(bool &i) { if( !is_xml() ) { if(is_saving()) { lsputc(i ? '1' : '0'); } else { i = lsgetc()=='1'; } } else { // bool xml if(is_saving()) { write_indent(); if( i ) { write( "true\n", sizeof("true\n")-1 ); } else { write( "false\n", sizeof("false\n")-1 ); } } else { // find start of tag while( lsgetc()!='<' ) { /* nothing */ } // check for correct tag char buffer[7]; read( buffer, 5 ); buffer[5] = 0; if( strcmp("bool>",buffer)!=0 ) { fatal( "loadsave_t::rdwr_bool()","expected \"\", got \"<%s\"", buffer ); } read( buffer, 4 ); buffer[4] = 0; i = strcmp("true",buffer)==0; while( lsgetc()!='<' ) { /* nothing */ } read( buffer, 6 ); buffer[6] = 0; if( strcmp("/bool>",buffer)!=0 ) { fatal( "loadsave_t::rdwr_bool()","expected \"\", got \"<%s\"", buffer ); } } } } void loadsave_t::rdwr_xml_number(sint64 &s, const char *typ) { if(is_saving()) { static char nr[256]; size_t len = sprintf( nr, "%*s<%s>%.0f\n", indent, "", typ, (double)s, typ ); write( nr, len ); } else { const int len = (int)strlen(typ); assert(len<256); // find start of tag and handle eof correctly while( 1 ) { int ch = lsgetc(); if( ch == '<' ) { break; } if( ch < 0 ) { fatal( "loadsave_t::rdwr_xml_number()", "Reached end of file while trying to read <%s>", typ ); } } // check for correct tag char buffer[256]; read( buffer, len ); buffer[len] = 0; if( strcmp(typ,buffer)!=0 ) { fatal( "loadsave_t::rdwr_xml_number()","expected \"<%s>\", got \"<%s>\"", typ, buffer ); } while( lsgetc()!='>' ) ; // read number; s = 0; bool minus = false; while(!is_eof()) { const int c = lsgetc(); if(c>='0' && c<='9' ) { s = (s*10)+(c-'0'); } else { if(s==0) { if( c=='-') { minus = true; continue; } else if(c=='+') { minus = false; continue; } } if(c==' ') { while( lsgetc()!='<' ) { /* nothing */ } break; } else if(c=='<') { break; } else { fatal( "loadsave_t::rdwr_xml_number()", "type %s, found %c in number!", typ, c ); } } } if(minus) { s = -s; } if( lsgetc()!='/' ) { fatal( "loadsave_t::rdwr_xml_number()", "missing '/' (not closing tag)" ); } read( buffer, len ); buffer[6] = 0; if( strcmp(typ,buffer)!=0 ) { fatal( "loadsave_t::rdwr_xml_number()","expected \"\", got \"\"", typ, buffer ); } while( lsgetc()!='>' ) ; } } // s is a malloc-ed string (will be freed and newly allocated on load time!) void loadsave_t::rdwr_str(const char *&s) { if(!is_xml()) { sint16 size; if(is_saving()) { size = s ? (sint16)min(LS_MAX_STRING_LEN,strlen(s)) : 0; #ifdef SIM_BIG_ENDIAN { uint16 ii = endian(size); write(&ii, sizeof(sint16)); } #else write(&size, sizeof(sint16)); #endif if(size > 0) { write(s, size); } } else { #ifdef SIM_BIG_ENDIAN { uint16 ii; read(&ii, sizeof(uint16)); size = endian(ii); } #else read(&size, sizeof(sint16)); #endif char *sneu = NULL; if(size > 0) { sneu = MALLOCN(char, size + 1); read(sneu, size); sneu[size] = '\0'; } if(s) { free(const_cast(s)); } s = sneu; } } else { // use CDATA tag: if(is_saving()) { write_indent(); write( "\n", 4 ); } else { char buffer[LS_STRING_BUF_SIZE]; rdwr_str( buffer, LS_STRING_BUF_SIZE ); if(s) { free(const_cast(s)); } s = buffer[0]!=0 ? strdup(buffer) : NULL; } } } // read a string into a preallocated buffer void loadsave_t::rdwr_str( char* result_buffer, size_t const size) { if(!is_xml()) { uint16 len; if(is_saving()) { len = (uint16)min(LS_MAX_STRING_LEN,strlen(result_buffer)); #ifdef SIM_BIG_ENDIAN { sint16 ii = endian(len); write(&ii, sizeof(sint16)); } #else write(&len, sizeof(uint16)); #endif write(result_buffer, len); } else { read(&len, sizeof(uint16)); len = endian(len); if( len >= size) { fatal( "loadsave_t::rdwr_str()","string longer (%i) than allowed size (%i)", len, size ); } read(result_buffer, len); result_buffer[len] = '\0'; } } else { // use CDATA tag: char *s = result_buffer; if(is_saving()) { write_indent(); write( "\n", 4 ); } else { // find start of tag while( lsgetc()!='<' ) { /* nothing */ } // check for correct tag char buffer[10]; read( buffer, 7 ); bool string = true; if (!strstart(buffer, "string>")) { if (!strstart(buffer, "![CDATA") || lsgetc() != '[') { buffer[7] = 0; fatal( "loadsave_t::rdwr_str()","expected str \"' ) { if (i >= 8 && strstart(s - 8, "=2 && strstart(s-3,"]]>") ) { s[-3] = 0; strcpy( result_buffer, temp ); return; } } *s = 0; fatal( "loadsave_t::rdwr_str()","string too long (exceeded %i characters)", size ); } } } } void loadsave_t::rdwr_str(plainstring& s) { if( is_loading() ) { const char* buf = NULL; rdwr_str(buf); if( buf ) { s = buf; free( const_cast(buf) ); } else { s = ""; } } else { char const* tmp = s.c_str(); rdwr_str(tmp); } } void loadsave_t::start_tag(const char *tag) { if( is_xml() ) { if(is_saving()) { write_indent(); write( "<", 1 ); write( tag, strlen(tag) ); write( ">\n", 2 ); indent ++; } else { char buf[256]; // find start of tag while( lsgetc()!='<' ) { /* nothing */ } read( buf, strlen(tag) ); if( !strstart(buf, tag) ) { fatal( "loadsave_t::start_tag()","expected \"%s\", got \"%s\"", tag, buf ); } while( lsgetc()!='>' ) ; } } } void loadsave_t::end_tag(const char *tag) { if( is_xml() ) { if(is_saving()) { indent --; write_indent(); write( "\n", 2 ); } else { // just use start tag with the end character ... char buf[256]; tstrncpy( buf+1, tag, 254 ); buf[0] = '/'; start_tag(buf); } } } void loadsave_t::wr_obj_id(sint16 id) { if(!is_saving()) { dbg->fatal( "loadsave_t::wr_obj_id()", "must be only called during saving!" ); } if(!is_xml()) { lsputc( id ); } else { sint64 ll=id; rdwr_xml_number( ll, "id" ); } } sint16 loadsave_t::rd_obj_id() { if(is_saving()) { dbg->fatal( "loadsave_t::rd_obj_id()", "must be only called during reading!" ); return INVALID_RDWR_ID; } if(!is_xml()) { sint8 idc; read(&idc, sizeof(sint8)); return (sint8)idc; } else { sint64 ll; rdwr_xml_number( ll, "id" ); return (sint16)ll; } } void loadsave_t::wr_obj_id(const char *id_text) { if(is_saving()) { if( !is_xml() ) { write( id_text, strlen(id_text) ); lsputc( 10 ); } else { write( "\n", 3 ); } } } void loadsave_t::rd_obj_id(char *id_buf, int size) { if(!is_saving()) { if( !is_xml() ) { int i=0; *id_buf = 0; do { id_buf[i++] = lsgetc(); } while (i < size && id_buf[i - 1] != 10); id_buf[i-1] = 0; } else { char buf[6]; // find start of tag while( lsgetc()!='<' ) { /* nothing */ } read( buf, 6 ); buf[5] = 0; if (!strstart(buf, "")) { fatal( "loadsave_t::rd_obj_id()","id tag not properly closed!" ); } } } } uint32 loadsave_t::int_version(const char *version_text, char *pak_extension_str) { // major number (0..) uint32 v0 = atoi(version_text); while(*version_text && *version_text++ != '.') ; if(!*version_text) { return 0; } // middle number (.99.) uint32 v1 = atoi(version_text); while(*version_text && *version_text++ != '.') ; if(!*version_text) { return 0; } // minor number (..08) uint32 v2 = atoi(version_text); uint32 version = v0 * 1000000 + v1 * 1000 + v2; while(*version_text && isdigit(*version_text)) { version_text++; } // simutrans-experimental savegame? // the next char is either 'b'/'z'/'-', // if it is '.' the we try to load a simutrans-experimental savegame if(*version_text == '.') { // Simutrans Extended savegame, we can't load it, return version=0 if (pak_extension_str) { std::strcpy(pak_extension_str,"(st-exp)"); } return 0; } if( version<=102002 ) { /* the compression and the mode we determined already ourselves * (otherwise we cannot read this => leave the mode alone but for unknown modes!) */ if (strstart(version_text, "bin")) { version_text += 3; } else if (strstart(version_text, "zip")) { version_text += 3; } else if( *version_text ) { // illegal version ... if (pak_extension_str) { std::strcpy(pak_extension_str,"(broken)"); } return 0; } } else { // skip the minus sign if (*version_text=='-') { version_text++; } } if( pak_extension_str ) { if( *version_text ) { // also pak extension was saved if(version>=99008) { while( *version_text>=32 ) { *pak_extension_str = *version_text; pak_extension_str++; version_text++; } } } *pak_extension_str = 0; } return version; } stream_loadsave_t::stream_loadsave_t(rdwr_stream_t *stream) { this->stream = stream; finfo.version = int_version(VERSION_NUMBER, NULL); } compare_loadsave_t::compare_loadsave_t(loadsave_t *file1, loadsave_t *file2) { stream = new compare_file_rd_stream_t(file1->stream, file2->stream); finfo.version = int_version(VERSION_NUMBER, NULL); set_buffered(false); file1->set_buffered(false); file2->set_buffered(false); } compare_loadsave_t::~compare_loadsave_t() { delete stream; stream = NULL; } simutrans-124.5/src/simutrans/dataobj/loadsave.h000066400000000000000000000142061520675460500217560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_LOADSAVE_H #define DATAOBJ_LOADSAVE_H #include #include #include "../simtypes.h" #include "../io/classify_file.h" #include "../io/rdwr/rdwr_stream.h" class plainstring; struct rgb888_t; /** * This class replaces the FILE when loading and saving games. * * Can now read and write 3 formats: text, binary and zipped * Input format is automatically detected. * Output format has a default, changeable with set_savemode, but can be * overwritten in wr_open. */ class loadsave_t { private: // during reading, a fatal error will rename the file to "oldnam"-error, so one can try again void NORETURN fatal(const char* who, const char* format, ...); struct buf_t { size_t pos; size_t len; char *buf; }; public: enum mode_t { binary = 0, text = 1 << 0, xml = 1 << 1, zipped = 1 << 2, bzip2 = 1 << 3, zstd = 1 << 4, xml_zipped = xml | zipped, xml_bzip2 = xml | bzip2, xml_zstd = xml | zstd }; enum file_status_t { FILE_STATUS_OK = 0, FILE_STATUS_ERR_GENERIC_ERROR, FILE_STATUS_ERR_INACCESSIBLE, FILE_STATUS_ERR_CORRUPT, FILE_STATUS_ERR_NO_VERSION, FILE_STATUS_ERR_FUTURE_VERSION, FILE_STATUS_ERR_UNSUPPORTED_COMPRESSION }; protected: int mode; ///< See mode_t bool buffered; unsigned curr_buff; buf_t buff[2]; int indent; // only for XML formatting file_info_t finfo; std::string filename; rdwr_stream_t *stream; /// @sa putc inline void lsputc(int c); /// @sa getc inline int lsgetc(); size_t read(void *buf, size_t len); size_t write(const void *buf, size_t len); void write_indent(); void rdwr_xml_number(sint64 &s, const char *typ); loadsave_t(const loadsave_t&); loadsave_t& operator=(const loadsave_t&); friend void *save_thread( void *ptr ); friend void *load_thread( void *ptr ); /** * Reads into buffer number @p buf_num. * @returns number of bytes read or -1 in case of error */ size_t fill_buffer(int buf_num); void flush_buffer(int buf_num); bool is_xml() const { return mode&xml; } public: static mode_t save_mode; ///< default to use for saving static mode_t autosave_mode; ///< default to use for autosaves and network mode client temp saves static int save_level; ///< default to use for compression (various libraries allow for size/speed settings) static int autosave_level; /** * Parses the version information from @p version_text to a version number. * * @param version_text * @param[out] pak Pointer to a sufficiently large buffer (>= 64 chars); when the function returns, * @p pak contains the pakset extension string. May be NULL. * @retval 0 if an error occurred or the save cannot be loaded * @retval !=0 the save version; in this case we can read the save file. */ static uint32 int_version(const char *version_text, char *pak); loadsave_t(); ~loadsave_t(); /// Open save file for reading. File format is detected automatically. file_status_t rd_open(const char *filename); /// Open save file for writing. file_status_t wr_open(const char *filename, mode_t mode, int level, const char *pak_extension, const char *savegame_version ); /// Close an open save file. Returns an error message if saving was unsuccessful, the empty string otherwise. const char *close(); static void set_savemode(mode_t mode) { save_mode = mode; } static void set_autosavemode(mode_t mode) { autosave_mode = mode; } static void set_savelevel(int level) { save_level = level; } static void set_autosavelevel(int level) { autosave_level = level; } /** * Checks end-of-file */ bool is_eof(); void set_buffered(bool enable); unsigned get_buf_pos(int buf_num) const { return buff[buf_num].pos; } bool is_loading() const { return stream && !stream->is_writing(); } bool is_saving() const { return stream && stream->is_writing(); } const char *get_pak_extension() const { return finfo.pak_extension; } uint32 get_version_int() const { return finfo.version; } inline bool is_version_atleast(uint32 major, uint32 save_minor) const { return !is_version_less(major, save_minor); } inline bool is_version_less(uint32 major, uint32 save_minor) const { return finfo.version < major * 1000U + save_minor; } inline bool is_version_equal(uint32 major, uint32 save_minor) const { return finfo.version == major * 1000U + save_minor; } void rdwr_byte(sint8 &c); void rdwr_byte(uint8 &c); void rdwr_short(sint16 &i); void rdwr_short(uint16 &i); void rdwr_long(sint32 &i); void rdwr_long(uint32 &i); void rdwr_longlong(sint64 &i); void rdwr_bool(bool &i); void rdwr_double(double &dbl); void rdwr_color(rgb888_t &color); void wr_obj_id(short id); short rd_obj_id(); void wr_obj_id(const char *id_text); void rd_obj_id(char *id_buf, int size); // s is a malloc-ed string (will be freed and newly allocated on load time!) void rdwr_str(const char *&s); /// @p s is a buf of size given /// @p size includes space for the trailing 0 void rdwr_str(char *s, size_t size); /** * Read/Write plainstring. * @param str the string to be read/written * @post str should not be NULL after reading. */ void rdwr_str(plainstring& str); // only meaningful for XML void start_tag( const char *tag ); void end_tag( const char *tag ); // use this for enum types template void rdwr_enum(X &x) { sint32 int_x; if(is_saving()) { int_x = (sint32)x; } rdwr_long(int_x); if(is_loading()) { x = (X)int_x; } } friend class compare_loadsave_t; // to access stream }; /** * Class used to produce hash of savegame_version */ class stream_loadsave_t : public loadsave_t { public: stream_loadsave_t(rdwr_stream_t *stream); }; /** * Class used to compare two savegames */ class compare_loadsave_t : public loadsave_t { public: compare_loadsave_t(loadsave_t *file1, loadsave_t *file2); ~compare_loadsave_t(); }; // this produces semi-automatic hierarchies class xml_tag_t { private: loadsave_t *file; const char *tag; public: xml_tag_t( loadsave_t *f, const char *t ) : file(f), tag(t) { file->start_tag(tag); } ~xml_tag_t() { file->end_tag(tag); } }; #endif simutrans-124.5/src/simutrans/dataobj/marker.cc000066400000000000000000000044771520675460500216100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simtypes.h" #include "../simdebug.h" #include "../ground/grund.h" #include "marker.h" marker_t marker_t::the_instance; marker_t marker_t::second_instance; void marker_t::init(int world_size_x, int world_size_y) { // do not reallocate it, if same size ... cached_size_x = world_size_x; int new_bits_length = (world_size_x*world_size_y + bit_mask) / (bit_unit); if( bits_length != new_bits_length ) { bits_length = new_bits_length; delete [] bits; if(bits_length) { bits = new unsigned char[bits_length]; } else { bits = NULL; } } unmark_all(); } marker_t& marker_t::instance(int world_size_x, int world_size_y) { the_instance.init(world_size_x, world_size_y); return the_instance; } marker_t& marker_t::instance_second(int world_size_x, int world_size_y) { second_instance.init(world_size_x, world_size_y); return second_instance; } marker_t::~marker_t() { delete [] bits; } void marker_t::unmark_all() { if(bits) { MEMZERON(bits, bits_length); } more.clear(); } void marker_t::mark(const grund_t *gr) { if(gr != NULL) { if(gr->ist_karten_boden()) { // ground level const int bit = gr->get_pos().y*cached_size_x+gr->get_pos().x; bits[bit/bit_unit] |= 1 << (bit & bit_mask); } else { more.set(gr, true); } } } void marker_t::unmark(const grund_t *gr) { if(gr != NULL) { if(gr->ist_karten_boden()) { // ground level const int bit = gr->get_pos().y*cached_size_x+gr->get_pos().x; bits[bit/bit_unit] &= ~(1 << (bit & bit_mask)); } else { more.remove(gr); } } } bool marker_t::is_marked(const grund_t *gr) const { if(gr==NULL) { return false; } if(gr->ist_karten_boden()) { // ground level const int bit = gr->get_pos().y*cached_size_x+gr->get_pos().x; return (bits[bit/bit_unit] & (1 << (bit & bit_mask))) != 0; } else { return more.get(gr); } } bool marker_t::test_and_mark(const grund_t *gr) { if(gr != NULL) { if(gr->ist_karten_boden()) { // ground level const int bit = gr->get_pos().y*cached_size_x+gr->get_pos().x; if ((bits[bit/bit_unit] & (1 << (bit & bit_mask))) != 0) { return true; } bits[bit/bit_unit] |= 1 << (bit & bit_mask); } else { return more.set(gr, true); } } return false; } simutrans-124.5/src/simutrans/dataobj/marker.h000066400000000000000000000040621520675460500214400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_MARKER_H #define DATAOBJ_MARKER_H #include "../tpl/ptrhashtable_tpl.h" class grund_t; /** * Class to mark tiles as visited during route search. * Singleton. */ class marker_t { // added bit mask, because it allows a more efficient // implementation (use & instead of %) enum { bit_unit = (8 * sizeof(unsigned char)), bit_mask = (8 * sizeof(unsigned char))-1 }; /// bit-field to mark ground tiles unsigned char *bits; /// length of field int bits_length; /// bit-field is made for this x-size int cached_size_x; /// hashtable to mark non-ground tiles (bridges, tunnels) ptrhashtable_tpl more; marker_t() : bits(NULL) { init(0, 0); } ~marker_t(); /** * Initializes marker. Set all tiles to not marked. * @param world_size_x x-size of map * @param world_size_y y-size of map */ void init(int world_size_x, int world_size_y); /// the instance static marker_t the_instance; static marker_t second_instance; public: /** * Return handle to marker instance. * @param world_size_x x-size of map * @param world_size_y y-size of map * @returns handle to the singleton instance */ static marker_t& instance(int world_size_x, int world_size_y); /** * Return handle to marker instance. * @param world_size_x x-size of map * @param world_size_y y-size of map * @returns handle to the singleton instance */ static marker_t& instance_second(int world_size_x, int world_size_y); /** * Marks tile as visited. */ void mark(const grund_t *gr); /** * Unmarks tile as visited. */ void unmark(const grund_t *gr); /** * Checks if tile is visited. * @returns true if tile was already visited */ bool is_marked(const grund_t *gr) const; /** * Checks if tile is visited. Marks tile as visited if not visited before. * @returns true if tile was already visited */ bool test_and_mark(const grund_t *gr); /** * Marks all fields as not visited. */ void unmark_all(); }; #endif simutrans-124.5/src/simutrans/dataobj/objlist.cc000066400000000000000000001164401520675460500217670ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../simdebug.h" #include "../display/simgraph.h" #include "../world/simworld.h" #include "../builder/hausbauer.h" #include "../obj/dummy.h" #include "../obj/wolke.h" #include "../obj/zeiger.h" #include "../obj/crossing.h" #include "../obj/baum.h" #include "../obj/bruecke.h" #include "../obj/field.h" #include "../obj/pillar.h" #include "../obj/tunnel.h" #include "../obj/gebaeude.h" #include "../obj/signal.h" #include "../obj/label.h" #include "../obj/leitung2.h" #include "../obj/wayobj.h" #include "../obj/roadsign.h" #include "../obj/groundobj.h" #include "../simtypes.h" #include "../obj/depot.h" #include "../simmem.h" #include "../player/simplay.h" #include "../vehicle/air_vehicle.h" #include "../vehicle/simroadtraffic.h" #include "../vehicle/pedestrian.h" #include "../vehicle/movingobj.h" #include "../descriptor/building_desc.h" #include "../descriptor/groundobj_desc.h" #include "../dataobj/loadsave.h" #include "../dataobj/freelist.h" #include "../dataobj/environment.h" #include "objlist.h" // priority for inserting into objlist #define PRI_WAY (0) // ways (always at the top!) #define PRI_DEPOT (1) // depots (must be before tunnel!) #define PRI_CROSSING (1) // crossings, treated like bridges or tunnels #define PRI_GROUNDOBJ (1) #define PRI_BRIDGE (2) #define PRI_TUNNEL (2) #define PRI_BUILDING (3) #define PRI_FIELD (3) #define PRI_TRANSFORMER (4) #define PRI_RAUCHER (5) #define PRI_SIGNAL (6) #define PRI_ROADSIGN PRI_SIGNAL #define PRI_PILLAR (7) #define PRI_WAYOBJ (8) #define PRI_LABEL (9) #define PRI_TREE (50) #define PRI_MOVABLE (100) // priority of moving things: should be smaller than the priority of powerlines #define PRI_AIRCRAFT (PRI_MOVABLE + 1) #define PRI_MOVINGOBJ (PRI_MOVABLE + 1) #define PRI_POWERLINE (150) #define PRI_CLOUD (200) #define PRI_ZEIGER (254) #define PRI_INVALID (255) // Maps obj_t::type -> objlist insertion order // unused entries have PRI_INVALID static uint8 type_to_pri[256] = { PRI_INVALID, // obj PRI_TREE, // baum PRI_ZEIGER, // cursor/pointers PRI_CLOUD, // wolke PRI_CLOUD, // sync_wolke PRI_CLOUD, // async_wolke PRI_INVALID, // gebaeude_alt, unused PRI_BUILDING, // gebaeude PRI_SIGNAL, // signal PRI_BRIDGE, // bridge PRI_TUNNEL, // tunnel PRI_INVALID, // old_gebaeudefundament, unused PRI_DEPOT, // bahndepot PRI_DEPOT, // strassendepot PRI_DEPOT, // schiffdepot PRI_RAUCHER, // smoke generator (not used any more) PRI_POWERLINE, // poweline PRI_TRANSFORMER, // pumpe PRI_TRANSFORMER, // senke PRI_ROADSIGN, // roadsign PRI_PILLAR, // pillar PRI_DEPOT, // airdepot PRI_DEPOT, // monoraildepot PRI_DEPOT, // tramdepot PRI_DEPOT, // maglevdepot PRI_WAYOBJ, // way objects (electrification) PRI_WAY, // way PRI_LABEL, // label, indicates ownership: insert before trees PRI_FIELD, // field (factory extension) PRI_CROSSING, // crossing PRI_GROUNDOBJ, // groundobjs, overlays over bare ground like lakes etc. PRI_DEPOT, // narrowgaugedepot // 32-63 left empty (old numbers) PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_MOVABLE, // pedestrians PRI_MOVABLE, // city cars PRI_MOVABLE, // road vehicle PRI_MOVABLE, // rail vehicle PRI_MOVABLE, // monorail PRI_MOVABLE, // maglev PRI_MOVABLE, // narrowgauge PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_MOVABLE, // ship PRI_AIRCRAFT, // aircraft (no trailer, could be handled by normal method) PRI_MOVINGOBJ, // movingobject (no trailer, could be handled by normal method) // 83-95 left empty (for other moving stuff); 95 is reserved for old choosesignals PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, // 96-128 left empty (old numbers) PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, // 128-255 left empty PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID, PRI_INVALID }; static void dl_free(obj_t** p, uint8 size) { assert(size > 1); if (size <= 16) { freelist_t::putback_node(sizeof(*p) * size, p); } else { free(p); } } static obj_t** dl_alloc(uint8 size) { assert(size > 1); obj_t** p; if (size <= 16) { p = static_cast(freelist_t::gimme_node(sizeof(*p) * size )); } else { p = MALLOCN(obj_t*, size); } return p; } objlist_t::objlist_t() { obj.one = NULL; capacity = 0; top = 0; } objlist_t::~objlist_t() { if( capacity == 1 ) { obj.one->set_flag(obj_t::not_on_map); if(!obj.one->has_managed_lifecycle()) { delete obj.one; } } else { for( uint8 i=0; iset_flag(obj_t::not_on_map); if(!object->has_managed_lifecycle()) { delete object; } } } if(capacity>1) { dl_free(obj.some, capacity); } obj.some = NULL; capacity = top = 0; } void objlist_t::set_capacity(uint16 req_cap) { // DBG_MESSAGE("objlist_t::set_capacity()", "old cap=%d, new cap=%d", capacity, new_cap); const uint8 new_cap = req_cap >= 255 ? 254 : (uint8)req_cap; // a single object is stored differentially if(new_cap==0) { if(capacity>1) { dl_free( obj.some, capacity ); } obj.one = NULL; capacity = 0; top = 0; } else if(new_cap==1) { if(capacity>1) { obj_t *tmp=NULL; // we have an obj to save into the list tmp = obj.some[0]; dl_free( obj.some, capacity ); obj.one = tmp; assert(top<2); } else if(top==0) { obj.one = NULL; } capacity = top; } else if(capacity<=1) { // this means we extend from 0 or 1 elements to more than 1 obj_t *tmp=obj.one; obj.some = dl_alloc(new_cap); MEMZERON(obj.some, new_cap); obj.some[0] = tmp; capacity = new_cap; assert(top<=1); } else { // we need to copy old list to large list assert( top<=new_cap ); // get memory obj_t **tmp = dl_alloc(new_cap); // free old memory if(obj.some) { memcpy( tmp, obj.some, sizeof(obj_t *)*top ); dl_free(obj.some, capacity); } obj.some = tmp; capacity = new_cap; } } bool objlist_t::grow_capacity() { if(capacity==0) { return true; } else if(capacity==1) { set_capacity( 4 ); return true; } else if(capacity>=254) { // capacity exceeded ... (and no need for THAT many objects here ... ) return false; } else { // size exceeded, extent uint16 new_cap = (uint16)capacity+4; set_capacity( new_cap ); return true; } } void objlist_t::shrink_capacity(uint8 o_top) { // strategy: avoid freeing mem if not needed. Only if we hold lots of memory then free it. // this is almost only called when deleting ways in practice if( capacity > 16 && o_top <= 4 ) { set_capacity(o_top); } } inline void objlist_t::intern_insert_at(obj_t* new_obj, uint8 pri) { // we have more than one object here, thus we can use obj.some exclusively! for( uint8 i=top; i>pri; i-- ) { obj.some[i] = obj.some[i-1]; } obj.some[pri] = new_obj; top++; } // only used internal for loading. DO NOT USE OTHERWISE! bool objlist_t::append(obj_t *new_obj) { if(capacity==0) { // the first one save direct obj.one = new_obj; top = 1; capacity = 1; return true; } if(top>=capacity && !grow_capacity()) { // memory exceeded return false; } intern_insert_at(new_obj, top); return true; } // this will automatically give the right order for citycars and the like ... bool objlist_t::intern_add_moving(obj_t* new_obj) { // we are more than one object, thus we exclusively use obj.some here! // it would be nice, if also the objects are inserted according to their priorities as // vehicles types (number returned by get_typ()). However, this would increase // the calculation even further. :( // insert at this lane uint8 lane = ((vehicle_base_t*)new_obj)->get_disp_lane(); // find out about the first car etc. moving thing. // We can start to insert between (start) and (end) uint8 start=0; while(startis_moving() || ((vehicle_base_t*)obj.some[start])->get_disp_lane() < lane) ) { start ++; } uint8 end = top; while( end>start && (!obj.some[end-1]->is_moving() || ((vehicle_base_t*)obj.some[end-1])->get_disp_lane() > lane) ) { end--; } /* Vehicles are sorted back to front by their disp_lane. Here we append them in tile entry order within their disp_lane, last one displayed in front. Those moving downwards on screen (direction NE, E, SE, S) must be prepended, first one in front. */ auto v = static_cast(new_obj); const uint8 direction = v->get_direction(); if (direction == ribi_t::north || (direction & ribi_t::west)) { start = end; // append } intern_insert_at(new_obj, start); return true; } /** * @returns true if tree1 must be sorted before tree2 (tree1 stands behind tree2) */ bool compare_trees(const obj_t *tree1, const obj_t *tree2) { // the tree with larger yoff is in front sint8 diff = tree2->get_yoff() - tree1->get_yoff(); if (diff==0) { // .. or the one that is the left most (ie xoff is small) diff = tree1->get_xoff() - tree2->get_xoff(); } return diff>0; } void objlist_t::sort_trees(uint8 index, uint8 count) { if(top>=index+count) { std::sort(&obj.some[index], &obj.some[index+count], compare_trees); } } bool objlist_t::add(obj_t* new_obj) { if(capacity==0) { // the first one save direct obj.one = new_obj; top = 1; capacity = 1; return true; } if(top>=capacity && !grow_capacity()) { // memory exceeded return false; } if(top==0) { intern_insert_at( new_obj, 0 ); return true; } // now top>0 // now insert it a the correct place const uint8 pri=type_to_pri[new_obj->get_typ()]; // vehicles need a special order if(pri==PRI_MOVABLE) { return intern_add_moving(new_obj); } // roads should be first and system_type==type_tram last! if(pri==0) { // check for other ways to keep order! (maximum is two ways per tile at the moment) weg_t const* const w = obj_cast(obj.some[0]); if (!w) { // first way intern_insert_at(new_obj, 0); return true; } bool w_is_tram = w->get_desc()->get_styp() == type_tram; bool new_is_tram = static_cast(new_obj)->get_desc()->get_styp() == type_tram; if(new_is_tram && !w_is_tram) { intern_insert_at(new_obj, 1); } else if(!new_is_tram && w_is_tram) { intern_insert_at(new_obj, 0); } else { uint8 const pos = w && w->get_waytype() < static_cast(new_obj)->get_waytype() ? 1 : 0; intern_insert_at(new_obj, pos); } return true; } uint8 i; for( i=0; itype_to_pri[obj.some[i]->get_typ()]; i++ ) ; // now i contains the position, where we either insert of just add ... if(i==top) { obj.some[top] = new_obj; top++; } else { if(pri==PRI_TREE) { /* trees are a little tricky, since they cast a shadow * therefore the y-order must be correct! */ for( ; i(obj.some[i]); if (!tree || compare_trees(new_obj, tree)) { break; } } } else if (pri == PRI_PILLAR) { // pillars have to be sorted wrt their y-offset, too. for( ; i(obj.some[i]); if (!pillar || new_obj->get_yoff() > pillar->get_yoff() ) { break; } } } else if( pri == PRI_WAYOBJ && obj.some[i]->get_typ()==obj_t::wayobj ) { wayobj_t const* const wo = obj_cast(obj.some[i]); if( wo && wo->get_waytype() < obj_cast(new_obj)->get_waytype() ) { // insert after a lower waytype i += 1; } } intern_insert_at(new_obj, i); } // then correct the upper border return true; } // take the thing out from the list // use this only for temporary removing // since it does not shrink list or checks for ownership obj_t *objlist_t::remove_last() { obj_t *last_obj=NULL; if(capacity==0) { // nothing } else if(capacity==1) { last_obj = obj.one; obj.one = NULL; capacity = top = 0; } else { if(top>0) { top --; last_obj = obj.some[top]; obj.some[top] = NULL; } } return last_obj; } bool objlist_t::remove(const obj_t* remove_obj) { if( capacity == 0 ) { return false; } else if( capacity == 1 ) { if( obj.one == remove_obj ) { obj.one = NULL; capacity = 0; top = 0; return true; } return false; } // we keep the array dense! for( uint8 i=0; i(remove_obj); if (v && remove_obj->get_typ() != obj_t::pedestrian && remove_obj->get_typ() != obj_t::road_user && remove_obj->get_typ() != obj_t::movingobj) { v->leave_tile(); } else { remove_obj->cleanup(player); remove_obj->set_flag(obj_t::not_on_map); // all objects except zeiger (pointer) are destroyed here // zeiger's will be deleted if their associated tool terminates if (!remove_obj->has_managed_lifecycle()) { delete remove_obj; } } } bool objlist_t::loesche_alle(player_t *player, uint8 offset) { if(top<=offset) { return false; } // something to delete? bool ok=false; if(capacity>1) { while( top>offset ) { top --; local_delete_object(obj.some[top], player); obj.some[top] = NULL; ok = true; } } else { if(capacity==1) { local_delete_object(obj.one, player); ok = true; obj.one = NULL; capacity = top = 0; } } shrink_capacity(top); return ok; } /* Rotates the display order of moving objects 90 deg CW. The objects themselves are not rotated, that is expected to happen afterwards. */ void objlist_t::rotate90_moving() { if (capacity < 2) return; // has no obj.some uint begin, end; for (begin = 0; begin < top; ++begin) { if (obj.some[begin]->is_moving()) break; } for (end = top; end > begin; --end) { if (obj.some[end - 1]->is_moving()) break; } if (end - begin < 2) return; obj_t **old_some = obj.some; obj_t* temp_some[254]; obj.some = temp_some; uint old_top = top; for (top = 0; top < begin; ++top) obj.some[top] = old_some[top]; /* intern_add_moving() appends vehicles in the order they enter a tile. To sort them back to front it reverses, i.e. prepends those moving downwards on screen (direction NE, E, SE, S). Additionally it sorts vehicles on different road lanes by their disp_lane, numbers which ascend back to front. We must maintain the original entry order, reverse vehicles which were prepended, and insert based on the rotated disp_lane and direction. Lane numbers are mirrored when a vehicle moves to the left side of the screen, so disp_lane will be mirrored if the current direction is NE, E, SW, or W. The order is lost if vehicles entered the same lane in different directions, that may cause a visual glitch but shouldn't really happen. */ static const uint append_dirs = ( (1 << ribi_t::southwest) | (1 << ribi_t::west) | (1 << ribi_t::northwest) | (1 << ribi_t::north)); static const uint rotated_append_dirs = ( (1 << ribi_t::southeast) | (1 << ribi_t::south) | (1 << ribi_t::southwest) | (1 << ribi_t::west)); static const uint rotated_mirror_dirs = ( (1 << ribi_t::northeast) | (1 << ribi_t::east) | (1 << ribi_t::southwest) | (1 << ribi_t::west)); auto rotated_disp_lane = [] (uint& dir_mask, const obj_t* o) { auto v = static_cast(o); dir_mask = 1 << v->get_direction(); uint lane = v->get_disp_lane(); return (rotated_mirror_dirs & dir_mask) ? 4 - lane : lane; }; auto insert = [&] (obj_t *o) { uint dir_mask, dm; uint lane = rotated_disp_lane(dir_mask, o); uint i; // Insert in the same disp_lane, first position. for (i = begin; i < top; ++i) { if (rotated_disp_lane(dm, obj.some[i]) >= lane) break; } // Append if the vehicle will move upwards on screen after // rotation, same as in intern_add_moving(). if (rotated_append_dirs & dir_mask) { for (; i < top; ++i) { if (rotated_disp_lane(dm, obj.some[i]) > lane) break; } } intern_insert_at(o, i); }; const vehicle_base_t* v; auto disp_lane = [&v] (const obj_t* o) { v = static_cast(o); return v->get_disp_lane(); }; uint prev_lane = disp_lane(old_some[begin]); for (uint i = begin; i < end;) { uint prepended_begin = i; uint lane; // Find the first appended vehicle. for (; i < end; ++i) { lane = disp_lane(old_some[i]); if (lane != prev_lane) break; if (append_dirs & (1 << v->get_direction())) break; } for (uint j = i; j > prepended_begin; --j) { insert(old_some[j - 1]); } for (; i < end; ++i) { lane = disp_lane(old_some[i]); if (lane != prev_lane) break; insert(old_some[i]); } prev_lane = lane; } while (top < old_top) obj.some[top++] = old_some[end++]; memcpy(old_some, obj.some, sizeof(void*) * top); obj.some = old_some; } /* returns the text of an error message, if obj could not be removed */ const char *objlist_t::kann_alle_entfernen(const player_t *player, uint8 offset) const { if(top<=offset) { return NULL; } if(capacity==1) { return obj.one->get_removal_error(player); } else { const char * msg = NULL; for(uint8 i=offset; iget_removal_error(player); if(msg != NULL) { return msg; } } } return NULL; } /* recalculates all images */ void objlist_t::calc_image() { if(capacity==0) { // nothing } else if(capacity==1) { obj.one->calc_image(); } else { for(uint8 i=0; icalc_image(); } } } void objlist_t::set_all_dirty() { if( capacity == 0 ) { // nothing } else if( capacity == 1 ) { obj.one->set_flag( obj_t::dirty ); } else { for( uint8 i = 0; i < top; i++ ) { obj.some[i]->set_flag( obj_t::dirty ); } } } /* check for obj */ bool objlist_t::ist_da(const obj_t* test_obj) const { if(capacity<=1) { return obj.one==test_obj; } else { for(uint8 i=0; i= top ) { // start==0 and top==0 is already covered by this too return NULL; } if( capacity <= 1 ) { // it will crash on capacity==1 and top==0, but this should never happen! // this is only reached for top==1 and start==0 return obj.one->get_typ()!=typ ? NULL : obj.one; } else { // else we have to search the list for(uint8 i=start; iget_typ()==typ) { return tmp; } } } return NULL; } obj_t *objlist_t::get_leitung() const { if( top == 0 ) { return NULL; } if( capacity <= 1 ) { // it will crash on capacity==1 and top==0, but this should never happen! if( obj.one->get_typ() >= obj_t::leitung && obj.one->get_typ() <= obj_t::senke ) { return obj.one; } } else { // else we have to search the list for( uint8 i=0; iget_typ(); if( typ >= obj_t::leitung && typ <= obj_t::senke ) { return obj.some[i]; } } } return NULL; } obj_t *objlist_t::get_convoi_vehicle() const { if( top == 0 ) { return NULL; } if( capacity <= 1 ) { // it will crash on capacity==1 and top==0, but this should never happen! // only ships and aircraft can go on tiles without ways => only test for those uint8 t = obj.one->get_typ(); if( t == obj_t::air_vehicle || t == obj_t::water_vehicle ) { return obj.one; } } else { for( uint8 i=0; i < top; i++ ) { uint8 typ = obj.some[i]->get_typ(); if( typ >= obj_t::road_vehicle && typ <= obj_t::air_vehicle ) { return obj.some[i]; } } } return NULL; } void objlist_t::rdwr(loadsave_t *file, koord3d current_pos) { if(file->is_loading()) { sint32 max_object_index; if( file->is_version_less(110, 1) ) { file->rdwr_long(max_object_index); if(max_object_index>254) { dbg->error("objlist_t::laden()","Too many objects (%i) at (%i,%i), some vehicle may not appear immediately.",max_object_index,current_pos.x,current_pos.y); } } else { uint8 obj_count; file->rdwr_byte(obj_count); max_object_index = -1+(sint32)obj_count; } for(sint32 i=0; i<=max_object_index; i++) { obj_t::typ typ = (obj_t::typ)file->rd_obj_id(); // DBG_DEBUG("objlist_t::laden()", "Thing type %d", typ); if(typ == -1) { continue; } obj_t *new_obj = NULL; switch(typ) { case obj_t::bruecke: new_obj = new bruecke_t(file); break; case obj_t::tunnel: new_obj = new tunnel_t(file); break; case obj_t::pumpe: new_obj = new pumpe_t(file); break; case obj_t::leitung: new_obj = new leitung_t(file); break; case obj_t::senke: new_obj = new senke_t(file); break; case obj_t::zeiger: new_obj = new zeiger_t(file); break; case obj_t::signal: new_obj = new signal_t(file); break; case obj_t::label: new_obj = new label_t(file); break; case obj_t::crossing: new_obj = new crossing_t(file); break; case obj_t::wayobj: { wayobj_t* const wo = new wayobj_t(file); if (wo->get_desc() == NULL) { // ignore missing wayobjs wo->set_flag(obj_t::not_on_map); delete wo; new_obj = NULL; } else { new_obj = wo; } break; } // some old offsets will be converted to new ones case obj_t::old_fussgaenger: typ = obj_t::pedestrian; /* FALLTHROUGH */ case obj_t::pedestrian: { pedestrian_t* const pedestrian = new pedestrian_t(file); if (pedestrian->get_desc() == NULL) { // no pedestrians ... delete this pedestrian->set_flag(obj_t::not_on_map); delete pedestrian; new_obj = NULL; } else { new_obj = pedestrian; } break; } case obj_t::old_verkehr: typ = obj_t::road_user; /* FALLTHROUGH */ case obj_t::road_user: { private_car_t* const car = new private_car_t(file); if (car->get_desc() == NULL) { // no citycars ... delete this car->set_flag(obj_t::not_on_map); delete car; } else { new_obj = car; } break; } case obj_t::old_monoraildepot: typ = obj_t::monoraildepot; /* FALLTHROUGH */ case obj_t::monoraildepot: new_obj = new monoraildepot_t(file); break; case obj_t::old_tramdepot: typ = obj_t::tramdepot; /* FALLTHROUGH */ case obj_t::tramdepot: new_obj = new tramdepot_t(file); break; case obj_t::strassendepot: new_obj = new strassendepot_t(file); break; case obj_t::schiffdepot: new_obj = new schiffdepot_t(file); break; case obj_t::old_airdepot: typ = obj_t::airdepot; /* FALLTHROUGH */ case obj_t::airdepot: new_obj = new airdepot_t(file); break; case obj_t::maglevdepot: new_obj = new maglevdepot_t(file); break; case obj_t::narrowgaugedepot: new_obj = new narrowgaugedepot_t(file); break; case obj_t::bahndepot: { // for compatibility reasons we may have to convert them to tram and monorail depots bahndepot_t* bd; gebaeude_t gb(file); building_tile_desc_t const* const tile = gb.get_tile(); if( tile ) { switch (tile->get_desc()->get_extra()) { case monorail_wt: bd = new monoraildepot_t( gb.get_pos(), gb.get_owner(), tile); break; case tram_wt: bd = new tramdepot_t( gb.get_pos(), gb.get_owner(), tile); break; default: bd = new bahndepot_t( gb.get_pos(), gb.get_owner(), tile); break; } } else { bd = new bahndepot_t( gb.get_pos(), gb.get_owner(), NULL ); } bd->rdwr_vehicles(file); new_obj = bd; typ = new_obj->get_typ(); // do not remove from this position, since there will be nothing gb.set_flag(obj_t::not_on_map); } break; // check for pillars case obj_t::old_pillar: typ = obj_t::pillar; /* FALLTHROUGH */ case obj_t::pillar: { pillar_t *p = new pillar_t(file); if(p->get_desc()!=NULL && p->get_desc()->get_pillar()!=0) { new_obj = p; } else { // has no pillar ... // do not remove from this position, since there will be nothing p->set_flag(obj_t::not_on_map); delete p; } } break; case obj_t::baum: { baum_t *b = new baum_t(file); if( !b->get_desc() ) { // is there a replacement possible if( const tree_desc_t *desc = tree_builder_t::random_tree_for_climate( world()->get_climate_at_height(current_pos.z) ) ) { b->set_desc( desc ); } else { // do not remove from map on this position, since there will be nothing b->set_flag(obj_t::not_on_map); delete b; b = NULL; } } else { new_obj = b; } } break; case obj_t::groundobj: { groundobj_t* const groundobj = new groundobj_t(file); if(groundobj->get_desc() == NULL) { // do not remove from this position, since there will be nothing groundobj->set_flag(obj_t::not_on_map); delete groundobj; } else { new_obj = groundobj; } break; } case obj_t::movingobj: { movingobj_t* const movingobj = new movingobj_t(file); if (movingobj->get_desc() == NULL) { // no citycars ... delete this movingobj->set_flag(obj_t::not_on_map); delete movingobj; } else { new_obj = movingobj; } break; } case obj_t::gebaeude: { gebaeude_t *gb = new gebaeude_t(file); if(gb->get_tile()==NULL) { // do not remove from this position, since there will be nothing gb->set_flag(obj_t::not_on_map); delete gb; gb = NULL; } else { new_obj = gb; } } break; case obj_t::old_roadsign: typ = obj_t::roadsign; /* FALLTHROUGH */ case obj_t::roadsign: { roadsign_t *rs = new roadsign_t(file); if(rs->get_desc()==NULL) { // roadsign_t without description => ignore rs->set_flag(obj_t::not_on_map); delete rs; } else { new_obj = rs; } } break; // will be ignored, was only used before 86.09 case obj_t::old_gebaeudefundament: { dummy_obj_t d(file); break; } // only factories can smoke; but then, the smoker is reinstated after loading case obj_t::old_raucher: { raucher_t r(file); break; } // wolke is not saved any more case obj_t::cloud: { wolke_t w(file); break; } case obj_t::old_async_wolke: { async_wolke_t w(file); break; } default: dbg->fatal("objlist_t::laden()", "During loading: Unknown object type '%d'", typ); } if(new_obj && new_obj->get_typ()!=typ) { dbg->warning( "objlist_t::rdwr()","typ error : %i instead %i on %i,%i, object ignored!", new_obj->get_typ(), typ, new_obj->get_pos().x, new_obj->get_pos().y ); new_obj = NULL; } if(new_obj && new_obj->get_pos()==koord3d::invalid) { new_obj->set_pos( current_pos ); } if(new_obj && new_obj->get_pos()!=current_pos) { dbg->warning("objlist_t::rdwr()","position error: %i,%i,%i instead %i,%i,%i (object will be ignored)",new_obj->get_pos().x,new_obj->get_pos().y,new_obj->get_pos().z,current_pos.x,current_pos.y,current_pos.z); new_obj = NULL; } if(new_obj) { append(new_obj); } } } else { /* here is the saving part ... * first: construct a list of stuff really needed to save */ obj_t *save[256]; sint32 max_object_index = 0; for( uint16 i=0; iget_typ()==obj_t::way // do not save smoke || new_obj->get_typ()==obj_t::cloud // fields will be built by factory || new_obj->get_typ()==obj_t::field // do not save factory buildings => factory will reconstruct them || (new_obj->get_typ()==obj_t::gebaeude && ((gebaeude_t *)new_obj)->get_fabrik()) // things with convoi will not be saved || (new_obj->get_typ()>=66 && new_obj->get_typ()<82) || (new_obj->get_typ()==obj_t::baum && file->is_version_atleast(110, 1) && (env_t::server || file->is_version_atleast(122, 2))) // trees are saved from boden_t ) { // these objects are simply not saved } else { save[max_object_index++] = new_obj; } } // now we know the number of stuff to save max_object_index --; if( file->is_version_less(110, 1) ) { file->rdwr_long( max_object_index ); } else { uint8 obj_count = max_object_index+1; file->rdwr_byte( obj_count ); } for(sint32 i=0; i<=max_object_index; i++) { obj_t *new_obj = save[i]; if(new_obj->get_pos()==current_pos) { file->wr_obj_id(new_obj->get_typ()); new_obj->rdwr(file); } else if (new_obj->get_pos().get_2d() == current_pos.get_2d()) { // ok, just error in z direction => we will correct it dbg->warning( "objlist_t::rdwr()","position error: z pos corrected on %i,%i from %i to %i", new_obj->get_pos().x, new_obj->get_pos().y, new_obj->get_pos().z, current_pos.z); file->wr_obj_id(new_obj->get_typ()); new_obj->set_pos(current_pos); new_obj->rdwr(file); } else { dbg->error("objlist_t::rdwr()","unresolvable position error: %i,%i instead %i,%i (object type %i will be not saved!)", new_obj->get_pos().x, new_obj->get_pos().y, current_pos.x, current_pos.y, new_obj->get_typ()); file->wr_obj_id(-1); } } } } /** display all things, faster, but will lead to clipping errors */ #ifdef MULTI_THREAD void objlist_t::display_obj_quick_and_dirty( const sint16 xpos, const sint16 ypos, const uint8 start_offset, const sint8 clip_num ) const #else void objlist_t::display_obj_quick_and_dirty( const sint16 xpos, const sint16 ypos, const uint8 start_offset, const bool is_global ) const #endif { if(capacity==0) { return; } else if(capacity==1) { if(start_offset==0) { // only draw background on request obj.one->display( xpos, ypos CLIP_NUM_PAR); } // foreground need to be drawn in any case #ifdef MULTI_THREAD obj.one->display_after(xpos, ypos, clip_num ); #else obj.one->display_after( xpos, ypos, is_global ); if( is_global ) { obj.one->clear_flag( obj_t::dirty ); } #endif return; } for( uint8 n = start_offset; n < top; n++ ) { // is there an object ? obj.some[n]->display( xpos, ypos CLIP_NUM_PAR); } // foreground (needs to be done backwards! for( size_t n = top; n-- != 0; ) { #ifdef MULTI_THREAD obj.some[n]->display_after( xpos, ypos, clip_num ); #else obj.some[n]->display_after( xpos, ypos, is_global ); if( is_global ) { obj.some[n]->clear_flag( obj_t::dirty ); } #endif } } /** * Routine to display background images of non-moving things * powerlines have to be drawn after vehicles (and thus are in the obj-array inserted after vehicles) * @return the index of the first moving thing (or powerline) * * objlist_t::display_obj_bg() .. called by the methods in grund_t * local_display_obj_bg() .. local function to avoid code duplication, returns false if the first non-valid obj is reached */ inline bool local_display_obj_bg(const obj_t *obj, const sint16 xpos, const sint16 ypos CLIP_NUM_DEF) { const bool display_obj = !obj->is_moving(); if( display_obj ) { obj->display( xpos, ypos CLIP_NUM_PAR); } return display_obj; } uint8 objlist_t::display_obj_bg( const sint16 xpos, const sint16 ypos, const uint8 start_offset CLIP_NUM_DEF) const { if( start_offset >= top ) { return start_offset; } if( capacity == 1 ) { return local_display_obj_bg( obj.one, xpos, ypos CLIP_NUM_PAR); } for( uint8 n = start_offset; n < top; n++ ) { if( !local_display_obj_bg( obj.some[n], xpos, ypos CLIP_NUM_PAR) ) { return n; } } return top; } /** * Routine to draw vehicles * .. vehicles are draws if driving in direction ribi (with special treatment of flying aircrafts) * .. clips vehicle only along relevant edges (depends on ribi and vehicle direction) * * @param draw_obj * @param xpos * @param ypos * @param ribi * @ param ontile if true then vehicles are on the tile that defines the clipping * @param CLIP_NUM_DEF * @return the index of the first non-moving thing * * objlist_t::display_obj_vh() .. called by the methods in grund_t * local_display_obj_vh() .. local function to avoid code duplication, returns false if the first non-valid obj is reached */ inline bool local_display_obj_vh(const obj_t *draw_obj, const sint16 xpos, const sint16 ypos, const ribi_t::ribi ribi, const bool ontile CLIP_NUM_DEF) { vehicle_base_t const* const v = obj_cast(draw_obj); air_vehicle_t const* a; if( v && (ontile || !(a = obj_cast(v)) || a->is_on_ground()) ) { const ribi_t::ribi veh_ribi = v->get_direction(); if( ontile || (veh_ribi & ribi) == ribi || (ribi_t::backward(veh_ribi) & ribi )== ribi || draw_obj->get_typ() == obj_t::air_vehicle ) { // activate clipping only for our direction masked by the ribi argument // use non-convex clipping (16) only if we are on the currently drawn tile or its n/w neighbours gfx->activate_ribi_clip( ((veh_ribi|ribi_t::backward(veh_ribi))&ribi) | (ontile || ribi == ribi_t::north || ribi == ribi_t::west ? 16 : 0) CLIP_NUM_PAR); draw_obj->display( xpos, ypos CLIP_NUM_PAR); } return true; } else { // if !ontile starting_offset is not correct, hence continue searching till the end return !ontile; } } uint8 objlist_t::display_obj_vh( const sint16 xpos, const sint16 ypos, const uint8 start_offset, const ribi_t::ribi ribi, const bool ontile CLIP_NUM_DEF) const { if( start_offset >= top ) { return start_offset; } if( capacity <= 1 ) { uint8 i = local_display_obj_vh( obj.one, xpos, ypos, ribi, ontile CLIP_NUM_PAR); gfx->activate_ribi_clip( ribi_t::all CLIP_NUM_PAR); return i; } uint8 nr_v = start_offset; for( uint8 n = start_offset; n < top; n++ ) { if( local_display_obj_vh( obj.some[n], xpos, ypos, ribi, ontile CLIP_NUM_PAR) ) { nr_v = n; } else { break; } } gfx->activate_ribi_clip( ribi_t::all CLIP_NUM_PAR); return nr_v+1; } /** * Routine to draw foreground images of everything on the tile (no clipping) and powerlines * * @param xpos * @param ypos * @param start_offset .. draws also background images of all objects with index>=start_offset * @param is_global will be only true for the main display; all miniworld windows should still reset main window */ #ifdef MULTI_THREAD void objlist_t::display_obj_fg( const sint16 xpos, const sint16 ypos, const uint8 start_offset, const sint8 clip_num ) const #else void objlist_t::display_obj_fg( const sint16 xpos, const sint16 ypos, const uint8 start_offset, const bool is_global ) const #endif { if( top == 0 ) { // nothing => finish return; } // now draw start_offset background and all foreground! if( capacity == 1 ) { if( start_offset == 0 ) { obj.one->display( xpos, ypos CLIP_NUM_PAR); } #ifdef MULTI_THREAD obj.one->display_after( xpos, ypos, clip_num ); #else obj.one->display_after( xpos, ypos, is_global ); if( is_global ) { obj.one->clear_flag(obj_t::dirty); } #endif return; } for( uint8 n = start_offset; n < top; n++ ) { obj.some[n]->display( xpos, ypos CLIP_NUM_PAR); } // foreground (needs to be done backwards!) for( size_t n = top; n-- != 0; ) { #ifdef MULTI_THREAD obj.some[n]->display_after( xpos, ypos, clip_num ); #else obj.some[n]->display_after( xpos, ypos, is_global ); if( is_global ) { obj.some[n]->clear_flag( obj_t::dirty ); } #endif } return; } #ifdef MULTI_THREAD void objlist_t::display_obj_overlay(const sint16 xpos, const sint16 ypos) const { if( top == 0 ) { return; // nothing => finish } if( capacity == 1 ) { obj.one->display_overlay( xpos, ypos ); obj.one->clear_flag( obj_t::dirty ); } else { for( size_t n = top; n-- != 0; ) { obj.some[n]->display_overlay( xpos, ypos ); obj.some[n]->clear_flag( obj_t::dirty ); } } } #endif void objlist_t::check_season(const bool calc_only_season_change) { if( 0 == top ) { return; } if( capacity <= 1 ) { // lets check here for consistency if( top != capacity ) { dbg->fatal( "objlist_t::check_season()", "top not matching!" ); } obj_t *check_obj = obj.one; if( !check_obj->check_season( calc_only_season_change ) ) { delete check_obj; } } else { // copy object pointers to check them vector_tpl list; for( uint8 i = 0; i < top; i++ ) { list.append(obj.some[i]); } // now work on the copied list // check_season may change this list (by planting new trees) for( uint8 i = 0, end = list.get_count(); i < end; i++ ) { obj_t *check_obj = list[i]; if( !check_obj->check_season( calc_only_season_change ) ) { delete check_obj; } } } } simutrans-124.5/src/simutrans/dataobj/objlist.h000066400000000000000000000075501520675460500216320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_OBJLIST_H #define DATAOBJ_OBJLIST_H #include "../simtypes.h" #include "../obj/simobj.h" /** * All things including ways are stored in this structure. * The entries are packed, i.e. the first free entry is at the top. * To save memory, a single element (like in the case for houses or a single tree) * is stored directly (obj.one) and capacity==1. Otherwise obj.some points to an * array. * The objects are sorted according to their drawing order. * ways are always first. */ class objlist_t { private: union { obj_t **some; // valid if capacity > 1 obj_t *one; // valid if capacity == 1 (and NULL if top or capacity==0!) } obj; /** * Number of items which can be stored without expanding * zero indicates empty list */ uint8 capacity; /** * 0-based index of the next free entry after the last element * therefore also the count of number of items which are stored */ uint8 top; void set_capacity(uint16 new_cap); bool grow_capacity(); void shrink_capacity(uint8 last_index); inline void intern_insert_at(obj_t* new_obj, uint8 pri); // only used internal for loading. DO NOT USE OTHERWISE! Use add instead! bool append(obj_t *obj); // this will automatically give the right order for citycars and the like ... bool intern_add_moving(obj_t* new_obj); objlist_t(objlist_t const&); objlist_t& operator=(objlist_t const&); public: objlist_t(); ~objlist_t(); void rdwr(loadsave_t *file,koord3d current_pos); obj_t * suche(obj_t::typ typ,uint8 start) const; // since this is often needed, it is defined here obj_t * get_leitung() const; obj_t * get_convoi_vehicle() const; /** * @param n thing index (unsigned value!) * @return thing at index n or NULL if n is out of bounds */ inline obj_t * bei(uint8 n) const { if( n >= top ) { return NULL; } return (capacity<=1) ? obj.one : obj.some[n]; } // usually used only for copying by grund_t obj_t *remove_last(); /// This routine will automatically obey the correct order of things during insertion. bool add(obj_t *obj); bool remove(const obj_t* obj); bool loesche_alle(player_t *player,uint8 offset); bool ist_da(const obj_t* obj) const; void rotate90_moving(); inline uint8 get_top() const {return top;} /** * sorts the trees according to their offsets */ void sort_trees(uint8 index, uint8 count); /** * @return NULL when OK, or message, why not? */ const char *kann_alle_entfernen(const player_t *player, uint8 offset) const; /** recalcs all objects on this tile */ void calc_image(); /** * Sets all objects dirty to prevent artifacts with smart hide cursor */ void set_all_dirty(); /** * Called whenever the season or snowline height changes */ void check_season(const bool calc_only_season_change); /** display all things, faster, but will lead to clipping errors */ #ifdef MULTI_THREAD void display_obj_quick_and_dirty( const sint16 xpos, const sint16 ypos, const uint8 start_offset, const sint8 clip_num ) const; #else void display_obj_quick_and_dirty( const sint16 xpos, const sint16 ypos, const uint8 start_offset, const bool is_global ) const; #endif /** * display all things, called by the routines in grund_t */ uint8 display_obj_bg(const sint16 xpos, const sint16 ypos, const uint8 start_offset CLIP_NUM_DEF) const; uint8 display_obj_vh(const sint16 xpos, const sint16 ypos, const uint8 start_offset, const ribi_t::ribi ribi, const bool ontile CLIP_NUM_DEF) const; #ifdef MULTI_THREAD void display_obj_fg(const sint16 xpos, const sint16 ypos, const uint8 start_offset, const sint8 clip_num ) const; void display_obj_overlay(const sint16 xpos, const sint16 ypos) const; #else void display_obj_fg(const sint16 xpos, const sint16 ypos, const uint8 start_offset, const bool is_global ) const; #endif } GCC_PACKED; #endif simutrans-124.5/src/simutrans/dataobj/pakset_downloader.cc000066400000000000000000000274431520675460500240320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include "pakset_downloader.h" #include "environment.h" #include "../sys/simsys.h" #include "../simloadingscreen.h" #ifdef _WIN32 #ifdef USE_URLMON // needs to add urlmon.lib to the linker #include #endif #endif #define USE_OWN_PAKINSTALL #ifdef __ANDROID__ // since the script does only work on few devices #define USE_OWN_PAKINSTALL extern const char* download_file(const char* url, const char* filename); #endif // check if pakset is installed, and if yes, check version string bool pak_need_update(const paksetinfo_t *pi) { cbuffer_t outside; outside.append(env_t::install_dir); outside.append(pi->name); outside.append(PATH_SEPARATOR); outside.append("ground.Outside.pak"); if( FILE* f = dr_fopen(outside.get_str(), "r") ) { fseek(f, 99, SEEK_SET); uint8 len = fgetc(f); fgetc(f); char versionstring[256]; fgets(versionstring, len, f); fclose(f); versionstring[len] = 0; // needs update, if string mismatch return strcmp(versionstring, pi->version); } return false; } // check, if we can handle this pak with the current configuration bool pak_can_download(const paksetinfo_t *pi) { #ifndef USE_OWN_PAKINSTALL // check script install #ifdef _WIN32 if (strstr(pi->url, ".zip")) { return true; } #else #ifndef __ANDROID__ return true; #endif // install with scripts does not work in Android return false; #endif #else // own routines require zip if (strstr(pi->url, ".zip")) { return true; } #endif return false; } #ifndef USE_OWN_PAKINSTALL // download and install pak into current path: skript method bool pak_download(vector_tplpaks) { // now install dr_mkdir( env_t::install_dir ); dr_chdir( env_t::install_dir ); loadingscreen_t ls("Install paks", paks.get_count(), true, false); int j = 0; for(paksetinfo_t *pi : paks) { cbuffer_t param; ls.set_info(ps->name); #ifdef _WIN32 param.append("powershell -ExecutionPolicy ByPass -NoExit "); param.append(env_t::install_dir); param.append("get_pak.ps1 \"" ); #else #ifndef __ANDROID__ param.append( env_t::install_dir); param.append("get_pak.sh \""); #else param.append("sh "); param.append(env_t::install_dir); param.append("get_pak.sh \""); #endif #endif param.append(ps->url); param.append("\""); const int retval = system( param ); DBG_DEBUG("pakinstaller_t::action_triggered", "Command '%s' returned %d", param.get_str(), retval); ls.set_progress(++j); } return true; } #else #include "../utils/simstring.h" #include "../network/network_file_transfer.h" #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wundef" # pragma GCC diagnostic ignored "-Wcast-qual" # ifndef __clang__ # pragma GCC diagnostic ignored "-Wcalloc-transposed-args" # endif #endif #define STDC #define voidpc voidpczip #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES extern "C" { #include "../../external/zip.c" }; #ifdef __GNUC__ # pragma GCC diagnostic pop #endif static void extract_pak_from_zip(const char* zipfile) { struct zip_t* zip = zip_open(zipfile, 0, 'r'); const int n = zip_entries_total(zip); bool has_simutrans_folder = false; loadingscreen_t ls("Extract files", n, true, false); for (int i = 0; i < n && i < 10 && !has_simutrans_folder; ++i) { zip_entry_openbyindex(zip, i); { int isdir = zip_entry_isdir(zip); if (isdir) { has_simutrans_folder = STRNICMP("simutrans", zip_entry_name(zip), 9) == 0; } } zip_entry_close(zip); } // now extract for (int i = 0; i < n; i++) { zip_entry_openbyindex(zip, i); { int isdir = zip_entry_isdir(zip); const char* name = zip_entry_name(zip) + (has_simutrans_folder ? 10 : 0); if (isdir) { // create directory (may fail if exists ... dr_mkdir(name); } else { // or extract file DBG_DEBUG("Install pak file", "%s", name); zip_entry_fread(zip, name); } } zip_entry_close(zip); ls.set_progress(i); } zip_close(zip); } // download and install pak into install path: using own zip/http routines bool pak_download(vector_tplpaks) { bool all_good=true; dr_mkdir(env_t::install_dir); dr_chdir(env_t::install_dir); loadingscreen_t ls("Install paks", paks.get_count() * 2, true, false); char outfilename[PATH_MAX]; int j = 0; for (paksetinfo_t* pi : paks) { ls.set_info(pi->name); if (strncmp(pi->url, "https://", 6) == 0) { #ifdef _WIN32 #ifndef USE_URLMON sprintf(outfilename, "powershell \"(New-Object System.Net.WebClient).DownloadFile('%s', 'temp.zip')\"", pi->url); system(outfilename); #else // use if (URLDownloadToFile(NULL, pi->url, "temp.zip", 0, NULL) != 0) { dbg->warning("pak_download()", "Pakset download failed"); j += 2; ls.set_progress(j); all_good = false; continue; } strcpy(outfilename, "temp.zip"); #endif #else #ifndef __ANDROID__ sprintf(outfilename, "curl --progress-bar -L '%s' > 'temp.zip'", pi->url); system(outfilename); strcpy(outfilename, "temp.zip"); #else // using system to download strcpy(outfilename, "temp.zip"); if (const char* err = download_file(pi->url, "temp.zip")) { outfilename[0] = 0; dbg->warning("pak_download()","Failed to download %s",pi->url); } #endif #endif } else { // download using our simutrans code sprintf(outfilename, "%s.zip", pi->name); const char* url = pi->url + 7; // minus http:// char site_ip[1024]; tstrncpy(site_ip, url, lengthof(site_ip)); const char* site_path = strchr(url, '/'); site_ip[site_path - url] = 0; strcat(site_ip, ":80"); const char* err = NULL; err = network_http_get_file(site_ip, site_path, outfilename); if (err) { dbg->warning("Pakset download failed with", "%s", err); j += 2; ls.set_progress(j); all_good = false; continue; } } ls.set_progress(++j); DBG_DEBUG(__FUNCTION__, "pak target %s", pi->name); DBG_DEBUG(__FUNCTION__, "download successful to %s, attempting extract", outfilename); extract_pak_from_zip(outfilename); dr_remove(outfilename); ls.set_progress(++j); } return all_good; } #endif #if 0 // curl code broken for now #include // linux/android specific, function to create folder makes use of opendir (linux system call) and mkdir (via system) static bool create_folder_if_required(const char* extracted_path) { char extracted_folder_name[FILENAME_MAX]; strcpy(extracted_folder_name, extracted_path); char* last_occurence = strrchr(extracted_folder_name, '/'); if (last_occurence != NULL) { last_occurence[0] = '\0'; } else { DBG_DEBUG(__FUNCTION__, "Error searching for path? %s", extracted_folder_name); return false; } char current_path[PATH_MAX]; dr_getcwd(current_path, PATH_MAX); if (dr_chdir(extracted_folder_name) != 0) { dr_mkdir(extracted_folder_name); DBG_DEBUG(__FUNCTION__, "- - Created folder %s", extracted_folder_name); } if (dr_chdir(extracted_folder_name) != 0) { DBG_DEBUG(__FUNCTION__, "Failed to create %s", extracted_folder_name); return false; } dr_chdir(current_path); return true; } static void extract_pak_from_zip(const char* outfilename) { zip_t* zip_archive; int err; if ((zip_archive = zip_open(outfilename, ZIP_RDONLY, &err)) == NULL) { zip_error_t error; zip_error_init_with_code(&error, err); DBG_DEBUG(__FUNCTION__, "cannot open zip archive \"%s\": %s : %d", outfilename, zip_error_strerror(&error), error); zip_error_fini(&error); } DBG_DEBUG(__FUNCTION__, "zip archive opened"); zip_uint64_t nentry = (zip_uint64_t)zip_get_num_entries(zip_archive, 0); for (zip_uint64_t idx = 0; idx < nentry; idx++) { struct zip_stat st; if (zip_stat_index(zip_archive, idx, 0, &st) == -1) { DBG_DEBUG(__FUNCTION__, "cannot read file stat %d in zip archive: %s", idx, zip_strerror(zip_archive)); continue; } zip_file_t* zip_file; if ((zip_file = zip_fopen_index(zip_archive, idx, 0)) == NULL) { DBG_DEBUG(__FUNCTION__, "cannot open file %s (index %d) in zip_archive: %s", st.name, idx, zip_strerror(zip_archive)); continue; } char* contents = new char[st.size]; zip_int64_t read_size; if ((read_size = zip_fread(zip_file, contents, st.size)) == -1) { DBG_DEBUG(__FUNCTION__, "failed to read content in file %s (index %d) of zip_archive: %s", st.name, idx, zip_file_strerror(zip_file)); } else { if (read_size != (zip_int64_t)st.size) { DBG_DEBUG(__FUNCTION__, "unexpected content size in file %s (index %d) of zip_archive; is %d, should be %d: %s", st.name, idx, read_size, st.size); } else { // path may start with simutrans/, in which case it can be removed safely bool start_with_simutrans = STRNICMP(st.name, "simutrans", 9) == 0; const char* target_filename = start_with_simutrans ? st.name + 10 : st.name; char extracted_path[PATH_MAX]; sprintf(extracted_path, "%s%s", env_t::install_dir, target_filename); DBG_DEBUG(__FUNCTION__, "- %s: %d", extracted_path, st.size); if (!create_folder_if_required(extracted_path)) { continue; } if (st.size > 0) { if (FILE* f = dr_fopen(extracted_path, "wb")) { fwrite(contents, st.size, 1, f); fclose(f); } else { DBG_DEBUG(__FUNCTION__, "Error writing file"); } } } } zip_fclose(zip_file); } if (zip_close(zip_archive) == -1) { DBG_DEBUG(__FUNCTION__, "cannot close zip archive: %s", zip_strerror(zip_archive)); } dr_chdir(env_t::install_dir); dr_remove(outfilename); } #include static size_t curl_write_data(void* ptr, size_t size, size_t nmemb, FILE* stream) { size_t written = fwrite(ptr, size, nmemb, stream); return written; } static CURLcode curl_download_file(CURL* curl, const char* target_file, const char* url) { FILE* fp = dr_fopen(target_file, "wb"); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); char cabundle_path[PATH_MAX]; sprintf(cabundle_path, "%s%s", env_t::base_dir, "cacert.pem"); FILE* cabundle_file; if ((cabundle_file = dr_fopen(cabundle_path, "r"))) { fclose(cabundle_file); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_CAINFO, cabundle_path); } else { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); dbg->warning(__FUNCTION__, "ssl certificate authority bundle not found at %s; https calls will not be validated; this may be a security concern", cabundle_path); } curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); res = curl_easy_perform(curl); fclose(fp); return res; } // download and install pak into install path: using curl and libzip (huge certificate storage) bool pak_download(vector_tplpaks) { CURL* curl = curl_easy_init(); // can only be called once during program lifecycle if (curl) { DBG_DEBUG(__FUNCTION__, "libcurl initialized"); char outfilename[FILENAME_MAX]; loadingscreen_t ls("Install paks", paks.get_count() * 2, true, false); int j = 0; for (paksetinfo_t *pi : paks.get_selections()) { ls.set_info(pi->name); sprintf(outfilename, "%s.zip", pi->name); dr_chdir(env_t::install_dir); CURLcode res = curl_download_file(curl, outfilename, pi->url); ls.set_progress(++j); DBG_DEBUG(__FUNCTION__, "pak target %s", pi->url); if (res == 0) { DBG_DEBUG(__FUNCTION__, "download successful to %s, attempting extract", outfilename); read_zip(outfilename); } else { DBG_DEBUG(__FUNCTION__, "download failed with error code %s, check curl errors; skipping", curl_easy_strerror(res)); } ls.set_progress(++j); } curl_easy_cleanup(curl); } else { DBG_DEBUG(__FUNCTION__, "libcurl failed to initialize, pakset not downloaded"); } finish_install = true; return false; } #endif simutrans-124.5/src/simutrans/dataobj/pakset_downloader.h000066400000000000000000000012721520675460500236640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef PAKSET_DOWNLOADER_H #define PAKSET_DOWNLOADER_H #include "../simtypes.h" #include "../tpl/vector_tpl.h" typedef struct { const char *url; const char *name; const char *version; uint size; } paksetinfo_t; // download and install pak into current path bool pak_download(vector_tpl); // check if pakset is installed, and if yes, check version string bool pak_need_update(const paksetinfo_t *); // check, if we can handle these kindS of paks with the current configuration bool pak_can_download(const paksetinfo_t *); bool pak_remove(const paksetinfo_t*); #endif simutrans-124.5/src/simutrans/dataobj/pakset_manager.cc000066400000000000000000000323031520675460500232750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "pakset_manager.h" #include "../utils/searchfolder.h" #include "../display/simgraph.h" #include "../sys/simsys.h" #include "../simskin.h" #include "../simloadingscreen.h" #include "../descriptor/ground_desc.h" #include "../descriptor/reader/obj_reader.h" #include "../descriptor/vehicle_desc.h" #include "../utils/simstring.h" #include "../utils/cbuffer.h" #include "../dataobj/translator.h" #include "../gui/help_frame.h" #include "../gui/simwin.h" #include "../dataobj/environment.h" #include "../network/pakset_info.h" pakset_manager_t::obj_map_t* pakset_manager_t::registered_readers; inthashtable_tpl > pakset_manager_t::loaded; pakset_manager_t::unresolved_map_t pakset_manager_t::unresolved; ptrhashtable_tpl pakset_manager_t::fatals; std::string pakset_manager_t::doublettes; stringhashtable_tpl pakset_manager_t::missing_pak_names; std::string pakset_manager_t::overlaid_warning; void pakset_manager_t::register_reader(obj_reader_t *reader) { if(!registered_readers) { registered_readers = new obj_map_t; } registered_readers->put(reader->get_type(), reader); //printf("This program can read %s objects\n", get_type_name()); } const char* pakset_manager_t::get_doubled_warning_message() { return translator::translate("Doubled objects detected. Click to see details."); } void pakset_manager_t::load_pakset(bool load_addons) { dbg->message("pakset_manager_t::load_pakset", "Reading object data from %s...", env_t::pak_dir.c_str()); if (!load_paks_from_directory( env_t::pak_dir.c_str(), load_addons, translator::translate("Loading paks ...") )) { dbg->fatal("pakset_manager_t::load_pakset", "Failed to load pakset. Please re-download or select another pakset."); } overlaid_warning.clear(); if( had_overlaid() ) { overlaid_warning.append( translator::translate("

Error

") ); overlaid_warning.append( env_t::pak_name + translator::translate("contains the following doubled objects:

") + get_overlaid() + "

" ); clear_overlaid(); } if( load_addons ) { // try to read addons from private directory dr_chdir( env_t::user_dir ); // Do not disable addons after failing to find addon objects // because a user might only have addon scenarios but no addon objects. // So do not check the return value here. load_paks_from_directory("addons/" + env_t::pak_name, true, translator::translate("Loading addon paks ...")); dr_chdir( env_t::base_dir ); if( had_overlaid() ) { overlaid_warning.append( translator::translate("

Warning

addons for") + env_t::pak_name + translator::translate("contains the following doubled objects:

") + get_overlaid() ); clear_overlaid(); } } if (!finish_loading()) { dbg->fatal("pakset_manager_t::load_pakset", "Failed to load pakset. Please re-download or select another pakset."); } pakset_info_t::calculate_checksum(); if( env_t::pakset_debug ) { pakset_info_t::debug(); } } void pakset_manager_t::open_doubled_warning_window() { help_frame_t *win = new help_frame_t(); win->set_text( overlaid_warning.c_str() ); create_win(win, w_info, magic_none); } bool pakset_manager_t::load_paks_from_directory(const std::string &path, bool load_addons, const char *message) { const bool drawing = gfx->is_display_init(); // step is a bitmask to decide when it's time to update the progress bar. // It takes the biggest power of 2 less than the number of elements and // divides it in 256 sub-steps at most (the -7 comes from here) searchfolder_t find; const searchfolder_t::search_flags_t addon_flags = load_addons ? searchfolder_t::SF_NONE : searchfolder_t::SF_NOADDONS; const sint32 max = find.search(path, "pak", addon_flags | searchfolder_t::SF_PREPEND_PATH, 4); sint32 step = -7; for( sint32 bit = 1; bit < max; bit += bit ) { step++; } if( step < 0 ) { step = 0; } step = (2<get_screen_size(); gfx->draw_rect( 0, 0, screen.w, screen.h, gfx->palette_lookup(COL_BLACK), true); if (!load_pak_file(path + "symbol.BigLogo.pak")) { dbg->warning("pakset_manager_t::load_paks_from_directory", "File 'symbol.BigLogo.pak' cannot be read, startup logo will not be displayed!"); } } loadingscreen_t ls( message, max, true ); if( ground_desc_t::outside==NULL ) { // define the pak tile width if (!load_pak_file(path + "ground.Outside.pak")) { return false; } if(ground_desc_t::outside==NULL) { dbg->warning("pakset_manager_t::load_paks_from_directory", "File ground.Outside.pak not found, cannot guess tile size! (driving on left will not work!)"); } else if (char const* const copyright = ground_desc_t::outside->get_copyright()) { ls.set_info(copyright); } } DBG_MESSAGE("pakset_manager_t::load_paks_from_directory", "Reading from '%s'", path.c_str()); uint n = 0; for (char* const& pak_filename : find) { if (!load_pak_file(pak_filename)) { dbg->warning("pakset_manager_t::load_paks_from_directory", "Cannot load '%s', some objects might be unavailable!", pak_filename); } if ((n++ & step) == 0 && drawing) { ls.set_progress(n); } } ls.set_progress(max); return find.begin()!=find.end(); } bool pakset_manager_t::load_pak_file(const std::string &filename) { // added trace PAKSET_INFO("loading", "name=%s", filename.c_str()); FILE* const fp = dr_fopen(filename.c_str(), "rb"); if (!fp) { dbg->error("pakset_manager_t::load_pak_file", "Reading '%s' failed!", filename.c_str()); return false; } // This is the normal header reading code int c; uint32 n = 0; do { c = fgetc(fp); n ++; } while(c != EOF && c != 0x1a); if(c == EOF) { dbg->error("pakset_manager_t::load_pak_file", "Unexpected end of file after %u bytes while reading '%s'!", n, filename.c_str()); fclose(fp); return false; } // Compiled Version char dummy[4]; n = fread(dummy, 4, 1, fp); if (n != 1) { fclose(fp); return false; } char *p = dummy; const uint32 version = decode_uint32(p); PAKSET_INFO("pakset_manager_t::load_pak_file", "%s, reading %u blocks, file version is %x", filename.c_str(), n, version); if(version <= COMPILER_VERSION_CODE) { obj_desc_t *data = NULL; if (!read_nodes(fp, data, 0, version)) { fclose(fp); return false; } } else { dbg->warning("pakset_manager_t::load_pak_file", "Version of '%s' is too old, %u instead of %u", filename.c_str(), version, COMPILER_VERSION_CODE ); fclose(fp); return false; } fclose(fp); return true; } /* * Do the last loading procedures * Resolve all xrefs */ bool pakset_manager_t::finish_loading() { // vehicle to follow to mark something cannot lead a convoi (prev[0]=any) or cannot end a convoi (next[0]=any) vehicle_desc_t::any_vehicle = new vehicle_desc_t(ignore_wt, 1, vehicle_desc_t::unknown); // first we add the any_vehicle to xrefs obj_for_xref( obj_vehicle, "any", vehicle_desc_t::any_vehicle ); resolve_xrefs(); for(auto const& elem : *registered_readers) { PAKSET_INFO("pakset_manager_t::finish_loading", "Checking %s objects...", elem.value->get_type_name()); if (!elem.value->successfully_loaded()) { dbg->warning("pakset_manager_t::finish_loading", "... failed!"); return false; } } return true; } void pakset_manager_t::clear_missing_paks() { missing_pak_names.clear(); } // store missing obj during load and their severity void pakset_manager_t::add_missing_paks( const char *name, missing_level_t level ) { if( missing_pak_names.get( name )==NOT_MISSING ) { missing_pak_names.put( strdup(name), level ); } } void pakset_manager_t::warn_if_paks_missing() { if( missing_pak_names.empty() ) { return; // everything OK } cbuffer_t msg; msg.append(""); msg.append(translator::translate("Missing pakfiles")); msg.append("\n"); cbuffer_t error_paks; cbuffer_t warning_paks; cbuffer_t paklog; paklog.append( "\n" ); for(auto const& i : missing_pak_names) { if (i.value <= MISSING_ERROR) { error_paks.append(translator::translate(i.key)); error_paks.append("
\n"); paklog.append( i.key ); paklog.append("\n" ); } else { warning_paks.append(translator::translate(i.key)); warning_paks.append("
\n"); } } if( error_paks.len()>0 ) { msg.append("

"); msg.append(translator::translate("Pak which may cause severe errors:")); msg.append("


\n"); msg.append("
\n"); msg.append( error_paks ); msg.append("
\n"); dbg->warning( "The following paks are missing and may cause errors", paklog ); } if( warning_paks.len()>0 ) { msg.append("

"); msg.append(translator::translate("Pak which may cause visual errors:")); msg.append("


\n"); msg.append("
\n"); msg.append( warning_paks ); msg.append("
\n"); } help_frame_t *win = new help_frame_t(); win->set_text( msg ); create_win(win, w_info, magic_pakset_info_t); } static bool read_node_info(obj_node_info_t& node, FILE* const f, uint32 const version) { char data[EXT_OBJ_NODE_INFO_SIZE]; if (fread(data, OBJ_NODE_INFO_SIZE, 1, f) != 1) { return false; } char *p = data; node.type = decode_uint32(p); node.nchildren = decode_uint16(p); node.size = decode_uint16(p); // can have larger records if (version != COMPILER_VERSION_CODE_11 && node.size == LARGE_RECORD_SIZE) { if (fread(p, sizeof(data) - OBJ_NODE_INFO_SIZE, 1, f) != 1) { return false; } node.size = decode_uint32(p); } return true; } bool pakset_manager_t::read_nodes(FILE *fp, obj_desc_t *&data, int node_depth, uint32 version) { obj_node_info_t node; if (!read_node_info(node, fp, version)) { return false; } obj_reader_t *reader = registered_readers->get(static_cast(node.type)); if(reader) { // PAKSET_INFO("pakset_manager_t::read_nodes", "Reading %.4s-node of length %d with '%s'", reinterpret_cast(&node.type), node.size, reader->get_type_name()); data = reader->read_node(fp, node); if (!data) { return false; } if (node.nchildren != 0) { data->children = new obj_desc_t *[node.nchildren]; for (int i = 0; i < node.nchildren; i++) { if (!read_nodes(fp, data->children[i], node_depth + 1, version)) { // Note: cannot delete siblings of data->children[i], since equal images point to the same desc delete data; // data->children is delete[]'d by the destructor data = NULL; return false; } } } //DBG_DEBUG("obj_reader_t","registering with '%s'", reader->get_type_name()); if(node_depth<2 || node.type!=obj_cursor) { // since many buildings are with cursors that do not need registration reader->register_obj(data); } } else { // no reader found ... dbg->warning("pakset_manager_t::read_nodes", "Skipping unknown %.4s-node\n", reinterpret_cast(&node.type)); if (fseek(fp, node.size, SEEK_CUR) != 0) { return false; } for(int i = 0; i < node.nchildren; i++) { if (!skip_nodes(fp,version)) { return false; } } data = NULL; } return true; } bool pakset_manager_t::skip_nodes(FILE *fp,uint32 version) { obj_node_info_t node; if (!read_node_info(node, fp, version)) { return false; } if (fseek(fp, node.size, SEEK_CUR) != 0) { return false; } for(int i = 0; i < node.nchildren; i++) { if (!skip_nodes(fp,version)) { return false; } } return true; } void pakset_manager_t::resolve_xrefs() { slist_tpl xref_nodes; for(auto const& u : unresolved) { for(auto const& i : u.value) { obj_desc_t *obj_loaded = NULL; if (!strempty(i.key)) { if (stringhashtable_tpl* const objtype_loaded = loaded.access(u.key)) { obj_loaded = objtype_loaded->get(i.key); } } for(obj_desc_t** const x : i.value) { if (!obj_loaded && fatals.get(x)) { dbg->fatal("pakset_manager_t::resolve_xrefs", "Cannot resolve '%4.4s-%s'", &u.key, i.key); } // delete old xref-node xref_nodes.append(*x); *x = obj_loaded; } } } while (!xref_nodes.empty()) { delete xref_nodes.remove_first(); } loaded.clear(); unresolved.clear(); fatals.clear(); } void pakset_manager_t::obj_for_xref(obj_type type, const char *name, obj_desc_t *data) { stringhashtable_tpl *objtype_loaded = loaded.access(type); if(!objtype_loaded) { loaded.put(type); objtype_loaded = loaded.access(type); } objtype_loaded->remove(name); objtype_loaded->put(name, data); } void pakset_manager_t::xref_to_resolve(obj_type type, const char *name, obj_desc_t **dest, bool fatal) { stringhashtable_tpl< slist_tpl > *typeunresolved = unresolved.access(type); if(!typeunresolved) { unresolved.put(type); typeunresolved = unresolved.access(type); } slist_tpl *list = typeunresolved->access(name); if(!list) { typeunresolved->put(name); list = typeunresolved->access(name); } list->insert(dest); if(fatal) { fatals.put(dest, 1); } } void pakset_manager_t::doubled(const char *what, const char *name) { doublettes.append( (std::string)what+"::"+name+"
" ); } simutrans-124.5/src/simutrans/dataobj/pakset_manager.h000066400000000000000000000074411520675460500231440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_PAKSET_MANAGER_H #define DATAOBJ_PAKSET_MANAGER_H #include "../descriptor/objversion.h" #include "../tpl/inthashtable_tpl.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/ptrhashtable_tpl.h" class obj_desc_t; class obj_reader_t; /// Missing things during loading: /// factories, vehicles, roadsigns or catenary may be severe enum missing_level_t { NOT_MISSING = 0, MISSING_FACTORY = 1, MISSING_VEHICLE = 2, MISSING_SIGN = 3, MISSING_WAYOBJ = 4, MISSING_ERROR = 4, MISSING_BRIDGE, MISSING_BUILDING, MISSING_WAY }; class pakset_manager_t { /// table of registered obj readers sorted by id typedef inthashtable_tpl obj_map_t; static obj_map_t *registered_readers; // // object addresses needed for resolving xrefs later // - stored in a hash table with type and name // static inthashtable_tpl > loaded; typedef inthashtable_tpl > > unresolved_map_t; static unresolved_map_t unresolved; static ptrhashtable_tpl fatals; /// Read a descriptor node. /// @param fp File to read from /// @param[out] data If reading is successful, contains descriptor for the object, else NULL. /// @param register_nodes Nesting level for desc-nodes, should normally be 0 /// @param version File format version static bool read_nodes(FILE *fp, obj_desc_t *&data, int register_nodes, uint32 version); static bool skip_nodes(FILE *fp, uint32 version); static std::string doublettes; static std::string overlaid_warning; static stringhashtable_tpl missing_pak_names; public: static void register_reader(obj_reader_t *reader); /// Loads pakset data from env_t::pak_dir, and also from env_t::user_dir/addons/env_t::pak_name if @p load_addons is true static void load_pakset(bool load_addons); /// Only for single files, must take care of all the cleanup/registering matrix themselves static bool load_pak_file(const std::string &filename); /// special error handling for double objects static void doubled(const char *what, const char *name); static void clear_missing_paks(); /// For warning, when stuff had to be removed/replaced /// level must be >=1 (1: factory, 2: vehicles, >=4: not so important) /// may be refined later static void add_missing_paks(const char *name, missing_level_t critical_level); // Display and log a warning if there are missing paks after loading a saved game static void warn_if_paks_missing(); static void xref_to_resolve(obj_type type, const char *name, obj_desc_t **dest, bool fatal); static void obj_for_xref(obj_type type, const char *name, obj_desc_t *data); // Warnings for doubled objects // Message sent when doubled objects were loaded static const char* get_doubled_warning_message(); // Return true if doubled objects were loaded static bool needs_doubled_warning_message() { return !overlaid_warning.empty(); } // Open a window to display the full warning static void open_doubled_warning_window(); private: /** * Loads all pak files from a directory and its subdirectories, displaying a progress bar if the display is initialized. * @param path Directory to be scanned for PAK files * @param load_addons true to also load paks from subdirectories named "addons" * @param message Label to show over the progress bar */ static bool load_paks_from_directory(const std::string &path, bool load_addons, const char *message); static bool finish_loading(); static bool had_overlaid() { return !doublettes.empty(); } static void clear_overlaid() { doublettes.clear(); } static std::string get_overlaid() { return doublettes; } static void resolve_xrefs(); }; #endif simutrans-124.5/src/simutrans/dataobj/powernet.cc000066400000000000000000000045431520675460500221640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "powernet.h" #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_mutex_t netlist_mutex = PTHREAD_MUTEX_INITIALIZER; #endif extern const uint32 POWER_TO_MW; // defined in leitung2 // maximum possible limit is (1 << (63 - FRACTION_PRECISION)) const uint64 powernet_t::max_capacity = (uint64)4000000 << POWER_TO_MW; // (4 TW) const uint8 powernet_t::FRACTION_PRECISION = 16; slist_tpl powernet_t::powernet_list; void powernet_t::new_world() { while(!powernet_list.empty()) { powernet_t *net = powernet_list.remove_first(); delete net; } } void powernet_t::step_all(uint32 delta_t) { for(powernet_t* const p : powernet_list) { p->step(delta_t); } } powernet_t::powernet_t() { #ifdef MULTI_THREAD pthread_mutex_lock( &netlist_mutex ); #endif powernet_list.insert( this ); #ifdef MULTI_THREAD pthread_mutex_unlock( &netlist_mutex ); #endif power_supply = 0; power_demand = 0; norm_demand = 0 << FRACTION_PRECISION; norm_supply = 0 << FRACTION_PRECISION; } powernet_t::~powernet_t() { #ifdef MULTI_THREAD pthread_mutex_lock( &netlist_mutex ); #endif powernet_list.remove( this ); #ifdef MULTI_THREAD pthread_mutex_unlock( &netlist_mutex ); #endif } /** * Computes a normalized supply/demand value. * If demand fully satisfies supply or supply is 0 then output is 1. */ sint32 compute_norm_sd(uint64 const demand, uint64 const supply) { // compute demand factor if( (demand >= supply) || (supply == 0) ) { return (sint32)1 << powernet_t::FRACTION_PRECISION; } else { return (sint32)((demand << powernet_t::FRACTION_PRECISION) / supply); } } void powernet_t::step(uint32 delta_t) { if( delta_t==0 ) { return; } // get limited values uint64 const supply = get_supply(); uint64 const demand = get_demand(); // compute normalized demand norm_demand = compute_norm_sd(demand, supply); norm_supply = compute_norm_sd(supply, demand); } /** * Clamps a power value to be within the maximum capacity. */ uint64 clamp_power(uint64 const power) { return power > powernet_t::max_capacity ? powernet_t::max_capacity : power; } uint64 powernet_t::get_supply() const { return clamp_power(power_supply); } uint64 powernet_t::get_demand() const { return clamp_power(power_demand); } simutrans-124.5/src/simutrans/dataobj/powernet.h000066400000000000000000000051051520675460500220210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_POWERNET_H #define DATAOBJ_POWERNET_H #include "../simtypes.h" #include "../tpl/slist_tpl.h" /** @file powernet.h Data structure to manage a net of powerlines - a powernet */ /** * Data class for power networks. A two phase queue to store * and hand out power. */ class powernet_t { public: /** * Max power capacity of each network. * Avoids possible overflows while providing a human friendly number. */ static const uint64 max_capacity; // number of fractional bits for network load values static const uint8 FRACTION_PRECISION; /** * Must be called when a new map is started or loaded. Clears the table of networks. */ static void new_world(); /// Steps all powernets static void step_all(uint32 delta_t); private: static slist_tpl powernet_list; // Network power supply. uint64 power_supply; // Network power demand. uint64 power_demand; // Computed normalized demand. sint32 norm_demand; // Computed normalized supply. sint32 norm_supply; // Just transfers power demand and supply to current step void step(uint32 delta_t); public: powernet_t(); ~powernet_t(); uint64 get_max_capacity() const { return max_capacity; } /** * Add power supply for next step. */ void add_supply(const uint32 p) { power_supply += (uint64)p; } /** * Subtract power supply for next step. */ void sub_supply(const uint32 p) { power_supply -= (uint64)p; } /** * Get the total power supply of the network. */ uint64 get_supply() const; /** * Add power demand for next step. */ void add_demand(const uint32 p) { power_demand += (uint64)p; } /** * Subtract power demand for next step. */ void sub_demand(const uint32 p) { power_demand -= (uint64)p; } /** * Get the total power demand of the network. */ uint64 get_demand() const; /** * Return the normalized value of demand in the network. * Will have a logical value between 0 (no demand) and 1 (all supply consumed). * Will have a logical value of 0 when no supply is present. * Return value is fixed point with FRACTION_PRECISION fractional bits. */ sint32 get_normal_demand() const { return norm_demand; } /** * Return the normalized value of supply in the network. * Will have a logical value between 0 (no supply) and 1 (all demand supplied). * Will have a logical value of 0 when no demand is present. * Return value is fixed point with FRACTION_PRECISION fractional bits. */ sint32 get_normal_supply() const { return norm_supply; } }; #endif simutrans-124.5/src/simutrans/dataobj/records.cc000066400000000000000000000075371520675460500217700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "records.h" #include "loadsave.h" #include "translator.h" #include "../player/simplay.h" #include "../simcolor.h" #include "../simmesg.h" #include "../simconvoi.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include "../vehicle/vehicle.h" #include "../macros.h" void records_t::speed_record_t::rdwr(loadsave_t* f) { f->rdwr_long(speed); f->rdwr_long(year_month); pos.rdwr(f); f->rdwr_byte(player_nr); if (f->is_loading()) { // for now it is not saved cnv = convoihandle_t(); } f->rdwr_str(name, lengthof(name)); } void records_t::rdwr(loadsave_t* f) { max_rail_speed.rdwr(f); max_monorail_speed.rdwr(f); max_maglev_speed.rdwr(f); max_narrowgauge_speed.rdwr(f); max_road_speed.rdwr(f); max_ship_speed.rdwr(f); max_air_speed.rdwr(f); } sint32 records_t::get_record_speed( waytype_t w ) const { switch(w) { case road_wt: return max_road_speed.speed; case track_wt: case tram_wt: return max_rail_speed.speed; case monorail_wt: return max_monorail_speed.speed; case maglev_wt: return max_maglev_speed.speed; case narrowgauge_wt: return max_narrowgauge_speed.speed; case water_wt: return max_ship_speed.speed; case air_wt: return max_air_speed.speed; default: return 0; } } void records_t::clear_speed_records() { max_road_speed.speed = 0; max_rail_speed.speed = 0; max_monorail_speed.speed = 0; max_maglev_speed.speed = 0; max_narrowgauge_speed.speed = 0; max_ship_speed.speed = 0; max_air_speed.speed = 0; } void records_t::notify_record( convoihandle_t cnv, sint32 max_speed, koord3d k, uint32 current_month ) { speed_record_t *sr = NULL; switch (cnv->front()->get_waytype()) { case road_wt: sr = &max_road_speed; break; case track_wt: case tram_wt: sr = &max_rail_speed; break; case monorail_wt: sr = &max_monorail_speed; break; case maglev_wt: sr = &max_maglev_speed; break; case narrowgauge_wt: sr = &max_narrowgauge_speed; break; case water_wt: sr = &max_ship_speed; break; case air_wt: sr = &max_air_speed; break; default: assert(0); } // avoid the case of two convois with identical max speed ... if(cnv!=sr->cnv && sr->speed+1==max_speed) { return; } // really new/faster? if(k!=sr->pos || sr->speed+1cnv = cnv; sr->speed = max_speed-1; sr->year_month = current_month; sr->pos = k; sr->player_nr = PLAYER_UNOWNED; // will be set, when accepted } else { // repeted same speed, same place sr->cnv = cnv; sr->speed = max_speed-1; sr->pos = k; // same convoi and same position if(sr->player_nr==PLAYER_UNOWNED && current_month!=sr->year_month) { // notify the world of this new record sr->speed = max_speed-1; sr->player_nr = cnv->get_owner()->get_player_nr(); tstrncpy(sr->name, cnv->get_internal_name(), lengthof(sr->name)); const char* text; switch (cnv->front()->get_waytype()) { default: NOT_REACHED case road_wt: text = "New world record for motorcars: %.1f km/h by %s."; break; case track_wt: case tram_wt: text = "New world record for railways: %.1f km/h by %s."; break; case monorail_wt: text = "New world record for monorails: %.1f km/h by %s."; break; case maglev_wt: text = "New world record for maglevs: %.1f km/h by %s."; break; case narrowgauge_wt: text = "New world record for narrowgauges: %.1f km/h by %s."; break; case water_wt: text = "New world record for ship: %.1f km/h by %s."; break; case air_wt: text = "New world record for planes: %.1f km/h by %s."; break; } cbuffer_t buf; buf.printf( translator::translate(text), speed_to_kmh(10*sr->speed)/10.0, sr->cnv->get_name() ); msg->add_message( buf, sr->pos, message_t::new_vehicle, PLAYER_FLAG|sr->player_nr ); } } } simutrans-124.5/src/simutrans/dataobj/records.h000066400000000000000000000032641520675460500216230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_RECORDS_H #define DATAOBJ_RECORDS_H #include "koord3d.h" #include "../convoihandle.h" class message_t; class player_t; class loadsave_t; /** * World record speed management. * Keeps track of the fastest vehicles in game. */ class records_t { public: records_t(message_t *_msg) : msg(_msg) {} ~records_t() {} /** Returns the current speed record for the given way type. */ sint32 get_record_speed( waytype_t w ) const; /** Posts a message that a new speed record has been set. */ void notify_record( convoihandle_t cnv, sint32 max_speed, koord3d k, uint32 current_month ); /** Resets all speed records. */ void clear_speed_records(); void rdwr(loadsave_t* f); private: // Destination for world record notifications. message_t *msg; /** * Class representing a word speed record. */ class speed_record_t { public: convoihandle_t cnv; char name[128]; sint32 speed; koord3d pos; sint8 player_nr; // Owner uint32 year_month; speed_record_t() : cnv(), speed(0), pos(koord3d::invalid), player_nr(PLAYER_UNOWNED), year_month(0) { name[0] = 0; } void rdwr(loadsave_t* f); }; /// World rail speed record speed_record_t max_rail_speed; /// World monorail speed record speed_record_t max_monorail_speed; /// World maglev speed record speed_record_t max_maglev_speed; /// World narrowgauge speed record speed_record_t max_narrowgauge_speed; /// World road speed record speed_record_t max_road_speed; /// World ship speed record speed_record_t max_ship_speed; /// World air speed record speed_record_t max_air_speed; }; #endif simutrans-124.5/src/simutrans/dataobj/rect.cc000066400000000000000000000057771520675460500212700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "rect.h" rect_t::rect_t() : origin(0, 0), size(0, 0) { } rect_t::rect_t(koord const origin, koord const size) : origin(origin), size(size) { } rect_t::rect_t(koord const origin, sint16 const x, sint16 const y) : origin(origin), size(x, y) { } size_t rect_t::fragment_difference(rect_t const &remove, rect_t *const result, size_t const result_length) const { if (result_length < rect_t::MAX_FRAGMENT_DIFFERENCE_COUNT) { assert("rect_t::fragment_difference : attempting to fragment when space in result array is less than MAX_FRAGMENT_DIFFERENCE_COUNT"); } rect_t internal_remove = remove; internal_remove.mask(*this); rect_t *fragment = result; if (internal_remove == *this) { // area subset of remove so nothing returned } else if(internal_remove.has_no_area()) { // no overlap so entire area unchanged *fragment = *this; fragment++; } else { // partial overlap so fragmentation occurs koord const internal_remove_extent = internal_remove.origin + internal_remove.size; koord const extent = origin + size; if (origin.y < internal_remove.origin.y) { // fragment at bottom of remove rect fragment->origin = origin; fragment->size.x = size.x; fragment->size.y = internal_remove.origin.y - origin.y; fragment++; } if (origin.x < internal_remove.origin.x) { // fragment at left of remove rect fragment->origin.x = origin.x; fragment->origin.y = internal_remove.origin.y; fragment->size.x = internal_remove.origin.x - origin.x; fragment->size.y = internal_remove.size.y; fragment++; } if (extent.x > internal_remove_extent.x) { // fragment at right of remove rect fragment->origin.x = internal_remove_extent.x; fragment->origin.y = internal_remove.origin.y; fragment->size.x = extent.x - internal_remove_extent.x; fragment->size.y = internal_remove.size.y; fragment++; } if (extent.y > internal_remove_extent.y) { // fragment at top of remove rect fragment->origin.x = origin.x; fragment->origin.y = internal_remove_extent.y; fragment->size.x = size.x; fragment->size.y = extent.y - internal_remove_extent.y; fragment++; } } return (size_t) (fragment - result); } void rect_t::mask(rect_t const &mask_rect) { koord extent = origin + size; koord const mask_extent = mask_rect.origin + mask_rect.size; if (origin.x >= mask_extent.x || extent.x <= mask_rect.origin.x || origin.y >= mask_extent.y || extent.y <= mask_rect.origin.y) { // completly masked origin = size = koord(0, 0); } else { // partially masked origin.clip_min(mask_rect.origin); extent.clip_max(mask_extent); size = extent - origin; } } bool rect_t::has_no_area() const { return size == koord(0, 0); } void rect_t::discard_area() { size = koord(0, 0); } bool rect_t::operator==(rect_t const &rhs) const { return origin == rhs.origin && size == rhs.size; } bool rect_t::operator!=(rect_t const &rhs) const { return !(*this == rhs); } simutrans-124.5/src/simutrans/dataobj/rect.h000066400000000000000000000043531520675460500211170ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_RECT_H #define DATAOBJ_RECT_H #include "koord.h" #include class rect_t { public: /** * @brief Map index is prepared value. */ static size_t const MAX_FRAGMENT_DIFFERENCE_COUNT = 4; /** * @brief Origin coordinates of this rect. * * The position of the lower left corner of this rect. */ koord origin; /** * @brief Size of dimensions of this rect. * * The size of the dimensions of this rect. This is the number of elements * covered by each dimension of this rect starting from the origin. Must * only ever be greater than or equal to 0. */ koord size; public: rect_t(); rect_t(koord const origin, koord const size); rect_t(koord const origin, sint16 const x, sint16 const y); /** * @brief Fragment this rect so as to remove area from another rect. * * Fragments this rect such that the resulting rects cover all area * contained by this rect that is not contained in the remove rect. The * returned value is the number of valid rect elements in the result array * that make up the requested area. Result length specifies the space * available in the result array to store fragmented rects and must be at * least MAX_FRAGMENT_DIFFERENCE_COUNT long. The fragmentation is baised * towards wide rects. */ size_t fragment_difference(rect_t const &remove, rect_t *const result, size_t const result_length) const; /** * @brief Mask part of this rect using a masking rect. * * Transforms this rect into the rect containing the intersection area * between this rect and the mask rect. Useful to clamp rects to map bounds. */ void mask(rect_t const &mask_rect); /** * @brief Returns true if this rect represents no area. * * Returns true of the size of area is 0. Such rects can occur after masking * operations or as place holder rects representing no map area. */ bool has_no_area() const; /** * @brief discard the current area of the rect. * * Set the size of this rect to 0. Useful to invalidate a rect. The origin * is not changed. */ void discard_area(); bool operator==(rect_t const &rhs) const; bool operator!=(rect_t const &rhs) const; }; #endif simutrans-124.5/src/simutrans/dataobj/ribi.cc000066400000000000000000000272371520675460500212530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../simconst.h" #include "ribi.h" #include "koord.h" #include "koord3d.h" // since we have now a dummy function instead an array const ribi_t::_nesw ribi_t::nesw;; // same like the layouts of buildings const ribi_t::ribi ribi_t::layout_to_ribi[4] = { south, east, north, west }; const int ribi_t::flags[16] = { 0, // none single | straight_ns, // north single | straight_ew, // east bend | twoway, // north-east single | straight_ns, // south straight_ns | twoway, // north-south bend | twoway, // south-east threeway, // north-south-east single | straight_ew, // west bend | twoway, // north-west straight_ew | twoway, // east-west threeway, // north-east-west bend | twoway, // south-west threeway, // north-south-west threeway, // south-east-west threeway, // all }; const ribi_t::ribi ribi_t::backwards[16] = { all, // none south, // north west, // east southwest, // north-east north, // south northsouth, // north-south northwest, // south-east west, // north-south-east east, // west southeast, // north-west eastwest, // east-west south, // north-east-west northeast, // south-west east, // north-south-west north, // south-east-west none // all }; const ribi_t::ribi ribi_t::doppelr[16] = { none, // none northsouth, // north eastwest, // east none, // north-east northsouth, // south northsouth, // north-south none, // south-east none, // north-south-east eastwest, // west none, // north-west eastwest, // east-west none, // north-east-west none, // south-west none, // north-south-west none, // south-east-west none // all }; static const ribi_t::ribi from_hang[81] = { ribi_t::none, // ribi_t::none:flat ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::south, // 4:north single height slope ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::south, // 8:north doubles height slope ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::east, // 12:west single height slope ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::east, // 24:west doubles height slope ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::west, // 28:east single height slope ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::north, // 36:south single height slope ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::west, // 56:east doubles height slope ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::north, // 72:south doubles height slope ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none, ribi_t::none // 80:all of the above }; const int slope_t::flags[81] = { way_ns | way_ew, // slope 0 # flat straight ns|ew 0, // slope 1 # sw1 doubles, // slope 2 # sw2 0, // slope 3 # se1 way_ns | single, // slope 4 # se1,sw1 straight ns doubles, // slope 5 # se1,sw2 doubles, // slope 6 # se2 doubles, // slope 7 # se2,sw1 way_ns | single | doubles, // slope 8 # se2,sw2 straight ns2 0, // slope 9 # ne1 0, // slope 10 # ne1, sw1 doubles, // slope 11 # ne1, sw2 way_ew | single, // slope 12 # ne1,se1 straight ew 0, // slope 13 # ne1,se1,sw1 doubles, // slope 14 # ne1,se1,sw2 doubles, // slope 15 # ne1,se2 doubles, // slope 16 # ne1,se2,sw1 doubles, // slope 17 # ne1,se2,sw2 doubles, // slope 18 # ne2 doubles, // slope 19 # ne2, sw1 doubles, // slope 20 # ne2, sw2 doubles, // slope 21 # ne2,se1 doubles, // slope 22 # ne2,se1,sw1 doubles, // slope 23 # ne2,se1,sw2 way_ew | single | doubles, // slope 24 # ne2,se2 straight ew2 doubles, // slope 25 # ne2,se2,sw1 doubles, // slope 26 # ne2,se2,sw2 0, // slope 27 # nw1 way_ew | single, // slope 28 # nw1, sw1 straight ew doubles, // slope 29 # nw1, sw2 0, // slope 30 # nw1, se1 0, // slope 31 # nw1, se1,sw1 doubles, // slope 32 # nw1, se1,sw2 doubles, // slope 33 # nw1, se2 doubles, // slope 34 # nw1, se2,sw1 doubles, // slope 35 # nw1, se2,sw2 way_ns | single, // slope 36 # nw1,ne1 straight ns 0, // slope 37 # nw1,ne1, sw1 doubles, // slope 38 # nw1,ne1, sw2 0, // slope 39 # nw1,ne1,se1 way_ns | way_ew | all_up, // slope 40 # nw1,ne1,se1,sw1 TODO 0 up 1 doubles | all_up, // slope 41 # nw1,ne1,se1,sw2 TODO 1 up 1 doubles, // slope 42 # nw1,ne1,se2 doubles | all_up, // slope 43 # nw1,ne1,se2,sw1 TODO 3 up 1 way_ns | single | doubles | all_up, // slope 44 # nw1,ne1,se2,sw2 TODO ns 4 up 1 doubles, // slope 45 # nw1,ne2 doubles, // slope 46 # nw1,ne2, sw1 doubles, // slope 47 # nw1,ne2, sw2 doubles, // slope 48 # nw1,ne2,se1 doubles | all_up, // slope 49 # nw1,ne2,se1,sw1 TODO 9 up 1 doubles | all_up, // slope 50 # nw1,ne2,se1,sw2 TODO 10 up 1 doubles, // slope 51 # nw1,ne2,se2 way_ew | single | doubles | all_up, // slope 52 # nw1,ne2,se2,sw1 TODO ew 12 up 1 doubles | all_up, // slope 53 # nw1,ne2,se2,sw2 TODO 13 up 1 doubles, // slope 54 # nw2 doubles, // slope 55 # nw2, sw1 way_ew | single | doubles, // slope 56 # nw2, sw2 straight ew2 doubles, // slope 57 # nw2, se1 doubles, // slope 58 # nw2, se1,sw1 doubles, // slope 59 # nw2, se1,sw2 doubles, // slope 60 # nw2, se2 doubles, // slope 61 # nw2, se2,sw1 doubles, // slope 62 # nw2, se2,sw2 doubles, // slope 63 # nw2,ne1 doubles, // slope 64 # nw2,ne1, sw1 doubles, // slope 65 # nw2,ne1, sw2 doubles, // slope 66 # nw2,ne1,se1 doubles | all_up, // slope 67 # nw2,ne1,se1,sw1 TODO 27 up 1 way_ew | single | doubles | all_up, // slope 68 # nw2,ne1,se1,sw2 TODO ew 28 up 1 doubles, // slope 69 # nw2,ne1,se2 doubles | all_up, // slope 70 # nw2,ne1,se2,sw1 TODO 30 up 1 doubles | all_up, // slope 71 # nw2,ne1,se2,sw2 TODO 31 up 1 way_ns | single | doubles, // slope 72 # nw2,ne2 straight ns2 doubles, // slope 73 # nw2,ne2, sw1 doubles, // slope 74 # nw2,ne2, sw2 doubles, // slope 75 # nw2,ne2,se1 way_ns | single | doubles | all_up, // slope 76 # nw2,ne2,se1,sw1 TODO ns 36 up 1 doubles | all_up, // slope 77 # nw2,ne2,se1,sw2 TODO 37 up 1 doubles, // slope 78 # nw2,ne2,se2 doubles | all_up, // slope 79 # nw2,ne2,se2,sw1 TODO 39 up 1 doubles | way_ns | way_ew | all_up // slope 80 # nw2,ne2,se2,sw2 TODO 0 up 2 }; const slope_t::type slope_from_ribi[16] = { 0, slope_t::south, slope_t::west, 0, slope_t::north, 0, 0, 0, slope_t::east, 0, 0, 0, 0, 0, 0, 0 }; const ribi_t::dir ribi_t::dirs[16] = { dir_invalid, // none dir_north, // north dir_east, // east dir_northeast, // north-east dir_south, // south dir_invalid, // north-south dir_southeast, // south-east dir_invalid, // north-south-east dir_west, // west dir_northwest, // north-west dir_invalid, // east-west dir_invalid, // north-east-west dir_southwest, // south-west dir_invalid, // north-south-west dir_invalid, // south-east-west dir_invalid // all }; const char ribi_t::names[16][5] = { " ", "N ", " E ", "NE ", " S ", "N S ", " ES ", "NES ", " W", "N W", " E W", "NE W", " SW", "N SW", " ESW", "NESW", }; ribi_t::ribi ribi_type(slope_t::type hang) // north slope -> south, ... ! { return from_hang[hang]; } ribi_t::ribi ribi_typ_intern(sint16 dx, sint16 dy) { ribi_t::ribi ribi = ribi_t::none; if(dx<0) { ribi |= ribi_t::west; } else if(dx>0) { ribi |= ribi_t::east; } if(dy<0) { ribi |= ribi_t::north; } else if(dy>0) { ribi |= ribi_t::south; } return ribi; } ribi_t::ribi ribi_type(const koord& dir) { return ribi_typ_intern(dir.x, dir.y); } ribi_t::ribi ribi_type(const koord3d& dir) { return ribi_typ_intern(dir.x, dir.y); } /** * check, if two directions are orthogonal * works with diagonals too */ bool ribi_t::is_perpendicular(ribi x, ribi y) { // for straight direction x use doppelr lookup table if(is_straight(x)) { return (doppelr[x] | doppelr[y]) == all; } // now diagonals (more tricky) if(x!=y) { return ((x-y)%3)==0; } // ok, then they are not orthogonal return false; } sint16 get_sloping_upwards(const slope_t::type slope, const ribi_t::ribi from) { // slope upwards relative to direction 'from' const slope_t::type from_slope = slope_type(from); if (from_slope == slope) { return 1; } else if (2*from_slope == slope) { return 2; } return 0; } slope_t::type slope_type(koord dir) { if(dir.x == 0) { if(dir.y < 0) { // north direction -> south slope return slope_t::south; } if(dir.y > 0) { return slope_t::north; // south direction -> north slope } } if(dir.y == 0) { if(dir.x < 0) { return slope_t::east; // west direction -> east slope } if(dir.x > 0) { return slope_t::west; // east direction -> west slope } } return slope_t::flat; // ??? } slope_t::type slope_type(ribi_t::ribi r) { return slope_from_ribi[r]; } simutrans-124.5/src/simutrans/dataobj/ribi.h000066400000000000000000000257051520675460500211130ustar00rootroot00000000000000 /* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_RIBI_H #define DATAOBJ_RIBI_H #include "../simtypes.h" #include "../simconst.h" #include "../simdebug.h" class koord; class koord3d; /** * Slopes of tiles. */ class slope_t { /// Static lookup table static const int flags[81]; /// Named constants for the flags table enum { doubles = 1 << 0, ///< two-height difference slopes way_ns = 1 << 1, ///< way possible in north-south direction way_ew = 1 << 2, ///< way possible in east-west direction single = 1 << 3, ///< way possible all_up = 1 << 4 ///< all corners raised }; public: typedef sint8 type; /** * Named constants for special cases. */ enum _type { flat = 0, northwest = 27, ///< NW corner northeast = 9, ///< NE corner southeast = 3, ///< SE corner southwest = 1, ///< SW corner north = slope_t::southeast+slope_t::southwest, ///< North slope west = slope_t::northeast+slope_t::southeast, ///< West slope east = slope_t::northwest+slope_t::southwest, ///< East slope south = slope_t::northwest+slope_t::northeast, ///< South slope all_up_one = slope_t::southwest+slope_t::southeast+slope_t::northeast+slope_t::northwest, ///all corners 1 high all_up_two = slope_t::all_up_one * 2, ///all corners 2 high raised = all_up_two, ///< special meaning: used as slope of bridgeheads and in terraforming tools (keep for compatibility) max_number = all_up_two }; /* * Macros to access the height of the 4 corners: */ #define corner_sw(i) ((i)%slope_t::southeast) // sw corner #define corner_se(i) (((i)/slope_t::southeast)%slope_t::southeast) // se corner #define corner_ne(i) (((i)/slope_t::northeast)%slope_t::southeast) // ne corner #define corner_nw(i) ((i)/slope_t::northwest) // nw corner #define encode_corners(sw, se, ne, nw) ( (sw) * slope_t::southwest + (se) * slope_t::southeast + (ne) * slope_t::northeast + (nw) * slope_t::northwest ) #define is_one_high(i) (i & 7) // quick method to know whether a slope is one high - relies on two high slopes being divisible by 8 -> i&7=0 (only works for slopes with flag single) /// Compute the slope opposite to @p x. Returns flat if @p x does not allow ways on it. static type opposite(type x) { return is_single(x) ? (is_one_high(x) ? (slope_t::all_up_one - x) : (slope_t::all_up_two - x)) : flat; } /// Rotate. static type rotate90(type x) { return ( ( (x % slope_t::southeast) * slope_t::northwest ) + ( ( x - (x % slope_t::southeast) ) / slope_t::southeast ) ); } /// Returns true if @p x has all corners raised. static bool is_all_up(type x) { return (flags[x] & all_up)>0; } /// Returns maximal height difference between the corners of this slope. static uint8 max_diff(type x) { return (x!=0)+(flags[x]&doubles); } /// Computes minimum height differnce between corners of @p high and @p low. static sint8 min_diff(type high, type low) { return min( min( corner_sw(high) - corner_sw(low), corner_se(high)-corner_se(low) ), min( corner_ne(high) - corner_ne(low), corner_nw(high) - corner_nw(low) ) ); } /// Returns if slope prefers certain way directions (either n/s or e/w). static bool is_single(type x) { return (flags[x] & single) != 0; } /// Returns if way can be build on this slope. static bool is_way(type x) { return (flags[x] & (way_ns | way_ew)) != 0; } /// Returns if way in n/s direction can be build on this slope. static bool is_way_ns(type x) { return (flags[x] & way_ns) != 0; } /// Returns if way in e/w direction can be build on this slope. static bool is_way_ew(type x) { return (flags[x] & way_ew) != 0; } }; /** * Old implementation of slopes: one bit per corner. * Used as bitfield to refer to specific corners of a tile * as well as for compatibility. */ struct slope4_t { /* bit-field: * Bit 0 is set if southwest corner is raised * Bit 1 is set if southeast corner is raised * Bit 2 is set if northeast corner is raised * Bit 3 is set if northwest corner is raised * * Don't get confused - the southern/southward slope has its northern corners raised * * Macros to access the height of the 4 corners for single slope: * One bit per corner */ enum _corners { corner_SW = 1 << 0, corner_SE = 1 << 1, corner_NE = 1 << 2, corner_NW = 1 << 3 }; typedef sint8 type; type value; public: explicit slope4_t(type v) : value(v) {} slope4_t(_corners c) : value(c) {} }; static inline sint8 scorner_sw(slope4_t sl) { return (sl.value & slope4_t::corner_SW) != 0; } // sw corner static inline sint8 scorner_se(slope4_t sl) { return (sl.value & slope4_t::corner_SE) != 0; } // se corner static inline sint8 scorner_ne(slope4_t sl) { return (sl.value & slope4_t::corner_NE) != 0; } // ne corner static inline sint8 scorner_nw(slope4_t sl) { return (sl.value & slope4_t::corner_NW) != 0; } // nw corner static inline slope_t::type slope_from_slope4(slope4_t sl, sint8 pak_height_factor) { return encode_corners(scorner_sw(sl) * pak_height_factor, scorner_se(sl) * pak_height_factor, scorner_ne(sl) * pak_height_factor, scorner_nw(sl) * pak_height_factor); } /** * Directions in simutrans. * ribi_t = Richtungs-Bit = Directions-Bitfield */ class ribi_t { /// Static lookup table static const int flags[16]; /// Named constants for properties of directions enum { single = 1 << 0, ///< only one bit set, way ends here straight_ns = 1 << 1, ///< contains straight n/s connection straight_ew = 1 << 2, ///< contains straight e/w connection bend = 1 << 3, ///< is a bend twoway = 1 << 4, ///< two bits set threeway = 1 << 5 ///< three bits set }; public: /** * Named constants for all possible directions. * 1=North, 2=East, 4=South, 8=West */ enum _ribi { none = 0, north = 1, east = 2, northeast = 3, south = 4, northsouth = 5, southeast = 6, northsoutheast = 7, west = 8, northwest = 9, eastwest = 10, northeastwest = 11, southwest = 12, northsouthwest = 13, southeastwest = 14, all = 15 }; typedef uint8 ribi; /** * Named constants to translate direction to image number for vehicles, signs. */ enum _dir { dir_invalid = 0, dir_south = 0, dir_west = 1, dir_southwest = 2, dir_southeast = 3, dir_north = 4, dir_east = 5, dir_northeast = 6, dir_northwest = 7 }; typedef uint8 dir; private: /// Lookup table to compute backward direction static const ribi backwards[16]; /// Lookup table ... static const ribi doppelr[16]; /// Lookup table to convert ribi to dir. static const dir dirs[16]; public: /// Lookup table to convert ribi mask to string static const char names[16][5]; /// Table containing the four compass directions (now as function) struct _nesw { ribi operator [] ( const uint8 i ) const { return 1< ns), map all others to zero static ribi doubles(ribi x) { return doppelr[x]; } /// Backward direction for single ribi's, bitwise-NOT for all others static ribi backward(ribi x) { return backwards[x]; } /// Convert ribi to dir static dir get_dir(ribi x) { return dirs[x]; } #else #ifdef USE_GCC_POPCOUNT static uint8 get_numways(ribi x) { return (__builtin_popcount(x)); } static bool is_twoway(ribi x) { return get_numways(x) == 2; } static bool is_threeway(ribi x) { return get_numways(x) > 2; } static bool is_single(ribi x) { return get_numways(x) == 1; } #else static bool is_twoway(ribi x) { return (0x1668 >> x) & 1; } static bool is_threeway(ribi x) { return (0xE880 >> x) & 1; } static bool is_single(ribi x) { return (0x0116 >> x) & 1; } #endif static bool is_bend(ribi x) { return (0x1248 >> x) & 1; } static bool is_straight(ribi x) { return (0x0536 >> x) & 1; } static bool is_straight_ns(ribi x) { return (0x0032 >> x) & 1; } static bool is_straight_ew(ribi x) { return (0x0504 >> x) & 1; } static ribi doubles(ribi x) { return (INT64_C(0x00000A0A00550A50) >> (x * 4)) & 0x0F; } static ribi backward(ribi x) { return (INT64_C(0x01234A628951C84F) >> (x * 4)) & 0x0F; } /// Convert ribi to dir static dir get_dir(ribi x) { return (INT64_C(0x0002007103006540) >> (x * 4)) & 0x7; } #endif /** * Same as backward, but for single directions only. * Effectively does bit rotation. Avoids lookup table backwards. * @returns backward(x) for single ribis, 0 for x==0. */ static inline ribi reverse_single(ribi x) { return ((x | x<<4) >> 2) & 0xf; } /// Rotate 90 degrees to the right. Does bit rotation. static ribi rotate90(ribi x) { return ((x | x<<4) >> 3) & 0xf; } /// Rotate 90 degrees to the left. Does bit rotation. static ribi rotate90l(ribi x) { return ((x | x<<4) >> 1) & 0xf; } static ribi rotate45(ribi x) { return (is_single(x) ? x|rotate90(x) : x&rotate90(x)); } // 45 to the right static ribi rotate45l(ribi x) { return (is_single(x) ? x|rotate90l(x) : x&rotate90l(x)); } // 45 to the left }; /** * Calculate slope from directions. * Go upward on the slope: going north translates to slope_t::south. */ slope_t::type slope_type(koord dir); /** * Calculate slope from directions. * Go upward on the slope: going north translates to slope_t::south. */ slope_t::type slope_type(ribi_t::ribi); /** * Check if the slope is upwards, relative to the direction @p from. * @returns 1 for single upwards and 2 for double upwards */ sint16 get_sloping_upwards(const slope_t::type slope, const ribi_t::ribi from); /** * Calculate direction bit from coordinate differences. */ ribi_t::ribi ribi_typ_intern(sint16 dx, sint16 dy); /** * Calculate direction bit from direction. */ ribi_t::ribi ribi_type(const koord& dir); ribi_t::ribi ribi_type(const koord3d& dir); /** * Calculate direction bit from slope. * Note: slope_t::north (slope north) will be translated to ribi_t::south (direction south). */ ribi_t::ribi ribi_type(slope_t::type slope); /** * Calculate direction bit for travel from @p from to @p to. */ template ribi_t::ribi ribi_type(const K1&from, const K2& to) { return ribi_typ_intern(to.x - from.x, to.y - from.y); } #endif simutrans-124.5/src/simutrans/dataobj/route.cc000066400000000000000000000511041520675460500214520ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../world/simworld.h" #include "../simintr.h" #include "../simhalt.h" #include "../obj/way/weg.h" #include "../ground/grund.h" #include "../ground/wasser.h" #include "../dataobj/marker.h" #include "../vehicle/simtestdriver.h" #include "loadsave.h" #include "route.h" #include "environment.h" #include"../utils/simrandom.h" // define USE_VALGRIND_MEMCHECK to make // valgrind aware of the memory pool for A* nodes #ifdef USE_VALGRIND_MEMCHECK #include #endif // if defined, print some profiling informations into the file //#define DEBUG_ROUTES // binary heap, the fastest #include "../tpl/binary_heap_tpl.h" #ifdef DEBUG_ROUTES #include "../sys/simsys.h" #endif void route_t::append(const route_t *r) { assert(r != NULL); const uint32 hops = r->get_count()-1; route.reserve(hops+1+route.get_count()); while (!route.empty() && back() == r->front()) { // skip identical end tiles route.pop_back(); } // then append for( unsigned int i=0; i<=hops; i++ ) { route.append(r->at(i)); } } void route_t::insert(koord3d k) { route.insert_at(0,k); } void route_t::remove_koord_from(uint32 i) { while( i+1 < get_count() ) { route.pop_back(); } } /** * Appends a straight line from the last koord3d in route to the desired target. * Will return false if failed */ bool route_t::append_straight_route(karte_t *welt, koord3d dest ) { const koord ziel=dest.get_2d(); if( !welt->is_within_limits(ziel) ) { return false; } // then try to calculate direct route koord pos = back().get_2d(); route.reserve( route.get_count()+koord_distance(pos,ziel)+2 ); DBG_MESSAGE("route_t::append_straight_route()","start from (%i,%i) to (%i,%i)",pos.x,pos.y,dest.x,dest.y); while(pos!=ziel) { // shortest way if(abs(pos.x-ziel.x)>=abs(pos.y-ziel.y)) { pos.x += (pos.x>ziel.x) ? -1 : 1; } else { pos.y += (pos.y>ziel.y) ? -1 : 1; } if(!welt->is_within_limits(pos)) { break; } route.append(welt->lookup_kartenboden(pos)->get_pos()); } DBG_MESSAGE("route_t::append_straight_route()","to (%i,%i) found.",ziel.x,ziel.y); return pos==ziel; } // node arrays route_t::ANode* route_t::nodes=NULL; uint32 route_t::MAX_STEP=0; #ifdef DEBUG bool route_t::node_in_use=false; #endif /** * find the route to an unknown location * * @param welt */ bool route_t::find_route(karte_t *welt, const koord3d start, test_driver_t *tdriver, const uint32 max_khm, uint8 start_dir, uint32 max_depth ) { bool ok = false; // check for existing koordinates const grund_t* g = welt->lookup(start); if( g == NULL ) { return false; } // some thing for the search const waytype_t wegtyp = tdriver->get_waytype(); // memory in static list ... if( nodes == NULL ) { MAX_STEP = welt->get_settings().get_max_route_steps(); nodes = new ANode[MAX_STEP]; } INT_CHECK("route 347"); // we clear it here probably twice: does not hurt ... route.clear(); // first tile is not valid?!? if( !tdriver->check_next_tile(g) ) { return false; } static binary_heap_tpl queue; GET_NODE(); #ifdef USE_VALGRIND_MEMCHECK VALGRIND_MAKE_MEM_UNDEFINED(nodes, sizeof(ANode)*MAX_STEP); #endif uint32 step = 0; ANode* tmp = &nodes[step++]; tmp->parent = NULL; tmp->gr = g; tmp->count = 0; tmp->f = 0; tmp->g = 0; tmp->dir = 0; assert(start_dir == ribi_t::all || ribi_t::is_single(start_dir) ); // start_dir is direction to start with, adjust ribi_from such that bit mask boiles down to start_dir tmp->ribi_from = ribi_t::reverse_single(start_dir) ^ 0x0f; // assert that mask in first step is equal to start_dir assert( (uint8)(~ribi_t::reverse_single(tmp->ribi_from)& 0xf) == start_dir); // nothing in lists marker_t& marker = marker_t::instance(welt->get_size().x, welt->get_size().y); queue.clear(); queue.insert(tmp); bool target_reached = false; do { // this is too expensive to be called each step if((step & 4095) == 0) { INT_CHECK("route 161"); } tmp = queue.pop(); const grund_t* gr = tmp->gr; if( marker.test_and_mark(gr) ) { // we were already here on a faster route, thus ignore this branch // (trading speed against memory consumption) continue; } // tile is marked as visited // already there if( tdriver->is_target( gr, tmp->parent==NULL ? NULL : tmp->parent->gr ) ) { // we added a target to the closed list: check for length target_reached = true; break; } // testing all four possible directions const ribi_t::ribi ribi = tdriver->get_ribi(gr) & ( ~ribi_t::reverse_single(tmp->ribi_from) ); for( int r=0; r<4; r++ ) { // a way goes here, and it is not marked (i.e. in the closed list) grund_t* to = NULL; if( (ribi & ribi_t::nesw[r] )!=0 // do not go backwards && koord_distance(start, gr->get_pos() + koord::nesw[r])get_neighbour(to, wegtyp, ribi_t::nesw[r]) // is connected && !marker.is_marked(to) // not already tested && tdriver->check_next_tile(to) // can be driven on && step < MAX_STEP // do not overrun nodes[] ) { // not in there or taken out => add new ANode* k = &nodes[step++]; k->parent = tmp; k->gr = to; k->count = tmp->count+1; k->f = 0; k->g = tmp->g + tdriver->get_cost(to, to->get_weg(wegtyp), max_khm, ribi_t::nesw[r]); k->ribi_from = ribi_t::nesw[r]; uint8 current_dir = ribi_t::nesw[r]; if(tmp->parent!=NULL) { current_dir |= tmp->ribi_from; if(tmp->dir!=current_dir) { k->g += 3; if(tmp->parent->dir!=tmp->dir && tmp->parent->parent!=NULL) { // discourage 90 degree turns k->g += 10; } else if(ribi_t::is_perpendicular(tmp->dir,current_dir)) { // discourage v turns heavily k->g += 25; } } } k->dir = current_dir; // insert here queue.insert(k); } } } while( !queue.empty() && step < MAX_STEP && queue.get_count() < max_depth ); INT_CHECK("route 194"); // target reached? if(!target_reached || step >= MAX_STEP) { if( step >= MAX_STEP ) { dbg->warning("route_t::find_route()","Too many steps (%i>=max %i) in route (too long/complex)",step,MAX_STEP); } } else { // reached => construct route route.store_at( tmp->count, tmp->gr->get_pos() ); while(tmp != NULL) { route[ tmp->count ] = tmp->gr->get_pos(); tmp = tmp->parent; } ok = !route.empty(); } RELEASE_NODE(); return ok; } ribi_t::ribi *get_next_dirs(const koord3d& gr_pos, const koord3d& ziel) { static ribi_t::ribi next_ribi[4]; if( abs(gr_pos.x-ziel.x)>abs(gr_pos.y-ziel.y) ) { next_ribi[0] = (ziel.x>gr_pos.x) ? ribi_t::east : ribi_t::west; next_ribi[1] = (ziel.y>gr_pos.y) ? ribi_t::south : ribi_t::north; } else { next_ribi[0] = (ziel.y>gr_pos.y) ? ribi_t::south : ribi_t::north; next_ribi[1] = (ziel.x>gr_pos.x) ? ribi_t::east : ribi_t::west; } next_ribi[2] = ribi_t::reverse_single( next_ribi[1] ); next_ribi[3] = ribi_t::reverse_single( next_ribi[0] ); return next_ribi; } bool route_t::intern_calc_route(karte_t *welt, const koord3d ziel, const koord3d start, test_driver_t *tdriver, const sint32 max_speed, const sint32 max_cost) { assert((get_random_mode() & SYNC_STEP_RANDOM) == 0); bool ok = false; // check for existing koordinates const grund_t *gr=welt->lookup(start); if( gr == NULL || welt->lookup(ziel) == NULL) { return false; } // we clear it here probably twice: does not hurt ... route.clear(); // first tile is not valid?!? if( !tdriver->check_next_tile(gr) ) { return false; } // some thing for the search const waytype_t wegtyp = tdriver->get_waytype(); const bool is_airplane = tdriver->get_waytype()==air_wt; const uint32 cost_upslope = tdriver->get_cost_upslope(); /* On water we will try jump point search (jps): * - If going straight do not turn, only if near an obstacle. * - If going diagonally only proceed in the two directions defining the diagonal. * Ideally, no water tile is visited twice. * Needs postprocessing to eliminate unnecessary turns. * * Reference: * Harabor D. and Grastien A. 2011. Online Graph Pruning for Pathfinding on Grid Maps. * In Proceedings of the 25th National Conference on Artificial Intelligence (AAAI), San Francisco, USA. * https://users.cecs.anu.edu.au/~dharabor/data/papers/harabor-grastien-aaai11.pdf */ const bool use_jps = tdriver->get_waytype()==water_wt; bool ziel_erreicht=false; // memory in static list ... if( nodes == NULL ) { MAX_STEP = welt->get_settings().get_max_route_steps(); // may need very much memory => configurable nodes = new ANode[MAX_STEP + 4 + 2]; } INT_CHECK("route 347"); static binary_heap_tpl queue; GET_NODE(); #ifdef USE_VALGRIND_MEMCHECK VALGRIND_MAKE_MEM_UNDEFINED(nodes, sizeof(ANode)*MAX_STEP); #endif uint32 step = 0; ANode* tmp = &nodes[step]; step ++; tmp->parent = NULL; tmp->gr = gr; tmp->f = calc_distance(start,ziel); tmp->g = 0; tmp->dir = 0; tmp->count = 0; tmp->ribi_from = ribi_t::none; tmp->jps_ribi = ribi_t::all; // nothing in lists marker_t& marker = marker_t::instance(welt->get_size().x, welt->get_size().y); // clear the queue (should be empty anyhow) queue.clear(); queue.insert(tmp); ANode* new_top = NULL; uint32 beat=1; do { // this is too expensive to be called each step if((beat++ & 4095) == 0) { INT_CHECK("route 161"); } if (new_top) { // this is not in closed list, no check necessary tmp = new_top; new_top = NULL; gr = tmp->gr; marker.mark(gr); } else { tmp = queue.pop(); gr = tmp->gr; if(marker.test_and_mark(gr)) { // we were already here on a faster route, thus ignore this branch // (trading speed against memory consumption) continue; } } // we took the target pos out of the closed list if( ziel == gr->get_pos() ) { ziel_erreicht = true; break; } uint32 topnode_f = !queue.empty() ? queue.front()->f : max_cost; const ribi_t::ribi way_ribi = tdriver->get_ribi(gr); // testing all four possible directions // mask direction we came from const ribi_t::ribi ribi = way_ribi & ( ~ribi_t::reverse_single(tmp->ribi_from) ) & tmp->jps_ribi; const ribi_t::ribi *next_ribi = get_next_dirs(gr->get_pos(), ziel); for(int r=0; r<4; r++) { // a way in our direction? if( (ribi & next_ribi[r])==0 ) { continue; } grund_t* to = NULL; if(is_airplane) { to = welt->lookup_kartenboden(gr->get_pos().get_2d()+koord(next_ribi[r])); } // a way goes here, and it is not marked (i.e. in the closed list) if((to || gr->get_neighbour(to, wegtyp, next_ribi[r])) && tdriver->check_next_tile(to) && !marker.is_marked(to)) { weg_t *w = to->get_weg(wegtyp); // Do not go on a tile, where a oneway sign forbids going. // This saves time and fixed the bug, that a oneway sign on the final tile was ignored. if (w && w->get_ribi_maske() && ribi_t::reverse_single(next_ribi[r]) == w->get_ribi()) { // there is a signal, and the only direction leaving the next tile // is back to our position continue; } // new values for cost g (without way it is either in the air or in water => no costs) uint32 new_g = tmp->g + (w ? tdriver->get_cost(to, w, max_speed, next_ribi[r]) : 1); // check for curves (usually, one would need the lastlast and the last; // if not there, then we could just take the last uint8 current_dir; if(tmp->parent!=NULL) { current_dir = next_ribi[r] | tmp->ribi_from; if(tmp->dir!=current_dir) { new_g += 3; if(tmp->parent->dir!=tmp->dir && tmp->parent->parent!=NULL) { // discourage 90 degree turns new_g += 10; } else if(ribi_t::is_perpendicular(tmp->dir,current_dir)) { // discourage v turns heavily new_g += 25; } } } else { current_dir = next_ribi[r]; } uint32 dist = calc_distance( to->get_pos(), ziel ); // count how many 45 degree turns are necessary to get to target sint8 turns = 0; if (dist>1) { ribi_t::ribi to_target = ribi_type(to->get_pos(), ziel ); if (to_target && (to_target!=current_dir)) { if (ribi_t::is_single(current_dir) != ribi_t::is_single(to_target)) { to_target = ribi_t::rotate45(to_target); turns ++; } while(to_target!=current_dir) { to_target = ribi_t::rotate90(to_target); turns +=2; } if (turns>4) turns = 8-turns; } } // add 3*turns to the heuristic bound // take height difference into account when calculating distance uint32 costup = 0; if (cost_upslope) { costup = cost_upslope * max(ziel.z - to->get_vmove(next_ribi[r]), 0); } const uint32 new_f = new_g + dist + turns * 3 + costup; if (step >= MAX_STEP) { break; // Do not overrun nodes[] } // add new ANode* k = &nodes[step++]; k->parent = tmp; k->gr = to; k->g = new_g; k->f = new_f; k->dir = current_dir; k->ribi_from = next_ribi[r]; k->count = tmp->count+1; k->jps_ribi = ribi_t::all; if (use_jps && to->is_water()) { // only check previous direction plus directions not available on this tile // if going straight only check straight direction // if going diagonally check both directions that generate this diagonal // also enter all available canals and turn to get around canals if (tmp->parent!=NULL) { k->jps_ribi = ~way_ribi | current_dir | ((wasser_t*)to)->get_canal_ribi(); if (gr->is_water()) { // turn on next tile to enter possible neighbours of canal tiles k->jps_ribi |= ((const wasser_t*)gr)->get_canal_ribi(); } } } if( new_f <= topnode_f ) { // do not put in queue if the new node is the best one topnode_f = new_f; if( new_top ) { queue.insert(new_top); } new_top = k; } else { queue.insert( k ); } } } } while ( (!queue.empty() || new_top) && step < MAX_STEP && tmp->g < max_cost ); #ifdef DEBUG_ROUTES // display marked route //minimap_t::get_instance()->calc_map(); DBG_DEBUG("route_t::intern_calc_route()","steps=%i (max %i) in route, open %i, cost %u (max %u)",step,MAX_STEP,queue.get_count(),tmp->g,max_cost); #endif INT_CHECK("route 194"); // target reached? if(!ziel_erreicht || step >= MAX_STEP || tmp->g >= max_cost || tmp->parent==NULL) { if( step >= MAX_STEP ) { dbg->warning("route_t::intern_calc_route()","Too many steps (%i>=max %i) in route (too long/complex)",step,MAX_STEP); } } else { #ifdef DEBUG sint32 best = tmp->g; #endif // reached => construct route route.store_at( tmp->count, tmp->gr->get_pos() ); while(tmp != NULL) { #ifdef DEBUG // debug heuristics if (tmp->f > best) { uint32 dist = calc_distance( tmp->gr->get_pos(), ziel); dbg->warning("route_t::intern_calc_route()", "Problem with heuristic: from %s to %s at %s, best = %d, cost = %d, heur = %d, dist = %d, turns = %d", start.get_str(), ziel.get_fullstr(), tmp->gr->get_pos().get_2d().get_str(), best, tmp->g, tmp->f, dist, tmp->f - tmp->g - dist); } #endif route[ tmp->count ] = tmp->gr->get_pos(); tmp = tmp->parent; } if (use_jps && tdriver->get_waytype()==water_wt) { postprocess_water_route(welt); } ok = true; } RELEASE_NODE(); return ok; } /* * Postprocess routes created by jump-point search. * These routes never turn when going straight. * So something like this can happen: * * >--+ * +--+ * +--> * This method tries to eliminate extra turns to make routes look more like * * >----+ * ++ * +--> */ void route_t::postprocess_water_route(karte_t *welt) { if (route.get_count() < 5) return; // direction of last straight part (and last index of straight part) ribi_t::ribi straight_ribi = ribi_type(route[0], route[1]); uint32 straight_end = 0; // search for route parts: // straight - diagonal - straight (same direction as first straight part) - diagonal // phase 0 1 2 <- postprocess after next change to diagonal uint8 phase = 0; uint32 i = 1; while( i < route.get_count()-1 ) { ribi_t::ribi ribi = ribi_type(route[i-1], route[i+1]); if (ribi_t::is_single(ribi)) { if (ribi == straight_ribi) { if (phase == 1) { // third part starts phase = 2; } else { if (phase == 0) { // still on first part straight_end = i; } } } else { // straight direction different than before - start anew phase = 0; straight_end = i; straight_ribi = ribi; } } else { if (phase < 1) { // second phase phase = 1; } else if (phase == 2) { // fourth phase // postprocess here bool ok = ribi_type(route[straight_end], route[i+1]) == ribi; // try to find straight route, which avoids one diagonal part koord3d_vector_t post; post.append( route[straight_end] ); koord3d &end = route[i]; for(uint32 j = straight_end; j < i && ok; j++) { ribi_t::ribi next = 0; koord diff = (end - post.back()).get_2d(); if (abs(diff.x)>=abs(diff.y)) { next = diff.x > 0 ? ribi_t::east : ribi_t::west; if (abs(diff.x)==abs(diff.y) && next == straight_ribi) { next = diff.y > 0 ? ribi_t::south : ribi_t::north; } } else { next = diff.y > 0 ? ribi_t::south : ribi_t::north; } koord3d pos = post.back() + koord(next); ok = false; if (grund_t *gr = welt->lookup(pos)) { if (gr->is_water()) { ok = true; post.append(pos); } } } // now substitute the new route part into the route if (ok) { for(uint32 j = straight_end; j < i && ok; j++) { route[j] = post[j-straight_end]; } // start again with the first straight part i = straight_end; } else { // set second straight part to be the first straight_end = i-1; } // start new search phase = 0; } } i++; } } /** * searches route, uses intern_calc_route() for distance between stations * handles only driving in stations by itself */ route_t::route_result_t route_t::calc_route(karte_t *welt, const koord3d ziel, const koord3d start, test_driver_t *tdriver, const sint32 max_khm, sint32 max_len ) { route.clear(); INT_CHECK("route 336"); #ifdef DEBUG_ROUTES const uint32 ms = dr_time(); #endif bool ok = intern_calc_route(welt, start, ziel, tdriver, max_khm, INT32_MAX); #ifdef DEBUG_ROUTES if(tdriver->get_waytype()==water_wt) { DBG_DEBUG("route_t::calc_route()", "route from %d,%d to %d,%d with %i steps in %u ms found.", start.x, start.y, ziel.x, ziel.y, route.get_count()-1, dr_time()-ms ); } #endif INT_CHECK("route 343"); if( !ok ) { DBG_MESSAGE("route_t::calc_route()","No route from %d,%d to %d,%d found",start.x, start.y, ziel.x, ziel.y); // no route found route.reserve(1); route.append(start); // just to be safe return no_route; } // advance so all convoi fits into a halt (only set for trains and cars) else if( max_len>1 ) { // we need a halt of course ... halthandle_t halt = welt->lookup(start)->get_halt(); if( halt.is_bound() ) { // first: find out how many tiles I am already in the station for( size_t i = route.get_count(); i-- != 0 && max_len != 0 && halt == haltestelle_t::get_halt(route[i], NULL, false); --max_len) { } // and now go forward, if possible if( max_len>0 ) { const uint32 max_n = route.get_count()-1; const koord3d zv = route[max_n] - route[max_n - 1]; const int ribi = ribi_type(zv); grund_t *gr = welt->lookup(start); const waytype_t wegtyp = tdriver->get_waytype(); ribi_t::ribi way_ribi = tdriver->get_ribi(gr); while( max_len>0 && ((way_ribi & ribi) != 0) && gr->get_neighbour(gr,wegtyp,ribi) && gr->get_halt()==halt && tdriver->check_next_tile(gr)) { // Do not go on a tile, where a oneway sign forbids going. // This saves time and fixed the bug, that a oneway sign on the final tile was ignored. ribi_t::ribi go_dir=gr->get_weg(wegtyp)->get_ribi_maske(); if( (ribi&go_dir)!=0 ) { break; } route.append(gr->get_pos()); max_len--; way_ribi = tdriver->get_ribi(gr); } // station too short => warning! if( max_len>0 ) { return valid_route_halt_too_short; } } } } return valid_route; } void route_t::rdwr(loadsave_t *file) { xml_tag_t r( file, "route_t" ); sint32 max_n = route.get_count()-1; file->rdwr_long(max_n); if(file->is_loading()) { koord3d k; route.clear(); route.reserve(max_n+2); for(sint32 i=0; i<=max_n; i++ ) { k.rdwr(file); route.append(k); } } else { // writing for(sint32 i=0; i<=max_n; i++) { route[i].rdwr(file); } } } simutrans-124.5/src/simutrans/dataobj/route.h000066400000000000000000000075401520675460500213210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_ROUTE_H #define DATAOBJ_ROUTE_H #include "../simdebug.h" #include "../dataobj/koord3d.h" #include "../tpl/vector_tpl.h" class karte_t; class test_driver_t; class grund_t; /** * Route, e.g. for vehicles */ class route_t { public: typedef uint16 index_t; static const index_t INVALID_INDEX = 0xFFFA; private: /** * The actual route search */ bool intern_calc_route(karte_t *w, koord3d start, koord3d ziel, test_driver_t *tdriver, const sint32 max_kmh, const sint32 max_cost); koord3d_vector_t route; // The coordinates for the vehicle route void postprocess_water_route(karte_t *welt); static inline uint32 calc_distance( const koord3d &p1, const koord3d &target ) { return koord_distance(p1, target); } public: enum route_result_t { no_route = 0, valid_route = 1, valid_route_halt_too_short = 3 }; /** * Nodes for A* or breadth-first search */ class ANode { public: ANode * parent; const grund_t* gr; sint32 f; ///< heuristic for cost to reach target sint32 g; ///< cost to reach this tile uint8 dir; ///< driving direction uint8 ribi_from; ///< we came from this direction uint16 count; ///< length of route up to here uint8 jps_ribi; ///< extra ribi mask for jump-point search /// sort nodes first with respect to f, then with respect to g inline bool operator <= (const ANode &k) const { return f==k.f ? g<=k.g : f<=k.f; } }; static ANode *nodes; static uint32 MAX_STEP; #ifdef DEBUG // a semaphore, since we only have a single version of the array in memory static bool node_in_use; static void GET_NODE() {if(node_in_use){ dbg->fatal("GET_NODE","called while list in use");} node_in_use =1; } static void RELEASE_NODE() {if(!node_in_use){ dbg->fatal("RELEASE_NODE","called while list free");} node_in_use =0; } #else static void GET_NODE() {} static void RELEASE_NODE() {} #endif const koord3d_vector_t &get_route() const { return route; } void rotate90( sint16 y_size ) { route.rotate90( y_size ); } bool is_contained(const koord3d &k) const { return route.is_contained(k); } uint32 index_of(const koord3d &k) const { return (uint32)(route.index_of(k)); } /** * @return Coordinate at index @p n. */ const koord3d& at(const uint16 n) const { return route[n]; } const koord3d& operator [](const uint16 n) const { return route[n]; } koord3d const& front() const { return route.front(); } koord3d const& back() const { return route.back(); } uint32 get_count() const { return route.get_count(); } bool empty() const { return route.get_count()<2; } /** * Appends the other route to ours. */ void append(const route_t *route); /** * Inserts @p k at position 0. */ void insert(koord3d k); /** * Appends position @p k. */ inline void append(koord3d k) { route.append(k); } /** * removes all tiles from the route */ void clear() { route.clear(); } /** * Removes all tiles at indices >@p i. */ void remove_koord_from(uint32 i); /** * Appends a straight line to the @p target. * Will return fals if fails */ bool append_straight_route( karte_t *w, koord3d target); /** * Finds route to a location, where @p tdriver->is_target becomes true. * * @param start * @param tdriver * @param max_khm * @param start_dir * @param max_depth is the maximum length of a route */ bool find_route(karte_t *w, const koord3d start, test_driver_t *tdriver, const uint32 max_khm, uint8 start_dir, uint32 max_depth ); /** * Calculates the route from @p start to @p target */ route_result_t calc_route(karte_t *welt, koord3d start, koord3d target, test_driver_t *tdriver, const sint32 max_speed_kmh, sint32 max_tile_len ); /** * Load/Save of the route. */ void rdwr(loadsave_t *file); }; #endif simutrans-124.5/src/simutrans/dataobj/scenario.cc000066400000000000000000001026051520675460500221220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../sys/simsys.h" #include "../simconst.h" #include "../simtypes.h" #include "../simdebug.h" #include "../player/simplay.h" #include "../world/simworld.h" #include "../simmesg.h" #include "../simmem.h" #include "../tool/simmenu.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../network/network.h" #include "../network/network_cmd_scenario.h" #include "../dataobj/schedule.h" #include "../utils/cbuffer.h" // error popup #include "../gui/simwin.h" #include "../gui/scenario_info.h" // scripting #include "../script/script.h" #include "../script/script_loader.h" #include "../script/api/api.h" #include "../script/api_param.h" #include "../script/api_class.h" #include "../script/api/api_simple.h" #include "../tpl/plainstringhashtable_tpl.h" #include "scenario.h" #include const int LOCAL_CACHE_TIME = 750; const int NETWORK_CACHE_TIME = 10000; // cache the scenario text files static plainstringhashtable_tpl cached_text_files; scenario_t::scenario_t(karte_t *w) : description_text("get_short_description"), info_text("get_info_text"), goal_text("get_goal_text"), rule_text("get_rule_text"), result_text("get_result_text"), about_text("get_about_text"), debug_text("get_debug_text") { welt = w; what_scenario = 0; script = NULL; won = false; lost = false; rdwr_error = false; need_toolbar_update = false; cached_text_files.clear(); } scenario_t::~scenario_t() { delete script; clear_rules(); cached_text_files.clear(); } const char* scenario_t::init( const char *scenario_base, const char *scenario_name_, karte_t *welt ) { this->welt = welt; scenario_name = scenario_name_; // path to scenario files cbuffer_t buf; buf.printf("%s%s/", scenario_base, scenario_name_); scenario_path = buf; // scenario script file buf.append("scenario.nut"); if (!load_script( buf )) { dbg->warning("scenario_t::init", "could not load script file %s", (const char*)buf); return "Loading scenario script failed"; } const char *err = NULL; plainstring mapfile; // load savegame if ((err = script->call_function(script_vm_t::FORCE, "get_map_file", mapfile))) { dbg->warning("scenario_t::init", "error [%s] calling get_map_file", err); return "No scenario map specified"; } // if savegame-string == "" then do not load a savegame, just attach to running game. if ( strcmp(mapfile, "") ) { // savegame location buf.clear(); buf.printf("%s%s/%s", scenario_base, scenario_name_, mapfile.c_str()); if (!welt->load( buf )) { dbg->warning("scenario_t::init", "error loading savegame %s", err, (const char*)buf); return "Could not load scenario map!"; } // set savegame name buf.clear(); buf.printf("%s.sve", scenario_name.c_str()); welt->get_settings().set_filename( strdup(buf) ); welt->type_of_generation = karte_t::SCENARIO_WORLD; // re-initialize coordinate and rotation handling script_api::coordinate_transform_t::initialize(); } script_loader_t::load_compatibility_script(script); // load translations translator::load_files_from_folder( scenario_path.c_str(), "scenario" ); cached_text_files.clear(); what_scenario = SCRIPTED; dynamic_string::CACHE_TIME = LOCAL_CACHE_TIME; // callback script->register_callback(&scenario_t::set_completion, "scenario_t_set_completed"); // register ourselves welt->set_scenario(this); welt->get_message()->clear(); welt->get_chat_message()->clear(); // set start time sint32 const time = welt->get_current_month(); welt->get_settings().set_starting_year( time / 12); welt->get_settings().set_starting_month( time % 12); // set my player number to PLAYER_UNOWNED script->set_my_player(PLAYER_UNOWNED); // now call startup function if ((err = script->call_function(script_vm_t::QUEUE, "start"))) { dbg->warning("scenario_t::init", "error [%s] calling start", err); } return NULL; } bool scenario_t::load_script(const char* filename) { delete script; // start vm script = script_loader_t::start_vm("scenario_base.nut", "script-scenario.log", scenario_path.c_str(), true); if (script == NULL) { return false; } // init strings dynamic_string::init(script); // register callback if (env_t::server) { nwc_scenario_t::init(script); } // load scenario definition if (const char* err = script->call_script(filename,2000000)) { dbg->error("scenario_t::load_script", "error [%s] calling %s", err, filename); return false; } return true; } void scenario_t::koord_sq2w(koord &k) const { script_api::coordinate_transform_t::koord_sq2w(k); } const char* scenario_t::get_forbidden_text() { static cbuffer_t buf; buf.clear(); buf.append("

Forbidden stuff:


"); for(uint pnr=0; pnr"); } } return buf; } sint32 scenario_t::forbidden_t::diff(const forbidden_t& other) const { sint32 diff = (sint32)type - (sint32)other.type; if (diff == 0) { diff = (sint32)toolnr - (sint32)other.toolnr; if (diff == 0) { // trick is waytype invalid is at end => finding also all previous waytypes first diff = (sint32)waytype - (sint32)other.waytype; if (diff == 0) { // trick is emopty hash is at end => finding also all previous hashes first diff = (sint32)parameter_hash - (sint32)other.parameter_hash; if (diff == 0 && type == forbidden_t::allow_tool_rect) { diff = pos_nw.x - other.pos_nw.x; if (diff == 0) { diff = pos_nw.y - other.pos_nw.y; if (diff == 0) { diff = (pos_nw.x - pos_se.x) * (pos_nw.y - pos_se.y) - (other.pos_nw.x - other.pos_se.x) * (other.pos_nw.y - other.pos_se.y); } } } } } } return diff; } bool scenario_t::forbidden_t::operator <(const forbidden_t &other) const { return diff(other) < 0; } bool scenario_t::forbidden_t::operator ==(const forbidden_t &other) const { bool eq = diff(other)==0; if (eq && type==forbid_tool_rect) { eq = eq && (hmin == other.hmin) && (hmax == other.hmax); eq = eq && (pos_nw == other.pos_nw); eq = eq && (pos_se == other.pos_se); } return eq; } scenario_t::forbidden_t::forbidden_t(const forbidden_t& other) : type(other.type), toolnr(other.toolnr), waytype(other.waytype), parameter_hash(other.parameter_hash), pos_nw(other.pos_nw), pos_se(other.pos_se), hmin(other.hmin), hmax(other.hmax), error(other.error) { } void scenario_t::forbidden_t::rotate90(const sint16 y_size) { switch(type) { case forbid_tool_rect: { pos_nw.rotate90(y_size); pos_se.rotate90(y_size); sint16 x = pos_nw.x; pos_nw.x = pos_se.x; pos_se.x = x; } default: ; } } uint32 scenario_t::find_first(const forbidden_t &other, uint player_nr) const { if (forbidden_tools[player_nr].empty() || *forbidden_tools[player_nr].back() < other) { // empty vector, or everything is smaller return forbidden_tools[player_nr].get_count(); } if (other < *forbidden_tools[player_nr][0]) { // everything is larger return forbidden_tools[player_nr].get_count(); } else if ( other <= *forbidden_tools[player_nr][0] ) { return 0; } // now: low < other <= high uint32 low = 0, high = forbidden_tools[player_nr].get_count()-1; while(low+1 < high) { uint32 mid = (low+high) / 2; if (*forbidden_tools[player_nr][mid] < other) { low = mid; // now low < other } else { high = mid; // now other <= high } }; // still: low < other <= high return high; } // only match type and toolnumber and return frist match (other could follow) uint32 scenario_t::find_first_type_tool_wt(const forbidden_t& other, uint player_nr) const { if (forbidden_tools[player_nr].empty()) { // empty return 0; } if(forbidden_tools[player_nr].back()->type < other.type || (forbidden_tools[player_nr].back()->type == other.type && forbidden_tools[player_nr].back()->toolnr < other.toolnr) ) { // everything is smaller return forbidden_tools[player_nr].get_count(); } if (forbidden_tools[player_nr][0]->type > other.type || (forbidden_tools[player_nr][0]->type == other.type && forbidden_tools[player_nr][0]->toolnr > other.toolnr)) { // everything is smaller return forbidden_tools[player_nr].get_count(); } else if(forbidden_tools[player_nr][0]->type == other.type && forbidden_tools[player_nr][0]->toolnr == other.toolnr ) { // first is matching return 0; } // now binary search: low < other <= high uint32 low = 0, high = forbidden_tools[player_nr].get_count() - 1; uint32 mid = high; while (low + 1 < high) { mid = (low + high) / 2; sint32 result = forbidden_tools[player_nr][mid]->diff(other); if(result<0) { low = mid; // now low < other } else if(result>0) { high = mid; // now other <= high } else { // exact match return mid; } }; // did we find something? if (forbidden_tools[player_nr][high]->toolnr == other.toolnr && forbidden_tools[player_nr][high]->type == other.type) { return high; } return forbidden_tools[player_nr].get_count(); } void scenario_t::intern_forbid(forbidden_t* test, uint player_nr, bool add_rule) { bool changed = false; forbidden_t::forbid_type type = test->type; if (test->waytype < 0) { test->waytype = 0; } bool current_add = add_rule; for (int i=0; i<1+add_rule; i++) { if(add_rule && type!=forbidden_t::forbid_tool) { if (type == forbidden_t::allow_tool_rect) { // before adding an allow rule, remove a identical forbid rule test->type = (i == 0) ? forbidden_t::forbid_tool_rect : forbidden_t::allow_tool_rect; } else if (type == forbidden_t::forbid_tool_rect) { // before adding a forbind rule, remove a identical allowed rule test->type = (i == 0) ? forbidden_t::allow_tool_rect : forbidden_t::forbid_tool_rect; } current_add = i; // first pass remove, next pass add } for (uint32 i = find_first(*test, player_nr); i < forbidden_tools[player_nr].get_count() && *forbidden_tools[player_nr][i] <= *test; i++) { if (*test == *forbidden_tools[player_nr][i]) { // entry exists already delete test; if (!current_add) { delete forbidden_tools[player_nr][i]; forbidden_tools[player_nr].remove_at(i); changed = true; } goto end; } } } // entry does not exist if (add_rule) { forbidden_tools[player_nr].insert_ordered(test, scenario_t::forbidden_t::compare); changed = true; } end: if (changed && type==forbidden_t::forbid_tool) { need_toolbar_update = true; } } void scenario_t::call_forbid_tool(forbidden_t *test, uint player_nr, bool forbid) { if (env_t::server) { // send information over network nwc_scenario_rules_t *nws = new nwc_scenario_rules_t(welt->get_sync_steps() + 1, welt->get_map_counter()); nws->rule = test; nws->forbid = forbid; nws->player_nr = player_nr; network_send_all(nws, false); } else { // directly apply intern_forbid(test, player_nr, forbid); } } void scenario_t::forbid_tool(uint8 player_nr, uint16 tool_id) { forbidden_t *test = new forbidden_t(forbidden_t::forbid_tool, tool_id, ignore_wt); call_forbid_tool(test, player_nr, true); } void scenario_t::clear_forbid_tool(uint8 player_nr, uint16 tool_id) { forbidden_t *test = new forbidden_t(forbidden_t::forbid_tool, tool_id, ignore_wt); call_forbid_tool(test, player_nr, false); } void scenario_t::forbid_way_tool(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param) { forbidden_t *test = new forbidden_t(forbidden_t::forbid_tool, tool_id, wt, param); call_forbid_tool(test, player_nr, true); } void scenario_t::clear_forbid_way_tool(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param) { forbidden_t *test = new forbidden_t(forbidden_t::forbid_tool, tool_id, wt, param); call_forbid_tool(test, player_nr, false); } void scenario_t::forbid_way_tool_rect(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord pos_nw, koord pos_se, plainstring err) { forbid_way_tool_cube(player_nr, tool_id, wt, param, koord3d(pos_nw, -128), koord3d(pos_se, 127), err); } void scenario_t::clear_way_tool_rect(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord pos_nw, koord pos_se, bool allow) { clear_way_tool_cube(player_nr, tool_id, wt, param, koord3d(pos_nw, -128), koord3d(pos_se, 127), allow); } void scenario_t::allow_way_tool_rect(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord pos_nw, koord pos_se) { allow_way_tool_cube(player_nr, tool_id, wt, param, koord3d(pos_nw, -128), koord3d(pos_se, 127)); } void scenario_t::forbid_way_tool_cube(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord3d pos_nw_0, koord3d pos_se_0, plainstring err) { koord pos_nw( min(pos_nw_0.x, pos_se_0.x), min(pos_nw_0.y, pos_se_0.y)); koord pos_se( max(pos_nw_0.x, pos_se_0.x), max(pos_nw_0.y, pos_se_0.y)); sint8 hmin( min(pos_nw_0.z, pos_se_0.z) ); sint8 hmax( max(pos_nw_0.z, pos_se_0.z) ); forbidden_t *test = new forbidden_t(tool_id, wt, param, pos_nw, pos_se, hmin, hmax); test->error = err; call_forbid_tool(test, player_nr, true); } void scenario_t::clear_way_tool_cube(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord3d pos_nw_0, koord3d pos_se_0, bool allow) { koord pos_nw(min(pos_nw_0.x, pos_se_0.x), min(pos_nw_0.y, pos_se_0.y)); koord pos_se(max(pos_nw_0.x, pos_se_0.x), max(pos_nw_0.y, pos_se_0.y)); sint8 hmin(min(pos_nw_0.z, pos_se_0.z)); sint8 hmax(max(pos_nw_0.z, pos_se_0.z)); forbidden_t* test = new forbidden_t(tool_id, wt, param, pos_nw, pos_se, hmin, hmax); test->type = allow ? forbidden_t::allow_tool_rect : forbidden_t::forbid_tool_rect; call_forbid_tool(test, player_nr, false); } void scenario_t::allow_way_tool_cube(uint8 player_nr, uint16 tool_id, waytype_t wt, const char *param, koord3d pos_nw_0, koord3d pos_se_0) { koord pos_nw( min(pos_nw_0.x, pos_se_0.x), min(pos_nw_0.y, pos_se_0.y)); koord pos_se( max(pos_nw_0.x, pos_se_0.x), max(pos_nw_0.y, pos_se_0.y)); sint8 hmin( min(pos_nw_0.z, pos_se_0.z) ); sint8 hmax( max(pos_nw_0.z, pos_se_0.z) ); forbidden_t *test = new forbidden_t(tool_id, wt, param, pos_nw, pos_se, hmin, hmax); test->type = forbidden_t::allow_tool_rect; call_forbid_tool(test, player_nr, true); } void scenario_t::clear_rules() { for (uint pnr = 0; pnr < MAX_PLAYER_COUNT; pnr++) { clear_ptr_vector(forbidden_tools[pnr]); } need_toolbar_update = true; } // intern helper function, searches with wildcards sint32 scenario_t::matching_rule(const uint8 player_nr, const forbidden_t &test, koord3d pos ) const { if (!forbidden_tools[player_nr].empty()) { forbidden_t test_wildcard = test; test_wildcard.parameter_hash = 0; // to find any hash test_wildcard.waytype = ignore_wt; // find if there is a a wildcard matchiung tool for (uint32 i = find_first_type_tool_wt(test_wildcard, player_nr); i < forbidden_tools[player_nr].get_count(); i++) { // there is something, we need to test more forbidden_t const& f = *forbidden_tools[player_nr][i]; if (f.type != test.type || f.toolnr != test.toolnr || f.waytype > test.waytype) { // reached end of forbidden tools with this id => done break; } if (f.waytype == ignore_wt || f.waytype == test.waytype) { if (f.parameter_hash == forbidden_t::EMPTY_HASH || f.parameter_hash == test.parameter_hash) { // parameter matches too => forbidden if (f.type == forbidden_t::forbid_tool) { return i; // matching rule found } // need to match retangle if (f.pos_nw.x <= pos.x && f.pos_nw.y <= pos.y && pos.x <= f.pos_se.x && pos.y <= f.pos_se.y) { // check height if (f.hmin <= pos.z && pos.z <= f.hmax) { return i; } } } } } } return -1; } void scenario_t::clear_player_rules(uint8 player_nr) { clear_ptr_vector(forbidden_tools[player_nr]); need_toolbar_update = true; } bool scenario_t::is_tool_allowed(const player_t* player, uint16 tool_id, sint16 wt, const char* param) { if (what_scenario != SCRIPTED && what_scenario != SCRIPTED_NETWORK) { return true; } // first test the list uint8 player_nr = player ? player->get_player_nr() : PLAYER_UNOWNED; forbidden_t test(forbidden_t::forbid_tool, tool_id, wt, param); sint32 idx = matching_rule(player_nr, test, koord3d::invalid); if (idx == -1 && player_nr != PLAYER_UNOWNED) { // retry as public player player_nr = PLAYER_UNOWNED; idx = matching_rule(player_nr, test, koord3d::invalid); } if (idx >= 0) { // we found a forbidden rule const char* err = forbidden_tools[player_nr][idx]->error.c_str(); if (err == NULL) { err = ""; } return false; } // then call script if available if (what_scenario == SCRIPTED) { bool ok = true; const char* err = script->call_function(script_vm_t::FORCE, "is_tool_allowed", ok, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), tool_id, wt, param); return err != NULL || ok; } return true; } const char* scenario_t::is_work_allowed_here(const player_t* player, uint16 tool_id, sint16 wt, const char* param, koord3d pos) { if (what_scenario != SCRIPTED && what_scenario != SCRIPTED_NETWORK) { return NULL; } // first test for allowed tools uint8 player_nr = player ? player->get_player_nr() : PLAYER_UNOWNED; forbidden_t test(forbidden_t::allow_tool_rect, tool_id, wt, param); sint32 idx = matching_rule(player_nr, test, pos); if (idx == -1 && player_nr != PLAYER_UNOWNED) { // retry as public player player_nr = PLAYER_UNOWNED; idx = matching_rule(player_nr, test, pos); } if (idx == -1) { // not allowed => test for forbidden area player_nr = player ? player->get_player_nr() : PLAYER_UNOWNED; test.type = forbidden_t::forbid_tool; idx = matching_rule(player_nr, test, koord3d::invalid); if (idx == -1 && player_nr != PLAYER_UNOWNED) { // retry as public player player_nr = PLAYER_UNOWNED; idx = matching_rule(player_nr, test, koord3d::invalid); } if (idx >= 0) { // we found a forbidden rule const char* err = forbidden_tools[player_nr][idx]->error.c_str(); if (err == NULL) { err = ""; } return err; } // not found => test rectangles if (idx == -1) { // not found player_nr = player ? player->get_player_nr() : PLAYER_UNOWNED; test.type = forbidden_t::forbid_tool_rect; idx = matching_rule(player_nr, test, pos); if (idx == -1 && player_nr != PLAYER_UNOWNED) { // retry as public player player_nr = PLAYER_UNOWNED; idx = matching_rule(player_nr, test, pos); } if (idx >= 0) { // we found a forbidden rule const char* err = forbidden_tools[player_nr][idx]->error.c_str(); if (err == NULL) { err = ""; } return err; } } } // then call the script // cannot be done for two_click_tool_t's as they depend on routefinding, // which is done per client if (what_scenario == SCRIPTED) { koord3d start_pos = pos; bool is_drag_tool = false; bool is_ctrl = false; bool is_shift = false; uint8 player_nr = player ? player->get_player_nr() : PLAYER_UNOWNED; if(player_nr != PLAYER_UNOWNED){ if (two_click_tool_t *two_tool = dynamic_cast(welt->get_tool(player_nr))) { start_pos = two_tool->get_start_pos(); is_drag_tool = !two_tool->is_first_click(); is_ctrl = two_tool->is_ctrl_pressed(); is_shift = two_tool->is_shift_pressed(); } else if (tool_t *tool = dynamic_cast(welt->get_tool(player_nr))) { is_ctrl = tool->is_ctrl_pressed(); is_shift = tool->is_shift_pressed(); } } static plainstring msg; const char *err = script->call_function(script_vm_t::FORCE, "is_work_allowed_here", msg, player_nr, tool_id, param, pos, script_api::mytool_data_t(start_pos, is_drag_tool, is_ctrl, is_shift)); return err == NULL ? msg.c_str() : NULL; } return NULL; } const char* scenario_t::is_schedule_allowed(const player_t* player, const schedule_t* schedule) { // sanity checks if (schedule == NULL) { return ""; } if (env_t::server) { // networkgame: allowed return NULL; } // call script if (what_scenario == SCRIPTED) { static plainstring msg; const char *err = script->call_function(script_vm_t::FORCE, "is_schedule_allowed", msg, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), schedule); return err == NULL ? msg.c_str() : NULL; } return NULL; } const char* scenario_t::is_convoy_allowed(const player_t* player, convoihandle_t cnv, depot_t* depot) { // sanity checks if (!cnv.is_bound() || depot == NULL) { return ""; } if (env_t::server) { // networkgame: allowed return NULL; } // call script if (what_scenario == SCRIPTED) { static plainstring msg; const char *err = script->call_function(script_vm_t::FORCE, "is_convoy_allowed", msg, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), cnv, (obj_t*)depot); return err == NULL ? msg.c_str() : NULL; } return NULL; } bool scenario_t::is_tool_enabled(const player_t * player, uint16 tool_id, sint16 wt, const char *param) { if (what_scenario != SCRIPTED && what_scenario != SCRIPTED_NETWORK) { return true; } // then call script if available if (what_scenario == SCRIPTED) { bool ok = true; const char* err = script->call_function(script_vm_t::FORCE, "is_tool_active", ok, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), tool_id, wt, param); return err != NULL || ok; } return true; } const char* scenario_t::jump_to_link_executed(koord3d pos) { if (env_t::server) { // networkgame: allowed return NULL; } // call script if (what_scenario == SCRIPTED) { static plainstring msg; const char *err = script->call_function(script_vm_t::FORCE, "jump_to_link_executed", msg, pos); return err == NULL ? msg.c_str() : NULL; } return NULL; } const char* scenario_t::get_error_text() { if (script) { return script->get_error(); } return NULL; } void scenario_t::step() { if (!script) { // update texts at clients if info window open if (env_t::networkmode && !env_t::server ) { // update texts if (scenario_info_t* win = (scenario_info_t*)win_get_magic(magic_scenario_info)) { update_scenario_texts(win->get_open_tab()); } else if (win_get_magic(magic_scenario_info)) { update_scenario_texts(DESCRIPTION); } } return; } uint16 new_won = 0; uint16 new_lost = 0; // first check, whether win/loss state of any player changed for(uint32 i=0; iget_player(i); uint16 mask = 1 << i; // player exists and has not won/lost yet if (player && (((won | lost) & mask)==0)) { sint32 percentage = 0; // callback script->prepare_callback("scenario_t_set_completed", 2, i, percentage ); // call script const char *err = script->call_function(script_vm_t::QUEUE, "is_scenario_completed", percentage, i); // clear callback script->clear_pending_callback(); // script might have deleted the player player = welt->get_player(i); if (player == NULL) { continue; } // call completed? if (script_vm_t::is_call_suspended(err)) { continue; } player->set_scenario_completion(percentage); // won ? if (percentage >= 100) { new_won |= mask; } // lost ? else if (percentage < 0) { new_lost |= mask; } } } update_won_lost(new_won, new_lost); // update texts if (scenario_info_t *win = (scenario_info_t*)win_get_magic(magic_scenario_info) ) { update_scenario_texts(win->get_open_tab()); } else if (win_get_magic(magic_scenario_info)) { update_scenario_texts(DESCRIPTION); } // update toolbars if necessary if (need_toolbar_update) { tool_t::update_toolbars(); need_toolbar_update = false; // reset active tool if now forbidden // check scenario conditions for all players for(uint8 player_nr = 0; player_nr < PLAYER_UNOWNED; player_nr++) { if (player_t *player = welt->get_player(player_nr)) { tool_t *tool = welt->get_tool(player_nr); if (!is_tool_allowed(player, tool->get_id(), tool->get_waytype(), tool->get_default_param())) { welt->local_set_tool(tool_t::general_tool[TOOL_QUERY], player); } } } } } void scenario_t::new_month() { if (script) { script->call_function(script_vm_t::QUEUE, "new_month"); } } void scenario_t::new_year() { if (script) { script->call_function(script_vm_t::QUEUE, "new_year"); } } void scenario_t::update_won_lost(uint16 new_won, uint16 new_lost) { // server sends the new state to the clients if (env_t::server && (new_won | new_lost)) { nwc_scenario_t *nwc = new nwc_scenario_t(); nwc->won = new_won; nwc->lost = new_lost; nwc->what = nwc_scenario_t::UPDATE_WON_LOST; network_send_all(nwc, true); } // we are the champions if (new_won) { won |= new_won; // those are the losers new_lost = ~new_won; } if (new_lost) { lost |= new_lost; } // notify active player if ( (new_won|new_lost) & (1<get_active_player_nr()) ) { // most likely result text has changed, force update result_text.update(script, welt->get_active_player(), true); open_info_win(); } } void scenario_t::update_scenario_texts(int what) { player_t* player = welt->get_active_player(); switch (what) { case INFO: info_text.update(script, player); return; case GOAL: goal_text.update(script, player); return; case RULE: rule_text.update(script, player); return; case RESULT: result_text.update(script, player); return; case ABOUT: about_text.update(script, player); return; case SCRIPT_DEBUG: debug_text.update(script, player); return; case DESCRIPTION: description_text.update(script, player); return; case ALL: info_text.update(script, player); goal_text.update(script, player); rule_text.update(script, player); result_text.update(script, player); about_text.update(script, player); debug_text.update(script, player); description_text.update(script, player); return; } } plainstring scenario_t::load_language_file(const char* filename) { if (filename == NULL) { return "(null)"; } std::string path = scenario_path.c_str(); // try user language std::string wanted_file = path + translator::get_lang()->iso + PATH_SEPARATOR + filename; const plainstring& cached = cached_text_files.get(wanted_file.c_str()); if (cached != NULL) { // file already cached return cached; } // not cached: try to read file FILE* file = dr_fopen(wanted_file.c_str(), "rb"); if (file == NULL) { // try English file = dr_fopen((path + "en" + PATH_SEPARATOR + filename).c_str(), "rb"); } if (file == NULL) { // try scenario directory file = dr_fopen((path + filename).c_str(), "rb"); } plainstring text = ""; if (file) { fseek(file,0,SEEK_END); long len = ftell(file); if(len>0) { char* const buf = MALLOCN(char, len + 1); fseek(file,0,SEEK_SET); if (fread(buf, 1, len, file) == (size_t)len) { buf[len] = '\0'; text = buf; } free(buf); } fclose(file); } // store text to cache cached_text_files.put(wanted_file.c_str(), text); return text; } bool scenario_t::open_info_win(const char* tab) const { // pop up for the win scenario_info_t *si = (scenario_info_t*)win_get_magic(magic_scenario_info); if (si == NULL) { si = new scenario_info_t(); if (create_win(si, w_info, magic_scenario_info) < 0) { // failed return false; } } si->open_tab(tab); return true; // dummy return value } void scenario_t::rdwr(loadsave_t *file) { file->rdwr_short(what_scenario); if (file->is_version_less(111, 5)) { uint32 city_nr = 0; file->rdwr_long(city_nr); sint64 factor = 0; file->rdwr_longlong(factor); koord k(0,0); k.rdwr( file ); } if (what_scenario != SCRIPTED && what_scenario != SCRIPTED_NETWORK) { if (file->is_loading()) { what_scenario = 0; } return; } script_api::coordinate_transform_t::rdwr(file); file->rdwr_short(won); file->rdwr_short(lost); file->rdwr_str(scenario_name); // load scripts and scenario files if (what_scenario == SCRIPTED) { if (file->is_loading()) { // load persistent scenario data plainstring str; file->rdwr_str(str); // forbid system execution (with manipulated savegames) in data string const char* s = strstr(str, "system"); if (s && (s == str || s[-1] <= ' ')) { s += 6; while (*s && *s++ <= ' ') {} if (*s == '(') { dbg->fatal("scenario_t::rdwr", "Manipulated savegame data contained \"system\" call: %s", str.c_str()); // we could actually continue by ignoring the script what_scenario = 0; script = NULL; str = ""; } } dbg->warning("scenario_t::rdwr", "loaded persistent scenario data: %s", str.c_str()); if (env_t::networkmode && !env_t::server) { // client playing network scenario game: // script files are not available what_scenario = SCRIPTED_NETWORK; script = NULL; } else { // load script cbuffer_t script_filename; // assume error rdwr_error = true; // try addon directory first if (env_t::default_settings.get_with_private_paks()) { scenario_path = ( std::string("addons/") + env_t::pak_name + "scenario" + PATH_SEPARATOR + scenario_name.c_str() + "/").c_str(); script_filename.printf("%sscenario.nut", scenario_path.c_str()); rdwr_error = !load_script(script_filename); } // failed, try scenario from pakset directory if (rdwr_error) { scenario_path = (env_t::pak_dir + "scenario/" + scenario_name.c_str() + PATH_SEPARATOR).c_str(); script_filename.clear(); script_filename.printf("%sscenario.nut", scenario_path.c_str()); rdwr_error = !load_script(script_filename); } if (!rdwr_error) { script_loader_t::load_compatibility_script(script); // restore persistent data const char* err = script->eval_string(str); if (err) { dbg->warning("scenario_t::rdwr", "error [%s] evaluating persistent scenario data", err); rdwr_error = true; } // load translations translator::load_files_from_folder( scenario_path.c_str(), "scenario" ); // callback script->register_callback(&scenario_t::set_completion, "scenario_t_set_completed"); } else { dbg->warning("scenario_t::rdwr", "could not load script file %s", (const char*)script_filename); } } } else { plainstring str; script->call_function(script_vm_t::FORCEX, "save", str); dbg->warning("scenario_t::rdwr", "write persistent scenario data: %s", str.c_str()); file->rdwr_str(str); } } // load forbidden tool if (file->is_loading()) { clear_rules(); } for (uint pnr = 0; pnr < MAX_PLAYER_COUNT; pnr++) { uint32 count = forbidden_tools[pnr].get_count(); file->rdwr_long(count); for (uint32 i = 0; i < count; i++) { if (file->is_loading()) { forbidden_tools[pnr].append(new forbidden_t()); } forbidden_tools[pnr][i]->rdwr(file); } } // cached strings if (file->is_version_atleast(120, 3)) { dynamic_string::rdwr_cache(file); } if (what_scenario == SCRIPTED && file->is_loading() && !rdwr_error) { const char* err = script->call_function(script_vm_t::FORCEX, "resume_game"); if (err) { dbg->warning("scenario_t::rdwr", "error [%s] calling resume_game", err); rdwr_error = true; } } // client side of scripted game but not on a client if ( (what_scenario == SCRIPTED_NETWORK) ^ (env_t::networkmode && env_t::server==0) ) { what_scenario = 0; rdwr_error = true; } dynamic_string::CACHE_TIME = what_scenario == SCRIPTED ? LOCAL_CACHE_TIME : NETWORK_CACHE_TIME; } void scenario_t::rotate90(const sint16 y_size) { for (uint pnr = 0; pnr < MAX_PLAYER_COUNT; pnr++) { for (uint32 i = 0; i < forbidden_tools[pnr].get_count(); i++) { forbidden_tools[pnr][i]->rotate90(y_size); } } } // return percentage completed sint32 scenario_t::get_completion(int player_nr) { if ( what_scenario == 0 || player_nr < 0 || player_nr >= PLAYER_UNOWNED) { return 0; } // check if won / lost uint32 pl = player_nr; if (won & (1<get_player(player_nr); if ( what_scenario == SCRIPTED ) { // take cached value if (player) { percentage = player->get_scenario_completion(); } } else if ( what_scenario == SCRIPTED_NETWORK ) { cbuffer_t buf; buf.printf("is_scenario_completed(%d)", pl); const char *ret = dynamic_string::fetch_result((const char*)buf, NULL, NULL); percentage = ret ? atoi(ret) : 0; // cache value if (player) { player->set_scenario_completion(percentage); } } return min( 100, percentage); } bool scenario_t::set_completion(sint32 player_nr, sint32 percentage) { if ( player_nr < 0 || player_nr >= PLAYER_UNOWNED) { return false; } player_t *player = welt->get_player(player_nr); if (player == NULL) { return false; } player->set_scenario_completion(percentage - player->get_scenario_completion()); uint16 mask = 1 << player_nr; // check won/lost if (percentage < 0 && (lost & mask)==0) { update_won_lost(0, mask); } else if (percentage >= 100 && (won & mask)==0) { update_won_lost(mask, 0); } return true; } const char* scenario_t::eval_string(const char* squirrel_string) const { if (what_scenario == SCRIPTED) { return script->eval_string(squirrel_string); } return ""; } simutrans-124.5/src/simutrans/dataobj/scenario.h000066400000000000000000000406621520675460500217700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_SCENARIO_H #define DATAOBJ_SCENARIO_H /** @file scenario.h declarations for scenario interface */ #include "koord3d.h" #include "../utils/plainstring.h" #include "../utils/simstring.h" #include "../script/dynamic_string.h" #include "../dataobj/ribi.h" #include "../convoihandle.h" #include "../tool/simmenu.h" class loadsave_t; class stadt_t; class fabrik_t; class karte_t; class schedule_t; class depot_t; /** * @class scenario_t * Controls scenarios in connection to a simutrans world. * * Scenarios are scripted. In network games, only the server has access to the script, * clients will be sent some results of the script. * * Each instance of karte_t carries a non-NULL pointer to a scenario_t * thus also the need for inactive scenarios. */ class scenario_t { private: /// possible states of scenario enum scenario_state_t { INACTIVE = 0, ///< scenario inactive SCRIPTED = 7, ///< scenario active (non-network game or at server) SCRIPTED_NETWORK = 8 ///< scenario active, network game at client }; /// state of the current scenario @see scenario_state_t uint16 what_scenario; /// the world we are scripting in karte_t *welt; /// name of scenario, files are searched in scenario_path/scenario_name/... /// e.g. my_scenario plainstring scenario_name; /// path to scenario directory (relative to env_t::user_dir) /// e.g. pak/scenario/my_scenario/ plainstring scenario_path; /** * loads scenario file with the given name * @param filename name scenario script file (including .nut extension) */ bool load_script(const char* filename); /// is set, if an error occurred during loading of savegame /// e.g. re-starting of scenario failed due to script error bool rdwr_error; /// pointer to virtual machine script_vm_t *script; /// @{ /// @name Interface to forbid tools in-game /** * Struct to store information about forbidden tools * * Necessary in network games: there, the list of forbidden tools * is transferred to clients. Needed to apply conditions to e.g. way-building * tools or to have toolbars reflect allowed tools. */ struct forbidden_t { static const uint32 EMPTY_HASH=0; enum forbid_type { forbid_tool = 1, allow_tool_rect = 2, forbid_tool_rect = 3 }; forbid_type type; /// id of tool to be forbidden, as set by constructors of classes derived from /// tool_t, @see tool/simtool.h uint16 toolnr; /// waytype of tool, @see waytype_t sint16 waytype; uint32 parameter_hash; koord pos_nw, pos_se; sint8 hmin, hmax; /// error message to be displayed if user tries to work with the tool plainstring error; /// constructor: forbid tool/etc for a certain player forbidden_t(forbid_type type_=forbid_tool, uint16 toolnr_=0, sint16 waytype_= ignore_wt, const char *param_=NULL) : type(type_), toolnr(toolnr_), waytype(waytype_ < 0 ? (sint16)ignore_wt : waytype_), pos_nw(koord::invalid), pos_se(koord::invalid), hmin(-128), hmax(127), error() { if (toolnr == (GENERAL_TOOL|TOOL_SCHEDULE_INS) || toolnr == (GENERAL_TOOL | TOOL_SCHEDULE_ADD)) { // paramter is pointer to binary => not checking parameter_hash = 0; return; } parameter_hash = string_to_hash(param_)&0x7FFFFFFul; } /// constructor: forbid tool for a certain player at certain locations (and heights) forbidden_t(uint16 toolnr_, sint16 waytype_, const char *param_, koord nw, koord se, sint8 hmin_=-128, sint8 hmax_=127) : type(forbid_tool_rect), toolnr(toolnr_), waytype(waytype_ < 0 ? (sint16)ignore_wt : waytype_), pos_nw(nw), pos_se(se), hmin(hmin_), hmax(hmax_), error() { parameter_hash = string_to_hash(param_) & 0x7FFFFFFul; } // copy constructor forbidden_t(const forbidden_t&); /** * @returns difference */ sint32 diff(const forbidden_t&) const; /** * @returns if this < other, compares: type, playernr, tool, wt, parameter * DIRTY: (a <= b) && (b <= a) DOES NOT imply a == b */ bool operator <(const forbidden_t &) const; bool operator <=(const forbidden_t &other) const { return !(other < *this); } static bool compare(const forbidden_t *a, const forbidden_t *b) { return a->diff(*b) < 0; } /** * compares everything (including coordinates) * DIRTY: (a <= b) && (b <= a) DOES NOT imply a == b */ bool operator ==(const forbidden_t &other) const; /** * templated load/save support */ template void rdwr(T *file) { uint8 t = (uint8)type; file->rdwr_byte(t); type= (forbid_type)t; file->rdwr_short(toolnr); file->rdwr_short(waytype); file->rdwr_long(parameter_hash); file->rdwr_short(pos_nw.x); file->rdwr_short(pos_nw.y); file->rdwr_short(pos_se.x); file->rdwr_short(pos_se.y); file->rdwr_byte(hmin); file->rdwr_byte(hmax); file->rdwr_str(error); } void rotate90(const sint16 y_size); private: const forbidden_t& operator=(const forbidden_t&); }; /// list of forbidden tools for each player (last is all players) vector_tplforbidden_tools[MAX_PLAYER_COUNT]; /// set to true if rules changed to update toolbars, /// toolbars and active tools will be updated in next call to step() bool need_toolbar_update; /** * helper function: * * @param other given record and the player_nr to test for (or PLAYER_UNOWNED) * @param player_nr player * @returns first index i such that * forbidden_tools[i-1] < other <= forbidden_tools[i] <= other * or returns forbidden_tools.get_count() if no such index is found */ uint32 find_first(const forbidden_t &other, uint player_nr) const; /** * helper function: * * @param other given record and the player_nr to test for (or PLAYER_UNOWNED) * @param player_nr player * @returns first index i such that * that the type, toolnumber, and waytype matches (but parameter may be wrong) * or returns forbidden_tools.get_count() if no such index is found */ uint32 find_first_type_tool_wt(const forbidden_t& other, uint player_nr) const; /** * Helper function: * Puts/removes new record into/from forbidden_tools list, checks for identical entries. * Only call this method from call_forbid_tool(forbidden_t *,bool) * * @param test must be pointer to allocated memory, will be invalid after call * @param player_nr player * @param add_rule if true add rule, if false removes rule from list * @returns value 1 if added rule, and 2 deleted previous rule, return 0 on error */ void intern_forbid(forbidden_t *test, uint player_nr, bool add_rule); /** * Helper function: works on forbidden_tools directly (if not in network-mode) * or sends information over network (if at server) * * @param test must be pointer to allocated memory, will be invalid after call * @param player_nr player * @param forbid if true forbids, if false allows the record */ void call_forbid_tool(forbidden_t *test, uint player_nr, bool forbid); /// @} // internal function, returns the idx of the first matching rule of 0xFFFFFF sint32 matching_rule(const uint8 player, const forbidden_t& test, koord3d pos) const; /// bit set if player has won / lost uint16 won; uint16 lost; /// function to update the won / lost bitset /// called if this information changes for some players void update_won_lost(uint16 new_won, uint16 new_lost); public: scenario_t(karte_t *w); ~scenario_t(); /** * Initializes scripted scenario */ const char* init( const char *scenario_base, const char *scenario_name, karte_t *welt ); /** * Load file with translations. Tries to load files in the following order * (1) script_addon_path/iso/filename * (2) script_addon_path/en/filename * (3) script_path/iso/filename * * Here, iso refers to iso-abbreviation of currently active language * @return content of loaded file */ plainstring load_language_file(const char* filename); /// Load/save support void rdwr(loadsave_t *file); /// @returns true if loading succeed, false if script failed during loading bool rdwr_ok() const { return !rdwr_error; } /** * Stop scenario */ void stop() { what_scenario = INACTIVE; } /// @return true if a scenario is present bool active() const { return what_scenario != INACTIVE; } /// @return true if scenario is scripted bool is_scripted() const { return what_scenario == SCRIPTED || what_scenario == SCRIPTED_NETWORK; } /// @return true if scenario is scripted and local bool is_local() const { return what_scenario == SCRIPTED; } /** * compiles and executes given string * @returns error msg (or NULL if succeeded) */ const char* eval_string(const char* squirrel_string) const; /** * Get percentage of scenario completion. Does not call script to update this value. * On clients: call server for update via dynamic_string logic. * Returns percentage of scenario completion. * @param player_nr player * @returns percentage of scenario completion: * if >= 100 then scenario is won * if < 0 then scenario is lost */ sint32 get_completion(int player_nr); /** * Sets percentage of scenario completion. Used as callback if script call got suspended. * * @param player_nr player * @param percentage * @returns dummy return value */ bool set_completion(sint32 player_nr, sint32 percentage); void rotate90(const sint16 y_size); /** * rotate original coordinates to actual world coordinates * uses the methods in script_api */ void koord_sq2w(koord &) const; /** * Text to be displayed in the finance info window * i.e. short description of scenario */ dynamic_string description_text; /// @{ /// @name Text to be displayed in the scenario info window dynamic_string info_text; dynamic_string goal_text; dynamic_string rule_text; dynamic_string result_text; dynamic_string about_text; dynamic_string debug_text; /// @} enum {INFO=0, GOAL, RULE, RESULT, ABOUT, SCRIPT_DEBUG, DESCRIPTION, ALL=-1}; /** * Called to update the scenario texts * @see dynamic_string::update */ void update_scenario_texts(int which); /** * opens scenario info window at tab @p tab. */ bool open_info_win(const char* tab = "result") const; /** * Last error of script */ const char* get_error_text(); /** * Calls scripted is_scenario_completed. Caches this value in statistics of player_t. * Server sends update of won/lost if necessary. */ void step(); /** * Called upon month change: at 0:00 of the first day of the new month. */ void new_month(); /** * Called upon new year: at 0:00 January 1st. */ void new_year(); /// @{ /// @name Interface to forbid tools in-game /** * Forbid tool * @ingroup squirrel-scen-api * * @param player_nr number of player this rule applies to, * if this is set to MAX_PLAYER_COUNT then this acts for all players except public player * @param tool_id id of tool */ void forbid_tool(uint8 player_nr, uint16 tool_id); /** * @ingroup squirrel-scen-api * @see forbid_tool */ void clear_forbid_tool(uint8 player_nr, uint16 tool_id); /** * Forbid tool with certain waytype * @ingroup squirrel-scen-api * * @param player_nr number of player this rule applies to, * if this is set to MAX_PLAYER_COUNT then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param param */ void forbid_way_tool(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param); /** * Forbid tool with certain waytype within rectangular region on the map * @ingroup squirrel-scen-api * * @param player_nr number of player this rule applies to, * if this is set to MAX_PLAYER_COUNT then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param param * @param pos_nw coordinate of north-western corner of rectangle * @param pos_se coordinate of south-eastern corner of rectangle * @param err error message presented to user when trying to apply this tool */ void forbid_way_tool_rect(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord pos_nw, koord pos_se, plainstring err); /** * Forbid tool with certain waytype within cubic region on the map. * @ingroup squirrel-scen-api * * @param player_nr number of player this rule applies to, * if this is set to MAX_PLAYER_COUNT then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param param * @param pos_nw coordinate of north-western corner of cube * @param pos_se coordinate of south-eastern corner of cube * @param err error message presented to user when trying to apply this tool */ void forbid_way_tool_cube(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord3d pos_nw, koord3d pos_se, plainstring err); /** * @ingroup squirrel-scen-api * @see forbid_way_tool */ void clear_forbid_way_tool(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param); void clear_way_tool_rect(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord pos_nw_0, koord pos_se_0, bool allow); /** * clear rule with certain waytype within cubic region on the map. * @ingroup squirrel-scen-api * * @param player_nr number of player this rule applies to, * if this is set to MAX_PLAYER_COUNT then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param param * @param pos_nw_0 coordinate of north-western corner of cube * @param pos_se_0 coordinate of south-eastern corner of cube * @param allow clear and allow rule (true) or clear a forbid rule (false) */ void clear_way_tool_cube(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord3d pos_nw_0, koord3d pos_se_0, bool allow); /** * Clears all rules. * @ingroup squirrel-scen-api */ void clear_rules(); /** * Clears all rules for a player selec * @ingroup squirrel-scen-api */ void clear_player_rules(uint8 player_nr); /** * @ingroup squirrel-scen-api * @see forbid_way_tool_rect */ void allow_way_tool_rect(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord pos_nw, koord pos_se); /** * @ingroup squirrel-scen-api * @see forbid_way_tool_cube */ void allow_way_tool_cube(uint8 player_nr, uint16 tool_id, waytype_t wt, const char* param, koord3d pos_nw, koord3d pos_se); /** * Toolbars/active tools need an update due to changed rules; update is done in step(). * @ingroup squirrel-scen-api */ void gui_needs_update() { need_toolbar_update = true; } /** * Checks if player can use this tool at all. * Called for instance in karte_t::local_set_tool to change active tool or when filling toolbars. * @return true if player can use this tool. */ bool is_tool_allowed(const player_t* player, uint16 tool_id, sint16 wt = ignore_wt, const char *param=0); /** * Checks if player can use the tool at this position. * @return NULL if allowed otherwise error message */ const char* is_work_allowed_here(const player_t* player, uint16 tool_id, sint16 wt, const char *param, koord3d pos); /** * Checks if player can use this schedule. * * @param player player * @param schedule the schedule * * @return null if allowed, an error message otherwise */ const char* is_schedule_allowed(const player_t* player, const schedule_t* schedule); /** * Checks if player can use this convoy. * Called when player wants to start convoy at depot. * * @param player player * @param cnv convoy * @param depot depot * * @return null if allowed, an error message otherwise */ const char* is_convoy_allowed(const player_t* player, convoihandle_t cnv, depot_t* depot); /** * Checks if this tool is currently available to the player * Checks if player can use this tool at all. * Called for instance in karte_t::local_set_tool to change active tool or when filling toolbars. * @return true if player can use this tool and false (and the tool is greyed out). */ bool is_tool_enabled(const player_t* player, uint16 tool_id, sint16 wt, const char *param); /** * Called when player click link in scenario windows, after position changed. * * @param pos coordinate go to in link * * @return an error message otherwise or null */ const char* jump_to_link_executed(koord3d pos); /// @return debug dump of forbidden tools const char* get_forbidden_text(); /// @} friend class nwc_scenario_t; ///< to access vm, update_won_lost() friend class nwc_scenario_rules_t; ///< to access forbidden_tool stuff }; #endif simutrans-124.5/src/simutrans/dataobj/schedule.cc000066400000000000000000000356641520675460500221250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../gui/simwin.h" #include "../simtypes.h" #include "../world/simworld.h" #include "../simhalt.h" #include "../display/simimg.h" #include "../utils/cbuffer.h" #include "../utils/simrandom.h" #include "../gui/messagebox.h" #include "../descriptor/building_desc.h" #include "../ground/grund.h" #include "../obj/gebaeude.h" #include "../player/simplay.h" #include "../obj/depot.h" #include "loadsave.h" #include "translator.h" #include "schedule.h" #include "../tpl/slist_tpl.h" schedule_entry_t schedule_t::dummy_entry(koord3d::invalid, 0, 0); // copy all entries from schedule src to this and adjusts current_stop void schedule_t::copy_from(const schedule_t *src) { // make sure, we can access both if( src==NULL ) { dbg->fatal("schedule_t::copy_to()","cannot copy from NULL"); } entries.clear(); for(schedule_entry_t const& i : src->entries) { entries.append(i); } set_current_stop( src->get_current_stop() ); editing_finished = src->is_editing_finished(); } bool schedule_t::is_stop_allowed(const grund_t *gr) const { // first: check, if we can go here waytype_t const my_waytype = get_waytype(); bool ok = gr->hat_weg(my_waytype); if( !ok ) { if( my_waytype==air_wt ) { // everywhere is ok but not on stops (we have to load at airports only ...) ok = !gr->get_halt().is_bound(); } else if( my_waytype==water_wt && gr->get_typ()==grund_t::wasser ) { ok = true; } else if( my_waytype==tram_wt ) { // tram rails are track internally ok = gr->hat_weg(track_wt); } } if( ok ) { // ok, we can go here; but we must also check, that we are not entering a foreign depot depot_t *dp = gr->get_depot(); ok &= (dp==NULL || (int)dp->get_tile()->get_desc()->get_extra()==my_waytype); } return ok; } /* returns a valid halthandle if there is a next halt in the schedule; * it may however not be allowed to load there, if the owner mismatches! */ halthandle_t schedule_t::get_next_halt( player_t *player, halthandle_t halt ) const { if( entries.get_count()>1 ) { for( uint i=1; i < entries.get_count(); i++ ) { halthandle_t h = haltestelle_t::get_halt( entries[ (current_stop+i) % entries.get_count() ].pos, player, get_waytype()==water_wt ); if( h.is_bound() && h != halt ) { return h; } } } return halthandle_t(); } /* returns a valid halthandle if there is a previous halt in the schedule; * it may however not be allowed to load there, if the owner mismatches! */ halthandle_t schedule_t::get_prev_halt( player_t *player ) const { if( entries.get_count()>1 ) { for( uint i=1; i < entries.get_count()-1u; i++ ) { halthandle_t h = haltestelle_t::get_halt( entries[ (current_stop+entries.get_count()-i) % entries.get_count() ].pos, player, get_waytype()==water_wt ); if( h.is_bound() ) { return h; } } } return halthandle_t(); } bool schedule_t::insert(const grund_t* gr, uint8 minimum_loading, uint16 waiting_time ) { // too many stops or wrong kind of stop if (entries.get_count()>=254 || !is_stop_allowed(gr)) { return false; } entries.insert_at(current_stop, schedule_entry_t(gr->get_pos(), minimum_loading, waiting_time)); current_stop ++; make_current_stop_valid(); return true; } bool schedule_t::append(const grund_t* gr, uint8 minimum_loading, uint16 waiting_time) { // too many stops or wrong kind of stop if (entries.get_count()>=254 || !is_stop_allowed(gr)) { return false; } entries.append(schedule_entry_t(gr->get_pos(), minimum_loading, waiting_time), 4); return true; } // cleanup a schedule void schedule_t::make_valid() { remove_double_entries(); if( entries.get_count() == 1 ) { // schedules with one entry not allowed entries.clear(); } make_current_stop_valid(); } void schedule_t::remove_double_entries() { if( entries.get_count() < 2 ) { return; // nothing to check } // first and last must not be the same! koord3d lastpos = entries.back().pos; // now we have to check all entries ... for( uint8 i=0; i delete_enty ) { current_stop--; } entries.remove_at(delete_enty); make_current_stop_valid(); } void schedule_t::move_entry_forward( uint8 cur ) { if( entries.get_count() <= 1 ) { return; } // not last entry if( cur < entries.get_count()-1 ) { // just append everything entries.insert_at( cur+2, entries[ cur ] ); entries.remove_at( cur ); } else { // last entry, just append everything entries.insert_at( 0, entries[cur] ); entries.remove_at( cur+1 ); } // if cur was not at end of list then cur and other changed places uint8 other = (cur + entries.get_count() + 1 ) % entries.get_count(); if (cur == entries.get_count()-1) { // all entries moved down one index current_stop = (current_stop + 1 + entries.get_count()) % entries.get_count(); } else if (current_stop == other) { current_stop = cur; } else if (current_stop == cur) { current_stop = other; } } void schedule_t::move_entry_backward( uint8 cur ) { if( entries.get_count() <= 1 ) { return; } if( cur==0 ) { //first entry entries.append( entries[0] ); entries.remove_at( 0 ); } else { // now move all to new position afterwards entries.insert_at( cur-1, entries[ cur ] ); entries.remove_at( cur+1 ); } // if cur was not at start of list then cur and other changed places uint8 other = (cur + entries.get_count() - 1 ) % entries.get_count(); if (cur == 0) { // all entries moved up one index current_stop = (current_stop - 1 + entries.get_count()) % entries.get_count(); } else if (current_stop == other) { current_stop = cur; } else if (current_stop == cur) { current_stop = other; } } void schedule_t::rdwr(loadsave_t *file) { xml_tag_t f( file, "fahrplan_t" ); assert(!file->is_loading() || entries.empty()); make_current_stop_valid(); uint8 size = entries.get_count(); if( file->is_version_less(101, 1) ) { uint32 dummy=current_stop; file->rdwr_long(dummy); current_stop = (uint8)dummy; sint32 maxi=size; file->rdwr_long(maxi); DBG_MESSAGE("schedule_t::rdwr()","read schedule %p with %i entries",this,maxi); if(file->is_version_less(86, 10)) { // old array had different maxi-counter maxi ++; } size = (uint8)max(0,maxi); } else { file->rdwr_byte(current_stop); file->rdwr_byte(size); } entries.resize(size); if(file->is_version_less(99, 12)) { for( uint8 i=0; irdwr_long(dummy); entries.append(schedule_entry_t(pos, (uint8)dummy, 0)); } } else { // loading/saving new version for( uint8 i=0; irdwr_byte(entries[i].minimum_loading); if(file->is_version_atleast(99, 18)) { if( file->is_version_atleast( 122, 1 ) ) { file->rdwr_short(entries[i].waiting_time); } else if(file->is_loading()) { uint8 wl=0; file->rdwr_byte(wl); if( entries[i].minimum_loading <= 100 ) { if( wl > 0 ) { // old value: maximum waiting time in 1/2^(16-n) parts of a month entries[ i ].waiting_time = 65535u / (1 << (16u - wl)); } } else if( entries[i].minimum_loading > 100 ) { // hack to store absolute departure times in old games entries[ i ].waiting_time = wl << 8; } } else { uint8 wl = entries[ i ].waiting_time >> 8; // loosing precision, but what can we do ... if( entries[ i ].minimum_loading <= 100 && entries[ i ].waiting_time > 0 ) { wl = log2( (uint32)entries[ i ].waiting_time )+1; } file->rdwr_byte(wl); } } } } if(file->is_loading()) { editing_finished = true; } if(current_stop>=entries.get_count() ) { if (!entries.empty()) { dbg->error("schedule_t::rdwr()","current_stop %i >count %i => current_stop = 0", current_stop, entries.get_count() ); } current_stop = 0; } } void schedule_t::rotate90( sint16 y_size ) { // now we have to rotate all entries ... for(schedule_entry_t & i : entries) { i.pos.rotate90(y_size); } } /** * compare this schedule (schedule) with another, passed in schedule */ bool schedule_t::matches(karte_t *welt, const schedule_t *schedule) { if( schedule == NULL ) { return false; } // same pointer => equal! if( this==schedule ) { return true; } // no match for empty schedules if( schedule->entries.empty() || entries.empty() ) { return false; } // now we have to check all entries ... // we need to do this that complicated, because the last stop may make the difference uint16 f1=0, f2=0; while( f1+f2entries.get_count() ) { if(f1entries.get_count() && schedule->entries[(uint8)f2].pos == entries[(uint8)f1].pos) { // minimum_loading/waiting ignored: identical f1++; f2++; } else { bool ok = false; if( f1lookup(entries[(uint8)f1].pos); if( gr1 && gr1->get_depot() ) { // skip depot f1++; ok = true; } } if( f2entries.get_count() ) { grund_t *gr2 = welt->lookup(schedule->entries[(uint8)f2].pos); if( gr2 && gr2->get_depot() ) { ok = true; f2++; } } // no depot but different => do not match! if( !ok ) { /* in principle we could also check for same halt; but this is dangerous, * since a rebuilding of a single square might change that */ return false; } } } return f1==entries.get_count() && f2==schedule->entries.get_count(); } /** * Ordering based on halt id */ class HaltIdOrdering { public: bool operator()(const halthandle_t& a, const halthandle_t& b) const { return a.get_id() < b.get_id(); } }; /** * compare this schedule (schedule) with another, ignoring order and exact positions and waypoints */ bool schedule_t::similar( const schedule_t *schedule, const player_t *player ) { if( schedule == NULL ) { return false; } // same pointer => equal! if( this == schedule ) { return true; } // unequal count => not equal const uint8 min_count = min( schedule->entries.get_count(), entries.get_count() ); if( min_count == 0 ) { return false; } // now we have to check all entries: So we add all stops to a vector we will iterate over vector_tpl halts; bool water = get_waytype() == water_wt; for( uint8 idx = 0; idx < this->entries.get_count(); idx++ ) { koord3d p = this->entries[idx].pos; halthandle_t halt = haltestelle_t::get_halt( p, player, water); if( halt.is_bound() ) { halts.insert_unique_ordered( halt, HaltIdOrdering() ); } } vector_tpl other_halts; for( uint8 idx = 0; idx < schedule->entries.get_count(); idx++ ) { koord3d p = schedule->entries[idx].pos; halthandle_t halt = haltestelle_t::get_halt( p, player, water); if( halt.is_bound() ) { other_halts.insert_unique_ordered( halt, HaltIdOrdering() ); } } // now compare them if( other_halts.get_count() != halts.get_count() ) { return false; } // number of unique halt similar => compare them now for( uint32 idx = 0; idx < halts.get_count(); idx++ ) { if( halts[idx] != other_halts[idx] ) { return false; } } return true; } void schedule_t::add_return_way(bool append_mirror) { if( append_mirror ) { // add mirror entries if( entries.get_count()<127 && entries.get_count()>1 ) { for( uint8 maxi=entries.get_count()-2; maxi>0; maxi-- ) { entries.append(entries[maxi]); } } } else { // invert if( entries.get_count()>1 ) { for( uint8 i=0; ierror( "schedule_t::sscanf_schedule()","incomplete entry termination!" ); return false; } p++; // then schedule type int type = atoi( p ); // .. check for correct type if( type != (int)get_type()) { dbg->error( "schedule_t::sscanf_schedule()","schedule has wrong type (%d)! should have been %d.", type, get_type() ); return false; } while( *p && *p!='|' ) { p++; } if( *p!='|' ) { dbg->error( "schedule_t::sscanf_schedule()","incomplete entry termination!" ); return false; } p++; // now scan the entries while( *p>0 ) { sint16 values[5]; for( sint8 i=0; i<5; i++ ) { values[i] = atoi( p ); while( *p && (*p!=',' && *p!='|') ) { p++; } if( i<4 && *p!=',' ) { dbg->error( "schedule_t::sscanf_schedule()","incomplete string!" ); return false; } if( i==4 && *p!='|' ) { dbg->error( "schedule_t::sscanf_schedule()","incomplete entry termination!" ); return false; } p++; } // ok, now we have a complete entry entries.append(schedule_entry_t(koord3d(values[0], values[1], (sint8)values[2]), (uint8)values[3], (uint16)values[4])); } make_valid(); return true; } void schedule_t::gimme_stop_name(cbuffer_t& buf, karte_t* welt, player_t const* const player_, bool is_water, schedule_entry_t const& entry, int const max_chars) { const char *p; halthandle_t halt = haltestelle_t::get_halt(entry.pos, player_, is_water); if(halt.is_bound()) { p = halt->get_name(); } else { const grund_t* gr = welt->lookup(entry.pos); if(gr==NULL) { p = translator::translate("Invalid coordinate"); } else if(gr->get_depot() != NULL) { p = translator::translate("Depot"); } else { p = translator::translate("Wegpunkt"); } } // finally append if(max_chars > 0 && strlen(p)>(unsigned)max_chars) { buf.printf("%.*s...", max_chars - 3, p); } else { buf.append(p); } // position (when no length restriction) if (max_chars <= 0) { buf.printf(" (%s)", entry.pos.get_str()); } } simutrans-124.5/src/simutrans/dataobj/schedule.h000066400000000000000000000216071520675460500217570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_SCHEDULE_H #define DATAOBJ_SCHEDULE_H #include "schedule_entry.h" #include "../halthandle.h" #include "../tpl/minivec_tpl.h" class cbuffer_t; class grund_t; class player_t; class karte_t; /** * Class to hold schedule of vehicles in Simutrans. */ class schedule_t { bool editing_finished; uint8 current_stop; static schedule_entry_t dummy_entry; /** * Fix up current_stop value, which we may have made out of range */ void make_current_stop_valid() { uint8 count = entries.get_count(); if( count == 0 ) { current_stop = 0; } else if( current_stop >= count ) { current_stop = count-1; } } protected: schedule_t() : editing_finished(false), current_stop(0) {} public: enum schedule_type { schedule = 0, truck_schedule = 1, train_schedule = 2, ship_schedule = 3, airplane_schedule = 4, monorail_schedule = 5, tram_schedule = 6, maglev_schedule = 7, narrowgauge_schedule = 8 }; minivec_tpl entries; /** * Returns error message if stops are not allowed */ virtual char const* get_error_msg() const = 0; /** * Returns true if this schedule allows stop at the * given tile. */ bool is_stop_allowed(const grund_t *gr) const; bool empty() const { return entries.empty(); } uint8 get_count() const { return entries.get_count(); } virtual schedule_type get_type() const = 0; virtual waytype_t get_waytype() const = 0; // remove this entry void remove_entry( uint8 entry ); // move the entry to front (and wrap) // you must use this function so all groups of departue entries are moved together void move_entry_forward( uint8 entry ); // move the entry to back (and wrap) // you must use this function so all groups of departue entries are moved together void move_entry_backward( uint8 entry ); /** * Get current stop of the schedule. */ uint8 get_current_stop() const { return current_stop; } /// returns the current stop, always a valid entry schedule_entry_t const& get_current_entry() const { return current_stop >= entries.get_count() ? dummy_entry : entries[current_stop]; } /** * Set the current stop of the schedule . * If new value is bigger than stops available, the max stop will be used. */ void set_current_stop(uint8 new_current_stop) { current_stop = new_current_stop; make_current_stop_valid(); } /// advance current_stop by one void advance() { if( !entries.empty() ) { if( entries[ current_stop ].minimum_loading > 100 ) { // skip the whole departure group for( uint8 next_stop = (current_stop + 1) % entries.get_count(); next_stop != current_stop; next_stop = (next_stop + 1) % entries.get_count() ) { if( entries[ next_stop ].pos != entries[ current_stop ].pos ) { current_stop = next_stop; break; } } } else { current_stop = (current_stop+1)%entries.get_count(); } } } inline bool is_editing_finished() const { return editing_finished; } void finish_editing() { editing_finished = true; } void start_editing() { editing_finished = false; } virtual ~schedule_t() {} /** * returns a halthandle for the next halt in the schedule (or unbound) */ halthandle_t get_next_halt( player_t *player, halthandle_t halt ) const; /** * returns a halthandle for the previous halt in the schedule (or unbound) */ halthandle_t get_prev_halt( player_t *player ) const; /** * Inserts a coordinate at current_stop into the schedule. */ bool insert(const grund_t* gr, uint8 minimum_loading = 0, uint16 waiting_time = 0); /** * Appends a coordinate to the schedule. */ bool append(const grund_t* gr, uint8 minimum_loading = 0, uint16 waiting_time = 0); /** * Makes schedule valid for driving: * - remove double entries * - clear schedule if it contains just one entry * - call make_current_stop_valid */ void make_valid(); /** * Remove double entries. */ void remove_double_entries(); /** * Remove current_stop entry from the schedule. */ bool remove(); void rdwr(loadsave_t *file); void rotate90( sint16 y_size ); /** * if the passed in schedule matches "this", then return true */ bool matches(karte_t *welt, const schedule_t *schedule); /** * Compare this schedule with another, ignoring order and exact positions and waypoints, wazting/scheduling. */ bool similar( const schedule_t *schedule, const player_t *player ); /** * Calculates a return way for this schedule. * Will add elements 1 to end in reverse order to schedule if ture * Or just mirror order if false */ void add_return_way(bool); virtual schedule_t* copy() = 0;//{ return new schedule_t(this); } // copy all entries from schedule src to this and adjusts current_stop void copy_from(const schedule_t *src); // fills the given buffer with a schedule void sprintf_schedule( cbuffer_t &buf ) const; /** * Converts this string into a schedule. * Ensure valid schedule. * @returns true reading worked and schedule is non-empty */ bool sscanf_schedule( const char * ); /** * Append description of entry to buf. * If @p max_chars > 0 then append short version, without loading level and position. */ static void gimme_stop_name(cbuffer_t& buf, karte_t* welt, player_t const* player_, bool wt, schedule_entry_t const& entry, int max_chars); }; /** * Schedules with stops on tracks. */ class train_schedule_t : public schedule_t { public: train_schedule_t() {} schedule_t* copy() OVERRIDE { schedule_t *s = new train_schedule_t(); s->copy_from(this); return s; } const char *get_error_msg() const OVERRIDE { return "Zughalt muss auf\nSchiene liegen!\n"; } schedule_type get_type() const OVERRIDE { return train_schedule; } waytype_t get_waytype() const OVERRIDE { return track_wt; } }; /** * Schedules with stops on tram tracks. */ class tram_schedule_t : public train_schedule_t { public: tram_schedule_t() {} schedule_t* copy() OVERRIDE { schedule_t *s = new tram_schedule_t(); s->copy_from(this); return s; } schedule_type get_type() const OVERRIDE { return tram_schedule; } waytype_t get_waytype() const OVERRIDE { return tram_wt; } }; /** * Schedules with stops on roads. */ class truck_schedule_t : public schedule_t { public: truck_schedule_t() {} schedule_t* copy() OVERRIDE { schedule_t *s = new truck_schedule_t(); s->copy_from(this); return s; } const char *get_error_msg() const OVERRIDE { return "Autohalt muss auf\nStrasse liegen!\n"; } schedule_type get_type() const OVERRIDE { return truck_schedule; } waytype_t get_waytype() const OVERRIDE { return road_wt; } }; /** * Schedules with stops on water. */ class ship_schedule_t : public schedule_t { public: ship_schedule_t() {} schedule_t* copy() OVERRIDE { schedule_t *s = new ship_schedule_t(); s->copy_from(this); return s; } const char *get_error_msg() const OVERRIDE { return "Schiffhalt muss im\nWasser liegen!\n"; } schedule_type get_type() const OVERRIDE { return ship_schedule; } waytype_t get_waytype() const OVERRIDE { return water_wt; } }; /** * Schedules for airplanes. */ class airplane_schedule_t : public schedule_t { public: airplane_schedule_t() {} schedule_t* copy() OVERRIDE { schedule_t *s = new airplane_schedule_t(); s->copy_from(this); return s; } const char *get_error_msg() const OVERRIDE { return "Flugzeughalt muss auf\nRunway liegen!\n"; } schedule_type get_type() const OVERRIDE { return airplane_schedule; } waytype_t get_waytype() const OVERRIDE { return air_wt; } }; /** * Schedules with stops on mono-rails. */ class monorail_schedule_t : public schedule_t { public: monorail_schedule_t() {} schedule_t* copy() OVERRIDE { schedule_t *s = new monorail_schedule_t(); s->copy_from(this); return s; } const char *get_error_msg() const OVERRIDE { return "Monorailhalt muss auf\nMonorail liegen!\n"; } schedule_type get_type() const OVERRIDE { return monorail_schedule; } waytype_t get_waytype() const OVERRIDE { return monorail_wt; } }; /** * Schedules with stops on maglev tracks. */ class maglev_schedule_t : public schedule_t { public: maglev_schedule_t() {} schedule_t* copy() OVERRIDE { schedule_t *s = new maglev_schedule_t(); s->copy_from(this); return s; } const char *get_error_msg() const OVERRIDE { return "Maglevhalt muss auf\nMaglevschiene liegen!\n"; } schedule_type get_type() const OVERRIDE { return maglev_schedule; } waytype_t get_waytype() const OVERRIDE { return maglev_wt; } }; /** * Schedules with stops on narrowgauge tracks. */ class narrowgauge_schedule_t : public schedule_t { public: narrowgauge_schedule_t() {} schedule_t* copy() OVERRIDE { schedule_t *s = new narrowgauge_schedule_t(); s->copy_from(this); return s; } const char *get_error_msg() const OVERRIDE { return "On narrowgauge track only!\n"; } schedule_type get_type() const OVERRIDE { return narrowgauge_schedule; } waytype_t get_waytype() const OVERRIDE { return narrowgauge_wt; } }; #endif simutrans-124.5/src/simutrans/dataobj/schedule_entry.h000066400000000000000000000032171520675460500231750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_SCHEDULE_ENTRY_H #define DATAOBJ_SCHEDULE_ENTRY_H #include "koord3d.h" #include "../tpl/minivec_tpl.h" #include "../world/simworld.h" /** * A schedule entry. */ struct schedule_entry_t { public: schedule_entry_t() {} schedule_entry_t(koord3d const& pos, uint8 const minimum_loading, uint16 const waiting_time) : pos(pos), minimum_loading(minimum_loading), waiting_time(waiting_time) {} /** * target position */ koord3d pos; /** * Wait for % load at this stops * (ignored on waypoints) * If this value is greater than 100, waiting_time_shift contains the first departure time, for 100-minimum loading times per month */ uint8 minimum_loading; /** * (only active if minimum_loading!=0) * contains a departing time in ticks, relative to the length of the month * The actual tick value is waiting_time << (tick_bit_per_month-16) */ uint16 waiting_time; uint32 get_waiting_ticks() const { return world()->ticks_per_world_month_shift >= 16 ? (uint32)waiting_time << (world()->ticks_per_world_month_shift - 16) : (uint32)waiting_time >> (16 - world()->ticks_per_world_month_shift); } // true if this is an absolute dparture time and not maximum waiting time uint8 get_absolute_departures() const { return minimum_loading > 100 ? minimum_loading - 100 : 0; } }; inline bool operator ==(const schedule_entry_t &a, const schedule_entry_t &b) { return a.pos == b.pos && a.minimum_loading == b.minimum_loading && a.waiting_time == b.waiting_time; } #endif simutrans-124.5/src/simutrans/dataobj/settings.cc000066400000000000000000002076671520675460500221750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../builder/wegbauer.h" #include "../descriptor/way_desc.h" #include "../pathes.h" #include "../player/finance.h" #include "../player/simplay.h" #include "../simconst.h" #include "../simdebug.h" #include "../simmain.h" #include "../simtypes.h" #include "../sys/simsys.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "../vehicle/vehicle_base.h" #include "../world/simworld.h" #include "environment.h" #include "loadsave.h" #include "settings.h" #include "tabfile.h" #include "translator.h" #include "../tpl/minivec_tpl.h" #include "../descriptor/ground_desc.h" #define NEVER 0xFFFFU settings_t::settings_t() { // default climate zones set_default_climates( ); for( int i=0; i<10; i++ ) { locality_factor_per_year[i].year = 0; locality_factor_per_year[i].factor = 0; } locality_factor_per_year[0].factor = 100; #ifdef OTTD_LIKE // crossconnect all factories (like OTTD and similar games) crossconnect_factories=true; crossconnect_factor=100; #endif just_in_time = env_t::just_in_time; // read default from env_t // should be set in simmain.cc (taken from pak-set simuconf.tab way_height_clearance = env_t::default_settings.get_way_height_clearance(); if (way_height_clearance >2) { // if outside bounds, then set to default = 1 way_height_clearance = 1; } strcpy( language_code_names, "en" ); // default AIs active for( int i=0; iis_version_less(86, 0)) { uint32 dummy; file->rdwr_long(size_x ); size_y = size_x; file->rdwr_long(map_number ); // to be compatible with previous savegames dummy = 0; file->rdwr_long(dummy ); //dummy! factory_count = 12; tourist_attractions = 12; // now towns mean_citizen_count = 1600; dummy = city_count; file->rdwr_long(dummy ); dummy &= 127; if(dummy>63) { dbg->warning("settings_t::rdwr()", "This game was saved with too many cities! (%i of maximum 63). Simutrans may crash!", dummy); } city_count = dummy; // rest file->rdwr_long(dummy ); // scroll ignored file->rdwr_long(traffic_level ); file->rdwr_long(dummy); // pedestrians at stops dummy = groundwater; file->rdwr_long(dummy ); groundwater = (sint16)(dummy/16); file->rdwr_double(max_mountain_height ); file->rdwr_double(map_roughness ); station_coverage_size = 3; beginner_mode = false; rotation = 0; } else { // newer versions file->rdwr_long(size_x ); file->rdwr_long(map_number ); // industries file->rdwr_long(factory_count ); if(file->is_version_less(99, 18)) { uint32 dummy; // was city chains file->rdwr_long(dummy ); } else { file->rdwr_long( electric_promille ); } file->rdwr_long(tourist_attractions ); // now towns file->rdwr_long(mean_citizen_count ); file->rdwr_long(city_count ); // rest if(file->is_version_less(101, 0)) { uint32 dummy; // was scroll dir file->rdwr_long(dummy ); } file->rdwr_long(traffic_level ); if (file->is_version_less(123, 2)) { sint32 dummy = env_t::stop_pedestrians; file->rdwr_long(dummy); env_t::stop_pedestrians = dummy; } sint32 dummy = groundwater; file->rdwr_long(dummy ); if(file->is_version_less(99, 5)) { groundwater = (sint16)(dummy/16); } else { groundwater = (sint16)dummy; } file->rdwr_double(max_mountain_height ); file->rdwr_double(map_roughness ); if(file->is_version_atleast(86, 3)) { dummy = station_coverage_size; file->rdwr_long(dummy ); station_coverage_size = (uint16)dummy; } if(file->is_version_atleast(86, 6)) { // handle also size on y direction file->rdwr_long(size_y ); } else { size_y = size_x; } if(file->is_version_atleast(86, 11)) { // some more settings file->rdwr_byte(allow_player_change ); file->rdwr_byte(use_timeline ); file->rdwr_short(starting_year ); } else { allow_player_change = 1; use_timeline = 1; starting_year = 1930; } if(file->is_version_atleast(88, 5)) { file->rdwr_short(bits_per_month ); } else { bits_per_month = 18; } if(file->is_version_atleast(89, 3)) { file->rdwr_bool(beginner_mode ); } else { beginner_mode = false; } if( file->is_version_atleast(120, 1) ){ file->rdwr_byte( just_in_time ); } else if( file->is_version_atleast(89, 4) ) { bool compat = just_in_time > 0; file->rdwr_bool( compat ); just_in_time = 0; if( compat ) { just_in_time = env_t::just_in_time ? env_t::just_in_time : 1; } } // rotation of the map with respect to the original value if(file->is_version_atleast(99, 15)) { file->rdwr_byte(rotation ); } else { rotation = 0; } // clear the name when loading ... if(file->is_loading()) { filename = ""; } // climate borders, no overlapping borders are possible if( file->is_version_atleast(91, 0) ){ if( file->is_version_less(121,1) ) { sint16 old_climate_borders[ MAX_CLIMATES ]; for( int cl = 0; cl < MAX_CLIMATES; cl++ ) { old_climate_borders[cl] = climate_borders[cl][0]; } if( file->is_version_less(120, 6) ) { old_climate_borders[arctic_climate] -= groundwater; } for( int i=0; i<8; i++ ) { file->rdwr_short(old_climate_borders[i] ); } if( file->is_version_less(120, 6) ) { old_climate_borders[arctic_climate] += groundwater; } if( file->is_loading() && file->is_version_less(112, 7) ) { groundwater *= env_t::pak_height_conversion_factor; for( int i = 0; i < MAX_CLIMATES; i++ ) { old_climate_borders[i] *= env_t::pak_height_conversion_factor; } } // convert to new borders if loading, es keep the current if( file->is_loading() ) { for( int cl = 0; cl < MAX_CLIMATES-1; cl++ ) { climate_borders[cl][0] = old_climate_borders[cl]; climate_borders[cl][1] = old_climate_borders[cl+1]; } climate_borders[0][1] = old_climate_borders[0]; climate_borders[arctic_climate][1] = old_climate_borders[arctic_climate]; } } else { for( int i=0; i<8; i++ ) { file->rdwr_short( climate_borders[i][0] ); file->rdwr_short( climate_borders[i][1] ); } file->rdwr_byte( tropic_humidity ); file->rdwr_byte( desert_humidity ); for( int i=0; i<5; i++ ) { file->rdwr_byte( climate_temperature_borders[i] ); } file->rdwr_byte( patch_size_percentage ); } file->rdwr_short( winter_snowline ); } // vehicle may need realignment afterwards! if(file->is_version_less(99, 19)) { vehicle_base_t::set_diagonal_multiplier( pak_diagonal_multiplier, 1024 ); } else { uint16 old_multiplier = pak_diagonal_multiplier; file->rdwr_short( old_multiplier ); vehicle_base_t::set_diagonal_multiplier( pak_diagonal_multiplier, old_multiplier ); // since vehicle will need realignment afterwards! } if(file->is_version_atleast(101, 0)) { // game mechanics file->rdwr_short(origin_x ); file->rdwr_short(origin_y ); file->rdwr_long(passenger_factor ); // town grow stuff if(file->is_version_atleast(102, 2)) { file->rdwr_long(passenger_multiplier ); file->rdwr_long(mail_multiplier ); file->rdwr_long(goods_multiplier ); file->rdwr_long(electricity_multiplier ); file->rdwr_long(growthfactor_small ); file->rdwr_long(growthfactor_medium ); file->rdwr_long(growthfactor_large ); file->rdwr_short(factory_worker_percentage ); file->rdwr_short(tourist_percentage ); file->rdwr_short(factory_worker_radius ); } file->rdwr_long(electric_promille ); file->rdwr_short(min_factory_spacing ); file->rdwr_bool(crossconnect_factories ); file->rdwr_short(crossconnect_factor ); if (file->is_version_less(123, 2)) { file->rdwr_bool(env_t::random_pedestrians); } file->rdwr_long(stadtauto_duration ); file->rdwr_bool(numbered_stations ); if( file->is_version_less(102, 3) ) { if( file->is_loading() ) { num_city_roads = 1; city_roads[0].intro = 0; city_roads[0].retire = 0; // intercity roads were not saved in old savegames num_intercity_roads = 0; } file->rdwr_str(city_roads[0].name, lengthof(city_roads[0].name) ); } else { // several roads ... file->rdwr_short(num_city_roads ); if( num_city_roads>=10 ) { dbg->fatal("settings_t::rdwr()", "Too many (%i) city roads!", num_city_roads); } for( int i=0; irdwr_str(city_roads[i].name, lengthof(city_roads[i].name) ); file->rdwr_short(city_roads[i].intro ); file->rdwr_short(city_roads[i].retire ); } // several intercity roads ... file->rdwr_short(num_intercity_roads ); if( num_intercity_roads>=10 ) { dbg->fatal("settings_t::rdwr()", "Too many (%i) intercity roads!", num_intercity_roads); } for( int i=0; irdwr_str(intercity_roads[i].name, lengthof(intercity_roads[i].name) ); file->rdwr_short(intercity_roads[i].intro ); file->rdwr_short(intercity_roads[i].retire ); } } file->rdwr_long(max_route_steps ); file->rdwr_long(max_transfers ); file->rdwr_long(max_hops ); file->rdwr_long(beginner_price_factor ); // name of stops file->rdwr_str(language_code_names, lengthof(language_code_names) ); // restore AI state for( int i=0; i<15; i++ ) { if(file->is_version_less(122,1)) { bool player_active = true; file->rdwr_bool(player_active); } file->rdwr_byte(player_type[i] ); if( file->is_version_less(102, 3) ) { char dummy[2] = { 0, 0 }; file->rdwr_str(dummy, lengthof(dummy) ); } } // cost section ... file->rdwr_bool(freeplay ); if( file->is_version_atleast(102, 3) ) { file->rdwr_longlong(starting_money ); // these must be saved, since new player will get different amounts eventually for( int i=0; i<10; i++ ) { file->rdwr_short(startingmoneyperyear[i].year ); file->rdwr_longlong(startingmoneyperyear[i].money ); file->rdwr_bool(startingmoneyperyear[i].interpol ); } } else { // compatibility code sint64 save_starting_money = starting_money; if( file->is_saving() ) { if(save_starting_money==0) { save_starting_money = get_starting_money(starting_year ); } if(save_starting_money==0) { save_starting_money = env_t::default_settings.get_starting_money(starting_year ); } if(save_starting_money==0) { save_starting_money = 20000000; } } file->rdwr_longlong(save_starting_money ); if(file->is_loading()) { if(save_starting_money==0) { save_starting_money = env_t::default_settings.get_starting_money(starting_year ); } if(save_starting_money==0) { save_starting_money = 20000000; } starting_money = save_starting_money; } } file->rdwr_long(maint_building ); file->rdwr_longlong(cst_multiply_dock ); file->rdwr_longlong(cst_multiply_station ); file->rdwr_longlong(cst_multiply_roadstop ); file->rdwr_longlong(cst_multiply_airterminal ); file->rdwr_longlong(cst_multiply_post ); file->rdwr_longlong(cst_multiply_headquarter ); file->rdwr_longlong(cst_depot_rail ); file->rdwr_longlong(cst_depot_road ); file->rdwr_longlong(cst_depot_ship ); file->rdwr_longlong(cst_depot_air ); if( file->is_version_less(102, 2) ) { sint64 dummy64 = 100000; file->rdwr_longlong(dummy64 ); file->rdwr_longlong(dummy64 ); file->rdwr_longlong(dummy64 ); } // alter landscape file->rdwr_longlong(cst_buy_land ); file->rdwr_longlong(cst_alter_land ); file->rdwr_longlong(cst_set_slope ); file->rdwr_longlong(cst_found_city ); file->rdwr_longlong(cst_multiply_found_industry ); file->rdwr_longlong(cst_remove_tree ); file->rdwr_longlong(cst_multiply_remove_haus ); file->rdwr_longlong(cst_multiply_remove_field ); // cost for transformers file->rdwr_longlong(cst_transformer ); file->rdwr_longlong(cst_maintain_transformer ); if( file->is_version_atleast(120, 3) ) { file->rdwr_longlong(cst_make_public_months); } if( file->is_version_atleast(120, 9) ) { file->rdwr_longlong(cst_multiply_merge_halt); } // wayfinder file->rdwr_long(way_count_straight ); file->rdwr_long(way_count_curve ); file->rdwr_long(way_count_double_curve ); file->rdwr_long(way_count_90_curve ); file->rdwr_long(way_count_slope ); file->rdwr_long(way_count_tunnel ); file->rdwr_long(way_max_bridge_len ); file->rdwr_long(way_count_leaving_way ); } else { // name of stops set_name_language_iso( env_t::language_iso ); // default AIs active for( int i=0; iis_version_atleast(101, 1)) { file->rdwr_bool( separate_halt_capacities ); file->rdwr_byte( pay_for_total_distance ); file->rdwr_short(starting_month ); file->rdwr_short( river_number ); file->rdwr_short( min_river_length ); file->rdwr_short( max_river_length ); } if(file->is_version_atleast(102, 1)) { file->rdwr_bool( avoid_overcrowding ); } if(file->is_version_atleast(102, 2)) { file->rdwr_bool( no_routing_over_overcrowding ); if (file->is_version_less(123, 2)) { file->rdwr_bool( with_private_paks ); } } if(file->is_version_atleast(102, 3)) { // network stuff // Superseded by simrand_rdwr in newer versions if (file->is_version_less(122, 1)) { random_counter = get_random_seed( ); file->rdwr_long( random_counter ); } if( !env_t::networkmode || env_t::server ) { frames_per_second = clamp(env_t::fps, env_t::min_fps, env_t::max_fps); // update it on the server to the current setting frames_per_step = env_t::network_frames_per_step; } file->rdwr_long( frames_per_second ); file->rdwr_long( frames_per_step ); if( !env_t::networkmode || env_t::server ) { frames_per_second = env_t::fps; // update it on the server to the current setting frames_per_step = env_t::network_frames_per_step; } file->rdwr_bool( allow_buying_obsolete_vehicles ); file->rdwr_long( factory_worker_minimum_towns ); file->rdwr_long( factory_worker_maximum_towns ); // forest stuff file->rdwr_byte( forest_base_size ); file->rdwr_byte( forest_map_size_divisor ); file->rdwr_byte( forest_count_divisor ); file->rdwr_short( forest_inverse_spare_tree_density ); file->rdwr_byte( max_no_of_trees_on_square ); file->rdwr_short( tree_climates ); file->rdwr_short( no_tree_climates ); bool no_trees = (tree_distribution==TREE_DIST_NONE); file->rdwr_bool( no_trees ); if( file->is_loading() ) { tree_distribution = no_trees ? TREE_DIST_NONE : TREE_DIST_RANDOM; } file->rdwr_long( minimum_city_distance ); file->rdwr_long( industry_increase ); } if( file->is_version_atleast(110, 0) ) { if( !env_t::networkmode || env_t::server ) { server_frames_ahead = env_t::server_frames_ahead; } file->rdwr_long( server_frames_ahead ); if( !env_t::networkmode || env_t::server ) { server_frames_ahead = env_t::server_frames_ahead; } file->rdwr_short( used_vehicle_reduction ); } if( file->is_version_atleast(110, 1) ) { file->rdwr_bool( default_player_color_random ); for( int i=0; irdwr_byte( default_player_color[i][0] ); file->rdwr_byte( default_player_color[i][1] ); } } else if( file->is_loading() ) { default_player_color_random = false; for( int i=0; iis_version_atleast(110, 5) ) { file->rdwr_short(factory_arrival_periods); file->rdwr_bool(factory_enforce_demand); } if( file->is_version_atleast(110, 7) ) { for( int i=0; i<10; i++ ) { file->rdwr_short(locality_factor_per_year[i].year ); file->rdwr_long(locality_factor_per_year[i].factor ); } file->rdwr_bool( drive_on_left ); file->rdwr_bool( signals_on_left ); file->rdwr_long( way_toll_runningcost_percentage ); file->rdwr_long( way_toll_waycost_percentage ); } if( file->is_version_atleast(111, 2) ) { file->rdwr_long( bonus_basefactor ); } else if( file->is_loading() ) { bonus_basefactor = 125; } if( file->is_version_atleast(111, 4) ) { file->rdwr_bool( allow_underground_transformers ); } if( file->is_version_atleast(111, 5) ) { file->rdwr_short( special_building_distance ); } if( file->is_version_atleast(112, 1) ) { file->rdwr_short( factory_maximum_intransit_percentage ); } if( file->is_version_atleast(112, 2) ) { file->rdwr_short( remove_dummy_player_months ); file->rdwr_short( unprotect_abandoned_player_months ); } if( file->is_version_atleast(112, 3) ) { file->rdwr_short( max_factory_spacing ); file->rdwr_short( max_factory_spacing_percentage ); } if( file->is_version_atleast(112, 8) ) { file->rdwr_longlong( cst_alter_climate ); file->rdwr_byte( way_height_clearance ); } if( file->is_version_atleast(120, 2) ) { file->rdwr_long( default_ai_construction_speed ); } else if( file->is_loading() ) { default_ai_construction_speed = env_t::default_ai_construction_speed; } if( file->is_version_atleast(122,0) ) { file->rdwr_byte(lake_height); file->rdwr_short(tree_distribution); } else if( file->is_version_atleast(120, 2) ) { bool this_lake = lake_height > groundwater; file->rdwr_bool(this_lake); bool no_trees = (tree_distribution==TREE_DIST_NONE); file->rdwr_bool( no_trees ); if( file->is_loading() ) { lake_height = this_lake ? 8 : groundwater; tree_distribution = no_trees ? TREE_DIST_NONE : TREE_DIST_RANDOM; } } if( file->is_version_atleast(120, 2) ) { file->rdwr_long( max_choose_route_steps ); } if( file->is_version_atleast(120, 4) ) { file->rdwr_bool(disable_make_way_public); } if( file->is_version_atleast(120, 6) ) { file->rdwr_byte(max_rail_convoi_length); file->rdwr_byte(max_road_convoi_length); file->rdwr_byte(max_ship_convoi_length); file->rdwr_byte(max_air_convoi_length); } if( file->is_version_atleast(120, 7) ) { file->rdwr_byte(world_maximum_height); file->rdwr_byte(world_minimum_height); world_maximum_height = clamp(world_maximum_height, 16, 127); world_minimum_height = clamp(world_minimum_height, -127, -12); } if( file->is_version_atleast(120, 9) ) { file->rdwr_long(allow_merge_distant_halt); } if( file->is_version_atleast(122, 1) ) { file->rdwr_enum(climate_generator); file->rdwr_byte(wind_direction); bool dummy = true; file->rdwr_bool(dummy /*was departures_on_time */); } else if( file->is_loading() ) { climate_generator = HEIGHT_BASED; switch (rotation) { default: case 0: wind_direction = ribi_t::west; break; case 1: wind_direction = ribi_t::north; break; case 2: wind_direction = ribi_t::east; break; case 3: wind_direction = ribi_t::south; break; } } if( file->is_version_atleast(122, 2) ) { file->rdwr_bool(stop_halt_as_scheduled); } // restoring the city speed limit vector if (file->is_version_atleast(123, 2)) { file->rdwr_short(city_road_speed_limit_num); for (uint16 i = 0; i < city_road_speed_limit_num; i++) { file->rdwr_short(city_road_speed_limit[i]); } } // otherwise the default values of the last one will be used if (file->is_version_atleast(124, 2)) { file->rdwr_long(way_count_no_way); file->rdwr_long(way_count_avoid_crossings); file->rdwr_long(way_count_maximum); } if (file->is_version_atleast(124, 4)) { file->rdwr_long(cst_kw_per_credit); } else { cst_kw_per_credit = 512; } } // sometimes broken savegames could have no legal direction for take off ... if( !ribi_t::is_single( wind_direction ) ) { wind_direction = ribi_t::west; } if (file->is_loading()) { motd = ""; } } // read the settings from this file void settings_t::parse_simuconf( tabfile_t& simuconf, sint16& disp_width, sint16& disp_height, sint16 &fullscreen, bool set_objfilename ) { tabfileobj_t contents; simuconf.read( contents ); // special day/night colors for( int i = 0; i < LIGHT_COUNT; i++ ) { char str[ 256 ]; sprintf( str, "special_color[%i]", i ); const vector_tpl c = contents.get_ints( str ); if( c.get_count() >= 6 ) { // now update RGB values const rgb888_t day_light = { (uint8)c[0], (uint8)c[1], (uint8)c[2] }; const rgb888_t night_light = { (uint8)c[3], (uint8)c[4], (uint8)c[5] }; gfx->set_light_color(i, day_light, night_light); } } // check for fontname, must be a valid name! // will be only changed if default! std::string fname = trim( contents.get_string( "fontname", env_t::fontname.c_str() ) ); if( FILE* f = dr_fopen( fname.c_str(), "r" ) ) { fclose( f ); env_t::fontname = fname; } env_t::fontsize = contents.get_int( "fontsize", env_t::fontsize ); env_t::water_animation = contents.get_int_clamped( "water_animation_ms", env_t::water_animation, 0, INT_MAX); env_t::ground_object_probability = contents.get_int_clamped( "random_grounds_probability", env_t::ground_object_probability, 0, INT_MAX); env_t::moving_object_probability = contents.get_int_clamped( "random_wildlife_probability", env_t::moving_object_probability, 0, INT_MAX); env_t::straight_way_without_control = contents.get_int( "straight_way_without_control", env_t::straight_way_without_control ) != 0; env_t::road_user_info = contents.get_int( "pedes_and_car_info", env_t::road_user_info ) != 0; env_t::tree_info = contents.get_int( "tree_info", env_t::tree_info ) != 0; env_t::ground_info = contents.get_int( "ground_info", env_t::ground_info ) != 0; env_t::townhall_info = contents.get_int( "townhall_info", env_t::townhall_info ) != 0; env_t::single_info = contents.get_int( "only_single_info", env_t::single_info ) != 0; env_t::single_line_gui = contents.get_int( "single_line_gui", env_t::single_line_gui ) != 0; env_t::compass_map_position = contents.get_int( "compass_map_position", env_t::compass_map_position ); env_t::compass_screen_position = contents.get_int( "compass_screen_position", env_t::compass_screen_position ); env_t::window_snap_distance = contents.get_int( "window_snap_distance", env_t::window_snap_distance ); env_t::window_buttons_right = contents.get_int( "window_buttons_right", env_t::window_buttons_right ) != 0; env_t::left_to_right_graphs = contents.get_int( "left_to_right_graphs", env_t::left_to_right_graphs ) != 0; env_t::window_frame_active = contents.get_int( "window_frame_active", env_t::window_frame_active ) != 0; env_t::second_open_closes_win = contents.get_int( "second_open_closes_win", env_t::second_open_closes_win ) != 0; env_t::remember_window_positions = contents.get_int( "remember_window_positions", env_t::remember_window_positions ) != 0; env_t::menupos = contents.get_int_clamped( "menubar_position", env_t::menupos, 0, 3); env_t::reselect_closes_tool = contents.get_int( "reselect_closes_tool", env_t::reselect_closes_tool ) != 0; env_t::single_toolbar_mode = contents.get_int( "single_toolbar", env_t::single_toolbar_mode ) != 0; env_t::stack_toolbars = contents.get_int( "stack_toolbars", env_t::stack_toolbars ) != 0; env_t::dpi_scale = contents.get_int( "dpi_scaling", env_t::dpi_scale ); env_t::show_tooltips = contents.get_int( "show_tooltips", env_t::show_tooltips ) != 0; env_t::tooltip_delay = contents.get_int_clamped( "tooltip_delay", env_t::tooltip_delay, 0, INT_MAX); env_t::tooltip_duration = contents.get_int_clamped( "tooltip_duration", env_t::tooltip_duration, 0, INT_MAX); env_t::toolbar_max_width = contents.get_int_clamped( "toolbar_max_width", env_t::toolbar_max_width, 0, INT_MAX); env_t::toolbar_max_height = contents.get_int_clamped( "toolbar_max_height", env_t::toolbar_max_height, 0, INT_MAX); // how to show the stuff outside the map env_t::draw_earth_border = contents.get_int( "draw_earth_border", env_t::draw_earth_border ) != 0; env_t::draw_outside_tile = contents.get_int( "draw_outside_tile", env_t::draw_outside_tile ) != 0; // display stuff env_t::show_names = contents.get_int_clamped( "show_names", env_t::show_names, 0, 7 ); env_t::show_month = contents.get_int_clamped( "show_month", env_t::show_month, 0, 7 ); env_t::show_vehicle_states = contents.get_int_clamped( "show_vehicle_states", env_t::show_vehicle_states, 0, 3 ); env_t::follow_convoi_underground = contents.get_int_clamped( "follow_convoi_underground", env_t::follow_convoi_underground, 0, 2 ); env_t::max_acceleration = contents.get_int_clamped( "fast_forward", env_t::max_acceleration, 0, 0x7FFF ); env_t::fps = contents.get_int_clamped( "frames_per_second", env_t::fps, env_t::min_fps, env_t::max_fps ); env_t::ff_fps = contents.get_int_clamped( "fast_forward_frames_per_second", env_t::ff_fps, env_t::min_fps, env_t::max_fps ); env_t::num_threads = contents.get_int_clamped( "threads", env_t::num_threads, 1, min(dr_get_max_threads(), MAX_THREADS) ); env_t::simple_drawing_default = contents.get_int_clamped( "simple_drawing_tile_size", env_t::simple_drawing_default, 2, 256 ); env_t::simple_drawing_fast_forward = contents.get_int( "simple_drawing_fast_forward", env_t::simple_drawing_fast_forward ) != 0; env_t::visualize_schedule = contents.get_int( "visualize_schedule", env_t::visualize_schedule ) != 0; env_t::hide_rail_return_ticket = contents.get_int( "hide_rail_return_ticket", env_t::hide_rail_return_ticket ) != 0; env_t::chat_window_transparency = contents.get_int_clamped( "chat_transparency", env_t::chat_window_transparency, 0, 100); env_t::hide_keyboard = contents.get_int( "hide_keyboard", env_t::hide_keyboard ) != 0; env_t::numpad_always_moves_map = contents.get_int( "numpad_always_moves_map", env_t::numpad_always_moves_map ) != 0; env_t::leftdrag_in_minimap = contents.get_int( "leftdrag_in_minimap", env_t::leftdrag_in_minimap ) != 0; env_t::player_finance_display_account = contents.get_int( "player_finance_display_account", env_t::player_finance_display_account ) != 0; // network stuff env_t::server_frames_ahead = contents.get_int_clamped( "server_frames_ahead", env_t::server_frames_ahead, 0, INT_MAX ); env_t::additional_client_frames_behind = contents.get_int_clamped( "additional_client_frames_behind", env_t::additional_client_frames_behind, 0, INT_MAX ); env_t::network_frames_per_step = contents.get_int_clamped( "server_frames_per_step", env_t::network_frames_per_step, 1, INT_MAX ); env_t::server_sync_steps_between_checks = contents.get_int_clamped( "server_frames_between_checks", env_t::server_sync_steps_between_checks, 1, INT_MAX ); env_t::pause_server_no_clients = contents.get_int( "pause_server_no_clients", env_t::pause_server_no_clients ) != 0; env_t::server_save_game_on_quit = contents.get_int( "server_save_game_on_quit", env_t::server_save_game_on_quit ) != 0; env_t::reload_and_save_on_quit = contents.get_int( "reload_and_save_on_quit", env_t::reload_and_save_on_quit ) != 0; if( !env_t::server ) { env_t::server_port = contents.get_int_clamped( "server_port", env_t::server_port, 0, 0xFFFF ); } else { env_t::server_port = env_t::server; } env_t::server_announce = contents.get_int( "announce_server", env_t::server_announce ) != 0; env_t::server_announce = contents.get_int( "server_announce", env_t::server_announce ) != 0; env_t::server_announce_interval = contents.get_int_clamped( "server_announce_intervall", env_t::server_announce_interval, 60, 24*60*60); env_t::server_announce_interval = contents.get_int_clamped( "server_announce_interval", env_t::server_announce_interval, 60, 24*60*60); if( *contents.get( "server_dns" ) ) { env_t::server_dns = ltrim( contents.get( "server_dns" ) ); } if( *contents.get( "server_altdns" ) ) { env_t::server_alt_dns = ltrim( contents.get( "server_altdns" ) ); } if( *contents.get( "server_name" ) ) { env_t::server_name = ltrim( contents.get( "server_name" ) ); } if( *contents.get( "server_comments" ) ) { env_t::server_comments = ltrim( contents.get( "server_comments" ) ); } if( *contents.get( "server_email" ) ) { env_t::server_email = ltrim( contents.get( "server_email" ) ); } if( *contents.get( "server_pakurl" ) ) { env_t::server_pakurl = ltrim( contents.get( "server_pakurl" ) ); } if( *contents.get( "server_infurl" ) ) { env_t::server_infurl = ltrim( contents.get( "server_infurl" ) ); } if( *contents.get( "server_admin_pw" ) ) { env_t::server_admin_pw = ltrim( contents.get( "server_admin_pw" ) ); } if( *contents.get( "nickname" ) ) { env_t::nickname = ltrim( contents.get( "nickname" ) ); } if( *contents.get( "server_motd_filename" ) ) { env_t::server_motd_filename = ltrim( contents.get( "server_motd_filename" ) ); } // listen directive is a comma separated list of IP addresses to listen on if( *contents.get( "listen" ) ) { env_t::listen.clear(); std::string s = ltrim( contents.get( "listen" ) ); // Find index of first ',' copy from start of string to that position // Set start index to last position, then repeat // When ',' not found, copy remainder of string size_t start = 0; size_t end; end = s.find_first_of( "," ); env_t::listen.append_unique( ltrim( s.substr( start, end ).c_str() ) ); while( end != std::string::npos ) { start = end; end = s.find_first_of( ",", start + 1 ); env_t::listen.append_unique( ltrim( s.substr( start + 1, end - 1 - start ).c_str() ) ); } } // listen directive is a comma separated list of years and cityroad speeds if (*contents.get("cityroad_speeds")) { const char *start = ltrim(contents.get("cityroad_speeds")); city_road_speed_limit_num = 0; city_road_speed_limit[city_road_speed_limit_num++]=atoi(start); while (*start && city_road_speed_limit_num<20) { start++; if (*start == ',') { start++; city_road_speed_limit[city_road_speed_limit_num++] = atoi(start); } } // make sure we have pairs if (city_road_speed_limit_num & 1) { dbg->error("Wrong number of entries in city_road_speed_limit", "Expected pairs yers,speed but only %i values", city_road_speed_limit_num); } } drive_on_left = contents.get_int( "drive_left", drive_on_left ) != 0; signals_on_left = contents.get_int( "signals_on_left", signals_on_left ) != 0; allow_underground_transformers = contents.get_int( "allow_underground_transformers", allow_underground_transformers ) != 0; disable_make_way_public = contents.get_int( "disable_make_way_public", disable_make_way_public ) != 0; // up to ten rivers are possible for( int i = 0; i < 10; i++ ) { char name[ 32 ]; sprintf( name, "river_type[%i]", i ); const char *test = ltrim( contents.get( name ) ); if( *test ) { const int add_river = i < env_t::river_types ? i : env_t::river_types; env_t::river_type[ add_river ] = test; if( add_river == env_t::river_types ) { env_t::river_types++; } } } // old syntax for single city road const char *str = ltrim( contents.get( "city_road_type" ) ); if( str[ 0 ] ) { num_city_roads = 1; tstrncpy( city_roads[ 0 ].name, str, lengthof( city_roads[ 0 ].name ) ); rtrim( city_roads[ 0 ].name ); // default her: always available city_roads[ 0 ].intro = 1; city_roads[ 0 ].retire = NEVER; } // new: up to ten city_roads are possible if( *contents.get( "city_road[0]" ) ) { // renew them always when a table is encountered ... num_city_roads = 0; for( int i = 0; i < 10; i++ ) { char name[ 256 ]; sprintf( name, "city_road[%i]", i ); // format is "city_road[%i]=name_of_road,using from (year), using to (year)" const char *test = ltrim( contents.get( name ) ); if( *test ) { const char *p = test; while( *p && *p != ',' ) { p++; } tstrncpy( city_roads[ num_city_roads ].name, test, (unsigned)(p - test) + 1 ); // default her: intro/retire=0 -> set later to intro/retire of way-desc city_roads[ num_city_roads ].intro = 0; city_roads[ num_city_roads ].retire = 0; if( *p == ',' ) { ++p; city_roads[ num_city_roads ].intro = atoi( p ) * 12; while( *p && *p != ',' ) { p++; } } if( *p == ',' ) { city_roads[ num_city_roads ].retire = atoi( p + 1 ) * 12; } num_city_roads++; } } } if( num_city_roads == 0 ) { // take fallback value: "city_road" tstrncpy( city_roads[ 0 ].name, "city_road", lengthof( city_roads[ 0 ].name ) ); // default her: always available city_roads[ 0 ].intro = 1; city_roads[ 0 ].retire = NEVER; num_city_roads = 1; } // intercity roads // old syntax for single intercity road str = ltrim( contents.get( "intercity_road_type" ) ); if( str[ 0 ] ) { num_intercity_roads = 1; tstrncpy( intercity_roads[ 0 ].name, str, lengthof( intercity_roads[ 0 ].name ) ); rtrim( intercity_roads[ 0 ].name ); // default her: always available intercity_roads[ 0 ].intro = 1; intercity_roads[ 0 ].retire = NEVER; } // new: up to ten intercity_roads are possible if( *contents.get( "intercity_road[0]" ) ) { // renew them always when a table is encountered ... num_intercity_roads = 0; for( int i = 0; i < 10; i++ ) { char name[ 256 ]; sprintf( name, "intercity_road[%i]", i ); // format is "intercity_road[%i]=name_of_road,using from (year), using to (year)" const char *test = ltrim( contents.get( name ) ); if( *test ) { const char *p = test; while( *p && *p != ',' ) { p++; } tstrncpy( intercity_roads[ num_intercity_roads ].name, test, (unsigned)(p - test) + 1 ); // default her: intro/retire=0 -> set later to intro/retire of way-desc intercity_roads[ num_intercity_roads ].intro = 0; intercity_roads[ num_intercity_roads ].retire = 0; if( *p == ',' ) { ++p; intercity_roads[ num_intercity_roads ].intro = atoi( p ) * 12; while( *p && *p != ',' ) { p++; } } if( *p == ',' ) { intercity_roads[ num_intercity_roads ].retire = atoi( p + 1 ) * 12; } num_intercity_roads++; } } } if( num_intercity_roads == 0 ) { // take fallback value: "asphalt_road" tstrncpy( intercity_roads[ 0 ].name, "asphalt_road", lengthof( intercity_roads[ 0 ].name ) ); // default her: always available intercity_roads[ 0 ].intro = 1; intercity_roads[ 0 ].retire = NEVER; num_intercity_roads = 1; } env_t::autosave = contents.get_int_clamped( "autosave", env_t::autosave, 0, INT_MAX ); // routing stuff max_route_steps = contents.get_int_clamped( "max_route_steps", max_route_steps, 1, INT_MAX ); max_choose_route_steps = contents.get_int_clamped( "max_choose_route_steps", max_choose_route_steps, 1, INT_MAX ); max_hops = contents.get_int_clamped( "max_hops", max_hops, 0, INT_MAX ); max_transfers = contents.get_int_clamped( "max_transfers", max_transfers, 0, INT_MAX ); bonus_basefactor = contents.get_int_clamped( "bonus_basefactor", bonus_basefactor, 0, 1000 ); special_building_distance = contents.get_int_clamped( "special_building_distance", special_building_distance, 1, INT_MAX ); minimum_city_distance = contents.get_int_clamped( "minimum_city_distance", minimum_city_distance, 1, INT_MAX ); industry_increase = contents.get_int_clamped( "industry_increase_every", industry_increase, 0, INT_MAX ); passenger_factor = contents.get_int_clamped( "passenger_factor", passenger_factor, 0, INT_MAX ); /* this can manipulate the passenger generation */ factory_worker_percentage = contents.get_int_clamped( "factory_worker_percentage", factory_worker_percentage, 0, 100 ); factory_worker_radius = contents.get_int_clamped( "factory_worker_radius", factory_worker_radius, 0, 0x7FFF ); factory_worker_minimum_towns = contents.get_int_clamped( "factory_worker_minimum_towns", factory_worker_minimum_towns, 0, 0x7FFF ); factory_worker_maximum_towns = contents.get_int_clamped( "factory_worker_maximum_towns", factory_worker_maximum_towns, 0, 0x7FFF ); factory_arrival_periods = contents.get_int_clamped( "factory_arrival_periods", factory_arrival_periods, 1, 16 ); factory_maximum_intransit_percentage = contents.get_int_clamped( "maximum_intransit_percentage", factory_maximum_intransit_percentage, 0, 0x7FFF ); factory_enforce_demand = contents.get_int( "factory_enforce_demand", factory_enforce_demand ) != 0; tourist_percentage = contents.get_int_clamped( "tourist_percentage", tourist_percentage, 0, 100 ); // .. read twice: old and right spelling separate_halt_capacities = contents.get_int( "seperate_halt_capacities", separate_halt_capacities ) != 0; separate_halt_capacities = contents.get_int( "separate_halt_capacities", separate_halt_capacities ) != 0; pay_for_total_distance = contents.get_int_clamped( "pay_for_total_distance", pay_for_total_distance, 0, 2 ); avoid_overcrowding = contents.get_int( "avoid_overcrowding", avoid_overcrowding ) != 0; no_routing_over_overcrowding = contents.get_int( "no_routing_over_overcrowded", no_routing_over_overcrowding ) != 0; // city stuff passenger_multiplier = contents.get_int_clamped( "passenger_multiplier", passenger_multiplier, 0, 100 ); mail_multiplier = contents.get_int_clamped( "mail_multiplier", mail_multiplier, 0, 100 ); goods_multiplier = contents.get_int_clamped( "goods_multiplier", goods_multiplier, 0, 100 ); electricity_multiplier = contents.get_int_clamped( "electricity_multiplier", electricity_multiplier, 0, 10000 ); growthfactor_small = contents.get_int_clamped( "growthfactor_villages", growthfactor_small, 1, 10000 ); growthfactor_medium = contents.get_int_clamped( "growthfactor_cities", growthfactor_medium, 1, 10000 ); growthfactor_large = contents.get_int_clamped( "growthfactor_capitals", growthfactor_large, 1, 10000 ); env_t::random_pedestrians = contents.get_int("random_pedestrians", env_t::random_pedestrians) != 0; env_t::stop_pedestrians = contents.get_int("stop_pedestrians", env_t::stop_pedestrians) != 0; allow_buying_obsolete_vehicles = contents.get_int( "allow_buying_obsolete_vehicles", allow_buying_obsolete_vehicles ) != 0; used_vehicle_reduction = contents.get_int_clamped( "used_vehicle_reduction", used_vehicle_reduction, 0, 1000 ); traffic_level = contents.get_int_clamped( "citycar_level", traffic_level, 0, 16); stadtauto_duration = contents.get_int_clamped( "default_citycar_life", stadtauto_duration, 0, 1200 ); // starting money starting_money = contents.get_int64( "starting_money", starting_money ); /* up to ten blocks year, money, interpolation={0,1} are possible: * starting_money[i]=y,m,int * y .. year * m .. money (in 1/100 Cr) * int .. interpolation: 0 - no interpolation, !=0 linear interpolated * (m) is the starting money for player start after (y), if (i)!=0, the starting money * is linearly interpolated between (y) and the next greater year given in another entry. * starting money for given year is: */ int j = 0; for( int i = 0; i < 10; i++ ) { char name[ 32 ]; sprintf( name, "starting_money[%i]", i ); const vector_tpl test = contents.get_sint64s( name ); if( (test.get_count() > 1) && (test.get_count() <= 3) ) { // insert sorted by years int k = 0; for( k = 0; k < i; k++ ) { if( startingmoneyperyear[ k ].year > test[ 0 ] ) { for( int l = j; l >= k; l-- ) memcpy( &startingmoneyperyear[ l + 1 ], &startingmoneyperyear[ l ], sizeof( yearmoney ) ); break; } } startingmoneyperyear[ k ].year = (sint16)test[ 0 ]; startingmoneyperyear[ k ].money = test[ 1 ]; if( test.get_count() == 3 ) { startingmoneyperyear[ k ].interpol = test[ 2 ] != 0; } else { startingmoneyperyear[ k ].interpol = false; } j++; } else { // invalid entry } } // at least one found => use this now! if( j > 0 && startingmoneyperyear[ 0 ].money > 0 ) { starting_money = 0; // fill remaining entries for( int i = j + 1; i < 10; i++ ) { startingmoneyperyear[ i ].year = 0; startingmoneyperyear[ i ].money = 0; startingmoneyperyear[ i ].interpol = 0; } } /* up to ten blocks year, locality_factor={0...2000000000} * locality_factor[i]=y,l * y .. year * l .. factor, the larger the more widespread */ j = 0; for( int i = 0; i < 10; i++ ) { char name[ 256 ]; sprintf( name, "locality_factor[%i]", i ); const vector_tpl test = contents.get_sint64s( name ); // two arguments, and then factor natural number if( test.empty() ) { continue; } else if( test.get_count() != 2 ) { // invalid entry dbg->warning("settings_t::parse_simuconf", "Parameter locality_factor[%i] has wrong syntax (Parameter ignored)", i); } else if( test[ 1 ] <= 0 ) { dbg->warning("settings_t::parse_simuconf", "Parameter locality_factor[%i] second value must be larger than zero! (Parameter ignored)", i ); } else { // insert sorted by years int k = 0; for( k = 0; k < i; k++ ) { if( locality_factor_per_year[ k ].year > test[ 0 ] ) { for( int l = j; l >= k; l-- ) memcpy( &locality_factor_per_year[ l + 1 ], &locality_factor_per_year[ l ], sizeof( yearly_locality_factor_t ) ); break; } } locality_factor_per_year[ k ].year = (sint16)test[ 0 ]; locality_factor_per_year[ k ].factor = (uint32)test[ 1 ]; j++; } } // add default, if nothing found if( j == 0 && locality_factor_per_year[ 0 ].factor == 0 ) { locality_factor_per_year[ 0 ].year = 0; locality_factor_per_year[ 0 ].factor = 100; j++; } // fill remaining entries while( j > 0 && j < 9 ) { j++; locality_factor_per_year[ j ].year = 0; locality_factor_per_year[ j ].factor = 0; } // player stuff remove_dummy_player_months = contents.get_int_clamped( "remove_dummy_player_months", remove_dummy_player_months, 0, MAX_PLAYER_HISTORY_YEARS*12 ); // .. read twice: old and correct spelling unprotect_abandoned_player_months = contents.get_int_clamped( "unprotect_abondoned_player_months", unprotect_abandoned_player_months, 0, MAX_PLAYER_HISTORY_YEARS*12 ); unprotect_abandoned_player_months = contents.get_int_clamped( "unprotect_abandoned_player_months", unprotect_abandoned_player_months, 0, MAX_PLAYER_HISTORY_YEARS*12 ); default_player_color_random = contents.get_int( "random_player_colors", default_player_color_random ) != 0; for( int i = 0; i < MAX_PLAYER_COUNT; i++ ) { char name[ 32 ]; sprintf( name, "player_color[%i]", i ); const char *command = contents.get( name ); int c1, c2; if( sscanf( command, "%i,%i", &c1, &c2 ) == 2 ) { default_player_color[ i ][ 0 ] = c1; default_player_color[ i ][ 1 ] = c2; } } default_ai_construction_speed = env_t::default_ai_construction_speed = contents.get_int_clamped( "ai_construction_speed", env_t::default_ai_construction_speed, 0, 1000000000 ); maint_building = contents.get_int_clamped( "maintenance_building", maint_building, 1, 100000000 ); numbered_stations = contents.get_int( "numbered_stations", numbered_stations ) != 0; station_coverage_size = contents.get_int_clamped( "station_coverage", station_coverage_size, 0, 64); // time stuff bits_per_month = contents.get_int_clamped( "bits_per_month", bits_per_month, 1, 31 ); use_timeline = contents.get_int_clamped( "use_timeline", use_timeline, 0, 3 ); starting_year = contents.get_int_clamped( "starting_year", starting_year, 0, 0x7FFF); starting_month = contents.get_int_clamped( "starting_month", starting_month+1, 1, 12 ) - 1; env_t::height_conv_mode = (env_t::height_conversion_mode)contents.get_int_clamped("new_height_map_conversion", (int)env_t::height_conv_mode, 0, env_t::NUM_HEIGHT_CONV_MODES-1); river_number = contents.get_int_clamped( "river_number", river_number, 0, 0x7FFF ); min_river_length = contents.get_int_clamped( "river_min_length", min_river_length, 0, 0x7FFF ); max_river_length = contents.get_int_clamped( "river_max_length", max_river_length, 0, 0x7FFF ); // forest stuff (now part of simuconf.tab) forest_base_size = contents.get_int_clamped( "forest_base_size", forest_base_size, 10, 255 ); forest_map_size_divisor = contents.get_int_clamped( "forest_map_size_divisor", forest_map_size_divisor, 2, 255 ); forest_count_divisor = contents.get_int_clamped( "forest_count_divisor", forest_count_divisor, 2, 255 ); forest_inverse_spare_tree_density = contents.get_int_clamped( "forest_inverse_spare_tree_density", forest_inverse_spare_tree_density, 33, 10000 ); max_no_of_trees_on_square = contents.get_int_clamped( "max_no_of_trees_on_square", max_no_of_trees_on_square, 0, 0xFF ); tree_climates = contents.get_int_clamped( "tree_climates", tree_climates, 0, 0xFFFF ); no_tree_climates = contents.get_int_clamped( "no_tree_climates", no_tree_climates, 0, 0xFFFF ); if (contents.get("no_trees")) { const bool no_trees = contents.get_int("no_trees", tree_distribution == TREE_DIST_NONE); tree_distribution = no_trees ? TREE_DIST_NONE : TREE_DIST_RANDOM; } // landscape stuff groundwater = contents.get_int_clamped("groundwater", groundwater, -128, 127); max_mountain_height = (double)contents.get_int_clamped( "max_mountain_height", (int)max_mountain_height, 0, 1000 ); map_roughness = (double)(contents.get_int_clamped( "map_roughness", (int)(map_roughness *20.0 + 0.5)-8, 0, 10 ) +8) / 20.0; winter_snowline = contents.get_int_clamped("winter_snowline", winter_snowline, groundwater, 127); // maximum size (w or h) of climate area relative to the original patch (min is 5 tiles, maximu 100m, hardcoded in simworld.cc) patch_size_percentage = contents.get_int_clamped("patch_size_percentage", patch_size_percentage, 0, 100); // all cimate height for( int i = desert_climate; i < MAX_CLIMATES; i++ ) { char name[ 32 ]; sprintf( name, "%s_height", ground_desc_t::get_climate_name_from_bit( (climate)(i) ) ); const char *command = contents.get( name ); int c1, c2; if( sscanf( command, "%i,%i", &c1, &c2 ) == 2 ) { if (c1 <= c2 && c1 >= groundwater && c2 < 127 ) { climate_borders[i][0] = c1; climate_borders[i][1] = c2; } } } // other humidity climate related for (int i = 0; i < 5; i++) { char str[128]; sprintf(str, "climate_temperature_borders[%d]", i); climate_temperature_borders[i] = contents.get_int(str, climate_temperature_borders[i]); } desert_humidity = contents.get_int_clamped("desert_humidity", desert_humidity, 0, 100); tropic_humidity = contents.get_int_clamped("tropic_humidity", tropic_humidity, 0, 100); moisture = contents.get_int_clamped("moisture_change_land", moisture, -5, 5); moisture_water = contents.get_int_clamped("moisture_change_water", moisture_water, -5, 5); // which climate generator preferred (the maps one does not work yet ...) if (const char* cl = contents.get("climate_generator")) { climate_generator = HEIGHT_BASED; if (tstrcasestr(cl, "humidity)")) { climate_generator = HUMIDITY_BASED; } } tree_distribution = contents.get_int_clamped( "tree_distribution", tree_distribution, TREE_DIST_NONE, TREE_DIST_COUNT-1 ); lake_height = contents.get_int("no_lakes", 1 )==0 ? -128 : lake_height; lake_height = contents.get_int_clamped("lake_height", lake_height, groundwater, 127 ); // these are pak specific; the diagonal length affect travelling time (is game critical) pak_diagonal_multiplier = contents.get_int_clamped("diagonal_multiplier", pak_diagonal_multiplier, 1, INT_MAX ); // the height in z-direction will only cause pixel errors but not a different behaviour env_t::pak_tile_height_step = contents.get_int_clamped("tile_height", env_t::pak_tile_height_step, 1, INT_MAX ); // new height for old slopes after conversion - 1=single height, 2=double height // Must be only overwrite when reading from pak dir ... env_t::pak_height_conversion_factor = contents.get_int_clamped("height_conversion_factor", env_t::pak_height_conversion_factor, 1, 2 ); // minimum clearance under under bridges: 1 or 2? (HACK: value only zero during loading of pak set config) const uint8 bounds = (way_height_clearance!=0); way_height_clearance = contents.get_int_clamped("way_height_clearance", way_height_clearance, bounds, 2 ); min_factory_spacing = contents.get_int_clamped("factory_spacing", min_factory_spacing, 0, 0x7FFF ); min_factory_spacing = contents.get_int_clamped("min_factory_spacing", min_factory_spacing, 0, 0x7FFF ); max_factory_spacing = contents.get_int_clamped("max_factory_spacing", max_factory_spacing, 0, 0x7FFF ); max_factory_spacing_percentage = contents.get_int_clamped("max_factory_spacing_percentage", max_factory_spacing_percentage, 0, 100 ); crossconnect_factor = contents.get_int_clamped("crossconnect_factories_percentage", crossconnect_factor, 0, 100 ); electric_promille = contents.get_int_clamped("electric_promille", electric_promille, 0, 1000 ); crossconnect_factories = contents.get_int("crossconnect_factories", crossconnect_factories ) != 0; env_t::just_in_time = contents.get_int_clamped("just_in_time", env_t::just_in_time, 0, 2); just_in_time = env_t::just_in_time; beginner_price_factor = contents.get_int_clamped("beginner_price_factor", beginner_price_factor, 1, 250000 ); /* this manipulates the good prices in beginner mode */ beginner_mode = contents.get_int("first_beginner", beginner_mode ) != 0; /* start in beginner mode */ way_toll_runningcost_percentage = contents.get_int_clamped("toll_runningcost_percentage", way_toll_runningcost_percentage, 0, 100 ); way_toll_waycost_percentage = contents.get_int_clamped("toll_waycost_percentage", way_toll_waycost_percentage, 0, 100 ); /* now the cost section */ cst_multiply_dock = contents.get_int64("cost_multiply_dock", cst_multiply_dock /(-100) ) * -100; cst_multiply_station = contents.get_int64("cost_multiply_station", cst_multiply_station /(-100) ) * -100; cst_multiply_roadstop = contents.get_int64("cost_multiply_roadstop", cst_multiply_roadstop /(-100) ) * -100; cst_multiply_airterminal = contents.get_int64("cost_multiply_airterminal", cst_multiply_airterminal/(-100) ) * -100; cst_multiply_post = contents.get_int64("cost_multiply_post", cst_multiply_post /(-100) ) * -100; cst_multiply_headquarter = contents.get_int64("cost_multiply_headquarter", cst_multiply_headquarter/(-100) ) * -100; cst_depot_air = contents.get_int64("cost_depot_air", cst_depot_air /(-100) ) * -100; cst_depot_rail = contents.get_int64("cost_depot_rail", cst_depot_rail /(-100) ) * -100; cst_depot_road = contents.get_int64("cost_depot_road", cst_depot_road /(-100) ) * -100; cst_depot_ship = contents.get_int64("cost_depot_ship", cst_depot_ship /(-100) ) * -100; allow_merge_distant_halt = contents.get_int_clamped("allow_merge_distant_halt", allow_merge_distant_halt, 0, INT_MAX); cst_multiply_merge_halt = contents.get_int64("cost_multiply_merge_halt", cst_multiply_merge_halt/(-100) ) * -100; // alter landscape cst_buy_land = contents.get_int64("cost_buy_land", cst_buy_land /(-100) ) * -100; cst_alter_land = contents.get_int64("cost_alter_land", cst_alter_land /(-100) ) * -100; cst_set_slope = contents.get_int64("cost_set_slope", cst_set_slope /(-100) ) * -100; cst_alter_climate = contents.get_int64("cost_alter_climate", cst_alter_climate /(-100) ) * -100; cst_found_city = contents.get_int64("cost_found_city", cst_found_city /(-100) ) * -100; cst_multiply_found_industry = contents.get_int64("cost_multiply_found_industry", cst_multiply_found_industry/(-100) ) * -100; cst_remove_tree = contents.get_int64("cost_remove_tree", cst_remove_tree /(-100) ) * -100; cst_multiply_remove_haus = contents.get_int64("cost_multiply_remove_haus", cst_multiply_remove_haus /(-100) ) * -100; cst_multiply_remove_field = contents.get_int64("cost_multiply_remove_field", cst_multiply_remove_field /(-100) ) * -100; // powerlines cst_kw_per_credit = contents.get_int64("cost_kw_per_credit", cst_kw_per_credit); cst_transformer = contents.get_int64("cost_transformer", cst_transformer /(-100) ) * -100; cst_maintain_transformer = contents.get_int64("cost_maintain_transformer", cst_maintain_transformer/(-100) ) * -100; cst_make_public_months = contents.get_int64("cost_make_public_months", cst_make_public_months); /* now the way builder */ way_count_straight = contents.get_int_clamped("way_straight", way_count_straight, 1, INT_MAX ); way_count_curve = contents.get_int_clamped("way_curve", way_count_curve, 1, INT_MAX ); way_count_double_curve = contents.get_int_clamped("way_double_curve", way_count_double_curve, 1, INT_MAX ); way_count_90_curve = contents.get_int_clamped("way_90_curve", way_count_90_curve, 1, INT_MAX ); way_count_slope = contents.get_int_clamped("way_slope", way_count_slope, 1, INT_MAX ); way_count_tunnel = contents.get_int_clamped("way_tunnel", way_count_tunnel, 1, INT_MAX ); way_max_bridge_len = contents.get_int_clamped("way_max_bridge_len", way_max_bridge_len, 1, INT_MAX); way_count_leaving_way = contents.get_int_clamped("way_leaving_road", way_count_leaving_way, 1, INT_MAX); way_count_no_way = contents.get_int_clamped("way_no_way", way_count_no_way, 1, INT_MAX); way_count_avoid_crossings = contents.get_int_clamped("way_avoid_crossings", way_count_avoid_crossings, 1, INT_MAX); way_count_maximum = contents.get_int_clamped("way_count_maximum", way_count_maximum, 1, INT_MAX); /* * Selection of savegame format through inifile */ str = contents.get("saveformat" ); while (*str == ' ') str++; if (strcmp(str, "binary") == 0) { loadsave_t::set_savemode(loadsave_t::binary ); } else if(strcmp(str, "zipped") == 0) { loadsave_t::set_savemode(loadsave_t::zipped ); } else if(strcmp(str, "xml") == 0) { loadsave_t::set_savemode(loadsave_t::xml ); } else if(strcmp(str, "xml_zipped") == 0) { loadsave_t::set_savemode(loadsave_t::xml_zipped ); } else if(strcmp(str, "bzip2") == 0) { loadsave_t::set_savemode(loadsave_t::bzip2 ); } else if(strcmp(str, "xml_bzip2") == 0) { loadsave_t::set_savemode(loadsave_t::xml_bzip2 ); } else if(strcmp(str, "zstd") == 0) { loadsave_t::set_savemode(loadsave_t::zstd ); } else if(strcmp(str, "xml_zstd") == 0) { loadsave_t::set_savemode(loadsave_t::xml_zstd ); } str = contents.get("autosaveformat" ); while (*str == ' ') str++; if (strcmp(str, "binary") == 0) { loadsave_t::set_autosavemode(loadsave_t::binary ); } else if(strcmp(str, "zipped") == 0) { loadsave_t::set_autosavemode(loadsave_t::zipped ); } else if(strcmp(str, "xml") == 0) { loadsave_t::set_autosavemode(loadsave_t::xml ); } else if(strcmp(str, "xml_zipped") == 0) { loadsave_t::set_autosavemode(loadsave_t::xml_zipped ); } else if(strcmp(str, "bzip2") == 0) { loadsave_t::set_autosavemode(loadsave_t::bzip2 ); } else if(strcmp(str, "xml_bzip2") == 0) { loadsave_t::set_autosavemode(loadsave_t::xml_bzip2 ); } else if(strcmp(str, "zstd") == 0) { loadsave_t::set_autosavemode(loadsave_t::zstd ); } else if(strcmp(str, "xml_zstd") == 0) { loadsave_t::set_autosavemode(loadsave_t::xml_zstd ); } loadsave_t::save_level = contents.get_int("save_level", loadsave_t::save_level ); loadsave_t::autosave_level = contents.get_int("autosave_level", loadsave_t::autosave_level ); /* * Default resolution */ disp_width = contents.get_int_clamped("display_width", disp_width, 0, 0x7FFF ); disp_height = contents.get_int_clamped("display_height", disp_height, 0, 0x7FFF ); fullscreen = contents.get_int_clamped("fullscreen", fullscreen, 0, 2 ); with_private_paks = contents.get_int("with_private_paks", with_private_paks)!=0; max_rail_convoi_length = contents.get_int_clamped("max_rail_convoi_length", max_rail_convoi_length, 1, 254); max_road_convoi_length = contents.get_int_clamped("max_road_convoi_length", max_road_convoi_length, 1, 254); max_ship_convoi_length = contents.get_int_clamped("max_ship_convoi_length", max_ship_convoi_length, 1, 254); max_air_convoi_length = contents.get_int_clamped("max_air_convoi_length", max_air_convoi_length, 1, 254); // note: no need to check for min_height < max_height, since -12 < 16 world_maximum_height = contents.get_int_clamped("world_maximum_height", world_maximum_height, 16, 127); world_minimum_height = contents.get_int_clamped("world_minimum_height", world_minimum_height, -127, -12); stop_halt_as_scheduled = contents.get_int("stop_halt_as_scheduled", stop_halt_as_scheduled); // Default pak file path if (set_objfilename && *contents.get("pak_file_path")) { set_pakdir(ltrim(contents.get("pak_file_path"))); } // FluidSynth MIDI parameters if( *contents.get("soundfont_filename") ) { env_t::soundfont_filename = ltrim(contents.get("soundfont_filename")); } env_t::pakset_tutorial_dir = ltrim( contents.get_string("pakset_tutorial_dir", env_t::pakset_tutorial_dir.c_str()) ); } // colour stuff can only be parsed when the graphic system has already started void settings_t::parse_colours(tabfile_t& simuconf) { tabfileobj_t contents; simuconf.read( contents ); env_t::default_window_title_color = contents.get_color("default_window_title_color", env_t::default_window_title_color, &env_t::default_window_title_color_rgb ); env_t::front_window_text_color = contents.get_color("front_window_text_color", env_t::front_window_text_color, &env_t::front_window_text_color_rgb ); env_t::bottom_window_text_color = contents.get_color("bottom_window_text_color", env_t::bottom_window_text_color, &env_t::bottom_window_text_color_rgb ); env_t::tooltip_color = contents.get_color("tooltip_background_color", env_t::tooltip_color, &env_t::tooltip_color_rgb ); env_t::tooltip_textcolor = contents.get_color("tooltip_text_color", env_t::tooltip_textcolor, &env_t::tooltip_textcolor_rgb ); env_t::cursor_overlay_color = contents.get_color("cursor_overlay_color", env_t::cursor_overlay_color, &env_t::cursor_overlay_color_rgb ); env_t::background_color = contents.get_color("background_color", env_t::background_color, &env_t::background_color_rgb ); env_t::bottom_window_darkness = contents.get_int("env_t::bottom_window_darkness", env_t::bottom_window_darkness); } int settings_t::get_name_language_id() const { int lang = translator::get_language( language_code_names ); if( lang == -1 ) { lang = translator::get_language(); } return lang; } sint64 settings_t::get_starting_money(sint16 const year) const { if( starting_money>0 ) { return starting_money; } // search entry with startingmoneyperyear[i].year > year int i; for( i=0; i<10; i++ ) { if(startingmoneyperyear[i].year!=0) { if (startingmoneyperyear[i].year>year) { break; } } else { // year is behind the latest given date return startingmoneyperyear[i>0 ? i-1 : 0].money; } } if (i==0) { // too early: use first entry return startingmoneyperyear[0].money; } else { // now: startingmoneyperyear[i-1].year <= year < startingmoneyperyear[i].year if (startingmoneyperyear[i-1].interpol) { // linear interpolation return startingmoneyperyear[i-1].money + (startingmoneyperyear[i].money-startingmoneyperyear[i-1].money) * (year-startingmoneyperyear[i-1].year) /(startingmoneyperyear[i].year-startingmoneyperyear[i-1].year ); } else { // no interpolation return startingmoneyperyear[i-1].money; } } } uint32 settings_t::get_locality_factor(sint16 const year) const { int i; for( i=0; i<10; i++ ) { if( locality_factor_per_year[i].year!=0 ) { if( locality_factor_per_year[i].year > year ) { break; } } else { // year is behind the latest given date return locality_factor_per_year[max(i-1,0)].factor; } } if( i==0 ) { // too early: use first entry return locality_factor_per_year[0].factor; } else { #if 0 // linear interpolation return locality_factor_per_year[i-1].factor + (locality_factor_per_year[i].factor-locality_factor_per_year[i-1].factor) * (year-locality_factor_per_year[i-1].year) /(locality_factor_per_year[i].year-locality_factor_per_year[i-1].year ); #else // exponential evaluation sint32 diff = (locality_factor_per_year[i].factor-locality_factor_per_year[i-1].factor); double a = exp((year-locality_factor_per_year[i-1].year)/20.0); double b = exp((locality_factor_per_year[i].year-locality_factor_per_year[i-1].year)/20.0); return locality_factor_per_year[i-1].factor + (sint32)((diff*(a-1.0))/(b-1.0) + 0.5); #endif } } /** * returns newest way-desc for road_timeline_t arrays * @param roads must be an array with at least @p num_roads elements, no range checks! */ static const way_desc_t *get_timeline_road_type( uint16 year, uint16 num_roads, road_timeline_t* roads) { const way_desc_t *desc = NULL; const way_desc_t *test; for( int i=0; iget_intro_year_month( ); } if( roads[i].retire==0 ) { // fill in real retire date roads[i].retire = test->get_retire_year_month( ); if( roads[i].retire==0 ) { roads[i].retire = NEVER; } } // find newest available ... if( year>=roads[i].intro && yearget_intro_year_month()get_intro_year_month() ) { desc = test; } } } } return desc; } way_desc_t const* settings_t::get_city_road_type(uint16 const year) { return get_timeline_road_type(year, num_city_roads, city_roads ); } uint16 settings_t::get_city_road_speed_limit(uint16 year) { uint16 limit = 50; sint32 last_year = -1; for (int i = 0; i < city_road_speed_limit_num; i += 2) { if (city_road_speed_limit[i] <= year && last_year < city_road_speed_limit[i]) { last_year = city_road_speed_limit[i]; limit = city_road_speed_limit[i + 1]; } } return limit; } way_desc_t const* settings_t::get_intercity_road_type(uint16 const year) { return get_timeline_road_type(year, num_intercity_roads, intercity_roads ); } void settings_t::copy_city_road(settings_t const& other) { num_city_roads = other.num_city_roads; for( int i=0; i<10; i++ ) { city_roads[i] = other.city_roads[i]; } } void settings_t::set_default_player_color(uint8 player_nr, uint8 color1, uint8 color2) { if (player_nr < MAX_PLAYER_COUNT) { default_player_color[player_nr][0] = color1 < 28 ? color1 : 255; default_player_color[player_nr][1] = color2 < 28 ? color2 : 255; } } // returns default player colors for new players void settings_t::set_player_color_to_default(player_t* const player) const { karte_ptr_t welt; uint8 color1 = default_player_color[player->get_player_nr()][0]; if( color1 == 255 ) { if( default_player_color_random ) { // build a vector with all colors minivec_tplall_colors1(28); for( uint8 i=0; i<28; i++ ) { all_colors1.append(i); } // remove all used colors for( uint8 i=0; iget_player(i); if( test_sp && player!=test_sp ) { uint8 rem = 1<<(player->get_player_color1()/8); if( all_colors1.is_contained(rem) ) { all_colors1.remove( rem ); } } else if( default_player_color[i][0]!=255 ) { uint8 rem = default_player_color[i][0]; if( all_colors1.is_contained(rem) ) { all_colors1.remove( rem ); } } } // now choose a random empty color color1 = pick_any(all_colors1); } else { color1 = player->get_player_nr(); } } uint8 color2 = default_player_color[player->get_player_nr()][1]; if( color2 == 255 ) { if( default_player_color_random ) { // build a vector with all colors minivec_tplall_colors2(28); for( uint8 i=0; i<28; i++ ) { all_colors2.append(i); } // remove color1 all_colors2.remove( color1/8 ); // remove all used colors for( uint8 i=0; iget_player(i); if( test_sp && player!=test_sp ) { uint8 rem = 1<<(player->get_player_color2()/8); if( all_colors2.is_contained(rem) ) { all_colors2.remove( rem ); } } else if( default_player_color[i][1]!=255 ) { uint8 rem = default_player_color[i][1]; if( all_colors2.is_contained(rem) ) { all_colors2.remove( rem ); } } } // now choose a random empty color color2 = pick_any(all_colors2); } else { color2 = player->get_player_nr() + 3; } } player->set_player_color( color1*8, color2*8 ); } simutrans-124.5/src/simutrans/dataobj/settings.h000066400000000000000000000602421520675460500220210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_SETTINGS_H #define DATAOBJ_SETTINGS_H #include #include "../simtypes.h" #include "../simconst.h" #include "ribi.h" class player_t; class loadsave_t; class tabfile_t; class way_desc_t; struct road_timeline_t { char name[64]; uint16 intro; uint16 retire; }; /** * Game settings * * @note The default values from this class should be kept in sync with the * default values from the default simuconf.tab located in simutrans/config/ * (same as for env_t) */ class settings_t { // these are the only classes, that are allowed to modify elements from settings_t // for all remaining special cases there are the set_...() routines friend class settings_general_stats_t; friend class settings_routing_stats_t; friend class settings_economy_stats_t; friend class settings_costs_stats_t; friend class settings_frame_t; friend class settings_climates_stats_t; friend class climate_gui_t; friend class welt_gui_t; public: typedef enum { HEIGHT_BASED = 0, HUMIDITY_BASED, MAP_BASED } climate_generate_t; private: sint32 size_x = 256; sint32 size_y = 256; sint32 map_number = 33; sint32 tourist_attractions = 16; sint32 factory_count = 12; sint32 electric_promille = 330; sint32 city_count = 16; sint32 mean_citizen_count = 1600; // town growth factors sint32 passenger_multiplier = 40; sint32 mail_multiplier = 20; sint32 goods_multiplier = 20; sint32 electricity_multiplier = 0; // Also there are size dependent factors (0=no growth) sint32 growthfactor_small = 400; sint32 growthfactor_medium = 200; sint32 growthfactor_large = 100; sint16 special_building_distance = 3; // distance between attraction to factory or other special buildings uint32 minimum_city_distance = 16; uint32 industry_increase = 2000; // add new factory (chain) when city grows to x * 2^n // percentage of routing sint16 factory_worker_percentage = 33; sint16 tourist_percentage = 16; // higher number: passengers are more evenly distributed around the map struct yearly_locality_factor_t { sint16 year; uint32 factor; }; yearly_locality_factor_t locality_factor_per_year[10]; // radius for factories sint16 factory_worker_radius = 77; sint32 factory_worker_minimum_towns = 1; ///< try to have at least a single town connected to a factory ... sint32 factory_worker_maximum_towns = 4; ///< ... but not more than 4 // number of periods for averaging the amount of arrived pax/mail at factories uint16 factory_arrival_periods = 4; // whether factory pax/mail demands are enforced bool factory_enforce_demand = true; uint16 station_coverage_size = 2; // the maximum length of each convoi uint8 max_rail_convoi_length = 24; uint8 max_road_convoi_length = 4; uint8 max_ship_convoi_length = 4; uint8 max_air_convoi_length = 1; /// At which level buildings generate traffic? sint32 traffic_level = 5; /** * the maximum and minimum allowed world height. */ sint8 world_maximum_height = 32; sint8 world_minimum_height = -12; /// @{ /// @ref set_default_climates climate_generate_t climate_generator = climate_generate_t::HEIGHT_BASED; sint16 groundwater = -2; sint16 winter_snowline = 7; ///< Not mediterranean sint16 climate_borders[MAX_CLIMATES][2]; sint8 climate_temperature_borders[5]; sint8 tropic_humidity = 75; sint8 desert_humidity = 65; ribi_t::ribi wind_direction = ribi_t::west; ///< Wind is coming from this direction. Must be single! (N/W/S/E) sint8 patch_size_percentage; // average size of a climate patch, if there are overlapping climates sint8 moisture; // how much increase of moisture per tile sint8 moisture_water; // how much increase of moisture per water tile /// @} double max_mountain_height = 160.0; // can be 0-160.0 double map_roughness = 0.6; // can be 0-1 // river stuff sint16 river_number = 16; sint16 min_river_length = 16; sint16 max_river_length = 256; // forest stuff uint8 forest_base_size = 36; // Minimal size of forests - map independent uint8 forest_map_size_divisor = 38; // The smaller it is, the larger are individual forests uint8 forest_count_divisor = 16; // The smaller it is, the more forest are generated uint16 forest_inverse_spare_tree_density = 5; // Determines how often are spare trees going to be planted (1 per x tiles on avg) uint8 max_no_of_trees_on_square = 3; // 2 - minimal usable, 3 good, 5 very nice looking uint16 tree_climates = 0; // bit set, if this climate is to be covered with trees entirely uint16 no_tree_climates = 0; // bit set, if this climate is to be void of random trees public: enum tree_distribution_t { TREE_DIST_NONE = 0, TREE_DIST_RANDOM = 1, TREE_DIST_RAINFALL = 2, TREE_DIST_COUNT }; // server message (if any) std::string motd = ""; private: uint16 tree_distribution = tree_distribution_t::TREE_DIST_RANDOM; /// height level until which lakes will appear. (absolute) sint8 lake_height = 8; // game mechanics uint8 allow_player_change = true; uint8 use_timeline = 2; sint16 starting_year = 1930; sint16 starting_month = 0; sint16 bits_per_month = 20; std::string filename = ""; bool beginner_mode = false; sint32 beginner_price_factor = 1500; // 1500 = +50% /* Industry supply model used. * 0 : Classic (no flow control?) * 1 : JIT Classic (maximum transit and storage limited) * 2 : JIT Version 2 (demand buffers with better consumption model) */ uint8 just_in_time = 1; // Overwritten by the value from env_t // default 0, will be incremented after each 90 degree rotation until 4 uint8 rotation = 0; sint16 origin_x = 0, origin_y = 0; // passenger manipulation factor (=16 about old value) sint32 passenger_factor = 16; sint16 min_factory_spacing = 6; sint16 max_factory_spacing = 40; sint16 max_factory_spacing_percentage = 0; /*no goods will put in route, when stored>gemax_storage and goods_in_transit*maximum_intransit_percentage/100>max_storage */ uint16 factory_maximum_intransit_percentage = 0; // crossconnect all factories (like OTTD and similar games) // These two values are overwritten if OTTD_LIKE is defined bool crossconnect_factories = false; sint16 crossconnect_factor = 33; sint32 stadtauto_duration = false; bool freeplay = false; sint64 starting_money = 20000000; struct yearmoney { sint16 year; sint64 money; bool interpol; }; yearmoney startingmoneyperyear[10]; uint16 num_city_roads = 0; road_timeline_t city_roads[10]; uint16 num_intercity_roads = 0; road_timeline_t intercity_roads[10]; // pairs of year,speed uint16 city_road_speed_limit_num = 0; uint16 city_road_speed_limit[20]; /** * Use numbering for stations? */ bool numbered_stations = false; /* maximum number of steps for breath search */ sint32 max_route_steps = 1000000; // maximum length for route search at signs/signals sint32 max_choose_route_steps = 200; // max steps for good routing sint32 max_hops = 2000; /* maximum number of steps for breath search */ sint32 max_transfers = 9; /* multiplier for steps on diagonal: * 1024: TT-like, factor 2, vehicle will be too long and too fast * 724: correct one, factor sqrt(2) */ uint16 pak_diagonal_multiplier = 724; // names of the stations ... char language_code_names[4]; // true, if the different capacities (passengers/mail/freight) are counted separately bool separate_halt_capacities = false; /** * payment is only for the distance that got shorter between target and start * three modes: * 0 = pay for travelled manhattan distance * 1 = pay for distance difference to next transfer stop * 2 = pay for distance to destination * 0 allows chaeting, but the income with 1 or two are much lower */ uint8 pay_for_total_distance = TO_PREVIOUS; /* if set, goods will avoid being routed over overcrowded stops */ bool avoid_overcrowding = false; /* if set, goods will not routed over overcrowded stations but rather try detours (if possible) */ bool no_routing_over_overcrowding = false; // lowest possible income with speedbonus (1000=1) default 125 sint32 bonus_basefactor = 125; // true, if this pak should be used with extensions (default=false) bool with_private_paks = false; /// what is the minimum clearance required under bridges /// read default from env_t, should be set in simmain.cc (taken from pak-set simuconf.tab) uint8 way_height_clearance = 2; // if true, you can buy obsolete stuff bool allow_buying_obsolete_vehicles = true; // vehicle value is decrease by this factor/1000 when a vehicle leaves the depot sint16 used_vehicle_reduction = 0; // some network things to keep client in sync uint32 random_counter = 0; // will be set when actually saving uint32 frames_per_second = 20; // only used in network mode uint32 frames_per_step = 4; // for network play uint32 server_frames_ahead = 4; // for network play bool drive_on_left = false; bool signals_on_left = false; // fraction of running costs charged for going on other players way sint32 way_toll_runningcost_percentage = 0; sint32 way_toll_waycost_percentage = 0; // true if transformers are allowed to built underground bool allow_underground_transformers = true; // true if companies can make ways public bool disable_make_way_public = false; // only for trains. If true, trains stop at the position designated in the schdule.. bool stop_halt_as_scheduled = false; // the big cost section public: sint32 maint_building = 5000; // normal building sint64 cst_multiply_dock = -50000; sint64 cst_multiply_station = -60000; sint64 cst_multiply_roadstop = -40000; sint64 cst_multiply_airterminal = -300000; sint64 cst_multiply_post = -30000; sint64 cst_multiply_headquarter = -100000; sint64 cst_depot_rail = -100000; sint64 cst_depot_road = -130000; sint64 cst_depot_ship = -250000; sint64 cst_depot_air = -500000; // cost to merge station uint32 allow_merge_distant_halt = 2; sint64 cst_multiply_merge_halt = -50000; // alter landscape sint64 cst_buy_land = -10000; sint64 cst_alter_land = -100000; sint64 cst_alter_climate = -100000; sint64 cst_set_slope = -250000; sint64 cst_found_city = -500000000; sint64 cst_multiply_found_industry = -2000000; sint64 cst_remove_tree = -10000; sint64 cst_multiply_remove_haus = -100000; sint64 cst_multiply_remove_field = -500000; sint64 cst_transformer = -250000; sint64 cst_maintain_transformer = -2000; // maintainance cost in months to make something public sint64 cst_make_public_months = 60; sint32 cst_kw_per_credit = 512; // default // costs for the way searcher sint32 way_count_straight = 1; // cost on existing way sint32 way_count_curve = 5; // diagonal curve sint32 way_count_double_curve = 10; sint32 way_count_90_curve = 30; sint32 way_count_slope = 20; sint32 way_count_tunnel = 16; sint32 way_count_no_way = 3; // slightly prefer existing ways sint32 way_count_avoid_crossings = 8; // prefer less system crossings sint32 way_count_leaving_way = 50; sint32 way_count_maximum = 2000; // limit for allowed ways (can be set lower to avoid covering the whole map with two clicks) uint32 way_max_bridge_len = 15; // 0 = empty, otherwise some value from simplay uint8 player_type[MAX_PLAYER_COUNT]; // how fast new AI will build something; value is copied from env_t uint32 default_ai_construction_speed = 8000; // player color suggestions for new games bool default_player_color_random = false; uint8 default_player_color[MAX_PLAYER_COUNT][2]; // remove dummy companies and remove password from abandoned companies uint16 remove_dummy_player_months = 6; uint16 unprotect_abandoned_player_months = 0; public: /** * If map is read from a heightfield, this is the name of the heightfield. * Set to empty string in order to avoid loading. */ std::string heightfield = ""; settings_t(); void rdwr(loadsave_t *file); void copy_city_road(settings_t const& other); // init from this file ... void parse_simuconf(tabfile_t& simuconf, sint16& disp_width, sint16& disp_height, sint16& fullscreen, bool set_pak); // init without screen parameters ... void parse_simuconf(tabfile_t& simuconf) { sint16 idummy = 0; std::string sdummy; parse_simuconf(simuconf, idummy, idummy, idummy, false); } void parse_colours(tabfile_t& simuconf); void set_size_x(sint32 g) {size_x=g;} void set_size_y(sint32 g) {size_y=g;} void set_size(sint32 x, sint32 y) {size_x = x; size_y=y;} sint32 get_size_x() const {return size_x;} sint32 get_size_y() const {return size_y;} sint32 get_map_number() const {return map_number;} void set_factory_count(sint32 d) { factory_count=d; } sint32 get_factory_count() const {return factory_count;} sint32 get_electric_promille() const {return electric_promille;} void set_tourist_attractions( sint32 n ) { tourist_attractions = n; } sint32 get_tourist_attractions() const {return tourist_attractions;} void set_city_count(sint32 n) {city_count=n;} sint32 get_city_count() const {return city_count;} void set_mean_citizen_count( sint32 n ) {mean_citizen_count = n;} sint32 get_mean_citizen_count() const {return mean_citizen_count;} void set_traffic_level(sint32 l) {traffic_level=l;} sint32 get_traffic_level() const {return traffic_level;} sint8 get_maximumheight() const { return world_maximum_height; } sint8 get_minimumheight() const { return world_minimum_height; } sint8 get_groundwater() const {return (sint8)groundwater;} double get_max_mountain_height() const {return max_mountain_height;} double get_map_roughness() const {return map_roughness;} uint16 get_station_coverage() const {return station_coverage_size;} uint8 get_max_rail_convoi_length() const {return max_rail_convoi_length;} uint8 get_max_road_convoi_length() const {return max_road_convoi_length;} uint8 get_max_ship_convoi_length() const {return max_ship_convoi_length;} uint8 get_max_air_convoi_length() const {return max_air_convoi_length;} void set_allow_player_change(char n) {allow_player_change=n;} uint8 get_allow_player_change() const {return allow_player_change;} void set_use_timeline(char n) {use_timeline=n;} uint8 get_use_timeline() const {return use_timeline;} void set_starting_year( sint16 n ) { starting_year = n; } sint16 get_starting_year() const {return starting_year;} void set_starting_month( sint16 n ) { starting_month = n; } sint16 get_starting_month() const {return starting_month;} sint16 get_bits_per_month() const {return bits_per_month;} void set_filename(const char *n) {filename=n;} const char* get_filename() const { return filename.c_str(); } bool get_beginner_mode() const {return beginner_mode;} void set_just_in_time(uint8 b) { just_in_time = b; } uint8 get_just_in_time() const {return just_in_time;} void rotate90() { rotation = (rotation+1)&3; set_size( size_y, size_x ); wind_direction = ribi_t::rotate90(wind_direction); } uint8 get_rotation() const { return rotation; } void set_origin_x(sint16 x) { origin_x = x; } void set_origin_y(sint16 y) { origin_y = y; } sint16 get_origin_x() const { return origin_x; } sint16 get_origin_y() const { return origin_y; } bool is_freeplay() const { return freeplay; } void set_freeplay( bool f ) { freeplay = f; } sint32 get_max_route_steps() const { return max_route_steps; } sint32 get_max_choose_route_steps() const { return max_choose_route_steps; } sint32 get_max_hops() const { return max_hops; } sint32 get_max_transfers() const { return max_transfers; } sint64 get_starting_money(sint16 year) const; sint16 get_special_building_distance() const { return special_building_distance; } sint16 get_min_factory_spacing() const { return min_factory_spacing; } sint16 get_max_factory_spacing() const { return max_factory_spacing; } sint16 get_max_factory_spacing_percent() const { return max_factory_spacing_percentage; } sint16 get_crossconnect_factor() const { return crossconnect_factor; } bool is_crossconnect_factories() const { return crossconnect_factories; } bool get_numbered_stations() const { return numbered_stations; } sint32 get_stadtauto_duration() const { return stadtauto_duration; } sint32 get_beginner_price_factor() const { return beginner_price_factor; } const way_desc_t *get_city_road_type( uint16 year ); const way_desc_t *get_intercity_road_type( uint16 year ); uint16 get_city_road_speed_limit(uint16 year); void set_pak_diagonal_multiplier(uint16 n) { pak_diagonal_multiplier = n; } uint16 get_pak_diagonal_multiplier() const { return pak_diagonal_multiplier; } int get_name_language_id() const; void set_name_language_iso( const char *iso ) { if (!iso) { language_code_names[0] = 0; language_code_names[1] = 0; } else { language_code_names[0] = iso[0]; language_code_names[1] = iso[1]; } language_code_names[2] = 0; } void set_player_type(uint8 i, uint8 t) { player_type[i] = t; } uint8 get_player_type(uint8 i) const { return player_type[i]; } bool is_separate_halt_capacities() const { return separate_halt_capacities ; } // allowed modes are 0,1,2 enum { TO_PREVIOUS = 0, TO_TRANSFER, TO_DESTINATION }; uint8 get_pay_for_total_distance_mode() const { return pay_for_total_distance ; } // do not take people to overcrowded destinations bool is_avoid_overcrowding() const { return avoid_overcrowding; } // do not allow routes over overcrowded destinations bool is_no_routing_over_overcrowding() const { return no_routing_over_overcrowding; } sint16 get_river_number() const { return river_number; } sint16 get_min_river_length() const { return min_river_length; } sint16 get_max_river_length() const { return max_river_length; } // true, if this pak should be used with extensions (default) void set_with_private_paks(bool b ) { with_private_paks = b; } bool get_with_private_paks() const { return with_private_paks; } sint32 get_passenger_factor() const { return passenger_factor; } // town growth stuff sint32 get_passenger_multiplier() const { return passenger_multiplier; } sint32 get_mail_multiplier() const { return mail_multiplier; } sint32 get_goods_multiplier() const { return goods_multiplier; } sint32 get_electricity_multiplier() const { return electricity_multiplier; } // Also there are size dependent factors (0=no growth) sint32 get_growthfactor_small() const { return growthfactor_small; } sint32 get_growthfactor_medium() const { return growthfactor_medium; } sint32 get_growthfactor_large() const { return growthfactor_large; } // percentage of passengers for different kinds of trips sint16 get_factory_worker_percentage() const { return factory_worker_percentage; } sint16 get_tourist_percentage() const { return tourist_percentage; } // radius from factories to get workers from towns (usually set to 77 but 1/8 of map size may be meaningful too) uint16 get_factory_worker_radius() const { return factory_worker_radius; } // any factory will be connected to at least this number of next cities uint32 get_factory_worker_minimum_towns() const { return factory_worker_minimum_towns; } void set_factory_worker_minimum_towns(uint32 n) { factory_worker_minimum_towns = n; } // any factory will be connected to not more than this number of next cities uint32 get_factory_worker_maximum_towns() const { return factory_worker_maximum_towns; } void set_factory_worker_maximum_towns(uint32 n) { factory_worker_maximum_towns = n; } // number of periods for averaging the amount of arrived pax/mail at factories uint16 get_factory_arrival_periods() const { return factory_arrival_periods; } // whether factory pax/mail demands are enforced bool get_factory_enforce_demand() const { return factory_enforce_demand; } uint16 get_factory_maximum_intransit_percentage() const { return factory_maximum_intransit_percentage; } uint32 get_locality_factor(sint16 year) const; // disallow using obsolete vehicles in depot bool get_allow_buying_obsolete_vehicles() const { return allow_buying_obsolete_vehicles; } // forest stuff uint8 get_forest_base_size() const { return forest_base_size; } uint8 get_forest_map_size_divisor() const { return forest_map_size_divisor; } uint8 get_forest_count_divisor() const { return forest_count_divisor; } uint16 get_forest_inverse_spare_tree_density() const { return forest_inverse_spare_tree_density; } uint8 get_max_no_of_trees_on_square() const { return max_no_of_trees_on_square; } uint16 get_tree_climates() const { return tree_climates; } uint16 get_no_tree_climates() const { return no_tree_climates; } tree_distribution_t get_tree_distribution() const { return (tree_distribution_t)tree_distribution; } void set_tree_distribution(tree_distribution_t value) { tree_distribution = value; } void set_default_climates(); sint8 get_climate_borders( sint8 climate, sint8 start_end ) const { return (sint8)climate_borders[climate][start_end]; } sint8 get_tropic_humidity() const { return tropic_humidity; } sint8 get_desert_humidity() const { return desert_humidity; } sint8 get_climate_temperature_borders( sint8 border ) const { return climate_temperature_borders[border]; } climate_generate_t get_climate_generator() const { return climate_generator; } void set_climate_generator(climate_generate_t generator) { climate_generator = generator; } sint8 get_moisture() const { return moisture; } sint8 get_moisture_water() const { return moisture_water; } sint16 get_winter_snowline() const { return winter_snowline; } sint8 get_lakeheight() const { return lake_height; } void set_lakeheight(sint8 h) { lake_height = h; } /// Wind is coming from this direction ribi_t::ribi get_wind_dir() const { return wind_direction; } ribi_t::ribi get_approach_dir() const { return wind_direction | ribi_t::rotate90(wind_direction); } sint8 get_patch_size_percentage() const { return patch_size_percentage; } uint32 get_industry_increase_every() const { return industry_increase; } void set_industry_increase_every( uint32 n ) { industry_increase = n; } uint32 get_minimum_city_distance() const { return minimum_city_distance; } sint16 get_used_vehicle_reduction() const { return used_vehicle_reduction; } void set_player_color_to_default( player_t *player ) const; void set_default_player_color(uint8 player_nr, uint8 color1, uint8 color2); // usually only used in network mode => no need to set them! uint32 get_random_counter() const { return random_counter; } uint32 get_frames_per_second() const { return frames_per_second; } uint32 get_frames_per_step() const { return frames_per_step; } uint32 get_server_frames_ahead() const { return server_frames_ahead; } bool is_drive_left() const { return drive_on_left; } bool is_signals_left() const { return signals_on_left; } sint32 get_way_toll_runningcost_percentage() const { return way_toll_runningcost_percentage; } sint32 get_way_toll_waycost_percentage() const { return way_toll_waycost_percentage; } sint32 get_bonus_basefactor() const { return bonus_basefactor; } bool get_allow_underground_transformers() const { return allow_underground_transformers; } bool get_disable_make_way_public() const { return disable_make_way_public; } uint32 get_allow_merge_distant_halt() const { return allow_merge_distant_halt; } uint16 get_remove_dummy_player_months() const { return remove_dummy_player_months; } uint16 get_unprotect_abandoned_player_months() const { return unprotect_abandoned_player_months; } uint8 get_way_height_clearance() const { return way_height_clearance; } void set_way_height_clearance( uint8 n ) { way_height_clearance = n; } uint32 get_default_ai_construction_speed() const { return default_ai_construction_speed; } void set_default_ai_construction_speed( uint32 n ) { default_ai_construction_speed = n; } bool get_stop_halt_as_scheduled() const { return stop_halt_as_scheduled; } void set_stop_halt_as_scheduled(bool b) { stop_halt_as_scheduled = b; } // some settigns are not to be saved in the global settings void reset_after_global_settings_reload(); sint64 get_make_public_months() const { return cst_make_public_months; } }; #endif simutrans-124.5/src/simutrans/dataobj/sve_cache.cc000066400000000000000000000110421520675460500222310ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "sve_cache.h" #include #include "../dataobj/loadsave.h" #include "../utils/simstring.h" #include "../pathes.h" #include "../sys/simsys.h" #include "../simversion.h" #include "../dataobj/environment.h" #include stringhashtable_tpl sve_cache_t::cached_info; sve_info_t::sve_info_t() : pak("") , mod_time(0) , file_size(0) , file_exists(false) { } sve_info_t::sve_info_t(const char *pak_, time_t mod_, sint32 fs) : pak("") , mod_time(mod_) , file_size(fs) { if(pak_) { pak = pak_; file_exists = true; } } bool sve_info_t::operator== (const sve_info_t &other) const { return (mod_time==other.mod_time) && (file_size == other.file_size) && (pak.compare(other.pak)==0); } void sve_info_t::rdwr(loadsave_t *file) { const char *s = strdup(pak.c_str()); file->rdwr_str(s); if (file->is_loading()) { pak = s ? s : ""; } free(const_cast(s)); file->rdwr_longlong(mod_time); file->rdwr_long(file_size); } void sve_cache_t::load_cache() { // load cached entries if (!cached_info.empty()) { // already loaded return; } loadsave_t file; // We rename the old cache file and remove any incomplete read version. // Upon an error the cache will be rebuilt then next time. dr_rename( SAVE_PATH_X "_cached.xml", SAVE_PATH_X "_load_cached.xml" ); if( file.rd_open(SAVE_PATH_X "_load_cached.xml") != loadsave_t::FILE_STATUS_OK ) { return; } // ignore comment const char *text = NULL; file.rdwr_str(text); bool ok = true; do { xml_tag_t t(&file, "save_game_info"); // first filename file.rdwr_str(text); if ((ok = !strempty(text))) { sve_info_t *svei = new sve_info_t(); svei->rdwr(&file); cached_info.put(text, svei); text = NULL; // it is used as key, do not delete it } } while (ok); if (text) { free(const_cast(text)); } file.close(); dr_rename( SAVE_PATH_X "_load_cached.xml", SAVE_PATH_X "_cached.xml" ); } void sve_cache_t::write_cache() { static const char *cache_file = SAVE_PATH_X "_cached.xml"; loadsave_t file; if( file.wr_open(cache_file, loadsave_t::xml, 0, "cache", SAVEGAME_VER_NR) != loadsave_t::FILE_STATUS_OK ) { return; } const char *text = "Automatically generated file. Do not edit. An invalid file may crash the game. Deleting is allowed though."; file.rdwr_str(text); for(auto const& i : cached_info) { // save only existing files if (i.value->file_exists) { xml_tag_t t(&file, "save_game_info"); char const* filename = i.key; file.rdwr_str(filename); i.value->rdwr(&file); } } // mark end with empty entry { xml_tag_t t(&file, "save_game_info"); text = ""; file.rdwr_str(text); } file.close(); } const char *sve_cache_t::get_info(const std::string &fname) { static char date[1024]; std::string pak_extension; // get file information struct stat sb; if (dr_stat(fname.c_str(), &sb) != 0) { // file not found? date[0] = 0; return date; } // check hash table sve_info_t *svei = cached_info.get(fname.c_str()); if (svei && svei->file_size == sb.st_size && svei->mod_time == sb.st_mtime) { // compare size and mtime // if both are equal then most likely the files are the same // no need to read the file for pak_extension pak_extension = svei->pak.c_str(); svei->file_exists = true; } else { // read pak_extension from file loadsave_t test; test.rd_open(fname.c_str()); // add pak extension pak_extension = test.get_pak_extension(); // now insert in hash_table sve_info_t *svei_new = new sve_info_t(pak_extension.c_str(), sb.st_mtime, sb.st_size ); // copy filename char *key = strdup(fname.c_str()); sve_info_t *svei_old = cached_info.set(key, svei_new); delete svei_old; } // write everything in string // add pak extension const size_t n = snprintf( date, lengthof(date), "%s - ", pak_extension.c_str()); // add the time too struct tm *tm = localtime(&sb.st_mtime); if(tm) { strftime(date+n, 18, "%Y-%m-%d %H:%M", tm); } else { tstrncpy(date, "??.??.???? ??:??", lengthof(date)); } date[lengthof(date)-1] = 0; return date; } std::string sve_cache_t::get_most_recent_compatible_save() { const char *best_filename = ""; sint64 best_time = 0; for (stringhashtable_tpl::iterator it = cached_info.begin(); it != cached_info.end(); ++it) { if (it->value->pak+PATH_SEPARATOR == env_t::pak_name && it->value->mod_time > best_time) { best_filename = it->key; best_time = it->value->mod_time; } } return best_filename; } simutrans-124.5/src/simutrans/dataobj/sve_cache.h000066400000000000000000000015311520675460500220750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_SVE_CACHE_H #define DATAOBJ_SVE_CACHE_H #include "../simtypes.h" #include "../tpl/stringhashtable_tpl.h" #include class loadsave_t; class sve_info_t { public: std::string pak; sint64 mod_time; sint32 file_size; bool file_exists; public: sve_info_t(); sve_info_t(const char *pak_, time_t mod_, sint32 fs); public: bool operator==(const sve_info_t &) const; void rdwr(loadsave_t *file); }; class sve_cache_t { static stringhashtable_tpl cached_info; public: static void load_cache(); static void write_cache(); // if the file is not already in the cache, it is added. static const char *get_info(const std::string &fname); static std::string get_most_recent_compatible_save(); }; #endif simutrans-124.5/src/simutrans/dataobj/tabfile.cc000066400000000000000000000521141520675460500217240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #ifdef MAKEOBJ #include "../descriptor/writer/obj_writer.h" #define dr_fopen fopen #endif #include "../sys/simsys.h" #include "../simdebug.h" #include "../descriptor/image.h" #include "koord.h" #include "tabfile.h" #define LINEBUFFER_SIZE (4096) bool tabfile_t::open(const char *filename) { close(); file = dr_fopen(filename, "r"); current_line_number = 0; return file != NULL; } bool tabfile_t::open(const std::string &filename) { return open(filename.c_str()); } void tabfile_t::close() { if(file) { fclose(file); file = NULL; } } const char *tabfileobj_t::get(const char *key) { obj_info_t *result = objinfo.access(key); if( result ) { result->retrieved = true; return result->str; } return ""; } /** * Get the string value for a key - key must be lowercase * @return def if key isn't found, value otherwise */ const char *tabfileobj_t::get_string(const char *key, const char * def) { obj_info_t *result = objinfo.access(key); if( result ) { result->retrieved = true; return result->str; } return def; } bool tabfileobj_t::put(const char *key, const char *value) { if(objinfo.get(key).str) { return false; } objinfo.put(strdup(key), obj_info_t(false,strdup(value)) ); return true; } void tabfileobj_t::clear() { #ifdef MAKEOBJ obj_writer_t::last_name = ""; #endif for(auto const& i : objinfo) { free(const_cast(i.key)); free(const_cast(i.value.str)); } objinfo.clear(); } // private helps to get x y value pairs needed for koord etc. template bool tabfileobj_t::get_x_y( const char *key, I &x, I &y ) { const char *value = get_string(key,NULL); const char *tmp; if(!value) { return false; } // 2. Determine value for(tmp = value; *tmp != ','; tmp++) { if(!*tmp) { return false; } } x = atoi(value); y = atoi(tmp + 1); return true; } const koord &tabfileobj_t::get_koord(const char *key, koord def) { static koord ret; ret = def; get_x_y( key, ret.x, ret.y ); return ret; } const scr_size &tabfileobj_t::get_scr_size(const char *key, scr_size def) { static scr_size ret; ret = def; get_x_y( key, ret.w, ret.h ); return ret; } PIXVAL tabfileobj_t::get_color(const char *key, PIXVAL def, rgb888_t *color_rgb) { const char *value = get_string(key,NULL); if(!value) { return def; } else { // skip spaces/tabs while ( *value>0 && *value<=32 ) { value ++; } #ifndef MAKEOBJ if( *value=='#' ) { const uint32 v = strtoul( value+1, NULL, 16 ) & 0xFFFFFFul; const rgb888_t col = { (uint8)(v >> 16), (uint8)(v >> 8), (uint8)(v >> 0) }; // we save in settings as RGB888 if (color_rgb) { *color_rgb = col; } // but the functions expect in the system colour (like RGB565) return get_system_color(col); } else { // this inputs also hex correct uint8 index = (uint8)strtoul( value, NULL, 0 ); // we save in settings as RGB888 if (color_rgb) { *color_rgb = gfx->get_color_rgb(index); } // but the functions expect in the system colour (like RGB565) return gfx->palette_lookup(index); } #else (void)color_rgb; return (uint8)strtoul( value, NULL, 0 ); #endif } } int tabfileobj_t::get_int(const char *key, int def) { const char *value = get_string(key,NULL); if(!value) { return def; } // skip spaces/tabs while ( *value>0 && *value<=32 ) { value ++; } // this inputs also hex correct return strtol( value, NULL, 0 ); } int tabfileobj_t::get_int_clamped(const char *key, int def, int min_value, int max_value) { const int int_value = get_int(key, def); const int clamped_value = clamp(int_value, min_value, max_value); if (clamped_value != int_value) { dbg->warning("tabfileobj_t::get_int_clamped()", "Value %d for key %s out of range %d..%d, resetting to %d", int_value, key, min_value, max_value, clamped_value); } return clamped_value; } sint64 atosint64(const char* a) { return (sint64)(atof(a)+0.5); } sint64 tabfileobj_t::get_int64(const char *key, sint64 def) { const char *value = get_string(key,NULL); if(!value) { return def; } else { return atosint64(value); } } vector_tpl tabfileobj_t::get_ints(const char *key) { const char *value = get_string(key,NULL); const char *tmp; int count = 0; vector_tpl result; if(!value) { return result; } // Determine number for(tmp = value; *tmp; tmp++) { if(*tmp == ',') { count++; } } // Create result vector and fill result.reserve(count); result.append(strtol( value, NULL, 0 )); for(tmp = value; *tmp; tmp++) { if(*tmp == ',') { // skip spaces/tabs do { tmp ++; } while ( *tmp>0 && *tmp<=32 ); // this inputs also hex correct result.append(strtol( tmp, NULL, 0 )); } } return result; } vector_tpl tabfileobj_t::get_sint64s(const char *key) { const char *value = get_string(key,NULL); const char *tmp; int count = 0; vector_tpl result; if(!value) { return result; } // Determine number for(tmp = value; *tmp; tmp++) { if(*tmp == ',') { count++; } } // Create result vector and fill result.reserve(count); result.append(atosint64(value)); for(tmp = value; *tmp; tmp++) { if(*tmp == ',') { result.append(atosint64(tmp + 1)); } } return result; } void tabfileobj_t::unused( const char *exclude_start_chars ) { for(auto const& i : objinfo) { if( !i.value.retrieved ) { // never retrieved if( !exclude_start_chars || !strchr( exclude_start_chars, *i.value.str ) ) { #ifdef MAKEOBJ dbg->warning( obj_writer_t::last_name, "Entry \"%s=%s\" ignored (check spelling)", i.key, i.value.str ); #else dbg->warning( "tabfile_t", "Entry \"%s=%s\" ignored (check spelling)", i.key, i.value.str ); #endif } objinfo.access( i.key )->retrieved = true; } } } static bool match_ribi(const char *p) { return (p[0] == '-' && (p[1] < '0' || p[1] > '9')) || std::tolower(p[0]) == 'n' || std::tolower(p[0]) == 'e' || std::tolower(p[0]) == 's' || std::tolower(p[0]) == 'w'; } bool tabfile_t::read(tabfileobj_t &objinfo, FILE *fp) { bool lines = false; char line[LINEBUFFER_SIZE]; char line_expand[LINEBUFFER_SIZE]; char delim_expand[LINEBUFFER_SIZE]; char buffer[LINEBUFFER_SIZE]; char *param[10]; char *expansion[10]; current_line_number = 0; objinfo.clear(); do { while(read_line(line, sizeof(line)) && *line != '-') { char *delim = strchr(line, '='); if(delim) { *delim++ = '\0'; format_key(line); if (line[0] == 0) { return false; } int parameters = 0; int expansions = 0; /* @line, the whole parameter text (everything before =) @delim, the whole value text (everything after =) @parameters, number of fields enclosed by square brackets [] @expansions, number of expansions included in the value (text inside angle brackets <>) @param, array containing the text inside each [] field @expansion, array containing the text inside each <> field */ if(find_parameter_expansion(line, delim, ¶meters, &expansions, param, expansion) > 0) { int parameter_value[10][256]; int parameter_length[10]; int parameter_values[10]; // number of possible 'values' inside each [] field | e.g. [0-4]=5 / [n,s,w]=3 char parameter_name[256][6]; // non-numeric ribis strings for all parameter fields consecutively bool parameter_ribi[10]; // true if parameters are ribi strings int combinations=1; int names = 0; // total number of ribi parameters // analyse and obtain all parameter expansions for(int i=0; i= 256) { dbg->fatal("tabfile_t::read", "Invalid number range %d-%d (Max range %d values) in line %d", start_range, end_range, 256 - parameter_values[i], current_line_number); } for(int range=start_range; range0) { // warp values around the number of parameters the expansion has for(int j=0; j0) { int expansion_length[10]; int expansion_value[10]; for(int i=0; i")-1; sprintf(buffer, "%.*s", expansion_length[i]+1, expansion[i]); expansion_value[i] = calculate(buffer, parameter_value, parameters, combination); } sprintf(delim_expand, "%.*s%d", (int)(expansion[0]-delim), delim, expansion_value[0]); for(int i=1; imessage("tabfile_t::read", "Parameter expansion: %s = %s", line_expand, delim_expand); objinfo.put(line_expand, delim_expand); if (fp != NULL) { fprintf(fp, "%s=%s\n", line_expand, delim_expand); } } } else { objinfo.put(line, delim); if (fp != NULL) { fprintf(fp, "%s=%s\n", line, delim); } } lines = true; } else if( *line ) { dbg->warning( "tabfile_t::read", "No data in \"%s\"", line ); } } } while(!lines && !feof(file)); // skip empty objects return lines; } bool tabfile_t::read_line(char *dest, size_t dest_size) { char *r; size_t l; do { current_line_number++; r = fgets(dest, dest_size, file); } while(r != NULL && (*dest == '#' || *dest == ' ') ); if(r) { l = strlen(r); while( l && (r[l-1] == '\n' || r[l-1] == '\r') ) { r[--l] = '\0'; } } return r != NULL; } int tabfile_t::find_parameter_expansion(char *key, char *data, int *parameters, int *expansions, char *param_ptr[10], char *expansion_ptr[10]) { // find params in key bool in_bracket = false; for(char *s = key; *s; s++) { if(*s == '[') { in_bracket = true; bool parameter = false; s++; char *t = s; while(*s && *s != ']') { if( (*s == ',' || *s == '-') && *(s-1) != '[' ) { parameter = true; } s++; } if(parameter) { if (*parameters >= 10) { dbg->error("tabfile_t::find_parameter_expansion", "Too many parameters (Only 10 allowed)"); return 0; } param_ptr[*parameters] = t; (*parameters)++; } } else if (*s == ']') { if (!in_bracket) { dbg->error("tabfile_t::find_parameter_expansion", "Found ']' before '['"); return 0; // invalid key } else { in_bracket = false; } } else if (!isalpha(*s) && !isdigit(*s) && *s != '_' && *s != '.') { // only allow [a-zA-Z0-9_.] in keys ('.' is required for city rules) dbg->error("tabfile_t::find_parameter_expansion", "Found invalid character '%c' in key of parameter expansion (Only alphanumeric characters, '.' and '_' allowed)", *s); return 0; } } // find expansions in data for(char *s = data; *s; s++) { if(*s == '<') { char *t = s; while(*s) { if(*s == '>') { if (*expansions >= 10) { dbg->error("tabfile_t::find_parameter_expansion", "Too many expansions (only $0 .. $9 allowed)"); return 0; } expansion_ptr[*expansions] = t; (*expansions)++; break; } s++; } } } return (*parameters)+(*expansions); } int tabfile_t::calculate(char *expression, const int (¶meter_value)[10][256], int parameters, int combination[10]) { char processed[LINEBUFFER_SIZE]; add_operator_parens(expression, processed); return calculate_internal(processed, parameter_value, parameters, combination, 0); } void tabfile_t::add_operator_parens(char *expression, char *processed) { char buffer[LINEBUFFER_SIZE]; char operators[5] = { '%','/','*','-','+' }; // first remove any spaces int j = 0; for(uint16 i = 0; i<=strlen(expression); i++) { if(expression[i]!=' ') { buffer[j++]=expression[i]; } } strcpy(processed, buffer); // add brackets around each operator to ensure processed in correct order // e.g. v+w*x+y/z becomes (((v+(w*x))+(y/z)) and x/y/z = ((x/y)/z) // for each operator take 'a operator b' and turn into '(a operator b)' for(size_t operator_loop = 0 ; operator_loop=processed && !expression_start) { switch(expression_pos[0]) { case ')': { int paren_level = 1; expression_start = expression_pos-1; while(expression_start>processed && paren_level>0) { switch(expression_start[0]) { case ')': paren_level++; break; case '(': paren_level--; break; } expression_start--; } if(paren_level>0) { dbg->fatal( "tabfile_t::add_operator_parens", "Mismatched parentheses in line %d", current_line_number ); } break; } case '%': case '/': case '*': case '+': case '-': case '(': case '<': expression_start = expression_pos; break; } expression_pos--; } if(expression_start==NULL) { expression_start = expression_pos; } // find b expression_pos = expression_ptr+1; char *expression_end = NULL; while(expression_pos[0]!='\0' && !expression_end) { switch(expression_pos[0]) { case '(': { int paren_level = 1; expression_end = expression_pos+1; while(expression_end[0]!='\0' && paren_level>0) { switch(expression_end[0]) { case '(': paren_level++; break; case ')': paren_level--; break; } expression_end++; } if(paren_level>0) { dbg->fatal( "tabfile_t::add_operator_parens", "Mismatched parentheses in line %d", current_line_number ); } break; } case '%': case '/': case '*': case '+': case '-': expression_end = expression_pos; break; } expression_pos++; } if(expression_end==NULL) { expression_end = expression_pos; } // construct expression with brackets around 'a operator b' const int prefix_len = (int)(expression_start-processed+1); const int lhs_len = (int)(expression_ptr-expression_start-1); const int rhs_len = (int)(expression_end-expression_ptr); const int suffix_len = strlen(expression_end); // make sure we have enough room if (prefix_len + 1 + lhs_len + rhs_len + 1 + suffix_len >= LINEBUFFER_SIZE-1) { dbg->fatal("tabfile_t::add_operator_parens", "Line too long to add operators in line %d", current_line_number); } sprintf(buffer,"%.*s(%.*s%.*s)%s", prefix_len, processed, lhs_len, expression_start+1, rhs_len, expression_ptr, expression_end); strcpy(processed, buffer); operator_count++; expression_ptr = strchr(processed, operators[operator_loop]); for(int i=0; ifatal("tabfile_t::calculate_internal", "Unable to tokenize '%s' in line %d", expression, current_line_number); } if(token_ptr[0]=='(') { token_ptr++; } while (token_ptr= 0 && parameter < parameters) { value = parameter_value[parameter][combination[parameter]]; } else { dbg->fatal("tabfile_t::calculate_internal", "Invalid reference to parameter $%d in line %d", parameter, current_line_number); } } else if(token_ptr[0]=='(') { size_t paren_expression_len; int paren_level=1; for(paren_expression_len=1; paren_expression_len0; paren_expression_len++) { switch(original[token_ptr-expression+paren_expression_len]) { case '(': paren_level++; break; case ')': paren_level--; break; } } // Note: We have to avoid too deep recursion here to avoid a stack overflow (each recursion adds > 8 kB to the stack) if (paren_expression_len >= LINEBUFFER_SIZE-1 || nest_level > 20) { dbg->fatal("tabfile_t::calculate_internal", "Cannot calculate expression (too nested or line too long) in line %d", current_line_number); } char buffer[LINEBUFFER_SIZE]; sprintf(buffer, "%.*s", (int)paren_expression_len, original+(token_ptr-expression)-1); value = calculate_internal(buffer+1, parameter_value, parameters, combination, nest_level+1); token_ptr += paren_expression_len; } else { value = atoi(token_ptr); } switch(operator_char) { case '+': answer += value; break; case '-': answer -= value; break; case '*': answer *= value; break; case '/': if (value == 0) { dbg->fatal("tabfile_t::calculate_internal", "Cannot divide by 0 in line %d", current_line_number); } answer /= value; break; case '%': if (value == 0) { dbg->fatal("tabfile_t::calculate_internal", "Cannot divide modulo 0 in line %d", current_line_number); } answer %= value; break; case '<': case '(': answer = value; break; } token_ptr += strcspn(token_ptr, "<-+*/%")+1; } return answer; } void tabfile_t::format_key(char *key) { char *s = key + strlen(key); char *t; // trim right while(s > key && s[-1] == ' ') { *--s = '\0'; } // make lowercase for(s = key; *s; s++) { *s = tolower(*s); } // skip spaces inside [] for(s = t = key; *s; s++) { if(*s == '[') { *t++ = *s++; while(*s && *s != ']') { if(*s == ' ') { s++; } else { *t++ = *s++; } } s--; } else { *t++ = *s; } } *t = '\0'; } simutrans-124.5/src/simutrans/dataobj/tabfile.h000066400000000000000000000117031520675460500215650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_TABFILE_H #define DATAOBJ_TABFILE_H #include #include "../simcolor.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/vector_tpl.h" class tabfileobj_t; class koord; class scr_coord; class scr_size; class obj_info_t { public: bool retrieved; const char *str; obj_info_t() { retrieved=false; str=0; } obj_info_t(bool b, const char *s ) { retrieved=b; str=s; } }; /** * This class can be used instead of FILE to read a game definition file, * usually with extension .tab in simutrans. * For the start only bridges.tab is read by this class. * Maybe we can make it a standard class for all tab-files, using the same * format in all. * * File format: * - Lines starting with '#' or ' ' are comment lines. * - The file content is treated as a list of objects. * - Objects are separated by a line starting with a dash (-) * - Each object can contain any number of lines in the format '<Key>=' * These line are NOT ordered * - If keys are duplicated for one object, the first value is used * - Keys are not case sensitive */ class tabfile_t { public: tabfile_t() : file(NULL) {} ~tabfile_t() { close(); } public: bool open(const char *filename); bool open(const std::string &filename); void close(); /** * Read an entire object from the open file. * * @param[out] objinfo will receive the object info * @param fp file * @return bool false, if empty object or eof */ bool read(tabfileobj_t &objinfo, FILE *fp = NULL); private: /** * Read one non-comment line from input. * Lines starting with ' ' are comment lines here. This differs from the * global read_line() function. * * @param dest line buffer * @param dest_size size of line buffer * * @returns false in case of eof */ bool read_line(char *dest, size_t dest_size); /** * Return parameters and expansions */ int find_parameter_expansion(char *key, char *data, int *parameters, int *expansions, char *parameter_ptr[10], char *expansion_ptr[10]); /** * Calculates expression provided in buffer, substituting parameters provided */ int calculate(char *expression, const int (¶meter_value)[10][256], int parameters, int combination[10]); /** * Adds brackets to expression to ensure calculate_internal processes expression correctly */ void add_operator_parens(char *expression, char *processed); /** * Calculates expression provided in buffer (do not call directly!) */ int calculate_internal(char *expression, const int (¶meter_value)[10][256], int parameters, int combination[10], int nest_level); /** * Format the key string (trimright and lowercase) */ void format_key(char *key); private: FILE *file; int current_line_number; }; /* * This class represents an object read from a tabfile_t. * It contains all strings key/value pairs read by tabfile_t::read(). * It may be reused for reading more objects. */ class tabfileobj_t { private: stringhashtable_tpl objinfo; template bool get_x_y( const char *key, I &x, I &y ); public: tabfileobj_t() { } ~tabfileobj_t() { clear(); } /** * prints all unused options lines in the file which do not start with a character from exclude_start_chars */ void unused( const char *exclude_start_chars ); /** * add a key/value pair - should only be used be tabfile_t::read */ bool put(const char *key, const char *value); /** * reinitializes this object */ void clear(); /** * Get the value for a key - key must be lowercase * * @return const char *returns at least an empty string, never NULL. */ const char *get(const char *key); /** * Get the string value for a key - key must be lowercase * @return def if key isn't found, value otherwise */ const char *get_string(const char *key, const char * def); /** * Get the value for a koord key - key must be lowercase * * @return def, if key is not found */ const koord &get_koord(const char *key, koord def); const scr_size &get_scr_size(const char *key, scr_size def); /** * Get a color in the system format when given a #AABBCC * and optionally set RGB888 for a chosen var with color_rgb */ PIXVAL get_color(const char *key, PIXVAL def, rgb888_t *color_rgb = NULL); /** * Get an int */ int get_int(const char *key, int def); /** * Get an int value. If the value is not between @p min_value and @p max_value, a warning * is emitted and the value is clamped to either @p min_value or @p max_value. */ int get_int_clamped(const char *key, int def, int min_value, int max_value); /** * Get an sint64 (actually uses double, thus only 48 bits are retrievable) */ sint64 get_int64(const char *key, sint64 def); /** * Parses a value with the format ",,..," * and returns a vector with these values. */ vector_tpl get_ints(const char *key); vector_tpl get_sint64s(const char *key); }; #endif simutrans-124.5/src/simutrans/dataobj/translator.cc000066400000000000000000000571161520675460500225160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include "../macros.h" #include "../simdebug.h" #include "../sys/simsys.h" #include "../simtypes.h" #include "../display/simgraph.h" // for unicode stuff #include "translator.h" #include "loadsave.h" #include "environment.h" #include "../simmem.h" #include "../utils/cbuffer.h" #include "../utils/searchfolder.h" #include "../utils/simstring.h" #include "../utils/unicode.h" #include "../tpl/vector_tpl.h" using std::string; // allow all kinds of line feeds static char *fgets_line(char *buffer, int max_len, FILE *file) { char *result = fgets(buffer, max_len, file); size_t len = strlen(buffer); // remove all trailing junk while( len>1 && (buffer[len-1]==13 || buffer[len-1]==10) ) { buffer[len-1] = 0; len--; } return result; } const char *translator::lang_info::translate(const char *text) const { if( text == NULL ) { return "(null)"; } if( text[0] == '\0' ) { return text; } const char *trans = texts.get(text); return trans != NULL ? trans : text; } /* Made to be dynamic, allowing any number of languages to be loaded */ static translator::lang_info langs[40]; static translator::lang_info *current_langinfo = langs; static stringhashtable_tpl compatibility; translator translator::single_instance; const translator::lang_info* translator::get_lang() { return current_langinfo; } const translator::lang_info* translator::get_langs() { return langs; } /* first two file functions needed in connection with utf */ /** * checks, if we need a unicode translation */ static bool is_unicode_file(FILE* f) { unsigned char str[2]; int pos = ftell(f); // DBG_DEBUG("is_unicode_file()", "checking for unicode"); // fflush(NULL); if (fread( str, 1, 2, f ) != 2) { return false; } // DBG_DEBUG("is_unicode_file()", "file starts with %x%x",str[0],str[1]); // fflush(NULL); if (str[0] == 0xC2 && str[1] == 0xA7) { // the first line must contain an UTF8 coded paragraph (Latin A7, UTF8 C2 A7), then it is unicode DBG_DEBUG("is_unicode_file()", "file is UTF-8"); return true; } if( str[0]==0xEF && str[1]==0xBB && fgetc(f)==0xBF ) { // the first letter is the byte order mark => may need to skip a paragraph (Latin A7, UTF8 C2 A7) pos = ftell(f); if (fread( str, 1, 2, f ) != 2) { return false; } if( str[0] != 0xC2 || str[1] == 0xA7 ) { fseek(f, pos, SEEK_SET); dbg->error( "is_unicode_file()", "file is UTF-8 but has no paragraph" ); } DBG_DEBUG("is_unicode_file()", "file is UTF-8"); return true; } fseek(f, pos, SEEK_SET); return false; } // recodes string to put them into the tables static char *recode(const char *src, bool translate_from_utf, bool translate_to_utf, bool is_latin2 ) { char *base; if( translate_to_utf != translate_from_utf ) { // worst case base = MALLOCN(char, strlen(src) * 2 + 2); } else { base = MALLOCN(char, strlen(src) + 2); } char *dst = base; uint8 c = 0; do { if (*src =='\\') { if (*(src + 1) == 0) { // backslash at end of line -> corrupted break; } src += 2; *dst++ = c = '\n'; } else { c = *src; if(c>127) { if( translate_from_utf == translate_to_utf ) { // but copy full letters! (or, if ASCII, copy more than one letter, does not matter do { *dst++ = *src++; } while (is_cont_char(*src)); c = *src; } else if( translate_to_utf ) { if( !is_latin2 ) { // make UTF8 from latin1 dst += c = utf16_to_utf8((unsigned char)*src++, (utf8*)dst); } else { dst += c = utf16_to_utf8( latin2_to_unicode( (uint8)*src++ ), (utf8*)dst ); } } else if( translate_from_utf ) { // make latin from UTF8 (ignore overflows!) const utf8 *p = reinterpret_cast(src); if( !is_latin2 ) { *dst++ = c = (uint8)utf8_decoder_t::decode(p); } else { *dst++ = c = unicode_to_latin2(utf8_decoder_t::decode(p)); } src = reinterpret_cast(p); } } else if(c>=13) { // just copy src ++; *dst++ = c; } else { // ignore this character src ++; } } } while (c != '\0'); *dst = 0; return base; } // List of custom city and streetnames vector_tpl translator::city_name_list; vector_tpl translator::street_name_list; // fills a list from a file with the given prefix followed by a language code void translator::load_custom_list( int lang, vector_tpl&name_list, const char *fileprefix ) { FILE *file; // Clean up all names for(char* const i : name_list) { free(i); } name_list.clear(); // first try in pakset { string local_file_name(env_t::user_dir); local_file_name = local_file_name + "addons/" + env_t::pak_name + "text/" + fileprefix + langs[lang].iso_base + ".txt"; DBG_DEBUG("translator::load_custom_list()", "try to read city name list from '%s'", local_file_name.c_str()); file = dr_fopen(local_file_name.c_str(), "rb"); } // not found => try user location if( file==NULL ) { string local_file_name(env_t::user_dir); local_file_name = local_file_name + fileprefix + langs[lang].iso_base + ".txt"; file = dr_fopen(local_file_name.c_str(), "rb"); DBG_DEBUG("translator::load_custom_list()", "try to read city name list from '%s'", local_file_name.c_str()); } // not found => try pak location if( file==NULL ) { string local_file_name(env_t::pak_dir + "text/" + fileprefix + langs[lang].iso_base + ".txt"); DBG_DEBUG("translator::load_custom_list()", "try to read city name list from '%s'", local_file_name.c_str()); file = dr_fopen(local_file_name.c_str(), "rb"); } // not found => try global translations if( file==NULL ) { string local_file_name(env_t::base_dir); local_file_name = local_file_name + "text/" + fileprefix + langs[lang].iso_base + ".txt"; DBG_DEBUG("translator::load_custom_list()", "try to read city name list from '%s'", local_file_name.c_str()); file = dr_fopen(local_file_name.c_str(), "rb"); } fflush(NULL); if (file != NULL) { // ok, could open file char buf[256]; bool file_is_utf = is_unicode_file(file); while( !feof(file) ) { if (fgets_line(buf, sizeof(buf), file)) { rtrim(buf); char *c = recode(buf, file_is_utf, true, langs[lang].is_latin2_based ); if( *c!=0 && *c!='#' ) { name_list.append(c); } } } fclose(file); DBG_DEBUG("translator::load_custom_list()","Loaded list %s_%s.txt.", fileprefix, langs[lang].iso_base ); } else { DBG_DEBUG("translator::load_custom_list()","No list %s_%s.txt found, using defaults.", fileprefix, langs[lang].iso_base ); } } /** * the city list is now reloaded after the language is changed * new cities will get their appropriate names */ void translator::init_custom_names(int lang) { // init names. There are two options: // // 1.) read list from file // 2.) create random names (only for cities) // try to read list load_custom_list( lang, city_name_list, "citylist_" ); load_custom_list( lang, street_name_list, "streetlist_" ); if (city_name_list.empty()) { DBG_MESSAGE("translator::init_city_names", "reading failed, creating random names."); // try to read list failed, create random names for( uint i = 0; i < 36; i++ ) { char name[32]; sprintf( name, "%%%c_CITY_SYLL", i+(i<10 ? '0' : 'A'-10 ) ); const char *s1 = translator::translate(name,lang); if(s1==name) { // name not available ... continue; } // now add all second name extensions ... const size_t l1 = strlen(s1); for( uint j = 0; j < 36; j++ ) { sprintf( name, "&%c_CITY_SYLL", j+(j<10 ? '0' : 'A'-10 ) ); const char *s2 = translator::translate(name,lang); if(s2==name) { // name not available ... continue; } const size_t l2 = strlen(s2); char *const c = MALLOCN(char, l1 + l2 + 1); sprintf(c, "%s%s", s1, s2); city_name_list.append(c); } } } } /* now on to the translate stuff */ static bool is_special_format_string(const char* str) { // %._CITY_SYLL if (*str == '%' && *(str+1) && strcmp(str+2, "_CITY_SYLL")==0) { return true; } // .center, .suburb, .extern if (*str && (strcmp(str+1, "center")==0 || strcmp(str+1, "suburb")==0 || strcmp(str+1, "extern")==0) ) { return true; } return false; } static void load_language_file_body(FILE* file, stringhashtable_tpl* table, bool language_is_utf, bool file_is_utf, bool language_is_latin2 ) { char buffer1 [4096]; char buffer2 [4096]; bool convert_to_unicode = language_is_utf && !file_is_utf; do { fgets_line(buffer1, sizeof(buffer1), file); if( buffer1[0] == '#' ) { // ignore comments continue; } if( !feof(file) ) { fgets_line(buffer2, sizeof(buffer2), file); if( strcmp(buffer1,buffer2) ) { // only add line which are actually different char *raw = recode(buffer1, file_is_utf, false, language_is_latin2 ); char *translated = recode(buffer2, false, convert_to_unicode,language_is_latin2); char *repaired = NULL; // check format strings (only for unicode, ignore special strings) if(language_is_utf) { if (!is_special_format_string(raw)) { // check and possibly repair the format string if (!cbuffer_t::check_and_repair_format_strings(raw, translated, &repaired) ) { free(raw); free(translated); continue; } } } if (repaired) { free(translated); translated = repaired; } table->set( raw, translated ); } } } while (!feof(file)); } void translator::load_language_file(FILE* file) { char buffer1[256]; bool file_is_utf = is_unicode_file(file); // Read language name fgets_line(buffer1, sizeof(buffer1), file); langs[single_instance.lang_count].name = strdup(buffer1); if( !file_is_utf ) { // find out the font if not unicode (and skip it) while( !feof(file) ) { fgets_line( buffer1, sizeof(buffer1), file ); if( buffer1[0] == '#' ) { continue; } if( strcmp(buffer1,"PROP_FONT_FILE") == 0 ) { fgets_line( buffer1, sizeof(buffer1), file ); // HACK: so we guess about latin2 from the font name! langs[single_instance.lang_count].is_latin2_based = STRNICMP( buffer1+5, "latin2", 6 )==0; // we must register now a unicode font langs[single_instance.lang_count].texts.set( "PROP_FONT_FILE", langs[single_instance.lang_count].is_latin2_based ? "cyr.bdf" : strdup(buffer1) ); break; } } } else { // since it is anyway UTF8 langs[single_instance.lang_count].is_latin2_based = false; } //load up translations, putting them into //language table of index 'lang' load_language_file_body(file, &langs[single_instance.lang_count].texts, true, file_is_utf, langs[single_instance.lang_count].is_latin2_based ); } static translator::lang_info* get_lang_by_iso(const char *iso) { for( translator::lang_info* i = langs; i != langs + translator::get_language_count(); ++i ) { if( i->iso_base[0] == iso[0] && i->iso_base[1] == iso[1] ) { return i; } } return NULL; } static uint32 get_highest_character( const utf8 *str ) { size_t len = 0; uint32 max_char = 0, symbol; do { symbol = utf8_decoder_t::decode( str, len ); str += len; if( symbol > max_char ) { max_char = symbol; } } while( symbol > 0 ); return max_char; } uint32 translator::guess_highest_unicode(int n) { const char* T1 = langs[n].texts.get( "Bruecke muss an\neinfachem\nHang beginnen!\n" ); uint32 max_char = 0xDF; if( T1 ) { max_char = get_highest_character( (const utf8 *)T1 ); } const char* T2 = langs[n].texts.get( "Start" ); if( T2 ) { uint32 max_char2 = get_highest_character( (const utf8 *)T2 ); max_char = max( max_char, max_char2 ); } return max_char; } void translator::load_files_from_folder(const char *folder_name, const char *what) { searchfolder_t folder; const int num_pak_lang_dat = folder.search(folder_name, "tab"); DBG_MESSAGE("translator::load_files_from_folder()", "search folder \"%s\" and found %i files", folder_name, num_pak_lang_dat); (void)num_pak_lang_dat; // read now the basic language infos // we allow either "LA.*.tab" or "*.LA.tab" whe LA is the ISO language code for(const char* const& filename : folder) { lang_info* lang = NULL; const char *filestr1 = strrchr(filename, '/'); const char *filestr2 = strrchr(filename, '\\'); const char *filestr = filestr1 > filestr2 ? filestr1 : filestr2; if (filestr) { if( filestr[3] == '.' ) { // try the start of the string first lang = get_lang_by_iso(filestr + 1); } } else if(filename[2] == '.') { // try the start of the filename if no path separator found lang = get_lang_by_iso(filename); } if (!lang) { const char *langcode = strrchr(filename, '.'); if( langcode ) { if( ((langcode - filename) > 3 && !isalnum(langcode[-3])) || (langcode - filename) == -2 ) { // try before the point lang = get_lang_by_iso(langcode - 2); } } } if (lang != NULL) { DBG_MESSAGE("translator::load_files_from_folder()", "loading %s translations from %s for language %s", what, filename, lang->iso_base); if (FILE* const file = dr_fopen(filename, "rb")) { bool file_is_utf = is_unicode_file(file); load_language_file_body(file, &lang->texts, true, file_is_utf, lang->is_latin2_based ); fclose(file); } else { dbg->warning("translator::load_files_from_folder()", "cannot open '%s'", filename); } } else { dbg->warning("translator::load_files_from_folder()", "%s no language '%s'", filename, what ); } } } bool translator::load() { //initialize these values to 0(ie. nothing loaded) single_instance.current_lang = -1; single_instance.lang_count = 0; DBG_MESSAGE("translator::load()", "Loading languages..."); dr_chdir( env_t::base_dir ); searchfolder_t folder; folder.search("text/", "tab"); //read now the basic language infos for (searchfolder_t::const_iterator i = folder.begin(), end = folder.end(); i != end; ++i) { const string fileName(*i); size_t pstart = fileName.rfind('/') + 1; const string iso = fileName.substr(pstart, fileName.size() - pstart - 4); if (FILE* const file = dr_fopen(fileName.c_str(), "rb")) { DBG_MESSAGE("translator::load()", "base file \"%s\" - iso: \"%s\"", fileName.c_str(), iso.c_str()); load_language_iso(iso); load_language_file(file); fclose(file); langs[single_instance.lang_count].highest_character = guess_highest_unicode( single_instance.lang_count ); single_instance.lang_count++; if (single_instance.lang_count == (int)lengthof(langs)) { if (++i != end) { // some languages were not loaded, let the user know what happened dbg->warning("translator::load()", "some languages were not loaded, limit reached"); for (; i != end; ++i) { dbg->warning("translator::load()", " %s not loaded", *i); } } break; } } } // now read the pakset specific text // there can be more than one file per language, provided it is name like iso_xyz.tab load_files_from_folder((env_t::pak_dir+"text"+PATH_SEPARATOR).c_str(), "pak"); if( env_t::default_settings.get_with_private_paks() ) { dr_chdir( env_t::user_dir ); // now read the pakset specific text // there can be more than one file per language, provided it is name like iso_xyz.tab const string folderName("addons/" + env_t::pak_name + "text/"); load_files_from_folder(folderName.c_str(), "pak addons"); dr_chdir( env_t::base_dir ); } //if NO languages were loaded then game cannot continue if (single_instance.lang_count < 1) { return false; } // now we try to read the compatibility stuff if (FILE* const file = dr_fopen((env_t::pak_dir + "compat.tab").c_str(), "rb")) { load_language_file_body(file, &compatibility, false, false, false ); DBG_MESSAGE("translator::load()", "pakset compatibility texts loaded."); fclose(file); } else { DBG_MESSAGE("translator::load()", "no pakset compatibility texts"); } // also addon compatibility ... if( env_t::default_settings.get_with_private_paks() ) { dr_chdir( env_t::user_dir ); if (FILE* const file = dr_fopen(string("addons/"+env_t::pak_name + "compat.tab").c_str(), "rb")) { load_language_file_body(file, &compatibility, false, false, false ); DBG_MESSAGE("translator::load()", "pakset addon compatibility texts loaded."); fclose(file); } dr_chdir( env_t::base_dir ); } // use english if available current_langinfo = get_lang_by_iso("en"); // it's all ok return true; } void translator::load_language_iso(const string &iso) { string base(iso); langs[single_instance.lang_count].iso = strdup(iso.c_str()); int loc = iso.find('_'); if (loc != -1) { base = iso.substr(0, loc); } langs[single_instance.lang_count].iso_base = strdup(base.c_str()); } void translator::set_language(int lang) { if( 0 <= lang && lang < single_instance.lang_count ) { single_instance.current_lang = lang; current_langinfo = langs+lang; env_t::language_iso = langs[lang].iso; current_langinfo->ellipsis_width = gfx->calc_text_width( translate("...") ); DBG_MESSAGE("translator::set_language()", "%s, unicode %d", langs[lang].name, true); } else { dbg->warning("translator::set_language()", "Out of bounds : %d", lang); } } // returns the id for this language or -1 if not there int translator::get_language(const char *iso) { for( int i = 0; i < single_instance.lang_count; i++ ) { const char *iso_base = langs[i].iso_base; if( iso_base[0] == iso[0] && iso_base[1] == iso[1] ) { return i; } } return -1; } bool translator::set_language(const char *iso) { for( int i = 0; i < single_instance.lang_count; i++ ) { const char *iso_base = langs[i].iso_base; if( iso_base[0] == iso[0] && iso_base[1] == iso[1] ) { set_language(i); return true; } } // if the request language does not exist if( single_instance.current_lang == -1 ) { for( int i = 0; i < single_instance.lang_count; i++ ) { const char* iso_base = langs[i].iso_base; if( iso_base[0] == 'e' && iso_base[1] == 'n' ) { set_language( i ); return false; } } // not even english found ... set_language(0); } return false; } const char *translator::translate(const char *str) { return get_lang()->translate(str); } const char *translator::translate(const char *str, int lang) { return langs[lang].translate(str); } const char* translator::get_obj_info(cbuffer_t &buf,const char *name) { buf.append(translator::translate(name)); buf.rtrim(); buf.append("\n\n"); // append extra info if it is a short name if (strlen(name) < 238) { char ei[256]; sprintf(ei, "obj_%s_details", name); const char* translated_ei = translate(ei); if (ei != translated_ei) { buf.append(translated_ei); buf.rtrim(); buf.append("\n\n"); } } return NULL; } const char *translator::get_month_name(uint16 month) { static const char *const month_names[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "Oktober", "November", "December" }; return translate(month_names[month % lengthof(month_names)]); } const char *translator::get_short_month_name(uint16 month) { static const char *const short_month_names[] = { "Jan.", "Feb.", "Mar.", "Apr.", "May", "June", "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec." }; return translate(short_month_names[month % lengthof(short_month_names)]); } const char *translator::get_date(uint16 year, uint16 month) { char const* const month_ = get_month_name(month); char const* const year_sym = strcmp("YEAR_SYMBOL", translate("YEAR_SYMBOL")) ? translate("YEAR_SYMBOL") : ""; static char sdate[256]; switch (env_t::show_month) { case env_t::DATE_FMT_JAPANESE: case env_t::DATE_FMT_JAPANESE_NO_SEASON: sprintf(sdate, "%4d%s %s", year, year_sym, month_); break; case env_t::DATE_FMT_GERMAN: case env_t::DATE_FMT_GERMAN_NO_SEASON: case env_t::DATE_FMT_US: case env_t::DATE_FMT_US_NO_SEASON: default: sprintf(sdate, "%s %4d%s", month_, year, year_sym); break; } return sdate; } const char *translator::get_date(uint16 year, uint16 month, uint16 day, char const* season) { char const* const month_ = get_month_name(month); char const* const year_sym = strcmp("YEAR_SYMBOL", translate("YEAR_SYMBOL")) ? translate("YEAR_SYMBOL") : ""; char const* const day_sym = strcmp("DAY_SYMBOL", translate("DAY_SYMBOL")) ? translate("DAY_SYMBOL") : ""; static char date[256]; switch(env_t::show_month) { case env_t::DATE_FMT_GERMAN: sprintf(date, "%s %d. %s %d%s ", season, day, month_, year, year_sym); break; case env_t::DATE_FMT_GERMAN_NO_SEASON: sprintf(date, "%d. %s %d%s ", day, month_, year, year_sym); break; case env_t::DATE_FMT_US: sprintf(date, "%s %s %d %d%s ", season, month_, day, year, year_sym); break; case env_t::DATE_FMT_US_NO_SEASON: sprintf(date, "%s %d %d%s ", month_, day, year, year_sym); break; case env_t::DATE_FMT_JAPANESE: sprintf(date, "%s %d%s %s %d%s ", season, year, year_sym, month_, day, day_sym); break; case env_t::DATE_FMT_JAPANESE_NO_SEASON: sprintf(date, "%d%s %s %d%s ", year, year_sym, month_, day, day_sym); break; case env_t::DATE_FMT_MONTH: sprintf(date, "%s, %s %d%s ", month_, season, year, year_sym); break; case env_t::DATE_FMT_SEASON: sprintf(date, "%s %d%s ", season, year, year_sym); break; } return date; } const char *translator::get_short_date(uint16 year, uint16 month) { char const* const month_ = get_short_month_name(month); char const* const year_sym = strcmp("YEAR_SYMBOL", translate("YEAR_SYMBOL")) ? translate("YEAR_SYMBOL") : ""; static char sdate[256]; switch (env_t::show_month) { case env_t::DATE_FMT_JAPANESE: case env_t::DATE_FMT_JAPANESE_NO_SEASON: sprintf(sdate, "%4d%s %s", year, year_sym , month_); break; case env_t::DATE_FMT_GERMAN: case env_t::DATE_FMT_GERMAN_NO_SEASON: case env_t::DATE_FMT_US: case env_t::DATE_FMT_US_NO_SEASON: default: sprintf(sdate, "%s %4d%s", month_, year, year_sym); break; } return sdate; } const char* translator::get_month_date( uint16 month, uint16 day ) { char const* const month_ = get_month_name( month ); char const* const day_sym = strcmp( "DAY_SYMBOL", translate( "DAY_SYMBOL" ) )?translate( "DAY_SYMBOL" ):""; static char date[256]; switch( env_t::show_month ) { case env_t::DATE_FMT_GERMAN: case env_t::DATE_FMT_GERMAN_NO_SEASON: sprintf( date, "%d. %s ", day, month_ ); break; case env_t::DATE_FMT_US: case env_t::DATE_FMT_US_NO_SEASON: sprintf( date, "%s %d ", month_, day ); break; case env_t::DATE_FMT_JAPANESE: case env_t::DATE_FMT_JAPANESE_NO_SEASON: sprintf( date, "%s %d%s", month_, day, day_sym ); break; case env_t::DATE_FMT_SEASON: case env_t::DATE_FMT_MONTH: sprintf( date, "%s, ", month_ ); break; } return date; } const char* translator::get_day_date(uint16 day) { char const* const day_sym = strcmp("ORDINAL_DAY_SYMBOL", translate("ORDINAL_DAY_SYMBOL")) ? translate("ORDINAL_DAY_SYMBOL") : "th "; static char date[256]; switch( env_t::show_month ) { case env_t::DATE_FMT_GERMAN: case env_t::DATE_FMT_GERMAN_NO_SEASON: sprintf( date, "%d%s", day, day_sym ); return translator::translate( date ); case env_t::DATE_FMT_US: case env_t::DATE_FMT_US_NO_SEASON: sprintf( date, "%d%s", day, day_sym ); return translator::translate( date ); case env_t::DATE_FMT_JAPANESE: case env_t::DATE_FMT_JAPANESE_NO_SEASON: sprintf( date, "%d%s", day, day_sym ); return translator::translate( date ); case env_t::DATE_FMT_SEASON: case env_t::DATE_FMT_MONTH: break; } return ""; } /* get a name for a non-matching object */ const char *translator::compatibility_name(const char *str) { if( str==NULL ) { return "(null)"; } if( str[0]=='\0' ) { return str; } const char *trans = compatibility.get(str); return trans != NULL ? trans : str; } simutrans-124.5/src/simutrans/dataobj/translator.h000066400000000000000000000101651520675460500223510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DATAOBJ_TRANSLATOR_H #define DATAOBJ_TRANSLATOR_H #include #include #include "../tpl/stringhashtable_tpl.h" #include "../tpl/vector_tpl.h" class cbuffer_t; /** * Central location for loading and translating language text for the * UI of Simutrans. * * The languages are 0 based index, with a valid range of(with lang being * required language): 0 <= lang < lang_count. */ class translator { private: //cannot be instantiated outside translator translator() { current_lang = -1; } int current_lang; int lang_count; /* The single instance that this class will use to gain access to * the member variables such as language names */ static translator single_instance; static uint32 guess_highest_unicode(int lang); /* Methods related to loading a language file into memory */ static void load_language_file(FILE* file); static void load_language_iso(const std::string &iso); static vector_tpl city_name_list; static vector_tpl street_name_list; static void load_custom_list( int lang, vector_tpl &name_list, const char *fileprefix ); public: struct lang_info { const char* translate(const char* text) const; stringhashtable_tpl texts; const char *name; const char *iso; const char *iso_base; bool is_latin2_based; uint32 highest_character; uint8 ellipsis_width; }; static void init_custom_names(int lang); static const vector_tpl &get_city_name_list() { return city_name_list; } static const vector_tpl &get_street_name_list() { return street_name_list; } /** * Loads up all files of language type from the 'language' directory. * This method must be called for languages to be loaded up, undefined * behaviour may follow if calls to translate message or similar are * called before load has been called */ static bool load(); /** * Loads all language file in folder folder_name * folder_name is relative to current dir (set by chdir) */ static void load_files_from_folder(const char* folder_name, const char* what); /** * Get/Set the currently selected language, based on the * index number */ static int get_language() { return single_instance.current_lang; } // returns the id for this language or -1 if not there static int get_language(const char* iso); /** Get information about the currently selected language */ static const lang_info* get_lang(); static const lang_info* get_langs(); /** * First checks to see whether the language is in bounds, will * then change what language is being used, otherwise prints * an error message, leaving the language as it is */ static void set_language(int lang); static bool set_language(const char* iso); /** * Returns the number of loaded languages. */ static int get_language_count() { return single_instance.lang_count; } /** * Translates a given string(key) to its locale * specific counterpart, using the current language * table. * the second variant just uses the language with the index * @return translated string, (null) if string is null, * or the string if the translation is not found */ static const char *translate(const char* str); static const char *translate(const char* str, int lang); /** * Fills the buffer with the obj name and detail translation (if there is any) */ static const char *get_obj_info(cbuffer_t &buf, const char *name); /** * @return replacement info for almost any object within the game */ static const char *compatibility_name(const char* str); // return the name of the month static const char *get_month_name(uint16 month); // return the short name of the month static const char *get_short_month_name(uint16 month); // return date in selected format static const char *get_date(uint16 year, uint16 month); static const char *get_date(uint16 year, uint16 month, uint16 day, char const* season); static const char* get_short_date(uint16 year, uint16 month); static const char* get_month_date(uint16 month, uint16 day); static const char* get_day_date( uint16 day ); }; #endif simutrans-124.5/src/simutrans/descriptor/000077500000000000000000000000001520675460500205565ustar00rootroot00000000000000simutrans-124.5/src/simutrans/descriptor/bridge_desc.cc000066400000000000000000000071101520675460500233160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "bridge_desc.h" #include "ground_desc.h" #include "../network/checksum.h" /** * Returns image index of a straight piece (excluding start pieces) */ bridge_desc_t::img_t bridge_desc_t::get_straight(ribi_t::ribi ribi, uint8 height) const { if( height>1 && get_background(NS_Segment2, 0)!=IMG_EMPTY ) { return (ribi & ribi_t::northsouth) ? NS_Segment2 : OW_Segment2; } else { return (ribi & ribi_t::northsouth) ? NS_Segment : OW_Segment; } } // ditto for pillars bridge_desc_t::img_t bridge_desc_t::get_pillar(ribi_t::ribi ribi) { return (ribi & ribi_t::northsouth) ? NS_Pillar : OW_Pillar; } /** * Returns image index of a straight bridge-start piece (on slope) */ bridge_desc_t::img_t bridge_desc_t::get_start(slope_t::type slope) const { // if double heights enabled and desc has 2 height images present then use these if( ground_desc_t::double_grounds && get_background(N_Start2, 0) != IMG_EMPTY ) { switch( slope ) { case slope_t::north: return N_Start; case slope_t::south: return S_Start; case slope_t::east: return O_Start; case slope_t::west: return W_Start; case slope_t::north * 2: return N_Start2; case slope_t::south * 2: return S_Start2; case slope_t::east * 2: return O_Start2; case slope_t::west * 2: return W_Start2; } } else { switch( slope ) { case slope_t::north: case slope_t::north * 2: return N_Start; case slope_t::south: case slope_t::south * 2: return S_Start; case slope_t::east: case slope_t::east * 2: return O_Start; case slope_t::west: case slope_t::west * 2: return W_Start; } } return (img_t) - 1; } /** * Returns image index of a ramp piece */ bridge_desc_t::img_t bridge_desc_t::get_ramp(slope_t::type slope) const { if( ground_desc_t::double_grounds && has_double_ramp() ) { switch( slope ) { case slope_t::north: return S_Ramp; case slope_t::south: return N_Ramp; case slope_t::east: return W_Ramp; case slope_t::west: return O_Ramp; case slope_t::north * 2: return S_Ramp2; case slope_t::south * 2: return N_Ramp2; case slope_t::east * 2: return W_Ramp2; case slope_t::west * 2: return O_Ramp2; } } else { switch( slope ) { case slope_t::north: case slope_t::north * 2: return S_Ramp; case slope_t::south: case slope_t::south * 2: return N_Ramp; case slope_t::east: case slope_t::east * 2: return W_Ramp; case slope_t::west: case slope_t::west * 2: return O_Ramp; } } return (img_t) - 1; } /** * returns image index for appropriate ramp or start image given ground and way slopes */ bridge_desc_t::img_t bridge_desc_t::get_end(slope_t::type test_slope, slope_t::type ground_slope, slope_t::type way_slope) const { img_t end_image; if( test_slope == slope_t::flat ) { end_image = get_ramp( way_slope ); } else { end_image = get_start( ground_slope ); } return end_image; } /** * returns whether desc has double height images for ramps */ bool bridge_desc_t::has_double_ramp() const { return (get_background(bridge_desc_t::N_Ramp2, 0)!=IMG_EMPTY || get_foreground(bridge_desc_t::N_Ramp2, 0)!=IMG_EMPTY); } bool bridge_desc_t::has_double_start() const { return (get_background(bridge_desc_t::N_Start2, 0) != IMG_EMPTY || get_foreground(bridge_desc_t::N_Start2, 0) != IMG_EMPTY); } void bridge_desc_t::calc_checksum(checksum_t *chk) const { obj_desc_transport_infrastructure_t::calc_checksum(chk); chk->input(pillars_every); chk->input(pillars_asymmetric); chk->input(max_length); chk->input(max_height); } simutrans-124.5/src/simutrans/descriptor/bridge_desc.h000066400000000000000000000074701520675460500231710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_BRIDGE_DESC_H #define DESCRIPTOR_BRIDGE_DESC_H #include "skin_desc.h" #include "image_list.h" #include "text_desc.h" #include "../simtypes.h" #include "../display/simimg.h" #include "../dataobj/ribi.h" class tool_t; class checksum_t; /* * BEWARE: non-standard node structure! * 0 Foreground-images * 1 Background-images * 2 Cursor/Icon * 3 Foreground-images - snow * 4 Background-images - snow */ class bridge_desc_t : public obj_desc_transport_infrastructure_t { friend class bridge_reader_t; private: uint8 pillars_every = 0; // =0 off bool pillars_asymmetric = false; // =0 off else leave one off for north/west slopes uint32 offset = 0; // flag, because old bridges had their name/copyright at the wrong position uint8 max_length = 0; // =0 off, else maximum length uint8 max_height = 0; // =0 off, else maximum length sint8 number_of_seasons = 0; // 0 = no seasons bool clip_below; // default is true public: /* * Numbering of all image pieces */ enum img_t { NS_Segment, OW_Segment, N_Start, S_Start, O_Start, W_Start, N_Ramp, S_Ramp, O_Ramp, W_Ramp, NS_Pillar, OW_Pillar, NS_Segment2, OW_Segment2, N_Start2, S_Start2, O_Start2, W_Start2, N_Ramp2, S_Ramp2, O_Ramp2, W_Ramp2, NS_Pillar2, OW_Pillar2 }; /* * Name and Copyright used to be saved only with the Cursor */ const char *get_name() const { return get_cursor()->get_name(); } const char *get_copyright() const { return get_cursor()->get_copyright(); } skin_desc_t const* get_cursor() const { return get_child(2 + offset); } image_id get_background(img_t img, uint8 season) const { const image_t *image = NULL; if(season && number_of_seasons == 1) { if (const image_list_t* il = get_child(3 + offset)) { image = il->get_image(img); } } if(image == NULL) { if (const image_list_t* il = get_child(0 + offset)) { image = il->get_image(img); } } return image != NULL ? image->get_id() : IMG_EMPTY; } image_id get_foreground(img_t img, uint8 season) const { const image_t *image = NULL; if(season && number_of_seasons == 1) { if (const image_list_t* il = get_child(4 + offset)) { image = il->get_image(img); } } if(image == NULL) { if (const image_list_t* il = get_child(1 + offset)) { image = il->get_image(img); } } return image != NULL ? image->get_id() : IMG_EMPTY; } bool is_clip_below() const { return clip_below; } img_t get_straight(ribi_t::ribi ribi, uint8 height) const; img_t get_start(slope_t::type slope) const; img_t get_ramp(slope_t::type slope) const; static img_t get_pillar(ribi_t::ribi ribi); /** * @return true if this bridge can raise two level from flat terrain */ bool has_double_ramp() const; /** * @return true if this bridge can start or end on a double slope */ bool has_double_start() const; img_t get_end(slope_t::type test_slope, slope_t::type ground_slope, slope_t::type way_slope) const; /** * There is no way to distinguish between train bridge and tram bridge. * However there are no real tram bridges possible in the game. */ waytype_t get_finance_waytype() const { return get_waytype(); } /** * Distance of pillars (=0 for no pillars) */ uint8 get_pillar() const { return pillars_every; } /** * skips lowest pillar on south/west slopes? */ bool has_pillar_asymmetric() const { return pillars_asymmetric; } /** * maximum bridge span (=0 for infinite) */ uint8 get_max_length() const { return max_length; } /** * maximum bridge height (=0 for infinite) */ uint8 get_max_height() const { return max_height; } void calc_checksum(checksum_t *chk) const; }; #endif simutrans-124.5/src/simutrans/descriptor/building_desc.cc000066400000000000000000000173731520675460500236730ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "../world/simworld.h" #include "../obj/way/weg.h" #include "building_desc.h" #include "intro_dates.h" #include "../network/checksum.h" /** * Calculate which layout the tile belongs to from the index. */ uint8 building_tile_desc_t::get_layout() const { koord size = get_desc()->get_size(); return index / (size.x * size.y); } /** * Return the relative position of an image in the whole building image */ koord building_tile_desc_t::get_offset() const { const building_desc_t *desc = get_desc(); koord size = desc->get_size(get_layout()); // rotate if necessary return koord( index % size.x, (index / size.x) % size.y ); } waytype_t building_desc_t::get_finance_waytype() const { switch( get_type() ) { case dock: return water_wt; case flat_dock: return water_wt; case depot: case generic_stop: case generic_extension: return (waytype_t) get_extra(); default: return ignore_wt; } } /** * Mail generation level */ uint16 building_desc_t::get_mail_level() const { switch (type) { default: case city_res: return level; case city_com: return level * 2; case city_ind: return level / 2; } } /** * true, if this building needs a connection with a town */ bool building_desc_t::is_connected_with_town() const { switch(get_type()) { case city_res: case city_com: case city_ind: // normal town buildings (RES, COM, IND) case monument: // monuments case townhall: // townhalls case headquarters: // headquarters return true; default: return false; } } /** * Returns the correct tile image on that position depending on the layout */ const building_tile_desc_t *building_desc_t::get_tile(uint8 layout, sint16 x, sint16 y) const { layout = adjust_layout(layout); koord dims = get_size(layout); if( x < 0 || y < 0 || layout >= layouts || x >= get_x(layout) || y >= get_y(layout) ) { dbg->fatal("building_tile_desc_t::get_tile()", "invalid request for l=%d, x=%d, y=%d on building %s (l=%d, x=%d, y=%d)", layout, x, y, get_name(), layouts, size.x, size.y); } return get_tile(layout * dims.x * dims.y + y * dims.x + x); } /** * Layout normalisation. Returns number of different layouts */ uint8 building_desc_t::adjust_layout(uint8 layout) const { if(layout >= 4 && layouts <= 4) { layout -= 4; } if(layout >= 2 && layouts <= 2) { // if layouts C and D are not defined, we use A and B as substitutes layout -= 2; } if(layout > 0 && layouts <= 1) { // if layout B is not defined and the building is squared, we us A as substitute if(size.x == size.y) { layout--; } } return layout; } void building_desc_t::calc_checksum(checksum_t *chk) const { obj_desc_timelined_t::calc_checksum(chk); #if MSG_LEVEL>0 switch(type) { case city_res: PAKSET_INFO("type=","res"); break; case city_com: PAKSET_INFO("type=","com"); break; case city_ind: PAKSET_INFO("type=","ind"); break; case attraction_land: case attraction_city: PAKSET_INFO("type=","cur"); break; case townhall: PAKSET_INFO("type=","tow"); break; case monument: PAKSET_INFO("type=","mon"); break; case headquarters: PAKSET_INFO("type=","hq"); break; case dock: PAKSET_INFO("type=","harbour"); break; case flat_dock: PAKSET_INFO("type=","dock"); break; case factory: break; case generic_stop: PAKSET_INFO("type=","stop"); break; case generic_extension: PAKSET_INFO("type=","extension"); break; case depot: PAKSET_INFO("type=","depot"); break; case others: PAKSET_INFO("type=","any"); break; default: break; } // extra_data and level switch(type) { case city_res: case city_com: case city_ind: if(extra_data) PAKSET_INFO("clusters=","%d",extra_data); // fallthrough case attraction_land: case factory: if(preservation_year_month!=DEFAULT_RETIRE_YEAR*12) { PAKSET_INFO("preservation_year=","%d",preservation_year_month/12); if(preservation_year_month%12) PAKSET_INFO("preservation_month=","%d",(preservation_year_month%12)+1); } //PAKSET_INFO("level=","%d",level); break; case attraction_city: case townhall: PAKSET_INFO("passengers=","%d",level); PAKSET_INFO("build_time=","%d",extra_data); break; case monument: PAKSET_INFO("passengers=","%d",level); break; case headquarters: PAKSET_INFO("passengers=","%d",level); PAKSET_INFO("hq_level=","%d",extra_data); break; case dock: case flat_dock: case generic_stop: case generic_extension: PAKSET_INFO("capacity=","%d",capacity); if(enables&1) PAKSET_INFO("enables_pax=","1"); if(enables&2) PAKSET_INFO("enables_post=","1"); if(enables&4) PAKSET_INFO("enables_ware=","1"); if(allow_underground) PAKSET_INFO("allow_underground=","1"); // fallthrough case depot: PAKSET_INFO("waytype=","%s",weg_t::waytype_to_string((waytype_t)extra_data)); break; case others: default: break; } if(distribution_weight!=100 && type!=factory) PAKSET_INFO("chance=","%d",distribution_weight); if(animation_time!=300) PAKSET_INFO("animation_time=","%d",animation_time); if(size.x*size.y*layouts>1) PAKSET_INFO("dims=","%d,%d,%d",size.x,size.y,layouts); if(flags&FLAG_NO_INFO) PAKSET_INFO("noinfo=","1"); if(flags&FLAG_NO_PIT) PAKSET_INFO("noconstruction=","1"); if(flags&FLAG_NEED_GROUND) PAKSET_INFO("needs_ground=","1"); // climates are missing ... #endif chk->input((uint8)type); chk->input(animation_time); chk->input(extra_data); chk->input(size.x); chk->input(size.y); chk->input((uint8)flags); chk->input(level); chk->input(layouts); chk->input(enables); chk->input(distribution_weight); chk->input((uint8)allowed_climates); chk->input(maintenance); chk->input(price); chk->input(capacity); chk->input(allow_underground); // now check the layout for(uint8 i=0; ihas_image()) { chk->input((sint16)(x+y+i)); } } } } chk->input(preservation_year_month); } /** * get functions - see building_desc.h for variable information */ sint32 building_desc_t::get_maintenance(karte_t *world) const { if( maintenance == PRICE_MAGIC ) { return world->get_settings().maint_building*get_level(); } else { return maintenance; } } sint32 building_desc_t::get_price(karte_t *world) const { if( price == PRICE_MAGIC ) { settings_t const& s = world->get_settings(); switch (get_type()) { case dock: case flat_dock: return -s.cst_multiply_dock * get_level(); case generic_extension: return -s.cst_multiply_post * get_level(); case generic_stop: switch(get_extra()) { case road_wt: return -s.cst_multiply_roadstop * get_level(); case track_wt: case monorail_wt: case maglev_wt: case narrowgauge_wt: case tram_wt: return -s.cst_multiply_station * get_level(); case water_wt: return -s.cst_multiply_dock * get_level(); case air_wt: return -s.cst_multiply_airterminal * get_level(); case 0: return -s.cst_multiply_post * get_level(); default: return 0; } case depot: switch(get_extra()) { case road_wt: return -s.cst_depot_road; case track_wt: case monorail_wt: case tram_wt: case maglev_wt: case narrowgauge_wt: return -s.cst_depot_rail; case water_wt: return -s.cst_depot_ship; case air_wt: return -s.cst_depot_air; default: return 0; } case headquarters: return -s.cst_multiply_headquarter * get_level(); default: return -s.cst_multiply_remove_haus * get_level(); } } else { return price; } } simutrans-124.5/src/simutrans/descriptor/building_desc.h000066400000000000000000000240151520675460500235240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_BUILDING_DESC_H #define DESCRIPTOR_BUILDING_DESC_H #include #include "image_array.h" #include "obj_base_desc.h" #include "skin_desc.h" #include "../dataobj/koord.h" class building_desc_t; class tool_t; class karte_t; class checksum_t; /** * Data for one tile of a potentially multi-tile building. * * Child nodes: * 0 Imagelist2D season 0 back * 1 Imagelist2D season 0 front * 2 Imagelist2D season 1 back * 3 Imagelist2D season 1 front * ... ... */ class building_tile_desc_t : public obj_desc_t { friend class tile_reader_t; const building_desc_t *building; uint8 seasons; uint8 phases; ///< number of animation phases uint16 index; public: void set_desc(const building_desc_t *building_desc) { building = building_desc; } const building_desc_t *get_desc() const { return building; } uint16 get_index() const { return index; } uint8 get_seasons() const { return seasons; } uint8 get_phases() const { return phases; } bool has_image() const { return get_background(0,0,0)!=IMG_EMPTY || get_foreground(0,0)!=IMG_EMPTY; } image_id get_background(uint16 phase, uint16 height, uint8 season) const { image_array_t const* const imglist = get_child(0 + 2 * season); if(phase>0 && phaseget_image(height, phase)) { return image->get_id(); } } // here if this phase does not exists ... image_t const* const image = imglist->get_image(height, 0); return image != NULL ? image->get_id() : IMG_EMPTY; } // returns true, if the background is animated bool is_background_animated(uint8 season) const { image_array_t const* const imglist = get_child(0 + 2 * season); const uint16 max_h = imglist->get_count(); for( uint16 phase=1; phaseget_image( h, phase ) ) { return true; } } } return false; } image_id get_foreground(uint16 phase, uint8 season) const { image_array_t const* const imglist = get_child(1 + 2 * season); if(phase>0 && phaseget_image(0, phase)) { return image->get_id(); } } // here if this phase does not exists ... image_t const* const image = imglist->get_image(0, 0); return image != NULL ? image->get_id() : IMG_EMPTY; } koord get_offset() const; uint8 get_layout() const; }; /** * Data for one building, consists of potentially more than one tile. * * Child nodes: * 0 Name * 1 Copyright * 2 Tile 1 * 3 Tile 2 * ... ... */ class building_desc_t : public obj_desc_timelined_t { friend class building_reader_t; public: /** * Building types */ enum btype { unknown = 0, attraction_city = 1, attraction_land = 2, monument = 3, factory = 4, townhall = 5, others = 6, ///< monorail foundation headquarters = 7, dock = 11, ///< dock, build on sloped coast // in these, the extra data points to a waytype depot = 33, generic_stop = 34, generic_extension = 35, flat_dock = 36, ///< dock, but can start on a flat coast line // city buildings city_res = 37, ///< residential city buildings city_com = 38, ///< commercial city buildings city_ind = 39 ///< industrial city buildings }; enum flag_t { FLAG_NULL = 0, FLAG_NO_INFO = 1 << 0, ///< do not show info window FLAG_NO_PIT = 1 << 1, ///< do not show construction pit FLAG_NEED_GROUND = 1 << 2, ///< needs ground drawn below FLAG_HAS_CURSOR = 1 << 3 ///< there is cursor/icon for this }; private: /** * Old named constants, only used for compatibility to load very old paks. * These will be converted in building_reader_t::register_obj to a valid btype. */ enum old_building_types_t { bahnhof = 8, bushalt = 9, ladebucht = 10, binnenhafen = 12, airport = 13, monorailstop = 14, bahnhof_geb = 16, bushalt_geb = 17, ladebucht_geb = 18, hafen_geb = 19, binnenhafen_geb = 20, airport_geb = 21, monorail_geb = 22, wartehalle = 30, mail = 31, lagerhalle = 32 }; building_desc_t::btype type; uint16 animation_time; // in ms uint32 extra_data; // extra data: // minimum population to build for city attractions, // waytype for depots // player level for headquarters // cluster number for city buildings (0 means no clustering) koord size; flag_t flags; uint16 level; // or passengers; uint8 layouts; // 1 2, 4, 8 or 16 uint8 enables; // if it is a stop, what is enabled ... uint8 distribution_weight; // chance to build, special buildings, only other is weight factor /** * Additional fields for separate capacity/maintenance * If these are not specified in the .dat file, they are set to * PRICE_MAGIC then calculated from the "level" in the old way. */ sint32 price; sint32 maintenance; uint16 capacity; #define PRICE_MAGIC (2147483647) climate_bits allowed_climates; /** * Whether this building can or must be built underground. * Only relevant for stations (generic_stop). * 0 = cannot be built underground * 1 = can only be built underground * 2 = can be built either underground or above ground. */ uint8 allow_underground; uint16 preservation_year_month; bool is_type(building_desc_t::btype u) const { return type == u; } tool_t *builder; public: koord get_size(uint8 layout = 0) const { return (layout & 1) ? koord(size.y, size.x) : size; } // size of the building sint16 get_y(uint8 layout = 0) const { return (layout & 1) ? size.x: size.y; } sint16 get_x(uint8 layout = 0) const { return (layout & 1) ? size.y : size.x; } sint16 get_area() const { return size.x * size.y; } uint8 get_all_layouts() const { return layouts; } uint32 get_extra() const { return extra_data; } /** Returns waytype used for finance stats (distinguishes between tram track and train track) */ waytype_t get_finance_waytype() const; // ground is transparent bool needs_ground() const { return (flags & FLAG_NEED_GROUND) != 0; } // no construction stage bool no_construction_pit() const { return (flags & FLAG_NO_PIT) != 0; } // do not open info for this bool no_info_window() const { return (flags & FLAG_NO_INFO) != 0; } // never replace this building for renovation (to create historic city centres) uint16 no_renovation_month() const { return preservation_year_month; } building_desc_t::btype get_type() const { return type; } bool is_townhall() const { return is_type(townhall); } bool is_headquarters() const { return is_type(headquarters); } bool is_attraction() const { return is_type(attraction_land) || is_type(attraction_city); } bool is_monument() const { return is_type(monument); } bool is_factory() const { return is_type(factory); } bool is_city_building() const { return is_type(city_res) || is_type(city_com) || is_type(city_ind); } bool is_transport_building() const { return type > headquarters && type <= flat_dock; } bool is_depot() const { return is_type(depot); } bool is_connected_with_town() const; /// @returns headquarters level (or -1 if building is not headquarters) sint32 get_headquarters_level() const { return (is_headquarters() ? get_extra() : -1) ; } /** * the level is used in many places: for price, for capacity, ... */ uint16 get_level() const { return level; } /** * Mail generation level */ uint16 get_mail_level() const; // how often will this appear uint8 get_distribution_weight() const { return distribution_weight; } const building_tile_desc_t *get_tile(uint16 index) const { assert(index < layouts * size.x * size.y); return get_child(index + 2); } const building_tile_desc_t *get_tile(uint8 layout, sint16 x, sint16 y) const; // returns true,if building can be rotated bool can_rotate() const { if(size.x!=size.y && layouts==1) { return false; } // check for missing tiles after rotation for( sint16 x=0; xhas_image() ^ get_tile( 1, get_x(1)-y-1, x )->has_image()) { return false; } } } return true; } uint8 adjust_layout(uint8 layout) const; /** * Skin: cursor (index 0) and icon (index 1) */ const skin_desc_t * get_cursor() const { return flags & FLAG_HAS_CURSOR ? get_child(2 + size.x * size.y * layouts) : 0; } // the right house for this area? bool is_allowed_climate( climate cl ) const { return ((1< 0; } bool can_be_built_aboveground() const { return allow_underground != 1; } uint32 get_clusters() const { // Only meaningful for res, com, ind return is_city_building() ? extra_data : 0; } }; ENUM_BITSET(building_desc_t::flag_t) #endif simutrans-124.5/src/simutrans/descriptor/citycar_desc.h000066400000000000000000000022431520675460500233640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_CITYCAR_DESC_H #define DESCRIPTOR_CITYCAR_DESC_H #include "obj_base_desc.h" #include "image_list.h" #include "../dataobj/ribi.h" #include "../simtypes.h" #include "../network/checksum.h" /** * Private city cars, not player owned. They automatically appear in cities. * * Child nodes: * 0 Name * 1 Copyright * 2 Image-list */ class citycar_desc_t : public obj_desc_timelined_t { friend class citycar_reader_t; uint16 distribution_weight; /// topspeed in internal speed units !!! not km/h!!! uint16 topspeed; public: image_id get_image_id(ribi_t::dir dir) const { image_t const* const image = get_child(2)->get_image(dir); return image != NULL ? image->get_id() : IMG_EMPTY; } uint16 get_distribution_weight() const { return distribution_weight; } /// topspeed in internal speed units !!! not km/h!!! uint16 get_topspeed() const { return topspeed; } void calc_checksum(checksum_t *chk) const { obj_desc_timelined_t::calc_checksum(chk); chk->input(distribution_weight); chk->input(topspeed); } }; #endif simutrans-124.5/src/simutrans/descriptor/crossing_desc.h000066400000000000000000000040641520675460500235600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_CROSSING_DESC_H #define DESCRIPTOR_CROSSING_DESC_H #include "obj_base_desc.h" #include "image.h" #include "image_list.h" #include "../simtypes.h" #include "../network/checksum.h" class checksum_t; /** * Child nodes: * 0 Name * 1 Copyright * 2 Image-list */ class crossing_desc_t : public obj_desc_timelined_t { friend class crossing_reader_t; private: waytype_t waytype1; waytype_t waytype2; sint8 sound; uint32 closed_animation_time; uint32 open_animation_time; sint32 topspeed1; // the topspeed depeds strongly on the crossing ... sint32 topspeed2; public: /* the imagelists are: * open_ns * open_ew * front_open_ns * front_open_ew * closed_ns * .... * =>ns=0 NorthSouth ns=1, East-West */ const image_t *get_background(uint8 ns, bool open, uint16 phase) const { if(open) { return get_child(2 + ns)->get_image(phase); } else { image_list_t const* const imglist = get_child(6 + ns); return imglist ? imglist->get_image(phase) : NULL; } } const image_t *get_foreground(uint8 ns, bool open, uint16 phase) const { uint8 const n = ns + (open ? 4 : 8); image_list_t const* const imglist = get_child(n); return imglist ? imglist->get_image(phase) : 0; } waytype_t get_waytype(int i) const { return i==0? waytype1 : waytype2; } sint32 get_maxspeed(int i) const { return i==0 ? topspeed1 : topspeed2; } uint16 get_phases(bool open, bool front) const { return get_child(6 - 4 * open + 2 * front)->get_count(); } uint32 get_animation_time(bool open) const { return open ? open_animation_time : closed_animation_time; } sint8 get_sound() const { return sound; } void calc_checksum(checksum_t *chk) const { chk->input(waytype1); chk->input(waytype2); chk->input(closed_animation_time); chk->input(open_animation_time); chk->input(topspeed1); chk->input(topspeed2); chk->input(intro_date); chk->input(retire_date); } }; #endif simutrans-124.5/src/simutrans/descriptor/factory_desc.cc000066400000000000000000000164021520675460500235350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "factory_desc.h" #include "xref_desc.h" #include "../network/checksum.h" void field_class_desc_t::calc_checksum(checksum_t* chk) const { #if MSG_LEVEL>0 PAKSET_INFO("--\n", "obj=field\nname=%s", get_name()); if (get_copyright()) PAKSET_INFO("copyright=", "%s", get_copyright()); if (snow_image) PAKSET_INFO("has_snow=", "1"); PAKSET_INFO("production=", "%d", production_per_field); if (storage_capacity) PAKSET_INFO("capacity=", "%d", storage_capacity); PAKSET_INFO("spawn_weight=", "%d", spawn_weight); #endif chk->input(production_per_field); chk->input(storage_capacity); chk->input(spawn_weight); } void field_group_desc_t::calc_checksum(checksum_t *chk) const { chk->input(probability); chk->input(max_fields); chk->input(min_fields); chk->input(field_classes); for(uint16 i=0; icalc_checksum(chk); } } void factory_supplier_desc_t::calc_checksum(checksum_t *chk) const { chk->input(capacity); chk->input(supplier_count); chk->input(consumption); chk->input(get_input_type()->get_name()); } void factory_product_desc_t::calc_checksum(checksum_t *chk) const { chk->input(capacity); chk->input(factor); chk->input(get_output_type()->get_name()); } void factory_desc_t::correct_smoke() { if( smokerotations == 0 && get_smoke() ) { // old type of factory, we have to build the tile and smoke offsets here const smoke_desc_t *oldsmoke = get_smoke(); const koord size = get_building()->get_size(0)-koord(1,1); smokerotations = get_building()->get_all_layouts(); for( int i = 0; i < smokerotations; i++ ) { smoketile[i] = oldsmoke->get_pos_off(size,i); smokeoffset[i] = oldsmoke->get_xy_off(i); smokeoffset[i] -= koord(0, LEGACY_SMOKE_YOFFSET); } smokeuplift = DEFAULT_SMOKE_UPLIFT; smokelifetime = DEFAULT_FACTORYSMOKE_TIME; } // check for smoke outside of boundries if (get_smoke()) { for (int i = 0; i < smokerotations; i++) { koord new_tile; new_tile.x = clamp((int)smoketile[i].x, 0, get_building()->get_x(i) - 1); new_tile.y = clamp((int)smoketile[i].y, 0, get_building()->get_y(i) - 1); if (new_tile != smoketile[i]) { // tile is outside => compensate with new offsets smokeoffset[i].x += ((smoketile[i].x - new_tile.x) * gfx->get_tile_raster_width()) / 2 - ((smoketile[i].y - new_tile.y) * gfx->get_tile_raster_width()) / 2; smokeoffset[i].y += ((smoketile[i].x - new_tile.x) * gfx->get_tile_raster_width()) / 4 + ((smoketile[i].y - new_tile.y) * gfx->get_tile_raster_width()) / 4; smoketile[i] = new_tile; dbg->warning("factory_desc_t::correct_smoke()", "smoke of \"%s\" in rotation %d, changed to (%s) and offsets %d,%d", get_name(), i, new_tile.get_str(), smoketile[i].x, smoketile[i].y); } } } } void factory_desc_t::calc_checksum(checksum_t *chk) const { chk->input((uint8)placement); chk->input(productivity); chk->input(range); chk->input(distribution_weight); chk->input(color); chk->input(supplier_count); chk->input(product_count); chk->input(fields); chk->input(pax_level); chk->input(electricity_producer); chk->input(expand_probability); chk->input(expand_minimum); chk->input(expand_range); chk->input(expand_times); chk->input(electric_boost); chk->input(pax_boost); chk->input(mail_boost); chk->input(electric_demand); chk->input(pax_demand); chk->input(mail_demand); for (uint8 i=0; icalc_checksum(chk); } for (uint8 i=0; icalc_checksum(chk); } #if MSG_LEVEL>0 PAKSET_INFO("obj=factory", ""); PAKSET_INFO("productivity=", "%d", productivity); PAKSET_INFO("range=", "%d", range); PAKSET_INFO("distribution_weight=", "%d", distribution_weight); // there is probably the same in the chance field in the building part PAKSET_INFO("mapcolor=", "%d", color); if (pax_level != 12) PAKSET_INFO("pax_level=", "%d", pax_level); if (expand_minimum != 0) PAKSET_INFO("expand_minimum=", "%d", expand_minimum); if (expand_range != 0) PAKSET_INFO("expand_range=", "%d", expand_range); if (expand_times != 0) PAKSET_INFO("expand_times=", "%d", expand_times); if (electric_demand != 65535u) PAKSET_INFO("electricity_demand=", "%d", electric_demand); if (pax_demand != 65535u) PAKSET_INFO("passenger_demand=", "%d", pax_demand); if (mail_demand != 65535u) PAKSET_INFO("mail_demand=", "%d", mail_demand); if (expand_times != 0) PAKSET_INFO("expand_times=", "%d", expand_times); if (electric_boost != 256) PAKSET_INFO("electricity_boost=", "%d", (electric_boost*1000)/256); if (pax_boost != 0) PAKSET_INFO("passenger_boost=", "%d", pax_boost); if (mail_boost != 0) PAKSET_INFO("mail_boost=", "%d", mail_boost); if (sound_interval != 0xFFFFFFFFul) PAKSET_INFO("sound_interval=", "%lu", sound_interval); #endif get_building()->calc_checksum(chk); #if MSG_LEVEL>0 if (smokerotations > 0) { if (smokeuplift != DEFAULT_SMOKE_UPLIFT) PAKSET_INFO("smokeuplift=", "%lu", smokeuplift); if (smokelifetime != DEFAULT_FACTORYSMOKE_TIME) PAKSET_INFO("smokelifetime=", "%lu", smokelifetime); for (int i = 0; i < smokerotations; i++) { PAKSET_INFO("smoketile", "[%d]=%d,%d", i, smoketile[i].x, smoketile[i].y); PAKSET_INFO("smokeoffset", "[%d]=%d,%d", i, smokeoffset[i].x, smokeoffset[i].y); } } else if (smoketile[0].x | smoketile[0].y | smokeoffset[0].x | smokeoffset[0].y) { if (smokeuplift != DEFAULT_SMOKE_UPLIFT) PAKSET_INFO("smokeuplift=", "%lu", smokeuplift); if (smokelifetime != DEFAULT_FACTORYSMOKE_TIME) PAKSET_INFO("smokelifetime=", "%lu", smokelifetime); PAKSET_INFO("smoketile", "[%d]=%d,%d", 0, smoketile[0].x, smoketile[0].y); PAKSET_INFO("smokeoffset", "[%d]=%d,%d", 0, smokeoffset[0].x, smokeoffset[0].y); } if (const field_group_desc_t* field_group = get_field_group()) { if (int count = field_group->get_field_class_count()) { PAKSET_INFO("probability_to_spawn", "%d", field_group->get_probability()); PAKSET_INFO("min_fields", "%d", field_group->get_min_fields()); PAKSET_INFO("max_fields", "%d", field_group->get_max_fields()); PAKSET_INFO("start_fields", "%d", field_group->get_start_fields()); for (int i = 0; i < count; i++) { PAKSET_INFO("fields", "[%d]=%s", i, field_group->get_field_class(i)->get_name()); } } } for (uint8 i = 0; i < supplier_count; i++) { const factory_supplier_desc_t* supp = get_supplier(i); PAKSET_INFO("inputgood", "[%d]=%s", i, supp->get_input_type()->get_name()); PAKSET_INFO("inputsupplier", "[%d]=%d", i, supp->get_supplier_count()); PAKSET_INFO("inputcapacity", "[%d]=%d", i, supp->get_capacity()); PAKSET_INFO("inputfactor", "[%d]=%d", i, supp->get_consumption()); } for (uint8 i = 0; i < product_count; i++) { const factory_product_desc_t* prod = get_product(i); PAKSET_INFO("outputgood", "[%d]=%s", i, prod->get_output_type()->get_name()); PAKSET_INFO("outputcapacity", "[%d]=%d", i, prod->get_capacity()); PAKSET_INFO("outputfactor", "[%d]=%d", i, prod->get_factor()); } #endif // moved here, since the field class write out the field entries if (const field_group_desc_t* field_group = get_field_group()) { field_group->calc_checksum(chk); } PAKSET_INFO("--", ""); } simutrans-124.5/src/simutrans/descriptor/factory_desc.h000066400000000000000000000222111520675460500233720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_FACTORY_DESC_H #define DESCRIPTOR_FACTORY_DESC_H #include "obj_desc.h" #include "building_desc.h" #include "skin_desc.h" #include "goods_desc.h" #include "../dataobj/koord.h" #include "../tpl/weighted_vector_tpl.h" #define DEFAULT_FACTORYSMOKE_TIME (2499) #define DEFAULT_SMOKE_UPLIFT (16) #define LEGACY_SMOKE_YOFFSET (8) class checksum_t; /** * this desc will store data specific to each class of fields * Fields are xref'ed from skin_desc_t */ class field_class_desc_t : public obj_desc_t { friend class factory_field_class_reader_t; friend class factory_field_group_reader_t; // this is a special case due to desc restructuring private: uint8 snow_image; // 0 or 1 for snow uint16 production_per_field; uint16 storage_capacity; uint16 spawn_weight; public: skin_desc_t const* get_images() const { return get_child(0); } const char *get_name() const { return get_images()->get_name(); } const char *get_copyright() const { return get_images()->get_copyright(); } uint8 has_snow_image() const { return snow_image; } uint16 get_field_production() const { return production_per_field; } uint16 get_storage_capacity() const { return storage_capacity; } uint16 get_spawn_weight() const { return spawn_weight; } void calc_checksum(checksum_t *chk) const; }; // this desc now only contains common, shared data regarding fields class field_group_desc_t : public obj_desc_t { friend class factory_field_group_reader_t; private: uint16 probability; // between 0 ...10000 uint16 max_fields; // maximum number of fields around a single factory uint16 min_fields; // minimum number of fields around a single factory uint16 start_fields; // number of fields between min and start_fields to spawn on creation uint16 field_classes; // number of field classes weighted_vector_tpl field_class_indices; public: // fills the array, is only called once during successfully_loaded() after resolve xrefs void init_field_class_indices() { if( field_classes>0 ) { field_class_indices.clear(); field_class_indices.reserve( field_classes ); for( uint16 i=0 ; iget_spawn_weight() ); } } } uint16 get_probability() const { return probability; } uint16 get_max_fields() const { return max_fields; } uint16 get_min_fields() const { return min_fields; } uint16 get_start_fields() const { return start_fields; } uint16 get_field_class_count() const { return field_classes; } field_class_desc_t const* get_field_class(uint16 const idx) const { return idx < field_classes ? get_child(idx) : 0; } const weighted_vector_tpl &get_field_class_indices() const { return field_class_indices; } void calc_checksum(checksum_t *chk) const; }; /** * Smoke objects for factories. * * Child nodes: * 0 SKin */ class smoke_desc_t : public obj_desc_t { friend class factory_smoke_reader_t; private: koord pos_off; koord xy_off; public: const char *get_name() const { return get_images()->get_name(); } const char *get_copyright() const { return get_images()->get_copyright(); } skin_desc_t const* get_images() const { return get_child(0); } // get the tile with the smoke koord get_pos_off( koord size, uint8 rotation) const { switch( rotation%4 ) { case 1: return koord( size.y-pos_off.y, pos_off.x ); case 2: return koord( size.x-pos_off.x, size.y-pos_off.y ); case 3: return koord( pos_off.y, size.x-pos_off.x ); } return pos_off; } // offset in pixel (depends on OBJECT_OFFSET_STEPS==16) koord get_xy_off(uint8 rotation) const { switch( rotation%4 ) { case 1: return koord( 0, xy_off.y+xy_off.x/2 ); case 2: return koord( -xy_off.x, xy_off.y ); case 3: return koord( 0, xy_off.y-xy_off.x/2 ); } return xy_off; } }; /** * Information about required goods for production * * Child nodes: * 0 Ware */ class factory_supplier_desc_t : public obj_desc_t { friend class factory_supplier_reader_t; private: uint16 capacity; uint16 supplier_count; uint16 consumption; public: goods_desc_t const* get_input_type() const { return get_child(0); } uint16 get_capacity() const { return capacity; } uint16 get_supplier_count() const { return supplier_count; } uint16 get_consumption() const { return consumption; } void calc_checksum(checksum_t *chk) const; }; /** * Information about produced goods of a factory * * Child nodes: * 0 Ware */ class factory_product_desc_t : public obj_desc_t { friend class factory_product_reader_t; private: uint16 capacity; /** * How much of this product is derived from one unit of factory * production? 256 means 1.0 */ uint16 factor; public: goods_desc_t const* get_output_type() const { return get_child(0); } uint16 get_capacity() const { return capacity; } uint16 get_factor() const { return factor; } void calc_checksum(checksum_t *chk) const; }; /** * Factory. * * Child nodes: * 0 House descriptor * 1 Smoke descriptor * 2 Supplier descriptor 1 * 3 Supplier descriptor 2 * ... ... * n+1 Supplier descriptor n * n+2 Consumer descriptor 1 * n+3 Consumer descriptor 2 * ... ... */ class factory_desc_t : public obj_desc_t { friend class factory_reader_t; public: enum site_t { Land, Water, City, river, shore, forest }; private: site_t placement = site_t::Land; uint16 productivity = 0; uint16 range = 0; uint16 distribution_weight = 0; // probability of construction of this factory uint8 color = 0; uint8 electricity_producer = 0xFF; // 0=no,1=yes, 0xFF=unknown uint8 fields = 0; // only if there are any ... uint16 supplier_count = 0; uint16 product_count = 0; uint16 pax_level = 12; uint16 expand_probability = 0; uint16 expand_minimum = 0; uint16 expand_range = 0; uint16 expand_times = 0; uint16 electric_boost = 256; uint16 pax_boost = 0; uint16 mail_boost = 0; uint16 electric_demand = 65535; uint16 pax_demand = 65535; uint16 mail_demand = 65535; uint16 smokeuplift = 0; uint16 smokelifetime = 0; koord smoketile[4]; koord smokeoffset[4]; uint8 smokerotations = 0; sint8 sound_id = 0; uint32 sound_interval = 0; public: const char *get_name() const { return get_building()->get_name(); } const char *get_copyright() const { return get_building()->get_copyright(); } building_desc_t const* get_building() const { return get_child(0); } smoke_desc_t const* get_smoke() const { return get_child(1); } void correct_smoke(); koord get_smoketile( uint8 rot ) const { return smoketile[ rot%smokerotations ]; } koord get_smokeoffset( uint8 rot ) const { return smokeoffset[ rot%smokerotations ]; } uint16 get_smokeuplift() const { return smokeuplift; } uint16 get_smokelifetime() const { return smokelifetime; } // we must take care, for the case of no producer/consumer const factory_supplier_desc_t *get_supplier(uint16 i) const { return i < supplier_count ? get_child(2 + i) : 0; } const factory_product_desc_t *get_product(uint16 i) const { return i < product_count ? get_child(2 + supplier_count + i) : 0; } const field_group_desc_t *get_field_group() const { if(!fields) { return NULL; } return get_child(2 + supplier_count + product_count); } bool is_consumer_only() const { return product_count == 0; } bool is_producer_only() const { return supplier_count == 0; } uint16 get_supplier_count() const { return supplier_count; } uint16 get_product_count() const { return product_count; } /* where to built */ site_t get_placement() const { return placement; } uint16 get_distribution_weight() const { return distribution_weight; } PIXVAL get_color() const { return gfx->palette_lookup(color); } void set_productivity(uint16 p) { productivity=p; } uint16 get_productivity() const { return productivity; } uint16 get_range() const { return range; } /* level for mail and passenger generation */ uint16 get_pax_level() const { return pax_level; } bool is_electricity_producer() const { return electricity_producer; } uint16 get_expand_probability() const { return expand_probability; } uint16 get_expand_minimum() const { return expand_minimum; } uint16 get_expand_range() const { return expand_range; } uint16 get_expand_times() const { return expand_times; } uint16 get_electric_boost() const { return electric_boost; } uint16 get_pax_boost() const { return pax_boost; } uint16 get_mail_boost() const { return mail_boost; } uint16 get_electric_demand() const { return electric_demand; } uint16 get_pax_demand() const { return pax_demand; } uint16 get_mail_demand() const { return mail_demand; } // more effects when producing sint8 get_sound() const { return sound_id; } uint32 get_sound_interval_ms() const { return sound_interval; } void calc_checksum(checksum_t *chk) const; }; #endif simutrans-124.5/src/simutrans/descriptor/goods_desc.cc000066400000000000000000000017001520675460500231740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "goods_desc.h" static const char * catg_names[32] = { "special freight", "CATEGORY_01", // was "piece goods", "CATEGORY_02", // was "bulk goods", "CATEGORY_03", // was "oil/gasoline", "CATEGORY_04", // was "cooled goods", "CATEGORY_05", // was "liquid food", "CATEGORY_06", // was "long goods", "CATEGORY_07", "CATEGORY_08", "CATEGORY_09", "CATEGORY_10", "CATEGORY_11", "CATEGORY_12", "CATEGORY_13", "CATEGORY_14", "CATEGORY_15", "CATEGORY_16", "CATEGORY_17", "CATEGORY_18", "CATEGORY_19", "CATEGORY_20", "CATEGORY_21", "CATEGORY_22", "CATEGORY_23", "CATEGORY_24", "CATEGORY_25", "CATEGORY_26", "CATEGORY_27", "CATEGORY_28", "CATEGORY_29", "CATEGORY_30", "CATEGORY_31", }; /** * @return Name of the category of the good */ const char * goods_desc_t::get_catg_name() const { return catg_names[catg & 31]; } simutrans-124.5/src/simutrans/descriptor/goods_desc.h000066400000000000000000000050021520675460500230350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_GOODS_DESC_H #define DESCRIPTOR_GOODS_DESC_H #include "obj_base_desc.h" #include "../simcolor.h" #include "../display/simgraph.h" #include "../network/checksum.h" class checksum_t; /** * Child nodes: * 0 Name * 1 Copyright * 2 Text: Name of measurement unit */ class goods_desc_t : public obj_named_desc_t { friend class goods_reader_t; friend class goods_manager_t; /// base value without price multipliers, read from the pak file sint64 base_value; /// Value with (beginner) price multiplier applied. /// This is the value that is is actually used in revenue calculation. sint64 value; /// Category of the good uint8 catg; /** * total index, all ware with same catg_index will be compatible, * including special freight * assigned during registration */ uint8 catg_index; /** * index of the type, * assigned during registration */ uint8 goods_index; uint8 color; /** * Bonus for fast transport given in percent! */ uint16 speed_bonus; /** * Weight in KG per unit of this good */ uint16 weight_per_unit; public: // the measure for that good (crates, people, bags ... ) const char *get_mass() const { return get_child(2)->get_text(); } sint64 get_value() const { return value; } /** * @return speed bonus value of the good */ uint16 get_speed_bonus() const { return speed_bonus; } /** * @return Category of the good */ uint8 get_catg() const { return catg; } /** * @return Category of the good */ uint8 get_catg_index() const { return catg_index; } /** * @return internal index (just a number, passenger, then mail, then something ... ) */ uint8 get_index() const { return goods_index; } /** * @return weight in KG per unit of the good */ uint16 get_weight_per_unit() const { return weight_per_unit; } /** * @return Name of the category of the good */ const char * get_catg_name() const; /** * Checks if this good can be interchanged with the other, in terms of * transportability. * * Inline because called very often */ bool is_interchangeable(const goods_desc_t *other) const { return catg_index == other->get_catg_index(); } /** * @return color for good table and waiting bars */ PIXVAL get_color() const { return gfx->palette_lookup(color); } void calc_checksum(checksum_t *chk) const { chk->input(base_value); chk->input(catg); chk->input(speed_bonus); chk->input(weight_per_unit); } }; #endif simutrans-124.5/src/simutrans/descriptor/ground_desc.cc000066400000000000000000001117361520675460500233720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../world/simworld.h" #include "../display/simgraph.h" #include "../simconst.h" #include "spezial_obj_tpl.h" #include "ground_desc.h" #include "../dataobj/environment.h" //const int totalslopes_single = 16; const int totalslopes = 81; /**************************************************************************************************** * some functions for manipulations/blending images * maybe they should be put in their own module, even though they are only used here ... */ #if COLOUR_DEPTH != 0 #define red_comp(pix) (((pix)>>10)&0x001f) #define green_comp(pix) (((pix)>>5)&0x001f) #define blue_comp(pix) ((pix)&0x001f) #endif /* combines a texture and a lightmap * just weights all pixels by the lightmap * @param binary if true, then a binary decision is made: if lightmap is grey then take original pixel, if not set to black */ static image_t* create_textured_tile(const image_t* image_lightmap, const image_t* image_texture, bool binary = false) { if( image_lightmap == NULL ) { image_t *image_dest = image_t::create_single_pixel(); image_dest->register_image(); return image_dest; } image_t *image_dest = image_lightmap->copy_rotate(0); #if COLOUR_DEPTH != 0 PIXVAL* dest = image_dest->get_data(); PIXVAL const* const texture = image_texture->get_data(); sint16 const x_y = image_texture->get_pic()->w; // now mix the images for (int j = 0; j < image_dest->get_pic()->h; j++) { sint32 x = *dest++; assert(x >= 0); const sint32 offset = (image_dest->get_pic()->y + j - image_texture->get_pic()->y) * (x_y + 3) + 2; // position of the pixel in a rectangular map do { sint16 runlen = *dest++; assert(runlen >= 0); for(int i=0; i0); uint16 mix = texture[offset+x]; if (!binary) { uint16 grey = *dest; uint16 rc = (red_comp(grey)*red_comp(mix))/16; if(rc>=32) { rc = 31; } uint16 gc = (green_comp(grey)*green_comp(mix))/16; if(gc>=32) { gc = 31; } uint16 bc = (blue_comp(grey)*blue_comp(mix))/16; if(bc>=32) { bc = 31; } *dest++ = (rc<<10) | (gc<<5) | bc; } else { if (*dest) { *dest = mix;} dest++; } x ++; } x += *dest; } while( (*dest++)!=0 ); } assert(dest - image_dest->get_data() == (ptrdiff_t)image_dest->get_pic()->len); #else (void)image_texture; (void)binary; #endif image_dest->register_image(); return image_dest; } /* combines a texture and a lightmap * does a very simple stretching of the texture and the mix images * BEWARE: Assumes all images but image_lightmap are square! * BEWARE: no special colors or your will see literally blue! */ static image_t* create_alpha_tile(const image_t* image_lightmap, slope_t::type slope, const image_t* image_alphamap) { if( image_lightmap == NULL || image_alphamap == NULL || image_alphamap->get_pic()->w < 2 ) { image_t *image_dest = image_t::create_single_pixel(); image_dest->register_image(); return image_dest; } assert( image_alphamap->get_pic()->w == image_alphamap->get_pic()->h); image_t *image_dest = image_lightmap->copy_rotate(0); PIXVAL const* const alphamap = image_alphamap->get_data(); const sint32 x_y = image_dest->get_pic()->w; const sint32 mix_x_y = image_alphamap->get_pic()->w; sint16 tile_x, tile_y; /* * to go from mixmap xy to tile xy is simple: * (x,y)_tile = (mixmap_x+mixmap_y)/2 , (mixmap_y-mixmap_x)/4+(3/4)*tilesize * This is easily inverted to * (x,y)mixmap = x_tile-2*y_tile+(3/2)*tilesize, x_tile+2*y_tile-(3/2)*tilesize * tricky are slopes. There we have to add an extra distortion * /4\ * 1+3 * \2/ * Luckily this distortion is only for the y direction. * for corner 1: max(0,(tilesize-(x+y))*HEIGHT_STEP)/tilesize ) * for corner 2: max(0,((y-x)*HEIGHT_STEP)/tilesize ) * for corner 3: max(0,((x+y)-tilesize)*HEIGHT_STEP)/tilesize ) * for corner 4: max(0,((x-y)*HEIGHT_STEP)/tilesize ) * the maximum operators make the inversion of the above equation nearly impossible. */ // we will need them very often ... const sint16 corner_sw_y = (3 * x_y) / 4 - corner_sw(slope) * tile_raster_scale_y( TILE_HEIGHT_STEP, x_y ); const sint16 corner_se_y = x_y - corner_se(slope) * tile_raster_scale_y( TILE_HEIGHT_STEP, x_y ); const sint16 corner_ne_y = (3 * x_y) / 4 - corner_ne(slope) * tile_raster_scale_y( TILE_HEIGHT_STEP, x_y ); const sint16 corner_nw_y = (x_y / 2) - corner_nw(slope) * tile_raster_scale_y( TILE_HEIGHT_STEP, x_y ); const sint16 middle_y = (corner_se_y + corner_nw_y) / 2; // now mix the images PIXVAL* dest = image_dest->get_data(); for( int j = 0; j < image_dest->get_pic()->h; j++ ) { tile_y = image_dest->get_pic()->y + j; tile_x = *dest++; do { sint16 runlen = *dest++; for( int i = 0; i < runlen; i++ ) { // now we must calculate the target pixel // after the upper explanation, you will understand this is longish: sint16 tile_y_corrected; // first; check, if we are front or back half // back half means, we are above a line from the left_y (corner_sw), middle_y, right_y (corner_se) const sint16 back_y = (x_y < 2) ? 0 : ( (tile_x < x_y / 2) ? corner_sw_y + ((middle_y - corner_sw_y) * tile_x) / (x_y / 2) : middle_y + ((corner_ne_y - middle_y) * (tile_x - (x_y / 2))) / (x_y / 2) ); // in the middle? then it is just the diagonal in the mixmap if( back_y == tile_y ) { tile_y_corrected = 0; } else if( back_y > tile_y ) { // left quadrant calulation: mirror of right quadrat sint16 x = tile_x; if( x >= x_y / 2 ) { x = x_y - tile_x; } // we are in the back tile => calculate border y sint16 backborder_y; if( tile_x > x_y / 2 ) { backborder_y = corner_nw_y + ((corner_ne_y - corner_nw_y) * (x_y / 2 - x)) / (x_y / 2); } else { backborder_y = corner_sw_y + ((corner_nw_y - corner_sw_y) * x) / (x_y / 2); } // ok, now we have to calculate the y coordinate ... if( backborder_y < tile_y ) { tile_y_corrected = -((back_y - tile_y) * x) / (back_y - backborder_y); } else { tile_y_corrected = -x; } } else { // left quadrant calulation: mirror of right quadrat sint16 x = tile_x; // put condition this way, testing (x >= x_y) breaks if x_y == 1. if( 2*x >= x_y ) { x = x_y - tile_x; } // we are in the front tile => calculate border y sint16 frontborder_y = 0; if( tile_x > x_y / 2 ) { frontborder_y = corner_se_y + ((corner_ne_y - corner_se_y) * (x_y / 2 - x)) / (x_y / 2); } else if( x_y >=2 ) { frontborder_y = corner_sw_y + ((corner_se_y - corner_sw_y) * x) / (x_y / 2); } // ok, now we have to calculate the y coordinate ... if( frontborder_y > tile_y ) { tile_y_corrected = -((back_y - tile_y) * x) / (frontborder_y - back_y); } else { tile_y_corrected = x; } } // now we have calulated the y_t of square tile that is rotated by 45 degree // so we just have to do a 45 deg backtransform ... // (and do not forget: tile_y_corrected middle = 0! sint32 x_t = tile_x - tile_y_corrected; sint32 y_t = tile_y_corrected + tile_x; // due to some inexactness of integer arithmethics, we have to take care of overflow and underflow x_t = clamp(x_t, 0, x_y-1); y_t = clamp(y_t, 0, x_y-1); sint32 alphamap_offset = ((y_t * mix_x_y) / x_y) * (mix_x_y + 3) + 2 + (x_t * mix_x_y) / x_y; // see only the mixmap for mixing // // clear 0x8000 bit as it has special meaning, // confuses rezoom_img() and crashes later *dest++ = alphamap[alphamap_offset] & 0x7fff; tile_x++; } tile_x += *dest; } while( *dest++ != 0 ); } image_dest->register_image(); return image_dest; } // copy ref texture, copy pixels from image into new texture static image_t* create_texture_from_tile(const image_t* image, const image_t* ref) { if( image == NULL || image->get_pic()->w < 2 ) { image_t *image_dest = image_t::create_single_pixel(); return image_dest; } // assumes ref is texture image with no clear runs, full rows. image_t *image_dest = image_t::copy_image(*ref); PIXVAL *const sp2 = image_dest->get_data(); assert(ref->w == ref->y + ref->h && ref->x == 0); const sint32 ref_w = ref->w; const sint32 height= image->get_pic()->h; // decode image and put it into dest const PIXVAL* sp = image->get_data(); for(int y = 0; y < height; y++ ) { int x = image->x; uint16 runlen = *sp++; do { // we start with a clear run x += runlen; // now get colored pixels runlen = (*sp++); for(uint16 i=0; iy <= (yy) && (yy) < ref->h && 0 <= (xx) && (xx) < ref_w) { \ size_t const index = (ref_w + 3) * (yy - ref->y) + xx + 2; \ assert(index < image_dest->len); \ sp2[index] = p; \ } /* Put multiple copies into dest image * * image is assumed to be tile shaped, * and is copied four times to cover tiles of neighboring tiles. * * copy + copy * | / \ | * + image + * | \ / | * copy + copy * * ref image is assumed to be rectangular, * it is used to fill holes due to missing pixels in image. */ copypixel(x, y + image->y); copypixel(x + ref_w/2, y + image->y + ref_w/4); copypixel(x - ref_w/2, y + image->y + ref_w/4); copypixel(x + ref_w/2, y + image->y - ref_w/4); copypixel(x - ref_w/2, y + image->y - ref_w/4); x++; } } while( (runlen = *sp++) ); } // image_dest not registered return image_dest; #undef copypixel } /**************************************************************************************************** * the real textures are registered/calculated below */ karte_t *ground_desc_t::world = NULL; /* convert double to single slopes */ const uint8 ground_desc_t::slopetable[80] = { 0, 1, 0xFF, 2, 3, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 0xFF, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 8, 9, 0xFF, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 12, 13, 0xFF, 14, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // since we only use valid slope (to gain some more image slots) we use this lookup table // 255 slopes are invalid /* for double slope it should look like this, and for single slope like above 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 255, 255, 40, 255, 255, 41, 42, 43, 44, 255, 255, 45, 255, 255, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 255, 255, 59, 255, 255, 60, 61, 62, 63, 255, 255, 64, 255, 255 */ uint16 doubleslope_to_imgnr[81]; // how many animation stages we got for waves uint16 ground_desc_t::water_animation_stages = 1; sint16 ground_desc_t::water_depth_levels = 0; // are double_grounds available in this pakset ? bool ground_desc_t::double_grounds = true; static const ground_desc_t* boden_texture = NULL; static const ground_desc_t* light_map = NULL; static const ground_desc_t* transition_water_texture = NULL; static const ground_desc_t* transition_slope_texture = NULL; const ground_desc_t *ground_desc_t::shore = NULL; const ground_desc_t *ground_desc_t::fundament = NULL; const ground_desc_t *ground_desc_t::slopes = NULL; const ground_desc_t *ground_desc_t::fences = NULL; const ground_desc_t *ground_desc_t::marker = NULL; const ground_desc_t *ground_desc_t::borders = NULL; const ground_desc_t *ground_desc_t::sea = NULL; const ground_desc_t *ground_desc_t::outside = NULL; static special_obj_tpl const grounds[] = { { &ground_desc_t::shore, "Shore" }, { &boden_texture, "ClimateTexture" }, { &light_map, "LightTexture" }, { &transition_water_texture, "ShoreTrans" }, { &transition_slope_texture, "SlopeTrans" }, { &ground_desc_t::fundament, "Basement" }, { &ground_desc_t::slopes, "Slopes" }, { &ground_desc_t::fences, "Fence" }, { &ground_desc_t::marker, "Marker" }, { &ground_desc_t::borders, "Borders" }, { &ground_desc_t::sea, "Water" }, { &ground_desc_t::outside, "Outside" }, { NULL, NULL } }; // the water and seven climates static const char* const climate_names[MAX_CLIMATES] = { "sea", "desert", "tropic", "mediterran", "temperate", "tundra", "rocky", "arctic" }; // from this number on there will be all ground images // i.e. 15 times slopes + 7 image_id ground_desc_t::image_offset = IMG_EMPTY; static const uint8 number_of_climates = 7; static slist_tpl ground_image_list; static image_id climate_image[32], water_image; image_id alpha_image[totalslopes]; image_id alpha_corners_image[totalslopes * 15]; image_id alpha_water_image[totalslopes * 15]; /* * called every time an object is read * the object will be assigned according to its name */ bool ground_desc_t::register_desc(const ground_desc_t *desc) { if(strcmp("Outside", desc->get_name())==0) { image_t const* const image = desc->get_child(2)->get_image(0,0); dbg->message("ground_desc_t::register_desc()", "setting raster width to %i", image->get_pic()->w); gfx->set_base_raster_width(image->get_pic()->w); } // find out water animation stages if(strcmp("Water", desc->get_name())==0) { water_animation_stages = 0; while( desc->get_image(0, water_animation_stages)!=IMG_EMPTY ) { DBG_MESSAGE( "water", "image(0,%i)=%u", water_animation_stages, desc->get_image(0, water_animation_stages) ); water_animation_stages ++; } // then ignore all ms settings if(water_animation_stages==1) { env_t::water_animation = 0; } water_depth_levels = desc->get_child(2)->get_count()-2; if(water_depth_levels<=0) { water_depth_levels = 0; } } return ::register_desc(grounds, desc); } /* * called after loading; usually checks for completeness * however, we have to calculate all textures * and put them into images */ bool ground_desc_t::successfully_loaded() { DBG_MESSAGE("ground_desc_t::successfully_loaded()","boden"); return ::successfully_loaded(grounds+1); } /* returns the untranslated name of the matching climate */ char const* ground_desc_t::get_climate_name_from_bit(climate n) { return nfree_all_images_above(image_offset); } #if COLOUR_DEPTH != 0 while (!ground_image_list.empty()) { delete ground_image_list.remove_first(); } #endif // not the wrong tile size? assert(boden_texture->get_image_ptr(0)->get_pic()->w == ground_desc_t::outside->get_image_ptr(0)->get_pic()->w); // create rotations of the mixer image_t *all_rotations_beach[totalslopes]; // water->sand->texture image_t *all_rotations_slope[totalslopes]; // texture1->texture2 image_t *final_tile = NULL; bool full_climate = true; // check if there are double slopes available for( int imgindex = 16; imgindex < totalslopes; imgindex++ ) { if( light_map->get_image_ptr(imgindex) == NULL ) { double_grounds = false; break; } } for( int imgindex = 4; imgindex < 15; imgindex++ ) { if( transition_slope_texture->get_image_ptr(imgindex) == NULL || (imgindex<=11 && transition_water_texture->get_image_ptr(imgindex) == NULL) ) { full_climate = false; break; } } // double slope needs full climates assert(!double_grounds || full_climate); // calculate the matching slopes ... doubleslope_to_imgnr[0] = 0; for( int slope = 1, slopeimgnr=1; slope < totalslopes; slope++ ) { all_rotations_beach[slope] = NULL; all_rotations_slope[slope] = NULL; doubleslope_to_imgnr[slope] = 255; if( slope != 80 && (slope_t::is_all_up(slope) || (!double_grounds && slope_t::max_diff(slope)>1) ) ) { // no need to initialize unneeded slopes // slope 80 is needed below continue; } // now add this image doubleslope_to_imgnr[slope] = slopeimgnr++; image_t *tmp_pic = NULL; switch( slope ) { case slope_t::north: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(0)->copy_rotate(180); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(0)->copy_rotate(180); break; } case slope_t::east: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(0)->copy_rotate(90); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(0)->copy_rotate(90); break; } case slope_t::south: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(0)->copy_rotate(0); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(0)->copy_rotate(0); break; } case slope_t::west: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(0)->copy_rotate(270); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(0)->copy_rotate(270); break; } case slope_t::northwest + slope_t::northeast + slope_t::southeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(1)->copy_rotate(0); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(1)->copy_rotate(0); break; } case slope_t::northeast + slope_t::southeast + slope_t::southwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(1)->copy_rotate(270); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(1)->copy_rotate(270); break; } case slope_t::southeast + slope_t::southwest + slope_t::northwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(1)->copy_rotate(180); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(1)->copy_rotate(180); break; } case slope_t::southwest + slope_t::northwest + slope_t::northeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(1)->copy_rotate(90); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(1)->copy_rotate(90); break; } case slope_t::northwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(2)->copy_rotate(90); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(2)->copy_rotate(90); break; } case slope_t::northeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(2)->copy_rotate(0); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(2)->copy_rotate(0); break; } case slope_t::southeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(2)->copy_rotate(270); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(2)->copy_rotate(270); break; } case slope_t::southwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(2)->copy_rotate(180); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(2)->copy_rotate(180); break; } case slope_t::southwest + slope_t::northeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(3)->copy_rotate(90); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(3)->copy_rotate(90); break; } case slope_t::southeast + slope_t::northwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(3)->copy_rotate(0); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(3)->copy_rotate(0); break; } case slope_t::southwest + slope_t::northeast + slope_t::southeast + slope_t::northwest: { if( double_grounds ) { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(14)->copy_rotate(0); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(11)->copy_rotate(0); } else { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(0)->copy_rotate(0); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(0)->copy_rotate(0); } break; } default: { if( full_climate ) { switch( slope ) { case slope_t::north * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(4)->copy_rotate(180); break; } case slope_t::east * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(4)->copy_rotate(90); break; } case slope_t::south * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(4)->copy_rotate(0); break; } case slope_t::west * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(4)->copy_rotate(270); break; } case slope_t::northwest * 2 + slope_t::northeast * 2 + slope_t::southeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(5)->copy_rotate(0); break; } case slope_t::northeast * 2 + slope_t::southeast * 2 + slope_t::southwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(5)->copy_rotate(270); break; } case slope_t::southeast * 2 + slope_t::southwest * 2 + slope_t::northwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(5)->copy_rotate(180); break; } case slope_t::southwest * 2 + slope_t::northwest * 2 + slope_t::northeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(5)->copy_rotate(90); break; } case slope_t::northwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(6)->copy_rotate(90); break; } case slope_t::northeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(6)->copy_rotate(0); break; } case slope_t::southeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(6)->copy_rotate(270); break; } case slope_t::southwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(6)->copy_rotate(180); break; } case slope_t::southwest * 2 + slope_t::northeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(7)->copy_rotate(90); break; } case slope_t::southeast * 2 + slope_t::northwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(7)->copy_rotate(0); break; } case slope_t::northwest + slope_t::northeast * 2 + slope_t::southeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(13)->copy_rotate(0); break; } case slope_t::northeast + slope_t::southeast * 2+slope_t::southwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(13)->copy_rotate(270); break; } case slope_t::southeast + slope_t::southwest * 2 + slope_t::northwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(13)->copy_rotate(180); break; } case slope_t::southwest + slope_t::northwest * 2 + slope_t::northeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(13)->copy_rotate(90); break; } case slope_t::northeast * 2 + slope_t::southeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(8)->copy_rotate(270); break; } case slope_t::southeast * 2 + slope_t::southwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(8)->copy_rotate(180); break; } case slope_t::southwest * 2 + slope_t::northwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(8)->copy_rotate(90); break; } case slope_t::northwest * 2 + slope_t::northeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(8)->copy_rotate(0); break; } case slope_t::northwest + slope_t::northeast * 2: { tmp_pic = transition_slope_texture->get_image_ptr(8)->copy_rotate(0); all_rotations_slope[slope] = tmp_pic->copy_flipvertical(); delete tmp_pic; break; } case slope_t::northeast + slope_t::southeast * 2: { tmp_pic = transition_slope_texture->get_image_ptr(8)->copy_rotate(90); all_rotations_slope[slope] = tmp_pic->copy_flipvertical(); delete tmp_pic; break; } case slope_t::southeast + slope_t::southwest * 2: { tmp_pic = transition_slope_texture->get_image_ptr(8)->copy_rotate(0); all_rotations_slope[slope] = tmp_pic->copy_fliphorizontal(); delete tmp_pic; break; } case slope_t::northwest * 2 + slope_t::southwest: { tmp_pic = transition_slope_texture->get_image_ptr(8)->copy_rotate(90); all_rotations_slope[slope] = tmp_pic->copy_fliphorizontal(); delete tmp_pic; break; } case slope_t::northwest * 2 + slope_t::southeast + slope_t::southwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(12)->copy_rotate(270); break; } case slope_t::northwest * 2 + slope_t::northeast * 2 + slope_t::southwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(12)->copy_rotate(180); break; } case slope_t::northwest + slope_t::northeast * 2 + slope_t::southeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(12)->copy_rotate(90); break; } case slope_t::northeast + slope_t::southeast * 2 + slope_t::southwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(12)->copy_rotate(0); break; } case slope_t::northeast * 2 + slope_t::southeast * 2 + slope_t::southwest: { tmp_pic = transition_slope_texture->get_image_ptr(12)->copy_rotate(270); all_rotations_slope[slope] = tmp_pic->copy_flipvertical(); delete tmp_pic; break; } case slope_t::northwest + slope_t::southeast * 2 + slope_t::southwest * 2: { tmp_pic = transition_slope_texture->get_image_ptr(12)->copy_rotate(180); all_rotations_slope[slope] = tmp_pic->copy_fliphorizontal(); delete tmp_pic; break; } case slope_t::northwest * 2 + slope_t::northeast + slope_t::southwest * 2: { tmp_pic = transition_slope_texture->get_image_ptr(12)->copy_rotate(90); all_rotations_slope[slope] = tmp_pic->copy_flipvertical(); delete tmp_pic; break; } case slope_t::northwest * 2 + slope_t::northeast * 2 + slope_t::southeast: { tmp_pic = transition_slope_texture->get_image_ptr(12)->copy_rotate(0); all_rotations_slope[slope] = tmp_pic->copy_fliphorizontal(); delete tmp_pic; break; } case slope_t::southwest + slope_t::southeast + slope_t::northeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(10)->copy_rotate(270); break; } case slope_t::southwest + slope_t::northwest + slope_t::southeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(10)->copy_rotate(180); break; } case slope_t::northwest + slope_t::northeast + slope_t::southwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(10)->copy_rotate(90); break; } case slope_t::southeast + slope_t::northeast + slope_t::northwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(10)->copy_rotate(0); break; } case slope_t::southwest + slope_t::southeast + slope_t::northwest * 2: { tmp_pic = transition_slope_texture->get_image_ptr(10)->copy_rotate(270); all_rotations_slope[slope] = tmp_pic->copy_flipvertical(); delete tmp_pic; break; } case slope_t::southwest + slope_t::northwest + slope_t::northeast * 2: { tmp_pic = transition_slope_texture->get_image_ptr(10)->copy_rotate(180); all_rotations_slope[slope] = tmp_pic->copy_fliphorizontal(); delete tmp_pic; break; } case slope_t::northwest + slope_t::northeast + slope_t::southeast * 2: { tmp_pic = transition_slope_texture->get_image_ptr(10)->copy_rotate(90); all_rotations_slope[slope] = tmp_pic->copy_flipvertical(); delete tmp_pic; break; } case slope_t::southeast + slope_t::northeast + slope_t::southwest * 2: { tmp_pic = transition_slope_texture->get_image_ptr(10)->copy_rotate(0); all_rotations_slope[slope] = tmp_pic->copy_fliphorizontal(); delete tmp_pic; break; } case slope_t::southeast + slope_t::northwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(9)->copy_rotate(270); break; } case slope_t::southwest + slope_t::northeast * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(9)->copy_rotate(180); break; } case slope_t::southeast * 2 + slope_t::northwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(9)->copy_rotate(90); break; } case slope_t::southwest * 2 + slope_t::northeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(9)->copy_rotate(0); break; } case slope_t::southeast * 2 + slope_t::northwest * 2 + slope_t::southwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(11)->copy_rotate(270); break; } case slope_t::southwest * 2 + slope_t::northeast * 2 + slope_t::northwest: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(11)->copy_rotate(180); break; } case slope_t::southeast * 2 + slope_t::northwest * 2 + slope_t::northeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(11)->copy_rotate(90); break; } case slope_t::southwest * 2 + slope_t::northeast * 2 + slope_t::southeast: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(11)->copy_rotate(0); break; } case slope_t::southwest * 2 + slope_t::northeast * 2 + slope_t::southeast * 2 + slope_t::northwest * 2: { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(14)->copy_rotate(0); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(11)->copy_rotate(0); break; } } } else { all_rotations_slope[slope] = NULL; if (slope == slope_t::southwest * 2 + slope_t::northeast * 2 + slope_t::southeast * 2 + slope_t::northwest * 2) { all_rotations_slope[slope] = transition_slope_texture->get_image_ptr(0)->copy_rotate(0); all_rotations_beach[slope] = transition_water_texture->get_image_ptr(0)->copy_rotate(0); } } break; } } } // from here on the images are generated by us => deletion also by us then image_offset = gfx->get_image_count(); DBG_MESSAGE("ground_desc_t::init_ground_textures()","image_offset=%d", image_offset ); // water images for water and overlay water_image = image_offset; image_t **water_stage_texture = new image_t*[water_animation_stages]; for(uint16 stage = 0; stage < water_animation_stages; stage++) { water_stage_texture[stage] = create_texture_from_tile(sea->get_image_ptr(0 /*depth*/, stage), boden_texture->get_image_ptr(water_climate)); } for( int dslope = 0; dslope < totalslopes - 1; dslope++ ) { for(uint16 stage = 0; stage < water_animation_stages; stage++) { if( doubleslope_to_imgnr[dslope] != 255 ) { int slope = double_grounds ? dslope : slopetable[dslope]; final_tile = create_textured_tile( light_map->get_image_ptr( slope ), water_stage_texture[stage], true); ground_image_list.append( final_tile ); } } } for(uint16 stage = 0; stage < water_animation_stages; stage++) { delete water_stage_texture[stage]; } delete [] water_stage_texture; // now the other transitions for( int i=0; i < number_of_climates; i++ ) { // normal tile (no transition, not snow) climate_image[i] = gfx->get_image_count(); for( int dslope = 0; dslope < totalslopes - 1; dslope++ ) { if( doubleslope_to_imgnr[dslope] != 255 ) { int slope = double_grounds ? dslope : slopetable[dslope]; final_tile = create_textured_tile( light_map->get_image_ptr( slope ), boden_texture->get_image_ptr( i+1 ) ); ground_image_list.append( final_tile ); } } } // finally full snow climate_image[number_of_climates] = final_tile->get_id() + 1; for( int dslope = 0; dslope < totalslopes - 1; dslope++ ) { if( doubleslope_to_imgnr[dslope] != 255 ) { int slope = double_grounds ? dslope : slopetable[dslope]; final_tile = create_textured_tile( light_map->get_image_ptr( slope ), boden_texture->get_image_ptr( arctic_climate ) ); ground_image_list.append( final_tile ); } } // alpha slopes for snowline for( int dslope = 1; dslope < totalslopes - 1; dslope++ ) { if( doubleslope_to_imgnr[dslope] != 255 ) { int slope = double_grounds ? dslope : slopetable[dslope]; final_tile = create_alpha_tile( light_map->get_image_ptr( slope ), dslope, all_rotations_slope[dslope] ); alpha_image[dslope] = final_tile->get_id(); } else { alpha_image[dslope] = IMG_EMPTY; } } // alpha transitions for climates for( int dslope = 0; dslope < totalslopes - 1; dslope++ ) { for( int corners = 1; corners < 16; corners++ ) { if( doubleslope_to_imgnr[dslope] != 255 ) { // slope of tile int slope = double_grounds ? dslope : slopetable[dslope]; // corners with transition uint8 double_corners = corners == 15 ? 80 : slope_from_slope4(slope4_t(corners), 1); // create alpha image final_tile = create_alpha_tile( light_map->get_image_ptr( slope ), dslope, all_rotations_slope[double_corners] ); alpha_corners_image[dslope * 15 + corners - 1] = final_tile->get_id(); double_corners = corners == 15 ? 80 : slope_from_slope4(slope4_t(15-corners), 1); if( all_rotations_beach[double_corners] ) { final_tile = create_alpha_tile( light_map->get_image_ptr( slope ), dslope, all_rotations_beach[double_corners] ); alpha_water_image[dslope * 15 + corners - 1] = final_tile->get_id(); } } else { alpha_corners_image[dslope * 15 + corners - 1] = IMG_EMPTY; alpha_water_image[dslope * 15 + corners - 1] = IMG_EMPTY; } } } #if COLOUR_DEPTH != 0 // free the helper bitmap for( int slope = 1; slope < totalslopes; slope++ ) { delete all_rotations_slope[slope]; delete all_rotations_beach[slope]; } #endif //dbg->message("ground_desc_t::calc_water_level()", "Last image nr %u", final_tile->get_pic()->imageid); DBG_DEBUG("ground_desc_t::init_ground_textures()", "Init ground textures successful"); } /* returns a ground image for all ground tiles * current is the current climate * transition is true, if the next level is another climate * snow is true, if above slowline * * Since not all of the climates are used in their numerical order, we use a * private (static table "height_to_texture_climate" for lookup) */ image_id ground_desc_t::get_ground_tile(grund_t *gr) { slope_t::type slope = gr->get_grund_hang(); sint16 height = gr->get_hoehe(); koord k = gr->get_pos().get_2d(); const sint16 tile_h = height - world->get_water_hgt(k); if( tile_h < 0 || (tile_h == 0 && slope == slope_t::flat) ) { // deep water image_array_t const* const list = sea->get_child(2); int nr = min( -tile_h, list->get_count() - 2 ); return list->get_image( nr, 0 )->get_id(); } else { const bool snow = height >= world->get_snowline(); const sint16 climate_nr = snow ? number_of_climates : (world->get_climate(k) > 1 ? world->get_climate(k) - 1 : 0); // returns base climate for tile, transitions will be overlayed later return climate_image[climate_nr] + doubleslope_to_imgnr[slope]; } return IMG_EMPTY; } image_id ground_desc_t::get_water_tile(slope_t::type slope, int stage) { return water_image + stage + water_animation_stages*doubleslope_to_imgnr[slope]; } image_id ground_desc_t::get_climate_tile(climate cl, slope_t::type slope) { return climate_image[cl <= 0 ? 0 : cl - 1] + doubleslope_to_imgnr[slope]; } image_id ground_desc_t::get_snow_tile(slope_t::type slope) { return climate_image[number_of_climates] + doubleslope_to_imgnr[slope]; } image_id ground_desc_t::get_beach_tile(slope_t::type slope, uint8 corners) { return alpha_water_image[slope * 15 + corners - 1]; } image_id ground_desc_t::get_alpha_tile(slope_t::type slope, uint8 corners) { return alpha_corners_image[slope * 15 + corners - 1]; } image_id ground_desc_t::get_alpha_tile(slope_t::type slope) { return alpha_image[slope]; } simutrans-124.5/src/simutrans/descriptor/ground_desc.h000066400000000000000000000062551520675460500232330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_GROUND_DESC_H #define DESCRIPTOR_GROUND_DESC_H #include "obj_base_desc.h" #include "image_array.h" #include "../simtypes.h" #include "../dataobj/ribi.h" class grund_t; class karte_t; /** * Images of all possible surface tiles: slopes, climates, transitions, etc. * * Child nodes: * 0 Name * 1 Copyright * 2 Image-array */ class ground_desc_t : public obj_named_desc_t { private: static karte_t *world; static image_id image_offset; public: static uint16 water_animation_stages; static sint16 water_depth_levels; // only these textures need external access static const ground_desc_t *shore; // nicer shore graphics, optional static const ground_desc_t *fundament; static const ground_desc_t *slopes; static const ground_desc_t *fences; static const ground_desc_t *marker; static const ground_desc_t *borders; static const ground_desc_t *sea; // different water depth static const ground_desc_t *outside; static char const* get_climate_name_from_bit(climate n); static bool double_grounds; static const uint8 slopetable[80]; // returns the pointer to an image structure const image_t *get_image_ptr(uint16 typ, uint16 stage=0) const { image_array_t const* const imgarray = get_child(2); image_list_t const* const list = imgarray->get_list(typ); if(list && list->get_count() > 0) { image_t const* const image = imgarray->get_image(typ, stage); return image; } return NULL; } // image for all non-climate stuff like foundations ... image_id get_image(uint16 typ, uint16 stage=0) const { image_t const* const image = get_image_ptr(typ, stage); return image ? image->get_id() : IMG_EMPTY; } // image for all ground tiles static image_id get_ground_tile(grund_t *gr); static image_id get_water_tile(slope_t::type slope, int stage); static image_id get_climate_tile(climate cl, slope_t::type slope); static image_id get_snow_tile(slope_t::type slope); static image_id get_beach_tile(slope_t::type slope, uint8 corners); static image_id get_alpha_tile(slope_t::type slope); static image_id get_alpha_tile(slope_t::type slope, uint8 corners); static bool register_desc(const ground_desc_t *desc); static bool successfully_loaded(); /** * Generates ground texture images, transition maps, etc. */ static void init_ground_textures(karte_t *world); static image_id get_marker_image(slope_t::type slope_in, bool background) { uint8 slope = double_grounds ? slope_in : slopetable[slope_in]; uint8 index = background ? (double_grounds ? (slope % 3) + 3 * ((uint8)(slope / 9)) + 27 : ((slope & 1) + ((slope >> 1) & 6) + 8)) : (double_grounds ? slope % 27 : (slope & 7 )); return marker->get_image(index); } static image_id get_border_image(slope_t::type slope_in) { uint8 slope = double_grounds ? slope_in : slopetable[slope_in]; uint8 index = double_grounds ? (slope % 3) + 3 * ((uint8)(slope / 9)) : (slope & 1) + ((slope >> 1) & 6); return borders->get_image(index); } }; #endif simutrans-124.5/src/simutrans/descriptor/groundobj_desc.h000066400000000000000000000047071520675460500237260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_GROUNDOBJ_DESC_H #define DESCRIPTOR_GROUNDOBJ_DESC_H #include "../simtypes.h" #include "obj_base_desc.h" #include "image_array.h" #include "../network/checksum.h" /** * This is the description for ground objects like small lakes, hunting post, birds, flower patch, stones, sheeps, ... * these can either reside on a tile (moving=0) * or move around the map (water_t=only on water, air_t=everywhere) * They are removable with certain costs. * * Child nodes: * 0 Name * 1 Copyright * 2 Image-array */ class groundobj_desc_t : public obj_named_desc_t { friend class groundobj_reader_t; friend class groundobj_t; friend class movingobj_t; climate_bits allowed_climates; uint16 distribution_weight; uint8 number_of_seasons; sint32 speed; uint16 index; bool trees_on_top; waytype_t wtyp; sint64 price; public: uint16 get_distribution_weight() const { return distribution_weight; } bool is_allowed_climate( climate cl ) const { return ((1<(2)->get_image(phase, season); } image_id get_image_id(uint8 season, uint16 phase) const { const image_t *image = get_child(2)->get_image(phase, season); return image != NULL ? image->get_id() : IMG_EMPTY; } // moving stuff should have eight // otherwise up to 16 for all slopes are ok // if count==1, this will not appear on slopes uint16 get_phases() const { return get_child(2)->get_count(); } uint8 get_seasons() const { return number_of_seasons; } sint32 get_speed() const { return speed; } bool can_build_trees_here() const { return trees_on_top; } waytype_t get_waytype() const { return wtyp; } sint64 get_price() const { return price; } uint16 get_index() const { return index; } void calc_checksum(checksum_t *chk) const { chk->input((uint8)allowed_climates); chk->input(distribution_weight); chk->input(number_of_seasons); chk->input(speed); chk->input(trees_on_top); chk->input((uint8)wtyp); chk->input(price); } }; #endif simutrans-124.5/src/simutrans/descriptor/image.cc000066400000000000000000000111241520675460500221460ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "image.h" #include "../display/simgraph.h" #include "../simtypes.h" #include "../simdebug.h" #include "../macros.h" #include #include /** * Definition of special colors */ const uint32 image_t::rgbtab[SPECIAL] = { 0x244B67, // Player color 1 0x395E7C, 0x4C7191, 0x6084A7, 0x7497BD, 0x88ABD3, 0x9CBEE9, 0xB0D2FF, 0x7B5803, // Player color 2 0x8E6F04, 0xA18605, 0xB49D07, 0xC6B408, 0xD9CB0A, 0xECE20B, 0xFFF90D, 0x57656F, // Dark windows, lit yellowish at night 0x7F9BF1, // Lighter windows, lit blueish at night 0xFFFF53, // Yellow light 0xFF211D, // Red light 0x01DD01, // Green light 0x6B6B6B, // Non-darkening grey 1 (menus) 0x9B9B9B, // Non-darkening grey 2 (menus) 0xB3B3B3, // non-darkening grey 3 (menus) 0xC9C9C9, // Non-darkening grey 4 (menus) 0xDFDFDF, // Non-darkening grey 5 (menus) 0xE3E3FF, // Nearly white light at day, yellowish light at night 0xC1B1D1, // Windows, lit yellow 0x4D4D4D, // Windows, lit yellow 0xFF017F, // purple light 0x0101FF, // blue light }; image_t* image_t::copy_image(const image_t& other) { image_t* img = new image_t(other.len); img->len = other.len; img->x = other.x; img->y = other.y; img->w = other.w; img->h = other.h; img->imageid = IMG_EMPTY; img->zoomable = other.zoomable; memcpy(img->data, other.data, other.len * sizeof(PIXVAL)); return img; } // creates a single pixel dummy picture image_t* image_t::create_single_pixel() { image_t* desc = new image_t(4); desc->len = 4; desc->x = 0; desc->y = 0; desc->w = 1; desc->h = 1; desc->zoomable = 0; desc->data[0] = 0; desc->data[1] = 0; desc->data[2] = 0; desc->data[3] = 0; return desc; } /* rotate_image_data - produces a (rotated) image * only rotates by 90 degrees or multiples thereof, and assumes a square image * Otherwise it will only succeed for angle=0; */ image_t *image_t::copy_rotate(const sint16 angle) const { #if COLOUR_DEPTH == 0 (void)angle; return create_single_pixel(); #endif assert(angle == 0 || (w == h && x == 0 && y == 0)); image_t* target_image = copy_image(*this); // the format is // transparent PIXELVAL number // PIXEL number of pixels, data*PIXVAL // repeated until zero transparent pixels // in pak64 case it is 0 64 64*PIXVAL 0 for a single line, e.g. 70 bytes per line for pak64 // first data will have an offset of two PIXVAL // now you should understand below arithmetics ... sint16 const x_y = w; PIXVAL const* const src = get_data(); PIXVAL* const target = target_image->get_data(); switch(angle) { case 90: for(int j=0; jget_data(); for( int j = 0; j < x_y; j++ ) { for( int i = 0; i < x_y; i++ ) { target[i * (x_y + 3) + j + 2] = src[(x_y - i - 1) * (x_y + 3) + j + 2]; } } return target_image; } image_t *image_t::copy_fliphorizontal() const { image_t* target_image = copy_image(*this); // the format is // transparent PIXELVAL number // PIXEL number of pixels, data*PIXVAL // repeated until zero transparent pixels // in pak64 case it is 0 64 64*PIXVAL 0 for a single line, e.g. 70 bytes per line for pak64 // first data will have an offset of two PIXVAL // now you should understand below arithmetics ... sint16 const x_y = w; PIXVAL const* const src = get_data(); PIXVAL* const target = target_image->get_data(); for( int i = 0; i < x_y; i++ ) { for( int j = 0; j < x_y; j++ ) { target[i * (x_y + 3) + j + 2] = src[i * (x_y + 3) + (x_y - j - 1) + 2]; } } return target_image; } simutrans-124.5/src/simutrans/descriptor/image.h000066400000000000000000000034351520675460500220160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_IMAGE_H #define DESCRIPTOR_IMAGE_H #include "../display/simgraph.h" #include "../display/simimg.h" #include "obj_desc.h" // number of special colors #define SPECIAL (31) #define SPECIAL_TRANSPARENT (0x00E7FFFF) /** * Data of one image * * Child nodes: * (none) */ class image_t : public obj_desc_t { public: static const uint32 rgbtab[SPECIAL]; size_t len; ///< length of data[] in PIXVAL units scr_coord_val x; ///< x offset of data[] image scr_coord_val y; ///< y offset of data[] image scr_coord_val w; ///< width of data[] image scr_coord_val h; ///< height of data[] image image_id imageid; ///< Graphics renderer image id uint8 zoomable; ///< some images may not be zoomed i.e. icons PIXVAL *data; ///< RLE encoded image data image_t(size_t len_=0) : data(NULL) { if (len_) { alloc(len_); } } ~image_t() { delete [] data; } void alloc(size_t len_) { delete [] data; data = new PIXVAL[len_]; len = len_; } static image_t* copy_image(const image_t& other); const image_t* get_pic() const { return this; } uint16 const* get_data() const { return data; } uint16* get_data() { return data; } image_id get_id() const { return imageid; } /* rotate_image_data - produces a (rotated) image * only rotates by 90 degrees or multiples thereof, and assumes a square image * Otherwise it will only succeed for angle=0; */ image_t* copy_rotate(const sint16 angle) const; image_t* copy_flipvertical() const; image_t* copy_fliphorizontal() const; static image_t* create_single_pixel(); void register_image() { this->imageid = gfx->register_image(this); } private: friend class image_reader_t; }; #endif simutrans-124.5/src/simutrans/descriptor/image_array.h000066400000000000000000000013671520675460500232160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_IMAGE_ARRAY_H #define DESCRIPTOR_IMAGE_ARRAY_H #include "image_list.h" /** * Two-dimensional array of images * * Child nodes: * 0 1st Image-list * 1 2nd Image-list * ... ... */ class image_array_t : public obj_desc_t { friend class imagelist2d_reader_t; uint16 count; public: image_array_t() : count(0) {} uint16 get_count() const { return count; } image_list_t const* get_list(uint16 i) const { return i < count ? get_child(i) : 0; } image_t const* get_image(uint16 i, uint16 j) const { return i < count ? get_child(i)->get_image(j) : 0; } }; #endif simutrans-124.5/src/simutrans/descriptor/image_list.h000066400000000000000000000013101520675460500230370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_IMAGE_LIST_H #define DESCRIPTOR_IMAGE_LIST_H #include "image.h" /** * One-dimensional image list. * * Child nodes: * 0 1st Image * 1 2nd Image * ... ... */ class image_list_t : public obj_desc_t { friend class imagelist_reader_t; uint16 count; public: image_list_t() : count(0) {} uint16 get_count() const { return count; } image_t const* get_image(uint16 i) const { return i < count ? get_child(i) : 0; } image_id get_image_id(uint16 i) const { const image_t *image = get_image(i); return image != NULL ? image->get_id() : IMG_EMPTY; } }; #endif simutrans-124.5/src/simutrans/descriptor/intro_dates.h000066400000000000000000000005061520675460500232430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_INTRO_DATES_H #define DESCRIPTOR_INTRO_DATES_H // just some default intro and retirement dates, if nothing else is defined #define DEFAULT_INTRO_YEAR (1900) #define DEFAULT_RETIRE_YEAR (2999) #endif simutrans-124.5/src/simutrans/descriptor/obj_base_desc.cc000066400000000000000000000025361520675460500236350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "obj_base_desc.h" #include "../network/checksum.h" #include "intro_dates.h" #include "../obj/way/weg.h" #include "../simdebug.h" void obj_desc_timelined_t::calc_checksum(checksum_t *chk) const { #if MSG_LEVEL>0 PAKSET_INFO("name=","%s",get_name()); if(get_copyright()) PAKSET_INFO("copyright=","%s",get_copyright()); if(intro_date!=DEFAULT_INTRO_YEAR*12) { PAKSET_INFO("intro_year=","%d",intro_date/12); if(intro_date%12) PAKSET_INFO("intro_month=","%d",(intro_date%12)+1); } if(retire_date!=DEFAULT_RETIRE_YEAR*12) { PAKSET_INFO("retire_year=","%d",retire_date/12); if(retire_date%12) PAKSET_INFO("retire_month=","%d",(retire_date%12)+1); } #endif chk->input(intro_date); chk->input(retire_date); } void obj_desc_transport_related_t::calc_checksum(checksum_t *chk) const { obj_desc_timelined_t::calc_checksum(chk); #if MSG_LEVEL>0 if(maintenance) PAKSET_INFO("maintenance=","%d",maintenance); if(price) PAKSET_INFO("cost=","%d",price); PAKSET_INFO("waytype=","%s",weg_t::waytype_to_string((waytype_t)wtyp)); if(topspeed) PAKSET_INFO("speed=","%d",topspeed); if(axle_load) PAKSET_INFO("axle_load=","%d",axle_load); #endif chk->input(maintenance); chk->input(price); chk->input(wtyp); chk->input(topspeed); chk->input(axle_load); } simutrans-124.5/src/simutrans/descriptor/obj_base_desc.h000066400000000000000000000057261520675460500235030ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_OBJ_BASE_DESC_H #define DESCRIPTOR_OBJ_BASE_DESC_H #include "intro_dates.h" #include "text_desc.h" class checksum_t; class tool_t; /** * Common base class for all object descriptors, which get their name and * copyright information from child 0 and 1 */ class obj_named_desc_t : public obj_desc_t { public: const char *get_name() const { return get_child(0)->get_text(); } const char *get_copyright() const { const text_desc_t *const ts = get_child(1); if (!ts) { return 0; } const char *const text = ts->get_text(); return text[0] != '\0' ? text : NULL; } }; /** * Base class for all stuff that depends on timeline. */ class obj_desc_timelined_t : public obj_named_desc_t { protected: uint16 intro_date = DEFAULT_INTRO_YEAR * 12; ///< this thing is available from this date uint16 retire_date = DEFAULT_RETIRE_YEAR * 12; ///< this thing is available until this date public: obj_desc_timelined_t() : obj_named_desc_t(), intro_date(0), retire_date(0) {} uint16 get_intro_year_month() const { return intro_date; } uint16 get_retire_year_month() const { return retire_date; } /// @return true if this object is available with timeline. bool is_available(const uint16 month_now) const { return month_now==0 || (intro_date<=month_now && retire_date>month_now); } /// @return true if this is still not available bool is_future(const uint16 month_now) const { return month_now && (intro_date > month_now); } /// @return true if this is obsolete bool is_retired(const uint16 month_now) const { return month_now && (retire_date <= month_now); } void calc_checksum(checksum_t *chk) const; }; /** * Base class for all transport related stuff. */ class obj_desc_transport_related_t : public obj_desc_timelined_t { protected: sint64 maintenance = 0; ///< monthly cost for bits_per_month=18 sint64 price = 0; ///< cost to build this thing [1/100 credits] per tile/object uint16 axle_load = 9999; ///< up to this load vehicle may pass (default 9999) uint8 wtyp = 255; ///< waytype of this thing sint32 topspeed = 0; ///< maximum allowed speed in km/h public: sint64 get_maintenance() const { return maintenance; } sint64 get_price() const { return price; } waytype_t get_waytype() const { return static_cast(wtyp); } waytype_t get_wtyp() const { return get_waytype(); } sint32 get_topspeed() const { return topspeed; } uint16 get_axle_load() const { return axle_load; } void calc_checksum(checksum_t *chk) const; }; /** * Base class for all transport infrastructure. */ class obj_desc_transport_infrastructure_t : public obj_desc_transport_related_t { protected: tool_t *builder = NULL; ///< default tool for building public: tool_t *get_builder() const { return builder; } void set_builder( tool_t *tool ) { builder = tool; } }; #endif simutrans-124.5/src/simutrans/descriptor/obj_desc.h000066400000000000000000000023531520675460500225020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_OBJ_DESC_H #define DESCRIPTOR_OBJ_DESC_H #include #include "../simtypes.h" /** * Basis of all desc_t classes, which are loaded from the .pak files. * No virtual methods are allowed! */ class obj_desc_t { friend class pakset_manager_t; public: obj_desc_t() : children() {} ~obj_desc_t() { delete [] children; } void* operator new(size_t size) { return ::operator new(size); } void* operator new(size_t size, size_t extra) { return ::operator new(size + extra); } /* * Only support basic delete operator. * Prevents C++14 and newer compilers from implicitly adding a sized delete operator. * The sized delete operator conflicts with the definiton of the placement new operator. */ void operator delete(void* ptr) { return ::operator delete(ptr); } protected: template T const* get_child(int const i) const { return static_cast(children[i]); } private: /* * Internal Node information - the derived class knows, * how many node child nodes really exist. */ obj_desc_t** children; friend class factory_field_group_reader_t; friend class obj_reader_t; }; #endif simutrans-124.5/src/simutrans/descriptor/obj_node_info.h000066400000000000000000000011201520675460500235130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_OBJ_NODE_INFO_H #define DESCRIPTOR_OBJ_NODE_INFO_H #include "../simtypes.h" // these are the sizes of the divers heades on the disk // if the uint16 size is LARGE_RECORD_SIZE the actual length is in a following uint32 #define OBJ_NODE_INFO_SIZE (8) #define EXT_OBJ_NODE_INFO_SIZE (12) #define LARGE_RECORD_SIZE (0xFFFFu) /** * Stored structure of a pak node inside the file. */ struct obj_node_info_t { uint32 type; uint16 nchildren; uint32 size; }; #endif simutrans-124.5/src/simutrans/descriptor/objversion.h000066400000000000000000000043051520675460500231110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_OBJVERSION_H #define DESCRIPTOR_OBJVERSION_H #include "../simtypes.h" #define COMPILER_VERSION "0.1.3exp" #define COMPILER_VERSION_CODE_11 (0 * 1000000 + 1 * 1000 + 1) #define COMPILER_VERSION_CODE (0 * 1000000 + 1 * 1000 + 3) /* * obj_type value are stored inside the pak-files. Values are choosen to make * them somewhat readable (up to 4 uppercase letters describing the type). * obj as 4 byte: 3073094 bytes total * obj as 2 byte: 3063046 bytes total * obj as 1 byte: 3058022 bytes total * saves 4 to 1: 15072 bytes = 0,5% not worth it */ #define C4ID(a, b ,c ,d) (((uint32)a) | ((uint32)b) << 8 | ((uint32)c) << 16 | ((uint32)d) << 24) enum obj_type : uint32 { obj_bridge = C4ID('B','R','D','G'), obj_building = C4ID('B','U','I','L'), obj_citycar = C4ID('C','C','A','R'), obj_crossing = C4ID('C','R','S','S'), obj_cursor = C4ID('C','U','R','S'), obj_factory = C4ID('F','A','C','T'), obj_ffield = C4ID('F','F','I','E'), obj_ffldclass = C4ID('F','F','C','L'), obj_field = C4ID('F','I','E','L'), obj_fproduct = C4ID('F','P','R','O'), obj_fsmoke = C4ID('F','S','M','O'), obj_fsupplier = C4ID('F','S','U','P'), obj_good = C4ID('G','O','O','D'), obj_ground = C4ID('G','R','N','D'), obj_groundobj = C4ID('G','O','B','J'), obj_image = C4ID('I','M','G', 0 ), obj_imagelist = C4ID('I','M','G','1'), obj_imagelist2d = C4ID('I','M','G','2'), obj_menu = C4ID('M','E','N','U'), obj_miscimages = C4ID('M','I','S','C'), obj_pedestrian = C4ID('P','A','S','S'), obj_roadsign = C4ID('S','I','G','N'), obj_root = C4ID('R','O','O','T'), obj_smoke = C4ID('S','M','O','K'), obj_sound = C4ID('S','O','U','N'), obj_symbol = C4ID('S','Y','M','B'), obj_text = C4ID('T','E','X','T'), obj_tile = C4ID('T','I','L','E'), obj_tree = C4ID('T','R','E','E'), obj_tunnel = C4ID('T','U','N','L'), obj_vehicle = C4ID('V','H','C','L'), obj_way = C4ID('W','A','Y', 0 ), obj_way_obj = C4ID('W','Y','O','B'), obj_xref = C4ID('X','R','E','F') }; #endif simutrans-124.5/src/simutrans/descriptor/pedestrian_desc.h000066400000000000000000000025451520675460500240710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_PEDESTRIAN_DESC_H #define DESCRIPTOR_PEDESTRIAN_DESC_H #include "obj_base_desc.h" #include "image_array.h" #include "image_list.h" #include "../dataobj/ribi.h" #include "../network/checksum.h" /** * Pedestrians. * * Child nodes: * 0 Name * 1 Copyright * 2 Image-list or 2d */ class pedestrian_desc_t : public obj_desc_timelined_t { friend class pedestrian_reader_t; uint16 distribution_weight; uint16 steps_per_frame; uint16 offset; public: image_id get_image_id(ribi_t::dir dir, uint16 phase=0) const { image_t const* image = NULL; if (steps_per_frame > 0) { image = get_child(2)->get_image(dir, phase); } else { image = get_child(2)->get_image(dir); } return image != NULL ? image->get_id() : IMG_EMPTY; } uint16 get_distribution_weight() const { return distribution_weight; } uint16 get_steps_per_frame() const { return steps_per_frame; } uint16 get_animation_count(ribi_t::dir dir) const { return steps_per_frame>0 ? get_child(2)->get_list(dir)->get_count() : 1; } void calc_checksum(checksum_t *chk) const { chk->input(distribution_weight); } // images are offset steps away from boundary uint16 get_offset() const { return offset; } }; #endif simutrans-124.5/src/simutrans/descriptor/reader/000077500000000000000000000000001520675460500220205ustar00rootroot00000000000000simutrans-124.5/src/simutrans/descriptor/reader/bridge_reader.cc000066400000000000000000000143161520675460500251120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simdebug.h" #include "../../builder/brueckenbauer.h" #include "../bridge_desc.h" #include "../intro_dates.h" #include "bridge_reader.h" #include "../obj_node_info.h" #include "../../network/pakset_info.h" #include #include void bridge_reader_t::register_obj(obj_desc_t *&data) { bridge_desc_t *desc = static_cast(data); bridge_builder_t::register_desc(desc); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } obj_desc_t *bridge_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const uint16 version = v & 0x8000 ? v & 0x7FFF : 0; // some defaults bridge_desc_t *desc = new bridge_desc_t; desc->maintenance = 800; // non-default maintenance for old objects if (version == 0) { desc->wtyp = (uint8)v; decode_uint16(p); // Menupos, not used any more desc->price = decode_uint32(p); } else if (version == 1) { desc->wtyp = (uint8)decode_uint16(p); desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); } else if (version == 2) { desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); } else if (version == 3) { // pillars added desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->pillars_every = decode_uint8(p); } else if (version == 4) { // max length desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->pillars_every = decode_uint8(p); desc->max_length = decode_uint8(p); } else if (version == 5) { // timeline desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->pillars_every = decode_uint8(p); desc->max_length = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); } else if (version == 6) { // snow desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->pillars_every = decode_uint8(p); desc->max_length = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); } else if (version==7 || version==8) { // max_height, asymmetric pillars desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->pillars_every = decode_uint8(p); desc->max_length = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->pillars_asymmetric = decode_uint8(p) != 0; desc->max_height = decode_uint8(p); desc->number_of_seasons = decode_uint8(p); } else if (version==9) { // axle load desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->pillars_every = decode_uint8(p); desc->max_length = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->pillars_asymmetric = decode_uint8(p) != 0; desc->axle_load = decode_uint16(p); desc->max_height = decode_uint8(p); desc->number_of_seasons = decode_uint8(p); } else if (version == 10) { // cost/maintenance as 64 bit ints desc->topspeed = decode_uint16(p); desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->wtyp = decode_uint8(p); desc->pillars_every = decode_uint8(p); desc->max_length = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->pillars_asymmetric = decode_uint8(p) != 0; desc->axle_load = decode_uint16(p); desc->max_height = decode_uint8(p); desc->number_of_seasons = decode_uint8(p); } else if (version == 11) { // cost/maintenance as 64 bit ints desc->topspeed = decode_uint16(p); desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->wtyp = decode_uint8(p); desc->pillars_every = decode_uint8(p); desc->max_length = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->pillars_asymmetric = decode_uint8(p) != 0; desc->axle_load = decode_uint16(p); desc->max_height = decode_uint8(p); desc->clip_below = decode_uint8(p); desc->number_of_seasons = decode_uint8(p); } else { dbg->fatal( "bridge_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // pillars cannot be higher than this to avoid drawing errors if(desc->pillars_every>0 && desc->max_height==0) { desc->max_height = 7; } // indicate for different copyright/name lookup desc->offset = version<8 ? 0 : 2; if( version < 9 ) { desc->axle_load = 9999; } if (version < 11) { desc->clip_below = desc->wtyp != powerline_wt; } PAKSET_INFO("bridge_reader_t::read_node()", "version=%d, waytype=%d, price=%" PRId64 ", maintenance=%" PRId64 ", topspeed=%d, axle_load=%i, max_length=%i, max_height=%i, pillars=%i, asymmetric=%i, seasons=%i, intro=%i/%i, retire=%i/%i", version, desc->wtyp, desc->price, desc->maintenance, desc->topspeed, desc->axle_load, desc->max_length, desc->max_height, desc->pillars_every, desc->pillars_asymmetric, desc->number_of_seasons, (desc->intro_date%12)+1, desc->intro_date/12, (desc->retire_date%12)+1, desc->retire_date/12); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/bridge_reader.h000066400000000000000000000010371520675460500247500ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_BRIDGE_READER_H #define DESCRIPTOR_READER_BRIDGE_READER_H #include "obj_reader.h" class bridge_reader_t : public obj_reader_t { OBJ_READER_DEF(bridge_reader_t, obj_bridge, "bridge"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/building_reader.cc000066400000000000000000000442721520675460500254570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../builder/hausbauer.h" #include "../../simdebug.h" #include "../building_desc.h" #include "../intro_dates.h" #include "../obj_node_info.h" #include "building_reader.h" #include "../../network/pakset_info.h" /** * Old building types, for compatibility ... */ struct old_btyp { /** * From type "unknown" also come special buildings e.q. Townhall */ enum typ { wohnung, gewerbe, industrie, unknown }; }; obj_desc_t * tile_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the highest bit was always cleared. const uint16 v = decode_uint16(p); const int version = (v & 0x8000)!=0 ? v&0x7FFF : 0; building_tile_desc_t *desc = new building_tile_desc_t(); if(version == 2) { // PAKSET_INFO("tile_reader_t::read_node()","version=1"); // Versioned node, version 1 desc->phases = (uint8)decode_uint16(p); desc->index = decode_uint16(p); desc->seasons = decode_uint8(p); desc->building = NULL; } else if(version == 1) { // PAKSET_INFO("tile_reader_t::read_node()","version=1"); // Versioned node, version 1 desc->phases = (uint8)decode_uint16(p); desc->index = decode_uint16(p); desc->seasons = 1; desc->building = NULL; } else { if( version ) { dbg->fatal( "tile_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // skip the pointer ... p += 2; desc->phases = (uint8)decode_uint16(p); desc->index = decode_uint16(p); desc->seasons = 1; desc->building = NULL; } // PAKSET_INFO("tile_reader_t::read_node()","phases=%i, index=%i, seasons=%i", desc->phases, desc->index, desc->seasons ); return desc; } void building_reader_t::register_obj(obj_desc_t *&data) { building_desc_t *desc = static_cast(data); if( desc->type == building_desc_t::factory ) { if( desc->enables == 0 ) { // this stuff is just for compatibility if( strcmp("Oelbohrinsel",desc->get_name())==0 ) { desc->enables = 1|2|4; } else if( strcmp("fish_swarm",desc->get_name())==0 ) { desc->enables = 4; } } } if( desc->type == building_desc_t::others && desc->enables == 0x80 ) { // this stuff is just for compatibility size_t checkpos = strlen(desc->get_name()); desc->enables = 0; // before station buildings were identified by their name ... if( strcmp("BusStop",desc->get_name()+checkpos-7)==0 ) { desc->type = building_desc_t::generic_stop; desc->extra_data = road_wt; desc->enables = 1; } if( strcmp("CarStop",desc->get_name()+checkpos-7)==0 ) { desc->type = building_desc_t::generic_stop; desc->extra_data = road_wt; desc->enables = 4; } else if( strcmp("TrainStop",desc->get_name()+checkpos-9)==0 ) { desc->type = building_desc_t::generic_stop; desc->extra_data = track_wt; desc->enables = 1|4; } else if( strcmp("ShipStop",desc->get_name()+checkpos-8)==0 ) { desc->type = building_desc_t::dock; desc->extra_data = water_wt; desc->enables = 1|4; } else if( strcmp("ChannelStop",desc->get_name()+checkpos-11)==0 ) { desc->type = building_desc_t::generic_stop; desc->extra_data = water_wt; desc->enables = 1|4; } else if( strcmp("PostOffice",desc->get_name()+checkpos-10)==0 ) { desc->type = building_desc_t::generic_extension; desc->extra_data = 0; desc->enables = 2; } else if( strcmp("StationBlg",desc->get_name()+checkpos-10)==0 ) { desc->type = building_desc_t::generic_extension; desc->extra_data = 0; desc->enables = 1|4; } } // now old style depots ... else if( desc->type==building_desc_t::others ) { size_t checkpos = strlen(desc->get_name()); if( strcmp("AirDepot",desc->get_name()+checkpos-8)==0 ) { desc->type = building_desc_t::depot; desc->extra_data = (uint16)air_wt; } else if( strcmp("TrainDepot",desc->get_name())==0 ) { desc->type = building_desc_t::depot; desc->extra_data = (uint16)track_wt; } else if( strcmp("TramDepot",desc->get_name())==0 ) { desc->type = building_desc_t::depot; desc->extra_data = (uint16)tram_wt; } else if( strcmp("MonorailDepot",desc->get_name())==0 ) { desc->type = building_desc_t::depot; desc->extra_data = (uint16)monorail_wt; } else if( strcmp("CarDepot",desc->get_name())==0 ) { desc->type = building_desc_t::depot; desc->extra_data = (uint16)road_wt; } else if( strcmp("ShipDepot",desc->get_name())==0 ) { desc->type = building_desc_t::depot; desc->extra_data = (uint16)water_wt; } } // and finally old stations ... // correct all building types in building_desc_t::old_building_types_t else if( (uint8)desc->get_type()>=building_desc_t::bahnhof && (uint8)desc->get_type()<=building_desc_t::lagerhalle) { // compatibility stuff static uint16 old_to_new_waytype[16] = { track_wt, road_wt, road_wt, water_wt, water_wt, air_wt, monorail_wt, 0, track_wt, road_wt, road_wt, 0 , water_wt, air_wt, monorail_wt, 0 }; uint8 type = desc->type; desc->extra_data = type <= building_desc_t::monorail_geb ? old_to_new_waytype[type-building_desc_t::bahnhof] : 0; if( type !=building_desc_t::dock ) { desc->type = type < building_desc_t::bahnhof_geb ? building_desc_t::generic_stop : building_desc_t::generic_extension; } } // after this point all building_desc_t's have type in building_desc_t::btype // allowed layouts are 1,2,4,8,16, where 8,16 is reserved for stations uint8 l = desc->type == building_desc_t::generic_stop ? 16 : 4; while (l > 0) { if ((desc->layouts & l) != 0 && (desc->layouts != l)) { dbg->error( "building_reader_t::register_obj()", "Building %s has %i layouts (illegal) => set to %i", desc->get_name(), desc->layouts, l ); desc->layouts = l; break; } l >>= 1; } if( desc->allow_underground == 255 ) { // only old stops were allowed underground desc->allow_underground = desc->type==building_desc_t::generic_stop ? 2 : 0; } if (hausbauer_t::register_desc(desc)) { // PAKSET_INFO("building_reader_t::register_obj", "Loaded '%s'", desc->get_name()); // do not calculate checksum of factory, will be done in factory_reader_t if( desc->type != building_desc_t::factory ) { checksum_t *chk = new checksum_t(); PAKSET_INFO("obj=building",""); desc->calc_checksum(chk); PAKSET_INFO("--",""); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } } } bool building_reader_t::successfully_loaded() const { return hausbauer_t::successfully_loaded(); } obj_desc_t *building_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the highest bit was always cleared. const uint16 v = decode_uint16(p); const uint16 version = (v & 0x8000)!=0 ? v&0x7FFF : 0; old_btyp::typ btyp; building_desc_t *desc = new building_desc_t(); if (version == 11) { // cost/maintenance as sint64 btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = (climate_bits)(decode_uint16(p) & ALL_CLIMATES); desc->enables = decode_uint8(p); desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->animation_time = decode_uint16(p); desc->capacity = decode_uint16(p); desc->maintenance = decode_sint64(p); desc->price = decode_sint64(p); desc->allow_underground = decode_uint8(p); desc->preservation_year_month = decode_uint16(p); } else if (version == 10) { // preservation date added btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = (climate_bits)(decode_uint16(p) & ALL_CLIMATES); desc->enables = decode_uint8(p); desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->animation_time = decode_uint16(p); desc->capacity = decode_uint16(p); desc->maintenance = decode_sint32(p); desc->price = decode_sint32(p); desc->allow_underground = decode_uint8(p); desc->preservation_year_month = decode_uint16(p); } else if (version == 8 || version == 9) { // station price, maintenance and capacity added btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = (climate_bits)(decode_uint16(p) & ALL_CLIMATES); desc->enables = decode_uint8(p); desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->animation_time = decode_uint16(p); desc->capacity = decode_uint16(p); desc->maintenance = decode_sint32(p); desc->price = decode_sint32(p); desc->allow_underground = decode_uint8(p); } else if(version == 7) { // underground mode added btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = (climate_bits)(decode_uint16(p) & ALL_CLIMATES); desc->enables = decode_uint8(p); desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->animation_time = decode_uint16(p); desc->allow_underground = decode_uint8(p); } else if(version == 5 || version==6) { // animation interval in ms added // only level logic is different between version 5 and 6 btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = (climate_bits)(decode_uint16(p) & ALL_CLIMATES); desc->enables = decode_uint8(p); desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->animation_time = decode_uint16(p); } else if(version == 4) { // climates and seasons added btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = (climate_bits)(decode_uint16(p) & ALL_CLIMATES); desc->enables = decode_uint8(p); desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->animation_time = 300; } else if(version == 3) { btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = all_but_water_climate; // all but water desc->enables = decode_uint8(p); desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->animation_time = 300; } else if(version == 2) { // added intro/retire date btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = all_but_water_climate; // all but water desc->enables = 0x80; desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->animation_time = 300; } else if(version == 1) { btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = all_but_water_climate; // all but water desc->enables = 0x80; desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = DEFAULT_INTRO_YEAR*12; desc->retire_date = DEFAULT_RETIRE_YEAR*12; desc->animation_time = 300; } else if (version == 0) { // old node, version 0 btyp = (old_btyp::typ)v; decode_uint16(p); desc->type = (building_desc_t::btype)decode_uint32(p); desc->level = decode_uint32(p); desc->extra_data = decode_uint32(p); desc->size.x = decode_uint16(p); desc->size.y = decode_uint16(p); desc->layouts = decode_uint32(p); desc->allowed_climates = all_but_water_climate; // all but water desc->enables = 0x80; desc->flags = (building_desc_t::flag_t)decode_uint32(p); desc->distribution_weight = 100; desc->intro_date = DEFAULT_INTRO_YEAR*12; desc->retire_date = DEFAULT_RETIRE_YEAR*12; desc->animation_time = 300; } else { dbg->fatal( "building_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // there are additional nodes for cursor/icon if( node.nchildren > 2+desc->size.x*desc->size.y*desc->layouts ) { desc->flags |= building_desc_t::FLAG_HAS_CURSOR; } // correct old station buildings if( version<=3 && ((uint8)desc->type >= building_desc_t::bahnhof || desc->type == building_desc_t::factory || desc->type == building_desc_t::depot) && desc->level==0 ) { PAKSET_INFO("building_reader_t::read_node()","old station building -> set level to 4"); desc->level = 4; } else if( version<=5 && (desc->type == building_desc_t::factory || desc->type == building_desc_t::depot) ) { desc->level ++; PAKSET_INFO("building_reader_t::read_node()","old station building -> increment level by one to %i", desc->level ); } if( version<=6 ) { // only stops were allowed underground desc->allow_underground = 255; } if( version<=7 ) { // capacity, maintenance and price were set from level desc->capacity = desc->level * 32; desc->maintenance = PRICE_MAGIC; desc->price = PRICE_MAGIC; } if (desc->level == 65535) { desc->level = 0; // apparently wrong level dbg->warning("building_reader_t::read_node()","level was 65535, intended was probably 0 => changed." ); } if ( version < 9 ) { switch(btyp) { case old_btyp::wohnung: desc->type = building_desc_t::city_res; break; case old_btyp::gewerbe: desc->type = building_desc_t::city_com; break; case old_btyp::industrie: desc->type = building_desc_t::city_ind; break; default: ; } } if( version < 10 ) { // can always replace desc->preservation_year_month = DEFAULT_RETIRE_YEAR*12; } PAKSET_INFO("building_reader_t::read_node()", "version=%d," " btyp=%d," " type=%d," " price=%d," " maintenance=%d," " capacity=%d," " level=%d," " extra_data=%d," " size.x=%d," " size.y=%d," " layouts=%d," " enables=%x," " flags=%d," " chance=%d," " climates=%X," " metro=%d," " anim=%d," " intro=%d/%d," " retire=%d/%d,", version, btyp, desc->type, desc->price, desc->maintenance, desc->capacity, desc->level, desc->extra_data, desc->size.x, desc->size.y, desc->layouts, desc->enables, desc->flags, desc->distribution_weight, desc->allowed_climates, desc->allow_underground, desc->animation_time, (desc->intro_date%12)+1, desc->intro_date/12, (desc->retire_date%12)+1, desc->retire_date/12 ); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/building_reader.h000066400000000000000000000015341520675460500253130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_BUILDING_READER_H #define DESCRIPTOR_READER_BUILDING_READER_H #include "obj_reader.h" class tile_reader_t : public obj_reader_t { OBJ_READER_DEF(tile_reader_t, obj_tile, "tile"); public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; class building_reader_t : public obj_reader_t { OBJ_READER_DEF(building_reader_t, obj_building, "building"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/citycar_reader.cc000066400000000000000000000051161520675460500253120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simunits.h" #include "../../vehicle/vehicle.h" #include "../../vehicle/simroadtraffic.h" #include "../citycar_desc.h" #include "../intro_dates.h" #include "citycar_reader.h" #include "../obj_node_info.h" #include "../../simdebug.h" #include "../../network/pakset_info.h" void citycar_reader_t::register_obj(obj_desc_t *&data) { citycar_desc_t *desc = static_cast(data); private_car_t::register_desc(desc); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool citycar_reader_t::successfully_loaded() const { return private_car_t::successfully_loaded(); } obj_desc_t * citycar_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; citycar_desc_t *desc = new citycar_desc_t(); if(version == 2) { // Versioned node, version 1 desc->distribution_weight = decode_uint16(p); desc->topspeed = kmh_to_speed(decode_uint16(p)/16); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); } else if(version == 1) { // Versioned node, version 1 desc->distribution_weight = decode_uint16(p); desc->topspeed = kmh_to_speed(decode_uint16(p)/16); uint16 intro_date = decode_uint16(p); desc->intro_date = (intro_date/16)*12 + (intro_date%12); uint16 retire_date = decode_uint16(p); desc->retire_date= (retire_date/16)*12 + (retire_date%12); } else { if( version ) { dbg->fatal( "citycar_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // old version 0 ... desc->distribution_weight = v; desc->topspeed = kmh_to_speed(80); desc->intro_date = DEFAULT_INTRO_YEAR*12; desc->retire_date = DEFAULT_RETIRE_YEAR*12; } // zero speed not allowed, we want something that moves! if( desc->topspeed<=16 ) { dbg->warning( "citycar_reader_t::read_node()", "citycar must have minimum speed => changed to 1.25 km/h!" ); desc->topspeed = 16; } PAKSET_INFO("citycar_reader_t::read_node()","version=%i, speed=%i, chance=%i, intro=%i/%i, retire=%i/%i", version, speed_to_kmh(desc->topspeed), desc->distribution_weight, (desc->intro_date%12)+1, desc->intro_date/12, (desc->retire_date%12)+1, desc->retire_date/12); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/citycar_reader.h000066400000000000000000000012021520675460500251440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_CITYCAR_READER_H #define DESCRIPTOR_READER_CITYCAR_READER_H #include "obj_reader.h" class citycar_reader_t : public obj_reader_t { OBJ_READER_DEF(citycar_reader_t, obj_citycar, "citycar"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/crossing_reader.cc000066400000000000000000000051751520675460500255100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/crossing_logic.h" #include "../sound_desc.h" #include "../crossing_desc.h" #include "crossing_reader.h" #include "../obj_node_info.h" #include "../../simdebug.h" #include "../../network/pakset_info.h" void crossing_reader_t::register_obj(obj_desc_t *&data) { crossing_desc_t *desc = static_cast(data); if(desc->topspeed1!=0) { crossing_logic_t::register_desc(desc); } checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } obj_desc_t * crossing_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; crossing_desc_t *desc = new crossing_desc_t(); if(version == 0) { dbg->error("crossing_reader_t::read_node()","Old version of crossings cannot be used!"); desc->waytype1 = (waytype_t)v; desc->waytype2 = (waytype_t)decode_uint16(p); desc->topspeed1 = 0; desc->topspeed2 = 0; } else if( version==1 || version==2 ) { desc->waytype1 = (waytype_t)decode_uint8(p); desc->waytype2 = (waytype_t)decode_uint8(p); desc->topspeed1 = decode_uint16(p); desc->topspeed2 = decode_uint16(p); desc->open_animation_time = decode_uint32(p); desc->closed_animation_time = decode_uint32(p); desc->sound = decode_sint8(p); if(desc->sound==LOAD_SOUND) { uint8 len=decode_sint8(p); char wavname[256]; wavname[len] = 0; for(uint8 i=0; isound = (sint8)sound_desc_t::get_sound_id(wavname); } else if(desc->sound>=0 && desc->sound<=MAX_OLD_SOUNDS) { sint16 old_id = desc->sound; desc->sound = (sint8)sound_desc_t::get_compatible_sound_id((sint8)old_id); } desc->intro_date = 0; desc->retire_date = 65535; if (version >= 2 ) { desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); } } else { dbg->fatal( "crossing_reader_t::read_node()", "Cannot handle too new node version %i", version ); } PAKSET_INFO("crossing_reader_t::read_node()", "version=%i, waytype1=%d, waytype2=%d, topspeed1=%i, topspeed2=%i, open_time=%i, close_time=%i, sound=%i", version, desc->waytype1, desc->waytype2, desc->topspeed1, desc->topspeed2, desc->open_animation_time, desc->closed_animation_time, desc->sound); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/crossing_reader.h000066400000000000000000000010531520675460500253410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_CROSSING_READER_H #define DESCRIPTOR_READER_CROSSING_READER_H #include "obj_reader.h" class crossing_reader_t : public obj_reader_t { OBJ_READER_DEF(crossing_reader_t, obj_crossing, "crossing"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/factory_reader.cc000066400000000000000000000405171520675460500253270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simfab.h" #include "../../builder/fabrikbauer.h" #include "../../simdebug.h" #include "../obj_node_info.h" #include "../sound_desc.h" #include "../factory_desc.h" #include "../xref_desc.h" #include "../../network/pakset_info.h" #include "factory_reader.h" // determine the combined probability of 256 rounds of chances uint16 rescale_probability(const uint16 p) { if( p ) { // probability is p / 10000 if (p >= 10000) { // too large, will lead to overflow here return 10000; } sint64 pp = ( (sint64)p << 30 ) / 10000LL; sint64 qq = ( 1LL << 30 ) - pp; uint16 ss = 256u; while( (ss >>= 1) ) { pp += (pp * qq) >> 30; qq = (qq * qq) >> 30; } return (uint16)(((pp * 10000LL) + (1LL << 29)) >> 30); } return p; } obj_desc_t *factory_field_class_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; uint16 v = decode_uint16(p); field_class_desc_t *desc = new field_class_desc_t(); if( v==0x8001 ) { // field class specific data desc->snow_image = decode_uint8(p); desc->production_per_field = decode_uint16(p); desc->storage_capacity = decode_uint16(p); desc->spawn_weight = decode_uint16(p); PAKSET_INFO("factory_field_class_reader_t::read_node()", "version=%i, has_snow=%i, production=%i, capacity=%i, spawn_weight=%i", v, desc->snow_image, desc->production_per_field, desc->storage_capacity, desc->spawn_weight); } else { dbg->fatal("factory_field_class_reader_t::read_node()","Cannot handle too new node version %i", v&0x00ff ); } return desc; } obj_desc_t *factory_field_group_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; uint16 v = decode_uint16(p); field_group_desc_t *desc = new field_group_desc_t(); if( v==0x8003 ) { desc->probability = rescale_probability( decode_uint16(p) ); desc->max_fields = decode_uint16(p); desc->min_fields = decode_uint16(p); desc->start_fields = decode_uint16(p); desc->field_classes = decode_uint16(p); } else if( v==0x8002 ) { // this version only store shared, common data desc->probability = rescale_probability( decode_uint16(p) ); desc->max_fields = decode_uint16(p); desc->min_fields = decode_uint16(p); desc->field_classes = decode_uint16(p); } else if( v==0x8001 ) { /* * leave shared, common data in field desc * field class specific data goes to field class desc */ field_class_desc_t *const field_class_desc = new field_class_desc_t(); field_class_desc->snow_image = decode_uint8(p); desc->probability = rescale_probability( decode_uint16(p) ); field_class_desc->production_per_field = decode_uint16(p); desc->max_fields = decode_uint16(p); desc->min_fields = decode_uint16(p); desc->field_classes = 1; field_class_desc->storage_capacity = 0; field_class_desc->spawn_weight = 1000; /* * store it in a static variable for further processing * later in factory_field_reader_t::register_obj() */ incomplete_field_class_desc = field_class_desc; PAKSET_INFO("factory_field_group_reader_t::read_node()", "version=%i, probability=%i, fields: max=%i / min=%i / start=%i, field classes=%i, storage=%i, field_prod=%i, chance=%i, has_snow=%i", v, desc->probability, desc->max_fields, desc->min_fields, desc->start_fields, desc->field_classes, field_class_desc->storage_capacity, field_class_desc->production_per_field, field_class_desc->spawn_weight, field_class_desc->snow_image); } else { dbg->fatal("factory_field_group_reader_t::read_node()","Cannot handle too new node version %i", v ); } if ( v>0x8001 ) { PAKSET_INFO("factory_field_group_reader_t::read_node()", "version=%i, probability=%i, fields: max=%i / min=%i / start=%i, field classes=%i", v, desc->probability, desc->max_fields, desc->min_fields, desc->start_fields, desc->field_classes); } return desc; } void factory_field_group_reader_t::register_obj(obj_desc_t *&data) { field_group_desc_t *const desc = static_cast(data); // check if we need to continue with the construction of field class desc if (field_class_desc_t *const field_class_desc = incomplete_field_class_desc) { // we *must* transfer the obj_desc_t array and not just the desc object itself // as xref reader has already logged the address of the array element for xref resolution field_class_desc->children = desc->children; desc->children = new obj_desc_t*[1]; desc->children[0] = field_class_desc; incomplete_field_class_desc = NULL; } } obj_desc_t *factory_smoke_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; sint16 x = decode_sint16(p); sint16 y = decode_sint16(p); smoke_desc_t *desc = new smoke_desc_t(); desc->pos_off = koord( x, y ); x = decode_sint16(p); y = decode_sint16(p); desc->xy_off = koord( x, y ); /*smoke speed*/ decode_sint16(p); PAKSET_INFO("factory_smoke_reader_t::read_node()","(size %i)",node.size); return desc; } obj_desc_t *factory_supplier_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; factory_supplier_desc_t *desc = new factory_supplier_desc_t(); if(version == 1) { // Versioned node, version 1 // not there yet ... } else { // old node, version 0 desc->capacity = v; desc->supplier_count = decode_uint16(p); desc->consumption = decode_uint16(p); } PAKSET_INFO("factory_product_reader_t::read_node()", "version=%d, capacity=%d, count=%d, consumption=%d", 0, desc->capacity, desc->supplier_count, desc->consumption); return desc; } obj_desc_t *factory_product_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; factory_product_desc_t *desc = new factory_product_desc_t(); if(version == 1) { // Versioned node, version 1 desc->capacity = decode_uint16(p); desc->factor = decode_uint16(p); } else { if( version ) { dbg->fatal( "factory_product_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // old node, version 0 decode_uint16(p); desc->capacity = v; desc->factor = 256; } PAKSET_INFO("factory_product_reader_t::read_node()", "version=%d, capacity=%d, factor=%x", version, desc->capacity, desc->factor); return desc; } obj_desc_t *factory_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; factory_desc_t *desc = new factory_desc_t(); desc->sound_id = NO_SOUND; desc->sound_interval = 0xFFFFFFFFul; desc->smokerotations = 0; typedef factory_desc_t::site_t site_t; if(version == 6) { // Versioned node, version 6 with explicit electricity producer information desc->placement = (site_t)decode_uint16(p); desc->productivity = decode_uint16(p); desc->range = decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->color = decode_uint8(p); desc->fields = decode_uint8(p); desc->supplier_count = decode_uint16(p); desc->product_count = decode_uint16(p); desc->pax_level = decode_uint16(p); desc->expand_probability = rescale_probability( decode_uint16(p) ); desc->expand_minimum = decode_uint16(p); desc->expand_range = decode_uint16(p); desc->expand_times = decode_uint16(p); desc->electric_boost = decode_uint16(p); desc->pax_boost = decode_uint16(p); desc->mail_boost = decode_uint16(p); desc->electric_demand = decode_uint16(p); desc->pax_demand = decode_uint16(p); desc->mail_demand = decode_uint16(p); desc->electricity_producer = decode_uint8(p); desc->sound_interval = decode_uint32(p); desc->sound_id = decode_sint8(p); desc->smokerotations = decode_sint8(p); for( int i = 0; i < 4; i++ ) { desc->smoketile [i].x = decode_sint16(p); desc->smoketile [i].y = decode_sint16(p); desc->smokeoffset[i].x = decode_sint16(p); desc->smokeoffset[i].y = decode_sint16(p); } desc->smokeuplift = decode_uint16(p); desc->smokelifetime = decode_uint16(p); } else if(version == 5) { // Versioned node, version 5 with smoke offsets desc->placement = (site_t)decode_uint16(p); desc->productivity = decode_uint16(p); desc->range = decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->color = decode_uint8(p); desc->fields = decode_uint8(p); desc->supplier_count = decode_uint16(p); desc->product_count = decode_uint16(p); desc->pax_level = decode_uint16(p); desc->expand_probability = rescale_probability( decode_uint16(p) ); desc->expand_minimum = decode_uint16(p); desc->expand_range = decode_uint16(p); desc->expand_times = decode_uint16(p); desc->electric_boost = decode_uint16(p); desc->pax_boost = decode_uint16(p); desc->mail_boost = decode_uint16(p); desc->electric_demand = decode_uint16(p); desc->pax_demand = decode_uint16(p); desc->mail_demand = decode_uint16(p); desc->sound_interval = decode_uint32(p); desc->sound_id = decode_sint8(p); desc->smokerotations = decode_sint8(p); for( int i = 0; i < 4; i++ ) { desc->smoketile [i].x = decode_sint16(p); desc->smoketile [i].y = decode_sint16(p); desc->smokeoffset[i].x = decode_sint16(p); desc->smokeoffset[i].y = decode_sint16(p); } desc->smokeuplift = decode_uint16(p); desc->smokelifetime = decode_uint16(p); } else if(version == 4) { // Versioned node, version 4 with sound and animation desc->placement = (site_t)decode_uint16(p); desc->productivity = decode_uint16(p); desc->range = decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->color = decode_uint8(p); desc->fields = decode_uint8(p); desc->supplier_count = decode_uint16(p); desc->product_count = decode_uint16(p); desc->pax_level = decode_uint16(p); desc->expand_probability = rescale_probability( decode_uint16(p) ); desc->expand_minimum = decode_uint16(p); desc->expand_range = decode_uint16(p); desc->expand_times = decode_uint16(p); desc->electric_boost = decode_uint16(p); desc->pax_boost = decode_uint16(p); desc->mail_boost = decode_uint16(p); desc->electric_demand = decode_uint16(p); desc->pax_demand = decode_uint16(p); desc->mail_demand = decode_uint16(p); desc->sound_interval = decode_uint32(p); desc->sound_id = decode_sint8(p); } else if(version == 3) { // Versioned node, version 3 desc->placement = (site_t)decode_uint16(p); desc->productivity = decode_uint16(p); desc->range = decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->color = decode_uint8(p); desc->fields = decode_uint8(p); desc->supplier_count = decode_uint16(p); desc->product_count = decode_uint16(p); desc->pax_level = decode_uint16(p); desc->expand_probability = rescale_probability( decode_uint16(p) ); desc->expand_minimum = decode_uint16(p); desc->expand_range = decode_uint16(p); desc->expand_times = decode_uint16(p); desc->electric_boost = decode_uint16(p); desc->pax_boost = decode_uint16(p); desc->mail_boost = decode_uint16(p); desc->electric_demand = decode_uint16(p); desc->pax_demand = decode_uint16(p); desc->mail_demand = decode_uint16(p); } else if(version == 2) { // Versioned node, version 2 desc->placement = (site_t)decode_uint16(p); desc->productivity = decode_uint16(p); desc->range = decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->color = decode_uint8(p); desc->fields = decode_uint8(p); desc->supplier_count = decode_uint16(p); desc->product_count = decode_uint16(p); desc->pax_level = decode_uint16(p); desc->expand_probability = 0; desc->expand_minimum = 0; desc->expand_range = 0; desc->expand_times = 0; desc->electric_boost = 256; desc->pax_boost = 0; desc->mail_boost = 0; desc->electric_demand = 65535; desc->pax_demand = 65535; desc->mail_demand = 65535; } else if(version == 1) { // Versioned node, version 1 desc->placement = (site_t)decode_uint16(p); desc->productivity = decode_uint16(p); desc->range = decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->color = (uint8)decode_uint16(p); desc->supplier_count = decode_uint16(p); desc->product_count = decode_uint16(p); desc->pax_level = decode_uint16(p); desc->fields = 0; desc->expand_probability = 0; desc->expand_minimum = 0; desc->expand_range = 0; desc->expand_times = 0; desc->electric_boost = 256; desc->pax_boost = 0; desc->mail_boost = 0; desc->electric_demand = 65535; desc->pax_demand = 65535; desc->mail_demand = 65535; } else { if( version ) { dbg->fatal( "factory_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // old node, version 0, without pax_level desc->placement = (site_t)v; decode_uint16(p); // alsways zero desc->productivity = decode_uint16(p)|0x8000; desc->range = decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->color = (uint8)decode_uint16(p); desc->supplier_count = decode_uint16(p); desc->product_count = decode_uint16(p); desc->pax_level = 12; desc->fields = 0; desc->expand_probability = 0; desc->expand_minimum = 0; desc->expand_range = 0; desc->expand_times = 0; desc->electric_boost = 256; desc->pax_boost = 0; desc->mail_boost = 0; desc->electric_demand = 65535; desc->pax_demand = 65535; desc->mail_demand = 65535; } if(desc->sound_id==LOAD_SOUND) { uint8 len=decode_sint8(p); char wavname[256]; wavname[len] = 0; for(uint8 i=0; isound_id = (sint8)sound_desc_t::get_sound_id(wavname); PAKSET_INFO("factory_reader_t::register_obj()","sound %s to %i",wavname,desc->sound_id); } else if(desc->sound_id>=0 && desc->sound_id<=MAX_OLD_SOUNDS) { sint16 old_id = desc->sound_id; desc->sound_id = (sint8)sound_desc_t::get_compatible_sound_id((sint8)old_id); PAKSET_INFO("factory_reader_t::register_obj()","old sound %i to %i",old_id,desc->sound_id); } return desc; } void factory_reader_t::register_obj(obj_desc_t *&data) { factory_desc_t *desc = static_cast(data); if (desc->electricity_producer == 0xFF) { // Not explicitly specified whether this is an electricity producer - // fall back to old behaviour of inferring this from the object name const size_t fab_name_len = strlen( desc->get_name() ); desc->electricity_producer = (fab_name_len>=10 && strcmp(desc->get_name()+fab_name_len- 9, "kraftwerk" )==0) || (fab_name_len>=12 && strcmp(desc->get_name()+fab_name_len-11, "Power Plant")==0); } desc->correct_smoke(); factory_builder_t::register_desc(desc); } bool factory_reader_t::successfully_loaded() const { return factory_builder_t::successfully_loaded(); } simutrans-124.5/src/simutrans/descriptor/reader/factory_reader.h000066400000000000000000000043321520675460500251640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_FACTORY_READER_H #define DESCRIPTOR_READER_FACTORY_READER_H #include "obj_reader.h" class field_class_desc_t; // new reader for field class desc class factory_field_class_reader_t : public obj_reader_t { friend class factory_field_group_reader_t; // this is a special case due to desc restructuring OBJ_READER_DEF(factory_field_class_reader_t, obj_ffldclass, "factory field class"); public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; class factory_field_group_reader_t : public obj_reader_t { OBJ_READER_DEF(factory_field_group_reader_t, obj_ffield, "factory field"); // hold a field class desc under construction static field_class_desc_t* incomplete_field_class_desc; protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; class factory_smoke_reader_t : public obj_reader_t { OBJ_READER_DEF(factory_smoke_reader_t, obj_fsmoke, "factory smoke"); public: /// @copydoc obj_reader_t::read_node obj_desc_t* read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; class factory_supplier_reader_t : public obj_reader_t { OBJ_READER_DEF(factory_supplier_reader_t, obj_fsupplier, "factory supplier"); public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; class factory_product_reader_t : public obj_reader_t { OBJ_READER_DEF(factory_product_reader_t, obj_fproduct, "factory product"); public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; class factory_reader_t : public obj_reader_t { OBJ_READER_DEF(factory_reader_t, obj_factory, "factory"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/good_reader.cc000066400000000000000000000051171520675460500246050ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../../builder/goods_manager.h" #include "good_reader.h" #include "../obj_node_info.h" #include "../goods_desc.h" #include "../../network/pakset_info.h" void goods_reader_t::register_obj(obj_desc_t *&data) { goods_desc_t *desc = static_cast(data); goods_manager_t::register_desc(desc); PAKSET_INFO("goods_reader_t::register_obj()","loaded good '%s'", desc->get_name()); pakset_manager_t::obj_for_xref(get_type(), desc->get_name(), data); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool goods_reader_t::successfully_loaded() const { return goods_manager_t::successfully_loaded(); } obj_desc_t * goods_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const uint16 version = v & 0x8000 ? v & 0x7FFF : 0; // some defaults goods_desc_t *desc = new goods_desc_t; desc->speed_bonus = 0; desc->weight_per_unit = 100; desc->color = 255; if (version == 0) { desc->base_value = v; desc->catg = (uint8)decode_uint16(p); } else if (version == 1) { desc->base_value = decode_uint16(p); desc->catg = (uint8)decode_uint16(p); desc->speed_bonus = decode_uint16(p); } else if (version == 2) { desc->base_value = decode_uint16(p); desc->catg = (uint8)decode_uint16(p); desc->speed_bonus = decode_uint16(p); desc->weight_per_unit = decode_uint16(p); } else if (version == 3) { desc->base_value = decode_uint16(p); desc->catg = decode_uint8(p); desc->speed_bonus = decode_uint16(p); desc->weight_per_unit = decode_uint16(p); desc->color = decode_uint8(p); } else if (version == 4) { desc->base_value = decode_sint64(p); desc->catg = decode_uint8(p); desc->speed_bonus = decode_uint16(p); desc->weight_per_unit = decode_uint16(p); desc->color = decode_uint8(p); } else { dbg->fatal( "goods_reader_t::read_node()", "Cannot handle too new node version %i", version ); } PAKSET_INFO("goods_reader_t::read_node()","version=%d, value=%d, catg=%d, bonus=%d, weight=%i, color=%i", version, desc->base_value, desc->catg, desc->speed_bonus, desc->weight_per_unit, desc->color); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/good_reader.h000066400000000000000000000011621520675460500244430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_GOOD_READER_H #define DESCRIPTOR_READER_GOOD_READER_H #include "obj_reader.h" class goods_reader_t : public obj_reader_t { OBJ_READER_DEF(goods_reader_t, obj_good, "good"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/ground_reader.cc000066400000000000000000000010661520675460500251520ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../ground_desc.h" #include "ground_reader.h" void ground_reader_t::register_obj(obj_desc_t *&data) { ground_desc_t *desc = static_cast(data); ground_desc_t::register_desc(desc); } bool ground_reader_t::successfully_loaded() const { return ground_desc_t::successfully_loaded(); } obj_desc_t* ground_reader_t::read_node(FILE*, obj_node_info_t& info) { return obj_reader_t::read_node(info); } simutrans-124.5/src/simutrans/descriptor/reader/ground_reader.h000066400000000000000000000011741520675460500250140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_GROUND_READER_H #define DESCRIPTOR_READER_GROUND_READER_H #include "obj_reader.h" class ground_reader_t : public obj_reader_t { OBJ_READER_DEF(ground_reader_t, obj_ground, "ground"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/groundobj_reader.cc000066400000000000000000000051631520675460500256470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simunits.h" #include "../../obj/simobj.h" #include "../../simdebug.h" #include "../../obj/groundobj.h" #include "../../vehicle/movingobj.h" #include "../groundobj_desc.h" #include "../obj_node_info.h" #include "groundobj_reader.h" #include "../../network/pakset_info.h" #include void groundobj_reader_t::register_obj(obj_desc_t *&data) { groundobj_desc_t *desc = static_cast(data); if(desc->speed==0) { groundobj_t::register_desc(desc); } else { movingobj_t::register_desc(desc); } checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool groundobj_reader_t::successfully_loaded() const { bool gok = groundobj_t::successfully_loaded(); return movingobj_t::successfully_loaded() && gok; } obj_desc_t *groundobj_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the highest bit was always cleared. const uint16 v = decode_uint16(p); const uint16 version = v & 0x8000 ? v & 0x7FFF : 0; groundobj_desc_t *desc = new groundobj_desc_t(); if (version == 2) { // cost as sint64 desc->allowed_climates = (climate_bits)decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); desc->trees_on_top = (bool)decode_uint8(p); desc->speed = kmh_to_speed( decode_uint16(p) ); desc->wtyp = (waytype_t)decode_uint16(p); desc->price = decode_sint64(p); } else if (version == 1) { desc->allowed_climates = (climate_bits)decode_uint16(p); desc->distribution_weight = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); desc->trees_on_top = (bool)decode_uint8(p); desc->speed = kmh_to_speed( decode_uint16(p) ); desc->wtyp = (waytype_t)decode_uint16(p); desc->price = decode_sint32(p); } else { // version 0, never existed dbg->fatal( "groundobj_reader_t::read_node()", "Cannot handle too new node version %i", version ); } PAKSET_INFO("groundobj_reader_t::read_node()", "version=%i, climates=$%X, seasons=%i, chance=%i, speed=%i, ways=%i, cost=%" PRId64 ", trees_on_top=%i", version, desc->allowed_climates, desc->number_of_seasons, desc->distribution_weight, speed_to_kmh(desc->speed), desc->wtyp, desc->price, desc->trees_on_top); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/groundobj_reader.h000066400000000000000000000012111520675460500254770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_GROUNDOBJ_READER_H #define DESCRIPTOR_READER_GROUNDOBJ_READER_H #include "obj_reader.h" class groundobj_reader_t : public obj_reader_t { OBJ_READER_DEF(groundobj_reader_t, obj_groundobj, "groundobj"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t*&) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/image_reader.cc000066400000000000000000000130601520675460500247330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../display/simgraph.h" #include "../../simdebug.h" #include "../../display/simimg.h" #include "../image.h" #include "image_reader.h" #include "../obj_node_info.h" #include #include "../../tpl/inthashtable_tpl.h" // if without graphics backend, do not copy any pixel #if COLOUR_DEPTH != 0 #define skip_reading_pixels_if_no_graphics #else #define skip_reading_pixels_if_no_graphics goto adjust_image #endif obj_desc_t *image_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; p.seek(6); // always zero in old version, since length was always less than 65535 // because a node could not hold more data uint8 version = decode_uint8(p); p.seek(0); #if COLOUR_DEPTH != 0 image_t *desc = new image_t(); #else // reserve space for one single pixel and initialize data image_t *desc = image_t::create_single_pixel(); #endif if(version==0) { desc->x = decode_uint8(p); desc->w = decode_uint8(p); desc->y = decode_uint8(p); desc->h = decode_uint8(p); desc->alloc(decode_uint32(p)); // len desc->imageid = IMG_EMPTY; p += 2; // dummys desc->zoomable = decode_uint8(p) != 0; skip_reading_pixels_if_no_graphics; //PAKSET_INFO("image_t::read_node()","x,y=%d,%d w,h=%d,%d, len=%i",desc->x,desc->y,desc->w,desc->h, desc->len); uint16* dest = desc->data; p.seek(12); if (desc->h > 0) { for (uint i = 0; i < desc->len; i++) { uint16 data = decode_uint16(p); if(data>=0x8000u && data<=0x800Fu) { // player color offset changed data ++; } *dest++ = data; } } } else if(version<=2) { desc->x = decode_sint16(p); desc->y = decode_sint16(p); desc->w = decode_uint8(p); desc->h = decode_uint8(p); p++; // skip version information desc->alloc(decode_uint16(p)); // len desc->zoomable = decode_uint8(p) != 0; desc->imageid = IMG_EMPTY; skip_reading_pixels_if_no_graphics; p.read_uint16_block(desc->data, desc->len); } else if(version==3) { desc->x = decode_sint16(p); desc->y = decode_sint16(p); desc->w = decode_sint16(p); p++; // skip version information desc->h = decode_sint16(p); desc->alloc((node.size-10)/2); // len desc->zoomable = decode_uint8(p) != 0; desc->imageid = IMG_EMPTY; skip_reading_pixels_if_no_graphics; p.read_uint16_block(desc->data, desc->len); } else { dbg->fatal( "image_reader_t::read_node()", "Cannot handle too new node version %i", version ); } #if COLOUR_DEPTH == 0 adjust_image: // reset image parameters, but only for non-empty images if( desc->h > 0 ) { desc->h = 1; } if( desc->w > 0 ) { desc->w = 1; } if( desc->len > 0 ) { desc->len = 4; memset(desc->data, 0, desc->len*sizeof(PIXVAL)); } desc->x = 0; desc->y = 0; #else if (!image_has_valid_data(desc)) { delete desc; return NULL; } #endif // check for left corner if(version<2 && desc->h>0) { // find left border uint16 left = 255; uint16 *dest = desc->data; uint16 *end = desc->data + desc->len; for( uint8 y=0; yh; y++ ) { if (dest >= end) { delete desc; return NULL; } left = min(left, *dest); // skip rest of the line do { dest++; if (dest >= end) { delete desc; return NULL; } dest += *dest + 1; if (dest >= end) { delete desc; return NULL; } } while (*dest != 0); dest++; // skip trailing zero } if(leftx) { dbg->warning( "image_reader_t::read_node()","left(%i)x ); } /// No need to check for valid dest pointer here, the code has the same structure as above dest = desc->data; for( uint8 y=0; yh; y++ ) { *dest -= left; // skip rest of the line do { dest++; dest += *dest + 1; } while (*dest != 0); dest++; // skip trailing zero } } if (desc->len != 0) { // get the adler hash (since we have zlib on board anyway ... ) bool do_register_image = true; uint32 adler = adler32(0L, NULL, 0 ); // remember len is sizeof(uint16)! adler = adler32(adler, (const Bytef *)(desc->data), desc->len*2 ); static inthashtable_tpl images_adlers; image_t *same = images_adlers.get(adler); if (same) { // same checksum => if same then skip! image_t const& a = *desc; image_t const& b = *same; do_register_image = a.x != b.x || a.y != b.y || a.w != b.w || a.h != b.h || a.zoomable != b.zoomable || a.len != b.len || memcmp(a.data, b.data, sizeof(*a.data) * a.len) != 0; } // unique image here if( do_register_image ) { if(!same) { images_adlers.put(adler,desc); // still with imageid == IMG_EMPTY! } // register image adds this image to the internal array maintained by simgraph??.cc desc->imageid = gfx->register_image(desc); } else { // no need to load doubles ... delete desc; desc = same; } } return desc; } #define TRANSPARENT_RUN (0x8000u) bool image_reader_t::image_has_valid_data(image_t *image_in) const { PIXVAL *src = image_in->data; PIXVAL *end = image_in->data + image_in->len; for( int y = 0; y < image_in->h; ++y ) { // decode line uint16 runlen = *src++; do { if (src >= end) { return false; } runlen = *src++ & ~TRANSPARENT_RUN; src += runlen; if (src >= end) { return false; } runlen = *src++; } while( runlen!=0 ); // end of row: runlen == 0 } return src == end; } simutrans-124.5/src/simutrans/descriptor/reader/image_reader.h000066400000000000000000000007771520675460500246100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_IMAGE_READER_H #define DESCRIPTOR_READER_IMAGE_READER_H #include "obj_reader.h" class image_t; class image_reader_t : public obj_reader_t { OBJ_READER_DEF(image_reader_t, obj_image, "image"); public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; private: bool image_has_valid_data(image_t *img) const; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/imagelist2d_reader.cc000066400000000000000000000011331520675460500260530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../image_array.h" #include "imagelist2d_reader.h" #include "../obj_node_info.h" obj_desc_t * imagelist2d_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; image_array_t *desc = new image_array_t(); desc->count = decode_uint16(p); // PAKSET_INFO("imagelist2d_reader_t::read_node()", "count=%d data read (node.size=%i)",desc->count, node.size); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/imagelist2d_reader.h000066400000000000000000000007261520675460500257240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_IMAGELIST2D_READER_H #define DESCRIPTOR_READER_IMAGELIST2D_READER_H #include "obj_reader.h" class imagelist2d_reader_t : public obj_reader_t { OBJ_READER_DEF(imagelist2d_reader_t, obj_imagelist2d, "imagelist2d"); public: /// @copydoc obj_reader::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/imagelist_reader.cc000066400000000000000000000007421520675460500256320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../image_list.h" #include "imagelist_reader.h" #include "../obj_node_info.h" obj_desc_t * imagelist_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; image_list_t *desc = new image_list_t(); desc->count = decode_uint16(p); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/imagelist_reader.h000066400000000000000000000007141520675460500254730ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_IMAGELIST_READER_H #define DESCRIPTOR_READER_IMAGELIST_READER_H #include "obj_reader.h" class imagelist_reader_t : public obj_reader_t { OBJ_READER_DEF(imagelist_reader_t, obj_imagelist, "imagelist"); public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/obj_reader.cc000066400000000000000000000005231520675460500244230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "obj_reader.h" size_t node_body_t::buf_size = 0; uint8* node_body_t::buf = 0; uint8* node_body_t::ptr = 0; uint8* node_body_t::end = 0; const char* node_body_t::type_name_ = 0; #if DEBUG uint8 node_body_t::usage = 0; #endif simutrans-124.5/src/simutrans/descriptor/reader/obj_reader.h000066400000000000000000000152061520675460500242710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_OBJ_READER_H #define DESCRIPTOR_READER_OBJ_READER_H #include #include "../obj_node_info.h" #include "../objversion.h" #include "../../simdebug.h" #include "../../simtypes.h" #include "../../tpl/array_tpl.h" #include "../../dataobj/pakset_manager.h" class obj_desc_t; template class inthashtable_tpl; template class stringhashtable_tpl; template class ptrhashtable_tpl; template class slist_tpl; /** * Reads uint8 from memory area. Advances pointer by 1 byte. */ inline uint8 decode_uint8(char * &data) { const sint8 v = *((sint8 *)data); data ++; return v; } #define decode_sint8(data) (sint8)decode_uint8(data) /** * Reads uint16 from memory area. Advances pointer by 2 bytes. */ inline uint16 decode_uint16(char * &data) { uint16 const v = (uint16)(uint8)data[0] | (uint16)(uint8)data[1] << 8; data += sizeof(v); return v; } #define decode_sint16(data) (sint16)decode_uint16(data) /** * Reads uint32 from memory area. Advances pointer by 4 bytes. */ inline uint32 decode_uint32(char * &data) { uint32 const v = (uint32)(uint8)data[0] << 0 | (uint32)(uint8)data[1] << 8 | (uint32)(uint8)data[2] << 16 | (uint32)(uint8)data[3] << 24; data += sizeof(v); return v; } #define decode_sint32(data) (sint32)decode_uint32(data) inline uint64 decode_uint64(char *&data) { const uint64 v = (uint64)(uint8)data[0] << 0 | (uint64)(uint8)data[1] << 8 | (uint64)(uint8)data[2] << 16 | (uint64)(uint8)data[3] << 24 | (uint64)(uint8)data[4] << 32 | (uint64)(uint8)data[5] << 40 | (uint64)(uint8)data[6] << 48 | (uint64)(uint8)data[7] << 56; data += sizeof(v); return v; } #define decode_sint64(data) (sint64)decode_uint64(data) #define OBJ_READER_DEF(classname, ty, ty_name) \ public: \ classname() { pakset_manager_t::register_reader(this); } \ obj_type get_type() const OVERRIDE { return ty; } \ const char *get_type_name() const OVERRIDE { return ty_name; } \ static classname *instance() { return &the_instance; } \ private: \ static classname the_instance /// Owns one pak node's bytes and a bounds-checked cursor over them. /// decode_*(node_body_t&) overloads read through it. class node_body_t { public: /// fread @p size bytes from @p fp; short read leaves a bool-false cursor. node_body_t(FILE* fp, size_t size, const char* type_name) { type_name_ = type_name; if (size > 0) { if (size > buf_size) { buf_size = size | 0x0FFF; buf = (uint8*)realloc(buf,buf_size); } ptr = buf; size_t n = fread(ptr, 1, size, fp); if (n != size) { dbg->fatal("node_body_t()", "Cannot read %lu (only %lu) for %s", size, n, type_name); } end = ptr + size; #if DEBUG usage++; #endif } else { dbg->fatal("node_body_t()", "Node of size 0 requested for %s", type_name); } } #if DEBUG ~node_body_t() { usage--; if (usage) { dbg->fatal("node_body_t()", "is not reentrant for %s", type_name_); } } #endif /// false on short fread. explicit operator bool() const { return ptr != NULL; } /// Bounds-checked `p += n`. inline node_body_t& operator+=(size_t n) { ptr += n; return *this; } /// Bounds-checked single-byte skip (`p++`); return value unused. inline node_body_t& operator++(int) { ++ptr; return *this; } void seek(size_t off) { if (buf + off > end) { dbg->fatal("node_body_t()", "seek to offset %zu past buffer end %zu for %s", off, (size_t)(ptr-end), type_name_); } ptr = buf + off; } /// Bounds-checked: returns the cursor, then advances @p n bytes (tail reads). char* read_bytes(size_t n) { if (ptr + n <= end) { char* p = (char *)ptr; ptr += n; return p; } complain(n); return 0; } void read_uint16_block(uint16 *dest, size_t n) { uint8* cpy_end = ptr + 2 * n; if (cpy_end <= end) { while (ptr < cpy_end) { uint16 v = *ptr++; v |= (uint16)*ptr++ << 8; *dest++ = v; } return; } complain(2*n); } inline uint8 read_uint8() { if (ptr < end) { return *ptr++; } return complain(1); } inline uint16 read_uint16() { if (ptr+1 < end) { uint16 v = (uint16)ptr[0] | (uint16)ptr[1] << 8; ptr += 2; return v; } return complain(2); } inline uint32 read_uint32() { if (ptr + 3 < end) { const uint32 v = (uint32)ptr[0] | (uint32)ptr[1] << 8 | (uint32)ptr[2] << 16 | (uint32)ptr[3] << 24; ptr += 4; return v; } return complain(4); } inline uint64 read_uint64() { if (ptr + 7 < end) { const uint64 v = (uint64)(uint8)ptr[0] << 0 | (uint64)(uint8)ptr[1] << 8 | (uint64)(uint8)ptr[2] << 16 | (uint64)(uint8)ptr[3] << 24 | (uint64)(uint8)ptr[4] << 32 | (uint64)(uint8)ptr[5] << 40 | (uint64)(uint8)ptr[6] << 48 | (uint64)(uint8)ptr[7] << 56; ptr += 8; return v; } return complain(8); } private: uint8 complain(size_t n) const { dbg->fatal("node_body_t()", "Requested read beyond buffer (%lu was %lu too much) for %s", n, (size_t)(end - ptr), type_name_); return 0; } #if DEBUG static uint8 usage; #endif static size_t buf_size; static uint8* buf; static uint8* ptr; static uint8* end; static const char* type_name_; }; /// decode_*(node_body_t&) overloads, chosen over the char*& ones by /// argument type so reader call sites stay `decode_uint16(p)`. inline uint8 decode_uint8(node_body_t& b) { return b.read_uint8(); } inline uint16 decode_uint16(node_body_t& b) { return b.read_uint16(); } inline uint32 decode_uint32(node_body_t& b) { return b.read_uint32(); } inline uint64 decode_uint64(node_body_t& b) { return b.read_uint64(); } class obj_reader_t { public: obj_reader_t() { /* Beware: Cannot register_reader() here! */ } virtual ~obj_reader_t() {} public: /// Read a descriptor from @p fp. Does version check and compatibility transformations. /// @returns The descriptor on success, or NULL on failure virtual obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) = 0; /// Register descriptor so the object described by the descriptor can be built in-game. virtual void register_obj(obj_desc_t *&/*desc*/) {} /// Does post-loading checks. /// @returns true if everything ok virtual bool successfully_loaded() const { return true; } template static T* read_node(obj_node_info_t const& node) { if (node.size != 0) { dbg->fatal("obj_reader_t::read_node()", "node of type %.4s must have size 0, but has size %u", reinterpret_cast(&node.type), node.size); } return new T(); } public: virtual obj_type get_type() const = 0; virtual const char *get_type_name() const = 0; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/pedestrian_reader.cc000066400000000000000000000041201520675460500260040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../vehicle/pedestrian.h" #include "../pedestrian_desc.h" #include "../obj_node_info.h" #include "../intro_dates.h" #include "pedestrian_reader.h" #include "../../network/pakset_info.h" void pedestrian_reader_t::register_obj(obj_desc_t *&data) { pedestrian_desc_t *desc = static_cast(data); pedestrian_t::register_desc(desc); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool pedestrian_reader_t::successfully_loaded() const { return pedestrian_t::successfully_loaded(); } /** * Read a pedestrian info node. Does version check and * compatibility transformations. */ obj_desc_t * pedestrian_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; pedestrian_desc_t *desc = new pedestrian_desc_t(); desc->steps_per_frame = 0; desc->offset = 20; // always there and never retire desc->intro_date = 1; desc->retire_date = 0xFFFEu; if (version == 1) { desc->distribution_weight = decode_uint16(p); desc->steps_per_frame = decode_uint16(p); desc->offset = decode_uint16(p); } else if(version == 2) { desc->distribution_weight = decode_uint16(p); desc->steps_per_frame = decode_uint16(p); desc->offset = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); } else { if( version ) { dbg->fatal( "pedestrian_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // old, nonversion node desc->distribution_weight = v; } PAKSET_INFO("pedestrian_reader_t::read_node()", "version=%i, chance=%i", version, desc->distribution_weight); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/pedestrian_reader.h000066400000000000000000000012241520675460500256500ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_PEDESTRIAN_READER_H #define DESCRIPTOR_READER_PEDESTRIAN_READER_H #include "obj_reader.h" class pedestrian_reader_t : public obj_reader_t { OBJ_READER_DEF(pedestrian_reader_t, obj_pedestrian, "pedestrian"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/roadsign_reader.cc000066400000000000000000000073521520675460500254660ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../obj/roadsign.h" #include "../../simunits.h" // for kmh to speed conversion #include "../roadsign_desc.h" #include "../intro_dates.h" #include "roadsign_reader.h" #include "../obj_node_info.h" #include "../../simdebug.h" #include "../../network/pakset_info.h" #include void roadsign_reader_t::register_obj(obj_desc_t *&data) { roadsign_desc_t *desc = static_cast(data); roadsign_t::register_desc(desc); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool roadsign_reader_t::successfully_loaded() const { return roadsign_t::successfully_loaded(); } obj_desc_t *roadsign_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; roadsign_desc_t *desc = new roadsign_desc_t(); if (version == 6) { // cost as sint64, maintenance added desc->min_speed = kmh_to_speed(decode_uint16(p)); desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->flags = decode_uint16(p); desc->offset_left = decode_sint8(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); } else if (version == 5) { desc->min_speed = kmh_to_speed(decode_uint16(p)); desc->price = decode_uint32(p); desc->flags = decode_uint16(p); desc->offset_left = decode_sint8(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); } else if (version == 4) { desc->min_speed = kmh_to_speed(decode_uint16(p)); desc->price = decode_uint32(p); desc->flags = decode_uint8(p); desc->offset_left = decode_sint8(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); } else if (version == 3) { desc->min_speed = kmh_to_speed(decode_uint16(p)); desc->price = decode_uint32(p); desc->flags = decode_uint8(p); desc->offset_left = 14; desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); } else if (version == 2) { desc->min_speed = kmh_to_speed(decode_uint16(p)); desc->price = decode_uint32(p); desc->flags = decode_uint8(p); desc->offset_left = 14; desc->intro_date = DEFAULT_INTRO_YEAR*12; desc->retire_date = DEFAULT_RETIRE_YEAR*12; desc->wtyp = road_wt; } else if (version == 1) { // Versioned node, version 1 desc->min_speed = kmh_to_speed(decode_uint16(p)); desc->price = 50000; desc->flags = decode_uint8(p); desc->offset_left = 14; desc->intro_date = DEFAULT_INTRO_YEAR*12; desc->retire_date = DEFAULT_RETIRE_YEAR*12; desc->wtyp = road_wt; } else { dbg->fatal( "roadsign_reader_t::read_node()", "Cannot handle too new node version %i", version ); } if( version<=3 && ( desc->is_choose_sign() || desc->is_private_way() ) && desc->get_waytype() == road_wt ) { // do not shift these signs to the left for compatibility desc->offset_left = 0; } PAKSET_INFO("roadsign_reader_t::read_node()", "version=%i, min_speed=%i, price=%" PRId64 ", maintenance=%" PRId64 ", flags=%x, wtyp=%i, offset_left=%i, intro=%i/%i, retire=%i/%i", version, desc->min_speed, desc->price/100, desc->maintenance, desc->flags, desc->wtyp, desc->offset_left, desc->intro_date%12+1, desc->intro_date/12, desc->retire_date%12+1, desc->retire_date/12); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/roadsign_reader.h000066400000000000000000000012031520675460500253150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_ROADSIGN_READER_H #define DESCRIPTOR_READER_ROADSIGN_READER_H #include "obj_reader.h" class roadsign_reader_t : public obj_reader_t { OBJ_READER_DEF(roadsign_reader_t, obj_roadsign, "roadsign"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t*&) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/root_reader.cc000066400000000000000000000005531520675460500246370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../obj_desc.h" #include "root_reader.h" void root_reader_t::register_obj(obj_desc_t *&data) { delete data; data = NULL; } obj_desc_t* root_reader_t::read_node(FILE*, obj_node_info_t& info) { return obj_reader_t::read_node(info); } simutrans-124.5/src/simutrans/descriptor/reader/root_reader.h000066400000000000000000000010231520675460500244720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_ROOT_READER_H #define DESCRIPTOR_READER_ROOT_READER_H #include "obj_reader.h" class root_reader_t : public obj_reader_t { OBJ_READER_DEF(root_reader_t, obj_root, "root"); public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/sim_reader.cc000066400000000000000000000055241520675460500244470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simskin.h" #include "text_reader.h" #include "image_reader.h" #include "imagelist_reader.h" #include "imagelist2d_reader.h" #include "bridge_reader.h" #include "tunnel_reader.h" #include "building_reader.h" #include "roadsign_reader.h" #include "citycar_reader.h" #include "pedestrian_reader.h" #include "crossing_reader.h" #include "factory_reader.h" #include "good_reader.h" #include "ground_reader.h" #include "groundobj_reader.h" #include "way_reader.h" #include "way_obj_reader.h" #include "root_reader.h" #include "xref_reader.h" #include "sound_reader.h" #include "skin_reader.h" #include "tree_reader.h" #include "vehicle_reader.h" /** * static data * * These classes are self-registering. The linker will not notify if there is a missing class, since it * may be also instantiated in the class itself. Therefore, all classes MUST be declared here to force * the linking and raise an error if we lack any of them. */ text_reader_t text_reader_t::the_instance; image_reader_t image_reader_t::the_instance; imagelist_reader_t imagelist_reader_t::the_instance; imagelist2d_reader_t imagelist2d_reader_t::the_instance; root_reader_t root_reader_t::the_instance; xref_reader_t xref_reader_t::the_instance; sound_reader_t sound_reader_t::the_instance; menuskin_reader_t menuskin_reader_t::the_instance; cursorskin_reader_t cursorskin_reader_t::the_instance; symbolskin_reader_t symbolskin_reader_t::the_instance; miscimages_reader_t miscimages_reader_t::the_instance; goods_reader_t goods_reader_t::the_instance; ground_reader_t ground_reader_t::the_instance; groundobj_reader_t groundobj_reader_t::the_instance; way_reader_t way_reader_t::the_instance; way_obj_reader_t way_obj_reader_t::the_instance; crossing_reader_t crossing_reader_t::the_instance; bridge_reader_t bridge_reader_t::the_instance; tunnel_reader_t tunnel_reader_t::the_instance; smoke_reader_t smoke_reader_t::the_instance; fieldskin_reader_t fieldskin_reader_t::the_instance; building_reader_t building_reader_t::the_instance; tile_reader_t tile_reader_t::the_instance; factory_reader_t factory_reader_t::the_instance; factory_supplier_reader_t factory_supplier_reader_t::the_instance; factory_product_reader_t factory_product_reader_t::the_instance; factory_smoke_reader_t factory_smoke_reader_t::the_instance; factory_field_group_reader_t factory_field_group_reader_t::the_instance; field_class_desc_t* factory_field_group_reader_t::incomplete_field_class_desc = NULL; factory_field_class_reader_t factory_field_class_reader_t::the_instance; vehicle_reader_t vehicle_reader_t::the_instance; roadsign_reader_t roadsign_reader_t::the_instance; citycar_reader_t citycar_reader_t::the_instance; pedestrian_reader_t pedestrian_reader_t::the_instance; tree_reader_t tree_reader_t::the_instance; simutrans-124.5/src/simutrans/descriptor/reader/skin_reader.cc000066400000000000000000000017541520675460500246240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../obj/simobj.h" #include "../../simdebug.h" #include "../../simskin.h" #include "../../obj/wolke.h" #include "../skin_desc.h" #include "skin_reader.h" void skin_reader_t::register_obj(obj_desc_t *&data) { skin_desc_t* desc = static_cast(data); if(get_skintype() != skinverwaltung_t::nothing) { skinverwaltung_t::register_desc(get_skintype(), desc); } else { pakset_manager_t::obj_for_xref(get_type(), desc->get_name(), data); // smoke needs its own registering if( get_type()==obj_smoke ) { wolke_t::register_desc(desc); } } } bool skin_reader_t::successfully_loaded() const { PAKSET_INFO("skin_reader_t::successfully_loaded()",""); return skinverwaltung_t::successfully_loaded(get_skintype()); } obj_desc_t* skin_reader_t::read_node(FILE*, obj_node_info_t& info) { return obj_reader_t::read_node(info); } simutrans-124.5/src/simutrans/descriptor/reader/skin_reader.h000066400000000000000000000044251520675460500244640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_SKIN_READER_H #define DESCRIPTOR_READER_SKIN_READER_H #include "../../simskin.h" #include "../../tpl/slist_tpl.h" #include "obj_reader.h" class skin_reader_t : public obj_reader_t { public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; /// @returns type of skin this reader is able to read virtual skinverwaltung_t::skintyp_t get_skintype() const = 0; }; class menuskin_reader_t : public skin_reader_t { OBJ_READER_DEF(menuskin_reader_t, obj_menu, "menu"); protected: /// @copydoc skin_reader_t::get_skintype skinverwaltung_t::skintyp_t get_skintype() const OVERRIDE { return skinverwaltung_t::menu; } }; class cursorskin_reader_t : public skin_reader_t { OBJ_READER_DEF(cursorskin_reader_t, obj_cursor, "cursor"); protected: /// @copydoc skin_reader_t::get_skintype skinverwaltung_t::skintyp_t get_skintype() const OVERRIDE { return skinverwaltung_t::cursor; } }; class symbolskin_reader_t : public skin_reader_t { OBJ_READER_DEF(symbolskin_reader_t, obj_symbol, "symbol"); protected: /// @copydoc skin_reader_t::get_skintype skinverwaltung_t::skintyp_t get_skintype() const OVERRIDE { return skinverwaltung_t::symbol; } }; class fieldskin_reader_t : public skin_reader_t { OBJ_READER_DEF(fieldskin_reader_t, obj_field, "field"); protected: /// @copydoc skin_reader_t::get_skintype skinverwaltung_t::skintyp_t get_skintype() const OVERRIDE { return skinverwaltung_t::nothing; } }; class smoke_reader_t : public skin_reader_t { OBJ_READER_DEF(smoke_reader_t, obj_smoke, "smoke"); protected: /// @copydoc skin_reader_t::get_skintype skinverwaltung_t::skintyp_t get_skintype() const OVERRIDE { return skinverwaltung_t::nothing; } }; class miscimages_reader_t : public skin_reader_t { OBJ_READER_DEF(miscimages_reader_t, obj_miscimages, "misc"); protected: /// @copydoc skin_reader_t::get_skintype skinverwaltung_t::skintyp_t get_skintype() const OVERRIDE { return skinverwaltung_t::misc; } }; #endif simutrans-124.5/src/simutrans/descriptor/reader/sound_reader.cc000066400000000000000000000022521520675460500250020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../sound_desc.h" #include "sound_reader.h" #include "../obj_node_info.h" #include "../../simdebug.h" void sound_reader_t::register_obj(obj_desc_t *&data) { sound_desc_t *desc = static_cast(data); sound_desc_t::register_desc(desc); PAKSET_INFO("sound_reader_t::read_node()","sound %s registered at %i",desc->get_name(),desc->sound_id); delete desc; } obj_desc_t * sound_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; sound_desc_t *desc = new sound_desc_t(); if(version==1) { // Versioned node, version 2 desc->nr = decode_uint16(p); } else if( version == 2 ) { // Versioned node, version 2 desc->nr = decode_uint16(p); uint16 len = decode_uint16(p); if( len>0 ) { desc->nr = desc->get_sound_id(p.read_bytes(len)); } } else { dbg->fatal( "sound_reader_t::read_node()", "Cannot handle too new node version %i", version ); } return desc; } simutrans-124.5/src/simutrans/descriptor/reader/sound_reader.h000066400000000000000000000010311520675460500246360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_SOUND_READER_H #define DESCRIPTOR_READER_SOUND_READER_H #include "obj_reader.h" class sound_reader_t : public obj_reader_t { OBJ_READER_DEF(sound_reader_t, obj_sound, "sound"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/text_reader.cc000066400000000000000000000010441520675460500246340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../text_desc.h" #include "text_reader.h" #include "../obj_node_info.h" obj_desc_t *text_reader_t::read_node(FILE *fp, obj_node_info_t &node) { text_desc_t *desc = new(node.size) text_desc_t(); // Read data if (fread(desc->text, node.size, 1, fp) != 1) { delete desc; return NULL; } // PAKSET_INFO("text_reader_t::read_node()", "text=%s", desc->get_text()); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/text_reader.h000066400000000000000000000006611520675460500245020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_TEXT_READER_H #define DESCRIPTOR_READER_TEXT_READER_H #include "obj_reader.h" class text_reader_t : public obj_reader_t { OBJ_READER_DEF(text_reader_t, obj_text, "text"); public: /// @copydoc obj_reader_t::register_obj obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/tree_reader.cc000066400000000000000000000037771520675460500246260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../obj/simobj.h" #include "../../simdebug.h" #include "../../obj/baum.h" #include "../tree_desc.h" #include "../obj_node_info.h" #include "tree_reader.h" #include "../../network/pakset_info.h" void tree_reader_t::register_obj(obj_desc_t *&data) { tree_desc_t *desc = static_cast(data); tree_builder_t::register_desc(desc); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool tree_reader_t::successfully_loaded() const { return tree_builder_t::successfully_loaded(); } obj_desc_t * tree_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the highest bit was always cleared. const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; tree_desc_t *desc = new tree_desc_t(); if(version==2) { // Versioned node, version 2 desc->allowed_climates = (climate_bits)decode_uint16(p); desc->distribution_weight = (uint8)decode_uint8(p); desc->number_of_seasons = (uint8)decode_uint8(p); } else if(version == 1) { // Versioned node, version 1 desc->allowed_climates = all_but_arctic_climate; desc->number_of_seasons = 0; decode_uint8(p); // ignore hoehenlage desc->distribution_weight = (uint8)decode_uint8(p); } else { if( version ) { dbg->fatal( "tree_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // old node, version 0 desc->number_of_seasons = 0; desc->allowed_climates = all_but_arctic_climate; desc->distribution_weight = 3; } PAKSET_INFO("tree_reader_t::read_node()", "version=%i, climates=$%X, seasons=%i, chance=%i (node.size=%i)", version, desc->allowed_climates, desc->number_of_seasons, desc->distribution_weight, node.size); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/tree_reader.h000066400000000000000000000011601520675460500244500ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_TREE_READER_H #define DESCRIPTOR_READER_TREE_READER_H #include "obj_reader.h" class tree_reader_t : public obj_reader_t { OBJ_READER_DEF(tree_reader_t, obj_tree, "tree"); protected: /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/tunnel_reader.cc000066400000000000000000000121431520675460500251570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../../dataobj/ribi.h" #include "../intro_dates.h" #include "../tunnel_desc.h" #include "../obj_desc.h" #include "../obj_node_info.h" #include "tunnel_reader.h" #include "../../builder/tunnelbauer.h" #include "../../network/pakset_info.h" #include void tunnel_reader_t::register_obj(obj_desc_t *&data) { tunnel_desc_t *desc = static_cast(data); if(desc->get_topspeed()==0) { convert_old_tunnel(desc); } PAKSET_INFO("tunnel_reader_t::register_obj", "Loaded '%s'", desc->get_name()); tunnel_builder_t::register_desc(desc); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } /** * Sets default data for ancient tunnel paks */ void tunnel_reader_t::convert_old_tunnel(tunnel_desc_t *desc) { // old style, need to convert if(strcmp(desc->get_name(),"RoadTunnel")==0) { desc->wtyp = (uint8)road_wt; desc->topspeed = 120; } else { desc->wtyp = (uint8)track_wt; desc->topspeed = 280; } desc->maintenance = 500; desc->price = 200000; desc->intro_date = DEFAULT_INTRO_YEAR*12; desc->retire_date = DEFAULT_RETIRE_YEAR*12; desc->has_way = false; } obj_desc_t * tunnel_reader_t::read_node(FILE *fp, obj_node_info_t &node) { tunnel_desc_t *desc = new tunnel_desc_t(); desc->topspeed = 0; // indicate, that we have to convert this to reasonable date, when read completely if(node.size==0) { return desc; } node_body_t p(fp, node.size, get_type_name()); if (!p) { delete desc; return NULL; } const uint16 v = decode_uint16(p); const uint16 version = v & 0x8000 ? v & 0x7FFF : 0; if (version == 6) { // cost/maintenance as sint64 desc->topspeed = decode_uint32(p); desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->axle_load = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); desc->has_way = decode_uint8(p); desc->broad_portals = decode_uint8(p); } else if (version == 5) { // axle load desc->topspeed = decode_uint32(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->axle_load = decode_uint16(p); // new desc->number_of_seasons = decode_uint8(p); desc->has_way = decode_uint8(p); desc->broad_portals = decode_uint8(p); } else if (version == 4) { // broad portal support desc->topspeed = decode_uint32(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); desc->has_way = decode_uint8(p); desc->broad_portals = decode_uint8(p); } else if (version == 3) { // underground way image support desc->topspeed = decode_uint32(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); desc->has_way = decode_uint8(p); desc->broad_portals = 0; } else if (version == 2) { // snow image support desc->topspeed = decode_uint32(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); desc->has_way = 0; desc->broad_portals = 0; } else if(version == 1) { desc->topspeed = decode_uint32(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->number_of_seasons = 0; desc->has_way = 0; desc->broad_portals = 0; } else { dbg->fatal( "tunnel_reader_t::read_node()", "Cannot handle too new node version %i", version ); } if( version < 5 ) { desc->axle_load = 9999; } PAKSET_INFO("tunnel_reader_t::read_node()", "version=%d, waytype=%d, price=%" PRId64 ", maintenance=%" PRId64 ", topspeed=%d, intro=%d/%d, retire=%d/%d, axle_load=%d, has_way=%i, seasons=%i, b_portals=%i", version, desc->wtyp, desc->price, desc->maintenance, desc->topspeed, (desc->intro_date %12)+1, desc->intro_date /12, (desc->retire_date%12)+1, desc->retire_date/12, desc->axle_load, desc->has_way, desc->number_of_seasons, desc->broad_portals ); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/tunnel_reader.h000066400000000000000000000011551520675460500250220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_TUNNEL_READER_H #define DESCRIPTOR_READER_TUNNEL_READER_H #include "obj_reader.h" class tunnel_desc_t; class tunnel_reader_t : public obj_reader_t { OBJ_READER_DEF(tunnel_reader_t, obj_tunnel, "tunnel"); static void convert_old_tunnel(tunnel_desc_t *desc); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/vehicle_reader.cc000066400000000000000000000310451520675460500252730ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../../simconst.h" #include "../../builder/vehikelbauer.h" #include "../sound_desc.h" #include "../vehicle_desc.h" #include "../intro_dates.h" #include "vehicle_reader.h" #include "../obj_node_info.h" #include "../../network/pakset_info.h" void vehicle_reader_t::register_obj(obj_desc_t *&data) { vehicle_desc_t *desc = static_cast(data); vehicle_builder_t::register_desc(desc); pakset_manager_t::obj_for_xref(get_type(), desc->get_name(), data); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool vehicle_reader_t::successfully_loaded() const { return vehicle_builder_t::successfully_loaded(); } obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const int version = v & 0x8000 ? v & 0x7FFF : 0; vehicle_desc_t *desc = new vehicle_desc_t(); if (version == 13) { // loading time as uint32 desc->price = decode_sint64(p); desc->capacity = decode_uint16(p); desc->loading_time = decode_uint32(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint32(p); desc->axle_load = decode_uint16(p); desc->power = decode_uint32(p); desc->running_cost = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->engine_type = decode_uint8(p); desc->len = decode_uint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->freight_image_type = decode_uint8(p); } else if (version == 12) { // Cost values as sint64 desc->price = decode_sint64(p); desc->capacity = decode_uint16(p); desc->loading_time = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint32(p); desc->axle_load = decode_uint16(p); desc->power = decode_uint32(p); desc->running_cost = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->engine_type = decode_uint8(p); desc->len = decode_uint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->freight_image_type = decode_uint8(p); } else if (version==11) { // new: fixed cost as uint32 desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->loading_time = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint32(p); desc->axle_load = decode_uint16(p); desc->power = decode_uint32(p); desc->running_cost = decode_uint16(p); desc->maintenance = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->engine_type = decode_uint8(p); desc->len = decode_uint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->freight_image_type = decode_uint8(p); } else if (version==10) { // new: weight in kgs desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->loading_time = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint32(p); desc->axle_load = decode_uint16(p); desc->power = decode_uint32(p); desc->running_cost = decode_uint16(p); desc->maintenance = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->engine_type = decode_uint8(p); desc->len = decode_uint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->freight_image_type = decode_uint8(p); } else if (version==9) { // new: fixed_cost, loading_time, axle_load desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->loading_time = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint16(p); desc->axle_load = decode_uint16(p); desc->power = decode_uint32(p); desc->running_cost = decode_uint16(p); desc->maintenance = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->engine_type = decode_uint8(p); desc->len = decode_uint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->freight_image_type = decode_uint8(p); } else if (version==8) { // multiple freight images desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint16(p); desc->power = decode_uint32(p); desc->running_cost = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->engine_type = decode_uint8(p); desc->len = decode_uint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->freight_image_type = decode_uint8(p); } else if (version==7) { // different length of cars desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint16(p); desc->power = decode_uint32(p); desc->running_cost = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->engine_type = decode_uint8(p); desc->len = decode_uint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); } else if (version==6) { // like version 5, just 32 bit for power and 16 Bit for gear desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint16(p); desc->power = decode_uint32(p); desc->running_cost = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->engine_type = decode_uint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); } else if (version==3 || version==4 || version==5) { // version 3: with retire date // version 4 is identical, just other values for the waytype // version 5 just uses the new scheme for data calculation desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint16(p); desc->power = decode_uint16(p); desc->running_cost = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->gear = decode_uint8(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->engine_type = decode_uint8(p); } else if (version == 2) { desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint16(p); desc->power = decode_uint16(p); desc->running_cost = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->gear = decode_uint8(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->engine_type = decode_uint8(p); desc->retire_date = (DEFAULT_RETIRE_YEAR*16); } else if (version == 1) { desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint16(p); desc->power = decode_uint16(p); desc->running_cost = decode_uint16(p); desc->intro_date = decode_uint16(p); desc->gear = decode_uint8(p); desc->wtyp = decode_uint8(p); desc->sound = decode_sint8(p); desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->retire_date = (DEFAULT_RETIRE_YEAR*16); } else { if( version ) { dbg->fatal( "vehicle_reader_t::read_node()", "Cannot handle too new node version %i", version ); } // old node, version 0 desc->wtyp = (sint8)v; desc->capacity = decode_uint16(p); desc->price = decode_uint32(p); desc->topspeed = decode_uint16(p); desc->weight = decode_uint16(p); desc->power = decode_uint16(p); desc->running_cost = decode_uint16(p); desc->sound = (sint8)decode_sint16(p); desc->leader_count = (sint8)decode_uint16(p); desc->trailer_count = (sint8)decode_uint16(p); desc->intro_date = DEFAULT_INTRO_YEAR * 16; desc->retire_date = DEFAULT_RETIRE_YEAR * 16; desc->gear = 64; } // correct the engine type for old vehicles if(version<2) { // steam eangines usually have a sound of 3 // electric engines will be overridden further down desc->engine_type = (desc->sound==3) ? vehicle_desc_t::steam : vehicle_desc_t::diesel; } //change the vehicle type if(version<4) { if(desc->wtyp==4) { desc->engine_type = vehicle_desc_t::electric; desc->wtyp = 1; } // convert to new standard static const waytype_t convert_from_old[8]={road_wt, track_wt, water_wt, air_wt, invalid_wt, monorail_wt, invalid_wt, tram_wt }; desc->wtyp = convert_from_old[desc->wtyp]; } // before version 5 dates were based on base 16 if(version<5) { desc->intro_date = (desc->intro_date /16)*12 + (desc->intro_date %16); desc->retire_date = (desc->retire_date/16)*12 + (desc->retire_date%16); } // before the length was always 1/8 (=half a tile) if(version<7) { desc->len = CARUNITS_PER_TILE/2; } // adjust length for different offset step sizes (which may arise in future) desc->len *= OBJECT_OFFSET_STEPS/CARUNITS_PER_TILE; // before version 8 vehicles could only have one freight image in each direction if(version<8) { desc->freight_image_type=0; } if(version<9) { desc->maintenance = 0; desc->axle_load = 0; desc->loading_time = 1000; } // old weights were tons if(version<10) { desc->weight *= 1000; } if(desc->sound==LOAD_SOUND) { uint8 len = decode_sint8(p); char wavname[256]; wavname[len] = 0; for(uint8 i=0; isound = (sint8)sound_desc_t::get_sound_id(wavname); PAKSET_INFO("vehicle_reader_t::register_obj()","sound %s to %i",wavname,desc->sound); } else if(desc->sound>=0 && desc->sound<=MAX_OLD_SOUNDS) { sint16 old_id = desc->sound; desc->sound = (sint8)sound_desc_t::get_compatible_sound_id((sint8)old_id); PAKSET_INFO("vehicle_reader_t::register_obj()","old sound %i to %i",old_id,desc->sound); } return desc; } simutrans-124.5/src/simutrans/descriptor/reader/vehicle_reader.h000066400000000000000000000011751520675460500251360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_VEHICLE_READER_H #define DESCRIPTOR_READER_VEHICLE_READER_H #include "obj_reader.h" class vehicle_reader_t : public obj_reader_t { OBJ_READER_DEF(vehicle_reader_t, obj_vehicle, "vehicle"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t*&) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/way_obj_reader.cc000066400000000000000000000043641520675460500253120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../../utils/simstring.h" #include "../way_obj_desc.h" #include "../../obj/wayobj.h" #include "way_obj_reader.h" #include "../obj_node_info.h" #include "../../network/pakset_info.h" #include void way_obj_reader_t::register_obj(obj_desc_t *&data) { way_obj_desc_t *desc = static_cast(data); wayobj_t::register_desc(desc); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool way_obj_reader_t::successfully_loaded() const { return wayobj_t::successfully_loaded(); } obj_desc_t * way_obj_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); const uint16 version = v & 0x7FFF; way_obj_desc_t *desc = new way_obj_desc_t(); if (version == 2) { // Cost/maintenance as sint64 desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->topspeed = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->own_wtyp = decode_uint8(p); } else if (version==1) { desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->topspeed = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->own_wtyp = decode_uint8(p); } else { dbg->fatal( "way_obj_reader_t::read_node()", "Cannot handle too new node version %i", version ); } PAKSET_INFO("way_obj_reader_t::read_node()", "version=%d price=%" PRId64 " maintenance=%" PRId64 " topspeed=%d wtype=%d own_wtype=%d intro=%i/%i, retire=%i/%i", version, desc->price, desc->maintenance, desc->topspeed, desc->wtyp, desc->own_wtyp, (desc->intro_date %12)+1, desc->intro_date /12, (desc->retire_date%12)+1, desc->retire_date/12 ); return desc; } simutrans-124.5/src/simutrans/descriptor/reader/way_obj_reader.h000066400000000000000000000012051520675460500251430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_WAY_OBJ_READER_H #define DESCRIPTOR_READER_WAY_OBJ_READER_H #include "obj_reader.h" class way_obj_reader_t : public obj_reader_t { OBJ_READER_DEF(way_obj_reader_t, obj_way_obj, "way-object"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/way_reader.cc000066400000000000000000000133441520675460500244560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../../utils/simstring.h" #include "../way_desc.h" #include "../intro_dates.h" #include "../../builder/wegbauer.h" #include "way_reader.h" #include "../obj_node_info.h" #include "../../network/pakset_info.h" void way_reader_t::register_obj(obj_desc_t *&data) { way_desc_t *desc = static_cast(data); way_builder_t::register_desc(desc); pakset_manager_t::obj_for_xref(get_type(), desc->get_name(), data); checksum_t *chk = new checksum_t(); desc->calc_checksum(chk); pakset_info_t::append(desc->get_name(), get_type_name(), chk); } bool way_reader_t::successfully_loaded() const { return way_builder_t::successfully_loaded(); } obj_desc_t * way_reader_t::read_node(FILE *fp, obj_node_info_t &node) { node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; way_desc_t *desc = new way_desc_t; const uint16 version = node.size==0 ? 0 : decode_uint16(p)&0x7FFFu; if (version == 8) { // clipping of elevated ways desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->topspeed = decode_uint32(p); desc->max_weight = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->axle_load = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->styp = decode_uint8(p); desc->draw_as_obj = decode_uint8(p); desc->clip_below = decode_uint8(p); desc->number_of_seasons = decode_sint8(p); } else if (version == 7) { // cost/maintenance as sint64 desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->topspeed = decode_uint32(p); desc->max_weight = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->axle_load = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->styp = decode_uint8(p); desc->draw_as_obj = decode_uint8(p); desc->number_of_seasons = decode_sint8(p); } else if (version == 6) { // with axle load desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->topspeed = decode_uint32(p); desc->max_weight = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->axle_load = decode_uint16(p); // new desc->wtyp = decode_uint8(p); desc->styp = decode_uint8(p); desc->draw_as_obj = decode_uint8(p); desc->number_of_seasons = decode_sint8(p); } else if (version==4 || version==5) { desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->topspeed = decode_uint32(p); desc->max_weight = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->styp = decode_uint8(p); desc->draw_as_obj = decode_uint8(p); desc->number_of_seasons = decode_sint8(p); } else if (version == 3) { desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->topspeed = decode_uint32(p); desc->max_weight = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->styp = decode_uint8(p); desc->draw_as_obj = decode_uint8(p); desc->number_of_seasons = 0; } else if (version == 2) { desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->topspeed = decode_uint32(p); desc->max_weight = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->styp = decode_uint8(p); desc->draw_as_obj = false; desc->number_of_seasons = 0; } else if (version == 1) { desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->topspeed = decode_uint32(p); desc->max_weight = decode_uint32(p); uint32 intro_date = decode_uint32(p); desc->intro_date = (intro_date/16)*12 + (intro_date%16); desc->wtyp = decode_uint8(p); desc->styp = decode_uint8(p); desc->retire_date = DEFAULT_RETIRE_YEAR*12; desc->draw_as_obj = false; desc->number_of_seasons = 0; } else if (version == 0) { // old node, compatiblity mode desc->price = 10000; desc->maintenance = 800; desc->topspeed = 999; desc->max_weight = 999; desc->intro_date = DEFAULT_INTRO_YEAR*12; desc->retire_date = DEFAULT_RETIRE_YEAR*12; desc->wtyp = road_wt; desc->styp = type_flat; desc->draw_as_obj = false; desc->number_of_seasons = 0; } else { dbg->fatal("way_reader_t::read_node()", "Cannot handle too new node version %i", version); } // some internal corrections to pay for previous confusion with two waytypes if(desc->wtyp==tram_wt) { desc->styp = type_tram; desc->wtyp = track_wt; } else if(desc->styp==5 && desc->wtyp==track_wt) { desc->wtyp = monorail_wt; desc->styp = type_flat; } else if(desc->wtyp==128) { desc->wtyp = powerline_wt; } if (version<=2 && desc->wtyp==air_wt && desc->topspeed>=250) { // runway! desc->styp = type_runway; } if( version < 6 ) { desc->axle_load = 9999; } if (version < 8) { desc->clip_below = desc->wtyp != powerline_wt; } // front images from version 5 on desc->front_images = version > 4; return desc; } simutrans-124.5/src/simutrans/descriptor/reader/way_reader.h000066400000000000000000000011521520675460500243120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_WAY_READER_H #define DESCRIPTOR_READER_WAY_READER_H #include "obj_reader.h" class way_reader_t : public obj_reader_t { OBJ_READER_DEF(way_reader_t, obj_way, "way"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; /// @copydoc obj_reader_t::successfully_loaded bool successfully_loaded() const OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/reader/xref_reader.cc000066400000000000000000000021441520675460500246160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../simdebug.h" #include "../xref_desc.h" #include "xref_reader.h" #include "../obj_node_info.h" obj_desc_t *xref_reader_t::read_node(FILE *fp, obj_node_info_t &node) { if (node.size < 5) { dbg->error("xref_reader_t::read_node", "node.size %u < 5", node.size); return NULL; } node_body_t p(fp, node.size, get_type_name()); if (!p) return NULL; const uint32 name_len = node.size - 4 - 1; xref_desc_t* desc = new(name_len) xref_desc_t(); desc->type = static_cast(decode_uint32(p)); desc->fatal = (decode_uint8(p) != 0); memcpy(desc->name, p.read_bytes(name_len), name_len); // PAKSET_INFO("xref_reader_t::read_node()", "%s",desc->get_text() ); return desc; } void xref_reader_t::register_obj(obj_desc_t *&data) { xref_desc_t* desc = static_cast(data); if (desc->name[0] != '\0' || desc->fatal) { pakset_manager_t::xref_to_resolve(desc->type, desc->name, &data, desc->fatal); } else { delete data; data = NULL; } } simutrans-124.5/src/simutrans/descriptor/reader/xref_reader.h000066400000000000000000000010301520675460500244510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_READER_XREF_READER_H #define DESCRIPTOR_READER_XREF_READER_H #include "obj_reader.h" class xref_reader_t : public obj_reader_t { OBJ_READER_DEF(xref_reader_t, obj_xref, "reference"); protected: /// @copydoc obj_reader_t::register_obj void register_obj(obj_desc_t *&desc) OVERRIDE; public: /// @copydoc obj_reader_t::read_node obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/roadsign_desc.h000066400000000000000000000054011520675460500235330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_ROADSIGN_DESC_H #define DESCRIPTOR_ROADSIGN_DESC_H #include "obj_base_desc.h" #include "image_list.h" #include "skin_desc.h" #include "../dataobj/ribi.h" #include "../simtypes.h" #include "../network/checksum.h" /** * Road signs * * Child nodes: * 0 Name * 1 Copyright * 2 Image list */ class roadsign_desc_t : public obj_desc_transport_infrastructure_t { friend class roadsign_reader_t; private: uint16 flags; sint8 offset_left; // default 14 uint16 min_speed; // 0 = no min speed public: enum types { NONE = 0, ONE_WAY = 1U << 0, CHOOSE_SIGN = 1U << 1, PRIVATE_ROAD = 1U << 2, SIGN_SIGNAL = 1U << 3, SIGN_PRE_SIGNAL = 1U << 4, ONLY_BACKIMAGE = 1U << 5, SIGN_LONGBLOCK_SIGNAL = 1U << 6, END_OF_CHOOSE_AREA = 1U << 7, SIGN_PRIORITY_SIGNAL = 1U << 8 }; image_id get_image_id(ribi_t::dir dir) const { image_t const* const image = get_child(2)->get_image(dir); return image != NULL ? image->get_id() : IMG_EMPTY; } uint16 get_count() const { return get_child(2)->get_count(); } skin_desc_t const* get_cursor() const { return get_child(3); } uint16 get_min_speed() const { return min_speed; } bool is_single_way() const { return (flags & ONE_WAY) != 0; } bool is_private_way() const { return (flags & PRIVATE_ROAD) != 0; } bool is_choose_sign() const { return (flags & CHOOSE_SIGN) != 0; } // return true for signal bool is_simple_signal() const { return (flags & ( SIGN_SIGNAL | SIGN_PRE_SIGNAL | SIGN_PRIORITY_SIGNAL | SIGN_LONGBLOCK_SIGNAL | CHOOSE_SIGN)) == SIGN_SIGNAL; } // return true for presignal bool is_pre_signal() const { return (flags & SIGN_PRE_SIGNAL) != 0; } // return true for priority signal bool is_priority_signal() const { return (flags & SIGN_PRIORITY_SIGNAL) != 0; } // return true for single track section signal bool is_longblock_signal() const { return (flags & SIGN_LONGBLOCK_SIGNAL) != 0; } bool is_end_choose_signal() const { return (flags & END_OF_CHOOSE_AREA) != 0; } bool is_signal_type() const { return (flags&( SIGN_SIGNAL | SIGN_PRE_SIGNAL | SIGN_PRIORITY_SIGNAL | SIGN_LONGBLOCK_SIGNAL) ) != 0; } // return true for a traffic light bool is_traffic_light() const { return !is_signal_type() && get_count() > 4; } uint16 get_flags() const { return flags; } sint8 get_offset_left() const { return offset_left; } void calc_checksum(checksum_t *chk) const { obj_desc_transport_infrastructure_t::calc_checksum(chk); chk->input(flags); chk->input(min_speed); } }; ENUM_BITSET(roadsign_desc_t::types) #endif simutrans-124.5/src/simutrans/descriptor/skin_desc.h000066400000000000000000000014241520675460500226720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_SKIN_DESC_H #define DESCRIPTOR_SKIN_DESC_H #include "../display/simimg.h" #include "obj_base_desc.h" #include "image_array.h" /** * An image list, with name and author attributes. Mostly used for gui purposes. * * Child nodes: * 0 Name * 1 Copyright * 2 Image list */ class skin_desc_t : public obj_named_desc_t { public: image_t const* get_image(uint16 i) const { return get_child(2)->get_image(i); } uint16 get_count() const { return get_child(2)->get_count(); } image_id get_image_id(uint16 i) const { const image_t *image = get_image(i); return image != NULL ? image->get_id() : IMG_EMPTY; } }; #endif simutrans-124.5/src/simutrans/descriptor/sound_desc.cc000066400000000000000000000107501520675460500232160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../simdebug.h" #include "../dataobj/tabfile.h" #include "../dataobj/environment.h" #include "../macros.h" #include "../sound/sound.h" #include "../utils/simstring.h" #include "../tpl/stringhashtable_tpl.h" #include "spezial_obj_tpl.h" #include "sound_desc.h" #include "ground_desc.h" /* * sound of the program */ class sound_ids { public: std::string filename; sint16 id; sound_ids() { id=NO_SOUND; } sound_ids(sint16 i) { id=i; } sound_ids(sint16 i, const char* fn) : filename(fn), id(i) {} }; static stringhashtable_tpl name_sound; static bool sound_on = false; const int NUM_SOUND_PATHS = 2; static std::string sound_paths[NUM_SOUND_PATHS]; sint16 sound_desc_t::compatible_sound_id[MAX_OLD_SOUNDS]= { NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND }; // sound with the names of climates and "beaches" and "forest" are reserved for ambient noises sint16 sound_desc_t::beach_sound = NO_SOUND; sint16 sound_desc_t::forest_sound = NO_SOUND; sint16 sound_desc_t::climate_sounds[MAX_CLIMATES]= { NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND, NO_SOUND }; sint16 sound_desc_t::message_sound; /* init sounds */ /* standard sounds and old sounds are found in the file /sound/sound.tab */ void sound_desc_t::init(const std::string &pak_dir, const std::string &addon_dir) { // ok, now init sound_on = true; sound_paths[0] = addon_dir + "sound/"; sound_paths[1] = pak_dir + "sound/"; tabfile_t soundconf; const std::string tabfilename = pak_dir + "sound/sound.tab"; if (!soundconf.open(tabfilename.c_str())) { return; } DBG_MESSAGE("sound_desc_t::init()","successfully opened sound/sound.tab" ); tabfileobj_t contents; soundconf.read(contents); // max. 16 old sounds ... for( unsigned i=0; i0) { DBG_MESSAGE("sound_desc_t::init()","reading sound %s", fn ); compatible_sound_id[i] = get_sound_id( fn ); DBG_MESSAGE("sound_desc_t::init()","assigned system sound %d to sound %s (id=%i)", i, (const char *)fn, compatible_sound_id[i] ); } } message_sound = get_sound_id("message.wav"); // now assign special sounds for climates, beaches and forest beach_sound = get_sound_id( "beaches.wav" ); forest_sound = get_sound_id( "forest.wav" ); for( int i=0; iid!=NO_SOUND) { DBG_MESSAGE("sound_desc_t::get_sound_id()", "Successfully retrieved sound \"%s\" with internal id %hi", s->filename.c_str(), s->id ); return s->id; } // not loaded: try to load it sint16 sample_id = NO_SOUND; for( int i=0; iwarning("sound_desc_t::get_sound_id()", "Sound \"%s\" not found", name ); return NO_SOUND; } s = new sound_ids(sample_id, name); name_sound.put(s->filename.c_str(), s ); DBG_MESSAGE("sound_desc_t::get_sound_id()", "Successfully loaded sound \"%s\" with internal id %hi", s->filename.c_str(), s->id ); return s->id; } /* * if there is already such a sound => fail, else success and get an internal sound id * do not store desc as it will be deleted anyway */ bool sound_desc_t::register_desc(sound_desc_t *desc) { if( !sound_on ) { return false; } // register, if not there (all done by this one here) desc->sound_id = get_sound_id( desc->get_name() ); if(desc->sound_id!=NO_SOUND) { if(desc->nr>=0 && desc->nr<=8) { compatible_sound_id[desc->nr] = desc->sound_id; DBG_MESSAGE("sound_desc_t::register_desc", "Successfully registered sound %s internal id %i as compatible sound %i", desc->get_name(), desc->sound_id, desc->nr ); return true; } } dbg->warning("sound_desc_t::register_desc", "Failed to register sound %s internal id %i", desc->get_name(), desc->sound_id); return false; } simutrans-124.5/src/simutrans/descriptor/sound_desc.h000066400000000000000000000032401520675460500230540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_SOUND_DESC_H #define DESCRIPTOR_SOUND_DESC_H #include "obj_base_desc.h" #include "../simtypes.h" #include #define NO_SOUND (sint16)(0xFFFFu) #define LOAD_SOUND (sint8)(0xFFFEu) #define AMBIENT_SOUND_INTERVALL (13000) #define SFX_CASH sound_desc_t::get_compatible_sound_id(15) #define SFX_REMOVER sound_desc_t::get_compatible_sound_id(14) #define SFX_DOCK sound_desc_t::get_compatible_sound_id(13) #define SFX_GAVEL sound_desc_t::get_compatible_sound_id(12) #define SFX_JACKHAMMER sound_desc_t::get_compatible_sound_id(11) #define SFX_FAILURE sound_desc_t::get_compatible_sound_id(10) #define SFX_SELECT sound_desc_t::get_compatible_sound_id(9) #define MAX_OLD_SOUNDS (16) /** * Sounds in the game; name is the file name * ingame, sounds are referred to by their number * * Child nodes: * 0 Name * 1 Copyright */ class sound_desc_t : public obj_named_desc_t { friend class sound_reader_t; private: static sint16 compatible_sound_id[MAX_OLD_SOUNDS]; sint16 sound_id; sint16 nr; // for old sounds/system sounds etc. public: // sounds for ambient static sint16 beach_sound; static sint16 forest_sound; static sint16 climate_sounds[MAX_CLIMATES]; // sound for message notification static sint16 message_sound; static sint16 get_sound_id(const char *name); static bool register_desc(sound_desc_t *desc); static void init(const std::string &pak_dir, const std::string &addon_dir); /* return old sound id from index */ static sint16 get_compatible_sound_id(const sint8 nr) { return compatible_sound_id[nr&(15)]; } }; #endif simutrans-124.5/src/simutrans/descriptor/spezial_obj_tpl.h000066400000000000000000000040171520675460500241110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_SPEZIAL_OBJ_TPL_H #define DESCRIPTOR_SPEZIAL_OBJ_TPL_H #include #include #include "../simdebug.h" /** * @file spezial_obj_tpl.h * * Routines to manage special object descriptors lists used in the program. */ /** * Descriptors of required objects. The following functions manage * the list. The list is "{NULL, NULL}" terminated. */ template struct special_obj_tpl { const desc_t** desc; const char* name; }; /** * An object pointer is set on the passed list, if the name of the * object belongs to one of the objects mentioned in the list. * @param so List to operate over. * @param desc Descriptor to add. */ template bool register_desc(special_obj_tpl const* so, desc_t const* const desc) { for (; so->name; ++so) { if (strcmp(so->name, desc->get_name()) == 0) { if (*so->desc != NULL ) { // these doublettes are harmless, and hence only recored at debug level 3 // dbg->doubled( "object", desc->get_name() ); dbg->message("register_desc()", "Notice: obj %s already defined", so->name); } *so->desc = desc; return true; } } return false; } /** * Verifies the passed list for all objects to be not NULL, ie are loaded. * @param so List to check. */ template bool successfully_loaded(special_obj_tpl const* so) { for (; so->name; ++so) { if (!*so->desc) { dbg->fatal("successfully_loaded()", "%s-object %s not found.\n*** PLEASE INSTALL PROPER BASE FILE AND CHECK PATH ***", typeid(**so->desc).name(), so->name); return false; } } return true; } /** * Shows debug messages showing which descriptors lack definition. * @param so List to check. */ template void warn_missing_objects(special_obj_tpl const* so) { for (; so->name; ++so) { if (!*so->desc) { dbg->message("warn_missing_objects", "Object %s not found, feature disabled", so->name); } } } #endif simutrans-124.5/src/simutrans/descriptor/text_desc.h000066400000000000000000000006051520675460500227120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_TEXT_DESC_H #define DESCRIPTOR_TEXT_DESC_H #include "obj_desc.h" class text_desc_t : public obj_desc_t { public: const char* get_text() const { return text; } using obj_desc_t::operator new; private: char text[]; friend class text_reader_t; }; #endif simutrans-124.5/src/simutrans/descriptor/tree_desc.h000066400000000000000000000027471520675460500226760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_TREE_DESC_H #define DESCRIPTOR_TREE_DESC_H #include "../simtypes.h" #include "obj_base_desc.h" #include "image_array.h" #include "../network/checksum.h" /** * Tree type description in Simutrans * * Child nodes: * 0 Name * 1 Copyright * 2 Image-array * * season 0 is always summer * season 1 is winter for two seasons * otherwise 0 summer, next seasons (autumn, winter, spring) .... */ class tree_desc_t : public obj_named_desc_t { friend class tree_reader_t; climate_bits allowed_climates; uint8 distribution_weight; uint8 number_of_seasons; public: uint16 get_distribution_weight() const { return distribution_weight; } bool is_allowed_climate( climate cl ) const { return ((1<(2)->get_image(i, season)->get_id(); } // old style trees and new style tree support ... uint8 get_seasons() const { if(number_of_seasons==0) { return get_child(2)->get_count() / 5; } return number_of_seasons; } void calc_checksum(checksum_t *chk) const { chk->input((uint8)allowed_climates); chk->input(distribution_weight); } }; #endif simutrans-124.5/src/simutrans/descriptor/tunnel_desc.cc000066400000000000000000000026241520675460500233740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../dataobj/ribi.h" #include "tunnel_desc.h" int tunnel_desc_t::slope_indices[81] = { -1, // 0: -1, // 1: -1, // 2: -1, // 3: 1, // 4:north slope -1, // 5: -1, // 6: -1, // 7: 1, // 8:north slope -1, // 9: -1, // 10: -1, // 11: 2, // 12:west slope -1, // 13: -1, // 14: -1, // 15: -1, // 16: -1, // 17: -1, // 18: -1, // 19: -1, // 20: -1, // 21: -1, // 22: -1, // 23: 2, // 24:west slope -1, // 25: -1, // 26: -1, // 27: 3, // 28:east slope -1, // 29: -1, // 30: -1, // 31: -1, // 32: -1, // 33: -1, // 34: -1, // 35: 0, // 36:south slope -1, // 37: -1, // 38: -1, // 39: -1, // 40: -1, // 41: -1, // 42: -1, // 43: -1, // 44: -1, // 45: -1, // 46: -1, // 47: -1, // 48: -1, // 49: -1, // 50: -1, // 51: -1, // 52: -1, // 53: -1, // 54: -1, // 55: 3, // 56:east slope -1, // 57: -1, // 58: -1, // 59: -1, // 60: -1, // 61: -1, // 62: -1, // 63: -1, // 64: -1, // 65: -1, // 66: -1, // 67: -1, // 68: -1, // 69: -1, // 70: -1, // 71: 0, // 72:south slope -1, // 73: -1, // 74: -1, // 75: -1, // 76: -1, // 77: -1, // 78: -1, // 79: -1 // 80: }; waytype_t tunnel_desc_t::get_finance_waytype() const { return ((get_way_desc() && (get_way_desc()->get_styp() == type_tram)) ? tram_wt : get_waytype()) ; } simutrans-124.5/src/simutrans/descriptor/tunnel_desc.h000066400000000000000000000043761520675460500232440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_TUNNEL_DESC_H #define DESCRIPTOR_TUNNEL_DESC_H #include "../display/simimg.h" #include "../simtypes.h" #include "obj_base_desc.h" #include "skin_desc.h" #include "image_array.h" #include "way_desc.h" /* * node structure: * 0 Name * 1 Copyright * 2 Image-list Background * 3 Image-list Foreground * 4 cursor(image 0) and icon (image 1) *[ 5 Image-list Background - snow ] (if present) *[ 6 Image-list Foreground - snow ] (if present) *[ 7 (or 5 if no snow image) underground way ] (if present) */ class tunnel_desc_t : public obj_desc_transport_infrastructure_t { friend class tunnel_reader_t; friend class tunnel_builder_t; // to convert the old tunnels to new ones private: static int slope_indices[81]; /* number of seasons (0 = none, 1 = no snow/snow */ sint8 number_of_seasons; /* has underground way image ? ( 0 = no, 1 = yes ) [way parameter] */ uint8 has_way; /* Has broad portals? */ uint8 broad_portals; public: const image_t *get_background(slope_t::type slope, uint8 season, uint8 type ) const { const uint8 n = season && number_of_seasons == 1 ? 5 : 2; return get_child(n)->get_image(slope_indices[slope] + 4 * type); } image_id get_background_id(slope_t::type slope, uint8 season, uint8 type ) const { const image_t *desc = get_background(slope, season, type ); return desc != NULL ? desc->get_id() : IMG_EMPTY; } const image_t *get_foreground(slope_t::type slope, uint8 season, uint8 type ) const { const uint8 n = season && number_of_seasons == 1 ? 6 : 3; return get_child(n)->get_image(slope_indices[slope] + 4 * type); } image_id get_foreground_id(slope_t::type slope, uint8 season, uint8 type) const { const image_t *desc = get_foreground(slope, season, type ); return desc != NULL ? desc->get_id() :IMG_EMPTY; } skin_desc_t const* get_cursor() const { return get_child(4); } waytype_t get_finance_waytype() const; const way_desc_t *get_way_desc() const { if(has_way) { return get_child(5 + number_of_seasons * 2); } return NULL; } bool has_broad_portals() const { return (broad_portals != 0); } }; #endif simutrans-124.5/src/simutrans/descriptor/vehicle_desc.cc000066400000000000000000000047231520675460500235100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "vehicle_desc.h" #include "xref_desc.h" #include "../network/checksum.h" #include "../simdebug.h" vehicle_desc_t *vehicle_desc_t::any_vehicle = NULL; void vehicle_desc_t::calc_checksum(checksum_t *chk) const { PAKSET_INFO("","obj=vehicle"); obj_desc_transport_related_t::calc_checksum(chk); #if MSG_LEVEL>0 if(capacity) PAKSET_INFO("payload=","%d",capacity); if(loading_time!=1000) PAKSET_INFO("loading_time=","%d",loading_time); if(weight) PAKSET_INFO("weight=","%d",weight/1000); if(power) { PAKSET_INFO("power=","%d",power); if(gear!=64) PAKSET_INFO("gear=","%d",(gear*100)/64); } if(running_cost) PAKSET_INFO("running_cost=","%d",running_cost); if(len!=8) PAKSET_INFO("length=","%d",len); if(const xref_desc_t *xref = get_child(2)) PAKSET_INFO("freight=","%s",xref->get_name()); switch((int)engine_type) { case electric: PAKSET_INFO("waytype=","electric"); break; case steam: PAKSET_INFO("waytype=","steam"); break; case bio: PAKSET_INFO("waytype=","bio"); break; case sail: PAKSET_INFO("waytype=","sail"); break; case fuel_cell: PAKSET_INFO("waytype=","fuel_cell"); break; case hydrogene: PAKSET_INFO("waytype=","hydrogene"); break; case battery: PAKSET_INFO("waytype=","battery"); break; case unknown: PAKSET_INFO("waytype=","unknown"); break; default: case diesel: // PAKSET_INFO("waytype=","diesel"); break; } // leader constraints for(uint16 i=0; i(6+i)) PAKSET_INFO("constraint[prev]","[%d]=%s",i,xref->get_name()); } // trailer constraints for(uint16 i=0; i(6+i+leader_count)) PAKSET_INFO("constraint[next]","[%d]=%s",i,xref->get_name()); } PAKSET_INFO("--",""); #endif chk->input(capacity); chk->input(loading_time); chk->input(weight); chk->input(power); chk->input(running_cost); chk->input(maintenance); chk->input(gear); chk->input(len); chk->input(leader_count); chk->input(trailer_count); chk->input(engine_type); // freight const xref_desc_t *xref = get_child(2); chk->input(xref ? xref->get_name() : "NULL"); // vehicle constraints for(uint16 i=0; i(6+i); chk->input(xref ? xref->get_name() : "NULL"); } } simutrans-124.5/src/simutrans/descriptor/vehicle_desc.h000066400000000000000000000157721520675460500233600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_VEHICLE_DESC_H #define DESCRIPTOR_VEHICLE_DESC_H #include "obj_base_desc.h" #include "goods_desc.h" #include "image_list.h" #include "image_array.h" #include "skin_desc.h" #include "sound_desc.h" #include "../dataobj/ribi.h" #include "../simtypes.h" #include "../simunits.h" class checksum_t; /** * Vehicle type description - all attributes of a vehicle type * * Child nodes: * 0 Name * 1 Copyright * 2 freight * 3 smoke * 4 empty 1d image list * 5 either 1d (freight_image_type==0) or 2d image list * 6 required leading vehicle 1 * 7 required leading vehicle 2 * ... ... * n+5 required leading vehicle n * n+6 allowed trailing vehicle 1 * n+7 allowed trailing vehicle 2 * ... ... * n+m+5 allowed trailing vehicle m * n+m+6 freight for which special images are defined */ class vehicle_desc_t : public obj_desc_transport_related_t { friend class vehicle_reader_t; friend class vehicle_builder_t; public: /** * Engine type */ enum engine_t { unknown = -1, steam = 0, diesel, electric, bio, sail, fuel_cell, hydrogene, battery }; private: uint32 weight = 1; uint32 power = 1; sint64 running_cost = 0; uint16 capacity = 0; uint32 loading_time = 1000; // time per full loading/unloading uint16 gear = 64; // engine gear (power multiplier), 64=100 uint8 len = CARUNITS_PER_TILE/2; // length (=8 is half a tile, the old default) sint8 sound = -1; uint8 leader_count = 0; // all defined leading vehicles uint8 trailer_count = 0; // all defined trailer uint8 engine_type = engine_t::unknown; // diesel, steam, electric (requires electrified ways), fuel_cell, etc. sint8 freight_image_type = 0; // number of freight images (displayed for different goods) public: // dummy vehicle for the XREF reader static vehicle_desc_t *any_vehicle; // since we have a second constructor vehicle_desc_t() = default; // default vehicle (used for way search and similar tasks) // since it has no images and not even a name node any calls to this will case a crash vehicle_desc_t(uint8 wtype, uint16 speed, engine_t engine) { maintenance = price = axle_load = intro_date = 0; wtyp = wtype; engine_type = (uint8)engine; topspeed = speed; } goods_desc_t const* get_freight_type() const { return get_child(2); } skin_desc_t const* get_smoke() const { return get_child(3); } image_id get_base_image() const { return get_image_id(ribi_t::dir_south, get_freight_type() ); } // returns the number of different directions uint8 get_dirs() const { return get_child(4)->get_image(4) ? 8 : 4; } // return a matching image // beware, there are three classes of vehicles // vehicles with and without freight images, and vehicles with different freight images // they can have 4 or 8 directions ... image_id get_image_id(ribi_t::dir dir, const goods_desc_t *ware) const { const image_t *image=0; const image_list_t *list=0; if(freight_image_type>0 && ware!=NULL) { // more freight images and a freight: find the right one sint8 goods_index=0; // freight images: if not found use first freight for( sint8 i=0; i(6 + trailer_count + leader_count + i)) { goods_index = i; break; } } // vehicle has freight images and we want to use - get appropriate one (if no list then fallback to empty image) image_array_t const* const list2d = get_child(5); image=list2d->get_image(dir, goods_index); if(!image) { if(dir>3) { image = list2d->get_image(dir - 4, goods_index); } } if (image != NULL) return image->get_id(); } // only try 1d freight image list for old style vehicles if(freight_image_type==0 && ware!=NULL) { list = get_child(5); } if(!list) { list = get_child(4); if(!list) { return IMG_EMPTY; } } image = list->get_image(dir); if(!image) { if(dir>3) { image = list->get_image(dir - 4); } if(!image) { return IMG_EMPTY; } } return image->get_id(); } /** * Returns allowed leader vehicles. * If get_leader(0) == NULL then either all or no leaders are allowed. * To distinguish these cases check get_leader_count(). */ const vehicle_desc_t *get_leader(uint8 i) const { if( i >= leader_count ) { return 0; } return get_child(6 + i); } uint8 get_leader_count() const { return leader_count; } /** * Returns vehicles that this vehicle is allowed to pull. * If get_trailer(0) == NULL then either all or no followers are allowed. * To distinguish these cases check get_trailer_count(). */ const vehicle_desc_t *get_trailer(uint8 i) const { if( i >= trailer_count ) { return 0; } return get_child(6 + leader_count + i); } uint8 get_trailer_count() const { return trailer_count; } /* returns true, if this veh can be before the next_veh * uses NULL to indicate end of convoi */ bool can_lead(const vehicle_desc_t *next_veh) const { if( trailer_count==0 ) { return true; } for( uint8 i=0; i(6 + leader_count + i); if( veh==next_veh ) { return true; } // not leading and "any" => we can follow if( next_veh!=NULL && veh==vehicle_desc_t::any_vehicle ) { return true; } } // only here if not allowed return false; } /* returns true, if this veh can be after the prev_veh * uses NULL to indicate front of convoi */ bool can_follow(const vehicle_desc_t *prev_veh) const { if( leader_count==0 ) { return true; } for( uint8 i=0; i(6 + i); if( veh==prev_veh ) { return true; } // not leading and "any" => we can follow if( prev_veh!=NULL && veh==vehicle_desc_t::any_vehicle ) { return true; } } // only here if not allowed return false; } bool can_follow_any() const { return trailer_count==0; } uint16 get_capacity() const { return capacity; } uint32 get_loading_time() const { return loading_time; } // ms per full loading/unloading uint32 get_weight() const { return weight; } uint32 get_power() const { return power; } sint64 get_running_cost() const { return running_cost; } sint64 get_fixed_cost() const { return get_maintenance(); } sint8 get_sound() const { return sound; } /** * 64 = 1.00 * @return gear value */ uint16 get_gear() const { return gear; } /** * @return engine type * eletric engines require an electrified way to run */ uint8 get_engine_type() const { return engine_type; } /** * @return the vehicles length in 1/8 of the normal len */ uint8 get_length() const { return len; } uint32 get_length_in_steps() const { return get_length() * VEHICLE_STEPS_PER_CARUNIT; } void calc_checksum(checksum_t *chk) const; }; #endif simutrans-124.5/src/simutrans/descriptor/way_desc.cc000066400000000000000000000012701520675460500226630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "way_desc.h" #include "../network/checksum.h" waytype_t way_desc_t::get_finance_waytype() const { return get_styp() == type_tram ? tram_wt : get_wtyp(); } void way_desc_t::calc_checksum(checksum_t *chk) const { PAKSET_INFO("","obj=way"); obj_desc_transport_infrastructure_t::calc_checksum(chk); #if MSG_LEVEL>0 if(max_weight) PAKSET_INFO("payload=","%d",max_weight); if(styp) PAKSET_INFO("system_type=","%d",styp); if(draw_as_obj) PAKSET_INFO("draw_as_ding=1",""); PAKSET_INFO("--",""); #endif chk->input(max_weight); chk->input(styp); chk->input(has_double_slopes()); } simutrans-124.5/src/simutrans/descriptor/way_desc.h000066400000000000000000000125131520675460500225270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WAY_DESC_H #define DESCRIPTOR_WAY_DESC_H #include "image_list.h" #include "obj_base_desc.h" #include "skin_desc.h" #include "../dataobj/ribi.h" class checksum_t; class tool_t; /** * Way type description. Contains all needed values to describe a * way type in Simutrans. * * Child nodes: * 0 Name * 1 Copyright * 2 Images for flat ways (indexed by ribi) * 3 Images for slopes * 4 Images for straight diagonal ways * 5 Skin (cursor and icon) * if number_of_seasons == 0 (no winter images) * 6-8 front images of image lists 2-4 * else * 6-8 winter images of image lists 2-4 * 9-11 front images of image lists 2-4 * 12-14 front winter images of image lists 2-4 */ class way_desc_t : public obj_desc_transport_infrastructure_t { friend class way_reader_t; private: /** * Max weight */ uint32 max_weight; /** * Way system type: i.e. for wtyp == track this * can be used to select track system type (tramlike=7, elevated=1, ignore=255) */ uint8 styp; /* true, if a tile with this way should be always drawn as a thing */ uint8 draw_as_obj; /* number of seasons (0 = none, 1 = no snow/snow */ sint8 number_of_seasons; /// if true front_images lists exists as nodes bool front_images; bool clip_below; // only relevant for elevated ways /** * calculates index of image list for flat ways * for winter and/or front images * add +1 and +2 to get slope and straight diagonal images, respectively */ uint16 image_list_base_index(bool snow, bool front) const { if (number_of_seasons == 0 || !snow) { if (front && front_images) { return (number_of_seasons == 0) ? 6 : 9; } else { return 2; } } else { // winter images if (front && front_images) { return 12; } else { return 6; } } } public: /** * @return waytype used in finance stats (needed to distinguish \ * between train track and tram track */ waytype_t get_finance_waytype() const; /** * returns the system type of this way (mostly used with rails) * @see systemtype_t */ systemtype_t get_styp() const { return (systemtype_t)styp; } bool is_tram() const { return wtyp == track_wt && styp == type_tram; } bool is_clip_below() const { return clip_below; } image_id get_image_id(ribi_t::ribi ribi, uint8 season, bool front = false) const { if (front && !front_images) { return IMG_EMPTY; } const uint16 n = image_list_base_index(season, front); return get_child(n)->get_image_id(ribi); } image_id get_switch_image_id(ribi_t::ribi ribi, uint8 season, bool nw, bool front = false) const { if (front && !front_images) { return IMG_EMPTY; } const uint16 n = image_list_base_index(season, front); image_list_t const* const imglist = get_child(n); // only do this if extended switches are there if( imglist->get_count()>16 ) { static uint8 ribi_to_extra[16] = { 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 1, 255, 2, 3, 4 }; return imglist->get_image_id( ribi_to_extra[ribi]+16+(nw*5) ); } // else return standard values return imglist->get_image_id( ribi ); } image_id get_slope_image_id(slope_t::type slope, uint8 season, bool front = false) const { if (front && !front_images) { return IMG_EMPTY; } const uint16 n = image_list_base_index(season, front) + 1; uint16 nr; switch(slope) { case slope_t::north: nr = 0; break; case slope_t::west: nr = 1; break; case slope_t::east: nr = 2; break; case slope_t::south: nr = 3; break; case slope_t::north*2: nr = 4; break; case slope_t::west*2: nr = 5; break; case slope_t::east*2: nr = 6; break; case slope_t::south*2: nr = 7; break; default: return IMG_EMPTY; } image_id slope_img = get_child(n)->get_image_id(nr); if( nr > 3 && slope_img == IMG_EMPTY && get_child(n)->get_count()<=4 ) { // hack for old ways without double height images to use single slope images for both nr -= 4; slope_img = get_child(n)->get_image_id(nr); } return slope_img; } image_id get_diagonal_image_id(ribi_t::ribi ribi, uint8 season, bool front = false) const { if (front && !front_images) { return IMG_EMPTY; } const uint16 n = image_list_base_index(season, front) + 2; return get_child(n)->get_image_id(ribi / 3 - 1); } bool has_double_slopes() const { return get_child(3)->get_count() > 4 || get_child(image_list_base_index(false, true) + 1)->get_count() > 4; } bool has_diagonal_image() const { return get_child(4)->get_image_id(0) != IMG_EMPTY || get_child(image_list_base_index(false, true)+2)->get_image_id(0) != IMG_EMPTY; } bool has_switch_image() const { return get_child(2)->get_count() > 16 || get_child(image_list_base_index(false, true))->get_count() > 16; } /* true, if this tile is to be drawn as a normal thing */ bool is_draw_as_obj() const { return draw_as_obj; } /** * Skin: cursor (index 0) and icon (index 1) */ const skin_desc_t * get_cursor() const { return get_child(5); } void calc_checksum(checksum_t *chk) const; }; #endif simutrans-124.5/src/simutrans/descriptor/way_obj_desc.h000066400000000000000000000100441520675460500233560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WAY_OBJ_DESC_H #define DESCRIPTOR_WAY_OBJ_DESC_H #include "image_list.h" #include "obj_base_desc.h" #include "skin_desc.h" #include "../dataobj/ribi.h" #include "../network/checksum.h" class tool_t; class checksum_t; /** * Way objects type description (like overhead lines). * * Child nodes: * 0 Name * 1 Copyright * 2 Image on flat ways * 3 Image on sloped ways * 4 Image on diagonal ways * 5 Skin (cursor and icon) */ class way_obj_desc_t : public obj_desc_transport_infrastructure_t { friend class way_obj_reader_t; private: /** * Type of the object, only overheadlines_wt is currently used. */ uint8 own_wtyp; public: bool is_overhead_line() const { return (waytype_t)own_wtyp == overheadlines_wt; } // way objects can have a front and a backimage, unlike ways ... image_id get_front_image_id(ribi_t::ribi ribi) const { return get_child(2)->get_image_id(ribi); } image_id get_crossing_image_id(ribi_t::ribi ribi, bool nw, bool front = false) const { if( front && !get_child(2)->get_count() ) { return IMG_EMPTY; } image_list_t const* const imglist = get_child(3-front); // only do this if extended switches are there if( imglist->get_count()>16 ) { static uint8 ribi_to_extra[16] = { 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 1, 255, 2, 3, 4 }; return ribi < 16 ? imglist->get_image_id( ribi_to_extra[ribi]+16+(nw*5) ) : IMG_EMPTY; } // else return standard values return imglist->get_image_id( ribi ); } image_id get_back_image_id(ribi_t::ribi ribi) const { return get_child(3)->get_image_id(ribi); } image_id get_front_slope_image_id(slope_t::type slope) const { uint16 nr; switch(slope) { case 4: nr = 0; break; case 12: nr = 1; break; case 28: nr = 2; break; case 36: nr = 3; break; case 8: nr = 4; break; case 24: nr = 5; break; case 56: nr = 6; break; case 72: nr = 7; break; default: return IMG_EMPTY; } image_id slope_img = get_child(4)->get_image_id(nr); if( nr > 3 && slope_img == IMG_EMPTY ) { // hack for old ways without double height images to use single slope images for both nr -= 4; slope_img = get_child(4)->get_image_id(nr); } return slope_img; } image_id get_back_slope_image_id(slope_t::type slope) const { uint16 nr; switch(slope) { case 4: nr = 0; break; case 12: nr = 1; break; case 28: nr = 2; break; case 36: nr = 3; break; case 8: nr = 4; break; case 24: nr = 5; break; case 56: nr = 6; break; case 72: nr = 7; break; default: return IMG_EMPTY; } image_id slope_img = get_child(5)->get_image_id(nr); if( nr > 3 && slope_img == IMG_EMPTY ) { // hack for old ways without double height images to use single slope images for both nr -= 4; slope_img = get_child(5)->get_image_id(nr); } return slope_img; } image_id get_front_diagonal_image_id(ribi_t::ribi ribi) const { if(!ribi_t::is_bend(ribi)) { return IMG_EMPTY; } return get_child(6)->get_image_id(ribi / 3 - 1); } image_id get_back_diagonal_image_id(ribi_t::ribi ribi) const { if(!ribi_t::is_bend(ribi)) { return IMG_EMPTY; } return get_child(7)->get_image_id(ribi / 3 - 1); } bool has_diagonal_image() const { if (get_child(4)->get_image(0)) { // has diagonal fontimage return true; } if (get_child(5)->get_image(0)) { // or diagonal back image return true; } return false; } /** * Skin: cursor (index 0) and icon (index 1) */ skin_desc_t const* get_cursor() const { return get_child(8); } void calc_checksum(checksum_t *chk) const { obj_desc_transport_infrastructure_t::calc_checksum(chk); chk->input(own_wtyp); } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/000077500000000000000000000000001520675460500220725ustar00rootroot00000000000000simutrans-124.5/src/simutrans/descriptor/writer/bridge_writer.cc000066400000000000000000000121701520675460500252320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "obj_pak_exception.h" #include "../bridge_desc.h" #include "text_writer.h" #include "imagelist_writer.h" #include "skin_writer.h" #include "get_waytype.h" #include "bridge_writer.h" #include static void write_bridge_images(FILE* outfp, obj_node_t& node, tabfileobj_t& obj, int season) { slist_tpl backkeys; slist_tpl frontkeys; static const char* const names[] = { "image", "ns", "ew", NULL, "start", "n", "s", "e", "w", NULL, "ramp", "n", "s", "e", "w", NULL, "pillar", "s", "w", NULL, "image2", "ns", "ew", NULL, "start2", "n", "s", "e", "w", NULL, "ramp2", "n", "s", "e", "w", NULL, "pillar2", "s", "w", NULL, NULL }; const char* const * ptr = names; const char* keyname = *ptr++; char keybuf[40]; do { const char* keyindex = *ptr++; do { std::string value; if( season < 0 ) { sprintf( keybuf, "back%s[%s]", keyname, keyindex ); value = obj.get( keybuf ); backkeys.append( value ); //intf("BACK: %s -> %s\n", keybuf, value.chars()); sprintf( keybuf, "front%s[%s]", keyname, keyindex ); } else { sprintf( keybuf, "back%s[%s][%d]", keyname, keyindex, season ); value = obj.get( keybuf ); backkeys.append( value ); //intf("BACK: %s -> %s\n", keybuf, value.chars()); sprintf( keybuf, "front%s[%s][%d]", keyname, keyindex, season ); } // must append to front keys even if empty to keep order correct (but warn anyway) value = obj.get( keybuf ); frontkeys.append( value ); //intf("FRNT: %s -> %s\n", keybuf, value.chars()); if( value.size() <= 2 ) { dbg->warning( obj_writer_t::last_name, "No %s specified (might still work)", keybuf ); } keyindex = *ptr++; } while( keyindex ); keyname = *ptr++; } while( keyname ); imagelist_writer_t::instance()->write_obj( outfp, node, backkeys ); imagelist_writer_t::instance()->write_obj( outfp, node, frontkeys ); if( season <= 0 ) { slist_tpl cursorkeys; cursorkeys.append( std::string( obj.get("cursor") ) ); cursorkeys.append( std::string( obj.get("icon") ) ); cursorskin_writer_t::instance()->write_obj( outfp, node, obj, cursorkeys ); cursorkeys.clear(); } backkeys.clear(); frontkeys.clear(); } void bridge_writer_t::write_obj(FILE *outfp, obj_node_t &parent, tabfileobj_t &obj) { const waytype_t waytype = get_waytype(obj.get("waytype")); const uint16 topspeed = obj.get_int("topspeed", 999); const sint64 price = obj.get_int64("cost", 0); const sint64 maintenance = obj.get_int64("maintenance", 1000); const uint8 pillars_every = obj.get_int_clamped("pillar_distance", 0, 0, UINT8_MAX); // distance==0 is off const uint8 pillar_asymmetric = obj.get_int_clamped("pillar_asymmetric", 0, 0, 1); // middle of tile uint8 max_length = obj.get_int_clamped("max_lenght", 0, 0, UINT8_MAX); // max_lenght==0: unlimited max_length = obj.get_int_clamped("max_length", max_length, 0, UINT8_MAX); // with correct spelling const uint8 max_height = obj.get_int_clamped("max_height", 0, 0, UINT8_MAX); // max_height==0: unlimited const uint16 axle_load = obj.get_int_clamped("axle_load", 9999, 0, UINT16_MAX); const uint8 clip_below = obj.get_int_clamped("clip_below", waytype!=powerline_wt, 0, 1); // clip ground below by default if not power bridge // timeline const uint16 intro_date = 12*obj.get_int_clamped("intro_year", DEFAULT_INTRO_YEAR, 0, INT32_MAX) + obj.get_int_clamped("intro_month", 1, 1, 12) - 1; const uint16 retire_date = 12*obj.get_int_clamped("retire_year", DEFAULT_RETIRE_YEAR, 0, INT32_MAX) + obj.get_int_clamped("retire_month", 1, 1, 12) - 1; obj_node_t node(this, 33, &parent); node.write_version(outfp, 11); node.write_uint16(outfp, topspeed); node.write_sint64(outfp, price); node.write_sint64(outfp, maintenance); node.write_uint8 (outfp, waytype); node.write_uint8 (outfp, pillars_every); node.write_uint8 (outfp, max_length); node.write_uint16(outfp, intro_date); node.write_uint16(outfp, retire_date); node.write_uint8 (outfp, pillar_asymmetric); node.write_uint16(outfp, axle_load); node.write_uint8 (outfp, max_height); node.write_uint8 (outfp, clip_below); char keybuf[40]; std::string str = obj.get("backimage[ns][0]"); sint8 number_of_seasons = 0; if (str.empty()) { node.write_uint8(outfp, number_of_seasons); write_name_and_copyright(outfp, node, obj); write_bridge_images( outfp, node, obj, -1 ); } else { while(number_of_seasons < 2) { sprintf(keybuf, "backimage[ns][%d]", number_of_seasons+1); std::string str = obj.get(keybuf); if (!str.empty()) { number_of_seasons++; } else { break; } } node.write_uint8(outfp, number_of_seasons); write_name_and_copyright(outfp, node, obj); for(uint8 season = 0 ; season <= number_of_seasons ; season++) { write_bridge_images( outfp, node, obj, season ); } } node.check_and_write_header(outfp); } simutrans-124.5/src/simutrans/descriptor/writer/bridge_writer.h000066400000000000000000000013671520675460500251020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_BRIDGE_WRITER_H #define DESCRIPTOR_WRITER_BRIDGE_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class bridge_writer_t : public obj_writer_t { private: static bridge_writer_t the_instance; bridge_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: /// Writes bridge node data to file void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_bridge; } const char* get_type_name() const OVERRIDE { return "bridge"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/building_writer.cc000066400000000000000000000274111520675460500255770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../utils/simstring.h" #include "../../dataobj/tabfile.h" #include "../building_desc.h" #include "obj_pak_exception.h" #include "obj_node.h" #include "text_writer.h" #include "imagelist2d_writer.h" #include "get_waytype.h" #include "get_climate.h" #include "building_writer.h" #include "skin_writer.h" using std::string; void tile_writer_t::write_obj(FILE* fp, obj_node_t& parent, int index, int seasons, slist_tpl > >& backkeys, slist_tpl > >& frontkeys ) { obj_node_t node(this, 7, &parent); uint8 phases = 0; for (int i = 0; i < seasons; i++) { for(slist_tpl const& s : backkeys.at(i)) { if (phases < s.get_count()) { phases = s.get_count(); } } for(slist_tpl const& s : frontkeys.at(i)) { if (phases < s.get_count()) { phases = s.get_count(); } } } for (int i = 0; i < seasons; i++) { imagelist2d_writer_t::instance()->write_obj(fp, node, backkeys.at(i)); imagelist2d_writer_t::instance()->write_obj(fp, node, frontkeys.at(i)); } // write version data node.write_version(fp, 2); node.write_uint16(fp, (uint16)phases); node.write_uint16(fp, (uint16)index); node.write_uint8 (fp, (uint8)seasons); node.check_and_write_header(fp); } // Subroutine for write_obj, to avoid duplicated code static uint32 get_cluster_data(tabfileobj_t& obj) { uint32 clusters = 0; vector_tpl ints = obj.get_ints("clusters"); for( uint32 i = 0; i < ints.get_count(); i++ ) { if( ints[i] >= 1 && ints[i] <= 32 ) { // Sanity check clusters |= 1<<(ints[i]-1); } } return clusters; } void building_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { // take care, hardcoded size of node on disc here! obj_node_t node(this, 47, &parent); write_name_and_copyright(fp, node, obj); koord size(1, 1); uint8 layouts = 0; vector_tpl ints = obj.get_ints("dims"); switch (ints.get_count()) { default: case 3: layouts = ints[2]; /* FALLTHROUGH */ case 2: size.y = ints[1]; /* FALLTHROUGH */ case 1: size.x = ints[0]; /* FALLTHROUGH */ case 0: break; } if (layouts == 0) { layouts = size.x == size.y ? 1 : 2; } if (size.x*size.y == 0) { dbg->fatal("building_writer_t::write_obj", "Cannot create a building with zero size (%i,%i)", size.x, size.y); } building_desc_t::btype type = building_desc_t::unknown; uint32 extra_data = 0; uint8 enables = 0; uint16 level = obj.get_int("level", 1) - 1; // get the allowed area for this building climate_bits allowed_climates = all_but_water_climate; // all but water const char* climate_str = obj.get("climates"); if (climate_str && strlen(climate_str) > 4) { allowed_climates = get_climate_bits(climate_str); } building_desc_t::flag_t const flags = (obj.get_int("noinfo", 0) > 0 ? building_desc_t::FLAG_NO_INFO : building_desc_t::FLAG_NULL) | (obj.get_int("noconstruction", 0) > 0 ? building_desc_t::FLAG_NO_PIT : building_desc_t::FLAG_NULL) | (obj.get_int("needs_ground", 0) > 0 ? building_desc_t::FLAG_NEED_GROUND : building_desc_t::FLAG_NULL); uint16 const animation_time = obj.get_int("animation_time", 300); const char* type_name = obj.get("type"); if (!STRICMP(type_name, "res")) { extra_data = get_cluster_data(obj); type = building_desc_t::city_res; } else if (!STRICMP(type_name, "com")) { extra_data = get_cluster_data(obj); type = building_desc_t::city_com; } else if (!STRICMP(type_name, "ind")) { extra_data = get_cluster_data(obj); type = building_desc_t::city_ind; } else if (!STRICMP(type_name, "cur")) { extra_data = obj.get_int("build_time", 0); level = obj.get_int("passengers", level); type = extra_data == 0 ? building_desc_t::attraction_land : building_desc_t::attraction_city; } else if (!STRICMP(type_name, "mon")) { type = building_desc_t::monument; level = obj.get_int("passengers", level); } else if (!STRICMP(type_name, "tow")) { level = obj.get_int("passengers", level); extra_data = obj.get_int("build_time", 0); type = building_desc_t::townhall; } else if (!STRICMP(type_name, "hq")) { level = obj.get_int("passengers", level); extra_data = obj.get_int("hq_level", 0); type = building_desc_t::headquarters; } else if (!STRICMP(type_name, "habour") || !STRICMP(type_name, "harbour")) { // buildable only on sloped shores type = building_desc_t::dock; extra_data = water_wt; } else if (!STRICMP(type_name, "dock")) { // buildable only on flat shores type = building_desc_t::flat_dock; extra_data = water_wt; } else if (!STRICMP(type_name, "fac")) { type = building_desc_t::factory; enables |= 4; } else if (!STRICMP(type_name, "stop")) { type = building_desc_t::generic_stop; extra_data = get_waytype(obj.get("waytype")); } else if (!STRICMP(type_name, "extension")) { type = building_desc_t::generic_extension; const char *wt = obj.get("waytype"); if(wt && *wt>' ') { // no waytype => just a generic extension that fits all extra_data = get_waytype(wt); } } else if (!STRICMP(type_name, "depot")) { type = building_desc_t::depot; extra_data = get_waytype(obj.get("waytype")); } else if (!STRICMP(type_name, "any") || *type_name == '\0') { // for instance "MonorailGround" type = building_desc_t::others; } else if ( !STRICMP(type_name, "station") || !STRICMP(type_name, "railstop") || !STRICMP(type_name, "monorailstop") || !STRICMP(type_name, "busstop") || !STRICMP(type_name, "carstop") || !STRICMP(type_name, "airport") || !STRICMP(type_name, "wharf") ) { dbg->fatal("building_writer_t::write_obj()","%s is obsolete type for %s; use stop/extension and waytype!", type_name, obj.get("name") ); } else if (!STRICMP(type_name, "hall") || !STRICMP(type_name, "post") || !STRICMP(type_name, "shed") ) { dbg->fatal("building_writer_t::write_obj()","%s is obsolete type for %s; use extension and waytype!", type_name, obj.get("name") ); } else { dbg->fatal( "building_writer_t::write_obj()","%s is obsolete type for %s", type_name, obj.get("name") ); } // is is an station extension building? if (obj.get_int("extension_building", 0) > 0) { dbg->fatal("building_writer_t::write_obj()","extension_building is obsolete keyword for %s; use stop/extension and waytype!", obj.get("name") ); } if (obj.get_int("enables_pax", 0) > 0) { enables |= 1; } if (obj.get_int("enables_post", 0) > 0) { enables |= 2; } if( type == building_desc_t::factory || obj.get_int("enables_ware", 0) > 0 ) { enables |= 4; } if( type == building_desc_t::generic_extension || type == building_desc_t::generic_stop || type == building_desc_t::dock || type == building_desc_t::depot || type == building_desc_t::factory ) { // since level was reduced by one beforehand ... ++level; } // read chance - default is 100% chance to be built uint8 const chance = obj.get_int("chance", 100); // timeline for buildings uint16 const intro_date = obj.get_int("intro_year", DEFAULT_INTRO_YEAR) * 12 + obj.get_int("intro_month", 1) - 1; uint16 const retire_date = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12 + obj.get_int("retire_month", 1) - 1; uint16 const preservation_date = obj.get_int("preservation_year", DEFAULT_RETIRE_YEAR) * 12 + obj.get_int("preservation_month", 1) - 1; // capacity and price information. // Stands in place of the "level" setting, but uses "level" data by default. //NOTE: Default for maintenance and price must be set when loading so use magic default here //also check for "station_xx" for experimental compatibility sint32 capacity = obj.get_int("capacity", level * 32); if( capacity == level * 32 ) { capacity = obj.get_int("station_capacity", level * 32); } sint64 maintenance = obj.get_int64("maintenance", PRICE_MAGIC); if( maintenance == PRICE_MAGIC ) { maintenance = obj.get_int64("station_maintenance", PRICE_MAGIC); } sint64 price = obj.get_int64("cost", PRICE_MAGIC); if( price == PRICE_MAGIC ) { price = obj.get_int64("station_price", PRICE_MAGIC); } uint8 allow_underground = obj.get_int("allow_underground", 2); if(allow_underground > 2) { // Prohibit illegal values here. allow_underground = 2; } // scan for most number of seasons int seasons = 1; for (int l = 0; l < layouts; l++) { // each layout int const h = l & 1 ? size.x : size.y; int const w = l & 1 ? size.y : size.x; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { // each tile for (int pos = 0; pos < 2; pos++) { for (int season = seasons; season < 12; season++) { char buf[256]; sprintf(buf, "%simage[%d][%d][%d][%d][%d][%d]", pos ? "back" : "front", l, y, x, 0, 0, season); string str = obj.get(buf); if (str.size() != 0) { seasons = season + 1; } else { break; } } } } } } int tile_index = 0; for (int l = 0; l < layouts; l++) { // each layout int const h = l & 1 ? size.x : size.y; int const w = l & 1 ? size.y : size.x; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { // each tile slist_tpl > > backkeys; slist_tpl > > frontkeys; for (int season = 0; season < seasons; season++) { backkeys.append(); frontkeys.append(); for (int pos = 0; pos < 2; pos++) { slist_tpl > >& keys = pos ? backkeys : frontkeys; for (unsigned int h = 0; ; h++) { // each height for (int phase = 0; ; phase++) { // each animation char buf[256]; sprintf(buf, "%simage[%d][%d][%d][%d][%d][%d]", pos ? "back" : "front", l, y, x, h, phase, season); string str = obj.get(buf); // if no string check to see whether using format without seasons parameter if (str.empty() && seasons == 1) { sprintf(buf, "%simage[%d][%d][%d][%d][%d]", pos ? "back" : "front", l, y, x, h, phase); str = obj.get(buf); } if (str.empty()) { #if 0 printf("Not found: %s\n", buf); fflush(NULL); #endif break; } else { // no higher front images if (h > 0 && pos == 0) { dbg->error( obj_writer_t::last_name, "Frontimage height MUST be one tile only!"); break; } } if (phase == 0) { keys.at(season).append(); } keys.at(season).at(h).append(str); } if (keys.at(season).get_count() <= h) { break; } } } } tile_writer_t::instance()->write_obj(fp, node, tile_index++, seasons, backkeys, frontkeys); } } } // write version data node.write_version(fp, 11); // write desc data node.write_uint8 (fp, 0); // was gtyp node.write_uint8 (fp, type); node.write_uint16(fp, level); node.write_uint32(fp, extra_data); node.write_uint16(fp, size.x); node.write_uint16(fp, size.y); node.write_uint8 (fp, layouts); node.write_uint16(fp, allowed_climates); node.write_uint8 (fp, enables); node.write_uint8 (fp, flags); node.write_uint8 (fp, chance); node.write_uint16(fp, intro_date); node.write_uint16(fp, retire_date); node.write_uint16(fp, animation_time); node.write_uint16(fp, capacity); node.write_sint64(fp, maintenance); node.write_sint64(fp, price); node.write_uint8 (fp, allow_underground); node.write_uint16(fp, preservation_date); // probably add some icons, if defined slist_tpl cursorkeys; string c = string(obj.get("cursor")); string i = string(obj.get("icon")); cursorkeys.append(c); cursorkeys.append(i); if (!c.empty() || !i.empty()) { cursorskin_writer_t::instance()->write_obj(fp, node, obj, cursorkeys); } node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/building_writer.h000066400000000000000000000025411520675460500254360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_BUILDING_WRITER_H #define DESCRIPTOR_WRITER_BUILDING_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" template class slist_tpl; class tile_writer_t : public obj_writer_t { private: static tile_writer_t the_instance; tile_writer_t() { register_writer(false); } public: static tile_writer_t *instance() { return &the_instance; } virtual void write_obj(FILE* fp, obj_node_t &parent, int index, int seasons, slist_tpl > >& backkeys, slist_tpl > >& frontkeys ); protected: obj_type get_type() const OVERRIDE { return obj_tile; } const char *get_type_name() const OVERRIDE { return "tile"; } }; class building_writer_t : public obj_writer_t { private: static building_writer_t the_instance; building_writer_t() { register_writer(true); } public: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } static building_writer_t* instance() { return &the_instance; } virtual void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_building; } const char* get_type_name() const OVERRIDE { return "building"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/citycar_writer.cc000066400000000000000000000026111520675460500254330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "text_writer.h" #include "imagelist_writer.h" #include "citycar_writer.h" void citycar_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { int i; uint16 const dist_weight = obj.get_int("distributionweight", 1); uint16 const intro_date = obj.get_int("intro_year", DEFAULT_INTRO_YEAR) * 12 + obj.get_int("intro_month", 1) - 1; uint16 const retire_date = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12 + obj.get_int("retire_month", 1) - 1; uint16 const topspeed = obj.get_int("speed", 80) * 16; obj_node_t node(this, 10, &parent); // new version with intro and obsolete dates node.write_version(fp, 2); // version information node.write_uint16(fp, dist_weight); node.write_uint16(fp, topspeed); node.write_uint16(fp, intro_date); node.write_uint16(fp, retire_date); write_name_and_copyright(fp, node, obj); static const char* const dir_codes[] = { "s", "w", "sw", "se", "n", "e", "ne", "nw" }; slist_tpl keys; std::string str; for (i = 0; i < 8; i++) { char buf[40]; sprintf(buf, "image[%s]", dir_codes[i]); str = obj.get(buf); keys.append(str); } imagelist_writer_t::instance()->write_obj(fp, node, keys); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/citycar_writer.h000066400000000000000000000014231520675460500252750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_CITYCAR_WRITER_H #define DESCRIPTOR_WRITER_CITYCAR_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" /** * Description of the automatically generated cars */ class citycar_writer_t : public obj_writer_t { private: static citycar_writer_t the_instance; citycar_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_citycar; } const char* get_type_name() const OVERRIDE { return "citycar"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/crossing_writer.cc000066400000000000000000000107741520675460500256350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "../../utils/simstring.h" #include "../../dataobj/tabfile.h" #include "../sound_desc.h" #include "obj_node.h" #include "obj_pak_exception.h" #include "text_writer.h" #include "image_writer.h" #include "get_waytype.h" #include "imagelist_writer.h" #include "crossing_writer.h" #include "xref_writer.h" using std::string; static void make_list(tabfileobj_t &obj, slist_tpl& list, char const* const key) { for (int i = 0;; ++i) { char buf[40]; sprintf(buf, "%s[%i]", key, i); string str(obj.get(buf)); if (str.empty()) { break; } // We have this direction list.append(str); } } static void write_list(FILE* const fp, obj_node_t& node, slist_tpl const& list) { if (list.empty()) { xref_writer_t::instance()->write_obj(fp, node, obj_imagelist, "", false); } else { imagelist_writer_t::instance()->write_obj(fp, node, list); } } void crossing_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { int total_len = 21; // must be done here, since it may affect the len of the header! string sound_str = ltrim( obj.get("sound") ); sint8 sound_id=NO_SOUND; if (!sound_str.empty()) { // ok, there is some sound sound_id = atoi(sound_str.c_str()); if (sound_id == 0 && sound_str[0] == '0') { sound_id = 0; sound_str = ""; } else if (sound_id != 0) { // old style id sound_str = ""; } if (!sound_str.empty()) { sound_id = LOAD_SOUND; total_len += sound_str.size() + 1; } } // ok, node can be allocated now obj_node_t node(this, total_len, &parent); write_name_and_copyright(fp, node, obj); node.write_version(fp, 2); // waytypes, waytype 2 will be on top uint8 waytype1 = get_waytype(obj.get("waytype[0]")); uint8 waytype2 = get_waytype(obj.get("waytype[1]")); if(waytype1==waytype2) { dbg->fatal( "Crossing", "Identical ways (%s) cannot cross (check waytypes)!", obj.get("waytype[0]") ); } node.write_uint8(fp, waytype1); node.write_uint8(fp, waytype2); // Top speed of this way const uint16 speed0 = obj.get_int("speed[0]", 0); const uint16 speed1 = obj.get_int("speed[1]", 0); if(speed0==0 || speed1 == 0) { dbg->fatal( "Crossing", "A maxspeed MUST be given for both ways!"); } node.write_uint16(fp, speed0); node.write_uint16(fp, speed1); // time between frames for animation uint32 uv32 = obj.get_int("animation_time_open", 0); node.write_uint32(fp, uv32); uv32 = obj.get_int("animation_time_closed", 0); node.write_uint32(fp, uv32); node.write_uint8(fp, sound_id); if (!sound_str.empty()) { sint8 sv8 = sound_str.size(); node.write_bytes(fp, sizeof(sint8), &sv8); node.write_bytes(fp, sound_str.size(), sound_str.c_str()); } uint16 intro_date = obj.get_int("intro_year", DEFAULT_INTRO_YEAR) * 12; intro_date += obj.get_int("intro_month", 1) - 1; uint16 retire_date = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12; retire_date += obj.get_int("retire_month", 1) - 1; node.write_uint16(fp, intro_date); node.write_uint16(fp, retire_date); // now the image stuff slist_tpl openkeys_ns; slist_tpl openkeys_ew; slist_tpl front_openkeys_ns; slist_tpl front_openkeys_ew; slist_tpl closekeys_ns; slist_tpl closekeys_ew; slist_tpl front_closekeys_ns; slist_tpl front_closekeys_ew; // open crossings ... make_list(obj, openkeys_ns, "openimage[ns]"); make_list(obj, openkeys_ew, "openimage[ew]"); // these must exists! if (openkeys_ns.empty() || openkeys_ew.empty()) { dbg->fatal( "Crossing", "Missing images (at least one openimage! (but %i and %i found)!)", openkeys_ns.get_count(), openkeys_ew.get_count() ); } write_list(fp, node, openkeys_ns); write_list(fp, node, openkeys_ew); // foreground make_list(obj, front_openkeys_ns, "front_openimage[ns]"); make_list(obj, front_openkeys_ew, "front_openimage[ew]"); // the following lists are optional write_list(fp, node, front_openkeys_ns); write_list(fp, node, front_openkeys_ew); // closed crossings ... make_list(obj, closekeys_ns, "closedimage[ns]"); make_list(obj, closekeys_ew, "closedimage[ew]"); write_list(fp, node, closekeys_ns); write_list(fp, node, closekeys_ew); // foreground make_list(obj, front_closekeys_ns, "front_closedimage[ns]"); make_list(obj, front_closekeys_ew, "front_closedimage[ew]"); write_list(fp, node, front_closekeys_ns); write_list(fp, node, front_closekeys_ew); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/crossing_writer.h000066400000000000000000000013371520675460500254720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_CROSSING_WRITER_H #define DESCRIPTOR_WRITER_CROSSING_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class crossing_writer_t : public obj_writer_t { private: static crossing_writer_t the_instance; crossing_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE *fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE *fp, obj_node_t &parent, tabfileobj_t &obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_crossing; } const char *get_type_name() const OVERRIDE { return "crossing"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/factory_writer.cc000066400000000000000000000303061520675460500254460ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../utils/simstring.h" #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "../skin_desc.h" #include "../factory_desc.h" #include "../sound_desc.h" #include "text_writer.h" #include "building_writer.h" #include "factory_writer.h" #include "xref_writer.h" using std::string; void factory_field_class_writer_t::write_obj(FILE* outfp, obj_node_t& parent, const char* field_name, int snow_image, int production, int capacity, int weight) { obj_node_t node(this, 9, &parent); xref_writer_t::instance()->write_obj(outfp, node, obj_field, field_name, true); // data specific to each field class node.write_version(outfp, 1); node.write_uint8 (outfp, snow_image); node.write_uint16(outfp, production); node.write_uint16(outfp, capacity); node.write_uint16(outfp, weight); node.check_and_write_header(outfp); } void factory_field_group_writer_t::write_obj(FILE* outfp, obj_node_t& parent, tabfileobj_t& obj) { obj_node_t node(this, 12, &parent); uint16 field_classes; if( *obj.get("fields") ) { // format with no square-bracketed subscripts field_classes = 1; const char *field_name = obj.get("fields"); int snow_image = obj.get_int("has_snow", 1); int production = obj.get_int("production_per_field", 16); int capacity = obj.get_int("storage_capacity", 0); // default is 0 to avoid breaking the balance of existing pakset objects int weight = obj.get_int("spawn_weight", 1000); factory_field_class_writer_t::instance()->write_obj(outfp, node, field_name, snow_image, production, capacity, weight); } else { // for each field class, retrieve its data and write a field class node for (field_classes = 0;; ++field_classes) { char buf[64]; sprintf(buf, "fields[%d]", field_classes); const char *field_name = obj.get(buf); if( !field_name || !*field_name ) { break; } sprintf(buf, "has_snow[%d]", field_classes); int snow_image = obj.get_int(buf, 1); sprintf(buf, "production_per_field[%d]", field_classes); int production = obj.get_int(buf, 16); sprintf(buf, "storage_capacity[%d]", field_classes); int capacity = obj.get_int(buf, 0); // default is 0 to avoid breaking the balance of existing pakset objects sprintf(buf, "spawn_weight[%d]", field_classes); int weight = obj.get_int(buf, 1000); factory_field_class_writer_t::instance()->write_obj(outfp, node, field_name, snow_image, production, capacity, weight); } } // common, shared field data uint16 probability = obj.get_int("probability_to_spawn", 10); // 0,1 % uint16 const max_fields = obj.get_int("max_fields", 25); uint16 const min_fields = obj.get_int("min_fields", 5); uint16 const start_fields = obj.get_int("start_fields", 5); if (probability >= 10000) { printf("probability_to_spawn too large, set to 10,000\n"); probability = 10000; } node.write_version(outfp, 3); // version node.write_uint16(outfp, probability); node.write_uint16(outfp, max_fields); node.write_uint16(outfp, min_fields); node.write_uint16(outfp, start_fields); node.write_uint16(outfp, field_classes); node.check_and_write_header(outfp); } void factory_smoke_writer_t::write_obj(FILE* outfp, obj_node_t& parent, tabfileobj_t& obj) { obj_node_t node(this, 10, &parent); xref_writer_t::instance()->write_obj(outfp, node, obj_smoke, obj.get("smoke"), true); koord const pos_off = obj.get_koord("smoketile", koord(0, 0)); koord const xy_off = obj.get_koord("smokeoffset", koord(0, 0)); sint16 const smokespeed = 0; /* was obj.get_int("smokespeed", 0); */ node.write_sint16(outfp, pos_off.x); node.write_sint16(outfp, pos_off.y); node.write_sint16(outfp, xy_off.x); node.write_sint16(outfp, xy_off.y); node.write_sint16(outfp, smokespeed); node.check_and_write_header(outfp); } void factory_product_writer_t::write_obj(FILE* outfp, obj_node_t& parent, int capacity, int factor, const char* warename) { obj_node_t node(this, sizeof(uint16) * 3, &parent); xref_writer_t::instance()->write_obj(outfp, node, obj_good, warename, true); // Version needs high bit set as trigger -> this is required // as marker because formerly nodes were unversioned // new version 2: pax-level added node.write_version(outfp, 1); node.write_uint16(outfp, capacity); node.write_uint16(outfp, factor); node.check_and_write_header(outfp); } void factory_supplier_writer_t::write_obj(FILE* outfp, obj_node_t& parent, int capacity, int count, int consumption, const char* warename) { obj_node_t node(this, 8, &parent); xref_writer_t::instance()->write_obj(outfp, node, obj_good, warename, true); node.write_uint16(outfp, capacity); node.write_uint16(outfp, count); node.write_uint16(outfp, consumption); node.write_uint16(outfp, 0); //dummy, unused (and uninitialized in past versions) node.check_and_write_header(outfp); } void factory_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { char const* const placing = obj.get("location"); factory_desc_t::site_t const placement = !STRICMP(placing, "land") ? factory_desc_t::Land : !STRICMP(placing, "water") ? factory_desc_t::Water : !STRICMP(placing, "city") ? factory_desc_t::City : !STRICMP(placing, "river") ? factory_desc_t::river : !STRICMP(placing, "shore") ? factory_desc_t::shore : !STRICMP(placing, "forest")? factory_desc_t::forest : factory_desc_t::Land; uint16 const productivity = obj.get_int("productivity", 10); uint16 const range = obj.get_int("range", 10); uint16 const chance = obj.get_int("distributionweight", 1); uint8 const color = obj.get_color("mapcolor", 255); if (color == 255) { dbg->fatal( "Factory", "%s missing an identification color! (mapcolor)", obj_writer_t::last_name ); } uint16 const pax_level = obj.get_int("pax_level", 12); uint16 expand_probability = obj.get_int("expand_probability", 0); if (expand_probability >= 10000) { printf("expand_probability too large, set to 10,000\n"); expand_probability = 10000; } uint16 const expand_minimum = obj.get_int("expand_minimum", 0); uint16 const expand_range = obj.get_int("expand_range", 0); uint16 const expand_times = obj.get_int("expand_times", 0); uint8 const electricity_producer = obj.get_int("electricity_producer", 0xFF); uint16 const electric_boost = (obj.get_int("electricity_boost", 1000) * 256 + 500) / 1000; uint16 const pax_boost = (obj.get_int("passenger_boost", 0) * 256 + 500) / 1000; uint16 const mail_boost = (obj.get_int("mail_boost", 0) * 256 + 500) / 1000; uint16 electric_demand = obj.get_int("electricity_amount", 65535); electric_demand = obj.get_int("electricity_demand", electric_demand); uint16 const pax_demand = obj.get_int("passenger_demand", 65535); uint16 const mail_demand = obj.get_int("mail_demand", 65535); // how long between sounds uint32 const sound_interval = obj.get_int("sound_interval", 0xFFFFFFFFul ); uint16 total_len = 81; // must be done here, since it may affect the len of the header! string sound_str = ltrim( obj.get("sound") ); sint8 sound_id=NO_SOUND; if (!sound_str.empty()) { // ok, there is some sound sound_id = atoi(sound_str.c_str()); if (sound_id == 0 && sound_str[0] == '0') { sound_id = 0; sound_str = ""; } else if (sound_id != 0) { // old style id sound_str = ""; } if (!sound_str.empty()) { sound_id = LOAD_SOUND; total_len += sound_str.size() + 1; } } obj_node_t node(this, total_len, &parent); obj.put("type", "fac"); // name/copyright is in building - write_head(fp, node, obj); building_writer_t::instance()->write_obj(fp, node, obj); if (*obj.get("smoke")) { factory_smoke_writer_t::instance()->write_obj(fp, node, obj); } else { xref_writer_t::instance()->write_obj(fp, node, obj_smoke, "", false); } uint16 supplier_count; for (supplier_count = 0;; ++supplier_count) { char buf[40]; sprintf(buf, "inputgood[%d]", supplier_count); const char* good = obj.get(buf); if (!good || !*good) { break; } sprintf(buf, "inputsupplier[%d]", supplier_count); int supp = obj.get_int(buf, 0); sprintf(buf, "inputcapacity[%d]", supplier_count); int cap = obj.get_int(buf, 0); sprintf(buf, "inputfactor[%d]", supplier_count); int consumption = (obj.get_int(buf, 100) * 256) / 100; factory_supplier_writer_t::instance()->write_obj(fp, node, cap, supp, consumption, good); } uint16 product_count; for (product_count = 0;; ++product_count) { char buf[40]; sprintf(buf, "outputgood[%d]", product_count); const char* good = obj.get(buf); if (!good || !*good) { break; } sprintf(buf, "outputcapacity[%d]", product_count); int cap = obj.get_int(buf, 0); if( cap<11 ) { dbg->error( "factory_writer_t::write_obj()", "Factory outputcapacity must be larger than 10! (currently %i)", cap ); } sprintf(buf, "outputfactor[%d]", product_count); int fac = (obj.get_int(buf, 100) * 256) / 100; factory_product_writer_t::instance()->write_obj(fp, node, cap, fac, good); } // fields (careful, are xref'ed) uint8 fields = 0; if( *obj.get("fields") || *obj.get("fields[0]") ) { // at least one field class available fields = 1; factory_field_group_writer_t::instance()->write_obj(fp, node, obj); } // now used here instead with the smoke koord pos_off[ 4 ]; koord xy_off[ 4 ]; uint8 num_smoke_offsets = 0; sint16 const smokeuplift = obj.get_int("smokeuplift", DEFAULT_SMOKE_UPLIFT ); sint16 const smokelifetime = obj.get_int("smokelifetime", DEFAULT_FACTORYSMOKE_TIME ); char str_smoketile[] = "smoketile[0]"; char str_smokeoffset[] = "smokeoffset[0]"; if( *obj.get( str_smoketile ) ) { for( int i = 0; i < 4; i++ ) { str_smoketile[10] = '0' + i; str_smokeoffset[12] = '0' + i; if( !*obj.get( str_smoketile ) ) { break; } pos_off[ i ] = obj.get_koord( str_smoketile, koord( 0, 0 ) ); if( !*obj.get( str_smokeoffset ) ) { dbg->error( "factory_writer_t::write_obj", "%s defined but not %s!", str_smoketile, str_smokeoffset ); } xy_off[ i ] = obj.get_koord( str_smokeoffset, koord( 0, 0 ) ); num_smoke_offsets++; } } else { pos_off[0] = obj.get_koord( "smoketile", koord( 0, 0 ) ); xy_off[0] = obj.get_koord( "smokeoffset", koord( 0, 0 ) ); } // new version with pax_level, and smoke offsets part of factory node.write_version(fp, 6); node.write_uint16(fp, placement); node.write_uint16(fp, productivity); node.write_uint16(fp, range); node.write_uint16(fp, chance); node.write_uint8 (fp, color); node.write_uint8 (fp, fields); node.write_uint16(fp, supplier_count); node.write_uint16(fp, product_count); node.write_uint16(fp, pax_level); node.write_uint16(fp, expand_probability); node.write_uint16(fp, expand_minimum); node.write_uint16(fp, expand_range); node.write_uint16(fp, expand_times); node.write_uint16(fp, electric_boost); node.write_uint16(fp, pax_boost); node.write_uint16(fp, mail_boost); node.write_uint16(fp, electric_demand); node.write_uint16(fp, pax_demand); node.write_uint16(fp, mail_demand); node.write_uint8 (fp, electricity_producer); node.write_uint32(fp, sound_interval); node.write_uint8 (fp, sound_id); node.write_uint8 (fp, num_smoke_offsets); for( int i = 0; i < 4; i++ ) { node.write_sint16( fp, pos_off[i].x); node.write_sint16( fp, pos_off[i].y); node.write_sint16( fp, xy_off[i].x); node.write_sint16( fp, xy_off[i].y); } node.write_sint16(fp, smokeuplift); node.write_sint16(fp, smokelifetime); // this should be always at the end sint8 sound_str_len = sound_str.size(); if (sound_str_len > 0) { node.write_sint8 (fp, sound_str_len); node.write_bytes(fp, sound_str_len, sound_str.c_str()); } node.check_and_write_header(fp); } std::string factory_writer_t::get_node_name(FILE* fp) const { obj_node_info_t node; // Name is inside building (BUIL) node, which is a child of factory (FACT) node if (!obj_node_t::read_node(fp, node)) return ""; if (node.type != obj_building) return ""; // If BUIL node not found, return blank to at least not crash fseek(fp, node.size, SEEK_CUR); return building_writer_t::instance()->get_node_name(fp); } simutrans-124.5/src/simutrans/descriptor/writer/factory_writer.h000066400000000000000000000062021520675460500253060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_FACTORY_WRITER_H #define DESCRIPTOR_WRITER_FACTORY_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" // new writer class for field class desc class factory_field_class_writer_t : public obj_writer_t { private: static factory_field_class_writer_t the_instance; factory_field_class_writer_t() { register_writer(false); } public: static factory_field_class_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_ffldclass; } const char* get_type_name() const OVERRIDE { return "factory field class"; } void write_obj(FILE* fp, obj_node_t& parent, const char* field_name, int snow_image, int production, int capacity, int weight); }; class factory_field_group_writer_t : public obj_writer_t { private: static factory_field_group_writer_t the_instance; factory_field_group_writer_t() { register_writer(false); } public: static factory_field_group_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_ffield; } const char *get_type_name() const OVERRIDE { return "factory field"; } void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; }; class factory_smoke_writer_t : public obj_writer_t { private: static factory_smoke_writer_t the_instance; factory_smoke_writer_t() { register_writer(false); } public: static factory_smoke_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_fsmoke; } const char *get_type_name() const OVERRIDE { return "factory smoke"; } void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; }; class factory_product_writer_t : public obj_writer_t { private: static factory_product_writer_t the_instance; factory_product_writer_t() { register_writer(false); } public: static factory_product_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_fproduct; } const char* get_type_name() const OVERRIDE { return "factory product"; } void write_obj(FILE* outfp, obj_node_t& parent, int capacity, int factor, const char* warename); }; class factory_supplier_writer_t : public obj_writer_t { private: static factory_supplier_writer_t the_instance; factory_supplier_writer_t() { register_writer(false); } public: static factory_supplier_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_fsupplier; } const char* get_type_name() const OVERRIDE { return "factory supplier"; } void write_obj(FILE* outfp, obj_node_t& parent, int capacity, int count, int consumption, const char* warename); }; class factory_writer_t : public obj_writer_t { private: static factory_writer_t the_instance; factory_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE; public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_factory; } const char* get_type_name() const OVERRIDE { return "factory"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/get_climate.cc000066400000000000000000000021361520675460500246600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simtypes.h" #include #include "../../utils/simstring.h" // for STRICMP #include "../ground_desc.h" #include "../../dataobj/tabfile.h" // the water and seven climates static const char* const climate_names[MAX_CLIMATES] = { "water", "desert", "tropic", "mediterran", "temperate", "tundra", "rocky", "arctic" }; /** * Convert climates string to bitfield */ climate_bits get_climate_bits(const char* climate_str) { uint16 uv16 = 0; uint16 i; const char* c = climate_str; do { while (*c == ' ' || *c == ',') { c++; } char end[256]; // skip the rest for (i = 0; i < 255 && *c > ' ' && *c != ','; i++, c++) { end[i] = *c; } end[i] = 0; // search for the string for (int i = 0; i < MAX_CLIMATES; i++) { if (STRICMP(end, climate_names[i]) == 0) { uv16 |= 1 << i; break; } } // allow "sea" as synonym for water if( uv16 == 0 && STRICMP(end,"sea") == 0) { uv16 |= 1; } } while (*c > 0 && *c != '#'); return (climate_bits)uv16; } simutrans-124.5/src/simutrans/descriptor/writer/get_climate.h000066400000000000000000000005071520675460500245220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_GET_CLIMATE_H #define DESCRIPTOR_WRITER_GET_CLIMATE_H #include "../../simtypes.h" /** * Convert climates string to bitfield */ climate_bits get_climate_bits(const char* climate_str); #endif simutrans-124.5/src/simutrans/descriptor/writer/get_waytype.cc000066400000000000000000000024321520675460500247430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../../utils/simstring.h" // for STRICMP #include "../../dataobj/tabfile.h" /** * Convert waytype string to enum waytype_t */ waytype_t get_waytype(const char* waytype) { waytype_t uv8 = road_wt; if (!STRICMP(waytype, "none")) { uv8 = ignore_wt; } else if (!STRICMP(waytype, "road")) { uv8 = road_wt; } else if (!STRICMP(waytype, "track")) { uv8 = track_wt; } else if (!STRICMP(waytype, "electrified_track")) { uv8 = overheadlines_wt; } else if (!STRICMP(waytype, "maglev_track")) { uv8 = maglev_wt; } else if (!STRICMP(waytype, "monorail_track")) { uv8 = monorail_wt; } else if (!STRICMP(waytype, "narrowgauge_track")) { uv8 = narrowgauge_wt; } else if (!STRICMP(waytype, "water")) { uv8 = water_wt; } else if (!STRICMP(waytype, "air")) { uv8 = air_wt; } else if (!STRICMP(waytype, "schiene_tram")) { uv8 = tram_wt; } else if (!STRICMP(waytype, "tram_track")) { uv8 = tram_wt; } else if (!STRICMP(waytype, "power")) { uv8 = powerline_wt; } else if (!STRICMP(waytype, "decoration")) { uv8 = decoration_wt; } else { dbg->fatal("get_waytype()","invalid waytype \"%s\"\n", waytype); } return uv8; } simutrans-124.5/src/simutrans/descriptor/writer/get_waytype.h000066400000000000000000000005001520675460500245770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_GET_WAYTYPE_H #define DESCRIPTOR_WRITER_GET_WAYTYPE_H #include "../../simtypes.h" /** * Convert waytype string to enum waytype_t */ waytype_t get_waytype(char const* waytype); #endif simutrans-124.5/src/simutrans/descriptor/writer/good_writer.cc000066400000000000000000000014471520675460500247330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../dataobj/tabfile.h" #include "../goods_desc.h" #include "obj_node.h" #include "text_writer.h" #include "good_writer.h" void goods_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { obj_node_t node(this, 16, &parent); write_name_and_copyright(fp, node, obj); text_writer_t::instance()->write_obj(fp, node, obj.get("metric")); node.write_version(fp, 4); node.write_sint64(fp, obj.get_int64("value", 0)); node.write_uint8 (fp, obj.get_int("catg", 0)); node.write_uint16(fp, obj.get_int("speed_bonus", 0)); node.write_uint16(fp, obj.get_int("weight_per_unit", 100)); node.write_uint8 (fp, obj.get_int("mapcolor", 255)); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/good_writer.h000066400000000000000000000013531520675460500245710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_GOOD_WRITER_H #define DESCRIPTOR_WRITER_GOOD_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class goods_writer_t : public obj_writer_t { private: static goods_writer_t the_instance; goods_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: /// Writes goods node data to file void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_good; } const char *get_type_name() const OVERRIDE { return "good"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/ground_writer.cc000066400000000000000000000016741520675460500253030ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "../ground_desc.h" #include "text_writer.h" #include "imagelist2d_writer.h" #include "ground_writer.h" void ground_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { obj_node_t node(this, 0, &parent); write_name_and_copyright(fp, node, obj); slist_tpl > keys; // summer images for (int slope = 0; slope < 128; slope++) { keys.append(); for (int phase = 0; ; phase++) { char buf[40]; sprintf(buf, "image[%d][%d]", slope, phase); std::string str = obj.get(buf); if (str.empty()) { break; } keys.at(slope).append(str); } // empty entries? if (keys.at(slope).empty()) { break; } } imagelist2d_writer_t::instance()->write_obj(fp, node, keys); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/ground_writer.h000066400000000000000000000013211520675460500251320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_GROUND_WRITER_H #define DESCRIPTOR_WRITER_GROUND_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class ground_writer_t : public obj_writer_t { private: static ground_writer_t the_instance; ground_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_ground; } const char *get_type_name() const OVERRIDE { return "ground"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/groundobj_writer.cc000066400000000000000000000064421520675460500257740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "text_writer.h" #include "imagelist2d_writer.h" #include "get_climate.h" #include "get_waytype.h" #include "groundobj_writer.h" using std::string; void groundobj_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { obj_node_t node(this, 20, &parent); write_name_and_copyright(fp, node, obj); climate_bits allowed_climates; const char *climate_str = obj.get("climates"); if (climate_str) { allowed_climates = get_climate_bits(climate_str); } else { dbg->warning( obj_writer_t::last_name, "No climates (using default)!"); allowed_climates = all_but_arctic_climate; } // seasons = 1: no seasons // otherwise the year will be divided by the (number_of_seasons-1) // The last image is always the snow image! uint8 const number_of_seasons = obj.get_int("seasons", 1); // distribution probability for all of this set uint16 const distribution_weight = obj.get_int("distributionweight", 3); // how much for removal sint64 const price = obj.get_int64("cost", 0); // !=0 for moving objects (sheeps, birds) uint16 const speed = obj.get_int("speed", 0); // 1 for to allow trees on this objects bool const trees_on_top = obj.get_int("trees_on_top", 1) != 0; // waytype for moving stuff; meaningful air for birds, water for fish, does not matter for everything else char const* const waytype_txt = obj.get("waytype"); waytype_t const waytype = waytype_txt && waytype_txt[0] != '\0' ? get_waytype(waytype_txt) : ignore_wt; // now for the images slist_tpl > keys; if (speed == 0) { // fixed stuff for (unsigned int phase = 0; 1; phase++) { keys.append(); for (int seasons = 0; seasons < number_of_seasons; seasons++) { char buf[40]; // Images of the tree // age is 1..5 (usually five stages, seasons is the seasons sprintf(buf, "image[%d][%d]", phase, seasons); string str = obj.get(buf); if (str.empty()) { if(seasons==0) { goto finish_images; } else { dbg->fatal("groundobj_writer_t","Season image for season %i missing!",seasons); } } keys.at(phase).append(str); } } } else { // moving stuff const char* const dir_codes[] = { "s", "w", "sw", "se", "n", "e", "ne", "nw" }; for (unsigned int dir = 0; dir<8; dir++) { keys.append(); for (int seasons = 0; seasons < number_of_seasons; seasons++) { char buf[40]; // Images of the tree // age is 1..5 (usually five stages, seasons is the seasons sprintf(buf, "image[%s][%d]", dir_codes[dir], seasons); string str = obj.get(buf); if( str.empty() ) { dbg->fatal("groundobj_writer_t","Season image for season %i missing (expected %s)!", seasons, buf ); } keys.at(dir).append(str); } } } finish_images: imagelist2d_writer_t::instance()->write_obj(fp, node, keys); // write version data node.write_version(fp, 2); node.write_uint16(fp, allowed_climates); node.write_uint16(fp, distribution_weight); node.write_uint8 (fp, number_of_seasons); node.write_uint8 (fp, trees_on_top); node.write_uint16(fp, speed); node.write_uint16(fp, waytype); node.write_sint64(fp, price); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/groundobj_writer.h000066400000000000000000000013471520675460500256350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_GROUNDOBJ_WRITER_H #define DESCRIPTOR_WRITER_GROUNDOBJ_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class groundobj_writer_t : public obj_writer_t { private: static groundobj_writer_t the_instance; groundobj_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_groundobj; } const char *get_type_name() const OVERRIDE { return "ground_obj"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/image_writer.cc000066400000000000000000000322231520675460500250610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include "image_writer.h" #include "root_writer.h" #include "obj_node.h" #include "obj_pak_exception.h" #include "../image.h" #include "../../macros.h" #include "../../utils/simstring.h" #include "../../simdebug.h" #include "../../io/raw_image.h" #ifndef _WIN32 #include #include #endif struct dimension { int xmin; int xmax; int ymin; int ymax; }; std::string image_writer_t::last_img_file; raw_image_t image_writer_t::input_img; int image_writer_t::img_size = 64; uint32 image_writer_t::block_getpix(int x, int y) { const uint8 *pixel_data = input_img.access_pixel(x, y); switch (input_img.get_format()) { case raw_image_t::FMT_GRAY8: { const uint8 gray_level = pixel_data[0]; return gray_level << 0 | gray_level << 8 | gray_level << 16; } case raw_image_t::FMT_RGBA8888: { const uint32 pixel = (pixel_data[2] << 0) + // B (pixel_data[1] << 8) + // G (pixel_data[0] << 16) + // R (pixel_data[3] << 24); // A // invert alpha channel, we want 0 == opaque return pixel ^ 0xFF000000; } case raw_image_t::FMT_RGB888: { return (pixel_data[2] << 0) | // B (pixel_data[1] << 8) | // G (pixel_data[0] << 16); // R } default: dbg->fatal("image_writer_t::block_getpix", "Unsupported input image format"); } } // colors with higher alpha are considered transparent #define ALPHA_THRESHOLD (0xF8000000u) /** * Encodes image data into the internal representation, * considers special colors. */ static uint16 pixrgb_to_pixval(uint32 rgb) { uint16 pix; // first: find about alpha assert( rgb < ALPHA_THRESHOLD ); int alpha = 30 - (rgb >> 24)/8; // transparency in 32 steps, but simutrans uses internally the reverse format if( rgb > 0x00FFFFFF ) { // alpha is now between 0 ... 30 // first see if this is a transparent special color (like player color) for (int i = 0; i < SPECIAL; i++) { if (image_t::rgbtab[i] == (uint32)(rgb & 0x00FFFFFFu)) { // player or light color pix = 0x8020 + i*31 + alpha; return endian(pix); } } // else store color as 3 red, 4, green, 3 red pix = ((rgb >> 14) & 0x0380) | ((rgb >> 9) & 0x0078) | ((rgb >> 5) & 0x07); pix = 0x8020 + 31*31 + pix*31 + alpha; return pix; } // non-transparent pixel for (int i = 0; i < SPECIAL; i++) { if (image_t::rgbtab[i] == (uint32)rgb) { pix = 0x8000 + i; return pix; } } const int r = (rgb >> 16); const int g = (rgb >> 8) & 0xFF; const int b = (rgb >> 0) & 0xFF; // RGB 555 pix = ((r & 0xF8) << 7) | ((g & 0xF8) << 2) | ((b & 0xF8) >> 3); return pix; } // true if transparent inline bool is_transparent( const uint32 pix ) { return (pix & 0x00FFFFFF) == SPECIAL_TRANSPARENT || (pix >= ALPHA_THRESHOLD); } static void init_dim(uint32 *image, dimension *dim, int img_size) { int x,y; bool found = false; dim->ymin = dim->xmin = img_size; dim->ymax = dim->xmax = 0; for (y = 0; y < img_size; y++) { for (x = 0; x < img_size; x++) { if( !is_transparent(image[x + y * img_size]) ) { if (x < dim->xmin) dim->xmin = x; if (y < dim->ymin) dim->ymin = y; if (x > dim->xmax) dim->xmax = x; if (y > dim->ymax) dim->ymax = y; found = true; } } } if (!found) { // Negative values not usable dim->xmin = 1; dim->ymin = 1; } } /** * Encodes an image into a sprite data structure, considers * special colors. */ uint16 *image_writer_t::encode_image(int x, int y, dimension* dim, int* len) { int line; uint16 *dest; uint16 *dest_base = new uint16[img_size * img_size * 2]; uint16 *colored_run_counter; dest = dest_base; x += dim->xmin; y += dim->ymin; const int img_width = dim->xmax - dim->xmin + 1; const int img_height = dim->ymax - dim->ymin + 1; for( line = 0; line < img_height; line++ ) { int row_px_count = 0; // index of the currently handled pixel uint16 clear_colored_run_pair_count = 0; uint32 pix = block_getpix( x + row_px_count, y + line ); row_px_count++; do { // read one row uint16 count = 0; // read transparent pixels while( is_transparent(pix) ) { count ++; if (row_px_count >= img_width) { // end of line ? break; } pix = block_getpix( x + row_px_count, y + line ); row_px_count++; } // write number of transparent pixels *dest++ = endian(count); // position to write number of colored pixels to colored_run_counter = dest++; count = 0; PIXVAL has_transparent = 0; while( !is_transparent(pix) ) { // write the colored pixel PIXVAL pixval = pixrgb_to_pixval(pix); if( pixval >= 0x8020 && !has_transparent ) { if( count ) { *colored_run_counter = endian(uint16(count)); *dest++ = endian(uint16(0x8000)); colored_run_counter = dest++; count = 0; } has_transparent = 0x8000; } else if( pixval < 0x8020 && has_transparent ) { if( count ) { *colored_run_counter = endian(uint16(count+has_transparent)); *dest++ = endian(uint16(0x8000)); colored_run_counter = dest++; count = 0; } has_transparent = 0; } *dest++ = endian(uint16(pixval)); count++; if (row_px_count >= img_width) { // end of line ? break; } pix = block_getpix( x + row_px_count, y + line ); row_px_count++; } /* * If it is not the first clear-colored-run pair and its colored run is empty * --> it is superfluous and can be removed by rolling back the pointer */ if( clear_colored_run_pair_count > 0 && count == 0 ) { dest -= 2; // this only happens at the end of a line, so no need to increment clear_colored_run_pair_count } else { *colored_run_counter = endian(uint16(count + has_transparent)); clear_colored_run_pair_count++; } } while( row_px_count < img_width ); *dest++ = 0; } *len = dest - dest_base; return dest_base; } bool image_writer_t::block_load(const char *fname) { // The last image file is cached // Note that this method accepts any file name if the content has a supported format, // even though makeobj only supports image file names with a ".png" suffix. // See image_writer_t::write_obj for details. if( last_img_file == fname ) { return true; } else if (load_image_from_file(fname)) { if ((input_img.get_width()%img_size != 0) || (input_img.get_height()%img_size != 0)) { dbg->error("image_writer_t::block_load", "Cannot load image file '%s': " "Size not divisible by %d.", fname, img_size); last_img_file = ""; return false; } last_img_file = fname; return true; } // error message is handled by image_writer_t::write_obj last_img_file = ""; return false; } bool image_writer_t::load_image_from_file(const char* fname) { if (input_img.read_from_file(fname)) { return true; } // Not an exact match, try to case-insensitive search. #ifndef _WIN32 std::string actual_path; size_t len = strlen(fname); actual_path.reserve(len); const char * sep_beg = fname; const char * sep_end = sep_beg + strspn(sep_beg, "/"); if (sep_end == sep_beg) { // relative actual_path = "./"; } char const * end = fname + len; std::string name; while (true) { actual_path.insert(actual_path.end(), sep_beg, sep_end); sep_beg = sep_end + strcspn(sep_end, "/"); if (sep_beg == sep_end) { break; } name.assign(sep_end, sep_beg); DIR * dir = opendir(actual_path.c_str()); if (!dir) { break; } struct dirent * ent = NULL; while ((ent = readdir(dir)) != NULL) { if (!STRICMP(ent->d_name, name.c_str())) { actual_path += ent->d_name; break; } } closedir(dir); if (!ent) { break; } if (sep_beg == end) { return input_img.read_from_file(actual_path.c_str()); } sep_end = sep_beg + strspn(sep_beg, "/"); } #endif return false; } /* the syntax for image the string is * "-" empty image * [> ]imagefilename_without_extension[[[[.row].col],xoffset],yoffset] * leading "> " set the flag for a non-zoomable image * after the dots also spaces and comments are allowed */ void image_writer_t::write_obj(FILE* outfp, obj_node_t& parent, std::string an_imagekey, uint32 index) { image_t image; dimension dim; uint16 *pixdata = NULL; MEMZERO(image); // if first char is a '>' then this image is not zoomable if( an_imagekey[0] == '>' ) { an_imagekey = an_imagekey.substr(1); image.zoomable = false; } else { image.zoomable = true; } std::string imagekey = trim(an_imagekey); if( imagekey != "-" && imagekey != "" ) { // divide key in filename and image number int row = -1, col = -1; std::string numkey; int j = imagekey.rfind('/'); if( j == -1 ) { numkey = imagekey; } else { numkey = imagekey.substr(j + 1, std::string::npos); } int i = numkey.find('.'); if( i == -1 ) { char reason[1024]; sprintf(reason, "no image number in %s", imagekey.c_str() ); throw obj_pak_exception_t("image_writer_t", reason); } numkey = numkey.substr( i+1, std::string::npos ); imagekey = root_writer_t::get_inpath() + imagekey.substr( 0, imagekey.size()-numkey.size() - 1 ) + ".png"; row = atoi(numkey.c_str()); i = numkey.find('.'); if(i != -1) { col = atoi( numkey.c_str()+i+1 ); // add image offsets int comma_pos = numkey.find(','); if(comma_pos != -1) { numkey = numkey.substr( comma_pos+1, std::string::npos); image.x = atoi( numkey.c_str() ); comma_pos = numkey.find(','); if(comma_pos != -1) { image.y = atoi(numkey.substr( comma_pos + 1, std::string::npos).c_str()); } } } // Load complete file if (!block_load(imagekey.c_str())) { char reason[1024]; sprintf(reason, "cannot open %s", imagekey .c_str()); throw obj_pak_exception_t("image_writer_t", reason); } if (col == -1) { col = row % (input_img.get_width() / img_size); row = row / (input_img.get_height() / img_size); } if (col >= (int)(input_img.get_width() / img_size) || row >= (int)(input_img.get_height() / img_size)) { char reason[1024]; sprintf(reason, "invalid image number in %s.%s", imagekey.c_str(), numkey.c_str()); throw obj_pak_exception_t("image_writer_t", reason); } row *= img_size; col *= img_size; // Temp. read image and determine drawing area. uint32 *image_data = new uint32[img_size * img_size]; for (int x = 0; x < img_size; x++) { for (int y = 0; y < img_size; y++) { image_data[x + y * img_size] = block_getpix(x + col, y + row); } } init_dim(image_data, &dim, img_size); delete [] image_data; image.x += dim.xmin; image.y += dim.ymin; image.w = dim.xmax - dim.xmin + 1; image.h = dim.ymax - dim.ymin + 1; image.len = 0; if (image.h > 0) { int len; pixdata = encode_image(col, row, &dim, &len); image.len = len; } dbg->debug( "", "image[%3u] =%-30s %-20s %5u %5u %5u %5u %5u %6u %4s", index, an_imagekey.c_str(), imagekey.c_str(), col, row, image.x, image.y, image.w, image.h, (image.zoomable) ? "yes" : "no" ); } else { dbg->debug( "", "image[%3u] =%-30s %-20s %5u %5u %5u %5u %5u %6u %4s", index, an_imagekey.c_str(), imagekey.c_str(), 0, 0, image.x, image.y, image.w, image.h, (image.zoomable) ? "yes" : "no" ); } #ifdef IMG_VERSION0 // version 0 obj_node_t node(this, 12 + (image.len * sizeof(uint16)), &parent); // to avoid any problems due to structure changes, we write manually the data node.write_uint8 (outfp, image.x, 0); node.write_uint8 (outfp, image.w, 1); node.write_uint8 (outfp, image.y, 2); node.write_uint8 (outfp, image.h, 3); node.write_uint32(outfp, image.len, 4); node.write_uint16(outfp, 0, 8); node.write_uint8 (outfp, image.zoomable, 10); node.write_uint8 (outfp, 0, 11); if (image.len) { // only called, if there is something to store node.write_data_at(outfp, pixdata, 12, image.len * sizeof(PIXVAL)); delete [] pixdata; } #elif IMG_VERSION2 // version 1 or 2 obj_node_t node(this, 10 + (image.len * sizeof(uint16)), &parent); // to avoid any problems due to structure changes, we write manually the data node.write_uint16(outfp, image.x, 0); node.write_uint16(outfp, image.y, 2); node.write_uint8 (outfp, image.w, 4); node.write_uint8 (outfp, image.h, 5); node.write_uint8 (outfp, 2, 6); // version node.write_uint16(outfp, image.len, 7); node.write_uint8 (outfp, image.zoomable, 9); if (image.len) { // only called, if there is something to store node.write_data_at(outfp, pixdata, 10, image.len * sizeof(PIXVAL)); delete [] pixdata; } #else // version 3 obj_node_t node(this, 10 + (image.len * sizeof(uint16)), &parent); // to avoid any problems due to structure changes, we write the data manually node.write_uint16(outfp, image.x); node.write_uint16(outfp, image.y); node.write_uint16(outfp, image.w); node.write_uint8 (outfp, 3); // version, always at position 6! node.write_uint16(outfp, image.h); // len is now automatically calculated node.write_uint8 (outfp, image.zoomable); if (image.len) { // only called, if there is something to store node.write_bytes(outfp, image.len * sizeof(uint16), pixdata); delete [] pixdata; } #endif node.check_and_write_header(outfp); } simutrans-124.5/src/simutrans/descriptor/writer/image_writer.h000066400000000000000000000025111520675460500247200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_IMAGE_WRITER_H #define DESCRIPTOR_WRITER_IMAGE_WRITER_H #include #include #include "obj_writer.h" #include "../objversion.h" #include "../../io/raw_image.h" class obj_node_t; struct dimension; class image_writer_t : public obj_writer_t { private: static image_writer_t the_instance; static std::string last_img_file; static raw_image_t input_img; static int img_size; // default 64 image_writer_t() { register_writer(false); } static uint32 block_getpix(int x, int y); public: static image_writer_t* instance() { return &the_instance; } static void set_img_size(int _img_size) { img_size = _img_size; } obj_type get_type() const OVERRIDE { return obj_image; } const char* get_type_name() const OVERRIDE { return "image"; } void write_obj(FILE* fp, obj_node_t& parent, std::string imagekey, uint32 index); private: bool block_load(const char* fname); /// Loads @ref input_img with the contents of @p fname, ignores case of @p filename. /// @returns true on success bool load_image_from_file(const char *fname); /// Encodes an image into a sprite data structure, considers /// special colors. static uint16 *encode_image(int x, int y, dimension* dim, int* len); }; #endif simutrans-124.5/src/simutrans/descriptor/writer/imagelist2d_writer.cc000066400000000000000000000011771520675460500262070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../tpl/slist_tpl.h" #include "obj_node.h" #include "imagelist_writer.h" #include "imagelist2d_writer.h" void imagelist2d_writer_t::write_obj(FILE* fp, obj_node_t& parent, const slist_tpl >& keys) { obj_node_t node(this, 4, &parent); uint16 const count = keys.get_count(); for(slist_tpl const& s : keys) { imagelist_writer_t::instance()->write_obj(fp, node, s); } node.write_uint16(fp, count); node.write_uint16(fp, 0); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/imagelist2d_writer.h000066400000000000000000000014161520675460500260450ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_IMAGELIST2D_WRITER_H #define DESCRIPTOR_WRITER_IMAGELIST2D_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" template class slist_tpl; class imagelist2d_writer_t : public obj_writer_t { private: static imagelist2d_writer_t the_instance; imagelist2d_writer_t() { register_writer(false); } public: static imagelist2d_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_imagelist2d; } const char* get_type_name() const OVERRIDE { return "imagelist2d"; } void write_obj(FILE* fp, obj_node_t& parent, const slist_tpl >& keys); }; #endif simutrans-124.5/src/simutrans/descriptor/writer/imagelist_writer.cc000066400000000000000000000021151520675460500257520ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../tpl/slist_tpl.h" #include "obj_node.h" #include "image_writer.h" #include "obj_pak_exception.h" #include "imagelist_writer.h" void imagelist_writer_t::write_obj(FILE* fp, obj_node_t& parent, const slist_tpl& keys) { obj_node_t node(this, 4, &parent); unsigned int count = 0; if( !keys.empty() && debuglevel>2 ) { dbg->debug("", "Source image X Y Off X Off Y Width Height Zoom\n"); dbg->debug("", "------------------------------------------ -------------------- ----- ----- ----- ----- ----- ------ ----\n"); } for(std::string const& s : keys) { image_writer_t::instance()->write_obj(fp, node, s, count); count ++; } if (count < keys.get_count()) { dbg->warning( obj_writer_t::last_name, "Expected %i but found %i images (might be correct)!\n", keys.get_count(), count); } node.write_uint16(fp, count); node.write_uint16(fp, 0); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/imagelist_writer.h000066400000000000000000000014271520675460500256210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_IMAGELIST_WRITER_H #define DESCRIPTOR_WRITER_IMAGELIST_WRITER_H #include #include #include "obj_writer.h" #include "../objversion.h" template class slist_tpl; class obj_node_t; class imagelist_writer_t : public obj_writer_t { private: static imagelist_writer_t the_instance; imagelist_writer_t() { register_writer(false); } public: static imagelist_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_imagelist; } const char* get_type_name() const OVERRIDE { return "imagelist"; } void write_obj(FILE* fp, obj_node_t& parent, const slist_tpl& keys); }; #endif simutrans-124.5/src/simutrans/descriptor/writer/obj_node.cc000066400000000000000000000072721520675460500241700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "obj_pak_exception.h" #include "obj_writer.h" #include "obj_node.h" #include "../obj_desc.h" #include uint32 obj_node_t::next_node_header_offset; // next free offset in file obj_node_t::obj_node_t(obj_writer_t* writer, uint32 size, obj_node_t* parent) { this->parent = parent; this->data_write_offset = 0; desc.type = writer->get_type(); desc.nchildren = 0; desc.size = size; if( sizebody_offset = obj_node_t::next_node_header_offset + OBJ_NODE_INFO_SIZE; // put size of dis here! obj_node_t::next_node_header_offset = this->body_offset + this->desc.size; } else { this->body_offset = obj_node_t::next_node_header_offset + EXT_OBJ_NODE_INFO_SIZE; // put size of dis here! obj_node_t::next_node_header_offset = this->body_offset + this->desc.size; } } // reads a node and advance the file pointer accordingly bool obj_node_t::read_node(FILE* fp, obj_node_info_t &node ) { uint32 u32 = 0; uint16 u16 = 0; if (fread(&u32, sizeof(uint32), 1, fp) != 1) { return false; } node.type = endian(u32); if (fread(&u16, sizeof(uint16), 1, fp) != 1) { return false; } node.nchildren = endian(u16); if (fread(&u16, sizeof(uint16), 1, fp) != 1) { return false; } node.size = endian(u16); if( node.size==LARGE_RECORD_SIZE ) { if (fread(&u32, sizeof(uint32), 1, fp) != 1) { return false; } node.size = endian(u32); } return true; } void obj_node_t::check_and_write_header(FILE *fp) { assert(this->data_write_offset == this->desc.size); if( desc.sizedesc.nchildren++; } } void obj_node_t::write_bytes(FILE* fp, uint32 size, const void *data) { // beware of overflow if (UINT32_MAX - size < this->data_write_offset || this->data_write_offset + size > desc.size) { char reason[1024]; sprintf(reason, "invalid parameters (%u + %u > %u)", this->data_write_offset, size, desc.size); throw obj_pak_exception_t("obj_node_t", reason); } fseek(fp, body_offset + this->data_write_offset, SEEK_SET); fwrite(data, size, 1, fp); this->data_write_offset += size; } void obj_node_t::write_version(FILE *fp, uint16 version) { // Version needs high bit set as trigger -> this is required // as marker because formerly nodes were unversionend version |= UINT16_C(0x8000); write_uint16(fp, version); } void obj_node_t::write_uint8(FILE *fp, uint8 data) { this->write_bytes(fp, sizeof(data), &data); } void obj_node_t::write_uint16(FILE *fp, uint16 data) { uint16 data2 = endian(data); this->write_bytes(fp, sizeof(data2), &data2); } void obj_node_t::write_uint32(FILE *fp, uint32 data) { uint32 data2 = endian(data); this->write_bytes(fp, sizeof(data2), &data2); } void obj_node_t::write_uint64(FILE *fp, uint64 data) { uint64 data2 = endian(data); this->write_bytes(fp, sizeof(data2), &data2); } simutrans-124.5/src/simutrans/descriptor/writer/obj_node.h000066400000000000000000000047071520675460500240320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_OBJ_NODE_H #define DESCRIPTOR_WRITER_OBJ_NODE_H #include "../obj_node_info.h" #include class obj_writer_t; class obj_node_t { private: /// Offset, from the beginning of the file, of the start of the next node header /// ( == number of bytes used for nodes so far) static uint32 next_node_header_offset; obj_node_info_t desc; uint32 body_offset; ///< Start of node data in file (after node.desc) uint32 data_write_offset; ///< Additional running offset from @ref body_offset where to write the next data item for this node obj_node_t *parent; public: // set_start_offset() - set offset of first node in file // ONLY CALL BEFORE ANY NODES ARE CREATED !!! static void set_start_offset(uint32 offset) { next_node_header_offset = offset; } // reads a node into a given obj_node_info_t static bool read_node(FILE* fp, obj_node_info_t &node ); /// construct a new node. /// @param writer object, that writes the node to the file /// @param size space needed for node data /// @param parent parent node obj_node_t(obj_writer_t* writer, uint32 size, obj_node_t* parent); // Write an array of bytes to the body of this node. // The caller is responsible that all areas are written by multiple calls // Throws obj_pak_exception_t on error /// @param[in,out] offset Offset from the beginning of @p fp to write the data. /// This is increased by @p size once the data has been fully written. void write_bytes(FILE *fp, uint32 nbytes, const void *bytes); /// Writes the node version to @p fp. @p offset advances by 2 bytes. void write_version(FILE *fp, uint16 version); /// Writes uint8 value to @p fp at @p offset. @p offset advances by 1 byte. void write_uint8 (FILE *fp, uint8 data); void write_uint16(FILE *fp, uint16 data); void write_uint32(FILE *fp, uint32 data); void write_uint64(FILE *fp, uint64 data); void write_sint8 (FILE* fp, sint8 data) { this->write_uint8 (fp, (uint8) data); } void write_sint16(FILE* fp, sint16 data) { this->write_uint16(fp, (uint16)data); } void write_sint32(FILE* fp, sint32 data) { this->write_uint32(fp, (uint32)data); } void write_sint64(FILE* fp, sint64 data) { this->write_uint64(fp, (uint64)data); } // Write the internal node info to the file // DO THIS AFTER ALL CHILD NODES ARE WRITTEN !!! void check_and_write_header(FILE *fp); }; #endif simutrans-124.5/src/simutrans/descriptor/writer/obj_pak_exception.h000066400000000000000000000010431520675460500257240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_OBJ_PAK_EXCEPTION_H #define DESCRIPTOR_WRITER_OBJ_PAK_EXCEPTION_H #include class obj_pak_exception_t { private: std::string classname; std::string problem; public: obj_pak_exception_t(const char* clsname, const char* prob) { classname = clsname; problem = prob; } const char* get_class() const { return classname.c_str(); } const char* get_info() const { return problem.c_str(); } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/obj_writer.cc000066400000000000000000000113461520675460500245540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../utils/simstring.h" #include "../../dataobj/tabfile.h" #include "../../tpl/stringhashtable_tpl.h" #include "../../tpl/inthashtable_tpl.h" #include "obj_node.h" #include "obj_writer.h" #include "image_writer.h" #include "text_writer.h" #include "xref_writer.h" const char *obj_writer_t::last_name = ""; int obj_writer_t::default_image_size = 64; void obj_writer_t::register_writer(bool main_obj) { if (!writer_by_name) { writer_by_name = new stringhashtable_tpl; writer_by_type = new inthashtable_tpl; } if (main_obj) { writer_by_name->put(get_type_name(), this); } writer_by_type->put(get_type(), this); /// printf("This program can compile %s objects\n", get_type_name()); } void obj_writer_t::write(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { const char *type = obj.get("obj"); const char *name = obj.get("name"); obj_writer_t *writer = writer_by_name->get(type); if (!writer) { printf("Skipping unknown %s object %s\n", type, name); return; } // now get the image size image_writer_t::set_img_size(obj.get_int("cell_size",default_image_size)); last_name = name; if (debuglevel >= log_t::LEVEL_WARN) { printf(" packing %s.%s\n", type, name); } writer->write_obj(fp, parent, obj); } void obj_writer_t::write_name_and_copyright(FILE* fp, obj_node_t& node, tabfileobj_t& obj) { const char* name = obj.get("name"); const char* msg = obj.get("copyright"); last_name = name; text_writer_t::instance()->write_obj(fp, node, name); text_writer_t::instance()->write_obj(fp, node, msg); } bool obj_writer_t::dump_nodes(FILE* infp, int level, uint16 index) { obj_node_info_t node; if (!obj_node_t::read_node(infp, node) ) { return false; } const long next_pos = ftell(infp) + node.size; obj_writer_t* writer = writer_by_type->get(node.type); if (writer) { printf("%*s%03u %4.4s-node (%s)", 3 * level, " ", index, (const char*)&node.type, writer->get_type_name()); bool ok = writer->dump_node(infp, node); printf("\n"); if (!ok) { return false; } } if (fseek(infp, next_pos, SEEK_SET) != 0) { return false; } for (int child_idx = 0; child_idx < node.nchildren; child_idx++) { if (!dump_nodes(infp, level + 1, child_idx)) { return false; } } return true; } bool obj_writer_t::list_nodes(FILE *infp) { obj_node_info_t node; if (!obj_node_t::read_node( infp, node ) || node.size == 0) { return false; } const long next_pos = ftell(infp) + node.size; obj_writer_t *writer = writer_by_type->get((obj_type)node.type); if (writer) { if (fseek(infp, node.size, SEEK_CUR) != 0) { return false; } const std::string node_name = writer->get_node_name(infp); printf("%-16s %-30s %5u ", writer->get_type_name(), node_name.c_str(), node.nchildren); } else { printf("(unknown %4.4s) %30.30s %5.5s ", (const char *)&node.type, " ", " "); } if (fseek(infp, next_pos, SEEK_SET) != 0) { return false; } size_t offset = 0; for (int i = 0; i < node.nchildren; i++) { if (!skip_nodes(infp, offset)) { return false; } } printf("%10lu\n", offset); return true; } void obj_writer_t::show_capabilites() { slist_tpl list; const char *min_s="A"; while (true) { const char *max_s = "zzz"; for(auto const& i : *writer_by_name) { if( STRICMP(i.key, min_s) > 0 && STRICMP(i.key, max_s) < 0 ) { max_s = i.key; } } if( strcmp(max_s,"zzz")==0 ) { break; } printf(" %s\n", max_s); min_s = max_s; } } std::string obj_writer_t::name_from_next_node(FILE *fp) const { std::string ret; obj_node_info_t node; if (!obj_node_t::read_node( fp, node ) || node.type!=obj_text || node.size == 0xFFFFFFFFu) { return ""; } char *buf = new char[node.size+1]; if (!buf) { return ""; } if (fread(buf, node.size, 1, fp) != 1) { delete[] buf; return ""; } buf[node.size] = 0; ret = buf; delete[] buf; return ret; } bool obj_writer_t::skip_nodes(FILE *fp, size_t &offset) { obj_node_info_t node; if (!obj_node_t::read_node( fp, node ) || fseek(fp, node.size, SEEK_CUR) != 0) { return false; } offset += node.size; for (int i = 0; i < node.nchildren; i++) { if (!skip_nodes(fp, offset)) { return false; } } return true; } bool obj_writer_t::dump_node(FILE */*infp*/, const obj_node_info_t& node) { printf(" %5u bytes", node.size); return true; } const char* obj_writer_t::node_writer_name(FILE* infp) const { obj_node_info_t node; obj_node_t::read_node( infp, node ); fseek(infp, node.size, SEEK_CUR); obj_writer_t* writer = writer_by_type->get(node.type); if (writer) { return writer->get_type_name(); } return "unknown"; } simutrans-124.5/src/simutrans/descriptor/writer/obj_writer.h000066400000000000000000000032171520675460500244140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_OBJ_WRITER_H #define DESCRIPTOR_WRITER_OBJ_WRITER_H #include #include #include "../intro_dates.h" #include "../objversion.h" class obj_node_t; struct obj_node_info_t; class tabfileobj_t; template class stringhashtable_tpl; template class inthashtable_tpl; class obj_writer_t { private: static stringhashtable_tpl* writer_by_name; static inthashtable_tpl *writer_by_type; static int default_image_size; protected: obj_writer_t() { /* Beware: Cannot register here! */ } void register_writer(bool main_obj); bool dump_nodes(FILE *infp, int level, uint16 index = 0); bool list_nodes(FILE *infp); bool skip_nodes(FILE* fp, size_t &offset); void show_capabilites(); std::string name_from_next_node(FILE* fp) const; const char* node_writer_name(FILE* infp) const; virtual std::string get_node_name(FILE* /*fp*/) const { return ""; } virtual bool dump_node(FILE *infp, const obj_node_info_t& node); virtual void write_obj(FILE */*fp*/, obj_node_t& /*parent*/, tabfileobj_t& /*obj*/) {} void write_name_and_copyright(FILE* fp, obj_node_t& node, tabfileobj_t& obj); public: // contains the name of the last obj/name header static const char *last_name; virtual ~obj_writer_t() {} virtual obj_type get_type() const = 0; virtual const char* get_type_name() const = 0; static void write(FILE* fp, obj_node_t& parent, tabfileobj_t& obj); static void set_img_size(int img_size) { obj_writer_t::default_image_size = img_size; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/pedestrian_writer.cc000066400000000000000000000042131520675460500261330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "text_writer.h" #include "imagelist_writer.h" #include "imagelist2d_writer.h" #include "pedestrian_writer.h" void pedestrian_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { int i; obj_node_t node(this, 12, &parent); write_name_and_copyright(fp, node, obj); uint16 const distribution_weight = obj.get_int("distributionweight", 1); static const char* const dir_codes[] = { "s", "w", "sw", "se", "n", "e", "ne", "nw" }; slist_tpl keys; slist_tpl > keys_animated; std::string str; char buf[40]; // test for animation images uint16 is_animated = 0; for (i = 0; i < 8; i++) { sprintf(buf, "image[%s][0]", dir_codes[i]); str = obj.get(buf); is_animated += !str.empty(); } for (i = 0; i < 8; i++) { if (is_animated) { for (uint16 j = 0; j<500; j++) { keys_animated.append(); sprintf(buf, "image[%s][%d]", dir_codes[i], j); str = obj.get(buf); printf("%s : %s\n", buf, str.c_str()); if (str.empty()) { break; } keys_animated.at(i).append(str); } } else { sprintf(buf, "image[%s]", dir_codes[i]); str = obj.get(buf); keys.append(str); } } uint16 steps_per_frame = is_animated ? max(obj.get_int("steps_per_frame", 1), 1) : 0; if (is_animated) { imagelist2d_writer_t::instance()->write_obj(fp, node, keys_animated); } else { imagelist_writer_t::instance()->write_obj(fp, node, keys); } uint16 offset = obj.get_int("offset", 20); uint16 const intro_date = obj.get_int("intro_year", 1) * 12 + // no DEFAULT_INTRO_DATE here obj.get_int("intro_month", 1) - 1; uint16 const retire_date = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12 + obj.get_int("retire_month", 1) - 1; node.write_version(fp, 2); node.write_uint16(fp, distribution_weight); node.write_uint16(fp, steps_per_frame); node.write_uint16(fp, offset); node.write_uint16(fp, intro_date); node.write_uint16(fp, retire_date); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/pedestrian_writer.h000066400000000000000000000013551520675460500260010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_PEDESTRIAN_WRITER_H #define DESCRIPTOR_WRITER_PEDESTRIAN_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class pedestrian_writer_t : public obj_writer_t { private: static pedestrian_writer_t the_instance; pedestrian_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_pedestrian; } const char* get_type_name() const OVERRIDE { return "pedestrian"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/roadsign_writer.cc000066400000000000000000000122211520675460500256010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../dataobj/tabfile.h" #include "../roadsign_desc.h" #include "obj_node.h" #include "text_writer.h" #include "imagelist_writer.h" #include "roadsign_writer.h" #include "get_waytype.h" #include "skin_writer.h" static const char* private_sign_directions[] = {"ns", "ew"}; static const char* traffic_light_directions[] = {"n", "s", "w", "e", "nw", "se", "sw", "ne"}; static const char* general_sign_directions[] = {"n", "s", "w", "e"}; // parse "image[direction][state]" syntax void parse_images_2d(slist_tpl& keys, tabfileobj_t& obj, roadsign_desc_t::types flags) { const char** directions; uint8 dir_cnt; // how many directions are there? if( flags&roadsign_desc_t::PRIVATE_ROAD ) { directions = private_sign_directions; dir_cnt = lengthof(private_sign_directions); } else if( *obj.get("image[ne][0]") ) { // Assume this is a traffic light. directions = traffic_light_directions; dir_cnt = lengthof(traffic_light_directions); } else { // Normal road sign or railway signal directions = general_sign_directions; dir_cnt = lengthof(general_sign_directions); } for( uint8 state=0; state<8; state++ ) { for( uint8 idx = 0; idx < dir_cnt; idx++ ) { char buf[64]; sprintf(buf, "image[%s][%i]", directions[idx], state); const char* img = obj.get(buf); if( !*img ){ if( state>(dir_cnt==2) && idx==0 ) { // Assume all further state numbers are invalid. return; } // image in the middle is missing => fatal error dbg->fatal("roadsign_writer", "%s is missing!", buf); } // append image number keys.append(img); } } } // parse "image[number]" syntax void parse_images_numbered(slist_tpl& keys, tabfileobj_t& obj) { for (int i = 0; i < 32; i++) { char buf[40]; sprintf(buf, "image[%i]", i); const char *str = obj.get(buf); // make sure, there are always 4, 8, 12, ... images (for all directions) if( !*str ) { if( i % 4 ) { dbg->fatal("roadsign_writer", "image count is %d but must be multiple of 4!", i); } break; } keys.append(str); } } void roadsign_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { const sint64 price = obj.get_int64("cost", 500) * 100; const sint64 maintenance = obj.get_int64("maintenance", 0); const uint16 min_speed = obj.get_int("min_speed", 0); const sint8 offset_left = obj.get_int("offset_left", 14); const uint8 wtyp = get_waytype(obj.get("waytype")); roadsign_desc_t::types flags = roadsign_desc_t::NONE; if( obj.get_int("is_signal",0) ) { flags = roadsign_desc_t::SIGN_SIGNAL; if( obj.get_int("free_route",0) ) { flags |= roadsign_desc_t::CHOOSE_SIGN; } } else if( obj.get_int("is_presignal",0) ) { flags = roadsign_desc_t::SIGN_PRE_SIGNAL; } else if( obj.get_int("is_prioritysignal",0) ) { flags = roadsign_desc_t::SIGN_PRIORITY_SIGNAL; } else if( obj.get_int("is_longblocksignal",0) ) { flags = roadsign_desc_t::SIGN_LONGBLOCK_SIGNAL; } else { // road or airsigns ... flags = (obj.get_int("single_way", 0) > 0 ? roadsign_desc_t::ONE_WAY : roadsign_desc_t::NONE) | (obj.get_int("free_route", 0) > 0 ? roadsign_desc_t::CHOOSE_SIGN : roadsign_desc_t::NONE) | (obj.get_int("is_private", 0) > 0 ? roadsign_desc_t::PRIVATE_ROAD : roadsign_desc_t::NONE) | (obj.get_int("no_foreground", 0) > 0 ? roadsign_desc_t::ONLY_BACKIMAGE : roadsign_desc_t::NONE) | (obj.get_int("end_of_choose", 0) > 0 ? roadsign_desc_t::END_OF_CHOOSE_AREA : roadsign_desc_t::NONE); } // this causes unused entries to give a warning that they are ignored obj_node_t node(this, 28, &parent); node.write_version(fp, 6); node.write_uint16(fp, min_speed); node.write_sint64(fp, price); node.write_sint64(fp, maintenance); node.write_uint16(fp, flags); node.write_uint8 (fp, offset_left); node.write_uint8 (fp, wtyp); uint16 intro_date = obj.get_int("intro_year", DEFAULT_INTRO_YEAR) * 12; intro_date += obj.get_int("intro_month", 1) - 1; node.write_uint16(fp, intro_date); uint16 retire_date = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12; retire_date += obj.get_int("retire_month", 1) - 1; node.write_uint16(fp, retire_date); write_name_and_copyright(fp, node, obj); // add the images slist_tpl keys; if( *obj.get("image[0]") ) { // image[0] is defined. // assume that images are defined in image[number] syntax. parse_images_numbered(keys, obj); } else { // image[0] is not defined. // assume that images are defined in image[direction][state] syntax. parse_images_2d(keys, obj, flags); } imagelist_writer_t::instance()->write_obj(fp, node, keys); // probably add some icons, if defined slist_tpl cursorkeys; const char *c = obj.get("cursor"), *i=obj.get("icon"); cursorkeys.append(c); cursorkeys.append(i); if (*c || *i) { cursorskin_writer_t::instance()->write_obj(fp, node, obj, cursorkeys); } node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/roadsign_writer.h000066400000000000000000000013371520675460500254510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_ROADSIGN_WRITER_H #define DESCRIPTOR_WRITER_ROADSIGN_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class roadsign_writer_t : public obj_writer_t { private: static roadsign_writer_t the_instance; roadsign_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_roadsign; } const char *get_type_name() const OVERRIDE { return "roadsign"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/root_writer.cc000066400000000000000000000336151520675460500247700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../dataobj/tabfile.h" #include "../../utils/searchfolder.h" #include "../obj_desc.h" #include "obj_node.h" #include "obj_writer.h" #include "root_writer.h" using std::string; string root_writer_t::inpath; void root_writer_t::write_header(FILE* fp) { fprintf(fp, "Simutrans object file\n" "Compiled with SimObjects " COMPILER_VERSION "\n\x1A" ); uint32 l = endian(uint32(COMPILER_VERSION_CODE)); fwrite(&l, 1, sizeof(uint32), fp); // Compiler version to check obj_node_t::set_start_offset(ftell(fp)); } // makes pak file(s) void root_writer_t::write(const char* filename, int argc, char* argv[]) { searchfolder_t find; FILE* outfp = NULL; obj_node_t* node = NULL; bool separate = false; string file = find.complete(filename, "pak"); if (file[file.size()-1] == '/') { printf("writing individual files to %s\n", filename); separate = true; } else { outfp = fopen(file.c_str(), "wb"); if (!outfp) { dbg->fatal( "Write pak", "Cannot create destination file %s", filename ); } if (debuglevel >= log_t::LEVEL_WARN) { printf("Writing file %s\n", filename); } write_header(outfp); node = new obj_node_t(this, 0, NULL); } for( int i=0; i==0 || i= log_t::LEVEL_WARN) { printf(" Reading file %s\n", i); } inpath = arg; string::size_type n = inpath.rfind('/'); if(n!=string::npos) { inpath = inpath.substr(0, n + 1); } else { inpath = ""; } while(infile.read(obj)) { if(separate) { string name(filename); name = name + obj.get("obj") + "." + obj.get("name") + ".pak"; outfp = fopen(name.c_str(), "wb"); if (!outfp) { dbg->fatal( "Write pak", "Cannot create destination file %s", filename ); } if (debuglevel >= log_t::LEVEL_WARN) { printf(" Writing file %s\n", name.c_str()); } write_header(outfp); node = new obj_node_t(this, 0, NULL); } obj_writer_t::write(outfp, *node, obj); obj.unused( "#;-/" ); if(separate) { node->check_and_write_header(outfp); delete node; fclose(outfp); } } } else { dbg->warning( "Write pak", "Cannot read %s", i); } } } if (!separate) { node->check_and_write_header(outfp); delete node; fclose(outfp); } } void root_writer_t::write_obj_node_info_t(FILE* outfp, const obj_node_info_t &root) { uint32 type = endian(root.type); uint16 children = endian(root.nchildren); uint16 root_size = endian(uint16(root.size)); fwrite(&type, 4, 1, outfp); fwrite(&children, 2, 1, outfp); if( root.size>=LARGE_RECORD_SIZE ) { root_size = LARGE_RECORD_SIZE; fwrite(&root_size, 2, 1, outfp); uint32 size = endian(root.size); fwrite(&size, 4, 1, outfp); } else { fwrite(&root_size, 2, 1, outfp); } } static bool skip_header(FILE* const f) { for (;;) { int const c = fgetc(f); if (c == EOF) { dbg->error( "skip_header", "reached end of file while skipping header"); return false; } if (c == '\x1A') { return true; } } } bool root_writer_t::do_dump(const char* open_file_name) { FILE *const infp = fopen(open_file_name, "rb"); if (!infp) { return false; } if (!skip_header(infp)) { fclose(infp); return false; } // Compiled Version uint32 version; if (fread(&version, sizeof(version), 1, infp) != 1) { fclose(infp); return false; } printf("File %s (version %d):\n", open_file_name, endian(version)); if (!dump_nodes(infp, 0)) { fclose(infp); return false; } fclose(infp); return true; } // dumps the node structure onto the screen void root_writer_t::dump(int argc, char* argv[]) { for (int i = 0; i < argc; i++) { bool any = false; // this is necessary to avoid the hassle with "./*.pak" otherwise if (strchr(argv[i], '*') == NULL) { any = do_dump(argv[i]); } else { searchfolder_t find; find.search(argv[i], "pak"); for(const char* const& i : find) { any |= do_dump(i); } } if (!any) { dbg->error( "root_writer_t::dump", "Could not read pak file or dir %s (not found or file corrupted)", argv[i] ); } } } bool root_writer_t::do_list(const char *open_file_name) { FILE *const infp = fopen(open_file_name, "rb"); if (!infp) { return false; } if (!skip_header(infp)) { fclose(infp); return false; } // Compiled Version uint32 version; if (fread(&version, sizeof(version), 1, infp) != 1) { fclose(infp); return false; } printf("Contents of file %s (pak version %u):\n", open_file_name, endian(version)); printf("type name nodes size\n" "---------------- ------------------------------ ----- ----------\n"); obj_node_info_t node; if (!obj_node_t::read_node( infp, node ) || fseek(infp, node.size, SEEK_CUR) != 0) { fclose(infp); return false; } for (int i = 0; i < node.nchildren; i++) { if (!list_nodes(infp)){ fclose(infp); return false; } } fclose(infp); return true; } // list the content of a file void root_writer_t::list(int argc, char* argv[]) { for (int i = 0; i < argc; i++) { bool any = false; // this is necessary to avoid the hassle with "./*.pak" otherwise if (strchr(argv[i],'*') == NULL) { any = do_list(argv[i]); } else { searchfolder_t find; find.search(argv[i], "pak"); for(const char* const& i : find) { any |= do_list(i); } } if (!any) { dbg->error( "root_writer_t::list", "Could not read pak file or dir %s (not found or file corrupted)", argv[i] ); } } } bool root_writer_t::do_copy(FILE* outfp, obj_node_info_t& root, const char* open_file_name) { bool any = false; if (FILE* const infp = fopen(open_file_name, "rb")) { if (skip_header(infp)) { // Compiled Version check (since the ancient ending was also pak) uint32 version; fread(&version, sizeof(version), 1, infp); if (endian(version) <= COMPILER_VERSION_CODE) { printf(" copying file %s\n", open_file_name); obj_node_info_t info; obj_node_t::read_node( infp, info ); root.nchildren += info.nchildren; copy_nodes(outfp, infp, info); any = true; } else { dbg->warning( "root_writer_t::do_copy", "skipping file %s - version mismatch (need same version to merge!)", open_file_name); } } fclose(infp); } return any; } // merges pak files into an archive // void root_writer_t::copy(const char* name, int argc, char* argv[]) { searchfolder_t find; FILE* outfp = NULL; if (strchr(name, '*') == NULL) { // is not a wildcard name outfp = fopen(name, "wb"); } if (outfp == NULL) { name = find.complete(name, "pak").c_str(); outfp = fopen(name, "wb"); } if (!outfp) { dbg->fatal( "Merge", "Cannot open destination file %s", name); } fclose(outfp); if (remove(name) != 0) { dbg->warning("Merge", "Could not delete %s"); } // create temporary file std::string tmpfile_name = name; tmpfile_name += ".tmp"; outfp = fopen(tmpfile_name.c_str(), "wb"); printf("writing to temporary file %s\n", tmpfile_name.c_str()); write_header(outfp); long start = ftell(outfp); // remember position for adding children obj_node_info_t root; root.nchildren = 0; // we will change this later root.size = 0; root.type = obj_root; this->write_obj_node_info_t(outfp, root); for( int i=0; iwarning( "Merge", "Skipping reading from output file"); } } } if (!any) { dbg->warning( "Merge", "file or dir %s not found\n", argv[i]); } } fseek(outfp, start, SEEK_SET); this->write_obj_node_info_t(outfp, root); fclose(outfp); printf("renaming temporary file %s to %s\n", tmpfile_name.c_str(), name); rename(tmpfile_name.c_str(), name); } /* makes single files from a merged file */ void root_writer_t::uncopy(const char* name) { FILE* infp = NULL; if (strchr(name,'*') == NULL) { // is not a wildcard name infp = fopen(name, "rb"); } if (infp == NULL) { searchfolder_t find; name = find.complete(name, "pak").c_str(); infp = fopen(name, "rb"); } if (!infp) { dbg->fatal( "Unmerge", "Cannot open archive file %s\n", name); } if (skip_header(infp)) { // check version of pak format uint32 version; fread(&version, sizeof(version), 1, infp); if (endian(version) <= COMPILER_VERSION_CODE) { // read root node obj_node_info_t root; obj_node_t::read_node( infp, root ); if (root.nchildren == 1) { dbg->error( "Unmerge", "%s is not an archive (aborting)", name); fclose(infp); exit(3); } printf(" found %d files to extract\n\n", root.nchildren); // now iterate over the archive for ( int number=0; numberwarning( "Unmerge", "%s has no name! (using %s) as default", writer.c_str(), (const char *)random_name ); node_name = random_name; } string outfile = writer + "." + node_name + ".pak"; FILE* outfp = fopen(outfile.c_str(), "wb"); if (!outfp) { dbg->error( "Unmerge", "Could not open %s for writing (aborting)", outfile.c_str()); fclose(infp); exit(3); } printf(" writing '%s' ... \n", outfile.c_str()); // now copy the nodes fseek(infp, start_pos, SEEK_SET); write_header(outfp); // write the root for the new pak file obj_node_info_t root; root.nchildren = 1; root.size = 0; root.type = obj_root; write_obj_node_info_t( outfp, root ); copy_nodes(outfp, infp, root); // this advances also the input to the next position fclose(outfp); } } else { dbg->warning( "Unmerge", "Skipping file %s - version mismatch", name); } } fclose(infp); } void root_writer_t::copy_nodes(FILE* outfp, FILE* infp, obj_node_info_t& start) { for( int i=0; ifatal( "Write dat", "Cannot create destination file %s", filename ); } if (debuglevel >= log_t::LEVEL_WARN) { printf("Writing file %s\n", filename); } } for( int i=0; i==0 || i= log_t::LEVEL_WARN) { printf(" Reading file %s\n", i); } inpath = arg; string::size_type n = inpath.rfind('/'); if(n!=string::npos) { inpath = inpath.substr(0, n + 1); } else { inpath = ""; } if(generate_name) { string name(i); name.replace(name.size() - 3, 3, "expanded.dat"); outfp = fopen(name.c_str(), "wb"); if (!outfp) { dbg->fatal( "Write pak", "Cannot create destination file %s", name.c_str() ); } if (debuglevel >= log_t::LEVEL_WARN) { printf(" Writing file %s\n", name.c_str()); } } // fprintf(outfp, "# Expanded file by makeobj\n"); while(infile.read(obj, outfp)) { fprintf(outfp, "---\n"); } if(generate_name) { fclose(outfp); } } else { dbg->warning( "Write dat", "Cannot read %s", i); } } } if(!generate_name) { fclose(outfp); } } simutrans-124.5/src/simutrans/descriptor/writer/root_writer.h000066400000000000000000000034131520675460500246230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_ROOT_WRITER_H #define DESCRIPTOR_WRITER_ROOT_WRITER_H #include #include #include "obj_writer.h" #include "../objversion.h" struct obj_node_info_t; class root_writer_t : public obj_writer_t { private: static root_writer_t the_instance; static std::string inpath; root_writer_t() { register_writer(false); } void copy_nodes(FILE* outfp, FILE* infp, obj_node_info_t& info); void write_header(FILE* fp); void write_obj_node_info_t(FILE* outfp, const obj_node_info_t &root); public: void capabilites(); static root_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_root; } const char *get_type_name() const OVERRIDE { return "root"; } void write(const char* name, int argc, char* argv[]); void dump(int argc, char* argv[]); void list(int argc, char* argv[]); void copy(const char* name, int argc, char* argv[]); /** * @brief Expands makeobj pre-processor stuff * * Dat files can contain 'minified' pre-processor-like features, this * function will run the dat on the tabfile reader and return write * its output to a $filename-expanded.dat file. * * @param name Filename of the output file or folder * @param argc Number of input files * @param argv List of input files' paths */ void expand_dat(const char* name, int argc, char* argv[]); /* makes single files from a merged file */ void uncopy(const char* name); static const std::string & get_inpath() { return inpath; } private: bool do_copy(FILE* outfp, obj_node_info_t& root, const char* open_file_name); bool do_dump(const char* open_file_name); bool do_list(const char* open_file_name); }; #endif simutrans-124.5/src/simutrans/descriptor/writer/sim_writer.cc000066400000000000000000000055641520675460500245770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "text_writer.h" #include "image_writer.h" #include "imagelist_writer.h" #include "imagelist2d_writer.h" #include "bridge_writer.h" #include "tunnel_writer.h" #include "building_writer.h" #include "roadsign_writer.h" #include "citycar_writer.h" #include "pedestrian_writer.h" #include "crossing_writer.h" #include "factory_writer.h" #include "good_writer.h" #include "ground_writer.h" #include "groundobj_writer.h" #include "way_writer.h" #include "way_obj_writer.h" #include "root_writer.h" #include "xref_writer.h" #include "skin_writer.h" #include "sound_writer.h" #include "tree_writer.h" #include "vehicle_writer.h" /** * static data * * These classes are self-registering. The linker will not notify if there is a missing class, since it * may be also instantiated in the class itself. Therefore, all classes MUST be declared here to force * the linking and raise an error if we lack any of them. */ stringhashtable_tpl* obj_writer_t::writer_by_name = NULL; inthashtable_tpl *obj_writer_t::writer_by_type = NULL; text_writer_t text_writer_t::the_instance; image_writer_t image_writer_t::the_instance; imagelist_writer_t imagelist_writer_t::the_instance; imagelist2d_writer_t imagelist2d_writer_t::the_instance; root_writer_t root_writer_t::the_instance; xref_writer_t xref_writer_t::the_instance; sound_writer_t sound_writer_t::the_instance; menuskin_writer_t menuskin_writer_t::the_instance; cursorskin_writer_t cursorskin_writer_t::the_instance; symbolskin_writer_t symbolskin_writer_t::the_instance; miscimages_writer_t miscimages_writer_t::the_instance; goods_writer_t goods_writer_t::the_instance; ground_writer_t ground_writer_t::the_instance; way_writer_t way_writer_t::the_instance; way_obj_writer_t way_obj_writer_t::the_instance; crossing_writer_t crossing_writer_t::the_instance; bridge_writer_t bridge_writer_t::the_instance; tunnel_writer_t tunnel_writer_t::the_instance; field_writer_t field_writer_t::the_instance; groundobj_writer_t groundobj_writer_t::the_instance; smoke_writer_t smoke_writer_t::the_instance; building_writer_t building_writer_t::the_instance; tile_writer_t tile_writer_t::the_instance; factory_writer_t factory_writer_t::the_instance; factory_supplier_writer_t factory_supplier_writer_t::the_instance; factory_product_writer_t factory_product_writer_t::the_instance; factory_smoke_writer_t factory_smoke_writer_t::the_instance; factory_field_group_writer_t factory_field_group_writer_t::the_instance; factory_field_class_writer_t factory_field_class_writer_t::the_instance; vehicle_writer_t vehicle_writer_t::the_instance; roadsign_writer_t roadsign_writer_t::the_instance; citycar_writer_t citycar_writer_t::the_instance; pedestrian_writer_t pedestrian_writer_t::the_instance; tree_writer_t tree_writer_t::the_instance; simutrans-124.5/src/simutrans/descriptor/writer/skin_writer.cc000066400000000000000000000020471520675460500247440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simdebug.h" #include "../../dataobj/tabfile.h" #include "../skin_desc.h" #include "obj_node.h" #include "text_writer.h" #include "imagelist_writer.h" #include "skin_writer.h" using std::string; void skin_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { slist_tpl keys; for( int i = 0; ; i++ ) { char buf[40]; sprintf(buf, "image[%d]", i); string str = obj.get(buf); if( str.empty() ) { break; } if( debuglevel > 2 ) { printf("%10sImage[%3u] =%s\n", " ", i, str.c_str()); } keys.append(str); } write_obj(fp, parent, obj, keys); } void skin_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj, const slist_tpl& imagekeys) { slist_tpl keys; obj_node_t node(this, 0, &parent); write_name_and_copyright(fp, node, obj); imagelist_writer_t::instance()->write_obj(fp, node, imagekeys); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/skin_writer.h000066400000000000000000000055321520675460500246100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_SKIN_WRITER_H #define DESCRIPTOR_WRITER_SKIN_WRITER_H #include "obj_writer.h" #include "../objversion.h" #include "../../tpl/slist_tpl.h" #include class skin_writer_t : public obj_writer_t { protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj, const slist_tpl& imagekeys); obj_type get_type() const OVERRIDE = 0; const char* get_type_name() const OVERRIDE = 0; }; class menuskin_writer_t : public skin_writer_t { private: static menuskin_writer_t the_instance; menuskin_writer_t() { register_writer(true); } public: static menuskin_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_menu; } const char* get_type_name() const OVERRIDE { return "menu"; } }; class cursorskin_writer_t : public skin_writer_t { private: static cursorskin_writer_t the_instance; cursorskin_writer_t() { register_writer(true); } public: static cursorskin_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_cursor; } const char* get_type_name() const OVERRIDE { return "cursor"; } }; class symbolskin_writer_t : public skin_writer_t { private: static symbolskin_writer_t the_instance; symbolskin_writer_t() { register_writer(true); } public: static symbolskin_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_symbol; } const char* get_type_name() const OVERRIDE { return "symbol"; } }; class smoke_writer_t : public skin_writer_t { private: static smoke_writer_t the_instance; smoke_writer_t() { register_writer(true); } public: static smoke_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_smoke; } const char* get_type_name() const OVERRIDE { return "smoke"; } }; class field_writer_t : public skin_writer_t { private: static field_writer_t the_instance; field_writer_t() { register_writer(true); } public: static field_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_field; } const char* get_type_name() const OVERRIDE { return "field"; } }; /* * Used for images needed by the game but not yet integrated as real objects */ class miscimages_writer_t : public skin_writer_t { private: static miscimages_writer_t the_instance; miscimages_writer_t() { register_writer(true); } public: static miscimages_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_miscimages; } const char* get_type_name() const OVERRIDE { return "misc"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/sound_writer.cc000066400000000000000000000016071520675460500251310ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "../sound_desc.h" #include "text_writer.h" #include "sound_writer.h" void sound_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { // eventual direct name input std::string str = obj.get("sound_name"); uint16 len = str.size(); obj_node_t node(this, 6+len+1, &parent); write_name_and_copyright(fp, node, obj); // Version needs high bit set as trigger -> this is required // as marker because formerly nodes were unversioned node.write_version(fp, 2); // for compatibility reasons; the old nr of a sound node.write_uint16(fp, (uint16)obj.get_int("sound_nr", NO_SOUND)); node.write_uint16(fp, len); node.write_bytes(fp, len + 1, str.c_str()); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/sound_writer.h000066400000000000000000000013121520675460500247640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_SOUND_WRITER_H #define DESCRIPTOR_WRITER_SOUND_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class sound_writer_t : public obj_writer_t { private: static sound_writer_t the_instance; sound_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_sound; } const char* get_type_name() const OVERRIDE { return "sound"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/text_writer.cc000066400000000000000000000014771520675460500247720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../text_desc.h" #include "obj_node.h" #include "text_writer.h" void text_writer_t::write_obj(FILE* outfp, obj_node_t& parent, const char* text) { if (!text) { text = ""; } const size_t len = strlen(text); obj_node_t node(this, len + 1, &parent); node.write_bytes(outfp, len+1, text); node.check_and_write_header(outfp); } bool text_writer_t::dump_node(FILE* infp, const obj_node_info_t& node) { if (!obj_writer_t::dump_node(infp, node)) { return false; } char *buf = new char[node.size+1]; if (!buf) { return false; } if (fread(buf, node.size, 1, infp) != 1) { delete[] buf; return false; } buf[node.size] = 0; printf(" '%s'", buf); delete[] buf; return true; } simutrans-124.5/src/simutrans/descriptor/writer/text_writer.h000066400000000000000000000013531520675460500246250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_TEXT_WRITER_H #define DESCRIPTOR_WRITER_TEXT_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class obj_node_t; class text_writer_t : public obj_writer_t { private: static text_writer_t the_instance; text_writer_t() { register_writer(false); } public: static text_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_text; } const char* get_type_name() const OVERRIDE { return "text"; } bool dump_node(FILE *infp, const obj_node_info_t& node) OVERRIDE; void write_obj(FILE *fp, obj_node_t& parent, const char* text); }; #endif simutrans-124.5/src/simutrans/descriptor/writer/tree_writer.cc000066400000000000000000000034451520675460500247420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "text_writer.h" #include "imagelist2d_writer.h" #include "get_climate.h" #include "tree_writer.h" using std::string; void tree_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { climate_bits allowed_climates; const char *climate_str = obj.get("climates"); if (climate_str) { allowed_climates = get_climate_bits(climate_str); } else { printf("WARNING: old syntax without climates!\n"); allowed_climates = all_but_arctic_climate; } // seasons = 1: no seasons // seasons = 2: 0=summer, 1=winter // seasons = 4, normal four seasons, starting with summer // seasons = 5, normal four seasons and snowy image uint8 const number_of_seasons = obj.get_int("seasons", 1); uint8 const distribution_weight = obj.get_int("distributionweight", 3); slist_tpl > keys; for (unsigned int age = 0; age < 5; age++) { keys.append(); for (int seasons = 0; seasons < number_of_seasons; seasons++) { char buf[40]; // Images of the tree // age is 1..5 (usually five stages, seasons is the seaons sprintf(buf, "image[%d][%d]", age, seasons); string str = obj.get(buf); if (str.empty()) { // else missing image dbg->fatal( "Tree", "Missing %s!", buf); } keys.at(age).append(str); } } obj_node_t node(this, 6, &parent); write_name_and_copyright(fp, node, obj); imagelist2d_writer_t::instance()->write_obj(fp, node, keys); // write version data node.write_version(fp, 2); node.write_uint16(fp, allowed_climates); node.write_uint8(fp, distribution_weight); node.write_uint8(fp, number_of_seasons); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/tree_writer.h000066400000000000000000000013031520675460500245730ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_TREE_WRITER_H #define DESCRIPTOR_WRITER_TREE_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class tree_writer_t : public obj_writer_t { private: static tree_writer_t the_instance; tree_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_tree; } const char* get_type_name() const OVERRIDE { return "tree"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/tunnel_writer.cc000066400000000000000000000067501520675460500253120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "../../dataobj/ribi.h" #include "../tunnel_desc.h" #include "obj_node.h" #include "text_writer.h" #include "xref_writer.h" #include "imagelist_writer.h" #include "skin_writer.h" #include "get_waytype.h" #include "tunnel_writer.h" using std::string; void tunnel_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { const sint32 topspeed = obj.get_int("topspeed", 999); const sint64 price = obj.get_int64("cost", 0); const sint64 maintenance = obj.get_int64("maintenance", 1000); const uint8 wtyp = get_waytype(obj.get("waytype")); const uint16 axle_load = obj.get_int("axle_load", 9999); // timeline uint16 intro_date = obj.get_int("intro_year", DEFAULT_INTRO_YEAR) * 12; intro_date += obj.get_int("intro_month", 1) - 1; uint16 retire_date = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12; retire_date += obj.get_int("retire_month", 1) - 1; // predefined string for directions static const char* const indices[] = { "n", "s", "e", "w" }; static const char* const add[] = { "", "l", "r", "m" }; char buf[40]; // Check for seasons sint8 number_of_seasons = 0; sprintf(buf, "%simage[%s][1]", "front", indices[0]); string str = obj.get(buf); if( str.size() != 0 ) { // Snow images are present. number_of_seasons = 1; } // Check for broad portals uint8 number_portals = 1; sprintf(buf, "%simage[%s%s][0]", "front", indices[0], add[1]); str = obj.get(buf); if (str.empty()) { // Test short version sprintf(buf, "%simage[%s%s]", "front", indices[0], add[1]); str = obj.get(buf); } if( str.size() != 0 ) { number_portals = 4; } obj_node_t node(this, 32, &parent); node.write_version(fp, 6); node.write_sint32(fp, topspeed); node.write_sint64(fp, price); node.write_sint64(fp, maintenance); node.write_uint8 (fp, wtyp); node.write_uint16(fp, intro_date); node.write_uint16(fp, retire_date); node.write_uint16(fp, axle_load); node.write_sint8 (fp, number_of_seasons); write_name_and_copyright(fp, node, obj); // and now the images slist_tpl backkeys; slist_tpl frontkeys; slist_tpl cursorkeys; cursorkeys.append(string(obj.get("cursor"))); cursorkeys.append(string(obj.get("icon"))); for( uint8 season = 0; season <= number_of_seasons; season++ ) { for( uint8 pos = 0; pos < 2; pos++ ) { for( uint8 j = 0; j < number_portals; j++ ) { for( uint8 i = 0; i < 4; i++ ) { sprintf(buf, "%simage[%s%s][%d]", pos ? "back" : "front", indices[i], add[j], season); string str = obj.get(buf); if (str.empty() && season == 0) { // Test also the short version. sprintf(buf, "%simage[%s%s]", pos ? "back" : "front", indices[i], add[j]); str = obj.get(buf); } (pos ? &backkeys : &frontkeys)->append(str); } } } imagelist_writer_t::instance()->write_obj(fp, node, backkeys); imagelist_writer_t::instance()->write_obj(fp, node, frontkeys); backkeys.clear(); frontkeys.clear(); if(season == 0) { cursorskin_writer_t::instance()->write_obj(fp, node, obj, cursorkeys); } } str = obj.get("way"); if (!str.empty()) { xref_writer_t::instance()->write_obj(fp, node, obj_way, str.c_str(), false); node.write_sint8(fp, 1); } else { node.write_sint8(fp, 0); } node.write_sint8(fp, (number_portals==4)); cursorkeys.clear(); node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/tunnel_writer.h000066400000000000000000000013211520675460500251410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_TUNNEL_WRITER_H #define DESCRIPTOR_WRITER_TUNNEL_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class tunnel_writer_t : public obj_writer_t { private: static tunnel_writer_t the_instance; tunnel_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_tunnel; } const char* get_type_name() const OVERRIDE { return "tunnel"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/vehicle_writer.cc000066400000000000000000000226661520675460500254300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../../utils/simstring.h" #include "../../dataobj/tabfile.h" #include "../vehicle_desc.h" #include "../sound_desc.h" #include "obj_pak_exception.h" #include "obj_node.h" #include "text_writer.h" #include "xref_writer.h" #include "imagelist_writer.h" #include "imagelist2d_writer.h" #include "get_waytype.h" #include "vehicle_writer.h" using std::string; /** * Calculate numeric engine type from engine type string */ static vehicle_desc_t::engine_t get_engine_type(char const* const engine_type) { vehicle_desc_t::engine_t uv8 = vehicle_desc_t::diesel; if (!STRICMP(engine_type, "diesel")) { uv8 = vehicle_desc_t::diesel; } else if (!STRICMP(engine_type, "electric")) { uv8 = vehicle_desc_t::electric; } else if (!STRICMP(engine_type, "steam")) { uv8 = vehicle_desc_t::steam; } else if (!STRICMP(engine_type, "bio")) { uv8 = vehicle_desc_t::bio; } else if (!STRICMP(engine_type, "sail")) { uv8 = vehicle_desc_t::sail; } else if (!STRICMP(engine_type, "fuel_cell")) { uv8 = vehicle_desc_t::fuel_cell; } else if (!STRICMP(engine_type, "hydrogene")) { uv8 = vehicle_desc_t::hydrogene; } else if (!STRICMP(engine_type, "battery")) { uv8 = vehicle_desc_t::battery; } else if (!STRICMP(engine_type, "unknown")) { uv8 = vehicle_desc_t::unknown; } // printf("Engine type %s -> %d\n", engine_type, uv8); return uv8; } /** * Writes vehicle node data to file */ void vehicle_writer_t::write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) { int i; uint8 uv8; uint32 total_len = 57; // version 13 size // must be done here since it may affect the len of the header! string sound_str = ltrim( obj.get("sound") ); sint8 sound_id = NO_SOUND; if (!sound_str.empty()) { // ok, there is some sound sound_id = atoi(sound_str.c_str()); if (sound_id == 0 && sound_str[0] == '0') { sound_id = 0; sound_str = ""; } else if (sound_id != 0) { // old style id sound_str = ""; } if (!sound_str.empty()) { sound_id = LOAD_SOUND; total_len += sound_str.size() + 1; } } obj_node_t node(this, total_len, &parent); write_name_and_copyright(fp, node, obj); node.write_version(fp, 13); // Price of this vehicle in cent const sint64 price = obj.get_int64("cost", 0); node.write_sint64(fp, price); // Maximum payload of this vehicle const uint16 capacity = obj.get_int("payload", 0); node.write_uint16(fp, capacity); // ms per loading/unloading everything const uint32 loading_time = obj.get_int_clamped("loading_time", 1000, 0, 0x7FFFFFFF); node.write_uint32(fp, loading_time); // Top speed of this vehicle. Must be greater than 0 const uint16 topspeed = obj.get_int("speed", 0); node.write_uint16(fp, topspeed); // Total weight of this vehicle in tons const char *weight_str = obj.get("weight"); uint32 weight = (uint32)(atof( weight_str )*1000.0 + 0.5); node.write_uint32(fp, weight); // axle_load (determine ways usage) const uint16 axle_load = obj.get_int("axle_load", 0); node.write_uint16(fp, axle_load); // Power of this vehicle in KW const uint32 power = obj.get_int("power", 0); node.write_uint32(fp, power); // Running costs, given in cent per square const sint64 running_cost = obj.get_int64("runningcost", 0); node.write_sint64(fp, running_cost); // monthly maintenance sint64 fixed_cost = obj.get_int64("fixed_cost", INT64_MAX); if( fixed_cost == INT64_MAX ) { fixed_cost = obj.get_int64("maintenance", 0); } node.write_sint64(fp, fixed_cost); // Introduction date (year * 12 + month) uint16 intro_date = obj.get_int("intro_year", DEFAULT_INTRO_YEAR) * 12 + obj.get_int("intro_month", 1)-1; node.write_uint16(fp, intro_date); // retire date (year * 12 + month) uint16 retire_date = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12 + obj.get_int("retire_month", 1)-1; node.write_uint16(fp, retire_date); // Engine gear (power multiplier) uint16 gear = (obj.get_int("gear", 100) * 64) / 100; node.write_uint16(fp, gear); // Type of way this vehicle drives on char const* const waytype_name = obj.get("waytype"); waytype_t const waytype = get_waytype(waytype_name); uv8 = waytype != overheadlines_wt ? waytype : track_wt; node.write_uint8(fp, uv8); // sound id byte node.write_sint8(fp, sound_id); // engine if (waytype == overheadlines_wt) { // compatibility for old style DAT files uv8 = vehicle_desc_t::electric; } else { const char* engine_type = obj.get("engine_type"); uv8 = get_engine_type(engine_type); } node.write_uint8(fp, uv8); // the length (default 8) uint8 length = obj.get_int("length", 8); node.write_uint8(fp, length); // The freight type const char* freight = obj.get("freight"); if (!*freight) { freight = "None"; } xref_writer_t::instance()->write_obj(fp, node, obj_good, freight, true); xref_writer_t::instance()->write_obj(fp, node, obj_smoke, obj.get("smoke"), false); // Now comes the Image-list static const char* const dir_codes[] = { "s", "w", "sw", "se", "n", "e", "ne", "nw" }; slist_tpl emptykeys; slist_tpl > freightkeys; slist_tpl freightkeys_old; string str; int freight_image_type = 0; bool has_8_images = false; // first: find out how many freight? for (i = 0; i < 127; i++) { char buf[40]; sprintf(buf, "freightimage[%d][%s]", i, dir_codes[0]); str = obj.get(buf); if (str.empty()) { freight_image_type = i; break; } } // now load the images strings for (i = 0; i < 8; i++) { char buf[40]; // Empty vehicle image for direction, direction in "s", "w", "sw", "se", unsymmetrical vehicles need also "n", "e", "ne", "nw" sprintf(buf, "emptyimage[%s]", dir_codes[i]); str = obj.get(buf); if (!str.empty()) { emptykeys.append(str); if (i >= 4) { has_8_images = true; } } else { // stop when empty string is found break; } if (freight_image_type == 0) { // a single freight image // old style definition - just [direction] sprintf(buf, "freightimage[%s]", dir_codes[i]); str = obj.get(buf); if (!str.empty()) { freightkeys_old.append(str); } } else { freightkeys.append(); for(int freight = 0; freight < freight_image_type; freight++) { sprintf(buf, "freightimage[%d][%s]", freight, dir_codes[i]); str = obj.get(buf); if (str.empty()) { dbg->fatal( "Vehicle", "Missing freightimage[%d][%s]!", freight, dir_codes[i]); } freightkeys.at(i).append(str); } } } // added more error checks if (has_8_images && emptykeys.get_count() < 8) { dbg->fatal( "Vehicle", "Missing images (must be either 4 or 8 directions (but %i found)!)", emptykeys.get_count()); } if (!freightkeys_old.empty() && emptykeys.get_count() != freightkeys_old.get_count()) { dbg->fatal( "Vehicle", "Missing freigthimages (must be either 4 or 8 directions (but %i found)!)", freightkeys_old.get_count()); } imagelist_writer_t::instance()->write_obj(fp, node, emptykeys); if (freight_image_type > 0) { imagelist2d_writer_t::instance()->write_obj(fp, node, freightkeys); } else { if (freightkeys_old.get_count() == emptykeys.get_count()) { imagelist_writer_t::instance()->write_obj(fp, node, freightkeys_old); } else { // really empty list ... xref_writer_t::instance()->write_obj(fp, node, obj_imagelist, "", false); } } // // following/leader vehicle constrains // uint8 leader_count = 0; bool found; do { char buf[40]; // Constraints for previous vehicles, "none" means only suitable at front of an convoi sprintf(buf, "constraint[prev][%d]", leader_count); str = obj.get(buf); found = !str.empty(); if (found) { if (!STRICMP(str.c_str(), "none")) { str = ""; } xref_writer_t::instance()->write_obj(fp, node, obj_vehicle, str.c_str(), false); leader_count++; } } while (found); uint8 trailer_count = 0; do { char buf[40]; // Constraints for next vehicle, "none" to disallow any followers sprintf(buf, "constraint[next][%d]", trailer_count); str = obj.get(buf); found = !str.empty(); if (found) { if (!STRICMP(str.c_str(), "none")) { str = ""; } xref_writer_t::instance()->write_obj(fp, node, obj_vehicle, str.c_str(), false); trailer_count++; } } while (found); // multiple freight image types - define what good uses each index // good without index will be an error for (i = 0; i <= freight_image_type; i++) { char buf[40]; sprintf(buf, "freightimagetype[%d]", i); str = obj.get(buf); if (i == freight_image_type) { // check for superfluous definitions if (str.size() > 0) { dbg->warning( obj_writer_t::last_name, "More freightimagetype (%i) than freight_images (%i)!", i, freight_image_type); fflush(NULL); } break; } if (str.size() == 0) { dbg->fatal( obj_writer_t::last_name, "Missing freightimagetype[%i] for %i freight_images!", i, freight_image_type + 1); } xref_writer_t::instance()->write_obj(fp, node, obj_good, str.c_str(), false); } // if no index defined then add default as vehicle good // if not using freight images then store zero string if (freight_image_type > 0) { xref_writer_t::instance()->write_obj(fp, node, obj_good, freight, false); } node.write_sint8(fp, leader_count); node.write_sint8(fp, trailer_count); node.write_uint8(fp, (uint8) freight_image_type); sint8 sound_str_len = sound_str.size(); if (sound_str_len > 0) { node.write_sint8 (fp, sound_str_len); node.write_bytes(fp, sound_str_len, sound_str.c_str()); } node.check_and_write_header(fp); } simutrans-124.5/src/simutrans/descriptor/writer/vehicle_writer.h000066400000000000000000000013761520675460500252650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_VEHICLE_WRITER_H #define DESCRIPTOR_WRITER_VEHICLE_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class vehicle_writer_t : public obj_writer_t { private: static vehicle_writer_t the_instance; vehicle_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: /// Writes vehicle node data to file void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_vehicle; } const char *get_type_name() const OVERRIDE { return "vehicle"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/way_obj_writer.cc000066400000000000000000000072161520675460500254350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "obj_node.h" #include "obj_pak_exception.h" #include "../way_obj_desc.h" #include "text_writer.h" #include "imagelist_writer.h" #include "skin_writer.h" #include "get_waytype.h" #include "way_obj_writer.h" using std::string; /** * Write a way object (lamps, overheadwires) */ void way_obj_writer_t::write_obj(FILE* outfp, obj_node_t& parent, tabfileobj_t& obj) { static const char* const ribi_codes[26] = { "-", "n", "e", "ne", "s", "ns", "se", "nse", "w", "nw", "ew", "new", "sw", "nsw", "sew", "nsew", "nse1", "new1", "nsw1", "sew1", "nsew1", // different crossings: northwest/southeast is oneway "nse2", "new2", "nsw2", "sew2", "nsew2", }; int ribi, slope; const sint64 price = obj.get_int64("cost", 100); const sint64 maintenance = obj.get_int64("maintenance", 100); const sint32 topspeed = obj.get_int ("topspeed", 999); uint16 intro = obj.get_int("intro_year", DEFAULT_INTRO_YEAR) * 12; intro += obj.get_int("intro_month", 1) - 1; uint16 retire = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12; retire += obj.get_int("retire_month", 1) - 1; uint8 wtyp = get_waytype(obj.get("waytype")); uint8 own_wtyp = get_waytype(obj.get("own_waytype")); obj_node_t node(this, 28, &parent); node.write_version(outfp, 2); node.write_sint64(outfp, price); node.write_sint64(outfp, maintenance); node.write_sint32(outfp, topspeed); node.write_uint16(outfp, intro); node.write_uint16(outfp, retire); node.write_uint8 (outfp, wtyp); node.write_uint8 (outfp, own_wtyp); write_name_and_copyright(outfp, node, obj); slist_tpl front_list; slist_tpl back_list; for (ribi = 0; ribi < lengthof(ribi_codes); ribi++) { char buf[40]; sprintf(buf, "frontimage[%s]", ribi_codes[ribi]); string str = obj.get(buf); front_list.append(str); sprintf(buf, "backimage[%s]", ribi_codes[ribi]); string str2 = obj.get(buf); back_list.append(str2); } imagelist_writer_t::instance()->write_obj(outfp, node, front_list); imagelist_writer_t::instance()->write_obj(outfp, node, back_list); front_list.clear(); back_list.clear(); for( slope = 3; slope <= 12; slope += 3 ) { char buf[40]; sprintf( buf, "frontimageup[%d]", slope ); string str = obj.get(buf); front_list.append(str); sprintf( buf, "backimageup[%d]", slope ); string str2 = obj.get(buf); back_list.append(str2); } for( slope = 3; slope <= 12; slope += 3 ) { char buf[40]; sprintf( buf, "frontimageup2[%d]", slope ); string str = obj.get(buf); if( !str.empty() ) { front_list.append(str); } sprintf( buf, "backimageup2[%d]", slope ); string str2 = obj.get(buf); if( !str2.empty() ) { back_list.append(str2); } } imagelist_writer_t::instance()->write_obj(outfp, node, front_list); imagelist_writer_t::instance()->write_obj(outfp, node, back_list); front_list.clear(); back_list.clear(); for (ribi = 3; ribi <= 12; ribi += 3) { char buf[40]; sprintf(buf, "frontdiagonal[%s]", ribi_codes[ribi]); string str = obj.get(buf); front_list.append(str); sprintf(buf, "backdiagonal[%s]", ribi_codes[ribi]); string str2 = obj.get(buf); back_list.append(str2); } imagelist_writer_t::instance()->write_obj(outfp, node, front_list); imagelist_writer_t::instance()->write_obj(outfp, node, back_list); slist_tpl cursorkeys; cursorkeys.append(string(obj.get("cursor"))); cursorkeys.append(string(obj.get("icon"))); cursorskin_writer_t::instance()->write_obj(outfp, node, obj, cursorkeys); node.check_and_write_header(outfp); } simutrans-124.5/src/simutrans/descriptor/writer/way_obj_writer.h000066400000000000000000000014051520675460500252710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_WAY_OBJ_WRITER_H #define DESCRIPTOR_WRITER_WAY_OBJ_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class way_obj_writer_t : public obj_writer_t { private: static way_obj_writer_t the_instance; way_obj_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE *fp) const OVERRIDE { return name_from_next_node(fp); } public: /// Write a way-object description node void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_way_obj; } const char *get_type_name() const OVERRIDE { return "way-object"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/way_writer.cc000066400000000000000000000150661520675460500246050ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../dataobj/tabfile.h" #include "../../utils/simstring.h" #include "obj_node.h" #include "obj_pak_exception.h" #include "../way_desc.h" #include "text_writer.h" #include "imagelist_writer.h" #include "skin_writer.h" #include "get_waytype.h" #include "way_writer.h" using std::string; /** * Write a waytype description node */ void way_writer_t::write_obj(FILE* outfp, obj_node_t& parent, tabfileobj_t& obj) { static const char* const ribi_codes[26] = { "-", "n", "e", "ne", "s", "ns", "se", "nse", "w", "nw", "ew", "new", "sw", "nsw", "sew", "nsew", "nse1", "new1", "nsw1", "sew1", "nsew1", // different crossings: northwest/southeast is straight "nse2", "new2", "nsw2", "sew2", "nsew2", }; int ribi; static const char *slope_heights[2] = { "", "2" }; static const char *slope_names[4] = { "n", "w", "e", "s" }; static const char* const image_type[] = { "", "front" }; const sint64 price = obj.get_int64("cost", 100); const sint64 maintenance = obj.get_int64("maintenance", 100); const sint32 topspeed = obj.get_int("topspeed", 999); const uint32 max_weight = obj.get_int("max_weight", 999); const uint16 axle_load = obj.get_int("axle_load", 9999); const uint8 clip_below = obj.get_int_clamped("clip_below", 1, 0, 1); // clip ground below (if elevated way) uint16 intro = obj.get_int("intro_year", DEFAULT_INTRO_YEAR) * 12; intro += obj.get_int("intro_month", 1) - 1; uint16 retire = obj.get_int("retire_year", DEFAULT_RETIRE_YEAR) * 12; retire += obj.get_int("retire_month", 1) - 1; uint8 wtyp = get_waytype(obj.get("waytype")); uint8 styp = obj.get_int("system_type", 0); // compatibility conversions if (wtyp == track_wt && styp == 5) { wtyp = monorail_wt; } else if (wtyp == track_wt && styp == 7) { wtyp = tram_wt; } // true to draw as foregrund and not much earlier (default) uint8 draw_as_ding = (obj.get_int("draw_as_ding", 0) == 1); sint8 number_of_seasons = 0; obj_node_t node(this, 37, &parent); node.write_version(outfp, 8); node.write_sint64(outfp, price); node.write_sint64(outfp, maintenance); node.write_sint32(outfp, topspeed); node.write_uint32(outfp, max_weight); node.write_uint16(outfp, intro); node.write_uint16(outfp, retire); node.write_uint16(outfp, axle_load); node.write_uint8 (outfp, wtyp); node.write_uint8 (outfp, styp); node.write_uint8 (outfp, draw_as_ding); node.write_uint8 (outfp, clip_below); slist_tpl keys; char buf[40]; sprintf(buf, "image[%s][0]", ribi_codes[0]); std::string str = obj.get(buf); if (str.empty()) { // no winter images node.write_sint8(outfp, number_of_seasons); write_name_and_copyright(outfp, node, obj); sprintf(buf, "image[%s]", ribi_codes[0]); string str = obj.get(buf); if (str.empty()) { dbg->fatal("way_writer_t::write_obj", "image with label %s missing", buf); } for(size_t backtofront = 0; backtofrontwrite_obj(outfp, node, keys); keys.clear(); for (uint32 h = 0; hwrite_obj(outfp, node, keys); keys.clear(); for (ribi = 3; ribi <= 12; ribi += 3) { char buf[40]; sprintf(buf, "%sdiagonal[%s]", image_type[backtofront], ribi_codes[ribi]); string str = obj.get(buf); keys.append(str); } imagelist_writer_t::instance()->write_obj(outfp, node, keys); keys.clear(); if(backtofront == 0) { slist_tpl cursorkeys; cursorkeys.append(string(obj.get("cursor"))); cursorkeys.append(string(obj.get("icon"))); cursorskin_writer_t::instance()->write_obj(outfp, node, obj, cursorkeys); } } } else { // with winter images sprintf(buf, "image[%s][%d]", ribi_codes[0], number_of_seasons+1); if (!strempty(obj.get(buf))) { number_of_seasons++; } node.write_sint8(outfp, number_of_seasons); write_name_and_copyright(outfp, node, obj); // has switch images for both directions? const uint8 ribinr = *(obj.get("image[new2][0]"))==0 ? 16 : 26; for(size_t backtofront = 0; backtofrontwrite_obj(outfp, node, keys); keys.clear(); for (uint32 h = 0; hwrite_obj(outfp, node, keys); keys.clear(); for (ribi = 3; ribi <= 12; ribi += 3) { char buf[40]; sprintf(buf, "%sdiagonal[%s][%d]", image_type[backtofront], ribi_codes[ribi], season); string str = obj.get(buf); keys.append(str); } imagelist_writer_t::instance()->write_obj(outfp, node, keys); keys.clear(); if(season == 0 && backtofront == 0) { slist_tpl cursorkeys; cursorkeys.append(string(obj.get("cursor"))); cursorkeys.append(string(obj.get("icon"))); cursorskin_writer_t::instance()->write_obj(outfp, node, obj, cursorkeys); } } } } node.check_and_write_header(outfp); } simutrans-124.5/src/simutrans/descriptor/writer/way_writer.h000066400000000000000000000013421520675460500244370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_WAY_WRITER_H #define DESCRIPTOR_WRITER_WAY_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class way_writer_t : public obj_writer_t { private: static way_writer_t the_instance; way_writer_t() { register_writer(true); } protected: std::string get_node_name(FILE* fp) const OVERRIDE { return name_from_next_node(fp); } public: /// Write a waytype descriptor node void write_obj(FILE* fp, obj_node_t& parent, tabfileobj_t& obj) OVERRIDE; obj_type get_type() const OVERRIDE { return obj_way; } const char* get_type_name() const OVERRIDE { return "way"; } }; #endif simutrans-124.5/src/simutrans/descriptor/writer/xref_writer.cc000066400000000000000000000021701520675460500247410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../text_desc.h" #include "obj_node.h" #include "xref_writer.h" void xref_writer_t::write_obj(FILE* outfp, obj_node_t& parent, obj_type type, const char* text, bool fatal) { if (!text) { text = ""; } int len = strlen(text); obj_node_t node( this, sizeof(char) + // Fatal-Flag sizeof(obj_type) + // type of dest node len + 1, // 0-terminated name of dest node &parent ); char c = fatal ? 1 : 0; node.write_uint32(outfp, (uint32)type); node.write_uint8 (outfp, c); node.write_bytes(outfp, len + 1, text); node.check_and_write_header(outfp); } bool xref_writer_t::dump_node(FILE* infp, const obj_node_info_t& node) { if (!obj_writer_t::dump_node(infp, node)) { return false; } if( node.size<5 ) { return false; } char* buf = new char[node.size+1]; buf[node.size] = 0; if (fread(buf, node.size, 1, infp) != 1) { delete[] buf; return false; } printf(" -> %4.4s-node (%s) '%s'", buf, buf[4] ? "required" : "optional", buf+5 ); delete[] buf; return true; } simutrans-124.5/src/simutrans/descriptor/writer/xref_writer.h000066400000000000000000000014061520675460500246040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_WRITER_XREF_WRITER_H #define DESCRIPTOR_WRITER_XREF_WRITER_H #include #include "obj_writer.h" #include "../objversion.h" class obj_node_t; class xref_writer_t : public obj_writer_t { private: static xref_writer_t the_instance; xref_writer_t() { register_writer(false); } public: static xref_writer_t* instance() { return &the_instance; } obj_type get_type() const OVERRIDE { return obj_xref; } const char *get_type_name() const OVERRIDE { return "xref"; } void write_obj(FILE *fp, obj_node_t& parent, obj_type type, const char* text, bool fatal); bool dump_node(FILE *infp, const obj_node_info_t& node) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/descriptor/xref_desc.h000066400000000000000000000006751520675460500227010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DESCRIPTOR_XREF_DESC_H #define DESCRIPTOR_XREF_DESC_H #include "obj_desc.h" #include "objversion.h" class xref_desc_t : public obj_desc_t { public: const char* get_name() const { return name; } using obj_desc_t::operator new; private: obj_type type; bool fatal; char name[]; friend class xref_reader_t; }; #endif simutrans-124.5/src/simutrans/display/000077500000000000000000000000001520675460500200455ustar00rootroot00000000000000simutrans-124.5/src/simutrans/display/clip_num.h000066400000000000000000000025441520675460500220310ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_CLIP_NUM_H #define DISPLAY_CLIP_NUM_H #include "../simtypes.h" /** * Macros to pass clip_num parameter around for * multi-threaded drawing. */ #ifdef MULTI_THREAD #define CLIP_NUM_VAR clip_num #define CLIP_NUM_PDECL const sint8 #define CLIP_NUM_DEFAULT_VALUE 0 #define CLIP_NUM_COMMA , #define CLIP_NUM_DEFAULT_ZERO = CLIP_NUM_DEFAULT_VALUE #define CLIP_NUM_INDEX [clip_num] #else #define CLIP_NUM_VAR #define CLIP_NUM_PDECL #define CLIP_NUM_COMMA #define CLIP_NUM_DEFAULT_VALUE #define CLIP_NUM_DEFAULT_ZERO #define CLIP_NUM_INDEX #endif /// parameter declaration to be used as first parameter #define CLIP_NUM_DEF0 CLIP_NUM_PDECL CLIP_NUM_VAR /// parameter declaration to be used as non-first parameter #define CLIP_NUM_DEF CLIP_NUM_COMMA CLIP_NUM_DEF0 /// parameter declaration to be used as first parameter, no variable name #define CLIP_NUM_DEF_NOUSE0 CLIP_NUM_PDECL /// parameter declaration to be used as non-first parameter, no variable name #define CLIP_NUM_DEF_NOUSE CLIP_NUM_COMMA CLIP_NUM_DEF_NOUSE0 /// pass clip_num as parameter #define CLIP_NUM_PAR CLIP_NUM_COMMA CLIP_NUM_VAR /// default value for passing clip_num around #define CLIP_NUM_DEFAULT CLIP_NUM_COMMA CLIP_NUM_DEFAULT_VALUE #endif simutrans-124.5/src/simutrans/display/font.cc000066400000000000000000000275311520675460500213320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "font.h" #include "../macros.h" #include "../simdebug.h" #include "../sys/simsys.h" #include "../simtypes.h" #include "../utils/simstring.h" #if COLOUR_DEPTH != 0 #include "../dataobj/environment.h" #endif #include #include #include #include #include font_t::glyph_t::glyph_t() : height(0), width(0), advance(0xFF), top(0) { bitmap = 0; } font_t::font_t() : linespace (0), descent(0) { fname[0] = 0; } #if 0 // FIXME /// Decodes a single line of a glyph static void dsp_decode_bdf_data_row(font_t::glyph_t *target, int y, int xoff, int g_width, const char *str) { char buf[3]; buf[0] = str[0]; buf[1] = str[1]; buf[2] = '\0'; uint16 data = (uint16)strtol(buf, NULL, 16)<<8; // read second byte but use only first nibble if( g_width > 8 ) { buf[0] = str[2]; buf[1] = str[3]; buf[2] = '\0'; data |= strtol(buf, NULL, 16); } data >>= xoff; // now store them, and the second nibble store interleaved target->bitmap[y] = data>>8; if( g_width+xoff > 8 ) { target->bitmap[y+GLYPH_BITMAP_HEIGHT] = data; } } /// Reads a single glyph. /// @returns index of glyph successfully read, or -1 on error static sint32 dsp_read_bdf_glyph(FILE *bdf_file, std::vector &data, int glyph_limit, int f_height, int f_desc) { sint32 glyph_nr = 0; int g_width = 0, h = 0, g_desc = 0; int glyph_advance = -1; int xoff = 0; while( !feof(bdf_file) ) { char str[256]; if( fgets(str, sizeof(str), bdf_file)==NULL && !feof(bdf_file) ) { return -1; } // encoding (sint8 number) in decimal if( strstart(str, "ENCODING") ) { glyph_nr = atoi(str + 8); if( glyph_nr <= 0 || glyph_nr >= glyph_limit ) { dbg->error("dsp_read_bdf_glyph", "Unexpected glyph (%i) for %i glyph font!\n", glyph_nr, glyph_limit); glyph_nr = 0; } continue; } // information over size and coding if( strstart(str, "BBX") ) { sscanf(str + 3, "%d %d %d %d", &g_width, &h, &xoff, &g_desc); continue; } // information over size and coding if( strstart(str, "DWIDTH") ) { glyph_advance = atoi(str + 6); continue; } // start if bitmap data if (strstart(str, "BITMAP")) { const int top = f_height + f_desc - h - g_desc; // maximum size GLYPH_HEIGHT pixels h = min(h + top, (int)GLYPH_BITMAP_HEIGHT); // read for height times for (int y = top; y < h; y++) { if( fgets(str, sizeof(str), bdf_file)==NULL && !feof(bdf_file) ) { return -1; } if( y>=0 ) { dsp_decode_bdf_data_row(&data[glyph_nr], y, xoff, g_width, str); } } continue; } // finally add width information (width = 0: not there!) if( strstart(str, "ENDCHAR") ) { uint8 start_h = 0; // find the start offset for( uint8 i=0; iwarning( "dsp_read_bdf_glyph", "BDF warning: Glyph %i has no advance (screen width) assigned!\n", glyph_nr); glyph_advance = g_width + 1; } data[glyph_nr].advance = glyph_advance; // finished return glyph_nr; } } return -1; } bool font_t::load_from_bdf(FILE *bdf_file) { dbg->message("font_t::load_from_bdf", "Loading BDF font '%s'", fname); glyphs.clear(); uint32 f_height = 0; int f_desc = 0; int f_numglyphs = 0; sint32 max_glyph = 0; while( !feof(bdf_file) ) { char str[256]; if( fgets(str, sizeof(str), bdf_file)==NULL && !feof(bdf_file) ) { return false; } if( strstart(str, "FONTBOUNDINGBOX") ) { sscanf(str + 15, "%*d %d %*d %d", &f_height, &f_desc); continue; } if( strstart(str, "CHARS") && str[5] <= ' ' ) { // the characters 0xFFFF and 0xFFFE are guaranteed to be non-unicode characters f_numglyphs = atoi(str + 5) <= 0x100 ? 0x100 : 0xFFFE; glyphs.resize(max(f_numglyphs, 0)); glyphs[(uint32)' '].advance = clamp(f_height / 2, 3u, GLYPH_BITMAP_HEIGHT); continue; } if( strstart(str, "STARTCHAR") && f_numglyphs > 0 ) { const sint32 glyph = dsp_read_bdf_glyph(bdf_file, glyphs, f_numglyphs, f_height, f_desc); max_glyph = max(max_glyph, glyph); continue; } } // ok, was successful? if( f_numglyphs <= 0 ) { return false; } // init default glyph for missing glyphs (just a box) const int real_font_height = min(f_height, (int)GLYPH_BITMAP_HEIGHT); int h = 2; glyphs[0].bitmap[1] = 0x7E; // 0111 1110 for( ; h < real_font_height-2; h++ ) { glyphs[0].bitmap[h] = 0x42; // 0100 0010 } glyphs[0].bitmap[h++] = 0x7E; // 0111 1110 glyphs[0].yoff = 1; // y-offset glyphs[0].width = 7; // real width glyphs[0].advance = 8; linespace = f_height; descent = f_desc; // Use only needed amount glyphs.resize( (uint32)max_glyph+1 ); return true; } #endif #if COLOUR_DEPTH != 0 #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_TRUETYPE_TABLES_H #include FT_BITMAP_H bool font_t::load_from_freetype(const char *fname, int pixel_height) { dbg->message( "font_t::load_from_freetype", "trying to load '%s' in size %d", fname, pixel_height); FT_Library ft_library = NULL; if( FT_Init_FreeType(&ft_library) != FT_Err_Ok ) { dbg->error( "font_t::load_from_freetype", "Freetype initialization failed" ); return false; } // Ok, we guessed something about the filename, now actually load it FT_Face face; if( FT_New_Face( ft_library, fname, 0, &face ) != FT_Err_Ok ) { dbg->warning( "font_t::load_from_freetype", "Cannot load %s", fname ); FT_Done_FreeType( ft_library ); return false; } if( FT_Set_Pixel_Sizes( face, 0, pixel_height ) != FT_Err_Ok ) { dbg->warning( "font_t::load_from_freetype", "Cannot set pixel size %d for %s", pixel_height, fname); // try to find closest available pixel_height int best = -1; for (int i=0; inum_fixed_sizes; i++) { int h = (face->available_sizes[i].y_ppem + 32) >> 6; if (best == -1 || abs(best - pixel_height) > abs(h - pixel_height)) { best = h; } } if (best == -1) { // failed FT_Done_Face(face); FT_Done_FreeType(ft_library); return false; } if( FT_Set_Pixel_Sizes( face, 0, best) != FT_Err_Ok ) { dbg->warning( "font_t::load_from_freetype", "Cannot set pixel size %d for %s", best, fname); } // continue anyway } glyphs.resize(0x10000); ascent = face->size->metrics.ascender/64; linespace = face->size->metrics.height/64; descent = face->size->metrics.descender/64; tstrncpy( this->fname, fname, lengthof(this->fname) ); uint32 num_glyphs = 0; // 8 bit bitmap FT_Bitmap bitmap_8; FT_Bitmap_Init(&bitmap_8); for( uint32 glyph_nr=0; glyph_nr<0xFFFF; glyph_nr++ ) { if (glyph_nr > 0) { uint32 c = FT_Get_Char_Index(face, glyph_nr); if (c == 0) { // glyph not in font => nothing to render glyphs[glyph_nr].advance = 0xFF; continue; } } /* load glyph image into the slot (erase previous one) */ if( FT_Load_Char( face, glyph_nr, FT_LOAD_RENDER) != FT_Err_Ok ) { // glyph not there ... glyphs[glyph_nr].advance = 0xFF; continue; } const FT_Error error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if(error != FT_Err_Ok) { // glyph not there ... glyphs[glyph_nr].advance = 0xFF; continue; } // use only needed amount num_glyphs = glyph_nr+1; /* now render into cache * the bitmap is at slot->bitmap * the glyph base is at slot->bitmap_left, CELL_HEIGHT - slot->bitmap_top */ glyph_t & glyph = glyphs[glyph_nr]; FT_Bitmap *bitmap = &face->glyph->bitmap; bool one_bit_pp = false; // check bit depth, for some reason bdf fonts get bitmaps with 1 bit per pixel if (face->glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) { // need to convert if (FT_Bitmap_Convert(ft_library, &face->glyph->bitmap, &bitmap_8, 4) != FT_Err_Ok) { // should not happen continue; } bitmap = &bitmap_8; one_bit_pp = true; } // set glyph size glyph.height = bitmap->rows; glyph.width = bitmap->width; glyph.advance = (face->glyph->advance.x + 31) / 64; // the bitmaps are all top aligned. Bitmap top is the ascent // above the base line // to find the real top position, we must take the font ascent // and reduce it by the glyph ascent glyph.top = ascent - face->glyph->bitmap_top - 1; glyph.left = face->glyph->bitmap_left; // transform glyph to Simutrans bitmap glyph.bitmap = (uint8*)calloc(glyph.height * glyph.width, 1); if (!one_bit_pp) { // 8 bit alpha per pixel for(int y = 0; y < glyph.height; y++) { for(int x = 0; x < glyph.width; x++) { uint16 alpha = bitmap->buffer[y * bitmap->pitch + x]; // simgraph blend routines want alpha in 0..32 glyph.bitmap[y * glyph.width + x] = (alpha * 32) / 255; } } } else { // 1 bit per pixel for(int y = 0; y < glyph.height; y++) { for(int x = 0; x < glyph.width; x++) { uint16 alpha = bitmap->buffer[y * bitmap->pitch + x]; // simgraph blend routines want alpha in 0..32 glyph.bitmap[y * glyph.width + x] = (alpha * 32); } } } // find out if invalid character if (glyph_nr && glyph.height>0 && glyphs[0].height == glyph.height && glyphs[0].width == glyph.width && memcmp(glyphs[0].bitmap, glyph.bitmap, glyph.height * glyph.width) == 0) { // same bitmap as character zero ?!?! glyph.advance = 0xFF; } } FT_Bitmap_Done(ft_library, &bitmap_8); if(num_glyphs < 0x80) { FT_Done_Face( face ); FT_Done_FreeType( ft_library ); return false; } // hack for not rendered full width space if( glyphs[0x3000].advance == 0xFF && glyphs[0x3001].advance != 0xFF ) { glyphs[0x3000].advance = glyphs[0x3001].advance; glyphs[0x3000].width = 0; glyphs[0x3000].top = 0; } if (glyphs[' '].advance == 0xFF) { glyphs[' '].advance = glyphs['n'].advance; } // Use only needed amount glyphs.resize(num_glyphs); FT_Done_Face( face ); FT_Done_FreeType( ft_library ); return true; } #endif void font_t::print_debug() const { dbg->debug("font_t::print_debug", "Loaded font %s with %i glyphs\n", get_fname(), get_num_glyphs()); dbg->debug("font_t::print_debug", "height: %i, descent: %i", linespace, descent ); /* for(uint8 glyph_nr = ' '; glyph_nr<128; glyph_nr ++) { char msg[128 + GLYPH_BITMAP_HEIGHT * (GLYPH_BITMAP_WIDTH+1)]; // +1 for trailing newline char *c = msg + sprintf(msg, "glyph %c: width %i, top %i\n", glyph_nr, get_glyph_width(glyph_nr), get_glyph_yoffset(glyph_nr) ); for( uint32 y = 0; y < GLYPH_BITMAP_HEIGHT; y++ ) { for( uint32 x = 0; x < (uint32)min(GLYPH_BITMAP_WIDTH, get_glyph_width(glyph_nr)); x++ ) { const uint8 data = get_glyph_bitmap(glyph_nr)[y+(x/CHAR_BIT)*GLYPH_BITMAP_HEIGHT]; const bool bit_set = (data & (0x80>>(x%CHAR_BIT))) != 0; *c++ = bit_set ? '*' : ' '; } *c++ = '\n'; } *c++ = 0; dbg->debug("font_t::print_debug", "glyph data: %s", msg ); } */ } bool font_t::load_from_file(const char *srcfilename) { tstrncpy( fname, srcfilename, lengthof(fname) ); #if COLOUR_DEPTH != 0 bool ok = load_from_freetype( fname, env_t::fontsize ); #if MSG_LEVEL>=4 if( ok ) { print_debug(); } #endif return ok; #else return true; #endif } sint16 font_t::get_glyph_advance(utf32 c) const { if( !is_loaded() ) { return 0; } else if( c >= get_num_glyphs() || glyphs[c].advance == 0xFF ) { return glyphs[0].advance; } return glyphs[c].advance; } const font_t::glyph_t& font_t::get_glyph(utf32 c) const { static glyph_t dummy; if (is_loaded()) { if (c < get_num_glyphs() && glyphs[c].advance < 0xFF) { return glyphs[c]; } else { return glyphs[0]; } } return dummy; } simutrans-124.5/src/simutrans/display/font.h000066400000000000000000000037661520675460500212000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_FONT_H #define DISPLAY_FONT_H #include "../simtypes.h" #include "../utils/unicode.h" #include #include /** * Terminology: * - glyph: display data of a single character * - font: a collection of glyphs (usually called a font face) * - width,height: size of the glyph * - advance: number of pixels between the start of a glyph and the next glyph * (not necessarily equal to width) */ class font_t { public: /// glyph data is stored dense in an array /// 23 rows of bytes, second column for widths between 8 and 16 struct glyph_t { glyph_t(); uint8* bitmap; sint16 height; sint16 width; sint16 advance; sint16 top; sint16 left; }; public: font_t(); // returns true if this character is contained in the font static bool is_char_in_freetype_font(const char* fname, utf16 testchar); public: /// @returns true on success bool load_from_file(const char *fname); bool is_loaded() const { return !glyphs.empty(); } const char *get_fname() const { return fname; } sint16 get_linespace() const { return linespace; } sint16 get_ascent() const { return ascent; } /// @returns true if this is a valid (defined) glyph bool is_valid_glyph(utf32 c) const { return is_loaded() && c < get_num_glyphs() && glyphs[c].advance != 0xFF; } /// @returns size in pixels between the start of this glyph and the next glyph sint16 get_glyph_advance(utf32 c) const; /// @returns glyph data for this utf32 char const glyph_t& get_glyph(utf32 c) const; private: #if COLOUR_DEPTH != 0 /// Load a freetype font bool load_from_freetype(const char *fname, int pixel_height); #endif void print_debug() const; uint32 get_num_glyphs() const { return glyphs.size(); } private: char fname[PATH_MAX]; sint16 linespace; sint16 ascent; sint16 descent; public: // for simgraph has_character() std::vector glyphs; }; #endif simutrans-124.5/src/simutrans/display/scr_coord.h000066400000000000000000000223061520675460500221760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_SCR_COORD_H #define DISPLAY_SCR_COORD_H #include #include "../dataobj/loadsave.h" #include "../simtypes.h" class koord; // Screen coordinate type typedef sint32 scr_coord_val; // Rectangle relations enum rect_relation_t { RECT_RELATION_INSIDE, RECT_RELATION_OVERLAP, RECT_RELATION_OUTSIDE }; // Two dimensional coordinate type class scr_coord { public: scr_coord_val x; scr_coord_val y; // Constructors scr_coord( ) { x = y = 0; } scr_coord( scr_coord_val x_, scr_coord_val y_ ) { x = x_; y=y_; } bool operator ==(const scr_coord& other) const { return ((x-other.x) | (y-other.y)) == 0; } bool operator !=(const scr_coord& other) const { return !(other == *this ); } const scr_coord operator +(const scr_coord& other ) const { return scr_coord( other.x + x, other.y + y); } const scr_coord operator -(const scr_coord& other ) const { return scr_coord( x - other.x, y - other.y ); } void rdwr(loadsave_t *file) { xml_tag_t k( file, "koord" ); file->rdwr_long(x); file->rdwr_long(y); } const scr_coord& operator +=(const scr_coord& other ) { x += other.x; y += other.y; return *this; } const scr_coord& operator -=(const scr_coord& other ) { x -= other.x; y -= other.y; return *this; } void add_offset( scr_coord_val delta_x, scr_coord_val delta_y ) { x += delta_x; y += delta_y; } void set( scr_coord_val x_par, scr_coord_val y_par ) { x = x_par; y = y_par; } inline void clip_lefttop( scr_coord scr_lefttop ) { if (x < scr_lefttop.x) { x = scr_lefttop.x; } if (y < scr_lefttop.y) { y = scr_lefttop.y; } } inline void clip_rightbottom( scr_coord scr_rightbottom ) { if (x > scr_rightbottom.x) { x = scr_rightbottom.x; } if (y > scr_rightbottom.y) { y = scr_rightbottom.y; } } static const scr_coord invalid; private: // conversions to/from koord not allowed anymore scr_coord( const koord); operator koord() const; }; static inline scr_coord operator * (const scr_coord &k, const sint16 m) { return scr_coord(k.x * m, k.y * m); } static inline scr_coord operator / (const scr_coord &k, const sint16 m) { return scr_coord(k.x / m, k.y / m); } // Very simple scr_size struct. class scr_size { public: scr_coord_val w; scr_coord_val h; public: // Constructors scr_size( ) { w = h = 0; } scr_size( scr_coord_val w_par, scr_coord_val h_par) { w = w_par; h = h_par; } operator scr_coord() const { return scr_coord(w,h); } //operator bool() const { return ((w != 0) && (h != 0)); } // or you get silent conversions to int!!! bool operator ==(const scr_size& other) const { return ((w-other.w) | (h-other.h)) == 0; } bool operator !=(const scr_size& other) const { return !(other == *this ); } const scr_size operator +(const scr_size& other ) const { return scr_size( other.w + w, other.h + h); } const scr_size operator -(const scr_size& other ) const { return scr_size( w - other.w, h - other.h ); } const scr_size operator +(const scr_coord& other ) const { return scr_size( other.x + w, other.y + h); } const scr_size operator -(const scr_coord& other ) const { return scr_size( w - other.x, h - other.y ); } const scr_size operator *(const scr_coord_val& i) const { return scr_size(w*i, h*i); } void rdwr(loadsave_t *file) { xml_tag_t k( file, "koord" ); file->rdwr_long(w); file->rdwr_long(h); } inline void clip_lefttop( scr_coord scr_lefttop ) { if (w < scr_lefttop.x) { w = scr_lefttop.x; } if (h < scr_lefttop.y) { h = scr_lefttop.y; } } inline void clip_rightbottom( scr_coord scr_rightbottom ) { if (w > scr_rightbottom.x) { w = scr_rightbottom.x; } if (h > scr_rightbottom.y) { h = scr_rightbottom.y; } } const scr_size& operator +=(const scr_size& other ) { w += other.w; h += other.h; return *this; } const scr_size& operator -=(const scr_size& other ) { w -= other.w; h -= other.h; return *this; } const scr_size& operator +=(const scr_coord& other ) { w += other.x; h += other.y; return *this; } const scr_size& operator -=(const scr_coord& other ) { w -= other.x; h -= other.y; return *this; } static const scr_size invalid; static const scr_size inf; private: // conversions to/from koord not allowed anymore operator koord() const; }; // Rectangle type class scr_rect { public: scr_coord_val x; scr_coord_val y; scr_coord_val w; scr_coord_val h; // to set it in one line ... void set( scr_coord_val x_par, scr_coord_val y_par, scr_coord_val w_par, scr_coord_val h_par ) { x = x_par; y = y_par; w = w_par; h = h_par; } // Constructors scr_rect() { set(0,0,0,0); } scr_rect( const scr_coord& pt ) { set( pt.x, pt.y, 0, 0 ); } scr_rect( const scr_coord& pt, scr_coord_val w, scr_coord_val h ) { set( pt.x, pt.y, w, h ); } scr_rect( const scr_coord& pt, const scr_size& sz ) { set( pt.x, pt.y, sz.w, sz.h ); } scr_rect( scr_coord_val x, scr_coord_val y, scr_coord_val w, scr_coord_val h ) { set( x, y, w, h ); } scr_rect( scr_size size ) { w = size.w; h=size.h; x=0; y=0; } scr_rect( const scr_coord& point1, const scr_coord& point2 ) { set( point1.x, point1.y, point2.x-point1.x, point2.y-point1.y ); } // Type cast operators operator scr_size() const { return scr_size (w,h); } operator scr_coord() const { return scr_coord(x,y); } // Unary operators const scr_rect operator +(const scr_coord& other ) const { scr_rect rect(x + other.x, y + other.y, w, h ); return rect; } const scr_rect operator -(const scr_coord& other ) const { scr_rect rect(x - other.x, y - other.y, w, h ); return rect; } const scr_rect operator +(const scr_size& sz ) const { return scr_rect(x,y, w+sz.w, h+sz.h ); } const scr_rect operator -(const scr_size& sz ) const { return scr_rect(x,y, w-sz.w, h-sz.h ); } // Validation functions bool is_empty() const { return (w|h) == 0; } bool is_valid() const { return !is_empty(); } // Helper functions scr_coord get_pos() const { return scr_coord( x, y ); } void set_pos( const scr_coord point ) { w = (x+w) - point.x; h = (y+h) - point.y; x = point.x; y = point.y; } const scr_coord get_bottomright() const { return scr_coord( x+w, y+h ); } void set_bottomright( const scr_coord& point ) { w = point.x - x; h = point.y - y; } scr_coord_val get_width() const { return w; } scr_coord_val get_right() const { return x+w; } scr_coord_val get_height() const { return h; } scr_coord_val get_bottom() const { return y+h; } void set_right(scr_coord_val right) { w = right-x; } void set_bottom(scr_coord_val bottom) { h = bottom-y; } // now just working on two of the four coordinates void move_to( scr_coord_val x_par, scr_coord_val y_par ) { x = x_par; y = y_par; } void move_to( const scr_coord& point ) { move_to( point.x, point.y ); } // and the other two with the size const scr_size get_size() const { return scr_size( w, h ); } void set_size( const scr_size &sz ) { w = sz.w; h = sz.h; } void set_size( scr_coord_val width, scr_coord_val height) { w = width; h = height; } // add to left, right and top, bottom void expand( scr_coord_val delta_x_par, scr_coord_val delta_y_par ) { x -= delta_x_par; y -= delta_y_par; w += (delta_x_par<<1); h += (delta_y_par<<1); } // Adjust rect to the bounding rect of this and rect. void outer_bounds( const scr_rect &rect ) { scr_coord pt( min(x,rect.x), min(y,rect.y) ); w = max(x+w,rect.x+rect.w)-pt.x; h = max(y+h,rect.y+rect.h)-pt.y; x = pt.x; y = pt.y; } // Enforce a positive width and height void normalize() { if( w<0 ) { x -= w; w = -w; } if( h<0 ) { y -= h; h = -h; } } // point in rect; border (x+w) is still counting as contained bool contains( const scr_coord& pt ) const { return ( x <= pt.x && x+w >= pt.x && y <=pt.y && y+h >= pt.y ); } // Now rect/rect functions: First check if rect is completely surrounded by this // maybe surrounds would be a better name, but contains seems more consistent bool contains( const scr_rect& rect ) const { return ( x <= rect.x && x+w >= rect.x+rect.w && y <= rect.y && y+h >= rect.y+rect.h ); } bool is_overlapping( const scr_rect &rect ) const { return !( (get_bottomright().x < rect.x) || (get_bottomright().y < rect.y) || (rect.get_bottomright().x < x) || (rect.get_bottomright().y < y) ); } rect_relation_t relation( const scr_rect& rect ) const { if( contains(rect) ) { return RECT_RELATION_INSIDE; } else if( is_overlapping( rect ) ) { return RECT_RELATION_OVERLAP; } return RECT_RELATION_OUTSIDE; } /** * reduces the current rect to the intersection area of two rects * in case of no overlap the new size is negative * @note maybe this could rather return a new rect */ void clip( const scr_rect clip_rect ) { x = max(x, clip_rect.x); set_right( min(x+w, clip_rect.x+clip_rect.w) ); y = max(y, clip_rect.y); set_bottom( min(y+h, clip_rect.y+clip_rect.h) ); } bool operator ==(const scr_rect& rect) const { return ( (x-rect.x) | (y-rect.y) | (w-rect.w) | (h-rect.h) ) == 0; } bool operator !=(const scr_rect& rect_par) const { return !(rect_par==*this); } private: // conversions to/from koord not allowed anymore scr_rect( const koord&); operator koord() const; }; #endif simutrans-124.5/src/simutrans/display/simgraph.cc000066400000000000000000000012201520675460500221610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simgraph.h" #include "simgraph0.h" #include "simgraph16.h" const simgraph_t *gfx; scr_coord_val default_font_ascent = 0; // a font height of zero could cause division by zero errors, even though it should not be used in a server scr_coord_val default_font_linespace = 1; const simgraph_t *simgraph_select(simgraph_type_t preferred_type) { switch (preferred_type) { #if COLOUR_DEPTH == 0 case SIMGRAPH_TYPE_NULL: return &g_simgraph0; #else case SIMGRAPH_TYPE_SOFTWARE: return &g_simgraph16; #endif default: return NULL; } } simutrans-124.5/src/simutrans/display/simgraph.h000066400000000000000000000412121520675460500220300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_SIMGRAPH_H #define DISPLAY_SIMGRAPH_H #include "../simcolor.h" #include "../simtypes.h" #include "clip_num.h" #include "simimg.h" #include "scr_coord.h" class image_t; /** * Alignment enum to align controls against each other. * Vertical and horizontal alignment can be masked together * Unused bits are reserved for future use, set to 0. */ enum control_alignments_t { ALIGN_NONE = 0x00, ALIGN_TOP = 0x01, ALIGN_CENTER_V = 0x02, ALIGN_BOTTOM = 0x03, ALIGN_LEFT = 0x04, ALIGN_CENTER_H = 0x08, ALIGN_RIGHT = 0x0C, // These flags does not belong in here but // are defined here until we sorted this out. // They are only used in display_text_proportional_len_clip_rgb() DT_CLIP = 0x4000 }; typedef uint16 control_alignment_t; struct clip_dimension { scr_coord_val x, xx, w, y, yy, h; }; // helper macros // save the current clipping and set a new one #define PUSH_CLIP(x,y,w,h) \ {\ clip_dimension const p_cr = gfx->get_clip_rect(CLIP_NUM_DEFAULT_VALUE); \ gfx->set_clip_rect(x, y, w, h CLIP_NUM_DEFAULT, false); // save the current clipping and set a new one // fit it to old clipping region #define PUSH_CLIP_FIT(x,y,w,h) \ {\ clip_dimension const p_cr = gfx->get_clip_rect(CLIP_NUM_DEFAULT_VALUE); \ gfx->set_clip_rect(x, y, w, h CLIP_NUM_DEFAULT, true); // restore a saved clipping rect #define POP_CLIP() \ gfx->set_clip_rect(p_cr.x, p_cr.y, p_cr.w, p_cr.h CLIP_NUM_DEFAULT, false); \ } // pointer to image display procedures typedef void (*draw_image_proc)(const image_id n, scr_coord_val xp, scr_coord_val yp, const signed char player_nr, const bool daynight, const bool dirty CLIP_NUM_DEF); typedef void (*draw_blend_proc)(const image_id n, scr_coord_val xp, scr_coord_val yp, const signed char player_nr, const FLAGGED_PIXVAL color_index, const bool daynight, const bool dirty CLIP_NUM_DEF); typedef void (*draw_alpha_proc)(const image_id n, const image_id alpha_n, const unsigned alpha_flags, scr_coord_val xp, scr_coord_val yp, const signed char player_nr, const FLAGGED_PIXVAL color_index, const bool daynight, const bool dirty CLIP_NUM_DEF); #define ALPHA_RED 0x1 #define ALPHA_GREEN 0x2 #define ALPHA_BLUE 0x4 /// 9-slice type typedef image_id stretch_map_t[3][3]; #if COLOUR_DEPTH != 0 # define MAX_ZOOM_FACTOR (9) # define ZOOM_NEUTRAL (3) #else # define MAX_ZOOM_FACTOR (0) # define ZOOM_NEUTRAL (0) #endif enum simgraph_type_t { SIMGRAPH_TYPE_NULL = 0, // Dummy renderer, used for headless servers SIMGRAPH_TYPE_SOFTWARE = 1, // 16 bit software (CPU) renderer, default }; /// Graphics renderer interface struct simgraph_t { simgraph_type_t type; /// Do no access directly, use the get_* functions below instead. scr_coord_val tile_raster_width; scr_coord_val base_tile_raster_width; signed short current_tile_raster_width; // variables for storing currently used image procedure set draw_image_proc draw_normal; draw_image_proc draw_color; draw_blend_proc draw_blend; draw_alpha_proc draw_alpha; inline scr_coord_val get_tile_raster_width() const { return tile_raster_width; } inline scr_coord_val get_base_tile_raster_width() const { return base_tile_raster_width; } inline scr_coord_val get_current_tile_raster_width() const { return current_tile_raster_width; } sint32 zoom_num[MAX_ZOOM_FACTOR+1]; sint32 zoom_den[MAX_ZOOM_FACTOR+1]; // // Colour palette stuff // /// Looks up a colour from its palette index PIXVAL (*palette_lookup)(palette_index_t idx); /// Retrieves the palette index of a color, or returns 0 if the color is not in the palette. palette_index_t (*palette_indexof)(PIXVAL color); /// Get 24bit RGB888 colour from an index of the old 8bit palette rgb888_t (*get_color_rgb)(palette_index_t idx); /// Environment colours from RGB888 to system format void (*env_t_rgb_to_system_colors)(); /// set primary and secondary company color for player void (*set_player_color_scheme)(int player_idx, uint8 col1_idx, uint8 col2_idx); /// Change the value of the day/night palette entry at index @p color_idx void (*set_light_color)(int color_idx, rgb888_t day_color, rgb888_t night_color); void (*set_daynight_level)(int night_level); /// changes the raster width after loading scr_coord_val (*set_base_raster_width)(scr_coord_val new_raster); int (*zoom_factor_up)(); int (*zoom_factor_down)(); /// Initialises the graphics module bool (*init)(scr_size window_size, sint16 fullscreen); bool (*is_display_init)(); /// Closes the graphics module void (*exit)(); void (*on_window_resized)(scr_size new_window_size); /// Loads the font, returns the number of characters in it /// @param reload if true forces reload bool (*load_font)(const char *fname, bool reload /*=false*/); /// Returns the number of currently registered images. image_id (*get_image_count)(); /// Registers an image with the renderer so the image can be drawn. /// @returns the image id (should be stored to img->imgid) image_id (*register_image)(const image_t *img); // delete all images above a certain number ... void (*free_all_images_above)(image_id above); /// unzoomed offsets scr_rect (*get_base_image_offset)(image_id image); /// zoomed offsets scr_rect (*get_image_offset)(image_id image); /// Mark image @p image at screen position (x,y) as dirty. void (*mark_img_dirty)(image_id image, scr_coord_val x, scr_coord_val y); /// Marks a rectangular region of the screen as dirty. /// Clips to screen only. void (*mark_rect_dirty_wc)(scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2); /// As @ref mark_rect_dirty_wc, but clips to active clip rect (@ref set_clip_rect) void (*mark_rect_dirty_clip)(scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2 CLIP_NUM_DEF); // clips to clip_rect /// Mark the whole screen dirty. void (*mark_screen_dirty)(); /// Returns the size of the drawable area. /// @note The size of the underlying render target texture may be larger for alignment reasons. scr_size (*get_screen_size)(); void (*set_screen_height) (scr_coord_val new_height); void (*set_screen_actual_width)(scr_coord_val new_actual_width); // get next smallest size when scaling to percent scr_size (*get_best_matching_size)(image_id n, sint16 zoom_percent); /// force a certain size on a image (for rescaling tool images) void (*fit_img_to_width)(image_id n, sint16 new_w); /// scrolls horizontally @p x_offset pixels to the left, will ignore clipping etc. void (*move_scroll_band)(scr_coord_val start_y, scr_coord_val x_offset, scr_coord_val h); /// for switching between image procedure sets, and for setting current tile raster width void (*set_image_procs)(bool is_global); /// Only used for GUI, display image inside a rect void (*draw_img_aligned)(image_id n, scr_rect area, int align, bool dirty); /// display image with day and night change void (*draw_img_aux)(image_id n, scr_coord_val xp, scr_coord_val yp, signed char player_nr, bool daynight, bool dirty CLIP_NUM_DEF); /// draws the images with alpha, either blended or as outline void (*draw_rezoomed_img_blend)(image_id n, scr_coord_val xp, scr_coord_val yp, signed char player_nr, FLAGGED_PIXVAL color_index, bool daynight, bool dirty CLIP_NUM_DEF); #define draw_img_blend( n, x, y, c, dn, d ) draw_rezoomed_img_blend( (n), (x), (y), 0, (c), (dn), (d) CLIP_NUM_DEFAULT) void (*draw_rezoomed_img_alpha)(image_id n, image_id alphamask_n, unsigned alpha_flags, scr_coord_val xp, scr_coord_val yp, signed char player_nr, FLAGGED_PIXVAL color_index, bool daynight, bool dirty CLIP_NUM_DEF); #define draw_img_alpha( n, a, f, x, y, c, dn, d ) draw_rezoomed_img_alpha( (n), (a), (f), (x), (y), 0, (c), (dn), (d) CLIP_NUM_DEFAULT) /// display image with color (if there) and optional day and night change void (*draw_color_img)(image_id n, scr_coord_val xp, scr_coord_val yp, signed char player_nr, bool daynight, bool dirty CLIP_NUM_DEF); // display unzoomed image void (*draw_base_img)(image_id n, scr_coord_val xp, scr_coord_val yp, signed char player_nr, bool daynight, bool dirty CLIP_NUM_DEF); // display unzoomed image with alpha, either blended or as outline void (*draw_base_img_blend)(image_id n, scr_coord_val xp, scr_coord_val yp, signed char player_nr, FLAGGED_PIXVAL color_index, bool daynight, bool dirty CLIP_NUM_DEF); void (*draw_base_img_alpha)(image_id n, image_id alpha_n, unsigned alpha_flags, scr_coord_val xp, scr_coord_val yp, sint8 player_nr, FLAGGED_PIXVAL color_index, bool daynight, bool dirty CLIP_NUM_DEF); /// this displays a 3x3 array of images to fit the scr_rect void (*draw_stretch_map)(const stretch_map_t &imag, scr_rect area); /// this displays a 3x3 array of images to fit the scr_rect like above, but blend the color void (*draw_stretch_map_blend)(const stretch_map_t &imag, scr_rect area, FLAGGED_PIXVAL color); /// Blends two colors PIXVAL (*blend_colors)(PIXVAL background, PIXVAL foreground, int percent_blend); /// Tints a rectangular framebuffer region with @p color using linear blending, clips to active clip rect /// @param opacity_percent 0=do nothing, 100=replace framebuffer completely void (*tint_rect)(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, int opacity_percent); /// Fills a rectangular framebuffer region with a solid color. /// @note Ignores active clip rect void (*draw_rect)(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty); /// Same as @ref draw_rect, but clips to active clip rect. void (*draw_rect_clipped)(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty CLIP_NUM_DEF); /// Draw rect with rounded corners. void (*draw_rounded_rect_clipped)(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty); /// Draws vertical line from {xp,yp} tp {xp,yp+h-1} void (*draw_vline_clipped)(scr_coord_val xp, scr_coord_val yp, scr_coord_val h, PIXVAL color, bool dirty CLIP_NUM_DEF); /// Flushes all dirty areas to the screen. void (*flush_framebuffer)(); /// Shows or hides the mouse cursor. void (*set_cursor_visible)(bool visible); void (*set_default_cursor)(int cursor_id); /// Changes whether the load cursor or the default cursor is shown. void (*set_show_load_cursor)(bool show); /// Copies rectangular region of pixels to the framebuffer. void (*draw_array)(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, const PIXVAL *arr); // // Font stuff and glyph metrics // TODO this should maybe be moved somewhere else // /// returns true if this is a valid character for the currently loaded font bool (*font_has_character)( utf16 char_code ); scr_coord_val (*get_char_width)(utf32 c); scr_coord_val (*get_number_width)(); /** * For the next logical character in the text, returns the character code * as well as retrieves the char byte count and the screen pixel width * CAUTION : The text pointer advances to point to the next logical character */ utf32 (*get_next_char_with_metrics)(const char *&text, unsigned char &byte_length, unsigned char &pixel_width); /** * For the previous logical character in the text, returns the character code * as well as retrieves the char byte count and the screen pixel width * CAUTION : The text pointer recedes to point to the previous logical character */ utf32 (*get_prev_char_with_metrics)(const char *&text, const char *const text_start, unsigned char &byte_length, unsigned char &pixel_width); // // Text rendering // /// Calculates the screen width of a UTF-8 encoded string in pixels. /// @param len number of bytes in @p text, or -1 if @p text is null-terminated. scr_coord_val (*calc_text_width_n)(const char *text, size_t len); inline scr_coord_val calc_text_width(const char *text) const { return calc_text_width_n(text, 0x7FFFu); } /// @returns Size of the bounding box which contains the single- or multiline @p text scr_size (*calc_multiline_text_size)(const char *text); /// Returns the index of the last character that would fit within the width /// If an ellipsis len is given, it will only return the last character up to this len if the full length cannot be fitted /// @returns index of next character. if text[index]==0 the whole string fits size_t (*calc_text_index_for_width)(const char *text, scr_coord_val max_width); scr_coord_val (*draw_text_clipped_n)(scr_coord_val x, scr_coord_val y, const char* txt, control_alignment_t flags, PIXVAL color, bool dirty, sint32 len CLIP_NUM_DEF); inline scr_coord_val draw_text_clipped(scr_coord_val x, scr_coord_val y, const char* txt, control_alignment_t flags, PIXVAL color, bool dirty) const { return draw_text_clipped_n(x, y, txt, flags | DT_CLIP, color, dirty, -1 CLIP_NUM_DEFAULT); } inline scr_coord_val draw_text(scr_coord_val x, scr_coord_val y, const char *txt, control_alignment_t flags, PIXVAL color, bool dirty) const { return draw_text_clipped_n(x, y, txt, flags, color, dirty, -1 CLIP_NUM_DEFAULT); } scr_coord_val (*draw_multiline_text)(scr_coord_val x, scr_coord_val y, const char *text, PIXVAL color); /// Display a string that is abbreviated by the (language specific) ellipsis character if too wide /// If enough space is given, it just display the full string void (*draw_text_ellipsis_shadowed)(scr_rect r, const char *text, int align, PIXVAL color, bool dirty, bool shadow, PIXVAL shadow_color); void draw_text_ellipsis(scr_rect r, const char *text, int align, PIXVAL color, bool dirty) const { draw_text_ellipsis_shadowed(r, text, align, color, dirty, false, 0); } // compound painting routines void (*draw_text_outlined)(scr_coord_val xpos, scr_coord_val ypos, PIXVAL text_color, PIXVAL shadow_color, const char *text, int dirty); void (*draw_text_shadowed)(scr_coord_val xpos, scr_coord_val ypos, PIXVAL text_color, PIXVAL shadow_color, const char *text, int dirty); void (*draw_box3d) (scr_coord_val x1, scr_coord_val y1, scr_coord_val w, scr_coord_val h, PIXVAL tl_color, PIXVAL rd_color, bool dirty); void (*draw_box3d_clipped)(scr_coord_val x1, scr_coord_val y1, scr_coord_val w, scr_coord_val h, PIXVAL tl_color, PIXVAL rd_color); void (*draw_textbox3d_clipped)(scr_coord_val xpos, scr_coord_val ypos, FLAGGED_PIXVAL ddd_farbe, FLAGGED_PIXVAL text_farbe, const char *text, int dirty CLIP_NUM_DEF); /// Draw a straight solid line. void (*draw_line) (scr_coord_val x, scr_coord_val y, scr_coord_val xx, scr_coord_val yy, PIXVAL color); void (*draw_line_dotted)(scr_coord_val x, scr_coord_val y, scr_coord_val xx, scr_coord_val yy, scr_coord_val draw, scr_coord_val dontDraw, PIXVAL color); void (*draw_empty_circle) (scr_coord_val x0, scr_coord_val y0, int radius, PIXVAL color); void (*draw_filled_circle)(scr_coord_val x0, scr_coord_val y0, int radius, PIXVAL color); /// Draw a cubic Bezier curve between (Ax,Ay) and (Bx,By). void (*draw_bezier)(scr_coord_val Ax, scr_coord_val Ay, scr_coord_val Bx, scr_coord_val By, scr_coord_val ADx, scr_coord_val ADy, scr_coord_val BDx, scr_coord_val BDy, PIXVAL colore, scr_coord_val draw, scr_coord_val dontDraw); /// Draw a right-facing triangle (e.g. for convoi progress bars) void (*draw_right_triangle)(scr_coord_val x, scr_coord_val y, scr_coord_val height, PIXVAL colval, bool dirty); void (*draw_signal_direction)(scr_coord_val x, scr_coord_val y, uint8 way_dir, uint8 sig_dir, PIXVAL col1, PIXVAL col1_dark, bool is_diagonal, uint8 slope); /// Takes a screenshot of @p screen_area and saves the image as 'simscrXX.png' (with XX being a placeholder for two numeric characters). /// @returns true on success /// @note For simgraph0 this is a no-op (as there is no display) bool (*take_screenshot)(const scr_rect &screen_area); // // Clipping // void (*set_clip_rect)(scr_coord_val x, scr_coord_val y, scr_coord_val w, scr_coord_val h CLIP_NUM_DEF, bool fit /*=false*/); clip_dimension (*get_clip_rect)(CLIP_NUM_DEF0); void (*push_clip_rect)(scr_coord_val x, scr_coord_val y, scr_coord_val w, scr_coord_val h CLIP_NUM_DEF); void (*swap_clip_rect)(CLIP_NUM_DEF0); void (*pop_clip_rect)(CLIP_NUM_DEF0); /// Add a polygonal clip line (for clipping along tile borders) void (*add_poly_clip)(int x0_,int y0_, int x1, int y1, int ribi CLIP_NUM_DEF); void (*clear_all_poly_clip)(CLIP_NUM_DEF0); void (*activate_ribi_clip)(int ribi CLIP_NUM_DEF); }; const simgraph_t *simgraph_select(simgraph_type_t preferred_type); /// Assign to this the output of simgraph_select extern const simgraph_t *gfx; // // Variables required for layouting // extern scr_coord_val default_font_ascent; extern scr_coord_val default_font_linespace; #define LINEASCENT (default_font_ascent) #define LINESPACE (default_font_linespace) #endif simutrans-124.5/src/simutrans/display/simgraph0.cc000066400000000000000000000606211520675460500222530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simconst.h" #include "../sys/simsys.h" #include "../descriptor/image.h" #include "simgraph.h" extern const sint32 zoom_num[1] = { 1 }; extern const sint32 zoom_den[1] = { 1 }; static PIXVAL simgraph0_palette_lookup (palette_index_t idx); static palette_index_t simgraph0_palette_indexof (PIXVAL color); static rgb888_t simgraph0_get_color_rgb (palette_index_t idx); static void simgraph0_env_t_rgb_to_system_colors (); static void simgraph0_set_player_color_scheme (const int player, const uint8 col1, const uint8 col2); static void simgraph0_set_light_color (int light_idx, rgb888_t day_light, rgb888_t night_light); static void simgraph0_set_daynight_level (int night); static scr_coord_val simgraph0_set_base_raster_width (scr_coord_val new_raster); static int simgraph0_zoom_factor_up (); static int simgraph0_zoom_factor_down (); static bool simgraph0_init (scr_size window_size, sint16 full_screen); static bool simgraph0_is_display_init (); static void simgraph0_exit (); static void simgraph0_on_window_resized (scr_size new_window_size); static bool simgraph0_load_font (const char *fname, bool reload); static image_id simgraph0_get_image_count (); static image_id simgraph0_register_image (const image_t *image_in); static void simgraph0_free_all_images_above (image_id above ); static scr_rect simgraph0_get_base_image_offset (image_id image); static scr_rect simgraph0_get_image_offset (image_id image); static void simgraph0_mark_img_dirty (image_id image, scr_coord_val xp, scr_coord_val yp); static void simgraph0_mark_rect_dirty_wc (scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2); static void simgraph0_mark_rect_dirty_clip (scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2 CLIP_NUM_DEF); static void simgraph0_mark_screen_dirty (); static scr_size simgraph0_get_screen_size (); static void simgraph0_set_screen_actual_width (scr_coord_val w); static void simgraph0_set_screen_height (scr_coord_val const h); static scr_size simgraph0_get_best_matching_size (const image_id n, sint16 zoom_percent); static void simgraph0_fit_img_to_width (const image_id n, sint16 new_w); static void simgraph0_move_scroll_band (scr_coord_val start_y, scr_coord_val x_offset, scr_coord_val h); static void simgraph0_set_image_procs (bool is_global); static void simgraph0_draw_img_aligned (const image_id n, scr_rect area, int align, const bool dirty); static void simgraph0_draw_img_aux (const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_rezoomed_img_blend (const image_id, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_rezoomed_img_alpha (const image_id, const image_id, const unsigned, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_color_img (const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_base_img (const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_base_img_blend (const image_id, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_base_img_alpha (const image_id, const image_id, const unsigned, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, bool CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_stretch_map (const stretch_map_t &, scr_rect); static void simgraph0_draw_stretch_map_blend (const stretch_map_t &, scr_rect, FLAGGED_PIXVAL); static PIXVAL simgraph0_blend_colors (PIXVAL, PIXVAL, int); static void simgraph0_tint_rect (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, int); static void simgraph0_draw_rect (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, bool); static void simgraph0_draw_rect_clipped (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, bool CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_rounded_rect_clipped (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, bool); static void simgraph0_draw_vline_clipped (scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, bool CLIP_NUM_DEF_NOUSE); static void simgraph0_flush_framebuffer (); static void simgraph0_set_cursor_visible (bool); static void simgraph0_set_default_cursor (int); static void simgraph0_set_show_load_cursor (bool); static void simgraph0_draw_array (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, const PIXVAL *); static scr_coord_val simgraph0_calc_text_width_n (const char *, size_t); static scr_size simgraph0_calc_multiline_text_size (const char *); static size_t simgraph0_calc_text_index_for_width (const char *, scr_coord_val); static bool simgraph0_font_has_character (utf16 char_code); static utf32 simgraph0_get_next_char_with_metrics (const char* &text, unsigned char &byte_length, unsigned char &pixel_width); static utf32 simgraph0_get_prev_char_with_metrics (const char* &text, const char *const text_start, unsigned char &byte_length, unsigned char &pixel_width); static scr_coord_val simgraph0_get_char_width (utf32 c); static scr_coord_val simgraph0_get_number_width (); static scr_coord_val simgraph0_draw_text_clipped_n (scr_coord_val, scr_coord_val, const char*, control_alignment_t , const PIXVAL, bool, sint32 CLIP_NUM_DEF_NOUSE); static scr_coord_val simgraph0_draw_multiline_text (scr_coord_val, scr_coord_val, const char *, PIXVAL); static void simgraph0_draw_text_ellipsis_shadowed(scr_rect, const char *, int, PIXVAL, bool, bool, PIXVAL); static void simgraph0_draw_text_outlined (scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, const char *, int); static void simgraph0_draw_text_shadowed (scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, const char *, int); static void simgraph0_draw_box3d (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, bool); static void simgraph0_draw_box3d_clipped (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, PIXVAL); static void simgraph0_draw_textbox3d_clipped (scr_coord_val, scr_coord_val, FLAGGED_PIXVAL, FLAGGED_PIXVAL, const char *, int CLIP_NUM_DEF_NOUSE); static void simgraph0_draw_line (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL); static void simgraph0_draw_line_dotted (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL); static void simgraph0_draw_empty_circle (scr_coord_val, scr_coord_val, int, const PIXVAL); static void simgraph0_draw_filled_circle (scr_coord_val, scr_coord_val, int, const PIXVAL); static void simgraph0_draw_bezier (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, const PIXVAL, scr_coord_val, scr_coord_val); static void simgraph0_draw_right_triangle (scr_coord_val, scr_coord_val, scr_coord_val, const PIXVAL, const bool); static bool simgraph0_take_screenshot (const scr_rect &); static void simgraph0_draw_signal_direction (scr_coord_val, scr_coord_val, uint8, uint8, PIXVAL, PIXVAL, bool, uint8); static void simgraph0_set_clip_rect (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val CLIP_NUM_DEF, bool fit); static clip_dimension simgraph0_get_clip_rect (CLIP_NUM_DEF_NOUSE0); static void simgraph0_push_clip_rect (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val CLIP_NUM_DEF_NOUSE); static void simgraph0_swap_clip_rect (CLIP_NUM_DEF_NOUSE0); static void simgraph0_pop_clip_rect (CLIP_NUM_DEF_NOUSE0); #ifdef MULTI_THREAD static void simgraph0_add_poly_clip (int, int, int, int, int CLIP_NUM_DEF_NOUSE); static void simgraph0_clear_all_poly_clip (const sint8); static void simgraph0_activate_ribi_clip (int CLIP_NUM_DEF_NOUSE); #else static void simgraph0_add_poly_clip (int, int, int, int, int); static void simgraph0_clear_all_poly_clip (); static void simgraph0_activate_ribi_clip (int); #endif simgraph_t g_simgraph0 = { /*.type =*/ SIMGRAPH_TYPE_NULL, /*.tile_raster_width =*/ 16, // zoomed /*.base_tile_raster_width =*/ 16, // original /*.current_tile_raster_width =*/ 0, /*.draw_normal =*/ NULL, /*.draw_color =*/ NULL, /*.draw_blend =*/ NULL, /*.draw_alpha =*/ NULL, /*.zoom_num =*/ { 1 }, /*.zoom_den =*/ { 0 }, /*.palette_lookup =*/ simgraph0_palette_lookup, /*.palette_indexof =*/ simgraph0_palette_indexof, /*.get_color_rgb =*/ simgraph0_get_color_rgb, /*.env_t_rgb_to_system_colors =*/ simgraph0_env_t_rgb_to_system_colors, /*.set_player_color_scheme =*/ simgraph0_set_player_color_scheme, /*.set_light_color =*/ simgraph0_set_light_color, /*.set_daynight_level =*/ simgraph0_set_daynight_level, /*.set_base_raster_width =*/ simgraph0_set_base_raster_width, /*.zoom_factor_up =*/ simgraph0_zoom_factor_up, /*.zoom_factor_down =*/ simgraph0_zoom_factor_down, /*.init =*/ simgraph0_init, /*.is_display_init =*/ simgraph0_is_display_init, /*.exit =*/ simgraph0_exit, /*.on_window_resized =*/ simgraph0_on_window_resized, /*.load_font =*/ simgraph0_load_font, /*.get_image_count =*/ simgraph0_get_image_count, /*.register_image =*/ simgraph0_register_image, /*.free_all_images_above =*/ simgraph0_free_all_images_above, /*.get_base_image_offset =*/ simgraph0_get_base_image_offset, /*.get_image_offset =*/ simgraph0_get_image_offset, /*.mark_img_dirty =*/ simgraph0_mark_img_dirty, /*.mark_rect_dirty_wc =*/ simgraph0_mark_rect_dirty_wc, /*.mark_rect_dirty_clip =*/ simgraph0_mark_rect_dirty_clip, /*.mark_screen_dirty =*/ simgraph0_mark_screen_dirty, /*.get_screen_size =*/ simgraph0_get_screen_size, /*.set_screen_height =*/ simgraph0_set_screen_height, /*.set_screen_actual_width =*/ simgraph0_set_screen_actual_width, /*.get_best_matching_size =*/ simgraph0_get_best_matching_size, /*.fit_img_to_width =*/ simgraph0_fit_img_to_width, /*.move_scroll_band =*/ simgraph0_move_scroll_band, /*.set_image_procs =*/ simgraph0_set_image_procs, /*.draw_img_aligned =*/ simgraph0_draw_img_aligned, /*.draw_img_aux =*/ simgraph0_draw_img_aux, /*.draw_rezoomed_img_blend =*/ simgraph0_draw_rezoomed_img_blend, /*.draw_rezoomed_img_alpha =*/ simgraph0_draw_rezoomed_img_alpha, /*.draw_color_img =*/ simgraph0_draw_color_img, /*.draw_base_img =*/ simgraph0_draw_base_img, /*.draw_base_img_blend =*/ simgraph0_draw_base_img_blend, /*.draw_base_img_alpha =*/ simgraph0_draw_base_img_alpha, /*.draw_stretch_map =*/ simgraph0_draw_stretch_map, /*.draw_stretch_map_blend =*/ simgraph0_draw_stretch_map_blend, /*.blend_colors =*/ simgraph0_blend_colors, /*.tint_rect =*/ simgraph0_tint_rect, /*.draw_rect =*/ simgraph0_draw_rect, /*.draw_rect_clipped =*/ simgraph0_draw_rect_clipped, /*.draw_rounded_rect_clipped =*/ simgraph0_draw_rounded_rect_clipped, /*.draw_vline_clipped =*/ simgraph0_draw_vline_clipped, /*.flush_framebuffer =*/ simgraph0_flush_framebuffer, /*.set_cursor_visible =*/ simgraph0_set_cursor_visible, /*.set_default_cursor =*/ simgraph0_set_default_cursor, /*.set_show_load_cursor =*/ simgraph0_set_show_load_cursor, /*.draw_array =*/ simgraph0_draw_array, /*.font_has_character =*/ simgraph0_font_has_character, /*.get_char_width =*/ simgraph0_get_char_width, /*.get_number_width =*/ simgraph0_get_number_width, /*.get_next_char_with_metrics =*/ simgraph0_get_next_char_with_metrics, /*.get_prev_char_with_metrics =*/ simgraph0_get_prev_char_with_metrics, /*.calc_text_width_n =*/ simgraph0_calc_text_width_n, /*.calc_multiline_text_size =*/ simgraph0_calc_multiline_text_size, /*.calc_text_index_for_width =*/ simgraph0_calc_text_index_for_width, /*.draw_text_clipped_n =*/ simgraph0_draw_text_clipped_n, /*.draw_multiline_text =*/ simgraph0_draw_multiline_text, /*.draw_text_ellipsis_shadowed =*/ simgraph0_draw_text_ellipsis_shadowed, /*.draw_text_outlined =*/ simgraph0_draw_text_outlined, /*.draw_text_shadowed =*/ simgraph0_draw_text_shadowed, /*.draw_box3d =*/ simgraph0_draw_box3d, /*.draw_box3d_clipped =*/ simgraph0_draw_box3d_clipped, /*.draw_textbox3d_clipped =*/ simgraph0_draw_textbox3d_clipped, /*.draw_line =*/ simgraph0_draw_line, /*.draw_line_dotted =*/ simgraph0_draw_line_dotted, /*.draw_empty_circle =*/ simgraph0_draw_empty_circle, /*.draw_filled_circle =*/ simgraph0_draw_filled_circle, /*.draw_bezier =*/ simgraph0_draw_bezier, /*.draw_right_triangle =*/ simgraph0_draw_right_triangle, /*.draw_signal_direction =*/ simgraph0_draw_signal_direction, /*.take_screenshot =*/ simgraph0_take_screenshot, /*.set_clip_rect =*/ simgraph0_set_clip_rect, /*.get_clip_rect =*/ simgraph0_get_clip_rect, /*.push_clip_rect =*/ simgraph0_push_clip_rect, /*.swap_clip_rect =*/ simgraph0_swap_clip_rect, /*.pop_clip_rect =*/ simgraph0_pop_clip_rect, /*.add_poly_clip =*/ simgraph0_add_poly_clip, /*.clear_all_poly_clip =*/ simgraph0_clear_all_poly_clip, /*.activate_ribi_clip =*/ simgraph0_activate_ribi_clip, }; static PIXVAL simgraph0_palette_lookup(palette_index_t idx) { return idx; } static palette_index_t simgraph0_palette_indexof(PIXVAL color) { return (palette_index_t)color; } static rgb888_t simgraph0_get_color_rgb(palette_index_t) { return {0,0,0}; } static void simgraph0_env_t_rgb_to_system_colors() { } static scr_coord_val simgraph0_set_base_raster_width(scr_coord_val) { return 0; } void set_zoom_factor(int) { } static int simgraph0_zoom_factor_up() { return false; } static int simgraph0_zoom_factor_down() { return false; } static void simgraph0_mark_rect_dirty_wc(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val) { } static void simgraph0_mark_rect_dirty_clip(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val CLIP_NUM_DEF_NOUSE) { } static void simgraph0_mark_screen_dirty() { } static void simgraph0_mark_img_dirty(image_id, scr_coord_val, scr_coord_val) { } static bool simgraph0_load_font(const char *, bool) { return true; } static scr_size simgraph0_get_screen_size() { return scr_size{ 0,0 }; } static void simgraph0_set_screen_height(scr_coord_val) { } static void simgraph0_set_screen_actual_width(scr_coord_val) { } static void simgraph0_set_daynight_level(int) { } static void simgraph0_set_player_color_scheme(const int, const uint8, const uint8) { } static image_id simgraph0_register_image(const image_t *image) { return 1; } static bool simgraph0_take_screenshot(const scr_rect &) { return false; } static scr_rect simgraph0_get_image_offset(image_id) { return scr_rect{ 0, 0, 0, 0 }; } static scr_rect simgraph0_get_base_image_offset(image_id) { return scr_rect{ 0, 0, 0, 0 }; } static clip_dimension simgraph0_get_clip_rect(CLIP_NUM_DEF_NOUSE0) { clip_dimension clip_rect; clip_rect.x = 0; clip_rect.xx = 0; clip_rect.w = 0; clip_rect.y = 0; clip_rect.yy = 0; clip_rect.h = 0; return clip_rect; } static void simgraph0_set_clip_rect(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val CLIP_NUM_DEF_NOUSE, bool) { } static void simgraph0_push_clip_rect(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val CLIP_NUM_DEF_NOUSE) { } static void simgraph0_swap_clip_rect(CLIP_NUM_DEF_NOUSE0) { } static void simgraph0_pop_clip_rect(CLIP_NUM_DEF_NOUSE0) { } static void simgraph0_move_scroll_band(scr_coord_val, scr_coord_val, scr_coord_val) { } static void simgraph0_draw_img_aux(const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE) { } static void simgraph0_draw_color_img(const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE) { } static scr_size simgraph0_get_best_matching_size(const image_id, sint16) { return scr_size(32, 32); // default size } static void simgraph0_draw_base_img(const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE) { } static void simgraph0_fit_img_to_width( const image_id, sint16) { } static void simgraph0_draw_stretch_map(const stretch_map_t &, scr_rect) { } static void simgraph0_draw_stretch_map_blend( const stretch_map_t &, scr_rect, FLAGGED_PIXVAL) { } static void simgraph0_draw_rezoomed_img_blend(const image_id, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE) { } static void simgraph0_draw_rezoomed_img_alpha(const image_id, const image_id, const unsigned, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE) { } static void simgraph0_draw_base_img_blend(const image_id, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE) { } static void simgraph0_draw_base_img_alpha(const image_id, const image_id, const unsigned, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, bool CLIP_NUM_DEF_NOUSE) { } static PIXVAL simgraph0_blend_colors(PIXVAL, PIXVAL, int) { return 0; } static void simgraph0_tint_rect(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, int) { } static void simgraph0_draw_rect(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, bool) { } static void simgraph0_draw_rect_clipped(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, bool CLIP_NUM_DEF_NOUSE) { } static void simgraph0_draw_vline_clipped(scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, bool CLIP_NUM_DEF_NOUSE) { } static void simgraph0_draw_rounded_rect_clipped(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, bool) { } static void simgraph0_draw_array(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, const PIXVAL *) { } static scr_coord_val simgraph0_get_char_width(utf32) { return 0; } static scr_coord_val simgraph0_get_number_width() { return 0; } static utf32 simgraph0_get_next_char_with_metrics(const char* &, unsigned char &, unsigned char &) { return 0; } static utf32 simgraph0_get_prev_char_with_metrics(const char* &, const char *const, unsigned char &, unsigned char &) { return 0; } static bool simgraph0_font_has_character( utf16 ) { return false; } static size_t simgraph0_calc_text_index_for_width(const char *, scr_coord_val) { return 0; } static scr_coord_val simgraph0_calc_text_width_n(const char*, size_t) { return 0; } static scr_size simgraph0_calc_multiline_text_size(const char *) { return { 0, 0 }; } static scr_coord_val simgraph0_draw_text_clipped_n(scr_coord_val, scr_coord_val, const char*, control_alignment_t , const PIXVAL, bool, sint32 CLIP_NUM_DEF_NOUSE) { return 0; } static void simgraph0_draw_text_outlined(scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, const char *, int) { } static void simgraph0_draw_text_shadowed(scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, const char *, int) { } static void simgraph0_draw_box3d(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, bool) { } static void simgraph0_draw_box3d_clipped(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, PIXVAL) { } static void simgraph0_draw_textbox3d_clipped(scr_coord_val, scr_coord_val, FLAGGED_PIXVAL, FLAGGED_PIXVAL, const char *, int CLIP_NUM_DEF_NOUSE) { } static scr_coord_val simgraph0_draw_multiline_text(scr_coord_val, scr_coord_val, const char *, PIXVAL) { return 0; } static void simgraph0_flush_framebuffer() { } static void simgraph0_set_cursor_visible(bool) { } static void simgraph0_set_default_cursor(int) { } static void simgraph0_set_show_load_cursor(bool) { } static bool simgraph0_init(scr_size, sint16) { return true; } static bool simgraph0_is_display_init() { return false; } static void simgraph0_free_all_images_above(image_id) { } static void simgraph0_exit() { dr_os_close(); } static void simgraph0_on_window_resized(scr_size) { } void display_snapshot() { } static void simgraph0_draw_line(const scr_coord_val, const scr_coord_val, const scr_coord_val, const scr_coord_val, const PIXVAL) { } static void simgraph0_draw_line_dotted(const scr_coord_val, const scr_coord_val, const scr_coord_val, const scr_coord_val, const scr_coord_val, const scr_coord_val, const PIXVAL) { } static void simgraph0_draw_empty_circle(scr_coord_val, scr_coord_val, int, const PIXVAL) { } static void simgraph0_draw_filled_circle(scr_coord_val, scr_coord_val, int, const PIXVAL) { } static void simgraph0_draw_bezier(scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, const PIXVAL, scr_coord_val, scr_coord_val) { } static void simgraph0_draw_right_triangle(scr_coord_val, scr_coord_val, scr_coord_val, const PIXVAL, const bool) { } static void simgraph0_draw_signal_direction( scr_coord_val, scr_coord_val, uint8, uint8, PIXVAL, PIXVAL, bool, uint8 ) { } static void simgraph0_draw_img_aligned(const image_id, scr_rect, int, bool) { } static void simgraph0_draw_text_ellipsis_shadowed( scr_rect, const char *, int, PIXVAL, bool, bool, PIXVAL) { } static image_id simgraph0_get_image_count() { return 0; } #ifdef MULTI_THREAD static void simgraph0_add_poly_clip(int, int, int, int, int CLIP_NUM_DEF_NOUSE) { } static void simgraph0_clear_all_poly_clip(const sint8) { } static void simgraph0_activate_ribi_clip(int CLIP_NUM_DEF_NOUSE) { } #else static void simgraph0_add_poly_clip(int, int, int, int, int) { } static void simgraph0_clear_all_poly_clip() { } static void simgraph0_activate_ribi_clip(int) { } #endif static void simgraph0_set_image_procs(bool is_global) { if( is_global ) { g_simgraph0.draw_normal = simgraph0_draw_img_aux; g_simgraph0.draw_color = simgraph0_draw_color_img; g_simgraph0.draw_blend = simgraph0_draw_rezoomed_img_blend; g_simgraph0.draw_alpha = simgraph0_draw_rezoomed_img_alpha; g_simgraph0.current_tile_raster_width = g_simgraph0.tile_raster_width; } else { g_simgraph0.draw_normal = simgraph0_draw_base_img; g_simgraph0.draw_color = simgraph0_draw_base_img; g_simgraph0.draw_blend = simgraph0_draw_base_img_blend; g_simgraph0.draw_alpha = simgraph0_draw_base_img_alpha; g_simgraph0.current_tile_raster_width = gfx->get_base_tile_raster_width(); } } void simgraph0_set_light_color(int, rgb888_t, rgb888_t) { } simutrans-124.5/src/simutrans/display/simgraph0.h000066400000000000000000000003471520675460500221140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_SIMGRAPH0_H #define DISPLAY_SIMGRAPH0_H #include "simgraph.h" extern const simgraph_t g_simgraph0; #endif simutrans-124.5/src/simutrans/display/simgraph16.cc000066400000000000000000004760641520675460500223560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simgraph.h" #include "font.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../descriptor/image.h" #include "../gui/simwin.h" #include "../io/raw_image.h" #include "../macros.h" #include "../obj/roadsign.h" #include "../pathes.h" #include "../simconst.h" #include "../simdebug.h" #include "../simmem.h" #include "../simticker.h" #include "../simtypes.h" #include "../sys/simsys.h" #include "../utils/simstring.h" #include "../utils/unicode.h" #include #include #include #include #include #ifdef _MSC_VER # include # define W_OK 2 #else # include #endif #ifdef MULTI_THREAD #include "../utils/simthread.h" // currently just redrawing/rezooming static pthread_mutex_t rezoom_img_mutex[MAX_THREADS]; static pthread_mutex_t recode_img_mutex; #endif // to pass the extra clipnum when not needed use this #ifdef MULTI_THREAD #define CLIPNUM_IGNORE , 0 #else #define CLIPNUM_IGNORE #endif // undefine for debugging the update routines //#define DEBUG_FLUSH_BUFFER #ifdef USE_SOFTPOINTER static int softpointer = -1; #endif static int standard_pointer = -1; #ifdef USE_SOFTPOINTER /* * Icon bar needs to redrawn on mouse hover */ int old_my = -1; #endif /* * struct to hold the information about visible area * at screen line y * associated to some clipline */ struct xrange { sint64 sx, sy; scr_coord_val y; bool non_convex_active; }; class clip_line_t { private: // line from (x0,y0) to (x1 y1) // clip (do not draw) everything right from the ray (x0,y0)->(x1,y1) // pixels on the ray are not drawn // (not_convex) if y0>=y1 then clip along the path (x0,-inf)->(x0,y0)->(x1,y1) // (not_convex) if y0(x1,y1)->(x1,+inf) int x0, y0; int dx, dy; sint64 sdy, sdx; sint64 inc; bool non_convex; public: void clip_from_to(int x0_, int y0_, int x1, int y1, bool non_convex_) { x0 = x0_; dx = x1 - x0; y0 = y0_; dy = y1 - y0; non_convex = non_convex_; int steps = (abs(dx) > abs(dy) ? abs(dx) : abs(dy)); if( steps == 0 ) { return; } sdx = ((sint64)dx << 16) / steps; sdy = ((sint64)dy << 16) / steps; // to stay right from the line // left border: xmin <= x // right border: x < xmax if( dy > 0 ) { if( dy > dx ) { inc = 1 << 16; } else { inc = ((sint64)dx << 16) / dy - (1 << 16); } } else if( dy < 0 ) { if( dy < dx ) { inc = 0; // (+1)-1 << 16; } else { inc = 0; } } } // clip if // ( x-x0) . ( y1-y0 ) // ( y-y0) . (-(x1-x0)) < 0 // -- initialize the clipping // has to be called before image will be drawn // return interval for x coordinate inline void get_x_range(scr_coord_val y, xrange &r, bool use_non_convex) const { // do everything for the previous row y--; r.y = y; r.non_convex_active = false; if( non_convex && use_non_convex && y < y0 && y < (y0 + dy) ) { r.non_convex_active = true; y = min(y0, y0+dy) - 1; } if( dy != 0 ) { // init Bresenham algorithm const sint64 t = (((sint64)y - y0) << 16) / sdy; // sx >> 16 = x // sy >> 16 = y r.sx = t * sdx + inc + ((sint64)x0 << 16); r.sy = t * sdy + ((sint64)y0 << 16); } } // -- step one line down, return interval for x coordinate inline void inc_y(xrange &r, int &xmin, int &xmax) const { r.y++; // switch between clip vertical and along ray if( r.non_convex_active ) { if( r.y == min( y0, y0 + dy ) ) { r.non_convex_active = false; } else { if( dy < 0 ) { const int r_xmax = x0 + dx; if( xmax > r_xmax ) { xmax = r_xmax; } } else { const int r_xmin = x0 + 1; if( xmin < r_xmin ) { xmin = r_xmin; } } return; } } // go along the ray, Bresenham if( dy != 0 ) { if( dy > 0 ) { do { r.sx += sdx; r.sy += sdy; } while( (r.sy >> 16) < r.y ); const int r_xmin = r.sx >> 16; if( xmin < r_xmin ) { xmin = r_xmin; } } else { do { r.sx -= sdx; r.sy -= sdy; } while( (r.sy >> 16) < r.y ); const int r_xmax = r.sx >> 16; if( xmax > r_xmax ) { xmax = r_xmax; } } } // horizontal clip else { const bool clip = dx * (r.y - y0) > 0; if( clip ) { // invisible row xmin = +1; xmax = -1; } } } }; #define MAX_POLY_CLIPS 6 MSVC_ALIGN(64) struct clipping_info_t { // current clipping rectangle clip_dimension clip_rect; // clipping rectangle to be swapped by display_clip_wh_toggle clip_dimension clip_rect_swap; bool swap_active; // poly clipping variables int number_of_clips; uint8 active_ribi; uint8 clip_ribi[MAX_POLY_CLIPS]; clip_line_t poly_clips[MAX_POLY_CLIPS]; xrange xranges[MAX_POLY_CLIPS]; } GCC_ALIGN(64); // aligned to separate cachelines #ifdef MULTI_THREAD clipping_info_t clips[MAX_THREADS]; #define CR0 clips[0] #else clipping_info_t clips; #define CR0 clips #endif #define CR clips CLIP_NUM_INDEX static font_t default_font; // needed for resizing gui static int default_font_numberwidth = 0; #define RGBMAPSIZE (0x8000+LIGHT_COUNT+MAX_PLAYER_COUNT+1024 /* 343 transparent */) // RGB 555/565 specific functions // different masks needed for RGB 555 and RGB 565 for blending #ifdef RGB555 #define ONE_OUT (0x3DEF) // mask out bits after applying >>1 #define TWO_OUT (0x1CE7) // mask out bits after applying >>2 #define MASK_32 (0x03e0f81f) // mask out bits after transforming to 32bit inline PIXVAL rgb(PIXVAL r, PIXVAL g, PIXVAL b) { return (r << 10) | (g << 5) | b; } inline PIXVAL red(PIXVAL rgb) { return rgb >> 10; } inline PIXVAL green(PIXVAL rgb) { return (rgb >> 5) & 0x1F; } #else #define ONE_OUT (0x7bef) // mask out bits after applying >>1 #define TWO_OUT (0x39E7) // mask out bits after applying >>2 #define MASK_32 (0x07e0f81f) // mask out bits after transforming to 32bit inline PIXVAL rgb(PIXVAL r, PIXVAL g, PIXVAL b) { return (r << 11) | (g << 5) | b; } inline PIXVAL red(PIXVAL rgb) { return rgb >> 11; } inline PIXVAL green(PIXVAL rgb) { return (rgb >> 5) & 0x3F; } #endif inline PIXVAL blue(PIXVAL rgb) { return rgb & 0x1F; } /** * Implement shift-and-mask for rgb values: * shift-right by 1 or 2, and mask it to a valid rgb number. */ inline PIXVAL rgb_shr1(PIXVAL c) { return (c >> 1) & ONE_OUT; } inline PIXVAL rgb_shr2(PIXVAL c) { return (c >> 2) & TWO_OUT; } /* * mapping tables for RGB 555 to actual output format * plus the special (player, day&night) colors appended * * 0x0000 - 0x7FFF: RGB colors * 0x8000 - 0x800F: Player colors * 0x8010 - 0x001F: Day&Night special colors * The following transparent colors are not in the colortable * 0x8020 - 0xFFE1: 3 4 3 RGB transparent colors in 31 transparency levels */ static PIXVAL rgbmap_day_night[RGBMAPSIZE]; /* * same as rgbmap_day_night, but always daytime colors */ static PIXVAL rgbmap_all_day[RGBMAPSIZE]; /* * used by pixel copy functions, is one of rgbmap_day_night * rgbmap_all_day */ static PIXVAL *rgbmap_current = 0; /* * mapping table for special-colors (AI player colors) * to actual output format - day&night mode * 16 sets of 16 colors */ static PIXVAL specialcolormap_day_night[256]; /* * mapping table for special-colors (AI player colors) * to actual output format - all day mode * 16 sets of 16 colors */ PIXVAL specialcolormap_all_day[256]; // offsets of first and second company color static uint8 player_offsets[MAX_PLAYER_COUNT][2]; /* * Image map descriptor structure */ struct imd { sint16 x; // current (zoomed) min x offset sint16 y; // current (zoomed) min y offset sint16 w; // current (zoomed) width sint16 h; // current (zoomed) height uint8 recode_flags; uint16 player_flags; // bit # is player number, ==1 cache image needs recoding PIXVAL* data[MAX_PLAYER_COUNT]; // current data - zoomed and recolored (player + daynight) PIXVAL* zoom_data; // zoomed original data uint32 len; // current zoom image data size (or base if not zoomed) (used for allocation purposes only) sint16 base_x; // min x offset sint16 base_y; // min y offset sint16 base_w; // width sint16 base_h; // height PIXVAL* base_data; // original image data }; // Flags for recoding #define FLAG_HAS_PLAYER_COLOR (1) #define FLAG_HAS_TRANSPARENT_COLOR (2) #define FLAG_ZOOMABLE (4) #define FLAG_REZOOM (8) //#define FLAG_POSITION_CHANGED (16) #define TRANSPARENT_RUN (0x8000u) static scr_coord_val disp_width = 640; // pitch (in pixels) of the render target texture static scr_coord_val disp_actual_width = 640; // window width static scr_coord_val disp_height = 480; // window height /* * Static buffers for rezoom_img() */ static uint8 *rezoom_baseimage[MAX_THREADS]; static PIXVAL *rezoom_baseimage2[MAX_THREADS]; static size_t rezoom_size[MAX_THREADS]; /* * Image table */ static struct imd* images = NULL; /* * Number of loaded images */ static image_id anz_images = 0; /* * Number of allocated entries for images * (>= anz_images) */ static image_id alloc_images = 0; /* * Output framebuffer */ static PIXVAL* textur = NULL; /* * dirty tile management structures */ #define DIRTY_TILE_SIZE 16 #define DIRTY_TILE_SHIFT 4 static uint32 *tile_dirty = NULL; static uint32 *tile_dirty_old = NULL; static int tiles_per_line = 0; static int tile_buffer_per_line = 0; // number of tiles that fit the allocated buffer per line - maintain alignment - x=0 is always first bit in a word for each row static int tile_lines = 0; static int tile_buffer_length = 0; static int light_level = 0; static int night_shift = -1; /* * special colors during daytime */ static rgb888_t display_day_lights[LIGHT_COUNT] = { { 0x57, 0x65, 0x6F }, // Dark windows, lit yellowish at night { 0x7F, 0x9B, 0xF1 }, // Lighter windows, lit blueish at night { 0xFF, 0xFF, 0x53 }, // Yellow light { 0xFF, 0x21, 0x1D }, // Red light { 0x01, 0xDD, 0x01 }, // Green light { 0x6B, 0x6B, 0x6B }, // Non-darkening grey 1 (menus) { 0x9B, 0x9B, 0x9B }, // Non-darkening grey 2 (menus) { 0xB3, 0xB3, 0xB3 }, // non-darkening grey 3 (menus) { 0xC9, 0xC9, 0xC9 }, // Non-darkening grey 4 (menus) { 0xDF, 0xDF, 0xDF }, // Non-darkening grey 5 (menus) { 0xE3, 0xE3, 0xFF }, // Nearly white light at day, yellowish light at night { 0xC1, 0xB1, 0xD1 }, // Windows, lit yellow { 0x4D, 0x4D, 0x4D }, // Windows, lit yellow { 0xE1, 0x00, 0xE1 }, // purple light for signals { 0x01, 0x01, 0xFF } // blue light }; /* * special colors during nighttime */ static rgb888_t display_night_lights[LIGHT_COUNT] = { { 0xD3, 0xC3, 0x80 }, // Dark windows, lit yellowish at night { 0x80, 0xC3, 0xD3 }, // Lighter windows, lit blueish at night { 0xFF, 0xFF, 0x53 }, // Yellow light { 0xFF, 0x21, 0x1D }, // Red light { 0x01, 0xDD, 0x01 }, // Green light { 0x6B, 0x6B, 0x6B }, // Non-darkening grey 1 (menus) { 0x9B, 0x9B, 0x9B }, // Non-darkening grey 2 (menus) { 0xB3, 0xB3, 0xB3 }, // non-darkening grey 3 (menus) { 0xC9, 0xC9, 0xC9 }, // Non-darkening grey 4 (menus) { 0xDF, 0xDF, 0xDF }, // Non-darkening grey 5 (menus) { 0xFF, 0xFF, 0xE3 }, // Nearly white light at day, yellowish light at night { 0xD3, 0xC3, 0x80 }, // Windows, lit yellow { 0xD3, 0xC3, 0x80 }, // Windows, lit yellow { 0xE1, 0x00, 0xE1 }, // purple light for signals { 0x01, 0x01, 0xFF } // blue light }; // the players colors and colors for simple drawing operations // each 8 colors are a player color static const rgb888_t special_pal[SPECIAL_COLOR_COUNT] = { { 36, 75, 103 }, { 57, 94, 124 }, { 76, 113, 145 }, { 96, 132, 167 }, { 116, 151, 189 }, { 136, 171, 211 }, { 156, 190, 233 }, { 176, 210, 255 }, { 88, 88, 88 }, { 107, 107, 107 }, { 125, 125, 125 }, { 144, 144, 144 }, { 162, 162, 162 }, { 181, 181, 181 }, { 200, 200, 200 }, { 219, 219, 219 }, { 17, 55, 133 }, { 27, 71, 150 }, { 37, 86, 167 }, { 48, 102, 185 }, { 58, 117, 202 }, { 69, 133, 220 }, { 79, 149, 237 }, { 90, 165, 255 }, { 123, 88, 3 }, { 142, 111, 4 }, { 161, 134, 5 }, { 180, 157, 7 }, { 198, 180, 8 }, { 217, 203, 10 }, { 236, 226, 11 }, { 255, 249, 13 }, { 86, 32, 14 }, { 110, 40, 16 }, { 134, 48, 18 }, { 158, 57, 20 }, { 182, 65, 22 }, { 206, 74, 24 }, { 230, 82, 26 }, { 255, 91, 28 }, { 34, 59, 10 }, { 44, 80, 14 }, { 53, 101, 18 }, { 63, 122, 22 }, { 77, 143, 29 }, { 92, 164, 37 }, { 106, 185, 44 }, { 121, 207, 52 }, { 0, 86, 78 }, { 0, 108, 98 }, { 0, 130, 118 }, { 0, 152, 138 }, { 0, 174, 158 }, { 0, 196, 178 }, { 0, 218, 198 }, { 0, 241, 219 }, { 74, 7, 122 }, { 95, 21, 139 }, { 116, 37, 156 }, { 138, 53, 173 }, { 160, 69, 191 }, { 181, 85, 208 }, { 203, 101, 225 }, { 225, 117, 243 }, { 59, 41, 0 }, { 83, 55, 0 }, { 107, 69, 0 }, { 131, 84, 0 }, { 155, 98, 0 }, { 179, 113, 0 }, { 203, 128, 0 }, { 227, 143, 0 }, { 87, 0, 43 }, { 111, 11, 69 }, { 135, 28, 92 }, { 159, 45, 115 }, { 183, 62, 138 }, { 230, 74, 174 }, { 245, 121, 194 }, { 255, 156, 209 }, { 20, 48, 10 }, { 44, 74, 28 }, { 68, 99, 45 }, { 93, 124, 62 }, { 118, 149, 79 }, { 143, 174, 96 }, { 168, 199, 113 }, { 193, 225, 130 }, { 54, 19, 29 }, { 82, 44, 44 }, { 110, 69, 58 }, { 139, 95, 72 }, { 168, 121, 86 }, { 197, 147, 101 }, { 226, 173, 115 }, { 255, 199, 130 }, { 8, 11, 100 }, { 14, 22, 116 }, { 20, 33, 139 }, { 26, 44, 162 }, { 41, 74, 185 }, { 57, 104, 208 }, { 76, 132, 231 }, { 96, 160, 255 }, { 43, 30, 46 }, { 68, 50, 85 }, { 93, 70, 110 }, { 118, 91, 130 }, { 143, 111, 170 }, { 168, 132, 190 }, { 193, 153, 210 }, { 219, 174, 230 }, { 63, 18, 12 }, { 90, 38, 30 }, { 117, 58, 42 }, { 145, 78, 55 }, { 172, 98, 67 }, { 200, 118, 80 }, { 227, 138, 92 }, { 255, 159, 105 }, { 11, 68, 30 }, { 33, 94, 56 }, { 54, 120, 81 }, { 76, 147, 106 }, { 98, 174, 131 }, { 120, 201, 156 }, { 142, 228, 181 }, { 164, 255, 207 }, { 64, 0, 0 }, { 96, 0, 0 }, { 128, 0, 0 }, { 192, 0, 0 }, { 255, 0, 0 }, { 255, 64, 64 }, { 255, 96, 96 }, { 255, 128, 128 }, { 0, 128, 0 }, { 0, 196, 0 }, { 0, 225, 0 }, { 0, 240, 0 }, { 0, 255, 0 }, { 64, 255, 64 }, { 94, 255, 94 }, { 128, 255, 128 }, { 0, 0, 128 }, { 0, 0, 192 }, { 0, 0, 224 }, { 0, 0, 255 }, { 0, 64, 255 }, { 0, 94, 255 }, { 0, 106, 255 }, { 0, 128, 255 }, { 128, 64, 0 }, { 193, 97, 0 }, { 215, 107, 0 }, { 235, 118, 0 }, { 255, 128, 0 }, { 255, 149, 43 }, { 255, 170, 85 }, { 255, 193, 132 }, { 8, 52, 0 }, { 16, 64, 0 }, { 32, 80, 4 }, { 48, 96, 4 }, { 64, 112, 12 }, { 84, 132, 20 }, { 104, 148, 28 }, { 128, 168, 44 }, { 164, 164, 0 }, { 180, 180, 0 }, { 193, 193, 0 }, { 215, 215, 0 }, { 235, 235, 0 }, { 255, 255, 0 }, { 255, 255, 64 }, { 255, 255, 128 }, { 32, 4, 0 }, { 64, 20, 8 }, { 84, 28, 16 }, { 108, 44, 28 }, { 128, 56, 40 }, { 148, 72, 56 }, { 168, 92, 76 }, { 184, 108, 88 }, { 64, 0, 0 }, { 96, 8, 0 }, { 112, 16, 0 }, { 120, 32, 8 }, { 138, 64, 16 }, { 156, 72, 32 }, { 174, 96, 48 }, { 192, 128, 64 }, { 32, 32, 0 }, { 64, 64, 0 }, { 96, 96, 0 }, { 128, 128, 0 }, { 144, 144, 0 }, { 172, 172, 0 }, { 192, 192, 0 }, { 224, 224, 0 }, { 64, 96, 8 }, { 80, 108, 32 }, { 96, 120, 48 }, { 112, 144, 56 }, { 128, 172, 64 }, { 150, 210, 68 }, { 172, 238, 80 }, { 192, 255, 96 }, { 32, 32, 32 }, { 48, 48, 48 }, { 64, 64, 64 }, { 80, 80, 80 }, { 96, 96, 96 }, { 172, 172, 172 }, { 236, 236, 236 }, { 255, 255, 255 }, { 41, 41, 54 }, { 60, 45, 70 }, { 75, 62, 108 }, { 95, 77, 136 }, { 113, 105, 150 }, { 135, 120, 176 }, { 165, 145, 218 }, { 198, 191, 232 } }; // // fwd decls // static PIXVAL simgraph16_palette_lookup (palette_index_t idx); static palette_index_t simgraph16_palette_indexof (PIXVAL color); static rgb888_t simgraph16_get_color_rgb (palette_index_t idx); static void simgraph16_env_t_rgb_to_system_colors (); static void simgraph16_set_player_color_scheme (const int player, const uint8 col1, const uint8 col2); static void simgraph16_set_light_color (int color_idx, rgb888_t day_colour, rgb888_t night_colour); static void simgraph16_set_daynight_level (int night); static scr_coord_val simgraph16_set_base_raster_width (scr_coord_val new_raster); static int simgraph16_zoom_factor_up (); static int simgraph16_zoom_factor_down (); static bool simgraph16_init (scr_size window_size, sint16 full_screen); static bool simgraph16_is_display_init (); static void simgraph16_exit (); static void simgraph16_on_window_resized (scr_size new_window_size); static bool simgraph16_load_font (const char *fname, bool reload); static image_id simgraph16_get_image_count (); static image_id simgraph16_register_image (const image_t *image_in); static void simgraph16_free_all_images_above (image_id above ); static scr_rect simgraph16_get_base_image_offset (image_id image); static scr_rect simgraph16_get_image_offset (image_id image); static void simgraph16_mark_img_dirty (image_id image, scr_coord_val xp, scr_coord_val yp); static void simgraph16_mark_rect_dirty_wc (scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2); static void simgraph16_mark_rect_dirty_clip (scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2 CLIP_NUM_DEF); static void simgraph16_mark_screen_dirty (); static scr_size simgraph16_get_screen_size (); static void simgraph16_set_screen_actual_width (scr_coord_val w); static void simgraph16_set_screen_height (scr_coord_val const h); static scr_size simgraph16_get_best_matching_size (const image_id n, sint16 zoom_percent); static void simgraph16_fit_img_to_width (const image_id n, sint16 new_w); static void simgraph16_move_scroll_band (scr_coord_val start_y, scr_coord_val x_offset, scr_coord_val h); static void simgraph16_draw_img_aligned (const image_id n, scr_rect area, int align, const bool dirty); static void simgraph16_set_image_procs (bool is_global); static void simgraph16_draw_img_aux (const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph16_draw_rezoomed_img_blend (const image_id, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph16_draw_rezoomed_img_alpha (const image_id, const image_id, const unsigned, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph16_draw_color_img (const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph16_draw_base_img (const image_id, scr_coord_val, scr_coord_val, const sint8, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph16_draw_base_img_blend (const image_id, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, const bool CLIP_NUM_DEF_NOUSE); static void simgraph16_draw_base_img_alpha (const image_id, const image_id, const unsigned, scr_coord_val, scr_coord_val, const sint8, const FLAGGED_PIXVAL, const bool, bool CLIP_NUM_DEF_NOUSE); static void simgraph16_draw_stretch_map (const stretch_map_t &imag, scr_rect area); static void simgraph16_draw_stretch_map_blend (const stretch_map_t &imag, scr_rect area, FLAGGED_PIXVAL color); static PIXVAL simgraph16_blend_colors (PIXVAL background, PIXVAL foreground, int percent_blend); static void simgraph16_tint_rect (scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL colval, int opacity); static void simgraph16_draw_rect (scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty); static void simgraph16_draw_rect_clipped (scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty CLIP_NUM_DEF); static void simgraph16_draw_rounded_rect_clipped (scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty); static void simgraph16_draw_vline_clipped (scr_coord_val xp, scr_coord_val yp, scr_coord_val h, PIXVAL color, bool dirty CLIP_NUM_DEF); static void simgraph16_flush_framebuffer (); static void simgraph16_set_cursor_visible (bool show); static void simgraph16_set_default_cursor (int cursor_id); static void simgraph16_set_show_load_cursor (bool show); static void simgraph16_draw_array (scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, const PIXVAL *arr); static scr_coord_val simgraph16_calc_text_width_n (const char *text, size_t len); static scr_size simgraph16_calc_multiline_text_size (const char *text); static size_t simgraph16_calc_text_index_for_width (const char *, scr_coord_val); static bool simgraph16_font_has_character (utf16 char_code); static utf32 simgraph16_get_next_char_with_metrics (const char* &text, unsigned char &byte_length, unsigned char &pixel_width); static utf32 simgraph16_get_prev_char_with_metrics (const char* &text, const char *const text_start, unsigned char &byte_length, unsigned char &pixel_width); static scr_coord_val simgraph16_get_char_width (utf32 c); static scr_coord_val simgraph16_get_number_width (); static scr_coord_val simgraph16_draw_text_clipped_n (scr_coord_val x, scr_coord_val y, const char* txt, control_alignment_t flags, const PIXVAL color, bool dirty, sint32 len CLIP_NUM_DEF); static scr_coord_val simgraph16_draw_multiline_text (scr_coord_val, scr_coord_val, const char *, PIXVAL); static void simgraph16_draw_text_ellipsis_shadowed(scr_rect, const char *, int, PIXVAL, bool, bool, PIXVAL); static void simgraph16_draw_text_outlined (scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, const char *, int); static void simgraph16_draw_text_shadowed (scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, const char *, int); static void simgraph16_draw_box3d (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, PIXVAL, bool); static void simgraph16_draw_box3d_clipped (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL, PIXVAL); static void simgraph16_draw_textbox3d_clipped (scr_coord_val, scr_coord_val, FLAGGED_PIXVAL, FLAGGED_PIXVAL, const char *, int CLIP_NUM_DEF_NOUSE); static void simgraph16_draw_line (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL); static void simgraph16_draw_line_dotted (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, PIXVAL); static void simgraph16_draw_empty_circle (scr_coord_val, scr_coord_val, int, const PIXVAL); static void simgraph16_draw_filled_circle (scr_coord_val, scr_coord_val, int, const PIXVAL); static void simgraph16_draw_bezier (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val, const PIXVAL, scr_coord_val, scr_coord_val); static void simgraph16_draw_right_triangle (scr_coord_val, scr_coord_val, scr_coord_val, const PIXVAL, const bool); static bool simgraph16_take_screenshot (const scr_rect &); static void simgraph16_draw_signal_direction (scr_coord_val, scr_coord_val, uint8, uint8, PIXVAL, PIXVAL, bool, uint8); static void simgraph16_set_clip_rect (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val CLIP_NUM_DEF, bool fit); static clip_dimension simgraph16_get_clip_rect (CLIP_NUM_DEF_NOUSE0); static void simgraph16_push_clip_rect (scr_coord_val, scr_coord_val, scr_coord_val, scr_coord_val CLIP_NUM_DEF_NOUSE); static void simgraph16_swap_clip_rect (CLIP_NUM_DEF_NOUSE0); static void simgraph16_pop_clip_rect (CLIP_NUM_DEF_NOUSE0); static void simgraph16_add_poly_clip (int x0,int y0, int x1, int y1, int ribi CLIP_NUM_DEF); static void simgraph16_clear_all_poly_clip (CLIP_NUM_DEF0); static void simgraph16_activate_ribi_clip (int ribi CLIP_NUM_DEF); simgraph_t g_simgraph16 = { /*.type =*/ SIMGRAPH_TYPE_SOFTWARE, /*.tile_raster_width =*/ 16, // zoomed /*.base_tile_raster_width =*/ 16, // original /*.current_tile_raster_width =*/ 0, /*.draw_normal =*/ NULL, /*.draw_color =*/ NULL, /*.draw_blend =*/ NULL, /*.draw_alpha =*/ NULL, /*.zoom_num =*/ { 2, 3, 4, 1, 3, 5, 1, 3, 1, 1 }, /*.zoom_den =*/ { 1, 2, 3, 1, 4, 8, 2, 8, 4, 8 }, /*.palette_lookup =*/ simgraph16_palette_lookup, /*.palette_indexof =*/ simgraph16_palette_indexof, /*.get_color_rgb =*/ simgraph16_get_color_rgb, /*.env_t_rgb_to_system_colors =*/ simgraph16_env_t_rgb_to_system_colors, /*.set_player_color_scheme =*/ simgraph16_set_player_color_scheme, /*.set_light_color =*/ simgraph16_set_light_color, /*.set_daynight_level =*/ simgraph16_set_daynight_level, /*.set_base_raster_width =*/ simgraph16_set_base_raster_width, /*.zoom_factor_up =*/ simgraph16_zoom_factor_up, /*.zoom_factor_down =*/ simgraph16_zoom_factor_down, /*.init =*/ simgraph16_init, /*.is_display_init =*/ simgraph16_is_display_init, /*.exit =*/ simgraph16_exit, /*.on_window_resized =*/ simgraph16_on_window_resized, /*.load_font =*/ simgraph16_load_font, /*.get_image_count =*/ simgraph16_get_image_count, /*.register_image =*/ simgraph16_register_image, /*.free_all_images_above =*/ simgraph16_free_all_images_above, /*.get_base_image_offset =*/ simgraph16_get_base_image_offset, /*.get_image_offset =*/ simgraph16_get_image_offset, /*.mark_img_dirty =*/ simgraph16_mark_img_dirty, /*.mark_rect_dirty_wc =*/ simgraph16_mark_rect_dirty_wc, /*.mark_rect_dirty_clip =*/ simgraph16_mark_rect_dirty_clip, /*.mark_screen_dirty =*/ simgraph16_mark_screen_dirty, /*.get_screen_size =*/ simgraph16_get_screen_size, /*.set_screen_height =*/ simgraph16_set_screen_height, /*.set_screen_actual_width =*/ simgraph16_set_screen_actual_width, /*.get_best_matching_size =*/ simgraph16_get_best_matching_size, /*.fit_img_to_width =*/ simgraph16_fit_img_to_width, /*.move_scroll_band =*/ simgraph16_move_scroll_band, /*.set_image_procs =*/ simgraph16_set_image_procs, /*.draw_img_aligned =*/ simgraph16_draw_img_aligned, /*.draw_img_aux =*/ simgraph16_draw_img_aux, /*.draw_rezoomed_img_blend =*/ simgraph16_draw_rezoomed_img_blend, /*.draw_rezoomed_img_alpha =*/ simgraph16_draw_rezoomed_img_alpha, /*.draw_color_img =*/ simgraph16_draw_color_img, /*.draw_base_img =*/ simgraph16_draw_base_img, /*.draw_base_img_blend =*/ simgraph16_draw_base_img_blend, /*.draw_base_img_alpha =*/ simgraph16_draw_base_img_alpha, /*.draw_stretch_map =*/ simgraph16_draw_stretch_map, /*.draw_stretch_map_blend =*/ simgraph16_draw_stretch_map_blend, /*.blend_colors =*/ simgraph16_blend_colors, /*.tint_rect =*/ simgraph16_tint_rect, /*.draw_rect =*/ simgraph16_draw_rect, /*.draw_rect_clipped =*/ simgraph16_draw_rect_clipped, /*.draw_rounded_rect_clipped =*/ simgraph16_draw_rounded_rect_clipped, /*.draw_vline_clipped =*/ simgraph16_draw_vline_clipped, /*.flush_framebuffer =*/ simgraph16_flush_framebuffer, /*.set_cursor_visible =*/ simgraph16_set_cursor_visible, /*.set_default_cursor =*/ simgraph16_set_default_cursor, /*.set_show_load_cursor =*/ simgraph16_set_show_load_cursor, /*.draw_array =*/ simgraph16_draw_array, /*.font_has_character =*/ simgraph16_font_has_character, /*.get_char_width =*/ simgraph16_get_char_width, /*.get_number_width =*/ simgraph16_get_number_width, /*.get_next_char_with_metrics =*/ simgraph16_get_next_char_with_metrics, /*.get_prev_char_with_metrics =*/ simgraph16_get_prev_char_with_metrics, /*.calc_text_width_n =*/ simgraph16_calc_text_width_n, /*.calc_multiline_text_size =*/ simgraph16_calc_multiline_text_size, /*.calc_text_index_for_width =*/ simgraph16_calc_text_index_for_width, /*.draw_text_clipped_n =*/ simgraph16_draw_text_clipped_n, /*.draw_multiline_text =*/ simgraph16_draw_multiline_text, /*.draw_text_ellipsis_shadowed =*/ simgraph16_draw_text_ellipsis_shadowed, /*.draw_text_outlined =*/ simgraph16_draw_text_outlined, /*.draw_text_shadowed =*/ simgraph16_draw_text_shadowed, /*.draw_box3d =*/ simgraph16_draw_box3d, /*.draw_box3d_clipped =*/ simgraph16_draw_box3d_clipped, /*.draw_textbox3d_clipped =*/ simgraph16_draw_textbox3d_clipped, /*.draw_line =*/ simgraph16_draw_line, /*.draw_line_dotted =*/ simgraph16_draw_line_dotted, /*.draw_empty_circle =*/ simgraph16_draw_empty_circle, /*.draw_filled_circle =*/ simgraph16_draw_filled_circle, /*.draw_bezier =*/ simgraph16_draw_bezier, /*.draw_right_triangle =*/ simgraph16_draw_right_triangle, /*.draw_signal_direction =*/ simgraph16_draw_signal_direction, /*.take_screenshot =*/ simgraph16_take_screenshot, /*.set_clip_rect =*/ simgraph16_set_clip_rect, /*.get_clip_rect =*/ simgraph16_get_clip_rect, /*.push_clip_rect =*/ simgraph16_push_clip_rect, /*.swap_clip_rect =*/ simgraph16_swap_clip_rect, /*.pop_clip_rect =*/ simgraph16_pop_clip_rect, /*.add_poly_clip =*/ simgraph16_add_poly_clip, /*.clear_all_poly_clip =*/ simgraph16_clear_all_poly_clip, /*.activate_ribi_clip =*/ simgraph16_activate_ribi_clip, }; static uint32 zoom_factor = ZOOM_NEUTRAL; static inline rgb888_t pixval_to_rgb888(PIXVAL colour) { // Scale each colour channel from 5 or 6 bits to 8 bits #ifdef RGB555 return { uint8(((colour >> 10) & 0x1F) * 0xFF / 0x1F), // R uint8(((colour >> 5) & 0x1F) * 0xFF / 0x1F), // G uint8(((colour >> 0) & 0x1F) * 0xFF / 0x1F) // B }; #else return { uint8(((colour >> 11) & 0x1F) * 0xFF / 0x1F), // R uint8(((colour >> 5) & 0x3F) * 0xFF / 0x3F), // G uint8(((colour >> 0) & 0x1F) * 0xFF / 0x1F) // B }; #endif } static inline PIXVAL pixval_to_rgb343(PIXVAL rgb) { // msb lsb // rgb555: xrrrrrgggggbbbbb // rgb565: rrrrrggggggbbbbb // rgb343: rrrggggbbb #ifdef RGB555 return ((rgb >> 5) & 0x0380) | ((rgb >> 3) & 0x0078) | ((rgb >> 2) & 0x07); #else return ((rgb >> 6) & 0x0380) | ((rgb >> 4) & 0x0078) | ((rgb >> 2) & 0x07); #endif } /* * Gets a colour index and returns RGB888 */ static rgb888_t simgraph16_get_color_rgb(palette_index_t idx) { // special_pal has 224 rgb colors if (idx < SPECIAL_COLOR_COUNT) { return special_pal[idx]; } // if it uses one of the special light colours it's under display_day_lights if (idx < SPECIAL_COLOR_COUNT + LIGHT_COUNT) { return display_day_lights[idx - SPECIAL_COLOR_COUNT]; } // Return black for anything else return rgb888_t{0,0,0}; } /** * Convert indexed colors to rgb and back */ static PIXVAL simgraph16_palette_lookup(palette_index_t idx) { return specialcolormap_all_day[idx]; } static palette_index_t simgraph16_palette_indexof(PIXVAL color) { for(PIXVAL i=0; i<=0xff; i++) { if (specialcolormap_all_day[i] == color) { return (palette_index_t)i; } } return 0; } /* * Convert env_t colours from RGB888 to the system format */ static void simgraph16_env_t_rgb_to_system_colors() { // get system colours for the default colours or settings.xml env_t::default_window_title_color = get_system_color(env_t::default_window_title_color_rgb); env_t::front_window_text_color = get_system_color(env_t::front_window_text_color_rgb); env_t::bottom_window_text_color = get_system_color(env_t::bottom_window_text_color_rgb); env_t::tooltip_color = get_system_color(env_t::tooltip_color_rgb); env_t::tooltip_textcolor = get_system_color(env_t::tooltip_textcolor_rgb); env_t::cursor_overlay_color = get_system_color(env_t::cursor_overlay_color_rgb); env_t::background_color = get_system_color(env_t::background_color_rgb); } /* changes the raster width after loading */ static scr_coord_val simgraph16_set_base_raster_width(scr_coord_val new_raster) { scr_coord_val old = g_simgraph16.base_tile_raster_width; g_simgraph16.base_tile_raster_width = new_raster; g_simgraph16.tile_raster_width = (new_raster * g_simgraph16.zoom_num[zoom_factor]) / g_simgraph16.zoom_den[zoom_factor]; return old; } // ----------------------------------- clipping routines ------------------------------------------ static scr_size simgraph16_get_screen_size() { return scr_size{ disp_actual_width, disp_height }; } // only use, if you are really really sure! static void simgraph16_set_screen_actual_width(scr_coord_val w) { disp_actual_width = w; } static void simgraph16_set_screen_height(scr_coord_val const h) { disp_height = h; } /** * Clips intervall [x,x+w] such that left <= x and x+w <= right. * If @p w is negative, it stays negative. * @returns difference between old and new value of @p x. */ inline int clip_intv(scr_coord_val &x, scr_coord_val &w, const scr_coord_val left, const scr_coord_val right) { scr_coord_val xx = min(x+w, right); scr_coord_val xoff = left - x; if (xoff > 0) { // equivalent to x < left x = left; } else { xoff = 0; } w = xx - x; return xoff; } /// wrapper for clip_intv static int clip_wh(scr_coord_val *x, scr_coord_val *w, const scr_coord_val left, const scr_coord_val right) { return clip_intv(*x, *w, left, right); } /// wrapper for clip_intv, @returns whether @p w is positive static bool clip_lr(scr_coord_val *x, scr_coord_val *w, const scr_coord_val left, const scr_coord_val right) { clip_intv(*x, *w, left, right); return *w > 0; } /** * Get the clipping rectangle dimensions */ static clip_dimension simgraph16_get_clip_rect(CLIP_NUM_DEF0) { return CR.clip_rect; } /** * Set the clipping rectangle dimensions * * here, a pixel at coordinate xp is displayed if * clip. x <= xp < clip.xx * the right-most pixel of an image located at xp with width w is displayed if * clip.x < xp+w <= clip.xx * analogously for the y coordinate */ static void simgraph16_set_clip_rect(scr_coord_val x, scr_coord_val y, scr_coord_val w, scr_coord_val h CLIP_NUM_DEF, bool fit) { if (!fit) { clip_wh( &x, &w, 0, disp_width); clip_wh( &y, &h, 0, disp_height); } else { clip_wh( &x, &w, CR.clip_rect.x, CR.clip_rect.xx); clip_wh( &y, &h, CR.clip_rect.y, CR.clip_rect.yy); } CR.clip_rect.x = x; CR.clip_rect.y = y; CR.clip_rect.w = w; CR.clip_rect.h = h; CR.clip_rect.xx = x + w; // watch out, clips to scr_coord_val max CR.clip_rect.yy = y + h; // watch out, clips to scr_coord_val max } static void simgraph16_push_clip_rect(scr_coord_val x, scr_coord_val y, scr_coord_val w, scr_coord_val h CLIP_NUM_DEF) { assert(!CR.swap_active); // save active clipping rectangle CR.clip_rect_swap = CR.clip_rect; // active rectangle provided by parameters simgraph16_set_clip_rect(x, y, w, h CLIP_NUM_PAR, false); CR.swap_active = true; } static void simgraph16_swap_clip_rect(CLIP_NUM_DEF0) { if (CR.swap_active) { // swap clipping rectangles clip_dimension save = CR.clip_rect; CR.clip_rect = CR.clip_rect_swap; CR.clip_rect_swap = save; } } static void simgraph16_pop_clip_rect(CLIP_NUM_DEF0) { if (CR.swap_active) { // swap original clipping rectangle back CR.clip_rect = CR.clip_rect_swap; CR.swap_active = false; } } /* * Add clipping line through (x0,y0) and (x1,y1) * with associated ribi * if ribi & 16 then non-convex clipping. */ static void simgraph16_add_poly_clip(int x0,int y0, int x1, int y1, int ribi CLIP_NUM_DEF) { if( CR.number_of_clips < MAX_POLY_CLIPS ) { CR.poly_clips[CR.number_of_clips].clip_from_to( x0, y0, x1, y1, ribi&16 ); CR.clip_ribi[CR.number_of_clips] = ribi&15; CR.number_of_clips++; } } /* * Clears all clipping lines */ static void simgraph16_clear_all_poly_clip(CLIP_NUM_DEF0) { CR.number_of_clips = 0; CR.active_ribi = 15; // set all to active } /* * Activates clipping lines associated with ribi * ie if clip_ribi[i] & active_ribi */ static void simgraph16_activate_ribi_clip(int ribi CLIP_NUM_DEF) { CR.active_ribi = ribi; } /* * Initialize clipping region for image starting at screen line y */ static inline void init_ranges(int y CLIP_NUM_DEF) { for( uint8 i = 0; i < CR.number_of_clips; i++ ) { if( (CR.clip_ribi[i] & CR.active_ribi) ) { CR.poly_clips[i].get_x_range( y, CR.xranges[i], CR.active_ribi & 16 ); } } } /* * Returns left/right border of visible area * Computes l/r border for the next line (ie y+1) * takes also clipping rectangle into account */ inline void get_xrange_and_step_y(int &xmin, int &xmax CLIP_NUM_DEF) { xmin = CR.clip_rect.x; xmax = CR.clip_rect.xx; for( uint8 i = 0; i < CR.number_of_clips; i++ ) { if( (CR.clip_ribi[i] & CR.active_ribi) ) { CR.poly_clips[i].inc_y( CR.xranges[i], xmin, xmax ); } } } // ------------------------------ dirty tile stuff -------------------------------- /* * Simutrans keeps a list of dirty areas, i.e. places that received new graphics * and must be copied to the screen after an update */ static inline void mark_tile_dirty(const int x, const int y) { const int bit = x + y * tile_buffer_per_line; #if 0 assert(bit / 8 < tile_buffer_length); #endif tile_dirty[bit >> 5] |= 1 << (bit & 31); } /** * Mark tile as dirty, with _NO_ clipping */ static void mark_rect_dirty_nc(scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2) { // floor to tile size x1 >>= DIRTY_TILE_SHIFT; y1 >>= DIRTY_TILE_SHIFT; x2 >>= DIRTY_TILE_SHIFT; y2 >>= DIRTY_TILE_SHIFT; #if 0 assert(x1 >= 0); assert(x1 < tiles_per_line); assert(y1 >= 0); assert(y1 < tile_lines); assert(x2 >= 0); assert(x2 < tiles_per_line); assert(y2 >= 0); assert(y2 < tile_lines); #endif for( ; y1 <= y2; y1++ ) { int bit = y1 * tile_buffer_per_line + x1; const int end = bit + x2 - x1; do { tile_dirty[bit >> 5] |= 1 << (bit & 31); } while( ++bit <= end ); } } /** * Mark tile as dirty, with clipping */ static void simgraph16_mark_rect_dirty_wc(scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2) { // inside display? if( x2 >= 0 && y2 >= 0 && x1 < disp_width && y1 < disp_height ) { if( x1 < 0 ) { x1 = 0; } if( y1 < 0 ) { y1 = 0; } if( x2 >= disp_width ) { x2 = disp_width - 1; } if( y2 >= disp_height ) { y2 = disp_height - 1; } mark_rect_dirty_nc( x1, y1, x2, y2 ); } } static void simgraph16_mark_rect_dirty_clip(scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2 CLIP_NUM_DEF) { // inside clip_rect? if( x2 >= CR.clip_rect.x && y2 >= CR.clip_rect.y && x1 < CR.clip_rect.xx && y1 < CR.clip_rect.yy ) { if( x1 < CR.clip_rect.x ) { x1 = CR.clip_rect.x; } if( y1 < CR.clip_rect.y ) { y1 = CR.clip_rect.y; } if( x2 >= CR.clip_rect.xx ) { x2 = CR.clip_rect.xx-1; } if( y2 >= CR.clip_rect.yy ) { y2 = CR.clip_rect.yy-1; } mark_rect_dirty_nc( x1, y1, x2, y2 ); } } /** * Mark the whole screen as dirty. * */ static void simgraph16_mark_screen_dirty() { memset( tile_dirty, 0xFFFFFFFF, sizeof(uint32) * tile_buffer_length ); } /** * the area of this image need update */ static void simgraph16_mark_img_dirty(image_id image, scr_coord_val xp, scr_coord_val yp) { if( image < anz_images ) { simgraph16_mark_rect_dirty_wc( xp + images[image].x, yp + images[image].y, xp + images[image].x + images[image].w - 1, yp + images[image].y + images[image].h - 1 ); } } // ------------------------- rendering images for display -------------------------------- /* * Simutrans caches player colored images, to allow faster drawing of them * They are derived from a base image, which may need zooming too */ /** * Flag all images for rezoom on next draw */ static void rezoom() { for( image_id n = 0; n < anz_images; n++ ) { if( (images[n].recode_flags & FLAG_ZOOMABLE) != 0 && images[n].base_h > 0 ) { images[n].recode_flags |= FLAG_REZOOM; } } } void set_zoom_factor(int z) { // do not zoom beyond 4 pixels if( (g_simgraph16.base_tile_raster_width * g_simgraph16.zoom_num[z]) / g_simgraph16.zoom_den[z] > 4 ) { zoom_factor = z; g_simgraph16.tile_raster_width = (g_simgraph16.base_tile_raster_width * g_simgraph16.zoom_num[zoom_factor]) / g_simgraph16.zoom_den[zoom_factor]; dbg->message("set_zoom_factor()", "Zoom level now %d (%i/%i)", zoom_factor, g_simgraph16.zoom_num[zoom_factor], g_simgraph16.zoom_den[zoom_factor] ); rezoom(); } } static int simgraph16_zoom_factor_up() { // zoom out, if size permits if( zoom_factor > 0 ) { set_zoom_factor( zoom_factor-1 ); return true; } return false; } static int simgraph16_zoom_factor_down() { if( zoom_factor < MAX_ZOOM_FACTOR ) { set_zoom_factor( zoom_factor+1 ); return true; } return false; } static uint8 player_night=0xFF; static uint8 player_day=0xFF; static void activate_player_color(sint8 player_nr, bool daynight) { // caches the last settings if(!daynight) { if(player_day!=player_nr) { int i; player_day = player_nr; for(i=0; i<8; i++ ) { rgbmap_all_day[0x8000+i] = specialcolormap_all_day[player_offsets[player_day][0]+i]; rgbmap_all_day[0x8008+i] = specialcolormap_all_day[player_offsets[player_day][1]+i]; } } rgbmap_current = rgbmap_all_day; } else { // changing color table if(player_night!=player_nr) { int i; player_night = player_nr; for(i=0; i<8; i++ ) { rgbmap_day_night[0x8000+i] = specialcolormap_day_night[player_offsets[player_night][0]+i]; rgbmap_day_night[0x8008+i] = specialcolormap_day_night[player_offsets[player_night][1]+i]; } } rgbmap_current = rgbmap_day_night; } } /** * Flag all images to recode colors on next draw */ static void recode() { for( image_id n = 0; n < anz_images; n++ ) { images[n].player_flags = 0xFFFF; // recode all player colors } } /** * Convert a certain image data to actual output data */ static void recode_img_src_target(scr_coord_val h, PIXVAL *src, PIXVAL *target) { if( h > 0 ) { do { uint16 runlen = *target++ = *src++; // decode rows do { // clear run is always ok runlen = *target++ = *src++; if( runlen & TRANSPARENT_RUN ) { runlen &= ~TRANSPARENT_RUN; while( runlen-- ) { if( *src < 0x8020+(31*16) ) { // expand transparent player color const uint8 alpha = (*src-0x8020) % 31; const PIXVAL colour = rgbmap_day_night[(*src-0x8020)/31+0x8000]; *target++ = 0x8020 + 31*31 + pixval_to_rgb343(colour)*31 + alpha; src ++; } else { *target++ = *src++; } } } else { // now just convert the color pixels while( runlen-- ) { *target++ = rgbmap_day_night[*src++]; } } // next clear run or zero = end } while( (runlen = *target++ = *src++) ); } while( --h ); } } static image_id simgraph16_get_image_count() { return anz_images; } /** * Handles the conversion of an image to the output color */ static void recode_img(const image_id n, const sint8 player_nr) { // may this image be zoomed #ifdef MULTI_THREAD pthread_mutex_lock( &recode_img_mutex ); if( (images[n].player_flags & (1< return transparent } // pixel transparent but all three neighbors not -> interpolate uint8 valid=0; uint8 r=0, g=0, b=0; SumSubpixel(pab); SumSubpixel(prl); if(valid==0) { return 0x73FE; } else if(valid==255) { return (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10); } else { return (r/valid) + (((uint16)(g/valid))<<5) + (((uint16)(b/valid))<<10); } } else { if ( (pab[0] & prl[0] & pdia[0])!=255) { // pixel and one neighbor not transparent return compress_pixel(p); } return 0x73FE; } } /** * Convert base image data to actual image size * Uses averages of all sampled points to get the "real" value * Blurs a bit */ static void rezoom_img(const image_id n) { // may this image be zoomed if( n < anz_images && images[n].base_h > 0 ) { #ifdef MULTI_THREAD pthread_mutex_lock( &rezoom_img_mutex[n % env_t::num_threads] ); if( (images[n].recode_flags & FLAG_REZOOM) == 0 ) { // other routine did already the re-zooming ... pthread_mutex_unlock( &rezoom_img_mutex[n % env_t::num_threads] ); return; } #endif // we may need night conversion afterwards images[n].player_flags = 0xFFFF; // recode all player colors // we recalculate the len (since it may be larger than before) // thus we have to free the old caches if( images[n].zoom_data != NULL ) { free( images[n].zoom_data ); images[n].zoom_data = NULL; } for( uint8 i = 0; i < MAX_PLAYER_COUNT; i++ ) { if( images[n].data[i] != NULL ) { free( images[n].data[i] ); images[n].data[i] = NULL; } } // just restore original size? if( zoom_factor == ZOOM_NEUTRAL || (images[n].recode_flags&FLAG_ZOOMABLE) == 0 ) { // this we can do be a simple copy ... images[n].x = images[n].base_x; images[n].w = images[n].base_w; images[n].y = images[n].base_y; images[n].h = images[n].base_h; // recalculate length sint16 h = images[n].base_h; PIXVAL *sp = images[n].base_data; while( h-- > 0 ) { do { // clear run + colored run + next clear run sp++; sp += (*sp)&(~TRANSPARENT_RUN); // MSVC crashes on (*sp)&(~TRANSPARENT_RUN) + 1 !!! sp ++; } while( *sp ); sp++; } images[n].len = (uint32)(size_t)(sp - images[n].base_data); images[n].recode_flags &= ~FLAG_REZOOM; #ifdef MULTI_THREAD pthread_mutex_unlock( &rezoom_img_mutex[n % env_t::num_threads] ); #endif return; } // now we want to downsize the image // just divide the sizes images[n].x = (images[n].base_x * g_simgraph16.zoom_num[zoom_factor]) / g_simgraph16.zoom_den[zoom_factor]; images[n].y = (images[n].base_y * g_simgraph16.zoom_num[zoom_factor]) / g_simgraph16.zoom_den[zoom_factor]; images[n].w = (images[n].base_w * g_simgraph16.zoom_num[zoom_factor]) / g_simgraph16.zoom_den[zoom_factor]; images[n].h = (images[n].base_h * g_simgraph16.zoom_num[zoom_factor]) / g_simgraph16.zoom_den[zoom_factor]; if( images[n].h > 0 && images[n].w > 0 ) { // just recalculate the image in the new size PIXVAL *src = images[n].base_data; PIXVAL *dest = NULL; // embed the baseimage in an image with margin ~ remainder const sint16 x_rem = (images[n].base_x * g_simgraph16.zoom_num[zoom_factor]) % g_simgraph16.zoom_den[zoom_factor]; const sint16 y_rem = (images[n].base_y * g_simgraph16.zoom_num[zoom_factor]) % g_simgraph16.zoom_den[zoom_factor]; const sint16 xl_margin = max( x_rem, 0); const sint16 xr_margin = max(-x_rem, 0); const sint16 yl_margin = max( y_rem, 0); const sint16 yr_margin = max(-y_rem, 0); // baseimage top-left corner is at (xl_margin, yl_margin) // ... low-right corner is at (xr_margin, yr_margin) sint32 orgzoomwidth = ((images[n].base_w + g_simgraph16.zoom_den[zoom_factor] - 1 ) / g_simgraph16.zoom_den[zoom_factor]) * g_simgraph16.zoom_den[zoom_factor]; sint32 newzoomwidth = (orgzoomwidth*g_simgraph16.zoom_num[zoom_factor])/g_simgraph16.zoom_den[zoom_factor]; sint32 orgzoomheight = ((images[n].base_h + g_simgraph16.zoom_den[zoom_factor] - 1 ) / g_simgraph16.zoom_den[zoom_factor]) * g_simgraph16.zoom_den[zoom_factor]; sint32 newzoomheight = (orgzoomheight * g_simgraph16.zoom_num[zoom_factor]) / g_simgraph16.zoom_den[zoom_factor]; // we will unpack, re-sample, pack it // thus the unpack buffer must at least fit the window => find out maximum size // Note: This value is certainly way bigger than the average size we'll get, // but it's the worst scenario possible, a succession of solid - transparent - solid - transparent // pattern. // This would encode EACH LINE as: // 0x0000 (0 transparent) 0x0001 PIXWORD 0x0001 (every 2 pixels, 3 words) 0x0000 (EOL) // The extra +1 is to make sure we cover divisions with module != 0 // We end with an over sized buffer for the normal usage, but since it's re-used for all re-zooms, // it's not performance critical and we are safe from all possible inputs. size_t new_size = ( ( (newzoomwidth * 3) / 2 ) + 1 + 2) * newzoomheight * sizeof(PIXVAL); size_t unpack_size = (xl_margin + orgzoomwidth + xr_margin) * (yl_margin + orgzoomheight + yr_margin) * 4; if( unpack_size > new_size ) { new_size = unpack_size; } new_size = ((new_size * 128) + 127) / 128; // enlarge slightly to try and keep buffers on their own cacheline for multithreaded access. A portable aligned_alloc would be better. if( rezoom_size[n % env_t::num_threads] < new_size ) { free( rezoom_baseimage2[n % env_t::num_threads] ); free( rezoom_baseimage[n % env_t::num_threads] ); rezoom_size[n % env_t::num_threads] = new_size; rezoom_baseimage[n % env_t::num_threads] = MALLOCN( uint8, new_size ); rezoom_baseimage2[n % env_t::num_threads] = (PIXVAL *)MALLOCN( uint8, new_size ); } memset( rezoom_baseimage[n % env_t::num_threads], 255, new_size ); // fill with invalid data to mark transparent regions // index of top-left corner uint32 baseoff = 4 * (yl_margin * (xl_margin + orgzoomwidth + xr_margin) + xl_margin); sint32 basewidth = xl_margin + orgzoomwidth + xr_margin; // now: unpack the image for( sint32 y = 0; y < images[n].base_h; ++y ) { uint16 runlen; uint8 *p = rezoom_baseimage[n % env_t::num_threads] + baseoff + y * (basewidth * 4); // decode line runlen = *src++; do { // clear run p += (runlen & ~TRANSPARENT_RUN) * 4; // color pixel runlen = (*src++) & ~TRANSPARENT_RUN; while( runlen-- ) { // get rgb components PIXVAL s = *src++; *p++ = (s>>15); *p++ = (s & 31); s >>= 5; *p++ = (s & 31); s >>= 5; *p++ = (s & 31); } runlen = *src++; } while( runlen != 0 ); } // now we have the image, we do a repack then dest = rezoom_baseimage2[n % env_t::num_threads]; switch( g_simgraph16.zoom_den[zoom_factor] ) { case 1: { assert(g_simgraph16.zoom_num[zoom_factor]==2); // first half row - just copy values, do not fiddle with neighbor colors uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff; for( sint16 x = 0; x < orgzoomwidth; x++ ) { PIXVAL c1 = compress_pixel_transparent( p1 + (x * 4) ); // now set the pixel ... dest[x * 2] = c1; dest[x * 2 + 1] = c1; } // skip one line dest += newzoomwidth; for( sint16 y = 0; y < orgzoomheight - 1; y++ ) { uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + y * (basewidth * 4); // copy leftmost pixels dest[0] = compress_pixel_transparent( p1 ); dest[newzoomwidth] = compress_pixel_transparent( p1 + basewidth * 4 ); for( sint16 x = 0; x < orgzoomwidth - 1; x++ ) { uint8 *px1 = p1 + (x * 4); // pixel at 2,2 in 2x2 superpixel dest[x * 2 + 1] = zoomin_pixel( px1, px1 + 4, px1 + basewidth * 4, px1 + basewidth * 4 + 4 ); // 2x2 superpixel is transparent but original pixel was not // preserve one pixel if( dest[x * 2 + 1] == 0x73FE && px1[0] != 255 && dest[x * 2] == 0x73FE && dest[x * 2 - newzoomwidth] == 0x73FE && dest[x * 2 - newzoomwidth - 1] == 0x73FE ) { // preserve one pixel dest[x * 2 + 1] = compress_pixel( px1 ); } // pixel at 2,1 in next 2x2 superpixel dest[x * 2 + 2] = zoomin_pixel( px1 + 4, px1, px1 + basewidth * 4 + 4, px1 + basewidth * 4 ); // pixel at 1,2 in next row 2x2 superpixel dest[x * 2 + newzoomwidth + 1] = zoomin_pixel( px1 + basewidth * 4, px1 + basewidth * 4 + 4, px1, px1 + 4 ); // pixel at 1,1 in next row next 2x2 superpixel dest[x * 2 + newzoomwidth + 2] = zoomin_pixel( px1 + basewidth * 4 + 4, px1 + basewidth * 4, px1 + 4, px1 ); } // copy rightmost pixels dest[2 * orgzoomwidth - 1] = compress_pixel_transparent( p1 + 4 * (orgzoomwidth - 1) ); dest[2 * orgzoomwidth + newzoomwidth - 1] = compress_pixel_transparent( p1 + 4 * (orgzoomwidth - 1) + basewidth * 4 ); // skip two lines dest += 2 * newzoomwidth; } // last half row - just copy values, do not fiddle with neighbor colors p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + (orgzoomheight - 1) * (basewidth * 4); for( sint16 x = 0; x < orgzoomwidth; x++ ) { PIXVAL c1 = compress_pixel_transparent( p1 + (x * 4) ); // now set the pixel ... dest[x * 2] = c1; dest[x * 2 + 1] = c1; } break; } case 2: for( sint16 y = 0; y < newzoomheight; y++ ) { uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 0 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p2 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 1 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); for( sint16 x = 0; x < newzoomwidth; x++ ) { uint8 valid = 0; uint8 r = 0, g = 0, b = 0; sint16 xreal1 = ((x * g_simgraph16.zoom_den[zoom_factor] + 0 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal2 = ((x * g_simgraph16.zoom_den[zoom_factor] + 1 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; SumSubpixel( p1 + xreal1 ); SumSubpixel( p1 + xreal2 ); SumSubpixel( p2 + xreal1 ); SumSubpixel( p2 + xreal2 ); if( valid == 0 ) { *dest++ = 0x73FE; } else if( valid == 255 ) { *dest++ = (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10); } else { *dest++ = (r/valid) + (((uint16)(g/valid))<<5) + (((uint16)(b/valid))<<10); } } } break; case 3: for( sint16 y = 0; y < newzoomheight; y++ ) { uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 0 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p2 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 1 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p3 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 2 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); for( sint16 x = 0; x < newzoomwidth; x++ ) { uint8 valid = 0; uint16 r = 0, g = 0, b = 0; sint16 xreal1 = ((x * g_simgraph16.zoom_den[zoom_factor] + 0 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal2 = ((x * g_simgraph16.zoom_den[zoom_factor] + 1 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal3 = ((x * g_simgraph16.zoom_den[zoom_factor] + 2 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; SumSubpixel( p1 + xreal1 ); SumSubpixel( p1 + xreal2 ); SumSubpixel( p1 + xreal3 ); SumSubpixel( p2 + xreal1 ); SumSubpixel( p2 + xreal2 ); SumSubpixel( p2 + xreal3 ); SumSubpixel( p3 + xreal1 ); SumSubpixel( p3 + xreal2 ); SumSubpixel( p3 + xreal3 ); if( valid == 0 ) { *dest++ = 0x73FE; } else if( valid == 255 ) { *dest++ = (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10); } else { *dest++ = (r/valid) | (((uint16)(g/valid))<<5) | (((uint16)(b/valid))<<10); } } } break; case 4: for( sint16 y = 0; y < newzoomheight; y++ ) { uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 0 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p2 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 1 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p3 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 2 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p4 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 3 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); for( sint16 x = 0; x < newzoomwidth; x++ ) { uint8 valid = 0; uint16 r = 0, g = 0, b = 0; sint16 xreal1 = ((x * g_simgraph16.zoom_den[zoom_factor] + 0 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal2 = ((x * g_simgraph16.zoom_den[zoom_factor] + 1 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal3 = ((x * g_simgraph16.zoom_den[zoom_factor] + 2 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal4 = ((x * g_simgraph16.zoom_den[zoom_factor] + 3 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; SumSubpixel( p1 + xreal1 ); SumSubpixel( p1 + xreal2 ); SumSubpixel( p1 + xreal3 ); SumSubpixel( p1 + xreal4 ); SumSubpixel( p2 + xreal1 ); SumSubpixel( p2 + xreal2 ); SumSubpixel( p2 + xreal3 ); SumSubpixel( p2 + xreal4 ); SumSubpixel( p3 + xreal1 ); SumSubpixel( p3 + xreal2 ); SumSubpixel( p3 + xreal3 ); SumSubpixel( p3 + xreal4 ); SumSubpixel( p4 + xreal1 ); SumSubpixel( p4 + xreal2 ); SumSubpixel( p4 + xreal3 ); SumSubpixel( p4 + xreal4 ); if( valid == 0 ) { *dest++ = 0x73FE; } else if( valid == 255 ) { *dest++ = (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10); } else { *dest++ = (r/valid) | (((uint16)(g/valid))<<5) | (((uint16)(b/valid))<<10); } } } break; case 8: for( sint16 y = 0; y < newzoomheight; y++ ) { uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 0 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p2 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 1 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p3 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 2 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p4 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 3 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p5 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 4 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p6 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 5 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p7 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 6 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); uint8 *p8 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * g_simgraph16.zoom_den[zoom_factor] + 7 - y_rem) / g_simgraph16.zoom_num[zoom_factor]) * (basewidth * 4); for( sint16 x = 0; x < newzoomwidth; x++ ) { uint8 valid = 0; uint16 r = 0, g = 0, b = 0; sint16 xreal1 = ((x * g_simgraph16.zoom_den[zoom_factor] + 0 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal2 = ((x * g_simgraph16.zoom_den[zoom_factor] + 1 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal3 = ((x * g_simgraph16.zoom_den[zoom_factor] + 2 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal4 = ((x * g_simgraph16.zoom_den[zoom_factor] + 3 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal5 = ((x * g_simgraph16.zoom_den[zoom_factor] + 4 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal6 = ((x * g_simgraph16.zoom_den[zoom_factor] + 5 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal7 = ((x * g_simgraph16.zoom_den[zoom_factor] + 6 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; sint16 xreal8 = ((x * g_simgraph16.zoom_den[zoom_factor] + 7 - x_rem) / g_simgraph16.zoom_num[zoom_factor]) * 4; SumSubpixel( p1 + xreal1 ); SumSubpixel( p1 + xreal2 ); SumSubpixel( p1 + xreal3 ); SumSubpixel( p1 + xreal4 ); SumSubpixel( p1 + xreal5 ); SumSubpixel( p1 + xreal6 ); SumSubpixel( p1 + xreal7 ); SumSubpixel( p1 + xreal8 ); SumSubpixel( p2 + xreal1 ); SumSubpixel( p2 + xreal2 ); SumSubpixel( p2 + xreal3 ); SumSubpixel( p2 + xreal4 ); SumSubpixel( p2 + xreal5 ); SumSubpixel( p2 + xreal6 ); SumSubpixel( p2 + xreal7 ); SumSubpixel( p2 + xreal8 ); SumSubpixel( p3 + xreal1 ); SumSubpixel( p3 + xreal2 ); SumSubpixel( p3 + xreal3 ); SumSubpixel( p3 + xreal4 ); SumSubpixel( p3 + xreal5 ); SumSubpixel( p3 + xreal6 ); SumSubpixel( p3 + xreal7 ); SumSubpixel( p3 + xreal8 ); SumSubpixel( p4 + xreal1 ); SumSubpixel( p4 + xreal2 ); SumSubpixel( p4 + xreal3 ); SumSubpixel( p4 + xreal4 ); SumSubpixel( p4 + xreal5 ); SumSubpixel( p4 + xreal6 ); SumSubpixel( p4 + xreal7 ); SumSubpixel( p4 + xreal8 ); SumSubpixel( p5 + xreal1 ); SumSubpixel( p5 + xreal2 ); SumSubpixel( p5 + xreal3 ); SumSubpixel( p5 + xreal4 ); SumSubpixel( p5 + xreal5 ); SumSubpixel( p5 + xreal6 ); SumSubpixel( p5 + xreal7 ); SumSubpixel( p5 + xreal8 ); SumSubpixel( p6 + xreal1 ); SumSubpixel( p6 + xreal2 ); SumSubpixel( p6 + xreal3 ); SumSubpixel( p6 + xreal4 ); SumSubpixel( p6 + xreal5 ); SumSubpixel( p6 + xreal6 ); SumSubpixel( p6 + xreal7 ); SumSubpixel( p6 + xreal8 ); SumSubpixel( p7 + xreal1 ); SumSubpixel( p7 + xreal2 ); SumSubpixel( p7 + xreal3 ); SumSubpixel( p7 + xreal4 ); SumSubpixel( p7 + xreal5 ); SumSubpixel( p7 + xreal6 ); SumSubpixel( p7 + xreal7 ); SumSubpixel( p7 + xreal8 ); SumSubpixel( p8 + xreal1 ); SumSubpixel( p8 + xreal2 ); SumSubpixel( p8 + xreal3 ); SumSubpixel( p8 + xreal4 ); SumSubpixel( p8 + xreal5 ); SumSubpixel( p8 + xreal6 ); SumSubpixel( p8 + xreal7 ); SumSubpixel( p8 + xreal8 ); if( valid == 0 ) { *dest++ = 0x73FE; } else if( valid == 255 ) { *dest++ = (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10); } else { *dest++ = (r/valid) | (((uint16)(g/valid))<<5) | (((uint16)(b/valid))<<10); } } } break; default: assert(0); } // now encode the image again dest = (PIXVAL*)rezoom_baseimage[n % env_t::num_threads]; for( sint16 y = 0; y < newzoomheight; y++ ) { PIXVAL *line = ((PIXVAL *)rezoom_baseimage2[n % env_t::num_threads]) + (y * newzoomwidth); PIXVAL count; sint16 x = 0; uint16 clear_colored_run_pair_count = 0; do { // check length of transparent pixels for( count = 0; x < newzoomwidth && line[x] == 0x73FE; count++, x++ ) {} // first runlength: transparent pixels *dest++ = count; uint16 has_alpha = 0; // copy for non-transparent count = 0; while( x < newzoomwidth && line[x] != 0x73FE ) { PIXVAL pixval = line[x++]; if( pixval >= 0x8020 && !has_alpha ) { if( count ) { *dest++ = count; dest += count; count = 0; *dest++ = TRANSPARENT_RUN; } has_alpha = TRANSPARENT_RUN; } else if( pixval < 0x8020 && has_alpha ) { if( count ) { *dest++ = count+TRANSPARENT_RUN; dest += count; count = 0; *dest++ = TRANSPARENT_RUN; } has_alpha = 0; } count++; dest[count] = pixval; } /* * If it is not the first clear-colored-run pair and its colored run is empty * --> it is superfluous and can be removed by rolling back the pointer */ if( clear_colored_run_pair_count > 0 && count == 0 ) { dest--; // this only happens at the end of a line, so no need to increment clear_colored_run_pair_count } else { *dest++ = count+has_alpha; // number of colored pixels dest += count; // skip them clear_colored_run_pair_count++; } } while( x < newzoomwidth ); *dest++ = 0; // mark line end } // something left? images[n].w = newzoomwidth; images[n].h = newzoomheight; if( newzoomheight > 0 ) { const size_t zoom_len = (size_t)(((uint8 *)dest) - ((uint8 *)rezoom_baseimage[n % env_t::num_threads])); images[n].len = (uint32)(zoom_len / sizeof(PIXVAL)); images[n].zoom_data = MALLOCN(PIXVAL, images[n].len); assert( images[n].zoom_data ); memcpy( images[n].zoom_data, rezoom_baseimage[n % env_t::num_threads], zoom_len ); } } else { // if (images[n].w <= 0) { // // h=0 will be ignored, with w=0 there was an error! // printf("WARNING: image%d w=0!\n", n); // } images[n].h = 0; } images[n].recode_flags &= ~FLAG_REZOOM; #ifdef MULTI_THREAD pthread_mutex_unlock( &rezoom_img_mutex[n % env_t::num_threads] ); #endif } } // get next smallest size when scaling to percent static scr_size simgraph16_get_best_matching_size(const image_id n, sint16 zoom_percent) { if (n < anz_images && images[n].base_h > 0) { int new_w = (images[n].base_w * zoom_percent + 1) / 100; for (int i = 0; i <= MAX_ZOOM_FACTOR; i++) { int zoom_w = (images[n].base_w * g_simgraph16.zoom_num[i]) / g_simgraph16.zoom_den[i]; int zoom_h = (images[n].base_h * g_simgraph16.zoom_num[i]) / g_simgraph16.zoom_den[i]; if (zoom_w <= new_w) { // first size smaller or equal to requested return scr_size(zoom_w, zoom_h); } } } return scr_size(32, 32); // default size } // force a certain size on a image (for rescaling tool images) static void simgraph16_fit_img_to_width(const image_id n, sint16 new_w) { if( n < anz_images && images[n].base_h > 0 && images[n].w != new_w ) { int old_zoom_factor = zoom_factor; for( int i=0; i<=MAX_ZOOM_FACTOR; i++ ) { int zoom_w = (images[n].base_w * g_simgraph16.zoom_num[i]) / g_simgraph16.zoom_den[i]; if( zoom_w <= new_w ) { uint8 old_zoom_flag = images[n].recode_flags & FLAG_ZOOMABLE; images[n].recode_flags |= FLAG_REZOOM | FLAG_ZOOMABLE; zoom_factor = i; rezoom_img(n); images[n].recode_flags &= ~FLAG_ZOOMABLE; images[n].recode_flags |= old_zoom_flag; zoom_factor = old_zoom_factor; return; } } } } static void calc_base_pal_from_night_shift(const int night) { const int night2 = min(night, 4); const int day = 4 - night2; unsigned int i; // constant multiplier 0,66 - dark night 255 will drop to 49, 55 to 10 // 0,7 - dark, but all is visible 61 13 // 0,73 72 15 // 0,75 - quite bright 80 17 // 0,8 bright 104 22 const double RG_night_multiplier = pow(0.75, night) * ((light_level + 8.0) / 8.0); const double B_night_multiplier = pow(0.83, night) * ((light_level + 8.0) / 8.0); for (i = 0; i < 0x8000; i++) { // (1<<15) this is total no of all possible colors in RGB555) // RGB 555 input int R = (i & 0x7C00) >> 7; int G = (i & 0x03E0) >> 2; int B = (i & 0x001F) << 3; // lines generate all possible colors in 555RGB code - input // however the result is in 888RGB - 8bit per channel R = (int)(R * RG_night_multiplier); G = (int)(G * RG_night_multiplier); B = (int)(B * B_night_multiplier); rgbmap_day_night[i] = get_system_color({ (uint8)R, (uint8)G, (uint8)B }); } // again the same but for transparent colors for (i = 0; i < 0x0400; i++) { // RGB 343 input int R = (i & 0x0380) >> 2; int G = (i & 0x0078) << 1; int B = (i & 0x0007) << 5; // lines generate all possible colors in 343RGB code - input // however the result is in 888RGB - 8bit per channel R = (int)(R * RG_night_multiplier); G = (int)(G * RG_night_multiplier); B = (int)(B * B_night_multiplier); PIXVAL color = get_system_color({ (uint8)R, (uint8)G, (uint8)B }); rgbmap_day_night[0x8000 +MAX_PLAYER_COUNT + LIGHT_COUNT + i] = color; } // player color map (and used for map display etc.) for (i = 0; i < SPECIAL_COLOR_COUNT; i++) { const int R = (int)(special_pal[i].r * RG_night_multiplier); const int G = (int)(special_pal[i].g * RG_night_multiplier); const int B = (int)(special_pal[i].b * B_night_multiplier); specialcolormap_day_night[i] = get_system_color({ (uint8)R, (uint8)G, (uint8)B }); } // special light colors (actually, only non-darkening greys should be used) for(i=0; i> 2; const int G = (day_G * day + night_G * night2) >> 2; const int B = (day_B * day + night_B * night2) >> 2; PIXVAL color = get_system_color({ (uint8)max(R,0), (uint8)max(G,0), (uint8)max(B,0) }); rgbmap_day_night[0x8000 + MAX_PLAYER_COUNT + i] = color; } // convert to RGB xxx recode(); } static void simgraph16_set_daynight_level(int night) { if( night != night_shift ) { night_shift = night; calc_base_pal_from_night_shift(night); simgraph16_mark_screen_dirty(); } } // set first and second company color for player static void simgraph16_set_player_color_scheme(const int player, const uint8 col1, const uint8 col2) { if(player_offsets[player][0]!=col1 || player_offsets[player][1]!=col2) { // set new player colors player_offsets[player][0] = col1; player_offsets[player][1] = col2; if(player==player_day || player==player_night) { // and recalculate map (and save it) calc_base_pal_from_night_shift(0); memcpy(rgbmap_all_day, rgbmap_day_night, RGBMAPSIZE * sizeof(PIXVAL)); if(night_shift!=0) { calc_base_pal_from_night_shift(night_shift); } // calc_base_pal_from_night_shift resets player_night to 0 player_day = player_night; } recode(); simgraph16_mark_screen_dirty(); } } static image_id simgraph16_register_image(const image_t *image_in) { struct imd *image; /* valid image? */ if( image_in->len == 0 || image_in->h == 0 ) { dbg->warning("register_image()", "Ignoring image %d because of missing data", anz_images); return IMG_EMPTY; } if( anz_images == alloc_images ) { if( images==NULL ) { alloc_images = 510; } else { alloc_images += 512; } if( anz_images > alloc_images ) { // overflow dbg->fatal( "register_image", "*** Out of images (more than %li!) ***", anz_images ); } images = REALLOC(images, imd, alloc_images); } const image_id id = anz_images; image = &images[anz_images]; anz_images++; image->x = image_in->x; image->w = image_in->w; image->y = image_in->y; image->h = image_in->h; image->recode_flags = FLAG_REZOOM; if( image_in->zoomable ) { image->recode_flags |= FLAG_ZOOMABLE; } image->player_flags = 0xFFFF; // recode all player colors // find out if there are really player colors for( PIXVAL *src = image_in->data, y = 0; y < image_in->h; ++y ) { uint16 runlen; // decode line runlen = *src++; do { // clear run .. nothing to do runlen = *src++; if( runlen & TRANSPARENT_RUN ) { image->recode_flags |= FLAG_HAS_TRANSPARENT_COLOR; runlen &= ~TRANSPARENT_RUN; } // no this many color pixel while( runlen-- ) { // get rgb components PIXVAL s = *src++; if( s>=0x8000 && s<0x8010 ) { image->recode_flags |= FLAG_HAS_PLAYER_COLOR; } } runlen = *src++; } while( runlen!=0 ); // end of row: runlen == 0 } for( uint8 i = 0; i < MAX_PLAYER_COUNT; i++ ) { image->data[i] = NULL; } image->zoom_data = NULL; image->len = image_in->len; image->base_x = image_in->x; image->base_w = image_in->w; image->base_y = image_in->y; image->base_h = image_in->h; // since we do not recode them, we can work with the original data image->base_data = image_in->data; return id; } // delete all images above a certain number ... // (mostly needed when changing climate zones) static void simgraph16_free_all_images_above( image_id above ) { while( above < anz_images ) { anz_images--; if( images[anz_images].zoom_data != NULL ) { free( images[anz_images].zoom_data ); } for( uint8 i = 0; i < MAX_PLAYER_COUNT; i++ ) { if( images[anz_images].data[i] != NULL ) { free( images[anz_images].data[i] ); } } } } // query offsets static scr_rect simgraph16_get_image_offset(image_id image) { if( image < anz_images ) { return scr_rect{ images[image].x, images[image].y, images[image].w, images[image].h }; } else { return scr_rect{ 0, 0, 0, 0 }; } } // query un-zoomed offsets static scr_rect simgraph16_get_base_image_offset(image_id image) { if( image < anz_images ) { return scr_rect{ images[image].base_x, images[image].base_y, images[image].base_w, images[image].base_h }; } else { return scr_rect{ 0, 0, 0, 0 }; } } // ------------------ display all kind of images from here on ------------------------------ // forward declaration, implementation is further below PIXVAL colors_blend_alpha32(PIXVAL background, PIXVAL foreground, int alpha); /** * Copy Pixel from src to dest */ static inline void pixcopy(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const end) { // for gcc this seems to produce the optimal code ... while (src < end) { *dest++ = *src++; } } /** * Copy pixel, replace player color */ static inline void colorpixcopy(PIXVAL* dest, const PIXVAL* src, const PIXVAL* const end) { if (*src < 0x8020) { while (src < end) { *dest++ = rgbmap_current[*src++]; } } else { while (src < end) { // a semi-transparent pixel uint16 aux = *src++ - 0x8020; uint16 alpha = (aux % 31) + 1; *dest = colors_blend_alpha32(*dest, rgbmap_day_night[0x8000 + aux / 31], alpha); dest++; } } } /** * Copy pixel, replace player color */ static inline void colorpixcopydaytime(PIXVAL* dest, const PIXVAL* src, const PIXVAL* const end) { if (*src < 0x8020) { while (src < end) { *dest++ = rgbmap_current[*src++]; } } else { while (src < end) { // a semi-transparent pixel uint16 aux = *src++ - 0x8020; uint16 alpha = (aux % 31) + 1; *dest = colors_blend_alpha32(*dest, rgbmap_all_day[0x8000 + aux / 31], alpha); dest++; } } } /** * templated pixel copy routines * to be used in display_img_pc */ enum pixcopy_routines { plain = 0, /// simply copies the pixels colored = 1, /// replaces player colors daytime = 2 /// use daytime color lookup for transparent pixels }; template void templated_pixcopy(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const end); template<> void templated_pixcopy(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const end) { pixcopy(dest, src, end); } template<> void templated_pixcopy(PIXVAL* dest, const PIXVAL* src, const PIXVAL* const end) { colorpixcopy(dest, src, end); } template<> void templated_pixcopy(PIXVAL* dest, const PIXVAL* src, const PIXVAL* const end) { colorpixcopydaytime(dest, src, end); } /** * draws image with clipping along arbitrary lines */ template static void display_img_pc(scr_coord_val h, const scr_coord_val xp, const scr_coord_val yp, const PIXVAL *sp CLIP_NUM_DEF) { if( h > 0 ) { PIXVAL *tp = textur + yp * disp_width; // initialize clipping init_ranges( yp CLIP_NUM_PAR); do { // line decoder int xpos = xp; // display image int runlen = *sp++; // get left/right boundary, step int xmin, xmax; get_xrange_and_step_y( xmin, xmax CLIP_NUM_PAR ); do { // we start with a clear run (which may be 0 pixels) xpos += (runlen & ~TRANSPARENT_RUN); // now get colored pixels runlen = *sp++; uint16 has_alpha = runlen & TRANSPARENT_RUN; runlen &= ~TRANSPARENT_RUN; // something to display? if (xmin < xmax && xpos + runlen > xmin && xpos < xmax) { const int left = (xpos >= xmin ? 0 : xmin - xpos); const int len = (xmax - xpos >= runlen ? runlen : xmax - xpos); if( !has_alpha ) { templated_pixcopy(tp + xpos + left, sp + left, sp + len); } else { colorpixcopy(tp + xpos + left, sp + left, sp + len); } } sp += runlen; xpos += runlen; } while ((runlen = *sp++)); tp += disp_width; } while (--h); } } /** * Draw image with horizontal clipping */ static void display_img_wc(scr_coord_val h, const scr_coord_val xp, const scr_coord_val yp, const PIXVAL *sp CLIP_NUM_DEF) { if( h > 0 ) { PIXVAL *tp = textur + yp * disp_width; do { // line decoder int xpos = xp; // display image uint16 runlen = *sp++; do { // we start with a clear run xpos += (runlen & ~TRANSPARENT_RUN); // now get colored pixels runlen = *sp++; uint16 has_alpha = runlen & TRANSPARENT_RUN; runlen &= ~TRANSPARENT_RUN; // something to display? if( xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx ) { const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos); const int len = (CR.clip_rect.xx - xpos >= runlen ? runlen : CR.clip_rect.xx - xpos); if( !has_alpha ) { pixcopy(tp + xpos + left, sp + left, sp + len); } else { colorpixcopy(tp + xpos + left, sp + left, sp + len); } } sp += runlen; xpos += runlen; } while ((runlen = *sp++)); tp += disp_width; } while (--h); } } /** * Draw each image without clipping */ static void display_img_nc(scr_coord_val h, const scr_coord_val xp, const scr_coord_val yp, const PIXVAL *sp) { if (h > 0) { PIXVAL *tp = textur + xp + yp * disp_width; do { // line decoder uint16 runlen = *sp++; PIXVAL *p = tp; // one line decoder do { // we start with a clear run p += (runlen & ~TRANSPARENT_RUN); // now get colored pixels runlen = *sp++; if( runlen & TRANSPARENT_RUN ) { runlen &= ~TRANSPARENT_RUN; colorpixcopy( p, sp, sp+runlen ); p += runlen; sp += runlen; } else { #ifdef LOW_LEVEL #ifdef SIM_BIG_ENDIAN // low level c++ without any unrolling while( runlen-- ) { *p++ = *sp++; } #else // trying to merge reads and writes if( runlen ) { // align to 4 bytes, should use uintptr_t but not available if( reinterpret_cast(p) & 0x2 ) { *p++ = *sp++; runlen--; } // aligned fast copy loop bool const postalign = runlen & 1; runlen >>= 1; uint32 *ld = (uint32 *)p; while (runlen--) { #if defined _MSC_VER // MSVC can read unaligned *ld++ = *(uint32 const *const)sp; #else // little endian order, assumed by default *ld++ = (uint32(sp[1]) << 16) | uint32(sp[0]); #endif sp += 2; } p = (PIXVAL*)ld; // finish unaligned remainder if( postalign ) { *p++ = *sp++; } } #endif #else // high level c++ const PIXVAL *const splast = sp + runlen; p = std::copy(sp, splast, p); sp = splast; #endif } runlen = *sp++; } while (runlen != 0); tp += disp_width; } while (--h > 0); } } // only used for GUI static void simgraph16_draw_img_aligned( const image_id n, scr_rect area, int align, const bool dirty) { if( n < anz_images ) { scr_coord_val x,y; // align the image horizontally x = area.x; if( (align & ALIGN_RIGHT) == ALIGN_CENTER_H ) { x -= images[n].x; x += (area.w-images[n].w)/2; } else if( (align & ALIGN_RIGHT) == ALIGN_RIGHT ) { x = area.get_right() - images[n].x - images[n].w; } // align the image vertically y = area.y; if( (align & ALIGN_BOTTOM) == ALIGN_CENTER_V ) { y -= images[n].y; y += (area.h-images[n].h)/2; } else if( (align & ALIGN_BOTTOM) == ALIGN_BOTTOM ) { y = area.get_bottom() - images[n].y - images[n].h; } gfx->draw_color_img( n, x, y, 0, false, dirty CLIP_NUM_DEFAULT); } } /** * Draw image with vertical clipping (quickly) and horizontal (slowly) */ static void simgraph16_draw_img_aux(const image_id n, scr_coord_val xp, scr_coord_val yp, const sint8 player_nr_raw, const bool /*daynight*/, const bool dirty CLIP_NUM_DEF) { if( n < anz_images ) { // only use player images if needed const sint8 use_player = (images[n].recode_flags & FLAG_HAS_PLAYER_COLOR) * player_nr_raw; // need to go to nightmode and or re-zoomed? PIXVAL *sp; if( use_player > 0 ) { // player colour images are rezoomed/recoloured in display_color_img sp = images[n].data[use_player]; if( sp == NULL ) { dbg->warning("display_img_aux", "CImg[%i] %u failed!", use_player, n); return; } } else { if( (images[n].recode_flags & FLAG_REZOOM) ) { rezoom_img( n ); recode_img( n, 0 ); } else if( (images[n].player_flags & 1) ) { recode_img( n, 0 ); } sp = images[n].data[0]; if( sp == NULL ) { dbg->warning("display_img_aux", "Img %u failed!", n); return; } } // now, since zooming may have change this image yp += images[n].y; scr_coord_val h = images[n].h; // may change due to vertical clipping // in the next line the vertical clipping will be handled // by that way the drawing routines must only take into account the horizontal clipping // this should be much faster in most cases // must the height be reduced? scr_coord_val reduce_h = yp + h - CR.clip_rect.yy; if( reduce_h > 0 ) { h -= reduce_h; } // still something to draw if( h <= 0 ) { return; } // vertically lines to skip (only bottom is visible scr_coord_val skip_lines = CR.clip_rect.y - (int)yp; if( skip_lines > 0 ) { if( skip_lines >= h ) { // not visible at all return; } h -= skip_lines; yp += skip_lines; // now skip them while (skip_lines--) { do { // clear run + colored run + next clear run sp++; sp += (*sp) & (~TRANSPARENT_RUN); sp ++; } while (*sp); sp++; } // now sp is the new start of an image with height h } // new block for new variables { // needed now ... const scr_coord_val w = images[n].w; xp += images[n].x; // clipping at poly lines? if( CR.number_of_clips > 0 ) { display_img_pc( h, xp, yp, sp CLIP_NUM_PAR ); // since height may be reduced, start marking here if( dirty ) { simgraph16_mark_rect_dirty_clip( xp, yp, xp + w - 1, yp + h - 1 CLIP_NUM_PAR ); } } else { // use horizontal clipping or skip it? if( xp >= CR.clip_rect.x && xp + w <= CR.clip_rect.xx ) { // marking change? if( dirty ) { mark_rect_dirty_nc( xp, yp, xp + w - 1, yp + h - 1 ); } display_img_nc( h, xp, yp, sp ); } else if( xp < CR.clip_rect.xx && xp + w > CR.clip_rect.x ) { display_img_wc( h, xp, yp, sp CLIP_NUM_PAR); // since height may be reduced, start marking here if( dirty ) { simgraph16_mark_rect_dirty_clip( xp, yp, xp + w - 1, yp + h - 1 CLIP_NUM_PAR ); } } } } } } // local helper function for tiles buttons static void display_three_image_row( image_id i1, image_id i2, image_id i3, scr_rect row, FLAGGED_PIXVAL) { if( i1!=IMG_EMPTY ) { scr_coord_val w = images[i1].w; gfx->draw_color_img( i1, row.x, row.y, 0, false, true CLIP_NUM_DEFAULT); row.x += w; row.w -= w; } // right if( i3!=IMG_EMPTY ) { scr_coord_val w = images[i3].w; gfx->draw_color_img( i3, row.get_right()-w, row.y, 0, false, true CLIP_NUM_DEFAULT); row.w -= w; } // middle if( i2!=IMG_EMPTY ) { scr_coord_val w = images[i2].w; // tile it wide while( w <= row.w ) { gfx->draw_color_img( i2, row.x, row.y, 0, false, true CLIP_NUM_DEFAULT); row.x += w; row.w -= w; } // for the rest we have to clip the rectangle if( row.w > 0 ) { clip_dimension const cl = simgraph16_get_clip_rect(CLIP_NUM_DEFAULT_VALUE); simgraph16_set_clip_rect( cl.x, cl.y, max(0,min(row.get_right(),cl.xx)-cl.x), cl.h CLIP_NUM_DEFAULT, false); gfx->draw_color_img( i2, row.x, row.y, 0, false, true CLIP_NUM_DEFAULT); simgraph16_set_clip_rect(cl.x, cl.y, cl.w, cl.h CLIP_NUM_DEFAULT, false); } } } static scr_coord_val get_img_width(image_id img) { return img != IMG_EMPTY ? images[ img ].w : 0; } static scr_coord_val get_img_height(image_id img) { return img != IMG_EMPTY ? images[ img ].h : 0; } typedef void (*DISP_THREE_ROW_FUNC)(image_id, image_id, image_id, scr_rect, FLAGGED_PIXVAL); /** * Base method to display a 3x3 array of images to fit the scr_rect. * Special cases: * - if images[*][1] are empty, display images[*][0] vertically aligned * - if images[1][*] are empty, display images[0][*] horizontally aligned */ static void display_img_stretch_intern( const stretch_map_t &imag, scr_rect area, DISP_THREE_ROW_FUNC display_three_image_rowf, FLAGGED_PIXVAL color) { scr_coord_val h_top = max(max( get_img_height(imag[0][0]), get_img_height(imag[1][0])), get_img_height(imag[2][0])); scr_coord_val h_middle = max(max( get_img_height(imag[0][1]), get_img_height(imag[1][1])), get_img_height(imag[2][1])); scr_coord_val h_bottom = max(max( get_img_height(imag[0][2]), get_img_height(imag[1][2])), get_img_height(imag[2][2])); // center vertically if images[*][1] are empty, display images[*][0] if( imag[0][1] == IMG_EMPTY && imag[1][1] == IMG_EMPTY && imag[2][1] == IMG_EMPTY ) { scr_coord_val h = max(h_top, get_img_height(imag[1][1])); // center vertically area.y += (area.h-h)/2; } // center horizontally if images[1][*] are empty, display images[0][*] if( imag[1][0] == IMG_EMPTY && imag[1][1] == IMG_EMPTY && imag[1][2] == IMG_EMPTY ) { scr_coord_val w_left = max(max( get_img_width(imag[0][0]), get_img_width(imag[0][1])), get_img_width(imag[0][2])); // center vertically area.x += (area.w-w_left)/2; } // top row display_three_image_rowf( imag[0][0], imag[1][0], imag[2][0], area, color); // bottom row if( h_bottom > 0 ) { scr_rect row( area.x, area.y+area.h-h_bottom, area.w, h_bottom ); display_three_image_rowf( imag[0][2], imag[1][2], imag[2][2], row, color); } // now stretch the middle if( h_middle > 0 ) { scr_rect row( area.x, area.y+h_top, area.w, area.h-h_top-h_bottom); // tile it wide while( h_middle <= row.h ) { display_three_image_rowf( imag[0][1], imag[1][1], imag[2][1], row, color); row.y += h_middle; row.h -= h_middle; } // for the rest we have to clip the rectangle if( row.h > 0 ) { clip_dimension const cl = simgraph16_get_clip_rect(CLIP_NUM_DEFAULT_VALUE); simgraph16_set_clip_rect( cl.x, cl.y, cl.w, max(0,min(row.get_bottom(),cl.yy)-cl.y) CLIP_NUM_DEFAULT, false); display_three_image_rowf( imag[0][1], imag[1][1], imag[2][1], row, color); simgraph16_set_clip_rect(cl.x, cl.y, cl.w, cl.h CLIP_NUM_DEFAULT, false); } } } static void simgraph16_draw_stretch_map(const stretch_map_t &imag, scr_rect area) { display_img_stretch_intern(imag, area, display_three_image_row, 0); } static void display_three_blend_row( image_id i1, image_id i2, image_id i3, scr_rect row, FLAGGED_PIXVAL color ) { if( i1!=IMG_EMPTY ) { scr_coord_val w = images[i1].w; gfx->draw_rezoomed_img_blend( i1, row.x, row.y, 0, color, false, true CLIPNUM_IGNORE ); row.x += w; row.w -= w; } // right if( i3!=IMG_EMPTY ) { scr_coord_val w = images[i3].w; gfx->draw_rezoomed_img_blend( i3, row.get_right()-w, row.y, 0, color, false, true CLIPNUM_IGNORE ); row.w -= w; } // middle if( i2!=IMG_EMPTY ) { scr_coord_val w = images[i2].w; // tile it wide while( w <= row.w ) { gfx->draw_rezoomed_img_blend( i2, row.x, row.y, 0, color, false, true CLIPNUM_IGNORE ); row.x += w; row.w -= w; } // for the rest we have to clip the rectangle if( row.w > 0 ) { clip_dimension const cl = simgraph16_get_clip_rect(CLIP_NUM_DEFAULT_VALUE); simgraph16_set_clip_rect( cl.x, cl.y, max(0,min(row.get_right(),cl.xx)-cl.x), cl.h CLIP_NUM_DEFAULT, false); gfx->draw_rezoomed_img_blend( i2, row.x, row.y, 0, color, false, true CLIPNUM_IGNORE ); simgraph16_set_clip_rect(cl.x, cl.y, cl.w, cl.h CLIP_NUM_DEFAULT, false); } } } // this displays a 3x3 array of images to fit the scr_rect like above, but blend the color static void simgraph16_draw_stretch_map_blend(const stretch_map_t &imag, scr_rect area, FLAGGED_PIXVAL color) { display_img_stretch_intern(imag, area, display_three_blend_row, color); } /** * Draw Image, replace player color, * assumes height is ok and valid data are calculated. * color replacement needs the original data => sp points to non-cached data */ static void display_color_img_wc(const PIXVAL* sp, scr_coord_val x, scr_coord_val y, scr_coord_val h CLIP_NUM_DEF) { PIXVAL* tp = textur + y * disp_width; do { // line decoder int xpos = x; // Display image uint16 runlen = *sp++; do { // we start with a clear run xpos += (runlen & ~TRANSPARENT_RUN); // now get colored pixels runlen = (*sp++) & ~TRANSPARENT_RUN; // we recode anyway, so no need to do it explicitely // something to display? if (xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx) { const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos); const int len = (CR.clip_rect.xx - xpos > runlen ? runlen : CR.clip_rect.xx - xpos); colorpixcopy(tp + xpos + left, sp + left, sp + len); } sp += runlen; xpos += runlen; } while ((runlen = *sp++)); tp += disp_width; } while (--h); } /** * Draw Image, replace player color, as above, but uses daytime colors for transparent pixels */ static void display_color_img_wc_daytime(const PIXVAL* sp, scr_coord_val x, scr_coord_val y, scr_coord_val h CLIP_NUM_DEF) { PIXVAL* tp = textur + y * disp_width; do { // line decoder int xpos = x; // Display image uint16 runlen = *sp++; do { // we start with a clear run xpos += (runlen & ~TRANSPARENT_RUN); // now get colored pixels runlen = (*sp++) & ~TRANSPARENT_RUN; // we recode anyway, so no need to do it explicitely // something to display? if (xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx) { const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos); const int len = (CR.clip_rect.xx - xpos > runlen ? runlen : CR.clip_rect.xx - xpos); colorpixcopydaytime(tp + xpos + left, sp + left, sp + len); } sp += runlen; xpos += runlen; } while ((runlen = *sp++)); tp += disp_width; } while (--h); } /** * Draw Image, replaced player color */ void simgraph16_draw_color_img(const image_id n, scr_coord_val xp, scr_coord_val yp, sint8 player_nr_raw, const bool daynight, const bool dirty CLIP_NUM_DEF) { if( n < anz_images ) { // do we have to use a player nr? const sint8 player_nr = (images[n].recode_flags & FLAG_HAS_PLAYER_COLOR) * player_nr_raw; // first: size check if( (images[n].recode_flags & FLAG_REZOOM) ) { rezoom_img( n ); } if( daynight || night_shift == 0 ) { // ok, now we could use the same faster code as for the normal images if( (images[n].player_flags & (1<= CR.clip_rect.xx || y >= CR.clip_rect.yy || x + w <= CR.clip_rect.x || y + h <= CR.clip_rect.y ) { // not visible => we are done // happens quite often ... return; } if( dirty ) { simgraph16_mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 ); } activate_player_color( player_nr, daynight ); // color replacement needs the original data => sp points to non-cached data const PIXVAL *sp = images[n].zoom_data != NULL ? images[n].zoom_data : images[n].base_data; // clip top/bottom scr_coord_val yoff = clip_wh( &y, &h, CR.clip_rect.y, CR.clip_rect.yy ); if( h > 0 ) { // clipping may have reduced it // clip top while( yoff ) { yoff--; do { // clear run + colored run + next clear run ++sp; sp += (*sp) & (~TRANSPARENT_RUN); sp++; } while (*sp); sp++; } // clipping at poly lines? if( CR.number_of_clips > 0 ) { daynight ? display_img_pc(h, x, y, sp CLIP_NUM_PAR) : display_img_pc(h, x, y, sp CLIP_NUM_PAR); } else { daynight ? display_color_img_wc(sp, x, y, h CLIP_NUM_PAR) : display_color_img_wc_daytime(sp, x, y, h CLIP_NUM_PAR); } } } } // number ok } /** * draw unscaled images, replaces base color */ static void simgraph16_draw_base_img(const image_id n, scr_coord_val xp, scr_coord_val yp, const sint8 player_nr, const bool daynight, const bool dirty CLIP_NUM_DEF) { if( g_simgraph16.tile_raster_width == g_simgraph16.base_tile_raster_width ) { // same size => use standard routine simgraph16_draw_color_img( n, xp, yp, player_nr, daynight, dirty CLIP_NUM_PAR); } else if( n < anz_images ) { // now test if visible and clipping needed const scr_coord_val x = images[n].base_x + xp; scr_coord_val y = images[n].base_y + yp; const scr_coord_val w = images[n].base_w; scr_coord_val h = images[n].base_h; if( h <= 0 || x >= CR.clip_rect.xx || y >= CR.clip_rect.yy || x + w <= CR.clip_rect.x || y + h <= CR.clip_rect.y ) { // not visible => we are done // happens quite often ... return; } if (dirty) { simgraph16_mark_rect_dirty_wc(x, y, x + w - 1, y + h - 1); } // colors for 2nd company color if(player_nr>=0) { activate_player_color( player_nr, daynight ); } else { // no player activate_player_color( 0, daynight ); } // color replacement needs the original data => sp points to non-cached data const PIXVAL *sp = images[n].base_data; // clip top/bottom scr_coord_val yoff = clip_wh( &y, &h, CR.clip_rect.y, CR.clip_rect.yy ); if( h > 0 ) { // clipping may have reduced it // clip top while( yoff ) { yoff--; do { // clear run + colored run + next clear run sp++; sp += (*sp) & (~TRANSPARENT_RUN); sp++; } while (*sp); sp++; } // clipping at poly lines? if( CR.number_of_clips > 0 ) { daynight ? display_img_pc(h, x, y, sp CLIP_NUM_PAR) : display_img_pc(h, x, y, sp CLIP_NUM_PAR); } else { daynight ? display_color_img_wc(sp, x, y, h CLIP_NUM_PAR) : display_color_img_wc_daytime(sp, x, y, h CLIP_NUM_PAR); } } } // number ok } inline PIXVAL colors_blend25(PIXVAL background, PIXVAL foreground) { return rgb_shr1(background) + rgb_shr2(background) + rgb_shr2(foreground); } inline PIXVAL colors_blend50(PIXVAL background, PIXVAL foreground) { return rgb_shr1(background) + rgb_shr1(foreground); } inline PIXVAL colors_blend75(PIXVAL background, PIXVAL foreground) { return rgb_shr2(background) + rgb_shr1(foreground) + rgb_shr2(foreground); } inline PIXVAL colors_blend_alpha32(PIXVAL background, PIXVAL foreground, int alpha) { uint32 b = ((background << 16) | background) & MASK_32; uint32 f = ((foreground << 16) | foreground) & MASK_32; uint32 r = ((f * alpha + (32-alpha) * b) >> 5) & MASK_32; return r | (r >> 16); } // Blends two colors. Possible values for alpha: 0..32 PIXVAL display_blend_colors_alpha32(PIXVAL background, PIXVAL foreground, int alpha) { // alpha takes values 0 .. 32 switch(alpha) { case 0: // nothing to do ... return background; case 8: return colors_blend25(background, foreground); case 16: return colors_blend50(background, foreground); case 24: return colors_blend75(background, foreground); case 32: return foreground; default: return colors_blend_alpha32(background, foreground, alpha); } } // Blends two colors static PIXVAL simgraph16_blend_colors(PIXVAL background, PIXVAL foreground, int percent_blend) { return display_blend_colors_alpha32(background, foreground, (percent_blend*32)/100); } /* from here code for transparent images */ typedef void (*blend_proc)(PIXVAL *dest, const PIXVAL *src, const PIXVAL colour, const PIXVAL len); // templated structures to specialize for the different blend modes: 25/50/75 percent struct blend25_t { static inline PIXVAL blend(PIXVAL background, PIXVAL foreground) { return 3 * rgb_shr2(background) + rgb_shr2(foreground); } }; struct blend50_t { static inline PIXVAL blend(PIXVAL background, PIXVAL foreground) { return rgb_shr1(background) + rgb_shr1(foreground); } }; struct blend75_t { static inline PIXVAL blend(PIXVAL background, PIXVAL foreground) { return rgb_shr2(background) + 3 * rgb_shr2(foreground); } }; template void pix_blend_tpl(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) { const PIXVAL *const end = dest + len; while (dest < end) { *dest = F::blend(*dest, *src); dest++; src++; } } // the following 6 functions are for display_base_img_blend() template void pix_blend_recode_tpl(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) { const PIXVAL *const end = dest + len; while (dest < end) { *dest = F::blend(*dest, rgbmap_current[*src]); dest++; src++; } } template void pix_outline_tpl(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len) { const PIXVAL *const end = dest + len; while (dest < end) { *dest = F::blend(*dest, colour); dest++; } } // save them for easier access static blend_proc blend[3] = { pix_blend_tpl, pix_blend_tpl, pix_blend_tpl }; static blend_proc blend_recode[3] = { pix_blend_recode_tpl, pix_blend_recode_tpl, pix_blend_recode_tpl}; static blend_proc outline[3] = { pix_outline_tpl, pix_outline_tpl, pix_outline_tpl}; /** * Blends a rectangular region with a color */ static void simgraph16_tint_rect(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL colval, int percent_blend) { if( clip_lr( &xp, &w, CR0.clip_rect.x, CR0.clip_rect.xx ) && clip_lr( &yp, &h, CR0.clip_rect.y, CR0.clip_rect.yy ) ) { const PIXVAL alpha = (percent_blend*64)/100; switch( alpha ) { case 0: // nothing to do break; case 16: case 32: case 48: { // fast blending with 1/4 | 1/2 | 3/4 percentage blend_proc blend = outline[ (alpha>>4) - 1 ]; for( scr_coord_val y=0; y0; yp++, h-- ) { PIXVAL *dest = textur + yp*disp_width + xp; const PIXVAL *const end = dest + w; while (dest < end) { const PIXVAL r_dest = red(*dest); const PIXVAL g_dest = green(*dest); const PIXVAL b_dest = blue(*dest); const PIXVAL r = r_dest + ( ( (r_src - r_dest) * alpha ) >> 6 ); const PIXVAL g = g_dest + ( ( (g_src - g_dest) * alpha ) >> 6 ); const PIXVAL b = b_dest + ( ( (b_src - b_dest) * alpha ) >> 6 ); *dest++ = rgb(r, g, b); } } } break; } } } static void display_img_blend_wc(scr_coord_val h, const scr_coord_val xp, const scr_coord_val yp, const PIXVAL *sp, int colour, blend_proc p CLIP_NUM_DEF ) { if( h > 0 ) { PIXVAL *tp = textur + yp * disp_width; do { // line decoder int xpos = xp; // display image uint16 runlen = *sp++; do { // we start with a clear run xpos += (runlen & ~TRANSPARENT_RUN); // now get colored pixels runlen = (*sp++) & (~TRANSPARENT_RUN); // something to display? if( xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx ) { const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos); const int len = (CR.clip_rect.xx - xpos >= runlen ? runlen : CR.clip_rect.xx - xpos); p(tp + xpos + left, sp + left, colour, len - left); } sp += runlen; xpos += runlen; } while ((runlen = *sp++)); tp += disp_width; } while (--h); } } /* from here code for transparent images */ static PIXVAL get_alpha_mask(const unsigned alpha_flags) { PIXVAL mask = 0; if (alpha_flags & ALPHA_RED) mask |= 0x7c00; if (alpha_flags & ALPHA_GREEN) mask |= 0x03e0; if (alpha_flags & ALPHA_BLUE) mask |= 0x001f; return mask; } typedef void (*alpha_proc)(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const PIXVAL alpha_mask, const PIXVAL colour, const PIXVAL len); static void alpha(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const PIXVAL alpha_mask, const PIXVAL , const PIXVAL len) { const PIXVAL *const end = dest + len; while( dest < end ) { // read mask components - always 15bpp uint16 masked = *alphamap & alpha_mask; uint16 alpha_value = (masked & 0x1f) + ((masked >> 5) & 0x1f) + ((masked >> 10) & 0x1f); if( alpha_value > 30 ) { // opaque, just copy source *dest = *src; } else if( alpha_value > 0 ) { alpha_value = alpha_value > 15 ? alpha_value + 1 : alpha_value; *dest = colors_blend_alpha32(*dest, *src, alpha_value); } dest++; src++; alphamap++; } } static void alpha_recode(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const PIXVAL alpha_mask, const PIXVAL , const PIXVAL len) { const PIXVAL *const end = dest + len; while( dest < end ) { // read mask components - always 15bpp uint16 masked = *alphamap & alpha_mask; uint16 alpha_value = (masked & 0x1f) + ((masked >> 5) & 0x1f) + ((masked >> 10) & 0x1f); if( alpha_value > 30 ) { // opaque, just copy source *dest = rgbmap_current[*src]; } else if( alpha_value > 0 ) { alpha_value = alpha_value > 15 ? alpha_value + 1 : alpha_value; *dest = colors_blend_alpha32(*dest, rgbmap_current[*src], alpha_value); } dest++; src++; alphamap++; } } static void display_img_alpha_wc(scr_coord_val h, const scr_coord_val xp, const scr_coord_val yp, const PIXVAL *sp, const PIXVAL *alphamap, const PIXVAL alpha_mask, int colour, alpha_proc p CLIP_NUM_DEF ) { if( h > 0 ) { PIXVAL *tp = textur + yp * disp_width; do { // line decoder int xpos = xp; // display image uint16 runlen = *sp++; alphamap++; do { // we start with a clear run xpos += runlen; // now get colored pixels runlen = ((*sp++) & ~TRANSPARENT_RUN); alphamap++; // something to display? if( xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx ) { const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos); const int len = (CR.clip_rect.xx - xpos >= runlen ? runlen : CR.clip_rect.xx - xpos); p( tp + xpos + left, sp + left, alphamap + left, alpha_mask, colour, len - left ); } sp += runlen; alphamap += runlen; xpos += runlen; alphamap++; } while( (runlen = *sp++) ); tp += disp_width; } while( --h ); } } /** * draws the transparent outline of an image */ static void simgraph16_draw_rezoomed_img_blend(const image_id n, scr_coord_val xp, scr_coord_val yp, const signed char /*player_nr*/, const FLAGGED_PIXVAL color_index, const bool /*daynight*/, const bool dirty CLIP_NUM_DEF) { if( n < anz_images ) { // need to go to nightmode and or rezoomed? if( (images[n].recode_flags & FLAG_REZOOM) ) { rezoom_img( n ); recode_img( n, 0 ); } else if( (images[n].player_flags & 1) ) { recode_img( n, 0 ); } PIXVAL *sp = images[n].data[0]; // now, since zooming may have change this image xp += images[n].x; yp += images[n].y; scr_coord_val h = images[n].h; // may change due to vertical clipping // in the next line the vertical clipping will be handled // by that way the drawing routines must only take into account the horizontal clipping // this should be much faster in most cases // must the height be reduced? scr_coord_val reduce_h = yp + h - CR.clip_rect.yy; if( reduce_h > 0 ) { h -= reduce_h; } // still something to draw if( h <= 0 ) return; // vertically lines to skip (only bottom is visible) scr_coord_val skip_lines = CR.clip_rect.y - (int)yp; if( skip_lines > 0 ) { if( skip_lines >= h ) { // not visible at all return; } h -= skip_lines; yp += skip_lines; // now skip them while (skip_lines--) { do { // clear run + colored run + next clear run sp++; sp += (*sp) & (~TRANSPARENT_RUN); sp++; } while (*sp); sp++; } // now sp is the new start of an image with height h } // new block for new variables { // needed now ... const scr_coord_val w = images[n].w; // get the real color const PIXVAL color = color_index & 0xFFFF; // we use function pointer for the blend runs for the moment ... blend_proc pix_blend = (color_index&OUTLINE_FLAG) ? outline[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ] : blend[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ]; // marking change? if( dirty ) { simgraph16_mark_rect_dirty_wc( xp, yp, xp + w - 1, yp + h - 1 ); } display_img_blend_wc( h, xp, yp, sp, color, pix_blend CLIP_NUM_PAR ); } } } static void simgraph16_draw_rezoomed_img_alpha(const image_id n, const image_id alpha_n, const unsigned alpha_flags, scr_coord_val xp, scr_coord_val yp, const sint8 /*player_nr*/, const FLAGGED_PIXVAL color_index, const bool /*daynight*/, const bool dirty CLIP_NUM_DEF) { if( n < anz_images && alpha_n < anz_images ) { // need to go to nightmode and or rezoomed? if( (images[n].recode_flags & FLAG_REZOOM) ) { rezoom_img( n ); recode_img( n, 0 ); } else if( (images[n].player_flags & 1) ) { recode_img( n, 0 ); } if( (images[alpha_n].recode_flags & FLAG_REZOOM) ) { rezoom_img( alpha_n ); } PIXVAL *sp = images[n].data[0]; // alphamap image uses base data as we don't want to recode PIXVAL *alphamap = images[alpha_n].zoom_data != NULL ? images[alpha_n].zoom_data : images[alpha_n].base_data; // now, since zooming may have change this image xp += images[n].x; yp += images[n].y; scr_coord_val h = images[n].h; // may change due to vertical clipping // in the next line the vertical clipping will be handled // by that way the drawing routines must only take into account the horizontal clipping // this should be much faster in most cases // must the height be reduced? scr_coord_val reduce_h = yp + h - CR.clip_rect.yy; if( reduce_h > 0 ) { h -= reduce_h; } // still something to draw if( h <= 0 ) { return; } // vertically lines to skip (only bottom is visible scr_coord_val skip_lines = CR.clip_rect.y - (int)yp; if( skip_lines > 0 ) { if( skip_lines >= h ) { // not visible at all return; } h -= skip_lines; yp += skip_lines; // now skip them while( skip_lines-- ) { do { // clear run + colored run + next clear run sp++; sp += (*sp) & (~TRANSPARENT_RUN); sp++; alphamap++; alphamap += (*alphamap) & (~TRANSPARENT_RUN); alphamap++; } while( *sp ); sp++; alphamap++; } // now sp is the new start of an image with height h (same for alphamap) } // new block for new variables { // needed now ... const scr_coord_val w = images[n].w; // get the real color const PIXVAL color = color_index & 0xFFFF; // marking change? if( dirty ) { simgraph16_mark_rect_dirty_wc( xp, yp, xp + w - 1, yp + h - 1 ); } display_img_alpha_wc( h, xp, yp, sp, alphamap, get_alpha_mask(alpha_flags), color, alpha CLIP_NUM_PAR ); } } } // For blending or outlining unzoomed image. Adapted from display_base_img() and display_unzoomed_img_blend() static void simgraph16_draw_base_img_blend(const image_id n, scr_coord_val xp, scr_coord_val yp, const signed char player_nr, const FLAGGED_PIXVAL color_index, const bool daynight, const bool dirty CLIP_NUM_DEF) { if( g_simgraph16.tile_raster_width == g_simgraph16.base_tile_raster_width ) { // same size => use standard routine simgraph16_draw_rezoomed_img_blend( n, xp, yp, player_nr, color_index, daynight, dirty CLIP_NUM_PAR ); } else if( n < anz_images ) { // now test if visible and clipping needed scr_coord_val x = images[n].base_x + xp; scr_coord_val y = images[n].base_y + yp; scr_coord_val w = images[n].base_w; scr_coord_val h = images[n].base_h; if( h == 0 || x >= CR.clip_rect.xx || y >= CR.clip_rect.yy || x + w <= CR.clip_rect.x || y + h <= CR.clip_rect.y ) { // not visible => we are done // happens quite often ... return; } PIXVAL *sp = images[n].base_data; // must the height be reduced? scr_coord_val reduce_h = y + h - CR.clip_rect.yy; if( reduce_h > 0 ) { h -= reduce_h; } // vertical lines to skip (only bottom is visible) scr_coord_val skip_lines = CR.clip_rect.y - (int)y; if( skip_lines > 0 ) { h -= skip_lines; y += skip_lines; // now skip them while (skip_lines--) { do { // clear run + colored run + next clear run sp++; sp += (*sp) & (~TRANSPARENT_RUN); sp++; } while (*sp); sp++; } // now sp is the new start of an image with height h } // new block for new variables { const PIXVAL color = color_index & 0xFFFF; blend_proc pix_blend = (color_index&OUTLINE_FLAG) ? outline[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ] : blend_recode[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ]; // recode is needed only for blending if( !(color_index&OUTLINE_FLAG) ) { // colors for 2nd company color if(player_nr>=0) { activate_player_color( player_nr, daynight ); } else { // no player activate_player_color( 0, daynight ); } } if( dirty ) { simgraph16_mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 ); } display_img_blend_wc( h, x, y, sp, color, pix_blend CLIP_NUM_PAR ); } } // number ok } static void simgraph16_draw_base_img_alpha(const image_id n, const image_id alpha_n, const unsigned alpha_flags, scr_coord_val xp, scr_coord_val yp, const sint8 player_nr, const FLAGGED_PIXVAL color_index, const bool daynight, const bool dirty CLIP_NUM_DEF) { if( g_simgraph16.tile_raster_width == g_simgraph16.base_tile_raster_width ) { // same size => use standard routine simgraph16_draw_rezoomed_img_alpha( n, alpha_n, alpha_flags, xp, yp, player_nr, color_index, daynight, dirty CLIP_NUM_PAR ); } else if( n < anz_images ) { // now test if visible and clipping needed scr_coord_val x = images[n].base_x + xp; scr_coord_val y = images[n].base_y + yp; scr_coord_val w = images[n].base_w; scr_coord_val h = images[n].base_h; if( h == 0 || x >= CR.clip_rect.xx || y >= CR.clip_rect.yy || x + w <= CR.clip_rect.x || y + h <= CR.clip_rect.y ) { // not visible => we are done // happens quite often ... return; } PIXVAL *sp = images[n].base_data; PIXVAL *alphamap = images[alpha_n].base_data; // must the height be reduced? scr_coord_val reduce_h = y + h - CR.clip_rect.yy; if( reduce_h > 0 ) { h -= reduce_h; } // vertical lines to skip (only bottom is visible) scr_coord_val skip_lines = CR.clip_rect.y - (int)y; if( skip_lines > 0 ) { h -= skip_lines; y += skip_lines; // now skip them while( skip_lines-- ) { do { // clear run + colored run + next clear run sp++; sp += (*sp) & (~TRANSPARENT_RUN); sp++; } while( *sp ); do { // clear run + colored run + next clear run alphamap++; alphamap += (*alphamap) & (~TRANSPARENT_RUN); alphamap++; } while( *alphamap ); sp++; alphamap++; } // now sp is the new start of an image with height h } // new block for new variables { const PIXVAL color = color_index & 0xFFFF; // recode is needed only for blending if( !(color_index & OUTLINE_FLAG) ) { // colors for 2nd company color if( player_nr >= 0 ) { activate_player_color( player_nr, daynight ); } else { // no player activate_player_color( 0, daynight ); } } if( dirty ) { simgraph16_mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 ); } display_img_alpha_wc( h, x, y, sp, alphamap, get_alpha_mask(alpha_flags), color, alpha_recode CLIP_NUM_PAR ); } } // number ok } // ----------------- basic painting procedures ---------------- // scrolls horizontally, will ignore clipping etc. static void simgraph16_move_scroll_band(scr_coord_val start_y, scr_coord_val x_offset, scr_coord_val h) { start_y = max(start_y, 0); x_offset = min(x_offset, disp_width); h = min(h, disp_height); const PIXVAL *const src = textur + start_y * disp_width + x_offset; PIXVAL *const dst = textur + start_y * disp_width; const size_t amount = sizeof(PIXVAL) * (h * disp_width - x_offset); memmove(dst, src, amount); } /** * Draw one Pixel */ #ifdef DEBUG_FLUSH_BUFFER static void display_pixel(scr_coord_val x, scr_coord_val y, PIXVAL color, bool mark_dirty=true) #else static void display_pixel(scr_coord_val x, scr_coord_val y, PIXVAL color) #endif { if( x >= CR0.clip_rect.x && x < CR0.clip_rect.xx && y >= CR0.clip_rect.y && y < CR0.clip_rect.yy ) { PIXVAL* const p = textur + x + y * disp_width; *p = color; #ifdef DEBUG_FLUSH_BUFFER if( mark_dirty ) { #endif mark_tile_dirty(x >> DIRTY_TILE_SHIFT, y >> DIRTY_TILE_SHIFT); #ifdef DEBUG_FLUSH_BUFFER } #endif } } /** * Draw filled rectangle */ static void display_fb_internal(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL colval, bool dirty, scr_coord_val cL, scr_coord_val cR, scr_coord_val cT, scr_coord_val cB) { if (clip_lr(&xp, &w, cL, cR) && clip_lr(&yp, &h, cT, cB)) { PIXVAL *p = textur + xp + yp * disp_width; const int dx = disp_width - w; if (dirty) { mark_rect_dirty_nc(xp, yp, xp + w - 1, yp + h - 1); } #if defined USE_ASSEMBLER && defined __GNUC__ && defined __i686__ // GCC might not use "rep stos" so force its use const uint32 longcolval = (colval << 16) | colval; do { unsigned int count = w; asm volatile ( // uneven words to copy? "shrl %1\n\t" "jnc 0f\n\t" // set first word "stosw\n\t" "0:\n\t" // now we set long words ... "rep\n\t" "stosl" : "+D" (p), "+c" (count) : "a" (longcolval) : "cc", "memory" ); p += dx; } while (--h); #elif defined LOW_LEVEL // low level c++ const uint32 colvald = (colval << 16) | colval; do { scr_coord_val count = w; // align to 4 bytes, should use uintptr_t but not available if( reinterpret_cast(p) & 0x2 ) { *p++ = (PIXVAL)colvald; count--; } // aligned fast fill loop bool const postalign = count & 1; count >>= 1; uint32 *lp = (uint32 *)p; while(count--) { *lp++ = colvald; } p = (PIXVAL *)lp; // finish unaligned remainder if( postalign ) { *p++ = (PIXVAL)colvald; } p += dx; } while (--h); #else // high level c++ do { PIXVAL *const fillend = p + w; std::fill(p, fillend, colval); p = fillend + dx; } while (--h); #endif } } static void simgraph16_draw_rect(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty) { display_fb_internal(xp, yp, w, h, color, dirty, 0, disp_width, 0, disp_height); } static void simgraph16_draw_rect_clipped(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty CLIP_NUM_DEF) { display_fb_internal( xp, yp, w, h, color, dirty, CR.clip_rect.x, CR.clip_rect.xx, CR.clip_rect.y, CR.clip_rect.yy ); } static void simgraph16_draw_rounded_rect_clipped(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, bool dirty) { simgraph16_draw_rect_clipped(xp+2, yp, w-4, h, color, dirty CLIP_NUM_DEFAULT); simgraph16_draw_rect_clipped(xp, yp+2, 1, h-4, color, dirty CLIP_NUM_DEFAULT); simgraph16_draw_rect_clipped(xp+1, yp+1, 1, h-2, color, dirty CLIP_NUM_DEFAULT); simgraph16_draw_rect_clipped(xp+w-1, yp+2, 1, h-4, color, dirty CLIP_NUM_DEFAULT); simgraph16_draw_rect_clipped(xp+w-2, yp+1, 1, h-2, color, dirty CLIP_NUM_DEFAULT); } /** * Draw vertical line */ static void display_vl_internal(const scr_coord_val xp, scr_coord_val yp, scr_coord_val h, const PIXVAL colval, int dirty, scr_coord_val cL, scr_coord_val cR, scr_coord_val cT, scr_coord_val cB) { if (xp >= cL && xp < cR && clip_lr(&yp, &h, cT, cB)) { PIXVAL *p = textur + xp + yp * disp_width; if (dirty) mark_rect_dirty_nc(xp, yp, xp, yp + h - 1); do { *p = colval; p += disp_width; } while (--h != 0); } } void display_vline_wh_rgb(const scr_coord_val xp, scr_coord_val yp, scr_coord_val h, const PIXVAL color, bool dirty) { display_vl_internal(xp, yp, h, color, dirty, 0, disp_width, 0, disp_height); } static void simgraph16_draw_vline_clipped(const scr_coord_val xp, scr_coord_val yp, scr_coord_val h, const PIXVAL color, bool dirty CLIP_NUM_DEF) { display_vl_internal( xp, yp, h, color, dirty, CR.clip_rect.x, CR.clip_rect.xx, CR.clip_rect.y, CR.clip_rect.yy ); } /** * Draw raw Pixel data */ static void simgraph16_draw_array(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, const PIXVAL *arr) { const int arr_w = w; const scr_coord_val xoff = clip_wh( &xp, &w, CR0.clip_rect.x, CR0.clip_rect.xx ); const scr_coord_val yoff = clip_wh( &yp, &h, CR0.clip_rect.y, CR0.clip_rect.yy ); if( w > 0 && h > 0 ) { PIXVAL *p = textur + xp + yp * disp_width; const PIXVAL *arr_src = arr; mark_rect_dirty_nc(xp, yp, xp + w - 1, yp + h - 1); if( xp == CR0.clip_rect.x ) arr_src += xoff; if( yp == CR0.clip_rect.y ) arr_src += yoff * arr_w; do { unsigned int ww = w; do { *p++ = *arr_src++; } while (--ww > 0); arr_src += arr_w - w; p += disp_width - w; } while (--h != 0); } } // --------------------------------- text rendering stuff ------------------------------ static bool simgraph16_load_font(const char *fname, bool reload) { font_t loaded_fnt; if( fname == NULL ) { dbg->error( "display_load_font", "NULL filename" ); return false; } // skip reloading if already in memory, if bdf font if( !reload && default_font.is_loaded() && strcmp( default_font.get_fname(), fname ) == 0 ) { return true; } if( loaded_fnt.load_from_file(fname) ) { default_font = loaded_fnt; default_font_ascent = default_font.get_ascent(); default_font_linespace = default_font.get_linespace(); // find default number width const char* digits = "0123456789"; default_font_numberwidth = 0; while (*digits) { int pixel = default_font.get_glyph_advance(*digits++); if (pixel > default_font_numberwidth) { default_font_numberwidth = pixel; } } env_t::fontname = fname; return default_font.is_loaded(); } return false; } static scr_coord_val simgraph16_get_char_width(utf32 c) { return default_font.get_glyph_advance(c); } static scr_coord_val simgraph16_get_number_width() { return default_font_numberwidth; } /** * For the next logical character in the text, returns the character code * as well as retrieves the char byte count and the screen pixel width * CAUTION : The text pointer advances to point to the next logical character */ static utf32 simgraph16_get_next_char_with_metrics(const char* &text, unsigned char &byte_length, unsigned char &pixel_width) { size_t len = 0; utf32 const char_code = utf8_decoder_t::decode((utf8 const *)text, len); if( char_code==UNICODE_NUL || char_code == '\n') { // case : end of text reached -> do not advance text pointer // also stop at linebreaks byte_length = 0; pixel_width = 0; return 0; } else { text += len; byte_length = (uint8)len; pixel_width = default_font.get_glyph_advance(char_code); } return char_code; } /* returns true, if this is a valid character */ static bool simgraph16_font_has_character(utf16 char_code) { return default_font.is_valid_glyph(char_code); } /* * returns the index of the last character that would fit within the width * If an ellipsis len is given, it will only return the last character up to this len if the full length cannot be fitted * @returns index of next character. if text[index]==0 the whole string fits */ static size_t simgraph16_calc_text_index_for_width(const char *text, scr_coord_val max_width) { size_t max_idx = 0; uint8 byte_length = 0; uint8 pixel_width = 0; scr_coord_val current_offset = 0; const char *tmp_text = text; while( simgraph16_get_next_char_with_metrics(tmp_text, byte_length, pixel_width) && max_width > (current_offset+pixel_width) ) { current_offset += pixel_width; max_idx += byte_length; } return max_idx; } /** * For the previous logical character in the text, returns the character code * as well as retrieves the char byte count and the screen pixel width * CAUTION : The text pointer recedes to point to the previous logical character */ static utf32 simgraph16_get_prev_char_with_metrics(const char* &text, const char *const text_start, unsigned char &byte_length, unsigned char &pixel_width) { if( text<=text_start ) { // case : start of text reached or passed -> do not move the pointer backwards byte_length = 0; pixel_width = 0; return 0; } utf32 char_code; // determine the start of the previous logical character do { --text; } while ( text>text_start && (*text & 0xC0)==0x80 ); size_t len = 0; char_code = utf8_decoder_t::decode((utf8 const *)text, len); byte_length = (uint8)len; pixel_width = default_font.get_glyph_advance(char_code); return char_code; } /* proportional_string_width with a text of a given length * extended for universal font routines with unicode support */ static scr_coord_val simgraph16_calc_text_width_n(const char *text, size_t len) { uint8 byte_length = 0; uint8 pixel_width = 0; size_t idx = 0; scr_coord_val width = 0; while (simgraph16_get_next_char_with_metrics(text, byte_length, pixel_width) && idx < len) { width += pixel_width; idx += byte_length; } return width; } /* display_calc_proportional_multiline_string_len_width * calculates the width and hieght of a box containing the text inside */ static scr_size simgraph16_calc_multiline_text_size(const char *text) { const font_t* const fnt = &default_font; int width = 0; bool last_cr = false; scr_size size{0,0}; const utf8 *p = reinterpret_cast(text); while (const utf32 iUnicode = utf8_decoder_t::decode(p)) { if( iUnicode == '\n' ) { // new line: record max width size.w = max( size.w, width ); size.h += LINESPACE; width = 0; last_cr = true; continue; } last_cr = false; width += fnt->get_glyph_advance(iUnicode); } size.w = max( size.w, width ); if (!last_cr) { // extra CR of the last was not already a CR size.h += LINESPACE; } return size; } /** * len parameter added - use -1 for previous behaviour. * completely renovated for unicode and 10 bit width and variable height */ static scr_coord_val simgraph16_draw_text_clipped_n(scr_coord_val x, scr_coord_val y, const char* txt, control_alignment_t flags, const PIXVAL color, bool dirty, sint32 len CLIP_NUM_DEF) { scr_coord_val cL, cR, cT, cB; // TAKE CARE: Clipping area may be larger than actual screen size if( (flags & DT_CLIP) ) { cL = CR.clip_rect.x; cR = CR.clip_rect.xx; cT = CR.clip_rect.y; cB = CR.clip_rect.yy; } else { cL = 0; cR = disp_width; cT = 0; cB = disp_height; } if (len < 0) { // don't know len yet len = 0x7FFF; } // adapt x-coordinate for alignment switch (flags & ( ALIGN_LEFT | ALIGN_CENTER_H | ALIGN_RIGHT) ) { case ALIGN_LEFT: // nothing to do break; case ALIGN_CENTER_H: x -= simgraph16_calc_text_width_n(txt, len) / 2; break; case ALIGN_RIGHT: x -= simgraph16_calc_text_width_n(txt, len); break; } // still something to display? const font_t *const fnt = &default_font; if (x >= cR || y >= cB || y + fnt->get_linespace() <= cT) { // nothing to display return 0; } // store the initial x (for dirty marking) const scr_coord_val x0 = x; // big loop, draw char by char utf8_decoder_t decoder((utf8 const*)txt); size_t iTextPos = 0; // pointer on text position while (iTextPos < (size_t)len && decoder.has_next()) { // decode char utf32 c = decoder.next(); iTextPos = decoder.get_position() - (utf8 const*)txt; if( c == '\n' ) { // stop at linebreak break; } // print unknown character? else if( !fnt->is_valid_glyph(c) ) { c = 0; } // get the data from the font const font_t::glyph_t& glyph = fnt->get_glyph(c); const uint8 *p = glyph.bitmap; int screen_pos = (y + glyph.top) * disp_width + x + glyph.left; // glyph x clipping int g_left = max(cL - x - glyph.left, 0); int g_right = min(cR - x - glyph.left, glyph.width); // all visible rows for (int h = 0; h < glyph.height; h++) { const int line = y + glyph.top + h; if(line >= cT && line < cB) { PIXVAL* dst = textur + screen_pos + g_left; // all columns for(int gx=g_left; gx 31) { // opaque *dst++ = color; } else { // partially transparent -> blend it PIXVAL old_color = *dst; *dst++ = colors_blend_alpha32(old_color, color, alpha); } } } screen_pos += disp_width; } x += fnt->get_glyph_advance(c); } if( dirty ) { // here, because only now we know the length also for ALIGN_LEFT text simgraph16_mark_rect_dirty_clip( x0, y, x - 1, y + LINESPACE - 1 CLIP_NUM_PAR); } // warning: actual len might be longer, due to clipping! return x - x0; } /// Displays a string which is abbreviated by the (language specific) ellipsis character if too wide /// If enough space is given then it just displays the full string static void simgraph16_draw_text_ellipsis_shadowed(scr_rect r, const char *text, int align, const PIXVAL color, const bool dirty, bool shadowed, PIXVAL shadow_color) { const scr_coord_val ellipsis_width = translator::get_lang()->ellipsis_width; const scr_coord_val max_screen_width = r.w; size_t max_idx = 0; uint8 byte_length = 0; uint8 pixel_width = 0; scr_coord_val current_offset = 0; if( align & ALIGN_CENTER_V ) { r.y += (r.h - LINESPACE)/2; align &= ~ALIGN_CENTER_V; } const char *tmp_text = text; while( simgraph16_get_next_char_with_metrics(tmp_text, byte_length, pixel_width) && max_screen_width >= (current_offset+ellipsis_width+pixel_width) ) { current_offset += pixel_width; max_idx += byte_length; } size_t max_idx_before_ellipsis = max_idx; scr_coord_val max_offset_before_ellipsis = current_offset; // now check if the text would fit completely if( ellipsis_width && pixel_width > 0 ) { // only when while above failed because of exceeding length current_offset += pixel_width; max_idx += byte_length; // check the rest ... while( simgraph16_get_next_char_with_metrics(tmp_text, byte_length, pixel_width) && max_screen_width >= (current_offset+pixel_width) ) { current_offset += pixel_width; max_idx += byte_length; } // if it does not fit if( max_screen_width < (current_offset+pixel_width) ) { scr_coord_val w = 0; // since we know the length already, we try to center the text with the remaining pixels of the last character if( align & ALIGN_CENTER_H ) { w = (max_screen_width-max_offset_before_ellipsis-ellipsis_width)/2; } if (shadowed) { simgraph16_draw_text_clipped_n( r.x+w+1, r.y+1, text, ALIGN_LEFT | DT_CLIP, shadow_color, dirty, max_idx_before_ellipsis CLIP_NUM_DEFAULT); } w += simgraph16_draw_text_clipped_n( r.x+w, r.y, text, ALIGN_LEFT | DT_CLIP, color, dirty, max_idx_before_ellipsis CLIP_NUM_DEFAULT); if (shadowed) { simgraph16_draw_text_clipped_n( r.x+w+1, r.y+1, translator::translate("..."), ALIGN_LEFT | DT_CLIP, shadow_color, dirty, -1 CLIP_NUM_DEFAULT); } simgraph16_draw_text_clipped_n( r.x+w, r.y, translator::translate("..."), ALIGN_LEFT | DT_CLIP, color, dirty, -1 CLIP_NUM_DEFAULT); return; } else { // if this fits, end of string max_idx += byte_length; current_offset += pixel_width; } } switch (align & ALIGN_RIGHT) { case ALIGN_CENTER_H: r.x += (max_screen_width - current_offset)/2; break; case ALIGN_RIGHT: r.x += max_screen_width - current_offset; default: ; } if (shadowed) { simgraph16_draw_text_clipped_n( r.x+1, r.y+1, text, ALIGN_LEFT | DT_CLIP, shadow_color, dirty, -1 CLIP_NUM_DEFAULT); } simgraph16_draw_text_clipped_n( r.x, r.y, text, ALIGN_LEFT | DT_CLIP, color, dirty, -1 CLIP_NUM_DEFAULT); } /** * Draw shaded rectangle using direct color values */ static void simgraph16_draw_box3d(scr_coord_val x1, scr_coord_val y1, scr_coord_val w, scr_coord_val h, PIXVAL tl_color, PIXVAL rd_color, bool dirty) { simgraph16_draw_rect(x1, y1, w, 1, tl_color, dirty); simgraph16_draw_rect(x1, y1 + h - 1, w, 1, rd_color, dirty); h -= 2; display_vline_wh_rgb(x1, y1 + 1, h, tl_color, dirty); display_vline_wh_rgb(x1 + w - 1, y1 + 1, h, rd_color, dirty); } static void simgraph16_draw_text_outlined(scr_coord_val xpos, scr_coord_val ypos, PIXVAL text_color, PIXVAL shadow_color, const char *text, int dirty) { const int flags = ALIGN_LEFT | DT_CLIP; simgraph16_draw_text_clipped_n(xpos - 1, ypos , text, flags, shadow_color, dirty, -1 CLIP_NUM_DEFAULT); simgraph16_draw_text_clipped_n(xpos + 1, ypos + 2, text, flags, shadow_color, dirty, -1 CLIP_NUM_DEFAULT); simgraph16_draw_text_clipped_n(xpos, ypos + 1, text, flags, text_color, dirty, -1 CLIP_NUM_DEFAULT); } static void simgraph16_draw_text_shadowed(scr_coord_val xpos, scr_coord_val ypos, PIXVAL text_color, PIXVAL shadow_color, const char *text, int dirty) { const int flags = ALIGN_LEFT | DT_CLIP; simgraph16_draw_text_clipped_n(xpos + 1, ypos + 1 + (12 - LINESPACE) / 2, text, flags, shadow_color, dirty, -1 CLIP_NUM_DEFAULT); simgraph16_draw_text_clipped_n(xpos, ypos + (12 - LINESPACE) / 2, text, flags, text_color, dirty, -1 CLIP_NUM_DEFAULT); } /** * Draw shaded rectangle using direct color values */ static void simgraph16_draw_box3d_clipped(scr_coord_val x1, scr_coord_val y1, scr_coord_val w, scr_coord_val h, PIXVAL tl_color, PIXVAL rd_color) { simgraph16_draw_rect_clipped(x1, y1, w, 1, tl_color, true CLIP_NUM_DEFAULT); simgraph16_draw_rect_clipped(x1, y1 + h - 1, w, 1, rd_color, true CLIP_NUM_DEFAULT); h -= 2; simgraph16_draw_vline_clipped(x1, y1 + 1, h, tl_color, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x1 + w - 1, y1 + 1, h, rd_color, true CLIP_NUM_DEFAULT); } /** * display text in 3d box with clipping */ static void simgraph16_draw_textbox3d_clipped(scr_coord_val xpos, scr_coord_val ypos, FLAGGED_PIXVAL ddd_color, FLAGGED_PIXVAL text_color, const char *text, int dirty CLIP_NUM_DEF) { const int vpadding = LINESPACE / 7; const int hpadding = LINESPACE / 4; scr_coord_val width = simgraph16_calc_text_width_n(text, 0x7FFFu); PIXVAL lighter = display_blend_colors_alpha32(ddd_color, gfx->palette_lookup(COL_WHITE), 8 /* 25% */); PIXVAL darker = display_blend_colors_alpha32(ddd_color, gfx->palette_lookup(COL_BLACK), 8 /* 25% */); simgraph16_draw_rect_clipped( xpos+1, ypos - vpadding + 1, width+2*hpadding-2, LINESPACE+2*vpadding-1, ddd_color, dirty CLIP_NUM_PAR); gfx->draw_rect_clipped( xpos, ypos - vpadding, width + 2*hpadding - 2, 1, lighter, dirty CLIP_NUM_DEFAULT); gfx->draw_rect_clipped( xpos, ypos + LINESPACE + vpadding, width + 2*hpadding - 2, 1, darker, dirty CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped( xpos, ypos - vpadding, LINESPACE + vpadding * 2, lighter, dirty CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped( xpos + width + 2*hpadding - 2, ypos - vpadding, LINESPACE + vpadding * 2, darker, dirty CLIP_NUM_DEFAULT); simgraph16_draw_text_clipped_n( xpos+hpadding, ypos+1, text, ALIGN_LEFT | DT_CLIP, text_color, dirty, -1 CLIP_NUM_DEFAULT); } /** * Draw multiline text */ static scr_coord_val simgraph16_draw_multiline_text(scr_coord_val x, scr_coord_val y, const char *buf, PIXVAL color) { scr_coord_val max_px_len = 0; if (buf != NULL && *buf != '\0') { const char *next; do { next = strchr(buf, '\n'); const scr_coord_val px_len = simgraph16_draw_text_clipped_n( x, y, buf, ALIGN_LEFT | DT_CLIP, color, true, next != NULL ? (int)(size_t)(next - buf) : -1 CLIP_NUM_DEFAULT ); if( px_len>max_px_len ) { max_px_len = px_len; } y += LINESPACE; } while ((void)(buf = (next ? next+1 : NULL)), buf != NULL); } return max_px_len; } /** * draw line from x,y to xx,yy **/ static void simgraph16_draw_line(const scr_coord_val x, const scr_coord_val y, const scr_coord_val xx, const scr_coord_val yy, const PIXVAL colval) { int i, steps; sint64 xp, yp; sint64 xs, ys; const int dx = xx - x; const int dy = yy - y; steps = (abs(dx) > abs(dy) ? abs(dx) : abs(dy)); if (steps == 0) { steps = 1; } xs = ((sint64)dx << 16) / steps; ys = ((sint64)dy << 16) / steps; xp = (sint64)x << 16; yp = (sint64)y << 16; for (i = 0; i <= steps; i++) { #ifdef DEBUG_FLUSH_BUFFER display_pixel(xp >> 16, yp >> 16, colval, false); #else display_pixel(xp >> 16, yp >> 16, colval); #endif xp += xs; yp += ys; } } //taken from function simgraph16_draw_line() above, to draw a dotted line: draw=pixels drawn, dontDraw=pixels skipped static void simgraph16_draw_line_dotted(const scr_coord_val x, const scr_coord_val y, const scr_coord_val xx, const scr_coord_val yy, const scr_coord_val draw, const scr_coord_val dontDraw, const PIXVAL colval) { int i, steps; sint64 xp, yp; sint64 xs, ys; int counter=0; bool mustDraw=true; const int dx = xx - x; const int dy = yy - y; steps = (abs(dx) > abs(dy) ? abs(dx) : abs(dy)); if (steps == 0) { steps = 1; } xs = ((sint64)dx << 16) / steps; ys = ((sint64)dy << 16) / steps; xp = (sint64)x << 16; yp = (sint64)y << 16; for( i = 0; i <= steps; i++ ) { counter ++; if( mustDraw ) { if( counter == draw ) { mustDraw = !mustDraw; counter = 0; } } if( !mustDraw ) { if( counter == dontDraw ) { mustDraw=!mustDraw; counter=0; } } if( mustDraw ) { display_pixel( xp >> 16, yp >> 16, colval ); } xp += xs; yp += ys; } } // bresenham circle (from wikipedia ...) static void simgraph16_draw_empty_circle( scr_coord_val x0, scr_coord_val y0, int radius, const PIXVAL colval ) { int f = 1 - radius; int ddF_x = 1; int ddF_y = -2 * radius; int x = 0; int y = radius; display_pixel( x0, y0 + radius, colval ); display_pixel( x0, y0 - radius, colval ); display_pixel( x0 + radius, y0, colval ); display_pixel( x0 - radius, y0, colval ); while(x < y) { // ddF_x == 2 * x + 1; // ddF_y == -2 * y; // f == x*x + y*y - radius*radius + 2*x - y + 1; if(f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; display_pixel( x0 + x, y0 + y, colval ); display_pixel( x0 - x, y0 + y, colval ); display_pixel( x0 + x, y0 - y, colval ); display_pixel( x0 - x, y0 - y, colval ); display_pixel( x0 + y, y0 + x, colval ); display_pixel( x0 - y, y0 + x, colval ); display_pixel( x0 + y, y0 - x, colval ); display_pixel( x0 - y, y0 - x, colval ); } } // bresenham circle (from wikipedia ...) static void simgraph16_draw_filled_circle(scr_coord_val x0, scr_coord_val y0, int radius, const PIXVAL colval) { int f = 1 - radius; int ddF_x = 1; int ddF_y = -2 * radius; int x = 0; int y = radius; display_fb_internal( x0-radius, y0, radius+radius+1, 1, colval, false, CR0.clip_rect.x, CR0.clip_rect.xx, CR0.clip_rect.y, CR0.clip_rect.yy ); display_pixel( x0, y0 + radius, colval ); display_pixel( x0, y0 - radius, colval ); display_pixel( x0 + radius, y0, colval ); display_pixel( x0 - radius, y0, colval ); while(x < y) { // ddF_x == 2 * x + 1; // ddF_y == -2 * y; // f == x*x + y*y - radius*radius + 2*x - y + 1; if(f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; display_fb_internal( x0-x, y0+y, x+x, 1, colval, false, CR0.clip_rect.x, CR0.clip_rect.xx, CR0.clip_rect.y, CR0.clip_rect.yy ); display_fb_internal( x0-x, y0-y, x+x, 1, colval, false, CR0.clip_rect.x, CR0.clip_rect.xx, CR0.clip_rect.y, CR0.clip_rect.yy ); display_fb_internal( x0-y, y0+x, y+y, 1, colval, false, CR0.clip_rect.x, CR0.clip_rect.xx, CR0.clip_rect.y, CR0.clip_rect.yy ); display_fb_internal( x0-y, y0-x, y+y, 1, colval, false, CR0.clip_rect.x, CR0.clip_rect.xx, CR0.clip_rect.y, CR0.clip_rect.yy ); } // mark_rect_dirty_wc( x0-radius, y0-radius, x0+radius+1, y0+radius+1 ); } static void simgraph16_draw_signal_direction(scr_coord_val x, scr_coord_val y, uint8 way_dir, uint8 sig_dir, PIXVAL col1, PIXVAL col1_dark, bool is_diagonal, uint8 slope) { uint8 width = is_diagonal ? g_simgraph16.current_tile_raster_width/6*0.353 : g_simgraph16.current_tile_raster_width/6; const uint8 height = is_diagonal ? g_simgraph16.current_tile_raster_width/6*0.353 : g_simgraph16.current_tile_raster_width/12; const uint8 thickness = max( g_simgraph16.current_tile_raster_width/36, 2); x += g_simgraph16.current_tile_raster_width/2; y += (g_simgraph16.current_tile_raster_width*9)/16; if (is_diagonal) { if (way_dir == ribi_t::northeast || way_dir == ribi_t::southwest) { // vertical x += (way_dir==ribi_t::northeast) ? g_simgraph16.current_tile_raster_width/4 : (-g_simgraph16.current_tile_raster_width/4); y += g_simgraph16.current_tile_raster_width/16; width = width<<2; // 4x // upper for (uint8 xoff = 0; xoff < width/2; xoff++) { const uint8 yoff = (uint8)((xoff+1)/2); // up if (sig_dir & ribi_t::east || sig_dir & ribi_t::south) { simgraph16_draw_vline_clipped(x + xoff, y+yoff, width/4 - yoff, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x-xoff-1, y+yoff, width/4 - yoff, col1, true CLIP_NUM_DEFAULT); } // down if (sig_dir & ribi_t::west || sig_dir & ribi_t::north) { simgraph16_draw_vline_clipped(x+xoff, y+g_simgraph16.current_tile_raster_width/6, width/4-yoff, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x+xoff, y+g_simgraph16.current_tile_raster_width/6+width/4-yoff, thickness, col1_dark, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x-xoff-1, y+g_simgraph16.current_tile_raster_width/6, width/4-yoff, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x-xoff-1, y+g_simgraph16.current_tile_raster_width/6+width/4-yoff, thickness, col1_dark, true CLIP_NUM_DEFAULT); } } // up if (sig_dir & ribi_t::east || sig_dir & ribi_t::south) { gfx->draw_rect_clipped(x - width/2, y + width/4, width, thickness, col1_dark, true CLIP_NUM_DEFAULT); } } else { // horizontal y -= g_simgraph16.current_tile_raster_width/12; if (way_dir == ribi_t::southeast) { y += g_simgraph16.current_tile_raster_width/4; } for (uint8 xoff = 0; xoff < width*2; xoff++) { const uint8 h = width*2 - (scr_coord_val)(xoff + 1); // left if (sig_dir & ribi_t::north || sig_dir & ribi_t::east) { simgraph16_draw_vline_clipped(x - xoff - width*2, y + (scr_coord_val)((xoff+1)/2), h, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x - xoff - width*2, y + (scr_coord_val)((xoff+1)/2)+h, thickness, col1_dark, true CLIP_NUM_DEFAULT); } // right if (sig_dir & ribi_t::south || sig_dir & ribi_t::west) { simgraph16_draw_vline_clipped(x + xoff + width*2, y + (scr_coord_val)((xoff+1)/2), h, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x + xoff + width*2, y + (scr_coord_val)((xoff+1)/2)+h, thickness, col1_dark, true CLIP_NUM_DEFAULT); } } } } else { if (sig_dir & ribi_t::south) { // upper right scr_coord_val slope_offset_y = corner_se( slope )*TILE_HEIGHT_STEP; for (uint8 xoff = 0; xoff < width; xoff++) { simgraph16_draw_vline_clipped( x + xoff, y - slope_offset_y, (scr_coord_val)(xoff/2) + 1, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped( x + xoff, y - slope_offset_y + (scr_coord_val)(xoff/2) + 1, thickness, col1_dark, true CLIP_NUM_DEFAULT); } } if (sig_dir & ribi_t::east) { scr_coord_val slope_offset_y = corner_se( slope )*TILE_HEIGHT_STEP; for (uint8 xoff = 0; xoff < width; xoff++) { simgraph16_draw_vline_clipped(x - xoff - 1, y - slope_offset_y, (scr_coord_val)(xoff/2) + 1, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x - xoff - 1, y - slope_offset_y + (scr_coord_val)(xoff/2) + 1, thickness, col1_dark, true CLIP_NUM_DEFAULT); } } if (sig_dir & ribi_t::west) { scr_coord_val slope_offset_y = corner_nw( slope )*TILE_HEIGHT_STEP; for (uint8 xoff = 0; xoff < width; xoff++) { simgraph16_draw_vline_clipped(x + xoff, y - slope_offset_y + height*2 - (scr_coord_val)(xoff/2) + 1, (scr_coord_val)(xoff/2) + 1, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x + xoff, y - slope_offset_y + height*2 + 1, thickness, col1_dark, true CLIP_NUM_DEFAULT); } } if (sig_dir & ribi_t::north) { scr_coord_val slope_offset_y = corner_nw( slope )*TILE_HEIGHT_STEP; for (uint8 xoff = 0; xoff < width; xoff++) { simgraph16_draw_vline_clipped(x - xoff - 1, y - slope_offset_y + height*2 - (scr_coord_val)(xoff/2) + 1, (scr_coord_val)(xoff/2) + 1, col1, true CLIP_NUM_DEFAULT); simgraph16_draw_vline_clipped(x - xoff - 1, y - slope_offset_y + height*2 + 1, thickness, col1_dark, true CLIP_NUM_DEFAULT); } } } } /** * Print a bezier curve between points A and B * @param Ax,Ay start coordinate of Bezier curve * @param Bx,By end coordinate of Bezier curve * @param ADx,ADy vector for start direction of curve * @param BDx,BDy vector for end direction of Bezier curve * @param colore color for curve to be drawn * @param draw for dotted lines, how many pixels to be drawn (leave 0 for solid line) * @param dontDraw for dotted lines, how many pixels to not be drawn (leave 0 for solid line) */ static void simgraph16_draw_bezier(scr_coord_val Ax, scr_coord_val Ay, scr_coord_val Bx, scr_coord_val By, scr_coord_val ADx, scr_coord_val ADy, scr_coord_val BDx, scr_coord_val BDy, const PIXVAL colore, scr_coord_val draw, scr_coord_val dontDraw) { scr_coord_val Cx,Cy,Dx,Dy; Cx = Ax + ADx; Cy = Ay + ADy; Dx = Bx + BDx; Dy = By + BDy; /* float a,b,rx,ry,oldx,oldy; for (float t=0.0;t<=1;t+=0.05) { a = t; b = 1.0 - t; if (t>0.0) { oldx=rx; oldy=ry; } rx = Ax*b*b*b + 3*Cx*b*b*a + 3*Dx*b*a*a + Bx*a*a*a; ry = Ay*b*b*b + 3*Cy*b*b*a + 3*Dy*b*a*a + By*a*a*a; if (t>0.0) if (!draw && !dontDraw) simgraph16_draw_line(rx,ry,oldx,oldy,colore); else display_direct_line_dotted_rgb(rx,ry,oldx,oldy,draw,dontDraw,colore); } */ sint32 rx = Ax*32*32*32; // init with a=0, b=32 sint32 ry = Ay*32*32*32; // init with a=0, b=32 // fixed point: we cycle between 0 and 32, rather than 0 and 1 for( sint32 a=1; a<=32; a++ ) { const sint32 b = 32 - a; const sint32 oldx = rx; const sint32 oldy = ry; rx = Ax*b*b*b + 3*Cx*b*b*a + 3*Dx*b*a*a + Bx*a*a*a; ry = Ay*b*b*b + 3*Cy*b*b*a + 3*Dy*b*a*a + By*a*a*a; // fixed point: due to cycling between 0 and 32 (1<<5), we divide by 32^3 == 1<<15 because of cubic interpolation if( !draw && !dontDraw ) { gfx->draw_line( rx>>15, ry>>15, oldx>>15, oldy>>15, colore ); } else { gfx->draw_line_dotted( rx>>15, ry>>15, oldx>>15, oldy>>15, draw, dontDraw, colore ); } } } // Only right facing at the moment static void simgraph16_draw_right_triangle(scr_coord_val x, scr_coord_val y, scr_coord_val height, const PIXVAL colval, const bool dirty) { y += (height / 2); while( height > 0 ) { display_vline_wh_rgb( x, y-(height/2), height, colval, dirty ); x++; height -= 2; } } // ------------------- other support routines that actually interface with the OS ----------------- /// Returns the index of the least significant set bit of a number, e.g. returns 2 for @p val == 12. /// Returns 0 for @p val == 0. static inline uint32 get_lowest_set_bit(uint32 val) { static const uint8 MultiplyDeBruijnBitPosition[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; return MultiplyDeBruijnBitPosition[(((val & -val) * 0x077CB531U)) >> 27]; } /** * copies only the changed areas to the screen using the "tile dirty buffer" * To get large changes, actually the current and the previous one is used. */ static void simgraph16_flush_framebuffer() { #ifdef USE_SOFTPOINTER ex_ord_update_mx_my(); const scr_coord_val ticker_ypos_bottom = display_get_height() - win_get_statusbar_height() - (env_t::menupos == MENU_BOTTOM) * env_t::iconsize.h; const scr_coord_val ticker_ypos_top = ticker_ypos_bottom - TICKER_HEIGHT; // use mouse pointer image if available if (softpointer != -1 && standard_pointer >= 0) { simgraph16_draw_color_img(standard_pointer, sys_event.mx, sys_event.my, 0, false, true CLIP_NUM_DEFAULT); // if software emulated mouse pointer is over the ticker, redraw it totally at next occurs if (!ticker::empty() && sys_event.my+images[standard_pointer].h >= ticker_ypos_top && sys_event.my <= ticker_ypos_bottom) { ticker::set_redraw_all(true); } } // no pointer image available, draw a crosshair else { display_fb_internal(sys_event.mx - 1, sys_event.my - 3, 3, 7, gfx->palette_lookup(COL_WHITE), true, 0, disp_width, 0, disp_height); display_fb_internal(sys_event.mx - 3, sys_event.my - 1, 7, 3, gfx->palette_lookup(COL_WHITE), true, 0, disp_width, 0, disp_height); simgraph16_draw_line( sys_event.mx-2, sys_event.my, sys_event.mx+2, sys_event.my, gfx->palette_lookup(COL_BLACK) ); display_direct_line_rgb( sys_event.mx, sys_event.my-2, sys_event.mx, sys_event.my+2, gfx->palette_lookup(COL_BLACK) ); // if crosshair is over the ticker, redraw it totally at next occurs if(!ticker::empty() && sys_event.my+2 >= ticker_ypos_top && sys_event.my-2 <= ticker_ypos_bottom) { ticker::set_redraw_all(true); } } old_my = sys_event.my; #endif // combine current with last dirty tiles for( int i = 0; i < tile_buffer_length; i++ ) { tile_dirty_old[i] |= tile_dirty[i]; } const int tile_words_per_line = tile_buffer_per_line >> 5; ALLOCA( uint32, masks, tile_words_per_line ); for( int x1 = 0; x1 < tiles_per_line; x1++ ) { const uint32 x_search_mask = 1 << (x1 & 31); // bit mask for finding bit x set int y1 = 0; do { const int word_max = (0 + (y1 + 1) * tile_buffer_per_line) >> 5; // first word on next line. limit search to < max const int word_x1 = (x1 + y1 * tile_buffer_per_line) >> 5; if( (tile_dirty_old[word_x1] & x_search_mask) == x_search_mask ) { // found dirty tile at x1, now find contiguous block of dirties - x2 const uint32 testval = ~((~(0xFFFFFFFF << (x1 & 31))) | tile_dirty_old[word_x1]); int word_x2 = word_x1; int x2; if( testval == 0 ) { // dirty block spans words masks[0] = tile_dirty_old[word_x1]; word_x2++; while( word_x2 < word_max && tile_dirty_old[word_x2] == 0xFFFFFFFF ) { masks[word_x2 - word_x1] = 0xFFFFFFFF; // dirty block spans this entire word word_x2++; } if( word_x2 >= word_max // dirty tiles extend all the way to screen edge || !(tile_dirty_old[word_x2] & 1) ) { // dirty block actually ended on the word edge x2 = 32; // set to whole word word_x2--; // masks already set in while loop above } else { // dirty block ends in word_x2 x2 = get_lowest_set_bit(~tile_dirty_old[word_x2]); masks[word_x2-word_x1] = 0xFFFFFFFF >> (32 - x2); } } else { // dirty block is all within one word - word_x1 x2 = get_lowest_set_bit(testval); masks[0] = (0xFFFFFFFF << (32 - x2 + (x1 & 31))) >> (32 - x2); } for( int i = word_x1; i <= word_x2; i++ ) { // clear dirty tile_dirty_old[i] &= ~masks[i - word_x1]; } // x2 from bit index to tile coords x2 += (x1 & ~31) + ((word_x2 - word_x1) << 5); // find how many rows can be combined into one rectangle int y2 = y1 + 1; bool xmatch = true; while( y2 < tile_lines && xmatch ) { const int li = (x1 + y2 * tile_buffer_per_line) >> 5; const int ri = li + word_x2 - word_x1; for( int i = li; i <= ri; i++ ) { if( (tile_dirty_old[i] & masks[i - li]) != masks[i - li] ) { xmatch = false; break; } } if( xmatch ) { for( int i = li; i <= ri; i++ ) { // clear dirty tile_dirty_old[i] &= ~masks[i - li]; } y2++; } } #ifdef DEBUG_FLUSH_BUFFER display_vline_wh_rgb( (x1 << DIRTY_TILE_SHIFT) - 1, y1 << DIRTY_TILE_SHIFT, (y2 - y1) << DIRTY_TILE_SHIFT, gfx->palette_lookup(COL_YELLOW), false); display_vline_wh_rgb( x2 << DIRTY_TILE_SHIFT, y1 << DIRTY_TILE_SHIFT, (y2 - y1) << DIRTY_TILE_SHIFT, gfx->palette_lookup(COL_YELLOW), false); display_fillbox_wh_rgb( x1 << DIRTY_TILE_SHIFT, y1 << DIRTY_TILE_SHIFT, (x2 - x1) << DIRTY_TILE_SHIFT, 1, gfx->palette_lookup(COL_YELLOW), false); display_fillbox_wh_rgb( x1 << DIRTY_TILE_SHIFT, (y2 << DIRTY_TILE_SHIFT) - 1, (x2 - x1) << DIRTY_TILE_SHIFT, 1, gfx->palette_lookup(COL_YELLOW), false); simgraph16_draw_line( x1 << DIRTY_TILE_SHIFT, y1 << DIRTY_TILE_SHIFT, x2 << DIRTY_TILE_SHIFT, (y2 << DIRTY_TILE_SHIFT) - 1, gfx->palette_lookup(COL_YELLOW) ); simgraph16_draw_line( x1 << DIRTY_TILE_SHIFT, (y2 << DIRTY_TILE_SHIFT) - 1, x2 << DIRTY_TILE_SHIFT, y1 << DIRTY_TILE_SHIFT, gfx->palette_lookup(COL_YELLOW) ); #else dr_textur( x1 << DIRTY_TILE_SHIFT, y1 << DIRTY_TILE_SHIFT, (x2 - x1) << DIRTY_TILE_SHIFT, (y2 - y1) << DIRTY_TILE_SHIFT ); #endif y1 = y2; // continue search from bottom of found rectangle } else { y1++; } } while( y1 < tile_lines ); } #ifdef DEBUG_FLUSH_BUFFER dr_textur(0, 0, disp_actual_width, disp_height ); #endif // swap tile buffers uint32 *tmp = tile_dirty_old; tile_dirty_old = tile_dirty; tile_dirty = tmp; // _old was cleared to 0 in above loops } /** * Turn mouse pointer visible/invisible */ static void simgraph16_set_cursor_visible(bool show) { #ifdef USE_SOFTPOINTER softpointer = show; #else show_pointer(show); #endif } /** * mouse pointer image */ static void simgraph16_set_default_cursor(int cursor_id) { standard_pointer = cursor_id; } /** * mouse pointer image */ static void simgraph16_set_show_load_cursor(bool show) { #ifdef USE_SOFTPOINTER softpointer = !show; #else set_pointer(show); #endif } /** * Initialises the graphics module */ static bool simgraph16_init(scr_size window_size, sint16 full_screen) { disp_actual_width = window_size.w; disp_height = window_size.h; #ifdef MULTI_THREAD pthread_mutex_init( &recode_img_mutex, NULL ); #endif // init rezoom_img() for( int i = 0; i < MAX_THREADS; i++ ) { #ifdef MULTI_THREAD pthread_mutex_init( &rezoom_img_mutex[i], NULL ); #endif rezoom_baseimage[i] = NULL; rezoom_baseimage2[i] = NULL; rezoom_size[i] = 0; } // get real width from os-dependent routines disp_width = dr_os_open(window_size, full_screen); if( disp_width<=0 ) { dr_fatal_notify( "Cannot open window!" ); return false; } textur = dr_textur_init(); // init, load, and check fonts if (!gfx->load_font(env_t::fontname.c_str(), false)) { env_t::fontname = dr_get_system_font(); if (!gfx->load_font(env_t::fontname.c_str(), false)) { env_t::fontname = FONT_PATH_X "cyr.bdf"; if (!gfx->load_font(env_t::fontname.c_str(), false)) { dr_fatal_notify("No fonts found!"); return false; } } } // allocate dirty tile flags tiles_per_line = (disp_width + DIRTY_TILE_SIZE - 1) / DIRTY_TILE_SIZE; tile_buffer_per_line = (tiles_per_line + 31) & ~31; tile_lines = (disp_height + DIRTY_TILE_SIZE - 1) / DIRTY_TILE_SIZE; tile_buffer_length = (tile_lines * tile_buffer_per_line / 32); tile_dirty = MALLOCN( uint32, tile_buffer_length ); tile_dirty_old = MALLOCN( uint32, tile_buffer_length ); simgraph16_mark_screen_dirty(); MEMZERON( tile_dirty_old, tile_buffer_length ); // init player colors for( int i = 0; i < MAX_PLAYER_COUNT; i++ ) { player_offsets[i][0] = i*8; player_offsets[i][1] = i*8+24; } simgraph16_set_clip_rect(0, 0, disp_width, disp_height CLIP_NUM_DEFAULT, false); // Calculate daylight rgbmap and save it for unshaded tile drawing player_day = 0; simgraph16_set_daynight_level(0); memcpy(specialcolormap_all_day, specialcolormap_day_night, 256 * sizeof(PIXVAL)); memcpy(rgbmap_all_day, rgbmap_day_night, RGBMAPSIZE * sizeof(PIXVAL)); // find out bit depth { PIXVAL c = get_system_color({ 0, 0xFF, 0 }); while ((c&1)==0) { c >>= 1; } if(c==0x1F) { // 5 bits for green channel -> 15 bits per pixel) #ifndef RGB555 dr_fatal_notify( "Compiled for 16 bit color depth but using 15!" ); #endif } else { #ifdef RGB555 dr_fatal_notify( "Compiled for 15 bit color depth but using 16!" ); #endif } } return true; } /** * Check if the graphic module already was initialized. */ static bool simgraph16_is_display_init() { return textur != NULL && default_font.is_loaded() && images!=NULL; } /** * Close the Graphic module */ static void simgraph16_exit() { dr_os_close(); free( tile_dirty_old ); free( tile_dirty ); gfx->free_all_images_above(0); free(images); tile_dirty = tile_dirty_old = NULL; images = NULL; #ifdef MULTI_THREAD pthread_mutex_destroy( &recode_img_mutex ); for( int i = 0; i < MAX_THREADS; i++ ) { pthread_mutex_destroy( &rezoom_img_mutex[i] ); } #endif } /* changes display size */ static void simgraph16_on_window_resized(scr_size new_window_size) { disp_actual_width = max( 16, new_window_size.w ); if( new_window_size.h<=0 ) { new_window_size.h = 64; } // only resize, if internal values are different if (disp_width != new_window_size.w || disp_height != new_window_size.h) { scr_coord_val new_pitch = dr_textur_resize(&textur, new_window_size.w, new_window_size.h); if( new_pitch!=disp_width || disp_height != new_window_size.h) { disp_width = new_pitch; disp_height = new_window_size.h; free( tile_dirty_old ); free( tile_dirty); // allocate dirty tile flags tiles_per_line = (disp_width + DIRTY_TILE_SIZE - 1) / DIRTY_TILE_SIZE; tile_buffer_per_line = (tiles_per_line + 31) & ~31; tile_lines = (disp_height + DIRTY_TILE_SIZE - 1) / DIRTY_TILE_SIZE; tile_buffer_length = (tile_lines * tile_buffer_per_line / 32); tile_dirty = MALLOCN( uint32, tile_buffer_length ); tile_dirty_old = MALLOCN( uint32, tile_buffer_length ); gfx->set_clip_rect(0, 0, disp_actual_width, disp_height CLIP_NUM_DEFAULT, false); } simgraph16_mark_screen_dirty(); MEMZERON( tile_dirty_old, tile_buffer_length ); } } /** * Take Screenshot */ static bool simgraph16_take_screenshot(const scr_rect &area) { if (access(SCREENSHOT_PATH_X, W_OK) == -1) { return false; // directory not accessible } static int number = 0; char filename[80]; // find the first not used screenshot image do { sprintf(filename, SCREENSHOT_PATH_X "simscr%02d.png", number++); } while (access(filename, W_OK) != -1); // now save the screenshot scr_rect clipped_area = area; clipped_area.clip(scr_rect(0, 0, disp_actual_width, disp_height)); raw_image_t img(clipped_area.w, clipped_area.h, raw_image_t::FMT_RGB888); for (scr_coord_val y = 0; y < clipped_area.h; ++y) { uint8 *dst = img.access_pixel(0, y); const PIXVAL *row = textur + (clipped_area.x + 0) + (clipped_area.y + y) * disp_width; for (scr_coord_val x = 0; x < clipped_area.w; ++x) { const rgb888_t pixel = pixval_to_rgb888(*row++); *dst++ = pixel.r; *dst++ = pixel.g; *dst++ = pixel.b; } } return img.write_png(filename); } static void simgraph16_set_image_procs(bool is_global) { if( is_global ) { g_simgraph16.draw_normal = simgraph16_draw_img_aux;; g_simgraph16.draw_color = simgraph16_draw_color_img; g_simgraph16.draw_blend = simgraph16_draw_rezoomed_img_blend; g_simgraph16.draw_alpha = simgraph16_draw_rezoomed_img_alpha; g_simgraph16.current_tile_raster_width = gfx->get_tile_raster_width(); } else { g_simgraph16.draw_normal = simgraph16_draw_base_img; g_simgraph16.draw_color = simgraph16_draw_base_img; g_simgraph16.draw_blend = simgraph16_draw_base_img_blend; g_simgraph16.draw_alpha = simgraph16_draw_base_img_alpha; g_simgraph16.current_tile_raster_width = gfx->get_base_tile_raster_width(); } } void simgraph16_set_light_color(int light_idx, rgb888_t day_colour, rgb888_t night_colour) { display_day_lights[light_idx] = day_colour; display_night_lights[light_idx] = night_colour; } simutrans-124.5/src/simutrans/display/simgraph16.h000066400000000000000000000003441520675460500222000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_SIMGRAPH16_H #define DISPLAY_SIMGRAPH16_H #include "simgraph.h" extern simgraph_t g_simgraph16; #endif simutrans-124.5/src/simutrans/display/simimg.h000066400000000000000000000004451520675460500215060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_SIMIMG_H #define DISPLAY_SIMIMG_H #include "../simtypes.h" /* * Defines to handle images */ typedef uint32 image_id; #define IMG_EMPTY ((image_id)0xFFFFFFFFu) #endif simutrans-124.5/src/simutrans/display/simview.cc000066400000000000000000000521671520675460500220520ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simworld.h" #include "simview.h" #include "simgraph.h" #include "viewport.h" #include "../simticker.h" #include "../simdebug.h" #include "../obj/simobj.h" #include "../simconst.h" #include "../world/simplan.h" #include "../tool/simmenu.h" #include "../player/simplay.h" #include "../descriptor/ground_desc.h" #include "../ground/wasser.h" #include "../dataobj/environment.h" #include "../obj/zeiger.h" #include "../utils/simrandom.h" uint16 win_get_statusbar_height(); // simwin.h main_view_t::main_view_t(karte_t *welt) { this->welt = welt; outside_visible = true; viewport = welt->get_viewport(); assert(welt && viewport); } #if COLOUR_DEPTH != 0 static const sint8 hours2night[] = { 4,4,4,4,4,4,4,4, 4,4,4,4,3,2,1,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 2,3,4,4,4,4,4,4 }; #endif #ifdef MULTI_THREAD #include "../utils/simthread.h" bool spawned_threads=false; // global job indicator array static simthread_barrier_t display_barrier_start; static simthread_barrier_t display_barrier_end; // to start a thread typedef struct{ main_view_t *show_routine; koord lt_cl, wh_cl; // pos/size of clipping rect for this thread koord lt, wh; // pos/size of region to display. set larger than clipping for correct display of trees at thread seams sint16 y_min; sint16 y_max; sint8 thread_num; } display_region_param_t; // now the parameters static display_region_param_t ka[MAX_THREADS]; void *display_region_thread( void *ptr ) { display_region_param_t *view = reinterpret_cast(ptr); while(true) { simthread_barrier_wait( &display_barrier_start ); // wait for all to start gfx->clear_all_poly_clip( view->thread_num ); gfx->set_clip_rect( view->lt_cl.x, view->lt_cl.y, view->wh_cl.x, view->wh_cl.y, view->thread_num, false); view->show_routine->display_region( view->lt, view->wh, view->y_min, view->y_max, false, true, view->thread_num ); simthread_barrier_wait( &display_barrier_end ); // wait for all to finish } } /* The following mutex is only needed for smart cursor */ // mutex for changing settings on hiding buildings/trees static pthread_mutex_t hide_mutex = PTHREAD_MUTEX_INITIALIZER; static bool threads_req_pause = false; // set true to pause all threads to display smartcursor region single threaded static uint8 num_threads_paused = 0; // number of threads in the paused state static pthread_cond_t hiding_cond = PTHREAD_COND_INITIALIZER; static pthread_cond_t waiting_cond = PTHREAD_COND_INITIALIZER; #if COLOUR_DEPTH != 0 static bool can_multithreading = true; #endif #endif void main_view_t::display(bool force_dirty) { const uint32 rs = get_random_seed(); #if COLOUR_DEPTH != 0 DBG_DEBUG4("main_view_t::display", "starting ..."); gfx->set_image_procs(true); const scr_size screen = gfx->get_screen_size(); const sint16 IMG_SIZE = gfx->get_tile_raster_width(); const sint16 disp_height = screen.h - win_get_statusbar_height() - (!ticker::empty() ? TICKER_HEIGHT : 0); scr_rect clip_rr(0, env_t::iconsize.w, screen.w, disp_height - env_t::iconsize.h); switch (env_t::menupos) { case MENU_TOP: // rect default break; case MENU_BOTTOM: clip_rr.y = win_get_statusbar_height() + (!ticker::empty() ? TICKER_HEIGHT : 0); break; case MENU_LEFT: clip_rr = scr_rect(env_t::iconsize.w, 0, screen.w - env_t::iconsize.w, disp_height); break; case MENU_RIGHT: clip_rr = scr_rect(0, 0, screen.w - env_t::iconsize.w, disp_height); break; } gfx->set_clip_rect(clip_rr.x, clip_rr.y, clip_rr.w, clip_rr.h CLIP_NUM_DEFAULT, false); // redraw everything? force_dirty = force_dirty || welt->is_dirty(); welt->unset_dirty(); if( force_dirty ) { gfx->mark_screen_dirty(); welt->set_background_dirty(); force_dirty = false; } const int dpy_width = screen.w / IMG_SIZE + 2; const int dpy_height = (screen.h*4)/IMG_SIZE; const int i_off = viewport->get_world_position().x + viewport->get_viewport_ij_offset().x; const int j_off = viewport->get_world_position().y + viewport->get_viewport_ij_offset().y; const int const_x_off = viewport->get_x_off(); const int const_y_off = viewport->get_y_off(); // change to night mode? // images will be recalculated only, when there has been a change, so we set always if(grund_t::underground_mode == grund_t::ugm_all) { gfx->set_daynight_level(0); } else if(!env_t::night_shift) { gfx->set_daynight_level(env_t::daynight_level); } else { // calculate also days if desired uint32 month = welt->get_last_month(); const uint32 ticks_this_month = welt->get_ticks() % welt->ticks_per_world_month; uint32 hours2; if (env_t::show_month > env_t::DATE_FMT_MONTH) { static sint32 days_per_month[12]={31,28,31,30,31,30,31,31,30,31,30,31}; hours2 = (((sint64)ticks_this_month*days_per_month[month]) >> (welt->ticks_per_world_month_shift-17)); hours2 = ((hours2*3) / 8192) % 48; } else { hours2 = ( (ticks_this_month * 3) >> (welt->ticks_per_world_month_shift-4) )%48; } gfx->set_daynight_level(hours2night[hours2]+env_t::daynight_level); } // not very elegant, but works: // fill everything with black for Underground mode ... if( grund_t::underground_mode ) { gfx->draw_rect(clip_rr.x, clip_rr.y, clip_rr.w, clip_rr.h, gfx->palette_lookup(COL_BLACK), force_dirty); } else if( welt->is_background_dirty() && outside_visible ) { // we check if background will be visible, no need to clear screen if it's not. display_background(clip_rr.x, clip_rr.y, clip_rr.w, clip_rr.h, force_dirty); welt->unset_background_dirty(); // reset outside_visible = false; } // to save calls to grund_t::get_disp_height // gr->get_disp_height() == min(gr->get_hoehe(), hmax_ground) const sint8 hmax_ground = (grund_t::underground_mode==grund_t::ugm_level) ? grund_t::underground_level : 127; // lower limit for y: display correctly water/outside graphics at upper border of screen int y_min = (-const_y_off + 4*tile_raster_scale_y( min(hmax_ground, welt->min_height)*TILE_HEIGHT_STEP, IMG_SIZE ) + 4*(clip_rr.y-IMG_SIZE)-IMG_SIZE/2-1) / IMG_SIZE; // prepare view rect_t const world_rect(koord(0, 0), welt->get_size()); koord const estimated_min(((y_min+(-2-((y_min+dpy_width) & 1))) >> 1) + i_off, ((y_min-(clip_rr.w - const_x_off) / (IMG_SIZE/2) - 1) >> 1) + j_off); sint16 const worst_case_mountain_extra = (welt->max_height - welt->min_height) / 2; koord const estimated_max((((dpy_height+4*4)+(screen.w - const_x_off) / (IMG_SIZE/2) - 1) >> 1) + i_off + worst_case_mountain_extra, (((dpy_height+4*4)-(-2-(((dpy_height+4*4)+dpy_width) & 1))) >> 1) + j_off + worst_case_mountain_extra); rect_t view_rect(estimated_min, estimated_max - estimated_min + koord(1, 1)); view_rect.mask(world_rect); if (view_rect != viewport->prepared_rect) { welt->prepare_tiles(view_rect, viewport->prepared_rect); viewport->prepared_rect = view_rect; } #ifdef MULTI_THREAD if( can_multithreading ) { if( !spawned_threads ) { // we can do the parallel display using posix threads ... pthread_t thread[MAX_THREADS]; /* Initialize and set thread detached attribute */ pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); // init barrier simthread_barrier_init( &display_barrier_start, NULL, env_t::num_threads ); simthread_barrier_init( &display_barrier_end, NULL, env_t::num_threads ); for( int t = 0; t < env_t::num_threads - 1; t++ ) { if( pthread_create( &thread[t], &attr, display_region_thread, (void *)&ka[t] ) ) { can_multithreading = false; dbg->error( "main_view_t::display()", "cannot multi-thread, error at thread #%i", t+1 ); return; } } spawned_threads = true; pthread_attr_destroy( &attr ); } // set parameter for each thread const scr_coord_val wh_x = clip_rr.w / env_t::num_threads; scr_coord_val lt_x = clip_rr.x; for( int t = 0; t < env_t::num_threads - 1; t++ ) { ka[t].show_routine = this; ka[t].lt_cl = koord( lt_x, clip_rr.y ); ka[t].wh_cl = koord( wh_x, clip_rr.h ); ka[t].lt = ka[t].lt_cl - koord( IMG_SIZE/2, 0 ); // process tiles IMG_SIZE/2 outside clipping range for correct tree display at thread seams ka[t].wh = ka[t].wh_cl + koord( IMG_SIZE, 0 ); ka[t].y_min = y_min; ka[t].y_max = dpy_height + 4 * 4; ka[t].thread_num = t; lt_x += wh_x; } // init variables required to draw smart cursor threads_req_pause = false; num_threads_paused = 0; // and start drawing simthread_barrier_wait( &display_barrier_start ); // the last we can run ourselves, setting clip_wh to the screen edge instead of wh_x (in case disp_width % num_threads != 0) gfx->clear_all_poly_clip( env_t::num_threads - 1 ); gfx->set_clip_rect( lt_x, clip_rr.y, clip_rr.w, clip_rr.h, env_t::num_threads - 1, false); display_region( koord( lt_x - IMG_SIZE / 2, clip_rr.y ), koord( clip_rr.x + clip_rr.w + IMG_SIZE, clip_rr.h ), y_min, dpy_height + 4 * 4, false, true, env_t::num_threads - 1 ); simthread_barrier_wait( &display_barrier_end ); gfx->clear_all_poly_clip( CLIP_NUM_DEFAULT_VALUE ); gfx->set_clip_rect(clip_rr.x, clip_rr.y, clip_rr.w, clip_rr.h CLIP_NUM_DEFAULT, false); } else { // slow serial way of display gfx->clear_all_poly_clip( CLIP_NUM_DEFAULT_VALUE ); display_region( koord(clip_rr.x, clip_rr.y), koord(clip_rr.w, clip_rr.h), y_min, dpy_height + 4 * 4, false, false, 0 ); } #else gfx->clear_all_poly_clip(); display_region(koord(clip_rr.x, clip_rr.y), koord(clip_rr.w, clip_rr.h), y_min, dpy_height + 4 * 4, false ); #endif // and finally overlays (station coverage and signs) bool plotted = false; // display overlays even on very large mountains for(sint16 y=y_min; y> 1) + i_off; const sint16 j = ((y - x) >> 1) + j_off; const sint16 xpos = x * (IMG_SIZE / 2) + const_x_off; if( xpos+IMG_SIZE>0 ) { const planquadrat_t *plan=welt->access(i,j); if(plan && plan->get_kartenboden()) { const grund_t *gr = plan->get_kartenboden(); sint16 yypos = ypos - tile_raster_scale_y( min(gr->get_hoehe(),hmax_ground)*TILE_HEIGHT_STEP, IMG_SIZE); if( yypos-IMG_SIZE < clip_rr.get_bottom() && yypos+IMG_SIZE>=clip_rr.y ) { plan->display_overlay( xpos, yypos ); plotted = true; } } } } } obj_t *zeiger = welt->get_zeiger(); DBG_DEBUG4("main_view_t::display", "display pointer"); if( zeiger && zeiger->get_pos() != koord3d::invalid ) { bool dirty = zeiger->get_flag(obj_t::dirty); scr_coord background_pos = viewport->get_screen_coord(zeiger->get_pos()); scr_coord pointer_pos = background_pos + viewport->scale_offset(koord(zeiger->get_xoff(),zeiger->get_yoff())); // mark the cursor position for all tools (except lower/raise) if(zeiger->get_yoff()==Z_PLAN) { grund_t *gr = welt->lookup( zeiger->get_pos() ); if(gr && gr->is_visible()) { const FLAGGED_PIXVAL transparent = TRANSPARENT25_FLAG|OUTLINE_FLAG| env_t::cursor_overlay_color; if( gr->get_image()==IMG_EMPTY ) { if( gr->hat_wege() ) { gfx->draw_img_blend( gr->obj_bei(0)->get_image(), background_pos.x, background_pos.y, transparent, 0, dirty ); } else { gfx->draw_img_blend( ground_desc_t::get_ground_tile(gr), background_pos.x, background_pos.y, transparent, 0, dirty ); } } else if( gr->get_typ()==grund_t::wasser ) { gfx->draw_img_blend( ground_desc_t::sea->get_image(gr->get_image(),wasser_t::stage), background_pos.x, background_pos.y, transparent, 0, dirty ); } else { gfx->draw_img_blend( gr->get_image(), background_pos.x, background_pos.y, transparent, 0, dirty ); } } } zeiger->display( pointer_pos.x , pointer_pos.y CLIP_NUM_DEFAULT); zeiger->clear_flag( obj_t::dirty ); } if(welt) { // show players income/cost messages switch (env_t::show_money_message) { case 0: // show messages of all players for(int x=0; xget_player(x) ) { welt->get_player(x)->display_messages(); } } break; case 1: // show message of active player if (welt->get_active_player()) { welt->get_active_player()->display_messages(); } break; default: // no messages break; } } assert( rs == get_random_seed() ); (void)rs; #else (void)force_dirty; (void)rs; #endif } void main_view_t::clear_prepared() const { viewport->prepared_rect.discard_area(); } #ifdef MULTI_THREAD void main_view_t::display_region( koord lt, koord wh, sint16 y_min, sint16 y_max, bool /*force_dirty*/, bool threaded, const sint8 clip_num ) #else void main_view_t::display_region( koord lt, koord wh, sint16 y_min, sint16 y_max, bool /*force_dirty*/ ) #endif { const sint16 IMG_SIZE = gfx->get_tile_raster_width(); const int i_off = viewport->get_world_position().x + viewport->get_viewport_ij_offset().x; const int j_off = viewport->get_world_position().y + viewport->get_viewport_ij_offset().y; const int const_x_off = viewport->get_x_off(); const int const_y_off = viewport->get_y_off(); const scr_size screen = gfx->get_screen_size(); const int dpy_width = screen.w / IMG_SIZE + 2; // to save calls to grund_t::get_disp_height const sint8 hmax_ground = (grund_t::underground_mode == grund_t::ugm_level) ? grund_t::underground_level : 127; // prepare for selectively display const koord cursor_pos = welt->get_zeiger() ? welt->get_zeiger()->get_pos().get_2d() : koord(-1000, -1000); const bool needs_hiding = !env_t::hide_trees || (env_t::hide_buildings != env_t::ALL_HIDDEN_BUILDING); for( int y = y_min; y < y_max; y++ ) { const sint16 ypos = y * (IMG_SIZE / 4) + const_y_off; // plotted = we plotted something bool plotted = false; for( sint16 x = -2 - ((y +dpy_width) & 1); (x * (IMG_SIZE / 2) + const_x_off) < (lt.x + wh.x); x += 2 ) { const sint16 i = ((y + x) >> 1) + i_off; const sint16 j = ((y - x) >> 1) + j_off; const sint16 xpos = x * (IMG_SIZE / 2) + const_x_off; if( xpos + IMG_SIZE > lt.x ) { const koord pos(i, j); if( grund_t* const kb = welt->lookup_kartenboden(pos) ) { const sint16 yypos = ypos - tile_raster_scale_y( min( kb->get_hoehe(), hmax_ground ) * TILE_HEIGHT_STEP, IMG_SIZE ); if( yypos - IMG_SIZE < lt.y + wh.y && yypos + IMG_SIZE > lt.y ) { #ifdef MULTI_THREAD bool force_show_grid = false; if( env_t::hide_under_cursor ) { const uint32 cursor_dist = shortest_distance( pos, cursor_pos ); if( cursor_dist <= env_t::cursor_hide_range + 2u ) { // +2 to allow for rapid diagonal movement kb->set_flag( grund_t::dirty ); if( cursor_dist <= env_t::cursor_hide_range ) { force_show_grid = true; } } } kb->display_if_visible( xpos, yypos, IMG_SIZE, clip_num, force_show_grid ); #else if( env_t::hide_under_cursor ) { const bool saved_grid = grund_t::show_grid; const uint32 cursor_dist = shortest_distance( pos, cursor_pos ); if( cursor_dist <= env_t::cursor_hide_range + 2u ) { kb->set_flag( grund_t::dirty ); if( cursor_dist <= env_t::cursor_hide_range ) { grund_t::show_grid = true; } } kb->display_if_visible( xpos, yypos, IMG_SIZE ); grund_t::show_grid = saved_grid; } else { kb->display_if_visible( xpos, yypos, IMG_SIZE ); } #endif plotted = true; } // not on screen? We still might need to plot the border ... else if( env_t::draw_earth_border && (pos.x-welt->get_size().x+1 == 0 || pos.y-welt->get_size().y+1 == 0) ) { kb->display_border( xpos, yypos, IMG_SIZE CLIP_NUM_PAR); } } else { // check if outside visible outside_visible = true; if( env_t::draw_outside_tile ) { const sint16 yypos = ypos - tile_raster_scale_y( welt->min_height * TILE_HEIGHT_STEP, IMG_SIZE ); gfx->draw_normal( ground_desc_t::outside->get_image(0), xpos, yypos, 0, true, false CLIP_NUM_PAR); } } } } // increase lower bound if nothing is visible if( !plotted ) { if (y == y_min) { y_min++; } } // increase upper bound if something is visible else { if (y == y_max-1) { y_max++; } } } // and then things (and other ground) // especially necessary for vehicles for( int y = y_min; y < y_max; y++ ) { const sint16 ypos = y * (IMG_SIZE / 4) + const_y_off; for( sint16 x = -2 - ((y + dpy_width) & 1); (x * (IMG_SIZE / 2) + const_x_off) < (lt.x + wh.x); x += 2 ) { const int i = ((y + x) >> 1) + i_off; const int j = ((y - x) >> 1) + j_off; const int xpos = x * (IMG_SIZE / 2) + const_x_off; if( xpos + IMG_SIZE > lt.x ) { const planquadrat_t *plan = welt->access(i,j); if( plan && plan->get_kartenboden() ) { const grund_t *gr = plan->get_kartenboden(); // minimum height: ground height for overground, // for the definition of underground_level see grund_t::set_underground_mode const sint8 hmin = min( gr->get_hoehe(), grund_t::underground_level ); // maximum height: 127 for overground, underground level for sliced, ground height-1 for complete underground view const sint8 hmax = grund_t::underground_mode == grund_t::ugm_all ? gr->get_hoehe() - (!gr->ist_tunnel()) : grund_t::underground_level; /* long version switch(grund_t::underground_mode) { case ugm_all: hmin = -128; hmax = gr->get_hoehe()-(!gr->ist_tunnel()); underground_level = -128; break; case ugm_level: hmin = min(gr->get_hoehe(), underground_level); hmax = underground_level; underground_level = level; break; case ugm_none: hmin = gr->get_hoehe(); hmax = 127; underground_level = 127; } */ sint16 yypos = ypos - tile_raster_scale_y( min( gr->get_hoehe(), hmax_ground ) * TILE_HEIGHT_STEP, IMG_SIZE ); if( yypos - IMG_SIZE * 3 < wh.y + lt.y && yypos + IMG_SIZE > lt.y ) { const koord pos(i,j); if( env_t::hide_under_cursor && needs_hiding ) { // If the corresponding setting is on, then hide trees and buildings under mouse cursor #ifdef MULTI_THREAD if( threaded ) { pthread_mutex_lock( &hide_mutex ); if( threads_req_pause ) { // another thread is requesting we pause num_threads_paused++; pthread_cond_broadcast( &waiting_cond ); // signal the requesting thread that another thread has paused // wait until no longer requested to pause while( threads_req_pause ) { pthread_cond_wait( &hiding_cond, &hide_mutex ); } num_threads_paused--; } if( shortest_distance( pos, cursor_pos ) <= env_t::cursor_hide_range ) { // wait until all threads are paused threads_req_pause = true; while( num_threads_paused < env_t::num_threads - 1 ) { pthread_cond_wait( &waiting_cond, &hide_mutex ); } // proceed with drawing in the hidden area singlethreaded const bool saved_hide_trees = env_t::hide_trees; const uint8 saved_hide_buildings = env_t::hide_buildings; env_t::hide_trees = true; env_t::hide_buildings = env_t::ALL_HIDDEN_BUILDING; plan->display_obj( xpos, yypos, IMG_SIZE, true, hmin, hmax, clip_num ); env_t::hide_trees = saved_hide_trees; env_t::hide_buildings = saved_hide_buildings; // unpause all threads threads_req_pause = false; pthread_cond_broadcast( &hiding_cond ); pthread_mutex_unlock( &hide_mutex ); } else { // not in the hidden area, draw multithreaded pthread_mutex_unlock( &hide_mutex ); plan->display_obj( xpos, yypos, IMG_SIZE, true, hmin, hmax, clip_num ); } } else { #endif if( shortest_distance( pos, cursor_pos ) <= env_t::cursor_hide_range ) { const bool saved_hide_trees = env_t::hide_trees; const uint8 saved_hide_buildings = env_t::hide_buildings; env_t::hide_trees = true; env_t::hide_buildings = env_t::ALL_HIDDEN_BUILDING; plan->display_obj( xpos, yypos, IMG_SIZE, true, hmin, hmax CLIP_NUM_PAR); env_t::hide_trees = saved_hide_trees; env_t::hide_buildings = saved_hide_buildings; } else { plan->display_obj( xpos, yypos, IMG_SIZE, true, hmin, hmax CLIP_NUM_PAR); } #ifdef MULTI_THREAD } #endif } else { // hiding turned off, draw multithreaded plan->display_obj( xpos, yypos, IMG_SIZE, true, hmin, hmax CLIP_NUM_PAR); } } } } } } #ifdef MULTI_THREAD // show thread as paused when finished if( threaded ) { pthread_mutex_lock( &hide_mutex ); num_threads_paused++; pthread_cond_broadcast( &waiting_cond ); pthread_mutex_unlock( &hide_mutex ); } #endif } void main_view_t::display_background( scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, bool dirty ) { if( !(env_t::draw_earth_border && env_t::draw_outside_tile) ) { gfx->draw_rect(xp, yp, w, h, env_t::background_color, dirty ); } } simutrans-124.5/src/simutrans/display/simview.h000066400000000000000000000062101520675460500217000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_SIMVIEW_H #define DISPLAY_SIMVIEW_H #include "simgraph.h" class karte_t; class viewport_t; /** * World view class, it contains the routines that handle world display to the pixel buffer. * @brief View for the simulated world. */ class main_view_t { private: /// The simulated world this view is associated to. karte_t *welt; /// We cache the camera here, it's the camera what we are supposed to render, not the whole world. viewport_t *viewport; /// Cached value from last display run to determine if the background was visible, we'll save redraws if it was not. bool outside_visible; public: main_view_t(karte_t *welt); /** * Draws the visible world on screen. * @param dirty If set to true, will mark the whole screen as dirty. * @see display_flush_buffer() for the consequences of setting screen areas dirty. */ void display(bool dirty); /** * @brief Clears the prepared area of the view. * * Sets the prepared area of this view to no area. The entire view will have * to be reprepared on next display call. Intended when the entire world has * changed, eg switching underground mode or layer. */ void clear_prepared() const; /** * Draws the simulated world in the specified rectangular area of the pixel buffer. This is a internal function of the class. *
* See lt and wt as the absolute limit a pixel this routine can draw, a clipping rectangle. y_min and y_max are the coordinates this routine starts, * locating and drawing objects. This mechanic is due to the fact objects are drawn in relation of their top-left pixel, and that coordinate might be outside * the clipping rectangle. Think of water tiles on the top of screen of high buildings that fall over the bottom of screen, but so high that their top has to * remain visible. * * @param lt Top-left pixel coordinate of the rectangle. In pixels. * @param wh Width and height f the rectangle. In pixels. * @param y_min Minimum height of the screen (top pixel row) to start processing objects to draw. * @param y_max Maximum height of the screen (bottom pixel row) to start processing objects to draw. * @param force_dirty If set to true, will mark the whole rectangle as dirty. * @param threaded If set to true, indicates there are more threads drawing on screen, and this routine will use mutexes when needed. */ #ifdef MULTI_THREAD void display_region( koord lt, koord wh, sint16 y_min, const sint16 y_max, bool force_dirty, bool threaded, const sint8 clip_num ); #else void display_region( koord lt, koord wh, sint16 y_min, const sint16 y_max, bool force_dirty ); #endif private: /** * Draws background in the specified rectangular screen coordinates. * @param xp X screen coordinate of the left-top corner. * @param yp Y screen coordinate y of the left-top corner. * @param w Width of the rectangle to draw. * @param h Height of the rectangle to draw. * @param dirty Mark the specified area as dirty. */ void display_background( scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, bool dirty ); }; #endif simutrans-124.5/src/simutrans/display/viewport.cc000066400000000000000000000241541520675460500222410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "viewport.h" #include "simgraph.h" #include "../world/simworld.h" #include "../dataobj/environment.h" #include "../dataobj/koord3d.h" #include "../ground/grund.h" #include "../obj/zeiger.h" void viewport_t::set_viewport_ij_offset( const koord &k ) { view_ij_off=k; update_cached_values(); } koord viewport_t::get_map2d_coord( const koord3d &viewpos ) const { // just calculate the offset from the z-position const sint16 new_yoff = tile_raster_scale_y(viewpos.z*TILE_HEIGHT_STEP,cached_img_size); sint16 lines = 0; if(new_yoff>0) { lines = (new_yoff + (cached_img_size/4))/(cached_img_size/2); } else { lines = (new_yoff - (cached_img_size/4))/(cached_img_size/2); } return world->get_closest_coordinate( viewpos.get_2d() - koord( lines, lines ) ); } koord viewport_t::get_viewport_coord( const koord& coord ) const { return coord+cached_aggregated_off; } scr_coord viewport_t::get_screen_coord( const koord3d& pos, const koord& off) const { // Historic disheartening comment to be preserved: // better not try to twist your brain to follow the retransformation ... koord scr_pos_2d = get_viewport_coord(pos.get_2d()); const sint16 x = (scr_pos_2d.x-scr_pos_2d.y)*(cached_img_size/2) + tile_raster_scale_x(off.x, cached_img_size) + x_off; const sint16 y = (scr_pos_2d.x+scr_pos_2d.y)*(cached_img_size/4) + tile_raster_scale_y(off.y-pos.z*TILE_HEIGHT_STEP, cached_img_size) + ((cached_disp_width/cached_img_size)&1)*(cached_img_size/4) + y_off; return scr_coord(x,y); } scr_coord viewport_t::scale_offset( const koord &value ) { return scr_coord(tile_raster_scale_x( value.x, cached_img_size ), tile_raster_scale_y( value.x, cached_img_size )); } // change the center viewport position void viewport_t::change_world_position( koord new_ij, sint16 new_xoff, sint16 new_yoff ) { // truncate new_xoff, modify new_ij.x new_ij.x -= new_xoff/cached_img_size; new_ij.y += new_xoff/cached_img_size; new_xoff %= cached_img_size; // truncate new_yoff, modify new_ij.y int lines = 0; if(new_yoff>0) { lines = (new_yoff + (cached_img_size/4))/(cached_img_size/2); } else { lines = (new_yoff - (cached_img_size/4))/(cached_img_size/2); } new_ij -= koord( lines, lines ); new_yoff -= (cached_img_size/2)*lines; new_ij = world->get_closest_coordinate(new_ij); //position changed? => update and mark dirty if(new_ij!=ij_off || new_xoff!=x_off || new_yoff!=y_off) { ij_off = new_ij; x_off = new_xoff; y_off = new_yoff; world->set_dirty(); update_cached_values(); } } void viewport_t::switch_underground_mode(const koord3d& pos) { if (grund_t *gr = world->lookup(pos)) { if (!gr->is_visible()) { if (gr->ist_tunnel()) { // position is underground (and not visible), change to sliced mode grund_t::set_underground_mode(grund_t::ugm_level, gr->get_hoehe()); } else if (!gr->ist_karten_boden() || grund_t::underground_mode != grund_t::ugm_all) { // position is overground, change to normal view // but not if we are in full underground view and gr is kartenboden grund_t::set_underground_mode(grund_t::ugm_none, 0); } // make dirty etc world->update_underground(); } } } // change the center viewport position for a certain ground tile // any possible convoi to follow will be disabled void viewport_t::change_world_position( const koord3d& new_ij, bool automatic_underground) { follow_convoi = convoihandle_t(); if (automatic_underground) { switch_underground_mode(new_ij); } change_world_position( get_map2d_coord( new_ij ) ); } void viewport_t::change_world_position(const koord3d& pos, const koord& off, scr_coord sc) { // see get_viewport_coord and update_cached_values koord scr_pos_2d = pos.get_2d() - view_ij_off; const sint16 c2 = cached_img_size/2; const sint16 c4 = cached_img_size/4; // see get_screen_coord - do not twist your brain etc // solve (-ijx + ijy)*(cached_img_size/2) + x_off = sc.x - xfix; // note: xfix and yfix need 32-bit precision or will overflow const sint32 xfix = (scr_pos_2d.x - scr_pos_2d.y)*(cached_img_size/2) + tile_raster_scale_x(off.x, cached_img_size); // solve (-ijx - ijy)*(cached_img_size/4) + y_off = sc.y - yfix; const sint32 yfix = (scr_pos_2d.x + scr_pos_2d.y)*(cached_img_size/4) + tile_raster_scale_y(off.y-pos.z*TILE_HEIGHT_STEP, cached_img_size) + ((cached_disp_width/cached_img_size)&1)*(cached_img_size/4); // calculate center position const sint16 new_ij_x = (-(c2*(sc.y - yfix ))/c4 - (sc.x - xfix) ) / (2*c2); const sint16 new_ij_y = (-(c2*(sc.y - yfix ))/c4 + (sc.x - xfix) ) / (2*c2); // set new offsets to solve equation const sint16 new_x_off = sc.x - xfix - (-new_ij_x + new_ij_y) * c2; const sint16 new_y_off = sc.y - yfix - (-new_ij_x - new_ij_y) * c4; change_world_position(koord(new_ij_x, new_ij_y), new_x_off, new_y_off); } grund_t* viewport_t::get_ground_on_screen_coordinate(scr_coord screen_pos, sint32 &found_i, sint32 &found_j, const bool intersect_grid) const { const int rw1 = cached_img_size; const int rw2 = rw1/2; const int rw4 = rw1/4; /* * berechnung der basis feldkoordinaten in i und j * this would calculate raster i,j koordinates if there was no height * die formeln stehen hier zur erinnerung wie sie in der urform aussehen int base_i = (screen_x+screen_y)/2; int base_j = (screen_y-screen_x)/2; int raster_base_i = (int)floor(base_i / 16.0); int raster_base_j = (int)floor(base_j / 16.0); */ screen_pos.y += - y_off - rw2 - ((cached_disp_width/rw1)&1)*rw4; screen_pos.x += - x_off - rw2; const int i_off = rw4*(ij_off.x+get_viewport_ij_offset().x); const int j_off = rw4*(ij_off.y+get_viewport_ij_offset().y); bool found = false; // uncomment to: ctrl-key selects ground //bool select_karten_boden = event_get_last_control_shift()==2; // fallback: take kartenboden if nothing else found grund_t *bd = NULL; grund_t *gr = NULL; // for the calculation of hmin/hmax see simview.cc // for the definition of underground_level see grund_t::set_underground_mode const sint8 hmin = grund_t::underground_mode != grund_t::ugm_all ? min( world->get_groundwater() - 4, grund_t::underground_level ) : world->get_min_allowed_height(); const sint8 hmax = grund_t::underground_mode == grund_t::ugm_all ? world->get_max_allowed_height() : min( grund_t::underground_level, world->get_max_allowed_height() ); // find matching and visible grund for(sint8 hgt = hmax; hgt>=hmin; hgt--) { const int base_i = (screen_pos.x/2 + screen_pos.y + tile_raster_scale_y((hgt*TILE_HEIGHT_STEP),rw1))/2; const int base_j = (screen_pos.y - screen_pos.x/2 + tile_raster_scale_y((hgt*TILE_HEIGHT_STEP),rw1))/2; found_i = (base_i + i_off) / rw4;; found_j = (base_j + j_off) / rw4;; gr = world->lookup(koord3d(found_i,found_j,hgt)); if (gr != NULL) { found = /*select_karten_boden ? gr->ist_karten_boden() :*/ gr->is_visible(); if( ( gr->get_typ() == grund_t::tunnelboden || gr->get_typ() == grund_t::monorailboden ) && gr->get_weg_nr(0) == NULL && !gr->get_leitung() && gr->find()) { // This is only a dummy ground placed by tool_build_tunnel_t or tool_build_way_t as a preview. found = false; } if (found) { break; } if (bd==NULL && gr->ist_karten_boden()) { bd = gr; } } if (grund_t::underground_mode==grund_t::ugm_level && hgt==hmax) { // fallback in sliced mode, if no ground is under cursor bd = world->lookup_kartenboden(found_i,found_j); } else if (intersect_grid){ // We try to intersect with virtual nonexistent border tiles in south and east. if( (gr = world->lookup_gridcoords( koord3d( found_i, found_j, hgt ) )) ){ found = true; break; } } // Last resort, try to intersect with the same tile +1 height, seems to be necessary on steep slopes // *NOTE* Don't do it on border tiles, since it will extend the range in which the cursor will be considered to be // inside world limits. if( found_i==(world->get_size().x-1) || found_j == (world->get_size().y-1) ) { continue; } gr = world->lookup(koord3d(found_i,found_j,hgt+1)); if(gr != NULL) { found = /*select_karten_boden ? gr->ist_karten_boden() :*/ gr->is_visible(); if( gr->is_dummy_ground() && gr->find()) { // This is only a dummy ground placed by tool_build_tunnel_t or tool_build_way_t as a preview. found = false; } if (found) { break; } if (bd==NULL && gr->ist_karten_boden()) { bd = gr; } } } if(found) { return gr; } else { if(bd!=NULL){ found_i = bd->get_pos().x; found_j = bd->get_pos().y; return bd; } return NULL; } } koord3d viewport_t::get_new_cursor_position( const scr_coord &screen_pos, bool grid_coordinates ) { const int rw4 = cached_img_size/4; int offset_y = 0; if(world->get_zeiger()->get_yoff() == Z_PLAN) { // already ok } else { // shifted by a quarter tile offset_y += rw4; } sint32 grid_x, grid_y; const grund_t *bd = get_ground_on_screen_coordinate(scr_coord(screen_pos.x, screen_pos.y + offset_y), grid_x, grid_y, grid_coordinates); // no suitable location found (outside map, ...) if (!bd) { return koord3d::invalid; } // offset needed for the raise / lower tool. sint8 groff = 0; if( bd->is_visible() && grid_coordinates) { groff = bd->get_hoehe(world->get_corner_to_operate(koord(grid_x, grid_y))) - bd->get_hoehe(); } return koord3d(grid_x, grid_y, bd->get_disp_height() + groff); } void viewport_t::metrics_updated() { const scr_size screen = gfx->get_screen_size(); cached_disp_width = screen.w; cached_disp_height = screen.h; cached_img_size = gfx->get_tile_raster_width(); set_viewport_ij_offset(koord( - cached_disp_width/(2*cached_img_size) - cached_disp_height/cached_img_size, cached_disp_width/(2*cached_img_size) - cached_disp_height/cached_img_size ) ); } void viewport_t::rotate90( sint16 y_size ) { ij_off.rotate90(y_size); update_cached_values(); } viewport_t::viewport_t( karte_t *world, const koord ij_off , sint16 x_off , sint16 y_off ) : prepared_rect() { this->world = world; assert(world); follow_convoi = convoihandle_t(); this->ij_off = ij_off; this->x_off = x_off; this->y_off = y_off; metrics_updated(); } simutrans-124.5/src/simutrans/display/viewport.h000066400000000000000000000204141520675460500220760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef DISPLAY_VIEWPORT_H #define DISPLAY_VIEWPORT_H #include "../simtypes.h" #include "scr_coord.h" #include "../dataobj/koord.h" #include "../dataobj/koord3d.h" #include "../dataobj/rect.h" #include "../convoihandle.h" class karte_t; class grund_t; /** * @brief Map viewing camera. * Describes a camera, looking at our world. Contains various routines to handle the coordinates conversion. * * We handle four level of coordinates, described here: * - map_coord: It's a three dimensional coordinate, located in the simulated world. Implemented with koord3d, most game objects are positioned like this. * - map2d_coord: Two-dimensional coordinate, located in the simulated world, they are assumed z=0. Implemented with koord. * - viewport_coord: Two-dimensional coordinate, with our camera transformations applied, where 0,0 is the center of screen. * - screen_coord: Final transformation, exact coordinate on the output frame buffer. * * Diagram of a viewport screen, each viewport_coord corresponds to one of the diamonds: * -------------------- * |\/\/\/\/\/\/\/\/\/| * |/\/\/\/\/\/\/\/\/\| * |\/\/\/\/\/\/\/\/\/| * |/\/\/\/\/\/\/\/\/\| * |\/\/\/\/\/\/\/\/\/| * |/\/\/\/\/\/\/\/\/\| * |\/\/\/\/\/\/\/\/\/| * -------------------- */ class viewport_t { public: /** * @brief The prepared area of the view port. * * The area that has already been prepared for this view port. When the view * port is moved then only the new area not already prepared will be * prepared. If the view port is stationary no area will be prepared. */ rect_t prepared_rect; private: /// The simulated world this view is associated to. karte_t *world; /** * @name Camera position * This variables are related to the view camera position. * @{ */ /** * Current view position, expressed in 2D map coordinates. * @see x_off * @see y_off */ koord ij_off; sint16 x_off; //!< Fine scrolling x offset. Units in pixels. sint16 y_off; //!< Fine scrolling y offset. Units in pixels. /** * This is the current offset for getting from tile to screen. * @note Extra offset, if added to ij_off it will give us the 2D coordinate of the tile on the *BOTTOM-RIGHT* of screen. */ koord view_ij_off; /** * For performance reasons, we cache ( (0,0)-ij_off-view_ij_off ) here */ koord cached_aggregated_off; /** * @} */ sint16 cached_disp_width; ///< Cached window width sint16 cached_disp_height; ///< Cached window height sint16 cached_img_size; ///< Cached base raster image size /** * Sets current ij offsets of this viewport, depends of its proportions and the zoom level. */ void set_viewport_ij_offset( const koord &k ); /** * The current convoi to follow. */ convoihandle_t follow_convoi; /** * Converts map_coord to map2d_coord actually used for main view. */ koord get_map2d_coord( const koord3d& ) const; /** * Returns the viewport_coord coordinates of the requested map2d_coord coordinate. */ koord get_viewport_coord( const koord& coord ) const; /** * Updates the transformation vector we have cached. */ void update_cached_values() { cached_aggregated_off = -ij_off-view_ij_off; } public: /** * @name Coordinate transformations. * This methods handle the projection from the map coordinates, to 2D, screen coordinates. * @{ */ /** * Takes a in-game 3D coordinates and returns which coordinate in screen corresponds to it. * @param pos 3D in-map coordinate. * @param off offset to add to the final coordinates, in pixels (will be scaled to zoom level). */ scr_coord get_screen_coord( const koord3d& pos, const koord& off = koord(0,0) ) const; /** * Scales the 2D dimensions, expressed on base raster size pixels, to the current viewport. */ scr_coord scale_offset(const koord &value); /** * Checks a 3d in-map coordinate, to know if it's centered on the current viewport. * @param pos 3D map coordinate to check. * @return true if the requested map coordinates are on the center of the viewport, false otherwise. */ bool is_on_center(const koord3d &pos) const { return ( (get_x_off() | get_y_off()) == 0 && get_world_position() == get_map2d_coord( pos ) ); } /** * @} */ /** * @name Camera position * This methods are related to the view camera position. * @{ */ /** * Viewpoint in tile coordinates. i,j coordinate of the tile in center of screen. */ koord get_world_position() const { return ij_off; } /** * Offset from tile on center to tile in top-left corner of screen. */ koord get_viewport_ij_offset() const { return view_ij_off; } /** * Set center viewport position. */ void change_world_position( koord ij, sint16 x=0, sint16 y=0 ); /** * Set center viewport position, taking height into account. * Possibly switches underground mode. */ void change_world_position( const koord3d& ij, bool automatic_underground = true ); /** * Set center viewport position, placing a in-game koord3d under the desired screen position. * Possibly switches underground mode. * @param pos map position to consider. * @param off extra offset. * @param sc screen position "pos" should be under. */ void change_world_position(const koord3d& pos, const koord& off, scr_coord sc); /** * Switch underground mode if target position @p pos is not visible. */ void switch_underground_mode(const koord3d& pos); /** * Fine offset within the viewport tile. */ int get_x_off() const {return x_off;} /** * Fine offset within the viewport tile. */ void set_x_off(sint16 value) {x_off = value;} /** * Fine offset within the viewport tile. */ int get_y_off() const {return y_off;} /** * Fine offset within the viewport tile. */ void set_y_off(sint16 value) {y_off = value;} /** * @} */ /** * @name Ray tracing * This methods are related to the act of finding a in-map entity, given a screen_cord. Used for player interaction, mainly. * @{ */ /** * Searches for the ground_t that's under the requested screen position. * @param screen_pos Screen coordinates to check for. * @param intersect_grid Special case for the lower/raise tool, will return a limit border tile if we are on the south/east border of screen. * @param found_i out parameter, i-coordinate of the found tile. It's necessary because it might point to a grid position that doesn't map to a real in-map coordinate, on border tiles (south and east border). * @param found_j out parameter, j-coordinate of the found tile. It's necessary because it might point to a grid position that doesn't map to a real in-map coordinate, on border tiles (south and east border). * @return the grund_t that's under the desired screen coordinate. NULL if we are outside map or we can't find it. */ grund_t* get_ground_on_screen_coordinate(scr_coord screen_pos, sint32 &found_i, sint32 &found_j, const bool intersect_grid=false) const; /** * Gets a new world position, under the requested screen coordinates. Used to move the cursor. * @param screen_pos Screen position to check. Input parameter. * @param grid_coordinates indicates if this function is to check against the map tiles, or the grid of heights. Input parameter. * @return koord3d::invalid if no position exists under the requested coordinate, a 3d koord directly under it otherwise. */ koord3d get_new_cursor_position(const scr_coord &screen_pos, bool grid_coordinates); /** * @} */ /** * @name Convoi following * This methods are related to the mechanics of a viewport follow a vehicle. * @{ */ /** * Function for following a convoi on the map give an unbound handle to unset. */ void set_follow_convoi(convoihandle_t cnv) { follow_convoi = cnv; } /** * Returns the convoi this viewport is following (if any). */ convoihandle_t get_follow_convoi() const { return follow_convoi; } /** * @} */ /** * This class caches the viewport dimensions, and the base image size. On zoom or viewport resize, call this * method to force its recalculation. */ void metrics_updated(); /** * (A better description is needed here) * Operations needed on map rotation. */ void rotate90( sint16 y_size ); viewport_t( karte_t *world, const koord ij_off = koord::invalid, sint16 x_off = 0, sint16 y_off = 0 ); }; #endif simutrans-124.5/src/simutrans/freight_list_sorter.cc000066400000000000000000000324461520675460500230010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "freight_list_sorter.h" #include "simhalt.h" #include "simline.h" #include "simconvoi.h" #include "simtypes.h" #include "simware.h" #include "simfab.h" #include "simmem.h" #include "world/simworld.h" #include "player/simplay.h" #include "dataobj/translator.h" #include "tpl/slist_tpl.h" #include "tpl/vector_tpl.h" #include "utils/cbuffer.h" karte_ptr_t freight_list_sorter_t::welt; freight_list_sorter_t::sort_mode_t freight_list_sorter_t::sortby=by_name; /** * This variable defines by which column the table is sorted * Values: 0 = destination * 1 = via * 2 = via_amount * 3 = amount * 4 = via owner of next stop * 5 = via next line (or lineless convoy) taken */ static const char* sort_text[freight_list_sorter_t::SORT_MODES] = { "Zielort", "Menge", "via", "via Menge", "via owner", "connection" }; const char *freight_list_sorter_t::get_sort_mode_string(uint8 sort_mode, halthandle_t h) { return sort_text[h.is_bound() ? sort_mode : (sort_mode%(SORT_MODES-1))]; } /** * @return whether w1 is less than w2 */ bool freight_list_sorter_t::compare_ware(ware_t const& w1, ware_t const& w2) { // sort according to freight // if w1 and w2 differ, they are sorted according to catg_index and index // we sort with respect to catg_indexfirst, since freights with the same category // will be displayed together int idx = w1.get_desc()->get_catg_index() - w2.get_desc()->get_catg_index(); if( idx == 0 ) { idx = w1.get_desc()->get_index() - w2.get_desc()->get_index(); } if( idx != 0 ) { return idx < 0; } switch (sortby) { default: dbg->error("freight_list_sorter::compare_ware()", "illegal sort mode!"); /* FALLTHROUGH */ case by_via_sum: case by_via_owner: case by_connection: case by_amount: { // sort by ware amount int const order = w2.amount - w1.amount; if( order != 0 ) { return order < 0; } } /* FALLTHROUGH */ case by_via: { // sort by via_destination name halthandle_t const v1 = w1.get_via_halt(); halthandle_t const v2 = w2.get_via_halt(); if( v1.is_bound() && v2.is_bound() ) { int const order = strcmp(v1->get_name(), v2->get_name()); if( order != 0) return order < 0; } else if( v1.is_bound() ) { return false; } else if( v2.is_bound() ) { return true; } } /* FALLTHROUGH */ case by_name: { // sort by destination name halthandle_t const d1 = w1.get_target_halt(); halthandle_t const d2 = w2.get_target_halt(); if( d1.is_bound() && d2.is_bound() ) { const fabrik_t *fab = NULL; const char *const name1 = ( w1.to_factory ? ( (fab=fabrik_t::get_fab(w1.get_target_pos())) ? fab->get_name() : "Invalid Factory" ) : d1->get_name() ); const char *const name2 = ( w2.to_factory ? ( (fab=fabrik_t::get_fab(w2.get_target_pos())) ? fab->get_name() : "Invalid Factory" ) : d2->get_name() ); return strcmp(name1, name2) < 0; } else if( d1.is_bound() ) { return false; } else if( d2.is_bound() ) { return true; } } } return false; } void freight_list_sorter_t::add_ware_heading( cbuffer_t &buf, uint64 sum, uint32 max, const ware_t *ware, const char *what_doing ) { uint32 const max_display = ~0; // not the first line? if( buf.len() > 0 ) { buf.append("\n"); } if (sum > max_display) { buf.printf(">%u", max_display); } else { buf.printf("%u", (uint32)sum); } if( max != 0 ) { // convois buf.printf("/%u", max); } goods_desc_t const& desc = *ware->get_desc(); char const* const unit = translator::translate(desc.get_mass()); // special freight (catg == 0) needs own name char const* const name = translator::translate(ware->get_catg() != 0 ? desc.get_catg_name() : desc.get_name()); char const* const what = translator::translate(what_doing); buf.printf("%s %s %s\n", unit, name, what); } void freight_list_sorter_t::sort_freight(vector_tpl const& warray, cbuffer_t& buf, sort_mode_t sort_mode, const slist_tpl* full_list, const char* what_doing, halthandle_t h) { sortby = sort_mode; if (!h.is_bound() && sort_mode == by_connection) { sort_mode = by_amount; } // added sorting to ware's destination list ware_t* wlist = MALLOCN(ware_t, warray.get_count()); // store the overflow when merging wares (during via sortings), looks very broken for now // only created when needed uint64* categories_goods_amount_lost = NULL; uint32 pos = 0; for(ware_t const& ware : warray) { if( ware.get_desc() == goods_manager_t::none || ware.amount == 0 || !ware.get_next_halt().is_bound()) { continue; } wlist[pos] = ware; if( sort_mode == by_via_sum ) { // via sort mode merges packets with a common next stop for( uint32 i=0; i 0 ) { // reached goods amount limit, have to discard amount and track category totals separatly if( categories_goods_amount_lost == NULL ) { categories_goods_amount_lost = new uint64[256](); // this should be tied to a category index limit constant } categories_goods_amount_lost[wi.get_desc()->get_catg_index()]+= remaining_amount; } pos--; // remove after merging break; } } } } if (sort_mode == by_connection) { // connection mode merges packets with all their next connections common for ( uint32 i = 0; i < pos; i++) { ware_t& wi = wlist[i]; if (wi.get_index() == ware.get_index()) { halthandle_t w_next = ware.get_next_halt(); halthandle_t wi_next = wi.get_next_halt(); bool merge = w_next == wi_next; bool has_line = false; if (!merge) { // not same via halt, but maybe same lines for (linehandle_t const& line : h->registered_lines) { if (line->get_goods_catg_index().is_contained(ware.get_catg_index())) { // only merge if both can travel with the same line to their stop bool has_line1 = w_next->registered_lines.is_contained(line); bool has_line2 = wi_next->registered_lines.is_contained(line); has_line |= has_line1 || has_line2; merge = has_line1 && has_line2; if (merge) { break; } } } } // Don't attempt to merge with convoys if at least one of the // wares can take a line, since then the lines differ. if (!merge && !has_line) { // not same via halt, not same line, but maybe same convoy for (convoihandle_t const& c : wi_next->registered_convoys) { if (c->get_goods_catg_index().is_contained(ware.get_catg_index()) && w_next->registered_convoys.is_contained(c) && (h.is_bound() && h->registered_convoys.is_contained(c))) { merge = true; break; } } } if (merge) { // same entry ware_t::goods_amount_t const remaining_amount = wi.add_goods(ware.amount); if (remaining_amount > 0) { // reached goods amount limit, have to discard amount and track category totals separatly if (categories_goods_amount_lost == NULL) { categories_goods_amount_lost = new uint64[256](); // this should be tied to a category index limit constant } categories_goods_amount_lost[wi.get_desc()->get_catg_index()] += remaining_amount; } pos--; // remove after merging break; } } } } else if( sort_mode == by_via_owner ) { // player sort mode merges packets which next stop is owned by the // same player player_t* owner = ware.get_next_halt()->get_owner(); for( uint32 i=0; iget_owner() == owner ) { ware_t::goods_amount_t const remaining_amount = wi.add_goods(ware.amount); if( remaining_amount > 0 ) { // reached goods amount limit, have to discard amount and track category totals separatly if( categories_goods_amount_lost == NULL ) { categories_goods_amount_lost = new uint64[256](); // this should be tied to a category index limit constant } categories_goods_amount_lost[wi.get_desc()->get_catg_index()]+= remaining_amount; } pos--; // remove after merging break; } } } pos++; } // if there, give the capacity for each freight slist_tpl const dummy; slist_tpl const& list = full_list ? *full_list : dummy; slist_tpl::const_iterator full_i = list.begin(); slist_tpl::const_iterator const full_end = list.end(); // at least some capacity added? if( pos != 0 ) { // sort the ware's list std::sort( wlist, wlist + pos, compare_ware ); // print the ware's list to buffer int last_goods_index = -1; int last_ware_catg = -1; for( uint32 j = 0; j < pos; j++ ) { halthandle_t const halt = wlist[j].get_target_halt(); halthandle_t const via_halt = wlist[j].get_via_halt(); const char * name = "Error in Routing"; if( halt.is_bound() ) { name = halt->get_name(); } ware_t const& ware = wlist[j]; if( last_goods_index!=ware.get_index() && last_ware_catg!=ware.get_catg() ) { uint64 sum = categories_goods_amount_lost != NULL ? categories_goods_amount_lost[ware.get_desc()->get_catg_index()] : 0; last_goods_index = ware.get_index(); // special freight => handle different last_ware_catg = (ware.get_catg()!=0) ? ware.get_catg() : -1; for( uint32 i=j; i=%u%s %s > " : " %u%s %s > "; buf.printf(good_description_format, ware.amount, translator::translate(desc.get_mass()), translator::translate(desc.get_name())); // special mode: simply retrieve player name if( sortby == by_via_owner ) { if( via_halt.is_bound() ) { buf.append(via_halt->get_owner()->get_name()); } else { buf.append(name); } buf.append("\n"); continue; } if (sort_mode == by_connection) { // when we are here, we have a valid halthandle in h halthandle_t h_next = ware.get_next_halt(); uint32 linecount = 0; // maybe same line for (linehandle_t const& line : h->registered_lines) { if (h_next->registered_lines.is_contained(line) && line->get_goods_catg_index().is_contained(ware.get_catg_index())) { if (linecount++) { buf.append(", "); } buf.append(line->get_name()); } } if (!linecount) { uint32 convoicount = 0; // not same line, but maybe same convoy for (convoihandle_t const& c : h->registered_convoys) { if (h_next->registered_convoys.is_contained(c) && c->get_goods_catg_index().is_contained(ware.get_index())) { if (convoicount++==0) { buf.append(c->get_name()); } } } if (convoicount > 1) { buf.append(" + "); buf.printf(translator::translate("%d more convois"), convoicount - 1); } } buf.append("\n"); continue; } // the target name is not correct for the via sort const bool is_factory_going = ( sortby!=by_via_sum && ware.to_factory ); // exclude merged packets if( sortby!=by_via_sum || via_halt==halt ) { if( is_factory_going ) { const fabrik_t *const factory = fabrik_t::get_fab( ware.get_target_pos() ); buf.printf("%s <%i,%i>", (factory ? factory->get_name() : "Invalid Factory"), ware.get_target_pos().x, ware.get_target_pos().y); } else { buf.append(name); } } if( via_halt!=halt || is_factory_going ) { if( via_halt.is_bound() ) { buf.printf(translator::translate("via %s\n"), via_halt->get_name()); } else { if( sortby == by_via_sum ) { // do not show undecided transfer halts buf.append(name); } buf.append("\n"); } } else { buf.append("\n"); } // debug end } } if (categories_goods_amount_lost != NULL) { delete[] categories_goods_amount_lost; } // still entire left? for( ; full_i != full_end; ++full_i ) { ware_t const& g = *full_i; add_ware_heading(buf, 0, g.amount, &g, what_doing); } free( wlist ); } simutrans-124.5/src/simutrans/freight_list_sorter.h000066400000000000000000000022201520675460500226260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef FREIGHT_LIST_SORTER_H #define FREIGHT_LIST_SORTER_H // same sorting for stations and vehicle/convoi freight ... #include "simtypes.h" #include "halthandle.h" template class slist_tpl; template class vector_tpl; class ware_t; class cbuffer_t; class karte_ptr_t; class freight_list_sorter_t { public: enum sort_mode_t { by_name = 0, by_amount = 1, by_via = 2, by_via_sum = 3, by_via_owner = 4, by_connection = 5, SORT_MODES }; // returns the string for the current sort mode (untranslated) static const char *get_sort_mode_string(uint8 mode, halthandle_t h); static void sort_freight(vector_tpl const& warray, cbuffer_t& buf, sort_mode_t sort_mode, const slist_tpl* full_list, const char* what_doing, halthandle_t h); private: static karte_ptr_t welt; static sort_mode_t sortby; static bool compare_ware(ware_t const& w1, ware_t const& w2); static void add_ware_heading( cbuffer_t &buf, uint64 sum, uint32 max, const ware_t *ware, const char *what_doing ); }; #endif simutrans-124.5/src/simutrans/ground/000077500000000000000000000000001520675460500176765ustar00rootroot00000000000000simutrans-124.5/src/simutrans/ground/boden.cc000066400000000000000000000101361520675460500212750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../world/simworld.h" #include "../simskin.h" #include "../obj/baum.h" #include "../dataobj/environment.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "boden.h" #include "../descriptor/ground_desc.h" #include "../descriptor/skin_desc.h" boden_t::boden_t(loadsave_t *file, koord pos ) : grund_t( koord3d(pos,0) ) { grund_t::rdwr( file ); // restoring trees (disadvantage: losing offsets but much smaller savegame footprint) if( file->is_version_atleast(110, 1) ) { sint16 id = file->rd_obj_id(); while( id!=-1 ) { if (id != (uint8)id) { dbg->warning("boden_t::boden_t", "Invalid tree id %hd, using %hhu", id, (uint8)id); } uint16 age; if (file->is_version_atleast(122, 2)) { file->rdwr_short(age); age &= 0xFFF; } else { sint32 val; file->rdwr_long(val); age = (uint32)val & 0xFFF; } // check if we still have this tree const tree_desc_t *desc = NULL; const char *desc_name = tree_builder_t::get_loaded_desc_name((uint8)id); if (desc_name) { desc = tree_builder_t::get_desc_by_name(desc_name); if (!desc) { desc = tree_builder_t::get_desc_by_name(translator::compatibility_name(desc_name)); } } else { desc = tree_builder_t::get_desc_by_id((uint8)id); } if (desc) { baum_t *tree = new baum_t( get_pos(), (uint8)id, age, slope ); objlist.add( tree ); } else { dbg->warning( "boden_t::boden_t()", "Could not restore tree type %hhu at (%s)", (uint8)id, pos.get_str() ); } // check for next tree id = file->rd_obj_id(); } } } boden_t::boden_t(koord3d pos, slope_t::type sl) : grund_t(pos) { slope = sl; } // only for more compact saving of trees void boden_t::rdwr(loadsave_t *file) { grund_t::rdwr(file); if( file->is_version_atleast(110, 1) ) { // a server sends the smallest possible savegames to clients, i.e. saves only types and age of trees if( (env_t::server || file->is_version_atleast(122, 2)) && !hat_wege() ) { for( uint8 i=0; iget_typ()==obj_t::baum ) { baum_t *tree = (baum_t *)obj; file->wr_obj_id( tree->get_desc_id() ); if (file->is_version_atleast(122, 2)) { uint16 age = tree->get_age(); file->rdwr_short( age ); } else { uint32 age = tree->get_age(); file->rdwr_long( age ); } } } } file->wr_obj_id( -1 ); } if (file->is_loading() && hat_weg(road_wt) && !slope_t::is_way(slope)) { // illegal slope => change to flat slope dbg->warning("boden_t::rdwr", "Illegal street slope %d at (%) set to flat.", slope, pos.get_str()); slope = slope_t::flat; } } const char *boden_t::get_name() const { if( get_crossing() ) { return "Kreuzung"; } else if( hat_wege() ) { return get_weg_nr(0)->get_name(); } else { return "Boden"; } } void boden_t::calc_image_internal(const bool calc_only_snowline_change) { const slope_t::type slope_this = get_disp_slope(); const weg_t *const weg = get_weg( road_wt ); if( weg && weg->hat_gehweg() ) { // single or double slope const uint8 imageid = (!slope_this || is_one_high(slope_this)) ? ground_desc_t::slopetable[slope_this] : ground_desc_t::slopetable[slope_this >> 1] + 12; if( (get_hoehe() >= welt->get_snowline() || welt->get_climate(pos.get_2d()) == arctic_climate) && skinverwaltung_t::fussweg->get_image_id(imageid + 1) != IMG_EMPTY ) { // snow images set_image( skinverwaltung_t::fussweg->get_image_id(imageid + 1) ); } else if( slope_this != 0 && get_hoehe() == welt->get_snowline() - 1 && skinverwaltung_t::fussweg->get_image_id(imageid + 2) != IMG_EMPTY ) { // transition images set_image( skinverwaltung_t::fussweg->get_image_id(imageid + 2) ); } else { set_image( skinverwaltung_t::fussweg->get_image_id(imageid) ); } } else { set_image( ground_desc_t::get_ground_tile(this) ); } if( !calc_only_snowline_change ) { grund_t::calc_back_image( get_disp_height(), slope_this ); } } simutrans-124.5/src/simutrans/ground/boden.h000066400000000000000000000017011520675460500211350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GROUND_BODEN_H #define GROUND_BODEN_H #include "grund.h" /** * boden_t are nature tiles (maybe with ways, powerlines, trees and beware: harbor buildings) */ class boden_t : public grund_t { protected: /// @copydoc grund_t::calc_image_internal void calc_image_internal(const bool calc_only_snowline_change) OVERRIDE; public: boden_t(loadsave_t *file, koord pos ); boden_t(koord3d pos, slope_t::type slope); public: /// @copydoc grund_t::rdwr void rdwr(loadsave_t *file) OVERRIDE; /// @copydoc grund_t::ist_natur bool ist_natur() const OVERRIDE { return (flags & (is_halt_flag | has_way1 | has_way2))==0; // equivalen to return !hat_wege() && !is_halt(); } /// @copydoc grund_t::get_name const char *get_name() const OVERRIDE; /// @copydoc grund_t::get_typ grund_t::typ get_typ() const OVERRIDE { return boden; } }; #endif simutrans-124.5/src/simutrans/ground/brueckenboden.cc000066400000000000000000000103631520675460500230160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "../world/simworld.h" #include "../obj/bruecke.h" #include "../builder/brueckenbauer.h" #include "../descriptor/ground_desc.h" #include "../descriptor/bridge_desc.h" #include "../dataobj/loadsave.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../utils/cbuffer.h" #include "brueckenboden.h" #include "../obj/way/weg.h" #include "../vehicle/vehicle_base.h" brueckenboden_t::brueckenboden_t(koord3d pos, slope_t::type grund_hang, slope_t::type weg_hang) : grund_t(pos), weg_hang(weg_hang) { slope = grund_hang; } void brueckenboden_t::calc_image_internal(const bool calc_only_snowline_change) { if( ist_karten_boden() ) { set_image( ground_desc_t::get_ground_tile(this) ); if( !calc_only_snowline_change ) { grund_t::calc_back_image( get_pos().z, slope ); set_flag( draw_as_obj ); if( (get_grund_hang() == slope_t::west && abs(back_imageid) > 11) || (get_grund_hang() == slope_t::north && get_back_image(0) != IMG_EMPTY) ) { // must draw as obj, since there is a slop here nearby koord pos = get_pos().get_2d() + koord( get_grund_hang() ); grund_t *gr = welt->lookup_kartenboden( pos ); gr->set_flag( grund_t::draw_as_obj ); } } } else { clear_back_image(); set_image(IMG_EMPTY); } } void brueckenboden_t::rdwr(loadsave_t *file) { xml_tag_t t( file, "brueckenboden_t" ); grund_t::rdwr(file); if(file->is_version_less(88, 9)) { uint8 sl; file->rdwr_byte(sl); slope = sl; } if( file->is_saving() && file->is_version_less(112, 7) ) { // truncate double weg_hang to single weg_hang, better than nothing uint8 sl = min( corner_sw(weg_hang), 1 ) + min( corner_se(weg_hang), 1 ) * 2 + min( corner_ne(weg_hang), 1 ) * 4 + min( corner_nw(weg_hang), 1 ) * 8; file->rdwr_byte(sl); } else { file->rdwr_byte(weg_hang); } if( file->is_loading() && file->is_version_less(112, 7) ) { // convert slopes from old single height saved game weg_hang = slope_from_slope4(slope4_t(weg_hang), env_t::pak_height_conversion_factor); } if(!find()) { dbg->error( "brueckenboden_t::rdwr()", "No bridge on bridge ground at (%s); try replacement", pos.get_str() ); weg_t *w = get_weg_nr(0); if(w) { const bridge_desc_t *br_desc = bridge_builder_t::find_bridge( w->get_waytype(), w->get_max_speed(), 0 ); if (!br_desc) { dbg->fatal("brueckenboden_t::rdwr", "No suitable bridge found for bridge ground at (%s)!", pos.get_str()); } const grund_t *kb = welt->lookup_kartenboden(get_pos().get_2d()); int height = 1; if( kb && get_pos().z - kb->get_pos().z > 1 ) { height = 2; } bruecke_t *br = new bruecke_t( get_pos(), welt->get_public_player(), br_desc, ist_karten_boden() ? br_desc->get_end( slope, get_grund_hang(), get_weg_hang() ) : br_desc->get_straight( w->get_ribi_unmasked(), height ) ); obj_add( br ); } } } void brueckenboden_t::rotate90() { if( sint8 way_offset = get_weg_yoff() ) { pos.rotate90( welt->get_size().y-1 ); slope = slope_t::rotate90( slope ); // since the y_off contains also the way height, we need to remove it before rotations and add it back afterwards for( uint8 i = 0; i < objlist.get_top(); i++ ) { obj_t * obj = obj_bei( i ); if( !dynamic_cast(obj) ) { obj->set_yoff( obj->get_yoff() + way_offset ); obj->rotate90(); obj->set_yoff( obj->get_yoff() - way_offset ); } else { // vehicle corrects its offset themselves obj->rotate90(); } } } else { weg_hang = slope_t::rotate90( weg_hang ); grund_t::rotate90(); } } sint8 brueckenboden_t::get_weg_yoff() const { if( ist_karten_boden() && weg_hang == 0 ) { // we want to find maximum height of slope corner shortcut as we know this is n, s, e or w and single heights are not integer multiples of 8 return TILE_HEIGHT_STEP * slope_t::max_diff(slope); } else { return 0; } } void brueckenboden_t::info(cbuffer_t & buf) const { const bruecke_t *bridge = find(); if(bridge && bridge->get_desc()) { const bridge_desc_t *desc = bridge->get_desc(); buf.append(translator::translate(desc->get_name())); buf.append("\n"); } grund_t::info(buf); } simutrans-124.5/src/simutrans/ground/brueckenboden.h000066400000000000000000000022321520675460500226540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GROUND_BRUECKENBODEN_H #define GROUND_BRUECKENBODEN_H #include "grund.h" class brueckenboden_t : public grund_t { private: slope_t::type weg_hang; ///< for e.g. ramps protected: /// @copydoc grund_t::calc_image_internal void calc_image_internal(const bool calc_only_snowline_change) OVERRIDE; public: brueckenboden_t(loadsave_t *file, koord pos ) : grund_t(koord3d(pos,0) ) { rdwr(file); } brueckenboden_t(koord3d pos, slope_t::type grund_hang, slope_t::type weg_hang); public: /// @copydoc grund_t::rdwr void rdwr(loadsave_t *file) OVERRIDE; /// @copydoc grund_t::rotate90 void rotate90() OVERRIDE; /// @copydoc grund_t::get_weg_yoff sint8 get_weg_yoff() const OVERRIDE; /// @copydoc grund_t::get_weg_hang slope_t::type get_weg_hang() const OVERRIDE { return weg_hang; } /// @copydoc grund_t::get_name const char *get_name() const OVERRIDE { return "Brueckenboden"; } /// @copydoc grund_t::get_typ typ get_typ() const OVERRIDE { return brueckenboden; } /// @copydoc grund_t::info void info(cbuffer_t & buf) const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/ground/fundament.cc000066400000000000000000000015121520675460500221650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simconst.h" #include "../descriptor/ground_desc.h" #include "../dataobj/loadsave.h" #include "grund.h" #include "fundament.h" fundament_t::fundament_t(loadsave_t *file, koord pos) : grund_t(koord3d(pos,0)) { rdwr(file); slope = slope_t::flat; } fundament_t::fundament_t(koord3d pos, slope_t::type hang) : grund_t(pos) { set_image( IMG_EMPTY ); if(hang != slope_t::flat) { pos = get_pos(); pos.z += slope_t::max_diff(hang); set_pos( pos ); } slope = slope_t::flat; } void fundament_t::calc_image_internal(const bool calc_only_snowline_change) { set_image( ground_desc_t::get_ground_tile(this) ); if( !calc_only_snowline_change ) { grund_t::calc_back_image( get_disp_height(), slope_t::flat ); } } simutrans-124.5/src/simutrans/ground/fundament.h000066400000000000000000000013271520675460500220330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GROUND_FUNDAMENT_H #define GROUND_FUNDAMENT_H #include "grund.h" /** * The foundation serves as base ground for all buildings in Simutrans. */ class fundament_t : public grund_t { protected: /// The foundation always has the same image. void calc_image_internal(const bool calc_only_snowline_change) OVERRIDE; public: fundament_t(loadsave_t *file, koord pos); fundament_t(koord3d pos, slope_t::type hang); public: /// @copydoc grund_t::get_name const char *get_name() const OVERRIDE { return "Fundament"; } /// @copydoc grund_t::get_typ typ get_typ() const OVERRIDE { return fundament; } }; #endif simutrans-124.5/src/simutrans/ground/grund.cc000066400000000000000000002252021520675460500213270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simcolor.h" #include "../simconst.h" #include "../simdebug.h" #include "../obj/depot.h" #include "../display/simgraph.h" #include "../display/viewport.h" #include "../simhalt.h" #include "../display/simimg.h" #include "../player/simplay.h" #include "../gui/simwin.h" #include "../world/simworld.h" #include "../simfab.h" #include "../builder/wegbauer.h" #include "../descriptor/ground_desc.h" #include "../descriptor/building_desc.h" #include "../descriptor/crossing_desc.h" #include "../descriptor/tunnel_desc.h" #include "../descriptor/way_desc.h" #include "../dataobj/freelist.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../obj/baum.h" #include "../obj/bruecke.h" #include "../obj/crossing.h" #include "../obj/groundobj.h" #include "../obj/label.h" #include "../obj/leitung2.h" #include "../obj/roadsign.h" #include "../obj/signal.h" #include "../obj/tunnel.h" #include "../obj/wayobj.h" #include "../obj/zeiger.h" #include "../gui/ground_info.h" #include "../gui/minimap.h" #include "../tpl/inthashtable_tpl.h" #include "../utils/cbuffer.h" #include "../vehicle/pedestrian.h" #include "../obj/way/kanal.h" #include "../obj/way/maglev.h" #include "../obj/way/monorail.h" #include "../obj/way/narrowgauge.h" #include "../obj/way/runway.h" #include "../obj/way/schiene.h" #include "../obj/way/strasse.h" #include "../obj/way/weg.h" #include "fundament.h" #include "grund.h" #include "tunnelboden.h" #include "wasser.h" /** * Pointer to the world of this ground. Static to conserve space. * Change to instance variable once more than one world is available. */ karte_ptr_t grund_t::welt; volatile bool grund_t::show_grid = false; uint8 grund_t::offsets[4]={0,1,2/*illegal!*/,2}; sint8 grund_t::underground_level = 127; uint8 grund_t::underground_mode = ugm_none; // ---------------------- text handling from here ---------------------- /** * Table of ground texts */ static inthashtable_tpl ground_texts; #define get_ground_text_key(k) ( (uint64)(k).x + ((uint64)(k).y << 16) + ((uint64)(k).z << 32) ) // and the reverse operation #define get_ground_koord3d_key(key) koord3d( (key) & 0x00007FFF, ((key)>>16) & 0x00007fff, (sint8)((key)>>32) ) void grund_t::set_text(const char *text) { if (text==NULL && !get_flag(has_text)) { // no text to delete return; } const uint64 n = get_ground_text_key(pos); if( text ) { char *new_text = strdup(text); free(ground_texts.remove(n)); ground_texts.put(n, new_text); set_flag(has_text); } else if( get_flag(has_text) ) { char *txt=ground_texts.remove(n); free(txt); clear_flag(has_text); } set_flag(dirty); welt->set_dirty(); } const char *grund_t::get_text() const { const char *result = 0; if( get_flag(has_text) ) { result = ground_texts.get( get_ground_text_key(pos) ); if(result==NULL) { return "undef"; } assert(result); } return result; } const player_t* grund_t::get_label_owner() const { const player_t* player = NULL; // if this ground belongs to a halt, the color should reflect the halt owner, not the ground owner! // Now, we use the color of label_t owner if(is_halt() && find()==NULL) { // only halt label const halthandle_t halt = get_halt(); player=halt->get_owner(); } // else color according to current owner else if(obj_bei(0)) { player = obj_bei(0)->get_owner(); // for cityhall const label_t* l = find(); if(l) { player = l->get_owner(); } } return player; } // ---------------- init, rdwr, and destruct from here --------------------- void* grund_t::operator new(size_t s) { return freelist_t::gimme_node(s); } void grund_t::operator delete(void* p, size_t s) { return freelist_t::putback_node(s, p); } void grund_t::rdwr(loadsave_t *file) { koord k = pos.get_2d(); // water saves its correct height => no need to save grid heights anymore sint8 z = welt->lookup_hgt( k ); // save grid height for water tiles - including partial water tiles sint8 z_w = welt->get_water_hgt( k ); if( !(get_typ() == grund_t::boden || get_typ() == grund_t::wasser) || pos.z > z_w || z > z_w ) { z = pos.z; // all other tiles save ground height } planquadrat_t *plan = welt->access( k ); uint8 climate_data = plan->get_climate() + (plan->get_climate_corners() << 4); xml_tag_t g( file, "grund_t" ); if(file->is_version_less(101, 0)) { pos.rdwr(file); z_w = welt->get_groundwater(); } else if( file->is_version_less(112, 7) ) { file->rdwr_byte(z); pos.z = get_typ() == grund_t::wasser ? welt->get_groundwater() : z; z_w = welt->get_groundwater(); } else { file->rdwr_byte(z); file->rdwr_byte(z_w); if( file->is_loading() && !is_water() && !welt->lookup_kartenboden( k ) && z < z_w ) { // partially in water, restore correct ground height while keeping grid height // if kartenboden doesn't exist we will become it pos.z = z_w; } else if( file->is_loading() ) { pos.z = get_typ() == grund_t::wasser ? z_w : z; } file->rdwr_byte(climate_data); plan->set_climate((climate)(climate_data & 7)); plan->set_climate_corners((climate_data >> 4)); } if( file->is_loading() && file->is_version_less(112, 7) ) { // convert heights from old single height saved game - water already at correct height pos.z = get_typ() == grund_t::wasser ? pos.z : pos.z * env_t::pak_height_conversion_factor; z = z * env_t::pak_height_conversion_factor; } if(file->is_saving()) { const char *text = get_text(); file->rdwr_str(text); } else { const char *text = 0; file->rdwr_str(text); if(text) { set_text(text); free(const_cast(text)); } } if(file->is_version_less(99, 7)) { bool label; file->rdwr_bool(label); if(label) { objlist.add( new label_t(pos, welt->get_player(0), get_text() ) ); } } sint8 owner_n=-1; if(file->is_version_less(99, 5)) { file->rdwr_byte(owner_n); } if(file->is_version_atleast(88, 9)) { uint8 sl = slope; if( file->is_version_less(112, 7) && file->is_saving() ) { // truncate double slopes to single slopes, better than nothing sl = min( corner_sw(slope), 1 ) + min( corner_se(slope), 1 ) * 2 + min( corner_ne(slope), 1 ) * 4 + min( corner_nw(slope), 1 ) * 8; } file->rdwr_byte(sl); if( file->is_loading() ) { slope = sl; } } else { // safe init for old version slope = 0; } if( file->is_loading() ) { if( file->is_version_less(112, 7) ) { // convert slopes from old single height saved game slope = slope_from_slope4(slope4_t(slope), env_t::pak_height_conversion_factor); } if( !ground_desc_t::double_grounds ) { // truncate double slopes to single slopes slope = encode_corners(min( corner_sw(slope), 1 ), min( corner_se(slope), 1 ), min( corner_ne(slope), 1 ), min( corner_nw(slope), 1 )); } } // restore grid if( file->is_loading() ) { if( get_typ() == grund_t::boden || get_typ() == grund_t::fundament ) { /* since those must be on the ground and broken grids occurred in the past * (due to incorrect restoration of grid heights on house slopes) * we simply reset the grid height to our current height */ z = pos.z; } // for south/east map edges we need to restore more than one point if( pos.x == welt->get_size().x-1 && pos.y == welt->get_size().y-1 ) { sint8 z_southeast = z; if( get_typ() == grund_t::wasser && z_southeast > z_w ) { z_southeast = z_w; } else { z_southeast += corner_se(slope); } welt->set_grid_hgt_nocheck( k + koord(1,1), z_southeast ); } if( pos.x == welt->get_size().x-1 ) { sint8 z_east = z; if( get_typ() == grund_t::wasser && z_east > z_w ) { z_east = z_w; } else { z_east += corner_ne(slope); } welt->set_grid_hgt_nocheck( k + koord(1,0), z_east ); } if( pos.y == welt->get_size().y-1 ) { sint8 z_south = z; if( get_typ() == grund_t::wasser && z_south > z_w ) { z_south = z_w; } else { z_south += corner_sw(slope); } welt->set_grid_hgt_nocheck( k + koord(0,1), z_south ); } if( get_typ() == grund_t::wasser && z > z_w ) { z = z_w; } else { z += corner_nw(slope); } welt->set_grid_hgt_nocheck( k, z ); welt->set_water_hgt_nocheck( k, z_w ); } // loading ways from here on if(file->is_loading()) { waytype_t wtyp; int i = -1; do { wtyp = (waytype_t)file->rd_obj_id(); weg_t *weg = NULL; if(++i < 2) { switch(wtyp) { default: #if MSG_LEVEL if( wtyp != invalid_wt ) { dbg->error( "grund_t::rdwr()", "invalid waytype %i!", (int)wtyp ); wtyp = invalid_wt; } #endif break; case road_wt: weg = new strasse_t(file); break; case monorail_wt: weg = new monorail_t(file); break; case maglev_wt: weg = new maglev_t(file); break; case narrowgauge_wt: weg = new narrowgauge_t(file); break; case track_wt: { schiene_t *sch = new schiene_t(file); if(sch->get_desc()->get_wtyp()==monorail_wt) { dbg->warning("grund_t::rdwr()", "converting railroad to monorail at (%i,%i)",get_pos().x, get_pos().y); // compatibility code: Convert to monorail monorail_t *w= new monorail_t(); w->set_desc(sch->get_desc()); w->set_max_speed(sch->get_max_speed()); w->set_ribi(sch->get_ribi_unmasked()); delete sch; weg = w; } else { weg = sch; } } break; case tram_wt: weg = new schiene_t(file); if(weg->get_desc()->get_styp()!=type_tram) { weg->set_desc(way_builder_t::weg_search(tram_wt,weg->get_max_speed(),0,type_tram)); } break; case water_wt: // ignore old type dock ... if(file->is_version_atleast(87, 0)) { weg = new kanal_t(file); } else { uint8 d8; sint16 d16; sint32 d32; file->rdwr_byte(d8); file->rdwr_short(d16); file->rdwr_long(d32); file->rdwr_long(d32); file->rdwr_long(d32); file->rdwr_long(d32); DBG_MESSAGE("grund_t::rdwr()","at (%i,%i) dock ignored",get_pos().x, get_pos().y); } break; case air_wt: weg = new runway_t(file); break; } if(weg) { if(get_typ()==fundament) { // remove this (but we can not correct the other ways, since possibly not yet loaded) dbg->error("grund_t::rdwr()","removing way from foundation at %i,%i",pos.x,pos.y); // we do not delete them, to keep maintenance costs correct } else { assert((flags&has_way2)==0); // maximum two ways on one tile ... weg->set_pos(pos); if(owner_n!=-1) { weg->set_owner(welt->get_player(owner_n)); } objlist.add(weg); if(flags&has_way1) { flags |= has_way2; } flags |= has_way1; } } } } while(wtyp != invalid_wt); flags |= dirty; } else { // saving all ways ... if (weg_t* const w = get_weg_nr(0)) { file->wr_obj_id(w->get_waytype()); w->rdwr(file); } if (weg_t* const w = get_weg_nr(1)) { file->wr_obj_id(w->get_waytype()); w->rdwr(file); } file->wr_obj_id(-1); // Way end } // all objects on this tile objlist.rdwr(file, get_pos()); // need to add/remove crossing for old games and init logic ... if (file->is_loading() && has_two_ways()) { const weg_t* w1 = ((weg_t*)obj_bei(0)); const weg_t* w2 = ((weg_t*)obj_bei(1)); if(w1->needs_crossing(w2->get_desc())){ if (!get_crossing()) { const crossing_desc_t* cr_desc = crossing_logic_t::get_crossing(w1->get_waytype(), w2->get_waytype(), w1->get_max_speed(), w2->get_max_speed(), 0); if (cr_desc == NULL) { dbg->warning("crossing_t::rdwr()", "requested for waytypes %i and %i not available, try to load object without timeline", w1->get_waytype(), w2->get_waytype()); cr_desc = crossing_logic_t::get_crossing(w1->get_waytype(), w2->get_waytype(), 0, 0, 0); } if (cr_desc == 0) { dbg->fatal("crossing_t::crossing_t()", "requested for waytypes %i and %i but nothing defined!", w1->get_waytype(), w2->get_waytype()); } crossing_t* cr = new crossing_t(w1->get_owner(), pos, cr_desc, ribi_t::is_straight_ns(get_weg(cr_desc->get_waytype(1))->get_ribi_unmasked())); objlist.add(cr); // the logic will be added thread safe in finish_rd() } } else { if (crossing_t* cr = get_crossing()) { objlist.remove(cr); delete cr; } } } } grund_t::grund_t(koord3d pos) { this->pos = pos; flags = 0; set_image(IMG_EMPTY); // set flags = dirty; back_imageid = 0; } grund_t::~grund_t() { destroy_win((ptrdiff_t)this); // remove text from table set_text(NULL); if(flags&is_halt_flag) { get_halt()->rem_grund(this); } } void grund_t::sort_trees() { if (get_typ() != boden) { return; } uint8 trees = 0, offset = 0; for( int i=0; iget_typ() == obj_t::baum) { trees++; offset = i; } } if(trees > 1) { objlist.sort_trees(offset-trees+1u, trees); } } void grund_t::rotate90() { pos.rotate90( welt->get_size().y-1 ); slope = slope_t::rotate90( slope ); // then rotate the things on this tile if (obj_count() == 254) { dbg->warning("grund_t::rotate90()", "Too many stuff on (%s)", pos.get_str()); } objlist.rotate90_moving(); uint8 trees = 0, offset = 0; for( uint8 i=0; irotate90(); if (obj_bei(i)->get_typ() == obj_t::baum) { trees++; offset = i; } } // if more than one tree on a tile .. resort since offsets changed if(trees > 1) { objlist.sort_trees(offset-trees+1u, trees); } } // after processing the last tile, we recalculate the hashes of the ground texts void grund_t::finish_rotate90() { inthashtable_tpl ground_texts_rotating; // first get the old hashes for(auto iter : ground_texts) { koord3d k = get_ground_koord3d_key( iter.key ); k.rotate90( welt->get_size().y-1 ); ground_texts_rotating.put( get_ground_text_key(k), iter.value ); } ground_texts.clear(); // then transfer all rotated texts for(auto iter : ground_texts_rotating) { ground_texts.put(iter.key, iter.value); } ground_texts_rotating.clear(); } void grund_t::enlarge_map( sint16, sint16 /*new_size_y*/ ) { inthashtable_tpl ground_texts_enlarged; // we have recalculate the keys for(auto iter : ground_texts) { koord3d k = get_ground_koord3d_key( iter.key ); ground_texts_enlarged.put( get_ground_text_key(k), iter.value ); } ground_texts.clear(); // then transfer all texts back for(auto iter : ground_texts_enlarged) { ground_texts.put(iter.key, iter.value); } ground_texts_enlarged.clear(); } // moves all objects from the old to the new grund_t void grund_t::take_obj_from(grund_t* other_gr) { // transfer all things while( other_gr->obj_count() ) { objlist.add( other_gr->obj_remove_top() ); } // transfer the way flags if(other_gr->get_flag(has_way1)) { flags |= has_way1; other_gr->clear_flag(has_way1); } if(other_gr->get_flag(has_way2)) { flags |= has_way2; other_gr->clear_flag(has_way2); } } void grund_t::open_info_window() { if(env_t::ground_info || hat_wege()) { if (hat_wege()) { } create_win(new grund_info_t(this), w_info, (ptrdiff_t)this); } } void grund_t::info(cbuffer_t& buf) const { if(!is_water()) { if(flags&has_way1) { // bridges / tunnels only carry dummy ways if (!ist_tunnel() && !ist_bruecke()) { translator::get_obj_info(buf, get_weg_nr(0)->get_name()); } obj_bei(0)->info(buf); // creator of bridge or tunnel graphic const char* maker = NULL; if (ist_tunnel()) { if (tunnel_t* tunnel = find(1)) { maker = tunnel->get_desc()->get_copyright(); } } if (ist_bruecke()) { if (bruecke_t* bridge = find(1)) { maker = bridge->get_desc()->get_copyright(); } } if (maker) { buf.printf(translator::translate("Constructed by %s"), maker); buf.append("\n\n"); } #if DEBUG if (get_weg_nr(0)->get_waytype() == road_wt && get_weg_nr(0)->hat_gehweg()) { buf.append("Has sidewalk (cityroad)\n\n"); } #endif // second way if(flags&has_way2) { //translator::get_obj_info(buf, get_weg_nr(0)->get_name()) // might get too long ... buf.append(translator::translate(get_weg_nr(1)->get_name())); buf.append("\n"); obj_bei(1)->info(buf); buf.append("\n"); if(crossing_t* crossing = get_crossing()) { crossing->info(buf); } } } buf.append(translator::translate(ground_desc_t::get_climate_name_from_bit(welt->get_climate(get_pos().get_2d())))); } #if MSG_LEVEL >= 4 buf.printf("\nflags $%0X", flags ); buf.printf("\n\npos: (%s)",pos.get_str()); buf.printf("\nslope: %i",get_grund_hang()); buf.printf("\nback0: %i",abs(back_imageid)%11); buf.printf("\nback1: %i",(abs(back_imageid)/11)+11); if( get_weg_nr(0) ) { buf.printf("\nway slope %i", (int)get_weg_hang() ); } if(get_weg_ribi_unmasked(water_wt)) { buf.printf("\nwater ribi: %i",get_weg_ribi_unmasked(water_wt)); } if(is_water()) { buf.printf("\ncanal ribi: %i", ((const wasser_t*)this)->get_canal_ribi()); } buf.printf("\ndraw_as_obj= %i",(flags&draw_as_obj)!=0); #endif } void grund_t::set_halt(halthandle_t halt) { bool add = halt.is_bound(); if( add ) { // ok, we want to add a stop: first check if it can apply to water if( get_weg_ribi(water_wt) || is_water() || (ist_karten_boden() && welt->get_climate(pos.get_2d())==water_climate) ) { add = (halt->get_station_type() & haltestelle_t::dock) > 0; } } // then add or remove halt flag // and record the halt if( add ) { this_halt = halt; flags |= is_halt_flag|dirty; } else { this_halt = halthandle_t(); flags &= ~is_halt_flag; flags |= dirty; } } halthandle_t grund_t::get_halt() const { return (flags&is_halt_flag) ? this_halt : halthandle_t(); } // ----------------------- image calculation stuff from here ------------------ void grund_t::calc_image() { // will automatically recalculate ways ... objlist.calc_image(); // since bridges may alter images of ways, this order is needed! calc_image_internal( false ); } void grund_t::set_underground_mode(const uint8 ugm, const sint8 level) { underground_mode = ugm; switch(ugm) { case ugm_all: underground_level = -128; break; case ugm_level: underground_level = level; break; case ugm_none: default: underground_mode = ugm_none; underground_level = 127; } } image_id grund_t::get_back_image(int leftback) const { if(back_imageid==0) { return IMG_EMPTY; } uint16 back_image = abs(back_imageid); back_image = leftback ? (back_image / grund_t::WALL_IMAGE_COUNT) + grund_t::WALL_IMAGE_COUNT : back_image % grund_t::WALL_IMAGE_COUNT; if(back_imageid<0) { return ground_desc_t::fundament->get_image(back_image); } else { return ground_desc_t::slopes->get_image(back_image); } } // with double height ground tiles! // can also happen with single height tiles static inline uint8 get_back_image_from_diff(sint8 h1, sint8 h2) { sint8 min_diff = min( h1, h2 ); while( min_diff > 2 || (min_diff > 0 && h1 != h2) ) { h1 -= min_diff > 1 ? 2 : 1; h2 -= min_diff > 1 ? 2 : 1; min_diff -= 2; } if(h1*h2<0) { // middle slop of double height return h1<0 ? 9 : 10; } else { return (h1>0?h1:0)+(h2>0?h2:0)*3; } } /** * if ground is deleted mark the old spot as dirty */ void grund_t::mark_image_dirty() const { // see obj_t::mark_image_dirty if (imageid != IMG_EMPTY) { const scr_coord scr_pos = welt->get_viewport()->get_screen_coord(koord3d(pos.get_2d(), get_disp_height())); gfx->mark_img_dirty(imageid, scr_pos.x, scr_pos.y); } } // artificial walls from here on ... void grund_t::calc_back_image(const sint8 hgt, const slope_t::type slope_this) { // full underground mode or not ground -> no back image, no need for draw_as_obj if( underground_mode == ugm_all || !ist_karten_boden() ) { clear_flag(grund_t::draw_as_obj); this->back_imageid = 0; return; } // store corner heights sw, nw, ne scaled to screen dimensions const sint16 scale_z_step = tile_raster_scale_y(TILE_HEIGHT_STEP,64); const sint16 scale_y_step = 64/2; sint16 corners[grund_t::BACK_CORNER_COUNT] = {(sint16)(scale_z_step*(hgt + corner_sw(slope_this))), (sint16)(scale_z_step*(hgt + corner_nw(slope_this))), (sint16)(scale_z_step*(hgt + corner_ne(slope_this)))}; sint16 corners_add[grund_t::BACK_CORNER_COUNT] = {0,0,0}; // extra height of possible back-image // now calculate back image sint8 back_imageid=0; bool is_building = get_typ()==grund_t::fundament; const bool isvisible = is_visible(); bool fence[grund_t::BACK_WALL_COUNT] = {false, false}; const koord k = get_pos().get_2d(); clear_flag(grund_t::draw_as_obj); weg_t const* w; if( ( (w = get_weg_nr(0)) && w->get_desc()->is_draw_as_obj() ) || ( (w = get_weg_nr(1)) && w->get_desc()->is_draw_as_obj() ) ) { set_flag(grund_t::draw_as_obj); } for( size_t i=0; ilookup_kartenboden(k + koord::nesw[(i+3)&3]) ) { const uint8 back_height = min(corner_nw(slope_this),(i==0?corner_sw(slope_this):corner_ne(slope_this))); const sint16 left_hgt=gr->get_disp_height()-back_height; const slope_t::type slope=gr->get_disp_slope(); const uint8 corner_a = (i==0?corner_sw(slope_this):corner_nw(slope_this))-back_height; const uint8 corner_b = (i==0?corner_nw(slope_this):corner_ne(slope_this))-back_height; sint8 diff_from_ground_1 = left_hgt+(i==0?corner_se(slope):corner_sw(slope))-hgt; sint8 diff_from_ground_2 = left_hgt+(i==0?corner_ne(slope):corner_se(slope))-hgt; if (underground_mode==ugm_level) { const bool gr_is_visible = gr->is_visible(); // if exactly one of (this) and (gr) is visible, show full walls if ( isvisible && !gr_is_visible){ diff_from_ground_1 += 1; diff_from_ground_2 += 1; set_flag(grund_t::draw_as_obj); fence[i] = corner_a==corner_b; } else if ( !isvisible && gr_is_visible){ diff_from_ground_1 = max(diff_from_ground_1, 1); diff_from_ground_2 = max(diff_from_ground_2, 1); } // avoid walls that cover the tunnel mounds if ( gr_is_visible && (gr->get_typ()==grund_t::tunnelboden) && ist_karten_boden() && gr->get_pos().z==underground_level && corner_se( gr->get_grund_hang() ) > 0 /* se corner */) { diff_from_ground_1 = 0; diff_from_ground_2 = 0; } if ( isvisible && (get_typ()==grund_t::tunnelboden) && ist_karten_boden() && pos.z==underground_level && corner_nw( get_grund_hang() ) > 0 /* nw corner */) { if ( (i==0) ^ (corner_sw( get_grund_hang() )==0) ) { diff_from_ground_1 = 0; diff_from_ground_2 = 0; } } } // up slope hiding something ... if(diff_from_ground_1-corner_a<0 || diff_from_ground_2-corner_b<0) { if( corner_a==corner_b ) { // ok, we need a fence here, if there is not a vertical bridgehead weg_t const* w; fence[i] = !(w = get_weg_nr(0)) || ( !(w->get_ribi_unmasked() & ribi_t::nesw[(i+3)&3]) && (!(w = get_weg_nr(1)) || !(w->get_ribi_unmasked() & ribi_t::nesw[(i+3)&3])) ); // no fences between water tiles or between invisible tiles if( fence[i] && ( (is_water() && gr->is_water()) || (!isvisible && !gr->is_visible()) ) ) { fence[i] = false; } } } // any height difference AND something to see? if( (diff_from_ground_1-corner_a>0 || diff_from_ground_2-corner_b>0) && (diff_from_ground_1>0 || diff_from_ground_2>0) ) { back_imageid += get_back_image_from_diff( diff_from_ground_1, diff_from_ground_2 )*(i==0?1:grund_t::WALL_IMAGE_COUNT); is_building |= gr->get_typ()==grund_t::fundament; } // update corner heights if (diff_from_ground_1 > corner_a) { corners_add[i] = max(corners_add[i], scale_z_step * (diff_from_ground_1-corner_a)); } if (diff_from_ground_2 > corner_b) { corners_add[i+1] = max(corners_add[i+1], scale_z_step * (diff_from_ground_2 - corner_b)); } } } for(uint i=0; i
tag while (*tail) { if (*lead == '<') { bool endtag = false; if (lead[1] == '/') { endtag = true; lead++; tail++; } // parse a tag (not allowed to exceed sizeof(word) letters) for (uint i = 0; *lead != '>' && *lead > 0 && i+2 < sizeof(word); i++) { lead++; } strncpy(word, (const char*)tail + 1, lead - tail - 1); word[lead - tail - 1] = '\0'; lead++; if (word[0] == 'p' || (word[0] == 'b' && word[1] == 'r')) { // unlike http, we can have as many newlines as we like att = ATT_NEWLINE; } else if (word[0] == 'a') { if (!endtag) { att = ATT_A_START; // search for href attributes // .. ignore any number of spaces // .. accept link string enclosed by " and ' // skip a and ' ' char* start = word; while(*start == 'a' || *start == ' ') start++; start = const_cast( strstart(start, "href") ); if (start) { // skip ",=, and ' ' while(*start == '"' || *start == ' ' || *start == '=' || *start == '\'') start++; char *end = start; // find first ',", terminate string there while(*end && *end != '"' && *end != '\'') end++; *end = 0; param = start; } else { param = ""; } link_it = true; } else { if (link_it) { att = ATT_A_END; links.append(hyperlink_t(param)); link_it = false; } else { // ignore closing without opening att = ATT_UNKNOWN; } } } else if (word[0] == 'i' && word[1] == 'm' && word[2] == 'g') { char* start = word + 3; if (!endtag) { att = ATT_IMG_START; // search for src attributes // .. ignore any number of spaces // .. accept link string enclosed by " and ' // skip a and ' ' while (*start == ' ') start++; start = const_cast( strstart(start, "src") ); if (start) { // skip ",=, and ' ' while(*start == '"' || *start == ' ' || *start == '=' || *start == '\'') start++; char *end = start; // find first ',", terminate string there while(*end && *end != '"' && *end != '\'') end++; *end = 0; param = start; } else { param = ""; } if (start && start[0]) { // now determine what is is if (param[0] != '#') { // full file name => unsupported for now } else switch (tolower(start[1])) { // Tag starting with # have a second letter to determine which tool/gadget image to display case 'w': images.append(image_t(skinverwaltung_t::gadget->get_image_id(atoi(start + 2)))); break; case 'g': images.append(image_t(skinverwaltung_t::tool_icons_general->get_image_id(atoi(start + 2)))); break; case 's': images.append(image_t(skinverwaltung_t::tool_icons_simple->get_image_id(atoi(start + 2)))); break; case 'd': images.append(image_t(skinverwaltung_t::tool_icons_dialoge->get_image_id(atoi(start + 2)))); break; case 't': images.append(image_t(skinverwaltung_t::tool_icons_toolbars->get_image_id(atoi(start + 2)))); break; } } } else { att = ATT_IMG_END; } } else if (word[0] == 'h' && word[1] == '1') { att = endtag ? ATT_H1_END : ATT_H1_START; } else if (word[0] == 'i') { att = endtag ? ATT_IT_END : ATT_IT_START; } else if (word[0] == 'e' && word[1] == 'm') { att = endtag ? ATT_EM_END : ATT_EM_START; } else if (word[0] == 's' && word[1] == 't') { att = endtag ? ATT_STRONG_END : ATT_STRONG_START; } else if (!endtag && strcmp(word, "title") == 0) { // title tag const unsigned char* title_start = lead; // parse title tag (again, enforce 511 limit) for (int i = 0; *lead != '<' && *lead > 0 && i < 511; i++) { lead++; } strncpy(title, (const char*)title_start, lead - title_start); title[lead - title_start] = '\0'; // close title tag (again, enforce 511 limit) for (int i = 0; *lead != '>' && *lead > 0 && i < 511; i++) { lead++; } if (*lead == '>') { lead++; } att = ATT_UNKNOWN; } else { // ignore all unknown att = ATT_UNKNOWN; } // end of commands } else if( lead[0]=='&' ) { if( lead[2]=='t' && lead[3]==';' ) { // either gt or lt strcpy( word, lead[1]=='l' ? "<" : ">" ); lead += 4; } else if( lead[1]=='#' ) { // decimal number word[0] = atoi( (const char *)lead+2 ); word[1] = 0; while( *lead++!=';' ) { } } else { // only copy ampersand strcpy( word, "&" ); lead ++; } att = *lead<=32 ? ATT_NONE : ATT_NO_SPACE; } else { // parse a word (and obey limits) att = ATT_NONE; for( uint i = 0; *lead != '<' && (*lead > 32 || (i==0 && *lead==32)) && i+1 < sizeof(word) && *lead != '&'; i++) { if( *lead>128 ) { size_t len = 0; utf32 symbol = utf8_decoder_t::decode(lead, len); if( symbol == 0x3000 ) { // space ... break; } lead += len; i += len; if( symbol == 0x3001 || symbol == 0x3002 ) { att = ATT_NO_SPACE; // CJK full stop, comma, space break; } // every CJK symbol could be used to break, so break after 10 characters if( symbol >= 0x2E80 && symbol <= 0xFE4F && i>6 ) { att = ATT_NO_SPACE; break; } } else { lead++; } } strncpy(word, (const char*)tail, lead - tail); if( *lead>32 && word[0]!=32 ) { att = ATT_NO_SPACE; } word[lead - tail] = '\0'; if( *word==0 ) { // do not add empty strings att = ATT_UNKNOWN; } } if( att != ATT_UNKNOWN ) { // only add know commands nodes.append(node_t(word, att)); } if( att==ATT_UNKNOWN || att==ATT_NONE || att==ATT_NO_SPACE || att==ATT_NEWLINE ) { // skip white spaces while (*lead <= 32 && *lead > 0) { lead++; } // skip wide spaces utf8 const *lead_search = lead; while( utf8_decoder_t::decode(lead_search) == 0x3000 ){ lead = lead_search; } } tail = lead; } dirty = true; // save size preferred_size = output(scr_size(0, 0), false, true); } const char* gui_flowtext_intern_t::get_title() const { return title; } void gui_flowtext_intern_t::set_size(scr_size size_par) { gui_component_t::set_size(size_par); // update preferred_size preferred_size = output(scr_size(0, 0), false, true); } /** * preferred size of text: * * get_preferred_size().w = max(width, maximal word length) * get_preferred_size().h = displayed height */ scr_size gui_flowtext_intern_t::get_preferred_size() const { return preferred_size; // cached result of output(scr_size(0, 0), false, true); } /** * wider than current width */ scr_size gui_flowtext_intern_t::get_text_size() { return output(scr_size(0, 0), false, false); } void gui_flowtext_intern_t::draw(scr_coord offset) { offset += pos; if(offset!=last_offset) { dirty = true; last_offset = offset; } output(offset, true); } scr_size gui_flowtext_intern_t::output(scr_coord offset, bool doit, bool return_max_width) { const int width = size.w-D_MARGIN_LEFT-D_MARGIN_RIGHT; slist_tpl::iterator link = links.begin(); slist_tpl::iterator image = images.begin(); int xpos = 0; int ypos = 0; PIXVAL color = SYSCOL_TEXT; PIXVAL double_color = SYSCOL_TEXT_SHADOW; bool double_it = false; bool link_it = false; // true, if currently underlining for a link int extra_pixel = 0; // extra pixel before next line int extra_pixels = 0; // extra pixels for image display int extra_x_pixels = 0; // extra pixels in x dimension for image display int last_link_x = 0; // at this position ye need to continue underline drawing int max_width = width; int text_width = width; const int space_width = gfx->calc_text_width(" "); for(node_t const& i : nodes) { switch (i.att) { case ATT_NONE: case ATT_NO_SPACE: { int nxpos = xpos + gfx->calc_text_width(i.text.c_str()); if (nxpos >= text_width) { text_width = nxpos; } // too wide if( nxpos >= width ) { if (nxpos - xpos > max_width) { // word too long max_width = nxpos-xpos; } nxpos -= xpos; // now word length, new xpos after linebreak if (xpos > 0) { if( xpos!=last_link_x && link_it ) { if( doit ) { // close the link gfx->draw_rect_clipped( offset.x + last_link_x + D_MARGIN_LEFT, ypos + offset.y + LINESPACE-1, xpos-last_link_x, 1, color, false CLIP_NUM_DEFAULT); } extra_pixel = 1; } if ( (extra_x_pixels + nxpos) > max_width ) { ypos += extra_pixels; extra_pixels = 0; extra_x_pixels = 0; } if ( extra_pixels > 0 ) { xpos = extra_x_pixels; nxpos += extra_x_pixels; extra_pixels -= LINESPACE+extra_pixel; } else { xpos = 0; extra_pixels = 0; } last_link_x = extra_x_pixels; ypos += LINESPACE+extra_pixel; extra_pixel = 0; } } if( i.att == ATT_NONE ) { // add trailing space nxpos += space_width; } if (doit) { if (double_it) { gfx->draw_text_clipped(offset.x + xpos + 1 + D_MARGIN_LEFT, offset.y + ypos + 1, i.text.c_str(), 0, double_color, false); extra_pixel |= 1; } scr_coord_val width = gfx->draw_text_clipped(offset.x + xpos + D_MARGIN_LEFT, offset.y + ypos, i.text.c_str(), 0, color, false); if( link_it ) { gfx->draw_rect_clipped( offset.x + last_link_x + D_MARGIN_LEFT, ypos + offset.y + LINESPACE-1, (xpos+width)-last_link_x, 1, color, false CLIP_NUM_DEFAULT); last_link_x = xpos+width; } } if( link_it ) { extra_pixel = 1; } xpos = nxpos; break; } case ATT_NEWLINE: xpos = 0; if( last_link_xdraw_rect_clipped( offset.x + last_link_x + D_MARGIN_LEFT, ypos + offset.y + LINESPACE-1, xpos-last_link_x, 1, color, false CLIP_NUM_DEFAULT); } extra_pixel = 1; } ypos += LINESPACE + extra_pixel + extra_pixels; extra_x_pixels=0; extra_pixels=0; last_link_x = 0; extra_pixel = 0; break; case ATT_A_START: color = SYSCOL_TEXT_TITLE; // link == links.end() if there is an endtag is missing if (link!=links.end()) { link->tl.x = xpos; link->tl.y = ypos; last_link_x = xpos; link_it = true; } break; case ATT_A_END: link->br.x = xpos; link->br.y = ypos + LINESPACE; ++link; link_it = false; color = SYSCOL_TEXT; break; case ATT_H1_START: color = SYSCOL_TEXT_TITLE; double_it = true; break; case ATT_H1_END: double_it = false; if(doit) { gfx->draw_rect_clipped(offset.x + 1 + D_MARGIN_LEFT, offset.y + ypos + LINESPACE, xpos, 1, color, false CLIP_NUM_DEFAULT); gfx->draw_rect_clipped(offset.x + D_MARGIN_LEFT, offset.y + ypos + LINESPACE-1, xpos, 1, double_color, false CLIP_NUM_DEFAULT); } xpos = 0; extra_pixel = 0; ypos += LINESPACE+2; color = SYSCOL_TEXT; break; case ATT_EM_START: color = SYSCOL_TEXT_HIGHLIGHT; break; case ATT_EM_END: color = SYSCOL_TEXT; break; case ATT_IT_START: color = SYSCOL_TEXT_HIGHLIGHT; double_it = true; break; case ATT_IT_END: color = SYSCOL_TEXT; double_it = false; break; case ATT_STRONG_START: if( !double_it ) { color = SYSCOL_TEXT_STRONG; } break; case ATT_STRONG_END: if( !double_it ) { color = SYSCOL_TEXT; } break; case ATT_IMG_START: if (image != images.end()) { //display image const scr_rect area = gfx->get_base_image_offset(image->id); if ( xpos + area.w > max_width && xpos != 0 ) { // New Line if image is too large to fit on line (unless already at new line) xpos=0; ypos += LINESPACE + extra_pixel + extra_pixels; extra_pixel = 0; extra_pixels = 0; extra_x_pixels = 0; } if ( doit ) { // display_base_img calls display_color_img if zoom level is 1:1; // this will display the scaled tool image, not the original one // and therefore causes graphical glitches when zooming in/out. // Force displaying non-scaled tool image instead. gfx->fit_img_to_width(image->id, area.w); gfx->draw_base_img(image->id, offset.x + xpos + 1 - area.x + D_MARGIN_LEFT, offset.y + ypos + area.y, 0, false, false CLIP_NUM_DEFAULT); } xpos += area.w+2; if ( extra_pixels < area.h - LINESPACE ) { extra_pixels = area.h - LINESPACE; // Set extra pixels for new line based on height of image. } image++; } break; case ATT_IMG_END: break; default: break; } } ypos += LINESPACE; if(dirty) { gfx->mark_rect_dirty_wc( offset.x + D_MARGIN_LEFT, offset.y, offset.x+max_width + D_MARGIN_LEFT, offset.y+ypos ); dirty = false; } return scr_size( (return_max_width ? max_width : text_width)+D_MARGIN_LEFT+D_MARGIN_RIGHT, ypos); } bool gui_flowtext_intern_t::infowin_event(const event_t* ev) { if (IS_LEFTRELEASE(ev)) { // scan links for hit const scr_coord evpos = ev->click_pos; // - get_pos(); for(hyperlink_t const& link : links) { if( link.tl.y+LINESPACE == link.br.y ) { if( link.tl.x <= evpos.x && evpos.x < link.br.x && link.tl.y <= evpos.y && evpos.y < link.br.y ) { call_listeners((void const*)link.param.c_str()); return true; } } else { // multi lined box => more difficult if( link.tl.x <= evpos.x && evpos.x < get_size().w && link.tl.y <= evpos.y && evpos.y < link.tl.y+LINESPACE ) { // in top line call_listeners((void const*)link.param.c_str()); return true; } else if( 0 <= evpos.x && evpos.x < link.br.x && link.br.y-LINESPACE <= evpos.y && evpos.y < link.br.y ) { // in last line call_listeners((void const*)link.param.c_str()); return true; } else if( 0 <= evpos.x && evpos.x < get_size().w && link.tl.y+LINESPACE <= evpos.y && evpos.y < link.br.y-LINESPACE ) { // line in between call_listeners((void const*)link.param.c_str()); return true; } } } } return false; } /** Implementation of the wrapping class **/ gui_flowtext_t::gui_flowtext_t() : gui_scrollpane_t(NULL, true, true) { flowtext = new gui_flowtext_intern_t(); set_component(flowtext); flowtext->add_listener(this); } gui_flowtext_t::~gui_flowtext_t() { delete flowtext; flowtext = NULL; } void gui_flowtext_t::set_text(const char* text) { // reset position if text is changed set_scroll_position(0, 0); flowtext->set_text(text); } const char* gui_flowtext_t::get_title() const { return flowtext->get_title(); } void gui_flowtext_t::set_size(scr_size size_par) { gui_scrollpane_t::set_size(size_par); // compute and set height flowtext->set_size( flowtext->get_preferred_size() ); // recalc twice to get correct scrollbar visibility recalc_sliders(get_size()); recalc_sliders(get_size()); } scr_size gui_flowtext_t::get_preferred_size() const { return flowtext->get_preferred_size(); } bool gui_flowtext_t::action_triggered(gui_action_creator_t*, value_t extra) { call_listeners(extra); return true; } simutrans-124.5/src/simutrans/gui/components/gui_flowtext.h000066400000000000000000000017611520675460500242470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_FLOWTEXT_H #define GUI_COMPONENTS_GUI_FLOWTEXT_H #include "action_listener.h" #include "gui_action_creator.h" #include "gui_scrollpane.h" class gui_flowtext_intern_t; /** * A component for floating text wrapped into a scrollpane. */ class gui_flowtext_t : public gui_action_creator_t, public action_listener_t, public gui_scrollpane_t { gui_flowtext_intern_t* flowtext; using gui_scrollpane_t::set_component; public: gui_flowtext_t(); ~gui_flowtext_t(); /** * Sets the text to display. */ void set_text(const char* text); const char* get_title() const; /** * Updates size and preferred_size. */ void set_size(scr_size size_par) OVERRIDE; /** * Computes and returns preferred size. * Depends on current width. */ scr_size get_preferred_size() const; bool action_triggered(gui_action_creator_t *comp, value_t extra) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_image.cc000066400000000000000000000041461520675460500236130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_image.h" #include "../gui_frame.h" gui_image_t::gui_image_t( const image_id i, const uint8 p, control_alignment_t alignment_par, bool remove_offset_enabled ) : alignment(alignment_par), player_nr(p), remove_offset(0,0), remove_enabled(remove_offset_enabled), color_index(0) { set_image(i,remove_offset_enabled); } scr_size gui_image_t::get_min_size() const { if( id != IMG_EMPTY ) { scr_rect r = gfx->get_base_image_offset(id); if (remove_enabled) { return scr_size(r.w, r.h); } else { // FIXME assert(0); return scr_size(r.x+r.w, r.y+r.h); } } return gui_component_t::get_min_size(); } void gui_image_t::set_size( scr_size size_par ) { if( id != IMG_EMPTY ) { scr_rect r = gfx->get_base_image_offset(id); if( remove_enabled ) { remove_offset = scr_coord(-r.x,-r.y); } size_par = scr_size( r.x+r.w+remove_offset.x, r.y+r.h+remove_offset.y ); } gui_component_t::set_size(size_par); } void gui_image_t::set_image( const image_id i, bool remove_offsets ) { id = i; remove_enabled = remove_offsets; if( id ==IMG_EMPTY ) { remove_offset = scr_coord(0,0); remove_enabled = false; } set_size( size ); } /** * Draw the component */ void gui_image_t::draw( scr_coord offset ) { if( id!=IMG_EMPTY ) { scr_rect r = gfx->get_base_image_offset(id); switch (alignment) { case ALIGN_RIGHT: offset.x += size.w - r.w + remove_offset.x; break; case ALIGN_BOTTOM: offset.y += size.h - r.h + remove_offset.y; break; case ALIGN_CENTER_H: offset.x += D_GET_CENTER_ALIGN_OFFSET(r.w, size.w); break; case ALIGN_CENTER_V: offset.y += D_GET_CENTER_ALIGN_OFFSET(r.h, size.h); break; } if (color_index) { gfx->draw_base_img_blend(id , pos.x+offset.x+remove_offset.x, pos.y+offset.y+remove_offset.y, player_nr, color_index, false, true CLIP_NUM_DEFAULT); } else { gfx->draw_base_img( id, pos.x+offset.x+remove_offset.x, pos.y+offset.y+remove_offset.y, (sint8)player_nr, false, true CLIP_NUM_DEFAULT); } } } simutrans-124.5/src/simutrans/gui/components/gui_image.h000066400000000000000000000022731520675460500234540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_IMAGE_H #define GUI_COMPONENTS_GUI_IMAGE_H #include "../../display/simimg.h" #include "../../display/simgraph.h" #include "gui_component.h" /** * Just displays an image */ class gui_image_t : public gui_component_t { control_alignment_t alignment; image_id id; uint16 player_nr; scr_coord remove_offset; bool remove_enabled; FLAGGED_PIXVAL color_index; public: gui_image_t( const image_id i=IMG_EMPTY, const uint8 p=0, control_alignment_t alignment_par = ALIGN_NONE, bool remove_offset = false ); public: void set_player_nr(uint8_t player) { player_nr = player; } void set_size( scr_size size_par ) OVERRIDE; void set_image( const image_id i, bool remove_offsets = false ); void enable_offset_removal(bool remove_offsets) { set_image(id,remove_offsets); } void set_transparent(FLAGGED_PIXVAL c) { color_index = c; } //// Draw the component void draw( scr_coord offset ) OVERRIDE; scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE { return get_min_size(); } }; #endif simutrans-124.5/src/simutrans/gui/components/gui_image_list.cc000066400000000000000000000077561520675460500246600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simdebug.h" #include "gui_image_list.h" #include "../../display/simgraph.h" #include "../../simevent.h" #include "../../simcolor.h" gui_image_list_t::gui_image_list_t(vector_tpl *images) : grid(16, 16), placement(16, 16) { this->images = images; player_nr = 0; max_rows = -1; max_width = -1; } /** * Events are notified to GUI components via this method */ bool gui_image_list_t::infowin_event(const event_t *ev) { int sel_index = index_at(scr_coord(0,0)-pos, ev->mouse_pos.x, ev->mouse_pos.y); if( sel_index != -1 && (IS_LEFTDBLCLK(ev) || IS_LEFTRELEASE(ev)) ) { value_t p; p.i = sel_index; call_listeners( p ); return true; } return false; } int gui_image_list_t::index_at(scr_coord parent_pos, int xpos, int ypos) const { xpos -= parent_pos.x + pos.x + BORDER; ypos -= parent_pos.y + pos.y + BORDER; if(xpos>=0 && ypos>=0 && xposget_count() && (*images)[index]->image != IMG_EMPTY) { return index; } } return -1; } void gui_image_list_t::draw(scr_coord parent_pos) { const int columns = (size.w - 2 * BORDER) / grid.x; // sel_index should come from infowin_event, but it is not sure? int sel_index = index_at(parent_pos, get_mouse_pos().x, get_mouse_pos().y); // Show available wagon types int xmin = parent_pos.x + pos.x + BORDER; int ymin = parent_pos.y + pos.y + BORDER; int xmax = xmin + columns * grid.x; int xpos = xmin; int ypos = ymin; for(image_data_t* const& iptr : *images) { image_data_t const& idata = *iptr; if(idata.count>=0) { // display mark if(idata.lcolor!=EMPTY_IMAGE_BAR) { gfx->draw_rect_clipped(xpos + 1, ypos + grid.y - 5, grid.x/2 - 1, 4, idata.lcolor, true CLIP_NUM_DEFAULT); } if(idata.rcolor!=EMPTY_IMAGE_BAR) { gfx->draw_rect_clipped(xpos + grid.x/2, ypos + grid.y - 5, grid.x - grid.x/2 - 1, 4, idata.rcolor, true CLIP_NUM_DEFAULT); } if (sel_index-- == 0) { gfx->draw_box3d_clipped(xpos, ypos, grid.x, grid.y, gfx->palette_lookup(MN_GREY4), gfx->palette_lookup(MN_GREY0)); } // Get image data scr_rect r = gfx->get_base_image_offset(idata.image); // calculate image offsets r.y = -r.y + (grid.y-r.h) - 6; // align to bottom mark r.x = -r.x + 2; // Add 2 pixel margin //display_base_img(idata.image, xpos + placement.x, ypos + placement.y, player_nr, false, true); gfx->draw_base_img(idata.image, xpos + r.x, ypos + r.y, player_nr, false, true CLIP_NUM_DEFAULT); // If necessary, display a number: if(idata.count > 0) { char text[20]; sprintf(text, "%d", idata.count); // Let's make a black background to ensure visibility for(int iy = -3; iy < 0; iy++) { for(int ix = 1; ix < 4; ix++) { gfx->draw_text_clipped(xpos + ix, ypos + iy, text, ALIGN_LEFT, gfx->palette_lookup(COL_BLACK), true); } } // Display the number white on black gfx->draw_text_clipped(xpos + 2, ypos - 2, text, ALIGN_LEFT, gfx->palette_lookup(COL_WHITE), true); } } // advance x, y to next position xpos += grid.x; if(xpos == xmax) { xpos = xmin; ypos += grid.y; } } } scr_size gui_image_list_t::get_min_size() const { return get_max_size(); } scr_size gui_image_list_t::get_max_size() const { if (max_rows > 0) { // this many images per column sint32 cols = (images->get_count() + max_rows - 1) / max_rows; return scr_size(cols*grid.x + 2*BORDER, max_rows*grid.y + 2*BORDER); } else if (max_width > 0) { sint32 cols = (max_width - 2*BORDER) / grid.x; sint32 rows = (images->get_count() + cols - 1) / cols; return scr_size(cols*grid.x + 2*BORDER, rows*grid.y + 2*BORDER); } return scr_size((images->get_count()+1)*grid.x + 2*BORDER, grid.y + 2*BORDER); } simutrans-124.5/src/simutrans/gui/components/gui_image_list.h000066400000000000000000000052541520675460500245110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_IMAGE_LIST_H #define GUI_COMPONENTS_GUI_IMAGE_LIST_H #include "gui_action_creator.h" #include "gui_component.h" #include "../../tpl/vector_tpl.h" #include "../../display/simimg.h" #include "../../simcolor.h" #define EMPTY_IMAGE_BAR (255) /** * A component that represents a list of images. * * Updated! class is used only for the vehicle dialog. SO I changed some things * for the new one:: * - cannot select no-image fields any more * - numbers can be drawn ontop an images * - color bar can be added to the images */ class gui_image_list_t : public gui_action_creator_t, public gui_component_t { public: struct image_data_t { const char *text; ///< can be NULL, used to store external data image_id image; ///< the image sint16 count; ///< display this number as overlay PIXVAL lcolor; ///< color of left half of color bar, use EMPTY_IMAGE_BAR to display no bar PIXVAL rcolor; ///< color of right half of color bar, use EMPTY_IMAGE_BAR to display no bar image_data_t(const char *text_, image_id image_, sint16 count_=0, PIXVAL lcolor_=EMPTY_IMAGE_BAR, PIXVAL rcolor_=EMPTY_IMAGE_BAR) : text(text_), image(image_), count(count_), lcolor(lcolor_), rcolor(rcolor_) {} }; /** * Graphic layout: * size of borders around the whole area (there are no borders around * individual images) */ enum { BORDER = 4 }; private: vector_tpl *images; scr_coord grid; scr_coord placement; /** * Player number to obtain player color used to display the images. */ sint8 player_nr; scr_coord_val max_width; sint32 max_rows; public: /** * Constructor: takes pointer to vector with image_data_t * @param images pointer to vector of pointers to image_data_t */ gui_image_list_t(vector_tpl *images); /** * This set horizontal and vertical spacing for the images. */ void set_grid(scr_coord grid) { this->grid = grid; } /** * This set the offset for the images. */ void set_placement(scr_coord placement) { this->placement = placement; } void set_player_nr(sint8 player_nr) { this->player_nr = player_nr; } bool infowin_event(event_t const*) OVERRIDE; /** * Draw/record the picture */ void draw(scr_coord offset) OVERRIDE; /** * Looks for the image at given position. * xpos and ypos relative to parent window. */ int index_at(scr_coord parent_pos, int xpos, int ypos) const; void set_max_rows(sint32 r) { max_rows = r; } void set_max_width(scr_coord_val w) { max_width = w; } scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_label.cc000066400000000000000000000105531520675460500236070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_label.h" #include "../gui_frame.h" #include "../../dataobj/translator.h" #include "../../utils/simstring.h" #include "../simwin.h" /* * just displays a text, will be auto-translated */ static scr_coord_val separator_width = 0; static scr_coord_val large_money_width = 0; gui_label_t::gui_label_t(const char* text, PIXVAL color_, align_t align_) : align(align_), tooltip(NULL) { separator_width = gfx->calc_text_width( ",00$" ); if (get_large_money_string()) { cbuffer_t buf; buf.printf("%s$", get_large_money_string()); large_money_width = gfx->calc_text_width((const char*) buf); } else { large_money_width = 0; } set_size( scr_size( D_BUTTON_WIDTH, D_LABEL_HEIGHT ) ); init( text, scr_coord (0,0), color_, align_); shadowed = false; } scr_size gui_label_t::get_min_size() const { return scr_size( text ? gfx->calc_text_width(text) : D_BUTTON_WIDTH, D_LABEL_HEIGHT ); } scr_size gui_label_t::get_max_size() const { return align == left ? get_min_size() : scr_size(scr_size::inf.w, get_min_size().h); } void gui_label_t::set_text(const char *text, bool autosize) { if (text != NULL) { set_text_pointer(translator::translate(text), autosize); } else { set_text_pointer(NULL, false); } } void gui_label_t::set_text_pointer(const char *text_par, bool autosize) { text = text_par; if (autosize && text && *text != '\0') { set_size( scr_size( gfx->calc_text_width(text), size.h ) ); } } void gui_label_t::draw(scr_coord offset) { if( align == money_right) { if(text) { const char* separator = NULL; const char *n = text; if (get_currency_left()) { n += strlen(get_currency_string()); } const bool not_a_number = atol(n)==0 && !isdigit(*n) && *n != '-'; scr_coord right = pos + offset; if( !not_a_number ) { // find first letter of large_money_width in text if (get_large_money_string()!=NULL) { separator = strrchr(text, *(get_large_money_string()) ); if (separator) { right.x += get_size().w - large_money_width; } } // look for fraction_sep (e.g., comma) if (separator==NULL) { // everything else align at decimal separator right.x += get_size().w - separator_width; separator = strrchr(text, get_fraction_sep()); } } if(separator) { gfx->draw_text_clipped(right.x, right.y, separator, ALIGN_LEFT, color, true); if( separator!=text ) { if (shadowed) { gfx->draw_text_clipped_n(right.x+1, right.y+1, text, ALIGN_RIGHT | DT_CLIP, color_shadow, true, separator - text CLIP_NUM_DEFAULT); } gfx->draw_text_clipped_n(right.x, right.y, text, ALIGN_RIGHT | DT_CLIP, color, true, separator-text CLIP_NUM_DEFAULT); } } else { // integer or normal text if (shadowed) { gfx->draw_text_clipped(right.x + 1, right.y + 1, text, ALIGN_RIGHT | DT_CLIP, color_shadow, true); } gfx->draw_text_clipped(right.x, right.y, text, ALIGN_RIGHT, color, true); } } } else if(text) { const scr_rect area( offset+pos, size ); int a = align == left ? ALIGN_LEFT : ( align == right ? ALIGN_RIGHT : ALIGN_CENTER_H); gfx->draw_text_ellipsis_shadowed( area, text, a | DT_CLIP, color, true, shadowed, color_shadow ); } if ( tooltip && getroffen(get_mouse_pos() - offset) ) { const scr_coord tooltip_base_pos{ get_mouse_pos().x, offset.y + pos.y + size.h }; win_set_tooltip(tooltip_base_pos + TOOLTIP_MOUSE_OFFSET, tooltip, this); } } void gui_label_t::set_tooltip(const char * t) { tooltip = t; } void gui_label_buf_t::init(PIXVAL color_par, align_t align_par) { gui_label_t::init(NULL, get_pos(), color_par, align_par); buf_changed = false; } void gui_label_buf_t::update() { buffer_read = buffer_write; buffer_write.clear(); gui_label_t::set_text_pointer( (const char*)buffer_read, false /*no autoresize*/ ); buf_changed = false; } void gui_label_buf_t::draw(scr_coord offset) { if (buf_changed) { update(); } gui_label_t::draw(offset); } void gui_label_buf_t::set_min_width(scr_coord_val w) { min_width = w; } scr_size gui_label_buf_t::get_min_size() const { scr_size min_size = gui_label_t::get_min_size(); min_size.w = max(min_size.w, min_width); return min_size; } void gui_label_buf_t::append_money(double money) { buffer_write.append_money(money); set_color(money >= 0 ? MONEY_PLUS : MONEY_MINUS); } simutrans-124.5/src/simutrans/gui/components/gui_label.h000066400000000000000000000062771520675460500234610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_LABEL_H #define GUI_COMPONENTS_GUI_LABEL_H #include "gui_component.h" #include "../../simcolor.h" #include "../gui_theme.h" #include "../../simskin.h" #include "../../utils/cbuffer.h" /** * The label component * just displays a text, will be auto-translated */ class gui_label_t : virtual public gui_component_t { public: enum align_t { left, centered, right, money_right }; private: align_t align; /** * Color of the Labels */ PIXVAL color; bool shadowed; PIXVAL color_shadow; const char * text; // only for direct access of non-translatable things. Do not use! const char * tooltip; protected: using gui_component_t::init; public: gui_label_t(const char* text=NULL, PIXVAL color=SYSCOL_TEXT, align_t align=left); void init( const char* text_par, scr_coord pos_par, PIXVAL color_par=SYSCOL_TEXT, align_t align_par=left) { set_pos ( pos_par ); set_text ( text_par ); set_color( color_par ); set_align( align_par ); } /** * Sets the text to display, after translating it. */ void set_text(const char *text, bool autosize=true); /** * Sets the text without translation. */ void set_text_pointer(const char *text, bool autosize=true); /** * returns the pointer (i.e. for freeing untranslated contents) */ const char * get_text_pointer() const { return text; } /** * returns the tooltip pointer (i.e. for freeing untranslated contents) */ const char * get_tooltip_pointer() { return tooltip; } /** * Draws the component. */ void draw(scr_coord offset) OVERRIDE; /** * Sets the colour of the label */ void set_color(PIXVAL colour) { this->color = colour; } virtual PIXVAL get_color() const { return color; } /** * Toggles shadow and sets shadow color. */ void set_shadow(PIXVAL color_shadow, bool shadowed) { this->color_shadow = color_shadow; this->shadowed = shadowed; } /** * Sets the alignment of the label */ void set_align(align_t align) { this->align = align; } /** * Sets the tooltip of this component. */ void set_tooltip(const char * t); scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; }; /** * Label with own buffer. */ class gui_label_buf_t : public gui_label_t { bool buf_changed; cbuffer_t buffer_write, buffer_read; scr_coord_val min_width = 0; public: gui_label_buf_t(PIXVAL color=SYSCOL_TEXT, align_t align=left) : gui_label_t(NULL, color, align), buf_changed(true) { } void init(PIXVAL color_par=SYSCOL_TEXT, align_t align_par=left); /** * Has to be called after access to buf() is finished. * Otherwise size calculations will be off. * Called by @ref draw. */ void update(); cbuffer_t& buf() { if (!buf_changed) { buffer_write.clear(); } buf_changed = true; return buffer_write; } /** * appends money string to write buf, sets color */ void append_money(double money); void draw(scr_coord offset) OVERRIDE; void set_min_width(scr_coord_val w); scr_size get_min_size() const OVERRIDE; protected: using gui_label_t::get_text_pointer; using gui_label_t::set_text; using gui_label_t::set_text_pointer; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_location_view.h000066400000000000000000000014411520675460500252300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_LOCATION_VIEW_H #define GUI_COMPONENTS_GUI_LOCATION_VIEW_H #include "gui_world_view.h" /** * Displays a location on the world */ class location_view_t : public world_view_t { private: koord3d location; /**< The location to display. */ public: location_view_t(koord3d const location, scr_size const size) : world_view_t(size), location(location) {} /** Set the location to be displayed. */ void set_location(koord3d const l) { location = l; } void map_rotate90(sint16 const new_ysize) { location.rotate90(new_ysize); } void draw(scr_coord offset) OVERRIDE { internal_draw(offset, 0); } koord3d get_location() OVERRIDE { return location; } }; #endif simutrans-124.5/src/simutrans/gui/components/gui_map_preview.cc000066400000000000000000000004701520675460500250430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_map_preview.h" #include "../../world/simworld.h" gui_map_preview_t::gui_map_preview_t() : gui_component_t() { map_data = NULL; set_size (scr_size( MAP_PREVIEW_SIZE_X,MAP_PREVIEW_SIZE_Y )); } simutrans-124.5/src/simutrans/gui/components/gui_map_preview.h000066400000000000000000000023661520675460500247130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_MAP_PREVIEW_H #define GUI_COMPONENTS_GUI_MAP_PREVIEW_H #include "gui_component.h" #include "../../simcolor.h" #include "../../display/simgraph.h" #include "../../tpl/array2d_tpl.h" #define MAP_PREVIEW_SIZE_X ((scr_coord_val)(64)) #define MAP_PREVIEW_SIZE_Y ((scr_coord_val)(64)) /** * A map preview component * */ class gui_map_preview_t : public gui_component_t { private: array2d_tpl *map_data; public: gui_map_preview_t(); void set_map_data(array2d_tpl *map_data_par) { map_data = map_data_par; } /** * Draws the component. */ void draw(scr_coord offset) OVERRIDE { gfx->draw_box3d_clipped(pos.x + offset.x, pos.y + offset.y, size.w, size.h, gfx->palette_lookup(MN_GREY0), gfx->palette_lookup(MN_GREY4)); if(map_data) { gfx->draw_array(pos.x + offset.x + 1, pos.y + offset.y + 1, map_data->get_width(), map_data->get_height(), map_data->to_array()); } } scr_size get_min_size() const OVERRIDE { return scr_size(MAP_PREVIEW_SIZE_X, MAP_PREVIEW_SIZE_Y); } scr_size get_max_size() const OVERRIDE { return scr_size(MAP_PREVIEW_SIZE_X, MAP_PREVIEW_SIZE_Y); } }; #endif simutrans-124.5/src/simutrans/gui/components/gui_numberinput.cc000066400000000000000000000226651520675460500251070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_numberinput.h" #include "../gui_frame.h" #include "../simwin.h" #include "../../display/simgraph.h" #include "../../macros.h" #include "../../dataobj/translator.h" uint32 log10( uint32 ); // simrandom.h char gui_numberinput_t::tooltip[256]; gui_numberinput_t::gui_numberinput_t() : gui_component_t(true) { bt_left.set_typ(button_t::repeatarrowleft ); bt_left.set_pos( scr_coord(0,2) ); bt_left.add_listener(this ); textinp.set_alignment( ALIGN_RIGHT ); textinp.set_color( SYSCOL_EDIT_TEXT ); textinp.add_listener( this ); bt_right.set_typ(button_t::repeatarrowright ); bt_right.add_listener(this ); set_limits(0, 9999); textbuffer[0] = 0; // start with empty buffer value = 0; textinp.set_text(textbuffer, 20); set_increment_mode( 1 ); wrap_mode( true ); b_enabled = true; no_tooltip = false; digits = 5; set_size( scr_size( D_BUTTON_WIDTH, D_EDIT_HEIGHT ) ); } void gui_numberinput_t::set_size(scr_size size_par) { gui_component_t::set_size(size_par); textinp.set_size( scr_size( size_par.w - bt_left.get_size().w - bt_right.get_size().w - D_H_SPACE, size_par.h) ); bt_left.set_pos( scr_coord(0,(size.h-D_ARROW_LEFT_HEIGHT)/2) ); textinp.align_to( &bt_left, scr_coord( D_H_SPACE / 2, 0) ); bt_right.align_to( &textinp, scr_coord( D_H_SPACE / 2, 0) ); } scr_size gui_numberinput_t::get_max_size() const { return get_min_size(); } scr_size gui_numberinput_t::get_min_size() const { return scr_size(max_numbertext_width + D_ARROW_LEFT_WIDTH + D_ARROW_RIGHT_WIDTH + 2*D_H_SPACE, max(LINESPACE+4, max( max(D_ARROW_LEFT_HEIGHT, D_ARROW_RIGHT_HEIGHT), D_EDIT_HEIGHT)) ); } void gui_numberinput_t::set_value(sint32 new_value) { // range check value = clamp( new_value, min_value, max_value ); gui_frame_t *win = win_get_top(); if( win && win->get_focus()!=this ) { // final value should be correct, but during editing wrong values are allowed new_value = value; } // To preserve cursor position if text was edited, only set new text if changed (or empty before) if( textbuffer[0]<32 || new_value != get_text_value() ) { sprintf(textbuffer, "%d", new_value); textinp.set_text(textbuffer, 20); } textinp.set_color( value == new_value ? (b_enabled ? SYSCOL_EDIT_TEXT : SYSCOL_EDIT_TEXT_DISABLED) : gfx->palette_lookup(COL_RED) ); value = new_value; } sint32 gui_numberinput_t::get_text_value() { return (sint32)atol( textinp.get_text() ); } sint32 gui_numberinput_t::get_value() { return clamp( value, min_value, max_value ); } bool gui_numberinput_t::check_value(sint32 _value) { return (_value >= min_value) && (_value <= max_value); } void gui_numberinput_t::set_limits(sint32 _min, sint32 _max) { min_value = _min; max_value = _max; // minus sign max_numbertext_width = (min_value > 0) ? 0 : gfx->get_char_width('-'); // count digits uint32 max_abs = max_value > 0 ? max_value : -max_value; if (min_value < 0 && (uint32) (-min_value) > max_abs) { max_abs = -min_value; } // width of digits while (max_abs) { max_numbertext_width += gfx->get_number_width(); max_abs /= 10; } // enforce a min width of 5 digits if (max_numbertext_width < 5 * gfx->get_number_width()) { max_numbertext_width = 5 * gfx->get_number_width(); } } bool gui_numberinput_t::action_triggered( gui_action_creator_t *comp, value_t /* */) { if( comp == &textinp ) { // .. if enter / esc pressed set_value( get_text_value() ); if(check_value(value)) { call_listeners(value_t(value)); } } else if( comp == &bt_left || comp == &bt_right ) { // value changed and feasible sint32 new_value = (comp == &bt_left) ? get_prev_value() : get_next_value(); if( new_value!=value ) { set_value( new_value ); if(check_value(new_value)) { // check for valid change - call listeners call_listeners(value_t(value)); } } } return false; } static const sint8 load_percents[7] = { 0, 1, 5, 10, 20, 50, 100 }; sint32 gui_numberinput_t::get_next_value() { if( value>=max_value ) { // turn over return (wrapping && value==max_value) ? min_value : max_value; } switch( step_mode ) { // automatic linear case AUTOLINEAR: { sint64 diff = (sint64)max_value - (sint64)min_value; sint32 one_percent = (sint32) (diff / 100l); return clamp( value+max(1,one_percent), min_value, max_value ); } // power of 2 case POWER2: { sint32 new_value=1; for( int i=0; i<32; i++ ) { if( value<(new_value<=0; i-- ) { if( value>(new_value<=0; i-- ) { if( value-min_value > ((diff*load_percents[i])/100l) ) { return min_value+(sint32)((diff*load_percents[i])/100l); } } return min_value; } // default value is step size default: return clamp( value-step_mode, min_value, max_value ); } } // all init in one ... void gui_numberinput_t::init( sint32 value, sint32 min, sint32 max, sint32 mode, bool wrap, uint16 digits_, bool tooltip ) { set_limits( min, max ); set_value( value ); set_increment_mode( mode ); wrap_mode( wrap ); allow_tooltip(tooltip); digits = digits_; } bool gui_numberinput_t::infowin_event(const event_t *ev) { // no action if disabled if (!b_enabled) { return false; } // buttons pressed if( bt_left.getroffen(ev->click_pos) && ev->ev_code == MOUSE_LEFTBUTTON ) { event_t ev2 = *ev; ev2.move_origin(bt_left.get_pos()); return bt_left.infowin_event(&ev2); } else if( bt_right.getroffen(ev->click_pos) && ev->ev_code == MOUSE_LEFTBUTTON ) { event_t ev2 = *ev; ev2.move_origin(bt_right.get_pos()); return bt_right.infowin_event(&ev2); } else if( ev->ev_class == INFOWIN && ev->ev_code == WIN_UNTOP ) { // losing focus set_value( get_text_value() ); // just to be sure, value may be the same call_listeners(value_t(value)); return false; } else { // since button have different callback ... bool result = false; sint32 new_value = value; // mouse wheel -> fast increase / decrease if (getroffen(ev->mouse_pos + this->pos)) { if(IS_WHEELUP(ev)) { new_value = get_next_value(); result = true; } else if(IS_WHEELDOWN(ev)){ new_value = get_prev_value(); result = true; } } // catch non-number keys if( IS_KEYDOWN(ev) || value==new_value ) { // assume false input bool call_textinp = !IS_KEYBOARD(ev); // editing keys, arrows, hom/end switch (ev->ev_code) { case '-': call_textinp = min_value < 0; break; case 1: // allow Ctrl-A (select all text) to function case 3: // allow Ctrl-C (copy text to clipboard) case 8: case 9: // allow text input to handle unfocus event case 22: // allow Ctrl-V (paste text from clipboard) case 24: // allow Ctrl-X (cut text and copy to clipboard) case 127: case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case SIM_KEYCODE_LEFT: case SIM_KEYCODE_RIGHT: case SIM_KEYCODE_HOME: case SIM_KEYCODE_END: call_textinp = true; break; case SIM_KEYCODE_UP: case SIM_KEYCODE_DOWN: // next/previous choice new_value = (ev->ev_code==SIM_KEYCODE_DOWN) ? get_prev_value() : get_next_value(); } if( call_textinp ) { event_t ev2 = *ev; ev2.move_origin(textinp.get_pos()); result = textinp.infowin_event(&ev2); new_value = get_text_value(); } } // value changed and feasible if( new_value!=value ) { set_value( new_value ); if(check_value(new_value)) { // check for valid change - call listeners call_listeners(value_t(value)); result = true; } } return result; } } /** * Draw the component */ void gui_numberinput_t::draw(scr_coord offset) { scr_coord new_offset = pos+offset; bt_left.draw(new_offset); textinp.display_with_focus( new_offset, (win_get_focus()==this) ); bt_right.draw(new_offset); if(!no_tooltip && getroffen( get_mouse_pos() - offset )) { sprintf( tooltip, translator::translate("enter a value between %i and %i"), min_value, max_value ); const scr_coord tooltip_base_pos{ get_mouse_pos().x, new_offset.y + size.h }; win_set_tooltip(tooltip_base_pos + TOOLTIP_MOUSE_OFFSET, tooltip, this); } } simutrans-124.5/src/simutrans/gui/components/gui_numberinput.h000066400000000000000000000056641520675460500247510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_NUMBERINPUT_H #define GUI_COMPONENTS_GUI_NUMBERINPUT_H #include "../../simtypes.h" #include "../../display/scr_coord.h" #include "action_listener.h" #include "gui_action_creator.h" #include "gui_textinput.h" #include "gui_button.h" #include "../gui_theme.h" /** * An input field for integer numbers (with arrow buttons for dec/inc) */ class gui_numberinput_t : public gui_action_creator_t, public gui_component_t, public action_listener_t { private: bool check_value(sint32 _value); scr_coord_val max_numbertext_width; // more sophisticated increase routines sint32 get_prev_value(); sint32 get_next_value(); // transformation char* -> int sint32 get_text_value(); // the input field gui_textinput_t textinp; // arrow buttons for increasing / decr. button_t bt_left, bt_right; sint32 value; sint32 min_value, max_value; // number of digits, // used to determine min size uint16 digits; char textbuffer[20]; sint32 step_mode; bool wrapping : 1; bool b_enabled : 1; bool no_tooltip : 1; // since only the last will prevail static char tooltip[256]; public: gui_numberinput_t(); void set_size(scr_size size) OVERRIDE; // all init in one ... void init( sint32 value, sint32 min, sint32 max, sint32 mode = 1, bool wrap = true, uint16 digits = 5, bool tooltip=true); /** * sets and get the current value. * return current value (or min or max in currently set to outside value) */ sint32 get_value(); void set_value(sint32); /** * digits: length of textbuffer */ void set_limits(sint32 _min, sint32 _max); enum { AUTOLINEAR = 0, POWER2 = -1, PROGRESS = -2 }; /** * AUTOLINEAR: linear increment, scroll wheel 1% range * POWER2: 16, 32, 64, ... * PROGRESS: 0, 1, 5, 10, 25, 50, 75, 90, 95, 99, 100% of range * any other mode value: actual step size */ void set_increment_mode( sint32 m ) { step_mode = m; } // true, if the component wraps around bool wrap_mode( bool new_mode ) { bool m=wrapping; wrapping=new_mode; return m; } bool infowin_event(event_t const*) OVERRIDE; void draw(scr_coord offset) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void enable() { b_enabled = true; set_focusable(true); bt_left.enable(); bt_right.enable(); set_value(value); } void disable() { b_enabled = false; set_focusable(false); bt_left.disable(); bt_right.disable(); set_value(value); } bool enabled() const { return b_enabled; } bool is_focusable() OVERRIDE { return b_enabled && gui_component_t::is_focusable(); } void enable( bool yesno ) { if( yesno && !gui_component_t::is_focusable() ) { enable(); } else if( !yesno && gui_component_t::is_focusable() ) { disable(); } } void allow_tooltip(bool b) { no_tooltip = !b; } scr_size get_max_size() const OVERRIDE; scr_size get_min_size() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_obj_view.cc000066400000000000000000000011641520675460500243320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_obj_view.h" #include "../../vehicle/air_vehicle.h" obj_view_t::obj_view_t(obj_t const* d, scr_size const size) : world_view_t(size), obj(d) { set_size(size); } void obj_view_t::set_size(scr_size size) { sint16 max_dy_off = 5; if( obj ) { air_vehicle_t const* const plane = obj_cast(obj); if( plane ) { max_dy_off = 11; } } gui_component_t::set_size(size); world_view_t::calc_offsets(size, max_dy_off); } koord3d obj_view_t::get_location() { return obj->get_pos(); } simutrans-124.5/src/simutrans/gui/components/gui_obj_view.h000066400000000000000000000015331520675460500241740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_OBJ_VIEW_H #define GUI_COMPONENTS_GUI_OBJ_VIEW_H #include "gui_world_view.h" class obj_t; /** * Displays a thing on the world */ class obj_view_t : public world_view_t { private: obj_t const *obj; /**< The object to display */ protected: koord3d get_location() OVERRIDE; public: obj_view_t(scr_size const size) : world_view_t(size), obj(NULL) {} obj_view_t(obj_t const *d, scr_size const size); obj_t const *get_obj() const { return obj; } void set_obj( obj_t const *d ) { obj = d; } void draw(scr_coord offset) OVERRIDE { internal_draw(offset, obj); } /** * resize window in response to a resize event * need to recalculate the list of offsets */ void set_size(scr_size size) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_schedule.cc000066400000000000000000000561271520675460500243330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simline.h" #include "../../simcolor.h" #include "../../simintr.h" #include "../../simhalt.h" #include "../../world/simworld.h" #include "../../tool/simmenu.h" #include "../../simconvoi.h" #include "../../display/simgraph.h" #include "../../display/viewport.h" #include "../../utils/simstring.h" #include "../../utils/cbuffer.h" #include "../../ground/grund.h" #include "../../obj/zeiger.h" #include "../../dataobj/schedule.h" #include "../../dataobj/loadsave.h" #include "../../dataobj/translator.h" #include "../../dataobj/environment.h" #include "../../player/simplay.h" #include "../../tpl/vector_tpl.h" #include "gui_button.h" #include "gui_image.h" #include "gui_textarea.h" #include "gui_timeinput.h" #include "gui_component.h" #include "gui_schedule.h" static karte_ptr_t welt; #define DELETE_FLAG (0x8000) #define UP_FLAG (0x4000) #define DOWN_FLAG (0x2000) /** * One entry in the list of schedule entries. */ class gui_schedule_entry_t : public gui_aligned_container_t, public gui_action_creator_t, public action_listener_t { schedule_entry_t entry; bool is_current; bool is_water; uint number; player_t* player; gui_label_buf_t stop; button_t arrow, up, down, del; gui_label_buf_t stop_extra; bool valid; public: gui_schedule_entry_t(player_t* pl, schedule_entry_t e, uint n, bool is_w) { is_water = is_w; player = pl; entry = e; number = n; is_current = false; valid = true; set_table_layout(6,1); arrow.init( button_t::posbutton_automatic, "" ); up.init( button_t::arrowup, "" ); up.add_listener( this ); down.init( button_t::arrowdown, "" ); down.add_listener( this ); add_component(&arrow); add_component(&stop); add_component(&up); add_component(&down); del.init( button_t::imagebox, NULL ); del.set_image( skinverwaltung_t::gadget->get_image_id(SKIN_GADGET_CLOSE) ); del.set_size( gui_theme_t::gui_arrow_left_size ); del.add_listener( this ); del.set_tooltip( "Delete the current stop" ); add_component( &del ); add_component(&stop_extra); update_label(); } void update_label() { arrow.set_targetpos3d(entry.pos); stop.buf().printf("%i) ", number+1); schedule_t::gimme_stop_name(stop.buf(), welt, player, is_water, entry, -1); stop.update(); if( haltestelle_t::get_halt( entry.pos, player, is_water ).is_bound() ) { if( !entry.get_absolute_departures() ) { if (entry.minimum_loading > 0) { if( entry.waiting_time > 0 ) { // relative waiting time stop_extra.buf().printf( "(%d%% %s%s)", (int)entry.minimum_loading, translator::translate( "in " ), difftick_to_string( entry.get_waiting_ticks(), false ) ); } else { stop_extra.buf().printf( "(%i%%)", entry.minimum_loading ); } } } else { // absolute departure time uint16 i = 0; uint32 starttick = entry.get_waiting_ticks(); uint32 delta = welt->ticks_per_world_month/entry.get_absolute_departures(); stop_extra.buf().append( tick_to_string( starttick, true ) ); while( ++i < entry.get_absolute_departures() ) { stop_extra.buf().append( ", " ); stop_extra.buf().append( tick_to_string( starttick+i*delta, true ) ); } } } stop_extra.update(); list_dirty = false; // or the first mouseclick will be swallowed! } void draw(scr_coord offset) OVERRIDE { update_label(); if (is_current || !valid) { gfx->draw_rect_clipped(pos.x + offset.x, pos.y + offset.y, size.w, size.h, valid ? SYSCOL_LIST_BACKGROUND_SELECTED_F : MONEY_MINUS, false CLIP_NUM_DEFAULT); } gui_aligned_container_t::draw(offset); } void set_active(bool yesno) { is_current = yesno; stop.set_color(yesno ? SYSCOL_TEXT_HIGHLIGHT : SYSCOL_TEXT); stop_extra.set_color(yesno ? SYSCOL_TEXT_HIGHLIGHT : SYSCOL_TEXT); } bool action_triggered( gui_action_creator_t *c, value_t ) OVERRIDE { if( c == &up ) { call_listeners( UP_FLAG | number ); return true; } if( c == &down ) { call_listeners( DOWN_FLAG | number ); return true; } if( c == &del ) { call_listeners( DELETE_FLAG | number ); return true; } return false; } bool infowin_event(const event_t *ev) OVERRIDE { if( IS_RIGHTRELEASE(ev) ) { // just center on it welt->get_viewport()->change_world_position( entry.pos ); return true; } else if(ev->mouse_button_state==1 ){ set_focus( this ); if( !gui_aligned_container_t::infowin_event( ev ) && stop.getroffen( ev->click_pos ) ) { // not handled, so we make it active call_listeners( number ); } return true; } return gui_aligned_container_t::infowin_event(ev); } void mark_valid(bool valid) { this->valid = valid; } }; /** * List of displayed schedule entries. */ class schedule_gui_stats_t : public gui_aligned_container_t, public action_listener_t, public gui_action_creator_t { static cbuffer_t buf; vector_tpl entries; schedule_t *last_schedule; ///< last displayed schedule zeiger_t *current_stop_mark; ///< mark current stop on map bool invalid_entries; public: schedule_t *schedule; ///< schedule under editing player_t* player; schedule_gui_stats_t() { set_table_layout(1,0); last_schedule = schedule = NULL; invalid_entries = false; current_stop_mark = new zeiger_t(koord3d::invalid, NULL ); current_stop_mark->set_image( tool_t::general_tool[TOOL_SCHEDULE_ADD]->cursor ); } ~schedule_gui_stats_t() { delete current_stop_mark; delete last_schedule; } // shows/deletes highlighting of tiles void highlight_schedule(bool marking) { marking &= env_t::visualize_schedule; for(schedule_entry_t const& i : schedule->entries) { if (grund_t* const gr = welt->lookup(i.pos)) { for( uint idx=0; idxobj_count(); idx++ ) { obj_t *obj = gr->obj_bei(idx); if( marking ) { if( !obj->is_moving() ) { obj->set_flag( obj_t::highlight ); } } else { obj->clear_flag( obj_t::highlight ); } } gr->set_flag( grund_t::dirty ); // here on water if( gr->is_water() || gr->ist_natur() ) { if( marking ) { gr->set_flag( grund_t::marked ); } else { gr->clear_flag( grund_t::marked ); } } } } // always remove if( grund_t *old_gr = welt->lookup(current_stop_mark->get_pos()) ) { current_stop_mark->mark_image_dirty( current_stop_mark->get_image(), 0 ); old_gr->obj_remove( current_stop_mark ); old_gr->set_flag( grund_t::dirty ); current_stop_mark->set_pos( koord3d::invalid ); } // add if required if( marking && schedule->get_current_stop() < schedule->get_count() ) { current_stop_mark->set_pos( schedule->entries[schedule->get_current_stop()].pos ); if( grund_t *gr = welt->lookup(current_stop_mark->get_pos()) ) { gr->obj_add( current_stop_mark ); current_stop_mark->set_flag( obj_t::dirty ); gr->set_flag( grund_t::dirty ); } } current_stop_mark->clear_flag( obj_t::highlight ); } void update_schedule(bool highlight) { // compare schedules bool ok = (last_schedule != NULL) && last_schedule->entries.get_count() == schedule->entries.get_count(); for(uint i=0; ok && ientries.get_count(); i++) { ok = last_schedule->entries[i] == schedule->entries[i]; } if (ok) { if (!last_schedule->empty()) { entries[ last_schedule->get_current_stop() ]->set_active(false); entries[ schedule->get_current_stop() ]->set_active(true); last_schedule->set_current_stop( schedule->get_current_stop() ); } } else { remove_all(); entries.clear(); invalid_entries = false; buf.clear(); buf.append(translator::translate("Please click on the map to add\nwaypoints or stops to this\nschedule.")); if (schedule->empty()) { new_component(&buf); } else { for(uint i=0; ientries.get_count(); i++) { gui_schedule_entry_t* entry = new_component(player, schedule->entries[i], i,schedule->get_waytype()==water_wt); // mark double entries as invalid bool valid = (schedule->entries[i].pos != schedule->entries[ (i+1) % schedule->entries.get_count() ].pos) && (schedule->entries[i].pos != schedule->entries[ (i-1+schedule->entries.get_count()) % schedule->entries.get_count() ].pos); entry->mark_valid(valid); entry->add_listener( this ); entries.append(entry); invalid_entries |= schedule->entries.get_count() > 1 && !valid; } entries[ schedule->get_current_stop() ]->set_active(true); } if (last_schedule) { last_schedule->copy_from(schedule); } else { last_schedule = schedule->copy(); } set_size(get_min_size()); call_listeners( schedule->get_current_stop() ); } if (highlight) { highlight_schedule(true); } } void draw(scr_coord offset) OVERRIDE { if( schedule ) { update_schedule(true); } gui_aligned_container_t::draw(offset); } bool action_triggered(gui_action_creator_t *, value_t v) OVERRIDE { // has to be one of the entries if( v.i & DELETE_FLAG ) { uint8 delete_stop = v.i & 0x00FF; highlight_schedule( false ); schedule->remove_entry( delete_stop ); highlight_schedule( true ); call_listeners( schedule->get_current_stop() ); } else if( v.i & UP_FLAG ) { uint8 up_stop = v.i & 0x00FF; schedule->move_entry_backward( up_stop ); call_listeners( schedule->get_current_stop() ); } else if( v.i & DOWN_FLAG ) { uint8 down_stop = v.i & 0x00FF; schedule->move_entry_forward( down_stop ); call_listeners( schedule->get_current_stop() ); } else { call_listeners(v); } return true; } bool has_invalid_entries() const { return invalid_entries; } }; cbuffer_t schedule_gui_stats_t::buf; schedule_t *gui_schedule_t::get_old_schedule() const { if( convoi_mode.is_bound() ) { return convoi_mode->get_schedule(); } else if( line_mode.is_bound() ) { return line_mode->get_schedule(); } return old_schedule; } void gui_schedule_t::highlight_schedule( bool hl ) { stats->highlight_schedule(hl); update_tool(hl); } gui_schedule_t::gui_schedule_t() : stats( new schedule_gui_stats_t() ), scrolly( stats ), departure( NULL ) { scrolly.set_maximize( true ); old_schedule = schedule = NULL; player = NULL; set_table_layout(1,0); // loading level and waiting time loading_details = add_table( 3, 3 ); loading_details->set_margin( scr_size(D_MARGIN_LEFT,0), scr_size(D_MARGIN_RIGHT,0) ); { add_component(&cb_wait,2); cb_wait.add_listener( this ); cb_wait.new_component( translator::translate( "Full load" ), SYSCOL_TEXT ); cb_wait.new_component( translator::translate( "Monthly departures" ), SYSCOL_TEXT ); cb_wait.set_rigid(true); new_component(); add_component(&lb_load_str); lb_load_str.set_rigid(true); numimp_load.add_listener(this); numimp_load.set_rigid(true); add_component(&numimp_load); new_component(); add_component(&lb_departure_str); lb_departure_str.set_rigid(true); departure.set_rigid(true); departure.add_listener(this); add_component(&departure); new_component(); } end_table(); // action button row button_row = add_table( 3, 1 ); button_row->set_margin( scr_size(D_MARGIN_LEFT,0), scr_size(D_MARGIN_RIGHT,0) ); { insert_mode.new_component(translator::translate("Ins Stop"), SYSCOL_TEXT); insert_mode.new_component(translator::translate("Add Stop"), SYSCOL_TEXT); insert_mode.set_selection( 1 ); insert_mode.add_listener(this); add_component(&insert_mode); bt_revert.init(button_t::roundbox | button_t::flexible, "Revert schedule"); bt_revert.set_tooltip("Revert to original schedule"); bt_revert.add_listener(this); bt_revert.pressed = false; bt_revert.enable(false); // schedule was not changed yet add_component(&bt_revert); bt_return.init(button_t::roundbox | button_t::flexible, "return ticket"); bt_return.add_listener(this); add_component(&bt_return); bt_remove_double.init(button_t::roundbox | button_t::flexible, "Cleanup schedule"); bt_remove_double.set_tooltip("Remove double stops from schedule"); bt_remove_double.add_listener(this); bt_remove_double.set_visible(false); add_component(&bt_remove_double); } end_table(); scrolly.set_show_scroll_x(true); scrolly.set_scroll_amount_y(LINESPACE+1); add_component(&scrolly); stats->add_listener(this); current_schedule_rotation = welt->get_settings().get_rotation(); scrolly.set_maximize(true); set_size( gui_aligned_container_t::get_min_size() ); } gui_schedule_t::~gui_schedule_t() { stats->highlight_schedule( false ); update_tool(false); delete stats; delete schedule; } void gui_schedule_t::init(schedule_t* schedule_, player_t* player, convoihandle_t cnv, linehandle_t lin, bool force) { if( force || old_schedule != schedule_ ) { if( old_schedule ) { stats->highlight_schedule( false ); update_tool( false ); } // initialization this->convoi_mode = cnv; this->line_mode = lin; this->player = player; current_schedule_rotation = welt->get_settings().get_rotation(); // prepare editing if (schedule != schedule_) { delete schedule; } schedule = schedule_->copy(); make_return = (schedule->get_waytype() == road_wt || schedule->get_waytype() == air_wt || schedule->get_waytype() == water_wt); if( make_return ) { // return tickets bt_return.set_text("return ticket"); bt_return.set_tooltip("Add stops for backward travel"); } else { bt_return.set_text("Invert stops"); bt_return.set_tooltip("Mirrors order of stops"); } stats->player = player; stats->schedule = schedule; stats->update_schedule(false); numimp_load.set_value( schedule->get_current_entry().minimum_loading ); // not allow to change entries beyond waiting time no_editing = (convoi_mode.is_bound() && line_mode.is_bound()); bt_return.enable( !no_editing ); update_selection(); this->old_schedule = schedule_; } else { schedule->set_current_stop(schedule_->get_current_stop()); update_selection(); } set_size(gui_aligned_container_t::get_min_size()); } void gui_schedule_t::update_tool(bool set) { if (set) { uint16 toolnr = insert_mode.get_selection() == 0 ? TOOL_SCHEDULE_INS : TOOL_SCHEDULE_ADD; tool_t::general_tool[toolnr]->set_default_param((const char *)schedule); welt->set_tool(tool_t::general_tool[toolnr], player ); } else { // we have to reset the tool (in particular, when window closes) if(welt->get_tool(player->get_player_nr())==tool_t::general_tool[TOOL_SCHEDULE_ADD]) { if(tool_t::general_tool[TOOL_SCHEDULE_ADD]->get_default_param()==(const char *)schedule) { welt->set_tool( tool_t::general_tool[TOOL_QUERY], player ); } } else if(welt->get_tool(player->get_player_nr())==tool_t::general_tool[TOOL_SCHEDULE_INS]) { if(tool_t::general_tool[TOOL_SCHEDULE_INS]->get_default_param()==(const char *)schedule) { welt->set_tool( tool_t::general_tool[TOOL_QUERY], player ); } } } } void gui_schedule_t::update_selection() { // set all elements invisible first cb_wait.set_visible(false); numimp_load.set_visible(false); departure.set_visible(false); lb_load_str.set_visible(false); lb_departure_str.set_visible(false); if( !schedule->empty() ) { schedule->set_current_stop( min(schedule->get_count()-1,schedule->get_current_stop()) ); const uint8 current_stop = schedule->get_current_stop(); if( haltestelle_t::get_halt(schedule->entries[current_stop].pos, player, schedule->get_waytype()==water_wt).is_bound() ) { cb_wait.set_visible(true); if( schedule->entries[current_stop].get_absolute_departures() ) { cb_wait.set_selection( 1 ); lb_load_str.set_visible(true); lb_load_str.set_text("Departures per month"); numimp_load.set_visible( true ); numimp_load.set_value( schedule->entries[current_stop].get_absolute_departures() ); numimp_load.set_limits( 1, 154 ); numimp_load.set_increment_mode( 1 ); lb_departure_str.set_visible( true ); lb_departure_str.set_text( "Departs at" ); departure.set_visible( true ); departure.set_ticks( schedule->entries[current_stop].waiting_time, true); } else { cb_wait.set_selection( 0 ); lb_load_str.set_visible(true); lb_load_str.set_text("Minimum load"); numimp_load.set_visible( true ); numimp_load.set_value( schedule->entries[current_stop].minimum_loading ); numimp_load.set_limits( 0, 100 ); numimp_load.set_increment_mode( gui_numberinput_t::PROGRESS ); lb_departure_str.set_text( "Max. waiting time" ); lb_departure_str.set_visible( true ); departure.set_visible( true ); departure.set_ticks( schedule->entries[current_stop].waiting_time, false ); } } else { // waypoint } } loading_details->set_size( loading_details->get_size() ); set_size(size); } bool gui_schedule_t::action_triggered( gui_action_creator_t *comp, value_t p) { if (comp == &bt_revert) { // revert changes and tell listener if (schedule) { stats->highlight_schedule(false); delete schedule; schedule = NULL; } schedule = get_old_schedule()->copy(); stats->schedule = schedule; stats->update_schedule(true); update_selection(); value_t v; v.p = NULL; call_listeners(v); } else if( comp == &bt_remove_double) { schedule->remove_double_entries(); stats->update_schedule(true); update_selection(); } else if( comp == &cb_wait) { if( p.i==1 && schedule->entries[schedule->get_current_stop()].get_absolute_departures()==0 ) { // absolute departure mode schedule->entries[schedule->get_current_stop()].minimum_loading = 101; } if( p.i==0 && schedule->entries[schedule->get_current_stop()].get_absolute_departures() ) { schedule->entries[schedule->get_current_stop()].minimum_loading = 0; } update_selection(); } else if( comp == &numimp_load ) { if (!schedule->empty()) { if( schedule->entries[schedule->get_current_stop()].minimum_loading <=100 ) { schedule->entries[schedule->get_current_stop()].minimum_loading = min( (uint8)p.i, 100 ); } else { schedule->entries[schedule->get_current_stop()].minimum_loading = max( 101, min(255,(uint8)p.i+100) ); // clip waiting time to 1/nth of the month if( schedule->entries[schedule->get_current_stop()].waiting_time>65535/(schedule->entries[schedule->get_current_stop()].minimum_loading-100) ) { schedule->entries[schedule->get_current_stop()].waiting_time=65535/(schedule->entries[schedule->get_current_stop()].minimum_loading-100); } } update_selection(); } } else if(comp == &departure) { if(!schedule->empty()) { schedule->entries[schedule->get_current_stop()].waiting_time = (uint16)p.i; // clip waiting time to 1/nth of the month if absolute departure time uint16 wt = schedule->entries[schedule->get_current_stop()].minimum_loading; if( wt>100 && schedule->entries[schedule->get_current_stop()].waiting_time>65535/(wt-100) ) { schedule->entries[schedule->get_current_stop()].waiting_time = 65535/(wt-100); } update_selection(); } } else if(comp == &bt_return) { schedule->add_return_way(make_return); } else if (comp == stats) { // click on one of the schedule entries const int line = p.i; if( line >= 0 && line < schedule->get_count() ) { schedule->set_current_stop( line ); update_selection(); } else if (schedule->empty()) { update_selection(); } } // do not reset tool during initialisation if (old_schedule) { update_tool(true); } return true; } void gui_schedule_t::draw(scr_coord pos) { if( schedule ) { // check if we missed a map rotation const uint8 world_rotation = world()->get_settings().get_rotation(); while( current_schedule_rotation != world_rotation ) { current_schedule_rotation = (current_schedule_rotation + 1) % 4; schedule->rotate90( (4+current_schedule_rotation - world_rotation) & 1 ? world()->get_size().y-1 : world()->get_size().x-1 ); } schedule_t *scd = get_old_schedule(); // change current entry while convois drives on koord3d current = scd->get_current_entry().pos; int idx = 0; bool is_allowed = player==welt->get_active_player() && !welt->get_active_player()->is_locked(); bool is_all_same = scd->get_count()==schedule->get_count(); is_all_same &= scd->get_current_stop() == schedule->get_current_stop(); is_all_same &= !(convoi_mode.is_bound() && line_mode.is_bound() && line_mode != convoi_mode->get_line()); for(schedule_entry_t ent : schedule->entries ) { #if 0 if( ent.pos == current ) { schedule->set_current_stop( idx ); } #else (void)current; (void)ent; #endif if( is_all_same ) { is_all_same = scd->entries[idx] == schedule->entries[idx]; } idx++; } bt_revert.enable( !is_all_same && is_allowed ); cb_wait.enable( is_allowed ); numimp_load.enable( is_allowed ); departure.enable( is_allowed ); bt_return.enable( is_allowed ); bool invalid = stats->has_invalid_entries(); bt_return.set_visible(!invalid); bt_remove_double.set_visible(invalid); button_row->set_size(button_row->get_size()); } // always dirty, to cater for shortening of halt names and change of selections gui_aligned_container_t::draw(pos); } /** * Set window size and adjust component sizes and/or positions accordingly */ void gui_schedule_t::set_size(scr_size size) { gui_aligned_container_t::set_size(size); size = get_size(); // make scrolly take all of space scrolly.set_size( scr_size(scrolly.get_size().w, get_size().h - scrolly.get_pos().y)); } void gui_schedule_t::rdwr(loadsave_t *file) { // convoy data convoi_t::rdwr_convoihandle_t(file, convoi_mode); simline_t::rdwr_linehandle_t(file, line_mode); departure.rdwr( file ); if( file->is_loading() ) { uint16 schedule_type, player_nr; schedule_t *load_schedule; file->rdwr_short(player_nr); player = welt->get_player(player_nr); file->rdwr_short(schedule_type); switch (schedule_type) { case schedule_t::truck_schedule: load_schedule = new truck_schedule_t(); break; case schedule_t::train_schedule: load_schedule = new train_schedule_t(); break; case schedule_t::ship_schedule: load_schedule = new ship_schedule_t(); break; case schedule_t::airplane_schedule: load_schedule = new airplane_schedule_t(); break; case schedule_t::monorail_schedule: load_schedule = new monorail_schedule_t(); break; case schedule_t::tram_schedule: load_schedule = new maglev_schedule_t(); break; case schedule_t::maglev_schedule: load_schedule = new truck_schedule_t(); break; case schedule_t::narrowgauge_schedule: load_schedule = new narrowgauge_schedule_t(); break; default: load_schedule = new train_schedule_t(); dbg->error("schedule_gui_t::rdwr", "Could not restore schedule window for generic schdule"); load_schedule->rdwr(file); delete load_schedule; return; } load_schedule->rdwr( file ); init(load_schedule, player, convoi_mode, line_mode); delete load_schedule; get_min_size(); set_size(size); } else { uint16 schedule_type = schedule->get_type(); uint16 player_nr = player->get_player_nr(); file->rdwr_short(player_nr); file->rdwr_short(schedule_type); schedule->rdwr( file ); } } simutrans-124.5/src/simutrans/gui/components/gui_schedule.h000066400000000000000000000052521520675460500241660ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_SCHEDULE_H #define GUI_COMPONENTS_GUI_SCHEDULE_H #include "gui_label.h" #include "gui_numberinput.h" #include "gui_aligned_container.h" #include "gui_button.h" #include "gui_action_creator.h" #include "gui_combobox.h" #include "gui_scrollpane.h" #include "gui_scrolled_list.h" #include "gui_timeinput.h" #include "action_listener.h" #include "../../convoihandle.h" #include "../../linehandle.h" #include "../../tpl/vector_tpl.h" class schedule_t; class player_t; class cbuffer_t; class loadsave_t; class schedule_gui_stats_t; /** * GUI for Schedule dialog */ class gui_schedule_t : public gui_action_creator_t, public gui_aligned_container_t, public action_listener_t { private: // always needed button_t bt_revert, bt_return, bt_remove_double; gui_label_t lb_load_str, lb_departure_str; gui_numberinput_t numimp_load; gui_combobox_t cb_wait, insert_mode; schedule_gui_stats_t* stats; gui_scrollpane_t scrolly; gui_aligned_container_t *loading_details, *button_row; gui_timeinput_t departure; bool make_return; // either reverting line or add mirror bool no_editing; // if convoi schedule is part of a line, if has to be removed from it // set the correct tool now ... void update_tool(bool set); // changes the waiting/loading levels if allowed void update_selection(); schedule_t *get_old_schedule() const; protected: // schedule of this line (or of convoi belonging to this line) is being edited linehandle_t line_mode; // schedule of this convoi is being edited convoihandle_t convoi_mode; // the schedule that is edited, copy of old_schedule schedule_t *schedule; // the original schedule (used to detect calls to init with same original schedule), might be non-null but invalid, do not dereference schedule_t *old_schedule; player_t *player; uint8 current_schedule_rotation; // to detect the rotation of the map independently public: gui_schedule_t(); void init(schedule_t* schedule = NULL, player_t* player = NULL, convoihandle_t cnv = convoihandle_t(), linehandle_t lin = linehandle_t(), bool force=false ); virtual ~gui_schedule_t(); void draw(scr_coord pos) OVERRIDE; // true if the schedule has changed bool has_pending_changes() { return bt_revert.enabled(); } scr_size get_max_size() const OVERRIDE { return scr_size::inf; } void set_size(scr_size size) OVERRIDE; schedule_t *get_schedule() const { return schedule; } void highlight_schedule( bool hl ); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void rdwr( loadsave_t *file ); bool is_marginless() const OVERRIDE { return true; } }; #endif simutrans-124.5/src/simutrans/gui/components/gui_scrollbar.cc000066400000000000000000000177071520675460500245230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simdebug.h" #include "../../simcolor.h" #include "../../display/simgraph.h" #include "../../simskin.h" #include "../../macros.h" #include "../../descriptor/skin_desc.h" #include "action_listener.h" #include "gui_scrollbar.h" #include "../gui_theme.h" scrollbar_t::scrollbar_t(type_t type) : type(type), dragging(false), sticky_bottom(false), knob_offset(0), knob_size(10), total_size(20), knob_scroll_amount(LINESPACE), // equals one line knob_scroll_discrete(false) { visible_mode = show_auto; if (type == vertical) { size = scr_size(D_SCROLLBAR_WIDTH,40); // 40 = default scrollbar height button_def[left_top_arrow_index].set_typ(button_t::arrowup); button_def[right_bottom_arrow_index].set_typ(button_t::arrowdown); } else { // horizontal size = scr_size(40,D_SCROLLBAR_HEIGHT); // 40 = default scrollbar length button_def[left_top_arrow_index].set_typ(button_t::arrowleft); button_def[right_bottom_arrow_index].set_typ(button_t::arrowright); } button_def[left_top_arrow_index].set_pos(scr_coord(0,0)); button_def[right_bottom_arrow_index].set_pos(scr_coord(0,0)); reposition_buttons(); } void scrollbar_t::set_size(scr_size size) { if (type == vertical) { this->size.h = size.h; } else { this->size.w = size.w; } reposition_buttons(); } scr_size scrollbar_t::get_min_size() const { if(type == vertical) { scr_size size = D_ARROW_UP_SIZE; size.clip_lefttop(D_ARROW_DOWN_SIZE); size.w = max(size.w, D_SCROLLBAR_WIDTH); size.h += 40; return size; } else { scr_size size = D_ARROW_LEFT_SIZE; size.clip_lefttop(D_ARROW_RIGHT_SIZE); size.h = max(size.h, D_SCROLLBAR_HEIGHT); size.w += 40; return size; } } scr_size scrollbar_t::get_max_size() const { scr_size min_size = get_min_size(); if(type == vertical) { return scr_size(min_size.w, scr_size::inf.h); } else { return scr_size(scr_size::inf.w, min_size.h); } } void scrollbar_t::set_sticky_bottom(bool b) { sticky_bottom = b; reposition_buttons(); } void scrollbar_t::set_knob(sint32 new_visible_size, sint32 new_total_size) { if( new_total_size != total_size ) { knob_offset = (sint32)( (double)knob_offset * ( (double)new_total_size / (double) total_size ) + 0.5 ); } total_size = max(1,new_total_size); knob_size = clamp( new_visible_size, 1, total_size ); knob_offset = clamp( knob_offset, 0, total_size-knob_size ); reposition_buttons(); } // reset variable position and size values of the three buttons void scrollbar_t::reposition_buttons() { const sint32 area = (type == vertical ? max(size.h - D_ARROW_UP_HEIGHT - D_ARROW_DOWN_HEIGHT, 0) : size.w - D_ARROW_LEFT_WIDTH - D_ARROW_RIGHT_WIDTH); // area will be actual area knob can move in if (!sticky_bottom) { knob_offset = clamp(knob_offset, 0, total_size - knob_size); } else { knob_offset = total_size - knob_size; } double size_ratio = (double)knob_size / (double)total_size; scr_coord_val length = min( area, (scr_coord_val)( area*size_ratio+0.5) ); if( type == vertical ) { length = max( length, D_SCROLL_MIN_HEIGHT ); } else { length = max( length, D_SCROLL_MIN_WIDTH ); } double offset_ratio = (double)knob_offset / (double)total_size; int offset = (int)( offset_ratio*area+0.5 ); offset = clamp( offset, 0, area-length ); if(type == vertical) { button_def[left_top_arrow_index].set_pos( scr_coord( (D_SCROLLBAR_WIDTH - D_ARROW_UP_WIDTH)/2, 0) ); button_def[right_bottom_arrow_index].set_pos( scr_coord( (D_SCROLLBAR_WIDTH - D_ARROW_DOWN_WIDTH)/2, max(D_ARROW_UP_HEIGHT+D_SCROLL_MIN_HEIGHT,size.h-D_ARROW_DOWN_HEIGHT) ) ); sliderarea.set( 0, D_ARROW_UP_HEIGHT, D_SCROLLBAR_WIDTH, area ); knobarea.set( 0, D_ARROW_UP_HEIGHT + max(offset,0), D_SCROLLBAR_WIDTH, length ); } else { // horizontal button_def[left_top_arrow_index].set_pos( scr_coord(0,(D_SCROLLBAR_HEIGHT - D_ARROW_LEFT_HEIGHT)/2) ); button_def[right_bottom_arrow_index].set_pos( scr_coord(size.w - D_ARROW_RIGHT_WIDTH, (D_SCROLLBAR_HEIGHT - D_ARROW_RIGHT_HEIGHT)/2) ); sliderarea.set( D_ARROW_LEFT_WIDTH, 0, area, D_SCROLLBAR_HEIGHT ); knobarea.set( D_ARROW_LEFT_WIDTH + offset, 0, length, D_SCROLLBAR_HEIGHT ); } full = (total_size<=knob_size); set_visible( !full ); } // actually handles crolling, callback etc. void scrollbar_t::scroll(sint32 updown) { sint32 new_knob_offset = clamp( knob_offset+updown, 0, total_size-knob_size ); if( new_knob_offset != knob_offset ) { sticky_bottom = false; knob_offset = new_knob_offset; call_listeners((long)knob_offset); reposition_buttons(); } } bool scrollbar_t::infowin_event(const event_t *ev) { const scr_coord click_pos = ev->click_pos; int i; if( IS_WHEELUP(ev) && (type == vertical) != IS_SHIFT_PRESSED(ev) ) { scroll( -knob_scroll_amount ); return true; } else if (IS_WHEELDOWN(ev) && (type == vertical) != IS_SHIFT_PRESSED(ev)) { scroll( +knob_scroll_amount ); return true; } else if( is_visible() && !full ) { // don't respond to these messages if not visible if( IS_LEFTCLICK(ev) ) { if( button_def[0].getroffen(click_pos) ) { button_def[0].pressed = true; scroll( -knob_scroll_amount ); } else if( button_def[1].getroffen(click_pos) ) { button_def[1].pressed = true; scroll( +knob_scroll_amount ); } else if( !dragging ) { // click above/below the slider? if( type == vertical ) { if( click_pos.y < knobarea.y ) { scroll( -knob_size); } else if( click_pos.y > knobarea.get_bottom() ) { scroll( +knob_size ); } else { dragging = true; } } else { if( click_pos.x < knobarea.x ) { scroll( -knob_size ); } else if( click_pos.x > knobarea.get_right() ) { scroll( +knob_size ); } else { dragging = true; } } } return true; } else if( IS_LEFTDRAG(ev) || (dragging && IS_LEFT_BUTTON_PRESSED(ev)) ) { // now dragging the slider ... if( knobarea.contains( click_pos ) || dragging ) { sint32 delta, change; // added vertical/horizontal check if(type == vertical) { delta = ev->mouse_pos.y - ev->click_pos.y; change = (sint32)(delta*( (double)total_size / (double) sliderarea.h ) + 0.5); delta = knobarea.y; scroll( change ); change_drag_start({ 0, knobarea.y-delta }); } else { delta = ev->mouse_pos.x - ev->click_pos.x; change = (sint32)(delta*( (double)total_size / (double) sliderarea.w ) + 0.5); delta = knobarea.x; scroll( change ); change_drag_start({ knobarea.x-delta, 0 }); } dragging = true; return true; } } else if (IS_LEFTRELEASE(ev)) { dragging = false; for (i=0;i<2;i++) { if (button_def[i].getroffen(click_pos)) { button_def[i].pressed = false; return true; } } } } return false; } void scrollbar_t::draw(scr_coord pos_par) { // Don't draw control if not visible if( (visible_mode != show_auto) && (!is_visible() || (visible_mode == show_never) ) ) { return; } pos_par += pos; // Draw place holder if scrollbar is full in auto mode if ( visible_mode == show_auto && full ) { // Draw place holder themed with scrollbar background gfx->draw_stretch_map( gui_theme_t::v_scroll_back_tiles, scr_rect( pos_par, size ) ); return; } // Draw scrollbar as normal button_def[left_top_arrow_index].draw(pos_par); button_def[right_bottom_arrow_index].draw(pos_par); // now background and slider if( type == vertical ) { gfx->draw_stretch_map( gui_theme_t::v_scroll_back_tiles, scr_rect( pos_par + sliderarea.get_pos(), sliderarea.get_size() ) ); gfx->draw_stretch_map( gui_theme_t::v_scroll_knob_tiles, scr_rect( pos_par + knobarea.get_pos(), knobarea.get_size() ) ); } else { gfx->draw_stretch_map( gui_theme_t::h_scroll_back_tiles, scr_rect( pos_par + sliderarea.get_pos(), sliderarea.get_size() ) ); gfx->draw_stretch_map( gui_theme_t::h_scroll_knob_tiles, scr_rect( pos_par + knobarea.get_pos(), knobarea.get_size() ) ); } } simutrans-124.5/src/simutrans/gui/components/gui_scrollbar.h000066400000000000000000000056721520675460500243630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_SCROLLBAR_H #define GUI_COMPONENTS_GUI_SCROLLBAR_H #include "gui_action_creator.h" #include "../../simevent.h" #include "gui_button.h" #include "../gui_theme.h" /** * Scrollbar class * scrollbar can be horizontal or vertical */ class scrollbar_t : public gui_action_creator_t, public gui_component_t { public: enum type_t { vertical, ///< Vertical scrollbar horizontal ///< Horizontal scrollbar }; enum visible_mode_t { show_never, ///< Never show the scrollbar show_always, ///< Always show the scrollbar, even at maximum show_disabled, ///< Disable scrollbar at maximum value show_auto ///< Hide scrollbar at maximum value, else show }; private: // private button indexes enum button_element_index { left_top_arrow_index, right_bottom_arrow_index }; type_t type; visible_mode_t visible_mode; // Show, hide or auto hide bool full; // Scrollbar is full bool dragging; // to handle event even when outside the knob ... bool sticky_bottom; // stays at the bottom until first scrolling (default off) // the following three values are from host (e.g. list), NOT actual size. sint32 knob_offset; // offset from top-left sint32 knob_size; sint32 total_size; /** * number of elements to scroll with arrow button press. default: 11 */ sint32 knob_scroll_amount; bool knob_scroll_discrete; // if true, knob_offset forced to be integer multiples of knob_scroll_amount // arrow buttons button_t button_def[2]; // size of button area scr_rect knobarea, sliderarea; void reposition_buttons(); void scroll( sint32 amout_to_scroll ); public: // type is either scrollbar_t::horizontal or scrollbar_t::vertical scrollbar_t(type_t type); void set_size(scr_size size) OVERRIDE; void set_scroll_amount(sint32 sa) { knob_scroll_amount = sa; } void set_scroll_discrete(const bool sd) { knob_scroll_discrete = sd; } /** * knob_size is visible size, total_size is total size (of whatever unit) * Scroolbars are not directly related to pixels! */ void set_knob(sint32 knob_size, sint32 total_size); sint32 get_knob_offset() const { // return clamped offset if really desired return knob_offset - ((knob_scroll_discrete && total_size!=knob_offset+knob_size) ? (knob_offset % knob_scroll_amount) : 0); } bool is_bottom() const { return total_size <= knob_offset + knob_size; } void set_knob_offset(sint32 v) { knob_offset = v; reposition_buttons(); } // repositioning sliders so it is always at the bottom until scrolled once void set_sticky_bottom(bool); void set_visible_mode(visible_mode_t vm) { visible_mode = vm; } visible_mode_t get_visible_mode() { return visible_mode; } bool infowin_event(event_t const*) OVERRIDE; void draw(scr_coord pos) OVERRIDE; scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_scrolled_list.cc000066400000000000000000000226241520675460500253740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "gui_scrollbar.h" #include "gui_scrolled_list.h" #include "../simwin.h" #include "../../display/simgraph.h" #include "../../descriptor/skin_desc.h" #include "../../simskin.h" // help for sorting bool gui_scrolled_list_t::scrollitem_t::compare(const gui_component_t *aa, const gui_component_t *bb ) { const scrollitem_t* a = dynamic_cast(aa); const scrollitem_t* b = dynamic_cast(bb); assert(a && b && a->get_text() != NULL && b->get_text() != NULL); return strcmp(a->get_text(), b->get_text() ); } scr_size gui_scrolled_list_t::const_text_scrollitem_t::get_min_size() const { if (!is_editable()) { const char* text = get_text(); return scr_size(2*D_H_SPACE + (text ? gfx->calc_text_width(text) : D_BUTTON_WIDTH), LINESPACE); } else { return scr_size(D_BUTTON_WIDTH, LINESPACE); } } scr_size gui_scrolled_list_t::const_text_scrollitem_t::get_max_size() const { return scr_size(scr_size::inf.w, LINESPACE); } // draws a single line of text void gui_scrolled_list_t::const_text_scrollitem_t::draw(scr_coord pos) { pos += get_pos(); if(selected) { // selected element gfx->draw_rect_clipped( pos.x+D_H_SPACE/2, pos.y-1, get_size().w-D_H_SPACE, get_size().h + 1, (focused ? SYSCOL_LIST_BACKGROUND_SELECTED_F : SYSCOL_LIST_BACKGROUND_SELECTED_NF), true CLIP_NUM_DEFAULT); gfx->draw_text_clipped( pos.x+D_H_SPACE, pos.y, get_text(), ALIGN_LEFT, (focused ? SYSCOL_LIST_TEXT_SELECTED_FOCUS : SYSCOL_LIST_TEXT_SELECTED_NOFOCUS), true); } else { // normal text gfx->draw_text_clipped( pos.x+D_H_SPACE, pos.y, get_text(), ALIGN_LEFT, get_color(), true); } } gui_scrolled_list_t::gui_scrolled_list_t(enum type type, item_compare_func cmp) : gui_scrollpane_t(NULL, true), item_list(container.get_components()) { container.set_table_layout(1,0); set_component(&container); this->type = type; compare = cmp; size = scr_size(0,0); pos = scr_coord(0,0); multiple_selection = false; maximize = false; sliders_dirty = true; } // set the scrollbar offset, so that the selected item is visible void gui_scrolled_list_t::show_selection(int sel) { set_selection(sel); cleanup_elements(); show_focused(); } void gui_scrolled_list_t::set_selection(int s) { if (s<0 || ((uint32)s)>=item_list.get_count()) { container.set_focus(NULL); return; } gui_component_t* new_focus = item_list[s]; // reset selected status for(gui_component_t* v : item_list) { scrollitem_t* item = dynamic_cast(v); if( item ) { item->selected = item==new_focus; } } container.set_focus(new_focus); } sint32 gui_scrolled_list_t::get_selection() const { scrollitem_t* focus = get_selected_item(); return focus ? item_list.index_of(focus) : -1; } gui_scrolled_list_t::scrollitem_t* gui_scrolled_list_t::get_selected_item() const { scrollitem_t* focus = dynamic_cast( comp->get_focus() ); return focus && item_list.is_contained(focus) ? focus : NULL; } vector_tpl gui_scrolled_list_t::get_selections() const { vector_tpl selections; for( uint32 i=0; i (item_list[i]); if( item && item->selected ) { selections.append(i); } } return selections; } void gui_scrolled_list_t::clear_elements() { container.remove_all(); } void gui_scrolled_list_t::sort( int offset ) { cleanup_elements(); if (compare == 0 || item_list.get_count() <= 1) { reset_container_size(); return; } if (offset >=0 && (uint32)offset < item_list.get_count()) { vector_tpl::iterator start = item_list.begin(); for(int i=0; i( comp->get_focus() ); event_t ev2 = *ev; // translate key up/down to tab/shift-tab if( IS_KEYDOWN(ev) && ev->ev_code == SIM_KEYCODE_UP && get_selection()>0) { ev2.ev_code = SIM_KEYCODE_TAB; ev2.ev_key_mod |= SIM_KEYMOD_SHIFT; } if( IS_KEYDOWN(ev) && ev->ev_code == SIM_KEYCODE_DOWN && (uint32)(get_selection()+1) < item_list.get_count()) { ev2.ev_code = SIM_KEYCODE_TAB; ev2.ev_key_mod &= ~SIM_KEYMOD_SHIFT; } bool swallowed = gui_scrollpane_t::infowin_event(&ev2); scrollitem_t* const new_focus = dynamic_cast( comp->get_focus() ); // if different element is focused, calculate selection and call listeners if ( focus != new_focus || (new_focus && IS_LEFTRELEASE(&ev2) && new_focus->getroffen(ev2.mouse_pos)) ) { calc_selection(focus, new_focus, *ev); const int new_selection = get_selection(); call_listeners((long)new_selection); swallowed = true; } return swallowed; } void gui_scrolled_list_t::calc_selection(scrollitem_t* old_focus, scrollitem_t* new_focus, event_t ev) { if( !new_focus ) { // do nothing. return; } else if( !multiple_selection || ev.ev_key_mod==0 ) { // simply select new_focus for(gui_component_t* v : item_list) { scrollitem_t* item = dynamic_cast(v); if( item ) { item->selected = item==new_focus; } } } else if( multiple_selection && IS_CONTROL_PRESSED(&ev) ) { // control key is pressed. select or deselect the focused one. new_focus->selected = !new_focus->selected; } else if( IS_SHIFT_PRESSED(&ev) ) { // shift key is pressed. sint32 old_idx = item_list.index_of(old_focus); sint32 new_idx = item_list.index_of(new_focus); if( old_idx==-1 || new_idx==-1 ) { // out of index!? return; } const bool sel = !new_focus->selected; for( sint32 i=min(old_idx,new_idx); i<=max(old_idx,new_idx); i++ ) { scrollitem_t* item = dynamic_cast(item_list[i]); if( item && i!=old_idx ) { item->selected = sel; } } } } void gui_scrolled_list_t::cleanup_elements(bool resize) { bool reset = false; for( vector_tpl::iterator iter = item_list.begin(); iter != item_list.end(); ) { if (scrollitem_t* const item = dynamic_cast( *iter ) ) { if( !item->is_valid() ) { container.remove_component(item); reset = resize; } else { ++iter; } } } if (reset) { reset_container_size(); } } void gui_scrolled_list_t::show_bottom() { scroll_y.set_sticky_bottom(true); } void gui_scrolled_list_t::draw(scr_coord offset) { // set focus scrollitem_t* focus = dynamic_cast( comp->get_focus() ); if( focus ) { for(gui_component_t* v : item_list) { scrollitem_t* item = dynamic_cast(v); if( item ) { item->focused = item->selected && win_get_focus()==focus; } } } cleanup_elements(); if (item_list.get_count() > 0) { scr_rect rect(pos + offset, get_size()); switch(type) { case windowskin: gfx->draw_stretch_map( gui_theme_t::windowback, rect); break; case listskin: gfx->draw_stretch_map( gui_theme_t::listbox, rect); break; case transparent: break; } } if (sliders_dirty) { recalc_sliders_visible(size); } gui_scrollpane_t::draw(offset); // find actual height and reset positions in case of size changes (as the final size is only know after redraws) scr_coord_val w = 0; scr_coord_val h = 0; for (vector_tpl::iterator iter = item_list.begin(); iter != item_list.end(); ++iter) { if ((*iter)->is_visible()) { (*iter)->set_pos(scr_coord(D_H_SPACE, h)); h += (*iter)->get_size().h; w = max(w, (*iter)->get_min_size().w); } } scr_size new_size(w + D_H_SPACE * 2, h); bool has_vscroll = new_size.h > size.h || (new_size.w > size.w && h + new_size.h > size.h); scr_coord_val client_width = size.w - 2 * D_H_SPACE - has_vscroll * D_SCROLLBAR_WIDTH; if (w < client_width) { w = client_width; new_size.w = client_width + 2 * D_H_SPACE; } for (vector_tpl::iterator iter = item_list.begin(); iter != item_list.end(); ++iter) { if ((*iter)->is_visible()) { scr_size c_size = (*iter)->get_size(); if (c_size.h == 0) { (*iter)->set_size(scr_size(w, (*iter)->get_min_size().h)); } else if (sliders_dirty || c_size.w < w ) { (*iter)->set_size(scr_size(w, c_size.h)); } } } // now reset the positions in case of height changes if (container.get_size() != new_size) { container.gui_component_t::set_size(new_size); } if(sliders_dirty) { // reset sliders recalc_sliders_visible(size); recalc_sliders(size); } sliders_dirty = false; // to avoid flicker } simutrans-124.5/src/simutrans/gui/components/gui_scrolled_list.h000066400000000000000000000113651520675460500252360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_SCROLLED_LIST_H #define GUI_COMPONENTS_GUI_SCROLLED_LIST_H #include "gui_aligned_container.h" #include "gui_scrollpane.h" #include "action_listener.h" #include "gui_action_creator.h" #include "gui_label.h" #include "../../simcolor.h" #include "../../tpl/vector_tpl.h" /** * Helper class to access the list of components in the scrolling container. */ class scroll_container_t : public gui_aligned_container_t { public: vector_tpl & get_components() { return components; } }; /** * Scrollable list of components that can be sorted, and has component selection. * * Displays list, scrollbuttons up/down, dragbar. * Has a min and a max size, and can be displayed with any size in between. * Does ONLY cater for vertical offset (yet). * two possible types: * -list. simply lists some items. * -selection. is a list, but additionally, one item can be selected. */ class gui_scrolled_list_t : public gui_action_creator_t, public gui_scrollpane_t { public: enum type { windowskin, listskin, transparent }; /** * Base class for elements in lists. Virtual inheritance. */ class scrollitem_t : virtual public gui_component_t { public: /// constructor: set focusable to true. /// focused element will be used to determine selection in gui_scrolled_list_t scrollitem_t() : gui_component_t(true /* focusable */), focused(false), selected(false) { } virtual char const* get_text() const = 0; virtual void set_text(char const *) {} virtual bool is_valid() const { return true; } // can be used to indicate invalid entries virtual bool is_editable() const { return false; } /// compares using get_text static bool compare(const gui_component_t *a, const gui_component_t *b ); bool focused, selected; protected: using gui_component_t::draw; }; typedef bool (*item_compare_func)(const gui_component_t* a, const gui_component_t* b); /** * Text entry, non-editable */ class const_text_scrollitem_t : public gui_label_t, public scrollitem_t { public: const_text_scrollitem_t(char const* const t, PIXVAL const col) : gui_label_t(NULL, col) { set_text_pointer(t); } char const* get_text() const OVERRIDE { return get_text_pointer(); } scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; void set_text(char const *) OVERRIDE {} void draw(scr_coord pos) OVERRIDE; using gui_label_t::get_color; private: using gui_label_t::set_text; }; private: enum type type; bool maximize; // true if to expand to bottom right corner bool sliders_dirty; // true if sliders need attention during next redraw scr_coord_val c_old_width; item_compare_func compare; bool multiple_selection; // true when multiple selection is enabled. void calc_selection(scrollitem_t*, scrollitem_t*, event_t); protected: scroll_container_t container; vector_tpl & item_list; void reset_container_size(); /// deletes invalid elements from list void cleanup_elements(bool resize=true); public: scr_size get_container_min_size() const { return container.get_min_size(); } virtual void set_skin_type(enum type t) { this->type = t; } void set_cmp(item_compare_func cmp) { compare = cmp; } void set_checkered(bool c) { container.set_checkered(c); } gui_scrolled_list_t(enum type t= windowskin, item_compare_func cmp = 0); ~gui_scrolled_list_t() { clear_elements(); } void show_selection(int s); void set_selection(int s); sint32 get_selection() const; vector_tpl get_selections() const; scrollitem_t* get_selected_item() const; uint32 get_count() const { return item_list.get_count(); } void enable_multiple_selection() { multiple_selection = true; } /* when rebuilding a list, be sure to call recalculate the slider * with recalculate_slider() to update the scrollbar properly. */ void clear_elements(); scrollitem_t *get_element(sint32 i) const { return (i>=0 && (uint32)i(item_list[i]) : NULL; } template void new_component(const As &... as) { return container.new_component(as...)->set_focusable(true); } void take_component(gui_component_t* comp) { container.take_component(comp, 1); } /** * Sorts the list. * Calls the virtual method scrollitem_t::sort of element at position @p offset. * Adjusts scrollbar. * @param offset sort list from element offset to end */ void sort( int offset); void set_size(scr_size size) OVERRIDE; bool infowin_event(event_t const*) OVERRIDE; void draw(scr_coord pos) OVERRIDE; bool is_marginless() const OVERRIDE { return maximize; } void set_maximize(bool b) { maximize = b; } void show_bottom(); }; #endif simutrans-124.5/src/simutrans/gui/components/gui_scrollpane.cc000066400000000000000000000234701520675460500246740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simdebug.h" #include "../gui_frame.h" #include "gui_scrollpane.h" #include "gui_scrollbar.h" #include "gui_button.h" #include "../../dataobj/loadsave.h" #include "../../dataobj/environment.h" #include "../../display/simgraph.h" #include "../../simcolor.h" /** * @param comp the scrolling component */ gui_scrollpane_t::gui_scrollpane_t(gui_component_t* comp, bool b_scroll_x, bool b_scroll_y) : scroll_x(scrollbar_t::horizontal), scroll_y(scrollbar_t::vertical) { this->comp = comp; max_width = D_DEFAULT_WIDTH - D_MARGIN_LEFT - D_MARGIN_RIGHT; max_height = D_DEFAULT_HEIGHT / 2 - D_MARGIN_TOP - D_MARGIN_BOTTOM; b_show_scroll_x = b_scroll_x; b_show_scroll_y = b_scroll_y; b_has_size_corner = true; b_can_drag = true; b_is_dragging = false; old_comp_size = scr_size::invalid; maximize = false; } scr_size gui_scrollpane_t::get_min_size() const { // the component does not have a minimum scroll size // use min_size and limit it with max_width/height scr_size csize = comp->get_min_size(); csize.w = min(csize.w, max_width); csize.h = min(csize.h, max_height); if (scroll_x.is_visible() && !b_show_scroll_y) { // need to add space for x-scroolbar as y-scrolling is out of question csize.h += D_SCROLLBAR_HEIGHT; } csize.w = max(csize.w, scroll_x.get_min_size().w); csize.h = max(csize.h, scroll_y.get_min_size().h); return csize; } scr_size gui_scrollpane_t::get_max_size() const { scr_size csize = comp->get_max_size(); if (csize.h!=scr_size::inf.h && scroll_x.is_visible() && !b_show_scroll_y) { // need to add space for x-scroolbar as y-scrolling is out of question csize.h += D_SCROLLBAR_HEIGHT; } return csize; } /** * recalc the scroll bar sizes */ void gui_scrollpane_t::recalc_sliders(scr_size size) { scroll_x.set_pos(scr_coord(0, size.h - D_SCROLLBAR_HEIGHT)); scroll_y.set_pos(scr_coord(size.w - D_SCROLLBAR_WIDTH, 0)); scr_coord_val off_x = (b_show_scroll_x && scroll_x.is_visible()); scr_coord_val off_y = (b_show_scroll_y && scroll_y.is_visible()); bool need_sizecorner = off_x | off_y; off_x |= need_sizecorner; off_y |= need_sizecorner; scroll_x.set_size(size - D_SCROLLBAR_SIZE*off_y); scroll_x.set_knob(size.w - D_SCROLLBAR_WIDTH*off_y, comp->get_size().w + comp->get_pos().x); scroll_y.set_size(size - D_SCROLLBAR_SIZE*off_x); scroll_y.set_knob(size.h - D_SCROLLBAR_HEIGHT*off_x, comp->get_size().h + comp->get_pos().y); old_comp_size = comp->get_size(); } // just hide or show then sliders according to the object size void gui_scrollpane_t::recalc_sliders_visible(scr_size size) { scr_coord k = comp->get_size() + comp->get_pos(); bool need_x = (k.x > size.w) && b_show_scroll_x; bool need_y = (k.y + need_x * D_SCROLLBAR_HEIGHT > size.h) && b_show_scroll_y; need_x = (k.x + need_y*D_SCROLLBAR_WIDTH > size.w) && b_show_scroll_x; scroll_x.set_visible(need_x); scroll_y.set_visible(need_y); } /** * Scrollpanes _must_ be used in this method to set the size */ void gui_scrollpane_t::set_size(scr_size size) { gui_component_t::set_size(size); recalc_sliders_visible(size); // automatically increase/decrease slider area scr_size c_size = size - comp->get_pos(); // resize scrolled component if (scroll_x.is_visible()) { c_size.h -= D_SCROLLBAR_HEIGHT; } if (scroll_y.is_visible()) { c_size.w -= D_SCROLLBAR_WIDTH; } c_size.clip_lefttop( comp->get_min_size() ); c_size.clip_rightbottom( comp->get_max_size() ); comp->set_size(c_size); recalc_sliders(size); show_focused(); } scr_size gui_scrollpane_t::request_size(scr_size request) { // do not enlarge past max size of comp scr_size cmax = comp->get_max_size(); if (cmax.w < request.w - comp->get_pos().x && cmax.h < request.h - comp->get_pos().y) { request = cmax; } set_size(request); return get_size(); } /** * Events are notified to GUI components via this method */ bool gui_scrollpane_t::infowin_event(const event_t *ev) { bool swallow = false; if( (b_show_scroll_y && scroll_y.is_visible()) && !IS_KEYBOARD(ev) && (scroll_y.getroffen(ev->mouse_pos) || scroll_y.getroffen(ev->click_pos)) ) { event_t ev2 = *ev; ev2.move_origin(scroll_y.get_pos()); b_is_dragging = false; return scroll_y.infowin_event(&ev2); } else if( (b_show_scroll_x && scroll_x.is_visible()) && !IS_KEYBOARD(ev) && (scroll_x.getroffen(ev->mouse_pos) || scroll_x.getroffen(ev->click_pos))) { event_t ev2 = *ev; ev2.move_origin(scroll_x.get_pos()); b_is_dragging = false; return scroll_x.infowin_event(&ev2); } else if( ev->ev_classmouse_pos.x>=0 && ev->mouse_pos.y>=0 && ev->mouse_pos.x<=size.w && ev->mouse_pos.y<=size.h) || b_is_dragging ) { // since we get can grab the focus to get keyboard events, we must make sure to handle mouse events only if we are hit if( ev->ev_class < EVENT_CLICK || IS_WHEELUP(ev) || IS_WHEELDOWN(ev) ) { b_is_dragging = false; } // we will handle dragging ourselves inf not prevented if( b_is_dragging && ev->ev_class < INFOWIN ) { // now drag: scrollbars are not in pixel, but we will scroll one unit per pixels ... scroll_x.set_sticky_bottom(false); scroll_y.set_sticky_bottom(false); scroll_x.set_knob_offset(scroll_x.get_knob_offset() - (ev->mouse_pos.x - origin.x)); scroll_y.set_knob_offset(scroll_y.get_knob_offset() - (ev->mouse_pos.y - origin.y)); origin = ev->mouse_pos; // and finally end dragging on release of any button if( ev->ev_class == EVENT_RELEASE ) { b_is_dragging = false; if( abs(ev->mouse_pos.x - ev->click_pos.x) >= 5 || abs(ev->click_pos.x-ev->mouse_pos.x)+abs(ev->click_pos.y-ev->mouse_pos.y) >= env_t::scroll_threshold ) { // dragged a lot => swallow click return true; } } else { // continue dragging, swallow other events return true; } } // translate according to scrolled position event_t ev2 = *ev; const scr_coord offset(scroll_x.get_knob_offset(), scroll_y.get_knob_offset()); ev2.move_origin(comp->get_pos() - offset); gui_component_t *focused = get_focus(); // hand event to component swallow = comp->infowin_event(&ev2); // now process wheel-events that are not swallowed by component, scroll the pane if(!swallow) { if( (IS_WHEELUP(ev) || IS_WHEELDOWN(ev)) && (((b_show_scroll_y && scroll_y.is_visible()) && !IS_SHIFT_PRESSED(ev)) || ((b_show_scroll_x && scroll_x.is_visible()) && IS_SHIFT_PRESSED(ev))) ) { // otherwise these events are only registered where directly over the scroll region // (and sometime even not then ... ) return IS_SHIFT_PRESSED(ev) ? scroll_x.infowin_event(ev) : scroll_y.infowin_event(ev); } } if( !swallow && b_can_drag && (ev->ev_class == EVENT_CLICK || ev->ev_class == EVENT_DRAG) ) { // init dragging? (Android SDL starts dragging without preceeding click!) if(!b_is_dragging) { origin = ev->mouse_pos; b_is_dragging = true; return true; } } // check if we need to scroll to the focused component gui_component_t *new_focus = get_focus(); if(new_focus && focused != new_focus) { show_focused(); } // hack: component could have changed size // this recalculates the scrollbars if( old_comp_size!=comp->get_size() ) { recalc_sliders(get_size()); } } return swallow; } void gui_scrollpane_t::show_focused() { const gui_component_t *const focused_comp = comp->get_focus(); if( focused_comp ) { const scr_size comp_size = focused_comp->get_size(); const scr_coord relative_pos = comp->get_focus_pos(); if( b_show_scroll_x ) { const sint32 knob_offset_x = scroll_x.get_knob_offset(); const sint32 view_width = size.w-D_SCROLLBAR_WIDTH; if( relative_pos.x+comp_size.wknob_offset_x+view_width ) { scroll_x.set_knob_offset(relative_pos.x+comp_size.w-view_width); } } if( b_show_scroll_y ) { const sint32 knob_offset_y = scroll_y.get_knob_offset(); const sint32 view_height = (b_has_size_corner || b_show_scroll_x) ? size.h-D_SCROLLBAR_HEIGHT : size.h; if( relative_pos.y+comp_size.hknob_offset_y+view_height ) { scroll_y.set_knob_offset(relative_pos.y+comp_size.h-view_height); } } } } /** * Set the position of the Scrollbars */ void gui_scrollpane_t::set_scroll_position(int x, int y) { scroll_x.set_knob_offset(x); scroll_y.set_knob_offset(y); } sint32 gui_scrollpane_t::get_scroll_x() const { return scroll_x.get_knob_offset(); } sint32 gui_scrollpane_t::get_scroll_y() const { return scroll_y.get_knob_offset(); } scr_rect gui_scrollpane_t::get_client( void ) { scr_rect client( pos, pos+size ); if( b_show_scroll_x && scroll_x.is_visible() ) { client.h -= D_SCROLLBAR_HEIGHT; } if( b_show_scroll_y && scroll_y.is_visible() ) { client.w -= D_SCROLLBAR_WIDTH; } return client; } /** * Draw the component */ void gui_scrollpane_t::draw(scr_coord pos) { // check, if we need to recalc slider size if( old_comp_size != comp->get_size() ) { recalc_sliders( size ); } // get client area (scroll panel - scrollbars) scr_rect client = get_client() + pos; PUSH_CLIP_FIT( client.x, client.y, client.w, client.h ) comp->draw( client.get_pos()-scr_coord(scroll_x.get_knob_offset(), scroll_y.get_knob_offset()) ); POP_CLIP() // sliding bar background color is now handled by the scrollbar! if( b_show_scroll_x && scroll_x.is_visible() ) { scroll_x.draw( pos+get_pos() ); } if( b_show_scroll_y && scroll_y.is_visible() ) { scroll_y.draw( pos+get_pos() ); } } void gui_scrollpane_t::rdwr( loadsave_t *file ) { sint32 x = get_scroll_x(); sint32 y = get_scroll_y(); file->rdwr_long(x); file->rdwr_long(y); if (file->is_loading()) { set_scroll_position(x, y); } } simutrans-124.5/src/simutrans/gui/components/gui_scrollpane.h000066400000000000000000000071311520675460500245320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_SCROLLPANE_H #define GUI_COMPONENTS_GUI_SCROLLPANE_H #include "gui_component.h" #include "gui_scrollbar.h" class loadsave_t; /* * this is a scrolling area in which subcomponents are drawn */ class gui_scrollpane_t : public gui_component_t { private: scr_size old_comp_size; bool b_show_scroll_x:1; bool b_show_scroll_y:1; bool b_has_size_corner:1; bool maximize:1; bool b_can_drag:1; bool b_is_dragging:1; // start of dragging scr_coord origin; protected: /** * The scrolling component */ gui_component_t *comp; /** * Scrollbar X/Y */ scrollbar_t scroll_x, scroll_y; // for oversized entries scr_coord_val max_width; scr_coord_val max_height; void recalc_sliders_visible(scr_size size); void recalc_sliders(scr_size size); public: /** * @param comp the scrolling component * @param b_scroll_x * @param b_scroll_y */ gui_scrollpane_t(gui_component_t *comp, bool b_scroll_x = false, bool b_scroll_y = true); void set_component(gui_component_t *comp) { this->comp = comp; } void set_allow_dragging(bool b) { b_can_drag = b; } bool has_visible_x_scrollbar() { return scroll_x.is_visible(); } bool has_visible_y_scrollbar() { return scroll_y.is_visible(); } /** * this is the maximum width a scrollbar requests as minimum size * default is the stadard width of a dialoge (4*button width+3*space) * @param width the minimum width it should strech to */ virtual void set_min_width( scr_coord_val width ) { max_width = width; } /** * This method MUST be used to set the size of scrollpanes. */ void set_size(scr_size size) OVERRIDE; /** * Request other pane-size. * @returns realized size. */ scr_size request_size(scr_size request); /** * Set the position of the Scrollbars */ void set_scroll_position(int x, int y); scr_rect get_client( void ) OVERRIDE; int get_scroll_x() const; int get_scroll_y() const; void set_scroll_amount_x(const sint32 sa) { scroll_x.set_scroll_amount(sa); } void set_scroll_amount_y(const sint32 sa) { scroll_y.set_scroll_amount(sa); } void set_scroll_discrete_x(const bool sd) { scroll_x.set_scroll_discrete(sd); } void set_scroll_discrete_y(const bool sd) { scroll_y.set_scroll_discrete(sd); } bool infowin_event(event_t const*) OVERRIDE; /** * Draw the component */ void draw(scr_coord offset) OVERRIDE; void set_show_scroll_x(bool yesno) { b_show_scroll_x = yesno; } void set_show_scroll_y(bool yesno) { b_show_scroll_y = yesno; } void set_scrollbar_mode(scrollbar_t::visible_mode_t mode) { scroll_x.set_visible_mode(mode); scroll_y.set_visible_mode(mode); } void set_size_corner(bool yesno) { b_has_size_corner = yesno; } /** * Returns true if the hosted component is focusable */ bool is_focusable() OVERRIDE { return comp->is_focusable(); } /** * returns element that has the focus */ gui_component_t *get_focus() OVERRIDE { return comp->get_focus(); } /** * Adjust scrollbars to make focused element visible if necessary */ void show_focused(); /** * Get the relative position of the focused component. * Used for auto-scrolling inside a scroll pane. */ scr_coord get_focus_pos() OVERRIDE { return pos + ( comp->get_focus_pos() - scr_coord( scroll_x.get_knob_offset(), scroll_y.get_knob_offset() ) ); } scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; /// load/save scrollbar positions void rdwr( loadsave_t *file ); bool is_marginless() const OVERRIDE { return maximize; } void set_maximize(bool b) { maximize = b; } }; #endif simutrans-124.5/src/simutrans/gui/components/gui_speedbar.cc000066400000000000000000000104371520675460500243160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "gui_speedbar.h" #include "../gui_theme.h" #include "../../display/simgraph.h" #include "../../simcolor.h" #include "../../simtypes.h" void gui_speedbar_t::set_base(sint32 base) { this->base = base!=0 ? base : 1; } void gui_speedbar_t::add_color_value(const sint32 *value, PIXVAL color) { info_t next = { color, value, -1 }; values.insert(next); } scr_size gui_speedbar_t::get_min_size() const { return D_INDICATOR_SIZE; } scr_size gui_speedbar_t::get_max_size() const { return scr_size(scr_size::inf.w, D_INDICATOR_HEIGHT); } void gui_speedbar_t::draw(scr_coord offset) { offset += pos; if(vertical) { sint32 from = size.h; for(info_t const& i : values) { sint32 const to = size.h - min(*i.value, base) * size.h / base; if(to < from) { gfx->draw_rect_clipped(offset.x, offset.y + to, size.w, from - to, i.color, true CLIP_NUM_DEFAULT); from = to - 1; } } if(from > 0) { gfx->draw_rect_clipped( offset.x, offset.y, size.w, from, gfx->palette_lookup(MN_GREY0), true CLIP_NUM_DEFAULT); } } else { sint32 from = 0; for(info_t const& i : values) { sint32 const to = min(*i.value, base) * size.w / base; if(to > from) { gfx->draw_rect_clipped(offset.x + from, offset.y, to - from, size.h, i.color, true CLIP_NUM_DEFAULT); from = to + 1; } } if(from < size.w) { gfx->draw_rect_clipped(offset.x + from, offset.y, size.w - from, size.h, gfx->palette_lookup(MN_GREY0), true CLIP_NUM_DEFAULT); } } } void gui_speedbar_fixed_length_t::draw(scr_coord offset) { offset += pos; sint32 from = 0; for (info_t const& i : values) { sint32 const to = min(*i.value, base) * fixed_width / base; if (to > from) { gfx->draw_rect_clipped(offset.x + from, offset.y, to - from, size.h, i.color, true CLIP_NUM_DEFAULT); from = to + 1; } if (from > size.w) { // reached size already break; } } if (from < size.w) { gfx->draw_rect_clipped(offset.x + from, offset.y, size.w - from, size.h, gfx->palette_lookup(MN_GREY0), true CLIP_NUM_DEFAULT); } } void gui_routebar_t::set_base(sint32 base) { this->base = base != 0 ? base : 1; } void gui_routebar_t::set_reservation(const sint32 *value, PIXVAL color) { reserve_value = value; reserved_color= color; } void gui_routebar_t::init(const sint32 *value, uint8 state) { this->value = value; this->state = state; } void gui_routebar_t::set_state(uint8 state) { this->state = state; } void gui_routebar_t::draw(scr_coord offset) { uint8 h = size.h % 2 ? size.h : size.h-1; if (h < 5) { h = 5; set_size( scr_size(size.w, h) ); } const uint16 w = size.w - h/2; offset += pos; gfx->draw_rect_clipped(offset.x, offset.y+h/2-1, w, 3, gfx->palette_lookup(MN_GREY1), true CLIP_NUM_DEFAULT); PIXVAL col; for (uint8 i = 0; i<5; i++) { col = i % 2 ? COL_GREY4-1 : MN_GREY0; gfx->draw_vline_clipped(offset.x + h/2 + w*i/4, offset.y+i%2, h-(i%2)*2, gfx->palette_lookup(col), true CLIP_NUM_DEFAULT); } sint32 const to = min(*value, base) * w / base; if (reserve_value && *reserve_value) { sint32 const reserved_to = min(*reserve_value, base) * w / base; gfx->draw_rect_clipped(offset.x + h / 2, offset.y + h / 2 - 1, reserved_to, 3, reserved_color, true CLIP_NUM_DEFAULT); } gfx->draw_rect_clipped(offset.x+h/2, offset.y+h/2-1, to, 3, gfx->palette_lookup(43), true CLIP_NUM_DEFAULT); switch (state) { case 1: gfx->draw_rect_clipped(offset.x + to + 1, offset.y + 1, h - 2, h - 2, gfx->palette_lookup(COL_YELLOW), true CLIP_NUM_DEFAULT); break; case 2: gfx->draw_rect_clipped(offset.x + to + 1, offset.y + 1, h - 2, h - 2, gfx->palette_lookup(COL_ORANGE), true CLIP_NUM_DEFAULT); break; case 3: gfx->draw_rect_clipped(offset.x + to + 1, offset.y + 1, h - 2, h - 2, gfx->palette_lookup(COL_RED), true CLIP_NUM_DEFAULT); break; case 0: default: gfx->draw_rect_clipped( offset.x+h/2, offset.y + 1, to-2, h - 2, gfx->palette_lookup( COL_GREEN ), true CLIP_NUM_DEFAULT); gfx->draw_right_triangle(offset.x + to, offset.y, h, gfx->palette_lookup(COL_MAGENTA), true); break; } } scr_size gui_routebar_t::get_min_size() const { return scr_size(D_INDICATOR_WIDTH, height); } scr_size gui_routebar_t::get_max_size() const { return scr_size(scr_size::inf.w, height); } simutrans-124.5/src/simutrans/gui/components/gui_speedbar.h000066400000000000000000000037101520675460500241540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_SPEEDBAR_H #define GUI_COMPONENTS_GUI_SPEEDBAR_H #include "gui_component.h" #include "../../tpl/slist_tpl.h" class gui_speedbar_t : public gui_component_t { protected: struct info_t { PIXVAL color; const sint32 *value; sint32 last; }; slist_tpl values; sint32 base; bool vertical; public: gui_speedbar_t() { base = 100; vertical = false; } void add_color_value(const sint32 *value, PIXVAL color); void set_base(sint32 base); void set_vertical(bool vertical) { this->vertical = vertical; } /** * Draw the component */ void draw(scr_coord offset) OVERRIDE; scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; }; class gui_speedbar_fixed_length_t : public gui_speedbar_t { scr_coord_val fixed_width; public: gui_speedbar_fixed_length_t() : gui_speedbar_t(), fixed_width(10) { set_vertical(false); } scr_size get_max_size() const OVERRIDE { return scr_size(fixed_width, gui_speedbar_t::get_min_size().h); } void set_width(scr_coord_val w) { fixed_width = w; } void draw(scr_coord offset) OVERRIDE; }; // route progress bar class gui_routebar_t : public gui_component_t { private: const sint32 *value; const sint32 *reserve_value; sint32 base; uint8 state; PIXVAL reserved_color; scr_coord_val height; public: gui_routebar_t() { base = 100; state = 0; height = 9; value = 0; reserve_value = 0; } void set_reservation(const sint32 *value, PIXVAL color = gfx->palette_lookup(COL_BLUE)); void set_reserved_color(PIXVAL color) { reserved_color = color; }; void set_base(sint32 base); void init(const sint32 *value, uint8 state); void set_state(uint8 state); /** * Draw the component */ void draw(scr_coord offset) OVERRIDE; void set_height(scr_coord_val h) { height = h; }; scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_tab_panel.cc000066400000000000000000000257241520675460500244630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_tab_panel.h" #include "../gui_frame.h" #include "../../simevent.h" #include "../../dataobj/environment.h" #include "../../display/simgraph.h" #include "../../simcolor.h" #include "../simwin.h" #include "../../world/simworld.h" #include "../../descriptor/skin_desc.h" #define IMG_WIDTH 20 scr_coord_val gui_tab_panel_t::header_vsize = 18; gui_tab_panel_t::gui_tab_panel_t() : required_size( 8, D_TAB_HEADER_HEIGHT ) { active_tab = 0; tab_offset_x = 0; is_dragging = false; left.init( button_t::arrowleft, NULL, scr_coord(0,0) ); left.add_listener( this ); right.init( button_t::arrowright, NULL, scr_coord(0,0) ); right.add_listener( this ); } void gui_tab_panel_t::add_tab(gui_component_t *c, const char *name, const skin_desc_t *desc, const char *tooltip ) { tabs.append( tab(c, desc?NULL:name, desc?desc->get_image(0):NULL, tooltip) ); // only call set_size, if size was already assigned if (size.w > 0 && size.h > 0) { set_size( get_size() ); } } void gui_tab_panel_t::set_size(scr_size size) { gui_component_t::set_size(size); required_size = scr_size( 8, required_size.h ); gui_component_t *last_component = NULL; for(tab & i : tabs) { i.x_offset = required_size.w - 4; if( i.title ) { i.width = D_H_SPACE*2 + gfx->calc_text_width( i.title ); required_size.h = max( required_size.h, LINESPACE + D_V_SPACE ); } else if( i.img ) { i.width = max( 2 + i.img->get_pic()->w, D_H_SPACE*2+IMG_WIDTH );; required_size.h = max( required_size.h, i.img->get_pic()->h + D_V_SPACE ); } else { i.width = 8+IMG_WIDTH; } required_size.w += i.width; if (i.component != last_component) { i.component->set_pos(scr_coord(0, required_size.h)); i.component->set_size(get_size() - scr_size(0, required_size.h)); last_component = i.component; } } if( required_size.w > size.w || tab_offset_x > 0 ) { left.set_pos( scr_coord( 0, 0 ) ); left.set_size( scr_size( D_ARROW_LEFT_WIDTH, required_size.h ) ); right.set_pos( scr_coord( size.w-D_ARROW_RIGHT_WIDTH, 0 ) ); right.set_size( scr_size( D_ARROW_RIGHT_WIDTH, required_size.h ) ); } else { tab_offset_x = 0; } } scr_size gui_tab_panel_t::get_min_size() const { scr_size t_size(0, required_size.h); scr_size c_size(0, 0); gui_component_t *last_component = NULL; for(tab const& iter : tabs) { if (iter.title) { t_size.h = max(t_size.h, LINESPACE + D_V_SPACE); } if (iter.component != last_component) { scr_size cs = iter.component->get_min_size(); if (!iter.component->is_marginless()) { cs.h += D_MARGIN_BOTTOM; } c_size.clip_lefttop( cs ); last_component = iter.component; } } return t_size+c_size; } bool gui_tab_panel_t::action_triggered(gui_action_creator_t *comp, value_t) { if( comp == &right ) { tab_offset_x += 32; } else if( comp == &left ) { tab_offset_x -= 32; } tab_offset_x = clamp(tab_offset_x, 0, required_size.w - size.w); return true; } bool gui_tab_panel_t::tab_getroffen(scr_coord p) { return p.x >= 0 && p.x < size.w && p.y >= 0 && p.y < required_size.h; } bool gui_tab_panel_t::infowin_event(const event_t *ev) { if (gui_component_t *t=get_aktives_tab()) { if (t->get_focus()) { // we must go through the containers or we do not get the correct offsets event_t ev2 = *ev; ev2.move_origin(t->get_pos()); if (t->infowin_event(&ev2)) { return true; } } } // since we get can grab the focus to get keyboard events, we must make sure to handle mouse events only if we are hit if (ev->ev_class < EVENT_CLICK || IS_WHEELUP(ev) || IS_WHEELDOWN(ev)) { is_dragging = false; } if( required_size.w > size.w && tab_getroffen(ev->click_pos) ) { if (!is_dragging) { // handle scroll buttons pressed if( left.getroffen(ev->click_pos) ) { event_t ev2 = *ev; ev2.move_origin(left.get_pos()); return left.infowin_event(&ev2); } if( right.getroffen(ev->click_pos) ) { event_t ev2 = *ev; ev2.move_origin(right.get_pos()); return right.infowin_event(&ev2); } } if (ev->ev_class == EVENT_CLICK || ev->ev_class == EVENT_DRAG) { // init dragging? (Android SDL starts dragging without preceeding click!) is_dragging = true; return true; } } // we will handle dragging ourselves inf not prevented if(is_dragging && ev->ev_class < INFOWIN) { // now drag: scrollbars are not in pixel, but we will scroll one unit per pixels ... tab_offset_x -= (ev->mouse_pos.x - ev->click_pos.x); tab_offset_x = clamp(tab_offset_x, 0, required_size.w - size.w); // and finally end dragging on release of any button if (ev->ev_class == EVENT_RELEASE) { is_dragging = false; if (abs(ev->mouse_pos.x - ev->click_pos.x) >= 5 || abs(ev->click_pos.x - ev->mouse_pos.x) + abs(ev->click_pos.y - ev->mouse_pos.y) >= env_t::scroll_threshold) { // dragged a lot => swallow click return true; } } else { // continue dragging, swallow other events return true; } } if( IS_LEFTRELEASE(ev) && tab_getroffen(ev->click_pos) ) { // tab selector was hit int text_x = (required_size.w>size.w ? D_ARROW_LEFT_WIDTH : 0) + D_H_SPACE - tab_offset_x; int k=0; for(tab const& i : tabs) { if (!i.component->is_visible()) { continue; } if (text_x <= ev->mouse_pos.x && text_x + i.width > ev->mouse_pos.x) { // either tooltip or change active_tab = k; call_listeners((long)active_tab); return true; } k++; text_x += i.width; } return false; } // navigate among the tabs using shift+tab and tab if( IS_KEYDOWN(ev) && ev->ev_code == SIM_KEYCODE_TAB ) { int di = 1; // tab -> go to the next tab if( IS_SHIFT_PRESSED(ev) ) { // shift+tab -> go to the previous tab di = -1; } // change index by di*i for(int i = 1; i<(int)tabs.get_count(); i++) { const int next_tab_idx = (active_tab + tabs.get_count() + di*i) % tabs.get_count(); if (tabs.at(next_tab_idx).component->is_visible()) { active_tab = next_tab_idx; call_listeners((long)active_tab); break; } } return true; } if( IS_KEYDOWN(ev) || DOES_WINDOW_CHILDREN_NEED(ev) || get_aktives_tab()->getroffen(ev->mouse_pos) || get_aktives_tab()->getroffen(ev->click_pos)) { // active tab was hit event_t ev2 = *ev; ev2.move_origin(get_aktives_tab()->get_pos()); return get_aktives_tab()->infowin_event(&ev2); } return false; } void gui_tab_panel_t::draw(scr_coord parent_pos) { // if active tab is invisible, choose the previous visible one if (!tabs.at(active_tab).component->is_visible()) { for(int i = 1; i<(int)tabs.get_count(); i++) { const int next_tab_idx = (active_tab + tabs.get_count() - i) % tabs.get_count(); if (tabs.at(next_tab_idx).component->is_visible()) { active_tab = next_tab_idx; break; } } } // Position in screen/window int xpos = parent_pos.x + pos.x; const int ypos = parent_pos.y + pos.y; if( required_size.w>size.w || tab_offset_x > 0) { left.draw( parent_pos+pos ); right.draw( parent_pos+pos ); //gfx->draw_rect_clipped(xpos, ypos+required_size.h-1, 10, 1, SYSCOL_TEXT_HIGHLIGHT, true CLIP_NUM_DEFAULT); gfx->draw_rect_clipped(xpos, ypos+required_size.h-1, D_ARROW_LEFT_WIDTH, 1, SYSCOL_HIGHLIGHT, true CLIP_NUM_DEFAULT); xpos += D_ARROW_LEFT_WIDTH; } int text_x = xpos + D_H_SPACE; int text_y = ypos + (required_size.h - LINESPACE)/2; //gfx->draw_rect_clipped(xpos, ypos+required_size.h-1, 4, 1, gfx->palette_lookup(COL_WHITE), true CLIP_NUM_DEFAULT); gfx->draw_rect_clipped(xpos, ypos+required_size.h-1, 4, 1, SYSCOL_HIGHLIGHT, true CLIP_NUM_DEFAULT); // do not draw under right button int xx = required_size.w>get_size().w ? get_size().w-(D_ARROW_LEFT_WIDTH+2+D_ARROW_RIGHT_WIDTH) : get_size().w; text_x -= tab_offset_x; int i=0; for(tab const& iter : tabs) { if (!iter.component->is_visible()) { continue; } // set clipping PUSH_CLIP_FIT(xpos, ypos, xx, required_size.h); // only start drawing here ... char const* const text = iter.title; if (i != active_tab) { // Non active tabs gfx->draw_rect_clipped(text_x+1, ypos+2, iter.width-2, 1, SYSCOL_HIGHLIGHT, true CLIP_NUM_DEFAULT); gfx->draw_rect_clipped(text_x, ypos+required_size.h-1, iter.width-2, 1, SYSCOL_HIGHLIGHT, true CLIP_NUM_DEFAULT); gfx->draw_vline_clipped(text_x, ypos+3, required_size.h-4, SYSCOL_HIGHLIGHT, true CLIP_NUM_DEFAULT); gfx->draw_vline_clipped(text_x+iter.width-1, ypos+3, required_size.h-4, SYSCOL_SHADOW, true CLIP_NUM_DEFAULT); if(text) { gfx->draw_text_clipped(text_x+D_H_SPACE, text_y+2, text, ALIGN_LEFT, SYSCOL_TEXT, true); } else { scr_coord_val const y = ypos - iter.img->get_pic()->y + required_size.h / 2 - iter.img->get_pic()->h / 2 + 1; scr_coord_val const x = text_x - iter.img->get_pic()->x + iter.width / 2 - iter.img->get_pic()->w / 2; // display_img_blend(iter.img->get_id(), x, y, TRANSPARENT50_FLAG, false, true); gfx->draw_base_img(iter.img->get_id(), x, y, 1, false, true CLIP_NUM_DEFAULT); } } else { // Active tab gfx->draw_rect_clipped(text_x+1, ypos, iter.width-2, 1, SYSCOL_HIGHLIGHT, true CLIP_NUM_DEFAULT); gfx->draw_vline_clipped(text_x, ypos+1, required_size.h-2, SYSCOL_HIGHLIGHT, true CLIP_NUM_DEFAULT); gfx->draw_vline_clipped(text_x+iter.width-1, ypos+1, required_size.h-2, SYSCOL_SHADOW, true CLIP_NUM_DEFAULT); if(text) { gfx->draw_text_clipped(text_x+D_H_SPACE, text_y, text, ALIGN_LEFT, SYSCOL_TEXT_HIGHLIGHT, true); } else { scr_coord_val const y = ypos - iter.img->get_pic()->y + required_size.h / 2 - iter.img->get_pic()->h / 2 - 1; scr_coord_val const x = text_x - iter.img->get_pic()->x + iter.width / 2 - iter.img->get_pic()->w / 2; gfx->draw_base_img(iter.img->get_id(), x, y, 1, false, true CLIP_NUM_DEFAULT); } } text_x += iter.width; // reset clipping POP_CLIP(); i++; } gfx->draw_rect_clipped(text_x, ypos+required_size.h-1, xpos+size.w-text_x, 1, SYSCOL_HIGHLIGHT, true CLIP_NUM_DEFAULT); // draw tab content after tab row // (combobox may open to above, and tab row may draw into it) get_aktives_tab()->draw(parent_pos + pos); // now for tooltips ... int my = get_mouse_pos().y-parent_pos.y-pos.y-6; if(my>=0 && my < required_size.h-1) { // Reiter getroffen? int mx = get_mouse_pos().x-parent_pos.x-pos.x; int text_x = D_H_SPACE + tab_offset_x; for(tab const& iter : tabs) { if(text_x <= mx && text_x+iter.width > mx && (required_size.w<=get_size().w || mx < right.get_pos().x-12)) { // tooltip or change const scr_coord tooltip_pos{ get_mouse_pos().x + 16, ypos + required_size.h + 12 }; win_set_tooltip(tooltip_pos, iter.tooltip, &iter, this); break; } text_x += iter.width; } } } void gui_tab_panel_t::clear() { tabs.clear(); active_tab = 0; } void gui_tab_panel_t::take_tabs(gui_tab_panel_t* other) { tabs.append_list(other->tabs); } void gui_tab_panel_t::rdwr( loadsave_t *file ) { sint32 a = get_active_tab_index(); file->rdwr_long(a); if (file->is_loading()) { set_active_tab_index(a); } } simutrans-124.5/src/simutrans/gui/components/gui_tab_panel.h000066400000000000000000000056101520675460500243150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_TAB_PANEL_H #define GUI_COMPONENTS_GUI_TAB_PANEL_H #include "../../display/simimg.h" #include "../../descriptor/skin_desc.h" #include "gui_action_creator.h" #include "gui_component.h" #include "gui_button.h" class image_t; class loadsave_t; /** * A class for distribution of tabs through the gui_component_t component. */ class gui_tab_panel_t : public gui_action_creator_t, public action_listener_t, public gui_component_t { private: struct tab { tab(gui_component_t* c, const char *name, const image_t *b, const char *tool) : component(c), title(name), img(b), tooltip(tool), x_offset(4) {} gui_component_t* component; const char *title; const image_t *img; const char *tooltip; sint16 x_offset; sint16 width; }; slist_tpl tabs; int active_tab; scr_size required_size; scr_coord_val tab_offset_x; button_t left, right; bool is_dragging; bool tab_getroffen(scr_coord p); public: static scr_coord_val header_vsize; gui_tab_panel_t(); /** * Add new tab to tab bar * * @param c is tab component * @param name is name for tab component * @param b * @param tooltip */ void add_tab(gui_component_t *c, const char *name, const skin_desc_t *b=NULL, const char *tooltip=NULL ); /** * Get the active component/active tab */ gui_component_t* get_aktives_tab() const { return get_tab(active_tab); } gui_component_t* get_tab( uint8 i ) const { return i < tabs.get_count() ? tabs.at(i).component : NULL; } int get_active_tab_index() const { return min((int)tabs.get_count()-1,active_tab); } void set_active_tab_index( int i ) { active_tab = min((int)tabs.get_count()-1,i); } bool infowin_event(event_t const*) OVERRIDE; /** * Draw tabs */ void draw(scr_coord offset) OVERRIDE; /** * Resizing must be propagated! */ void set_size(scr_size size) OVERRIDE; /** * Remove all tabs. */ void clear(); /** * How many tabs we have? */ uint32 get_count () const { return tabs.get_count(); } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; /** * Returns true if the hosted component of the active tab is focusable */ bool is_focusable() OVERRIDE { return get_aktives_tab()->is_focusable(); } gui_component_t *get_focus() OVERRIDE { return get_aktives_tab()->get_focus(); } /** * Get the relative position of the focused component. * Used for auto-scrolling inside a scroll pane. */ scr_coord get_focus_pos() OVERRIDE { return pos + get_aktives_tab()->get_focus_pos(); } scr_size get_min_size() const OVERRIDE; // size of tab header scr_size get_required_size() const { return required_size; } bool is_marginless() const OVERRIDE { return true; } /** * Take tabs from other tab. */ void take_tabs(gui_tab_panel_t* other); /// save active tab void rdwr( loadsave_t *file ); }; #endif simutrans-124.5/src/simutrans/gui/components/gui_textarea.cc000066400000000000000000000054121520675460500243430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "gui_textarea.h" #include "../../display/simgraph.h" #include "../../simdebug.h" #include "../../simcolor.h" #include "../../simskin.h" #include "../gui_theme.h" #include "../../utils/cbuffer.h" #define L_PADDING_RIGHT (10) gui_textarea_t::gui_textarea_t(cbuffer_t* buf_) { set_buf(buf_); } void gui_textarea_t::set_buf( cbuffer_t* buf_ ) { buf = buf_; recalc_size(); } scr_size gui_textarea_t::get_min_size() const { return calc_size(); } scr_size gui_textarea_t::get_max_size() const { return get_min_size(); } void gui_textarea_t::recalc_size() { set_size(calc_size()); } scr_size gui_textarea_t::calc_size() const { if (buf) { const char *text(*buf); // since we also want to dynamically change the size of the component int new_lines=0; scr_coord_val x_size = 0; if ( (text != NULL) && (*text != '\0') ) { const char *buf=text; const char *next; do { next = strchr(buf, '\n'); const size_t len = next ? next-buf : 99999; const int px_len = gfx->calc_text_width_n(buf, len); if( px_len > x_size ) { x_size = px_len; } new_lines += LINESPACE; } while( next != NULL && ((void)(buf = next+1), *buf!=0) ); } if (x_size > 0) { x_size += L_PADDING_RIGHT; } return scr_size( x_size, new_lines ); } else { return scr_size(0, 0); } } /** * Draw the component */ void gui_textarea_t::draw(scr_coord offset) { const char *text(*buf); // we cannot use: display_multiline_text(pos.x+offset.x, pos.y+offset.y+10, text, gfx->palette_lookup(COL_BLACK)); // since we also want to dynamically change the size of the component scr_coord_val new_lines = 0; // keep previous maximum width scr_coord_val x_size = get_size().w - L_PADDING_RIGHT; if ( (text != NULL) && (*text != '\0') ) { const char *buf=text; const char *next; const scr_coord_val x = pos.x+offset.x; scr_coord_val y = pos.y+offset.y; do { next = strchr(buf, '\n'); const size_t len = next != NULL ? next - buf : -1; scr_coord_val const draw_y = y + new_lines; int px_len; if ( -LINESPACE <= draw_y && draw_y <= gfx->get_screen_size().h + LINESPACE) { // draw when in screen area px_len = gfx->draw_text_clipped_n(x, draw_y, buf, ALIGN_LEFT | DT_CLIP, SYSCOL_TEXT, true, len CLIP_NUM_DEFAULT); } else { // track required length when out of screen area px_len = gfx->calc_text_width_n(buf, len); } if(px_len>x_size) { x_size = px_len; } new_lines += LINESPACE; } while( next != NULL && ((void)(buf = next+1), *buf!=0) ); } scr_size size( max( x_size + L_PADDING_RIGHT, get_size().w ), new_lines ); if( size!=get_size() ) { set_size(size); } } simutrans-124.5/src/simutrans/gui/components/gui_textarea.h000066400000000000000000000016011520675460500242010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_TEXTAREA_H #define GUI_COMPONENTS_GUI_TEXTAREA_H #include "gui_component.h" class cbuffer_t; /** * A text display component */ class gui_textarea_t : public gui_component_t { private: /** * The text to display. May be multi-lined. */ cbuffer_t* buf; /** * recalc the current size, needed for speculative size calculations * @returns size necessary to show the component */ scr_size calc_size() const; public: gui_textarea_t(cbuffer_t* buf_); void set_buf( cbuffer_t* buf_ ); /** * recalc the current size, needed for speculative size calculations */ void recalc_size(); /** * Draw the component */ void draw(scr_coord offset) OVERRIDE; scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_textinput.cc000066400000000000000000000645361520675460500246060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "gui_textinput.h" #include "../simwin.h" #include "../../dataobj/translator.h" #include "../../utils/simstring.h" #include "../../utils/unicode.h" #include "../../sys/simsys.h" gui_textinput_t::gui_textinput_t() : gui_component_t(true), text(NULL), placeholder_text(NULL), composition(), composition_target_start(0), composition_target_length(0), max(0), head_cursor_pos(0), tail_cursor_pos(0), scroll_offset(0), align(ALIGN_LEFT), textcol(SYSCOL_EDIT_TEXT), text_dirty(false), focus_received(false), enabled(true), notify_all_changes_delay(0xFFFF), cursor_reference_time(0) { } scr_size gui_textinput_t::get_min_size() const { return scr_size(4*LINESPACE, ::max(LINESPACE+4, D_EDIT_HEIGHT) ); } scr_size gui_textinput_t::get_max_size() const { return scr_size( scr_size::inf.w, ::max(LINESPACE+4, D_EDIT_HEIGHT) ); } /** * determine new cursor position from event coordinates */ size_t gui_textinput_t::calc_cursor_pos(const int x) { size_t new_cursor_pos = 0; if( text ) { const char* tmp_text = text; uint8 byte_length = 0; uint8 pixel_width = 0; scr_coord_val current_offset = 0; const scr_coord_val adjusted_offset = x - 1 + scroll_offset; while( gfx->get_next_char_with_metrics(tmp_text, byte_length, pixel_width) && adjusted_offset>(current_offset+(pixel_width>>1)) ) { current_offset += pixel_width; new_cursor_pos += byte_length; } } return new_cursor_pos; } /** * Remove selected text portion, if any. * Returns true if some selected text is actually deleted. */ bool gui_textinput_t::remove_selection() { if( head_cursor_pos!=tail_cursor_pos ) { size_t len = strlen(text); size_t start_pos = min(head_cursor_pos, tail_cursor_pos); size_t end_pos = min(len, ::max(head_cursor_pos, tail_cursor_pos) ); tail_cursor_pos = head_cursor_pos = start_pos; do { text_dirty = true; text[start_pos++] = text[end_pos]; } while( text[end_pos++]!=0 ); return true; } return false; } void gui_textinput_t::set_composition_status( char *c, int start, int length ) { composition.clear(); if( win_get_focus()==this ) { if( c && c[0]!='\0' ) { if( head_cursor_pos!=tail_cursor_pos ) { remove_selection(); } composition.clear(); composition.append( (char *)c ); composition_target_start = start; composition_target_length = length; const scr_coord gui_xy = win_get_pos( win_get_top() ); const int offset_to_target = gfx->calc_text_width_n( composition.get_str(), composition_target_start ); const scr_coord_val x = pos.x + gui_xy.x + get_current_cursor_x() + offset_to_target; const scr_coord_val y = pos.x + gui_xy.y + D_TITLEBAR_HEIGHT; dr_notify_input_pos({ x, y }); } } } /** * Events are notified to GUI components via this method */ bool gui_textinput_t::infowin_event(const event_t *ev) { if (!enabled) { return false; } if( IS_KEYDOWN(ev) ) { if( text ) { size_t len = strlen(text); switch(ev->ev_code) { // handled by container case SIM_KEYCODE_ENTER: if( text_dirty ) { text_dirty = false; call_listeners((long)INPUT_FINISHED); } head_cursor_pos = len; tail_cursor_pos = 0; return false; case SIM_KEYCODE_TAB: // focus is going to be lost -> reset cursor positions to select the whole text by default head_cursor_pos = len; tail_cursor_pos = 0; return false; case SIM_KEYCODE_ESCAPE: return false; case 1: // if Ctrl-A -> select the whole text if( IS_CONTROL_PRESSED(ev) ) { head_cursor_pos = len; tail_cursor_pos = 0; } break; case 3: // if Ctrl-C -> copy selected text to clipboard if( IS_CONTROL_PRESSED(ev) && head_cursor_pos!=tail_cursor_pos ) { const size_t start_pos = min(head_cursor_pos, tail_cursor_pos); const size_t end_pos = ::max(head_cursor_pos, tail_cursor_pos); dr_copy(text + start_pos, end_pos - start_pos); } break; case 22: // if Ctrl-V -> paste selected text to cursor position if( IS_CONTROL_PRESSED(ev) ) { if( remove_selection() ) { // recalculate text length after deleting selection len = strlen(text); } tail_cursor_pos = ( head_cursor_pos += dr_paste(text + head_cursor_pos, max - len - 1) ); text_dirty = true; next_update_call = notify_all_changes_delay == 0xFFFFu ? 0xFFFFFFFFul : dr_time() + notify_all_changes_delay; } break; case 24: // if Ctrl-X -> cut and copy selected text to clipboard if( IS_CONTROL_PRESSED(ev) && head_cursor_pos!=tail_cursor_pos ) { const size_t start_pos = min(head_cursor_pos, tail_cursor_pos); const size_t end_pos = ::max(head_cursor_pos, tail_cursor_pos); dr_copy(text + start_pos, end_pos - start_pos); remove_selection(); text_dirty = true; next_update_call = notify_all_changes_delay == 0xFFFFu ? 0xFFFFFFFFul : dr_time() + notify_all_changes_delay; } break; case SIM_KEYCODE_DOWN: // down arrow // not used currently break; case SIM_KEYCODE_LEFT: // left arrow if( head_cursor_pos>0 ) { // Ctrl key pressed -> skip over to the start of the previous word (as delimited by space(s)) if( IS_CONTROL_PRESSED(ev) ) { const char* tmp_text = text + head_cursor_pos; uint8 byte_length = 0; uint8 pixel_width = 0; // first skip over all contiguous space characters to the left while( head_cursor_pos>0 && gfx->get_prev_char_with_metrics(tmp_text, text, byte_length, pixel_width)==SIM_KEYCODE_SPACE ) { head_cursor_pos -= byte_length; } // revert text pointer for further processing if( head_cursor_pos>0 ) { tmp_text += byte_length; } // then skip over all contiguous non-space characters further to the left while( head_cursor_pos>0 && gfx->get_prev_char_with_metrics(tmp_text, text, byte_length, pixel_width)!=SIM_KEYCODE_SPACE ) { head_cursor_pos -= byte_length; } } else { head_cursor_pos = utf8_get_prev_char(text, head_cursor_pos); } } // do not update tail cursor if SHIFT key is pressed -> enables text selection if( !IS_SHIFT_PRESSED(ev) ) { tail_cursor_pos = head_cursor_pos; } break; case SIM_KEYCODE_RIGHT: // right arrow if( head_cursor_pos skip over to the start of the next word (as delimited by space(s)) if( IS_CONTROL_PRESSED(ev) ) { const char* tmp_text = text + head_cursor_pos; uint8 byte_length = 0; uint8 pixel_width = 0; // first skip over all contiguous non-space characters to the right while( head_cursor_posget_next_char_with_metrics(tmp_text, byte_length, pixel_width)!=SIM_KEYCODE_SPACE ) { head_cursor_pos += byte_length; } // revert text pointer for further processing if( head_cursor_posget_next_char_with_metrics(tmp_text, byte_length, pixel_width)==SIM_KEYCODE_SPACE ) { head_cursor_pos += byte_length; } } else { head_cursor_pos = utf8_get_next_char(text, head_cursor_pos); } } // do not update tail cursor if SHIFT key is pressed -> enables text selection if( !IS_SHIFT_PRESSED(ev) ) { tail_cursor_pos = head_cursor_pos; } break; case SIM_KEYCODE_UP: // up arrow // not used currently break; case SIM_KEYCODE_HOME: // home head_cursor_pos = 0; // do not update tail cursor if SHIFT key is pressed -> enables text selection if( !IS_SHIFT_PRESSED(ev) ) { tail_cursor_pos = head_cursor_pos; } break; case SIM_KEYCODE_END: // end head_cursor_pos = len; // do not update tail cursor if SHIFT key is pressed -> enables text selection if( !IS_SHIFT_PRESSED(ev) ) { tail_cursor_pos = head_cursor_pos; } break; case SIM_KEYCODE_BACKSPACE: // backspace // check and remove any selected text first if( !remove_selection() && head_cursor_pos>0 ) { if ( head_cursor_pos 0) { text_dirty = true; next_update_call = notify_all_changes_delay == 0xFFFFu ? 0xFFFFFFFFul : dr_time() + notify_all_changes_delay; } } break; case SIM_KEYCODE_DELETE: // delete // check and remove any selected text first if( !remove_selection() && head_cursor_pos<=len ) { size_t next_pos = utf8_get_next_char(text, head_cursor_pos); for( size_t pos=head_cursor_pos; posev_code < 32) { // ignore special keys not handled so far break; } else if (ev->ev_code>=SIM_KEYCODE_NUMPAD_BASE && ev->ev_code<=SIM_KEYCODE_NUMPAD_BASE+9) { // ignore numpad keys if numlock is off // Could also return false here to move the map diagonally but this does not work for 2/4/6/8 // (SIM_KEYCODE_LEFT/_RIGHT moves the cursor already) break; } // insert letters, numbers, and special characters // first check if it is necessary to remove selected text portion if( remove_selection() ) { // recalculate text length after deleting selection len = strlen(text); } // test, if we have top convert letter char letter[16]; if(ev->ev_code>=128) { sprintf( letter, "CHR%X", ev->ev_code ); //DBG_MESSAGE( "gui_textinput_t::gui_textinput_t()","%i=%s",ev->ev_code,letter); const char *more_letter=translator::translate(letter); // could not convert ... if(letter==more_letter) { char *out=letter; out[ utf16_to_utf8(ev->ev_code, (utf8 *)out) ] = 0; } else { // successful converted letter strcpy( letter, more_letter ); } } else { letter[0] = ev->ev_code; letter[1] = 0; } const size_t num_letter = strlen(letter); if(len+num_letter>=max) { // too many chars ... break; } // insert into text? if (head_cursor_pos < len) { // copy the trailing '\0' too for( sint64 pos=len; pos>=(sint64)head_cursor_pos; pos-- ) { text[pos+num_letter] = text[pos]; } memcpy( text+head_cursor_pos, letter, num_letter ); } else { // append to text memcpy( text+len, letter, num_letter ); text[len+num_letter] = 0; } text_dirty = true; next_update_call = notify_all_changes_delay == 0xFFFFu ? 0xFFFFFFFFul : dr_time() + notify_all_changes_delay; tail_cursor_pos = ( head_cursor_pos += num_letter ); /* end default */ } } else { DBG_MESSAGE("gui_textinput_t::infowin_event", "called but text is NULL"); } cursor_reference_time = dr_time(); // update reference time for cursor blinking if (text_dirty && next_update_call < dr_time()) { // time to update content text_dirty = false; call_listeners((long)INPUT_CHANGED); } return true; } else if( ev->ev_class==EVENT_STRING ) { composition.clear(); // UTF8 multi-byte sequence if( text && ev->ev_ptr ) { size_t len = strlen(text); utf8 *in = (utf8 *)ev->ev_ptr; size_t in_pos = 0; // first check if it is necessary to remove selected text portion if( remove_selection() ) { // recalculate text length after deleting selection len = strlen(text); } while( *in ) { utf32 const uc = utf8_decoder_t::decode(in, in_pos); text_dirty = true; size_t num_letter = in_pos; char letter[16]; // test, if we have top convert letter sprintf( letter, "CHR%X", uc ); const char *more_letter = translator::translate(letter); // could not convert ... if( letter == more_letter ) { tstrncpy( letter, (const char *)in, in_pos+1 ); } else { // successful converted letter strcpy( letter, more_letter ); num_letter = strlen(more_letter); } in += in_pos; if( len+num_letter >= max ) { // too many chars ... break; } // insert into text? if( len>0 && head_cursor_pos < len ) { for( sint64 pos=len+num_letter; pos>=(sint64)head_cursor_pos; pos-- ) { text[pos] = text[pos-num_letter]; } memcpy( text+head_cursor_pos, letter, num_letter ); } else { // append to text memcpy( text+len, letter, num_letter ); text[len+num_letter] = 0; } text_dirty = true; tail_cursor_pos = ( head_cursor_pos += num_letter ); len += num_letter; } /* while still characters in string */ } if (text_dirty && notify_all_changes_delay ==0) { // time to update content text_dirty = false; call_listeners((long)INPUT_CHANGED); } else { text_dirty = true; next_update_call = notify_all_changes_delay == 0xFFFFu ? 0xFFFFFFFFul : dr_time() + notify_all_changes_delay; } return false; } else if( IS_LEFTCLICK(ev) ) { // since now the focus could be received while the mouse no there, we must release it scr_rect this_comp( get_size() ); if( !this_comp.contains(scr_coord(ev->click_pos.x,ev->click_pos.y) ) ) { // not us, just in old focus from previous selection or tab return false; } // acting on release causes unwanted recalculations of cursor position for long strings and (scroll_offset>0) // moreover, only (click) or (release) event happened inside textinput, the other one could lie outside // use mouse *click* position; update both head and tail cursors tail_cursor_pos = 0; if( text ) { tail_cursor_pos = head_cursor_pos = gfx->calc_text_index_for_width( text, ev->click_pos.x - 2 + scroll_offset ); } cursor_reference_time = dr_time(); // update reference time for cursor blinking return true; } else if( IS_LEFTDRAG(ev) ) { // since now the focus could be received while the mouse is not there, we must release it scr_rect this_comp( get_size() ); if( !this_comp.contains(scr_coord(ev->click_pos.x,ev->click_pos.y) ) ) { // not us, just in old focus from previous selection or tab return false; } // use mouse *move* position; update head cursor only in order to enable text selection head_cursor_pos = 0; if( text ) { head_cursor_pos = gfx->calc_text_index_for_width( text, ev->mouse_pos.x - 1 + scroll_offset ); } cursor_reference_time = dr_time(); // update reference time for cursor blinking return true; } else if( IS_LEFTDBLCLK(ev) ) { // since now the focus could be received while the mouse is not there, we must release it scr_rect this_comp( get_size() ); if( !this_comp.contains(scr_coord(ev->click_pos.x,ev->click_pos.y) ) ) { // not us, just in old focus from previous selection or tab return false; } // select a word as delimited by spaces // for tail cursor pos -> skip over all contiguous non-space characters to the left const char* tmp_text = text + tail_cursor_pos; uint8 byte_length; uint8 pixel_width; while( tail_cursor_pos>0 && gfx->get_prev_char_with_metrics(tmp_text, text, byte_length, pixel_width)!=SIM_KEYCODE_SPACE ) { tail_cursor_pos -= byte_length; } // for head cursor pos -> skip over all contiguous non-space characters to the right const size_t len = strlen(text); tmp_text = text + head_cursor_pos; while( head_cursor_posget_next_char_with_metrics(tmp_text, byte_length, pixel_width)!=SIM_KEYCODE_SPACE ) { head_cursor_pos += byte_length; } } else if( IS_LEFTTPLCLK(ev) ) { // since now the focus could be received while the mouse not there, we must release it scr_rect this_comp( get_size() ); if( !this_comp.contains(scr_coord(ev->click_pos.x,ev->click_pos.y) ) ) { // not us, just in old focus from previous selection or tab return false; } // select the whole text head_cursor_pos = strlen(text); tail_cursor_pos = 0; } else if( ev->ev_class==INFOWIN && ev->ev_code==WIN_UNTOP ) { if( text_dirty ) { text_dirty = false; call_listeners((long)INPUT_UNTOP); } return true; } else if( ev->ev_class == INFOWIN && ev->ev_code == WIN_CLOSE && focus_received ) { // release focus on close and close keyboard dr_stop_textinput(); if (text_dirty) { // note all pending changes text_dirty = false; call_listeners((long)INPUT_UNTOP); } focus_received = false; } if (text_dirty && next_update_call < dr_time()) { // time to update content text_dirty = false; call_listeners((long)INPUT_CHANGED); } return false; } /** * Draw the component */ void gui_textinput_t::draw(scr_coord offset) { display_with_focus( offset, (win_get_focus()==this) ); if (text_dirty && dr_time() > next_update_call) { // need to trigger a dummy event for next processing event_t* ev = new event_t(EVENT_KEYDOWN); ev->ev_code = 0; ev->ev_key_mod = 0; queue_event(ev); } } /** * Detect change of focus state and determine whether cursor should be displayed, * and call the function that performs the actual display */ void gui_textinput_t::display_with_focus(scr_coord offset, bool has_focus) { // check if focus state has changed if( focus_received!=has_focus ) { if( has_focus ) { // update reference time for cursor blinking if focus has just been received cursor_reference_time = dr_time(); dr_start_textinput(); const scr_coord gui_xy = win_get_pos( win_get_top() ); const scr_coord_val x = pos.x + gui_xy.x + get_current_cursor_x(); const scr_coord_val y = pos.x + gui_xy.y + D_TITLEBAR_HEIGHT; dr_notify_input_pos({ x, y }); } else { dr_stop_textinput(); } focus_received = has_focus; } display_with_cursor( offset, has_focus, (has_focus && ((dr_time()-cursor_reference_time)&512ul)==0) ); } void gui_textinput_t::display_with_cursor(scr_coord offset, bool cursor_active, bool cursor_visible) { if (!enabled) { gfx->draw_stretch_map_blend(gui_theme_t::editfield, scr_rect(pos + offset, size), TRANSPARENT50_FLAG|SYSCOL_EDIT_TEXT_DISABLED); } else { gfx->draw_stretch_map(gui_theme_t::editfield, scr_rect(pos + offset, size)); } if (head_cursor_pos == 0xFFFF) { if (text) { // since before the first draw, the text was likely not set correctly head_cursor_pos = strlen(text); } else { head_cursor_pos = 0; } } const char* display_text = (text && text[0]) ? text : placeholder_text; PIXVAL display_color = display_text == placeholder_text ? SYSCOL_EDIT_TEXT_DISABLED : (enabled ? textcol : SYSCOL_EDIT_TEXT_DISABLED); if (display_text) { // recalculate scroll offset const int text_width = gfx->calc_text_width(display_text); const scr_coord_val view_width = size.w - 3; const int cursor_offset = cursor_active ? gfx->calc_text_width_n(display_text, head_cursor_pos) : 0; if (text_width <= view_width) { // case : text is shorter than displayable width of the text input // -> the only case where left and right alignments differ // -> pad empty spaces on the left if right-aligned scroll_offset = align==ALIGN_RIGHT ? text_width - view_width : 0; } else { if( scroll_offset<0 ) { // case : if text is longer than displayable width of the text input // but there is empty space to the left // -> the text should move leftwards to fill up the empty space scroll_offset = 0; } if( cursor_offset-scroll_offset>view_width ) { // case : cursor has moved too far off the right side scroll_offset = cursor_offset - view_width; } else if( cursor_offsettext_width ) { // case : if text is longer than displayable width of the text input // but there is empty space to the right // -> the text should move rightwards to fill up the empty space scroll_offset = text_width - view_width; } } // set clipping to be within textinput button const int text_clip_x = pos.x + offset.x + 1; const int text_clip_w = size.w - 2; const int text_clip_y = pos.y + offset.y + 1; const int text_clip_h = size.h - 2; PUSH_CLIP_FIT(text_clip_x, text_clip_y, text_clip_w, text_clip_h); const int x_base_offset = pos.x+offset.x+2-scroll_offset; const int y_offset = pos.y+offset.y+D_GET_CENTER_ALIGN_OFFSET(LINESPACE,size.h); // display text (before composition) gfx->draw_text_clipped_n(x_base_offset, y_offset, display_text, ALIGN_LEFT | DT_CLIP, display_color, true, head_cursor_pos CLIP_NUM_DEFAULT); int x_offset = gfx->calc_text_width_n(text, head_cursor_pos); // IME text to display? if (composition.len()) { // assert(head_cursor_pos==tail_cursor_pos); gfx->draw_text_clipped(x_base_offset + x_offset, y_offset, composition.get_str(), ALIGN_LEFT | DT_CLIP, display_color, true); // draw underline int composition_width = gfx->calc_text_width(composition.get_str()); gfx->draw_line(x_base_offset + x_offset, y_offset + LINESPACE, x_base_offset + x_offset + composition_width, y_offset + LINESPACE - 1, display_color); // mark targeted part in a similar manner to selected text int start_offset = gfx->calc_text_width_n(composition.get_str(), composition_target_start); int highlight_width = gfx->calc_text_width_n(composition.get_str()+composition_target_start, composition_target_length); gfx->draw_rect_clipped(x_base_offset+x_offset+start_offset, y_offset, highlight_width, LINESPACE, SYSCOL_EDIT_BACKGROUND_SELECTED, true CLIP_NUM_DEFAULT); gfx->draw_text_clipped_n(x_base_offset+x_offset+start_offset, y_offset, composition.get_str()+composition_target_start, ALIGN_LEFT|DT_CLIP, SYSCOL_EDIT_TEXT_SELECTED, false, composition_target_length CLIP_NUM_DEFAULT); x_offset += composition_width; } // display text (after composition) gfx->draw_text_clipped(x_base_offset + x_offset, y_offset, display_text + head_cursor_pos, ALIGN_LEFT | DT_CLIP, display_color, true); if (cursor_active && enabled) { // display selected text block with light grey text on charcoal bounding box if (head_cursor_pos != tail_cursor_pos) { const size_t start_pos = min(head_cursor_pos, tail_cursor_pos); size_t end_pos = ::max(head_cursor_pos, tail_cursor_pos); const scr_coord_val start_offset = gfx->calc_text_width_n(display_text, start_pos); const scr_coord_val highlight_width = gfx->calc_text_width_n(display_text + start_pos, end_pos - start_pos); gfx->draw_rect_clipped(x_base_offset + start_offset, y_offset, highlight_width, LINESPACE, SYSCOL_EDIT_BACKGROUND_SELECTED, true CLIP_NUM_DEFAULT); gfx->draw_text_clipped_n(x_base_offset + start_offset, y_offset, display_text + start_pos, ALIGN_LEFT | DT_CLIP, SYSCOL_EDIT_TEXT_SELECTED, false, end_pos - start_pos CLIP_NUM_DEFAULT); } // display blinking cursor if( cursor_visible ) { gfx->draw_rect_clipped(x_base_offset+cursor_offset-1, y_offset, 1, LINESPACE, SYSCOL_CURSOR_BEAM, true CLIP_NUM_DEFAULT); } } // reset clipping POP_CLIP(); } } void gui_textinput_t::set_text(char *t, size_t max) { char *old_text = text; this->text = t; this->max = max; if (old_text && tail_cursor_pos == head_cursor_pos) { // if same, keep positions } else { // whole text is selected by default head_cursor_pos = 0xFFFF; tail_cursor_pos = 0; } text_dirty = false; } // needed to set the cursor on the right position bool gui_hidden_textinput_t::infowin_event(const event_t *ev) { if( IS_LEFTRELEASE(ev) ) { // since now the focus could be received while the mouse no there, we must release it scr_rect this_comp( get_size() ); if( !this_comp.contains(scr_coord(ev->click_pos.x,ev->click_pos.y) ) ) { // not us, just in old focus from previous selection or tab return false; } // acting on release causes unwanted recalculations of cursor position for long strings and (cursor_offset>0) // moreover, only (click) or (release) event happened inside textinput, the other one could lie outside sint16 asterix_width = gfx->calc_text_width_n("*", 1); head_cursor_pos = 0; if ( text ) { head_cursor_pos = min( strlen(text), ev->click_pos.x/asterix_width ); } cursor_reference_time = dr_time(); // update reference time for cursor blinking return true; } else { return gui_textinput_t::infowin_event( ev ); } } void gui_hidden_textinput_t::display_with_cursor(scr_coord const offset, bool, bool const cursor_visible) { gfx->draw_stretch_map( gui_theme_t::editfield, scr_rect( pos+offset, size ) ); if( text ) { // the text will be all asterisk, thus we draw them letter by letter // set clipping to be within textinput button const clip_dimension old_clip = gfx->get_clip_rect(CLIP_NUM_DEFAULT_VALUE); int text_clip_x = pos.x+offset.x + 1, text_clip_w = size.w - 2; // something to draw? if (text_clip_x >= old_clip.xx || text_clip_x+text_clip_w <= old_clip.x || text_clip_w<=0) { return; } const int clip_x = old_clip.x > text_clip_x ? old_clip.x : text_clip_x; gfx->set_clip_rect( clip_x, old_clip.y, min(old_clip.xx, text_clip_x+text_clip_w)-clip_x, old_clip.h CLIP_NUM_DEFAULT, false); utf8 const *text_pos = (utf8 const*)text; utf8 const *end = (utf8 const*)text + max; sint16 xpos = pos.x+offset.x+2; utf32 c = 0; do { // cursor? if( cursor_visible && text_pos == (utf8 const*)text + head_cursor_pos ) { gfx->draw_rect_clipped( xpos, pos.y+offset.y+1+(size.h-LINESPACE)/2, 1, LINESPACE, SYSCOL_CURSOR_BEAM, true CLIP_NUM_DEFAULT); } c = utf8_decoder_t::decode((utf8 const *&)text_pos); if(c) { xpos += gfx->draw_text_clipped( xpos, pos.y+offset.y+1+(size.h-LINESPACE)/2, "*", ALIGN_LEFT | DT_CLIP, textcol, true); } } while( text_pos < end && c != UNICODE_NUL ); // reset clipping gfx->set_clip_rect(old_clip.x, old_clip.y, old_clip.w, old_clip.h CLIP_NUM_DEFAULT, false); } } simutrans-124.5/src/simutrans/gui/components/gui_textinput.h000066400000000000000000000101011520675460500244230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_TEXTINPUT_H #define GUI_COMPONENTS_GUI_TEXTINPUT_H #include "gui_action_creator.h" #include "gui_component.h" #include "../../simcolor.h" #include "../../display/simgraph.h" #include "../../utils/cbuffer.h" /** * A simple text input field. It has no Text Buffer, * only a pointer to a buffer created by someone else. */ class gui_textinput_t : public gui_action_creator_t, public gui_component_t { protected: /** * The string buffer */ char *text; /** * Placeholder text to be displayed when no text has been typed * if NULL, nothing will be shown */ const char *placeholder_text; // text, which has not yet inputted (i.e. by an IME) cbuffer_t composition; size_t composition_target_start; size_t composition_target_length; /** * Maximum length of the string buffer */ size_t max; /** * position of head cursor to the text * represents front end of the selected text portion */ size_t head_cursor_pos; /** * position of tail cursor to the text * represent rear end of the selected text portion */ size_t tail_cursor_pos; /** * offset for controlling horizontal text scroll */ scr_coord_val scroll_offset; /** * text alignment */ uint8 align; PIXVAL textcol; // true if there were changed but no notification was sent yet bool text_dirty : 1; /** * whether focus has been received */ bool focus_received : 1; // otherwise just draw empty frame bool enabled : 1; /** * whether focus has been received */ uint16 notify_all_changes_delay; uint32 next_update_call; /** * reference time for regulating cursor blinking */ uint32 cursor_reference_time; /** * determine new cursor position from event coordinates */ size_t calc_cursor_pos(const int x); /** * Remove selected text portion, if any. * Returns true if some selected text is actually deleted. */ bool remove_selection(); public: // three messages for the calling enum { INPUT_UNTOP = 0, INPUT_FINISHED, INPUT_CHANGED }; gui_textinput_t(); // update changes with delay (in ms), 0=immeadiately 0xFFFF=never void set_notify_all_changes_delay(uint16 _n) { notify_all_changes_delay = _n; } /** * Sets the Text buffer */ void set_text(char *text, size_t max); void set_placeholder_text(const char *ptext) { placeholder_text = ptext; }; // text which is not yet inputed (i.e. for east asian text), assuming either native or utf8 encoding void set_composition_status( char *composition, int target_start, int target_length ); /** * Return the Text buffer */ char *get_text() const { return text; } const char *get_composition() const { return composition.get_str(); } bool infowin_event(event_t const*) OVERRIDE; /** * Draw the component */ void draw(scr_coord offset) OVERRIDE; // x position of the current cursor (for IME purposes) scr_coord_val get_current_cursor_x() { return calc_cursor_pos(head_cursor_pos); } // set currsen selection (not checks!) void set_cursor(sint32 h, sint32 t) { head_cursor_pos = h; tail_cursor_pos = t; } /** * Detect change of focus state and determine whether cursor should be displayed, * and call the function that performs the actual display */ void display_with_focus(scr_coord offset, bool has_focus); // function that performs the actual display virtual void display_with_cursor(scr_coord offset, bool cursor_active, bool cursor_visible); // to allow for right-aligned text void set_alignment(uint8 _align){ align = _align;} // to set text color void set_color(PIXVAL col){ textcol = col;} scr_size get_max_size() const OVERRIDE; scr_size get_min_size() const OVERRIDE; void set_enabled(bool e) { enabled = e; } }; class gui_hidden_textinput_t : public gui_textinput_t { // and set the cursor right when clicking with the mouse bool infowin_event(event_t const*) OVERRIDE; // function that performs the actual display; just draw with stars ... void display_with_cursor(scr_coord offset, bool cursor_active, bool cursor_visible) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/components/gui_timeinput.cc000066400000000000000000000044661520675460500245540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_timeinput.h" #include "../simwin.h" #include "../../display/simgraph.h" #include "../../macros.h" #include "../../dataobj/translator.h" #include "../../simevent.h" #include "../../dataobj/environment.h" gui_timeinput_t::gui_timeinput_t(const char *) { bool has_days = env_t::show_month > env_t::DATE_FMT_MONTH; set_table_layout(3+has_days, 1); set_alignment(ALIGN_LEFT | ALIGN_TOP); set_margin(scr_size(D_H_SPACE, 0), scr_size(D_H_SPACE, 0)); days.set_visible(has_days); days.init(0, 0, 31); days.add_listener(this); days.allow_tooltip(false); add_component(&days); hours.init(0, 0, 23); hours.add_listener(this); hours.allow_tooltip(false); add_component(&hours); new_component(":"); minutes.init(0, 0, 59); minutes.add_listener(this); minutes.allow_tooltip(false); add_component(&minutes); b_enabled = true; } sint32 gui_timeinput_t::get_ticks() { sint32 dms = (days.get_value()-b_absolute) * 24 * 60 + hours.get_value() * 60 + minutes.get_value(); if (dms == 0) { return 0; } if (env_t::show_month <= env_t::DATE_FMT_MONTH) { return (dms * 65536u) / (24 * 60)+1; } return (dms * 65536u) / (31 * 24 * 60)+1; } void gui_timeinput_t::set_ticks(uint16 t,bool absolute) { sint32 ticks = t; b_absolute = absolute; days.set_limits( 0+absolute, 30+absolute ); // this is actually ticks*daylength*24*60/65536 but to avoid overflow the factor 32 was removed from both) sint32 new_dms = (ticks * (env_t::show_month == env_t::DATE_FMT_MONTH ? 1 : 31) * 3 * 15) / (2048); days.set_value(new_dms / (24 * 60)+absolute); hours.set_value((new_dms / 60) % 24); minutes.set_value(new_dms % 60); } bool gui_timeinput_t::action_triggered(gui_action_creator_t*, value_t) { uint16 t = get_ticks(); call_listeners(t); return false; } void gui_timeinput_t::draw(scr_coord offset) { gui_aligned_container_t::draw(offset); if(b_enabled && getroffen( get_mouse_pos() - offset )) { win_set_tooltip(get_mouse_pos() + TOOLTIP_MOUSE_OFFSET, translator::translate("Enter intervall in days, hours, minutes" ), this); } } void gui_timeinput_t::rdwr( loadsave_t *file ) { uint16 ticks=get_ticks(); file->rdwr_short( ticks ); file->rdwr_bool( b_absolute ); set_ticks(ticks,b_absolute); } simutrans-124.5/src/simutrans/gui/components/gui_timeinput.h000066400000000000000000000020421520675460500244020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_TIMEINPUT_H #define GUI_COMPONENTS_GUI_TIMEINPUT_H #include "../../simtypes.h" #include "../../display/scr_coord.h" #include "gui_action_creator.h" #include "gui_label.h" #include "gui_button.h" #include "gui_aligned_container.h" #include "gui_numberinput.h" /** * An input field for game time (internally as ticks) * The actual input is 0...65535, which is minute precision and then shifted by bit_shift_per_month-16 */ class gui_timeinput_t : public gui_action_creator_t, public gui_aligned_container_t, public action_listener_t { private: gui_numberinput_t days, hours, minutes; bool b_enabled, b_absolute; public: gui_timeinput_t(const char *null_text); sint32 get_ticks(); void set_ticks(uint16 t,bool absolute); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void rdwr( loadsave_t *file ); void draw(scr_coord offset) OVERRIDE; void enable(bool b) { b_enabled = b; } }; #endif simutrans-124.5/src/simutrans/gui/components/gui_waytype_tab_panel.cc000066400000000000000000000063321520675460500262370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_waytype_tab_panel.h" #include "../../simskin.h" #include "../../simhalt.h" #include "../../builder/vehikelbauer.h" #include "../../obj/way/kanal.h" #include "../../obj/way/maglev.h" #include "../../obj/way/monorail.h" #include "../../obj/way/narrowgauge.h" #include "../../obj/way/runway.h" #include "../../obj/way/schiene.h" #include "../../obj/way/strasse.h" #include "../../dataobj/translator.h" #include "../../descriptor/skin_desc.h" void gui_waytype_tab_panel_t::init_tabs(gui_component_t* c) { uint8 max_idx = 0; add_tab(c, translator::translate("All")); tabs_to_waytype[max_idx++] = ignore_wt; // now add all specific tabs if (maglev_t::default_maglev) { add_tab(c, translator::translate("Maglev"), skinverwaltung_t::maglevhaltsymbol, translator::translate("Maglev")); tabs_to_waytype[max_idx++] = maglev_wt; } if (monorail_t::default_monorail) { add_tab(c, translator::translate("Monorail"), skinverwaltung_t::monorailhaltsymbol, translator::translate("Monorail")); tabs_to_waytype[max_idx++] = monorail_wt; } if (schiene_t::default_schiene) { add_tab(c, translator::translate("Train"), skinverwaltung_t::zughaltsymbol, translator::translate("Train")); tabs_to_waytype[max_idx++] = track_wt; } if (narrowgauge_t::default_narrowgauge) { add_tab(c, translator::translate("Narrowgauge"), skinverwaltung_t::narrowgaugehaltsymbol, translator::translate("Narrowgauge")); tabs_to_waytype[max_idx++] = narrowgauge_wt; } if (!vehicle_builder_t::get_info(tram_wt).empty()) { add_tab(c, translator::translate("Tram"), skinverwaltung_t::tramhaltsymbol, translator::translate("Tram")); tabs_to_waytype[max_idx++] = tram_wt; } if (strasse_t::default_strasse) { add_tab(c, translator::translate("Truck"), skinverwaltung_t::autohaltsymbol, translator::translate("Truck")); tabs_to_waytype[max_idx++] = road_wt; } if (!vehicle_builder_t::get_info(water_wt).empty()) { add_tab(c, translator::translate("Ship"), skinverwaltung_t::schiffshaltsymbol, translator::translate("Ship")); tabs_to_waytype[max_idx++] = water_wt; } if (runway_t::default_runway) { add_tab(c, translator::translate("Aircraft"), skinverwaltung_t::airhaltsymbol, translator::translate("Aircraft")); tabs_to_waytype[max_idx++] = air_wt; } } void gui_waytype_tab_panel_t::set_active_tab_waytype(waytype_t wt) { for(uint32 i=0; i < get_count(); i++ ) { if (wt == tabs_to_waytype[i]) { set_active_tab_index(i); return; } } // assume invalid type set_active_tab_index(0); } haltestelle_t::stationtyp gui_waytype_tab_panel_t::get_active_tab_stationtype() const { switch(get_active_tab_waytype()) { case air_wt: return haltestelle_t::airstop; case road_wt: return haltestelle_t::loadingbay | haltestelle_t::busstop; case track_wt: return haltestelle_t::railstation; case water_wt: return haltestelle_t::dock; case monorail_wt: return haltestelle_t::monorailstop; case maglev_wt: return haltestelle_t::maglevstop; case tram_wt: return haltestelle_t::tramstop; case narrowgauge_wt: return haltestelle_t::narrowgaugestop; default: return haltestelle_t::invalid; } } simutrans-124.5/src/simutrans/gui/components/gui_waytype_tab_panel.h000066400000000000000000000016671520675460500261070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_WAYTYPE_TAB_PANEL_H #define GUI_COMPONENTS_GUI_WAYTYPE_TAB_PANEL_H #include "../../simtypes.h" #include "../../simhalt.h" #include "gui_tab_panel.h" // panel that show the available waytypes class gui_waytype_tab_panel_t : public gui_tab_panel_t { private: // since waytypes may change during timeline waytype_t tabs_to_waytype[9]; public: gui_waytype_tab_panel_t() { gui_tab_panel_t(); } // all tabs habe the same gui_component with this! void init_tabs(gui_component_t *c); waytype_t get_active_tab_waytype() const { return tabs_to_waytype[get_active_tab_index()]; } void set_active_tab_waytype(waytype_t wt); haltestelle_t::stationtyp get_active_tab_stationtype() const; waytype_t get_tab_waytype(int i) const { return 0<=i && (uint32)i #include "gui_world_view.h" #include "../../world/simworld.h" #include "../../display/viewport.h" #include "../../obj/simobj.h" #include "../../obj/zeiger.h" #include "../../display/simgraph.h" #include "../../simcolor.h" #include "../../ground/grund.h" #include "../../dataobj/environment.h" #include "../../dataobj/koord3d.h" #include "../../vehicle/air_vehicle.h" vector_tpl world_view_t::view_list; void world_view_t::invalidate_all() { world_view_t *const *const endpointer = world_view_t::view_list.end(); for (world_view_t *const *pointer = world_view_t::view_list.begin() ; pointer != endpointer ; pointer++) { (*pointer)->prepared_rect.discard_area(); } } world_view_t::world_view_t(scr_size size ) : prepared_rect(), display_rect(), raster(gfx->get_base_tile_raster_width()) { set_size( size ); min_size = size; world_view_t::view_list.append(this); } world_view_t::world_view_t() : prepared_rect(), display_rect(), raster(gfx->get_base_tile_raster_width()) { world_view_t::view_list.append(this); } world_view_t::~world_view_t() { world_view_t::view_list.remove(this); } /** * Events are notified to GUI components via this method */ bool world_view_t::infowin_event(const event_t* ev) { if(IS_LEFTRELEASE(ev)) { koord3d const& pos = get_location(); if (welt->is_within_limits(pos.get_2d())) { welt->get_viewport()->change_world_position(pos); welt->get_zeiger()->change_pos( pos ); } return true; } return false; } void world_view_t::internal_draw(const scr_coord offset, obj_t const* const obj) { gfx->set_image_procs(false); const koord3d here3d = get_location(); const koord here = here3d.get_2d(); scr_coord fine_here = scr_coord(0, 0); sint16 y_offset = 0; if(obj) { // offsets? fine_here = scr_coord(tile_raster_scale_x(-obj->get_xoff(), raster), tile_raster_scale_y(-obj->get_yoff() % (OBJECT_OFFSET_STEPS * 2), raster)); y_offset = obj->get_yoff() / (OBJECT_OFFSET_STEPS * 2); if(vehicle_base_t const* const v = obj_cast(obj)) { int x = 0; int y = 0; v->get_screen_offset(x, y, raster); fine_here -= scr_coord(x, y); } } koord const height_offset(y_offset, y_offset); grund_t const* const kb = welt->lookup_kartenboden(here); if(!kb) { return; } int hgt = tile_raster_scale_y(here3d.z * TILE_HEIGHT_STEP, raster); if(obj) { air_vehicle_t const* const plane = obj_cast(obj); if(plane) { hgt += tile_raster_scale_y(plane->get_flyingheight(), raster); } } const scr_coord pos = get_pos() + offset + scr_coord(1, 1); // do not draw outside (may happen with scroll bars) const clip_dimension old_clip = gfx->get_clip_rect(CLIP_NUM_DEFAULT_VALUE); // something to draw? const scr_size size = get_size() - scr_size(2, 2); if( old_clip.xx <= pos.x || pos.x + size.w <= old_clip.x || size.w <= 0 ) { return; } if( old_clip.yy <= pos.y || pos.y + size.h <= old_clip.y || size.h <= 0 ) { return; } // prepare view rect_t const world_rect(koord(0, 0), welt->get_size()); rect_t view_rect = display_rect; view_rect.origin+= here + height_offset; view_rect.mask(world_rect); if (view_rect != prepared_rect) { welt->prepare_tiles(view_rect, prepared_rect); prepared_rect = view_rect; } // prepare clip area const int clip_x = max(old_clip.x, pos.x); const int clip_y = max(old_clip.y, pos.y); gfx->set_clip_rect(clip_x, clip_y, min(old_clip.xx, pos.x + size.w) - clip_x, min(old_clip.yy, pos.y + size.h) - clip_y CLIP_NUM_DEFAULT, false); gfx->mark_rect_dirty_wc(pos.x, pos.y, pos.x + size.w, pos.y + size.h); /* Not very elegant, but works: Fill everything with black for underground * mode. */ if( grund_t::underground_mode ) { gfx->draw_rect_clipped(pos.x, pos.y, size.w, size.h, gfx->palette_lookup(COL_BLACK), true CLIP_NUM_DEFAULT); } else { gfx->draw_rect_clipped(pos.x, pos.y, size.w, size.h, env_t::background_color, true CLIP_NUM_DEFAULT); } const sint16 yoff = obj && obj->is_moving() ? size.h / 2 - raster * 3 / 4 : // align 1/4 raster from the bottom of the image size.h - raster; // align the bottom of the image const scr_coord display_off = scr_coord((size.w - raster) / 2, hgt + yoff) + fine_here; // display grounds for(koord const& off : offsets) { const koord k = here + off + height_offset; const sint16 off_x = (off.x - off.y) * 32 * raster / 64 + display_off.x; if( off_x + raster < 0 || size.w < off_x || k.x < 0 || k.y < 0 ) { continue; } grund_t *kb = welt->lookup_kartenboden(k); if( !kb ) { continue; } const sint16 yypos = display_off.y + (off.y + off.x) * 16 * raster / 64 - tile_raster_scale_y(kb->get_disp_height() * TILE_HEIGHT_STEP, raster); if( size.h < yypos ) { break; // enough with grounds } else if( 0 <= yypos + raster ) { #ifdef MULTI_THREAD kb->display_if_visible( pos.x + off_x, pos.y + yypos, raster, 0 ); #else kb->display_if_visible( pos.x + off_x, pos.y + yypos, raster ); #endif } } // display things for(koord const& off : offsets) { const koord k = here + off + height_offset; const sint16 off_x = (off.x - off.y) * 32 * raster / 64 + display_off.x; if( off_x + raster < 0 || size.w < off_x || k.x < 0 || k.y < 0 ) { continue; } const planquadrat_t * const plan = welt->access(k); if(!plan) { continue; } const grund_t * const kb = plan->get_kartenboden(); if(!kb) { continue; } const sint8 h = kb->get_hoehe(); // minimum height: ground height for overground, // for the definition of underground_level see grund_t::set_underground_mode const sint8 hmin = min(h, grund_t::underground_level); // maximum height: 127 for overground, underground level for sliced, ground height-1 for complete underground view const sint8 hmax = grund_t::underground_mode == grund_t::ugm_all ? h - !kb->ist_tunnel() : grund_t::underground_level; const sint16 yypos = display_off.y + (off.y + off.x) * 16 * raster / 64 - tile_raster_scale_y(kb->get_disp_height() * TILE_HEIGHT_STEP, raster); if( 0 <= yypos + raster && yypos - raster * 2 < size.h ) { #ifdef MULTI_THREAD plan->display_obj( pos.x + off_x, pos.y + yypos, raster, false, hmin, hmax, 0 ); #else plan->display_obj( pos.x + off_x, pos.y + yypos, raster, false, hmin, hmax ); #endif } else if( yypos > size.h ) { break; // now we can finish } } // this should only happen for airplanes: out of image, so we need to extra display them if( y_offset != 0 ) { const grund_t * const g = welt->lookup(obj->get_pos()); const sint16 yypos = display_off.y - tile_raster_scale_y(2 * y_offset * 16, raster) - tile_raster_scale_y(g->get_disp_height() * TILE_HEIGHT_STEP, raster); #ifdef MULTI_THREAD g->display_obj_all( pos.x + display_off.x, pos.y + yypos, raster, false, 0 ); #else g->display_obj_all( pos.x + display_off.x, pos.y + yypos, raster, false ); #endif } gfx->set_clip_rect(old_clip.x, old_clip.y, old_clip.w, old_clip.h CLIP_NUM_DEFAULT, false); gfx->draw_box3d_clipped(pos.x - 1, pos.y - 1, size.w + 2, size.h + 2, gfx->palette_lookup(MN_GREY0), gfx->palette_lookup(MN_GREY4)); } /** * Resize the contents of the window */ void world_view_t::set_size(scr_size size) { gui_component_t::set_size(size); calc_offsets(size, 5); } /** * Recalculates the number of tiles needed */ void world_view_t::calc_offsets(scr_size size, sint16 dy_off) { const sint16 max_dx = size.w/(raster/2) + 2; const sint16 max_dy = (size.h/(raster/2) + dy_off)&0x0FFE; // build offset list offsets.clear(); for( sint16 dy = -max_dy; dy <= 5; ) { { for( sint16 dx =- 2; dx < max_dx; dx += 2 ) { const koord check( (dy + dx) / 2, (dy - dx) / 2); offsets.append(check); } } dy++; for( sint16 dx = -1; dx < max_dx; dx += 2 ) { const koord check( (dy + dx) / 2, (dy - dx) / 2); offsets.append(check); } dy++; } // determine preparation extents koord offset_extent_min(0, 0); koord offset_extent_max(0, 0); koord const *const iter_end = offsets.end(); for (koord const *iter = offsets.begin() ; iter != iter_end ; iter++) { sint16 const x = iter->x; sint16 const y = iter->y; if (x < offset_extent_min.x) { offset_extent_min.x = x; }else if (x > offset_extent_max.x) { offset_extent_max.x = x; } if (y < offset_extent_min.y) { offset_extent_min.y = y; }else if (y > offset_extent_max.y) { offset_extent_max.y = y; } } display_rect = rect_t(offset_extent_min, offset_extent_max - offset_extent_min + koord(1, 1)); } simutrans-124.5/src/simutrans/gui/components/gui_world_view.h000066400000000000000000000044761520675460500245620ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_COMPONENTS_GUI_WORLD_VIEW_H #define GUI_COMPONENTS_GUI_WORLD_VIEW_H #include "gui_component.h" #include "../../dataobj/koord3d.h" #include "../../tpl/vector_tpl.h" #include "../../dataobj/rect.h" class obj_t; /** * Displays a little piece of the world */ class world_view_t : public gui_world_component_t { private: /** * @brief Contains a reference to every world_view_t object. * * Used to allow mass invalidating of all prepared area for cases such as * changing underground mode and snow levels. */ static vector_tpl view_list; /** * @brief The prepared area of the view port. * * The area that has already been prepared for this view port. When the view * port is moved then only the new area not already prepared will be * prepared. If the view port is stationary no area will be prepared. */ rect_t prepared_rect; /** * @brief The display area centered around the map origin. * * The area used by this view to display the world. It is centered around * the origin but can easilly be shifted to where the actual view is * focused. */ rect_t display_rect; vector_tpl offsets; /**< Offsets are stored. */ sint16 raster; /**< For this rastersize. */ scr_size min_size; ///< set by constructor protected: virtual koord3d get_location() = 0; void internal_draw(scr_coord offset, obj_t const *); void calc_offsets(scr_size size, sint16 dy_off); public: /** * @brief Clears all prepared area. * * Set prepared rect to have no area. Used when all views must be reprepared * such as changing underground mode or slice. This method is required * because object views use completly separate draw logic from the main map * view and must also have their prepared area invalidated for correct * graphic reproduction. */ static void invalidate_all(); world_view_t(scr_size size); world_view_t(); ~world_view_t(); bool infowin_event(event_t const*) OVERRIDE; /** * resize window in response to a resize event * need to recalculate the list of offsets */ void set_size(scr_size size) OVERRIDE; scr_size get_min_size() const OVERRIDE { return min_size; } scr_size get_max_size() const OVERRIDE { return min_size; } }; #endif simutrans-124.5/src/simutrans/gui/convoi_detail.cc000066400000000000000000000152761520675460500223250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "convoi_detail.h" #include "components/gui_divider.h" #include "components/gui_image.h" #include "components/gui_textarea.h" #include "../simconvoi.h" #include "../vehicle/vehicle.h" #include "../simcolor.h" #include "../world/simworld.h" #include "../simware.h" #include "../simintr.h" #include "../dataobj/translator.h" #include "../dataobj/loadsave.h" #include "../player/simplay.h" #include "../utils/simstring.h" #include "../utils/cbuffer.h" karte_ptr_t convoi_detail_t::welt; class gui_vehicleinfo_t : public gui_aligned_container_t { vehicle_t *v; cbuffer_t freight_info; gui_label_buf_t label_resale, label_friction, label_freight_summary; gui_textarea_t freight; public: gui_vehicleinfo_t(vehicle_t *v, sint32 cnv_kmh) : freight(&freight_info) { this->v = v; set_table_layout(2,0); set_alignment(ALIGN_TOP | ALIGN_LEFT); const int month_now = world()->get_timeline_year_month(); // image new_component(v->get_loaded_image(), v->get_owner_nr())->enable_offset_removal(true); add_table(1,0); { // name new_component( v->get_desc()->get_name(), world()->use_timeline() && v->get_desc()->is_retired(month_now) ? SYSCOL_OBSOLETE : SYSCOL_TEXT); // age gui_label_buf_t* l = new_component(); const sint32 month = v->get_purchase_time(); l->buf().printf("%s %s %i", translator::translate("Manufactured:"), translator::get_month_name(month%12), month/12 ); l->update(); // value add_component(&label_resale); // max income sint64 max_income = - v->get_operating_cost(); // cnv_kmh == SPEED_UNLIMITED means that meaningful revenue // cannot be calculated yet (e.g. vehicle in depot or stopped at station) if(v->get_cargo_max() > 0 && cnv_kmh != SPEED_UNLIMITED) { max_income += (v->get_cargo_max() * ware_t::calc_revenue(v->get_cargo_type(), v->get_waytype(), cnv_kmh))/3000; } add_table(2,1); { new_component("Max income:"); l = new_component(); l->buf().append_money(max_income/100.0); l->update(); } end_table(); // loading time if (v->get_desc()->get_capacity() > 0) { l = new_component(); l->buf().printf("%s%s", translator::translate("Loading time:"), difftick_to_string(v->get_desc()->get_loading_time(),false) ); l->update(); } // power if(v->get_desc()->get_power()>0) { l = new_component(); l->buf().printf("%s %i kW, %s %.2f", translator::translate("Power:"), v->get_desc()->get_power(), translator::translate("Gear:"), v->get_desc()->get_gear()/64.0 ); l->update(); } // friction add_component(&label_friction); if(v->get_cargo_max() > 0) { add_component( &label_freight_summary ); add_component(&freight); } } end_table(); update_labels(); } void update_labels() { label_resale.buf().printf("%s ", translator::translate("Restwert:")); label_resale.buf().append_money(v->calc_sale_value() / 100.0); if( sint64 fix_cost = world()->scale_with_month_length((sint64)v->get_desc()->get_maintenance()) ) { cbuffer_t temp_buf; temp_buf.printf( translator::translate("(%.2f$/km %.2f$/m)"), (double)v->get_desc()->get_running_cost()/100.0, (double)fix_cost/100.0 ); label_resale.buf().append( temp_buf ); } else { cbuffer_t temp_buf; temp_buf.printf( translator::translate("(%.2f$/km)"), (double)v->get_desc()->get_running_cost()/100.0 ); label_resale.buf().append( temp_buf ); } label_resale.update(); label_friction.buf().printf( "%s %i", translator::translate("Friction:"), v->get_frictionfactor() ); label_friction.update(); if(v->get_cargo_max() > 0) { // freight type goods_desc_t const& g = *v->get_cargo_type(); char const* const name = translator::translate( g.get_catg() == 0?g.get_name():g.get_catg_name() ); label_freight_summary.buf().printf( "%u/%u%s %s", v->get_total_cargo(), v->get_cargo_max(), translator::translate( v->get_cargo_mass() ), name ); label_freight_summary.update(); freight_info.clear(); v->get_cargo_info(freight_info); } } void draw(scr_coord offset) OVERRIDE { update_labels(); gui_aligned_container_t::draw(offset); } }; convoi_detail_t::convoi_detail_t(convoihandle_t cnv) : scrolly(&container_veh) { if (cnv.is_bound()) { init(cnv); } } void convoi_detail_t::init(convoihandle_t cnv) { this->cnv = cnv; set_table_layout(1,0); add_component( &container_txt ); container_txt.set_margin( scr_size(D_MARGIN_LEFT,0), scr_size(D_MARGIN_RIGHT,0) ); container_txt.set_table_layout(1,0); container_txt.add_component(&label_power); container_txt.add_component(&label_odometer); container_txt.add_component(&label_length); container_txt.add_component(&label_resale); container_txt.add_component(&label_speed); add_component(&scrolly); const sint32 cnv_kmh = (cnv->front()->get_waytype() == air_wt) ? speed_to_kmh(cnv->get_min_top_speed()) : cnv->get_speedbonus_kmh(); container_veh.set_table_layout(1,0); container_veh.set_checkered(true); container_veh.new_component(); for(unsigned veh=0; vehget_vehicle_count(); veh++ ) { // if(veh>0) { // only on non-checkred lists // container_veh.new_component(); // } vehicle_t *v = cnv->get_vehicle(veh); container_veh.new_component(v, cnv_kmh); } update_labels(); } void convoi_detail_t::update_labels() { char number[128]; number_to_string( number, (double)cnv->get_total_distance_traveled(), 0 ); label_odometer.buf().printf(translator::translate("Odometer: %s km"), number ); label_odometer.update(); label_power.buf().printf( translator::translate("Leistung: %d kW"), cnv->get_sum_power() ); label_power.update(); if( cnv->get_vehicle_count()>0 && cnv->get_vehicle( 0 )->get_desc()->get_waytype()==water_wt ) { label_length.buf().printf( "%s %i", translator::translate( "Vehicle count:" ), cnv->get_vehicle_count() ); } else { label_length.buf().printf( "%s %i %s %s %i %s", translator::translate( "Vehicle count:" ), cnv->get_vehicle_count(), "(", translator::translate( "Station tiles:" ), cnv->get_tile_length(), ")"); } label_length.update(); label_resale.buf().printf("%s ", translator::translate("Restwert:")); label_resale.buf().append_money( cnv->calc_restwert() / 100.0 ); label_resale.update(); label_speed.buf().printf(translator::translate("Bonusspeed: %i km/h"), cnv->get_speedbonus_kmh() ); label_speed.update(); } void convoi_detail_t::draw(scr_coord offset) { update_labels(); scrolly.set_size(scrolly.get_size()); gui_aligned_container_t::draw(offset); } void convoi_detail_t::rdwr(loadsave_t *file) { scrolly.rdwr(file); } simutrans-124.5/src/simutrans/gui/convoi_detail.h000066400000000000000000000022211520675460500221510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_CONVOI_DETAIL_H #define GUI_CONVOI_DETAIL_H #include "components/gui_aligned_container.h" #include "components/gui_scrollpane.h" #include "components/gui_label.h" #include "../convoihandle.h" class scr_coord; class karte_ptr_t; /** * Convoi details component * Fills information table for convoi */ class convoi_detail_t : public gui_aligned_container_t { public: enum sort_mode_t { by_destination = 0, by_via = 1, by_amount_via = 2, by_amount = 3, SORT_MODES = 4 }; private: gui_aligned_container_t container_veh, container_txt; gui_scrollpane_t scrolly; gui_label_buf_t label_power, label_odometer, label_resale, label_length, label_speed; convoihandle_t cnv; static karte_ptr_t welt; public: convoi_detail_t(convoihandle_t cnv = convoihandle_t()); /** * Initializes layout, @p cnv needs to be valid. */ void init(convoihandle_t cnv); void draw(scr_coord offset) OVERRIDE; void update_labels(); void rdwr( loadsave_t *file ); bool is_marginless() const OVERRIDE { return true; } }; #endif simutrans-124.5/src/simutrans/gui/convoi_filter_frame.cc000066400000000000000000000116651520675460500235200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "convoi_filter_frame.h" #include "convoi_frame.h" #include "components/gui_convoiinfo.h" #include "../simcolor.h" #include "../descriptor/goods_desc.h" #include "../builder/goods_manager.h" #include "../dataobj/translator.h" const char *convoi_filter_frame_t::filter_buttons_text[FILTER_BUTTONS] = { "clf_chk_spezial_filter", "clf_chk_waren", "clf_chk_noroute", "clf_chk_stucked", "clf_chk_noincome", "clf_chk_indepot", "clf_chk_noschedule", "clf_chk_noline", "clf_chk_obsolete" }; convoi_filter_frame_t::filter_flag_t convoi_filter_frame_t::filter_buttons_types[FILTER_BUTTONS] = { special_filter, ware_filter, noroute_filter, stucked_filter, noincome_filter, indepot_filter, noschedule_filter, noline_filter, obsolete_filter }; slist_tplconvoi_filter_frame_t::active_ware; uint32 convoi_filter_frame_t::filter_flags = 0; convoi_filter_frame_t::convoi_filter_frame_t(player_t *player, convoi_frame_t *m) : gui_frame_t( translator::translate("clf_title"), player), main_frame(m), ware_scrolly(&ware_cont) { for( int i=0; i < FILTER_BUTTONS; i++ ) { filter_buttons[i].init(button_t::square_state, filter_buttons_text[i]); filter_buttons[i].add_listener(this); if(filter_buttons_types[i] < sub_filter) { filter_buttons[i].background_color = gfx->palette_lookup(COL_WHITE); } filter_buttons[i].pressed = get_filter(filter_buttons_types[i]); } set_table_layout(1,0); set_alignment(ALIGN_TOP); add_table(3,0); { ware_alle.init(button_t::roundbox, "clf_btn_alle"); ware_alle.add_listener(this); add_component(&ware_alle); ware_keine.init(button_t::roundbox, "clf_btn_keine"); ware_keine.add_listener(this); add_component(&ware_keine); ware_invers.init(button_t::roundbox, "clf_btn_invers"); ware_invers.add_listener(this); add_component(&ware_invers); } end_table(); add_table( 2, 0 ); { add_table(2,0); { // special buttons add_component(filter_buttons, 2); for( int i=2; i(); add_component(filter_buttons + i); } } end_table(); ware_scrolly.set_scroll_amount_y(D_BUTTON_HEIGHT); add_component( &ware_scrolly ); } end_table(); ware_cont.set_table_layout(2,0); all_ware.clear(); ware_cont.add_component(filter_buttons + 1,2); for( int i=0; i < goods_manager_t::get_count(); i++ ) { const goods_desc_t *ware = goods_manager_t::get_info(i); if( ware == goods_manager_t::none ) { continue; } if( ware->get_catg() == 0 ) { // Special freight: Each good is special ware_cont.new_component(); ware_item_t *item = ware_cont.new_component(this, ware); item->init(button_t::square_state, translator::translate(ware->get_name())); item->pressed = active_ware.is_contained(ware); all_ware.append(item); } } // now add other good categories for( int i=1; i < goods_manager_t::get_max_catg_index(); i++ ) { const goods_desc_t *ware = goods_manager_t::get_info_catg(i); if( ware->get_catg() != 0 ) { ware_cont.new_component(); ware_item_t *item = ware_cont.new_component(this, ware); item->init(button_t::square_state, translator::translate(ware->get_catg_name())); item->pressed = active_ware.is_contained(ware); all_ware.append(item); } } set_resizemode(diagonal_resize); reset_min_windowsize(); } void convoi_filter_frame_t::init(uint32 filter_flags, const slist_tpl* wares) { for (int i = 0; i < FILTER_BUTTONS; i++) { set_filter(filter_buttons_types[i], filter_flags & filter_buttons_types[i]); filter_buttons[i].pressed = get_filter(filter_buttons_types[i]); } if (&active_ware != wares) { active_ware.clear(); if (wares) { for(ware_item_t* wi : all_ware) { wi->pressed = wares->is_contained(wi->ware); if (wi->pressed) { active_ware.append(wi->ware); } } } } } /** * This method is called if an action is triggered */ bool convoi_filter_frame_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { for( int i = 0; i < FILTER_BUTTONS; i++) { if(comp == filter_buttons + i) { filter_buttons[i].pressed ^= 1; set_filter( filter_buttons_types[i], !get_filter(filter_buttons_types[i]) ); sort_list(); return true; } } if(comp == &ware_alle) { for(ware_item_t * wi : all_ware ) { wi->pressed = true; } sort_list(); return true; } if(comp == &ware_keine) { for(ware_item_t * wi : all_ware ) { wi->pressed = false; } sort_list(); return true; } if( comp == &ware_invers ) { for(ware_item_t * wi : all_ware ) { wi->pressed ^= true; } sort_list(); return true; } return false; } void convoi_filter_frame_t::sort_list() { active_ware.clear(); for(ware_item_t * wi : all_ware ) { if( wi->pressed ) { active_ware.append( wi->ware ); } } main_frame->sort_list( filter_flags, &active_ware ); } simutrans-124.5/src/simutrans/gui/convoi_filter_frame.h000066400000000000000000000060261520675460500233550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_CONVOI_FILTER_FRAME_H #define GUI_CONVOI_FILTER_FRAME_H #include "gui_frame.h" #include "components/gui_label.h" #include "components/gui_scrollpane.h" #include "components/action_listener.h" #include "components/gui_button.h" class convoi_frame_t; class player_t; class goods_desc_t; /** * Displays a filter settings dialog for the convoi list */ class convoi_filter_frame_t : public gui_frame_t , private action_listener_t { public: enum filter_flag_t { any_filter = 1 << 0, special_filter = 1 << 1, ware_filter = 1 << 2, noroute_filter = 1 << 3, noschedule_filter = 1 << 4, noincome_filter = 1 << 5, indepot_filter = 1 << 6, noline_filter = 1 << 7, stucked_filter = 1 << 8, monorail_filter = 1 << 9, maglev_filter = 1 << 10, narrowgauge_filter = 1 << 11, tram_filter = 1 << 12, obsolete_filter = 1 << 13, // number of first special filter sub_filter = noroute_filter }; enum { FILTER_BUTTONS = 9 }; private: static uint32 filter_flags; bool get_filter(convoi_filter_frame_t::filter_flag_t filter) { return (filter_flags & filter) != 0; } void set_filter(convoi_filter_frame_t::filter_flag_t filter, bool on) { filter_flags = (on ? (filter_flags | filter) : (filter_flags & ~filter) ); } /* * Helper class for the entries of the scrollable list of goods. * Needed since a button_t does not know its parent. */ class ware_item_t : public button_t { public: const goods_desc_t *ware; convoi_filter_frame_t *parent; ware_item_t(convoi_filter_frame_t *parent, const goods_desc_t *ware) { this->ware = ware; this->parent = parent; } bool infowin_event(event_t const* const ev) OVERRIDE { bool swallow = button_t::infowin_event(ev); if( IS_LEFTRELEASE(ev) && swallow ) { pressed ^= 1; parent->sort_list(); } return swallow; } }; slist_tplall_ware; static slist_tplactive_ware; static filter_flag_t filter_buttons_types[FILTER_BUTTONS]; static const char *filter_buttons_text[FILTER_BUTTONS]; /* * We are bound to this window. All filter states are stored in main_frame. */ convoi_frame_t *main_frame; /* * All gui elements of this dialog: */ button_t filter_buttons[FILTER_BUTTONS]; button_t typ_filter_enable; button_t ware_alle; button_t ware_keine; button_t ware_invers; gui_aligned_container_t ware_cont; gui_scrollpane_t ware_scrolly; public: void sort_list(); /** * Constructor. Generates all necessary Subcomponents. */ convoi_filter_frame_t(player_t *player, convoi_frame_t *parent); void init(uint32 filter_flags, const slist_tpl* wares); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "convoi_filter.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/convoi_frame.cc000066400000000000000000000234601520675460500221470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "components/gui_convoiinfo.h" #include "convoi_frame.h" #include "convoi_filter_frame.h" #include "../builder/goods_manager.h" #include "../builder/vehikelbauer.h" #include "../obj/way/kanal.h" #include "../obj/way/maglev.h" #include "../obj/way/monorail.h" #include "../obj/way/narrowgauge.h" #include "../obj/way/runway.h" #include "../obj/way/schiene.h" #include "../obj/way/strasse.h" #include "simwin.h" #include "../simconvoi.h" #include "../world/simworld.h" #include "../utils/unicode.h" #include "../descriptor/goods_desc.h" #include "../builder/goods_manager.h" #include "../dataobj/translator.h" #include "../player/simplay.h" #include "../utils/simstring.h" #include "../vehicle/vehicle.h" /** * All filter and sort settings are static, so the old settings are * used when the window is reopened. */ convoi_frame_t::sort_mode_t convoi_frame_t::sortby = convoi_frame_t::nach_name; bool convoi_frame_t::sortreverse = false; const char *convoi_frame_t::sort_text[SORT_MODES] = { "cl_btn_sort_name", "cl_btn_sort_income", "cl_btn_sort_type", "cl_btn_sort_id" }; const slist_tpl* convoi_frame_t::waren_filter = NULL; waytype_t convoi_frame_t::current_wt = waytype_t::ignore_wt; uint32 convoi_frame_t::filter_flags = 0; char convoi_frame_t::last_name_filter[256] = ""; char convoi_frame_t::name_filter[256] = ""; /** * Scrolled list of gui_convoiinfo_ts. * Filters (by setting visibility) and sorts. */ class gui_scrolled_convoy_list_t : public gui_scrolled_list_t { convoi_frame_t *main; static convoi_frame_t *main_static; public: gui_scrolled_convoy_list_t(convoi_frame_t *m) : gui_scrolled_list_t(gui_scrolled_list_t::windowskin) { main = m; set_cmp(compare); } void sort() { main_static = main; gui_scrolled_list_t::sort(0); } static bool compare(const gui_component_t *aa, const gui_component_t *bb) { const gui_convoiinfo_t *a = dynamic_cast(aa); const gui_convoiinfo_t *b = dynamic_cast(bb); return main_static->compare_convois(a->get_cnv(), b->get_cnv()); } }; convoi_frame_t* gui_scrolled_convoy_list_t::main_static; bool convoi_frame_t::passes_filter(convoihandle_t cnv) { if(current_wt && cnv->front()->get_desc()->get_waytype() != current_wt ) { // not the right kind of vehivle return false; } if( name_filter[0]!=0 && !utf8caseutf8(cnv->get_name(), name_filter) ) { // not the right name return false; } if( get_filter(convoi_filter_frame_t::special_filter) ) { if ((!get_filter(convoi_filter_frame_t::noroute_filter) || cnv->get_state() != convoi_t::NO_ROUTE) && (!get_filter(convoi_filter_frame_t::stucked_filter) || (cnv->get_state() != convoi_t::WAITING_FOR_CLEARANCE_TWO_MONTHS && cnv->get_state() != convoi_t::CAN_START_TWO_MONTHS)) && (!get_filter(convoi_filter_frame_t::indepot_filter) || !cnv->in_depot()) && (!get_filter(convoi_filter_frame_t::noline_filter) || cnv->get_line().is_bound()) && (!get_filter(convoi_filter_frame_t::noschedule_filter) || cnv->get_schedule()) && (!get_filter(convoi_filter_frame_t::noincome_filter) || cnv->get_jahresgewinn() >= 100) && (!get_filter(convoi_filter_frame_t::obsolete_filter) || !cnv->has_obsolete_vehicles())) { return false; } } if( get_filter(convoi_filter_frame_t::ware_filter) ) { unsigned i; for( i = 0; i < cnv->get_vehicle_count(); i++) { const goods_desc_t *wb = cnv->get_vehicle(i)->get_cargo_type(); if( wb->get_catg()!=0 ) { wb = goods_manager_t::get_info_catg(wb->get_catg()); } if( waren_filter->is_contained(wb) ) { return true; } } if( i == cnv->get_vehicle_count() ) { return false; } } return true; } bool convoi_frame_t::compare_convois(convoihandle_t const cnv1, convoihandle_t const cnv2) { sint32 result = 0; switch (sortby) { default: case nach_name: result = strcmp(cnv1->get_internal_name(), cnv2->get_internal_name()); break; case nach_gewinn: result = sgn(cnv1->get_jahresgewinn() - cnv2->get_jahresgewinn()); break; case nach_typ: if(cnv1->get_vehicle_count()*cnv2->get_vehicle_count()>0) { vehicle_t const* const fahr1 = cnv1->front(); vehicle_t const* const fahr2 = cnv2->front(); result = fahr1->get_typ() - fahr2->get_typ(); if(result == 0) { result = fahr1->get_cargo_type()->get_catg_index() - fahr2->get_cargo_type()->get_catg_index(); if(result == 0) { result = fahr1->get_base_image() - fahr2->get_base_image(); } } } break; case nach_id: result = cnv1.get_id()-cnv2.get_id(); break; } return sortreverse ? result > 0 : result < 0; } void convoi_frame_t::fill_list() { last_world_convois = welt->convoys().get_count(); current_wt = tabs.get_active_tab_waytype(); const bool all = owner->is_public_service(); scrolly->clear_elements(); for(convoihandle_t const cnv : welt->convoys()) { if( all || cnv->get_owner()==owner ) { if( passes_filter( cnv ) ) { scrolly->new_component( cnv ); } } } sort_list(); scrolly->set_size(scr_size(get_windowsize().w, scrolly->get_size().h)); } void convoi_frame_t::sort_list() { scrolly->sort(); sortedby.set_selection(sortby); } void convoi_frame_t::sort_list( uint32 filter, const slist_tpl *wares ) { waren_filter = wares; filter_flags = filter; fill_list(); } convoi_frame_t::convoi_frame_t() : gui_frame_t( translator::translate("cl_title"), welt->get_active_player()) { owner = welt->get_active_player(); set_table_layout(1,0); add_table(4,2); { new_component("Filter:"); name_filter_input.set_text(name_filter, lengthof(name_filter)); name_filter_input.set_notify_all_changes_delay(500); name_filter_input.add_listener(this); add_component(&name_filter_input); name_filter_input.add_listener(this); filter_details.init(button_t::roundbox, "cl_btn_filter_settings"); filter_details.add_listener(this); add_component(&filter_details); new_component(); new_component("cl_txt_sort"); sortedby.set_unsorted(); // do not sort for( size_t i=0; i < lengthof(sort_text); i++ ) { sortedby.new_component(translator::translate(sort_text[i]),SYSCOL_TEXT); } sortedby.set_selection(get_sortierung()); sortedby.add_listener(this); add_component(&sortedby); sorteddir.init(button_t::sortarrow_automatic, NULL); sorteddir.add_listener(this); sorteddir.pressed = get_reverse(); add_component(&sorteddir); new_component(); } end_table(); scrolly = new gui_scrolled_convoy_list_t(this); scrolly->set_maximize( true ); scrolly->set_checkered( true ); tabs.init_tabs(scrolly); for( uint32 i = 0; i < tabs.get_count(); i++ ) { if(current_wt == tabs.get_tab_waytype(i)) { tabs.set_active_tab_index(i); break; } } tabs.add_listener(this); add_component(&tabs); fill_list(); set_resizemode(diagonal_resize); reset_min_windowsize(); } convoi_frame_t::~convoi_frame_t() { destroy_win( magic_convoi_list_filter+owner->get_player_nr() ); } bool convoi_frame_t::infowin_event(const event_t *ev) { if(ev->ev_class == INFOWIN && ev->ev_code == WIN_CLOSE) { destroy_win( magic_convoi_list_filter+owner->get_player_nr() ); } return gui_frame_t::infowin_event(ev); } /** * This method is called if an action is triggered */ bool convoi_frame_t::action_triggered( gui_action_creator_t *comp, value_t /* */ ) { if( comp == &name_filter_input ) { fill_list(); } else if( comp == &tabs ) { fill_list(); } else if( comp == &sortedby ) { set_sortierung( (sort_mode_t)((get_sortierung() + 1) % SORT_MODES) ); sort_list(); } else if( comp == &sorteddir ) { set_reverse( !get_reverse() ); sort_list(); } else if( comp == &filter_details ) { if( !destroy_win( magic_convoi_list_filter+owner->get_player_nr() ) ) { convoi_filter_frame_t *gui_cff = new convoi_filter_frame_t(owner, this); gui_cff->init(filter_flags, waren_filter); create_win( gui_cff, w_info, magic_convoi_list_filter+owner->get_player_nr() ); } } return true; } void convoi_frame_t::draw(scr_coord pos, scr_size size) { filter_details.pressed = win_get_magic( magic_convoi_list_filter+owner->get_player_nr() ); sorteddir.pressed = get_reverse(); if (last_world_convois != welt->convoys().get_count()) { // some deleted/ added => resort fill_list(); } gui_frame_t::draw(pos, size); } void convoi_frame_t::rdwr( loadsave_t *file ) { scr_size size = get_windowsize(); uint8 player_nr = owner->get_player_nr(); sint16 sort_mode = sortby; file->rdwr_byte( player_nr ); size.rdwr( file ); tabs.rdwr( file ); scrolly->rdwr( file ); file->rdwr_str( name_filter, lengthof( name_filter ) ); file->rdwr_short( sort_mode ); file->rdwr_bool( sortreverse ); file->rdwr_long( filter_flags ); if( file->is_saving() ) { uint8 good_nr = get_filter(convoi_filter_frame_t::ware_filter) ? waren_filter->get_count() : 0; file->rdwr_byte( good_nr ); if (good_nr > 0) { for(const goods_desc_t * const i : *waren_filter ) { char *name = const_cast(i->get_name()); file->rdwr_str(name,256); } } } else { uint8 good_nr; file->rdwr_byte( good_nr ); if( good_nr > 0 ) { static slist_tplwaren_filter_rd; for( sint16 i = 0; i < good_nr; i++ ) { char name[256]; file->rdwr_str(name, lengthof(name)); if (const goods_desc_t *gd = goods_manager_t::get_info(name)) { waren_filter_rd.append(gd); } } waren_filter = &waren_filter_rd; } sortby = (sort_mode_t)sort_mode; owner = welt->get_player( player_nr ); win_set_magic(this, magic_convoi_list+player_nr ); current_wt = tabs.get_active_tab_waytype(); fill_list(); set_windowsize( size ); } } simutrans-124.5/src/simutrans/gui/convoi_frame.h000066400000000000000000000054501520675460500220100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_CONVOI_FRAME_H #define GUI_CONVOI_FRAME_H #include "gui_frame.h" #include "simwin.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_combobox.h" #include "components/gui_textinput.h" #include "components/gui_waytype_tab_panel.h" #include "../convoihandle.h" class player_t; class goods_desc_t; class gui_scrolled_convoy_list_t; /** * Displays a scrollable list of all convois of a player */ class convoi_frame_t : public gui_frame_t, private action_listener_t { public: enum sort_mode_t { nach_name = 0, nach_gewinn = 1, nach_typ = 2, nach_id = 3, SORT_MODES = 4 }; private: player_t *owner; static const char *sort_text[SORT_MODES]; /// number of convoys the last time we checked. uint32 last_world_convois; // these are part of the top UI gui_combobox_t sortedby; button_t sorteddir; button_t filter_details; static char name_filter[256], last_name_filter[256]; gui_textinput_t name_filter_input; // scroll container of list of convois gui_scrolled_convoy_list_t *scrolly; gui_waytype_tab_panel_t tabs; // actual filter setting static const slist_tpl*waren_filter; static uint32 filter_flags; static waytype_t current_wt; bool get_filter(uint32 filter) { return (filter_flags & filter) != 0; } void set_filter(uint32 filter, bool on) { filter_flags = on ? (filter_flags | filter) : (filter_flags & ~filter); } /// sort & filter convoys in list void sort_list(); /// refill the list of convoy info elements void fill_list(); /* * All filter settings are static, so they are not reset each * time the window closes. */ static sort_mode_t sortby; static bool sortreverse; public: static bool compare_convois(convoihandle_t, convoihandle_t); /** * Check all filters for one convoi. * returns true, if it is not filtered away. */ bool passes_filter(convoihandle_t cnv); /** * Resorts convois */ void sort_list( uint32 filter, const slist_tpl *wares ); convoi_frame_t(); ~convoi_frame_t(); /** * Events are notified to GUI components via this method */ bool infowin_event(const event_t *ev) OVERRIDE; void draw(scr_coord pos, scr_size size) OVERRIDE; const char * get_help_filename() const OVERRIDE {return "convoi.txt"; } static sort_mode_t get_sortierung() { return sortby; } static void set_sortierung(sort_mode_t sm) { sortby = sm; } static bool get_reverse() { return sortreverse; } static void set_reverse(bool reverse) { sortreverse = reverse; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void rdwr( loadsave_t *file ) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_convoi_list; } }; #endif simutrans-124.5/src/simutrans/gui/convoi_info.cc000066400000000000000000000551571520675460500220200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "convoi_info.h" #include "minimap.h" #include "../vehicle/rail_vehicle.h" #include "../simcolor.h" #include "../display/viewport.h" #include "../world/simworld.h" #include "../tool/simmenu.h" #include "simwin.h" #include "../dataobj/schedule.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/loadsave.h" #include "../freight_list_sorter.h" #include "../simconvoi.h" #include "../simline.h" #include "../player/simplay.h" #include "../utils/simstring.h" #include "depot_frame.h" #include "line_item.h" #include "convoi_detail.h" #include "line_management_gui.h" #define CHART_HEIGHT (100) static const char cost_type[convoi_t::MAX_CONVOI_COST][64] = { "Free Capacity", "Transported", "Revenue", "Operation", "Profit", "Distance", "Maxspeed", "Way toll" }; static const uint8 cost_type_color[convoi_t::MAX_CONVOI_COST] = { COL_FREE_CAPACITY, COL_TRANSPORTED, COL_REVENUE, COL_OPERATION, COL_PROFIT, COL_DISTANCE, COL_MAXSPEED, COL_TOLL }; static const bool cost_type_money[convoi_t::MAX_CONVOI_COST] = { false, false, true, true, true, false, false, true }; bool convoi_info_t::route_search_in_progress=false; convoi_info_t::convoi_info_t(convoihandle_t cnv, bool edit_schedule) : gui_frame_t(""), text(&freight_info), view(scr_size(max(64, gfx->get_base_tile_raster_width()), max(56, (gfx->get_base_tile_raster_width() * 7) / 8))), scroll_freight(&container_freight, true, true) { is_saving_gui = false; if( cnv.is_bound() ) { init( cnv ); if( edit_schedule ) { change_schedule(); } } } void convoi_info_t::init(convoihandle_t cnv) { this->cnv = cnv; this->mean_convoi_speed = speed_to_kmh(cnv->get_akt_speed()*4); this->max_convoi_speed = speed_to_kmh(cnv->get_min_top_speed()*4); gui_frame_t::set_name(cnv->get_name()); gui_frame_t::set_owner(cnv->get_owner()); minimap_t::get_instance()->set_selected_cnv(cnv); set_table_layout(1,0); input.add_listener(this); reset_cnv_name(); input.set_notify_all_changes_delay(500); add_component(&input); // top part: speedbars, view, buttons add_table(2,0)->set_alignment(ALIGN_TOP); { container_top = add_table(1,0); { add_table(2,1); add_component(&speed_label); add_component(&speed_bar); end_table(); add_table(3,1); new_component("Gewinn"); profit_label.set_align(gui_label_t::left); add_component(&profit_label); add_component(&running_cost_label); end_table(); add_table(2,1); add_component(&weight_label); add_component(&filled_bar); end_table(); add_table(2,1); add_component(&target_label); add_component(&route_bar); end_table(); add_table(2,1); line_button2.init( button_t::arrowright, NULL ); line_button2.add_listener( this ); add_component( &line_button2 ); add_component( &line_label ); end_table(); } end_table(); add_component(&view); view.set_obj(cnv->front()); } end_table(); // tab panel: connections, chart panels, details add_component(&switch_mode); switch_mode.add_listener( this ); switch_mode.add_tab(&scroll_freight, translator::translate("Freight")); container_freight.set_table_layout(1,2); container_freight.add_table(2,1); { container_freight.new_component("loaded passenger/freight"); sort_mode_button.init(button_t::roundbox, freight_list_sorter_t::get_sort_mode_string(env_t::default_sortmode,halthandle_t())); sort_mode_button.set_tooltip("Sort by"); sort_mode_button.add_listener(this); container_freight.add_component(&sort_mode_button); } container_freight.end_table(); container_freight.add_component(&text); switch_mode.add_tab(&container_schedule, translator::translate("Fahrplan")); // tooltip would be set_tooltip("Alters a schedule."); container_schedule.set_table_layout(1, 0); container_schedule.add_table( 3, 1 ); { container_schedule.new_component("Serves Line:"); line_selector.clear_elements(); container_schedule.add_component(&line_selector); line_selector.add_listener(this); line_button.init( button_t::roundbox, "Update Line" ); line_button.set_tooltip("Modify the selected line"); line_button.add_listener(this); container_schedule.add_component(&line_button); } container_schedule.end_table(); scd.init(cnv->get_schedule(), cnv->get_owner(), cnv, cnv->get_line() ); old_schedule_count = scd.get_schedule()->get_count()+1; init_line_selector(); container_schedule.add_component(&scd); scd.add_listener(this); switch_mode.add_tab(&container_stats, translator::translate("Chart")); container_stats.set_table_layout(1,0); chart.set_dimension(12, 10000); chart.set_background(SYSCOL_CHART_BACKGROUND); chart.set_min_size(scr_size(0, CHART_HEIGHT)); container_stats.add_component(&chart); container_stats.add_table(D_BUTTONS_PER_ROW,0)->set_force_equal_columns(true); for (int cost = 0; costpalette_lookup(cost_type_color[cost]), cnv->get_finance_history(), convoi_t::MAX_CONVOI_COST, cost, MAX_MONTHS, cost_type_money[cost], false, true, cost_type_money[cost]*2 ); button_t *b = container_stats.new_component(); b->init(button_t::box_state_automatic | button_t::flexible, cost_type[cost]); b->background_color = gfx->palette_lookup(cost_type_color[cost]); b->pressed = false; button_to_chart.append(b, &chart, curve); } container_stats.end_table(); // convoy details in tab switch_mode.add_tab(&container_details, translator::translate("Vehicle details")); container_details.set_table_layout(1,0); container_details.add_table(D_BUTTONS_PER_ROW, 0)->set_force_equal_columns(true); { no_load_button.init(button_t::roundbox | button_t::flexible, "no load"); no_load_button.set_tooltip("No goods are loaded onto this convoi."); no_load_button.add_listener(this); container_details.add_component(&no_load_button); withdraw_button.init(button_t::roundbox | button_t::flexible, "withdraw"); withdraw_button.set_tooltip("Convoi is sold when all wagons are empty."); withdraw_button.add_listener(this); container_details.add_component(&withdraw_button); go_home_button.init(button_t::roundbox | button_t::flexible, "go home"); go_home_button.set_tooltip("Sends the convoi to the last depot it departed from!"); go_home_button.add_listener(this); container_details.add_component(&go_home_button); sale_button.init(button_t::roundbox | button_t::flexible, "Verkauf"); sale_button.set_tooltip("Remove vehicle from map. Use with care!"); sale_button.add_listener(this); container_details.add_component(&sale_button); } container_details.end_table(); details = container_details.new_component(cnv); // indicator bars filled_bar.add_color_value(&cnv->get_loading_limit(), gfx->palette_lookup(COL_YELLOW)); filled_bar.add_color_value(&cnv->get_loading_level(), gfx->palette_lookup(COL_GREEN)); speed_bar.set_base(max_convoi_speed); speed_bar.set_vertical(false); speed_bar.add_color_value(&mean_convoi_speed, gfx->palette_lookup(COL_GREEN)); // we update this ourself! route_bar.init(&cnv_route_index, 0); if( cnv->get_vehicle_count()>0 && dynamic_cast(cnv->front()) ) { // only for trains etc. route_bar.set_reservation( &next_reservation_index ); } route_bar.set_height(9); cnv->reset_freight_info(); update_labels(); reset_min_windowsize(); set_windowsize(get_min_windowsize()); set_resizemode(diagonal_resize); } // only handle a pending renaming ... convoi_info_t::~convoi_info_t() { button_to_chart.clear(); // rename if necessary rename_cnv(); } // apply new schedule void convoi_info_t::apply_schedule() { if( (!cnv.is_bound()) || (cnv->get_state()!=convoi_t::EDIT_SCHEDULE && cnv->get_state()!=convoi_t::INITIAL) ) { // no change allowed (one can only enter this state when editing was allowed) return; } // do not send changes if the convoi is about to be deleted if (cnv->get_state() != convoi_t::SELF_DESTRUCT) { if (cnv->in_depot()) { const grund_t* const ground = welt->lookup(cnv->get_home_depot()); if (ground) { const depot_t* const depot = ground->get_depot(); if (depot) { depot_frame_t* const frame = dynamic_cast(win_get_magic((ptrdiff_t)depot)); if (frame) { frame->update_data(); } } } } // update new line instead if (line != cnv->get_line()) { char id[16]; sprintf(id, "%i,%i", line.get_id(), cnv->get_schedule()->get_current_stop()); cnv->call_convoi_tool('l', id); } // since waiting times might be different from line cbuffer_t buf; scd.get_schedule()->sprintf_schedule(buf); cnv->call_convoi_tool('g', buf); } } void convoi_info_t::init_line_selector() { if (cnv.is_bound()) { line_selector.clear_elements(); int selection = 0; vector_tpl lines; cnv->get_owner()->simlinemgmt.get_lines(cnv->get_schedule()->get_type(), &lines); bool new_bound = false; for(linehandle_t other_line : lines) { if( scd.get_schedule()->matches( world(), other_line->get_schedule() ) ) { if( line != other_line ) { line = other_line; reset_min_windowsize(); scd.init( scd.get_schedule(), cnv->get_owner(), cnv, line ); } new_bound = true; break; } } if( !new_bound && line.is_bound() ) { // remove linehandle from schedule line = linehandle_t(); scd.init( scd.get_schedule(), cnv->get_owner(), cnv, line ); } int offset = 2; if (!line.is_bound()) { selection = 0; offset = 3; line_selector.new_component(translator::translate(""), SYSCOL_TEXT); line_selector.new_component(translator::translate(""), SYSCOL_TEXT); line_selector.new_component("--------------------------------", SYSCOL_TEXT); } for(linehandle_t other_line : lines) { line_selector.new_component(other_line); if (line == other_line) { selection = line_selector.count_elements() - 1; } } line_selector.set_selection(selection); line_scrollitem_t::sort_mode = line_scrollitem_t::SORT_BY_NAME; line_selector.sort(offset); old_line_count = cnv->get_owner()->simlinemgmt.get_line_count(); } } void convoi_info_t::update_labels() { switch (cnv->get_state()) { case convoi_t::DRIVING: route_bar.set_state(0); break; case convoi_t::WAITING_FOR_CLEARANCE_ONE_MONTH: case convoi_t::WAITING_FOR_CLEARANCE_TWO_MONTHS: case convoi_t::CAN_START_ONE_MONTH: case convoi_t::CAN_START_TWO_MONTHS: route_bar.set_state(2); break; case convoi_t::NO_ROUTE: route_bar.set_state(3); break; default: route_bar.set_state(1); break; } // use median speed to avoid flickering mean_convoi_speed += speed_to_kmh(cnv->get_akt_speed()*4); mean_convoi_speed /= 2; speed_label.buf().printf( translator::translate("%i km/h (max. %ikm/h)"), (mean_convoi_speed+3)/4, speed_to_kmh(cnv->get_min_top_speed()) ); speed_label.update(); profit_label.append_money(cnv->get_jahresgewinn()/100.0); profit_label.update(); running_cost_label.buf().append("("); running_cost_label.append_money(cnv->get_running_cost()/100.0); running_cost_label.buf().append("/km)"); running_cost_label.update(); weight_label.buf().append( translator::translate("Gewicht") ); weight_label.buf().append( ": " ); weight_label.buf().append( cnv->get_sum_gesamtweight()/1000.0, 1 ); weight_label.buf().append( "t (" ); weight_label.buf().append( (cnv->get_sum_gesamtweight()-cnv->get_sum_weight())/1000.0, 1 ); weight_label.buf().append( "t)" ); weight_label.update(); // next stop target_label.buf().append(translator::translate("Fahrtziel")); schedule_t::gimme_stop_name(target_label.buf(), welt, cnv->get_owner(), cnv->front()->get_waytype()==water_wt, cnv->get_schedule()->get_current_entry(), 34); target_label.update(); // only show assigned line, if there is one! if( cnv->get_line().is_bound() ) { line_label.buf().append(cnv->get_line()->get_name()); line_label.set_color(cnv->get_line()->get_state_color()); } else { line_label.buf().clear(); } line_button2.set_visible( cnv->get_line().is_bound() ); line_label.update(); if( switch_mode.get_aktives_tab() == &scroll_freight ) { sort_mode_button.set_text(freight_list_sorter_t::get_sort_mode_string(env_t::default_sortmode, halthandle_t())); // buffer update now only when needed by convoi itself => dedicated buffer for this const int old_len = freight_info.len(); cnv->get_freight_info(freight_info); if( old_len != freight_info.len() ) { text.recalc_size(); scroll_freight.set_size(scroll_freight.get_size()); } } // realign container - necessary if strings changed length container_top->set_size( container_top->get_size() ); } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void convoi_info_t::draw(scr_coord pos, scr_size size) { if(!cnv.is_bound()) { destroy_win(this); return; } next_reservation_index = cnv->get_next_reservation_index(); bool is_change_allowed = cnv->get_owner() == welt->get_active_player() && !welt->get_active_player()->is_locked(); if( line_selector.get_selection()>1 && !line.is_bound() ) { init_line_selector(); } if( !scd.get_schedule()->empty() && line_selector.get_selection()==1 && !line.is_bound() ) { // catch new schedule if promoting to line init_line_selector(); if( !line.is_bound() ) { line_selector.set_selection(1); } reset_min_windowsize(); } else if( old_schedule_count != scd.get_schedule()->get_count() ) { // entry added or removed init_line_selector(); reset_min_windowsize(); old_schedule_count = scd.get_schedule()->get_count(); } else if( old_line_count != cnv->get_owner()->simlinemgmt.get_line_count() ) { // line added or removed init_line_selector(); reset_min_windowsize(); } line_button.enable( dynamic_cast(line_selector.get_selected_item()) ); line_button2.enable( line.is_bound() ); go_home_button.enable(!route_search_in_progress && is_change_allowed); if( grund_t* gr=welt->lookup(cnv->get_schedule()->get_current_entry().pos) ) { go_home_button.pressed = gr->get_depot() != NULL; } no_load_button.pressed = cnv->get_no_load(); no_load_button.enable(is_change_allowed); withdraw_button.enable(is_change_allowed); sale_button.enable(is_change_allowed); line_selector.enable( is_change_allowed ); withdraw_button.pressed = cnv->get_withdraw(); update_labels(); route_bar.set_base(cnv->get_route()->get_count()-1); cnv_route_index = cnv->front()->get_route_index() - 1; // all gui stuff set => display it gui_frame_t::draw(pos, size); } bool convoi_info_t::is_weltpos() { return (welt->get_viewport()->get_follow_convoi()==cnv); } koord3d convoi_info_t::get_weltpos( bool set ) { if( set ) { if( !is_weltpos() ) { welt->get_viewport()->set_follow_convoi( cnv ); } else { welt->get_viewport()->set_follow_convoi( convoihandle_t() ); } return koord3d::invalid; } else { return cnv.is_bound() ? cnv->get_pos() : koord3d::invalid; } } /** * This method is called if an action is triggered */ bool convoi_info_t::action_triggered( gui_action_creator_t *comp, value_t v) { minimap_t::get_instance()->set_selected_cnv(cnv); if( comp == &line_button ) { // open selected line as schedule if( line_scrollitem_t* li = dynamic_cast(line_selector.get_selected_item()) ) { if( li->get_line().is_bound() ) { cnv->get_owner()->simlinemgmt.show_lineinfo( cnv->get_owner(), li->get_line(), 0 ); } } } else if( comp == &line_button2 ) { // open selected line as schedule if( cnv->get_line().is_bound() ) { cnv->get_owner()->simlinemgmt.show_lineinfo( cnv->get_owner(), cnv->get_line(), -1 ); } } else if( comp == &input ) { // rename if necessary rename_cnv(); } // sort by what else if( comp == &sort_mode_button ) { // sort by what env_t::default_sortmode = (int)(env_t::default_sortmode+1) % ((int)freight_list_sorter_t::SORT_MODES-1); cnv->reset_freight_info(); } bool edit_allowed = (cnv.is_bound() && (cnv->get_owner() == welt->get_active_player() || welt->get_active_player()->is_public_service())); if (comp == &switch_mode) { if (v.i == 1) { if(edit_allowed && !cnv->in_depot()) { // if not in depot: // set state to EDIT_SCHEDULE, calls cnv->schedule->start_editing(), reset in gui_schedule_t::~gui_schedule_t cnv->call_convoi_tool('s', "1"); scd.init(cnv->get_schedule(), cnv->get_owner(), cnv, cnv->get_line()); reset_min_windowsize(); } } else if(cnv->get_state()==convoi_t::EDIT_SCHEDULE || cnv->get_state()==convoi_t::INITIAL) { apply_schedule(); } scd.highlight_schedule(v.i == 1); } // some actions only allowed, when I am the player if(edit_allowed) { if( comp == &no_load_button && !route_search_in_progress ) { cnv->call_convoi_tool( 'n', NULL ); return true; } if( comp == &go_home_button && !route_search_in_progress ) { // limit update to certain states that are considered to be safe for schedule updates int state = cnv->get_state(); if(state==convoi_t::EDIT_SCHEDULE || cnv->get_state()==convoi_t::INITIAL) { return true; } grund_t* gr = welt->lookup(cnv->get_schedule()->get_current_entry().pos); const bool enable_gohome = gr && gr->get_depot() == NULL; if( enable_gohome ) { // go to depot route_search_in_progress = true; cnv->call_convoi_tool( 'd', NULL ); } else { // back to normal schedule schedule_t* schedule = cnv->get_schedule()->copy(); schedule->remove(); // remove depot entry cbuffer_t buf; schedule->sprintf_schedule( buf ); cnv->call_convoi_tool( 'g', buf ); delete schedule; } } // end go home button if (comp == &sale_button) { cnv->call_convoi_tool('x', NULL); return true; } else if (comp == &withdraw_button) { cnv->call_convoi_tool('w', NULL); return true; } else if (comp == &scd) { if( v.p == NULL ) { scd.init( cnv->get_schedule(), cnv->get_owner(), cnv, cnv->get_line() ); // revert schedule init_line_selector(); reset_min_windowsize(); } } else if (comp == &line_selector) { uint32 selection = v.i; if( line_scrollitem_t* li = dynamic_cast(line_selector.get_element(selection)) ) { line = li->get_line(); scd.init(line->get_schedule(), cnv->get_owner(), cnv, line); reset_min_windowsize(); } else if( v.i==1 ) { // create line schedule via tool! tool_t* tool = create_tool(TOOL_CHANGE_LINE | SIMPLE_TOOL); cbuffer_t buf; buf.printf("c,0,%i,%ld,", (int)scd.get_schedule()->get_type(), (long)(intptr_t)cnv->get_schedule()); scd.get_schedule()->sprintf_schedule(buf); tool->set_default_param(buf); welt->set_tool(tool, cnv->get_owner()); // since init always returns false, it is safe to delete immediately delete tool; } else { // remove line line = linehandle_t(); line_selector.set_selection(0); schedule_t *temp = scd.get_schedule()->copy(); scd.init(temp, cnv->get_owner(), cnv, line); reset_min_windowsize(); delete temp; } return true; } } return false; } void convoi_info_t::change_schedule() { if( switch_mode.get_aktives_tab() != &container_schedule ) { switch_mode.set_active_tab_index(1); scd.highlight_schedule( true ); value_t v( 1 ); action_triggered( &switch_mode, 1 ); } } void convoi_info_t::update_schedule() { cnv->check_pending_updates(); if (switch_mode.get_aktives_tab() == &container_schedule) { scd.init(cnv->get_schedule(), cnv->get_owner(), cnv, cnv->get_line(),true); change_schedule(); cnv->set_state(convoi_t::EDIT_SCHEDULE); scd.highlight_schedule(true); } } bool convoi_info_t::infowin_event(const event_t *ev) { if( ev->ev_class == INFOWIN && ev->ev_code == WIN_CLOSE ) { if( switch_mode.get_aktives_tab() == &container_schedule && !is_saving_gui ) { // apply schedule here, to reset wait_lock for convoys in convoi_t::set_schedule apply_schedule(); } scd.highlight_schedule(false); minimap_t::get_instance()->set_selected_cnv(convoihandle_t()); } if( ev->ev_class == INFOWIN && ev->ev_code == WIN_TOP ) { if( switch_mode.get_aktives_tab() == &container_schedule && !cnv->in_depot() ) { cnv->call_convoi_tool( 's', "1" ); scd.highlight_schedule( true ); } minimap_t::get_instance()->set_selected_cnv(cnv); } is_saving_gui = false; return gui_frame_t::infowin_event(ev); } void convoi_info_t::reset_cnv_name() { // change text input of selected line if (cnv.is_bound()) { tstrncpy(old_cnv_name, cnv->get_name(), sizeof(old_cnv_name)); tstrncpy(cnv_name, cnv->get_name(), sizeof(cnv_name)); input.set_text(cnv_name, sizeof(cnv_name)); } } void convoi_info_t::rename_cnv() { if (cnv.is_bound()) { const char *t = input.get_text(); // only change if old name and current name are the same // otherwise some unintended undo if renaming would occur if( t && t[0] && strcmp(t, cnv->get_name()) && strcmp(old_cnv_name, cnv->get_name())==0) { // text changed => call tool cbuffer_t buf; buf.printf( "c%u,%s", cnv.get_id(), t ); tool_t *tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tool->set_default_param(buf); welt->set_tool(tool, cnv->get_owner()); // since init always returns false, it is safe to delete immediately delete tool; // do not trigger this command again tstrncpy(old_cnv_name, t, sizeof(old_cnv_name)); } } } void convoi_info_t::rdwr(loadsave_t *file) { is_saving_gui = true; // so we do not apply the schedule on window closing // handle convoi_t::rdwr_convoihandle_t(file, cnv); file->rdwr_byte( env_t::default_sortmode ); // window size scr_size size = get_windowsize(); size.rdwr( file ); // init window if( file->is_loading() && cnv.is_bound()) { init(cnv); win_set_magic(this, magic_convoi_info + cnv.get_id()); } // after initialization // components scroll_freight.rdwr(file); switch_mode.rdwr(file); // schedule stuff simline_t::rdwr_linehandle_t(file, line); scd.rdwr(file); // button-to-chart array button_to_chart.rdwr(file); // convoi details details->rdwr(file); // convoy vanished if( !cnv.is_bound() ) { dbg->error( "convoi_info_t::rdwr()", "Could not restore convoi info window of (%d)", cnv.get_id() ); destroy_win( this ); return; } if( file->is_loading() ) { reset_min_windowsize(); set_windowsize(size); if (switch_mode.get_aktives_tab() == &container_schedule) { cnv->set_state(convoi_t::EDIT_SCHEDULE); scd.highlight_schedule(true); } cnv->reset_freight_info(); } } simutrans-124.5/src/simutrans/gui/convoi_info.h000066400000000000000000000067341520675460500216570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_CONVOI_INFO_H #define GUI_CONVOI_INFO_H #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_button_to_chart.h" #include "components/gui_chart.h" #include "components/gui_combobox.h" #include "components/gui_label.h" #include "components/gui_obj_view.h" #include "components/gui_schedule.h" #include "components/gui_scrollpane.h" #include "components/gui_speedbar.h" #include "components/gui_tab_panel.h" #include "components/gui_textarea.h" #include "components/gui_textinput.h" #include "../convoihandle.h" #include "simwin.h" #include "../utils/cbuffer.h" class convoi_detail_t; /** * Displays an information window for a convoi */ class convoi_info_t : public gui_frame_t, private action_listener_t { private: /** * Buffer for freight info text string. */ cbuffer_t freight_info; gui_textarea_t text; obj_view_t view; gui_label_buf_t speed_label, profit_label, running_cost_label, weight_label, target_label, line_label; gui_textinput_t input; gui_speedbar_t filled_bar; gui_speedbar_t speed_bar; gui_routebar_t route_bar; sint32 next_reservation_index; gui_chart_t chart; gui_tab_panel_t switch_mode; gui_aligned_container_t container_freight, container_schedule, container_stats, *container_top, container_details; convoi_detail_t *details; gui_scrollpane_t scroll_freight; button_t sort_mode_button; button_t line_button, line_button2; bool line_bound; // schedule handling button_t go_home_button; button_t no_load_button; button_t sale_button; button_t withdraw_button; gui_combobox_t line_selector; // to add new lines automatically uint32 old_line_count; uint8 old_schedule_count; linehandle_t line; gui_schedule_t scd; convoihandle_t cnv; sint32 mean_convoi_speed; sint32 max_convoi_speed; // current pointer to route ... sint32 cnv_route_index; char cnv_name[256],old_cnv_name[256]; void update_labels(); // resets textinput to current convoi name // necessary after convoi was renamed void reset_cnv_name(); // rename selected convoi // checks if possible / necessary void rename_cnv(); static bool route_search_in_progress; void show_hide_statistics( bool show ); gui_button_to_chart_array_t button_to_chart; void init(convoihandle_t cnv); void apply_schedule(); bool is_saving_gui; public: convoi_info_t(convoihandle_t cnv = convoihandle_t(), bool change_schedule = false); virtual ~convoi_info_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE { return "convoiinfo.txt"; } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; bool is_weltpos() OVERRIDE; koord3d get_weltpos( bool set ) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; /** * called when convoi was renamed */ void update_data() { reset_cnv_name(); set_dirty(); } void init_line_selector(); bool infowin_event( const event_t *ev ) OVERRIDE; void rdwr( loadsave_t *file ) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_convoi_info+cnv.get_id(); } void route_search_finished() { route_search_in_progress = false; } void change_schedule(); void update_schedule(); }; #endif simutrans-124.5/src/simutrans/gui/convoy_item.cc000066400000000000000000000015201520675460500220240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "convoy_item.h" #include "../simconvoi.h" #include "../tool/simmenu.h" #include "../world/simworld.h" #include "../utils/cbuffer.h" const char* convoy_scrollitem_t::get_text() const { return cnv->get_name(); } PIXVAL convoy_scrollitem_t::get_color() const { return cnv->get_status_color(); } void convoy_scrollitem_t::set_text(char const* const t) { if( t && t[0] && strcmp( t, cnv->get_name() ) ) { // text changed => call tool cbuffer_t buf; buf.printf("c%u,%s", cnv.get_id(), t ); tool_t *tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tool->set_default_param( buf ); world()->set_tool( tool, cnv->get_owner() ); // since init always returns false, it is safe to delete immediately delete tool; } } simutrans-124.5/src/simutrans/gui/convoy_item.h000066400000000000000000000015731520675460500216760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_CONVOY_ITEM_H #define GUI_CONVOY_ITEM_H #include "components/gui_scrolled_list.h" #include "../convoihandle.h" /** * Container for list entries - consisting of text and color */ class convoy_scrollitem_t : public gui_scrolled_list_t::const_text_scrollitem_t { private: convoihandle_t cnv; public: convoy_scrollitem_t( convoihandle_t c ) : gui_scrolled_list_t::const_text_scrollitem_t( NULL, gfx->palette_lookup(COL_ORANGE) ) { cnv = c; } PIXVAL get_color() const OVERRIDE; convoihandle_t get_convoy() const { return cnv; } char const* get_text() const OVERRIDE; void set_text(char const*) OVERRIDE; bool is_editable() const OVERRIDE { return true; } bool is_valid() const OVERRIDE { return cnv.is_bound(); } // can be used to indicate invalid entries }; #endif simutrans-124.5/src/simutrans/gui/curiosity_edit.cc000066400000000000000000000272131520675460500225370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simworld.h" #include "../tool/simtool.h" #include "../builder/hausbauer.h" #include "../descriptor/ground_desc.h" #include "../descriptor/intro_dates.h" #include "../dataobj/translator.h" #include "../utils/cbuffer.h" #include "curiosity_edit.h" #include "components/gui_label.h" // new tool definition tool_build_house_t curiosity_edit_frame_t::haus_tool=tool_build_house_t(); cbuffer_t curiosity_edit_frame_t::param_str; bool curiosity_edit_frame_t::sortreverse = false; static bool compare_building_desc(const building_desc_t* a, const building_desc_t* b) { int diff = strcmp( a->get_name(), b->get_name() ); return curiosity_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_building_desc_name(const building_desc_t* a, const building_desc_t* b) { int diff = strcmp( translator::translate(a->get_name()), translator::translate(b->get_name()) ); if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return curiosity_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_building_desc_level_pax(const building_desc_t* a, const building_desc_t* b) { int diff = a->get_level() - b->get_level(); if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return curiosity_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_building_desc_level_mail(const building_desc_t* a, const building_desc_t* b) { int diff = a->get_mail_level() - b->get_mail_level(); if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return curiosity_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_building_desc_date_intro(const building_desc_t* a, const building_desc_t* b) { int diff = a->get_intro_year_month() - b->get_intro_year_month(); if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return curiosity_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_building_desc_date_retire(const building_desc_t* a, const building_desc_t* b) { int diff = a->get_retire_year_month() - b->get_retire_year_month(); if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return curiosity_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_building_desc_size(const building_desc_t* a, const building_desc_t* b) { koord a_koord = a->get_size(); koord b_koord = b->get_size(); int diff = a_koord.x * a_koord.y - b_koord.x * b_koord.y; if( diff==0 ) { //same area - sort by side to seperate different shapes diff = a_koord.x - b_koord.x; } if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return curiosity_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } curiosity_edit_frame_t::curiosity_edit_frame_t(player_t* player_) : extend_edit_gui_t(translator::translate("curiosity builder"), player_), building_list(16) { desc = NULL; haus_tool.set_default_param(NULL); haus_tool.cursor = tool_t::general_tool[TOOL_BUILD_HOUSE]->cursor; bt_city_attraction.init( button_t::square_state, "City attraction"); bt_city_attraction.add_listener(this); bt_city_attraction.pressed = true; cont_filter.add_component(&bt_city_attraction,3); bt_land_attraction.init( button_t::square_state, "Land attraction"); bt_land_attraction.add_listener(this); bt_land_attraction.pressed = true; cont_filter.add_component(&bt_land_attraction,3); bt_monuments.init( button_t::square_state, "Monument"); bt_monuments.add_listener(this); cont_filter.add_component(&bt_monuments,3); // add to sorting selection cb_sortedby.new_component(gui_sorting_item_t::BY_LEVEL_PAX); cb_sortedby.new_component(gui_sorting_item_t::BY_LEVEL_MAIL); cb_sortedby.new_component(gui_sorting_item_t::BY_DATE_INTRO); cb_sortedby.new_component(gui_sorting_item_t::BY_DATE_RETIRE); cb_sortedby.new_component(gui_sorting_item_t::BY_SIZE); // rotation gui_aligned_container_t *tbl = cont_options.add_table(2,0); tbl->new_component("Rotation"); tbl->add_component(&cb_rotation); cb_rotation.add_listener(this); cb_rotation.new_component(gui_rotation_item_t::random); cont_options.end_table(); fill_list(); reset_min_windowsize(); } // put item in list according to filter/sorter void curiosity_edit_frame_t::put_item_in_list( const building_desc_t* desc ) { const bool allow_obsolete = bt_obsolete.pressed; const bool use_timeline = bt_timeline.pressed | bt_timeline_custom.pressed; const sint32 month_now = bt_timeline.pressed ? welt->get_current_month() : bt_timeline_custom.pressed ? ni_timeline_year.get_value()*12 + ni_timeline_month.get_value()-1 : 0; const uint8 chosen_climate = get_climate(); const uint8 sortedby = get_sortedby(); sortreverse = sort_order.pressed; if( (!use_timeline || (!desc->is_future(month_now) && (!desc->is_retired(month_now) || allow_obsolete)) ) && ( desc->get_allowed_climate_bits() & chosen_climate) ) { // timeline allows for this, and so does climates setting switch(sortedby) { case gui_sorting_item_t::BY_NAME_TRANSLATED: building_list.insert_ordered( desc, compare_building_desc_name ); break; case gui_sorting_item_t::BY_LEVEL_PAX: building_list.insert_ordered( desc, compare_building_desc_level_pax ); break; case gui_sorting_item_t::BY_LEVEL_MAIL: building_list.insert_ordered( desc, compare_building_desc_level_mail ); break; case gui_sorting_item_t::BY_DATE_INTRO: building_list.insert_ordered( desc, compare_building_desc_date_intro ); break; case gui_sorting_item_t::BY_DATE_RETIRE: building_list.insert_ordered( desc, compare_building_desc_date_retire ); break; case gui_sorting_item_t::BY_SIZE: building_list.insert_ordered( desc, compare_building_desc_size ); break; default: building_list.insert_ordered( desc, compare_building_desc ); } } } // fill the current building_list void curiosity_edit_frame_t::fill_list() { building_list.clear(); if(bt_city_attraction.pressed) { for(building_desc_t const* const desc : *hausbauer_t::get_list(building_desc_t::attraction_city)) { put_item_in_list(desc); } } if(bt_land_attraction.pressed) { for(building_desc_t const* const desc : *hausbauer_t::get_list(building_desc_t::attraction_land)) { put_item_in_list(desc); } } if(bt_monuments.pressed) { for(building_desc_t const* const desc : *hausbauer_t::get_list(building_desc_t::monument)) { put_item_in_list(desc); } } // now build scrolled list scl.clear_elements(); scl.set_selection(-1); for(building_desc_t const* const i : building_list) { // color code for objects: BLACK: normal, YELLOW: consumer only, GREEN: source only PIXVAL color; switch (i->get_type()) { case building_desc_t::attraction_city: color = gfx->palette_lookup(COL_DARK_BLUE+env_t::gui_player_color_dark); break; case building_desc_t::attraction_land: color = gfx->palette_lookup(40 +env_t::gui_player_color_dark); break; default: color = SYSCOL_TEXT; break; } char const* const name = get_sortedby()==gui_sorting_item_t::BY_NAME_OBJECT ? i->get_name() : translator::translate(i->get_name()); scl.new_component(name, color); if (i == desc) { scl.set_selection(scl.get_count()-1); } } // always update current selection (since the tool may depend on it) change_item_info( scl.get_selection() ); } bool curiosity_edit_frame_t::action_triggered( gui_action_creator_t *comp,value_t e) { // only one chain can be shown if( comp==&bt_city_attraction ) { bt_city_attraction.pressed ^= 1; fill_list(); } else if( comp==&bt_land_attraction ) { bt_land_attraction.pressed ^= 1; fill_list(); } else if( comp==&bt_monuments ) { bt_monuments.pressed ^= 1; fill_list(); } else if( comp == &cb_rotation) { change_item_info( scl.get_selection() ); } return extend_edit_gui_t::action_triggered(comp,e); } void curiosity_edit_frame_t::change_item_info(sint32 entry) { if(entry>=0 && entry<(sint32)building_list.get_count()) { const building_desc_t *new_desc = building_list[entry]; if(new_desc!=desc) { buf.clear(); desc = new_desc; if(desc->get_type()==building_desc_t::attraction_city) { buf.printf("%s (%s: %i)",translator::translate( "City attraction" ), translator::translate("Bauzeit"),desc->get_extra()); } else if(desc->get_type()==building_desc_t::attraction_land) { buf.append( translator::translate( "Land attraction" ) ); } else if(desc->get_type()==building_desc_t::monument) { buf.append( translator::translate( "Monument" ) ); } buf.append("\n\n"); buf.append( translator::translate( desc->get_name() ) ); buf.trim(); buf.append("\n\n"); // climates buf.append( translator::translate("allowed climates:\n") ); uint16 cl = desc->get_allowed_climate_bits(); if(cl==0) { buf.append( translator::translate("none") ); buf.append("\n"); } else { for(uint16 i=0; i<=arctic_climate; i++ ) { if(cl & (1<get_level()); if(desc->get_type()==building_desc_t::attraction_land) { // same with passengers buf.printf("%s: %i\n",translator::translate("Postrate"),desc->get_level()); } else { buf.printf("%s: %i\n",translator::translate("Postrate"),desc->get_mail_level()); } buf.printf("%s%u", translator::translate("\nBauzeit von"), desc->get_intro_year_month() / 12); if(desc->get_retire_year_month()!=DEFAULT_RETIRE_YEAR*12) { buf.printf("%s%u", translator::translate("\nBauzeit bis"), desc->get_retire_year_month() / 12); } buf.append("\n"); if (char const* const maker = desc->get_copyright()) { buf.append("\n"); buf.printf(translator::translate("Constructed by %s"), maker); buf.append("\n"); } // reset combobox cb_rotation.clear_elements(); cb_rotation.new_component(gui_rotation_item_t::random); for(uint8 i = 0; iget_all_layouts(); i++) { cb_rotation.new_component(i); } // orientation (255=random) if(desc->get_all_layouts()>1) { cb_rotation.set_selection(0); } else { cb_rotation.set_selection(1); } } uint8 rotation = get_rotation(); uint8 rot = (rotation==255) ? 0 : rotation; building_image.init(desc, rot); // the tools will be always updated, even though the data up there might be still current param_str.clear(); param_str.printf("%i%c%s", bt_climates.pressed, rotation==255 ? '#' : '0'+rotation, desc->get_name() ); haus_tool.set_default_param(param_str); welt->set_tool( &haus_tool, player ); } else { desc = NULL; if(welt->get_tool(player->get_player_nr())==&haus_tool) { welt->set_tool( tool_t::general_tool[TOOL_QUERY], player ); } building_image.init(NULL, 0); cb_rotation.clear_elements(); cb_rotation.new_component(gui_rotation_item_t::random); buf.clear(); } reset_min_windowsize(); } void curiosity_edit_frame_t::draw(scr_coord pos, scr_size size) { // remove constructed monuments from list if(desc && desc->get_type()==building_desc_t::monument && !hausbauer_t::is_valid_monument(desc) ) { change_item_info(0x7FFFFFFF); scl.set_selection(-1); fill_list(); } extend_edit_gui_t::draw(pos,size); } simutrans-124.5/src/simutrans/gui/curiosity_edit.h000066400000000000000000000026451520675460500224030ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_CURIOSITY_EDIT_H #define GUI_CURIOSITY_EDIT_H #include "extend_edit.h" class tool_build_house_t; class building_desc_t; /* * Curiosity (attractions) builder dialog */ class curiosity_edit_frame_t : public extend_edit_gui_t { private: static tool_build_house_t haus_tool; static cbuffer_t param_str; const building_desc_t *desc; vector_tplbuilding_list; button_t bt_city_attraction; button_t bt_land_attraction; button_t bt_monuments; void fill_list() OVERRIDE; void put_item_in_list(const building_desc_t* desc ); void change_item_info( sint32 i ) OVERRIDE; public: curiosity_edit_frame_t(player_t* player); static bool sortreverse; /** * in top-level windows the name is displayed in titlebar * @return the non-translated component name */ const char* get_name() const { return "curiosity builder"; } /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char* get_help_filename() const OVERRIDE { return "curiosity_build.txt"; } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/curiositylist_frame.cc000066400000000000000000000127101520675460500235740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "curiositylist_frame.h" #include "curiositylist_stats.h" #include "components/gui_label.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../simcolor.h" #include "../world/simworld.h" #include "../player/simplay.h" #include "../obj/gebaeude.h" #include "../descriptor/building_desc.h" #include "../utils/unicode.h" char curiositylist_frame_t::name_filter[256]; const char* sort_text[curiositylist::SORT_MODES] = { "hl_btn_sort_name", "Passagierrate" }; class playername_const_scroll_item_t : public gui_scrolled_list_t::const_text_scrollitem_t { public: const uint8 player_nr; playername_const_scroll_item_t( player_t *pl ) : gui_scrolled_list_t::const_text_scrollitem_t( pl->get_name(), gfx->palette_lookup(pl->get_player_color1()+env_t::gui_player_color_dark) ), player_nr(pl->get_player_nr()) { } }; curiositylist_frame_t::curiositylist_frame_t() : gui_frame_t( translator::translate("curlist_title") ), scrolly(gui_scrolled_list_t::windowskin, curiositylist_stats_t::compare) { attraction_count = 0; scrolly.set_checkered(true); scrolly.set_maximize(true); set_table_layout(3,0); new_component("Filter:"); name_filter_input.set_text(name_filter, lengthof(name_filter)); name_filter_input.set_notify_all_changes_delay(0); name_filter_input.add_listener(this); add_component(&name_filter_input); new_component(); filter_by_owner.init(button_t::square_automatic, "Served by"); filter_by_owner.add_listener(this); filter_by_owner.set_tooltip("At least one tile is connected to one stop."); add_component(&filter_by_owner); filterowner.new_component(translator::translate("No player"), SYSCOL_TEXT); for (int i = 0; i < MAX_PLAYER_COUNT; i++) { if (player_t* pl = welt->get_player(i)) { filterowner.new_component(pl); if (pl == welt->get_active_player()) { filterowner.set_selection(filterowner.count_elements() - 1); } } } filterowner.add_listener(this); add_component(&filterowner); new_component(); new_component("hl_txt_sort"); sortedby.set_unsorted(); // do not sort for (size_t i = 0; i < lengthof(sort_text); i++) { sortedby.new_component(translator::translate(sort_text[i]), SYSCOL_TEXT); } sortedby.set_selection(curiositylist_stats_t::sortby); sortedby.add_listener(this); add_component(&sortedby); sorteddir.init(button_t::sortarrow_state, NULL); sorteddir.add_listener(this); sorteddir.pressed = curiositylist_stats_t::sortby; add_component(&sorteddir); add_component(&scrolly,3); fill_list(); set_resizemode(diagonal_resize); scrolly.set_maximize(true); reset_min_windowsize(); } void curiositylist_frame_t::fill_list() { scrolly.clear_elements(); const weighted_vector_tpl& world_attractions = welt->get_attractions(); attraction_count = world_attractions.get_count(); player_t* pl = (filter_by_owner.pressed && filterowner.get_selection() >= 0) ? welt->get_player(((const playername_const_scroll_item_t*)(filterowner.get_selected_item()))->player_nr) : NULL; if (filter_by_owner.pressed && filterowner.get_selection() == 0) { for(gebaeude_t* const geb : world_attractions) { if (geb != NULL && geb->get_first_tile() == geb && geb->get_passagier_level() != 0) { bool add = (name_filter[0] == 0 || utf8caseutf8(translator::translate(geb->get_tile()->get_desc()->get_name()), name_filter)); for (int i = 0; add && i < MAX_PLAYER_COUNT; i++) { if (player_t* pl = welt->get_player(i)) { if (geb->is_within_players_network(pl)) { // already connected add = false; } } } if (add) { scrolly.new_component(geb); } } } } else { for(gebaeude_t* const geb : world_attractions) { if (geb != NULL && geb->get_first_tile() == geb && geb->get_passagier_level() != 0) { if( pl == NULL || geb->is_within_players_network( pl ) ) { curiositylist_stats_t* cs = new curiositylist_stats_t(geb); if( name_filter[0] == 0 || utf8caseutf8(cs->get_text(), name_filter) ) { scrolly.take_component(cs); } else { delete cs; } } } } } scrolly.sort(0); scrolly.set_size(scr_size(get_windowsize().w, scrolly.get_size().h)); } bool curiositylist_frame_t::action_triggered( gui_action_creator_t *comp,value_t v) { if(comp == &sortedby) { curiositylist_stats_t::sortby = (curiositylist::sort_mode_t)v.i; scrolly.sort(0); } else if(comp == &sorteddir) { curiositylist_stats_t::sortreverse = !curiositylist_stats_t::sortreverse; sorteddir.pressed = curiositylist_stats_t::sortreverse; scrolly.sort(0); } else if (comp == &name_filter_input) { fill_list(); } else if (comp == &filterowner) { if( filter_by_owner.pressed ) { fill_list(); } } else if( comp == &filter_by_owner ) { fill_list(); } return true; } void curiositylist_frame_t::rdwr(loadsave_t* file) { scr_size size = get_windowsize(); sint16 sort_mode = curiositylist_stats_t::sortby; size.rdwr(file); scrolly.rdwr(file); file->rdwr_str(name_filter, lengthof(name_filter)); file->rdwr_short(sort_mode); file->rdwr_bool(curiositylist_stats_t::sortreverse); if (file->is_loading()) { curiositylist_stats_t::sortby = (curiositylist::sort_mode_t)sort_mode; fill_list(); set_windowsize(size); } } simutrans-124.5/src/simutrans/gui/curiositylist_frame.h000066400000000000000000000021521520675460500234350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_CURIOSITYLIST_FRAME_H #define GUI_CURIOSITYLIST_FRAME_H #include "simwin.h" #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_scrolled_list.h" #include "components/gui_button.h" #include "components/gui_combobox.h" /** * Curiosity list window */ class curiositylist_frame_t : public gui_frame_t, private action_listener_t { private: gui_combobox_t sortedby; button_t sorteddir; gui_scrolled_list_t scrolly; gui_aligned_container_t list; button_t filter_by_owner; gui_combobox_t filterowner; uint32 attraction_count; static char name_filter[256]; gui_textinput_t name_filter_input; public: curiositylist_frame_t(); void fill_list(); const char *get_help_filename() const OVERRIDE {return "curiositylist_filter.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void map_rotate90( sint16 ) OVERRIDE { fill_list(); } void rdwr(loadsave_t* file) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_curiositylist; } }; #endif simutrans-124.5/src/simutrans/gui/curiositylist_stats.cc000066400000000000000000000115641520675460500236460ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "curiositylist_stats.h" #include "components/gui_image.h" #include "../simtypes.h" #include "../simcolor.h" #include "../world/simworld.h" #include "../display/viewport.h" #include "../tool/simmenu.h" #include "../simhalt.h" #include "../simskin.h" #include "../obj/gebaeude.h" #include "../descriptor/building_desc.h" #include "../descriptor/skin_desc.h" #include "../dataobj/translator.h" #include "../utils/simstring.h" #include "../utils/cbuffer.h" curiositylist::sort_mode_t curiositylist_stats_t::sortby = curiositylist::by_name; bool curiositylist_stats_t::sortreverse = false; static karte_ptr_t welt; bool curiositylist_stats_t::compare(const gui_component_t *aa, const gui_component_t *bb ) { const curiositylist_stats_t* ca = dynamic_cast(aa); const curiositylist_stats_t* cb = dynamic_cast(bb); const gebaeude_t* a = ca->attraction; const gebaeude_t* b = cb->attraction; int cmp = 0; switch (sortby) { default: NOT_REACHED case curiositylist::by_paxlevel: if (a->get_passagier_level() != b->get_passagier_level()) { cmp = a->get_passagier_level() - b->get_passagier_level(); break; } // fall-through case curiositylist::by_name: { const char* a_name = translator::translate(a->get_tile()->get_desc()->get_name()); const char* b_name = translator::translate(b->get_tile()->get_desc()->get_name()); cmp = STRICMP(a_name, b_name); break; } } return sortreverse ? cmp > 0 : cmp < 0; } curiositylist_stats_t::curiositylist_stats_t(gebaeude_t *att) { attraction = att; // pos button set_table_layout(4,1); button_t *b = new_component(); b->set_typ(button_t::posbutton_automatic); b->set_targetpos3d(attraction->get_pos()); // indicator bar add_component(&indicator); indicator.set_max_size(scr_size(D_INDICATOR_WIDTH,D_INDICATOR_HEIGHT)); // city attraction images if (attraction->get_tile()->get_desc()->get_extra() != 0) { new_component(skinverwaltung_t::intown->get_image_id(0), 0, ALIGN_NONE, true); } else { new_component(); } // name gui_label_buf_t *l = new_component(); l->buf().printf("%s (%d)", get_text(), attraction->get_passagier_level()); l->update(); } const char* curiositylist_stats_t::get_text() const { const unsigned char *name = (const unsigned char *)ltrim( translator::translate(attraction->get_tile()->get_desc()->get_name()) ); static char short_name[256]; char* dst = short_name; int cr = 0; for( int j=0; name[j]>0 && j<255 && cr<10; j++ ) { if(name[j]<=' ') { cr++; if( name[j]<32 ) { break; } if (dst != short_name && dst[-1] != ' ') { *dst++ = ' '; } } else { *dst++ = name[j]; } } *dst = '\0'; // now we have a short name ... return short_name; } bool curiositylist_stats_t::is_valid() const { return world()->get_attractions().is_contained(attraction); } /** * Events are notified to GUI components via this method */ bool curiositylist_stats_t::infowin_event(const event_t * ev) { bool swallowed = gui_aligned_container_t::infowin_event(ev); if (!swallowed) { // either open dialog or goto (with control or right click) if (IS_LEFTRELEASE(ev)) { if ((event_get_last_control_shift() ^ tool_t::control_invert) == 2) { world()->get_viewport()->change_world_position(attraction->get_pos()); } else { attraction->show_info(); } return true; } if (IS_RIGHTRELEASE(ev)) { world()->get_viewport()->change_world_position(attraction->get_pos()); return true; } } return swallowed; } /** * Draw the component */ void curiositylist_stats_t::draw(scr_coord offset) { // is connected? => decide on indicatorfarbe (indicator color) PIXVAL indicatorfarbe; bool mail=false; bool pax=false; bool all_crowded=true; bool some_crowded=false; const planquadrat_t *plan = welt->access(attraction->get_pos().get_2d()); const halthandle_t *halt_list = plan->get_haltlist(); for( unsigned h=0; (mail&pax)==0 && hget_haltlist_count(); h++ ) { halthandle_t halt = halt_list[h]; if (halt->get_pax_enabled()) { pax = true; if (halt->get_pax_unhappy() > 40) { some_crowded |= true; } else { all_crowded = false; } } if (halt->get_mail_enabled()) { mail = true; if (halt->get_pax_unhappy() > 40) { some_crowded |= true; } else { all_crowded = false; } } } // now decide on color if(some_crowded) { indicatorfarbe = gfx->palette_lookup(all_crowded ? COL_RED : COL_ORANGE); } else if(pax) { indicatorfarbe = gfx->palette_lookup(mail ? COL_TURQUOISE : COL_DARK_GREEN); } else { indicatorfarbe = gfx->palette_lookup(mail ? COL_BLUE : COL_YELLOW); } indicator.set_color(indicatorfarbe); gui_aligned_container_t::draw(offset); } simutrans-124.5/src/simutrans/gui/curiositylist_stats.h000066400000000000000000000020221520675460500234750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_CURIOSITYLIST_STATS_H #define GUI_CURIOSITYLIST_STATS_H #include "components/gui_aligned_container.h" #include "components/gui_colorbox.h" #include "components/gui_scrolled_list.h" class gebaeude_t; namespace curiositylist { enum sort_mode_t { by_name=0, by_paxlevel, // by_maillevel, SORT_MODES }; }; /** * Where curiosity (attractions) stats are calculated for list dialog */ class curiositylist_stats_t : public gui_aligned_container_t, public gui_scrolled_list_t::scrollitem_t { private: gebaeude_t* attraction; gui_colorbox_t indicator; public: static curiositylist::sort_mode_t sortby; static bool sortreverse; static bool compare(const gui_component_t *a, const gui_component_t *b ); curiositylist_stats_t(gebaeude_t *att); char const* get_text() const OVERRIDE; bool is_valid() const OVERRIDE; bool infowin_event(event_t const*) OVERRIDE; void draw(scr_coord offset) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/depot_frame.cc000066400000000000000000001535621520675460500217740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* * The depot window, where to buy convois */ #include #include #include #include "../simunits.h" #include "../simintr.h" #include "../world/simworld.h" #include "../vehicle/vehicle.h" #include "../simconvoi.h" #include "../obj/depot.h" #include "simwin.h" // #include "../simcolor.h" #include "../simdebug.h" #include "../display/viewport.h" #include "../simline.h" #include "../simlinemgmt.h" #include "../tool/simmenu.h" #include "../simskin.h" #include "../tpl/slist_tpl.h" #include "line_management_gui.h" #include "line_item.h" #include "convoy_item.h" #include "messagebox.h" #include "depot_frame.h" #include "schedule_list.h" #include "components/gui_divider.h" #include "../descriptor/goods_desc.h" #include "../descriptor/intro_dates.h" #include "../builder/vehikelbauer.h" #include "../dataobj/schedule.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../player/simplay.h" #include "../utils/simstring.h" #include "../utils/cbuffer.h" #include "../builder/goods_manager.h" #include "../obj/way/weg.h" #include "../utils/unicode.h" char depot_frame_t::name_filter_value[64] = ""; class depot_convoi_capacity_t : public gui_aligned_container_t { private: gui_label_buf_t labels[3]; gui_image_t images[3]; uint32 total_pax; uint32 total_mail; uint32 total_goods; public: depot_convoi_capacity_t() { set_table_layout(6, 1); set_spacing(scr_size(D_H_SPACE, 0)); total_pax = 0; total_mail = 0; total_goods = 0; images[0].set_image(skinverwaltung_t::passengers->get_image_id(0), true); images[1].set_image(skinverwaltung_t::mail->get_image_id(0), true); images[2].set_image(skinverwaltung_t::goods->get_image_id(0), true); for(int i=0; i<3; i++) { add_component(&labels[i]); add_component(&images[i]); } } void set_totals(uint32 pax, uint32 mail, uint32 goods) { total_pax = pax; total_mail = mail; total_goods = goods; update(); } void update() { labels[0].buf().printf("%s %d", translator::translate("Capacity:"), total_pax); labels[1].buf().printf("%d", total_mail); labels[2].buf().printf("%d", total_goods); for(int i=0; i<3; i++) { labels[i].update(); } } }; class gui_aligned_container_zero_width_t : public gui_aligned_container_t { public: scr_size get_min_size() const OVERRIDE { return scr_size(0, gui_aligned_container_t::get_min_size().h); } }; // Labels with dynamic buffers, index into labels[] enum { // convoi properties LB_CNV_COUNT, LB_CNV_COST, LB_CNV_VALUE, LB_CNV_POWER, LB_CNV_WEIGHT, LB_CNV_SPEED, // vehicle properties LB_VEH_NAME, LB_VEH_COST, LB_VEH_WEIGHT, LB_VEH_CAPACITY, LB_VEH_DATE, LB_VEH_SPEED, LB_VEH_AUTHOR, LB_VEH_POWER, LB_VEH_VALUE, LB_VEH_LOADINGTIME, LB_MAX, LB_CNV_ALL = LB_VEH_NAME }; static int sort_by_action; bool depot_frame_t::show_retired_vehicles = false; bool depot_frame_t::show_all = true; depot_frame_t::depot_frame_t(depot_t* depot) : gui_frame_t("", NULL), depot(depot), icnv(-1), convoi(&convoi_pics), scrolly_convoi(&cont_convoi, true, false), pas(&pas_vec), electrics(&electrics_vec), loks(&loks_vec), waggons(&waggons_vec), scrolly_pas(&pas), scrolly_electrics(&electrics), scrolly_loks(&loks), scrolly_waggons(&waggons), line_selector(line_scrollitem_t::compare) { old_vehicle_count = 0; if (depot) { old_vehicle_count = depot->get_vehicle_list().get_count() + 1; old_veh_type = NULL; init(depot); } } void depot_frame_t::init(depot_t *dep) { depot = dep; set_name(translator::translate(depot->get_name())); set_owner(depot->get_owner()); icnv = depot->convoi_count()-1; veh_action = va_append; last_selected_line = depot->get_last_selected_line(); no_schedule_text = translator::translate(""); clear_schedule_text = translator::translate(""); unique_schedule_text = translator::translate(""); new_line_text = translator::translate(""); line_seperator = translator::translate("--------------------------------"); new_convoy_text = translator::translate("new convoi"); promote_to_line_text = translator::translate(""); // allocate labels for(int i=0; iget_image_id(0), true); img_bolt.set_rigid(true); // second row // goto line button line_button.set_typ(button_t::posbutton); line_button.set_targetpos3d(koord3d::invalid); line_button.add_listener(this); add_component(&line_button); new_component("Serves Line:", SYSCOL_TEXT, gui_label_t::left); /* * [SELECT ROUTE]: */ line_selector.add_listener(this); line_selector.set_wrapping(false); add_component(&line_selector); new_component(); end_table(); /* * [CONVOI] */ cont_convoi.set_table_layout(1,0); cont_convoi.set_margin(scr_size(0,0), scr_size(0,0)); cont_convoi.set_spacing(scr_size(0,0)); cont_convoi.add_component(&convoi); convoi.set_max_rows(1); sb_convoi_length.set_base( depot->get_max_convoi_length() * CARUNITS_PER_TILE / 2 - 1); convoi_length_ok_sb = 0; convoi_length_slower_sb = 0; convoi_length_too_slow_sb = 0; convoi_tile_length_sb = 0; new_vehicle_length_sb = 0; if( depot->get_max_convoi_length() > 1 ) { // no convoy length bar for ships or aircraft sb_convoi_length.add_color_value(&convoi_tile_length_sb, gfx->palette_lookup(COL_BROWN)); sb_convoi_length.add_color_value(&new_vehicle_length_sb, gfx->palette_lookup(COL_DARK_GREEN)); sb_convoi_length.add_color_value(&convoi_length_ok_sb, gfx->palette_lookup(COL_GREEN)); sb_convoi_length.add_color_value(&convoi_length_slower_sb, gfx->palette_lookup(COL_ORANGE)); sb_convoi_length.add_color_value(&convoi_length_too_slow_sb, gfx->palette_lookup(COL_RED)); cont_convoi.add_component(&sb_convoi_length); } add_component(&scrolly_convoi); { gui_aligned_container_t* t = add_table(2, 4); t->set_force_equal_columns(true); t->set_spacing(scr_size(D_H_SPACE, 0)); add_component(labels[LB_CNV_COUNT]); cont_convoi_capacity = new_component(); add_component(labels[LB_CNV_COST]); add_component(labels[LB_CNV_VALUE]); add_component(labels[LB_CNV_POWER]); add_component(labels[LB_CNV_WEIGHT]); add_component(labels[LB_CNV_SPEED], 2); end_table(); } /* * [ACTIONS] */ add_table(4,0)->set_force_equal_columns(true); bt_start.init(button_t::roundbox | button_t::flexible, "Start"); bt_start.add_listener(this); bt_start.set_tooltip("Start the selected vehicle(s)"); add_component(&bt_start); bt_schedule.init(button_t::roundbox | button_t::flexible, "Fahrplan"); bt_schedule.add_listener(this); bt_schedule.set_tooltip("Give the selected vehicle(s) an individual schedule"); // translated to "Edit the selected vehicle(s) individual schedule or assigned line" add_component(&bt_schedule); bt_copy_convoi.init(button_t::roundbox | button_t::flexible, "Copy Convoi"); bt_copy_convoi.add_listener(this); bt_copy_convoi.set_tooltip("Copy the selected convoi and its schedule or line"); add_component(&bt_copy_convoi); bt_sell.init(button_t::roundbox | button_t::flexible, "verkaufen"); bt_sell.add_listener(this); bt_sell.set_tooltip("Sell the selected vehicle(s)"); add_component(&bt_sell); end_table(); /* * [PANEL] */ add_component(&tabs); /* * [BOTTOM] */ new_component(); cont_veh_action = add_table(4,0); { // put columns 1-3 in extra container to force correct button width in 4th column gui_aligned_container_t *cont_3cols = new_component_span(3,0,3); cont_3cols->add_component(&lb_convoi_count); cont_3cols->new_component(); cont_3cols->new_component("Fahrzeuge:", SYSCOL_TEXT, gui_label_t::right); } bt_veh_action.set_typ(button_t::roundbox | button_t::flexible); bt_veh_action.add_listener(this); bt_veh_action.set_tooltip("Choose operation executed on clicking stored/new vehicles"); add_component(&bt_veh_action); { // put columns 1-4 in extra container to force correct button width in last column gui_aligned_container_t *cont_4cols = new_component_span(4,0,3); bt_obsolete.init(button_t::square_state, "Show obsolete"); bt_obsolete.pressed = show_retired_vehicles; if( welt->get_settings().get_allow_buying_obsolete_vehicles() ) { bt_obsolete.add_listener(this); bt_obsolete.set_tooltip("Show also vehicles no longer in production."); cont_4cols->add_component(&bt_obsolete); } else { cont_4cols->new_component(); } cont_4cols->new_component("Filter:", SYSCOL_TEXT, gui_label_t::right); vehicle_filter.add_listener(this); vehicle_filter.set_force_selection(true); cont_4cols->add_component(&vehicle_filter); cont_4cols->new_component("Search:", SYSCOL_TEXT, gui_label_t::right); } name_filter_input.set_text(name_filter_value, 60); add_component(&name_filter_input); name_filter_input.add_listener(this); { // put columns 1-3 in extra container to force correct button width in 4th column gui_aligned_container_t *cont_3cols = new_component_span(3,0,3); bt_show_all.init(button_t::square_state, "Show all"); bt_show_all.add_listener(this); bt_show_all.set_tooltip("Show also vehicles that do not match for current action."); bt_show_all.pressed = show_all; cont_3cols->add_component(&bt_show_all); cont_3cols->new_component(); cont_3cols->new_component("Sort by:", SYSCOL_TEXT, gui_label_t::right); } sort_by.add_listener(this); add_component(&sort_by); end_table(); new_component(); /* * [VEHICLE] */ // special container with min-width == zero // (prevents mouse-over-events to spoil the layout and force a large min-width) cont_vehicle_labels = new gui_aligned_container_zero_width_t(); cont_vehicle_labels->set_table_layout(2,0); cont_vehicle_labels->set_force_equal_columns(true); cont_vehicle_labels->set_spacing(scr_size(D_H_SPACE, 0)); cont_vehicle_labels->add_component(labels[LB_VEH_NAME],2); cont_vehicle_labels->add_component(labels[LB_VEH_COST]); cont_vehicle_labels->add_component(labels[LB_VEH_WEIGHT]); cont_vehicle_labels->add_component(labels[LB_VEH_CAPACITY]); cont_vehicle_labels->add_component(labels[LB_VEH_LOADINGTIME]); cont_vehicle_labels->add_component(labels[LB_VEH_SPEED]); cont_vehicle_labels->add_component(labels[LB_VEH_DATE]); cont_vehicle_labels->add_component(labels[LB_VEH_POWER]); cont_vehicle_labels->add_component(labels[LB_VEH_VALUE]); cont_vehicle_labels->add_component(labels[LB_VEH_AUTHOR]); add_component(cont_vehicle_labels); /* * [END OF WINDOW] */ // initialize image lists, scrollies, speedbar { /* * These parameter are adjusted to resolution. * - Some extra space looks nicer. */ scr_coord placement; scr_coord grid; grid.x = depot->get_x_grid() * gfx->get_base_tile_raster_width() / 64 + 4; grid.y = depot->get_y_grid() * gfx->get_base_tile_raster_width() / 64 + 6; placement.x = depot->get_x_placement() * gfx->get_base_tile_raster_width() / 64 + 2; placement.y = depot->get_y_placement() * gfx->get_base_tile_raster_width() / 64 + 2; scr_coord_val grid_dx = depot->get_x_grid() * gfx->get_base_tile_raster_width() / 64 / 2; scr_coord_val placement_dx = depot->get_x_grid() * gfx->get_base_tile_raster_width() / 64 / 4; sb_convoi_length.set_width(depot->get_max_convoi_length() * (grid.x - grid_dx)); gui_image_list_t* ilists[] = {&convoi, &pas, &electrics, &loks, &waggons}; for(uint32 i = 0; iset_grid(scr_coord(grid.x - grid_dx, grid.y)); il->set_placement(scr_coord(placement.x - placement_dx, placement.y)); il->set_player_nr(depot->get_owner_nr()); il->add_listener(this); // only convoi list gets overlapping images grid_dx = 0; placement_dx = 0; } gui_scrollpane_t* scrollies[] = {&scrolly_convoi, &scrolly_pas, &scrolly_electrics, &scrolly_loks, &scrolly_waggons}; for(uint32 i = 0; iset_scrollbar_mode( scrollbar_t::show_disabled ); scrolly->set_size_corner(false); } } // update all the elements build_vehicle_lists(); update_data(); reset_min_windowsize(); set_windowsize(get_min_windowsize()); set_resizemode( diagonal_resize ); depot->clear_command_pending(); } // free memory: all the image_data_t depot_frame_t::~depot_frame_t() { clear_ptr_vector(pas_vec); clear_ptr_vector(electrics_vec); clear_ptr_vector(loks_vec); clear_ptr_vector(waggons_vec); } // returns position of depot on the map koord3d depot_frame_t::get_weltpos(bool) { return depot->get_pos(); } bool depot_frame_t::is_weltpos() { return ( welt->get_viewport()->is_on_center( get_weltpos(false) ) ); } void depot_frame_t::set_windowsize( scr_size size ) { // set width of image-lists gui_image_list_t* ilists[] = {&pas, &electrics, &loks, &waggons}; for(uint32 i = 0; iset_max_width(size.w); } sb_convoi_length.set_size(scr_size(size.w - D_MARGINS_X, D_INDICATOR_HEIGHT)); gui_frame_t::set_windowsize(size); } void depot_frame_t::activate_convoi( convoihandle_t c ) { icnv = -1; // deselect for( uint i = 0; i < depot->convoi_count(); i++ ) { if( c == depot->get_convoi(i) ) { icnv = i; break; } } build_vehicle_lists(); } // true if already stored here bool depot_frame_t::is_in_vehicle_list(const vehicle_desc_t *info) { for(vehicle_t* const v : depot->get_vehicle_list()) { if( v->get_desc() == info ) { return true; } } return false; } // add a single vehicle (helper function) void depot_frame_t::add_to_vehicle_list(const vehicle_desc_t *info) { // Check if vehicle should be filtered const goods_desc_t *freight = info->get_freight_type(); // Only filter when required and never filter engines if (depot->selected_filter > 0 && info->get_capacity() > 0) { if (depot->selected_filter == VEHICLE_FILTER_RELEVANT) { if(freight->get_catg_index() >= 3) { bool found = false; for(goods_desc_t const* const i : welt->get_goods_list()) { if (freight->get_catg_index() == i->get_catg_index()) { found = true; break; } } // If no current goods can be transported by this vehicle, don't display it if (!found) return; } } else if (depot->selected_filter > VEHICLE_FILTER_RELEVANT) { // Filter on specific selected good uint32 goods_index = depot->selected_filter - VEHICLE_FILTER_GOODS_OFFSET; if (goods_index < welt->get_goods_list().get_count()) { const goods_desc_t *selected_good = welt->get_goods_list()[goods_index]; if (freight->get_catg_index() != selected_good->get_catg_index()) { return; // This vehicle can't transport the selected good } } } } gui_image_list_t::image_data_t* img_data = new gui_image_list_t::image_data_t(info->get_name(), info->get_base_image()); if( info->get_engine_type() == vehicle_desc_t::electric && (info->get_freight_type()==goods_manager_t::passengers || info->get_freight_type()==goods_manager_t::mail) ) { electrics_vec.append(img_data); } // since they come "pre-sorted" from the vehikelbauer, we have to do nothing to keep them sorted else if(info->get_freight_type() == goods_manager_t::passengers || info->get_freight_type() == goods_manager_t::mail) { pas_vec.append(img_data); } else if(info->get_power() > 0 || info->get_capacity()==0) { loks_vec.append(img_data); } else { waggons_vec.append(img_data); } // add reference to map vehicle_map.set(info, img_data); } // add all current vehicles void depot_frame_t::build_vehicle_lists() { if (depot->get_vehicle_type().empty()) { // there are tracks etc. but no vehicles => do nothing // at least initialize some data update_data(); update_tabs(); return; } const int month_now = welt->get_timeline_year_month(); // free vectors clear_ptr_vector(pas_vec); clear_ptr_vector(electrics_vec); clear_ptr_vector(loks_vec); clear_ptr_vector(waggons_vec); // clear map vehicle_map.clear(); // we do not allow to built electric vehicle in a depot without electrification const waytype_t wt = depot->get_waytype(); const weg_t *w = welt->lookup(depot->get_pos())->get_weg(wt!=tram_wt ? wt : track_wt); const bool weg_electrified = w ? w->is_electrified() : false; img_bolt.set_visible(weg_electrified); sort_by_action = depot->selected_sort_by; vector_tpl typ_list; if(!show_all && veh_action==va_sell) { // show only sellable vehicles for(vehicle_t* const v : depot->get_vehicle_list()) { vehicle_desc_t const* const d = v->get_desc(); typ_list.append(d); } } else { slist_tpl const& tmp_list = depot->get_vehicle_type(sort_by_action); for(slist_tpl::const_iterator itr = tmp_list.begin(); itr != tmp_list.end(); ++itr) { typ_list.append(*itr); } } // use this to show only sellable vehicles if(!show_all && veh_action==va_sell) { // just list the one to sell for(vehicle_desc_t const* const info : typ_list) { if (vehicle_map.get(info)) continue; add_to_vehicle_list(info); } } else { // list only matching ones for(vehicle_desc_t const* const info : typ_list) { const vehicle_desc_t *veh = NULL; convoihandle_t cnv = depot->get_convoi(icnv); if(cnv.is_bound() && cnv->get_vehicle_count()>0) { veh = (veh_action == va_insert ? cnv->front() : cnv->back())->get_desc(); } // current vehicle if( is_in_vehicle_list(info) || ((weg_electrified || info->get_engine_type()!=vehicle_desc_t::electric) && ((!info->is_future(month_now)) && (show_retired_vehicles || (!info->is_retired(month_now)) ) ) )) { // check, if allowed bool append = true; if(!show_all) { if(veh_action == va_insert) { append = info->can_lead(veh) && (veh==NULL || veh->can_follow(info)); } else if(veh_action == va_append) { append = info->can_follow(veh) && (veh==NULL || veh->can_lead(info)); } } if(append) { // name filter. Try to check both object name and translation name (case sensitive though!) if( name_filter_value[0]==0 || (utf8caseutf8(info->get_name(), name_filter_value) || utf8caseutf8(translator::translate(info->get_name()), name_filter_value)) ) { add_to_vehicle_list( info ); } } } } } DBG_DEBUG("depot_frame_t::build_vehicle_lists()","finally %i passenger vehicle, %i engines, %i good wagons",pas_vec.get_count(),loks_vec.get_count(),waggons_vec.get_count()); update_data(); update_tabs(); } static void get_line_list(const depot_t* depot, vector_tpl* lines) { depot->get_owner()->simlinemgmt.get_lines(depot->get_line_type(), lines); } void depot_frame_t::update_data() { static const char *txt_veh_action[3] = { "anhaengen", "voranstellen", "verkaufen" }; // change green into blue for retired vehicles const int month_now = welt->get_timeline_year_month(); bt_veh_action.set_text(txt_veh_action[veh_action]); cbuffer_t &txt_convois = lb_convois.buf(); switch( depot->convoi_count() ) { case 0: { txt_convois.append( translator::translate("no convois") ); break; } case 1: { if( icnv == -1 ) { txt_convois.append( translator::translate("1 convoi") ); } else { txt_convois.printf( translator::translate("convoi %d of %d"), icnv + 1, depot->convoi_count() ); } break; } default: { if( icnv == -1 ) { txt_convois.printf( translator::translate("%d convois"), depot->convoi_count() ); } else { txt_convois.printf( translator::translate("convoi %d of %d"), icnv + 1, depot->convoi_count() ); } break; } } lb_convois.update(); /* * Reset counts and check for valid vehicles */ convoihandle_t cnv = depot->get_convoi( icnv ); // update convoy selector convoy_selector.clear_elements(); convoy_selector.new_component( new_convoy_text, SYSCOL_TEXT ) ; convoy_selector.set_selection(0); // check all matching convoys for(convoihandle_t const c : depot->get_convoy_list()) { convoy_selector.new_component(c) ; if( cnv.is_bound() && c == cnv ) { convoy_selector.set_selection( convoy_selector.count_elements() - 1 ); } } const vehicle_desc_t *veh = NULL; scr_size old_convoi_size = convoi.get_min_size(); clear_ptr_vector( convoi_pics ); if( cnv.is_bound() && cnv->get_vehicle_count() > 0 ) { for( unsigned i=0; i < cnv->get_vehicle_count(); i++ ) { // just make sure, there is this vehicle also here! const vehicle_desc_t *info=cnv->get_vehicle(i)->get_desc(); if( vehicle_map.get( info ) == NULL ) { add_to_vehicle_list( info ); } gui_image_list_t::image_data_t* img_data = new gui_image_list_t::image_data_t(info->get_name(), info->get_base_image()); convoi_pics.append(img_data); } /* color bars for current convoi: */ convoi_pics[0]->lcolor = gfx->palette_lookup(cnv->front()->get_desc()->can_follow(NULL) ? COL_GREEN : COL_YELLOW); { unsigned i; for( i = 1; i < cnv->get_vehicle_count(); i++ ) { convoi_pics[i - 1]->rcolor = gfx->palette_lookup(cnv->get_vehicle(i-1)->get_desc()->can_lead(cnv->get_vehicle(i)->get_desc()) ? COL_GREEN : COL_RED); convoi_pics[i]->lcolor = gfx->palette_lookup(cnv->get_vehicle(i)->get_desc()->can_follow(cnv->get_vehicle(i-1)->get_desc()) ? COL_GREEN : COL_RED); } convoi_pics[i - 1]->rcolor = gfx->palette_lookup(cnv->get_vehicle(i-1)->get_desc()->can_lead(NULL) ? COL_GREEN : COL_YELLOW); } // change green into blue for vehicles that are not available for( unsigned i = 0; i < cnv->get_vehicle_count(); i++ ) { if( !cnv->get_vehicle(i)->get_desc()->is_available(month_now) ) { if( convoi_pics[i]->lcolor == gfx->palette_lookup(COL_GREEN) ) { convoi_pics[i]->lcolor = gui_theme_t::gui_color_obsolete; } if( convoi_pics[i]->rcolor == gfx->palette_lookup(COL_GREEN) ) { convoi_pics[i]->rcolor = gui_theme_t::gui_color_obsolete; } } } veh = (veh_action == va_insert ? cnv->front() : cnv->back())->get_desc(); } for(auto const& i : vehicle_map) { vehicle_desc_t const* const info = i.key; gui_image_list_t::image_data_t& img = *i.value; const PIXVAL ok_color = info->is_available(month_now) ? gfx->palette_lookup(COL_GREEN) : gui_theme_t::gui_color_obsolete; img.count = 0; img.lcolor = ok_color; img.rcolor = ok_color; /* * color bars for current convoi: * green/green okay to append/insert * red/red cannot be appended/inserted * green/yellow append okay, cannot be end of train * yellow/green insert okay, cannot be start of train */ if(veh_action == va_insert) { if(!info->can_lead(veh) || (veh && !veh->can_follow(info))) { img.lcolor = gfx->palette_lookup(COL_RED); img.rcolor = gfx->palette_lookup(COL_RED); } else if(!info->can_follow(NULL)) { img.lcolor = gfx->palette_lookup(COL_YELLOW); } } else if(veh_action == va_append) { if(!info->can_follow(veh) || (veh && !veh->can_lead(info))) { img.lcolor = gfx->palette_lookup(COL_RED); img.rcolor = gfx->palette_lookup(COL_RED); } else if(!info->can_lead(NULL)) { img.rcolor = gfx->palette_lookup(COL_YELLOW); } } else if( veh_action == va_sell ) { img.lcolor = gfx->palette_lookup(COL_RED); img.rcolor = gfx->palette_lookup(COL_RED); } } for(vehicle_t* const v : depot->get_vehicle_list()) { // can fail, if currently not visible if (gui_image_list_t::image_data_t* const imgdat = vehicle_map.get(v->get_desc())) { imgdat->count++; if(veh_action == va_sell) { imgdat->lcolor = gfx->palette_lookup(COL_GREEN); imgdat->rcolor = gfx->palette_lookup(COL_GREEN); } } } // update the line selector line_selector.clear_elements(); if( !last_selected_line.is_bound() ) { // new line may have a valid line now last_selected_line = selected_line; // if still nothing, resort to line management dialoge if( !last_selected_line.is_bound() ) { // try last specific line last_selected_line = schedule_list_gui_t::selected_line[ depot->get_owner_nr() ][ depot->get_line_type() ]; } if( !last_selected_line.is_bound() ) { // try last general line last_selected_line = schedule_list_gui_t::selected_line[ depot->get_owner_nr() ][ 0 ]; if( last_selected_line.is_bound() && last_selected_line->get_linetype() != depot->get_line_type() ) { last_selected_line = linehandle_t(); } } } if( cnv.is_bound() && cnv->get_schedule() && !cnv->get_schedule()->empty() ) { if( cnv->get_line().is_bound() ) { line_selector.new_component( clear_schedule_text, SYSCOL_TEXT ) ; line_selector.new_component( new_line_text, SYSCOL_TEXT ) ; } else { line_selector.new_component( unique_schedule_text, SYSCOL_TEXT ) ; line_selector.new_component( promote_to_line_text, SYSCOL_TEXT ) ; } } else { line_selector.new_component( no_schedule_text, SYSCOL_TEXT ) ; line_selector.new_component( new_line_text, SYSCOL_TEXT ) ; } if( last_selected_line.is_bound() ) { line_selector.new_component( last_selected_line ) ; } line_selector.new_component( line_seperator, SYSCOL_TEXT ) ; // check all matching lines if( cnv.is_bound() ) { selected_line = cnv->get_line(); } vector_tpl lines; get_line_list(depot, &lines); // select "create new schedule" line_selector.set_selection( 0 ); for(linehandle_t const line : lines ) { line_selector.new_component(line) ; if( selected_line.is_bound() && selected_line == line ) { line_selector.set_selection( line_selector.count_elements() - 1 ); } } if( line_selector.get_selection() == 0 ) { // no line selected selected_line = linehandle_t(); } line_selector.sort( last_selected_line.is_bound()+3 ); // Update vehicle filter vehicle_filter.clear_elements(); vehicle_filter.new_component(translator::translate("All"), SYSCOL_TEXT); vehicle_filter.new_component(translator::translate("Relevant"), SYSCOL_TEXT); for(goods_desc_t const* const i : welt->get_goods_list()) { vehicle_filter.new_component(translator::translate(i->get_name()), SYSCOL_TEXT); } if( depot->selected_filter > (sint32)vehicle_filter.count_elements() ) { depot->selected_filter = VEHICLE_FILTER_RELEVANT; } vehicle_filter.set_selection(depot->selected_filter); vehicle_filter.set_size(vehicle_filter.get_size()); sort_by.clear_elements(); for(int i = 0; i < vehicle_builder_t::sb_length; i++) { sort_by.new_component(translator::translate(vehicle_builder_t::vehicle_sort_by[i]), SYSCOL_TEXT); } sort_by.set_size(sort_by.get_size()); if( depot->selected_sort_by > (sint32)sort_by.count_elements() ) { depot->selected_sort_by = vehicle_builder_t::sb_name; } sort_by.set_selection(depot->selected_sort_by); // finally: update text uint32 total_pax = 0; uint32 total_mail = 0; uint32 total_goods = 0; uint64 total_power = 0; uint32 total_empty_weight = 0; uint32 total_selected_weight = 0; uint32 total_max_weight = 0; uint32 total_min_weight = 0; bool use_sel_weight = true; if( cnv.is_bound() && cnv->get_vehicle_count() > 0 ) { { uint8 selected_good_index = 0; if( depot->selected_filter > VEHICLE_FILTER_RELEVANT ) { // Filter is set to specific good const uint32 goods_index = depot->selected_filter - VEHICLE_FILTER_GOODS_OFFSET; if( goods_index < welt->get_goods_list().get_count() ) { selected_good_index = welt->get_goods_list()[goods_index]->get_index(); } } for( unsigned i = 0; i < cnv->get_vehicle_count(); i++ ) { const vehicle_desc_t *desc = cnv->get_vehicle(i)->get_desc(); total_power += desc->get_power()*desc->get_gear(); uint32 sel_weight = 0; // actual weight using vehicle filter selected good to fill uint32 max_weight = 0; uint32 min_weight = 100000; bool sel_found = false; for( uint32 j=0; jget_freight_type()->get_catg_index() == ware->get_catg_index() ) { max_weight = max(max_weight, (uint32)ware->get_weight_per_unit()); min_weight = min(min_weight, (uint32)ware->get_weight_per_unit()); // find number of goods in in this category. TODO: gotta be a better way... uint8 catg_count = 0; for(goods_desc_t const* const i : welt->get_goods_list()) { if( ware->get_catg_index() == i->get_catg_index() ) { catg_count++; } } if( ware->get_index() == selected_good_index || catg_count < 2 ) { sel_found = true; sel_weight = ware->get_weight_per_unit(); } } } if( !sel_found ) { // vehicle carries more than one good, but not the selected one use_sel_weight = false; } total_empty_weight += desc->get_weight(); total_selected_weight += desc->get_weight() + sel_weight * desc->get_capacity(); total_max_weight += desc->get_weight() + max_weight * desc->get_capacity(); total_min_weight += desc->get_weight() + min_weight * desc->get_capacity(); const goods_desc_t* const ware = desc->get_freight_type(); switch( ware->get_catg_index() ) { case goods_manager_t::INDEX_PAS: { total_pax += desc->get_capacity(); break; } case goods_manager_t::INDEX_MAIL: { total_mail += desc->get_capacity(); break; } default: { total_goods += desc->get_capacity(); break; } } } sint32 empty_kmh, sel_kmh, max_kmh, min_kmh; if( cnv->front()->get_waytype() == air_wt ) { // flying aircraft have 0 friction --> speed not limited by power, so just use top_speed empty_kmh = sel_kmh = max_kmh = min_kmh = speed_to_kmh( cnv->get_min_top_speed() ); } else { empty_kmh = speed_to_kmh(convoi_t::calc_max_speed(total_power, total_empty_weight, cnv->get_min_top_speed())); sel_kmh = speed_to_kmh(convoi_t::calc_max_speed(total_power, total_selected_weight, cnv->get_min_top_speed())); max_kmh = speed_to_kmh(convoi_t::calc_max_speed(total_power, total_min_weight, cnv->get_min_top_speed())); min_kmh = speed_to_kmh(convoi_t::calc_max_speed(total_power, total_max_weight, cnv->get_min_top_speed())); } const sint32 convoi_length = (cnv->get_vehicle_count()) * CARUNITS_PER_TILE / 2 - 1; convoi_tile_length_sb = convoi_length + (cnv->get_tile_length() * CARUNITS_PER_TILE - cnv->get_length()); cbuffer_t& txt_convoi_count = labels[LB_CNV_COUNT]->buf(); if( cnv->get_vehicle_count()>1 ) { txt_convoi_count.printf( translator::translate("%i car(s),"), cnv->get_vehicle_count() ); } txt_convoi_count.append( translator::translate("Station tiles:") ); txt_convoi_count.append( (double)cnv->get_tile_length(), 0 ); cbuffer_t& txt_convoi_speed = labels[LB_CNV_SPEED]->buf(); if( empty_kmh > 3 && empty_kmh != (use_sel_weight ? sel_kmh : min_kmh) ) { convoi_length_ok_sb = 0; if( max_kmh != min_kmh && !use_sel_weight ) { txt_convoi_speed.printf("%s %d km/h, %d-%d km/h %s", translator::translate("Max. speed:"), empty_kmh, min_kmh, max_kmh, translator::translate("loaded") ); if( max_kmh != empty_kmh ) { // lightest good in category slows convoi => too_slow convoi_length_slower_sb = 0; convoi_length_too_slow_sb = convoi_length; } else { // heaviest good in category slows convoi, but not lightest => slower convoi_length_slower_sb = convoi_length; convoi_length_too_slow_sb = 0; } } else { // all goods possible have same weight slowing convoi => too_slow txt_convoi_speed.printf("%s %d km/h, %d km/h %s", translator::translate("Max. speed:"), empty_kmh, use_sel_weight ? sel_kmh : min_kmh, translator::translate("loaded") ); convoi_length_slower_sb = 0; convoi_length_too_slow_sb = convoi_length; } } else { txt_convoi_speed.printf("%s %d km/h", translator::translate("Max. speed:"), empty_kmh ); convoi_length_slower_sb = 0; if( empty_kmh > 3) { // convoi is same acceptable speed loaded or empty => ok convoi_length_ok_sb = convoi_length; convoi_length_too_slow_sb = 0; } else { // convoi way too slow => too_slow convoi_length_ok_sb = 0; convoi_length_too_slow_sb = convoi_length; } } { char buf[128]; cbuffer_t& txt_convoi_value = labels[LB_CNV_VALUE]->buf(); money_to_string( buf, cnv->calc_restwert() / 100.0, false ); txt_convoi_value.printf("%s %8s", translator::translate("Restwert:"), buf ); cbuffer_t& txt_convoi_cost = labels[LB_CNV_COST]->buf(); if( sint64 fix_cost = cnv->get_fixed_cost() ) { money_to_string( buf, (double)cnv->get_purchase_cost() / 100.0, false ); txt_convoi_cost.printf( translator::translate("Cost: %8s (%.2f$/km %.2f$/m)\n"), buf, (double)cnv->get_running_cost()/100.0, (double)fix_cost/100.0 ); } else { money_to_string( buf, cnv->get_purchase_cost() / 100.0, false ); txt_convoi_cost.printf( translator::translate("Cost: %8s (%.2f$/km)\n"), buf, (double)cnv->get_running_cost() / 100.0 ); } } cbuffer_t& txt_convoi_power = labels[LB_CNV_POWER]->buf(); txt_convoi_power.printf( translator::translate("Power: %4d kW\n"), cnv->get_sum_power() ); cbuffer_t& txt_convoi_weight = labels[LB_CNV_WEIGHT]->buf(); if( total_empty_weight != (use_sel_weight ? total_selected_weight : total_max_weight) ) { if( total_min_weight != total_max_weight && !use_sel_weight ) { txt_convoi_weight.printf("%s %.1ft, %.1f-%.1ft", translator::translate("Weight:"), total_empty_weight / 1000.0, total_min_weight / 1000.0, total_max_weight / 1000.0 ); } else { txt_convoi_weight.printf("%s %.1ft, %.1ft", translator::translate("Weight:"), total_empty_weight / 1000.0, (use_sel_weight ? total_selected_weight : total_max_weight) / 1000.0 ); } } else { txt_convoi_weight.printf("%s %.1ft", translator::translate("Weight:"), total_empty_weight / 1000.0 ); } sb_convoi_length.set_visible(true); cont_convoi_capacity->set_totals( total_pax, total_mail, total_goods ); cont_convoi_capacity->set_visible(true); } } else { sb_convoi_length.set_visible(false); cont_convoi_capacity->set_visible(false); for(uint32 i=0; ibuf(); } labels[LB_CNV_COUNT]->buf().append(translator::translate("Keine Einzelfahrzeuge im Depot")); } for(uint32 i=0; iupdate(); } // update window if convoi container size changed if (old_convoi_size.w != convoi.get_min_size().w) { resize(scr_size(0,0)); } } sint64 depot_frame_t::calc_restwert(const vehicle_desc_t *veh_type) { sint64 wert = 0; for(vehicle_t* const v : depot->get_vehicle_list()) { if( v->get_desc() == veh_type ) { wert += v->calc_sale_value(); } } return wert; } void depot_frame_t::image_from_storage_list(gui_image_list_t::image_data_t *image_data) { if( image_data->lcolor != gfx->palette_lookup(COL_RED) && image_data->rcolor != gfx->palette_lookup(COL_RED) ) { if( veh_action == va_sell ) { depot->call_depot_tool('s', convoihandle_t(), image_data->text ); } else { convoihandle_t cnv = depot->get_convoi( icnv ); if( !cnv.is_bound() && !depot->get_owner()->is_locked() ) { // adding new convoi, block depot actions until command executed // otherwise in multiplayer it's likely multiple convois get created // rather than one new convoi with multiple vehicles depot->set_command_pending(); } depot->call_depot_tool( veh_action == va_insert ? 'i' : 'a', cnv, image_data->text ); } } } void depot_frame_t::image_from_convoi_list(uint nr, bool to_end) { const convoihandle_t cnv = depot->get_convoi( icnv ); if( cnv.is_bound() && nr < cnv->get_vehicle_count() ) { // we remove all connected vehicles together! // find start unsigned start_nr = nr; while( start_nr > 0 ) { start_nr--; const vehicle_desc_t *info = cnv->get_vehicle(start_nr)->get_desc(); if( info->get_trailer_count() != 1 ) { start_nr++; break; } } cbuffer_t start; start.printf("%u", start_nr); const char tool = to_end ? 'R' : 'r'; depot->call_depot_tool( tool, cnv, start ); } } bool depot_frame_t::action_triggered( gui_action_creator_t *comp, value_t p) { convoihandle_t cnv = depot->get_convoi( icnv ); if( depot->is_command_pending() ) { // block new commands until last command is executed return true; } if( comp != NULL ) { // message from outside! if( comp == &bt_start ) { if( cnv.is_bound() ) { //first: close schedule (will update schedule on clients) destroy_win( (ptrdiff_t)cnv->get_schedule() ); // only then call the tool to start char tool = (event_get_last_control_shift() ^ tool_t::control_invert)==2 ? 'B' : 'b'; // start all with CTRL-click depot->call_depot_tool( tool, cnv, NULL); } } else if( comp == &bt_schedule ) { if( line_selector.get_selection() == 1 && !line_selector.is_dropped() ) { // create new line // promote existing individual schedule to line cbuffer_t buf; if( cnv.is_bound() && !selected_line.is_bound() ) { schedule_t* schedule = cnv->get_schedule(); if( schedule ) { schedule->sprintf_schedule( buf ); } } depot->call_depot_tool('l', convoihandle_t(), buf); return true; } else { open_schedule_editor(); return true; } } else if( comp == &line_button ) { if( cnv.is_bound() ) { cnv->get_owner()->simlinemgmt.show_lineinfo( cnv->get_owner(), cnv->get_line(), 0 ); welt->set_dirty(); } } else if( comp == &bt_sell ) { char tool = (event_get_last_control_shift() ^ tool_t::control_invert)==2 ? 'V' : 'v'; // sell all with CTRL-click depot->call_depot_tool( tool, cnv, NULL); } // image list selection here ... else if( comp == &convoi ) { image_from_convoi_list( p.i, last_meta_event_get_class() == EVENT_DOUBLE_CLICK); } else if( comp == &pas && last_meta_event_get_class() != EVENT_DOUBLE_CLICK ) { image_from_storage_list(pas_vec[p.i]); } else if( comp == &electrics && last_meta_event_get_class() != EVENT_DOUBLE_CLICK ) { image_from_storage_list(electrics_vec[p.i]); } else if( comp == &loks && last_meta_event_get_class() != EVENT_DOUBLE_CLICK ) { image_from_storage_list(loks_vec[p.i]); } else if( comp == &waggons && last_meta_event_get_class() != EVENT_DOUBLE_CLICK ) { image_from_storage_list(waggons_vec[p.i]); } // convoi filters else if( comp == &bt_obsolete ) { show_retired_vehicles = (show_retired_vehicles == 0); bt_obsolete.pressed = show_retired_vehicles; depot_t::update_all_win(); } else if( comp == &bt_show_all ) { show_all = (show_all == 0); bt_show_all.pressed = show_all; depot_t::update_all_win(); } else if( comp == &name_filter_input ) { depot_t::update_all_win(); } else if( comp == &bt_veh_action ) { if( veh_action == va_sell ) { veh_action = va_append; } else { veh_action++; } } else if( comp == &sort_by ) { depot->selected_sort_by = sort_by.get_selection(); } else if( comp == &bt_copy_convoi ) { if( cnv.is_bound() ) { if( !welt->use_timeline() || welt->get_settings().get_allow_buying_obsolete_vehicles() || depot->check_obsolete_inventory( cnv ) ) { depot->call_depot_tool('c', cnv, NULL); } else { create_win( new news_img("Can't buy obsolete vehicles!"), w_time_delete, magic_none ); } } return true; } else if( comp == &convoy_selector ) { icnv = p.i - 1; } else if( comp == &line_selector ) { const int selection = p.i; if( selection == 0 ) { // unique if( selected_line.is_bound() ) { selected_line = linehandle_t(); apply_line(); } return true; } else if( selection == 1 ) { // create new line if( line_selector.is_dropped() ) { // but not from next/prev buttons // promote existing individual schedule to line cbuffer_t buf; if( cnv.is_bound() && !selected_line.is_bound() ) { schedule_t* schedule = cnv->get_schedule(); if( schedule ) { schedule->sprintf_schedule( buf ); } } last_selected_line = linehandle_t(); // clear last selected line so we can get a new one ... depot->call_depot_tool('l', convoihandle_t(), buf); } return true; } if( last_selected_line.is_bound() ) { if( selection == 2 ) { // last selected line selected_line = last_selected_line; apply_line(); return true; } } // access the selected element to get selected line line_scrollitem_t *item = dynamic_cast(line_selector.get_element(selection)); if( item ) { selected_line = item->get_line(); depot->set_last_selected_line( selected_line ); last_selected_line = selected_line; apply_line(); return true; } } else if( comp == &vehicle_filter ) { depot->selected_filter = vehicle_filter.get_selection(); } else { return false; } build_vehicle_lists(); } else { update_data(); update_tabs(); } return true; } bool depot_frame_t::infowin_event(const event_t *ev) { // enable disable button actions if( ev->ev_class < INFOWIN && (depot == NULL || welt->get_active_player() != depot->get_owner()) ) { return false; } const bool swallowed = gui_frame_t::infowin_event(ev); if(IS_WINDOW_CHOOSE_NEXT(ev)) { bool dir = (ev->ev_code==NEXT_WINDOW); depot_t *next_dep = depot_t::find_depot( depot->get_pos(), depot->get_typ(), depot->get_owner(), dir == NEXT_WINDOW ); if(next_dep == NULL) { if(dir == NEXT_WINDOW) { // check the next from start of map next_dep = depot_t::find_depot( koord3d(-1,-1,0), depot->get_typ(), depot->get_owner(), true ); } else { // respective end of map next_dep = depot_t::find_depot( koord3d(8192,8192,127), depot->get_typ(), depot->get_owner(), false ); } } if(next_dep && next_dep!=this->depot) { // Replace our depot_frame_t with a new at the same position. const scr_coord pos = win_get_pos(this); destroy_win( this ); next_dep->show_info(); win_set_pos(win_get_magic((ptrdiff_t)next_dep), pos); welt->get_viewport()->change_world_position(next_dep->get_pos()); } else { // recenter on current depot welt->get_viewport()->change_world_position(depot->get_pos()); } return true; } if (ev->ev_code == WIN_TOP) { update_data(); } if( swallowed && get_focus()==&name_filter_input && (IS_KEYDOWN(ev) || ev->ev_class == EVENT_STRING) ) { depot_t::update_all_win(); } return swallowed; } void depot_frame_t::draw(scr_coord pos, scr_size size) { const bool action_allowed = welt->get_active_player() == depot->get_owner(); bt_copy_convoi.enable( action_allowed ); bt_start.enable( action_allowed ); bt_schedule.enable( action_allowed ); bt_sell.enable( action_allowed ); bt_obsolete.enable( action_allowed ); bt_show_all.enable( action_allowed ); bt_veh_action.enable( action_allowed ); line_button.enable( action_allowed ); convoihandle_t cnv = depot->get_convoi(icnv); // check for data inconsistencies (can happen with withdraw-all and vehicle in depot) if( !cnv.is_bound() && !convoi_pics.empty() ) { icnv=0; update_data(); cnv = depot->get_convoi(icnv); } if (win_get_top() == this) { update_vehicle_info_text(pos); } gui_frame_t::draw(pos, size); } void depot_frame_t::apply_line() { if( icnv > -1 ) { convoihandle_t cnv = depot->get_convoi( icnv ); // if no convoi is selected, do nothing if( !cnv.is_bound() ) { return; } if( selected_line.is_bound() ) { // set new route only, a valid route is selected: char id[16]; sprintf( id, "%i", selected_line.get_id() ); cnv->call_convoi_tool('l', id ); } else { // sometimes the user might wish to remove convoy from line // => we clear the schedule completely schedule_t *dummy = cnv->create_schedule()->copy(); dummy->entries.clear(); cbuffer_t buf; dummy->sprintf_schedule(buf); cnv->call_convoi_tool('g', (const char*)buf ); delete dummy; } } } void depot_frame_t::open_schedule_editor() { convoihandle_t cnv = depot->get_convoi( icnv ); if( cnv.is_bound() && cnv->get_vehicle_count() > 0 ) { if( selected_line.is_bound() && (event_get_last_control_shift() ^ tool_t::control_invert)==2 ) { // update line with CTRL-click create_win( new line_management_gui_t( selected_line, depot->get_owner(), 0 ), w_info, (ptrdiff_t)selected_line.get_rep() ); } else { // edit individual schedule // this can happen locally, since any update of the schedule is done during closing window cnv->open_schedule_window( welt->get_active_player() == cnv->get_owner() ); } } else { create_win( new news_img("Please choose vehicles first\n"), w_time_delete, magic_none ); } } void depot_frame_t::update_vehicle_info_text(scr_coord pos) { // number of convoys if (old_vehicle_count != depot->get_vehicle_list().get_count()) { const uint32 count = depot->get_vehicle_list().get_count(); switch (count) { case 0: { lb_convoi_count.buf().append( translator::translate("Keine Einzelfahrzeuge im Depot") ); break; } case 1: { lb_convoi_count.buf().append( translator::translate("1 Einzelfahrzeug im Depot") ); break; } default: { lb_convoi_count.buf().printf( translator::translate("%d Einzelfahrzeuge im Depot"), count ); break; } } old_vehicle_count = count; lb_convoi_count.update(); cont_veh_action->set_size(cont_veh_action->get_size()); } // unmark vehicle for(gui_image_list_t::image_data_t* const& iptr : convoi_pics) { iptr->count = 0; } // Find vehicle under mouse cursor gui_component_t const* const tab = tabs.get_aktives_tab(); gui_image_list_t const* const lst = tab == &scrolly_pas ? &pas : tab == &scrolly_electrics ? &electrics : tab == &scrolly_loks ? &loks : &waggons; const scr_coord mouse_pos = get_mouse_pos(); double resale_value = -1.0; const vehicle_desc_t *veh_type = NULL; bool new_vehicle_length_sb_force_zero = false; sint16 convoi_number = -1; scr_coord relpos = scr_coord(0, ((gui_scrollpane_t *)tabs.get_aktives_tab())->get_scroll_y()); int sel_index = lst->index_at( pos + tabs.get_pos() - relpos, mouse_pos.x, mouse_pos.y - D_TITLEBAR_HEIGHT - tabs.get_required_size().h); if( (sel_index != -1) && (tabs.getroffen({ mouse_pos.x-pos.x, mouse_pos.y-pos.y-D_TITLEBAR_HEIGHT })) ) { // cursor over a vehicle in the selection list const vector_tpl& vec = (lst == &electrics ? electrics_vec : (lst == &pas ? pas_vec : (lst == &loks ? loks_vec : waggons_vec))); veh_type = vehicle_builder_t::get_info( vec[sel_index]->text ); if( vec[sel_index]->lcolor == gfx->palette_lookup(COL_RED) || veh_action == va_sell ) { // don't show new_vehicle_length_sb when can't actually add the highlighted vehicle, or selling from inventory new_vehicle_length_sb_force_zero = true; } if( vec[sel_index]->count > 0 ) { resale_value = (double)calc_restwert( veh_type ); } } else { // cursor over a vehicle in the convoi relpos = scr_coord(scrolly_convoi.get_scroll_x(), 0); convoi_number = sel_index = convoi.index_at(pos - relpos + scrolly_convoi.get_pos(), mouse_pos.x, mouse_pos.y - D_TITLEBAR_HEIGHT); if( sel_index != -1 ) { convoihandle_t cnv = depot->get_convoi( icnv ); veh_type = cnv->get_vehicle( sel_index )->get_desc(); resale_value = cnv->get_vehicle( sel_index )->calc_sale_value(); new_vehicle_length_sb_force_zero = true; // mark selected vehicle in convoi convoi_pics[convoi_number]->count = convoi_number+1; } } if( veh_type && veh_type != old_veh_type) { labels[LB_VEH_NAME]->buf().printf( "%s", translator::translate( veh_type->get_name(), welt->get_settings().get_name_language_id() ) ); if( veh_type->get_power() > 0 ) { // engine type labels[LB_VEH_NAME]->buf().printf( " (%s)\n", translator::translate( vehicle_builder_t::engine_type_names[veh_type->get_engine_type()+1] ) ); } if( sint64 fix_cost = welt->scale_with_month_length( veh_type->get_fixed_cost() ) ) { char tmp[128]; money_to_string( tmp, veh_type->get_price() / 100.0, false ); labels[LB_VEH_COST]->buf().printf( translator::translate("Cost: %8s (%.2f$/km %.2f$/m)\n"), tmp, veh_type->get_running_cost()/100.0, fix_cost/100.0 ); } else { char tmp[128]; money_to_string( tmp, veh_type->get_price() / 100.0, false ); labels[LB_VEH_COST]->buf().printf( translator::translate("Cost: %8s (%.2f$/km)\n"), tmp, veh_type->get_running_cost()/100.0 ); } if( veh_type->get_capacity() > 0 ) { // must translate as "Capacity: %3d%s %s\n" labels[LB_VEH_CAPACITY]->buf().printf( translator::translate("Capacity: %d%s %s\n"), veh_type->get_capacity(), translator::translate( veh_type->get_freight_type()->get_mass() ), veh_type->get_freight_type()->get_catg()==0 ? translator::translate( veh_type->get_freight_type()->get_name() ) : translator::translate( veh_type->get_freight_type()->get_catg_name() ) ); labels[LB_VEH_LOADINGTIME]->buf().printf("%s%s", translator::translate("Loading time:"), difftick_to_string(veh_type->get_loading_time(), false)); } else { labels[LB_VEH_CAPACITY]->buf().clear(); labels[LB_VEH_LOADINGTIME]->buf().clear(); } labels[LB_VEH_SPEED]->buf().printf( "%s %3d km/h\n", translator::translate("Max. speed:"), veh_type->get_topspeed() ); if( veh_type->get_power() > 0 ) { if( veh_type->get_gear() != 64 ){ labels[LB_VEH_POWER]->buf().printf( "%s %4d kW (x%0.2f)\n", translator::translate("Power:"), veh_type->get_power(), veh_type->get_gear() / 64.0 ); } else { labels[LB_VEH_POWER]->buf().printf( translator::translate("Power: %4d kW\n"), veh_type->get_power() ); } } // column 2 labels[LB_VEH_WEIGHT]->buf().printf( "%s %4.1ft\n", translator::translate("Weight:"), veh_type->get_weight() / 1000.0 ); labels[LB_VEH_DATE]->buf().printf( "%s: %s - ", translator::translate("Available"), translator::get_short_date(veh_type->get_intro_year_month() / 12, veh_type->get_intro_year_month() % 12) ); if( veh_type->get_retire_year_month() != DEFAULT_RETIRE_YEAR * 12 ) { labels[LB_VEH_DATE]->buf().printf( "%s\n", translator::get_short_date(veh_type->get_retire_year_month() / 12, veh_type->get_retire_year_month() % 12) ); } if( char const* const copyright = veh_type->get_copyright() ) { labels[LB_VEH_AUTHOR]->buf().printf( translator::translate("Constructed by %s"), copyright ); } else { labels[LB_VEH_AUTHOR]->buf().clear(); } if( resale_value != -1.0 ) { char tmp[128]; money_to_string( tmp, resale_value / 100.0, false ); labels[LB_VEH_VALUE]->buf().printf( "%s %8s", translator::translate("Restwert:"), tmp ); } else { labels[LB_VEH_VALUE]->buf().clear(); } // update speedbar new_vehicle_length_sb = new_vehicle_length_sb_force_zero ? 0 : convoi_length_ok_sb + convoi_length_slower_sb + convoi_length_too_slow_sb + veh_type->get_length(); } else if (veh_type == NULL) { new_vehicle_length_sb = 0; for(uint32 i=LB_CNV_ALL; ibuf().clear(); } } if (veh_type != old_veh_type) { old_veh_type = veh_type; // update labels and size for(uint32 i=LB_CNV_ALL; iupdate(); } cont_vehicle_labels->set_size(cont_vehicle_labels->get_size()); } } void depot_frame_t::update_tabs() { gui_component_t *old_tab = tabs.get_aktives_tab(); tabs.clear(); bool one = false; // add only if there are any if( !pas_vec.empty() ) { tabs.add_tab(&scrolly_pas, translator::translate( depot->get_passenger_name() ) ); one = true; } // add only if there are any trolleybuses if( !electrics_vec.empty() ) { tabs.add_tab(&scrolly_electrics, translator::translate( depot->get_electrics_name() ) ); one = true; } // add, if wagons are there ... if( !loks_vec.empty() || !waggons_vec.empty() ) { tabs.add_tab(&scrolly_loks, translator::translate( depot->get_zieher_name() ) ); one = true; } // only add, if there are wagons if( !waggons_vec.empty() ) { tabs.add_tab(&scrolly_waggons, translator::translate( depot->get_haenger_name() ) ); one = true; } if( !one ) { // add passenger as default tabs.add_tab(&scrolly_pas, translator::translate( depot->get_passenger_name() ) ); } // Look, if there is our old tab present again (otherwise it will be 0 by tabs.clear()). for( uint8 i = 0; i < tabs.get_count(); i++ ) { if( old_tab == tabs.get_tab(i) ) { // Found it! tabs.set_active_tab_index(i); break; } } } uint32 depot_frame_t::get_rdwr_id() { return magic_depot; } void depot_frame_t::rdwr( loadsave_t *file) { if (file->is_version_less(120, 9)) { destroy_win(this); return; } // depot position koord3d pos; if( file->is_saving() ) { pos = depot->get_pos(); } pos.rdwr( file ); // window size scr_size size = get_windowsize(); size.rdwr( file ); if( file->is_loading() ) { depot_t *dep = welt->lookup(pos)->get_depot(); if (dep) { init(dep); } } tabs.rdwr(file); vehicle_filter.rdwr(file); file->rdwr_byte(veh_action); file->rdwr_long(icnv); sort_by.rdwr(file); simline_t::rdwr_linehandle_t(file, selected_line); if( depot && file->is_loading() ) { update_data(); update_tabs(); reset_min_windowsize(); set_windowsize(size); win_set_magic(this, (ptrdiff_t)depot); } if (depot == NULL) { destroy_win(this); } } simutrans-124.5/src/simutrans/gui/depot_frame.h000066400000000000000000000141411520675460500216230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_DEPOT_FRAME_H #define GUI_DEPOT_FRAME_H #include "gui_frame.h" #include "components/gui_label.h" #include "components/gui_image.h" #include "components/gui_image_list.h" #include "components/gui_textinput.h" #include "components/gui_combobox.h" #include "../tpl/ptrhashtable_tpl.h" #include "../tpl/vector_tpl.h" #include "components/gui_tab_panel.h" #include "components/gui_button.h" #include "components/action_listener.h" #include "components/gui_scrollpane.h" #include "components/gui_speedbar.h" #include "../simtypes.h" #include "../utils/cbuffer.h" #include "../linehandle.h" #include "../convoihandle.h" class depot_t; class vehicle_desc_t; class depot_convoi_capacity_t; class gui_aligned_container_zero_width_t; /** * Depot frame, handles all interaction with a vehicle depot. */ class depot_frame_t : public gui_frame_t, public action_listener_t { private: /** * The depot to display */ depot_t *depot; /** * The current convoi to display. */ int icnv; /** * show retired vehicles (same for all depot) */ static bool show_retired_vehicles; /** * show retired vehicles (same for all depot) */ static bool show_all; /** * Gui elements */ gui_label_buf_t lb_convois, lb_convoi_count; /// contains the current translation of "new convoi" const char* new_convoy_text; gui_combobox_t convoy_selector; button_t line_button; // goto line ... vector_tpl labels; gui_aligned_container_t *cont_veh_action; gui_aligned_container_zero_width_t *cont_vehicle_labels; depot_convoi_capacity_t* cont_convoi_capacity; gui_speedbar_fixed_length_t sb_convoi_length; sint32 convoi_length_ok_sb, convoi_length_slower_sb, convoi_length_too_slow_sb, convoi_tile_length_sb, new_vehicle_length_sb; button_t bt_start; button_t bt_schedule; button_t bt_sell; button_t bt_obsolete; button_t bt_show_all; gui_combobox_t sort_by; static char name_filter_value[64]; gui_textinput_t name_filter_input; gui_tab_panel_t tabs; button_t bt_veh_action; button_t bt_copy_convoi; vector_tpl convoi_pics; gui_image_list_t convoi; gui_aligned_container_t cont_convoi; gui_scrollpane_t scrolly_convoi; /// image list of passenger cars vector_tpl pas_vec; /// image list of electrified passenger carrier units vector_tpl electrics_vec; /// image list of all other powered vehicles (and vehicles without freight capacity) vector_tpl loks_vec; /// image list of all other cars (freight, non-powered) vector_tpl waggons_vec; gui_image_list_t pas; gui_image_list_t electrics; gui_image_list_t loks; gui_image_list_t waggons; gui_scrollpane_t scrolly_pas; gui_scrollpane_t scrolly_electrics; gui_scrollpane_t scrolly_loks; gui_scrollpane_t scrolly_waggons; /// contains the current translation of "" const char* no_schedule_text; /// contains the current translation of "" const char* clear_schedule_text; /// contains the current translation of "" const char* unique_schedule_text; /// contains the current translation of "" const char* new_line_text; /// contains the current translation of "" const char* promote_to_line_text; /// "-----------" between header items and lines const char* line_seperator; gui_combobox_t line_selector; gui_combobox_t vehicle_filter; gui_image_t img_bolt; linehandle_t selected_line, last_selected_line; enum { va_append, va_insert, va_sell }; uint8 veh_action; /** * A helper map to update loks_vec and waggons_Vec. All entries from * loks_vec and waggons_vec are referenced here. */ typedef ptrhashtable_tpl vehicle_image_map; vehicle_image_map vehicle_map; /** * Update the info text for the vehicle the mouse is over - if any. */ void update_vehicle_info_text(scr_coord pos); // cache old values uint32 old_vehicle_count; const vehicle_desc_t *old_veh_type; /** * Calculate the values of the vehicles of the given type owned by the * player. */ sint64 calc_restwert(const vehicle_desc_t *veh_type); /// true if already stored here bool is_in_vehicle_list(const vehicle_desc_t *info); /// add a single vehicle (helper function) void add_to_vehicle_list(const vehicle_desc_t *info); /// for convoi image void image_from_convoi_list(uint nr, bool to_end); void image_from_storage_list(gui_image_list_t::image_data_t *image_data); /// initialize everything void init(depot_t *depot); public: // the next two are only needed for depot_t update notifications void activate_convoi( convoihandle_t cnv ); int get_icnv() const { return icnv; } /** * Update texts, image lists and buttons according to the current state. */ void update_data(); // more general functions ... depot_frame_t(depot_t* depot = NULL); ~depot_frame_t(); /** * Set the window size */ void set_windowsize(scr_size size) OVERRIDE; /** * Create and fill loks_vec and waggons_vec. */ void build_vehicle_lists(); /** * Will update the tabs (don't show empty ones). */ void update_tabs(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE {return "depot.txt";} /** * Does this window need a next button in the title bar? * @return true if such a button is needed */ bool has_next() const OVERRIDE {return true;} koord3d get_weltpos(bool) OVERRIDE; bool is_weltpos() OVERRIDE; /** * Open dialog for schedule entry. */ void open_schedule_editor(); bool infowin_event(event_t const*) OVERRIDE; /** * Draw the Frame */ void draw(scr_coord pos, scr_size size) OVERRIDE; void apply_line(); void set_selected_line(linehandle_t line) { selected_line = line; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; uint32 get_rdwr_id() OVERRIDE; void rdwr( loadsave_t * ) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/depotlist_frame.cc000066400000000000000000000143211520675460500226550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "depotlist_frame.h" #include "gui_theme.h" #include "../player/simplay.h" #include "../obj/depot.h" #include "../simskin.h" #include "../world/simworld.h" #include "../display/viewport.h" #include "../tool/simmenu.h" #include "../dataobj/translator.h" #include "../descriptor/skin_desc.h" depotlist_stats_t::depotlist_stats_t(depot_t *d) { this->depot = d; // pos button set_table_layout(3,1); gotopos.set_typ(button_t::posbutton_automatic); gotopos.set_targetpos3d(depot->get_pos()); add_component(&gotopos); // now add all specific tabs switch( d->get_waytype() ) { case maglev_wt: waytype_symbol.set_image( skinverwaltung_t::maglevhaltsymbol->get_image_id(0), true ); break; case monorail_wt: waytype_symbol.set_image( skinverwaltung_t::monorailhaltsymbol->get_image_id(0), true ); break; case track_wt: waytype_symbol.set_image( skinverwaltung_t::zughaltsymbol->get_image_id(0), true ); break; case tram_wt: waytype_symbol.set_image( skinverwaltung_t::tramhaltsymbol->get_image_id(0), true ); break; case narrowgauge_wt: waytype_symbol.set_image( skinverwaltung_t::narrowgaugehaltsymbol->get_image_id(0), true ); break; case road_wt: waytype_symbol.set_image( skinverwaltung_t::autohaltsymbol->get_image_id(0), true ); break; case water_wt: waytype_symbol.set_image( skinverwaltung_t::schiffshaltsymbol->get_image_id(0), true ); break; case air_wt: waytype_symbol.set_image( skinverwaltung_t::airhaltsymbol->get_image_id(0), true ); break; default: ; } add_component(&waytype_symbol); add_component(&label); update_label(); } void depotlist_stats_t::update_label() { cbuffer_t &buf = label.buf(); buf.append( translator::translate(depot->get_name()) ); buf.printf( " %s ", depot->get_pos().get_2d().get_fullstr() ); int cnvs = depot->convoi_count(); if( cnvs == 0 ) { // buf.append( translator::translate( "no convois" ) ); } else if( cnvs == 1 ) { buf.append( translator::translate( "1 convoi" ) ); buf.append(" "); } else { buf.printf( translator::translate( "%d convois" ), cnvs ); buf.append(" "); } int vhls = depot->get_vehicle_list().get_count(); if( vhls == 0 ) { buf.append( translator::translate( "Keine Einzelfahrzeuge im Depot" ) ); } else if( vhls == 1 ) { buf.append( translator::translate( "1 Einzelfahrzeug im Depot" ) ); } else { buf.printf( translator::translate( "%d Einzelfahrzeuge im Depot" ), vhls ); } label.update(); } void depotlist_stats_t::set_size(scr_size size) { gui_aligned_container_t::set_size(size); label.set_size(scr_size(get_size().w - label.get_pos().x, label.get_size().h)); } bool depotlist_stats_t::is_valid() const { return depot_t::get_depot_list().is_contained(depot); } bool depotlist_stats_t::infowin_event(const event_t * ev) { bool swallowed = gui_aligned_container_t::infowin_event(ev); if (!swallowed) { // either open dialog or goto (with control or right click) if (IS_LEFTRELEASE(ev)) { if ((event_get_last_control_shift() ^ tool_t::control_invert) == 2) { world()->get_viewport()->change_world_position(depot->get_pos()); } else { depot->show_info(); } return true; } if (IS_RIGHTRELEASE(ev)) { world()->get_viewport()->change_world_position(depot->get_pos()); } } return swallowed; } void depotlist_stats_t::draw(scr_coord pos) { update_label(); gui_aligned_container_t::draw(pos); } bool depotlist_stats_t::compare(const gui_component_t *aa, const gui_component_t *bb) { const depotlist_stats_t* fa = dynamic_cast(aa); const depotlist_stats_t* fb = dynamic_cast(bb); // good luck with mixed lists assert(fa != NULL && fb != NULL); depot_t *a=fa->depot, *b=fb->depot; int cmp = a->convoi_count() - b->convoi_count(); if( cmp == 0 ) { cmp = a->get_vehicle_list().get_count() - b->get_vehicle_list().get_count(); } if (cmp == 0) { cmp = koord_distance( a->get_pos(), koord( 0, 0 ) ) - koord_distance( b->get_pos(), koord( 0, 0 ) ); if( cmp == 0 ) { cmp = a->get_pos().x - b->get_pos().x; } } return cmp > 0; } depotlist_frame_t::depotlist_frame_t(player_t *player) : gui_frame_t( translator::translate("dp_title"), player ), scrolly(gui_scrolled_list_t::windowskin, depotlist_stats_t::compare) { this->player = player; last_depot_count = 0; set_table_layout(1,0); scrolly.set_maximize(true); scrolly.set_checkered(true); tabs.init_tabs(&scrolly); tabs.add_listener(this); add_component(&tabs); fill_list(); set_resizemode(diagonal_resize); reset_min_windowsize(); } depotlist_frame_t::depotlist_frame_t() : gui_frame_t(translator::translate("dp_title"), NULL), scrolly(gui_scrolled_list_t::windowskin, depotlist_stats_t::compare) { player = NULL; last_depot_count = 0; set_table_layout(1, 0); scrolly.set_maximize(true); tabs.init_tabs(&scrolly); tabs.add_listener(this); add_component(&tabs); set_resizemode(diagonal_resize); reset_min_windowsize(); } /** * This method is called if an action is triggered */ bool depotlist_frame_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { if (comp == &tabs) { fill_list(); } return true; } void depotlist_frame_t::fill_list() { scrolly.clear_elements(); for(depot_t* const depot : depot_t::get_depot_list()) { if( depot->get_owner() == player ) { if( tabs.get_active_tab_index() == 0 || depot->get_waytype() == tabs.get_active_tab_waytype() ) { scrolly.new_component(depot); } } } scrolly.sort(0); scrolly.set_size(scr_size(get_windowsize().w, scrolly.get_size().h)); last_depot_count = depot_t::get_depot_list().get_count(); } void depotlist_frame_t::draw(scr_coord pos, scr_size size) { if( depot_t::get_depot_list().get_count() != last_depot_count ) { fill_list(); } gui_frame_t::draw(pos,size); } void depotlist_frame_t::rdwr(loadsave_t* file) { scr_size size = get_windowsize(); uint8 player_nr = player ? player->get_player_nr() : 0; file->rdwr_byte(player_nr); size.rdwr(file); tabs.rdwr(file); scrolly.rdwr(file); if (file->is_loading()) { player = welt->get_player(player_nr); win_set_magic(this, magic_depotlist + player_nr); fill_list(); set_windowsize(size); } } simutrans-124.5/src/simutrans/gui/depotlist_frame.h000066400000000000000000000031621520675460500225200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_DEPOTLIST_FRAME_H #define GUI_DEPOTLIST_FRAME_H #include "gui_frame.h" #include "simwin.h" #include "components/gui_scrollpane.h" #include "components/gui_scrolled_list.h" #include "components/gui_waytype_tab_panel.h" #include "components/gui_label.h" #include "components/gui_image.h" class depot_t; class depotlist_frame_t : public gui_frame_t, private action_listener_t { private: gui_scrolled_list_t scrolly; gui_waytype_tab_panel_t tabs; uint32 last_depot_count; player_t *player; public: depotlist_frame_t(); depotlist_frame_t(player_t* player); void fill_list(); const char *get_help_filename() const OVERRIDE {return "depotlist.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void draw(scr_coord pos, scr_size size) OVERRIDE; void map_rotate90( sint16 ) OVERRIDE { fill_list(); } void rdwr(loadsave_t* file) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_depotlist; } }; class depotlist_stats_t : public gui_aligned_container_t, public gui_scrolled_list_t::scrollitem_t { private: depot_t *depot; gui_label_buf_t label; gui_image_t waytype_symbol; button_t gotopos; void update_label(); public: depotlist_stats_t(depot_t *); void draw( scr_coord pos) OVERRIDE; char const* get_text() const OVERRIDE { return ""; /* label.buf().get_str(); */ } bool infowin_event(const event_t *) OVERRIDE; bool is_valid() const OVERRIDE; void set_size(scr_size size) OVERRIDE; static bool compare(const gui_component_t *a, const gui_component_t *b ); }; #endif simutrans-124.5/src/simutrans/gui/display_settings.cc000066400000000000000000000705111520675460500230640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "display_settings.h" #include "../simdebug.h" #include "../world/simworld.h" #include "../display/simimg.h" #include "../simintr.h" #include "../simcolor.h" #include "../dataobj/settings.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../obj/baum.h" #include "../obj/zeiger.h" #include "../display/simgraph.h" #include "../tool/simmenu.h" #include "../player/simplay.h" #include "../utils/simstring.h" #include "../sys/simsys.h" #include "gui_theme.h" #include "themeselector.h" #include "loadfont_frame.h" #include "simwin.h" // display text label in player colors void display_text_label(sint16 xpos, sint16 ypos, const char* text, const player_t *player, bool dirty); // grund.cc enum { IDBTN_SCROLL_INVERSE, IDBTN_IGNORE_NUMLOCK, IDBTN_PEDESTRIANS_AT_STOPS, IDBTN_PEDESTRIANS_IN_TOWNS, IDBTN_DAY_NIGHT_CHANGE, IDBTN_TRANSPARENT_INSTEAD_OF_HIDDEN, IDBTN_HIDE_TREES, IDBTN_TRANSPARENT_STATION_COVERAGE, IDBTN_SHOW_STATION_COVERAGE, IDBTN_UNDERGROUND_VIEW, IDBTN_SHOW_GRID, IDBTN_HIDE_LABELS, IDBTN_LABEL_STYLE_ARROW, IDBTN_SHOW_WAITING_BARS, IDBTN_SHOW_SLICE_MAP_VIEW, IDBTN_HIDE_BUILDINGS, IDBTN_SHOW_SCHEDULES_STOP, IDBTN_SHOW_THEMEMANAGER, IDBTN_SIMPLE_DRAWING, IDBTN_CHANGE_FONT, IDBTN_INFINITE_SCROLL, IDBTN_LEFTDRAG_MINIMAP, COLORS_MAX_BUTTONS }; static button_t buttons[COLORS_MAX_BUTTONS]; /** * class to visualize station names IDBTN_SHOW_FACTORY_STORAGE, */ class gui_label_stationname_t : public gui_label_t { karte_ptr_t welt; public: gui_label_stationname_t(const char* text) : gui_label_t(text) {} void draw(scr_coord offset) OVERRIDE { scr_coord p = pos + offset; const player_t* player = welt->get_active_player(); const char *text = get_text_pointer(); display_text_label(p.x, p.y, text, player, true); } scr_size get_min_size() const OVERRIDE { return gui_label_t::get_min_size() + scr_size(LINESPACE + D_H_SPACE, 4); } }; gui_settings_t::gui_settings_t() { set_table_layout( 3, 0 ); // Show thememanager buttons[ IDBTN_SHOW_THEMEMANAGER ].init( button_t::roundbox_state | button_t::flexible, "Select a theme for display" ); add_component( buttons + IDBTN_SHOW_THEMEMANAGER, 3 ); // Change font buttons[ IDBTN_CHANGE_FONT ].init( button_t::roundbox_state | button_t::flexible, "Select display font" ); add_component( buttons + IDBTN_CHANGE_FONT, 3 ); // screen scale number input new_component("Screen scale:"); add_table(2,0); { screen_scale_numinp.init(dr_get_screen_scale(), 25, 400, 25, false); screen_scale_numinp.add_listener(this); add_component(&screen_scale_numinp); screen_scale_auto.init(button_t::roundbox_state, "Auto"); screen_scale_auto.add_listener(this); add_component(&screen_scale_auto); } end_table(); new_component(); // screen scale number input new_component("Icon size:"); base_icon_height = 32; if (tool_t::general_tool[TOOL_QUERY]->get_icon(NULL) != IMG_EMPTY) { scr_rect r = gfx->get_base_image_offset(tool_t::general_tool[TOOL_QUERY]->get_icon(NULL)); base_icon_height = r.h; } // find current zoom factor for (icon_zoom = 0; icon_zoom <= MAX_ZOOM_FACTOR; icon_zoom++) { if (env_t::iconsize.h >= (base_icon_height * gfx->zoom_num[icon_zoom]) / gfx->zoom_den[icon_zoom]) { // since we go from large size break; } } add_table(2, 0); { icon_scale_down.set_typ(button_t::arrowleft); icon_scale_down.add_listener(this); add_component(&icon_scale_down); icon_scale_up.set_typ(button_t::arrowright); icon_scale_up.add_listener(this); add_component(&icon_scale_up); } end_table(); new_component(); // position of menu new_component("Toolbar position:"); switch (env_t::menupos) { case MENU_TOP: toolbar_pos.init(button_t::arrowup, NULL); break; case MENU_LEFT: toolbar_pos.init(button_t::arrowleft, NULL); break; case MENU_BOTTOM: toolbar_pos.init(button_t::arrowdown, NULL); break; case MENU_RIGHT: toolbar_pos.init(button_t::arrowright, NULL); break; } add_component(&toolbar_pos,2); single_toolbar.init( button_t::square_state, "Single toolbar only" ); single_toolbar.pressed = ( env_t::single_toolbar_mode ); add_component( &single_toolbar, 3 ); stack_toolbars.init( button_t::square_state, "Stack toolbars" ); stack_toolbars.pressed = ( env_t::stack_toolbars ); add_component( &stack_toolbars, 3 ); reselect_closes_tool.init( button_t::square_state, "Reselect closes tools" ); reselect_closes_tool.pressed = env_t::reselect_closes_tool; add_component( &reselect_closes_tool, 3 ); fullscreen.init( button_t::square_state, "Fullscreen (changed after restart)" ); fullscreen.pressed = ( dr_get_fullscreen() == FULLSCREEN ); fullscreen.enable(dr_has_fullscreen()); add_component( &fullscreen, 3 ); borderless.init( button_t::square_state, "Borderless (disabled on fullscreen)" ); borderless.enable ( dr_get_fullscreen() != FULLSCREEN ); borderless.pressed = ( dr_get_fullscreen() == BORDERLESS ); add_component( &borderless, 3 ); // Frame time label new_component("Frame time:"); frame_time_value_label.buf().printf(" 9999 ms"); frame_time_value_label.update(); add_component( &frame_time_value_label, 2 ); // Idle time label new_component("Idle:"); idle_time_value_label.buf().printf(" 9999 ms"); idle_time_value_label.update(); add_component( &idle_time_value_label, 2 ); // FPS label new_component("FPS:"); fps_value_label.buf().printf(" 99.9 fps"); fps_value_label.update(); add_component( &fps_value_label, 2 ); // Simloops label new_component("Sim:"); simloops_value_label.buf().printf(" 999.9"); simloops_value_label.update(); add_component( &simloops_value_label, 2 ); } void gui_settings_t::draw(scr_coord offset) { // Update label buffers frame_time_value_label.buf().printf(" %d ms", get_frame_time() ); frame_time_value_label.update(); idle_time_value_label.buf().printf(" %d ms", world()->get_idle_time() ); idle_time_value_label.update(); // fps_label uint32 target_fps = world()->is_fast_forward() ? env_t::ff_fps : env_t::fps; uint32 loops = world()->get_realFPS(); PIXVAL color = SYSCOL_TEXT_HIGHLIGHT; if( loops < (target_fps*16*3)/4 ) { color = gfx->palette_lookup(( loops <= target_fps*16/2 ) ? COL_RED : COL_YELLOW); } fps_value_label.set_color(color); fps_value_label.buf().printf(" %d fps", loops/16 ); #if MSG_LEVEL >= 3 if( env_t::simple_drawing ) { fps_value_label.buf().append( "*" ); } #endif fps_value_label.update(); //simloops_label loops = world()->get_simloops(); color = SYSCOL_TEXT_HIGHLIGHT; if( loops <= 30 ) { color = gfx->palette_lookup((loops<=20) ? COL_RED : COL_YELLOW); } simloops_value_label.set_color(color); simloops_value_label.buf().printf(" %d%c%d", loops/10, get_fraction_sep(), loops%10 ); simloops_value_label.update(); // All components are updated, now draw them... gui_aligned_container_t::draw(offset); } bool gui_settings_t::action_triggered(gui_action_creator_t *comp, value_t v) { if (comp == &screen_scale_numinp) { env_t::dpi_scale = v.i; dr_set_screen_scale(v.i); } else if (comp == &screen_scale_auto) { env_t::dpi_scale = -1; dr_set_screen_scale(-1); screen_scale_numinp.set_value(dr_get_screen_scale()); } else if (comp == &icon_scale_down) { if (icon_zoom < MAX_ZOOM_FACTOR) { icon_scale_up.enable(true); icon_zoom++; goto icon_resize; } icon_scale_down.enable(false); } else if (comp == &icon_scale_up) { if (icon_zoom > 0) { icon_scale_down.enable(true); icon_zoom--; goto icon_resize; } icon_scale_up.enable(false); } return true; icon_resize: scr_coord_val new_size = (gfx->zoom_num[icon_zoom] * base_icon_height) / gfx->zoom_den[icon_zoom]; env_t::iconsize.h = env_t::iconsize.w = new_size; // reload windows to update event_t* ev = new event_t(); ev->ev_class = EVENT_SYSTEM; ev->ev_code = SYSTEM_RELOAD_WINDOWS; queue_event(ev); return true; } map_settings_t::map_settings_t() { set_table_layout( 2, 0 ); // Show grid checkbox buttons[ IDBTN_SHOW_GRID ].init( button_t::square_state, "show grid" ); add_component( buttons + IDBTN_SHOW_GRID, 2 ); // Underground view checkbox buttons[ IDBTN_UNDERGROUND_VIEW ].init( button_t::square_state, "underground mode" ); add_component( buttons + IDBTN_UNDERGROUND_VIEW, 2 ); // Show slice map view checkbox buttons[ IDBTN_SHOW_SLICE_MAP_VIEW ].init( button_t::square_state, "sliced underground mode" ); add_component( buttons + IDBTN_SHOW_SLICE_MAP_VIEW ); // underground slice edit inp_underground_level.set_value( grund_t::underground_mode == grund_t::ugm_level ? grund_t::underground_level : world()->get_zeiger()->get_pos().z ); inp_underground_level.set_limits( world()->get_groundwater() - 10, 32 ); inp_underground_level.add_listener( this ); add_component( &inp_underground_level ); // Day/night change checkbox buttons[ IDBTN_DAY_NIGHT_CHANGE ].init( button_t::square_state, "8WORLD_CHOOSE" ); add_component( buttons + IDBTN_DAY_NIGHT_CHANGE, 2 ); // Brightness label new_component( "1LIGHT_CHOOSE" ); // brightness edit brightness.set_value( env_t::daynight_level ); brightness.set_limits( 0, 9 ); brightness.add_listener( this ); add_component( &brightness ); // Numpad key buttons[IDBTN_IGNORE_NUMLOCK].init(button_t::square_state, "Num pad keys always move map"); buttons[IDBTN_IGNORE_NUMLOCK].pressed = env_t::numpad_always_moves_map; add_component(buttons + IDBTN_IGNORE_NUMLOCK, 2); // Numpad key buttons[IDBTN_LEFTDRAG_MINIMAP].init(button_t::square_state, "Left button drags minimap too"); buttons[IDBTN_LEFTDRAG_MINIMAP].pressed = env_t::leftdrag_in_minimap; add_component(buttons + IDBTN_LEFTDRAG_MINIMAP, 2); // Scroll inverse checkbox buttons[IDBTN_SCROLL_INVERSE].init(button_t::square_state, "4LIGHT_CHOOSE"); add_component(buttons + IDBTN_SCROLL_INVERSE, 2); // Scroll infinite checkbox buttons[IDBTN_INFINITE_SCROLL].init(button_t::square_state, "Infinite mouse scrolling"); buttons[IDBTN_INFINITE_SCROLL].set_tooltip("Infinite scrolling using mouse"); add_component(buttons + IDBTN_INFINITE_SCROLL, 2); // scroll with genral tool selected if moved above a threshold new_component("Scroll threshold"); scroll_threshold.init(env_t::scroll_threshold, 1, 64, 1, false); scroll_threshold.add_listener(this); add_component(&scroll_threshold); // Scroll speed label new_component( "3LIGHT_CHOOSE" ); // Scroll speed edit scrollspeed.set_value( abs( env_t::scroll_multi ) ); scrollspeed.set_limits( 1, 9 ); scrollspeed.add_listener( this ); add_component( &scrollspeed ); #ifdef DEBUG // Toggle simple drawing for debugging buttons[IDBTN_SIMPLE_DRAWING].init(button_t::square_state, "Simple drawing"); add_component(buttons+IDBTN_SIMPLE_DRAWING, 2); #endif // Set date format new_component( "Date format" ); time_setting.set_focusable( false ); uint8 old_show_month = env_t::show_month; sint32 current_tick = world()->get_ticks(); for( env_t::show_month = 0; env_t::show_month<8; env_t::show_month++ ) { tstrncpy( time_str[env_t::show_month], tick_to_string( current_tick ), 64 ); time_setting.new_component( time_str[env_t::show_month], SYSCOL_TEXT ); } env_t::show_month = old_show_month; time_setting.set_selection( old_show_month ); add_component( &time_setting ); time_setting.add_listener( this ); } bool map_settings_t::action_triggered( gui_action_creator_t *comp, value_t v ) { // Brightness edit if( &brightness == comp ) { env_t::daynight_level = (sint8)v.i; } // Scroll speed edit else if (&scroll_threshold == comp) { env_t::scroll_threshold = v.i; } // Scroll speed edit else if (&scrollspeed == comp) { env_t::scroll_multi = (sint16)(buttons[IDBTN_SCROLL_INVERSE].pressed ? -v.i : v.i); } // underground slice edit else if( comp == &inp_underground_level ) { if( grund_t::underground_mode == grund_t::ugm_level ) { grund_t::underground_level = inp_underground_level.get_value(); // calc new images world()->update_underground(); } } else if( comp == &time_setting ) { env_t::show_month = v.i; return true; } return true; } transparency_settings_t::transparency_settings_t() { set_table_layout( 2, 0 ); // Transparent instead of hidden checkbox buttons[ IDBTN_TRANSPARENT_INSTEAD_OF_HIDDEN ].init( button_t::square_state, "hide transparent" ); add_component( buttons + IDBTN_TRANSPARENT_INSTEAD_OF_HIDDEN, 2 ); // Hide trees checkbox buttons[ IDBTN_HIDE_TREES ].init( button_t::square_state, "hide trees" ); add_component( buttons + IDBTN_HIDE_TREES, 2 ); // Hide buildings hide_buildings.set_focusable( false ); hide_buildings.new_component( translator::translate( "no buildings hidden" ), SYSCOL_TEXT ); hide_buildings.new_component( translator::translate( "hide city building" ), SYSCOL_TEXT ); hide_buildings.new_component( translator::translate( "hide all building" ), SYSCOL_TEXT ); hide_buildings.set_selection( env_t::hide_buildings ); add_component( &hide_buildings, 2 ); hide_buildings.add_listener( this ); buttons[ IDBTN_HIDE_BUILDINGS ].set_tooltip( "hide objects under cursor" ); buttons[ IDBTN_HIDE_BUILDINGS ].init( button_t::square_state, "Smart hide objects" ); add_component( buttons + IDBTN_HIDE_BUILDINGS ); // Smart hide objects edit cursor_hide_range.set_value( env_t::cursor_hide_range ); cursor_hide_range.set_limits( 0, 10 ); cursor_hide_range.add_listener( this ); add_component( &cursor_hide_range ); new_component( "Industry overlay" )->set_tooltip( translator::translate( "Display bars above factory to show the status" ) ); factory_tooltip.set_focusable( false ); factory_tooltip.new_component( translator::translate( "Do not show" ), SYSCOL_TEXT ); factory_tooltip.new_component( translator::translate( "On mouseover" ), SYSCOL_TEXT ); factory_tooltip.new_component( translator::translate( "Served by me" ), SYSCOL_TEXT ); factory_tooltip.new_component( translator::translate( "Show always" ), SYSCOL_TEXT ); factory_tooltip.set_selection( env_t::show_factory_storage_bar ); add_component( &factory_tooltip ); factory_tooltip.add_listener( this ); } bool transparency_settings_t::action_triggered( gui_action_creator_t *comp, value_t v ) { // Smart hide objects edit if( &cursor_hide_range == comp ) { env_t::cursor_hide_range = cursor_hide_range.get_value(); } // Hide building if( &hide_buildings == comp ) { env_t::hide_buildings = (uint8)v.i; world()->set_dirty(); } if( comp == &factory_tooltip ) { env_t::show_factory_storage_bar = (uint8)v.i; world()->set_dirty(); return true; } return true; } void transparency_settings_t::draw( scr_coord offset ) { hide_buildings.set_selection( env_t::hide_buildings ); gui_aligned_container_t::draw(offset); } station_settings_t::station_settings_t() { set_table_layout( 2, 0 ); // Transparent station coverage buttons[ IDBTN_TRANSPARENT_STATION_COVERAGE ].init( button_t::square_state, "transparent station coverage" ); buttons[ IDBTN_TRANSPARENT_STATION_COVERAGE ].pressed = env_t::use_transparency_station_coverage; add_component( buttons + IDBTN_TRANSPARENT_STATION_COVERAGE, 2 ); // Show station coverage buttons[ IDBTN_SHOW_STATION_COVERAGE ].init( button_t::square_state, "show station coverage" ); add_component( buttons + IDBTN_SHOW_STATION_COVERAGE, 2 ); // Show station coverage buttons[IDBTN_HIDE_LABELS].init(button_t::square_state, "Hide labels"); add_component(buttons + IDBTN_HIDE_LABELS, 2); // Show station names arrow add_table( 2, 1 ); { buttons[ IDBTN_LABEL_STYLE_ARROW ].set_typ( button_t::arrowright ); buttons[ IDBTN_LABEL_STYLE_ARROW ].set_tooltip( "Change label style" ); add_component( buttons + IDBTN_LABEL_STYLE_ARROW ); new_component( "Change label style" ); } end_table(); new_component(); // Show waiting bars checkbox buttons[ IDBTN_SHOW_WAITING_BARS ].init( button_t::square_state, "show waiting bars" ); buttons[ IDBTN_SHOW_WAITING_BARS ].pressed = env_t::show_names & 2; add_component( buttons + IDBTN_SHOW_WAITING_BARS, 2 ); } traffic_settings_t::traffic_settings_t() { set_table_layout( 2, 0 ); // Pedestrians in towns checkbox buttons[IDBTN_PEDESTRIANS_IN_TOWNS].init(button_t::square_state, "6LIGHT_CHOOSE"); buttons[IDBTN_PEDESTRIANS_IN_TOWNS].pressed = env_t::random_pedestrians; add_component(buttons+IDBTN_PEDESTRIANS_IN_TOWNS, 2); // Pedestrians at stops checkbox buttons[IDBTN_PEDESTRIANS_AT_STOPS].init(button_t::square_state, "5LIGHT_CHOOSE"); buttons[IDBTN_PEDESTRIANS_AT_STOPS].pressed = env_t::stop_pedestrians; add_component(buttons+IDBTN_PEDESTRIANS_AT_STOPS, 2); // Traffic density label new_component("6WORLD_CHOOSE"); // Traffic density edit traffic_density.set_value(world()->get_settings().get_traffic_level()); traffic_density.set_limits( 0, 16 ); traffic_density.add_listener(this); add_component(&traffic_density); // Convoy tooltip convoy_tooltip.set_focusable(false); convoy_tooltip.new_component(translator::translate("convoi error tooltips"), SYSCOL_TEXT); convoy_tooltip.new_component(translator::translate("convoi mouseover tooltips"), SYSCOL_TEXT); convoy_tooltip.new_component(translator::translate("line name mouseover tooltips"), SYSCOL_TEXT); convoy_tooltip.new_component(translator::translate("all convoi tooltips"), SYSCOL_TEXT); convoy_tooltip.set_selection(env_t::show_vehicle_states); add_component(&convoy_tooltip, 2); convoy_tooltip.add_listener(this); // Convoy follow mode new_component("Convoi following mode"); follow_mode.set_focusable(false); follow_mode.new_component(translator::translate("None"), SYSCOL_TEXT); follow_mode.new_component(translator::translate("underground mode"), SYSCOL_TEXT); follow_mode.new_component(translator::translate("sliced underground mode"), SYSCOL_TEXT); follow_mode.set_selection(env_t::follow_convoi_underground); add_component(&follow_mode); follow_mode.add_listener(this); // Show schedule's stop checkbox buttons[IDBTN_SHOW_SCHEDULES_STOP].init( button_t::square_state , "Highlite schedule" ); add_component(buttons+IDBTN_SHOW_SCHEDULES_STOP, 2); // convoi booking message options money_booking.set_focusable( false ); money_booking.new_component(translator::translate("Show all revenue messages"), SYSCOL_TEXT ); money_booking.new_component(translator::translate("Show only player's revenue"), SYSCOL_TEXT ); money_booking.new_component(translator::translate("Show no revenue messages"), SYSCOL_TEXT ); money_booking.set_selection( env_t::show_money_message ); add_component(&money_booking, 2); money_booking.add_listener(this); } bool traffic_settings_t::action_triggered( gui_action_creator_t *comp, value_t v ) { // Traffic density edit if( &traffic_density == comp ) { if( !env_t::networkmode || world()->get_active_player_nr() == PLAYER_PUBLIC_NR ) { static char level[ 16 ]; sprintf( level, "%li", v.i ); tool_t::simple_tool[ TOOL_TRAFFIC_LEVEL & 0xFFF ]->set_default_param( level ); world()->set_tool( tool_t::simple_tool[ TOOL_TRAFFIC_LEVEL & 0xFFF ], world()->get_active_player() ); } else { traffic_density.set_value( world()->get_settings().get_traffic_level() ); } } // Convoy tooltip if( &convoy_tooltip == comp ) { env_t::show_vehicle_states = (uint8)v.i; } if( &follow_mode == comp ) { env_t::follow_convoi_underground = (uint8)v.i; } if( &money_booking == comp ) { env_t::show_money_message = (sint8)v.i; } return true; } color_gui_t::color_gui_t() : gui_frame_t( translator::translate( "Display settings" ) ), scrolly_gui(&gui_settings), scrolly_map(&map_settings), scrolly_transparency(&transparency_settings), scrolly_station(&station_settings), scrolly_traffic(&traffic_settings) { set_table_layout( 1, 0 ); scrolly_gui.set_scroll_amount_y(D_BUTTON_HEIGHT/2); scrolly_map.set_scroll_amount_y(D_BUTTON_HEIGHT/2); scrolly_transparency.set_scroll_amount_y(D_BUTTON_HEIGHT/2); scrolly_station.set_scroll_amount_y(D_BUTTON_HEIGHT/2); scrolly_traffic.set_scroll_amount_y(D_BUTTON_HEIGHT/2); tabs.add_tab(&scrolly_gui, translator::translate("GUI settings")); tabs.add_tab(&scrolly_map, translator::translate("map view")); tabs.add_tab(&scrolly_transparency, translator::translate("transparencies")); tabs.add_tab(&scrolly_station, translator::translate("station labels")); tabs.add_tab(&scrolly_traffic, translator::translate("traffic settings")); add_component(&tabs); for( int i = 0; i < COLORS_MAX_BUTTONS; i++ ) { buttons[ i ].add_listener( this ); } gui_settings.toolbar_pos.add_listener( this ); gui_settings.single_toolbar.add_listener(this); gui_settings.stack_toolbars.add_listener(this); gui_settings.reselect_closes_tool.add_listener(this); gui_settings.fullscreen.add_listener( this ); gui_settings.borderless.add_listener( this ); set_resizemode(diagonal_resize); set_min_windowsize( gui_settings.get_min_size()+scr_size(0,D_TAB_HEADER_HEIGHT) ); set_windowsize( scr_size(get_min_windowsize().w, map_settings.get_min_size().h) ); resize( scr_coord( 0, 0 ) ); } bool color_gui_t::action_triggered( gui_action_creator_t *comp, value_t) { if( comp == &gui_settings.toolbar_pos ) { env_t::menupos++; env_t::menupos &= 3; switch (env_t::menupos) { case MENU_TOP: gui_settings.toolbar_pos.set_typ(button_t::arrowup); break; case MENU_LEFT: gui_settings.toolbar_pos.set_typ(button_t::arrowleft); break; case MENU_BOTTOM: gui_settings.toolbar_pos.set_typ(button_t::arrowdown); break; case MENU_RIGHT: gui_settings.toolbar_pos.set_typ(button_t::arrowright); break; } welt->set_dirty(); // move all windows event_t* ev = new event_t(); ev->ev_class = EVENT_SYSTEM; ev->ev_code = SYSTEM_RELOAD_WINDOWS; queue_event(ev); return true; } if( comp == &gui_settings.fullscreen ) { gui_settings.fullscreen.pressed = !gui_settings.fullscreen.pressed; env_t::fullscreen = gui_settings.fullscreen.pressed; gui_settings.borderless.pressed = false; return true; } if( comp == &gui_settings.borderless ) { env_t::fullscreen = dr_toggle_borderless(); gui_settings.borderless.pressed = dr_get_fullscreen(); gui_settings.fullscreen.pressed = false; return true; } if( comp == &gui_settings.reselect_closes_tool ) { env_t::reselect_closes_tool = !env_t::reselect_closes_tool; gui_settings.reselect_closes_tool.pressed = env_t::reselect_closes_tool; return true; } if( comp == &gui_settings.single_toolbar ) { env_t::single_toolbar_mode = !env_t::single_toolbar_mode; gui_settings.single_toolbar.pressed = env_t::single_toolbar_mode; return true; } if( comp == &gui_settings.stack_toolbars ) { env_t::stack_toolbars = !env_t::stack_toolbars; gui_settings.stack_toolbars.pressed = env_t::stack_toolbars; return true; } int i; for( i=0; iset_tool( tool_t::simple_tool[ TOOL_TOOGLE_PAX & 0xFFF ], welt->get_active_player() ); break; case IDBTN_PEDESTRIANS_IN_TOWNS: welt->set_tool( tool_t::simple_tool[ TOOL_TOOGLE_PEDESTRIANS & 0xFFF ], welt->get_active_player() ); break; case IDBTN_HIDE_TREES: env_t::hide_trees = !env_t::hide_trees; baum_t::recalc_outline_color(); break; case IDBTN_DAY_NIGHT_CHANGE: env_t::night_shift = !env_t::night_shift; break; case IDBTN_TRANSPARENT_INSTEAD_OF_HIDDEN: env_t::hide_with_transparency = !env_t::hide_with_transparency; baum_t::recalc_outline_color(); break; case IDBTN_TRANSPARENT_STATION_COVERAGE: env_t::use_transparency_station_coverage = !env_t::use_transparency_station_coverage; break; case IDBTN_SHOW_STATION_COVERAGE: env_t::station_coverage_show = env_t::station_coverage_show == 0 ? 0xFF : 0; break; case IDBTN_UNDERGROUND_VIEW: // see tool/simtool.cc::tool_show_underground_t::init grund_t::set_underground_mode( buttons[ IDBTN_UNDERGROUND_VIEW ].pressed ? grund_t::ugm_none : grund_t::ugm_all, map_settings.inp_underground_level.get_value() ); // calc new images welt->update_underground(); // renew toolbar tool_t::update_toolbars(); break; case IDBTN_SHOW_GRID: grund_t::toggle_grid(); break; case IDBTN_LABEL_STYLE_ARROW: { int label_style = ((env_t::show_names >> 2)+1)%3; env_t::show_names = (env_t::show_names & 3) + (label_style << 2); } break; case IDBTN_SHOW_WAITING_BARS: env_t::show_names ^= 2; break; case IDBTN_HIDE_LABELS: env_t::show_names ^= 1; break; case IDBTN_SHOW_SLICE_MAP_VIEW: // see tool/simtool.cc::tool_show_underground_t::init grund_t::set_underground_mode( buttons[ IDBTN_SHOW_SLICE_MAP_VIEW ].pressed ? grund_t::ugm_none : grund_t::ugm_level, map_settings.inp_underground_level.get_value() ); // calc new images welt->update_underground(); // renew toolbar tool_t::update_toolbars(); break; case IDBTN_HIDE_BUILDINGS: // see tool/simtool.cc::tool_hide_under_cursor_t::init env_t::hide_under_cursor = !env_t::hide_under_cursor && env_t::cursor_hide_range > 0; // renew toolbar tool_t::update_toolbars(); break; case IDBTN_SHOW_SCHEDULES_STOP: env_t::visualize_schedule = !env_t::visualize_schedule; break; case IDBTN_SHOW_THEMEMANAGER: create_win( new themeselector_t(), w_info, magic_themes ); break; case IDBTN_SIMPLE_DRAWING: env_t::simple_drawing = !env_t::simple_drawing; break; case IDBTN_CHANGE_FONT: create_win( new loadfont_frame_t(), w_info, magic_font ); break; default: assert( 0 ); } welt->set_dirty(); return true; } void color_gui_t::draw(scr_coord pos, scr_size size) { // Update button states that was changed with keyboard ... buttons[IDBTN_PEDESTRIANS_AT_STOPS].pressed = env_t::stop_pedestrians; buttons[IDBTN_PEDESTRIANS_IN_TOWNS].pressed = env_t::random_pedestrians; buttons[IDBTN_HIDE_TREES].pressed = env_t::hide_trees; buttons[IDBTN_HIDE_BUILDINGS].pressed = env_t::hide_under_cursor; buttons[IDBTN_SHOW_STATION_COVERAGE].pressed = env_t::station_coverage_show; buttons[IDBTN_UNDERGROUND_VIEW].pressed = grund_t::underground_mode == grund_t::ugm_all; buttons[IDBTN_SHOW_GRID].pressed = grund_t::show_grid; buttons[IDBTN_SHOW_WAITING_BARS].pressed = (env_t::show_names&2)!=0; buttons[IDBTN_HIDE_LABELS].pressed = (env_t::show_names & 1) == 0; buttons[IDBTN_SHOW_SLICE_MAP_VIEW].pressed = grund_t::underground_mode == grund_t::ugm_level; buttons[IDBTN_SHOW_SCHEDULES_STOP].pressed = env_t::visualize_schedule; buttons[IDBTN_SIMPLE_DRAWING].pressed = env_t::simple_drawing; buttons[IDBTN_SIMPLE_DRAWING].enable(welt->is_paused()); buttons[IDBTN_SCROLL_INVERSE].pressed = env_t::scroll_multi < 0; buttons[IDBTN_INFINITE_SCROLL].pressed = env_t::scroll_infinite; buttons[IDBTN_DAY_NIGHT_CHANGE].pressed = env_t::night_shift; buttons[IDBTN_SHOW_SLICE_MAP_VIEW].pressed = grund_t::underground_mode == grund_t::ugm_level; buttons[IDBTN_UNDERGROUND_VIEW].pressed = grund_t::underground_mode == grund_t::ugm_all; buttons[IDBTN_TRANSPARENT_STATION_COVERAGE].pressed = env_t::use_transparency_station_coverage; buttons[IDBTN_TRANSPARENT_INSTEAD_OF_HIDDEN].pressed = env_t::hide_with_transparency; // All components are updated, now draw them... gui_frame_t::draw(pos, size); } void color_gui_t::rdwr(loadsave_t *f) { tabs.rdwr(f); scrolly_gui.rdwr(f); scrolly_map.rdwr(f); scrolly_transparency.rdwr(f); scrolly_station.rdwr(f); scrolly_traffic.rdwr(f); } simutrans-124.5/src/simutrans/gui/display_settings.h000066400000000000000000000057111520675460500227260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_DISPLAY_SETTINGS_H #define GUI_DISPLAY_SETTINGS_H #include "simwin.h" #include "gui_frame.h" #include "components/gui_divider.h" #include "components/gui_label.h" #include "components/gui_button.h" #include "components/gui_numberinput.h" #include "components/gui_combobox.h" #include "components/gui_tab_panel.h" /** * Menu with display settings */ class gui_settings_t : public gui_aligned_container_t, public action_listener_t { private: gui_numberinput_t screen_scale_numinp; button_t icon_scale_down, icon_scale_up; button_t screen_scale_auto; scr_coord_val base_icon_height; uint icon_zoom; gui_label_buf_t frame_time_value_label, idle_time_value_label, fps_value_label, simloops_value_label; public: button_t toolbar_pos, reselect_closes_tool, single_toolbar, stack_toolbars, fullscreen, borderless; gui_settings_t(); void draw( scr_coord offset ) OVERRIDE; bool action_triggered( gui_action_creator_t *comp, value_t v) OVERRIDE; }; class map_settings_t : public gui_aligned_container_t, public action_listener_t { private: char time_str[8][64]; gui_combobox_t time_setting; public: gui_numberinput_t inp_underground_level, brightness, scrollspeed, scroll_threshold; map_settings_t(); bool action_triggered( gui_action_creator_t *comp, value_t v ) OVERRIDE; }; class station_settings_t : public gui_aligned_container_t { public: station_settings_t(); }; class transparency_settings_t : public gui_aligned_container_t, public action_listener_t { private: gui_numberinput_t cursor_hide_range; gui_combobox_t hide_buildings; gui_combobox_t factory_tooltip; public: transparency_settings_t(); bool action_triggered( gui_action_creator_t *comp, value_t v ) OVERRIDE; void draw(scr_coord offset) OVERRIDE; }; class traffic_settings_t : public gui_aligned_container_t, public action_listener_t { private: gui_numberinput_t traffic_density; gui_combobox_t convoy_tooltip, money_booking, follow_mode; public: traffic_settings_t(); bool action_triggered( gui_action_creator_t *comp, value_t v ) OVERRIDE; }; /** * Display settings dialog */ class color_gui_t : public gui_frame_t, private action_listener_t { private: gui_settings_t gui_settings; map_settings_t map_settings; transparency_settings_t transparency_settings; station_settings_t station_settings; traffic_settings_t traffic_settings; gui_scrollpane_t scrolly_gui, scrolly_map, scrolly_transparency, scrolly_station, scrolly_traffic; gui_tab_panel_t tabs; public: color_gui_t(); /** * Some windows have associated help text. * @return The help file name or NULL */ const char * get_help_filename() const OVERRIDE { return "display.txt"; } void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_color_gui_t; } void rdwr(loadsave_t*) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/enlarge_map_frame.cc000066400000000000000000000140611520675460500231210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "enlarge_map_frame.h" #include "minimap.h" #include "welt.h" #include "components/gui_divider.h" #include "../simdebug.h" #include "../world/simworld.h" #include "simwin.h" #include "../display/simimg.h" #include "../dataobj/settings.h" #include "../dataobj/translator.h" #include "../simcolor.h" #include "../display/simgraph.h" #include "../utils/simrandom.h" koord enlarge_map_frame_t::koord_from_rotation(settings_t const* const sets, sint16 const x, sint16 const y, sint16 const w, sint16 const h) { koord offset( sets->get_origin_x(), sets->get_origin_y() ); switch( sets->get_rotation() ) { default: case 0: return offset+koord(x,y); case 1: return offset+koord(y,w-x); case 2: return offset+koord(w-x,h-y); case 3: return offset+koord(h-y,x); } } enlarge_map_frame_t::enlarge_map_frame_t() : gui_frame_t( translator::translate("enlarge map") ), sets(new settings_t(welt->get_settings())), // Make a copy. map(MAP_PREVIEW_SIZE_X-2, MAP_PREVIEW_SIZE_Y-2) { sets->set_size_x(welt->get_size().x); sets->set_size_y(welt->get_size().y); changed_number_of_towns = false; // Component creation set_table_layout(1,0); // top part: preview, maps size add_table(2,1); { // input fields add_table(2,2); { // map seed number label map_number_label.init(); map_number_label.buf().printf("%s %d", translator::translate("2WORLD_CHOOSE"), welt->get_settings().get_map_number()); map_number_label.update(); add_component(&map_number_label); // Map X size edit inp_x_size.init( sets->get_size_x(), welt->get_size().x, 32766, sets->get_size_x()>=512 ? 128 : 64, false ); inp_x_size.add_listener(this); add_component( &inp_x_size ); // Map size label size_label.init(); size_label.buf().printf(translator::translate("Size (%d MB):"), 9999); size_label.update(); add_component( &size_label ); // Map size Y edit inp_y_size.init( sets->get_size_y(), sets->get_size_y(), 32766, sets->get_size_y()>=512 ? 128 : 64, false ); inp_y_size.add_listener(this); add_component( &inp_y_size ); } end_table(); // Map preview (will be initialized in update_preview add_component( &map_preview ); } end_table(); // specify map parameters add_table(2,0); { // Number of towns new_component("5WORLD_CHOOSE"); inp_number_of_towns.add_listener(this); inp_number_of_towns.init(abs(sets->get_city_count()), 0, 999); add_component( &inp_number_of_towns ); // Town size new_component("Median Citizen per town"); inp_town_size.add_listener(this); inp_town_size.set_limits(0,999999); inp_town_size.set_increment_mode(50); inp_town_size.set_value( sets->get_mean_citizen_count() ); add_component( &inp_town_size ); } end_table(); new_component(); // start game start_button.init( button_t::roundbox | button_t::flexible, "enlarge map"); start_button.add_listener( this ); add_component( &start_button ); welt_gui_t::update_memory(&size_label, sets); update_preview(); reset_min_windowsize(); set_windowsize(get_min_windowsize()); } enlarge_map_frame_t::~enlarge_map_frame_t() { delete sets; } /** * This method is called if an action is triggered */ bool enlarge_map_frame_t::action_triggered( gui_action_creator_t *comp,value_t v) { if(comp==&inp_x_size) { sets->set_size_x( v.i ); inp_x_size.set_increment_mode( v.i>=64 ? (v.i>=512 ? 128 : 64) : 8 ); update_preview(); } else if(comp==&inp_y_size) { sets->set_size_y( v.i ); inp_y_size.set_increment_mode( v.i>=64 ? (v.i>=512 ? 128 : 64) : 8 ); update_preview(); } else if(comp==&inp_number_of_towns) { sets->set_city_count( v.i ); } else if(comp==&inp_town_size) { sets->set_mean_citizen_count( v.i ); } else if(comp==&start_button) { destroy_all_win( true ); welt->enlarge_map(sets, NULL); } else { return false; } return true; } void enlarge_map_frame_t::draw(scr_coord pos, scr_size size) { while (welt->get_settings().get_rotation() != sets->get_rotation()) { // map was rotated while we are active ... => rotate too! sets->rotate90(); sets->set_size( sets->get_size_y(), sets->get_size_x() ); update_preview(); } gui_frame_t::draw(pos, size); } /** * Calculate the new Map-Preview. Initialize the new RNG! */ void enlarge_map_frame_t::update_preview() { // reset noise seed setsimrand(0xFFFFFFFF, welt->get_settings().get_map_number()); // "welt" still knows the old size. The new size is saved in "sets". uint16 old_x = welt->get_size().x; uint16 old_y = welt->get_size().y; uint16 pre_x = min(sets->get_size_x(), map.get_width()); uint16 pre_y = min(sets->get_size_y(), map.get_height()); const int mx = sets->get_size_x()/pre_x; const int my = sets->get_size_y()/pre_y; for( int j=0; jpalette_lookup(COL_WHITE); } else { const sint16 height = welt->lookup_hgt( pos ); color = minimap_t::calc_height_color(height, sets->get_groundwater()); } } else { // new part const sint16 height = karte_t::perlin_hoehe(sets, pos, koord(old_x,old_y) ); color = minimap_t::calc_height_color(height, sets->get_groundwater()); } map.at(i,j) = color; } } for( uint j=0; jpalette_lookup(COL_GREY1); } } map_preview.set_map_data(&map); sets->heightfield = ""; if(!changed_number_of_towns){// Interpolate number of towns. sint32 new_area = sets->get_size_x() * sets->get_size_y(); sint32 old_area = old_x * old_y; sint32 const towns = welt->get_settings().get_city_count(); sets->set_city_count( towns * new_area / old_area - towns ); inp_number_of_towns.set_value(abs(sets->get_city_count()) ); } // guess the new memory needed welt_gui_t::update_memory(&size_label, sets); } simutrans-124.5/src/simutrans/gui/enlarge_map_frame.h000066400000000000000000000032611520675460500227630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_ENLARGE_MAP_FRAME_H #define GUI_ENLARGE_MAP_FRAME_H #include "gui_frame.h" #include "components/gui_label.h" #include "components/action_listener.h" #include "components/gui_numberinput.h" #include "components/gui_map_preview.h" class settings_t; /* * Dialogue to increase map size. */ class enlarge_map_frame_t : public gui_frame_t, private action_listener_t { private: // local settings of the new world ... settings_t* sets; /** * Mini Map-Preview */ array2d_tpl map; gui_map_preview_t map_preview; bool changed_number_of_towns; gui_numberinput_t inp_x_size, inp_y_size, inp_number_of_towns, inp_town_size; /* * Label to display current map seed number. */ gui_label_buf_t map_number_label; button_t start_button; gui_label_buf_t size_label; // memory requirement public: static inline koord koord_from_rotation(settings_t const*, sint16 y, sint16 x, sint16 w, sint16 h); enlarge_map_frame_t(); ~enlarge_map_frame_t(); /** * Calculate the new Map-Preview. Initialize the new RNG! * public, because also the climate dialog need it */ void update_preview(); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE { return "enlarge_map.txt";} /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/extend_edit.cc000066400000000000000000000231411520675460500217700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simworld.h" #include "../simevent.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../descriptor/ground_desc.h" #include "../player/simplay.h" #include "extend_edit.h" gui_rotation_item_t::gui_rotation_item_t(uint8 r) : gui_scrolled_list_t::const_text_scrollitem_t(NULL, SYSCOL_TEXT) { rotation = r; switch(rotation) { case 0: text = translator::translate("[0] south-facing"); break; case 1: text = translator::translate("[1] east-facing"); break; case 2: text = translator::translate("[2] north-facing"); break; case 3: text = translator::translate("[3] west-facing"); break; case 4: text = translator::translate("[4] southeast corner"); break; case 5: text = translator::translate("[5] northeast corner"); break; case 6: text = translator::translate("[6] northwest corner"); break; case 7: text = translator::translate("[7] southwest corner"); break; case automatic: text = translator::translate("auto"); break; case random: text = translator::translate("random"); break; default: text = ""; } } gui_climates_item_t::gui_climates_item_t(uint8 c) : gui_scrolled_list_t::const_text_scrollitem_t(NULL, SYSCOL_TEXT) { if(c(); // right column add_component(&cont_right,2); cont_right.set_table_layout(1,0); // add object settings (rotations, ...) cont_right.add_component(&cont_options); cont_options.set_table_layout(1,0); // add divider element cont_right.new_component(); // add scrollable element cont_right.add_component(&scrolly); cont_right.new_component(); //cont scrolly is already in scrolly cont_scrolly.set_table_layout(2,0); cont_scrolly.set_margin( scr_size( 0, D_V_SPACE ), scr_size( 0, D_V_SPACE ) ); // add object description cont_scrolly.add_component(&info_text,2); // add object image cont_scrolly.add_component(&building_image); cont_scrolly.new_component(true,false); cont_scrolly.new_component_span(false,true,2); //end of layouting. Now fill elements // init scrolled list scl.set_selection(-1); scl.add_listener(this); scl.set_min_width( (D_DEFAULT_WIDTH-D_MARGIN_LEFT-D_MARGIN_RIGHT-2*D_H_SPACE)/2 ); // start filling cont_timeline--------------------------------------------------------------------------------------------- bt_timeline.init( button_t::square_state, "timeline"); bt_timeline.pressed = welt->get_settings().get_use_timeline(); bt_timeline.add_listener(this); cont_timeline.add_component(&bt_timeline, 4); bt_timeline_custom.init( button_t::square_state, "Available at custom date"); bt_timeline_custom.add_listener(this); cont_timeline.add_component(&bt_timeline_custom, 4); // respect year/month order according to language settings bool year_month_order = ( env_t::show_month == env_t::DATE_FMT_JAPANESE || env_t::show_month == env_t::DATE_FMT_JAPANESE_NO_SEASON ); if( !year_month_order ) { // month first then year cont_timeline.new_component("Month"); cont_timeline.add_component(&ni_timeline_month); } cont_timeline.new_component("Year"); cont_timeline.add_component(&ni_timeline_year); if( year_month_order ) { // year first then month cont_timeline.new_component("Month"); cont_timeline.add_component(&ni_timeline_month); } ni_timeline_month.init( (sint32)(welt->get_current_month()%12+1), 1, 12, 1, true ); ni_timeline_month.add_listener(this); ni_timeline_year.init( (sint32)(welt->get_current_month()/12), 0, 2999, 1, false ); ni_timeline_year.add_listener(this); bt_obsolete.init( button_t::square_state, "Show obsolete"); bt_obsolete.add_listener(this); cont_timeline.add_component(&bt_obsolete, 4); // end filling cont_timeline--------------------------------------------------------------------------------------------- // start filling cont_filter--------------------------------------------------------------------------------------------- // climate filter cont_filter.new_component("Climate"); cont_filter.add_component(&cb_climates, 2); cb_climates.add_listener(this); cb_climates.new_component(climate::MAX_CLIMATES); for(uint8 i=climate::desert_climate; i(i); } cb_climates.set_selection(0); // Sorting box cont_filter.new_component("Sort by"); cont_filter.add_component(&cb_sortedby); sort_order.init(button_t::sortarrow_state, ""); sort_order.set_tooltip(translator::translate("hl_btn_sort_order")); sort_order.add_listener(this); sort_order.pressed = false; cont_filter.add_component(&sort_order); cb_sortedby.add_listener(this); cb_sortedby.new_component(gui_sorting_item_t::BY_NAME_TRANSLATED); cb_sortedby.new_component(gui_sorting_item_t::BY_NAME_OBJECT); cb_sortedby.set_selection(0); // end filling cont_filter--------------------------------------------------------------------------------------------- //filling cont_options bt_climates.init( button_t::square_state, "ignore climates"); bt_climates.add_listener(this); cont_options.add_component(&bt_climates); //setting scrollable content box scrolly.set_visible(true); scrolly.set_min_width( (D_DEFAULT_WIDTH-D_MARGIN_LEFT-D_MARGIN_RIGHT-2*D_H_SPACE)/2 ); } /** * Mouse click are hereby reported to its GUI-Components */ bool extend_edit_gui_t::infowin_event(const event_t *ev) { if(ev->ev_class == INFOWIN && ev->ev_code == WIN_CLOSE) { change_item_info(-1); } return gui_frame_t::infowin_event(ev); } // resize flowtext to avoid horizontal scrollbar void extend_edit_gui_t::set_windowsize( scr_size s ) { gui_frame_t::set_windowsize( s ); info_text.set_width( scrolly.get_client().w ); } bool extend_edit_gui_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { if (comp == &scl) { // select an item of scroll list ? change_item_info(scl.get_selection()); } else if( comp==&bt_obsolete ) { bt_obsolete.pressed ^= 1; fill_list(); } else if( comp==&bt_climates ) { bt_climates.pressed ^= 1; fill_list(); } else if( comp==&bt_timeline ) { bt_timeline_custom.pressed = false; bt_timeline.pressed ^= 1; fill_list(); } else if ( comp==&bt_timeline_custom ) { bt_timeline.pressed = false; bt_timeline_custom.pressed ^= 1; fill_list(); } else if ( comp==&ni_timeline_year && bt_timeline_custom.pressed) { fill_list(); } else if ( comp==&ni_timeline_month && bt_timeline_custom.pressed) { fill_list(); } else if( comp==&cb_climates ) { fill_list(); change_item_info(scl.get_selection()); } else if( comp==&cb_sortedby ) { fill_list(); } else if( comp == &sort_order ) { sort_order.pressed = !sort_order.pressed; fill_list(); } return true; } uint8 extend_edit_gui_t::get_rotation() const { if (gui_rotation_item_t *item = dynamic_cast( cb_rotation.get_selected_item() ) ) { return item->get_rotation(); } return 0; } uint8 extend_edit_gui_t::get_climate() const { if (gui_climates_item_t *item = dynamic_cast( cb_climates.get_selected_item() ) ) { return item->get_climate(); } return ALL_CLIMATES; } uint8 extend_edit_gui_t::get_sortedby() const { if (gui_sorting_item_t *item = dynamic_cast( cb_sortedby.get_selected_item() ) ) { return item->get_sortedby(); } return ALL_CLIMATES; } simutrans-124.5/src/simutrans/gui/extend_edit.h000066400000000000000000000070741520675460500216410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_EXTEND_EDIT_H #define GUI_EXTEND_EDIT_H #include "gui_frame.h" #include "components/gui_textinput.h" #include "components/gui_scrolled_list.h" #include "components/gui_scrollpane.h" #include "components/gui_tab_panel.h" #include "components/gui_button.h" #include "components/gui_image.h" #include "components/gui_fixedwidth_textarea.h" #include "components/gui_building.h" #include "components/gui_combobox.h" #include "components/gui_numberinput.h" #include "components/gui_divider.h" #include "../utils/cbuffer.h" #include "../simtypes.h" class player_t; /** * Entries for rotation selection. */ class gui_rotation_item_t : public gui_scrolled_list_t::const_text_scrollitem_t { private: const char *text; uint8 rotation; ///< 0-3, 255 = random public: enum special_rotations_t { automatic = 254, random = 255 }; gui_rotation_item_t(uint8 r); char const* get_text () const OVERRIDE { return text; } sint8 get_rotation() const { return rotation; } }; /** * Entries for climate selection. */ class gui_climates_item_t : public gui_scrolled_list_t::const_text_scrollitem_t { private: const char *text; uint8 climate_; public: gui_climates_item_t(uint8 r); char const* get_text () const OVERRIDE { return text; } sint8 get_climate() const { return climate_; } }; /** * Entries for sorting selection. */ class gui_sorting_item_t : public gui_scrolled_list_t::const_text_scrollitem_t { private: const char *text; uint8 sorted_by; public: enum sorting_options { BY_NAME_TRANSLATED, BY_NAME_OBJECT, BY_LEVEL_PAX, BY_LEVEL_MAIL, BY_DATE_INTRO, BY_DATE_RETIRE, BY_SIZE, BY_COST, BY_GOODS_NUMBER, BY_REMOVAL }; gui_sorting_item_t(uint8 r); char const* get_text () const OVERRIDE { return text; } sint8 get_sortedby() const { return sorted_by; } }; /** * Base class map editor dialogues to select object to place on map. */ class extend_edit_gui_t : public gui_frame_t, public action_listener_t { protected: player_t *player; /// cont_left: left column, cont_right: right column gui_aligned_container_t cont_left, cont_right; /// cont_filter: Settings about the content of the list (above list) /// cont_timeline: timeline-related filter settings /// cont_options: Settings about the active object (eg. rotation) /// cont_scrolly: the scrollable container (image + desc) gui_aligned_container_t cont_filter, cont_options, cont_timeline, cont_scrolly; cbuffer_t buf; gui_fixedwidth_textarea_t info_text; //container for object description gui_scrollpane_t scrolly; gui_scrolled_list_t scl; //image gui_building_t building_image; button_t bt_obsolete, bt_timeline, bt_climates, bt_timeline_custom, sort_order; // we make this available for child classes gui_combobox_t cb_rotation, cb_climates, cb_sortedby; gui_numberinput_t ni_timeline_year, ni_timeline_month; virtual void fill_list() {} virtual void change_item_info( sint32 /*entry, -1= none */ ) {} /** * @returns selected rotation of cb_rotation. * assumes that cb_rotation only contains gui_rotation_item_t-items. * defaults to zero. */ uint8 get_rotation() const; /** * @returns selected climate of cb_climates. */ uint8 get_climate() const; /** * @returns selected sorting method. */ uint8 get_sortedby() const; public: extend_edit_gui_t(const char *name, player_t* player_); void set_windowsize( scr_size s ) OVERRIDE; bool infowin_event(event_t const*) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/fabrik_info.cc000066400000000000000000000301311520675460500217420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "fabrik_info.h" #include "components/gui_label.h" #include "help_frame.h" #include "factory_chart.h" #include "../simfab.h" #include "../simcolor.h" #include "../display/simgraph.h" #include "../display/viewport.h" #include "../world/simcity.h" #include "simwin.h" #include "../tool/simmenu.h" #include "../player/simplay.h" #include "../world/simworld.h" #include "../simskin.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../utils/simstring.h" /** * Helper class: one row of entries in consumer / supplier table */ class gui_factory_connection_t : public gui_aligned_container_t { const fabrik_t *fab; koord target; bool supplier; gui_image_t* image; bool is_active() const { if (supplier) { if( const fabrik_t *src = fabrik_t::get_fab(target) ) { return src->is_active_consumer(fab->get_pos().get_2d()); } } else { return fab->is_active_consumer(target); } return false; } public: gui_factory_connection_t(const fabrik_t* f, koord t, bool s) : fab(f), target(t), supplier(s) { set_table_layout(3,1); button_t* b = new_component(); b->set_typ(button_t::posbutton_automatic); b->set_targetpos(target); image = new_component(skinverwaltung_t::goods->get_image_id(0)); image->enable_offset_removal(true); gui_label_buf_t* l = new_component(); if (const fabrik_t *other = fabrik_t::get_fab(target) ) { l->buf().printf("%s (%d,%d)", other->get_name(), target.x, target.y); l->update(); } } void draw(scr_coord offset) OVERRIDE { image->set_transparent(is_active() ? 0 : TRANSPARENT50_FLAG); gui_aligned_container_t::draw(offset); } }; fabrik_info_t::fabrik_info_t(fabrik_t* fab_, const gebaeude_t* gb) : gui_frame_t(""), fab(fab_), chart(NULL), view(scr_size( max(64, gfx->get_base_tile_raster_width()), max(56, (gfx->get_base_tile_raster_width() * 7) / 8))), prod(&prod_buf), txt(&info_buf), scroll_info(&container_info) { if (fab) { init(fab, gb); } } void fabrik_info_t::init(fabrik_t* fab_, const gebaeude_t* gb) { fab = fab_; // window name tstrncpy( fabname, fab->get_name(), lengthof(fabname) ); gui_frame_t::set_name(fab->get_name()); set_owner(fab->get_owner()); set_table_layout(1,0); // input name input.set_text( fabname, lengthof(fabname) ); input.set_notify_all_changes_delay(500); // since it triggers a tool input.add_listener(this); add_component(&input); highlight_suppliers.set_text("Suppliers"); highlight_suppliers.set_typ(button_t::roundbox_state); highlight_suppliers.add_listener(this); highlight_consumers.set_text("Abnehmer"); highlight_consumers.set_typ(button_t::roundbox_state); highlight_consumers.add_listener(this); // top part: production number & details, boost indicators, factory view add_table(6,0)->set_alignment(ALIGN_LEFT | ALIGN_TOP); { // production details per input/output fab->info_prod( prod_buf ); prod.recalc_size(); add_component( &prod ); new_component(); // indicator for possible boost by electricity, passengers, mail if (fab->get_desc()->get_electric_boost() ) { boost_electric.set_image(skinverwaltung_t::electricity->get_image_id(0), true); add_component(&boost_electric); } if (fab->get_desc()->get_pax_boost() ) { boost_passenger.set_image(skinverwaltung_t::passengers->get_image_id(0), true); add_component(&boost_passenger); } if (fab->get_desc()->get_mail_boost() ) { boost_mail.set_image(skinverwaltung_t::mail->get_image_id(0), true); add_component(&boost_mail); } // world view object with boost overlays add_table(1,0)->set_spacing( scr_size(0,0)); { add_component(&view); view.set_obj(gb); add_component(&indicator_color); indicator_color.set_max_size(view.get_max_size()); } end_table(); } end_table(); // tab panel: connections, chart panels, details add_component(&switch_mode); switch_mode.add_tab(&scroll_info, translator::translate("Connections")); // connection information container_info.set_table_layout(1,0); container_info.add_component(&all_suppliers); container_info.add_component(&all_consumers); container_info.add_component(&all_cities); container_info.add_component(&txt); fab->info_conn(info_buf); // initialize to zero, update_info will do the rest old_suppliers_count = 0; old_consumers_count = 0; old_stops_count = 0; old_cities_count = 0; update_info(); // take-over chart tabs into our chart.set_factory(fab); switch_mode.take_tabs(chart.get_tab_panel()); // factory description in tab { bool add_tab = false; details_buf.clear(); // factory details char key[256]; sprintf(key, "factory_%s_details", fab->get_desc()->get_name()); const char * value = translator::translate(key); if(value && *value != 'f') { details_buf.append(value); add_tab = true; } if (char const* const maker = fab->get_desc()->get_copyright()) { details_buf.append("

"); details_buf.printf(translator::translate("Constructed by %s"), maker); add_tab = true; } if (add_tab) { switch_mode.add_tab(&container_details, translator::translate("Details")); container_details.set_table_layout(1,0); gui_flowtext_t* f = container_details.new_component(); f->set_text( (const char*)details_buf); } } reset_min_windowsize(); set_windowsize(get_min_windowsize()); set_resizemode(gui_frame_t::diagonal_resize); } fabrik_info_t::~fabrik_info_t() { rename_factory(); fabname[0] = 0; if (highlight_consumers.pressed) { highlight(fab->get_consumer(), false); } if (highlight_suppliers.pressed) { highlight(fab->get_suppliers(), false); } } void fabrik_info_t::rename_factory() { if( fabname[0] && welt->get_fab_list().is_contained(fab) && strcmp(fabname, fab->get_name()) ) { // text changed and factory still exists => call tool cbuffer_t buf; buf.printf( "f%s,%s", fab->get_pos().get_str(), fabname ); tool_t *tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tool->set_default_param( buf ); welt->set_tool( tool, welt->get_public_player()); // since init always returns false, it is safe to delete immediately delete tool; } } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void fabrik_info_t::draw(scr_coord pos, scr_size size) { update_components(); // boost stuff boost_electric.set_transparent(fab->get_prodfactor_electric()>0 ? 0 : TRANSPARENT50_FLAG | OUTLINE_FLAG | SYSCOL_IMAGE_TRANSPARENCY); boost_passenger.set_transparent(fab->get_prodfactor_pax()>0 ? 0 : TRANSPARENT50_FLAG | OUTLINE_FLAG | SYSCOL_IMAGE_TRANSPARENCY); boost_mail.set_transparent(fab->get_prodfactor_mail()>0 ? 0 : TRANSPARENT50_FLAG | OUTLINE_FLAG | SYSCOL_IMAGE_TRANSPARENCY); indicator_color.set_color( gfx->palette_lookup(fabrik_t::status_to_color[fab->get_status()]) ); chart.update(); gui_frame_t::draw(pos, size); } bool fabrik_info_t::is_weltpos() { return ( welt->get_viewport()->is_on_center( get_weltpos(false) ) ); } /** * This method is called if an action is triggered * * Returns true, if action is done and no more * components should be triggered. */ bool fabrik_info_t::action_triggered( gui_action_creator_t *comp, value_t) { if( comp == &input ) { rename_factory(); return false; } else if (comp == &highlight_consumers) { highlight_consumers.pressed ^= 1; highlight(fab->get_consumer(), highlight_consumers.pressed); } else if (comp == &highlight_suppliers) { highlight_suppliers.pressed ^= 1; highlight(fab->get_suppliers(), highlight_suppliers.pressed); } return true; } void fabrik_info_t::map_rotate90(sint16) { // force update old_suppliers_count ++; old_consumers_count ++; old_stops_count ++; old_cities_count ++; update_components(); } // update name and buffers void fabrik_info_t::update_info() { tstrncpy( fabname, fab->get_name(), lengthof(fabname) ); gui_frame_t::set_name(fab->get_name()); input.set_text( fabname, lengthof(fabname) ); update_components(); } // update all buffers void fabrik_info_t::update_components() { // update texts fab->info_prod( prod_buf ); fab->info_conn( info_buf ); // consumers if( fab->get_consumer().get_count() != old_consumers_count ) { all_consumers.remove_all(); all_consumers.set_table_layout(1,0); all_consumers.set_margin(scr_size(0,0), scr_size(0,D_V_SPACE)); all_consumers.add_component(&highlight_consumers); for(koord k : fab->get_consumer() ) { all_consumers.new_component(fab, k, false); } old_consumers_count = fab->get_consumer().get_count(); } // suppliers if( fab->get_suppliers().get_count() != old_suppliers_count ) { all_suppliers.remove_all(); all_suppliers.set_table_layout(1,0); all_suppliers.set_margin(scr_size(0,0), scr_size(0,D_V_SPACE)); all_suppliers.add_component(&highlight_suppliers); for(koord k : fab->get_suppliers() ) { if( const fabrik_t *src = fabrik_t::get_fab(k) ) { all_suppliers.new_component(fab, src->get_pos().get_2d(), true); } } old_suppliers_count = fab->get_suppliers().get_count(); } // cities if( fab->get_target_cities().get_count() != old_cities_count ) { all_cities.remove_all(); all_cities.set_table_layout(6,0); all_cities.set_margin(scr_size(0,0), scr_size(0,D_V_SPACE)); all_cities.new_component_span(fab->is_end_consumer() ? "Customers live in:" : "Arbeiter aus:", 6); // no new class for entries to get better alignment for columns for(stadt_t* const c : fab->get_target_cities()) { button_t* b = all_cities.new_component(); b->set_typ(button_t::posbutton_automatic); b->set_targetpos(c->get_center()); // name gui_label_buf_t *l = all_cities.new_component(); l->buf().printf("%s", c->get_name()); l->update(); stadt_t::factory_entry_t const* const pax_entry = c->get_target_factories_for_pax().get_entry(fab); stadt_t::factory_entry_t const* const mail_entry = c->get_target_factories_for_mail().get_entry(fab); assert( pax_entry && mail_entry ); // passengers l = all_cities.new_component(); l->buf().printf("%i", pax_entry->supply); l->update(); l->set_align(gui_label_t::right); all_cities.new_component(skinverwaltung_t::passengers->get_image_id(0))->enable_offset_removal(true); // mail l = all_cities.new_component(); l->buf().printf("%i", mail_entry->supply); l->update(); l->set_align(gui_label_t::right); all_cities.new_component(skinverwaltung_t::mail->get_image_id(0))->enable_offset_removal(true); } old_cities_count = fab->get_target_cities().get_count(); } container_info.set_size(container_info.get_min_size()); set_dirty(); } /***************** Saveload stuff from here *****************/ void fabrik_info_t::rdwr( loadsave_t *file ) { // the factory first koord fabpos; if( file->is_saving() ) { fabpos = fab->get_pos().get_2d(); } fabpos.rdwr( file ); // window size scr_size size = get_windowsize(); size.rdwr( file ); if( file->is_loading() ) { fab = fabrik_t::get_fab(fabpos ); gebaeude_t* gb = welt->lookup_kartenboden( fabpos )->find(); if (fab != NULL && gb != NULL) { init(fab, gb); } win_set_magic(this, (ptrdiff_t)fab); } chart.rdwr(file); scroll_info.rdwr(file); switch_mode.rdwr(file); if( file->is_loading() ) { reset_min_windowsize(); set_windowsize(size); } } void fabrik_info_t::highlight(vector_tpl fab_koords, bool marking) { fab_koords.append(fab->get_pos().get_2d()); for (uint i = 0; i < fab_koords.get_count(); i++) { vector_tpl fab_tiles; if(grund_t *gr = welt->lookup_kartenboden(fab_koords[i])) { if(gebaeude_t *gb=gr->find() ) { gb->get_tile_list(fab_tiles); for(grund_t* gr : fab_tiles ) { // no need for check, we just did before ... gebaeude_t* gb = gr->find(); if( marking ) { gb->set_flag( obj_t::highlight ); } else { gb->clear_flag( obj_t::highlight ); } gr->set_flag( grund_t::dirty ); } } } } } simutrans-124.5/src/simutrans/gui/fabrik_info.h000066400000000000000000000046761520675460500216230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_FABRIK_INFO_H #define GUI_FABRIK_INFO_H #include "simwin.h" #include "factory_chart.h" #include "components/action_listener.h" #include "components/gui_scrollpane.h" #include "components/gui_textarea.h" #include "components/gui_textinput.h" #include "components/gui_obj_view.h" #include "components/gui_container.h" #include "components/gui_colorbox.h" #include "components/gui_image.h" #include "components/gui_tab_panel.h" #include "../utils/cbuffer.h" class welt_t; class fabrik_t; class gebaeude_t; class button_t; /** * Info window for factories */ class fabrik_info_t : public gui_frame_t, public action_listener_t { private: fabrik_t *fab; cbuffer_t info_buf, prod_buf, details_buf; factory_chart_t chart; obj_view_t view; char fabname[256]; gui_textinput_t input; gui_textarea_t prod, txt; gui_colorbox_t indicator_color; gui_tab_panel_t switch_mode; gui_image_t boost_electric, boost_passenger, boost_mail; gui_aligned_container_t container_info, container_details; gui_aligned_container_t all_suppliers, all_consumers, all_stops, all_cities; uint32 old_suppliers_count, old_consumers_count, old_stops_count, old_cities_count; gui_scrollpane_t scroll_info; button_t highlight_suppliers; button_t highlight_consumers; void rename_factory(); void update_components(); public: // refreshes text, images, indicator void update_info(); fabrik_info_t(fabrik_t* fab = NULL, const gebaeude_t* gb = NULL); virtual ~fabrik_info_t(); void init(fabrik_t* fab, const gebaeude_t* gb); fabrik_t* get_factory() { return fab; } /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "industry_info.txt";} koord3d get_weltpos(bool) OVERRIDE { return fab->get_pos(); } bool is_weltpos() OVERRIDE; /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; // rotated map need new info ... void map_rotate90( sint16 ) OVERRIDE; void rdwr( loadsave_t *file ) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_factory_info; } void highlight(vector_tpl fab_koords, bool marking); }; #endif simutrans-124.5/src/simutrans/gui/factory_chart.cc000066400000000000000000000266251520675460500223360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../obj/leitung2.h" #include "../dataobj/translator.h" #include "factory_chart.h" #define CHART_HEIGHT (90) #define MAX_GOODS_COLOR (24) static const uint8 goods_color[MAX_GOODS_COLOR] = { /* greyish blue */ 0, /* bright orange */ 33, /* cyan */ 48, /* lemon yellow */ 24, /* purple */ 57, /* greyish green */ 80, /* lilac */ 105, /* pale brown */ 89, /* blue */ COL_DARK_BLUE, /* dark green */ 161, /* dark brown */ 177, /* dark blue */ 97, /* green */ 41, /* reddish brown */ 113, /* magenta */ COL_DARK_PURPLE, /* turquoise */ 121, /* red */ 129, /* muddy yellow */ 192, /* bright green */ COL_DARK_GREEN, /* dull orange */ 65, /* pale yellow */ 167, /* pale green */ 201, /* orange */ 152, /* pale purple */ 217 }; static const char *const input_type[MAX_FAB_GOODS_STAT] = { "Storage", "Arrived", "Consumed", "In Transit" }; static const char *const output_type[MAX_FAB_GOODS_STAT] = { "Storage", "Delivered", "Produced", "In Transit" }; static const gui_chart_t::convert_proc goods_convert[MAX_FAB_GOODS_STAT] = { convert_goods, NULL, convert_goods, NULL }; static const char *const prod_type[MAX_FAB_STAT] = { "Produktion", "Usage/Output", "Electricity", "Passagiere", "Post", "Generated", "Departed", "Arrived", "Generated", "Departed", "Arrived" }; static const char *const ref_type[MAX_FAB_REF_LINE] = { "Electricity", "Passagiere", "Post", "Electricity", "Passagiere", "Post", }; static const uint8 prod_color[MAX_FAB_STAT] = { COL_LILAC, COL_LEMON_YELLOW, COL_LIGHT_GREEN, 23, COL_LIGHT_PURPLE, COL_LIGHT_TURQUOISE, 51, 49, COL_LIGHT_ORANGE, COL_ORANGE, COL_DARK_ORANGE }; static const gui_chart_t::convert_proc prod_convert[MAX_FAB_STAT] = { NULL, convert_power, convert_boost, convert_boost, convert_boost, NULL, NULL, NULL, NULL, NULL, NULL }; static const gui_chart_t::convert_proc ref_convert[MAX_FAB_REF_LINE] = { convert_boost, convert_boost, convert_boost, convert_power, NULL, NULL }; static const uint8 ref_color[MAX_FAB_REF_LINE] = { 137, COL_LIGHT_BLUE, COL_LIGHT_RED, COL_DARK_GREEN, COL_SOFT_BLUE, COL_OPERATION }; static const char *const label_text[MAX_PROD_LABEL] = { "Boost (%%)", "Max Boost (%%)", "Demand", "Passagiere", "Post", "Power (MW)" }; // Mappings from cell position to buttons, labels, charts static const uint8 prod_cell_button[] = { FAB_PRODUCTION, MAX_FAB_STAT, MAX_FAB_STAT, FAB_POWER, MAX_FAB_STAT, FAB_BOOST_ELECTRIC, FAB_BOOST_PAX, FAB_BOOST_MAIL, MAX_FAB_STAT, MAX_FAB_STAT, MAX_FAB_STAT, MAX_FAB_STAT, MAX_FAB_STAT, MAX_FAB_STAT, MAX_FAB_STAT, MAX_FAB_STAT, MAX_FAB_STAT, FAB_PAX_GENERATED, FAB_PAX_DEPARTED, FAB_PAX_ARRIVED, MAX_FAB_STAT, FAB_MAIL_GENERATED, FAB_MAIL_DEPARTED, FAB_MAIL_ARRIVED, }; static const uint8 prod_cell_label[] = { MAX_PROD_LABEL, MAX_PROD_LABEL, 5, MAX_PROD_LABEL, 0, MAX_PROD_LABEL, MAX_PROD_LABEL, MAX_PROD_LABEL, 1, MAX_PROD_LABEL, MAX_PROD_LABEL, MAX_PROD_LABEL, 2, MAX_PROD_LABEL, MAX_PROD_LABEL, MAX_PROD_LABEL, 3, MAX_PROD_LABEL, MAX_PROD_LABEL, MAX_PROD_LABEL, 4, MAX_PROD_LABEL, MAX_PROD_LABEL, MAX_PROD_LABEL, }; static const uint8 prod_cell_ref[] = { MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, FAB_REF_MAX_BOOST_ELECTRIC, FAB_REF_MAX_BOOST_PAX, FAB_REF_MAX_BOOST_MAIL, MAX_FAB_REF_LINE, FAB_REF_DEMAND_ELECTRIC, FAB_REF_DEMAND_PAX, FAB_REF_DEMAND_MAIL, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, MAX_FAB_REF_LINE, }; factory_chart_t::factory_chart_t(const fabrik_t *_factory) : goods_pane(&goods_cont, true, true), prod_pane(&prod_cont, true, true) { set_factory(_factory); } void factory_chart_t::set_factory(const fabrik_t *_factory) { factory = _factory; if (factory == NULL) { return; } remove_all(); goods_cont.remove_all(); prod_cont.remove_all(); button_to_chart.clear(); set_table_layout(1,0); add_component( &tab_panel ); const uint32 input_count = factory->get_input().get_count(); const uint32 output_count = factory->get_output().get_count(); if( input_count>0 || output_count>0 ) { // only add tab if there is something to display goods_cont.set_table_layout(4, 0); goods_cont.set_force_equal_columns(true); goods_cont.add_component(&goods_chart,4); // GUI components for goods input/output statistics goods_chart.set_min_size(scr_size(D_DEFAULT_WIDTH - D_MARGIN_LEFT - D_MARGIN_RIGHT, CHART_HEIGHT)); goods_chart.set_dimension(12, 10000); goods_chart.set_background(SYSCOL_CHART_BACKGROUND); uint32 count = 0; // first tab: charts for goods production/consumption if (input_count > 0) { // create table of buttons, insert curves to chart goods_cont.new_component_span("Verbrauch", 4); const array_tpl& input = factory->get_input(); for (uint32 g = 0; g < input_count; ++g) { goods_cont.new_component(input[g].get_typ()->get_name()); for (int s = 0; s < MAX_FAB_GOODS_STAT; ++s) { uint16 curve = goods_chart.add_curve(gfx->palette_lookup(goods_color[count % MAX_GOODS_COLOR] + (s * 3) / 2), input[g].get_stats(), MAX_FAB_GOODS_STAT, s, MAX_MONTH, false, false, true, 0, goods_convert[s]); button_t* b = goods_cont.new_component(); b->init(button_t::box_state_automatic | button_t::flexible, input_type[s]); b->background_color = gfx->palette_lookup(goods_color[count % MAX_GOODS_COLOR] + (s * 3) / 2); b->pressed = false; button_to_chart.append(b, &goods_chart, curve); if ((s % 2) == 1) { // skip last cell in current row, first cell in next row goods_cont.new_component(); if (s + 1 < MAX_FAB_GOODS_STAT) { goods_cont.new_component(); } } } count++; } } if (output_count > 0) { goods_cont.new_component_span("Produktion", 4); const array_tpl& output = factory->get_output(); for (uint32 g = 0; g < output_count; ++g) { goods_cont.new_component(output[g].get_typ()->get_name()); for (int s = 0; s < 3; ++s) { uint16 curve = goods_chart.add_curve(gfx->palette_lookup(goods_color[count % MAX_GOODS_COLOR] + s * 2), output[g].get_stats(), MAX_FAB_GOODS_STAT, s, MAX_MONTH, false, false, true, 0, goods_convert[s]); button_t* b = goods_cont.new_component(); b->init(button_t::box_state_automatic | button_t::flexible, output_type[s]); b->background_color = gfx->palette_lookup(goods_color[count % MAX_GOODS_COLOR] + s * 2); b->pressed = false; button_to_chart.append(b, &goods_chart, curve); } count++; } } goods_cont.new_component(); scr_size contsz = goods_cont.get_min_size(); if (contsz.w > gfx->get_screen_size().w) { tab_panel.add_tab(&goods_pane, translator::translate("Goods")); } else { tab_panel.add_tab(&goods_cont, translator::translate("Goods")); } } prod_cont.set_table_layout(1,0); prod_cont.add_component( &prod_chart ); prod_cont.add_table(4, 0)->set_force_equal_columns(true); // GUI components for other production-related statistics prod_chart.set_min_size( scr_size( D_DEFAULT_WIDTH-D_MARGIN_LEFT-D_MARGIN_RIGHT, CHART_HEIGHT ) ); prod_chart.set_dimension(12, 10000); prod_chart.set_background(SYSCOL_CHART_BACKGROUND); for( int row = 0, cell = 0; row<6; row++) { for( int col = 0; col<4; col++, cell++) { // labels if (prod_cell_label[cell] < MAX_PROD_LABEL) { prod_cont.new_component(label_text[ prod_cell_label[cell] ]); } // chart, buttons for production else if (prod_cell_button[cell] < MAX_FAB_STAT) { uint8 s = prod_cell_button[cell]; // add curve uint16 curve = prod_chart.add_curve( gfx->palette_lookup(prod_color[s]), factory->get_stats(), MAX_FAB_STAT, s, MAX_MONTH, (2<=s && s<=4) ? gui_chart_t::PERCENT : gui_chart_t::STANDARD, false, true, 0, prod_convert[s] ); // only show buttons, if the is something to do ... if( (s==FAB_BOOST_ELECTRIC && (factory->get_desc()->is_electricity_producer() || factory->get_desc()->get_electric_boost()==0)) || (s==FAB_BOOST_PAX && factory->get_desc()->get_pax_boost()==0) || (s==FAB_BOOST_MAIL && factory->get_desc()->get_mail_boost()==0) ) { prod_cont.new_component(); continue; } // add button button_t *b = prod_cont.new_component(); b->init(button_t::box_state_automatic | button_t::flexible, prod_type[s]); b->background_color = gfx->palette_lookup(prod_color[s]); b->pressed = false; button_to_chart.append(b, &prod_chart, curve); } // chart, buttons for reference lines else if (prod_cell_ref[cell] < MAX_FAB_REF_LINE) { uint8 r = prod_cell_ref[cell]; // add curve uint16 curve = prod_chart.add_curve( gfx->palette_lookup(ref_color[r]), prod_ref_line_data + r, 0, 0, MAX_MONTH, r<3 ? gui_chart_t::PERCENT : gui_chart_t::STANDARD, false, true, 0, ref_convert[r] ); if( (r==FAB_REF_MAX_BOOST_ELECTRIC && (factory->get_desc()->is_electricity_producer() || factory->get_desc()->get_electric_boost()==0)) || (r==FAB_REF_MAX_BOOST_PAX && factory->get_desc()->get_pax_boost()==0) || (r==FAB_REF_MAX_BOOST_MAIL && factory->get_desc()->get_mail_boost()==0) || (r==FAB_REF_DEMAND_ELECTRIC && (factory->get_desc()->is_electricity_producer() || factory->get_desc()->get_electric_demand()==0)) || (r==FAB_REF_DEMAND_PAX && factory->get_desc()->get_pax_demand()==0) || (r==FAB_REF_DEMAND_MAIL && factory->get_desc()->get_mail_demand()==0) ) { prod_cont.new_component(); continue; } // add button button_t *b = prod_cont.new_component(); b->init(button_t::box_state_automatic | button_t::flexible, ref_type[r]); b->background_color = gfx->palette_lookup(ref_color[r]); b->pressed = false; button_to_chart.append(b, &prod_chart, curve); } else { prod_cont.new_component(); } } } prod_cont.end_table(); prod_cont.new_component(); scr_size sz = prod_cont.get_min_size(); if (sz.w > gfx->get_screen_size().w) { tab_panel.add_tab(&prod_pane, translator::translate("Production/Boost")); } else { tab_panel.add_tab(&prod_cont, translator::translate("Production/Boost")); } set_size(get_min_size()); // initialize reference lines' data (these do not change over time) prod_ref_line_data[FAB_REF_MAX_BOOST_ELECTRIC] = factory->get_desc()->get_electric_boost(); prod_ref_line_data[FAB_REF_MAX_BOOST_PAX] = factory->get_desc()->get_pax_boost(); prod_ref_line_data[FAB_REF_MAX_BOOST_MAIL] = factory->get_desc()->get_mail_boost(); } factory_chart_t::~factory_chart_t() { button_to_chart.clear(); } void factory_chart_t::update() { // update reference lines' data (these might change over time) prod_ref_line_data[FAB_REF_DEMAND_ELECTRIC] = ( factory->get_desc()->is_electricity_producer() ? 0 : factory->get_scaled_electric_demand() ); prod_ref_line_data[FAB_REF_DEMAND_PAX] = factory->get_scaled_pax_demand(); prod_ref_line_data[FAB_REF_DEMAND_MAIL] = factory->get_scaled_mail_demand(); } void factory_chart_t::rdwr( loadsave_t *file ) { // button-to-chart array button_to_chart.rdwr(file); } simutrans-124.5/src/simutrans/gui/factory_chart.h000066400000000000000000000030311520675460500221620ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_FACTORY_CHART_H #define GUI_FACTORY_CHART_H #define MAX_PROD_LABEL (7-1) #include "../simfab.h" #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_label.h" #include "components/gui_button.h" #include "components/gui_button_to_chart.h" #include "components/gui_chart.h" #include "components/gui_tab_panel.h" #include "components/gui_aligned_container.h" #include "components/gui_button_to_chart.h" #include "components/gui_scrollpane.h" class factory_chart_t : public gui_aligned_container_t { private: const fabrik_t *factory; // Tab panel for grouping 2 sets of statistics gui_tab_panel_t tab_panel; // GUI components for input/output goods' statistics gui_aligned_container_t goods_cont; gui_chart_t goods_chart; // GUI components for other production-related statistics gui_aligned_container_t prod_cont; gui_chart_t prod_chart; // use only if dialog too wide for screen gui_scrollpane_t goods_pane, prod_pane; // Variables for reference lines sint64 prod_ref_line_data[MAX_FAB_REF_LINE]; gui_button_to_chart_array_t button_to_chart; public: factory_chart_t(const fabrik_t *_factory); virtual ~factory_chart_t(); void set_factory(const fabrik_t *_factory); void update(); void rdwr( loadsave_t *file ); /** * factory window will take our tabs, * we only initialize them and update charts */ gui_tab_panel_t* get_tab_panel() { return &tab_panel; } }; #endif simutrans-124.5/src/simutrans/gui/factory_edit.cc000066400000000000000000000357241520675460500221620ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simworld.h" #include "../tool/simtool.h" #include "../builder/fabrikbauer.h" #include "../descriptor/ground_desc.h" #include "../descriptor/intro_dates.h" #include "../descriptor/factory_desc.h" #include "../dataobj/translator.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "../utils/cbuffer.h" #include "factory_edit.h" #include "components/gui_label.h" // new tool definition tool_build_land_chain_t factory_edit_frame_t::land_chain_tool = tool_build_land_chain_t(); tool_city_chain_t factory_edit_frame_t::city_chain_tool = tool_city_chain_t(); tool_build_factory_t factory_edit_frame_t::fab_tool = tool_build_factory_t(); cbuffer_t factory_edit_frame_t::param_str; bool factory_edit_frame_t::sortreverse = false; static bool compare_factory_desc(const factory_desc_t* a, const factory_desc_t* b) { int diff = strcmp( a->get_name(), b->get_name() ); return factory_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_factory_desc_name(const factory_desc_t* a, const factory_desc_t* b) { int diff = strcmp( translator::translate(a->get_name()), translator::translate(b->get_name()) ); if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return factory_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_factory_desc_level_pax(const factory_desc_t* a, const factory_desc_t* b) { int diff = (a->get_pax_demand() != 65535 ? a->get_pax_demand() : a->get_pax_level()) -(b->get_pax_demand() != 65535 ? b->get_pax_demand() : b->get_pax_level()); if ( diff == 0 ) { diff = strcmp(a->get_name(), b->get_name()); } return factory_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_factory_desc_level_mail(const factory_desc_t* a, const factory_desc_t* b) { int diff = (a->get_mail_demand() != 65535 ? a->get_mail_demand() : a->get_pax_level()>>2) -(b->get_mail_demand() != 65535 ? b->get_mail_demand() : b->get_pax_level()>>2); if ( diff == 0 ) { diff = strcmp(a->get_name(), b->get_name()); } return factory_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_factory_desc_date_intro(const factory_desc_t* a, const factory_desc_t* b) { int diff = a->get_building()->get_intro_year_month() - b->get_building()->get_intro_year_month(); if ( diff == 0) { diff = strcmp(a->get_name(), b->get_name()); } return factory_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_factory_desc_date_retire(const factory_desc_t* a, const factory_desc_t* b) { int diff = a->get_building()->get_retire_year_month() - b->get_building()->get_retire_year_month(); if ( diff == 0) { diff = strcmp(a->get_name(), b->get_name()); } return factory_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_factory_desc_size(const factory_desc_t* a, const factory_desc_t* b) { koord a_koord = a->get_building()->get_size(); koord b_koord = b->get_building()->get_size(); int diff = a_koord.x * a_koord.y - b_koord.x * b_koord.y; if( diff==0 ) { //same area - sort by side to seperate different shapes diff = a_koord.x - b_koord.x; } if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return factory_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_factory_desc_goods_number(const factory_desc_t* a, const factory_desc_t* b) { int diff = a->get_product_count() - b->get_product_count(); if( diff==0 ) { //same number of products - go by number of required goods diff = a->get_supplier_count() - b->get_supplier_count(); } if( diff==0 ) { diff = strcmp(a->get_name(), b->get_name()); } return factory_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } factory_edit_frame_t::factory_edit_frame_t(player_t* player_) : extend_edit_gui_t(translator::translate("factorybuilder"), player_), factory_list(16) { land_chain_tool.cursor = city_chain_tool.cursor = fab_tool.cursor = tool_t::general_tool[TOOL_BUILD_FACTORY]->cursor; fac_desc = NULL; bt_city_chain.init( button_t::square_state, "Only city chains"); bt_city_chain.add_listener(this); cont_filter.add_component(&bt_city_chain,3); bt_land_chain.init( button_t::square_state, "Only land chains"); bt_land_chain.add_listener(this); cont_filter.add_component(&bt_land_chain,3); // add water to climate selection cb_climates.new_component(climate::water_climate); // add to sorting selection cb_sortedby.new_component(gui_sorting_item_t::BY_LEVEL_PAX); cb_sortedby.new_component(gui_sorting_item_t::BY_LEVEL_MAIL); cb_sortedby.new_component(gui_sorting_item_t::BY_DATE_INTRO); cb_sortedby.new_component(gui_sorting_item_t::BY_DATE_RETIRE); cb_sortedby.new_component(gui_sorting_item_t::BY_SIZE); cb_sortedby.new_component(gui_sorting_item_t::BY_GOODS_NUMBER); // rotation, production gui_aligned_container_t *tbl = cont_options.add_table(2,2); tbl->new_component("Rotation"); tbl->add_component(&cb_rotation); cb_rotation.add_listener(this); cb_rotation.new_component(translator::translate("random"), SYSCOL_TEXT) ; tbl->new_component("Produktion"); inp_production.set_limits(0,9999); inp_production.add_listener( this ); tbl->add_component(&inp_production); cont_options.end_table(); fill_list(); reset_min_windowsize(); } // fill the current factory_list void factory_edit_frame_t::fill_list() { const bool allow_obsolete = bt_obsolete.pressed; const bool use_timeline = bt_timeline.pressed | bt_timeline_custom.pressed; const bool city_chain = bt_city_chain.pressed; const bool land_chain = bt_land_chain.pressed; const sint32 month_now = bt_timeline.pressed ? welt->get_current_month() : bt_timeline_custom.pressed ? ni_timeline_year.get_value()*12 + ni_timeline_month.get_value()-1 : 0; const uint8 sortedby = get_sortedby(); sortreverse = sort_order.pressed; factory_list.clear(); // timeline will be obeyed; however, we may show obsolete ones ... for(auto const& i : factory_builder_t::get_factory_table()) { factory_desc_t const* const desc = i.value; if(desc->get_distribution_weight() > 0 || allow_obsolete) { // DistributionWeight=0 is obsoleted item, only for backward compatibility if( (!use_timeline || (!desc->get_building()->is_future(month_now) && (!desc->get_building()->is_retired(month_now) || allow_obsolete))) && ( desc->get_building()->get_allowed_climate_bits() & get_climate() || (get_climate()==1 && desc->get_placement()==factory_desc_t::Water) )) { // timeline allows for this, and so does climates setting if( ( city_chain && (desc->get_placement() == factory_desc_t::City && desc->is_consumer_only() ) ) || ( land_chain && (desc->get_placement() != factory_desc_t::City && desc->is_consumer_only() ) ) || (!city_chain && !land_chain) ) { switch(sortedby) { case gui_sorting_item_t::BY_NAME_TRANSLATED: factory_list.insert_ordered( desc, compare_factory_desc_name ); break; case gui_sorting_item_t::BY_LEVEL_PAX: factory_list.insert_ordered( desc, compare_factory_desc_level_pax ); break; case gui_sorting_item_t::BY_LEVEL_MAIL: factory_list.insert_ordered( desc, compare_factory_desc_level_mail ); break; case gui_sorting_item_t::BY_DATE_INTRO: factory_list.insert_ordered( desc, compare_factory_desc_date_intro ); break; case gui_sorting_item_t::BY_DATE_RETIRE: factory_list.insert_ordered( desc, compare_factory_desc_date_retire ); break; case gui_sorting_item_t::BY_SIZE: factory_list.insert_ordered( desc, compare_factory_desc_size ); break; case gui_sorting_item_t::BY_GOODS_NUMBER: factory_list.insert_ordered( desc, compare_factory_desc_goods_number ); break; default: factory_list.insert_ordered( desc, compare_factory_desc ); } } } } } // now build scrolled list scl.clear_elements(); scl.set_selection(-1); for(factory_desc_t const* const i : factory_list) { PIXVAL const color = i->is_consumer_only() ? gfx->palette_lookup(COL_DARK_BLUE + env_t::gui_player_color_dark) : i->is_producer_only() ? gfx->palette_lookup(40 + env_t::gui_player_color_dark) : SYSCOL_TEXT; char const* const name = get_sortedby()==gui_sorting_item_t::BY_NAME_OBJECT ? i->get_name() : translator::translate(i->get_name()); scl.new_component(name, color); if (i == fac_desc) { scl.set_selection(scl.get_count()-1); } } // always update current selection (since the tool may depend on it) change_item_info( scl.get_selection() ); reset_min_windowsize(); } bool factory_edit_frame_t::action_triggered( gui_action_creator_t *comp,value_t e) { // only one chain can be shown if( comp==&bt_city_chain ) { bt_city_chain.pressed ^= 1; if(bt_city_chain.pressed) { bt_land_chain.pressed = 0; } fill_list(); } else if( comp==&bt_land_chain ) { bt_land_chain.pressed ^= 1; if(bt_land_chain.pressed) { bt_city_chain.pressed = 0; } fill_list(); } else if( comp == &cb_rotation) { change_item_info( scl.get_selection() ); } else if(fac_desc) { if (comp==&inp_production) { production = inp_production.get_value(); } // update info ... change_item_info( scl.get_selection() ); } return extend_edit_gui_t::action_triggered(comp,e); } void factory_edit_frame_t::change_item_info(sint32 entry) { if(entry>=0 && entry<(sint32)factory_list.get_count()) { const factory_desc_t *new_fac_desc = factory_list[entry]; if(new_fac_desc!=fac_desc) { fac_desc = new_fac_desc; production = fac_desc->get_productivity() + sim_async_rand( fac_desc->get_range() ); // should also consider the effects of the minimum number of fields const field_group_desc_t *const field_group_desc = fac_desc->get_field_group(); if( field_group_desc && field_group_desc->get_field_class_count()>0 ) { const weighted_vector_tpl &field_class_indices = field_group_desc->get_field_class_indices(); sint32 min_fields = field_group_desc->get_min_fields(); while( min_fields-- > 0 ) { const uint16 field_class_index = field_class_indices.at_weight( sim_async_rand( field_class_indices.get_sum_weight() ) ); production += field_group_desc->get_field_class(field_class_index)->get_field_production(); } } production = welt->scale_with_month_length(production); inp_production.set_value( production); // show produced goods buf.clear(); if (!fac_desc->is_consumer_only()) { buf.append( translator::translate("Produktion") ); buf.append("\n"); for (uint i = 0; i < fac_desc->get_product_count(); i++) { buf.append(" - "); buf.append( translator::translate(fac_desc->get_product(i)->get_output_type()->get_name()) ); buf.append( " (" ); buf.append( translator::translate(fac_desc->get_product(i)->get_output_type()->get_catg_name()) ); buf.append( ")\n" ); } buf.append("\n"); } // show consumed goods if (!fac_desc->is_producer_only()) { buf.append( translator::translate("Verbrauch") ); buf.append("\n"); for( int i=0; iget_supplier_count(); i++ ) { buf.append(" - "); buf.append( translator::translate(fac_desc->get_supplier(i)->get_input_type()->get_name()) ); buf.append( " (" ); buf.append( translator::translate(fac_desc->get_supplier(i)->get_input_type()->get_catg_name()) ); buf.append( ")\n" ); } buf.append("\n"); } if(fac_desc->is_electricity_producer()) { buf.append( translator::translate( "Electricity producer\n\n" ) ); } // now the house stuff const building_desc_t *desc = fac_desc->get_building(); // climates buf.append( translator::translate("allowed climates:\n") ); uint16 cl = desc->get_allowed_climate_bits(); if(cl==0) { buf.append( translator::translate("none") ); buf.append("\n"); } else { for(uint16 i=0; i<=arctic_climate; i++ ) { if(cl & (1<> 2); buf.printf("%s%u", translator::translate("\nBauzeit von"), desc->get_intro_year_month() / 12); if(desc->get_retire_year_month()!=DEFAULT_RETIRE_YEAR*12) { buf.printf("%s%u", translator::translate("\nBauzeit bis"), desc->get_retire_year_month() / 12); } if (char const* const maker = desc->get_copyright()) { buf.append("\n"); buf.printf(translator::translate("Constructed by %s"), maker); } buf.append("\n"); // reset combobox cb_rotation.clear_elements(); cb_rotation.new_component(gui_rotation_item_t::random); for(uint8 i = 0; iget_all_layouts(); i++) { cb_rotation.new_component(i); } // orientation (255=random) if(desc->get_all_layouts()>1) { cb_rotation.set_selection(0); } else { cb_rotation.set_selection(1); } // now for the tool fac_desc = factory_list[entry]; } const building_desc_t *desc = fac_desc->get_building(); uint8 rotation = get_rotation(); uint8 rot = (rotation==255) ? 0 : rotation; building_image.init(desc, rot); // the tools will be always updated, even though the data up there might be still current param_str.clear(); param_str.printf("%i%c%i,%s", bt_climates.pressed, rotation==255 ? '#' : '0'+rotation, production, fac_desc->get_name() ); if(bt_land_chain.pressed) { land_chain_tool.set_default_param(param_str); welt->set_tool( &land_chain_tool, player ); } else if(bt_city_chain.pressed) { city_chain_tool.set_default_param(param_str); welt->set_tool( &city_chain_tool, player ); } else { fab_tool.set_default_param(param_str); welt->set_tool( &fab_tool, player ); } } else if(fac_desc!=NULL) { cb_rotation.clear_elements(); cb_rotation.new_component(gui_rotation_item_t::random); building_image.init(NULL, 0); fac_desc = NULL; welt->set_tool( tool_t::general_tool[TOOL_QUERY], player ); buf.clear(); } info_text.recalc_size(); reset_min_windowsize(); } void factory_edit_frame_t::set_windowsize(scr_size size) { extend_edit_gui_t::set_windowsize(size); // manually set width of cb_rotation and inp_production scr_size cbs = cb_rotation.get_size(); scr_size nis = inp_production.get_size(); scr_coord_val w = max(cbs.w, nis.w); cb_rotation.set_size(scr_size(w, cbs.h)); inp_production.set_size(scr_size(w, nis.h)); } simutrans-124.5/src/simutrans/gui/factory_edit.h000066400000000000000000000026421520675460500220150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_FACTORY_EDIT_H #define GUI_FACTORY_EDIT_H #include "extend_edit.h" #include "components/gui_numberinput.h" #include "../utils/cbuffer.h" class factory_desc_t; class tool_build_land_chain_t; class tool_city_chain_t; class tool_build_factory_t; /* * Factories builder dialog */ class factory_edit_frame_t : public extend_edit_gui_t { private: static tool_build_land_chain_t land_chain_tool; static tool_city_chain_t city_chain_tool; static tool_build_factory_t fab_tool; static cbuffer_t param_str; const factory_desc_t *fac_desc; uint32 production; vector_tplfactory_list; button_t bt_city_chain; button_t bt_land_chain; gui_numberinput_t inp_production; void fill_list() OVERRIDE; void change_item_info( sint32 i ) OVERRIDE; public: factory_edit_frame_t(player_t* player); static bool sortreverse; /** * in top-level windows the name is displayed in titlebar * @return the non-translated component name */ const char* get_name() const { return "factorybuilder"; } /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char* get_help_filename() const OVERRIDE { return "factory_build.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void set_windowsize(scr_size size) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/factorylist_frame.cc000066400000000000000000000115071520675460500232140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "factorylist_frame.h" #include "gui_theme.h" #include "../dataobj/translator.h" #include "../player/simplay.h" #include "../world/simworld.h" #include "../dataobj/environment.h" #include "../utils/unicode.h" char factorylist_frame_t::name_filter[256]; const char *factorylist_frame_t::sort_text[factorylist::SORT_MODES] = { "Fabrikname", "Input", "Output", "Produktion", "Rating", "Power" }; class playername_const_scroll_item_t : public gui_scrolled_list_t::const_text_scrollitem_t { public: const uint8 player_nr; playername_const_scroll_item_t( player_t *pl ) : gui_scrolled_list_t::const_text_scrollitem_t( pl->get_name(), gfx->palette_lookup(pl->get_player_color1()+env_t::gui_player_color_dark) ) , player_nr(pl->get_player_nr()) { } }; factorylist_frame_t::factorylist_frame_t() : gui_frame_t( translator::translate("fl_title") ), scrolly(gui_scrolled_list_t::windowskin, factorylist_stats_t::compare) { scrolly.set_checkered(true); set_table_layout(3,4); new_component("Filter:"); name_filter_input.set_text(name_filter, lengthof(name_filter)); name_filter_input.set_notify_all_changes_delay(0); name_filter_input.add_listener(this); add_component(&name_filter_input); new_component(); filter_by_owner.init(button_t::square_automatic, "Served by"); filter_by_owner.add_listener(this); filter_by_owner.set_tooltip("At least one tile is connected to one stop."); add_component(&filter_by_owner); filterowner.new_component(translator::translate("No player"), SYSCOL_TEXT); for (int i = 0; i < MAX_PLAYER_COUNT; i++) { if (player_t* pl = welt->get_player(i)) { filterowner.new_component(pl); if (pl == welt->get_active_player()) { filterowner.set_selection(filterowner.count_elements() - 1); } } } filterowner.add_listener(this); add_component(&filterowner); new_component(); new_component("hl_txt_sort"); sortedby.set_unsorted(); // do not sort for (size_t i = 0; i < lengthof(sort_text); i++) { sortedby.new_component(translator::translate(sort_text[i]), SYSCOL_TEXT); } sortedby.set_selection(factorylist_stats_t::sort_mode); sortedby.add_listener(this); add_component(&sortedby); sorteddir.init(button_t::sortarrow_state, NULL); sorteddir.add_listener(this); sorteddir.pressed = factorylist_stats_t::reverse; add_component(&sorteddir); add_component(&scrolly,3); scrolly.set_maximize(true); fill_list(); set_resizemode(diagonal_resize); reset_min_windowsize(); } /** * This method is called if an action is triggered */ bool factorylist_frame_t::action_triggered( gui_action_creator_t *comp,value_t v) { if (comp == &sortedby) { factorylist_stats_t::sort_mode = v.i; scrolly.sort(0); } else if (comp == &sorteddir) { factorylist_stats_t::reverse = !factorylist_stats_t::reverse; sorteddir.pressed = factorylist_stats_t::reverse; scrolly.sort(0); } else if(comp == &filterowner) { if( filter_by_owner.pressed ) { fill_list(); } } else if (comp == &filter_by_owner) { fill_list(); } else if (comp == &name_filter_input) { fill_list(); } return true; } void factorylist_frame_t::fill_list() { old_factories_count = world()->get_fab_list().get_count(); // to avoid too many redraws ... scrolly.clear_elements(); if (filter_by_owner.pressed && filterowner.get_selection() == 0) { for(fabrik_t* fab : world()->get_fab_list()) { bool add = (name_filter[0] == 0 || utf8caseutf8(fab->get_name(), name_filter)); for( int i = 0; add && i < MAX_PLAYER_COUNT; i++ ) { if( player_t* pl = welt->get_player(i) ) { if (fab->is_within_players_network(pl)) { // already connected add = false; } } } if (add) { scrolly.new_component(fab); } } } else { player_t* pl = (filter_by_owner.pressed && filterowner.get_selection() >= 1) ? welt->get_player(((const playername_const_scroll_item_t*)(filterowner.get_selected_item()))->player_nr) : NULL; for(fabrik_t * fab : world()->get_fab_list()) { if( pl == NULL || fab->is_within_players_network( pl ) ) { if( name_filter[0] == 0 || utf8caseutf8(fab->get_name(), name_filter)) { scrolly.new_component( fab ); } } } } scrolly.sort(0); scrolly.set_size(scr_size(get_windowsize().w, scrolly.get_size().h)); } void factorylist_frame_t::rdwr(loadsave_t* file) { scr_size size = get_windowsize(); size.rdwr(file); scrolly.rdwr(file); file->rdwr_str(name_filter, lengthof(name_filter)); file->rdwr_short(factorylist_stats_t::sort_mode); file->rdwr_bool(factorylist_stats_t::reverse); if (file->is_loading()) { fill_list(); set_windowsize(size); } } simutrans-124.5/src/simutrans/gui/factorylist_frame.h000066400000000000000000000023001520675460500230450ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_FACTORYLIST_FRAME_H #define GUI_FACTORYLIST_FRAME_H #include "simwin.h" #include "gui_frame.h" #include "components/gui_scrollpane.h" #include "components/gui_label.h" #include "components/gui_button.h" #include "components/gui_combobox.h" #include "factorylist_stats.h" /* * Factory list window */ class factorylist_frame_t : public gui_frame_t, private action_listener_t { private: static const char *sort_text[factorylist::SORT_MODES]; gui_combobox_t sortedby; button_t sorteddir; button_t filter_by_owner; gui_combobox_t filterowner; gui_scrolled_list_t scrolly; static char name_filter[256]; gui_textinput_t name_filter_input; uint32 old_factories_count; public: factorylist_frame_t(); // can be called externally when open or crash! void fill_list(); const char *get_help_filename() const OVERRIDE {return "factorylist_filter.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void map_rotate90( sint16 ) OVERRIDE { fill_list(); } void rdwr(loadsave_t* file) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_factorylist; } }; #endif simutrans-124.5/src/simutrans/gui/factorylist_stats.cc000066400000000000000000000117101520675460500232540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "factorylist_stats.h" #include "../simskin.h" #include "../simfab.h" #include "../world/simworld.h" #include "../display/viewport.h" #include "../tool/simmenu.h" #include "../simskin.h" #include "../builder/goods_manager.h" #include "../descriptor/skin_desc.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" sint16 factorylist_stats_t::sort_mode = factorylist::by_name; bool factorylist_stats_t::reverse = false; factorylist_stats_t::factorylist_stats_t(fabrik_t *fab) { this->fab = fab; // pos button set_table_layout(0,1); button_t *b = new_component(); b->set_typ(button_t::posbutton_automatic); b->set_targetpos3d(fab->get_pos()); // indicator bar add_component(&indicator); indicator.set_max_size(scr_size(D_INDICATOR_WIDTH,D_INDICATOR_HEIGHT)); // boost images if (fab->get_desc()->get_electric_boost() ) { boost_electric.set_image(skinverwaltung_t::electricity->get_image_id(0), true); add_component(&boost_electric); } if (fab->get_desc()->get_pax_boost() ) { boost_passenger.set_image(skinverwaltung_t::passengers->get_image_id(0), true); add_component(&boost_passenger); } if (fab->get_desc()->get_mail_boost() ) { boost_mail.set_image(skinverwaltung_t::mail->get_image_id(0), true); add_component(&boost_mail); } // factory name update_label(); add_component(&label); } void factorylist_stats_t::update_label() { cbuffer_t &buf = label.buf(); buf.append(fab->get_name()); buf.append(" ("); if (!fab->get_input().empty()) { buf.printf( "%i+%i", fab->get_total_in(), fab->get_total_transit() ); } else { buf.append("-"); } buf.append(", "); if (!fab->get_output().empty()) { buf.append(fab->get_total_out(),0); } else { buf.append("-"); } buf.append(", "); buf.append(fab->get_current_production(),0); buf.append(") "); label.update(); } bool factorylist_stats_t::is_valid() const { return world()->get_fab_list().is_contained(fab); } bool factorylist_stats_t::infowin_event(const event_t * ev) { bool swallowed = gui_aligned_container_t::infowin_event(ev); if (!swallowed) { // either open dialog or goto (with control or right click) if (IS_LEFTRELEASE(ev)) { if ((event_get_last_control_shift() ^ tool_t::control_invert) == 2) { world()->get_viewport()->change_world_position(fab->get_pos()); } else { fab->open_info_window(); } return true; } if (IS_RIGHTRELEASE(ev)) { world()->get_viewport()->change_world_position(fab->get_pos()); return true; } } return swallowed; } void factorylist_stats_t::draw(scr_coord pos) { update_label(); // boost stuff boost_electric.set_transparent(fab->get_prodfactor_electric()>0 ? 0 : TRANSPARENT50_FLAG | OUTLINE_FLAG | SYSCOL_IMAGE_TRANSPARENCY); boost_passenger.set_transparent(fab->get_prodfactor_pax()>0 ? 0 : TRANSPARENT50_FLAG | OUTLINE_FLAG | SYSCOL_IMAGE_TRANSPARENCY); boost_mail.set_transparent(fab->get_prodfactor_mail()>0 ? 0 : TRANSPARENT50_FLAG | OUTLINE_FLAG | SYSCOL_IMAGE_TRANSPARENCY); indicator.set_color( gfx->palette_lookup(fabrik_t::status_to_color[fab->get_status()]) ); gui_aligned_container_t::draw(pos); } bool factorylist_stats_t::compare(const gui_component_t *aa, const gui_component_t *bb) { const factorylist_stats_t* fa = dynamic_cast(aa); const factorylist_stats_t* fb = dynamic_cast(bb); // good luck with mixed lists assert(fa != NULL && fb != NULL); fabrik_t *a=fa->fab, *b=fb->fab; int cmp; switch (sort_mode) { default: case factorylist::by_name: cmp = 0; break; case factorylist::by_input: { int a_in = a->get_input().empty() ? -1 : (int)a->get_total_in(); int b_in = b->get_input().empty() ? -1 : (int)b->get_total_in(); cmp = a_in - b_in; break; } case factorylist::by_transit: { int a_transit = a->get_input().empty() ? -1 : (int)a->get_total_transit(); int b_transit = b->get_input().empty() ? -1 : (int)b->get_total_transit(); cmp = a_transit - b_transit; break; } case factorylist::by_available: { int a_in = a->get_input().empty() ? -1 : (int)(a->get_total_in()+a->get_total_transit()); int b_in = b->get_input().empty() ? -1 : (int)(b->get_total_in()+b->get_total_transit()); cmp = a_in - b_in; break; } case factorylist::by_output: { int a_out = a->get_output().empty() ? -1 : (int)a->get_total_out(); int b_out = b->get_output().empty() ? -1 : (int)b->get_total_out(); cmp = a_out - b_out; break; } case factorylist::by_maxprod: cmp = a->get_base_production()*a->get_prodfactor() - b->get_base_production()*b->get_prodfactor(); break; case factorylist::by_status: cmp = a->get_status() - b->get_status(); break; case factorylist::by_power: cmp = a->get_prodfactor_electric() - b->get_prodfactor_electric(); break; } if (cmp == 0) { cmp = STRICMP(a->get_name(), b->get_name()); } return reverse ? cmp > 0 : cmp < 0; } simutrans-124.5/src/simutrans/gui/factorylist_stats.h000066400000000000000000000023771520675460500231270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_FACTORYLIST_STATS_H #define GUI_FACTORYLIST_STATS_H #include "components/gui_colorbox.h" #include "components/gui_image.h" #include "components/gui_label.h" #include "components/gui_scrolled_list.h" #include "../simfab.h" class fabrik_t; namespace factorylist { enum sort_mode_t { by_name = 0, by_available, by_output, by_maxprod, by_status, by_power, SORT_MODES, // the last two are unused by_input, by_transit }; }; /** * Factory list stats display * Where factory stats are calculated for list dialog */ class factorylist_stats_t : public gui_aligned_container_t, public gui_scrolled_list_t::scrollitem_t { private: fabrik_t *fab; gui_colorbox_t indicator; gui_image_t boost_electric, boost_passenger, boost_mail; gui_label_buf_t label; void update_label(); public: static sint16 sort_mode; static bool reverse; factorylist_stats_t(fabrik_t *); void draw( scr_coord pos) OVERRIDE; char const* get_text() const OVERRIDE { return fab->get_name(); } bool infowin_event(const event_t *) OVERRIDE; bool is_valid() const OVERRIDE; static bool compare(const gui_component_t *a, const gui_component_t *b ); }; #endif simutrans-124.5/src/simutrans/gui/goods_frame.cc000066400000000000000000000234671520675460500217740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "goods_frame.h" #include "components/gui_scrollpane.h" #include "../builder/goods_manager.h" #include "../builder/vehikelbauer.h" #include "../descriptor/goods_desc.h" #include "../dataobj/translator.h" #include "../simcolor.h" #include "simwin.h" #include "../world/simworld.h" sint32 goods_frame_t::selected_speed = 1; bool goods_frame_t::average_selection = true; goods_frame_t::sort_mode_t goods_frame_t::sortby = unsortiert; bool goods_frame_t::sortreverse = false; const char *goods_frame_t::sort_text[SORT_MODES] = { "gl_btn_unsort", "gl_btn_sort_name", "gl_btn_sort_revenue", "gl_btn_sort_bonus", "gl_btn_sort_catg" }; bool goods_frame_t::filter_goods = false; simline_t::linetype goods_frame_t::last_scheduletype = simline_t::trainline; goods_frame_t::goods_frame_t() : gui_frame_t( translator::translate("gl_title") ), speed_text( &speed_message), goods_stats(), scrolly(&goods_stats) { set_table_layout(1,0); // top line: combo, input, text add_table(3,1); { build_linetype_list(filter_goods); add_component(&scheduletype); scheduletype.add_listener( this ); speed.init(1, 1, 1, gui_numberinput_t::AUTOLINEAR, false); if (average_selection) { selected_speed = welt->get_average_speed(simline_t::linetype_to_waytype(last_scheduletype)); } set_linetype(last_scheduletype, selected_speed); add_component(&speed); speed.add_listener( this ); gui_label_buf_t *lb = new_component(SYSCOL_TEXT_HIGHLIGHT); lb->buf().printf(translator::translate("100 km/h = %i tiles/month"), welt->speed_to_tiles_per_month(kmh_to_speed(100)) ); lb->update(); } end_table(); add_component(&speed_text); filter_goods_toggle.init(button_t::square_state, "Show only used"); filter_goods_toggle.set_tooltip(translator::translate("Only show goods which are currently handled by factories")); filter_goods_toggle.add_listener(this); filter_goods_toggle.pressed = filter_goods; add_component(&filter_goods_toggle); // sort mode sort_row = add_table(3,1); { new_component("hl_txt_sort"); sortedby.set_unsorted(); // do not sort for (size_t i = 0; i < lengthof(sort_text); i++) { sortedby.new_component(translator::translate(sort_text[i]), SYSCOL_TEXT); } sortedby.set_selection(sortby); sortedby.add_listener(this); add_component(&sortedby); sorteddir.init(button_t::sortarrow_state, ""); sorteddir.pressed = sortreverse; sorteddir.add_listener(this); add_component(&sorteddir); } end_table(); add_component(&scrolly); scrolly.set_maximize(true); sort_list(); reset_min_windowsize(); set_windowsize(get_min_windowsize()); set_resizemode(diagonal_resize); } void goods_frame_t::build_linetype_list(bool const show_used) { // build combobox list scheduletype.enable(); scheduletype.clear_elements(); size_t item_count = 0; for (int i = 1; i < simline_t::MAX_LINE_TYPE; i++) { simline_t::linetype const linetype = (simline_t::linetype)i; waytype_t const waytype = simline_t::linetype_to_waytype(linetype); sint32 const maximum_speed = vehicle_builder_t::get_fastest_vehicle_speed(waytype, welt->get_current_month(), welt->get_settings().get_use_timeline() && show_used, welt->get_settings().get_allow_buying_obsolete_vehicles()); if (!show_used || maximum_speed > 0) { scheduletype.new_component(simline_t::get_linetype_name(linetype), SYSCOL_TEXT); linetype_selection_map[item_count] = linetype; item_count+= 1; } } if (item_count == 0) { // no vehicles found, eg due to unsupported date or incomplete pakset if (show_used) { build_linetype_list(false); } scheduletype.disable(); } // select schedule type bool selected = false; for (size_t index = 0 ; index < item_count ; index++) { if (linetype_selection_map[index] == last_scheduletype) { scheduletype.set_selection((int)index); selected = true; break; } } if (!selected && item_count > 0) { scheduletype.set_selection(0); set_linetype(linetype_selection_map[0]); } } bool goods_frame_t::compare_goods(goods_desc_t const* const w1, goods_desc_t const* const w2) { int order = 0; switch (sortby) { case 0: // sort by number order = w1->get_index() - w2->get_index(); break; case 2: // sort by revenue { const sint16 relative_speed_change = (100*selected_speed)/welt->get_average_speed(simline_t::linetype_to_waytype(last_scheduletype)); const sint32 grundwert1281 = w1->get_value() * goods_frame_t::welt->get_settings().get_bonus_basefactor(); const sint32 grundwert1282 = w2->get_value() * goods_frame_t::welt->get_settings().get_bonus_basefactor(); const sint32 grundwert_bonus1 = w1->get_value()*(1000l+(relative_speed_change-100l)*w1->get_speed_bonus()); const sint32 grundwert_bonus2 = w2->get_value()*(1000l+(relative_speed_change-100l)*w2->get_speed_bonus()); const sint32 price1 = (grundwert1281>grundwert_bonus1 ? grundwert1281 : grundwert_bonus1); const sint32 price2 = (grundwert1282>grundwert_bonus2 ? grundwert1282 : grundwert_bonus2); order = price1-price2; } break; case 3: // sort by speed bonus order = w1->get_speed_bonus()-w2->get_speed_bonus(); break; case 4: // sort by catg_index order = w1->get_catg()-w2->get_catg(); break; default: ; // make compiler happy, order will be determined below anyway } if( order==0 ) { // sort by name if not sorted or not unique order = strcmp(translator::translate(w1->get_name()), translator::translate(w2->get_name())); } return sortreverse ? order > 0 : order < 0; } void goods_frame_t::set_linetype(simline_t::linetype const linetype, sint32 const speed_value) { waytype_t const waytype = simline_t::linetype_to_waytype(linetype); sint32 const average_speed = welt->get_average_speed(waytype); sint32 new_value = speed_value; if (new_value == 0) { double const relative_speed_change = speed.get_value() / (double)welt->get_average_speed(simline_t::linetype_to_waytype(last_scheduletype)); new_value = (sint32)(average_speed * relative_speed_change + 0.5); } sint32 const maximum_speed = vehicle_builder_t::get_fastest_vehicle_speed(waytype, welt->get_current_month(), welt->get_settings().get_use_timeline(), welt->get_settings().get_allow_buying_obsolete_vehicles()); if (maximum_speed > 0) { speed.enable(); speed.set_limits(1, maximum_speed); } else { speed.disable(); speed.set_limits(new_value, new_value); } speed.set_value(new_value); selected_speed = new_value; average_selection = new_value == average_speed; if (last_scheduletype != linetype) { for (size_t index = 0 ; index < simline_t::MAX_LINE_TYPE ; index++) { if (linetype_selection_map[index] == linetype) { scheduletype.set_selection((int)index); break; } } } last_scheduletype = linetype; } // creates the list and pass it to the child function good_stats, which does the display stuff ... void goods_frame_t::sort_list() { sint32 const average_speed = welt->get_average_speed(simline_t::linetype_to_waytype(last_scheduletype)); // update all strings speed_message.clear(); speed_message.printf(translator::translate("Bonus Speed: %i km/h"), average_speed ); speed_message.append("\n"); speed_message.printf(translator::translate("Bonus Multiplier: %i%%"), (int)(100.0 * 10.0 * (((double)selected_speed / (double)average_speed) - 1.0)) ); speed_message.append("\n"); if (!speed.enabled()) { speed_message.append(translator::translate("No vehicles are available for purchase.")); } if (speed_text.get_min_size().h > speed_text.get_size().h) { resize(scr_coord(0,0)); } // update buttons sortedby.set_selection(sortby); sorteddir.pressed = sortreverse; filter_goods_toggle.pressed = filter_goods; sort_row->set_size(sort_row->get_min_size()); // now prepare the sort // Fetch the list of goods produced by the factories that exist in the current game const vector_tpl &goods_in_game = welt->get_goods_list(); good_list.clear(); for(unsigned int i=0; iget_value()!=0 && (!filter_goods || goods_in_game.is_contained(wtyp)) ) { good_list.insert_ordered( wtyp, compare_goods ); } } goods_stats.update_goodslist( good_list, (100 * selected_speed) / average_speed); } /** * This method is called if an action is triggered */ bool goods_frame_t::action_triggered( gui_action_creator_t *comp,value_t p) { if(comp == &sortedby) { // sort by what sortby = (goods_frame_t::sort_mode_t)p.i; } else if(comp == &sorteddir) { // order sortreverse ^= 1; } else if(comp == &filter_goods_toggle) { filter_goods = !filter_goods; build_linetype_list(filter_goods); } else if(comp == &scheduletype) { set_linetype(linetype_selection_map[scheduletype.get_selection()]); } else if(comp == &speed) { set_linetype(last_scheduletype, speed.get_value()); } sort_list(); return true; } uint32 goods_frame_t::get_rdwr_id() { return magic_goodslist; } void goods_frame_t::rdwr( loadsave_t *file ) { // This used to be realitive speed in percentage but is now absolute speed in km/h. sint16 saved_speed = (sint16)std::min(selected_speed, (sint32)std::numeric_limits::max()); file->rdwr_short( saved_speed ); file->rdwr_bool( sortreverse ); file->rdwr_bool( filter_goods ); sint16 s = last_scheduletype; file->rdwr_short( s ); sint16 b = sortby; file->rdwr_short( b ); if( file->is_loading() ) { build_linetype_list(filter_goods); if(saved_speed < 1) { saved_speed = 1; } set_linetype((simline_t::linetype)s, saved_speed); sortby = (sort_mode_t)b; sort_list(); } } simutrans-124.5/src/simutrans/gui/goods_frame.h000066400000000000000000000066361520675460500216350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_GOODS_FRAME_H #define GUI_GOODS_FRAME_H #include "gui_frame.h" #include "components/gui_button.h" #include "components/gui_numberinput.h" #include "components/gui_textarea.h" #include "components/gui_combobox.h" #include "components/gui_scrollpane.h" #include "components/gui_label.h" #include "components/action_listener.h" #include "goods_stats.h" #include "../simline.h" #include "../utils/cbuffer.h" class goods_desc_t; /** * Shows statistics. Only goods so far. */ class goods_frame_t : public gui_frame_t, private action_listener_t { private: enum sort_mode_t { unsortiert = 0, nach_name = 1, nach_gewinn = 2, nach_bonus = 3, nach_catg = 4, SORT_MODES = 5 }; static const char *sort_text[SORT_MODES]; // Variables used for remembering last state of window when closed. // The last selected speed. static sint32 selected_speed; // If the last selected speed was the average speed so as to allow selected speed to follow average speed over time. static bool average_selection; /** * This variable defines by which column the table is sorted * Values: 0 = Unsorted (passengers and mail first) * 1 = Alphabetical * 2 = Revenue */ static sort_mode_t sortby; /** * This variable defines the sort order (ascending or descending) * Values: 1 = ascending, 2 = descending) */ static bool sortreverse; /** * This variable controls whether all goods, way types and speeds are displayed, or * just the ones relevant to the current game * Values: false = all goods, way types and maximum speed shown, true = goods with factories, way types with vehicles and current best speed shown. */ static bool filter_goods; static simline_t::linetype last_scheduletype; cbuffer_t speed_message; vector_tpl good_list; gui_combobox_t sortedby; button_t sorteddir; gui_numberinput_t speed; gui_combobox_t scheduletype; gui_textarea_t speed_text; gui_aligned_container_t *sort_row; button_t filter_goods_toggle; goods_stats_t goods_stats; gui_scrollpane_t scrolly; /* Array to map linetype combobox selection to linetypes. * Allows the combo box to be filtered or sorted. */ simline_t::linetype linetype_selection_map[simline_t::MAX_LINE_TYPE]; /* Builds the linetype combobox list. * When show_used is specified then only types which have at least 1 powered vehicle will be shown. */ void build_linetype_list(bool const show_used); // creates the list and pass it to the child function good_stats, which does the display stuff ... static bool compare_goods(goods_desc_t const* const w1, goods_desc_t const* const w2); /* Changes the currently set linetype. * Updates the selectable speed limit as well as automatically adjusts the value to keep the same bonus. * A non 0 speed_value will set the speed value while a 0 value will automatically adjust. */ void set_linetype(simline_t::linetype const linetype, sint32 const speed_value = 0); void sort_list(); public: goods_frame_t(); // yes we can reload uint32 get_rdwr_id() OVERRIDE; void rdwr( loadsave_t *file ) OVERRIDE; /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE {return "goods_filter.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/goods_stats.cc000066400000000000000000000041741520675460500220320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "goods_stats.h" #include "../simcolor.h" #include "../world/simworld.h" #include "../builder/goods_manager.h" #include "../descriptor/goods_desc.h" #include "../dataobj/translator.h" #include "components/gui_button.h" #include "components/gui_colorbox.h" #include "components/gui_label.h" karte_ptr_t goods_stats_t::welt; void goods_stats_t::update_goodslist(vector_tplgoods, int bonus) { scr_size size = get_size(); remove_all(); set_table_layout(6,0); new_component(); new_component("Name") ->set_align(gui_label_t::left); new_component("Revenue/unit/100 tiles")->set_align(gui_label_t::right); new_component("Speed Bonus") ->set_align(gui_label_t::right); new_component("Category") ->set_align(gui_label_t::left); new_component("Weight/unit") ->set_align(gui_label_t::right); for(const goods_desc_t* wtyp : goods) { new_component(wtyp->get_color())->set_max_size(scr_size(D_INDICATOR_WIDTH, D_INDICATOR_HEIGHT)); new_component(wtyp->get_name()); const sint64 grundwert128 = wtyp->get_value() * welt->get_settings().get_bonus_basefactor(); // bonus price will be always at least this const sint64 grundwert_bonus = wtyp->get_value() * (1000l + (bonus-100l) * wtyp->get_speed_bonus()); const sint64 price = std::max(grundwert128, grundwert_bonus); gui_label_buf_t *lb = new_component(SYSCOL_TEXT, gui_label_t::right); lb->buf().append_money(price/3000.0); lb->update(); lb = new_component(SYSCOL_TEXT, gui_label_t::right); lb->buf().printf("%d%%", wtyp->get_speed_bonus()); lb->update(); new_component(wtyp->get_catg_name()); lb = new_component(SYSCOL_TEXT, gui_label_t::right); lb->buf().printf("%dKg", wtyp->get_weight_per_unit()); lb->update(); } scr_size min_size = get_min_size(); set_size(scr_size(max(size.w, min_size.w), min_size.h) ); } simutrans-124.5/src/simutrans/gui/goods_stats.h000066400000000000000000000007671520675460500217000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_GOODS_STATS_H #define GUI_GOODS_STATS_H #include "../simtypes.h" #include "components/gui_aligned_container.h" template class vector_tpl; class goods_desc_t; class goods_stats_t : public gui_aligned_container_t { static karte_ptr_t welt; public: goods_stats_t() {} // update list and resize void update_goodslist(vector_tpl, int bonus); }; #endif simutrans-124.5/src/simutrans/gui/ground_info.cc000066400000000000000000000030721520675460500220060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../display/simgraph.h" #include "../display/viewport.h" #include "../world/simworld.h" #include "../dataobj/translator.h" #include "ground_info.h" grund_info_t::grund_info_t(const grund_t* _gr) : base_infowin_t("",NULL), gr(_gr), lview(_gr->get_pos(), scr_size(max(64, gfx->get_base_tile_raster_width()), max(56, (gfx->get_base_tile_raster_width() * 7) / 8))) { fill_buffer(); set_embedded(&lview); textarea.set_width(textarea.get_size().w + gfx->get_base_tile_raster_width() - 64); recalc_size(); } void grund_info_t::fill_buffer() { const cbuffer_t old_buf(buf); buf.clear(); gr->info(buf); } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void grund_info_t::draw(scr_coord pos, scr_size size) { scr_size old_t_size = textarea.get_size(); fill_buffer(); // update for owner and name change set_dirty(); const obj_t *const d = gr->obj_bei(0); if ( d!=NULL ) { set_owner( d->get_owner() ); } gui_frame_t::set_name(translator::translate(gr->get_name())); gui_frame_t::draw(pos, size); set_embedded(&lview); if (old_t_size != textarea.get_size()) { recalc_size(); } } koord3d grund_info_t::get_weltpos(bool) { return gr->get_pos(); } bool grund_info_t::is_weltpos() { return ( welt->get_viewport()->is_on_center( gr->get_pos() ) ); } void grund_info_t::map_rotate90( sint16 new_ysize ) { lview.map_rotate90(new_ysize); } simutrans-124.5/src/simutrans/gui/ground_info.h000066400000000000000000000011271520675460500216470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_GROUND_INFO_H #define GUI_GROUND_INFO_H #include "gui_frame.h" #include "components/gui_location_view.h" #include "obj_info.h" class grund_t; class grund_info_t : public base_infowin_t { protected: const grund_t* gr; location_view_t lview; public: grund_info_t(const grund_t* _gr); koord3d get_weltpos(bool) OVERRIDE; bool is_weltpos() OVERRIDE; void fill_buffer(); void draw(scr_coord pos, scr_size size) OVERRIDE; void map_rotate90( sint16 ) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/groundobj_edit.cc000066400000000000000000000130011520675460500224640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* * The trees builder */ #include #include "../player/finance.h" // convert_money #include "../world/simworld.h" #include "../tool/simtool.h" #include "../tool/simmenu.h" #include "../dataobj/translator.h" #include "../descriptor/image.h" #include "../descriptor/ground_desc.h" #include "../utils/cbuffer.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "groundobj_edit.h" // new tool definition tool_plant_groundobj_t groundobj_edit_frame_t::groundobj_tool; cbuffer_t groundobj_edit_frame_t::param_str; bool groundobj_edit_frame_t::sortreverse = false; static bool compare_groundobj_desc(const groundobj_desc_t* a, const groundobj_desc_t* b) { int diff = strcmp( a->get_name(), b->get_name() ); return groundobj_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_groundobj_desc_name(const groundobj_desc_t* a, const groundobj_desc_t* b) { int diff = strcmp( translator::translate(a->get_name()), translator::translate(b->get_name()) ); if(diff ==0) { diff = strcmp( a->get_name(), b->get_name() ); } return groundobj_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } static bool compare_groundobj_desc_cost(const groundobj_desc_t* a, const groundobj_desc_t* b) { int diff = a->get_price() - b->get_price(); if(diff ==0) { diff = strcmp( a->get_name(), b->get_name() ); } return groundobj_edit_frame_t::sortreverse ? diff > 0 : diff < 0; } groundobj_edit_frame_t::groundobj_edit_frame_t(player_t* player_) : extend_edit_gui_t(translator::translate("groundobj builder"), player_), groundobj_list(16) { cont_timeline.set_visible(false); cb_sortedby.new_component(gui_sorting_item_t::BY_REMOVAL); desc = NULL; groundobj_tool.set_default_param(NULL); fill_list(); // since we do not have a building image, we have to add the image again ourselves ... cont_scrolly.remove_all(); cont_scrolly.set_table_layout(2, 0); cont_scrolly.set_margin(scr_size(0, D_V_SPACE), scr_size(0, D_V_SPACE)); // add object description cont_scrolly.add_component(&info_text, 2); // add object image cont_scrolly.add_component(&groundobj_image); cont_scrolly.new_component(true, false); cont_scrolly.new_component_span(false, true, 2); } // fill the current groundobj_list void groundobj_edit_frame_t::fill_list() { groundobj_list.clear(); const uint8 sortedby = get_sortedby(); sortreverse = sort_order.pressed; for(groundobj_desc_t const* const i : groundobj_t::get_all_desc()) { if ( i && (i->get_allowed_climate_bits() & get_climate()) ) { switch(sortedby) { case gui_sorting_item_t::BY_NAME_TRANSLATED: groundobj_list.insert_ordered( i, compare_groundobj_desc_name ); break; case gui_sorting_item_t::BY_REMOVAL: groundobj_list.insert_ordered( i, compare_groundobj_desc_cost ); break; default: groundobj_list.insert_ordered( i, compare_groundobj_desc ); } } } // now build scrolled list scl.clear_elements(); scl.set_selection(-1); for(groundobj_desc_t const* const i : groundobj_list) { char const* const name = sortedby==gui_sorting_item_t::BY_NAME_OBJECT ? i->get_name() : translator::translate(i->get_name()); scl.new_component(name, SYSCOL_TEXT); if (i == desc) { scl.set_selection(scl.get_count()-1); } } // always update current selection (since the tool may depend on it) change_item_info( scl.get_selection() ); } void groundobj_edit_frame_t::change_item_info(sint32 entry) { buf.clear(); if(entry>=0 && entry<(sint32)groundobj_list.get_count()) { desc = groundobj_list[entry]; buf.append(translator::translate(desc->get_name())); buf.append("\n\n"); // climates buf.append( translator::translate("allowed climates:\n") ); uint16 cl = desc->get_allowed_climate_bits(); if(cl==0) { buf.append( translator::translate("None") ); buf.append("\n"); } else { for(uint16 i=0; i<=arctic_climate; i++ ) { if(cl & (1<get_seasons(); if( seasons > 1){ buf.printf( "\n%s\n", translator::translate("Has Snow")); buf.printf( "%s %i\n", translator::translate("Seasons"), desc->get_seasons()-1 ); } if(desc->get_phases() > 2){ buf.printf( "\n%s\n", translator::translate("Has slope graphics")); } if(desc->can_build_trees_here()){ buf.printf( "\n%s\n", translator::translate("Can be overgrown") ); } buf.printf("\n%s ", translator::translate("cost for removal")); buf.append_money( convert_money( desc->get_price() ) ); buf.append("\n"); if (char const* const maker = desc->get_copyright()) { buf.append("\n"); buf.printf(translator::translate("Constructed by %s"), maker); buf.append("\n"); } groundobj_image.set_image(desc->get_image_id( seasons>2 ? 2 : 0, 0 ), true); param_str.clear(); param_str.printf( "%i%i,%s", bt_climates.pressed, bt_timeline.pressed, desc->get_name() ); groundobj_tool.set_default_param(param_str); groundobj_tool.cursor = tool_t::general_tool[TOOL_PLANT_GROUNDOBJ]->cursor; welt->set_tool( &groundobj_tool, player ); } else if(welt->get_tool(player->get_player_nr())==&groundobj_tool) { desc = NULL; groundobj_image.set_image(IMG_EMPTY, true); welt->set_tool( tool_t::general_tool[TOOL_QUERY], player ); } info_text.recalc_size(); reset_min_windowsize(); } simutrans-124.5/src/simutrans/gui/groundobj_edit.h000066400000000000000000000021041520675460500223300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_GROUNDOBJ_EDIT_H #define GUI_GROUNDOBJ_EDIT_H #include "extend_edit.h" #include "components/gui_image.h" class groundobj_desc_t; class tool_plant_groundobj_t; /* * The groundobj builder */ class groundobj_edit_frame_t : public extend_edit_gui_t { private: static tool_plant_groundobj_t groundobj_tool; static cbuffer_t param_str; const groundobj_desc_t *desc; gui_image_t groundobj_image; vector_tplgroundobj_list; void fill_list() OVERRIDE; void change_item_info( sint32 i ) OVERRIDE; public: groundobj_edit_frame_t(player_t* player_); static bool sortreverse; /** * in top-level windows the name is displayed in titlebar * @return the non-translated component name */ const char* get_name() const { return "groundobj builder"; } /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char* get_help_filename() const OVERRIDE { return "groundobj_build.txt"; } }; #endif simutrans-124.5/src/simutrans/gui/gui_frame.cc000066400000000000000000000127141520675460500214360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "gui_frame.h" #include "../simcolor.h" #include "../dataobj/environment.h" #include "../display/simgraph.h" #include "simwin.h" #include "../world/simworld.h" #include "../player/simplay.h" #include "../descriptor/reader/obj_reader.h" #include "../descriptor/skin_desc.h" #include "../simskin.h" karte_ptr_t gui_frame_t::welt; // Insert the container gui_frame_t::gui_frame_t(char const* const name, player_t const* const player) { this->name = name; windowsize = scr_size(200, 100); min_windowsize = scr_size(0,0); owner = player; set_resizemode(no_resize); //25-may-02 markus weber added opaque = true; dirty = true; // set default margin and spacing gui_aligned_container_t::set_margin_from_theme(); gui_aligned_container_t::set_spacing_from_theme(); // initialize even if we cannot call has_title() here gui_aligned_container_t::set_pos(scr_coord(0, D_TITLEBAR_HEIGHT)); } /** * Set the window size */ void gui_frame_t::set_windowsize(scr_size new_windowsize) { gui_aligned_container_t::set_pos(scr_coord(0, has_title()*D_TITLEBAR_HEIGHT)); if( new_windowsize != windowsize ) { // mark old size dirty scr_coord const& pos = win_get_pos(this); gfx->mark_rect_dirty_wc( pos.x, pos.y, pos.x+windowsize.w, pos.y+windowsize.h ); // minimum size //25-may-02 markus weber added new_windowsize.clip_lefttop(min_windowsize); windowsize = new_windowsize; dirty = true; // TODO respect gui_aligned_container_t::get_max_size() } // recompute always, to react on resize(scr_coord(0,0)) if (gui_aligned_container_t::is_table()) { gui_aligned_container_t::set_size(get_client_windowsize()); } } void gui_frame_t::reset_min_windowsize() { gui_aligned_container_t::set_pos(scr_coord(0, has_title()*D_TITLEBAR_HEIGHT)); if (gui_aligned_container_t::is_table()) { const bool at_min_size = windowsize == min_windowsize; const scr_size csize = gui_aligned_container_t::get_min_size(); const scr_coord pos = gui_aligned_container_t::get_pos(); const scr_size new_min_windowsize(csize.w + pos.x, csize.h + pos.y); if (at_min_size) { set_windowsize(new_min_windowsize); } else { scr_size wsize = windowsize; wsize.clip_lefttop(new_min_windowsize); set_windowsize(wsize); } set_min_windowsize( new_min_windowsize ); } } /** * get color information for the window title * -borders and -body background */ FLAGGED_PIXVAL gui_frame_t::get_titlecolor() const { return owner ? (PLAYER_FLAG | gfx->palette_lookup(owner->get_player_color1()+env_t::gui_player_color_dark)) : env_t::default_window_title_color; } /** * Events are notified to GUI components via this method */ bool gui_frame_t::infowin_event(const event_t *ev) { // %DB0 printf( "\nMessage: gui_frame_t::infowin_event( event_t const * ev ) : Fenster|Window %p : Event is %d", (void*)this, ev->ev_class ); if (ev->ev_class==EVENT_SYSTEM && ev->ev_code == SYSTEM_THEME_CHANGED) { if (gui_aligned_container_t::is_table()) { reset_min_windowsize(); } return true; } if(IS_WINDOW_RESIZE(ev)) { scr_coord delta ( resize_mode & horizontal_resize ? ev->mouse_pos.x - ev->click_pos.x : 0, resize_mode & vertical_resize ? ev->mouse_pos.y - ev->click_pos.y : 0); resize(delta); return true; // don't pass to children! } else if(IS_WINDOW_MAKE_MIN_SIZE(ev)) { set_windowsize( get_min_windowsize() ) ; resize( scr_coord(0,0) ) ; return true; // don't pass to children! } else if(ev->ev_class==INFOWIN && (ev->ev_code==WIN_CLOSE || ev->ev_code==WIN_OPEN || ev->ev_code==WIN_TOP)) { dirty = true; gui_aligned_container_t::clear_dirty(); } event_t ev2 = *ev; ev2.move_origin(scr_coord(0, (int)has_title()*D_TITLEBAR_HEIGHT)); return gui_aligned_container_t::infowin_event(&ev2); } /** * resize window in response to a resize event */ void gui_frame_t::resize(const scr_coord delta) { dirty = true; scr_size new_size = windowsize + delta; // resize window to the minimum size new_size.clip_lefttop(min_windowsize); scr_coord size_change = new_size - windowsize; // resize window set_windowsize(new_size); // change drag start change_drag_start(size_change); } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void gui_frame_t::draw(scr_coord pos, scr_size size) { scr_size titlebar_size(0, ( has_title()*D_TITLEBAR_HEIGHT )); // draw background if( opaque ) { gfx->draw_stretch_map( gui_theme_t::windowback, scr_rect( pos + titlebar_size, size - titlebar_size ) ); if( dirty ) { gfx->mark_rect_dirty_wc(pos.x, pos.y, pos.x + size.w, pos.y + titlebar_size.h ); } } else { if( dirty ) { gfx->mark_rect_dirty_wc(pos.x, pos.y, pos.x + size.w, pos.y + size.h + titlebar_size.h ); } gfx->tint_rect( pos.x+1, pos.y+titlebar_size.h, size.w-2, size.h-titlebar_size.h, color_transparent, percent_transparent ); } dirty = false; PUSH_CLIP_FIT(pos.x+1, pos.y+titlebar_size.h+1, size.w-2, size.h-titlebar_size.h-2); gui_aligned_container_t::draw(pos); POP_CLIP(); // for shadows of the windows if( gui_theme_t::gui_drop_shadows ) { gfx->tint_rect( pos.x+size.w, pos.y+1, 2, size.h, gfx->palette_lookup(COL_BLACK), 50 ); gfx->tint_rect( pos.x+1, pos.y+size.h, size.w, 2, gfx->palette_lookup(COL_BLACK), 50 ); } } uint32 gui_frame_t::get_rdwr_id() { return magic_reserved; } simutrans-124.5/src/simutrans/gui/gui_frame.h000066400000000000000000000126251520675460500213010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_GUI_FRAME_H #define GUI_GUI_FRAME_H #include "../display/scr_coord.h" #include "../display/simgraph.h" #include "../simcolor.h" #include "../dataobj/koord3d.h" #include "components/gui_aligned_container.h" #include "components/gui_button.h" #include "gui_theme.h" class loadsave_t; class karte_ptr_t; class player_t; /** * A Class for window with Component. * Unlike other Window Classes in Simutrans, this is * a true component-oriented window that all actions * delegates to its component. */ class gui_frame_t : protected gui_aligned_container_t { public: /** * Resize modes */ enum resize_modes { no_resize = 0, vertical_resize = 1, horizontal_resize = 2, diagonal_resize = 3 }; private: const char *name; scr_size windowsize; ///< Size of the whole window (possibly with title bar) scr_size min_windowsize; ///< min size of the whole window resize_modes resize_mode; const player_t *owner; // set true for total redraw bool dirty:1; bool opaque:1; uint8 percent_transparent; PIXVAL color_transparent; using gui_aligned_container_t::draw; using gui_aligned_container_t::set_size; protected: void set_dirty() { dirty=true; } void unset_dirty() { dirty=false; } /** * resize window in response to a resize event */ virtual void resize(const scr_coord delta); void set_owner( const player_t *player ) { owner = player; } void set_transparent( uint8 percent, PIXVAL col ) { opaque = percent==0; percent_transparent = percent; color_transparent = col; } static karte_ptr_t welt; public: /** * @param name Window title * @param player owner for color */ gui_frame_t(const char *name, const player_t *player=NULL); virtual ~gui_frame_t() {} /** * The name is displayed in the titlebar * @return the non-translated name of the Component */ const char *get_name() const { return name; } /** * sets the Name (Window title) */ void set_name(const char *name) { this->name=name; } /** * This returns an unique id (different from magic_reserved), if the dialogue can be saved. */ virtual uint32 get_rdwr_id(); virtual void rdwr( loadsave_t * ) {} /** * get color information for the window title * -borders and -body background */ virtual FLAGGED_PIXVAL get_titlecolor() const; /** * @return gets the window sizes */ scr_size get_windowsize() const { return windowsize; } /** * Sets the window sizes */ virtual void set_windowsize(scr_size size); protected: /** * Set minimum size of the window */ void set_min_windowsize(scr_size new_size) { min_windowsize = new_size; } /** * Set minimum window size to minimum size of container. */ void reset_min_windowsize(); public: /** * Get minimum size of the window */ scr_size get_min_windowsize() { return min_windowsize; } /** * Max Kielland 2013: Client size auto calculation with title bar and margins. * @return the usable width and height of the window */ scr_size get_client_windowsize() const { return windowsize - scr_size(0, ( has_title()*D_TITLEBAR_HEIGHT ) ); } /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ virtual const char * get_help_filename() const {return NULL;} /** * Does this window need a next button in the title bar? * @return true if such a button is needed */ virtual bool has_next() const {return false;} /** * Does this window need a prev button in the title bar? * @return true if such a button is needed */ virtual bool has_prev() const {return has_next();} /** * Does this window need a sticky in the title bar? * @return true if such a button is needed */ virtual bool has_sticky() const { return true; } /** * Does this window need its title to be shown? * @return if false title and all gadgets will be not drawn */ virtual bool has_title() const { return true; } // position of a connected thing on the map // The calling parameter is only true when actually clicking the gadget virtual koord3d get_weltpos( bool /*set*/ ) { return koord3d::invalid; } // returns true, when the window show the current object already virtual bool is_weltpos() { return false; } bool is_dirty() const { return dirty; } /** * Set resize mode */ void set_resizemode(resize_modes mode) { resize_mode = mode; } /** * Get resize mode */ resize_modes get_resizemode() const { return resize_mode; } /** * Returns true, if inside window area. */ virtual bool is_hit(int x, int y) { scr_size size = get_windowsize(); return ( x>=0 && y>=0 && xpalette_lookup(COL_BLACK); gui_color_text_highlight = gfx->palette_lookup(COL_WHITE); gui_color_text_shadow = gfx->palette_lookup(COL_BLACK); gui_color_text_title = gfx->palette_lookup(207); gui_color_text_strong = gfx->palette_lookup(COL_RED); gui_color_text_minus = gfx->palette_lookup(COL_RED); gui_color_text_plus = gfx->palette_lookup(COL_BLACK); gui_color_text_unused = gfx->palette_lookup(COL_YELLOW); gui_color_edit_text = gfx->palette_lookup(COL_WHITE); gui_color_edit_text_selected = gfx->palette_lookup(COL_GREY5); gui_color_edit_text_disabled = gfx->palette_lookup(COL_GREY3); gui_color_edit_background_selected = gfx->palette_lookup(COL_GREY2); gui_color_edit_beam = gfx->palette_lookup(COL_WHITE); gui_color_chart_background = gfx->palette_lookup(MN_GREY1); gui_color_chart_lines_zero = gfx->palette_lookup(MN_GREY4); gui_color_chart_lines_odd = gfx->palette_lookup(COL_WHITE); gui_color_chart_lines_even = gfx->palette_lookup(MN_GREY0); gui_color_list_text_selected_focus = gfx->palette_lookup(COL_WHITE); gui_color_list_text_selected_nofocus = gfx->palette_lookup(MN_GREY3); gui_color_list_background_selected_f = gfx->palette_lookup(COL_BLUE); gui_color_list_background_selected_nf = gfx->palette_lookup(COL_LIGHT_BLUE); gui_color_button_text = gfx->palette_lookup(COL_BLACK); gui_color_button_text_disabled = gfx->palette_lookup(MN_GREY0); gui_color_button_text_selected = gfx->palette_lookup(COL_BLACK); gui_color_colored_button_text = gfx->palette_lookup(COL_BLACK); gui_color_colored_button_text_selected = gfx->palette_lookup(COL_WHITE); gui_color_checkbox_text = gfx->palette_lookup(COL_BLACK); gui_color_checkbox_text_disabled = gfx->palette_lookup(MN_GREY0); gui_color_ticker_background = gfx->palette_lookup(MN_GREY2); gui_color_ticker_divider = gfx->palette_lookup(COL_BLACK); gui_color_statusbar_text = gfx->palette_lookup(COL_BLACK); gui_color_statusbar_background = gfx->palette_lookup(MN_GREY1); gui_color_statusbar_divider = gfx->palette_lookup(MN_GREY4); gui_highlight_color = gfx->palette_lookup(MN_GREY4); gui_shadow_color = gfx->palette_lookup(MN_GREY0); gui_color_loadingbar_inner = gfx->palette_lookup(COL_GREY5); gui_color_loadingbar_progress = gfx->palette_lookup(COL_BLUE); gui_color_obsolete = gfx->palette_lookup(COL_BLUE); gui_color_empty = gfx->palette_lookup(COL_WHITE); gui_color_image_transparency = gfx->palette_lookup(COL_BLACK); gui_color_object_highlight = gfx->palette_lookup(COL_RED); env_t::gui_player_color_bright = 4; env_t::gui_player_color_dark = 1; gui_button_size = scr_size(92,14); gui_color_button_size = scr_size(92,16); gui_button_text_offset = scr_size(0,0); gui_color_button_text_offset = scr_size(0,0); gui_divider_size = scr_size(92,2+D_V_SPACE*2); gui_checkbox_size = scr_size(10,10); gui_pos_button_size = scr_size(14,LINESPACE); gui_arrow_left_size = scr_size(14,14); gui_arrow_right_size = scr_size(14,14); gui_arrow_up_size = scr_size(14,14); gui_arrow_down_size = scr_size(14,14); gui_scrollbar_size = scr_size(14,14); gui_min_scrollbar_size = scr_size(3,3); gui_label_size = scr_size(92,LINESPACE); gui_edit_size = scr_size(92,max(LINESPACE+2, max(D_ARROW_LEFT_HEIGHT, D_ARROW_RIGHT_HEIGHT) )); gui_gadget_size = scr_size(16,16); gui_indicator_size = scr_size(20,4); gui_focus_offset = scr_coord(1,1); gui_buttons_per_row = max(2, min(4, gfx->get_screen_size().w / (D_BUTTON_WIDTH + D_H_SPACE + D_MARGIN_LEFT))); gui_titlebar_height = 16; gui_frame_left = 10; gui_frame_top = 10; gui_frame_right = 10; gui_frame_bottom = 10; gui_hspace = 4; gui_vspace = 4; gui_filelist_vspace = 1; gui_waitingbar_width = 4; gui_divider_size.h = D_V_SPACE*2; gui_drop_shadows = false; gui_color_chat_window_network_transparency = gfx->palette_lookup(COL_WHITE); } // helper for easier init void gui_theme_t::init_size_from_image( const image_t *image, scr_size &k ) { if( image ) { k = scr_size(image->x+image->w,image->y+image->h); } } /** * Lazy button image number init */ void gui_theme_t::init_gui_from_images() { // Calculate posbutton size if( skinverwaltung_t::divider == NULL ) { // usually there should be a default theme which would provided missing images even for outdated themes dbg->fatal( "gui_theme_t::init_gui_themes", "Wrong theme loaded" ); } init_size_from_image( skinverwaltung_t::posbutton->get_image( SKIN_BUTTON_POS ), gui_pos_button_size ); init_size_from_image( skinverwaltung_t::check_button->get_image( SKIN_BUTTON_CHECKBOX ), gui_checkbox_size ); for( int i=0; i<3; i++ ) { pos_button_img[i] = skinverwaltung_t::posbutton->get_image_id( SKIN_BUTTON_POS+i ); check_button_img[i] = skinverwaltung_t::check_button->get_image_id( SKIN_BUTTON_CHECKBOX+i ); } // Normal buttons (colorful ones) scr_coord_val y = gui_button_size.h; scr_size k; init_size_from_image( skinverwaltung_t::button->get_image( SKIN_BUTTON_SIDE_LEFT ), k ); y = max( y, k.h ); init_size_from_image( skinverwaltung_t::button->get_image( SKIN_BUTTON_SIDE_RIGHT ), k ); y = max( y, k.h ); init_size_from_image( skinverwaltung_t::button->get_image( SKIN_BUTTON_BODY ), k ); y = max( y, k.h ); for( int i=0; i<3; i++ ) { for( int j=0; j<9; j++ ) { button_tiles[i][j%3][j/3] = skinverwaltung_t::button->get_image_id( i*9+j ); } } image_id has_second_mask = 0xFFFF; for( int i=0; i<2; i++ ) { has_second_mask = 0xFFFF; for( int j=0; j<9; j++ ) { button_color_tiles[i][j%3][j/3] = skinverwaltung_t::button->get_image_id( i*9+j+27 ); has_second_mask &= button_color_tiles[i][j%3][j/3]; } } if( has_second_mask == 0xFFFF ) { // has no second mask => copy first for( int j=0; j<9; j++ ) { button_color_tiles[1][j%3][j/3] = button_color_tiles[0][j%3][j/3]; } } // Round buttons for( int i=0; i<3; i++ ) { for( int j=0; j<9; j++ ) { round_button_tiles[i][j%3][j/3] = skinverwaltung_t::round_button->get_image_id( i*9+j ); } } // background for editfields, listbuttons, and windows for( int j=0; j<9; j++ ) { editfield[j%3][j/3] = skinverwaltung_t::editfield->get_image_id( j ); listbox[j%3][j/3] = skinverwaltung_t::listbox->get_image_id( j ); windowback[j%3][j/3] = skinverwaltung_t::back->get_image_id( j ); } // Divider (vspace will be added later on) init_size_from_image( skinverwaltung_t::divider->get_image(1), gui_divider_size ); for( int i=0; i<3; i++ ) { divider[i][0] = skinverwaltung_t::divider->get_image_id( i ); divider[i][1] = IMG_EMPTY; divider[i][2] = IMG_EMPTY; } // Calculate arrow size init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_BUTTON_ARROW_LEFT ), gui_arrow_left_size ); init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_BUTTON_ARROW_RIGHT ), gui_arrow_right_size ); init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_BUTTON_ARROW_UP ), gui_arrow_up_size ); init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_BUTTON_ARROW_DOWN ), gui_arrow_down_size ); for( int i=0; i<3; i++ ) { arrow_button_left_img[i] = skinverwaltung_t::scrollbar->get_image_id( SKIN_BUTTON_ARROW_LEFT+i ); arrow_button_right_img[i] = skinverwaltung_t::scrollbar->get_image_id( SKIN_BUTTON_ARROW_RIGHT+i ); arrow_button_up_img[i] = skinverwaltung_t::scrollbar->get_image_id( SKIN_BUTTON_ARROW_UP+i ); arrow_button_down_img[i] = skinverwaltung_t::scrollbar->get_image_id( SKIN_BUTTON_ARROW_DOWN+i ); } if( gui_theme_t::gui_arrow_right_size != gui_theme_t::gui_arrow_left_size ) { dbg->warning( "gui_theme_t::themes_init()", "Size of left and right arrows differ" ); } if( gui_theme_t::gui_arrow_up_size != gui_theme_t::gui_arrow_down_size ) { dbg->warning( "gui_theme_t::themes_init()", "Size of up and down arrows differ" ); } // now init this button dependent size here too gui_edit_size = scr_size(92,max(LINESPACE+2, max(D_ARROW_LEFT_HEIGHT, D_ARROW_RIGHT_HEIGHT) )); // init horizontal scrollbar buttons for( int i=0; i<3; i++ ) { h_scroll_back_tiles[i][0] = skinverwaltung_t::scrollbar->get_image_id( SKIN_SCROLLBAR_H_BACKGROUND_LEFT+i ); h_scroll_back_tiles[i][1] = IMG_EMPTY; h_scroll_back_tiles[i][2] = IMG_EMPTY; h_scroll_knob_tiles[i][0] = skinverwaltung_t::scrollbar->get_image_id( SKIN_SCROLLBAR_H_KNOB_LEFT+i ); h_scroll_knob_tiles[i][1] = IMG_EMPTY; h_scroll_knob_tiles[i][2] = IMG_EMPTY; } // init vertical scrollbar buttons for( int i=0; i<3; i++ ) { v_scroll_back_tiles[0][i] = skinverwaltung_t::scrollbar->get_image_id( SKIN_SCROLLBAR_V_BACKGROUND_TOP+i ); v_scroll_back_tiles[1][i] = IMG_EMPTY; v_scroll_back_tiles[2][i] = IMG_EMPTY; v_scroll_knob_tiles[0][i] = skinverwaltung_t::scrollbar->get_image_id( SKIN_SCROLLBAR_V_KNOB_TOP+i ); v_scroll_knob_tiles[1][i] = IMG_EMPTY; v_scroll_knob_tiles[2][i] = IMG_EMPTY; } // Calculate V scrollbar size { scr_size back, front; init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_SCROLLBAR_V_BACKGROUND ), back ); init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_SCROLLBAR_V_KNOB_BODY ), front ); gui_scrollbar_size.w = max(front.w, back.w); // Calculate H scrollbar size init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_SCROLLBAR_H_BACKGROUND ), back ); init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_SCROLLBAR_H_KNOB_BODY ), front ); gui_scrollbar_size.h = max(front.h, back.h); // calculate minimum width init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_SCROLLBAR_H_KNOB_LEFT ), back ); init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_SCROLLBAR_H_KNOB_RIGHT ), front ); gui_min_scrollbar_size.w = back.w + front.w; // calculate minimum height init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_SCROLLBAR_V_KNOB_TOP ), back ); init_size_from_image( skinverwaltung_t::scrollbar->get_image( SKIN_SCROLLBAR_V_KNOB_BOTTOM ), front ); gui_min_scrollbar_size.h = back.h + front.h; } // gadgets gui_dragger_size = gui_scrollbar_size; if (skinverwaltung_t::gadget) { init_size_from_image( skinverwaltung_t::gadget->get_image( SKIN_GADGET_CLOSE ), gui_gadget_size ); } } /** * Reads theme configuration data, still not final * * Note, there will be a theme manager later on and * each gui object will find their own parameters by * themselves after registering its class to the theme * manager. This will be done as the last step in * the chain when loading a theme. */ bool gui_theme_t::themes_init(const char *file_name, bool init_fonts, bool init_tools ) { tabfile_t themesconf; // first take user data, then user global data if( !themesconf.open(file_name) ) { dbg->warning("simwin.cc themes_init()", "Can't read themes from %s", file_name ); return false; } // define a default even when stuff is missing from the table init_gui_defaults(); tabfileobj_t contents; themesconf.read(contents); // theme name to find out current theme std::string theme_name = contents.get( "name" ); // reload current font if requested size differs and we are allowed to do so uint8 new_size = contents.get_int("font_size", env_t::fontsize ); if( init_fonts && new_size!=0 && LINESPACE!=new_size ) { if( gfx->load_font(env_t::fontname.c_str(), false) ) { env_t::fontsize = new_size; } } // first get the images ( to be able to overload default sizes) const std::string buttonpak = contents.get("themeimages"); if( buttonpak.length()>0 ) { std::string path; char *pathname = strdup(file_name); if( char *s = strrchr( pathname, '/' ) ) { *s = 0; } else if( char *s = strrchr( pathname, '\\' ) ) { *s = 0; } dr_chdir( pathname ); pakset_manager_t::load_pak_file(buttonpak); gui_theme_t::init_gui_from_images(); free(pathname); dr_chdir(env_t::user_dir); } // first the stuff for the dialogues gui_theme_t::gui_titlebar_height = (uint32)contents.get_int("gui_titlebar_height", gui_theme_t::gui_titlebar_height ); gui_theme_t::gui_frame_left = (uint32)contents.get_int("gui_frame_left", gui_theme_t::gui_frame_left ); gui_theme_t::gui_frame_top = (uint32)contents.get_int("gui_frame_top", gui_theme_t::gui_frame_top ); gui_theme_t::gui_frame_right = (uint32)contents.get_int("gui_frame_right", gui_theme_t::gui_frame_right ); gui_theme_t::gui_frame_bottom = (uint32)contents.get_int("gui_frame_bottom", gui_theme_t::gui_frame_bottom ); gui_theme_t::gui_hspace = (uint32)contents.get_int("gui_hspace", gui_theme_t::gui_hspace ); gui_theme_t::gui_vspace = (uint32)contents.get_int("gui_vspace", gui_theme_t::gui_vspace ); gui_theme_t::gui_filelist_vspace = (uint32)contents.get_int("gui_filelist_vspace", gui_theme_t::gui_filelist_vspace ); // the divider needs the vspace added to it for know gui_divider_size.h += gui_vspace*2; gui_theme_t::gui_divider_size.h = contents.get_int("gui_divider_vsize", gui_theme_t::gui_divider_size.h ); gui_theme_t::gui_button_size.w = (uint32)contents.get_int("gui_button_width", gui_theme_t::gui_button_size.w ); gui_theme_t::gui_button_size.h = (uint32)contents.get_int("gui_button_height", gui_theme_t::gui_button_size.h ); gui_theme_t::gui_edit_size.h = (uint32)contents.get_int("gui_edit_height", gui_theme_t::gui_edit_size.h ); gui_theme_t::gui_checkbox_size.w = (uint32)contents.get_int("gui_checkbox_width", gui_theme_t::gui_checkbox_size.w ); gui_theme_t::gui_checkbox_size.h = (uint32)contents.get_int("gui_checkbox_height", gui_theme_t::gui_checkbox_size.h ); gui_theme_t::gui_gadget_size.w = (uint32)contents.get_int("gui_gadget_width", gui_theme_t::gui_gadget_size.w ); gui_theme_t::gui_gadget_size.h = (uint32)contents.get_int("gui_gadget_height", gui_theme_t::gui_gadget_size.h ); // make them fit at least the font height gui_theme_t::gui_titlebar_height = max( LINESPACE+2, gui_theme_t::gui_titlebar_height ); gui_theme_t::gui_button_size.h = max( LINESPACE+2, gui_theme_t::gui_button_size.h ); gui_theme_t::gui_edit_size.h = max( LINESPACE+2, gui_theme_t::gui_edit_size.h ); // since the arrows are used in scrollbars, the need similar sizes gui_theme_t::gui_arrow_left_size.w = (uint32)contents.get_int("gui_horizontal_arrow_width", gui_theme_t::gui_arrow_left_size.w ); gui_theme_t::gui_arrow_left_size.h = (uint32)contents.get_int("gui_horizontal_arrow_height", gui_theme_t::gui_arrow_left_size.h ); gui_theme_t::gui_arrow_right_size = gui_theme_t::gui_arrow_left_size; gui_theme_t::gui_arrow_up_size.w = (uint32)contents.get_int("gui_vertical_arrow_width", gui_theme_t::gui_arrow_up_size.w ); gui_theme_t::gui_arrow_up_size.h = (uint32)contents.get_int("gui_vertical_arrow_height", gui_theme_t::gui_arrow_up_size.h ); gui_theme_t::gui_arrow_down_size = gui_theme_t::gui_arrow_up_size; // since scrollbar must have a certain size gui_theme_t::gui_scrollbar_size.w = max( gui_min_scrollbar_size.w, (uint32)contents.get_int("gui_scrollbar_width", gui_theme_t::gui_scrollbar_size.w ) ); gui_theme_t::gui_scrollbar_size.h = max( gui_min_scrollbar_size.h, (uint32)contents.get_int("gui_scrollbar_height", gui_theme_t::gui_scrollbar_size.h ) ); // dragger size must be as large as scrollbar size if (skinverwaltung_t::gadget) { init_size_from_image( skinverwaltung_t::gadget->get_image( SKIN_WINDOW_RESIZE), gui_dragger_size ); gui_dragger_size.clip_lefttop(scr_coord(gui_scrollbar_size.w, gui_scrollbar_size.h)); } // in practice, posbutton min height better is LINESPACE gui_theme_t::gui_pos_button_size.w = (uint32)contents.get_int("gui_posbutton_width", gui_theme_t::gui_pos_button_size.w ); gui_theme_t::gui_pos_button_size.h = (uint32)contents.get_int("gui_posbutton_height", gui_theme_t::gui_pos_button_size.h ); // read ../dataobj/tabfile.h for clarification of this area vector_tpl color_button_text_offsets = contents.get_ints("gui_color_button_text_offset"); if( color_button_text_offsets.get_count() > 2 ) { gui_theme_t::gui_color_button_text_offset = scr_size(color_button_text_offsets[0], color_button_text_offsets[1]); gui_theme_t::gui_color_button_text_offset_right = scr_coord(color_button_text_offsets[2], 0); } vector_tpl button_text_offsets = contents.get_ints("gui_button_text_offset"); if( button_text_offsets.get_count() > 2 ) { gui_theme_t::gui_button_text_offset = scr_size(button_text_offsets[0], button_text_offsets[1]); gui_theme_t::gui_button_text_offset_right = scr_coord(button_text_offsets[2], 0); } // maybe not the best place, rather use simwin for the static defines? gui_theme_t::gui_color_text = (PIXVAL)contents.get_color("gui_color_text", SYSCOL_TEXT); gui_theme_t::gui_color_text_highlight = (PIXVAL)contents.get_color("gui_color_text_highlight", SYSCOL_TEXT_HIGHLIGHT); gui_theme_t::gui_color_text_shadow = (PIXVAL)contents.get_color("gui_color_text_shadow", SYSCOL_TEXT_SHADOW); gui_theme_t::gui_color_text_title = (PIXVAL)contents.get_color("gui_color_text_title", SYSCOL_TEXT_TITLE); gui_theme_t::gui_color_text_strong = (PIXVAL)contents.get_color("gui_color_text_strong", SYSCOL_TEXT_STRONG); gui_theme_t::gui_color_text_minus = (PIXVAL)contents.get_color("gui_color_text_minus", MONEY_MINUS); gui_theme_t::gui_color_text_plus = (PIXVAL)contents.get_color("gui_color_text_plus", MONEY_PLUS); gui_theme_t::gui_color_text_unused = (PIXVAL)contents.get_color("gui_color_text_unused", SYSCOL_TEXT_UNUSED); gui_theme_t::gui_color_edit_text = (PIXVAL)contents.get_color("gui_color_edit_text", SYSCOL_EDIT_TEXT); gui_theme_t::gui_color_edit_text_selected = (PIXVAL)contents.get_color("gui_color_edit_text_selected", SYSCOL_EDIT_TEXT_SELECTED); gui_theme_t::gui_color_edit_text_disabled = (PIXVAL)contents.get_color("gui_color_edit_text_disabled", SYSCOL_EDIT_TEXT_DISABLED); gui_theme_t::gui_color_edit_background_selected = (PIXVAL)contents.get_color("gui_color_edit_background_selected", SYSCOL_EDIT_BACKGROUND_SELECTED); gui_theme_t::gui_color_edit_beam = (PIXVAL)contents.get_color("gui_color_edit_beam", SYSCOL_CURSOR_BEAM); gui_theme_t::gui_color_chart_background = (PIXVAL)contents.get_color("gui_color_chart_background", SYSCOL_CHART_BACKGROUND); gui_theme_t::gui_color_chart_lines_zero = (PIXVAL)contents.get_color("gui_color_chart_lines_zero", SYSCOL_CHART_LINES_ZERO); gui_theme_t::gui_color_chart_lines_odd = (PIXVAL)contents.get_color("gui_color_chart_lines_odd", SYSCOL_CHART_LINES_ODD); gui_theme_t::gui_color_chart_lines_even = (PIXVAL)contents.get_color("gui_color_chart_lines_even", SYSCOL_CHART_LINES_EVEN); gui_theme_t::gui_color_list_text_selected_focus = (PIXVAL)contents.get_color("gui_color_list_text_selected_focus", SYSCOL_LIST_TEXT_SELECTED_FOCUS); gui_theme_t::gui_color_list_text_selected_nofocus = (PIXVAL)contents.get_color("gui_color_list_text_selected_nofocus", SYSCOL_LIST_TEXT_SELECTED_NOFOCUS); gui_theme_t::gui_color_list_background_selected_f = (PIXVAL)contents.get_color("gui_color_list_background_selected_focus", SYSCOL_LIST_BACKGROUND_SELECTED_F); gui_theme_t::gui_color_list_background_selected_nf = (PIXVAL)contents.get_color("gui_color_list_background_selected_nofocus", SYSCOL_LIST_BACKGROUND_SELECTED_NF); gui_theme_t::gui_color_button_text = (PIXVAL)contents.get_color("gui_color_button_text", SYSCOL_BUTTON_TEXT); gui_theme_t::gui_color_button_text_disabled = (PIXVAL)contents.get_color("gui_color_button_text_disabled", SYSCOL_BUTTON_TEXT_DISABLED); gui_theme_t::gui_color_button_text_selected = (PIXVAL)contents.get_color("gui_color_button_text_selected", SYSCOL_BUTTON_TEXT_SELECTED); gui_theme_t::gui_color_colored_button_text = (PIXVAL)contents.get_color("gui_color_colored_button_text", SYSCOL_COLORED_BUTTON_TEXT); gui_theme_t::gui_color_colored_button_text_selected = (PIXVAL)contents.get_color("gui_color_colored_button_text_selected", SYSCOL_COLORED_BUTTON_TEXT_SELECTED); gui_theme_t::gui_color_checkbox_text = (PIXVAL)contents.get_color("gui_color_checkbox_text", SYSCOL_CHECKBOX_TEXT); gui_theme_t::gui_color_checkbox_text_disabled = (PIXVAL)contents.get_color("gui_color_checkbox_text_disabled", SYSCOL_CHECKBOX_TEXT_DISABLED); gui_theme_t::gui_color_ticker_background = (PIXVAL)contents.get_color("gui_color_ticker_background", SYSCOL_TICKER_BACKGROUND); gui_theme_t::gui_color_ticker_divider = (PIXVAL)contents.get_color("gui_color_ticker_divider", SYSCOL_TICKER_DIVIDER); gui_theme_t::gui_color_statusbar_text = (PIXVAL)contents.get_color("gui_color_statusbar_text", SYSCOL_STATUSBAR_TEXT); gui_theme_t::gui_color_statusbar_background = (PIXVAL)contents.get_color("gui_color_statusbar_background", SYSCOL_STATUSBAR_BACKGROUND); gui_theme_t::gui_color_statusbar_divider = (PIXVAL)contents.get_color("gui_color_statusbar_divider", SYSCOL_STATUSBAR_DIVIDER); gui_theme_t::gui_highlight_color = (PIXVAL)contents.get_color("gui_highlight_color", SYSCOL_HIGHLIGHT); gui_theme_t::gui_shadow_color = (PIXVAL)contents.get_color("gui_shadow_color", SYSCOL_SHADOW); gui_theme_t::gui_color_loadingbar_inner = (PIXVAL)contents.get_color("gui_color_loadingbar_inner", SYSCOL_LOADINGBAR_INNER); gui_theme_t::gui_color_loadingbar_progress = (PIXVAL)contents.get_color("gui_color_loadingbar_progress", SYSCOL_LOADINGBAR_PROGRESS); gui_theme_t::gui_color_obsolete = (PIXVAL)contents.get_color("gui_color_obsolete", SYSCOL_OBSOLETE); gui_theme_t::gui_color_empty = (PIXVAL)contents.get_color("gui_color_empty", SYSCOL_EMPTY); gui_theme_t::gui_color_chat_window_network_transparency = (PIXVAL)contents.get_color("gui_color_chat_window_network_transparency", gui_color_chat_window_network_transparency); gui_theme_t::gui_color_image_transparency = (PIXVAL)contents.get_color("gui_color_image_transparency", SYSCOL_IMAGE_TRANSPARENCY); gui_theme_t::gui_color_object_highlight = (PIXVAL)contents.get_color("gui_color_object_highlight", SYSCOL_OBJECT_HIGHLIGHT); gui_theme_t::gui_waitingbar_width = (uint32)contents.get_int("gui_waitingbar_width", gui_theme_t::gui_waitingbar_width); // those two may be rather an own control later on? gui_theme_t::gui_indicator_size = contents.get_scr_size("gui_indicator_size", gui_theme_t::gui_indicator_size ); gui_tab_panel_t::header_vsize = (uint32)contents.get_int("gui_tab_header_vsize", gui_tab_panel_t::header_vsize ); // stuff in env_t but rather GUI env_t::window_buttons_right = contents.get_int("window_buttons_right", env_t::window_buttons_right ); env_t::left_to_right_graphs = contents.get_int("left_to_right_graphs", env_t::left_to_right_graphs ); env_t::window_frame_active = contents.get_int("window_frame_active", env_t::window_frame_active ); env_t::second_open_closes_win = contents.get_int("second_open_closes_win", env_t::second_open_closes_win ); env_t::remember_window_positions = contents.get_int("remember_window_positions", env_t::remember_window_positions ); env_t::window_snap_distance = contents.get_int("window_snap_distance", env_t::window_snap_distance ); gui_theme_t::gui_drop_shadows = contents.get_int("gui_drop_shadows", gui_theme_t::gui_drop_shadows ); env_t::bottom_window_darkness = contents.get_int("bottom_window_darkness", env_t::bottom_window_darkness ); env_t::menupos = contents.get_int("menubar_position", env_t::menupos); env_t::gui_player_color_bright = contents.get_int("gui_player_color_bright", env_t::gui_player_color_bright ); env_t::gui_player_color_dark = contents.get_int("gui_player_color_dark", env_t::gui_player_color_dark ); env_t::default_window_title_color = contents.get_color("default_window_title_color", env_t::default_window_title_color, &env_t::default_window_title_color_rgb ); env_t::front_window_text_color = contents.get_color("front_window_text_color", env_t::front_window_text_color, &env_t::front_window_text_color_rgb ); env_t::bottom_window_text_color = contents.get_color("bottom_window_text_color", env_t::bottom_window_text_color, &env_t::bottom_window_text_color_rgb ); env_t::cursor_overlay_color = contents.get_color("cursor_overlay_color", env_t::cursor_overlay_color, &env_t::cursor_overlay_color_rgb ); env_t::tooltip_color = contents.get_color("tooltip_background_color", env_t::tooltip_color, &env_t::tooltip_color_rgb ); env_t::tooltip_textcolor = contents.get_color("tooltip_text_color", env_t::tooltip_textcolor, &env_t::tooltip_textcolor_rgb ); env_t::show_tooltips = contents.get_int("show_tooltips", env_t::show_tooltips ); env_t::tooltip_delay = contents.get_int("tooltip_delay", env_t::tooltip_delay ); env_t::tooltip_duration = contents.get_int("tooltip_duration", env_t::tooltip_duration ); env_t::toolbar_max_width = contents.get_int("toolbar_max_width", env_t::toolbar_max_width ); env_t::toolbar_max_height = contents.get_int("toolbar_max_height", env_t::toolbar_max_height ); // default iconsize scaling (must be read before the images) env_t::iconscaling = contents.get_int("icon_scaling", env_t::iconscaling); if (skinverwaltung_t::tool_icons_general) { env_t::iconsize = gfx->get_best_matching_size(skinverwaltung_t::tool_icons_general->get_image_id(0), env_t::iconscaling); } env_t::chat_window_transparency = 100 - contents.get_int("gui_chat_window_network_transparency", 100 - env_t::chat_window_transparency); // needs to be changed after resize events too gui_buttons_per_row = max(2, min(4, gfx->get_screen_size().w / (D_BUTTON_WIDTH + D_H_SPACE + D_MARGIN_LEFT))); if( toolbar_last_used_t::last_used_tools && init_tools ) { // only re-init if already inited tool_t::update_toolbars(); } env_t::default_theme = file_name; return true; } simutrans-124.5/src/simutrans/gui/gui_theme.h000066400000000000000000000365621520675460500213170ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_GUI_THEME_H #define GUI_GUI_THEME_H #include "../dataobj/koord.h" #include "../simcolor.h" #include "../display/simgraph.h" class image_t; /* * The following gives positioning aids for elements in dialogues * Only those, LINESPACE, and dimensions of elements itself must be * exclusively used to calculate positions in dialogues to have a * scalable interface * * Max Kielland: * Added more defines for theme testing. * These is going to be moved into the theme handling later. */ #define D_BUTTONS_PER_ROW (gui_theme_t::gui_buttons_per_row) #define D_BUTTON_SIZE (gui_theme_t::gui_button_size ) #define D_BUTTON_WIDTH (gui_theme_t::gui_button_size.w) #define D_BUTTON_HEIGHT (gui_theme_t::gui_button_size.h) #define D_BUTTON_PADDINGS_X (gui_theme_t::gui_button_text_offset.w + gui_theme_t::gui_button_text_offset_right.x) #define D_FILTER_BUTTON_SIZE (gui_theme_t::gui_color_button_size ) #define D_FILTER_BUTTON_WIDTH (gui_theme_t::gui_color_button_size.w) #define D_FILTER_BUTTON_HEIGHT (gui_theme_t::gui_color_button_size.h) #define D_CHECKBOX_SIZE (gui_theme_t::gui_checkbox_size ) #define D_CHECKBOX_WIDTH (gui_theme_t::gui_checkbox_size.w) #define D_CHECKBOX_HEIGHT (gui_theme_t::gui_checkbox_size.h) #define D_POS_BUTTON_SIZE (gui_theme_t::gui_pos_button_size ) #define D_POS_BUTTON_WIDTH (gui_theme_t::gui_pos_button_size.w) #define D_POS_BUTTON_HEIGHT (gui_theme_t::gui_pos_button_size.h) #define D_ARROW_LEFT_SIZE (gui_theme_t::gui_arrow_left_size ) #define D_ARROW_LEFT_WIDTH (gui_theme_t::gui_arrow_left_size.w) #define D_ARROW_LEFT_HEIGHT (gui_theme_t::gui_arrow_left_size.h) #define D_ARROW_RIGHT_SIZE (gui_theme_t::gui_arrow_right_size ) #define D_ARROW_RIGHT_WIDTH (gui_theme_t::gui_arrow_right_size.w) #define D_ARROW_RIGHT_HEIGHT (gui_theme_t::gui_arrow_right_size.h) #define D_ARROW_UP_SIZE (gui_theme_t::gui_arrow_up_size ) #define D_ARROW_UP_WIDTH (gui_theme_t::gui_arrow_up_size.w) #define D_ARROW_UP_HEIGHT (gui_theme_t::gui_arrow_up_size.h) #define D_ARROW_DOWN_SIZE (gui_theme_t::gui_arrow_down_size ) #define D_ARROW_DOWN_WIDTH (gui_theme_t::gui_arrow_down_size.w) #define D_ARROW_DOWN_HEIGHT (gui_theme_t::gui_arrow_down_size.h) #define D_SCROLLBAR_SIZE (gui_theme_t::gui_scrollbar_size ) #define D_SCROLLBAR_HEIGHT (gui_theme_t::gui_scrollbar_size.h) #define D_SCROLLBAR_WIDTH (gui_theme_t::gui_scrollbar_size.w) #define D_SCROLL_MIN_SIZE (gui_theme_t::gui_min_scrollbar_size ) #define D_SCROLL_MIN_WIDTH (gui_theme_t::gui_min_scrollbar_size.w) #define D_SCROLL_MIN_HEIGHT (gui_theme_t::gui_min_scrollbar_size.h) #define D_GADGET_SIZE (gui_theme_t::gui_gadget_size ) #define D_GADGET_WIDTH (gui_theme_t::gui_gadget_size.w) #define D_GADGET_HEIGHT (gui_theme_t::gui_gadget_size.h) #define D_DRAGGER_SIZE (gui_theme_t::gui_dragger_size ) #define D_DRAGGER_WIDTH (gui_theme_t::gui_dragger_size.w) #define D_DRAGGER_HEIGHT (gui_theme_t::gui_dragger_size.h) #define D_INDICATOR_BOX_SIZE (gui_theme_t::gui_indicator_size ) #define D_INDICATOR_BOX_HEIGHT (gui_theme_t::gui_indicator_size.h) #define D_INDICATOR_BOX_WIDTH (gui_theme_t::gui_indicator_size.w) #define D_INDICATOR_SIZE (gui_theme_t::gui_indicator_size ) #define D_INDICATOR_WIDTH (gui_theme_t::gui_indicator_size.w) #define D_INDICATOR_HEIGHT (gui_theme_t::gui_indicator_size.h) #define D_LABEL_SIZE (gui_theme_t::gui_label_size ) #define D_LABEL_WIDTH (gui_theme_t::gui_label_size.w) #define D_LABEL_HEIGHT (gui_theme_t::gui_label_size.h) #define D_EDIT_SIZE (gui_theme_t::gui_edit_size ) #define D_EDIT_WIDTH (gui_theme_t::gui_edit_size.w) #define D_EDIT_HEIGHT (gui_theme_t::gui_edit_size.h) #define D_FOCUS_OFFSET (gui_theme_t::gui_focus_offset ) #define D_FOCUS_OFFSET_H (gui_theme_t::gui_focus_offset.x) #define D_FOCUS_OFFSET_V (gui_theme_t::gui_focus_offset.y) #define D_TITLEBAR_HEIGHT (gui_theme_t::gui_titlebar_height) #define D_DIVIDER_HEIGHT (gui_theme_t::gui_divider_size.h) #define D_STATUSBAR_HEIGHT (max(16,LINESPACE)) // statusbar bottom of screen #define D_TAB_HEADER_HEIGHT (gui_tab_panel_t::header_vsize) // Tab page params (replace with real values from the skin images) // Dialog borders #define D_MARGIN_LEFT (gui_theme_t::gui_frame_left) #define D_MARGIN_TOP (gui_theme_t::gui_frame_top) #define D_MARGIN_RIGHT (gui_theme_t::gui_frame_right) #define D_MARGIN_BOTTOM (gui_theme_t::gui_frame_bottom) // Dialogue border helpers #define D_MARGINS_X (D_MARGIN_LEFT + D_MARGIN_RIGHT) #define D_MARGINS_Y (D_MARGIN_TOP + D_MARGIN_BOTTOM) // space between two elements #define D_H_SPACE (gui_theme_t::gui_hspace) #define D_V_SPACE (gui_theme_t::gui_vspace) // bars of goods waiting in stations #define D_WAITINGBAR_WIDTH (gui_theme_t::gui_waitingbar_width) #if 0 // Button grid helpers #define BUTTON1_X (D_MARGIN_LEFT) #define BUTTON2_X (D_MARGIN_LEFT+1*(D_BUTTON_WIDTH+D_H_SPACE)) #define BUTTON3_X (D_MARGIN_LEFT+2*(D_BUTTON_WIDTH+D_H_SPACE)) #define BUTTON4_X (D_MARGIN_LEFT+3*(D_BUTTON_WIDTH+D_H_SPACE)) #define BUTTON_X(col) ( (col) * (D_BUTTON_WIDTH + D_H_SPACE) ) #define BUTTON_Y(row) ( (row) * (D_BUTTON_HEIGHT + D_V_SPACE) ) #endif // The width of a typical dialogue (either list/covoi/factory) and initial width when it makes sense #define D_DEFAULT_WIDTH (D_MARGINS_X + D_BUTTONS_PER_ROW*D_BUTTON_WIDTH + (D_BUTTONS_PER_ROW-1)*D_H_SPACE) #define D_DEFAULT_HEIGHT (max(56, gfx->get_base_tile_raster_width() * 7 / 8) + 208 + D_SCROLLBAR_HEIGHT) // Max Kielland: align helper, returns the offset to apply to N1 for a center alignment around N2 #define D_GET_CENTER_ALIGN_OFFSET(N1,N2) ((N2-N1)>>1) #define D_GET_FAR_ALIGN_OFFSET(N1,N2) (N2-N1) #define D_FILELIST_V_SPACE (gui_theme_t::gui_filelist_vspace) #define TOOLTIP_MOUSE_OFFSET_X (16) #define TOOLTIP_MOUSE_OFFSET_Y (12) #define TOOLTIP_MOUSE_OFFSET (scr_coord{ TOOLTIP_MOUSE_OFFSET_X, TOOLTIP_MOUSE_OFFSET_Y }) // these define the offset of images in their definitions enum gadget_t { SKIN_WINDOW_BACKGROUND=0, // gadget (window GUI buttons) SKIN_GADGET_CLOSE=0, SKIN_GADGET_HELP, SKIN_GADGET_MINIMIZE, SKIN_BUTTON_PREVIOUS, SKIN_BUTTON_NEXT, SKIN_GADGET_NOTPINNED, SKIN_GADGET_PINNED, SKIN_WINDOW_RESIZE, SKIN_GADGET_GOTOPOS, // SKIN_GADGET_BUTTON, SKIN_GADGET_COUNT, // maximum number, NOT AN IMAGE // scrollbars horizontal SKIN_BUTTON_ARROW_LEFT = 0, SKIN_BUTTON_ARROW_LEFT_PRESSED, SKIN_BUTTON_ARROW_LEFT_DISABLED, SKIN_BUTTON_ARROW_RIGHT, SKIN_BUTTON_ARROW_RIGHT_PRESSED, SKIN_BUTTON_ARROW_RIGHT_DISABLED, SKIN_SCROLLBAR_H_BACKGROUND_LEFT, SKIN_SCROLLBAR_H_BACKGROUND, SKIN_SCROLLBAR_H_BACKGROUND_RIGHT, SKIN_SCROLLBAR_H_KNOB_LEFT, SKIN_SCROLLBAR_H_KNOB_BODY, SKIN_SCROLLBAR_H_KNOB_RIGHT, // and vertical SKIN_BUTTON_ARROW_UP, SKIN_BUTTON_ARROW_UP_PRESSED, SKIN_BUTTON_ARROW_UP_DISABLED, SKIN_BUTTON_ARROW_DOWN, SKIN_BUTTON_ARROW_DOWN_PRESSED, SKIN_BUTTON_ARROW_DOWN_DISABLED, SKIN_SCROLLBAR_V_BACKGROUND_TOP, SKIN_SCROLLBAR_V_BACKGROUND, SKIN_SCROLLBAR_V_BACKGROUND_BOTTOM, SKIN_SCROLLBAR_V_KNOB_TOP, SKIN_SCROLLBAR_V_KNOB_BODY, SKIN_SCROLLBAR_V_KNOB_BOTTOM, // squarebutton SKIN_BUTTON_CHECKBOX = 0, SKIN_BUTTON_CHECKBOX_PRESSED, SKIN_BUTTON_CHECKBOX_DISABLED, // posbutton SKIN_BUTTON_POS = 0, SKIN_BUTTON_POS_PRESSED, SKIN_BUTTON_POS_DISABLED, // normal buttons and round buttons SKIN_BUTTON_SIDE_LEFT = 0, SKIN_BUTTON_BODY, SKIN_BUTTON_SIDE_RIGHT, SKIN_BUTTON_SIDE_LEFT_PRESSED, SKIN_BUTTON_BODY_PRESSED, SKIN_BUTTON_SIDE_RIGHT_PRESSED, SKIN_BUTTON_SIDE_RIGHT_DISABLED, SKIN_BUTTON_BODY_DISABLED, SKIN_BUTTON_SIDE_LEFT_DISABLED, SKIN_BUTTON_COLOR_MASK_LEFT, SKIN_BUTTON_COLOR_MASK_BODY, SKIN_BUTTON_COLOR_MASK_RIGHT }; class gui_theme_t { public: /// @name system colours used by gui components /// @{ static PIXVAL gui_color_text; //@< Color to draw standard text static PIXVAL gui_color_text_highlight; //@< Color to draw highlighted text (tabs, finance window headlines, ware list bonus text, fps info in video options, it and em tags) static PIXVAL gui_color_text_title; //@< Color to draw title text (banner, h1 and a tags) static PIXVAL gui_color_text_shadow; //@< Color to draw text shadow static PIXVAL gui_color_text_strong; //@< Color to draw strong text (strong tags) static PIXVAL gui_color_text_minus; //@< Color to draw negative values static PIXVAL gui_color_text_plus; //@< Color to draw positive values static PIXVAL gui_color_text_unused; //@< Color to draw unused items static PIXVAL gui_color_edit_text; //@< Color to draw text in edit areas static PIXVAL gui_color_edit_text_selected; //@< Color to draw selected text in edit areas static PIXVAL gui_color_edit_text_disabled; //@< Color to draw disabled text in edit areas static PIXVAL gui_color_edit_background_selected; //@< Color to draw background of selected text in edit areas static PIXVAL gui_color_edit_beam; //@< Color to draw the cursor beam static PIXVAL gui_color_chart_background; //@< Color to draw background of charts static PIXVAL gui_color_chart_lines_zero; //@< Color to draw in-chart horizontal zero line static PIXVAL gui_color_chart_lines_odd; //@< Color to draw in-chart vertical odd lines and text static PIXVAL gui_color_chart_lines_even; //@< Color to draw in-chart vertical even lines and text static PIXVAL gui_color_list_text_selected_focus; //@< Colour to draw the selected element text in list when window has focus static PIXVAL gui_color_list_text_selected_nofocus; //@< Colour to draw the selected element text in list when window is not in focus static PIXVAL gui_color_list_background_selected_f; //@< Colour to draw the selected element background in list when window has focus static PIXVAL gui_color_list_background_selected_nf; //@< Colour to draw the selected element background in list when window is not in focus static PIXVAL gui_color_button_text; //@< Color to draw text in normal buttons static PIXVAL gui_color_button_text_disabled; //@< Color to draw text in disabled buttons static PIXVAL gui_color_button_text_selected; //@< Color to draw text in pressed normal buttons static PIXVAL gui_color_colored_button_text; //@< Color to draw text in colored buttons static PIXVAL gui_color_colored_button_text_selected; //@< Color to draw text in pressed colored buttons static PIXVAL gui_color_checkbox_text; //@< Color to draw text in checkboxes static PIXVAL gui_color_checkbox_text_disabled; //@< Color to draw text in disabled checkboxes static PIXVAL gui_color_ticker_background; //@< Color to draw ticker background static PIXVAL gui_color_ticker_divider; //@< Color to draw ticker divider static PIXVAL gui_color_statusbar_text; //@< Color to draw text in statusbar static PIXVAL gui_color_statusbar_background; //@< Color to draw statusbar background static PIXVAL gui_color_statusbar_divider; //@< Color to draw statusbar divider static PIXVAL gui_highlight_color; //@< Color to draw highlight dividers (tabs) static PIXVAL gui_shadow_color; //@< Color to draw shadowed dividers (tabs) static PIXVAL gui_color_loadingbar_inner; static PIXVAL gui_color_loadingbar_progress; static PIXVAL gui_color_obsolete; //@< Color for obsolete convois/server entries static PIXVAL gui_color_empty; //@< Color for empty entries static PIXVAL gui_color_chat_window_network_transparency; //@< Color if chat window is transparent in network mode static PIXVAL gui_color_image_transparency; //@< Transparency color for some icons/images static PIXVAL gui_color_object_highlight; //@< Color (Red) to mark objects on the map /// @} /// @name GUI element sizes used by gui components /// @{ static sint16 gui_buttons_per_row; // how many buttons in a default dialog static scr_size gui_divider_size; static scr_size gui_button_size; static scr_size gui_color_button_text_offset; // extra offset for the text (in case of asymmetric or buttons with color on the left) static scr_size gui_button_text_offset; // extra offset for the text (in case of asymmetric or buttons with checkmark on the left) static scr_size gui_color_button_size; static scr_size gui_checkbox_size; static scr_size gui_pos_button_size; static scr_size gui_arrow_left_size; static scr_size gui_arrow_right_size; static scr_size gui_arrow_up_size; static scr_size gui_arrow_down_size; static scr_size gui_scrollbar_size; static scr_size gui_min_scrollbar_size; // minimum width and height of a scrollbar slider static scr_size gui_label_size; static scr_size gui_edit_size; static scr_size gui_indicator_size; static scr_size gui_gadget_size; static scr_size gui_dragger_size; static scr_coord gui_focus_offset; static scr_coord gui_color_button_text_offset_right; // extra right offset for the text (in case of asymmetric or buttons with color on the right) static scr_coord gui_button_text_offset_right; // extra right offset for the text (in case of asymmetric or buttons with checkmark on the right) static scr_coord_val gui_titlebar_height; static scr_coord_val gui_frame_left; static scr_coord_val gui_frame_top; static scr_coord_val gui_frame_right; static scr_coord_val gui_frame_bottom; static scr_coord_val gui_hspace; static scr_coord_val gui_vspace; static scr_coord_val gui_waitingbar_width; // one special entries, since there are lot of lists with files/fonts/paks/... where less spacing could fit more entires on the screen static scr_coord_val gui_filelist_vspace; /// @} // those are the 3x3 images which are used for stretching static stretch_map_t button_tiles[3]; static stretch_map_t button_color_tiles[2]; static stretch_map_t round_button_tiles[3]; static stretch_map_t h_scroll_back_tiles; static stretch_map_t h_scroll_knob_tiles; static stretch_map_t v_scroll_back_tiles; static stretch_map_t v_scroll_knob_tiles; static stretch_map_t divider; static stretch_map_t editfield; static stretch_map_t listbox; static stretch_map_t windowback; // those are the normal, selected and disabled simple buttons static image_id arrow_button_left_img[3]; static image_id arrow_button_right_img[3]; static image_id arrow_button_up_img[3]; static image_id arrow_button_down_img[3]; static image_id check_button_img[3]; static image_id pos_button_img[3]; static bool gui_drop_shadows; public: // default dimensions and colors static void init_gui_defaults(); // assings k with the dimension of this image static void init_size_from_image( const image_t *pic, scr_size &k ); // init the skin dimensions form file static void init_gui_from_images(); /** * Reads theme configuration data, still not final * searches a theme.tab inside the specified folder */ static bool themes_init(const char *dir_name,bool init_font,bool init_tools); }; #endif simutrans-124.5/src/simutrans/gui/halt_info.cc000066400000000000000000001010211520675460500214310ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "halt_info.h" #include "components/gui_button_to_chart.h" #include "../world/simworld.h" #include "../simware.h" #include "../simcolor.h" #include "../simconvoi.h" #include "../simfab.h" #include "../simintr.h" #include "../display/simgraph.h" #include "../display/viewport.h" #include "../tool/simmenu.h" #include "../simskin.h" #include "../simline.h" #include "../freight_list_sorter.h" #include "../dataobj/schedule.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../dataobj/loadsave.h" #include "../player/finance.h" #include "../player/simplay.h" #include "../vehicle/vehicle.h" #include "../utils/simstring.h" #include "../utils/unicode.h" #include "../descriptor/skin_desc.h" #define CHART_HEIGHT (100) /** * Window with destination information for a stop */ class gui_departure_board_t : public gui_aligned_container_t { // helper class to compute departure board class dest_info_t { public: bool compare( const dest_info_t &other ) const; halthandle_t halt; sint32 delta_ticks; convoihandle_t cnv; dest_info_t() : delta_ticks(0) {} dest_info_t( halthandle_t h, sint32 d_t, convoihandle_t c) : halt(h), delta_ticks(d_t), cnv(c) {} bool operator == (const dest_info_t &other) const { return ( this->cnv==other.cnv ); } }; static bool compare_hi(const dest_info_t &a, const dest_info_t &b) { return a.delta_ticks <= b.delta_ticks; } static karte_ptr_t welt; vector_tpl destinations; vector_tpl origins; static button_t absolute_times; uint32 calc_ticks_until_arrival( convoihandle_t cnv ); void insert_image(convoihandle_t cnv); public: // if nothing changed, this is the next refresh to recalculate the content of the departure board sint8 next_refresh; gui_departure_board_t() : gui_aligned_container_t() { next_refresh = -1; set_table_layout(3,0); add_component( &absolute_times ); absolute_times.init( button_t::square_automatic, "Absolute times" ); } void update_departures(halthandle_t halt); scr_size get_max_size() const OVERRIDE { return get_min_size(); } }; button_t gui_departure_board_t::absolute_times; // all connections class gui_halt_detail_t : public gui_aligned_container_t, action_listener_t { halthandle_t halt; uint16 halt_permissions; button_t connected_players[MAX_PLAYER_COUNT]; /** * Button to open line window */ class gui_line_button_t : public button_t, public action_listener_t { static karte_ptr_t welt; linehandle_t line; public: gui_line_button_t(linehandle_t line) : button_t() { this->line = line; init(button_t::posbutton, NULL); this->set_targetpos(koord::invalid); add_listener(this); } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE { player_t *player = welt->get_active_player(); if( player == line->get_owner() ) { player->simlinemgmt.show_lineinfo(player, line, 3); } return true; } void draw(scr_coord offset) OVERRIDE { if (line->get_owner() == welt->get_active_player()) { button_t::draw(offset); } } }; /** * Button to open convoi window */ class gui_convoi_button_t : public button_t, public action_listener_t { convoihandle_t convoi; public: gui_convoi_button_t(convoihandle_t convoi) : button_t() { this->convoi = convoi; init(button_t::posbutton, NULL); add_listener(this); } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE { convoi->open_info_window(); return true; } }; private: static bool compare_connection(haltestelle_t::connection_t const& a, haltestelle_t::connection_t const& b) { return strcmp(a.halt->get_name(), b.halt->get_name()) <=0; } static bool compare_line (linehandle_t a, linehandle_t b) { return a->get_linetype() == b->get_linetype() ? strcmp(a->get_name(), b->get_name()) <= 0 : strcmp(a->get_linetype_name(a->get_linetype()), b->get_linetype_name(b->get_linetype())) <= 0; } uint32 cached_line_count; uint32 cached_convoy_count; void insert_empty_row() { new_component(" "); new_component(); } void insert_show_nothing() { new_component(); new_component("keine"); } public: uint8 destination_counter; // last destination counter of the halt; if mismatch to current, then redraw destinations gui_halt_detail_t(halthandle_t h) : gui_aligned_container_t() { halt_permissions = 0; for (uint16 i = 0; i < MAX_PLAYER_COUNT; i++) { connected_players[i].pressed = 0; connected_players[i].add_listener(this); } destination_counter = 0xFF; cached_line_count = 0xFFFFFFFFul; cached_convoy_count = 0xFFFFFFFFul; update_connections(h); } bool action_triggered(gui_action_creator_t* , value_t ) { // change permissions here halt_permissions = 0; for (uint16 i = 0; i < MAX_PLAYER_COUNT; i++) { if (connected_players[i].pressed) { halt_permissions |= 1 << i; } } if (halt.is_bound()) { // can be only triggered when current owner is visbile ... cbuffer_t param; param.printf("%u,%u", halt.get_id(), halt_permissions); tool_t::simple_tool[TOOL_HALT_PERMISSION]->set_default_param(param); world()->set_tool(tool_t::simple_tool[TOOL_HALT_PERMISSION], world()->get_active_player()); } return false; } // will update if schedule or connection changed void update_connections(halthandle_t h); scr_size get_max_size() const OVERRIDE { return get_min_size(); } }; karte_ptr_t gui_departure_board_t::welt; karte_ptr_t gui_halt_detail_t::gui_line_button_t::welt; const char cost_type[MAX_HALT_COST][64] = { "Happy", "Unhappy", "No Route", "hl_btn_sort_waiting", "Arrived", "Departed", "Convoys", "Walked" }; const uint8 index_of_haltinfo[MAX_HALT_COST] = { HALT_HAPPY, HALT_UNHAPPY, HALT_NOROUTE, HALT_WAITING, HALT_ARRIVED, HALT_DEPARTED, HALT_CONVOIS_ARRIVED, HALT_WALKED }; const uint8 cost_type_color[MAX_HALT_COST] = { COL_HAPPY, COL_UNHAPPY, COL_NO_ROUTE, COL_WAITING, COL_ARRIVED, COL_DEPARTED, COL_CONVOI_COUNT, COL_LILAC }; struct type_symbol_t { haltestelle_t::stationtyp type; const skin_desc_t **desc; }; const type_symbol_t symbols[] = { { haltestelle_t::railstation, &skinverwaltung_t::zughaltsymbol }, { haltestelle_t::loadingbay, &skinverwaltung_t::autohaltsymbol }, { haltestelle_t::busstop, &skinverwaltung_t::bushaltsymbol }, { haltestelle_t::dock, &skinverwaltung_t::schiffshaltsymbol }, { haltestelle_t::airstop, &skinverwaltung_t::airhaltsymbol }, { haltestelle_t::monorailstop, &skinverwaltung_t::monorailhaltsymbol }, { haltestelle_t::tramstop, &skinverwaltung_t::tramhaltsymbol }, { haltestelle_t::maglevstop, &skinverwaltung_t::maglevhaltsymbol }, { haltestelle_t::narrowgaugestop, &skinverwaltung_t::narrowgaugehaltsymbol } }; // helper class gui_halt_type_images_t::gui_halt_type_images_t(halthandle_t h) { halt = h; set_table_layout(lengthof(symbols), 1); set_alignment(ALIGN_LEFT | ALIGN_CENTER_V); assert( lengthof(img_transport) == lengthof(symbols) ); // indicator for supplied transport modes haltestelle_t::stationtyp const halttype = halt->get_station_type(); for(uint i=0; i < lengthof(symbols); i++) { if ( *symbols[i].desc ) { add_component(img_transport + i); img_transport[i].set_image( (*symbols[i].desc)->get_image_id(0)); img_transport[i].enable_offset_removal(true); img_transport[i].set_visible( (halttype & symbols[i].type) != 0); } } } void gui_halt_type_images_t::draw(scr_coord offset) { haltestelle_t::stationtyp const halttype = halt->get_station_type(); for(uint i=0; i < lengthof(symbols); i++) { img_transport[i].set_visible( (halttype & symbols[i].type) != 0); } gui_aligned_container_t::draw(offset); } // main class halt_info_t::halt_info_t(halthandle_t halt) : gui_frame_t("", NULL), departure_board( new gui_departure_board_t()), halt_detail( new gui_halt_detail_t(halt)), text_freight(&freight_info), scrolly_freight(&container_freight, true, true), scrolly_departure(departure_board, true, true), scrolly_details(halt_detail, true, true), view(koord3d::invalid, scr_size(max(64, gfx->get_base_tile_raster_width()), max(56, gfx->get_base_tile_raster_width() * 7 / 8))) { if (halt.is_bound()) { init(halt); } } void halt_info_t::init(halthandle_t halt) { this->halt = halt; set_name(halt->get_name()); set_owner(halt->get_owner() ); set_table_layout(1,0); // top part add_table(2,1)->set_alignment(ALIGN_CENTER_H); { container_top = add_table(1,0); { // input name tstrncpy(edit_name, halt->get_name(), lengthof(edit_name)); input.set_text(edit_name, lengthof(edit_name)); input.set_notify_all_changes_delay(500); // since each letter triggers a tool input.add_listener(this); add_component(&input, 2); // status images add_table(5,1)->set_alignment(ALIGN_CENTER_V); { add_component(&indicator_color); // indicator for enabled freight type img_enable[0].set_image(skinverwaltung_t::passengers->get_image_id(0)); img_enable[1].set_image(skinverwaltung_t::mail->get_image_id(0)); img_enable[2].set_image(skinverwaltung_t::goods->get_image_id(0)); for(uint i=0; i<3; i++) { add_component(img_enable + i); img_enable[i].enable_offset_removal(true); } img_types = new_component(halt); } end_table(); // capacities add_table(6,1); { add_component(&lb_capacity[0]); if (welt->get_settings().is_separate_halt_capacities()) { new_component(skinverwaltung_t::passengers->get_image_id(0), 0, ALIGN_NONE, true); add_component(&lb_capacity[1]); new_component(skinverwaltung_t::mail->get_image_id(0), 0, ALIGN_NONE, true); add_component(&lb_capacity[2]); new_component(skinverwaltung_t::goods->get_image_id(0), 0, ALIGN_NONE, true); } } end_table(); // happy / unhappy / no route add_table(6,1); { add_component(&lb_happy[0]); if (skinverwaltung_t::happy && skinverwaltung_t::unhappy && skinverwaltung_t::no_route) { new_component(skinverwaltung_t::happy->get_image_id(0), 0, ALIGN_NONE, true); add_component(&lb_happy[1]); new_component(skinverwaltung_t::unhappy->get_image_id(0), 0, ALIGN_NONE, true); add_component(&lb_happy[2]); new_component(skinverwaltung_t::no_route->get_image_id(0), 0, ALIGN_NONE, true); } } end_table(); } end_table(); add_component(&view); view.set_location(halt->get_basis_pos3d()); } end_table(); // tabs: waiting, departure, chart add_component(&switch_mode); switch_mode.add_listener(this); switch_mode.add_tab(&scrolly_freight, translator::translate("Hier warten/lagern:")); // list of waiting cargo // sort mode container_freight.set_table_layout(1,0); container_freight.add_table(2, 1); { container_freight.new_component("Sort waiting list by"); sort_mode_button.init(button_t::roundbox, freight_list_sorter_t::get_sort_mode_string(env_t::default_sortmode, halt)); sort_mode_button.set_tooltip("Sort waiting list by"); sort_mode_button.add_listener(this); container_freight.add_component(&sort_mode_button); } container_freight.end_table(); container_freight.add_component(&text_freight,2); // departure board switch_mode.add_tab(&scrolly_departure, translator::translate("Departure board")); departure_board->update_departures(halt); departure_board->set_size( departure_board->get_min_size() ); // connection info switch_mode.add_tab(&scrolly_details, translator::translate("Connections")); halt_detail->update_connections(halt); halt_detail->set_size( halt_detail->get_min_size() ); // chart switch_mode.add_tab(&container_chart, translator::translate("Chart")); container_chart.set_table_layout(D_BUTTONS_PER_ROW,0); container_chart.set_force_equal_columns(true); chart.set_min_size(scr_size(0, CHART_HEIGHT)); chart.set_dimension(12, 10000); chart.set_background(SYSCOL_CHART_BACKGROUND); container_chart.add_component(&chart, D_BUTTONS_PER_ROW); for (int cost = 0; costpalette_lookup(cost_type_color[cost]), halt->get_finance_history(), MAX_HALT_COST, index_of_haltinfo[cost], MAX_MONTHS, 0, false, true, 0); button_t *b = container_chart.new_component(); b->init(button_t::box_state_automatic | button_t::flexible, cost_type[cost]); b->background_color = gfx->palette_lookup(cost_type_color[cost]); b->pressed = false; button_to_chart.append(b, &chart, curve); } halt->reset_freight_info(); update_components(); set_resizemode(diagonal_resize); reset_min_windowsize(); set_windowsize(get_min_windowsize()); } halt_info_t::~halt_info_t() { if( halt.is_bound() && strcmp(halt->get_name(),edit_name) && edit_name[0] ) { // text changed => call tool cbuffer_t buf; buf.printf( "h%u,%s", halt.get_id(), edit_name ); tool_t *tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tool->set_default_param( buf ); welt->set_tool( tool, halt->get_owner() ); // since init always returns false, it is safe to delete immediately delete tool; } delete departure_board; delete halt_detail; } koord3d halt_info_t::get_weltpos(bool) { return halt->get_basis_pos3d(); } bool halt_info_t::is_weltpos() { return ( welt->get_viewport()->is_on_center(get_weltpos(false))); } void halt_info_t::update_components() { indicator_color.set_color(halt->get_status_farbe()); lb_capacity[0].buf().printf("%s: %u", translator::translate("Storage capacity"), halt->get_capacity(0)); lb_capacity[0].update(); lb_capacity[1].buf().printf(" %u", halt->get_capacity(1)); lb_capacity[1].update(); lb_capacity[2].buf().printf(" %u", halt->get_capacity(2)); lb_capacity[2].update(); if (skinverwaltung_t::happy && skinverwaltung_t::unhappy && skinverwaltung_t::no_route) { lb_happy[0].buf().printf("%s: %u", translator::translate("Passagiere"), halt->get_pax_happy()); lb_happy[0].update(); lb_happy[1].buf().printf(" %u", halt->get_pax_unhappy()); lb_happy[1].update(); lb_happy[2].buf().printf(" %u", halt->get_pax_no_route()); lb_happy[2].update(); } else { if( gfx->font_has_character( 0x263A ) ) { utf8 happy[4], unhappy[4]; happy[ utf16_to_utf8( 0x263A, happy ) ] = 0; unhappy[ utf16_to_utf8( 0x2639, unhappy ) ] = 0; lb_happy[0].buf().printf(translator::translate("Passengers %d %s, %d %s, %d no route"), halt->get_pax_happy(), happy, halt->get_pax_unhappy(), unhappy, halt->get_pax_no_route()); } else if( gfx->font_has_character( 30 ) ) { lb_happy[0].buf().printf(translator::translate("Passengers %d %c, %d %c, %d no route"), halt->get_pax_happy(), 30, halt->get_pax_unhappy(), 31, halt->get_pax_no_route()); } else { lb_happy[0].buf().printf(translator::translate("Passengers %d %c, %d %c, %d no route"), halt->get_pax_happy(), '+', halt->get_pax_unhappy(), '-', halt->get_pax_no_route()); } lb_happy[0].update(); } img_enable[0].set_visible(halt->get_pax_enabled()); img_enable[1].set_visible(halt->get_mail_enabled()); img_enable[2].set_visible(halt->get_ware_enabled()); container_top->set_size( container_top->get_size()); if (switch_mode.get_aktives_tab() == &scrolly_freight) { sort_mode_button.set_text(freight_list_sorter_t::get_sort_mode_string(env_t::default_sortmode, halt)); // buffer update now only when needed by halt itself => dedicated buffer for this int old_len = freight_info.len(); halt->get_freight_info(freight_info); if (old_len != freight_info.len()) { text_freight.recalc_size(); container_freight.set_size(container_freight.get_min_size()); } } else if (switch_mode.get_aktives_tab() == &scrolly_departure) { departure_board->update_departures(halt); } else if (switch_mode.get_aktives_tab() == &scrolly_details) { halt_detail->update_connections(halt); } set_dirty(); } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void halt_info_t::draw(scr_coord pos, scr_size size) { assert(halt.is_bound()); update_components(); gui_frame_t::draw(pos, size); } void gui_halt_detail_t::update_connections( halthandle_t halt ) { this->halt = halt; if( !halt.is_bound() ) { // first call, or invalid handle remove_all(); return; } // follow player change bool allow_change = halt->get_owner() == world()->get_active_player() || world()->get_active_player()->is_public_service(); for (uint16 i = 0; i < MAX_PLAYER_COUNT; i++) { connected_players[i].enable(allow_change); } if( halt->get_permissions()==halt_permissions && halt->get_reconnect_counter()==destination_counter && halt->registered_lines.get_count()==cached_line_count && halt->registered_convoys.get_count()==cached_convoy_count ) { // all current, so do nothing return; } // update connections from here remove_all(); set_table_layout(2, 0); gui_aligned_container_t *t = new gui_aligned_container_t(2, 0); t->new_component_span("Stopping permissions", 2); int how_many = 0; halt_permissions = halt->get_permissions(); for (uint16 i = 0; i < MAX_PLAYER_COUNT; i++) { if (player_t *pl = world()->get_player(i)) { if (pl->is_public_service() && !pl->get_finance()->has_convoi()) { // only show public service when it owns convois ... continue; } if (pl == halt->get_owner()) { // owner is always allowed continue; } connected_players[i].init(button_t::square_automatic,pl->get_name()); connected_players[i].text_color = PLAYER_FLAG | gfx->palette_lookup(pl->get_player_color1() + env_t::gui_player_color_dark); t->add_component(connected_players + i); how_many++; } connected_players[i].pressed = halt_permissions & (1< 0) { take_component(t, 2); } const slist_tpl & fab_list = halt->get_fab_list(); slist_tpl nimmt_an; new_component_span("Fabrikanschluss", 2); if (!fab_list.empty()) { for(fabrik_t* const fab : fab_list) { const koord3d pos = fab->get_pos(); // target button ... button_t *pb = new_component(); pb->init( button_t::posbutton_automatic, NULL); pb->set_targetpos3d( pos ); // .. name gui_label_buf_t *lb = new_component(); lb->buf().printf("%s (%d, %d)\n", translator::translate(fab->get_name()), pos.x, pos.y); lb->update(); for(ware_production_t const& i : fab->get_input()) { goods_desc_t const* const ware = i.get_typ(); if(!nimmt_an.is_contained(ware)) { nimmt_an.append(ware); } } } } else { insert_show_nothing(); } insert_empty_row(); new_component_span("Angenommene Waren", 2); if (!nimmt_an.empty() && halt->get_ware_enabled()) { for(uint32 i=0; i(); new_component(ware->get_name()); } } } else { insert_show_nothing(); } insert_empty_row(); // add lines that serve this stop new_component_span("Lines serving this stop", 2); if( !halt->registered_lines.empty() ) { simline_t::linetype previous_linetype = simline_t::MAX_LINE_TYPE; vector_tpl sorted_lines; for(linehandle_t l : halt->registered_lines) { if( l.is_bound() ) { sorted_lines.insert_unique_ordered( l, gui_halt_detail_t::compare_line ); } } for(linehandle_t line : sorted_lines) { // Linetype if it is the first if( line->get_linetype() != previous_linetype ) { previous_linetype = line->get_linetype(); new_component_span(simline_t::get_linetype_name(previous_linetype),2); } new_component(line); // Line labels with color of player gui_label_buf_t *lb = new_component(PLAYER_FLAG | gfx->palette_lookup(line->get_owner()->get_player_color1()+env_t::gui_player_color_dark) ); lb->buf().append( line->get_name() ); lb->update(); } } else { insert_show_nothing(); } insert_empty_row(); // add lineless convoys which serve this stop new_component_span("Lineless convoys serving this stop", 2); if( !halt->registered_convoys.empty() ) { for( uint32 i=0; iregistered_convoys.get_count(); ++i ) { convoihandle_t cnv = halt->registered_convoys[i]; // Convoy buttons new_component(cnv); // Line labels with color of player gui_label_buf_t *lb = new_component(PLAYER_FLAG | gfx->palette_lookup(cnv->get_owner()->get_player_color1()+env_t::gui_player_color_dark) ); lb->buf().append( cnv->get_name() ); lb->update(); } } else { insert_show_nothing(); } insert_empty_row(); new_component_span("Direkt erreichbare Haltestellen", 2); bool has_stops = false; for (uint i=0; i const& connections = halt->get_connections(i); if( !connections.empty() ) { gui_label_buf_t *lb = new_component_span(2); lb->buf().append(" \xC2\xB7"); const goods_desc_t* info = goods_manager_t::get_info_catg_index(i); // If it is a special freight, we display the name of the good, otherwise the name of the category. lb->buf().append(translator::translate(info->get_catg()==0 ? info->get_name() : info->get_catg_name() ) ); #if MSG_LEVEL>=4 if( halt->is_transfer(i) ) { lb->buf().append("*"); } #endif lb->buf().append(":\n"); lb->update(); vector_tpl sorted; for(haltestelle_t::connection_t const& conn : connections) { if( conn.halt.is_bound() ) { sorted.insert_unique_ordered(conn, gui_halt_detail_t::compare_connection); } } for(haltestelle_t::connection_t const& conn : sorted) { has_stops = true; button_t *pb = new_component(); pb->init( button_t::posbutton_automatic, NULL); pb->set_targetpos3d( conn.halt->get_basis_pos3d() ); gui_label_buf_t *lb = new_component(); lb->buf().printf("%s <%u>", conn.halt->get_name(), conn.weight); lb->update(); } } } if (!has_stops) { insert_show_nothing(); } // ok, we have now this counter for pending updates destination_counter = halt->get_reconnect_counter(); cached_line_count = halt->registered_lines.get_count(); cached_convoy_count = halt->registered_convoys.get_count(); set_size( get_min_size() ); } // a sophisticated guess of a convois arrival time, taking into account the braking too and the current convoi state uint32 gui_departure_board_t::calc_ticks_until_arrival( convoihandle_t cnv ) { /* calculate the time needed: * tiles << (8+12) / (kmh_to_speed(max_kmh) = ticks */ uint32 delta_t = 0; sint32 delta_tiles = cnv->get_route()->get_count() - cnv->front()->get_route_index(); uint32 kmh_average = (cnv->get_average_kmh()*900 ) / 1024u; // last braking tile if( delta_tiles > 1 && kmh_average > 25 ) { delta_tiles --; delta_t += 3276; // ( (1 << (8+12)) / kmh_to_speed(25) ); } // second last braking tile if( delta_tiles > 1 && kmh_average > 50 ) { delta_tiles --; delta_t += 1638; // ( (1 << (8+12)) / kmh_to_speed(50) ); } // third last braking tile if( delta_tiles > 1 && kmh_average > 100 ) { delta_tiles --; delta_t += 819; // ( (1 << (8+12)) / kmh_to_speed(100) ); } // waiting at signal? if( cnv->get_state() != convoi_t::DRIVING ) { // extra time for acceleration delta_t += kmh_average * 25; } delta_t += ( ((sint64)delta_tiles << (8+12) ) / kmh_to_speed( kmh_average ) ); return delta_t; } // refreshes the departure string void gui_departure_board_t::update_departures(halthandle_t halt) { if (!halt.is_bound()) { return; } if (--next_refresh >= 0) { return; } vector_tpl old_origins(origins); destinations.clear(); origins.clear(); const uint32 cur_ticks = welt->get_ticks() % welt->ticks_per_world_month; static uint32 last_ticks = 0; if( last_ticks > cur_ticks ) { // new month has started => invalidate average buffer old_origins.clear(); } last_ticks = cur_ticks; // iterate over all convoys stopping here for(convoihandle_t cnv : halt->get_loading_convois() ) { if( !cnv.is_bound() || cnv->get_state()!=convoi_t::LOADING ) { continue; } halthandle_t next_halt = cnv->get_schedule()->get_next_halt(cnv->get_owner(),halt); if( next_halt.is_bound() ) { uint32 delta_ticks = 0; if( cnv->get_schedule()->get_current_entry().get_absolute_departures() ) { // absolute schedule delta_ticks = cnv->get_departure_ticks(); } else if( cnv->get_schedule()->get_current_entry().waiting_time > 0 ) { // waiting for load with max time delta_ticks = cnv->get_arrival_ticks() + cnv->get_schedule()->get_current_entry().get_waiting_ticks(); } // avoid overflow when departure time has passed but convoi is still loading etc. uint32 ct = welt->get_ticks(); if (ct > delta_ticks) { delta_ticks = 0; } else { delta_ticks -= ct; } dest_info_t next( next_halt, delta_ticks, cnv ); destinations.append_unique( next ); if( grund_t *gr = welt->lookup( cnv->get_vehicle(0)->last_stop_pos ) ) { if( gr->get_halt().is_bound() && gr->get_halt() != halt ) { dest_info_t prev( gr->get_halt(), 0, cnv ); origins.append_unique( prev ); } } } } // now exactly the same for convoys en route; the only change is that we estimate their arrival time too for(linehandle_t line : halt->registered_lines ) { for( uint j = 0; j < line->count_convoys(); j++ ) { convoihandle_t cnv = line->get_convoy(j); if( cnv.is_bound() && ( cnv->get_state() == convoi_t::DRIVING || cnv->is_waiting() ) && haltestelle_t::get_halt( cnv->get_schedule()->get_current_entry().pos, cnv->get_owner() ) == halt ) { halthandle_t prev_halt = haltestelle_t::get_halt( cnv->front()->last_stop_pos, cnv->get_owner(), true ); sint32 delta_t = calc_ticks_until_arrival( cnv ); if( prev_halt.is_bound() ) { dest_info_t prev( prev_halt, delta_t, cnv ); // smooth times a little for(dest_info_t &elem : old_origins ) { if( elem.cnv == cnv ) { delta_t = ( delta_t + 3*elem.delta_ticks ) / 4; prev.delta_ticks = delta_t; break; } } origins.insert_ordered( prev, compare_hi ); } halthandle_t next_halt = cnv->get_schedule()->get_next_halt(cnv->get_owner(),halt); if( next_halt.is_bound() ) { if( cnv->get_schedule()->get_current_entry().get_absolute_departures() ) { delta_t = cnv->get_departure_ticks( welt->get_ticks()+delta_t )-welt->get_ticks(); } else if( cnv->get_schedule()->get_current_entry().waiting_time > 0 ) { // waiting for load with max time delta_t += cnv->get_schedule()->get_current_entry().get_waiting_ticks(); } else { delta_t += 2000; } dest_info_t next( next_halt, delta_t+2000, cnv ); destinations.insert_ordered( next, compare_hi ); } } } } for(convoihandle_t cnv : halt->registered_convoys ) { if( cnv.is_bound() && ( cnv->get_state() == convoi_t::DRIVING || cnv->is_waiting() ) && haltestelle_t::get_halt( cnv->get_schedule()->get_current_entry().pos, cnv->get_owner(), true ) == halt ) { halthandle_t prev_halt = haltestelle_t::get_halt( cnv->front()->last_stop_pos, cnv->get_owner(), true ); sint32 delta_t = cur_ticks + calc_ticks_until_arrival( cnv ); if( prev_halt.is_bound() ) { dest_info_t prev( prev_halt, delta_t, cnv ); // smooth times a little for(dest_info_t &elem : old_origins ) { if( elem.cnv == cnv ) { delta_t = ( delta_t + 3*elem.delta_ticks ) / 4; prev.delta_ticks = delta_t; break; } } origins.insert_ordered( prev, compare_hi ); } halthandle_t next_halt = cnv->get_schedule()->get_next_halt(cnv->get_owner(),halt); if( next_halt.is_bound() ) { dest_info_t next( next_halt, delta_t+2000, cnv ); destinations.insert_ordered( next, compare_hi ); } } } // fill the board remove_all(); add_component( &absolute_times,3 ); slist_tpl exclude; if( destinations.get_count()>0 ) { new_component_span("Departures to\n", 3); for(dest_info_t hi : destinations ) { if( freight_list_sorter_t::by_via > env_t::default_sortmode || !exclude.is_contained( hi.halt ) ) { gui_label_buf_t *lb = new_component(SYSCOL_TEXT, gui_label_t::right); if( hi.delta_ticks == 0 ) { lb->buf().append( translator::translate( "now" ) ); } else { if( absolute_times.pressed ) { lb->buf().printf( "%s", tick_to_string( welt->get_ticks()+hi.delta_ticks, true ) ); } else { lb->buf().printf( "%s", difftick_to_string( hi.delta_ticks, true ) ); } } lb->update(); insert_image(hi.cnv); new_component(hi.halt->get_name() ); exclude.append( hi.halt ); } } } exclude.clear(); if( origins.get_count()>0 ) { new_component_span("Arrivals from\n", 3); for(dest_info_t hi : origins ) { if( freight_list_sorter_t::by_via_sum != env_t::default_sortmode || !exclude.is_contained( hi.halt ) ) { gui_label_buf_t *lb = new_component(SYSCOL_TEXT, gui_label_t::right); if( hi.delta_ticks == 0 ) { lb->buf().append( translator::translate( "now" ) ); } else { if( absolute_times.pressed ) { lb->buf().printf( "%s", tick_to_string( welt->get_ticks()+hi.delta_ticks, true ) ); } else { lb->buf().printf( "%s", difftick_to_string( hi.delta_ticks, true ) ); } } lb->update(); insert_image(hi.cnv); new_component(hi.halt->get_name() ); exclude.append( hi.halt ); } } } set_size( get_min_size() ); next_refresh = 5; } void gui_departure_board_t::insert_image(convoihandle_t cnv) { gui_image_t *im = NULL; switch(cnv->front()->get_waytype()) { case road_wt: { if (cnv->front()->get_cargo_type() == goods_manager_t::passengers) { im = new_component(skinverwaltung_t::bushaltsymbol->get_image_id(0)); } else { im = new_component(skinverwaltung_t::autohaltsymbol->get_image_id(0)); } break; } case track_wt: { if (cnv->front()->get_desc()->get_waytype() == tram_wt) { im = new_component(skinverwaltung_t::tramhaltsymbol->get_image_id(0)); } else { im = new_component(skinverwaltung_t::zughaltsymbol->get_image_id(0)); } break; } case water_wt: im = new_component(skinverwaltung_t::schiffshaltsymbol->get_image_id(0)); break; case air_wt: im = new_component(skinverwaltung_t::airhaltsymbol->get_image_id(0)); break; case monorail_wt: im = new_component(skinverwaltung_t::monorailhaltsymbol->get_image_id(0)); break; case maglev_wt: im = new_component(skinverwaltung_t::maglevhaltsymbol->get_image_id(0)); break; case narrowgauge_wt: im = new_component(skinverwaltung_t::narrowgaugehaltsymbol->get_image_id(0)); break; default: new_component(); break; } if (im) { im->enable_offset_removal(true); } } /** * This method is called if an action is triggered */ bool halt_info_t::action_triggered( gui_action_creator_t *comp, value_t ) { if (comp == &sort_mode_button) { env_t::default_sortmode = ((int)(env_t::default_sortmode + 1) % (int)freight_list_sorter_t::SORT_MODES); halt->reset_freight_info(); } else if( comp == &input ) { if (strcmp(halt->get_name(), edit_name)) { // text changed => call tool cbuffer_t buf; buf.printf( "h%u,%s", halt.get_id(), edit_name ); tool_t *tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tool->set_default_param( buf ); welt->set_tool( tool, halt->get_owner() ); // since init always returns false, it is safe to delete immediately delete tool; } } else if (comp == &switch_mode) { departure_board->next_refresh = -1; } return true; } void halt_info_t::map_rotate90( sint16 new_ysize ) { view.map_rotate90(new_ysize); } void halt_info_t::rdwr(loadsave_t *file) { // window size scr_size size = get_windowsize(); size.rdwr( file ); // halt koord3d halt_pos; if( file->is_saving() ) { halt_pos = halt->get_basis_pos3d(); } halt_pos.rdwr( file ); if( file->is_loading() ) { halt = welt->lookup( halt_pos )->get_halt(); if (halt.is_bound()) { init(halt); win_set_magic(this, magic_halt_info+halt.get_id()); reset_min_windowsize(); set_windowsize(size); } } // sort mode is global ... no need to safe scrolly_freight.rdwr(file); scrolly_departure.rdwr(file); switch_mode.rdwr(file); // button-to-chart array button_to_chart.rdwr(file); if (!halt.is_bound()) { destroy_win( this ); } } simutrans-124.5/src/simutrans/gui/halt_info.h000066400000000000000000000054661520675460500213130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_HALT_INFO_H #define GUI_HALT_INFO_H #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_button_to_chart.h" #include "components/gui_chart.h" #include "components/gui_colorbox.h" #include "components/gui_image.h" #include "components/gui_label.h" #include "components/gui_location_view.h" #include "components/gui_scrollpane.h" #include "components/gui_tab_panel.h" #include "components/gui_textarea.h" #include "components/gui_textinput.h" #include "../utils/cbuffer.h" #include "../simhalt.h" #include "simwin.h" class gui_departure_board_t; class gui_halt_detail_t; /** * Helper class to show type symbols (train, bus, etc) */ class gui_halt_type_images_t : public gui_aligned_container_t { halthandle_t halt; gui_image_t img_transport[9]; public: gui_halt_type_images_t(halthandle_t h); void draw(scr_coord offset) OVERRIDE; }; /** * Main class: the station info window. * Window with destination information for a stop */ class halt_info_t : public gui_frame_t, private action_listener_t { private: gui_aligned_container_t *container_top; gui_label_buf_t lb_capacity[3], lb_happy[3]; gui_colorbox_t indicator_color; gui_image_t img_enable[3]; gui_halt_type_images_t *img_types; /** * Buffer for freight info text string. */ cbuffer_t freight_info; // departure stuff (departure and arrival times display) gui_departure_board_t *departure_board; // halt connections plane gui_halt_detail_t *halt_detail; // other UI definitions gui_aligned_container_t container_freight, container_chart; gui_textarea_t text_freight; gui_scrollpane_t scrolly_freight, scrolly_departure, scrolly_details; gui_textinput_t input; gui_chart_t chart; location_view_t view; button_t sort_mode_button; gui_button_to_chart_array_t button_to_chart; gui_tab_panel_t switch_mode; halthandle_t halt; char edit_name[256]; void update_components(); void init(halthandle_t halt); public: halt_info_t(halthandle_t halt = halthandle_t()); virtual ~halt_info_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "station.txt";} /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; koord3d get_weltpos(bool) OVERRIDE; bool is_weltpos() OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void map_rotate90( sint16 ) OVERRIDE; void rdwr( loadsave_t *file ) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_halt_info+halt.get_id(); } }; #endif simutrans-124.5/src/simutrans/gui/halt_list_filter_frame.cc000066400000000000000000000134741520675460500242060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "halt_list_filter_frame.h" #include "../simcolor.h" #include "../descriptor/goods_desc.h" #include "../builder/goods_manager.h" #include "../dataobj/translator.h" const char *halt_list_filter_frame_t::filter_buttons_text[FILTER_BUTTONS] = { "hlf_chk_spezial_filter", "hlf_chk_waren_annahme", "hlf_chk_waren_abgabe", "hlf_chk_overflow", "hlf_chk_keine_verb" }; halt_list_frame_t::filter_flag_t halt_list_filter_frame_t::filter_buttons_types[FILTER_BUTTONS] = { halt_list_frame_t::spezial_filter, halt_list_frame_t::ware_an_filter, halt_list_frame_t::ware_ab_filter, halt_list_frame_t::ueberfuellt_filter, halt_list_frame_t::ohneverb_filter }; halt_list_filter_frame_t::halt_list_filter_frame_t(player_t *player, halt_list_frame_t *main_frame) : gui_frame_t( translator::translate("hlf_title"), player), ware_scrolly_ab(&ware_cont_ab), ware_scrolly_an(&ware_cont_an) { this->main_frame = main_frame; // init buttons for( int i=0; i(); special_filter.add_component(filter_buttons + 3); special_filter.new_component(); special_filter.add_component(filter_buttons + 4); } // second columns: accepted cargo types accepts_filter.set_table_layout(1,0); { accepts_filter.add_component(filter_buttons + 1); accepts_filter.add_table(3,0); { ware_alle_an.init(button_t::roundbox, "hlf_btn_alle"); ware_alle_an.add_listener(this); accepts_filter.add_component(&ware_alle_an); ware_keine_an.init(button_t::roundbox, "hlf_btn_keine"); ware_keine_an.add_listener(this); accepts_filter.add_component(&ware_keine_an); ware_invers_an.init(button_t::roundbox, "hlf_btn_invers"); ware_invers_an.add_listener(this); accepts_filter.add_component(&ware_invers_an); } accepts_filter.end_table(); ware_scrolly_an.set_scroll_amount_y(D_BUTTON_HEIGHT); accepts_filter.add_component(&ware_scrolly_an); ware_cont_an.set_table_layout(2,0); for( int i=0; i(this, (const goods_desc_t*)NULL, ware); item->init(button_t::square, translator::translate(ware->get_name())); } } } // second columns: outgoing cargo types sends_filter.set_table_layout(1,0); { sends_filter.add_component(filter_buttons + 2); sends_filter.add_table(3,0); { ware_alle_ab.init(button_t::roundbox, "hlf_btn_alle"); ware_alle_ab.add_listener(this); sends_filter.add_component(&ware_alle_ab); ware_keine_ab.init(button_t::roundbox, "hlf_btn_keine"); ware_keine_ab.add_listener(this); sends_filter.add_component(&ware_keine_ab); ware_invers_ab.init(button_t::roundbox, "hlf_btn_invers"); ware_invers_ab.add_listener(this); sends_filter.add_component(&ware_invers_ab); } sends_filter.end_table(); ware_scrolly_ab.set_scroll_amount_y(D_BUTTON_HEIGHT); sends_filter.add_component(&ware_scrolly_ab); ware_cont_ab.set_table_layout(2,0); for( int i=0; i(this, ware, (const goods_desc_t*)NULL); item->init(button_t::square, translator::translate(ware->get_name())); } } } set_resizemode(diagonal_resize); reset_min_windowsize(); } halt_list_filter_frame_t::~halt_list_filter_frame_t() { main_frame->filter_frame_closed(); } /** * This method is called if an action is triggered */ bool halt_list_filter_frame_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { for (int i = 0; i < FILTER_BUTTONS; i++) { if (comp == filter_buttons + i) { main_frame->set_filter(filter_buttons_types[i], !main_frame->get_filter(filter_buttons_types[i])); main_frame->sort_list(); return true; } } if (comp == &ware_alle_ab) { main_frame->set_alle_ware_filter_ab(1); main_frame->sort_list(); return true; } if (comp == &ware_keine_ab) { main_frame->set_alle_ware_filter_ab(0); main_frame->sort_list(); return true; } if (comp == &ware_invers_ab) { main_frame->set_alle_ware_filter_ab(-1); main_frame->sort_list(); return true; } if (comp == &ware_alle_an) { main_frame->set_alle_ware_filter_an(1); main_frame->sort_list(); return true; } if (comp == &ware_keine_an) { main_frame->set_alle_ware_filter_an(0); main_frame->sort_list(); return true; } if (comp == &ware_invers_an) { main_frame->set_alle_ware_filter_an(-1); main_frame->sort_list(); return true; } if (comp == &name_filter_input) { main_frame->sort_list(); return true; } return false; } void halt_list_filter_frame_t::ware_item_triggered(const goods_desc_t *ware_ab, const goods_desc_t *ware_an) { if (ware_ab) { main_frame->set_ware_filter_ab(ware_ab, -1); } if (ware_an) { main_frame->set_ware_filter_an(ware_an, -1); } main_frame->sort_list(); } void halt_list_filter_frame_t::draw(scr_coord pos, scr_size size) { for(int i = 0; i < FILTER_BUTTONS; i++) { filter_buttons[i].pressed = main_frame->get_filter(filter_buttons_types[i]); } gui_frame_t::draw(pos, size); } simutrans-124.5/src/simutrans/gui/halt_list_filter_frame.h000066400000000000000000000070611520675460500240430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_HALT_LIST_FILTER_FRAME_H #define GUI_HALT_LIST_FILTER_FRAME_H #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_aligned_container.h" #include "components/gui_button.h" #include "components/gui_label.h" #include "components/gui_scrollpane.h" #include "components/gui_tab_panel.h" #include "components/gui_textinput.h" #include "halt_list_frame.h" class player_t; /** * Stations/stops list filter dialog * Displays filter settings for the halt list */ class halt_list_filter_frame_t : public gui_frame_t , private action_listener_t { private: /** * Helper class for the entries of the scrollable list of goods. * Needed since a button_t does not know its parent. */ class ware_item_t : public button_t { const goods_desc_t *ware_ab; const goods_desc_t *ware_an; halt_list_filter_frame_t *parent; public: ware_item_t(halt_list_filter_frame_t *parent, const goods_desc_t *ware_ab, const goods_desc_t *ware_an) { this->ware_ab = ware_ab; this->ware_an = ware_an; this->parent = parent; } bool infowin_event(event_t const* const ev) OVERRIDE { bool swallow = button_t::infowin_event( ev ); if( swallow && IS_LEFTRELEASE(ev) ) { // only handle, if we are hit! parent->ware_item_triggered( ware_ab, ware_an ); } return swallow; } void draw(scr_coord offset) OVERRIDE { if(ware_ab) { pressed = parent->get_ware_filter_ab(ware_ab); } if(ware_an) { pressed = parent->get_ware_filter_an(ware_an); } button_t::draw(offset); } }; /** * As long we do not have resource scripts, we display make * some tables for the main attributes of each button. */ enum { FILTER_BUTTONS = 5 }; static halt_list_frame_t::filter_flag_t filter_buttons_types[FILTER_BUTTONS]; static const char *filter_buttons_text[FILTER_BUTTONS]; /** * We are bound to this window. All filter states are stored in main_frame. */ halt_list_frame_t *main_frame; /** * All gui elements of this dialog: */ button_t filter_buttons[FILTER_BUTTONS]; gui_aligned_container_t special_filter, accepts_filter, sends_filter; gui_tab_panel_t tabs; gui_textinput_t name_filter_input; button_t typ_filter_enable; button_t ware_alle_ab; button_t ware_keine_ab; button_t ware_invers_ab; gui_aligned_container_t ware_cont_ab; gui_scrollpane_t ware_scrolly_ab; button_t ware_alle_an; button_t ware_keine_an; button_t ware_invers_an; gui_aligned_container_t ware_cont_an; gui_scrollpane_t ware_scrolly_an; public: halt_list_filter_frame_t(player_t *player, halt_list_frame_t *main_frame); ~halt_list_filter_frame_t(); /** * Propagate function from main_frame for ware_item_t */ bool get_ware_filter_ab(const goods_desc_t *ware) const { return main_frame->get_ware_filter_ab(ware); } bool get_ware_filter_an(const goods_desc_t *ware) const { return main_frame->get_ware_filter_an(ware); } /** * Handler for ware_item_t event. */ void ware_item_triggered(const goods_desc_t *ware_ab, const goods_desc_t *ware_an); /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE {return "haltlist_filter.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/halt_list_frame.cc000066400000000000000000000304501520675460500226320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "halt_list_frame.h" #include "halt_list_filter_frame.h" #include "../player/simplay.h" #include "../simhalt.h" #include "../simware.h" #include "../simfab.h" #include "../utils/unicode.h" #include "simwin.h" #include "../descriptor/skin_desc.h" #include "../builder/goods_manager.h" #include "../dataobj/translator.h" #include "../utils/cbuffer.h" /** * Scrolled list of halt_list_stats_ts. * Filters (by setting visibility) and sorts. */ class gui_scrolled_halt_list_t : public gui_scrolled_list_t { public: gui_scrolled_halt_list_t() : gui_scrolled_list_t(gui_scrolled_list_t::windowskin, compare) {} void sort() { gui_scrolled_list_t::sort(0); } static bool compare(const gui_component_t *aa, const gui_component_t *bb) { const halt_list_stats_t *a = dynamic_cast(aa); const halt_list_stats_t *b = dynamic_cast(bb); return halt_list_frame_t::compare_halts(a->get_halt(), b->get_halt()); } }; /** * All filter and sort settings are static, so the old settings are * used when the window is reopened. */ /** * This variable defines by which column the table is sorted */ halt_list_frame_t::sort_mode_t halt_list_frame_t::sortby = nach_name; /** * This variable defines the sort order (ascending or descending) * Values: 1 = ascending, 2 = descending) */ bool halt_list_frame_t::sortreverse = false; /** * Default filter: no Oil rigs! */ uint8 halt_list_frame_t::filter_flags = 0; char halt_list_frame_t::name_filter[256] = ""; slist_tpl halt_list_frame_t::waren_filter_ab; slist_tpl halt_list_frame_t::waren_filter_an; const char *halt_list_frame_t::sort_text[SORT_MODES] = { "hl_btn_sort_name", "hl_btn_sort_waiting", "hl_btn_sort_type" }; /** * This function compares two stations * * @return halt1 < halt2 */ bool halt_list_frame_t::compare_halts(halthandle_t const halt1, halthandle_t const halt2) { int order; /*********************************** * Compare station 1 and station 2 ***********************************/ switch (sortby) { default: case nach_name: // sort by station name order = 0; break; case nach_wartend: // sort by waiting goods order = (int)(halt1->get_finance_history( 0, HALT_WAITING ) - halt2->get_finance_history( 0, HALT_WAITING )); break; case nach_typ: // sort by station type order = halt1->get_station_type() - halt2->get_station_type(); break; } /** * use name as an additional sort, to make sort more stable. */ if(order == 0) { order = strcmp(halt1->get_name(), halt2->get_name()); } /*********************************** * Consider sorting order ***********************************/ return sortreverse ? order > 0 : order < 0; } halt_list_frame_t::halt_list_frame_t() : gui_frame_t( translator::translate("hl_title"), welt->get_active_player()) { m_player = welt->get_active_player(); filter_frame = NULL; set_table_layout(1, 0); add_table(4, 2); { new_component("Filter:", 2); name_filter_input.set_text(name_filter, lengthof(name_filter)); add_component(&name_filter_input); filter_details.init(button_t::roundbox, "cl_btn_filter_settings"); filter_details.add_listener(this); add_component(&filter_details); new_component(); new_component_span("hl_txt_sort", 1); sortedby.set_unsorted(); // do not sort for (size_t i = 0; i < lengthof(sort_text); i++) { sortedby.new_component(translator::translate(sort_text[i]), SYSCOL_TEXT); } sortedby.set_selection(get_sortierung()); sortedby.add_listener(this); add_component(&sortedby); sorteddir.init(button_t::sortarrow_state, NULL); sorteddir.add_listener(this); sorteddir.pressed = get_reverse(); add_component(&sorteddir); new_component(); } end_table(); scrolly = new gui_scrolled_halt_list_t(); scrolly->set_maximize(true); scrolly->set_checkered(true); tabs.init_tabs(scrolly); tabs.add_listener(this); add_component(&tabs); sort_list(); set_resizemode(diagonal_resize); set_resizemode(diagonal_resize); reset_min_windowsize(); } halt_list_frame_t::~halt_list_frame_t() { if(filter_frame) { destroy_win(filter_frame); } } static bool passes_filter_special(haltestelle_t const& s) { if (!halt_list_frame_t::get_filter(halt_list_frame_t::spezial_filter)) return true; if (halt_list_frame_t::get_filter(halt_list_frame_t::ueberfuellt_filter)) { PIXVAL const color = s.get_status_farbe(); if (color == gfx->palette_lookup(COL_RED) || color == gfx->palette_lookup(COL_ORANGE)) { return true; // overcrowded } } if (halt_list_frame_t::get_filter(halt_list_frame_t::ohneverb_filter)) { for (uint8 i = 0; i < goods_manager_t::get_max_catg_index(); ++i) { if (!s.get_connections(i).empty()) return false; // only display stations with NO connection } return true; } return false; } static bool passes_filter_out(haltestelle_t const& s) { if (!halt_list_frame_t::get_filter(halt_list_frame_t::ware_ab_filter)) return true; /* * Die Unterkriterien werden gebildet aus: * - die Ware wird produziert (pax/post_enabled bzw. fabrik vorhanden) * - es existiert eine Zugverbindung mit dieser Ware (!ziele[...].empty()) */ for (uint32 i = 0; i != goods_manager_t::get_count(); ++i) { goods_desc_t const* const ware = goods_manager_t::get_info(i); if (!halt_list_frame_t::get_ware_filter_ab(ware)) continue; if (ware == goods_manager_t::passengers) { if (s.get_pax_enabled()) return true; } else if (ware == goods_manager_t::mail) { if (s.get_mail_enabled()) return true; } else if (ware != goods_manager_t::none) { // Sigh - a doubly nested loop per halt // Fortunately the number of factories and their number of outputs // is limited (usually 1-2 factories and 0-1 outputs per factory) for(fabrik_t* const f : s.get_fab_list()) { for(ware_production_t const& j : f->get_output()) { if (j.get_typ() == ware) return true; } } } } return false; } static bool passes_filter_in(haltestelle_t const& s) { if (!halt_list_frame_t::get_filter(halt_list_frame_t::ware_an_filter)) return true; /* * Die Unterkriterien werden gebildet aus: * - die Ware wird verbraucht (pax/post_enabled bzw. fabrik vorhanden) * - es existiert eine Zugverbindung mit dieser Ware (!ziele[...].empty()) */ for (uint32 i = 0; i != goods_manager_t::get_count(); ++i) { goods_desc_t const* const ware = goods_manager_t::get_info(i); if (!halt_list_frame_t::get_ware_filter_an(ware)) continue; if (ware == goods_manager_t::passengers) { if (s.get_pax_enabled()) return true; } else if (ware == goods_manager_t::mail) { if (s.get_mail_enabled()) return true; } else if (ware != goods_manager_t::none) { // Sigh - a doubly nested loop per halt // Fortunately the number of factories and their number of outputs // is limited (usually 1-2 factories and 0-1 outputs per factory) for(fabrik_t* const f : s.get_fab_list()) { for(ware_production_t const& j : f->get_input()) { if (j.get_typ() == ware) return true; } } } } return false; } /** * Check all filters for one halt. * returns true, if it is not filtered away. */ static bool passes_filter(haltestelle_t const& s) { if (!passes_filter_special(s)) return false; if (!passes_filter_out(s)) return false; if (!passes_filter_in(s)) return false; return true; } /** * This function refreshes the station-list */ void halt_list_frame_t::sort_list() { last_world_stops = haltestelle_t::get_alle_haltestellen().get_count(); // count of stations haltestelle_t::stationtyp current_type = tabs.get_active_tab_stationtype(); scrolly->clear_elements(); for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if (halt->get_owner() != m_player) { continue; } if (name_filter[0] != 0 && !utf8caseutf8(halt->get_name(), name_filter)) { // not the right name continue; } if (current_type != 0 && (halt->get_station_type() & current_type) == 0) { continue; } if( passes_filter(*halt.get_rep()) ) { scrolly->new_component(halt); } } scrolly->sort(); scrolly->set_size(scr_size(get_windowsize().w, scrolly->get_size().h)); } bool halt_list_frame_t::infowin_event(const event_t *ev) { if(ev->ev_class == INFOWIN && ev->ev_code == WIN_CLOSE) { if(filter_frame) { filter_frame->infowin_event(ev); } } return gui_frame_t::infowin_event(ev); } /** * This method is called if an action is triggered */ bool halt_list_frame_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { if ( comp == &sortedby ) { set_sortierung((sort_mode_t)((get_sortierung() + 1) % SORT_MODES)); sort_list(); } else if (comp == &tabs) { sort_list(); } else if (comp == &sorteddir) { set_reverse(!get_reverse()); sorteddir.pressed = get_reverse(); sort_list(); } else if ( comp == &filter_details ) { if (filter_frame) { destroy_win(filter_frame); } else { filter_frame = new halt_list_filter_frame_t(m_player, this); create_win(filter_frame, w_info, magic_haltlist_filter); } } return true; } void halt_list_frame_t::draw(scr_coord pos, scr_size size) { filter_details.pressed = filter_frame != NULL; if( last_world_stops != haltestelle_t::get_alle_haltestellen().get_count() || strcmp(last_name_filter, name_filter) ) { // some deleted/ added => resort strcpy(last_name_filter, name_filter); sort_list(); } gui_frame_t::draw(pos, size); } void halt_list_frame_t::set_ware_filter_ab(const goods_desc_t *ware, int mode) { if(ware != goods_manager_t::none) { if(get_ware_filter_ab(ware)) { if(mode != 1) { waren_filter_ab.remove(ware); } } else { if(mode != 0) { waren_filter_ab.append(ware); } } } } void halt_list_frame_t::set_ware_filter_an(const goods_desc_t *ware, int mode) { if(ware != goods_manager_t::none) { if(get_ware_filter_an(ware)) { if(mode != 1) { waren_filter_an.remove(ware); } } else { if(mode != 0) { waren_filter_an.append(ware); } } } } void halt_list_frame_t::set_alle_ware_filter_ab(int mode) { if(mode == 0) { waren_filter_ab.clear(); } else { for(unsigned int i = 0; iget_player_nr(); sint16 sort_mode = sortby; file->rdwr_byte(player_nr); size.rdwr(file); tabs.rdwr(file); scrolly->rdwr(file); file->rdwr_str(name_filter, lengthof(name_filter)); file->rdwr_short(sort_mode); file->rdwr_bool(sortreverse); file->rdwr_byte(filter_flags); if (file->is_saving()) { uint8 good_nr = waren_filter_ab.get_count(); file->rdwr_byte(good_nr); if (good_nr > 0) { for(const goods_desc_t* const i : waren_filter_ab) { char* name = const_cast(i->get_name()); file->rdwr_str(name, 256); } } good_nr = waren_filter_an.get_count(); file->rdwr_byte(good_nr); if (good_nr > 0) { for(const goods_desc_t* const i : waren_filter_an) { char* name = const_cast(i->get_name()); file->rdwr_str(name, 256); } } } else { // restore warenfilter uint8 good_nr; file->rdwr_byte(good_nr); if (good_nr > 0) { waren_filter_ab.clear(); for (sint16 i = 0; i < good_nr; i++) { char name[256]; file->rdwr_str(name, lengthof(name)); if (const goods_desc_t* gd = goods_manager_t::get_info(name)) { waren_filter_ab.append(gd); } } } file->rdwr_byte(good_nr); if (good_nr > 0) { waren_filter_an.clear(); for (sint16 i = 0; i < good_nr; i++) { char name[256]; file->rdwr_str(name, lengthof(name)); if (const goods_desc_t* gd = goods_manager_t::get_info(name)) { waren_filter_an.append(gd); } } } sortby = (sort_mode_t)sort_mode; m_player = welt->get_player(player_nr); set_owner(m_player); win_set_magic(this, magic_halt_list + player_nr); sort_list(); set_windowsize(size); } } simutrans-124.5/src/simutrans/gui/halt_list_frame.h000066400000000000000000000071001520675460500224700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_HALT_LIST_FRAME_H #define GUI_HALT_LIST_FRAME_H #include "simwin.h" #include "gui_frame.h" #include "../simhalt.h" #include "halt_list_stats.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_combobox.h" #include "components/gui_textinput.h" #include "components/gui_waytype_tab_panel.h" #include "../tpl/vector_tpl.h" class player_t; class goods_desc_t; class gui_scrolled_halt_list_t; /** * Displays a scrollable list of all stations of a player */ class halt_list_frame_t : public gui_frame_t , private action_listener_t { public: enum sort_mode_t { nach_name, nach_wartend, nach_typ, SORT_MODES }; enum filter_flag_t { spezial_filter = 1 << 1, ware_ab_filter = 1 << 2, ware_an_filter = 1 << 3, ueberfuellt_filter = 1 << 4, ohneverb_filter = 1 << 5 }; private: player_t *m_player; static const char *sort_text[SORT_MODES]; uint32 last_world_stops; /* * All gui elements of this dialog: */ gui_combobox_t sortedby; button_t sorteddir; button_t filter_details; gui_scrolled_halt_list_t *scrolly; static char name_filter[256]; char last_name_filter[256]; gui_textinput_t name_filter_input; gui_waytype_tab_panel_t tabs; /* * Child window, if open */ gui_frame_t *filter_frame; /* * All filter settings are static, so they are not reset each * time the window closes. */ static sort_mode_t sortby; static bool sortreverse; static uint8 filter_flags; static slist_tpl waren_filter_ab; static slist_tpl waren_filter_an; public: static bool compare_halts(halthandle_t, halthandle_t); halt_list_frame_t(); ~halt_list_frame_t(); /** * The filter frame tells us when it is closed. */ void filter_frame_closed() { filter_frame = NULL; } // must be handled, because we could not use scrollpanel bool infowin_event(event_t const*) OVERRIDE; /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; /// sort & filter halts in list void sort_list(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "haltlist.txt"; } static sort_mode_t get_sortierung() { return sortby; } static void set_sortierung(sort_mode_t sm) { sortby = sm; } static bool get_reverse() { return sortreverse; } static void set_reverse(bool reverse) { sortreverse = reverse; } static bool get_filter(filter_flag_t filter) { return (filter_flags & filter) != 0; } static void set_filter(filter_flag_t filter, bool on) { filter_flags = on ? (filter_flags | filter) : (filter_flags & ~filter); } static bool get_ware_filter_ab(const goods_desc_t *ware) { return waren_filter_ab.is_contained(ware); } static void set_ware_filter_ab(const goods_desc_t *ware, int mode); static void set_alle_ware_filter_ab(int mode); static bool get_ware_filter_an(const goods_desc_t *ware) { return waren_filter_an.is_contained(ware); } static void set_ware_filter_an(const goods_desc_t *ware, int mode); static void set_alle_ware_filter_an(int mode); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void map_rotate90( sint16 ) OVERRIDE { sort_list(); } void rdwr(loadsave_t* file) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_halt_list; } }; #endif simutrans-124.5/src/simutrans/gui/halt_list_stats.cc000066400000000000000000000060101520675460500226710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "halt_list_stats.h" #include "../simhalt.h" #include "../simskin.h" #include "../simcolor.h" #include "../tool/simtool.h" #include "../display/simgraph.h" #include "../display/viewport.h" #include "../player/simplay.h" #include "../world/simworld.h" #include "../display/simimg.h" #include "../dataobj/translator.h" #include "../descriptor/skin_desc.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include "gui_frame.h" #include "halt_info.h" // gui_halt_type_images_t static karte_ptr_t welt; /** * Events are notified to GUI components via this method */ bool halt_list_stats_t::infowin_event(const event_t *ev) { bool swallowed = gui_aligned_container_t::infowin_event(ev); if( !swallowed && halt.is_bound() ) { if(IS_LEFTRELEASE(ev)) { if ((event_get_last_control_shift() ^ tool_t::control_invert) == 2) { welt->get_viewport()->change_world_position(halt->get_basis_pos3d()); } else { halt->open_info_window(); } return true; } if( IS_RIGHTRELEASE(ev) ) { welt->get_viewport()->change_world_position(halt->get_basis_pos3d()); return true; } } return swallowed; } halt_list_stats_t::halt_list_stats_t(halthandle_t h) { halt = h; set_table_layout(2,2); set_spacing(scr_size(D_H_SPACE, 0)); gotopos.set_typ(button_t::posbutton_automatic); gotopos.set_targetpos3d(halt->get_basis_pos3d()); add_component(&gotopos); add_table(2,1); { add_component(&label_name); label_name.buf().append(halt->get_name()); label_name.update(); img_types = new_component(halt); } end_table(); // second row, skip posbutton new_component(); add_table(4,1); { add_component(&img_enabled[0]); img_enabled[0].set_image(skinverwaltung_t::passengers->get_image_id(0), true); add_component(&img_enabled[1]); img_enabled[1].set_image(skinverwaltung_t::mail->get_image_id(0), true); add_component(&img_enabled[2]); img_enabled[2].set_image(skinverwaltung_t::goods->get_image_id(0), true); img_enabled[0].set_rigid(true); img_enabled[1].set_rigid(true); img_enabled[2].set_rigid(true); add_component(&label_cargo); halt->get_short_freight_info( label_cargo.buf() ); label_cargo.update(); } end_table(); } const char* halt_list_stats_t::get_text() const { return halt->get_name(); } /** * Draw the component */ void halt_list_stats_t::draw(scr_coord offset) { gotopos.set_targetpos3d(halt->get_basis_pos3d()); // since roation may have changed the target pos img_enabled[0].set_visible(halt->get_pax_enabled()); img_enabled[1].set_visible(halt->get_mail_enabled()); img_enabled[2].set_visible(halt->get_ware_enabled()); label_name.buf().append(halt->get_name()); label_name.update(); label_name.set_color(halt->get_status_farbe()); label_name.set_shadow(SYSCOL_TEXT,true); halt->get_short_freight_info( label_cargo.buf() ); label_cargo.update(); set_size(get_size()); gui_aligned_container_t::draw(offset); } simutrans-124.5/src/simutrans/gui/halt_list_stats.h000066400000000000000000000024721520675460500225430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_HALT_LIST_STATS_H #define GUI_HALT_LIST_STATS_H #include "components/gui_component.h" #include "components/gui_aligned_container.h" #include "components/gui_colorbox.h" #include "components/gui_button.h" #include "components/gui_image.h" #include "components/gui_label.h" #include "components/gui_scrolled_list.h" #include "../halthandle.h" class gui_halt_type_images_t; class halt_list_stats_t : public gui_aligned_container_t, public gui_scrolled_list_t::scrollitem_t { private: halthandle_t halt; public: gui_label_buf_t label_name, label_cargo; gui_image_t img_enabled[3]; gui_halt_type_images_t *img_types; gui_colorbox_t indicator; button_t gotopos; public: halt_list_stats_t(halthandle_t halt_); bool infowin_event(event_t const*) OVERRIDE; /** * Draw the component */ void draw(scr_coord offset) OVERRIDE; void update_label(); const char* get_text() const OVERRIDE; bool is_valid() const OVERRIDE { return halt.is_bound(); } halthandle_t get_halt() const { return halt; } scr_size get_min_size() const OVERRIDE { return gui_aligned_container_t::get_min_size() + scr_size(D_H_SPACE, D_V_SPACE); } scr_size get_max_size() const OVERRIDE { return get_min_size(); } }; #endif simutrans-124.5/src/simutrans/gui/headquarter_info.cc000066400000000000000000000036451520675460500230230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "headquarter_info.h" #include "simwin.h" #include "../tool/simmenu.h" #include "../world/simworld.h" #include "../obj/gebaeude.h" #include "../player/simplay.h" headquarter_info_t::headquarter_info_t(player_t* player) : base_infowin_t(player->get_name(), player), headquarter_view(koord3d::invalid, scr_size( max(64, gfx->get_base_tile_raster_width()), max(56, (gfx->get_base_tile_raster_width()*7)/8) ) ) { this->player = player; set_embedded(&headquarter_view); upgrade.init(button_t::roundbox, "upgrade HQ"); upgrade.add_listener(this); add_component(&upgrade); update(); recalc_size(); } void headquarter_info_t::update() { // possibly update hq position const koord p2d = player->get_headquarter_pos(); if (p2d != koord::invalid) { // update hq position grund_t *gr = welt->lookup_kartenboden(p2d); koord3d pos = gr->get_pos(); headquarter_view.set_location(pos); gebaeude_t* hq = gr->find(); if (hq) { buf.clear(); hq->info(buf); } } else { set_embedded(NULL); } // check for possible upgrades const char * c = tool_t::general_tool[TOOL_HEADQUARTER]->get_tooltip(player); if(c) { // only true, if the headquarter can be built/updated headquarter_tooltip.clear(); headquarter_tooltip.append(c); upgrade.set_tooltip(headquarter_tooltip); upgrade.enable(); } else { upgrade.disable(); upgrade.set_tooltip(NULL); } reset_min_windowsize(); } void headquarter_info_t::draw(scr_coord pos, scr_size size) { update(); if (get_embedded() == NULL) { // no headquarter anymore, destroy window destroy_win(this); return; } base_infowin_t::draw(pos, size); } bool headquarter_info_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { if( comp == &upgrade ) { welt->set_tool( tool_t::general_tool[TOOL_HEADQUARTER], player ); } return true; } simutrans-124.5/src/simutrans/gui/headquarter_info.h000066400000000000000000000013711520675460500226570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_HEADQUARTER_INFO_H #define GUI_HEADQUARTER_INFO_H #include "base_info.h" #include "components/gui_button.h" #include "components/action_listener.h" #include "components/gui_location_view.h" #include "../utils/cbuffer.h" class gebaeude_t; class player_t; class headquarter_info_t : public base_infowin_t, private action_listener_t { button_t upgrade; cbuffer_t headquarter_tooltip; player_t *player; location_view_t headquarter_view; void update(); public: headquarter_info_t(player_t* player); void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t *comp, value_t extra) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/help_frame.cc000066400000000000000000000453051520675460500216040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simmem.h" #include "simwin.h" #include "../tool/simmenu.h" #include "../sys/simsys.h" #include "../world/simworld.h" #include "../simticker.h" // TICKER_HEIGHT #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../utils/unicode.h" #include "../player/simplay.h" #include "tool_selector.h" #include "help_frame.h" #define DIALOG_MIN_WIDTH (100) help_frame_t::help_frame_t() : gui_frame_t( translator::translate("Help") ) { // info windows do not show general text generaltext.set_visible( false ); set_text("Unnamed

No text set

"); set_table_layout(1,0); helptext.add_listener(this); add_component(&helptext); set_resizemode(diagonal_resize); reset_min_windowsize(); set_windowsize( scr_size( D_MARGIN_LEFT + (D_SCROLLBAR_WIDTH<<1) + DIALOG_MIN_WIDTH + D_MARGIN_RIGHT, D_TITLEBAR_HEIGHT + D_MARGIN_TOP + (D_SCROLLBAR_HEIGHT<<1) + D_MARGIN_BOTTOM) ); } help_frame_t::help_frame_t(char const* const filename) : gui_frame_t( translator::translate("Help") ) { set_table_layout(2,0); set_alignment(ALIGN_TOP | ALIGN_LEFT); set_margin(scr_size(0,D_MARGIN_TOP), scr_size(0,0)); add_component(&generaltext); generaltext.add_listener(this); add_component(&helptext); helptext.add_listener(this); // we now exclusive build out index on the fly slist_tpl already_there; cbuffer_t index_txt; cbuffer_t introduction; cbuffer_t usage; cbuffer_t toolbars; cbuffer_t game_start; cbuffer_t how_to_play; cbuffer_t others; add_helpfile( introduction, NULL, "simutrans.txt", true, 0 ); // main usage section { // get title of keyboard help ... std::string kbtitle = extract_title( translator::translate( "Keyboard Help\n

Keyboard Help

\n" ) ); assert( !kbtitle.empty() ); usage.printf( "%s
\n", kbtitle.c_str() ); } add_helpfile( usage, NULL, "mouse.txt", true, 0 ); add_helpfile( usage, NULL, "window.txt", true, 0 ); // enumerate toolbars bool special = false; add_helpfile( toolbars, NULL, "mainmenu.txt", false, 0 ); for(toolbar_t * iter : tool_t::toolbar_tool ) { if( strstart(iter->get_tool_selector()->get_help_filename(),"list.txt" ) ) { continue; } add_helpfile( toolbars, translator::translate(iter->get_tool_selector()->get_internal_name()), iter->get_tool_selector()->get_help_filename(), false, 0 ); if( strstart(iter->get_tool_selector()->get_help_filename(),"railtools.txt" ) ) { add_helpfile( toolbars, NULL, "bridges.txt", true, 1 ); add_helpfile( toolbars, NULL, "signals.txt", true, 1 ); add_helpfile( toolbars, "set signal spacing", "signal_spacing.txt", false, 1 ); } if( strstart(iter->get_tool_selector()->get_help_filename(),"roadtools.txt" ) ) { add_helpfile( toolbars, NULL, "privatesign_info.txt", false, 1 ); add_helpfile( toolbars, NULL, "trafficlight_info.txt", false, 1 ); } if( !special && ( strstart(iter->get_tool_selector()->get_help_filename(),"special.txt" ) || strstart(iter->get_tool_selector()->get_help_filename(),"edittools.txt" ) ) ) { special = true; add_helpfile( toolbars, "baum builder", "baum_build.txt", false, 1 ); add_helpfile( toolbars, "citybuilding builder", "citybuilding_build.txt", false, 1 ); add_helpfile( toolbars, "curiosity builder", "curiosity_build.txt", false, 1 ); add_helpfile( toolbars, "factorybuilder", "factory_build.txt", false, 1 ); } } add_helpfile( toolbars, NULL, "inspection_tool.txt", true, 0 ); add_helpfile( toolbars, NULL, "removal_tool.txt", true, 0 ); add_helpfile( toolbars, "LISTTOOLS", "list.txt", false, 0 ); add_helpfile( toolbars, NULL, "citylist_filter.txt", false, 1 ); add_helpfile( toolbars, NULL, "convoi.txt", false, 1 ); add_helpfile( toolbars, NULL, "convoi_filter.txt", false, 2 ); add_helpfile( toolbars, NULL, "curiositylist_filter.txt", false, 1 ); add_helpfile( toolbars, NULL, "factorylist_filter.txt", false, 1 ); add_helpfile( toolbars, NULL, "goods_filter.txt", false, 1 ); add_helpfile( toolbars, NULL, "haltlist.txt", false, 1 ); add_helpfile( toolbars, NULL, "haltlist_filter.txt", false, 2 ); add_helpfile( toolbars, NULL, "labellist_filter.txt", false, 1 ); add_helpfile( game_start, "Neue Welt", "new_world.txt", false, 0 ); add_helpfile( game_start, "Lade Relief", "load_relief.txt", false, 1 ); add_helpfile( game_start, "Climate Control", "climates.txt", false, 1 ); add_helpfile( game_start, "Setting", "settings.txt", false, 1 ); add_helpfile( game_start, "Load game", "load.txt", false, 0 ); add_helpfile( game_start, "Load scenario", "scenario.txt", false, 0 ); add_helpfile( game_start, "join game", "server.txt", false, 0 ); add_helpfile( game_start, "Speichern", "save.txt", false, 0 ); add_helpfile( how_to_play, "Reliefkarte", "map.txt", false, 0 ); add_helpfile( how_to_play, "enlarge map", "enlarge_map.txt", false, 1 ); add_helpfile( how_to_play, NULL, "underground.txt", true, 0 ); add_helpfile( how_to_play, NULL, "citywindow.txt", true, 0 ); add_helpfile( how_to_play, NULL, "depot.txt", false, 0 ); add_helpfile( how_to_play, NULL, "convoiinfo.txt", false, 0 ); add_helpfile( how_to_play, NULL, "convoidetail.txt", false, 1 ); add_helpfile( how_to_play, "Line Management", "linemanagement.txt", false, 0 ); add_helpfile( how_to_play, "Fahrplan", "schedule.txt", false, 1 ); add_helpfile( how_to_play, NULL, "station.txt", false, 0 ); add_helpfile( how_to_play, NULL, "station_details.txt", false, 1 ); add_helpfile( how_to_play, NULL, "industry_info.txt", false, 0 ); add_helpfile( how_to_play, "Spielerliste", "players.txt", false, 0 ); add_helpfile( how_to_play, "Finanzen", "finances.txt", false, 1 ); add_helpfile( how_to_play, "Farbe", "color.txt", false, 1 ); // add_helpfile( how_to_play, "Scenario", "scenario.txt", false, 1 ); add_helpfile( how_to_play, "Enter Password", "password.txt", false, 1 ); add_helpfile( others, "Einstellungen aendern", "options.txt", false, 0 ); add_helpfile( others, "Display settings", "display.txt", false, 0 ); add_helpfile( others, "Mailbox", "mailbox.txt", false, 0 ); add_helpfile( others, "Sound settings", "sound.txt", false, 0 ); add_helpfile( others, "Sprachen", "language.txt", false, 0 ); index_txt.printf( translator::translate("

Index

*: only english

General

%s

Usage

%s

Tools

%s

Start

%s

How to play

%s

Others:

%s"), (const char *)introduction, (const char *)usage, (const char *)toolbars, (const char *)game_start, (const char *)how_to_play, (const char *)others ); generaltext.set_text( index_txt ); set_helpfile( filename, true ); set_resizemode(diagonal_resize); reset_min_windowsize(); set_windowsize(scr_size(200, D_DEFAULT_HEIGHT)); } // opens or tops main helpfile void help_frame_t::open_help_on( const char *helpfilename ) { if( help_frame_t *gui = (help_frame_t *)win_get_magic( magic_mainhelp ) ) { top_win( gui ); gui->set_helpfile( helpfilename, false ); } else { create_win( new help_frame_t( helpfilename ), w_info, magic_mainhelp ); } } // just loads a whole help file as one chunk static char *load_text(char const* const filename ) { std::string file_prefix= std::string("text") + PATH_SEPARATOR; std::string fullname = file_prefix + translator::get_lang()->iso + PATH_SEPARATOR + filename; dr_chdir(env_t::pak_dir.c_str()); FILE* file = dr_fopen(fullname.c_str(), "rb"); if (!file) { dr_chdir(env_t::base_dir); file = dr_fopen(fullname.c_str(), "rb"); } if (!file) { //Check for the 'base' language(ie en from en_gb) file = dr_fopen((file_prefix + translator::get_lang()->iso_base + PATH_SEPARATOR + filename).c_str(), "rb"); } if (!file) { // check fallback english file = dr_fopen((file_prefix + PATH_SEPARATOR + "en" + PATH_SEPARATOR + filename).c_str(), "rb"); } if (!file) { // no help file found then sarch [pakset]/text/[lang]/ file = dr_fopen((env_t::pak_dir + file_prefix + translator::get_lang()->iso + PATH_SEPARATOR + filename).c_str(), "rb"); } if (!file) { // no help file found then sarch [pakset]/text/[lang (bese)]/ file = dr_fopen((env_t::pak_dir + file_prefix + translator::get_lang()->iso_base + PATH_SEPARATOR + filename).c_str(), "rb"); } if (!file) { // no help file found then sarch [pakset]/text/en/ file = dr_fopen((env_t::pak_dir + file_prefix + "en" + PATH_SEPARATOR + filename).c_str(), "rb"); } // go back to load/save dir dr_chdir( env_t::user_dir ); if(file) { fseek(file,0,SEEK_END); long len = ftell(file); char *buf = NULL; if( len>0 ) { buf = MALLOCN(char, len + 1); fseek( file, 0, SEEK_SET ); len = fread( buf, 1, len, file); buf[len] = '\0'; fclose( file ); } // now we may need to translate the text ... if( len>0 ) { bool is_latin = strchr( buf, 0xF6 )!=NULL; // "o-umlaut, is forbidden for unicode if( !is_latin && translator::get_lang()->is_latin2_based ) { is_latin |= strchr( buf, 0xF8 )!=NULL; // "o-umlaut, is forbidden for unicode } if( !is_latin && translator::get_lang()->is_latin2_based ) { is_latin |= strchr( buf, 0xFE )!=NULL; // "o-umlaut, is forbidden for unicode } if( !is_latin && translator::get_lang()->is_latin2_based ) { is_latin |= strchr( buf, 0xFA )!=NULL; // "o-umlaut, is forbidden for unicode } if( !is_latin && translator::get_lang()->is_latin2_based ) { is_latin |= strchr( buf, 0xF3 )!=NULL; // "o-umlaut, is forbidden for unicode } if( !is_latin && translator::get_lang()->is_latin2_based ) { is_latin |= strchr( buf, 0xF1 )!=NULL; // "o-umlaut, is forbidden for unicode } if( is_latin ) { // we need to translate charwise ... utf8 *buf2 = MALLOCN(utf8, len*2 + 1); //assume the worst utf8 *src = (utf8 *)buf, *dest = buf2; if( translator::get_lang()->is_latin2_based ) { do { dest += utf16_to_utf8( latin2_to_unicode(*src), dest ); } while( *src++ ); *dest = 0; } else { do { dest += utf16_to_utf8( *src, dest ); } while( *src++ ); *dest = 0; } free( buf ); buf = (char *)buf2; } } return buf; } return NULL; } void help_frame_t::set_text(const char * buf, bool resize_frame ) { helptext.set_text(buf); const scr_size screen = gfx->get_screen_size(); if( resize_frame ) { // try to get the following sizes // y<400 or, if not possible, x<620 helptext.set_size(scr_size(220, 0)); int last_y = 0; scr_coord curr = helptext.get_preferred_size(); for( int i = 0; i<10 && curr.y>400 && curr.y!=last_y; i++ ) { helptext.set_size(scr_size(260+i*40, 0)); last_y = curr.y; curr = helptext.get_preferred_size(); } helptext.set_size(helptext.get_preferred_size()); if( generaltext.is_visible() ) { generaltext.set_pos( scr_coord(D_MARGIN_LEFT, D_MARGIN_TOP) ); generaltext.set_size( scr_size( min(180, screen.w/3), 0 ) ); int generalwidth = min( screen.w/3, generaltext.get_preferred_size().w ); generaltext.set_size( scr_size( generalwidth, helptext.get_size().h ) ); generaltext.set_size( generaltext.get_preferred_size() ); } else { generaltext.set_size( scr_size(D_MARGIN_LEFT, D_MARGIN_TOP) ); } // calculate sizes (might not a help but info window, which do not have general text) scr_coord_val size_x = helptext.get_size().w + D_MARGIN_LEFT + D_MARGIN_RIGHT + D_SCROLLBAR_WIDTH; scr_coord_val size_y = helptext.get_size().h + D_TITLEBAR_HEIGHT + D_MARGIN_TOP + D_MARGIN_BOTTOM + D_SCROLLBAR_HEIGHT; if( generaltext.is_visible() ) { size_x += generaltext.get_size().w + D_SCROLLBAR_WIDTH + D_H_SPACE; } // set window size if( size_x > screen.w-32 ) { size_x = screen.w-32; } const scr_coord_val h = screen.h - D_TITLEBAR_HEIGHT - win_get_statusbar_height() - TICKER_HEIGHT; if( size_y>h) { size_y = h; } set_windowsize( scr_size( size_x, size_y ) ); } // generate title title = ""; if( generaltext.is_visible() ) { title = translator::translate( "Help" ); title += " - "; } title += helptext.get_title(); set_name( title.c_str() ); resize( scr_coord(0,0) ); set_dirty(); } // show the help to one topic void help_frame_t::set_helpfile(const char *filename, bool resize_frame ) { // the key help texts are built automagically if (strcmp(filename, "keys.txt") == 0) { cbuffer_t buf; buf.append( translator::translate( "Keyboard Help\n

Keyboard Help

\n" ) ); player_t *player = welt->get_active_player(); const char *trad_str = translator::translate( "%s - %s
\n" ); for(tool_t* const i : tool_t::char_to_tool) { cbuffer_t c; char str[16]; if( i->command_flags & SIM_KEYMOD_CTRL ) { c.append( translator::translate( "[CTRL]" ) ); c.append( " + " ); } if( i->command_flags & SIM_KEYMOD_SHIFT ) { c.append( translator::translate( "[SHIFT]" ) ); c.append( " + " ); } switch (uint16 const key = i->command_key) { case '<': c.append( "<" ); break; case '>': c.append( ">" ); break; case 27: c.append( translator::translate( "[ESCAPE]" ) ); break; case 127: c.append( translator::translate( "[DELETE]" ) ); break; case SIM_KEYCODE_HOME: c.append( translator::translate( "[HOME]" ) ); break; case SIM_KEYCODE_END: c.append( translator::translate( "[END]" ) ); break; case SIM_KEYCODE_SCROLLLOCK: c.append( translator::translate( "[SCROLLLOCK]" ) ); break; default: if (key <= 26) { c.printf("%c", '@' + key); } else if (key < 256) { c.printf("%c", key); } else if (key < SIM_KEYCODE_F15) { c.printf("F%i", key - SIM_KEYCODE_F1 + 1); } else { // try unicode str[utf16_to_utf8(key, (utf8*)str)] = '\0'; c.append( str ); } break; } buf.printf(trad_str, c.get_str(), i->get_tooltip(player)); } set_text( buf, resize_frame ); } else if( strcmp( filename, "general.txt" )!=0 ) { // and the actual help text (if not identical) if( char *buf = load_text( filename ) ) { set_text( buf, resize_frame ); free(buf); } else {{ cbuffer_t buf; buf.printf("%s%s", translator::translate("Error"), translator::translate("Help text not found")); set_text(buf, resize_frame ); }} } else { // default text when opening general help if( char *buf = load_text( "about.txt" ) ) { set_text( buf, resize_frame ); free(buf); } else if( char *buf = load_text( "simutrans.txt" ) ) { set_text( buf, resize_frame ); free(buf); } else { set_text( "", resize_frame ); } } } FILE *help_frame_t::has_helpfile( char const* const filename, int &mode ) { mode = native; std::string file_prefix = std::string("text") + PATH_SEPARATOR; std::string fullname = file_prefix + translator::get_lang()->iso + PATH_SEPARATOR + filename; dr_chdir(env_t::base_dir); FILE* file = dr_fopen(fullname.c_str(), "rb"); if( !file && strcmp(translator::get_lang()->iso,translator::get_lang()->iso_base) ) { //Check for the 'base' language(ie en from en_gb) file = dr_fopen( (file_prefix + translator::get_lang()->iso_base + PATH_SEPARATOR + filename).c_str(), "rb" ); } if( !file ) { // check fallback english file = dr_fopen((file_prefix + "en/" + filename).c_str(), "rb"); mode = english; } // go back to load/save dir dr_chdir( env_t::user_dir ); // success? if( !file ) { mode = missing; } return file; } // extracts the title and ASCII from a string std::string help_frame_t::extract_title( const char *htmllines ) { const uint8 *start = (const uint8 *)strstr( htmllines, "" ); const uint8 *end = (const uint8 *)strstr( htmllines, "" ); bool convert_to_utf = false; uint8 title_form_html[1024]; if( start && end && (size_t)(end-start)=32 ) { // convert to UTF if needed if( !convert_to_utf && *c >= 0x80 ) { size_t len; utf8_decoder_t::decode(c, len); convert_to_utf = (len <= 1); } if (convert_to_utf) { if (translator::get_lang()->is_latin2_based) { dest += utf16_to_utf8(latin2_to_unicode(*c), dest); } else { dest += utf16_to_utf8(*c, dest); } c++; } else { size_t len; utf32 cc = utf8_decoder_t::decode(c, len); dest += utf16_to_utf8(cc, dest); c += len; } } else { // avoid double spaces if( dest!=title_form_html && dest[-1]!=' ' ) { *dest++ = ' '; } c++; } } *dest = 0; return (const char *)title_form_html; } return ""; } void help_frame_t::add_helpfile( cbuffer_t §ion, const char *titlename, const char *filename, bool only_native, int indent_level ) { if( strempty(filename) ) { return; } int mode; FILE *file = has_helpfile( filename, mode ); if( ( only_native && mode!=native ) || mode==missing ) { return; } std::string filetitle; // just in case as temporary storage ... if( titlename == NULL && file ) { // get the title from the helpfile char htmlline[1024]; if (fread( htmlline, lengthof(htmlline)-1, 1, file ) == 1) { filetitle = extract_title( htmlline ); if( filetitle.empty() ) { // no idea how to generate the right name ... titlename = filename; } else { titlename = filetitle.c_str(); } } else { titlename = filename; } } else { titlename = translator::translate( titlename ); } // now build the entry if( mode != missing ) { while( indent_level-- > 0 ) { section.append( "+" ); } if( mode == native ) { section.printf( "%s
\n", filename, titlename ); } else if( mode == english ) { section.printf( "%s%s
\n", filename, "*", titlename ); } fclose( file ); } } /** * Called upon link activation * @param extra the name of the help file */ bool help_frame_t::action_triggered( gui_action_creator_t *, value_t extra) { top_win( this ); set_helpfile( (const char *)(extra.p), false ); return true; } void help_frame_t::resize(const scr_coord delta) { gui_frame_t::resize(delta); scr_coord_val generalwidth = 0; if( generaltext.is_visible() ) { // do not use more than 1/3 for the general infomations generalwidth = min( get_windowsize().w/3, generaltext.get_preferred_size().w ) + D_SCROLLBAR_WIDTH; generaltext.set_size( scr_size( generalwidth, get_client_windowsize().h - D_MARGIN_BOTTOM) ); generalwidth += D_H_SPACE; helptext.set_pos( generaltext.get_pos() + scr_size( generalwidth, 0 ) ); } helptext.set_size( get_client_windowsize() - scr_size( generalwidth, 0) -scr_size(0,D_MARGIN_BOTTOM) ); } simutrans-124.5/src/simutrans/gui/help_frame.h000066400000000000000000000022171520675460500214410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_HELP_FRAME_H #define GUI_HELP_FRAME_H #include #include "gui_frame.h" #include "components/gui_flowtext.h" #include "components/action_listener.h" class cbuffer_t; class help_frame_t : public gui_frame_t, action_listener_t { private: enum { missing, native, english }; gui_flowtext_t generaltext, helptext; std::string title; // show the help to one topic void set_helpfile(const char *filename, bool resize_frame ); help_frame_t(char const* filename); void add_helpfile( cbuffer_t §ion, const char *titlename, const char *filename, bool only_native, int indent_level ); std::string extract_title( const char *htmllines ); static FILE *has_helpfile( char const* const filename, int &mode ); public: help_frame_t(); static void open_help_on( const char *helpfilename ); void set_text(const char * text, bool resize = true ); /** * resize window in response to a resize event */ void resize(const scr_coord delta) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/jump_frame.cc000066400000000000000000000027121520675460500216220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../world/simworld.h" #include "../display/viewport.h" #include "../obj/zeiger.h" #include "jump_frame.h" #include "components/gui_divider.h" #include "../dataobj/translator.h" jump_frame_t::jump_frame_t() : gui_frame_t( translator::translate("Jump to") ) { set_table_layout(1,0); // Input box for new name sprintf(buf, "%i,%i", welt->get_viewport()->get_world_position().x, welt->get_viewport()->get_world_position().y ); input.set_text(buf, 62); input.add_listener(this); add_component(&input); new_component(); jumpbutton.init( button_t::roundbox, "Jump to"); jumpbutton.add_listener(this); add_component(&jumpbutton); set_focus(&input); reset_min_windowsize(); set_windowsize(get_min_windowsize()); } /** * This method is called if an action is triggered */ bool jump_frame_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { if(comp == &input || comp == &jumpbutton) { // OK- Button or Enter-Key pressed //--------------------------------------- koord my_pos; sint16 h; bool has_h = sscanf(buf, "%hd,%hd,%hd", &my_pos.x, &my_pos.y, &h)>2; if(welt->is_within_limits(my_pos)) { koord3d k( my_pos, has_h ? h : welt->min_hgt( my_pos ) ); welt->get_viewport()->change_world_position(k); welt->get_zeiger()->change_pos( k ); } } return true; } simutrans-124.5/src/simutrans/gui/jump_frame.h000066400000000000000000000012671520675460500214700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_JUMP_FRAME_H #define GUI_JUMP_FRAME_H #include "components/action_listener.h" #include "gui_frame.h" #include "components/gui_textinput.h" #include "components/gui_button.h" class jump_frame_t : public gui_frame_t, action_listener_t { char buf[64]; gui_textinput_t input; button_t jumpbutton; public: jump_frame_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE { return "jump_frame.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/kennfarbe.cc000066400000000000000000000077401520675460500214360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "kennfarbe.h" #include "../world/simworld.h" #include "../simskin.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../player/simplay.h" #include "components/gui_label.h" #include "components/gui_image.h" #include "../tool/simmenu.h" /** * Buttons forced to be square ... */ class choose_color_button_t : public button_t { scr_coord_val w; public: choose_color_button_t() : button_t() { w = max(D_BUTTON_HEIGHT, gfx->get_char_width('X') + D_BUTTON_PADDINGS_X); } scr_size get_min_size() const OVERRIDE { return scr_size(w, D_BUTTON_HEIGHT); } }; farbengui_t::farbengui_t(player_t *player_) : gui_frame_t( translator::translate("Farbe"), player_ ), txt(&buf) { player = player_; buf.clear(); buf.append(translator::translate("COLOR_CHOOSE\n")); set_table_layout(1,0); add_table(2,1); // Info text txt.recalc_size(); add_component( &txt ); // Picture new_component(skinverwaltung_t::color_options->get_image_id(0), player->get_player_nr(), ALIGN_NONE, true); end_table(); // Player's primary color label new_component("Your primary color:"); // Get all colors (except the current player's) uint32 used_colors1 = 0; uint32 used_colors2 = 0; for( int i=0; iget_player_nr() && welt->get_player(i) ) { used_colors1 |= 1 << (welt->get_player(i)->get_player_color1() / 8); used_colors2 |= 1 << (welt->get_player(i)->get_player_color2() / 8); } } add_table(14,2); // Primary color buttons for(unsigned i=0; i<28; i++) { player_color_1[i] = new_component(); player_color_1[i]->init( button_t::box_state, (used_colors1 & (1<background_color = gfx->palette_lookup(i*8+4); player_color_1[i]->add_listener(this); } player_color_1[player->get_player_color1()/8]->pressed = true; end_table(); // Player's secondary color label new_component("Your secondary color:"); add_table(14,2); // Secondary color buttons for(unsigned i=0; i<28; i++) { player_color_2[i] = new_component(); player_color_2[i]->init( button_t::box_state, (used_colors2 & (1<background_color = gfx->palette_lookup(i*8+4); player_color_2[i]->add_listener(this); } player_color_2[player->get_player_color2()/8]->pressed = true; end_table(); reset_min_windowsize(); } /** * This method is called if an action is triggered */ bool farbengui_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { for(unsigned i=0; i<28; i++) { // new player 1 color? if(comp==player_color_1[i]) { for(unsigned j=0; j<28; j++) { player_color_1[j]->pressed = false; } player_color_1[i]->pressed = true; //env_t::default_settings.set_default_player_color(player->get_player_nr(), player->get_player_color1(), player->get_player_color2()); // re-colour a player cbuffer_t buf; buf.printf( "1%u,%i", player->get_player_nr(), i*8); tool_t *w = create_tool( TOOL_RECOLOUR_TOOL | SIMPLE_TOOL ); w->set_default_param( buf ); world()->set_tool( w, player ); // since init always returns false, it is save to delete immediately delete w; return true; } // new player color 2? if(comp==player_color_2[i]) { for(unsigned j=0; j<28; j++) { player_color_2[j]->pressed = false; } player_color_2[i]->pressed = true; //env_t::default_settings.set_default_player_color(player->get_player_nr(), player->get_player_color1(), player->get_player_color2()); // re-colour a player cbuffer_t buf; buf.printf( "2%u,%i", player->get_player_nr(), i*8); tool_t *w = create_tool( TOOL_RECOLOUR_TOOL | SIMPLE_TOOL ); w->set_default_param( buf ); world()->set_tool( w, player ); // since init always returns false, it is save to delete immediately delete w; return true; } } return false; } simutrans-124.5/src/simutrans/gui/kennfarbe.h000066400000000000000000000016531520675460500212750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_KENNFARBE_H #define GUI_KENNFARBE_H #include "../utils/cbuffer.h" #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_textarea.h" #include "components/gui_button.h" class choose_color_button_t; /** * Company colors window * Dialog to set the player's color */ class farbengui_t : public gui_frame_t, action_listener_t { private: player_t *player; cbuffer_t buf; gui_textarea_t txt; choose_color_button_t* player_color_1[28]; choose_color_button_t* player_color_2[28]; public: farbengui_t(player_t *player_); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE { return "color.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/label_info.cc000066400000000000000000000037621520675460500215750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "label_info.h" #include "components/gui_label.h" #include "../world/simworld.h" #include "simwin.h" #include "../tool/simmenu.h" #include "../obj/label.h" #include "../player/simplay.h" #include "../utils/simstring.h" #include "../utils/cbuffer.h" label_info_t::label_info_t(label_t* l) : gui_frame_t( translator::translate("Marker"), l->get_owner()), view(l->get_pos(), scr_size( max(64, gfx->get_base_tile_raster_width()), max(56, (gfx->get_base_tile_raster_width()*7)/8) )) { label = l; set_table_layout(1,0); // input add_component(&input); input.add_listener(this); add_table(3,0)->set_alignment(ALIGN_TOP); // left: player name new_component(label->get_owner() ? label->get_owner()->get_name() : "" ); new_component(); // right column: view add_component( &view ); end_table(); grund_t *gr = welt->lookup(l->get_pos()); if(gr->get_text()) { tstrncpy(edit_name, gr->get_text(), lengthof(edit_name)); } else { edit_name[0] = '\0'; } // text input input.set_text(edit_name, lengthof(edit_name)); set_focus(&input); reset_min_windowsize(); } /** * This method is called if an action is triggered */ bool label_info_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { if(comp == &input && welt->get_active_player()==label->get_owner()) { // check owner to change text grund_t *gd = welt->lookup(label->get_pos()); if( strcmp(gd->get_text(),edit_name) ) { // text changed => call tool cbuffer_t buf; buf.printf( "m%s,%s", label->get_pos().get_str(), edit_name ); tool_t *tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tool->set_default_param( buf ); welt->set_tool( tool, label->get_owner() ); // since init always returns false, it is safe to delete immediately delete tool; } destroy_win(this); } return true; } void label_info_t::map_rotate90( sint16 new_ysize ) { view.map_rotate90(new_ysize); } simutrans-124.5/src/simutrans/gui/label_info.h000066400000000000000000000013041520675460500214250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LABEL_INFO_H #define GUI_LABEL_INFO_H #include "gui_frame.h" #include "components/gui_textinput.h" #include "components/gui_location_view.h" #include "components/action_listener.h" class label_t; /** * Label creation/edition window */ class label_info_t : public gui_frame_t, private action_listener_t { private: label_t *label; gui_textinput_t input; location_view_t view; char edit_name[256]; public: label_info_t(label_t* l); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; // rotated map need new info ... void map_rotate90( sint16 ) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/labellist_frame.cc000066400000000000000000000076201520675460500226250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "labellist_frame.h" #include "labellist_stats.h" #include "../dataobj/translator.h" #include "../obj/label.h" #include "../world/simworld.h" #include "../utils/unicode.h" char labellist_frame_t::name_filter[256]; static const char *sort_text[labellist::SORT_MODES] = { "hl_btn_sort_name", "koord", "player" }; labellist_frame_t::labellist_frame_t() : gui_frame_t(translator::translate("labellist_title")), scrolly(gui_scrolled_list_t::windowskin, labellist_stats_t::compare) { set_table_layout(3,0); new_component("Filter:"); name_filter_input.set_text(name_filter, lengthof(name_filter)); name_filter_input.set_notify_all_changes_delay(0); name_filter_input.add_listener(this); add_component(&name_filter_input); new_component(); filter.init( button_t::square_automatic, "Active player only"); filter.pressed = labellist_stats_t::filter; add_component(&filter, 2); filter.add_listener( this ); new_component(); new_component("hl_txt_sort"); sortedby.set_unsorted(); // do not sort for (size_t i = 0; i < lengthof(sort_text); i++) { sortedby.new_component(translator::translate(sort_text[i]), SYSCOL_TEXT); } sortedby.set_selection(labellist_stats_t::sortby); sortedby.add_listener(this); add_component(&sortedby); sorteddir.init(button_t::sortarrow_state, NULL); sorteddir.add_listener(this); sorteddir.pressed = labellist_stats_t::sortreverse; add_component(&sorteddir); scrolly.set_maximize(true); scrolly.set_checkered(true); add_component(&scrolly,3); fill_list(); reset_min_windowsize(); set_resizemode(diagonal_resize); } void labellist_frame_t::fill_list() { label_count = welt->get_label_list().get_count(); scrolly.clear_elements(); for(koord const& pos : welt->get_label_list()) { label_t* label = welt->lookup_kartenboden(pos)->find(); const char* name = welt->lookup_kartenboden(pos)->get_text(); // some old version games don't have label nor name. // Check them to avoid crashes. if(label && name && (!labellist_stats_t::filter || (label && (label->get_owner() == welt->get_active_player())))) { if( name_filter[0] == 0 || utf8caseutf8(name, name_filter) ) { scrolly.new_component(pos); } } } scrolly.sort(0); reset_min_windowsize(); } uint32 labellist_frame_t::count_label() { uint32 labelcount = 0; for(koord const& pos : welt->get_label_list()) { label_t* label = welt->lookup_kartenboden(pos)->find(); const char* name = welt->lookup_kartenboden(pos)->get_text(); // some old version games don't have label nor name. if(label && name && (!labellist_stats_t::filter || (label && (label->get_owner() == welt->get_active_player())))) { labelcount++; } } return labelcount; } bool labellist_frame_t::action_triggered( gui_action_creator_t *comp,value_t v) { if(comp == &sortedby) { labellist_stats_t::sortby = (labellist::sort_mode_t)v.i; scrolly.sort(0); } else if(comp == &sorteddir) { labellist_stats_t::sortreverse = !labellist_stats_t::sortreverse; sorteddir.pressed = labellist_stats_t::sortreverse; scrolly.sort(0); } else if (comp == &name_filter_input) { fill_list(); } else if (comp == &filter) { labellist_stats_t::filter = !labellist_stats_t::filter; fill_list(); } return true; } void labellist_frame_t::rdwr(loadsave_t* file) { scr_size size = get_windowsize(); sint16 sort_mode = labellist_stats_t::sortby; size.rdwr(file); scrolly.rdwr(file); file->rdwr_str(name_filter, lengthof(name_filter)); file->rdwr_short(sort_mode); file->rdwr_bool(labellist_stats_t::sortreverse); file->rdwr_bool(labellist_stats_t::filter); if (file->is_loading()) { labellist_stats_t::sortby = (labellist::sort_mode_t)sort_mode; fill_list(); set_windowsize(size); } } simutrans-124.5/src/simutrans/gui/labellist_frame.h000066400000000000000000000020461520675460500224640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LABELLIST_FRAME_H #define GUI_LABELLIST_FRAME_H #include "simwin.h" #include "gui_frame.h" #include "components/gui_button.h" #include "components/gui_combobox.h" #include "components/gui_scrolled_list.h" #include "components/gui_textinput.h" /** * label list window */ class labellist_frame_t : public gui_frame_t, private action_listener_t { private: gui_combobox_t sortedby; button_t sorteddir; button_t filter; gui_scrolled_list_t scrolly; static char name_filter[256]; gui_textinput_t name_filter_input; uint32 label_count; public: labellist_frame_t(); const char *get_help_filename() const OVERRIDE {return "labellist_filter.txt"; } bool action_triggered( gui_action_creator_t *comp,value_t /* */) OVERRIDE; uint32 count_label(); void fill_list(); void map_rotate90( sint16 ) OVERRIDE { fill_list(); } void rdwr(loadsave_t* file) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_labellist; } }; #endif simutrans-124.5/src/simutrans/gui/labellist_stats.cc000066400000000000000000000066641520675460500227000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "labellist_stats.h" #include "components/gui_button.h" #include "../world/simworld.h" #include "../display/viewport.h" #include "../tool/simmenu.h" #include "../player/simplay.h" #include "../obj/label.h" #include "../utils/cbuffer.h" #include "../dataobj/environment.h" labellist::sort_mode_t labellist_stats_t::sortby = labellist::by_name; bool labellist_stats_t::sortreverse = false; bool labellist_stats_t::filter = false; static karte_ptr_t welt; bool labellist_stats_t::compare(const gui_component_t *aa, const gui_component_t *bb) { const labellist_stats_t* a = dynamic_cast(aa); const labellist_stats_t* b = dynamic_cast(bb); int cmp = 0; switch (sortby) { default: NOT_REACHED case labellist::by_name: { cmp = 0; break; } case labellist::by_koord: cmp = a->label_pos.x - b->label_pos.x; if(cmp==0) { cmp = a->label_pos.y - b->label_pos.y; } break; case labellist::by_player: { if(!filter) { const label_t* a_l = a->get_label(); const label_t* b_l = b->get_label(); if(a_l && b_l) { cmp = a_l->get_owner_nr() - b_l->get_owner_nr(); } } break; } } if(cmp==0) { const char* a_name = a->get_text(); const char* b_name = b->get_text(); cmp = strcmp(a_name, b_name); } return sortreverse ? cmp > 0 : cmp < 0; } labellist_stats_t::labellist_stats_t(koord label_pos) { this->label_pos = label_pos; set_table_layout(2,1); button_t *b = new_component(); b->set_typ(button_t::posbutton_automatic); b->set_targetpos(label_pos); add_component(&label); label.buf().printf("(%s) %s", label_pos.get_str(), get_text()); label.update(); if (const label_t *lb = get_label()) { if (lb->get_owner()) { label.set_color(PLAYER_FLAG | gfx->palette_lookup(lb->get_owner()->get_player_color1() + env_t::gui_player_color_dark)); } else { label.set_color(env_t::default_window_title_color); } } } const label_t* labellist_stats_t::get_label() const { if (grund_t *gr = welt->lookup_kartenboden(label_pos)) { return gr->find(); } return NULL; } void labellist_stats_t::map_rotate90( sint16 y_size ) { label_pos.rotate90(y_size); } bool labellist_stats_t::is_valid() const { return get_label() != NULL; } const char* labellist_stats_t::get_text() const { if (grund_t *gr = welt->lookup_kartenboden(label_pos)) { return gr->get_text(); } return ""; } /** * Events are notified to GUI components via this method */ bool labellist_stats_t::infowin_event(const event_t * ev) { bool swallowed = gui_aligned_container_t::infowin_event(ev); if (!swallowed) { if (grund_t* gr = welt->lookup_kartenboden(label_pos)) { // either open dialog or goto (with control or right click) if (IS_LEFTRELEASE(ev)) { if ((event_get_last_control_shift() ^ tool_t::control_invert) == 2) { world()->get_viewport()->change_world_position(gr->get_pos()); } else { if (label_t* lb = gr->find()) { lb->show_info(); } } return true; } if (IS_RIGHTRELEASE(ev)) { world()->get_viewport()->change_world_position(gr->get_pos()); return true; } } } return swallowed; } /** * Draw the component */ void labellist_stats_t::draw(scr_coord offset) { label.buf().printf("(%s) %s", label_pos.get_str(), get_text()); gui_aligned_container_t::draw(offset); } simutrans-124.5/src/simutrans/gui/labellist_stats.h000066400000000000000000000020021520675460500225200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LABELLIST_STATS_H #define GUI_LABELLIST_STATS_H #include "components/gui_aligned_container.h" #include "components/gui_label.h" #include "components/gui_scrolled_list.h" namespace labellist { enum sort_mode_t { by_name = 0, by_koord, by_player, SORT_MODES }; }; class label_t; class labellist_stats_t : public gui_aligned_container_t, public gui_scrolled_list_t::scrollitem_t { private: koord label_pos; gui_label_buf_t label; const label_t* get_label() const; public: static labellist::sort_mode_t sortby; static bool sortreverse, filter; static bool compare(const gui_component_t *a, const gui_component_t *b ); labellist_stats_t(koord label_pos); bool infowin_event(event_t const*) OVERRIDE; /** * Draw the component */ void draw(scr_coord offset) OVERRIDE; void map_rotate90( sint16 ); bool is_valid() const OVERRIDE; const char* get_text() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/line_item.cc000066400000000000000000000104611520675460500214420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "simwin.h" #include "line_item.h" #include "../simline.h" #include "../dataobj/schedule.h" #include "../tool/simmenu.h" #include "../world/simworld.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" const char* line_scrollitem_t::get_text() const { return line->get_name(); } PIXVAL line_scrollitem_t::get_color() const { return line->get_state_color(); } void line_scrollitem_t::set_text(char const* const t) { if( t && t[0] && strcmp(t, line->get_name())) { // text changed => call tool cbuffer_t buf; buf.printf( "l%u,%s", line.get_id(), t ); tool_t *tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tool->set_default_param( buf ); karte_ptr_t welt; welt->set_tool( tool, line->get_owner() ); // since init always returns false, it is safe to delete immediately delete tool; } } void line_scrollitem_t::draw( scr_coord offset ) { bool old_selected = selected; if (select_mode == SELECT_WIN) { selected = win_get_magic( (ptrdiff_t)line.get_rep() ); } static char infotext[256]; if( getroffen( get_mouse_pos() - offset ) ) { char convoi_tmp[128]; const char * convoicount; // update convoi info switch( line->count_convoys() ) { case 0: convoicount = translator::translate( "no convois" ); break; case 1: convoicount = translator::translate( "1 convoi" ); break; default: sprintf( convoi_tmp, translator::translate( "%d convois" ), line->count_convoys() ); convoicount = convoi_tmp; break; } char profit_str[64]; money_to_string( profit_str, line->get_finance_history( 0, LINE_PROFIT )/100.0, true ); sprintf( infotext, "%s, %s %s", convoicount, translator::translate( "Gewinn" ), profit_str ); win_set_tooltip( get_mouse_pos() + TOOLTIP_MOUSE_OFFSET, infotext); } gui_scrolled_list_t::const_text_scrollitem_t::draw( offset ); selected = old_selected; } // static helper function for sorting lineintems line_scrollitem_t::sort_modes_t line_scrollitem_t::sort_mode = line_scrollitem_t::SORT_BY_NAME; bool line_scrollitem_t::sort_reverse; bool line_scrollitem_t::compare(const gui_component_t *aa, const gui_component_t *bb) { const line_scrollitem_t *a = dynamic_cast(aa); const line_scrollitem_t *b = dynamic_cast(bb); // for sorting in reverse order: exchange a,b if (sort_reverse) { std::swap(a,b); } // good luck with mixed lists assert(a != NULL && b != NULL); (void)(a==b); if( sort_mode != SORT_BY_NAME ) { switch( sort_mode ) { case SORT_BY_NAME: // default break; case SORT_BY_ID: return a->get_line().get_id() < b->get_line().get_id(); case SORT_BY_PROFIT: return a->get_line()->get_finance_history(1, LINE_PROFIT ) - b->get_line()->get_finance_history(1, LINE_PROFIT ) < 0; case SORT_BY_REVENUE: return a->get_line()->get_finance_history(1, LINE_REVENUE ) - b->get_line()->get_finance_history(1, LINE_REVENUE ) < 0; case SORT_BY_TRANSPORTED: return a->get_line()->get_finance_history(1, LINE_TRANSPORTED_GOODS) - b->get_line()->get_finance_history(1, LINE_TRANSPORTED_GOODS) < 0; case SORT_BY_CONVOIS: return a->get_line()->get_finance_history(1, LINE_CONVOIS) - b->get_line()->get_finance_history(1, LINE_CONVOIS) < 0; case SORT_BY_DISTANCE: // normalizing to the number of convoys to get the fastest ones ... return a->get_line()->get_finance_history(1, LINE_DISTANCE)/max(1,a->get_line()->get_finance_history(1, LINE_CONVOIS)) - b->get_line()->get_finance_history(1, LINE_DISTANCE)/max(1,b->get_line()->get_finance_history(1, LINE_CONVOIS)) < 0; default: break; } // default sorting ... } // first: try to sort by number const char *atxt = a->get_text(); int aint = 0; // isdigit produces with UTF8 assertions ... if( atxt[0]>='0' && atxt[0]<='9' ) { aint = atoi( atxt ); } else if( atxt[0]=='(' && atxt[1]>='0' && atxt[1]<='9' ) { aint = atoi( atxt+1 ); } const char *btxt = b->get_text(); int bint = 0; if( btxt[0]>='0' && btxt[0]<='9' ) { bint = atoi( btxt ); } else if( btxt[0]=='(' && btxt[1]>='0' && btxt[1]<='9' ) { bint = atoi( btxt+1 ); } if( aint!=bint ) { return aint - bint < 0; } // otherwise: sort by name return strcmp(atxt, btxt) < 0; } simutrans-124.5/src/simutrans/gui/line_item.h000066400000000000000000000030341520675460500213020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LINE_ITEM_H #define GUI_LINE_ITEM_H #include "components/gui_scrolled_list.h" #include "../linehandle.h" /** * Container for list entries - consisting of text and color */ class line_scrollitem_t : public gui_scrolled_list_t::const_text_scrollitem_t { public: // helper to sort enum sort_modes_t { SORT_BY_NAME = 0, SORT_BY_ID, SORT_BY_PROFIT, SORT_BY_REVENUE, SORT_BY_TRANSPORTED, SORT_BY_CONVOIS, SORT_BY_DISTANCE, MAX_SORT_MODES }; // selection mode enum select_modes_t { SELECT_ITEM, ///< show selected: if item is selected in list to apply line for convoy SELECT_WIN, ///< show selected: if line window is open }; static sort_modes_t sort_mode; static bool sort_reverse; // to update selected status void draw( scr_coord offset ) OVERRIDE; // normal items line_scrollitem_t( linehandle_t l, select_modes_t sm = SELECT_ITEM) : gui_scrolled_list_t::const_text_scrollitem_t( NULL, gfx->palette_lookup(COL_ORANGE) ), select_mode(sm) { line = l; } PIXVAL get_color() const OVERRIDE; linehandle_t get_line() const { return line; } char const* get_text() const OVERRIDE; void set_text(char const*) OVERRIDE; bool is_valid() const OVERRIDE { return line.is_bound(); } // can be used to indicate invalid entries bool is_editable() const OVERRIDE { return true; } static bool compare(const gui_component_t *a, const gui_component_t *b ); private: linehandle_t line; select_modes_t select_mode; }; #endif simutrans-124.5/src/simutrans/gui/line_management_gui.cc000066400000000000000000000364101520675460500234660ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simwin.h" #include "minimap.h" #include "components/gui_convoiinfo.h" #include "../dataobj/schedule.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../vehicle/vehicle.h" #include "../convoihandle.h" #include "../simconvoi.h" #include "../obj/depot.h" #include "../simhalt.h" #include "../simline.h" #include "../tool/simmenu.h" #include "../player/simplay.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include "halt_list_stats.h" #include "line_management_gui.h" #define CHART_HEIGHT (100) static const char *cost_type[MAX_LINE_COST] = { "Free Capacity", "Transported", "Revenue", "Operation", "Road toll", "Profit", "Convoys", "Distance", "Maxspeed" }; static const uint8 cost_type_color[MAX_LINE_COST] = { COL_FREE_CAPACITY, COL_TRANSPORTED, COL_REVENUE, COL_OPERATION, COL_TOLL, COL_PROFIT, COL_CONVOI_COUNT, COL_DISTANCE, COL_MAXSPEED }; static uint8 idx2cost[MAX_LINE_COST] = { LINE_CAPACITY, LINE_TRANSPORTED_GOODS, LINE_REVENUE, LINE_OPERATIONS, LINE_WAYTOLL, LINE_PROFIT, LINE_CONVOIS, LINE_DISTANCE, LINE_MAXSPEED, }; static const bool cost_type_money[ MAX_LINE_COST ] = { false, false, true, true, true, true, false, false, false }; // which buttons as defualt in statistic static bool cost_type_default[MAX_LINE_COST] = { 0,0,0,0,0,0,0,0,0 }; // statistics as first default to open static int default_opening_tab = 1; line_management_gui_t::line_management_gui_t( linehandle_t line_, player_t* player_, int active_tab) : gui_frame_t( translator::translate( "Fahrplan" ), player_ ), scrolly_convois( gui_scrolled_list_t::windowskin ), scrolly_halts( gui_scrolled_list_t::windowskin ), loading_info( &loading_text ) { is_saving_gui = false; set_table_layout( 3, 0 ); set_alignment(ALIGN_TOP); line = line_; player = player_; scrolly_convois.set_checkered(true); scrolly_halts.set_checkered(true); // line name inp_name.add_listener( this ); add_component( &inp_name, 3 ); lb_convoi_count.set_text( "no convois" ); add_component( &lb_convoi_count, 3 ); capacity_bar.add_color_value( &load, gfx->palette_lookup( COL_GREEN ) ); add_component( &capacity_bar, 2 ); new_component(); add_component( &lb_profit_value ); add_component( &loading_info, 2 ); loading_text.printf( translator::translate("Capacity: %s\nLoad: %d (%d%%)"), 0, 0, 0); // tab panel: connections, chart panels, details add_component( &switch_mode, 3 ); switch_mode.add_listener( this ); switch_mode.add_tab( &container_schedule, translator::translate( "Fahrplan" ) ); container_schedule.set_table_layout( 1, 0 ); container_schedule.add_component( &scd ); scd.add_listener( this ); switch_mode.add_tab( &container_stats, translator::translate( "Chart" ) ); container_stats.set_table_layout( D_BUTTONS_PER_ROW, 0 ); container_stats.set_force_equal_columns(true); chart.set_dimension( 12, 10000 ); chart.set_background( SYSCOL_CHART_BACKGROUND ); chart.set_min_size( scr_size( 0, CHART_HEIGHT ) ); container_stats.add_component( &chart, D_BUTTONS_PER_ROW); switch_mode.add_tab( &container_convois, translator::translate( "cl_title" ) ); container_convois.set_table_layout( 1, 0 ); container_convois.add_table( D_BUTTONS_PER_ROW, 0 )->set_force_equal_columns(D_BUTTONS_PER_ROW>=4); bt_delete_line.init( button_t::roundbox | button_t::flexible, "Delete Line" ); bt_delete_line.set_tooltip( "Delete the selected line (if without associated convois)." ); bt_delete_line.add_listener( this ); bt_delete_line.disable(); container_convois.add_component( &bt_delete_line ); bt_withdraw_line.init( button_t::roundbox_state | button_t::flexible, "Withdraw All" ); bt_withdraw_line.set_tooltip( "Convoi is sold when all wagons are empty." ); bt_withdraw_line.add_listener( this ); container_convois.add_component( &bt_withdraw_line ); bt_find_convois.init( button_t::roundbox | button_t::flexible, "Find matching convois" ); bt_find_convois.set_tooltip( "Add convois with similar schedule to this line." ); bt_find_convois.add_listener( this ); container_convois.add_component( &bt_find_convois ); container_convois.end_table(); container_convois.add_component(&scrolly_convois); switch_mode.add_tab(&container_halts, translator::translate("hl_title")); container_halts.set_table_layout(1,0); scrolly_halts.set_maximize( true ); container_halts.add_component(&scrolly_halts); if (active_tab < 0) { active_tab = default_opening_tab; } switch_mode.set_active_tab_index(active_tab); if (line.is_bound() ) { init(true); } old_convoi_count = old_halt_count = 0; set_resizemode(diagonal_resize); reset_min_windowsize(); set_windowsize(get_windowsize()); } void line_management_gui_t::init(bool not_rdwr) { if( line.is_bound() ) { // title set_name(line->get_name() ); // schedule scd.init(not_rdwr?line->get_schedule():scd.get_schedule(), player, convoihandle_t(), linehandle_t(),true); // we use local buffer to prevent sudden death on line deletion tstrncpy(old_line_name, line->get_name(), sizeof(old_line_name)); tstrncpy(line_name, line->get_name(), sizeof(line_name)); inp_name.set_text(line_name, sizeof(line_name)); bt_delete_line.enable(); bt_withdraw_line.pressed = line->get_withdraw(); // init_chart if( chart.get_curve_count() == 0 ) { for( int cost = 0; cost < MAX_LINE_COST; cost++ ) { uint16 curve = chart.add_curve( gfx->palette_lookup( cost_type_color[ cost ] ), line->get_finance_history(), MAX_LINE_COST, idx2cost[cost], MAX_MONTHS, cost_type_money[ cost ], cost_type_default[cost], true, cost_type_money[ cost ] * 2 ); button_t *b = container_stats.new_component(); b->init( button_t::box_state_automatic | button_t::flexible, cost_type[ cost ] ); b->background_color = gfx->palette_lookup( cost_type_color[ cost ] ); b->pressed = cost_type_default[cost]; b->add_listener( this ); button_to_chart.append( b, &chart, curve ); } old_convoi_count = -1; // recalc! } // start editing scd.highlight_schedule(switch_mode.get_aktives_tab() == &container_schedule); if (line->count_convoys() > 0) { minimap_t::get_instance()->set_selected_cnv(line->get_convoy(0)); } } } void line_management_gui_t::draw(scr_coord pos, scr_size size) { if( line.is_bound() ) { player_t *ap = welt->get_active_player(); bool is_change_allowed = player == ap && !welt->get_active_player()->is_locked(); bool has_changed = false; // then we need to recalc sizes ... if (!is_change_allowed) { bt_delete_line.enable(false); } bt_withdraw_line.enable(is_change_allowed); bt_find_convois.enable(is_change_allowed); if( line->count_convoys() != old_convoi_count ) { old_convoi_count = line->count_convoys(); // update convoi info switch( old_convoi_count ) { case 0: lb_convoi_count.set_text( "no convois" ); break; case 1: lb_convoi_count.set_text( "1 convoi" ); break; default: lb_convoi_count_text.clear(); lb_convoi_count_text.printf( translator::translate( "%d convois" ), old_convoi_count ); lb_convoi_count.set_text( lb_convoi_count_text ); break; } bt_withdraw_line.enable(is_change_allowed && old_convoi_count != 0 ); // fill convoi container scrolly_convois.clear_elements(); for( uint32 i = 0; i < line->count_convoys(); i++ ) { convoihandle_t cnv = line->get_convoy( i ); scrolly_convois.new_component( cnv ); } has_changed = true; if( old_convoi_count>0 ) { scrolly_convois.set_maximize( true ); } } capacity = 0; load = 0; for( uint32 i = 0; i < line->count_convoys(); i++ ) { convoihandle_t cnv = line->get_convoy( i ); for (unsigned i = 0; iget_vehicle_count(); i++) { capacity += cnv->get_vehicle(i)->get_cargo_max(); load += cnv->get_vehicle( i )->get_total_cargo(); } } capacity_bar.set_base( capacity ); // we check if cap is zero, since theoretically a // conv can consist of only 1 vehicle, which has no cap (eg. locomotive) // and we do not like to divide by zero, do we? sint32 loadfactor = 0; if (capacity > 0) { loadfactor = (load * 100) / capacity; } char ctmp[ 20 ]; number_to_string(ctmp, capacity, 2); loading_text.clear(); loading_text.printf( translator::translate("Capacity: %s\nLoad: %d (%d%%)"), ctmp, load, loadfactor ); char profit_str[64]; money_to_string( profit_str, line->get_finance_history( 0, LINE_PROFIT )/100.0, true ); lb_profit_value_text.clear(); lb_profit_value_text.printf( "%s %s", translator::translate("Gewinn"), profit_str ); lb_profit_value.set_text_pointer( lb_profit_value_text ); lb_profit_value.set_color( line->get_finance_history( 0, LINE_PROFIT ) >= 0 ? MONEY_PLUS : MONEY_MINUS ); if( line->get_schedule()->get_count() != old_halt_count ) { // update halt info old_halt_count = line->get_schedule()->get_count(); bool allow_water = line->get_schedule()->get_waytype() == water_wt; // fill haltestellen container with info of stops of the line scrolly_halts.clear_elements(); for(schedule_entry_t const& i : line->get_schedule()->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, player, allow_water); if( halt.is_bound() ) { scrolly_halts.new_component(halt); } } has_changed = true; } if( has_changed ) { reset_min_windowsize(); } bt_withdraw_line.enable( is_change_allowed ); if( strcmp( get_name(), line->get_name() ) ) { set_name( line->get_name() ); welt->set_dirty(); } } else { destroy_win( this ); } gui_frame_t::draw(pos,size); } void line_management_gui_t::rdwr(loadsave_t *file) { sint32 cont_xoff, cont_yoff; sint32 halt_xoff, halt_yoff; uint8 player_nr; is_saving_gui = file->is_saving(); if( file->is_saving() ) { cont_xoff = scrolly_convois.get_scroll_x(); cont_yoff = scrolly_convois.get_scroll_y(); halt_xoff = scrolly_halts.get_scroll_x(); halt_yoff = scrolly_halts.get_scroll_y(); player_nr = player->get_player_nr(); } scr_size size = get_windowsize(); size.rdwr( file ); file->rdwr_long( cont_xoff ); file->rdwr_long( cont_yoff ); file->rdwr_long( halt_xoff ); file->rdwr_long( halt_yoff ); file->rdwr_byte( player_nr ); file->rdwr_str( old_line_name, lengthof( old_line_name ) ); file->rdwr_str( line_name, lengthof( line_name ) ); simline_t::rdwr_linehandle_t(file, line); scd.rdwr( file ); if( file->is_loading() ) { player = welt->get_player( player_nr ); gui_frame_t::set_owner(player); if( line.is_bound() ) { set_windowsize(size); set_windowsize( size ); win_set_magic(this, (ptrdiff_t)line.get_rep()); scrolly_convois.set_scroll_position( cont_xoff, cont_yoff ); scrolly_halts.set_scroll_position( halt_xoff, halt_yoff ); init(false); } else { line = linehandle_t(); destroy_win( this ); dbg->error( "line_management_gui_t::rdwr", "Could not restore schedule window for line id %i", line.get_id() ); } } switch_mode.rdwr( file ); button_to_chart.rdwr( file ); } void line_management_gui_t::apply_schedule() { if( scd.has_pending_changes() && line.is_bound() && (player == welt->get_active_player() || welt->get_active_player()->is_public_service()) ) { // update line schedule via tool! tool_t *tool = create_tool( TOOL_CHANGE_LINE | SIMPLE_TOOL ); cbuffer_t buf; buf.printf( "g,%i,", line.get_id() ); scd.get_schedule()->sprintf_schedule( buf ); tool->set_default_param( buf ); world()->set_tool( tool, line->get_owner() ); // since init always returns false, it is safe to delete immediately delete tool; } } bool line_management_gui_t::action_triggered( gui_action_creator_t *comp, value_t v ) { if(line->count_convoys()>0) { minimap_t::get_instance()->set_selected_cnv(line->get_convoy(0)); } if( comp == &scd ) { if( !v.p ) { // revert scd.init( line->get_schedule(), player, convoihandle_t(), line ); reset_min_windowsize(); } return true; } else if( comp == &switch_mode ) { bool edit_schedule = switch_mode.get_aktives_tab() == &container_schedule; if( edit_schedule ) { scd.init( line->get_schedule(), player, convoihandle_t(), line ); scd.highlight_schedule( true ); reset_min_windowsize(); } else { default_opening_tab = v.i; scd.highlight_schedule( false ); apply_schedule(); } } else if( comp == &inp_name ) { rename_line(); } else if( comp == &bt_delete_line ) { if( line.is_bound() ) { tool_t *tmp_tool = create_tool( TOOL_CHANGE_LINE | SIMPLE_TOOL ); cbuffer_t buf; buf.printf( "d,%i", line.get_id() ); tmp_tool->set_default_param(buf); welt->set_tool( tmp_tool, player ); // since init always returns false, it is safe to delete immediately delete tmp_tool; depot_t::update_all_win(); } } else if( comp == &bt_withdraw_line ) { bt_withdraw_line.pressed ^= 1; if ( line.is_bound() ) { tool_t *tmp_tool = create_tool( TOOL_CHANGE_LINE | SIMPLE_TOOL ); cbuffer_t buf; buf.printf( "w,%i,%i", line.get_id(), bt_withdraw_line.pressed ); tmp_tool->set_default_param(buf); welt->set_tool( tmp_tool, player ); // since init always returns false, it is safe to delete immediately delete tmp_tool; } } else if( comp == &bt_find_convois ) { for(convoihandle_t cnv : welt->convoys()) { if( cnv->get_owner()==player ) { if( !cnv->get_line().is_bound() ) { if( line->get_schedule()->matches( welt, cnv->get_schedule() ) ) { // same schedule, and no line =< add to line char id[16]; sprintf(id, "%i,%i", line.get_id(), cnv->get_schedule()->get_current_stop()); cnv->call_convoi_tool('l', id); } } } } } else { const vector_tpl &l = button_to_chart.list(); for( uint32 i = 0; iget_button() ) { cost_type_default[i] = l[i]->get_button()->pressed; break; } } } return false; } void line_management_gui_t::rename_line() { if( line.is_bound() ) { const char *t = inp_name.get_text(); // only change if old name and current name are the same // otherwise some unintended undo if renaming would occur if( t && t[0] && strcmp(t, line->get_name()) && strcmp(old_line_name, line->get_name())==0 ) { // text changed => call tool cbuffer_t buf; buf.printf( "l%u,%s", line.get_id(), t ); tool_t *tmp_tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tmp_tool->set_default_param( buf ); welt->set_tool( tmp_tool, line->get_owner() ); // since init always returns false, it is safe to delete immediately delete tmp_tool; // do not trigger this command again tstrncpy(old_line_name, t, sizeof(old_line_name)); } } } bool line_management_gui_t::infowin_event( const event_t *ev ) { if( ev->ev_class == INFOWIN && ev->ev_code == WIN_CLOSE ) { if( switch_mode.get_aktives_tab() == &container_schedule && !is_saving_gui ) { apply_schedule(); } scd.highlight_schedule( false ); minimap_t::get_instance()->set_selected_cnv(convoihandle_t()); } if( ev->ev_class == INFOWIN && ev->ev_code == WIN_TOP ) { if( switch_mode.get_aktives_tab() == &container_schedule ) { scd.highlight_schedule( true ); } if (line->count_convoys() > 0) { minimap_t::get_instance()->set_selected_cnv(line->get_convoy(0)); } } is_saving_gui = false; return gui_frame_t::infowin_event( ev ); } simutrans-124.5/src/simutrans/gui/line_management_gui.h000066400000000000000000000044141520675460500233270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LINE_MANAGEMENT_GUI_H #define GUI_LINE_MANAGEMENT_GUI_H #include "simwin.h" #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_button_to_chart.h" #include "components/gui_chart.h" #include "components/gui_label.h" #include "components/gui_schedule.h" #include "components/gui_scrolled_list.h" #include "components/gui_speedbar.h" #include "components/gui_tab_panel.h" #include "components/gui_textarea.h" #include "components/gui_textinput.h" #include "../linehandle.h" class player_t; class loadsave_t; class line_management_gui_t : public gui_frame_t, public action_listener_t { linehandle_t line; gui_textinput_t inp_name; gui_label_t lb_convoi_count; cbuffer_t lb_convoi_count_text; gui_speedbar_t capacity_bar; cbuffer_t lb_profit_value_text; gui_label_t lb_profit_value; button_t bt_delete_line; gui_tab_panel_t switch_mode; gui_schedule_t scd; gui_chart_t chart; gui_button_to_chart_array_t button_to_chart; button_t bt_withdraw_line, bt_find_convois; gui_scrolled_list_t scrolly_convois, scrolly_halts; gui_aligned_container_t container_schedule, container_stats, container_convois, container_halts; cbuffer_t loading_text; gui_textarea_t loading_info; player_t *player; // so even japanese can have long enough names ... char line_name[512], old_line_name[512]; // rename selected line // checks if possible / necessary void rename_line(); uint32 old_convoi_count, old_halt_count; sint32 capacity, load; // only true if the window is closing due to finishign a game. The changes will not apply! bool is_saving_gui; void init(bool); void apply_schedule(); public: line_management_gui_t(linehandle_t line = linehandle_t(), player_t* player_ = NULL, int active_tab=0 ); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void draw(scr_coord pos, scr_size size) OVERRIDE; // stuff for UI saving void rdwr( loadsave_t *file ) OVERRIDE; const char *get_help_filename() const OVERRIDE { return "linedetails.txt"; } bool infowin_event( const event_t *ev ) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_line_schedule_rdwr_dummy; } }; #endif simutrans-124.5/src/simutrans/gui/load_relief_frame.cc000066400000000000000000000104151520675460500231130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../world/simworld.h" #include "load_relief_frame.h" #include "welt.h" #include "simwin.h" #include "../dataobj/translator.h" #include "../dataobj/settings.h" #include "../dataobj/environment.h" #include "../dataobj/height_map_loader.h" #include "components/gui_scrolled_list.h" static const char *load_mode_texts[env_t::NUM_HEIGHT_CONV_MODES] = { "legacy (small heights)", "legacy (large heights)", "linear", "clamp" }; class load_relief_mode_scrollitem_t : public gui_scrolled_list_t::const_text_scrollitem_t { public: load_relief_mode_scrollitem_t(env_t::height_conversion_mode mode) : gui_scrolled_list_t::const_text_scrollitem_t(translator::translate(load_mode_texts[(int)mode]), SYSCOL_TEXT), mode(mode) {} env_t::height_conversion_mode get_mode() const { return mode; } private: env_t::height_conversion_mode mode; }; /** * Action, started on button pressing */ bool load_relief_frame_t::item_action(const char *fullpath) { sets->heightfield = fullpath; if (gui_frame_t *new_world_gui = win_get_magic( magic_welt_gui_t )) { const gui_scrolled_list_t::scrollitem_t *selected = load_mode.get_selected_item(); if (selected) { env_t::height_conv_mode = static_cast(selected)->get_mode(); } static_cast(new_world_gui)->update_preview(true); } return false; } load_relief_frame_t::load_relief_frame_t(settings_t* const sets) : savegame_frame_t( NULL, false, "maps/", true ) { gui_aligned_container_t *table = bottom_left_frame.add_table(2, 1); load_mode_label.init(translator::translate("Load mode:"), scr_coord(0, 0)); load_mode_label.set_visible(true); table->add_component( &load_mode_label ); load_mode.init(scr_coord(0, 0)); load_mode.new_component(env_t::HEIGHT_CONV_LEGACY_SMALL); load_mode.new_component(env_t::HEIGHT_CONV_LEGACY_LARGE); load_mode.new_component(env_t::HEIGHT_CONV_LINEAR); load_mode.new_component(env_t::HEIGHT_CONV_CLAMP); load_mode.set_selection(env_t::HEIGHT_CONV_LINEAR); load_mode.add_listener(this); table->add_component( &load_mode ); bottom_left_frame.end_table(); const std::string extra_path = env_t::pak_dir + "maps/"; this->add_path(extra_path.c_str()); set_name(translator::translate("Lade Relief")); this->sets = sets; sets->heightfield = ""; } const char *load_relief_frame_t::get_info(const char *fullpath) { static char size[64]; sint16 w, h; sint8 *h_field = NULL; const sint8 min_h = world()->get_settings().get_minimumheight(); const sint8 max_h = world()->get_settings().get_maximumheight(); const gui_scrolled_list_t::scrollitem_t *selected = load_mode.get_selected_item(); env_t::height_conversion_mode new_mode = env_t::height_conv_mode; if (selected) { new_mode = static_cast(selected)->get_mode(); } height_map_loader_t hml(min_h, max_h, new_mode); if(hml.get_height_data_from_file(fullpath, (sint8)sets->get_groundwater(), h_field, w, h, true )) { sprintf( size, "%i x %i", w, h ); env_t::height_conv_mode = new_mode; return size; } return ""; } bool load_relief_frame_t::check_file( const char *fullpath, const char * ) { const sint8 min_h = world()->get_settings().get_minimumheight(); const sint8 max_h = world()->get_settings().get_maximumheight(); const gui_scrolled_list_t::scrollitem_t *selected = load_mode.get_selected_item(); env_t::height_conversion_mode new_mode = env_t::height_conv_mode; if (selected) { new_mode = static_cast(selected)->get_mode(); } height_map_loader_t hml(min_h, max_h, env_t::height_conv_mode); sint16 w, h; sint8 *h_field = NULL; if(hml.get_height_data_from_file(fullpath, (sint8)sets->get_groundwater(), h_field, w, h, true )) { env_t::height_conv_mode = new_mode; return w>0 && h>0; } return false; } bool load_relief_frame_t::action_triggered(gui_action_creator_t* comp, value_t extra) { if (comp == &load_mode) { return item_action(sets->heightfield.c_str()); } else { return savegame_frame_t::action_triggered(comp, extra); } } simutrans-124.5/src/simutrans/gui/load_relief_frame.h000066400000000000000000000015741520675460500227630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LOAD_RELIEF_FRAME_H #define GUI_LOAD_RELIEF_FRAME_H #include "components/gui_combobox.h" #include "savegame_frame.h" class settings_t; class load_relief_frame_t : public savegame_frame_t { private: settings_t* sets; gui_label_t load_mode_label; gui_combobox_t load_mode; protected: bool item_action(const char *fullpath) OVERRIDE; const char *get_info(const char *fullpath) OVERRIDE; bool check_file(const char *fullpath, const char *suffix) OVERRIDE; public: /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE { return "load_relief.txt"; } load_relief_frame_t(settings_t*); bool action_triggered(gui_action_creator_t *comp, value_t extra) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/loadfont_frame.cc000066400000000000000000000152411520675460500224560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include #include #include #include "loadfont_frame.h" #include "../sys/simsys.h" #include "../world/simworld.h" #include "../simversion.h" #include "../pathes.h" #include "../utils/unicode.h" #include "../utils/searchfolder.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "gui_theme.h" #include "../utils/simstring.h" // static, since we keep them over reloading std::string loadfont_frame_t::old_fontname; uint8 loadfont_frame_t::old_linespace; bool loadfont_frame_t::use_unicode=true; bool loadfont_frame_t::is_resizable_font (const char *fontname) { const char *start_extension = strrchr(fontname, '.' ); return start_extension && STRICMP( start_extension, ".fnt" ) && STRICMP( start_extension, ".bdf" ); } /** * Action that's started with a button click */ bool loadfont_frame_t::item_action(const char *filename) { fontsize.enable( is_resizable_font(filename) ); win_load_font(filename, env_t::fontsize); return false; } bool loadfont_frame_t::ok_action(const char *filename) { item_action(filename); old_fontname.clear(); return true; } bool loadfont_frame_t::cancel_action(const char *) { win_load_font(old_fontname.c_str(), old_linespace); old_fontname.clear(); return true; } loadfont_frame_t::loadfont_frame_t() : savegame_frame_t(NULL,false,NULL,false) { set_name(translator::translate("Select display font")); use_unicode = true; // only try matching fonts top_frame.remove_component(&input); fontsize.init( env_t::fontsize, 6, 40, gui_numberinput_t::AUTOLINEAR, false ); fontsize.add_listener(this); fontsize.enable( is_resizable_font(env_t::fontname.c_str()) ); fnlabel.set_text( "font size" ); top_frame.add_component(&fontsize); unicode_only.init( button_t::square_automatic, "Only full Unicode fonts"); unicode_only.pressed = use_unicode; unicode_only.add_listener(this); top_frame.add_component(&unicode_only, 2); } const char *loadfont_frame_t::get_info(const char *fname) { return fname; } bool loadfont_frame_t::compare_items ( const dir_entry_t & entry, const char *info, const char *) { return (STRICMP(entry.info, info) > 0); } /** * CHECK FILE * Check if a file name qualifies to be added to the item list. */ bool loadfont_frame_t::check_file(const char *filename, const char *) { FILE *test = dr_fopen( filename, "r" ); if( test == NULL ) { return false; } fclose(test); // just match textension for buildin fonts const char *start_extension = strrchr(filename, '.' ); // we only show matching fonts for this language const utf8 *new_world = (const utf8 *)translator::translate("Beenden"); size_t len; utf16 testfor_this_character = utf8_decoder_t::decode(new_world, len); // no support for windows fon files, so we skip them to speed things up if( start_extension && !STRICMP( start_extension, ".fon" ) ) { return false; } #if COLOUR_DEPTH != 0 if( ft_library ) { // if we can open this font, it is probably ok ... FT_Face face; if( !FT_New_Face( ft_library, filename, 0, &face ) ) { // can load (no error returned) bool ok = false; if( FT_Get_Char_Index( face, '}' )!=0 && (STRICMP(face->style_name,"Regular")==0 || STRICMP(face->style_name,"Bold")==0) ) { // ok, we have at least charecter 126, and it is a regular font, so it is probably a valid font) ok = !use_unicode; if( FT_Get_Char_Index(face, testfor_this_character)!=0 ) { // the char NAMA does exist ok = true; // in pricipal we must also check if it can be rendered ... } } FT_Done_Face(face); return ok; } // next check for extension, might be still a valid font } #endif return false; } // parses the directory, using freetype lib, in installed void loadfont_frame_t::fill_list() { add_path( ((std::string)env_t::base_dir+"font/").c_str() ); #if COLOUR_DEPTH != 0 // ok, we can handle TTF fonts ft_library = NULL; if( FT_Init_FreeType(&ft_library) != FT_Err_Ok ) { ft_library = NULL; } else { const char *addpath; searchfolder_t subfolders; for( int i=0; ( addpath = dr_query_fontpath(i) ); i++ ) { add_path( addpath ); subfolders.search(addpath, "", searchfolder_t::SF_ONLYDIRS | searchfolder_t::SF_PREPEND_PATH, 4); for( const char * folder : subfolders ){ add_path( ((std::string) folder + PATH_SEPARATOR).c_str() ); } } } #endif if( old_fontname.empty() ) { old_fontname = env_t::fontname; old_linespace = env_t::fontsize; } // do the search ... savegame_frame_t::fill_list(); // mark current fonts for(dir_entry_t const& i : entries) { if (i.type == LI_HEADER) { continue; } i.button->set_typ(button_t::roundbox_state | button_t::flexible); #if COLOUR_DEPTH == 0 } #else // Use internal name instead the cutted file name if( ft_library ) { FT_Face face; if( FT_New_Face( ft_library, i.info, 0, &face )==FT_Err_Ok ) { if ( face->family_name ) { const size_t len = strlen(face->family_name) + 1 + (face->style_name ? strlen(face->style_name) + 1 : 0); char *name = new char[len]; strcpy( name, face->family_name ); if (face->style_name) { strcat( name, " "); strcat( name, face->style_name ); } delete [] const_cast(i.button->get_text()); i.button->set_text(name); i.button->pressed = strstr( env_t::fontname.c_str(), i.info ) != NULL; } FT_Done_Face( face ); } } } FT_Done_FreeType( ft_library ); ft_library = NULL; #endif // force new resize after we have rearranged the gui resize(scr_coord(0,0)); } void loadfont_frame_t::draw(scr_coord pos, scr_size size) { // mark current fonts for(dir_entry_t const& i : entries) { if (i.type == LI_HEADER) { continue; } i.button->pressed = strstr( env_t::fontname.c_str(), i.info ); } savegame_frame_t::draw(pos, size); } void loadfont_frame_t::rdwr( loadsave_t *file ) { file->rdwr_bool( unicode_only.pressed ); scr_size size = get_windowsize(); size.rdwr( file ); if( file->is_loading() ) { set_windowsize( size ); resize( scr_coord(0,0) ); } } bool loadfont_frame_t::action_triggered(gui_action_creator_t *component, value_t v) { if( &unicode_only==component ) { // send event, this will reload window event_t *ev = new event_t(); ev->ev_class = EVENT_SYSTEM; ev->ev_code = SYSTEM_RELOAD_WINDOWS; queue_event( ev ); use_unicode = unicode_only.pressed; return false; } if( &fontsize==component ) { win_load_font(env_t::fontname.c_str(), fontsize.get_value()); fontsize.set_limits(6, 40); return false; } return savegame_frame_t::action_triggered(component,v); } simutrans-124.5/src/simutrans/gui/loadfont_frame.h000066400000000000000000000034571520675460500223260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LOADFONT_FRAME_H #define GUI_LOADFONT_FRAME_H #if COLOUR_DEPTH != 0 #include "gui_theme.h" #include "../sys/simsys.h" #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_TRUETYPE_TABLES_H #endif #include "simwin.h" #include "savegame_frame.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_numberinput.h" #include "../tpl/stringhashtable_tpl.h" #include class loadfont_frame_t : public savegame_frame_t { private: #if COLOUR_DEPTH != 0 FT_Library ft_library; #endif static bool use_unicode; protected: static std::string old_fontname; static uint8 old_linespace; button_t unicode_only; gui_numberinput_t fontsize; bool is_resizable_font (const char *fontname); /** * Action that's started with a button click */ bool item_action (const char *filename) OVERRIDE; bool ok_action (const char *fullpath) OVERRIDE; bool cancel_action(const char *) OVERRIDE; // returns extra file info const char *get_info(const char *fname) OVERRIDE; // sort with respect to info, which is date bool compare_items ( const dir_entry_t & entry, const char *info, const char *) OVERRIDE; bool check_file( const char *filename, const char *suffix ) OVERRIDE; void fill_list() OVERRIDE; public: /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE { return "load_font.txt"; } loadfont_frame_t(); void draw(scr_coord pos, scr_size size) OVERRIDE; uint32 get_rdwr_id( void ) OVERRIDE { return magic_font; } void rdwr( loadsave_t *file ) OVERRIDE; bool action_triggered(gui_action_creator_t *, value_t v) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/loadsave_frame.cc000066400000000000000000000044321520675460500224460ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include #include "loadsave_frame.h" #include "../sys/simsys.h" #include "../world/simworld.h" #include "../simversion.h" #include "../pathes.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/sve_cache.h" #include "../network/network.h" #include "../network/network_cmd.h" #include "../network/network_cmd_ingame.h" #include "../network/network_socket_list.h" #include "../tool/simtool.h" #include "../utils/simstring.h" /** * Action that's started with a button click */ bool loadsave_frame_t::item_action(const char *filename) { if(do_load) { cbuffer_t param; param.printf("l%c%s", easy_server.pressed+'0', filename); tool_t::simple_tool[TOOL_WORK_WORLD]->set_default_param(param); welt->set_tool(tool_t::simple_tool[TOOL_WORK_WORLD], NULL); } else { cbuffer_t param; param.printf("s%s", filename); tool_t::simple_tool[TOOL_WORK_WORLD]->set_default_param(param); welt->set_tool(tool_t::simple_tool[TOOL_WORK_WORLD], NULL); } return true; } bool loadsave_frame_t::ok_action(const char *filename) { return item_action(filename); } loadsave_frame_t::loadsave_frame_t(bool do_load, bool back_to_menu) : savegame_frame_t(".sve",false,"save/",true, back_to_menu) { this->do_load = do_load; if(do_load) { set_name(translator::translate("Laden")); easy_server.init( button_t::square_automatic, "Start this as a server"); bottom_left_frame.add_component(&easy_server); } else { set_filename(welt->get_settings().get_filename()); set_name(translator::translate("Save")); } sve_cache_t::load_cache(); } const char *loadsave_frame_t::get_info(const char *fname) { return sve_cache_t::get_info(fname); } /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *loadsave_frame_t::get_help_filename() const { return do_load ? "load.txt" : "save.txt"; } loadsave_frame_t::~loadsave_frame_t() { // save hashtable sve_cache_t::write_cache(); } bool loadsave_frame_t::compare_items ( const dir_entry_t & entry, const char *info, const char *) { return (strcmp(entry.label->get_text_pointer(), info) < 0); } simutrans-124.5/src/simutrans/gui/loadsave_frame.h000066400000000000000000000021421520675460500223040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LOADSAVE_FRAME_H #define GUI_LOADSAVE_FRAME_H #include #include "savegame_frame.h" #include "../tpl/stringhashtable_tpl.h" #include class loadsave_t; class sve_info_t; class loadsave_frame_t : public savegame_frame_t { private: bool do_load; button_t easy_server; // only active on loading savegames protected: /** * Action that's started with a button click */ bool item_action (const char *filename) OVERRIDE; bool ok_action (const char *fullpath) OVERRIDE; // returns extra file info const char *get_info(const char *fname) OVERRIDE; // sort with respect to info, which is date bool compare_items ( const dir_entry_t & entry, const char *info, const char *) OVERRIDE; public: /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE; loadsave_frame_t(bool do_load, bool back_to_menu = false); /** * save hashtable to xml file */ ~loadsave_frame_t(); }; #endif simutrans-124.5/src/simutrans/gui/loadsoundfont_frame.cc000066400000000000000000000064331520675460500235320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include #include "loadsoundfont_frame.h" #include "../pathes.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../music/music.h" #include "../simsound.h" #include "gui_theme.h" #include "../utils/simstring.h" // static, since we keep them over reloading std::string loadsoundfont_frame_t::old_soundfontname; /** * Action that's started with a button click */ bool loadsoundfont_frame_t::item_action(const char *filename) { if( dr_load_sf( filename ) && midi_get_mute() ) { midi_set_mute( false ); midi_play( env_t::shuffle_midi ? -1 : 0 ); } return false; } bool loadsoundfont_frame_t::ok_action(const char *filename) { item_action( filename ); old_soundfontname.clear(); return true; } bool loadsoundfont_frame_t::cancel_action(const char *) { dr_load_sf( old_soundfontname.c_str() ); old_soundfontname.clear(); return true; } loadsoundfont_frame_t::loadsoundfont_frame_t() : savegame_frame_t(NULL,false,NULL,false) { set_name( translator::translate("Select soundfont") ); fnlabel.set_text("Soundfonts are located in the music directory."); top_frame.remove_component( &input ); } const char *loadsoundfont_frame_t::get_info(const char *sfname) { return sfname; } bool loadsoundfont_frame_t::compare_items ( const dir_entry_t & entry, const char *info, const char *) { return (STRICMP(entry.info, info) > 0); } /** * CHECK FILE * Check if a file name qualifies to be added to the item list. */ bool loadsoundfont_frame_t::check_file(const char *filename, const char *) { FILE *test = fopen( filename, "r" ); if( test == NULL ) { return false; } fclose( test ); // just match extension for building soundfonts const char *start_extension = strrchr(filename, '.' ); if( start_extension && ( !STRICMP( start_extension, ".sf2" ) || !STRICMP( start_extension, ".sf3" ) ) ) { return true; } return false; } // parses the directory void loadsoundfont_frame_t::fill_list() { add_path( ((std::string)env_t::base_dir + "music/").c_str() ); add_path( "/usr/share/soundfonts/" ); add_path( "/usr/share/sounds/sf2/" ); if( old_soundfontname.empty() ) { old_soundfontname = env_t::soundfont_filename; } // do the search ... savegame_frame_t::fill_list(); // mark current fonts for(dir_entry_t const& i : entries ) { if( i.type == LI_HEADER ) { continue; } i.button->set_typ( button_t::roundbox_state | button_t::flexible ); } // force new resize after we have rearranged the gui resize( scr_coord(0,0) ); } void loadsoundfont_frame_t::draw(scr_coord pos, scr_size size) { // mark current fonts for(dir_entry_t const& i : entries) { if( i.type == LI_HEADER ) { continue; } i.button->pressed = strstr( env_t::soundfont_filename.c_str(), i.info ); } savegame_frame_t::draw( pos, size ); } void loadsoundfont_frame_t::rdwr( loadsave_t *file ) { scr_size size = get_windowsize(); size.rdwr( file ); if( file->is_loading() ) { set_windowsize( size ); resize( scr_coord(0,0) ); } } bool loadsoundfont_frame_t::action_triggered(gui_action_creator_t *component, value_t v) { return savegame_frame_t::action_triggered( component, v ); } simutrans-124.5/src/simutrans/gui/loadsoundfont_frame.h000066400000000000000000000026361520675460500233750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_LOADSOUNDFONT_FRAME_H #define GUI_LOADSOUNDFONT_FRAME_H #include "simwin.h" #include "savegame_frame.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "../tpl/stringhashtable_tpl.h" #include class loadsoundfont_frame_t : public savegame_frame_t { protected: static std::string old_soundfontname; /** * Action that's started with a button click */ bool item_action (const char *filename) OVERRIDE; bool ok_action (const char *fullpath) OVERRIDE; bool cancel_action(const char *) OVERRIDE; // returns extra file info const char *get_info(const char *fname) OVERRIDE; // sort with respect to info, which is date bool compare_items ( const dir_entry_t & entry, const char *info, const char *) OVERRIDE; bool check_file( const char *filename, const char *suffix ) OVERRIDE; void fill_list() OVERRIDE; public: /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE { return "load_soundfont.txt"; } loadsoundfont_frame_t(); void draw(scr_coord pos, scr_size size) OVERRIDE; uint32 get_rdwr_id( void ) OVERRIDE { return magic_soundfont; } void rdwr( loadsave_t *file ) OVERRIDE; bool action_triggered(gui_action_creator_t *, value_t v) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/map_frame.cc000066400000000000000000000670161520675460500214340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "minimap.h" #include "map_frame.h" #include "simwin.h" #include "../sys/simsys.h" #include "../world/simworld.h" #include "../display/simgraph.h" #include "../display/viewport.h" #include "../simcolor.h" #include "../builder/fabrikbauer.h" #include "../builder/goods_manager.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../dataobj/koord.h" #include "../dataobj/loadsave.h" #include "../descriptor/factory_desc.h" #include "../simfab.h" #include "../player/finance.h" static koord old_ij=koord::invalid; karte_ptr_t map_frame_t::welt; scr_size map_frame_t::window_size; bool map_frame_t::legend_visible=false; bool map_frame_t::network_option_visible = false; bool map_frame_t::scale_visible=false; bool map_frame_t::directory_visible=false; bool map_frame_t::filter_factory_list=true; bool map_frame_t::zoomed = true; // we track our position onscreen scr_coord map_frame_t::screenpos; /** * Entries in factory legend: show color indicator + name */ class legend_entry_t : public gui_component_t { gui_label_t label; PIXVAL color; public: legend_entry_t(const char* text, PIXVAL c) : label(text), color(c) {} scr_size get_min_size() const OVERRIDE { return label.get_min_size() + scr_size(D_INDICATOR_BOX_WIDTH + D_H_SPACE, 0); } scr_size get_max_size() const OVERRIDE { return scr_size( scr_size::inf.w, label.get_max_size().h ); } void draw(scr_coord offset) OVERRIDE { scr_coord pos = get_pos() + offset; gfx->draw_rect_clipped( pos.x, pos.y+D_GET_CENTER_ALIGN_OFFSET(D_INDICATOR_BOX_HEIGHT,LINESPACE), D_INDICATOR_BOX_WIDTH, D_INDICATOR_BOX_HEIGHT, color, false CLIP_NUM_DEFAULT); label.draw( pos+scr_size(D_INDICATOR_BOX_WIDTH+D_H_SPACE,0) ); } }; /** * Show scale of severity-MAX_SEVERITY_COLORS */ class gui_scale_t : public gui_component_t { public: void draw(scr_coord offset) OVERRIDE { scr_coord pos = get_pos() + offset; double bar_width = (double)get_size().w/(double)MAX_SEVERITY_COLORS; // color bar for( int i=0; idraw_rect_clipped(pos.x + (scr_coord_val)(i*bar_width), pos.y+2, (scr_coord_val)bar_width+1, 7, minimap_t::calc_severity_color(i, MAX_SEVERITY_COLORS-1), false CLIP_NUM_DEFAULT); } } scr_size get_min_size() const OVERRIDE { return scr_size(0, 9); } scr_size get_max_size() const OVERRIDE { return scr_size( scr_size::inf.w, 9); } }; typedef struct { PIXVAL color; PIXVAL select_color; const char *button_text; const char *tooltip_text; minimap_t::MAP_DISPLAY_MODE mode; } map_button_t; map_button_t button_init[MAP_MAX_BUTTONS] = { { COL_LIGHT_GREEN, COL_DARK_GREEN, "Towns", "Overlay town names", minimap_t::MAP_TOWN }, { COL_LIGHT_GREEN, COL_DARK_GREEN, "CityLimit", "Overlay city limits", minimap_t::MAP_CITYLIMIT }, { COL_WHITE, COL_GREY5, "Buildings", "Show level of city buildings", minimap_t::MAP_LEVEL }, { COL_LIGHT_GREEN, COL_DARK_GREEN, "PaxDest", "Overlay passenger destinations when a town window is open", minimap_t::MAP_PAX_DEST }, { COL_LIGHT_GREEN, COL_DARK_GREEN, "Tourists", "Highlite tourist attraction", minimap_t::MAP_TOURIST }, { COL_LIGHT_GREEN, COL_DARK_GREEN, "Factories", "Highlite factories", minimap_t::MAP_FACTORIES }, { COL_LIGHT_YELLOW, COL_DARK_YELLOW, "Passagiere", "Show passenger coverage/passenger network", minimap_t::MAP_PASSENGER }, { COL_LIGHT_YELLOW, COL_DARK_YELLOW, "Post", "Show mail service coverage/mail network", minimap_t::MAP_MAIL }, { COL_LIGHT_YELLOW, COL_DARK_YELLOW, "Fracht", "Show transported freight/freight network", minimap_t::MAP_FREIGHT }, { COL_LIGHT_PURPLE, COL_DARK_PURPLE, "Status", "Show capacity and if halt is overcrowded", minimap_t::MAP_STATUS }, { COL_LIGHT_PURPLE, COL_DARK_PURPLE, "hl_btn_sort_waiting", "Show how many people/much is waiting at halts", minimap_t::MAP_WAITING }, { COL_LIGHT_PURPLE, COL_DARK_PURPLE, "Queueing", "Show the change of waiting at halts", minimap_t::MAP_WAITCHANGE }, { COL_LIGHT_PURPLE, COL_DARK_PURPLE, "Service", "Show how many convoi reach a station", minimap_t::MAP_SERVICE }, { COL_LIGHT_PURPLE, COL_DARK_PURPLE, "Transfers", "Sum of departure/arrivals at halts", minimap_t::MAP_TRANSFER }, { COL_LIGHT_PURPLE, COL_DARK_PURPLE, "Origin", "Show initial passenger departure", minimap_t::MAP_ORIGIN }, { COL_WHITE, COL_GREY5, "Traffic", "Show usage of network", minimap_t::MAP_TRAFFIC }, { COL_WHITE, COL_GREY5, "Speedlimit", "Show speedlimit of ways", minimap_t::MAX_SPEEDLIMIT }, { COL_WHITE, COL_GREY5, "Tracks", "Highlight railroad tracks", minimap_t::MAP_TRACKS }, { COL_LIGHT_GREEN, COL_DARK_GREEN, "Depots", "Highlite depots", minimap_t::MAP_DEPOT }, { COL_WHITE, COL_GREY5, "Powerlines", "Highlite electrical transmission lines", minimap_t::MAP_POWERLINES }, { COL_WHITE, COL_GREY5, "Forest", "Highlite forests", minimap_t::MAP_FOREST }, { COL_WHITE, COL_GREY5, "Ownership", "Show the owenership of infrastructure", minimap_t::MAP_OWNER } }; // tiny helper function bool map_frame_t::zoom(bool magnify) { if (minimap_t::get_instance()->change_zoom_factor(magnify)) { map_frame_t::zoomed = true; // recalculate scroll bar width scrolly.set_size(scrolly.get_size()); // invalidate old offsets old_ij = koord::invalid; return true; } return false; } map_frame_t::map_frame_t() : gui_frame_t( translator::translate("Reliefkarte") ), karte(minimap_t::get_instance()), scrolly(karte) { // init statics old_ij = koord::invalid; zoomed = false; karte->init(); karte->set_display_mode( ( minimap_t::MAP_DISPLAY_MODE)env_t::default_mapmode ); // show all players by default karte->player_showed_on_map = -1; // init map scrolly.set_maximize(true); scrolly.set_min_width(D_DEFAULT_WIDTH - D_MARGIN_LEFT - D_MARGIN_RIGHT); // map the current position visible const koord ij = welt->get_viewport()->get_world_position(); const scr_size size = karte->get_size(); const scr_size s_size = scrolly.get_size(); const scr_size win_size = size - s_size; // this is the visible area scrolly.set_scroll_position( clamp(ij.x-win_size.w/2, 0, size.w), clamp(ij.y-win_size.h/2, 0, size.h) ); scrolly.set_focusable( true ); scrolly.set_scrollbar_mode(scrollbar_t::show_always); set_table_layout(1,0); // first row of controls add_table(D_BUTTONS_PER_ROW,0); { // first row of controls b_show_network_option.init(button_t::roundbox_state, "Show networks"); b_show_network_option.set_tooltip("Shows buttons on network overlay."); b_show_network_option.set_size(D_BUTTON_SIZE); b_show_network_option.add_listener(this); add_component(&b_show_network_option); // selections button b_show_legend.init(button_t::roundbox_state, "Show legend"); b_show_legend.set_tooltip("Shows buttons on special topics."); b_show_legend.add_listener(this); add_component(&b_show_legend); // industry list button b_show_directory.init(button_t::roundbox_state, "Show industry"); b_show_directory.set_tooltip("Shows a listing with all industries on the map."); b_show_directory.add_listener(this); add_component(&b_show_directory); // scale button b_show_scale.init(button_t::roundbox_state, "Show map scale"); b_show_scale.set_tooltip("Shows the color code for several selections."); b_show_scale.add_listener(this); add_component(&b_show_scale); } end_table(); // second row of controls if (D_BUTTONS_PER_ROW >= 4) { zoom_row = add_table(0, 1); { // zoom levels label new_component("map zoom"); // zoom levels arrow left zoom_buttons[0].init(button_t::repeatarrowleft, NULL); zoom_buttons[0].add_listener(this); add_component(zoom_buttons + 0); // zoom level value label sint16 zoom_in, zoom_out; minimap_t::get_instance()->get_zoom_factors(zoom_out, zoom_in); zoom_value_label.buf().printf("%i:%i", zoom_in, zoom_out); zoom_value_label.update(); add_component(&zoom_value_label); // zoom levels arrow right zoom_buttons[1].init(button_t::repeatarrowright, NULL); zoom_buttons[1].add_listener(this); add_component(zoom_buttons + 1); } } else { // narrow screen zoom_row = add_table(0, 1); { // zoom levels label new_component("map zoom"); // zoom levels arrow left zoom_buttons[0].init(button_t::repeatarrowleft, NULL); zoom_buttons[0].add_listener(this); add_component(zoom_buttons + 0); // zoom level value label sint16 zoom_in, zoom_out; minimap_t::get_instance()->get_zoom_factors(zoom_out, zoom_in); zoom_value_label.buf().printf("%i:%i", zoom_in, zoom_out); zoom_value_label.update(); add_component(&zoom_value_label); // zoom levels arrow right zoom_buttons[1].init(button_t::repeatarrowright, NULL); zoom_buttons[1].add_listener(this); add_component(zoom_buttons + 1); new_component(); } end_table(); add_table(0,1); } { // rotate map 45 degrees (isometric view) b_rotate45.init( button_t::square_state, "isometric map"); b_rotate45.set_tooltip("Similar view as the main window"); b_rotate45.add_listener(this); b_rotate45.pressed = karte->is_isometric(); add_component(&b_rotate45); // show contour c_show_outlines.new_component( translator::translate( "Show contour" ), SYSCOL_TEXT ); c_show_outlines.new_component( translator::translate( "Show climates" ), SYSCOL_TEXT ); c_show_outlines.new_component( translator::translate( "Show outline" ), SYSCOL_TEXT ); c_show_outlines.add_listener( this ); if( (env_t::default_mapmode & minimap_t::MAP_CLIMATES) != 0 ) { c_show_outlines.set_selection( 1 ); } else if( (env_t::default_mapmode & minimap_t::MAP_HIDE_CONTOUR) != 0 ) { c_show_outlines.set_selection( 2 ); } else { c_show_outlines.set_selection( 0 ); } add_component(&c_show_outlines); new_component(); } end_table(); // networks filter container network_filter_container.set_visible(false); add_component(&network_filter_container); if (D_BUTTONS_PER_ROW == 4) { network_filter_container.set_table_layout(0, 1); } else { network_filter_container.set_table_layout(1, 0); } network_filter_container.add_table(0, 1); // insert selections: show networks, in filter container b_overlay_networks.init(button_t::square_state, "Networks"); b_overlay_networks.set_tooltip("Overlay schedules/network"); b_overlay_networks.add_listener(this); b_overlay_networks.pressed = (env_t::default_mapmode & minimap_t::MAP_LINES)!=0; network_filter_container.add_component( &b_overlay_networks ); // player combo for network overlay viewed_player_c.new_component(translator::translate("All"), SYSCOL_TEXT); viewable_players[ 0 ] = -1; for( int np = 0, count = 1; np < MAX_PLAYER_COUNT; np++ ) { if( welt->get_player( np ) && welt->get_player( np )->get_finance()->has_convoi()) { viewed_player_c.new_component(welt->get_player( np )->get_name(), gfx->palette_lookup(welt->get_player( np )->get_player_color1()+env_t::gui_player_color_dark)); viewable_players[ count++ ] = np; } } viewed_player_c.set_selection(0); viewed_player_c.set_focusable( true ); viewed_player_c.add_listener( this ); network_filter_container.add_component(&viewed_player_c); network_filter_container.end_table(); // freight combo for network overlay network_filter_container.add_table(0, 1); { viewable_freight_types.append(NULL); freight_type_c.new_component( translator::translate("All"), SYSCOL_TEXT) ; viewable_freight_types.append(goods_manager_t::passengers); freight_type_c.new_component( translator::translate("Passagiere"), SYSCOL_TEXT) ; viewable_freight_types.append(goods_manager_t::mail); freight_type_c.new_component( translator::translate("Post"), SYSCOL_TEXT) ; viewable_freight_types.append(goods_manager_t::none); // for all freight ... freight_type_c.new_component( translator::translate("Fracht"), SYSCOL_TEXT) ; for( int i = 0; i < goods_manager_t::get_max_catg_index(); i++ ) { const goods_desc_t *freight_type = goods_manager_t::get_info_catg(i); const int index = freight_type->get_catg_index(); if( index == goods_manager_t::INDEX_NONE || freight_type->get_catg()==0 ) { continue; } freight_type_c.new_component(translator::translate(freight_type->get_catg_name()), SYSCOL_TEXT); viewable_freight_types.append(freight_type); } for( int i=0; i < goods_manager_t::get_count(); i++ ) { const goods_desc_t *ware = goods_manager_t::get_info(i); if( ware->get_catg() == 0 && ware->get_index() > 2 ) { // Special freight: Each good is special viewable_freight_types.append(ware); freight_type_c.new_component( translator::translate(ware->get_name()), SYSCOL_TEXT) ; } } } freight_type_c.set_selection(0); minimap_t::get_instance()->freight_type_group_index_showed_on_map = NULL; freight_type_c.set_focusable( true ); freight_type_c.add_listener( this ); network_filter_container.add_component(&freight_type_c); // mode of transport combo for network overlay for (int i = 0; i < simline_t::MAX_LINE_TYPE; i++) { transport_type_c.new_component(simline_t::get_linetype_name((simline_t::linetype)i), SYSCOL_TEXT); } transport_type_c.set_selection(0); minimap_t::get_instance()->transport_type_showed_on_map = simline_t::line; transport_type_c.set_focusable( true ); transport_type_c.add_listener( this ); network_filter_container.add_component(&transport_type_c); b_overlay_networks_load_factor.init(button_t::square_state, "Free Capacity"); b_overlay_networks_load_factor.set_tooltip("Color according to transport capacity left"); b_overlay_networks_load_factor.add_listener(this); b_overlay_networks_load_factor.pressed = 0; minimap_t::get_instance()->show_network_load_factor = 0; network_filter_container.add_component( &b_overlay_networks_load_factor ); network_filter_container.end_table(); // filter container filter_container.set_visible(false); add_component(&filter_container); filter_container.set_table_layout(1, 0); int w = network_filter_container.get_min_size().w; const int FILTER_BUTTONS_PER_ROW = max(3, min(5, max(gfx->get_screen_size().w, w) / (D_BUTTON_WIDTH + D_H_SPACE + D_MARGIN_LEFT))); filter_container.add_table(FILTER_BUTTONS_PER_ROW,0)->set_force_equal_columns(true); // insert filter buttons in legend container for (int index=0; index("min"); scale_container.new_component(); scale_container.new_component("max"); // map scrolly scrolly.set_show_scroll_x(true); scrolly.set_scroll_discrete_y(false); add_component(&scrolly); // restore window size and options show_hide_legend( legend_visible ); show_hide_network_option( network_option_visible ); show_hide_scale( scale_visible ); show_hide_directory( directory_visible ); reset_min_windowsize(); set_windowsize( window_size ); set_resizemode(diagonal_resize); } void map_frame_t::update_buttons() { for( int i=0; ipalette_lookup(filter_buttons[i].pressed ? button_init[i].select_color : button_init[i].color); } } static bool compare_factories(const factory_desc_t *const a, const factory_desc_t *const b) { const bool a_producer_only = a->get_supplier_count() == 0; const bool b_producer_only = b->get_supplier_count() == 0; const bool a_consumer_only = a->get_product_count() == 0; const bool b_consumer_only = b->get_product_count() == 0; if (a_producer_only != b_producer_only) { return a_producer_only; // producers to the front } else if (a_consumer_only != b_consumer_only) { return !a_consumer_only; // consumers to the end } else { // both of same type, sort by name return strcmp(translator::translate(a->get_name()), translator::translate(b->get_name())) < 0; } } void map_frame_t::update_factory_legend() { directory_container.remove_all(); directory_container.add_component( &b_filter_factory_list, 4 ); if( directory_visible ) { vector_tpl factory_types; // generate list of factory types if( filter_factory_list ) { for(fabrik_t* const f : welt->get_fab_list()) { if( f->get_desc()->get_distribution_weight() > 0 ) { factory_types.insert_unique_ordered(f->get_desc(), compare_factories); } } } else { for(auto i : factory_builder_t::get_factory_table()) { factory_desc_t const* const d = i.value; if (d->get_distribution_weight() > 0) { factory_types.insert_unique_ordered(d, compare_factories); } } } // now sort // add corresponding legend entries for(const factory_desc_t* f : factory_types) { directory_container.new_component(f->get_name(), f->get_color()); } } } void map_frame_t::show_hide_legend(const bool show) { filter_container.set_visible(show); b_show_legend.pressed = show; legend_visible = show; reset_min_windowsize(); } void map_frame_t::show_hide_network_option(const bool show) { network_filter_container.set_visible(show); b_show_network_option.pressed = show; network_option_visible = show; reset_min_windowsize(); } void map_frame_t::show_hide_scale(const bool show) { scale_container.set_visible(show); b_show_scale.pressed = show; scale_visible = show; reset_min_windowsize(); } void map_frame_t::show_hide_directory(const bool show) { directory_container.set_visible(show); b_show_directory.pressed = show; b_filter_factory_list.pressed = filter_factory_list; directory_visible = show; update_factory_legend(); reset_min_windowsize(); } bool map_frame_t::action_triggered( gui_action_creator_t *comp, value_t v ) { if( comp == &b_show_legend ) { show_hide_legend( !b_show_legend.pressed ); } else if(comp==&b_show_network_option) { show_hide_network_option( !b_show_network_option.pressed ); } else if( comp == &b_show_scale ) { show_hide_scale( !b_show_scale.pressed ); } else if( comp == &b_show_directory ) { show_hide_directory( !b_show_directory.pressed ); } else if( comp == &c_show_outlines ) { env_t::default_mapmode &= ~(minimap_t::MAP_CLIMATES | minimap_t::MAP_HIDE_CONTOUR); if( v.i == 2 ) { env_t::default_mapmode |= minimap_t::MAP_HIDE_CONTOUR; } else if( v.i == 1 ) { env_t::default_mapmode |= minimap_t::MAP_CLIMATES; } minimap_t::get_instance()->set_display_mode( (minimap_t::MAP_DISPLAY_MODE)env_t::default_mapmode ); } else if ( comp == &b_filter_factory_list ) { filter_factory_list = !filter_factory_list; show_hide_directory( b_show_directory.pressed ); } else if (comp == zoom_buttons+0 || comp == zoom_buttons+1) { // zoom in/out zoomed |= zoom(comp != zoom_buttons); } else if( comp == &b_rotate45 ) { // rotated/straight map minimap_t::get_instance()->toggle_isometric(); minimap_t::get_instance()->calc_map_size(); b_rotate45.pressed = minimap_t::get_instance()->is_isometric(); scrolly.set_size( scrolly.get_size() ); zoomed = true; old_ij = koord::invalid; } else if( comp == &b_overlay_networks ) { b_overlay_networks.pressed ^= 1; if( b_overlay_networks.pressed ) { env_t::default_mapmode |= minimap_t::MAP_LINES; } else { env_t::default_mapmode &= ~minimap_t::MAP_LINES; } minimap_t::get_instance()->set_display_mode( ( minimap_t::MAP_DISPLAY_MODE)env_t::default_mapmode ); minimap_t::get_instance()->invalidate_map_lines_cache(); } else if ( comp == &viewed_player_c ) { minimap_t::get_instance()->player_showed_on_map = viewable_players[viewed_player_c.get_selection()]; minimap_t::get_instance()->invalidate_map_lines_cache(); } else if ( comp == &transport_type_c ) { minimap_t::get_instance()->transport_type_showed_on_map = transport_type_c.get_selection(); minimap_t::get_instance()->invalidate_map_lines_cache(); } else if ( comp == &freight_type_c ) { minimap_t::get_instance()->freight_type_group_index_showed_on_map = viewable_freight_types[freight_type_c.get_selection()]; minimap_t::get_instance()->invalidate_map_lines_cache(); } else if ( comp == &b_overlay_networks_load_factor ) { minimap_t::get_instance()->show_network_load_factor = !minimap_t::get_instance()->show_network_load_factor; b_overlay_networks_load_factor.pressed = !b_overlay_networks_load_factor.pressed; minimap_t::get_instance()->invalidate_map_lines_cache(); } else { for( int i=0; iset_display_mode( (minimap_t::MAP_DISPLAY_MODE)env_t::default_mapmode ); update_buttons(); } return true; } /** * Report events to the GUI-components */ bool map_frame_t::infowin_event(const event_t *ev) { event_t ev2 = *ev; ev2.move_origin(scrolly.get_pos() + scr_coord(0, D_TITLEBAR_HEIGHT)); if(ev->ev_class == INFOWIN) { if(ev->ev_code == WIN_OPEN) { minimap_t::get_instance()->set_xy_offset_size( scr_coord(0,0), scr_size(0,0) ); } else if(ev->ev_code == WIN_CLOSE) { minimap_t::get_instance()->is_visible = false; } } // center map with rightdobuleclick if (IS_RIGHTDBLCLK(ev)) { // zoom minimap to fit window while (karte->change_zoom_factor(true)) {} if (scrolly.get_client().w < karte->get_size().w || scrolly.get_client().h < karte->get_size().h) { // zoom out until no longer fits while (karte->change_zoom_factor(false) && (scrolly.get_client().w < karte->get_size().w || scrolly.get_client().h < karte->get_size().h)) {} } map_frame_t::zoomed = true; scrolly.set_size(scrolly.get_size()); // invalidate old offsets old_ij = koord::invalid; return true; } scr_size karte_sz = karte->get_size(); bool swallowed = gui_frame_t::infowin_event(ev); if (karte_sz != karte->get_size()) { // minimap was zoomed map_frame_t::zoomed = true; scrolly.set_size(scrolly.get_size()); old_ij = koord::invalid; } return swallowed; } /** * size window in response and save it in static size */ void map_frame_t::set_windowsize(scr_size size) { gui_frame_t::set_windowsize( size ); window_size = get_windowsize(); } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void map_frame_t::draw(scr_coord pos, scr_size size) { // update our stored screen position screenpos = pos; minimap_t::get_instance()->set_xy_offset_size( scr_coord(scrolly.get_scroll_x(), scrolly.get_scroll_y()), scrolly.get_client().get_size() ); // first: check if cursor within map screen size koord ij = welt->get_viewport()->get_world_position(); if(welt->is_within_limits(ij)) { scr_coord center = minimap_t::get_instance()->map_to_screen_coord(ij); // only re-center if zoomed or world position has changed and its outside visible area const scr_size size = scrolly.get_size(); if(zoomed || ( old_ij != ij && ( scrolly.get_scroll_x()>center.x || scrolly.get_scroll_x()+size.w<=center.x || scrolly.get_scroll_y()>center.y || scrolly.get_scroll_y()+size.h<=center.y ) ) ) { // re-center cursor by scrolling scrolly.set_scroll_position( max(0,center.x-(size.w/2)), max(0,center.y-(size.h/2)) ); zoomed = false; // update zoom factors and zoom label sint16 zoom_in, zoom_out; minimap_t::get_instance()->get_zoom_factors(zoom_out, zoom_in); zoom_value_label.buf().printf("%i:%i", zoom_in, zoom_out); zoom_value_label.update(); zoom_row->set_size(zoom_row->get_size()); } // remember world position, we do not want to have surprises when scrolling later on old_ij = ij; } // draw all child controls gui_frame_t::draw(pos, size); // may add compass if( skinverwaltung_t::compass_map && env_t::compass_map_position!=0 ) { const uint16 isometric_img_offset = minimap_t::get_instance()->is_isometric() ? 4 : 0; gfx->draw_img_aligned( skinverwaltung_t::compass_map->get_image_id( isometric_img_offset+welt->get_settings().get_rotation() ), scrolly.get_client()+pos+scr_coord(4,4+D_TITLEBAR_HEIGHT)-scr_size(8,8), env_t::compass_map_position, false ); } } void map_frame_t::rdwr( loadsave_t *file ) { file->rdwr_bool( legend_visible ); file->rdwr_bool( scale_visible ); file->rdwr_bool( directory_visible ); file->rdwr_long( env_t::default_mapmode ); file->rdwr_bool( b_overlay_networks_load_factor.pressed ); minimap_t::get_instance()->rdwr(file); window_size.rdwr(file); scrolly.rdwr(file); viewed_player_c.rdwr(file); transport_type_c.rdwr(file); freight_type_c.rdwr(file); if( file->is_version_atleast(123, 2) ) { file->rdwr_bool( network_option_visible ); } if( file->is_loading() ) { set_windowsize( window_size ); // notify minimap of new settings minimap_t::get_instance()->calc_map_size(); scrolly.set_size( scrolly.get_size() ); minimap_t::get_instance()->set_display_mode(( minimap_t::MAP_DISPLAY_MODE)env_t::default_mapmode); update_buttons(); show_hide_directory(directory_visible); show_hide_legend(legend_visible); show_hide_network_option(network_option_visible); show_hide_scale(scale_visible); b_overlay_networks.pressed = (env_t::default_mapmode & minimap_t::MAP_LINES)!=0; minimap_t::get_instance()->player_showed_on_map = viewable_players[viewed_player_c.get_selection()]; minimap_t::get_instance()->transport_type_showed_on_map = transport_type_c.get_selection(); minimap_t::get_instance()->freight_type_group_index_showed_on_map = viewable_freight_types[freight_type_c.get_selection()]; minimap_t::get_instance()->show_network_load_factor = b_overlay_networks_load_factor.pressed; minimap_t::get_instance()->invalidate_map_lines_cache(); } } simutrans-124.5/src/simutrans/gui/map_frame.h000066400000000000000000000056521520675460500212740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_MAP_FRAME_H #define GUI_MAP_FRAME_H #include "gui_frame.h" #include "simwin.h" #include "components/gui_scrollpane.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_combobox.h" #include "components/gui_label.h" #include "../descriptor/factory_desc.h" #include "../tpl/stringhashtable_tpl.h" #include "../player/simplay.h" #include "../simline.h" class karte_ptr_t; class minimap_t; #define MAP_MAX_BUTTONS (22) /** * Minimap window */ class map_frame_t : public gui_frame_t, public action_listener_t { private: static karte_ptr_t welt; minimap_t* karte; /** * This is kind of hack: we know there can only be one map frame * at a time, and we want to save the current size for the next object * so we use a static variable here. */ static scr_size window_size; static bool legend_visible; static bool network_option_visible; static bool scale_visible; static bool directory_visible; static bool filter_factory_list; int viewable_players[MAX_PLAYER_COUNT+1]; vector_tpl viewable_freight_types; gui_aligned_container_t filter_container, network_filter_container, scale_container, directory_container, *zoom_row; gui_scrollpane_t scrolly; button_t filter_buttons[MAP_MAX_BUTTONS]; button_t zoom_buttons[2]; button_t b_rotate45; button_t b_show_legend; button_t b_show_network_option; button_t b_show_scale; gui_combobox_t c_show_outlines; button_t b_show_directory; button_t b_overlay_networks; button_t b_overlay_networks_load_factor; button_t b_filter_factory_list; gui_label_buf_t zoom_value_label; gui_combobox_t viewed_player_c; gui_combobox_t transport_type_c; gui_combobox_t freight_type_c; void update_buttons(); void update_factory_legend(); void show_hide_legend(const bool show); void show_hide_network_option(const bool show); void show_hide_scale(const bool show); void show_hide_directory(const bool show); static bool zoomed; // if true, zoom label will be updated on next redraw bool zoom(bool magnify); public: /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "map.txt";} static scr_coord screenpos; /** * Constructor. Adds all necessary Subcomponents. */ map_frame_t(); void rdwr( loadsave_t *file ) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_reliefmap; } bool infowin_event(event_t const*) OVERRIDE; /** * Sets the window sizes */ void set_windowsize(scr_size size) OVERRIDE; /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/message_frame.cc000066400000000000000000000120741520675460500222750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simwin.h" #include "message_frame.h" #include "message_option.h" #include "message_stats.h" #include "chat_frame.h" #include "../world/simworld.h" #include "../tool/simmenu.h" #include "../simmesg.h" #include "../sys/simsys.h" #include "../dataobj/scenario.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../network/network_cmd_ingame.h" #include "../player/simplay.h" #define MAX_MESG_TABS (8) static sint32 categories[MAX_MESG_TABS] = { (1 << message_t::chat), // obsolete!! (1 << message_t::scenario), (1 << message_t::problems), (1 << message_t::traffic_jams) | (1 << message_t::warnings), (1 << message_t::full), (1 << message_t::city) | (1 << message_t::industry), (1 << message_t::ai), (1 << message_t::general) | (1 << message_t::new_vehicle) }; static char const* const tab_strings[]= { "Chat_msg", // obsolete!! "Scenario_msg", "Problems_msg", "Warnings_msg", "Station_msg", "Town_msg", "Company_msg", "Game_msg" }; message_frame_t::message_frame_t() : gui_frame_t( translator::translate("Mailbox") ), scrolly(gui_scrolled_list_t::windowskin, message_stats_t::compare) { last_count = 0; message_type = -1; set_table_layout(1,0); add_table(0,1); { option_bt.init(button_t::roundbox, translator::translate("Optionen")); option_bt.add_listener(this); add_component(&option_bt); copy_bt.init(button_t::roundbox, translator::translate("Copy to clipboard")); copy_bt.add_listener(this); add_component(©_bt); if (env_t::networkmode || !welt->get_chat_message()->get_list().empty()) { open_chat_bt.init(button_t::roundbox, translator::translate("Chat")); open_chat_bt.set_size(D_BUTTON_SIZE); open_chat_bt.add_listener(this); add_component(&open_chat_bt); } new_component(); } end_table(); // add tabs for classifying messages tabs.add_tab( &scrolly, translator::translate("All") ); tab_categories.append( -1 ); if (welt->get_scenario()->is_scripted()) { tabs.add_tab( &scrolly, translator::translate(tab_strings[1]) ); tab_categories.append( categories[1] ); } for( int i=2; iget_message()->get_list() ) { scrolly.new_component(i, id++); } last_count = welt->get_message()->get_list().get_count(); // trigger filtering sint32 t = message_type; message_type = -1; // filter & sort filter_list(t); scrolly.set_size( scrolly.get_size()); } void message_frame_t::filter_list(sint32 type) { if (type != message_type) { for(int i=0, end=scrolly.get_count(); i(scrolly.get_element(i)); // message type filtering controls visibility if (a) { a->set_visible(type == -1 || a->get_msg()->get_type_shifted() & type); } } message_type = type; } scrolly.sort(0); scrolly.set_size( scrolly.get_size()); } bool message_frame_t::action_triggered( gui_action_creator_t *comp, value_t v ) { if( comp==&option_bt ) { create_win({ 320, 200 }, new message_option_t(), w_info, magic_message_options ); } if( comp==&open_chat_bt ) { create_win({ 0, 200 }, new chat_frame_t(), w_info, magic_chatframe); } else if( comp==©_bt ) { cbuffer_t clipboard; const sint32 message_type = tab_categories[ tabs.get_active_tab_index() ]; int count = 20; // just copy the last 20 for(message_node_t* const i : welt->get_message()->get_list() ) { if( i->get_type_shifted() & message_type ) { // add them to clipboard char msg_no_break[ 258 ]; for( int j = 0; j < 256; j++ ) { msg_no_break[ j ] = i->msg[ j ] == '\n' ? ' ' : i->msg[ j ]; if( msg_no_break[ j ] == 0 ) { msg_no_break[ j++ ] = '\n'; msg_no_break[ j ] = 0; break; } } clipboard.append( msg_no_break ); if( count-- < 0 ) { break; } } } // copy, if there was anything ... if( clipboard.len() > 0 ) { dr_copy( clipboard, clipboard.len() ); } } else if( comp==&tabs ) { // filter messages by type where necessary filter_list(tab_categories[v.i]); } return true; } void message_frame_t::draw(scr_coord pos, scr_size size) { if( welt->get_message()->get_list().get_count() != last_count ) { fill_list(); } gui_frame_t::draw(pos, size); } void message_frame_t::rdwr(loadsave_t *file) { // window size scr_size size = get_windowsize(); size.rdwr( file ); scrolly.rdwr(file); tabs.rdwr(file); if (file->is_version_less(124, 1)) { char dummy[256] = { 0 }; file->rdwr_str(dummy, lengthof(dummy)); } if( file->is_loading() ) { fill_list(); reset_min_windowsize(); set_windowsize(size); } } simutrans-124.5/src/simutrans/gui/message_frame.h000066400000000000000000000025041520675460500221340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_MESSAGE_FRAME_H #define GUI_MESSAGE_FRAME_H #include "simwin.h" #include "gui_frame.h" #include "components/gui_button.h" #include "components/gui_scrolled_list.h" #include "components/gui_tab_panel.h" #include "components/action_listener.h" /** * All messages since the start of the program */ class message_frame_t : public gui_frame_t, private action_listener_t { private: gui_scrolled_list_t scrolly; gui_tab_panel_t tabs; // tab panel for filtering messages button_t option_bt, copy_bt, open_chat_bt; vector_tpl tab_categories; uint32 last_count; // of messages in list sint32 message_type; // message type for filtering; -1 indicates no filtering void fill_list(); void filter_list(sint32 type); public: message_frame_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE {return "mailbox.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void rdwr(loadsave_t *) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_messageframe; } void draw(scr_coord pos, scr_size size) OVERRIDE; void map_rotate90(sint16 /*new_ysize*/) OVERRIDE { fill_list(); } }; #endif simutrans-124.5/src/simutrans/gui/message_option.cc000066400000000000000000000054251520675460500225150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simmesg.h" #include "../simskin.h" #include "../world/simworld.h" #include "../descriptor/skin_desc.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "message_option.h" #include "components/gui_image.h" message_option_t::message_option_t() : gui_frame_t( translator::translate("Mailbox Options") ) { set_table_layout(5,0); // first row images new_component_span(2); if (skinverwaltung_t::message_options->get_count() >=3 ) { // three single images for(int i=0; i<3; i++) { new_component(skinverwaltung_t::message_options->get_image_id(i), 0, 0, true); } } else { // one monolithic image new_component_span(skinverwaltung_t::message_options->get_image_id(0), 0, 0, true, 3); } // The text is unfortunately a single text, which we have to chop into pieces. const unsigned char *p = (const unsigned char *)translator::translate( "MessageOptionsText" ); welt->get_message()->get_message_flags( &ticker_msg, &window_msg, &auto_msg, &ignore_msg ); for( int i = 0; i < message_t::MAX_MESSAGE_TYPE; i++ ) { option_texts[i][0] = 0; } for( int i=0; i>i)&1)==0; buttons[i*4].add_listener(this); add_component( buttons+i*4 ); // copy the next line of the option text while( *p < ' ' && *p ) { p++; } for( int j=0; *p>=' '; p++ ) { if( j < MAX_MESSAGE_OPTION_TEXTLEN-1 ) { option_texts[i][j++] = *p; option_texts[i][j] = 0; } } text_lbl[i].set_text( option_texts[i] ); add_component( text_lbl+i ); buttons[i*4+1].set_typ(button_t::square_state); buttons[i*4+1].pressed = (ticker_msg>>i)&1; buttons[i*4+1].add_listener(this); add_component( buttons+i*4+1 ); buttons[i*4+2].set_typ(button_t::square_state); buttons[i*4+2].pressed = (auto_msg>>i)&1; buttons[i*4+2].add_listener(this); add_component( buttons+i*4+2 ); buttons[i*4+3].set_typ(button_t::square_state); buttons[i*4+3].pressed = (window_msg>>i)&1; buttons[i*4+3].add_listener(this); add_component( buttons+i*4+3 ); } reset_min_windowsize(); } bool message_option_t::action_triggered( gui_action_creator_t *comp, value_t ) { ((button_t*)comp)->pressed ^= 1; for( int i=0; iget_message()->set_message_flags( ticker_msg, window_msg, auto_msg, ignore_msg ); return true; } simutrans-124.5/src/simutrans/gui/message_option.h000066400000000000000000000017201520675460500223510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_MESSAGE_OPTION_H #define GUI_MESSAGE_OPTION_H #include "../simmesg.h" #include "simwin.h" #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_label.h" #include "../utils/cbuffer.h" #define MAX_MESSAGE_OPTION_TEXTLEN (64) class message_option_t : public gui_frame_t, private action_listener_t { private: button_t buttons[4*message_t::MAX_MESSAGE_TYPE]; gui_label_t text_lbl[message_t::MAX_MESSAGE_TYPE]; sint32 ticker_msg, window_msg, auto_msg, ignore_msg; char option_texts[message_t::MAX_MESSAGE_TYPE][MAX_MESSAGE_OPTION_TEXTLEN]; public: message_option_t(); const char * get_help_filename() const OVERRIDE {return "mailbox.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_message_options; } }; #endif simutrans-124.5/src/simutrans/gui/message_stats.cc000066400000000000000000000036571520675460500223500ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "message_stats.h" #include "components/gui_button.h" #include "components/gui_label.h" #include "messagebox.h" #include "simwin.h" #include "../world/simworld.h" #include "../display/viewport.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" static karte_ptr_t welt; message_stats_t::message_stats_t(const message_node_t *m, uint32 sid) : msg(m), sortid(sid) { set_table_layout(2,1); // pos-button, visible or not button_t *b = new_component(); b->set_typ(button_t::posbutton_automatic); if (msg->pos!=koord3d::invalid) { b->set_targetpos3d(msg->pos); } else { b->set_visible(false); b->set_rigid(true); } // text buffer gui_label_buf_t *label = new_component(msg->get_player_color(welt)); // now fill buffer, first the date label->buf().printf( "(%s) ", translator::get_short_date( msg->time/12, msg->time%12 ) ); // then the text (without line break) for(int j=0; ; j++) { char c = msg->msg[j]; if (c==0) { break; } label->buf().printf("%c", c == '\n' ? ' ': c); } label->update(); } bool message_stats_t::compare(const gui_component_t *aa, const gui_component_t *bb ) { const message_stats_t* a = dynamic_cast(aa); const message_stats_t* b = dynamic_cast(bb); assert(a && b); return a->sortid < b->sortid; } /** * Click on message => go to position */ bool message_stats_t::infowin_event(const event_t * ev) { bool swallowed = gui_aligned_container_t::infowin_event(ev); if( !swallowed && IS_LEFTRELEASE(ev) ) { msg->open_msg_window(false /* open as normal not autoclose */); swallowed = true; } else if( !swallowed && IS_RIGHTRELEASE(ev) && msg->pos!=koord3d::invalid ) { welt->get_viewport()->change_world_position( msg->pos ); swallowed = true; } return swallowed; } simutrans-124.5/src/simutrans/gui/message_stats.h000066400000000000000000000014571520675460500222060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_MESSAGE_STATS_H #define GUI_MESSAGE_STATS_H #include "components/gui_aligned_container.h" #include "components/gui_scrolled_list.h" #include "../simmesg.h" /** * Message display */ class message_stats_t : public gui_aligned_container_t, public gui_scrolled_list_t::scrollitem_t { private: const message_node_t *msg; uint32 sortid; // sortid reflects the order in message_t public: message_stats_t(const message_node_t *m, uint32 sid); const message_node_t* get_msg() const { return msg; } char const* get_text() const OVERRIDE { return msg->msg; } bool infowin_event(const event_t * ev) OVERRIDE; static bool compare(const gui_component_t *a, const gui_component_t *b ); }; #endif simutrans-124.5/src/simutrans/gui/messagebox.cc000066400000000000000000000045031520675460500216320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../world/simworld.h" #include "../display/simgraph.h" #include "../simskin.h" #include "../descriptor/skin_desc.h" #include "../dataobj/translator.h" #include "messagebox.h" #include "../sys/simsys.h" news_window::news_window(const char* text, FLAGGED_PIXVAL title_color) : base_infowin_t( translator::translate("Meldung" ) ), color(title_color) { buf.clear(); buf.append(translator::translate(text)); textarea.set_size(scr_size(textarea.get_size().w + 1, 0)); recalc_size(); } fatal_news::fatal_news(const char* text) : news_window(text, env_t::default_window_title_color) { copy_to_clipboard.init(button_t::roundbox, "Copy to clipboard"); copy_to_clipboard.add_listener( this ); copy_to_clipboard.set_focusable(false); add_component(©_to_clipboard); textarea.set_width(gfx->get_screen_size().w/2); recalc_size(); } bool fatal_news::action_triggered(gui_action_creator_t *comp, value_t) { if (comp == ©_to_clipboard) { dr_copy(buf, buf.len()); return false; } return true; } news_img::news_img(const char* text) : news_window(text, env_t::default_window_title_color), image() { init(skinverwaltung_t::meldungsymbol->get_image_id(0)); } news_img::news_img(const char* text, image_id id, FLAGGED_PIXVAL color) : news_window(text, color), image() { init(id); } /** * just puts the image in top-right corner * only cembedded.d from constructor * @param id id of image */ void news_img::init(image_id id) { if( id!=IMG_EMPTY ) { image.set_image(id, true); image.enable_offset_removal(true); image.set_size(image.get_min_size()); textarea.set_size(scr_size(textarea.get_size().w + image.get_size().w + D_H_SPACE + 1,0)); set_embedded(&image); } } news_loc::news_loc(const char* text, koord3d k, FLAGGED_PIXVAL color) : news_window(text, color), view(k, scr_size( max(64, gfx->get_base_tile_raster_width()), max(56, (gfx->get_base_tile_raster_width()*7)/8) )) { textarea.set_size(scr_size(textarea.get_size().w + view.get_size().w + D_H_SPACE + 1, 0)); set_embedded(&view); } // returns position of the location shown in the subwindow koord3d news_loc::get_weltpos(bool) { return view.get_location(); } void news_loc::map_rotate90( sint16 new_ysize ) { view.map_rotate90(new_ysize); } simutrans-124.5/src/simutrans/gui/messagebox.h000066400000000000000000000026771520675460500215060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_MESSAGEBOX_H #define GUI_MESSAGEBOX_H #include "base_info.h" #include "components/gui_location_view.h" #include "components/gui_image.h" #include "../simcolor.h" #include "../dataobj/environment.h" /** * A class for Message/news window. */ class news_window : public base_infowin_t { public: FLAGGED_PIXVAL get_titlecolor() const OVERRIDE { return color; } protected: news_window(const char* text, FLAGGED_PIXVAL color); private: FLAGGED_PIXVAL color; }; /** * Displays fatal error message. */ class fatal_news : public news_window, private action_listener_t { button_t copy_to_clipboard; public: fatal_news(const char* text); private: bool action_triggered(gui_action_creator_t *comp, value_t extra) OVERRIDE; }; /** * Shows a news window with an image */ class news_img : public news_window { public: news_img(const char* text); news_img(const char* text, image_id image, FLAGGED_PIXVAL color = env_t::default_window_title_color); private: void init(image_id image); gui_image_t image; }; /** * Shows a news window with a view on some location */ class news_loc : public news_window { public: news_loc(const char* text, koord3d k, FLAGGED_PIXVAL color = env_t::default_window_title_color); void map_rotate90( sint16 new_ysize ) OVERRIDE; koord3d get_weltpos(bool) OVERRIDE; private: location_view_t view; }; #endif simutrans-124.5/src/simutrans/gui/minimap.cc000066400000000000000000001616411520675460500211360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simevent.h" #include "../simcolor.h" #include "../simconvoi.h" #include "../vehicle/vehicle.h" #include "../world/simworld.h" #include "../obj/depot.h" #include "../simhalt.h" #include "../simskin.h" #include "../ground/grund.h" #include "../tool/simtool.h" #include "../simfab.h" #include "../world/simcity.h" #include "fabrik_info.h" #include "simwin.h" #include "minimap.h" #include "../dataobj/translator.h" #include "../dataobj/settings.h" #include "../dataobj/schedule.h" #include "../dataobj/powernet.h" #include "../dataobj/ribi.h" #include "../dataobj/loadsave.h" #include "../obj/way/schiene.h" #include "../obj/leitung2.h" #include "../utils/cbuffer.h" #include "../display/scr_coord.h" #include "../display/simgraph.h" #include "../display/viewport.h" #include "../utils/simrandom.h" #include "../player/simplay.h" #include "../tpl/inthashtable_tpl.h" #include "../tpl/slist_tpl.h" #include sint32 minimap_t::max_cargo=0; sint32 minimap_t::max_passed=0; static sint32 max_waiting_change = 1; static sint32 max_tourist_ziele = 1; static sint32 max_waiting = 1; static sint32 max_origin = 1; static sint32 max_transfer = 1; static sint32 max_service = 1; static sint32 max_building_level = 0; minimap_t * minimap_t::single_instance = NULL; karte_ptr_t minimap_t::world; minimap_t::MAP_DISPLAY_MODE minimap_t::mode = MAP_TOWN; minimap_t::MAP_DISPLAY_MODE minimap_t::last_mode = MAP_TOWN; bool minimap_t::is_visible = false; #define MAX_MAP_TYPE_LAND 31 #define MAX_MAP_TYPE_WATER 5 // color for the land static const uint8 map_type_color[MAX_MAP_TYPE_WATER+MAX_MAP_TYPE_LAND] = { // water level 97, 99, 19, 21, 23, // terrain level 160, 161, 162, 163, 164, 165, 166, 167, 205, 206, 207, 172, 174, 159, COL_LIGHT_ORANGE, COL_TOLL, 156, 154, 115, 114, 113, 112, 216, 217, 218, 219, 220, COL_LILAC, 222, 223, 224 }; const uint8 minimap_t::severity_color[MAX_SEVERITY_COLORS] = { 106, 2, 85, 86, 29, 30, COL_YELLOW, 71, 39, COL_OPERATION }; minimap_t::line_segment_t::line_segment_t(koord s, uint8 so, koord e, uint8 eo, schedule_t* sched, player_t* p, uint8 cc, bool diagonal) { schedule = sched; waytype = sched->get_waytype(); player = p; colorcount = cc; start_diagonal = diagonal; if( s.xsimilar( other.schedule, player ); } // Ordering based on first start then end coordinate bool minimap_t::LineSegmentOrdering::operator()(const minimap_t::line_segment_t& a, const minimap_t::line_segment_t& b) const { if( a.start.x == b.start.x ) { // same start ... return a.end.x < b.end.x; } return a.start.x < b.start.x; } static uint8 colore_idx = 0; static inthashtable_tpl< int, slist_tpl > waypoint_hash; // add the schedule to the map (if there is a valid one) void minimap_t::add_to_schedule_cache( convoihandle_t cnv, bool with_waypoints ) { // make sure this is valid! if( !cnv.is_bound() ) { return; } schedule_t *schedule = cnv->get_schedule(); if( !show_network_load_factor ) { colore_idx += 8; if( colore_idx >= 208 ) { colore_idx = (colore_idx % 8) + 1; if( colore_idx == 7 ) { colore_idx = 0; } } } else { //TODO: extract common part from with schedule_list_gui_t::display() int capacity = 0, load = 0; // total capacity and load of line (=sum of all conv's cap/load) if(cnv->get_line().is_bound()) { for( uint i = 0; i < cnv->get_line()->count_convoys(); i++ ) { convoihandle_t const cnv_in_line = cnv->get_line()->get_convoy(i); // we do not want to count the capacity of depot convois if( !cnv_in_line->in_depot() ) { for( unsigned j = 0; j < cnv_in_line->get_vehicle_count(); j++ ) { capacity += cnv_in_line->get_vehicle(j)->get_cargo_max(); load += cnv_in_line->get_vehicle(j)->get_total_cargo(); } } } } else { // we do not want to count the capacity of depot convois if(!cnv->in_depot()) { for(unsigned j = 0; j < cnv->get_vehicle_count(); j++) { capacity += cnv->get_vehicle(j)->get_cargo_max(); load += cnv->get_vehicle(j)->get_total_cargo(); } } } // we check if cap is zero, since theoretically a // conv can consist of only 1 vehicle, which has no cap (eg. locomotive) // and we do not like to divide by zero, do we? if(capacity > 0) { const uint32 load_idx = clamp(load * MAX_SEVERITY_COLORS / capacity, 0, MAX_SEVERITY_COLORS-1); colore_idx = severity_color[MAX_SEVERITY_COLORS-1 - load_idx]; } else { colore_idx = severity_color[MAX_SEVERITY_COLORS-1]; } } // ok, add this schedule to map // from here on we have a valid convoi int stops = 0; uint8 old_offset = 0, first_offset = 0, temp_offset = 0; koord old_stop, first_stop, temp_stop; bool last_diagonal = false; const bool add_schedule = schedule->get_waytype() != air_wt; for(schedule_entry_t cur : schedule->entries ) { //cycle on stops //try to read station's coordinates if there's a station at this schedule stop halthandle_t station = haltestelle_t::get_halt( cur.pos, cnv->get_owner(), add_schedule ); if( station.is_bound() ) { stop_cache.append_unique( station ); temp_stop = station->get_basis_pos(); stops ++; } else if( with_waypoints ) { temp_stop = cur.pos.get_2d(); stops ++; } else { continue; } const int key = temp_stop.x + temp_stop.y*world->get_size().x; waypoint_hash.put( key ); // now get the offset slist_tpl*pt_list = waypoint_hash.access(key); if( add_schedule ) { // init key if( !pt_list->is_contained( schedule ) ) { // not known => append temp_offset = pt_list->get_count(); } else { // how many times we reached here? temp_offset = pt_list->index_of( schedule ); } } else { temp_offset = 0; } if( stops>1 ) { last_diagonal ^= true; if( (temp_stop.x-old_stop.x)*(temp_stop.y-old_stop.y) == 0 ) { last_diagonal = false; } if( !schedule_cache.insert_unique_ordered( line_segment_t( temp_stop, temp_offset, old_stop, old_offset, schedule, cnv->get_owner(), colore_idx, last_diagonal ), LineSegmentOrdering() ) && add_schedule ) { // append if added and not yet there if( !pt_list->is_contained( schedule ) ) { pt_list->append( schedule ); } if( stops == 2 ) { // append first stop too, when this is called for the first time const int key = first_stop.x + first_stop.y*world->get_size().x; waypoint_hash.put( key ); slist_tpl*pt_list = waypoint_hash.access(key); if( !pt_list->is_contained( schedule ) ) { pt_list->append( schedule ); } } } old_stop = temp_stop; old_offset = temp_offset; } else { first_stop = temp_stop; first_offset = temp_offset; old_stop = temp_stop; old_offset = temp_offset; } } if( stops > 2 ) { // connect to start last_diagonal ^= true; schedule_cache.insert_unique_ordered( line_segment_t( first_stop, first_offset, old_stop, old_offset, schedule, cnv->get_owner(), colore_idx, last_diagonal ), LineSegmentOrdering() ); } } // some routines for the minimap with schedules static uint32 number_to_radius( uint32 n ) { return log2( n>>5 ); } static void display_airport( const scr_coord_val xx, const scr_coord_val yy, const FLAGGED_PIXVAL color ) { int x = xx + 5; int y = yy - 11; if ( y < 0 ) { y = 0; } const char symbol[] = { 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X', 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X', 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X', 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', 'X', 'X', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', '.', '.', 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X', 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X' }; for ( int i = 0; i < 11; i++ ) { for ( int j = 0; j < 11; j++ ) { if ( symbol[i + j * 11] == 'X' ) { gfx->draw_vline_clipped(x + i, y + j, 1, color, true CLIP_NUM_DEFAULT); } } } } static void display_harbor( const scr_coord_val xx, const scr_coord_val yy, const FLAGGED_PIXVAL color ) { int x = xx + 5; int y = yy - 11 + 13; //to not overwrite airline symbol if ( y < 0 ) { y = 0; } const char symbol[] = { 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', 'X', 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X', 'X', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', 'X', 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', 'X', 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X', 'X', 'X', 'X', '.', '.', 'X', '.', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', 'X', '.', 'X', 'X', 'X', 'X', 'X', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', 'X', 'X', '.', '.', 'X', 'X', 'X', 'X', 'X', '.', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X' }; for ( int i = 0; i < 11; i++ ) { for ( int j = 0; j < 11; j++ ) { if ( symbol[i + j * 11] == 'X' ) { gfx->draw_vline_clipped(x + i, y + j, 1, color, true CLIP_NUM_DEFAULT); } } } } // those will be replaced by pak images later ...! static void display_thick_line( scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2, PIXVAL col, bool dotting, short dot_full, short dot_empty, short thickness ) { double delta_x = abs( x1 - x2 ); double delta_y = abs( y1 - y2 ); if( delta_x == 0.0 || delta_y/delta_x > 2.0 ) { // mostly vertical x1 -= thickness/2; x2 -= thickness/2; for( int i = 0; i < thickness; i++ ) { if ( !dotting ) { gfx->draw_line( x1 + i, y1, x2 + i, y2, col ); } else { gfx->draw_line_dotted( x1 + i, y1, x2 + i, y2, dot_full, dot_empty, col ); } } } else if( delta_y == 0.0 || delta_x/delta_y > 2.0 ) { // mostly horizontal y1 -= thickness/2; y2 -= thickness/2; for( int i = 0; i < thickness; i++ ) { if ( !dotting ) { gfx->draw_line( x1, y1 + i, x2, y2 + i, col ); } else { gfx->draw_line_dotted( x1, y1 + i, x2, y2 + i, dot_full, dot_empty, col ); } } } else { // diagonal int y_multiplier = (x1-x2)/(y1-y2) < 0 ? +1 : -1; thickness = (thickness*7)/8; x1 -= thickness/2; x2 -= thickness/2; y1 -= thickness*y_multiplier/2; y2 -= thickness*y_multiplier/2; for( int i = 0; i < thickness; i++ ) { if ( !dotting ) { gfx->draw_line( x1+i, y1+i*y_multiplier, x2+i, y2+i*y_multiplier, col ); gfx->draw_line( x1+i+1, y1+i*y_multiplier, x2+i+1, y2+i*y_multiplier, col ); } else { gfx->draw_line_dotted( x1 + i, y1 + i*y_multiplier, x2 + i, y2 + i*y_multiplier, dot_full, dot_empty, col ); gfx->draw_line_dotted( x1 + i + 1, y1 + i*y_multiplier, x2 + i + 1, y2 + i*y_multiplier, dot_full, dot_empty, col ); } } } } static void line_segment_draw( waytype_t type, scr_coord start, const uint8 start_offset, scr_coord end, const uint8 end_offset, bool start_diagonal, const PIXVAL colore ) { // airplanes are different, so we must check for them first if( type == air_wt ) { // ignore offset for airplanes gfx->draw_bezier(start.x, start.y, end.x, end.y, 50, 50, 50, 50, colore, 5, 5); gfx->draw_bezier(start.x+1, start.y+1, end.x+1, end.y+1, 50, 50, 50, 50, colore, 5, 5); } else { // determine line style uint8 thickness = 3; bool dotted = false; switch( type ) { case monorail_wt: case maglev_wt: thickness = 5; break; case track_wt: thickness = 4; break; case road_wt: thickness = 2; break; case tram_wt: case narrowgauge_wt: thickness = 3; break; default: thickness = 3; dotted = true; } // move to the correct locations start.x += 3*start_offset; start.y += 3*start_offset; end.x += 3*end_offset; end.y += 3*end_offset; scr_coord delta = end-start; // Reduce diagonal line overlap: Genereally respect the requested start_diagonal. // In case of NW to SE lines, ignore the requested diagonal, if either start or end of the line has index 0 if(sgn(delta.x) * sgn(delta.y) == 1){ if(end_offset){ start_diagonal = true; } if(start_offset){ start_diagonal = false; } } // We always start straight, then diagonal. If the other way round is desired, simply swap start and end. if(start_diagonal ){ delta = delta*-1; const scr_coord tmp = start; start=end; end=tmp; } const int d = min(abs(delta.x),abs(delta.y));//pick absolute of smallest component const scr_coord diag = scr_coord(d*sgn(delta.x),d*sgn(delta.y)); const scr_coord mid=end-diag; if(start!=mid) { display_thick_line(start.x, start.y, mid.x, mid.y, colore, dotted, 5, 3, thickness); } if(mid!=end) { display_thick_line(mid.x, mid.y, end.x, end.y, colore, dotted, 5, 3, thickness); } } } // converts map (karte) koordinates to screen koordinates scr_coord minimap_t::map_to_screen_coord(const koord &k) const { assert(zoom_in ==1 || zoom_out ==1 ); scr_coord_val x = (scr_coord_val)k.x * zoom_in; scr_coord_val y = (scr_coord_val)k.y * zoom_in; if(isometric) { // 45 rotate view scr_coord_val xrot = (scr_coord_val)world->get_size().y * zoom_in + x - y - 1; y = ( x + y )/2; x = xrot; } return scr_coord(x/zoom_out, y/zoom_out); } // and re-transform koord minimap_t::screen_to_map_coord(const scr_coord &c) const { sint32 x = ((sint32)c.x*zoom_out)/zoom_in; sint32 y = ((sint32)c.y*zoom_out)/zoom_in; if(isometric) { y *= 2; x = (x + y - world->get_size().y)/2; y = y - x; } return koord(x,y); } bool minimap_t::change_zoom_factor(bool magnify) { bool zoomed = false; if( magnify ) { // zoom in if( zoom_out > 1 ) { zoom_out--; zoomed = true; } else { // check here for maximum zoom-out, otherwise there will be integer overflows // with large maps as we calculate with sint16 coordinates ... const int max_zoom_in = min( INT_MAX / (2*world->get_size_max()), 16); if( zoom_in < max_zoom_in ) { zoom_in++; zoomed = true; } } } else { // zoom out if( zoom_in > 1 ) { zoom_in--; zoomed = true; } else if( zoom_out < 16 ) { zoom_out++; zoomed = true; } } if( zoomed ){ // recalc map size calc_map_size(); } return zoomed; } PIXVAL minimap_t::calc_severity_color(sint32 amount, sint32 max_value) { if(max_value!=0) { // color array goes from light blue to red const sint32 severity = ((sint64)amount * MAX_SEVERITY_COLORS) / ((sint64)max_value + 1); return gfx->palette_lookup( minimap_t::severity_color[ clamp( severity, 0, MAX_SEVERITY_COLORS-1 ) ]); } return gfx->palette_lookup( minimap_t::severity_color[0]); } PIXVAL minimap_t::calc_severity_color_log(sint32 amount, sint32 max_value) { if( max_value>1 ) { sint32 severity; if( amount <= 0x003FFFFF ) { severity = log2( (uint32)( (amount << MAX_SEVERITY_COLORS) / (max_value+1) ) ); } else { severity = (uint32)( log( (double)amount*(double)(1<palette_lookup( minimap_t::severity_color[ clamp( severity, 0, MAX_SEVERITY_COLORS-1 ) ]); } return gfx->palette_lookup( minimap_t::severity_color[0]); } void minimap_t::set_map_color_clip( sint16 x, sint16 y, PIXVAL color ) { if( 0<=x && (uint16)x < map_data->get_width() && 0<=y && (uint16)y < map_data->get_height() ) { map_data->at( x, y ) = color; } } void minimap_t::set_map_color(koord k_, const PIXVAL color) { // if map is in normal mode, set new color for map // otherwise do nothing // result: convois will not "paint over" special maps if ( map_data==NULL || !world->is_within_limits(k_)) { return; } scr_coord c = map_to_screen_coord(k_); c -= cur_off; if( isometric ) { // since isometric is distorted const sint32 xw = zoom_out>=2 ? 1 : 2*zoom_in; // increase size at zoom_in 2, 5, 9, 11 const scr_coord_val mid_y = ((xw+1) / 5) + (xw / 18); // center line for( int x=0; x 0 ) { scr_coord_val left = 2, right = xw-2 + ((xw>>1)&1); for( scr_coord_val y_offset = 1; y_offset <= mid_y; y_offset++ ) { for( int x=left; xget_width(); x++ ) { for( sint32 y = max(0,c.y); y < zoom_in+c.y && (uint32)y < map_data->get_height(); y++ ) { map_data->at(x, y) = color; } } } } /** * calculates ground color for position relative to water height * @param hoehe height of the tile * @param groundwater water height */ PIXVAL minimap_t::calc_height_color(const sint16 hoehe, const sint16 groundwater) { sint16 relative_index; if( hoehe>groundwater ) { // adjust index for world_maximum_height relative_index = (hoehe-groundwater)*MAX_MAP_TYPE_LAND/world->get_settings().get_maximumheight(); if( (hoehe-groundwater)*MAX_MAP_TYPE_LAND%world->get_settings().get_maximumheight()!=0 ) { // to avoid relative_index==0 relative_index += 1; } } else { relative_index = hoehe-groundwater; } return gfx->palette_lookup(map_type_color[clamp( relative_index+MAX_MAP_TYPE_WATER-1, 0, MAX_MAP_TYPE_WATER+MAX_MAP_TYPE_LAND-1 )]); } /** * Calculates the minimap color of a ground tile */ PIXVAL minimap_t::calc_ground_color(const grund_t *gr) { PIXVAL color = gfx->palette_lookup(COL_BLACK); #ifdef DEBUG_ROUTES /* for debug purposes only ...*/ if(gr->get_flag(grund_t::marked)) { color = gfx->palette_lookup(COL_PURPLE); } else #endif if(gr->get_halt().is_bound()) { color = COL_HALT; } else { switch(gr->get_typ()) { case grund_t::brueckenboden: color = gfx->palette_lookup(MN_GREY3); break; case grund_t::tunnelboden: color = gfx->palette_lookup(COL_BROWN); break; case grund_t::monorailboden: color = COL_MONORAIL; break; case grund_t::fundament: { // object at zero is either factory or house (or attraction ... ) gebaeude_t *gb = gr->find(); fabrik_t *fab = gb ? gb->get_fabrik() : NULL; if(fab==NULL) { color = gfx->palette_lookup(COL_GREY3); } else { color = fab->get_color(); } } break; case grund_t::wasser: { // object at zero is either factory or boat gebaeude_t *gb = gr->find(); fabrik_t *fab = gb ? gb->get_fabrik() : NULL; if(fab==NULL) { sint16 height = corner_sw(gr->get_grund_hang()); if( mode&MAP_HIDE_CONTOUR ) { color = gfx->palette_lookup(map_type_color[1]); // second deep water color } else { color = calc_height_color( world->lookup_hgt( gr->get_pos().get_2d() ) + height, world->get_water_hgt( gr->get_pos().get_2d() ) ); } //color = gfx->palette_lookup(COL_BLUE); // water with boat? } else { color = fab->get_color(); } } break; // normal ground ... default: if(gr->hat_wege()) { switch(gr->get_weg_nr(0)->get_waytype()) { case road_wt: color = COL_ROAD; break; case tram_wt: case track_wt: color = COL_RAIL; break; case water_wt: color = COL_CANAL; break; case air_wt: color = COL_RUNWAY; break; case monorail_wt: default: // all other ways light red ... color = gfx->palette_lookup(135); break; } } else { const leitung_t* lt = gr->find(); if(lt!=NULL) { color = COL_POWERLINE; } else { if( mode&MAP_HIDE_CONTOUR ) { color = gfx->palette_lookup(map_type_color[MAX_MAP_TYPE_WATER+2]); // lowest land color } else if( mode&MAP_CLIMATES ) { static uint8 climate_color[ 8 ] = { 0, COL_YELLOW, COL_LIGHT_GREEN, COL_GREEN, COL_DARK_GREEN, COL_DARK_YELLOW, COL_BROWN, COL_GREY4 }; color = gfx->palette_lookup( climate_color[ world->get_climate(gr->get_pos().get_2d()) ] ); } else { sint16 height = corner_sw(gr->get_grund_hang()); if( gr->get_hoehe() > world->get_groundwater() ) { color = calc_height_color( gr->get_hoehe() + height, world->get_groundwater() ); } else { color = calc_height_color( gr->get_hoehe() + height, gr->get_hoehe() + height - 1); } } } } break; } } return color; } bool minimap_t::calc_map_pixel(const grund_t *gr) { if (!gr) { return false; } const koord k = gr->get_pos().get_2d(); if(mode != MAP_PAX_DEST && gr->get_convoi_vehicle()) { set_map_color(k, COL_VEHICLE); return true; } switch (mode & ~MAP_MODE_FLAGS) { case MAP_PASSENGER: case MAP_MAIL: return false; // show usage case MAP_FREIGHT: // need to init the maximum? if (max_cargo == 0) { max_cargo = 1; calc_map(); return true; } else if (gr->hat_wege()) { // now calc again ... sint32 cargo = 0; // maximum two ways for one ground const weg_t* w = gr->get_weg_nr(0); if (w) { cargo = w->get_statistics(WAY_STAT_GOODS); const weg_t* w = gr->get_weg_nr(1); if (w) { cargo += w->get_statistics(WAY_STAT_GOODS); } if (cargo > max_cargo) { max_cargo = cargo; } set_map_color(k, calc_severity_color_log(cargo, max_cargo)); return true; } } break; // show traffic (=convois/month) case MAP_TRAFFIC: // need to init the maximum? if (max_passed == 0) { max_passed = 1; calc_map(); } else if (gr->hat_wege()) { // now calc again ... sint32 passed = 0; // maximum two ways for one ground const weg_t* w = gr->get_weg_nr(0); if (w) { passed = w->get_statistics(WAY_STAT_CONVOIS); if (weg_t* w = gr->get_weg_nr(1)) { passed += w->get_statistics(WAY_STAT_CONVOIS); } if (passed > max_passed) { max_passed = passed; } set_map_color(k, calc_severity_color_log(passed, max_passed)); return true; } } break; // show tracks: white: no electricity, red: electricity, yellow: signal case MAP_TRACKS: // show track if (gr->hat_weg(track_wt)) { const schiene_t* sch = (const schiene_t*)(gr->get_weg(track_wt)); // show signals if (sch->has_sign() || sch->has_signal()) { set_map_color(k, gfx->palette_lookup(COL_YELLOW)); return true; } else if (sch->is_electrified()) { set_map_color(k, gfx->palette_lookup(COL_RED)); return true; } else { set_map_color(k, gfx->palette_lookup(COL_WHITE)); return true; } } break; // show max speed (if there) case MAX_SPEEDLIMIT: if (gr->get_max_speed()) { set_map_color(k, calc_severity_color(gr->get_max_speed(), 450)); return true; } break; // find power lines case MAP_POWERLINES: if (const leitung_t* lt = gr->find()) { const sint32 saturated_demand = std::min(lt->get_net()->get_demand(), INT32_MAX); const sint32 saturated_supply = std::min(lt->get_net()->get_supply(), INT32_MAX); set_map_color(k, calc_severity_color(saturated_demand, saturated_supply)); return true; } break; case MAP_FOREST: if (gr->obj_count() > 1 && gr->obj_bei(gr->obj_count() - 1)->get_typ() == obj_t::baum) { set_map_color(k, gfx->palette_lookup(COL_GREEN)); return true; } break; case MAP_OWNER: // show ownership if (gr->is_halt()) { set_map_color(k, gfx->palette_lookup(gr->get_halt()->get_owner()->get_player_color1() + 3)); return true; } else if (weg_t* weg = gr->get_weg_nr(0)) { set_map_color(k, weg->get_owner() == NULL ? gfx->palette_lookup(COL_ORANGE) : gfx->palette_lookup(weg->get_owner()->get_player_color1() + 3)); return true; } if (gebaeude_t* gb = gr->find()) { if (gb->get_owner() != NULL) { set_map_color(k, gfx->palette_lookup(gb->get_owner()->get_player_color1() + 3)); return true; } } break; case MAP_LEVEL: if (max_building_level == 0) { // init maximum max_building_level = 1; calc_map(); return true; } else if (gr->get_typ() == grund_t::fundament) { if (gebaeude_t* gb = gr->find()) { if (gb->is_city_building()) { sint32 level = gb->get_tile()->get_desc()->get_level(); if (level > max_building_level) { max_building_level = level; } set_map_color(k, calc_severity_color(level, max_building_level)); return true; } } } break; default: return false; } return false; } void minimap_t::calc_map_pixel(const koord k) { // no pixels visible, so noting to calculate if(!is_visible) { return; } // always use to uppermost ground const planquadrat_t *plan=world->access(k); if(plan==NULL || plan->get_boden_count()==0) { return; } switch (mode & ~MAP_MODE_FLAGS) { // show passenger coverage // display coverage case MAP_PASSENGER: for (int i = 0; i < plan->get_haltlist_count(); i++) { halthandle_t halt = plan->get_haltlist()[i]; if (halt->get_pax_enabled() && !halt->get_pax_connections().empty()) { set_map_color(k, gfx->palette_lookup(halt->get_owner()->get_player_color1() + 3)); return; } } break; // show mail coverage // display coverage case MAP_MAIL: for (int i = 0; i < plan->get_haltlist_count(); i++) { halthandle_t halt = plan->get_haltlist()[i]; if (halt->get_mail_enabled() && !halt->get_mail_connections().empty()) { set_map_color(k, gfx->palette_lookup(halt->get_owner()->get_player_color1() + 3)); return; } } break; } if(grund_t::underground_mode == grund_t::ugm_all) { const grund_t* last_tunnel = 0; for (uint8 i = 1; i < plan->get_boden_count(); i++) { const grund_t* gr = plan->get_boden_bei(i); if (gr->get_typ()==grund_t::tunnelboden) { if (calc_map_pixel(gr)) { return; } last_tunnel = gr; } } if(last_tunnel) { set_map_color(k, calc_ground_color(last_tunnel)); } else { set_map_color(k, gfx->palette_lookup(COL_BLACK)); } } else if(grund_t::underground_mode == grund_t::ugm_level) { for (uint8 i = 0; i < plan->get_boden_count(); i++) { const grund_t* gr = plan->get_boden_bei(i); if ((gr->get_hoehe() == grund_t::underground_level || (i==0 && gr->get_hoehe() == grund_t::underground_level)) && calc_map_pixel(gr)) { return; } } grund_t* gr = plan->get_boden_in_hoehe(grund_t::underground_level); if (!gr) { gr = plan->get_kartenboden(); } if (gr->get_hoehe() <= grund_t::underground_level) { set_map_color(k, calc_ground_color(gr)); } else { set_map_color(k, gfx->palette_lookup(COL_BLACK)); } } else { for (uint8 i = 0; i < plan->get_boden_count(); i++) { const grund_t* gr = plan->get_boden_bei(i); if (calc_map_pixel(gr)) { return; } } // Nothing special => calculate ground color based on last index set_map_color(k, calc_ground_color(plan->get_boden_bei(plan->get_boden_count() - 1))); } } scr_size minimap_t::get_min_size() const { return get_max_size(); //scr_size(0,0); } scr_size minimap_t::get_max_size() const { scr_coord size = map_to_screen_coord( koord( world->get_size().x, 0 ) ); scr_coord down = map_to_screen_coord( koord( world->get_size().x, world->get_size().y ) ); size.y = down.y; if( isometric ) { size.x += zoom_in*2; } return scr_size(size.x, size.y); } void minimap_t::calc_map_size() { set_size( get_max_size() ); // of the gui_component to adjust scroll bars needs_redraw = true; } void minimap_t::calc_map() { // only use bitmap size like screen size scr_size minimap_size ( min( get_size().w, new_size.w ), min( get_size().h, new_size.h ) ); // actually the following line should reduce new/deletes, but does not work properly if( map_data==NULL || (sint16) map_data->get_width()!=minimap_size.w || (sint16) map_data->get_height()!=minimap_size.h ) { delete map_data; map_data = new array2d_tpl ( minimap_size.w,minimap_size.h); } cur_off = new_off; cur_size = new_size; needs_redraw = false; is_visible = true; // redraw the map if( !isometric ) { koord k; koord start_off = koord( (cur_off.x*zoom_out)/zoom_in, (cur_off.y*zoom_out)/zoom_in ); koord end_off = start_off+koord( ( map_data->get_width()*zoom_out)/zoom_in+1, ( map_data->get_height()*zoom_out)/zoom_in+1 ); for( k.y=start_off.y; k.yinit( gfx->palette_lookup(COL_BLACK) ); } koord k; for( k.y=0; k.y < world->get_size().y; k.y++ ) { for( k.x=0; k.x < world->get_size().x; k.x++ ) { calc_map_pixel(k); } } } } minimap_t::minimap_t() { map_data = NULL; zoom_in = 1; zoom_out = 1; isometric = false; show_network_load_factor = false; mode = MAP_TOWN; selected_city = NULL; cur_off = new_off = scr_coord(0,0); cur_size = new_size = scr_size(0,0); needs_redraw = true; transport_type_showed_on_map = simline_t::line; } minimap_t::~minimap_t() { delete map_data; } minimap_t *minimap_t::get_instance() { if(single_instance == NULL) { single_instance = new minimap_t(); } return single_instance; } void minimap_t::init() { delete map_data; map_data = NULL; needs_redraw = true; is_visible = false; calc_map_size(); max_building_level = max_cargo = max_passed = 0; max_tourist_ziele = max_waiting = max_origin = max_transfer = max_service = 1; last_schedule_counter = world->get_schedule_counter()-1; set_selected_cnv(convoihandle_t()); } void minimap_t::set_display_mode(MAP_DISPLAY_MODE new_mode) { mode = new_mode; needs_redraw = true; } void minimap_t::new_month() { needs_redraw = true; } void minimap_t::invalidate_map_lines_cache() { last_schedule_counter = world->get_schedule_counter() - 1; needs_redraw = true; } // these two are the only gui_container specific routines // handle event bool minimap_t::infowin_event(const event_t *ev) { const koord k = screen_to_map_coord(ev->mouse_pos); // get factory under mouse cursor last_world_pos = k; if (IS_WHEELDOWN(ev) || IS_WHEELUP(ev)) { change_zoom_factor(IS_WHEELUP(ev)); return true; } // recenter if(IS_LEFTRELEASE(ev) || ((IS_LEFTCLICK(ev) || IS_LEFTDRAG(ev)) && !env_t::leftdrag_in_minimap)) { world->get_viewport()->set_follow_convoi( convoihandle_t() ); const sint8 min_hgt = world->is_within_grid_limits(k) ? world->min_hgt(k) : 0; world->get_viewport()->change_world_position(koord3d(k,min_hgt),false); // change position bu do not touch underground mode return true; } return false; } // helper function for finding nearby factory const fabrik_t* minimap_t::get_factory_near( const koord, bool enlarge ) const { const fabrik_t *fab = fabrik_t::get_fab(last_world_pos); for( int i=0; i<4 && fab==NULL; i++ ) { fab = fabrik_t::get_fab( last_world_pos+koord::nesw[i] ); } if( enlarge ) { for( int i=0; i<4 && fab==NULL; i++ ) { fab = fabrik_t::get_fab( last_world_pos+koord::nesw[i]*2 ); } } return fab; } // helper function for redraw: factory connections const fabrik_t* minimap_t::draw_factory_connections(const fabrik_t* const fab, bool supplier_link, const scr_coord pos) const { if(fab) { PIXVAL color = supplier_link ? gfx->palette_lookup(COL_RED) : gfx->palette_lookup(COL_WHITE); scr_coord fabpos = map_to_screen_coord( fab->get_pos().get_2d() ) + pos; const vector_tpl& consumer = supplier_link ? fab->get_suppliers() : fab->get_consumer(); for(koord lieferziel : consumer) { const fabrik_t * fab2 = fabrik_t::get_fab(lieferziel); if (fab2) { const scr_coord end = map_to_screen_coord( lieferziel ) + pos; gfx->draw_line(fabpos.x, fabpos.y, end.x, end.y, color); gfx->draw_rect_clipped(end.x, end.y, 3, 3, ((world->get_ticks() >> 10) & 1) == 0 ? gfx->palette_lookup(COL_RED) : gfx->palette_lookup(COL_WHITE), true CLIP_NUM_DEFAULT); scr_coord boxpos = end + scr_coord(10, 0); const char * name = translator::translate(fab2->get_name()); int name_width = gfx->calc_text_width(name) + (LINESPACE/2); boxpos.x = clamp( boxpos.x, pos.x, pos.x+get_size().w-name_width ); gfx->draw_textbox3d_clipped(boxpos.x, boxpos.y, gfx->palette_lookup(5), gfx->palette_lookup(COL_WHITE), name, true CLIP_NUM_DEFAULT); } } } return fab; } // show the schedule on the minimap void minimap_t::set_selected_cnv( convoihandle_t c ) { current_cnv = c; schedule_cache.clear(); stop_cache.clear(); colore_idx = 0; add_to_schedule_cache( current_cnv, true ); last_schedule_counter = world->get_schedule_counter()-1; } // draw the map (and the overlays!) void minimap_t::draw(scr_coord pos) { // sanity check, needed for overlarge maps if( (new_off.x|new_off.y)<0 ) { new_off = cur_off; } if( (new_size.w|new_size.h)<0 ) { new_size = cur_size; } if( last_mode != mode ) { // only needing update, if last mode was also not about halts or background rendering ... needs_redraw = (mode^last_mode) & (~MAP_MODE_FLAGS | MAP_CLIMATES | MAP_HIDE_CONTOUR); if( (mode & MAP_LINES) == 0 || (mode^last_mode) & MAP_MODE_HALT_FLAGS ) { // rebuilt stop_cache needed stop_cache.clear(); if( (mode & MAP_LINES) && (last_mode & MAP_LINES) && current_cnv.is_bound() ) { schedule_cache.clear(); add_to_schedule_cache(current_cnv, true); needs_redraw = true; } } if( (mode & MAP_LINES) && (last_mode & MAP_LINES) == 0 && current_cnv.is_bound() ) { schedule_cache.clear(); add_to_schedule_cache(current_cnv, true); needs_redraw = true; } if( (mode^last_mode) & (MAP_PASSENGER|MAP_MAIL|MAP_FREIGHT|MAP_LINES) || (mode&MAP_LINES && stop_cache.empty()) ) { // rebuilt line display last_schedule_counter = world->get_schedule_counter()-1; } last_mode = mode; } if( needs_redraw || cur_off!=new_off || cur_size!=new_size ) { calc_map(); needs_redraw = false; } if( map_data==NULL) { return; } if( mode & MAP_PAX_DEST && selected_city!=NULL ) { const uint32 current_pax_destinations = selected_city->get_pax_destinations_new_change(); if( pax_destinations_last_change > current_pax_destinations ) { // new month started. calc_map(); } else if( pax_destinations_last_change < current_pax_destinations ) { // new pax_dest in city. const sparse_tpl &pax_dests = selected_city->get_pax_destinations_new(); koord pos, min, max; pax_dest_status_t dst_status; for( uint16 i = 0; i < pax_dests.get_data_count(); i++ ) { pax_dests.get_nonzero( i, pos, dst_status); min = koord((pos.x*world->get_size().x)/PAX_DESTINATIONS_SIZE, (pos.y*world->get_size().y)/PAX_DESTINATIONS_SIZE); max = koord(((pos.x+1)*world->get_size().x)/PAX_DESTINATIONS_SIZE, ((pos.y+1)*world->get_size().y)/PAX_DESTINATIONS_SIZE); pos = min; do { do { set_map_color(pos, pax_dest_status_colors[dst_status]); pos.y++; } while(pos.y < max.y); pos.x++; } while (pos.x < max.x); } } pax_destinations_last_change = selected_city->get_pax_destinations_new_change(); } if( (uint16)cur_size.w > map_data->get_width() ) { gfx->draw_rect_clipped( pos.x+new_off.x+map_data->get_width(), new_off.y+pos.y, 32767, map_data->get_height(), gfx->palette_lookup(COL_BLACK), true CLIP_NUM_DEFAULT); } if( (uint16)cur_size.h > map_data->get_height() ) { gfx->draw_rect_clipped( pos.x+new_off.x, pos.y+new_off.y+map_data->get_height(), 32767, 32767, gfx->palette_lookup(COL_BLACK), true CLIP_NUM_DEFAULT); } gfx->draw_array( cur_off.x+pos.x, new_off.y+pos.y, map_data->get_width(), map_data->get_height(), map_data->to_array()); if( !current_cnv.is_bound() && mode & MAP_LINES ) { vector_tpl linee; if( last_schedule_counter != world->get_schedule_counter() ) { // rebuild cache last_schedule_counter = world->get_schedule_counter(); schedule_cache.clear(); stop_cache.clear(); waypoint_hash.clear(); colore_idx = 0; for( int np = 0; np < MAX_PLAYER_COUNT; np++ ) { if( player_showed_on_map != -1 && player_showed_on_map != np ) { continue; } //cycle on players if( world->get_player( np ) && world->get_player( np )->simlinemgmt.get_line_count() > 0 ) { world->get_player( np )->simlinemgmt.get_lines( simline_t::line, &linee ); for( uint32 j = 0; j < linee.get_count(); j++ ) { //cycle on lines if( transport_type_showed_on_map != simline_t::line && linee[j]->get_linetype() != transport_type_showed_on_map ) { continue; } if( !is_matching_freight_catg( linee[j]->get_goods_catg_index() ) ) { continue; } // ware matches; now find at least a running convoi on this line ... convoihandle_t cnv; for( uint32 k = 0; k < linee[j]->count_convoys(); k++ ) { convoihandle_t test_cnv = linee[j]->get_convoy(k); if( test_cnv.is_bound() ) { int state = test_cnv->get_state(); if( state != convoi_t::INITIAL && state != convoi_t::ENTERING_DEPOT && state != convoi_t::SELF_DESTRUCT ) { cnv = test_cnv; break; } } } if( !cnv.is_bound() ) { continue; } int state = cnv->get_state(); if( state != convoi_t::INITIAL && state != convoi_t::ENTERING_DEPOT && state != convoi_t::SELF_DESTRUCT ) { add_to_schedule_cache( cnv, false ); } } } } // now add all unbound convois player_t * required_vehicle_owner = NULL; if (player_showed_on_map != -1) { required_vehicle_owner = world->get_player(player_showed_on_map); } for(convoihandle_t cnv : world->convoys() ) { if( !cnv.is_bound() || cnv->get_line().is_bound() ) { // not there or already part of a line continue; } if( required_vehicle_owner!= NULL && required_vehicle_owner != cnv->get_owner() ) { continue; } if( transport_type_showed_on_map != simline_t::line ) { if( transport_type_showed_on_map != simline_t::waytype_to_linetype(cnv->front()->get_waytype()) ) { continue; } } int state = cnv->get_state(); if( state != convoi_t::INITIAL && state != convoi_t::ENTERING_DEPOT && state != convoi_t::SELF_DESTRUCT ) { if( !is_matching_freight_catg(cnv->get_goods_catg_index()) ) { continue; } add_to_schedule_cache( cnv, false ); } } } /************ ATTENTION: The schedule pointers schedule in the line segments ****************** ************ are invalid after this point! ******************/ } //end MAP_LINES bool showing_schedule = false; if( mode & MAP_LINES ) { showing_schedule = !schedule_cache.empty(); } else { schedule_cache.clear(); colore_idx = 0; last_schedule_counter = world->get_schedule_counter()-1; } // since the schedule whitens out the background, we have to draw it first int offset = 1; koord last_start(0,0), last_end(0,0); bool diagonal = false; if( showing_schedule ) { // lighten background if( isometric ) { // isometric => lighten in three parts scr_coord p1 = map_to_screen_coord( koord(0,0) ); scr_coord p2 = map_to_screen_coord( koord( world->get_size().x, 0 ) ); scr_coord p3 = map_to_screen_coord( koord( world->get_size().x, world->get_size().y ) ); scr_coord p4 = map_to_screen_coord( koord( 0, world->get_size().y ) ); // top and bottom part const int toplines = min( p4.y, p2.y ); for( scr_coord_val y = 0; y < toplines; y++ ) { gfx->tint_rect( pos.x+p1.x-2*y, pos.y+y, 4*y+4, 1, gfx->palette_lookup(COL_WHITE), 75 ); gfx->tint_rect( pos.x+p3.x-2*y, pos.y+p3.y-y-1, 4*y+4, 1, gfx->palette_lookup(COL_WHITE), 75 ); } // center area if( p1.x < p3.x ) { for( scr_coord_val y = toplines; y < p3.y-toplines; y++ ) { gfx->tint_rect( pos.x+(y-toplines)*2, pos.y+y, 4*toplines+4, 1, gfx->palette_lookup(COL_WHITE), 75 ); } } else { for( scr_coord_val y = toplines; y < p3.y-toplines; y++ ) { gfx->tint_rect( pos.x+(y-toplines)*2, pos.y+p3.y-y-1, 4*toplines+4, 1, gfx->palette_lookup(COL_WHITE), 75 ); } } } else { // easier with rectangular maps ... gfx->tint_rect( cur_off.x+pos.x, cur_off.y+pos.y, map_data->get_width(), map_data->get_height(), gfx->palette_lookup(COL_WHITE), 75 ); } scr_coord k1,k2; // DISPLAY STATIONS AND AIRPORTS: moved here so station spots are not overwritten by lines drawn for(line_segment_t seg : schedule_cache ) { uint8 color = seg.colorcount; if( (event_get_last_control_shift() ^ tool_t::control_invert)==2 || current_cnv.is_bound() ) { // on control / single convoi use only player colors static uint8 last_color = color; color = seg.player->get_player_color1()+1; // all lines same thickness if same color if( color == last_color ) { offset = 0; } last_color = color; } if( seg.start != last_start || seg.end != last_end ) { last_start = seg.start; k1 = map_to_screen_coord( seg.start ); k1 += pos; last_end = seg.end; k2 = map_to_screen_coord( seg.end ); k2 += pos; // use same diagonal for all parallel segments diagonal = seg.start_diagonal; } // and finally draw ... line_segment_draw( seg.waytype, k1, seg.start_offset*offset, k2, seg.end_offset*offset, diagonal, gfx->palette_lookup(color) ); } } // display station information here (even without overlay) halthandle_t display_station; // only fill cache if needed if( mode & MAP_MODE_HALT_FLAGS && stop_cache.empty() ) { if( mode & MAP_ORIGIN ) { for(halthandle_t halt : haltestelle_t::get_alle_haltestellen() ) { if( halt->get_pax_enabled() || halt->get_mail_enabled() ) { stop_cache.append( halt ); } } } else if( mode & MAP_TRANSFER ) { for(halthandle_t halt : haltestelle_t::get_alle_haltestellen() ) { for (int catg = goods_manager_t::INDEX_PAS; catg != goods_manager_t::get_max_catg_index(); ++catg) { if (catg != goods_manager_t::INDEX_NONE && halt->is_transfer(catg)) { stop_cache.append(halt); break; } } } } else if( mode&MAP_STATUS || mode&MAP_SERVICE || mode&MAP_WAITING || mode&MAP_WAITCHANGE ) { for(halthandle_t halt : haltestelle_t::get_alle_haltestellen() ) { stop_cache.append( halt ); } } } // now draw stop cache // if needed to get new values sint32 new_max_waiting_change = 1; for(halthandle_t station : stop_cache ) { if( !station.is_bound() ) { // maybe deleted in the meanwhile continue; } int radius = 0; PIXVAL color; int diagonal_dist = 0; scr_coord temp_stop = map_to_screen_coord( station->get_basis_pos() ); temp_stop = temp_stop + pos; if( mode & MAP_STATUS ) { color = station->get_status_farbe(); radius = number_to_radius( station->get_capacity(0) ); } else if( mode & MAP_SERVICE ) { const sint32 service = (sint32)station->get_finance_history( 1, HALT_CONVOIS_ARRIVED ); if( service > max_service ) { max_service = service; } color = calc_severity_color_log( service, max_service ); radius = log2( (uint32)( (service << 7) / max_service ) ); } else if( mode & MAP_WAITING ) { const sint32 waiting = (sint32)station->get_finance_history( 0, HALT_WAITING ); if( waiting > max_waiting ) { max_waiting = waiting; } color = calc_severity_color_log( waiting, max_waiting ); radius = number_to_radius( waiting ); } else if( mode & MAP_WAITCHANGE ) { const sint32 waiting_diff = (sint32)(station->get_finance_history( 0, HALT_WAITING ) - station->get_finance_history( 1, HALT_WAITING )); if( waiting_diff > new_max_waiting_change ) { new_max_waiting_change = waiting_diff; } if( waiting_diff < -new_max_waiting_change ) { new_max_waiting_change = -waiting_diff; } const sint32 span = max(new_max_waiting_change,max_waiting_change); const sint32 diff = waiting_diff + span; color = calc_severity_color( diff, span*2 ) ; radius = number_to_radius( abs(waiting_diff) ); } else if( mode & MAP_ORIGIN ) { if( !station->get_pax_enabled() && !station->get_mail_enabled() ) { continue; } const sint32 pax_origin = (sint32)(station->get_finance_history( 1, HALT_HAPPY ) + station->get_finance_history( 1, HALT_UNHAPPY ) + station->get_finance_history( 1, HALT_NOROUTE )); if( pax_origin > max_origin ) { max_origin = pax_origin; } color = calc_severity_color_log( pax_origin, max_origin ); radius = number_to_radius( pax_origin ); } else if( mode & MAP_TRANSFER ) { const sint32 transfer = (sint32)(station->get_finance_history( 1, HALT_ARRIVED ) + station->get_finance_history( 1, HALT_DEPARTED )); if( transfer > max_transfer ) { max_transfer = transfer; } color = calc_severity_color_log( transfer, max_transfer ); radius = number_to_radius( transfer ); } else { const int stype = station->get_station_type(); color = gfx->palette_lookup(station->get_owner()->get_player_color1()+3); // invalid=0, loadingbay=1, railstation = 2, dock = 4, busstop = 8, airstop = 16, monorailstop = 32, tramstop = 64, maglevstop=128, narrowgaugestop=256 if( stype > 0 ) { radius = 1; if( stype & ~(haltestelle_t::loadingbay | haltestelle_t::busstop | haltestelle_t::tramstop) ) { radius = 3; } } // with control, show only circles if((event_get_last_control_shift() ^ tool_t::control_invert)==2) { // else elongate them ... const int key = station->get_basis_pos().x + station->get_basis_pos().y * world->get_size().x; diagonal_dist = waypoint_hash.get( key ).get_count(); if( diagonal_dist ) { diagonal_dist--; } diagonal_dist = (diagonal_dist*3)-1; } // show the mode of transport of the station if( skinverwaltung_t::station_type ) { int icon = 0; for( int type=0; type<9; type++ ) { if( (stype>>type)&1 ) { image_id img = skinverwaltung_t::station_type->get_image_id(type); if( img!=IMG_EMPTY ) { gfx->draw_color_img( img, temp_stop.x+diagonal_dist+4+(icon/2)*12, temp_stop.y+diagonal_dist+4+(icon&1)*12, station->get_owner()->get_player_nr(), false, false CLIP_NUM_DEFAULT); icon++; } } } } else { if( stype & haltestelle_t::airstop ) { display_airport( temp_stop.x+diagonal_dist, temp_stop.y+diagonal_dist, color ); } if( stype & haltestelle_t::dock ) { display_harbor( temp_stop.x+diagonal_dist, temp_stop.y+diagonal_dist, color ); } } } // avoid too small circles when zoomed out if( zoom_in > 1 ) { radius ++; } int out_radius = (radius == 0) ? 1 : radius; gfx->draw_filled_circle(temp_stop.x, temp_stop.y, radius, color ); gfx->draw_empty_circle (temp_stop.x, temp_stop.y, out_radius, gfx->palette_lookup(COL_BLACK)); if( diagonal_dist>0 ) { gfx->draw_filled_circle( temp_stop.x+diagonal_dist, temp_stop.y+diagonal_dist, radius, color ); gfx->draw_empty_circle ( temp_stop.x+diagonal_dist, temp_stop.y+diagonal_dist, out_radius, gfx->palette_lookup(COL_BLACK)); for( int i=1; i < diagonal_dist; i++ ) { gfx->draw_filled_circle( temp_stop.x+i, temp_stop.y+i, radius, color ); } out_radius = sqrt_i32( 2*out_radius+1 ); gfx->draw_line( temp_stop.x+out_radius, temp_stop.y-out_radius, temp_stop.x+out_radius+diagonal_dist, temp_stop.y-out_radius+diagonal_dist, gfx->palette_lookup(COL_BLACK) ); gfx->draw_line( temp_stop.x-out_radius, temp_stop.y+out_radius, temp_stop.x-out_radius+diagonal_dist, temp_stop.y+out_radius+diagonal_dist, gfx->palette_lookup(COL_BLACK) ); } if( koord_distance( last_world_pos, station->get_basis_pos() ) <= 2 ) { // draw stop name with an index if close to mouse display_station = station; } } max_waiting_change = new_max_waiting_change; // update waiting tendencies // if we do not do this here, vehicles would erase the town names // ADD: if CRTL key is pressed, temporary show the name if( mode & MAP_TOWN ) { const weighted_vector_tpl& staedte = world->get_cities(); const PIXVAL col = gfx->palette_lookup(showing_schedule ? COL_BLACK : COL_WHITE); for(stadt_t* const stadt : staedte ) { const char * name = stadt->get_name(); scr_coord p = map_to_screen_coord( stadt->get_pos() ); p += pos; gfx->draw_text_clipped( p.x, p.y, name, ALIGN_LEFT, col, true ); } } // draw city limit if( mode & MAP_CITYLIMIT ) { const PIXVAL col = gfx->palette_lookup(showing_schedule ? COL_DARK_BROWN : COL_ORANGE); // for all cities for(stadt_t* const stadt : world->get_cities() ) { koord k[4]; k[0] = stadt->get_linksoben(); // top left k[2] = stadt->get_rechtsunten(); // bottom right k[1] = koord(k[0].x, k[2].y); // bottom left k[3] = koord(k[2].x, k[0].y); // top right k[0] += koord(0, -1); // top left k[2] += koord(1, 0); // bottom right k[3] += koord(1, -1); // top right // calculate and draw the rotated coordinates scr_coord c[4]; for(uint i=0; idraw_line_dotted( c[0].x, c[0].y, c[1].x, c[1].y, 3, 3, col ); gfx->draw_line_dotted( c[1].x, c[1].y, c[2].x, c[2].y, 3, 3, col ); gfx->draw_line_dotted( c[2].x, c[2].y, c[3].x, c[3].y, 3, 3, col ); gfx->draw_line_dotted( c[3].x, c[3].y, c[0].x, c[0].y, 3, 3, col ); } } // since we do iterate the tourist info list, this must be done here // find tourist spots if( mode & MAP_TOURIST ) { for(gebaeude_t* const gb : world->get_attractions() ) { if( gb->get_first_tile() == gb ) { scr_coord gb_pos = map_to_screen_coord( gb->get_pos().get_2d() ); gb_pos = gb_pos + pos; int const pax = gb->get_passagier_level(); if( max_tourist_ziele < pax ) { max_tourist_ziele = pax; } PIXVAL color = calc_severity_color_log(gb->get_passagier_level(), max_tourist_ziele); int radius = max( (number_to_radius( pax*4 )*zoom_in)/zoom_out, 1 ); gfx->draw_filled_circle(gb_pos.x, gb_pos.y, radius, color); gfx->draw_empty_circle (gb_pos.x, gb_pos.y, radius, gfx->palette_lookup(COL_BLACK) ); } // otherwise larger attraction will be shown more often ... } } if( mode & MAP_FACTORIES ) { for(fabrik_t* const f : world->get_fab_list() ) { // find top-left tile position koord3d fab_tl_pos = f->get_pos(); if (grund_t *gr = world->lookup(f->get_pos())) { if (gebaeude_t* gb = gr->find()) { fab_tl_pos = gb->get_pos() - gb->get_tile()->get_offset(); } } scr_coord fab_pos = map_to_screen_coord( fab_tl_pos.get_2d() ); fab_pos = fab_pos + pos; koord size = f->get_desc()->get_building()->get_size(f->get_rotate()); sint16 x_size = max( 5, size.x*zoom_in ); sint16 y_size = max( 5, size.y*zoom_in ); gfx->draw_rect_clipped( fab_pos.x-1, fab_pos.y-1, x_size+2, y_size+2, gfx->palette_lookup(COL_BLACK), false CLIP_NUM_DEFAULT); gfx->draw_rect_clipped( fab_pos.x, fab_pos.y, x_size, y_size, f->get_color(), false CLIP_NUM_DEFAULT); } } if( mode & MAP_DEPOT ) { for(depot_t* const d : depot_t::get_depot_list() ) { if( d->get_owner() == world->get_active_player() ) { scr_coord depot_pos = map_to_screen_coord( d->get_pos().get_2d() ); depot_pos = depot_pos + pos; // offset of one to avoid static uint8 depot_typ_to_color[19]={ COL_ORANGE, COL_YELLOW, COL_RED, 0, 0, 0, 0, 0, 0, COL_PURPLE, COL_DARK_RED, COL_DARK_ORANGE, 0, 0, 0, 0, 0, 0, COL_LIGHT_RED }; gfx->draw_filled_circle(depot_pos.x, depot_pos.y, 4, gfx->palette_lookup(depot_typ_to_color[d->get_typ() - obj_t::bahndepot]) ); gfx->draw_empty_circle (depot_pos.x, depot_pos.y, 4, gfx->palette_lookup(COL_BLACK) ); } } } if( display_station.is_bound() ) { scr_coord temp_stop = map_to_screen_coord( display_station->get_basis_pos() ); temp_stop = temp_stop + pos; gfx->draw_textbox3d_clipped( temp_stop.x + 10, temp_stop.y + 7, gfx->palette_lookup(display_station->get_owner()->get_player_color1()+3), gfx->palette_lookup(COL_WHITE), display_station->get_name(), false CLIP_NUM_DEFAULT); } // zoom/resize "selection box" in map // this must be rotated by 45 degree (sin45=cos45=0,5*sqrt(2)=0.707...) const sint16 raster = gfx->get_tile_raster_width(); const scr_size screen = gfx->get_screen_size(); // calculate and draw the rotated coordinates koord ij = world->get_viewport()->get_world_position(); const koord diff = koord( screen.w/(2*raster), screen.h/raster ); koord view[4]; scr_coord test[4]; // default coordinates - may be off if screen shows high mountains view[0] = ij + koord( -diff.y+diff.x, -diff.y-diff.x ); view[1] = ij + koord( -diff.y-diff.x, -diff.y+diff.x ); view[2] = ij + koord( diff.y-diff.x, diff.y+diff.x ); view[3] = ij + koord( diff.y+diff.x, diff.y-diff.x ); // try to find tile under the four corners of the screen test[0] = scr_coord(screen.w,0); test[1] = scr_coord(0,0); test[2] = scr_coord(0,screen.h); test[3] = scr_coord(screen.w, screen.h); for(int i=0; i<4; i++) { sint32 dummy1, dummy2; if (grund_t *gr = world->get_viewport()->get_ground_on_screen_coordinate( test[i], dummy1, dummy2 ) ) { view[i] = gr->get_pos().get_2d(); } } scr_coord c[4]; // translate to coordinates in the minimap for( int i=0; i<4; i++ ) { c[i] = map_to_screen_coord( view[i] ) + pos; } for( int i=0; i<4; i++ ) { gfx->draw_line( c[i].x, c[i].y, c[(i+1)%4].x, c[(i+1)%4].y, gfx->palette_lookup(showing_schedule?COL_RED:COL_YELLOW)); } if( !showing_schedule ) { // Add factory name tooltips and draw factory connections, if on a factory const fabrik_t* const fab = get_factory_near(last_world_pos, (mode & MAP_FACTORIES)); if(fab) { if (mode & MAP_FACTORIES) { draw_factory_connections(fab,event_get_last_control_shift() & 1, pos); } scr_coord fabpos = map_to_screen_coord( fab->get_pos().get_2d() ); scr_coord boxpos = fabpos + scr_coord(10, 0); const char * name = translator::translate(fab->get_name()); int name_width = gfx->calc_text_width(name) + (LINESPACE/2); boxpos.x = clamp( boxpos.x, 0, 0+get_size().w-name_width ); boxpos += pos; gfx->draw_textbox3d_clipped(boxpos.x, boxpos.y, gfx->palette_lookup(10), gfx->palette_lookup(COL_WHITE), name, true CLIP_NUM_DEFAULT); } for (int i = win_get_open_count() - 1; i >= 0; i--) { gui_frame_t *g = win_get_index(i); if(g->get_rdwr_id()== magic_factory_info) { // is a factory info window const fabrik_t * const fab = dynamic_cast(g)->get_factory(); draw_factory_connections(fab, true, pos); draw_factory_connections(fab, false, pos); break; // only show top window } } } if (current_cnv.is_bound()) { scr_coord_val offset = zoom_in / zoom_out /2; scr_coord_val wd = max( zoom_in/zoom_out, 1) * 2 + 1; // to have it always odd for( int i = 0; i < current_cnv->get_vehicle_count(); i++ ) { const scr_coord veh_pos = map_to_screen_coord(current_cnv->get_vehicle(i)->get_pos().get_2d()) + pos; gfx->draw_rect_clipped(veh_pos.x-(wd/2)+offset, veh_pos.y-(wd/2)+offset, wd, wd, gfx->palette_lookup(COL_MAGENTA), true CLIP_NUM_DEFAULT); } } } void minimap_t::set_selected_city( const stadt_t* _city ) { if( selected_city != _city ) { selected_city = _city; if( _city ) { pax_destinations_last_change = _city->get_pax_destinations_new_change(); } calc_map(); } } void minimap_t::rdwr(loadsave_t *file) { file->rdwr_short(zoom_out); file->rdwr_short(zoom_in); file->rdwr_bool(isometric); } bool minimap_t::is_matching_freight_catg(const minivec_tpl &goods_catg_index) { // does this line/convoi has a matching freight if( freight_type_group_index_showed_on_map == goods_manager_t::passengers ) { return goods_catg_index.is_contained(goods_manager_t::INDEX_PAS); } else if( freight_type_group_index_showed_on_map == goods_manager_t::mail ) { return goods_catg_index.is_contained(goods_manager_t::INDEX_MAIL); } else if( freight_type_group_index_showed_on_map == goods_manager_t::none ) { // all freights but not pax or mail for( uint8 i = 0; i < goods_catg_index.get_count(); i++ ) { if( goods_catg_index[i] > goods_manager_t::INDEX_NONE ) { return true; } } return false; } else if( freight_type_group_index_showed_on_map != NULL ) { for( uint8 i = 0; i < goods_catg_index.get_count(); i++ ) { if( goods_catg_index[i] == freight_type_group_index_showed_on_map->get_catg_index() ) { return true; } } return false; } // NULL show all but obey modes if( mode & MAP_PASSENGER ) { return goods_catg_index.is_contained(goods_manager_t::INDEX_PAS); } else if( mode & MAP_MAIL ) { return goods_catg_index.is_contained(goods_manager_t::INDEX_MAIL); } else if( mode & MAP_FREIGHT ) { // all freights but not pax or mail for( uint8 i = 0; i < goods_catg_index.get_count(); i++ ) { if( goods_catg_index[i]>2 ) { return true; } } return false; } // all true return true; } simutrans-124.5/src/simutrans/gui/minimap.h000066400000000000000000000145521520675460500207760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_MINIMAP_H #define GUI_MINIMAP_H #include "components/gui_component.h" #include "../halthandle.h" #include "../simline.h" #include "../convoihandle.h" #include "../dataobj/schedule.h" #include "../tpl/array2d_tpl.h" #include "../tpl/vector_tpl.h" class karte_ptr_t; class fabrik_t; class grund_t; class stadt_t; class player_t; class schedule_t; class loadsave_t; class goods_desc_t; #define MAX_SEVERITY_COLORS 10 /** * This class is used to render the actual minimap. * Implemented as singleton. */ class minimap_t : public gui_component_t { public: enum MAP_DISPLAY_MODE { PLAIN = 0, MAP_TOWN = 1 << 0, MAP_PASSENGER = 1 << 1, MAP_MAIL = 1 << 2, MAP_FREIGHT = 1 << 3, MAP_STATUS = 1 << 4, MAP_SERVICE = 1 << 5, MAP_TRAFFIC = 1 << 6, MAP_ORIGIN = 1 << 7, MAP_TRANSFER = 1 << 8, MAP_WAITING = 1 << 9, MAP_TRACKS = 1 << 10, MAX_SPEEDLIMIT = 1 << 11, MAP_POWERLINES = 1 << 12, MAP_TOURIST = 1 << 13, MAP_FACTORIES = 1 << 14, MAP_DEPOT = 1 << 15, MAP_FOREST = 1 << 16, MAP_CITYLIMIT = 1 << 17, MAP_PAX_DEST = 1 << 18, MAP_OWNER = 1 << 19, MAP_LINES = 1 << 20, MAP_LEVEL = 1 << 21, MAP_WAITCHANGE = 1 << 22, MAP_HIDE_CONTOUR = 1 << 23, MAP_CLIMATES = 1 << 24, MAP_MODE_HALT_FLAGS = (MAP_STATUS|MAP_SERVICE|MAP_ORIGIN|MAP_TRANSFER|MAP_WAITING|MAP_WAITCHANGE), MAP_MODE_FLAGS = (MAP_TOWN|MAP_CITYLIMIT|MAP_STATUS|MAP_SERVICE|MAP_WAITING|MAP_WAITCHANGE|MAP_TRANSFER|MAP_LINES|MAP_FACTORIES|MAP_ORIGIN|MAP_DEPOT|MAP_TOURIST|MAP_PAX_DEST|MAP_HIDE_CONTOUR|MAP_CLIMATES) }; private: static karte_ptr_t world; minimap_t(); static minimap_t *single_instance; /// the terrain map array2d_tpl *map_data; void set_map_color_clip( sint16 x, sint16 y, PIXVAL color ); /// all stuff connected with schedule display class line_segment_t { public: koord start, end; schedule_t *schedule; player_t *player; waytype_t waytype; uint8 colorcount; uint8 start_offset; uint8 end_offset; bool start_diagonal; line_segment_t() {} line_segment_t( koord s, uint8 so, koord e, uint8 eo, schedule_t *sched, player_t *p, uint8 cc, bool diagonal ); bool operator==(const line_segment_t & other) const; }; /// Ordering based on first start then end coordinate class LineSegmentOrdering { public: bool operator()(const minimap_t::line_segment_t& a, const minimap_t::line_segment_t& b) const; }; vector_tpl schedule_cache; convoihandle_t current_cnv; uint8 last_schedule_counter; vector_tpl stop_cache; /// adds a schedule to cache void add_to_schedule_cache( convoihandle_t cnv, bool with_waypoints ); /** * 0: normal * everything else: special map */ static MAP_DISPLAY_MODE mode; static MAP_DISPLAY_MODE last_mode; static const uint8 severity_color[MAX_SEVERITY_COLORS]; koord screen_to_map_coord(const scr_coord&) const; /// for passenger destination display const stadt_t *selected_city; uint32 pax_destinations_last_change; koord last_world_pos; /** * current and new offset and size (to avoid drawing invisible parts) * * gui_component_t::size is always equal to max_size. * * These are size and offset of visible part of map. * We only show and compute this. */ scr_coord cur_off, new_off; scr_size cur_size, new_size; /// true, if full redraw is needed bool needs_redraw; const fabrik_t* get_factory_near(koord pos, bool large_area) const; const fabrik_t* draw_factory_connections(const fabrik_t* const fab, bool supplier_link, const scr_coord pos) const; static sint32 max_cargo; static sint32 max_passed; /// the zoom factors sint16 zoom_out, zoom_in; /// if true, draw the map with 45 degree rotation bool isometric; bool is_matching_freight_catg(const minivec_tpl &goods_catg_index); /// nonstatic, if we have someday many maps ... void set_map_color(koord k, PIXVAL color); public: scr_coord map_to_screen_coord(const koord &k) const; void toggle_isometric() { isometric = !isometric; } bool is_isometric() const { return isometric; } static bool is_visible; bool show_network_load_factor; int player_showed_on_map; int transport_type_showed_on_map; const goods_desc_t *freight_type_group_index_showed_on_map; /** * returns a color based on an amount (high amount/scale -> color shifts from green to red) */ static PIXVAL calc_severity_color(sint32 amount, sint32 scale); /** * returns a color based on an amount (high amount/scale -> color shifts from green to red) * but using log scale */ static PIXVAL calc_severity_color_log(sint32 amount, sint32 scale); /** * returns a color based on the current height */ static PIXVAL calc_height_color(const sint16 height, const sint16 groundwater); /// needed for town passenger map static PIXVAL calc_ground_color (const grund_t *gr); /// we are single instance ... static minimap_t *get_instance(); // HACK! since we cannot set cleanly the current offset/size, we use this helper function void set_xy_offset_size( scr_coord off, scr_size size ) { new_off = off; new_size = size; } /// update color with render mode (but few are ignored ... ) void calc_map_pixel(const koord k); /// update color with render mode (but for that kind of ground) // true for bool calc_map_pixel(const grund_t *gr); void calc_map(); /// calculates the current size of the map (but do not change anything else) void calc_map_size(); ~minimap_t(); void init(); void set_display_mode(MAP_DISPLAY_MODE new_mode); MAP_DISPLAY_MODE get_display_mode() const { return mode; } /// updates the map (if needed) void new_month(); void invalidate_map_lines_cache(); bool infowin_event(event_t const*) OVERRIDE; void draw(scr_coord pos) OVERRIDE; void set_selected_cnv( convoihandle_t c ); void set_selected_city( const stadt_t* _city ); bool is_city_selected(const stadt_t* city) const { return selected_city == city; } /** * @returns true if zoom factors changed */ bool change_zoom_factor(bool magnify); void get_zoom_factors(sint16 &zoom_out_, sint16 &zoom_in_) const { zoom_in_ = zoom_in; zoom_out_ = zoom_out; } void rdwr(loadsave_t *file); scr_size get_min_size() const OVERRIDE; scr_size get_max_size() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/money_frame.cc000066400000000000000000000444261520675460500220060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "money_frame.h" #include "ai_option.h" #include "headquarter_info.h" #include "simwin.h" #include "../world/simworld.h" #include "../player/ai_scripted.h" #include "../simdebug.h" #include "../display/simgraph.h" #include "../simcolor.h" #include "../utils/simstring.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/scenario.h" #include "../dataobj/loadsave.h" #include "components/gui_button_to_chart.h" // for headquarter construction only ... #include "../tool/simmenu.h" // remembers last settings static vector_tpl bFilterStates; #define BUTTONSPACE max(D_BUTTON_HEIGHT, LINESPACE) static const char *cost_type_name[MAX_PLAYER_COST_BUTTON] = { "Transported", "Revenue", "Operation", "Maintenance", "Road toll", "Ops Profit", "New Vehicles", "Construction_Btn", "Gross Profit", "Cash", "Assets", "Margin (%%)", "Net Wealth" }; static const uint8 cost_type_color[MAX_PLAYER_COST_BUTTON] = { COL_TRANSPORTED, COL_REVENUE, COL_OPERATION, COL_MAINTENANCE, COL_TOLL, COL_OPS_PROFIT, COL_NEW_VEHICLES, COL_CONSTRUCTION, COL_PROFIT, COL_CASH, COL_VEHICLE_ASSETS, COL_MARGIN, COL_WEALTH }; static const uint8 cost_type[3*MAX_PLAYER_COST_BUTTON] = { ATV_TRANSPORTED, TT_ALL, gui_chart_t::STANDARD, // all transported goods ATV_REVENUE, TT_ALL, gui_chart_t::MONEY, // Income ATV_RUNNING_COST, TT_ALL, gui_chart_t::MONEY, // Vehicle running costs ATV_INFRASTRUCTURE_MAINTENANCE, TT_ALL, gui_chart_t::MONEY, // Upkeep ATV_WAY_TOLL, TT_ALL, gui_chart_t::MONEY, ATV_OPERATING_PROFIT, TT_ALL, gui_chart_t::MONEY, ATV_NEW_VEHICLE, TT_ALL, gui_chart_t::MONEY, // New vehicles ATV_CONSTRUCTION_COST, TT_ALL, gui_chart_t::MONEY, // Construction ATV_PROFIT, TT_ALL, gui_chart_t::MONEY, ATC_CASH, TT_MAX, gui_chart_t::MONEY, // Cash ATV_NON_FINANCIAL_ASSETS, TT_ALL, gui_chart_t::MONEY, // value of all vehicles and buildings ATV_PROFIT_MARGIN, TT_ALL, gui_chart_t::PERCENT, ATC_NETWEALTH, TT_MAX, gui_chart_t::MONEY, // Total Cash + Assets }; static const sint8 cell_to_buttons[] = { 0, -1, -1, -1, -1, 1, -1, -1, -1, -1, 2, -1, -1, -1, -1, 3, -1, -1, -1, -1, 4, -1, -1, -1, -1, 5, -1, -1, 9, -1, 6, -1, -1, 10, -1, 7, -1, -1, 11, -1, 8, -1, -1, 12, -1 }; // money label types: tt, atv, current/previous, type static const uint16 label_type[] = { TT_ALL, ATV_TRANSPORTED, 0, gui_chart_t::STANDARD, TT_ALL, ATV_TRANSPORTED, 1, gui_chart_t::STANDARD, TT_ALL, ATV_REVENUE, 0, gui_chart_t::MONEY, TT_ALL, ATV_REVENUE, 1, gui_chart_t::MONEY, TT_ALL, ATV_RUNNING_COST, 0, gui_chart_t::MONEY, TT_ALL, ATV_RUNNING_COST, 1, gui_chart_t::MONEY, TT_ALL, ATV_INFRASTRUCTURE_MAINTENANCE, 0, gui_chart_t::MONEY, TT_ALL, ATV_INFRASTRUCTURE_MAINTENANCE, 1, gui_chart_t::MONEY, TT_ALL, ATV_WAY_TOLL, 0, gui_chart_t::MONEY, TT_ALL, ATV_WAY_TOLL, 1, gui_chart_t::MONEY, TT_ALL, ATV_OPERATING_PROFIT, 0, gui_chart_t::MONEY, TT_ALL, ATV_OPERATING_PROFIT, 1, gui_chart_t::MONEY, TT_ALL, ATV_NEW_VEHICLE, 0, gui_chart_t::MONEY, TT_ALL, ATV_NEW_VEHICLE, 1, gui_chart_t::MONEY, TT_ALL, ATV_CONSTRUCTION_COST, 0, gui_chart_t::MONEY, TT_ALL, ATV_CONSTRUCTION_COST, 1, gui_chart_t::MONEY, TT_ALL, ATV_PROFIT, 0, gui_chart_t::MONEY, TT_ALL, ATV_PROFIT, 1, gui_chart_t::MONEY, TT_MAX, ATC_CASH, 0, gui_chart_t::MONEY, TT_ALL, ATV_NON_FINANCIAL_ASSETS, 0, gui_chart_t::MONEY, TT_ALL, ATV_PROFIT_MARGIN, 0, gui_chart_t::PERCENT, TT_MAX, ATC_NETWEALTH, 0, gui_chart_t::MONEY }; static const sint8 cell_to_moneylabel[] = { -1, 0, 1, -1, -1, -1, 2, 3, -1, -1, -1, 4, 5, -1, -1, -1, 6, 7, -1, -1, -1, 8, 9, -1, -1, -1, 10, 11, -1, 18, -1, 12, 13, -1, 19, -1, 14, 15, -1, 20, -1, 16, 17, -1, 21, }; /// Helper method to query data from players statistics sint64 money_frame_t::get_statistics_value(int tt, uint8 type, int yearmonth, bool monthly) { const finance_t* finance = player->get_finance(); if (tt == TT_MAX) { return monthly ? finance->get_history_com_month(yearmonth, type) : finance->get_history_com_year( yearmonth, type); } else { assert(0 <= tt && tt < TT_MAX); return monthly ? finance->get_history_veh_month((transport_type)tt, yearmonth, type) : finance->get_history_veh_year( (transport_type)tt, yearmonth, type); } } class money_frame_label_t : public gui_label_buf_t { uint8 transport_type; uint8 type; uint8 label_type; uint8 index; bool monthly; public: money_frame_label_t(uint8 tt, uint8 t, uint8 lt, uint8 i, bool mon) : gui_label_buf_t(lt == gui_chart_t::STANDARD ? SYSCOL_TEXT : MONEY_PLUS, lt != gui_chart_t::MONEY ? gui_label_t::right : gui_label_t::money_right) , transport_type(tt), type(t), label_type(lt), index(i), monthly(mon) { } void update(money_frame_t *mf) { uint8 tt = transport_type == TT_ALL ? mf->transport_type_option : transport_type; sint64 value = mf->get_statistics_value(tt, type, index, monthly ? 1 : 0); PIXVAL color = value >= 0 ? (value > 0 ? MONEY_PLUS : SYSCOL_TEXT_UNUSED) : MONEY_MINUS; switch (label_type) { case gui_chart_t::MONEY: buf().append_money(value / 100.0); break; case gui_chart_t::PERCENT: buf().append(value / 100.0, 2); buf().append("%"); break; default: buf().append(value * 1.0, 0); } gui_label_buf_t::update(); set_color(color); } }; void money_frame_t::fill_chart_tables() { // fill tables for chart curves for (int i = 0; iget_finance()->get_maintenance_with_bits((transport_type)ttoption) != 0) { return false; } // search for any non-zero values for (int i = 0; iget_player(0)!=player) { money_frame_title.printf(translator::translate("Finances of %s"), translator::translate(player->get_name()) ); set_name(money_frame_title); } this->player = player; set_table_layout(1,0); // bankruptcy notice add_component(&warn); warn.set_visible(false); // scenario name if(player->get_player_nr()!=1 && welt->get_scenario()->active()) { add_table(3,1); new_component("Scenario_", SYSCOL_TEXT_HIGHLIGHT); add_component(&scenario_desc); add_component(&scenario_completion); end_table(); } // select transport type gui_aligned_container_t *top = add_table(4,1); { new_component("Show finances for transport type"); transport_type_c.set_focusable( false ); for(int i=0, count=0; i(translator::translate(finance_t::transport_type_values[i]), SYSCOL_TEXT); transport_types[ count++ ] = i; } } add_component(&transport_type_c); transport_type_c.add_listener( this ); transport_type_c.set_selection(0); top->set_focus( &transport_type_c ); set_focus(top); new_component(); // headquarter/ai settings button/ai info string if (player->get_ai_id() != player_t::HUMAN && player->get_ai_id() != player_t::AI_SCRIPTED) { headquarter.init(button_t::roundbox, "Configure AI"); headquarter.add_listener(this); headquarter.set_tooltip("Configure AI setttings"); add_component(&headquarter); } else if (player->get_ai_id() == player_t::HUMAN) { koord pos = player->get_headquarter_pos(); headquarter.set_text(pos != koord::invalid ? "show HQ" : "build HQ"); headquarter.set_tooltip(NULL); if (pos == koord::invalid) { if (player == welt->get_active_player()) { // reuse tooltip from tool_headquarter_t const char* c = tool_t::general_tool[TOOL_HEADQUARTER]->get_tooltip(player); if (c) { // only true, if the headquarter can be built/updated headquarter_tooltip.clear(); headquarter_tooltip.append(c); headquarter.set_tooltip(headquarter_tooltip); } } } headquarter.add_listener(this); add_component(&headquarter); } else { new_component(dynamic_cast(player)->get_ai_name()); } } end_table(); // tab panels // tab (month/year) year_month_tabs.add_tab( &container_year, translator::translate("Years")); year_month_tabs.add_tab( &container_month, translator::translate("Months")); year_month_tabs.add_listener(this); add_component(&year_month_tabs); // fill both containers gui_aligned_container_t *current = &container_year; gui_chart_t *current_chart = &chart; // .. put the same buttons in both containers button_t* buttons[MAX_PLAYER_COST_BUTTON]; for(uint8 i = 0; i < 2 ; i++) { uint8 k = 0; current->set_table_layout(1,0); current->add_table(5,10); // first row: some labels current->new_component(); current->new_component(i==0 ? "This Year" : "This Month", SYSCOL_TEXT_HIGHLIGHT); current->new_component(i==0 ? "Last Year" : "Last Month", SYSCOL_TEXT_HIGHLIGHT); current->new_component(); current->new_component(); // all other rows: mix of buttons and money-labels for(uint8 r = 0; r < 9; r++) { for(uint8 c = 0; c < 5; c++, k++) { sint8 cost = cell_to_buttons[k]; sint8 l = cell_to_moneylabel[k]; // button + chart line if (cost >=0 ) { // add chart line const int curve_type = cost_type[3*cost+2]; const int curve_precision = curve_type == gui_chart_t::STANDARD ? 0 : 2; sint16 curve = i == 0 ? chart.add_curve( gfx->palette_lookup(cost_type_color[cost]), *chart_table_year, MAX_PLAYER_COST_BUTTON, cost, MAX_PLAYER_HISTORY_YEARS, curve_type, false, true, curve_precision) : mchart.add_curve( gfx->palette_lookup(cost_type_color[cost]), *chart_table_month, MAX_PLAYER_COST_BUTTON, cost, MAX_PLAYER_HISTORY_MONTHS, curve_type, false, true, curve_precision); // add button button_t *b; if (i == 0) { b = current->new_component(); b->init(button_t::box_state_automatic | button_t::flexible, cost_type_name[cost]); b->background_color = gfx->palette_lookup(cost_type_color[cost]); b->pressed = false; buttons[cost] = b; } else { b = buttons[cost]; current->add_component(b); } button_to_chart.append(b, current_chart, curve); } else if (l >= 0) { // money_frame_label_t(uint8 tt, uint8 t, uint8 lt, uint8 i, bool mon) money_labels.append( current->new_component(label_type[4*l], label_type[4*l+1], label_type[4*l+3], label_type[4*l+2], i==1) ); } else { if (r >= 2 && r<=4 && c == 4) { switch(r) { case 2: current->new_component("This Month", SYSCOL_TEXT_HIGHLIGHT); break; case 3: current->add_component(&maintenance_money); break; case 4: current->new_component("This Year", SYSCOL_TEXT_HIGHLIGHT); } } else { current->new_component(); } } } } current->end_table(); current->add_component(current_chart); current = &container_month; current_chart = &mchart; } // recover button states if (bFilterStates.get_count() > 0) { for(uint8 i = 0; iget_button()->pressed = bFilterStates[i] > 0; button_to_chart[i]->update(); } } chart.set_min_size(scr_size(0 ,8*BUTTONSPACE)); chart.set_dimension(MAX_PLAYER_HISTORY_YEARS, 10000); chart.set_seed(welt->get_last_year()); chart.set_background(SYSCOL_CHART_BACKGROUND); mchart.set_min_size(scr_size(0,8*BUTTONSPACE)); mchart.set_dimension(MAX_PLAYER_HISTORY_MONTHS, 10000); mchart.set_seed(0); mchart.set_background(SYSCOL_CHART_BACKGROUND); update_labels(); reset_min_windowsize(); set_windowsize(get_min_windowsize()); set_resizemode(diagonal_resize); } money_frame_t::~money_frame_t() { bFilterStates.clear(); // save button states for(gui_button_to_chart_t* b2c : button_to_chart.list()) { bFilterStates.append( b2c->get_button()->pressed ? 1 : 0); } } void money_frame_t::update_labels() { for(money_frame_label_t* lb : money_labels) { lb->update(this); } // scenario if(player->get_player_nr()!=1 && welt->get_scenario()->active() && welt->get_scenario()->description_text) { // the text are updated asynchron from the scenario welt->get_scenario()->update_scenario_texts(scenario_t::DESCRIPTION); scenario_desc.buf().append( welt->get_scenario()->description_text ); scenario_desc.buf().append( ":" ); scenario_desc.update(); sint32 percent = welt->get_scenario()->get_completion(player->get_player_nr()); if (percent >= 0) { scenario_completion.buf().printf(translator::translate("Scenario complete: %i%%"), percent ); } else { scenario_completion.buf().printf(translator::translate("Scenario lost!")); } scenario_completion.update(); } if (player->get_ai_id() == player_t::HUMAN) { koord pos = player->get_headquarter_pos(); headquarter.set_text(pos != koord::invalid ? "show HQ" : "build HQ"); headquarter.set_tooltip(NULL); if (pos == koord::invalid) { if (player == welt->get_active_player()) { // reuse tooltip from tool_headquarter_t if (const char* c = tool_t::general_tool[TOOL_HEADQUARTER]->get_tooltip(player)) { // only true, if the headquarter can be built/updated headquarter_tooltip.clear(); headquarter_tooltip.append(c); headquarter.set_tooltip(headquarter_tooltip); } } } headquarter.enable(player == welt->get_active_player()); } // current maintenance double maintenance = player->get_finance()->get_maintenance_with_bits((transport_type)transport_type_option) / 100.0; maintenance_money.append_money(-maintenance); maintenance_money.update(); // bankruptcy warning bool visible = warn.is_visible(); if(player->get_finance()->get_history_com_year(0, ATC_NETWEALTH)<0) { warn.set_color( MONEY_MINUS ); warn.buf().append( translator::translate("Company bankrupt") ); warn.set_visible(true); } else if( player->get_finance()->get_history_com_year(0, ATC_NETWEALTH)*10 < welt->get_settings().get_starting_money(welt->get_current_month()/12) ){ warn.set_color( MONEY_MINUS ); warn.buf().append( translator::translate("Net wealth near zero") ); warn.set_visible(true); } else if( player->get_account_overdrawn() ) { warn.set_color( SYSCOL_TEXT_STRONG ); warn.buf().printf(translator::translate("On loan since %i month(s)"), player->get_account_overdrawn() ); warn.set_visible(true); } else { warn.set_visible(false); } warn.update(); if (visible != warn.is_visible()) { resize(scr_size(0,0)); } } void money_frame_t::set_windowsize(scr_size size) { gui_frame_t::set_windowsize(size); // recompute the active container if (year_month_tabs.get_active_tab_index() == 0) { container_year.set_size( container_year.get_size() ); } else { container_month.set_size( container_month.get_size() ); } } void money_frame_t::draw(scr_coord pos, scr_size size) { player->get_finance()->calc_finance_history(); fill_chart_tables(); update_labels(); // update chart seed chart.set_seed(welt->get_last_year()); gui_frame_t::draw(pos, size); } bool money_frame_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { if( comp == &headquarter ) { if( player->get_ai_id()!=player_t::HUMAN ) { create_win( new ai_option_t(player), w_info, magic_ai_options_t+player->get_player_nr() ); } else { if (player->get_headquarter_pos() == koord::invalid) { welt->set_tool( tool_t::general_tool[TOOL_HEADQUARTER], player ); } else { // open dedicated HQ window create_win( new headquarter_info_t(player), w_info, magic_headquarter+player->get_player_nr() ); } } return true; } if (comp == &year_month_tabs) { if (year_month_tabs.get_active_tab_index() == 0) { container_year.set_size( container_year.get_size() ); } else { container_month.set_size( container_month.get_size() ); } } if( comp == &transport_type_c) { const sint32 tmp = transport_type_c.get_selection(); if((0 <= tmp) && (tmp < (sint32)transport_type_c.count_elements())) { transport_type_option = transport_types[tmp]; } return true; } return false; } bool money_frame_t::infowin_event(const event_t *ev) { bool swallowed = gui_frame_t::infowin_event(ev); set_focus( &transport_type_c ); return swallowed; } uint32 money_frame_t::get_rdwr_id() { return magic_finances_t+player->get_player_nr(); } void money_frame_t::rdwr( loadsave_t *file ) { // button-to-chart array button_to_chart.rdwr(file); year_month_tabs.rdwr(file); file->rdwr_short(transport_type_option); if (file->is_loading()) { for(uint32 i=0; i money_labels; void update_labels(); void fill_chart_tables(); bool is_chart_table_zero(int ttoption); public: /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE {return "finances.txt";} /** * Constructor. Adds all necessary Subcomponents. */ money_frame_t(player_t *player); ~money_frame_t(); /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; /** * Set window size and adjust component sizes and/or positions accordingly */ void set_windowsize(scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; bool infowin_event(const event_t *ev) OVERRIDE; // saving/restore stuff uint32 get_rdwr_id() OVERRIDE; void rdwr( loadsave_t *file ) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/obj_info.cc000066400000000000000000000022261520675460500212620ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../display/simgraph.h" #include "../display/viewport.h" #include "../world/simworld.h" #include "../dataobj/translator.h" #include "obj_info.h" obj_infowin_t::obj_infowin_t(const obj_t* obj) : base_infowin_t(translator::translate( obj->get_name() ), obj->get_owner()), view(obj, scr_size( max(64, gfx->get_base_tile_raster_width()), max(56, (gfx->get_base_tile_raster_width()*7)/8) )) { fill_buffer(); //textarea.set_width( textarea.get_size().w + get_base_tile_raster_width() - 64); set_embedded(&view); } void obj_infowin_t::fill_buffer() { buf.clear(); get_obj()->info(buf); } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void obj_infowin_t::draw(scr_coord pos, scr_size size) { const cbuffer_t old_buf(buf); fill_buffer(); if( strcmp( buf, old_buf ) ) { recalc_size(); } base_infowin_t::draw( pos, size ); } bool obj_infowin_t::is_weltpos() { return ( welt->get_viewport()->is_on_center(get_obj()->get_pos())); } simutrans-124.5/src/simutrans/gui/obj_info.h000066400000000000000000000015601520675460500211240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_OBJ_INFO_H #define GUI_OBJ_INFO_H #include "../simdebug.h" #include "../obj/simobj.h" #include "base_info.h" #include "components/gui_obj_view.h" /** * An adapter class to display info windows for things (objects) */ class obj_infowin_t : public base_infowin_t { protected: obj_view_t view; public: obj_infowin_t(const obj_t* obj); obj_t const* get_obj() const { return view.get_obj(); } koord3d get_weltpos(bool) OVERRIDE { return get_obj()->get_pos(); } bool is_weltpos() OVERRIDE; // refill buffer void fill_buffer(); /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/optionen.cc000066400000000000000000000066051520675460500213350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_frame.h" #include "components/gui_button.h" #include "components/gui_divider.h" #include "components/action_listener.h" #include "../world/simworld.h" #include "../tool/simmenu.h" #include "simwin.h" #include "optionen.h" #include "display_settings.h" #include "sprachen.h" #include "player_frame.h" #include "kennfarbe.h" #include "sound_frame.h" #include "scenario_info.h" #include "banner.h" #include "../dataobj/scenario.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../sys/simsys.h" enum BUTTONS { BUTTON_LANGUAGE = 0, BUTTON_PLAYERS, BUTTON_DISPLAY, BUTTON_PLAYER_COLORS, BUTTON_SOUND, BUTTON_SAVE_GAME, BUTTON_SCENARIO_INFO, BUTTON_MENU, BUTTON_QUIT }; static char const *const option_buttons_text[] = { "Sprache", "Spieler(mz)", "Display settings", "Farbe", "Sound", "Save", "Scenario", "Return to menu", "Beenden" }; optionen_gui_t::optionen_gui_t() : gui_frame_t( translator::translate("Einstellungen aendern")) { assert( lengthof(option_buttons)==lengthof(option_buttons_text) ); assert( lengthof(option_buttons)==BUTTON_QUIT+1 ); set_table_layout(2,0); set_force_equal_columns(true); for( uint i=0; iget_scenario()->active() ) { option_buttons[BUTTON_SCENARIO_INFO].disable(); } // Squeeze in divider new_component(); add_component(option_buttons + i); new_component_span(2); } else add_component(option_buttons + i); } reset_min_windowsize(); set_windowsize(get_min_windowsize()); set_resizemode(gui_frame_t::horizontal_resize); } /** * This method is called if an action is triggered */ bool optionen_gui_t::action_triggered( gui_action_creator_t *comp,value_t /* */) { if( comp == option_buttons + BUTTON_LANGUAGE ) { create_win(new sprachengui_t(), w_info, magic_sprachengui_t); } else if( comp == option_buttons + BUTTON_PLAYER_COLORS ) { create_win(new farbengui_t(welt->get_active_player()), w_info, magic_farbengui_t); } else if( comp == option_buttons + BUTTON_DISPLAY ) { create_win(new color_gui_t(), w_info, magic_color_gui_t); } else if( comp == option_buttons + BUTTON_SOUND ) { create_win(new sound_frame_t(), w_info, magic_sound_kontroll_t); } else if( comp == option_buttons + BUTTON_PLAYERS ) { create_win(new ki_kontroll_t(), w_info, magic_ki_kontroll_t); } else if( comp == option_buttons + BUTTON_SCENARIO_INFO ) { create_win(new scenario_info_t(), w_info, magic_scenario_info); } else if( comp == option_buttons + BUTTON_SAVE_GAME ) { destroy_win(this); tool_t *tmp_tool = create_tool( DIALOG_SAVE | DIALOGE_TOOL ); welt->set_tool( tmp_tool, welt->get_active_player() ); // since init always returns false, it is safe to delete immediately delete tmp_tool; } else if (comp == option_buttons + BUTTON_MENU) { // return to menu banner_t::show_banner(); } else if( comp == option_buttons + BUTTON_QUIT ) { // we call the proper tool for quitting welt->set_tool(tool_t::simple_tool[TOOL_QUIT], NULL); } else { // not our? return false; } return true; } simutrans-124.5/src/simutrans/gui/optionen.h000066400000000000000000000015051520675460500211710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_OPTIONEN_H #define GUI_OPTIONEN_H #include "simwin.h" #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_button.h" /* * Settings in the game * * Dialog for game options/Main menu */ class optionen_gui_t : public gui_frame_t, action_listener_t { private: button_t option_buttons[9]; public: optionen_gui_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE {return "options.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void rdwr(loadsave_t *) OVERRIDE {} uint32 get_rdwr_id() OVERRIDE { return magic_optionen_gui_t; } }; #endif simutrans-124.5/src/simutrans/gui/pakinstaller.cc000066400000000000000000000053721520675460500221730ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../utils/cbuffer.h" #include "pakinstaller.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../sys/simsys.h" #include "../world/simworld.h" #include "../dataobj/pakset_downloader.h" #include "../simloadingscreen.h" #include "../../paksetinfo.h" bool pakinstaller_t::finish_install; pakinstaller_t::pakinstaller_t() : gui_frame_t(translator::translate("Install graphics")), paks(gui_scrolled_list_t::listskin), obsolete_paks(gui_scrolled_list_t::listskin) { finish_install = false; set_table_layout(2, 0); new_component_span( "Select one or more graphics to install (Ctrl+click):", 2 ); for (int i = 0; i < OBSOLETE_FROM; i++) { if (pak_can_download(pakinfo + i)) { paklist.append(pakinfo + i); paks.new_component(pakinfo[i].name, SYSCOL_TEXT); paks.get_element(paks.get_count()-1)->selected = pak_need_update(pakinfo + i); } } paks.enable_multiple_selection(); scr_coord_val paks_h = paks.get_max_size().h; add_component(&paks,2); new_component_span( "The following graphics are unmaintained:", 2 ); for (int i = OBSOLETE_FROM; i < PAKSET_COUNT; i++) { if (pak_can_download(pakinfo + i)) { paklist.append(pakinfo + i); obsolete_paks.new_component(pakinfo[i].name, SYSCOL_TEXT_SHADOW); obsolete_paks.get_element(obsolete_paks.get_count()-1)->selected = pak_need_update(pakinfo + i); } } obsolete_paks.enable_multiple_selection(); scr_coord_val obsolete_paks_h = obsolete_paks.get_max_size().h; add_component(&obsolete_paks,2); bool is_world = world(); install.init(button_t::roundbox_state | button_t::flexible, "Install"); add_component(&install,is_world+1); install.add_listener(this); if( !is_world ) { cancel.init( button_t::roundbox_state | button_t::flexible, "Cancel" ); cancel.add_listener( this ); add_component( &cancel ); } reset_min_windowsize(); set_windowsize(get_min_windowsize()+scr_size(0,paks_h-paks.get_size().h + obsolete_paks_h- obsolete_paks.get_size().h)); } // compile list of paks to be installed by pakset_downloader bool pakinstaller_t::action_triggered(gui_action_creator_t *comp, value_t) { if (comp == &cancel) { finish_install = true; return false; } // now install dr_mkdir( env_t::install_dir ); dr_chdir( env_t::install_dir ); vector_tplinstall_paks; for (sint32 i : paks.get_selections()) { install_paks.append(paklist[i]); } for (sint32 i : obsolete_paks.get_selections()) { install_paks.append(paklist[i+paks.get_count()]); } pak_download(install_paks); finish_install = true; return false; } simutrans-124.5/src/simutrans/gui/pakinstaller.h000066400000000000000000000013451520675460500220310ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_PAKINSTALLER_H #define GUI_PAKINSTALLER_H #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_label.h" #include "components/gui_scrolled_list.h" #include "components/gui_button.h" #include "simwin.h" #include "../dataobj/pakset_downloader.h" class pakinstaller_t : public gui_frame_t, private action_listener_t { private: gui_scrolled_list_t paks; gui_scrolled_list_t obsolete_paks; button_t install, cancel; vector_tpl paklist; public: static bool finish_install; pakinstaller_t(); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/pakselector.cc000066400000000000000000000101431520675460500220060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "pakselector.h" #include "pakinstaller.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../sys/simsys.h" #include "../pathes.h" pakselector_t::pakselector_t() : savegame_frame_t( NULL, true, NULL, true ), notice_label(¬ice_buffer) { // if true, we would call the installler afterwards pakinstaller_t::finish_install = false; // remove unnecessary buttons top_frame.remove_component( &input ); savebutton.set_visible(false); cancelbutton.set_visible(false); // don't show list item labels label_enabled = false; fnlabel.set_text( "Choose one graphics set for playing:" ); notice_buffer.printf("%s", "To avoid seeing this dialogue define a path by:\n" " - adding 'pak_file_path = pak/' to your simuconf.tab\n" " - using '-objects pakxyz/' on the command line" ); notice_label.recalc_size(); add_component(¬ice_label); installbutton.init( button_t::roundbox, "Install" ); installbutton.add_listener( &ps ); add_component(&installbutton); resize(scr_coord(0,0)); dr_chdir(env_t::base_dir); char bd[PATH_MAX], id[PATH_MAX]; dr_getcwd(bd, lengthof(bd)-2); strcat(bd, PATH_SEPARATOR); dr_chdir(env_t::install_dir); dr_getcwd(id, lengthof(id)-2); strcat(id, PATH_SEPARATOR); add_path( bd ); if( strcmp(bd,id) ) { add_path(id); } dr_chdir( env_t::user_dir ); if( !dr_chdir(USER_PAK_PATH) ) { char ud[PATH_MAX]; dr_getcwd(ud, lengthof(ud)-2 ); strcat(ud, PATH_SEPARATOR); if( strcmp(id, ud) ) { add_path(ud); } } } /** * what to do after loading */ bool pakselector_t::item_action(const char *fullpath) { env_t::pak_dir = fullpath; env_t::pak_dir += PATH_SEPARATOR; env_t::pak_name = (str_get_filename(fullpath, true)+PATH_SEPARATOR); env_t::default_settings.set_with_private_paks( false ); return true; } bool pakselector_t::del_action(const char *fullpath) { // cannot delete set => use this for selection env_t::pak_dir = fullpath; env_t::pak_dir += PATH_SEPARATOR; env_t::pak_name = str_get_filename(fullpath, true)+PATH_SEPARATOR; env_t::default_settings.set_with_private_paks( true ); return true; } const char *pakselector_t::get_info(const char *) { return ""; } /** * This method returns true if filename is what we want and false if not. * A PAK directory is considered valid if the file ground.outside.pak exists. */ bool pakselector_t::check_file(const char *filename, const char *) { cbuffer_t buf; buf.printf("%s/ground.Outside.pak", filename); // if we can open the file, it is valid. if (FILE* const f = dr_fopen(buf, "r")) { fclose(f); return true; } // the file was not found or couldn't be opened. return false; } void pakselector_t::fill_list() { cbuffer_t path; // do the search ... savegame_frame_t::fill_list(); // do not sort or the path names are in the wrong positions // entries.sort(dir_entry_t::compare); for(dir_entry_t &i : entries) { if (i.type == LI_HEADER) { continue; } // look for addon directory path.clear(); path.printf("%saddons/%s", env_t::user_dir, i.button->get_text()); // reuse delete button as load-with-addons button delete i.del; i.del = new button_t(); i.del->init(button_t::roundbox, "Load with addons"); // if we can't change directory to /addon // Hide the addon button if( dr_chdir( path ) != 0 ) { i.del->disable(); // if list contains only one header, one pakset entry without addons // store path to pakset temporary, reset later if more choices available // if env_t::pak_dir is non-empty then simmain.cc will close the window immediately env_t::pak_name = (std::string)i.button->get_text() + PATH_SEPARATOR; env_t::pak_dir = (std::string)i.info + PATH_SEPARATOR; } } dr_chdir( env_t::base_dir ); if(entries.get_count() > this->num_sections+1) { // empty path as more than one pakset is present, user has to choose env_t::pak_dir.clear(); env_t::pak_name.clear(); } } bool pakselector_install_action_t::action_triggered( gui_action_creator_t*, value_t) { pakinstaller_t::finish_install = true; return true; } simutrans-124.5/src/simutrans/gui/pakselector.h000066400000000000000000000021671520675460500216570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_PAKSELECTOR_H #define GUI_PAKSELECTOR_H #include "savegame_frame.h" #include "components/gui_textarea.h" #include "../utils/cbuffer.h" class pakselector_install_action_t : public action_listener_t { bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; class pakselector_t : public savegame_frame_t { protected: cbuffer_t notice_buffer; gui_textarea_t notice_label; button_t installbutton; pakselector_install_action_t ps; const char *get_info ( const char *filename ) OVERRIDE; bool check_file ( const char *filename, const char *suffix ) OVERRIDE; bool item_action ( const char *fullpath ) OVERRIDE; bool del_action ( const char *fullpath ) OVERRIDE; void fill_list ( void ) OVERRIDE; public: pakselector_t(); const char *get_help_filename ( void ) const OVERRIDE { return ""; } bool has_title ( void ) const OVERRIDE { return false; } bool has_pak ( void ) const { return !entries.empty(); } }; #endif simutrans-124.5/src/simutrans/gui/password_frame.cc000066400000000000000000000064571520675460500225230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../tool/simmenu.h" #include "simwin.h" #include "../world/simworld.h" #include "../player/simplay.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../network/network_cmd_ingame.h" #include "../utils/cbuffer.h" #include "../utils/sha1.h" #include "../utils/simstring.h" #include "password_frame.h" #include "player_frame.h" password_frame_t::password_frame_t( player_t *player ) : gui_frame_t( translator::translate("Enter Password"), player ) { set_table_layout(2,0); this->player = player; if( !player->is_locked() || (welt->get_active_player_nr()==PLAYER_PUBLIC_NR && !welt->get_public_player()->is_locked()) ) { // allow to change name name tstrncpy( player_name_str, player->get_name(), lengthof(player_name_str) ); player_name.set_text(player_name_str, lengthof(player_name_str)); player_name.add_listener(this); add_component(&player_name, 2); } else { const_player_name.set_text( player->get_name() ); add_component(&const_player_name, 2); } fnlabel.set_text( "Password" ); // so we have a width now add_component(&fnlabel); // Input box for password ibuf[0] = 0; password.set_text(ibuf, lengthof(ibuf) ); password.add_listener(this); add_component( &password ); set_focus( &password ); reset_min_windowsize(); set_windowsize(get_min_windowsize() ); } /** * This method is called if an action is triggered */ bool password_frame_t::action_triggered( gui_action_creator_t *comp, value_t p ) { if(comp == &password && (ibuf[0]!=0 || p.i == 1)) { if (player->is_unlock_pending()) { // unlock already pending, do not do everything twice return true; } // Enter-Key pressed // test for matching password to unlock size_t len = strlen( password.get_text() ); pwd_hash_t hash; // remove hash to re-open slot if password is empty if(len>0) { SHA1 sha1; sha1.Input( password.get_text(), len ); sha1.Result(hash); } // store the hash welt->store_player_password_hash( player->get_player_nr(), hash ); if( env_t::networkmode) { player->unlock(!player->is_locked(), true); // send hash to server: it will unlock player or change password nwc_auth_player_t *nwc = new nwc_auth_player_t(player->get_player_nr(), hash); network_send_server(nwc); } else { /* if current active player is player 1 and this is unlocked, he may reset passwords * otherwise you need the valid previous password */ if( !player->is_locked() || (welt->get_active_player_nr()==PLAYER_PUBLIC_NR && !welt->get_public_player()->is_locked()) ) { // set password player->access_password_hash() = hash; player->unlock(true, false); } else { player->check_unlock(hash); } } } if( comp == &player_name ) { // rename a player cbuffer_t buf; buf.printf( "p%u,%s", player->get_player_nr(), player_name.get_text() ); tool_t *tmp_tool = create_tool( TOOL_RENAME | SIMPLE_TOOL ); tmp_tool->set_default_param( buf ); welt->set_tool( tmp_tool, player ); // since init always returns false, it is safe to delete immediately delete tmp_tool; } if( p.i==1 ) { // destroy window after enter is pressed destroy_win(this); } return true; } simutrans-124.5/src/simutrans/gui/password_frame.h000066400000000000000000000014241520675460500223520ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_PASSWORD_FRAME_H #define GUI_PASSWORD_FRAME_H #include "components/action_listener.h" #include "gui_frame.h" #include "components/gui_container.h" #include "components/gui_textinput.h" #include "components/gui_label.h" class password_frame_t : public gui_frame_t, action_listener_t { private: char ibuf[256], player_name_str[256]; protected: player_t *player; gui_textinput_t player_name; gui_hidden_textinput_t password; gui_label_t fnlabel, const_player_name; public: password_frame_t( player_t *player ); const char * get_help_filename() const OVERRIDE {return "password.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/player_frame.cc000066400000000000000000000307261520675460500221510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../simcolor.h" #include "../world/simworld.h" #include "../tool/simmenu.h" #include "../network/network_cmd_ingame.h" #include "../dataobj/environment.h" #include "../dataobj/scenario.h" #include "../dataobj/translator.h" #include "simwin.h" #include "../utils/simstring.h" #include "../player/ai_scripted.h" #include "money_frame.h" // for the finances #include "password_frame.h" // for the password #include "ai_selector.h" #include "player_frame.h" #include "sprachen.h" class password_button_t : public button_t { public: password_button_t() : button_t() { init(button_t::box, ""); } scr_size get_min_size() const OVERRIDE { return scr_size(D_BUTTON_HEIGHT,D_BUTTON_HEIGHT); } }; ki_kontroll_t::ki_kontroll_t() : gui_frame_t( translator::translate("Spielerliste") ) { set_table_layout(5,0); for(int i=0; iget_player(i); // activate player buttons // .. not available for the two first players (first human and second public) if( i >= 2 ) { // AI button (small square) player_active[i-2].init(button_t::square_state, ""); player_active[i-2].add_listener(this); player_active[i-2].set_rigid(true); add_component( player_active+(i-2) ); } else { new_component(); } // Player select button (arrow) player_change_to[i].init(button_t::arrowright_state, ""); player_change_to[i].add_listener(this); player_change_to[i].set_rigid(true); add_component(player_change_to+i); // Prepare finances button player_get_finances[i].init( button_t::box | button_t::flexible, ""); player_get_finances[i].background_color = PLAYER_FLAG | gfx->palette_lookup((player ? player->get_player_color1():i*8)+env_t::gui_player_color_bright); player_get_finances[i].add_listener(this); // Player type selector, Combobox player_select[i].set_focusable( false ); // add table that contains these two buttons, only one of them will be visible add_table(1,0); // When adding new players, activate the interface player_select[i].set_selection(welt->get_settings().get_player_type(i)); player_select[i].add_listener(this); add_component( player_get_finances+i ); add_component( player_select+i ); if( player != NULL ) { player_get_finances[i].set_text( player->get_name() ); player_select[i].set_visible(false); } else { player_get_finances[i].set_visible(false); } end_table(); // password/locked button player_lock[i] = new_component(); player_lock[i]->background_color = gfx->palette_lookup((player && player->is_locked()) ? (player->is_unlock_pending() ? COL_YELLOW : COL_RED) : COL_GREEN); player_lock[i]->add_listener(this); player_lock[i]->set_rigid(true); // Income label ai_income[i] = new_component(MONEY_PLUS, gui_label_t::money_right); ai_income[i]->set_rigid(true); } // freeplay mode freeplay.init( button_t::square_state, "freeplay mode"); freeplay.add_listener(this); freeplay.pressed = welt->get_settings().is_freeplay(); add_component( &freeplay, 5 ); sprachengui_t::init_currency_from_lang(); // not yet init when reloaded // currency position currency_left.init(button_t::square_automatic, "currency left"); currency_left.add_listener(this); currency_left.pressed = get_currency_left(); add_component(¤cy_left, 3); // and symbol strcpy(currency_symbol, get_currency_string()); currency_string.set_text(currency_symbol, lengthof(currency_symbol)); currency_string.add_listener(this); add_component(¤cy_string, 2); update_data(); // calls reset_min_windowsize set_windowsize(get_min_windowsize()); } /** * This method is called if an action is triggered */ bool ki_kontroll_t::action_triggered( gui_action_creator_t *comp, value_t p ) { // Free play button? if( comp == &freeplay ) { welt->call_change_player_tool(karte_t::toggle_freeplay, 255, 0); return true; } if (comp == ¤cy_string || comp == ¤cy_left) { bool text_changed = strcmp(get_currency_string(), currency_symbol); env_t::currency_left = currency_left.pressed; if (text_changed) { strcpy(env_t::currency_symbol, currency_symbol); } else { env_t::currency_left = currency_left.pressed; } // set the values sprachengui_t::init_currency_from_lang(); strcpy(currency_symbol, get_currency_string()); currency_left.pressed = env_t::currency_left; return true; } // Check the GUI list of buttons for(int i=0; i=2 && comp == (player_active+i-2) ) { // switch AI on/off if( welt->get_player(i)==NULL ) { // create new AI welt->call_change_player_tool(karte_t::new_player, i, player_select[i].get_selection()); // if scripted ai without script -> open script selector window ai_scripted_t *ai = dynamic_cast(welt->get_player(i)); if (ai && !ai->has_script() && (!env_t::networkmode || env_t::server)) { create_win( new ai_selector_t(i), w_info, magic_finances_t + i ); } } else { // If turning on again, reload script if (!env_t::networkmode && !welt->get_player(i)->is_active()) { if (ai_scripted_t *ai = dynamic_cast(welt->get_player(i))) { ai->reload_script(); } } // Current AI on/off welt->call_change_player_tool(karte_t::toggle_player_active, i, !welt->get_player(i)->is_active()); } break; } // Finance button pressed if( comp == (player_get_finances+i) ) { // get finances player_get_finances[i].pressed = false; // if scripted ai without script -> open script selector window ai_scripted_t *ai = dynamic_cast(welt->get_player(i)); if (ai && !ai->has_script() && (!env_t::networkmode || env_t::server)) { create_win( new ai_selector_t(i), w_info, magic_finances_t + i ); } else { create_win( new money_frame_t(welt->get_player(i)), w_info, magic_finances_t + i ); } break; } // Changed active player if( comp == (player_change_to+i) ) { // make active player player_t *const prevplayer = welt->get_active_player(); welt->switch_active_player(i,false); // unlocked public service player can change into any company in multiplayer games player_t *const player = welt->get_active_player(); if( env_t::networkmode && prevplayer == welt->get_public_player() && !prevplayer->is_locked() && player->is_locked() ) { player->unlock(false, true); // send unlock command nwc_auth_player_t *nwc = new nwc_auth_player_t(); nwc->player_nr = player->get_player_nr(); network_send_server(nwc); } break; } // Change player name and/or password if( comp == (player_lock[i]) && welt->get_player(i) ) { if (!welt->get_player(i)->is_unlock_pending()) { // set password create_win(new password_frame_t(welt->get_player(i)), w_info, magic_pwd_t + i ); player_lock[i]->pressed = false; } } // New player assigned in an empty slot if( comp == (player_select+i) ) { // make active player if( p.i0 ) { player_active[i-2].set_visible(true); welt->get_settings().set_player_type(i, (uint8)p.i); } else { player_active[i-2].set_visible(false); player_select[i].set_selection(0); welt->get_settings().set_player_type(i, 0); } break; } } return true; } void ki_kontroll_t::update_data() { // switching active player allowed? bool player_change_allowed = welt->get_settings().get_allow_player_change() || !welt->get_public_player()->is_locked(); // activate player etc allowed? bool player_tools_allowed = true; // check also scenario rules if (welt->get_scenario()->is_scripted()) { player_tools_allowed = welt->get_scenario()->is_tool_allowed(NULL, TOOL_SWITCH_PLAYER | SIMPLE_TOOL); player_change_allowed &= player_tools_allowed; } freeplay.enable(); if (welt->get_public_player()->is_locked() || !welt->get_settings().get_allow_player_change() || !player_tools_allowed) { freeplay.disable(); } for(int i=0; iget_player(i) ) { player_select[i].set_visible(false); player_get_finances[i].set_visible(true); player_change_to[i].set_visible(player_change_allowed); player_lock[i]->set_visible(player_tools_allowed); // scripted ai without script get different button without color ai_scripted_t *ai = dynamic_cast(player); if (ai && !ai->has_script() && (!env_t::networkmode || env_t::server)) { player_get_finances[i].set_typ(button_t::roundbox | button_t::flexible); player_get_finances[i].set_text("Load Scripted AI"); } else { player_get_finances[i].set_typ(button_t::box | button_t::flexible); player_get_finances[i].set_text(player->get_name()); } // always update locking status player_get_finances[i].background_color = PLAYER_FLAG | gfx->palette_lookup(player->get_player_color1()+env_t::gui_player_color_bright); player_lock[i]->background_color = gfx->palette_lookup(player->is_locked() ? (player->is_unlock_pending() ? COL_YELLOW : COL_RED) : COL_GREEN); // human players cannot be deactivated if (i>1) { player_active[i-2].set_visible( player->get_ai_id()!=player_t::HUMAN && player_tools_allowed); } ai_income[i]->set_visible(true); } else { // inactive player => button needs removal? player_get_finances[i].set_visible(false); player_change_to[i].set_visible(false); player_select[i].set_visible(player_tools_allowed); player_lock[i]->set_visible(false); if (i>1) { player_active[i-2].set_visible(0 < player_select[i].get_selection() && player_select[i].get_selection() < player_t::MAX_AI); } // Create combobox list data int select = player_select[i].get_selection(); player_select[i].clear_elements(); player_select[i].new_component( translator::translate("slot empty"), SYSCOL_TEXT ) ; player_select[i].new_component( translator::translate("Manual (Human)"), SYSCOL_TEXT ) ; if( !welt->get_public_player()->is_locked() || !env_t::networkmode ) { player_select[i].new_component( translator::translate("Goods AI"), SYSCOL_TEXT ) ; player_select[i].new_component( translator::translate("Passenger AI"), SYSCOL_TEXT ) ; if (!env_t::networkmode || env_t::server) { // only server can start scripted players player_select[i].new_component( translator::translate("Scripted AI's"), SYSCOL_TEXT ) ; } } player_select[i].set_selection(select); assert( player_t::MAX_AI==5 ); ai_income[i]->set_visible(false); } } update_income(); reset_min_windowsize(); } void ki_kontroll_t::update_income() { // Update finance for(int i=0; ibuf().clear(); player_t *player = welt->get_player(i); if( player != NULL ) { if (i != 1 && !welt->get_settings().is_freeplay() && player->get_finance()->get_history_com_year(0, ATC_NETWEALTH) < 0) { ai_income[i]->set_color( MONEY_MINUS ); ai_income[i]->buf().append(translator::translate("Company bankrupt")); ai_income[i]->set_align(gui_label_t::left); } else { double account=player->get_account_balance_as_double(); char str[128]; money_to_string(str, account ); ai_income[i]->buf().append(str); ai_income[i]->set_color( account>=0.0 ? MONEY_PLUS : MONEY_MINUS ); ai_income[i]->set_align(gui_label_t::money_right); } } ai_income[i]->update(); } } /** * Draw the component */ void ki_kontroll_t::draw(scr_coord pos, scr_size size) { // Update free play freeplay.pressed = welt->get_settings().is_freeplay(); if (welt->get_public_player()->is_locked() || !welt->get_settings().get_allow_player_change()) { freeplay.disable(); } else { freeplay.enable(); } // Update finance update_income(); // Update buttons for(int i=0; iget_player(i); player_change_to[i].pressed = false; if(i>=2) { player_active[i-2].pressed = player !=NULL && player->is_active(); } player_lock[i]->background_color = gfx->palette_lookup(player && player->is_locked() ? (player->is_unlock_pending() ? COL_YELLOW : COL_RED) : COL_GREEN); } player_change_to[welt->get_active_player_nr()].pressed = true; // All controls updated, draw them... gui_frame_t::draw(pos, size); } simutrans-124.5/src/simutrans/gui/player_frame.h000066400000000000000000000035541520675460500220120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_PLAYER_FRAME_H #define GUI_PLAYER_FRAME_H #include "../simconst.h" #include "gui_frame.h" #include "components/gui_button.h" #include "components/gui_combobox.h" #include "components/gui_label.h" #include "components/action_listener.h" #include "simwin.h" /** * Menu for the player list */ class ki_kontroll_t : public gui_frame_t, private action_listener_t { private: char currency_symbol[16]; gui_label_buf_t *ai_income[MAX_PLAYER_COUNT-1]; // Income labels button_t player_active[MAX_PLAYER_COUNT-2-1], // AI on/off button player_get_finances[MAX_PLAYER_COUNT-1], // Finance buttons player_change_to[MAX_PLAYER_COUNT-1], // Set active player button *player_lock[MAX_PLAYER_COUNT-1], // Set name & password button freeplay, currency_left; gui_textinput_t currency_string; gui_combobox_t player_select[MAX_PLAYER_COUNT-1]; void update_income(); public: ki_kontroll_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE {return "players.txt";} /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; /** * Updates the dialogue window after changes to players states * called from tool_change_player_t::init * necessary for network games to keep dialogues synchronous */ void update_data(); // since no information are needed to be saved to restore this, returning magic is enough uint32 get_rdwr_id() OVERRIDE { return magic_ki_kontroll_t; } }; #endif simutrans-124.5/src/simutrans/gui/player_ranking_frame.cc000066400000000000000000000423261520675460500236610ustar00rootroot00000000000000/* * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ #include "../macros.h" #include "../simcolor.h" #include "../world/simworld.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../builder/wegbauer.h" #include "simwin.h" #include "money_frame.h" // for the finances #include "player_ranking_frame.h" uint8 player_ranking_frame_t::transport_type_option = TT_ALL; // Text should match that of the finance dialog static const char* cost_type_name[player_ranking_frame_t::MAX_PLAYER_RANKING_CHARTS] = { "Revenue", "Ops Profit", "Margin (%%)", "Passagiere", "Post", "Goods", "Cash", "Net Wealth", "Convoys", // "Vehicles", // "Stops" // "way_distances" // Way kilometrage // "travel_distance" }; static const uint8 cost_type[player_ranking_frame_t::MAX_PLAYER_RANKING_CHARTS] = { gui_chart_t::MONEY, gui_chart_t::MONEY, gui_chart_t::PERCENT, gui_chart_t::STANDARD, gui_chart_t::STANDARD, gui_chart_t::STANDARD, gui_chart_t::MONEY, gui_chart_t::MONEY, gui_chart_t::STANDARD }; static const uint8 cost_type_color[player_ranking_frame_t::MAX_PLAYER_RANKING_CHARTS] = { COL_REVENUE, COL_PROFIT, COL_MARGIN, COL_LIGHT_PURPLE, COL_TRANSPORTED, COL_BROWN, COL_CASH, COL_WEALTH, COL_NEW_VEHICLES }; // is_atv=1, ATV:vehicle finance record, ATC:common finance record static const uint8 history_type_idx[player_ranking_frame_t::MAX_PLAYER_RANKING_CHARTS * 2] = { 1,ATV_REVENUE, 1,ATV_OPERATING_PROFIT, 1,ATV_PROFIT_MARGIN, 1,ATV_TRANSPORTED_PASSENGER, 1,ATV_TRANSPORTED_MAIL, 1,ATV_TRANSPORTED_GOOD, 0,ATC_CASH, 0,ATC_NETWEALTH, 0,ATC_ALL_CONVOIS, // 1,ATV_VEHICLES, // 0,ATC_HALTS }; sint16 years_back = 1; static int compare_atv(uint8 player_nr_a, uint8 player_nr_b, uint8 atv_index) { sint64 comp = 0; // otherwise values may overflow player_t* a_player = world()->get_player(player_nr_a); player_t* b_player = world()->get_player(player_nr_b); if (a_player && b_player) { comp = b_player->get_finance()->get_history_veh_year((transport_type)player_ranking_frame_t::transport_type_option, years_back, atv_index) - a_player->get_finance()->get_history_veh_year((transport_type)player_ranking_frame_t::transport_type_option, years_back, atv_index); if (comp == 0 && years_back > 0) { // tie breaker: next year comp = b_player->get_finance()->get_history_veh_year((transport_type)player_ranking_frame_t::transport_type_option, years_back - 1, atv_index) - a_player->get_finance()->get_history_veh_year((transport_type)player_ranking_frame_t::transport_type_option, years_back - 1, atv_index); } } // if at least one if defined, it must go the the head of the list else if(a_player) { return true; } else if (b_player) { return false; } if (comp == 0) { comp = player_nr_b - player_nr_a; } return comp>0; // sort just test for larger than 0 } static int compare_atc(uint8 player_nr_a, uint8 player_nr_b, uint8 atc_index) { sint64 comp = 0; const player_t *a_player = world()->get_player(player_nr_a); const player_t *b_player = world()->get_player(player_nr_b); if (a_player && b_player) { comp = b_player->get_finance()->get_history_com_year(years_back, atc_index) - a_player->get_finance()->get_history_com_year(years_back, atc_index); if (comp == 0 && years_back > 0) { comp = b_player->get_finance()->get_history_com_year(years_back - 1, atc_index) - a_player->get_finance()->get_history_com_year(years_back - 1, atc_index); } } // if at least one if defined, it must go the the head of the list else if (a_player) { return true; } else if (b_player) { return false; } if (comp == 0) { comp = player_nr_b - player_nr_a; } return comp>0; } static int compare_revenue(player_button_t* const& a, player_button_t* const& b) { return compare_atv(a->get_player_nr(), b->get_player_nr(), ATV_REVENUE); } static int compare_profit(player_button_t* const& a, player_button_t* const& b) { return compare_atv(a->get_player_nr(), b->get_player_nr(), ATV_OPERATING_PROFIT); } static int compare_transport_pax(player_button_t* const& a, player_button_t* const& b) { return compare_atv(a->get_player_nr(), b->get_player_nr(), ATV_TRANSPORTED_PASSENGER); } static int compare_transport_mail(player_button_t* const& a, player_button_t* const& b) { return compare_atv(a->get_player_nr(), b->get_player_nr(), ATV_TRANSPORTED_MAIL); } static int compare_transport_goods(player_button_t* const& a, player_button_t* const& b) { return compare_atv(a->get_player_nr(), b->get_player_nr(), ATV_TRANSPORTED_GOOD); } static int compare_margin(player_button_t* const& a, player_button_t* const& b) { return compare_atv(a->get_player_nr(), b->get_player_nr(), ATV_PROFIT_MARGIN); } static int compare_cash(player_button_t* const& a, player_button_t* const& b) { return compare_atc(a->get_player_nr(), b->get_player_nr(), ATC_CASH); } static int compare_netwealth(player_button_t* const& a, player_button_t* const& b) { return compare_atc(a->get_player_nr(), b->get_player_nr(), ATC_NETWEALTH); } static int compare_convois(player_button_t* const& a, player_button_t* const& b) { return compare_atc(a->get_player_nr(), b->get_player_nr(), ATC_ALL_CONVOIS); } /* static int compare_halts(player_button_t* const& a, player_button_t* const& b) { return compare_atc(a->get_player_nr(), b->get_player_nr(), ATC_HALTS); } */ player_button_t::player_button_t(uint8 player_nr_) { player_nr = player_nr_; init(button_t::box_state | button_t::flexible, NULL, scr_coord(0, 0), D_BUTTON_SIZE); set_tooltip("Click to open the finance dialog."); update(); } void player_button_t::update() { player_t* player = world()->get_player(player_nr); if (player) { set_text(player->get_name()); background_color = gfx->palette_lookup(player->get_player_color1() + env_t::gui_player_color_bright); enable(); set_visible(true); } else { set_visible(false); disable(); } } player_ranking_frame_t::player_ranking_frame_t(uint8 selected_player_nr) : gui_frame_t(translator::translate("Player ranking")), scrolly(&cont_players, true, true) { selected_player = selected_player_nr; last_year = 0; // update on first drawing count = 0; set_table_layout(1, 0); add_table(2, 1)->set_alignment(ALIGN_TOP); { add_component(&chart); sint32 years_back = clamp(welt->get_last_year() - welt->get_settings().get_starting_year(), 2, MAX_PLAYER_HISTORY_YEARS); chart.set_dimension(years_back, 10000); chart.set_seed(welt->get_last_year()); chart.set_background(SYSCOL_CHART_BACKGROUND); for (int np = 0; np < MAX_PLAYER_COUNT; np++) { if (np == PLAYER_PUBLIC_NR) continue; if (welt->get_player(np)) { player_button_t* b = new player_button_t(np); b->add_listener(this); if (np == selected_player) { b->pressed = true; } buttons.append(b); } } add_table(2, 2); { years_back_c.new_component(translator::translate("This Year"), SYSCOL_TEXT); years_back_c.new_component(translator::translate("Last Year"), SYSCOL_TEXT); years_back_c.add_listener(this); years_back_c.set_selection(0); add_component(&years_back_c); for (int i = 0, count = 0; i < TT_OTHER; ++i) { transport_type_c.new_component(translator::translate(finance_t::transport_type_values[i]), SYSCOL_TEXT); transport_types[count++] = i; } transport_type_c.add_listener(this); transport_type_c.set_selection(0); transport_type_c.set_rigid(true); add_component(&transport_type_c); scrolly.set_maximize(true); scrolly.set_size_corner(false); add_component(&scrolly, 2); } end_table(); } end_table(); add_table(4, 0)->set_force_equal_columns(true); { for (uint8 i = 0; i < MAX_PLAYER_RANKING_CHARTS; i++) { bt_charts[i].init(button_t::roundbox_state | button_t::flexible, cost_type_name[i]); bt_charts[i].background_color = gfx->palette_lookup(cost_type_color[i]); if (i == selected_item) bt_charts[i].pressed = true; bt_charts[i].add_listener(this); add_component(&bt_charts[i]); } } end_table(); update_chart(true); set_windowsize(get_min_windowsize()); set_resizemode(diagonal_resize); } player_ranking_frame_t::~player_ranking_frame_t() { while (!buttons.empty()) { player_button_t* b = buttons.remove_first(); cont_players.remove_component(b); delete b; } } static int (*compare_function[player_ranking_frame_t::MAX_PLAYER_RANKING_CHARTS])(player_button_t* const& a, player_button_t* const& b) = { compare_revenue,//ATV_REVENUE, compare_profit,//ATV_OPERATING_PROFIT, compare_margin,//ATV_PROFIT_MARGIN, compare_transport_pax,//ATV_TRANSPORTED_PASSENGER, compare_transport_mail,//ATV_TRANSPORTED_MAIL, compare_transport_goods,//ATV_TRANSPORTED_GOOD, compare_cash,//ATC_CASH, compare_netwealth,//ATC_NETWEALTH, compare_convois//ATC_ALL_CONVOIS, // 1,ATV_VEHICLES, // 0,ATC_HALTS }; bool player_ranking_frame_t::is_chart_table_zero(uint8 player_nr) const { // search for any non-zero values if (player_t* player = welt->get_player(player_nr)) { const finance_t* finance = player->get_finance(); const bool is_atv = history_type_idx[selected_item * 2]; for (int y = 0; y < MAX_PLAYER_HISTORY_MONTHS; y++) { sint64 val = is_atv ? finance->get_history_veh_year((transport_type)player_ranking_frame_t::transport_type_option, y, history_type_idx[selected_item * 2 + 1]) : finance->get_history_com_year(y, history_type_idx[selected_item * 2 + 1]); if (val) return false; } } return true; } /** * This method is called if an action is triggered */ bool player_ranking_frame_t::action_triggered(gui_action_creator_t* comp, value_t v) { // Check the GUI list of buttons // player filter for (auto bt : buttons) { if (comp == bt) { if (player_t* p = welt->get_player(bt->get_player_nr())) { create_win(new money_frame_t(p), w_info, magic_finances_t + p->get_player_nr()); } return true; } } // chart selector for (uint8 i = 0; i < MAX_PLAYER_RANKING_CHARTS; i++) { if (comp == &bt_charts[i]) { if (bt_charts[i].pressed) { // nothing to do return true; } selected_item = i; update_chart(true); return true; } } if (comp == &years_back_c) { update_chart(true); return true; } if (comp == &transport_type_c) { if (v.i > 0) { transport_type_option = (uint8)v.i; transport_type_option = transport_types[v.i]; update_chart(true); } else { transport_type_option = TT_ALL; } return true; } return true; } void player_ranking_frame_t::update_chart(bool full_update) { if (years_back == 0) { // first update player finances if in the current year for (int np = 0; np < MAX_PLAYER_COUNT - 1; np++) { if (player_t* player = welt->get_player(np)) { player->get_finance()->calc_finance_history(); } } } // check if we have to upgrade to full update if (!full_update) { // check order full_update |= buttons.sort(compare_function[selected_item]); // check if the number of entries changed uint8 new_count = 0; for (uint i = 0; i < buttons.get_count(); i++) { const uint8 player_nr = buttons.at(i)->get_player_nr(); // Exclude players who are not in the competition if (player_nr == PLAYER_PUBLIC_NR || is_chart_table_zero(player_nr)) { continue; } new_count++; } full_update |= count != new_count; } // deselect chart buttons for (uint8 i = 0; i < MAX_PLAYER_RANKING_CHARTS; i++) { bt_charts[i].pressed = i == selected_item; } // rebuild list if (full_update) { cont_players.remove_all(); cont_players.set_table_layout(3, 0); cont_players.set_alignment(ALIGN_CENTER_H); cont_players.set_margin(scr_size(0, 0), scr_size(D_SCROLLBAR_WIDTH + D_H_SPACE, D_SCROLLBAR_HEIGHT)); count = 0; for (uint i = 0; i < buttons.get_count(); i++) { const uint8 player_nr = buttons.at(i)->get_player_nr(); // Exclude players who are not in the competition if (player_nr == PLAYER_PUBLIC_NR || is_chart_table_zero(player_nr)) { continue; } count++; switch (count) { case 1: cont_players.new_component("1", gfx->palette_lookup(COL_YELLOW), gui_label_t::centered); break; case 2: cont_players.new_component("2", 0, gui_label_t::centered); break; case 3: cont_players.new_component("3", 0, gui_label_t::centered); break; default: gui_label_buf_t* lb = cont_players.new_component(SYSCOL_TEXT, gui_label_t::centered); lb->buf().printf("%u", count); lb->update(); lb->set_min_width(lb->get_min_size().w); break; } if (player_nr != selected_player) { buttons.at(i)->pressed = false; } cont_players.add_component(buttons.at(i)); cont_players.add_component(&lb_player_val[i]); } cont_players.new_component_span(1,1,3); } const bool is_atv = history_type_idx[selected_item * 2]; sint32 chart_years_back = clamp(welt->get_last_year() - welt->get_settings().get_starting_year(), 2, MAX_PLAYER_HISTORY_YEARS); if (full_update) { chart.set_dimension(chart_years_back, 10000); // need to clear the chart once to update the suffix and digit chart.remove_curves(); for (int np = 0; np < MAX_PLAYER_COUNT - 1; np++) { if (np == PLAYER_PUBLIC_NR) continue; if (player_t* player = welt->get_player(np)) { if (is_chart_table_zero(np)) { continue; } // create chart const int curve_type = (int)cost_type[selected_item]; const int curve_precision = (curve_type == gui_chart_t::STANDARD) ? 0 : (curve_type == gui_chart_t::MONEY || curve_type == gui_chart_t::PERCENT) ? 2 : 1; // gui_chart_t::chart_marker_t marker = (np==selected_player) ? gui_chart_t::square : gui_chart_t::none; chart.add_curve(gfx->palette_lookup(player->get_player_color1() + 3), *p_chart_table, MAX_PLAYER_COUNT - 1, np, chart_years_back, curve_type, true, false, curve_precision, NULL); } } for (int np = 0; np < MAX_PLAYER_COUNT - 1; np++) { if (player_t* player = welt->get_player(np)) { // update chart records for (int y = 0; y < chart_years_back; y++) { const finance_t* finance = player->get_finance(); p_chart_table[y][np] = is_atv ? finance->get_history_veh_year((transport_type)player_ranking_frame_t::transport_type_option, y, history_type_idx[selected_item * 2 + 1]) : finance->get_history_com_year(y, history_type_idx[selected_item * 2 + 1]); } chart.show_curve(np); } } } // update labels years_back = clamp(years_back_c.get_selection(), 0, chart_years_back); scr_size cont_min_sz = cont_players.get_min_size(); for (uint i = 0; i < buttons.get_count(); i++) { const uint8 player_nr = buttons.at(i)->get_player_nr(); // Exclude players who are not in the competition if (player_nr == PLAYER_PUBLIC_NR || is_chart_table_zero(player_nr)) { continue; } if (player_nr != selected_player) { buttons.at(i)->pressed = false; } const finance_t* finance = welt->get_player(player_nr)->get_finance(); PIXVAL color = SYSCOL_TEXT; sint64 value = is_atv ? finance->get_history_veh_year((transport_type)player_ranking_frame_t::transport_type_option, years_back, history_type_idx[selected_item * 2 + 1]) : finance->get_history_com_year(years_back, history_type_idx[selected_item * 2 + 1]); switch (cost_type[selected_item]) { case gui_chart_t::MONEY: lb_player_val[i].buf().append_money(value / 100.0); color = value >= 0 ? (value > 0 ? MONEY_PLUS : SYSCOL_TEXT_UNUSED) : MONEY_MINUS; break; case gui_chart_t::PERCENT: lb_player_val[i].buf().append(value / 100.0, 2); lb_player_val[i].buf().append("%"); color = value >= 0 ? (value > 0 ? MONEY_PLUS : SYSCOL_TEXT_UNUSED) : MONEY_MINUS; break; case gui_chart_t::STANDARD: default: lb_player_val[i].buf().append(value, 0); break; } lb_player_val[i].set_color(color); lb_player_val[i].set_align(gui_label_t::right); lb_player_val[i].update(); } transport_type_c.set_visible(is_atv); scrolly.set_visible(count > 0); if (full_update || cont_min_sz!=cont_players.get_min_size()) { resize(scr_size(0, 0)); reset_min_windowsize(); } } void player_ranking_frame_t::draw(scr_coord pos, scr_size size) { if (last_year != world()->get_last_year()) { // new year has started: last_year = world()->get_last_year(); sint32 years_back = clamp(last_year - welt->get_settings().get_starting_year(), 2, MAX_PLAYER_HISTORY_YEARS); // rebuild year list sint32 sel = years_back_c.get_selection(); years_back_c.clear_elements(); years_back_c.new_component(translator::translate("This Year"), SYSCOL_TEXT); years_back_c.new_component(translator::translate("Last Year"), SYSCOL_TEXT); for (int i = 1; i < years_back; i++) { sprintf(years_back_s[i - 1], "%i", last_year - i - 1); years_back_c.new_component(years_back_s[i - 1], SYSCOL_TEXT); } chart.set_seed(last_year); years_back_c.set_selection(sel <= 0 ? 0 : (sel == 1 ? 1 : sel + 1)); update_chart(true); } else { update_chart(false); } gui_frame_t::draw(pos, size); } void player_ranking_frame_t::rdwr(loadsave_t* file) { transport_type_c.rdwr(file); years_back_c.rdwr(file); file->rdwr_byte(selected_item); if (file->is_loading()) { last_year = world()->get_last_year(); chart.set_seed(last_year); update_chart(true); } scrolly.rdwr(file); } simutrans-124.5/src/simutrans/gui/player_ranking_frame.h000066400000000000000000000043261520675460500235210ustar00rootroot00000000000000/* * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_PLAYER_RANKING_GUI_H #define GUI_PLAYER_RANKING_GUI_H #include "gui_frame.h" #include "../player/finance.h" #include "components/gui_label.h" #include "components/gui_chart.h" #include "components/gui_button.h" #include "components/action_listener.h" #include "simwin.h" #include "../tpl/vector_tpl.h" #include "../utils/cbuffer.h" class player_button_t : public button_t { uint8 player_nr; public: player_button_t(uint8 player_nr); // set color and name void update(); uint8 get_player_nr() const { return player_nr; } }; /** * Menu for the player list */ class player_ranking_frame_t : public gui_frame_t, private action_listener_t { public: enum { PR_REVENUE, PR_PROFIT, PR_MARGIN, PR_TRANSPORT_PAX, PR_TRANSPORT_MAIL, PR_TRANSPORT_GOODS, PR_CASH, PR_NETWEALTH, PR_CONVOIS, // PR_VEHICLES, // PR_HALTS, MAX_PLAYER_RANKING_CHARTS }; static uint8 transport_type_option; private: sint16 last_year; gui_chart_t chart; gui_aligned_container_t cont_players; gui_scrollpane_t scrolly; button_t bt_charts[MAX_PLAYER_RANKING_CHARTS]; char years_back_s[MAX_PLAYER_HISTORY_YEARS][5]; gui_combobox_t years_back_c, transport_type_c; uint16 transport_types[TT_OTHER]; gui_label_buf_t lb_player_val[MAX_PLAYER_COUNT-1]; sint64 p_chart_table[MAX_PLAYER_HISTORY_YEARS][MAX_PLAYER_COUNT-1]; slist_tpl buttons; uint8 selected_item= PR_REVENUE; uint8 selected_player; uint8 count; bool is_chart_table_zero(uint8 player_nr) const; public: player_ranking_frame_t(uint8 selected_player_nr=255); ~player_ranking_frame_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE { return "player_ranking.txt"; } void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void update_chart(bool full_update); // since no information are needed to be saved to restore this, returning magic is enough uint32 get_rdwr_id() OVERRIDE { return magic_player_ranking; } void rdwr(loadsave_t* file) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/privatesign_info.cc000066400000000000000000000042141520675460500230420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "privatesign_info.h" #include "components/gui_label.h" #include "../obj/roadsign.h" #include "../player/simplay.h" #include "../tool/simmenu.h" #include "../world/simworld.h" privatesign_info_t::privatesign_info_t(roadsign_t* s) : obj_infowin_t(s), sign(s) { uint16 mask = sign->get_player_mask(); for( int i=0; iget_player(i) ) { players[i].init( button_t::square_state, welt->get_player(i)->get_name()); players[i].add_listener( this ); } else { players[i].init( button_t::square_state, ""); players[i].disable(); } players[i].pressed = mask & (1<get_desc()->get_copyright()) { gui_label_buf_t* lb = new_component(); lb->buf().printf(translator::translate("Constructed by %s"), maker); lb->update(); } recalc_size(); } /** * This method is called if an action is triggered * * Returns true, if action is done and no more * components should be triggered. */ bool privatesign_info_t::action_triggered( gui_action_creator_t *comp, value_t /* */) { if( welt->get_active_player() == sign->get_owner() ) { char param[256]; for( int i=0; iget_player_mask(); mask ^= (1 << i); // change active player mask for this private sign if( i<8 ) { sprintf( param, "%s,2,%u", sign->get_pos().get_str(), mask & 0x00FF ); } else { sprintf( param, "%s,0,%u", sign->get_pos().get_str(), mask >> 8 ); } tool_t::simple_tool[TOOL_CHANGE_TRAFFIC_LIGHT]->set_default_param( param ); welt->set_tool( tool_t::simple_tool[TOOL_CHANGE_TRAFFIC_LIGHT], welt->get_active_player() ); players[i].pressed = (mask >> i)&1; } } } return true; } // notify for an external update void privatesign_info_t::update_data() { uint16 mask = sign->get_player_mask(); for( int i=0; i> i)&1; } } simutrans-124.5/src/simutrans/gui/privatesign_info.h000066400000000000000000000015651520675460500227120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_PRIVATESIGN_INFO_H #define GUI_PRIVATESIGN_INFO_H #include "../simconst.h" #include "obj_info.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_container.h" class roadsign_t; /** * Info window for factories */ class privatesign_info_t : public obj_infowin_t, public action_listener_t { private: roadsign_t* sign; button_t players[PLAYER_UNOWNED]; public: privatesign_info_t(roadsign_t* s); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "privatesign_info.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; // called, after external change void update_data(); }; #endif simutrans-124.5/src/simutrans/gui/savegame_frame.cc000066400000000000000000000417361520675460500224500ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "savegame_frame.h" #include "../pathes.h" #include "../sys/simsys.h" #include "../simdebug.h" #include "simwin.h" #include "../utils/simstring.h" #include "../utils/searchfolder.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "banner.h" #define L_DEFAULT_ROWS (12) // Number of file entries to show as default #define L_SHORTENED_SIZE (48) /** * Small helper class for tiny 'X' delete buttons */ class del_button_t : public button_t { scr_coord_val w; public: del_button_t() : button_t() { init(button_t::roundbox, "X"); w = max(D_BUTTON_HEIGHT, gfx->get_char_width('X') + D_BUTTON_PADDINGS_X); } scr_size get_min_size() const OVERRIDE { return scr_size(w, D_BUTTON_HEIGHT); } }; /** * @param suffix Optional file pattern to populate the file list. * Example ".sve" or "sve" * Default value is NULL to disregard extension. * @param only_directories Populate the file list with only directories. This is * optional with a default value of false. * @param path Optional search path. If null next call to add_path() * defines the deefault path. Default value is NULL. * @param delete_enabled Show (true) or hide (false) the delete buttons. * This is an optional parameter with a default value of true. * @param back_to_menu */ savegame_frame_t::savegame_frame_t(const char *suffix, bool only_directories, const char *path, const bool delete_enabled, bool back_to_menu) : gui_frame_t( translator::translate("Load/Save") ), suffix(suffix), in_action(false), only_directories(only_directories), searchpath_defined(false), input(), fnlabel("Filename"), scrolly(&button_frame, true), num_sections(0), delete_enabled(delete_enabled) { is_back_to_menu = back_to_menu; set_table_layout(1,0); label_enabled = true; // Filename input top_frame.set_table_layout(2,0); add_component(&top_frame); { top_frame.add_component(&fnlabel); tstrncpy(ibuf, "", lengthof(ibuf)); input.set_text(ibuf, 128); top_frame.add_component(&input); } // Needs to be scrollable, size is adjusted in set_windowsize() scrolly.set_scroll_amount_y(D_BUTTON_HEIGHT + D_FOCUS_OFFSET_V); scrolly.set_size_corner(false); add_component(&scrolly); scrolly.set_maximize(true); // Controls below will be sized and positioned in set_windowsize() new_component(); add_component(&bottom_left_frame); bottom_left_frame.set_table_layout(1,0); add_table(3, 1)->set_force_equal_columns(true); { new_component(); savebutton.init(button_t::roundbox | button_t::flexible, "Ok"); savebutton.add_listener(this); add_component(&savebutton); cancelbutton.init(button_t::roundbox | button_t::flexible, "Cancel"); cancelbutton.add_listener(this); add_component(&cancelbutton); } end_table(); top_frame.set_focus( &input ); set_focus(&top_frame); if(this->suffix == NULL) { this->suffix = ""; } if(path != NULL) { this->add_path(path); // needed? dr_mkdir(path); } set_resizemode(diagonal_resize); } /** * Free all list items. */ savegame_frame_t::~savegame_frame_t() { for(dir_entry_t const& i : entries) { if(i.button) { delete [] const_cast(i.button->get_text()); delete i.button; } if(i.label) { char *tooltip = const_cast(i.label->get_tooltip_pointer()); delete [] tooltip; delete [] const_cast(i.label->get_text_pointer()); delete i.label; } delete i.del; delete [] i.info; } this->paths.clear(); } /** * Adds a section entry to the list... * * @param name Section name?!? */ void savegame_frame_t::add_section(std::string &name){ const char *prefix_label = translator::translate("Files from:"); size_t prefix_len = strlen(prefix_label); // NOTE: These char buffers will be freed on the destructor // +2 because of the space in printf and the ending \0 char *label_text = new char [L_SHORTENED_SIZE+prefix_len+2]; char *path_expanded = new char[FILENAME_MAX]; const size_t data_dir_len = strlen(env_t::base_dir); if( name[0]=='/' || name[0]=='\\' || name[1]==':' || strncmp(name.c_str(),env_t::base_dir,data_dir_len) == 0 ) { // starts with data_dir or an absolute path tstrncpy(path_expanded, name.c_str(), FILENAME_MAX); } else { // user_dir path size_t name_len = strlen(name.c_str()); size_t user_dir_len = strlen(env_t::user_dir); if ( name_len+user_dir_len > FILENAME_MAX-1 ) { // shouldn't happen, but I'll control anyway strcpy(path_expanded,"** ERROR ** Path too long"); } else { sprintf(path_expanded,"%s%s", env_t::user_dir, name.c_str()); } } cleanup_path(path_expanded); char shortened_path[L_SHORTENED_SIZE+1]; shorten_path(shortened_path,path_expanded,L_SHORTENED_SIZE); sprintf(label_text,"%s %s", prefix_label , shortened_path); gui_label_t* l = new gui_label_t(NULL, SYSCOL_TEXT_HIGHLIGHT); l->set_text_pointer(label_text); l->set_tooltip(path_expanded); this->entries.append(dir_entry_t(NULL, NULL, l, LI_HEADER, NULL)); this->num_sections++; } /** * Adds a path to the list of path included in the file search. * Several paths can be added one at a time. All added paths will * be searched by fill_list(). * * @param path A nul terminated path to include in the search. */ void savegame_frame_t::add_path(const char * path) { if (!this->searchpath_defined) { sprintf(this->searchpath, "%s", path); this->searchpath_defined = true; } this->paths.append(path); } /** * Populates the item list with matching file names. Each matching file * is first checked (check_file) and then added (add_file). */ void savegame_frame_t::fill_list( void ) { const char *suffixnodot; searchfolder_t sf; char *fullname; bool not_cutting_extension = (suffix==NULL || suffix[0]!='.'); if( suffix == NULL ){ suffixnodot = NULL; } else { suffixnodot = (suffix[0] == '.') ?suffix+1 : suffix; } // for each path, we search. for(std::string &path : paths){ const char *path_c = path.c_str(); const size_t path_c_len = strlen(path_c); const searchfolder_t::search_flags_t search_flags = this->only_directories ? searchfolder_t::SF_ONLYDIRS : searchfolder_t::SF_NONE; sf.search(path, std::string(suffixnodot), search_flags, false); bool section_added = false; // Add the entries that pass the check for(const char* const &name : sf) { fullname = new char [path_c_len+strlen(name)+1]; sprintf(fullname,"%s%s",path_c,name); if( check_file(fullname, suffix) ){ if(!section_added) { add_section(path); section_added = true; } add_file(fullname, name, get_info(fullname), not_cutting_extension); } else { // NOTE: we just free "fullname" memory when add_file is not called. That memory will be // freed in the class destructor. This way we save the cost of re-allocate/copy it inside there delete [] fullname; } } } } /** * All items has been inserted in the list. * On return resize() is called and all item's GYU members are positioned and resized. * Therefore it is no use to set the button's and label's width or any items y position. * The only control keeping its size is the delete button. */ void savegame_frame_t::list_filled( void ) { uint cols = (delete_enabled ? 1 : 0) + 1 + (label_enabled ? 1 : 0); button_frame.set_table_layout(1,0); button_frame.add_table(cols,0)->set_spacing(scr_size(D_H_SPACE,D_FILELIST_V_SPACE)); // less vertical spacing between entries to see more of them on screen button_t *pressed_button = NULL; for(dir_entry_t const& i : entries) { button_t* const delete_button = i.del; button_t* const action_button = i.button; gui_label_t* const label = i.label; if(i.type == LI_HEADER) { if(this->num_sections < 2) { // If just 1 section added, we won't print the header label->set_visible(false); continue; } button_frame.add_component(label, cols); } else { if (dr_cantrash()) { delete_button->set_tooltip("Send this file to the system trash bin. SHIFT+CLICK to permanently delete."); } else { delete_button->set_tooltip("Delete this file."); } delete_button->add_listener(this); action_button->add_listener(this); if (delete_enabled) { button_frame.add_component(delete_button); } button_frame.add_component(action_button); if (label_enabled) { button_frame.add_component(label); } if (pressed_button == NULL && action_button->pressed) { pressed_button = action_button; } } } button_frame.end_table(); const scr_coord_val row_height = max( D_LABEL_HEIGHT, D_BUTTON_HEIGHT ) + D_V_SPACE; reset_min_windowsize(); scr_size size = get_min_size() + scr_size(0, min(entries.get_count(), L_DEFAULT_ROWS) * row_height); // TODO do something smarter here size.w = max(size.w, button_frame.get_min_size().w + D_SCROLLBAR_WIDTH); set_windowsize(size); if (pressed_button) { scrolly.set_scroll_position(0, max(0, pressed_button->get_pos().y - 2 * D_BUTTON_HEIGHT) ); } } /** * Check if a file name qualifies to be added to he item list. * * @param filename The found file name * @param suffix Suffix pattern to match. If this is NULL * all filenames qualifies. * * @retval true The filename qualified and will be added. * @retval false This file doesn't qualify, don't add. */ bool savegame_frame_t::check_file(const char *filename, const char *suffix) { // assume truth, if there is no pattern to compare return suffix==NULL || suffix[0]==0 || (strncmp(filename+strlen(filename)-4, suffix, 4)== 0); } /** * Create and add a list item from the given parameters. The button is set * to the filename, the label to the string returned by get_info(). * * @param fullpath The full path to associate with this item. * @param filename The file name to assign the action button (i.button). * @param info Information to set in the label. * @param no_cutting_suffix Keep the suffix (true) in the file name. */ void savegame_frame_t::add_file(const char *fullpath, const char *filename, const char *info, const bool no_cutting_suffix) { button_t *button = new button_t(); char *name = new char[strlen(filename)+10]; char *text = new char[strlen(info)+1]; strcpy(text, info); strcpy(name, filename); if(!no_cutting_suffix) { name[strlen(name)-4] = '\0'; } button->set_typ( button_t::roundbox | button_t::flexible); button->set_no_translate(true); button->set_text(name); // to avoid translation std::string const compare_to = !env_t::pak_name.empty() ? env_t::pak_name.substr(0, env_t::pak_name.size() - 1) + " -" : std::string(); // sort descending with respect to compare_items slist_tpl::iterator i = entries.begin(); slist_tpl::iterator end = entries.end(); // This needs optimizing, advance to the last section, since inserts come always to the last section, we could just update last one on last_section slist_tpl::iterator lastfound; while(i != end) { if(i->type == LI_HEADER) { lastfound = i; } i++; } i = ++lastfound; // END of optimizing if(!strstart(info, compare_to.c_str())) { // skip current ones while(i != end) { // extract pakname in same format than in savegames ... if(!strstart(i->label->get_text_pointer(), compare_to.c_str())) { break; } ++i; } // now sort with respect to label on button or info text (ie date) while(i != end) { if( compare_items(*i, text, name ) ) { break; } ++i; } } else { // Insert to our games (or in front if none) while(i != end) { if(i->type == LI_HEADER) { ++i; continue; } if(compare_items( *i, text, name ) ) { break; } // not our savegame any more => insert if(!strstart(i->label->get_text_pointer(), compare_to.c_str())) { break; } ++i; } } gui_label_t* l = new gui_label_t(NULL); l->set_text_pointer(text); entries.insert(i, dir_entry_t(button, new del_button_t(), l, LI_ENTRY, fullpath)); } /** * This dialogue's message event handler. The enter key is dispateched as * an action button click event. The WIN_OPEN event starts to fill the file * list if it is empty. * * @param event The received event message. * * @retval true Stop event propagation. * @retval false Continue event propagation. */ bool savegame_frame_t::infowin_event(const event_t *event) { if(event->ev_class == INFOWIN && event->ev_code == WIN_OPEN && entries.empty()) { // before no virtual functions can be used ... fill_list(); // Notify of the end list_filled(); } if( IS_KEYDOWN(event) && event->ev_code == SIM_KEYCODE_ENTER ) { action_triggered(&input, (long)0); return true; // swallowed } return gui_frame_t::infowin_event(event); } /** * Click event handler and dispatcher. This function is called * every time a button is clicked and the corresponding handler * is called from here. * * @param component The component that was clicked. * * @retval true This function always returns true to stop * the event propagation. */ bool savegame_frame_t::action_triggered(gui_action_creator_t *component, value_t ) { char buf[PATH_MAX] = {}; if(component==&input || component==&savebutton) { // Save/Load Button or Enter-Key pressed //--------------------------------------- if(strstart(ibuf, "net:")) { tstrncpy(buf, ibuf, lengthof(buf)); } else { if(searchpath_defined) { tstrncpy(buf, searchpath, lengthof(buf)); } else { buf[0] = 0; } strcat(buf, ibuf); if(suffix) { strcat(buf, suffix); } } ok_action(buf); destroy_win(this); } else if(component == &cancelbutton) { // Cancel-button pressed //---------------------------- cancel_action(buf); if(is_back_to_menu) { banner_t::show_banner(); } destroy_win(this); } else { // File in list selected //-------------------------- for(dir_entry_t const& i : entries) { if(in_action){ break; } if(component==i.button || component==i.del) { in_action = true; bool const action_btn = component == i.button; if(action_btn) { if(item_action(i.info)) { destroy_win(this); } } else { if(del_action(i.info)) { destroy_win(this); } else { // do not delete components // simply hide them i.button->set_visible(false); i.del->set_visible(false); i.label->set_visible(false); resize(scr_coord(0, 0)); } } in_action = false; break; } } } return true; } /** * Generic delete button click handler. This will delete the * item from the storage media. If the system supports a * trash bin, the file is moved over there instead of being deleted. * A shift + Delete always deletes the file imediatly * * @param fullpath Full path to the file being deleted. * * @retval false This function always return false to prevent the * dialogue from being closed. */ bool savegame_frame_t::del_action(const char *fullpath) { if (!dr_cantrash() || event_get_last_control_shift() & 1) { // shift pressed, delete without trash bin dr_remove(fullpath); return false; } dr_movetotrash(fullpath); return false; } /** * Sets the current filename in the input box * * @param file_name A nul terminated string to assign the edit control. */ void savegame_frame_t::set_filename(const char *file_name) { size_t len = strlen(file_name); if(len>=4 && len-SAVE_PATH_X_LEN-3<128) { if(strstart(file_name, SAVE_PATH_X)) { tstrncpy(ibuf, file_name+SAVE_PATH_X_LEN, len-SAVE_PATH_X_LEN-3 ); } else { tstrncpy(ibuf, file_name, len-3); } input.set_text(ibuf, 128); } } /** * ONLY WIN32 * Translates all / into \ in a given path string. If a drive * letter is present it is translated to upper case * * @param path A pointer to the path string. This string is modified. */ void savegame_frame_t::cleanup_path(char *path) { #ifdef _WIN32 char *p = path; while (*(p++) != '\0'){ if(*p == '/') { *p='\\'; } } if ( strlen(path)>2 && path[1]==':' ) { path[0] = (char) toupper(path[0]); } #else (void)path; #endif } /** * Outputs a truncated path by replacing the middle portion with "..." * * @param dest Destination string. * @param source Nul terminated source string to parse. * @param max_size Truncate the string to this number of characters. */ void savegame_frame_t::shorten_path(char *dest,const char *source,const size_t max_size) { assert (max_size > 2); const size_t orig_size = strlen(source); if ( orig_size <= max_size ) { strcpy(dest,source); return; } const int half = max_size/2; const int odd = max_size%2; strncpy(dest,source,half-1); memcpy(&dest[half-1],"...",sizeof(char) * 3); strcpy(&dest[half+2],&source[orig_size-half+2-odd]); } bool savegame_frame_t::compare_items ( const dir_entry_t & entry, const char *, const char *name ) { return (strcmp(name, entry.button->get_text()) < 0); } simutrans-124.5/src/simutrans/gui/savegame_frame.h000066400000000000000000000132271520675460500223040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SAVEGAME_FRAME_H #define GUI_SAVEGAME_FRAME_H #include #include #include "../utils/simstring.h" #include "gui_frame.h" #include "../tpl/slist_tpl.h" #include "../tpl/vector_tpl.h" #include "components/action_listener.h" #include "components/gui_container.h" #include "components/gui_scrollpane.h" #include "components/gui_textinput.h" #include "components/gui_divider.h" #include "components/gui_label.h" #include "components/gui_button.h" class loadfont_frame_t; /** * Base class from which all GUI dialogs to load/save generics can inherit from * @note When I refer to a "qualified" path I mean it can contain sub-directories or even fully qualified path. i.e. : "save/a.sve" or "c:\simutrans\scenario\file.nut" */ class savegame_frame_t : public gui_frame_t, public action_listener_t { friend class loadfont_frame_t; private: vector_tpl paths; //@< Paths in which this dialog will search for const char *suffix; //@< Extension of the files this dialog will use, can be NULL Can include or not the "." at start, will work on both cases char ibuf[PATH_MAX]; //@< Input buffer for the text input component char searchpath[PATH_MAX]; //@< Default search path bool in_action; //@< To avoid double mouse action bool only_directories; //@< Search for directories (used in pak_selector) bool searchpath_defined; //@< Is default path defined? bool is_back_to_menu; //@< Open the banner menu on cancel void add_section(std::string &name); protected: /** * Entries in list can be actual file entries or * headers, that have a different look. */ enum dirlist_item_t { LI_HEADER, //@< This is a header list item. LI_ENTRY //@< This is a data list item. }; /** * A list item. * Max Kielland: Shouldn't this be an ADT and then have * each derivate to define their own item class? This would also * take care of differences, sorting and freeing resources. */ struct dir_entry_t { dir_entry_t(button_t* button_, button_t* del_, gui_label_t* label_, dirlist_item_t type_ = LI_ENTRY, const char *info_=NULL) : del(del_), button(button_), label(label_), type(type_), info(info_) {} button_t *del; //@< Delete button placed in the first column. button_t *button; //@< Action button placed in the second column. gui_label_t *label; //@< Label placed in the third column. dirlist_item_t type; //@< Item type, data or header. const char *info; //@< A qualified path (might be relative) to the file, not just the name static int compare (const dir_entry_t & l, const dir_entry_t &r) { if (( NULL == l.info ) && ( NULL != r.info )) { return -1; } if (( NULL == r.info ) && ( NULL != l.info )) { return 1; } if (( NULL != l.info ) && ( NULL != r.info )) { return strcmp ( l.info, r.info ); } return 0; } }; // Standard GUI controls in dialogue gui_textinput_t input; //@< Filename input field button_t savebutton; //@< Save button button_t cancelbutton; //@< Cancel button gui_label_t fnlabel; //@< Static file name label gui_aligned_container_t top_frame, //@< Contains input field bottom_left_frame, //@< container for elements on the left of the last row button_frame; //@< Gui container for all items gui_scrollpane_t scrolly; //@< Scroll panel for the GUI container slist_tpl entries; //@< Internal list representing the file listing uint32 num_sections; //@< Internal counter representing the number of sections added to the list bool delete_enabled; //@< Show the first column of delete buttons. bool label_enabled; //@< Show the third column of labels. void add_file ( const char *path, const char *filename, const char *pak, const bool no_cutting_suffix ); void add_path ( const char *path ); void set_filename ( const char *file_name ); void set_extension( const char *ext ) { suffix = ext; } void cleanup_path ( char *path ); void shorten_path ( char *dest, const char *source, const size_t max_size ); void list_filled ( void ); virtual void fill_list ( void ); // compare item to another with info and filename virtual bool compare_items ( const dir_entry_t & entry, const char *info, const char *filename ); // Virtual callback function that will be executed when the user clicks ok, virtual bool cancel_action ( const char * /*fullpath*/ ) { return true; } // Callback for cancel button click virtual bool del_action ( const char * fullpath ); // Callback for delete button click virtual bool ok_action ( const char * /*fullpath*/ ) { return true; } // Callback for ok button click virtual bool check_file ( const char *filename, const char *suffix ); // Pure virtual functions virtual const char *get_info ( const char *fname ) = 0; virtual bool item_action ( const char *fullpath ) = 0; public: savegame_frame_t(const char *suffix, bool only_directorie, const char *path, const bool delete_enabled = true, const bool back_to_menu = false ); // savegame_frame_t(const char *suffix = NULL, bool only_directories = false, const char *path = NULL, const bool delete_enabled = true); virtual ~savegame_frame_t(); bool action_triggered ( gui_action_creator_t*, value_t ) OVERRIDE; bool infowin_event ( event_t const* ) OVERRIDE; sint16 get_entries_count () { return entries.get_count(); } ; }; #endif simutrans-124.5/src/simutrans/gui/scenario_frame.cc000066400000000000000000000041431520675460500224520ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "../sys/simsys.h" #include "scenario_frame.h" #include "scenario_info.h" #include "messagebox.h" #include "simwin.h" #include "../world/simworld.h" #include "../tool/simmenu.h" #include "../dataobj/environment.h" #include "../dataobj/scenario.h" #include "../dataobj/translator.h" #include "../network/network.h" #include "../network/network_cmd.h" #include "../utils/cbuffer.h" scenario_frame_t::scenario_frame_t() : savegame_frame_t(NULL, true, NULL, false, true) { static cbuffer_t pakset_scenario; static cbuffer_t addons_scenario; pakset_scenario.clear(); pakset_scenario.printf("%sscenario/", env_t::pak_dir.c_str()); addons_scenario.clear(); addons_scenario.printf("addons/%sscenario/", env_t::pak_name.c_str()); if (env_t::default_settings.get_with_private_paks()) { this->add_path(addons_scenario); } this->add_path(pakset_scenario); easy_server.init( button_t::square_automatic, "Start this as a server"); bottom_left_frame.add_component(&easy_server); set_name(translator::translate("Load scenario")); set_focus(NULL); } void scenario_frame_t::load_scenario(const char *fullpath, bool is_easy_server) { cbuffer_t param; param.printf("%i,%s", is_easy_server, fullpath); tool_t::simple_tool[TOOL_LOAD_SCENARIO]->set_default_param(param); welt->set_tool(tool_t::simple_tool[TOOL_LOAD_SCENARIO], NULL); tool_t::simple_tool[TOOL_LOAD_SCENARIO]->set_default_param(0); } /** * Action, started after button pressing. */ bool scenario_frame_t::item_action(const char *fullpath) { scenario_frame_t::load_scenario(fullpath, easy_server.pressed); return true; } const char *scenario_frame_t::get_info(const char *filename) { static char info[PATH_MAX]; sprintf(info,"%s",str_get_filename(filename, false).c_str()); return info; } bool scenario_frame_t::check_file( const char *filename, const char * ) { char buf[PATH_MAX]; sprintf( buf, "%s/scenario.nut", filename ); if (FILE* const f = dr_fopen(buf, "r")) { fclose(f); return true; } return false; } simutrans-124.5/src/simutrans/gui/scenario_frame.h000066400000000000000000000020051520675460500223070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SCENARIO_FRAME_H #define GUI_SCENARIO_FRAME_H #include "savegame_frame.h" #include "../utils/cbuffer.h" class scenario_frame_t : public savegame_frame_t { private: cbuffer_t path; button_t easy_server; protected: /** * Action that's started by the press of a button. */ bool item_action(const char *fullpath) OVERRIDE; /** * Action, started after X-Button pressing */ bool del_action(const char *f) OVERRIDE { return item_action(f); } // returns extra file info const char *get_info(const char *fname) OVERRIDE; public: // true, if valid bool check_file( const char *filename, const char *suffix ) OVERRIDE; /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE { return "scenario.txt"; } void load_scenario(const char *fullpath, bool is_easy_server = false); scenario_frame_t(); }; #endif simutrans-124.5/src/simutrans/gui/scenario_info.cc000066400000000000000000000131061520675460500223120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "scenario_info.h" #include "../world/simworld.h" #include "../tool/simtool.h" #include "../display/viewport.h" #include "../obj/zeiger.h" #include "../dataobj/scenario.h" #include "../dataobj/translator.h" #include "../utils/simstring.h" static dynamic_string* tab2dyn[scenario_t::SCRIPT_DEBUG + 1]; bool scenario_info_t::update_dynamic_texts(gui_flowtext_t *flow, dynamic_string *text, scr_size size, bool init) { if (text->has_changed() || init) { flow->set_text( *text ); text->clear_changed(); flow->set_size( size ); set_dirty(); return true; } return false; } uint16 scenario_info_t::get_tab_index(const char* which) { const char *shorts[] = { "info", "goal", "rules", "result", "about", "debug" }; for (uint i = 0; iget_scenario(); tabs.add_tab(&info, translator::translate("Scenario Info")); tab2dyn[i++] = &(scen->info_text); tabs.add_tab(&goal, translator::translate("Scenario Goal")); tab2dyn[i++] = &(scen->goal_text); tabs.add_tab(&rule, translator::translate("Scenario Rules")); tab2dyn[i++] = &(scen->rule_text); tabs.add_tab(&result, translator::translate("Scenario Result")); tab2dyn[i++] = &(scen->result_text); tabs.add_tab(&about, translator::translate("About scenario")); tab2dyn[i++] = &(scen->about_text); tabs.add_tab(&debug_msg, translator::translate("Scenario Debug")); tab2dyn[i++] = &(scen->debug_text); // cannot update text => possibly opened from sync_step hash_text = 0; // fetch possible error message const char *err_text = welt->get_scenario()->get_error_text(); if (err_text) { tabs.add_tab(&error, translator::translate("Scenario Error Log")); error.set_text( err_text ); } add_component(&tabs); tabs.add_listener(this); scr_coord pane_pos(D_MARGIN_LEFT, D_MARGIN_TOP); gui_flowtext_t *texts[] = { &info, &goal, &rule, &result, &about, &error, &debug_msg}; for(uint32 i=0; iset_pos(pane_pos); texts[i]->add_listener(this); } set_resizemode(diagonal_resize); const scr_size screen = gfx->get_screen_size(); scr_size ms(min(screen.w / 2, 500), min(screen.h / 2, 300)); set_min_windowsize(ms); set_windowsize(ms); } /** * fetches actualized texts and resizes flow text element */ void scenario_info_t::update_scenario_texts() { // we only update the current tab ... scenario_t *scen = welt->get_scenario(); if (!scen->active()) { return; } scr_size border_size = get_client_windowsize() - info.get_pos() - scr_size(D_MARGIN_RIGHT + D_SCROLLBAR_WIDTH, D_MARGIN_BOTTOM + D_SCROLLBAR_HEIGHT); int active = tabs.get_active_tab_index(); gui_flowtext_t* ft = dynamic_cast(tabs.get_aktives_tab()); // since we do not update the scroll position if the top text did not changed uint32 new_hash_text = string_to_hash((const char *)(*tab2dyn[active]), 64); int x = ft->get_scroll_x(); int y = ft->get_scroll_y(); bool changed = update_dynamic_texts(ft, tab2dyn[active], border_size, hash_text==0); const char* d = scen->debug_text; debug_msg.set_visible(d && *d); if (changed || new_hash_text != hash_text) { set_windowsize(get_windowsize()); } if (new_hash_text == hash_text) { // first 256 bytes the same => keep scroll position ft->set_scroll_position(x, y); } hash_text = new_hash_text; } void scenario_info_t::draw(scr_coord pos, scr_size size) { update_scenario_texts(); gui_frame_t::draw(pos, size); } bool scenario_info_t::action_triggered( gui_action_creator_t *comp, value_t v) { if ( comp == &tabs ) { set_dirty(); hash_text = 0; // forces redraw } if ( comp == &info || comp == &goal || comp == &rule || comp == &result || comp == &about || comp == &debug_msg ) { // parse hyperlink const char *link = (const char*)v.p; if (link && *link) { if (link[0]=='(') { // jump to coordinate int x=-1, y=-1, z=-1; // try 3d coordinates first int n = sscanf(link, "(%i,%i,%i)", &x, &y, &z); if (n < 3) { // now try 2d n = sscanf(link, "(%i,%i)", &x, &y); } if (n >= 2) { // at least 2d coordinates supplied koord k(x,y); welt->get_scenario()->koord_sq2w( k ); if (welt->is_within_limits(k)) { koord3d p(k,z); if (n < 3) { // take z coordinate from ground p = welt->lookup_kartenboden(k)->get_pos(); } welt->get_viewport()->change_world_position( p ); welt->get_zeiger()->change_pos( p ); const char* err = welt->get_scenario()->jump_to_link_executed(p); if (err) { open_error_msg_win(err); } } } } else if (const char* func = strstart(link, "script:") ) { const char* err = welt->get_scenario()->eval_string(func); if (err && strcmp(err, "suspended") != 0) { dbg->warning("scenario_info_t::action_triggered", "error `%s' when evaluating: %s", err, func); } } else { open_tab(link); } } } return true; } void scenario_info_t::open_tab(const char* which) { tabs.set_active_tab_index(get_tab_index(which)); hash_text = 0; resize(scr_coord(0,0)); set_dirty(); } void scenario_info_t::rdwr( loadsave_t *file ) { // window size scr_size size = get_windowsize(); size.rdwr( file ); tabs.rdwr(file); if( file->is_loading() ) { reset_min_windowsize(); set_windowsize(size); } } simutrans-124.5/src/simutrans/gui/scenario_info.h000066400000000000000000000023341520675460500221550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SCENARIO_INFO_H #define GUI_SCENARIO_INFO_H #include "gui_frame.h" #include "simwin.h" #include "components/gui_flowtext.h" #include "components/gui_tab_panel.h" class dynamic_string; /** * All messages since the start of the program */ class scenario_info_t : public gui_frame_t, private action_listener_t { private: gui_tab_panel_t tabs; gui_flowtext_t info, goal, rule, result, about, error, debug_msg; uint32 hash_text; bool update_dynamic_texts(gui_flowtext_t *flow, dynamic_string *text, scr_size size, bool init); uint16 get_tab_index(const char* which); public: scenario_info_t(); /** * This method is called if an action is triggered * * Returns true, if action is done and no more * components should be triggered. */ bool action_triggered( gui_action_creator_t *comp, value_t extra) OVERRIDE; void draw(scr_coord pos, scr_size size) OVERRIDE; void update_scenario_texts(); void open_tab(const char* which); int get_open_tab() { return tabs.get_active_tab_index(); } uint32 get_rdwr_id() OVERRIDE { return magic_scenario_info; } void rdwr( loadsave_t *file ) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/schedule_list.cc000066400000000000000000000335031520675460500223260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "messagebox.h" #include "schedule_list.h" #include "line_management_gui.h" #include "components/gui_convoiinfo.h" #include "line_item.h" #include "simwin.h" #include "../simcolor.h" #include "../obj/depot.h" #include "../simhalt.h" #include "../world/simworld.h" #include "../simevent.h" #include "../display/simgraph.h" #include "../simskin.h" #include "../simconvoi.h" #include "../vehicle/vehicle.h" #include "../simlinemgmt.h" #include "../tool/simmenu.h" #include "../utils/simstring.h" #include "../player/simplay.h" #include "../builder/vehikelbauer.h" #include "../dataobj/schedule.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../obj/way/kanal.h" #include "../obj/way/maglev.h" #include "../obj/way/monorail.h" #include "../obj/way/narrowgauge.h" #include "../obj/way/runway.h" #include "../obj/way/schiene.h" #include "../obj/way/strasse.h" #include "../utils/unicode.h" #include "minimap.h" #define MAX_SORT_IDX (5) static uint8 idx_to_sort_mode[MAX_SORT_IDX] = { line_scrollitem_t::SORT_BY_NAME, line_scrollitem_t::SORT_BY_PROFIT, line_scrollitem_t::SORT_BY_REVENUE, line_scrollitem_t::SORT_BY_TRANSPORTED, line_scrollitem_t::SORT_BY_CONVOIS }; static const char *idx_to_sort_text[MAX_SORT_IDX] = { "Name", "Profit", "Revenue", "Transported", "Number of convois" }; /// selected tab per player static uint8 selected_tab[MAX_PLAYER_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /// selected line per tab (static) linehandle_t schedule_list_gui_t::selected_line[MAX_PLAYER_COUNT][simline_t::MAX_LINE_TYPE]; schedule_list_gui_t::schedule_list_gui_t(player_t *player_) : gui_frame_t( translator::translate("Line Management"), player_), player(player_), scl(gui_scrolled_list_t::listskin, line_scrollitem_t::compare) { schedule_filter[0] = 0; old_schedule_filter[0] = 0; current_sort_mode = 0; line_scrollitem_t::sort_reverse = false; // add components // first column: scrolled list of all lines set_table_layout(1,0); add_table(4,0); { // below line list: line filter new_component("Filter:"); inp_filter.set_text( schedule_filter, lengthof( schedule_filter ) ); inp_filter.add_listener( this ); add_component( &inp_filter, 2 ); new_component(); // freight type filter new_component(); freight_type_c.set_force_selection(true); viewable_freight_types.append(NULL); freight_type_c.new_component(translator::translate("All"), SYSCOL_TEXT); viewable_freight_types.append(goods_manager_t::passengers); freight_type_c.new_component(translator::translate("Passagiere"), SYSCOL_TEXT); viewable_freight_types.append(goods_manager_t::mail); freight_type_c.new_component(translator::translate("Post"), SYSCOL_TEXT); viewable_freight_types.append(goods_manager_t::none); // for all freight ... freight_type_c.new_component(translator::translate("Fracht"), SYSCOL_TEXT); for (int i = 0; i < goods_manager_t::get_max_catg_index(); i++) { const goods_desc_t* freight_type = goods_manager_t::get_info_catg(i); const int index = freight_type->get_catg_index(); if (index == goods_manager_t::INDEX_NONE || freight_type->get_catg() == 0) { continue; } freight_type_c.new_component(translator::translate(freight_type->get_catg_name()), SYSCOL_TEXT); viewable_freight_types.append(freight_type); } for (int i = 0; i < goods_manager_t::get_count(); i++) { const goods_desc_t* ware = goods_manager_t::get_info(i); if (ware->get_catg() == 0 && ware->get_index() > 2) { // Special freight: Each good is special viewable_freight_types.append(ware); freight_type_c.new_component(translator::translate(ware->get_name()), SYSCOL_TEXT); } } freight_type_c.set_selection(0); freight_type_c.set_focusable(true); freight_type_c.add_listener(this); add_component(&freight_type_c, 2); new_component(); // sort by what new_component("hl_txt_sort"); sort_type_c.set_force_selection(true); for( int i=0; i( translator::translate(idx_to_sort_text[i]), SYSCOL_TEXT) ; } sort_type_c.set_selection(current_sort_mode); sort_type_c.set_focusable( true ); sort_type_c.add_listener( this ); add_component(&sort_type_c); sorteddir.init(button_t::sortarrow_state, NULL); sorteddir.add_listener(this); sorteddir.pressed = line_scrollitem_t::sort_reverse; add_component(&sorteddir); new_component(); } end_table(); add_table(3, 1); { // line control buttons bt_new_line.init(button_t::roundbox, "New Line"); bt_new_line.add_listener(this); add_component(&bt_new_line); bt_delete_line.init(button_t::roundbox, "Delete Line"); bt_delete_line.set_tooltip("Delete the selected line (if without associated convois)."); bt_delete_line.add_listener(this); add_component( &bt_delete_line ); bt_single_line.init( button_t::square_automatic | button_t::flexible, "Single GUI" ); bt_single_line.set_tooltip( "Closes topmost line window when new line selected." ); bt_single_line.add_listener( this ); bt_single_line.pressed = env_t::single_line_gui; add_component( &bt_single_line ); new_component(); } end_table(); // init scrolled list scl.add_listener(this); // tab panel tabs.init_tabs(&scl); tabs.add_listener(this); add_component(&tabs); // recover last selected line tabs.set_active_tab_waytype(simline_t::linetype_to_waytype((simline_t::linetype)selected_tab[player->get_player_nr()])); build_line_list((simline_t::linetype)selected_tab[player->get_player_nr()]); set_resizemode(diagonal_resize); reset_min_windowsize(); } bool schedule_list_gui_t::action_triggered( gui_action_creator_t *comp, value_t v ) { if( comp == &bt_new_line ) { // create typed line assert( tabs.get_active_tab_index() > 0 ); // update line schedule via tool! tool_t *tmp_tool = create_tool( TOOL_CHANGE_LINE | SIMPLE_TOOL ); cbuffer_t buf; int type = simline_t::waytype_to_linetype(tabs.get_active_tab_waytype()); buf.printf( "c,0,%i,0,0|%i|", type, type ); tmp_tool->set_default_param(buf); welt->set_tool( tmp_tool, player ); // since init always returns false, it is safe to delete immediately delete tmp_tool; depot_t::update_all_win(); } else if( comp == &bt_delete_line ) { vector_tpl sel = scl.get_selections(); for(sint32 i : sel) { linehandle_t line = ((line_scrollitem_t*)scl.get_element(i))->get_line(); if (line->count_convoys()==0) { // delete this line via tool tool_t* tmp_tool = create_tool(TOOL_CHANGE_LINE | SIMPLE_TOOL); cbuffer_t buf; buf.printf("d,%i", line.get_id()); tmp_tool->set_default_param(buf); welt->set_tool(tmp_tool, player); // since init always returns false, it is safe to delete immediately delete tmp_tool; } } depot_t::update_all_win(); } else if( comp == &bt_single_line ) { env_t::single_line_gui ^= 1; bt_single_line.pressed = env_t::single_line_gui; } else if( comp == &tabs ) { int const tab = tabs.get_active_tab_index(); uint8 old_selected_tab = selected_tab[player->get_player_nr()]; selected_tab[player->get_player_nr()] = simline_t::waytype_to_linetype(tabs.get_active_tab_waytype()); if( old_selected_tab == simline_t::line && selected_line[player->get_player_nr()][0].is_bound() && selected_line[player->get_player_nr()][0]->get_linetype() == selected_tab[player->get_player_nr()] ) { // switching from general to same waytype tab while line is seletced => use current line instead selected_line[player->get_player_nr()][selected_tab[player->get_player_nr()]] = selected_line[player->get_player_nr()][0]; } build_line_list((simline_t::linetype)selected_tab[player->get_player_nr()]); if( tab>0 ) { bt_new_line.enable(); } else { bt_new_line.disable(); } } else if( comp == &scl ) { if( line_scrollitem_t *li=(line_scrollitem_t *)scl.get_element(v.i) ) { line = li->get_line(); gui_frame_t *line_info = win_get_magic( (ptrdiff_t)line.get_rep() ); // try to open to the right scr_coord sc = win_get_pos( this ); scr_coord lc = sc; const scr_size screen = gfx->get_screen_size(); lc.x += get_windowsize().w; if( lc.x > screen.w ) { lc.x = max( 0, screen.w - 100 ); } if( line_info ) { // close if open destroy_win( line_info ); } else { if( bt_single_line.pressed && win_get_open_count()>=2 ) { // close second topmost line window for( int i = win_get_open_count()-1; i>=0; i-- ) { if( gui_frame_t* gui = win_get_index( i ) ) { if( gui->get_rdwr_id()==magic_line_schedule_rdwr_dummy ) { destroy_win( gui ); break; } } } } create_win( lc, new line_management_gui_t(line, player, -1), w_info, (ptrdiff_t)line.get_rep() ); } } scl.set_selection( -1 ); } else if( comp == &inp_filter ) { if( strcmp(old_schedule_filter,schedule_filter) ) { build_line_list(simline_t::waytype_to_linetype(tabs.get_active_tab_waytype())); strcpy(old_schedule_filter,schedule_filter); } } else if( comp == &freight_type_c ) { build_line_list(simline_t::waytype_to_linetype(tabs.get_active_tab_waytype())); } else if (comp == &sorteddir) { line_scrollitem_t::sort_reverse = !line_scrollitem_t::sort_reverse; sorteddir.pressed = line_scrollitem_t::sort_reverse; build_line_list(simline_t::waytype_to_linetype(tabs.get_active_tab_waytype())); } else if( comp == &sort_type_c ) { build_line_list(simline_t::waytype_to_linetype(tabs.get_active_tab_waytype())); } return true; } void schedule_list_gui_t::draw(scr_coord pos, scr_size size) { // deativate buttons, if not curretn player const bool activate = player == welt->get_active_player() || welt->get_active_player()==welt->get_player( 1 ); bt_new_line.enable( activate && tabs.get_active_tab_index() > 0); bt_delete_line.enable(activate); // if search string changed, update line selection if( old_line_count != player->simlinemgmt.get_line_count() || strcmp( old_schedule_filter, schedule_filter ) ) { old_line_count = player->simlinemgmt.get_line_count(); build_line_list(simline_t::waytype_to_linetype(tabs.get_active_tab_waytype())); strcpy( old_schedule_filter, schedule_filter ); } vector_tpl sel = scl.get_selections(); bool can_delete = sel.get_count() > 0 && activate; if (can_delete) { for(sint32 i : sel) { linehandle_t line = ((line_scrollitem_t*)scl.get_element(i))->get_line(); if (line->count_convoys() > 0) { can_delete = false; break; } } } bt_delete_line.enable(can_delete); gui_frame_t::draw(pos, size); } void schedule_list_gui_t::build_line_list(simline_t::linetype filter) { vector_tpllines; sint32 sel = -1; scl.clear_elements(); player->simlinemgmt.get_lines(filter, &lines); for(linehandle_t const l : lines) { // search name if( !*schedule_filter || utf8caseutf8(l->get_name(), schedule_filter) ) { // match good category if( is_matching_freight_catg( l->get_goods_catg_index() ) ) { scl.new_component(l, line_scrollitem_t::SELECT_WIN); } if( line == l ) { sel = scl.get_count() - 1; } } } scl.set_selection( sel ); current_sort_mode = idx_to_sort_mode[ sort_type_c.get_selection() ]; line_scrollitem_t::sort_mode = (line_scrollitem_t::sort_modes_t)current_sort_mode; scl.sort( 0 ); scl.set_size(scl.get_size()); old_line_count = player->simlinemgmt.get_line_count(); } uint32 schedule_list_gui_t::get_rdwr_id() { return magic_line_management_t+player->get_player_nr(); } void schedule_list_gui_t::rdwr( loadsave_t *file ) { scr_size size; if( file->is_saving() ) { size = get_windowsize(); } size.rdwr( file ); tabs.rdwr( file ); simline_t::rdwr_linehandle_t(file, line); scl.rdwr(file); sort_type_c.rdwr(file); freight_type_c.rdwr(file); file->rdwr_str(schedule_filter, lengthof(schedule_filter)); file->rdwr_bool(line_scrollitem_t::sort_reverse); // open dialogue if( file->is_loading() ) { current_sort_mode = idx_to_sort_mode[sort_type_c.get_selection()]; line_scrollitem_t::sort_mode = (line_scrollitem_t::sort_modes_t)current_sort_mode; build_line_list(simline_t::waytype_to_linetype(tabs.get_active_tab_waytype())); set_windowsize(size); // that would be the proper way to restore windows, would keep the simwin side cleaner) ... // win_set_magic(magic_line_management_t + player->get_player_nr()) } } // borrowed code from minimap bool schedule_list_gui_t::is_matching_freight_catg(const minivec_tpl &goods_catg_index) { const goods_desc_t *line_freight_type_group_index = viewable_freight_types[freight_type_c.get_selection()]; // does this line/convoi has a matching freight if( line_freight_type_group_index == goods_manager_t::passengers ) { return goods_catg_index.is_contained(goods_manager_t::INDEX_PAS); } else if( line_freight_type_group_index == goods_manager_t::mail ) { return goods_catg_index.is_contained(goods_manager_t::INDEX_MAIL); } else if( line_freight_type_group_index == goods_manager_t::none ) { // all freights but not pax or mail for( uint8 i = 0; i < goods_catg_index.get_count(); i++ ) { if( goods_catg_index[i] > goods_manager_t::INDEX_NONE ) { return true; } } return false; } else if( line_freight_type_group_index != NULL ) { for( uint8 i = 0; i < goods_catg_index.get_count(); i++ ) { if( goods_catg_index[i] == line_freight_type_group_index->get_catg_index() ) { return true; } } return false; } // all true return true; } simutrans-124.5/src/simutrans/gui/schedule_list.h000066400000000000000000000034011520675460500221620ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SCHEDULE_LIST_H #define GUI_SCHEDULE_LIST_H #include "gui_frame.h" #include "components/gui_combobox.h" #include "components/gui_textinput.h" #include "components/gui_scrolled_list.h" #include "components/gui_convoiinfo.h" #include "components/gui_waytype_tab_panel.h" #include "../tpl/vector_tpl.h" #include "../tpl/minivec_tpl.h" #include "../simline.h" #include "../descriptor/goods_desc.h" class player_t; /** * Window displaying information about all schedules and lines. */ class schedule_list_gui_t : public gui_frame_t, public action_listener_t { private: player_t *player; button_t bt_new_line, bt_delete_line, bt_single_line; gui_scrolled_list_t scl; gui_textinput_t inp_filter; button_t filterButtons[MAX_LINE_COST]; gui_waytype_tab_panel_t tabs; gui_combobox_t freight_type_c, sort_type_c; button_t sorteddir; uint32 old_line_count; // to filter for line names char schedule_filter[256], old_schedule_filter[256]; vector_tpl viewable_freight_types; bool is_matching_freight_catg( const minivec_tpl &goods_catg_index ); uint8 current_sort_mode; void build_line_list(simline_t::linetype filter); // last active line linehandle_t line; public: /// last selected line per tab static linehandle_t selected_line[MAX_PLAYER_COUNT][simline_t::MAX_LINE_TYPE]; schedule_list_gui_t(player_t* player_); const char* get_help_filename() const OVERRIDE { return "linemanagement.txt"; } void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; // following: rdwr stuff void rdwr( loadsave_t *file ) OVERRIDE; uint32 get_rdwr_id() OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/script_generator_frame.cc000066400000000000000000000024111520675460500242150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "script_generator_frame.h" #include "../tool/simtool.h" #include "../sys/simsys.h" script_generator_frame_t::script_generator_frame_t(tool_generate_script_t* tl, const char *_p, cbuffer_t &cmd, koord a) : savegame_frame_t("", false, _p, true) { this->tool = tl; command = cmd; area = a; set_name(translator::translate("Save generated script")); } /** * Action, started after button pressing. */ bool script_generator_frame_t::item_action(const char *fullpath) { tool->save_script(fullpath,command,area); return true; } bool script_generator_frame_t::ok_action(const char *fullpath) { tool->save_script(fullpath,command,area); return true; } bool script_generator_frame_t::del_action(const char* fullpath) { dr_chdir(fullpath); dr_remove("description.tab"); dr_remove("tool.nut"); dr_remove("script-exec-0.log"); dr_chdir(".."); const char* p = strrchr(fullpath, *PATH_SEPARATOR); if (!p) { p = strrchr(fullpath, '/'); } if (p) { return dr_remove(p + 1); } return false; } const char *script_generator_frame_t::get_info(const char *) { return ""; } bool script_generator_frame_t::check_file( const char *f, const char * ) { return *f; } simutrans-124.5/src/simutrans/gui/script_generator_frame.h000066400000000000000000000021651520675460500240650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SCRIPT_GENERATOR_INFO_H #define GUI_SCRIPT_GENERATOR_INFO_H #include "savegame_frame.h" #include "../utils/cbuffer.h" #include "../tool/simtool.h" class script_generator_frame_t : public savegame_frame_t { private: tool_generate_script_t* tool; cbuffer_t command; koord area; protected: /** * Action that's started by the press of a button. */ bool item_action(const char *fullpath) OVERRIDE; bool ok_action(const char* fullpath) OVERRIDE; bool del_action(const char* fullpath) OVERRIDE; /** * Returns extra file info: title of tool from description.tab */ const char *get_info(const char *path) OVERRIDE; // true, if valid bool check_file( const char *filename, const char *suffix ) OVERRIDE; public: script_generator_frame_t(tool_generate_script_t*, const char *path, cbuffer_t &cmd, koord area); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE { return "script_generator.txt"; } }; #endif simutrans-124.5/src/simutrans/gui/script_tool_frame.cc000066400000000000000000000047321520675460500232140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "script_tool_frame.h" #include "../script/script_tool_manager.h" #include "../dataobj/environment.h" #include "../dataobj/tabfile.h" #include "../dataobj/translator.h" #include "../simdebug.h" #include "../world/simworld.h" #include "../tool/simtool-scripted.h" #include "../sys/simsys.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" script_tool_frame_t::~script_tool_frame_t() { clear_ptr_vector(infos); } script_tool_frame_t::script_tool_frame_t() : savegame_frame_t(NULL, true, NULL, false) { static cbuffer_t pakset_script_tool; static cbuffer_t addons_script_tool; static cbuffer_t user_addons_script_tool; pakset_script_tool.clear(); pakset_script_tool.printf("%stool%s", env_t::pak_dir.c_str(), PATH_SEPARATOR); addons_script_tool.clear(); addons_script_tool.printf("%saddons%s%stool%s", env_t::install_dir, PATH_SEPARATOR, env_t::pak_name.c_str(), PATH_SEPARATOR); user_addons_script_tool.clear(); user_addons_script_tool.printf("%saddons%s%stool%s", env_t::user_dir, PATH_SEPARATOR, env_t::pak_name.c_str(), PATH_SEPARATOR); if (env_t::default_settings.get_with_private_paks()) { this->add_path(addons_script_tool); } this->add_path(user_addons_script_tool); // always add user addons this->add_path(pakset_script_tool); set_name(translator::translate("Load script tool")); set_focus(NULL); } /** * Action, started after button pressing. */ bool script_tool_frame_t::item_action(const char *fullpath) { const scripted_tool_info_t* info = script_tool_manager_t::get_script_info(fullpath); bool is_one_click = info->is_one_click; delete info; tool_t* tool = script_tool_manager_t::load_tool(fullpath, tool_t::general_tool[is_one_click ? TOOL_EXEC_SCRIPT : TOOL_EXEC_TWO_CLICK_SCRIPT]); assert(tool); const char* p = strrchr(fullpath, *PATH_SEPARATOR); static plainstring last_default_param; last_default_param = p ? p + 1 : fullpath; tool->set_default_param(last_default_param); welt->set_tool(tool, welt->get_active_player()); return true; } // calls tool manager to read description.tab const char *script_tool_frame_t::get_info(const char *path) { const scripted_tool_info_t* info = script_tool_manager_t::get_script_info(path); infos.append(info); return info->title.c_str(); } bool script_tool_frame_t::check_file( const char *filename, const char * ) { return script_tool_manager_t::check_file(filename); } simutrans-124.5/src/simutrans/gui/script_tool_frame.h000066400000000000000000000017411520675460500230530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SCRIPT_TOOL_FRAME_H #define GUI_SCRIPT_TOOL_FRAME_H #include "savegame_frame.h" #include "../tpl/vector_tpl.h" struct scripted_tool_info_t; class script_tool_frame_t : public savegame_frame_t { private: // pointers to info structures vector_tpl infos; protected: /** * Action that's started by the press of a button. */ bool item_action(const char *fullpath) OVERRIDE; /** * Returns extra file info: title of tool from description.tab */ const char *get_info(const char *path) OVERRIDE; // true, if valid bool check_file( const char *filename, const char *suffix ) OVERRIDE; public: script_tool_frame_t(); ~script_tool_frame_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE { return "script_tool.txt"; } }; #endif simutrans-124.5/src/simutrans/gui/server_frame.cc000066400000000000000000000416541520675460500221650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "server_frame.h" #include "simwin.h" #include "messagebox.h" #include "chat_frame.h" #include "help_frame.h" #include "components/gui_divider.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../display/simgraph.h" #include "../network/network.h" #include "../network/network_cmd_ingame.h" #include "../network/network_cmp_pakset.h" #include "../network/network_file_transfer.h" #include "../network/pakset_info.h" #include "../player/simplay.h" #include "../simcolor.h" #include "../simversion.h" #include "../tool/simtool.h" #include "../utils/cbuffer.h" #include "../utils/csv.h" #include "../utils/simstring.h" #include "../world/simworld.h" static char nick_buf[256]; char server_frame_t::newserver_name[2048] = ""; class gui_minimap_t : public gui_component_t { gameinfo_t *gi; public: gui_minimap_t() { gi = NULL; } void set_gameinfo(gameinfo_t *gi) { this->gi = gi; } void draw( scr_coord offset ) OVERRIDE { if (gi) { scr_coord p = get_pos() + offset; scr_size mapsize( gi->get_map()->get_width(), gi->get_map()->get_height() ); // 3D border around the map graphic gfx->draw_box3d_clipped(p.x, p.y, mapsize.w + 2, mapsize.h + 2, gfx->palette_lookup(MN_GREY0), gfx->palette_lookup(MN_GREY4) ); gfx->draw_array( p.x + 1, p.y + 1, mapsize.w, mapsize.h, gi->get_map()->to_array() ); } } scr_size get_min_size() const OVERRIDE { return gi ? scr_size(gi->get_map()->get_width()+2, gi->get_map()->get_height()+2) : scr_size(0,0); } scr_size get_max_size() const OVERRIDE { return get_min_size(); } }; server_frame_t::server_frame_t() : gui_frame_t( translator::translate("Game info") ), gi(welt), custom_valid(false), serverlist( gui_scrolled_list_t::listskin, gui_scrolled_list_t::scrollitem_t::compare ), game_text(&buf) { map = new gui_minimap_t(); update_info(); map->set_gameinfo(&gi); set_table_layout(3,0); // When in network mode, display only local map info (and nickname changer) // When not in network mode, display server picker if ( !env_t::networkmode ) { new_component_span("Select a server to join:",2); join.init(button_t::roundbox | button_t::flexible, "join game"); join.disable(); join.add_listener(this); add_component(&join); // Server listing serverlist.set_selection( 0 ); add_component( &serverlist, 3 ); serverlist.add_listener(this); // Show mismatched checkbox show_mismatched.init( button_t::square_state | button_t::flexible, "Show mismatched"); show_mismatched.set_tooltip( "Show servers where game version or pakset does not match your client" ); show_mismatched.add_listener( this ); add_component( &show_mismatched ); // Show offline checkbox show_offline.init( button_t::square_state | button_t::flexible, "Show offline"); show_offline.set_tooltip( "Show servers that are offline" ); show_offline.add_listener( this ); add_component( &show_offline, 2 ); new_component_span(3); new_component_span("Or enter a server manually:", 2); add.init(button_t::roundbox | button_t::flexible, "Query server"); add.add_listener(this); add_component(&add); // Add server input/button addinput.set_text( newserver_name, sizeof( newserver_name ) ); addinput.add_listener( this ); add_component( &addinput, 3 ); new_component_span(3); } if(!env_t::networkmode) { add_component(&revision); find_mismatch.init(button_t::roundbox | button_t::flexible, "find mismatch"); show_offline.set_tooltip("Compile a report what pak files are wrong (can be slow)"); find_mismatch.add_listener(this); add_component(&find_mismatch,2); } else { add_component(&revision, 3); } show_mismatched.pressed = gi.get_game_engine_revision() == 0; add_component( &pak_version, 3 ); #if MSG_LEVEL>=4 add_component( &pakset_checksum, 3 ); #endif new_component_span(3); add_table(2, 1, 3); { add_component(map); add_table(3,2); { add_component(&label_size); new_component(); add_component(&date); add_component(&game_text); new_component(); show_motd.init(button_t::roundbox | button_t::flexible, "Server message"); show_motd.set_rigid(true); show_motd.set_visible(gi.motd.size() > 0); show_motd.add_listener(this); add_component(&show_motd, 2); } end_table(); } end_table(); new_component_span(3); add_table(2, 1, 3); { new_component("Nickname:" ); nick.add_listener(this); nick.set_text( nick_buf, lengthof( nick_buf ) ); tstrncpy( nick_buf, env_t::nickname.c_str(), min( lengthof( nick_buf ), env_t::nickname.length() + 1 ) ); add_component(&nick); } end_table(); if ( !env_t::networkmode ) { // only update serverlist, when not already in network mode // otherwise desync to current game may happen update_serverlist(); } set_resizemode( diagonal_resize ); reset_min_windowsize(); } void server_frame_t::update_error (const char* errortext) { buf.clear(); buf.printf("%s", errortext ); map->set_gameinfo(NULL); date.buf().clear(); label_size.buf().clear(); revision.buf().clear(); pak_version.set_text( "" ); join.disable(); find_mismatch.disable(); } PIXVAL server_frame_t::update_info() { map->set_gameinfo(&gi); // Compare with current world to determine status gameinfo_t current( welt ); bool engine_match = false; bool pakset_match = false; // Zero means do not know => assume match if( current.get_game_engine_revision() == 0 || gi.get_game_engine_revision() == 0 || current.get_game_engine_revision() == gi.get_game_engine_revision() ) { engine_match = true; } if( gi.get_pakset_checksum() == current.get_pakset_checksum() ) { pakset_match = true; find_mismatch.disable(); } else { find_mismatch.enable(); } // State of join button if( (serverlist.get_selection() >= 0 || custom_valid) && pakset_match && engine_match ) { join.enable(); } else { join.disable(); } label_size.buf().printf("%ux%u\n", gi.get_size_x(), gi.get_size_y()); label_size.update(); // Update all text fields char temp[32]; buf.clear(); if ( gi.get_clients() != 255 ) { uint8 player = 0, locked = 0; for ( uint8 i = 0; i < MAX_PLAYER_COUNT; i++ ) { if ( gi.get_player_type(i)&~player_t::PASSWORD_PROTECTED ) { player ++; if ( gi.get_player_type(i)&player_t::PASSWORD_PROTECTED ) { locked ++; } } } buf.printf( translator::translate("%u Player (%u locked)\n"), player, locked ); buf.printf( translator::translate("%u Client(s)\n"), (unsigned)gi.get_clients() ); } buf.printf( "%s %u\n", translator::translate("Towns"), gi.get_city_count() ); number_to_string( temp, gi.get_citizen_count(), 0 ); buf.printf( "%s %s\n", translator::translate("citicens"), temp ); buf.printf( "%s %u\n", translator::translate("Factories"), gi.get_industries() ); buf.printf( "%s %u\n", translator::translate("Convoys"), gi.get_convoi_count() ); buf.printf( "%s %u\n", translator::translate("Stops"), gi.get_halt_count() ); revision.buf().printf( "%s %u", translator::translate( "Revision:" ), gi.get_game_engine_revision() ); revision.set_color( engine_match ? SYSCOL_TEXT : MONEY_MINUS ); revision.update(); show_motd.set_visible(gi.motd.size() > 0); pak_version.set_text( gi.get_pak_name() ); pak_version.set_color( pakset_match ? SYSCOL_TEXT : SYSCOL_OBSOLETE ); #if MSG_LEVEL>=4 pakset_checksum.buf().printf("%s %s",translator::translate( "Pakset checksum:" ), gi.get_pakset_checksum().get_str(8)); pakset_checksum.update(); pakset_checksum.set_color( pakset_match ? SYSCOL_TEXT : SYSCOL_TEXT_STRONG ); #endif date.buf().printf("%s", translator::get_date(gi.get_current_year(), gi.get_current_month())); date.update(); set_dirty(); resize(scr_size(0,0)); return pakset_match && engine_match ? SYSCOL_TEXT : SYSCOL_OBSOLETE; } bool server_frame_t::update_serverlist () { // Based on current dialog settings, should we show mismatched servers or not uint revision = 0; const char* pakset = NULL; gameinfo_t current( welt ); if( !show_mismatched.pressed ) { revision = current.get_game_engine_revision(); pakset = current.get_pak_name(); } // Download game listing from listings server into memory cbuffer_t buf; const char *err = NULL; if ((err = network_http_get(ANNOUNCE_SERVER1, ANNOUNCE_LIST_URL, buf))) { dbg->warning("server_frame_t::update_serverlist", "Could not download server list from %s: %s", ANNOUNCE_SERVER1, err); #ifdef ANNOUNCE_SERVER2 if ((err = network_http_get(ANNOUNCE_SERVER2, ANNOUNCE_LIST_URL, buf))) { dbg->warning("server_frame_t::update_serverlist", "Could not download server list from %s: %s", ANNOUNCE_SERVER2, err); #ifdef ANNOUNCE_SERVER3 if ((err = network_http_get(ANNOUNCE_SERVER3, ANNOUNCE_LIST_URL, buf))) { dbg->error("server_frame_t::update_serverlist", "Could not download server list from %s: %s", ANNOUNCE_SERVER3, err); return false; } #else return false; #endif } #else return false; #endif } // Parse listing into CSV_t object CSV_t csvdata( buf.get_str() ); int ret; dbg->message( "server_frame_t::update_serverlist", "CSV_t: %s", csvdata.get_str() ); // For each listing entry, determine if it matches the version supplied to this function do { // First field is display name of server cbuffer_t servername; ret = csvdata.get_next_field( servername ); dbg->message( "server_frame_t::update_serverlist", "servername: %s", servername.get_str() ); // Skip invalid lines if( ret <= 0 ) { continue; } // Second field is dns name of server cbuffer_t serverdns; ret = csvdata.get_next_field( serverdns ); dbg->message( "server_frame_t::update_serverlist", "serverdns: %s", serverdns.get_str() ); if( ret <= 0 ) { continue; } cbuffer_t serverdns2; // Strip default port if( strcmp(serverdns.get_str() + serverdns.len() - 6, ":13353") == 0 ) { dbg->message( "server_frame_t::update_serverlist", "stripping default port from entry %s", serverdns.get_str() ); serverdns2.append( serverdns.get_str(), strlen( serverdns.get_str() ) - 6 ); serverdns = serverdns2; serverdns2.clear(); } // Third field is server revision (use for filtering) cbuffer_t serverrevision; ret = csvdata.get_next_field( serverrevision ); dbg->message( "server_frame_t::update_serverlist", "serverrevision: %s", serverrevision.get_str() ); if( ret <= 0 ) { continue; } uint32 serverrev = atol( serverrevision.get_str() ); if( revision != 0 && revision != serverrev ) { // do not add mismatched servers dbg->warning( "server_frame_t::update_serverlist", "revision %i does not match our revision (%i), skipping", serverrev, revision ); continue; } // Fourth field is server pakset (use for filtering) cbuffer_t serverpakset; ret = csvdata.get_next_field( serverpakset ); dbg->message( "server_frame_t::update_serverlist", "serverpakset: %s", serverpakset.get_str() ); if( ret <= 0 ) { continue; } // now check pakset match if ( pakset != NULL ) { if (!strstart(serverpakset.get_str(), pakset)) { dbg->warning( "server_frame_t::update_serverlist", "pakset '%s' does not match our pakset ('%s'), skipping", serverpakset.get_str(), pakset ); continue; } } // Fifth field is server online/offline status (use for colour-coding) cbuffer_t serverstatus; ret = csvdata.get_next_field( serverstatus ); dbg->message( "server_frame_t::update_serverlist", "serverstatus: %s", serverstatus.get_str() ); uint32 status = 0; if( ret > 0 ) { status = atol( serverstatus.get_str() ); // sixth field on new list the alt dns, if there is one ret = csvdata.get_next_field(serverdns2); dbg->message("server_frame_t::update_serverlist", "altdns: %s", serverdns2.get_str()); } // Only show offline servers if the checkbox is set if( status == 1 || show_offline.pressed ) { PIXVAL color = status == 1 ? SYSCOL_EMPTY : MONEY_MINUS; if( pakset && !strstart( serverpakset.get_str(), pakset ) ) { color = SYSCOL_OBSOLETE; } serverlist.new_component( servername, serverdns, serverdns2, status, color ); dbg->message( "server_frame_t::update_serverlist", "Appended %s (%s) to list", servername.get_str(), serverdns.get_str() ); } } while( csvdata.next_line() ); // Set no default selection serverlist.set_selection( -1 ); serverlist.set_size(serverlist.get_size()); set_dirty(); resize(scr_size(0, 0)); return true; } bool server_frame_t::infowin_event (const event_t *ev) { return gui_frame_t::infowin_event( ev ); } bool server_frame_t::action_triggered (gui_action_creator_t *comp, value_t p) { // Selection has changed if( &serverlist == comp ) { if( p.i <= -1 ) { join.disable(); gi = gameinfo_t(welt); update_info(); } else { join.disable(); server_scrollitem_t *item = (server_scrollitem_t*)serverlist.get_element( p.i ); if( item->online() ) { gfx->set_show_load_cursor(true); const char *err = network_gameinfo( ((server_scrollitem_t*)serverlist.get_element( p.i ))->get_dns(), &gi ); if( err && *((server_scrollitem_t*)serverlist.get_element(p.i))->get_altdns() ) { item->set_color(MONEY_MINUS); update_error(err); err = network_gameinfo(((server_scrollitem_t*)serverlist.get_element(p.i))->get_altdns(), &gi); } if( err == NULL ) { item->set_color( update_info() ); } else { item->set_color( MONEY_MINUS ); update_error( err ); } gfx->set_show_load_cursor(false); } else { item->set_color( MONEY_MINUS ); update_error( "Cannot connect to offline server!" ); } } } else if ( &add == comp || &addinput ==comp ) { if ( newserver_name[0] != '\0' ) { join.disable(); dbg->warning("action_triggered()", "newserver_name: %s", newserver_name); gfx->set_show_load_cursor(true); const char *err = network_gameinfo( newserver_name, &gi ); if ( err == NULL ) { custom_valid = true; update_info(); } else { custom_valid = false; join.disable(); update_error( "Server did not respond!" ); } gfx->set_show_load_cursor(false); serverlist.set_selection( -1 ); } } else if ( &show_mismatched == comp ) { show_mismatched.pressed ^= 1; serverlist.clear_elements(); update_serverlist(); } else if ( &show_offline == comp ) { show_offline.pressed ^= 1; serverlist.clear_elements(); update_serverlist(); } else if ( &nick == comp ) { char* nickname = nick.get_text(); if ( env_t::networkmode ) { // Only try and change the nick with server if we're in network mode if ( env_t::nickname != nickname ) { env_t::nickname = nickname; nwc_nick_t* nwc = new nwc_nick_t( nickname ); network_send_server( nwc ); } } else { world()->get_chat_message()->rename_client(env_t::nickname.c_str(), nickname); env_t::nickname = nickname; } } else if ( &join == comp ) { char* nickname = nick.get_text(); if ( strlen( nickname ) == 0 ) { // forbid joining? } env_t::nickname = nickname; const char *server = NULL; // Prefer serverlist entry if one is selected if ( serverlist.get_selection() >= 0 ) { server = ((server_scrollitem_t*)serverlist.get_selected_item())->get_dns(); } // If we have a valid custom server entry, connect to that else if ( custom_valid ) { server = newserver_name; } else { dbg->error( "server_frame_t::action_triggered()", "join pressed without valid selection or custom server entry" ); join.disable(); } if ( server ) { cbuffer_t param; param.printf("l0net:%s", server); tool_t::simple_tool[TOOL_WORK_WORLD]->set_default_param(param); welt->set_tool(tool_t::simple_tool[TOOL_WORK_WORLD], NULL); } } else if ( &find_mismatch == comp ) { if ( gui_frame_t *info = win_get_magic(magic_pakset_info_t) ) { top_win( info ); } else { std::string msg; if ( serverlist.get_selection() >= 0 ) { network_compare_pakset_with_server( ((server_scrollitem_t*)serverlist.get_selected_item())->get_dns(), msg ); } else if ( custom_valid ) { network_compare_pakset_with_server( newserver_name, msg ); } else { dbg->error( "server_frame_t::action_triggered()", "find_mismatch pressed without valid selection or custom server entry" ); find_mismatch.disable(); } if ( !msg.empty() ) { help_frame_t *win = new help_frame_t(); win->set_text( msg.c_str() ); create_win( win, w_info, magic_pakset_info_t ); } } } else if (&show_motd == comp) { help_frame_t* win = new help_frame_t(); win->set_text(gi.motd.c_str()); create_win(win, w_info, magic_motd); } return true; } void server_frame_t::draw (scr_coord pos, scr_size size) { // update nickname if necessary if ( get_focus() != &nick && env_t::nickname != nick_buf ) { open_error_msg_win(translator::translate("Cannot change name: Name already in use")); tstrncpy( nick_buf, env_t::nickname.c_str(), min( lengthof( nick_buf ), env_t::nickname.length() + 1 ) ); } gui_frame_t::draw( pos, size ); } simutrans-124.5/src/simutrans/gui/server_frame.h000066400000000000000000000063631520675460500220250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SERVER_FRAME_H #define GUI_SERVER_FRAME_H #include "gui_frame.h" #include "components/gui_button.h" #include "components/gui_combobox.h" #include "components/gui_label.h" #include "components/action_listener.h" #include "components/gui_textarea.h" #include "components/gui_textinput.h" #include "../dataobj/gameinfo.h" #include "../utils/cbuffer.h" class gui_minimap_t; /** * When connected to a network server, this dialog shows game information * When not connected, it provides the mechanism for joining a remote game */ class server_frame_t : public gui_frame_t, private action_listener_t { private: gameinfo_t gi; cbuffer_t buf; bool custom_valid; // Custom server entry is valid gui_textinput_t addinput, nick; static char newserver_name[2048]; gui_scrolled_list_t serverlist; button_t add, join, find_mismatch, show_motd; button_t show_mismatched, show_offline; gui_label_t pak_version; gui_label_buf_t revision, date, label_size; #if MSG_LEVEL>=4 gui_label_buf_t pakset_checksum; #endif gui_minimap_t *map; gui_textarea_t game_text; /** * Server items to add to the listing panel * Stores dnsname (for connection) and servername (for display in list) * Also stores online/offline status of the server for filtering * Adds get_dns() method to retrieve dnsname */ class server_scrollitem_t : public gui_scrolled_list_t::const_text_scrollitem_t { private: cbuffer_t servername; cbuffer_t serverdns; cbuffer_t server_altdns; bool status; public: server_scrollitem_t (const cbuffer_t& name, const cbuffer_t& dns, const cbuffer_t& altdns, bool status, PIXVAL col) : gui_scrolled_list_t::const_text_scrollitem_t( NULL, col ), servername( name ), serverdns( dns ), server_altdns(altdns), status( status ) { servername.append( " (" ); servername.append( serverdns.get_str() ); if ( server_altdns.len() > 0 ) { servername.append(" or "); servername.append(server_altdns.get_str()); } servername.append(")"); } char const* get_text () const OVERRIDE { return servername.get_str(); } char const* get_dns() const { return serverdns.get_str(); } char const* get_altdns() const { return server_altdns.get_str(); } void set_text (char const* newtext) OVERRIDE { servername.clear(); servername.append( newtext ); serverdns.clear(); serverdns.append( newtext ); } bool online () { return status; } }; /** * Update UI fields with data from the current state of gameinfo_t gi */ PIXVAL update_info(); /** * Update UI fields to show connection errors */ void update_error ( const char* ); /** * Update server listing (retrieve from listings server) * Display depends on the state of the show_mismatched and * show_offline checkboxes */ bool update_serverlist (); public: server_frame_t(); void draw(scr_coord pos, scr_size size) OVERRIDE; /** * Return name of file which contains associated help text for this window * @return Help file name, nor NULL if no help file exists */ const char *get_help_filename() const OVERRIDE {return "server.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; bool infowin_event(event_t const*) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/settings_frame.cc000066400000000000000000000132371520675460500225130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simcity.h" #include "../sys/simsys.h" #include "simwin.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../dataobj/loadsave.h" #include "../dataobj/tabfile.h" #include "settings_frame.h" #include "components/gui_label.h" #include "../world/simworld.h" #include "components/action_listener.h" using std::string; settings_frame_t::settings_frame_t(settings_t* const s) : gui_frame_t( translator::translate("Setting") ), sets(s), scrolly_general(&general, true, true), scrolly_display(&display, true, true), scrolly_economy(&economy, true, true), scrolly_routing(&routing, true, true), scrolly_costs(&costs, true, true), scrolly_climates(&climates, true, true) { set_table_layout(1,0); add_table(0,1); { new_component("Load settings from"); revert_to_simuconf.init( button_t::roundbox | button_t::flexible, "Simuconf.tab"); revert_to_simuconf.add_listener( this ); add_component( &revert_to_simuconf ); revert_to_default_sve.init( button_t::roundbox | button_t::flexible, "Default.sve"); revert_to_default_sve.add_listener( this ); add_component( &revert_to_default_sve); if (world()->type_of_generation != karte_t::AUTO_GENERATED && sets != &welt->get_settings()) { revert_to_last_save.init(button_t::roundbox | button_t::flexible, "Current game"); revert_to_last_save.add_listener(this); add_component(&revert_to_last_save); } } end_table(); general.init( sets ); display.init( sets ); economy.init( sets ); routing.init( sets ); costs.init( sets ); climates.init( sets ); scrolly_general.set_scroll_amount_y(D_BUTTON_HEIGHT/2); scrolly_economy.set_scroll_amount_y(D_BUTTON_HEIGHT/2); scrolly_routing.set_scroll_amount_y(D_BUTTON_HEIGHT/2); scrolly_costs.set_scroll_amount_y(D_BUTTON_HEIGHT/2); scrolly_climates.set_scroll_amount_y(D_BUTTON_HEIGHT/2); tabs.add_tab(&scrolly_general, translator::translate("General")); tabs.add_tab(&scrolly_display, translator::translate("Display settings")); tabs.add_tab(&scrolly_economy, translator::translate("Economy")); tabs.add_tab(&scrolly_routing, translator::translate("Routing")); tabs.add_tab(&scrolly_costs, translator::translate("Costs")); tabs.add_tab(&scrolly_climates, translator::translate("Climate Control")); add_component(&tabs); reset_min_windowsize(); set_windowsize(get_min_windowsize() + general.get_min_size()); set_resizemode(diagonal_resize); } /* triggered, when button clicked; only single button registered, so the action is clear ... */ bool settings_frame_t::action_triggered( gui_action_creator_t *comp, value_t ) { // some things must stay the same when loading defaults const sint32 old_size_x = sets->size_x; const sint32 old_size_y = sets->size_y; const sint32 old_number = sets->map_number; const uint8 old_rot = sets->rotation; const uint32 old_city_count = sets->city_count; std::string old_save = sets->get_filename(); uint8 old_player_type[MAX_PLAYER_COUNT]; memcpy(old_player_type, sets->player_type, sizeof(old_player_type)); // now we can change values if( comp==&revert_to_simuconf ) { // reread from simucon.tab(s) the settings and apply them tabfile_t simuconf; env_t::init(); *sets = settings_t(); dr_chdir( env_t::base_dir ); if(simuconf.open("config/simuconf.tab")) { sets->parse_simuconf( simuconf ); sets->parse_colours( simuconf ); } stadt_t::cityrules_init(); dr_chdir( env_t::pak_dir.c_str() ); if(simuconf.open("config/simuconf.tab")) { sets->parse_simuconf( simuconf ); sets->parse_colours( simuconf ); } dr_chdir(env_t::user_dir); if (simuconf.open(string("addons/")+env_t::pak_name + "config/simuconf.tab")) { sets->parse_simuconf(simuconf); sets->parse_colours(simuconf); } if(simuconf.open("simuconf.tab")) { sets->parse_simuconf( simuconf ); sets->parse_colours( simuconf ); } simuconf.close(); // and update ... general.init( sets ); display.init( sets ); economy.init( sets ); routing.init( sets ); costs.init( sets ); climates.init( sets ); set_windowsize(get_windowsize()); } else if( comp==&revert_to_default_sve) { // load settings of last generated map dr_chdir( env_t::user_dir ); loadsave_t file; if( file.rd_open("default.sve") == loadsave_t::FILE_STATUS_OK ) { sets->rdwr(&file); sets->reset_after_global_settings_reload(); file.close(); } // and update ... general.init( sets ); display.init( sets ); economy.init( sets ); routing.init( sets ); costs.init( sets ); climates.init( sets ); set_windowsize(get_windowsize()); } else if (comp == &revert_to_last_save) { // setting of the current map *sets = env_t::default_settings; // and update ... general.init(sets); display.init(sets); economy.init(sets); routing.init(sets); costs.init(sets); climates.init(sets); set_windowsize(get_windowsize()); } // restore essential values sets->set_size(old_size_x, old_size_y); sets->map_number = old_number; sets->rotation = old_rot; sets->city_count = old_city_count; sets->set_filename(old_save.c_str()); memcpy(sets->player_type, old_player_type, sizeof(old_player_type)); return true; } bool settings_frame_t::infowin_event(const event_t *ev) { if( ev->ev_class == INFOWIN && ev->ev_code == WIN_CLOSE ) { general.read( sets ); display.read( sets ); routing.read( sets ); economy.read( sets ); costs.read( sets ); climates.read( sets ); // only the rgb colours have been changed, the colours in system format must be updated gfx->env_t_rgb_to_system_colors(); } return gui_frame_t::infowin_event(ev); } simutrans-124.5/src/simutrans/gui/settings_frame.h000066400000000000000000000030151520675460500223460ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SETTINGS_FRAME_H #define GUI_SETTINGS_FRAME_H #include "gui_frame.h" #include "components/gui_tab_panel.h" #include "components/gui_button.h" #include "components/gui_scrollpane.h" #include "settings_stats.h" #include "components/action_listener.h" class settings_t; /** * All messages since the start of the program */ class settings_frame_t : public gui_frame_t, action_listener_t { private: settings_t* sets; gui_tab_panel_t tabs; settings_general_stats_t general; gui_scrollpane_t scrolly_general; settings_display_stats_t display; gui_scrollpane_t scrolly_display; settings_economy_stats_t economy; gui_scrollpane_t scrolly_economy; settings_routing_stats_t routing; gui_scrollpane_t scrolly_routing; settings_costs_stats_t costs; gui_scrollpane_t scrolly_costs; settings_climates_stats_t climates; gui_scrollpane_t scrolly_climates; button_t revert_to_simuconf, revert_to_default_sve, revert_to_last_save; public: settings_frame_t(settings_t*); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "settings.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; // does not work during new world dialogue bool has_sticky() const OVERRIDE { return false; } bool infowin_event(event_t const*) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/settings_stats.cc000066400000000000000000000746361520675460500225710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "welt.h" #include "simwin.h" #include "../simversion.h" #include "../dataobj/settings.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../player/finance.h" // MAX_PLAYER_HISTORY_YEARS #include "../vehicle/vehicle_base.h" #include "settings_stats.h" #include "components/gui_divider.h" /* stuff not set here .... INIT_NUM( "intercity_road_length", env_t::intercity_road_length); INIT_NUM( "diagonal_multiplier", pak_diagonal_multiplier); */ static char const* const version[] = { "0.99.17", "0.100.0", "0.101.0", "0.102.1", "0.102.2", "0.102.5", "0.110.0", "0.110.1", "0.111.0", "0.111.1", "0.111.2", "0.111.3", "0.111.4", "0.112.0", "0.112.2", "0.120.1", "0.120.2", "0.120.3", "0.120.4", "0.120.5", "122.0" }; bool settings_general_stats_t::action_triggered(gui_action_creator_t *comp, value_t v) { assert( comp==&savegame ); (void)comp; if( v.i==-1 ) { savegame.set_selection( 0 ); } return true; } /* Nearly automatic lists with controls: * BEWARE: The init exit pair MUST match in the same order or else!!! */ void settings_general_stats_t::init(settings_t const* const sets) { INIT_INIT // combobox for savegame version savegame.clear_elements(); for( uint32 i=0; i( version[i]+2, SYSCOL_TEXT ) ; if( strcmp(version[i],env_t::savegame_version_str)==0 ) { savegame.set_selection( i ); } } savegame.set_focusable( false ); add_component( &savegame ); savegame.add_listener( this ); INIT_LB( "savegame version" ); SEPERATOR INIT_BOOL( "drive_left", sets->is_drive_left() ); INIT_BOOL( "signals_on_left", sets->is_signals_left() ); SEPERATOR INIT_NUM( "autosave", env_t::autosave, 0, 12, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "fast_forward", env_t::max_acceleration, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_BOOL( "numbered_stations", sets->get_numbered_stations() ); INIT_NUM( "show_names", env_t::show_names, 0, 3, gui_numberinput_t::AUTOLINEAR, true ); SEPERATOR INIT_NUM( "bits_per_month", sets->get_bits_per_month(), 16, 24, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "use_timeline", sets->get_use_timeline(), 0, 3, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM_NEW( "starting_year", sets->get_starting_year(), 0, 2999, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM_NEW( "starting_month", sets->get_starting_month(), 0, 11, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "show_month", env_t::show_month, 0, 7, gui_numberinput_t::AUTOLINEAR, true ); SEPERATOR INIT_NUM( "random_grounds_probability", env_t::ground_object_probability, 0, 0x7FFFFFFFul, gui_numberinput_t::POWER2, false ); INIT_NUM( "random_wildlife_probability", env_t::moving_object_probability, 0, 0x7FFFFFFFul, gui_numberinput_t::POWER2, false ); SEPERATOR INIT_BOOL( "pedes_and_car_info", env_t::road_user_info ); INIT_BOOL( "tree_info", env_t::tree_info ); INIT_BOOL( "ground_info", env_t::ground_info ); INIT_BOOL( "townhall_info", env_t::townhall_info ); INIT_BOOL( "only_single_info", env_t::single_info ); SEPERATOR INIT_NUM( "compass_map_position", env_t::compass_map_position, 0, 16, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "compass_screen_position", env_t::compass_screen_position, 0, 16, gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_NUM( "world_maximum_height", sets->get_maximumheight(), 16, 127, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "world_minimum_height", sets->get_minimumheight(), -127, -12, gui_numberinput_t::AUTOLINEAR, false ); INIT_END clear_dirty(); } void settings_general_stats_t::read(settings_t* const sets) { READ_INIT int selected = savegame.get_selection(); if( 0 <= selected && (uint32)selected < lengthof(version) ) { env_t::savegame_version_str = version[ selected ]; } READ_BOOL_VALUE( sets->drive_on_left ); vehicle_base_t::set_overtaking_offsets( sets->drive_on_left ); READ_BOOL_VALUE( sets->signals_on_left ); READ_NUM_VALUE( env_t::autosave ); READ_NUM_VALUE( env_t::max_acceleration ); READ_BOOL_VALUE( sets->numbered_stations ); READ_NUM_VALUE( env_t::show_names ); READ_NUM_VALUE( sets->bits_per_month ); READ_NUM_VALUE( sets->use_timeline ); READ_NUM_VALUE_NEW( sets->starting_year ); READ_NUM_VALUE_NEW( sets->starting_month ); READ_NUM_VALUE( env_t::show_month ); READ_NUM_VALUE( env_t::ground_object_probability ); READ_NUM_VALUE( env_t::moving_object_probability ); READ_BOOL_VALUE( env_t::road_user_info ); READ_BOOL_VALUE( env_t::tree_info ); READ_BOOL_VALUE( env_t::ground_info ); READ_BOOL_VALUE( env_t::townhall_info ); READ_BOOL_VALUE( env_t::single_info ); READ_NUM_VALUE( env_t::compass_map_position ); READ_NUM_VALUE( env_t::compass_screen_position ); READ_NUM_VALUE( sets->world_maximum_height ); READ_NUM_VALUE( sets->world_minimum_height ); } void settings_display_stats_t::init(settings_t const* const) { INIT_INIT INIT_NUM( "frames_per_second",env_t::fps, env_t::min_fps, env_t::max_fps, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "fast_forward_frames_per_second", env_t::ff_fps, env_t::min_fps, env_t::max_fps, gui_numberinput_t::AUTOLINEAR, false); INIT_NUM( "simple_drawing_tile_size",env_t::simple_drawing_default, 2, 256, gui_numberinput_t::POWER2, false ); INIT_BOOL( "simple_drawing_fast_forward",env_t::simple_drawing_fast_forward ); INIT_NUM( "water_animation_ms", env_t::water_animation, 0, 1000, 25, false ); INIT_NUM( "follow_convoi_underground", env_t::follow_convoi_underground, 0, 2, 1, true ); SEPERATOR INIT_BOOL( "window_buttons_right", env_t::window_buttons_right ); INIT_BOOL( "window_frame_active", env_t::window_frame_active ); INIT_COLOR( "default_window_title_color", env_t::default_window_title_color_rgb, gui_numberinput_t::AUTOLINEAR ); INIT_COLOR( "front_window_text_color", env_t::front_window_text_color_rgb, gui_numberinput_t::AUTOLINEAR ); INIT_COLOR( "bottom_window_text_color", env_t::bottom_window_text_color_rgb, gui_numberinput_t::AUTOLINEAR ); INIT_NUM( "bottom_window_darkness", env_t::bottom_window_darkness, 0, 100, gui_numberinput_t::AUTOLINEAR, 0 ); SEPERATOR INIT_BOOL( "show_tooltips", env_t::show_tooltips ); INIT_COLOR( "tooltip_background_color", env_t::tooltip_color_rgb, 1 ); INIT_COLOR( "tooltip_text_color", env_t::tooltip_textcolor_rgb, 1 ); INIT_NUM( "tooltip_delay", env_t::tooltip_delay, 0, 10000, gui_numberinput_t::AUTOLINEAR, 0 ); INIT_NUM( "tooltip_duration", env_t::tooltip_duration, 0, 30000, gui_numberinput_t::AUTOLINEAR, 0 ); SEPERATOR INIT_COLOR( "cursor_overlay_color", env_t::cursor_overlay_color_rgb, gui_numberinput_t::AUTOLINEAR ); INIT_BOOL( "left_to_right_graphs", env_t::left_to_right_graphs ); SEPERATOR INIT_BOOL( "player_finance_display_account", env_t::player_finance_display_account ); INIT_END } void settings_display_stats_t::read(settings_t* const) { READ_INIT // all visual stuff READ_NUM_VALUE( env_t::fps ); READ_NUM_VALUE( env_t::ff_fps ); READ_NUM_VALUE( env_t::simple_drawing_default ); READ_BOOL_VALUE( env_t::simple_drawing_fast_forward ); READ_NUM_VALUE( env_t::water_animation ); READ_NUM_VALUE( env_t::follow_convoi_underground ); READ_BOOL_VALUE( env_t::window_buttons_right ); READ_BOOL_VALUE( env_t::window_frame_active ); READ_COL_VALUE( env_t::default_window_title_color_rgb ); READ_COL_VALUE( env_t::front_window_text_color_rgb ); READ_COL_VALUE( env_t::bottom_window_text_color_rgb ); READ_NUM_VALUE( env_t::bottom_window_darkness ); READ_BOOL_VALUE( env_t::show_tooltips ); READ_COL_VALUE( env_t::tooltip_color_rgb ); READ_COL_VALUE( env_t::tooltip_textcolor_rgb ); READ_NUM_VALUE( env_t::tooltip_delay ); READ_NUM_VALUE( env_t::tooltip_duration ); READ_COL_VALUE( env_t::cursor_overlay_color_rgb ); READ_BOOL_VALUE( env_t::left_to_right_graphs ); READ_BOOL_VALUE( env_t::player_finance_display_account ); } void settings_routing_stats_t::init(settings_t const* const sets) { INIT_INIT INIT_BOOL( "separate_halt_capacities", sets->is_separate_halt_capacities() ); INIT_BOOL( "avoid_overcrowding", sets->is_avoid_overcrowding() ); INIT_BOOL( "no_routing_over_overcrowded", sets->is_no_routing_over_overcrowding() ); INIT_NUM( "station_coverage", sets->get_station_coverage(), 1, 8, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "allow_merge_distant_halt", sets->get_allow_merge_distant_halt(), 0, 0x7FFFFFFFul, gui_numberinput_t::POWER2, false ); SEPERATOR INIT_NUM( "max_route_steps", sets->get_max_route_steps(), 1, 0x7FFFFFFFul, gui_numberinput_t::POWER2, false ); INIT_NUM( "max_choose_route_steps", sets->get_max_choose_route_steps(), 1, 0x7FFFFFFFul, gui_numberinput_t::POWER2, false ); INIT_NUM( "max_hops", sets->get_max_hops(), 100, 65000, gui_numberinput_t::POWER2, false ); INIT_NUM( "max_transfers", sets->get_max_transfers(), 1, 100, gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_NUM( "way_straight", sets->way_count_straight, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "way_curve", sets->way_count_curve, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "way_double_curve", sets->way_count_double_curve, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "way_90_curve", sets->way_count_90_curve, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "way_slope", sets->way_count_slope, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "way_tunnel", sets->way_count_tunnel, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "way_max_bridge_len", sets->way_max_bridge_len, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "way_leaving_road", sets->way_count_leaving_way, 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_BOOL( "stop_halt_as_scheduled", sets->get_stop_halt_as_scheduled() ); INIT_END } void settings_routing_stats_t::read(settings_t* const sets) { READ_INIT // routing of goods READ_BOOL_VALUE( sets->separate_halt_capacities ); READ_BOOL_VALUE( sets->avoid_overcrowding ); READ_BOOL_VALUE( sets->no_routing_over_overcrowding ); READ_NUM_VALUE( sets->station_coverage_size ); READ_NUM_VALUE( sets->allow_merge_distant_halt ); READ_NUM_VALUE( sets->max_route_steps ); READ_NUM_VALUE( sets->max_choose_route_steps ); READ_NUM_VALUE( sets->max_hops ); READ_NUM_VALUE( sets->max_transfers ); // routing on ways READ_NUM_VALUE( sets->way_count_straight ); READ_NUM_VALUE( sets->way_count_curve ); READ_NUM_VALUE( sets->way_count_double_curve ); READ_NUM_VALUE( sets->way_count_90_curve ); READ_NUM_VALUE( sets->way_count_slope ); READ_NUM_VALUE( sets->way_count_tunnel ); READ_NUM_VALUE( sets->way_max_bridge_len ); READ_NUM_VALUE( sets->way_count_leaving_way ); READ_BOOL_VALUE( sets->stop_halt_as_scheduled ); } void settings_economy_stats_t::init(settings_t const* const sets) { INIT_INIT INIT_NUM( "remove_dummy_player_months", sets->get_remove_dummy_player_months(), 0, MAX_PLAYER_HISTORY_YEARS*12, 12, false ); INIT_NUM( "unprotect_abandoned_player_months", sets->get_unprotect_abandoned_player_months(), 0, MAX_PLAYER_HISTORY_YEARS*12, 12, false ); INIT_NUM( "ai_construction_speed", sets->get_default_ai_construction_speed(), 0, 1000000000, 1000, false ); SEPERATOR INIT_COST( "starting_money", sets->get_starting_money(sets->get_starting_year()), 1, 0x7FFFFFFFul, 10000, false ); INIT_NUM( "pay_for_total_distance", sets->get_pay_for_total_distance_mode(), 0, 2, gui_numberinput_t::AUTOLINEAR, true ); INIT_NUM( "bonus_basefactor", sets->get_bonus_basefactor(), 0, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_BOOL_NEW( "first_beginner", sets->get_beginner_mode() ); INIT_NUM( "beginner_price_factor", sets->get_beginner_price_factor(), 1, 25000, gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_BOOL( "allow_buying_obsolete_vehicles", sets->get_allow_buying_obsolete_vehicles() ); INIT_NUM( "used_vehicle_reduction", sets->get_used_vehicle_reduction(), 1, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "max_rail_convoi_length", sets->get_max_rail_convoi_length(), 1, 254, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "max_road_convoi_length", sets->get_max_road_convoi_length(), 1, 254, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "max_ship_convoi_length", sets->get_max_ship_convoi_length(), 1, 254, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "max_air_convoi_length", sets->get_max_air_convoi_length(), 1, 254, gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_NUM( "toll_runningcost_percentage", sets->get_way_toll_runningcost_percentage(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "toll_waycost_percentage", sets->get_way_toll_waycost_percentage(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); INIT_BOOL("disable_make_way_public", sets->get_disable_make_way_public()); SEPERATOR INIT_NUM( "just_in_time", sets->get_just_in_time(), 0, 2, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "maximum_intransit_percentage", sets->get_factory_maximum_intransit_percentage(), 0, 32767, gui_numberinput_t::AUTOLINEAR, false ); INIT_BOOL( "crossconnect_factories", sets->is_crossconnect_factories() ); INIT_NUM( "crossconnect_factories_percentage", sets->get_crossconnect_factor(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "industry_increase_every", sets->get_industry_increase_every(), 0, 100000, 100, false ); INIT_NUM( "min_factory_spacing", sets->get_min_factory_spacing(), 1, 32767, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "max_factory_spacing_percent", sets->get_max_factory_spacing_percent(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "max_factory_spacing", sets->get_max_factory_spacing(), 1, 32767, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "electric_promille", sets->get_electric_promille(), 0, 1000, gui_numberinput_t::AUTOLINEAR, false ); INIT_BOOL( "allow_underground_transformers", sets->get_allow_underground_transformers() ); INIT_NUM( "way_height_clearance", sets->get_way_height_clearance(), 1, 2, gui_numberinput_t::AUTOLINEAR, true ); SEPERATOR INIT_NUM( "passenger_factor", sets->get_passenger_factor(), 0, 16, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "minimum_city_distance", sets->get_minimum_city_distance(), 1, 20000, 10, false ); INIT_NUM( "special_building_distance", sets->get_special_building_distance(), 1, 150, 1, false ); INIT_NUM( "factory_worker_radius", sets->get_factory_worker_radius(), 0, 32767, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "factory_worker_minimum_towns", sets->get_factory_worker_minimum_towns(), 0, 32767, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "factory_worker_maximum_towns", sets->get_factory_worker_maximum_towns(), 0, 32767, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "factory_arrival_periods", sets->get_factory_arrival_periods(), 1, 16, gui_numberinput_t::AUTOLINEAR, false ); INIT_BOOL( "factory_enforce_demand", sets->get_factory_enforce_demand() ); INIT_NUM( "factory_worker_percentage", sets->get_factory_worker_percentage(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "tourist_percentage", sets->get_tourist_percentage(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_NUM( "locality_factor[0].year", sets->locality_factor_per_year[0].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[0].factor", sets->locality_factor_per_year[0].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[1].year", sets->locality_factor_per_year[1].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[1].factor", sets->locality_factor_per_year[1].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[2].year", sets->locality_factor_per_year[2].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[2].factor", sets->locality_factor_per_year[2].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[3].year", sets->locality_factor_per_year[3].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[3].factor", sets->locality_factor_per_year[3].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[4].year", sets->locality_factor_per_year[4].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[4].factor", sets->locality_factor_per_year[4].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[5].year", sets->locality_factor_per_year[5].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[5].factor", sets->locality_factor_per_year[5].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[6].year", sets->locality_factor_per_year[6].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[6].factor", sets->locality_factor_per_year[6].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[7].year", sets->locality_factor_per_year[7].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[7].factor", sets->locality_factor_per_year[7].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[8].year", sets->locality_factor_per_year[8].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[8].factor", sets->locality_factor_per_year[8].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); INIT_NUM( "locality_factor[9].year", sets->locality_factor_per_year[9].year, 0, 2999, 10, false ); INIT_NUM( "locality_factor[9].factor", sets->locality_factor_per_year[9].factor, 1, 0x7FFFFFFFu, gui_numberinput_t::POWER2, false ); SEPERATOR INIT_NUM( "passenger_multiplier", sets->get_passenger_multiplier(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "mail_multiplier", sets->get_mail_multiplier(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM( "goods_multiplier", sets->get_goods_multiplier(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); // INIT_NUM( "electricity_multiplier", sets->get_electricity_multiplier(), 0, 10000, 10, false ); SEPERATOR INIT_NUM( "growthfactor_villages", sets->get_growthfactor_small(), 1, 10000, 10, false ); INIT_NUM( "growthfactor_cities", sets->get_growthfactor_medium(), 1, 10000, 10, false ); INIT_NUM( "growthfactor_capitals", sets->get_growthfactor_large(), 1, 10000, 10, false ); SEPERATOR INIT_BOOL( "random_pedestrians", env_t::random_pedestrians); INIT_BOOL( "stop_pedestrians", env_t::stop_pedestrians); INIT_NUM( "citycar_level", sets->get_traffic_level(), 0, 16, 1, false ); INIT_NUM( "default_citycar_life", sets->get_stadtauto_duration(), 1, 1200, 12, false ); INIT_END } void settings_economy_stats_t::read(settings_t* const sets) { READ_INIT sint64 start_money_temp; READ_NUM_VALUE( sets->remove_dummy_player_months ); READ_NUM_VALUE( sets->unprotect_abandoned_player_months ); READ_NUM_VALUE( sets->default_ai_construction_speed ); env_t::default_ai_construction_speed = sets->get_default_ai_construction_speed(); READ_COST_VALUE( start_money_temp ); if( sets->get_starting_money(sets->get_starting_year())!=start_money_temp ) { // because this will render the table based values invalid, we do this only when needed sets->starting_money = start_money_temp; } READ_NUM_VALUE( sets->pay_for_total_distance ); READ_NUM_VALUE( sets->bonus_basefactor ); READ_BOOL_VALUE_NEW( sets->beginner_mode ); READ_NUM_VALUE( sets->beginner_price_factor ); READ_BOOL_VALUE( sets->allow_buying_obsolete_vehicles ); READ_NUM_VALUE( sets->used_vehicle_reduction ); READ_NUM_VALUE( sets->max_rail_convoi_length ); READ_NUM_VALUE( sets->max_road_convoi_length ); READ_NUM_VALUE( sets->max_ship_convoi_length ); READ_NUM_VALUE( sets->max_air_convoi_length ); READ_NUM_VALUE( sets->way_toll_runningcost_percentage ); READ_NUM_VALUE( sets->way_toll_waycost_percentage ); READ_BOOL_VALUE(sets->disable_make_way_public); READ_NUM_VALUE( env_t::just_in_time ); sets->just_in_time = env_t::just_in_time; READ_NUM_VALUE( sets->factory_maximum_intransit_percentage ); READ_BOOL_VALUE( sets->crossconnect_factories ); READ_NUM_VALUE( sets->crossconnect_factor ); READ_NUM_VALUE( sets->industry_increase ); READ_NUM_VALUE( sets->min_factory_spacing ); READ_NUM_VALUE( sets->max_factory_spacing_percentage ); READ_NUM_VALUE( sets->max_factory_spacing ); READ_NUM_VALUE( sets->electric_promille ); READ_BOOL_VALUE( sets->allow_underground_transformers ); READ_NUM_VALUE( sets->way_height_clearance ); READ_NUM_VALUE( sets->passenger_factor ); READ_NUM_VALUE( sets->minimum_city_distance ); READ_NUM_VALUE( sets->special_building_distance ); READ_NUM_VALUE( sets->factory_worker_radius ); READ_NUM_VALUE( sets->factory_worker_minimum_towns ); READ_NUM_VALUE( sets->factory_worker_maximum_towns ); READ_NUM_VALUE( sets->factory_arrival_periods ); READ_BOOL_VALUE( sets->factory_enforce_demand ); READ_NUM_VALUE( sets->factory_worker_percentage ); READ_NUM_VALUE( sets->tourist_percentage ); for( int i=0; i<10; i++ ) { READ_NUM_VALUE( sets->locality_factor_per_year[i].year ); READ_NUM_VALUE( sets->locality_factor_per_year[i].factor ); } READ_NUM_VALUE( sets->passenger_multiplier ); READ_NUM_VALUE( sets->mail_multiplier ); READ_NUM_VALUE( sets->goods_multiplier ); // READ_NUM_VALUE( sets->set_electricity_multiplier ); READ_NUM_VALUE( sets->growthfactor_small ); READ_NUM_VALUE( sets->growthfactor_medium ); READ_NUM_VALUE( sets->growthfactor_large ); READ_BOOL_VALUE( env_t::random_pedestrians ); READ_BOOL_VALUE( env_t::stop_pedestrians ); READ_NUM( sets->set_traffic_level ); READ_NUM_VALUE( sets->stadtauto_duration ); } void settings_costs_stats_t::init(settings_t const* const sets) { INIT_INIT INIT_NUM( "maintenance_building", sets->maint_building, 1, 100000000, 100, false ); INIT_COST( "cost_multiply_dock", -sets->cst_multiply_dock, 1, 100000000, 10, false ); INIT_COST( "cost_multiply_station", -sets->cst_multiply_station, 1, 100000000, 10, false ); INIT_COST( "cost_multiply_roadstop", -sets->cst_multiply_roadstop, 1, 100000000, 10, false ); INIT_COST( "cost_multiply_airterminal", -sets->cst_multiply_airterminal, 1, 100000000, 10, false ); INIT_COST( "cost_multiply_post", -sets->cst_multiply_post, 1, 100000000, 10, false ); INIT_COST( "cost_multiply_headquarter", -sets->cst_multiply_headquarter, 1, 100000000, 10, false ); INIT_COST( "cost_depot_air", -sets->cst_depot_air, 1, 100000000, 10, false ); INIT_COST( "cost_depot_rail", -sets->cst_depot_rail, 1, 100000000, 10, false ); INIT_COST( "cost_depot_road", -sets->cst_depot_road, 1, 100000000, 10, false ); INIT_COST( "cost_depot_ship", -sets->cst_depot_ship, 1, 100000000, 10, false ); INIT_COST( "cost_buy_land", -sets->cst_buy_land, 1, 100000000, 10, false ); INIT_COST( "cost_alter_land", -sets->cst_alter_land, 1, 100000000, 10, false ); INIT_COST( "cost_set_slope", -sets->cst_set_slope, 1, 100000000, 10, false ); INIT_COST( "cost_alter_climate", -sets->cst_alter_climate, 1, 100000000, 10, false ); INIT_COST( "cost_found_city", -sets->cst_found_city, 1, 100000000, 10, false ); INIT_COST( "cost_multiply_found_industry", -sets->cst_multiply_found_industry, 1, 100000000, 10, false ); INIT_COST( "cost_remove_tree", -sets->cst_remove_tree, 1, 100000000, 10, false ); INIT_COST( "cost_multiply_remove_haus", -sets->cst_multiply_remove_haus, 1, 100000000, 10, false ); INIT_COST( "cost_multiply_remove_field", -sets->cst_multiply_remove_field, 1, 100000000, 10, false ); INIT_NUM( "cost_kw_per_credit", sets->cst_kw_per_credit, 1, (1<<24), gui_numberinput_t::POWER2, false); INIT_COST( "cost_transformer", -sets->cst_transformer, 1, 100000000, 10, false ); INIT_COST( "cost_maintain_transformer", -sets->cst_maintain_transformer, 1, 100000000, 10, false ); INIT_NUM("cost_make_public_months", sets->cst_make_public_months, 0, 36000, gui_numberinput_t::AUTOLINEAR, false ); INIT_END } void settings_costs_stats_t::read(settings_t* const sets) { READ_INIT (void)booliter; READ_NUM_VALUE( sets->maint_building ); READ_COST_VALUE( sets->cst_multiply_dock )*(-1); READ_COST_VALUE( sets->cst_multiply_station )*(-1); READ_COST_VALUE( sets->cst_multiply_roadstop )*(-1); READ_COST_VALUE( sets->cst_multiply_airterminal )*(-1); READ_COST_VALUE( sets->cst_multiply_post )*(-1); READ_COST_VALUE( sets->cst_multiply_headquarter )*(-1); READ_COST_VALUE( sets->cst_depot_air )*(-1); READ_COST_VALUE( sets->cst_depot_rail )*(-1); READ_COST_VALUE( sets->cst_depot_road )*(-1); READ_COST_VALUE( sets->cst_depot_ship )*(-1); READ_COST_VALUE( sets->cst_buy_land )*(-1); READ_COST_VALUE( sets->cst_alter_land )*(-1); READ_COST_VALUE( sets->cst_set_slope )*(-1); READ_COST_VALUE( sets->cst_alter_climate )*(-1); READ_COST_VALUE( sets->cst_found_city )*(-1); READ_COST_VALUE( sets->cst_multiply_found_industry )*(-1); READ_COST_VALUE( sets->cst_remove_tree )*(-1); READ_COST_VALUE( sets->cst_multiply_remove_haus )*(-1); READ_COST_VALUE( sets->cst_multiply_remove_field )*(-1); READ_NUM_VALUE(sets->cst_kw_per_credit); READ_COST_VALUE(sets->cst_transformer)* (-1); READ_COST_VALUE( sets->cst_maintain_transformer )*(-1); READ_NUM_VALUE(sets->cst_make_public_months); } #include "../descriptor/ground_desc.h" static char const * const climate_generate_string[] = { "height based", "temperature-humidity based" }; static char const * const tree_generate_string[] = { "none", "random", "rainfall" }; void settings_climates_stats_t::init(settings_t* const sets) { int mountain_height_start = (int)sets->get_max_mountain_height(); int mountain_roughness_start = (int)(sets->get_map_roughness()*20.0 + 0.5)-8; local_sets = sets; INIT_INIT INIT_NUM_NEW( "height_map_conversion_version", env_t::pak_height_conversion_factor, 1, 2, 0, false ); SEPERATOR INIT_NUM_NEW( "Water level", sets->get_groundwater(), -20*(ground_desc_t::double_grounds?2:1), 20, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM_NEW( "Mountain height", mountain_height_start, 0, min(1000,100*(11-mountain_roughness_start)), 10, false ); INIT_NUM_NEW( "Map roughness", mountain_roughness_start, 0, min(10, 11-((mountain_height_start+99)/100)), gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_NUM_NEW( "Wind direction", sets->wind_direction, 0, 3, 1, true ); // combobox for climate generator climate_generate.clear_elements(); for( uint32 i=0; i( climate_generate_string[i], SYSCOL_TEXT ) ; } climate_generate.set_selection( sets->get_climate_generator() ); climate_generate.set_focusable( false ); add_component( &climate_generate ); INIT_LB( "climate generator" ); add_table(3,0); { // other climate borders ... for( int i=desert_climate; i<=arctic_climate; i++ ) { INIT_NUM( ground_desc_t::get_climate_name_from_bit((climate)i), sets->get_climate_borders(i,0), sets->get_groundwater(), 127, gui_numberinput_t::AUTOLINEAR, false ); gui_numberinput_t *ni = new_component(); ni->init( sets->get_climate_borders(i,1), sets->get_groundwater(), 127, gui_numberinput_t::AUTOLINEAR, false ); numinp.append( ni ); } } end_table(); new_component(); INIT_NUM_NEW( "climate area percentage", sets->get_patch_size_percentage(), 0, 100, gui_numberinput_t::AUTOLINEAR, false ); SEPERATOR INIT_NUM_NEW( "lake_height", sets->get_lakeheight(), 0, 127, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM_NEW( "Number of rivers", sets->get_river_number(), 0, 1024, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM_NEW( "minimum length of rivers", sets->get_min_river_length(), 0, max(16,sets->get_max_river_length())-16, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM_NEW( "maximum length of rivers", sets->get_max_river_length(), sets->get_min_river_length()+16, 8196, gui_numberinput_t::AUTOLINEAR, false ); // add listener to all of them for(gui_numberinput_t* const n : numinp) { n->add_listener(this); } // the following are independent and thus need no listener SEPERATOR // combobox for trees generator tree_generate.clear_elements(); for( uint32 i=0; i( tree_generate_string[i], SYSCOL_TEXT ) ; } tree_generate.set_selection( sets->get_tree_distribution() ); tree_generate.set_focusable( false ); add_component( &tree_generate, 2); INIT_NUM_NEW( "forest_base_size", sets->get_forest_base_size(), 10, 255, 1, false ); INIT_NUM_NEW( "forest_map_size_divisor", sets->get_forest_map_size_divisor(), 2, 255, 1, false ); INIT_NUM_NEW( "forest_count_divisor", sets->get_forest_count_divisor(), 2, 255, 1, false ); INIT_NUM_NEW( "forest_inverse_spare_tree_density", sets->get_forest_inverse_spare_tree_density(), 33, 10000, 10, false ); INIT_NUM( "max_no_of_trees_on_square", sets->get_max_no_of_trees_on_square(), 1, 5, 1, true ); INIT_NUM_NEW( "tree_climates", sets->get_tree_climates(), 0, 255, 1, false ); INIT_NUM_NEW( "no_tree_climates", sets->get_no_tree_climates(), 0, 255, 1, false ); INIT_END } void settings_climates_stats_t::read(settings_t* const sets) { sets->climate_generator = (settings_t::climate_generate_t)max( 0, climate_generate.get_selection() ); sets->tree_distribution = ::clamp(tree_generate.get_selection(), (int)settings_t::TREE_DIST_NONE, (int)settings_t::TREE_DIST_COUNT-1 ); READ_INIT READ_NUM_VALUE_NEW( env_t::pak_height_conversion_factor ); READ_NUM_VALUE_NEW( sets->groundwater ); READ_NUM_VALUE_NEW( sets->max_mountain_height ); READ_NUM_VALUE_NEW( sets->wind_direction ); double n = 0; READ_NUM_VALUE_NEW( n ); if( new_world ) { sets->map_roughness = (n+8.0)/20.0; } // other climate borders ... for( int i=desert_climate; i<=arctic_climate; i++ ) { sint16 ch; READ_NUM_VALUE( ch ); sets->climate_borders[i][0] = ch; READ_NUM_VALUE( ch ); sets->climate_borders[i][1] = ch; } READ_NUM_VALUE_NEW( sets->patch_size_percentage ); READ_NUM_VALUE_NEW( sets->lake_height ); READ_NUM_VALUE_NEW( sets->river_number ); READ_NUM_VALUE_NEW( sets->min_river_length ); READ_NUM_VALUE_NEW( sets->max_river_length ); READ_NUM_VALUE_NEW( sets->forest_base_size ); READ_NUM_VALUE_NEW( sets->forest_map_size_divisor ); READ_NUM_VALUE_NEW( sets->forest_count_divisor ); READ_NUM_VALUE_NEW( sets->forest_inverse_spare_tree_density ); READ_NUM_VALUE( sets->max_no_of_trees_on_square ); READ_NUM_VALUE_NEW( sets->tree_climates ); READ_NUM_VALUE_NEW( sets->no_tree_climates ); (void)booliter; // silence warning } bool settings_climates_stats_t::action_triggered(gui_action_creator_t *comp, value_t) { welt_gui_t *welt_gui = dynamic_cast(win_get_magic( magic_welt_gui_t )); read( local_sets ); uint i = 0; for(gui_numberinput_t* const n : numinp) { if (n == comp && i < 3 && welt_gui) { // update world preview welt_gui->update_preview(); } i++; } return true; } simutrans-124.5/src/simutrans/gui/settings_stats.h000066400000000000000000000123771520675460500224250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SETTINGS_STATS_H #define GUI_SETTINGS_STATS_H #include #include "../tpl/vector_tpl.h" #include "../utils/cbuffer.h" #include "components/gui_container.h" #include "components/gui_component.h" #include "components/gui_numberinput.h" #include "components/gui_label.h" #include "components/action_listener.h" #include "components/gui_combobox.h" class settings_t; /* With the following macros, elements could be added to the property lists. * ATTENTION: In the init and read procedures, the order of the items MUST be identical! */ // call this before any init is done ... #define INIT_INIT \ new_world = (win_get_magic( magic_welt_gui_t )!=NULL);\ numinp.clear(); \ button.clear(); \ remove_all(); \ set_table_layout(1,0); \ add_table(2,0); // at the end of init #define INIT_END \ end_table();\ set_size( get_min_size()); #define INIT_NUM(t,a,b,c,d,e) \ {\ gui_numberinput_t *ni = new_component();\ ni->init( (sint32)(a), (b), (c), (d), (e) );\ numinp.append( ni );\ gui_label_t *lb = new_component();\ lb->set_text_pointer(t);\ } #define INIT_NUM_NEW(t,a,b,c,d,e) if( new_world ) INIT_NUM( (t), (a), (b), (c), (d) , (e) ) #define INIT_COLOR(t,val,step) {\ gui_numberinput_t *ni = new_component();\ ni->init((sint32)((val).r << 16 | (val).g << 8 | (val).b), 0, 0x00FFFFFF, (step), 0);\ numinp.append(ni);\ gui_label_t *lb = new_component();\ lb->set_text_pointer(t);\ } #define INIT_COLOR_NEW(t,val,step) if (new_world) INIT_COLOR( (t), (val), (step) ) #define INIT_COST(t,a,b,c,d,e) \ {\ gui_numberinput_t *ni = new_component();\ ni->init( (sint32)( (a)/(sint64)100 ), (b), (c), (d), (e) );\ numinp.append( ni );\ gui_label_t *lb = new_component();\ lb->set_text_pointer(t);\ }\ #define INIT_COST_NEW(t,a,b,c,d,e) if( new_world ) INIT_COST( (t), (a), (b), (c), (d) , (e) ) #define INIT_LB(t) \ {\ gui_label_t *lb = new_component();\ lb->set_text_pointer(t);\ }\ #define INIT_LB_NEW(t) if( new_world ) INIT_LB( (t) ) #define INIT_BOOL(t,a) \ {\ button_t *bt = new_component_span(2);\ bt->init(button_t::square_automatic, (t));\ button.append( bt );\ bt->pressed = (a);\ }\ #define INIT_BOOL_NEW(t,a) if( new_world ) INIT_BOOL( (t), (a) ) #define SEPERATOR \ new_component_span(2); // call this before and READ_... #define READ_INIT \ slist_tpl::const_iterator numiter = numinp.begin(); \ slist_tpl::const_iterator booliter = button.begin(); #define READ_NUM(t) (t)((*numiter++)->get_value()) #define READ_NUM_NEW(t) if(new_world) { READ_NUM(t); } #define READ_COST(t) (t)((sint64)((*numiter++)->get_value()) * 100) #define READ_NUM_VALUE(t) (t) = (*numiter++)->get_value() #define READ_NUM_VALUE_NEW(t) if(new_world) { READ_NUM_VALUE(t); } #define READ_COL_VALUE(t) do { const uint32_t v = (*numiter++)->get_value(); (t).r = v >> 16; (t).g = v >> 8; (t).b = v; } while (false) #define READ_COL_VALUE_NEW(t) if (new_world) { READ_COL_VALUE(t); } #define READ_COST_VALUE(t) (t) = (sint64)((*numiter++)->get_value()) * 100 #define READ_COST_VALUE_NEW(t) if(new_world) { READ_COST_VALUE(t); } #define READ_BOOL(t) (t)((*booliter++)->pressed) #define READ_BOOL_NEW(t) if(new_world) { READ_BOOL(t); } #define READ_BOOL_VALUE(t) (t) = (*booliter++)->pressed #define READ_BOOL_VALUE_NEW(t) if(new_world) { READ_BOOL_VALUE(t); } /** * Settings for property lists */ class settings_stats_t { protected: bool new_world; // since the copy constructor will not copy the right action listener => pointer slist_tpl numinp; slist_tpl button; public: settings_stats_t() {} void init(settings_t const*); void read(settings_t const*); }; // the only task left are the respective init/reading routines class settings_general_stats_t : protected settings_stats_t, public gui_aligned_container_t, public action_listener_t { gui_combobox_t savegame; public: // needed for savegame combobox bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void init(settings_t const*); void read(settings_t*); }; class settings_display_stats_t : protected settings_stats_t, public gui_aligned_container_t { public: void init(settings_t const*); void read(settings_t*); }; class settings_routing_stats_t : protected settings_stats_t, public gui_aligned_container_t { public: void init(settings_t const*); void read(settings_t*); }; class settings_economy_stats_t : protected settings_stats_t, public gui_aligned_container_t { public: void init(settings_t const*); void read(settings_t*); }; class settings_costs_stats_t : protected settings_stats_t, public gui_aligned_container_t { public: void init(settings_t const*); void read(settings_t*); }; class settings_climates_stats_t : protected settings_stats_t, public gui_aligned_container_t, public action_listener_t { private: gui_combobox_t climate_generate; gui_combobox_t tree_generate; settings_t* local_sets; public: void init(settings_t*); void read(settings_t*); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/signal_info.cc000066400000000000000000000041031520675460500217610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "components/gui_label.h" #include "../obj/signal.h" #include "../player/simplay.h" #include "signal_info.h" #include "../tool/simmenu.h" #include "../world/simworld.h" #include "../dataobj/ribi.h" signal_info_t::signal_info_t(signal_t *s) : obj_infowin_t(s), sig(s) { two_ways_toggle.init( button_t::square_state, "allow reverse passage"); two_ways_toggle.add_listener( this ); two_ways_toggle.pressed = sig->get_two_ways(); add_component( &two_ways_toggle ); remove.init( button_t::roundbox, "remove signal"); remove.add_listener( this ); add_component( &remove ); // show author below the settings if( char const* const maker = sig->get_desc()->get_copyright() ) { gui_label_buf_t* lb = new_component(); lb->buf().printf(translator::translate("Constructed by %s"), maker); lb->update(); } recalc_size(); } bool signal_info_t::action_triggered( gui_action_creator_t *comp, value_t /* */) { if (comp == &remove) { bool suspended_execution=false; koord3d pos = sig->get_pos(); tool_t::general_tool[TOOL_REMOVE_SIGNAL]->set_default_param(NULL); const char *err = welt->call_work_api( tool_t::general_tool[TOOL_REMOVE_SIGNAL], welt->get_active_player(), pos, suspended_execution); if(!suspended_execution) { // play sound / error message welt->get_active_player()->tell_tool_result(tool_t::general_tool[TOOL_REMOVE_SIGNAL], pos, err); } } else if (comp == &two_ways_toggle) { char param[64]; sprintf(param, "%s,1,%u", sig->get_pos().get_str(), !sig->get_two_ways()); tool_t::simple_tool[TOOL_CHANGE_TRAFFIC_LIGHT]->set_default_param(param); welt->set_tool(tool_t::simple_tool[TOOL_CHANGE_TRAFFIC_LIGHT], welt->get_active_player()); } return true; } void signal_info_t::draw( scr_coord pos, scr_size size ) { two_ways_toggle.pressed = sig->get_two_ways(); two_ways_toggle.enable( ribi_t::is_single(sig->get_dir()) ); remove.enable( !sig->get_removal_error( welt->get_active_player() ) ); obj_infowin_t::draw( pos, size ); } simutrans-124.5/src/simutrans/gui/signal_info.h000066400000000000000000000013711520675460500216270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SIGNAL_INFO_H #define GUI_SIGNAL_INFO_H #include "../simconst.h" #include "obj_info.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_container.h" class signal_t; /** * Info window for factories */ class signal_info_t : public obj_infowin_t, public action_listener_t { private: button_t two_ways_toggle; button_t remove; signal_t *sig; public: signal_info_t(signal_t *s); const char *get_help_filename() const OVERRIDE {return "signal_info.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void draw( scr_coord pos, scr_size size ) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/signal_spacing.cc000066400000000000000000000035111520675460500224540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_frame.h" #include "components/gui_button.h" #include "components/gui_label.h" #include "components/action_listener.h" #include "components/gui_numberinput.h" #include "signal_spacing.h" #include "../tool/simtool.h" uint8 signal_spacing_frame_t::signal_spacing = 2; bool signal_spacing_frame_t::remove = true; bool signal_spacing_frame_t::replace = true; signal_spacing_frame_t::signal_spacing_frame_t(player_t *player_, tool_build_roadsign_t* tool_) : gui_frame_t( translator::translate("set signal spacing") ), signal_label("signal spacing") { player = player_; tool = tool_; tool->get_values(player, signal_spacing, remove, replace); set_table_layout(3,0); add_component( &signal_label ); new_component(); signal_spacing_inp.add_listener(this); signal_spacing_inp.init(signal_spacing, 1, 50, 1, true, 3); add_component( &signal_spacing_inp ); remove_button.init( button_t::square_state, "remove interm. signals"); remove_button.add_listener(this); remove_button.pressed = remove; add_component( &remove_button, 3); replace_button.init( button_t::square_state, "replace other signals"); replace_button.add_listener(this); replace_button.pressed = replace; add_component( &replace_button, 3); reset_min_windowsize(); set_windowsize(get_min_windowsize() ); } bool signal_spacing_frame_t::action_triggered( gui_action_creator_t *comp, value_t) { if( comp == &signal_spacing_inp ) { signal_spacing = signal_spacing_inp.get_value(); } else if( comp == &remove_button ) { remove = !remove; remove_button.pressed = remove; } else if( comp == &replace_button ) { replace = !replace; replace_button.pressed = replace; } tool->set_values(player, signal_spacing, remove, replace); return true; } simutrans-124.5/src/simutrans/gui/signal_spacing.h000066400000000000000000000020461520675460500223200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SIGNAL_SPACING_H #define GUI_SIGNAL_SPACING_H #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_numberinput.h" #include "components/gui_label.h" class gui_numberinput_t; class button_t; class gui_label_t; class tool_build_roadsign_t; class player_t; /* * Dialogue to set the signal spacing, when CTRL+clicking a signal on toolbar * Used by tool_build_roadsign_t */ class signal_spacing_frame_t : public gui_frame_t, private action_listener_t { private: static uint8 signal_spacing; static bool remove, replace; player_t *player; tool_build_roadsign_t* tool; gui_numberinput_t signal_spacing_inp; gui_label_t signal_label; button_t remove_button, replace_button; public: signal_spacing_frame_t( player_t *, tool_build_roadsign_t * ); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; const char * get_help_filename() const OVERRIDE { return "signal_spacing.txt"; } }; #endif simutrans-124.5/src/simutrans/gui/simwin.cc000066400000000000000000002254421520675460500210120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "../simcolor.h" #include "../simevent.h" #include "../display/simgraph.h" #include "../display/viewport.h" #include "../tool/simmenu.h" #include "../simskin.h" #include "../sys/simsys.h" #include "../simticker.h" #include "simwin.h" #include "../simintr.h" #include "../simhalt.h" #include "../world/simworld.h" #include "../utils/simrandom.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/loadsave.h" #include "../dataobj/tabfile.h" #include "../descriptor/skin_desc.h" #include "../obj/zeiger.h" #include "../player/simplay.h" #include "../tpl/inthashtable_tpl.h" #include "../tpl/vector_tpl.h" #include "../utils/simstring.h" #include "../utils/cbuffer.h" #include "map_frame.h" #include "help_frame.h" #include "messagebox.h" #include "gui_frame.h" // needed to restore/save them #include "tool_selector.h" #include "player_frame.h" #include "money_frame.h" #include "halt_info.h" #include "convoi_detail.h" #include "convoi_frame.h" #include "convoi_info.h" #include "line_management_gui.h" #include "schedule_list.h" #include "city_info.h" #include "citylist_frame.h" #include "message_frame.h" #include "chat_frame.h" #include "message_option.h" #include "fabrik_info.h" #include "themeselector.h" #include "goods_frame.h" #include "loadfont_frame.h" #ifdef USE_FLUIDSYNTH_MIDI #include "loadsoundfont_frame.h" #endif #include "scenario_info.h" #include "depot_frame.h" #include "depotlist_frame.h" #include "halt_list_frame.h" #include "vehiclelist_frame.h" #include "curiositylist_frame.h" #include "factorylist_frame.h" #include "labellist_frame.h" #include "display_settings.h" #include "optionen.h" #include "player_ranking_frame.h" #include "../simversion.h" class inthashtable_tpl old_win_pos; // hash-table: magic number to windowsize class inthashtable_tpl saved_windowsizes; // true if there is little space for the status messages static bool status_show_compact = false; // true if toolbar magic bool inline is_toolbar(ptrdiff_t magic) { return magic >= magic_toolbar && magic < magic_script_error; } // I added a button to the map window to fix it's size to the best one. // This struct is the flow back to the object of the refactoring. class simwin_gadget_flags_t { public: simwin_gadget_flags_t( ) : title(true), close( false ), help( false ), prev( false ), size( false ), next( false ), sticky( false ), gotopos( false ) { } bool title:1; bool close:1; bool help:1; bool prev:1; bool size:1; bool next:1; bool sticky:1; bool gotopos:1; }; class simwin_t { public: scr_coord pos; // Window position uint32 dauer; // How long should the window stay open? uint8 wt; // the flags for the window type ptrdiff_t magic_number; // either magic number or this pointer (which is unique too) gui_frame_t *gui; uint16 gadget_state; // which buttons to hilite bool sticky; // true if window is sticky bool rollup; bool dirty; simwin_gadget_flags_t flags; // See Above. simwin_t() : flags() {} bool operator== (const simwin_t &) const; }; bool simwin_t::operator== (const simwin_t &other) const { return gui == other.gui; } #define MAX_WIN (64) static vector_tpl wins(MAX_WIN); static vector_tpl kill_list(MAX_WIN); static karte_t* wl = NULL; // Pointer to current world is set in win_set_world static int top_win(int win, bool keep_state ); static void display_win(int win); // tooltip data static scr_coord_val tooltip_xpos = 0; static scr_coord_val tooltip_ypos = 0; static const char * tooltip_text = 0; static std::string static_tooltip_text; // For timed tooltip with initial delay and finite visible duration. // Valid owners are required for timing. Invalid (NULL) owners disable timing. static const void * tooltip_owner = 0; // owner of the registered tooltip static const void * tooltip_group = 0; // group to which the owner belongs static uint32 tooltip_register_time = 0; // time at which a tooltip is initially registered static bool show_ticker=0; /* if we are inside the event handler, * the window handler has gui pointer as value, * to defer destruction if this window */ static void *inside_event_handling = NULL; // only this gui element can set a tooltip static void *tooltip_element = NULL; static bool destroy_framed_win(simwin_t *win); //========================================================================= // Helper Functions #define REVERSE_GADGETS (!env_t::window_buttons_right) /** * Display a window gadget */ static int display_gadget_box(sint8 code, int const x, int const y, PIXVAL lighter, PIXVAL darker, bool const pushed) { // If we have a skin, get gadget image data const image_t *img = NULL; if( skinverwaltung_t::gadget ) { // "x", "?", "=", "<<", ">>" img = skinverwaltung_t::gadget->get_image(code); } if(pushed) { // mark_rect_dirty_wc(x, y, D_GADGET_WIDTH, D_TITLEBAR_HEIGHT); gfx->draw_rect_clipped(x, y, D_GADGET_WIDTH, D_TITLEBAR_HEIGHT, lighter, false CLIP_NUM_DEFAULT); } // Do we have a gadget image? if( img != NULL ) { // Max Kielland: This center the gadget image and compensates for any left/top margins within the image to be backward compatible with older PAK sets. gfx->draw_img_aligned(img->imageid, scr_rect(x, y, D_GADGET_WIDTH, D_TITLEBAR_HEIGHT), ALIGN_CENTER_H | ALIGN_CENTER_V, false); } else { const char *gadget_text = "#"; static const char *gadget_texts[5]={ "X", "?", "=", "<", ">" }; if( code <= SKIN_BUTTON_NEXT ) { gadget_text = gadget_texts[code]; } else if( code == SKIN_GADGET_GOTOPOS ) { gadget_text = "*"; } else if( code == SKIN_GADGET_NOTPINNED ) { gadget_text = "s"; } else if( code == SKIN_GADGET_PINNED ) { gadget_text = "S"; } gfx->draw_text( x+4, y+4, gadget_text, ALIGN_LEFT, SYSCOL_TEXT, false ); } int side = x+REVERSE_GADGETS*D_GADGET_WIDTH-1; gfx->draw_vline_clipped(side+1, y+1, D_TITLEBAR_HEIGHT-2, lighter, false CLIP_NUM_DEFAULT); gfx->draw_vline_clipped(side, y+1, D_TITLEBAR_HEIGHT-2, darker, false CLIP_NUM_DEFAULT); // return width of gadget return D_GADGET_WIDTH; } static int display_gadget_boxes( simwin_gadget_flags_t* flags, int x, int y, PIXVAL lighter, PIXVAL darker, uint16 gadget_state, bool sticky_pushed, bool goto_pushed ) { int width = 0; const int k=(REVERSE_GADGETS?1:-1); // Only the close and sticky gadget can be pushed. if( flags->close ) { width += k*display_gadget_box( SKIN_GADGET_CLOSE, x + width, y, lighter, darker, gadget_state & (1<size ) { width += k*display_gadget_box( SKIN_GADGET_MINIMIZE, x + width, y, lighter, darker, gadget_state & (1<help ) { width += k*display_gadget_box( SKIN_GADGET_HELP, x + width, y, lighter, darker, gadget_state & (1<prev ) { width += k*display_gadget_box( SKIN_BUTTON_PREVIOUS, x + width, y, lighter, darker, gadget_state & (1<next ) { width += k*display_gadget_box( SKIN_BUTTON_NEXT, x + width, y, lighter, darker, gadget_state & (1<gotopos ) { width += k*display_gadget_box( SKIN_GADGET_GOTOPOS, x + width, y, lighter, darker, goto_pushed || (gadget_state & (1<sticky ) { width += k*display_gadget_box( sticky_pushed ? SKIN_GADGET_PINNED : SKIN_GADGET_NOTPINNED, x + width, y, lighter, darker, gadget_state & (1<close ) { if( offset >= 0 && offsetsize ) { if( offset >= 0 && offsethelp ) { if( offset >= 0 && offsetprev ) { if( offset >= 0 && offsetnext ) { if( offset >= 0 && offsetgotopos ) { if( offset >= 0 && offsetsticky ) { if( offset >= 0 && offsetblend_colors(title_color, gfx->palette_lookup(COL_WHITE), 25); PIXVAL darker = gfx->blend_colors(title_color, gfx->palette_lookup(COL_BLACK), 25); // fill title bar with color gfx->draw_rect_clipped(pos.x, pos.y, size.w, D_TITLEBAR_HEIGHT, title_color, true CLIP_NUM_DEFAULT); // border of title bar gfx->draw_rect_clipped( pos.x + 1, pos.y, size.w - 2, 1, lighter, false CLIP_NUM_DEFAULT); // top gfx->draw_rect_clipped( pos.x + 1, pos.y + D_TITLEBAR_HEIGHT - 1, size.w - 2, 1, darker, false CLIP_NUM_DEFAULT); // bottom gfx->draw_vline_clipped( pos.x, pos.y, D_TITLEBAR_HEIGHT, lighter, false CLIP_NUM_DEFAULT); // left gfx->draw_vline_clipped( pos.x + size.w - 1, pos.y, D_TITLEBAR_HEIGHT, darker, false CLIP_NUM_DEFAULT); // right // Draw the gadgets and then move left and draw text. flags.gotopos = (welt_pos != koord3d::invalid); int width = display_gadget_boxes( &flags, pos.x+(REVERSE_GADGETS?0:size.w-D_GADGET_WIDTH), pos.y, lighter, darker, gadget_state, sticky, goto_pushed ); int titlewidth = gfx->draw_text_clipped( pos.x + (REVERSE_GADGETS?width+4:4), pos.y+(D_TITLEBAR_HEIGHT-LINEASCENT)/2, text, ALIGN_LEFT, text_color, false ); if( flags.gotopos ) { gfx->draw_text_clipped( pos.x + (REVERSE_GADGETS?width+4:4)+titlewidth+8, pos.y+(D_TITLEBAR_HEIGHT-LINEASCENT)/2, welt_pos.get_fullstr(), ALIGN_LEFT, text_color, false ); } POP_CLIP(); } //------------------------------------------------------------------------- /** * Draw dragger widget */ static void win_draw_window_dragger(scr_coord pos, scr_size size) { pos += size; if( skinverwaltung_t::gadget && skinverwaltung_t::gadget->get_image_id(SKIN_WINDOW_RESIZE)!=IMG_EMPTY ) { const image_t *dragger = skinverwaltung_t::gadget->get_image(SKIN_WINDOW_RESIZE); gfx->draw_color_img( dragger->get_id(), pos.x-dragger->get_pic()->w, pos.y-dragger->get_pic()->h, 0, false, false CLIP_NUM_DEFAULT); } else { int dragger_size = min(D_DRAGGER_WIDTH, D_DRAGGER_HEIGHT); for( int x=1; xdraw_rect_clipped( pos.x-x, pos.y-dragger_size+x, x, 1, gfx->palette_lookup((x & 1) ? COL_BLACK : MN_GREY4), true CLIP_NUM_DEFAULT); } } } // Functions to save & restore windowsize ptrdiff_t guess_magic_number(simwin_t *win, ptrdiff_t magic = magic_none) { if (win) { magic = win->magic_number; } // reduce player-wise magic numbers and magic numbers derived from handles const ptrdiff_t magic_pl[] = { magic_finances_t, magic_convoi_list, magic_convoi_list_filter, magic_line_list, magic_halt_list, magic_line_management_t, magic_ai_options_t, magic_ai_selector, magic_pwd_t, magic_jump, magic_headquarter, magic_headquarter + MAX_PLAYER_COUNT, magic_none, magic_convoi_info, magic_halt_info, magic_toolbar, }; for (uint i=1; igui->get_rdwr_id(); if (id != magic_reserved) { return id; } // anything outside the magic number's ranges will be ignored if (magic <= magic_reserved || magic >= magic_max) { magic = magic_none; } return magic; } static void save_windowsize(simwin_t *win) { ptrdiff_t magic = guess_magic_number(win); if (magic != magic_none) { saved_windowsizes.set(magic, win->gui->get_windowsize() ); } } static scr_size get_stored_windowsize(simwin_t *win) { ptrdiff_t magic = guess_magic_number(win); if (magic != magic_none) { return saved_windowsizes.get(magic); } return scr_size(); } void rdwr_win_settings(loadsave_t *file) { if (file->is_loading()) { saved_windowsizes.clear(); ptrdiff_t magic; do { sint64 rd_magic; file->rdwr_longlong(rd_magic); magic = (ptrdiff_t)rd_magic; if (magic != magic_none) { scr_size s; file->rdwr_long(s.w); file->rdwr_long(s.h); // ignore stuff that will not be used anymore if (magic == guess_magic_number(NULL, magic)) { saved_windowsizes.put(magic, s); } } } while (magic != magic_none); } else { for(auto it : saved_windowsizes) { sint64 m = it.key; file->rdwr_longlong(m); file->rdwr_long(it.value.w); file->rdwr_long(it.value.h); } sint64 m = magic_none; file->rdwr_longlong(m); } } //========================================================================= // returns the window (if open) otherwise zero gui_frame_t *win_get_magic(ptrdiff_t magic) { if( magic!=-1 && magic!=0 ) { // there is at most one window with a positive magic number for(simwin_t const& i : wins ) { if( i.magic_number == magic ) { // if 'special' magic number, return it return i.gui; } } } return NULL; } // sets the magic of a gui_frame_t (needed during reload of windows) bool win_set_magic( gui_frame_t *gui, ptrdiff_t magic ) { if( magic!=-1 && magic!=0 ) { // there is at most one window with a positive magic number for(simwin_t &i : wins ) { if( i.gui == gui ) { i.magic_number = magic; return true; } } } return false; } /** * Returns top window */ gui_frame_t *win_get_top() { return wins.empty() ? 0 : wins.back().gui; } /** * returns the focused component of the top window */ gui_component_t *win_get_focus() { return wins.empty() ? 0 : wins.back().gui->get_focus(); } bool win_is_textinput() { if( gui_component_t *comp = win_get_focus() ) { return dynamic_cast(comp) || dynamic_cast(comp) || dynamic_cast(comp); } return false; } uint32 win_get_open_count() { return wins.get_count(); } gui_frame_t* win_get_index(uint32 i) { if (i < wins.get_count()) { return wins[i].gui; } return NULL; } // brings a window to front, if open bool top_win( const gui_frame_t *gui, bool keep_rollup ) { for( uint i=0; iis_version_atleast(120, 8) ) { if( file->is_saving() ) { for(simwin_t & i : wins) { uint32 id = i.gui->get_rdwr_id(); if( id!=magic_reserved ) { file->rdwr_long( id ); i.pos.rdwr(file); file->rdwr_byte(i.wt); file->rdwr_bool(i.sticky); file->rdwr_bool(i.rollup); i.gui->rdwr(file); } } uint32 end = magic_none; file->rdwr_long( end ); } else { // restore windows while(1) { uint32 id; file->rdwr_long(id); // create the matching gui_frame_t *w = NULL; switch(magic_numbers(id)) { // end of dialogues case magic_none: return; // actual dialogues to restore case magic_themes: w = new themeselector_t(); break; case magic_reliefmap: w = new map_frame_t(); break; case magic_ki_kontroll_t: w = new ki_kontroll_t(); break; case magic_line_schedule_rdwr_dummy: w = new line_management_gui_t(); break; case magic_city_info_t: w = new city_info_t(); break; case magic_messageframe: w = new message_frame_t(); break; case magic_message_options: w = new message_option_t(); break; case magic_chatframe: w = new chat_frame_t(); break; case magic_factory_info: w = new fabrik_info_t(); break; case magic_goodslist: w = new goods_frame_t(); break; case magic_font: w = new loadfont_frame_t(); break; #ifdef USE_FLUIDSYNTH_MIDI case magic_soundfont: w = new loadsoundfont_frame_t(); break; #endif case magic_scenario_info: w = new scenario_info_t(); break; case magic_depot: w = new depot_frame_t(); break; case magic_convoi_list: w = new convoi_frame_t(); break; case magic_depotlist: w = new depotlist_frame_t(); break; case magic_vehiclelist: w = new vehiclelist_frame_t(); break; case magic_halt_list: w = new halt_list_frame_t(); break; case magic_citylist_frame_t: w = new citylist_frame_t(); break; case magic_curiositylist: w = new curiositylist_frame_t(); break; case magic_factorylist: w = new factorylist_frame_t(); break; case magic_labellist: w = new labellist_frame_t(); break; case magic_color_gui_t: w = new color_gui_t(); break; case magic_optionen_gui_t: w = new optionen_gui_t(); break; case magic_player_ranking: w = new player_ranking_frame_t(); break; default: if( id>=magic_finances_t && idget_player(id-magic_finances_t) ); } else if( id>=magic_line_management_t && idget_player(id-magic_line_management_t) ); } else if( id>=magic_toolbar && idupdate(wl->get_active_player()); w = tool_t::toolbar_tool[id-magic_toolbar]->get_tool_selector(); } else if( id>=magic_convoi_info && id=magic_halt_info && iderror( "rdwr_all_win()", "No idea how to restore magic 0x%X", id ); return; } } /* sequence is now the same for all dialogues * restore coordinates * create window * read state * restore content * restore state - gui_frame_t::rdwr() might create its own window ->> want to restore state to that window */ scr_coord p; p.rdwr(file); uint8 win_type; file->rdwr_byte( win_type ); create_win( p, w, (wintype)win_type, id ); bool sticky, rollup; file->rdwr_bool( sticky ); file->rdwr_bool( rollup ); // now load the window uint32 count = wins.get_count(); w->rdwr( file ); // restore sticky / rollup status // ensure that the new status is to currently loaded window if (wins.get_count() >= count) { wins.back().sticky = sticky; wins.back().rollup = rollup; } } } } } scr_rect win_get_max_window_area() { const scr_size screen = gfx->get_screen_size(); scr_coord other_pos((env_t::menupos == MENU_LEFT) * env_t::iconsize.w, (env_t::menupos == MENU_TOP) * env_t::iconsize.h + (env_t::menupos == MENU_BOTTOM) * win_get_statusbar_height()); scr_size other_size{ screen.w - other_pos.x - (env_t::menupos == MENU_RIGHT) * env_t::iconsize.w, screen.h - win_get_statusbar_height() - env_t::iconsize.h }; if (show_ticker) { other_size.h -= TICKER_HEIGHT; if (env_t::menupos == MENU_BOTTOM) { other_pos.y += TICKER_HEIGHT; } } return scr_rect(other_pos, other_size); } /* tries to get a window on the screen * without titlebar hidden by any menubar/satus bar elements */ void win_clamp_xywh_position( gui_frame_t* gui, scr_coord &pos, scr_size wh, bool move_topleft ) { // reposition to keep on screen scr_rect screen = win_get_max_window_area(); scr_coord other_pos = screen.get_pos(); scr_size other_size = screen.get_size(); if (pos.y < other_pos.y) { // y alsways visible pos.y = other_pos.y; } if (move_topleft) { if (pos.x < other_pos.x) { pos.x = other_pos.x; } } // should we move to be fully on screen? if (move_topleft) { if (pos.x + wh.w > screen.x + screen.w) { pos.x = screen.x + screen.w - wh.w; } if (pos.y + wh.h > screen.y + screen.h) { pos.y = screen.y + screen.h - wh.h; } } if (gui) { if (wh.w > other_size.w || wh.h > other_size.h) { // window still to big => maybe we can resize it to fit the screen scr_size minsize = gui->get_min_windowsize(); if (minsize != wh) { bool resize = false; if (pos.x < other_pos.x && minsize.w < other_size.w) { // too wide pos.x = other_pos.x; minsize.w = other_size.w; resize = true; } if (pos.y < other_pos.y && minsize.h < other_size.h) { //too high pos.y = other_pos.y; minsize.h = other_size.h; resize = true; } if (resize) { gui->set_windowsize(minsize); } } else if (move_topleft) { // center on screen if (pos.x < other_pos.x) { pos.x = other_pos.x - (wh.w - other_size.w) / 2; } if (pos.y < other_pos.y) { pos.y = other_pos.y - (wh.h - other_size.h) / 2; } } // make sure some corner remains visible if (pos.x + wh.w < other_pos.x + D_MARGIN_LEFT) { pos.x = other_pos.x + D_MARGIN_LEFT - wh.w; } if (pos.x > other_pos.x + other_size.w - D_MARGIN_LEFT) { pos.x = other_pos.x + other_size.w - D_MARGIN_LEFT; } if (pos.y + D_TITLEBAR_HEIGHT > other_pos.y + other_size.h) { pos.y = other_pos.y + other_size.h - D_TITLEBAR_HEIGHT; } } } } int create_win(gui_frame_t* const gui, wintype const wt, ptrdiff_t const magic) { return create_win({ -1, -1 }, gui, wt, magic); } int create_win(scr_coord pos, gui_frame_t *const gui, wintype const wt, ptrdiff_t const magic) { assert(gui!=NULL && magic!=0); if( gui_frame_t *win = win_get_magic(magic) ) { if( env_t::second_open_closes_win ) { destroy_win( win ); if( !( wt & w_do_not_delete ) ) { delete gui; } } else { top_win( win ); } return -1; } if( pos.x==-1 && pos.y==-1 && env_t::remember_window_positions ) { // look for window in hash table if( scr_coord *k = old_win_pos.access(magic) ) { bool overlap = false; if (env_t::stack_toolbars && is_toolbar(magic)) { // make sure we do not overlap with other toolsbars for (uint32 i = 0; i < wins.get_count() && !overlap; i++) { if (wins[i].wt && is_toolbar(wins[i].magic_number)) { overlap = k->y >= wins[i].pos.y && k->y < wins[i].pos.y + wins[i].gui->get_windowsize().h; } } } if(!overlap) { pos = *k; } } } /* if there are too many handles (likely in large games) * we search for any error/news message at the bottom of the stack and delete it * => newer information might be more important ... * This is a stopgap to avoid simutrans to freeze due to many error etc windows */ if( wins.get_count()==MAX_WIN ) { // we try to remove one of the lowest news windows (magic_none) for( uint i=0; i(wins[i].gui)!=NULL ) { destroy_win( wins[i].gui ); break; } } } if( wins.get_count() < MAX_WIN ) { if (!wins.empty()) { // mark old dirty const scr_size size = wins.back().gui->get_windowsize(); gfx->mark_rect_dirty_wc( wins.back().pos.x - 1, wins.back().pos.y - 1, wins.back().pos.x + size.w + 2, wins.back().pos.y + size.h + 2 + D_TITLEBAR_HEIGHT ); // -1, +2 for env_t::window_frame_active } wins.append( simwin_t() ); simwin_t& win = wins.back(); // Make Sure Closes Aren't Forgotten. // Must Reset as the entries and thus flags are reused win.flags.close = true; win.flags.title = gui->has_title(); win.flags.help = ( gui->get_help_filename() != NULL ); win.flags.prev = gui->has_prev(); win.flags.next = gui->has_next(); win.flags.sticky = gui->has_sticky(); win.gui = gui; // take care of time delete windows ... win.wt = wt & w_time_delete ? w_info : wt; win.dauer = (wt&w_time_delete) ? MESG_WAIT : -1; win.magic_number = magic; win.gadget_state = 0; win.rollup = false; win.sticky = false; win.dirty = true; // Notify window to be shown assert(gui); event_t ev; ev.ev_class = INFOWIN; ev.ev_code = WIN_OPEN; ev.mouse_pos.x = 0; ev.mouse_pos.y = 0; ev.click_pos.x = 0; ev.click_pos.y = 0; ev.mouse_button_state = 0; void *old = inside_event_handling; inside_event_handling = gui; gui->infowin_event(&ev); inside_event_handling = old; // restore windowsize scr_size stored = get_stored_windowsize(&win); if (stored == scr_size()) { // not stored, take current stored = gui->get_windowsize(); } // use default width stored.clip_lefttop(scr_size(D_DEFAULT_WIDTH, D_DEFAULT_HEIGHT)); // clip to display size const scr_size screen = gfx->get_screen_size(); stored.clip_rightbottom( scr_size(screen.w, screen.h - env_t::iconsize.h - win_get_statusbar_height() ) ); if (stored != gui->get_windowsize()) { // send tailored resize event scr_size delta = stored - gui->get_windowsize(); event_t wev; wev.ev_class = WINDOW_RESIZE; wev.ev_code = 0; wev.mouse_pos.x = delta.w; wev.mouse_pos.y = delta.h; inside_event_handling = gui; gui->infowin_event(&wev); inside_event_handling = old; } if (pos.x == -1) { if (env_t::stack_toolbars && is_toolbar(magic)) { // try to keep the toolbar next to other toolbars scr_coord_val direction = 1; pos.x = 0; pos.y = 0; if (env_t::menupos == MENU_BOTTOM) { direction = -1; pos.y = gfx->get_screen_size().h - gui->get_windowsize().h - env_t::iconsize.h; } // now we just open below (above), not caring for holes for (uint32 i=0; i 0) { pos.y = max(pos.y, wins[i].pos.y + wins[i].gui->get_windowsize().h); } else { pos.y = min(pos.y, wins[i].pos.y - gui->get_windowsize().h - D_TITLEBAR_HEIGHT); } } } if (env_t::menupos == MENU_RIGHT) { // right aligned pos.x = gfx->get_screen_size().w - gui->get_windowsize().w - env_t::iconsize.w; } win_clamp_xywh_position(gui, pos, gui->get_windowsize(), true); } else { // try to go next to mouse bar pos.x = get_mouse_pos().x - gui->get_windowsize().w / 2; pos.y = get_mouse_pos().y - gui->get_windowsize().h - gfx->get_tile_raster_width()/4; } } // make sure window is on screen win_clamp_xywh_position(gui, pos, gui->get_windowsize(), true); win.pos = pos; win.dirty = true; return wins.get_count(); } else { if( !( wt & w_do_not_delete ) ) { delete gui; } return -1; } } static int notify_top_win() { // mark new dirty scr_size size = wins.back().gui->get_windowsize(); gfx->mark_rect_dirty_wc( wins.back().pos.x - 1, wins.back().pos.y - 1, wins.back().pos.x + size.w + 2, wins.back().pos.y + size.h + 2 ); // -1, +2 for env_t::window_frame_active event_t ev; ev.ev_class = INFOWIN; ev.ev_code = WIN_TOP; ev.mouse_pos.x = 0; ev.mouse_pos.y = 0; ev.click_pos.x = 0; ev.click_pos.y = 0; ev.mouse_button_state = 0; void *old = inside_event_handling; inside_event_handling = wins.back().gui; wins.back().gui->infowin_event(&ev); inside_event_handling = old; return wins.get_count()-1; } int top_win(int win, bool keep_state ) { if( (uint32)win==wins.get_count()-1 ) { return win; } // already topped // mark old dirty scr_size size = wins.back().gui->get_windowsize(); gfx->mark_rect_dirty_wc( wins.back().pos.x - 1, wins.back().pos.y - 1, wins.back().pos.x + size.w + 2, wins.back().pos.y + size.h + 2 ); // -1, +2 for env_t::window_frame_active wins.back().dirty = true; simwin_t tmp = wins[win]; wins.remove_at(win); if( !keep_state ) { tmp.rollup = false; // make visible when topping } wins.append(tmp); return notify_top_win(); } static void save_win_position(const simwin_t &win) { if( win.magic_number < magic_max ) { if( scr_coord *k = old_win_pos.access(win.magic_number) ) { *k = win.pos; } else { old_win_pos.put( win.magic_number, win.pos ); } } } /* sometimes a window cannot destroyed while it is still handled; * in those cases it will added to kill list and it is only destructed * by this function */ static void process_kill_list() { bool needs_top = !kill_list.empty(); for(simwin_t & i : kill_list) { if (inside_event_handling != i.gui) { save_win_position(i); destroy_framed_win(&i); // we call this first, otherwise the focus may not be recognized wins.remove(i); } } kill_list.clear(); // top message always after destruction if( needs_top && wins.get_count()>1 ) { notify_top_win(); } } /** * Destroy a framed window */ static bool destroy_framed_win(simwin_t *wins) { bool r = true; // mark dirty const scr_size size = wins->gui->get_windowsize(); gfx->mark_rect_dirty_wc( wins->pos.x - 1, wins->pos.y - 1, wins->pos.x + size.w + 2, wins->pos.y + size.h + 2 ); // -1, +2 for env_t::window_frame_active // save windowsize for later save_windowsize(wins); // save pointer to gui window: might be modified in event handling, // or could be modified if wins points to value in kill_list and kill_list is modified! nasty surprise gui_frame_t* gui = wins->gui; if( gui ) { event_t ev; ev.ev_class = INFOWIN; ev.ev_code = WIN_CLOSE; ev.mouse_pos.x = 0; ev.mouse_pos.y = 0; ev.click_pos.x = 0; ev.click_pos.y = 0; ev.mouse_button_state = 0; void *old = inside_event_handling; inside_event_handling = gui; wins->gui->infowin_event(&ev); inside_event_handling = old; } if( (wins->wt&w_do_not_delete) == 0 ) { if( wins->gui == gui ) { // remove from kill list first, otherwise delete will be called again on that window for( uint32 j = 0; j < kill_list.get_count(); j++ ) { if( kill_list[j].gui == gui ) { kill_list.remove_at(j); break; } } delete gui; } else { // wins likely modified during event handling. Assume this was just a schedule window destroying itself, and top_win() therefore modifying wins. // return false to signal destroy_all_win() that wins was already modified. r = false; } } // set dirty flag to refill background if( wl ) { wl->set_background_dirty(); } tooltip_text = 0; return r; } bool destroy_win(const ptrdiff_t magic) { const gui_frame_t *gui = win_get_magic(magic); if(gui) { return destroy_win( gui ); } return false; } bool destroy_win(const gui_frame_t *gui) { for( uint i=0; i1 ) { notify_top_win(); } } return true; } } return false; } void destroy_all_win(bool destroy_sticky) { for( sint32 curWin = 0; curWin < (sint32)wins.get_count(); curWin++ ) { if( destroy_sticky || !wins[curWin].sticky ) { save_win_position(wins[curWin]); if( inside_event_handling == wins[curWin].gui ) { // only add this, if not already added kill_list.append_unique(wins[curWin]); // compact the window list wins.remove_at(curWin); curWin--; } else { if( destroy_framed_win(&wins[curWin]) ) { // compact the window list wins.remove_at(curWin); curWin--; } // else wins was already modified - assume by the schedule window closing itself during event handling } } } } void rollup_all_win() { bool all_rolldown_flag = true; // If any dialog is open, all rolldown will not be performed for ( sint32 curWin = 0; curWin < (sint32)wins.get_count(); curWin++ ) { if ( !wins[curWin].rollup ) { wins[curWin].rollup = true; gui_frame_t *gui = wins[curWin].gui; scr_size size = gui->get_windowsize(); gfx->mark_rect_dirty_wc( wins[curWin].pos.x, wins[curWin].pos.y, wins[curWin].pos.x+size.w, wins[curWin].pos.y+size.h ); all_rolldown_flag = false; } } if ( !all_rolldown_flag ) { wl->set_background_dirty(); } else if ( wins.get_count() ) { rolldown_all_win(); } } void rolldown_all_win() { for ( sint32 curWin = 0; curWin < (sint32)wins.get_count(); curWin++ ) { wins[curWin].rollup = false; gui_frame_t *gui = wins[curWin].gui; scr_size size = gui->get_windowsize(); gfx->mark_rect_dirty_wc( wins[curWin].pos.x, wins[curWin].pos.y, wins[curWin].pos.x+size.w, wins[curWin].pos.y+size.h ); } } void display_win(int win) { // ok, now process it gui_frame_t* comp = wins[win].gui; scr_size size = comp->get_windowsize(); // minimising flag if resize allowed wins[win].flags.size = (comp->get_resizemode() != 0); scr_coord pos = wins[win].pos; FLAGGED_PIXVAL title_color = (comp->get_titlecolor()&0xFFFF); FLAGGED_PIXVAL text_color = env_t::front_window_text_color; if( (unsigned)win!=wins.get_count()-1 ) { // not top => darker title_color = gfx->blend_colors(title_color, gfx->palette_lookup(COL_BLACK), env_t::bottom_window_darkness); text_color = env_t::bottom_window_text_color; } bool need_dragger = comp->get_resizemode() != gui_frame_t::no_resize; // HACK So draw will know if gadget is needed. wins[win].flags.help = ( comp->get_help_filename() != NULL ); if( wins[win].flags.title ) { win_draw_window_title(wins[win].pos, size, title_color, comp->get_name(), text_color, comp->get_weltpos(false), wins[win].gadget_state, wins[win].sticky, comp->is_weltpos(), wins[win].flags ); } if( wins[win].dirty ) { // not sure this is still a useful call gfx->mark_rect_dirty_wc( wins[win].pos.x, wins[win].pos.y, wins[win].pos.x+size.w+1, wins[win].pos.y+2 ); wins[win].dirty = false; } // mark top window, if requested if(env_t::window_frame_active && (unsigned)win==wins.get_count()-1) { if(!wins[win].rollup) { gfx->draw_box3d_clipped( wins[win].pos.x-1, wins[win].pos.y-1, size.w+2, size.h+2, title_color, title_color); } else { gfx->draw_box3d_clipped( wins[win].pos.x-1, wins[win].pos.y-1, size.w+2, D_TITLEBAR_HEIGHT + 2, title_color, title_color); } } if(!wins[win].rollup) { comp->draw(wins[win].pos, size); // draw dragger if(need_dragger) { win_draw_window_dragger( pos, size); } } } void display_all_win() { // first: empty kill list process_kill_list(); // check which window can set tooltip const sint16 x = get_mouse_pos().x; const sint16 y = get_mouse_pos().y; tooltip_element = NULL; for( uint32 i = wins.get_count(); i-- != 0; ) { if( (!wins[i].rollup && wins[i].gui->is_hit(x-wins[i].pos.x,y-wins[i].pos.y)) || (wins[i].rollup && x>=wins[i].pos.x && xget_windowsize().w && y>=wins[i].pos.y && ymap_rotate90(new_ysize); } } static void remove_old_win() { // Destroy (close) old window when life time expire for( int i=wins.get_count()-1; i>=0; i=min(i,(int)wins.get_count())-1 ) { if(wins[i].dauer > 0) { wins[i].dauer --; if(wins[i].dauer == 0) { destroy_win( wins[i].gui ); } } } } static inline void snap_check_distance( scr_coord_val *r, const scr_coord_val a, const scr_coord_val b ) { if( abs(a-b)<=env_t::window_snap_distance ) { *r = a; } } static void snap_check_win( const int win, scr_coord *r, const scr_coord from_pos, const scr_coord from_size, const scr_coord to_pos, const scr_coord to_size ) { bool resize; if( from_size==to_size && from_pos!=to_pos ) { // check if we're moving resize = false; } else if( from_size!=to_size && from_pos==from_pos ) { // or resizing the window resize = true; } else { return; // or nothing to do. } const int wins_count = wins.get_count(); for( int i=0; i<=wins_count; i++ ) { if( i==win ) { // Don't snap to self continue; } scr_coord other_pos; scr_coord other_size; if( i==wins_count ) { // Allow snap to screen edge const scr_size screen = gfx->get_screen_size(); other_pos.x = (env_t::menupos==MENU_LEFT)*env_t::iconsize.w; other_pos.y = (env_t::menupos==MENU_TOP)*env_t::iconsize.h + (env_t::menupos==MENU_BOTTOM)*win_get_statusbar_height(); other_size.x = screen.w - other_pos.x - (env_t::menupos==MENU_RIGHT)*env_t::iconsize.w; other_size.y = screen.h - win_get_statusbar_height()-env_t::iconsize.h; if( show_ticker ) { other_size.y -= TICKER_HEIGHT; } } else { // Snap to other window other_size = wins[i].gui->get_windowsize(); other_pos = wins[i].pos; if( wins[i].rollup ) { other_size.y = D_TITLEBAR_HEIGHT; } } // my bottom below other top and my top above other bottom ---- in same vertical band if( from_pos.y+from_size.y >= other_pos.y && from_pos.y <= other_pos.y+other_size.y ) { if( resize ) { // other right side and my new right side within snap snap_check_distance( &r->x, other_pos.x+other_size.x-from_pos.x, to_size.x ); // snap right - align right sides } else { // other right side and my new right side within snap snap_check_distance( &r->x, other_pos.x+other_size.x-from_size.x, to_pos.x ); // snap right - align right sides // other left side and my new left side within snap snap_check_distance( &r->x, other_pos.x, to_pos.x ); // snap left - align left sides } } // my new bottom below other top and my new top above other bottom ---- in same vertical band if( resize ) { if( from_pos.y+to_size.y>other_pos.y && from_pos.yx, other_pos.x-from_pos.x, to_size.x ); // snap right - align my right to other left } } else { if( to_pos.y+from_size.y>other_pos.y && to_pos.yx, other_pos.x-from_size.x, to_pos.x ); // snap right - align my right to other left // other right side and my new left within snap snap_check_distance( &r->x, other_pos.x+other_size.x, to_pos.x ); // snap left - align my left to other right } } // my right side right of other left side and my left side left of other right side ---- in same horizontal band if( from_pos.x+from_size.x>=other_pos.x && from_pos.x<=other_pos.x+other_size.x ) { if( resize ) { // other bottom and my new bottom within snap snap_check_distance( &r->y, other_pos.y+other_size.y-from_pos.y, to_size.y ); // snap down - align bottoms } else { // other bottom and my new bottom within snap snap_check_distance( &r->y, other_pos.y+other_size.y-from_size.y, to_pos.y ); // snap down - align bottoms // other top and my new top within snap snap_check_distance( &r->y, other_pos.y, to_pos.y ); // snap up - align tops } } // my new right side right of other left side and my new left side left of other right side ---- in same horizontal band if ( resize ) { if( from_pos.x+to_size.x>other_pos.x && from_pos.xy, other_pos.y-from_pos.y, to_size.y ); // snap down - align my bottom to other top } } else { if( to_pos.x+from_size.x>other_pos.x && to_pos.xy, other_pos.y-from_size.y, to_pos.y ); // snap down - align my bottom to other top // other bottom and my new top within snap snap_check_distance( &r->y, other_pos.y+other_size.y, to_pos.y ); // snap up - align my top to other bottom } } } } static void move_win(int win, event_t *ev) { const scr_coord mouse_from = ev->click_pos; const scr_coord mouse_to = ev->mouse_pos; const scr_coord from_pos = wins[win].pos; scr_coord from_size = scr_coord(wins[win].gui->get_windowsize().w,wins[win].gui->get_windowsize().h); if( wins[win].rollup ) { from_size.y = D_TITLEBAR_HEIGHT + 2; } scr_coord to_pos = wins[win].pos+(mouse_to-mouse_from); const scr_coord to_size = from_size; if( env_t::window_snap_distance>0 ) { snap_check_win( win, &to_pos, from_pos, from_size, to_pos, to_size ); } // CLIP(wert,min,max) win_clamp_xywh_position(wins[win].gui, to_pos, wins[win].gui->get_windowsize(), false); // delta is actual window movement. const scr_coord delta = to_pos - from_pos; wins[win].pos += delta; // need to mark all of old and new positions dirty. -1, +2 for env_t::window_frame_active gfx->mark_rect_dirty_wc( from_pos.x - 1, from_pos.y - 1, from_pos.x + from_size.x + 2, from_pos.y + from_size.y + 2 ); wins[win].dirty = true; // set dirty flag to refill background if(wl) { wl->set_background_dirty(); } change_drag_start( delta ); } static void resize_win(int win, event_t *ev) { event_t wev = *ev; wev.ev_class = WINDOW_RESIZE; wev.ev_code = 0; const scr_coord mouse_from = wev.click_pos; const scr_coord mouse_to = wev.mouse_pos; const scr_coord from_pos = wins[win].pos; const scr_coord from_size = scr_coord(wins[win].gui->get_windowsize().w,wins[win].gui->get_windowsize().h); const scr_coord to_pos = from_pos; scr_coord to_size = from_size+(mouse_to-mouse_from); if( env_t::window_snap_distance>0 ) { snap_check_win( win, &to_size, from_pos, from_size, to_pos, to_size ); } // since we may be smaller afterwards gfx->mark_rect_dirty_wc( from_pos.x - 1, from_pos.y - 1, from_pos.x + from_size.x + 2, from_pos.y + from_size.y + 2 ); // -1, +2 for env_t::window_frame_active // set dirty flag to refill background if(wl) { wl->set_background_dirty(); } // adjust event mouse scr_coord per snap wev.mouse_pos = wev.click_pos + to_size - from_size; wins[win].gui->infowin_event( &wev ); } // returns true, if gui is a open window handle static bool win_is_open(gui_frame_t *gui) { for(simwin_t const& i : wins) { if (i.gui == gui) { for(simwin_t const& j : kill_list) { if (j.gui == gui) { return false; } } return true; } } return false; } scr_coord const& win_get_pos(gui_frame_t const* const gui) { for( uint32 i = wins.get_count(); i-- != 0; ) { if( wins[i].gui == gui ) { return wins[i].pos; } } static scr_coord const bad(-1, -1); return bad; } void win_set_pos(gui_frame_t *gui, scr_coord new_pos) { for( uint32 i = wins.get_count(); i-- != 0; ) { if( wins[i].gui == gui ) { win_clamp_xywh_position(gui, new_pos, gui->get_windowsize(), true); wins[i].pos = new_pos; wins[i].dirty = true; return; } } } bool last_drag_is_caught = false; // since check_pos_win is processed before i.e. scrolling map // we do not want to catch the mouse, if we use it already void catch_dragging() { last_drag_is_caught = true; } /* * main window event handler */ bool check_pos_win(event_t *ev,bool modal) { static int is_resizing = -1; static int is_moving = -1; static bool is_dragging = false; bool swallowed = false; const int x = ev->ev_class==EVENT_MOVE?ev->mouse_pos.x:ev->click_pos.x; const int y = ev->ev_class==EVENT_MOVE?ev->mouse_pos.y:ev->click_pos.y; if( last_drag_is_caught ) { if( ev->ev_class == EVENT_DRAG ) { // somebody else drags already => do nothing return false; } if( ev->ev_class == EVENT_RELEASE ) { // we will handle dragging events again after this last_drag_is_caught = false; return false; } } // for the moment, no none events if (ev->ev_class == EVENT_NONE) { process_kill_list(); return false; } // we stop resizing once the user releases the button if( (is_resizing>=0 || is_moving>=0) && (IS_LEFTRELEASE(ev) || (ev->mouse_button_state&1)==0) ) { is_resizing = -1; is_moving = -1; if( IS_LEFTRELEASE(ev) ) { // should not proceed, otherwise the left release event will be fed to other components; // return true (i.e. event swallowed) to prevent propagation back to the main view return true; } } // disable any active tooltip upon mouse click by forcing expiration of tooltip duration if( ev->ev_class==EVENT_CLICK ) { tooltip_register_time = 0; } // click in main menu? const scr_size screen = gfx->get_screen_size(); scr_coord menuoffset((env_t::menupos == MENU_RIGHT) * (screen.w - env_t::iconsize.w), (env_t::menupos == MENU_BOTTOM) * (screen.h - env_t::iconsize.h) - D_TITLEBAR_HEIGHT); if (!tool_t::toolbar_tool.empty() && tool_t::toolbar_tool[0]->get_tool_selector() && (is_dragging || tool_t::toolbar_tool[0]->get_tool_selector()->is_hit(x-menuoffset.x, y-menuoffset.y)) && y > menuoffset.y+D_TITLEBAR_HEIGHT && (ev->ev_class >= EVENT_CLICK && ev->ev_class <= EVENT_DRAG) ) { // first finish dragging event_t wev = *ev; wev.move_origin(menuoffset); inside_event_handling = tool_t::toolbar_tool[0]; tool_t::toolbar_tool[0]->get_tool_selector()->infowin_event(&wev); inside_event_handling = NULL; if (!tool_t::toolbar_tool[0]->get_tool_selector()->is_hit(ev->mouse_pos.x, ev->mouse_pos.y)) { // find out if the toolbar should be move to another corner uint8 new_pos = 0; int mx = get_mouse_pos().x; // inclippe values needed int my = get_mouse_pos().y; if (my < env_t::iconsize.h) { new_pos = 1 << MENU_TOP; } if (my > screen.h- env_t::iconsize.h) { new_pos |= 1 << MENU_BOTTOM; } if (mx < env_t::iconsize.w) { new_pos |= 1 << MENU_LEFT; } if (mx > screen.w - env_t::iconsize.w) { new_pos |= 1 << MENU_RIGHT; } if (new_pos && new_pos!=(1<>= 1; rot++; } // single rotation only if (check_pos == 1 && rot!=env_t::menupos) { env_t::menupos = rot; world()->set_dirty(); // if no inside a modal from if (!modal) { // move all windows event_t* ev = new event_t(); ev->ev_class = EVENT_SYSTEM; ev->ev_code = SYSTEM_RELOAD_WINDOWS; queue_event(ev); } is_dragging = false; return true; } } } is_dragging = ev->ev_class != EVENT_RELEASE && ev->mouse_button_state>1; // swallow event return true; } // cursor event only go to top window (but not if rolled up) if( (IS_KEYDOWN(ev) || ev->ev_class == EVENT_STRING) && !wins.empty() ) { simwin_t &win = wins.back(); if( !win.rollup ) { inside_event_handling = win.gui; swallowed = win.gui->infowin_event(ev); } inside_event_handling = NULL; process_kill_list(); return swallowed; } // just move top window until button release if( is_moving>=0 && (unsigned)is_moving=0 && (unsigned)is_resizingev_class != EVENT_STRING && y > gfx->get_screen_size().h - win_get_statusbar_height() ) { // swallow event return true; } // swallow all other events in ticker (if there) if( !IS_KEYDOWN(ev) && ev->ev_class != EVENT_STRING && show_ticker && y > gfx->get_screen_size().h - win_get_statusbar_height() - TICKER_HEIGHT ) { if( IS_LEFTCLICK(ev) ) { ticker::process_click(x); } // swallow event return true; } // handle all the other events for( int i=wins.get_count()-1; i>=0 && !swallowed; i=min(i,(int)wins.get_count())-1 ) { if( wins[i].gui->is_hit( x-wins[i].pos.x, y-wins[i].pos.y ) ) { // all events in window are swallowed swallowed = true; inside_event_handling = wins[i].gui; // Top window first if( (int)wins.get_count()-1>i && IS_LEFTCLICK(ev) && (!wins[i].rollup || ev->click_pos.yget_help_filename() != NULL ); // Where Was It ? sint8 code = decode_gadget_boxes( ( & wins[i].flags ), wins[i].pos.x + (REVERSE_GADGETS?0:wins[i].gui->get_windowsize().w-D_GADGET_WIDTH), x ); if( code < SKIN_GADGET_COUNT ) { if( IS_LEFTCLICK(ev) ) { wins[i].gadget_state |= (1 << code); } else if( IS_LEFTRELEASE(ev) ) { wins[i].gadget_state &= ~(1 << code); if( ev->mouse_pos.y >= wins[i].pos.y && ev->mouse_pos.y < wins[i].pos.y+D_TITLEBAR_HEIGHT && decode_gadget_boxes( ( & wins[i].flags ), wins[i].pos.x + (REVERSE_GADGETS?0:wins[i].gui->get_windowsize().w-D_GADGET_WIDTH), ev->mouse_pos.x )==code ) { // do whatever needs to be done switch( code ) { case SKIN_GADGET_CLOSE : destroy_win(wins[i].gui); break; case SKIN_GADGET_MINIMIZE: // (Mathew Hounsell) ev->ev_class = WINDOW_MAKE_MIN_SIZE; ev->ev_code = 0; wl->set_background_dirty(); wins[i].gui->infowin_event( ev ); break; case SKIN_GADGET_HELP : help_frame_t::open_help_on( wins[i].gui->get_help_filename() ); break; case SKIN_BUTTON_PREVIOUS: ev->ev_class = WINDOW_CHOOSE_NEXT; ev->ev_code = PREV_WINDOW; // backward wins[i].gui->infowin_event( ev ); break; case SKIN_BUTTON_NEXT: ev->ev_class = WINDOW_CHOOSE_NEXT; ev->ev_code = NEXT_WINDOW; // forward wins[i].gui->infowin_event( ev ); break; case SKIN_GADGET_GOTOPOS: { // change position on map (or follow) koord3d k = wins[i].gui->get_weltpos(true); if( k!=koord3d::invalid ) { wl->get_viewport()->change_world_position( k ); wl->get_zeiger()->change_pos( k ); } } break; case SKIN_GADGET_NOTPINNED: wins[i].sticky = !wins[i].sticky; break; } } } } else { // Somewhere on the titlebar if (IS_LEFTDRAG(ev)) { i = top_win(i,false); move_win(i, ev); is_moving = i; } if(IS_RIGHTCLICK(ev) || IS_LEFTDBLCLK(ev) ) { wins[i].rollup ^= 1; gui_frame_t *gui = wins[i].gui; scr_size size = gui->get_windowsize(); gfx->mark_rect_dirty_wc( wins[i].pos.x, wins[i].pos.y, wins[i].pos.x+size.w, wins[i].pos.y+size.h ); if( wins[i].rollup ) { wl->set_background_dirty(); } } } // It has been handled so stop checking. break; } else { if(!wins[i].rollup) { // click in Window / Resize? //11-May-02 markus weber added scr_size size = wins[i].gui->get_windowsize(); // resizer hit ? const bool canresize = is_resizing>=0 || (ev->click_pos.x > wins[i].pos.x + size.w - D_DRAGGER_WIDTH && ev->click_pos.y > wins[i].pos.y + size.h - D_DRAGGER_HEIGHT); if((IS_LEFTCLICK(ev) || IS_LEFTDRAG(ev)) && canresize && wins[i].gui->get_resizemode()!=gui_frame_t::no_resize) { resize_win( i, ev ); is_resizing = i; } else { is_resizing = -1; // click in Window event_t wev = *ev; wev.move_origin(wins[i].pos); wins[i].gui->infowin_event( &wev ); } } else { swallowed = false; } } inside_event_handling = NULL; } } inside_event_handling = NULL; process_kill_list(); return swallowed; } void win_poll_event(event_t* const ev) { display_poll_event(ev); // main window resized if( ev->ev_class==EVENT_SYSTEM && ev->ev_code==SYSTEM_RESIZE ) { // main window resized gfx->on_window_resized( ev->new_window_size ); const scr_size screen = gfx->get_screen_size(); gui_theme_t::gui_buttons_per_row = max(2, min(4, screen.w / (D_BUTTON_WIDTH + D_H_SPACE + D_MARGIN_LEFT))); ticker::redraw(); tool_t::update_toolbars(); for( uint i = 0; iget_min_windowsize(), true ); } wl->set_dirty(); wl->get_viewport()->metrics_updated(); ev->ev_class = IGNORE_EVENT; // now see how much of the status text fits the display scr_coord_val time_width = gfx->calc_text_width(tick_to_string(wl->get_ticks())); scr_coord_val player_width = gfx->calc_text_width(wl->get_active_player()->get_name()); char buffer[128]; if (env_t::player_finance_display_account) { money_to_string(buffer, (double)world()->get_active_player()->get_finance()->get_account_balance() / 100.0); } else { money_to_string(buffer, (double)world()->get_active_player()->get_finance()->get_netwealth() / 100.0); } scr_coord_val money_width = gfx->calc_text_width(buffer); scr_coord_val position_width = gfx->calc_text_width(wl->get_zeiger()->get_pos().get_str()); // if too long shorten radically status_show_compact = (time_width + player_width + money_width + position_width) * 1.2 > ev->new_window_size.w; } // save and reload all windows (currently only used when a new theme is applied) else if( ev->ev_class==EVENT_SYSTEM && ev->ev_code==SYSTEM_RELOAD_WINDOWS ) { dr_chdir( env_t::user_dir ); loadsave_t dlg; if( dlg.wr_open( "dlgpos.xml", loadsave_t::xml_zipped, 1, "temp", SERVER_SAVEGAME_VER_NR ) == loadsave_t::FILE_STATUS_OK ) { // save all rdwr_all_win( &dlg ); dlg.close(); destroy_all_win( true ); if( dlg.rd_open( "dlgpos.xml" ) == loadsave_t::FILE_STATUS_OK ) { // and reload them ... rdwr_all_win( &dlg ); } } wl->set_dirty(); ev->ev_class = IGNORE_EVENT; ticker::redraw(); } else if( ev->ev_class==EVENT_SYSTEM && ev->ev_code==SYSTEM_THEME_CHANGED ) { // called when font is changed ev->mouse_pos.x = ev->mouse_pos.y = ev->click_pos.x = ev->click_pos.y = 0; for(simwin_t const& i : wins) { i.gui->infowin_event(ev); } ev->ev_class = IGNORE_EVENT; ticker::redraw(); } } uint16 win_get_statusbar_height() { return max(LINESPACE + 2, 15); } // finally updates the display void win_display_flush(double konto) { const scr_size screen = gfx->get_screen_size(); // display main menu tool_selector_t *main_menu = tool_t::toolbar_tool[0]->get_tool_selector(); scr_coord menu_pos(0,0); scr_size menu_size(screen.w, env_t::iconsize.h); scr_rect clip_rr(0, env_t::iconsize.h, screen.w, screen.h - env_t::iconsize.h); switch (env_t::menupos) { case MENU_TOP: // pos default (see above) // size default // rect default break; case MENU_BOTTOM: menu_pos = scr_coord(0, screen.h - env_t::iconsize.h); // size default clip_rr.y = 0; break; case MENU_LEFT: // pos default (see above) menu_size = scr_size(env_t::iconsize.w, screen.h - win_get_statusbar_height()-show_ticker*TICKER_HEIGHT); clip_rr = scr_rect(env_t::iconsize.h, 0, screen.w - env_t::iconsize.w, screen.h); break; case MENU_RIGHT: menu_pos.x = screen.h - env_t::iconsize.w; menu_size = scr_size(env_t::iconsize.w, screen.h - win_get_statusbar_height()-show_ticker*TICKER_HEIGHT ); clip_rr = scr_rect(0, 0, screen.w - env_t::iconsize.w, screen.h); break; } gfx->set_clip_rect( menu_pos.x, menu_pos.y, menu_size.w, menu_size.h CLIP_NUM_DEFAULT, false); if( skinverwaltung_t::toolbar_background && skinverwaltung_t::toolbar_background->get_image_id(0) != IMG_EMPTY ) { const image_id back_img = skinverwaltung_t::toolbar_background->get_image_id(0); gfx->fit_img_to_width( back_img, env_t::iconsize.w ); stretch_map_t imag = { { IMG_EMPTY, IMG_EMPTY, IMG_EMPTY }, { IMG_EMPTY, back_img, IMG_EMPTY }, { IMG_EMPTY, IMG_EMPTY, IMG_EMPTY } }; gfx->draw_stretch_map(imag, scr_rect(menu_pos, menu_size)); } else { gfx->draw_rect( menu_pos.x, menu_pos.y, menu_size.w, menu_size.h, gfx->palette_lookup(MN_GREY2), false ); } // .. extra logic to enable tooltips tooltip_element = main_menu->is_hit( get_mouse_pos().x-menu_pos.x, get_mouse_pos().y-menu_pos.y) ? main_menu : NULL; void *old_inside_event_handling = inside_event_handling; inside_event_handling = main_menu; menu_pos.y -= D_TITLEBAR_HEIGHT; main_menu->draw(menu_pos, menu_size); inside_event_handling = old_inside_event_handling; gfx->set_clip_rect(clip_rr.x, clip_rr.y, clip_rr.w, clip_rr.h CLIP_NUM_DEFAULT, false); if(show_ticker || !ticker::empty()) { if (!show_ticker || wl->is_dirty()) { ticker::redraw(); } show_ticker = !ticker::empty(); ticker::draw(); } else { show_ticker = false; } if( skinverwaltung_t::compass_iso && env_t::compass_screen_position ) { const image_id img = skinverwaltung_t::compass_iso->get_image_id(wl->get_settings().get_rotation()); const scr_rect area = { D_MARGIN_LEFT, env_t::iconsize.h+D_MARGIN_TOP, screen.w - 2*4, screen.h - env_t::iconsize.h - D_MARGIN_TOP - D_MARGIN_BOTTOM - win_get_statusbar_height() - (TICKER_HEIGHT)*show_ticker }; gfx->draw_img_aligned(img, area, env_t::compass_screen_position, false ); } { // clip windows to avoid drawing into menu, ticker, or status-bar PUSH_CLIP(clip_rr.x, clip_rr.y+(env_t::menupos==MENU_BOTTOM ? win_get_statusbar_height() + show_ticker*TICKER_HEIGHT:0), clip_rr.w, clip_rr.h - (TICKER_HEIGHT)*show_ticker - win_get_statusbar_height() ); display_all_win(); remove_old_win(); if(env_t::show_tooltips) { // check if there is a tooltip to display if( tooltip_text && *tooltip_text ) { // display tooltip when current owner is invalid or when it is within visible duration uint32 elapsed_time; if( !tooltip_owner || ((elapsed_time=dr_time()-tooltip_register_time)>env_t::tooltip_delay && elapsed_time<=env_t::tooltip_delay+env_t::tooltip_duration) ) { const sint16 width = gfx->calc_text_width(tooltip_text)+(LINESPACE/2); scr_coord pos{ tooltip_xpos, tooltip_ypos }; win_clamp_xywh_position( NULL, pos, scr_size( width, (LINESPACE*9)/7 ), true ); gfx->draw_textbox3d_clipped( pos.x, pos.y, env_t::tooltip_color, env_t::tooltip_textcolor, tooltip_text, true CLIP_NUM_DEFAULT); if(wl) { wl->set_background_dirty(); } } } else if(!static_tooltip_text.empty()) { const sint16 width = gfx->calc_text_width(static_tooltip_text.c_str())+ (LINESPACE/2); scr_coord pos = get_mouse_pos(); win_clamp_xywh_position( NULL, pos, scr_size(width, (LINESPACE*9)/7), true); gfx->draw_textbox3d_clipped(pos.x, pos.y, env_t::tooltip_color, env_t::tooltip_textcolor, static_tooltip_text.c_str(), true CLIP_NUM_DEFAULT); if(wl) { wl->set_background_dirty(); } } // reset owner and group if no tooltip has been registered if( !tooltip_text ) { tooltip_owner = 0; tooltip_group = 0; } // clear tooltip text to avoid sticky tooltips tooltip_text = 0; } POP_CLIP(); if(!wl) { // no infos during loading etc return; } } char const* time; if (status_show_compact && env_t::show_month >= env_t::DATE_FMT_JAPANESE && env_t::show_month <= env_t::DATE_FMT_GERMAN) { // omit season from the date string env_t::show_month += 3; time = tick_to_string(wl->get_ticks()); env_t::show_month -= 3; } else { time = tick_to_string(wl->get_ticks()); } // statusbar background scr_coord_val const status_bar_height = win_get_statusbar_height(); scr_coord_val const status_bar_y =env_t::menupos == MENU_BOTTOM ? 0 : screen.h - status_bar_height; scr_coord_val const status_bar_text_y = status_bar_y + (status_bar_height - LINESPACE) / 2; scr_coord_val const status_bar_icon_y = status_bar_y + (status_bar_height - 15) / 2; gfx->set_clip_rect( 0, 0, screen.w, screen.h CLIP_NUM_DEFAULT, false); gfx->draw_rect(0, status_bar_y - 1, screen.w, 1, SYSCOL_STATUSBAR_DIVIDER, false); gfx->draw_rect(0, env_t::menupos == MENU_BOTTOM ? status_bar_height : status_bar_y - 1, screen.w, 1, SYSCOL_STATUSBAR_DIVIDER, false); gfx->draw_rect(0, status_bar_y, screen.w, status_bar_height, SYSCOL_STATUSBAR_BACKGROUND, false); bool tooltip_check = env_t::menupos == MENU_BOTTOM ? get_mouse_pos().y < status_bar_height : get_mouse_pos().y > status_bar_y; if( tooltip_check ) { tooltip_xpos = get_mouse_pos().x; tooltip_ypos = env_t::menupos == MENU_BOTTOM ? status_bar_height + 10 + TICKER_HEIGHT * show_ticker : status_bar_y - 10 - TICKER_HEIGHT * show_ticker; } scr_coord_val left_border = 2; // season icon gfx->draw_color_img( skinverwaltung_t::seasons_icons->get_image_id(wl->get_season()), left_border, status_bar_icon_y, 0, false, true CLIP_NUM_DEFAULT); if( tooltip_check && tooltip_xpos<14 ) { static char const* const seasons[] = { "q2", "q3", "q4", "q1" }; tooltip_text = translator::translate(seasons[wl->get_season()]); tooltip_check = false; } left_border += 14; scr_coord_val right_border = screen.w - 4; // shown if timeline game if( wl->use_timeline() && skinverwaltung_t::timelinesymbol ) { right_border -= 14; gfx->draw_color_img( skinverwaltung_t::timelinesymbol->get_image_id(0), right_border, status_bar_icon_y, 0, false, true CLIP_NUM_DEFAULT); if( tooltip_check && tooltip_xpos>=right_border ) { tooltip_text = translator::translate("timeline"); tooltip_check = false; } } // shown if connected if( env_t::networkmode && skinverwaltung_t::networksymbol ) { right_border -= 14; gfx->draw_color_img( skinverwaltung_t::networksymbol->get_image_id(0), right_border, status_bar_icon_y, 0, false, true CLIP_NUM_DEFAULT); if( tooltip_check && tooltip_xpos>=right_border ) { tooltip_text = translator::translate("Connected with server"); tooltip_check = false; } } // put pause icon if( wl->is_paused() && skinverwaltung_t::pausesymbol ) { right_border -= 14; gfx->draw_color_img( skinverwaltung_t::pausesymbol->get_image_id(0), right_border, status_bar_icon_y, 0, false, true CLIP_NUM_DEFAULT); if( tooltip_check && tooltip_xpos>=right_border ) { tooltip_text = translator::translate("GAME PAUSED"); tooltip_check = false; } } // put fast forward icon if( wl->is_fast_forward() && skinverwaltung_t::fastforwardsymbol ) { right_border -= 14; gfx->draw_color_img( skinverwaltung_t::fastforwardsymbol->get_image_id(0), right_border, status_bar_icon_y, 0, false, true CLIP_NUM_DEFAULT); if( tooltip_check && tooltip_xpos>=right_border ) { tooltip_text = translator::translate("Fast forward"); tooltip_check = false; } } right_border -= 4; koord3d pos = wl->get_zeiger()->get_pos(); static cbuffer_t info; info.clear(); if( pos!=koord3d::invalid ) { info.printf( "(%s)", pos.get_str() ); } if( skinverwaltung_t::timelinesymbol==NULL ) { info.printf( " %s", translator::translate(wl->use_timeline()?"timeline":"no timeline") ); } if(wl->show_distance!=koord3d::invalid && wl->show_distance!=pos) { info.printf("-(%d,%d)", wl->show_distance.x-pos.x, wl->show_distance.y-pos.y ); } if( !env_t::networkmode ) { // time multiplier text if(wl->is_fast_forward()) { info.printf(" %s(T~%1.2f)", skinverwaltung_t::fastforwardsymbol?"":">> ", wl->get_simloops()/50.0 ); } else if(!wl->is_paused()) { if (wl->get_time_multiplier() != 16) { info.printf(" (T=%1.2f)", wl->get_time_multiplier() / 16.0); } } else if( skinverwaltung_t::pausesymbol==NULL ) { info.printf( " %s", translator::translate("GAME PAUSED") ); } } #ifdef DEBUG if( env_t::verbose_debug>1 ) { if( haltestelle_t::get_rerouting_status()==RECONNECTING ) { info.append( " +" ); } else if( haltestelle_t::get_rerouting_status()==REROUTING ) { info.append( " *" ); } if( skinverwaltung_t::compass_iso == NULL && wl->get_settings().get_rotation() ) { static const char *compass_dir[4] = { "North", "East", "South", "West" }; info.append( " " ); info.append( translator::translate( compass_dir[ 4-wl->get_settings().get_rotation() ] ) ); } } #endif left_border += 4 + gfx->draw_text(left_border, status_bar_text_y, time, ALIGN_LEFT, SYSCOL_STATUSBAR_TEXT, true); right_border -= 4 + gfx->draw_text(right_border, status_bar_text_y, info, ALIGN_RIGHT, SYSCOL_STATUSBAR_TEXT, true); /* Since the visual center jumps left and right with proportional fonts, we quantisze by 16 pixel */ left_border = (left_border + 15) & 0xFFFFFFF0ul; right_border = (right_border - 15) & 0xFFFFFFF0ul; if(wl->get_active_player()) { char buffer[256]; scr_coord_val textwidth_pl = 0; textwidth_pl = D_H_SPACE + gfx->calc_text_width(wl->get_active_player()->get_name()); if (status_show_compact) { number_to_string_fit(buffer, konto, 0, 10); strcat(buffer, "$"); } else { money_to_string(buffer, konto); } scr_coord_val textwidth_mn = (gfx->calc_text_width(buffer) + 15) & 0xFFFFFFF0ul; if (textwidth_mn + textwidth_pl < right_border - left_border) { // everything fits left_border += ( (right_border - left_border) - (textwidth_mn + textwidth_pl) ) / 2; left_border += D_H_SPACE + gfx->draw_text(left_border, status_bar_text_y, wl->get_active_player()->get_name(), ALIGN_LEFT, PLAYER_FLAG | gfx->palette_lookup(wl->get_active_player()->get_player_color1() + env_t::gui_player_color_dark), true); gfx->draw_text(left_border, status_bar_text_y, buffer, ALIGN_LEFT, konto >= 0.0 ? MONEY_PLUS : MONEY_MINUS, true); } else { // no space => only money status_show_compact = true; // make sure the seasons are omitted next time scr_coord_val width_left = (right_border - left_border - textwidth_mn); if (width_left > 50) { scr_rect r(left_border, status_bar_text_y, width_left-D_H_SPACE, LINESPACE); gfx->draw_text_ellipsis(r, wl->get_active_player()->get_name(), ALIGN_LEFT, PLAYER_FLAG | gfx->palette_lookup(wl->get_active_player()->get_player_color1() + env_t::gui_player_color_dark), true); left_border += width_left; // normal color gfx->draw_text(left_border, status_bar_text_y, buffer, ALIGN_LEFT, konto >= 0.0 ? MONEY_PLUS : MONEY_MINUS, true); } else { // just money in player color, no player name if (width_left > 0) { left_border += (right_border - left_border - textwidth_mn) / 2; } else { // shortest possible money strong number_to_string_fit(buffer, konto, 0, 5); strcat(buffer, "$"); } gfx->draw_text(left_border, status_bar_text_y, buffer, ALIGN_LEFT, konto >= 0.0 ? gfx->palette_lookup(wl->get_active_player()->get_player_color1() + env_t::gui_player_color_dark) : MONEY_MINUS, true); } } } } void win_set_world(karte_t *world) { wl = world; // remove all save window positions old_win_pos.clear(); } void win_redraw_world() { if(wl) { wl->set_dirty(); } } bool win_change_zoom_factor(bool magnify) { const bool result = magnify ? gfx->zoom_factor_up() : gfx->zoom_factor_down(); if(magnify && wl->is_step_mode_normal()) { wl->reset_timer(); } wl->get_viewport()->metrics_updated(); return result; } void win_load_font(const char *fname, uint8 fontsize) { bool force_reload = fontsize != env_t::fontsize; env_t::fontsize = fontsize; if (gfx->load_font(fname, force_reload) ) { // successfull gui_theme_t::themes_init( env_t::default_theme, false, false ); event_t *ev = new event_t(); ev->ev_class = EVENT_SYSTEM; ev->ev_code = SYSTEM_THEME_CHANGED; queue_event( ev ); } else { // restore old font gfx->load_font(env_t::fontname.c_str(), true); } win_redraw_world(); } /** * Sets the tooltip to display. * Has to be called from within gui_frame_t::draw * * @param pos * @param text * @param group * @param owner : owner==NULL disables timing (initial delay and visible duration) */ void win_set_tooltip(scr_coord pos, const char *text, const void *const owner, const void *const group) { // check whether the right window will set the tooltip if(inside_event_handling != tooltip_element ) { return; } // must be set every time as win_display_flush() will reset them tooltip_text = text; // update ownership if changed if( owner!=tooltip_owner ) { tooltip_owner = owner; // update register time only if owner is valid if( owner ) { const uint32 current_time = dr_time(); if( group && group==tooltip_group ) { // case : same group const uint32 elapsed_time = current_time - tooltip_register_time; const uint32 threshold = env_t::tooltip_delay - (env_t::tooltip_delay>>2); // 3/4 of delay if( elapsed_time>threshold && elapsed_time<=env_t::tooltip_delay+env_t::tooltip_duration ) { // case : threshold was reached and duration not expired -> delay time is reduced to 1/4 tooltip_register_time = current_time - threshold; } else { // case : either before threshold or duration expired tooltip_register_time = current_time; } } else { // case : owner has no associated group or group is different -> simply reset to current time tooltip_group = group; tooltip_register_time = current_time; } } else { // no owner to associate with a group even if the group is valid tooltip_group = 0; } } if (text) { const scr_size tt_size = scr_size(gfx->calc_text_width(text), LINESPACE + 2); win_clamp_xywh_position(NULL, pos, tt_size, true); pos.y += LINESPACE / 2 + 1; } tooltip_xpos = pos.x; tooltip_ypos = pos.y; } /** * Sets the tooltip to display. */ void win_set_static_tooltip(const char *text) { static_tooltip_text = text ? text : ""; } // shows a modal dialoge void modal_dialogue(gui_frame_t* gui, ptrdiff_t magic, karte_t* welt, bool (*quit)(), bool dismissible) { const scr_size screen = gfx->get_screen_size(); if (screen.w == 0) { dbg->error("modal_dialogue", "called without a display driver => nothing will be shown!"); env_t::quit_simutrans = true; // cannot handle this! return; } // switch off autosave sint32 old_autosave = env_t::autosave; env_t::autosave = 0; event_t ev; create_win(scr_coord(0,0), gui, w_info, magic); scr_coord pos{ (screen.w - gui->get_windowsize().w) / 2, (screen.h - gui->get_windowsize().h) / 2 }; win_clamp_xywh_position(gui, pos, gui->get_windowsize(), true); wins[wins[0].gui!=gui].pos = pos; if (welt) { welt->set_pause(false); welt->reset_interaction(); welt->reset_timer(); set_random_mode(MODAL_RANDOM); const uint32 ms_per_frame = 1000 / env_t::fps; const uint32 sync_steps_per_step = 5; // env_t::network_frames_per_step uint32 frame_start_time; uint32 sync_steps_until_step = sync_steps_per_step; while (gui && win_is_open(gui) && !env_t::quit_simutrans && !quit()) { frame_start_time = dr_time(); do { DBG_DEBUG4("modal_dialogue", "calling win_poll_event"); win_poll_event(&ev); if (ev.ev_class == EVENT_SYSTEM && ev.ev_code == SYSTEM_QUIT) { welt->stop(true); break; } win_clamp_xywh_position(NULL, ev.mouse_pos, scr_size(1, 1), false); win_clamp_xywh_position(NULL, ev.mouse_pos, scr_size(1, 1), false); if (IS_KEYDOWN(&ev) && ev.ev_code == SIM_KEYCODE_F1) { if (gui_frame_t* win = win_get_top()) { if (const char* helpfile = win->get_help_filename()) { help_frame_t::open_help_on(helpfile); continue; } } } if(dismissible && gui){ if ( (IS_KEYDOWN(&ev)) || (ev.ev_class == EVENT_CLICK && !gui->is_hit(ev.click_pos.x - pos.x, ev.click_pos.y - pos.y)) ) { destroy_win(gui); gui = NULL; } } DBG_DEBUG4("modal_dialogue", "calling check_pos_win"); check_pos_win(&ev, true); } while( ev.ev_class != EVENT_NONE && dr_time()-frame_start_time < 50 ); const uint32 delta_t = (ms_per_frame * welt->get_time_multiplier()) / 16; DBG_DEBUG4("modal_dialogue", "calling welt->sync_step(%u)", delta_t); welt->sync_step(delta_t); welt->display(delta_t); if (--sync_steps_until_step == 0) { DBG_DEBUG4("modal_dialogue", "calling welt->step"); intr_disable(); welt->step(); intr_enable(); sync_steps_until_step = sync_steps_per_step; } const uint32 next_frame_start_time = frame_start_time + ms_per_frame; const uint32 now = dr_time(); if (now < next_frame_start_time) { dr_sleep(next_frame_start_time - now); } } clear_random_mode(MODAL_RANDOM); } else { gfx->set_cursor_visible(true); gfx->set_show_load_cursor(false); gfx->draw_rect(0, 0, screen.w, screen.h, gfx->palette_lookup(COL_BLACK), true); while (gui && win_is_open(gui) && !env_t::quit_simutrans && !quit()) { // do not move, do not close it! // check for events again after waiting if (quit()) { break; } dr_prepare_flush(); gui->draw(win_get_pos(gui), gui->get_windowsize()); dr_flush(); // some backends (etc. SDL2) tend to flood the queue with events. Hence we need to process them all quickly uint32 frame_start_time = dr_time(); do { display_poll_event(&ev); if (ev.ev_class == EVENT_SYSTEM) { if (ev.ev_code == SYSTEM_RESIZE) { // main window resized gfx->on_window_resized(ev.new_window_size); scr_coord pos{ (screen.w - gui->get_windowsize().w) / 2, (screen.h - gui->get_windowsize().h) / 2 }; win_clamp_xywh_position(gui, pos, gui->get_windowsize(), true); wins[wins[0].gui != gui].pos = pos; dr_prepare_flush(); gfx->draw_rect(0, 0, ev.new_window_size.w, ev.new_window_size.h, gfx->palette_lookup(COL_BLACK), true); gui->draw(win_get_pos(gui), gui->get_windowsize()); dr_flush(); } else if (ev.ev_code == SYSTEM_QUIT) { // no world yet => simple quit env_t::quit_simutrans = true; break; } } else { // other events check_pos_win(&ev,true); } // however, after 50ms we do a redraw ... } while (ev.ev_class != EVENT_NONE && dr_time() - frame_start_time < 50); // sleep the rest of the time uint32 end_time = dr_time(); if (end_time - frame_start_time < 50) { dr_sleep(50 - (end_time - frame_start_time)); } } gfx->set_show_load_cursor(true); dr_prepare_flush(); gfx->draw_rect(0, 0, screen.w, screen.h, gfx->palette_lookup(COL_BLACK), true); dr_flush(); } // just trigger not another following window => wait for button release if (IS_LEFTCLICK(&ev)) { do { display_poll_event(&ev); } while (!IS_LEFTRELEASE(&ev)); } // restore autosave env_t::autosave = old_autosave; } simutrans-124.5/src/simutrans/gui/simwin.h000066400000000000000000000145721520675460500206540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SIMWIN_H #define GUI_SIMWIN_H #include // for ptrdiff_t #include "../simtypes.h" #include "../simconst.h" #include "../display/scr_coord.h" /* * The function implements a WindowManager 'Object' * There's only one WindowManager */ class karte_t; class scr_coord; class loadsave_t; class gui_frame_t; class gui_component_t; struct event_t; /* Types for the window */ enum wintype { w_info = 1 << 0, // A info window w_do_not_delete = 1 << 1, // A window whose GUI object should not be deleted on close w_no_overlap = 1 << 2, // try to place it below a previous window with the same flag w_time_delete = 1 << 3 // deletion after MESG_WAIT has elapsed }; ENUM_BITSET(wintype) enum magic_numbers { magic_none = -1, magic_reserved = 0, // from here on, delete second 'new'-ed object in create_win magic_settings_frame_t, magic_sprachengui_t, magic_themes, magic_welt_gui_t, magic_climate, magic_reliefmap, magic_farbengui_t, magic_color_gui_t, magic_ki_kontroll_t, magic_optionen_gui_t, magic_sound_kontroll_t, magic_load_t, magic_load_scenario, magic_load_script, magic_save_t, magic_UNUSED_railtools, magic_UNUSED_monorailtools, magic_UNUSED_tramtools, // Dario: Tramway magic_UNUSED_roadtools, magic_UNUSED_shiptools, magic_UNUSED_airtools, magic_UNUSED_specialtools, magic_UNUSED_listtools, magic_UNUSED_edittools, magic_UNUSED_slopetools, magic_UNUSED_halt_list_t, magic_UNUSED_label_frame, magic_city_info_t, magic_citylist_frame_t, magic_mainhelp, // player dependent stuff => 16 times present magic_finances_t, magic_convoi_list = magic_finances_t + MAX_PLAYER_COUNT, magic_convoi_list_filter = magic_convoi_list + MAX_PLAYER_COUNT, magic_line_list = magic_convoi_list_filter + MAX_PLAYER_COUNT, magic_halt_list = magic_line_list + MAX_PLAYER_COUNT, magic_line_management_t = magic_halt_list + MAX_PLAYER_COUNT, magic_ai_options_t = magic_line_management_t + MAX_PLAYER_COUNT, magic_ai_selector = magic_ai_options_t + MAX_PLAYER_COUNT, magic_pwd_t = magic_ai_selector + MAX_PLAYER_COUNT, magic_jump = magic_pwd_t + MAX_PLAYER_COUNT, magic_headquarter = magic_jump + MAX_PLAYER_COUNT, // normal stuff magic_curiositylist, magic_factorylist, magic_goodslist, magic_messageframe, magic_message_options, magic_scenario_frame, magic_scenario_info, magic_edit_factory, magic_edit_attraction, magic_edit_house, magic_edit_tree, magic_bigger_map, magic_labellist, magic_station_building_select, magic_server_frame_t, magic_pakset_info_t, magic_line_schedule_rdwr_dummy, // only used to save/load line schedules magic_motd, magic_factory_info, // only used to load/save magic_font, magic_soundfont, // only with USE_FLUIDSYNTH_MIDI magic_edit_groundobj, // magic numbers with big jumps between them magic_convoi_info, magic_UNUSED_convoi_detail = magic_convoi_info + 0x10000, // unused range magic_halt_info = magic_UNUSED_convoi_detail + 0x10000, magic_UNUSED_halt_detail = magic_halt_info + 0x10000, // unused range magic_toolbar = magic_UNUSED_halt_detail + 0x10000, magic_script_error = magic_toolbar + 0x100, magic_haltlist_filter, magic_depot, // only used to load/save magic_depotlist = magic_depot + MAX_PLAYER_COUNT, magic_vehiclelist = magic_depotlist + MAX_PLAYER_COUNT, magic_pakinstall, magic_chatframe, magic_player_ranking, magic_script_generator, // only during saving magic_max }; // Holding time for auto-closing windows #define MESG_WAIT 80 // windows with a valid id can be saved and restored void rdwr_all_win(loadsave_t *file); // save windowsizes in settings void rdwr_win_settings(loadsave_t *file); // returns the client area and offset scr_rect win_get_max_window_area(); void win_clamp_xywh_position(gui_frame_t *gui, scr_coord &pos, bool move_to_full_view); int create_win(gui_frame_t *gui, wintype type, ptrdiff_t magic); int create_win(scr_coord pos, gui_frame_t *gui, wintype type, ptrdiff_t magic); // call to avoid the main menu getting mouse events while dragging void catch_dragging(); bool check_pos_win(event_t*,bool modal); scr_coord const& win_get_pos(gui_frame_t const*); void win_set_pos(gui_frame_t *window, scr_coord new_pos); gui_frame_t *win_get_top(); // returns the focused component of the top window gui_component_t *win_get_focus(); // true, if the focus is currently in a text field bool win_is_textinput(); uint32 win_get_open_count(); gui_frame_t* win_get_index(uint32 i); // returns the window (if open) otherwise zero gui_frame_t *win_get_magic(ptrdiff_t magic); // sets the magic of a gui_frame_t (needed during reload of windows) bool win_set_magic( gui_frame_t *gui, ptrdiff_t magic ); /** * Checks if a window is a top level window */ bool win_is_top(const gui_frame_t *ig); // return true if actually window was destroyed (or marked for destruction) bool destroy_win(const gui_frame_t *ig); bool destroy_win(const ptrdiff_t magic); void destroy_all_win(bool destroy_sticky); void rollup_all_win(); void rolldown_all_win(); bool top_win(const gui_frame_t *ig, bool keep_rollup=false ); void display_all_win(); void win_rotate90( sint16 new_size ); void win_display_flush(double konto); // draw the frame and all windows uint16 win_get_statusbar_height(); void win_poll_event(event_t*); bool win_change_zoom_factor(bool magnify); /** * Sets the world this window manager is attached to. */ void win_set_world(karte_t *world); /** * Forces the redraw of the world on next frame. */ void win_redraw_world(); /** * Loads new font. Notifies gui's, world. */ void win_load_font(const char *fname, uint8 fontsize); /** * Sets the tooltip to display. * @param owner : owner==NULL disables timing (initial delay and visible duration) */ void win_set_tooltip(scr_coord pos, const char *text, const void *const owner = NULL, const void *const group = NULL); /** * Sets a static tooltip that follows the mouse * *MUST* be explicitly unset! */ void win_set_static_tooltip(const char *text); // shows a modal dialoge (blocks other interaction) void modal_dialogue(gui_frame_t* gui, ptrdiff_t magic, karte_t* welt, bool (*quit)(), bool dismissible = false); #endif simutrans-124.5/src/simutrans/gui/sound_frame.cc000066400000000000000000000173351520675460500220060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* * Dialog for sound settings */ #include "sound_frame.h" #include "../simsound.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "components/gui_divider.h" #ifdef USE_FLUIDSYNTH_MIDI #include "loadsoundfont_frame.h" #endif #define L_KNOB_SIZE (32) void sound_frame_t::update_song_name() { const int current_midi_index = get_current_midi(); std::string credits = ""; if( current_midi_index < 0 ) { song_name_label.buf().printf( translator::translate("Music playing disabled/not available") ); } #ifdef USE_FLUIDSYNTH_MIDI else if( strcmp( env_t::soundfont_filename.c_str(), "Error") == 0 ){ song_name_label.buf().printf( translator::translate("Soundfont not found. Please select a soundfont below.") ); } #endif else { midi_info_t current_midi = sound_get_midi_info( current_midi_index ); song_name_label.buf().printf("%d - %s", current_midi_index + 1, current_midi.title.c_str() ); if ( current_midi.composer != "-" ) { credits.append( translator::translate("Composed by") ); credits.append(" " + current_midi.composer + " "); if( current_midi.arranger != "-" ) { credits.append( translator::translate("and arranged by") ); credits.append( " " + current_midi.arranger ); } } } song_name_label.update(); song_name_label.set_size( song_name_label.get_min_size() ); song_credits_label.buf().printf(credits.c_str()); song_credits_label.update(); song_credits_label.set_size( song_credits_label.get_min_size() ); // Loadsoundfont dialog may unmute us, update mute status music_mute_button.pressed = midi_get_mute(); previous_song_button.enable( !music_mute_button.pressed ); next_song_button.enable( !music_mute_button.pressed ); } const char *specific_volume_names[ MAX_SOUND_TYPES ] = { "TOOL_SOUND", "TRAFFIC_SOUND", "AMBIENT_SOUND", "FACTORY_SOUND", "CROSSING_SOUND", "CASH_SOUND" }; sound_frame_t::sound_frame_t() : gui_frame_t( translator::translate("Sound settings") ), sound_volume_scrollbar(scrollbar_t::horizontal), music_volume_scrollbar(scrollbar_t::horizontal) { set_table_layout(1,0); sound_mute_button.init( button_t::square_state, "mute sound"); sound_mute_button.pressed = sound_get_mute(); sound_mute_button.add_listener( this ); add_component(&sound_mute_button); add_table(2,0); { // Sound volume label new_component( "Sound volume:" ); sound_volume_scrollbar.set_knob( L_KNOB_SIZE, 255 + L_KNOB_SIZE ); sound_volume_scrollbar.set_knob_offset( sound_get_global_volume() ); sound_volume_scrollbar.set_scroll_discrete( false ); sound_volume_scrollbar.add_listener( this ); add_component( &sound_volume_scrollbar ); new_component( "Sound range:" ); sound_range.set_value( env_t::sound_distance_scaling); sound_range.set_limits( 1, 32 ); sound_range.add_listener(this); // sound_range.set_tooltip( "Lower values mean more local sounds" ) add_component(&sound_range); for( int i = 0; i < MAX_SOUND_TYPES; i++ ) { new_component( specific_volume_names[i] ); specific_volume_scrollbar[ i ] = new scrollbar_t( scrollbar_t::horizontal ); specific_volume_scrollbar[i]->set_knob( L_KNOB_SIZE, 255 + L_KNOB_SIZE ); specific_volume_scrollbar[i]->set_knob_offset( sound_get_specific_volume((sound_type_t)i) ); specific_volume_scrollbar[i]->set_scroll_discrete( false ); specific_volume_scrollbar[i]->add_listener( this ); add_component( specific_volume_scrollbar[i] ); } } end_table(); new_component(); // Music music_mute_button.init( button_t::square_state, "disable midi"); music_mute_button.pressed = midi_get_mute(); music_mute_button.add_listener( this ); #ifdef USE_FLUIDSYNTH_MIDI if( strcmp( env_t::soundfont_filename.c_str(), "Error" ) == 0 ){ music_mute_button.enable( !music_mute_button.pressed ); } #endif add_component(&music_mute_button); shuffle_song_button.init( button_t::square_state, "shuffle midis" ); shuffle_song_button.pressed = sound_get_shuffle_midi(); shuffle_song_button.add_listener(this); add_component(&shuffle_song_button); add_table(2,0); { new_component( "Music volume:" ); music_volume_scrollbar.set_knob( L_KNOB_SIZE, 255 + L_KNOB_SIZE ); music_volume_scrollbar.set_knob_offset( sound_get_midi_volume() ); music_volume_scrollbar.set_scroll_discrete( false ); music_volume_scrollbar.add_listener( this ); add_component( &music_volume_scrollbar ); } end_table(); new_component(); // song selection new_component( "Currently playing:" ); add_table( 3, 1 ); { previous_song_button.set_typ( button_t::arrowleft ); previous_song_button.add_listener( this ); previous_song_button.enable( !music_mute_button.pressed ); add_component( &previous_song_button ); next_song_button.set_typ( button_t::arrowright ); next_song_button.add_listener( this ); next_song_button.enable( !music_mute_button.pressed ); add_component( &next_song_button ); add_component( &song_name_label ); update_song_name(); } end_table(); add_component( &song_credits_label ); new_component(); #ifdef USE_FLUIDSYNTH_MIDI // Soundfont selection soundfont_button.init( button_t::roundbox_state | button_t::flexible, "Select soundfont" ); soundfont_button.add_listener(this); add_component( &soundfont_button ); #endif set_resizemode(diagonal_resize); reset_min_windowsize(); } bool sound_frame_t::action_triggered( gui_action_creator_t *comp, value_t p) { if (comp == &next_song_button) { midi_stop(); midi_next_track(); check_midi(); update_song_name(); } else if (comp == &previous_song_button) { midi_stop(); midi_last_track(); check_midi(); update_song_name(); } else if (comp == &shuffle_song_button) { sound_set_shuffle_midi( !sound_get_shuffle_midi() ); shuffle_song_button.pressed = sound_get_shuffle_midi(); } else if (comp == &sound_mute_button) { sound_set_mute( !sound_mute_button.pressed ); sound_mute_button.pressed = sound_get_mute(); } else if (comp == &music_mute_button) { midi_set_mute( !music_mute_button.pressed ); music_mute_button.pressed = midi_get_mute(); previous_song_button.enable( !music_mute_button.pressed ); next_song_button.enable( !music_mute_button.pressed ); } else if (comp == &sound_volume_scrollbar) { sound_set_global_volume(p.i); } else if (comp == &music_volume_scrollbar) { uint32 volume = p.i; volume = (volume * volume * volume * volume) / 16581375ul; // (255 ^ 3) approximates a logarithmic sound, see https://www.dr-lex.be/info-stuff/volumecontrols.html sound_set_midi_volume(volume); } else if (comp == &sound_range) { env_t::sound_distance_scaling = p.i; } #ifdef USE_FLUIDSYNTH_MIDI else if( comp == &soundfont_button ) { create_win( new loadsoundfont_frame_t(), w_info, magic_soundfont ); } #endif else { for( int i = 0; i < MAX_SOUND_TYPES; i++ ) { if( comp == specific_volume_scrollbar[ i ] ) { uint32 volume = p.i; volume = (volume * volume * volume * volume) / 16581375ul; // (255 ^ 3) approximates a logarithmic sound sound_set_specific_volume( volume, (sound_type_t)i ); } } } return true; } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void sound_frame_t::draw(scr_coord pos, scr_size size) { // update song name label update_song_name(); #ifdef USE_FLUIDSYNTH_MIDI if( strcmp(env_t::soundfont_filename.c_str(), "Error") != 0 ){ music_mute_button.enable( true ); } #endif gui_frame_t::draw(pos, size); } // need to delete scroll bars sound_frame_t::~sound_frame_t() { for( int i = 0; i < MAX_SOUND_TYPES; i++ ) { delete specific_volume_scrollbar[ i ]; } } simutrans-124.5/src/simutrans/gui/sound_frame.h000066400000000000000000000023341520675460500216410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SOUND_FRAME_H #define GUI_SOUND_FRAME_H #include "gui_frame.h" #include "components/gui_scrollbar.h" #include "components/gui_label.h" #include "components/gui_button.h" #include "components/gui_numberinput.h" #include "components/action_listener.h" /** * Dialog for sound settings */ class sound_frame_t : public gui_frame_t, action_listener_t { private: scrollbar_t sound_volume_scrollbar; scrollbar_t music_volume_scrollbar; scrollbar_t *specific_volume_scrollbar[MAX_SOUND_TYPES+1]; gui_numberinput_t sound_range; button_t sound_mute_button; button_t music_mute_button; button_t next_song_button; button_t previous_song_button; button_t shuffle_song_button; #ifdef USE_FLUIDSYNTH_MIDI button_t soundfont_button; #endif gui_label_buf_t song_name_label; gui_label_buf_t song_credits_label; void update_song_name(); public: const char *get_help_filename() const OVERRIDE {return "sound.txt";} sound_frame_t(); virtual ~sound_frame_t(); // used for updating the song title void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/sprachen.cc000066400000000000000000000157111520675460500213030ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../pathes.h" #include "../display/simimg.h" #include "../simskin.h" #include "../tool/simmenu.h" #include "../descriptor/skin_desc.h" #include "sprachen.h" #include "loadfont_frame.h" #include "simwin.h" #include "components/gui_image.h" #include "components/gui_divider.h" #include "../display/font.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../sys/simsys.h" #include "../utils/simstring.h" #include "../utils/unicode.h" #include "../world/simworld.h" int sprachengui_t::cmp_language_button(sprachengui_t::language_button_t a, sprachengui_t::language_button_t b) { return strcmp( a.button->get_text(), b.button->get_text() )<0; } // set currency symbol void sprachengui_t::init_currency_from_lang() { const char* org = "CURRENCY"; const char* trans = translator::translate(org); if (trans != org && env_t::currency_symbol[0] == 0) { // no special symbol selected bool left = *translator::translate("CURRENCY_LEFT") == '1'; set_currency_string(trans, left); env_t::currency_symbol[0] = 0; env_t::currency_left = left; } else if (env_t::currency_symbol[0]) { set_currency_string(env_t::currency_symbol, env_t::currency_left); } else { env_t::currency_left = false; set_currency_string("$", false); } } /** * Causes the required fonts for currently selected * language to be loaded */ void sprachengui_t::init_font_from_lang() { #if COLOUR_DEPTH != 0 const utf8* new_world = (const utf8*)translator::translate("Beenden"); size_t len; utf16 testfor_this_character = utf8_decoder_t::decode(new_world, len); bool reload_font = !gfx->font_has_character(testfor_this_character); if(reload_font) { if (env_t::fontsize == 11) { // can only use fixed default with current font size std::string old_font = env_t::fontname; static const char* default_name = "PROP_FONT_FILE"; const char* prop_font_file = translator::translate(default_name); if (prop_font_file == default_name) { // missing font } else { // load large font dr_chdir(env_t::base_dir); bool ok = false; char prop_font_file_name[4096]; tstrncpy(prop_font_file_name, prop_font_file, lengthof(prop_font_file_name)); char* f = strtok(prop_font_file_name, ";"); do { std::string fname = FONT_PATH_X; fname += prop_font_file_name; ok = gfx->load_font(fname.c_str(), false); f = strtok(NULL, ";"); } while (!ok && f); dr_chdir(env_t::user_dir); reload_font = false; } } // if nothing is found => display font selector } #endif init_currency_from_lang(); const char * p = translator::translate("SEP_THOUSAND"); char c = ','; if(*p != 'S') { c = *p; } set_thousand_sep(c); set_thousand_sep_exponent(atoi(translator::translate("SEP_THOUSAND_EXPONENT"))); p = translator::translate("SEP_FRACTION"); c = '.'; if(*p != 'S') { c = *p; } set_fraction_sep(c); const char *str = "LARGE_NUMBER_STRING"; p = translator::translate(str); double v = atof( translator::translate("LARGE_NUMBER_VALUE") ); if(p == str || v==0.0) { // fallback: will ignore it p = ""; v = 1e99; } set_large_amount(p,v); } sprachengui_t::sprachengui_t() : gui_frame_t( translator::translate("Sprachen") ), text_label(&buf), buttons(translator::get_language_count()) { set_table_layout(2,0); buf.clear(); buf.append(translator::translate("LANG_CHOOSE\n")); text_label.set_buf(&buf); // force recalculation of size add_component( &text_label ); if (skinverwaltung_t::flaggensymbol) { gui_image_t *flags = new_component(skinverwaltung_t::flaggensymbol->get_image_id(0)); flags->enable_offset_removal(true); } else { new_component(); } tab.add_tab(&button_plane, translator::translate("GUI")); if (env_t::networkmode || !world()) { new_component_span(2); add_component(&button_plane, 2); tab.set_active_tab_index(0); } else { tab.add_tab(&button_plane, translator::translate("Names")); add_component(&tab,2); tab.add_listener(this); } button_plane.set_table_layout(0, 2); const translator::lang_info* lang = translator::get_langs(); dr_chdir( env_t::base_dir ); for (int i = 0; i < translator::get_language_count(); ++i, ++lang) { button_t* b = new button_t(); b->set_typ(button_t::square_state); b->set_text(lang->name); b->set_no_translate(true); // we do not check for a working font anymore b->add_listener(this); // press button int id = translator::get_language(lang->iso); if( translator::get_language() == id ) { b->pressed = true; } // insert ordered by language name language_button_t lb; lb.button = b; lb.id = id; buttons.insert_ordered(lb, sprachengui_t::cmp_language_button); } dr_chdir(env_t::user_dir); // insert buttons such that language appears columnswise button_plane.set_table_layout(3, 0); const uint32 count = buttons.get_count(); const uint32 half = (count+1)/2; for(uint32 i=0; i < half; i++) { button_plane.add_component(buttons[i].button); if (i+ half < count) { button_plane.add_component(buttons[i+half].button); } } button_plane.set_size(button_plane.get_max_size()); reset_min_windowsize(); set_windowsize(get_min_windowsize() ); } bool sprachengui_t::action_triggered( gui_action_creator_t *comp, value_t p) { if (comp == &tab) { int lang_id = (p.i == 0) ? translator::get_language() : world()->get_settings().get_name_language_id(); for (int i = 0; i < translator::get_language_count(); i++) { button_t* b = buttons[i].button; b->pressed = lang_id == buttons[i].id; } return true; } for(int i=0; ipressed = true; if(p.i==0) { env_t::language_names_iso = NULL; translator::set_language( buttons[i].id ); init_font_from_lang(); event_t* ev = new event_t(); ev->ev_class = EVENT_SYSTEM; ev->ev_code = SYSTEM_RELOAD_WINDOWS; queue_event( ev ); // check if we need another font ... // we only show matching fonts for this language const utf8 *new_world = (const utf8 *)translator::translate("Beenden"); size_t len; if (!gfx->font_has_character(utf8_decoder_t::decode(new_world, len))) { // load a matching font ... create_win(new loadfont_frame_t(), w_info, magic_font); } if (world()) { // must not update non-existent toolbars tool_t::update_toolbars(); } } else { if (translator::get_language() == buttons[i].id) { world()->get_settings().set_name_language_iso(NULL); } else { world()->get_settings().set_name_language_iso((translator::get_langs())[buttons[i].id].iso); // maybe we should warn that there might no be a matching font in not unicode ... translator::init_custom_names(world()->get_settings().get_name_language_id()); } } } else { b->pressed = false; } } return true; } simutrans-124.5/src/simutrans/gui/sprachen.h000066400000000000000000000024131520675460500211400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_SPRACHEN_H #define GUI_SPRACHEN_H #include "gui_frame.h" #include "components/action_listener.h" #include "components/gui_button.h" #include "components/gui_textarea.h" #include "components/gui_tab_panel.h" #include "../utils/cbuffer.h" #include "../tpl/vector_tpl.h" /** * Dialog for language change */ class sprachengui_t : public gui_frame_t, private action_listener_t { private: cbuffer_t buf; gui_textarea_t text_label; gui_tab_panel_t tab; gui_aligned_container_t button_plane; struct language_button_t { button_t* button; int id; }; vector_tpl buttons; static int cmp_language_button(sprachengui_t::language_button_t a, sprachengui_t::language_button_t b); public: // currency symbol static void init_currency_from_lang(); /** * Causes the required fonts for currently selected * language to be loaded if true */ static void init_font_from_lang(); sprachengui_t(); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "language.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/station_building_select.cc000066400000000000000000000037011520675460500243710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* * Building facing selector, when CTRL+Clicking over a building icon in menu */ #include "../world/simworld.h" #include "../tool/simtool.h" #include "simwin.h" #include "../utils/cbuffer.h" #include "station_building_select.h" #include "components/gui_label.h" #include "components/gui_building.h" #include "../descriptor/building_desc.h" static const char label_text[4][64] = { "sued", "ost", "nord", "west" }; tool_build_station_t station_building_select_t::tool=tool_build_station_t(); station_building_select_t::station_building_select_t(const building_desc_t *desc) : gui_frame_t( translator::translate("Choose direction") ) { this->desc = desc; set_table_layout(1,0); set_alignment(ALIGN_CENTER_H); gui_label_buf_t *lb = new_component(); lb->buf().printf( translator::translate("Width = %d, Height = %d"), desc->get_x(0), desc->get_y(0)); lb->update(); // the image array add_table(2,2); { for( sint16 i=0; iget_all_layouts(),4); i++ ) { add_table(1,0)->set_alignment(ALIGN_CENTER_H); { gui_building_t *g = new_component(desc, i); g->add_listener(this); actionbutton[i].init( button_t::roundbox | button_t::flexible, translator::translate(label_text[i]) ); actionbutton[i].add_listener(this); add_component(&actionbutton[i]); } end_table(); } } end_table(); reset_min_windowsize(); } /** * This method is called if an action is triggered */ bool station_building_select_t::action_triggered( gui_action_creator_t *comp, value_t v) { for(int i=0; i<4; i++) { if(comp == &actionbutton[i] || v.i == i) { static cbuffer_t default_str; default_str.clear(); default_str.printf("%s,%i", desc->get_name(), i ); tool.set_default_param(default_str); welt->set_tool( &tool, welt->get_active_player() ); destroy_win(this); } } return true; } simutrans-124.5/src/simutrans/gui/station_building_select.h000066400000000000000000000013311520675460500242300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_STATION_BUILDING_SELECT_H #define GUI_STATION_BUILDING_SELECT_H #include "components/action_listener.h" #include "gui_frame.h" #include "components/gui_button.h" class building_desc_t; class tool_build_station_t; /* * Building facing selector, when CTRL+Clicking over a building icon in menu */ class station_building_select_t : public gui_frame_t, action_listener_t { const building_desc_t *desc; button_t actionbutton[4]; static tool_build_station_t tool; public: station_building_select_t(const building_desc_t *desc); bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/themeselector.cc000066400000000000000000000062671520675460500223510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "themeselector.h" #include "simwin.h" #include "../sys/simsys.h" #include "../simevent.h" #include "gui_theme.h" #include "../utils/simstring.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/tabfile.h" std::string themeselector_t::undo = ""; themeselector_t::themeselector_t() : savegame_frame_t( ".tab", false, NULL, false ) { // remove unnecessary buttons top_frame.remove_component( &input ); delete_enabled = false; label_enabled = false; set_name( translator::translate( "Theme selector" ) ); fnlabel.set_text_pointer( translator::translate( "Select a theme for display" ) ); if( undo.empty() ) { undo = env_t::default_theme; set_windowsize(get_min_windowsize()); } } bool themeselector_t::check_file(const char *filename, const char *suffix) { return savegame_frame_t::check_file(filename,suffix); } // A theme button was pressed bool themeselector_t::item_action(const char *fullpath) { gui_theme_t::themes_init(fullpath,true,true); event_t *ev = new event_t(); ev->ev_class = EVENT_SYSTEM; ev->ev_code = SYSTEM_RELOAD_WINDOWS; queue_event( ev ); return false; } // Ok button was pressed bool themeselector_t::ok_action(const char *) { undo = ""; return true; } // Cancel button was pressed bool themeselector_t::cancel_action(const char *) { item_action(undo.c_str()); undo = ""; return true; } // returns the additional name of the file const char *themeselector_t::get_info(const char *fn ) { const char *info = ""; tabfile_t themesconf; if( themesconf.open(fn) ) { tabfileobj_t contents; // get trimmed theme name themesconf.read(contents); std::string name( contents.get( "name" ) ); info = strdup( trim( name ).c_str() ); } themesconf.close(); return info; } void themeselector_t::fill_list() { add_path( ((std::string)env_t::base_dir+"themes/").c_str() ); if( strncmp(env_t::user_dir, env_t::base_dir, PATH_MAX) != 0 ) { // not signle user add_path( ((std::string)env_t::user_dir+"themes/").c_str() ); } // do the search ... savegame_frame_t::fill_list(); for(dir_entry_t const& i : entries) { if (i.type == LI_HEADER) { continue; } delete[] i.button->get_text(); // free up default allocation. i.button->set_typ(button_t::roundbox_state | button_t::flexible); i.button->set_text(i.label->get_text_pointer()); i.button->pressed = !strcmp( env_t::default_theme.c_str(), i.info ); // mark current theme i.label->set_text_pointer( NULL ); // remove reference to prevent conflicts at delete[] // Get a new label buffer since the original is now owned by i.button. // i.label->set_text_pointer( strdup( get_filename(i.info).c_str() ) ); } if(entries.get_count() <= this->num_sections+1) { // less than two themes exist => we coudl close now ... } // force new resize after we have rearranged the gui resize(scr_coord(0,0)); } void themeselector_t::rdwr( loadsave_t *file ) { scr_size size = get_windowsize(); size.rdwr( file ); if( file->is_loading() ) { set_windowsize( size ); resize( scr_coord(0,0) ); } } simutrans-124.5/src/simutrans/gui/themeselector.h000066400000000000000000000020411520675460500221750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_THEMESELECTOR_H #define GUI_THEMESELECTOR_H #include "simwin.h" #include "savegame_frame.h" #include "components/gui_textarea.h" #include "../utils/cbuffer.h" /* * selection of paks at the start time */ class themeselector_t : public savegame_frame_t { protected: static std::string undo; // undo buffer bool item_action ( const char *fullpath ) OVERRIDE; bool ok_action ( const char *fullpath ) OVERRIDE; bool cancel_action ( const char *fullpath ) OVERRIDE; const char* get_info ( const char *fname ) OVERRIDE; bool check_file ( const char *filename, const char *suffix ) OVERRIDE; void fill_list ( void ) OVERRIDE; public: themeselector_t ( void ); const char* get_help_filename ( void ) const OVERRIDE { return NULL; } uint32 get_rdwr_id ( void ) OVERRIDE { return magic_themes; } void rdwr ( loadsave_t *file ) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/tool_selector.cc000066400000000000000000000376301520675460500223610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../dataobj/environment.h" #include "../display/simimg.h" #include "../display/simgraph.h" #include "../player/simplay.h" #include "../utils/simstring.h" #include "../world/simworld.h" #include "../tool/simmenu.h" #include "../simskin.h" #include "gui_frame.h" #include "simwin.h" #include "tool_selector.h" #define MIN_WIDTH (80) tool_selector_t::tool_selector_t(const char* title, const char *help_file, uint32 toolbar_id, bool allow_break) : gui_frame_t( translator::translate(title) ), tools(0) { set_table_layout(0,0); // we do our own positioning of icons (for now) this->toolbar_id = toolbar_id; this->allow_break = allow_break; this->help_file = help_file; this->tool_icon_disp_start = 0; this->tool_icon_disp_end = 0; this->title = title; has_prev_next = false; is_dragging = false; offset = scr_coord( 0, 0 ); set_windowsize( scr_size(max(env_t::iconsize.w,MIN_WIDTH), D_TITLEBAR_HEIGHT) ); dirty = true; } /** * Add a new tool with values and tooltip text. * tool_in must be created by new tool_t(copy_tool)! */ void tool_selector_t::add_tool_selector(tool_t *tool_in) { image_id tool_img = tool_in->get_icon(welt->get_active_player()); if( tool_img == IMG_EMPTY && tool_in!=tool_t::dummy ) { return; } // only for non-empty icons ... tools.append(tool_in); scr_rect screen = win_get_max_window_area(); int ww = max(3,(screen.w/env_t::iconsize.w)-2); // to avoid zero or negative ww on posix (no graphic) backends tool_icon_width = tools.get_count(); DBG_DEBUG4("tool_selector_t::add_tool()","ww=%i, tool_icon_width=%i",ww,tool_icon_width); if( allow_break && (ww0 && env_t::toolbar_max_width 0) { ww = min(ww, env_t::toolbar_max_width); } else if (env_t::toolbar_max_width < 0) { ww = min(3, ww - env_t::toolbar_max_width); } int rows = (tool_icon_width/ww)+((tool_icon_width % ww)>0); uint16 max_tool_icon_height = max(1, (screen.h - win_get_statusbar_height()) / env_t::iconsize.h); tool_icon_width = min(ww, tool_icon_width); tool_icon_height = min(rows, max_tool_icon_height); } else { tool_icon_height = 1; } if( env_t::toolbar_max_height > 0 ) { tool_icon_height = min(tool_icon_height, env_t::toolbar_max_height); } dirty = true; set_windowsize( scr_size( tool_icon_width*env_t::iconsize.w, tool_icon_height*env_t::iconsize.h+D_TITLEBAR_HEIGHT ) ); tool_icon_disp_start = 0; tool_icon_disp_end = min( tool_icon_disp_start+tool_icon_width*tool_icon_height, tools.get_count() ); has_prev_next = ((uint32)tool_icon_width*tool_icon_height < tools.get_count()); DBG_DEBUG4("tool_selector_t::add_tool()", "at position %i (width %i)", tools.get_count(), tool_icon_width); } // reset the tools to empty state void tool_selector_t::reset_tools() { tools.clear(); gui_frame_t::set_windowsize( scr_size(max(env_t::iconsize.w,MIN_WIDTH), D_TITLEBAR_HEIGHT) ); tool_icon_width = 0; tool_icon_disp_start = 0; tool_icon_disp_end = 0; offset = scr_coord( 0, 0 ); } bool tool_selector_t::is_hit(int x, int y) { int dx = (x-offset.x)/env_t::iconsize.w; int dy = (y-D_TITLEBAR_HEIGHT-offset.y)/env_t::iconsize.h; // either click in titlebar or on an icon if( x>=0 && y>=0 && ( (ymouse_pos.x - ev->click_pos.x, 0) : scr_coord(0, ev->mouse_pos.y - ev->click_pos.y)); int xy = tool_icon_width*tool_icon_height; if( tool_icon_height == 1 && tool_icon_disp_start + xy >= (int)tools.get_count() ) { // we have to take into account that the height is not a full icon tool_icon_disp_start = max(0, (int)tools.get_count() - xy); offset.x = -min(-offset.x, env_t::iconsize.w-(get_windowsize().w % env_t::iconsize.w)); } if( tool_icon_width == 1 && tool_icon_disp_start + xy >= (int)tools.get_count() ) { // we have to take into account that the height is not a full icon tool_icon_disp_start = max(0, (int)tools.get_count() - xy); offset.y = -min( -offset.y, (get_windowsize().h-D_TITLEBAR_HEIGHT) % env_t::iconsize.h); } if( tool_icon_disp_start == 0 && (offset.x > 0 || offset.y > 0) ) { offset.x = 0; offset.y = 0; } if (offset.x > 0) { // we must change the old offset, since the mouse starting point changed! old_offset.x -= env_t::iconsize.w; offset.x -= env_t::iconsize.w; tool_icon_disp_start--; } if (offset.x <= -env_t::iconsize.w) { old_offset.x += env_t::iconsize.w; offset.x += env_t::iconsize.w; tool_icon_disp_start++; } if (offset.y > 0) { // we must change the old offset, since the mouse starting point changed! old_offset.y -= env_t::iconsize.h; offset.y -= env_t::iconsize.h; tool_icon_disp_start--; } if (offset.y <= -env_t::iconsize.h) { old_offset.y += env_t::iconsize.h; offset.y += env_t::iconsize.h; tool_icon_disp_start++; } if( !IS_LEFTRELEASE(ev) && ev->mouse_button_state != MOUSE_LEFTBUTTON ) { is_dragging = false; } } // offsets sanity check while (offset.x < -env_t::iconsize.w) { offset.x += env_t::iconsize.w; } while (offset.x > 0) { offset.x -= env_t::iconsize.w; } while (offset.y < -env_t::iconsize.h) { offset.y += env_t::iconsize.h; } while (offset.y > 0) { offset.y -= env_t::iconsize.h; } if( tool_icon_disp_start > tool_icon_disp_end ) { tool_icon_disp_start = 0; offset.x = 0; offset.y = 0; } int xy = tool_icon_width*tool_icon_height; tool_icon_disp_end = (tool_icon_height == 1) ? min(tool_icon_disp_start+xy+(offset.x!=0), tools.get_count()) : min(tool_icon_disp_start + xy + (offset.y != 0), tools.get_count()); if(IS_LEFTRELEASE(ev) || IS_RIGHTRELEASE(ev)) { if( is_dragging ) { is_dragging = false; if( abs(old_offset.x - offset.x) > 2 || abs(old_offset.y - offset.y) > 2 || ev->click_pos.x-ev->mouse_pos.x != offset.x || ev->click_pos.x - ev->mouse_pos.y != offset.y ) { // we did dragg sucesfully before, so no tool selection! return true; } } // No dragging => Next check tooltips const int x = (ev->mouse_pos.x-offset.x) / env_t::iconsize.w; const int y = (ev->mouse_pos.y-offset.y-D_TITLEBAR_HEIGHT) / env_t::iconsize.h; const int wz_idx = x+(tool_icon_width*y)+tool_icon_disp_start; if( wz_idx>=0 && wz_idx < (int)tools.get_count() ) { // change tool tool_t *tool = tools[wz_idx].tool; if(IS_LEFTRELEASE(ev)) { if( env_t::reselect_closes_tool && tool && tool->is_selected() ) { // ->exit triggers tool_selector_t::infowin_event in the closing toolbar, // which resets active tool to query tool if( tool->exit( welt->get_active_player() ) ) { welt->set_tool( tool_t::general_tool[TOOL_QUERY], welt->get_active_player() ); } } else { welt->set_tool( tool, welt->get_active_player() ); } } else { // right-click on toolbar icon closes toolbars and dialogues. Resets selectable simple and general tools to the query-tool if( tool && tool->is_selected() ) { // ->exit triggers tool_selector_t::infowin_event in the closing toolbar, // which resets active tool to query tool if( tool->exit(welt->get_active_player()) ) { welt->set_tool( tool_t::general_tool[TOOL_QUERY], welt->get_active_player() ); } } } return true; } } // this resets to query-tool, when closing toolsbar - but only for selected general tools in the closing toolbar else if(ev->ev_class==INFOWIN && ev->ev_code==WIN_CLOSE) { for(tool_data_t const i : tools) { if (i.tool->is_selected() && i.tool->get_id() & GENERAL_TOOL) { welt->set_tool( tool_t::general_tool[TOOL_QUERY], welt->get_active_player() ); break; } } } // reset title, language may have changed else if(ev->ev_class==INFOWIN && (ev->ev_code==WIN_TOP || ev->ev_code==WIN_OPEN) ) { set_name( translator::translate(title) ); } if(IS_WINDOW_CHOOSE_NEXT(ev)) { int xy = tool_icon_width*tool_icon_height; if(ev->ev_code==NEXT_WINDOW) { assert( xy >= tool_icon_width ); tool_icon_disp_start += xy; if( tool_icon_disp_start + xy > (int)tools.get_count() ) { tool_icon_disp_start = tools.get_count() - xy; } offset = scr_coord( 0, 0 ); } else { if( tool_icon_disp_start > xy ) { tool_icon_disp_start -= xy; } else { tool_icon_disp_start = 0; } offset = scr_coord( 0, 0 ); } tool_icon_disp_end = min(tool_icon_disp_start+xy, tools.get_count()); dirty = true; } return false; } void tool_selector_t::draw(scr_coord pos, scr_size sz) { player_t* player = welt->get_active_player(); const scr_size screen = gfx->get_screen_size(); if (toolbar_id == 0) { // checks for main menu (since it can change during changing layout) if (env_t::menupos == MENU_TOP || env_t::menupos == MENU_BOTTOM) { offset.y = 0; allow_break = false; tool_icon_width = (screen.w + env_t::iconsize.w - 1) / env_t::iconsize.w; tool_icon_height = 1; // only single row for title bar set_windowsize(sz); // check for too large values (after changing width etc.) if (screen.w >= (int)tools.get_count() * env_t::iconsize.w) { tool_icon_disp_start = 0; offset.x = 0; } else { scr_coord_val wx = (tools.get_count() - tool_icon_disp_start + 1) * env_t::iconsize.w + offset.x; if (wx < screen.w) { tool_icon_disp_start = tool_icon_disp_end < tool_icon_height ? 0 : tool_icon_disp_end - tool_icon_width; offset.x = screen.w - (tools.get_count() - tool_icon_disp_start) * env_t::iconsize.w; } } has_prev_next = (int)tools.get_count() * env_t::iconsize.w > sz.w; } else { offset.x = 0; allow_break = false; tool_icon_width = 1; // only single column for title bar scr_rect screen = win_get_max_window_area(); tool_icon_height = max(1, (screen.h - win_get_statusbar_height() + env_t::iconsize.h - 1) / env_t::iconsize.h); set_windowsize(scr_size(env_t::iconsize.w, screen.h - win_get_statusbar_height())); if (screen.h >= (int)tools.get_count() * env_t::iconsize.h) { tool_icon_disp_start = 0; offset.y = 0; } else { scr_coord_val hx = (tools.get_count() - tool_icon_disp_start + 1) * env_t::iconsize.h + offset.y; if (hx < screen.h) { tool_icon_disp_end = tools.get_count(); tool_icon_disp_start = tool_icon_disp_end < tool_icon_height ? 0 : tool_icon_disp_end - tool_icon_height; offset.y = screen.h - (tools.get_count() - tool_icon_disp_start) * env_t::iconsize.h; } } has_prev_next = (int)tools.get_count() * env_t::iconsize.h > sz.h; } } clip_dimension const p_cr = gfx->get_clip_rect(CLIP_NUM_DEFAULT_VALUE); if (toolbar_id != 0) { gfx->set_clip_rect(pos.x, pos.y, sz.w, sz.h CLIP_NUM_DEFAULT, false); } for( uint i = tool_icon_disp_start; i < tool_icon_disp_end; i++ ) { const image_id icon_img = tools[i].tool->get_icon(player); #if COLOUR_DEPTH != 0 const scr_coord_val additional_xoffset = ( (i-tool_icon_disp_start)%(tool_icon_width+(offset.x!=0)) )*env_t::iconsize.w; const scr_coord_val additional_yoffset = D_TITLEBAR_HEIGHT+( (i-tool_icon_disp_start)/(tool_icon_width+(offset.x!=0)) )*env_t::iconsize.h; #else const scr_coord_val additional_xoffset = 0; const scr_coord_val additional_yoffset = 0; #endif const scr_coord draw_pos = pos + offset + scr_coord(additional_xoffset, additional_yoffset); const char *param = tools[i].tool->get_default_param(); // we don't draw in main menu as it is already made in simwin.cc // no background if separator starts with "-b" and has an icon defined if( toolbar_id>0 && !(strstart((param==NULL)? "" : param, "-b")) ) { if( skinverwaltung_t::toolbar_background && skinverwaltung_t::toolbar_background->get_image_id(toolbar_id) != IMG_EMPTY ) { const image_id back_img = skinverwaltung_t::toolbar_background->get_image_id(toolbar_id); gfx->fit_img_to_width( back_img, env_t::iconsize.w ); gfx->draw_color_img( back_img, draw_pos.x, draw_pos.y, welt->get_active_player_nr(), false, true CLIP_NUM_DEFAULT); } else { gfx->draw_rect_clipped(draw_pos.x, draw_pos.y, env_t::iconsize.w, env_t::iconsize.h, gfx->palette_lookup(MN_GREY2), false CLIP_NUM_DEFAULT); } } // if there's no image we simply skip, button will be transparent showing toolbar background if( icon_img != IMG_EMPTY ) { bool tool_dirty = dirty || (tools[i].tool->is_selected() ^ tools[i].selected); gfx->fit_img_to_width( icon_img, env_t::iconsize.w ); gfx->draw_color_img(icon_img, draw_pos.x, draw_pos.y, player->get_player_nr(), false, tool_dirty CLIP_NUM_DEFAULT); tools[i].tool->draw_after( draw_pos, tool_dirty); if (!tools[i].tool->enabled) { // grey out disbaled entries gfx->draw_img_blend(icon_img, draw_pos.x, draw_pos.y, TRANSPARENT75_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_WHITE), false, tool_dirty); } // store whether tool was selected tools[i].selected = tools[i].tool->is_selected(); } } if( is_dragging ) { gfx->mark_rect_dirty_wc(pos.x, pos.y+D_TITLEBAR_HEIGHT, pos.x+sz.w, pos.y+sz.h+D_TITLEBAR_HEIGHT ); } else if( dirty && (tool_icon_disp_end-tool_icon_disp_start < tool_icon_width*tool_icon_height) ) { // mark empty space empty gfx->mark_rect_dirty_wc(pos.x, pos.y, pos.x + tool_icon_width*env_t::iconsize.w, pos.y + tool_icon_height*env_t::iconsize.h); } if (toolbar_id != 0) { gfx->set_clip_rect(p_cr.x, p_cr.y, p_cr.w, p_cr.h CLIP_NUM_DEFAULT, false); } if( offset.x != 0 && tool_icon_disp_start > 0 ) { gfx->draw_color_img(gui_theme_t::arrow_button_left_img[0], pos.x, pos.y + D_TITLEBAR_HEIGHT, 0, false, false CLIP_NUM_DEFAULT); } if( offset.y != 0 && tool_icon_disp_start > 0 ) { gfx->draw_color_img(gui_theme_t::arrow_button_up_img[0], pos.x, pos.y + D_TITLEBAR_HEIGHT, 0, false, false CLIP_NUM_DEFAULT); } if( tool_icon_height == 1 && (tool_icon_disp_start+tool_icon_width < tools.get_count() || (-offset.x) < env_t::iconsize.w*tool_icon_width-get_windowsize().w) ) { gfx->draw_color_img( gui_theme_t::arrow_button_right_img[0], pos.x+sz.w-D_ARROW_UP_WIDTH, pos.y+D_TITLEBAR_HEIGHT, 0, false, false CLIP_NUM_DEFAULT); } if( tool_icon_width == 1 && (tool_icon_disp_start+tool_icon_height < tools.get_count() || (-offset.y) < env_t::iconsize.h*tool_icon_height-get_windowsize().h) ) { gfx->draw_color_img(gui_theme_t::arrow_button_down_img[0], pos.x+sz.w-D_ARROW_DOWN_WIDTH, pos.y+D_TITLEBAR_HEIGHT+sz.h-D_ARROW_DOWN_HEIGHT, 0, false, false CLIP_NUM_DEFAULT); } if( !is_dragging ) { // tooltips? const scr_coord mouse_pos = get_mouse_pos(); if( is_hit(mouse_pos.x-pos.x, mouse_pos.y-pos.y) && (mouse_pos.y-pos.y > D_TITLEBAR_HEIGHT)) { const scr_coord_val xdiff = (mouse_pos.x - pos.x - offset.x) / env_t::iconsize.w; const scr_coord_val ydiff = (mouse_pos.y - pos.y - D_TITLEBAR_HEIGHT - offset.y) / env_t::iconsize.h; if( xdiff>=0 && xdiff=0 ) { const int tipnr = xdiff+(tool_icon_width*ydiff)+tool_icon_disp_start; if( tipnr < (int)tool_icon_disp_end ) { const char* tipstr = tools[tipnr].tool->get_tooltip(welt->get_active_player()); win_set_tooltip(mouse_pos + TOOLTIP_MOUSE_OFFSET, tipstr, tools[tipnr].tool, this); } } } } dirty = false; //as we do not call gui_frame_t::draw, we reset dirty flag explicitly unset_dirty(); } bool tool_selector_t::empty(player_t *player) const { for(tool_data_t w : tools) { if (w.tool->get_icon(player) != IMG_EMPTY) { return false; } } return true; } simutrans-124.5/src/simutrans/gui/tool_selector.h000066400000000000000000000046121520675460500222150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_TOOL_SELECTOR_H #define GUI_TOOL_SELECTOR_H #include "gui_frame.h" #include "../tpl/vector_tpl.h" #include "simwin.h" #include "../dataobj/environment.h" class tool_t; /** * This class defines all toolbar dialogues, floating bar of tools, i.e. the part the user will see */ class tool_selector_t : public gui_frame_t { private: struct tool_data_t { tool_data_t(tool_t* t=NULL) : tool(t), selected(false) {} tool_t* tool; ///< pointer to associated tool bool selected; ///< store whether tool was active during last call to tool_selector_t::draw }; /// tool definitions vector_tpl tools; // get current toolbar number for saving uint32 toolbar_id; /** * window width in toolboxes */ uint16 tool_icon_width; uint16 tool_icon_height; scr_coord offset, old_offset; uint16 tool_icon_disp_start; uint16 tool_icon_disp_end; bool has_prev_next, is_dragging; /** * Window title */ const char *title; /** * Name of the help file */ const char *help_file; // needs dirty redraw (only when changed) bool dirty; bool allow_break; public: tool_selector_t(const char *title, const char *help_file, uint32 toolbar_id, bool allow_break=true ); /** * Add a new tool with values and tooltip text. */ void add_tool_selector(tool_t *tool_in); // purges toolbar void reset_tools(); // untranslated title const char *get_internal_name() const {return title;} bool has_title() const OVERRIDE { return toolbar_id!=0; } const char *get_help_filename() const OVERRIDE {return help_file;} FLAGGED_PIXVAL get_titlecolor() const OVERRIDE { return env_t::default_window_title_color; } bool is_hit(int x, int y) OVERRIDE; /** * Does this window need a next button in the title bar? * @return true if such a button is needed */ bool has_next() const OVERRIDE {return has_prev_next;} bool infowin_event(event_t const*) OVERRIDE; /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; // since no information are needed to be saved to restore this, returning magic is enough uint32 get_rdwr_id() OVERRIDE { return magic_toolbar+toolbar_id; } bool empty(player_t *player) const; }; #endif simutrans-124.5/src/simutrans/gui/trafficlight_info.cc000066400000000000000000000044421520675460500231600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "trafficlight_info.h" #include "components/gui_label.h" #include "../obj/roadsign.h" // The rest of the dialog #include "../tool/simmenu.h" #include "../world/simworld.h" void trafficlight_info_t::update_data() { ns.set_value( roadsign->get_ticks_ns() ); ow.set_value( roadsign->get_ticks_ow() ); offset.set_value( roadsign->get_ticks_offset() ); yellow_ns.set_value( roadsign->get_ticks_yellow_ns() ); yellow_ow.set_value( roadsign->get_ticks_yellow_ow() ); } trafficlight_info_t::trafficlight_info_t(roadsign_t* s) : obj_infowin_t(s), roadsign(s) { add_table(3,1); { ns.set_limits( 1, 255 ); ns.wrap_mode( false ); ns.add_listener( this ); add_component( &ns ); ow.set_limits( 1, 255 ); ow.wrap_mode( false ); ow.add_listener( this ); add_component( &ow ); offset.set_limits( 0, 255 ); offset.wrap_mode( false ); offset.add_listener( this ); add_component( &offset ); } end_table(); add_table(2,1); { yellow_ns.set_limits( 1, 255 ); yellow_ns.wrap_mode( false ); yellow_ns.add_listener( this ); add_component( &yellow_ns ); yellow_ow.set_limits( 1, 255 ); yellow_ow.wrap_mode( false ); yellow_ow.add_listener( this ); add_component( &yellow_ow ); } end_table(); update_data(); // show author below the settings if (char const* const maker = roadsign->get_desc()->get_copyright()) { gui_label_buf_t* lb = new_component(); lb->buf().printf(translator::translate("Constructed by %s"), maker); lb->update(); } recalc_size(); } bool trafficlight_info_t::action_triggered( gui_action_creator_t *comp, value_t v) { char param[256]; int toolnr = 0; if(comp == &ns) { toolnr = 1; } else if(comp == &ow) { toolnr = 0; } else if(comp == &offset) { toolnr = 2; } else if(comp == &yellow_ns) { toolnr = 4; } else if(comp == &yellow_ow) { toolnr = 3; } else { dbg->fatal( "trafficlight_info_t","Wrong action triggered" ); } sprintf( param, "%s,%d,%i", roadsign->get_pos().get_str(), toolnr, (int)v.i ); tool_t::simple_tool[TOOL_CHANGE_TRAFFIC_LIGHT]->set_default_param( param ); welt->set_tool( tool_t::simple_tool[TOOL_CHANGE_TRAFFIC_LIGHT], welt->get_active_player() ); return true; } simutrans-124.5/src/simutrans/gui/trafficlight_info.h000066400000000000000000000016001520675460500230130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_TRAFFICLIGHT_INFO_H #define GUI_TRAFFICLIGHT_INFO_H #include "obj_info.h" #include "components/action_listener.h" #include "components/gui_numberinput.h" #include "components/gui_container.h" class roadsign_t; /** * Traffic light phase buttons */ class trafficlight_info_t : public obj_infowin_t, public action_listener_t { private: roadsign_t* roadsign; gui_numberinput_t ns, ow, offset, yellow_ns, yellow_ow; public: trafficlight_info_t(roadsign_t* s); /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char *get_help_filename() const OVERRIDE {return "trafficlight_info.txt";} bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; // called, after external change void update_data(); }; #endif simutrans-124.5/src/simutrans/gui/vehiclelist_frame.cc000066400000000000000000000264231520675460500231670ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "gui_theme.h" #include "vehiclelist_frame.h" #include "../builder/goods_manager.h" #include "../builder/vehikelbauer.h" #include "../simskin.h" #include "../simintr.h" #include "../world/simworld.h" #include "../display/simgraph.h" #include "../dataobj/translator.h" #include "../descriptor/goods_desc.h" #include "../descriptor/intro_dates.h" #include "../descriptor/skin_desc.h" #include "../descriptor/vehicle_desc.h" #include "../utils/simstring.h" #include "../utils/unicode.h" int vehiclelist_stats_t::sort_mode = vehicle_builder_t::sb_intro_date; bool vehiclelist_stats_t::reverse = false; // for having uniform spaced columns int vehiclelist_stats_t::img_width = 100; vehiclelist_stats_t::vehiclelist_stats_t(const vehicle_desc_t *v) : details(&details_buf,0) { veh = v; // width of image const image_id image = veh->get_image_id( ribi_t::dir_south, veh->get_freight_type() ); const scr_rect r = gfx->get_base_image_offset(image); if( r.w > img_width ) { img_width = r.w + D_H_SPACE; } height = r.h; // name is the widest entry in column 1 name_width = gfx->calc_text_width( translator::translate( veh->get_name(), world()->get_settings().get_name_language_id() ) ); if( veh->get_power() > 0 ) { char str[ 256 ]; sprintf( str, " (%s)", translator::translate( vehicle_builder_t::engine_type_names[ veh->get_engine_type() + 1 ] ) ); name_width += gfx->calc_text_width( str ); } scr_coord_val name_h = LINESPACE; // column 1 part1.clear(); if( sint64 fix_cost = world()->scale_with_month_length( veh->get_maintenance() ) ) { char tmp[ 128 ]; money_to_string( tmp, veh->get_price() / 100.0, false ); part1.printf( translator::translate( "Cost: %8s (%.2f$/km %.2f$/m)\n" ), tmp, veh->get_running_cost() / 100.0, fix_cost / 100.0 ); } else { char tmp[ 128 ]; money_to_string( tmp, veh->get_price() / 100.0, false ); part1.printf( translator::translate( "Cost: %8s (%.2f$/km)\n" ), tmp, veh->get_running_cost() / 100.0 ); } if( veh->get_capacity() > 0 ) { // must translate as "Capacity: %3d%s %s\n" part1.printf( translator::translate( "Capacity: %d%s %s\n" ), veh->get_capacity(), translator::translate( veh->get_freight_type()->get_mass() ), veh->get_freight_type()->get_catg() == 0 ? translator::translate( veh->get_freight_type()->get_name() ) : translator::translate( veh->get_freight_type()->get_catg_name() ) ); part1.printf("%s%s\n", translator::translate("Loading time:"), difftick_to_string(veh->get_loading_time(), false)); } part1.printf( "%s %3d km/h\n", translator::translate( "Max. speed:" ), veh->get_topspeed() ); if( veh->get_power() > 0 ) { if( veh->get_gear() != 64 ) { part1.printf( "%s %4d kW (x%0.2f)\n", translator::translate( "Power:" ), veh->get_power(), veh->get_gear() / 64.0 ); } else { part1.printf( translator::translate( "Power: %4d kW\n" ), veh->get_power() ); } } const scr_size text1_size = gfx->calc_multiline_text_size(part1); col1_width = text1_size.w + D_H_SPACE; // column 2 part2.clear(); part2.printf( "%s %4.1ft\n", translator::translate( "Weight:" ), veh->get_weight() / 1000.0 ); part2.printf( "%s: %s - ", translator::translate( "Available" ), translator::get_short_date( veh->get_intro_year_month() / 12, veh->get_intro_year_month() % 12 ) ); if( veh->get_retire_year_month() != DEFAULT_RETIRE_YEAR * 12 ) { part2.printf( "%s", translator::get_short_date( veh->get_retire_year_month() / 12, veh->get_retire_year_month() % 12 ) ); } if( char const* const copyright = veh->get_copyright() ) { part2.append( "\n" ); part2.printf( translator::translate( "Constructed by %s" ), copyright ); } const scr_size text2_size = gfx->calc_multiline_text_size(part2); col2_width = text2_size.w; // we need to find out manually, if we have extra text to show if (strlen(veh->get_name()) < 238) { char ei[256]; sprintf(ei, "obj_%s_details", veh->get_name()); const char* translated_ei = translator::translate(ei); if (ei != translated_ei) { details_buf.append(translated_ei); details.set_width(col1_width + col2_width); name_h += details.get_size().h; } } height = max( height, max( text1_size.h, text2_size.h ) + name_h ) + D_V_SPACE; } void vehiclelist_stats_t::draw( scr_coord offset ) { uint32 month = world()->get_current_month(); offset += pos; offset.x += D_MARGIN_LEFT; offset.y += D_V_SPACE/2; const image_id image = veh->get_image_id( ribi_t::dir_south, veh->get_freight_type() ); const scr_rect r = gfx->get_base_image_offset(image); gfx->draw_base_img(image, offset.x - r.x, offset.y - r.y, world()->get_active_player_nr(), false, true CLIP_NUM_DEFAULT); // first name offset.x += img_width; int dx = gfx->draw_text( offset.x, offset.y, translator::translate( veh->get_name(), world()->get_settings().get_name_language_id() ), ALIGN_LEFT|DT_CLIP, veh->is_future(month) ? SYSCOL_TEXT_HIGHLIGHT : (veh->is_available(month) ? SYSCOL_TEXT : gui_theme_t::gui_color_obsolete), false ); if( veh->get_power() > 0 ) { char str[ 256 ]; sprintf( str, " (%s)", translator::translate( vehicle_builder_t::engine_type_names[ veh->get_engine_type() + 1 ] ) ); gfx->draw_text( offset.x+dx, offset.y, str, ALIGN_LEFT|DT_CLIP, SYSCOL_TEXT, false ); } // maybe there are detailed text to the vehicle? int yyy = offset.y + LINESPACE; if (details_buf.len()>0) { details.draw(scr_coord(offset.x, yyy)); yyy += details.get_size().h; } // now the rest in two columns gfx->draw_multiline_text( offset.x, yyy, part1, SYSCOL_TEXT ); gfx->draw_multiline_text( offset.x + col1_width, yyy, part2, SYSCOL_TEXT ); } const char *vehiclelist_stats_t::get_text() const { return translator::translate( veh->get_name() ); } bool vehiclelist_stats_t::compare(const gui_component_t *aa, const gui_component_t *bb) { bool result = vehicle_builder_t::compare_vehicles( dynamic_cast(aa)->veh, dynamic_cast(bb)->veh, (vehicle_builder_t::sort_mode_t)vehiclelist_stats_t::sort_mode ); return vehiclelist_stats_t::reverse ? !result : result; } vehiclelist_frame_t::vehiclelist_frame_t() : gui_frame_t( translator::translate("vh_title") ), scrolly(gui_scrolled_list_t::windowskin, vehiclelist_stats_t::compare) { name_filter[0] = 0; scrolly.set_cmp( vehiclelist_stats_t::compare ); scrolly.set_checkered(true); set_table_layout(3,0); new_component("Filter:"); name_filter_input.set_text(name_filter, lengthof(name_filter)); name_filter_input.set_notify_all_changes_delay(0); add_component(&name_filter_input,2); name_filter_input.add_listener(this); // next rows bt_obsolete.init(button_t::square_state, "Show obsolete"); bt_obsolete.add_listener(this); add_component(&bt_obsolete); bt_future.init(button_t::square_state, "Show future"); bt_future.add_listener(this); bt_future.pressed = true; add_component(&bt_future); ware_filter.clear_elements(); ware_filter.new_component(translator::translate("All"), SYSCOL_TEXT); idx_to_ware.append(NULL); for (int i = 0; i < goods_manager_t::get_count(); i++) { const goods_desc_t* ware = goods_manager_t::get_info(i); if (ware == goods_manager_t::none) { continue; } if (ware->get_catg() == 0) { ware_filter.new_component(translator::translate(ware->get_name()), SYSCOL_TEXT); idx_to_ware.append(ware); } } // now add other good categories for (int i = 1; i < goods_manager_t::get_max_catg_index(); i++) { const goods_desc_t* ware = goods_manager_t::get_info_catg(i); if (ware->get_catg() != 0) { ware_filter.new_component(translator::translate(ware->get_catg_name()), SYSCOL_TEXT); idx_to_ware.append(ware); } } ware_filter.set_selection(0); ware_filter.add_listener(this); add_component(&ware_filter); // second row new_component( "hl_txt_sort" ); sort_by.clear_elements(); for( int i = 0; i < vehicle_builder_t::sb_length; i++ ) { sort_by.new_component(translator::translate(vehicle_builder_t::vehicle_sort_by[i]), SYSCOL_TEXT); } sort_by.set_selection( vehiclelist_stats_t::sort_mode ); sort_by.add_listener( this ); add_component( &sort_by ); sorteddir.init( button_t::sortarrow_state, NULL ); sorteddir.pressed = vehiclelist_stats_t::reverse; sorteddir.add_listener( this ); add_component( &sorteddir ); tabs.init_tabs(&scrolly); tabs.add_listener(this); add_component(&tabs,3); fill_list(); set_resizemode(diagonal_resize); scrolly.set_maximize(true); reset_min_windowsize(); } /** * This method is called if an action is triggered */ bool vehiclelist_frame_t::action_triggered( gui_action_creator_t *comp,value_t v) { if(comp == &sort_by) { vehiclelist_stats_t::sort_mode = max(0,v.i); fill_list(); } else if(comp == &ware_filter) { fill_list(); } else if(comp == &sorteddir) { vehiclelist_stats_t::reverse = !vehiclelist_stats_t::reverse; sorteddir.set_text( vehiclelist_stats_t::reverse ? "hl_btn_sort_desc" : "hl_btn_sort_asc"); scrolly.sort(0); sorteddir.pressed = vehiclelist_stats_t::reverse; } else if(comp == &bt_obsolete) { bt_obsolete.pressed ^= 1; fill_list(); } else if(comp == &bt_future) { bt_future.pressed ^= 1; fill_list(); } else if(comp == &tabs) { fill_list(); } else if(comp == &name_filter_input) { fill_list(); } return true; } void vehiclelist_frame_t::fill_list() { scrolly.clear_elements(); vehiclelist_stats_t::img_width = 32; // reset col1 width uint32 month = world()->get_current_month(); const goods_desc_t *ware = idx_to_ware[ max( 0, ware_filter.get_selection() ) ]; // adding all vehiles, i.e. iterate over all available waytypes for (uint32 i = 1; i < tabs.get_count(); i++) { if( tabs.get_active_tab_index()>0 && (sint32)i!=tabs.get_active_tab_index() ) { // wrong waytype continue; } for(vehicle_desc_t const* const veh : vehicle_builder_t::get_info(tabs.get_tab_waytype(i)) ) { if( bt_obsolete.pressed || !veh->is_retired( month ) ) { if( bt_future.pressed || !veh->is_future( month ) ) { if( name_filter[0]==0 || utf8caseutf8(translator::translate(veh->get_name()),name_filter) ) { if( ware ) { const goods_desc_t *vware = veh->get_freight_type(); if( (ware->get_catg_index() > 0 && vware->get_catg_index() == ware->get_catg_index()) || vware->get_index() == ware->get_index() ) { scrolly.new_component( veh ); } } else { scrolly.new_component( veh ); } } } } } } if( vehiclelist_stats_t::sort_mode != 0 ) { scrolly.sort(0); } else { scrolly.set_size( scrolly.get_size() ); } } void vehiclelist_frame_t::rdwr(loadsave_t* file) { scr_size size = get_windowsize(); size.rdwr(file); tabs.rdwr(file); scrolly.rdwr(file); ware_filter.rdwr(file); sort_by.rdwr(file); file->rdwr_bool(sorteddir.pressed); file->rdwr_bool(bt_obsolete.pressed); file->rdwr_bool(bt_future.pressed); if (file->is_version_atleast(123, 2)) { file->rdwr_str(name_filter, lengthof(name_filter)); } if (file->is_loading()) { vehiclelist_stats_t::reverse = sorteddir.pressed; vehiclelist_stats_t::sort_mode = sort_by.get_selection(); fill_list(); set_windowsize(size); } } simutrans-124.5/src/simutrans/gui/vehiclelist_frame.h000066400000000000000000000042261520675460500230260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_VEHICLELIST_FRAME_H #define GUI_VEHICLELIST_FRAME_H #include "simwin.h" #include "gui_frame.h" #include "components/gui_scrollpane.h" #include "components/gui_scrolled_list.h" #include "components/gui_label.h" #include "components/gui_image.h" #include "components/gui_waytype_tab_panel.h" #include "components/gui_combobox.h" #include "components/gui_textinput.h" #include "components/gui_fixedwidth_textarea.h" class vehicle_desc_t; class goods_desc_t; class vehiclelist_frame_t : public gui_frame_t, private action_listener_t { private: gui_textinput_t name_filter_input; button_t sorteddir, bt_obsolete, bt_future; gui_scrolled_list_t scrolly; gui_waytype_tab_panel_t tabs; gui_combobox_t sort_by, ware_filter; vector_tplidx_to_ware; char name_filter[256]; public: vehiclelist_frame_t(); void fill_list(); const char *get_help_filename() const OVERRIDE {return "vehiclelist.txt"; } bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; void rdwr(loadsave_t* file) OVERRIDE; uint32 get_rdwr_id() OVERRIDE { return magic_vehiclelist; } }; class vehiclelist_stats_t : public gui_scrolled_list_t::scrollitem_t { private: const vehicle_desc_t *veh; cbuffer_t part1, part2, details_buf; int name_width; int col1_width; int col2_width; int height; gui_fixedwidth_textarea_t details; public: static int sort_mode; static bool reverse; static int img_width; vehiclelist_stats_t(const vehicle_desc_t *); char const* get_text() const OVERRIDE; scr_size get_size() const OVERRIDE { return scr_size( D_MARGIN_LEFT+img_width+max(col1_width+col2_width,name_width)+D_MARGIN_RIGHT, height ); } scr_size get_min_size() const OVERRIDE { return scr_size( D_MARGIN_LEFT+img_width+max(col1_width+col2_width,name_width)+D_MARGIN_RIGHT, height ); } scr_size get_max_size() const OVERRIDE { return scr_size( D_MARGIN_LEFT+img_width+max(col1_width+col2_width,name_width)+D_MARGIN_RIGHT, height ); } static bool compare(const gui_component_t *a, const gui_component_t *b ); void draw( scr_coord offset ) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/gui/welt.cc000066400000000000000000000464021520675460500204540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "welt.h" #include "minimap.h" #include "../simdebug.h" #include "../world/simworld.h" #include "simwin.h" #include "../display/simimg.h" #include "../simmesg.h" #include "../simskin.h" #include "../simversion.h" #include "../builder/hausbauer.h" #include "../builder/wegbauer.h" #include "../descriptor/building_desc.h" #include "../dataobj/height_map_loader.h" #include "../dataobj/settings.h" #include "../dataobj/loadsave.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../dataobj/tabfile.h" // just for their structure size ... #include "../obj/way/schiene.h" #include "../obj/baum.h" #include "../world/simcity.h" #include "../vehicle/vehicle.h" #include "../player/simplay.h" #include "../simconvoi.h" #include "../simcolor.h" #include "../display/simgraph.h" #include "../sys/simsys.h" #include "../tool/simmenu.h" #include "../utils/simstring.h" #include "../utils/simrandom.h" #include "components/gui_divider.h" #include "banner.h" #include "sprachen.h" #include "climates.h" #include "settings_frame.h" #include "load_relief_frame.h" #include "messagebox.h" // Local adjustment #define L_PREVIEW_SIZE_MIN (16) welt_gui_t::welt_gui_t() : gui_frame_t( translator::translate("Neue Welt" ) ), map(0,0) { { // reread from simucon.tab(s) the settings and apply them tabfile_t simuconf; sets = new settings_t(); *sets = env_t::default_settings; dr_chdir(env_t::base_dir); if (simuconf.open("config/simuconf.tab")) { sets->parse_simuconf(simuconf); sets->parse_colours(simuconf); } stadt_t::cityrules_init(); dr_chdir(env_t::pak_dir.c_str()); if (simuconf.open("config/simuconf.tab")) { sets->parse_simuconf(simuconf); sets->parse_colours(simuconf); } dr_chdir(env_t::user_dir); if (simuconf.open("simuconf.tab")) { sets->parse_simuconf(simuconf); sets->parse_colours(simuconf); } simuconf.close(); } sets->beginner_mode = env_t::default_settings.get_beginner_mode(); city_density = ( sets->get_city_count() ) ? sqrt((double)sets->get_size_x()*sets->get_size_y()) / sets->get_city_count() : 0.0; industry_density = ( sets->get_factory_count() ) ? sqrt((double)sets->get_size_x()*sets->get_size_y()) / sets->get_factory_count() : 0.0; attraction_density = ( sets->get_tourist_attractions() ) ? sqrt((double)sets->get_size_x()*sets->get_size_y()) / sets->get_tourist_attractions() : 0.0; river_density = ( sets->get_river_number() ) ? sqrt((double)sets->get_size_x()*sets->get_size_y()) / sets->get_river_number() : 0.0; // find earliest start and end date ... uint16 game_start = 4999; uint16 game_ends = 0; // first check town halls for(building_desc_t const* const desc : *hausbauer_t::get_list(building_desc_t::townhall)) { uint16 intro_year = (desc->get_intro_year_month()+11)/12; if( intro_yearget_retire_year_month()+11)/12; if( retire_year>game_ends ) { game_ends = retire_year; } } // then streets game_start = max( game_start, (way_builder_t::get_earliest_way(road_wt)->get_intro_year_month()+11)/12 ); game_ends = min( game_ends, (way_builder_t::get_latest_way(road_wt)->get_retire_year_month()+11)/12 ); loaded_heightfield = load_heightfield = false; sets->heightfield = ""; //****************************************************************** // Component creation set_table_layout(1,0); // top part: preview, maps size add_table(3,1); { // input fields add_table(2,0); { new_component("2WORLD_CHOOSE"); inp_map_number.init( abs(sets->get_map_number()), 0, 0x7FFFFFFF, 1, true ); inp_map_number.add_listener( this ); add_component( &inp_map_number ); // Map size label size_label.init(); size_label.buf().printf(translator::translate("Size (%d MB):"), 99999); size_label.update(); size_label.set_min_width(size_label.get_min_size().w); // Make sure to not make the component size too small when the window is opened with a small map size that is increased afterwards add_component( &size_label ); add_table(3,0); { // Map X size edit inp_x_size.init( sets->get_size_x(), 8, 32766, sets->get_size_x()>=512 ? 128 : 64, false ); inp_x_size.add_listener(this); add_component( &inp_x_size ); new_component("x"); // Map size Y edit inp_y_size.init( sets->get_size_y(), 8, 32766, sets->get_size_y()>=512 ? 128 : 64, false ); inp_y_size.add_listener(this); add_component( &inp_y_size ); } end_table(); } end_table(); new_component(); // Map preview (will be initialized in update_preview add_component( &map_preview ); } end_table(); // two buttons add_table(2,0)->set_force_equal_columns(true); { // Random map button random_map.init(button_t::roundbox | button_t::flexible, "Random map"); random_map.set_tooltip("chooses a random map"); random_map.add_listener( this ); add_component( &random_map ); // Load height map button load_map.init(button_t::roundbox | button_t::flexible, "Lade Relief"); load_map.set_tooltip("load height data from file"); load_map.add_listener( this ); add_component( &load_map ); } end_table(); // specify map parameters add_table(2,0); { // Number of towns new_component("5WORLD_CHOOSE"); inp_number_of_towns.add_listener(this); inp_number_of_towns.init(abs(sets->get_city_count()), 0, 999); add_component( &inp_number_of_towns ); // Town size new_component("Median Citizen per town"); inp_town_size.add_listener(this); inp_town_size.set_limits(0,999999); inp_town_size.set_increment_mode(50); inp_town_size.set_value( sets->get_mean_citizen_count() ); add_component( &inp_town_size ); // Intercity road length new_component("Intercity road len:"); inp_intercity_road_len.add_listener(this); inp_intercity_road_len.set_limits(0,9999); inp_intercity_road_len.set_value( env_t::intercity_road_length ); inp_intercity_road_len.set_increment_mode( env_t::intercity_road_length>=1000 ? 100 : 20 ); add_component( &inp_intercity_road_len ); // Factories new_component("No. of Factories"); inp_other_industries.add_listener(this); inp_other_industries.set_limits(0,999); inp_other_industries.set_value(abs(sets->get_factory_count()) ); add_component( &inp_other_industries ); // Tourist attr. new_component("Tourist attractions"); inp_tourist_attractions.add_listener(this); inp_tourist_attractions.set_limits(0,999); inp_tourist_attractions.set_value(abs(sets->get_tourist_attractions()) ); add_component( &inp_tourist_attractions ); // Use timeline checkbox use_intro_dates.init(button_t::square_state, "Use timeline start year"); use_intro_dates.pressed = sets->get_use_timeline()&1; use_intro_dates.add_listener( this ); add_component( &use_intro_dates ); // Timeline year edit inp_intro_date.add_listener(this); inp_intro_date.set_limits(game_start,game_ends); inp_intro_date.set_increment_mode(10); inp_intro_date.set_value(abs(sets->get_starting_year()) ); add_component( &inp_intro_date ); // Use beginner mode checkbox use_beginner_mode.init(button_t::square_state, "Use beginner mode"); use_beginner_mode.set_tooltip("Higher transport fees, crossconnect all factories"); use_beginner_mode.pressed = sets->get_beginner_mode(); use_beginner_mode.add_listener( this ); add_component( &use_beginner_mode ); add_table(3, 0); { new_component(translator::translate("Transport fees: +")); beginner_price_factor.init((env_t::default_settings.beginner_price_factor - 1000) / 10, -100, 25000, /*step_size=*/10, /*wrap=*/false); beginner_price_factor.enable(use_beginner_mode.pressed); beginner_price_factor.add_listener(this); add_component(&beginner_price_factor); new_component("%"); } end_table(); } end_table(); new_component(); add_table(2,1)->set_force_equal_columns(true); { // Map settings button open_setting_gui.init(button_t::roundbox | button_t::flexible, "Setting"); open_setting_gui.pressed = win_get_magic( magic_settings_frame_t ); open_setting_gui.add_listener( this ); add_component( &open_setting_gui ); // Landscape settings button open_climate_gui.init(button_t::roundbox | button_t::flexible,"Climate Control"); open_climate_gui.pressed = win_get_magic( magic_climate ); open_climate_gui.add_listener( this ); add_component( &open_climate_gui ); } end_table(); new_component(); add_table(2,0)->set_force_equal_columns(true); { // start game start_game.init(button_t::roundbox | button_t::flexible, "Starte Spiel"); start_game.add_listener( this ); add_component( &start_game ); // return to menu return_menu.init(button_t::roundbox | button_t::flexible,"Return to menu"); return_menu.add_listener( this ); add_component( &return_menu ); } end_table(); update_preview(); update_memory(&size_label, sets); reset_min_windowsize(); set_windowsize(get_min_windowsize()); set_resizemode(gui_frame_t::diagonal_resize); } /** * Calculates preview from height map * @param filename name of heightfield file */ bool welt_gui_t::update_from_heightfield(const char *filename) { DBG_MESSAGE("welt_gui_t::update_from_heightfield()", "%s", filename); const sint8 min_h = env_t::default_settings.get_minimumheight(); const sint8 max_h = env_t::default_settings.get_maximumheight(); height_map_loader_t hml(min_h, max_h, env_t::height_conv_mode); sint16 w, h; sint8 *h_field=NULL; if(hml.get_height_data_from_file(filename, (sint8)sets->get_groundwater(), h_field, w, h, false )) { sets->set_size_x(w); sets->set_size_y(h); update_densities(); inp_x_size.set_value(sets->get_size_x()); inp_y_size.set_value(sets->get_size_y()); resize_preview(); const int mx = sets->get_size_x()/map_size.w; const int my = sets->get_size_y()/map_size.h; for( int y=0; yget_groundwater()-1 ); } } map_preview.set_map_data(&map); free(h_field); return true; } return false; } // sets the new values for the number input filed for the densities void welt_gui_t::update_densities() { if( city_density!=0.0 ) { inp_number_of_towns.set_value( max( 1, (sint32)(0.5+sqrt((double)sets->get_size_x()*sets->get_size_y())/city_density) ) ); sets->set_city_count( inp_number_of_towns.get_value() ); } if( industry_density!=0.0 ) { inp_other_industries.set_value( max( 1, (sint32)(0.5+sqrt((double)sets->get_size_x()*sets->get_size_y())/industry_density) ) ); sets->set_factory_count( inp_other_industries.get_value() ); } if( attraction_density!=0.0 ) { inp_tourist_attractions.set_value( max( 1, (sint32)(0.5+sqrt((double)sets->get_size_x()*sets->get_size_y())/attraction_density) ) ); sets->set_tourist_attractions( inp_tourist_attractions.get_value() ); } if( river_density!=0.0 ) { sets->river_number = max( 1, (sint32)(0.5+sqrt((double)sets->get_size_x()*sets->get_size_y())/river_density) ); if( climate_gui_t *climate_gui = (climate_gui_t *)win_get_magic( magic_climate ) ) { climate_gui->update_river_number( sets->get_river_number() ); } } } void welt_gui_t::update_memory(gui_label_buf_t *label, const settings_t* sets) { // Calculate map memory const uint sx = sets->get_size_x(); const uint sy = sets->get_size_y(); const uint64 memory = ( (uint64)sizeof(karte_t) + sizeof(player_t) * 8 + sizeof(convoi_t) * 1000 + (sizeof(schiene_t) + sizeof(vehicle_t)) * 10 * (sx + sy) + sizeof(stadt_t) * sets->get_city_count() + ( sizeof(grund_t) + sizeof(planquadrat_t) + sizeof(baum_t)*(sets->get_tree_distribution()!=settings_t::TREE_DIST_NONE) + /* only one since a lot will be water */ sizeof(void*)*2 ) * (uint64)sx * (uint64)sy ) / (1024ll * 1024ll); label->buf().printf(translator::translate("Size (%d MB):"), memory); label->update(); } /** * Calculate the new Map-Preview. Initialize the new RNG! */ void welt_gui_t::update_preview(bool load_heightfield) { if( loaded_heightfield || load_heightfield) { loaded_heightfield = true; update_from_heightfield(sets->heightfield.c_str()); } else { resize_preview(); setsimrand( 0xFFFFFFFF, sets->get_map_number() ); const int mx = sets->get_size_x()/map_size.w; const int my = sets->get_size_y()/map_size.h; for( int y=0; yget_groundwater()); } } sets->heightfield = ""; } map_preview.set_map_data(&map); } void welt_gui_t::resize_preview() { const float world_aspect = (float)sets->get_size_x() / (float)sets->get_size_y(); if( world_aspect > 1.0 ) { map_size.w = MAP_PREVIEW_SIZE_X-2; map_size.h = (sint16) max( (int)((float)map_size.w / world_aspect), L_PREVIEW_SIZE_MIN-2); } else { map_size.h = MAP_PREVIEW_SIZE_Y-2; map_size.w = (sint16) max( (int)((float)map_size.h * world_aspect), L_PREVIEW_SIZE_MIN-2); } map.resize( map_size.w, map_size.h ); } /** * This method is called if an action is triggered */ bool welt_gui_t::action_triggered( gui_action_creator_t *comp,value_t v) { if (!sets) { // new map has been already created => ignored late events return true; } // check for changed map (update preview for any event) int knr = inp_map_number.get_value(); // if(comp==&inp_map_number) { sets->heightfield = ""; loaded_heightfield = false; } else if(comp==&inp_x_size) { if( !loaded_heightfield ) { sets->set_size_x( v.i ); inp_x_size.set_increment_mode( v.i>=64 ? (v.i>=512 ? 128 : 64) : 8 ); inp_y_size.set_limits( 8, 32766 ); update_densities(); } else { inp_x_size.set_value(sets->get_size_x()); // can't change size with heightfield loaded } } else if(comp==&inp_y_size) { if( !loaded_heightfield ) { sets->set_size_y( v.i ); inp_y_size.set_increment_mode( v.i>=64 ? (v.i>=512 ? 128 : 64) : 8 ); inp_x_size.set_limits( 8, 32766 ); update_densities(); } else { inp_y_size.set_value(sets->get_size_y()); // can't change size with heightfield loaded } } else if (comp == &inp_number_of_towns) { sets->set_city_count(v.i); city_density = sets->get_city_count() ? sqrt((double)sets->get_size_x() * sets->get_size_y()) / sets->get_city_count() : 0.0; } else if (comp == &inp_town_size) { sets->set_mean_citizen_count(v.i); } else if (comp == &inp_intercity_road_len) { env_t::intercity_road_length = v.i; inp_intercity_road_len.set_increment_mode(v.i >= 1000 ? 100 : 20); } else if (comp == &inp_other_industries) { sets->set_factory_count(v.i); industry_density = sets->get_factory_count() ? sqrt((double)sets->get_size_x() * sets->get_size_y()) / sets->get_factory_count() : 0.0; } else if (comp == &inp_tourist_attractions) { sets->set_tourist_attractions(v.i); attraction_density = sets->get_tourist_attractions() ? sqrt((double)sets->get_size_x() * sets->get_size_y()) / sets->get_tourist_attractions() : 0.0; } else if (comp == &inp_intro_date) { sets->set_starting_year((sint16)(v.i)); } else if (comp == &random_map) { knr = sim_async_rand(9999); inp_map_number.set_value(knr); sets->heightfield = ""; loaded_heightfield = false; } else if (comp == &load_map) { // load relief loaded_heightfield = false; sets->heightfield = ""; load_relief_frame_t* lrf = new load_relief_frame_t(sets); create_win(lrf, w_info, magic_load_t); const scr_coord new_pos{ (gfx->get_screen_size().w - lrf->get_windowsize().w - 10), env_t::iconsize.h }; win_set_pos(lrf, new_pos); knr = sets->get_map_number(); // otherwise using cancel would not show the normal generated map again } else if (comp == &use_intro_dates) { // 0,1 should force setting to new game as well. don't allow to change // 2,3 allow to change if (sets->get_use_timeline() & 2) { // don't change bit1. bit1 affects loading saved game sets->set_use_timeline(sets->get_use_timeline() ^ 1); use_intro_dates.pressed = sets->get_use_timeline() & 1; } } else if (comp == &use_beginner_mode) { sets->beginner_mode = sets->get_beginner_mode() ^ 1; use_beginner_mode.pressed = sets->get_beginner_mode(); beginner_price_factor.enable(use_beginner_mode.pressed); } else if (comp == &beginner_price_factor) { sets->beginner_price_factor = 1000 + 10 * beginner_price_factor.get_value(); } else if (comp == &open_setting_gui) { gui_frame_t* sg = win_get_magic(magic_settings_frame_t); if (sg) { destroy_win(sg); open_setting_gui.pressed = false; } else { create_win({ 10, 40 }, new settings_frame_t(sets), w_info, magic_settings_frame_t); open_setting_gui.pressed = true; } } else if (comp == &open_climate_gui) { gui_frame_t* climate_gui = win_get_magic(magic_climate); if (climate_gui) { destroy_win(climate_gui); open_climate_gui.pressed = false; } else { climate_gui_t* cg = new climate_gui_t(sets); const scr_coord_val xoff = min(win_get_pos(this).x + this->size.w, gfx->get_screen_size().w - cg->get_windowsize().w); const scr_coord_val yoff = win_get_pos(this).y; create_win({ xoff, yoff }, cg, w_info, magic_climate); open_climate_gui.pressed = true; } } else if (comp == &start_game) { destroy_all_win(true); if (!loaded_heightfield) { env_t::default_settings.heightfield = ""; } env_t::default_settings = *sets; welt->get_message()->clear(); delete sets; sets = NULL; // save setting ... loadsave_t file; chdir(env_t::user_dir); if (file.wr_open("default.sve", loadsave_t::binary, 0, "settings only", SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK) { // save default setting env_t::default_settings.rdwr(&file); file.close(); } tool_t::simple_tool[TOOL_WORK_WORLD]->set_default_param("n"); welt->set_tool(tool_t::simple_tool[TOOL_WORK_WORLD], NULL); } else if(comp==&return_menu) { destroy_all_win(true); banner_t::show_banner(); } if(knr>=0 && sets) { sets->map_number = knr; if(!loaded_heightfield) { update_preview(); } } return true; } void welt_gui_t::draw(scr_coord pos, scr_size size) { // Coordinates are relative to parent (TITLEHEIGHT **NOT** subtracted) cbuffer_t buf; // Update child controls before redraw if(!loaded_heightfield && sets->heightfield.size()!=0) { if(update_from_heightfield(sets->heightfield.c_str())) { loaded_heightfield = true; } else { loaded_heightfield = false; sets->heightfield = ""; } } // simulate toggle buttons open_setting_gui.pressed = win_get_magic( magic_settings_frame_t ); open_climate_gui.pressed = false; if( win_get_magic( magic_climate ) ) { open_climate_gui.pressed = true; // check if number was directly changed sint16 new_river_number = max( 1, (sint32)(0.5+sqrt((double)sets->get_size_x()*sets->get_size_y())/river_density) ); if( sets->get_river_number() != new_river_number ) { river_density = sets->get_river_number() ? sqrt((double)sets->get_size_x()*sets->get_size_y()) / sets->get_river_number() : 0.0; } } use_intro_dates.pressed = sets->get_use_timeline()&1; use_beginner_mode.pressed = sets->get_beginner_mode(); update_memory(&size_label, sets); // draw child controls gui_frame_t::draw(pos, size); } simutrans-124.5/src/simutrans/gui/welt.h000066400000000000000000000047401520675460500203150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef GUI_WELT_H #define GUI_WELT_H #include "gui_frame.h" #include "components/gui_button.h" #include "components/gui_label.h" #include "components/action_listener.h" #include "components/gui_textinput.h" #include "components/gui_numberinput.h" #include "components/gui_map_preview.h" #include "../tpl/array2d_tpl.h" class settings_t; /** * The dialog for new world generation */ class welt_gui_t : public gui_frame_t, private action_listener_t { settings_t *sets; /// Mini Map-Preview array2d_tpl map; scr_size map_size; bool load_heightfield; bool loaded_heightfield; double city_density; double industry_density; double attraction_density; double river_density; gui_map_preview_t map_preview; gui_numberinput_t inp_map_number, inp_x_size, inp_y_size; button_t random_map, load_map; gui_numberinput_t inp_number_of_towns, inp_town_size, inp_intercity_road_len, inp_other_industries, inp_tourist_attractions, inp_intro_date; gui_label_buf_t size_label; button_t use_intro_dates, use_beginner_mode, open_climate_gui, open_setting_gui, start_game, return_menu; gui_numberinput_t beginner_price_factor; /** * Calculates preview from height map * @param filename name of heightfield file */ bool update_from_heightfield(const char *filename); void resize_preview(); void update_densities(); public: welt_gui_t(); public: /** * Berechnet Preview-Karte neu. Inititialisiert RNG neu! * public, because also the climate dialog need it */ void update_preview(bool load_heightfield = false); void clear_loaded_heightfield() { loaded_heightfield =0; } bool get_loaded_heightfield() const { return loaded_heightfield; } /** * Set the window associated helptext * @return the filename for the helptext, or NULL */ const char * get_help_filename() const OVERRIDE {return "new_world.txt";} settings_t* get_sets() const { return sets; } // does not work during new world dialog bool has_sticky() const OVERRIDE { return false; } /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the * component is displayed. */ void draw(scr_coord pos, scr_size size) OVERRIDE; bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE; static void update_memory(gui_label_buf_t *label, const settings_t* sets); }; #endif simutrans-124.5/src/simutrans/halthandle.h000066400000000000000000000004061520675460500206550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef HALTHANDLE_H #define HALTHANDLE_H #include "tpl/quickstone_tpl.h" class haltestelle_t; typedef quickstone_tpl halthandle_t; #endif simutrans-124.5/src/simutrans/io/000077500000000000000000000000001520675460500170075ustar00rootroot00000000000000simutrans-124.5/src/simutrans/io/classify_file.cc000066400000000000000000000173571520675460500221470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "classify_file.h" #include "rdwr/raw_file_rdwr_stream.h" #ifndef MAKEOBJ #include "rdwr/bzip2_file_rdwr_stream.h" #include "rdwr/zlib_file_rdwr_stream.h" #endif #ifdef USE_ZSTD #include "rdwr/zstd_file_rdwr_stream.h" #endif #include "../dataobj/loadsave.h" #include "../simdebug.h" #include "../simversion.h" #include "../sys/simsys.h" #include "../utils/simstring.h" #include "../macros.h" #include #ifdef MAKEOBJ #define dr_fopen fopen #endif bool classify_as_png(FILE *f, file_info_t *info); bool classify_as_bmp(FILE *f, file_info_t *info); bool classify_as_ppm(FILE *f, file_info_t *info); bool classify_as_zstd(FILE *f, file_info_t *info); bool classify_as_bzip2(FILE *f, file_info_t *info); bool classify_as_zip(FILE *f, file_info_t *info); bool classify_file_data(rdwr_stream_t *stream, file_info_t *info); file_info_t::file_info_t() : file_type(TYPE_RAW), version(INVALID_FILE_VERSION), header_size(0) { pak_extension[0] = 0; } file_info_t::file_info_t(file_type_t file_type, uint32 version) : file_type(file_type), version(version) { pak_extension[0] = 0; } file_classify_status_t classify_save_file(const char *path, file_info_t *info) { if (!info) { dbg->error("classify_file()", "Cannot classify file: info is NULL"); return FILE_CLASSIFY_INVALID_ARGS; } else if( !path || !*path ) { dbg->error("classify_file()", "Invalid path"); return FILE_CLASSIFY_INVALID_ARGS; } FILE *f = dr_fopen(path, "rb"); if (!f) { // Do not warn about this since we can also use this function to check whether a file exists return FILE_CLASSIFY_NOT_EXISTING; } #ifdef MAKEOBJ info->file_type = file_info_t::TYPE_RAW; info->version = INVALID_FILE_VERSION; info->header_size = 0; return FILE_CLASSIFY_OK; #else fseek(f, 0, SEEK_SET); if (classify_as_zstd(f, info)) { fclose(f); #if USE_ZSTD // otherwise we cannot read it zstd_file_rdwr_stream_t s(path, false, 0); if (!classify_file_data(&s, info)) { #else { #endif info->file_type = file_info_t::TYPE_ZSTD; info->version = INVALID_FILE_VERSION; info->header_size = 0; } return FILE_CLASSIFY_OK; } fseek(f, 0, SEEK_SET); if (classify_as_bzip2(f, info)) { fclose(f); bzip2_file_rdwr_stream_t s(path, false); if (!classify_file_data(&s, info)) { info->file_type = file_info_t::TYPE_RAW; info->version = INVALID_FILE_VERSION; info->header_size = 0; } return FILE_CLASSIFY_OK; } fseek(f, 0, SEEK_SET); if (classify_as_zip(f, info)) { fclose(f); zlib_file_rdwr_stream_t s(path, false, 0); if (!classify_file_data(&s, info)) { info->file_type = file_info_t::TYPE_RAW; info->version = INVALID_FILE_VERSION; info->header_size = 0; } return FILE_CLASSIFY_OK; } fseek(f, 0, SEEK_SET); raw_file_rdwr_stream_t s(f, false); if (!classify_file_data(&s, info)) { info->file_type = file_info_t::TYPE_RAW; info->version = INVALID_FILE_VERSION; info->header_size = 0; } return FILE_CLASSIFY_OK; #endif // MAKEOBJ } file_classify_status_t classify_image_file(const char *path, file_info_t *info) { if (!info) { dbg->error("classify_file()", "Cannot classify file: info is NULL"); return FILE_CLASSIFY_INVALID_ARGS; } else if( !path || !*path ) { dbg->error("classify_file()", "Invalid path"); return FILE_CLASSIFY_INVALID_ARGS; } FILE *f = dr_fopen(path, "rb"); if (!f) { // Do not warn about this since we can also use this function to check whether a file exists return FILE_CLASSIFY_NOT_EXISTING; } fseek(f, 0, SEEK_SET); if (classify_as_png(f, info)) { fclose(f); return FILE_CLASSIFY_OK; } fseek(f, 0, SEEK_SET); if (classify_as_bmp(f, info)) { fclose(f); return FILE_CLASSIFY_OK; } fseek(f, 0, SEEK_SET); if (classify_as_ppm(f, info)) { fclose(f); return FILE_CLASSIFY_OK; } info->file_type = file_info_t::TYPE_RAW; info->version = INVALID_FILE_VERSION; info->header_size = 0; return FILE_CLASSIFY_OK; } bool classify_as_png(FILE *f, file_info_t *info) { char buf[80]; if (fread(buf, 1, 8, f) != 8) { return false; } if (memcmp(buf, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) != 0) { return false; } info->file_type = file_info_t::TYPE_PNG; return true; } bool classify_as_bmp(FILE *f, file_info_t *info) { char buf[80]; if (fread(buf, 1, 2, f) != 2) { return false; } if (memcmp(buf, "BM", 2) != 0) { return false; } info->file_type = file_info_t::TYPE_BMP; return true; } bool classify_as_ppm(FILE *f, file_info_t *info) { char buf[80]; if (fread(buf, 1, 2, f) != 2) { return false; } if (memcmp(buf, "P6", 2) != 0) { return false; } info->file_type = file_info_t::TYPE_PPM; return true; } #ifndef MAKEOBJ bool classify_as_bzip2(FILE *f, file_info_t *info) { char buf[80]; if (fread(buf, 1, 2, f) != 2) { return false; } if( memcmp(buf, "BZ", 2) != 0) { return false; // not bzip2 compressed } info->file_type = file_info_t::TYPE_BZIP2; return true; } bool classify_as_zstd(FILE *f, file_info_t *info) { char buf[80]; if (fread(buf, 1, 2, f) != 2) { return false; } if( memcmp(buf, "ZD", 2) != 0) { return false; // not zstd compressed } info->file_type = file_info_t::TYPE_ZSTD; return true; } bool classify_as_zip(FILE *f, file_info_t *info) { char buf[80]; if (fread(buf, 1, 2, f) != 2) { return false; } if( memcmp(buf, "\x1f\x8b", 2) != 0 && buf[0] != 0x78 ) { return false; // not zlib/gzip compressed } info->file_type = file_info_t::TYPE_ZIPPED; return true; } bool classify_file_data(rdwr_stream_t *stream, file_info_t *info) { char buf[80]; MEMZERO(buf); if( stream->read( buf, sizeof( SAVEGAME_PREFIX ) ) != sizeof( SAVEGAME_PREFIX ) ) { info->version = INVALID_FILE_VERSION; return false; // file too short } info->header_size = sizeof(SAVEGAME_PREFIX); // get the rest of the string for( int i = sizeof( SAVEGAME_PREFIX ); i < 79; ) { char ch; stream->read(&ch, 1); info->header_size++; if( ch < ' ' ) { break; } buf[ i++ ] = (char)ch; buf[ i ] = 0; } if (strstart(buf, SAVEGAME_PREFIX)) { info->version = loadsave_t::int_version(buf + sizeof(SAVEGAME_PREFIX) - 1, info->pak_extension); if( info->version == 0 ) { info->version = INVALID_FILE_VERSION; return false; } } else if (strstart(buf, XML_SAVEGAME_PREFIX)) { info->file_type |= file_info_t::TYPE_XML; char ch; do { info->header_size++; stream->read(&ch, 1); } while (ch != '<'); stream->read(buf, sizeof(SAVEGAME_PREFIX) - 1); info->header_size += sizeof(SAVEGAME_PREFIX) -1; if (!strstart(buf, SAVEGAME_PREFIX)) { // not a simutrans XML file ... info->version = INVALID_FILE_VERSION; return false; } stream->read(buf, sizeof("version=\"") - 1); info->header_size += sizeof("version=\"") - 1; char str[256]; char *s = str; for (int i = 0; i < 255; i++) { char c; stream->read(&c, 1); info->header_size++; if (c=='\"') { break; } *s++ = c; } *s = 0; info->version = loadsave_t::int_version(str, info->pak_extension); if( info->version == 0 ) { info->version = INVALID_FILE_VERSION; info->header_size = 0; return false; } stream->read(buf, sizeof(" pak=\"") - 1); info->header_size += sizeof(" pak=\"") - 1; if (info->version > 0) { s = info->pak_extension; for (int i = 0; i < 63; i++) { char c; stream->read(&c, 1); info->header_size++; if (c=='\"') { break; } *s++ = c; } *s = 0; char c; do { info->header_size++; stream->read(&c, 1); } while (c != '>'); } } else { // not a Simutrans specific file info->version = INVALID_FILE_VERSION; info->header_size = 0; return false; } if(*info->pak_extension == 0) { strcpy( info->pak_extension, "(unknown)" ); } return true; } #endif simutrans-124.5/src/simutrans/io/classify_file.h000066400000000000000000000041521520675460500217760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef IO_CLASSIFY_FILE_H #define IO_CLASSIFY_FILE_H #include "../simtypes.h" enum file_classify_status_t { FILE_CLASSIFY_OK = 0, FILE_CLASSIFY_INVALID_ARGS, FILE_CLASSIFY_NOT_EXISTING }; #define INVALID_FILE_VERSION 0xFFFFFFFFu /** * Holds information about file type, format and version. * Use @ref classify_save_file and @ref classify_image_file to classify * save files and image files, respectively. */ struct file_info_t { enum file_type_t { TYPE_RAW = 0, // either raw binary or text (dat etc) TYPE_ZIPPED, // zipped save TYPE_BZIP2, // bzip2 compressed save TYPE_ZSTD, // zstd compressed save TYPE_PNG, // PNG image TYPE_BMP, TYPE_PPM, TYPE_XML = 1u << 31, // Combined file formats TYPE_XML_ZIPPED = TYPE_XML | TYPE_ZIPPED, TYPE_XML_BZIP2 = TYPE_XML | TYPE_BZIP2, TYPE_XML_ZSTD = TYPE_XML | TYPE_ZSTD }; public: file_info_t(); explicit file_info_t(file_type_t file_type, uint32 version = INVALID_FILE_VERSION); public: file_type_t file_type; uint32 version; ///< Version of saved game. char pak_extension[64]; ///< Pak extension of saved game. size_t header_size; ///< Header size of saved game in bytes }; ENUM_BITSET(file_info_t::file_type_t); /** * Classify a save file. * @param path must a valid system name, either a short name for windows or UTF8 for other plattforms * @param info If successfully classified, holds information about file format and version. * Must not be NULL. * @returns FILE_CLASSIFY_OK iff successfully classified. */ file_classify_status_t classify_save_file(const char *path, file_info_t *info); /** * Classify an image file. * @param path must a valid system name, either a short name for windows or UTF8 for other plattforms * @param info If successfully classified, holds information about file format and version. * Must not be NULL. * @returns FILE_CLASSIFY_OK iff successfully classified. */ file_classify_status_t classify_image_file(const char *path, file_info_t *info); #endif simutrans-124.5/src/simutrans/io/raw_image.cc000066400000000000000000000037121520675460500212540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "raw_image.h" #include "classify_file.h" #include "../simmem.h" #include #include #include #include raw_image_t::raw_image_t() : data(NULL) , width(0) , height(0) , fmt(FMT_INVALID) , bpp(0) { } raw_image_t::raw_image_t(uint32 width, uint32 height, raw_image_t::format_t format) : data(NULL) , width(width) , height(height) , fmt(format) , bpp(bpp_for_format(format)) { data = MALLOCN(uint8, width * height * (bpp/CHAR_BIT)); } raw_image_t::raw_image_t(const raw_image_t& rhs) : data(NULL), width(rhs.width), height(rhs.height), fmt(rhs.fmt), bpp(rhs.bpp) { const size_t data_size = width * height * (bpp/CHAR_BIT); data = MALLOCN(uint8, data_size); memcpy(data, rhs.data, data_size); } raw_image_t::~raw_image_t() { if (data) { free(data); } } raw_image_t &raw_image_t::operator=(raw_image_t rhs) { swap(*this, rhs); return *this; } void raw_image_t::copy_from(const raw_image_t &rhs) { *this = rhs; } bool raw_image_t::read_from_file(const char *filename) { file_info_t finfo; const file_classify_status_t status = classify_image_file(filename, &finfo); if (status != FILE_CLASSIFY_OK) { return false; } switch (finfo.file_type) { case file_info_t::TYPE_PNG: return read_png(filename); case file_info_t::TYPE_BMP: return read_bmp(filename); case file_info_t::TYPE_PPM: return read_ppm(filename); default: return false; } } uint8 raw_image_t::bpp_for_format(raw_image_t::format_t format) { switch (format) { case FMT_INVALID: return 0; case FMT_GRAY8: return 8; case FMT_RGB888: return 24; case FMT_RGBA8888: return 32; default: assert(false); return 0; } } void swap(raw_image_t &lhs, raw_image_t &rhs) { using std::swap; swap(lhs.data, rhs.data); swap(lhs.width, rhs.width); swap(lhs.height, rhs.height); swap(lhs.fmt, rhs.fmt); swap(lhs.bpp, rhs.bpp); } simutrans-124.5/src/simutrans/io/raw_image.h000066400000000000000000000046251520675460500211220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef IO_RAW_IMAGE_H #define IO_RAW_IMAGE_H #include "../simtypes.h" #include #include /** * Class to store, read or write raw image data in various formats. * The data are stored without gaps, i.e. * @code * access_pixel(x, y+1) == access_pixel(x, y) + get_width() * (get_bpp()/CHAR_BIT) * @endcode * for 0 <= x < width and 0 <= y < height-1 */ class raw_image_t { public: enum format_t { FMT_INVALID = 0xFF, FMT_GRAY8 = 0, ///< 8 bpp grayscale FMT_RGBA8888, ///< 32 bpp RGBA; alpha == 0xFF is fully opaque FMT_RGB888 ///< 24 bpp RGB }; public: /// Constructs an empty invalid image. raw_image_t(); /// Allocates a writable image with the specified dimensions and format. raw_image_t(uint32 width, uint32 height, format_t format); ~raw_image_t(); friend void swap(raw_image_t &lhs, raw_image_t &rhs); private: /// Copy ctor; use copy_from for explicit copying raw_image_t(const raw_image_t &rhs); raw_image_t &operator=(raw_image_t rhs); public: format_t get_format() const { return (format_t)fmt; } uint8 get_bpp() const { return bpp; } uint32 get_width() const { return width; } uint32 get_height() const { return height; } /// Access the first byte of a pixel. The actual number of bytes per pixel and the order of color channels depends on the format. const uint8 *access_pixel(uint32 x, uint32 y) const { assert(fmt != FMT_INVALID); return data + (x + y*width) * (bpp/CHAR_BIT); } uint8 *access_pixel(uint32 x, uint32 y) { assert(fmt != FMT_INVALID); return data + (x + y*width) * (bpp/CHAR_BIT); } /// @returns the bits per pixel for @p format static uint8 bpp_for_format(format_t format); void copy_from(const raw_image_t &rhs); public: /// Read image from file. File format is detected automatically. /// @returns true on success bool read_from_file(const char *filename); /// @returns true on success bool write_png(const char *filename) const; bool write_bmp(const char *filename) const; bool write_ppm(const char *filename) const; private: bool read_bmp(const char *filename); bool read_ppm(const char *filename); bool read_png(const char *filename); bool read_png_data(FILE *file); private: uint8 *data; uint32 width; uint32 height; uint8 fmt; uint8 bpp; }; void swap(raw_image_t &lhs, raw_image_t &rhs); #endif simutrans-124.5/src/simutrans/io/raw_image_bmp.cc000066400000000000000000000301221520675460500221050ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "raw_image.h" #include "../sys/simsys.h" #include "../simdebug.h" #include "../simmem.h" #include "../tpl/array_tpl.h" #include "rdwr/raw_file_rdwr_stream.h" #define BMPINFOHEADER_OFFSET (14) #ifdef MAKEOBJ #define dr_fopen fopen #endif #ifdef _MSC_VER #pragma pack(push, 1) #endif struct bitmap_file_header_t { uint8 magic[2]; uint32 file_size; uint16 reserved1; uint16 reserved2; uint32 image_data_offset; } GCC_PACKED; struct bitmap_info_header_t { uint32 header_size; sint32 width; sint32 height; uint16 num_color_planes; uint16 bpp; uint32 compression; uint32 image_size; uint32 horizontal_resolution; uint32 vertical_resolution; uint32 num_palette_colors; uint32 num_important_colors; } GCC_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif enum compression_method_t { BI_RGB = 0, BI_RLE8 = 1, BI_RLE4 = 2 }; static bool is_format_supported(uint16 bpp, uint32 compression) { if (compression == BI_RGB) { return bpp == 8 || bpp == 24; } else if (compression == BI_RLE8) { return bpp == 8; } else { return false; } } bool raw_image_t::read_bmp(const char *filename) { FILE *file = dr_fopen(filename, "rb"); bitmap_file_header_t bmp_header; if (fread(&bmp_header, sizeof(bitmap_file_header_t), 1, file) != 1) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } else if (bmp_header.magic[0] != 'B' || bmp_header.magic[1] != 'M') { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } const uint32 image_data_offset = endian(bmp_header.image_data_offset); bitmap_info_header_t bmpinfo_header; if (fseek(file, BMPINFOHEADER_OFFSET, SEEK_SET) != 0 || fread(&bmpinfo_header, sizeof(bitmap_info_header_t), 1, file) != 1) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } const uint32 bmpinfoheader_size = endian(bmpinfo_header.header_size); if (bmpinfo_header.header_size < sizeof(bitmap_info_header_t)) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } sint32 width = endian(bmpinfo_header.width); sint32 height = endian(bmpinfo_header.height); const uint16 bit_depth = endian(bmpinfo_header.bpp); const uint32 compression = endian(bmpinfo_header.compression); uint32 table = endian(bmpinfo_header.num_palette_colors); // We only allow the following image formats: // 8 bit: BI_RGB (uncompressed) // BI_RLE8 (8 bit RLE) // 24 bit: BI_RGB if( !is_format_supported(bit_depth, compression) ) { dbg->warning("raw_image_t::read_bmp", "Can only use 8 bit (RLE or normal) or 24 bit bitmaps!"); fclose(file); return false; } this->width = abs(width); this->height = abs(height); this->fmt = FMT_RGBA8888; this->bpp = 32; // now read the data and convert them on the fly data = REALLOC(data, uint8, abs(width) * abs(height) * (bpp / CHAR_BIT)); if (!data) { dbg->warning("raw_image_t::read_bmp", "Not enough memory"); fclose(file); return false; } if( bit_depth==8 ) { // convert color tables to height levels if( table==0 ) { table = 256; } const uint32 colortable_offset = BMPINFOHEADER_OFFSET + bmpinfoheader_size; if( fseek( file, colortable_offset, SEEK_SET ) != 0 ) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } else if (table > 256) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } uint8 h_table[256 * 4]; for( uint32 i=0; iwarning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } h_table[4*i + 0] = R; h_table[4*i + 1] = G; h_table[4*i + 2] = B; h_table[4*i + 3] = 0xFF; } // now read the data if( fseek( file, image_data_offset, SEEK_SET ) != 0 ) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } const bool mirror = (height<0); height = abs(height); width = abs(width); if( compression==0 ) { // uncompressed (usually mirrored, if h<0) const int padding = (4 - (width & 3)) & 3; // padding at end of line for( sint32 y=0; ywarning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } data[offset + x * (bpp/CHAR_BIT) + 0] = h_table[4*res + 0]; data[offset + x * (bpp/CHAR_BIT) + 1] = h_table[4*res + 1]; data[offset + x * (bpp/CHAR_BIT) + 2] = h_table[4*res + 2]; data[offset + x * (bpp/CHAR_BIT) + 3] = h_table[4*res + 3]; } if( padding != 0 ) { // ignore missing padding at end of file if (fseek(file, padding, SEEK_CUR)!=0 && y < height-1) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } } } } else { // compressed RLE (reverse y, since mirrored) sint32 x=0, y = mirror ? 0 : height-1; while( !feof(file) && (mirror ? (y < height) : (y >= 0))) { int res = fgetc(file); if( res == EOF ) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } uint8 Count = res; res = fgetc(file); if( res == EOF ) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } const uint8 ColorIndex = res; if( Count>0 ) { for( sint32 k=0; kwarning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } x += (uint8)xx; y = mirror ? y+(uint8)yy : y-(uint8)yy; } else { // uncompressed run Count = Flag; for( sint32 k=0; kwarning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } access_pixel(x, y)[0] = h_table[4 * (uint8)idx + 0]; access_pixel(x, y)[1] = h_table[4 * (uint8)idx + 1]; access_pixel(x, y)[2] = h_table[4 * (uint8)idx + 2]; access_pixel(x, y)[3] = h_table[4 * (uint8)idx + 3]; } // always even offset in file const long int pos = ftell(file); if( pos==-1L || (pos&1 && fseek(file, 1, SEEK_CUR)!=0) ) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } } } } } } else { // uncompressed 24 bits const bool mirror = (height<0); height = abs(height); // Now read the data if( fseek( file, image_data_offset, SEEK_SET ) != 0 ) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } for( sint32 y=0; ywarning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } access_pixel(x, real_y)[0] = R; access_pixel(x, real_y)[1] = G; access_pixel(x, real_y)[2] = B; access_pixel(x, real_y)[3] = 0xFF; } // skip padding to 4 bytes at the end of each scanline const int padding = (4 - ((width*3) & 3)) & 3; if( padding != 0 && fseek( file, padding, SEEK_CUR ) != 0 ) { // Allow missing padding at end of file if (y != height-1) { dbg->warning("raw_image_t::read_bmp", "Malformed bmp file"); fclose(file); return false; } } } } fclose(file); return true; } bool raw_image_t::write_bmp(const char *filename) const { if (fmt == FMT_INVALID) { dbg->error("raw_image_t::write_bmp", "Invalid format"); return false; } else if (fmt == FMT_GRAY8) { dbg->error("raw_image_t::write_bmp", "Saving greyscale images is not implemented"); return false; } bitmap_file_header_t fheader; fheader.magic[0] = 'B'; fheader.magic[1] = 'M'; fheader.file_size = 0; // This is overwritten by the correct value below fheader.reserved1 = 0; fheader.reserved2 = 0; fheader.image_data_offset = 0; // This is overwritten by the correct value below bitmap_info_header_t iheader; iheader.header_size = sizeof(bitmap_info_header_t); iheader.width = endian(sint32(get_width())); iheader.height = endian(sint32(get_height())); iheader.num_color_planes = endian(uint16(1)); iheader.bpp = endian(uint16(bpp)); iheader.compression = endian(uint32(BI_RGB)); iheader.horizontal_resolution = 0; iheader.vertical_resolution = 0; iheader.num_palette_colors = 0; iheader.num_important_colors = 0; // size in bytes const uint32 pitch = ((get_width() * (bpp / CHAR_BIT)) + 3) & ~3; // align to 4 bytes const uint32 image_data_size = pitch * get_height(); const uint32 headers_size = uint32(sizeof(bitmap_file_header_t) + sizeof(bitmap_info_header_t)); const uint32 gap1_size = 2; // padding between headers_size and next multiple of 4 bytes fheader.image_data_offset = endian(uint32(headers_size + gap1_size)); fheader.file_size = endian(uint32(headers_size + gap1_size + image_data_size)); // now actually write the data FILE *f = dr_fopen(filename, "wb"); if (!f) { return false; } if (fwrite(&fheader, sizeof(bitmap_file_header_t), 1, f) != 1) { fclose(f); return false; } if (fwrite(&iheader, sizeof(bitmap_info_header_t), 1, f) != 1) { fclose(f); return false; } const uint8 zeroes[4] = { 0, 0, 0, 0 }; if (fwrite(zeroes, 1, gap1_size, f) != gap1_size) { fclose(f); return false; } // write row by row, add padding const uint32 row_size = get_width() * (bpp/CHAR_BIT); const uint32 padding = pitch - row_size; assert(padding < 4); array_tpl row_buffer(pitch); for (sint32 y = get_height()-1; y >= 0; --y) { const uint8 *row_start = access_pixel(0, y); const uint8 *row_end = row_start + row_size; array_tpl::iterator dst = row_buffer.begin(); switch (fmt) { case FMT_RGB888: { const uint8 *src = row_start; while (src < row_end) { const uint8 R = *src++; const uint8 G = *src++; const uint8 B = *src++; *dst++ = B; *dst++ = G; *dst++ = R; } for (uint32 i = 0; i < padding; ++i) { *dst++ = 0; } if (fwrite(row_buffer.begin(), 1, pitch, f) != pitch) { fclose(f); return false; } } continue; case FMT_RGBA8888: { const uint8 *src = row_start; while (src < row_end) { const uint8 R = *src++; const uint8 G = *src++; const uint8 B = *src++; const uint8 A = *src++; *dst++ = B; *dst++ = G; *dst++ = R; *dst++ = A; } assert(padding == 0); if (fwrite(row_buffer.begin(), 1, pitch, f) != pitch) { fclose(f); return false; } } continue; default: // we only write supported formats here assert(false); break; } } fclose(f); return true; } simutrans-124.5/src/simutrans/io/raw_image_png.cc000066400000000000000000000162101520675460500221150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "raw_image.h" #include #include #include #include #include #include // strerror #include "../simmem.h" #include "../simdebug.h" #ifdef MAKEOBJ #define dr_fopen fopen #else #include "../sys/simsys.h" #endif static std::string filename_; bool raw_image_t::read_png_data(FILE *file) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); if (png_ptr == NULL) { dbg->error( "raw_image_t::read_png_data", "Could not create read struct"); return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { dbg->error( "raw_image_t::read_png_data", "Could not create info struct"); png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return false; } #ifdef PNG_SETJMP_SUPPORTED if( setjmp(png_jmpbuf(png_ptr) )) { dbg->error( "raw_image_t::read_png_data", "Fatal error in %s.", filename_.c_str()); png_destroy_read_struct(&png_ptr, &info_ptr, (png_info**)0); return false; } #endif /* Set up the input control if you are using standard C streams */ png_init_io(png_ptr, file); /* The call to png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). REQUIRED */ png_read_info(png_ptr, info_ptr); //png_uint_32 is 64 bit on some architectures! png_uint_32 new_width; png_uint_32 new_height; int bit_depth; int color_type; if (!png_get_IHDR(png_ptr, info_ptr, &new_width, &new_height, &bit_depth, &color_type, 0, 0, 0)) { dbg->error("raw_image_t::read_png_data", "Failed to read IHDR from '%s'", filename_.c_str()); png_destroy_read_struct(&png_ptr, &info_ptr, (png_info**)0); return false; } /* tell libpng to strip 16 bit/color files down to 8 bits/color */ png_set_strip_16(png_ptr); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ png_set_packing(png_ptr); /* Expand paletted colors into true RGB triplets */ if( color_type == PNG_COLOR_TYPE_PALETTE ) { png_set_expand(png_ptr); // tRNS to alpha if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); } color_type = PNG_COLOR_TYPE_RGBA; } else if (color_type == PNG_COLOR_TYPE_RGB) { // add opaque alpha channel png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); color_type = PNG_COLOR_TYPE_RGBA; } else if (color_type == PNG_COLOR_TYPE_GA) { dbg->warning("raw_image_t::read_png_data", "Ignoring alpha channel for grayscale image '%s'", filename_.c_str()); png_set_strip_alpha(png_ptr); color_type = PNG_COLOR_TYPE_GRAY; } // update info - png_get_rowbytes might return incorrect values png_read_update_info( png_ptr, info_ptr); const size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); png_bytep *row_pointers = MALLOCN(png_bytep, new_height); row_pointers[0] = MALLOCN(png_byte, rowbytes * new_height); for (uint32 row = 1; row < new_height; row++) { row_pointers[row] = row_pointers[row - 1] + rowbytes; } /* Read the entire image in one go */ png_read_image(png_ptr, row_pointers); // we use fixed height here because block is of limited, fixed size // not fixed any more format_t new_fmt = FMT_INVALID; switch (color_type) { case PNG_COLOR_TYPE_RGBA: new_fmt = FMT_RGBA8888; break; case PNG_COLOR_TYPE_RGB: new_fmt = FMT_RGB888; break; case PNG_COLOR_TYPE_GRAY: new_fmt = FMT_GRAY8; break; } if (new_fmt == FMT_INVALID) { dbg->error("raw_image_t::read_png_data", "Unsupported PNG format"); png_destroy_read_struct(&png_ptr, &info_ptr, (png_info**)0); return false; } const uint8 new_bpp = raw_image_t::bpp_for_format(raw_image_t::format_t(new_fmt)); const size_t old_size = width * height * (bpp /CHAR_BIT); const size_t new_size = new_width * new_height * (new_bpp/CHAR_BIT); if (new_size == 0) { free(data); data = NULL; } else if (new_size > old_size) { data = REALLOC(data, uint8, new_size); } fmt = new_fmt; width = new_width; height = new_height; bpp = new_bpp; if (new_size > 0) { uint8 *dst = data; for (uint32 y = 0; y < height; y++) { for (uint32 x = 0; x < width * (bpp/CHAR_BIT); x++) { *dst++ = row_pointers[y][x]; } } } free(row_pointers[0]); free(row_pointers); /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); /* At this point you have read the entire image */ /* clean up after the read, and free any memory allocated - REQUIRED */ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return true; } bool raw_image_t::read_png(const char *fname) { // remember the file name for better error messages. filename_ = fname; FILE* file = dr_fopen(fname, "rb"); if (file) { const bool ok = read_png_data(file); fclose(file); return ok; } else { dbg->warning( "raw_image_t::read_png", "Cannot open %s: %s", fname, strerror(errno) ); return false; } } bool raw_image_t::write_png(const char *file_name) const { // remember the file name for better error messages. filename_ = file_name; png_structp png_ptr = NULL; png_infop info_ptr = NULL; FILE *fp = dr_fopen(file_name, "wb"); if (!fp) { return false; } // init structures png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { fclose( fp ); return false; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); fclose( fp ); return false; } #ifdef PNG_SETJMP_SUPPORTED if( setjmp( png_jmpbuf(png_ptr) ) ) { dbg->error( "raw_image_t::write_png", "fatal error"); png_destroy_write_struct(&png_ptr, &info_ptr); return false; } #endif // assign file png_init_io(png_ptr, fp); #if PNG_LIBPNG_VER_MAJOR<=1 && PNG_LIBPNG_VER_MINOR<5 /* set the zlib compression level */ png_set_compression_level( png_ptr, Z_BEST_COMPRESSION ); #endif // output header int color_type; switch (fmt) { case FMT_RGBA8888: color_type = PNG_COLOR_TYPE_RGBA; break; case FMT_RGB888: color_type = PNG_COLOR_TYPE_RGB; break; case FMT_GRAY8: color_type = PNG_COLOR_TYPE_GRAY; break; default: dbg->fatal( "raw_image_t::write_png", "Cannot write png file: Unupported source format" ); } png_set_IHDR( png_ptr, info_ptr, width, height, 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); png_write_info(png_ptr, info_ptr); switch (fmt) { case FMT_RGBA8888: case FMT_RGB888: case FMT_GRAY8: { for (uint32 y = 0; y < height; ++y) { // hack to compile with old libpng versions that take a png_bytep instead of a png_const_bytep const uint8 *row = access_pixel(0, y); png_write_row(png_ptr, const_cast(row)); } } break; default: dbg->error("raw_image_t::write_png", "Unsupported source format for writing png"); png_destroy_write_struct(&png_ptr, &info_ptr); fclose( fp ); return false; } // free all png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose( fp ); return true; } simutrans-124.5/src/simutrans/io/raw_image_ppm.cc000066400000000000000000000045501520675460500221310ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "raw_image.h" #include "../simdebug.h" #include "../sys/simsys.h" #include "../simio.h" #include "../simmem.h" #ifdef MAKEOBJ #define dr_fopen fopen #else #include "../sys/simsys.h" #endif bool raw_image_t::read_ppm(const char *filename) { FILE *file = dr_fopen(filename, "rb"); // ppm format char buf[255]; const char *c = ""; sint32 param[3] = {0, 0, 0}; char id[2]; if (fread(id, sizeof(char), 2, file) != 2 || id[0]!='P' || id[1]!='6') { fclose(file); dbg->error("raw_image::read_ppm", "Malformed ppm file"); return false; } for( int index=0; index<3; ) { // the format is "P6[whitespace]width[whitespace]height[[whitespace bitdepth]]newline] // however, Photoshop is the first program that uses space for the first whitespace // so we cater for Photoshop too while( *c!=0 && *c<=32 ) { c++; } // usually, after P6 there comes a comment with the maker // but comments can be anywhere if( *c==0 ) { if( read_line(buf, sizeof(buf), file) == NULL ) { dbg->error("raw_image::read_ppm", "Malformed ppm file"); fclose(file); return false; } c = buf; continue; } param[index++] = atoi(c); while( *c>='0' && *c<='9' ) { c++; } } // now the data const sint32 w = param[0]; const sint32 h = param[1]; if (w <= 0 || h <= 0) { dbg->error("raw_image_t::read_ppm", "Heightfield has invalid image size (%dx%d)", w, h); fclose(file); return false; } if( param[2]!=255 ) { dbg->warning("raw_image_t::read_ppm", "Heightfield has wrong color depth (was %d, must be 255)", param[2]); } width = w; height = h; fmt = FMT_RGBA8888; bpp = 32; data = REALLOC(data, uint8, width * height * (bpp/CHAR_BIT)); for( sint16 y=0; yerror("raw_image_t::read_ppm", "Malformed ppm file"); return false; } *data++ = R; *data++ = G; *data++ = B; *data++ = 0xFF; } } fclose(file); return true; } bool raw_image_t::write_ppm(const char *) const { dbg->error("raw_image_t::write_ppm", "Not implemented"); return false; } simutrans-124.5/src/simutrans/io/rdwr/000077500000000000000000000000001520675460500177655ustar00rootroot00000000000000simutrans-124.5/src/simutrans/io/rdwr/adler32_stream.cc000066400000000000000000000013651520675460500231100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "adler32_stream.h" #include "../../simdebug.h" #include adler32_stream_t::adler32_stream_t() : rdwr_stream_t(true) { adler_checksum = adler32(0, NULL, 0); status = STATUS_OK; } size_t adler32_stream_t::read(void *, size_t) { dbg->fatal("adler32_stream_t::read", "Cannot reconstruct original message from checksum!"); return 0; } size_t adler32_stream_t::write(const void *buf, size_t len) { adler_checksum = adler32(adler_checksum, static_cast(buf), len); return len; } uint32 adler32_stream_t::get_hash() { const uint32 result = adler_checksum; adler_checksum = adler32(0, NULL, 0); return result; } simutrans-124.5/src/simutrans/io/rdwr/adler32_stream.h000066400000000000000000000012061520675460500227440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef IO_RDWR_ADLER32_STREAM_H #define IO_RDWR_ADLER32_STREAM_H #include "rdwr_stream.h" /// Computes the adler32 checksum over some data class adler32_stream_t : public rdwr_stream_t { public: adler32_stream_t(); public: /// @copydoc rdwr_stream_t::write size_t write(const void *buf, size_t len) OVERRIDE; /// @returns the adler32 checksum of all the data written since the last get_hash() uint32 get_hash(); private: /// DO NOT USE! size_t read(void *buf, size_t len) OVERRIDE; private: uint32 adler_checksum; }; #endif simutrans-124.5/src/simutrans/io/rdwr/bzip2_file_rdwr_stream.cc000066400000000000000000000034131520675460500247330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "bzip2_file_rdwr_stream.h" #include "../../sys/simsys.h" #include bzip2_file_rdwr_stream_t::bzip2_file_rdwr_stream_t(const std::string &filename, bool writing) : rdwr_stream_t(writing) { if (is_writing()) { fp = dr_fopen(filename.c_str(), "wb"); bzfp = BZ2_bzWriteOpen( &bse, fp, 9, 0, 30 /* default is 30 */ ); } else { fp = dr_fopen(filename.c_str(), "rb"); bzfp = BZ2_bzReadOpen( &bse, fp, 0, 0, NULL, 0 ); } status = STATUS_OK; if (fp==NULL) { status = STATUS_ERR_FILE_INACCESSIBLE; } if( bse!=BZ_OK ) { status = STATUS_ERR_WRITEFAILURE; } } bzip2_file_rdwr_stream_t::~bzip2_file_rdwr_stream_t() { if (is_writing()) { // BZLIB seems to eat the last byte if it is at an odd position // => we just write a dummy zero padding byte if (status == STATUS_OK) { write( "", 1 ); } BZ2_bzWriteClose( &bse, bzfp, 0, NULL, NULL ); } else { BZ2_bzReadClose( &bse, bzfp ); } if (fp) { fclose( fp ); } } size_t bzip2_file_rdwr_stream_t::read(void *buf, size_t len) { assert(!is_writing()); assert(bse == BZ_OK || bse == BZ_STREAM_END); assert(len < 0x7FFFFFFFU); const int bytes_read = (bse == BZ_OK) ? BZ2_bzRead(&bse, bzfp, buf, len) : 0; switch (bse) { case BZ_OK: status = STATUS_OK; return bytes_read; case BZ_STREAM_END: status = STATUS_EOF; return bytes_read; default: status = STATUS_ERR_CORRUPT; return 0; } } size_t bzip2_file_rdwr_stream_t::write(const void* buf, size_t len) { assert(is_writing()); assert(bse==BZ_OK); BZ2_bzWrite( &bse, bzfp, const_cast(buf), len); if (bse == BZ_OK) { return len; } else { status = STATUS_ERR_FULL; return 0; } } simutrans-124.5/src/simutrans/io/rdwr/bzip2_file_rdwr_stream.h000066400000000000000000000012611520675460500245740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef IO_RDWR_BZIP2_FILE_RDWR_STREAM_H #define IO_RDWR_BZIP2_FILE_RDWR_STREAM_H #include "rdwr_stream.h" #include /// Reads/writes data from/to a bzip2 compressed file. class bzip2_file_rdwr_stream_t : public rdwr_stream_t { public: bzip2_file_rdwr_stream_t(const std::string &filename, bool writing); ~bzip2_file_rdwr_stream_t(); public: /// @copydoc rdwr_stream_t::read size_t read(void *buf, size_t len) OVERRIDE; /// @copydoc rdwr_stream_t::write size_t write(const void *buf, size_t len) OVERRIDE; private: FILE *fp; BZFILE *bzfp; int bse; }; #endif simutrans-124.5/src/simutrans/io/rdwr/compare_file_rd_stream.cc000066400000000000000000000023071520675460500247630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "compare_file_rd_stream.h" #include #include compare_file_rd_stream_t::compare_file_rd_stream_t(rdwr_stream_t *s1, rdwr_stream_t* s2) : rdwr_stream_t(false) { assert(s1 && s1->is_reading() && s1->get_status() == STATUS_OK); assert(s2 && s2->is_reading() && s2->get_status() == STATUS_OK); stream1 = s1; stream2 = s2; status = STATUS_OK; our_buf = NULL; our_len = 0; } compare_file_rd_stream_t::~compare_file_rd_stream_t() { free(our_buf); } size_t compare_file_rd_stream_t::read(void *buf, size_t len) { if (len > our_len) { free(our_buf); our_buf = (char*)malloc(len); our_len = len; } assert(!is_writing()); size_t r1 = stream1->read(buf, len); size_t r2 = stream2->read(our_buf, len); bool ok = (r1 == r2) && memcmp(buf, our_buf, r1)==0; // dump if difference is found if (!ok) { printf("File 1: "); for(int i=0; i /// Compares two streams: reads from both, throws error if streams are different. class compare_file_rd_stream_t : public rdwr_stream_t { size_t our_len; char *our_buf; public: /// Takes two streams, does not take ownership compare_file_rd_stream_t(rdwr_stream_t *s1, rdwr_stream_t* s2); ~compare_file_rd_stream_t(); public: /// @copydoc rdwr_stream_t::read size_t read(void *buf, size_t len) OVERRIDE; // not implemented size_t write(const void *, size_t) OVERRIDE { return -1; } private: rdwr_stream_t *stream1, *stream2; }; #endif simutrans-124.5/src/simutrans/io/rdwr/raw_file_rdwr_stream.cc000066400000000000000000000027171520675460500245040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "raw_file_rdwr_stream.h" #include "../../sys/simsys.h" #include raw_file_rdwr_stream_t::raw_file_rdwr_stream_t(const std::string &filename, bool writing) : rdwr_stream_t(writing) { file = dr_fopen(filename.c_str(), writing ? "wb" : "rb"); if (!file) { status = STATUS_ERR_FILE_INACCESSIBLE; } else { status = STATUS_OK; } } raw_file_rdwr_stream_t::raw_file_rdwr_stream_t(FILE *f, bool writing) : rdwr_stream_t(writing), file(f) { if (!file) { status = STATUS_ERR_FILE_INACCESSIBLE; } else if (ferror(file)) { status = STATUS_ERR_CORRUPT; } else if (feof(file)) { status = STATUS_EOF; } else { status = STATUS_OK; } } raw_file_rdwr_stream_t::~raw_file_rdwr_stream_t() { if (file) { fflush(file); fclose(file); } } size_t raw_file_rdwr_stream_t::read(void *buf, size_t len) { assert(!is_writing()); const size_t bytes_read = fread(buf, 1, len, file); if (bytes_read == len) { status = STATUS_OK; return bytes_read; } else if (feof(file)) { status = STATUS_EOF; return bytes_read; } else { status = STATUS_ERR_CORRUPT; return 0; } } size_t raw_file_rdwr_stream_t::write(const void *buf, size_t len) { assert(is_writing()); const size_t bytes_written = fwrite(buf, 1, len, file); if (bytes_written == len) { status = STATUS_OK; } else { status = STATUS_ERR_FULL; } return bytes_written; } simutrans-124.5/src/simutrans/io/rdwr/raw_file_rdwr_stream.h000066400000000000000000000014461520675460500243440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef IO_RDWR_RAW_FILE_RDWR_STREAM_H #define IO_RDWR_RAW_FILE_RDWR_STREAM_H #include "rdwr_stream.h" #include /// Reads/writes raw data from/to a file. class raw_file_rdwr_stream_t : public rdwr_stream_t { public: raw_file_rdwr_stream_t(const std::string &filename, bool writing); /// Takes ownership of an already open file. /// @p writing must match the mode with which the file was opened. raw_file_rdwr_stream_t(FILE *f, bool writing); ~raw_file_rdwr_stream_t(); public: /// @copydoc rdwr_stream_t::read size_t read(void *buf, size_t len) OVERRIDE; /// @copydoc rdwr_stream_t::write size_t write(const void *buf, size_t len) OVERRIDE; private: FILE *file; }; #endif simutrans-124.5/src/simutrans/io/rdwr/rdwr_stream.cc000066400000000000000000000003521520675460500226250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "rdwr_stream.h" rdwr_stream_t::rdwr_stream_t(bool writing) : status(STATUS_ERR_NOT_INITIALIZED), writing(writing) { } simutrans-124.5/src/simutrans/io/rdwr/rdwr_stream.h000066400000000000000000000047441520675460500225000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef IO_RDWR_RDWR_STREAM_H #define IO_RDWR_RDWR_STREAM_H #include "../../simtypes.h" #include /// Either reads or writes data (but not both at the same time). class rdwr_stream_t { public: /// Note: On failure, this does not throw. Instead, call @ref get_status() to check if /// @ref read() or @ref write() can be called. Derived classes must update @ref status accordingly. rdwr_stream_t(bool writing); virtual ~rdwr_stream_t() {} public: enum status_t { STATUS_OK = 0, STATUS_EOF = 1, ///< (when reading) end of file/buffer reached // error codes STATUS_ERR_NOT_INITIALIZED = -1, ///< Not initialized STATUS_ERR_GENERIC_ERROR = -2, ///< Catch-all for unknown errors STATUS_ERR_FILE_INACCESSIBLE = -3, ///< File not found or wrong permissions on file or directory STATUS_ERR_FULL = -4, ///< (when writing) No space left in buffer or hard drive STATUS_ERR_WRITEFAILURE = -5, ///< Failed to produce a valid file for whatever reason STATUS_ERR_NO_VERSION = -6, STATUS_ERR_FUTURE_VERSION = -7, STATUS_ERR_OBSOLETE_VERSION = -8, ///< Version too old STATUS_ERR_CORRUPT = -9 ///< ex.: file malformed }; status_t get_status() const { return status; } bool is_writing() const { return writing; } bool is_reading() const { return !writing; } public: /// Read at most @p len bytes into @p buf; must not be called when writing. /// If this function fails, call @ref get_status() to get information about the type of error. /// /// @param buf Must not be NULL. /// @param len Must not be 0. /// /// @returns @p len, if successful. /// @returns The number of bytes successfully read, if end-of-data occurs. /// @returns Undefined (but not @p len), if an error occurred. virtual size_t read(void *buf, size_t len) = 0; /// Write at most @p len bytes from @p buf; must not be called when reading. /// If this function fails, call @ref get_status() to get information about the type of error. /// /// @param buf Must not be NULL. /// @param len Must not be 0. /// /// @returns @p len, if successful. /// @returns Undefined (but not @p len), if an error occurred. virtual size_t write(const void *buf, size_t len) = 0; protected: /// @warning This must be updated to the correct value when @p read() or @p write() or the constructor fails. status_t status; private: const bool writing; }; #endif simutrans-124.5/src/simutrans/io/rdwr/zlib_file_rdwr_stream.cc000066400000000000000000000060401520675460500246440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "zlib_file_rdwr_stream.h" #include "../../sys/simsys.h" #include "../../macros.h" #include "../../simdebug.h" #include #include zlib_file_rdwr_stream_t::zlib_file_rdwr_stream_t(const std::string &filename, bool writing, int compression) : rdwr_stream_t(writing) { // Should be 0 anyway, but make sure to only catch errors from zlib // zlib might not set errno appropriately in all error cases // (source: https://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/zlib-gzopen-1.html) // so reset it to a known value. The value will be translated to GENERIC_ERROR by set_status_from_errno errno = 0; if (is_writing()) { compression = clamp( compression, 1, 9 ); char compr[4] = { 'w', 'b', (char)('0' + compression), 0 }; gzfp = dr_gzopen(filename.c_str(), compr); } else { gzfp = dr_gzopen(filename.c_str(), "rb"); } if (gzfp == Z_NULL) { set_status_from_errno(); } else { gzbuffer(gzfp, 65536); status = STATUS_OK; } } zlib_file_rdwr_stream_t::~zlib_file_rdwr_stream_t() { if (is_writing()) { gzflush(gzfp, Z_FINISH); } gzclose(gzfp); } size_t zlib_file_rdwr_stream_t::read(void *buf, size_t len) { assert(!is_writing()); const int bytes_read = gzread(gzfp, buf, len); if (bytes_read >= 0 && (size_t)bytes_read == len) { status = STATUS_OK; return bytes_read; } else if (bytes_read != -1) { // not error => eof reached status = STATUS_EOF; return bytes_read; } else { // error int errnum = 0; const char *errmsg = gzerror(gzfp, &errnum); if (!errmsg) errmsg = ""; dbg->error("zlib_file_rdwr_stream_t::read", "Error: %s", errmsg); status = STATUS_ERR_CORRUPT; return 0; } } size_t zlib_file_rdwr_stream_t::write(const void *buf, size_t len) { assert(is_writing()); assert(len > 0); const int bytes_written = gzwrite(gzfp, const_cast(buf), len); if (bytes_written <= 0) { // error occurred int errnum = Z_OK; gzerror(gzfp, &errnum); switch (errnum) { case Z_MEM_ERROR: status = STATUS_ERR_WRITEFAILURE; return 0; // memerror means the lib could not malloc 260k => something else must be very wrong case Z_STREAM_ERROR: status = STATUS_ERR_NOT_INITIALIZED; return 0; case Z_BUF_ERROR: status = STATUS_ERR_FULL; return 0; case Z_ERRNO: set_status_from_errno(); return 0; } status = STATUS_ERR_GENERIC_ERROR; return 0; } else { status = STATUS_OK; return bytes_written; } } void zlib_file_rdwr_stream_t::set_status_from_errno() { switch (errno) { case EPERM: case ENOENT: case EIO: case EBADF: case EACCES: case ENODEV: case EISDIR: case ELOOP: status = STATUS_ERR_FILE_INACCESSIBLE; break; case ENOMEM: // this can only fail if there is invalid chunk data in the lib status = STATUS_ERR_CORRUPT; break; case EFBIG: case ENOSPC: status = STATUS_ERR_FULL; break; default: status = STATUS_ERR_GENERIC_ERROR; break; } } simutrans-124.5/src/simutrans/io/rdwr/zlib_file_rdwr_stream.h000066400000000000000000000013351520675460500245100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef IO_RDWR_ZLIB_FILE_RDWR_STREAM_H #define IO_RDWR_ZLIB_FILE_RDWR_STREAM_H #include "rdwr_stream.h" #include /// Reads/writes data from/to a zlib/gzip (deflate) compressed file. class zlib_file_rdwr_stream_t : public rdwr_stream_t { public: zlib_file_rdwr_stream_t(const std::string &filename, bool writing, int compression); ~zlib_file_rdwr_stream_t(); public: /// @copydoc rdwr_stream_t::read size_t read(void *buf, size_t len) OVERRIDE; /// @copydoc rdwr_stream_t::write size_t write(const void *buf, size_t len) OVERRIDE; private: void set_status_from_errno(); private: gzFile gzfp; }; #endif simutrans-124.5/src/simutrans/io/rdwr/zstd_file_rdwr_stream.cc000066400000000000000000000121621520675460500246720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "zstd_file_rdwr_stream.h" #include "../../dataobj/environment.h" #include "../../simdebug.h" #include "../../simmem.h" #include #define ZSTD_FILE_BUF_SIZE (1 << 20) // 1MiB zstd_file_rdwr_stream_t::zstd_file_rdwr_stream_t(const std::string &filename, bool writing, int compression_level) : raw_file_rdwr_stream_t(filename, writing), zbuff(NULL) { if (status != STATUS_OK) { return; // Could not open file } if (writing) { // compressing compression_context = ZSTD_createCCtx(); if( compression_context == NULL ) { // zstd could not init status = STATUS_ERR_NOT_INITIALIZED; return; } #if ZSTD_VERSION_MAJOR<=1 && ZSTD_VERSION_MINOR<=3 ZSTD_initCStream( compression_context, compression_level ); #else size_t ret1 = ZSTD_CCtx_setParameter( compression_context, ZSTD_c_compressionLevel, compression_level ); if (ZSTD_isError(ret1)) { dbg->error("zstd_file_rdwr_stream_t::zstd_file_rdwr_stream_t", "Cannot set compression level: %s", ZSTD_getErrorName(ret1)); status = STATUS_ERR_NOT_INITIALIZED; return; } // const size_t ret2 = ZSTD_CCtx_setParameter( compression_context, ZSTD_c_checksumFlag, 1 ); // if (ZSTD_isError(ret2)) { // dbg->error("zstd_file_rdwr_stream_t::zstd_file_rdwr_stream_t", "Cannot enable file checksumming: %s", ZSTD_getErrorName(ret2)); // status = STATUS_ERR_NOT_INITIALIZED; // return; // } #ifdef MULTI_THREAD ret1 = ZSTD_CCtx_setParameter( compression_context, ZSTD_c_nbWorkers, env_t::num_threads ); if (ZSTD_isError(ret1)) { // since compression should continue anyway, do not stop! dbg->warning("zstd_file_rdwr_stream_t::zstd_file_rdwr_stream_t", "Cannot set workers: %s", ZSTD_getErrorName(ret1)); } #endif #endif // the additional magic for zstd if (raw_file_rdwr_stream_t::write("ZD", 2) != 2) { return; } } else { // decompressing decompression_context = ZSTD_createDCtx(); if (decompression_context == NULL) { status = STATUS_ERR_CORRUPT; return; } char buf[2]; if (raw_file_rdwr_stream_t::read(buf, 2) != 2) { status = STATUS_ERR_CORRUPT; return; } else if (buf[0] != 'Z' || buf[1] != 'D') { status = STATUS_ERR_CORRUPT; return; } } zbuff = xmalloc(ZSTD_FILE_BUF_SIZE); if (writing) { zin.src = NULL; zin.size = 0; zin.pos = 0; zout.dst = zbuff; zout.size = ZSTD_FILE_BUF_SIZE; zout.pos = 0; } else { zin.src = zbuff; zin.size = ZSTD_FILE_BUF_SIZE; zin.pos = ZSTD_FILE_BUF_SIZE; zout.dst = NULL; zout.size = 0; zout.pos = 0; } status = STATUS_OK; } zstd_file_rdwr_stream_t::~zstd_file_rdwr_stream_t() { if (is_writing()) { // write zero length dummy to indicate end of data zin.src = ""; zin.size = 0; zin.pos = 0; zout.dst = zbuff; zout.size = ZSTD_FILE_BUF_SIZE; zout.pos = 0; size_t ret; do { zout.pos = 0; ret = ZSTD_endStream( compression_context, &zout ); if (ZSTD_isError(ret)) { dbg->error("zstd_file_rdwr_stream_t::~zstd_file_rdwr_stream_t", "Error flushing stream: %s", ZSTD_getErrorName(ret)); } raw_file_rdwr_stream_t::write( zout.dst, zout.pos ); } while( ret>0 ); ZSTD_freeCCtx( compression_context ); } else { ZSTD_freeDCtx( decompression_context ); } free( zbuff ); } size_t zstd_file_rdwr_stream_t::read(void *buf, size_t len) { zout.dst = buf; zout.size = len; zout.pos = 0; do { // first decompress from remaining input buffer while( zin.pos < zin.size && zout.pos < zout.size ) { const size_t ret = ZSTD_decompressStream( decompression_context, &zout, &zin ); if (ZSTD_isError(ret)) { dbg->error("zstd_file_rdwr_stream_t::read", "Error during decompression: %s", ZSTD_getErrorName(ret)); status = STATUS_ERR_CORRUPT; return 0; } if( ret == 0 ) { zout.size = zout.pos; } } // not enough data to fill output buffer => read more data from file if( zout.pos < zout.size ) { const size_t bytes_read = raw_file_rdwr_stream_t::read(zbuff, ZSTD_FILE_BUF_SIZE); zin.pos = 0; zin.size = bytes_read; if (status != rdwr_stream_t::STATUS_OK && status != rdwr_stream_t::STATUS_EOF) { // an error occurred, status is already set to the appropriate value return 0; } // reset status since EOF is indicated by end of decompressed data, // not end of compressed data status = STATUS_OK; } } while( zin.pos < zin.size && zout.pos < zout.size ); if (zout.pos < len) { // end of decompressed data reached status = STATUS_EOF; } return zout.pos; } size_t zstd_file_rdwr_stream_t::write(const void *buf, size_t len) { // compress the next data zin.src = buf; zin.size = len; zin.pos = 0; while( zin.pos < zin.size ) { zout.pos = 0; const size_t ret = ZSTD_compressStream( compression_context, &zout, &zin ); if (ZSTD_isError(ret)) { dbg->error("zstd_file_rdwr_stream_t::write", "Error during compression: %s", ZSTD_getErrorName(ret)); status = STATUS_ERR_WRITEFAILURE; return 0; } if (raw_file_rdwr_stream_t::write( zout.dst, zout.pos ) != zout.pos) { status = STATUS_ERR_FULL; return 0; } } return zin.pos; } simutrans-124.5/src/simutrans/io/rdwr/zstd_file_rdwr_stream.h000066400000000000000000000017061520675460500245360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef IO_RDWR_ZSTD_FILE_RDWR_STREAM_H #define IO_RDWR_ZSTD_FILE_RDWR_STREAM_H #include "raw_file_rdwr_stream.h" #include #if !USE_ZSTD #pragma message( "warning: Cannot use zstd_file_rdwr_stream_t: zstd not enabled") #endif /// Reads/writes data data from/to a zstd compressed file. class zstd_file_rdwr_stream_t : public raw_file_rdwr_stream_t { public: zstd_file_rdwr_stream_t(const std::string &filename, bool writing, int compression); ~zstd_file_rdwr_stream_t(); public: /// @copydoc rdwr_stream_t::read size_t read(void *buf, size_t len) OVERRIDE; /// @copydoc rdwr_stream_t::write size_t write(const void *buf, size_t len) OVERRIDE; private: void *zbuff; // buffer for compressed data, i.e. file <-> zbuff ZSTD_inBuffer zin; ZSTD_outBuffer zout; ZSTD_CCtx *compression_context; ZSTD_DCtx *decompression_context; }; #endif simutrans-124.5/src/simutrans/linehandle.h000066400000000000000000000003761520675460500206620ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef LINEHANDLE_H #define LINEHANDLE_H #include "tpl/quickstone_tpl.h" class simline_t; typedef quickstone_tpl linehandle_t; #endif simutrans-124.5/src/simutrans/macros.h000066400000000000000000000015611520675460500200400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef MACROS_H #define MACROS_H #include "simtypes.h" #include template static constexpr size_t lengthof(const T (&)[N]) { return N; } template static constexpr T *endof(T (&arr)[N]) { return arr + N; } #define QUOTEME_(x) #x #define QUOTEME(x) QUOTEME_(x) #define MEMZERON(ptr, n) memset((ptr), 0, sizeof(*(ptr)) * (n)) #define MEMZERO(obj) MEMZERON(&(obj), 1) // make sure, a value in within the borders template static inline T clamp(T v, T l, T u) { return v < l ? l : (v > u ? u :v); } namespace sim { template inline void swap(T& a, T& b) { T t = a; a = b; b = t; } // XXX Workaround for GCC 2.95 template static inline T up_cast(T x) { return x; } } #endif simutrans-124.5/src/simutrans/music/000077500000000000000000000000001520675460500175205ustar00rootroot00000000000000simutrans-124.5/src/simutrans/music/AVF_core-audio_midi.mm000066400000000000000000000034671520675460500236120ustar00rootroot00000000000000/* * Apple OSX Core Audio MIDI routine added by Leopard * * This file is part of the Simutrans project under the artistic licence. * */ #import "music.h" #include "../simdebug.h" #import #import #import static int nowPlaying = -1; // the number of the track currently being played static NSMutableArray* players; void dr_set_midi_volume(int const vol) { // Not supportd by AVMIDIPlayer } int dr_load_midi(char const* const filename) { NSURL* const url = [NSURL fileURLWithPath: [NSString stringWithUTF8String: filename]]; AVMIDIPlayer* const player = [[AVMIDIPlayer alloc] initWithContentsOfURL:url soundBankURL: nil error: nil]; if (player) { [player prepareToPlay]; [players addObject: player]; } return [players count] - 1; } void dr_play_midi(int const key) { // Play the file referenced by the supplied key. AVMIDIPlayer* const player = [players objectAtIndex: key]; double duration = [player duration]; double currentPosition = [player currentPosition]; if (currentPosition >= duration) { [player setCurrentPosition: 0.0]; } try { [player play: ^{}]; } catch (NSException *e) { dbg->warning("dr_play_midi()", "AVFoundation: Error playing midi"); } nowPlaying = key; } void dr_stop_midi() { if( nowPlaying!= -1 ) { // We assume the 'nowPlaying' key holds the most recently started track. AVMIDIPlayer* const player = [players objectAtIndex: nowPlaying]; [player stop]; } } sint32 dr_midi_pos() { if (nowPlaying == -1) { return -1; } float const rate = [[players objectAtIndex: nowPlaying] rate]; return rate > 0 ? 0 : -1; } void dr_destroy_midi() { if (nowPlaying != -1) { dr_stop_midi(); } } bool dr_init_midi() { players = [NSMutableArray arrayWithCapacity: MAX_MIDI]; return true; } simutrans-124.5/src/simutrans/music/core-audio_midi.mm000066400000000000000000000031461520675460500231100ustar00rootroot00000000000000/* * Apple OSX Core Audio MIDI routine added by Leopard * * This file is part of the Simutrans project under the artistic licence. * */ #import "music.h" #import #import #import static float defaultVolume = 0.5; // a nice default volume static int nowPlaying = -1; // the number of the track currently being played static NSMutableArray* movies; void dr_set_midi_volume(int const vol) { // We are given an integer from 0 - 255, we need a float between 0 and 1. defaultVolume = vol / 255.f; if (nowPlaying != -1) { [[movies objectAtIndex: nowPlaying] setVolume: defaultVolume]; } } int dr_load_midi(char const* const filename) { NSString* const s = [NSString stringWithUTF8String: filename]; QTMovie* const m = [QTMovie movieWithFile: s error: nil]; if (m) { [movies addObject: m]; } return [movies count] - 1; } void dr_play_midi(int const key) { // Play the file referenced by the supplied key. QTMovie* const m = [movies objectAtIndex: key]; [m setVolume:defaultVolume]; [m play]; nowPlaying = key; } void dr_stop_midi() { if( nowPlaying!= -1 ) { // We assume the 'nowPlaying' key holds the most recently started track. QTMovie* const m = [movies objectAtIndex: nowPlaying]; [m stop]; } } sint32 dr_midi_pos() { if (nowPlaying == -1) { return -1; } float const rate = [[movies objectAtIndex: nowPlaying] rate]; return rate > 0 ? 0 : -1; } void dr_destroy_midi() { if (nowPlaying != -1) { dr_stop_midi(); } } bool dr_init_midi() { movies = [NSMutableArray arrayWithCapacity: MAX_MIDI]; return true; } simutrans-124.5/src/simutrans/music/fluidsynth.cc000066400000000000000000000151351520675460500222250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../utils/plainstring.h" #include "../dataobj/environment.h" #include "music.h" // fluidsynth music routine interfaces static int midi_number = -1; static plainstring midi_filenames[MAX_MIDI]; fluid_settings_t* settings; fluid_synth_t* synth; fluid_audio_driver_t* adriver; fluid_player_t* player; // Predefined list of paths to search for soundfonts static const char * default_sf_paths[] = { /* RedHat/Fedora/Arch path */ "/usr/share/soundfonts/", /* Debian/Ubuntu/OpenSUSE path */ "/usr/share/sounds/sf2/", NULL }; // Soundfonts included on linux distros or bundled with Simutrans static const char * default_sf_names[] = { "default.sf3", "default.sf2", "freepats-general-midi.sf2", "PCLite.sf2", "TimGM6mb.sf2", "FluidR3_GM.sf2", "FluidR3_GS.sf2", NULL }; #ifdef __ANDROID__ /* Fluidsynth on Android is too old and does not export some functions */ extern "C" int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan); #endif /** * sets midi playback volume */ void dr_set_midi_volume(int vol) { /* Allowed range of synth.gain is 0.0 to 10.0 */ /* fluidsynth's default gain is 0.2, to avoid possible clipping. * Set gain using Simutrans's volume, as a number between 0 * and 0.7 to balance with sound effects. */ if( fluid_settings_setnum( settings, "synth.gain", 0.7 * vol / 255.0 ) != FLUID_OK ) { dbg->warning("dr_set_midi_volume()", "FluidSynth: Could not set volume."); } } /** * Loads a MIDI file */ int dr_load_midi(const char *filename) { if( midi_number < MAX_MIDI - 1 ) { const int i = midi_number + 1; if( i >= 0 && i < MAX_MIDI && fluid_is_midifile( filename ) ) { midi_number = i; midi_filenames[i] = filename; } else { dbg->warning("dr_load_midi()", "FluidSynth: Failed to load MIDI %s.", filename ); } } return midi_number; } /** * Plays a MIDI file */ void dr_play_midi(int key) { if( dr_midi_pos() != -1 ) { dr_stop_midi(); } if( !(player = new_fluid_player( synth )) ) { dbg->warning("dr_play_midi()", "FluidSynth: MIDI player setup failed."); return; } if( fluid_player_add( player, midi_filenames[key] ) != FLUID_OK ) { dbg->warning("dr_play_midi()", "FluidSynth: %s MIDI file load failed.", midi_filenames[key].c_str() ); return; } if( fluid_player_play( player ) != FLUID_OK ) { dbg->warning("dr_play_midi()", "FluidSynth: MIDI player start failed."); return; } } /** * Stops playing MIDI file */ void dr_stop_midi(void) { if( !player ) { return; } fluid_player_stop( player ); if( fluid_player_join( player ) != FLUID_OK ) { dbg->warning("dr_stop_midi()", "FluidSynth: Player join failed."); } fluid_synth_all_notes_off( synth, -1 ); delete_fluid_player( player ); player = NULL; } /** * Returns the midi_pos variable <- doesn't actually do this * Simutrans only needs to know whether file has finished (so that it can start the next music) * Returns -1 if current music has finished, else 0 */ sint32 dr_midi_pos(void) { if( !player || fluid_player_get_status( player ) != FLUID_PLAYER_PLAYING ) { return -1; } return 0; } /** * Midi shutdown/cleanup */ void dr_destroy_midi(void) { dr_stop_midi(); delete_fluid_audio_driver(adriver); delete_fluid_synth(synth); delete_fluid_settings(settings); midi_number = -1; } /** * MIDI initialisation routines */ bool dr_load_sf(const char * filename){ static int previous_id = -1; if( synth && fluid_is_soundfont( filename ) ) { int next_id = fluid_synth_sfload( synth, filename, 1 ); if( next_id != FLUID_FAILED ) { if( previous_id != -1 ) { fluid_synth_sfunload( synth, previous_id, 1 ); } previous_id = next_id; env_t::soundfont_filename = filename; return true; } } return false; } #if FLUIDSYNTH_VERSION_MAJOR >= 2 static void fluid_log(int level, const char* message, void*) #else static void fluid_log(int level, char *message, void *) #endif { switch (level) { case FLUID_PANIC: dbg->fatal("FluidSynth", "%s", message); case FLUID_ERR: dbg->error("FluidSynth", "%s", message); break; case FLUID_WARN: dbg->warning("FluidSynth", "%s", message); break; case FLUID_INFO: dbg->message("FluidSynth", "%s", message); break; case FLUID_DBG: dbg->debug("FluidSynth", "%s", message); break; } } bool dr_init_midi() { fluid_set_log_function(FLUID_PANIC, fluid_log, NULL); fluid_set_log_function(FLUID_ERR, fluid_log, NULL); fluid_set_log_function(FLUID_WARN, fluid_log, NULL); fluid_set_log_function(FLUID_INFO, fluid_log, NULL); fluid_set_log_function(FLUID_DBG, fluid_log, NULL); #ifdef _WIN32 std::string fluidsynth_driver = "dsound"; #elif defined(__APPLE__) && __APPLE__ std::string fluidsynth_driver = "coreaudio"; #elif defined(__ANDROID__) && __ANDROID__ std::string fluidsynth_driver = "oboe"; #else std::string fluidsynth_driver = "pulseaudio"; #endif if( !(settings = new_fluid_settings()) ) { dbg->warning("dr_init_midi()", "FluidSynth: MIDI settings failed."); return false; } fluid_settings_setint( settings, "synth.cpu-cores", env_t::num_threads ); fluid_settings_setstr( settings, "synth.midi-bank-select", "gm" ); if( fluid_settings_setstr( settings, "audio.driver", fluidsynth_driver.c_str() ) != FLUID_OK ) { dbg->warning("dr_init_midi()", "FluidSynth: Set MIDI driver %s failed.", fluidsynth_driver.c_str()); return false; } if( !(synth = new_fluid_synth( settings )) ) { dbg->warning("dr_init_midi()", "FluidSynth: Synth setup failed."); return false; } if( !(adriver = new_fluid_audio_driver( settings, synth )) ) { dbg->warning("dr_init_midi()", "FluidSynth: Audio driver setup failed."); return false; } // User defined font first if( dr_load_sf( env_t::soundfont_filename.c_str() ) || dr_load_sf( ((std::string)env_t::base_dir + "music/" + env_t::soundfont_filename).c_str() ) ) { return true; } // Bundled soundfonts second for( int i = 0; default_sf_names[i]; i++ ) { if( dr_load_sf( ((std::string)env_t::base_dir + "music/" + (std::string)default_sf_names[i] ).c_str() ) ) { return true; } } // System soundfonts at last for( int i = 0; default_sf_paths[i]; i++ ) { for( int j = 0; default_sf_names[j]; j++ ){ if( dr_load_sf( ((std::string)default_sf_paths[i] + (std::string)default_sf_names[j]).c_str() ) ) { return true; } } } env_t::soundfont_filename = "Error"; dbg->warning("dr_init_midi()", "FluidSynth: No soundfont was found."); return true; // MIDI system has been initialed even if no soundfont was loaded. A user can load a soundfont after. } simutrans-124.5/src/simutrans/music/music.h000066400000000000000000000015721520675460500210160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef MUSIC_MUSIC_H #define MUSIC_MUSIC_H #include "../simtypes.h" #define MAX_MIDI (128) /** * MIDI initialisation routine * false: leave midi alone */ bool dr_init_midi(); /** * sets midi playback volume * @param vol volume in range 0..255 */ void dr_set_midi_volume(int vol); /** * Loads a MIDI file */ int dr_load_midi(const char* filename); /** * Plays a MIDI file */ void dr_play_midi(int key); /** * Stops playing MIDI file */ void dr_stop_midi(); /** * @return -1 if current track has finished, 0 otherwise. */ sint32 dr_midi_pos(); /** * Midi shutdown/cleanup */ void dr_destroy_midi(); /** * Load a soundfont. Only available if we are using FluidSynth. */ #ifdef USE_FLUIDSYNTH_MIDI bool dr_load_sf(const char * filename); #endif #endif simutrans-124.5/src/simutrans/music/no_midi.cc000066400000000000000000000012631520675460500214470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "music.h" // Dummy midi routines - only provide interface, does nothing /** * sets midi playback volume */ void dr_set_midi_volume(int) { } /** * Loads a MIDI file */ int dr_load_midi(const char *) { return -1; } /** * Plays a MIDI file */ void dr_play_midi(int) { } /** * Stops playing MIDI file */ void dr_stop_midi(void) { } /** * Returns the midi_pos variable */ sint32 dr_midi_pos(void) { return 0; } /** * Midi shutdown/cleanup */ void dr_destroy_midi(void) { } /** * MIDI initialisation routines */ bool dr_init_midi(void) { return false; } simutrans-124.5/src/simutrans/music/sdl_midi.cc000066400000000000000000000042421520675460500216150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../simdebug.h" #include "../utils/plainstring.h" #include "music.h" // SDL_Mixer music routine interfaces static int midi_number = -1; static plainstring midi_filenames[MAX_MIDI]; //Mix_Music *music[MAX_MIDI]; Mix_Music *music = NULL; /** * sets midi playback volume */ void dr_set_midi_volume(int vol) { Mix_VolumeMusic((vol*MIX_MAX_VOLUME)/256); } /** * Loads a MIDI file */ int dr_load_midi(const char * filename) { if (midi_number < MAX_MIDI - 1) { const int i = midi_number + 1; if(i >= 0 && i < MAX_MIDI) { music = Mix_LoadMUS(filename); if(music) { midi_number = i; midi_filenames[i] = filename; } else { dbg->warning( "dr_load_midi()", "Failed to load MIDI %s because %s", filename, Mix_GetError() ); } Mix_FreeMusic(music); music = NULL; } } return midi_number; } /** * Plays a MIDI file */ void dr_play_midi(int key) { if(dr_midi_pos()!= -1) { dr_stop_midi(); } music = Mix_LoadMUS(midi_filenames[key]); Mix_PlayMusic(music, 1); } /** * Stops playing MIDI file */ void dr_stop_midi(void) { if(music) { Mix_HaltMusic(); Mix_FreeMusic(music); music = NULL; } } /** * Returns the midi_pos variable <- doesn't actually do this * Simutrans only needs to know whether file has finished (so that it can start the next music) * Returns -1 if current music has finished, else 0 */ sint32 dr_midi_pos(void) { if(Mix_PlayingMusic()== 0) { return -1; } else { return 0; } } /** * Midi shutdown/cleanup */ void dr_destroy_midi(void) { dr_stop_midi(); midi_number = -1; } /** * MIDI initialisation routines */ bool dr_init_midi(void) { // if audio not init if(!SDL_WasInit(SDL_INIT_AUDIO)) { // if audio subsys is ok if(SDL_InitSubSystem(SDL_INIT_AUDIO) != -1) { if(Mix_OpenAudio(22050, AUDIO_S16SYS, 2, 1024)==-1) { //if OpenAudio returns error, dr_init_midi is false return false; } } else { //if SDL_InitSubSystem returns error, dr_init_midi is false return false; } } //if all is fine, return true return true; } simutrans-124.5/src/simutrans/music/w32_midi.cc000066400000000000000000000171671520675460500214600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include "../utils/plainstring.h" #include "music.h" #include "../simsound.h" #include "../simdebug.h" // native win32 midi playing routines static int midi_number = -1; static plainstring midi_filenames[MAX_MIDI]; static int OldMIDIVol[2] = {-1, -1}; #define __MIDI_VOL_SIMU 1 // 0-255 #define __MIDI_VOL_WIN32 2 // 0-65535 void __win32_set_midi_volume(int type, int left, int right); /** * sets midi playback volume */ void dr_set_midi_volume(int vol) { __win32_set_midi_volume(__MIDI_VOL_SIMU, vol, vol); } /** * Loads a MIDI file */ int dr_load_midi(const char *filename) { if(midi_number < MAX_MIDI-1) { const int i = midi_number + 1; if (i >= 0 && i < MAX_MIDI) { // MCI doesn't like relative paths // but we get absolute ones anyway // already absolute path midi_filenames[i] = filename; // need to make dos path separators for (char* j = midi_filenames[i]; *j != '\0'; ++j) { if (*j == '/') { *j = '\\'; } } midi_number = i; } } return midi_number; } /** * Plays a MIDI file * Key: The index of the MIDI file to be played */ void dr_play_midi(int key) { char str[200], retstr[200]; if (midi_number > 0) { if (key >= 0 && key <= midi_number) { sprintf(str, "open \"%s\" type sequencer alias SimuMIDI", midi_filenames[key].c_str()); dbg->debug("dr_play_midi(w32)", "MCI string: %s", str); if (mciSendStringA(str, NULL, 0, NULL) != 0) { dbg->warning("dr_play_midi(w32)", "Unable to load MIDI %d", key); } else if (mciSendStringA("play SimuMIDI", retstr, 200, NULL) != 0) { dbg->warning("dr_play_midi(w32)", "Unable to play MIDI %d - %s\n", key, retstr); } } else { dbg->warning("dr_play_midi(w32)", "Unable to play MIDI %d", key); } } } /** * Stops playing MIDI file */ void dr_stop_midi() { // stop_midi(); char retstr[200]; mciSendStringA("stop SimuMIDI", retstr, 200, NULL); mciSendStringA("close SimuMIDI", retstr, 200, NULL); } /** * Returns the midi_pos variable */ sint32 dr_midi_pos() { char retstr[200]; long length; mciSendStringA("set SimuMIDI time format milliseconds", retstr, 200, NULL); mciSendStringA("status SimuMIDI length", retstr, 200, NULL); length = atol(retstr); if (mciSendStringA("status SimuMIDI position", retstr, 200, NULL) == 0) { const long pos = atol(retstr); if (pos == length) { mciSendStringA("stop SimuMIDI", retstr, 200, NULL); // We must stop ourselves mciSendStringA("close SimuMIDI", retstr, 200, NULL); return (-1); } else { return pos; } } return 0; } /** * Midi shutdown/cleanup */ void dr_destroy_midi() { __win32_set_midi_volume(__MIDI_VOL_WIN32, OldMIDIVol[0], OldMIDIVol[1]); midi_number = -1; } /** * MIDI initialisation routines */ bool dr_init_midi() { #ifdef MIXER_VOLUME UINT nMIDIDevices; MIXERLINECONTROLS mlc; MIXERCONTROL MixControl; MIXERCONTROLDETAILS MixControlDetails; MIXERCONTROLDETAILS_UNSIGNED MixControlDetailsUnsigned[2]; MIXERLINE MixerLine; HMIXER hMixer; MIXERCAPS DevCaps; // Reset MIDI volume nMIDIDevices = midiOutGetNumDevs(); if (nMIDIDevices == 0) return; mixerOpen(&hMixer, 0, 0, 0, MIXER_OBJECTF_MIDIOUT); mixerGetDevCaps((UINT) hMixer, &DevCaps, sizeof(DevCaps)); mixerClose(hMixer); MixerLine.cbStruct = sizeof(MixerLine); MixerLine.Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; MixerLine.Target.wMid = DevCaps.wMid; MixerLine.Target.wPid = DevCaps.wPid; MixerLine.Target.vDriverVersion = DevCaps.vDriverVersion; strcpy(MixerLine.Target.szPname, DevCaps.szPname); mixerGetLineInfo(0, &MixerLine, MIXER_GETLINEINFOF_TARGETTYPE | MIXER_OBJECTF_MIDIOUT); mlc.cbStruct = sizeof(mlc); mlc.dwLineID = MixerLine.dwLineID; mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mlc.cControls = 1; mlc.cbmxctrl = sizeof(MixControl); mlc.pamxctrl = &MixControl; MixControl.cbStruct = sizeof(MixControl); MixControl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mixerGetLineControls(0, &mlc, MIXER_OBJECTF_MIDIOUT | MIXER_GETLINECONTROLSF_ONEBYTYPE); MixControlDetails.cbStruct = sizeof(MixControlDetails); MixControlDetails.dwControlID = MixControl.dwControlID; MixControlDetails.cChannels = MixerLine.cChannels; MixControlDetails.hwndOwner = NULL; MixControlDetails.cMultipleItems = 0; MixControlDetails.cbDetails = sizeof(MixControlDetailsUnsigned[0])*2; MixControlDetails.paDetails = &MixControlDetailsUnsigned[0]; mixerGetControlDetails(0, &MixControlDetails, MIXER_OBJECTF_MIDIOUT | MIXER_GETCONTROLDETAILSF_VALUE); OldMIDIVol[0] = MixControlDetailsUnsigned[0].dwValue; // Save the old volume OldMIDIVol[1] = MixControlDetailsUnsigned[0].dwValue; sound_set_midi_volume_var(OldMIDIVol[0] >> 8); // Set the MIDI volume mixerSetControlDetails(0, &MixControlDetails, MIXER_OBJECTF_MIDIOUT | MIXER_SETCONTROLDETAILSF_VALUE); #else if( midiOutGetNumDevs()== 0 ) { return false; } DWORD old_volume; midiOutGetVolume( 0, &old_volume ); OldMIDIVol[0] = old_volume>>24; OldMIDIVol[1] = (old_volume&0x0000FF00)>>8; #endif // assuming if we got here, all is set up to work properly return true; } #ifdef MIXER_VOLUME // Sets the MIDI volume - internal routine void __win32_set_midi_volume(int type, int left, int right) { UINT nMIDIDevices; MIXERLINECONTROLS mlc; MIXERCONTROL MixControl; MIXERCONTROLDETAILS MixControlDetails; MIXERCONTROLDETAILS_UNSIGNED MixControlDetailsUnsigned[2]; MIXERLINE MixerLine; HMIXER hMixer; MIXERCAPS DevCaps; nMIDIDevices = midiOutGetNumDevs(); if (nMIDIDevices == 0) { return; } mixerOpen(&hMixer, 0, 0, 0, MIXER_OBJECTF_MIDIOUT); mixerGetDevCaps((UINT) hMixer, &DevCaps, sizeof(DevCaps)); mixerClose(hMixer); MixerLine.cbStruct = sizeof(MixerLine); MixerLine.Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; MixerLine.Target.wMid = DevCaps.wMid; MixerLine.Target.wPid = DevCaps.wPid; MixerLine.Target.vDriverVersion = DevCaps.vDriverVersion; strcpy(MixerLine.Target.szPname, DevCaps.szPname); mixerGetLineInfo(0, &MixerLine, MIXER_GETLINEINFOF_TARGETTYPE | MIXER_OBJECTF_MIDIOUT); mlc.cbStruct = sizeof(mlc); mlc.dwLineID = MixerLine.dwLineID; mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mlc.cControls = 1; mlc.cbmxctrl = sizeof(MixControl); mlc.pamxctrl = &MixControl; MixControl.cbStruct = sizeof(MixControl); MixControl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mixerGetLineControls(0, &mlc, MIXER_OBJECTF_MIDIOUT | MIXER_GETLINECONTROLSF_ONEBYTYPE); MixControlDetails.cbStruct = sizeof(MixControlDetails); MixControlDetails.dwControlID = MixControl.dwControlID; MixControlDetails.cChannels = MixerLine.cChannels; MixControlDetails.hwndOwner = NULL; MixControlDetails.cMultipleItems = 0; MixControlDetails.cbDetails = sizeof(MixControlDetailsUnsigned[0])*2; MixControlDetails.paDetails = &MixControlDetailsUnsigned[0]; mixerGetControlDetails(0, &MixControlDetails, MIXER_OBJECTF_MIDIOUT | MIXER_GETCONTROLDETAILSF_VALUE); if (type == __MIDI_VOL_SIMU) { MixControlDetailsUnsigned[0].dwValue = (left << 8); MixControlDetailsUnsigned[1].dwValue = (right << 8); } else { MixControlDetailsUnsigned[0].dwValue = left; MixControlDetailsUnsigned[1].dwValue = right; } mixerSetControlDetails(0, &MixControlDetails, MIXER_OBJECTF_MIDIOUT | MIXER_SETCONTROLDETAILSF_VALUE); // Phew, I'm glad that's over! What a horrible API... } #else // Sets the MIDI volume - internal routine void __win32_set_midi_volume(int , int left, int right) { // short version long vol = (left<<24)|(right<<8); if( midiOutGetNumDevs()== 0 ) { return; } midiOutSetVolume( 0, vol ); } #endif simutrans-124.5/src/simutrans/network/000077500000000000000000000000001520675460500200715ustar00rootroot00000000000000simutrans-124.5/src/simutrans/network/checksum.cc000066400000000000000000000053531520675460500222100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "checksum.h" #include #include #include "../simdebug.h" checksum_t::checksum_t() { sha = NULL; reset(); } checksum_t& checksum_t::operator=(const checksum_t& other) { assert(other.valid); valid = true; delete sha; sha = NULL; for(uint8 i=0; i<20; i++) { message_digest[i] = other.message_digest[i]; } return *this; } checksum_t::checksum_t(const checksum_t &other) { assert(other.valid); valid = true; sha = NULL; for(uint8 i=0; i<20; i++) { message_digest[i] = other.message_digest[i]; } } checksum_t::~checksum_t() { delete sha; } void checksum_t::reset() { if (sha == NULL) { sha = new SHA1(); } else { sha->Reset(); } valid = false; } bool checksum_t::operator== (checksum_t &other) { if (!valid) { finish(); } if (!other.valid) { other.finish(); } for(uint8 i=0; i<20; i++) { if (message_digest[i] != other.message_digest[i]) { return false; } } return true; } bool checksum_t::operator== (const checksum_t &other) const { assert(valid && other.valid); for(uint8 i=0; i<20; i++) { if (message_digest[i] != other.message_digest[i]) { return false; } } return true; } void checksum_t::input(bool data) { assert(sha); // save bool as (uint8)1 uint8 bool1 = data ? 1 : 0; input(bool1); } void checksum_t::input(uint8 data) { assert(sha); sha->Input((const char*)&data, sizeof(uint8)); } void checksum_t::input(sint8 data) { input((uint8)data); } void checksum_t::input(uint16 data) { uint16 little_endian = endian(data); assert(sha); sha->Input((const char*)&little_endian, sizeof(uint16)); } void checksum_t::input(sint16 data) { input((uint16)data); } void checksum_t::input(uint32 data) { uint32 little_endian = endian(data); assert(sha); sha->Input((const char*)&little_endian, sizeof(uint32)); } void checksum_t::input(sint32 data) { input((uint32)data); } void checksum_t::input(uint64 data) { uint64 little_endian = endian(data); assert(sha); sha->Input((const char *)&little_endian, sizeof(uint64)); } void checksum_t::input(sint64 data) { input((uint64)data); } void checksum_t::input(const char *data) { if (data==NULL) { data = "null"; } assert(sha); sha->Input(data, strlen(data)); } const char* checksum_t::get_str(const int maxlen) const { static char buf[41]; for(uint8 i=0; iinput(message_digest[i]); } } void checksum_t::finish() { if (!valid) { assert(sha); sha->Result(message_digest); valid = true; delete sha; sha = NULL; } } simutrans-124.5/src/simutrans/network/checksum.h000066400000000000000000000024321520675460500220450ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_CHECKSUM_H #define NETWORK_CHECKSUM_H #include "../utils/sha1.h" class checksum_t { private: sha1_hash_t message_digest; bool valid:1; SHA1 *sha; public: checksum_t(); checksum_t& operator=(const checksum_t&); checksum_t(const checksum_t&); ~checksum_t(); /** * fetches the result */ void finish(); void reset(); bool operator== (checksum_t &); bool operator== (const checksum_t &) const; bool is_valid() const { return valid; } void input(bool data); void input(uint8 data); void input(sint8 data); void input(uint16 data); void input(sint16 data); void input(uint32 data); void input(sint32 data); void input(uint64 data); void input(sint64 data); void input(const char *data); const char* get_str(const int maxlen=20) const; // templated to be able to read/write from/to loadsave_t and packet_t template void rdwr(rdwr_able *file) { if(file->is_saving()) { if (!valid) { finish(); } } else { valid = true; delete sha; sha = 0; } for(uint8 i=0; i<20; i++) { file->rdwr_byte(message_digest[i]); } } /** * build checksum of checksums */ void calc_checksum(checksum_t *chk) const; }; #endif simutrans-124.5/src/simutrans/network/memory_rw.cc000066400000000000000000000051601520675460500224220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "memory_rw.h" #include #include #include "../simdebug.h" #include "../simmem.h" #include "../utils/plainstring.h" memory_rw_t::memory_rw_t( void *ptr, uint32 max, bool saving ) { this->saving = saving; this->ptr = (char *)ptr; index = 0; max_size = max; overflow = false; } void memory_rw_t::rdwr(void *data, uint32 len) { assert(data); if( len+index>max_size ) { len = max_size-index; overflow = true; } if(is_saving()) { memmove(ptr+index, data, len); } else { memmove(data, ptr+index, len); } index += len; } void memory_rw_t::rdwr_byte(sint8 &c) { rdwr(&c, sizeof(sint8)); } void memory_rw_t::rdwr_byte(uint8 &c) { rdwr(&c, sizeof(uint8)); } void memory_rw_t::rdwr_bool(bool &i) { // only read i on saving uint8 b = is_loading() || i; rdwr_byte(b); i = b!=0; } void memory_rw_t::rdwr_short(sint16 &i) { #ifdef SIM_BIG_ENDIAN sint16 ii; if(is_saving()) { ii = endian(i); } rdwr(&ii, sizeof(sint16)); if(is_loading()) { i = endian(ii); } #else rdwr(&i, sizeof(sint16)); #endif } void memory_rw_t::rdwr_short(uint16 &i) { sint16 ii=i; rdwr_short(ii); i = (uint16)ii; } void memory_rw_t::rdwr_long(sint32 &l) { #ifdef SIM_BIG_ENDIAN sint32 ii; if(is_saving()) { ii = endian(l); } rdwr(&ii, sizeof(sint32)); if(is_loading()) { l = endian(ii); } #else rdwr(&l, sizeof(sint32)); #endif } void memory_rw_t::rdwr_long(uint32 &l) { sint32 ll=l; rdwr_long(ll); l = (uint32)ll; } void memory_rw_t::rdwr_longlong(sint64 &ll) { #ifdef SIM_BIG_ENDIAN sint64 ii; if(is_saving()) { ii = endian(ii); } rdwr(&ii, sizeof(sint64)); if(is_loading()) { ll = endian(ii); } #else rdwr(&ll, sizeof(sint64)); #endif } void memory_rw_t::rdwr_str(char *&s) { // string length uint16 len = 0; if(is_saving()) { len = s ? strlen(s) : 0; } rdwr_short(len); // now the string if (is_saving()) { if(len>0 && !overflow) { rdwr( s, len); } } else { free( (void *)s ); s = 0; if(len>0 && !overflow) { s = MALLOCN(char, len + 1); rdwr(s, len); s[len] = '\0'; } } } void memory_rw_t::rdwr_str(plainstring &s) { if(is_loading()) { char *t = NULL; rdwr_str(t); s = t; free(t); } else { char *t = s; rdwr_str(t); } } void memory_rw_t::append(const memory_rw_t &mem) { assert(saving && mem.saving); rdwr(mem.ptr, mem.get_current_index()); } void memory_rw_t::append_tail(const memory_rw_t &mem) { assert(saving && !mem.saving); rdwr(mem.ptr + mem.get_current_index(), mem.max_size - mem.get_current_index()); } simutrans-124.5/src/simutrans/network/memory_rw.h000066400000000000000000000031411520675460500222610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_MEMORY_RW_H #define NETWORK_MEMORY_RW_H /* This is a class to write data to memory in intel byte order * on every architecture */ #include "../simtypes.h" class plainstring; class memory_rw_t { private: // pointer to the buffer char *ptr; // actual read/write position uint32 index; // maximal buffer size uint32 max_size; bool saving:1; bool overflow:1; public: memory_rw_t( void *ptr, uint32 max, bool saving ); protected: void set_max_size( uint32 new_max_size ) { max_size = new_max_size; } void set_index(uint32 new_index) { index = new_index; } public: uint32 get_current_index() const { return index; } bool is_saving() const { return saving; } bool is_loading() const { return !saving; } bool is_overflow() const { return overflow; } void rdwr_byte(sint8 &c); void rdwr_byte(uint8 &c); void rdwr_short(sint16 &i); void rdwr_short(uint16 &i); void rdwr_long(sint32 &i); void rdwr_long(uint32 &i); void rdwr_longlong(sint64 &i); void rdwr_bool(bool &i); void rdwr_double(double &dbl); // s: pointer to a string allocated with malloc! void rdwr_str(char *&s); void rdwr_str(plainstring& s); /** * appends the contents of the other buffer from [0 .. index-1] * (only if saving) */ void append(const memory_rw_t &mem); /** * appends the contents of the other buffer from [index .. max_size-1] * (only if reading) */ void append_tail(const memory_rw_t &mem); private: // Low-Level Read / Write function void rdwr(void *data, uint32 len); }; #endif simutrans-124.5/src/simutrans/network/network.cc000066400000000000000000000764311520675460500221040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* basic network functionality, borrowed from OpenTTD */ #if defined(__amiga__) // warning: IPv6 will only work on Windows XP and up ... #define USE_IP4_ONLY #endif #include #include #include #include #include #include "network.h" #include "network_address.h" #include "network_packet.h" #include "network_socket_list.h" #include "network_cmd.h" #include "network_cmd_ingame.h" #include "network_cmp_pakset.h" #include "../simconst.h" #include "../simversion.h" #ifndef NETTOOL #include "../dataobj/environment.h" #endif #include "../utils/simstring.h" #include "../tpl/slist_tpl.h" static bool network_active = false; uint16 network_server_port = 0; // list of received commands static slist_tpl received_command_queue; // blacklist address_list_t blacklist; void clear_command_queue() { while(!received_command_queue.empty()) { network_command_t *nwc = received_command_queue.remove_first(); delete nwc; } } #if USE_WINSOCK #define RET_ERR_STR { DWORD errnr = WSAGetLastError(); if( errnr!=0 ) FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM,NULL,errnr,MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),err_str,sizeof(err_str),NULL); err = err_str; return INVALID_SOCKET; } #else #define RET_ERR_STR { err = err_str; return INVALID_SOCKET; } #include #endif // global client id static uint32 client_id; void network_set_client_id(uint32 id) { client_id = id; } uint32 network_get_client_id() { return client_id; } /** * Initializes the network core (as that is needed for some platforms * @return true if the core has been initialized, false otherwise */ static bool network_initialize() { if(!network_active) { socket_list_t::reset(); #if USE_WINSOCK /* Let's load the network in windows */ WSADATA wsa; if(int err = WSAStartup( MAKEWORD(2, 2), &wsa)) { dbg->warning("NetworkInitialize()","failed loading windows socket library with %i", err); return false; } #endif } network_active = true; return true; } /** * Open a socket connected to a remote address * In case of failure err is populated with a meaningful error * @return a valid socket or INVALID_SOCKET if the connection fails */ SOCKET network_open_address(char const* cp, char const*& err) { err = NULL; if (!network_initialize()) { err = "Cannot init network!"; return INVALID_SOCKET; } #ifdef USE_IP4_ONLY // Network load. Address format e.g.: "128.0.0.1:13353" char address[1024]; static char err_str[256]; uint16 port = 13353; const char *cp2 = strrchr(cp,':'); if(cp2!=NULL) { port=atoi(cp2+1); // Copy the address part tstrncpy(address,cp,cp2-cp>sizeof(address)-1?sizeof(address)-1:cp2-cp+1); cp = address; } struct sockaddr_in server_name; memset(&server_name,0,sizeof(server_name)); server_name.sin_family=AF_INET; #if USE_WINSOCK bool ok = true; server_name.sin_addr.s_addr = inet_addr(cp); // for windows we must first try to resolve the number if((int)server_name.sin_addr.s_addr==-1) { // Bad address ok = false; struct hostent *theHost; theHost = gethostbyname( cp ); // ... before resolving a name ... if(theHost) { server_name.sin_addr = *(struct in_addr *)theHost->h_addr_list[0]; ok = true; } } if(!ok) { #else /* inet_anon does not work on BeOS; but since gethostbyname() can * do this job on all other systems too, we use it only: * instead of if(inet_aton(cp,&server_name.sin_addr)==0) { // Bad address */ struct hostent *theHost; theHost = gethostbyname( cp ); if(theHost) { server_name.sin_addr = *(struct in_addr *)theHost->h_addr_list[0]; } else {// Bad address #endif sprintf( err_str, "Bad address %s", cp ); RET_ERR_STR; } server_name.sin_port=htons(port); SOCKET my_client_socket = socket(AF_INET,SOCK_STREAM,0); if(my_client_socket==INVALID_SOCKET) { err = "Cannot create socket"; return INVALID_SOCKET; } if (connect(my_client_socket, (struct sockaddr*)&server_name, sizeof(server_name)) == -1) { sprintf(err_str, "Could not connect to %s", cp); RET_ERR_STR; } #else // Address format e.g.: "example.com:13353", "128.0.0.1:13353" or "[::1]:80" // this should be replaced with a URI parser... static char err_str[256]; // scan address string for key features const char *cp2 = strrchr( cp, ':' ); const char *ipv6end = strrchr( cp, ']' ); const char *ipv6start = strchr( cp, '[' ); // extract port if available std::string cpport; if ( cp2 != NULL && cp2 > ipv6end ) { cpport = std::string(cp2 + 1); } else { cpport = std::string("13353"); cp2 = strrchr(cp, '\0'); } // remove brackets from IPv6 addresses const bool ipv6exists = ipv6start != NULL && ipv6end != NULL; const char *const start = ipv6exists ? ipv6start + 1 : cp; const char *const end = ipv6exists ? ipv6end : cp2; // extract address const size_t addresslen = size_t(end - start); std::string const cpaddress(start, addresslen); SOCKET my_client_socket = INVALID_SOCKET; bool connected = false; #ifdef NETTOOL // Nettool doesn't have env, so fake it vector_tpl ips; ips.append_unique("::"); ips.append_unique("0.0.0.0"); #else vector_tpl const& ips = env_t::listen; #endif // For each address in the list of listen addresses try and create a socket to transmit on // Use the first one which works for (uint i = 0; !connected && i != ips.get_count(); ++i) { std::string const& ip = ips[i]; int ret; // Set up remote addrinfo struct addrinfo *remote; struct addrinfo remote_hints; memset( &remote_hints, 0, sizeof( struct addrinfo ) ); remote_hints.ai_socktype = SOCK_STREAM; remote_hints.ai_family = PF_UNSPEC; // Test remote address to ensure it is valid // Fill out remote address structure if ( (ret = getaddrinfo(cpaddress.c_str(), cpport.c_str(), &remote_hints, &remote)) != 0) { sprintf( err_str, "Bad address %s", cp ); if( i+1==ips.get_count() ) { RET_ERR_STR; } // maybe it is an IPv4 only on the second interface .. continue; } // Set up local addrinfo struct addrinfo *local; struct addrinfo local_hints; memset( &local_hints, 0, sizeof( struct addrinfo ) ); local_hints.ai_socktype = SOCK_STREAM; local_hints.ai_family = PF_UNSPEC; // Insert listen address into local_hints struct to influence local address to bind to DBG_MESSAGE( "network_open_address()", "Preparing to bind address: %s", ip.c_str() ); if ( (ret = getaddrinfo( ip.c_str(), 0, &local_hints, &local )) != 0 ) { dbg->warning( "network_open_address()", "Failed to getaddrinfo for %s, error was: %s", ip.c_str(), gai_strerror(ret) ); #ifndef NETTOOL env_t::listen.remove_at( i ); #endif i --; continue; } // Now try and open a socket to communicate with our remote endpoint struct addrinfo *walk_local; for( walk_local = local; !connected && walk_local != NULL; walk_local = walk_local->ai_next ) { char ipstr_local[INET6_ADDRSTRLEN]; // Validate address + get string representation for logging if ( (ret = getnameinfo( (walk_local->ai_addr), (socklen_t)walk_local->ai_addrlen, ipstr_local, sizeof(ipstr_local), NULL, 0, NI_NUMERICHOST )) !=0 ) { dbg->warning( "network_open_address()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret) ); continue; } DBG_MESSAGE( "network_open_address()", "Potential local address: %s", ipstr_local ); my_client_socket = socket( walk_local->ai_family, walk_local->ai_socktype, walk_local->ai_protocol ); if ( my_client_socket == INVALID_SOCKET ) { DBG_MESSAGE( "network_open_address()", "Could not create socket! Error: \"%s\"", strerror(GET_LAST_ERROR()) ); continue; } // Bind socket to local IP if ( bind( my_client_socket, walk_local->ai_addr, walk_local->ai_addrlen ) ) { DBG_MESSAGE( "network_open_address()", "Unable to bind socket to local IP address! Error: \"%s\"", strerror(GET_LAST_ERROR()) ); network_close_socket( my_client_socket ); my_client_socket = INVALID_SOCKET; continue; } // For each address in remote, try and connect struct addrinfo *walk_remote; for ( walk_remote = remote; !connected && walk_remote != NULL; walk_remote = walk_remote->ai_next ) { char ipstr_remote[INET6_ADDRSTRLEN]; // Validate remote address + get string representation for logging if ( (ret = getnameinfo( walk_remote->ai_addr, (socklen_t)walk_remote->ai_addrlen, ipstr_remote, sizeof(ipstr_remote), NULL, 0, NI_NUMERICHOST )) !=0 ) { dbg->warning( "network_open_address()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret) ); continue; } DBG_MESSAGE( "network_open_address()", "Potential remote address: %s", ipstr_remote ); #ifdef WIN32 // put socked in non-blocking mode... (does only work for windows) u_long block = 1; if( ioctlsocket(my_client_socket, FIONBIO, &block) != 0 ) { my_client_socket = INVALID_SOCKET; DBG_MESSAGE("network_open_address()", "Failed to make unblocking socket."); continue; } #endif if (connect(my_client_socket, walk_remote->ai_addr, (socklen_t)walk_remote->ai_addrlen) != 0) { if( GET_LAST_ERROR() != EINPROGRESS) { // connection failed network_close_socket(my_client_socket); continue; } // connection pending fd_set setW, setE; FD_ZERO(&setW); FD_SET(my_client_socket, &setW); FD_ZERO(&setE); FD_SET(my_client_socket, &setE); timeval time_out; time_out.tv_sec = 2; // 2 seconds or joining a server would not make sense time_out.tv_usec = 0; int ret = select(my_client_socket+1, NULL, &setW, &setE, &time_out); if (ret <= 0) { // select() failed or connection timed out if (ret < 0) { DBG_MESSAGE("network_open_address()", "Could not connect using this socket. select() Error: \"%s\"", strerror(GET_LAST_ERROR())); } else { DBG_MESSAGE("network_open_address()", "Could not connect using this socket. select() timeout" ); err = "Connection timeout"; } #ifdef WIN32 ioctlsocket(my_client_socket, FIONBIO, &block); // back to blocking #endif continue; } if( FD_ISSET(my_client_socket, &setE) ) { // connection failed DBG_MESSAGE("network_open_address()", "Could not connect FD_ISSET failed."); #ifdef WIN32 ioctlsocket(my_client_socket, FIONBIO, &block); // back to blocking #endif continue; } // connection successful #ifdef WIN32 // put socket in blocking mode... block = 0; bool blocking_mode = ioctlsocket(my_client_socket, FIONBIO, &block) == 0; if (!blocking_mode) { DBG_MESSAGE("network_open_address()", "Could not reset to non-blocking."); network_close_socket(my_client_socket); continue; } // linux non-blocking sockets seems to not work at all! #endif } connected = true; } } // If no connection throw away this socket and try the next one if (!connected) { network_close_socket(my_client_socket); my_client_socket = INVALID_SOCKET; } freeaddrinfo( local ); freeaddrinfo( remote ); } if ( my_client_socket == INVALID_SOCKET ) { sprintf( err_str, "Could not connect to %s", cp ); RET_ERR_STR; } #endif return my_client_socket; } /** * Start up server on the port specified * Server will listen on all addresses specified in @p listen_addrs * @return true on success */ bool network_init_server( int port, const vector_tpl &listen_addrs ) { if ( port==0 ) { dbg->fatal( "network_init_server()", "Cannot host on port 0!" ); } // First activate network if ( !network_initialize() ) { dbg->fatal( "network_init_server()", "Failed to initialize network!" ); } socket_list_t::reset(); #ifdef USE_IP4_ONLY (void)listen_addrs; const SOCKET my = socket( PF_INET, SOCK_STREAM, 0 ); if ( my == INVALID_SOCKET ) { dbg->fatal( "network_init_server()", "Failed to open socket!" ); return false; } struct sockaddr_in name; name.sin_family = AF_INET; name.sin_port = htons( port ); name.sin_addr.s_addr = htonl( INADDR_ANY ); if ( bind( my, (struct sockaddr *)&name, sizeof( name ) ) == -1 ) { dbg->fatal( "network_init_server()", "Bind failed!" ); return false; } /* Max pending connections */ if ( listen( my, MAX_PLAYER_COUNT ) == -1 ) { dbg->fatal( "network_init_server()", "Listen failed for %i sockets!", MAX_PLAYER_COUNT ); return false; } socket_list_t::add_server( my ); #else // For each address in the list of listen addresses try and create a socket to listen on for(std::string const& ip : listen_addrs) { int ret; char port_nr[16]; sprintf( port_nr, "%u", port ); struct addrinfo *server; struct addrinfo hints; memset( &hints, 0, sizeof( struct addrinfo ) ); hints.ai_socktype = SOCK_STREAM; // Insert potential listen address into hints struct to influence local address to bind to DBG_MESSAGE( "network_init_server()", "Preparing to bind address: \"%s\"", ip.c_str() ); hints.ai_family = PF_UNSPEC; if ( (ret = getaddrinfo( ip.c_str(), port_nr, &hints, &server )) != 0 ) { dbg->fatal( "network_init_server()", "Call to getaddrinfo() failed for: \"%s\", error was: \"%s\" - check listen directive in simuconf.tab!", ip.c_str(), gai_strerror(ret) ); } else { dbg->message( "network_init_server()", "Attempting to bind listening sockets for: \"%s\"\n", ip.c_str() ); } SOCKET server_socket; struct addrinfo *walk; // Open a listen socket for each IP address specified by this entry in the listen list for ( walk = server; walk != NULL; walk = walk->ai_next ) { char ipstr[INET6_ADDRSTRLEN]; // Validate address + get string representation for logging if ( (ret = getnameinfo( walk->ai_addr, (socklen_t)walk->ai_addrlen, ipstr, sizeof(ipstr), NULL, 0, NI_NUMERICHOST )) != 0 ) { dbg->warning( "network_init_server()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret) ); continue; } DBG_MESSAGE( "network_init_server()", "Potential bind address: %s", ipstr ); server_socket = socket( walk->ai_family, walk->ai_socktype, walk->ai_protocol ); if ( server_socket == INVALID_SOCKET ) { dbg->warning( "network_init_server()", "Could not create socket! Error: \"%s\"", strerror(GET_LAST_ERROR()) ); continue; } /* Disable IPv4-mapped IPv6 addresses for this IPv6 listen socket This ensures that we are using separate sockets for dual-stack, one for v4, one for v6 */ if ( walk->ai_family == AF_INET6 ) { int const on = 1; if ( setsockopt(server_socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&on), sizeof(on)) != 0 ) { dbg->warning( "network_init_server()", "Call to setsockopt(IPV6_V6ONLY) failed for: \"%s\", error was: \"%s\"", ip.c_str(), strerror(GET_LAST_ERROR()) ); network_close_socket( server_socket ); server_socket = INVALID_SOCKET; continue; } } // Enable reusing of local addresses int const enable = 1; if ( setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&enable), sizeof(enable)) != 0 ) { dbg->warning( "network_init_server()", "Call to setsockopt(SO_REUSEADDR) failed for: \"%s\", error was: \"%s\"", ip.c_str(), strerror(GET_LAST_ERROR()) ); } if ( bind( server_socket, walk->ai_addr, walk->ai_addrlen ) ) { /* Unable to bind a socket - abort execution as we are supposed to be a server on this interface */ dbg->fatal( "network_init_server()", "Unable to bind socket to IP address: \"%s\", error was: \"%s\"", ipstr, strerror(GET_LAST_ERROR()) ); } if ( listen( server_socket, 32 ) == -1 ) { /* Unable to listen on bound socket - abort execution as we are supposed to be a server on this interface */ dbg->fatal( "network_init_server()", "Unable to set socket to listen for incoming connections on: \"%s\"", ipstr ); } dbg->message( "network_init_server()", "Added valid listen socket for address: \"%s\"\n", ipstr); socket_list_t::add_server( server_socket ); } freeaddrinfo( server ); } // Fatal error if no server sockets could be opened, since we're supposed to be a server! if ( socket_list_t::get_server_sockets() == 0 ) { dbg->fatal( "network_init_server()", "Unable to add any server sockets!" ); } else { dbg->message("network_init_server", "Server started, added %d server sockets", socket_list_t::get_server_sockets()); } #endif network_server_port = port; client_id = 0; network_reset_server(); return true; } void network_set_socket_nodelay( SOCKET sock ) { #if (defined(TCP_NODELAY) || COLOUR_DEPTH == 0) // do not wait to join small (command) packets when sending (may cause 200ms delay!) // force this for dedicated servers int b = 1; setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b) ); #else #warning TCP_NODELAY not defined. Expect multiplayer problems. (void)sock; #endif } network_command_t* network_get_received_command() { if (!received_command_queue.empty()) { return received_command_queue.remove_first(); } return NULL; } /* do appropriate action for network games: * - server: accept connection to a new client * - all: receive commands and puts them to the received_command_queue */ network_command_t *network_check_activity(int timeout) { fd_set fds; FD_ZERO(&fds); socket_list_t::fill_set(&fds); // time out: MAC complains about too long timeouts struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000ul; int action = select( FD_SETSIZE, &fds, NULL, NULL, &tv ); if( action<=0 ) { // timeout: return command from the queue return network_get_received_command(); } // accept new connection socket_list_t::server_socket_iterator_t iter_s(&fds); while(iter_s.next()) { SOCKET accept_sock = iter_s.get_current(); if( accept_sock!=INVALID_SOCKET ) { struct sockaddr_in client_name; socklen_t size = sizeof(client_name); SOCKET s = accept(accept_sock, (struct sockaddr *)&client_name, &size); if( s!=INVALID_SOCKET ) { #if USE_WINSOCK uint32 ip = ntohl((uint32)client_name.sin_addr.S_un.S_addr); #else uint32 ip = ntohl((uint32)client_name.sin_addr.s_addr); #endif if (blacklist.contains(net_address_t( ip ))) { // refuse connection network_close_socket(s); continue; } #ifdef __BEOS__ char name[256]; sprintf(name, "%lh", client_name.sin_addr.s_addr ); #else const char *name = inet_ntoa(client_name.sin_addr); #endif dbg->message("check_activity()", "Accepted connection from: %s.", name); socket_list_t::add_client(s, ip); } } } // receive from clients socket_list_t::client_socket_iterator_t iter_c(&fds); while(iter_c.next()) { SOCKET sender = iter_c.get_current(); if (sender != INVALID_SOCKET && socket_list_t::has_client(sender)) { uint32 client_id = socket_list_t::get_client_id(sender); network_command_t *nwc = socket_list_t::get_client(client_id).receive_nwc(); if (nwc) { received_command_queue.append(nwc); dbg->warning( "network_check_activity()", "received cmd %s (id %d) from socket[%d]", nwc->get_name(), nwc->get_id(), sender ); } // errors are caught and treated in socket_info_t::receive_nwc } } return network_get_received_command(); } void network_process_send_queues(int timeout) { fd_set fds; FD_ZERO(&fds); socket_list_t::fill_set(&fds); // time out struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000ul; int action = select( FD_SETSIZE, NULL, &fds, NULL, &tv ); if( action<=0 ) { // timeout: return return; } // send to clients socket_list_t::client_socket_iterator_t iter_c(&fds); while(iter_c.next() && action>0) { SOCKET sock = iter_c.get_current(); if (sock != INVALID_SOCKET && socket_list_t::has_client(sock)) { uint32 client_id = socket_list_t::get_client_id(sock); socket_list_t::get_client(client_id).process_send_queue(); // errors are caught and treated in socket_info_t::process_send_queue } action --; } } bool network_check_server_connection() { if( !network_server_port ) { // I am client // If I am playing, playing_clients should be at least one. if( socket_list_t::get_playing_clients()==0 ) { return false; } fd_set fds_read, fds_write; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds_read); socket_list_t::fill_set(&fds_read); FD_ZERO(&fds_write); socket_list_t::fill_set(&fds_write); int action = select( FD_SETSIZE, &fds_read, &fds_write, NULL, &tv ); if( action < 0 ) { // error - connection lost return false; } } return true; } // send data to all PLAYING clients // nwc is invalid after the call void network_send_all(network_command_t* nwc, bool exclude_us, uint8 player_nr) { if (nwc) { nwc->prepare_to_send(); socket_list_t::send_all(nwc, true, player_nr); if( !exclude_us && network_server_port ) { // I am the server nwc->get_packet()->sent_by_server(); received_command_queue.append(nwc); } else { delete nwc; } } } // send data to server // nwc is invalid after the call void network_send_server(network_command_t* nwc ) { if (nwc) { nwc->prepare_to_send(); if( !network_server_port ) { // I am client socket_list_t::send_all(nwc, true); delete nwc; } else { // I am the server nwc->get_packet()->sent_by_server(); received_command_queue.append(nwc); } } } /** * send data to dest * * @param dest * @param buf the data * @param size length of buffer and number of bytes to be sent * @param count * @param timeout_ms * @return true if data was completely send, false if an error occurs and connection needs to be closed */ bool network_send_data( SOCKET dest, const char *buf, const uint16 size, uint16 &count, const int timeout_ms ) { count = 0; #if USE_WINSOCK == 0 // ignore SIGPIPE sent by send() function. signal(SIGPIPE, SIG_IGN); #endif while (count < size) { const int sent = send(dest, buf+count, size-count, 0); if (sent == -1) { const int err = GET_LAST_ERROR(); if (err != EWOULDBLOCK) { dbg->warning("network_send_data", "Could not send to [%d]: \"%s\"", dest, strerror(err)); return false; } if (timeout_ms <= 0) { // no timeout, continue sending later return true; } else { // try again, test whether sending is possible fd_set fds; FD_ZERO(&fds); FD_SET(dest,&fds); struct timeval tv; tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000ul; // can we write? if( select( FD_SETSIZE, NULL, &fds, NULL, &tv )!=1 ) { dbg->warning("network_send_data", "Could not write to socket [%d]", dest); return false; } } continue; } if (sent == 0) { // connection closed dbg->warning("network_send_data", "Connection [%d] already closed (sent %hu of %hu)", dest, count, size ); return false; } count += sent; DBG_DEBUG4("network_send_data", "Sent %d bytes to socket[%d]; size=%d, left=%d", count, dest, size, size-count ); } #if USE_WINSOCK == 0 signal(SIGPIPE, SIG_DFL); #endif // we reach here only if data are sent completely return true; } /** * receive data from sender * * @param sender * @param dest the destination buffer * @param len length of destination buffer and number of bytes to be received * @param[out] received number of received bytes is returned here * @param timeout_ms * @return true if connection is still valid, false if an error occurs and connection needs to be closed */ bool network_receive_data( SOCKET sender, void *dest, const uint16 len, uint16 &received, const int timeout_ms ) { received = 0; char *ptr = (char *)dest; do { fd_set fds; FD_ZERO(&fds); FD_SET(sender,&fds); struct timeval tv; tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000ul; // can we read? if( select( FD_SETSIZE, &fds, NULL, NULL, &tv )!=1 ) { return true; } // now receive const int res = recv( sender, ptr+received, len-received, 0 ); if (res == -1) { const int err = GET_LAST_ERROR(); if (err != EWOULDBLOCK) { dbg->warning("network_receive_data", "Could not receive from [%d]: \"%s\"", sender, strerror(err)); return false; } // try again later return true; } if (res == 0) { // connection closed // output warning / throw fatal error depending on heavy mode setting #ifndef NETTOOL void (log_t::*outfn)(const char*, const char*, ...) = (env_t::network_heavy_mode == 2 ? &log_t::fatal : &log_t::warning); (dbg->*outfn)("network_receive_data", "Connection [%d] already closed", sender); #endif return false; } received += res; } while( received #include } #if MINIUPNPC_API_VERSION < 14 #define upnpDiscover(a,b,c,d,e,f,g) upnpDiscover(a,b,c,d,e,g) #define UPNP_LOCAL_PORT_ANY 0 #endif bool prepare_for_server( char *externalIPAddress, char *externalAltIPAddress, int port ) { char lanaddr[64] = "unset"; /* my ip address on the LAN */ int error = 0; const char *multicastif = 0; const char *minissdpdpath = 0; int localport = UPNP_LOCAL_PORT_ANY; int ipv6 = 0; // probably not needed for IPv6 ever ... unsigned char ttl = 2; (void)ttl; /* defaulting to 2 */ struct UPNPDev *devlist = 0; bool has_IP = false; if( (devlist = upnpDiscover( 2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error )) ) { struct UPNPUrls urls; struct IGDdatas data; #if MINIUPNPC_API_VERSION <= 17 UPNP_GetValidIGD( devlist, &urls, &data, lanaddr, sizeof(lanaddr) ); #else char wanaddr[64] = "uset"; UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(lanaddr)); #endif // we must know our IP address first if( UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress) == UPNPCOMMAND_SUCCESS ) { // this is our ID (at least the routes tells us this) char eport[19]; char *iport = eport; sprintf( eport, "%d", port ); // remove anz forwarding UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport, "TCP", NULL); // setting up tcp redirect forever (last parameter "0") if( UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, eport, iport, lanaddr, "simutrans", "TCP", 0, "0") == UPNPCOMMAND_SUCCESS ) { // ok, we have our ID and redirected a port to us has_IP = true; } else { dbg->warning( "prepare_for_server()", "Could not redirect port (but may be still ok" ); has_IP = true; } } FreeUPNPUrls(&urls); } freeUPNPDevlist(devlist); externalAltIPAddress[0] = 0; // use the same routine as later the announce routine, otherwise update with dynamic IP fails cbuffer_t myIPaddr, altIPaddr; if( get_external_IP( myIPaddr, altIPaddr ) ) { has_IP = true; strcpy( externalIPAddress, myIPaddr ); if( altIPaddr.len() ) { strcpy( externalAltIPAddress, altIPaddr ); } } return has_IP; } // removes the redirect (or do nothing) void remove_port_forwarding( int port ) { if( port <= 0 || env_t::easy_server != 1 ) { return; } char lanaddr[64] = "unset"; /* my ip address on the LAN */ char externalIPAddress[64]; int error = 0; const char *multicastif = 0; const char *minissdpdpath = 0; int localport = UPNP_LOCAL_PORT_ANY; int ipv6 = 0; // probably not needed for IPv6 ever ... unsigned char ttl = 2; (void)ttl; /* defaulting to 2 */ struct UPNPDev *devlist = 0; if( (devlist = upnpDiscover( 2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error )) ) { struct UPNPUrls urls; struct IGDdatas data; #if MINIUPNPC_API_VERSION <= 17 UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); #else char wanaddr[64] = "uset"; UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(lanaddr)); #endif // we must know our IP address first if( UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress) == UPNPCOMMAND_SUCCESS ) { // this is our ID (at least the routes tells us this) char eport[19]; sprintf( eport, "%d", port ); UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport, "TCP", NULL); } FreeUPNPUrls(&urls); } freeUPNPDevlist(devlist); } #else // or we just get only our IP and hope we are not behind a router ... bool prepare_for_server(char* externalIPAddress, char* externalAltIPAddress, int /*port*/) { externalAltIPAddress[0] = 0; // use the same routine as later the announce routine, otherwise update with dynamic IP fails cbuffer_t myIPaddr, altIPaddr; if (get_external_IP(myIPaddr, altIPaddr)) { strcpy(externalIPAddress, myIPaddr); if (altIPaddr.len()) { strcpy(externalAltIPAddress, altIPaddr); } return true; } return false; } void remove_port_forwarding( int ) { } #endif #endif // not NETTOOL simutrans-124.5/src/simutrans/network/network.h000066400000000000000000000104471520675460500217410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_H #define NETWORK_NETWORK_H #if defined(_WIN32) && !defined(__CYGWIN__) #define USE_WINSOCK 1 #else #define USE_WINSOCK 0 #endif // windows headers #if USE_WINSOCK // must be include before all simutrans stuff! # include //# include # include # ifndef IPV6_V6ONLY # define IPV6_V6ONLY (27) # endif # if WINVER<=0x0500 // compiling for Windows 2000 # define USE_IP4_ONLY # endif # define GET_LAST_ERROR() WSAGetLastError() # include # undef EINPROGRESS # define EINPROGRESS WSAEWOULDBLOCK #else // beos specific headers # ifdef __BEOS__ # include # include // non-beos / non-windows # else # include # include # include # include # include # include # include # include # endif # ifdef __HAIKU__ # include # endif // all non-windows # include # include // to keep compatibility to MS windows typedef int SOCKET; # define INVALID_SOCKET -1 # define GET_LAST_ERROR() (errno) #endif #include "../tpl/vector_tpl.h" #include "../simconst.h" #include "../simtypes.h" #include "../utils/cbuffer.h" // version of network protocol code #define NETWORK_VERSION (1) class network_command_t; class gameinfo_t; class karte_t; void network_close_socket( SOCKET sock ); void network_set_socket_nodelay( SOCKET sock ); // open a socket or give a decent error message SOCKET network_open_address(char const* cp, char const*& err); // if successful, starts a server on this port bool network_init_server( int port, const vector_tpl &listen_addrs ); /** * returns pointer to command or NULL */ network_command_t* network_get_received_command(); /** * do appropriate action for network games: * - server: accept connection to a new client * - all: receive commands and puts them to the received_command_queue * * @param timeout in milliseconds * @return pointer to first received command * more commands can be obtained by call to network_get_received_command */ network_command_t* network_check_activity(int timeout); /** * send data to dest: * if timeout_ms is positive: * try to send all data, return true if all data are sent otherwise false * if timeout_ms is not positive: * try to send as much as possible but return after one send attempt * return true if connection is still open and sending can be continued later * * @param buf the data * @param size length of buffer and number of bytes to be sent * @param[out] count number of bytes actually sent * @param timeout_ms time-out in milliseconds */ bool network_send_data( SOCKET dest, const char *buf, const uint16 size, uint16 &count, const int timeout_ms ); /** * receive data from sender * @param dest the destination buffer * @param len length of destination buffer and number of bytes to be received * @param[out] received number of received bytes is returned here * @param timeout_ms time-out in milliseconds * @return true if connection is still valid, false if an error occurs and connection needs to be closed */ bool network_receive_data( SOCKET sender, void *dest, uint16 len, uint16 &received, int timeout_ms ); void network_process_send_queues(int timeout); // true, if I can write on the server connection bool network_check_server_connection(); /** * send command to all clients (even us). * if @p player_nr is valid, then send only to clients with this player unlocked * @note nwc is invalid after the call */ void network_send_all(network_command_t* nwc, bool exclude_us, uint8 player_nr = PLAYER_UNOWNED); // send data to server only // nwc is invalid after the call void network_send_server(network_command_t* nwc ); void network_reset_server(); void network_core_shutdown(); // get & set our id on the server uint32 network_get_client_id(); void network_set_client_id(uint32 id); bool get_external_IP( cbuffer_t &myIPaddr, cbuffer_t &alt_IP ); // trys to open port on router (if there) and get external IP bool prepare_for_server( char *externalIPAddress, char *alter_IP, int port ); // removes the redirect (or do nothing) void remove_port_forwarding( int port ); #endif simutrans-124.5/src/simutrans/network/network_address.cc000066400000000000000000000024541520675460500236030ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "network_address.h" #include "network_packet.h" #include #include net_address_t::net_address_t(uint32 ip_, uint32 mask_) : ip(ip_), mask(mask_) { ipstr[0] = '\0'; init_ipstr(); } net_address_t::net_address_t(const char *text) { ipstr[0] = '\0'; uint32 offset = 0; ip = 0; mask = 0; for(sint8 j=24; j>=0; j-=8) { uint32 n = atoi(text); ip |= (n & 0xff) << j; mask |= 0xff << j; text = strchr(text+offset, '.'); if (text) { text++; } else { break; } } init_ipstr(); } void net_address_t::init_ipstr() { if ( ipstr[0] == '\0' ) { sprintf( ipstr, "%i.%i.%i.%i", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255 ); } } const char* net_address_t::get_str () const { return ipstr; } void address_list_t::rdwr(packet_t *packet) { uint32 count = get_count(); packet->rdwr_long(count); // Each entry costs 8 bytes on the wire (ip + mask); refuse a count // beyond what the packet body can hold. if (packet->is_loading() && count > (MAX_PACKET_LEN - HEADER_SIZE) / 8) { packet->failed(); return; } for(uint32 i=0; iis_loading()) { append(net_address_t()); } (*this)[i].rdwr(packet); } } simutrans-124.5/src/simutrans/network/network_address.h000066400000000000000000000025101520675460500234360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_ADDRESS_H #define NETWORK_NETWORK_ADDRESS_H #include "../simtypes.h" #include "../tpl/vector_tpl.h" class packet_t; class net_address_t { public: uint32 ip; uint32 mask; private: char ipstr[16]; /** * Generate human readable representation of this IP address */ void init_ipstr(); public: net_address_t(uint32 ip_=0, uint32 mask_ = 0xffffffff); net_address_t(const char *); bool matches(const net_address_t &other) const { return (other.ip & mask)==(ip & mask); } template void rdwr(F *packet) { packet->rdwr_long(ip); packet->rdwr_long(mask); if (packet->is_loading()) { ipstr[0] = '\0'; init_ipstr(); } } /** * Return human readable representation of this IP address */ const char* get_str () const; bool operator==(const net_address_t& other) const { return ip==other.ip && mask == other.mask; } uint32 get_ip() const { return ip; } }; class address_list_t : public vector_tpl { public: address_list_t() : vector_tpl(10) {} bool contains(const net_address_t &other) { for(net_address_t const& i : *this) { if (i.matches(other)) { return true; } } return false; } void rdwr(packet_t *packet); }; #endif simutrans-124.5/src/simutrans/network/network_cmd.cc000066400000000000000000000107501520675460500227170ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "network_cmd.h" #include "network.h" #include "network_packet.h" #include "network_socket_list.h" #ifndef NETTOOL #include "../dataobj/environment.h" #endif #include // needed by world to kick clients if needed SOCKET network_command_t::get_sender() { return packet->get_sender(); } network_command_t::network_command_t(uint16 id_) { packet = new packet_t(); id = id_; our_client_id = (uint32)network_get_client_id(); ready = false; } // default constructor network_command_t::network_command_t() : packet(NULL), id(0), our_client_id(0), ready(false) {} bool network_command_t::receive(packet_t *p) { ready = true; delete packet; packet = p; id = p->get_id(); rdwr(); return (!packet->has_failed()); } network_command_t::~network_command_t() { delete packet; packet = NULL; } void network_command_t::rdwr() { if (packet->is_saving()) { packet->set_id(id); ready = true; } packet->rdwr_long(our_client_id); dbg->message("network_command_t::rdwr", "%s packet_id=%s, client_id=%u", packet->is_saving() ? "write" : "read", get_name(), our_client_id); } void network_command_t::prepare_to_send() { // saves the data to the packet if(!ready) { rdwr(); } } bool network_command_t::send(SOCKET s) { prepare_to_send(); packet->send(s, true); bool ok = packet->is_ready(); if (!ok) { dbg->warning("network_command_t::send", "Sending %s to [%d] failed", get_name(), s); } return ok; } packet_t* network_command_t::copy_packet() const { if (packet) { return new packet_t(*packet); } else { return NULL; } } void nwc_auth_player_t::rdwr() { network_command_t::rdwr(); for(uint32 i=0; i<20; i++) { packet->rdwr_byte( hash[i] ); } packet->rdwr_byte( player_nr ); packet->rdwr_short(player_unlocked); } nwc_service_t::~nwc_service_t() { // vector_tpl only frees its own array, not the pointed-to // elements. if (socket_info) { for (socket_info_t *si : *socket_info) { delete si; } delete socket_info; } delete address_list; free(text); } extern address_list_t blacklist; void nwc_service_t::rdwr() { network_command_t::rdwr(); #ifndef NETTOOL // nwc_service_t is the admin-tool <-> server channel: nettool // drives the request side, the server drives the response side. // An in-game multiplayer client never legitimately exchanges // nwc_service_t. Bail before parsing the body so a peer can't // drive socket_list_t::rdwr / address_list_t::rdwr through an // unbounded allocation loop with a wire-supplied count: // - server side: closes the pre-auth crash, since execute()'s // admin-auth gate fires only after rdwr() returns; // - client side: closes the equivalent OOM that a malicious // server could push to a joined client. if (!env_t::server) { return; } #endif packet->rdwr_long(flag); packet->rdwr_long(number); switch(flag) { case SRVC_LOGIN_ADMIN: case SRVC_BAN_IP: case SRVC_UNBAN_IP: case SRVC_ADMIN_MSG: case SRVC_GET_COMPANY_LIST: case SRVC_GET_COMPANY_INFO: packet->rdwr_str(text); break; // Direction-asymmetric: the list rides on the server->admin // response only; the admin->server request body is just the // flag. Each side handles exactly one leg, so there's no // untrusted-bytes-into-list-count path left. case SRVC_GET_CLIENT_LIST: #ifdef NETTOOL if (packet->is_loading()) { socket_info = new vector_tpl(10); socket_list_t::rdwr(packet, socket_info); } #else if (packet->is_saving()) { socket_list_t::rdwr(packet); } #endif break; case SRVC_GET_BLACK_LIST: #ifdef NETTOOL if (packet->is_loading()) { address_list = new address_list_t(); address_list->rdwr(packet); } #else if (packet->is_saving()) { blacklist.rdwr(packet); } #endif break; default: ; } } const char *network_command_t::id_to_string(uint16 id) { #define CASE_TO_STRING(c) case c: return #c switch (id) { CASE_TO_STRING(NWC_INVALID); CASE_TO_STRING(NWC_GAMEINFO); CASE_TO_STRING(NWC_NICK); CASE_TO_STRING(NWC_CHAT); CASE_TO_STRING(NWC_JOIN); CASE_TO_STRING(NWC_SYNC); CASE_TO_STRING(NWC_GAME); CASE_TO_STRING(NWC_READY); CASE_TO_STRING(NWC_TOOL); CASE_TO_STRING(NWC_CHECK); CASE_TO_STRING(NWC_PAKSETINFO); CASE_TO_STRING(NWC_SERVICE); CASE_TO_STRING(NWC_AUTH_PLAYER); CASE_TO_STRING(NWC_CHG_PLAYER); CASE_TO_STRING(NWC_SCENARIO); CASE_TO_STRING(NWC_SCENARIO_RULES); CASE_TO_STRING(NWC_STEP); } return ""; } simutrans-124.5/src/simutrans/network/network_cmd.h000066400000000000000000000070171520675460500225630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_CMD_H #define NETWORK_NETWORK_CMD_H #include "../simtypes.h" #include "../tpl/slist_tpl.h" #include "../tpl/vector_tpl.h" #include "network.h" #include "../utils/sha1_hash.h" class address_list_t; class karte_t; class packet_t; class socket_info_t; // actual commands enum { NWC_INVALID = 0, NWC_GAMEINFO, NWC_NICK, NWC_CHAT, NWC_JOIN, NWC_SYNC, NWC_GAME, NWC_READY, NWC_TOOL, NWC_CHECK, NWC_PAKSETINFO, NWC_SERVICE, NWC_AUTH_PLAYER, NWC_CHG_PLAYER, NWC_SCENARIO, NWC_SCENARIO_RULES, NWC_STEP, NWC_COUNT }; class network_command_t { protected: packet_t *packet; // always send: uint16 id; uint32 our_client_id; // ready for sending bool ready; public: network_command_t(uint16 id); network_command_t(); virtual ~network_command_t(); // receive: calls rdwr from packet // return true on success bool receive(packet_t *p); // calls rdwr if packet is empty void prepare_to_send(); /** * sends to a client * sends complete command-packet * @return whether send was successful */ bool send(SOCKET s); // write our data to the packet virtual void rdwr(); // executes the command // (see network_world_command_t::execute() ) // if returns true this can be deleted afterwards virtual bool execute(karte_t *) { return true;} uint16 get_id() { return id;} const char *get_name() const { return id_to_string(id); } SOCKET get_sender(); packet_t *get_packet() const { return packet; } /** * returns ptr to a copy of the packet */ packet_t *copy_packet() const; // creates an instance: // gets the nwc-id from the packet, and reads its data static network_command_t* read_from_packet(packet_t *p); static const char *id_to_string(uint16 id); }; /** * base class for any service commands */ class nwc_service_t : public network_command_t { public: uint32 flag; enum { SRVC_LOGIN_ADMIN = 0, SRVC_ANNOUNCE_SERVER = 1, SRVC_GET_CLIENT_LIST = 2, SRVC_KICK_CLIENT = 3, SRVC_BAN_CLIENT = 4, SRVC_GET_BLACK_LIST = 5, SRVC_BAN_IP = 6, SRVC_UNBAN_IP = 7, SRVC_ADMIN_MSG = 8, SRVC_SHUTDOWN = 9, SRVC_FORCE_SYNC = 10, SRVC_GET_COMPANY_LIST = 11, SRVC_GET_COMPANY_INFO = 12, SRVC_UNLOCK_COMPANY = 13, SRVC_REMOVE_COMPANY = 14, SRVC_LOCK_COMPANY = 15, SRVC_MAX }; uint32 number; char *text; vector_tpl *socket_info; address_list_t *address_list; nwc_service_t() : network_command_t(NWC_SERVICE), text(NULL), socket_info(NULL), address_list(NULL) { } ~nwc_service_t(); #ifndef NETTOOL bool execute(karte_t *) OVERRIDE; #endif void rdwr() OVERRIDE; }; /** * nwc_auth_player_t * @from-client: client sends password hash to unlock player / set player password * server sends nwc_auth_player_t to sender * @from-server: * information whether players are locked / unlocked */ class nwc_auth_player_t : public network_command_t { public: nwc_auth_player_t() : network_command_t(NWC_AUTH_PLAYER), hash(), player_unlocked(0), player_nr(255) { } nwc_auth_player_t(uint8 nr, const pwd_hash_t& hash_) : network_command_t(NWC_AUTH_PLAYER), hash(hash_), player_unlocked(0), player_nr(nr) { } #ifndef NETTOOL bool execute(karte_t *) OVERRIDE; #endif void rdwr() OVERRIDE; pwd_hash_t hash; uint16 player_unlocked; uint8 player_nr; /** * sets unlocked flags for playing at server */ static void init_player_lock_server(karte_t *); }; #endif simutrans-124.5/src/simutrans/network/network_cmd_ingame.cc000066400000000000000000001351131520675460500242400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "network_cmd_ingame.h" #include "network.h" #include "network_file_transfer.h" #include "network_packet.h" #include "network_socket_list.h" #include "network_cmp_pakset.h" #include "network_cmd_scenario.h" #include "../dataobj/loadsave.h" #include "../dataobj/gameinfo.h" #include "../dataobj/scenario.h" #include "../tool/simmenu.h" #include "../simversion.h" #include "../gui/simwin.h" #include "../simmesg.h" #include "../sys/simsys.h" #include "../dataobj/environment.h" #include "../player/simplay.h" #include "../gui/player_frame.h" #include "../utils/simrandom.h" #include "../utils/cbuffer.h" #include "../utils/csv.h" #include "../display/viewport.h" #include "../script/script.h" // callback for calls to tools network_command_t* network_command_t::read_from_packet(packet_t *p) { // check data if (p==NULL || p->has_failed() || !p->check_version()) { delete p; dbg->warning("network_command_t::read_from_packet", "error in packet"); return NULL; } network_command_t* nwc = NULL; switch (p->get_id()) { case NWC_GAMEINFO: nwc = new nwc_gameinfo_t(); break; case NWC_NICK: nwc = new nwc_nick_t(); break; case NWC_CHAT: nwc = new nwc_chat_t(); break; case NWC_JOIN: nwc = new nwc_join_t(); break; case NWC_SYNC: nwc = new nwc_sync_t(); break; case NWC_GAME: nwc = new nwc_game_t(); break; case NWC_READY: nwc = new nwc_ready_t(); break; case NWC_TOOL: nwc = new nwc_tool_t(); break; case NWC_CHECK: nwc = new nwc_check_t(); break; case NWC_PAKSETINFO: nwc = new nwc_pakset_info_t(); break; case NWC_SERVICE: nwc = new nwc_service_t(); break; case NWC_AUTH_PLAYER: nwc = new nwc_auth_player_t(); break; case NWC_CHG_PLAYER: nwc = new nwc_chg_player_t(); break; case NWC_SCENARIO: nwc = new nwc_scenario_t(); break; case NWC_SCENARIO_RULES: nwc = new nwc_scenario_rules_t(); break; case NWC_STEP: nwc = new nwc_step_t(); break; default: dbg->warning("network_command_t::read_from_socket", "received unknown packet id %d", p->get_id()); } if (nwc == NULL) { // Unknown / unsupported wire id: free p before returning so a // malicious peer can't drive the server out of memory by // flooding it with junk-id packets (callers null out their // packet pointer unconditionally after this returns, so they // won't free it either). delete p; return NULL; } if (!nwc->receive(p) || p->has_failed()) { dbg->warning("network_command_t::read_from_packet", "error while reading cmd from packet"); delete nwc; nwc = NULL; } else if (env_t::server) { // The wire-supplied our_client_id is attacker-controlled. // Identify the sender by its socket instead, so any later // auth check (nwc_auth_player_t, nwc_chg_player_t, // nwc_tool_t) reads the real slot and cannot be tricked // into looking up someone else's player_unlocked bitmap // or indexing past the socket list. nwc->our_client_id = socket_list_t::get_client_id(p->get_sender()); } return nwc; } void nwc_gameinfo_t::rdwr() { network_command_t::rdwr(); packet->rdwr_long(len); } // will send the gameinfo to the client bool nwc_gameinfo_t::execute(karte_t *welt) { if (env_t::server) { DBG_MESSAGE("nwc_gameinfo_t::execute", ""); // TODO: check whether we can send a file nwc_gameinfo_t nwgi; // init the rest of the packet SOCKET s = packet->get_sender(); loadsave_t fd; if( fd.wr_open( "serverinfo.sve", loadsave_t::xml_bzip2, 0, "info", SERVER_SAVEGAME_VER_NR ) == loadsave_t::FILE_STATUS_OK ) { gameinfo_t gi(welt); gi.rdwr( &fd ); fd.close(); // get gameinfo size FILE *fh = dr_fopen( "serverinfo.sve", "rb" ); fseek( fh, 0, SEEK_END ); nwgi.len = ftell( fh ); rewind( fh ); nwgi.rdwr(); if ( nwgi.send( s ) ) { // send gameinfo while( !feof(fh) ) { char buffer[1024]; int bytes_read = (int)fread( buffer, 1, sizeof(buffer), fh ); uint16 dummy; if( !network_send_data(s,buffer,bytes_read,dummy,250)) { dbg->warning( "nwc_gameinfo_t::execute", "Client closed connection during transfer" ); break; } } } else { dbg->warning( "nwc_gameinfo_t::execute", "send of NWC_GAMEINFO failed" ); } fclose( fh ); dr_remove("serverinfo.sve"); } socket_list_t::remove_client( s ); } else { len = 0; } return true; } void nwc_nick_t::rdwr() { network_command_t::rdwr(); packet->rdwr_str(nickname); if (packet->is_loading() && env_t::server && id == NWC_NICK) { const SOCKET sock = packet->get_sender(); const socket_info_t &client = socket_list_t::get_client(socket_list_t::get_client_id(sock)); if (client.state != socket_info_t::playing) { packet->failed(); } } } static bool nick_already_taken(const plainstring& nick,uint32 client_id) { // check for same nick for (uint32 i = 0; i < socket_list_t::get_count(); i++) { socket_info_t& info = socket_list_t::get_client(i); if ((info.state == socket_info_t::playing || i == 0) && i != client_id && nick == info.nickname.c_str()) { return true; } } return false; } /** * if server: checks whether nickname is taken and generates default nick */ bool nwc_nick_t::execute(karte_t *welt) { if(env_t::server) { uint32 client_id = socket_list_t::get_client_id(packet->get_sender()); if(nickname==NULL || nickname=="Admin" || nick_already_taken(nickname, client_id)) { if (nickname && id == NWC_NICK) { // Player tried to change nickname, but it is already taken nwc_nick_t::server_tools(welt, client_id, NICK_TAKEN, nickname); return true; } else { goto generate_default_nick; } } if (id == NWC_NICK) { // do not call this tool if called by nwc_join_t::execute nwc_nick_t::server_tools(welt, client_id, CHANGE_NICK, nickname); } return true; generate_default_nick: // nick exists already // generate default nick from city names const vector_tpl& city_names = translator::get_city_name_list(); do { nickname = city_names[sim_async_rand(city_names.get_count())]; } while (nick_already_taken(nickname, client_id)); return true; } else { env_t::nickname = nickname!=NULL ? nickname.c_str() : "(null)"; } return true; } void nwc_nick_t::server_tools(karte_t *welt, uint32 client_id, uint8 what, const char* nick) { if (!socket_list_t::is_valid_client_id(client_id)) { return; } socket_info_t &info = socket_list_t::get_client(client_id); cbuffer_t buf; buf.printf("%d,", chat_message_t::DO_NOT_SAVE_MSG); switch(what) { case WELCOME: { // welcome message buf.printf(translator::translate("Welcome, %s!", welt->get_settings().get_name_language_id()), info.nickname.c_str()); // Log chat message - please don't change order of fields CSV_t csv; csv.add_field( "connect" ); csv.add_field( client_id ); csv.add_field( info.address.get_str() ); csv.add_field( info.nickname.c_str() ); DBG_MESSAGE( "__ChatLog__", "%s", csv.get_str() ); break; } case CHANGE_NICK: { if (nick==NULL || info.nickname == nick) { return; } // change nickname buf.printf(translator::translate("%s now known as %s.", welt->get_settings().get_name_language_id()), info.nickname.c_str(), nick); welt->get_chat_message()->rename_client(info.nickname, nick); // record nickname change for(uint8 i=0; i 0) { // send new nickname back to client nwc_nick_t nwc(nick); nwc.send(info.socket); } else { // human at server env_t::nickname = nick; } break; } case NICK_TAKEN: { if (client_id > 0) { // send old nickname back to client nwc_nick_t nwc(info.nickname); nwc.send(info.socket); } else { // human at server env_t::nickname = info.nickname; } return; } case FAREWELL: { buf.printf(translator::translate("%s has left.", welt->get_settings().get_name_language_id()), info.nickname.c_str()); // Log chat message - please don't change order of fields CSV_t csv; csv.add_field( "disconnect" ); csv.add_field( client_id ); csv.add_field( info.address.get_str() ); csv.add_field( info.nickname.c_str() ); DBG_MESSAGE( "__ChatLog__", "%s", csv.get_str() ); break; } default: return; } tool_t *tmp_tool = create_tool( TOOL_ADD_MESSAGE | GENERAL_TOOL ); tmp_tool->set_default_param( buf ); // queue tool for network nwc_tool_t *nwc = new nwc_tool_t(NULL, tmp_tool, koord3d::invalid, 0, welt->get_map_counter(), false); network_send_server(nwc); // since init always returns false, it is safe to delete immediately delete tmp_tool; // now tell all clients the new nicks nwc_chat_t* nwchat = new nwc_chat_t(socket_list_t::get_all_nicks(), PLAYER_UNOWNED, -2, NULL, NULL, koord::invalid); network_send_server(nwchat); } void nwc_chat_t::rdwr() { network_command_t::rdwr(); packet->rdwr_str( message ); packet->rdwr_byte( player_nr ); packet->rdwr_str( clientname ); packet->rdwr_str( destination ); packet->rdwr_byte( channel_nr ); packet->rdwr_short(pos.x); packet->rdwr_short(pos.y); if (packet->is_loading() && env_t::server) { const SOCKET sock = packet->get_sender(); const socket_info_t &client = socket_list_t::get_client(socket_list_t::get_client_id(sock)); if (client.state != socket_info_t::playing) { packet->failed(); } } DBG_MESSAGE("nwc_chat_t::rdwr", "rdwr message=%s plnr=%d clientname=%s destination=%s", message.c_str(), player_nr, clientname.c_str(), destination.c_str()); } void nwc_chat_t::add_message(karte_t* welt) const { cbuffer_t buf; // Output which will be printed to chat window FLAGGED_PIXVAL color = player_nr < PLAYER_UNOWNED ? PLAYER_FLAG | player_nr : gfx->palette_lookup(COL_WHITE); uint16 flag = message_t::chat; if ( destination == NULL ) { if ( player_nr < PLAYER_UNOWNED && welt->get_player(player_nr)) { buf.printf( "%s <%s>: %s", clientname.c_str(), welt->get_player( player_nr )->get_name(), message.c_str() ); } else { buf.printf( "%s: %s", clientname.c_str(), message.c_str() ); } } else { // Whisper, do not store message in savegame flag |= message_t::PLAYER_MSG; if ( player_nr < PLAYER_UNOWNED && welt->get_player(player_nr)) { buf.printf( "%s <%s> --> %s: %s", clientname.c_str(), welt->get_player( player_nr )->get_name(), destination.c_str(), message.c_str() ); } else { buf.printf( "%s --> %s: %s", clientname.c_str(), destination.c_str(), message.c_str() ); } } welt->get_message()->add_message( buf.get_str(), koord3d::invalid, flag, color, IMG_EMPTY ); } bool nwc_chat_t::execute (karte_t* welt) { if ( message == NULL ) { dbg->warning("nwc_chat_t::execute", "null message"); return true; } // Relay message to all listening clients if ( env_t::server ) { uint32 client_id = socket_list_t::get_client_id( packet->get_sender() ); DBG_MESSAGE("nwc_chat_t::execute", "server, client id: %d", client_id); // Clients can only send messages as companies they have unlocked if ( player_nr < PLAYER_UNOWNED && !socket_list_t::get_client( client_id ).is_player_unlocked( player_nr ) ) { dbg->warning("nwc_chat_t::execute", "attempt to send message as locked company by client %d, redirecting to PLAYER_UNOWNED", client_id); player_nr = PLAYER_UNOWNED; } // Otherwise forward message as appropriate socket_info_t &info = socket_list_t::get_client( client_id ); nwc_chat_t* nwchat = new nwc_chat_t( message, player_nr, channel_nr, info.nickname.c_str(), destination, pos ); if ( destination == NULL || destination=="") { // Do not send messages to ourself (server) network_send_all( nwchat, true ); // Act on message (for display of messages on server, and to keep record of messages for new clients joining) welt->get_chat_message()->add_chat_message(message.c_str(), channel_nr, player_nr, info.nickname, destination, pos); // Log chat message - please don't change order of fields CSV_t csv; csv.add_field( "chat" ); csv.add_field( client_id ); csv.add_field( info.address.get_str() ); csv.add_field( info.nickname.c_str() ); csv.add_field( player_nr ); csv.add_field( player_nr < PLAYER_UNOWNED ? welt->get_player( player_nr )->get_name() : "" ); csv.add_field( message.c_str() ); DBG_MESSAGE( "__ChatLog__", "%s", csv.get_str() ); } else { // Send to a specific client // Look up a client with a matching name, if none matches // send a message back saying that client doesn't exist // is it for us => no need to send further away if (destination == env_t::nickname.c_str()) { welt->get_chat_message()->add_chat_message(message.c_str(), channel_nr, player_nr, info.nickname, destination, pos); } // Check if destination nick exists for ( uint32 i = 0; i < socket_list_t::get_count(); i++ ) { socket_info_t& dest_info = socket_list_t::get_client(i); if ( (dest_info.state == socket_info_t::playing || i == 0 ) && i != client_id && destination == dest_info.nickname.c_str() ) { nwchat->send( dest_info.socket ); } } // TODO also send a copy back to sending client for logging delete nwchat; // Log chat message - please don't change order of fields CSV_t csv; csv.add_field( "private" ); csv.add_field( client_id ); csv.add_field( info.address.get_str() ); csv.add_field( info.nickname.c_str() ); csv.add_field( player_nr ); csv.add_field( player_nr < PLAYER_UNOWNED ? welt->get_player( player_nr )->get_name() : "" ); csv.add_field( destination.c_str() ); csv.add_field( message.c_str() ); DBG_MESSAGE( "__ChatLog__", "%s", csv.get_str() ); } } else { welt->get_chat_message()->add_chat_message(message.c_str(), channel_nr, player_nr, clientname, destination, pos); } return true; } SOCKET nwc_join_t::pending_join_client = INVALID_SOCKET; void nwc_join_t::rdwr() { nwc_nick_t::rdwr(); packet->rdwr_long(client_id); packet->rdwr_byte(answer); } bool nwc_join_t::execute(karte_t *welt) { if(env_t::server) { DBG_MESSAGE("nwc_join_t::execute", ""); // TODO: check whether we can send a file nwc_join_t nwj; nwj.client_id = socket_list_t::get_client_id(packet->get_sender()); //save nickname if (socket_list_t::is_valid_client_id(nwj.client_id)) { // check nickname nwc_nick_t::execute(welt); nwj.nickname = nickname; socket_list_t::get_client(nwj.client_id).nickname = nickname; } // no other joining process active? nwj.answer = socket_list_t::get_client(nwj.client_id).is_active() && pending_join_client == INVALID_SOCKET ? 1 : 0; DBG_MESSAGE( "nwc_join_t::execute", "client_id=%i active=%i pending_join_client=%i active=%d", socket_list_t::get_client_id(packet->get_sender()), socket_list_t::get_client(nwj.client_id).is_active(), pending_join_client, nwj.answer ); nwj.rdwr(); if( nwj.send( packet->get_sender() ) ) { if( nwj.answer==1 ) { // now send sync command const uint32 new_map_counter = welt->generate_new_map_counter(); // since network_send_all() does not include non-playing clients -> send sync command separately to the joining client nwc_sync_t nw_sync(welt->get_sync_steps() + 1, welt->get_map_counter(), nwj.client_id, new_map_counter); nw_sync.rdwr(); if( nw_sync.send( packet->get_sender() ) ) { // now send sync command to the server and the remaining clients nwc_sync_t *nws = new nwc_sync_t(welt->get_sync_steps() + 1, welt->get_map_counter(), nwj.client_id, new_map_counter); network_send_all(nws, false); pending_join_client = packet->get_sender(); DBG_MESSAGE( "nwc_join_t::execute", "pending_join_client now %i", pending_join_client); // unpause world if (welt->is_paused()) { welt->set_pause(false); } } else { dbg->warning("nwc_join_t::execute", "send of NWC_SYNC to the joining client failed"); } } } else { dbg->warning( "nwc_join_t::execute", "send of NWC_JOIN failed" ); } } return true; } /** * saves the history of map counters * the current one is at index zero, the older ones behind */ #define MAX_MAP_COUNTERS (7) vector_tpl nwc_ready_t::all_map_counters(MAX_MAP_COUNTERS); void nwc_ready_t::append_map_counter(uint32 map_counter_) { if (all_map_counters.get_count() == MAX_MAP_COUNTERS) { all_map_counters.pop_back(); } all_map_counters.insert_at(0, map_counter_); } void nwc_ready_t::clear_map_counters() { all_map_counters.clear(); } bool nwc_ready_t::execute(karte_t *welt) { if( env_t::server ) { // compare checklist if( welt->is_checklist_available(sync_step) && checklist!=welt->get_checklist_at(sync_step) ) { // client has gone out of sync socket_list_t::remove_client( get_sender() ); cbuffer_t buf; welt->get_checklist_at(sync_step).print(buf, "server"); checklist.print(buf, "client"); dbg->warning("nwc_ready_t::execute", "disconnect client due to checklist mismatch : sync_step=%u %s", sync_step, buf.get_str()); return true; } // check the validity of the map counter for(uint32 const i : all_map_counters) { if (i == map_counter) { // unpause the sender by sending nwc_ready_t back nwc_ready_t nwc(sync_step, map_counter, checklist); if( !nwc.send( get_sender()) ) { dbg->warning("nwc_ready_t::execute", "send of NWC_READY failed"); } return true; } } // no matching map counter -> disconnect client socket_list_t::remove_client( get_sender() ); dbg->warning("nwc_ready_t::execute", "disconnect client id=%u due to invalid map counter", our_client_id); } else { dbg->warning("nwc_ready_t::execute", "set sync_step=%d where map_counter=%d", sync_step, map_counter); if( map_counter==welt->get_map_counter() ) { welt->network_game_set_pause(false, sync_step); welt->set_checklist_at(sync_step, checklist); } else { welt->network_disconnect(); dbg->warning("nwc_ready_t::execute", "disconnecting due to map counter mismatch"); } } return true; } void nwc_ready_t::rdwr() { network_command_t::rdwr(); packet->rdwr_long(sync_step); packet->rdwr_long(map_counter); checklist.rdwr(packet); } void nwc_game_t::rdwr() { network_command_t::rdwr(); packet->rdwr_long(len); if (packet->is_loading() && env_t::server) { packet->failed(); } } bool nwc_auth_player_t::execute(karte_t *welt) { DBG_MESSAGE("nwc_auth_player_t::execute","plnr = %d unlock = %d our_client_id = %d", player_nr, player_unlocked, our_client_id); if( env_t::server && !(our_client_id==0 && player_nr==255)) { // sent to server, and not sent to player playing on server if (socket_list_t::is_valid_client_id(our_client_id)) { // player activated for this client? or admin connection via nettool? socket_info_t &info = socket_list_t::get_client(our_client_id); // `player_nr` is wire-controlled; the slot may be NULL. player_t *player = welt->get_player(player_nr); if (info.is_player_unlocked(player_nr) || info.state == socket_info_t::admin) { DBG_MESSAGE("nwc_auth_player_t::execute","set pwd for plnr = %d", player_nr); // change password if (player && player->access_password_hash() != hash) { player->access_password_hash() = hash; // unlock all clients if new password is empty // otherwise lock all socket_list_t::unlock_player_all(player_nr, hash.empty(), our_client_id); } } else if (player_nr < PLAYER_UNOWNED && player) { // players with public service player access always pass password checks if( info.is_player_unlocked(1) ) { info.unlock_player(player_nr); } // check password else if (player->access_password_hash() == hash) { DBG_MESSAGE("nwc_auth_player_t::execute","unlock plnr = %d at our_client_id = %d", player_nr, our_client_id); info.unlock_player(player_nr); } } // report back to client who sent the command if (our_client_id == 0) { // unlock player on the server and clear unlock_pending flag if (player) { player->unlock(info.is_player_unlocked(player_nr), false); } } else { // send unlock-info to player on the client (to clear unlock_pending flag) nwc_auth_player_t nwc; nwc.player_unlocked = info.player_unlocked; nwc.send( get_sender()); } } } else { for(uint8 i=0; iget_player(i)) { player->unlock( player_unlocked & (1<update_data(); } return true; } void nwc_auth_player_t::init_player_lock_server(karte_t *welt) { uint16 player_unlocked = 0; for(uint8 i=0; iget_player(i); if (player == NULL || player->access_password_hash() == welt->get_player_password_hash(i) ) { player_unlocked |= 1<unlock( player_unlocked & (1<sync_step = sync_step; this->map_counter = map_counter; } void network_world_command_t::rdwr() { network_command_t::rdwr(); packet->rdwr_long(sync_step); packet->rdwr_long(map_counter); } bool network_world_command_t::execute(karte_t *welt) { DBG_MESSAGE("network_world_command_t::execute","do_command %d at sync_step %d world now at %d", get_id(), get_sync_step(), welt->get_sync_steps()); // want to execute something in the past? if (get_sync_step() < welt->get_sync_steps()) { if (!ignore_old_events()) { dbg->warning("network_world_command_t::execute", "wanted to execute(%d) in the past", get_id()); welt->network_disconnect(); } return true; // to delete cmd } if (map_counter != welt->get_map_counter()) { // command from another world // could happen if we are behind and still have to execute the next sync command dbg->warning("network_world_command_t::execute", "wanted to execute(%d) from another world (mpc=%d)", get_id(), map_counter); if (env_t::server) { return true; // to delete cmd } // map_counter has to be checked before calling do_command() } welt->command_queue_append(this); return false; } void nwc_sync_t::rdwr() { network_world_command_t::rdwr(); packet->rdwr_long(client_id); packet->rdwr_long(new_map_counter); if (packet->is_loading() && env_t::server) { packet->failed(); } } // save, load, pause, if server send game void nwc_sync_t::do_command(karte_t *welt) { dbg->warning("nwc_sync_t::do_command", "sync_steps %d", get_sync_step()); // save screen coordinates & offsets const koord ij = welt->get_viewport()->get_world_position(); const sint16 xoff = welt->get_viewport()->get_x_off(); const sint16 yoff = welt->get_viewport()->get_y_off(); // save active player const uint8 active_player = welt->get_active_player_nr(); // save lock state uint16 player_unlocked = 0; for(uint8 i=0; iget_player(i)) { if (!player->is_locked()) { player_unlocked |= 1<save( fn, true, SERVER_SAVEGAME_VER_NR, false ); uint32 old_sync_steps = welt->get_sync_steps(); welt->load( fn ); welt->type_of_generation = karte_t::CLIENT_WORLD; env_t::restore_UI = old_restore_UI; // pause clients, restore steps welt->network_game_set_pause( true, old_sync_steps); // apply new map counter welt->set_map_counter(new_map_counter); // tell server we are ready network_command_t *nwc = new nwc_ready_t( old_sync_steps, welt->get_map_counter(), welt->get_checklist_at(old_sync_steps) ); network_send_server(nwc); } else { // we are on the server char fn[256]; uint16 unlocked_players = 0; if (!welt->has_current_network_save) { // no an ready savegame yet // first save password hashes sprintf( fn, "server%d-pwdhash.sve", env_t::server ); loadsave_t file; if( file.wr_open(fn, loadsave_t::zipped, 1, "hashes", SAVEGAME_VER_NR ) == loadsave_t::FILE_STATUS_OK ) { welt->rdwr_player_password_hashes( &file ); file.close(); } // remove passwords before transfer on the server and set default client mask // they will be restored below for( int i=0; iget_player(i); if( player==NULL || player->access_password_hash().empty() ) { unlocked_players |= (1<access_password_hash().clear(); } } // save game sprintf( fn, "server%d-network.sve", env_t::server ); bool old_restore_UI = env_t::restore_UI; env_t::restore_UI = true; welt->save( fn, false, SERVER_SAVEGAME_VER_NR, false ); env_t::restore_UI = old_restore_UI; } else { sprintf(fn, "server%d-network.sve", env_t::server); for (int i = 0; i < PLAYER_UNOWNED; i++) { player_t* player = welt->get_player(i); if (player == NULL || player->access_password_hash().empty()) { unlocked_players |= (1 << i); } } } // ok, now sending game // this sends nwc_game_t const char *err = network_send_file( socket_list_t::get_socket(client_id), fn ); if (err) { dbg->warning("nwc_sync_t::do_command","send game failed with: %s", err); } uint32 old_sync_steps = welt->get_sync_steps(); welt->load( fn ); welt->type_of_generation = karte_t::LOADED_WORLD; // restore steps welt->network_game_set_pause( false, old_sync_steps); // apply new map counter welt->set_map_counter(new_map_counter); // unpause the client that received the game // we do not want to wait for him (maybe loading failed due to pakset-errors) SOCKET sock = socket_list_t::get_socket(client_id); if( sock != INVALID_SOCKET ) { nwc_ready_t nwc( old_sync_steps, welt->get_map_counter(), welt->get_checklist_at(old_sync_steps) ); if (nwc.send(sock)) { socket_list_t::change_state(client_id, socket_info_t::playing); if (socket_list_t::is_valid_client_id(client_id)) { socket_list_t::get_client(client_id).player_unlocked = unlocked_players; // send information about locked state nwc_auth_player_t nwc; nwc.player_unlocked = unlocked_players; nwc.send(sock); // welcome message nwc_nick_t::server_tools(welt, client_id, nwc_nick_t::WELCOME, NULL); } else { dbg->warning("nwc_sync_t::do_command(karte_t *welt)", "client_id %d became invalid during sync!", client_id); } } else { dbg->warning( "nwc_sync_t::do_command", "send of NWC_READY failed" ); } } nwc_join_t::pending_join_client = INVALID_SOCKET; } // restore screen coordinates & offsets welt->get_viewport()->change_world_position(ij, xoff, yoff); welt->switch_active_player(active_player,true); // restore lock state for(uint8 i=0; iget_player(i)) { player->unlock(player_unlocked & (1<rdwr_long(server_sync_step); if (packet->is_loading() && env_t::server) { // server does not receive nwc_check_t-commands packet->failed(); } } void network_broadcast_world_command_t::rdwr() { network_world_command_t::rdwr(); packet->rdwr_bool(exec); if (packet->is_loading() && env_t::server && exec) { // server does not receive exec-commands packet->failed(); } } bool network_broadcast_world_command_t::execute(karte_t *welt) { if (exec) { // append to command queue return network_world_command_t::execute(welt); } else if (env_t::server) { // check map_counter if (map_counter != welt->get_map_counter()) { // command from another world dbg->warning("network_broadcast_world_command_t::execute", "wanted to execute(%d) from another world", get_id()); return true; } // clone network_broadcast_world_command_t *nwc = clone(welt); if (nwc == NULL) { return true; } // next call to execute will put it in command queue nwc->exec = true; nwc->sync_step = welt->get_sync_steps() + 1; // broadcast network_send_all(nwc, false); // return true to delete this command only if clone() returned something new return true; } else { dbg->warning("network_broadcast_world_command_t::execute", "should not reach here!"); } return true; } nwc_chg_player_t::~nwc_chg_player_t() { delete pending_company_creator; } void nwc_chg_player_t::rdwr() { network_broadcast_world_command_t::rdwr(); packet->rdwr_byte(cmd); packet->rdwr_byte(player_nr); packet->rdwr_short(param); packet->rdwr_bool(scripted_call); } network_broadcast_world_command_t* nwc_chg_player_t::clone(karte_t *welt) { if (!socket_list_t::is_valid_client_id(our_client_id)) { return NULL; } socket_info_t const& info = socket_list_t::get_client(our_client_id); // scripts only run on server if (socket_list_t::get_client_id(packet->get_sender()) != 0) { // not sent by server, clear flag scripted_call = false; // only server can start scripted AI for now if (cmd == karte_t::new_player && param == player_t::AI_SCRIPTED) { return NULL; } } if (!welt->change_player_tool(cmd, player_nr, param, info.is_player_unlocked(1) || scripted_call, false)) { return NULL; } // now create the new command nwc_chg_player_t* nwc = new nwc_chg_player_t(get_sync_step(), get_map_counter(), cmd, player_nr, param); // .. and store company_creator if necessary if (cmd == karte_t::new_player) { nwc->pending_company_creator = new connection_info_t(info); dbg->warning("nwc_chg_player_t::clone", "pending_company_creator for %d is set to %s/%s", player_nr, info.address.get_str(), info.nickname.c_str()); } return nwc; } connection_info_t* nwc_chg_player_t::company_creator[PLAYER_UNOWNED] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; slist_tpl nwc_chg_player_t::company_active_clients[PLAYER_UNOWNED]; void nwc_chg_player_t::company_removed(uint8 player_nr) { // delete history delete company_creator[player_nr]; company_creator[player_nr] = NULL; company_active_clients[player_nr].clear(); } void nwc_chg_player_t::do_command(karte_t *welt) { welt->change_player_tool(cmd, player_nr, param, true, true); // store IP of client who created this company if (env_t::server && cmd == karte_t::new_player && player_nr < lengthof(company_creator)) { company_creator[player_nr] = pending_company_creator; if (pending_company_creator) { dbg->warning("nwc_chg_player_t::do_command", "company_creator for %d is set to %s/%s", player_nr, pending_company_creator->address.get_str(), pending_company_creator->nickname.c_str()); } pending_company_creator = NULL; // to prevent deletion in ~nwc_chg_player_t } // reset locked state if (cmd == karte_t::new_player || cmd == karte_t::delete_player) { socket_list_t::unlock_player_all(player_nr, true); } // update the window ki_kontroll_t* playerwin = (ki_kontroll_t*)win_get_magic(magic_ki_kontroll_t); if (playerwin) { playerwin->update_data(); } } nwc_tool_t::nwc_tool_t() : network_broadcast_world_command_t(NWC_TOOL, 0, 0), init(false), custom_data(custom_data_buf, lengthof(custom_data_buf), true) { tool = NULL; } nwc_tool_t::nwc_tool_t(player_t *player, tool_t *tool_, koord3d pos_, uint32 sync_steps, uint32 map_counter, bool init_) : network_broadcast_world_command_t(NWC_TOOL, sync_steps, map_counter), custom_data(custom_data_buf, lengthof(custom_data_buf), true) { pos = pos_; player_nr = player ? player->get_player_nr() : -1; tool_id = tool_->get_id(); wt = tool_->get_waytype(); default_param = tool_->get_default_param(player); init = init_; tool_client_id = 0; flags = tool_->flags; karte_ptr_t welt; last_sync_step = welt->get_last_checklist_sync_step(); last_checklist = welt->get_last_checklist(); callback_id = tool_->callback_id; // write custom data of tool_ to our internal buffer if (player) { tool_->rdwr_custom_data(&custom_data); } tool = NULL; } nwc_tool_t::nwc_tool_t(const nwc_tool_t &nwt) : network_broadcast_world_command_t(NWC_TOOL, nwt.get_sync_step(), nwt.get_map_counter()), custom_data(custom_data_buf, lengthof(custom_data_buf), true) { pos = nwt.pos; player_nr = nwt.player_nr; tool_id = nwt.tool_id; wt = nwt.wt; default_param = nwt.default_param; init = nwt.init; tool_client_id = nwt.our_client_id; flags = nwt.flags; callback_id = nwt.callback_id; // copy custom data of tool to our internal buffer custom_data.append(nwt.custom_data); tool = NULL; } nwc_tool_t::~nwc_tool_t() { delete tool; } void nwc_tool_t::rdwr() { network_broadcast_world_command_t::rdwr(); packet->rdwr_long(last_sync_step); last_checklist.rdwr(packet); packet->rdwr_byte(player_nr); sint16 posx = pos.x; packet->rdwr_short(posx); pos.x = posx; sint16 posy = pos.y; packet->rdwr_short(posy); pos.y = posy; sint8 posz = pos.z; packet->rdwr_byte(posz); pos.z = posz; packet->rdwr_short(tool_id); packet->rdwr_short(wt); packet->rdwr_str(default_param); packet->rdwr_bool(init); packet->rdwr_long(tool_client_id); packet->rdwr_byte(flags); packet->rdwr_long(callback_id); // copy custom data of tool to/from packet if (packet->is_saving()) { // write to packet packet->append(custom_data); } else { // read from packet custom_data.append_tail(*packet); } DBG_MESSAGE("nwc_tool_t::rdwr", "rdwr id=%d client=%d plnr=%d pos=%s tool_id=%s defpar=%s init=%d flags=%d", id, tool_client_id, player_nr, pos.get_str(), tool_t::id_to_string(tool_id), default_param.c_str(), init, flags); } void nwc_tool_t::init_tool() { delete tool; // create new memory_rw_t that is in reading mode to read tool data memory_rw_t new_custom_data(custom_data_buf, custom_data.get_current_index(), false); if ( (tool = create_tool(tool_id)) ) { tool->set_default_param(default_param); tool->rdwr_custom_data(&new_custom_data); } } network_broadcast_world_command_t* nwc_tool_t::clone(karte_t *welt) { init_tool(); if (tool == NULL) { // invalid id return NULL; } // do not open dialog windows across network if ( init ? tool->is_init_keeps_game_state() : tool->is_work_keeps_game_state() ){ // no reason to send request over network return NULL; } // scenario scripts only run on server if (socket_list_t::get_client_id(packet->get_sender()) != 0) { // not sent by server, clear flag flags &= ~tool_t::WFL_NO_CHK; } // scripted calls do not need authentication check bool const needs_check = (flags & tool_t::WFL_NO_CHK) == 0; // check authentication and scenario rules if (needs_check) { scenario_t *scen = welt->get_scenario(); // check for map editor tools - they need unlocked public player // scenario should check itself if (!scen->is_scripted()) { switch( tool_id ) { case TOOL_CHANGE_CITY_SIZE | GENERAL_TOOL: case TOOL_BUILD_HOUSE | GENERAL_TOOL: case TOOL_BUILD_LAND_CHAIN | GENERAL_TOOL: case TOOL_CITY_CHAIN | GENERAL_TOOL: case TOOL_BUILD_FACTORY | GENERAL_TOOL: case TOOL_LINK_FACTORY | GENERAL_TOOL: case TOOL_ADD_CITYCAR | GENERAL_TOOL: case TOOL_INCREASE_INDUSTRY | SIMPLE_TOOL: case TOOL_STEP_YEAR | SIMPLE_TOOL: case TOOL_FILL_TREES | SIMPLE_TOOL: player_nr = 1; default: ; } } // check whether player is authorized do this if (!socket_list_t::is_valid_client_id(our_client_id)) { return NULL; } socket_info_t const& info = socket_list_t::get_client(our_client_id); if ( player_nr < PLAYER_UNOWNED && !info.is_player_unlocked(player_nr) ) { if (tool_id == (TOOL_ADD_MESSAGE | GENERAL_TOOL)) { player_nr = PLAYER_UNOWNED; } else { dbg->warning("nwc_tool_t::clone", "client %d not allowed to act as player %d", our_client_id, player_nr); return NULL; // indicate failure } } // log that this client acted as this player if ( player_nr < PLAYER_UNOWNED) { nwc_chg_player_t::company_active_clients[player_nr].append_unique( connection_info_t(info) ); } // do scenario checks here, send error message back if ( scen->is_scripted() ) { if (!scen->is_tool_allowed(welt->get_player(player_nr), tool_id, wt, default_param)) { dbg->warning("nwc_tool_t::clone", "tool_id=%s wt=%d param=%p, tool not allowed", tool_t::id_to_string(tool_id), wt, default_param.c_str()); // TODO return error message ? return NULL; } if (!init) { const char *err = scen->is_work_allowed_here(welt->get_player(player_nr), tool_id, wt, default_param, pos); if (err == NULL) { if (two_click_tool_t *two_ctool = dynamic_cast(tool)) { if (!two_ctool->is_first_click()) { err = scen->is_work_allowed_here(welt->get_player(player_nr), tool_id, wt, default_param, two_ctool->get_start_pos()); } } } if (err) { nwc_tool_t *nwt = new nwc_tool_t(*this); nwt->tool_id = TOOL_ERROR_MESSAGE | GENERAL_TOOL; nwt->default_param = err; nwt->last_sync_step = welt->get_last_checklist_sync_step(); nwt->last_checklist = welt->get_last_checklist(); dbg->warning("nwc_tool_t::clone", "send sync_steps=%d tool_id=%s error=%s", nwt->get_sync_step(), tool_t::id_to_string(tool_id), err); return nwt; } } } } // copy data, sets tool_client_id to sender client_id nwc_tool_t *nwt = new nwc_tool_t(*this); nwt->last_sync_step = welt->get_last_checklist_sync_step(); nwt->last_checklist = welt->get_last_checklist(); DBG_MESSAGE("nwc_tool_t::clone", "send sync_steps=%d tool_id=%s %s", nwt->get_sync_step(), tool_t::id_to_string(tool_id), init ? "init" : "work"); return nwt; } bool nwc_tool_t::ignore_old_events() const { // messages are allowed to arrive at any time (return true if message) return tool_id==(GENERAL_TOOL|TOOL_ADD_MESSAGE); } void nwc_tool_t::do_command(karte_t *welt) { if (tool == NULL) { init_tool(); } DBG_MESSAGE("nwc_tool_t::do_command", "steps %d tool_id %d %s", get_sync_step(), tool_id, init ? "init" : "work"); // commands are treated differently if they come from this client or not bool local = tool_client_id == network_get_client_id(); player_t *player = player_nr < PLAYER_UNOWNED ? welt->get_player(player_nr) : NULL; // before calling work initialize new tool assert(tool); bool init_successfull = true; if (!init) { // init command was not sent if tool->is_init_keeps_game_state() returned true tool->flags = 0; // init tool init_successfull = tool->init(player); } // read custom data (again, necessary for two_click_tool_t) { memory_rw_t new_custom_data(custom_data_buf, custom_data.get_current_index(), false); tool->rdwr_custom_data(&new_custom_data); } // set flags correctly if (local) { tool->flags = flags | tool_t::WFL_LOCAL; } else { tool->flags = flags & ~tool_t::WFL_LOCAL; } DBG_MESSAGE("nwc_tool_t::do_command","id=%d init=%d defpar=%s flag=%d",tool_id&0xFFF,init,(const char*)default_param,tool->flags); const char* err = NULL; bool res = false; // call INIT if( init ) { // we should be here only if tool->init() returns false // no need to change active tool of world res = tool->init(player); } // call WORK else if (init_successfull) { // remove preview tiles of active tool two_click_tool_t *active_tool = dynamic_cast(welt->get_tool(welt->get_active_player_nr())); if(active_tool && active_tool->remove_preview_necessary()) { active_tool->cleanup(); } err = tool->work( player, pos ); // only local players get the callback if (local && callback_id == 0 && player) { player->tell_tool_result(tool, pos, err); } if (err) { dbg->warning("nwc_tool_t::do_command","Tool %s failed with '%s'", tool->get_name(), err); } tool->exit(player); } else { err = "Init was not succesfull, returned false."; } // callback to script here if (local && callback_id != 0) { if (init) { suspended_scripts_t::tell_return_value(callback_id, res); } else { suspended_scripts_t::tell_return_value(callback_id, err); } } } extern address_list_t blacklist; bool nwc_service_t::execute(karte_t *welt) { if (flag>=SRVC_MAX || !env_t::server) { // wrong flag, no server return true; // to delete } // check whether admin connection is established const uint32 sender_id = socket_list_t::get_client_id(packet->get_sender()); const bool admin_logged_in = socket_list_t::get_client(sender_id).state == socket_info_t::admin; if (!admin_logged_in && (flag != SRVC_LOGIN_ADMIN && flag != SRVC_ANNOUNCE_SERVER) ) { return true; // to delete } switch(flag) { case SRVC_LOGIN_ADMIN: { nwc_service_t nws; nws.flag = SRVC_LOGIN_ADMIN; // check password bool ok = !env_t::server_admin_pw.empty() && env_t::server_admin_pw.compare(text)==0; if (ok) { socket_list_t::get_client(sender_id).state = socket_info_t::admin; } nws.number = ok ? sender_id : 0; nws.send(packet->get_sender()); break; } case SRVC_ANNOUNCE_SERVER: // Startup announce, to force full details resend welt->announce_server( karte_t::SERVER_ANNOUNCE_HELLO ); break; case SRVC_GET_CLIENT_LIST: { nwc_service_t nws; nws.flag = SRVC_GET_CLIENT_LIST; // send, socket list will be written in rdwr nws.send(packet->get_sender()); break; } case SRVC_KICK_CLIENT: case SRVC_BAN_CLIENT: { bool ban = flag == SRVC_BAN_CLIENT; uint32 client_id = number; net_address_t address; SOCKET kick = socket_list_t::get_socket(client_id); if (kick!=INVALID_SOCKET) { socket_info_t &info = socket_list_t::get_client(client_id); address.ip = info.address.ip; if (info.state == socket_info_t::playing) { socket_list_t::remove_client(info.socket); } } if (ban && address.ip) { fd_set fd; FD_ZERO(&fd); socket_list_t::fill_set(&fd); socket_list_t::client_socket_iterator_t iter(&fd); while(iter.next()) { SOCKET sock = iter.get_current(); socket_info_t& info = socket_list_t::get_client(socket_list_t::get_client_id(sock)); if (address.matches(info.address)) { socket_list_t::remove_client(sock); } } blacklist.append(address); } break; } case SRVC_GET_BLACK_LIST: { nwc_service_t nws; nws.flag = SRVC_GET_BLACK_LIST; // send, blacklist will be written in rdwr nws.send(packet->get_sender()); break; } case SRVC_BAN_IP: case SRVC_UNBAN_IP: { net_address_t address(text); if (address.ip) { if (flag==SRVC_BAN_IP) { blacklist.append(address); } else { blacklist.remove(address); } } break; } case SRVC_ADMIN_MSG: if (text) { // Send message to all clients as Public Service // with reserved username Admin nwc_chat_t* nwchat = new nwc_chat_t( text, 1, -1, "Admin" ); network_send_all( nwchat, false ); // Log chat message - please don't change order of fields CSV_t csv; csv.add_field( "adminmsg" ); csv.add_field( text ); dbg->warning( "__ChatLog__", "%s", csv.get_str() ); } break; case SRVC_SHUTDOWN: { welt->stop( true ); break; } case SRVC_FORCE_SYNC: { const uint32 new_map_counter = welt->generate_new_map_counter(); nwc_sync_t *nw_sync = new nwc_sync_t(welt->get_sync_steps() + 1, welt->get_map_counter(), -1, new_map_counter); if (welt->is_paused()) { if (socket_list_t::get_playing_clients() == 0) { // we can save directly without disturbing clients nw_sync->do_command(welt); } delete nw_sync; } else { // send sync command network_send_all(nw_sync, false); } break; } case SRVC_GET_COMPANY_LIST: case SRVC_GET_COMPANY_INFO: { bool detailed = flag == SRVC_GET_COMPANY_INFO && number < PLAYER_UNOWNED; uint8 min_index = detailed ? number : 0; uint8 max_index = detailed ? number+1 : PLAYER_UNOWNED; cbuffer_t buf; for (uint8 i=min_index; iget_player(i)) { buf.printf("Company #%d: %s\n", i, player->get_name()); buf.printf(" Password: %sset\n", player->access_password_hash().empty() ? "NOT " :""); // print creator information if (i < lengthof(nwc_chg_player_t::company_creator)) { if (connection_info_t const* creator = nwc_chg_player_t::company_creator[i]) { buf.printf(" founded by %s at %s\n", creator->nickname.c_str(), creator->address.get_str()); } } // print clients who have this player unlocked for(uint32 j = 0; j < socket_list_t::get_count(); j++) { socket_info_t const& info = socket_list_t::get_client(j); if (info.is_active() && info.is_player_unlocked(i)) { buf.printf(" unlocked for [%d] %s at %s\n", j, info.nickname.c_str(), info.address.get_str()); } } // print clients who played for this company uint32 j=0; for(connection_info_t &iter : nwc_chg_player_t::company_active_clients[i]) { if (!detailed && j > 3 && nwc_chg_player_t::company_active_clients[i].get_count() > 5) { buf.printf(" .. and %d more.\n", nwc_chg_player_t::company_active_clients[i].get_count()-j); break; } buf.printf(" played by %s at %s\n", iter.nickname.c_str(), iter.address.get_str()); j++; } } } nwc_service_t nws; nws.flag = flag; nws.text = strdup(buf); if (text && (strlen(text) > MAX_PACKET_LEN - 256)) { text[MAX_PACKET_LEN - 256] = 0; } nws.send(packet->get_sender()); break; } case SRVC_UNLOCK_COMPANY: { if (number >= PLAYER_UNOWNED) { break; // invalid number } uint8 player_nr = number; // empty password player_t *player = welt->get_player(player_nr); if (player) { player->access_password_hash().clear(); // unlock all clients socket_list_t::unlock_player_all(player_nr, true, packet->get_sender()); // unlock player on the server player->unlock(true, false); } break; } case SRVC_REMOVE_COMPANY: { if (number >= PLAYER_UNOWNED) { break; // invalid number } nwc_chg_player_t *nwc = new nwc_chg_player_t(welt->get_sync_steps(), welt->get_map_counter(), karte_t::delete_player, number); if (nwc->execute(welt)) { delete nwc; } break; } default: ; } return true; // to delete } simutrans-124.5/src/simutrans/network/network_cmd_ingame.h000066400000000000000000000305471520675460500241070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_CMD_INGAME_H #define NETWORK_NETWORK_CMD_INGAME_H #include "network_cmd.h" #include "memory_rw.h" #include "../world/simworld.h" #include "../tpl/slist_tpl.h" #include "../utils/plainstring.h" #include "../dataobj/koord3d.h" class connection_info_t; class packet_t; class player_t; class tool_t; /** * nwc_gameinfo_t * @from-client: client wants map info * server sends nwc_gameinfo_t to sender * @from-server: * @data len of gameinfo * client processes this in network_connect */ class nwc_gameinfo_t : public network_command_t { public: nwc_gameinfo_t() : network_command_t(NWC_GAMEINFO) { len = 0; } bool execute(karte_t *) OVERRIDE; void rdwr() OVERRIDE; uint32 client_id; uint32 len; }; /** * nwc_nick_t * @from-client: client sends new nickname, * server checks if nickname is already taken, * and generates a default one if this is the case * @from-server: server sends the checked nickname back to the client */ class nwc_nick_t : public network_command_t { public: nwc_nick_t(const char* nick=NULL) : network_command_t(NWC_NICK), nickname(nick) { } bool execute(karte_t *) OVERRIDE; void rdwr() OVERRIDE; plainstring nickname; enum { WELCOME, CHANGE_NICK, NICK_TAKEN, FAREWELL }; /** * Server-side nickname related stuff: * what = WELCOME .. new player joined: send welcome message * what = CHANGE_NICK .. change nickname: in socket_list per client, send new nick back to client, tell others as well * what = NICK_TAKEN .. player tried to use a nickname already taken, send old nick instead * what = FAREWELL .. player has left: send message */ static void server_tools(karte_t *welt, uint32 client_id, uint8 what, const char* nick); private: nwc_nick_t(const nwc_nick_t&); nwc_nick_t& operator=(const nwc_nick_t&); }; /** * nwc_chat_t * @from-client: client sends chat message to server * server logs message and sends it to all clients * @from-server: server sends a chat message for display on the client */ class nwc_chat_t : public network_command_t { public: nwc_chat_t (const char* msg = NULL, sint8 pn = -1, sint8 ch= -1, const char* cn = NULL, const char* dn = NULL, koord pos_=koord::invalid) : network_command_t(NWC_CHAT) , message(msg) , player_nr(pn) , channel_nr(ch) , pos(pos_) , clientname(cn) , destination(dn) {} bool execute (karte_t * welt) OVERRIDE; void rdwr () OVERRIDE; void add_message(karte_t*) const; plainstring message; // Message text sint8 player_nr; // Company number message was sent as sint8 channel_nr; // Company channnel (-1 for public) koord pos; plainstring clientname; // Name of client message is from plainstring destination; // Client to send message to (NULL for all) private: nwc_chat_t(const nwc_chat_t&); nwc_chat_t& operator=(const nwc_chat_t&); }; /** * nwc_join_t * @from-client: client wants to join the server * server sends nwc_join_t to sender, nwc_sync_t to all clients * @from-server: * @data answer == 1 (if joining now is ok) * @data client_id * client ignores the following nwc_sync_t, waits for nwc_ready_t */ class nwc_join_t : public nwc_nick_t { public: nwc_join_t(const char* nick=NULL) : nwc_nick_t(nick), client_id(0), answer(0) { id = NWC_JOIN; } bool execute(karte_t *) OVERRIDE; void rdwr() OVERRIDE; uint32 client_id; uint8 answer; /** * this clients is in the process of joining */ static SOCKET pending_join_client; static bool is_pending() { return pending_join_client != INVALID_SOCKET; } private: nwc_join_t(const nwc_join_t&); nwc_join_t& operator=(const nwc_join_t&); }; /** * nwc_ready_t * @from-client: * @data sync_steps at which client will continue * client paused, waits for unpause * @from-server: * data is resent to client * map_counter to identify network_commands * unpause client */ class nwc_ready_t : public network_command_t { public: nwc_ready_t() : network_command_t(NWC_READY), sync_step(0), map_counter(0) { } nwc_ready_t(uint32 sync_step_, uint32 map_counter_, const checklist_t &checklist_) : network_command_t(NWC_READY), sync_step(sync_step_), map_counter(map_counter_), checklist(checklist_) { } bool execute(karte_t *) OVERRIDE; void rdwr() OVERRIDE; uint32 sync_step; uint32 map_counter; checklist_t checklist; static void append_map_counter(uint32 map_counter_); static void clear_map_counters(); private: static vector_tplall_map_counters; }; /** * nwc_game_t * @from-server: * @data len of savegame * client processes this in network_connect */ class nwc_game_t : public network_command_t { public: nwc_game_t(uint32 len_=0) : network_command_t(NWC_GAME), len(len_) {} void rdwr() OVERRIDE; uint32 len; }; /** * commands that have to be executed at a certain sync_step */ class network_world_command_t : public network_command_t { public: network_world_command_t() : network_command_t(), sync_step(0), map_counter(0) {} network_world_command_t(uint16 /*id*/, uint32 /*sync_step*/, uint32 /*map_counter*/); void rdwr() OVERRIDE; // put it to the command queue bool execute(karte_t *) OVERRIDE; // apply it to the world virtual void do_command(karte_t*) {} uint32 get_sync_step() const { return sync_step; } uint32 get_map_counter() const { return map_counter; } // ignore events that lie in the past? // if false: any cmd with sync_step < world->sync_step forces network disconnect virtual bool ignore_old_events() const { return false;} // for sorted data structures bool operator <= (network_world_command_t c) const { return sync_step <= c.sync_step; } static bool cmp(network_world_command_t *nwc1, network_world_command_t *nwc2) { return nwc1->get_sync_step() <= nwc2->get_sync_step(); } protected: uint32 sync_step; // when this has to be executed uint32 map_counter; // cmd comes from world at this stage }; /** * nwc_sync_t * @from-server: * @data client_id this client wants to receive the game * @data new_map_counter new map counter for the new world after game reloading * clients: pause game, save, load, wait for nwc_ready_t command to unpause * server: pause game, save, load, send game to client, send nwc_ready_t command to client */ class nwc_sync_t : public network_world_command_t { public: nwc_sync_t() : network_world_command_t(NWC_SYNC, 0, 0), client_id(0), new_map_counter(0) {} nwc_sync_t(uint32 sync_steps, uint32 map_counter, uint32 send_to_client, uint32 _new_map_counter) : network_world_command_t(NWC_SYNC, sync_steps, map_counter), client_id(send_to_client), new_map_counter(_new_map_counter) { } void rdwr() OVERRIDE; void do_command(karte_t*) OVERRIDE; uint32 get_new_map_counter() const { return new_map_counter; } private: uint32 client_id; // this client shall receive the game uint32 new_map_counter; // map counter to be applied to the new world after game reloading }; /** * nwc_check_t * @from-server: * @data checklist random seed and quickstone next check entries at previous sync_step * clients: check random seed, if check fails disconnect. * the check is done in karte_t::interactive */ class nwc_check_t : public network_world_command_t { public: nwc_check_t() : network_world_command_t(NWC_CHECK, 0, 0), server_sync_step(0) { } nwc_check_t(uint32 sync_steps, uint32 map_counter, const checklist_t &server_checklist_, uint32 server_sync_step_) : network_world_command_t(NWC_CHECK, sync_steps, map_counter), server_checklist(server_checklist_), server_sync_step(server_sync_step_) {} void rdwr() OVERRIDE; void do_command(karte_t*) OVERRIDE { } checklist_t server_checklist; uint32 server_sync_step; // no action required -> can be ignored if too old bool ignore_old_events() const OVERRIDE { return true; } }; /** * commands that need to be executed at a certain syncstep * the command will be cloned at the server and broadcasted to all clients */ class network_broadcast_world_command_t : public network_world_command_t { public: network_broadcast_world_command_t(uint16 id, uint32 sync_step=0, uint32 map_counter=0) : network_world_command_t(id, sync_step, map_counter), exec(false) { } void rdwr() OVERRIDE; bool execute(karte_t *) OVERRIDE; // clones the command to be broadcasted // all validity checks must be done here // it must return a new command // clone() must return NULL to indicate failure virtual network_broadcast_world_command_t* clone(karte_t *) = 0; bool is_from_initiator() const { return !exec; } private: // at server: true if command needs to be put in command queue // it will be cloned and broadcasted otherwise bool exec; }; /** * nwc_chg_player_t * commands that require special authentication checks: toggle freeplay, start AI player * @from-server: * @data cmd command to perform (see karte_t::change_player_tool) * @data player_nr affected player * @data param */ class nwc_chg_player_t : public network_broadcast_world_command_t { public: nwc_chg_player_t() : network_broadcast_world_command_t(NWC_CHG_PLAYER, 0, 0), pending_company_creator(NULL) { } nwc_chg_player_t(uint32 sync_steps, uint32 map_counter, uint8 cmd_=255, uint8 player_nr_=255, uint16 param_=0, bool scripted_call_=false) : network_broadcast_world_command_t(NWC_CHG_PLAYER, sync_steps, map_counter), cmd(cmd_), player_nr(player_nr_), param(param_), scripted_call(scripted_call_), pending_company_creator(NULL) {} ~nwc_chg_player_t(); void rdwr() OVERRIDE; void do_command(karte_t*) OVERRIDE; // do some special checks network_broadcast_world_command_t* clone(karte_t *) OVERRIDE; uint8 cmd = 0xFF; uint8 player_nr = 0xFF; uint16 param = 0; bool scripted_call = false; connection_info_t* pending_company_creator = NULL; // this client want to create new company (not sent) /// store information about client that created a company static connection_info_t* company_creator[PLAYER_UNOWNED]; /// store information about clients that played with a company static slist_tpl company_active_clients[PLAYER_UNOWNED]; /// callback when company was removed static void company_removed(uint8 player_nr); private: nwc_chg_player_t(const nwc_chg_player_t&); nwc_chg_player_t& operator=(const nwc_chg_player_t&); }; /** * nwc_tool_t * @from-client: client sends tool init/work * @from-server: server sends nwc_tool_t to all clients with step when tool has to be executed * @data client_id (the client that launched the tool, sent by server) * @data player_nr * @data init (if true call init else work) * @data tool_id * @data pos * @data default_param * @data exec (if true executes, else server sends it to clients) */ class nwc_tool_t : public network_broadcast_world_command_t { public: // to detect desync we sent these infos always together (only valid for tools) checklist_t last_checklist; uint32 last_sync_step; nwc_tool_t(); nwc_tool_t(player_t *player, tool_t *tool, koord3d pos, uint32 sync_steps, uint32 map_counter, bool init); nwc_tool_t(const nwc_tool_t&); // messages are allowed to arrive at any time bool ignore_old_events() const OVERRIDE; virtual ~nwc_tool_t(); void rdwr() OVERRIDE; // clone performs authentication checks network_broadcast_world_command_t* clone(karte_t *) OVERRIDE; // really executes it, here exec should be true void do_command(karte_t*) OVERRIDE; void init_tool(); private: // transfered data plainstring default_param; uint32 tool_client_id; uint32 callback_id; uint16 tool_id; sint16 wt; // needed for scenario checks koord3d pos; uint8 flags; uint8 player_nr; bool init; uint8 custom_data_buf[256]; memory_rw_t custom_data; // tool that will be executed tool_t* tool; // compare default_param's (NULL pointers allowed) // @return true if default_param are equal static bool cmp_default_param(const char *d1, const char *d2); }; /** * nwc_step_t * @from-server: * @data contains the current sync_steps of the server * defining the maximum sync_steps a client can advance to. */ class nwc_step_t : public network_world_command_t { public: nwc_step_t() : network_world_command_t(NWC_STEP, 0, 0) { } nwc_step_t(uint32 sync_steps, uint32 map_counter) : network_world_command_t(NWC_STEP, sync_steps, map_counter) {} bool execute(karte_t *) OVERRIDE { return true;} }; #endif simutrans-124.5/src/simutrans/network/network_cmd_scenario.cc000066400000000000000000000062321520675460500246020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "network_cmd_scenario.h" #include "network_packet.h" #include "network_socket_list.h" #include "../world/simworld.h" #include "../dataobj/scenario.h" #include "../dataobj/environment.h" #include "../script/script.h" void nwc_scenario_t::init(script_vm_t *script) { script->register_callback(&nwc_scenario_t::record_result, "nwc_scenario_t_record_result"); } bool nwc_scenario_t::record_result(const char* function, plainstring result, uint32 client_id) { SOCKET sock = socket_list_t::get_socket(client_id); if (sock == INVALID_SOCKET) { return false; } nwc_scenario_t nwc; nwc.what = CALL_SCRIPT_ANSWER; nwc.function = function; nwc.result = result; nwc.send( sock ); return true; } void nwc_scenario_t::rdwr() { network_command_t::rdwr(); packet->rdwr_short(what); packet->rdwr_short(won); packet->rdwr_short(lost); packet->rdwr_str(function); packet->rdwr_str(result); } bool nwc_scenario_t::execute(karte_t *welt) { scenario_t *scen = welt->get_scenario(); if (scen == NULL || !scen->is_scripted()) { return true; } script_vm_t *script = scen->script; if (what == OPEN_SCEN_WIN) { // open window on server and clients scen->open_info_win(function); return true; } if (env_t::server) { switch (what) { case CALL_SCRIPT: case CALL_SCRIPT_ANSWER: { // register callback to send result back to client if script is delayed. script->prepare_callback("nwc_scenario_t_record_result", 2, function, (const char*)"", socket_list_t::get_client_id( packet->get_sender() ) ); plainstring res = dynamic_string::fetch_result(function, script, NULL, what==CALL_SCRIPT_ANSWER); // clear callback, in case function call was successfull script->clear_pending_callback(); nwc_scenario_t nwc; nwc.what = CALL_SCRIPT_ANSWER; nwc.function = function; nwc.result = res; nwc.send( packet->get_sender() ); break; } case UPDATE_WON_LOST: default: ; } } else { switch (what) { case CALL_SCRIPT_ANSWER: // store result, call listening string dynamic_string::record_result(function, result); break; case UPDATE_WON_LOST: scen->update_won_lost(won, lost); break; default: ; } } return true; } void nwc_scenario_rules_t::do_command(karte_t *welt) { scenario_t *scen = welt->get_scenario(); scen->intern_forbid(rule, player_nr, forbid); rule = NULL; // pointer is now invalid } void nwc_scenario_rules_t::rdwr() { network_broadcast_world_command_t::rdwr(); rule->rdwr(packet); packet->rdwr_bool(forbid); packet->rdwr_short(player_nr); } nwc_scenario_rules_t::nwc_scenario_rules_t(const nwc_scenario_rules_t& nwr) : network_broadcast_world_command_t(NWC_SCENARIO_RULES, nwr.get_sync_step(), nwr.get_map_counter()) { forbid = nwr.forbid; player_nr = nwr.player_nr; rule = new scenario_t::forbidden_t(*nwr.rule); } network_broadcast_world_command_t* nwc_scenario_rules_t::clone(karte_t *) { // scenario scripts only run on server if (socket_list_t::get_client_id(packet->get_sender()) != 0) { // not sent by server return NULL; } return new nwc_scenario_rules_t(*this); } simutrans-124.5/src/simutrans/network/network_cmd_scenario.h000066400000000000000000000043161520675460500244450ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_CMD_SCENARIO_H #define NETWORK_NETWORK_CMD_SCENARIO_H #include "network_cmd.h" #include "network_cmd_ingame.h" #include "../dataobj/scenario.h" #include "../utils/plainstring.h" /** * nwc_scenario_t: * scenario script runs on server only, to communicate results this command is used */ class nwc_scenario_t : public network_command_t { public: nwc_scenario_t() : network_command_t(NWC_SCENARIO), what(UNKNOWN), won(0), lost(0), function(NULL), result(NULL) { } bool execute(karte_t *) OVERRIDE; void rdwr() OVERRIDE; enum { UNKNOWN, CALL_SCRIPT, /// client asks for an update CALL_SCRIPT_ANSWER, /// client wants string, server sends string UPDATE_WON_LOST, /// update win/lose flags of the scenario OPEN_SCEN_WIN /// open scenario info window }; uint16 what; uint16 won, lost; plainstring function; plainstring result; /// register the callback to the script engine static void init(script_vm_t *script); /** * Callback method: sends answer back to client. * * @param function result of this function is returned * @param result * @param client_id to send result to * @returns dummy boolean value */ static bool record_result(const char* function, plainstring result, uint32 client_id); private: nwc_scenario_t(const nwc_scenario_t&); nwc_scenario_t& operator=(const nwc_scenario_t&); }; /** * nwc_scenario_rules_t: * scenario script runs on server only, use this command to send new rules to client */ class nwc_scenario_rules_t : public network_broadcast_world_command_t { public: nwc_scenario_rules_t(uint32 sync_step=0, uint32 map_counter=0) : network_broadcast_world_command_t(NWC_SCENARIO_RULES, sync_step, map_counter), rule( new scenario_t::forbidden_t()), forbid(true) { } ~nwc_scenario_rules_t() { delete rule; } void do_command(karte_t *) OVERRIDE; void rdwr() OVERRIDE; network_broadcast_world_command_t* clone(karte_t *) OVERRIDE; scenario_t::forbidden_t *rule; bool forbid; uint16 player_nr; private: nwc_scenario_rules_t(const nwc_scenario_rules_t&); nwc_scenario_rules_t& operator=(const nwc_scenario_rules_t&); }; #endif simutrans-124.5/src/simutrans/network/network_cmp_pakset.cc000066400000000000000000000175771520675460500243200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "network_cmp_pakset.h" #include "network_packet.h" #include "network.h" #include "network_socket_list.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../utils/cbuffer.h" #include "../simloadingscreen.h" #include stringhashtable_tpl::iterator nwc_pakset_info_t::server_iterator; SOCKET nwc_pakset_info_t::server_receiver = INVALID_SOCKET; nwc_pakset_info_t::~nwc_pakset_info_t() { delete chk; free(name); } bool nwc_pakset_info_t::execute(karte_t *) { // server side of the communication // client side in network_compare_pakset_with_server if( env_t::server ) { nwc_pakset_info_t nwi; bool send = false; bool ready = false; switch(flag) { case CL_INIT: // client want pakset info { if (server_receiver!=INVALID_SOCKET && socket_list_t::has_client(server_receiver)) { // we are already talking to another client nwi.flag = SV_ERROR; nwi.send(packet->get_sender()); // ignore result of send, we don't want to talk to that client either break; } server_receiver = packet->get_sender(); // restart iterator server_iterator = pakset_info_t::info.begin(); nwi.flag = SV_PAKSET; nwi.chk = new checksum_t(*pakset_info_t::get_checksum()); nwi.name = strdup("pakset"); DBG_MESSAGE("nwc_pakset_info_t::execute", "send info about %s",nwi.name); send = true; break; } case CL_WANT_NEXT: // client received one info packet, wants next if (server_iterator != pakset_info_t::info.end()) { nwi.flag = SV_DATA; nwi.chk = new checksum_t(*server_iterator->value); nwi.name = strdup(server_iterator->key); DBG_MESSAGE("nwc_pakset_info_t::execute", "send info about %s",nwi.name); ++server_iterator; } else { nwi.flag = SV_LAST; ready = true; } send = true; break; case CL_QUIT: // client ends this negotiation server_receiver = INVALID_SOCKET; break; default: ; } if( send ) { if(socket_list_t::has_client(server_receiver)) { // send, if unsuccessful stop comparing if (!nwi.send(server_receiver)) { ready = true; } } else { // client disappeared server_receiver = INVALID_SOCKET; } } if( ready ) { // all information sent server_receiver = INVALID_SOCKET; } } return true; } void nwc_pakset_info_t::rdwr() { network_command_t::rdwr(); packet->rdwr_byte(flag); packet->rdwr_str(name); bool has_info = (chk!=NULL && chk->is_valid()) || packet->is_loading(); packet->rdwr_bool(has_info); if( has_info ) { if( packet->is_loading() ) { chk = new checksum_t(); } chk->rdwr(packet); } } static bool str_cmp(const char *a, const char *b) { return strcmp(a,b) < 0; } void network_compare_pakset_with_server(const char* cp, std::string &msg) { // open from network const char *err = NULL; SOCKET const my_client_socket = network_open_address(cp, err); if( err==NULL ) { socket_list_t::add_client(my_client_socket); // for network_check_activity // client side of comparison // server part in nwc_pakset_info_t::execute { // start nwc_pakset_info_t nwi(nwc_pakset_info_t::CL_INIT); if (!nwi.send(my_client_socket)) { dbg->warning("network_compare_pakset_with_server", "send of NWC_PAKSETINFO failed"); socket_list_t::remove_client(my_client_socket); return; } } // copy our info to addon // ie treat all our paks as if they were not present on the server stringhashtable_tpl addons; { for(auto const& i : pakset_info_t::get_info()) { addons.put(i.key, i.value); } } // we do a sorted verctor of names ... vector_tpl missing, different; // show progress bar const uint32 num_paks = addons.get_count()+1; uint32 progress = 0; #define MAX_WRONG_PAKS 10 uint16 wrong_paks = 0; if(num_paks>0) { loadingscreen_t ls(translator::translate("Comparing pak files ..."), num_paks ); // communication loop bool ready = false; do { nwc_pakset_info_t *nwi = NULL; // wait for nwc_pakset_info_t, ignore other commands for(uint8 i=0; i<5; i++) { network_command_t* nwc = network_check_activity(10000); if (nwc && nwc->get_id() == NWC_PAKSETINFO) { nwi = (nwc_pakset_info_t*)nwc; break; } delete nwc; } if (nwi == NULL) { dbg->warning("network_compare_pakset_with_server", "server did not answer"); nwc_pakset_info_t nwi_quit(nwc_pakset_info_t::CL_QUIT); if (!nwi_quit.send(my_client_socket)) { err = "send of NWC_PAKSETINFO failed"; } break; } switch(nwi->flag) { case nwc_pakset_info_t::SV_PAKSET: { if(pakset_info_t::get_pakset_checksum()==(*(nwi->chk))) { // found identical paksets } else { wrong_paks++; } progress++; // request new data nwc_pakset_info_t nwi_data(nwc_pakset_info_t::CL_WANT_NEXT); if(!nwi_data.send(my_client_socket)) { err = "send of NWC_PAKSETINFO failed"; ready = true; } break; } case nwc_pakset_info_t::SV_DATA: { checksum_t* chk = addons.remove(nwi->name); if(chk) { if((*chk)==(*(nwi->chk))) { // found identical desc's } else { different.insert_ordered( nwi->name, str_cmp ); nwi->clear(); wrong_paks++; } progress++; } else { missing.insert_ordered( nwi->name, str_cmp ); nwi->clear(); wrong_paks++; } nwc_pakset_info_t nwi_next; if (wrong_paks<=MAX_WRONG_PAKS) { // request new data nwi_next.flag = nwc_pakset_info_t::CL_WANT_NEXT; } else { nwi_next.flag = nwc_pakset_info_t::CL_QUIT; } if(!nwi_next.send(my_client_socket)) { err = "send of NWC_PAKSETINFO failed"; ready = true; } break; } case nwc_pakset_info_t::SV_LAST: case nwc_pakset_info_t::SV_ERROR: default: ready = true; } // update progress bar ls.set_progress(progress); delete nwi; } while (!ready && wrong_paks<=MAX_WRONG_PAKS); } // now report the result msg.append(""); msg.append(translator::translate("Pakset differences")); msg.append("\n"); if(wrong_paks<=MAX_WRONG_PAKS && !addons.empty()) { msg.append("

"); msg.append(translator::translate("Pak(s) not on server:")); msg.append("


\n"); for(auto const& i : addons) { dbg->warning("network_compare_pakset_with_server", "PAK NOT ON SERVER: %s", i.key); msg.append(translator::translate(i.key+3)); msg.append("
\n"); } msg.append("
\n"); } if (!different.empty()) { msg.append("

"); msg.append(translator::translate("Pak(s) different:")); msg.append("


\n"); for(const char * const& i : different) { dbg->warning("network_compare_pakset_with_server", "PAK DIFFERENT: %s", i); msg.append(translator::translate(i+3)); // the first three letters are the type ... msg.append("
\n"); } msg.append("
\n"); } if (!missing.empty()) { msg.append("

"); msg.append(translator::translate("Pak(s) missing on client:")); msg.append("


\n"); for(const char * const& i : missing) { dbg->warning("network_compare_pakset_with_server", "PAK MISSING: %s", i); msg.append(translator::translate(i+3)); // the first three letters are the type ... msg.append("
\n"); } } if (wrong_paks>MAX_WRONG_PAKS) { msg.append("
\n"); msg.append("
\n"); cbuffer_t buf; buf.printf(translator::translate("Only first %d differing paks reported. There are probably more."), wrong_paks); msg.append((const char*)buf); msg.append("
\n"); } socket_list_t::remove_client(my_client_socket); } if(err) { dbg->warning("network_compare_pakset_with_server", err); } } simutrans-124.5/src/simutrans/network/network_cmp_pakset.h000066400000000000000000000031161520675460500241420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_CMP_PAKSET_H #define NETWORK_NETWORK_CMP_PAKSET_H #include "network_cmd.h" #include "pakset_info.h" #include /** * Compare paksets on server and client * client side of communication * @param cp url of server * @param msg contains html-text of differences to be displayed in a help_frame window */ void network_compare_pakset_with_server(const char* cp, std::string &msg); /** * nwc_pakset_info_t * @from-client: client wants to get pakset info from server * @from-server: server sends pakset info back to client */ class nwc_pakset_info_t : public network_command_t { public: nwc_pakset_info_t(uint8 flag_=UNDEFINED) : network_command_t(NWC_PAKSETINFO), flag(flag_), name(NULL), chk(NULL) {} ~nwc_pakset_info_t(); bool execute(karte_t *) OVERRIDE; void rdwr() OVERRIDE; enum { CL_INIT = 0, // client want pakset info CL_WANT_NEXT = 1, // client received one info packet, wants next CL_QUIT = 2, // client ends this negotiation SV_ERROR = 10, // server busy etc SV_PAKSET = 11, // server sends pakset checksum SV_DATA = 12, // server sends data SV_LAST = 19, // server sends last info packet UNDEFINED = 255 }; uint8 flag; // name of and info about descriptor char *name; checksum_t *chk; void clear() { name = NULL; chk = NULL; } // for the communication of the server with the client static stringhashtable_tpl::iterator server_iterator; static SOCKET server_receiver; }; #endif simutrans-124.5/src/simutrans/network/network_file_transfer.cc000066400000000000000000000415651520675460500250070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "network_file_transfer.h" #include "../simdebug.h" #include "../simloadingscreen.h" #include "../sys/simsys.h" #include #include #include #include "../utils/cbuffer.h" #ifndef NETTOOL #include "../dataobj/translator.h" #else #define dr_remove remove #define dr_fopen fopen #endif #include "../simversion.h" /* * Functions required by both Simutrans and Nettool */ #ifndef NETTOOL /// Parses a Content-Length header value. /// @returns a non-negative value on success, or -1 on error (e.g. missing/invalid/out-of-range value) static inline sint32 parse_content_length(const char *content_length_str) { char *endp = NULL; errno = 0; const long parsed = strtol(content_length_str, &endp, 10); if (endp == content_length_str || errno != 0 || parsed < 0 || parsed > INT32_MAX) { return -1; } return (sint32)parsed; } #endif const char *network_receive_file(const SOCKET src_sock, const char *const save_as, sint32 const length, sint32 const timeout ) { // ok, we have a socket to connect dr_remove(save_as); DBG_MESSAGE("network_receive_file", "File size %i", length ); if(length>0) { #ifndef NETTOOL // no display, no translator available loadingscreen_t ls(translator::translate("Downloading"),length,true,true); #endif // good place to show a progress bar char rbuf[4096]; sint32 length_read = 0; if (FILE* const f = dr_fopen(save_as, "wb")) { while(length_read < length) { if( timeout > 0 ) { /** 10s for 4096 bytes: * As long as you are not connected with less than 1200 Baud that should be fine * otherwise upgrade your acoustic coupler to 56k ... */ fd_set fds; FD_ZERO(&fds); FD_SET(src_sock,&fds); struct timeval tv; // 10 s timeout tv.tv_sec = 10000 / 1000; tv.tv_usec = (10000 % 1000) * 1000ul; // can we read? if( select( FD_SETSIZE, &fds, NULL, NULL, &tv )!=1 ) { dbg->warning("network_receive_file", "Timeout during transfer: %s", strerror(errno) ); break; } } // ok, now here should be something new to read int i = recv(src_sock, rbuf, length_read + 4096 < length ? 4096 : length - length_read, 0); if (i > 0) { fwrite(rbuf, 1, i, f); length_read += i; #ifndef NETTOOL ls.set_progress(length_read); #endif } else { if (i < 0) { dbg->warning("network_receive_file", "recv failed with %i", i); } break; } } fclose(f); } if( length_read(nwc); if (nwgi==NULL) { err = "Protocol error (expected NWC_GAMEINFO)"; goto end; } if (nwgi->len==0) { err = "Server busy"; goto end; } len = nwgi->len; sprintf( filename, "client%i-network.sve", nwgi->len ); err = network_receive_file( my_client_socket, filename, len ); if (err == NULL) { // now into gameinfo const loadsave_t::file_status_t status = fd.rd_open( filename ); if( status == loadsave_t::FILE_STATUS_ERR_FUTURE_VERSION ) { err = "Server version too new"; } else if( status == loadsave_t::FILE_STATUS_ERR_NO_VERSION ) { err = "Unknown server version"; } else if( status != loadsave_t::FILE_STATUS_OK ) { err = "Server busy"; } else if( fd.is_version_less(120, 8) ) { // Querying gameinfo of older server versions may crash the client // See gameinfo_t::rdwr err = "Server version too old"; } else { *gi = gameinfo_t( &fd ); } fd.close(); } dr_remove( filename ); end: socket_list_t::remove_client( my_client_socket ); } network_close_socket( my_client_socket ); if(err) { dbg->warning("network_gameinfo", err); } return err; } // connect to address (cp), receive game, save to client%i-network.sve const char *network_connect(const char *cp, karte_t *world) { // open from network const char *err = NULL; SOCKET const my_client_socket = network_open_address(cp, err); if( err==NULL ) { // want to join { nwc_join_t nwc_join( env_t::nickname.c_str() ); nwc_join.rdwr(); if (!nwc_join.send(my_client_socket)) { err = "send of NWC_JOIN failed"; goto end; } } socket_list_t::reset(); socket_list_t::add_client(my_client_socket); // wait for join command (tolerate some wrong commands) network_command_t *nwc = NULL; for(uint8 i=0; i<5; i++) { nwc = network_check_activity(10000); if (nwc && nwc->get_id() == NWC_JOIN) break; } if (nwc==NULL) { err = "Server did not respond!"; goto end; } nwc_join_t *nwj = dynamic_cast(nwc); if (nwj==NULL) { err = "Protocol error (expected NWC_JOIN)"; goto end; } if (nwj->answer!=1) { err = "Server busy"; goto end; } // set nickname env_t::nickname = nwj->nickname.c_str(); network_set_client_id(nwj->client_id); // update map counter // wait for sync command (tolerate some wrong commands) for( uint8 i=0; i<5; ++i ) { nwc = network_check_activity(10000); if( nwc && nwc->get_id()==NWC_SYNC ) break; } if( nwc==NULL || nwc->get_id()!=NWC_SYNC ) { err = "Protocol error (expected NWC_SYNC)"; goto end; } world->set_map_counter( ((nwc_sync_t*)nwc)->get_new_map_counter() ); // receive nwc_game_t { #ifndef NETTOOL // no display, no translator available loadingscreen_t ls(translator::translate("Server preparing game ..."),300,true,true); #endif // wait for game command for 5 min (tolerate some wrong commands) to leave it enough time for saving for(int i=0; i<300; i++) { #ifndef NETTOOL // no display, no translator available ls.set_progress(i); #endif nwc = network_check_activity(1000); if (nwc && nwc->get_id() == NWC_GAME) { break; } } } if (nwc == NULL || nwc->get_id()!=NWC_GAME) { err = "Protocol error (expected NWC_GAME)"; goto end; } int len = ((nwc_game_t*)nwc)->len; // guaranteed individual file name ... char filename[256]; sprintf( filename, "client%i-network.sve", network_get_client_id() ); err = network_receive_file( my_client_socket, filename, len ); } end: if(err) { dbg->warning("network_connect", err); if (!socket_list_t::remove_client(my_client_socket)) { network_close_socket( my_client_socket ); } } else { const uint32 id = socket_list_t::get_client_id(my_client_socket); socket_list_t::change_state(id, socket_info_t::playing); } return err; } const char *network_send_file( const SOCKET dst_sock, const char *filename ) { FILE *fp = dr_fopen(filename,"rb"); if (fp == NULL) { dbg->warning("network_send_file", "could not open file %s", filename); return "Could not open file"; } char buffer[1024]; // find out length fseek(fp, 0, SEEK_END); long length = (long)ftell(fp); rewind(fp); uint32 bytes_sent = 0; // send size of file nwc_game_t nwc(length); if (dst_sock==INVALID_SOCKET || !nwc.send(dst_sock)) { goto error; } // good place to show a progress bar if(length>0) { loadingscreen_t ls( translator::translate("Transferring game ..."), length, true, true ); while( !feof(fp) ) { int bytes_read = (int)fread( buffer, 1, sizeof(buffer), fp ); uint16 dummy; if( !network_send_data(dst_sock, buffer, bytes_read, dummy, 250) ) { socket_list_t::remove_client(dst_sock); goto error; } bytes_sent += bytes_read; ls.set_progress( bytes_sent ); } } // ok, new client has savegame fclose(fp); return NULL; error: // an error occurred: close file fclose(fp); return "Client closed connection during transfer"; } /// POST a message (poststr) to an HTTP server at the specified address and relative path (name) /// Optionally: Receive response to file localname const char *network_http_post( const char *address, const char *name, const char *poststr, const char *localname ) { DBG_MESSAGE("network_http_post", address); // Open socket const char *err = NULL; SOCKET const my_client_socket = network_open_address(address, err); if (err==NULL) { #ifndef REVISION # define REVISION 0 #endif const char* format = "POST %s HTTP/1.1\r\n" "User-Agent: Simutrans/r%s\r\n" "Host: %s\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %d\r\n\r\n%s"; if ((strlen(format) + strlen(name) + strlen(address) + strlen(poststr) + strlen(QUOTEME(REVISION))) > 4060) { // We will get a buffer overwrite here if we continue dbg->fatal( "network_http_post", "Error: String too long (>4096)" ); } DBG_MESSAGE("network_http_post", "2"); char request[4096]; int const len = sprintf(request, format, name, QUOTEME(REVISION), address, strlen(poststr), poststr); uint16 dummy; if (!network_send_data(my_client_socket, request, len, dummy, 250)) { err = "Server did not respond!"; } DBG_MESSAGE("network_http_post", "3"); // Read the response header // line is max length of a header line // rbuf is a single char char line[1024], rbuf; unsigned int pos = 0; sint32 length = 0; // TODO better handling of error message from listing server // TODO while(1) { // Returns number of bytes received // Receive one char from socket into rbuf int i = recv( my_client_socket, &rbuf, 1, 0 ); if( i>0 ) { // If char is above 32 in ascii table + not going to overflow line[] // Note: This will truncate any header to 1023 chars! // Add char to line at the next position // This ignores non-printable chars, including the CR char if( rbuf>=32 && pos now data will follow break; } // NUL at the end of our constructed line string line[pos] = 0; // we only need the length tag // Compare string so far constructed against the header we // are seeking (e.g. Content-Length) DBG_MESSAGE("network_http_post", "received header: %s", line); if( STRNICMP("Content-Length:",line,15)==0 ) { length = parse_content_length(line + 15); } // Begin again to parse the next line pos = 0; } } else { break; } } DBG_MESSAGE("network_http_post", "5"); // for a simple query, just pass an empty filename if( localname && *localname ) { err = network_receive_file( my_client_socket, localname, length ); } network_close_socket( my_client_socket ); } else { dbg->warning("network_http_post()", "failed with %s", err); } return err; } const char* network_http_get(const char* address, const char* name, cbuffer_t& local) { const int REQ_HEADER_LEN = 1024; // open from network const char* err = NULL; SOCKET const my_client_socket = network_open_address(address, err); if (err == NULL) { #ifndef REVISION # define REVISION 0 #endif const char* format = "GET %s HTTP/1.1\r\n" "User-Agent: Simutrans/r%s\r\n" "Host: %s\r\n\r\n"; if ((strlen(format) + strlen(name) + strlen(address) + strlen(QUOTEME(REVISION))) > (REQ_HEADER_LEN - 1)) { // We will get a buffer overwrite here if we continue return "Error: String too long"; } char request[REQ_HEADER_LEN]; int const len = sprintf(request, format, name, QUOTEME(REVISION), address); uint16 dummy; if (!network_send_data(my_client_socket, request, len, dummy, 250)) { err = "Server did not respond!"; } // Read the response header and parse arguments as needed char line[1024], rbuf; unsigned int pos = 0; sint32 length = -1; while (1) { // Receive one character at a time the HTTP headers int i = recv(my_client_socket, &rbuf, 1, 0); if (i > 0) { if (rbuf >= 32 && pos < sizeof(line) - 1) { line[pos++] = rbuf; } if (rbuf == 10) { if (pos == 0) { // this line was empty => now data will follow break; } line[pos] = 0; DBG_MESSAGE("network_http_get", "received header: %s", line); if (STRNICMP("Content-Length:", line, 15) == 0) { length = parse_content_length(line + 15); } pos = 0; } } else { break; } } if (length < 0 || length > UINT16_MAX) { // network_receive_data takes a uint16 length; bigger bodies would need chunking. network_close_socket(my_client_socket); return "Error: missing or invalid Content-Length"; } char* buffer = new char[(size_t)length + 1]; uint16 bytesreceived = 0; if (length > 0 && !network_receive_data(my_client_socket, buffer, (uint16)length, bytesreceived, 10000)) { err = "Error: network_receive_data failed!"; } else if (bytesreceived != length) { err = "Error: Bytes received does not match length!"; } else { buffer[length] = 0; local.append(buffer, length); } DBG_MESSAGE("network_http_get", "received data length: %i", local.len()); delete[] buffer; network_close_socket(my_client_socket); } return err; } const char *network_http_get_file( const char* address, const char* name, const char *filename ) { const int REQ_HEADER_LEN = 1024; // open from network const char *err = NULL; SOCKET const my_client_socket = network_open_address( address, err ); if ( err==NULL ) { #ifndef REVISION # define REVISION 0 #endif const char* format = "GET %s HTTP/1.1\r\n" "User-Agent: Simutrans/r%s\r\n" "Host: %s\r\n\r\n"; if ( (strlen( format ) + strlen( name ) + strlen( address ) + strlen( QUOTEME(REVISION)) ) > ( REQ_HEADER_LEN - 1 ) ) { // We will get a buffer overwrite here if we continue return "Error: String too long"; } char request[REQ_HEADER_LEN]; int const len = sprintf( request, format, name, QUOTEME(REVISION), address ); uint16 dummy; if ( !network_send_data( my_client_socket, request, len, dummy, 250 ) ) { err = "Server did not respond!"; } // Read the response header and parse arguments as needed char line[1024], rbuf; unsigned int pos = 0; sint32 length = 0; while(pos 0 ) { if ( rbuf >= 32 && pos < sizeof(line) - 1 ) { line[pos++] = rbuf; } if ( rbuf == 10 ) { if ( pos > 0 && line[pos-1]==10) { line[pos++] = 10; line[pos++] = 0; // this line was empty => now data will follow break; } line[pos++] = 10; } } else { break; } } DBG_DEBUG("network_http_get_file()", "%s", line); int http_code = atoi(strchr(line, ' ')); // if not sucessful maybe redirect if(http_code==302 || http_code==301) { network_close_socket(my_client_socket); char new_ip[1024]; char new_path[1024]; if(char *c= tstrcasestr(line,"\nLocation: http://")) { tstrncpy(new_ip, c + 18, lengthof(new_ip)); if(char *c = strchr(new_ip, '/')) { tstrncpy(new_path, c, lengthof(new_path)); strcpy( c, ":80"); *strchr(new_path, 10) = 0; return network_http_get_file(new_ip, new_path, filename); } } if (tstrcasestr(line, "\nLocation: https://")) { return "Cannot handle https."; } return "Unknown redirect."; } if (http_code == 200) { if (char *c=strstr(line,"Content-Length:")) { length = parse_content_length(c + 15); } DBG_MESSAGE("network_http_get", "received data length: %i", length); err = network_receive_file(my_client_socket, filename, length); } else { static char err_code[64]; sprintf(err_code,"Cannot handle https: Server returned %d", http_code); err = err_code; } network_close_socket( my_client_socket ); } return err; } #endif simutrans-124.5/src/simutrans/network/network_file_transfer.h000066400000000000000000000030211520675460500246320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_FILE_TRANSFER_H #define NETWORK_NETWORK_FILE_TRANSFER_H /** * Contains functions to send & receive files over network * .. and to connect to a running simutrans server */ #include "network.h" class cbuffer_t; class karte_t; class gameinfo_t; // connect to address (cp), receive gameinfo, close const char *network_gameinfo(const char *cp, gameinfo_t *gi); // connects to server at (cp), receives game, save to client%i-network.sve const char *network_connect(const char *cp, karte_t *world); /// Send file over network const char *network_send_file(const SOCKET dst_sock, const char *filename); /// Receive file (directly to disk) const char *network_receive_file(const SOCKET src_sock, const char *const save_as, const sint32 length, const sint32 timeout=10000); /** * Use HTTP POST request to submit poststr to an HTTP server * Any response is saved to the file given by localname (pass NULL to ignore response) * Connection is closed after request is completed */ const char *network_http_post ( const char *address, const char *name, const char *poststr, const char *localname ); /** * Use HTTP to retrieve a file into the cbuffer_t object provided */ const char *network_http_get ( const char *address, const char *name, cbuffer_t& local ); /** * Use HTTP to retrieve a file into the FILE */ const char* network_http_get_file( const char* address, const char* name, const char *filename ); #endif simutrans-124.5/src/simutrans/network/network_packet.cc000066400000000000000000000053731520675460500234300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "network_packet.h" #include "network_socket_list.h" void packet_t::rdwr_header() { rdwr_short( size ); rdwr_short( version ); rdwr_short( id ); if (version > NETWORK_VERSION) { error = true; } } packet_t::packet_t() : memory_rw_t(buf,MAX_PACKET_LEN,true), size(0), version(NETWORK_VERSION), id(0), sock(INVALID_SOCKET), error(false), ready(false), count(0) { set_index(HEADER_SIZE); } packet_t::packet_t(const packet_t &p) : memory_rw_t(buf,MAX_PACKET_LEN,true) { version = p.version; id = p.id; error = p.error; ready = p.ready; sock = INVALID_SOCKET; size = 0; count = 0; uint16 index = p.get_current_index(); for(uint16 i = 0; i MAX_PACKET_LEN) { dbg->warning("packet_t::recv", "packet from [%d] has wrong size (%d)", sock, size); error = true; return; } } else { return; } } if (count >= HEADER_SIZE) { received = 0; if (!network_receive_data(sock, buf + count, size - count, received, 0)) { error = true; return; } count += received; if (count == size) { set_max_size(size); ready = true; } } } void packet_t::send(SOCKET s, bool complete) { if (has_failed()) { return; } // header written ? if (size == 0) { size = get_current_index(); // write header at right place set_index(0); set_max_size(HEADER_SIZE); rdwr_header(); } uint16 sent; const int timeout_ms = complete ? 250 : 0; if ( !network_send_data(s, (const char*) buf+count, size-count, sent, timeout_ms) ) { dbg->warning("packet_t::send", "error while sending to [%d]", s); error = true; return; } count += sent; // ready ? if (count == size) { ready = true; dbg->message("packet_t::send", "sent %d bytes to socket[%d]; id=%d, size=%d", count, s, id, size); } else { dbg->message("packet_t::send", "sent %d bytes to socket[%d]; id=%d, size=%d, left=%d", count, s, id, size, size-count); } } void packet_t::sent_by_server() { sock = socket_list_t::get_socket(0); } simutrans-124.5/src/simutrans/network/network_packet.h000066400000000000000000000033471520675460500232710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_PACKET_H #define NETWORK_NETWORK_PACKET_H #include "../simtypes.h" #include "memory_rw.h" #include "network.h" #define MAX_PACKET_LEN (8192) // static const do not work on all compilers/architectures #define HEADER_SIZE (6) // the network sizes are given ... class packet_t : public memory_rw_t { private: // the buffer uint8 buf[MAX_PACKET_LEN]; // the header // [0] size uint16 size; // [2] version uint16 version; // [4] id uint16 id; // who sent this packet SOCKET sock; bool error:1; // ready for sending / fully received bool ready:1; // how much already sent / received uint16 count; void rdwr_header(); public: /** * constructor: packet is in saving-mode */ packet_t(); packet_t(const packet_t &p); /** * constructor: packet is in loading-mode * @param s socket from where the packet has to be received */ packet_t(SOCKET s); /** * start/continue sending * sets bools ready or error * * @param s * @param complete forces to send the complete packet */ void send(SOCKET s, bool complete); /** * start/continue receiving * sets bools ready or error */ void recv(); bool has_failed() const { return error || is_overflow();} void failed() { error = true; } bool is_ready() const { return ready; } // can we understand the received packet? bool check_version() const { return is_saving() || (version <= NETWORK_VERSION); } uint16 get_id() const { return id; } void set_id(uint16 id_) { id = id_; } SOCKET get_sender() { return sock; } /** * mark this packet as sent by the server * @see network_send_server */ void sent_by_server(); }; #endif simutrans-124.5/src/simutrans/network/network_socket_list.cc000066400000000000000000000233331520675460500245000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "network_socket_list.h" #include "network_cmd.h" #include "network_cmd_ingame.h" #include "network_packet.h" #ifndef NETTOOL #include "../dataobj/environment.h" #endif bool connection_info_t::operator==(const connection_info_t& other) const { return (address.get_ip() == other.address.get_ip()) && ( strcmp(nickname.c_str(), other.nickname.c_str())==0 ); } socket_info_t::~socket_info_t() { reset(); } void socket_info_t::reset() { delete packet; packet = NULL; while(!send_queue.empty()) { packet_t *p = send_queue.remove_first(); delete p; } if (socket != INVALID_SOCKET) { network_close_socket(socket); } if (state != has_left) { state = inactive; } socket = INVALID_SOCKET; player_unlocked = 0; } network_command_t* socket_info_t::receive_nwc() { if (!is_active()) { return NULL; } if (packet == NULL) { packet = new packet_t(socket); } packet->recv(); if (packet->has_failed()) { // close this client (will delete packet) socket_list_t::remove_client(socket); } else if (packet->is_ready()) { // create command network_command_t *nwc = network_command_t::read_from_packet(packet); // the network_command takes care of deleting packet packet = NULL; return nwc; } return NULL; } void socket_info_t::process_send_queue() { while(!send_queue.empty()) { packet_t *p = send_queue.front(); p->send(socket, false); if (p->has_failed()) { // close this client, clear the send_queue socket_list_t::remove_client(socket); break; } else if (p->is_ready()) { // packet complete sent, remove from queue send_queue.remove_first(); delete p; // proceed with next packet } else { break; } } } void socket_info_t::send_queue_append(packet_t *p) { if (p) { if (!p->has_failed()) { send_queue.append(p); } else { delete p; } } } void socket_info_t::rdwr(packet_t *p) { address.rdwr(p); } /** * list: contains _all_ sockets * with the convention that all server_sockets are at indices 0..server_sockets-1 */ vector_tplsocket_list_t::list(20); uint32 socket_list_t::connected_clients; uint32 socket_list_t::playing_clients; /** * server: max number of server_sockets ever created * do not decrease! * client: number of server connections */ uint32 socket_list_t::server_sockets; /** * book-keeping for the number of connected / playing clients */ void socket_list_t::book_state_change(socket_info_t::connection_state_t state, sint8 incr) { switch (state) { case socket_info_t::connected: connected_clients += incr; break; case socket_info_t::playing: playing_clients += incr; break; // do not change case socket_info_t::inactive: case socket_info_t::server: default: break; } } void socket_list_t::change_state(uint32 id, socket_info_t::connection_state_t new_state) { book_state_change(list[id]->state, -1); list[id]->state = new_state; list[id]->player_unlocked = 0; book_state_change(list[id]->state, +1); } void socket_list_t::reset() { for(socket_info_t* const i : list) { i->reset(); } connected_clients = 0; playing_clients = 0; server_sockets = 0; } void socket_list_t::reset_clients() { for(uint32 j=server_sockets; jreset(); } connected_clients = 0; playing_clients = 0; } void socket_list_t::add_client( SOCKET sock, uint32 ip ) { dbg->message("socket_list_t::add_client", "add client socket[%d] at address %xd", sock, ip); uint32 i = list.get_count(); // check whether socket already added for( uint32 j=server_sockets; jsocket == sock && list[j]->state != socket_info_t::inactive ) { return; } if( list[j]->state == socket_info_t::inactive && i == list.get_count() ) { i = j; } } if( i == list.get_count() ) { list.append(new socket_info_t() ); } list[i]->socket = sock; list[i]->address = net_address_t(ip, 0); change_state( i, socket_info_t::connected ); network_set_socket_nodelay( sock ); } void socket_list_t::add_server( SOCKET sock ) { dbg->message("socket_list_t::add_server", "add server socket[%d]", sock); assert(connected_clients==0 && playing_clients==0); uint32 i = server_sockets; // check whether socket already added for(uint32 j=0; jsocket == sock && list[j]->state == socket_info_t::server) { return; } if (list[j]->state == socket_info_t::inactive && i == server_sockets) { i = j; } } if (i == server_sockets) { list.insert_at(server_sockets, new socket_info_t()); server_sockets++; } list[i]->socket = sock; change_state(i, socket_info_t::server); if (i==0) { #ifndef NETTOOL // set server nickname if (!env_t::nickname.empty()) { list[i]->nickname = env_t::nickname.c_str(); } else { list[i]->nickname = "Server#0"; env_t::nickname = list[i]->nickname.c_str(); } #endif } network_set_socket_nodelay( sock ); } bool socket_list_t::remove_client( SOCKET sock ) { dbg->message("socket_list_t::remove_client", "remove client socket[%d]", sock); for(uint32 j=0; jsocket == sock) { #ifdef NETTOOL if (list[j]->state == socket_info_t::playing) { #else if (env_t::server && list[j]->state == socket_info_t::playing) { #endif change_state(j, socket_info_t::has_left); } else { change_state(j, socket_info_t::inactive); } list[j]->reset(); network_close_socket(sock); return true; } } return false; } bool socket_list_t::has_client( SOCKET sock ) { return get_client_id(sock) < list.get_count(); } uint32 socket_list_t::get_client_id( SOCKET sock ){ for(uint32 j=0; jstate != socket_info_t::inactive && list[j]->socket == sock) { return j; } } return list.get_count(); } void socket_list_t::unlock_player_all(uint8 player_nr, bool unlock, uint32 except_client) { // nettool does not know about nwc_auth_player_t #ifndef NETTOOL for(uint32 i=0; istate == socket_info_t::playing) ) { uint16 old_player_unlocked = list[i]->player_unlocked; if (unlock) { list[i]->unlock_player(player_nr); } else { list[i]->lock_player(player_nr); } if (old_player_unlocked != list[i]->player_unlocked) { dbg->warning("socket_list_t::unlock_player_all", "old = %d new = %d id = %d", old_player_unlocked, list[i]->player_unlocked, i); // tell the player nwc_auth_player_t *nwc = new nwc_auth_player_t(); nwc->player_unlocked = list[i]->player_unlocked; if (i==0) { network_send_server(nwc); } else { nwc->send(list[i]->socket); delete nwc; } } } } #else (void) player_nr; (void) unlock; (void) except_client; #endif } void socket_list_t::send_all(network_command_t* nwc, bool only_playing_clients, uint8 player_nr) { if (nwc == NULL) { return; } for(uint32 i=server_sockets; iis_active() && list[i]->socket!=INVALID_SOCKET && (!only_playing_clients || list[i]->state == socket_info_t::playing || list[i]->state == socket_info_t::connected) && (player_nr >= PLAYER_UNOWNED || list[i]->is_player_unlocked(player_nr)) ) { packet_t *p = nwc->copy_packet(); list[i]->send_queue_append(p); } } } plainstring socket_list_t::get_all_nicks() { std::string nicks #if COLOUR_DEPTH>0 // only add server nick, if server has a gui = env_t::nickname #endif ; for (uint32 i = server_sockets; i < list.get_count(); i++) { if (list[i]->is_active() && list[i]->socket != INVALID_SOCKET && (list[i]->state == socket_info_t::playing || list[i]->state == socket_info_t::connected) ) { if (!nicks.empty()) { nicks += "\t"; } nicks += list[i]->nickname; } } return nicks.c_str(); } SOCKET socket_list_t::fill_set(fd_set *fds) { SOCKET s_max = 0; for(socket_info_t* const i : list) { if (i->state != socket_info_t::inactive && i->socket != INVALID_SOCKET) { SOCKET const s = i->socket; s_max = max( s, s_max ); FD_SET( s, fds ); } } return s_max+1; } SOCKET socket_list_t::fd_isset(fd_set *fds, bool use_server_sockets, uint32 *offset) { const uint32 begin = offset ? *offset : (use_server_sockets ? 0 : server_sockets); const uint32 end = use_server_sockets ? server_sockets : list.get_count(); for(uint32 i=begin; isocket; if (socket!=INVALID_SOCKET) { if( FD_ISSET(socket, fds) ) { if (offset) { *offset = i+1; } return socket; } } } if (offset) { *offset = end; } return INVALID_SOCKET; } socket_list_t::socket_iterator_t::socket_iterator_t(fd_set *fds) { index = 0; this->fds = fds; current = INVALID_SOCKET; } bool socket_list_t::server_socket_iterator_t::next() { current = socket_list_t::fd_isset(fds, true, &index); return current != INVALID_SOCKET; } bool socket_list_t::client_socket_iterator_t::next() { current = socket_list_t::fd_isset(fds, false, &index); return current != INVALID_SOCKET; } void socket_list_t::rdwr(packet_t *p, vector_tpl *list) { assert(p->is_saving() || list!=&socket_list_t::list); uint32 count = list->get_count(); p->rdwr_long(count); // Each entry costs at least one byte on the wire (the state byte // below); refuse a count beyond what the packet body can hold. if (p->is_loading() && count > MAX_PACKET_LEN - HEADER_SIZE) { p->failed(); return; } for(uint32 i=0; iis_loading()) { list->append(new socket_info_t()); } uint8 s = (*list)[i]->state; p->rdwr_byte(s); (*list)[i]->state = socket_info_t::connection_state_t(s); if ( s==socket_info_t::playing) { (*list)[i]->rdwr(p); } } } simutrans-124.5/src/simutrans/network/network_socket_list.h000066400000000000000000000143561520675460500243470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_NETWORK_SOCKET_LIST_H #define NETWORK_NETWORK_SOCKET_LIST_H #include "network.h" #include "network_address.h" #include "../simconst.h" #include "../tpl/slist_tpl.h" #include "../tpl/vector_tpl.h" #include "../utils/plainstring.h" class network_command_t; class packet_t; /** * Class to store pairs of (address, nickname) for logging and admin purposes. */ class connection_info_t { public: /// address of connection net_address_t address; /// client nickname plainstring nickname; connection_info_t() : address(), nickname() {} connection_info_t(const connection_info_t& other) : address(other.address), nickname(other.nickname) {} template void rdwr(F *packet) { address.rdwr(packet); packet->rdwr_str(nickname); } bool operator==(const connection_info_t& other) const; bool operator!=(const connection_info_t& other) const { return !(*this == other); } }; class socket_info_t : public connection_info_t { public: enum connection_state_t { inactive = 0, ///< client disconnected server = 1, ///< server socket connected = 2, ///< connection established but client does not participate in the game yet playing = 3, ///< client actively plays has_left = 4, ///< was playing but left admin = 5 ///< admin connection }; private: packet_t *packet; slist_tpl send_queue; public: connection_state_t state; SOCKET socket; uint16 player_unlocked; public: socket_info_t() : connection_info_t(), packet(0), send_queue(), state(inactive), socket(INVALID_SOCKET), player_unlocked(0) {} ~socket_info_t(); /** * marks all information as invalid * closes socket, deletes packet */ void reset(); bool is_active() const { return state != inactive; } /** * receive the next command: continues receiving the packet * if an error occurs while receiving the packet, (this) is reset * @return the command if packet is fully received */ network_command_t* receive_nwc(); /** * */ void process_send_queue(); void send_queue_append(packet_t *p); /** * rdwr client information to packet */ void rdwr(packet_t *p); /** * human players on this connection can play with in-game companies/players? */ bool is_player_unlocked(uint8 player_nr) const { return (player_nr < PLAYER_UNOWNED) && ((player_unlocked & 1<list; static uint32 connected_clients; static uint32 playing_clients; static uint32 server_sockets; public: static uint32 get_server_sockets() { return server_sockets; } static uint32 get_connected_clients() { return connected_clients; } static uint32 get_playing_clients() { return playing_clients; } /** * clears list, closes all sockets */ static void reset(); /** * clears and closes all client sockets */ static void reset_clients(); /** * adds server socket * assumes that no active client sockets are present */ static void add_server( SOCKET sock ); /** * server: adds client socket (ie connection to client) * client: adds client socket (ie connection to server) */ static void add_client( SOCKET sock, uint32 ip = 0 ); /** * @ returns true if socket is already in our list */ static bool has_client( SOCKET sock ); /** * @return true if client was found and removed */ static bool remove_client( SOCKET sock ); static uint32 get_client_id( SOCKET sock ); static bool is_valid_client_id( uint32 client_id ) { return client_id < list.get_count(); } uint32 static get_count() { return list.get_count(); } static SOCKET get_socket( uint32 client_id ) { return client_id < list.get_count() && list[client_id]->state != socket_info_t::inactive ? list[client_id]->socket : INVALID_SOCKET; } static socket_info_t& get_client(uint32 client_id ) { assert (client_id < list.get_count()); return *list[client_id]; } /** * unlocks/locks player for all clients, except client number except_client */ static void unlock_player_all(uint8 player_nr, bool unlock, uint32 except_client = list.get_count()); /** * send command to all clients * * @param nwc * @param only_playing_clients if true then send only to playing clients * @param player_nr if != PLAYER_UNOWNED then only send to clients with this player unlocked */ static void send_all(network_command_t* nwc, bool only_playing_clients, uint8 player_nr = PLAYER_UNOWNED); static plainstring get_all_nicks(); static void change_state(uint32 id, socket_info_t::connection_state_t new_state); /** * rdwr client-list information to packet */ static void rdwr(packet_t *p, vector_tpl *writeto=&list); private: static void book_state_change(socket_info_t::connection_state_t state, sint8 incr); public: // from now stuff to deal with fd_set's /** * @param fds * @param offset pointer to an offset * @return the first client whose bit is set in fd_set */ static SOCKET fd_isset(fd_set *fds, bool use_server_sockets, uint32 *offset=NULL); /** * fill set with all active sockets */ static SOCKET fill_set(fd_set *fds); /** * iterators to iterate through all sockets whose bits are set in fd_set */ class socket_iterator_t { protected: uint32 index; // index to the socket list fd_set *fds; // fds as modified by select(), be sure it gets not deleted while the iterator is alive SOCKET current; public: socket_iterator_t(fd_set *fds); SOCKET get_current() const { return current; } }; /** * .. iterate through server sockets */ class server_socket_iterator_t : public socket_iterator_t { public: server_socket_iterator_t(fd_set *fds) : socket_iterator_t(fds) {} bool next(); }; /** * .. and client sockets */ class client_socket_iterator_t : public socket_iterator_t { public: client_socket_iterator_t(fd_set *fds) : socket_iterator_t(fds) { index = server_sockets; } bool next(); }; }; #endif simutrans-124.5/src/simutrans/network/pakset_info.cc000066400000000000000000000033531520675460500227060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "pakset_info.h" #include "../simdebug.h" #include "../dataobj/translator.h" #include "../dataobj/pakset_manager.h" #include "../tpl/vector_tpl.h" #include "../utils/cbuffer.h" stringhashtable_tpl pakset_info_t::info; checksum_t pakset_info_t::general; void pakset_info_t::append(const char* name, const char* type, checksum_t *chk) { chk->finish(); static cbuffer_t buf; buf.clear(); buf.printf("%s:%s", type, name); checksum_t *old = info.set( strdup( (const char*)buf), chk ); if (old) { if (*chk == *old) { dbg->warning("pakset_info_t::append", "Object %s::%s is overlaid by identical object, using data of new object", type, name); } else { dbg->warning("pakset_info_t::append", "Object %s::%s is overlaid by different object, using data of new object", type, name); pakset_manager_t::doubled(type, name); } } delete old; } void pakset_info_t::debug() { #if MSG_LEVEL >= 4 for(auto const& i : info) { DBG_DEBUG4("pakset_info_t::debug", "%.30s -> sha1 = %s", i.key, i.value->get_str()); } #endif } /** * order all pak checksums by name */ struct entry_t { entry_t(const char* n=NULL, const checksum_t* i=NULL) : name(n), chk(i) {} const char* name; const checksum_t* chk; }; static bool entry_cmp(entry_t a, entry_t b) { return strcmp(a.name, b.name) < 0; } void pakset_info_t::calculate_checksum() { general.reset(); // first sort all the desc's vector_tpl sorted(info.get_count()); for(auto const& i : info) { sorted.insert_ordered(entry_t(i.key, i.value), entry_cmp); } // now loop for(entry_t const& i : sorted) { i.chk->calc_checksum(&general); } general.finish(); } simutrans-124.5/src/simutrans/network/pakset_info.h000066400000000000000000000020151520675460500225420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef NETWORK_PAKSET_INFO_H #define NETWORK_PAKSET_INFO_H #include "../tpl/stringhashtable_tpl.h" #include "../descriptor/objversion.h" #include "checksum.h" class pakset_info_t { /** * checksums of all desc's * since their names are unique we can index them by name */ static stringhashtable_tpl info; /** * pakset checksum */ static checksum_t general; public: static const checksum_t& get_pakset_checksum() { return general; } static const stringhashtable_tpl& get_info() { return info; } static void calculate_checksum(); static checksum_t* get_checksum() { return &general; } /** * Register object with given @p name and @p type. * Sends warning to pakset_manager_t for objects with same name, type but different checksum. */ static void append(const char* name, const char* type, checksum_t *chk); static void debug(); friend class nwc_pakset_info_t; }; #endif simutrans-124.5/src/simutrans/obj/000077500000000000000000000000001520675460500171525ustar00rootroot00000000000000simutrans-124.5/src/simutrans/obj/baum.cc000066400000000000000000000166451520675460500204210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "baum.h" #include "groundobj.h" #include "../ground/grund.h" #include "../dataobj/environment.h" #include "../dataobj/freelist.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../descriptor/tree_desc.h" #include "../display/simimg.h" #include "../player/simplay.h" #include "../simdebug.h" #include "../simtypes.h" #include "../world/simworld.h" #include "../utils/cbuffer.h" #include "../utils/simrandom.h" #include #include #include FLAGGED_PIXVAL baum_t::outline_color = 0; freelist_tpl baum_t::fl; baum_t::baum_t(loadsave_t *file) : obj_t(), geburt(welt->get_current_month()), tree_id(0), season(0) { rdwr(file); } baum_t::baum_t(koord3d pos) : obj_t(pos), geburt(welt->get_current_month() - simrand(baum_t::AGE_LIMIT-1)), // generate aged trees, might underflow season(0) { tree_id = (uint8)tree_builder_t::random_tree_id_for_climate( welt->get_climate( pos.get_2d() ) ); calc_off( welt->lookup( get_pos())->get_grund_hang() ); calc_image(); } baum_t::baum_t(koord3d pos, uint8 type, uint16 age, slope_t::type slope ) : obj_t(pos), geburt(welt->get_current_month() - age), // might underflow tree_id(type), season(0) { calc_off( slope ); calc_image(); } baum_t::baum_t(koord3d pos, const tree_desc_t *desc) : obj_t(pos), geburt(welt->get_current_month()), tree_id(tree_builder_t::get_id_by_desc(desc)), season(0) { calc_off( welt->lookup( get_pos())->get_grund_hang() ); calc_image(); } void baum_t::rdwr(loadsave_t *file) { xml_tag_t d( file, "baum_t" ); obj_t::rdwr(file); uint32 age = get_age() << 18; file->rdwr_long(age); // after loading, calculate anew age &= 0xFFF << 18; geburt = welt->get_current_month() - (age>>18); if(file->is_loading()) { char buf[128]; file->rdwr_str(buf, lengthof(buf)); const tree_desc_t *desc = tree_builder_t::get_desc_by_name( buf ); if( !desc ) { desc = tree_builder_t::get_desc_by_name( translator::compatibility_name(buf) ); } if( desc ) { tree_id = tree_builder_t::get_id_by_desc( desc ); } else { // not a tree tree_id = tree_builder_t::get_num_trees(); } } else { const char *c = get_desc()->get_name(); file->rdwr_str(c); } // z-offset if(file->is_version_atleast(111, 1)) { uint8 zoff_ = zoff; file->rdwr_byte(zoff_); zoff = zoff_; } else { // correct z-offset if(file->is_loading()) { // this will trigger recalculation of offset in finish_rd() // we cant call calc_off() since this->pos is still invalid set_xoff(-128); } } } void baum_t::finish_rd() { if(get_xoff()==-128) { calc_off(welt->lookup( get_pos())->get_grund_hang()); } } image_id baum_t::get_image() const { if( env_t::hide_trees ) { if( env_t::hide_with_transparency ) { // we need the real age for transparency or real image return IMG_EMPTY; } else { return tree_builder_t::get_tree_image(tree_id, 0, season); } } return tree_builder_t::get_tree_image(tree_id, get_age(), season); // return get_desc()->get_image_id( season, baum_alter ); } // image which transparent outline is used image_id baum_t::get_outline_image() const { return tree_builder_t::get_tree_image(tree_id, get_age(), season); // return get_desc()->get_image_id( season, baum_alter ); } // actually calculates only the season void baum_t::calc_image() { // summer autumn winter spring season = welt->get_season(); if( welt->get_snowline() <= get_pos().z || welt->get_climate( get_pos().get_2d() ) == arctic_climate ) { // snowy winter graphics season = 4; } else if(welt->get_snowline()<=get_pos().z+1 && season==0) { // snowline crossing in summer // so at least some weeks spring/autumn season = welt->get_last_month() <=5 ? 3 : 1; } } FLAGGED_PIXVAL baum_t::get_outline_colour() const { return outline_color; } void baum_t::recalc_outline_color() { outline_color = (env_t::hide_trees && env_t::hide_with_transparency) ? (TRANSPARENT25_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_BLACK)) : 0; } /* we should be as fast as possible, because trees are nearly the most common object on a map */ bool baum_t::check_season(const bool) { // take care of birth/death and seasons const uint16 age = get_age(); if( age >= baum_t::SPAWN_PERIOD_START && age < baum_t::SPAWN_PERIOD_START + baum_t::SPAWN_PERIOD_LENGTH ) { // only in this month a tree can span new trees // only 1-3 trees will be planted.... uint8 const c_plant_tree_max = 1 + simrand( welt->get_settings().get_max_no_of_trees_on_square() ); uint retries = 0; for( uint8 c_temp = 0; c_temp < c_plant_tree_max && retries < c_plant_tree_max; c_temp++ ) { if( !tree_builder_t::spawn_tree_near(this) ) { retries++; c_temp--; } } // we make the tree four months older to avoid second spawning geburt -= baum_t::SPAWN_PERIOD_LENGTH; } // tree will die after 704 months (i.e. 58 years 8 months) if( age >= baum_t::AGE_LIMIT ) { mark_image_dirty( get_image(), 0 ); return false; } // update seasonal image const uint8 old_season = season; calc_image(); if( season != old_season ) { mark_image_dirty( get_image(), 0 ); } return true; } void baum_t::rotate90() { // cant use obj_t::rotate90 to rotate offsets as it rotates them only if xoff!=0 sint8 old_yoff = get_yoff() + zoff; sint8 old_xoff = get_xoff(); // rotate position obj_t::rotate90(); // .. and the offsets set_xoff( -2 * old_yoff ); set_yoff( old_xoff/2 - zoff); } void baum_t::recalc_off() { // reconstruct position on tile const sint8 xoff = get_xoff() + 32; // = x+y const sint8 yoff = 2*(get_yoff() + zoff); // = y-x sint8 x = (xoff - yoff) / 2; sint8 y = (xoff + yoff) / 2; calc_off(welt->lookup( get_pos())->get_grund_hang(), x, y); } // calculates tree position on a tile // takes care of slopes void baum_t::calc_off(slope_t::type slope, sint8 x_, sint8 y_) { const sint16 random = (sint16)( get_pos().x + get_pos().y + get_pos().z + slope + tree_id + geburt ); // point on tile (imaginary origin at sw corner, x axis: north, y axis: east sint16 x = x_==-128 ? (random + tree_id) & 31 : x_; sint16 y = y_==-128 ? (random + geburt) & 31 : y_; // the last bit has to be the same y ^= x&1; // bilinear interpolation of tile height const uint32 zoff_ = ((corner_ne(slope) * x * y + corner_nw(slope) * x * (32-y) + corner_se(slope) * (32-x) * y + corner_sw(slope) * (32-x) * (32-y)) * TILE_HEIGHT_STEP) / (32*32); // now zoff between 0 and TILE_HEIGHT_STEP-1 zoff = zoff_ < (uint32)TILE_HEIGHT_STEP ? zoff_ : TILE_HEIGHT_STEP-1u; // xoff must be even set_xoff( x + y - 32 ); set_yoff( (y - x)/2 - zoff); } void baum_t::show_info() { if(env_t::tree_info) { obj_t::show_info(); } } void baum_t::info(cbuffer_t &buf) const { translator::get_obj_info(buf, get_desc()->get_name() ); const int age = (int)get_age(); buf.printf( translator::translate("%i years %i months old."), age/12, (age%12) ); if (char const* const maker = get_desc()->get_copyright()) { buf.append("\n\n"); buf.printf(translator::translate("Constructed by %s"), maker); } } void baum_t::cleanup(player_t *player) { player_t::book_construction_costs(player, welt->get_settings().cst_remove_tree, get_pos().get_2d(), ignore_wt); mark_image_dirty( get_image(), 0 ); } uint16 baum_t::get_age() const { return (welt->get_current_month() - (uint32)geburt) % (1<<12); } simutrans-124.5/src/simutrans/obj/baum.h000066400000000000000000000063741520675460500202610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_BAUM_H #define OBJ_BAUM_H #include "simobj.h" #include "../builder/tree_builder.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/vector_tpl.h" #include "../tpl/weighted_vector_tpl.h" #include "../tpl/freelist_tpl.h" #include "../descriptor/tree_desc.h" #include "../simcolor.h" #include "../dataobj/environment.h" #include /** * Simulated trees for Simutrans. */ class baum_t : public obj_t { friend class tree_builder_t; public: static const uint16 AGE_LIMIT = 704; // in months (58 years 8 months). Must be smaller than 4095. static const uint16 SPAWN_PERIOD_START = 512; static const uint16 SPAWN_PERIOD_LENGTH = 4; private: static FLAGGED_PIXVAL outline_color; /// month of birth uint16 geburt; /// type of tree (was 9 but for more compact saves now only 254 different tree types are allowed) uint8 tree_id; uint8 season:3; /// z-offset, max TILE_HEIGHT_STEP i.e. 4 bits uint8 zoff:4; // one bit free ;) static freelist_tpl fl; public: /// Only the load save constructor should be called outside. /// Otherwise I suggest use the plant tree function (@see tree_builder_t) baum_t(loadsave_t *file); baum_t(koord3d pos); /** * @param pos * @param type * @param age Must be smaller than 4095 * @param slope * */ baum_t(koord3d pos, uint8 type, uint16 age, slope_t::type slope ); baum_t(koord3d pos, const tree_desc_t *desc); void* operator new(size_t) { return fl.gimme_node(); } void operator delete(void* p) { return fl.putback_node(p); } public: /// @copydoc obj_t::get_name const char *get_name() const OVERRIDE { return "Baum"; } /// @copydoc obj_t::get_typ typ get_typ() const OVERRIDE { return baum; } /// @copydoc obj_t::rdwr /// @deprecated Only used for loading old saves that did save tree offsets void rdwr(loadsave_t *file) OVERRIDE; /// @copydoc obj_t::finish_rd void finish_rd() OVERRIDE; /// @copydoc obj_t::get_image image_id get_image() const OVERRIDE; /// @copydoc obj_t::get_outline_image image_id get_outline_image() const OVERRIDE; /// @copydoc obj_t::calc_image /// Calculates tree image dependent on tree age void calc_image() OVERRIDE; /// @copydoc obj_t::get_outline_colour /// hide trees eventually with transparency FLAGGED_PIXVAL get_outline_colour() const OVERRIDE; static void recalc_outline_color(); /// @copydoc obj_t::check_season bool check_season(const bool) OVERRIDE; /// @copydoc obj_t::rotate90 void rotate90() OVERRIDE; /// re-calculate z-offset if slope of the tile has changed void recalc_off(); /// @copydoc obj_t::show_info void show_info() OVERRIDE; /// @copydoc obj_t::info void info(cbuffer_t & buf) const OVERRIDE; /// @copydoc obj_t::cleanup void cleanup(player_t *player) OVERRIDE; public: const tree_desc_t *get_desc() const { return tree_builder_t::get_desc_by_id(tree_id); } void set_desc(const tree_desc_t *desc) { tree_id = tree_builder_t::get_id_by_desc(desc); } uint16 get_desc_id() const { return tree_id; } /// @returns age of the tree in months, between 0 and 4095 uint16 get_age() const; private: /// calculate offsets for new trees void calc_off(slope_t::type slope, sint8 x=-128, sint8 y=-128); }; #endif simutrans-124.5/src/simutrans/obj/bruecke.cc000066400000000000000000000221501520675460500211010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../world/simworld.h" #include "../simtypes.h" #include "simobj.h" #include "../ground/grund.h" #include "../player/simplay.h" #include "../display/simimg.h" #include "../builder/brueckenbauer.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/pakset_manager.h" #include "bruecke.h" bruecke_t::bruecke_t(loadsave_t* const file) : obj_no_info_t() { rdwr(file); } bool bruecke_t::is_clipping_below_needed() const { // elevated no clip? return desc->is_clip_below(); } bruecke_t::bruecke_t(koord3d pos, player_t *player, const bridge_desc_t *desc, bridge_desc_t::img_t img) : obj_no_info_t(pos) { this->desc = desc; assert(img >= bridge_desc_t::NS_Segment && img <= bridge_desc_t::OW_Pillar2); this->img = img; set_owner( player ); player_t::book_construction_costs( get_owner(), -desc->get_price(), get_pos().get_2d(), desc->get_waytype()); } // single height segments static bridge_desc_t::img_t single_img[24]= { bridge_desc_t::NS_Segment, bridge_desc_t::OW_Segment, bridge_desc_t::N_Start, bridge_desc_t::S_Start, bridge_desc_t::O_Start, bridge_desc_t::W_Start, bridge_desc_t::N_Ramp, bridge_desc_t::S_Ramp, bridge_desc_t::O_Ramp, bridge_desc_t::W_Ramp, bridge_desc_t::NS_Pillar, bridge_desc_t::OW_Pillar, bridge_desc_t::NS_Segment, bridge_desc_t::OW_Segment, bridge_desc_t::N_Start, bridge_desc_t::S_Start, bridge_desc_t::O_Start, bridge_desc_t::W_Start, bridge_desc_t::N_Ramp, bridge_desc_t::S_Ramp, bridge_desc_t::O_Ramp, bridge_desc_t::W_Ramp, bridge_desc_t::NS_Pillar, bridge_desc_t::OW_Pillar }; void bruecke_t::calc_image() { grund_t *gr=welt->lookup(get_pos()); if(gr) { // if we are on the bridge, put the image into the ground, so we can have two ways ... if( weg_t *weg0 = gr->get_weg_nr(0) ) { #ifdef MULTI_THREAD weg0->lock_mutex(); #endif // if on a slope then start of bridge - take the upper value const slope_t::type slope = gr->get_grund_hang(); bool is_snow = welt->get_climate( get_pos().get_2d() ) == arctic_climate || get_pos().z + slope_t::max_diff(slope) >= welt->get_snowline(); // handle cases where old bridges don't have correct images image_id display_image=desc->get_background( img, is_snow ); if( display_image==IMG_EMPTY && desc->get_foreground( img, is_snow )==IMG_EMPTY ) { display_image=desc->get_background( single_img[img], is_snow ); } weg0->set_image( display_image ); // must always set both offsets, because after roation the xoffset contains the yoffset weg0->set_yoff( -gr->get_weg_yoff() ); weg0->set_xoff( 0 ); weg0->set_foreground_image(IMG_EMPTY); weg0->set_flag(obj_t::dirty); #ifdef MULTI_THREAD weg0->unlock_mutex(); #endif if( weg_t *weg1 = gr->get_weg_nr(1) ) { #ifdef MULTI_THREAD weg1->lock_mutex(); #endif weg1->set_yoff( -gr->get_weg_yoff() ); weg1->set_xoff( 0 ); #ifdef MULTI_THREAD weg1->unlock_mutex(); #endif } } set_yoff( -gr->get_weg_yoff() ); set_xoff( 0 ); } } image_id bruecke_t::get_front_image() const { grund_t *gr=welt->lookup(get_pos()); // if on a slope then start of bridge - take the upper value const slope_t::type slope = gr->get_grund_hang(); bool is_snow = welt->get_climate( get_pos().get_2d() ) == arctic_climate || get_pos().z + slope_t::max_diff(slope) >= welt->get_snowline(); // handle cases where old bridges don't have correct images image_id display_image=desc->get_foreground( img, is_snow ); if( display_image==IMG_EMPTY && desc->get_background( img, is_snow )==IMG_EMPTY ) { display_image=desc->get_foreground( single_img[img], is_snow ); } return display_image; } void bruecke_t::rdwr(loadsave_t *file) { xml_tag_t d( file, "bruecke_t" ); obj_t::rdwr(file); const char *s = NULL; if(file->is_saving()) { s = desc->get_name(); } file->rdwr_str(s); file->rdwr_enum(img); if(file->is_loading()) { if (!s) { dbg->fatal("bruecke_t::rdwr", "No bridge name for bridge at (%s)", get_pos().get_str()); } desc = bridge_builder_t::get_desc(s); if(desc==NULL) { desc = bridge_builder_t::get_desc(translator::compatibility_name(s)); } if(desc==NULL) { dbg->warning( "bruecke_t::rdwr", "Unknown bridge \"%s\" at (%s) will be replaced with best match!", s, get_pos().get_str() ); pakset_manager_t::add_missing_paks( s, MISSING_BRIDGE ); } free(const_cast(s)); if( file->is_version_less(112, 7) && env_t::pak_height_conversion_factor==2 ) { switch(img) { case bridge_desc_t::OW_Segment: img = bridge_desc_t::OW_Segment2; break; case bridge_desc_t::NS_Segment: img = bridge_desc_t::NS_Segment2; break; case bridge_desc_t::O_Start: img = bridge_desc_t::O_Start2; break; case bridge_desc_t::W_Start: img = bridge_desc_t::W_Start2; break; case bridge_desc_t::S_Start: img = bridge_desc_t::S_Start2; break; case bridge_desc_t::N_Start: img = bridge_desc_t::N_Start2; break; case bridge_desc_t::O_Ramp: img = bridge_desc_t::O_Ramp2; break; case bridge_desc_t::W_Ramp: img = bridge_desc_t::W_Ramp2; break; case bridge_desc_t::S_Ramp: img = bridge_desc_t::S_Ramp2; break; case bridge_desc_t::N_Ramp: img = bridge_desc_t::N_Ramp2; break; case bridge_desc_t::OW_Pillar: img = bridge_desc_t::OW_Pillar2; break; case bridge_desc_t::NS_Pillar: img = bridge_desc_t::NS_Pillar2; break; default: break; } } assert(img >= bridge_desc_t::NS_Segment && img <= bridge_desc_t::OW_Pillar2); } } // correct speed and maintenance void bruecke_t::finish_rd() { grund_t *gr = welt->lookup(get_pos()); if(desc==NULL) { if( weg_t *weg = gr->get_weg_nr(0) ) { desc = bridge_builder_t::find_bridge( weg->get_waytype(), weg->get_max_speed(), welt->get_timeline_year_month() ); if(desc==NULL) { desc = bridge_builder_t::find_bridge( weg->get_waytype(), weg->get_max_speed(), 0 ); } if(desc==NULL) { dbg->fatal("bruecke_t::finish_rd()", "Unknown bridge for type %x at (%s)", weg->get_waytype(), get_pos().get_str() ); } } else { // assume this is a powerbridge, since otherwise there should be a way desc = bridge_builder_t::find_bridge( powerline_wt, 0, welt->get_timeline_year_month() ); if(desc==NULL) { desc = bridge_builder_t::find_bridge( powerline_wt, 0, 0 ); } if(desc==NULL) { dbg->fatal("bruecke_t::finish_rd()", "No powerline bridge to build bridge type at (%s)", get_pos().get_str() ); } } } player_t *player=get_owner(); // change maintenance if(desc->get_waytype()!=powerline_wt) { weg_t *weg = gr->get_weg(desc->get_waytype()); if(weg==NULL) { dbg->error("bruecke_t::finish_rd()","Bridge without way at(%s)!", gr->get_pos().get_str() ); weg = weg_t::alloc( desc->get_waytype() ); gr->neuen_weg_bauen( weg, 0, NULL ); weg->set_owner(player); } weg->set_max_speed(desc->get_topspeed()); // take ownership of way weg->set_owner(player); } player_t::add_maintenance(player, desc->get_maintenance(), desc->get_finance_waytype()); // with double heights may need to correct image on load (not all desc have double images) // at present only start images have 2 height variants, others to follow... if( gr->ist_karten_boden() ) { if( gr->get_grund_hang() != slope_t::flat ) { img = desc->get_start( gr->get_grund_hang() ); } } } // correct speed and maintenance void bruecke_t::cleanup( player_t *player2 ) { player_t *player = get_owner(); // change maintenance, reset max-speed and y-offset if( const grund_t *gr = welt->lookup(get_pos()) ) { if( weg_t *weg0 = gr->get_weg( desc->get_waytype() ) ) { weg0->set_max_speed( weg0->get_desc()->get_topspeed() ); // reset offsets weg0->set_xoff(0); weg0->set_yoff(0); if( weg_t *weg1 = gr->get_weg_nr(1) ) { weg1->set_xoff(0); weg1->set_yoff(0); } } } player_t::add_maintenance( player, -desc->get_maintenance(), desc->get_finance_waytype() ); player_t::book_construction_costs( player2, -desc->get_price(), get_pos().get_2d(), desc->get_waytype() ); } // rotated segment names static bridge_desc_t::img_t rotate90_img[24]= { bridge_desc_t::OW_Segment, bridge_desc_t::NS_Segment, bridge_desc_t::O_Start, bridge_desc_t::W_Start, bridge_desc_t::S_Start, bridge_desc_t::N_Start, bridge_desc_t::O_Ramp, bridge_desc_t::W_Ramp, bridge_desc_t::S_Ramp, bridge_desc_t::N_Ramp, bridge_desc_t::OW_Pillar, bridge_desc_t::NS_Pillar, bridge_desc_t::OW_Segment2, bridge_desc_t::NS_Segment2, bridge_desc_t::O_Start2, bridge_desc_t::W_Start2, bridge_desc_t::S_Start2, bridge_desc_t::N_Start2, bridge_desc_t::O_Ramp2, bridge_desc_t::W_Ramp2, bridge_desc_t::S_Ramp2, bridge_desc_t::N_Ramp2, bridge_desc_t::OW_Pillar2, bridge_desc_t::NS_Pillar2 }; void bruecke_t::rotate90() { set_yoff(0); obj_t::rotate90(); // the rotated image parameter is just one in front/back img = rotate90_img[img]; } // returns NULL, if removal is allowed // players can remove public owned ways const char *bruecke_t::get_removal_error(const player_t *player) { if (get_owner_nr()==PLAYER_PUBLIC_NR) { return NULL; } else { return obj_t::get_removal_error(player); } } simutrans-124.5/src/simutrans/obj/bruecke.h000066400000000000000000000031771520675460500207530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_BRUECKE_H #define OBJ_BRUECKE_H class karte_t; #include "../descriptor/bridge_desc.h" #include "simobj.h" /** * Bridges (visible) */ class bruecke_t : public obj_no_info_t { private: const bridge_desc_t *desc; bridge_desc_t::img_t img; protected: void rdwr(loadsave_t *file) OVERRIDE; public: bruecke_t(loadsave_t *file); bruecke_t(koord3d pos, player_t *player, const bridge_desc_t *desc, bridge_desc_t::img_t img); const char *get_name() const OVERRIDE {return "Bruecke";} typ get_typ() const OVERRIDE { return bruecke; } /** * waytype associated with this object */ waytype_t get_waytype() const OVERRIDE { return desc ? desc->get_waytype() : invalid_wt; } void set_desc(const bridge_desc_t *b) { desc = b; } const bridge_desc_t *get_desc() const { return desc; } virtual bool is_clipping_below_needed() const OVERRIDE; // we will always replace first way image image_id get_image() const OVERRIDE { return IMG_EMPTY; } image_id get_front_image() const OVERRIDE; void calc_image() OVERRIDE; /** * Called whenever the season or snowline height changes * return false and the obj_t will be deleted */ bool check_season(const bool calc_only_season_change) OVERRIDE { if( !calc_only_season_change ) { calc_image(); } return true; } // depends on snowline only void finish_rd() OVERRIDE; void cleanup(player_t *player) OVERRIDE; void rotate90() OVERRIDE; /** * @return NULL wenn OK, ansonsten eine Fehlermeldung */ const char *get_removal_error(const player_t *player) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/crossing.cc000066400000000000000000000132171520675460500213140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../world/simworld.h" #include "simobj.h" #include "../display/simimg.h" #include "../descriptor/crossing_desc.h" #include "../ground/grund.h" #include "../obj/way/weg.h" #include "../dataobj/loadsave.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../utils/cbuffer.h" #include "../player/simplay.h" #include "crossing.h" #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_mutex_t crossing_logic_mutex; static recursive_mutex_maker_t crossing_lm_maker(crossing_logic_mutex); #endif crossing_t::crossing_t(loadsave_t* const file) : obj_no_info_t() { image = foreground_image = IMG_EMPTY; logic = NULL; rdwr(file); } crossing_t::crossing_t(player_t* const player, koord3d const pos, crossing_desc_t const* const desc, uint8 const ns) : obj_no_info_t(pos) { this->ns = ns; this->desc = desc; logic = NULL; state = crossing_logic_t::CROSSING_INVALID; image = foreground_image = IMG_EMPTY; set_owner( player ); } crossing_t::~crossing_t() { if(logic) { crossing_logic_t *old_logic = logic; logic = NULL; old_logic->remove(this); } } void crossing_t::rotate90() { obj_t::rotate90(); ns ^= 1; } // changed state: mark dirty void crossing_t::state_changed() { mark_image_dirty( image, 0 ); mark_image_dirty( foreground_image, 0 ); calc_image(); } /** * Dient zur Neuberechnung des Bildes */ void crossing_t::calc_image() { #ifdef MULTI_THREAD pthread_mutex_lock( &crossing_logic_mutex ); #endif if( logic ) { state = logic->get_state(); } #ifdef MULTI_THREAD pthread_mutex_unlock( &crossing_logic_mutex ); #endif const bool snow_image = get_pos().z >= welt->get_snowline() || welt->get_climate( get_pos().get_2d() ) == arctic_climate; // recalc image each step ... const image_t *a = desc->get_foreground( ns, state!=crossing_logic_t::CROSSING_CLOSED, snow_image ); if( a==NULL && snow_image ) { // no snow image? take normal one a = desc->get_foreground( ns, state!=crossing_logic_t::CROSSING_CLOSED, 0); } foreground_image = a ? a->get_id() : IMG_EMPTY; const image_t *b = desc->get_background( ns, state!=crossing_logic_t::CROSSING_CLOSED, snow_image ); if (b==NULL && snow_image) { // no snow image? take normal one b = desc->get_background( ns, state!=crossing_logic_t::CROSSING_CLOSED, 0); } image = b ? b->get_id() : IMG_EMPTY; } void crossing_t::rdwr(loadsave_t *file) { xml_tag_t d( file, "crossing_t" ); obj_t::rdwr(file); // variables ... attention, logic now in crossing_logic_t state = logic==NULL ? crossing_logic_t::CROSSING_INVALID : logic->get_state(); file->rdwr_byte(state); state = clamp(state, 0, crossing_logic_t::NUM_CROSSING_STATES-1); file->rdwr_byte(ns); if(file->is_version_less(99, 16)) { uint32 ldummy=0; uint8 bdummy=0; file->rdwr_byte(bdummy); file->rdwr_long(ldummy); dbg->fatal("crossing_t::rdwr()","I should be never forced to load old style crossings!" ); } // which waytypes? uint8 w1, w2; sint32 speedlimit0 = 999; sint32 speedlimit1 = 999; if(file->is_saving()) { w1 = desc->get_waytype(0); w2 = desc->get_waytype(1); speedlimit0 = desc->get_maxspeed(0); speedlimit1 = desc->get_maxspeed(1); } file->rdwr_byte(w1); file->rdwr_byte(w2); if( file->is_version_atleast(110, 0) ) { file->rdwr_long( speedlimit0 ); } if( file->is_version_atleast(110, 1) ) { file->rdwr_long( speedlimit1 ); } if( file->is_loading() ) { desc = crossing_logic_t::get_crossing( (waytype_t)w1, (waytype_t)w2, speedlimit0, speedlimit1, welt->get_timeline_year_month()); if(desc==NULL) { dbg->warning("crossing_t::rdwr()","requested for waytypes %i and %i not available, try to load object without timeline", w1, w2 ); desc = crossing_logic_t::get_crossing( (waytype_t)w1, (waytype_t)w2, 0, 0, 0); } if(desc==NULL) { dbg->fatal("crossing_t::rdwr()","requested for waytypes %i and %i but nothing defined!", w1, w2 ); } } } void crossing_t::finish_rd() { grund_t *gr=welt->lookup(get_pos()); if(gr==NULL || !gr->hat_weg(desc->get_waytype(0)) || !gr->hat_weg(desc->get_waytype(1))) { dbg->error("crossing_t::finish_rd","way/ground missing at %i,%i => ignore", get_pos().x, get_pos().y ); } else { // try to find crossing that matches way max speed weg_t *w1=gr->get_weg(desc->get_waytype(0)); weg_t *w2=gr->get_weg(desc->get_waytype(1)); const crossing_desc_t *test = crossing_logic_t::get_crossing( desc->get_waytype(0), desc->get_waytype(1), w1->get_desc()->get_topspeed(), w2->get_desc()->get_topspeed(), welt->get_timeline_year_month()); if (test && test!=desc) { desc = test; } // after loading restore speedlimits w1->count_sign(); w2->count_sign(); ns = ribi_t::is_straight_ns(w2->get_ribi_unmasked()); if (!logic) { #ifdef MULTI_THREAD pthread_mutex_lock(&crossing_logic_mutex); #endif crossing_logic_t::add(this, static_cast(state)); logic->recalc_state(); #ifdef MULTI_THREAD pthread_mutex_unlock(&crossing_logic_mutex); #endif } } } // returns NULL, if removal is allowed // players can remove public owned ways const char *crossing_t::get_removal_error(const player_t *player) { if (get_owner_nr()==PLAYER_PUBLIC_NR) { return NULL; } else { return obj_t::get_removal_error(player); } } void crossing_t::info(cbuffer_t & buf) const { buf.append(translator::translate(get_name())); buf.append("\n"); logic->info(buf); buf.append("\n"); if (char const* const maker = get_desc()->get_copyright()) { buf.printf(translator::translate("Constructed by %s"), maker); buf.append("\n\n"); } } simutrans-124.5/src/simutrans/obj/crossing.h000066400000000000000000000054321520675460500211560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_CROSSING_H #define OBJ_CROSSING_H #include "simobj.h" #include "../simtypes.h" #include "../display/simimg.h" #include "../descriptor/crossing_desc.h" #include "../dataobj/crossing_logic.h" class vehicle_base_t; /** * road sign for traffic (one way minimum speed, traffic lights) */ class crossing_t : public obj_no_info_t { protected: image_id foreground_image, image; uint8 ns; // direction uint8 state; // only needed for loading ... crossing_logic_t *logic; const crossing_desc_t *desc; public: typ get_typ() const OVERRIDE { return crossing; } const char* get_name() const OVERRIDE { return "Kreuzung"; } /** * waytype associated with this object * for crossings: return invalid_wt since they do not need a way * if the way is deleted the crossing will be deleted, too */ waytype_t get_waytype() const OVERRIDE { return invalid_wt; } crossing_t(loadsave_t *file); crossing_t(player_t *player, koord3d pos, const crossing_desc_t *desc, uint8 ns = 0); /** * crossing logic is removed here */ virtual ~crossing_t(); void rotate90() OVERRIDE; const crossing_desc_t *get_desc() const { return desc; } /** * @param[out] buf string (only used for debug at the moment) */ void info(cbuffer_t & buf) const OVERRIDE; /** * @return NULL when OK, otherwise an error message */ const char *get_removal_error(const player_t *player) OVERRIDE; // returns true, if the crossing can be passed by this vehicle bool request_crossing( const vehicle_base_t *v ) { return logic->request_crossing( v ); } // adds to crossing void add_to_crossing( const vehicle_base_t *v ) { return logic->add_to_crossing( v ); } // removes the vehicle from the crossing void release_crossing( const vehicle_base_t *v ) { return logic->release_crossing( v ); } crossing_logic_t::crossing_state_t get_state() { return logic->get_state(); } // called from the logic directly void state_changed(); uint8 get_dir() { return ns; } void set_logic( crossing_logic_t *l ) { logic = l; } crossing_logic_t *get_logic() { return logic; } /** * Dient zur Neuberechnung des Bildes */ void calc_image() OVERRIDE; /** * Called whenever the season or snowline height changes * return false and the obj_t will be deleted */ bool check_season(const bool calc_only_season_change) OVERRIDE { if( !calc_only_season_change ) { calc_image(); } return true; } // depends on snowline only // changes the state of a traffic light image_id get_image() const OVERRIDE { return image; } /** * For the front image hiding vehicles */ image_id get_front_image() const OVERRIDE { return foreground_image; } void rdwr(loadsave_t *file) OVERRIDE; void finish_rd() OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/depot.cc000066400000000000000000000421771520675460500206070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "../simconvoi.h" #include "../gui/simwin.h" #include "../player/simplay.h" #include "../world/simworld.h" #include "../obj/depot.h" #include "../simline.h" #include "../simlinemgmt.h" #include "../tool/simmenu.h" #include "../gui/depotlist_frame.h" #include "../gui/depot_frame.h" #include "../gui/messagebox.h" #include "../gui/simwin.h" #include "../dataobj/schedule.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../builder/hausbauer.h" #include "gebaeude.h" #include "../builder/vehikelbauer.h" #include "../descriptor/building_desc.h" #include "../utils/cbuffer.h" #include "../vehicle/air_vehicle.h" #include "../vehicle/rail_vehicle.h" #include "../vehicle/road_vehicle.h" #include "../vehicle/water_vehicle.h" slist_tpl depot_t::all_depots; depot_t::depot_t(loadsave_t *file) : gebaeude_t() { rdwr(file); if(file->is_version_less(88, 2)) { set_yoff(0); } all_depots.append(this); selected_filter = VEHICLE_FILTER_RELEVANT; selected_sort_by = SORT_BY_DEFAULT; last_selected_line = linehandle_t(); command_pending = false; } depot_t::depot_t(koord3d pos, player_t *player, const building_tile_desc_t *t) : gebaeude_t(pos, player, t) { all_depots.append(this); selected_filter = VEHICLE_FILTER_RELEVANT; selected_sort_by = SORT_BY_DEFAULT; last_selected_line = linehandle_t(); command_pending = false; if (depotlist_frame_t* f = (depotlist_frame_t*)win_get_magic(magic_depotlist + player->get_player_nr())) { f->fill_list(); } } depot_t::~depot_t() { destroy_win((ptrdiff_t)this); all_depots.remove(this); } // finds the next/previous depot relative to the current position depot_t *depot_t::find_depot( koord3d start, const obj_t::typ depot_type, const player_t *player, bool forward) { depot_t *found = NULL; koord3d found_pos = forward ? koord3d(welt->get_size().x+1,welt->get_size().y+1,welt->get_groundwater()) : koord3d(-1,-1,-1); sint32 found_hash = forward ? 0x7FFFFFF : -1; sint32 start_hash = start.x + (8192 * start.y); for(depot_t* const d : all_depots) { if(d->get_typ()==depot_type && d->get_owner()==player) { // ok, the right type of depot const koord3d pos = d->get_pos(); if(pos==start) { // ignore the start point continue; } sint32 hash = (pos.x + (8192 * pos.y)); if(forward) { if(hash>start_hash || (hash==start_hash && pos.z>start.z)) { // found a suitable one if(hashfound_hash || (hash==found_hash && pos.z>found_pos.z)) { // which is closer ... found = d; found_pos = pos; found_hash = hash; } } } } } return found; } // again needed for server void depot_t::call_depot_tool( char tool, convoihandle_t cnv, const char *extra) { // call depot tool tool_t *tmp_tool = create_tool( TOOL_CHANGE_DEPOT | SIMPLE_TOOL ); cbuffer_t buf; buf.printf( "%c,%s,%hu", tool, get_pos().get_str(), cnv.get_id() ); if( extra ) { buf.append( "," ); buf.append( extra ); } tmp_tool->set_default_param(buf); welt->set_tool( tmp_tool, get_owner() ); // since init always returns false, it is safe to delete immediately delete tmp_tool; } /* this is called on two occasions: * first a convoy reaches the depot during its journey * second during loading a convoi is stored in a depot => only store it again */ void depot_t::convoi_arrived(convoihandle_t acnv, bool schedule_adjust) { if(schedule_adjust) { // here a regular convoi arrived for(unsigned i=0; iget_vehicle_count(); i++) { vehicle_t *v = acnv->get_vehicle(i); // reset vehicle data v->discard_cargo(); v->set_pos( koord3d::invalid ); v->set_leading( i==0 ); v->set_last( i+1==acnv->get_vehicle_count() ); } // Volker: remove depot from schedule schedule_t *schedule = acnv->get_schedule(); for( int i=0; iget_count(); i++ ) { // only if convoi found if(schedule->entries[i].pos==get_pos()) { schedule->set_current_stop( i ); schedule->remove(); acnv->set_schedule(schedule); break; } } } // this part stores the convoi in the depot convois.append(acnv); depot_frame_t *depot_frame = dynamic_cast(win_get_magic( (ptrdiff_t)this )); if(depot_frame) { depot_frame->action_triggered(NULL,(long int)0); } acnv->set_home_depot( get_pos() ); DBG_MESSAGE("depot_t::convoi_arrived()", "convoi %d, %p entered depot", acnv.get_id(), acnv.get_rep()); } void depot_t::show_info() { create_win( new depot_frame_t(this), w_info, (ptrdiff_t)this ); } vehicle_t* depot_t::buy_vehicle(const vehicle_desc_t* info) { DBG_DEBUG("depot_t::buy_vehicle()", info->get_name()); vehicle_t* veh = vehicle_builder_t::build(get_pos(), get_owner(), NULL, info ); DBG_DEBUG("depot_t::buy_vehicle()", "vehiclebauer %p", veh); vehicles.append(veh); DBG_DEBUG("depot_t::buy_vehicle()", "appended %i vehicle", vehicles.get_count()); return veh; } void depot_t::append_vehicle(convoihandle_t cnv, vehicle_t* veh, bool infront, bool local_execution) { /* create a new convoi, if necessary */ if (!cnv.is_bound()) { cnv = add_convoi( local_execution ); } veh->set_pos(get_pos()); cnv->add_vehicle(veh, infront); vehicles.remove(veh); } void depot_t::remove_vehicle(convoihandle_t cnv, int ipos) { vehicle_t* veh = cnv->remove_vehicle_at( ipos ); if( veh ) { vehicles.append( veh ); } } void depot_t::remove_vehicles_to_end(convoihandle_t cnv, int ipos) { while( vehicle_t* veh = cnv->remove_vehicle_at( ipos ) ) { vehicles.append( veh ); } } void depot_t::sell_vehicle(vehicle_t* veh) { vehicles.remove(veh); get_owner()->book_new_vehicle((sint64)veh->calc_sale_value(), get_pos().get_2d(), get_waytype() ); DBG_MESSAGE("depot_t::sell_vehicle()", "this=%p sells %p", this, veh); delete veh; } // returns the index of the oldest/newest vehicle in a list vehicle_t* depot_t::find_oldest_newest(const vehicle_desc_t* desc, bool old) { vehicle_t* found_veh = NULL; for(vehicle_t* const veh : vehicles ) { if( veh->get_desc() == desc ) { // joy of XOR, finally a line where I could use it! if( found_veh == NULL || old ^ (found_veh->get_purchase_time() > veh->get_purchase_time()) ) { found_veh = veh; } } } return found_veh; } convoihandle_t depot_t::add_convoi(bool local_execution) { convoi_t* new_cnv = new convoi_t(get_owner()); new_cnv->set_home_depot(get_pos()); convois.append(new_cnv->self); depot_frame_t *win = dynamic_cast(win_get_magic( (ptrdiff_t)this )); if( win && local_execution ) { win->activate_convoi( new_cnv->self ); } return new_cnv->self; } bool depot_t::check_obsolete_inventory(convoihandle_t cnv) { bool ok = true; slist_tpl veh_tmp_list; for( int i = 0; i < cnv->get_vehicle_count(); i++ ) { const vehicle_desc_t* const vb = cnv->get_vehicle(i)->get_desc(); if( vb ) { // search storage for matching vehicle vehicle_t* veh = NULL; for( slist_tpl::iterator i = vehicles.begin(); i != vehicles.end(); ++i ) { if( (*i)->get_desc() == vb ) { // found in storage, remove to temp list while searching for next vehicle veh = *i; vehicles.erase(i); veh_tmp_list.append( veh ); break; } } if( !veh ) { // need to buy new if( vb->is_retired( welt->get_timeline_year_month() ) ) { // is obsolete, return false ok = false; break; } } } } // put vehicles back into storage vehicles.append_list( veh_tmp_list ); return ok; } convoihandle_t depot_t::copy_convoi(convoihandle_t old_cnv, bool local_execution) { if( old_cnv.is_bound() && !convoihandle_t::is_exhausted() && old_cnv->get_vehicle_count() > 0 && get_waytype() == old_cnv->front()->get_desc()->get_waytype() ) { convoihandle_t new_cnv = add_convoi( false ); new_cnv->set_name(old_cnv->get_internal_name()); int vehicle_count = old_cnv->get_vehicle_count(); for (int i = 0; iget_vehicle(i)->get_desc(); if (info != NULL) { // search in depot for an existing vehicle of correct type vehicle_t* oldest_vehicle = get_oldest_vehicle(info); if (oldest_vehicle != NULL) { // append existing vehicle append_vehicle( new_cnv, oldest_vehicle, false, local_execution ); } else { // buy new vehicle vehicle_t* veh = vehicle_builder_t::build(get_pos(), get_owner(), NULL, info ); veh->set_pos(get_pos()); new_cnv->add_vehicle(veh, false); } } } if (old_cnv->get_line().is_bound()) { new_cnv->set_line(old_cnv->get_line()); new_cnv->get_schedule()->set_current_stop( old_cnv->get_schedule()->get_current_stop() ); } else { if (old_cnv->get_schedule() != NULL) { new_cnv->set_schedule(old_cnv->get_schedule()->copy()); } } // make this the current selected convoi depot_frame_t *win = dynamic_cast(win_get_magic( (ptrdiff_t)this )); if( win ) { if( local_execution ) { win->activate_convoi( new_cnv ); } else { win->update_data(); } } return new_cnv; } return convoihandle_t(); } bool depot_t::disassemble_convoi(convoihandle_t cnv, bool sell) { if( cnv.is_bound() ) { if( !sell ) { // store vehicles in depot while( vehicle_t* const v = cnv->remove_vehicle_at(0) ) { v->discard_cargo(); v->set_leading(false); v->set_last(false); vehicles.append(v); } } // remove from depot lists remove_convoi( cnv ); // and remove from welt cnv->self_destruct(); return true; } return false; } bool depot_t::disassemble_all_convois(bool sell) { uint32 i = 0; while( i < convois.get_count() ) { if( !disassemble_convoi( convois.at(i), sell ) ) { i++; } } return (convois.get_count() == 0); } bool depot_t::start_all_convoys() { uint32 i = 0; while( i < convois.get_count() ) { if( !start_convoi( convois.at(i), false ) ) { i++; } } return (convois.get_count() == 0); } // implementation in tool/simtool.cc bool scenario_check_convoy(karte_t *welt, player_t *player, convoihandle_t cnv, depot_t* depot, bool local); bool depot_t::start_convoi(convoihandle_t cnv, bool local_execution) { // close schedule window if not yet closed if(cnv.is_bound() && cnv->get_schedule()!=NULL) { if(!cnv->get_schedule()->is_editing_finished()) { // close the schedule window destroy_win((ptrdiff_t)cnv->get_schedule()); } } // convoi not in depot anymore, maybe user double-clicked on start-button if(!convois.is_contained(cnv)) { return false; } if (cnv.is_bound() && cnv->get_schedule() && !cnv->get_schedule()->empty()) { // if next schedule entry is this depot => advance to next entry const koord3d& cur_pos = cnv->get_schedule()->get_current_entry().pos; if (cur_pos == get_pos()) { cnv->get_schedule()->advance(); } // check if convoi is complete if(cnv->get_sum_power() == 0 || !cnv->pruefe_alle()) { if (local_execution) { create_win( new news_img("Diese Zusammenstellung kann nicht fahren!\n"), w_time_delete, magic_none); } } else if( !cnv->front()->calc_route(this->get_pos(), cur_pos, cnv->get_min_top_speed(), cnv->access_route()) ) { // no route to go ... if(local_execution) { static cbuffer_t buf; buf.clear(); buf.printf( translator::translate("Vehicle %s can't find a route!"), cnv->get_name() ); create_win( new news_img(buf), w_time_delete, magic_none); } } else if (!scenario_check_convoy(welt, get_owner(), cnv, this, local_execution) ) { // not allowed by scenario } else { // convoi can start now cnv->start(); // remove from depot lists remove_convoi( cnv ); return true; } } else { if (local_execution) { create_win( new news_img("Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n"), w_time_delete, magic_none); } if (!cnv.is_bound()) { dbg->warning("depot_t::start_convoi()","No convoi to start!"); } else if (!cnv->get_schedule()) { dbg->warning("depot_t::start_convoi()","No schedule for convoi."); } else if (!cnv->get_schedule()->is_editing_finished()) { dbg->warning("depot_t::start_convoi()","Schedule is incomplete/not finished"); } } return false; } void depot_t::remove_convoi( convoihandle_t cnv ) { depot_frame_t *win = dynamic_cast(win_get_magic( (ptrdiff_t)this )); if( win ) { // get currently selected convoi to restore selection if not removed int icnv = win->get_icnv(); convoihandle_t c = icnv > -1 ? get_convoi( icnv ) : convoihandle_t(); icnv = convois.index_of( cnv ); convois.remove( cnv ); if( c == cnv ) { // removing currently selected, select next in list or last instead c = !convois.empty() ? convois.at( min((uint32)icnv, convois.get_count() - 1) ) : convoihandle_t(); } win->activate_convoi( c ); } else { convois.remove( cnv ); } } // attention! this will not be used for railway depots! They will be loaded by hand ... void depot_t::rdwr(loadsave_t *file) { gebaeude_t::rdwr(file); rdwr_vehikel(vehicles, file); if (file->is_version_less(81, 33)) { // wagons are stored extra, just add them to vehicles assert(file->is_loading()); rdwr_vehikel(vehicles, file); } } void depot_t::rdwr_vehikel(slist_tpl &list, loadsave_t *file) { sint32 count; if(file->is_saving()) { count = list.get_count(); DBG_MESSAGE("depot_t::vehikel_laden()","saving %d vehicles",count); } file->rdwr_long(count); if(file->is_loading()) { // no house definition for this => use a normal hut ... if( this->get_tile()==NULL ) { dbg->error( "depot_t::rdwr()", "tile for depot not found!" ); set_tile( (*hausbauer_t::get_citybuilding_list( building_desc_t::city_res ))[0]->get_tile(0), true ); } DBG_MESSAGE("depot_t::vehikel_laden()","loading %d vehicles",count); for(int i=0; ird_obj_id(); vehicle_t *v = NULL; const bool first = false; const bool last = false; switch( typ ) { case old_automobil: case road_vehicle: v = new road_vehicle_t(file, first, last); break; case old_waggon: case rail_vehicle: v = new rail_vehicle_t(file, first, last); break; case old_schiff: case water_vehicle: v = new water_vehicle_t(file, first, last); break; case old_aircraft: case air_vehicle: v = new air_vehicle_t(file, first, last); break; case old_monorailwaggon: case monorail_vehicle: v = new monorail_vehicle_t(file, first, last); break; case maglev_vehicle: v = new maglev_vehicle_t(file, first, last); break; case narrowgauge_vehicle: v = new narrowgauge_vehicle_t(file, first, last); break; default: dbg->fatal("depot_t::vehikel_laden()","invalid vehicle type $%X", typ); } if(v->get_desc()) { DBG_MESSAGE("depot_t::vehikel_laden()","loaded %s", v->get_desc()->get_name()); list.append( v ); } else { dbg->error("depot_t::vehikel_laden()","vehicle has no desc => ignored"); } } } else { for(vehicle_t* const v : list) { file->wr_obj_id(v->get_typ()); v->rdwr_from_convoi(file); } } } /** * @return NULL when OK, otherwise an error message */ const char *depot_t::get_removal_error(const player_t *player) { if(player!=get_owner() && player!=welt->get_public_player()) { return "Das Feld gehoert\neinem anderen Spieler\n"; } if (!vehicles.empty()) { return "There are still vehicles\nstored in this depot!\n"; } for(convoihandle_t const c : convois) { if (c.is_bound() && c->get_vehicle_count() > 0) { return "There are still vehicles\nstored in this depot!\n"; } } return NULL; } slist_tpl const& depot_t::get_vehicle_type(uint8 sortkey) const { return vehicle_builder_t::get_info(get_waytype(), sortkey); } vehicle_t* depot_t::get_oldest_vehicle(const vehicle_desc_t* desc) { vehicle_t* oldest_veh = NULL; for(vehicle_t* const veh : get_vehicle_list()) { if (veh->get_desc() == desc) { if (oldest_veh == NULL || oldest_veh->get_purchase_time() > veh->get_purchase_time()) { oldest_veh = veh; } } } return oldest_veh; } void depot_t::update_win() { depot_frame_t *depot_frame = dynamic_cast(win_get_magic( (ptrdiff_t)this )); if(depot_frame) { depot_frame->build_vehicle_lists(); } } void depot_t::new_month() { // since vehicles may have become obsolete update_all_win(); } void depot_t::update_all_win() { for(depot_t* const d : all_depots) { d->update_win(); } } unsigned bahndepot_t::get_max_convoi_length() const { return welt->get_settings().get_max_rail_convoi_length(); } unsigned strassendepot_t::get_max_convoi_length() const { return welt->get_settings().get_max_road_convoi_length(); } unsigned schiffdepot_t::get_max_convoi_length() const { return welt->get_settings().get_max_ship_convoi_length(); } unsigned airdepot_t::get_max_convoi_length() const { return welt->get_settings().get_max_air_convoi_length(); } simutrans-124.5/src/simutrans/obj/depot.h000066400000000000000000000275751520675460500204560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_DEPOT_H #define OBJ_DEPOT_H #include "../tpl/slist_tpl.h" #include "gebaeude.h" #include "../convoihandle.h" #include "../simline.h" #define VEHICLE_FILTER_RELEVANT 1 #define VEHICLE_FILTER_GOODS_OFFSET 2 #define SORT_BY_DEFAULT 0 class karte_t; class vehicle_t; class depot_frame_t; class vehicle_desc_t; /** * In Depots werden Fahrzeuge gekauft, gewartet, verkauft und gelagert. */ class depot_t : public gebaeude_t { protected: /** * Reworked depot data! * * It can now contain any number of vehicles bought by the user (as before). * And it can hold any number of convois (before only one). * It is possible to have 0 convois in a depot, but an empty one shall be * automatically created, when necessary. * Convois are numbered 0...(n-1). * Vehicles are accessed by type. */ slist_tpl vehicles; slist_tpl convois; void rdwr_vehikel(slist_tpl &list, loadsave_t *file); static slist_tpl all_depots; public: // Last selected vehicle filter int selected_filter; // Last selected vehicle sort int selected_sort_by; // finds the next/previous depot relative to the current position static depot_t *find_depot( koord3d start, const obj_t::typ depot_type, const player_t *player, bool next); static const slist_tpl& get_depot_list() { return all_depots; } depot_t(loadsave_t *file); depot_t(koord3d pos, player_t *player, const building_tile_desc_t *t); virtual ~depot_t(); void call_depot_tool( char tool, convoihandle_t cnv, const char *extra ); virtual simline_t::linetype get_line_type() const = 0; void rdwr(loadsave_t *file) OVERRIDE; // text for the tabs is defaulted to the train names virtual const char * get_electrics_name() { return "Electrics_tab"; } virtual const char * get_passenger_name() { return "Pas_tab"; } virtual const char * get_zieher_name() { return "Lokomotive_tab"; } virtual const char * get_haenger_name() { return "Waggon_tab"; } vehicle_t* find_oldest_newest(const vehicle_desc_t* desc, bool old); /** * Access to convoi list. */ unsigned convoi_count() const { return convois.get_count(); } convoihandle_t get_convoi(unsigned int icnv) const { return icnv < convoi_count() ? convois.at(icnv) : convoihandle_t(); } convoihandle_t add_convoi(bool local_execution); slist_tpl const& get_convoy_list() { return convois; } // checks if cnv can be copied by using only stored vehicles and non-obsolete purchased vehicles bool check_obsolete_inventory(convoihandle_t cnv); /** * copies convoi and its schedule or line */ convoihandle_t copy_convoi(convoihandle_t old_cnv, bool local_execution); /** * Let convoi leave the depot. * If not possible, a message is displayed and the function returns false. * * @param cnv * @param local_execution if true, this method creates pop-ups in case of errors */ bool start_convoi(convoihandle_t cnv, bool local_execution); bool start_all_convoys(); /** * Destroy the convoi and put the vehicles in the vehicles list (sell==false), * or sell all immediately (sell==true). */ bool disassemble_convoi(convoihandle_t cnv, bool sell); bool disassemble_all_convois(bool sell); /** * Remove the convoi from the depot lists * updating depot gui frame as necessary */ void remove_convoi( convoihandle_t cnv ); /** * Remove vehicle from vehicle list and add it to the convoi. Two positions * are possible - in front or at the rear. */ void append_vehicle(convoihandle_t cnv, vehicle_t* veh, bool infront, bool local_execution); /** * Remove the vehicle at given position from the convoi and put it in the * vehicle list. */ void remove_vehicle(convoihandle_t cnv, int ipos); void remove_vehicles_to_end(convoihandle_t cnv, int ipos); /** * Access to vehicles not bound to a convoi. They are not ordered * in any way. */ slist_tpl const& get_vehicle_list() { return vehicles; } /** * A new vehicle is bought and added to the vehicle list. * The new vehicle in the list is returned. */ vehicle_t* buy_vehicle(const vehicle_desc_t* info); /** * Sell a vehicle from the vehicle list. */ void sell_vehicle(vehicle_t* veh); /** * Access to vehicle types which can be bought in the depot. */ slist_tpl const& get_vehicle_type(uint8 sortkey = SORT_BY_DEFAULT) const; /** * A convoi arrived at the depot and is added to the convoi list. * If schedule_adjust is true, the current depot is removed from schedule. */ void convoi_arrived(convoihandle_t cnv, bool schedule_adjust); /** * Parameters to determine layout and behaviour of the depot_frame_t. */ virtual int get_x_grid() const = 0; virtual int get_y_grid() const = 0; virtual int get_x_placement() const = 0; virtual int get_y_placement() const = 0; virtual unsigned get_max_convoi_length() const = 0; /** * Opens a new info window for that object. */ void show_info() OVERRIDE; /** * Can object be removed? * @return NULL when OK, otherwise an error message */ const char *get_removal_error(const player_t *player) OVERRIDE; /** * identifies the oldest vehicle of a certain type * @return NULL if no vehicle is found */ vehicle_t* get_oldest_vehicle(const vehicle_desc_t* desc); /** * Sets/gets the line that was selected the last time in the depot dialog */ void set_last_selected_line(const linehandle_t last_line) { last_selected_line=last_line; } linehandle_t get_last_selected_line() const { return last_selected_line; } /** * Will update all depot_frame_t (new vehicles!) */ static void update_all_win(); static void new_month(); /** * Update the depot_frame_t. */ void update_win(); private: linehandle_t last_selected_line; /** * Used to block new actions from depot frame gui when convois are being added to the depot. * Otherwise lag in multipler results in actions being performed on the wrong convoi. * Only works for a single client making changes in a depot at once. Multiple clients can still result in wrong convois being changed. */ bool command_pending; public: bool is_command_pending() const { return command_pending; } void clear_command_pending() { command_pending = false; } void set_command_pending() { command_pending = true; } }; /** * Depots for train vehicles. * @see depot_t * @see gebaeude_t */ class bahndepot_t : public depot_t { public: bahndepot_t(loadsave_t *file) : depot_t(file) {} bahndepot_t(koord3d pos,player_t *player, const building_tile_desc_t *t) : depot_t(pos,player,t) {} simline_t::linetype get_line_type() const OVERRIDE { return simline_t::trainline; } void rdwr_vehicles(loadsave_t *file) { depot_t::rdwr_vehikel(vehicles,file); } /** * Parameters to determine layout and behaviour of the depot_frame_t. */ int get_x_placement() const OVERRIDE {return -25; } int get_y_placement() const OVERRIDE {return -28; } int get_x_grid() const OVERRIDE { return 24; } int get_y_grid() const OVERRIDE { return 24; } unsigned get_max_convoi_length() const OVERRIDE; obj_t::typ get_typ() const OVERRIDE { return bahndepot; } const char *get_name() const OVERRIDE {return "Bahndepot"; } }; class tramdepot_t : public bahndepot_t { public: tramdepot_t(loadsave_t *file):bahndepot_t(file) {} tramdepot_t(koord3d pos,player_t *player, const building_tile_desc_t *t): bahndepot_t(pos,player,t) {} simline_t::linetype get_line_type() const OVERRIDE { return simline_t::tramline; } obj_t::typ get_typ() const OVERRIDE { return tramdepot; } const char *get_name() const OVERRIDE {return "Tramdepot"; } }; class monoraildepot_t : public bahndepot_t { public: monoraildepot_t(loadsave_t *file):bahndepot_t(file) {} monoraildepot_t(koord3d pos,player_t *player, const building_tile_desc_t *t): bahndepot_t(pos,player,t) {} simline_t::linetype get_line_type() const OVERRIDE { return simline_t::monorailline; } obj_t::typ get_typ() const OVERRIDE { return monoraildepot; } const char *get_name() const OVERRIDE {return "Monoraildepot"; } }; class maglevdepot_t : public bahndepot_t { public: maglevdepot_t(loadsave_t *file):bahndepot_t(file) {} maglevdepot_t(koord3d pos,player_t *player, const building_tile_desc_t *t): bahndepot_t(pos,player,t) {} simline_t::linetype get_line_type() const OVERRIDE { return simline_t::maglevline; } obj_t::typ get_typ() const OVERRIDE { return maglevdepot; } const char *get_name() const OVERRIDE {return "Maglevdepot"; } }; class narrowgaugedepot_t : public bahndepot_t { public: narrowgaugedepot_t(loadsave_t *file):bahndepot_t(file) {} narrowgaugedepot_t(koord3d pos,player_t *player, const building_tile_desc_t *t): bahndepot_t(pos,player,t) {} simline_t::linetype get_line_type() const OVERRIDE { return simline_t::narrowgaugeline; } obj_t::typ get_typ() const OVERRIDE { return narrowgaugedepot; } const char *get_name() const OVERRIDE {return "Narrowgaugedepot"; } }; /** * Depots for street vehicles * @see depot_t * @see gebaeude_t */ class strassendepot_t : public depot_t { protected: const char * get_passenger_name() OVERRIDE { return "Bus_tab"; } const char * get_electrics_name() OVERRIDE { return "TrolleyBus_tab"; } const char * get_zieher_name() OVERRIDE { return "LKW_tab"; } const char * get_haenger_name() OVERRIDE { return "Anhaenger_tab"; } public: strassendepot_t(loadsave_t *file) : depot_t(file) {} strassendepot_t(koord3d pos,player_t *player, const building_tile_desc_t *t) : depot_t(pos,player,t) {} simline_t::linetype get_line_type() const OVERRIDE { return simline_t::truckline; } /** * Parameters to determine layout and behaviour of the depot_frame_t. */ int get_x_placement() const OVERRIDE { return -20; } int get_y_placement() const OVERRIDE { return -25; } int get_x_grid() const OVERRIDE { return 24; } int get_y_grid() const OVERRIDE { return 24; } unsigned get_max_convoi_length() const OVERRIDE; obj_t::typ get_typ() const OVERRIDE { return strassendepot; } const char *get_name() const OVERRIDE {return "Strassendepot";} }; /** * Depots for ships * @see depot_t * @see gebaeude_t */ class schiffdepot_t : public depot_t { protected: const char * get_passenger_name() OVERRIDE { return "Ferry_tab"; } const char * get_zieher_name() OVERRIDE { return "Schiff_tab"; } const char * get_haenger_name() OVERRIDE { return "Schleppkahn_tab"; } public: schiffdepot_t(loadsave_t *file) : depot_t(file) {} schiffdepot_t(koord3d pos, player_t *player, const building_tile_desc_t *t) : depot_t(pos,player,t) {} simline_t::linetype get_line_type() const OVERRIDE { return simline_t::shipline; } /** * Parameters to determine layout and behaviour of the depot_frame_t. */ int get_x_placement() const OVERRIDE { return -1; } int get_y_placement() const OVERRIDE { return -11; } int get_x_grid() const OVERRIDE { return 60; } int get_y_grid() const OVERRIDE { return 46; } unsigned get_max_convoi_length() const OVERRIDE; obj_t::typ get_typ() const OVERRIDE { return schiffdepot; } const char *get_name() const OVERRIDE {return "Schiffdepot";} }; /** * Depots for aircrafts */ class airdepot_t : public depot_t { protected: const char * get_zieher_name() OVERRIDE { return "aircraft_tab"; } const char * get_passenger_name() OVERRIDE { return "Flug_tab"; } public: airdepot_t(loadsave_t *file) : depot_t(file) {} airdepot_t(koord3d pos,player_t *player, const building_tile_desc_t *t) : depot_t(pos,player,t) {} simline_t::linetype get_line_type() const OVERRIDE { return simline_t::airline; } /** * Parameters to determine layout and behaviour of the depot_frame_t. */ int get_x_placement() const OVERRIDE {return -10; } int get_y_placement() const OVERRIDE {return -23; } int get_x_grid() const OVERRIDE { return 36; } int get_y_grid() const OVERRIDE { return 36; } unsigned get_max_convoi_length() const OVERRIDE; obj_t::typ get_typ() const OVERRIDE { return airdepot; } const char *get_name() const OVERRIDE {return "Hangar";} }; #endif simutrans-124.5/src/simutrans/obj/dummy.h000066400000000000000000000011521520675460500204550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_DUMMY_H #define OBJ_DUMMY_H #include "simobj.h" #include "../display/simimg.h" /** * A dummy type for old things, which are now ignored */ class dummy_obj_t : public obj_t { public: dummy_obj_t(loadsave_t* file) : obj_t() { rdwr(file); // do not remove from this position, since there will be nothing obj_t::set_flag(obj_t::not_on_map); } typ get_typ() const OVERRIDE { return obj_t::undefined; } image_id get_image() const OVERRIDE { return IMG_EMPTY; } }; #endif simutrans-124.5/src/simutrans/obj/field.cc000066400000000000000000000041351520675460500205470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simworld.h" #include "simobj.h" #include "../simfab.h" #include "../display/simimg.h" #include "../ground/grund.h" #include "../dataobj/loadsave.h" #include "../player/simplay.h" #include "field.h" field_t::field_t(koord3d p, player_t *player, const field_class_desc_t *desc, fabrik_t *fab) : obj_t() { this->desc = desc; this->fab = fab; set_owner( player ); p.z = welt->max_hgt(p.get_2d()); set_pos( p ); } field_t::~field_t() { // mark field image area as dirty for removal mark_image_dirty( get_image(), 0 ); fab->remove_field_at( get_pos().get_2d() ); } const char *field_t::get_removal_error(const player_t *p) { if (p == NULL) { // not allowed by city growth return "Der Besitzer erlaubt das Entfernen nicht"; } // we allow removal, if there is less than return (fab->get_field_count() > fab->get_desc()->get_field_group()->get_min_fields()) ? NULL : "Not enough fields would remain."; } // remove costs void field_t::cleanup(player_t *player) { player_t::book_construction_costs(player, welt->get_settings().cst_multiply_remove_field, get_pos().get_2d(), ignore_wt); mark_image_dirty( get_image(), 0 ); } // return the right month graphic for factories image_id field_t::get_image() const { const skin_desc_t *s=desc->get_images(); uint16 count=s->get_count() - desc->has_snow_image(); if( desc->has_snow_image() && (get_pos().z >= welt->get_snowline() || welt->get_climate( get_pos().get_2d() ) == arctic_climate) ) { // last images will be shown above snowline return s->get_image_id(count); } else { // resolution 1/8th month (0..95) const uint32 yearsteps = (welt->get_current_month()%12)*8 + ((welt->get_ticks()>>(welt->ticks_per_world_month_shift-3))&7) + 1; const image_id image = s->get_image_id( (count*yearsteps-1)/96 ); if((count*yearsteps-1)%96open_info_window(); } simutrans-124.5/src/simutrans/obj/field.h000066400000000000000000000015641520675460500204140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_FIELD_H #define OBJ_FIELD_H #include "simobj.h" #include "../display/simimg.h" class field_class_desc_t; class fabrik_t; class field_t : public obj_t { fabrik_t *fab; const field_class_desc_t *desc; public: field_t(const koord3d pos, player_t *player, const field_class_desc_t *desc, fabrik_t *fab); virtual ~field_t(); const char* get_name() const OVERRIDE { return "Field"; } typ get_typ() const OVERRIDE { return obj_t::field; } image_id get_image() const OVERRIDE; /// @copydoc obj_t::show_info void show_info() OVERRIDE; /** * @return NULL when OK, otherwise an error message */ const char *get_removal_error(const player_t *) OVERRIDE; void cleanup(player_t *player) OVERRIDE; fabrik_t* get_factory() const { return fab; } }; #endif simutrans-124.5/src/simutrans/obj/gebaeude.cc000066400000000000000000001034111520675460500212220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include // repair multitile building which have lost thier orientation due to replacements // may not work properly with multithread builds //#define REPAIR_MULTITILE_BUILD #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_mutex_t sync_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t add_to_city_mutex = PTHREAD_MUTEX_INITIALIZER; #endif #include "../builder/hausbauer.h" #include "../gui/headquarter_info.h" #include "../world/simworld.h" #include "simobj.h" #include "../simconst.h" #include "../simfab.h" #include "../display/simimg.h" #include "../display/simgraph.h" #include "../simhalt.h" #include "../gui/simwin.h" #include "../world/simcity.h" #include "../player/simplay.h" #include "../simdebug.h" #include "../simintr.h" #include "../simskin.h" #include "../simachievements.h" #include "../ground/grund.h" #include "../descriptor/building_desc.h" #include "../descriptor/intro_dates.h" #include "../descriptor/ground_desc.h" #include "../utils/cbuffer.h" #include "../utils/simrandom.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/settings.h" #include "../dataobj/environment.h" #include "../dataobj/pakset_manager.h" #include "../gui/obj_info.h" #include "gebaeude.h" /** * Initializes all variables with safe, usable values */ void gebaeude_t::init() { tile = NULL; anim_time = 0; sync = false; zeige_baugrube = false; is_factory = false; season = 0; background_animated = false; remove_ground = true; anim_frame = 0; // insta_zeit = 0; // init in set_tile() ptr.fab = NULL; } gebaeude_t::gebaeude_t() : obj_t() { init(); } gebaeude_t::gebaeude_t(loadsave_t *file) : obj_t() { init(); rdwr(file); if(file->is_version_less(88, 2)) { set_yoff(0); } if(tile && tile->get_phases()>1) { welt->sync_buildings.add( this ); sync = true; } } gebaeude_t::gebaeude_t(koord3d pos, player_t *player, const building_tile_desc_t *t) : obj_t(pos) { set_owner( player ); init(); if(t) { set_tile(t,true); // this will set init time etc. player_t::add_maintenance(get_owner(), tile->get_desc()->get_maintenance(welt), tile->get_desc()->get_finance_waytype() ); } // get correct y offset for bridges grund_t *gr=welt->lookup(pos); if(gr && gr->get_weg_hang()!=gr->get_grund_hang()) { set_yoff( -gr->get_weg_yoff() ); } } /** * Destructor. Removes this from the list of sync objects if necessary. */ gebaeude_t::~gebaeude_t() { if(welt->is_destroying()) { return; // avoid book-keeping } if(sync) { sync = false; welt->sync_buildings.remove(this); } assert(get_flag(obj_t::not_on_map) || get_stadt() == NULL); // tiles might be invalid, if no description is found during loading if(tile && tile->get_desc() && tile->get_desc()->is_attraction()) { welt->remove_attraction(this); } if(tile) { player_t::add_maintenance(get_owner(), -tile->get_desc()->get_maintenance(welt), tile->get_desc()->get_finance_waytype()); } } void gebaeude_t::rotate90() { obj_t::rotate90(); // must or can rotate? const building_desc_t* const building_desc = tile->get_desc(); if (building_desc->get_all_layouts() > 1 || building_desc->get_x() * building_desc->get_y() > 1) { uint8 layout = tile->get_layout(); koord new_offset = tile->get_offset(); if(building_desc->get_type() == building_desc_t::unknown || building_desc->get_all_layouts()<=4) { layout = ((layout+3) % building_desc->get_all_layouts() & 3); } else if( building_desc->get_all_layouts()==8 && building_desc->get_type() >= building_desc_t::city_res ) { // eight layout city building layout = (layout & 4) + ((layout+3) & 3); } else { // 8 & 16 tile lyoutout for stations static uint8 layout_rotate[16] = { 1, 8, 5, 10, 3, 12, 7, 14, 9, 0, 13, 2, 11, 4, 15, 6 }; layout = layout_rotate[layout] % building_desc->get_all_layouts(); } // have to rotate the tiles :( if( !building_desc->can_rotate() && building_desc->get_all_layouts() == 1 ) { if ((welt->get_settings().get_rotation() & 1) == 0) { // rotate 180 degree new_offset = koord(building_desc->get_x() - 1 - new_offset.x, building_desc->get_y() - 1 - new_offset.y); } // do nothing here, since we cannot fix it properly } else { // rotate on ... new_offset = koord(building_desc->get_y(tile->get_layout()) - 1 - new_offset.y, new_offset.x); } // such a tile exist? if( building_desc->get_x(layout) > new_offset.x && building_desc->get_y(layout) > new_offset.y ) { const building_tile_desc_t* const new_tile = building_desc->get_tile(layout, new_offset.x, new_offset.y); // add new tile: but make them old (no construction) uint32 old_insta_zeit = insta_zeit; set_tile( new_tile, false ); insta_zeit = old_insta_zeit; if( building_desc->get_type() != building_desc_t::dock && !tile->has_image() ) { // may have a rotation, that is not recoverable if( !is_factory && new_offset!=koord(0,0) ) { welt->set_nosave_warning(); } if( is_factory ) { // there are factories with a broken tile // => this map rotation cannot be reloaded! welt->set_nosave(); } } } else { welt->set_nosave(); } } } /** sets the corresponding pointer to a factory */ void gebaeude_t::set_fab(fabrik_t *fd) { // sets the pointer in non-zero if(fd) { if(!is_factory && ptr.stadt!=NULL) { dbg->fatal("gebaeude_t::set_fab()","building already bound to city!"); } is_factory = true; ptr.fab = fd; } else if(is_factory) { ptr.fab = NULL; } } /** sets the corresponding city */ void gebaeude_t::set_stadt(stadt_t *s) { if(is_factory && ptr.fab!=NULL) { dbg->fatal("gebaeude_t::set_stadt()","building at (%s) already bound to factory!", get_pos().get_str() ); } // sets the pointer in non-zero is_factory = false; ptr.stadt = s; } /* make this building without construction */ void gebaeude_t::add_alter(uint32 a) { insta_zeit -= min(a,insta_zeit); } void gebaeude_t::set_tile( const building_tile_desc_t *new_tile, bool start_with_construction ) { insta_zeit = welt->get_ticks(); if(!zeige_baugrube && tile!=NULL) { // mark old tile dirty mark_images_dirty(); } zeige_baugrube = !new_tile->get_desc()->no_construction_pit() && start_with_construction; if(sync) { if( new_tile->get_phases()<=1 && !zeige_baugrube ) { // need to stop animation #ifdef MULTI_THREAD pthread_mutex_lock( &sync_mutex ); #endif welt->sync_buildings.remove(this); sync = false; anim_frame = 0; #ifdef MULTI_THREAD pthread_mutex_unlock( &sync_mutex ); #endif } } else if( (new_tile->get_phases()>1 && (!is_factory || get_fabrik()->is_currently_producing()) ) || zeige_baugrube ) { // needs now animation #ifdef MULTI_THREAD pthread_mutex_lock( &sync_mutex ); #endif anim_frame = sim_async_rand( new_tile->get_phases() ); anim_time = 0; welt->sync_buildings.add(this); sync = true; #ifdef MULTI_THREAD pthread_mutex_unlock( &sync_mutex ); #endif } tile = new_tile; remove_ground = tile->has_image() && !tile->get_desc()->needs_ground(); set_flag(obj_t::dirty); } sync_result gebaeude_t::sync_step(uint32 delta_t) { if( zeige_baugrube ) { // still under construction? if( welt->get_ticks() - insta_zeit > 5000 ) { set_flag( obj_t::dirty ); mark_image_dirty( get_image(), 0 ); zeige_baugrube = false; if( tile->get_phases() <= 1 ) { sync = false; return SYNC_REMOVE; } } } else { if( !is_factory || get_fabrik()->is_currently_producing() ) { // normal animated building anim_time += delta_t; if( anim_time > tile->get_desc()->get_animation_time() ) { anim_time -= tile->get_desc()->get_animation_time(); // old positions need redraw if( background_animated ) { set_flag( obj_t::dirty ); mark_images_dirty(); } else { // try foreground image_id image = tile->get_foreground( anim_frame, season ); mark_image_dirty( image, 0 ); } anim_frame++; if( anim_frame >= tile->get_phases() ) { anim_frame = 0; } if( !background_animated ) { // next phase must be marked dirty too ... image_id image = tile->get_foreground( anim_frame, season ); mark_image_dirty( image, 0 ); } } } } return SYNC_OK; } void gebaeude_t::calc_image() { grund_t *gr = welt->lookup( get_pos() ); // need no ground? if( remove_ground && gr->get_typ() == grund_t::fundament ) { gr->set_image( IMG_EMPTY ); } static uint8 effective_season[][5] = { {0,0,0,0,0}, {0,0,0,0,1}, {0,0,0,0,1}, {0,1,2,3,2}, {0,1,2,3,4} }; // season image lookup from [number of images] and [actual season/snow] if( (gr->ist_tunnel() && !gr->ist_karten_boden()) || tile->get_seasons() < 2 ) { season = 0; } else if( get_pos().z - (get_yoff() / TILE_HEIGHT_STEP) >= welt->get_snowline() || welt->get_climate( get_pos().get_2d() ) == arctic_climate ) { // snowy winter graphics season = effective_season[tile->get_seasons() - 1][4]; } else if( get_pos().z - (get_yoff() / TILE_HEIGHT_STEP) >= welt->get_snowline() - 1 && welt->get_season() == 0 ) { // snowline crossing in summer // so at least some weeks spring/autumn season = effective_season[tile->get_seasons() - 1][welt->get_last_month() <= 5 ? 3 : 1]; } else { season = effective_season[tile->get_seasons() - 1][welt->get_season()]; } background_animated = tile->is_background_animated( season ); } image_id gebaeude_t::get_image() const { if(env_t::hide_buildings!=0 && tile->has_image()) { // opaque houses if (is_city_building()) { if (skinverwaltung_t::construction_site->get_count() == 1) { // only one kind of construction site? return skinverwaltung_t::construction_site->get_image_id(0); } // 6 is special building, 7-9 is res com ind (our type) return skinverwaltung_t::construction_site->get_count() > 7 ? skinverwaltung_t::construction_site->get_image_id(tile->get_desc()->get_type() - building_desc_t::city_res + 7) : skinverwaltung_t::construction_site->get_image_id(0); } else if( env_t::hide_buildings == env_t::ALL_HIDDEN_BUILDING && tile->get_desc()->get_type() < building_desc_t::others ) { // hide with transparency or tile without information if(env_t::hide_with_transparency) { if(tile->get_desc()->get_type() == building_desc_t::factory && ptr.fab->get_desc()->get_placement() == factory_desc_t::Water) { // no ground tiles for water things return IMG_EMPTY; } return skinverwaltung_t::fussweg->get_image_id(0); } // only one kind of construction site if (skinverwaltung_t::construction_site->get_count() == 1) { return skinverwaltung_t::construction_site->get_image_id(0); } // 6 is special building, 7-9 is res com ind (handled above) => we only go up to 6 uint16 kind = tile->get_desc()->get_type() > 6 ? 6 : tile->get_desc()->get_type(); return skinverwaltung_t::construction_site->get_image_id( kind ); } } if( zeige_baugrube ) { return skinverwaltung_t::construction_site->get_image_id(0); } else { return tile->get_background( anim_frame, 0, season ); } } image_id gebaeude_t::get_outline_image() const { if(env_t::hide_buildings!=0 && env_t::hide_with_transparency && !zeige_baugrube) { // opaque houses return tile->get_background( anim_frame, 0, season ); } return IMG_EMPTY; } /* gives outline colour and plots background tile if needed for transparent view */ FLAGGED_PIXVAL gebaeude_t::get_outline_colour() const { uint8 colours[] = { COL_BLACK, COL_YELLOW, COL_YELLOW, COL_PURPLE, COL_RED, COL_GREEN }; FLAGGED_PIXVAL disp_colour = 0; if(env_t::hide_buildings!=env_t::NOT_HIDE) { if(is_city_building()) { disp_colour = gfx->palette_lookup(colours[0]) | TRANSPARENT50_FLAG | OUTLINE_FLAG; } else if (env_t::hide_buildings == env_t::ALL_HIDDEN_BUILDING && tile->get_desc()->get_type() < building_desc_t::others) { // special building disp_colour = gfx->palette_lookup(colours[tile->get_desc()->get_type()]) | TRANSPARENT50_FLAG | OUTLINE_FLAG; } } return disp_colour; } void gebaeude_t::display(int xpos, int ypos CLIP_NUM_DEF) const { const bool is_dirty = get_flag(obj_t::dirty); uint8 owner_n = get_owner_nr(); const int raster_width = gfx->get_current_tile_raster_width(); ypos += tile_raster_scale_y(get_yoff(), raster_width); // if there is a slope below if (env_t::hide_buildings != 0 && env_t::hide_with_transparency && !zeige_baugrube) { // transparent building image_id img = tile->get_background(anim_frame, 0, season); if (img == IMG_EMPTY) { return; } uint8 colours[] = { COL_BLACK, COL_YELLOW, COL_YELLOW, COL_PURPLE, COL_RED, COL_GREEN }; FLAGGED_PIXVAL disp_colour = 0; if (is_city_building()) { disp_colour = gfx->palette_lookup(colours[0]) | TRANSPARENT50_FLAG | OUTLINE_FLAG; } else if (env_t::hide_buildings == env_t::ALL_HIDDEN_BUILDING && tile->get_desc()->get_type() < building_desc_t::others) { // special building disp_colour = gfx->palette_lookup(colours[tile->get_desc()->get_type()]) | TRANSPARENT50_FLAG | OUTLINE_FLAG; } if (image_id ground = get_image()) { if (owner_n != PLAYER_UNOWNED) { if (obj_t::show_owner) { gfx->draw_blend(ground, xpos, ypos, owner_n, gfx->palette_lookup(welt->get_player(owner_n)->get_player_color1() + 2) | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } else { gfx->draw_color(ground, xpos, ypos, owner_n, true, is_dirty CLIP_NUM_PAR); } } else { gfx->draw_normal(ground, xpos, ypos, 0, true, is_dirty CLIP_NUM_PAR); } } if (TRANSPARENT_FLAGS & disp_colour) { // only transparent outline gfx->draw_blend(get_outline_image(), xpos, ypos, owner_n, disp_colour, 0, is_dirty CLIP_NUM_PAR); } else if (obj_t::get_flag(highlight)) { // highlight this tile gfx->draw_blend(get_image(), xpos, ypos, owner_n, SYSCOL_OBJECT_HIGHLIGHT | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } // finish return; } // not hidden image_id image = get_image(); if (image != IMG_EMPTY) { const bool is_dirty = get_flag(obj_t::dirty); const int start_ypos = ypos; for (int j = 0; image != IMG_EMPTY; ) { if (owner_n != PLAYER_UNOWNED) { if (obj_t::show_owner) { gfx->draw_blend(image, xpos, ypos, owner_n, gfx->palette_lookup(welt->get_player(owner_n)->get_player_color1() + 2) | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } else { gfx->draw_color(image, xpos, ypos, owner_n, true, is_dirty CLIP_NUM_PAR); } } else { gfx->draw_normal(image, xpos, ypos, 0, true, is_dirty CLIP_NUM_PAR); } if (obj_t::get_flag(highlight)) { // highlight this tile gfx->draw_blend(image, xpos, start_ypos, owner_n, SYSCOL_OBJECT_HIGHLIGHT | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } // this obj has another image on top (e.g. skyscraper) ypos -= raster_width; if (zeige_baugrube || env_t::hide_buildings) { // finish return; } image = tile->get_background(anim_frame, ++j, season); } } } image_id gebaeude_t::get_front_image() const { if(zeige_baugrube) { return IMG_EMPTY; } if (env_t::hide_buildings != 0 && (is_city_building() || (env_t::hide_buildings == env_t::ALL_HIDDEN_BUILDING && tile->get_desc()->get_type() < building_desc_t::others))) { return IMG_EMPTY; } else { // Show depots, station buildings etc. return tile->get_foreground( anim_frame, season ); } } /** * calculate the passenger level as function of the city size (if there) */ int gebaeude_t::get_passagier_level() const { koord dim = tile->get_desc()->get_size(); sint32 pax = tile->get_desc()->get_level(); if( !is_factory && ptr.stadt != NULL ) { // belongs to a city ... return ((pax + 6) >> 2) * welt->get_settings().get_passenger_factor() / 16; } return pax*dim.x*dim.y; } int gebaeude_t::get_mail_level() const { koord dim = tile->get_desc()->get_size(); sint32 mail = tile->get_desc()->get_mail_level(); if( !is_factory && ptr.stadt != NULL ) { return ((mail + 5) >> 2) * welt->get_settings().get_passenger_factor() / 16; } return mail*dim.x*dim.y; } /** * @return eigener Name oder Name der Fabrik falls Teil einer Fabrik */ const char *gebaeude_t::get_name() const { if(is_factory && ptr.fab) { return ptr.fab->get_name(); } switch(tile->get_desc()->get_type()) { case building_desc_t::attraction_city: return "Besonderes Gebaeude"; case building_desc_t::attraction_land: return "Sehenswuerdigkeit"; case building_desc_t::monument: return "Denkmal"; case building_desc_t::townhall: return "Rathaus"; default: break; } return "Gebaeude"; } /** * waytype associated with this object */ waytype_t gebaeude_t::get_waytype() const { const building_desc_t *desc = tile->get_desc(); waytype_t wt = invalid_wt; const building_desc_t::btype type = tile->get_desc()->get_type(); if (type == building_desc_t::depot || type == building_desc_t::generic_stop || type == building_desc_t::generic_extension) { wt = (waytype_t)desc->get_extra(); } return wt; } bool gebaeude_t::is_townhall() const { return tile->get_desc()->is_townhall(); } bool gebaeude_t::is_monument() const { return tile->get_desc()->get_type() == building_desc_t::monument; } bool gebaeude_t::is_headquarter() const { return tile->get_desc()->is_headquarters(); } bool gebaeude_t::is_city_building() const { return tile->get_desc()->is_city_building(); } uint32 gebaeude_t::get_tile_list( vector_tpl &list ) const { koord size = get_tile()->get_desc()->get_size( get_tile()->get_layout() ); const koord3d pos0 = get_pos() - get_tile()->get_offset(); // get origin koord k; list.clear(); // add all tiles for( k.y = 0; k.y < size.y; k.y++ ) { for( k.x = 0; k.x < size.x; k.x++ ) { if( grund_t* gr = welt->lookup( pos0+k ) ) { if( gebaeude_t* const add_gb = gr->find() ) { if( is_same_building( add_gb ) ) { list.append( gr ); } } } } } return list.get_count(); } void gebaeude_t::show_info() { if(get_fabrik()) { ptr.fab->open_info_window(); return; } const uint32 old_count = win_get_open_count(); const bool special = is_headquarter() || is_townhall(); if(is_headquarter()) { create_win( new headquarter_info_t(get_owner()), w_info, magic_headquarter+get_owner()->get_player_nr() ); } else if (is_townhall()) { ptr.stadt->open_info_window(); } if(!tile->get_desc()->no_info_window()) { if(!special || (env_t::townhall_info && old_count==win_get_open_count()) ) { // iterate over all places to check if there is already an open window gebaeude_t * first_tile = NULL; static vector_tpl gb_tiles; get_tile_list( gb_tiles ); for(grund_t* gr : gb_tiles ) { // no need for check, we jsut did before ... gebaeude_t* gb = gr->find(); if( win_get_magic( (ptrdiff_t)gb ) ) { // already open return; } if( first_tile==0 ) { first_tile = gb; } } // open info window for the first tile of our building (not relying on presence of (0,0) tile) first_tile->obj_t::show_info(); simachievements_t::check_query_ach(tile->get_desc()->get_name()); } } } // returns true, if there is a halt serving at least one tile bool gebaeude_t::is_within_players_network(const player_t* player) const { if( get_fabrik() ) { return ptr.fab->is_within_players_network( player ); } if( is_townhall() ) { ptr.stadt->is_within_players_network( player ); } // normal building: iterate over all tiles vector_tpl gb_tiles; get_tile_list( gb_tiles ); for(grund_t* gr : gb_tiles ) { // no need for check, we jsut did before ... if( const planquadrat_t* plan = welt->access( gr->get_pos().get_2d()) ) { if( plan->get_haltlist_count() > 0 ) { const halthandle_t* const halt_list = plan->get_haltlist(); for( int h = 0; h < plan->get_haltlist_count(); h++ ) { if( halt_list[h].is_bound() && (halt_list[h]->get_pax_enabled() || halt_list[h]->get_mail_enabled()) && halt_list[h]->has_available_network( player ) ) { return true; } } } } } return false; } bool gebaeude_t::is_same_building(const gebaeude_t* other) const { if (other) { const building_tile_desc_t* otile = other->get_tile(); // same descriptor and house tile if (get_tile()->get_desc() == otile->get_desc() && get_tile()->get_layout() == otile->get_layout() // same position of (0,0) tile && (get_pos() - get_tile()->get_offset() == other->get_pos() - otile->get_offset())) { return true; } } return false; } gebaeude_t* gebaeude_t::get_first_tile() { koord size = get_tile()->get_desc()->get_size( get_tile()->get_layout() ); const koord3d pos0 = get_pos() - get_tile()->get_offset(); // get origin koord k; // add all tiles for( k.y = 0; k.y < size.y; k.y++ ) { for( k.x = 0; k.x < size.x; k.x++ ) { if( grund_t* gr = welt->lookup( pos0+k ) ) { if( gebaeude_t* const add_gb = obj_cast(gr->first_no_way_obj()) ) { if( is_same_building( add_gb ) ) { return add_gb; } } } } } return this; } void gebaeude_t::info(cbuffer_t & buf) const { const char* name=tile->get_desc()->get_name(); if(is_factory && ptr.fab != NULL) { buf.append((char *)0); } else if(zeige_baugrube) { buf.append(translator::translate("Baustelle")); buf.append("\n"); } else if(name) { const char *trans_name = translator::translate(name); if(trans_name == name) { // no translaion here switch(tile->get_desc()->get_type()) { case building_desc_t::city_res: buf.append(translator::translate("residential house")); break; case building_desc_t::city_ind: buf.append(translator::translate("industrial building")); break; case building_desc_t::city_com: buf.append(translator::translate("shops and stores")); break; default: buf.append(name); break; } } else { // since the format changed, we remove all but double newlines char *text = new char[strlen(trans_name)+1]; char *dest = text; const char *src = trans_name; while( *src!=0 ) { *dest = *src; if(src[0]=='\n') { if(src[1]=='\n') { src ++; dest++; *dest = '\n'; } else { *dest = ' '; } } src ++; dest ++; } // remove double line breaks at the end *dest = 0; while( dest>text && *--dest=='\n' ) { *dest = 0; } buf.append(text); delete [] text; } buf.append( "\n\n" ); obj_t::info(buf); // append owner // belongs to which city? if( !is_factory && ptr.stadt != NULL ) { buf.printf(translator::translate("Town: %s\n"), ptr.stadt->get_name()); } if( !get_tile()->get_desc()->is_transport_building() ) { buf.printf("%s: %d\n", translator::translate("Passagierrate"), get_passagier_level()); buf.printf("%s: %d\n", translator::translate("Postrate"), get_mail_level()); } building_desc_t const& h = *tile->get_desc(); buf.printf("%s%u", translator::translate("\nBauzeit von"), h.get_intro_year_month() / 12); if (h.get_retire_year_month() != DEFAULT_RETIRE_YEAR * 12) { buf.printf("%s%u", translator::translate("\nBauzeit bis"), h.get_retire_year_month() / 12); } buf.append("\n"); if(get_owner()==NULL) { const sint32 v = (sint32)( -welt->get_settings().cst_multiply_remove_haus * (tile->get_desc()->get_level() + 1) / 100 ); buf.printf("\n%s: %d$\n", translator::translate("Wert"), v); } if (char const* const maker = tile->get_desc()->get_copyright()) { buf.append("\n"); buf.printf(translator::translate("Constructed by %s"), maker); } #ifdef DEBUG buf.append( "\n\nrotation " ); buf.append( tile->get_layout(), 0 ); buf.append( "\n" ); #endif } } void gebaeude_t::rdwr(loadsave_t *file) { // do not save factory buildings => factory will reconstruct them assert(!is_factory); xml_tag_t d( file, "gebaeude_t" ); obj_t::rdwr(file); char buf[128]; short idx; if(file->is_saving()) { const char *s = tile->get_desc()->get_name(); file->rdwr_str(s); idx = tile->get_index(); } else { file->rdwr_str(buf, lengthof(buf)); } file->rdwr_short(idx); file->rdwr_long(insta_zeit); if(file->is_loading()) { tile = hausbauer_t::find_tile(buf, idx); if(tile==NULL) { // try with compatibility list first tile = hausbauer_t::find_tile(translator::compatibility_name(buf), idx); if(tile==NULL) { DBG_MESSAGE("gebaeude_t::rdwr()","neither %s nor %s, tile %i not found, try other replacement",translator::compatibility_name(buf),buf,idx); } else { DBG_MESSAGE("gebaeude_t::rdwr()","%s replaced by %s, tile %i",buf,translator::compatibility_name(buf),idx); } } if(tile==NULL) { // first check for special buildings if(strstr(buf,"TrainStop")!=NULL) { tile = hausbauer_t::find_tile("TrainStop", idx); } else if(strstr(buf,"BusStop")!=NULL) { tile = hausbauer_t::find_tile("BusStop", idx); } else if(strstr(buf,"ShipStop")!=NULL) { tile = hausbauer_t::find_tile("ShipStop", idx); } else if(strstr(buf,"PostOffice")!=NULL) { tile = hausbauer_t::find_tile("PostOffice", idx); } else if(strstr(buf,"StationBlg")!=NULL) { tile = hausbauer_t::find_tile("StationBlg", idx); } else { // try to find a fitting building int level=atoi(buf); building_desc_t::btype type = building_desc_t::unknown; if(level>0) { // May be an old 64er, so we can try some if(strncmp(buf+3,"WOHN",4)==0) { type = building_desc_t::city_res; } else if(strncmp(buf+3,"FAB",3)==0) { type = building_desc_t::city_ind; } else { type = building_desc_t::city_com; } level --; } else if(buf[3]=='_') { /* should have the form of RES/IND/COM_xx_level * xx is usually a number by can be anything without underscores */ level = atoi(strrchr( buf, '_' )+1); if(level>0) { switch(toupper(buf[0])) { case 'R': type = building_desc_t::city_res; break; case 'I': type = building_desc_t::city_ind; break; case 'C': type = building_desc_t::city_com; break; } } level --; } // we try to replace citybuildings with their matching counterparts // if none are matching, we try again without climates and timeline! switch(type) { case building_desc_t::city_res: { const building_desc_t *bdsc = hausbauer_t::get_residential( level, welt->get_timeline_year_month(), welt->get_climate( get_pos().get_2d() ), 0, 1, 1 ); if(bdsc==NULL) { bdsc = hausbauer_t::get_residential(level,0, MAX_CLIMATES, 0, 1, 1 ); } if( bdsc) { dbg->message("gebaeude_t::rwdr", "replace unknown building %s with residence level %i by %s",buf,level,bdsc->get_name()); tile = bdsc->get_tile(0); } } break; case building_desc_t::city_com: { // for replacement, ignore cluster and size const building_desc_t *bdsc = hausbauer_t::get_commercial( level, welt->get_timeline_year_month(), welt->get_climate( get_pos().get_2d() ), 0, 1, 1 ); if(bdsc==NULL) { bdsc = hausbauer_t::get_commercial(level, 0, MAX_CLIMATES, 0, 1, 1 ); } if(bdsc) { dbg->message("gebaeude_t::rwdr", "replace unknown building %s with commercial level %i by %s",buf,level,bdsc->get_name()); tile = bdsc->get_tile(0); } } break; case building_desc_t::city_ind: { const building_desc_t *bdsc = hausbauer_t::get_industrial( level, welt->get_timeline_year_month(), welt->get_climate( get_pos().get_2d() ), 0, 1, 1 ); if(bdsc==NULL) { bdsc = hausbauer_t::get_industrial(level, 0, MAX_CLIMATES, 0, 1, 1 ); if(bdsc==NULL) { bdsc = hausbauer_t::get_residential(level, 0, MAX_CLIMATES, 0, 1, 1 ); } } if (bdsc) { dbg->message("gebaeude_t::rwdr", "replace unknown building %s with industry level %i by %s",buf,level,bdsc->get_name()); tile = bdsc->get_tile(0); } } break; default: dbg->warning("gebaeude_t::rwdr", "description %s for building at %d,%d not found (will be removed)!", buf, get_pos().x, get_pos().y); pakset_manager_t::add_missing_paks( buf, MISSING_BUILDING ); } } } // here we should have a valid tile pointer or nothing ... /* avoid double construction of monuments: * remove them from selection lists */ if (tile && tile->get_desc()->get_type() == building_desc_t::monument) { hausbauer_t::monument_erected(tile->get_desc()); } if (tile) { remove_ground = tile->has_image() && !tile->get_desc()->needs_ground(); } } if(file->is_version_less(99, 6)) { // ignore the sync flag uint8 dummy=sync; file->rdwr_byte(dummy); } // restore city pointer here if( file->is_version_atleast(99, 14) ) { sint32 city_index = -1; if( file->is_saving() && ptr.stadt!=NULL ) { city_index = welt->get_cities().index_of( ptr.stadt ); } file->rdwr_long(city_index); if( file->is_loading() && (tile==NULL || tile->get_desc()==NULL || tile->get_desc()->is_connected_with_town()) ) { if (city_index != -1) { ptr.stadt = welt->get_cities()[city_index]; } else { ptr.stadt = NULL; } } } if(file->is_loading()) { anim_frame = 0; anim_time = 0; sync = false; // rebuild tourist attraction list if(tile && tile->get_desc()->is_attraction()) { welt->add_attraction( this ); } } } void gebaeude_t::finish_rd() { player_t::add_maintenance(get_owner(), tile->get_desc()->get_maintenance(welt), tile->get_desc()->get_finance_waytype()); // citybuilding, but no town? if( tile->get_offset()==koord(0,0) ) { if( tile->get_desc()->is_connected_with_town() ) { stadt_t *city = (ptr.stadt==NULL) ? welt->find_nearest_city( get_pos().get_2d() ) : ptr.stadt; if(city) { #ifdef MULTI_THREAD pthread_mutex_lock( &add_to_city_mutex ); #endif city->add_gebaeude_to_stadt(this, true); #ifdef MULTI_THREAD pthread_mutex_unlock( &add_to_city_mutex ); #endif } } else if( !is_factory ) { ptr.stadt = NULL; } #ifdef REPAIR_MULTITILE_BUILD // check multitile buildings for lost tiles koord size = tile->get_desc()->get_size(0); const building_desc_t* desc = tile->get_desc(); if (size.x + size.y > 2) { // ATTENTION: So far does only work for symmetrical buildings if (grund_t* gr = welt->lookup_kartenboden(get_pos().get_2d() - size + koord(1, 1))) { if (gebaeude_t* gb = gr->find()) { if (gb->get_tile() == tile) { // found misalinged building here => correct its tiles for (sint16 dx = 0; dx < size.x; dx++) { for (sint16 dy = 0; dy < size.x; dy++) { if (grund_t* gbgr = welt->lookup(gr->get_pos() + koord(dx, dy))) { if (gebaeude_t* gb = gbgr->find()) { gb->set_tile(desc->get_tile(0, dx, dy), false); } } } } } } } } #endif } } void gebaeude_t::cleanup(player_t *player) { // DBG_MESSAGE("gebaeude_t::entferne()","gb %i"); // remove costs sint64 cost = welt->get_settings().cst_multiply_remove_haus; // tearing down halts is always single costs only if (!tile->get_desc()->is_transport_building()) { cost *= tile->get_desc()->get_level() + 1; } player_t::book_construction_costs(player, cost, get_pos().get_2d(), tile->get_desc()->get_finance_waytype()); if (stadt_t *city=get_stadt()) { city->remove_gebaeude_from_stadt(this); } // may need to update next buildings, in the case of start, middle, end buildings (only for stations for now) if(tile->get_desc()->get_all_layouts()>1 && tile->get_desc()->is_transport_building()) { // realign surrounding buildings... uint32 layout = tile->get_layout(); // detect if we are connected at far (north/west) end grund_t * gr = welt->lookup( get_pos() ); if(gr) { sint8 offset = gr->get_weg_yoff()/TILE_HEIGHT_STEP; gr = welt->lookup( get_pos()+koord3d( (layout & 1 ? koord::east : koord::south), offset) ); if(!gr) { // check whether bridge end tile grund_t * gr_tmp = welt->lookup( get_pos()+koord3d( (layout & 1 ? koord::east : koord::south),offset - 1) ); if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 1) { gr = gr_tmp; } } if(gr) { gebaeude_t* gb = gr->find(); if(gb && gb->get_tile()->get_desc()->get_all_layouts()>4u) { koord xy = gb->get_tile()->get_offset(); uint8 layoutbase = gb->get_tile()->get_layout(); if((layoutbase & 1u) == (layout & 1u)) { layoutbase |= 4u; // set far bit on neighbour gb->set_tile( gb->get_tile()->get_desc()->get_tile(layoutbase, xy.x, xy.y), false ); } } } // detect if near (south/east) end gr = welt->lookup( get_pos()+koord3d( (layout & 1 ? koord::west : koord::north), offset) ); if(!gr) { // check whether bridge end tile grund_t * gr_tmp = welt->lookup( get_pos()+koord3d( (layout & 1 ? koord::west : koord::north),offset - 1) ); if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 1) { gr = gr_tmp; } } if(gr) { gebaeude_t* gb = gr->find(); if(gb && gb->get_tile()->get_desc()->get_all_layouts()>4) { koord xy = gb->get_tile()->get_offset(); uint8 layoutbase = gb->get_tile()->get_layout(); if((layoutbase & 1u) == (layout & 1u)) { layoutbase |= 2u; // set near bit on neighbour gb->set_tile( gb->get_tile()->get_desc()->get_tile(layoutbase, xy.x, xy.y), false ); } } } } } mark_images_dirty(); } void gebaeude_t::mark_images_dirty() const { // remove all traces from the screen image_id img; if( zeige_baugrube || (!env_t::hide_with_transparency && env_t::hide_buildings>(is_city_building() ? env_t::NOT_HIDE : env_t::SOME_HIDDEN_BUILDING)) ) { img = skinverwaltung_t::construction_site->get_image_id(0); mark_image_dirty(img, 0); } else { img = tile->get_background( anim_frame, 0, season ) ; for (int i = 0; img != IMG_EMPTY; ) { mark_image_dirty(img, -(i * gfx->get_tile_raster_width())); img = tile->get_background(anim_frame, ++i, season); } } } simutrans-124.5/src/simutrans/obj/gebaeude.h000066400000000000000000000074471520675460500211000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_GEBAEUDE_H #define OBJ_GEBAEUDE_H #include "simobj.h" #include "../simcolor.h" class building_tile_desc_t; class fabrik_t; class stadt_t; class grund_t; /** * Asynchronous or synchronous animations for buildings. */ class gebaeude_t : public obj_t { private: const building_tile_desc_t *tile; /** * Time control for animation progress. */ uint16 anim_time; /** * Is this a sync animated object? */ uint8 sync:1; /** * Boolean flag if a construction site or buildings image * shall be displayed. */ uint8 zeige_baugrube:1; /** * if true, this ptr union contains a factory pointer */ uint8 is_factory:1; uint8 season:3; uint8 background_animated:1; uint8 remove_ground:1; // true if ground image can go uint8 anim_frame; /** * Zeitpunkt an dem das Gebaeude Gebaut wurde */ uint32 insta_zeit; /** * either point to a factory or a city */ union { fabrik_t *fab; stadt_t *stadt; } ptr; /** * Initializes all variables with safe, usable values */ void init(); protected: gebaeude_t(); public: gebaeude_t(loadsave_t *file); gebaeude_t(koord3d pos,player_t *player, const building_tile_desc_t *t); virtual ~gebaeude_t(); void rotate90() OVERRIDE; void add_alter(uint32 a); void set_fab(fabrik_t *fd); void set_stadt(stadt_t *s); fabrik_t* get_fabrik() const { return is_factory ? ptr.fab : NULL; } stadt_t* get_stadt() const { return is_factory ? NULL : ptr.stadt; } obj_t::typ get_typ() const OVERRIDE { return obj_t::gebaeude; } /** * waytype associated with this object */ waytype_t get_waytype() const OVERRIDE; image_id get_image() const OVERRIDE; image_id get_front_image() const OVERRIDE; void mark_images_dirty() const; image_id get_outline_image() const OVERRIDE; FLAGGED_PIXVAL get_outline_colour() const OVERRIDE; void display(int xpos, int ypos CLIP_NUM_DEF) const OVERRIDE; // caches image at height 0 void calc_image() OVERRIDE; /** * Called whenever the season or snowline height changes * return false and the obj_t will be deleted */ bool check_season(const bool) OVERRIDE { calc_image(); return true; } /** * @return eigener Name oder Name der Fabrik falls Teil einer Fabrik */ const char *get_name() const OVERRIDE; bool is_townhall() const; bool is_headquarter() const; bool is_monument() const; bool is_city_building() const; /// fills vector with a list of all tiles with this building /// @return number of actual tiles uint32 get_tile_list( vector_tpl& list ) const; /// @copydoc obj_t::info void info(cbuffer_t & buf) const OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; /** * Play animations of animated buildings. * Count-down to replace construction site image by regular image. */ sync_result sync_step(uint32 delta_t); /** * @return Den level (die Ausbaustufe) des Gebaudes */ int get_passagier_level() const; int get_mail_level() const; void set_tile( const building_tile_desc_t *t, bool start_with_construction ); const building_tile_desc_t *get_tile() const { return tile; } bool is_within_players_network(const player_t* player) const; void show_info() OVERRIDE; void cleanup(player_t *player) OVERRIDE; /** * Called after the world is completely * add buildings to towns if needed * may repair broken buildings if REPAIR_MULTITILE_BUILD is set */ void finish_rd() OVERRIDE; /** * @returns pointer to first tile of a multi-tile building. */ gebaeude_t* get_first_tile(); /** * @returns true if both building tiles are part of one (multi-tile) building. */ bool is_same_building(const gebaeude_t* other) const; }; template<> inline gebaeude_t* obj_cast(obj_t* const d) { return dynamic_cast(d); } #endif simutrans-124.5/src/simutrans/obj/groundobj.cc000066400000000000000000000172451520675460500214630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "../world/simworld.h" #include "simobj.h" #include "../display/simimg.h" #include "../player/simplay.h" #include "../simtypes.h" #include "../ground/grund.h" #include "../descriptor/groundobj_desc.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include "../utils/simrandom.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/freelist.h" #include "groundobj.h" /******************************** static routines for desc management ****************************************************************/ vector_tpl groundobj_t::groundobj_typen(0); weighted_vector_tpl* groundobj_t::groundobj_list_per_climate = NULL; stringhashtable_tpl groundobj_t::desc_table; bool compare_groundobj_desc(const groundobj_desc_t* a, const groundobj_desc_t* b) { return strcmp(a->get_name(), b->get_name())<0; } // total number of groundobj for a certain climate int groundobj_t::get_count(climate cl) { return groundobj_list_per_climate[cl].get_count(); } /** * groundobj planting function - it takes care of checking suitability of area */ bool groundobj_t::plant_groundobj_on_coordinate(koord pos, const groundobj_desc_t *desc, const bool check_climate) { // none there if( desc_table.empty() ) { return false; } grund_t *gr = welt->lookup_kartenboden(pos); if( gr ) { if( gr->ist_natur() && (!check_climate || desc->is_allowed_climate( welt->get_climate(pos) )) ) { if( gr->obj_count() > 0 ) { switch(gr->obj_bei(0)->get_typ()) { case obj_t::cloud: case obj_t::air_vehicle: case obj_t::leitung: case obj_t::label: case obj_t::zeiger: // ok to built here break; case obj_t::baum: if( desc->can_build_trees_here() ) { break; } /* FALLTHROUGH */ // leave these (and all other empty) default: return false; } } groundobj_t *g = new groundobj_t(gr->get_pos(), desc); //plants the groundobj gr->obj_add( g ); return true; //groundobj was planted - currently unused value is not checked } } return false; } bool groundobj_t::successfully_loaded() { groundobj_typen.reserve(desc_table.get_count()); for(auto const& i : desc_table) { groundobj_typen.insert_ordered(i.value, compare_groundobj_desc); } // iterate again to assign the index for(auto const& i : desc_table) { i.value->index = groundobj_typen.index_of(i.value); } if(desc_table.empty()) { groundobj_typen.append( NULL ); DBG_MESSAGE("groundobj_t", "No groundobj found - feature disabled"); } delete [] groundobj_list_per_climate; groundobj_list_per_climate = new weighted_vector_tpl[MAX_CLIMATES]; for( uint32 typ=0; typis_allowed_climate((climate)j) ) { groundobj_list_per_climate[j].append(typ, groundobj_typen[typ]->get_distribution_weight()); } } } return true; } bool groundobj_t::register_desc(groundobj_desc_t *desc) { assert(desc->get_speed()==0); // remove duplicates if(groundobj_desc_t *old = desc_table.remove( desc->get_name() ) ) { delete old; } desc_table.put(desc->get_name(), desc ); return true; } /** * also checks for distribution values */ const groundobj_desc_t *groundobj_t::random_groundobj_for_climate(climate_bits cl, slope_t::type slope ) { // none there if( desc_table.empty() ) { return NULL; } int weight = 0; for(groundobj_desc_t const* const i : groundobj_typen ) { if( i->is_allowed_climate_bits(cl) && (slope == slope_t::flat || (i->get_phases() >= slope && i->get_image_id(0,slope)!=IMG_EMPTY ) ) ) { weight += i->get_distribution_weight(); } } // now weight their distribution if( weight > 0 ) { const int w=simrand(weight); weight = 0; for(groundobj_desc_t const* const i : groundobj_typen) { if( i->is_allowed_climate_bits(cl) && (slope == slope_t::flat || (i->get_phases() >= slope && i->get_image_id(0,slope)!=IMG_EMPTY ) ) ) { weight += i->get_distribution_weight(); if(weight>=w) { return i; } } } } return NULL; } /******************************* end of static ******************************************/ uint16 groundobj_t::random_groundobj_for_climate_intern(climate cl) { // now weight their distribution weighted_vector_tpl const& g = groundobj_list_per_climate[cl]; return g.empty() ? 0xFFFF : pick_any_weighted(g); } // recalculates only the seasonal image void groundobj_t::calc_image() { const groundobj_desc_t *desc=get_desc(); const uint8 seasons = desc->get_seasons()-1; uint8 season = 0; // two possibilities switch(seasons) { // summer only case 0: season = 0; break; // summer, snow case 1: season = welt->get_snowline() <= get_pos().z || welt->get_climate( get_pos().get_2d() ) == arctic_climate; break; // summer, winter, snow case 2: season = (welt->get_snowline() <= get_pos().z || welt->get_climate( get_pos().get_2d() ) == arctic_climate) ? 2 : welt->get_season() == 1; break; default: if( welt->get_snowline() <= get_pos().z || welt->get_climate( get_pos().get_2d() ) == arctic_climate ) { season = seasons; } else { // resolution 1/8th month (0..95) const uint32 yearsteps = (welt->get_current_month()%12)*8 + ((welt->get_ticks()>>(welt->ticks_per_world_month_shift-3))&7) + 1; season = (seasons*yearsteps-1)/96; } break; } // check for slopes? uint16 phase = 0; if(desc->get_phases()>1) { phase = welt->lookup(get_pos())->get_grund_hang(); } image = get_desc()->get_image_id( season, phase ); } groundobj_t::groundobj_t(loadsave_t *file) : obj_t() { rdwr(file); } groundobj_t::groundobj_t(koord3d pos, const groundobj_desc_t *b ) : obj_t(pos) { groundobjtype = groundobj_typen.index_of(b); calc_image(); } bool groundobj_t::check_season(const bool) { const image_id old_image = get_image(); calc_image(); if( get_image() != old_image ) { mark_image_dirty( get_image(), 0 ); } return true; } void groundobj_t::rdwr(loadsave_t *file) { xml_tag_t d( file, "groundobj_t" ); obj_t::rdwr(file); if(file->is_saving()) { const char *s = get_desc()->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); groundobj_desc_t *desc = desc_table.get( bname ); if( desc==NULL ) { desc = desc_table.get( translator::compatibility_name( bname ) ); } if( desc==NULL ) { groundobjtype = simrand(groundobj_typen.get_count()); } else { groundobjtype = desc->get_index(); } // if not there, desc will be zero } } void groundobj_t::show_info() { if(env_t::tree_info) { obj_t::show_info(); } } void groundobj_t::info(cbuffer_t & buf) const { translator::get_obj_info(buf, get_desc()->get_name()); if (char const* const maker = get_desc()->get_copyright()) { buf.append("\n"); buf.printf(translator::translate("Constructed by %s"), maker); } buf.append("\n"); buf.append(translator::translate("cost for removal")); char buffer[128]; money_to_string( buffer, get_desc()->get_price()/100.0 ); buf.append( buffer ); } void groundobj_t::cleanup(player_t *player) { player_t::book_construction_costs(player, -get_desc()->get_price(), get_pos().get_2d(), ignore_wt); mark_image_dirty( get_image(), 0 ); } void *groundobj_t::operator new(size_t /*s*/) { return freelist_t::gimme_node(sizeof(groundobj_t)); } void groundobj_t::operator delete(void *p) { freelist_t::putback_node(sizeof(groundobj_t),p); } simutrans-124.5/src/simutrans/obj/groundobj.h000066400000000000000000000047561520675460500213300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_GROUNDOBJ_H #define OBJ_GROUNDOBJ_H #include "simobj.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/vector_tpl.h" #include "../tpl/weighted_vector_tpl.h" #include "../descriptor/groundobj_desc.h" #include "../dataobj/environment.h" /** * Decorative objects, like rocks, ponds etc. */ class groundobj_t : public obj_t { private: /// type of object, index into groundobj_typen uint16 groundobjtype; /// the image, cached image_id image; /// table to lookup object based on name static stringhashtable_tpl desc_table; /// all such objects static vector_tpl groundobj_typen; static weighted_vector_tpl* groundobj_list_per_climate; static uint16 random_groundobj_for_climate_intern(climate cl); public: static bool register_desc(groundobj_desc_t *desc); static bool successfully_loaded(); static const groundobj_desc_t *random_groundobj_for_climate(climate_bits cl, slope_t::type slope ); groundobj_t(loadsave_t *file); groundobj_t(koord3d pos, const groundobj_desc_t *); void rdwr(loadsave_t *file) OVERRIDE; image_id get_image() const OVERRIDE { return image; } /// recalculates image depending on season and slope of ground void calc_image() OVERRIDE; /** * Called whenever the season or snowline height changes * return false and the obj_t will be deleted */ bool check_season(const bool) OVERRIDE; const char *get_name() const OVERRIDE {return "Groundobj";} typ get_typ() const OVERRIDE { return groundobj; } void show_info() OVERRIDE; void info(cbuffer_t & buf) const OVERRIDE; void cleanup(player_t *player) OVERRIDE; const groundobj_desc_t* get_desc() const { return groundobj_typen[groundobjtype]; } static vector_tpl const& get_all_desc() { return groundobj_typen; } static const groundobj_desc_t *find_groundobj( const char *obj_name ) { return groundobj_typen.empty() ? NULL : desc_table.get(obj_name); } static bool plant_groundobj_on_coordinate(koord pos, const groundobj_desc_t *desc, const bool check_climate); static const groundobj_desc_t *random_groundobj_for_climate(climate cl) { uint16 b = random_groundobj_for_climate_intern(cl); return b!=0xFFFF ? groundobj_typen[b] : NULL; } void * operator new(size_t s); void operator delete(void *p); static int get_count() { return groundobj_typen.get_count()-1; } static int get_count(climate cl); }; #endif simutrans-124.5/src/simutrans/obj/label.cc000066400000000000000000000042651520675460500205470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simworld.h" #include "simobj.h" #include "../display/simimg.h" #include "../simskin.h" #include "../gui/simwin.h" #include "../simhalt.h" #include "../player/simplay.h" #include "../gui/label_info.h" #include "../descriptor/ground_desc.h" #include "../descriptor/skin_desc.h" #include "../dataobj/environment.h" #include "label.h" #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_mutex_t add_label_mutex = PTHREAD_MUTEX_INITIALIZER; #endif label_t::label_t(loadsave_t *file) : obj_t() { rdwr(file); } label_t::label_t(koord3d pos, player_t *player, const char *text) : obj_t(pos) { set_owner( player ); grund_t *gr=welt->lookup_kartenboden(pos.get_2d()); if(gr) { if (text) { gr->set_text(text); } player_t::book_construction_costs(player, welt->get_settings().cst_buy_land, pos.get_2d(), ignore_wt); welt->add_label(pos.get_2d()); } else { dbg->error("label_t", "cannot create label on empty ground at %s", pos.get_fullstr()); } } label_t::~label_t() { koord k = get_pos().get_2d(); welt->remove_label(k); grund_t *gr = welt->lookup_kartenboden(k); if(gr) { // do not remove name from halts if (!gr->is_halt() || gr->get_halt()->get_basis_pos3d()!=gr->get_pos()) { gr->set_text(NULL); } } else { dbg->error("label_t", "cannot remove label on empty ground at %s", get_pos().get_fullstr()); } } void label_t::finish_rd() { #ifdef MULTI_THREAD pthread_mutex_lock( &add_label_mutex ); #endif // only now coordinates are known welt->add_label(get_pos().get_2d()); // broken label? set text to "" grund_t *gr = welt->lookup_kartenboden(get_pos().get_2d()); if (!gr->get_flag(grund_t::has_text)) { gr->set_text(""); } #ifdef MULTI_THREAD pthread_mutex_unlock( &add_label_mutex ); #endif } image_id label_t::get_image() const { grund_t *gr=welt->lookup(get_pos()); return gr && gr->obj_bei(0) == sim::up_cast(this) ? skinverwaltung_t::belegtzeiger->get_image_id(0) : IMG_EMPTY; } void label_t::show_info() { label_t* l = this; create_win(new label_info_t(l), w_info, (ptrdiff_t)this ); } simutrans-124.5/src/simutrans/obj/label.h000066400000000000000000000011201520675460500203740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_LABEL_H #define OBJ_LABEL_H #include "simobj.h" #include "../display/simimg.h" /* * Object which shows the label that indicates that the ground is owned by somebody */ class label_t : public obj_t { public: label_t(loadsave_t *file); label_t(koord3d pos, player_t *player, const char *text); ~label_t(); void finish_rd() OVERRIDE; void show_info() OVERRIDE; typ get_typ() const OVERRIDE { return obj_t::label; } image_id get_image() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/leitung2.cc000066400000000000000000000501021520675460500212100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_mutex_t verbinde_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t calc_image_mutex = PTHREAD_MUTEX_INITIALIZER; #endif #include "leitung2.h" #include "../simdebug.h" #include "../world/simworld.h" #include "simobj.h" #include "../player/simplay.h" #include "../display/simimg.h" #include "../simfab.h" #include "../simskin.h" #include "../display/simgraph.h" #include "../utils/cbuffer.h" #include "../dataobj/translator.h" #include "../dataobj/loadsave.h" #include "../dataobj/powernet.h" #include "../dataobj/environment.h" #include "../dataobj/pakset_manager.h" #include "../ground/grund.h" #include "../builder/wegbauer.h" const uint32 POWER_TO_MW = 12; // use same precision as powernet const uint8 leitung_t::FRACTION_PRECISION = powernet_t::FRACTION_PRECISION; /** * returns possible directions for powerline on this tile */ ribi_t::ribi get_powerline_ribi(grund_t *gr) { slope_t::type slope = gr->get_weg_hang(); ribi_t::ribi ribi = (ribi_t::ribi)ribi_t::all; if (slope == slope_t::flat) { // respect possible directions for bridge and tunnel starts if (gr->ist_karten_boden() && (gr->ist_tunnel() || gr->ist_bruecke())) { ribi = ribi_t::doubles( ribi_type( gr->get_grund_hang() ) ); } } else { ribi = ribi_t::doubles( ribi_type(slope) ); } return ribi; } int leitung_t::gimme_neighbours(leitung_t **conn) { int count = 0; grund_t *gr_base = welt->lookup(get_pos()); ribi_t::ribi ribi = get_powerline_ribi(gr_base); for(int i=0; i<4; i++) { // get next connected tile (if there) grund_t *gr; conn[i] = NULL; if( (ribi & ribi_t::nesw[i]) && gr_base->get_neighbour( gr, invalid_wt, ribi_t::nesw[i] ) ) { leitung_t *lt = gr->get_leitung(); // check that we can connect to the other tile: correct slope, // both ground or both tunnel or both not tunnel bool const ok = (gr->ist_karten_boden() && gr_base->ist_karten_boden()) || (gr->ist_tunnel()==gr_base->ist_tunnel()); if( lt && (ribi_t::backward(ribi_t::nesw[i]) & get_powerline_ribi(gr)) && ok ) { const player_t *owner = get_owner(); const player_t *other = lt->get_owner(); const player_t *super = welt->get_public_player(); if (owner==other || owner==super || other==super) { conn[i] = lt; count++; } } } } return count; } fabrik_t *leitung_t::suche_fab_4(const koord pos) { for(int k=0; k<4; k++) { fabrik_t *fab = fabrik_t::get_fab( pos+koord::nesw[k] ); if(fab) { return fab; } } return NULL; } leitung_t::leitung_t(loadsave_t *file) : obj_t() { image = IMG_EMPTY; set_net(NULL); ribi = ribi_t::none; is_transformer = false; rdwr(file); } leitung_t::leitung_t(koord3d pos, player_t *player) : obj_t(pos) { image = IMG_EMPTY; set_net(NULL); set_owner( player ); set_desc(way_builder_t::leitung_desc); is_transformer = false; } leitung_t::~leitung_t() { if (welt->is_destroying()) { return; } grund_t *gr = welt->lookup(get_pos()); if(gr) { leitung_t *conn[4]; int neighbours = gimme_neighbours(conn); gr->obj_remove(this); set_flag( obj_t::not_on_map ); if(neighbours>1) { // only reconnect if two connections ... bool first = true; for(int i=0; i<4; i++) { if(conn[i]!=NULL) { if(!first) { // replace both nets powernet_t *new_net = new powernet_t(); conn[i]->replace(new_net); } first = false; } } } // recalc images for(int i=0; i<4; i++) { if(conn[i]!=NULL) { conn[i]->calc_neighbourhood(); } } if(neighbours==0) { delete net; } player_t::add_maintenance(get_owner(), -get_maintenance(), powerline_wt); } } void leitung_t::cleanup(player_t *player) { player_t::book_construction_costs(player, -desc->get_price()/2, get_pos().get_2d(), powerline_wt); mark_image_dirty( image, 0 ); } /** * called during map rotation */ void leitung_t::rotate90() { obj_t::rotate90(); ribi = ribi_t::rotate90( ribi ); } /** * replace networks connection * non-trivial to handle transformers correctly */ void leitung_t::replace(powernet_t* new_net) { if (get_net() == new_net) { return; } // Use an explicit stack to handle the traversal iteratively to avoid stack overflow on larger maps vector_tpltodo_stack; todo_stack.reserve(256); // Small optimization to avoid frequent reallocations todo_stack.append(this); while (!todo_stack.empty()) { leitung_t* current = todo_stack.back(); todo_stack.pop_back(); // lready processed => next if (current->get_net() == new_net) { continue; } // Update the network for the current segment current->set_net(new_net); // Check and gather neighbors leitung_t* conn[4]; if (current->gimme_neighbours(conn) > 0) { for (int i = 0; i < 4; i++) { // Only add neighbors that actually need updating if (conn[i] && conn[i]->get_net() != new_net) { todo_stack.append(conn[i]); } } } } } /** * Connect this piece of powerline to its neighbours * -> this can merge power networks */ void leitung_t::verbinde() { // first get my own ... powernet_t *new_net = get_net(); //DBG_MESSAGE("leitung_t::verbinde()","Searching net at (%i,%i)",get_pos().x,get_pos().x); leitung_t * conn[4]; if(gimme_neighbours(conn)>0) { for( uint8 i=0; i<4 && new_net==NULL; i++ ) { if(conn[i]) { new_net = conn[i]->get_net(); } } } //DBG_MESSAGE("leitung_t::verbinde()","Found net %p",new_net); // we are alone? if(get_net()==NULL) { if(new_net!=NULL) { replace(new_net); } else { // then we start a new net set_net(new powernet_t()); //DBG_MESSAGE("leitung_t::verbinde()","Creating new net %p",new_net); } } else if(new_net) { powernet_t *my_net = get_net(); for( uint8 i=0; i<4; i++ ) { if(conn[i] && conn[i]->get_net()!=new_net) { conn[i]->replace(new_net); } } if(my_net && my_net!=new_net) { delete my_net; } } } void leitung_t::calc_image() { is_crossing = false; const koord pos = get_pos().get_2d(); bool snow = get_pos().z >= welt->get_snowline() || welt->get_climate( get_pos().get_2d() ) == arctic_climate; grund_t *gr = welt->lookup(get_pos()); if(gr==NULL) { // no valid ground; usually happens during building ... return; } if(gr->ist_bruecke() || (gr->get_typ()==grund_t::tunnelboden && gr->ist_karten_boden())) { // don't display on a bridge or in a tunnel) set_image(IMG_EMPTY); return; } image_id old_image = get_image(); slope_t::type hang = gr->get_weg_hang(); if(hang != slope_t::flat) { set_image( desc->get_slope_image_id(hang, snow)); } else { if(gr->hat_wege()) { // crossing with road or rail weg_t* way = gr->get_weg_nr(0); if(ribi_t::is_straight_ew(way->get_ribi())) { set_image( desc->get_diagonal_image_id(ribi_t::north|ribi_t::east, snow)); } else { set_image( desc->get_diagonal_image_id(ribi_t::south|ribi_t::east, snow)); } is_crossing = true; } else { if(ribi_t::is_straight(ribi) && !ribi_t::is_single(ribi) && (pos.x+pos.y)&1) { // every second skip mast if(ribi_t::is_straight_ns(ribi)) { set_image( desc->get_diagonal_image_id(ribi_t::north|ribi_t::west, snow)); } else { set_image( desc->get_diagonal_image_id(ribi_t::south|ribi_t::west, snow)); } } else { set_image( desc->get_image_id(ribi, snow)); } } } if (old_image != get_image()) { mark_image_dirty(old_image,0); } } /** * Recalculates the images of all neighbouring * powerlines and the powerline itself */ void leitung_t::calc_neighbourhood() { leitung_t *conn[4]; ribi = ribi_t::none; if(gimme_neighbours(conn)>0) { for( uint8 i=0; i<4 ; i++ ) { if(conn[i] && conn[i]->get_net()==get_net()) { ribi |= ribi_t::nesw[i]; conn[i]->add_ribi(ribi_t::backward(ribi_t::nesw[i])); conn[i]->calc_image(); } } } set_flag( obj_t::dirty ); calc_image(); } void leitung_t::info(cbuffer_t & buf) const { translator::get_obj_info(buf, desc->get_name()); obj_t::info(buf); powernet_t * const net = get_net(); buf.printf(translator::translate("Net ID: %p"), net); buf.printf("\n"); //buf.printf(translator::translate("Capacity: %.0f MW"), (double)(net->get_max_capacity() >> POWER_TO_MW)); //buf.printf("\n"); buf.printf(translator::translate("Demand: %.0f MW"), (double)(net->get_demand() >> POWER_TO_MW)); buf.printf("\n"); buf.printf(translator::translate("Generation: %.0f MW"), (double)(net->get_supply() >> POWER_TO_MW)); buf.printf("\n"); buf.printf(translator::translate("Usage: %.0f %%"), (double)((100 * net->get_normal_demand()) >> powernet_t::FRACTION_PRECISION)); } void leitung_t::finish_rd() { #ifdef MULTI_THREAD pthread_mutex_lock( &verbinde_mutex ); verbinde(); pthread_mutex_unlock( &verbinde_mutex ); pthread_mutex_lock( &calc_image_mutex ); calc_neighbourhood(); pthread_mutex_unlock( &calc_image_mutex ); #else verbinde(); calc_neighbourhood(); #endif grund_t *gr = welt->lookup(get_pos()); assert(gr); (void)gr; player_t::add_maintenance(get_owner(), get_maintenance(), powerline_wt); } void leitung_t::rdwr(loadsave_t *file) { xml_tag_t d( file, "leitung_t" ); obj_t::rdwr(file); // no longer save power net pointer as it is no longer used if( file->is_version_less(120, 4) ) { uint32 value = 0; file->rdwr_long(value); } if( file->is_loading() ) { set_net(NULL); } if(get_typ()==leitung) { /* ATTENTION: during loading thus MUST not be called from the constructor!!! * (Otherwise it will be always true! */ if(file->is_version_atleast(102, 3)) { if(file->is_saving()) { const char *s = desc->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); const way_desc_t *desc = way_builder_t::get_desc(bname); if(desc==NULL) { desc = way_builder_t::get_desc(translator::compatibility_name(bname)); if(desc==NULL) { pakset_manager_t::add_missing_paks( bname, MISSING_WAY ); desc = way_builder_t::leitung_desc; if (!desc) { dbg->fatal("leitung_t::rdwr", "Trying to load powerline but pakset has none!"); } } dbg->warning("leitung_t::rdwr()", "Unknown powerline %s replaced by %s", bname, desc->get_name() ); } set_desc(desc); } } else { if (file->is_loading()) { set_desc(way_builder_t::leitung_desc); } } } } // returns NULL, if removal is allowed // players can remove public owned powerlines const char *leitung_t::get_removal_error(const player_t *player) { if( this->get_owner_nr()==PLAYER_PUBLIC_NR) { return NULL; } else if (player && (player->get_player_nr() == this->get_owner_nr() || player->is_public_service())) { return NULL; } return "Der Besitzer erlaubt das Entfernen nicht"; } sint64 leitung_t::get_maintenance() const { if (!is_transformer) { return desc->get_maintenance(); } else { return -welt->get_settings().cst_maintain_transformer; } } /************************************ from here on pump (source) stuff ********************************************/ freelist_iter_tpl pumpe_t::pl; pumpe_t::pumpe_t(loadsave_t *file ) : leitung_t( koord3d::invalid, NULL ) { fab = NULL; power_supply = 0; is_transformer = true; rdwr( file ); } pumpe_t::pumpe_t(koord3d pos, player_t *player) : leitung_t(pos, player) { fab = NULL; power_supply = 0; is_transformer = true; player_t::book_construction_costs(player, welt->get_settings().cst_transformer, get_pos().get_2d(), powerline_wt); } pumpe_t::~pumpe_t() { if(fab) { fab->remove_transformer_connected(this); fab = NULL; } if( net != NULL ) { net->sub_supply(power_supply); } } sync_result pumpe_t::sync_step(uint32 delta_t) { if( fab == NULL ) { return SYNC_DELETE; } else if( delta_t == 0 ) { return SYNC_OK; } // resolve image uint16 winter_offset = 0; if( skinverwaltung_t::senke->get_count() > 3 && (get_pos().z >= welt->get_snowline() || welt->get_climate( get_pos().get_2d() ) == arctic_climate) ) { winter_offset = 2; } uint16 const image_offset = power_supply > 0 ? 1 : 0; image_id const new_image = skinverwaltung_t::pumpe->get_image_id(image_offset + winter_offset); // update image if( image != new_image ) { set_flag(obj_t::dirty); set_image(new_image); } return SYNC_OK; } void pumpe_t::set_net(powernet_t * p) { powernet_t * p_old = get_net(); if( p_old != NULL ) { p_old->sub_supply(power_supply); } leitung_t::set_net(p); if( p != NULL ) { p->add_supply(power_supply); } } void pumpe_t::set_power_supply(uint32 newsupply) { // update power network powernet_t *const p = get_net(); if( p != NULL ) { p->sub_supply(power_supply); p->add_supply(newsupply); } power_supply = newsupply; } sint32 pumpe_t::get_power_consumption() const { powernet_t const *const p = get_net(); return p->get_normal_demand(); } void pumpe_t::rdwr(loadsave_t * file) { xml_tag_t d( file, "pumpe_t" ); leitung_t::rdwr(file); // current power state if( file->is_version_atleast(120, 4) ) { file->rdwr_long(power_supply); } } void pumpe_t::finish_rd() { leitung_t::finish_rd(); assert(get_net()); if( fab==NULL ) { if(welt->lookup(get_pos())->ist_karten_boden()) { // on surface, check around fab = leitung_t::suche_fab_4(get_pos().get_2d()); } else { // underground, check directly above fab = fabrik_t::get_fab(get_pos().get_2d()); } if( fab ) { // only add when factory there fab->add_transformer_connected(this); } } #ifdef MULTI_THREAD pthread_mutex_lock( &calc_image_mutex ); set_image(skinverwaltung_t::pumpe->get_image_id(0)); is_crossing = false; pthread_mutex_unlock( &calc_image_mutex ); #else set_image(skinverwaltung_t::pumpe->get_image_id(0)); is_crossing = false; #endif } void pumpe_t::info(cbuffer_t & buf) const { translator::get_obj_info(buf, desc->get_name()); obj_t::info( buf ); buf.printf(translator::translate("Net ID: %p"), get_net()); buf.printf("\n"); buf.printf(translator::translate("Generation: %.0f MW"), (double)(power_supply >> POWER_TO_MW)); buf.printf("\n"); buf.printf(translator::translate("Usage: %.0f %%"), (double)((100 * get_net()->get_normal_demand()) >> powernet_t::FRACTION_PRECISION)); buf.printf("\n"); // pad for consistent dialog size } /************************************ Distriubtion Transformer Code ********************************************/ freelist_iter_tpl senke_t::sl; uint32 senke_t::payment_timer = 0; bool senke_t::payout = false; void senke_t::new_world() { payment_timer = 0; payout = false; } void senke_t::static_rdwr(loadsave_t *file) { if( file->is_version_atleast(120, 4) ) { file->rdwr_long(payment_timer); } } void senke_t::sync_handler(uint32 delta_t) { // payment period (could be tied to game setting) const uint32 pay_period = PRODUCTION_DELTA_T * 10; // 10 seconds // revenue payout timer payment_timer += delta_t; payout = payment_timer >= pay_period; payment_timer %= pay_period; sl.sync_step(delta_t); } senke_t::senke_t(loadsave_t *file) : leitung_t( koord3d::invalid, NULL ) { fab = NULL; delta_t_sum = 0; next_t = 0; power_demand = 0; energy_acc = 0; is_transformer = true; rdwr( file ); } senke_t::senke_t(koord3d pos, player_t *player) : leitung_t(pos, player) { fab = NULL; delta_t_sum = 0; next_t = 0; power_demand = 0; energy_acc = 0; is_transformer = true; player_t::book_construction_costs(player, welt->get_settings().cst_transformer, get_pos().get_2d(), powerline_wt); } senke_t::~senke_t() { // one last final income pay_revenue(); if(fab!=NULL) { fab->remove_transformer_connected(this); fab = NULL; } if( net != NULL ) { net->sub_demand(power_demand); } } void senke_t::pay_revenue() { // megajoules (megawatt seconds) per cent const uint64 mjpc = (1 << (POWER_TO_MW-10)) * welt->get_settings().cst_kw_per_credit; // calculate payment in cent const sint64 payment = (sint64)(energy_acc / mjpc); // make payment if( payment > 0 ) { // enough has accumulated for a payment get_owner()->book_revenue( payment, get_pos().get_2d(), powerline_wt ); // remove payment from accumulator energy_acc %= mjpc; } } void senke_t::set_net(powernet_t * p) { powernet_t * p_old = get_net(); if( p_old != NULL ) { p_old->sub_demand(power_demand); } leitung_t::set_net(p); if( p != NULL ) { p->add_demand(power_demand); } } void senke_t::set_power_demand(uint32 newdemand) { // update power network powernet_t *const p = get_net(); if( p != NULL ) { p->sub_demand(power_demand); p->add_demand(newdemand); } power_demand = newdemand; } sint32 senke_t::get_power_satisfaction() const { powernet_t const *const p = get_net(); return p->get_normal_supply(); } sync_result senke_t::sync_step(uint32 delta_t) { if(fab==NULL) { return SYNC_DELETE; } // advance timers delta_t_sum += delta_t; next_t += delta_t; // change graphics at most 16 times a second if( next_t > PRODUCTION_DELTA_T / 16 ) { // enforce timer periods delta_t_sum %= PRODUCTION_DELTA_T; // 1 second next_t %= PRODUCTION_DELTA_T / 16; // 1/16 seconds // determine pwm period for image change uint32 pwm_period = 0; const sint32 satisfaction = get_net()->get_normal_supply(); if( satisfaction >= 1 << powernet_t::FRACTION_PRECISION ) { // always on pwm_period = PRODUCTION_DELTA_T; } else if( satisfaction >= ((7 << powernet_t::FRACTION_PRECISION) / 8) ) { // limit to at most 7/8 of a second pwm_period = 7 * PRODUCTION_DELTA_T / 8; } else if( satisfaction > ((1 << powernet_t::FRACTION_PRECISION) / 8) ) { // duty cycle based on power satisfaction pwm_period = (uint32)(((uint64)PRODUCTION_DELTA_T * (uint64)satisfaction) >> powernet_t::FRACTION_PRECISION); } else if( satisfaction > 0 ) { // limit to at least 1/8 of a second pwm_period = PRODUCTION_DELTA_T / 8; } // determine image with PWM logic const uint16 work_offset = (delta_t_sum < pwm_period) ? 1 : 0; // apply seasonal image offset uint16 winter_offset = 0; if( skinverwaltung_t::senke->get_count() > 3 && (get_pos().z >= welt->get_snowline() || welt->get_climate(get_pos().get_2d()) == arctic_climate) ) { winter_offset = 2; } // update displayed image image_id new_image = skinverwaltung_t::senke->get_image_id(work_offset + winter_offset); if( image != new_image ) { set_flag( obj_t::dirty ); set_image( new_image ); } } // energy metering logic energy_acc += ((uint64)power_demand * (uint64)get_net()->get_normal_supply() * (uint64)delta_t) / ((uint64)PRODUCTION_DELTA_T << powernet_t::FRACTION_PRECISION); if (payout) { pay_revenue(); } return SYNC_OK; } void senke_t::rdwr(loadsave_t *file) { xml_tag_t d( file, "senke_t" ); leitung_t::rdwr(file); // current power state if( file->is_version_atleast(120, 4) ) { file->rdwr_longlong((sint64 &)energy_acc); file->rdwr_long(power_demand); } else if (file->is_loading()) { energy_acc = 0; power_demand = 0; } if (file->is_version_atleast(122, 1)) { file->rdwr_long(delta_t_sum); file->rdwr_long(next_t); } else if (file->is_loading()) { delta_t_sum = 0; next_t = 0; } } void senke_t::finish_rd() { leitung_t::finish_rd(); assert(get_net()); if( fab==NULL ) { if(welt->lookup(get_pos())->ist_karten_boden()) { // on surface, check around fab = leitung_t::suche_fab_4(get_pos().get_2d()); } else { // underground, check directly above fab = fabrik_t::get_fab(get_pos().get_2d()); } if( fab ) { fab->add_transformer_connected(this); } } #ifdef MULTI_THREAD pthread_mutex_lock( &calc_image_mutex ); #endif set_image(skinverwaltung_t::senke->get_image_id(0)); is_crossing = false; #ifdef MULTI_THREAD pthread_mutex_unlock( &calc_image_mutex ); #endif } void senke_t::info(cbuffer_t & buf) const { translator::get_obj_info(buf, desc->get_name()); obj_t::info(buf); buf.printf(translator::translate("Net ID: %p"), get_net()); buf.printf("\n"); buf.printf(translator::translate("Demand: %.0f MW"), (double)(power_demand >> POWER_TO_MW)); buf.printf("\n"); buf.printf(translator::translate("Supplied: %.0f %%"), (double)((100 * get_net()->get_normal_supply()) >> powernet_t::FRACTION_PRECISION)); buf.printf("\n"); // pad for consistent dialog size } simutrans-124.5/src/simutrans/obj/leitung2.h000066400000000000000000000145361520675460500210650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_LEITUNG2_H #define OBJ_LEITUNG2_H #include "../dataobj/koord3d.h" #include "../dataobj/ribi.h" #include "simobj.h" #include "../tpl/freelist_iter_tpl.h" // bitshift for converting internal power values to MW for display extern const uint32 POWER_TO_MW; #define CREDIT_PER_MWS 2 class powernet_t; class player_t; class fabrik_t; class way_desc_t; class leitung_t : public obj_t { protected: image_id image; // powerline over ways bool is_crossing:1; bool is_transformer:1; // direction of the next pylon ribi_t::ribi ribi:4; /** * We are part of this network */ powernet_t * net; const way_desc_t *desc; /** * Connect this piece of powerline to its neighbours * -> this can merge power networks */ void verbinde(); void replace(powernet_t* neu); void add_ribi(ribi_t::ribi r) { ribi |= r; } /** * Dient zur Neuberechnung des Bildes */ void calc_image() OVERRIDE; public: // number of fractional bits for network load values static const uint8 FRACTION_PRECISION; powernet_t* get_net() const { return net; } /** * Changes the currently registered power net. * Can be overwritten to modify the power net on change. */ virtual void set_net(powernet_t* p) { net = p; } const way_desc_t * get_desc() { return desc; } void set_desc(const way_desc_t *new_desc) { desc = new_desc; } int gimme_neighbours(leitung_t **conn); static fabrik_t * suche_fab_4(koord pos); leitung_t(loadsave_t *file); leitung_t(koord3d pos, player_t *player); virtual ~leitung_t(); // just book the costs for destruction void cleanup(player_t *) OVERRIDE; // for map rotation void rotate90() OVERRIDE; typ get_typ() const OVERRIDE { return leitung; } const char *get_name() const OVERRIDE {return "Leitung"; } /** * waytype associated with this object */ waytype_t get_waytype() const OVERRIDE { return powerline_wt; } /// @copydoc obj_t::info void info(cbuffer_t & buf) const OVERRIDE; ribi_t::ribi get_ribi() const { return ribi; } inline void set_image( image_id b ) { image = b; } image_id get_image() const OVERRIDE {return is_crossing ? IMG_EMPTY : image;} image_id get_front_image() const OVERRIDE {return is_crossing ? image : IMG_EMPTY;} /** * Recalculates the images of all neighbouring * powerlines and the powerline itself */ void calc_neighbourhood(); void rdwr(loadsave_t *file) OVERRIDE; void finish_rd() OVERRIDE; /** * @return NULL if OK, otherwise an error message */ const char *get_removal_error(const player_t *player) OVERRIDE; /** * @return maintenance of this object (powerline or transformer) */ sint64 get_maintenance() const; }; class pumpe_t : public leitung_t { private: fabrik_t *fab; // The power supplied through the transformer. uint32 power_supply; static freelist_iter_tpl pl; // if not declared static, it would consume 4 bytes due to empty class nonzero rules public: pumpe_t(loadsave_t *file); pumpe_t(koord3d pos, player_t *player); ~pumpe_t(); void* operator new(size_t) { return pl.gimme_node(); } void operator delete(void* p) { return pl.putback_node(p); } static void sync_handler(uint32 delta_t) { pl.sync_step(delta_t); } sync_result sync_step(uint32 delta_t); void set_net(powernet_t* p) OVERRIDE; /** * Set the power supply of the transformer. */ void set_power_supply(uint32 newsupply); /** * Get the power supply of the transformer. */ uint32 get_power_supply() const {return power_supply;} /** * Get the normalized satisfaction value of the power consumed, updated every tick. * Return value is fixed point with FRACTION_PRECISION fractional bits. */ sint32 get_power_consumption() const; typ get_typ() const OVERRIDE { return pumpe; } const char *get_name() const OVERRIDE {return "Aufspanntransformator";} void info(cbuffer_t & buf) const OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; void finish_rd() OVERRIDE; void calc_image() OVERRIDE {} // empty; otherwise it will change to leitung const fabrik_t* get_factory() const { return fab; } }; /* * Distribution transformers act as an interface between power networks and * and power consuming factories. */ class senke_t : public leitung_t { private: // Timer for global power payment. static uint32 payment_timer; fabrik_t *fab; // Pwm timer for duty cycling image. uint32 delta_t_sum; // Timer for recalculating image. uint32 next_t; // The power requested through the transformer. uint32 power_demand; // Energy accumulator (how much energy has been metered). uint64 energy_acc; void step(uint32 delta_t); // Pay out revenue for the energy metered. void pay_revenue(); static freelist_iter_tpl sl; // if not declared static, it would consume 4 bytes due to empty class nonzero rules // true, needs payout static bool payout; public: static void new_world(); /** * Read and write static state. * This is used to make payments occur at the same time after load as after saving. */ static void static_rdwr(loadsave_t* file); senke_t(loadsave_t *file); senke_t(koord3d pos, player_t *player); ~senke_t(); void* operator new(size_t) { return sl.gimme_node(); } void operator delete(void* p) { return sl.putback_node(p); } static void sync_handler(uint32 delta_t); void set_net(powernet_t* p) OVERRIDE; typ get_typ() const OVERRIDE { return senke; } /** * Set the power demand of the transformer. */ void set_power_demand(uint32 newdemand); /** * Get the power demand of the transformer. */ uint32 get_power_demand() const {return power_demand;} /** * Get the normalized satisfaction value of the power demand, updated every tick. * Return value is fixed point with FRACTION_PRECISION fractional bits. */ sint32 get_power_satisfaction() const; /** * Used to alternate between displaying power on and power off images. * Frequency determined by the percentage of power supplied. * Gives players a visual indication of a power network with insufficient generation. */ sync_result sync_step(uint32 delta_t); const char *get_name() const OVERRIDE {return "Abspanntransformator";} void info(cbuffer_t & buf) const OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; void finish_rd() OVERRIDE; void calc_image() OVERRIDE {} // empty; otherwise it will change to leitung const fabrik_t* get_factory() const { return fab; } }; #endif simutrans-124.5/src/simutrans/obj/pillar.cc000066400000000000000000000076301520675460500207520ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simworld.h" #include "simobj.h" #include "../simmem.h" #include "../display/simimg.h" #include "../builder/brueckenbauer.h" #include "../descriptor/bridge_desc.h" #include "../ground/grund.h" #include "../dataobj/loadsave.h" #include "pillar.h" #include "bruecke.h" #include "../dataobj/environment.h" pillar_t::pillar_t(loadsave_t *file) : obj_t() { desc = NULL; asymmetric = false; rdwr(file); } pillar_t::pillar_t( koord3d pos, player_t *player, const bridge_desc_t *desc, bridge_desc_t::img_t img, int hoehe ) : obj_t( pos ) { this->desc = desc; this->dir = (uint8)img; set_yoff( -hoehe ); set_owner( player ); asymmetric = desc->has_pillar_asymmetric(); calc_image(); } void pillar_t::calc_image() { bool hide = false; int height = get_yoff(); if( grund_t *gr = welt->lookup(get_pos()) ) { slope_t::type slope = gr->get_grund_hang(); if( desc->has_pillar_asymmetric() ) { if( dir == bridge_desc_t::NS_Pillar ) { height += ( (corner_sw(slope) + corner_se(slope) ) * TILE_HEIGHT_STEP )/2; } else { height += ( ( corner_se(slope) + corner_ne(slope) ) * TILE_HEIGHT_STEP ) / 2; } if( height > 0 ) { hide = true; } } else { // on slope use mean height ... height += ( ( corner_se(slope) + corner_ne(slope) + corner_sw(slope) + corner_se(slope) ) * TILE_HEIGHT_STEP ) / 4; } } image = hide ? IMG_EMPTY : desc->get_background( (bridge_desc_t::img_t)dir, get_pos().z-height/TILE_HEIGHT_STEP >= welt->get_snowline() || welt->get_climate( get_pos().get_2d() ) == arctic_climate ); } /** * Einen Beschreibungsstring fuer das Objekt, der z.B. in einem * Beobachtungsfenster angezeigt wird. */ void pillar_t::show_info() { planquadrat_t *plan=welt->access(get_pos().get_2d()); for(unsigned i=0; iget_boden_count(); i++ ) { grund_t *bd=plan->get_boden_bei(i); if(bd->ist_bruecke()) { bruecke_t* br = bd->find(); if(br && br->get_desc()==desc) { br->show_info(); } } } } void pillar_t::rdwr(loadsave_t *file) { xml_tag_t p( file, "pillar_t" ); obj_t::rdwr(file); if(file->is_saving()) { const char *s = desc->get_name(); file->rdwr_str(s); file->rdwr_byte(dir); } else { char s[256]; file->rdwr_str(s, lengthof(s)); file->rdwr_byte(dir); desc = bridge_builder_t::get_desc(s); if(desc==0) { if(strstr(s,"ail")) { desc = bridge_builder_t::get_desc("ClassicRail"); dbg->warning("pillar_t::rdwr()","Unknown bridge %s replaced by ClassicRail",s); } else if(strstr(s,"oad")) { desc = bridge_builder_t::get_desc("ClassicRoad"); dbg->warning("pillar_t::rdwr()","Unknown bridge %s replaced by ClassicRoad",s); } } asymmetric = desc && desc->has_pillar_asymmetric(); if( file->is_version_less(112, 7) && env_t::pak_height_conversion_factor==2 ) { switch(dir) { case bridge_desc_t::OW_Pillar: dir = bridge_desc_t::OW_Pillar2; break; case bridge_desc_t::NS_Pillar: dir = bridge_desc_t::NS_Pillar2; break; } } } } void pillar_t::rotate90() { // since we may have a "3D" offset from the slope, we must remove it beofer rotation sint8 hoff = get_yoff(); set_yoff(0); obj_t::rotate90(); set_yoff(hoff); // may need to hide/show asymmetric pillars // this is done now in calc_image, which is called after karte_t::rotate anyway // we cannot decide this here, since welt->lookup(get_pos())->get_grund_hang() cannot be called // since we are in the middle of the rotation process // the rotated image parameter is just one in front/back switch(dir) { case bridge_desc_t::NS_Pillar: dir=bridge_desc_t::OW_Pillar ; break; case bridge_desc_t::OW_Pillar: dir=bridge_desc_t::NS_Pillar ; break; case bridge_desc_t::NS_Pillar2: dir=bridge_desc_t::OW_Pillar2 ; break; case bridge_desc_t::OW_Pillar2: dir=bridge_desc_t::NS_Pillar2 ; break; } } simutrans-124.5/src/simutrans/obj/pillar.h000066400000000000000000000024431520675460500206110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_PILLAR_H #define OBJ_PILLAR_H #include "simobj.h" #include "../descriptor/bridge_desc.h" class loadsave_t; class karte_t; /** * Bridge piece (visible) */ class pillar_t : public obj_t { const bridge_desc_t *desc; uint8 dir; bool asymmetric; image_id image; protected: void rdwr(loadsave_t *file) OVERRIDE; public: pillar_t(loadsave_t *file); pillar_t(koord3d pos, player_t *player, const bridge_desc_t *desc, bridge_desc_t::img_t img, int hoehe); const char* get_name() const OVERRIDE { return "Pillar"; } typ get_typ() const OVERRIDE { return obj_t::pillar; } const bridge_desc_t* get_desc() const { return desc; } image_id get_image() const OVERRIDE { return asymmetric ? IMG_EMPTY : image; } // asymmetric pillars are placed at the southern/eastern boundary of the tile // thus the images have to be displayed after vehicles image_id get_front_image() const OVERRIDE { return asymmetric ? image : IMG_EMPTY;} // needs to check for hiding asymmetric pillars void calc_image() OVERRIDE; /** * Einen Beschreibungsstring fuer das Objekt, der z.B. in einem * Beobachtungsfenster angezeigt wird. */ void show_info() OVERRIDE; void rotate90() OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/roadsign.cc000066400000000000000000000524771520675460500213060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simunits.h" #include "../simdebug.h" #include "simobj.h" #include "../display/simimg.h" #include "../player/simplay.h" #include "../tool/simtool.h" #include "../world/simworld.h" #include "../descriptor/roadsign_desc.h" #include "../descriptor/skin_desc.h" #include "../ground/grund.h" #include "../obj/way/strasse.h" #include "../dataobj/loadsave.h" #include "../dataobj/scenario.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/pakset_manager.h" #include "../gui/trafficlight_info.h" #include "../gui/privatesign_info.h" #include "../gui/tool_selector.h" #include "../tpl/stringhashtable_tpl.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include "roadsign.h" freelist_tpl roadsign_t::rs; const roadsign_desc_t *roadsign_t::default_signal=NULL; stringhashtable_tpl roadsign_t::table; roadsign_t::roadsign_t(loadsave_t *file) : obj_t () { image = foreground_image = IMG_EMPTY; preview = false; rdwr(file); if(desc) { /* if more than one state, we will switch direction and phase for traffic lights * however also gate signs need indications */ automatic = (desc->get_count()>4 && desc->get_wtyp()==road_wt) || (desc->get_count()>2 && desc->is_private_way()); } else { automatic = false; } // some sve had rather strange entries in state if( !automatic || desc==NULL ) { state = 0; } // only traffic light need switches if( automatic ) { welt->sync_roadsigns.add(this); } } roadsign_t::roadsign_t(player_t *player, koord3d pos, ribi_t::ribi dir, const roadsign_desc_t *desc, bool preview) : obj_t(pos) { this->desc = desc; this->dir = dir; this->preview = preview; image = foreground_image = IMG_EMPTY; state = 0; ticks_ns = ticks_ow = 16; ticks_offset = 0; ticks_yellow_ns = ticks_yellow_ow = 2; set_owner( player ); if( desc->is_private_way() ) { // init ownership of private ways ticks_offset = ticks_ow = 0; if( player->get_player_nr() >= 8 ) { ticks_ow = 1 << (player->get_player_nr()-8); } else { ticks_offset = 1 << player->get_player_nr(); } } if( desc->is_signal_type() ) { set_two_ways(false); } /* if more than one state, we will switch direction and phase for traffic lights * however also gate signs need indications */ automatic = (desc->get_count()>4 && desc->get_wtyp()==road_wt) || (desc->get_count()>2 && desc->is_private_way()); // only traffic light need switches if( automatic ) { welt->sync_roadsigns.add(this); } } roadsign_t::~roadsign_t() { if( desc ) { const grund_t *gr = welt->lookup(get_pos()); waytype_t finance_waytype = desc->get_waytype(); if(gr) { weg_t *weg = gr->get_weg(desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt); if (weg) { finance_waytype = weg->get_waytype(); if (!preview) { if (desc->is_single_way() || desc->is_signal_type()) { // signal removed, remove direction mask weg->set_ribi_maske(ribi_t::none); } weg->clear_sign_flag(); } } else { dbg->error("roadsign_t::~roadsign_t()","roadsign_t %p was deleted but ground has no way of type %d!", this, desc->get_wtyp() ); } } player_t::add_maintenance( this->get_owner(), -desc->get_maintenance(), finance_waytype); } if (automatic) { welt->sync_roadsigns.remove(this); } } void roadsign_t::set_dir(ribi_t::ribi dir) { ribi_t::ribi olddir = this->dir; this->dir = dir; if (!preview) { weg_t *weg = welt->lookup(get_pos())->get_weg(desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt); if( desc->get_wtyp()!=track_wt && desc->get_wtyp()!=monorail_wt && desc->get_wtyp()!=maglev_wt && desc->get_wtyp()!=narrowgauge_wt ) { weg->count_sign(); } if(desc->is_single_way() || (desc->is_signal_type() && !get_two_ways())) { // set mask, if it is a single way ... weg->count_sign(); weg->set_ribi_maske(calc_mask()); DBG_MESSAGE("roadsign_t::set_dir()","ribi %i",dir); } } // force redraw mark_image_dirty(get_image(),0); // some more magic to get left side images right ... sint8 old_x = get_xoff(); set_xoff( after_xoffset ); mark_image_dirty(foreground_image,after_yoffset-get_yoff()); set_xoff( old_x ); image = IMG_EMPTY; foreground_image = IMG_EMPTY; calc_image(); if (preview) this->dir = olddir; } void roadsign_t::set_two_ways(bool yesno) { ticks_ns = yesno ? 1 : 0; if(desc->is_signal_type() && !preview) { // Mask needs a change weg_t *weg = welt->lookup(get_pos())->get_weg(desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt); if (weg) { weg->count_sign(); weg->set_ribi_maske(yesno ? (ribi_t::ribi)ribi_t::none : calc_mask()); } } } void roadsign_t::show_info() { if( desc->is_private_way() ) { create_win(new privatesign_info_t(this), w_info, (ptrdiff_t)this ); } else if( automatic ) { create_win(new trafficlight_info_t(this), w_info, (ptrdiff_t)this ); } else { obj_t::show_info(); } } void roadsign_t::info(cbuffer_t & buf) const { if( !desc->is_private_way() ) { buf.append(translator::translate("Roadsign")); buf.append("\n"); if(desc->is_single_way()) { buf.append(translator::translate("\nsingle way")); } if(desc->get_min_speed()!=0) { buf.printf("%s%d", translator::translate("\nminimum speed:"), speed_to_kmh(desc->get_min_speed())); } buf.printf("%s%s\n", translator::translate("\ndirection:"), ribi_t::names[dir]); if( automatic ) { buf.append(translator::translate("\nSet phases:")); } if (!automatic) { if (char const* const maker = desc->get_copyright()) { buf.append("\n"); buf.printf(translator::translate("Constructed by %s"), maker); } // extra treatment of trafficlights & private signs, author will be shown by those windows themselves } } } // could be still better aligned for drive_left settings ... void roadsign_t::calc_image() { set_flag(obj_t::dirty); // vertical offset of the signal positions const grund_t *gr=welt->lookup(get_pos()); if(gr==NULL) { return; } after_xoffset = 0; after_yoffset = 0; sint8 xoff = 0, yoff = 0; // left offsets defined, and image-on-the-left activated const bool left_offsets = desc->get_offset_left() && ( (desc->get_wtyp()==road_wt && welt->get_settings().is_drive_left() ) || (desc->get_wtyp()!=air_wt && desc->get_wtyp()!=road_wt && welt->get_settings().is_signals_left()) ); const slope_t::type full_hang = gr->get_weg_hang(); const sint8 hang_diff = slope_t::max_diff(full_hang); const ribi_t::ribi hang_dir = ribi_t::backward( ribi_type(full_hang) ); // private way have also closed/open states if( desc->is_private_way() ) { uint8 image = 1-(dir&1); if( (1<get_active_player_nr()) & get_player_mask() ) { // gate open image += 2; } set_image( desc->get_image_id(image) ); set_yoff( 0 ); if( hang_diff ) { set_yoff( -(TILE_HEIGHT_STEP*hang_diff)/2 ); } else { set_yoff( -gr->get_weg_yoff() ); } foreground_image = IMG_EMPTY; return; } if( hang_diff == 0 ) { yoff = -gr->get_weg_yoff(); after_yoffset = yoff; } else { // since the places were switched const ribi_t::ribi test_hang = left_offsets ? ribi_t::backward(hang_dir) : hang_dir; if(test_hang==ribi_t::east || test_hang==ribi_t::north) { yoff = -TILE_HEIGHT_STEP*hang_diff; after_yoffset = 0; } else { yoff = 0; after_yoffset = -TILE_HEIGHT_STEP*hang_diff; } } image_id tmp_image=IMG_EMPTY; if(!automatic) { assert( state==0 ); foreground_image = IMG_EMPTY; ribi_t::ribi temp_dir = dir; if( gr->get_typ()==grund_t::tunnelboden && gr->ist_karten_boden() && (grund_t::underground_mode==grund_t::ugm_none || (grund_t::underground_mode==grund_t::ugm_level && gr->get_hoehe()get_grund_hang()) ); if( tunnel_hang_dir==ribi_t::east || tunnel_hang_dir==ribi_t::north ) { temp_dir &= ~ribi_t::southwest; } else { temp_dir &= ~ribi_t::northeast; } } // signs for left side need other offsets and other front/back order if( left_offsets ) { const sint16 XOFF = 2*desc->get_offset_left(); const sint16 YOFF = desc->get_offset_left(); if(temp_dir&ribi_t::east) { tmp_image = desc->get_image_id(3); xoff += XOFF; yoff += -YOFF; } if(temp_dir&ribi_t::north) { if(tmp_image!=IMG_EMPTY) { foreground_image = desc->get_image_id(0); after_xoffset += -XOFF; after_yoffset += -YOFF; } else { tmp_image = desc->get_image_id(0); xoff += -XOFF; yoff += -YOFF; } } if(temp_dir&ribi_t::west) { foreground_image = desc->get_image_id(2); after_xoffset += -XOFF; after_yoffset += YOFF; } if(temp_dir&ribi_t::south) { if(foreground_image!=IMG_EMPTY) { tmp_image = desc->get_image_id(1); xoff += XOFF; yoff += YOFF; } else { foreground_image = desc->get_image_id(1); after_xoffset += XOFF; after_yoffset += YOFF; } } } else { if(temp_dir&ribi_t::east) { foreground_image = desc->get_image_id(3); } if(temp_dir&ribi_t::north) { if(foreground_image!=IMG_EMPTY) { tmp_image = desc->get_image_id(0); } else { foreground_image = desc->get_image_id(0); } } if(temp_dir&ribi_t::west) { tmp_image = desc->get_image_id(2); } if(temp_dir&ribi_t::south) { if(tmp_image!=IMG_EMPTY) { foreground_image = desc->get_image_id(1); } else { tmp_image = desc->get_image_id(1); } } } // some signs on roads must not have a background (but then they have only two rotations) if( desc->get_flags()&roadsign_desc_t::ONLY_BACKIMAGE ) { if(foreground_image!=IMG_EMPTY) { tmp_image = foreground_image; } foreground_image = IMG_EMPTY; } } else { // traffic light weg_t *str=gr->get_weg(road_wt); if(str) { const uint8 weg_dir = str->get_ribi_unmasked(); const uint8 direction = desc->get_count()>16 ? (state&2) + ((state+1) & 1) : (state+1) & 1; // other front/back images for left side ... if( left_offsets ) { const sint16 XOFF = 2*desc->get_offset_left(); const sint16 YOFF = desc->get_offset_left(); if(weg_dir&ribi_t::north) { if(weg_dir&ribi_t::east) { foreground_image = desc->get_image_id(6+direction*8); after_xoffset += 0; after_yoffset += 0; } else { foreground_image = desc->get_image_id(1+direction*8); after_xoffset += XOFF; after_yoffset += YOFF; } } else if(weg_dir&ribi_t::east) { foreground_image = desc->get_image_id(2+direction*8); after_xoffset += -XOFF; after_yoffset += YOFF; } if(weg_dir&ribi_t::west) { if(weg_dir&ribi_t::south) { tmp_image = desc->get_image_id(7+direction*8); xoff += 0; yoff += 0; } else { tmp_image = desc->get_image_id(3+direction*8); xoff += XOFF; yoff += -YOFF; } } else if(weg_dir&ribi_t::south) { tmp_image = desc->get_image_id(0+direction*8); xoff += -XOFF; yoff += -YOFF; } } else { // drive right ... if(weg_dir&ribi_t::south) { if(weg_dir&ribi_t::east) { foreground_image = desc->get_image_id(4+direction*8); } else { foreground_image = desc->get_image_id(0+direction*8); } } else if(weg_dir&ribi_t::east) { foreground_image = desc->get_image_id(2+direction*8); } if(weg_dir&ribi_t::west) { if(weg_dir&ribi_t::north) { tmp_image = desc->get_image_id(5+direction*8); } else { tmp_image = desc->get_image_id(3+direction*8); } } else if(weg_dir&ribi_t::north) { tmp_image = desc->get_image_id(1+direction*8); } } } } // set image and offsets set_image( tmp_image ); set_xoff( xoff ); set_yoff( yoff ); } // only used for traffic light: change the current state sync_result roadsign_t::sync_step(uint32 /*delta_t*/) { if( desc->is_private_way() ) { uint8 image = 1-(dir&1); if( (1<get_active_player_nr()) & get_player_mask() ) { // gate open image += 2; // force redraw mark_image_dirty(get_image(),0); } set_image( desc->get_image_id(image) ); } else { if (grund_t* gr = welt->lookup(get_pos())) { ribi_t::ribi r = gr->get_weg_ribi_unmasked(road_wt); if ( ribi_t::none==r || ribi_t::is_single(r) || (ribi_t::is_twoway(r) && !ribi_t::is_straight(r)) ) { // not at least a straight road => remove the lights return SYNC_DELETE; } } else { return SYNC_DELETE; } // Must not overflow if ticks_ns+ticks_ow+ticks_yellow_ns+ticks_yellow_ow=256 uint32 ticks = ((welt->get_ticks()>>10)+ticks_offset) % ((uint32)ticks_ns+(uint32)ticks_ow+(uint32)ticks_yellow_ns+(uint32)ticks_yellow_ow); uint8 new_state = 0; //traffic light transition: e-w dir -> yellow e-w -> n-s dir -> yellow n-s -> ... if( ticks < ticks_ow ) { new_state = 0; } else if( ticks < ticks_ow+ticks_yellow_ow ) { new_state = 2; } else if( ticks < (uint32)ticks_ow+ticks_yellow_ow+ticks_ns ) { new_state = 1; } else { new_state = 3; } if(state!=new_state) { state = new_state; switch(new_state) { case 0: dir = ribi_t::northsouth; break; case 1: dir = ribi_t::eastwest; break; default: // yellow state dir = ribi_t::none; break; } calc_image(); } } return SYNC_OK; } void roadsign_t::rotate90() { // only meaningful for traffic lights obj_t::rotate90(); if(automatic && !desc->is_private_way()) { state = (state&2/*whether yellow*/) + ((state+1)&1); if( ticks_offset >= ticks_ns ) { ticks_offset -= ticks_ns; } else { ticks_offset += ticks_ow; } uint8 temp = ticks_ns; ticks_ns = ticks_ow; ticks_ow = temp; temp = ticks_yellow_ns; ticks_yellow_ns = ticks_yellow_ow; ticks_yellow_ow = temp; trafficlight_info_t *const trafficlight_win = dynamic_cast( win_get_magic( (ptrdiff_t)this ) ); if( trafficlight_win ) { trafficlight_win->update_data(); } } dir = ribi_t::rotate90( dir ); } // to correct offset on slopes #ifdef MULTI_THREAD void roadsign_t::display_after(int xpos, int ypos, const sint8 clip_num ) const #else void roadsign_t::display_after(int xpos, int ypos, bool ) const #endif { if( foreground_image != IMG_EMPTY ) { const int raster_width = gfx->get_current_tile_raster_width(); xpos += tile_raster_scale_x( after_xoffset, raster_width ); ypos += tile_raster_scale_y( after_yoffset, raster_width ); // draw with owner if( get_owner_nr() != PLAYER_UNOWNED ) { if( obj_t::show_owner ) { gfx->draw_blend( foreground_image, xpos, ypos, 0, gfx->palette_lookup(get_owner()->get_player_color1()+2) | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, get_flag(obj_t::dirty) CLIP_NUM_PAR); } else { gfx->draw_color( foreground_image, xpos, ypos, get_owner_nr(), true, get_flag(obj_t::dirty) CLIP_NUM_PAR); } } else { gfx->draw_normal( foreground_image, xpos, ypos, 0, true, get_flag(obj_t::dirty) CLIP_NUM_PAR); } } } void roadsign_t::rdwr(loadsave_t *file) { xml_tag_t r( file, "roadsign_t" ); obj_t::rdwr(file); uint8 dummy=0; if( file->is_version_less(102, 3) ) { file->rdwr_byte(dummy); if( file->is_loading() ) { ticks_ns = ticks_ow = 16; } } else { file->rdwr_byte(ticks_ns); file->rdwr_byte(ticks_ow); } if( file->is_version_atleast(110, 7) ) { file->rdwr_byte(ticks_offset); } else { if( file->is_loading() ) { ticks_offset = 0; } } if( file->is_version_atleast(122,2) ) { file->rdwr_byte(ticks_yellow_ns); file->rdwr_byte(ticks_yellow_ow); } else if( file->is_loading() ){ ticks_yellow_ns = ticks_yellow_ow = 2; } dummy = state; file->rdwr_byte(dummy); state = dummy; dummy = dir; file->rdwr_byte(dummy); dir = dummy; if(file->is_version_less(89, 0)) { dir = ribi_t::backward(dir); } if(file->is_saving()) { const char *s = desc->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); desc = roadsign_t::table.get(bname); if(desc==NULL) { desc = roadsign_t::table.get(translator::compatibility_name(bname)); if( desc==NULL ) { dbg->warning("roadsign_t::rwdr", "description %s for roadsign/signal at %d,%d not found! (may be ignored)", bname, get_pos().x, get_pos().y); pakset_manager_t::add_missing_paks( bname, MISSING_SIGN ); } else { dbg->warning("roadsign_t::rwdr", "roadsign/signal %s at %d,%d replaced by %s", bname, get_pos().x, get_pos().y, desc->get_name() ); } } // init ownership of private ways signs if( desc && desc->is_private_way() ) { if( file->is_version_less(124, 2) ) { // private sign mask now in ticks_ow and ticks_offset ticks_offset = ticks_ns; ticks_ns = 0xFF; } } if( desc && desc->is_signal_type() && ticks_ns >= 2 ) { ticks_ns = 0; } } } void roadsign_t::cleanup(player_t *player) { player_t::book_construction_costs(player, -desc->get_price(), get_pos().get_2d(), get_waytype()); } void roadsign_t::finish_rd() { grund_t *gr=welt->lookup(get_pos()); if( gr==NULL || !gr->hat_weg(desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt) ) { dbg->error("roadsign_t::finish_rd", "way/ground missing at (%i,%i); roadsign ignored", get_pos().x, get_pos().y); } else { // after loading restore directions set_dir(dir); weg_t *way = gr->get_weg(desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt); way->count_sign(); player_t::add_maintenance(this->get_owner(), desc->get_maintenance(), way->get_waytype()); } } // to sort compare_roadsign_desc for always the same menu order static bool compare_roadsign_desc(const roadsign_desc_t* a, const roadsign_desc_t* b) { int diff = a->get_wtyp() - b->get_wtyp(); if (diff == 0) { if(a->is_choose_sign()) { diff += 120; } if(b->is_choose_sign()) { diff -= 120; } diff += (int)(a->get_flags() & ~roadsign_desc_t::SIGN_SIGNAL) - (int)(b->get_flags() & ~roadsign_desc_t::SIGN_SIGNAL); } if (diff == 0) { diff = (sint32)a->get_intro_year_month() - (sint32)b->get_intro_year_month(); } if (diff == 0) { /* Same type and intro date: sort by name */ diff = strcmp(a->get_name(), b->get_name()); } return diff < 0; } /* static stuff from here on ... */ bool roadsign_t::successfully_loaded() { if(table.empty()) { DBG_MESSAGE("roadsign_t", "No signs found - feature disabled"); } return true; } bool roadsign_t::register_desc(roadsign_desc_t *desc) { // avoid duplicates with same name if(const roadsign_desc_t *old_desc = table.remove(desc->get_name())) { tool_t::general_tool.remove( old_desc->get_builder() ); delete old_desc->get_builder(); delete old_desc; } if( desc->get_cursor()->get_image_id(1)!=IMG_EMPTY ) { // add the tool tool_build_roadsign_t *tool = new tool_build_roadsign_t(); tool->set_icon( desc->get_cursor()->get_image_id(1) ); tool->cursor = desc->get_cursor()->get_image_id(0); tool->set_default_param(desc->get_name()); tool_t::general_tool.append( tool ); desc->set_builder( tool ); } else { desc->set_builder( NULL ); } roadsign_t::table.put(desc->get_name(), desc); if( desc->get_wtyp()==track_wt && desc->get_flags()==roadsign_desc_t::SIGN_SIGNAL ) { default_signal = desc; } return true; } /** * Fill menu with icons of given signals/roadsings from the list */ void roadsign_t::fill_menu(tool_selector_t *tool_selector, waytype_t wtyp, sint16 /*sound_ok*/) { // check if scenario forbids this if (!welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_ROADSIGN | GENERAL_TOOL, wtyp)) { return; } bool enable = welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_ROADSIGN | GENERAL_TOOL, wtyp, 0); const uint16 time = welt->get_timeline_year_month(); vector_tplmatching; for(auto const& i : table) { roadsign_desc_t const* const desc = i.value; // only add items with a cursor if( desc->is_available(time) && desc->get_wtyp()==wtyp && desc->get_builder() ) { if (welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_ROADSIGN | GENERAL_TOOL, wtyp, desc->get_name())) { matching.insert_ordered(desc, compare_roadsign_desc); } } } for(roadsign_desc_t const* const i : matching) { i->get_builder()->enabled = enable && welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_ROADSIGN | GENERAL_TOOL, wtyp, i->get_name()); tool_selector->add_tool_selector(i->get_builder()); } } /** * Finds a matching roadsign */ const roadsign_desc_t *roadsign_t::roadsign_search(roadsign_desc_t::types const flag, waytype_t const wt, uint16 const time) { for(auto const& i : table) { roadsign_desc_t const* const desc = i.value; if( desc->is_available(time) && desc->get_wtyp()==wt && desc->get_flags()==flag ) { return desc; } } return NULL; } const vector_tpl& roadsign_t::get_available_signs(const waytype_t wt) { static vector_tpl dummy; dummy.clear(); const uint16 time = welt->get_timeline_year_month(); for(auto const& i : table) { roadsign_desc_t const* const desc = i.value; if (desc->is_available(time) && desc->get_wtyp()==wt && desc->get_builder()) { dummy.append(desc); } } return dummy; } simutrans-124.5/src/simutrans/obj/roadsign.h000066400000000000000000000135621520675460500211400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_ROADSIGN_H #define OBJ_ROADSIGN_H #include "simobj.h" #include "../simtypes.h" #include "../descriptor/roadsign_desc.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/freelist_tpl.h" template class vector_tpl; class tool_selector_t; /** * road sign for traffic (one way minimum speed, traffic lights) */ class roadsign_t : public obj_t { protected: image_id image; image_id foreground_image; enum { SHOW_FRONT = 1, SHOW_BACK = 2, SWITCH_AUTOMATIC = 16 }; uint8 state:2; // counter for steps ... uint8 dir:4; uint8 automatic:1; uint8 preview:1; /* * Many things go in these... * Traffic lights -> intended usage * Private way signs -> bits go in ticks_ow (MSB) and ticks_iffset (LSB) * Signals -> two_ways state goes in ticks_ns */ uint8 ticks_ns; uint8 ticks_ow; uint8 ticks_yellow_ns, ticks_yellow_ow; uint8 ticks_offset; sint8 after_yoffset, after_xoffset; const roadsign_desc_t *desc; ribi_t::ribi calc_mask() const { return ribi_t::is_single(dir) ? dir : (ribi_t::ribi)ribi_t::none; } static freelist_tplrs; // if not declared static, it would consume 4 bytes due to empty class nonzero rules public: enum signalstate { STATE_RED = 0, STATE_GREEN = 1, STATE_YELLOW = 2 // next state is red }; /** * return direction or the state of the traffic light */ ribi_t::ribi get_dir() const { return dir; } /* * sets ribi mask of the sign * Caution: it will modify way ribis directly unless in preview mode! */ void set_dir(ribi_t::ribi dir); /* * Only makes sense for signals! */ bool get_two_ways() const { return ticks_ns != 0; } void set_two_ways(bool yesno); void set_state(signalstate z) {state = z; calc_image();} signalstate get_state() { return (signalstate)state; } typ get_typ() const OVERRIDE { return roadsign; } const char* get_name() const OVERRIDE { return "Roadsign"; } // assuming this is a private way sign uint16 get_player_mask() const { return (ticks_ow<<8)|ticks_offset; } /** * waytype associated with this object */ waytype_t get_waytype() const OVERRIDE { return desc ? desc->get_wtyp() : invalid_wt; } roadsign_t(loadsave_t *file); roadsign_t(player_t *player, koord3d pos, ribi_t::ribi dir, const roadsign_desc_t* desc, bool preview = false); const roadsign_desc_t *get_desc() const {return desc;} void* operator new(size_t) { return rs.gimme_node(); } void operator delete(void* p) { return rs.putback_node(p); } /** * signale muessen bei der destruktion von der * Blockstrecke abgemeldet werden */ ~roadsign_t(); // since traffic lights need their own window void show_info() OVERRIDE; /// @copydoc obj_t::info void info(cbuffer_t & buf) const OVERRIDE; /** * Calculate actual image */ void calc_image() OVERRIDE; // true, if a free route choose point (these are always single way the avoid recalculation of long return routes) bool is_free_route(uint8 check_dir) const { return desc->is_choose_sign() && check_dir == dir; } // changes the state of a traffic light sync_result sync_step(uint32); // change the phases of the traffic lights uint8 get_ticks_ns() const { return ticks_ns; } void set_ticks_ns(uint8 ns) { ticks_ns = ns; // To prevent overflow in ticks_offset when rotating if (ticks_ow > 256-ticks_ns - ticks_yellow_ns - ticks_yellow_ow ) { ticks_ow = 256-ticks_ns-ticks_yellow_ns-ticks_yellow_ow; } } uint8 get_ticks_ow() const { return ticks_ow; } void set_ticks_ow(uint8 ow) { ticks_ow = ow; // To prevent overflow in ticks_offset when rotating if (ticks_ns > 256-ticks_ow - ticks_yellow_ns-ticks_yellow_ow ) { ticks_ns = 256-ticks_ow-ticks_yellow_ns-ticks_yellow_ow; } } uint8 get_ticks_yellow_ns() const { return ticks_yellow_ns; } void set_ticks_yellow_ns(uint8 yellow) { ticks_yellow_ns = yellow; // To prevent overflow in ticks_offset when rotating if (ticks_yellow_ns > 256-ticks_ns - ticks_ow - ticks_yellow_ow) { ticks_yellow_ns = 256-ticks_ns-ticks_ow-ticks_yellow_ow; } } uint8 get_ticks_yellow_ow() const { return ticks_yellow_ow; } void set_ticks_yellow_ow(uint8 yellow) { ticks_yellow_ow = yellow; // To prevent overflow in ticks_offset when rotating if (ticks_yellow_ow > 256-ticks_ns - ticks_ow - ticks_yellow_ns) { ticks_yellow_ow = 256-ticks_ns-ticks_ow-ticks_yellow_ns; } } uint8 get_ticks_offset() const { return ticks_offset; } void set_ticks_offset(uint8 offset) { ticks_offset = offset; } inline void set_image( image_id b ) { image = b; } image_id get_image() const OVERRIDE { return image; } /** * For the front image hiding vehicles */ image_id get_front_image() const OVERRIDE { return foreground_image; } /** * draw the part overlapping the vehicles * (needed to get the right offset even on hills) */ #ifdef MULTI_THREAD void display_after(int xpos, int ypos, const sint8 clip_num) const OVERRIDE; #else void display_after(int xpos, int ypos, bool dirty) const OVERRIDE; #endif void rdwr(loadsave_t *file) OVERRIDE; void rotate90() OVERRIDE; // subtracts cost void cleanup(player_t *player) OVERRIDE; void finish_rd() OVERRIDE; // static routines from here private: static stringhashtable_tpl table; protected: static const roadsign_desc_t *default_signal; public: static bool register_desc(roadsign_desc_t *desc); static bool successfully_loaded(); /** * Fill menu with icons of given stops from the list */ static void fill_menu(tool_selector_t *tool_selector, waytype_t wtyp, sint16 sound_ok); static const roadsign_desc_t *roadsign_search(roadsign_desc_t::types flag, const waytype_t wt, const uint16 time); static const roadsign_desc_t *find_desc(const char *name) { return table.get(name); } static const vector_tpl& get_available_signs(const waytype_t wt); }; #endif simutrans-124.5/src/simutrans/obj/signal.cc000066400000000000000000000114101520675460500207330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../simdebug.h" #include "../world/simworld.h" #include "simobj.h" #include "../obj/way/schiene.h" #include "../ground/grund.h" #include "../display/simimg.h" #include "../dataobj/ribi.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../gui/signal_info.h" #include "../gui/simwin.h" #include "../utils/cbuffer.h" #include "signal.h" signal_t::signal_t(loadsave_t *file) : roadsign_t(file) { if(desc==NULL) { desc = roadsign_t::default_signal; } state = STATE_RED; } void signal_t::info(cbuffer_t & buf) const { translator::get_obj_info(buf, desc->get_name()); obj_t::info(buf); buf.printf("%s%s", translator::translate("\ndirection:")+1, ribi_t::names[get_dir()]); // copyright obmitted, signal dialog will show it } void signal_t::calc_image() { foreground_image = IMG_EMPTY; image_id image = IMG_EMPTY; after_xoffset = 0; after_yoffset = 0; sint8 xoff = 0, yoff = 0; const bool left_swap = welt->get_settings().is_signals_left() && desc->get_offset_left(); grund_t *gr = welt->lookup(get_pos()); if(gr) { const slope_t::type full_hang = gr->get_weg_hang(); const sint8 hang_diff = slope_t::max_diff(full_hang); const ribi_t::ribi hang_dir = ribi_t::backward( ribi_type(full_hang) ); set_flag(obj_t::dirty); weg_t *sch = gr->get_weg(desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt); if(sch) { uint16 offset=0; ribi_t::ribi dir = sch->get_ribi_unmasked() & (~calc_mask()); if(sch->is_electrified() && (desc->get_count()/8)>1) { offset = (desc->is_pre_signal() || desc->is_priority_signal()) ? 12 : 8; } // vertical offset of the signal positions if(full_hang==slope_t::flat) { yoff = -gr->get_weg_yoff(); after_yoffset = yoff; } else { const ribi_t::ribi test_hang = left_swap ? ribi_t::backward(hang_dir) : hang_dir; if(test_hang==ribi_t::east || test_hang==ribi_t::north) { yoff = -TILE_HEIGHT_STEP*hang_diff; after_yoffset = 0; } else { yoff = 0; after_yoffset = -TILE_HEIGHT_STEP*hang_diff; } } // and now calculate the images: // we need to hide the "second" image on tunnel entries ribi_t::ribi temp_dir = dir; if( gr->get_typ()==grund_t::tunnelboden && gr->ist_karten_boden() && (grund_t::underground_mode==grund_t::ugm_none || (grund_t::underground_mode==grund_t::ugm_level && gr->get_hoehe()get_grund_hang()) ); if( tunnel_hang_dir==ribi_t::east || tunnel_hang_dir==ribi_t::north ) { temp_dir &= ~ribi_t::southwest; } else { temp_dir &= ~ribi_t::northeast; } } // signs for left side need other offsets and other front/back order if( left_swap ) { const sint16 XOFF = 2*desc->get_offset_left(); const sint16 YOFF = desc->get_offset_left(); if(temp_dir&ribi_t::east) { image = desc->get_image_id(3+state*4+offset); xoff += XOFF; yoff += -YOFF; } if(temp_dir&ribi_t::north) { if(image!=IMG_EMPTY) { foreground_image = desc->get_image_id(0+state*4+offset); after_xoffset += -XOFF; after_yoffset += -YOFF; } else { image = desc->get_image_id(0+state*4+offset); xoff += -XOFF; yoff += -YOFF; } } if(temp_dir&ribi_t::west) { foreground_image = desc->get_image_id(2+state*4+offset); after_xoffset += -XOFF; after_yoffset += YOFF; } if(temp_dir&ribi_t::south) { if(foreground_image!=IMG_EMPTY) { image = desc->get_image_id(1+state*4+offset); xoff += XOFF; yoff += YOFF; } else { foreground_image = desc->get_image_id(1+state*4+offset); after_xoffset += XOFF; after_yoffset += YOFF; } } } else { if(temp_dir&ribi_t::east) { foreground_image = desc->get_image_id(3+state*4+offset); } if(temp_dir&ribi_t::north) { if(foreground_image==IMG_EMPTY) { foreground_image = desc->get_image_id(0+state*4+offset); } else { image = desc->get_image_id(0+state*4+offset); } } if(temp_dir&ribi_t::west) { image = desc->get_image_id(2+state*4+offset); } if(temp_dir&ribi_t::south) { if(image==IMG_EMPTY) { image = desc->get_image_id(1+state*4+offset); } else { foreground_image = desc->get_image_id(1+state*4+offset); } } } } } set_xoff( xoff ); set_yoff( yoff ); set_image(image); } void signal_t::show_info() { create_win( new signal_info_t( this ), w_info, (ptrdiff_t)this ); } simutrans-124.5/src/simutrans/obj/signal.h000066400000000000000000000015051520675460500206010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_SIGNAL_H #define OBJ_SIGNAL_H #include "roadsign.h" #include "simobj.h" /** * Signals for rail tracks. * * @see blockstrecke_t * @see blockmanager */ class signal_t : public roadsign_t { public: signal_t(loadsave_t *file); signal_t(player_t *player, koord3d pos, ribi_t::ribi dir,const roadsign_desc_t *desc, bool preview = false) : roadsign_t(player,pos,dir,desc,preview) { state = STATE_RED;} const roadsign_desc_t* get_desc() const { return desc; } /// @copydoc obj_t::info void info(cbuffer_t & buf) const OVERRIDE; typ get_typ() const OVERRIDE { return obj_t::signal; } const char *get_name() const OVERRIDE {return "Signal";} void show_info() OVERRIDE; void calc_image() OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/simobj.cc000066400000000000000000000217351520675460500207540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simobj.h" #include "baum.h" #include "../ground/grund.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../display/simgraph.h" #include "../display/simimg.h" #include "../display/viewport.h" #include "../player/simplay.h" #include "../gui/obj_info.h" #include "../gui/simwin.h" #include "../vehicle/vehicle_base.h" #include "../simcolor.h" #include "../simdebug.h" #include "../world/simworld.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include #include #include /** * Pointer to the world of this thing. Static to conserve space. * Change to instance variable once more than one world is available. */ karte_ptr_t obj_t::welt; bool obj_t::show_owner = false; void obj_t::init() { pos = koord3d::invalid; xoff = 0; yoff = 0; owner_n = PLAYER_UNOWNED; flags = no_flags; set_flag(dirty); } obj_t::obj_t() { init(); } obj_t::obj_t(koord3d pos) { init(); this->pos = pos; } // removes an object and tries to delete it also from the corresponding objlist obj_t::~obj_t() { destroy_win((ptrdiff_t)this); if(flags¬_on_map || !welt->is_within_limits(pos.get_2d())) { return; } // find object on the map and remove it grund_t *gr = welt->lookup(pos); if(!gr || !gr->obj_remove(this)) { // not found? => try harder at all map locations dbg->warning("obj_t::~obj_t()", "Could not remove %p from (%s)", (void *)this, pos.get_str()); // first: try different height ... gr = welt->access(pos.get_2d())->get_boden_von_obj(this); if(gr && gr->obj_remove(this)) { dbg->warning("obj_t::~obj_t()", "Removed %p from (%hi,%hi,%hhi), but it should have been on (%hi,%hi,%hhi)", (void *)this, gr->get_pos().x, gr->get_pos().y, gr->get_pos().z, pos.x, pos.y, pos.z); return; } // then search entire map koord k; for(k.y=0; k.yget_size().y; k.y++) { for(k.x=0; k.xget_size().x; k.x++) { grund_t *gr = welt->access(k)->get_boden_von_obj(this); if (gr && gr->obj_remove(this)) { dbg->warning("obj_t::~obj_t()", "Removed %p from (%hi,%hi,%hhi), but it should have been on (%hi,%hi,%hhi)", (void *)this, gr->get_pos().x, gr->get_pos().y, gr->get_pos().z, pos.x, pos.y, pos.z); return; } } } } } /** * sets owner of object */ void obj_t::set_owner(player_t *player) { int i = welt->sp2num(player); assert(i>=0); owner_n = (uint8)i; } player_t *obj_t::get_owner() const { return welt->get_player(owner_n); } /* the only general info we can give is the name * we want to format it nicely, * with two linebreaks at the end => thus the little extra effort */ void obj_t::info(cbuffer_t & buf) const { char translation[256]; char const* const owner = owner_n == 1 ? translator::translate("Eigenbesitz\n") : owner_n == PLAYER_UNOWNED ? "" : // was translator::translate("Kein Besitzer\n") : get_owner()->get_name(); tstrncpy(translation, owner, lengthof(translation)); // remove trailing linebreaks etc. rtrim(translation); buf.append( translation ); // only append linebreaks if not empty if(owner[0]>0) { buf.append( "\n\n" ); } } void obj_t::show_info() { create_win( new obj_infowin_t(this), w_info, (ptrdiff_t)this); } bool obj_t::has_managed_lifecycle() const { return false; } // returns NULL, if removal is allowed const char *obj_t::get_removal_error(const player_t *player) { if(owner_n==PLAYER_UNOWNED || welt->get_player(owner_n) == player || welt->get_public_player() == player) { return NULL; } else { return "Der Besitzer erlaubt das Entfernen nicht"; } } void obj_t::rdwr(loadsave_t *file) { xml_tag_t d( file, "obj_t" ); if( file->is_version_less(101, 0) ) { pos.rdwr( file ); } sint8 byte = (sint8)(((sint16)16*(sint16)xoff)/OBJECT_OFFSET_STEPS); file->rdwr_byte(byte); xoff = (sint8)(((sint16)byte*OBJECT_OFFSET_STEPS)/16); byte = (sint8)(((sint16)16*(sint16)yoff)/OBJECT_OFFSET_STEPS); file->rdwr_byte(byte); yoff = (sint8)(((sint16)byte*OBJECT_OFFSET_STEPS)/16); byte = owner_n; file->rdwr_byte(byte); owner_n = byte; } /** * draw the object * the dirty-flag is reset from objlist_t::display_obj_fg, or objlist_t::display_overlay when multithreaded */ void obj_t::display(int xpos, int ypos CLIP_NUM_DEF) const { image_id image = get_image(); image_id const outline_image = get_outline_image(); if( image!=IMG_EMPTY || outline_image!=IMG_EMPTY ) { const int raster_width = gfx->get_current_tile_raster_width(); const bool is_dirty = get_flag(obj_t::dirty); if (vehicle_base_t const* const v = obj_cast(this)) { // vehicles need finer steps to appear smoother v->get_screen_offset( xpos, ypos, raster_width ); } xpos += tile_raster_scale_x(get_xoff(), raster_width); ypos += tile_raster_scale_y(get_yoff(), raster_width); if( owner_n != PLAYER_UNOWNED ) { if( obj_t::show_owner ) { gfx->draw_blend( image, xpos, ypos, owner_n, gfx->palette_lookup(welt->get_player(owner_n)->get_player_color1()+2) | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } else { gfx->draw_color( image, xpos, ypos, owner_n, true, is_dirty CLIP_NUM_PAR); } } else { gfx->draw_normal( image, xpos, ypos, 0, true, is_dirty CLIP_NUM_PAR); } if( outline_image != IMG_EMPTY ) { // transparency? const FLAGGED_PIXVAL transparent = get_outline_colour(); if( TRANSPARENT_FLAGS&transparent ) { // only transparent outline gfx->draw_blend( get_outline_image(), xpos, ypos, owner_n, transparent, 0, is_dirty CLIP_NUM_PAR); } else if( obj_t::get_flag( highlight ) ) { // highlight this tile gfx->draw_blend( image, xpos, ypos, owner_n, SYSCOL_OBJECT_HIGHLIGHT | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } } else if( obj_t::get_flag( highlight ) ) { // highlight this tile gfx->draw_blend( get_image(), xpos, ypos, owner_n, SYSCOL_OBJECT_HIGHLIGHT | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } } } // called during map rotation void obj_t::rotate90() { // most basic: rotate coordinate pos.rotate90( welt->get_size().y-1 ); sint8 new_dx = -2*yoff; yoff = xoff/2; xoff = new_dx; } #ifdef MULTI_THREAD void obj_t::display_after(int xpos, int ypos, const sint8 clip_num) const #else void obj_t::display_after(int xpos, int ypos, bool) const #endif { image_id image = get_front_image(); if( image != IMG_EMPTY ) { const int raster_width = gfx->get_current_tile_raster_width(); const bool is_dirty = get_flag( obj_t::dirty ); xpos += tile_raster_scale_x( get_xoff(), raster_width ); ypos += tile_raster_scale_y( get_yoff(), raster_width ); if( owner_n != PLAYER_UNOWNED ) { if( obj_t::show_owner ) { gfx->draw_blend( image, xpos, ypos, owner_n, gfx->palette_lookup(welt->get_player(owner_n)->get_player_color1()+2) | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } else if( obj_t::get_flag( highlight ) ) { // highlight this tile gfx->draw_blend( image, xpos, ypos, owner_n, SYSCOL_OBJECT_HIGHLIGHT | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } else { gfx->draw_color( image, xpos, ypos, owner_n, true, is_dirty CLIP_NUM_PAR); } } else if( obj_t::get_flag( highlight ) ) { // highlight this tile gfx->draw_blend( image, xpos, ypos, owner_n, SYSCOL_OBJECT_HIGHLIGHT | OUTLINE_FLAG | TRANSPARENT75_FLAG, 0, is_dirty CLIP_NUM_PAR); } else { gfx->draw_normal( image, xpos, ypos, 0, true, is_dirty CLIP_NUM_PAR); } } } /* * when a vehicle moves or a cloud moves, it needs to mark the old spot as dirty (to copy to screen) * sometimes they have an extra offset, this is the yoff parameter */ void obj_t::mark_image_dirty(image_id image, sint16 yoff) const { if( image != IMG_EMPTY ) { const sint16 rasterweite = gfx->get_tile_raster_width(); int xpos=0, ypos=0; if( is_moving() ) { vehicle_base_t const* const v = obj_cast(this); // vehicles need finer steps to appear smoother v->get_screen_offset( xpos, ypos, gfx->get_tile_raster_width() ); } viewport_t *vp = welt->get_viewport(); scr_coord scr_pos = vp->get_screen_coord(get_pos(), koord(get_xoff(), get_yoff())); // xpos, ypos, yoff are already in pixel units, no scaling needed // mark the region after the image as dirty gfx->mark_img_dirty(image, scr_pos.x + xpos, scr_pos.y + ypos + yoff); // too close to border => set dirty to be sure (smoke, skyscrapers, birds, or the like) const scr_rect r = gfx->get_image_offset(image); const sint16 distance_to_border = 3 - (yoff+get_yoff()+r.y)/(rasterweite/4); if( pos.x <= distance_to_border || pos.y <= distance_to_border ) { // but only if the image is actually visible ... const scr_size screen = gfx->get_screen_size(); if( scr_pos.x+r.x+r.w >= 0 && xpos <= screen.w && scr_pos.y+r.y+r.h >= 0 && ypos+r.y < screen.h ) { welt->set_background_dirty(); } } } } simutrans-124.5/src/simutrans/obj/simobj.h000066400000000000000000000206141520675460500206110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_SIMOBJ_H #define OBJ_SIMOBJ_H #include "../simtypes.h" #include "../display/clip_num.h" #include "../display/simimg.h" #include "../simcolor.h" #include "../dataobj/koord3d.h" class cbuffer_t; class karte_ptr_t; class player_t; /** * Base class of all objects on the map, obj == thing * Since everything is a 'obj' on the map, we need to make this as compact and fast as possible. */ class obj_t { public: // flags enum flag_values { no_flags = 0, /// no special properties dirty = 1 << 0, /// mark image dirty when drawing not_on_map = 1 << 1, /// this object is not placed on any tile (e.g. vehicles in a depot) is_vehicle = 1 << 2, /// this object is a vehicle obviously highlight = 1 << 3 /// for drawing some highlighted outline }; // display only outline with player color on owner stuff static bool show_owner; private: obj_t(obj_t const&); obj_t& operator=(obj_t const&); /** * Coordinate of position */ koord3d pos; /** * x-offset of the object on the tile * used for drawing object image * if xoff and yoff are between 0 and OBJECT_OFFSET_STEPS-1 then * the top-left corner of the image is within tile boundaries */ sint8 xoff; /** * y-offset of the object on the tile */ sint8 yoff; /** * Owner of the object (1 - public player, 15 - unowned) */ uint8 owner_n:4; /** * @see flag_values */ uint8 flags:4; private: /** * Used by all constructors to initialize all vars with safe values * -> single source principle */ void init(); protected: obj_t(); // since we often need access during loading void set_owner_nr(uint8 value) { owner_n = value; } /** * Pointer to the world of this thing. Static to conserve space. * Change to instance variable once more than one world is available. */ static karte_ptr_t welt; public: // needed for drawing images sint8 get_owner_nr() const { return owner_n; } /** * sets owner of object */ void set_owner(player_t *player); /** * returns owner of object */ player_t *get_owner() const; /** * routines to set, clear, get bit flags */ inline void set_flag(flag_values flag) {flags |= flag;} inline void clear_flag(flag_values flag) {flags &= ~flag;} inline bool get_flag(flag_values flag) const {return ((flags & flag) != 0);} /// all the different types of objects enum typ { undefined=-1, obj=0, baum=1, zeiger=2, cloud=4, gebaeude=7, // animated buildings (6 not used any more) signal=8, bruecke=9, tunnel=10, bahndepot=12, strassendepot=13, schiffdepot = 14, leitung = 16, pumpe = 17, senke = 18, roadsign = 19, pillar = 20, airdepot = 21, monoraildepot=22, tramdepot=23, maglevdepot=24, wayobj = 25, way = 26, // since 99.04 ways are normal things and stored in the objliste_t! label = 27, // indicates ownership field = 28, crossing = 29, groundobj = 30, // lakes, stones narrowgaugedepot=31, // after this only moving stuff // reserved values for vehicles: 64 to 95 pedestrian=64, road_user=65, road_vehicle=66, rail_vehicle=67, monorail_vehicle=68, maglev_vehicle=69, narrowgauge_vehicle=70, water_vehicle=80, air_vehicle=81, movingobj=82, // other new objs (obsolete, only used during loading old games // lagerhaus = 24, (never really used) // gebaeude_alt=6, (very, very old? old_wolke=3, old_async_wolke=5, old_gebaeudefundament=11, // wall below buildings, not used any more old_raucher=15, // obsolete old_automobil=32, old_waggon=33, old_schiff=34, old_aircraft=35, old_monorailwaggon=36, old_verkehr=41, old_fussgaenger=42, old_choosesignal = 95, old_presignal = 96, old_roadsign = 97, old_pillar = 98, old_airdepot = 99, old_monoraildepot=100, old_tramdepot=101 }; inline sint8 get_xoff() const {return xoff;} inline sint8 get_yoff() const {return yoff;} // true for all moving objects inline bool is_moving() const { return flags&is_vehicle; } // while in principle, this should trigger the dirty, it takes just too much time to do it // TAKE CARE OF SET IT DIRTY YOURSELF!!! inline void set_xoff(sint8 xoff) {this->xoff = xoff; } inline void set_yoff(sint8 yoff) {this->yoff = yoff; } /** * Constructor to set position of object * This does *not* add the object to the tile */ obj_t(koord3d pos); /** * Destructor: removes object from tile, should close any inspection windows */ virtual ~obj_t(); /** * Routine for cleanup if object is removed (ie book maintenance, cost for removal) */ virtual void cleanup(player_t *) {} /** * @returns untranslated name of object */ virtual const char *get_name() const {return "Ding";} /** * @return object type * @see typ */ virtual typ get_typ() const = 0; /** * waytype associated with this object */ virtual waytype_t get_waytype() const { return invalid_wt; } /// true if clipping below is needed virtual bool is_clipping_below_needed() const { return true; } /** * called whenever the snowline height changes * return false and the obj_t will be deleted */ virtual bool check_season(const bool) { return true; } /** * called during map rotation */ virtual void rotate90(); /** * Every object needs an image. * @return number of current image for that object */ virtual image_id get_image() const = 0; /** * this image is drawn after all get_image() on this tile * Currently only single height is supported for this feature */ virtual image_id get_front_image() const {return IMG_EMPTY;} /** * if a function returns a value here with TRANSPARENT_FLAGS set * then a transparent outline with the color from the lower 8 bit is drawn */ virtual FLAGGED_PIXVAL get_outline_colour() const {return 0;} /** * The image, that will be outlined */ virtual image_id get_outline_image() const { return IMG_EMPTY; } /** * Save and Load of object data in one routine */ virtual void rdwr(loadsave_t *file); /** * Called after the world is completely loaded from savegame */ virtual void finish_rd() {} /** * @return position */ inline koord3d get_pos() const {return pos;} /** * set position - you would not have guessed it :) */ inline void set_pos(koord3d k) { if(k!=pos) { set_flag(dirty); pos = k;} } /** * put description of object into the buffer * (used for certain windows) * @see simwin */ virtual void info(cbuffer_t & buf) const; /** * Opens a new info window for the object */ virtual void show_info(); /** * @return True if the object lifecycle is managed by another system so cannot be destroyed. * False if the object can be destroyed at any time. */ virtual bool has_managed_lifecycle() const; /// @return NULL if this obj can be removed by @p player, otherwise an error message virtual const char *get_removal_error(const player_t *player); /** * Draw background image of object (but only single height) * (everything that could be potentially behind vehicles) * override for multi imge objects */ virtual void display(int xpos, int ypos CLIP_NUM_DEF) const; /** * Draw foreground image * (everything that is in front of vehicles) */ #ifdef MULTI_THREAD virtual void display_after(int xpos, int ypos, const sint8 clip_num) const; #else virtual void display_after(int xpos, int ypos, bool is_global) const; #endif #ifdef MULTI_THREAD /** * Draw overlays * (convoi tooltips) */ virtual void display_overlay(int /*xpos*/, int /*ypos*/) const { return; } #endif /** * When a vehicle moves or a cloud moves, it needs to mark the old spot as dirty (to copy to screen). * This routine already takes position, and offsets (x_off, y_off) into account. * * @param image * @param yoff extra y-offset, in most cases 0, in pixels. */ void mark_image_dirty(image_id image, sint16 yoff) const; /** * Function for recalculating the image. */ virtual void calc_image() {} }; /** * Template to do casting of pointers based on obj_t::typ * as a replacement of the slower dynamic_cast<> */ template static T* obj_cast(obj_t*); template static inline T const* obj_cast(obj_t const* const d) { return obj_cast(const_cast(d)); } /** * Game objects that do not have description windows (for instance zeiger_t, wolke_t) */ class obj_no_info_t : public obj_t { public: obj_no_info_t(koord3d pos) : obj_t(pos) {} void show_info() OVERRIDE {} protected: obj_no_info_t() : obj_t() {} }; #endif simutrans-124.5/src/simutrans/obj/tunnel.cc000066400000000000000000000144001520675460500207650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../world/simworld.h" #include "simobj.h" #include "../player/simplay.h" #include "../ground/grund.h" #include "../display/simimg.h" #include "../builder/tunnelbauer.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/pakset_manager.h" #include "../descriptor/tunnel_desc.h" #include "leitung2.h" #include "../builder/wegbauer.h" #include "tunnel.h" #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_mutex_t tunnel_calc_image_mutex; static recursive_mutex_maker_t tunnel_cim_maker(tunnel_calc_image_mutex); #endif tunnel_t::tunnel_t(loadsave_t* const file) : obj_no_info_t() { desc = 0; rdwr(file); image = foreground_image = IMG_EMPTY; broad_type = 0; } tunnel_t::tunnel_t(koord3d pos, player_t *player, const tunnel_desc_t *desc) : obj_no_info_t(pos) { assert(desc); this->desc = desc; set_owner( player ); image = foreground_image = IMG_EMPTY; broad_type = 0; } waytype_t tunnel_t::get_waytype() const { return desc ? desc->get_waytype() : invalid_wt; } void tunnel_t::calc_image() { #ifdef MULTI_THREAD pthread_mutex_lock( &tunnel_calc_image_mutex ); #endif const grund_t *gr = welt->lookup(get_pos()); if( gr->ist_karten_boden() && desc ) { slope_t::type hang = gr->get_grund_hang(); broad_type = 0; if( desc->has_broad_portals() ) { ribi_t::ribi dir = ribi_t::rotate90( ribi_type( hang ) ); if( dir==0 ) { dbg->error( "tunnel_t::calc_image()", "pos=%s, dir=%i, hang=%i", get_pos().get_str(), dir, hang ); } else { const grund_t *gr_l = welt->lookup(get_pos() + dir); tunnel_t* tunnel_l = gr_l ? gr_l->find() : NULL; if( tunnel_l && tunnel_l->get_desc() == desc && gr_l->get_grund_hang() == hang ) { broad_type += 1; if( !(tunnel_l->get_broad_type() & 2) ) { tunnel_l->calc_image(); } } const grund_t *gr_r = welt->lookup(get_pos() - dir); tunnel_t* tunnel_r = gr_r ? gr_r->find() : NULL; if( tunnel_r && tunnel_r->get_desc() == desc && gr_r->get_grund_hang() == hang ) { broad_type += 2; if( !(tunnel_r->get_broad_type() & 1) ) { tunnel_r->calc_image(); } } } } if (weg_t* w = gr->get_weg_nr(0)) { w->set_image(desc->get_background_id(hang, get_pos().z >= welt->get_snowline() || welt->get_climate(get_pos().get_2d()) == arctic_climate, broad_type)); w->set_foreground_image(desc->get_foreground_id(hang, get_pos().z >= welt->get_snowline() || welt->get_climate(get_pos().get_2d()) == arctic_climate, broad_type)); if (weg_t* w = gr->get_weg_nr(1)) { if (corner_se(hang) > 0) { w->set_image(IMG_EMPTY); w->set_foreground_image(IMG_EMPTY); } else { w->set_images(weg_t::image_flat, w->get_ribi_unmasked(), false); } } set_image(IMG_EMPTY); set_foreground_image(IMG_EMPTY); } else { set_image(desc->get_background_id(hang, get_pos().z >= welt->get_snowline() || welt->get_climate(get_pos().get_2d()) == arctic_climate, broad_type)); set_foreground_image(desc->get_foreground_id(hang, get_pos().z >= welt->get_snowline() || welt->get_climate(get_pos().get_2d()) == arctic_climate, broad_type)); } } else { set_image( IMG_EMPTY ); set_foreground_image( IMG_EMPTY ); } #ifdef MULTI_THREAD pthread_mutex_unlock( &tunnel_calc_image_mutex ); #endif } void tunnel_t::rdwr(loadsave_t *file) { xml_tag_t t( file, "tunnel_t" ); obj_t::rdwr(file); if( file->is_version_atleast(99, 1) ) { char buf[256]; if( file->is_loading() ) { file->rdwr_str(buf, lengthof(buf)); desc = tunnel_builder_t::get_desc(buf); if( desc==NULL ) { desc = tunnel_builder_t::get_desc(translator::compatibility_name(buf)); } if( desc==NULL ) { pakset_manager_t::add_missing_paks( buf, MISSING_WAY ); } } else { strcpy( buf, desc->get_name() ); file->rdwr_str(buf,0); } } } void tunnel_t::finish_rd() { const grund_t *gr = welt->lookup(get_pos()); player_t *player=get_owner(); if(desc==NULL) { // find a matching desc if (gr->get_weg_nr(0)==NULL) { // no way? underground powerline if (gr->get_leitung()) { desc = tunnel_builder_t::get_tunnel_desc(powerline_wt, 1, 0); } // no tunnel -> use dummy road tunnel if (desc==NULL) { desc = tunnel_builder_t::get_tunnel_desc(road_wt, 1, 0); } } else { desc = tunnel_builder_t::get_tunnel_desc(gr->get_weg_nr(0)->get_desc()->get_wtyp(), 450, 0); if( desc == NULL ) { dbg->error( "tunnel_t::finish_rd()", "Completely unknown tunnel for this waytype: Lets use a rail tunnel!" ); desc = tunnel_builder_t::get_tunnel_desc(track_wt, 1, 0); } } } if(player) { // change maintenance weg_t *weg = gr->get_weg(desc->get_waytype()); if(weg) { weg->set_max_speed(desc->get_topspeed()); player_t::add_maintenance( player, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype()); } player_t::add_maintenance( player, desc->get_maintenance(), desc->get_finance_waytype() ); } } // correct speed and maintenance void tunnel_t::cleanup( player_t *player2 ) { player_t *player = get_owner(); if(player) { // inside tunnel => do nothing but change maintenance const grund_t *gr = welt->lookup(get_pos()); if(gr) { weg_t *weg = gr->get_weg( desc->get_waytype() ); if(weg) { weg->set_max_speed( weg->get_desc()->get_topspeed() ); player_t::add_maintenance( player, weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype()); } player_t::add_maintenance( player, -desc->get_maintenance(), desc->get_finance_waytype() ); } } player_t::book_construction_costs(player2, -desc->get_price(), get_pos().get_2d(), desc->get_finance_waytype() ); } void tunnel_t::set_image( image_id b ) { mark_image_dirty( image, 0 ); mark_image_dirty( b, 0 ); image = b; } void tunnel_t::set_foreground_image( image_id b ) { mark_image_dirty( foreground_image, 0 ); mark_image_dirty( b, 0 ); foreground_image = b; } // returns NULL, if removal is allowed // players can remove public owned ways const char *tunnel_t::get_removal_error(const player_t *player) { if (get_owner_nr()==PLAYER_PUBLIC_NR) { return NULL; } else { return obj_t::get_removal_error(player); } } simutrans-124.5/src/simutrans/obj/tunnel.h000066400000000000000000000031541520675460500206330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_TUNNEL_H #define OBJ_TUNNEL_H #include "simobj.h" #include "../display/simimg.h" class tunnel_desc_t; class tunnel_t : public obj_no_info_t { private: const tunnel_desc_t *desc; image_id image; image_id foreground_image; uint8 broad_type; // Is this a broad tunnel mouth? public: tunnel_t(loadsave_t *file); tunnel_t(koord3d pos, player_t *player, const tunnel_desc_t *desc); const char *get_name() const OVERRIDE {return "Tunnelmuendung";} typ get_typ() const OVERRIDE { return tunnel; } /** * waytype associated with this object */ waytype_t get_waytype() const OVERRIDE; void calc_image() OVERRIDE; /** * Called whenever the season or snowline height changes * return false and the obj_t will be deleted */ bool check_season(const bool calc_only_season_change) OVERRIDE { if( !calc_only_season_change ) { calc_image(); } return true; } // depends on snowline only void set_image( image_id b ); void set_foreground_image( image_id b ); image_id get_image() const OVERRIDE {return image;} image_id get_front_image() const OVERRIDE { return foreground_image; } const tunnel_desc_t *get_desc() const { return desc; } void set_desc( const tunnel_desc_t *_desc ) { desc = _desc; } void rdwr(loadsave_t *file) OVERRIDE; void finish_rd() OVERRIDE; void cleanup(player_t *player) OVERRIDE; uint8 get_broad_type() const { return broad_type; } /** * @return NULL when OK, otherwise an error message */ const char *get_removal_error(const player_t *player) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/way/000077500000000000000000000000001520675460500177525ustar00rootroot00000000000000simutrans-124.5/src/simutrans/obj/way/kanal.cc000066400000000000000000000031511520675460500213470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../world/simworld.h" #include "../../display/simimg.h" #include "../../descriptor/ground_desc.h" #include "../../descriptor/way_desc.h" #include "../../builder/wegbauer.h" #include "../../dataobj/translator.h" #include "../../dataobj/pakset_manager.h" #include "kanal.h" const way_desc_t *kanal_t::default_kanal=NULL; kanal_t::kanal_t(loadsave_t *file) : weg_t() { rdwr(file); } kanal_t::kanal_t() : weg_t() { set_desc(default_kanal); } void kanal_t::rdwr(loadsave_t *file) { weg_t::rdwr(file); if(file->is_version_less(87, 1)) { set_desc(default_kanal); return; } if(file->is_saving()) { const char *s = get_desc()->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); const way_desc_t *desc = way_builder_t::get_desc(bname); int old_max_speed = get_max_speed(); if(desc==NULL) { desc = way_builder_t::get_desc(translator::compatibility_name(bname)); if(desc==NULL) { desc = default_kanal; if (desc == NULL) { dbg->fatal("kanal_t::rdwr", "Trying to load canal but pakset has no water ways!"); } pakset_manager_t::add_missing_paks( bname, MISSING_WAY ); dbg->warning("kanal_t::rdwr()", "Unknown canal '%s' replaced by '%s' (old_max_speed %i)", bname, desc->get_name(), old_max_speed); } else { DBG_DEBUG("kanal_t::rdwr()", "Old canal '%s' replaced by '%s'", bname, desc->get_name()); } } set_desc(desc); if(old_max_speed>0) { set_max_speed(old_max_speed); } } } simutrans-124.5/src/simutrans/obj/way/kanal.h000066400000000000000000000010001520675460500212000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAY_KANAL_H #define OBJ_WAY_KANAL_H #include "weg.h" #include "../../dataobj/loadsave.h" #include "../../utils/cbuffer.h" /** * Ships can be created on docks */ class kanal_t : public weg_t { public: static const way_desc_t *default_kanal; kanal_t(loadsave_t *file); kanal_t(); waytype_t get_waytype() const OVERRIDE {return water_wt;} void rdwr(loadsave_t *file) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/way/maglev.cc000066400000000000000000000017621520675460500215420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simtypes.h" #include "../../builder/wegbauer.h" #include "../../descriptor/way_desc.h" #include "maglev.h" const way_desc_t *maglev_t::default_maglev=NULL; /** * File loading constructor. */ maglev_t::maglev_t(loadsave_t *file) : schiene_t() { rdwr(file); } void maglev_t::rdwr(loadsave_t *file) { schiene_t::rdwr(file); if(get_desc()->get_wtyp()!=maglev_wt) { int old_max_speed = get_max_speed(); const way_desc_t *desc = way_builder_t::weg_search( maglev_wt, (old_max_speed>0 ? old_max_speed : 120), 0, (systemtype_t)((get_desc()->get_styp()==type_elevated)*type_elevated) ); if (desc==NULL) { dbg->fatal("maglev_t::rwdr()", "No maglev way available"); } dbg->warning("maglev_t::rwdr()", "Unknown way replaced by maglev %s (old_max_speed %i)", desc->get_name(), old_max_speed ); set_desc(desc); if(old_max_speed>0) { set_max_speed(old_max_speed); } } } simutrans-124.5/src/simutrans/obj/way/maglev.h000066400000000000000000000010741520675460500214000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAY_MAGLEV_H #define OBJ_WAY_MAGLEV_H #include "schiene.h" /** * derived from schiene, because signals will behave similar */ class maglev_t : public schiene_t { public: static const way_desc_t *default_maglev; maglev_t() : schiene_t() { set_desc(default_maglev); } /** * File loading constructor. */ maglev_t(loadsave_t *file); waytype_t get_waytype() const OVERRIDE {return maglev_wt;} void rdwr(loadsave_t *file) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/way/monorail.cc000066400000000000000000000017461520675460500221110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../simtypes.h" #include "../../builder/wegbauer.h" #include "../../descriptor/way_desc.h" #include "monorail.h" const way_desc_t *monorail_t::default_monorail=NULL; monorail_t::monorail_t(loadsave_t *file) : schiene_t() { rdwr(file); } void monorail_t::rdwr(loadsave_t *file) { schiene_t::rdwr(file); if(get_desc()->get_wtyp()!=monorail_wt) { int old_max_speed = get_max_speed(); const way_desc_t *desc = way_builder_t::weg_search( monorail_wt, (old_max_speed>0 ? old_max_speed : 120), 0, (systemtype_t)((get_desc()->get_styp()==type_elevated)*type_elevated) ); if (desc==NULL) { dbg->fatal("monorail_t::rwdr()", "No monorail way available"); } dbg->warning("monorail_t::rwdr()", "Unknown way replaced by monorail %s (old_max_speed %i)", desc->get_name(), old_max_speed ); set_desc(desc); if(old_max_speed>0) { set_max_speed(old_max_speed); } } } simutrans-124.5/src/simutrans/obj/way/monorail.h000066400000000000000000000012311520675460500217400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAY_MONORAIL_H #define OBJ_WAY_MONORAIL_H #include "schiene.h" /** * Class for monorail tracks, derived from schiene. * Monorail trains can drive on this tracks. * Each track belongs to a section block */ class monorail_t : public schiene_t { public: static const way_desc_t *default_monorail; monorail_t() : schiene_t() { set_desc(default_monorail); } /** * File loading constructor. */ monorail_t(loadsave_t *file); waytype_t get_waytype() const OVERRIDE {return monorail_wt;} void rdwr(loadsave_t *file) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/way/narrowgauge.cc000066400000000000000000000017561520675460500226130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../builder/wegbauer.h" #include "../../descriptor/way_desc.h" #include "narrowgauge.h" const way_desc_t *narrowgauge_t::default_narrowgauge=NULL; narrowgauge_t::narrowgauge_t(loadsave_t *file) : schiene_t() { rdwr(file); } void narrowgauge_t::rdwr(loadsave_t *file) { schiene_t::rdwr(file); if(get_desc()->get_wtyp()!=narrowgauge_wt) { int old_max_speed = get_max_speed(); const way_desc_t *desc = way_builder_t::weg_search( narrowgauge_wt, (old_max_speed>0 ? old_max_speed : 120), 0, (systemtype_t)((get_desc()->get_styp()==type_elevated)*type_elevated) ); if (desc==NULL) { dbg->fatal("narrowgauge_t::rwdr()", "No narrowgauge way available"); } dbg->warning("narrowgauge_t::rwdr()", "Unknown way replaced by narrow gauge %s (old_max_speed %i)", desc->get_name(), old_max_speed ); set_desc(desc); if(old_max_speed>0) { set_max_speed(old_max_speed); } } } simutrans-124.5/src/simutrans/obj/way/narrowgauge.h000066400000000000000000000011441520675460500224440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAY_NARROWGAUGE_H #define OBJ_WAY_NARROWGAUGE_H #include "schiene.h" /** * derived from schiene, because signals will behave similar */ class narrowgauge_t : public schiene_t { public: static const way_desc_t *default_narrowgauge; narrowgauge_t() : schiene_t() { set_desc(default_narrowgauge); } /** * File loading constructor. */ narrowgauge_t(loadsave_t *file); waytype_t get_waytype() const OVERRIDE {return narrowgauge_wt;} void rdwr(loadsave_t *file) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/way/runway.cc000066400000000000000000000035171520675460500216140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../../world/simworld.h" #include "../../builder/wegbauer.h" #include "../../descriptor/way_desc.h" #include "../../dataobj/loadsave.h" #include "../../dataobj/translator.h" #include "../../dataobj/pakset_manager.h" #include "../../utils/cbuffer.h" #include "runway.h" const way_desc_t *runway_t::default_runway=NULL; runway_t::runway_t() : schiene_t() { set_desc(default_runway); } runway_t::runway_t(loadsave_t *file) : schiene_t() { rdwr(file); } void runway_t::info(cbuffer_t & buf) const { schiene_t::info(buf); cbuffer_t reserved; if( get_desc()->get_styp()==type_runway ) { reserved.printf( "%s %i\n", translator::translate("waiting"), reservations.get_count() ); buf.append( reserved ); } } void runway_t::rdwr(loadsave_t *file) { xml_tag_t t( file, "runway_t" ); weg_t::rdwr(file); if(file->is_saving()) { const char *s = get_desc()->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); const way_desc_t *desc = way_builder_t::get_desc(bname); int old_max_speed=get_max_speed(); if (desc == NULL) { desc = way_builder_t::get_desc(translator::compatibility_name(bname)); if (desc == NULL) { desc = way_builder_t::weg_search(air_wt, old_max_speed > 0 ? old_max_speed : 20, 0, (systemtype_t)(old_max_speed > 250)); if (desc == NULL) { desc = default_runway; pakset_manager_t::add_missing_paks(bname, MISSING_WAY); if (desc == NULL) { dbg->fatal("runway_t::rdwr()", "No runway available"); } } dbg->warning("runway_t::rdwr()", "Unknown runway %s replaced by %s (old_max_speed %i)", bname, desc->get_name(), old_max_speed); } } if(old_max_speed>0) { set_max_speed(old_max_speed); } set_desc(desc); } } simutrans-124.5/src/simutrans/obj/way/runway.h000066400000000000000000000023541520675460500214540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAY_RUNWAY_H #define OBJ_WAY_RUNWAY_H #include "../../tpl/vector_tpl.h" #include "../../convoihandle.h" #include "schiene.h" /** * Class for aiport runaway in Simutrans. * system type==1 are for takeoff */ class runway_t : public schiene_t { private: vector_tpl reservations; public: static const way_desc_t *default_runway; runway_t(loadsave_t *file); runway_t(); inline waytype_t get_waytype() const OVERRIDE {return air_wt;} /** * @param[out] buf additional info is reservation! */ void info(cbuffer_t & buf) const OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; // the code below is only used for runways // the more reservations, the higher the cost for landing there. // Should quickly lead to equal usage of runways uint32 get_reservation_count() const { return reservations.get_count(); } void add_convoi_reservation( convoihandle_t cnv ) { reservations.append_unique(cnv); } void remove_convoi_reservation( convoihandle_t cnv ) { reservations.remove(cnv); } bool unreserve( convoihandle_t cnv ) OVERRIDE { reservations.remove(cnv); schiene_t::unreserve(cnv); return true; } }; #endif simutrans-124.5/src/simutrans/obj/way/schiene.cc000066400000000000000000000102351520675460500217000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../../simconvoi.h" #include "../../world/simworld.h" #include "../../vehicle/vehicle.h" #include "../../dataobj/environment.h" #include "../../dataobj/loadsave.h" #include "../../dataobj/translator.h" #include "../../dataobj/pakset_manager.h" #include "../../utils/cbuffer.h" #include "../../descriptor/way_desc.h" #include "../../builder/wegbauer.h" #include "schiene.h" const way_desc_t *schiene_t::default_schiene=NULL; bool schiene_t::show_reservations = false; schiene_t::schiene_t() : weg_t() { reserved = convoihandle_t(); if (schiene_t::default_schiene) { set_desc(schiene_t::default_schiene); } else { dbg->fatal("schiene_t::schiene_t()", "No rail way available!"); } } schiene_t::schiene_t(loadsave_t *file) : weg_t() { reserved = convoihandle_t(); rdwr(file); } void schiene_t::cleanup(player_t *) { // removes reservation if(reserved.is_bound()) { set_ribi(ribi_t::none); reserved->suche_neue_route(); } } void schiene_t::info(cbuffer_t & buf) const { weg_t::info(buf); if(reserved.is_bound()) { const char* reserve_text = translator::translate("\nis reserved by:"); // ignore linebreak if (reserve_text[0] == '\n') { reserve_text++; } buf.append(reserve_text); buf.append(reserved->get_name()); buf.append("\n"); #ifdef DEBUG_PBS reserved->open_info_window(); #endif } } /** * true, if this rail can be reserved */ bool schiene_t::reserve(convoihandle_t c, ribi_t::ribi dir ) { if(can_reserve(c)) { reserved = c; /* for threeway and fourway switches we may need to alter graphic, if * direction is a diagonal (i.e. on the switching part) * and there are switching graphics */ if( ribi_t::is_threeway(get_ribi_unmasked()) && ribi_t::is_bend(dir) && get_desc()->has_switch_image() ) { mark_image_dirty( get_image(), 0 ); mark_image_dirty( get_front_image(), 0 ); set_switched(dir == ribi_t::northeast || dir == ribi_t::southwest); set_images(image_switch, get_ribi_unmasked(), is_snow(), has_switched() ); set_flag( obj_t::dirty ); } if(schiene_t::show_reservations) { set_flag( obj_t::dirty ); } return true; } // reserve anyway ... return false; } /** * releases previous reservation * only true, if there was something to release */ bool schiene_t::unreserve(convoihandle_t c) { // is this tile reserved by us? if(reserved.is_bound() && reserved==c) { reserved = convoihandle_t(); if(schiene_t::show_reservations) { set_flag( obj_t::dirty ); } return true; } return false; } void schiene_t::rdwr(loadsave_t *file) { xml_tag_t t( file, "schiene_t" ); weg_t::rdwr(file); if(file->is_version_less(99, 8)) { sint32 blocknr=-1; file->rdwr_long(blocknr); } if(file->is_version_less(89, 0)) { uint8 dummy; file->rdwr_byte(dummy); set_electrify(dummy); } if(file->is_saving()) { const char *s = get_desc()->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); int old_max_speed = get_max_speed(); const way_desc_t *desc = way_builder_t::get_desc(bname); if(desc==NULL) { int old_max_speed=get_max_speed(); desc = way_builder_t::get_desc(translator::compatibility_name(bname)); if(desc==NULL) { desc = default_schiene; if (!desc) { dbg->fatal("schiene_t::rdwr", "Trying to load train tracks but pakset has none!"); } pakset_manager_t::add_missing_paks( bname, MISSING_WAY ); } dbg->warning("schiene_t::rdwr()", "Unknown rail '%s' replaced by '%s' (old_max_speed %i)", bname, desc->get_name(), old_max_speed ); } set_desc(desc); if(old_max_speed>0) { set_max_speed(old_max_speed); } // DBG_MESSAGE("schiene_t::rdwr","track %s at (%s) max_speed %i", bname, get_pos().get_str(), old_max_speed); } } FLAGGED_PIXVAL schiene_t::get_outline_colour() const { if (env_t::show_single_ways && ribi_t::is_single(get_ribi_unmasked())) { return TRANSPARENT75_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_RED); } if (show_reservations && reserved.is_bound()) { return TRANSPARENT75_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_RED); } return 0; } simutrans-124.5/src/simutrans/obj/way/schiene.h000066400000000000000000000040031520675460500215360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAY_SCHIENE_H #define OBJ_WAY_SCHIENE_H #include "weg.h" #include "../../convoihandle.h" class vehicle_t; /** * Class for Rails in Simutrans. * Trains can run over rails. * Every rail belongs to a section block */ class schiene_t : public weg_t { protected: /** * Bound when this block was successfully reserved by the convoi */ convoihandle_t reserved; public: static const way_desc_t *default_schiene; static bool show_reservations; /** * File loading constructor. */ schiene_t(loadsave_t *file); schiene_t(); waytype_t get_waytype() const OVERRIDE {return track_wt;} /** * @param[out] buf additional info is reservation! */ void info(cbuffer_t & buf) const OVERRIDE; /** * true, if this rail can be reserved */ bool can_reserve(convoihandle_t c) const { return !reserved.is_bound() || c==reserved; } /** * true, if this rail can be reserved */ bool is_reserved() const { return reserved.is_bound(); } /** * true, then this rail was reserved */ bool reserve(convoihandle_t c, ribi_t::ribi dir); /** * releases previous reservation */ virtual bool unreserve( convoihandle_t c); /** * releases previous reservation */ bool unreserve( vehicle_t *) { return unreserve(reserved); } /* called before deletion; * last chance to unreserve tiles ... */ void cleanup(player_t *player) OVERRIDE; /** * gets the related convoi */ convoihandle_t get_reserved_convoi() const {return reserved;} void rdwr(loadsave_t *file) OVERRIDE; /** * if a function return here a value with TRANSPARENT_FLAGS set * then a transparent outline with the color form the lower 8 Bit is drawn */ FLAGGED_PIXVAL get_outline_colour() const OVERRIDE; /* * to show reservations if needed */ image_id get_outline_image() const OVERRIDE { return weg_t::get_image(); } }; template<> inline schiene_t* obj_cast(obj_t* const d) { return dynamic_cast(d); } #endif simutrans-124.5/src/simutrans/obj/way/strasse.cc000066400000000000000000000032211520675460500217430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "strasse.h" #include "../../world/simworld.h" #include "../../dataobj/loadsave.h" #include "../../descriptor/way_desc.h" #include "../../builder/wegbauer.h" #include "../../dataobj/translator.h" #include "../../dataobj/pakset_manager.h" const way_desc_t *strasse_t::default_strasse=NULL; strasse_t::strasse_t(loadsave_t *file) : weg_t() { rdwr(file); } strasse_t::strasse_t() : weg_t() { set_gehweg(false); set_desc(default_strasse); } void strasse_t::rdwr(loadsave_t *file) { xml_tag_t s( file, "strasse_t" ); weg_t::rdwr(file); if(file->is_version_less(89, 0)) { bool gehweg; file->rdwr_bool(gehweg); set_gehweg(gehweg); } if(file->is_saving()) { const char *s = get_desc()->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); const way_desc_t *desc = way_builder_t::get_desc(bname); int old_max_speed = get_max_speed(); if(desc==NULL) { desc = way_builder_t::get_desc(translator::compatibility_name(bname)); if(desc==NULL) { desc = default_strasse; pakset_manager_t::add_missing_paks( bname, MISSING_WAY ); dbg->warning("strasse_t::rdwr()", "Unknown street %s replaced by %s (old_max_speed %i)", bname, desc->get_name(), old_max_speed); } else { DBG_DEBUG("strasse_t::rdwr()", "Old street %s replaced by %s", bname, desc->get_name()); } } set_desc(desc); if(old_max_speed>0) { set_max_speed(old_max_speed); } if(hat_gehweg()) { set_max_speed(min(desc->get_topspeed(),weg_t::get_cityroad_speedlimit())); } } } simutrans-124.5/src/simutrans/obj/way/strasse.h000066400000000000000000000007631520675460500216150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAY_STRASSE_H #define OBJ_WAY_STRASSE_H #include "weg.h" /** * Cars are able to drive on roads. */ class strasse_t : public weg_t { public: static const way_desc_t *default_strasse; strasse_t(loadsave_t *file); strasse_t(); inline waytype_t get_waytype() const OVERRIDE {return road_wt;} //void set_gehweg(bool janein); void rdwr(loadsave_t *file) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/obj/way/weg.cc000066400000000000000000000415451520675460500210540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "weg.h" #include "schiene.h" #include "strasse.h" #include "monorail.h" #include "maglev.h" #include "narrowgauge.h" #include "kanal.h" #include "runway.h" #include "../gebaeude.h" #include "../../ground/grund.h" #include "../../world/simworld.h" #include "../../display/simimg.h" #include "../../simhalt.h" #include "../../obj/simobj.h" #include "../../player/simplay.h" #include "../../obj/roadsign.h" #include "../../obj/signal.h" #include "../../obj/crossing.h" #include "../../utils/cbuffer.h" #include "../../dataobj/environment.h" // TILE_HEIGHT_STEP #include "../../dataobj/translator.h" #include "../../dataobj/loadsave.h" #include "../../descriptor/way_desc.h" #include "../../descriptor/roadsign_desc.h" #include "../../tpl/slist_tpl.h" #ifdef MULTI_THREAD #include "../../utils/simthread.h" static pthread_mutex_t weg_calc_image_mutex; static recursive_mutex_maker_t weg_cim_maker(weg_calc_image_mutex); #endif /** * Alle instantiierten Wege */ slist_tpl alle_wege; uint16 weg_t::cityroad_speed = 50; /** * Get list of all ways */ const slist_tpl &weg_t::get_alle_wege() { return alle_wege; } bool weg_t::is_clipping_below_needed() const { // elevated no clip? return desc->is_clip_below(); } void weg_t::set_cityroad_speedlimit(uint16 new_limit) { if (cityroad_speed != new_limit) { cityroad_speed = new_limit; for(weg_t *w : alle_wege) { if( w->hat_gehweg() && w->get_waytype() == road_wt ) { if (const way_desc_t* desc = w->get_desc()) { w->set_max_speed(min(desc->get_topspeed(), cityroad_speed)); } else { // should never occur ??? w->set_max_speed(cityroad_speed); } } } } } // returns a way with matching waytype weg_t* weg_t::alloc(waytype_t wt) { weg_t *weg = NULL; switch(wt) { case tram_wt: case track_wt: weg = new schiene_t(); break; case monorail_wt: weg = new monorail_t(); break; case maglev_wt: weg = new maglev_t(); break; case narrowgauge_wt: weg = new narrowgauge_t(); break; case road_wt: weg = new strasse_t(); break; case water_wt: weg = new kanal_t(); break; case air_wt: weg = new runway_t(); break; default: // keep compiler happy; should never reach here anyway assert(0); break; } return weg; } // returns a string with the "official name of the waytype" const char *weg_t::waytype_to_string(waytype_t wt) { switch(wt) { case tram_wt: return "tram_track"; case track_wt: return "track"; case monorail_wt: return "monorail_track"; case maglev_wt: return "maglev_track"; case narrowgauge_wt: return "narrowgauge_track"; case road_wt: return "road"; case water_wt: return "water"; case air_wt: return "air"; // these are just for translation case powerline_wt: return "power"; case decoration_wt: return "decoration"; default: return "invalid waytype"; } } void weg_t::set_desc(const way_desc_t *b) { desc = b; if( hat_gehweg() && desc->get_wtyp() == road_wt && desc->get_topspeed() > cityroad_speed ) { max_speed = cityroad_speed; } else { max_speed = desc->get_topspeed(); } } /** * initializes statistic array */ void weg_t::init_statistics() { for( int type=0; typeget_maintenance(), desc->get_finance_waytype() ); } } bool weg_t::needs_crossing(const way_desc_t* other) const { // certain way always needs crossing (or never) switch (desc->get_waytype()) { case powerline_wt: return false; case water_wt: case air_wt: case decoration_wt: return true; default: break; } switch (other->get_waytype()) { case powerline_wt: return false; case water_wt: case air_wt: case decoration_wt: return true; default: break; } // only now we can check if there is a tramway involved if (desc->get_styp() == type_tram) { // we are tramway return false; } if (other->get_styp() == type_tram) { // other is tramway return false; } // needs a crossing return true; } void weg_t::rdwr(loadsave_t *file) { xml_tag_t t( file, "weg_t" ); // save owner if( file->is_version_atleast(99, 6) ) { sint8 spnum=get_owner_nr(); file->rdwr_byte(spnum); set_owner_nr(spnum); } // all connected directions uint8 dummy8 = ribi; file->rdwr_byte(dummy8); if( file->is_loading() ) { ribi = dummy8 & 15; // before: high bits was maske ribi_maske = 0; // maske will be restored by signal/roadsing } uint16 dummy16=max_speed; file->rdwr_short(dummy16); max_speed=dummy16; if( file->is_version_atleast(89, 0) ) { dummy8 = flags; file->rdwr_byte(dummy8); if( file->is_loading() ) { // all other flags are restored afterwards flags = dummy8 & HAS_SIDEWALK; } } for( int type=0; typerdwr_long(w); statistics[month][type] = (sint16)w; // DBG_DEBUG("weg_t::rdwr()", "statistics[%d][%d]=%d", month, type, statistics[month][type]); } } } void weg_t::info(cbuffer_t & buf) const { obj_t::info(buf); buf.printf("%s %u%s", translator::translate("Max. speed:"), max_speed, translator::translate("km/h\n")); buf.printf("%s%s", translator::translate("\nRibi (unmasked)"), ribi_t::names[get_ribi_unmasked()]); buf.printf("%s%s\n", translator::translate("\nRibi (masked)"), ribi_t::names[get_ribi()]); if(has_sign()) { buf.append(translator::translate("\nwith sign/signal\n")); } if(is_electrified()) { buf.append(translator::translate("\nelektrified")); } else { buf.append(translator::translate("\nnot elektrified")); } #if 1 buf.printf(translator::translate("convoi passed last\nmonth %i\n"), statistics[1][1]); #else // Debug - output stats buf.append("\n"); for (int type=0; typeget_copyright()) { buf.printf(translator::translate("Constructed by %s"), maker); buf.append("\n"); } } /** * called during map rotation */ void weg_t::rotate90() { obj_t::rotate90(); ribi = ribi_t::rotate90( ribi ); ribi_maske = ribi_t::rotate90( ribi_maske ); } /** * counts signals on this tile; * It would be enough for the signals to register and unregister themselves, but this is more secure ... */ void weg_t::count_sign() { // Either only sign or signal please ... flags &= ~(HAS_SIGN|HAS_SIGNAL|HAS_CROSSING); const grund_t *gr=welt->lookup(get_pos()); if(gr) { uint8 i = 1; // if there is a crossing, the start index is at least three ... if(const crossing_t* cr = gr->get_crossing()) { max_speed = desc->get_topspeed(); // reset max_speed flags |= HAS_CROSSING; i = 3; const sint32 top_speed = cr->get_desc()->get_maxspeed( cr->get_desc()->get_waytype(0)==get_waytype() ? 0 : 1); max_speed = min(max_speed, top_speed); } // since way 0 is at least present here ... for( ; iobj_count(); i++ ) { obj_t *obj=gr->obj_bei(i); // sign for us? if( roadsign_t const* const sign = obj_cast(obj) ) { if( sign->get_desc()->get_wtyp() == get_desc()->get_wtyp() ) { // here is a sign ... flags |= HAS_SIGN; return; } } if( signal_t const* const signal = obj_cast(obj) ) { if( signal->get_desc()->get_wtyp() == get_desc()->get_wtyp() ) { // here is a signal ... flags |= HAS_SIGNAL; return; } } } } } void weg_t::set_images(image_type typ, uint8 ribi, bool snow, bool switch_nw) { switch(typ) { case image_flat: default: set_image( desc->get_image_id( ribi, snow ) ); set_foreground_image( desc->get_image_id( ribi, snow, true ) ); break; case image_slope: set_image( desc->get_slope_image_id( (slope_t::type)ribi, snow ) ); set_foreground_image( desc->get_slope_image_id( (slope_t::type)ribi, snow, true ) ); break; case image_switch: set_image( desc->get_switch_image_id(ribi, snow, switch_nw) ); set_foreground_image( desc->get_switch_image_id(ribi, snow, switch_nw, true) ); break; case image_diagonal: set_image( desc->get_diagonal_image_id(ribi, snow) ); set_foreground_image( desc->get_diagonal_image_id(ribi, snow, true) ); break; } } // much faster recalculation of season image bool weg_t::check_season(const bool calc_only_season_change) { if( calc_only_season_change ) { // nothing depends on season, only snowline return true; } // no way to calculate this or no image set (not visible, in tunnel mouth, etc) if( desc == NULL || image == IMG_EMPTY ) { return true; } grund_t *gr = welt->lookup( get_pos() ); if( gr->ist_bruecke() && gr->obj_bei(0) == this ) { // first way on a bridge (bruecke_t will set the image) return true; } // use snow image if above snowline and above ground bool snow = (gr->ist_karten_boden() || !gr->ist_tunnel()) && (get_pos().z + gr->get_weg_yoff()/TILE_HEIGHT_STEP >= welt->get_snowline() || welt->get_climate( get_pos().get_2d() ) == arctic_climate); bool old_snow = (flags&IS_SNOW) != 0; if( !(snow ^ old_snow) ) { // season is not changing ... return true; } // set snow flake flags &= ~IS_SNOW; if( snow ) { flags |= IS_SNOW; } slope_t::type hang = gr->get_weg_hang(); if( hang != slope_t::flat ) { set_images( image_slope, hang, snow ); return true; } if( is_diagonal() ) { set_images( image_diagonal, ribi, snow ); } else if( ribi_t::is_threeway( ribi ) && desc->get_waytype()!=road_wt ) { set_images(image_switch, ribi, snow, has_switched()); } else { // level track set_images( image_flat, ribi, snow ); if(foreground_image != IMG_EMPTY && ribi_t::is_straight(ribi)) { // on straight level tracks may be stations or depots => no foreground then if (gr->is_halt()) { // no foreground in stations set_foreground_image(IMG_EMPTY); } else { // check for any building on this tile for ( uint8 i = 1; i < gr->obj_count(); i++ ) { if (dynamic_cast(gr->obj_bei(i))) { // no foreground in depots set_foreground_image(IMG_EMPTY); break; } } } } } return true; } #ifdef MULTI_THREAD void weg_t::lock_mutex() { pthread_mutex_lock( &weg_calc_image_mutex ); } void weg_t::unlock_mutex() { pthread_mutex_unlock( &weg_calc_image_mutex ); } #endif void weg_t::calc_image() { #ifdef MULTI_THREAD pthread_mutex_lock( &weg_calc_image_mutex ); #endif grund_t *from = welt->lookup(get_pos()); grund_t *to; image_id old_image = image; if( from==NULL || desc==NULL ) { // no ground, in tunnel set_image(IMG_EMPTY); set_foreground_image(IMG_EMPTY); if( from==NULL ) { dbg->error( "weg_t::calc_image()", "Own way at %s not found!", get_pos().get_str() ); } #ifdef MULTI_THREAD pthread_mutex_unlock( &weg_calc_image_mutex ); #endif return; // otherwise crashing during enlargement } else if( from->ist_tunnel() && from->ist_karten_boden() && (grund_t::underground_mode==grund_t::ugm_none || (grund_t::underground_mode==grund_t::ugm_level && from->get_hoehe()ist_bruecke() && from->obj_bei(0)==this ) { // first way on a bridge (bruecke_t will set the image) #ifdef MULTI_THREAD pthread_mutex_unlock( &weg_calc_image_mutex ); #endif return; } else { // use snow image if above snowline and above ground bool snow = (from->ist_karten_boden() || !from->ist_tunnel()) && (get_pos().z + from->get_weg_yoff()/TILE_HEIGHT_STEP >= welt->get_snowline() || welt->get_climate( get_pos().get_2d() ) == arctic_climate ); flags &= ~IS_SNOW; if( snow ) { flags |= IS_SNOW; } slope_t::type hang = from->get_weg_hang(); if(hang != slope_t::flat) { // on slope set_images(image_slope, hang, snow); } else if (ribi_t::is_threeway(ribi)) { set_images(image_switch, ribi, snow, has_switched()); } else if (!ribi_t::is_twoway(ribi)) { set_images(image_flat, ribi, snow); // nide foreground in stations and depots if (foreground_image != IMG_EMPTY) { if (from->is_halt()) { // no foreground in stations set_foreground_image(IMG_EMPTY); } else if (ribi_t::is_single(ribi) && from->obj_count() > 1) { // check for any building on this tile for (uint8 i = 1; i < from->obj_count(); i++) { if (dynamic_cast(from->obj_bei(i))) { // no foreground in depots set_foreground_image(IMG_EMPTY); break; } } } } } // recursion to find out of diagonal else { static int recursion = 0; /* Communicate among different instances of this method */ // flat way set_images(image_flat, ribi, snow); // recalc image of neighbors also when this changed to non-diagonal if(recursion == 0) { recursion++; for(int r = 0; r < 4; r++) { if( from->get_neighbour(to, get_waytype(), ribi_t::nesw[r]) ) { // can fail on water tiles if( weg_t *w=to->get_weg(get_waytype()) ) { // and will only change the outcome, if it has a diagonal image ... if( w->get_desc()->has_diagonal_image() ) { w->calc_image(); } } } } recursion--; } // try diagonal image if( desc->has_diagonal_image() ) { check_diagonal(); // now apply diagonal image if(is_diagonal()) { if( desc->get_diagonal_image_id(ribi, snow) != IMG_EMPTY || desc->get_diagonal_image_id(ribi, snow, true) != IMG_EMPTY) { set_images(image_diagonal, ribi, snow); } } } // level track if (foreground_image != IMG_EMPTY) { if (from->is_halt()) { // no foreground in stations set_foreground_image(IMG_EMPTY); } else if (ribi_t::is_straight(ribi) && from->obj_count() > 1) { // check for any building on this tile for (uint8 i = 1; i < from->obj_count(); i++) { if (dynamic_cast(from->obj_bei(i))) { // no foreground in depots set_foreground_image(IMG_EMPTY); break; } } } } } } if( image!=old_image ) { mark_image_dirty(old_image, from->get_weg_yoff()); mark_image_dirty(image, from->get_weg_yoff()); } #ifdef MULTI_THREAD pthread_mutex_unlock( &weg_calc_image_mutex ); #endif } // checks, if this way qualifies as diagonal void weg_t::check_diagonal() { bool diagonal = false; flags &= ~IS_DIAGONAL; const ribi_t::ribi ribi = get_ribi_unmasked(); if( !ribi_t::is_bend(ribi) ) { // This is not a curve, it can't be a diagonal return; } grund_t *from = welt->lookup(get_pos()); grund_t *to; ribi_t::ribi r1 = ribi_t::none; ribi_t::ribi r2 = ribi_t::none; // get the ribis of the ways that connect to us // r1 will be 45 degree clockwise ribi (eg northeast->east), r2 will be anticlockwise ribi (eg northeast->north) if( from->get_neighbour(to, get_waytype(), ribi_t::rotate45(ribi)) ) { r1 = to->get_weg_ribi_unmasked(get_waytype()); } if( from->get_neighbour(to, get_waytype(), ribi_t::rotate45l(ribi)) ) { r2 = to->get_weg_ribi_unmasked(get_waytype()); } // diagonal if r1 or r2 are our reverse and neither one is 90 degree rotation of us diagonal = (r1 == ribi_t::backward(ribi) || r2 == ribi_t::backward(ribi)) && r1 != ribi_t::rotate90l(ribi) && r2 != ribi_t::rotate90(ribi); if( diagonal ) { flags |= IS_DIAGONAL; } } /** * new month */ void weg_t::new_month() { for (int type=0; type0; month--) { statistics[month][type] = statistics[month-1][type]; } statistics[0][type] = 0; } } // correct speed and maintenance void weg_t::finish_rd() { player_t *player = get_owner(); if( player && desc ) { player_t::add_maintenance( player, desc->get_maintenance(), desc->get_finance_waytype() ); } } // returns NULL, if removal is allowed // players can remove public owned ways const char *weg_t::get_removal_error(const player_t *player) { if( get_owner_nr()==PLAYER_PUBLIC_NR ) { return NULL; } return obj_t::get_removal_error(player); } FLAGGED_PIXVAL weg_t::get_outline_colour() const { if (env_t::show_single_ways && ribi_t::is_single(ribi)) { return TRANSPARENT75_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_RED); } return 0; } simutrans-124.5/src/simutrans/obj/way/weg.h000066400000000000000000000200731520675460500207070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAY_WEG_H #define OBJ_WAY_WEG_H #include "../../display/simimg.h" #include "../../simtypes.h" #include "../../obj/simobj.h" #include "../../descriptor/way_desc.h" #include "../../dataobj/koord3d.h" class karte_t; class way_desc_t; class cbuffer_t; template class slist_tpl; // maximum number of months to store information #define MAX_WAY_STAT_MONTHS 2 // number of different statistics collected #define MAX_WAY_STATISTICS 2 enum way_statistics { WAY_STAT_GOODS = 0, ///< number of goods transported over this way WAY_STAT_CONVOIS = 1, ///< number of convois that passed this way WAY_STAT_MAX }; /** * Ways is the base class for all traffic routes. (roads, track, runway etc.) * Ways always "belong" to a ground. They have direction bits (ribis) as well as * a mask for ribis. * * A way always is of a single type (waytype_t). * Crossings are supported by the fact that a ground can have more than one way. */ class weg_t : public obj_no_info_t { public: /** * Get list of all ways */ static const slist_tpl & get_alle_wege(); enum { HAS_SIDEWALK = 1 << 0, // only roads HAS_SWITCHED = 1 << 0, // only rails IS_ELECTRIFIED = 1 << 1, HAS_SIGN = 1 << 2, HAS_SIGNAL = 1 << 3, HAS_WAYOBJ = 1 << 4, HAS_CROSSING = 1 << 5, IS_DIAGONAL = 1 << 6, // marker for diagonal image IS_SNOW = 1 << 7 // marker, if above snowline currently }; private: /** * array for statistical values * MAX_WAY_STAT_MONTHS: [0] = actual value; [1] = last month value * MAX_WAY_STATISTICS: see #define at top of file */ sint16 statistics[MAX_WAY_STAT_MONTHS][MAX_WAY_STATISTICS]; static uint16 cityroad_speed; /** * Way type description */ const way_desc_t * desc; /** * Direction bits (ribis) for the way. North is in the upper right corner of the monitor. * 1=North, 2=East, 4=South, 8=West */ uint8 ribi:4; /** * Mask for ribi (Richtungsbits => Direction Bits) */ uint8 ribi_maske:4; /** * flags like walkway, electrification, road sings */ uint8 flags; /** * max speed; could not be taken for desc, since other object may modify the speed */ uint16 max_speed; image_id image; image_id foreground_image; /** * Initializes all member variables */ void init(); /** * initializes statistic array */ void init_statistics(); protected: public: weg_t(loadsave_t*) : obj_no_info_t() { init(); } weg_t() : obj_no_info_t() { init(); } virtual ~weg_t(); /** * @returns true if a crossing is needed */ bool needs_crossing(const way_desc_t* other) const; #ifdef MULTI_THREAD void lock_mutex(); void unlock_mutex(); #endif virtual bool is_clipping_below_needed() const OVERRIDE; /** * Actual image recalculation */ void calc_image() OVERRIDE; enum image_type { image_flat, image_slope, image_diagonal, image_switch }; /** * initializes both front and back images * switch images are set in schiene_t::reserve * needed by tunnel mouths */ void set_images(image_type typ, uint8 ribi, bool snow, bool switch_nw = false); /** * Called whenever the season or snowline height changes * return false and the obj_t will be deleted */ bool check_season(const bool calc_only_season_change) OVERRIDE; void set_max_speed(sint32 s) { max_speed = s; } sint32 get_max_speed() const { return max_speed; } static void set_cityroad_speedlimit(uint16 new_limit); static uint16 get_cityroad_speedlimit() { return cityroad_speed; } /// @note Replaces max speed of the way by the max speed property of the descriptor. void set_desc(const way_desc_t *b); const way_desc_t *get_desc() const { return desc; } // returns a way with the matching type static weg_t *alloc(waytype_t wt); // returns a string with the "official name of the waytype" static const char *waytype_to_string(waytype_t wt); void rdwr(loadsave_t *file) OVERRIDE; /** * Info-text for this way */ void info(cbuffer_t & buf) const OVERRIDE; /** * @return NULL if OK, otherwise an error message */ const char *get_removal_error(const player_t *player) OVERRIDE; waytype_t get_waytype() const OVERRIDE = 0; /// @copydoc obj_t::get_typ typ get_typ() const OVERRIDE { return obj_t::way; } /** * Die Bezeichnung des Wegs */ const char *get_name() const OVERRIDE { return desc->get_name(); } /** * Add direction bits (ribi) for a way. * * @note After changing of ribi the image of the way is wrong. To correct this, * grund_t::calc_image needs to be called. This is not done here (Too expensive). */ void ribi_add(ribi_t::ribi ribi) { this->ribi |= (uint8)ribi;} /** * Remove direction bits (ribi) for a way. * * @note After changing of ribi the image of the way is wrong. To correct this, * grund_t::calc_image needs to be called. This is not done here (Too expensive). */ void ribi_rem(ribi_t::ribi ribi) { this->ribi &= (uint8)~ribi;} /** * Set direction bits (ribi) for the way. * * @note After changing of ribi the image of the way is wrong. To correct this, * grund_t::calc_image needs to be called. This is not done here (Too expensive). */ void set_ribi(ribi_t::ribi ribi) { this->ribi = (uint8)ribi;} /** * Get the unmasked direction bits (ribi) for the way (without signals or other ribi changer). */ ribi_t::ribi get_ribi_unmasked() const { return (ribi_t::ribi)ribi; } /** * Get the masked direction bits (ribi) for the way (with signals or other ribi changer). */ ribi_t::ribi get_ribi() const { return (ribi_t::ribi)(ribi & ~ribi_maske); } /** * For signals it is necessary to mask out certain ribi to prevent vehicles * from driving the wrong way (e.g. oneway roads) */ void set_ribi_maske(ribi_t::ribi ribi) { ribi_maske = (uint8)ribi; } ribi_t::ribi get_ribi_maske() const { return (ribi_t::ribi)ribi_maske; } /** * called during map rotation */ void rotate90() OVERRIDE; /** * book statistics - is called very often and therefore inline */ void book(int amount, way_statistics type) { statistics[0][type] += amount; } /** * return statistics value * always returns last month's value */ int get_statistics(int type) const { return statistics[1][type]; } sint64 get_stat(int month, int stat_type) const { assert(stat_type #include "../ground/grund.h" #include "../world/simworld.h" #include "../display/simimg.h" #include "simobj.h" #include "../player/simplay.h" #include "../tool/simtool.h" #include "../dataobj/loadsave.h" #include "../dataobj/ribi.h" #include "../dataobj/scenario.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../dataobj/pakset_manager.h" #include "../descriptor/bridge_desc.h" #include "../descriptor/tunnel_desc.h" #include "../descriptor/way_obj_desc.h" #include "../gui/tool_selector.h" #include "../ground/grund.h" #include "../obj/way/weg.h" #include "../obj/way/strasse.h" #include "../tpl/stringhashtable_tpl.h" #include "../utils/simstring.h" #include "bruecke.h" #include "tunnel.h" #include "wayobj.h" #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_mutex_t wayobj_calc_image_mutex; static recursive_mutex_maker_t wayobj_cim_maker(wayobj_calc_image_mutex); #endif // the descriptions ... const way_obj_desc_t *wayobj_t::default_oberleitung=NULL; stringhashtable_tpl wayobj_t::table; wayobj_t::wayobj_t(loadsave_t* const file) : obj_no_info_t(), desc(NULL), diagonal(false), hang(slope_t::flat), nw(false), dir(dir_unknown) { rdwr(file); } wayobj_t::wayobj_t(koord3d const pos, player_t* const owner, ribi_t::ribi const d, way_obj_desc_t const* const b) : obj_no_info_t(pos), desc(b), diagonal(false), hang(slope_t::flat), nw(false), dir(d) { set_owner(owner); } wayobj_t::~wayobj_t() { if(!desc) { return; } if(get_owner()) { player_t::add_maintenance(get_owner(), -desc->get_maintenance(), get_waytype()); } if(desc->is_overhead_line()) { grund_t *gr=welt->lookup(get_pos()); weg_t *weg=NULL; if(gr) { const waytype_t wt = (desc->get_wtyp()==tram_wt) ? track_wt : desc->get_wtyp(); weg = gr->get_weg(wt); if(weg) { // Weg wieder freigeben, wenn das Signal nicht mehr da ist. weg->set_electrify(false); // restore old speed limit sint32 max_speed = weg->get_desc()->get_topspeed(); if (wt==road_wt && weg->hat_gehweg()) { max_speed = weg_t::get_cityroad_speedlimit(); } if(gr->get_typ()==grund_t::tunnelboden) { tunnel_t *t = gr->find(1); if(t) { max_speed = t->get_desc()->get_topspeed(); } } if(gr->get_typ()==grund_t::brueckenboden) { bruecke_t *b = gr->find(1); if(b) { max_speed = b->get_desc()->get_topspeed(); } } weg->set_max_speed(max_speed); } else { dbg->warning("wayobj_t::~wayobj_t()","ground was not a way!"); } } } mark_image_dirty( get_front_image(), 0 ); mark_image_dirty( get_image(), 0 ); grund_t *gr = welt->lookup( get_pos() ); if( gr ) { for( uint8 i = 0; i < 4; i++ ) { // Remove ribis from adjacent wayobj. if( ribi_t::nesw[i] & get_dir() ) { grund_t *next_gr; if( gr->get_neighbour( next_gr, desc->get_wtyp(), ribi_t::nesw[i] ) ) { wayobj_t *wo2 = next_gr->get_wayobj( desc->get_wtyp() ); if( wo2 ) { wo2->mark_image_dirty( wo2->get_front_image(), 0 ); wo2->mark_image_dirty( wo2->get_image(), 0 ); wo2->set_dir( wo2->get_dir() & ~ribi_t::backward(ribi_t::nesw[i]) ); wo2->mark_image_dirty( wo2->get_front_image(), 0 ); wo2->mark_image_dirty( wo2->get_image(), 0 ); wo2->set_flag(obj_t::dirty); } } } } } } void wayobj_t::rdwr(loadsave_t *file) { xml_tag_t t( file, "wayobj_t" ); obj_t::rdwr(file); if(file->is_version_atleast(89, 0)) { uint8 ddir = dir; file->rdwr_byte(ddir); dir = ddir; if(file->is_saving()) { const char *s = desc->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); desc = wayobj_t::table.get(bname); if(desc==NULL) { desc = wayobj_t::table.get(translator::compatibility_name(bname)); if(desc==NULL) { if(strstr(bname,"atenary") || strstr(bname,"electri") || strstr(bname,"power") ) { desc = default_oberleitung; } } if(desc==NULL) { dbg->warning("wayobj_t::rwdr", "description %s for wayobj_t at %d,%d not found, will be removed!", bname, get_pos().x, get_pos().y ); pakset_manager_t::add_missing_paks( bname, MISSING_WAYOBJ ); } else { dbg->warning("wayobj_t::rwdr", "wayobj %s at %d,%d replaced by %s", bname, get_pos().x, get_pos().y, desc->get_name() ); } } } } else { desc = default_oberleitung; dir = dir_unknown; } } void wayobj_t::cleanup(player_t *player) { if(desc) { player_t::book_construction_costs(player, -desc->get_price(), get_pos().get_2d(), desc->get_wtyp()); } } // returns NULL, if removal is allowed // players can remove public owned wayobjs const char *wayobj_t::get_removal_error(const player_t *player) { if( get_owner_nr()==PLAYER_PUBLIC_NR ) { return NULL; } return obj_t::get_removal_error(player); } void wayobj_t::finish_rd() { // (re)set dir if(dir==dir_unknown) { const waytype_t wt = (desc->get_wtyp()==tram_wt) ? track_wt : desc->get_wtyp(); weg_t *w=welt->lookup(get_pos())->get_weg(wt); if(w) { dir = w->get_ribi_unmasked(); } else { dir = ribi_t::all; } } // electrify a way if we are a catenary if(desc->is_overhead_line()) { const waytype_t wt = (desc->get_wtyp()==tram_wt) ? track_wt : desc->get_wtyp(); weg_t *weg = welt->lookup(get_pos())->get_weg(wt); if (wt == decoration_wt) { weg_t *weg2 = welt->lookup(get_pos())->get_weg_nr(1); if (weg2) { weg2->set_electrify(true); if(weg2->get_max_speed()>desc->get_topspeed()) { weg2->set_max_speed(desc->get_topspeed()); } } } if(weg) { // Weg wieder freigeben, wenn das Signal nicht mehr da ist. weg->set_electrify(true); if(weg->get_max_speed()>desc->get_topspeed()) { weg->set_max_speed(desc->get_topspeed()); } } else { dbg->warning("wayobj_t::finish_rd()","ground was not a way!"); } } if(get_owner()) { player_t::add_maintenance(get_owner(), desc->get_maintenance(), desc->get_wtyp()); } } void wayobj_t::rotate90() { obj_t::rotate90(); dir = ribi_t::rotate90( dir); hang = slope_t::rotate90( hang ); } // helper function: gets the ribi on next tile ribi_t::ribi wayobj_t::find_next_ribi(const grund_t *start, ribi_t::ribi const dir, const waytype_t wt) const { grund_t *to; ribi_t::ribi r1 = ribi_t::none; if(start->get_neighbour(to,wt,dir)) { const wayobj_t* wo = to->get_wayobj( wt ); if(wo) { r1 = wo->get_dir(); } } return r1; } void wayobj_t::calc_image() { #ifdef MULTI_THREAD pthread_mutex_lock( &wayobj_calc_image_mutex ); #endif grund_t *gr = welt->lookup(get_pos()); diagonal = false; if(gr) { const waytype_t wt = (desc->get_wtyp()==tram_wt) ? track_wt : desc->get_wtyp(); weg_t *w=gr->get_weg(wt); if(!w) { dbg->error("wayobj_t::calc_image()","without way at (%s)", get_pos().get_str() ); // well, we are not on a way anymore? => delete us cleanup(get_owner()); delete this; gr->set_flag(grund_t::dirty); #ifdef MULTI_THREAD pthread_mutex_unlock( &wayobj_calc_image_mutex ); #endif return; } ribi_t::ribi sec_way_ribi_unmasked = 0; if(wt == decoration_wt) { if (weg_t *sec_w = gr->get_weg_nr(1)) { sec_way_ribi_unmasked = sec_w->get_ribi_unmasked(); } } set_yoff( -gr->get_weg_yoff() ); set_xoff( 0 ); dir &= (w->get_ribi_unmasked() | sec_way_ribi_unmasked); // if there is a slope, we are finished, only four choices here (so far) hang = gr->get_weg_hang(); if(hang!=slope_t::flat) { #ifdef MULTI_THREAD pthread_mutex_unlock( &wayobj_calc_image_mutex ); #endif return; } // find out whether using diagonals or straight lines if(ribi_t::is_bend(dir) && desc->has_diagonal_image()) { ribi_t::ribi r1 = ribi_t::none, r2 = ribi_t::none; // get the ribis of the ways that connect to us // r1 will be 45 degree clockwise ribi (eg northeast->east), r2 will be anticlockwise ribi (eg northeast->north) r1 = find_next_ribi( gr, ribi_t::rotate45(dir), wt ); r2 = find_next_ribi( gr, ribi_t::rotate45l(dir), wt ); // diagonal if r1 or r2 are our reverse and neither one is 90 degree rotation of us diagonal = (r1 == ribi_t::backward(dir) || r2 == ribi_t::backward(dir)) && r1 != ribi_t::rotate90l(dir) && r2 != ribi_t::rotate90(dir); if(diagonal) { // with this, we avoid calling us endlessly // HACK (originally by hajo?) static int rekursion = 0; if(rekursion == 0) { grund_t *to; rekursion++; for(int r = 0; r < 4; r++) { if(gr->get_neighbour(to, wt, ribi_t::nesw[r])) { wayobj_t* wo = to->get_wayobj( wt ); if(wo) { wo->calc_image(); } } } rekursion--; } image_id after = desc->get_front_diagonal_image_id(dir); image_id image = desc->get_back_diagonal_image_id(dir); if(image==IMG_EMPTY && after==IMG_EMPTY) { // no diagonals available diagonal = false; } } } } #ifdef MULTI_THREAD pthread_mutex_unlock( &wayobj_calc_image_mutex ); #endif } /******************** static stuff from here **********************/ /* better use this constrcutor for new wayobj; it will extend a matching obj or make an new one */ void wayobj_t::extend_wayobj(koord3d pos, player_t *new_owner, ribi_t::ribi dir, const way_obj_desc_t *desc, bool keep_existing_faster_way) { grund_t *gr=welt->lookup(pos); if(gr) { wayobj_t *existing_wayobj = gr->get_wayobj( desc->get_wtyp() ); if( existing_wayobj ) { const bool is_speed_ok = existing_wayobj->get_desc()->get_topspeed() < desc->get_topspeed() || !keep_existing_faster_way; const bool is_owner_ok = player_t::check_owner(new_owner, existing_wayobj->get_owner()) || new_owner == welt->get_public_player(); if( is_speed_ok && is_owner_ok && existing_wayobj->get_desc() != desc) { // replace slower by faster if desired dir = dir | existing_wayobj->get_dir(); gr->set_flag(grund_t::dirty); delete existing_wayobj; } else { // extend this one instead existing_wayobj->set_dir(dir|existing_wayobj->get_dir()); existing_wayobj->mark_image_dirty( existing_wayobj->get_front_image(), 0 ); existing_wayobj->mark_image_dirty( existing_wayobj->get_image(), 0 ); existing_wayobj->set_flag(obj_t::dirty); return; } } // nothing found => make a new one wayobj_t *wo = new wayobj_t(pos,new_owner,dir,desc); gr->obj_add(wo); wo->finish_rd(); wo->calc_image(); wo->mark_image_dirty( wo->get_front_image(), 0 ); wo->set_flag(obj_t::dirty); player_t::book_construction_costs( new_owner, -desc->get_price(), pos.get_2d(), desc->get_wtyp()); for( uint8 i = 0; i < 4; i++ ) { // Extend wayobjects around the new one, that aren't already connected. if( ribi_t::nesw[i] & ~wo->get_dir() ) { grund_t *next_gr; if( gr->get_neighbour( next_gr, desc->get_wtyp(), ribi_t::nesw[i] ) ) { wayobj_t *wo2 = next_gr->get_wayobj( desc->get_wtyp() ); if( wo2 ) { wo2->set_dir( wo2->get_dir() | ribi_t::backward(ribi_t::nesw[i]) ); wo2->mark_image_dirty( wo2->get_front_image(), 0 ); wo->set_dir( wo->get_dir() | ribi_t::nesw[i] ); wo->mark_image_dirty( wo->get_front_image(), 0 ); } } } } } } // to sort wayobj for always the same menu order static bool compare_wayobj_desc(const way_obj_desc_t* a, const way_obj_desc_t* b) { int diff = a->get_wtyp() - b->get_wtyp(); if (diff == 0) { diff = a->get_topspeed() - b->get_topspeed(); } if (diff == 0) { /* Some speed: sort by name */ diff = strcmp(a->get_name(), b->get_name()); } return diff < 0; } bool wayobj_t::successfully_loaded() { if(table.empty()) { dbg->warning("wayobj_t::successfully_loaded()", "No obj found - may crash when loading catenary."); } way_obj_desc_t const* def = 0; for(auto const& i : table) { way_obj_desc_t const& b = *i.value; if (!b.is_overhead_line()) continue; if (b.get_wtyp() != track_wt) continue; if (def && def->get_topspeed() >= b.get_topspeed()) continue; def = &b; } default_oberleitung = def; return true; } bool wayobj_t::register_desc(way_obj_desc_t *desc) { // avoid duplicates with same name if( const way_obj_desc_t *old_desc = table.remove(desc->get_name()) ) { tool_t::general_tool.remove( old_desc->get_builder() ); delete old_desc->get_builder(); delete old_desc; } if( desc->get_cursor()->get_image_id(1)!=IMG_EMPTY ) { // only add images for wayobjexts with cursor ... tool_build_wayobj_t *tool = new tool_build_wayobj_t(); tool->set_icon( desc->get_cursor()->get_image_id(1) ); tool->cursor = desc->get_cursor()->get_image_id(0); tool->set_default_param(desc->get_name()); tool_t::general_tool.append( tool ); desc->set_builder( tool ); } else { desc->set_builder( NULL ); } table.put(desc->get_name(), desc); DBG_DEBUG( "wayobj_t::register_desc()","%s", desc->get_name() ); return true; } /** * Fill menu with icons of given wayobjects from the list */ void wayobj_t::fill_menu(tool_selector_t *tool_selector, waytype_t wtyp, sint16 /*sound_ok*/) { // check if scenario forbids this if (!welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_WAYOBJ | GENERAL_TOOL, wtyp)) { return; } bool enable = welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_WAYOBJ | GENERAL_TOOL, wtyp, 0 ); const uint16 time=welt->get_timeline_year_month(); vector_tplmatching; for(auto const& i : table) { way_obj_desc_t const* const desc = i.value; if( desc->is_available(time) ) { DBG_DEBUG("wayobj_t::fill_menu()", "try to add %s(%p)", desc->get_name(), desc); // only add items with a cursor if( desc->get_builder() && wtyp==desc->get_wtyp() ) { if (welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_WAYOBJ | GENERAL_TOOL, wtyp, desc->get_name())) { matching.append(desc); } } } } // sort the tools before adding to menu std::sort(matching.begin(), matching.end(), compare_wayobj_desc); for(way_obj_desc_t const* const i : matching) { i->get_builder()->enabled = enable && welt->get_scenario()->is_tool_enabled(welt->get_active_player(), TOOL_BUILD_WAYOBJ | GENERAL_TOOL, wtyp, i->get_name()); tool_selector->add_tool_selector(i->get_builder()); } } const way_obj_desc_t *wayobj_t::get_overhead_line(waytype_t wt, uint16 time) { for(auto const& i : table) { way_obj_desc_t const* const desc = i.value; if( desc->is_available(time) && desc->get_wtyp()==wt && desc->is_overhead_line() ) { return desc; } } return NULL; } const way_obj_desc_t* wayobj_t::find_desc(const char *str) { return wayobj_t::table.get(str); } simutrans-124.5/src/simutrans/obj/wayobj.h000066400000000000000000000060551520675460500206240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WAYOBJ_H #define OBJ_WAYOBJ_H #include "../simtypes.h" #include "../display/simimg.h" #include "simobj.h" #include "../dataobj/ribi.h" #include "../descriptor/way_obj_desc.h" #include "../tpl/stringhashtable_tpl.h" class player_t; class karte_t; class koord; class grund_t; class tool_selector_t; /* wayobj enable various functionality of ways, most prominent are overhead power lines */ class wayobj_t : public obj_no_info_t { private: const way_obj_desc_t *desc; uint8 diagonal:1; uint8 hang:7; // direction of this wayobj uint8 nw:1; uint8 dir:7; static const uint8 dir_unknown = 127; ribi_t::ribi find_next_ribi(const grund_t *start, const ribi_t::ribi dir, const waytype_t wt) const; public: wayobj_t(koord3d pos, player_t *owner, ribi_t::ribi dir, const way_obj_desc_t *desc); wayobj_t(loadsave_t *file); virtual ~wayobj_t(); const way_obj_desc_t *get_desc() const {return desc;} void rotate90() OVERRIDE; /** * the back image, drawn before vehicles */ image_id get_image() const OVERRIDE { return hang!=slope_t::flat ? desc->get_back_slope_image_id(hang) : (dir>16 ? desc->get_crossing_image_id(dir,nw,false) : (diagonal ? desc->get_back_diagonal_image_id(dir) : desc->get_back_image_id(dir)) ); } /** * the front image, drawn after everything else */ image_id get_front_image() const OVERRIDE { return hang!=slope_t::flat ? desc->get_front_slope_image_id(hang) : (dir>16 ? desc->get_crossing_image_id(dir,nw,true) : (diagonal ? desc->get_front_diagonal_image_id(dir) : desc->get_front_image_id(dir)) ); } typ get_typ() const OVERRIDE { return wayobj; } /** * waytype associated with this object */ waytype_t get_waytype() const OVERRIDE { return desc ? desc->get_wtyp() : invalid_wt; } void calc_image() OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; void cleanup(player_t *player) OVERRIDE; const char *get_removal_error(const player_t *player) OVERRIDE; void finish_rd() OVERRIDE; // specific for wayobj void set_dir(ribi_t::ribi d) { dir = d; calc_image(); } ribi_t::ribi get_dir() const { return dir; } /* the static routines */ private: static stringhashtable_tpl table; public: static const way_obj_desc_t *default_oberleitung; // use this constructor; it will extend a matching existing wayobj static void extend_wayobj(koord3d pos, player_t *owner, ribi_t::ribi dir, const way_obj_desc_t *desc, bool keep_existing_faster_way); static bool register_desc(way_obj_desc_t *desc); static bool successfully_loaded(); // search an object (currently only used by AI for catenary) static const way_obj_desc_t *get_overhead_line(waytype_t wt, uint16 time); static const way_obj_desc_t *find_desc(const char *); /** * Fill menu with icons of given stops from the list */ static void fill_menu(tool_selector_t *tool_selector, waytype_t wtyp, sint16 sound_ok); static const stringhashtable_tpl& get_list() { return table; } }; #endif simutrans-124.5/src/simutrans/obj/wolke.cc000066400000000000000000000076571520675460500206210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../world/simworld.h" #include "simobj.h" #include "../utils/simrandom.h" #include "wolke.h" #include "../dataobj/loadsave.h" #include "../descriptor/factory_desc.h" #include "../tpl/vector_tpl.h" vector_tplwolke_t::all_clouds(0); freelist_iter_tpl wolke_t::fl; bool wolke_t::register_desc(const skin_desc_t* desc) { // avoid duplicates with same name for(skin_desc_t const* & i : all_clouds) { if (strcmp(i->get_name(), desc->get_name()) == 0) { i = desc; return true; } } return all_clouds.append_unique( desc ); } wolke_t::wolke_t(koord3d pos, sint8 b_x_off, sint8 b_y_off, sint16 b_h_off, uint16 lt, uint16 ul, const skin_desc_t* desc ) : obj_no_info_t(pos) { cloud_nr = all_clouds.index_of(desc); base_y_off = b_h_off; set_xoff( b_x_off ); set_yoff( b_y_off ); insta_zeit = 0; lifetime = lt; uplift = ul; } wolke_t::~wolke_t() { mark_image_dirty( get_image(), calc_yoff() ); } wolke_t::wolke_t(loadsave_t* const file) : obj_no_info_t() { rdwr(file); } image_id wolke_t::get_front_image() const { const skin_desc_t *desc = all_clouds[cloud_nr]; return desc->get_image_id( (insta_zeit*desc->get_count())/(lifetime+1) ); } sint16 wolke_t::calc_yoff() const { return tile_raster_scale_y( base_y_off - (((long)insta_zeit * uplift * OBJECT_OFFSET_STEPS) >> 16), gfx->get_current_tile_raster_width() ); } void wolke_t::rdwr(loadsave_t *file) { // not saving clouds! (and loading only for compatibility) assert(file->is_loading()); obj_t::rdwr( file ); cloud_nr = 0; insta_zeit = 0; uint32 ldummy = 0; file->rdwr_long(ldummy); uint16 dummy = 0; file->rdwr_short(dummy); file->rdwr_short(dummy); // do not remove from this position, since there will be nothing obj_t::set_flag(obj_t::not_on_map); } sync_result wolke_t::sync_step(uint32 delta_t) { // we query the image twice, since it may have changed (there are sure more efficient ways for that ... const image_id old_img = get_front_image(); const sint16 old_yoff = calc_yoff(); insta_zeit += delta_t; if( insta_zeit >= lifetime ) { // delete wolke ... set_flag( obj_t::dirty ); mark_image_dirty( old_img, old_yoff ); insta_zeit = lifetime; return SYNC_DELETE; } const image_id new_img = get_front_image(); // move cloud up const sint16 new_yoff = calc_yoff(); if( new_img != old_img ) { // change cloud set_flag( obj_t::dirty ); mark_image_dirty( old_img, old_yoff ); } if( new_yoff != old_yoff ) { // move cloud set_flag( obj_t::dirty ); mark_image_dirty( old_img, old_yoff ); // wind effect set_xoff( get_xoff() - sim_async_rand(2)); } return SYNC_OK; } #ifdef MULTI_THREAD void wolke_t::display_after( int xpos, int ypos, const sint8 extra_param ) const #else void wolke_t::display_after( int xpos, int ypos, bool extra_param ) const #endif { obj_t::display_after( xpos, ypos + calc_yoff(), extra_param ); } // called during map rotation void wolke_t::rotate90() { // most basic: rotate coordinate obj_t::rotate90(); if( lifetime != DEFAULT_EXHAUSTSMOKE_TIME ) { // since factory smoke may come from the wrong tile, remove it on rotation insta_zeit = lifetime; } } /***************************** just for compatibility, the old raucher and smoke clouds *********************************/ raucher_t::raucher_t(loadsave_t *file) : obj_t() { assert(file->is_loading()); obj_t::rdwr( file ); const char *s = NULL; file->rdwr_str(s); free(const_cast(s)); // do not remove from this position, since there will be nothing obj_t::set_flag(obj_t::not_on_map); } async_wolke_t::async_wolke_t(loadsave_t *file) : obj_t() { // not saving clouds! assert(file->is_loading()); obj_t::rdwr( file ); uint32 dummy; file->rdwr_long(dummy); // do not remove from this position, since there will be nothing obj_t::set_flag(obj_t::not_on_map); } simutrans-124.5/src/simutrans/obj/wolke.h000066400000000000000000000046471520675460500204570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_WOLKE_H #define OBJ_WOLKE_H #include "simobj.h" #include "../descriptor/skin_desc.h" #include "../tpl/vector_tpl.h" #include "../display/simimg.h" #include "../tpl/freelist_iter_tpl.h" class karte_t; #define DEFAULT_EXHAUSTSMOKE_TIME (2497) /** * smoke clouds (formerly sync_wolke_t) */ class wolke_t : public obj_no_info_t { private: static vector_tplall_clouds; uint16 insta_zeit; // clouds vanish when insta_zeit>2500 => maximum 5 images ... sint16 base_y_off; // since sint8 may overflow wiht larger pak sizes uint16 lifetime; // since factories generate other smoke than uint16 uplift; uint8 cloud_nr; sint16 calc_yoff() const; // calculate the smoke height using uplift and insta_zeit static freelist_iter_tpl fl; // if not declared static, it would consume 4 bytes due to empty class nonzero rules public: static bool register_desc(const skin_desc_t *desc); wolke_t(loadsave_t *file); wolke_t(koord3d pos, sint8 xoff, sint8 yoff, sint16 hoff, uint16 lifetime, uint16 uplift, const skin_desc_t *cloud ); ~wolke_t(); sync_result sync_step(uint32 delta_t); void* operator new(size_t) { return fl.gimme_node(); } void operator delete(void* p) { return fl.putback_node(p); } static void sync_handler(uint32 delta_t) { fl.sync_step(delta_t); } const char* get_name() const OVERRIDE { return "Wolke"; } typ get_typ() const OVERRIDE { return cloud; } image_id get_image() const OVERRIDE { return IMG_EMPTY; } image_id get_front_image() const OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; void rotate90() OVERRIDE; /** * Draw foreground image * (everything that is in front of vehicles) */ #ifdef MULTI_THREAD virtual void display_after( int xpos, int ypos, const sint8 clip_num ) const OVERRIDE; #else virtual void display_after( int xpos, int ypos, bool is_global ) const OVERRIDE; #endif }; /** * following two classes are just for compatibility for old save games */ class async_wolke_t : public obj_t { public: async_wolke_t(loadsave_t *file); typ get_typ() const OVERRIDE { return old_async_wolke; } image_id get_image() const OVERRIDE { return IMG_EMPTY; } }; class raucher_t : public obj_t { public: raucher_t(loadsave_t *file); typ get_typ() const OVERRIDE { return old_raucher; } image_id get_image() const OVERRIDE { return IMG_EMPTY; } }; #endif simutrans-124.5/src/simutrans/obj/zeiger.cc000066400000000000000000000051451520675460500207530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /** @file zeiger.cc object to mark tiles */ #include #include "../world/simworld.h" #include "simobj.h" #include "../simhalt.h" #include "../ground/grund.h" #include "../dataobj/environment.h" #include "zeiger.h" zeiger_t::zeiger_t(loadsave_t *file) : obj_no_info_t() { image = IMG_EMPTY; foreground_image = IMG_EMPTY; area = koord(0,0); offset = koord(0,0); rdwr(file); } zeiger_t::zeiger_t(koord3d pos, player_t *player) : obj_no_info_t(pos) { set_owner( player ); image = IMG_EMPTY; foreground_image = IMG_EMPTY; area = koord(0,0); offset = koord(0,0); } /** * We want to be able to highlight the current tile. * Unmarks area around old and marks area around new position. * Use this routine to change position. */ void zeiger_t::change_pos(koord3d k ) { if( k != get_pos() ) { // remove from old position // and clear mark grund_t *gr = welt->lookup( get_pos() ); if(gr==NULL) { gr = welt->lookup_kartenboden( get_pos().get_2d() ); } if(gr) { if( gr->get_halt().is_bound() ) { gr->get_halt()->mark_unmark_coverage( false ); } welt->mark_area( get_pos()- offset, area, false ); } if( get_pos().x >= welt->get_size().x-1 || get_pos().y >= welt->get_size().y-1 ) { // the raise and lower tool actually can go to size! welt->set_background_dirty(); // this removes crap form large cursors overlapping into the nirvana } mark_image_dirty( get_image(), 0 ); mark_image_dirty( get_front_image(), 0 ); set_flag( obj_t::dirty ); obj_t::set_pos( k ); if( get_yoff() == Z_PLAN ) { gr = welt->lookup( k ); if( gr == NULL ) { gr = welt->lookup_kartenboden( k.get_2d() ); } if(gr) { if( gr->get_halt().is_bound() && env_t::station_coverage_show ) { gr->get_halt()->mark_unmark_coverage( true ); } welt->mark_area( k-offset, area, true ); } } } } void zeiger_t::set_image( image_id b ) { // mark dirty mark_image_dirty( image, 0 ); mark_image_dirty( b, 0 ); image = b; } void zeiger_t::set_foreground_image( image_id b ) { // mark dirty mark_image_dirty( foreground_image, 0 ); mark_image_dirty( b, 0 ); foreground_image = b; } void zeiger_t::set_area(koord new_area, bool center, koord new_offset) { if (center) { new_offset = new_area/2; } if(new_area==area && new_offset==offset) { return; } welt->mark_area( get_pos()-offset, area, false ); area = new_area; offset = new_offset; welt->mark_area( get_pos()-offset, area, true ); } bool zeiger_t::has_managed_lifecycle() const { return true; } simutrans-124.5/src/simutrans/obj/zeiger.h000066400000000000000000000025341520675460500206140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OBJ_ZEIGER_H #define OBJ_ZEIGER_H #include "simobj.h" #include "../simtypes.h" #include "../display/simimg.h" /** @file zeiger.h object to mark tiles */ /** * These objects mark tiles for tools. It marks the current tile * pointed to by mouse pointer (and possibly an area around it). */ class zeiger_t : public obj_no_info_t { private: koord area, offset; /// images image_id image, foreground_image; public: zeiger_t(loadsave_t *file); zeiger_t(koord3d pos, player_t *player); void change_pos(koord3d k); const char *get_name() const OVERRIDE {return "Zeiger";} typ get_typ() const OVERRIDE { return zeiger; } /** * Set area to be marked around cursor * @param area size of marked area * @param center true if cursor is centered within marked area * @param offset if center==false then cursor is at position @p offset */ void set_area( koord area, bool center, koord offset = koord(0,0) ); /// set back image void set_image( image_id b ); /// get back image image_id get_image() const OVERRIDE {return image;} /// set front image void set_foreground_image( image_id b ); /// get front image image_id get_front_image() const OVERRIDE {return foreground_image;} bool has_managed_lifecycle() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/old_blockmanager.cc000066400000000000000000000152301520675460500221730ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "simdebug.h" #include "world/simworld.h" #include "player/simplay.h" #include "simmesg.h" #include "display/simimg.h" #include "obj/signal.h" #include "obj/tunnel.h" #include "ground/grund.h" #include "obj/way/schiene.h" #include "dataobj/loadsave.h" #include "dataobj/translator.h" #include "dataobj/environment.h" #include "tpl/slist_tpl.h" #include "old_blockmanager.h" // only needed for loading old games class oldsignal_t : public obj_t { protected: uint8 state; uint8 blockend; uint8 dir; obj_t::typ type; public: oldsignal_t(loadsave_t *file, obj_t::typ type); /** * return direction or the state of the traffic light */ ribi_t::ribi get_dir() const { return dir; } bool ist_blockiert() const {return blockend != 0;} obj_t::typ get_typ() const OVERRIDE { return type; } void rdwr(loadsave_t *file) OVERRIDE; image_id get_image() const OVERRIDE { return IMG_EMPTY; } }; static slist_tpl signale; //------------------------- old blockmanager ---------------------------- // only there to convert old games to 89.02 and higher // these two routines for compatibility oldsignal_t::oldsignal_t(loadsave_t *file, obj_t::typ type) : obj_t () { this->type = type; rdwr(file); } void oldsignal_t::rdwr(loadsave_t *file) { if(!file->is_loading()) { dbg->fatal("oldsignal_t::rdwr()","cannot be saved!"); } // loading from blockmanager! obj_t::rdwr(file); file->rdwr_byte(blockend); file->rdwr_byte(state); file->rdwr_byte(dir); } // now the old block reader void old_blockmanager_t::rdwr_block(karte_t *,loadsave_t *file) { sint32 count; short int typ = obj_t::signal; // load signal file->rdwr_long(count); for(int i=0; ird_obj_id(); oldsignal_t *sig = new oldsignal_t(file, (obj_t::typ)typ); DBG_MESSAGE("oldsignal_t()","on %i,%i with dir=%i blockend=%i",sig->get_pos().x,sig->get_pos().y,sig->get_dir(),sig->ist_blockiert()); signale.insert( sig ); } // counters if(file->is_version_less(88, 6)) { // old style sint32 dummy = 0; file->rdwr_long(dummy); file->rdwr_long(dummy); } else { sint16 dummy; file->rdwr_short(dummy); } } void old_blockmanager_t::rdwr(karte_t *welt, loadsave_t *file) { signale.clear(); if(file->is_version_atleast(89, 0)) { // nothing to do any more ... return; } assert(file->is_loading()); // this routine just reads the of signal positions // and converts them to the new type> sint32 count; file->rdwr_long(count); for(int i=0; ilookup(os1->get_pos()); grund_t *to=NULL; uint8 directions=0; waytype_t wt=gr->hat_weg(track_wt) ? track_wt : monorail_wt; if( gr->get_neighbour(to,wt,os1->get_dir()) ) { for(oldsignal_t* const s : signale) { if (s->get_pos() == to->get_pos()) { os2 = s; break; } } if(os2==NULL) { dbg->error("old_blockmanager_t::finish_rd()","old signal near (%i,%i) is unpaired!",gr->get_pos().x,gr->get_pos().y); welt->get_message()->add_message(translator::translate("Orphan signal during loading!"),os1->get_pos(),message_t::problems); } } else { dbg->error("old_blockmanager_t::finish_rd()","old signal near (%i,%i) is unpaired!",gr->get_pos().x,gr->get_pos().y); welt->get_message()->add_message(translator::translate("Orphan signal during loading!"),os1->get_pos(),message_t::problems); } // remove second signal from list if(os2) { signale.remove(os2); } // now we should have a pair of signals ... or something was very wrong grund_t* new_signal_gr = 0; roadsign_desc_t::types type = roadsign_desc_t::SIGN_SIGNAL; ribi_t::ribi dir = 0; // now find out about type and direction if(os2 && !os2->ist_blockiert()) { // built the signal here, if possible grund_t *tmp=to; to = gr; gr = tmp; if(os2->get_typ()==obj_t::old_presignal) { type = roadsign_desc_t::SIGN_PRE_SIGNAL; } else if(os2->get_typ()==obj_t::old_choosesignal) { type |= roadsign_desc_t::CHOOSE_SIGN; } dir = os2->get_dir(); directions = 1; } else { // gr is already the first choice // so we just have to determine the type if(os1->get_typ()==obj_t::old_presignal) { type = roadsign_desc_t::SIGN_PRE_SIGNAL; } else if(os1->get_typ()==obj_t::old_choosesignal) { type |= roadsign_desc_t::CHOOSE_SIGN; } } // take care of one way if(!os1->ist_blockiert()) { directions ++; dir |= os1->get_dir(); } // now check where we can built best if(gr->hat_weg(wt) && ribi_t::is_twoway(gr->get_weg(wt)->get_ribi_unmasked())) { new_signal_gr = gr; } if((new_signal_gr==NULL || !os1->ist_blockiert()) && to && to->hat_weg(wt) && ribi_t::is_twoway(to->get_weg(wt)->get_ribi_unmasked())) { new_signal_gr = to; } if(directions==2 && new_signal_gr) { dir = new_signal_gr->get_weg(wt)->get_ribi_unmasked(); } // found a suitable location, ribi, signal type => construct if(new_signal_gr && dir!=0) { const roadsign_desc_t *sb=roadsign_t::roadsign_search(type,wt,0); if(sb!=NULL) { signal_t *sig = new signal_t(new_signal_gr->get_weg(wt)->get_owner(),new_signal_gr->get_pos(),dir,sb); new_signal_gr->obj_add(sig); //DBG_MESSAGE("old_blockmanager::finish_rd()","signal restored at %i,%i with dir %i",gr->get_pos().x,gr->get_pos().y,dir); } else { dbg->error("old_blockmanager_t::finish_rd()","no roadsign for way %x with type %d found!",type,wt); sprintf(buf,err_text,os1->get_pos().x,os1->get_pos().y); welt->get_message()->add_message(buf,os1->get_pos(),message_t::problems); failure++; } } else { dbg->warning("old_blockmanager_t::finish_rd()","could not restore old signal near (%i,%i), dir=%i",gr->get_pos().x,gr->get_pos().y,dir); sprintf(buf,err_text,os1->get_pos().x,os1->get_pos().y); welt->get_message()->add_message(buf,os1->get_pos(),message_t::problems); failure ++; } os1->set_pos(koord3d::invalid); delete os1; if(os2) { os2->set_pos(koord3d::invalid); delete os2; } } if(failure) { dbg->warning("old_blockmanager_t::finish_rd()","failed on %d signal pairs.",failure); } } simutrans-124.5/src/simutrans/old_blockmanager.h000066400000000000000000000007541520675460500220420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef OLD_BLOCKMANAGER_H #define OLD_BLOCKMANAGER_H class karte_t; class loadsave_t; /** * Old class to manage track reservations. * Only needed to load very old savegames. */ class old_blockmanager_t { private: static void rdwr_block(karte_t *welt,loadsave_t *file); public: static void rdwr(karte_t *welt, loadsave_t *file); static void finish_rd(karte_t *welt); }; #endif simutrans-124.5/src/simutrans/pathes.h000066400000000000000000000014641520675460500200420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef PATHES_H #define PATHES_H /** * This header defines all paths used be simutrans relative to the game directory. * * \parblock * two defines for all paths - if You want the root path, use: * #define _PATH "" * #define _PATH_X "" * else use * #define _PATH "somewhere" * #define _PATH_X _PATH "/" * \endparblock */ #define USER_PAK_PATH "simutrans" #define USER_PAK_PATH_X USER_PAK_PATH "/" #define FONT_PATH "font" #define FONT_PATH_X FONT_PATH "/" #define SAVE_PATH "save" #define SAVE_PATH_X SAVE_PATH "/" #define SAVE_PATH_X_LEN (sizeof(SAVE_PATH_X) - 1) #define SCREENSHOT_PATH "screenshot" #define SCREENSHOT_PATH_X SCREENSHOT_PATH "/" #endif simutrans-124.5/src/simutrans/player/000077500000000000000000000000001520675460500176745ustar00rootroot00000000000000simutrans-124.5/src/simutrans/player/ai.cc000066400000000000000000000414361520675460500206040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "finance.h" #include "ai.h" #include "../world/simcity.h" #include "../simconvoi.h" #include "../simhalt.h" #include "../simintr.h" #include "../tool/simmenu.h" #include "../simskin.h" #include "../simware.h" #include "../builder/brueckenbauer.h" #include "../builder/hausbauer.h" #include "../builder/tunnelbauer.h" #include "../builder/vehikelbauer.h" #include "../builder/wegbauer.h" #include "../descriptor/building_desc.h" #include "../dataobj/environment.h" #include "../dataobj/loadsave.h" #include "../obj/zeiger.h" #include "../utils/cbuffer.h" #include "../vehicle/vehicle.h" /* The flesh for the place with road for headquarters searcher ... */ bool ai_building_place_with_road_finder::is_road_at(sint16 x, sint16 y) const { grund_t *bd = welt->lookup_kartenboden( koord(x,y) ); return bd && bd->hat_weg(road_wt); } bool ai_building_place_with_road_finder::is_area_ok(koord pos, sint16 w, sint16 h, climate_bits cl) const { if(building_placefinder_t::is_area_ok(pos, w, h, cl)) { // check to not built on a road int i, j; for(j=pos.x; jlookup_kartenboden(pos) ) { return haltestelle_t::get_halt( gr->get_pos(), this ); } return halthandle_t(); } /** * returns true, * if there is already a connection */ bool ai_t::is_connected( const koord start_pos, const koord dest_pos, const goods_desc_t *wtyp ) const { // Dario: Check if there's a stop near destination const planquadrat_t* start_plan = welt->access(start_pos); const halthandle_t* start_list = start_plan->get_haltlist(); const uint16 start_halt_count = start_plan->get_haltlist_count(); // now try to find a route // ok, they are not in walking distance ware_t ware(wtyp); ware.set_target_pos(dest_pos); ware.amount = 1; return (start_halt_count != 0) && (haltestelle_t::search_route( start_list, start_halt_count, false, ware ) != haltestelle_t::NO_ROUTE); } // calls a general tool just like a player work do bool ai_t::call_general_tool( int tool, koord k, const char *param ) { grund_t *gr = welt->lookup_kartenboden(k); koord3d pos = gr ? gr->get_pos() : koord3d::invalid; const char *old_param = tool_t::general_tool[tool]->get_default_param(); tool_t::general_tool[tool]->set_default_param(param); const char * err = tool_t::general_tool[tool]->work( this, pos ); if(err) { if(*err) { dbg->message("ai_t::call_general_tool()","failed for tool %i at (%s) because of \"%s\"", tool, pos.get_str(), err ); } else { dbg->message("ai_t::call_general_tool()","not successful for tool %i at (%s)", tool, pos.get_str() ); } } tool_t::general_tool[tool]->set_default_param(old_param); return err==0; } /* returns ok, of there is a suitable space found * only check into two directions, the ones given by dir */ bool ai_t::suche_platz(koord pos, koord &size, koord *dirs) { sint16 length = abs( size.x + size.y ); const grund_t *gr = welt->lookup_kartenboden(pos); if( gr==NULL ) { return false; } sint8 start_z = gr->get_hoehe(); int max_dir = length==0 ? 1 : 2; bool place_ok; // two rotations for( int dir=0; dirlookup_kartenboden( pos + (dirs[dir]*i) ); if( gr == NULL || gr->get_halt().is_bound() || !welt->can_flatten_tile(this, pos, start_z ) || !gr->ist_natur() || gr->kann_alle_obj_entfernen(this) != NULL || gr->get_hoehe() < welt->get_water_hgt( pos + (dirs[dir] * i) ) ) { // something is blocking construction, try next rotation place_ok = false; break; } } if( place_ok ) { // apparently we can build this rotation here size = dirs[dir]*length; return true; } } return false; } /** needed renovation due to different sized factories * also try "nicest" place first */ bool ai_t::suche_platz(koord &start, koord &size, koord target, koord off) { // distance of last found point int dist=0x7FFFFFFF; koord platz; int const cov = welt->get_settings().get_station_coverage(); int xpos = start.x; int ypos = start.y; koord dir[2]; if( abs(start.x-target.x)get_size().y, ypos + off.y + cov ); int maxx = min( welt->get_size().x, xpos + off.x + cov ); for (int y = max(0,ypos-cov); y < maxy; y++) { for (int x = max(0,xpos-cov); x < maxx; x++) { platz = koord(x,y); // no water tiles if( welt->lookup_kartenboden(platz)->get_hoehe() <= welt->get_water_hgt(platz) ) { continue; } // thus now check them int current_dist = koord_distance(platz,target); if( current_distlookup_kartenboden(pos); zeiger_t *z = gr->find(); delete z; } } } void ai_t::set_marker( koord place, koord size ) { koord pos; if(size.y<0) { place.y += size.y; size.y = -size.y; } if(size.x<0) { place.x += size.x; size.x = -size.x; } for( pos.y=place.y; pos.y<=place.y+size.y; pos.y++ ) { for( pos.x=place.x; pos.x<=place.x+size.x; pos.x++ ) { grund_t *gr = welt->lookup_kartenboden(pos); zeiger_t *z = new zeiger_t(gr->get_pos(), this); z->set_image( skinverwaltung_t::belegtzeiger->get_image_id(0) ); gr->obj_add( z ); } } } /* builds headquarters or upgrades one */ bool ai_t::built_update_headquarter() { // find next level const building_desc_t* desc = hausbauer_t::get_headquarters(get_headquarter_level(), welt->get_timeline_year_month()); // is the a suitable one? if(desc!=NULL) { // cost is negative! sint64 const cost = welt->get_settings().cst_multiply_headquarter * desc->get_level() * desc->get_x() * desc->get_y(); if( finance->get_account_balance()+cost > finance->get_starting_money() ) { // and enough money left ... koord place = get_headquarter_pos(); if(place!=koord::invalid) { // remove old hq grund_t *gr = welt->lookup_kartenboden(place); gebaeude_t *prev_hq = gr->find(); // other size? if( desc->get_size()!=prev_hq->get_tile()->get_desc()->get_size() ) { // needs new place place = koord::invalid; } else { // old positions false after rotation => correct it place = prev_hq->get_pos().get_2d() - prev_hq->get_tile()->get_offset(); } } // needs new place? if(place==koord::invalid) { stadt_t *st = NULL; for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if( halt->get_owner()==this ) { st = welt->find_nearest_city(halt->get_basis_pos()); break; } } if(st) { bool is_rotate=desc->get_all_layouts()>1; place = ai_building_place_with_road_finder(welt).find_place(st->get_pos(), desc->get_x(), desc->get_y(), desc->get_allowed_climate_bits(), &is_rotate); } } const char *err="No suitable ground!"; if( place!=koord::invalid ) { err = tool_t::general_tool[TOOL_HEADQUARTER]->work( this, welt->lookup_kartenboden(place)->get_pos() ); // success if( err==NULL ) { return true; } } // failed if( place==koord::invalid || err!=NULL ) { dbg->warning( "ai_t::built_update_headquarter()", "HQ failed with : %s", translator::translate(err) ); } return false; } } return false; } /** * Find the last water tile using line algorithm * start MUST be on land! */ koord ai_t::find_shore(koord start, koord end) const { int x = start.x; int y = start.y; int xx = end.x; int yy = end.y; int i, steps; int xp, yp; int xs, ys; const int dx = xx - x; const int dy = yy - y; steps = (abs(dx) > abs(dy) ? abs(dx) : abs(dy)); if (steps == 0) steps = 1; xs = (dx << 16) / steps; ys = (dy << 16) / steps; xp = x << 16; yp = y << 16; koord last = start; for (i = 0; i <= steps; i++) { koord next(xp>>16,yp>>16); if(next!=last) { if(!welt->lookup_kartenboden(next)->is_water()) { last = next; } } xp += xs; yp += ys; } // should always find something, since it ends in water ... return last; } bool ai_t::find_harbour(koord &start, koord &size, koord target) { koord shore = find_shore(target,start); // distance of last found point int dist=0x7FFFFFFF; koord k; // now find a nice shore next to here for( k.y=max(1,shore.y-5); k.yget_size().y-2; k.y++ ) { for( k.x=max(1,shore.x-5); k.xget_size().x-2; k.x++ ) { grund_t *gr = welt->lookup_kartenboden(k); if( gr && gr->get_grund_hang() != 0 && slope_t::is_way( gr->get_grund_hang() ) && gr->ist_natur() && gr->get_hoehe() == welt->get_water_hgt(k) && !gr->is_halt() ) { koord zv = koord(gr->get_grund_hang()); if(welt->lookup_kartenboden(k-zv)->get_weg_ribi(water_wt)) { // next place is also water koord dir[2] = { zv, koord(zv.y,zv.x) }; koord platz = k+zv; int current_dist = koord_distance(k,target); if( current_distget_climate(platz1); climate c2 = welt->get_climate(platz2); if( !(welt->flatten_tile( this, platz1, welt->lookup_kartenboden(platz1)->get_hoehe() ) && welt->flatten_tile( this, platz2, welt->lookup_kartenboden(platz2)->get_hoehe() )) ) { // no flat land here?!? return false; } // ensure is land grund_t* bd = welt->lookup_kartenboden(platz1); if (bd->get_typ() == grund_t::wasser) { welt->set_water_hgt_nocheck(platz1, bd->get_hoehe()-1); welt->access(platz1)->correct_water(); welt->set_climate(platz1, c1, true); } bd = welt->lookup_kartenboden(platz2); if (bd->get_typ() == grund_t::wasser) { welt->set_water_hgt_nocheck(platz2, bd->get_hoehe()-1); welt->access(platz2)->correct_water(); welt->set_climate(platz2, c2, true); } // is there already a connection? // get a default vehicle vehicle_desc_t test_desc(road_wt, 25, vehicle_desc_t::diesel ); vehicle_t* test_driver = vehicle_builder_t::build(welt->lookup_kartenboden(platz1)->get_pos(), this, NULL, &test_desc); test_driver->set_flag( obj_t::not_on_map ); route_t verbindung; if( verbindung.calc_route(welt, welt->lookup_kartenboden(platz1)->get_pos(), welt->lookup_kartenboden(platz2)->get_pos(), test_driver, 0, 0) && verbindung.get_count() < 2u*koord_distance(platz1,platz2)) { DBG_MESSAGE("ai_passenger_t::create_simple_road_transport()","Already connection between %d,%d to %d,%d is only %i",platz1.x, platz1.y, platz2.x, platz2.y, verbindung.get_count() ); // found something with the nearly same length delete test_driver; return true; } delete test_driver; // no connection => built one! way_builder_t bauigel(this); bauigel.init_builder( way_builder_t::strasse, road_weg, tunnel_builder_t::get_tunnel_desc(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()) ); // we won't destroy cities (and save the money) bauigel.set_keep_existing_faster_ways(true); bauigel.set_keep_city_roads(true); bauigel.set_maximum(10000); bauigel.calc_route(welt->lookup_kartenboden(platz1)->get_pos(),welt->lookup_kartenboden(platz2)->get_pos()); INT_CHECK("ai 501"); if( bauigel.calc_costs() > finance->get_netwealth() ) { // too expensive return false; } // now try route with terraforming way_builder_t baumaulwurf(this); baumaulwurf.init_builder( way_builder_t::strasse|way_builder_t::terraform_flag, road_weg, tunnel_builder_t::get_tunnel_desc(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()) ); baumaulwurf.set_keep_existing_faster_ways(true); baumaulwurf.set_keep_city_roads(true); baumaulwurf.set_maximum(10000); baumaulwurf.calc_route(welt->lookup_kartenboden(platz1)->get_pos(),welt->lookup_kartenboden(platz2)->get_pos()); // build with terraforming if shorter and enough money is available bool with_tf = (baumaulwurf.get_count() > 2) && (10*baumaulwurf.get_count() < 9*bauigel.get_count() || bauigel.get_count() <= 2); if( with_tf && baumaulwurf.calc_costs() > finance->get_netwealth() ) { // too expensive with_tf = false; } // now build with or without terraforming if (with_tf) { DBG_MESSAGE("ai_t::create_simple_road_transport()","building not so simple road from %d,%d to %d,%d",platz1.x, platz1.y, platz2.x, platz2.y); baumaulwurf.build(); return true; } else if(bauigel.get_count() > 2) { DBG_MESSAGE("ai_t::create_simple_road_transport()","building simple road from %d,%d to %d,%d",platz1.x, platz1.y, platz2.x, platz2.y); bauigel.build(); return true; } // beware: The stop position might have changes! DBG_MESSAGE("ai_t::create_simple_road_transport()","building simple road from %d,%d to %d,%d failed",platz1.x, platz1.y, platz2.x, platz2.y); return false; } /* create new AI */ ai_t::ai_t(uint8 nr) : player_t( nr ), road_transport(false), rail_transport(false), ship_transport(false), air_transport(false), construction_speed(env_t::default_ai_construction_speed) { } void ai_t::rdwr(loadsave_t *file) { player_t::rdwr(file); if( file->is_version_less(111, 1) ) { // do not know about ai_t return; } file->rdwr_long( construction_speed ); file->rdwr_bool( road_transport ); file->rdwr_bool( rail_transport ); file->rdwr_bool( air_transport ); file->rdwr_bool( ship_transport ); } const vehicle_desc_t *ai_t::vehicle_search(waytype_t typ, const uint32 target_power, const sint32 target_speed, const goods_desc_t * target_freight, bool include_electric) { bool obsolete_allowed = welt->get_settings().get_allow_buying_obsolete_vehicles(); return vehicle_builder_t::vehicle_search(typ, welt->get_timeline_year_month(), target_power, target_speed, target_freight, include_electric, !obsolete_allowed); } simutrans-124.5/src/simutrans/player/ai.h000066400000000000000000000056441520675460500204470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef PLAYER_AI_H #define PLAYER_AI_H #include "simplay.h" #include "../world/building_placefinder.h" class karte_t; class vehicle_desc_t; class goods_desc_t; /** * Search for a free location using the function find_place(). */ class ai_building_place_with_road_finder : public building_placefinder_t { public: ai_building_place_with_road_finder(karte_t *welt) : building_placefinder_t(welt) {} bool is_road_at(sint16 x, sint16 y) const; bool is_area_ok(koord pos, sint16 w, sint16 h, climate_bits cl) const OVERRIDE; }; // AI helper functions class ai_t : public player_t { protected: // set the allowed modes of transport bool road_transport; bool rail_transport; bool ship_transport; bool air_transport; // the shorter the faster construction will occur sint32 construction_speed; public: ai_t(uint8 nr); bool has_road_transport() const { return road_transport; } virtual void set_road_transport( bool yesno ) { road_transport = yesno; } bool has_rail_transport() const { return rail_transport; } virtual void set_rail_transport( bool yesno ) { rail_transport = yesno; } bool has_ship_transport() const { return ship_transport; } virtual void set_ship_transport( bool yesno ) { ship_transport = yesno; } bool has_air_transport() const { return air_transport; } virtual void set_air_transport( bool yesno ) { air_transport = yesno; } sint32 get_construction_speed() const { return construction_speed; } virtual void set_construction_speed( sint32 newspeed ) { construction_speed = newspeed; } void rdwr(loadsave_t *file) OVERRIDE; // return true, if there is already a connection bool is_connected(const koord star_pos, const koord end_pos, const goods_desc_t *wtyp) const; // calls a general tool just like a human player work do bool call_general_tool( int tool, koord k, const char *param ); // find space for stations bool suche_platz(koord pos, koord &size, koord *dirs); bool suche_platz(koord &start, koord &size, koord target, koord off); // removes building markers void clean_marker( koord place, koord size ); void set_marker( koord place, koord size ); halthandle_t get_halt( const koord haltpos ) const; /** * Find the first water tile using line algorithm * start MUST be on land! */ koord find_shore(koord start, koord end) const; bool find_harbour(koord &start, koord &size, koord target); bool built_update_headquarter(); // builds a round between those two places or returns false bool create_simple_road_transport(koord platz1, koord size1, koord platz2, koord size2, const way_desc_t *road ); /// helper method to call vehicle_builder_t::vehicle_search and fill in time-line related parameters static const vehicle_desc_t *vehicle_search(waytype_t typ, const uint32 target_power, const sint32 target_speed, const goods_desc_t * target_freight, bool include_electric); }; #endif simutrans-124.5/src/simutrans/player/ai_goods.cc000066400000000000000000001463401520675460500217770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simconvoi.h" #include "../simfab.h" #include "../tool/simmenu.h" #include "../simtypes.h" #include "../tool/simtool.h" #include "../simunits.h" #include "finance.h" #include "simplay.h" #include "../simhalt.h" #include "../simintr.h" #include "../simline.h" #include "../simmesg.h" #include "../world/simworld.h" #include "../builder/brueckenbauer.h" #include "../builder/hausbauer.h" #include "../builder/tunnelbauer.h" #include "../builder/vehikelbauer.h" #include "../builder/wegbauer.h" #include "../dataobj/schedule.h" #include "../dataobj/loadsave.h" #include "../obj/wayobj.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "../utils/cbuffer.h" #include "../vehicle/vehicle.h" #include "ai_goods.h" ai_goods_t::ai_goods_t(uint8 nr) : ai_t(nr), state(NR_INIT), root(NULL), start(NULL), ziel(NULL), freight(NULL), rail_vehicle(NULL), rail_engine(NULL), road_vehicle(NULL), ship_vehicle(NULL), rail_weg(NULL), road_weg(NULL), count_rail(0), count_road(0), count(0), next_construction_steps(welt->get_steps()+50) { road_transport = nr!=7; rail_transport = nr>2; ship_transport = true; air_transport = false; } /** * Methode fuer jaehrliche Aktionen */ void ai_goods_t::new_year() { player_t::new_year(); // AI will reconsider the oldest unbuiltable lines again uint remove = (uint)max(0,(int)forbidden_connections.get_count()-3); while( remove < forbidden_connections.get_count() ) { forbidden_connections.remove_first(); } } void ai_goods_t::rotate90( const sint16 y_size ) { player_t::rotate90( y_size ); // rotate places platz1.rotate90( y_size ); platz2.rotate90( y_size ); size1.rotate90( 0 ); size2.rotate90( 0 ); harbour.rotate90( y_size ); } /** * Activates/deactivates a player */ bool ai_goods_t::set_active(bool new_state) { // something to change? if(active!=new_state) { if(!new_state) { // deactivate AI active = false; state = NR_INIT; start = ziel = NULL; } else { // activate AI active = true; } } return active; } /* recursive lookup of a factory tree: * sets start and destination to the next needed supplier * start always with the first branch, if there are more goods */ bool ai_goods_t::get_factory_tree_lowest_missing( fabrik_t *fab ) { // now check for all products (should be changed later for the root) for( int i=0; iget_desc()->get_supplier_count(); i++ ) { const goods_desc_t *ware = fab->get_desc()->get_supplier(i)->get_input_type(); // find out how much is there const array_tpl& input = fab->get_input(); uint ware_nr; for( ware_nr=0; ware_nr input[ware_nr].max/4 ) { // already enough supplied to continue; } for(koord const& q : fab->get_suppliers()) { fabrik_t* const qfab = fabrik_t::get_fab(q); const factory_desc_t* const fd = qfab->get_desc(); for( uint qq = 0; qq < fd->get_product_count(); qq++ ) { if( fd->get_product(qq)->get_output_type() == ware && !is_forbidden(qfab, fab, ware) && !is_connected(q, fab->get_pos().get_2d(), ware) ) { // find out how much is there const array_tpl& output = qfab->get_output(); uint ware_nr; for(ware_nr=0; ware_nrget_suppliers().get_count()>0 && get_factory_tree_lowest_missing( qfab )) { return true; } } start = qfab; ziel = fab; freight = ware; return true; } } } // completely supplied??? } return false; } /* recursive lookup of a tree and how many factories must be at least connected * returns -1, if this tree is can't be completed */ int ai_goods_t::get_factory_tree_missing_count( fabrik_t *fab ) { int numbers=0; // how many missing? factory_desc_t const& d = *fab->get_desc(); // ok, this is a source ... if (d.is_producer_only()) { return 0; } // now check for all for (int i = 0; i < d.get_supplier_count(); ++i) { goods_desc_t const* const ware = d.get_supplier(i)->get_input_type(); bool complete = false; // found at least one factory for(koord const& q : fab->get_suppliers()) { fabrik_t* const qfab = fabrik_t::get_fab(q); if(!qfab) { dbg->error("fabrik_t::get_fab()","fab %s at %s does not find supplier at %s.", fab->get_name(), fab->get_pos().get_str(), q.get_str()); continue; } if( !is_forbidden( qfab, fab, ware ) ) { const factory_desc_t* const fd = qfab->get_desc(); for (uint qq = 0; qq < fd->get_product_count(); qq++) { if (fd->get_product(qq)->get_output_type() == ware ) { int n = get_factory_tree_missing_count( qfab ); if(n>=0) { complete = true; if (!is_connected(q, fab->get_pos().get_2d(), ware)) { numbers += 1; } numbers += n; } } } } } if(!complete) { return -1; } } return numbers; } void add_neighbourhood( vector_tpl &list, const uint16 size) { uint32 old_size = list.get_count(); koord test; for( uint32 i = 0; i < old_size; i++ ) { for( test.x = -size; test.x < size+1; test.x++ ) { for( test.y = -size; test.y < size+1; test.y++ ) { list.append_unique( list[i] + test ); } } } } bool ai_goods_t::suche_platz1_platz2(fabrik_t *qfab, fabrik_t *zfab, int length ) { clean_marker(platz1,size1); clean_marker(platz2,size2); koord start( qfab->get_pos().get_2d() ); koord start_size( length, 0 ); koord ziel( zfab->get_pos().get_2d() ); koord ziel_size( length, 0 ); bool ok = false; bool has_ziel = false; if(qfab->get_desc()->get_placement()!=factory_desc_t::Water) { if( length == 0 ) { vector_tpl tile_list[2]; uint16 const cov = welt->get_settings().get_station_coverage(); koord test; for( uint8 i = 0; i < 2; i++ ) { fabrik_t *fab = i==0 ? qfab : zfab; vector_tpl fab_tiles; fab->get_tile_list( fab_tiles ); add_neighbourhood( fab_tiles, cov ); vector_tpl one_more( fab_tiles ); add_neighbourhood( one_more, 1 ); // Any halts here? vector_tpl halts; for(koord const& j : one_more) { halthandle_t const halt = get_halt(j); if( halt.is_bound() && !halts.is_contained(halt->get_basis_pos()) ) { bool halt_connected = halt->get_fab_list().is_contained( fab ); for(haltestelle_t::tile_t const& i : halt->get_tiles() ) { koord const pos = i.grund->get_pos().get_2d(); if( halt_connected || fab_tiles.is_contained(pos) ) { halts.append_unique( pos ); } } } } add_neighbourhood( halts, 1 ); vector_tpl *next = &halts; for( uint8 k = 0; k < 2; k++ ) { // On which tiles we can start? for(koord const& j : *next) { grund_t const* const gr = welt->lookup_kartenboden(j); if( gr && gr->get_grund_hang() == slope_t::flat && !gr->hat_wege() && !gr->get_leitung() ) { tile_list[i].append_unique( gr->get_pos() ); } } if( !tile_list[i].empty() ) { // Skip, if found tiles beneath halts. break; } next = &fab_tiles; } } // Test which tiles are the best: way_builder_t bauigel(this); bauigel.init_builder( way_builder_t::strasse, road_weg, tunnel_builder_t::get_tunnel_desc(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()) ); // we won't destroy cities (and save the money) bauigel.set_keep_existing_faster_ways(true); bauigel.set_keep_city_roads(true); bauigel.set_maximum(10000); bauigel.calc_route(tile_list[0], tile_list[1]); if( bauigel.get_count() > 2 ) { // Sometimes reverse route is the best, so we have to change the koords. koord3d_vector_t const& r = bauigel.get_route(); start = r.front().get_2d(); ziel = r.back().get_2d(); if( !tile_list[0].is_contained(r.front()) ) { sim::swap(start, ziel); } ok = true; has_ziel = true; } } if( !ok ) { ok = suche_platz(start, start_size, ziel, qfab->get_desc()->get_building()->get_size(qfab->get_rotate()) ); } } else { // water factory => find harbour location ok = find_harbour(start, start_size, ziel ); } if( ok && !has_ziel ) { // found a place, search for target ok = suche_platz(ziel, ziel_size, start, zfab->get_desc()->get_building()->get_size(zfab->get_rotate()) ); } INT_CHECK("simplay 1729"); if( !ok ) { // no suitable locations found DBG_MESSAGE( "ai_t::suche_platz1_platz2()", "no suitable locations found" ); } else { // save places platz1 = start; size1 = start_size; platz2 = ziel; size2 = ziel_size; DBG_MESSAGE( "ai_t::suche_platz1_platz2()", "platz1=%d,%d platz2=%d,%d", platz1.x, platz1.y, platz2.x, platz2.y ); // reserve space with marker set_marker( platz1, size1 ); set_marker( platz2, size2 ); } return ok; } /** * build docks and ships */ bool ai_goods_t::create_ship_transport_vehicle(fabrik_t *qfab, int vehicle_count) { // pak64 has barges ... const vehicle_desc_t *v_second = NULL; if(ship_vehicle->get_power()==0) { v_second = ship_vehicle; if(v_second->get_leader_count()==0 || v_second->get_leader(0)==NULL) { // pushed barge? if(ship_vehicle->get_trailer_count()>0 && ship_vehicle->get_trailer(0)!=NULL) { v_second = ship_vehicle->get_trailer(0); } else { return false; } } else { ship_vehicle = v_second->get_leader(0); } } DBG_MESSAGE( "ai_goods_t::create_ship_transport_vehicle()", "for %i ships", vehicle_count ); if( convoihandle_t::is_exhausted() ) { // too many convois => cannot do anything about this ... return false; } // must remove marker grund_t* gr = welt->lookup_kartenboden(platz1); if (gr) { gr->obj_loesche_alle(this); } // try to built dock const building_desc_t* h = hausbauer_t::get_random_station(building_desc_t::dock, water_wt, welt->get_timeline_year_month(), haltestelle_t::WARE); if(h==NULL || !call_general_tool(TOOL_BUILD_STATION, platz1, h->get_name())) { return false; } // sea pos (and not on harbour ... ) halthandle_t halt = haltestelle_t::get_halt(gr->get_pos(),this); koord pos1 = platz1 - koord(gr->get_grund_hang())*h->get_size().y; koord best_pos = pos1; uint16 const cov = welt->get_settings().get_station_coverage(); for( int y = pos1.y - cov; y <= pos1.y + cov; ++y ) { for( int x = pos1.x - cov; x <= pos1.x + cov; ++x ) { koord p(x,y); grund_t *gr = welt->lookup_kartenboden(p); // check for water tile, do not start in depots if( gr->is_water() && halt == get_halt(p) && gr->get_depot()==NULL ) { if( koord_distance(best_pos,platz2)append( welt->lookup_kartenboden(best_pos), 0 ); schedule->append( welt->lookup(qfab->get_pos()), 100 ); schedule->set_current_stop( 1 ); schedule->finish_editing(); linehandle_t line=simlinemgmt.create_line(simline_t::shipline,this,schedule); delete schedule; // now create all vehicles as convois for(int i=0; i cannot do anything about this ... return i>0; } vehicle_t* v = vehicle_builder_t::build( qfab->get_pos(), this, NULL, ship_vehicle); convoi_t* cnv = new convoi_t(this); // give the new convoi name from first vehicle cnv->set_name(v->get_desc()->get_name()); cnv->add_vehicle( v ); // two part consist if(v_second!=NULL) { v = vehicle_builder_t::build( qfab->get_pos(), this, NULL, v_second ); cnv->add_vehicle( v ); } cnv->set_line(line); cnv->start(); } clean_marker(platz1,size1); clean_marker(platz2,size2); platz1 += koord(welt->lookup_kartenboden(platz1)->get_grund_hang()); return true; } void ai_goods_t::create_road_transport_vehikel(fabrik_t *qfab, int vehicle_count) { const building_desc_t* fh = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::WARE); // succeed in frachthof creation if(fh && call_general_tool(TOOL_BUILD_STATION, platz1, fh->get_name()) && call_general_tool(TOOL_BUILD_STATION, platz2, fh->get_name()) ) { koord3d pos1 = welt->lookup_kartenboden(platz1)->get_pos(); koord3d pos2 = welt->lookup_kartenboden(platz2)->get_pos(); int start_location=0; // sometimes, when factories are very close, we need exact calculation const koord3d& qpos = qfab->get_pos(); if ((qpos.x - platz1.x) * (qpos.x - platz1.x) + (qpos.y - platz1.y) * (qpos.y - platz1.y) > (qpos.x - platz2.x) * (qpos.x - platz2.x) + (qpos.y - platz2.y) * (qpos.y - platz2.y)) { start_location = 1; } // calculate vehicle start position koord3d startpos=(start_location==0)?pos1:pos2; ribi_t::ribi w_ribi = welt->lookup(startpos)->get_weg_ribi_unmasked(road_wt); // now start all vehicle one field before, so they load immediately startpos = welt->lookup_kartenboden(koord(startpos.get_2d())+koord(w_ribi))->get_pos(); // since 86.01 we use lines for road vehicles ... schedule_t *schedule=new truck_schedule_t(); schedule->append(welt->lookup(pos1), start_location == 0 ? 100 : 0); schedule->append(welt->lookup(pos2), start_location == 1 ? 100 : 0); schedule->set_current_stop( start_location ); schedule->finish_editing(); linehandle_t line=simlinemgmt.create_line(simline_t::truckline,this,schedule); delete schedule; // now create all vehicles as convois for(int i=0; i cannot do anything about this ... return; } vehicle_t* v = vehicle_builder_t::build(startpos, this, NULL, road_vehicle); convoi_t* cnv = new convoi_t(this); // give the new convoi name from first vehicle cnv->set_name(v->get_desc()->get_name()); cnv->add_vehicle( v ); cnv->set_line(line); cnv->start(); } } } void ai_goods_t::create_rail_transport_vehikel(const koord platz1, const koord platz2, int vehicle_count, int minimum_loading) { // now obeys timeline and use "more clever" scheme for vehicle selection schedule_t *schedule; if( convoihandle_t::is_exhausted() ) { // too many convois => cannot do anything about this ... return; } convoi_t* cnv = new convoi_t(this); koord3d pos1= welt->lookup_kartenboden(platz1)->get_pos(); koord3d pos2 = welt->lookup_kartenboden(platz2)->get_pos(); // probably need to electrify the track? if( rail_engine->get_engine_type()==vehicle_desc_t::electric ) { // we need overhead wires const way_obj_desc_t *e = wayobj_t::get_overhead_line(track_wt, welt->get_timeline_year_month()); tool_build_wayobj_t tool; tool.set_default_param(e->get_name()); tool.init( this ); tool.work( this, welt->lookup_kartenboden(platz1)->get_pos() ); tool.work( this, welt->lookup_kartenboden(platz2)->get_pos() ); tool.exit( this ); } koord3d start_pos = welt->lookup_kartenboden(pos1.get_2d() + (abs(size1.x)>abs(size1.y) ? koord(size1.x,0) : koord(0,size1.y)))->get_pos(); vehicle_t* v = vehicle_builder_t::build( start_pos, this, NULL, rail_engine); // give the new convoi name from first vehicle cnv->set_name(rail_engine->get_name()); cnv->add_vehicle( v ); DBG_MESSAGE( "ai_goods_t::create_rail_transport_vehikel","for %i cars",vehicle_count); /* now we add cars: * check here also for introduction years */ for(int i = 0; i < vehicle_count; i++) { // use the vehicle we searched before vehicle_t* v = vehicle_builder_t::build(start_pos, this, NULL, rail_vehicle); cnv->add_vehicle( v ); } schedule = cnv->front()->generate_new_schedule(); schedule->set_current_stop( 0 ); schedule->append(welt->lookup(pos1), minimum_loading); schedule->append(welt->lookup(pos2), 0); schedule->finish_editing(); cnv->set_schedule(schedule); cnv->start(); } /** * build a station * Can fail even though check has been done before */ int ai_goods_t::baue_bahnhof(const koord* p, int vehicle_count) { int laenge = max(((rail_vehicle->get_length()*vehicle_count)+rail_engine->get_length()+CARUNITS_PER_TILE-1)/CARUNITS_PER_TILE,1); int baulaenge = 0; ribi_t::ribi ribi = welt->lookup_kartenboden(*p)->get_weg_ribi(track_wt); koord zv ( ribi ); koord t = *p; bool ok = true; for( int i=0; ilookup_kartenboden(t); ok &= (gr != NULL) && !gr->has_two_ways() && gr->get_weg_hang()==slope_t::flat; if(!ok) { break; } baulaenge ++; t += zv; } // too short if(baulaenge<=1) { return 0; } // to avoid broken stations, they will be always built next to an existing bool make_all_bahnhof=false; // find a freight train station const building_desc_t* desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, track_wt, welt->get_timeline_year_month(), haltestelle_t::WARE); if(desc==NULL) { // no freight station return 0; } koord pos; for( pos=t-zv; pos!=*p; pos-=zv ) { if( make_all_bahnhof || get_halt(pos+koord(-1,-1)).is_bound() || get_halt(pos+koord(-1, 1)).is_bound() || get_halt(pos+koord( 1,-1)).is_bound() || get_halt(pos+koord( 1, 1)).is_bound() ) { // start building, if next to an existing station make_all_bahnhof = true; call_general_tool( TOOL_BUILD_STATION, pos, desc->get_name() ); } INT_CHECK("simplay 753"); } // now add the other squares (going backwards) for( pos=*p; pos!=t; pos+=zv ) { if( !get_halt(pos).is_bound() ) { call_general_tool( TOOL_BUILD_STATION, pos, desc->get_name() ); } } laenge = min( vehicle_count, (baulaenge*CARUNITS_PER_TILE - rail_engine->get_length())/rail_vehicle->get_length() ); //DBG_MESSAGE("ai_goods_t::baue_bahnhof","Final station at (%i,%i) with %i tiles for %i cars",p->x,p->y,baulaenge,laenge); return laenge; } /** * built a very simple track with just the minimum effort * usually good enough, since it can use road crossings */ bool ai_goods_t::create_simple_rail_transport() { clean_marker(platz1,size1); clean_marker(platz2,size2); way_builder_t bauigel(this); bauigel.init_builder( way_builder_t::schiene|way_builder_t::bot_flag, rail_weg, tunnel_builder_t::get_tunnel_desc(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()) ); bauigel.set_keep_existing_ways(false); // for stations way_builder_t bauigel1(this); bauigel1.init_builder( way_builder_t::schiene|way_builder_t::bot_flag, rail_weg, tunnel_builder_t::get_tunnel_desc(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()) ); bauigel1.set_keep_existing_ways(false); way_builder_t bauigel2(this); bauigel2.init_builder( way_builder_t::schiene|way_builder_t::bot_flag, rail_weg, tunnel_builder_t::get_tunnel_desc(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()) ); bauigel2.set_keep_existing_ways(false); // first: make plain stations tiles as intended sint8 z1 = max( welt->get_water_hgt(platz1) + 1, welt->lookup_kartenboden(platz1)->get_hoehe() ); koord k = platz1; koord diff1( sgn(size1.x), sgn(size1.y) ); koord perpend( sgn(size1.y), sgn(size1.x) ); while(k!=size1+platz1) { climate c = welt->get_climate(k); if(!welt->flatten_tile( this, k, z1 )) { return false; } // ensure is land grund_t* bd = welt->lookup_kartenboden(k); if (bd->get_typ() == grund_t::wasser) { welt->set_water_hgt_nocheck(k, bd->get_hoehe()-1); welt->access(k)->correct_water(); welt->set_climate(k, c, true); } k += diff1; } // make the second ones flat ... sint8 z2 = max( welt->get_water_hgt(platz2) + 1, welt->lookup_kartenboden(platz2)->get_hoehe() ); k = platz2; perpend = koord( sgn(size2.y), sgn(size2.x) ); koord diff2( sgn(size2.x), sgn(size2.y) ); while(k!=size2+platz2) { climate c = welt->get_climate(k); if(!welt->flatten_tile(this,k,z2)) { return false; } // ensure is land grund_t* bd = welt->lookup_kartenboden(k); if (bd->get_typ() == grund_t::wasser) { welt->set_water_hgt_nocheck(k, bd->get_hoehe()-1); welt->access(k)->correct_water(); welt->set_climate(k, c, true); } k += diff2; } bauigel1.calc_route( koord3d(platz1,z1), koord3d(platz1+size1-diff1, z1)); bauigel2.calc_route( koord3d(platz2,z2), koord3d(platz2+size2-diff2, z2)); // build immediately, otherwise way-builder could get confused and connect way to a tile in the middle of the station bauigel1.build(); bauigel2.build(); vector_tpl starttiles, endtiles; // now calc the route starttiles.append(welt->lookup_kartenboden(platz1 + size1)->get_pos()); starttiles.append(welt->lookup_kartenboden(platz1 - diff1)->get_pos()); endtiles.append(welt->lookup_kartenboden(platz2 + size2)->get_pos()); endtiles.append(welt->lookup_kartenboden(platz2 - diff2)->get_pos()); bauigel.calc_route( starttiles, endtiles ); INT_CHECK("ai_goods 672"); // build only if enough cash available bool build_no_tf = (bauigel.get_count() > 4) && (bauigel.calc_costs() <= finance->get_netwealth()); // now try route with terraforming way_builder_t baumaulwurf(this); baumaulwurf.init_builder( way_builder_t::schiene|way_builder_t::bot_flag|way_builder_t::terraform_flag, rail_weg, tunnel_builder_t::get_tunnel_desc(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()) ); baumaulwurf.set_keep_existing_ways(false); baumaulwurf.calc_route( starttiles, endtiles ); // build with terraforming if shorter and enough money is available bool with_tf = (baumaulwurf.get_count() > 4) && (10*baumaulwurf.get_count() < 9*bauigel.get_count() || bauigel.get_count() <= 4); // too expensive ? with_tf = with_tf && (baumaulwurf.calc_costs() <= finance->get_netwealth()); // now build with or without terraforming if (with_tf) { baumaulwurf.build(); } else if (build_no_tf) { bauigel.build(); } // connect track to station if( with_tf || build_no_tf ) { DBG_MESSAGE("ai_goods_t::create_simple_rail_transport()","building simple track from %d,%d to %d,%d",platz1.x, platz1.y, platz2.x, platz2.y); // connect to track koord3d_vector_t const& r = with_tf ? baumaulwurf.get_route() : bauigel.get_route(); koord3d tile1 = r.front(); koord3d tile2 = r.back(); if (!starttiles.is_contained(tile1)) sim::swap(tile1, tile2); // No botflag, since we want to connect with the station. bauigel.init_builder( way_builder_t::schiene, rail_weg, tunnel_builder_t::get_tunnel_desc(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(track_wt,rail_engine->get_topspeed(),welt->get_timeline_year_month()) ); bauigel.calc_straight_route( koord3d(platz1,z1), tile1); bauigel.build(); bauigel.calc_straight_route( koord3d(platz2,z2), tile2); bauigel.build(); // If connection is built not at platz1/2, we must alter platz1/2, otherwise baue_bahnhof gets confused. if( tile1.get_2d() != platz1 + size1 ) { platz1 = platz1 + size1 - diff1; size1 = -size1; } if( tile2.get_2d() != platz2 + size2 ) { platz2 = platz2 + size2 - diff2; } return true; } else { // not ok: remove station ... k = platz1; while(k!=size1+platz1) { int cost = -welt->lookup_kartenboden(k)->weg_entfernen( track_wt, true ); book_construction_costs(this, cost, k, track_wt); k += diff1; } k = platz2; while(k!=size2+platz2) { int cost = -welt->lookup_kartenboden(k)->weg_entfernen( track_wt, true ); book_construction_costs(this, cost, k, track_wt); k += diff2; } } return false; } // the normal length procedure for freight AI void ai_goods_t::step() { // needed for schedule of stops ... player_t::step(); if(!active) { // I am off ... return; } // one route per month ... if( welt->get_steps() < next_construction_steps ) { return; } if( finance->get_netwealth() < (finance->get_starting_money()/8) ) { // nothing to do but to remove unneeded convois to gain some money state = CHECK_CONVOI; } switch(state) { case NR_INIT: state = NR_SAMMLE_ROUTEN; count = 0; built_update_headquarter(); if(root==NULL) { // find a tree root to complete weighted_vector_tpl start_fabs(20); for(fabrik_t* const fab : welt->get_fab_list() ) { // consumer and not completely overcrowded if( fab->get_desc()->is_consumer_only() && fab->get_status() != fabrik_t::STATUS_BAD ) { int missing = get_factory_tree_missing_count( fab ); if( missing>0 ) { start_fabs.append( fab, 100/(missing+1)+1 ); } } } if( !start_fabs.empty() ) { root = pick_any_weighted(start_fabs); } } // still nothing => we have to check convois ... if(root==NULL) { state = CHECK_CONVOI; } break; /* try to built a network: * last target also new target ... */ case NR_SAMMLE_ROUTEN: if( get_factory_tree_lowest_missing(root) ) { if( start->get_desc()->get_placement()!=factory_desc_t::Water || vehicle_search( water_wt, 0, 10, freight, false)!=NULL ) { DBG_MESSAGE("ai_goods_t::do_ki", "Consider route from %s (%i,%i) to %s (%i,%i)", start->get_name(), start->get_pos().x, start->get_pos().y, ziel->get_name(), ziel->get_pos().x, ziel->get_pos().y ); state = NR_BAUE_ROUTE1; } else { // add to impossible connections forbidden_connections.append( new fabconnection_t( start, ziel, freight ) ); state = CHECK_CONVOI; } } else { // did all I could do here ... root = NULL; state = CHECK_CONVOI; } break; // now we need so select the cheapest mean to get maximum profit case NR_BAUE_ROUTE1: { /* if we reached here, we decide to built a route; * the KI just chooses the way to run the operation at maximum profit (minimum loss). * The KI will built also a loosing route; this might be required by future versions to * be able to built a network! */ /* for the calculation we need: * a suitable car (and engine) * a suitable way */ uint32 dist = koord_distance( start->get_pos(), ziel->get_pos() ); // guess the "optimum" speed (usually a little too low) sint32 best_rail_speed = 80;// is ok enough for goods, was: min(60+freight->get_speed_bonus()*5, 140 ); sint32 best_road_speed = min(60+freight->get_speed_bonus()*5, 130 ); INT_CHECK("simplay 1265"); // is rail transport allowed? if(rail_transport) { // any rail car that transport this good (actually this weg_t the largest) rail_vehicle = vehicle_search( track_wt, 0, best_rail_speed, freight, true); } rail_engine = NULL; rail_weg = NULL; DBG_MESSAGE("do_ki()","rail vehicle %p",rail_vehicle); // is road transport allowed? if(road_transport) { // any road car that transport this good (actually this returns the largest) road_vehicle = vehicle_search( road_wt, 10, best_road_speed, freight, false); } road_weg = NULL; DBG_MESSAGE("do_ki()","road vehicle %p",road_vehicle); ship_vehicle = NULL; if(start->get_desc()->get_placement()==factory_desc_t::Water) { // largest ship available ship_vehicle = vehicle_search( water_wt, 0, 20, freight, false); } INT_CHECK("simplay 1265"); // properly calculate production const array_tpl& output = start->get_output(); uint start_ware=0; while( start_wareget_base_production(), ( start->get_base_production() * start->get_desc()->get_product(start_ware)->get_factor() )/256u - (uint32)(start->get_output()[start_ware].get_stat(1, FAB_GOODS_DELIVERED)) ); DBG_MESSAGE("do_ki()","check railway"); /* calculate number of cars for railroad */ count_rail=255; // no cars yet if( rail_vehicle!=NULL ) { // if our car is faster: well use slower speed to save money best_rail_speed = min(51, rail_vehicle->get_topspeed()); // for engine: guess number of cars count_rail = (prod*dist) / (rail_vehicle->get_capacity()*best_rail_speed)+1; // assume the engine weight 100 tons for power needed calculation int total_weight = count_rail*( rail_vehicle->get_capacity()*freight->get_weight_per_unit() + rail_vehicle->get_weight() ); // long power_needed = (long)(((best_rail_speed*best_rail_speed)/2500.0+1.0)*(100.0+count_rail*(rail_vehicle->get_weight()+rail_vehicle->get_capacity()*freight->get_weight_per_unit()*0.001))); rail_engine = vehicle_search( track_wt, total_weight/1000, best_rail_speed, NULL, wayobj_t::default_oberleitung!=NULL); if( rail_engine!=NULL ) { best_rail_speed = min(rail_engine->get_topspeed(),rail_vehicle->get_topspeed()); // find cheapest track with that speed (and no monorail/elevated/tram tracks, please) rail_weg = way_builder_t::weg_search( track_wt, best_rail_speed, welt->get_timeline_year_month(),type_flat ); if( rail_weg!=NULL ) { if( best_rail_speed>rail_weg->get_topspeed() ) { best_rail_speed = rail_weg->get_topspeed(); } // no train can have more than 15 cars count_rail = min( 22, (3*prod*dist) / (rail_vehicle->get_capacity()*best_rail_speed*2) ); // if engine too week, reduce number of cars if( count_rail*80*64>(int)(rail_engine->get_power()*rail_engine->get_gear()) ) { count_rail = rail_engine->get_power()*rail_engine->get_gear()/(80*64); } count_rail = ((count_rail+1)&0x0FE)+1; DBG_MESSAGE("ai_goods_t::do_ki()","Engine %s guess to need %d rail cars %s for route (%s)", rail_engine->get_name(), count_rail, rail_vehicle->get_name(), rail_weg->get_name() ); } } if( rail_engine==NULL || rail_weg==NULL ) { // no rail transport possible DBG_MESSAGE("ai_goods_t::do_ki()","No railway possible."); rail_vehicle = NULL; count_rail = 255; } } INT_CHECK("simplay 1265"); DBG_MESSAGE("do_ki()","check railway"); /* calculate number of cars for road; much easier */ count_road=255; // no cars yet if( road_vehicle!=NULL ) { best_road_speed = road_vehicle->get_topspeed(); // find cheapest road road_weg = way_builder_t::weg_search( road_wt, best_road_speed, welt->get_timeline_year_month(),type_flat ); if( road_weg!=NULL ) { if( best_road_speed>road_weg->get_topspeed() ) { best_road_speed = road_weg->get_topspeed(); } // minimum vehicle is 1, maximum vehicle is 48, more just result in congestion count_road = min( 254, (prod*dist) / (road_vehicle->get_capacity()*best_road_speed*2)+2 ); DBG_MESSAGE("ai_goods_t::do_ki()","guess to need %d road cars %s for route %s", count_road, road_vehicle->get_name(), road_weg->get_name() ); } else { // no roads there !?! DBG_MESSAGE("ai_goods_t::do_ki()","No roadway possible."); } } // find the cheapest transport ... // assume maximum cost int cost_rail=0x7FFFFFFF, cost_road=0x7FFFFFFF; int income_rail=0, income_road=0; // calculate cost for rail if( count_rail<255 ) { int freight_price = (freight->get_value()*rail_vehicle->get_capacity()*count_rail)/24*((8000+(best_rail_speed-80)*freight->get_speed_bonus())/1000); // calculated here, since the above number was based on production // only uneven number of cars bigger than 3 makes sense ... count_rail = max( 3, count_rail ); income_rail = (freight_price*best_rail_speed)/(2*dist+count_rail); cost_rail = rail_weg->get_maintenance() + (((count_rail+1)/2)*300)/dist + ((count_rail*rail_vehicle->get_running_cost()+rail_engine->get_running_cost())*best_rail_speed)/(2*dist+count_rail); DBG_MESSAGE("ai_goods_t::do_ki()","Netto credits per day for rail transport %.2f (income %.2f)",cost_rail/100.0, income_rail/100.0 ); cost_rail -= income_rail; } // and calculate cost for road if( count_road<255 ) { // for short distance: reduce number of cars // calculated here, since the above number was based on production count_road = clamp( (sint32)(dist*15)/best_road_speed, 2, count_road ); int freight_price = (freight->get_value()*road_vehicle->get_capacity()*count_road)/24*((8000+(best_road_speed-80)*freight->get_speed_bonus())/1000); cost_road = road_weg->get_maintenance() + 300/dist + (count_road*road_vehicle->get_running_cost()*best_road_speed)/(2*dist+5); income_road = (freight_price*best_road_speed)/(2*dist+5); DBG_MESSAGE("ai_goods_t::do_ki()","Netto credits per day and km for road transport %.2f (income %.2f)",cost_road/100.0, income_road/100.0 ); cost_road -= income_road; } // check location, if vehicles found if( min(count_road,count_rail)!=255 ) { // road or rail? int length = 1; if( cost_railget_length() + count_rail*rail_vehicle->get_length()+CARUNITS_PER_TILE-1)/CARUNITS_PER_TILE; if(suche_platz1_platz2(start, ziel, length)) { state = ship_vehicle ? NR_BAUE_WATER_ROUTE : NR_BAUE_SIMPLE_SCHIENEN_ROUTE; next_construction_steps += 10; } } // if state is still NR_BAUE_ROUTE1 then there are no suitable places if(state==NR_BAUE_ROUTE1 && (count_road != 255) && suche_platz1_platz2(start, ziel, 0)) { // rail was too expensive or not successful count_rail = 255; state = ship_vehicle ? NR_BAUE_WATER_ROUTE : NR_BAUE_STRASSEN_ROUTE; next_construction_steps += 10; } } // no success at all? if(state==NR_BAUE_ROUTE1) { // maybe this route is not builtable ... add to forbidden connections forbidden_connections.append( new fabconnection_t( start, ziel, freight ) ); ziel = NULL; // otherwise it may always try to built the same route! state = CHECK_CONVOI; } } break; // built a simple ship route case NR_BAUE_WATER_ROUTE: if(is_connected(start->get_pos().get_2d(), ziel->get_pos().get_2d(), freight)) { state = CHECK_CONVOI; } else { // properly calculate production const array_tpl& output = start->get_output(); uint start_ware=0; while( start_wareget_base_production(), (sint32)(start->get_base_production() * start->get_desc()->get_product(start_ware)->get_factor()) - (sint32)(start->get_output()[start_ware].get_stat(1, FAB_GOODS_DELIVERED)) ); if(prod<0) { // too much supplied last time?!? => retry state = CHECK_CONVOI; break; } // just remember the position, where the harbour will be built harbour=platz1; int ships_needed = 1 + (prod*koord_distance(harbour,start->get_pos().get_2d())) / (ship_vehicle->get_capacity()*max(20,ship_vehicle->get_topspeed())); if(create_ship_transport_vehicle(start,ships_needed)) { bool already_connected = false; const planquadrat_t* pl = welt->access(harbour); for( uint8 i=0; iget_boden_count(); i++ ) { if( pl->get_boden_bei(i)->get_halt()->get_fab_list().is_contained(ziel) ) { already_connected = true; } } if( already_connected ) { // so close, so we are already connected grund_t *gr = welt->lookup_kartenboden(platz2); if (gr) gr->obj_loesche_alle(this); state = (rail_vehicle && count_rail<255) ? NR_RAIL_SUCCESS : NR_ROAD_SUCCESS; } else { // else we need to built the second part of the route state = (rail_vehicle && count_rail<255) ? NR_BAUE_SIMPLE_SCHIENEN_ROUTE : NR_BAUE_STRASSEN_ROUTE; } } else { ship_vehicle = NULL; state = NR_BAUE_CLEAN_UP; } } break; // built a simple railroad case NR_BAUE_SIMPLE_SCHIENEN_ROUTE: if(is_connected(start->get_pos().get_2d(), ziel->get_pos().get_2d(), freight)) { state = ship_vehicle ? NR_BAUE_CLEAN_UP : CHECK_CONVOI; } else if(create_simple_rail_transport()) { sint16 org_count_rail = count_rail; count_rail = baue_bahnhof(&platz1, count_rail); if(count_rail>=3) { count_rail = baue_bahnhof(&platz2, count_rail); } if(count_rail>=3) { if(count_railget_topspeed()); // for engine: guess number of cars sint32 power_needed=(sint32)(((best_rail_speed*best_rail_speed)/2500.0+1.0)*(100.0+count_rail*( (rail_vehicle->get_weight()+rail_vehicle->get_capacity()*freight->get_weight_per_unit())*0.001 ))); const vehicle_desc_t *v=vehicle_search( track_wt, power_needed, best_rail_speed, NULL, false); if(v->get_running_cost()get_running_cost()) { rail_engine = v; } } create_rail_transport_vehikel( platz1, platz2, count_rail, 100 ); state = NR_RAIL_SUCCESS; } else { DBG_MESSAGE("ai_goods_t::step()","remove already constructed rail between %i,%i and %i,%i and try road",platz1.x,platz1.y,platz2.x,platz2.y); // no success: clean route char param[16]; sprintf( param, "%i", track_wt ); tool_wayremover_t tool; tool.set_default_param(param); tool.init( this ); tool.work( this, welt->lookup_kartenboden(platz1)->get_pos() ); tool.work( this, welt->lookup_kartenboden(platz2)->get_pos() ); tool.exit( this ); if( (count_road != 255) && suche_platz1_platz2(start, ziel, 0) ) { state = NR_BAUE_STRASSEN_ROUTE; } else { state = NR_BAUE_CLEAN_UP; } } } else { if( (count_road != 255) && suche_platz1_platz2(start, ziel, 0) ) { state = NR_BAUE_STRASSEN_ROUTE; } else { state = NR_BAUE_CLEAN_UP; } } break; // built a simple road (no bridges, no tunnels) case NR_BAUE_STRASSEN_ROUTE: if(is_connected(start->get_pos().get_2d(), ziel->get_pos().get_2d(), freight)) { state = ship_vehicle ? NR_BAUE_CLEAN_UP : CHECK_CONVOI; } else if(create_simple_road_transport(platz1,size1,platz2,size2,road_weg)) { create_road_transport_vehikel(start, count_road ); state = NR_ROAD_SUCCESS; } else { state = NR_BAUE_CLEAN_UP; } break; // remove marker etc. case NR_BAUE_CLEAN_UP: { if(start!=NULL && ziel!=NULL) { forbidden_connections.append( new fabconnection_t( start, ziel, freight ) ); } if(ship_vehicle) { // only here, if we could built ships but no connection halthandle_t start_halt = get_halt(harbour); if( start_halt.is_bound() && (start_halt->get_station_type()&haltestelle_t::dock)!=0 ) { // delete all ships on this line vector_tpl lines; simlinemgmt.get_lines( simline_t::shipline, &lines ); if(!lines.empty()) { linehandle_t line = lines.back(); schedule_t *schedule=line->get_schedule(); if(schedule->get_count()>1 && haltestelle_t::get_halt(schedule->entries[0].pos,this)==start_halt) { while(line->count_convoys()>0) { convoihandle_t cnv = line->get_convoy(0); cnv->self_destruct(); if(cnv.is_bound()) { cnv->step(); } } simlinemgmt.delete_line( line ); } } } // delete harbour call_general_tool( TOOL_REMOVER, harbour, NULL ); } harbour = koord::invalid; // otherwise it may always try to built the same route! ziel = NULL; // schilder aufraeumen clean_marker(platz1,size1); clean_marker(platz2,size2); state = CHECK_CONVOI; break; } // successful rail construction case NR_RAIL_SUCCESS: { // tell the player cbuffer_t buf; const koord3d& spos = start->get_pos(); const koord3d& zpos = ziel->get_pos(); buf.printf( translator::translate("%s\nopened a new railway\nbetween %s\nat (%i,%i) and\n%s at (%i,%i)."), get_name(), translator::translate(start->get_name()), spos.x, spos.y, translator::translate(ziel->get_name()), zpos.x, zpos.y); welt->get_message()->add_message(buf, spos, message_t::ai, PLAYER_FLAG|player_nr, rail_engine->get_base_image()); harbour = koord::invalid; state = CHECK_CONVOI; } break; // successful rail construction case NR_ROAD_SUCCESS: { // tell the player cbuffer_t buf; const koord3d& spos = start->get_pos(); const koord3d& zpos = ziel->get_pos(); buf.printf( translator::translate("%s\nnow operates\n%i trucks between\n%s at (%i,%i)\nand %s at (%i,%i)."), get_name(), count_road, translator::translate(start->get_name()), spos.x, spos.y, translator::translate(ziel->get_name()), zpos.x, zpos.y); welt->get_message()->add_message(buf, spos, message_t::ai, PLAYER_FLAG|player_nr, road_vehicle->get_base_image()); harbour = koord::invalid; state = CHECK_CONVOI; } break; // remove stuck vehicles (only from roads!) case CHECK_CONVOI: { next_construction_steps = welt->get_steps() + ((1+finance->get_account_overdrawn())>0)*simrand( ai_t::construction_speed ) + 25; for (size_t i = welt->convoys().get_count(); i-- != 0;) { convoihandle_t const cnv = welt->convoys()[i]; if(!cnv.is_bound() || cnv->get_owner()!=this) { continue; } if (cnv->front()->get_waytype() == water_wt) { // ships will be only deleted together with the connecting convoi continue; } sint64 gewinn = 0; for( int j=0; j<12; j++ ) { gewinn += cnv->get_finance_history( j, convoi_t::CONVOI_PROFIT ); } // apparently we got the totally wrong vehicle here ... // (but we will delete it only, if we need, because it may be needed for a chain) bool delete_this = (finance->get_account_overdrawn() > 0) && (gewinn < -cnv->calc_restwert()); // check for empty vehicles (likely stuck) that are making no plus and remove them ... // take care, that the vehicle is old enough ... if (!delete_this && (welt->get_current_month() - cnv->front()->get_purchase_time()) > 6 && gewinn <= 0) { sint64 goods=0; // no goods for six months? for( int i=0; i<6; i ++) { goods += cnv->get_finance_history( i, convoi_t::CONVOI_TRANSPORTED_GOODS ); } delete_this = (goods==0); } // well, then delete this (likely stuck somewhere) or insanely unneeded if(delete_this) { waytype_t const wt = cnv->front()->get_desc()->get_waytype(); linehandle_t line = cnv->get_line(); DBG_MESSAGE("ai_goods_t::do_ki()","%s retires convoi %s!", get_name(), cnv->get_name()); koord3d start_pos, end_pos; schedule_t *schedule = cnv->get_schedule(); if(schedule && schedule->get_count()>1) { start_pos = schedule->entries[0].pos; end_pos = schedule->entries[1].pos; } cnv->self_destruct(); if(cnv.is_bound()) { cnv->step(); // to really get rid of it } // last vehicle on that connection (no line => railroad) if( !line.is_bound() || line->count_convoys()==0 ) { // check if a connection boat must be removed halthandle_t start_halt = haltestelle_t::get_halt(start_pos,this); if(start_halt.is_bound() && (start_halt->get_station_type()&haltestelle_t::dock)!=0) { // delete all ships on this line vector_tpl lines; koord water_stop = koord::invalid; simlinemgmt.get_lines( simline_t::shipline, &lines ); for(linehandle_t const line : lines) { schedule_t *schedule=line->get_schedule(); if(schedule->get_count()>1 && haltestelle_t::get_halt(schedule->entries[0].pos,this)==start_halt) { water_stop = koord( (start_pos.x+schedule->entries[0].pos.x)/2, (start_pos.y+schedule->entries[0].pos.y)/2 ); while(line->count_convoys()>0) { convoihandle_t cnv = line->get_convoy(0); cnv->self_destruct(); if(cnv.is_bound()) { cnv->step(); } } simlinemgmt.delete_line( line ); } } // delete harbour call_general_tool( TOOL_REMOVER, water_stop, NULL ); } } if(wt==track_wt) { char param[16]; sprintf( param, "%i", track_wt ); tool_wayremover_t tool; tool.set_default_param(param); tool.init( this ); tool.work( this, start_pos ); tool.work( this, end_pos ); tool.exit( this ); } else { // last convoi => remove completely< if(line.is_bound() && line->count_convoys()==0) { simlinemgmt.delete_line( line ); char param[16]; sprintf( param, "%i", wt ); tool_wayremover_t tool; tool.set_default_param(param); tool.init( this ); tool.work( this, start_pos ); if(tool.work( this, end_pos )!=NULL) { // cannot remove all => likely some other convois there too // remove loading bays and road on start and end, if we cannot remove the whole way tool.work( this, start_pos ); tool.work( this, start_pos ); tool.work( this, end_pos ); tool.work( this, end_pos ); } tool.exit( this ); } } break; } } state = NR_INIT; } break; default: dbg->warning("ai_goods_t::step()","Illegal state!", state ); state = NR_INIT; } } void ai_goods_t::rdwr(loadsave_t *file) { if( file->is_version_less(102, 2) ) { // due to an error the player was never saved correctly player_t::rdwr(file); return; } xml_tag_t t( file, "ai_goods_t" ); ai_t::rdwr(file); // then check, if we have to do something or the game is too old ... if(file->is_version_less(101, 0)) { // ignore saving, reinit on loading if( file->is_loading() ) { state = NR_INIT; road_vehicle = NULL; road_weg = NULL; next_construction_steps = welt->get_steps()+simrand(400); root = start = ziel = NULL; } return; } // now save current state ... file->rdwr_enum(state); platz1.rdwr( file ); size1.rdwr( file ); platz2.rdwr( file ); size2.rdwr( file ); file->rdwr_long(count_rail); file->rdwr_long(count_road); file->rdwr_long(count); if( file->is_version_less(111, 1) ) { file->rdwr_bool(road_transport); file->rdwr_bool(rail_transport); file->rdwr_bool(ship_transport); air_transport = false; } if(file->is_saving()) { // save current pointers sint32 delta_steps = next_construction_steps-welt->get_steps(); file->rdwr_long(delta_steps); koord3d k3d = root ? root->get_pos() : koord3d::invalid; k3d.rdwr(file); k3d = start ? start->get_pos() : koord3d::invalid; k3d.rdwr(file); k3d = ziel ? ziel->get_pos() : koord3d::invalid; k3d.rdwr(file); // what freight? const char *s = freight ? freight->get_name() : NULL; file->rdwr_str( s ); // vehicles desc s = rail_engine ? rail_engine->get_name() : NULL; file->rdwr_str( s ); s = rail_vehicle ? rail_vehicle->get_name() : NULL; file->rdwr_str( s ); s = road_vehicle ? road_vehicle->get_name() : NULL; file->rdwr_str( s ); s = ship_vehicle ? ship_vehicle->get_name() : NULL; file->rdwr_str( s ); // ways s = rail_weg ? rail_weg->get_name() : NULL; file->rdwr_str( s ); s = road_weg ? road_weg->get_name() : NULL; file->rdwr_str( s ); } else { // since steps in loaded game == 0 file->rdwr_long(next_construction_steps); // reinit current pointers koord3d k3d; k3d.rdwr(file); root = fabrik_t::get_fab( k3d.get_2d() ); k3d.rdwr(file); start = fabrik_t::get_fab( k3d.get_2d() ); k3d.rdwr(file); ziel = fabrik_t::get_fab( k3d.get_2d() ); // freight? const char *temp=NULL; file->rdwr_str( temp ); freight = temp ? goods_manager_t::get_info(temp) : NULL; // vehicles file->rdwr_str( temp ); rail_engine = temp ? vehicle_builder_t::get_info(temp) : NULL; file->rdwr_str( temp ); rail_vehicle = temp ? vehicle_builder_t::get_info(temp) : NULL; file->rdwr_str( temp ); road_vehicle = temp ? vehicle_builder_t::get_info(temp) : NULL; file->rdwr_str( temp ); ship_vehicle = temp ? vehicle_builder_t::get_info(temp) : NULL; // ways file->rdwr_str( temp ); rail_weg = temp ? way_builder_t::get_desc(temp,0) : NULL; file->rdwr_str( temp ); road_weg = temp ? way_builder_t::get_desc(temp,0) : NULL; } // finally: forbidden connection list sint32 cnt = forbidden_connections.get_count(); file->rdwr_long(cnt); if(file->is_saving()) { for(fabconnection_t* const fc : forbidden_connections) { fc->rdwr(file); } } else { forbidden_connections.clear(); while( cnt-->0 ) { fabconnection_t *fc = new fabconnection_t(0,0,0); fc->rdwr(file); // Don't add if fab or ware no longer in the game. if( fc->fab1 && fc->fab2 && fc->ware ) { forbidden_connections.append(fc); } else { delete fc; } } } // save harbour position if( file->is_version_atleast(110, 1) ) { harbour.rdwr(file); } } bool ai_goods_t::is_forbidden( fabrik_t *fab1, fabrik_t *fab2, const goods_desc_t *w ) const { fabconnection_t fc(fab1, fab2, w); for(fabconnection_t* const test_fc : forbidden_connections) { if (fc == (*test_fc)) { return true; } } return false; } void ai_goods_t::fabconnection_t::rdwr(loadsave_t *file) { koord3d k3d; if(file->is_saving()) { k3d = fab1->get_pos(); k3d.rdwr(file); k3d = fab2->get_pos(); k3d.rdwr(file); const char *s = ware->get_name(); file->rdwr_str( s ); } else { koord3d pos1, pos2; pos1.rdwr(file); fab1 = fabrik_t::get_fab( pos1.get_2d() ); pos2.rdwr(file); fab2 = fabrik_t::get_fab( pos2.get_2d() ); const char *temp=NULL; file->rdwr_str( temp ); if (!temp) { dbg->fatal("ai_goods_t::fabconnection_t::rdwr", "No name for freight connection between %s and %s", pos1.get_fullstr(), pos2.get_fullstr()); } ware = goods_manager_t::get_info(temp); } } /** * Dealing with stuck or lost vehicles: * - delete lost ones * - ignore stuck ones */ void ai_goods_t::report_vehicle_problem(convoihandle_t cnv,const koord3d ziel) { if( cnv->get_state() == convoi_t::NO_ROUTE && this!=welt->get_active_player() ) { DBG_MESSAGE("ai_passenger_t::report_vehicle_problem","Vehicle %s can't find a route to (%i,%i)!", cnv->get_name(),ziel.x,ziel.y); cnv->self_destruct(); return; } player_t::report_vehicle_problem( cnv, ziel ); } /** * Tells the player that a fabrik_t is going to be deleted. * It could also tell, that a fab has been created, but by now the factory_builder_t does not. */ void ai_goods_t::notify_factory(notification_factory_t flag, const fabrik_t* fab) { switch(flag) { // factory is going to be deleted case notify_delete: if (start==fab || ziel==fab || root==fab) { root = NULL; start = NULL; ziel = NULL; // set new state if (state == NR_SAMMLE_ROUTEN || state == NR_BAUE_ROUTE1) { state = NR_INIT; } else if (state == NR_BAUE_SIMPLE_SCHIENEN_ROUTE || state == NR_BAUE_STRASSEN_ROUTE || state == NR_BAUE_WATER_ROUTE) { state = NR_BAUE_CLEAN_UP; } else if (state == NR_RAIL_SUCCESS || state == NR_ROAD_SUCCESS || state == NR_WATER_SUCCESS) { state = CHECK_CONVOI; } for( slist_tpl::iterator i=forbidden_connections.begin(); i!=forbidden_connections.end(); ) { fabconnection_t *fc = *i; if (fc->fab1 == fab || fc->fab2 == fab) { i = forbidden_connections.erase(i); delete fc; } else { ++i; } } } break; default: ; } } simutrans-124.5/src/simutrans/player/ai_goods.h000066400000000000000000000071741520675460500216420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef PLAYER_AI_GOODS_H #define PLAYER_AI_GOODS_H #include "ai.h" /// Simple goods transport AI class ai_goods_t : public ai_t { private: enum state { NR_INIT, NR_SAMMLE_ROUTEN, NR_BAUE_ROUTE1, NR_BAUE_SIMPLE_SCHIENEN_ROUTE, NR_BAUE_STRASSEN_ROUTE, NR_BAUE_WATER_ROUTE, NR_BAUE_CLEAN_UP, NR_RAIL_SUCCESS, NR_ROAD_SUCCESS, NR_WATER_SUCCESS, CHECK_CONVOI }; // vars for the KI state state; /** * test more than one supplier and more than one good * * save last factory for building next supplier/consumer * */ fabrik_t *root; // actual route to be built between those fabrik_t *start; fabrik_t *ziel; const goods_desc_t *freight; // we will use this vehicle! const vehicle_desc_t *rail_vehicle; const vehicle_desc_t *rail_engine; const vehicle_desc_t *road_vehicle; const vehicle_desc_t *ship_vehicle; // and the convoi will run on this track: const way_desc_t *rail_weg; const way_desc_t *road_weg; sint32 count_rail; sint32 count_road; // multi-purpose counter sint32 count; // time to wait before next construction sint32 next_construction_steps; /* start and end stop position (and their size) */ koord platz1, size1, platz2, size2, harbour; // KI helper class class fabconnection_t{ friend class ai_goods_t; fabrik_t *fab1; fabrik_t *fab2; // koord1 must be always "smaller" than koord2 const goods_desc_t *ware; public: fabconnection_t( fabrik_t *k1=0, fabrik_t *k2=0, const goods_desc_t *w=0 ) : fab1(k1), fab2(k2), ware(w) {} void rdwr( loadsave_t *file ); bool operator != (const fabconnection_t & k) const { return fab1 != k.fab1 || fab2 != k.fab2 || ware != k.ware; } bool operator == (const fabconnection_t & k) const { return fab1 == k.fab1 && fab2 == k.fab2 && ware == k.ware; } // const bool operator < (const fabconnection_t & k) { return (abs(fab1.x)+abs(fab1.y)) - (abs(k.fab1.x)+abs(k.fab1.y)) < 0; } }; slist_tpl forbidden_connections; // return true, if this a route to avoid (i.e. we did a construction without success here ...) bool is_forbidden( fabrik_t *fab1, fabrik_t *fab2, const goods_desc_t *w ) const; /* recursive lookup of a factory tree: * sets start and destination to the next needed supplier * start always with the first branch, if there are more goods */ bool get_factory_tree_lowest_missing( fabrik_t *fab ); /* recursive lookup of a tree and how many factories must be at least connected * returns -1, if this tree is can't be completed */ int get_factory_tree_missing_count( fabrik_t *fab ); bool suche_platz1_platz2(fabrik_t *qfab, fabrik_t *zfab, int length); int baue_bahnhof(const koord* p, int vehicle_count); bool create_simple_rail_transport(); // create way and stops for these routes bool create_ship_transport_vehicle(fabrik_t *qfab, int vehicle_count); void create_road_transport_vehikel(fabrik_t *qfab, int vehicle_count); void create_rail_transport_vehikel(const koord pos1,const koord pos2, int vehicle_count, int minimum_loading); public: ai_goods_t(uint8 nr); // this type of AIs identifier uint8 get_ai_id() const OVERRIDE { return AI_GOODS; } // cannot do airfreight at the moment void set_air_transport( bool ) OVERRIDE { air_transport = false; } void rdwr(loadsave_t *file) OVERRIDE; void report_vehicle_problem(convoihandle_t cnv,const koord3d ziel) OVERRIDE; bool set_active( bool b ) OVERRIDE; void step() OVERRIDE; void new_year() OVERRIDE; void rotate90( const sint16 y_size ) OVERRIDE; void notify_factory(notification_factory_t flag, const fabrik_t*) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/player/ai_passenger.cc000066400000000000000000001521071520675460500226510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../world/simcity.h" #include "../simconvoi.h" #include "../simfab.h" #include "../simhalt.h" #include "../simline.h" #include "../tool/simmenu.h" #include "../simmesg.h" #include "../world/simworld.h" #include "../builder/brueckenbauer.h" #include "../builder/hausbauer.h" #include "../builder/tunnelbauer.h" #include "../builder/vehikelbauer.h" #include "../builder/wegbauer.h" #include "../dataobj/schedule.h" #include "../dataobj/loadsave.h" #include "../dataobj/marker.h" #include "../utils/cbuffer.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "../vehicle/vehicle.h" #include "ai_passenger.h" #include "finance.h" ai_passenger_t::ai_passenger_t(uint8 nr) : ai_t( nr ) { state = NR_INIT; road_vehicle = NULL; road_weg = NULL; next_construction_steps = welt->get_steps() + 50; road_transport = true; rail_transport = false; air_transport = true; ship_transport = true; start_stadt = end_stadt = NULL; ziel = NULL; end_attraction = NULL; } /** * Activates/deactivates a player */ bool ai_passenger_t::set_active(bool new_state) { // only activate, when there are buses available! if( new_state ) { new_state = NULL!=vehicle_search( road_wt, 50, 80, goods_manager_t::passengers, false); } return player_t::set_active( new_state ); } /** return the hub of a city (always the very first stop) or zero */ halthandle_t ai_passenger_t::get_our_hub( const stadt_t *s ) const { for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if (halt->get_owner() == sim::up_cast(this)) { if( halt->get_pax_enabled() && (halt->get_station_type()&haltestelle_t::busstop)!=0 ) { koord h=halt->get_basis_pos(); if(h.x>=s->get_linksoben().x && h.y>=s->get_linksoben().y && h.x<=s->get_rechtsunten().x && h.y<=s->get_rechtsunten().y ) { return halt; } } } } return halthandle_t(); } koord ai_passenger_t::find_area_for_hub( const koord lo, const koord ru, const koord basis ) const { // no found found koord best_pos = koord::invalid; // "shortest" distance int dist = 999; for( sint16 x=lo.x; x<=ru.x; x++ ) { for( sint16 y=lo.y; y<=ru.y; y++ ) { const koord trypos(x,y); const grund_t * gr = welt->lookup_kartenboden(trypos); if(gr) { // flat, solid if( gr->get_typ()==grund_t::boden && gr->get_grund_hang()==slope_t::flat ) { const obj_t* obj = gr->obj_bei(0); int test_dist = koord_distance( trypos, basis ); if (!obj || !obj->get_owner() || obj->get_owner() == sim::up_cast(this)) { if( gr->is_halt() && check_owner( gr->get_halt()->get_owner(), this ) && gr->hat_weg(road_wt) ) { // ok, one halt belongs already to us ... (should not really happen!) but might be a public stop return trypos; } else if( test_disthat_weg(road_wt) && !gr->is_halt() ) { ribi_t::ribi ribi = gr->get_weg_ribi_unmasked(road_wt); if( ribi_t::is_straight(ribi) || ribi_t::is_single(ribi) ) { best_pos = trypos; dist = test_dist; } } else if( test_dist+2ist_natur() ) { // also ok for a stop, but second choice // so we gave it a malus of 2 best_pos = trypos; dist = test_dist+2; } } } } } } DBG_MESSAGE("ai_passenger_t::find_area_for_hub()","suggest hub at (%i,%i)",best_pos.x,best_pos.y); return best_pos; } /** * tries to built a hub near the koordinate */ koord ai_passenger_t::find_place_for_hub( const stadt_t *s ) const { halthandle_t h = get_our_hub( s ); if(h.is_bound()) { return h->get_basis_pos(); } return find_area_for_hub( s->get_linksoben(), s->get_rechtsunten(), s->get_pos() ); } koord ai_passenger_t::find_harbour_pos(karte_t* welt, const stadt_t *s ) { koord bestpos = koord::invalid, k; sint32 bestdist = 999999; // try to find an airport place as close to the city as possible for( k.y=max(6,s->get_linksoben().y-10); k.y<=min(welt->get_size().y-3-6,s->get_rechtsunten().y+10); k.y++ ) { for( k.x=max(6,s->get_linksoben().x-25); k.x<=min(welt->get_size().x-3-6,s->get_rechtsunten().x+10); k.x++ ) { sint32 testdist = koord_distance( k, s->get_pos() ); if( testdistget_linksoben().x || k.y+2get_linksoben().y || k.x>=s->get_rechtsunten().x || k.y>=s->get_rechtsunten().y ) { // malus for out of town testdist += 5; } if( testdistlookup_kartenboden(k); slope_t::type hang = gr->get_grund_hang(); if( gr->ist_natur() && gr->get_hoehe() == welt->get_water_hgt(k) && slope_t::is_way(hang) && welt->is_water( k - koord(hang), koord(hang) * 4 + koord(1, 1) ) ) { // can built busstop here? koord bushalt = k+koord(hang); gr = welt->lookup_kartenboden(bushalt); if(gr && gr->ist_natur()) { bestpos = k; bestdist = testdist; } } } } } } return bestpos; } bool ai_passenger_t::create_water_transport_vehicle(const stadt_t* start_stadt, const koord target_pos) { const vehicle_desc_t *v_desc = vehicle_search(water_wt, 10, 40, goods_manager_t::passengers, false); if(v_desc==NULL ) { // no ship there return false; } if( convoihandle_t::is_exhausted() ) { // too many convois => cannot do anything about this ... return false; } stadt_t *end_stadt = NULL; grund_t *ziel = welt->lookup_kartenboden(target_pos); gebaeude_t *gb = ziel->find(); if(gb && gb->is_townhall()) { end_stadt = gb->get_stadt(); } else if(!ziel->is_halt() || !ziel->is_water()) { // not townhall, not factory => we will not built this line for attractions! return false; } halthandle_t start_hub = get_our_hub( start_stadt ); halthandle_t start_connect_hub; koord start_harbour = koord::invalid; if(start_hub.is_bound()) { if( (start_hub->get_station_type()&haltestelle_t::dock)==0 ) { start_connect_hub = start_hub; start_hub = halthandle_t(); // is there already one harbour next to this one? for(haltestelle_t::connection_t const& i : start_connect_hub->get_pax_connections()) { halthandle_t const h = i.halt; if( h->get_station_type()&haltestelle_t::dock ) { start_hub = h; break; } } } else { start_harbour = start_hub->get_basis_pos(); } } // find an dock place if(!start_hub.is_bound()) { start_harbour = find_harbour_pos( welt, start_stadt ); } if(start_harbour==koord::invalid) { // sorry, no suitable place return false; } // same for end halthandle_t end_hub = end_stadt ? get_our_hub( end_stadt ) : ziel->get_halt(); halthandle_t end_connect_hub; koord end_harbour = koord::invalid; if(end_hub.is_bound()) { if( (end_hub->get_station_type()&haltestelle_t::dock)==0 ) { end_connect_hub = end_hub; end_hub = halthandle_t(); // is there already one harbour next to this one? for(haltestelle_t::connection_t const& i : end_connect_hub->get_pax_connections()) { halthandle_t const h = i.halt; if( h->get_station_type()&haltestelle_t::dock ) { start_hub = h; break; } } } else { end_harbour = end_hub->get_basis_pos(); } } if(!end_hub.is_bound() && end_stadt) { // find an dock place end_harbour = find_harbour_pos( welt, end_stadt ); } if(end_harbour==koord::invalid) { // sorry, no suitable place return false; } const koord start_dx(welt->lookup_kartenboden(start_harbour)->get_grund_hang()); const koord end_dx(welt->lookup_kartenboden(end_harbour)->get_grund_hang()); // now we must check, if these two seas are connected { // we use the free own vehicle_desc_t vehicle_desc_t remover_desc( water_wt, 500, vehicle_desc_t::diesel ); vehicle_t* test_driver = vehicle_builder_t::build( koord3d( start_harbour - start_dx, welt->get_water_hgt( start_harbour - start_dx ) ), this, NULL, &remover_desc ); test_driver->set_flag( obj_t::not_on_map ); route_t verbindung; bool connected = verbindung.calc_route( welt, koord3d( start_harbour - start_dx, welt->get_water_hgt( start_harbour - start_dx ) ), koord3d( end_harbour - end_dx, welt->get_water_hgt( end_harbour - end_dx ) ), test_driver, 0, 0 ); delete test_driver; if(!connected) { return false; } } // build the harbour if necessary if(!start_hub.is_bound()) { koord bushalt = start_harbour+start_dx; const koord town_road = find_place_for_hub( start_stadt ); // first: built street to harbour if(town_road!=bushalt) { way_builder_t bauigel(this); // no bridges => otherwise first tile might be bridge start ... bauigel.init_builder( way_builder_t::strasse, way_builder_t::weg_search( road_wt, 25, welt->get_timeline_year_month(), type_flat ), tunnel_builder_t::get_tunnel_desc(road_wt,road_vehicle->get_topspeed(),welt->get_timeline_year_month()), NULL ); bauigel.set_keep_existing_faster_ways(true); bauigel.set_keep_city_roads(true); bauigel.set_maximum(10000); bauigel.calc_route( welt->lookup_kartenboden(bushalt)->get_pos(), welt->lookup_kartenboden(town_road)->get_pos() ); if(bauigel.get_count()-1 <= 1) { return false; } bauigel.build(); } } if(!end_hub.is_bound()) { koord bushalt = end_harbour+end_dx; const koord town_road = find_place_for_hub( end_stadt ); // first: built street to harbour if(town_road!=bushalt) { way_builder_t bauigel(this); // no bridges => otherwise first tile might be bridge start ... bauigel.init_builder( way_builder_t::strasse, way_builder_t::weg_search( road_wt, 25, welt->get_timeline_year_month(), type_flat ), tunnel_builder_t::get_tunnel_desc(road_wt,road_vehicle->get_topspeed(),welt->get_timeline_year_month()), NULL ); bauigel.set_keep_existing_faster_ways(true); bauigel.set_keep_city_roads(true); bauigel.set_maximum(10000); bauigel.calc_route( welt->lookup_kartenboden(bushalt)->get_pos(), welt->lookup_kartenboden(town_road)->get_pos() ); if(bauigel.get_count()-1 <= 1) { return false; } bauigel.build(); } } // now built the stops ... (since the roads were ok!) if(!start_hub.is_bound()) { /* first we must built the bus stop, since this will be the default stop for all our buses * we want to keep the name of a dock, thus we will create it beforehand */ koord bushalt = start_harbour+start_dx; const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX ); // now built the bus stop if(!call_general_tool( TOOL_BUILD_STATION, bushalt, busstop_desc->get_name() )) { return false; } // and change name to dock ... grund_t *gr = welt->lookup_kartenboden(bushalt); halthandle_t halt = gr ? gr->get_halt() : halthandle_t(); if (halt.is_bound()) { // it should be, but avoid crashes char* const name = halt->create_name(bushalt, "Dock"); halt->set_name( name ); free(name); } // finally built the dock const building_desc_t* dock_desc = hausbauer_t::get_random_station(building_desc_t::dock, water_wt, welt->get_timeline_year_month(), 0); welt->lookup_kartenboden(start_harbour)->obj_loesche_alle(this); call_general_tool( TOOL_BUILD_STATION, start_harbour, dock_desc->get_name() ); grund_t *harbour_gr = welt->lookup_kartenboden(start_harbour); start_hub = harbour_gr ? harbour_gr->get_halt() : halthandle_t(); // eventually we must built a hub in the next town start_connect_hub = get_our_hub( start_stadt ); if(!start_connect_hub.is_bound()) { koord sch = find_place_for_hub( start_stadt ); call_general_tool( TOOL_BUILD_STATION, sch, busstop_desc->get_name() ); start_connect_hub = get_our_hub( start_stadt ); assert( start_connect_hub.is_bound() ); } } if(!end_hub.is_bound()) { /* again we must built the bus stop first, since this will be the default stop for all our buses * we want to keep the name of a dock, thus we will create it beforehand */ koord bushalt = end_harbour+end_dx; const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX ); // now built the bus stop if(!call_general_tool( TOOL_BUILD_STATION, bushalt, busstop_desc->get_name() )) { return false; } // and change name to dock ... grund_t *gr = welt->lookup_kartenboden(bushalt); halthandle_t halt = gr ? gr->get_halt() : halthandle_t(); if (halt.is_bound()) { // it should be, but avoid crashes char* const name = halt->create_name(bushalt, "Dock"); halt->set_name( name ); free(name); } // finally built the dock const building_desc_t* dock_desc = hausbauer_t::get_random_station(building_desc_t::dock, water_wt, welt->get_timeline_year_month(), 0 ); welt->lookup_kartenboden(end_harbour)->obj_loesche_alle(this); call_general_tool( TOOL_BUILD_STATION, end_harbour, dock_desc->get_name() ); grund_t *harbour_gr = welt->lookup_kartenboden(end_harbour); end_hub = harbour_gr ? harbour_gr->get_halt() : halthandle_t(); // eventually we must built a hub in the next town end_connect_hub = get_our_hub( end_stadt ); if(!end_connect_hub.is_bound()) { koord ech = find_place_for_hub( end_stadt ); call_general_tool( TOOL_BUILD_STATION, ech, busstop_desc->get_name() ); end_connect_hub = get_our_hub( end_stadt ); assert( end_connect_hub.is_bound() ); } } uint16 const cov = welt->get_settings().get_station_coverage(); // now we have harbour => find start position for ships koord pos1 = start_harbour-start_dx; koord start_pos = pos1; for (int y = pos1.y - cov; y <= pos1.y + cov; ++y) { for (int x = pos1.x - cov; x <= pos1.x + cov; ++x) { koord p(x,y); const planquadrat_t *plan = welt->access(p); if(plan) { grund_t *gr = plan->get_kartenboden(); if( gr->is_water() && !gr->get_halt().is_bound() ) { if(plan->get_haltlist_count()>=1 && plan->get_haltlist()[0]==start_hub && koord_distance(start_pos,end_harbour)>koord_distance(p,end_harbour)) { start_pos = p; } } } } } // now we have harbour => find start position for ships pos1 = end_harbour-end_dx; koord end_pos = pos1; for (int y = pos1.y - cov; y <= pos1.y + cov; ++y) { for (int x = pos1.x - cov; x <= pos1.x + cov; ++x) { koord p(x,y); const planquadrat_t *plan = welt->access(p); if(plan) { grund_t *gr = plan->get_kartenboden(); if( gr->is_water() && !gr->get_halt().is_bound() ) { if(plan->get_haltlist_count()>=1 && plan->get_haltlist()[0]==end_hub && koord_distance(end_pos,start_harbour)>koord_distance(p,start_harbour)) { end_pos = p; } } } } } // since 86.01 we use lines for vehicles ... schedule_t *schedule=new ship_schedule_t(); schedule->append( welt->lookup_kartenboden(start_pos), 0, 0 ); schedule->append( welt->lookup_kartenboden(end_pos), 90, 0 ); schedule->set_current_stop( 1 ); schedule->finish_editing(); linehandle_t line=simlinemgmt.create_line(simline_t::shipline,this,schedule); delete schedule; // now create one ship vehicle_t* v = vehicle_builder_t::build( koord3d( start_pos, welt->get_water_hgt( start_pos ) ), this, NULL, v_desc ); convoi_t* cnv = new convoi_t(this); cnv->set_name(v->get_desc()->get_name()); cnv->add_vehicle( v ); cnv->set_line(line); cnv->start(); // eventually build a shuttle bus ... if(start_connect_hub.is_bound() && start_connect_hub!=start_hub) { koord stops[2] = { start_harbour+start_dx, start_connect_hub->get_basis_pos() }; create_bus_transport_vehicle( stops[1], 1, stops, 2, false ); } // eventually build a airport shuttle bus ... if(end_connect_hub.is_bound() && end_connect_hub!=end_hub) { koord stops[2] = { end_harbour+end_dx, end_connect_hub->get_basis_pos() }; create_bus_transport_vehicle( stops[1], 1, stops, 2, false ); } return true; } halthandle_t ai_passenger_t::build_airport(const stadt_t* city, koord pos, int rotation) { // not too close to border? if(pos.x<6 || pos.y<6 || pos.x+3+6>=welt->get_size().x || pos.y+3+6>=welt->get_size().y ) { return halthandle_t(); } // ok, not prematurely doomed // can we built airports at all? const way_desc_t *taxi_desc = way_builder_t::weg_search( air_wt, 25, welt->get_timeline_year_month(), type_flat ); const way_desc_t *runway_desc = way_builder_t::weg_search( air_wt, 250, welt->get_timeline_year_month(), type_runway ); if(taxi_desc==NULL || runway_desc==NULL) { return halthandle_t(); } // first, check if at least one tile is within city limits const koord lo = city->get_linksoben(); koord size(3-1,3-1); // make sure pos is within city limits! if(pos.xget_basis_pos() : find_place_for_hub( city ); if( town_road==koord::invalid) { return halthandle_t(); } // ok, now we could built it => flatten the land sint8 h = max( welt->get_water_hgt(pos) + 1, welt->lookup_kartenboden(pos)->get_hoehe() ); const koord dx( size.x/2, size.y/2 ); for( sint16 i=0; i!=size.y+dx.y; i+=dx.y ) { for( sint16 j=0; j!=size.x+dx.x; j+=dx.x ) { climate c = welt->get_climate(pos+koord(j,i)); if(!welt->flatten_tile(this,pos+koord(j,i),h)) { return halthandle_t(); } // ensure is land grund_t* bd = welt->lookup_kartenboden(pos+koord(j,i)); if (bd->get_typ() == grund_t::wasser) { welt->set_water_hgt_nocheck(pos+koord(j,i), bd->get_hoehe()-1); welt->access(pos+koord(j,i))->correct_water(); welt->set_climate(pos+koord(j,i), c, true); } } } // now taxiways way_builder_t bauigel(this); // 3x3 layout, first we make the taxiway cross koord center=pos+dx; bauigel.init_builder( way_builder_t::luft, taxi_desc, NULL, NULL ); bauigel.calc_straight_route( welt->lookup_kartenboden(center+koord::north)->get_pos(), welt->lookup_kartenboden(center+koord::south)->get_pos() ); assert(bauigel.get_count()-1 > 1); bauigel.build(); bauigel.init_builder( way_builder_t::luft, taxi_desc, NULL, NULL ); bauigel.calc_straight_route( welt->lookup_kartenboden(center+koord::west)->get_pos(), welt->lookup_kartenboden(center+koord::east)->get_pos() ); assert(bauigel.get_count()-1 > 1); bauigel.build(); // now try to connect one of the corners with a road koord bushalt = koord::invalid, runwaystart, runwayend; koord trypos[4] = { koord(0,0), koord(size.x,0), koord(0,size.y), koord(size.x,size.y) }; uint32 length=9999; rotation=-1; bauigel.init_builder( way_builder_t::strasse, way_builder_t::weg_search( road_wt, 25, welt->get_timeline_year_month(), type_flat ), tunnel_builder_t::get_tunnel_desc(road_wt,road_vehicle->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(road_wt,road_vehicle->get_topspeed(),welt->get_timeline_year_month()) ); bauigel.set_keep_existing_faster_ways(true); bauigel.set_keep_city_roads(true); bauigel.set_maximum(10000); // find the closest one to town ... for( int i=0; i<4; i++ ) { bushalt = pos+trypos[i]; bauigel.calc_route(welt->lookup_kartenboden(bushalt)->get_pos(),welt->lookup_kartenboden(town_road)->get_pos()); // no road => try next if( bauigel.get_count() >= 2 && bauigel.get_count() < length+1 ) { rotation = i; length = bauigel.get_count()-1; } } if(rotation==-1) { // if we every get here that means no connection road => remove airport welt->lookup_kartenboden(center+koord::north)->remove_everything_from_way( this, air_wt, ribi_t::none ); welt->lookup_kartenboden(center+koord::south)->remove_everything_from_way( this, air_wt, ribi_t::none ); welt->lookup_kartenboden(center+koord::west)->remove_everything_from_way( this, air_wt, ribi_t::none ); welt->lookup_kartenboden(center+koord::east)->remove_everything_from_way( this, air_wt, ribi_t::none ); welt->lookup_kartenboden(center)->remove_everything_from_way( this, air_wt, ribi_t::all ); return halthandle_t(); } bushalt = pos+trypos[rotation]; bauigel.calc_route(welt->lookup_kartenboden(bushalt)->get_pos(),welt->lookup_kartenboden(town_road)->get_pos()); bauigel.build(); // now the busstop (our next hub ... ) const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX ); // get an airport name (even though the hub is the bus stop ... ) // now built the bus stop if(!call_general_tool( TOOL_BUILD_STATION, bushalt, busstop_desc->get_name() )) { welt->lookup_kartenboden(center+koord::north)->remove_everything_from_way( this, air_wt, ribi_t::none ); welt->lookup_kartenboden(center+koord::south)->remove_everything_from_way( this, air_wt, ribi_t::none ); welt->lookup_kartenboden(center+koord::west)->remove_everything_from_way( this, air_wt, ribi_t::none ); welt->lookup_kartenboden(center+koord::east)->remove_everything_from_way( this, air_wt, ribi_t::none ); welt->lookup_kartenboden(center)->remove_everything_from_way( this, air_wt, ribi_t::all ); return halthandle_t(); } // and change name to airport ... grund_t *gr = welt->lookup_kartenboden(bushalt); halthandle_t halt = gr ? gr->get_halt() : halthandle_t(); if (halt.is_bound()) { // it should be, but avoid crashes char* const name = halt->create_name(bushalt, "Airport"); halt->set_name( name ); free(name); } // built also runway now ... bauigel.init_builder( way_builder_t::luft, runway_desc, NULL, NULL ); bauigel.calc_straight_route( welt->lookup_kartenboden(pos+trypos[rotation==0?3:0])->get_pos(), welt->lookup_kartenboden(pos+trypos[1+(rotation&1)])->get_pos() ); assert(bauigel.get_count()-1 > 1); bauigel.build(); // now the airstops (only on single tiles, this will always work const building_desc_t* airstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, air_wt, welt->get_timeline_year_month(), 0 ); for( int i=0; i<4; i++ ) { if( koord_distance(center+koord::nesw[i],bushalt)==1 && ribi_t::is_single( welt->lookup_kartenboden(center+koord::nesw[i])->get_weg_ribi_unmasked(air_wt) ) ) { call_general_tool( TOOL_BUILD_STATION, center+koord::nesw[i], airstop_desc->get_name() ); } } // and now the one far away ... for( int i=0; i<4; i++ ) { if( koord_distance(center+koord::nesw[i],bushalt)>1 && ribi_t::is_single( welt->lookup_kartenboden(center+koord::nesw[i])->get_weg_ribi_unmasked(air_wt) ) ) { call_general_tool( TOOL_BUILD_STATION, center+koord::nesw[i], airstop_desc->get_name() ); } } // success return halt; } static koord find_airport_pos(karte_t* welt, const stadt_t *s ) { koord bestpos = koord::invalid, k; sint32 bestdist = 999999; // try to find an airport place as close to the city as possible for( k.y=max(6,s->get_linksoben().y-10); k.y<=min(welt->get_size().y-3-6,s->get_rechtsunten().y+10); k.y++ ) { for( k.x=max(6,s->get_linksoben().x-25); k.x<=min(welt->get_size().x-3-6,s->get_rechtsunten().x+10); k.x++ ) { sint32 testdist = koord_distance( k, s->get_pos() ); if( testdistget_linksoben().x || k.y+2get_linksoben().y || k.x>=s->get_rechtsunten().x || k.y>=s->get_rechtsunten().y ) { // malus for out of town testdist += 5; } if( testdistsquare_is_free( k, 3, 3, NULL, ALL_CLIMATES ) ) { bestpos = k; bestdist = testdist; } } } } return bestpos; } /** build airports and planes */ bool ai_passenger_t::create_air_transport_vehicle(const stadt_t *start_stadt, const stadt_t *end_stadt) { const vehicle_desc_t *v_desc = vehicle_search(air_wt, 10, 900, goods_manager_t::passengers, false); if(v_desc==NULL) { // no aircraft there return false; } if( convoihandle_t::is_exhausted() ) { // too many convois => cannot do anything about this ... return false; } halthandle_t start_hub = get_our_hub( start_stadt ); halthandle_t start_connect_hub; koord start_airport; if(start_hub.is_bound()) { if( (start_hub->get_station_type()&haltestelle_t::airstop)==0 ) { start_connect_hub = start_hub; start_hub = halthandle_t(); // is there already one airport next to this town? for(haltestelle_t::connection_t const& i : start_connect_hub->get_pax_connections()) { halthandle_t const h = i.halt; if( h->get_station_type()&haltestelle_t::airstop ) { start_hub = h; break; } } } else { start_airport = start_hub->get_basis_pos(); } } // find an airport place if(!start_hub.is_bound()) { start_airport = find_airport_pos( welt, start_stadt ); if(start_airport==koord::invalid) { // sorry, no suitable place return false; } } // same for end halthandle_t end_hub = get_our_hub( end_stadt ); halthandle_t end_connect_hub; koord end_airport; if(end_hub.is_bound()) { if( (end_hub->get_station_type()&haltestelle_t::airstop)==0 ) { end_connect_hub = end_hub; end_hub = halthandle_t(); // is there already one airport next to this town? for(haltestelle_t::connection_t const& i : end_connect_hub->get_pax_connections()) { halthandle_t const h = i.halt; if( h->get_station_type()&haltestelle_t::airstop ) { start_hub = h; break; } } } else { end_airport = end_hub->get_basis_pos(); } } if(!end_hub.is_bound()) { // find an airport place end_airport = find_airport_pos( welt, end_stadt ); if(end_airport==koord::invalid) { // sorry, no suitable place return false; } } // eventually construct them if(start_airport!=koord::invalid && end_airport!=koord::invalid) { // build the airport if necessary if(!start_hub.is_bound()) { start_hub = build_airport(start_stadt, start_airport, true); if(!start_hub.is_bound()) { return false; } start_connect_hub = get_our_hub( start_stadt ); if(!start_connect_hub.is_bound()) { koord sch = find_place_for_hub( start_stadt ); // probably we must construct a city hub, since the airport is outside of the city limits const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX ); call_general_tool( TOOL_BUILD_STATION, sch, busstop_desc->get_name() ); start_connect_hub = get_our_hub( start_stadt ); assert( start_connect_hub.is_bound() ); } } if(!end_hub.is_bound()) { end_hub = build_airport(end_stadt, end_airport, true); if(!end_hub.is_bound()) { if (start_hub->get_pax_connections().empty()) { // remove airport busstop welt->lookup_kartenboden(start_hub->get_basis_pos())->remove_everything_from_way( this, road_wt, ribi_t::none ); koord center = start_hub->get_basis_pos() + koord( welt->lookup_kartenboden(start_hub->get_basis_pos())->get_weg_ribi_unmasked( air_wt ) ); // now the remaining taxi-/runways for( sint16 y=center.y-1; y<=center.y+1; y++ ) { for( sint16 x=center.x-1; x<=center.x+1; x++ ) { welt->lookup_kartenboden(koord(x,y))->remove_everything_from_way( this, air_wt, ribi_t::none ); } } } return false; } end_connect_hub = get_our_hub( end_stadt ); if(!end_connect_hub.is_bound()) { koord ech = find_place_for_hub( end_stadt ); // probably we must construct a city hub, since the airport is outside of the city limits const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX ); call_general_tool( TOOL_BUILD_STATION, ech, busstop_desc->get_name() ); end_connect_hub = get_our_hub( end_stadt ); assert( end_connect_hub.is_bound() ); } } } // now we have airports (albeit first tile is a bus stop) const grund_t *start = start_hub->find_matching_position(air_wt); const grund_t *end = end_hub->find_matching_position(air_wt); // since 86.01 we use lines for vehicles ... schedule_t *schedule=new airplane_schedule_t(); schedule->append( start, 0, 0 ); schedule->append( end, 90, 0 ); schedule->set_current_stop( 1 ); schedule->finish_editing(); linehandle_t line=simlinemgmt.create_line(simline_t::airline,this,schedule); delete schedule; // now create one plane vehicle_t* v = vehicle_builder_t::build( start->get_pos(), this, NULL, v_desc); convoi_t* cnv = new convoi_t(this); cnv->set_name(v->get_desc()->get_name()); cnv->add_vehicle( v ); cnv->set_line(line); cnv->start(); // eventually build a airport shuttle bus ... if(start_connect_hub.is_bound() && start_connect_hub!=start_hub) { koord stops[2] = { start_hub->get_basis_pos(), start_connect_hub->get_basis_pos() }; create_bus_transport_vehicle( stops[1], 1, stops, 2, false ); } // eventually build a airport shuttle bus ... if(end_connect_hub.is_bound() && end_connect_hub!=end_hub) { koord stops[2] = { end_hub->get_basis_pos(), end_connect_hub->get_basis_pos() }; create_bus_transport_vehicle( stops[1], 1, stops, 2, false ); } return true; } /** creates a more general road transport */ void ai_passenger_t::create_bus_transport_vehicle(koord startpos2d,int vehicle_count,koord *stops,int count,bool do_wait) { DBG_MESSAGE("ai_passenger_t::create_bus_transport_vehicle()","bus at (%i,%i)",startpos2d.x,startpos2d.y); // now start all vehicle one field before, so they load immediately koord3d startpos = welt->lookup_kartenboden(startpos2d)->get_pos(); // since 86.01 we use lines for road vehicles ... schedule_t *schedule=new truck_schedule_t(); // do not start at current stop => wont work ... for(int j=0; jappend(welt->lookup_kartenboden(stops[j]), j == 0 || !do_wait ? 0 : 10, j == 0 || !do_wait ? 0 : 6553 ); } schedule->set_current_stop( stops[0]==startpos2d ); schedule->finish_editing(); linehandle_t line=simlinemgmt.create_line(simline_t::truckline,this,schedule); delete schedule; // now create all vehicles as convois for(int i=0; i cannot do anything about this ... return; } convoi_t* cnv = new convoi_t(this); // give the new convoi name from first vehicle cnv->set_name(v->get_desc()->get_name()); cnv->add_vehicle( v ); cnv->set_line(line); cnv->start(); } } /* now we follow all adjacent streets recursively and mark them * if they below to this stop, then we continue */ void ai_passenger_t::walk_city(linehandle_t const line, grund_t* const start, int const limit) { //maximum number of stops reached? if(line->get_schedule()->get_count()>=limit) { return; } ribi_t::ribi ribi = start->get_weg_ribi(road_wt); for(int r=0; r<4; r++) { // a way in our direction? if( (ribi & ribi_t::nesw[r])==0 ) { continue; } // ok, if connected, not marked, and not owner by somebody else grund_t *to; if( start->get_neighbour(to, road_wt, ribi_t::nesw[r] ) && !marker->is_marked(to) && check_owner(to->obj_bei(0)->get_owner(),this) ) { // ok, here is a valid street tile marker->mark(to); // can built a station here if( ribi_t::is_straight(to->get_weg_ribi(road_wt)) ) { // find out how many tiles we have covered already int covered_tiles=0; int house_tiles=0; uint16 const cov = welt->get_settings().get_station_coverage(); for (sint16 y = to->get_pos().y - cov; y <= to->get_pos().y + cov + 1; ++y) { for (sint16 x = to->get_pos().x - cov; x <= to->get_pos().x + cov + 1; ++x) { const planquadrat_t *pl = welt->access(koord(x,y)); // check, if we have a passenger stop already here if(pl && pl->get_haltlist_count()>0) { const halthandle_t *hl=pl->get_haltlist(); for( uint8 own=0; ownget_haltlist_count(); own++ ) { if( hl[own]->is_enabled(goods_manager_t::INDEX_PAS) ) { // our stop => nothing to do #ifdef AUTOJOIN_PUBLIC // we leave also public stops alone if( hl[own]->get_owner()==this || hl[own]->get_owner()==welt->get_public_player() ) { #else if( hl[own]->get_owner()==this ) { #endif covered_tiles ++; break; } } } } // check for houses if(pl && pl->get_kartenboden()->get_typ()==grund_t::fundament) { house_tiles++; } } } // now decide, if we build here // just using the ration of covered tiles versus house tiles int const max_tiles = cov * 2 + 1; if( covered_tiles<(max_tiles*max_tiles)/3 && house_tiles>=3 ) { // ok, lets do it const building_desc_t* bs = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX); if( call_general_tool( TOOL_BUILD_STATION, to->get_pos().get_2d(), bs->get_name() ) ) { //add to line line->get_schedule()->append(to,0); // no need to register it yet; done automatically, when convois will be assigned } } // start road, but no houses anywhere => stop searching if(house_tiles==0) { return; } } // now do recursion walk_city( line, to, limit ); } } } /* tries to cover a city with bus stops that does not overlap much and cover as much as possible * returns the line created, if successful */ void ai_passenger_t::cover_city_with_bus_route(koord start_pos, int number_of_stops) { if( convoihandle_t::is_exhausted() ) { // too many convois => cannot do anything about this ... return; } // nothing in lists marker = &marker_t::instance(welt->get_size().x, welt->get_size().y); // and init all stuff for recursion grund_t *start = welt->lookup_kartenboden(start_pos); linehandle_t line = simlinemgmt.create_line( simline_t::truckline,this, new truck_schedule_t() ); line->get_schedule()->append(start,0); // now create a line walk_city( line, start, number_of_stops ); line->get_schedule()->finish_editing(); road_vehicle = vehicle_search( road_wt, 1, 50, goods_manager_t::passengers, false); if( line->get_schedule()->get_count()>1 ) { // success: add a bus to the line vehicle_t* v = vehicle_builder_t::build(start->get_pos(), this, NULL, road_vehicle); convoi_t* cnv = new convoi_t(this); cnv->set_name(v->get_desc()->get_name()); cnv->add_vehicle( v ); cnv->set_line(line); cnv->start(); } else { simlinemgmt.delete_line( line ); } marker = NULL; } void ai_passenger_t::step() { // needed for schedule of stops ... player_t::step(); if(!active) { // I am off ... return; } // one route per month ... if( welt->get_steps() < next_construction_steps ) { return; } switch(state) { case NR_INIT: { // time to update hq? built_update_headquarter(); // assume fail state = CHECK_CONVOI; /* if we have this little money, we do nothing * The second condition may happen due to extensive replacement operations; * in such a case it is save enough to expand anyway. */ if(!(finance->get_account_balance()>0 || finance->has_money_or_assets()) ) { return; } const weighted_vector_tpl& staedte = welt->get_cities(); int count = staedte.get_count(); int offset = (count>1) ? simrand(count-1) : 0; // start with previous target const stadt_t* last_start_stadt = start_stadt; start_stadt = end_stadt; end_stadt = NULL; end_attraction = NULL; ziel = NULL; platz2 = koord::invalid; // if no previous town => find one if(start_stadt==NULL) { if (staedte.empty()) { return; } // larger start town preferred start_stadt = pick_any_weighted(staedte); offset = staedte.index_of(start_stadt); } DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","using city %s for start",start_stadt->get_name()); const halthandle_t start_halt = get_our_hub(start_stadt); // find starting place if(!start_halt.is_bound()) { DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","new_hub"); } platz1 = start_halt.is_bound() ? start_halt->get_basis_pos() : find_place_for_hub( start_stadt ); if(platz1==koord::invalid) { return; } DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","using place (%i,%i) for start",platz1.x,platz1.y); if(count==1 || simrand(3)==0) { DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","searching attraction"); // 25 % of all connections are tourist attractions const weighted_vector_tpl &attractions = welt->get_attractions(); // this way, we are sure, our factory is connected to this town ... const vector_tpl &fabriken = start_stadt->get_target_factories_for_pax().get_entries(); unsigned last_dist = 0xFFFFFFFF; bool ausflug=simrand(2)!=0; // holidays first ... int ziel_count=ausflug?attractions.get_count():fabriken.get_count(); for( int i=0; iget_mail_level() <= 25) { // not a good object to go to ... continue; } pos = a->get_pos().get_2d(); size = a->get_tile()->get_desc()->get_size(a->get_tile()->get_layout()); } else { const fabrik_t* f = fabriken[i].factory; const factory_desc_t *const desc = f->get_desc(); if (( desc->get_pax_demand()==65535 ? desc->get_pax_level() : desc->get_pax_demand() ) <= 10) { // not a good object to go to ... we want more action ... continue; } pos = f->get_pos().get_2d(); size = f->get_desc()->get_building()->get_size(f->get_rotate()); } const stadt_t *next_town = welt->find_nearest_city(pos); if(next_town==NULL || start_stadt==next_town) { // this is either a town already served (so we do not create a new hub) // or a lonely point somewhere // in any case we do not want to serve this location already uint16 const c = welt->get_settings().get_station_coverage(); koord const cov = koord(c, c); koord test_platz=find_area_for_hub(pos-cov,pos+size+cov,pos); if( !get_halt(test_platz).is_bound() ) { // not served dist = koord_distance(platz1,test_platz); if(dist+simrand(50)3) { // but closer than the others if(ausflug) { end_attraction = attractions[i]; } else { ziel = fabriken[i].factory; } last_dist = dist; platz2 = test_platz; } } } } // test for success if(platz2!=koord::invalid) { // found something state = NR_SAMMLE_ROUTEN; DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","decision: %s wants to built network between %s and %s",get_name(),start_stadt->get_name(),ausflug?end_attraction->get_tile()->get_desc()->get_name():ziel->get_name()); } } else { DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","searching town"); int last_dist = 9999999; // find a good route for( int i=0; iget_pos()); if( end_halt.is_bound() && is_connected(platz1,end_halt->get_basis_pos(),goods_manager_t::passengers) ) { // already connected continue; } // check if more close if( distget_name(),end_stadt->get_name()); } } } break; // so far only busses case NR_SAMMLE_ROUTEN: { // koord end_hub_pos = koord::invalid; DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","Find hub"); // also for target (if not tourist attraction!) if(end_stadt!=NULL) { DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","try to built connect to city %p", end_stadt ); // target is town halthandle_t h = get_our_hub(end_stadt); if(h.is_bound()) { end_hub_pos = h->get_basis_pos(); } else { end_hub_pos = find_place_for_hub( end_stadt ); } } else { // already found place end_hub_pos = platz2; } // was successful? if(end_hub_pos!=koord::invalid) { // ok, now we check the vehicle platz2 = end_hub_pos; DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","hub found -> NR_BAUE_ROUTE1"); state = NR_BAUE_ROUTE1; } else { // no success DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","no suitable hub found"); end_stadt = NULL; state = CHECK_CONVOI; } } break; // get a suitable vehicle case NR_BAUE_ROUTE1: // wait for construction semaphore { // we want the fastest we can get! road_vehicle = vehicle_search( road_wt, 50, 80, goods_manager_t::passengers, false); if(road_vehicle!=NULL) { // find the best => AI will never survive // road_weg = way_builder_t::weg_search( road_wt, road_vehicle->get_topspeed(), welt->get_timeline_year_month(),type_flat ); // find the really cheapest road road_weg = way_builder_t::weg_search( road_wt, 10, welt->get_timeline_year_month(), type_flat ); state = NR_BAUE_STRASSEN_ROUTE; DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","using %s on %s",road_vehicle->get_name(),road_weg->get_name()); } else { // no success end_stadt = NULL; state = CHECK_CONVOI; } } break; // built a simple road (no bridges, no tunnels) case NR_BAUE_STRASSEN_ROUTE: { state = NR_BAUE_WATER_ROUTE; // assume failure if( !ai_t::road_transport ) { // no overland bus lines break; } const building_desc_t* bs = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX); if(bs && create_simple_road_transport(platz1, koord(1,1),platz2,koord(1,1),road_weg) ) { // since the road may have led to a crossing at the intended stop position ... bool ok = true; if( !get_halt(platz1).is_bound() ) { if( !call_general_tool( TOOL_BUILD_STATION, platz1, bs->get_name() ) ) { platz1 = find_area_for_hub( platz1-koord(2,2), platz1+koord(2,2), platz1 ); ok = call_general_tool( TOOL_BUILD_STATION, platz1, bs->get_name() ); } } if( ok ) { if( !get_halt(platz2).is_bound() ) { if( !call_general_tool( TOOL_BUILD_STATION, platz2, bs->get_name() ) ) { platz2 = find_area_for_hub( platz2-koord(2,2), platz2+koord(2,2), platz2 ); ok = call_general_tool( TOOL_BUILD_STATION, platz2, bs->get_name() ); } } } // still continue? if(ok) { koord list[2]={ platz1, platz2 }; // wait only, if target is not a hub but an attraction/factory create_bus_transport_vehicle(platz1,1,list,2,end_stadt==NULL); state = NR_SUCCESS; // tell the player cbuffer_t buf; if(end_attraction!=NULL) { platz1 = end_attraction->get_pos().get_2d(); buf.printf(translator::translate("%s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n"), get_name(), start_stadt->get_name(), make_single_line_string(translator::translate(end_attraction->get_tile()->get_desc()->get_name()),2), platz1.x, platz1.y ); end_stadt = start_stadt; } else if(ziel!=NULL) { platz1 = ziel->get_pos().get_2d(); buf.printf( translator::translate("%s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n"), get_name(), start_stadt->get_name(), make_single_line_string(translator::translate(ziel->get_name()),2), platz1.x, platz1.y ); end_stadt = start_stadt; } else { buf.printf( translator::translate("Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n"), get_name(), start_stadt->get_name(), end_stadt->get_name() ); // add two intown routes cover_city_with_bus_route(platz1, 6); cover_city_with_bus_route(platz2, 6); } welt->get_message()->add_message(buf, welt->lookup_kartenboden(platz1)->get_pos(), message_t::ai, PLAYER_FLAG | player_nr, road_vehicle->get_base_image()); } } } break; case NR_BAUE_WATER_ROUTE: if( !ai_t::ship_transport ) { // no overland bus lines state = NR_BAUE_AIRPORT_ROUTE; break; } if( end_attraction == NULL && ship_transport && create_water_transport_vehicle(start_stadt, end_stadt ? end_stadt->get_pos() : ziel->get_pos().get_2d())) { // add two intown routes cover_city_with_bus_route( get_our_hub(start_stadt)->get_basis_pos(), 6); if(end_stadt!=NULL) { cover_city_with_bus_route( get_our_hub(end_stadt)->get_basis_pos(), 6); } else { // start again with same town end_stadt = start_stadt; } cbuffer_t buf; buf.printf( translator::translate("Ferry service by\n%s\nnow between\n%s \nand %s.\n"), get_name(), start_stadt->get_name(), end_stadt->get_name() ); koord3d pos = welt->lookup_kartenboden(end_stadt->get_pos())->get_pos(); welt->get_message()->add_message(buf, pos, message_t::ai, PLAYER_FLAG | player_nr, road_vehicle->get_base_image()); state = NR_SUCCESS; } else { if( end_attraction==NULL && ziel==NULL ) { state = NR_BAUE_AIRPORT_ROUTE; } else { state = NR_BAUE_CLEAN_UP; } } break; // despite its name: try airplane case NR_BAUE_AIRPORT_ROUTE: // try airline (if we are wealthy enough) ... if( !air_transport || finance->get_history_com_month(1, ATC_CASH) < finance->get_starting_money() || !end_stadt || !create_air_transport_vehicle( start_stadt, end_stadt ) ) { state = NR_BAUE_CLEAN_UP; } else { // add two intown routes cover_city_with_bus_route( get_our_hub(start_stadt)->get_basis_pos(), 6); cover_city_with_bus_route( get_our_hub(end_stadt)->get_basis_pos(), 6); cbuffer_t buf; buf.printf( translator::translate("Airline service by\n%s\nnow between\n%s \nand %s.\n"), get_name(), start_stadt->get_name(), end_stadt->get_name() ); koord3d pos = welt->lookup_kartenboden(end_stadt->get_pos())->get_pos(); welt->get_message()->add_message(buf, pos, message_t::ai, PLAYER_FLAG | player_nr, road_vehicle->get_base_image()); state = NR_SUCCESS; } break; // remove marker etc. case NR_BAUE_CLEAN_UP: state = CHECK_CONVOI; end_stadt = NULL; // otherwise it may always try to built the same route! break; // successful construction case NR_SUCCESS: { state = CHECK_CONVOI; next_construction_steps = welt->get_steps() + simrand( construction_speed/16 ); } break; // add vehicles to crowded lines case CHECK_CONVOI: { // next time: do something different state = NR_INIT; next_construction_steps = welt->get_steps() + simrand( ai_t::construction_speed ) + 25; vector_tpl lines(0); simlinemgmt.get_lines( simline_t::line, &lines); const uint32 offset = simrand(lines.get_count()); for (uint32 i = 0; iget_linetype()!=simline_t::airline && line->get_linetype()!=simline_t::truckline) { continue; } // remove empty lines if(line->count_convoys()==0) { simlinemgmt.delete_line(line); break; } // avoid empty schedule ?!? assert(!line->get_schedule()->empty()); // made loss with this line if(line->get_finance_history(0,LINE_PROFIT)<0) { // try to update the vehicles if(welt->use_timeline() && line->count_convoys()>1) { // do not update unimportant lines with single vehicles slist_tpl obsolete; uint32 capacity = 0; for( uint i=0; icount_convoys(); i++ ) { convoihandle_t cnv = line->get_convoy(i); if(cnv->has_obsolete_vehicles()) { obsolete.append(cnv); capacity += cnv->front()->get_desc()->get_capacity(); } } if(capacity>0) { // now try to find new vehicle vehicle_t const& v = *line->get_convoy(0)->front(); waytype_t const wt = v.get_waytype(); vehicle_desc_t const* const v_desc = vehicle_builder_t::vehicle_search(wt, welt->get_current_month(), 50, welt->get_average_speed(wt), goods_manager_t::passengers, false, true); if (!v_desc->is_retired(welt->get_current_month()) && v_desc != v.get_desc()) { // there is a newer one ... for( uint32 new_capacity=0; capacity>new_capacity; new_capacity+=v_desc->get_capacity()) { if( convoihandle_t::is_exhausted() ) { // too many convois => cannot do anything about this ... break; } vehicle_t* v = vehicle_builder_t::build( line->get_schedule()->entries[0].pos, this, NULL, v_desc ); convoi_t* new_cnv = new convoi_t(this); new_cnv->set_name( v->get_desc()->get_name() ); new_cnv->add_vehicle( v ); new_cnv->set_line(line); new_cnv->start(); } // delete all old convois while(!obsolete.empty()) { obsolete.remove_first()->self_destruct(); } return; } } } } // next: check for stuck convois ... sint64 free_cap = line->get_finance_history(0,LINE_CAPACITY); sint64 used_cap = line->get_finance_history(0,LINE_TRANSPORTED_GOODS); if(free_cap+used_cap==0) { continue; } sint32 ratio = (sint32)((free_cap*100l)/(free_cap+used_cap)); // next: check for overflowing lines, i.e. running with 3/4 of the capacity if( ratio<10 && !convoihandle_t::is_exhausted() ) { // else add the first convoi again vehicle_t* const v = vehicle_builder_t::build(line->get_schedule()->entries[0].pos, this, NULL, line->get_convoy(0)->front()->get_desc()); convoi_t* new_cnv = new convoi_t(this); new_cnv->set_name( v->get_desc()->get_name() ); new_cnv->add_vehicle( v ); new_cnv->set_line( line ); // on waiting line, wait at alternating stations for load balancing if( line->get_schedule()->entries[1].minimum_loading==90 && line->get_linetype()!=simline_t::truckline && (line->count_convoys()&1)==0 ) { new_cnv->get_schedule()->entries[0].minimum_loading = 90; new_cnv->get_schedule()->entries[1].minimum_loading = 0; } new_cnv->start(); return; } // next: check for too many cars, i.e. running with too many cars if( ratio>40 && line->count_convoys()>1) { // remove one convoi line->get_convoy(0)->self_destruct(); return; } } } break; default: DBG_MESSAGE("ai_passenger_t::do_passenger_ki()", "Illegal state %d!", state ); end_stadt = NULL; state = NR_INIT; } } void ai_passenger_t::rdwr(loadsave_t *file) { if( file->is_version_less(102, 2) ) { // due to an error the player was never saved correctly player_t::rdwr(file); return; } xml_tag_t t( file, "ai_passenger_t" ); ai_t::rdwr(file); // then check, if we have to do something or the game is too old ... if(file->is_version_less(101, 0)) { // ignore saving, reinit on loading if( file->is_loading() ) { next_construction_steps = welt->get_steps()+simrand(ai_t::construction_speed); } return; } // now save current state ... file->rdwr_enum(state); if( file->is_version_less(111, 1) ) { file->rdwr_long(ai_t::construction_speed); file->rdwr_bool(air_transport); file->rdwr_bool(ship_transport); road_transport = true; rail_transport = false; } platz1.rdwr( file ); platz2.rdwr( file ); if(file->is_saving()) { // save current pointers sint32 delta_steps = next_construction_steps-welt->get_steps(); file->rdwr_long(delta_steps); koord k = start_stadt ? start_stadt->get_pos() : koord::invalid; k.rdwr(file); k = end_stadt ? end_stadt->get_pos() : koord::invalid; k.rdwr(file); koord3d k3d = end_attraction ? end_attraction->get_pos() : koord3d::invalid; k3d.rdwr(file); k3d = ziel ? ziel->get_pos() : koord3d::invalid; k3d.rdwr(file); } else { // since steps in loaded game == 0 file->rdwr_long(next_construction_steps); next_construction_steps += welt->get_steps(); // reinit current pointers koord k; k.rdwr(file); start_stadt = welt->find_nearest_city(k); k.rdwr(file); end_stadt = welt->find_nearest_city(k); koord3d k3d; k3d.rdwr(file); end_attraction = welt->lookup(k3d) ? welt->lookup(k3d)->find() : NULL; k3d.rdwr(file); ziel = fabrik_t::get_fab(k3d.get_2d() ); } if (file->is_version_atleast(122, 1)) { plainstring road_vehicle_name = road_vehicle ? road_vehicle->get_name() : ""; file->rdwr_str(road_vehicle_name); if (file->is_loading() && road_vehicle_name != "") { road_vehicle = vehicle_builder_t::get_info(road_vehicle_name); } } } /** * Dealing with stuck or lost vehicles: * - delete lost ones * - ignore stuck ones */ void ai_passenger_t::report_vehicle_problem(convoihandle_t cnv,const koord3d ziel) { if( cnv->get_state() == convoi_t::NO_ROUTE && this!=welt->get_active_player() ) { DBG_MESSAGE("ai_passenger_t::report_vehicle_problem","Vehicle %s can't find a route to (%i,%i)!", cnv->get_name(),ziel.x,ziel.y); cnv->self_destruct(); return; } player_t::report_vehicle_problem( cnv, ziel ); } void ai_passenger_t::finish_rd() { if (!road_vehicle) { dbg->warning("ai_passenger_t::finish_rd", "Default road vehicle could not be loaded, searching for one..."); road_vehicle = vehicle_search( road_wt, 50, 80, goods_manager_t::passengers, false); } if (road_vehicle == NULL) { // reset state end_stadt = NULL; state = CHECK_CONVOI; } } simutrans-124.5/src/simutrans/player/ai_passenger.h000066400000000000000000000046271520675460500225160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef PLAYER_AI_PASSENGER_H #define PLAYER_AI_PASSENGER_H #include "ai.h" class marker_t; /// Simple passenger transport AI class ai_passenger_t : public ai_t { private: enum state { NR_INIT, NR_SAMMLE_ROUTEN, NR_BAUE_ROUTE1, NR_BAUE_AIRPORT_ROUTE, NR_BAUE_STRASSEN_ROUTE, NR_BAUE_WATER_ROUTE, NR_BAUE_CLEAN_UP, NR_SUCCESS, NR_WATER_SUCCESS, CHECK_CONVOI }; // vars for the KI state state; // we will use this vehicle! const vehicle_desc_t *road_vehicle; // and the convoi will run on this track: const way_desc_t *road_weg ; // time to wait before next construction sint32 next_construction_steps; /* start and end stop position (and their size) */ koord platz1, platz2; const stadt_t *start_stadt; const stadt_t *end_stadt; // target is town const gebaeude_t *end_attraction; fabrik_t *ziel; // marker field marker_t *marker; halthandle_t get_our_hub( const stadt_t *s ) const; koord find_area_for_hub( const koord lo, const koord ru, const koord basis ) const; koord find_place_for_hub( const stadt_t *s ) const; /** builds harbours and ferries */ koord find_harbour_pos(karte_t* welt, const stadt_t *s ); bool create_water_transport_vehicle(const stadt_t* start_stadt, const koord target_pos); // builds a simple 3x3 three stop airport with town connection road halthandle_t build_airport(const stadt_t* city, koord pos, int rotate); /** build airports and planes */ bool create_air_transport_vehicle(const stadt_t *start_stadt, const stadt_t *end_stadt); // helper function for bus stops intown void walk_city(linehandle_t line, grund_t* start, int limit); // tries to cover a city with bus stops that does not overlap much and cover as much as possible void cover_city_with_bus_route(koord start_pos, int number_of_stops); void create_bus_transport_vehicle(koord startpos,int vehicle_count,koord *stops,int count,bool do_wait); public: ai_passenger_t(uint8 nr); // this type of AIs identifier uint8 get_ai_id() const OVERRIDE { return AI_PASSENGER; } // cannot do rail void set_rail_transport( bool ) OVERRIDE { rail_transport = false; } void report_vehicle_problem(convoihandle_t cnv,const koord3d ziel) OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; void finish_rd() OVERRIDE; bool set_active( bool b ) OVERRIDE; void step() OVERRIDE; }; #endif simutrans-124.5/src/simutrans/player/ai_scripted.cc000066400000000000000000000164711520675460500225020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "ai_scripted.h" #include "../dataobj/loadsave.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../gui/simwin.h" #include "../gui/player_frame.h" // scripting #include "../script/script.h" #include "../script/script_loader.h" #include "../script/api/api.h" // TODO ai debug window ai_scripted_t::ai_scripted_t(uint8 nr) : ai_t(nr) { script = NULL; set_name("The unknown AI player"); } ai_scripted_t::~ai_scripted_t() { delete script; script = NULL; } const char* ai_scripted_t::init( const char *ai_base, const char *ai_name_) { if (script) { return "AI already initialized"; } ai_name = ai_name_; // path to ai files cbuffer_t buf; buf.printf("%s%s/", ai_base, ai_name_); ai_path = buf; // ai script file buf.append("ai.nut"); if (!load_script( buf )) { dbg->warning("ai_scripted_t::init", "could not load script file %s", (const char*)buf); delete script; script = NULL; return "Loading ai script failed"; } // load translations translator::load_files_from_folder( ai_path.c_str(), "scenario" ); // set the standard name buf.clear(); buf.printf("player %i", player_nr-1); set_name(buf); // now call startup function uint8 dummy = 0; if (const char* err = script->call_function(script_vm_t::QUEUE, "start", dummy, get_player_nr())) { if (strcmp(err, "suspended")) { dbg->warning("ai_scripted_t::init", "error [%s] calling start", err); } } // update player window if (ki_kontroll_t *frame = dynamic_cast( win_get_magic(magic_ki_kontroll_t) ) ) { frame->update_data(); } return NULL; } bool ai_scripted_t::load_script(const char* filename) { cbuffer_t buf; buf.printf("script-ai-%d.log", player_nr); // start vm script = script_loader_t::start_vm("ai_base.nut", buf, ai_path.c_str(), false); if (script == NULL) { return false; } // set my player number script->set_my_player(get_player_nr()); // load ai definition if (const char* err = script->call_script(filename)) { if (strcmp(err, "suspended")) { dbg->error("ai_scripted_t::load_script", "error [%s] calling %s", err, filename); } return false; } return true; } const char* ai_scripted_t::reload_script() { if (script == NULL) { return NULL; } // save the script to string plainstring str; script->call_function(script_vm_t::FORCEX, "save", str); // save old vm instance script_vm_t *old_script = script; script = NULL; // ai script file cbuffer_t buf; buf.printf("%sai.nut", ai_path.c_str()); if (!load_script( buf )) { dbg->warning("ai_scripted_t::reload_script", "could not reload script file %s", (const char*)buf); delete script; script = old_script; return "Reloading ai script failed"; } // restore persistent data const char* err = script->eval_string(str); if (err) { dbg->warning("ai_scripted_t::reload_script", "error [%s] evaluating persistent ai data", err); return "Reloading ai script data failed"; } // resume uint8 dummy = 0; script->call_function(script_vm_t::QUEUE, "resume_game", dummy, get_player_nr()); return NULL; } void ai_scripted_t::step() { if(!active) { return; } if (script) { script->call_function(script_vm_t::QUEUE, "step"); } } bool ai_scripted_t::new_month() { bool res = ai_t::new_month(); if (res && script) { script->call_function(script_vm_t::QUEUE, "new_month"); } return res; } void ai_scripted_t::new_year() { ai_t::new_year(); if (script) { script->call_function(script_vm_t::QUEUE, "new_year"); } } void repair_table_keys(plainstring &str) { // iterate linewise // replace [0\t]*([0-9][.*]) = by ["\1"]= // replace [0\t]*(".*") = by [\1]= char *p = str; char *line = p; // start of line char *equal = NULL; // place of equality sign char *first_digit = NULL; // first digit in line while (*p) { if (*p == '=') { if (first_digit && first_digit > line && equal == NULL) { // match /" =/ if (line+2 < p && *(p-2) == '"') { *(p-1) = ']'; } else { *(p-1) = '"'; *(p ) = ']'; *(p+1) = '='; } equal = p; } } else if (*p == '\n') { line = p; equal = NULL; first_digit = NULL; } else if('0' <= *p && *p <= '9') { if (first_digit == NULL && p >= line+2) { *(p-2) = '['; *(p-1) = '"'; first_digit = p; } } else if (first_digit == NULL && *p != ' ' && *p != '\t') { if (*p == '"' && line+1 < p ) { // " *(p-1) = '['; first_digit = p+1; } else { first_digit = line; } } // some keys contained broken umlauts if (equal == NULL && *p < 0) { *p = '_'; } p++; } } void ai_scripted_t::rdwr(loadsave_t *file) { ai_t::rdwr(file); script_api::coordinate_transform_t::rdwr(file); file->rdwr_str(ai_name); if (file->is_loading()) { // ai saved, but no script was assigned if (ai_name == NULL || *ai_name == 0) { script = NULL; return; } // load persistent data plainstring str; file->rdwr_str(str); dbg->message("ai_scripted_t::rdwr", "loaded persistent ai data: %s", str.c_str()); if (env_t::networkmode && !env_t::server) { // scripted players run on server only, for now at least delete script; script = NULL; return; } // DO NOT READ ANYTHING FROM file AFTER THIS POINT // load script cbuffer_t script_filename; // try addon directory first bool rdwr_error = true; if (env_t::default_settings.get_with_private_paks()) { ai_path = ( std::string("addons/ai/") + ai_name.c_str() + "/").c_str(); script_filename.printf("%sai.nut", ai_path.c_str()); rdwr_error = !load_script(script_filename); } // failed, try ai from data directory if (rdwr_error) { ai_path = ( std::string(env_t::base_dir) + "/ai/" + ai_name.c_str() + "/").c_str(); script_filename.clear(); script_filename.printf("%sai.nut", ai_path.c_str()); rdwr_error = !load_script(script_filename); } if (!rdwr_error) { // restore persistent data const char* err = script->eval_string(str); if (err && strcmp(err, "Error compiling string buffer")==0) { // sqai produced invalid table keys up to r9007 (and later as well... ) repair_table_keys(str); dbg->warning("ai_scripted_t::rdwr", "repaired persistent ai data: %s", str.c_str()); // close error message destroy_win(magic_script_error); // and try again err = script->eval_string(str); } if (err) { dbg->warning("ai_scripted_t::rdwr", "error [%s] evaluating persistent ai data for script '%s'", err, ai_name.c_str()); rdwr_error = true; } } else { dbg->warning("ai_scripted_t::rdwr", "could not load script file %s", (const char*)script_filename); } if (rdwr_error) { delete script; script = NULL; } } else { plainstring str(""); if (script) { script->call_function(script_vm_t::FORCEX, "save", str); dbg->warning("ai_scripted_t::rdwr", "write persistent ai data: %s", str.c_str()); } if (ai_name && *ai_name) { // valid name, save even if script is NULL file->rdwr_str(str); } } } void ai_scripted_t::finish_rd() { ai_t::finish_rd(); if (script) { uint8 dummy = 0; if (const char* err = script->call_function(script_vm_t::QUEUE, "resume_game", dummy, get_player_nr())) { if (strcmp(err, "suspended")) { dbg->warning("ai_scripted_t::finish_rd", "error [%s] calling resume_game", err); } } } } simutrans-124.5/src/simutrans/player/ai_scripted.h000066400000000000000000000030721520675460500223350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef PLAYER_AI_SCRIPTED_H #define PLAYER_AI_SCRIPTED_H #include "ai.h" #include "../utils/plainstring.h" class script_vm_t; /// Squirrel Script AI class ai_scripted_t : public ai_t { /// name of ai, files are searched in ai/ai_name/... /// e.g. my_ai plainstring ai_name; /// path to ai directory (relative to env_t::user_dir) /// e.g. ai/my_ai/ plainstring ai_path; /** * loads ai file with the given name * @param filename name ai script file (including .nut extension) */ bool load_script(const char* filename); /// pointer to virtual machine script_vm_t *script; public: ai_scripted_t(uint8 nr); ~ai_scripted_t(); /** * Initializes scripted ai */ const char* init( const char *ai_base, const char *ai_name); /** * Reloads script * @returns error message */ const char* reload_script(); bool has_script() const { return script; } uint8 get_ai_id() const OVERRIDE { return AI_SCRIPTED; } const char* get_ai_name() const { return ai_name; } void step() OVERRIDE; /** * Called monthly by simworld.cc during simulation * @returns false if player has to be removed (bankrupt/inactive) */ bool new_month() OVERRIDE; /** * Called yearly by simworld.cc during simulation */ void new_year() OVERRIDE; /** * Stores/loads the player state * @param file where the data will be saved/loaded */ void rdwr(loadsave_t *file) OVERRIDE; /** * Called after game is fully loaded; */ void finish_rd() OVERRIDE; }; #endif simutrans-124.5/src/simutrans/player/finance.cc000066400000000000000000001006651520675460500216160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../world/simworld.h" #include "../descriptor/building_desc.h" #include "../dataobj/loadsave.h" #include "../dataobj/scenario.h" #include "simplay.h" #include "finance.h" /** * initialize finance history arrays */ finance_t::finance_t(player_t *, karte_t * _world) : world(_world) { account_balance = world->get_settings().get_starting_money(world->get_last_year()); starting_money = account_balance; account_overdrawn = 0; for (int year=0; year= ATV_REVENUE )) { veh_month[TT_ALL][0][j] += veh_month[tt][0][j]; } } } for( int j=0; j< ATV_MAX; ++j ) { veh_year[TT_ALL][0][j] =0; for( int tt=1; tt= ATV_REVENUE )) { veh_year[TT_ALL][0][j] += veh_year[tt][0][j]; } } } // recalc margin for TT_ALL veh_month[TT_ALL][0][ATV_PROFIT_MARGIN] = calc_margin(veh_month[TT_ALL][0][ATV_OPERATING_PROFIT], veh_month[TT_ALL][0][ATV_REVENUE]); veh_year[TT_ALL][0][ATV_PROFIT_MARGIN] = calc_margin(veh_year[TT_ALL][0][ATV_OPERATING_PROFIT], veh_year[TT_ALL][0][ATV_REVENUE]); // undistinguishable by type of transport com_month[0][ATC_CASH] = account_balance; com_year [0][ATC_CASH] = account_balance; com_month[0][ATC_NETWEALTH] = veh_month[TT_ALL][0][ATV_NON_FINANCIAL_ASSETS] + account_balance; com_year [0][ATC_NETWEALTH] = veh_year[TT_ALL][0][ATV_NON_FINANCIAL_ASSETS] + account_balance; } sint64 finance_t::get_maintenance_with_bits(transport_type tt) const { assert(ttscale_with_month_length(maintenance[tt]); } sint64 finance_t::get_vehicle_maintenance_with_bits(transport_type tt) const { assert(ttscale_with_month_length(vehicle_maintenance[tt]); } bool finance_t::is_bancrupted() const { return ( get_netwealth() <=0 && veh_year[TT_ALL][0][ATV_INFRASTRUCTURE_MAINTENANCE] == 0 && maintenance[TT_ALL] == 0 && com_year[0][ATC_ALL_CONVOIS] == 0 ); } void finance_t::new_month() { calc_finance_history(); roll_history_month(); if(world->get_last_month()==0) { roll_history_year(); } // subtract maintenance for(int i=0; iis_version_less(112, 5) ) { rdwr_compatibility(file); return; } /* following lines enables FORWARD compatibility / you will be still able to load future versions of games with: * longer history * more transport_types * and new items in ATC_ or ATV_ */ sint8 max_years = MAX_PLAYER_HISTORY_YEARS; sint8 max_months = MAX_PLAYER_HISTORY_MONTHS; sint8 max_tt = TT_MAX; sint8 max_atc = ATC_MAX; sint8 max_atv = ATV_MAX; // used for reading longer history sint64 dummy = 0; // calc finance history for TT_ALL to save it correctly if( ! file->is_loading() ) { calc_finance_history(); } file->rdwr_longlong(account_balance); file->rdwr_long(account_overdrawn); file->rdwr_longlong(starting_money); file->rdwr_byte( max_years ); file->rdwr_byte( max_months ); file->rdwr_byte( max_tt ); // tt = transport type file->rdwr_byte( max_atc ); // atc = accounting type common file->rdwr_byte( max_atv ); // atv = accounting type vehicles for(int year = 0; year < max_years ; ++year ) { for( int cost_type = 0; cost_type < max_atc ; ++cost_type ) { if( ( year < MAX_PLAYER_HISTORY_YEARS ) && ( cost_type < ATC_MAX ) ) { file->rdwr_longlong( com_year[year][cost_type] ); } else { file->rdwr_longlong( dummy ); } } } for(int month = 0; month < max_months; ++month) { for( int cost_type = 0; cost_type < max_atc; ++cost_type ) { if( ( month < MAX_PLAYER_HISTORY_MONTHS ) && ( cost_type < ATC_MAX ) ) { file->rdwr_longlong( com_month[month][cost_type] ); } else { file->rdwr_longlong( dummy ); } } } for(int tt=0; tt < max_tt; ++tt){ for( int year = 0; year < max_years; ++year ) { for( int cost_type = 0; cost_type < max_atv; ++cost_type ) { if( ( tt < TT_MAX ) && ( year < MAX_PLAYER_HISTORY_YEARS ) && ( cost_type < ATV_MAX ) ) { file->rdwr_longlong( veh_year[tt][year][cost_type] ); } else { file->rdwr_longlong( dummy ); } } } } for(int tt=0; tt < max_tt; ++tt){ for( int month = 0; month < max_months; ++month ) { for( int cost_type = 0; cost_type < max_atv; ++cost_type ) { if( ( tt < TT_MAX ) && ( month < MAX_PLAYER_HISTORY_MONTHS ) && ( cost_type < ATV_MAX ) ) { file->rdwr_longlong( veh_month[tt][month][cost_type] ); } else { file->rdwr_longlong( dummy ); } } } } } void finance_t::roll_history_month() { // undistinguishable for (int i=MAX_PLAYER_HISTORY_MONTHS-1; i>0; i--) { for(int accounting_type=0; accounting_type0; i--) { for(int accounting_type=0; accounting_type0; i--) { for(int accounting_type=0; accounting_type0; i--) { for(int accounting_type=0; accounting_type 0 ){ veh_month[TT_OTHER][i][ATV_TOLL_RECEIVED] = finance_history_month[i][COST_WAY_TOLLS]; veh_month[TT_ALL ][i][ATV_TOLL_RECEIVED] = finance_history_month[i][COST_WAY_TOLLS]; } else{ veh_month[TT_OTHER][i][ATV_TOLL_PAID] = finance_history_month[i][COST_WAY_TOLLS]; veh_month[TT_ALL ][i][ATV_TOLL_PAID] = finance_history_month[i][COST_WAY_TOLLS]; } veh_month[TT_OTHER][i][ATV_WAY_TOLL] = finance_history_month[i][COST_WAY_TOLLS]; veh_month[TT_ALL ][i][ATV_WAY_TOLL] = finance_history_month[i][COST_WAY_TOLLS]; } } void finance_t::import_from_cost_year( const sint64 finance_history_year[][OLD_MAX_PLAYER_COST]) { for(int i=0; i 0 ){ veh_year[TT_OTHER][i][ATV_TOLL_RECEIVED] = finance_history_year[i][COST_WAY_TOLLS]; veh_year[TT_ALL ][i][ATV_TOLL_RECEIVED] = finance_history_year[i][COST_WAY_TOLLS]; } else{ veh_year[TT_OTHER][i][ATV_TOLL_PAID] = finance_history_year[i][COST_WAY_TOLLS]; veh_year[TT_ALL ][i][ATV_TOLL_PAID] = finance_history_year[i][COST_WAY_TOLLS]; } veh_year[TT_OTHER][i][ATV_WAY_TOLL] = finance_history_year[i][COST_WAY_TOLLS]; veh_year[TT_ALL ][i][ATV_WAY_TOLL] = finance_history_year[i][COST_WAY_TOLLS]; } } void finance_t::rdwr_compatibility(loadsave_t *file) { sint64 finance_history_year[OLD_MAX_PLAYER_HISTORY_YEARS][OLD_MAX_PLAYER_COST]; sint64 finance_history_month[OLD_MAX_PLAYER_HISTORY_MONTHS][OLD_MAX_PLAYER_COST]; for (int year=0; yearis_version_less(112, 5) ) && ( ! file->is_loading() ) ) { // for saving of game in old format export_to_cost_month( finance_history_month ); export_to_cost_year( finance_history_year ); } if (file->is_version_less(84, 8)) { // not so old save game for (int year = 0;yearis_version_less(84, 7)) { // a cost_type has has been added. For old savegames we only have 9 cost_types, now we have 10. // for old savegames only load 9 types and calculate the 10th; for new savegames load all 10 values if (cost_type < 9) { file->rdwr_longlong(finance_history_year[year][cost_type]); } } else { if (cost_type < 10) { file->rdwr_longlong(finance_history_year[year][cost_type]); } } } } } else if (file->is_version_less(86, 0)) { for (int year = 0;yearrdwr_longlong(finance_history_year[year][cost_type]); } } // in 84008 monthly finance history was introduced for (int month = 0;monthrdwr_longlong(finance_history_month[month][cost_type]); } } } else if (file->is_version_less(99, 11)) { // powerline category missing for (int year = 0;yearrdwr_longlong(finance_history_year[year][cost_type]); } } for (int month = 0;monthrdwr_longlong(finance_history_month[month][cost_type]); } } } else if (file->is_version_less(99, 17)) { // without detailed goo statistics for (int year = 0;yearrdwr_longlong(finance_history_year[year][cost_type]); } } for (int month = 0;monthrdwr_longlong(finance_history_month[month][cost_type]); } } } else if( file->is_version_less(102, 3) ) { // saved everything for (int year = 0;yearrdwr_longlong(finance_history_year[year][cost_type]); } } for (int month = 0;monthrdwr_longlong(finance_history_month[month][cost_type]); } } } else if( file->is_version_less(110, 7) ) { // only save what is needed for(int year = 0; yearCOST_MARGIN ) { file->rdwr_longlong(finance_history_year[year][cost_type]); } } } for (int month = 0;monthCOST_MARGIN ) { file->rdwr_longlong(finance_history_month[month][cost_type]); } } } } else if ( file->is_version_less(112, 5) ) { // savegame version: now with toll for(int year = 0; yearCOST_MARGIN ) { file->rdwr_longlong(finance_history_year[year][cost_type]); } } } for (int month = 0;monthCOST_MARGIN ) { file->rdwr_longlong(finance_history_month[month][cost_type]); } } } } if( file->is_version_atleast(102, 3) ) { file->rdwr_longlong(starting_money); } // we have to pay maintenance at the beginning of a month if(file->is_version_less(99, 18) && file->is_loading()) { finance_history_month[0][COST_MAINTENANCE] -= finance_history_month[1][COST_MAINTENANCE]; finance_history_year [0][COST_MAINTENANCE] -= finance_history_month[1][COST_MAINTENANCE]; set_account_balance(get_account_balance() - finance_history_month[1][COST_MAINTENANCE]); } if(file->is_loading()) { /* prior versions calculated margin incorrectly. * we also save only some values and recalculate all dependent ones * (remember: negative costs are just saved as negative numbers!) */ for( int year=0; year #include "../simtypes.h" /// for compatibility with old versions #define OLD_MAX_PLAYER_COST (19) /// number of years to keep history #define MAX_PLAYER_HISTORY_YEARS (25) /// number of months to keep history #define MAX_PLAYER_HISTORY_MONTHS (25) /** * Type of transport used in accounting statistics. * waytype_t was not used because of values assigned to air_wt and powerline_wt. * There are also buildings like railway station that can be distinguished * by transport_type and can not be distinguished by waytype_t. */ enum transport_type { TT_ALL=0, TT_ROAD, TT_RAILWAY, TT_SHIP, TT_MONORAIL, TT_MAGLEV, TT_TRAM, TT_NARROWGAUGE, TT_AIR, TT_OTHER, ///< everything else that can not be differentiated (e.g. houses), not powerlines TT_MAX_VEH = TT_OTHER, TT_POWERLINE, TT_MAX }; /** * ATC = accounting type common (common means data common for all transport types). * * Supersedes COST_ types, that CAN NOT be distinguished by type of transport- * - the data are concerning to whole company */ enum accounting_type_common { ATC_CASH = 0, ///< Cash ATC_NETWEALTH, ///< Total Cash + Assets ATC_ALL_CONVOIS, ///< Convoy count ATC_SCENARIO_COMPLETED, ///< Scenario success (only useful if there is one ... ) ATC_MAX }; /** * ATV = accounting type vehicles. * Supersedes COST_ types, that CAN be distinguished by type of transport. */ enum accounting_type_vehicles { ATV_REVENUE_PASSENGER = 0, ///< Revenue from passenger transport ATV_REVENUE_MAIL, ///< Revenue from mail transport ATV_REVENUE_GOOD, ///< Revenue from good transport ATV_REVENUE_TRANSPORT, ///< Operating profit = passenger + mail + goods = was: COST_INCOME ATV_TOLL_RECEIVED, ///< Toll paid to you by another player ATV_REVENUE, ///< Operating profit = revenue_transport + toll = passenger + mail+ goods + toll_received ATV_RUNNING_COST, ///< Distance based running costs, was: COST_VEHICLE_RUN ATV_VEHICLE_MAINTENANCE, ///< Monthly vehicle maintenance. Unused. ATV_INFRASTRUCTURE_MAINTENANCE, ///< Infrastructure maintenance (roads, railway, ...), was: COST_MAINTENANCE ATV_TOLL_PAID, ///< Toll paid by you to another player ATV_EXPENDITURE, ///< Total expenditure = RUNNING_COSTS+VEHICLE_MAINTENANCE+INFRACTRUCTURE_MAINTENANCE+TOLL_PAID ATV_OPERATING_PROFIT, ///< ATV_REVENUE - ATV_EXPENDITURE, was: COST_OPERATING_PROFIT ATV_NEW_VEHICLE, ///< New vehicles ATV_CONSTRUCTION_COST, ///< Construction cost, COST_CONSTRUCTION mapped here ATV_PROFIT, ///< ATV_OPERATING_PROFIT - (CONSTRUCTION_COST + NEW_VEHICLE), was: COST_PROFIT ATV_WAY_TOLL, ///< = ATV_TOLL_PAID + ATV_TOLL_RECEIVED, was: COST_WAY_TOLLS ATV_NON_FINANCIAL_ASSETS, ///< Value of vehicles owned by your company, was: COST_ASSETS ATV_PROFIT_MARGIN, ///< ATV_OPERATING_PROFIT / ATV_REVENUE, was: COST_MARGIN ATV_TRANSPORTED_PASSENGER, ///< Number of transported passengers ATV_TRANSPORTED_MAIL, ///< Number of transported mail ATV_TRANSPORTED_GOOD, ///< Number of transported goods ATV_TRANSPORTED, ///< Total number of transported cargo, was COST_ALL_TRANSPORTED ATV_DELIVERED_PASSENGER, ///< Number of delivered passengers, was: COST_TRANSPORTED_PAS ATV_DELIVERED_MAIL, ///< Number of delivered mail, was: COST_TRANSPORTED_MAIL ATV_DELIVERED_GOOD, ///< Number of delivered goods, was: COST_TRANSPORTED_GOOD ATV_DELIVERED, ///< Total number of delivered cargo ATV_MAX }; class loadsave_t; class karte_t; class player_t; class scenario_t; /** * Encapsulate margin calculation (Operating_Profit / Income) */ inline sint64 calc_margin(sint64 operating_profit, sint64 proceeds) { if (proceeds == 0) { return 0; } return (10000 * operating_profit) / proceeds; } /** * convert to displayed value */ inline sint64 convert_money(sint64 value) { return (value + 50) / 100; } /** * Class to encapsulate all company related statistics. */ class finance_t { karte_t * world; /** * Amount of money, previously known as "konto" */ sint64 account_balance; /** * Shows how many months you have been in red numbers. */ sint32 account_overdrawn; /** * Remember the starting money, used e.g. in scenarios. */ sint64 starting_money; /** * Contains values having relation with whole company but not with particular * type of transport (com - common). */ sint64 com_year[MAX_PLAYER_HISTORY_YEARS][ATC_MAX]; /** * Monthly finance history, data not distinguishable by transport type. */ sint64 com_month[MAX_PLAYER_HISTORY_MONTHS][ATC_MAX]; /** * Finance history having relation with particular type of service */ sint64 veh_year[TT_MAX][MAX_PLAYER_HISTORY_YEARS][ATV_MAX]; sint64 veh_month[TT_MAX][MAX_PLAYER_HISTORY_MONTHS][ATV_MAX]; /** * Monthly maintenance cost */ sint64 maintenance[TT_MAX]; /** * Monthly vehicle maintenance cost per transport type. */ sint64 vehicle_maintenance[TT_MAX]; public: finance_t(player_t * _player, karte_t * _world); /** * Adds construction cost to finance stats. * @param amount sum of money * @param wt way type, e.g. tram_wt */ inline void book_construction_costs(const sint64 amount, const waytype_t wt) { transport_type tt = translate_waytype_to_tt(wt); veh_year[tt][0][ATV_CONSTRUCTION_COST] += amount; veh_month[tt][0][ATV_CONSTRUCTION_COST] += amount; account_balance += amount; } /** * Adds count to number of convois in statistics. */ inline void book_convoi_number( const int count ) { com_year[0][ATC_ALL_CONVOIS] += count; com_month[0][ATC_ALL_CONVOIS] += count; } /** * Adds maintenance into/from finance stats. * @param change monthly maintenance cost difference * @param wt - waytype for accounting purposes */ inline sint64 book_maintenance(sint64 change, waytype_t const wt) { transport_type tt = translate_waytype_to_tt(wt); maintenance[tt] += change; maintenance[TT_ALL] += change; return maintenance[tt]; } /** * Account purchase of new vehicle: Subtracts money, increases assets. * @param amount money paid for vehicle (negative) * @param wt - waytype of vehicle */ inline void book_new_vehicle(const sint64 amount, const waytype_t wt) { const transport_type tt = translate_waytype_to_tt(wt); veh_year[ tt][0][ATV_NEW_VEHICLE] += amount; veh_month[tt][0][ATV_NEW_VEHICLE] += amount; update_assets(-amount, wt); account_balance += amount; } /** * Accounts income from transport of passenger, mail, or, goods. * @param amount earned money * @param wt waytype of vehicle * @param index 0 = passenger, 1 = mail, 2 = goods */ inline void book_revenue(const sint64 amount, const waytype_t wt, sint32 index) { const transport_type tt = translate_waytype_to_tt(wt); index = ((0 <= index) && (index <= 2)? index : 2); veh_year[tt][0][ATV_REVENUE_PASSENGER+index] += amount; veh_month[tt][0][ATV_REVENUE_PASSENGER+index] += amount; account_balance += amount; } /** * Accounts distance-based running costs * @param amount sum of money * @param wt way type */ inline void book_running_costs(const sint64 amount, const waytype_t wt) { const transport_type tt = translate_waytype_to_tt(wt); veh_year[tt][0][ATV_RUNNING_COST] += amount; veh_month[tt][0][ATV_RUNNING_COST] += amount; account_balance += amount; } /** * Account toll we have paid to any other company. * @param amount sum of money * @param wt way type */ inline void book_toll_paid(const sint64 amount, const waytype_t wt) { const transport_type tt = translate_waytype_to_tt(wt); veh_year[tt][0][ATV_TOLL_PAID] += amount; veh_month[tt][0][ATV_TOLL_PAID] += amount; account_balance += amount; } /** * Account toll we have received from another company. * @param amount sum of money * @param wt way type */ inline void book_toll_received(const sint64 amount, const waytype_t wt) { const transport_type tt = translate_waytype_to_tt(wt); veh_year[tt][0][ATV_TOLL_RECEIVED] += amount; veh_month[tt][0][ATV_TOLL_RECEIVED] += amount; account_balance += amount; } /** * Makes stats of amount of transported passenger, mail and goods * @param amount sum of money * @param wt way type * @param index 0 = passenger, 1 = mail, 2 = goods */ inline void book_transported(const sint64 amount, const waytype_t wt, int index) { const transport_type tt = translate_waytype_to_tt(wt); // there are: passenger, mail, goods if( (index < 0) || (index > 2)){ index = 2; } veh_year[ tt][0][ATV_TRANSPORTED_PASSENGER+index] += amount; veh_month[tt][0][ATV_TRANSPORTED_PASSENGER+index] += amount; } /** * Makes stats of amount of delivered passenger, mail and goods * @param amount sum of money * @param wt way type * @param index 0 = passenger, 1 = mail, 2 = goods */ inline void book_delivered(const sint64 amount, const waytype_t wt, int index) { const transport_type tt = translate_waytype_to_tt(wt); // there are: passenger, mail, goods if( (index < 0) || (index > 2)){ index = 2; } veh_year[ tt][0][ATV_DELIVERED_PASSENGER+index] += amount; veh_month[tt][0][ATV_DELIVERED_PASSENGER+index] += amount; } /** * Calculates the finance history for player. * This method has to be called before reading any variables besides account_balance! */ void calc_finance_history(); /** * @returns amount of money on account (also known as konto) */ inline sint64 get_account_balance() { return account_balance; } /** * Books amount of money to account (also known as konto) */ void book_account(sint64 amount) { account_balance += amount; com_month[0][ATC_CASH] = account_balance; com_year [0][ATC_CASH] = account_balance; com_month[0][ATC_NETWEALTH] += amount; com_year [0][ATC_NETWEALTH] += amount; // BUG profit is not adjusted when calling this method } /** * Returns the finance history (indistinguishable part) for player. * Call calc_finance_history before use! * @param year 0 .. current year, 1 .. last year, etc * @param type one of accounting_type_common */ sint64 get_history_com_year(int year, int type) const { return com_year[year][type]; } sint64 get_history_com_month(int month, int type) const { return com_month[month][type]; } /** * Returns the finance history (distinguishable by type of transport) for player. * Call calc_finance_history before use! * @param tt one of transport_type * @param year 0 .. current year, 1 .. last year, etc * @param type one of accounting_type_vehicles */ sint64 get_history_veh_year(transport_type tt, int year, int type) const { return veh_year[tt][year][type]; } sint64 get_history_veh_month(transport_type tt, int month, int type) const { return veh_month[tt][month][type]; } /** * @return how much month we have been in red numbers (= we had negative account balance) */ sint32 get_account_overdrawn() const { return account_overdrawn; } /** * @returns maintenance * @param tt transport type (Truck, Ship Aircraft, ...) */ sint64 get_maintenance(transport_type tt=TT_ALL) const { assert(tt 0); } /** * returns TRUE if net wealth > 0 (but this of course requires that we keep netwealth up to date!) */ bool has_money_or_assets() const { return ( get_netwealth() > 0 ); } /** * increases number of month for which the company is in red numbers */ void increase_account_overdrawn() { account_overdrawn++; } /** * returns true if company is bankrupt */ bool is_bancrupted() const; /** * Called at beginning of new month. */ void new_month(); /** * rolls the finance history for player (needed when new_year() or new_month()) triggered */ void roll_history_year(); void roll_history_month(); /** * loads or saves finance statistic */ void rdwr(loadsave_t *file); /// loads statistics of old versions void rdwr_compatibility(loadsave_t *file); /** * Sets account balance. This method enables to load old game format. * Do NOT use it in any other places! */ void set_account_balance(sint64 amount) { account_balance = amount; } void set_assets(const sint64 (&assets)[TT_MAX]); /** * Sets number of months for that the account balance is below zero. This method enables to load old game format. * Do NOT use it in any other places for any other purpose! */ void set_account_overdrawn(sint32 num) { account_overdrawn = num; } void set_starting_money(sint64 amount) { starting_money = amount; } /** * Translates waytype_t to transport_type */ static transport_type translate_waytype_to_tt(waytype_t wt); // to tranlate back to strings for finances GUI static const char* transport_type_values[TT_MAX]; void update_assets(sint64 delta, waytype_t wt); private: /** * Translates finance statistics from new format to old one. * Used for saving data in old format */ void export_to_cost_month(sint64 finance_history_month[][OLD_MAX_PLAYER_COST]); void export_to_cost_year( sint64 finance_history_year[][OLD_MAX_PLAYER_COST]); /** * Translates finance statistics from old format to new one. * Used for loading data from old format */ void import_from_cost_month(const sint64 finance_history_month[][OLD_MAX_PLAYER_COST]); void import_from_cost_year( const sint64 finance_history_year[][OLD_MAX_PLAYER_COST]); }; #endif simutrans-124.5/src/simutrans/player/simplay.cc000066400000000000000000000647731520675460500217020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "../simconvoi.h" #include "../simdebug.h" #include "../obj/depot.h" #include "../simhalt.h" #include "../simintr.h" #include "../simline.h" #include "../simmesg.h" #include "../simsound.h" #include "../simticker.h" #include "../tool/simtool.h" #include "../gui/simwin.h" #include "../world/simworld.h" #include "../display/viewport.h" #include "../builder/brueckenbauer.h" #include "../builder/hausbauer.h" #include "../builder/tunnelbauer.h" #include "../descriptor/tunnel_desc.h" #include "../descriptor/way_desc.h" #include "../ground/grund.h" #include "../dataobj/settings.h" #include "../dataobj/scenario.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../network/network_socket_list.h" #include "../obj/bruecke.h" #include "../obj/gebaeude.h" #include "../obj/leitung2.h" #include "../obj/tunnel.h" #include "../obj/roadsign.h" #include "../gui/messagebox.h" #include "../gui/player_frame.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" #include "../vehicle/air_vehicle.h" #include "simplay.h" #include "finance.h" karte_ptr_t player_t::welt; #ifdef MULTI_THREAD #include "../utils/simthread.h" static pthread_mutex_t load_mutex = PTHREAD_MUTEX_INITIALIZER; #endif player_t::player_t(uint8 nr) : finance(new finance_t(this, welt)), player_age(0), player_color_1(0), player_color_2(0), player_nr(nr), active(false), locked(false), unlock_pending(false), simlinemgmt(), undo_type(invalid_wt), headquarter_level(0), headquarter_pos(koord::invalid) { welt->get_settings().set_player_color_to_default(this); // we have different AI, try to find out our type: sprintf(player_name_buf,"player %i",player_nr-1); } player_t::~player_t() { while( !messages.empty() ) { delete messages.remove_first(); } destroy_win(magic_finances_t + get_player_nr()); delete finance; finance = NULL; } void player_t::book_construction_costs(player_t * const player, const sint64 amount, const koord k, const waytype_t wt) { if(player!=NULL) { player->finance->book_construction_costs(amount, wt); if(k != koord::invalid) { player->add_money_message(amount, k); } } } sint64 player_t::add_maintenance(sint64 change, waytype_t const wt) { #ifdef MULTI_THREAD pthread_mutex_lock( &load_mutex ); #endif const sint64 tmp = finance->book_maintenance(change, wt); #ifdef MULTI_THREAD pthread_mutex_unlock( &load_mutex ); #endif return tmp; } void player_t::add_money_message(const sint64 amount, const koord pos) { if(amount != 0 && player_nr != 1) { const scr_size screen = gfx->get_screen_size(); if( koord_distance(welt->get_viewport()->get_world_position(),pos)<2*(uint32)(screen.w / gfx->get_tile_raster_width())+3 ) { // only display, if near the screen ... add_message(amount, pos); // and same for sound too ... if( amount>=10000 && !welt->is_fast_forward() ) { welt->play_sound_area_clipped(pos, SFX_CASH, CASH_SOUND ); } } } } void player_t::book_new_vehicle(const sint64 amount, const koord k, const waytype_t wt) { finance->book_new_vehicle(amount, wt); add_money_message(amount, k); } void player_t::book_revenue(const sint64 amount, const koord k, const waytype_t wt, sint32 index) { finance->book_revenue(amount, wt, index); add_money_message(amount, k); } void player_t::book_running_costs(const sint64 amount, const waytype_t wt) { finance->book_running_costs(amount, wt); } void player_t::book_toll_paid(const sint64 amount, const waytype_t wt) { finance->book_toll_paid(amount, wt); } void player_t::book_toll_received(const sint64 amount, const waytype_t wt) { finance->book_toll_received(amount, wt); } void player_t::book_transported(const sint64 amount, const waytype_t wt, int index) { finance->book_transported(amount, wt, index); } void player_t::book_delivered(const sint64 amount, const waytype_t wt, int index) { finance->book_delivered(amount, wt, index); } const char* player_t::get_name() const { return translator::translate(player_name_buf); } void player_t::set_name(const char *new_name) { tstrncpy( player_name_buf, new_name, lengthof(player_name_buf) ); // update player window if (ki_kontroll_t *frame = dynamic_cast( win_get_magic(magic_ki_kontroll_t) ) ) { frame->update_data(); } } player_t::income_message_t::income_message_t( sint64 betrag, koord p ) { money_to_string(str, betrag/100.0); alter = 127; pos = p; amount = betrag; } void *player_t::income_message_t::operator new(size_t /*s*/) { return freelist_t::gimme_node(sizeof(player_t::income_message_t)); } void player_t::income_message_t::operator delete(void *p) { freelist_t::putback_node(sizeof(player_t::income_message_t),p); } void player_t::display_messages() { const viewport_t *vp = welt->get_viewport(); for(income_message_t* const m : messages) { const scr_coord scr_pos = vp->get_screen_coord(koord3d(m->pos,welt->lookup_hgt(m->pos)),koord(0,m->alter >> 4)) + scr_coord((gfx->get_tile_raster_width()-gfx->calc_text_width_n(m->str, 0x7FFF))/2,0); gfx->draw_text_shadowed( scr_pos.x, scr_pos.y, PLAYER_FLAG|gfx->palette_lookup(player_color_1+3), gfx->palette_lookup(COL_BLACK), m->str, true); if( m->pos.x < 3 || m->pos.y < 3 ) { // very close to border => renew background welt->set_background_dirty(); } } } void player_t::age_messages(uint32 /*delta_t*/) { for(slist_tpl::iterator iter = messages.begin(); iter != messages.end(); ) { income_message_t *m = *iter; m->alter -= 5; if(m->alter<-80) { iter = messages.erase(iter); delete m; } else { ++iter; } } } void player_t::add_message(sint64 betrag, koord k) { if( !messages.empty() && messages.back()->pos==k && messages.back()->alter==127 ) { // last message exactly at same place, not aged messages.back()->amount += betrag; money_to_string(messages.back()->str, messages.back()->amount/100.0); } else { // otherwise new message income_message_t *m = new income_message_t(betrag,k); messages.append( m ); } } void player_t::set_player_color(uint8 col1, uint8 col2) { player_color_1 = col1; player_color_2 = col2; gfx->set_player_color_scheme(player_nr, col1, col2); // update player window if (ki_kontroll_t* frame = dynamic_cast(win_get_magic(magic_ki_kontroll_t))) { frame->update_data(); } } void player_t::step() { } bool player_t::new_month() { // since the messages must remain on the screen longer ... static cbuffer_t buf; finance->new_month(); // new month has started => recalculate vehicle value calc_assets(); finance->calc_finance_history(); // Bankrupt ? if( finance->get_account_balance() < 0 ) { finance->increase_account_overdrawn(); if( !welt->get_settings().is_freeplay() && player_nr != 1 ) { if( welt->get_active_player_nr()==player_nr && !env_t::networkmode ) { if( finance->get_netwealth() < 0 ) { destroy_all_win(true); create_win({ gfx->get_screen_size().w/2-128, 40 }, new news_img("Bankrott:\n\nDu bist bankrott.\n"), w_info, magic_none); ticker::add_msg( translator::translate("Bankrott:\n\nDu bist bankrott.\n"), koord3d::invalid, PLAYER_FLAG | player_nr ); welt->stop(false); } else if( finance->get_netwealth()*10 < welt->get_settings().get_starting_money(welt->get_current_month()/12) ){ // tell the player (problem!) welt->get_message()->add_message( translator::translate("Net wealth less than 10%% of starting capital!"), koord3d::invalid, message_t::problems, PLAYER_FLAG | player_nr, IMG_EMPTY ); } else { // tell the player (just warning) buf.clear(); buf.printf( translator::translate("On loan since %i month(s)"), finance->get_account_overdrawn() ); welt->get_message()->add_message( buf, koord3d::invalid, message_t::ai, PLAYER_FLAG | player_nr, IMG_EMPTY ); } } // no assets => nothing to go bankrupt about again else if( finance->get_maintenance(TT_ALL) != 0 || finance->has_convoi() ) { // for AI, we only declare bankrupt, if total assets are below zero if( finance->get_netwealth() < 0 ) { return false; } // tell the current player (even during networkgames) if( welt->get_active_player_nr()==player_nr ) { if( finance->get_netwealth()*10 < welt->get_settings().get_starting_money(welt->get_current_month()/12) ){ // net wealth nearly spent (problem!) welt->get_message()->add_message( translator::translate("Net wealth near zero"), koord3d::invalid, message_t::problems, PLAYER_FLAG | player_nr, IMG_EMPTY ); } else { // just minus in account (just tell) buf.clear(); buf.printf( translator::translate("On loan since %i month(s)"), finance->get_account_overdrawn() ); welt->get_message()->add_message( buf, koord3d::invalid, message_t::ai, PLAYER_FLAG | player_nr, IMG_EMPTY ); } } } } } else { finance->set_account_overdrawn( 0 ); } if( env_t::networkmode && player_nr>1 && !active ) { // find out dummy companies (i.e. no vehicle running within x months) if( welt->get_settings().get_remove_dummy_player_months() && player_age >= welt->get_settings().get_remove_dummy_player_months() ) { bool no_cnv = true; const uint16 months = min( MAX_PLAYER_HISTORY_MONTHS, welt->get_settings().get_remove_dummy_player_months() ); for( uint16 m=0; mget_history_com_month(m, ATC_ALL_CONVOIS) ==0; } const uint16 years = max( MAX_PLAYER_HISTORY_YEARS, (welt->get_settings().get_remove_dummy_player_months() - 1) / 12 ); for( uint16 y=0; yget_history_com_year(y, ATC_ALL_CONVOIS)==0; } // never run a convoi => dummy if( no_cnv ) { return false; // remove immediately } } // find out abandoned companies (no activity within x months) if( welt->get_settings().get_unprotect_abandoned_player_months() && player_age >= welt->get_settings().get_unprotect_abandoned_player_months() ) { bool abandoned = true; const uint16 months = min( MAX_PLAYER_HISTORY_MONTHS, welt->get_settings().get_unprotect_abandoned_player_months() ); for( uint16 m = 0; m < months && abandoned; m++ ) { abandoned &= finance->get_history_veh_month(TT_ALL, m, ATV_NEW_VEHICLE)==0 && finance->get_history_veh_month(TT_ALL, m, ATV_CONSTRUCTION_COST)==0; } const uint16 years = min( MAX_PLAYER_HISTORY_YEARS, (welt->get_settings().get_unprotect_abandoned_player_months() - 1) / 12); for( uint16 y = 0; y < years && abandoned; y++ ) { abandoned &= finance->get_history_veh_year(TT_ALL, y, ATV_NEW_VEHICLE)==0 && finance->get_history_veh_year(TT_ALL, y, ATV_CONSTRUCTION_COST)==0; } // never changed convoi, never built => abandoned if( abandoned ) { if (env_t::server) { // server: unlock this player for all clients socket_list_t::unlock_player_all(player_nr, true); } // clients: local unlock pwd_hash.clear(); locked = false; unlock_pending = false; } } } // update line info simlinemgmt.new_month(); // subtract maintenance after bankruptcy check finance->book_account( -finance->get_maintenance_with_bits(TT_ALL) ); // company gets older ... player_age ++; return true; // still active } void player_t::calc_assets() { sint64 assets[TT_MAX]; for(int i=0; i < TT_MAX; ++i){ assets[i] = 0; } // all convois for(convoihandle_t const cnv : welt->convoys()) { if( cnv->get_owner() == this ) { sint64 restwert = cnv->calc_restwert(); assets[TT_ALL] += restwert; assets[finance->translate_waytype_to_tt(cnv->front()->get_desc()->get_waytype())] += restwert; } } // all vehicles stored in depot not part of a convoi for(depot_t* const depot : depot_t::get_depot_list()) { if( depot->get_owner_nr() == player_nr ) { for(vehicle_t* const veh : depot->get_vehicle_list()) { sint64 restwert = veh->calc_sale_value(); assets[TT_ALL] += restwert; assets[finance->translate_waytype_to_tt(veh->get_desc()->get_waytype())] += restwert; } } } finance->set_assets(assets); } void player_t::update_assets(sint64 const delta, const waytype_t wt) { finance->update_assets(delta, wt); } sint32 player_t::get_scenario_completion() const { return finance->get_scenario_completed(); } void player_t::set_scenario_completion(sint32 percent) { finance->set_scenario_completed(percent); } bool player_t::check_owner( const player_t *owner, const player_t *test ) { return owner == test || owner == NULL || test == welt->get_public_player(); } void player_t::ai_bankrupt() { player_t *const psplayer = welt->get_public_player(); DBG_MESSAGE("player_t::ai_bankrupt()","Removing convois"); for (size_t i = welt->convoys().get_count(); i-- != 0;) { convoihandle_t const cnv = welt->convoys()[i]; if(cnv->get_owner()!=this) { continue; } linehandle_t line = cnv->get_line(); cnv->self_destruct(); // convois not in depots must step to really get rid of them if( cnv.is_bound() && cnv->get_state() != convoi_t::INITIAL ) { cnv->step(); } // last vehicle on that connection (no line => railroad) if( !line.is_bound() || line->count_convoys()==0 ) { simlinemgmt.delete_line( line ); } } // remove headquarters pos headquarter_pos = koord::invalid; // remove all stops // first generate list of our stops slist_tpl halt_list; for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if( halt->get_owner()==this ) { halt_list.append(halt); } } // ... and destroy them while (!halt_list.empty()) { halthandle_t h = halt_list.remove_first(); haltestelle_t::destroy( h ); } // transfer all ways in public stops belonging to me to no one for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if( halt->get_owner()==welt->get_public_player() ) { // only concerns public stops tiles for(haltestelle_t::tile_t const& i : halt->get_tiles()) { grund_t const* const gr = i.grund; for( uint8 wnr=0; wnr<2; wnr++ ) { weg_t *w = gr->get_weg_nr(wnr); // make public if( w && w->get_owner()==this ) { // tunnels and bridges are handled later? (logic needs to be checked for correct maintenance costs) if (!gr->ist_bruecke() && !gr->ist_tunnel()) { sint32 const costs = w->get_desc()->get_maintenance(); waytype_t const wt = w->get_desc()->get_finance_waytype(); player_t::add_maintenance(this, -costs, wt); player_t::add_maintenance(psplayer, costs, wt); } w->set_owner(psplayer); } } } } } // deactivate active tool (remove dummy grounds) welt->set_tool(tool_t::general_tool[TOOL_QUERY], this); // next remove all ways, depot etc, that are not road or channels for( int y=0; yget_size().y; y++ ) { for( int x=0; xget_size().x; x++ ) { planquadrat_t *plan = welt->access(x,y); for (size_t b = plan->get_boden_count(); b-- != 0;) { grund_t *gr = plan->get_boden_bei(b); // remove tunnel and bridges first if( gr->obj_count()>0 && gr->obj_bei(0)->get_owner()==this && (gr->ist_bruecke() || gr->ist_tunnel()) ) { koord3d pos = gr->get_pos(); waytype_t wt = gr->hat_wege() ? gr->get_weg_nr(0)->get_waytype() : powerline_wt; if (gr->ist_bruecke()) { bridge_builder_t::remove( this, pos, wt ); // fails if powerline bridge somehow connected to powerline bridge of another player } else { tunnel_builder_t::remove( this, pos, wt, true ); } // maybe there are some objects left (station on bridge head etc) gr = plan->get_boden_in_hoehe(pos.z); if (gr == NULL) { continue; } } for (uint8 i = gr->obj_count(); i-- != 0;) { obj_t *obj = gr->obj_bei(i); if(obj->get_owner()==this) { sint32 costs = 0; waytype_t wt = ignore_wt; switch(obj->get_typ()) { case obj_t::signal: case obj_t::airdepot: case obj_t::bahndepot: case obj_t::monoraildepot: case obj_t::tramdepot: case obj_t::strassendepot: case obj_t::schiffdepot: case obj_t::senke: case obj_t::pumpe: case obj_t::wayobj: case obj_t::label: obj->cleanup(this); delete obj; break; case obj_t::leitung: // do not remove powerline from bridges if(gr->ist_bruecke()) { costs = ((leitung_t*)obj)->get_desc()->get_maintenance(); add_maintenance(-costs, powerline_wt); psplayer->add_maintenance(costs, powerline_wt); obj->set_owner(psplayer); } else { obj->cleanup(this); delete obj; } break; case obj_t::gebaeude: hausbauer_t::remove( this, (gebaeude_t *)obj ); gr = plan->get_boden_bei(b); // fundament has now been replaced by normal ground break; case obj_t::way: { weg_t *w=(weg_t *)obj; // tunnels and bridges made public if (gr->ist_bruecke() || gr->ist_tunnel()) { w->set_owner(psplayer); } // roads and water ways also made public else if(w->get_waytype()==road_wt || w->get_waytype()==water_wt) { costs = w->get_desc()->get_maintenance(); wt = w->get_waytype(); add_maintenance(-costs, wt); psplayer->add_maintenance(costs, wt); w->set_owner(psplayer); } else { gr->weg_entfernen( w->get_waytype(), true ); } break; } case obj_t::bruecke: costs = ((bruecke_t*)obj)->get_desc()->get_maintenance(); wt = obj->get_waytype(); add_maintenance(-costs, wt); psplayer->add_maintenance(costs, wt); obj->set_owner(psplayer); break; case obj_t::tunnel: costs = ((tunnel_t*)obj)->get_desc()->get_maintenance(); wt = ((tunnel_t*)obj)->get_desc()->get_finance_waytype(); add_maintenance(-costs, wt); psplayer->add_maintenance(costs, wt); obj->set_owner(psplayer); break; case obj_t::roadsign: { costs = ((roadsign_t *)obj)->get_desc()->get_maintenance(); wt = ((roadsign_t *)obj)->get_desc()->get_waytype(); // take care of normal rail signals on tram track if (wt == track_wt && gr->get_weg(tram_wt)) { wt = tram_wt; } this->add_maintenance(-costs, wt); psplayer->add_maintenance(costs, wt); obj->set_owner(psplayer); break; } default: obj->set_owner(psplayer); } } } // remove empty tiles (elevated ways) if (!gr->ist_karten_boden() && gr->obj_count()==0) { plan->boden_entfernen(gr); } } } } active = false; // make account negative if (finance->get_account_balance() > 0) { finance->book_account( -finance->get_account_balance() -1 ); } cbuffer_t buf; buf.printf( translator::translate("%s\nwas liquidated."), get_name() ); welt->get_message()->add_message( buf, koord3d::invalid, message_t::ai, PLAYER_FLAG|player_nr ); } void player_t::rdwr(loadsave_t *file) { xml_tag_t sss( file, "spieler_t" ); if(file->is_version_less(112, 5)) { sint64 konto = finance->get_account_balance(); file->rdwr_longlong(konto); finance->set_account_balance(konto); sint32 account_overdrawn = finance->get_account_overdrawn(); file->rdwr_long(account_overdrawn); finance->set_account_overdrawn( account_overdrawn ); } if(file->is_version_less(101, 0)) { // ignore steps sint32 ldummy=0; file->rdwr_long(ldummy); } if(file->is_version_less(99, 9)) { sint32 farbe; file->rdwr_long(farbe); player_color_1 = (uint8)farbe*2; player_color_2 = player_color_1+24; } else { file->rdwr_byte(player_color_1); file->rdwr_byte(player_color_2); } sint32 halt_count=0; if(file->is_version_less(99, 8)) { file->rdwr_long(halt_count); } if(file->is_version_less(112, 3)) { sint32 haltcount = 0; file->rdwr_long(haltcount); } // save all the financial statistics finance->rdwr( file ); file->rdwr_bool(active); // state is not saved anymore if(file->is_version_less(99, 14)) { sint32 ldummy=0; file->rdwr_long(ldummy); file->rdwr_long(ldummy); } // the AI stuff is now saved directly by the different AI if( file->is_version_less(101, 0)) { sint32 ldummy = -1; file->rdwr_long(ldummy); file->rdwr_long(ldummy); file->rdwr_long(ldummy); file->rdwr_long(ldummy); koord k(-1,-1); k.rdwr( file ); k.rdwr( file ); } if(file->is_loading()) { // halt_count will be zero for newer savegames DBG_DEBUG("player_t::rdwr()","player %i: loading %i halts.",welt->sp2num( this ),halt_count); for(int i=0; iis_version_less(86, 4)) { headquarter_level = 0; headquarter_pos = koord::invalid; } else { file->rdwr_long(headquarter_level); headquarter_pos.rdwr( file ); if(file->is_loading()) { if(headquarter_level<0) { headquarter_pos = koord::invalid; headquarter_level = 0; } } } // line management if(file->is_version_atleast(88, 3)) { simlinemgmt.rdwr(file,this); } if(file->is_version_atleast(102, 3)) { // password hash for( int i=0; i<20; i++ ) { file->rdwr_byte(pwd_hash[i]); } if( file->is_loading() ) { // disallow all actions, if password set (might be unlocked by password gui ) locked = !pwd_hash.empty(); } } // save the name too if( file->is_version_atleast(102, 4) ) { file->rdwr_str( player_name_buf, lengthof(player_name_buf) ); } // save age if( file->is_version_atleast(112, 2) ) { file->rdwr_short( player_age ); } } void player_t::finish_rd() { simlinemgmt.finish_rd(); gfx->set_player_color_scheme(player_nr, player_color_1, player_color_2); // recalculate vehicle value calc_assets(); finance->calc_finance_history(); } void player_t::rotate90( const sint16 y_size ) { simlinemgmt.rotate90( y_size ); headquarter_pos.rotate90( y_size ); } void player_t::report_vehicle_problem(convoihandle_t cnv,const koord3d position) { switch(cnv->get_state()) { case convoi_t::NO_ROUTE: DBG_MESSAGE("player_t::report_vehicle_problem","Vehicle %s can't find a route to (%i,%i)!", cnv->get_name(),position.x,position.y); { cbuffer_t buf; buf.printf( translator::translate("Vehicle %s can't find a route!"), cnv->get_name()); welt->get_message()->add_message( (const char *)buf, cnv->get_pos(), message_t::problems, PLAYER_FLAG | player_nr, cnv->front()->get_base_image()); } break; case convoi_t::WAITING_FOR_CLEARANCE_ONE_MONTH: case convoi_t::CAN_START_ONE_MONTH: case convoi_t::CAN_START_TWO_MONTHS: DBG_MESSAGE("player_t::report_vehicle_problem","Vehicle %s stuck!", cnv->get_name(),position.x,position.y); { cbuffer_t buf; buf.printf( translator::translate("Vehicle %s is stucked!"), cnv->get_name()); welt->get_message()->add_message( (const char *)buf, cnv->get_pos(), message_t::warnings, PLAYER_FLAG | player_nr, cnv->front()->get_base_image()); } break; default: DBG_MESSAGE("player_t::report_vehicle_problem","Vehicle %s, state %i!", cnv->get_name(), cnv->get_state()); } (void)position; } void player_t::init_undo( waytype_t wtype, unsigned short max ) { // only human player // allow for UNDO for real player DBG_MESSAGE("player_t::int_undo()","undo tiles %i",max); last_built.clear(); last_built.reserve(max+1); if(max>0) { undo_type = wtype; } } void player_t::add_undo(koord3d k) { if(last_built.get_capacity()>0) { //DBG_DEBUG("player_t::add_undo()","tile at (%i,%i)",k.x,k.y); last_built.append(k); } } sint64 player_t::undo() { if (last_built.empty()) { // nothing to UNDO return false; } // check, if we can still do undo for(koord3d const& i : last_built) { grund_t* const gr = welt->lookup(i); if(gr==NULL || gr->get_typ()!=grund_t::boden) { // well, something was built here ... so no undo last_built.clear(); return false; } // we allow ways, unimportant stuff but no vehicles, signals, wayobjs etc if(gr->obj_count()>0) { for( unsigned i=0; iobj_count(); i++ ) { switch(gr->obj_bei(i)->get_typ()) { // these are allowed case obj_t::zeiger: case obj_t::cloud: case obj_t::leitung: case obj_t::pillar: case obj_t::way: case obj_t::label: case obj_t::crossing: case obj_t::pedestrian: case obj_t::road_user: case obj_t::movingobj: break; // special case airplane // they can be everywhere, so we allow for everything but runway undo case obj_t::air_vehicle: { if(undo_type!=air_wt) { break; } const air_vehicle_t* aircraft = obj_cast(gr->obj_bei(i)); // flying aircrafts are ok if(!aircraft->is_on_ground()) { break; } } /* FALLTHROUGH */ // all other are forbidden => no undo any more default: last_built.clear(); return false; } } } } // ok, now remove everything last built sint64 cost=0; for(koord3d const& i : last_built) { grund_t* const gr = welt->lookup(i); if( undo_type != powerline_wt ) { cost += gr->weg_entfernen(undo_type,true); } else { if (leitung_t* lt = gr->get_leitung()) { cost += lt->get_desc()->get_price(); lt->cleanup(NULL); delete lt; } } } last_built.clear(); return cost; } void player_t::tell_tool_result(tool_t *tool, koord3d, const char *err) { /* tools can return three kinds of messages * NULL = success * "" = failure, but just do not try again * "bla" error message, which should be shown */ if (welt->get_active_player()==this) { if(err==NULL) { if(tool->ok_sound!=NO_SOUND) { sound_play(tool->ok_sound,255,TOOL_SOUND); } } else if(*err!=0) { // something went really wrong sound_play( SFX_FAILURE, 255, TOOL_SOUND ); // look for coordinate in error message // syntax: either @x,y or (x,y) open_error_msg_win(err); } } } void player_t::book_convoi_number(int count) { finance->book_convoi_number(count); } double player_t::get_account_balance_as_double() const { return finance->get_account_balance() / 100.0; } int player_t::get_account_overdrawn() const { return finance->get_account_overdrawn(); } bool player_t::has_money_or_assets() const { return finance->has_money_or_assets(); } bool player_t::can_afford(sint64 cost) const { return welt->get_settings().is_freeplay() || is_public_service() || get_finance()->get_netwealth() >= -cost; } bool player_t::is_public_service() const { return get_player_nr() == PLAYER_PUBLIC_NR; } simutrans-124.5/src/simutrans/player/simplay.h000066400000000000000000000227061520675460500215320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef PLAYER_SIMPLAY_H #define PLAYER_SIMPLAY_H #include "../utils/sha1_hash.h" #include "../simtypes.h" #include "../simlinemgmt.h" #include "../halthandle.h" #include "../convoihandle.h" #include "../dataobj/koord.h" #include "../tpl/slist_tpl.h" #include "../tpl/vector_tpl.h" class karte_ptr_t; class fabrik_t; class koord3d; class tool_t; class finance_t; #define PLAYER_HUMAN_NR (0) #define PLAYER_PUBLIC_NR (1) /** * Class to hold information about one player/company. AI players are derived from this class. */ class player_t { public: enum { EMPTY = 0, HUMAN = 1, AI_GOODS = 2, AI_PASSENGER = 3, AI_SCRIPTED = 4, MAX_AI, PASSWORD_PROTECTED = 128 }; protected: char player_name_buf[256]; /* "new" finance history */ finance_t *finance; /** * Die Welt in der gespielt wird. */ static karte_ptr_t welt; // when was the company founded uint16 player_age; /** * Floating massages for all players here */ class income_message_t { public: char str[33]; koord pos; sint64 amount; sint8 alter; income_message_t() { str[0]=0; alter=127; pos=koord::invalid; amount=0; } income_message_t( sint64 betrag, koord pos ); void * operator new(size_t s); void operator delete(void *p); }; slist_tpl messages; /** * Creates new income message entry or merges with existing one if the * most recent one is at the same coordinate */ void add_message(sint64 amount, koord k); /** * Displays amount of money when koordinates are on screen */ void add_money_message(sint64 amount, koord k); /** * Colors of the player */ uint8 player_color_1, player_color_2; /** * Player number; only player 0 can do interaction */ uint8 player_nr; /** * Adds some amount to the maintenance costs. * * @param change the change * @param wt * @return the new maintenance costs */ sint64 add_maintenance(sint64 change, waytype_t const wt=ignore_wt); /** * Is this player an AI player? */ bool active; /** * Are this player allowed to do any changes? */ bool locked; bool unlock_pending; // contains the password hash for local games pwd_hash_t pwd_hash; public: /** * Sums up "count" with number of convois in statistics, * supersedes buche( count, COST_ALL_CONVOIS). */ void book_convoi_number(int count); /** * Adds construction costs to accounting statistics. * * @param player * @param amount How much does it cost * @param k * @param wt type of transport */ static void book_construction_costs(player_t * const player, const sint64 amount, const koord k, const waytype_t wt=ignore_wt); /** * Accounts bought/sold vehicles. * * @param price money used for purchase of vehicle, * negative value = vehicle bought, * positive value = vehicle sold * @param k * @param wt type of transport for accounting purpose */ void book_new_vehicle(const sint64 price, const koord k, const waytype_t wt=ignore_wt); /** * Adds income to accounting statistics. * * @param amount earned money * @param k * @param wt transport type used in accounting statistics * @param cathegory parameter * 0 ... passenger * 1 ... mail * 2 ... good (and powerlines revenue) */ void book_revenue(const sint64 amount, const koord k, const waytype_t wt=ignore_wt, sint32 cathegory=2); /** * Adds running costs to accounting statistics. * @param amount How much does it cost * @param wt type of transport used for accounting statistics */ void book_running_costs(const sint64 amount, const waytype_t wt=ignore_wt); /** * Books toll paid by our company to someone else. * @param amount money paid to our company * @param wt type of transport used for accounting statistics */ void book_toll_paid(const sint64 amount, const waytype_t wt=ignore_wt); /** * Books toll paid to our company by someone else. * @param amount money paid for usage of our roads,railway,channels, ... ; positive sign * @param wt type of transport used for accounting statistics */ void book_toll_received(const sint64 amount, waytype_t wt=ignore_wt); /** * Add amount of transported passenger, mail, goods to accounting statistics. * @param amount sum of money * @param wt way type * @param index 0 = passenger, 1 = mail, 2 = goods */ void book_transported(const sint64 amount, const waytype_t wt=ignore_wt, int index=2); /** * Add amount of delivered passenger, mail, goods to accounting statistics. * @param amount sum of money * @param wt way type * @param index 0 = passenger, 1 = mail, 2 = goods */ void book_delivered(const sint64 amount, const waytype_t wt=ignore_wt, int index=2); bool has_money_or_assets() const; /** * Test if the player has sufficient funds for an action. * Returns true if the player has the funds or does not need to. * @param cost the amount of funds that want to be added to the balance */ bool can_afford(sint64 cost) const; finance_t * get_finance() const { return finance; } virtual bool set_active( bool b ) { return active = b; } bool is_active() const { return active; } bool is_locked() const { return locked; } bool is_unlock_pending() const { return unlock_pending; } void unlock(bool unlock_, bool unlock_pending_=false) { locked = !unlock_; unlock_pending = unlock_pending_; } void check_unlock( const pwd_hash_t& hash ) { locked = (pwd_hash != hash); } // some routine needs this for direct manipulation pwd_hash_t& access_password_hash() { return pwd_hash; } // this type of AIs identifier virtual uint8 get_ai_id() const { return HUMAN; } simlinemgmt_t simlinemgmt; /// Age messages (move them upwards) void age_messages(uint32 delta_t); /// Handles player colors uint8 get_player_color1() const { return player_color_1; } uint8 get_player_color2() const { return player_color_2; } void set_player_color(uint8 col1, uint8 col2); /** * @return the name of the player; "player -1" sits in front of the screen */ const char* get_name() const; void set_name(const char *); sint8 get_player_nr() const {return player_nr; } /** * Test if this player is a public service player. * @return true if the player is a public service player, otherwise false. */ bool is_public_service() const; /** * return true, if the owner is none, myself or player(1), i.e. the ownership can be taken by player test */ static bool check_owner( const player_t *owner, const player_t *test ); /** * @param player_nr Number assigned to this player - it's an ID. */ player_t(uint8 player_nr ); virtual ~player_t(); static inline sint64 add_maintenance(player_t *player, sint64 const change, waytype_t const wt=ignore_wt) { if(player) { return player->add_maintenance(change, wt); } return 0; } /** * Cached value of scenario completion percentage. * To get correct values for clients call scenario_t::get_completion instead. */ sint32 get_scenario_completion() const; void set_scenario_completion(sint32 percent); /** * @return Account balance as a double (floating point) value */ double get_account_balance_as_double() const; /** * @return true when account balance is overdrawn */ int get_account_overdrawn() const; /** * Displays messages from the queue of the player on the screen * Show income messages */ void display_messages(); /** * Called often by simworld.cc during simulation * @note Any action goes here (only need for AI at the moment) */ virtual void step(); /** * Called monthly by simworld.cc during simulation * @returns false if player has to be removed (bankrupt/inactive) */ virtual bool new_month(); /** * Called yearly by simworld.cc during simulation */ virtual void new_year() {} /** * Stores/loads the player state * @param file where the data will be saved/loaded */ virtual void rdwr(loadsave_t *file); /* * Called after game is fully loaded; */ virtual void finish_rd(); virtual void rotate90( const sint16 y_size ); /** * Calculates the assets of the player */ void calc_assets(); /** * Updates the assets value of the player */ void update_assets(sint64 const delta, const waytype_t wt = ignore_wt); /** * Report the player one of his vehicles has a problem */ virtual void report_vehicle_problem(convoihandle_t cnv,const koord3d position); /** * Tells the player the result of tool-work commands. * If player is active then play sound, popup error msg etc. */ void tell_tool_result(tool_t *tool, koord3d pos, const char *err); /** * Tells the player that the factory * is going to be deleted (flag==0) */ enum notification_factory_t { notify_delete // notified immediately before object is deleted (and before nulled in the slist_tpl<>)! }; virtual void notify_factory(notification_factory_t, const fabrik_t*) {} private: /// undo information vector_tpl last_built; waytype_t undo_type; public: /** * Function for UNDO */ void init_undo(waytype_t t, unsigned short max ); /** * Function for UNDO */ void add_undo(koord3d k); /** * Function for UNDO */ sint64 undo(); private: // headquarters stuff sint32 headquarter_level; koord headquarter_pos; public: void add_headquarter(short hq_level, koord hq_pos) { headquarter_level = hq_level; headquarter_pos = hq_pos; } koord get_headquarter_pos() const { return headquarter_pos; } short get_headquarter_level() const { return headquarter_level; } void ai_bankrupt(); }; #endif simutrans-124.5/src/simutrans/script/000077500000000000000000000000001520675460500177045ustar00rootroot00000000000000simutrans-124.5/src/simutrans/script/api/000077500000000000000000000000001520675460500204555ustar00rootroot00000000000000simutrans-124.5/src/simutrans/script/api/api.h000066400000000000000000000020501520675460500213740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_API_H #define SCRIPT_API_API_H /** @file api.h declarations of export functions. */ #include "../../../squirrel/squirrel.h" #include "api_command.h" void export_city(HSQUIRRELVM vm); void export_control(HSQUIRRELVM vm); void export_convoy(HSQUIRRELVM vm); void export_factory(HSQUIRRELVM vm); void export_goods_desc(HSQUIRRELVM vm); void export_gui(HSQUIRRELVM vm, bool scenario); void export_halt(HSQUIRRELVM vm); void export_line(HSQUIRRELVM vm); void export_map_objects(HSQUIRRELVM vm); void export_player(HSQUIRRELVM vm, bool scenario); void export_scenario(HSQUIRRELVM vm); void export_settings(HSQUIRRELVM vm); void export_schedule(HSQUIRRELVM vm); void export_simple(HSQUIRRELVM vm); void export_string_methods(HSQUIRRELVM vm); // api_scenario.cc void export_tiles(HSQUIRRELVM vm); void export_world(HSQUIRRELVM vm, bool scenario); void export_pathfinding(HSQUIRRELVM vm); void export_global_constants(HSQUIRRELVM vm); #endif simutrans-124.5/src/simutrans/script/api/api_base.h000066400000000000000000000041601520675460500223720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ // This file should not be compiled. It wont compile either. /** @file api_base.h documents base classes that are defined elsewhere. */ #ifdef DOXYGEN /** * Translatable text. * * Class takes raw string. It can do variable substitution. * * When transferred to simutrans: * -# the translation of the string will be fetched * -# variables will be substituted * * Translated strings will be taken from tab-files inside the scenario_name/ directory. * * Usage: * @code * local text = ttext("You have {N} trains running.") * text.N = 20; * // if returned to simutrans, the output will be "You have 20 trains running." * @endcode */ class ttext { // begin_class("ttext", 0) /** * Constructor. * @param text raw string to be translated. * @typemask (string) */ register_function(vm,, "constructor"); /** * Does translation and variable substitution. * @returns Translated string with all variables replaced by their values. * @typemask string() */ register_function("to_string"); }; // end_class /** * Loads text from a file. * * It does variable substitution as ttext. */ class ttextfile : public ttext { // begin_class("ttextfile", "ttext") /** * Constructor. Text is taken from a file. * * The file is searched in the following paths: * -# scenario_name/iso/ .. iso is language ISO abbreviation * -# scenario_name/en/ .. fall-back: English version * -# scenario_name/ * * @param file filename * @typemask (string) */ register_function(vm,, "constructor"); }; // end_class /** * Class that implements an extended get-method. * * If child class has method * @code * function get_something() * @endcode * then function result can be accessed by * @code * local result = some_instance.something * @endcode * as if the class has a member variable. * * * Example: * * @code * local player = player_x(0) * local cash1 = player.get_cash()[0] * local cash2 = player.cash[0] * // now cash1 == cash2 * @endcode */ class extend_get { // begin_class("extend_get", 0) }; // end_class #endif simutrans-124.5/src/simutrans/script/api/api_city.cc000066400000000000000000000210561520675460500225710ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_city.cc exports city related functions. */ #include "get_next.h" #include "../api_class.h" #include "../api_function.h" #include "../../world/simcity.h" #include "../../tool/simmenu.h" #include "../../world/simworld.h" #include "../../dataobj/scenario.h" using namespace script_api; vector_tpl const& get_city_stat(stadt_t* city, bool monthly, sint32 INDEX) { static vector_tpl v; v.clear(); if (city && 0<=INDEX && INDEXget_finance_history_month(i, INDEX) ); } else { v.append( city->get_finance_history_year(i, INDEX) ); } } } return v; } SQInteger world_get_next_city(HSQUIRRELVM vm) { return generic_get_next(vm, welt->get_cities().get_count()); } namespace script_api { declare_fake_param(city_list_t, "city_list_x"); } stadt_t* world_get_city_by_index(city_list_t, uint32 index) { return index < welt->get_cities().get_count() ? welt->get_cities()[index] : NULL; } call_tool_init set_citygrowth(stadt_t *city, bool allow) { cbuffer_t buf; buf.printf("g%hi,%hi,%hi", city->get_pos().x, city->get_pos().y, (short)allow ); return call_tool_init(TOOL_CHANGE_CITY | SIMPLE_TOOL, (const char*)buf, 0, welt->get_public_player()); } call_tool_init city_set_name(stadt_t* city, const char* name) { return command_rename(welt->get_public_player(), 't', welt->get_cities().index_of(city), name); } call_tool_work city_change_size(stadt_t *city, sint32 delta) { cbuffer_t buf; buf.printf("%i", delta); grund_t *gr = welt->lookup_kartenboden(city->get_pos()); if (gr) { return call_tool_work(TOOL_CHANGE_CITY_SIZE | GENERAL_TOOL, (const char*)buf, 0, welt->get_public_player(), gr->get_pos()); } else { return "Invalid coordinate."; } } void export_city(HSQUIRRELVM vm) { /** * Implements iterator to iterate through the list of all cities on the map. * * Usage: * @code * local list = city_list_x() * foreach(city in list) { * ... // city is an instance of the city_x class * } * @endcode */ begin_class(vm, "city_list_x", 0); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, world_get_next_city, "_nexti", 2, "x o|i"); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_method(vm, &world_get_city_by_index, "_get", true); end_class(vm); /** * Class to access cities. */ begin_class(vm, "city_x", "coord,extend_get,ingame_object"); /** * Constructor. * @param x x-coordinate * @param y y-coordinate * @typemask (integer,integer) */ // actually defined in simutrans/script/script_base.nut // register_function(..., "constructor", ...); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") /** * Return name of city * @returns name */ register_method(vm, &stadt_t::get_name, "get_name"); /** * Change city name. * @ingroup rename_func */ register_method(vm, &city_set_name, "set_name", true); /** * Get monthly statistics of number of citizens. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_city_stat, "get_citizens", freevariable(true, HIST_CITIZENS), true); /** * Get monthly statistics of number of city growth. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_city_stat, "get_growth", freevariable(true, HIST_GROWTH), true ); /** * Get monthly statistics of number of buildings. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_city_stat, "get_buildings", freevariable(true, HIST_BUILDING), true ); /** * Get monthly statistics of number of citycars started. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_city_stat, "get_citycars", freevariable(true, HIST_CITYCARS), true ); /** * Get monthly statistics of number of transported passengers. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_city_stat, "get_transported_pax", freevariable(true, HIST_PAS_TRANSPORTED), true ); /** * Get monthly statistics of number of generated passengers. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_city_stat, "get_generated_pax", freevariable(true, HIST_PAS_GENERATED), true ); /** * Get monthly statistics of number of transported mail. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_city_stat, "get_transported_mail", freevariable(true, HIST_MAIL_TRANSPORTED), true ); /** * Get monthly statistics of number of generated mail. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_city_stat, "get_generated_mail", freevariable(true, HIST_MAIL_GENERATED), true ); /** * Get per year statistics of number of citizens. * @returns array, index [0] corresponds to current year */ register_method_fv(vm, &get_city_stat, "get_year_citizens", freevariable(false, HIST_CITIZENS), true ); /** * Get per year statistics of number of city growth. * @returns array, index [0] corresponds to current year */ register_method_fv(vm, &get_city_stat, "get_year_growth", freevariable(false, HIST_GROWTH), true ); /** * Get per year statistics of number of buildings. * @returns array, index [0] corresponds to current year */ register_method_fv(vm, &get_city_stat, "get_year_buildings", freevariable(false, HIST_BUILDING), true ); /** * Get per year statistics of number of citycars started. * @returns array, index [0] corresponds to current year */ register_method_fv(vm, &get_city_stat, "get_year_citycars", freevariable(false, HIST_CITYCARS), true ); /** * Get per year statistics of number of transported passengers. * @returns array, index [0] corresponds to current year */ register_method_fv(vm, &get_city_stat, "get_year_transported_pax", freevariable(false, HIST_PAS_TRANSPORTED), true ); /** * Get per year statistics of number of generated passengers. * @returns array, index [0] corresponds to current year */ register_method_fv(vm, &get_city_stat, "get_year_generated_pax", freevariable(false, HIST_PAS_GENERATED), true ); /** * Get per year statistics of number of transported mail. * @returns array, index [0] corresponds to current year */ register_method_fv(vm, &get_city_stat, "get_year_transported_mail", freevariable(false, HIST_MAIL_TRANSPORTED), true ); /** * Get per year statistics of number of generated mail. * @returns array, index [0] corresponds to current year */ register_method_fv(vm, &get_city_stat, "get_year_generated_mail", freevariable(false, HIST_MAIL_GENERATED), true ); /** * Check city growth allowance. * @returns whether city growth is enabled for this city */ register_method(vm, &stadt_t::get_citygrowth, "get_citygrowth_enabled"); /** * Position of town-hall. * @returns town-hall position */ register_method(vm, &stadt_t::get_pos, "get_pos"); /** * City limits. * * City area is between get_pos_nw().x and get_pos_se().x, and get_pos_nw().y and get_pos_se().y. * * It is @b not guaranteed that get_pos_nw().x <= get_pos_se().x or get_pos_nw().y <= get_pos_se().y holds! * @returns coordinate of one corner of city limit */ register_method(vm, &stadt_t::get_linksoben, "get_pos_nw"); /** * City limits. * * City area is between get_pos_nw().x and get_pos_se().x, and get_pos_nw().y and get_pos_se().y. * * It is @b not guaranteed that get_pos_nw().x <= get_pos_se().x or get_pos_nw().y <= get_pos_se().y holds! * @returns coordinate of another corner of city limit */ register_method(vm, &stadt_t::get_rechtsunten, "get_pos_se"); /** * Change city size. City will immediately grow. * @param delta City size will change by this number. * @ingroup game_cmd */ register_method(vm, city_change_size, "change_size", true); /** * Enable or disable city growth. * @ingroup game_cmd */ register_method(vm, &set_citygrowth, "set_citygrowth_enabled", true); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_command.cc000066400000000000000000000521471520675460500232440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_command.cc exports the command_x class, which encodes the tools to manipulate a game */ #include "api_command.h" #include "api_obj_desc_base.h" #include "api_simple.h" #include "../api_param.h" #include "../api_class.h" #include "../api_function.h" #include "../../builder/brueckenbauer.h" #include "../../ground/grund.h" #include "../../tool/simtool.h" #include "../../world/simworld.h" #include "../../dataobj/environment.h" #include "../../script/script.h" #include "../../descriptor/bridge_desc.h" #include "../../descriptor/building_desc.h" #include "../../descriptor/roadsign_desc.h" #include "../../descriptor/way_obj_desc.h" #include // auto_ptr using namespace script_api; SQInteger command_constructor(HSQUIRRELVM vm) { // create tool uint16 id = param::get(vm, 2); if (id & GENERAL_TOOL) { // we do not want scripts to open dialogues or quitting the game etc if (id == (TOOL_EXEC_TWO_CLICK_SCRIPT | GENERAL_TOOL)) { // do not create & attach instance, will be done separately return 0; } else { if (tool_t *tool = create_tool(id)) { my_tool_t* mtool = new my_tool_t(tool); attach_instance(vm, 1, mtool); return 0; } } } return sq_raise_error(vm, "Invalid tool called (%d / 0x%x)", id & 0xfff, id); } SQInteger param::push(HSQUIRRELVM vm, call_tool_init v) { if (v.error) { return sq_raise_error(vm, *v.error ? v.error : "Strange error occurred"); } // create tool, if necessary, delete on exit std::unique_ptr our_tool; tool_t *tool = v.tool; if (tool == NULL) { our_tool.reset(v.create_tool()); tool = our_tool.get(); } if (tool == NULL) { return sq_raise_error(vm, "Called null tool"); } // set correct flags tool->flags &= (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL); tool->flags |= tool_t::WFL_SCRIPT; // get player parameter player_t *player = get_my_player(vm); if (player == NULL) { player = v.player; // must be scenario - set flag tool->flags |= tool_t::WFL_NO_CHK; } #if 0 // sanity check if (player == NULL) { return sq_raise_error(vm, "Called tool with player == null"); } #endif // check if calling suspendable tools is blocked if (!v.no_block) { if (const char* blocker = env_t::networkmode ? sq_get_suspend_blocker(vm) : NULL) { return sq_raise_error(vm, "Cannot call this tool from within `%s'.", blocker); } } // register this tool call for callback with this id uint32 callback_id = suspended_scripts_t::get_unique_key(tool); tool->callback_id = callback_id; // HACK call karte_t::set_tool bool suspended; welt->set_tool_api(tool, player, suspended); // in networkmode, call is suspended if (suspended && !v.no_block) { // register for wakeup suspended_scripts_t::register_suspended_script(callback_id, vm); // suspend vm for now, after wakeup it returns to the script return sq_suspendvm(vm); } else { bool res = true; return param::push(vm, res); } } call_tool_init script_api::command_rename(player_t *owner, char what, koord3d pos, const char* name) { // build param string (see tool_rename_t::init) cbuffer_t buf; buf.printf( "%c%s,%s", what, pos.get_str(), name); // empty name? trigger error by setting id to zero uint16 tool_id = name ? TOOL_RENAME | SIMPLE_TOOL : 0; return call_tool_init(tool_id, (const char*)buf, 0, owner); } call_tool_init script_api::command_rename(player_t *owner, char what, uint32 id, const char* name) { // build param string (see tool_rename_t::init) cbuffer_t buf; buf.printf( "%c%u,%s", what, id, name); // empty name? trigger error by setting id to zero uint16 tool_id = name ? TOOL_RENAME | SIMPLE_TOOL : 0; return call_tool_init(tool_id, (const char*)buf, 0, owner); } SQInteger command_work(HSQUIRRELVM vm) { tool_t *tool = param::get(vm, 1); player_t *player = param::get(vm, 2); koord3d pos1 = param::get(vm, 3); SQInteger top = sq_gettop(vm); // top >=3 /* three possible calling conventions: * (1) tool.work(pl, pos) * (2) tool.work(pl, pos, param) * (3) tool.work(pl, pos, pos2, param) */ const char* default_param = top>3 ? param::get(vm, top) : NULL; koord3d pos2 = top>4 ? param::get(vm, 4) : koord3d::invalid; bool twoclick = top>4; // save & set default_param my_tool_t *mtool = get_attached_instance(vm, 1, param::tag()); if (mtool == NULL) { return sq_raise_error(vm, "Called from an instance different to tool_x"); } plainstring &pdefault_param = mtool->default_param; pdefault_param = default_param; // copy tool->set_default_param( (const char*)pdefault_param ); // .. and set param if (!twoclick) { return param::push(vm, call_tool_work(tool, player, pos1)); } else { return param::push(vm, call_tool_work(tool, player, pos1, pos2)); } } tool_t * call_tool_base_t::create_tool() { tool_t *tool = ::create_tool(tool_id); if (tool) { tool->set_default_param(default_param); tool->flags = flags; } return tool; } SQInteger param::push(HSQUIRRELVM vm, call_tool_work v) { if (v.error) { if (v.tool_id==0) { // return error as if the tool failed normally return param::push(vm, v.error); } else { return sq_raise_error(vm, *v.error ? v.error : "Strange error occurred"); } } // create tool, if necessary, delete on exit std::unique_ptr our_tool; tool_t *tool = v.tool; if (tool == NULL) { our_tool.reset(v.create_tool()); tool = our_tool.get(); } if (tool == NULL) { return sq_raise_error(vm, "Called null tool"); } // set correct flags tool->flags &= (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL); tool->flags |= tool_t::WFL_SCRIPT; // get player parameter player_t *player = get_my_player(vm); if (player == NULL) { player = v.player; // must be scenario - set flag tool->flags |= tool_t::WFL_NO_CHK; } #if 0 // sanity check if (player == NULL) { return sq_raise_error(vm, "Called tool with player == null"); } #endif uint8 flags = tool->flags; // might be reset by init() // call init before work (but check network safety) if (!tool->is_init_keeps_game_state()) { return sq_raise_error(vm, "Initializing tool has side effects"); } if (!tool->init(player)) { return sq_raise_error(vm, "Error during initializing tool"); } // set flags tool->flags = flags; // test work if (tool->is_work_keeps_game_state() || (!v.twoclick && tool->is_work_here_keeps_game_state(player, v.start))) { return sq_raise_error(vm, "Tool has no effects"); } // two-click tool if (v.twoclick) { if (dynamic_cast(tool)==NULL) { return sq_raise_error(vm, "Cannot call this tool with two coordinates"); } if (!tool->is_work_here_keeps_game_state(player, v.start)) { return sq_raise_error(vm, "First click has side effects"); } } // check if calling suspendable tools is blocked if (!v.no_block) { if (const char* blocker = env_t::networkmode ? sq_get_suspend_blocker(vm) : NULL) { return sq_raise_error(vm, "Cannot call this tool from within `%s'.", blocker); } } bool suspended = false; const char* err = NULL; // first click of two_click_tool_t if (v.twoclick) { err = welt->call_work_api(tool, player, v.start, suspended); assert(!suspended); } // call the work that has effects if (err == NULL) { // register this tool call for callback with this id uint32 callback_id = suspended_scripts_t::get_unique_key(tool); tool->callback_id = callback_id; err = welt->call_work_api(tool, player, v.twoclick ? v.end : v.start, suspended); if (suspended && !v.no_block) { // register for wakeup suspended_scripts_t::register_suspended_script(callback_id, vm); // suspend vm for now, after wakeup it returns to the script return sq_suspendvm(vm); } } // return error message or NULL if tool was called return param::push(vm, err); } uint8 get_flags(tool_t *tool) { return tool->flags & (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL); } void set_flags(tool_t *tool, uint8 flags) { tool->flags = flags & (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL); } void* script_api::param::tag() { return (void*)param::get; } const char* is_available(const obj_desc_timelined_t* desc) { return desc->is_available(welt->get_timeline_year_month()) ? NULL : "Object not available (retired or future)."; } call_tool_work build_way(player_t* pl, koord3d start, koord3d end, const way_desc_t* way, bool straight, bool keep_city_roads) { if (way == NULL) { return call_tool_work("No way provided"); } if (const char* err = is_available(way)) { return call_tool_work(err); } return call_tool_work(TOOL_BUILD_WAY | GENERAL_TOOL, way->get_name(), (straight ? 2 : 0) + (keep_city_roads ? 1 : 0), pl, start, end); } call_tool_work build_wayobj(player_t* pl, koord3d start, koord3d end, const way_obj_desc_t* wayobj) { if (wayobj == NULL) { return call_tool_work("No wayobj provided"); } if (const char* err = is_available(wayobj)) { return call_tool_work(err); } return call_tool_work(TOOL_BUILD_WAYOBJ | GENERAL_TOOL, wayobj->get_name(), 0, pl, start, end); } typedef call_tool_work(*bsr_type)(player_t*, koord3d, const building_desc_t*, sint16); call_tool_work build_station_rotation(player_t* pl, koord3d pos, const building_desc_t* building, sint16 layout) { // rotation: SENW -> 0123, see station_building_select_t if (building == NULL || !building->is_transport_building()) { return call_tool_work("No building provided"); } if (const char* err = is_available(building)) { return call_tool_work(err); } static cbuffer_t buf; buf.clear(); if (layout >= 0) { uint8 rotation = welt->get_settings().get_rotation(); // we need to rotate the station back according to the map if (rotation) { if (building->get_all_layouts() <= 4) { layout = (layout + 4 - rotation) & (building->get_all_layouts() - 1); } else for (uint8 i = 0; i < rotation; i++) { // this is different for station building that houses ... static uint8 layout_rotate[16] = { 1, 8, 5, 10, 3, 12, 7, 14, 9, 0, 13, 2, 11, 4, 15, 6 }; // 8 & 16 tile lyoutout for stations layout = layout_rotate[layout] & (building->get_all_layouts()-1); } } buf.printf("%s,%i", building->get_name(), layout); } else { buf.printf("%s", building->get_name()); } return call_tool_work(TOOL_BUILD_STATION | GENERAL_TOOL, buf, 0, pl, pos); } SQInteger command_build_station(HSQUIRRELVM vm) { /* possible calling conventions: * * build_station(player, pos, desc) - top == 4 * build_station(player, pos, desc, layout ) - top == 5 */ if (sq_gettop(vm) == 4) { // layout parameter missing, push default value sq_pushinteger(vm, -1); } return embed_call_t::call_function(vm, build_station_rotation, false); } call_tool_work build_depot(player_t* pl, koord3d pos, const building_desc_t* building) { if (building == NULL || !building->is_depot()) { return call_tool_work("No depot provided"); } if (const char* err = is_available(building)) { return call_tool_work(err); } return call_tool_work(TOOL_BUILD_DEPOT | GENERAL_TOOL, building->get_name(), 0, pl, pos); } call_tool_work build_bridge(player_t* pl, koord3d start, koord3d end, const bridge_desc_t* bridge) { if (bridge == NULL) { return call_tool_work("No bridge provided"); } if (const char* err = is_available(bridge)) { return call_tool_work(err); } return call_tool_work(TOOL_BUILD_BRIDGE | GENERAL_TOOL, bridge->get_name(), 2, pl, start, end); } call_tool_work build_bridge_at(player_t* pl, koord3d start, const bridge_desc_t* bridge) { if (bridge == NULL) { return call_tool_work("No bridge provided"); } if (const char* err = is_available(bridge)) { return call_tool_work(err); } if (grund_t *gr = world()->lookup(start)) { sint8 height; koord3d end = start; if (const char *err = bridge_builder_t::find_end_pos(pl, start, -koord(gr->get_weg_hang()), height, bridge, 1, 10, false)) { return call_tool_work(err); // to keep compatibility with old error message } return call_tool_work(TOOL_BUILD_BRIDGE | GENERAL_TOOL, bridge->get_name(), 0, pl, start, end); } return call_tool_work("Bridge is too long for this type!\n"); // to keep compatibility with old error message } call_tool_work set_slope(player_t* pl, koord3d start, my_slope_t slope) { // communicate per default_param static char buf[8]; sprintf(buf, "%2d", (uint8)slope); static tool_setslope_t tool; // we do not want our slopes translated to double-height (even for single-height paksets), they are already in the double-height system tool.old_slope_compatibility_mode = false; tool.set_default_param(buf); return call_tool_work(&tool, pl, start); } call_tool_work restore_slope(player_t* pl, koord3d start) { return call_tool_work(TOOL_RESTORESLOPE | GENERAL_TOOL, "", 0, pl, start); } const char* can_set_slope(player_t* pl, koord3d pos, my_slope_t slope) { return tool_setslope_t::tool_set_slope_work(pl, pos, slope, false /* compatibility */, true /* check */); } sint64 set_slope_get_price(my_slope_t slope) { return slope == RESTORE_SLOPE ? -welt->get_settings().cst_alter_land : -welt->get_settings().cst_set_slope; } call_tool_work build_sign_at(player_t* pl, koord3d start, const roadsign_desc_t* sign) { if (sign == NULL) { return call_tool_work("No sign provided"); } if (const char* err = is_available(sign)) { return call_tool_work(err); } return call_tool_work(TOOL_BUILD_ROADSIGN | GENERAL_TOOL, sign->get_name(), 0, pl, start); } call_tool_work change_climate_at(player_t* pl, koord3d start, int climate) { if (climate < water_climate || climate >= MAX_CLIMATES) { return call_tool_work("Invalid climate number provided"); } // communicate per default_param static cbuffer_t param; param.clear(); param.printf("%d", climate); return call_tool_work(TOOL_SET_CLIMATE | GENERAL_TOOL, param, 0, pl, start, start); } call_tool_work lower_raise(player_t* pl, koord3d pos, bool lower) { // need to transform coordinate to grid coordinates switch(coordinate_transform_t::get_rotation()) { case 1: pos += koord(1,0); break; case 2: pos += koord(1,1); break; case 3: pos += koord(0,1); break; default:; } return call_tool_work((lower ? TOOL_LOWER_LAND : TOOL_RAISE_LAND) | GENERAL_TOOL, NULL, 0, pl, pos); } void export_commands(HSQUIRRELVM vm) { /** * Proof-of-concept to make tools available to scripts. * * The default_param is not checked. Use at own risk. * @ingroup game_cmd */ create_class(vm, "command_x"); /** * Constructor to obtain a tool. * @param id id of the tool * @typemask command_x(tool_ids) */ register_function(vm, command_constructor, "constructor", 2, "xi"); // set type tag to custom getter sq_settypetag(vm, -1, param::tag()); /** * @returns flags set for this tool */ register_method(vm, &get_flags, "get_flags", true); /** * Sets flags for this tool. * Simulates pressing shift or ctrl while clicking with mouse. * @param flags bitmap, 1 = shift pressed, 2 = ctrl pressed */ register_method(vm, &set_flags, "set_flags", true); /** * Does the dirty work. * @note In network games script will be suspended until the command is executed. * @param pl player to pay for the work * @param pos coordinate, where something should happen * @returns null upon success, an error string otherwise * @typemask string(player_x,coord3d) */ register_function(vm, command_work, "work", -3, "x t|x|y t|x|y"); #ifdef DOXYGEN // command_work works with different numbers of parameters. // Their documentation is included here. /** * Does the dirty work. * @note In network games script will be suspended until the command is executed. * @param pl player to pay for the work * @param pos coordinate, where something should happen * @param param magic parameter string * @returns null upon success, an error string otherwise * @typemask string(player_x,coord3d,string) */ register_function(vm,, "work"); const char* work(player_x player, coord3d pos, string param); /** * Does the dirty work. * Needs two positions for start and end of the work, for e.g., road building. * @note In network games script will be suspended until the command is executed. * @param pl player to pay for the work * @param start coordinate, where work begins * @param end coordinate, where work ends * @param param magic parameter string * @returns null upon success, an error string otherwise * @typemask string(player_x,coord3d,coord3d,string) */ register_function(vm,, "work"); #endif /** * Build a way. * @param pl player to pay for the work * @param start coordinate, where work begins * @param end coordinate, where work ends * @param way type of way to be built * @param straight force building of straight ways, similar as building way with control key pressed */ STATIC register_method_fv(vm, build_way, "build_way", freevariable(false), false, true); /** * Build a road. * @param pl player to pay for the work * @param start coordinate, where work begins * @param end coordinate, where work ends * @param way type of way to be built * @param straight force building of straight ways, similar as building way with control key pressed * @param keep_city_roads if true city roads will not be replaced */ STATIC register_method(vm, build_way, "build_road", false, true); /** * Build a depot. * @param pl player to pay for the work * @param pos position to place the depot * @param depot type of depot to be built */ STATIC register_method(vm, build_depot, "build_depot", false, true); /** * Build a station or station extension building. * @param pl player to pay for the work * @param pos position to place the depot * @param station type of station to be built * @param layout (optional parameter) rotation of building (only used for flat docks and extensions, ribi are not allowed and must be converted!) */ STATIC register_function(vm, command_build_station, "build_station", -4 /* at least 4 parameters */, func_signature_t::get_typemask(false).c_str(), false /* static */); log_squirrel_type(func_signature_t::get_squirrel_class(false), "build_station", func_signature_t::get_squirrel_type(false, 0)); /** * Build a bridge. * Similar to drag-and-build of bridges in-game. * @param pl player to pay for the work * @param start coordinate, where bridge begins * @param end coordinate, where bridge ends * @param bridge type of bridge to be built */ STATIC register_method(vm, build_bridge, "build_bridge", false, true); /** * Build a bridge. * Similar to one click with mouse on suitable start tile: program will figure out bridge span itself. * @param pl player to pay for the work * @param start coordinate, where bridge begins, the end point will be automatically determined * @param bridge type of bridge to be built */ STATIC register_method(vm, build_bridge_at, "build_bridge_at", false, true); /** * Modify the slope of one tile. * @param pl player to pay for the work * @param pos position of tile * @param slope new slope, can also be one of @ref slope::all_up_slope or @ref slope::all_down_slope. */ STATIC register_method(vm, set_slope, "set_slope", false, true); /** * Restore natural slope of one tile. * @param pl player to pay for the work * @param pos position of tile */ STATIC register_method(vm, restore_slope, "restore_slope", false, true); /** * Checks whether player @p pl can do this terraforming. * @param pl player * @param pos position * @param slope new slope, can also be one of @ref slope::all_up_slope or @ref slope::all_down_slope * @returns null (if allowed) or an error message otherwise */ STATIC register_method(vm, can_set_slope, "can_set_slope", false, true); /** * Costs of using @ref set_slope. * @returns cost */ STATIC register_method(vm, set_slope_get_price, "slope_get_price", false, true); /** * Build signal / road-sign. If such a sign already exists then change its direction. * @param pl player to pay for the work * @param pos position of tile * @param sign type of road-sign or signal to be built */ STATIC register_method(vm, build_sign_at, "build_sign_at", false, true); /** * Build way-object. * @param pl player to pay for the work * @param start coordinate, where work begins * @param end coordinate, where work ends * @param wayobj type of wayobj to be built */ STATIC register_method(vm, build_wayobj, "build_wayobj", false, true); /** * Change climate of tile * @param pl player to pay for the work * @param pos coordinate of tile * @param climate new climate, possible values see @ref climates */ STATIC register_method(vm, change_climate_at, "change_climate_at", false, true); /** * Lower grid point * @param pl player to pay for the work * @param pos coordinate of tile, grid point in nw corner will be lowered */ STATIC register_method_fv(vm, lower_raise, "grid_lower", freevariable(true), false, true); /** * Raise grid point * @param pl player to pay for the work * @param pos coordinate of tile, grid point in nw corner will be lowered */ STATIC register_method_fv(vm, lower_raise, "grid_raise", freevariable(false), false, true); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_command.h000066400000000000000000000106641520675460500231040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_API_COMMAND_H #define SCRIPT_API_API_COMMAND_H #include "../api_param.h" #include "../../dataobj/koord3d.h" #include "../../utils/plainstring.h" void export_commands(HSQUIRRELVM vm); class koord3d; class player_t; class tool_t; namespace script_api { /** * Class to hold pointer to tool_t and to manage its default_param string. */ struct my_tool_t { tool_t *tool; plainstring default_param; my_tool_t(tool_t *tool_) : tool(tool_) { } }; /* * Return types for tool calls: * - call_tool_init * - call_tool_work * * Their param<>::push methods actually call work/init and suspend. * * To implement a tool, make a method returning call_tool_work: * * call_tool_work call_x_tool(parameters..) * { * .... * return call_tool_work(tool, player, koord3d) * } * * call_tool_init call_x_tool(parameters..) * { * .... * return call_tool_init(tool, player) * } */ class call_tool_base_t { protected: // tool to be called, can be NULL tool_t *tool; // create tool with this id, default_param, and flags if tool == NULL uint16 tool_id; plainstring default_param; uint8 flags; player_t *player; // error message when calling, returned by push const char* error; // if true, calls to tools that were not immediately executed will not suspend the vm // but rather return null (as if the tool was successfully executed) bool no_block; public: call_tool_base_t(tool_t* t, player_t* pl) : tool(t), tool_id(0), default_param(NULL), flags(0), player(pl), error(NULL), no_block(false) { } call_tool_base_t(uint16 id, const char* dp, uint8 f, player_t* pl) : tool(NULL), tool_id(id), default_param(dp), flags(f), player(pl), error(NULL), no_block(false) { } call_tool_base_t(const char* err) : tool(NULL), tool_id(0), default_param(NULL), flags(0), player(NULL), error(err), no_block(false) { } tool_t * create_tool(); }; class call_tool_init : public call_tool_base_t { friend struct param; public: call_tool_init(tool_t* t, player_t* pl) : call_tool_base_t(t, pl) { } call_tool_init(uint16 id, const char* dp, uint8 f, player_t* pl) : call_tool_base_t(id, dp, f, pl) { } call_tool_init(const char* err) : call_tool_base_t(err) { } }; class call_tool_work : public call_tool_base_t { // parameters to call the work methods koord3d start; koord3d end; bool twoclick; friend struct param; public: call_tool_work(tool_t* t, player_t* pl, koord3d pos1) : call_tool_base_t(t, pl), start(pos1), end(koord3d::invalid), twoclick(false) { } call_tool_work(tool_t* t, player_t* pl, koord3d pos1, koord3d pos2) : call_tool_base_t(t, pl), start(pos1), end(pos2), twoclick(true) { } call_tool_work(uint16 id, const char* dp, uint8 f, player_t* pl, koord3d pos1) : call_tool_base_t(id, dp, f, pl), start(pos1), end(koord3d::invalid), twoclick(false) { } call_tool_work(uint16 id, const char* dp, uint8 f, player_t* pl, koord3d pos1, koord3d pos2) : call_tool_base_t(id, dp, f, pl), start(pos1), end(pos2), twoclick(true) { } call_tool_work(const char* err) : call_tool_base_t(err), start(koord3d::invalid), end(koord3d::invalid), twoclick(false) { } call_tool_work set_no_block(bool nb) { no_block = nb; return *this; } }; template<> struct param { /** * Pretends to be a push method. BUT, actually, * Calls tool->init (or sends to server & suspends the script). * Returns error string at the end of the day. */ static SQInteger push(HSQUIRRELVM vm, call_tool_init v); // returns nothing sensible static const char* squirrel_type() { return "void"; } }; template<> struct param { /** * Pretends to be a push method. BUT, actually, * Calls tool->work (or sends to server & suspends the script). * Returns error string at the end of the day. */ static SQInteger push(HSQUIRRELVM vm, call_tool_work v); // returns strings static const char* squirrel_type() { return param::squirrel_type(); } }; /** * Helper functions to rename objects. * @param what indicates type (see tool_rename_t::init) */ call_tool_init command_rename(player_t *owner, char what, koord3d pos, const char* name); call_tool_init command_rename(player_t *owner, char what, uint32 id, const char* name); }; #endif simutrans-124.5/src/simutrans/script/api/api_const.cc000066400000000000000000000263441520675460500227540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_const.cc exports constants */ #include "../api_param.h" #include "../../obj/simobj.h" #include "../../tool/simmenu.h" #include "../../simunits.h" using namespace script_api; #define begin_enum(name) sq_pushconsttable(vm); #define end_enum() sq_setconsttable(vm); #define enum_slot create_slot void export_global_constants(HSQUIRRELVM vm) { /** * Constants to identify tools, ie actions a player can perform to * alter the state of the game. */ begin_enum("tool_ids"); /// remover tool enum_slot(vm, "tool_remover", TOOL_REMOVER | GENERAL_TOOL); /// raise land tool enum_slot(vm, "tool_raise_land", TOOL_RAISE_LAND | GENERAL_TOOL); /// lower land tool enum_slot(vm, "tool_lower_land", TOOL_LOWER_LAND | GENERAL_TOOL); /// artificial slope enum_slot(vm, "tool_setslope", TOOL_SETSLOPE | GENERAL_TOOL); /// restore natural slope enum_slot(vm, "tool_restoreslope", TOOL_RESTORESLOPE | GENERAL_TOOL); /// set marker enum_slot(vm, "tool_set_marker", TOOL_MARKER | GENERAL_TOOL); /// clear block reservation enum_slot(vm, "tool_clear_reservation", TOOL_CLEAR_RESERVATION | GENERAL_TOOL); /// build transformer enum_slot(vm, "tool_build_transformer", TOOL_TRANSFORMER | GENERAL_TOOL); /// add city enum_slot(vm, "tool_add_city", TOOL_ADD_CITY | GENERAL_TOOL); /// change city size enum_slot(vm, "tool_change_city_size", TOOL_CHANGE_CITY_SIZE | GENERAL_TOOL); /// plant a tree enum_slot(vm, "tool_plant_tree", TOOL_PLANT_TREE | GENERAL_TOOL); // not needed? enum__slot(vm, "tool_schedule_add", TOOL_SCHEDULE_ADD | GENERAL_TOOL); // not needed? enum__slot(vm, "tool_schedule_ins", TOOL_SCHEDULE_INS | GENERAL_TOOL); /// build groundobj/movingobj enum_slot(vm, "tool_build_groundobj", TOOL_PLANT_GROUNDOBJ | GENERAL_TOOL); /// build ways enum_slot(vm, "tool_build_way", TOOL_BUILD_WAY | GENERAL_TOOL); /// build bridges enum_slot(vm, "tool_build_bridge", TOOL_BUILD_BRIDGE | GENERAL_TOOL); /// build tunnel enum_slot(vm, "tool_build_tunnel", TOOL_BUILD_TUNNEL | GENERAL_TOOL); /// remove way enum_slot(vm, "tool_remove_way", TOOL_WAYREMOVER | GENERAL_TOOL); /// build overhead wires enum_slot(vm, "tool_build_wayobj", TOOL_BUILD_WAYOBJ | GENERAL_TOOL); /// build stations enum_slot(vm, "tool_build_station", TOOL_BUILD_STATION | GENERAL_TOOL); /// build signals and road signs enum_slot(vm, "tool_build_roadsign", TOOL_BUILD_ROADSIGN | GENERAL_TOOL); /// build depot enum_slot(vm, "tool_build_depot", TOOL_BUILD_DEPOT | GENERAL_TOOL); /// build city houses enum_slot(vm, "tool_build_house", TOOL_BUILD_HOUSE | GENERAL_TOOL); /// create industry chain with end consumer not in cities enum_slot(vm, "tool_land_chain", TOOL_BUILD_LAND_CHAIN | GENERAL_TOOL); /// create industry chain with end consumer in cities enum_slot(vm, "tool_city_chain", TOOL_CITY_CHAIN | GENERAL_TOOL); /// build a factory enum_slot(vm, "tool_build_factory", TOOL_BUILD_FACTORY | GENERAL_TOOL); /// link factories enum_slot(vm, "tool_link_factory", TOOL_LINK_FACTORY | GENERAL_TOOL); /// build headquarters enum_slot(vm, "tool_headquarter", TOOL_HEADQUARTER | GENERAL_TOOL); /// lock map: switching players not allowed anymore enum_slot(vm, "tool_lock_game", TOOL_LOCK_GAME | GENERAL_TOOL); /// add city car enum_slot(vm, "tool_add_citycar", TOOL_ADD_CITYCAR | GENERAL_TOOL); /// create forest enum_slot(vm, "tool_forest", TOOL_FOREST | GENERAL_TOOL); /// move stop tool enum_slot(vm, "tool_stop_mover", TOOL_STOP_MOVER | GENERAL_TOOL); /// make stop public enum_slot(vm, "tool_make_stop_public", TOOL_MAKE_STOP_PUBLIC | GENERAL_TOOL); /// remove way objects like overheadwires enum_slot(vm, "tool_remove_wayobj", TOOL_REMOVE_WAYOBJ | GENERAL_TOOL); // not needed? enum__slot(vm, "tool_sliced_and_underground_view", TOOL_SLICED_AND_UNDERGROUND_VIEW | GENERAL_TOOL); /// buy a house enum_slot(vm, "tool_buy_house", TOOL_BUY_HOUSE | GENERAL_TOOL); /// build city road with pavement enum_slot(vm, "tool_build_cityroad", TOOL_BUILD_CITYROAD | GENERAL_TOOL); /// alter water height enum_slot(vm, "tool_change_water_height", TOOL_CHANGE_WATER_HEIGHT | GENERAL_TOOL); /// change climate of tiles enum_slot(vm, "tool_set_climate", TOOL_SET_CLIMATE | GENERAL_TOOL); /// rotate a building enum_slot(vm, "tool_rotate_building", TOOL_ROTATE_BUILDING | GENERAL_TOOL); /// merge two stops enum_slot(vm, "tool_merge_stop", TOOL_MERGE_STOP | GENERAL_TOOL); /// scripted tool (one-click) enum_slot(vm, "tool_exec_script", TOOL_EXEC_SCRIPT | GENERAL_TOOL); /// scripted tool (two-click) enum_slot(vm, "tool_exec_two_click_script", TOOL_EXEC_TWO_CLICK_SCRIPT | GENERAL_TOOL); /// change owner of nth object on tile enum_slot(vm, "tool_set_owner", TOOL_SET_OWNER | GENERAL_TOOL); // simple tools /// increase industry density enum_slot(vm, "tool_increase_industry", TOOL_INCREASE_INDUSTRY | SIMPLE_TOOL); /// switch player enum_slot(vm, "tool_switch_player", TOOL_SWITCH_PLAYER | SIMPLE_TOOL); /// step year forward enum_slot(vm, "tool_step_year", TOOL_STEP_YEAR | SIMPLE_TOOL); /// fill area with trees enum_slot(vm, "tool_fill_trees", TOOL_FILL_TREES | SIMPLE_TOOL); /// set traffic level enum_slot(vm, "tool_set_traffic_level", TOOL_TRAFFIC_LEVEL | SIMPLE_TOOL); // tools to open certain windows /// open factory editor window enum_slot(vm, "dialog_edit_factory", DIALOG_EDIT_FACTORY | DIALOGE_TOOL); /// open tourist attraction editor window enum_slot(vm, "dialog_edit_attraction", DIALOG_EDIT_ATTRACTION | DIALOGE_TOOL); /// open house editor window enum_slot(vm, "dialog_edit_house", DIALOG_EDIT_HOUSE | DIALOGE_TOOL); /// open tree editor window enum_slot(vm, "dialog_edit_tree", DIALOG_EDIT_TREE | DIALOGE_TOOL); /// open map enlargement window enum_slot(vm, "dialog_enlarge_map", DIALOG_ENLARGE_MAP | DIALOGE_TOOL); end_enum(); /** * Flags for scripted tools. */ begin_enum("tool_flags"); enum_slot(vm, "shift_pressed", tool_t::WFL_SHIFT); enum_slot(vm, "ctrl_pressed", tool_t::WFL_CTRL); end_enum(); /** * Constants for different way types. */ begin_enum("way_types"); /// catch all value: used to forbid tools for all waytypes enum_slot(vm, "wt_all", ignore_wt); /// road enum_slot(vm, "wt_road", road_wt); /// rail enum_slot(vm, "wt_rail", track_wt); /// water enum_slot(vm, "wt_water", water_wt); /// monorail enum_slot(vm, "wt_monorail", monorail_wt); /// maglev enum_slot(vm, "wt_maglev", maglev_wt); /// trams enum_slot(vm, "wt_tram", tram_wt); /// narrow gauge enum_slot(vm, "wt_narrowgauge", narrowgauge_wt); /// aircrafts and airports enum_slot(vm, "wt_air", air_wt); /// powerlines enum_slot(vm, "wt_power", powerline_wt); /// decomartion for unspecifed wayobj enum_slot(vm, "wt_decoration", decoration_wt); /// invalid enum_slot(vm, "wt_invalid", invalid_wt); end_enum(); /** * Constants for different system types of ways. * System type could take also other values than the ones defined here. */ begin_enum("way_system_types"); /// flat ways enum_slot(vm, "st_flat", type_flat); /// elevated ways enum_slot(vm, "st_elevated", type_elevated); /// runway (for wt_air), equal to st_elevated enum_slot(vm, "st_runway", type_runway); /// tram tracks (here way type has to be wt_tram) enum_slot(vm, "st_tram", type_tram); end_enum(); // players begin_enum(""); /// constant to forbid/allow tools for all players (except public player) enum_slot(vm, "player_all", PLAYER_UNOWNED); end_enum(); /** * Types of map objects. */ begin_enum("map_objects"); /// tree enum_slot(vm, "mo_tree", obj_t::baum); /// pointer (bulldozers etc) enum_slot(vm, "mo_pointer", obj_t::zeiger); /// cloud and smoke enum_slot(vm, "mo_cloud", obj_t::cloud); /// building (houses, halts, factories ...) enum_slot(vm, "mo_building", obj_t::gebaeude); /// signal enum_slot(vm, "mo_signal", obj_t::signal); /// bridge enum_slot(vm, "mo_bridge", obj_t::bruecke); /// tunnel enum_slot(vm, "mo_tunnel", obj_t::tunnel); /// depot: rail enum_slot(vm, "mo_depot_rail", obj_t::bahndepot); /// depot: road enum_slot(vm, "mo_depot_road", obj_t::strassendepot); /// depot: ship enum_slot(vm, "mo_depot_water", obj_t::schiffdepot); /// powerline enum_slot(vm, "mo_powerline", obj_t::leitung); /// transformer at powerplant enum_slot(vm, "mo_transformer_s", obj_t::pumpe); /// transformer at factory enum_slot(vm, "mo_transformer_c", obj_t::senke); /// road-sign enum_slot(vm, "mo_roadsign", obj_t::roadsign); /// bridge pillar enum_slot(vm, "mo_pillar", obj_t::pillar); /// depot: airplanes enum_slot(vm, "mo_depot_air", obj_t::airdepot); /// depot: monorail enum_slot(vm, "mo_depot_monorail", obj_t::monoraildepot); /// depot: tram enum_slot(vm, "mo_depot_tram", obj_t::tramdepot); /// depot: maglev enum_slot(vm, "mo_depot_maglev", obj_t::maglevdepot); /// way object (overhead wires) enum_slot(vm, "mo_wayobj", obj_t::wayobj); /// way enum_slot(vm, "mo_way", obj_t::way); /// text label enum_slot(vm, "mo_label", obj_t::label); /// field enum_slot(vm, "mo_field", obj_t::field); /// crossing enum_slot(vm, "mo_crossing", obj_t::crossing); /// decorative objects (rocks, lakes ...) enum_slot(vm, "mo_groundobj", obj_t::groundobj); /// depot: narrowgauge enum_slot(vm, "mo_depot_narrowgauge", obj_t::narrowgaugedepot); /// pedestrian enum_slot(vm, "mo_pedestrian", obj_t::pedestrian); /// city car - not player owned enum_slot(vm, "mo_city_car", obj_t::road_user); /// road vehicle enum_slot(vm, "mo_car", obj_t::road_vehicle); /// rail vehicle enum_slot(vm, "mo_train", obj_t::rail_vehicle); /// monorail vehicle enum_slot(vm, "mo_monorail", obj_t::monorail_vehicle); /// maglev vehicle enum_slot(vm, "mo_maglev", obj_t::maglev_vehicle); /// narrowgauge vehicle enum_slot(vm, "mo_narrowgauge", obj_t::narrowgauge_vehicle); /// ship enum_slot(vm, "mo_ship", obj_t::water_vehicle); /// airplane enum_slot(vm, "mo_airplane", obj_t::air_vehicle); /// moving object (sheep ...) enum_slot(vm, "mo_moving_object", obj_t::movingobj); end_enum(); /** * Internal units. */ begin_enum("units"); /// The length of one side of a tile in car units. @see vehicle_desc_x::get_length enum_slot(vm, "CARUNITS_PER_TILE", (uint32)CARUNITS_PER_TILE); end_enum(); /** * Climate zones. Their naming may differ from the graphical representation and * translation in some paksets. */ begin_enum("climates"); enum_slot(vm, "cl_water", water_climate); enum_slot(vm, "cl_desert", desert_climate); enum_slot(vm, "cl_tropic", tropic_climate); enum_slot(vm, "cl_mediterran", mediterran_climate); enum_slot(vm, "cl_temperate", temperate_climate); enum_slot(vm, "cl_tundra", tundra_climate); enum_slot(vm, "cl_rocky", rocky_climate); enum_slot(vm, "cl_arctic", arctic_climate); end_enum(); /** * Gadgets ides. They are used to obtain the image id of the gadgets. */ begin_enum("gadgets"); enum_slot(vm, "ga_close", SKIN_GADGET_CLOSE); enum_slot(vm, "ga_help", SKIN_GADGET_HELP); enum_slot(vm, "ga_minimize", SKIN_GADGET_MINIMIZE); enum_slot(vm, "ga_previous", SKIN_BUTTON_PREVIOUS); enum_slot(vm, "ga_next", SKIN_BUTTON_NEXT); enum_slot(vm, "ga_notpinned", SKIN_GADGET_NOTPINNED); enum_slot(vm, "ga_pinned", SKIN_GADGET_PINNED); enum_slot(vm, "ga_resize", SKIN_WINDOW_RESIZE); enum_slot(vm, "ga_gotopos", SKIN_GADGET_GOTOPOS); end_enum(); } simutrans-124.5/src/simutrans/script/api/api_control.cc000066400000000000000000000045601520675460500233020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_control.cc script control and debug functions. */ #include "../api_function.h" #include "../api_class.h" #include "../script.h" #include "../../../squirrel/sq_extensions.h" #include "../../tool/simtool.h" namespace script_api { bool pause_game() { tool_pause_t t; if (!t.is_selected()) { t.init(NULL); } return t.is_selected(); } bool is_game_paused() { tool_pause_t t; return t.is_selected(); } }; using namespace script_api; SQInteger sleep(HSQUIRRELVM vm) { if (const char* blocker = sq_get_suspend_blocker(vm)) { return sq_raise_error(vm, "Cannot call sleep from within `%s'.", blocker); } return sq_suspendvm(vm); } SQInteger set_pause_on_error(HSQUIRRELVM vm) { if (script_vm_t *script = (script_vm_t*)sq_getforeignptr(vm)) { bool poe = param::get(vm, 2); script->pause_on_error = poe; } return SQ_OK; } void export_control(HSQUIRRELVM vm) { // in global scope /** * Suspends execution of the script. Upon resume, the script * will have full count of opcodes to spent for fragile code, * i.e. writing into @ref persistent. * @typemask void() */ register_function(vm, sleep, "sleep", 1, "."); /** * @returns total amount of opcodes executed by vm */ register_function(vm, sq_get_ops_total, "get_ops_total"); /** * @returns amount of remaining opcodes until vm will be suspended */ register_function(vm, sq_get_ops_remaing, "get_ops_remaining"); begin_class(vm, "debug"); /** * Pauses game. Does not work in network games. Use with care. * @returns true when successful. */ STATIC register_method(vm, &pause_game, "pause", false, true); /** * Checks whether game is paused. * Note that scripts only run for unpaused games. * This can only be used in functions that are called by user actions (e.g., is_*_allowed). * @returns true when successful. */ STATIC register_method(vm, &is_game_paused, "is_paused", false, true); /** * Scripts can pause the game in case of error. Toggle this behavior by parameter @p p. * Does not work in network games. Use with care. * @param p true if script should make the game pause in case of error */ STATIC register_function(vm, set_pause_on_error, "set_pause_on_error", true); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_convoy.cc000066400000000000000000000311141520675460500231320ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_convoy.cc exports convoy related functions. */ #include "get_next.h" #include "api_obj_desc_base.h" #include "../api_class.h" #include "../api_function.h" #include "../../simconvoi.h" #include "../../simhalt.h" #include "../../simline.h" #include "../../world/simworld.h" #include "../../display/viewport.h" #include "../../vehicle/vehicle.h" // for convoy tools #include "../../tool/simmenu.h" #include "../../dataobj/schedule.h" using namespace script_api; waytype_t get_convoy_wt(convoi_t const *cnv) { if (cnv && cnv->get_vehicle_count() > 0) { return cnv->front()->get_waytype(); } return invalid_wt; } vector_tpl const& get_convoy_stat(convoi_t* cnv, sint32 INDEX) { static vector_tpl v; v.clear(); if (cnv && 0<=INDEX && INDEXget_stat_converted(i, INDEX)); } } return v; } vector_tpl const& convoi_get_vehicles(convoi_t* cnv) { static vector_tpl v; v.clear(); if (cnv) { for(uint16 i=0; iget_vehicle_count(); i++) { v.append(cnv->get_vehicle(i)->get_desc()); } } return v; } sint32 convoy_get_kmh(convoi_t const* cnv) { return cnv ? speed_to_kmh(cnv->get_akt_speed()) : -1; } vector_tpl const* generic_get_convoy_list(HSQUIRRELVM vm, SQInteger index) { uint16 id; bool use_world; if (SQ_SUCCEEDED(get_slot(vm, "halt_id", id, index))) { halthandle_t halt; halt.set_id(id); if (halt.is_bound()) { return &halt->registered_convoys; } } if (SQ_SUCCEEDED(get_slot(vm, "line_id", id, index))) { linehandle_t line; line.set_id(id); if (line.is_bound()) { return &line->get_convoys(); } } if (SQ_SUCCEEDED(get_slot(vm, "use_world", use_world, index)) && use_world) { return &welt->convoys(); } sq_raise_error(vm, "Invalid convoy list."); return NULL; } SQInteger generic_get_convoy_count(HSQUIRRELVM vm) { vector_tpl const* list = generic_get_convoy_list(vm, 1); return list ? param::push(vm, list->get_count()) : SQ_ERROR; } SQInteger generic_get_next_convoy(HSQUIRRELVM vm) { vector_tpl const* list = generic_get_convoy_list(vm, 1); return list ? generic_get_next(vm, list->get_count()) : SQ_ERROR; } SQInteger generic_get_convoy_by_index(HSQUIRRELVM vm) { vector_tpl const* list = generic_get_convoy_list(vm, 1); sint32 index = param::get(vm, 2); convoihandle_t cnv; if (list) { cnv = (0<=index && (uint32)index < list->get_count()) ? (*list)[index] : convoihandle_t(); return push_instance(vm, "convoy_x", cnv.is_bound() ? cnv.get_id() : 0); } return SQ_ERROR; } uint32 calc_max_kmh(uint32 power, uint32 weight, sint32 speed_limit) { return speed_to_kmh(convoi_t::calc_max_speed(power, weight, kmh_to_speed(speed_limit)) ); } uint32 kmh_to_tiles_per_month(uint32 kmh) { return welt->speed_to_tiles_per_month( kmh_to_speed(kmh)); } call_tool_init convoy_set_name(convoi_t *cnv, const char* name) { return command_rename(cnv->get_owner(), 'c', cnv->self.get_id(), name); } call_tool_init convoy_set_line(convoi_t *cnv, player_t *player, linehandle_t line) { if (!line.is_bound()) { return "Invalid line handle provided"; } // see depot_frame_t::apply_line() and convoi_t::call_convoi_tool() cbuffer_t buf; buf.printf("%c,%u,%i", 'l', cnv->self.get_id(), line->get_handle().get_id()); return call_tool_init(TOOL_CHANGE_CONVOI | SIMPLE_TOOL, buf, 0, player); } call_tool_init convoy_generic_tool(convoi_t *cnv, player_t *player, uint8 cnvtool) { char c = (char)cnvtool; if (strchr("wx", c) == NULL) { return "Invalid convoy tool called"; } // see convoi_t::call_convoi_tool() cbuffer_t buf; buf.printf("%c,%u", c, cnv->self.get_id()); return call_tool_init(TOOL_CHANGE_CONVOI | SIMPLE_TOOL, buf, 0, player); } bool convoy_is_schedule_editor_open(convoi_t *cnv) { return cnv->get_state() == convoi_t::EDIT_SCHEDULE; } sint32 convoy_is_followed(convoi_t const *cnv) { return cnv ? world()->get_viewport()->get_follow_convoi().get_id() == cnv->self.get_id() : false; } sint32 convoy_set_followed(convoi_t const *cnv) { if (cnv) { world()->get_viewport()->set_follow_convoi(cnv->self); } return 0; } bool convoy_is_loading(convoi_t const *cnv) { return cnv->get_state() == convoi_t::LOADING; } call_tool_init convoy_change_schedule(convoi_t *cnv, player_t *player, schedule_t *sched) { if (sched) { cbuffer_t buf; // make a copy, and perform validation on it schedule_t *copy = sched->copy(); copy->make_valid(); if (copy->get_count() >= 2) { // build param string (see convoi_info_t::apply_schedule and convoi_t::call_convoi_tool()) buf.printf("g,%u,", cnv->self.get_id()); copy->sprintf_schedule(buf); } else { return "Invalid schedule provided: less than two entries remained after removing doubles"; } delete copy; return call_tool_init(TOOL_CHANGE_CONVOI | SIMPLE_TOOL, buf, 0, player); } return "Invalid schedule provided"; } void export_convoy(HSQUIRRELVM vm) { /** * Implements iterator to iterate through lists of convoys. * * Usage: * @code * local list = world.get_convoy_list_x() * foreach(cnv in list) { * ... // cnv is an instance of the convoy_x class * } * @endcode * * @see world::get_convoy_list, halt_x::get_convoy_list, line_x::get_convoy_list */ begin_class(vm, "convoy_list_x", 0); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, generic_get_next_convoy, "_nexti", 2, "x o|i"); /** * Meta-method to be used in foreach loops. Do not call them directly. * @typemask convoy_x() */ register_function(vm, generic_get_convoy_by_index, "_get", 2, "xi"); /** * Returns number of convoys in the list. * @typemask integer() */ register_function(vm, generic_get_convoy_count, "get_count", 1, "x"); end_class(vm); /** * Class to access a convoy. * Player vehicles are convoys, which themselves consist of individual vehicles (trucks, trailers, ...). */ begin_class(vm, "convoy_x", "extend_get,ingame_object"); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") /** * Does convoy needs electrified ways? * @returns true if this is the case */ register_method(vm, &convoi_t::needs_electrification, "needs_electrification"); /** * Name of convoy. * @returns name */ register_method(vm, &convoi_t::get_name, "get_name"); /** * Sets convoy name. * @ingroup rename_func */ register_method(vm, &convoy_set_name, "set_name", true); /** * Position of convoy. * @returns pos */ register_method(vm, &convoi_t::get_pos, "get_pos"); /** * Owner of convoy. * @returns owner, which is instance of player_x */ register_method(vm, &convoi_t::get_owner, "get_owner"); /** * Returns array of goods categories that can be transported by this convoy. * @returns array */ register_method(vm, &convoi_t::get_goods_catg_index, "get_goods_catg_index"); /** * Returns waytype of convoy. * @returns waytype * @see way_types */ register_method(vm, &get_convoy_wt, "get_waytype", true); /** * Schedule of this convoy. */ register_method(vm, &convoi_t::get_schedule, "get_schedule"); /** * Get monthly statistics of capacity. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_convoy_stat, "get_capacity", freevariable(convoi_t::CONVOI_CAPACITY), true); /** * Get monthly statistics of number of transported goods. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_convoy_stat, "get_transported_goods", freevariable(convoi_t::CONVOI_TRANSPORTED_GOODS), true ); /** * Get monthly statistics of revenue. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_convoy_stat, "get_revenue", freevariable(convoi_t::CONVOI_REVENUE), true ); /** * Get monthly statistics of running costs. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_convoy_stat, "get_cost", freevariable(convoi_t::CONVOI_OPERATIONS), true ); /** * Get monthly statistics of profit. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_convoy_stat, "get_profit", freevariable(convoi_t::CONVOI_PROFIT), true ); /** * Get monthly statistics of traveled distance. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_convoy_stat, "get_traveled_distance", freevariable(convoi_t::CONVOI_DISTANCE), true ); /** * Get monthly statistics of income/loss due to way tolls. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_convoy_stat, "get_way_tolls", freevariable(convoi_t::CONVOI_WAYTOLL), true ); /** * @returns lifetime traveled distance of this convoy */ register_method(vm, &convoi_t::get_total_distance_traveled, "get_distance_traveled_total"); /** * @returns the line the convoy belongs to, null if there is no line */ register_method(vm, &convoi_t::get_line, "get_line"); /** * Assigns the convoy to the given line. * @param player * @param line * @ingroup game_cmd */ register_method(vm, convoy_set_line, "set_line", true); /** * @returns returns an array containing the vehicle_desc_x 's of the vehicles of this convoy */ register_method(vm, convoi_get_vehicles, "get_vehicles", true); /** * @returns current speed of convoy */ register_method(vm, convoy_get_kmh, "get_speed", true); /** * @returns get current loading limit: waiting for full load gives 100 */ register_method(vm, &convoi_t::get_loading_limit, "get_loading_limit"); /** * @returns get current loading level */ register_method(vm, &convoi_t::get_loading_level, "get_loading_level"); /** * @returns gets location of home depot */ register_method(vm, &convoi_t::get_home_depot, "get_home_depot"); /** * @returns whether convoi has obsolete vehicles */ register_method(vm, &convoi_t::has_obsolete_vehicles, "has_obsolete_vehicles"); /** * Toggle the flag 'withdraw convoy' * @ingroup game_cmd */ register_method_fv(vm, convoy_generic_tool, "toggle_withdraw", freevariable('w'), true ); /** * @returns the flag 'withdraw convoy' */ register_method(vm, &convoi_t::get_withdraw, "is_withdrawn"); /** * @returns true if convoy is in depot */ register_method(vm, &convoi_t::in_depot, "is_in_depot"); /** * @returns true if convoy is currently waiting (for way clearance) */ register_method(vm, &convoi_t::is_waiting, "is_waiting"); /** * @returns true if convoy is currently being followed */ register_method(vm, convoy_is_followed, "is_followed", true); /** * Centers the camera on this convoy and follows it. * @ingroup game_cmd */ register_method(vm, convoy_set_followed, "set_followed", true); /** * @returns true if convoy is currently loading or unloading at a stop */ register_method(vm, convoy_is_loading, "is_loading", true); /** * Destroy the convoy. * The convoy will be marked for destroying, it will be destroyed when the simulation continues. * Call @ref sleep to be sure that the convoy is destroyed before script continues. * @ingroup game_cmd */ register_method_fv(vm, convoy_generic_tool, "destroy", freevariable('x'), true); /** * @returns returns true if the schedule of the convoy is currently being edited. */ register_method(vm, convoy_is_schedule_editor_open, "is_schedule_editor_open", true); /** * @returns returns the number of station tiles covered by the convoy. */ register_method(vm, &convoi_t::get_tile_length, "get_tile_length"); /** * Change schedule of convoy. * Schedule should not contain doubled entries and more than two entries. * This might make the convoy lose its line. * @ingroup game_cmd */ register_method(vm, convoy_change_schedule, "change_schedule", true); #define STATIC /** * Static method to compute the potential max speed of a convoy * with the given parameters. * @param power total power of convoy * @param weight weight of convoy * @param speed_limit speed limit induced by convoy's vehicles * @returns max speed */ STATIC register_method(vm, &calc_max_kmh, "calc_max_speed"); /** * Static method to convert speed (from km per hour) to tiles per month. * @param speed * @returns tile per month */ STATIC register_method(vm, &kmh_to_tiles_per_month, "speed_to_tiles_per_month"); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_doc.h000066400000000000000000000325221520675460500222300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /** @file api_doc.h Just contains pages of exciting documentation */ /** * @mainpage * * Simutrans offers to possibility to add scripted scenarios and computer-controlled players. * * @tableofcontents * * Scripting language * ------------------ * * The scripts have to be written in squirrel. The manual can be found at Squirrel main page. * As squirrels like to crack nuts, understandably the script files get the extension '.nut'. * * @section s_scenarios Scripted scenarios * * See also @ref scen_skel and @ref scen_only. * * @subsection sec_howto How to create a scenario. * * You first need an @e idea - a vision what a scenario may look like. Then you have * to cast this idea into a @e savegame. That is, create the world in which your scenario will live. * You are the ruler of this toy universe, you are in charge of the rules, which go into the @e script. * * * @subsection sec_ex Example scenario scripts * * There are several commented examples. * -# @ref page_script_mill * -# @ref page_script_pharm * * @subsection sec_dir Recommended directory structure * * The scenario plays in a savegame. This savegame is tied to the pak-set you are using (e.g. pak64, pak128.Britain). * Hence, the scenario files have to go into a sub-folder of the pak-set. * The pak-set is found in a directory named pak-something, which is in the same directory, where the program * itself is located or under user-directory/addons/. * * Your scenario file goes into the folder * * * pak-something/scenario/myscenario/ * * * The scenario script must be in the file * * * pak-something/scenario/myscenario/scenario.nut * * * Translation files go into the scenario sub-directory with the same name as your file (just without extension) * * * pak-something/scenario/myscenario/ * * * If you need more complex texts, these go into sub-directories named after the language, in which they are written. * * * pak-something/scenario/myscenario/en/ <- English files go here. * * pak-something/scenario/myscenario/de/ <- German files go here. * * * Scenarios can also be put into the addons folder: * * * addons/pak-something/scenario/myscenario/ * * * * @section s_ai_player Scripted AI players * * See also @ref ai_skel. * * @subsection sec_dir Recommended directory structure * * Your script file goes into the folder * * * ai/myai/ * * * The main script file must be * * * ai/myai/ai.nut * * * The ai scripts can also be put into * * * addons/ai/myai/ * * * @section s_scripted_tools Scripted tools * * See also @ref tool_skel and @ref tool_only. * * @subsection sec_dir_tools Recommended directory structure * * The script file (tool.nut) as well as the configuration file (description.tab) go into * * * pak-something/tool/mytool/ * * * Related pak-files have to be placed in * * * pak-something/ * * * directly. * * @subsection sec_description_tab The file description.tab * * This is a plain text file with the following entries:
* * title=Name of tool to be shown in tool selection dialog
* type=one_click or two_click
* tooltip=Tooltip for the icon in the toolbar
* restart=Set to 1 if the virtual machine has to be restarted after each call to the work functions.
* menu=Parameter that can be used to load tools with menuconf.tab
* icon=Name of cursor object (loaded from some pak-file), used images: 0 = cursor, 1 = icon, 2 = marker image
*
* * @section s_general_advice General scripting advice * * Check out the sections on the Modules page. * See also @ref pitfalls. * * @subsection sec_data_types Squirrel data types * * Squirrel has some built-in data types: among them integer, string, tables, and arrays. These are * widely used throughout this documentation in the function declarations. * * If tables or arrays carry template parameters, this indicates the expected type of elements of the array or table. * See factory_x::input for an example. * * @subsection sec_coord Coordinates * * All coordinates in the script are with respect to the initial rotation of the map. If a player rotates * a map, then all coordinates are translated to the original rotation. * This affects the classes @ref coord and @ref coord3d as well as any functions that expect coordinates as input, as * for instance factory_x::factory_x or rules::forbid_way_tool_rect. * * @subsection sec_network Network play * * A lot of stuff is possible for network play. There are some limitations of course. These are due to the fact, that * the scripts are solely run on the server, the client does not need to have the script available at all. * This implies for instance that all scenario texts are translated to the server's language. * * @subsection sec_err Logging and error handling * If simutrans is started with '-debug 2', all errors and warnings are logged to standard output (i.e. terminal). * If simutrans is started in addition with '-log', all the output is written to the file script.log. In particular, * everything that is print-ed by the script, goes into this file. * * In case of error, an error window pops up showing the call stack and the values of local variables. * You can then repair your script, and restart the scenario via New Game - Scenario. You do not need to restart * simutrans to reload your script. * * @subsection sec_rdwr Load-Save support * Such support is available of course. If a running scenario is saved, the information about the scenario is saved within the * savegame. Upon loading, the scenario is resumed, by calling the function ::resume_game. * * You can save data in the savegame. In order to do so, you have to keep these data in the global table ::persistent. * This table is saved and loaded. * * */ /** * @example millionaire/scenario.nut * This example is commented in detail at the page @ref page_script_mill. */ /** * @page page_script_mill A simple example script * * Let us inspect a sample script file. We will have a look into the pak64 Millionaire scenario. * The objective of this scenario is to get rich as fast as possible. * * @dontinclude millionaire/scenario.nut * As first the savegame is specified: * @skipline map.file * Then we provide some meta-information about the ::scenario * @skipline scenario * @skipline scenario * @skipline scenario * Rules and goals are not that special. The texts returned in this functions * will be shown in the scenario info window. String constants with leading * '@' can span multiple lines. * @until } * @until } * @until } * The method presenting the result text is a little bit more advanced. If the * player has enough cash, he has won the scenario. * @until } * The get_cash() method accesses the player's bank account via player_x.cash, which * is equivalent to calling player_x.get_cash(). * @until } * Last but not least, the function that computes the actual progress in the scenario. * If it returns a negative value, the player has lost. If the return value is larger than 100, * this is equivalent to winning the scenario. * @until } */ /** * @example pharmacy-max/scenario.nut * This example is used to explain translatable texts at the page @ref page_script_pharm. */ /** * @page page_script_pharm A script showing translatable texts. * * Let us inspect another scenario script file. We will have a look into the pak64 Pharmacy-max scenario. * The objective is to get the pharmacy selling as much medicine as possible. * * We will learn how to make the translation of the script's output possible. * * @dontinclude pharmacy-max/scenario.nut * We do not care about the meta information, and jump right into the @ref get_rule_text method. * @skip get_rule_text * @until } * This function does not return a plain string, it sends the string into an instance of @ref ttext. * When the simutrans program now fetches the text, it will magically translate it. * This magic works only if there are translation files present. * * These files must be placed in a directory with the same name as the scenario, in our case at * * pharmacy-max/ * * The translation files itself are plain text files with the naming convention iso.tab, where @em iso * refers to the ISO abbreviation of the language (en, de, cz etc). * * These text files have a simple line-oriented structure. Lets look into de.tab: * * @code * You can build everything you like. The sky (and your bank account) is the limit. * Alles ist erlaubt. Der Himmel (und das Bankkonto) ist die Grenze. * @endcode * * The first line is the string as it appears in the script, it will be mapped to the string in the following line. * A German user thus will read 'Alles ist erlaubt. Der Himmel (und das Bankkonto) ist die Grenze.'. * * Similarly, the methods @ref get_info_text and @ref get_goal_text are implemented. * Some interesting stuff however happens in @ref get_result_text : * @skip get_result_text * @until } * Do you see the strange {med} string in the text? Here, you can see variable substitution in action: * @until text.med * That means the text now gets to know the precise value of {med}. * * When this text is transferred to simutrans (or when text.to_string() is called) the following happens: * -# First the translation of the string is searched, which can contain this {med} as well. * -# Then the occurrences of {med} are replaced by the concrete number. * The user will then hear 'The pharmacy sold 11 units of medicine per month.'. * * Now a second string is created, which features some html-like tags. * @until } * Both of the strings are translated by means of calls to ttext::to_string and the result is returned. * * That's it. The remaining parts of this script are plain routine. */ /** * @page pitfalls Common pitfalls * * @subsection pitfall_pass_by_reference Tables, arrays, strings, classes, instances etc are passed by reference * * @code * local a = {} // create an empty table * local b = a // b refers to the same table as a * a.x <- 1 // b.x is now also 1 * b.x <- 2 // a.x is changed to 2 * @endcode * * * @subsection nocalls_in_global_scope Do not call API-functions in global scope in the script * * @code * local start_time = settings.get_start_time() // this call is executed BEFORE the savegame is loaded -> undefined * * local factory_coalmine = null // we can create the variable ... * function start() * { * factory_coalmine = factory_x(40, 78) // ... but we can initialize it safely only in a function * } * @endcode * */ /** * @defgroup scen_skel Scenario interface * * The following methods are vital for the functioning of a scripted scenario. * They will be called from simutrans to interact with the script. You should consider * implementing them. * */ /** * @defgroup scen_only Scenario only functions * * These classes and methods are only available for scripted scenarios. * */ /** * @defgroup ai_skel AI interface * * The following methods are vital for the functioning of a scripted AI. * They will be called from simutrans to interact with the script. You should consider * implementing them. * */ /** * @defgroup ai_only AI only functions * * These classes and methods are only available for scripted AI players. * */ /** * @defgroup tool_skel Tool interface * * The following methods are vital for the functioning of scripted tools. * They will be called from simutrans to interact with the script. You should consider * implementing them. * */ /** * @defgroup tool_only Tool only functions * * These classes and methods are only available for scripted scenarios. * */ /** * @defgroup game_cmd Functions to alter the state of the game and map * * The player parameter in these functions represents the player that executes the command * and pays for it. If the call is from an AI player then the parameter is set to player_x::self, * and it will be checked whether the * player is permitted to execute the command. Calls from scenario always pass this check. * * In network games, the script is suspended until the command is executed, which is transparent to the script. * Hence such commands cannot be called from within functions that should return immediately, see @ref quick_return_func. */ /** * @defgroup rename_func Functions to rename something in the game * @ingroup game_cmd * * In AI mode, renaming works only if the object is owned by the script's player. */ /** * @defgroup quick_return_func Functions that should return quickly. * * These functions are intended to quickly return a result. In network games, it is not * allowed to call any map-altering tools from within such a function, see the section on @ref game_cmd. */ /** * @page page_sqstdlib Available functions from the squirrel standard lib * * - Math Library, see http://squirrel-lang.org/squirreldoc/stdlib/stdmathlib.html#squirrel-api * - String Library, see http://squirrel-lang.org/squirreldoc/stdlib/stdstringlib.html#squirrel-api * - System Library: functions clock, time, date, see http://squirrel-lang.org/squirreldoc/stdlib/stdsystemlib.html#squirrel-api * */ simutrans-124.5/src/simutrans/script/api/api_factory.cc000066400000000000000000000344051520675460500232720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_factory.cc exports factory related functions. */ #include "get_next.h" #include "api_obj_desc_base.h" #include "../api_class.h" #include "../api_function.h" #include "../../dataobj/scenario.h" #include "../../simfab.h" #include "../../descriptor/factory_desc.h" using namespace script_api; SQInteger exp_factory_constructor(HSQUIRRELVM vm) { sint16 x = param::get(vm, 2); sint16 y = param::get(vm, 3); // set coordinates set_slot(vm, "x", x, 1); set_slot(vm, "y", y, 1); // transform coordinates koord pos(x,y); coordinate_transform_t::koord_sq2w(pos); fabrik_t *fab = fabrik_t::get_fab(pos); if (!fab) { return sq_raise_error(vm, "No factory found at (%s)", pos.get_str()); } const factory_desc_t *desc = fab->get_desc(); // create input/output tables for (int io=0; io<2; io++) { sq_pushstring(vm, io==0 ? "input" : "output", -1); sq_newtable(vm); const array_tpl &prodslot = io==0 ? fab->get_input() :fab->get_output(); for(uint32 p=0; p < prodslot.get_count(); p++) { // create slots 'good name' <- {x,y,name} //'factory_production' sq_pushstring(vm, prodslot[p].get_typ()->get_name(), -1); // create instance of factory_production_x if(!SQ_SUCCEEDED(push_instance(vm, "factory_production_x", x, y, prodslot[p].get_typ()->get_name(), p + (io > 0 ? fab->get_input().get_count() : 0)))) { // create empty table sq_newtable(vm); } sint64 factor = io == 0 ? desc->get_supplier(p)->get_consumption() : desc->get_product(p)->get_factor(); // set max value - see fabrik_t::info_prod set_slot(vm, "max_storage", convert_goods( (sint64)prodslot[p].max * factor), -1); // production/consumption scaling set_slot(vm, "scaling", factor, -1); // put class into table sq_newslot(vm, -3, false); } sq_set(vm, 1); } return 0; // dummy return type } vector_tpl const& get_factory_stat(fabrik_t *fab, sint32 INDEX) { static vector_tpl v; v.clear(); if (fab && 0<=INDEX && INDEXget_stat_converted(i, INDEX)); } } return v; } vector_tpl const& get_factory_production_stat(const ware_production_t *prod_slot, sint32 INDEX) { static vector_tpl v; v.clear(); if (prod_slot && 0<=INDEX && INDEXget_stat_converted(i, INDEX)); } } return v; } uint32 get_production_factor(const factory_product_desc_t *desc) { return desc ? ( (1<< (DEFAULT_PRODUCTION_FACTOR_BITS-1)) + (uint32)desc->get_factor() * 100) >> DEFAULT_PRODUCTION_FACTOR_BITS : 0; } uint32 get_consumption_factor(const factory_supplier_desc_t *desc) { return desc ? ( (1<< (DEFAULT_PRODUCTION_FACTOR_BITS-1)) + (uint32)desc->get_consumption() * 100) >> DEFAULT_PRODUCTION_FACTOR_BITS : 0; } vector_tpl const& factory_get_tile_list(fabrik_t *fab) { static vector_tpl list; fab->get_tile_list(list); static vector_tpl tile_list; tile_list.clear(); for(koord k : list) { tile_list.append(welt->lookup_kartenboden(k)); } return tile_list; } vector_tpl const& factory_get_fields_list(fabrik_t *fab) { static vector_tpl list; fab->get_fields_list(list); return list; } vector_tpl const& square_get_halt_list(planquadrat_t *plan); // api_tiles.cc vector_tpl const& factory_get_halt_list(fabrik_t *fab) { planquadrat_t *plan = welt->access(fab->get_pos().get_2d()); return square_get_halt_list(plan); } leitung_t* factory_get_transformer( fabrik_t* fab ) { return fab->get_transformers().empty() ? NULL :fab->get_transformers().front(); } call_tool_init factory_set_name(fabrik_t *fab, const char* name) { return command_rename(welt->get_public_player(), 'f', fab->get_pos(), name); } const char* fabrik_get_raw_name(fabrik_t *fab) { return fab->get_desc()->get_name(); } SQInteger ware_production_get_production(HSQUIRRELVM vm) { fabrik_t* fab = param::get(vm, 1); sint64 prod = 0; if (fab) { sint64 scaling = 0; if (SQ_SUCCEEDED(get_slot(vm, "scaling", scaling, 1))) { // see fabrik_t::step prod = (scaling * welt->scale_with_month_length(fab->get_base_production()) * DEFAULT_PRODUCTION_FACTOR ) >> (8+8); } } return param::push(vm, prod); } SQInteger world_get_next_factory(HSQUIRRELVM vm) { return generic_get_next(vm, welt->get_fab_list().get_count()); } SQInteger world_get_factory_by_index(HSQUIRRELVM vm) { uint32 index = param::get(vm, -1); fabrik_t *fab = welt->get_fab(index); return param::push(vm, fab); } SQInteger world_get_factory_count(HSQUIRRELVM vm) { return param::push(vm, welt->get_fab_list().get_count()); } void export_factory(HSQUIRRELVM vm) { /** * Implements iterator to iterate through the list of all factories on the map. * * Usage: * @code * local list = factory_list_x() * foreach(factory in list) { * ... // factory is an instance of the factory_x class * } * @endcode */ begin_class(vm, "factory_list_x", 0); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, world_get_next_factory, "_nexti", 2, "x o|i"); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, world_get_factory_by_index, "_get", 2, "xi"); /** * Returns number of factories in the list. * @typemask integer() */ register_function(vm, world_get_factory_count, "get_count", 1, "x"); end_class(vm); /** * Class to access information about factories. * Identified by coordinate. */ begin_class(vm, "factory_x", "coord,extend_get,ingame_object"); /** * Constructor. * @param x x-coordinate * @param y y-coordinate * @typemask (integer,integer) */ register_function(vm, exp_factory_constructor, "constructor", 3, "xii"); #ifdef SQAPI_DOC // document members /** * Table containing input/consumption slots. * Entries can be retrieved by raw name of the good (as defined in the associated pak-file). * * Entries are of type factory_production_x. * * To print a list of all available goods names use this example: * @code * foreach(key,value in input) { * // print raw name of the good * print("Input slot key: " + key) * // print current storage * print("Input slot storage: " + value.get_storage()[0]) * } * @endcode * To catch the output of this example see @ref sec_err. */ table input; /** * Table containing output/production slots. * Entries can be retrieved by raw name of the good (as defined in the associated pak-file). * * Entries are of type factory_production_x. * * For an example to retrieve the list of goods, see factory_x::input. */ table output; #endif /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") /** * Get list of consumers of this factory. * @returns array of coordinates of consumers */ register_method(vm, &fabrik_t::get_consumer, "get_consumers"); /** * Get list of consumers of this factory. * @returns array of coordinates of suppliers */ register_method(vm, &fabrik_t::get_suppliers, "get_suppliers"); /** * Get (translated or custom) name of factory. * @returns name */ register_method(vm, &fabrik_t::get_name, "get_name"); /** * Get untranslated name of factory. * @returns name */ register_method(vm, &fabrik_get_raw_name, "get_raw_name", true); /** * Change name. * @ingroup rename_func */ register_method(vm, &factory_set_name, "set_name", true); /** * Get monthly statistics of production. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_production", freevariable(FAB_PRODUCTION), true); /** * Get monthly statistics of power consumption/production. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_power", freevariable(FAB_POWER), true); /** * Get monthly statistics of production boost due to electric power supply. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_boost_electric", freevariable(FAB_BOOST_ELECTRIC), true); /** * Get monthly statistics of production boost due to arrived passengers. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_boost_pax", freevariable(FAB_BOOST_PAX), true); /** * Get monthly statistics of production boost due to arrived mail. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_boost_mail", freevariable(FAB_BOOST_MAIL), true); /** * Get monthly statistics of generated passengers. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_pax_generated", freevariable(FAB_PAX_GENERATED), true); /** * Get monthly statistics of departed passengers. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_pax_departed", freevariable(FAB_PAX_DEPARTED), true); /** * Get monthly statistics of arrived passengers. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_pax_arrived", freevariable(FAB_PAX_ARRIVED), true); /** * Get monthly statistics of generated mail. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_mail_generated", freevariable(FAB_MAIL_GENERATED), true); /** * Get monthly statistics of departed mail. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_mail_departed", freevariable(FAB_MAIL_DEPARTED), true); /** * Get monthly statistics of arrived mail. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_stat, "get_mail_arrived", freevariable(FAB_MAIL_ARRIVED), true); /** * Get list of all tiles occupied by buildings belonging to this factory. * @returns array of tile_x objects */ register_method(vm, &factory_get_tile_list, "get_tile_list", true); /** * Get list of all tiles occupied by fields belonging to this factory. * @returns array of tile_x objects */ register_method(vm, &factory_get_fields_list, "get_fields_list", true); /** * Get list of all halts that serve this this factory. * @returns array of tile_x objects */ register_method(vm, &factory_get_halt_list, "get_halt_list", true); /** * Checks whether a transformer is connected. * @returns name */ register_method(vm, &fabrik_t::is_transformer_connected, "is_transformer_connected"); /** * Get connected transformer (if any). * @returns transformer */ register_method(vm, factory_get_transformer, "get_transformer", true); /** * @returns number of fields belonging to this factory */ register_method(vm, &fabrik_t::get_field_count, "get_field_count"); /** * @returns minimum number of fields required */ register_method(vm, &fabrik_t::get_min_field_count, "get_min_field_count"); /** * @returns factory descriptor */ register_method(vm, &fabrik_t::get_desc, "get_desc"); // pop class end_class(vm); /** * Class to access storage slots of factories. * Are automatically instantiated by factory_x constructor. */ begin_class(vm, "factory_production_x", "extend_get"); #ifdef SQAPI_DOC // document members /** * Maximum storage of this slot. */ integer max_storage; #endif /** * Get monthly statistics of storage. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_production_stat, "get_storage", freevariable(FAB_GOODS_STORAGE), true); /** * Get monthly statistics of received goods (for input slots). * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_production_stat, "get_received", freevariable(FAB_GOODS_RECEIVED), true); /** * Get monthly statistics of consumed goods (for input slots). * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_production_stat, "get_consumed", freevariable(FAB_GOODS_CONSUMED), true); /** * Get monthly statistics of in-transit goods (for input slots). * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_production_stat, "get_in_transit",freevariable(FAB_GOODS_TRANSIT), true); /** * Get monthly statistics of delivered goods (for output slots). * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_production_stat, "get_delivered", freevariable(FAB_GOODS_DELIVERED), true); /** * Get monthly statistics of produced goods (for output slots). * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_factory_production_stat, "get_produced", freevariable(FAB_GOODS_PRODUCED), true); /** * Returns base maximum production of this good per month. * Does not take any productivity boost into account. * @typemask integer() */ register_function(vm, &ware_production_get_production, "get_base_production", 1, "x"); /** * Returns base maximum consumption of this good per month. * Does not take any productivity boost into account. * @typemask integer() */ register_function(vm, &ware_production_get_production, "get_base_consumption", 1, "x"); /** * Returns number of consumed units of this good for the production of 100 units of generic outputs unit. * Does not take any productivity boost into account. * @typemask integer() */ register_method(vm, &get_consumption_factor, "get_consumption_factor", 1, "x"); /** * Returns number of produced units of this good due to the production of 100 units of generic outputs unit. * Does not take any productivity boost into account. * @typemask integer() */ register_method(vm, &get_production_factor, "get_production_factor", 1, "x"); // pop class end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_gui.cc000066400000000000000000000111171520675460500224020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_gui.cc exports gui-related functions. */ #include "../api_class.h" #include "../api_function.h" #include "api_simple.h" #include "../../simmesg.h" #include "../../tool/simmenu.h" #include "../../world/simworld.h" #include "../../dataobj/scenario.h" #include "../../player/simplay.h" #include "../../dataobj/environment.h" #include "../../network/network.h" #include "../../network/network_cmd_scenario.h" using namespace script_api; call_tool_work add_scenario_message_at(const char* text, my_koord3d pos) { // build param string (see tool_add_message_t::init) cbuffer_t buf; buf.printf("%d,%s", message_t::scenario, text); return call_tool_work(TOOL_ADD_MESSAGE | GENERAL_TOOL, (const char*)buf, 0, welt->get_active_player(), pos).set_no_block(true); } call_tool_work add_ai_message_at(player_t *player, const char* text, my_koord3d pos) { // build param string (see tool_add_message_t::init) cbuffer_t buf; buf.printf("%d,%s", message_t::ai, text); return call_tool_work(TOOL_ADD_MESSAGE | GENERAL_TOOL, (const char*)buf, 0, player, pos).set_no_block(true); } call_tool_work add_scenario_message(player_t* player, const char* text) { // build param string (see tool_add_message_t::init) cbuffer_t buf; buf.printf("%d,%s", message_t::scenario, text); return call_tool_work(TOOL_ADD_MESSAGE | GENERAL_TOOL, (const char*)buf, 0, player ? player : welt->get_active_player(), koord3d::invalid).set_no_block(true); } void open_info_win_client(const char* tab, uint8 player_nr) { if (env_t::server) { // void network_send_all(network_command_t* nwc, bool exclude_us ) nwc_scenario_t *nwc = new nwc_scenario_t(); nwc->what = nwc_scenario_t::OPEN_SCEN_WIN; nwc->function = tab; network_send_all(nwc, false, player_nr); } else { welt->get_scenario()->open_info_win(tab); } } void open_info_win_at(const char* tab) { open_info_win_client(tab, PLAYER_UNOWNED); } void open_info_win() { open_info_win_client("", PLAYER_UNOWNED); } void export_gui(HSQUIRRELVM vm, bool scenario) { /** * Table with methods to access gui functions. */ begin_class(vm, "gui", 0); if (scenario) { /** * Opens scenario info window and shows 'info' tab. * In network mode, opens window on all clients and server. * @note Only available in scenario mode. */ STATIC register_method(vm, open_info_win, "open_info_win", true); /** * Opens scenario info window with specific tab open. * In network mode, opens window on all clients and server. * @param tab possible values are "info", "goal", "rules", "result", "about", "debug" * @note Only available in scenario mode. */ STATIC register_method(vm, open_info_win_at, "open_info_win_at"); /** * Opens scenario info window for certain clients (and the server), * with specific tab open. * @param tab possible values are "info", "goal", "rules", "result", "about", "debug" * @param player_nr opens scenario info window on all clients that have this player unlocked. * @note Window is always opened on server. */ STATIC register_method(vm, open_info_win_client, "open_info_win_client"); /** * Adds message to the players mailboxes. * Will be shown in ticker or as pop-up window depending on players preferences. * Message window has small view of world. * * @param text Text to be shown. Has to be a translated string or a translatable string. * @param position Position of the view on the map. Clicking on the message will center viewport at this position. * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &add_scenario_message_at, "add_message_at"); /** * Adds message to the players mailboxes. * Will be shown in ticker or as pop-up window depending on players preferences. * * @param text Text to be shown. Has to be a translated string or a translatable string. * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &add_scenario_message, "add_message"); } else { /** * Adds message to the players mailboxes. * Will be shown in ticker or as pop-up window depending on players preferences. * Message window has small view of world. * * @param player sending this message * @param text Text to be shown. Has to be a translated string or a translatable string. * @param position Position of the view on the map. Clicking on the message will center viewport at this position. * @ingroup ai_only */ STATIC register_method(vm, &add_ai_message_at, "add_message_at"); } end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_halt.cc000066400000000000000000000226471520675460500225600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_halt.cc exports halt/station related functions. */ #include "get_next.h" #include "api_obj_desc_base.h" #include "../api_class.h" #include "../api_function.h" #include "../../simhalt.h" #include "../../world/simworld.h" halthandle_t get_halt_from_koord3d(koord3d pos, const player_t *player ); // api_schedule.cc, interfaces haltestelle_t::get_halt namespace script_api { declare_specialized_param(haltestelle_t::tile_t, "t|x|y", "tile_x"); declare_specialized_param(haltestelle_t::connection_t, "t|x|y", "halt_x"); SQInteger param::push(HSQUIRRELVM vm, haltestelle_t::tile_t const& v) { return param::push(vm, v.grund); } SQInteger param::push(HSQUIRRELVM vm, haltestelle_t::connection_t const& v) { return param::push(vm, v.halt); } }; using namespace script_api; vector_tpl const& get_halt_stat(const haltestelle_t *halt, sint32 INDEX) { static vector_tpl v; v.clear(); if (halt && 0<=INDEX && INDEXget_finance_history(i, INDEX) ); } } return v; } bool is_rerouting_finished() { return haltestelle_t::get_rerouting_status() == 0 && haltestelle_t::get_reconnect_counter() == world()->get_schedule_counter(); } SQInteger world_get_next_halt(HSQUIRRELVM vm) { return generic_get_next(vm, haltestelle_t::get_alle_haltestellen().get_count()); } SQInteger world_get_halt_by_index(HSQUIRRELVM vm) { sint32 index = param::get(vm, -1); const vector_tpl& list = haltestelle_t::get_alle_haltestellen(); halthandle_t halt = (0<=index && (uint32)index::push(vm, halt); } SQInteger halt_export_convoy_list(HSQUIRRELVM vm) { halthandle_t halt = param::get(vm, 1); if (halt.is_bound()) { push_instance(vm, "convoy_list_x"); set_slot(vm, "halt_id", halt.get_id()); return 1; } else { sq_raise_error(vm, "Invalid halt id %d", halt.get_id()); return SQ_ERROR; } } call_tool_init halt_set_name(halthandle_t halt, const char* name) { if (!halt.is_bound()) { return "Invalid halt provided"; } return command_rename(halt->get_owner(), 'h', halt.get_id(), name); } SQInteger halt_export_line_list(HSQUIRRELVM vm) { halthandle_t halt = param::get(vm, 1); if (halt.is_bound()) { push_instance(vm, "line_list_x"); set_slot(vm, "halt_id", halt.get_id()); return 1; } else { sq_raise_error(vm, "Invalid halt id %d", halt.get_id()); return SQ_ERROR; } } uint32 halt_get_capacity(const haltestelle_t *halt, const goods_desc_t *desc) { // passenger has index 0, mail 1, everything else >=2 return halt && desc ? halt->get_capacity(min( desc->get_catg_index(), 2 )) : 0; } // 0: not connected // 1: connected // -1: undecided sint8 is_halt_connected(const haltestelle_t *a, halthandle_t b, const goods_desc_t *desc) { if (desc == 0 || a == NULL || !b.is_bound()) { return 0; } return a->is_connected(b, desc->get_catg_index()); } // compare two halts to find out if they are the same SQInteger halt_compare(halthandle_t a, halthandle_t b) { return (SQInteger)a.get_id() - (SQInteger)b.get_id(); } vector_tpl const& halt_get_connections(const haltestelle_t *halt, const goods_desc_t* freight) { static vector_tpl dummy; dummy.clear(); return freight ? halt->get_connections(freight->get_catg_index()) : dummy; } void export_halt(HSQUIRRELVM vm) { /** * Implements iterator to iterate through the list of all halts on the map. * * Usage: * @code * local list = halt_list_x() * foreach(halt in list) { * ... // halt is an instance of the halt_x class * } * @endcode */ create_class(vm, "halt_list_x"); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, world_get_next_halt, "_nexti", 2, "x o|i"); /** * Meta-method to be used in foreach loops. Do not call them directly. * @typemask halt_x() */ register_function(vm, world_get_halt_by_index, "_get", 2, "xi"); end_class(vm); /** * Class to access halts / stations / bus stops. */ begin_class(vm, "halt_x", "extend_get,ingame_object"); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") /** * Station name. * @returns name */ register_method(vm, &haltestelle_t::get_name, "get_name"); /** * Sets station name. * @ingroup rename_func */ register_method(vm, &halt_set_name, "set_name", true); /** * Station owner. * @returns owner */ register_method(vm, &haltestelle_t::get_owner, "get_owner"); /** * compare classes using metamethods * @param halt the other halt * @returns difference in the unique id of the halthandle */ register_method(vm, &halt_compare, "_cmp", true); /** * Quick check if there is connection for certain freight to the other halt. * @param halt the other halt * @param freight_type freight type * @return 0 - not connected, 1 - connected, -1 - undecided (call this method again later) */ register_method(vm, &is_halt_connected, "is_connected", true); /** * Does this station accept this type of good? * @param freight_type freight type * @returns the answer to this question */ register_method(vm, &haltestelle_t::is_enabled, "accepts_good", false); /** * Get monthly statistics of number of arrived goods. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_arrived", freevariable(HALT_ARRIVED), true); /** * Get monthly statistics of number of departed goods. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_departed", freevariable(HALT_DEPARTED), true); /** * Get monthly statistics of number of waiting goods. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_waiting", freevariable(HALT_WAITING), true); /** * Get monthly statistics of number of happy passengers. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_happy", freevariable(HALT_HAPPY), true); /** * Get monthly statistics of number of unhappy passengers. * * These passengers could not start their journey as station was crowded. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_unhappy", freevariable(HALT_UNHAPPY), true); /** * Get monthly statistics of number of passengers with no-route. * * These passengers could not start their journey as they find no route to their destination. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_noroute", freevariable(HALT_NOROUTE), true); /** * Get monthly statistics of number of convoys that stopped at this station. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_convoys", freevariable(HALT_CONVOIS_ARRIVED), true); /** * Get monthly statistics of number of passengers that could walk to their destination. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_walked", freevariable(HALT_WALKED), true); /** * Exports list of convoys that stop at this halt. * @typemask convoy_list_x() */ register_function(vm, &halt_export_convoy_list, "get_convoy_list", 1, param::typemask()); /** * Exports list of lines that serve this halt. * @typemask line_list_x() */ register_function(vm, &halt_export_line_list, "get_line_list", 1, param::typemask()); /** * Get list of tiles belonging to this station. */ register_method(vm, &haltestelle_t::get_tiles, "get_tile_list"); /** * Get list of factories connected to this station. */ register_method(vm, &haltestelle_t::get_fab_list, "get_factory_list"); /** * Returns amount of @p freight at this halt that is going to @p target * @param freight freight type * @param target coordinate of target */ register_method(vm, &haltestelle_t::get_ware_fuer_zielpos, "get_freight_to_dest"); /** * Returns amount of @p freight at this halt that scheduled to @p stop * @param freight freight type * @param stop next transfer stop */ register_method(vm, &haltestelle_t::get_ware_fuer_zwischenziel, "get_freight_to_halt"); /** * Returns capacity of this halt for the given @p freight * @param freight freight type */ register_method(vm, &halt_get_capacity, "get_capacity", true); /** * Returns list of connected halts for the specific @p freight type. * @param freight freight type */ register_method(vm, &halt_get_connections, "get_connections", true); /** * Returns halt at given position. * @param pos coordinate * @param pl player that wants to use halt here * @return halt instance */ STATIC register_method(vm, &get_halt_from_koord3d, "get_halt", false, true); /** * Checks if the rerouting of halts is finsihed * @return true if all reconnections are finished */ STATIC register_method(vm, &is_rerouting_finished, "is_rerouting_finished", false, true); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_include.cc000066400000000000000000000040061520675460500232400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "../api_function.h" #include "../../../squirrel/sqstdio.h" // for loadfile #include "../../../squirrel/sq_extensions.h" // for sq_call_restricted #include "../../sys/simsys.h" #include "../../dataobj/environment.h" /** @file api_include.cc exports include. */ using namespace script_api; SQInteger include_aux(HSQUIRRELVM vm) { const char* filename = param::get(vm, 2); const char* include_path = param::get(vm, 3); if (filename == NULL) { return sq_raise_error(vm, "Include got filename"); } // path to scenario files cbuffer_t buf; buf.printf("%s%s%s.nut", include_path, PATH_SEPARATOR, filename); struct stat dummy; if (dr_stat(buf,&dummy)) { // file does not exist, try other path buf.clear(); buf.printf("%sscript%s%s.nut", env_t::base_dir, PATH_SEPARATOR, filename); } // load script if (!SQ_SUCCEEDED(sqstd_loadfile(vm, (const char*)buf, true))) { return sq_raise_error(vm, "Reading / compiling script %s failed", filename); } // call it sq_pushroottable(vm); if (!SQ_SUCCEEDED(sq_call_restricted(vm, 1, SQFalse, SQTrue, 100000))) { sq_pop(vm, 1); // pop script return sq_raise_error(vm, "Call script %s failed", filename); } if (sq_getvmstate(vm) == SQ_VMSTATE_SUSPENDED) { return sq_raise_error(vm, "Calling scriptfile %s took too long", filename); } sq_pop(vm, 1); // pop script return SQ_OK; } void export_include(HSQUIRRELVM vm, const char* include_path) { sq_pushroottable(vm); /** * Includes the given file. * The file will be included in global scope. * Inclusion happens when the include-command gets executed. * No compile time inclusion! * @param filename name of file to be included (relative to scenario directory, without .nut extension) * @typemask void(string) */ register_function_fv(vm, &include_aux, "include", 2, ".s", freevariable(include_path)); sq_pop(vm, 1); // root table } simutrans-124.5/src/simutrans/script/api/api_line.cc000066400000000000000000000201411520675460500225420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_line.cc exports line related functions. */ #include "get_next.h" #include "api_obj_desc_base.h" #include "../api_class.h" #include "../api_function.h" #include "../../simhalt.h" #include "../../simline.h" #include "../../world/simworld.h" #include "../../player/simplay.h" // for manipulation of lines #include "../../tool/simmenu.h" #include "../../dataobj/schedule.h" // template<> schedule_t* script_api::param::get(HSQUIRRELVM, SQInteger); using namespace script_api; vector_tpl const& get_line_stat(simline_t *line, sint32 INDEX) { static vector_tpl v; v.clear(); if (line && 0<=INDEX && INDEXget_stat_converted(i, INDEX) ); } } return v; } waytype_t line_way_type(simline_t *line) { if (line) { switch (line->get_linetype()) { case simline_t::truckline: return road_wt; case simline_t::trainline: return track_wt; case simline_t::shipline: return water_wt; case simline_t::monorailline: return monorail_wt; case simline_t::maglevline: return maglev_wt; case simline_t::narrowgaugeline: return narrowgauge_wt; case simline_t::airline: return air_wt; case simline_t::tramline: return tram_wt; default: ; } } return invalid_wt; } call_tool_init line_change_schedule(simline_t* line, player_t *player, schedule_t *sched) { if (sched) { cbuffer_t buf; // make a copy, and perform validation on it schedule_t *copy = sched->copy(); copy->make_valid(); if (copy->get_count() >= 2) { // build param string (see line_management_gui_t::apply_schedule) buf.printf( "g,%i,", line->get_handle().get_id() ); copy->sprintf_schedule( buf ); } else { return "Invalid schedule provided: less than two entries remained after removing doubles"; } delete copy; return call_tool_init(TOOL_CHANGE_LINE | SIMPLE_TOOL, buf, 0, player); } return "Invalid schedule provided"; } call_tool_init line_delete(simline_t* line, player_t *player) { if (line->count_convoys() == 0) { cbuffer_t buf; buf.printf( "d,%i", line->get_handle().get_id() ); return call_tool_init(TOOL_CHANGE_LINE | SIMPLE_TOOL, buf, 0, player); } return "Cannot delete lines with associated convoys"; } SQInteger line_export_convoy_list(HSQUIRRELVM vm) { linehandle_t line = param::get(vm, 1); if (line.is_bound()) { push_instance(vm, "convoy_list_x"); set_slot(vm, "line_id", line.get_id()); return 1; } sq_raise_error(vm, "Invalid line handle provided"); return SQ_ERROR; } call_tool_init line_set_name(simline_t* line, const char* name) { return command_rename(line->get_owner(), 'l', line->get_handle().get_id(), name); } vector_tpl const* generic_get_line_list(HSQUIRRELVM vm, SQInteger index) { uint16 id; if (SQ_SUCCEEDED(get_slot(vm, "halt_id", id, index))) { halthandle_t halt; halt.set_id(id); if (halt.is_bound()) { return &halt->registered_lines; } } if (SQ_SUCCEEDED(get_slot(vm, "player_id", id, index)) && id < PLAYER_UNOWNED) { if (player_t *player = welt->get_player(id)) { return &player->simlinemgmt.get_line_list(); } } sq_raise_error(vm, "Invalid line list."); return NULL; } SQInteger generic_get_next_line(HSQUIRRELVM vm) { vector_tpl const* list = generic_get_line_list(vm, 1); return list ? generic_get_next(vm, list->get_count()) : SQ_ERROR; } SQInteger generic_get_line_by_index(HSQUIRRELVM vm) { vector_tpl const* list = generic_get_line_list(vm, 1); sint32 index = param::get(vm, 2); linehandle_t line; if (list) { line = (0<=index && (uint32)index < list->get_count()) ? (*list)[index] : linehandle_t(); return push_instance(vm, "line_x", line.is_bound() ? line.get_id() : 0); } return SQ_ERROR; } SQInteger generic_get_line_count(HSQUIRRELVM vm) { vector_tpl const* list = generic_get_line_list(vm, 1); return param::push(vm, list ? list->get_count() : 0); } void export_line(HSQUIRRELVM vm) { /** * Implements iterator to iterate through lists of lines. * * Usage: * @code * local list = player_x(0).get_line_list() * foreach(line in list) { * ... // line is an instance of the line_x class * } * @endcode * * @see player_x::get_line_list, halt_x::get_line_list */ begin_class(vm, "line_list_x", 0); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, generic_get_next_line, "_nexti", 2, "x o|i"); /** * Meta-method to be used in foreach loops. Do not call them directly. * @typemask line_x() */ register_function(vm, generic_get_line_by_index, "_get", 2, "xi"); /** * Returns number of lines in the list. * @typemask integer() */ register_function(vm, generic_get_line_count, "get_count", 1, "x"); end_class(vm); /** * Class to access lines. */ begin_class(vm, "line_x", "extend_get,ingame_object"); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") /** * Line name. * @returns name */ register_method(vm, &simline_t::get_name, "get_name"); /** * Sets line name. * @ingroup rename_func * @typemask void(string) */ register_method(vm, &line_set_name, "set_name", true); /** * Line owner. * @returns owner */ register_method(vm, &simline_t::get_owner, "get_owner"); /** * Schedule of this line. */ register_method(vm, &simline_t::get_schedule, "get_schedule"); /** * Returns array of goods categories that can be transported by this convoy. * @returns array */ register_method(vm, &simline_t::get_goods_catg_index, "get_goods_catg_index"); /** * Get monthly statistics of capacity. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_capacity", freevariable(LINE_CAPACITY), true); /** * Get monthly statistics of number of transported goods. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_transported_goods", freevariable(LINE_TRANSPORTED_GOODS), true ); /** * Get monthly statistics of number of convoys in this line. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_convoy_count", freevariable(LINE_CONVOIS), true ); /** * Get monthly statistics of revenue. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_revenue", freevariable(LINE_REVENUE), true ); /** * Get monthly statistics of running costs. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_cost", freevariable(LINE_OPERATIONS), true ); /** * Get monthly statistics of profit. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_profit", freevariable(LINE_PROFIT), true ); /** * Get monthly statistics of traveled distance. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_traveled_distance", freevariable(LINE_DISTANCE), true ); /** * Get monthly statistics of income/loss due to way tolls. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_way_tolls", freevariable(LINE_WAYTOLL), true ); /** * Exports list of convoys belonging to this line. * @typemask convoy_list_x() */ register_function(vm, &line_export_convoy_list, "get_convoy_list", 1, param::typemask()); /** * @return waytype of the line */ register_method(vm, &line_way_type, "get_waytype", true); /** * Change schedule of line. * Schedule should not contain doubled entries and more than two entries. * @ingroup game_cmd */ register_method(vm, line_change_schedule, "change_schedule", true); /** * Delete line * @ingroup game_cmd */ register_method(vm, line_delete, "destroy", true); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_map_objects.cc000066400000000000000000000567161520675460500241220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_map_objects.cc exports all map-objects. */ #include "api_obj_desc_base.h" #include "api_simple.h" #include "../api_class.h" #include "../api_function.h" #include "../../obj/simobj.h" #include "../../obj/depot.h" #include "../../world/simworld.h" #include "../../ground/grund.h" #include "../../dataobj/scenario.h" #include "../../descriptor/ground_desc.h" #include "../../obj/baum.h" #include "../../obj/bruecke.h" #include "../../obj/gebaeude.h" #include "../../obj/field.h" #include "../../obj/label.h" #include "../../obj/leitung2.h" #include "../../obj/roadsign.h" #include "../../obj/signal.h" #include "../../obj/tunnel.h" #include "../../obj/wayobj.h" #include "../../player/simplay.h" // for depot tools #include "../../simconvoi.h" #include "../../tool/simmenu.h" #include "../../descriptor/building_desc.h" #include "../../descriptor/vehicle_desc.h" using namespace script_api; // use pointers to obj_t_tag[ ] to tag the obj_t classes static uint8 obj_t_tag[256]; /* * template struct to bind obj_t::typ to obj_t classes */ template struct bind_code; /* * Class to templatify access of obj_t objects */ template struct access_objs { /* * Access object: check whether object is still on this tile. */ static D* get_by_pos(HSQUIRRELVM vm, SQInteger index) { SQUserPointer tag = obj_t_tag + bind_code::objtype; SQUserPointer p = NULL; if (SQ_SUCCEEDED(sq_getinstanceup(vm, index, &p, tag)) && p) { D *obj = static_cast(p); koord3d pos = param::get(vm, index); grund_t *gr = welt->lookup(pos); if (gr && gr->obj_ist_da(obj)) { return obj; } else { // object or tile disappeared: clear userpointer sq_setinstanceup(vm, index, NULL); } sq_raise_error(vm, "Object of type %s vanished from (%s).", param::squirrel_type(), pos.get_str()); } else { sq_raise_error(vm, "Object is not of type %s.", param::squirrel_type); } return NULL; } /* * Create instance: call constructor with coordinates. */ static SQInteger push_with_pos(HSQUIRRELVM vm, D* const& obj) { if (obj == NULL) { sq_pushnull(vm); return 1; } koord pos = obj->get_pos().get_2d(); coordinate_transform_t::koord_w2sq(pos); sint16 x = pos.x; sint16 y = pos.y; sint8 z = obj->get_pos().z; if (bind_code::objtype == obj_t::obj) { // generic object, object type as fourth parameter if (!SQ_SUCCEEDED(push_instance(vm, script_api::param::squirrel_type(), x, y, z, obj->get_typ()))) { return SQ_ERROR; } } else if (bind_code::objtype != obj->get_typ()) { // obj is instance of derived class return script_api::param::push(vm, obj); } else { // specific object with its own constructor, type already preset as default parameter if (!SQ_SUCCEEDED(push_instance(vm, script_api::param::squirrel_type(), x, y, z))) { return SQ_ERROR; } } sq_setinstanceup(vm, -1, obj); return 1; } }; SQInteger exp_obj_pos_constructor(HSQUIRRELVM vm) // parameters: sint16 x, sint16 y, sint8 z, obj_t::typ type { // get coordinates sint16 x = param::get(vm, 2); sint16 y = param::get(vm, 3); sint8 z = param::get(vm, 4); // set coordinates set_slot(vm, "x", x, 1); set_slot(vm, "y", y, 1); koord pos(x,y); coordinate_transform_t::koord_sq2w(pos); // find tile - some objects have larger z-coordinate (e.g., on foundations) grund_t *gr = welt->lookup(koord3d(pos, z)); for(uint8 i=1, end = ground_desc_t::double_grounds ? 2 : 1; gr == NULL && i<=end; i++) { gr = welt->lookup(koord3d(pos, z-i)); } // find object and set instance up if (gr) { // correct z-coordinate set_slot(vm, "z", gr->get_pos().z, 1); // search for object obj_t::typ type = (obj_t::typ)param::get(vm, 5); obj_t *obj = NULL; if (type == obj_t::roadsign || type == obj_t::signal) { // special treatment of signals/roadsigns obj = gr->suche_obj(obj_t::roadsign); if (obj == NULL) { obj = gr->suche_obj(obj_t::signal); } } else if (type == obj_t::pumpe || type == obj_t::senke) { obj = gr->suche_obj(obj_t::pumpe); if (obj == NULL) { obj = gr->suche_obj(obj_t::senke); } } else if (type != obj_t::old_airdepot) { // special treatment of depots obj = gr->suche_obj(type); } else { obj = gr->get_depot(); } if (obj) { sq_setinstanceup(vm, 1, obj); return SQ_OK; } } return sq_raise_error(vm, "No object of requested type on tile (or no tile at this position)"); } template<> struct bind_code { static const uint8 objtype = obj_t::obj; }; // macro to implement get and push for obj_t's with position #define getpush_obj_pos(D, type) \ D* script_api::param::get(HSQUIRRELVM vm, SQInteger index) \ { \ return access_objs::get_by_pos(vm, index); \ } \ SQInteger script_api::param::push(HSQUIRRELVM vm, D* const& obj) \ { \ return access_objs::push_with_pos(vm, obj); \ } \ template<> struct bind_code { static const uint8 objtype = type; }; // implementation of get and push by macros getpush_obj_pos(baum_t, obj_t::baum); getpush_obj_pos(gebaeude_t, obj_t::gebaeude); getpush_obj_pos(label_t, obj_t::label); getpush_obj_pos(weg_t, obj_t::way); getpush_obj_pos(leitung_t, obj_t::leitung); getpush_obj_pos(field_t, obj_t::field); getpush_obj_pos(wayobj_t, obj_t::wayobj); getpush_obj_pos(bruecke_t, obj_t::bruecke); getpush_obj_pos(tunnel_t, obj_t::tunnel); namespace script_api { // each depot has its own class declare_specialized_param(depot_t*, "t|x|y", "depot_x"); declare_specialized_param(airdepot_t*, "t|x|y", "depot_x"); declare_specialized_param(narrowgaugedepot_t*, "t|x|y", "depot_x"); declare_specialized_param(bahndepot_t*, "t|x|y", "depot_x"); declare_specialized_param(strassendepot_t*, "t|x|y", "depot_x"); declare_specialized_param(schiffdepot_t*, "t|x|y", "depot_x"); declare_specialized_param(monoraildepot_t*, "t|x|y", "depot_x"); declare_specialized_param(tramdepot_t*, "t|x|y", "depot_x"); declare_specialized_param(maglevdepot_t*, "t|x|y", "depot_x"); // map roadsign_t and signal_t to the same class declare_specialized_param(roadsign_t*, "t|x|y", "sign_x"); declare_specialized_param(signal_t*, "t|x|y", "sign_x"); // map all to one transformer class declare_specialized_param(pumpe_t*, "t|x|y", "transformer_x"); declare_specialized_param(senke_t*, "t|x|y", "transformer_x"); }; // base depot class, use old_airdepot as identifier here getpush_obj_pos(depot_t, obj_t::old_airdepot); // now all the derived classes getpush_obj_pos(airdepot_t, obj_t::airdepot); getpush_obj_pos(narrowgaugedepot_t, obj_t::narrowgaugedepot); getpush_obj_pos(bahndepot_t, obj_t::bahndepot); getpush_obj_pos(strassendepot_t, obj_t::strassendepot); getpush_obj_pos(schiffdepot_t, obj_t::schiffdepot); getpush_obj_pos(monoraildepot_t, obj_t::monoraildepot); getpush_obj_pos(tramdepot_t, obj_t::tramdepot); getpush_obj_pos(maglevdepot_t, obj_t::maglevdepot); // roadsigns/signals getpush_obj_pos(roadsign_t, obj_t::roadsign); getpush_obj_pos(signal_t, obj_t::signal); // powerlines getpush_obj_pos(pumpe_t, obj_t::pumpe); getpush_obj_pos(senke_t, obj_t::senke); #define case_resolve_obj(D) \ case bind_code::objtype: \ return script_api::param::push(vm, (D*)obj); // we have to resolve instances of derived classes here... SQInteger script_api::param::push(HSQUIRRELVM vm, obj_t* const& obj) { if (obj == NULL) { sq_pushnull(vm); return 1; } obj_t::typ type = obj->get_typ(); switch(type) { case_resolve_obj(baum_t); case_resolve_obj(gebaeude_t); case_resolve_obj(label_t); case_resolve_obj(weg_t); case_resolve_obj(roadsign_t); case_resolve_obj(signal_t); case_resolve_obj(field_t); case_resolve_obj(airdepot_t); case_resolve_obj(narrowgaugedepot_t); case_resolve_obj(bahndepot_t); case_resolve_obj(strassendepot_t); case_resolve_obj(schiffdepot_t); case_resolve_obj(monoraildepot_t); case_resolve_obj(tramdepot_t); case_resolve_obj(maglevdepot_t); case_resolve_obj(leitung_t); case_resolve_obj(pumpe_t); case_resolve_obj(senke_t); case_resolve_obj(wayobj_t); case_resolve_obj(bruecke_t); case_resolve_obj(tunnel_t); default: return access_objs::push_with_pos(vm, obj); } } obj_t* script_api::param::get(HSQUIRRELVM vm, SQInteger index) { return access_objs::get_by_pos(vm, index); } // return way ribis, have to implement a wrapper, to correctly rotate ribi static SQInteger get_way_ribi(HSQUIRRELVM vm) { weg_t *w = param::get(vm, 1); bool masked = param::get(vm, 2); ribi_t::ribi ribi = w ? (masked ? w->get_ribi() : w->get_ribi_unmasked() ) : 0; return param::push(vm, ribi); } template static SQInteger map_obj_to_string(HSQUIRRELVM vm) // parameters: obj { static cbuffer_t buf; buf.clear(); koord3d pos = script_api::param::get(vm, 1); D* obj = script_api::param::get(vm, 1); buf.printf("%s@%s", script_api::param::squirrel_type(), pos.get_str()); if (obj == NULL) { buf.append(" [invalid]"); } sq_pushstring(vm, (const char*)buf, -1); return 1; } // create class template static void begin_obj_class(HSQUIRRELVM vm, const char* name, const char* base = NULL) { SQInteger res = create_class(vm, name, base); if( !SQ_SUCCEEDED(res) ) { // base class not found: maybe script_base.nut is not up-to-date dbg->error( "begin_obj_class()", "Create class failed for %s. Base class %s missing. Please update simutrans (or just script/script_base.nut)!", name, base ); sq_raise_error(vm, "Create class failed for %s. Base class %s missing. Please update simutrans (or just script/script_base.nut)!", name, base); } uint8 objtype = bind_code::objtype; // store typetag to identify pointers sq_settypetag(vm, -1, obj_t_tag + objtype); // export constructor register_function_fv(vm, exp_obj_pos_constructor, "constructor", 4, "xiiii", freevariable(objtype)); // export _tostring method register_function(vm, map_obj_to_string, "_tostring", 1, "x"); // now functions can be registered } // mark objects static void mark_object(obj_t* obj) { obj->set_flag(obj_t::highlight); obj->set_flag(obj_t::dirty); } static void unmark_object(obj_t* obj) { obj->clear_flag(obj_t::highlight); obj->set_flag(obj_t::dirty); } static bool object_is_marked(obj_t* obj) { return obj->get_flag(obj_t::highlight); } // markers / labels static call_tool_work create_marker(koord pos, player_t* player, const char* text) { if (text == NULL) { return "Cannot create label with text == null"; } return call_tool_work(TOOL_MARKER | GENERAL_TOOL, text, 0, player, koord3d(pos, 0)); } static call_tool_init label_set_text(label_t *l, const char* text) { return command_rename(l->get_owner(), 'm', l->get_pos(), text); } static const char* label_get_text(label_t* l) { if (l) { if (grund_t *gr = welt->lookup(l->get_pos())) { return gr->get_text(); } } return NULL; } // roadsign static bool roadsign_can_pass(const roadsign_t* rs, player_t* player) { return player && rs->get_desc()->is_private_way() ? (rs->get_player_mask() & (1<get_player_nr()))!=0 : true; } // depot static call_tool_init depot_append_vehicle(depot_t *depot, player_t *player, convoihandle_t cnv, const vehicle_desc_t *desc) { if (desc == NULL) { return "Invalid vehicle_desc_x provided"; } // see depot_frame_t::image_from_storage_list: tool = 'a' // see depot_t::call_depot_tool for command string composition cbuffer_t buf; buf.printf( "%c,%s,%hu,%s", 'a', depot->get_pos().get_str(), cnv.get_id(), desc->get_name()); return call_tool_init(TOOL_CHANGE_DEPOT | SIMPLE_TOOL, buf, 0, player); } static call_tool_init depot_start_convoy(depot_t *depot, player_t *player, convoihandle_t cnv) { // see depot_frame_t::action_triggered: tool = 'b' // see depot_t::call_depot_tool for command string composition cbuffer_t buf; if (cnv.is_bound()) { buf.printf( "%c,%s,%hu", 'b', depot->get_pos().get_str(), cnv->self.get_id()); } else { buf.printf( "%c,%s,%hu", 'B', depot->get_pos().get_str(), 0); } return call_tool_init(TOOL_CHANGE_DEPOT | SIMPLE_TOOL, buf, 0, player); } static vector_tpl const& depot_get_convoy_list(depot_t *depot) { static vector_tpl list; list.clear(); if (depot==NULL) { return list; } // fill list slist_tpl const& slist = depot->get_convoy_list(); for(slist_tpl::const_iterator i = slist.begin(), end = slist.end(); i!=end; ++i) { list.append(*i); } return list; } static vector_tpl const& get_depot_list(player_t *player, waytype_t wt) { static vector_tpl list; list.clear(); // do the conversion of waytype_t to depot-type by linetype simline_t::linetype line_type = simline_t::waytype_to_linetype(wt); if (player == NULL || line_type == simline_t::MAX_LINE_TYPE) { return list; } // fill list const slist_tpl &depot_list = depot_t::get_depot_list(); for(depot_t* d : depot_list) { if(d->get_line_type()==line_type && d->get_owner()==player) { list.append(d); } } return list; } static const fabrik_t* transformer_get_factory(leitung_t *lt) { if (pumpe_t *p = dynamic_cast(lt)) { return p->get_factory(); } if (senke_t *s = dynamic_cast(lt)) { return s->get_factory(); } return NULL; } static bool leitung_is_connected(leitung_t* lt1, leitung_t* lt2) { return lt2 != NULL && lt1->get_net() == lt2->get_net(); } static bool field_is_deletable(field_t* f) { return f->get_removal_error( welt->get_public_player() ) == NULL; } static vector_tpl const& get_way_stat(weg_t* weg, sint32 INDEX) { static vector_tpl v; v.clear(); if (weg && 0<=INDEX && INDEXget_stat(i, INDEX) ); } } return v; } static vector_tpl const& get_tile_list( gebaeude_t *gb ) { static vector_tpl list; gb->get_tile_list( list ); return list; } void export_map_objects(HSQUIRRELVM vm) { /** * Class to access objects on the map * These classes cannot modify anything. */ begin_class(vm, "map_object_x", "coord3d,extend_get,ingame_object"); uint8 objtype = bind_code::objtype; sq_settypetag(vm, -1, obj_t_tag + objtype); /** * Constructor. Implemented by derived classes. * Fails if no object of precisely the requested type is on the tile. * If there is more than one object of this type on the tile then it will return the first. * @param x * @param y * @param z * @param type of the map object * @typemask void(integer,integer,integer,map_objects) */ register_function(vm, exp_obj_pos_constructor, "constructor", 5, "xiiii"); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") /** * @returns owner of the object. */ register_method(vm, &obj_t::get_owner, "get_owner"); /** * @returns raw name. */ register_method(vm, &obj_t::get_name, "get_name"); /** * @returns way type, can be @ref wt_invalid. */ register_method(vm, &obj_t::get_waytype, "get_waytype"); /** * @returns position. */ register_method(vm, &obj_t::get_pos, "get_pos"); /** * Checks whether player can remove this object. * @returns error message or null if object can be removed. */ register_method(vm, &obj_t::get_removal_error, "is_removable"); /** * @returns type of object. */ register_method(vm, &obj_t::get_typ, "get_type"); /** * Marks the object for highlighting. Use with care. */ register_method(vm, &mark_object, "mark", true); /** * Unmarks the object for highlighting. */ register_method(vm, &unmark_object, "unmark", true); /** * @returns whether object is highlighted. */ register_method(vm, &object_is_marked, "is_marked", true); end_class(vm); /** * Trees on the map. */ begin_obj_class(vm, "tree_x", "map_object_x"); /** * @returns age of tree in months. */ register_method(vm, &baum_t::get_age, "get_age"); /** * @returns object descriptor. */ register_method(vm, &baum_t::get_desc, "get_desc"); end_class(vm); /** * Buildings. */ begin_obj_class(vm, "building_x", "map_object_x"); /** * @returns factory if building belongs to one, otherwise null */ register_method(vm, &gebaeude_t::get_fabrik, "get_factory"); /** * @returns city if building belongs to one, otherwise null */ register_method(vm, &gebaeude_t::get_stadt, "get_city"); /** * @returns whether building is townhall */ register_method(vm, &gebaeude_t::is_townhall, "is_townhall"); /** * @returns whether building is headquarters */ register_method(vm, &gebaeude_t::is_headquarter, "is_headquarter"); /** * @returns whether building is a monument */ register_method(vm, &gebaeude_t::is_monument, "is_monument"); /** * Passenger level controls how many passengers will be generated by this building. * @returns passenger level */ register_method(vm, &gebaeude_t::get_passagier_level, "get_passenger_level"); /** * Mail level controls how many mail will be generated by this building. * @returns mail level */ register_method(vm, &gebaeude_t::get_mail_level, "get_mail_level"); /** * @returns object descriptor. */ register_method(vm, &gebaeude_t::get_tile, "get_desc"); /** * @returns coord for all tiles */ register_method( vm, &get_tile_list, "get_tile_list", true ); /** * @returns true if both building tiles are part of one (multi-tile) building */ register_method(vm, &gebaeude_t::is_same_building, "is_same_building"); end_class(vm); /** * Class to access depots. * Main purpose is the manipulation of convoys in depots. */ begin_obj_class(vm, "depot_x", "building_x"); /** * Append a car to the convoy in this depot. If the convoy does not exist, a new one is created first. * @param pl player owns the convoy * @param cnv the convoy * @param desc decriptor of the vehicle * @ingroup game_cmd */ register_method(vm, depot_append_vehicle, "append_vehicle", true); /** * Start the convoy in this depot. * @ingroup game_cmd */ register_method(vm, depot_start_convoy, "start_convoy", true); /** * Start all convoys in this depot. * @ingroup game_cmd */ register_method_fv(vm, depot_start_convoy, "start_all_convoys", freevariable(convoihandle_t()), true); /** * @returns list of convoys sitting in this depot */ register_method(vm, &depot_get_convoy_list, "get_convoy_list", true); /** * List of all depots of player @p pl of way-type @p wt * @param pl * @param wt * @returns depot list of player */ STATIC register_method(vm, &get_depot_list, "get_depot_list", false, true); end_class(vm); /** * Ways. */ begin_obj_class(vm, "way_x", "map_object_x"); /** * @return if this way has sidewalk - only meaningful for roads */ register_method(vm, &weg_t::hat_gehweg, "has_sidewalk"); /** * @return whether way is electrified by some way-object */ register_method(vm, &weg_t::is_electrified, "is_electrified"); /** * @return whether there is a road-sign associated to the way on the tile */ register_method(vm, &weg_t::has_sign, "has_sign"); /** * @return whether there is a signal associated to the way on the tile */ register_method(vm, &weg_t::has_signal, "has_signal"); /** * @return whether there is a way-object associated to the way on the tile */ register_method(vm, &weg_t::has_wayobj, "has_wayobj"); /** * @return whether there is a crossing associated to the way on the tile */ register_method(vm, &weg_t::is_crossing, "is_crossing"); /** * Return directions of this way. One-way signs are ignored here. * @returns direction * @typemask dir() */ register_function_fv(vm, &get_way_ribi, "get_dirs", 1, "x", freevariable(false) ); /** * Return directions of this way. Some signs restrict available directions. * @returns direction * @typemask dir() */ register_function_fv(vm, &get_way_ribi, "get_dirs_masked", 1, "x", freevariable(true) ); /** * @returns object descriptor. */ register_method(vm, &weg_t::get_desc, "get_desc"); /** * Returns maximal allowed speed on this way. * Takes limits from crossings, overhead-wires, bridges, etc, into account. * @returns max speed in kmh. */ register_method(vm, &weg_t::get_max_speed, "get_max_speed"); /** * Get monthly statistics of goods transported on this way. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_way_stat, "get_transported_goods", freevariable(WAY_STAT_GOODS), true); /** * Get monthly statistics of convoys that used this way. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_way_stat, "get_convoys_passed", freevariable(WAY_STAT_CONVOIS), true); end_class(vm); /** * Labels. */ begin_obj_class(vm, "label_x", "map_object_x"); /** * Creates a new marker. * @param pos position * @param pl owner * @param text text * @ingroup game_cmd */ STATIC register_method(vm, &create_marker, "create", false, true); /** * Set text of marker. * @param text text * @ingroup rename_func */ register_method(vm, &label_set_text, "set_text", true); /** * Get text of marker. * @see tile_x::get_text */ register_method(vm, &label_get_text, "get_text", true); end_class(vm); /** * Roadsigns and railway signals. */ begin_obj_class(vm, "sign_x", "map_object_x"); /** * @returns object descriptor. */ register_method(vm, &roadsign_t::get_desc, "get_desc"); /** * Test whether @p player 's vehicles can pass private-way sign. * Returns true if this is not a private-way sign. * @param player */ register_method(vm, &roadsign_can_pass, "can_pass", true); end_class(vm); /** * Powerlines. * (Including transformers. Can be differentiated by @ref obj_x::get_type. */ begin_obj_class(vm, "powerline_x", "map_object_x"); /** * Checkes whether powerline (or transformer) is connected to @p pl are connected. * @param pl other powerline/transformer */ register_method(vm, &leitung_is_connected, "is_connected", true); end_class(vm); /** * Transformers. * Sink and source can be differentiated by @ref obj_x::get_type. */ begin_obj_class(vm, "transformer_x", "powerline_x"); /** * Get factory connected to this transformer. */ register_method(vm, &transformer_get_factory, "get_factory", true); end_class(vm); /** * Fields */ begin_obj_class(vm, "field_x", "map_object_x"); /** * Fields can only be deleted if enough are present. * @see factory_x::get_field_count, factory_x::get_min_field_count * @return whether this one can be deleted */ register_method(vm, &field_is_deletable, "is_deletable", true); /** * @returns factory this field belongs to * @see factory_x::get_field_count, factory_x::get_min_field_count */ register_method(vm, &field_t::get_factory, "get_factory"); end_class(vm); /** * Way-objects: overhead-wires, fences, etc. Only one such object can exist on a tile. */ begin_obj_class(vm, "wayobj_x", "map_object_x"); /** * @returns object descriptor. */ register_method(vm, &wayobj_t::get_desc, "get_desc"); end_class(vm); /** * Bridges: bridge objects of bridge ramps and tiles */ begin_obj_class(vm, "bridge_x", "map_object_x"); /** * @returns object descriptor. */ register_method(vm, &bruecke_t::get_desc, "get_desc"); end_class(vm); /** * Tunnels */ begin_obj_class(vm, "tunnel_x", "map_object_x"); /** * @returns object descriptor. */ register_method(vm, &tunnel_t::get_desc, "get_desc"); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_obj_desc.cc000066400000000000000000000652071520675460500233770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_obj_desc.cc exports goods descriptors - *_desc_t. */ #include "api_obj_desc_base.h" #include "api_simple.h" #include "export_desc.h" #include "get_next.h" #include "../api_class.h" #include "../api_function.h" #include "../../descriptor/bridge_desc.h" #include "../../descriptor/building_desc.h" #include "../../descriptor/vehicle_desc.h" #include "../../descriptor/goods_desc.h" #include "../../descriptor/roadsign_desc.h" #include "../../descriptor/way_obj_desc.h" #include "../../descriptor/factory_desc.h" #include "../../builder/brueckenbauer.h" #include "../../builder/hausbauer.h" #include "../../builder/fabrikbauer.h" #include "../../builder/tunnelbauer.h" #include "../../builder/vehikelbauer.h" #include "../../builder/goods_manager.h" #include "../../builder/wegbauer.h" #include "../../obj/roadsign.h" #include "../../obj/wayobj.h" #include "../../simhalt.h" #include "../../simware.h" #include "../../tool/simtool.h" #include "../../world/simworld.h" #define begin_enum(name) #define end_enum() #define enum_slot create_slot using namespace script_api; SQInteger get_next_ware_desc(HSQUIRRELVM vm) { return generic_get_next(vm, goods_manager_t::get_count()); } SQInteger get_goods_desc_index(HSQUIRRELVM vm) { uint32 index = param::get(vm, -1); const char* name = "None"; // fall-back if (index < goods_manager_t::get_count()) { name = goods_manager_t::get_info(index)->get_name(); } return push_instance(vm, "good_desc_x", name); } bool are_equal(const obj_named_desc_t* a, const obj_named_desc_t* b) { return (a==b); } sint64 get_scaled_maintenance(const obj_desc_transport_related_t* desc) { return desc ? welt->scale_with_month_length(desc->get_maintenance()) : 0; } sint64 get_scaled_maintenance_vehicle(const vehicle_desc_t* desc) { return desc ? welt->scale_with_month_length(desc->vehicle_desc_t::get_maintenance()) : 0; } sint64 get_scaled_maintenance_building(const building_desc_t* desc) { return desc ? welt->scale_with_month_length(desc->get_maintenance(welt)) : 0; } bool building_enables(const building_desc_t* desc, uint8 which) { return desc ? desc->get_enabled() & which : 0; } SQInteger building_get_size(HSQUIRRELVM vm) { const building_desc_t* desc = param::get(vm, 1); uint8 rotation = param::get(vm, 2); if (desc) { koord size = desc->get_size(rotation); // no automatic coordinate transform please return push_instance(vm, "coord", size.x, size.y); } return -1; } mytime_t get_intro_retire(const obj_desc_timelined_t* desc, bool intro) { return (uint32)(desc ? ( intro ? desc->get_intro_year_month() : desc->get_retire_year_month() ) : 1); } bool is_obsolete_future(const obj_desc_timelined_t* desc, mytime_t time, uint8 what) { if (desc) { switch(what) { case 0: return desc->is_future(time.raw); case 1: return desc->is_retired(time.raw); case 2: return desc->is_available(time.raw); default: ; } } return false; } const vector_tpl& get_building_list(building_desc_t::btype type) { const vector_tpl* p = hausbauer_t::get_list(type); static const vector_tpl dummy; return p ? *p : dummy; } const vector_tpl& get_available_stations(building_desc_t::btype type, waytype_t wt, const goods_desc_t *freight) { static vector_tpl dummy; dummy.clear(); switch(type) { case building_desc_t::depot: case building_desc_t::generic_stop: case building_desc_t::generic_extension: case building_desc_t::dock: case building_desc_t::flat_dock: break; default: return dummy; } const vector_tpl* p = hausbauer_t::get_list(type); // translate freight to enables-flags uint8 enables = 0; if (freight && type != building_desc_t::depot) { switch(freight->get_catg_index()) { case goods_manager_t::INDEX_PAS: enables = haltestelle_t::PAX; break; case goods_manager_t::INDEX_MAIL: enables = haltestelle_t::POST; break; default: enables = haltestelle_t::WARE; } } // use wt_all (which is equal to invalid_wt, see api_const.cc) to get buildings for all waytypes bool accept_all_wt = wt == invalid_wt || wt == ignore_wt; uint16 time = welt->get_timeline_year_month(); for(building_desc_t const* const desc : *p) { if( desc->get_type()==type && (accept_all_wt || desc->get_extra()==(uint32)wt) && (enables==0 || (desc->get_enabled()&enables)!=0) && desc->get_builder() && desc->is_available(time)) { dummy.append(desc); } } return dummy; } sint64 building_get_cost(const building_desc_t* desc) { return desc->get_price(welt) * desc->get_x() * desc->get_y(); } bool building_is_terminus(const building_desc_t *desc) { return desc && desc->get_type() == building_desc_t::generic_stop && desc->get_all_layouts() == 4; } bool can_be_first(const vehicle_desc_t *desc) { return desc->can_follow(NULL); } bool can_be_last(const vehicle_desc_t *desc) { return desc->can_lead(NULL); } bool is_coupling_allowed(const vehicle_desc_t *desc1, const vehicle_desc_t *desc2) { return desc1 && desc2 && desc1->can_lead(desc2) && desc2->can_follow(desc1); } const vector_tpl& get_predecessors(const vehicle_desc_t *desc) { static vector_tpl dummy; dummy.clear(); for(int i=0; iget_leader_count(); i++) { if (desc->get_leader(i)) { dummy.append(desc->get_leader(i)); } } return dummy; } const vector_tpl& get_successors(const vehicle_desc_t *desc) { static vector_tpl dummy; dummy.clear(); for(int i=0; iget_trailer_count(); i++) { if (desc->get_trailer(i)) { dummy.append(desc->get_trailer(i)); } } return dummy; } const vector_tpl& get_available_vehicles(waytype_t wt) { static vector_tpl dummy; bool use_obsolete = welt->get_settings().get_allow_buying_obsolete_vehicles(); uint16 time = welt->get_timeline_year_month(); dummy.clear(); slist_tpl const& list = vehicle_builder_t::get_info(wt); for(vehicle_desc_t const* const i : list) { if (!i->is_retired(time) || use_obsolete) { if (!i->is_future(time)) { dummy.append(i); } } } return dummy; } uint32 get_power(const vehicle_desc_t *desc) { return desc->get_power() * desc->get_gear(); } bool vehicle_needs_electrification(const vehicle_desc_t *desc) { return desc->get_power() && (desc->get_engine_type()==vehicle_desc_t::electric); } // export of building_desc_t::btype only here namespace script_api { declare_enum_param(building_desc_t::btype, uint16, "building_desc_x::building_type"); }; bool is_traffic_light(const roadsign_desc_t *d) { return !d->is_signal_type() && d->is_traffic_light(); } sint64 tree_get_price() { return -welt->get_settings().cst_remove_tree; } const vector_tpl& get_available_wayobjs(waytype_t wt) { static vector_tpl dummy; uint16 time = welt->get_timeline_year_month(); dummy.clear(); for(auto i : wayobj_t::get_list()) { const way_obj_desc_t* desc = i.value; if (desc->get_waytype()==wt && desc->is_available(time) && desc->get_builder()) { dummy.append(desc); } } return dummy; } #ifdef DOXYGEN /** * Description of input/output slots */ struct factory_slot_information_x { // begin_class("factory_slot_information_x") good_desc_x good; ///< type of produced/consumed good integer capacity; ///< capacity to store the good integer factor; ///< production/consumption factor }; // end_class #endif SQInteger get_factory_outputs(HSQUIRRELVM vm) { const factory_desc_t *desc = param::get(vm, -1); sq_newarray(vm, 0); if (desc) { for(uint16 i=0; iget_product_count(); i++) { const factory_product_desc_t *fp = desc->get_product(i); sq_newtable(vm); create_slot(vm, "good", fp->get_output_type()); create_slot(vm, "capacity", fp->get_capacity()); create_slot(vm, "factor", fp->get_factor()); sq_arrayappend(vm, -2); } } return 1; } SQInteger get_factory_inputs(HSQUIRRELVM vm) { const factory_desc_t *desc = param::get(vm, -1); sq_newarray(vm, 0); if (desc) { for(uint16 i=0; iget_supplier_count(); i++) { const factory_supplier_desc_t *fp = desc->get_supplier(i); sq_newtable(vm); create_slot(vm, "good", fp->get_input_type()); create_slot(vm, "capacity", fp->get_capacity()); create_slot(vm, "factor", fp->get_consumption()); sq_arrayappend(vm, -2); } } return 1; } void export_goods_desc(HSQUIRRELVM vm) { /** * Base class of all object descriptors. */ create_class(vm, "obj_desc_x", "extend_get"); /** * @return raw (untranslated) name * @typemask string() */ register_method(vm, &obj_named_desc_t::get_name, "get_name"); /** * Checks if two object descriptor are equal. * @param other * @return true if this==other */ register_method(vm, &are_equal, "is_equal", true); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") end_class(vm); /** * Base class of object descriptors with intro / retire dates. */ create_class(vm, "obj_desc_time_x", "obj_desc_x"); /** * @return introduction date of this object */ register_method_fv(vm, &get_intro_retire, "get_intro_date", freevariable(true), true); /** * @return retirement date of this object */ register_method_fv(vm, &get_intro_retire, "get_retire_date", freevariable(false), true); /** * @param time to test (0 means no timeline game) * @return true if not available as intro date is in future */ register_method_fv(vm, &is_obsolete_future, "is_future", freevariable(0), true); /** * @param time to test (0 means no timeline game) * @return true if not available as retirement date already passed */ register_method_fv(vm, &is_obsolete_future, "is_retired", freevariable(1), true); /** * @param time to test (0 means no timeline game) * @return true if available: introduction and retirement date checked */ register_method_fv(vm, &is_obsolete_future, "is_available", freevariable(2), true); end_class(vm); /** * Base class of object descriptors for transport related stuff. */ create_class(vm, "obj_desc_transport_x", "obj_desc_time_x"); /** * @returns monthly maintenance cost [in 1/100 credits] of one object of this type. */ register_local_method(vm, &get_scaled_maintenance, "get_maintenance"); /** * @returns cost [in 1/100 credits] to buy or build one piece or tile of this thing. */ register_method(vm, &obj_desc_transport_related_t::get_price, "get_cost"); /** * @returns way type, can be @ref wt_invalid. */ register_method(vm, &obj_desc_transport_related_t::get_waytype, "get_waytype"); /** * @returns top speed: maximal possible or allowed speed, in km/h. */ register_method(vm, &obj_desc_transport_related_t::get_topspeed, "get_topspeed"); end_class(vm); /** * Vehicle descriptors */ begin_desc_class(vm, "vehicle_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); /** * @returns true if this vehicle can lead a convoy */ register_method(vm, &can_be_first, "can_be_first", true); /** * @returns true if this vehicle can be the last of a convoy */ register_method(vm, &can_be_last, "can_be_last", true); /** * @returns list of possible successors, if list is empty then all are allowed (if @ref can_be_last returns true, or no successors are allowed if @ref can_be_last is false). */ register_method(vm, &get_successors, "get_successors", true); /** * @returns list of possible predecessors, if list is empty then all are allowed (if @ref can_be_first returns true, or no predecessors are allowed if @ref can_be_first is false). */ register_method(vm, &get_predecessors, "get_predecessors", true); /** * @returns a list of all available vehicles at the current in-game-time */ STATIC register_method(vm, &get_available_vehicles, "get_available_vehicles", false, true); /** * Power of the vehicle. This value can be used in convoy_x::calc_max_speed. * It returns (power of vehicle in kW) * (gear value) * 64, where power and gear are as shown in-game. * @returns the total power of the vehicle (takes power and gear from pak-files into account) */ register_method(vm, &get_power, "get_power", true); /** * @returns true if this vehicle needs electrification (and is powered) */ register_method(vm, &vehicle_needs_electrification, "needs_electrification", true); /** * @returns freight that can be transported (or null) */ register_method(vm, &vehicle_desc_t::get_freight_type, "get_freight"); /** * @returns capacity */ register_method(vm, &vehicle_desc_t::get_capacity, "get_capacity"); /** * @returns running cost in 1/100 credits per tile */ register_method(vm, &vehicle_desc_t::get_running_cost, "get_running_cost"); /** * @returns fixed cost in 1/100 credits per month */ register_method(vm, &get_scaled_maintenance_vehicle, "get_maintenance", true); /** * @returns weight of the empty vehicle */ register_method(vm, &vehicle_desc_t::get_weight, "get_weight"); // in kg /** * @returns lengths in @ref units::CARUNITS_PER_TILE */ register_method(vm, &vehicle_desc_t::get_length, "get_length"); // in CAR_UNITS_PER_TILE /** * Checks if the coupling of @p first and @p second is possible in this order. * @param first * @param second * @returns true if coupling is possible */ STATIC register_method(vm, is_coupling_allowed, "is_coupling_allowed", false, true); end_class(vm); /** * Object descriptors for trees. */ begin_desc_class(vm, "tree_desc_x", "obj_desc_x", (GETDESCFUNC)param::getfunc()); /** * Returns price to fell one tree. * @returns price (should be positive) */ STATIC register_method(vm, tree_get_price, "get_price", false, true); end_class(vm); /** * Object descriptors for buildings: houses, attractions, stations and extensions, depots, harbours. */ begin_desc_class(vm, "building_desc_x", "obj_desc_time_x", (GETDESCFUNC)param::getfunc()); /** * @returns whether building is an attraction */ register_method(vm, &building_desc_t::is_attraction, "is_attraction"); /** * @param rotation * @return size of building in the given @p rotation * @typemask coord(integer) */ register_function(vm, building_get_size, "get_size", 2, "x i"); /** * @return monthly maintenance cost */ register_method(vm, &get_scaled_maintenance_building, "get_maintenance", true); /** * Price to build this building, takes size, level, and type into account. * @return price [in 1/100 credits] to build this building */ register_method(vm, &building_get_cost, "get_cost", true); /** * @return capacity */ register_method(vm, &building_desc_t::get_capacity, "get_capacity"); /** * @return whether station building can be built underground */ register_method(vm, &building_desc_t::can_be_built_underground, "can_be_built_underground"); /** * @return whether station building can be built above ground */ register_method(vm, &building_desc_t::can_be_built_aboveground, "can_be_built_aboveground"); /** * @return whether this station building can handle passengers */ register_method_fv(vm, &building_enables, "enables_pax", freevariable(1), true); /** * @return whether this station building can handle mail */ register_method_fv(vm, &building_enables, "enables_mail", freevariable(2), true); /** * @return whether this station building can handle freight */ register_method_fv(vm, &building_enables, "enables_freight", freevariable(4), true); /// building types begin_enum("building_type") /// tourist attraction to be built in cities enum_slot(vm, "attraction_city", (uint8)building_desc_t::attraction_city, true); /// tourist attraction to be built outside cities enum_slot(vm, "attraction_land", (uint8)building_desc_t::attraction_land, true); /// monument, built only once per map enum_slot(vm, "monument", (uint8)building_desc_t::monument, true); /// factory enum_slot(vm, "factory", (uint8)building_desc_t::factory, true); /// townhall enum_slot(vm, "townhall", (uint8)building_desc_t::townhall, true); /// company headquarters enum_slot(vm, "headquarter", (uint8)building_desc_t::headquarters, true); /// harbour enum_slot(vm, "harbour", (uint8)building_desc_t::dock, true); /// harbour without a slope (buildable on flat ground beaches) enum_slot(vm, "flat_harbour", (uint8)building_desc_t::flat_dock, true); /// depot enum_slot(vm, "depot", (uint8)building_desc_t::depot, true); /// station enum_slot(vm, "station", (uint8)building_desc_t::generic_stop, true); /// station extension enum_slot(vm, "station_extension", (uint8)building_desc_t::generic_extension, true); /// city building: residential enum_slot(vm, "city_res", (uint8)building_desc_t::city_res, true); /// city building: commercial enum_slot(vm, "city_com", (uint8)building_desc_t::city_com, true); /// city building: industrial enum_slot(vm, "city_ind", (uint8)building_desc_t::city_ind, true); end_enum(); /** * @returns building type */ register_method(vm, &building_desc_t::get_type, "get_type"); /** * @returns way type, can be @ref wt_invalid. */ register_method(vm, &building_desc_t::get_finance_waytype, "get_waytype"); /** * @returns headquarter level (or -1 if building is not headquarter) */ register_method(vm, &building_desc_t::get_headquarters_level, "get_headquarter_level"); /** * Returns an array with all buildings of the given type. * @warning If @p type is one of building_desc_x::harbour, building_desc_x::depot, building_desc_x::station, building_desc_x::station_extension then always the same list is generated. * You have to filter out e.g. station buildings yourself. */ STATIC register_method(vm, &get_building_list, "get_building_list", false, true); /** * Returns an array of available station/extension/depot buildings. * Entries are of type @ref building_desc_x. * @param type building type from @ref building_desc_x::building_type * @param wt waytype (can be wt_all to ignore waytype of desc) * @param freight station should accept this freight (if equal to {}, i.e., empty table, then return all buildings) * @returns the list */ STATIC register_method(vm, &get_available_stations, "get_available_stations", false, true); /** * @returns true if this is a station building that can be used as terminus */ register_method(vm, &building_is_terminus, "is_terminus", true); end_class(vm); /** * Object descriptors for factories. */ begin_desc_class(vm, "factory_desc_x", "obj_desc_x", (GETDESCFUNC)param::getfunc()); /** * @returns name */ register_method(vm, &factory_desc_t::get_name, "get_name"); /** * Descriptor of associated building. */ register_method(vm, &factory_desc_t::get_building, "get_building_desc"); /** * @returns true if this factory produces electricity */ register_method(vm, &factory_desc_t::is_electricity_producer, "is_electricity_producer"); /** * Initial production of a factory is get_productivity_base() + rand( get_productivity_range() ). * @returns base production */ register_method(vm, &factory_desc_t::get_productivity, "get_productivity_base"); /** * @returns base production variable range */ register_method(vm, &factory_desc_t::get_range, "get_productivity_range"); /** * Returns array with information about input goods * @typemask array () */ register_function(vm, get_factory_inputs, "get_inputs", 1, "t|x|y"); /** * Returns array with information about input goods * @typemask array () */ register_function(vm, get_factory_outputs, "get_outputs", 1, "t|x|y"); /** * Returns table with all factory types. * The factory names are used as table keys. */ register_method(vm, &factory_builder_t::get_factory_table, "get_list", 1); end_class(vm); /** * Object descriptors for ways. */ begin_desc_class(vm, "way_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); /** * @returns true if this way can be build on the steeper (double) slopes. */ register_method(vm, &way_desc_t::has_double_slopes, "has_double_slopes"); /** * @returns system type of the way, see @ref way_system_types. */ register_method(vm, &way_desc_t::get_styp, "get_system_type"); /** * Generates a list of all ways available for building. * @param wt waytype * @param st system type of way * @returns the list */ STATIC register_method(vm, way_builder_t::get_way_list, "get_available_ways", false, true); /** * Returns the last used (default) way descriptor for a given waytype. * This is the way that would be selected when pressing the shortcut key (e.g. 's' for roads). * @param wt waytype (1=road_wt, 2=track_wt, 3=tram_wt, etc.) * @returns way_desc_x object or null if none available */ STATIC register_method(vm, &tool_build_way_t::get_default_desc, "get_default_way_desc", false, true); end_class(vm); /** * Object descriptors for tunnels. */ begin_desc_class(vm, "tunnel_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); /** * Returns a list with available tunnel types. */ STATIC register_method(vm, tunnel_builder_t::get_available_tunnels, "get_available_tunnels", false, true); end_class(vm); /** * Object descriptors for bridges. */ begin_desc_class(vm, "bridge_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); /** * @return true if this bridge can raise two level from flat terrain */ register_method(vm, &bridge_desc_t::has_double_ramp, "has_double_ramp"); /** * @return true if this bridge can start or end on a double slope */ register_method(vm, &bridge_desc_t::has_double_start, "has_double_start"); /** * @return maximal bridge length in tiles */ register_method(vm, &bridge_desc_t::get_max_length, "get_max_length"); /** * @return maximal bridge height */ register_method(vm, &bridge_desc_t::get_max_height, "get_max_height"); /** * Returns a bridge with the given name */ STATIC register_method(vm, bridge_builder_t::get_desc, "get_desc", false, true); /** * Returns a list with available bridge types. */ STATIC register_method(vm, bridge_builder_t::get_available_bridges, "get_available_bridges", false, true); end_class(vm); /** * Implements iterator to iterate through the list of all good types. * * Usage: * @code * local list = good_desc_list_x() * foreach(good_desc in list) { * ... // good_desc is an instance of the good_desc_x class * } * @endcode */ create_class(vm, "good_desc_list_x", 0); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, get_next_ware_desc, "_nexti", 2, "x o|i"); /** * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, get_goods_desc_index, "_get", 2, "xi"); end_class(vm); /** * Descriptor of goods and freight types. */ begin_desc_class(vm, "good_desc_x", "obj_desc_x", (GETDESCFUNC)param::getfunc()); // dummy entry to create documentation of constructor /** * Constructor. * @param name raw name of the freight type. * @typemask (string) */ // register_function( .., "constructor", .. ) create_slot(vm, "passenger", goods_manager_t::passengers, true); create_slot(vm, "mail", goods_manager_t::mail, true); #ifdef DOXYGEN static good_desc_x passenger; ///< descriptor for passenger static good_desc_x mail; ///< descriptor for mail #endif /** * @return freight category. 0=Passengers, 1=Mail, 2=None, >=3 anything else */ register_method(vm, &goods_desc_t::get_catg_index, "get_catg_index"); /** * Checks if this good can be interchanged with the other, in terms of * transportability. */ register_method(vm, &goods_desc_t::is_interchangeable, "is_interchangeable"); /** * @returns weight of one unit of this freight */ register_method(vm, &goods_desc_t::get_weight_per_unit, "get_weight_per_unit"); // in kg /** * @returns metric unit name */ register_method(vm, &goods_desc_t::get_mass, "get_metric"); /** * Calculates transport revenue per tile and freight unit. * Takes speedbonus into account. * Value contains an additional factor of 3000. Don't ask. * Divide by 3000 *after* calculating revenue for a loaded convoy. * @param wt waytype of vehicle * @param speedkmh actual achieved speed in km/h * @returns revenue */ register_method(vm, &ware_t::calc_revenue, "calc_revenue", true); end_class(vm); /** * Descriptor of roadsigns and signals. */ begin_desc_class(vm, "sign_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); /** * @returns true if sign is one-way sign */ register_method(vm, &roadsign_desc_t::is_single_way, "is_one_way"); /** * @returns true if sign is private-way sign */ register_method(vm, &roadsign_desc_t::is_private_way, "is_private_way"); /** * @returns true if sign is traffic light */ register_method(vm, &is_traffic_light, "is_traffic_light", true); /** * @returns true if sign is choose sign */ register_method(vm, &roadsign_desc_t::is_choose_sign, "is_choose_sign"); /** * @returns true if sign is signal */ register_method(vm, &roadsign_desc_t::is_simple_signal, "is_signal"); /** * @returns true if sign is pre signal (distant signal) */ register_method(vm, &roadsign_desc_t::is_pre_signal, "is_pre_signal"); /** * @returns true if sign is priority signal */ register_method(vm, &roadsign_desc_t::is_priority_signal, "is_priority_signal"); /** * @returns true if sign is long-block signal */ register_method(vm, &roadsign_desc_t::is_longblock_signal, "is_longblock_signal"); /** * @returns true if sign is end-of-choose sign */ register_method(vm, &roadsign_desc_t::is_end_choose_signal, "is_end_choose_signal"); /** * Returns a list with available sign types. * @param wt waytype */ STATIC register_method(vm, roadsign_t::get_available_signs, "get_available_signs", false, true); end_class(vm); /** * Descriptor of way-objects. */ begin_desc_class(vm, "wayobj_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); /** * @returns true for over-head lines. */ register_method(vm, &way_obj_desc_t::is_overhead_line, "is_overhead_line"); /** * Returns a list with available wayobj-types. * @param wt waytype */ STATIC register_method(vm, get_available_wayobjs, "get_available_wayobjs", false, true); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_obj_desc_base.cc000066400000000000000000000050321520675460500243570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api_obj_desc_base.h" #include "../../builder/brueckenbauer.h" #include "../../builder/fabrikbauer.h" #include "../../builder/hausbauer.h" #include "../../builder/tunnelbauer.h" #include "../../builder/vehikelbauer.h" #include "../../builder/goods_manager.h" #include "../../builder/wegbauer.h" #include "../../descriptor/bridge_desc.h" #include "../../descriptor/tunnel_desc.h" #include "../../descriptor/vehicle_desc.h" #include "../../descriptor/goods_desc.h" #include "../../descriptor/way_desc.h" #include "../../descriptor/way_obj_desc.h" #include "../../obj/baum.h" #include "../../obj/roadsign.h" #include "../../obj/wayobj.h" #include "../../../squirrel/sq_extensions.h" using namespace script_api; static const way_desc_t *my_get_desc(const char *name) { return way_builder_t::get_desc(name); } implement_desc_param(tree_desc_t, "tree_desc_x", &tree_builder_t::find_tree); implement_desc_param(building_desc_t, "building_desc_x", &hausbauer_t::get_desc); implement_desc_param(goods_desc_t, "good_desc_x", (const goods_desc_t* (*)(const char*))(&goods_manager_t::get_info) ); implement_desc_param(way_desc_t, "way_desc_x", &my_get_desc); implement_desc_param(vehicle_desc_t, "vehicle_desc_x", &vehicle_builder_t::get_info); implement_desc_param(tunnel_desc_t, "tunnel_desc_x", &tunnel_builder_t::get_desc); implement_desc_param(bridge_desc_t, "bridge_desc_x", &bridge_builder_t::get_desc); implement_desc_param(roadsign_desc_t, "sign_desc_x", &roadsign_t::find_desc); implement_desc_param(way_obj_desc_t, "wayobj_desc_x", &wayobj_t::find_desc); implement_desc_param(factory_desc_t, "factory_desc_x", &factory_builder_t::get_desc); /** * Macro to get the implementation of get method based on unique tag. */ #define implement_class_with_tag(class) \ /* tag: points to itself */ \ static void *class ## _tag = &class ## _tag; \ \ const class* param::get(HSQUIRRELVM vm, SQInteger index) \ { \ return (const class*)get_instanceup(vm, index, tag(), squirrel_type()); \ } \ \ void* param::tag() \ { \ return class ## _tag; \ } // use the macro to obtain the interface of some abstract classes implement_class_with_tag(obj_named_desc_t); implement_class_with_tag(obj_desc_timelined_t); implement_class_with_tag(obj_desc_transport_related_t); SQInteger param::push(HSQUIRRELVM vm, const building_tile_desc_t* b) { return param::push(vm, b ? b->get_desc() : NULL); } simutrans-124.5/src/simutrans/script/api/api_obj_desc_base.h000066400000000000000000000053541520675460500242300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_API_OBJ_DESC_BASE_H #define SCRIPT_API_API_OBJ_DESC_BASE_H /** @file api_obj_desc_base.h templates for transfer of desc-pointers */ #include "../api_param.h" #include "../api_class.h" class obj_named_desc_t; class obj_desc_timelined_t; class obj_desc_transport_related_t; class tree_desc_t; class bridge_desc_t; class building_desc_t; class building_tile_desc_t; class factory_desc_t; class goods_desc_t; class roadsign_desc_t; class tunnel_desc_t; class vehicle_desc_t; class way_desc_t; class way_obj_desc_t; namespace script_api { #define declare_desc_param(T, sqtype) \ template<> \ struct param { \ \ typedef const T* (*GETFUNC)(const char*);\ \ static GETFUNC getfunc(); \ \ declare_types("t|x|y", sqtype); \ \ static void* tag() \ { \ return (void*)(getfunc()); \ } \ static const T* get(HSQUIRRELVM vm, SQInteger index); \ \ static SQInteger push(HSQUIRRELVM vm, const T* b); \ }; \ template<> \ struct param { \ \ declare_types("t|x|y", sqtype); \ }; #define implement_desc_param(T, sqtype, func) \ param::GETFUNC param::getfunc() \ { \ return func; \ } \ const T* param::get(HSQUIRRELVM vm, SQInteger index) \ { \ return (const T*)get_instanceup(vm, index, tag(), sqtype); \ } \ SQInteger param::push(HSQUIRRELVM vm, const T* b) \ { \ if (b) { \ return push_instance_up(vm, b); \ } \ else { \ sq_pushnull(vm); return 1; \ } \ } declare_specialized_param(const obj_named_desc_t*, "t|x|y", "obj_desc_x"); declare_param_mask(obj_named_desc_t*, "t|x|y", "obj_desc_x"); declare_specialized_param(const obj_desc_timelined_t*, "t|x|y", "obj_desc_time_x"); declare_param_mask(obj_desc_timelined_t*, "t|x|y", "obj_desc_time_x"); declare_specialized_param(const obj_desc_transport_related_t*, "t|x|y", "obj_desc_transport_x"); declare_param_mask(obj_desc_transport_related_t*, "t|x|y", "obj_desc_transport_x"); declare_desc_param(tree_desc_t, "tree_desc_x"); declare_desc_param(goods_desc_t, "good_desc_x"); declare_desc_param(building_desc_t, "building_desc_x"); declare_desc_param(way_desc_t, "way_desc_x"); declare_desc_param(vehicle_desc_t, "vehicle_desc_x"); declare_desc_param(tunnel_desc_t, "tunnel_desc_x"); declare_desc_param(bridge_desc_t, "bridge_desc_x"); declare_desc_param(roadsign_desc_t, "sign_desc_x"); declare_desc_param(way_obj_desc_t, "wayobj_desc_x"); declare_desc_param(factory_desc_t, "factory_desc_x"); // only push the building_desc_t-pointer declare_desc_param(building_tile_desc_t, "building_desc_x"); }; #endif simutrans-124.5/src/simutrans/script/api/api_pathfinding.cc000066400000000000000000000140441520675460500241130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_pathfinding.cc exports heap structure and construction helpers. */ #include "api_obj_desc_base.h" #include "api_simple.h" #include "../api_class.h" #include "../api_function.h" #include "../../builder/brueckenbauer.h" #include "../../builder/wegbauer.h" #include "../../descriptor/bridge_desc.h" #include "../../descriptor/way_desc.h" #include "../../tpl/binary_heap_tpl.h" #include "../../world/simworld.h" using namespace script_api; struct heap_node_t { sint32 weight; sint32 value; heap_node_t(sint32 w, sint32 v) : weight(w), value(v) {} // dereferencing to be used in binary_heap_tpl inline sint32 operator * () const { return weight; } }; typedef binary_heap_tpl simple_heap_t; namespace script_api{ declare_specialized_param(heap_node_t, "t|x|y", "simple_heap_x::node_x"); declare_specialized_param(simple_heap_t*, "t|x|y", "simple_heap_x"); }; SQInteger heap_constructor(HSQUIRRELVM vm) { simple_heap_t* heap = new simple_heap_t(); attach_instance(vm, 1, heap); return SQ_OK; } void* script_api::param::tag() { return (void*)&heap_constructor; } simple_heap_t* script_api::param::get(HSQUIRRELVM vm, SQInteger index) { simple_heap_t *heap = get_attached_instance(vm, index, param::tag()); return heap; } SQInteger param::push(HSQUIRRELVM vm, heap_node_t const& v) { sq_newtable(vm); create_slot(vm, "weight", v.weight); create_slot(vm, "value", v.value); return 1; } void simple_heap_insert(simple_heap_t *heap, sint32 weight, sint32 value) { heap->insert( heap_node_t(weight, value) ); } SQInteger simple_heap_pop(HSQUIRRELVM vm) // instance { simple_heap_t *heap = param::get(vm, 1); if (heap) { if (!heap->empty()) { return param::push(vm, heap->pop()); } else { return sq_raise_error(vm, "Called pop() on empty heap"); } } else { return sq_raise_error(vm, "Not a heap instance"); // should not happen } } SQInteger way_builder_constructor(HSQUIRRELVM vm) // instance, player { // get player parameter player_t *player = get_my_player(vm); if (player == NULL) { player = param::get(vm, 2); } if (player == NULL) { return SQ_ERROR; } way_builder_t* bob = new way_builder_t(player); bob->set_keep_existing_faster_ways(true); bob->set_keep_city_roads(true); attach_instance(vm, 1, bob); return SQ_OK; } void way_builder_set_build_types(way_builder_t *bob, const way_desc_t *way) { if (way) { bob->init_builder( (way_builder_t::bautyp_t)way->get_waytype(), way, NULL /*tunnel*/, NULL /*bridge*/); } } bool way_builder_is_allowed_step(way_builder_t *bob, grund_t *from, grund_t *to) { if (from == NULL || to == NULL) { return false; } sint32 costs = 0; return bob->is_allowed_step(from, to, &costs); } koord3d bridge_builder_find_end_pos(player_t *player, koord3d pos, my_ribi_t mribi, const bridge_desc_t *bridge, uint32 min_length) { sint8 height; ribi_t::ribi ribi(mribi); if (player == NULL || bridge == NULL) { return koord3d::invalid; } if (bridge_builder_t::find_end_pos(player, pos, ribi, height, bridge, min_length, 10, false) != NULL) { return koord3d::invalid; } return pos; } void export_pathfinding(HSQUIRRELVM vm) { /** * Exports a binary heap structure with * with simple nodes. */ create_class(vm, "simple_heap_x", 0); /** * Constructor */ register_function(vm, heap_constructor, "constructor", 1, "x"); sq_settypetag(vm, -1, param::tag()); /// Clears the heap. register_method(vm, &simple_heap_t::clear, "clear"); /// @returns number of nodes in heap register_method(vm, &simple_heap_t::get_count, "len"); /// @returns true when heap is empty register_method(vm, &simple_heap_t::empty, "is_empty"); /** * Inserts a node into the heap. * @param weight heap is sorted with respect to weight * @param value the data to be stored */ register_method(vm, simple_heap_insert, "insert", true); /** * Pops the top node of the heap. * Raises error if heap is empty. * @returns top node */ register_function(vm, simple_heap_pop, "pop", 1, "x"); #ifdef SQAPI_DOC /** * Helper node class. */ class node_x { int weight; ///< heap is sorted with respect to weight int value; ///< the data to be stored } #endif end_class(vm); /** * Class with helper methods for way planning. */ create_class(vm, "way_planner_x", 0); /** * Constructor * @param pl player that wants to plan, for ai scripts is set to ai player_x::self * @typemask way_planner_x(player_x) */ register_function(vm, way_builder_constructor, "constructor", 2, "xx"); sq_settypetag(vm, -1, param::tag()); /** * Sets way descriptor, which is needed for @ref is_allowed_step. * @param way descriptor of way to be planned. */ register_method(vm, way_builder_set_build_types, "set_build_types", true); /** * Checks if player can build way from @p from to @p to. * @param from from here * @param to to here, @p from and @p to must be adjacent. */ register_method(vm, way_builder_is_allowed_step, "is_allowed_step", true); end_class(vm); /** * Class with helper methods for bridge planning. */ create_class(vm, "bridge_planner_x", 0); /** * Find suitable end tile for bridge starting at @p pos going into direction @p dir. * @param pl who wants to build a bridge * @param pos start tile for bridge * @param dir direction * @param bridge bridge descriptor * @param min_length bridge should have this minimal length * @returns coordinate of end tile or an invalid coordinate */ STATIC register_method(vm, bridge_builder_find_end_pos, "find_end", false, true); end_class(vm); } void* script_api::param::tag() { return (void*)&way_builder_constructor; } way_builder_t* script_api::param::get(HSQUIRRELVM vm, SQInteger index) { way_builder_t *bob = get_attached_instance(vm, index, param::tag()); return bob; } simutrans-124.5/src/simutrans/script/api/api_player.cc000066400000000000000000000254251520675460500231210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_player.cc exports player/company related functions. */ #include "../api_class.h" #include "../api_function.h" #include "../../player/simplay.h" #include "../../player/finance.h" // for creation of lines #include "../../simline.h" #include "../../tool/simmenu.h" #include "../../world/simworld.h" using namespace script_api; vector_tpl const& get_player_stat(player_t *player, sint32 INDEX, sint32 TTYPE, bool monthly) { static vector_tpl v; v.clear(); bool atv = false; if (TTYPE<0 || TTYPE>=TT_MAX) { if (INDEX<0 || INDEX>ATC_MAX) { return v; } } else { if (INDEX<0 || INDEX>ATV_MAX) { return v; } atv = true; } if (player) { finance_t *finance = player->get_finance(); // calculate current month finance->calc_finance_history(); uint16 maxi = monthly ? MAX_PLAYER_HISTORY_MONTHS : MAX_PLAYER_HISTORY_YEARS; for(uint16 i = 0; i < maxi; i++) { sint64 m = atv ? ( monthly ? finance->get_history_veh_month((transport_type)TTYPE, i, INDEX) : finance->get_history_veh_year((transport_type)TTYPE, i, INDEX) ) : ( monthly ? finance->get_history_com_month(i, INDEX) : finance->get_history_com_year(i, INDEX) ); if (atv) { if (INDEX != ATV_TRANSPORTED_PASSENGER && INDEX != ATV_TRANSPORTED_MAIL && INDEX != ATV_TRANSPORTED_GOOD && INDEX !=ATV_TRANSPORTED) { m = convert_money(m); } } else { if (INDEX != ATC_ALL_CONVOIS) { m = convert_money(m); } } v.append(m); } } return v; } // export of finance_t only here namespace script_api { declare_specialized_param(finance_t*, param::typemask(), param::squirrel_type()); finance_t* param::get(HSQUIRRELVM vm, SQInteger index) { player_t *player = param::get(vm, index); return player ? player->get_finance() : NULL; } }; // also export transport_type - to get correct parameters namespace script_api { declare_specialized_param(transport_type, "i", "integer"); transport_type param::get(HSQUIRRELVM vm, SQInteger index) { return (transport_type) min(param::get(vm, index), TT_MAX-1); } }; SQInteger player_export_line_list(HSQUIRRELVM vm) { if (player_t* player = param::get(vm, 1)) { push_instance(vm, "line_list_x"); set_slot(vm, "player_id", player->get_player_nr()); return 1; } return SQ_ERROR; } call_tool_init player_create_line(player_t *player, waytype_t wt) { simline_t::linetype lt = simline_t::waytype_to_linetype(wt); if (lt == simline_t::MAX_LINE_TYPE) { return "Invalid waytype provided"; } // build param string (see schedule_list_gui_t::action_triggered) cbuffer_t buf; buf.printf( "c,0,%i,0,0|%i|", lt, lt); return call_tool_init(TOOL_CHANGE_LINE | SIMPLE_TOOL, buf, 0, player); } call_tool_init player_book_account(player_t *player, sint32 delta) { // build param string (see tool_change_player_t) cbuffer_t buf; buf.printf( "$,%i,%i", player->get_player_nr(), delta); return call_tool_init(TOOL_CHANGE_PLAYER | SIMPLE_TOOL, buf, 0, welt->get_public_player()); } SQInteger player_get_my_player(HSQUIRRELVM vm) { return script_api::param::push(vm, get_my_player(vm) ); } call_tool_init player_set_name(player_t *player, const char* name) { return script_api::command_rename(player, 'p', player->get_player_nr(), name); } void export_player(HSQUIRRELVM vm, bool scenario) { /** * Class to access player statistics. * Here, a player refers to one transport company, not to an individual playing simutrans. */ begin_class(vm, "player_x", "extend_get,ingame_object"); /** * Constructor. * @param nr player number, 0 = standard player, 1 = public player * @typemask (integer) */ // actually defined in simutrans/script/script_base.nut // register_function(..., "constructor", ...); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") if (!scenario) { /** * @returns player associated with the AI. * @ingroup ai_only * @typemask player_x() * @note Only available in AI mode. */ STATIC register_function(vm, &player_get_my_player, "self", 0, "", true); } /** * Return headquarters level. * @returns level, level is zero if no headquarters was built */ register_method(vm, &player_t::get_headquarter_level, "get_headquarter_level"); /** * Return headquarters position. * @returns coordinate, (-1,-1) if no headquarters was built */ register_method(vm, &player_t::get_headquarter_pos, "get_headquarter_pos"); /** * Return name of company. * @returns name */ register_method(vm, &player_t::get_name, "get_name"); /** * Sets name of company. * @param name the new name * @ingroup rename_func */ register_method(vm, &player_set_name, "set_name", true); /** * Get monthly statistics of construction costs. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_construction", freevariable(ATV_CONSTRUCTION_COST, TT_ALL, true), true); /** * Get monthly statistics of vehicle running costs. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_vehicle_maint", freevariable(ATV_RUNNING_COST, TT_ALL, true), true); /** * Get monthly statistics of costs for vehicle purchase. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_new_vehicles", freevariable(ATV_NEW_VEHICLE, TT_ALL, true), true); /** * Get monthly statistics of income. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_income", freevariable(ATV_REVENUE_TRANSPORT, TT_ALL, true), true); /** * Get monthly statistics of infrastructure maintenance. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_maintenance", freevariable(ATV_INFRASTRUCTURE_MAINTENANCE, TT_ALL, true), true); /** * Get monthly statistics of assets. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_assets", freevariable(ATV_NON_FINANCIAL_ASSETS, TT_ALL, true), true); /** * Get monthly statistics of cash. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_cash", freevariable(ATC_CASH, -1, true), true); /** * Get monthly statistics of net wealth. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_net_wealth", freevariable(ATC_NETWEALTH, -1, true), true); /** * Get monthly statistics of profit. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_profit", freevariable(ATV_PROFIT, TT_ALL, true), true); /** * Get monthly statistics of operating profit. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_operating_profit", freevariable(ATV_OPERATING_PROFIT, TT_ALL, true), true); /** * Get monthly statistics of margin. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_margin", freevariable(ATV_PROFIT_MARGIN, -1, true), true); /** * Get monthly statistics of all transported goods. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_transported", freevariable(ATV_TRANSPORTED, TT_ALL, true), true); /** * Get monthly statistics of income from powerlines. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_powerline", freevariable(ATV_REVENUE, TT_POWERLINE, true), true); /** * Get monthly statistics of transported passengers. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_transported_pax", freevariable(ATV_TRANSPORTED_PASSENGER, TT_ALL, true), true); /** * Get monthly statistics of transported mail. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_transported_mail", freevariable(ATV_TRANSPORTED_MAIL, TT_ALL, true), true); /** * Get monthly statistics of transported goods. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_transported_goods", freevariable(ATV_TRANSPORTED_GOOD, TT_ALL, true), true); /** * Get monthly statistics of number of convoys. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_convoys", freevariable(ATC_ALL_CONVOIS, -1, true), true); /** * Get monthly statistics of income/loss due to way tolls. * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_player_stat, "get_way_tolls", freevariable(ATV_WAY_TOLL, TT_ALL, true), true); if (scenario) { /** * Change bank account of player by given amount @p delta. * @param delta * @ingroup scen_only */ register_method(vm, player_book_account, "book_cash", true); } /** * Returns the current account balance. */ register_method(vm, &player_t::get_account_balance_as_double, "get_current_cash"); /** * Returns the current net worth [in 1/100 cr]. */ register_method(vm, &finance_t::get_netwealth, "get_current_net_wealth"); /** * Returns the current maintenance [in 1/100 cr]. */ register_method_fv(vm, &finance_t::get_maintenance_with_bits, "get_current_maintenance", freevariable(TT_ALL)); /** * Returns whether the player (still) exists in the game. * * @deprecated Only available for api versions less than 120.1, see @ref get_api_version. * @typemask bool() */ // register_method(vm,, "is_active", true); implemented in scenario_compat.nut /** * Exports list of lines of this player. * @typemask line_list_x() */ register_function(vm, &player_export_line_list, "get_line_list", 1, param::typemask()); /** * Creates a new line for the player of the given way type. * @param wt way type * @ingroup game_cmd */ register_method(vm, &player_create_line, "create_line", true); /** * Returns player type: 1 = human, 2,3 = old c++ AI, 4 = scripted AI */ register_method(vm, &player_t::get_ai_id, "get_type"); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_scenario.cc000066400000000000000000000341061520675460500234240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_scenario.cc exports scenario related functions. */ #include "../api_class.h" #include "../api_function.h" #include "../../dataobj/koord.h" #include "../../dataobj/koord3d.h" #include "../../dataobj/scenario.h" #include "../../dataobj/translator.h" #include "../../utils/simstring.h" #include "../../simintr.h" #include "../../simevent.h" using namespace script_api; static char buf[40]; static plainstring double_to_string(double f, sint32 decimals) { number_to_string(buf, f, decimals); return buf; } static plainstring integer_to_string(sint64 f) { number_to_string(buf, f, 0); return buf; } static plainstring money_to_string_intern(sint64 m) { money_to_string(buf, m, false); return buf; } static plainstring difftick_to_string_intern(sint32 f ) { return difftick_to_string( f, false ); } static plainstring koord_to_string_intern(koord k) { return k.get_str(); } static plainstring koord3d_to_string_intern(koord3d k) { return k.get_str(); } static plainstring get_tool_key_intern(uint16 tool_id) { vector_tpl *tool_list; uint16 category = tool_id & 0xF000; uint16 local_id = tool_id & 0x0FFF; switch (category) { case GENERAL_TOOL: tool_list = &tool_t::general_tool; break; case SIMPLE_TOOL: tool_list = &tool_t::simple_tool; break; case DIALOGE_TOOL: tool_list = &tool_t::dialog_tool; break; case TOOLBAR_TOOL: return ""; default: return ""; } if (!tool_list || local_id >= tool_list->get_count()) { return ""; } tool_t *tool = (*tool_list)[local_id]; if (!tool) { return ""; } // just to save some typing ... const uint16 key = tool->command_key; // no binding => finish if (key == 0 || key == 0xFFFF) { return ""; } char mod[2]; mod[0] = tool->command_flags == 2 ? '+' : (tool->command_flags == 1 ? '^' : 0); mod[1] = 0; // keypad if (key >= SIM_KEYCODE_NUMPAD_BASE && key < SIM_KEYCODE_NUMPAD_BASE + 10) { char str[8]; sprintf(str, "%sNUM_%d", mod, key - SIM_KEYCODE_NUMPAD_BASE); return str; } // function keys if (key >= SIM_KEYCODE_F1 && key <= SIM_KEYCODE_F15) { char str[8]; sprintf(str, "%sF%d", mod, key - SIM_KEYCODE_F1 + 1); return str; } // speacial keys switch (key) { case ',': return "COMMA"; // case SIM_KEYCODE_SCROLLLOCK: return "SCROLLLOCK"; case SIM_KEYCODE_PAUSE: return "PAUSE"; case SIM_KEYCODE_HOME: return "HOME"; case SIM_KEYCODE_END: return "END"; case SIM_KEYCODE_SPACE: return "SPACE"; case SIM_KEYCODE_ESCAPE: return "ESC"; case SIM_KEYCODE_DELETE: return "DELETE"; case SIM_KEYCODE_BACKSPACE: return "BACKSPACE"; } if (key < 32) { char str[6]; sprintf(str, "^%c", key + 64); return str; } if (key < 127) { char str[2]; str[0] = key; str[1] = 0; return str; } char str[10]; sprintf(str, "%s#%d", mod, key); return str; } void export_string_methods(HSQUIRRELVM vm) { /** * Helper method to translate strings, needed to identify the correct overloaded function * @param text string to translate * @returns translation of given string */ register_method(vm, &translator::translate, "translate"); /** * Pretty-print floating point numbers, use language specific separator for powers of thousands. * @param value number to print * @param decimals how many decimals should be printed * @returns a nice string */ register_method(vm, &double_to_string, "double_to_string"); /** * Pretty-print integers, use language specific separator for powers of thousands. * @param value number to print * @returns a nice string */ register_method(vm, &integer_to_string, "integer_to_string"); /** * Pretty-print money values, use language specific separator for powers of thousands. * @param value number to print * @returns a nice string with trailing dollar-sign */ register_method(vm, &money_to_string_intern, "money_to_string"); /** * Print coordinates, does automatic rotation. * @param c coordinate * @returns a nice string */ register_method(vm, &koord_to_string_intern, "coord_to_string"); /** * Print coordinates, does automatic rotation. * @param c coordinate * @returns a nice string */ register_method(vm, &koord3d_to_string_intern, "coord3d_to_string"); /** * Get name of given month. * @param month number between 0 (january) and 11 (december) * @returns month name in language of server */ register_method(vm, &translator::get_month_name, "get_month_name"); /** * Translates difference of ticks to string which could even have months in it. * @param difftick_to_string_intern ticks in simutrans units * @returns time difference using the current local setting */ register_method(vm, &difftick_to_string_intern, "difftick_to_string"); /** * Gets the tool_key (shortcut key) associated with a general tool by its ID. * The ID corresponds to the index used in menuconf.tab (e.g. 0 = query tool, 1 = remover, etc.). * Returns the hotkey as a string (e.g. "r", "M", "COMMA, "^s, "+F10", "#1234") without quotation marks * or an empty string if there is no hotkey or invalid ID. * @param tool_id the numeric ID of the general tool (as defined in menuconf.tab) * @returns a string representing the hotkey, or empty if none or invalid */ register_method(vm, &get_tool_key_intern, "get_tool_key"); } void export_scenario(HSQUIRRELVM vm) { /** * Helper method to load scenario-related translation files. * Tries to load files in the following order relative to pakxx/scenario: * -# scenario-name/iso/filename * -# scenario-name/en/filename * -# scenario-name/filename * * Here, iso refers to iso-abbreviation of currently active language. * * The content of the files is cached. The cache is cleared upon reloading of savegame. * @param file name of txt-file * @return content of loaded file * @note Only available in scenario mode. * @ingroup scen_only */ register_method(vm, &scenario_t::load_language_file, "load_language_file"); /** * Table with methods to forbid and allow tools. * * Tools that are set to forbidden using the forbid_* methods can be allowed * again by calls to the respective allow_* method with exact the same parameters. * * @note Only available in scenario mode. * @ingroup scen_only */ begin_class(vm, "rules", 0); /** * Forbid an internal tool. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::forbid_tool, "forbid_tool"); /** * Forbid an internal tool with certain waytype. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param default_paramter object name or null (to catch all) * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::forbid_way_tool, "forbid_way_tool"); /** * Forbid an internal tool with certain waytype within rectangular region on the map. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param default_paramter object name or null (to catch all) * @param pos_nw coordinate of north-western corner of rectangle * @param pos_se coordinate of south-eastern corner of rectangle * @param err error message presented to user when trying to apply this tool, see also @ref is_work_allowed_here * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::forbid_way_tool_rect, "forbid_way_tool_rect"); /** * Forbid an internal tool with certain waytype within cubic region on the map. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param default_paramter object name or null (to catch all) * @param pos_nw 3d-coordinate of north-western corner of cube * @param pos_se 3d-coordinate of south-eastern corner of cube * @param err error message presented to user when trying to apply this tool, see also @ref is_work_allowed_here * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::forbid_way_tool_cube, "forbid_way_tool_cube"); /** * Allow again an internal tool. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::clear_forbid_tool, "clear_forbid_tool"); /** * clear fule with certain waytype. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param default_paramter object name or 0 (to catch all) * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::clear_forbid_way_tool, "clear_forbid_way_tool"); /** * clear rule with certain waytype within rectangular region on the map. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param default_paramter object name or 0 (to catch all) * @param pos_nw coordinate of north-western corner of rectangle * @param pos_se coordinate of south-eastern corner of rectangle * @param allow clear either allo (true) or forbid (false) rule * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::clear_way_tool_rect, "clear_way_tool_rect"); /** * clear rule with certain waytype within cubic region on the map. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param default_paramter object name or 0 (to catch all) * @param pos_nw 3d-coordinate of north-western corner of cube * @param pos_se 3d-coordinate of south-eastern corner of cube * @param allow clear either allo (true) or forbid (false) rule * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::clear_way_tool_cube, "clear_way_tool_cube"); /** * Clear all forbidding rules, effectively allowing all tools again that were forbidden using functions of the table @ref rules. * * Only effects tools forbidden by rules::forbid_tool, rules::forbid_way_tool, rules::forbid_way_tool_cube, rules::forbid_way_tool_rect. * The result of ::is_tool_allowed and ::is_work_allowed_here is not influenced. * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::clear_rules, "clear"); /** * Clear all forbidding rules for a selec player * * Only effects tools forbidden by rules::forbid_tool, rules::forbid_way_tool, rules::forbid_way_tool_cube, rules::forbid_way_tool_rect. * The result of ::is_tool_allowed and ::is_work_allowed_here is not influenced. * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::clear_player_rules, "clear_player"); /** * Allow tool with certain waytype within rectangular region on the map. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param default_paramter object name or 0 (to catch all) * @param pos_nw coordinate of north-western corner of rectangle * @param pos_se coordinate of south-eastern corner of rectangle * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::allow_way_tool_rect, "allow_way_tool_rect"); /** * Allow tool with certain waytype within cubic region on the map. * * @param player_nr number of player this rule applies to, * if this is set to player_all then this acts for all players except public player * @param tool_id id of tool * @param wt waytype * @param default_paramter object name or 0 (to catch all) * @param pos_nw 3d-coordinate of north-western corner of cube * @param pos_se 3d-coordinate of south-eastern corner of cube * @see tool_ids way_types player_all * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::allow_way_tool_cube, "allow_way_tool_cube"); /** * Signals that toolbars and active tools need to be checked against scenario rules again. * * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::gui_needs_update, "gui_needs_update"); end_class(vm); /** * Table with methods help debugging. * @note Only available in scenario mode. * @ingroup scen_only */ begin_class(vm, "debug", 0); /** * @returns text containing all active rules, can be used in @ref get_debug_text * @note Only available in scenario mode. * @ingroup scen_only */ STATIC register_method(vm, &scenario_t::get_forbidden_text, "get_forbidden_text"); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_schedule.cc000066400000000000000000000145661520675460500234250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_schedule.cc exports schedule structure and related functions. */ #include "../api_class.h" #include "../api_function.h" #include "../../dataobj/schedule.h" #include "../../simhalt.h" #include "../../simintr.h" #include "../../world/simworld.h" using namespace script_api; SQInteger schedule_entry_constructor(HSQUIRRELVM vm) // instance, coord3d, load, wait { koord3d pos = param::get(vm, 2); // world coordinates uint8 load = param::get(vm, 3); uint16 wait = param::get(vm, 4); // transform coordinates to squirrel coordinates koord k(pos.get_2d()); coordinate_transform_t::koord_w2sq(k); // propagate error SQInteger res = pos != koord3d::invalid ? SQ_OK : SQ_ERROR; if (SQ_SUCCEEDED(res)) { res = set_slot(vm, "x", k.x, 1); } if (SQ_SUCCEEDED(res)) { res = set_slot(vm, "y", k.y, 1); } if (SQ_SUCCEEDED(res)) { res = set_slot(vm, "z", pos.z, 1); } if (SQ_SUCCEEDED(res)) { res = set_slot(vm, "wait", wait, 1); } if (SQ_SUCCEEDED(res)) { res = set_slot(vm, "load", load, 1); } return res; } halthandle_t get_halt_from_koord3d(koord3d pos, const player_t *player ) { if( player == NULL ) { return halthandle_t(); } return haltestelle_t::get_halt(pos, player); } SQInteger waiting_time_to_string(HSQUIRRELVM vm) { schedule_entry_t entry(koord3d::invalid, 0, 0); get_slot(vm, "wait", entry.waiting_time, -1); plainstring str = difftick_to_string(entry.get_waiting_ticks(), false); return param::push(vm, str); } SQInteger schedule_constructor(HSQUIRRELVM vm) // instance, wt, entries = [], current = 0 { waytype_t wt = param::get(vm, 2); SQInteger res = set_slot(vm, "waytype", wt, 1); if (SQ_SUCCEEDED(res)) { sq_pushstring(vm, "entries", -1); // entries parameter if (sq_gettop(vm) >= 3) { sq_push(vm, 3); } else { // not provided, push empty array sq_newarray(vm, 4); } res = sq_set(vm, 1); } if (SQ_SUCCEEDED(res)) { uint8 current = 0; if (sq_gettop(vm) >= 4) { current = param::get(vm, 4); } res = set_slot(vm, "current", current, 1); } if (SQ_SUCCEEDED(res)) { // attach a schedule instance // the c++ schedule_t instance will be refilled everytime when transfered to the c++ side of things. schedule_t* sched = NULL; switch(wt) { case road_wt: sched = new truck_schedule_t(); break; case track_wt: sched = new train_schedule_t(); break; case water_wt: sched = new ship_schedule_t(); break; case air_wt: sched = new airplane_schedule_t(); break; case monorail_wt: sched = new monorail_schedule_t(); break; case tram_wt: sched = new tram_schedule_t(); break; case maglev_wt: sched = new maglev_schedule_t(); break; case narrowgauge_wt: sched = new narrowgauge_schedule_t(); break; default: sq_raise_error(vm, "Invalid waytype %d", wt); return SQ_ERROR; } attach_instance(vm, 1, sched); } return res; } // read entry at index, append to schedule void append_entry(HSQUIRRELVM vm, SQInteger index, schedule_t* sched) { koord3d pos = param::get(vm, index); uint8 minimum_loading = 0; get_slot(vm, "load", minimum_loading, index); uint16 waiting_time_shift = 0; get_slot(vm, "wait", waiting_time_shift, index); grund_t *gr = welt->lookup(pos); if (gr) { sched->append(gr, minimum_loading, waiting_time_shift); } } schedule_t* script_api::param::get(HSQUIRRELVM vm, SQInteger index) { // get waytype waytype_t wt = invalid_wt; get_slot(vm, "waytype", wt, index); // get instance pointer schedule_t* sched = get_attached_instance(vm, index, param::tag()); if (sched) { // check waytype if (wt != sched->get_waytype()) { sq_raise_error(vm, "Waytype mismatch in schedule"); return NULL; } sched->entries.clear(); // now read the entries sq_pushstring(vm, "entries", -1); SQInteger new_index = index > 0 ? index : index-1; if (SQ_SUCCEEDED(sq_get(vm, new_index))) { // now entries array at the top of the stack // foreach loop sq_pushnull(vm); while(SQ_SUCCEEDED(sq_next(vm, -2))) { append_entry(vm, -1, sched); sq_pop(vm, 2); } sq_pop(vm, 1); } // current stop uint8 current = 0; get_slot(vm, "current", current, index); sched->set_current_stop(current); } return sched; } void* script_api::param::tag() { return (void*)&script_api::param::get; } void export_schedule(HSQUIRRELVM vm) { /** * Schedule entries */ begin_class(vm, "schedule_entry_x", "coord3d"); #ifdef SQAPI_DOC // document members /** * Minimum loading percentage. */ integer load; /** * Waiting time setting. */ integer wait; #endif /** * Constructor * @param pos coordinate of entry * @param wait waiting time * @param load minimum loading */ register_function(vm, schedule_entry_constructor, "constructor"); /** * Returns halt at this entry position. * @param pl player that wants to use halt here * @return halt instance * @typemask halt_x(player_x) */ register_method(vm, &get_halt_from_koord3d, "get_halt", true); /** * Returns waiting time formatted as string. * @typemask string() */ register_function(vm, waiting_time_to_string, "waiting_time_to_string", 1, "x" ); end_class(vm); /** * Class holding the schedule */ create_class(vm, "schedule_x"); /** * Constructor. * Parameters @p entries and @p current are optional. * @param wt way type * @param entries array of entries of the new schedule (default = []) * @param current current stop of schedule (default = 0) * * @typemask command_x(way_types, array< schedule_entry_x >, integer) */ register_function(vm, schedule_constructor, "constructor", 3, "xi."); sq_settypetag(vm, -1, param::tag()); #ifdef SQAPI_DOC // document members /** * The list of schedule targets. */ array< schedule_entry_x > entries; /** * Waytype of schedule. */ way_types waytype; /** * Current stop of schedule: * default first stop (schedule of line) * or next stop of convoy (schedule of convoy). * * In order to change this value for a convoy * call @ref convoy_x::change_schedule. */ integer current; #else create_slot(vm, "entries", 0); create_slot(vm, "waytype", 0); create_slot(vm, "current", 0); #endif end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_settings.cc000066400000000000000000000124601520675460500234600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_settings.cc exports game settings functions. */ #include "api_command.h" #include "api_simple.h" #include "../api_class.h" #include "../api_function.h" #include "../../dataobj/settings.h" #include "../../descriptor/ground_desc.h" #include "../../ground/grund.h" #include "../../tool/simmenu.h" #include "../../world/simworld.h" using namespace script_api; mytime_t get_start_time(settings_t* settings) { uint32 yearmonth = 12*( max( settings->get_starting_year(),0) ) + max( settings->get_starting_month(),0); return yearmonth; } call_tool_init set_traffic_level(settings_t*, sint16 rate) { cbuffer_t buf; buf.printf("%i", rate); return call_tool_init(TOOL_TRAFFIC_LEVEL | SIMPLE_TOOL, buf, 0, welt->get_public_player()); } sint8 get_underground_view_level() { return grund_t::underground_level; } bool has_double_slopes() { return ground_desc_t::double_grounds; } void export_settings(HSQUIRRELVM vm) { /** * Table with methods to access game settings. */ begin_class(vm, "settings", 0); /** * New industries will be spawned if cities grow to over 2^n times this setting. * @returns number */ register_method(vm, &settings_t::get_industry_increase_every, "get_industry_increase_every"); /** * New industries will be spawned if cities grow to over 2^n times @p count. * Set to zero to prevent new industries to be spawned. * @param count * @warning cannot be used in network games. */ register_method(vm, &settings_t::set_industry_increase_every, "set_industry_increase_every"); /** * Get traffic level. */ register_method(vm, &settings_t::get_traffic_level, "get_traffic_level"); /** * Set traffic level. The higher the level the more city cars will be created. * @param rate new traffic level, must be between 0 and 16 */ register_method(vm, &set_traffic_level, "set_traffic_level", true); /** * Returns starting time of the game. * @returns table { "year" = .., "month" = .. } */ register_local_method(vm, get_start_time, "get_start_time"); /// @returns station coverage register_method(vm, &settings_t::get_station_coverage, "get_station_coverage"); /// @returns passenger factors influences passenger generation in cities register_method(vm, &settings_t::get_passenger_factor, "get_passenger_factor"); /// @returns maximum distance of city to factory for supplying workers register_method(vm, &settings_t::get_factory_worker_radius, "get_factory_worker_radius"); /// @returns minimum number of cities to supply workers for a factory register_method(vm, &settings_t::get_factory_worker_minimum_towns, "get_factory_worker_minimum_towns"); /// @returns maximum number of cities to supply workers for a factory register_method(vm, &settings_t::get_factory_worker_maximum_towns, "get_factory_worker_maximum_towns"); /// @returns freight will not enter convoy if next transfer halt is overcrowded register_method(vm, &settings_t::is_avoid_overcrowding, "avoid_overcrowding"); /// @returns freight will not start when best route goes through overcrowded halt register_method(vm, &settings_t::is_no_routing_over_overcrowding, "no_routing_over_overcrowding"); /// @returns true if halt capacity is separated between passengers, mail, freight register_method(vm, &settings_t::is_separate_halt_capacities, "separate_halt_capacities"); /// @returns true if it is allowed to buy obsolete vehicles register_method(vm, &settings_t::get_allow_buying_obsolete_vehicles, "obsolete_vehicles_allowed"); /// @returns max number of vehicles of road convois register_method(vm, &settings_t::get_max_road_convoi_length, "get_max_road_convoi_length"); /// @returns max number of vehicles of rail convois register_method(vm, &settings_t::get_max_rail_convoi_length, "get_max_rail_convoi_length"); /// @returns max number of vehicles of ship convois register_method(vm, &settings_t::get_max_ship_convoi_length, "get_max_ship_convoi_length"); /// @returns max number of vehicles of air convois register_method(vm, &settings_t::get_max_air_convoi_length, "get_max_air_convoi_length"); /// @returns true, if drive-on-left is on register_method(vm, &settings_t::is_drive_left, "get_drive_on_left"); /// @returns minimal z-distance between tiles with ways (possible values = 1,2) register_method(vm, &settings_t::get_way_height_clearance, "get_way_height_clearance"); /// @returns whether paksets support two different slope heights register_method(vm, &has_double_slopes, "has_double_slopes", true); /** * Three modes of transfer payment: * 0 = pay for travelled Manhattan distance * 1 = pay for distance difference to next transfer stop * 2 = pay for distance to destination */ register_method(vm, &settings_t::get_pay_for_total_distance_mode, "get_pay_for_total_distance_mode"); /** * Returns current view mode and underground view level. * Possible return values: * -128 == full underground view * +127 == normal view * other values == sliced view at the given value */ register_method(vm, &get_underground_view_level, "get_underground_view_level", true); /** * Months after which public player can take over ways/bridges/tunnels * @returns number of months (uint16) */ register_method(vm, &settings_t::get_make_public_months, "get_make_public_months"); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_simple.cc000066400000000000000000000241041520675460500231070ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_simple.cc exports simple data types. */ #include "api_simple.h" #include "../api_class.h" #include "../api_function.h" #include "../../dataobj/ribi.h" #include "../../world/simworld.h" #include "../../dataobj/scenario.h" using namespace script_api; #ifdef DOXYGEN /** * Struct to hold information about time as month-year. * If used as argument to functions then only the value of @ref raw matters. * If filled by an api-call the year and month will be set, too. * Relation: raw = 12*month + year. * @see world::get_time obj_desc_time_x */ class time_x { // begin_class("time_x") public: #ifdef SQAPI_DOC // document members integer raw; ///< raw integer value of date integer year; ///< year integer month; ///< month in 0..11 #endif }; // end_class /** * Struct to get precise information about time. * @see world::get_time */ class time_ticks_x : public time_x { // begin_class("time_ticks_x", "time_x") public: #ifdef SQAPI_DOC // document members integer ticks; ///< current time in ticks integer ticks_per_month; ///< length of one in-game month in ticks integer next_month_ticks; ///< new month will start at this time #endif }; // end_class #endif // pushes table = { raw = , year = , month = } SQInteger param::push(HSQUIRRELVM vm, mytime_t const& v) { sq_newtableex(vm, 6); create_slot(vm, "raw", v.raw); create_slot(vm, "year", v.raw/12); create_slot(vm, "month", v.raw%12); return 1; } SQInteger param::push(HSQUIRRELVM vm, mytime_ticks_t const& v) { param::push(vm, v); create_slot(vm, "ticks", v.ticks); create_slot(vm, "ticks_per_month", v.ticks_per_month); create_slot(vm, "next_month_ticks", v.next_month_ticks); return 1; } SQInteger param::push(HSQUIRRELVM vm, mytool_data_t const& v) { sq_newtableex(vm, 6); create_slot(vm, "start_pos", v.start_pos); create_slot(vm, "is_drag_tool", v.is_drag_tool); create_slot(vm, "is_ctrl", v.is_ctrl); create_slot(vm, "is_shift", v.is_shift); return 1; } mytime_t param::get(HSQUIRRELVM vm, SQInteger index) { // 0 has special meaning of 'no-timeline' SQInteger i=1; if (!SQ_SUCCEEDED(sq_getinteger(vm, index, &i))) { get_slot(vm, "raw", i, index); } return (uint16) (i >= 0 ? i : 1); } #define map_ribi_any(f,type) \ type export_## f(my_ribi_t r) \ { \ return ribi_t::f(r); \ } // export the ribi functions map_ribi_any(is_single, bool); map_ribi_any(is_twoway, bool); map_ribi_any(is_threeway, bool); map_ribi_any(is_bend, bool); map_ribi_any(is_straight, bool); map_ribi_any(doubles, my_ribi_t); map_ribi_any(backward, my_ribi_t); my_slope_t ribi_to_slope(my_ribi_t ribi) { return slope_type((ribi_t::ribi)ribi); } my_ribi_t slope_to_ribi(my_slope_t slope) { return ribi_type((slope_t::type)slope); } template SQInteger coord_to_ribi(HSQUIRRELVM vm) { // get coordinate vector without transformation sint16 x=-1, y=-1; get_slot(vm, "x", x, idx); get_slot(vm, "y", y, idx); koord k(x,y); // and do not transform ribi either uint8 ribi = ribi_type(k); return param::push(vm, ribi); } SQInteger ribi_to_coord(HSQUIRRELVM vm) { const uint8 ribi = param::get(vm, 2); if ((ribi & ~(uint8)ribi_t::all) != 0) { return sq_raise_error(vm, "Invalid dir %hhu (valid values are 0..15)", ribi); } koord k( (ribi_t::ribi)ribi ); return push_instance(vm, "coord", k.x, k.y); } void export_simple(HSQUIRRELVM vm) { /** * Class that holds 2d coordinates. * All functions that use this as input parameters will accept every data structure that contains members x and y. * * Coordinates always refer to the original rotation in @ref map.file. * They will be rotated if transferred between the game engine and squirrel. */ begin_class(vm, "coord"); #ifdef SQAPI_DOC // document members /// x-coordinate integer x; /// y-coordinate integer y; // operators are defined in script_base.nut coord(int x, int y); coord operator + (coord other); coord operator - (coord other); coord operator - (); coord operator * (integer fac); coord operator / (integer fac); /** * Converts coordinate to string containing the coordinates in the current rotation of the map. * * Cannot be used in links in scenario texts. Use @ref href instead. */ string _tostring(); /** * Generates text to generate links to coordinates in scenario texts. * @param text text to be shown in the link * @returns a-tag with link in href * @see get_rule_text */ string href(string text); #endif /** * Helper function to convert direction vector to dir type. * @typemask dir() */ register_function(vm, coord_to_ribi<1>, "to_dir", 1, "t|x|y"); end_class(vm); /** * Class that holds 3d coordinates. * All functions that use this as input parameters will accept every data structure that contains members x, y, and z. * * Coordinates always refer to the original rotation in @ref map.file. * They will be rotated if transferred between the game engine and squirrel. */ begin_class(vm, "coord3d", "coord"); #ifdef SQAPI_DOC // document members /// x-coordinate integer x; /// y-coordinate integer y; /// z-coordinate - height integer z; // operators are defined in script_base.nut coord(int x, int y, int z); coord3d operator + (coord3d other); coord3d operator - (coord other); coord3d operator + (coord3d other); coord3d operator - (coord other); coord3d operator - (); coord3d operator * (integer fac); coord3d operator / (integer fac); /** * Converts coordinate to string containing the coordinates in the current rotation of the map. * * Cannot be used in links in scenario texts. Use @ref href instead. */ string _tostring(); /** * Generates text to generate links to coordinates in scenario texts. * @param text text to be shown in the link * @returns a-tag with link in href * @see get_rule_text */ string href(string text); #endif end_class(vm); /** * Helper function to convert direction vector to dir type. * @param dir * @typemask dir(coord) */ register_function(vm, coord_to_ribi<2>, "coord_to_dir", 2, "x t|x|y"); /** * Class holding static methods to work with directions. * Directions are just bit-encoded integers. */ begin_class(vm, "dir"); #ifdef SQAPI_DOC // document members /** @name Named directions. */ //@{ static const dir none; static const dir north; static const dir east; static const dir northeast; static const dir south; static const dir northsouth; static const dir southeast; static const dir northsoutheast; static const dir west; static const dir northwest; static const dir eastwest; static const dir northeastwest; static const dir southwest; static const dir northsouthwest; static const dir southeastwest; static const dir all; //@} #endif /** * @param d direction to test * @return whether direction is single direction, i.e. just one of n/s/e/w */ STATIC register_method(vm, &export_is_single, "is_single", false, true); /** * @param d direction to test * @return whether direction is double direction, e.g. n+e, n+s. */ STATIC register_method(vm, &export_is_twoway, "is_twoway", false, true); /** * @param d direction to test * @return whether direction is triple direction, e.g. n+s+e or n+s+e+w. */ STATIC register_method(vm, &export_is_threeway, "is_threeway", false, true); /** * @param d direction to test * @return whether direction is curve, e.g. n+e, s+w. */ STATIC register_method(vm, &export_is_bend, "is_curve", false, true); /** * @param d direction to test * @return whether direction is straight and has no curves in it, e.g. n+s, w. */ STATIC register_method(vm, &export_is_straight, "is_straight", false, true); /** * @param d direction * @return complements direction to complete straight, i.e. w -> w+e, but n+w -> 0. */ STATIC register_method(vm, &export_doubles, "double", false, true); /** * @param d direction to test * @return backward direction, e.g. w -> e, n+w -> s+e, n+w+s -> e. */ STATIC register_method(vm, &export_backward, "backward", false, true); /** * Converts direction to slope: direction goes upward on slope. * @param d direction */ STATIC register_method(vm, &ribi_to_slope, "to_slope", false, true); /** * Helper function to convert direction vector to dir type. * @typemask coord(dir) */ STATIC register_function(vm, ribi_to_coord, "to_coord", 2, ".i", true); end_class(vm); /** * Class holding static methods to work with slopes. * Slopes are just integers. */ begin_class(vm, "slope"); #ifdef SQAPI_DOC // document members /** @name Named slopes. */ //@{ static const slope flat; static const slope north; ///< North slope static const slope west; ///< West slope static const slope east; ///< East slope static const slope south; ///< South slope static const slope northwest; ///< NW corner static const slope northeast; ///< NE corner static const slope southeast; ///< SE corner static const slope southwest; ///< SW corner static const slope raised; ///< special meaning: used as slope of bridgeheads static const slope all_up_slope = 82; ///< used for terraforming tools static const slope all_down_slope = 83; ///< used for terraforming tools //@} #endif /** * Converts slope to dir: direction goes upward on slope. * If slope cannot be walked on, it returns @ref dir::none. * @param s slope */ STATIC register_method(vm, &slope_to_ribi, "to_dir", false, true); end_class(vm); #ifdef SQAPI_DOC /** * Classes to access in-game objects. * Creating an instance of such classes will not create an object in the game. * Instead, these classes contain data to access the in-game object. * If the in-game object disappears, then any access to it via these classes will fail. * Use the method @r ingame_object::is_valid to check whether the in-game object is still alive. */ class ingame_object { /** * @returns true if in-game object can still be accessed. */ bool is_valid(); } #endif } simutrans-124.5/src/simutrans/script/api/api_simple.h000066400000000000000000000027721520675460500227600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_API_SIMPLE_H #define SCRIPT_API_API_SIMPLE_H #include "../../../squirrel/squirrel.h" #include "../../dataobj/ribi.h" #include "../../dataobj/koord3d.h" /** @file api_simple.h Helper functions to export simple datatypes: ribi & friends */ /** * Use own type for ribis and slopes */ struct my_ribi_t { uint8 data; my_ribi_t(ribi_t::ribi r) : data(r) { } operator ribi_t::ribi() const { return data; } }; struct my_slope_t { uint8 data; my_slope_t(slope_t::type r) : data(r) { } operator slope_t::type() const { return data; } }; /** * Coordinate that accepts either 2d or 3d coordinates. * Converts to kartenboden/ground when supplied with 2d coordinates. */ struct my_koord3d { koord3d data; my_koord3d(koord3d k) : data(k) { } operator koord3d() const { return data; } }; namespace script_api { struct mytime_t { uint32 raw; mytime_t(uint32 r_) : raw(r_) {} }; struct mytime_ticks_t : public mytime_t { uint32 ticks; uint32 ticks_per_month; uint32 next_month_ticks; mytime_ticks_t(uint32 r, uint32 t, uint32 tpm, uint32 nmt) : mytime_t(r), ticks(t), ticks_per_month(tpm), next_month_ticks(nmt) {} }; struct mytool_data_t { koord3d start_pos; bool is_drag_tool; bool is_ctrl; bool is_shift; mytool_data_t(koord3d pos, bool drag, bool ctrl, bool shift) : start_pos(pos), is_drag_tool(drag), is_ctrl(ctrl), is_shift(shift) {} }; }; #endif simutrans-124.5/src/simutrans/script/api/api_skeleton.cc000066400000000000000000000266661520675460500234610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /** @file api_skeleton.cc Documents the necessary functions to be implemented by a script */ // It is in a C++ file to be processed by Doxygen filters to get function typemasks right #ifdef DOXYGEN /** * This function is called when the scenario starts. Do all the initializations here, * as you cannot initialize global variables with non-built-in squirrel types. * @typemask void() * @ingroup scen_skel */ register_function("start"); /** * This function is called when the AI starts. Do all the initializations here, * as you cannot initialize global variables with non-built-in squirrel types. * * @param pl_num the number of the AI player. Call player_x(pl_num) to obtain * a corresponding player_x instance. Definitely store this value! * * @typemask void(int) * @ingroup ai_skel */ register_function("start"); /** * This function is called when a savegame with active scenario is loaded. * Do all the initializations and post-processing here. * @typemask void() * @ingroup scen_skel * @ingroup quick_return_func */ register_function("resume_game"); /** * This function is called when a savegame with active AI player is loaded. * Do all the initializations and post-processing here. * * @param pl_num the number of the AI player. Call player_x(pl_num) to obtain * a corresponding player_x instance. Definitely store this value! * @typemask void(int) * @ingroup ai_skel */ register_function("resume_game"); /** * The heartbeat of the AI player. Here, all AI-related calculations and work can be done. * * @typemask void() * @ingroup ai_skel */ register_function("step"); /** * Called at the beginning of a new month. * Statistics of the last (complete) month is now in position [1] of any statistics array. * @typemask void() * @ingroup scen_skel * @ingroup ai_skel */ register_function("new_month") /** * Called at the beginning of a new year. * Statistics of the last (complete) year is now in position [1] of any statistics array. * @typemask void() * @ingroup scen_skel * @ingroup ai_skel */ register_function("new_year") /** * Called before starting the scenario. * Should return filename of the savegame to be used for this scenario. * If it returns "" the scenario is started with the currently running world. * By default returns the string @ref map.file. * @returns filename or "" * @typemask string() * @ingroup scen_skel * @ingroup quick_return_func */ register_function("get_map_file") /** * Text shown in the 'About' tab in the scenario info window. * * There is a default implementation, which returns concatenation * of #scenario author, version, short_description. * Html-like tags can be used, see @ref get_rule_text. * * @param pl player number of active player * @typemask string(integer) * @ingroup scen_skel */ register_function("get_about_text"); /** * Text shown in the 'Rules' tab in the scenario info window. * Must return quickly and must not modify the map! Calling tools is forbidden! * * Text can contain several html-like tags: * - \, \: line break * - \, \, \, \: text between start and end tag will be colored. It's a matter of taste, of course. * - \: insert hyper link, text between start and end tag will be colored blue. * - link to another tab of scenario info window: href="tabname", where tabname is one of: info, goal, rules, result, about * - link to position on the map: href="(x,y)" or href="(x,y,z)", click on link will jump to the map position * - call scripted method: href="script:bla(1)" will call bla(1). The characters \>, ', \" are not allowed in the string and will produce weird results. * The called method must return quickly and must not modify the map! Calling tools is forbidden! * * @code *

Here is an example.

*
* Do not build anything at the position
near Cologne. * The mayor of Berlin seems to be frustrated with your airport building capabilities. *
* Your results can be found in the results tab. * @endcode * * @param pl player number of active player * @typemask string(integer) * @ingroup scen_skel */ register_function("get_rule_text"); /** * Text shown in the 'Goal' tab in the scenario info window. * * Html-like tags can be used, see @ref get_rule_text. * * @param pl player number of active player * @typemask string(integer) * @ingroup scen_skel */ register_function("get_goal_text"); /** * Text shown in the 'Info' tab in the scenario info window. * * Html-like tags can be used, see @ref get_rule_text. * * @param pl player number of active player * @typemask string(integer) * @ingroup scen_skel */ register_function("get_info_text"); /** * Text shown in the 'Result' tab in the scenario info window. * * Html-like tags can be used, see @ref get_rule_text. * * @param pl player number of active player * @typemask string(integer) * @ingroup scen_skel */ register_function("get_result_text"); /** * Text shown in the 'Debug' tab in the scenario info window. * If the method returns null then the 'Debug'-tab will not be shown. * * Html-like tags can be used, see @ref get_rule_text. * * @param pl player number of active player * @typemask string(integer) * @ingroup scen_skel */ register_function("get_debug_text"); /** * Returns string containing the version of the api * that the scenario supports. * By default returns the string @ref scenario.api. * * If it returns "*" then this indicates that the scenario works in the most current api version. * Currently "112.3" and "120.1" are supported. * * @typemask string() * @ingroup scen_skel * @ingroup quick_return_func */ register_function("get_api_version"); /** * Core function of a scenario: It returns the completion percentage for the * specified player. * * If it returns a negative value the respective player has lost. This state will not be changed * even if the function later returns positive values again. * * If it returns a value >= 100 then the respective player has won. This state will not be changed * even if the function later returns lower values again. * * @param pl player number of active player * @typemask integer(integer) * @ingroup scen_skel */ register_function("is_scenario_completed"); /** * Function is called when toolbars are filled with icons or when a tool should be activated. * * There is a default implementation, which forbids map-editing tools. * * @attention Results are not transfered over network, use the functions provided in #rules in this case. * * @param pl player number * @param tool_id see @ref tool_ids * @param wt waytype of tool * @param name is parameter (string) i.e. description for way tools * @returns true if tool is allowed. * @typemask bool(integer,integer,way_types) * @ingroup scen_skel * @ingroup quick_return_func */ register_function("is_tool_allowed"); /** * Called when user clicks on a tile to build or remove something. * * This function is network-aware: * Error messages are sent back over network to clients. * * The error message can contain a coordinate, which is used to show a location on the map. * In order to show the right place, use @ref coord_to_string. The must be enclosed in parentheses * or prefixed with @ . * @code return "You cannot do this. The guy living at (" + coord_to_string({x=47, y=11}) + ") does not like you!" @endcode * * @attention Does not work with waybuilding and all tools that need path-finding, use the functions provided in #rules in this case. * * @param pl player number * @param tool_id see @ref tool_ids * @param name is parameter (string) i.e. description for way tools * @param pos coordinate * * @return null if allowed, an error message otherwise * @typemask string(integer,integer,coord3d) * @ingroup scen_skel * @ingroup quick_return_func */ register_function("is_work_allowed_here"); /** * Called when user changed a schedule (closes schedule window). * * @warning Function will NOT be called in network games. * * @param pl player number * @param schedule the schedule * * @return null if allowed, an error message otherwise * @typemask string(integer,schedule_x) * @ingroup scen_skel * @ingroup quick_return_func */ register_function("is_schedule_allowed"); /** * Called when user wants to start convoy. * * @warning Function will NOT be called in network games. * * @param pl player number * @param convoy convoy to start * @param depot convoy is in this depot * * @return null if allowed, an error message otherwise * @typemask string(integer,convoy_x,depot_x) * @ingroup scen_skel * @ingroup quick_return_func */ register_function("is_convoy_allowed"); /** * Called when player click link in scenario windows. * * @param pos coordinate go to in link * * @return null if allowed, an error message otherwise * @typemask string(coord3d) * @ingroup scen_skel * @ingroup quick_return_func */ register_function("jump_to_link_executed"); /** * Initializes the tool. * @returns true upon success. * * @param pl player instance to use this tool. * @ingroup tool_skel * @ingroup quick_return_func * @typemask bool(player_x) */ register_function("init"); /** * Exits the tool. Do cleanup here. * @returns true upon success. * * @param pl player instance to use this tool. * @ingroup tool_skel * @ingroup quick_return_func * @typemask bool(player_x) */ register_function("exit"); /** * Does the work (for tools of one-click type). * @returns null upon success, an error message otherwise. * * @param pl player instance to use this tool. * @param pos tile clicked by user, here the work should be done. * @param keys state of ctrl/shift keys. * @ingroup tool_skel * @typemask string(player_x, coord3d, int) */ register_function("work"); /** * Does the work (for tools of two-click type). * @returns null upon success, an error message otherwise. * * @param pl player instance to use this tool. * @param start first tile clicked by user. * @param end second tile clicked by user. * @param keys state of ctrl/shift keys. * @ingroup tool_skel * @typemask string(player_x, coord3d, coord3d, int) */ register_function("do_work"); /** * Mark tiles for working (for tools of two-click type). * Call @ref mark_tile from here. * * @param pl player instance to use this tool. * @param start first tile clicked by user. * @param end second tile clicked by user. * @param keys state of ctrl/shift keys. * @ingroup tool_skel * @typemask void(player_x, coord3d, coord3d, int) */ register_function("mark_tiles"); /** * Place marker image of scripted tool at @p pos. * Marker images will be deleted automatically. * @returns true if succesfull * * @param pos position to be marked * @ingroup tool_only * @typemask bool(coord3d) */ // see tool/simtool.scripted.cc register_function("mark_tile"); /** * Can the tool start/end on @p pos? If it is the second click, @p start is the position of the first click. * Possible return values: * 0 = no * 1 = This tool can work on this tile (with single click) * 2 = On this tile can dragging start/end * 3 = Both (1 and 2) * * @param pl player instance to use this tool. * @param pos position to test * @param start first tile clicked by user * @ingroup tool_skel * @ingroup quick_return_func * @typemask void(player_x, coord3d, coord3d) */ register_function("is_valid_pos"); #endif simutrans-124.5/src/simutrans/script/api/api_skeleton.h000066400000000000000000000033131520675460500233030ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /** @file api_skeleton.h Documents the global variables in a scenario script */ #ifdef SQAPI_DOC // document global structs struct { /// Short description of scenario, is shown for example in finance window. string short_description; /// Author(s) of the scenario and/or scripts. string author; /// Version of script. string version; /** * Script requires this version of api. * Corresponds to release numbers (e.g. 112.3). * Set it to "*" to support nightly versions. * If this string is not set, the default "112.3" will be used. * * @see get_api_version */ /// Required version of api. string api; } /** * Meta information about the scenario. */ scenario; struct { /** * Name of savegame. The scenario starts with the world saved there. * If file == "" then do not load a saved, attach * the scenario to the running world instead. * * @see get_map_file */ string file; } /** * Information about game map/world. */ map; /** * Only this table is saved and reloaded with an savegame. * Only plain data is saved: no classes / functions, no cyclic references. * * Instances of classes can be saved if * (1) the class implements the function _save that * (2) returns something which can reconstruct the instance, i.e. an constructor call, embedded in a string. * @code * * class my_class_with_save { * foo = 0 * * constructor(f) * { * foo = f; * } * function _save() * { * return "my_class_with_save("+ foo + ")" * } * } * @endcode * * @brief * Persistent data should go into this table. */ table persistent; #endif simutrans-124.5/src/simutrans/script/api/api_tiles.cc000066400000000000000000000272061520675460500227440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_tiles.cc exports tile related functions. */ #include "api_simple.h" #include "get_next.h" #include "../api_class.h" #include "../api_function.h" #include "../../tool/simmenu.h" #include "../../world/simworld.h" #include "../../ground/wasser.h" #include "../../obj/crossing.h" #include "../../simconvoi.h" #include "../../vehicle/vehicle.h" namespace script_api { declare_enum_param(grund_t::flag_values, uint8, "flags"); declare_specialized_param(depot_t*, "t|x|y", "depot_x"); }; using namespace script_api; SQInteger get_next_object(HSQUIRRELVM vm) { grund_t *gr = param::get(vm, 1); return generic_get_next(vm, gr ? gr->obj_count() : 0); } SQInteger get_object_index(HSQUIRRELVM vm) { grund_t *gr = param::get(vm, 1); uint8 index = param::get(vm, 2); obj_t *obj = NULL; if (gr && index < gr->obj_count()) { obj = gr->obj_bei(index); } return param::push(vm, obj); } SQInteger get_object_count(HSQUIRRELVM vm) { grund_t *gr = param::get(vm, 1); return param::push(vm, gr ? gr->obj_count() : 0); } call_tool_work tile_remove_object(grund_t* gr, player_t* player, obj_t::typ type) { cbuffer_t buf; buf.printf("%d", (int)type); return call_tool_work(TOOL_REMOVER | GENERAL_TOOL, (const char*)buf, 0, player, gr->get_pos()); } // return way ribis, have to implement a wrapper, to correctly rotate ribi static SQInteger get_way_ribi(HSQUIRRELVM vm) { grund_t *gr = param::get(vm, 1); waytype_t wt = param::get(vm, 2); bool masked = param::get(vm, 3); ribi_t::ribi ribi = gr ? (masked ? gr->get_weg_ribi(wt) : gr->get_weg_ribi_unmasked(wt) ) : 0; return param::push(vm, ribi); } static SQInteger get_canal_ribi(HSQUIRRELVM vm) { grund_t *gr = param::get(vm, 1); ribi_t::ribi ribi = gr && gr->is_water() ? ((wasser_t*)gr)->get_canal_ribi() : (ribi_t::ribi)0; return param::push(vm, ribi); } // we have to implement a wrapper, to correctly rotate ribi grund_t* get_neighbour(grund_t *gr, waytype_t wt, my_ribi_t ribi) { grund_t *to = NULL; if (gr && ribi_t::is_single(ribi)) { gr->get_neighbour(to, wt, ribi); } return to; } my_slope_t get_slope(grund_t *gr) { return gr->get_grund_hang(); } bool is_crossing(grund_t *gr) { return gr->find(); } halthandle_t get_first_halt_on_square(planquadrat_t* plan) { return plan->get_halt(NULL); } vector_tpl const& square_get_halt_list(planquadrat_t *plan) { static vector_tpl list; list.clear(); if (plan) { const halthandle_t* haltlist = plan->get_haltlist(); for(uint8 i=0, end = plan->get_haltlist_count(); i < end; i++) { list.append(haltlist[i]); } } return list; } vector_tpl const get_convoy_list(grund_t* gr) { static vector_tpl list; list.clear(); for( uint8 n = 0; nobj_count(); n++) { obj_t* obj = gr->obj_bei( n ); if( vehicle_t* veh = dynamic_cast(obj) ) { convoihandle_t cnv; if( veh->get_convoi() ) { list.append_unique( veh->get_convoi()->self ); } } } return list; } void export_tiles(HSQUIRRELVM vm) { /** * Class to access tiles on the map. * * There is the possibility to iterate through all objects on the tile: * @code * local tile = tile_x( ... ) * foreach(thing in tile) { * ... // thing is an instance of the map_object_x (or a derived) class * } * @endcode */ begin_class(vm, "tile_x", "coord3d,extend_get,ingame_object"); /** * Constructor. Returns tile at particular 3d coordinate. * If not tile is found, it returns the ground tile. * Raises error, if (@p x, @p y) coordinates are out-of-range. * @param x x-coordinate * @param y y-coordinate * @param z z-coordinate * @typemask (integer,integer,integer) */ // actually defined in simutrans/script/script_base.nut // register_function(..., "constructor", ...); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") /** * Search for a given object type on the tile. * @return some instance or null if not found */ register_method(vm, &grund_t::suche_obj, "find_object"); /** * Remove object of given type from the tile. * Type @p type must be one of the following: ::mo_label, ::mo_pedestrian, ::mo_city_car, ::mo_powerline, ::mo_transformer_s, * ::mo_transformer_c, ::mo_signal, ::mo_roadsign, ::mo_wayobj, ::mo_tunnel, ::mo_bridge, ::mo_field, ::mo_building, ::mo_depot_rail. * Setting @p type to ::mo_depot_rail deletes depots of every type. * @param pl player that pays for removal * @param type object type * @returns null upon success, an error message otherwise * @warning Does not work with all object types. * @ingroup game_cmd */ register_method(vm, &tile_remove_object, "remove_object", true); /** * Access halt at this tile. * @returns halt_x instance or null/false if no halt is present */ register_method(vm, &grund_t::get_halt, "get_halt"); /** * Queries tile type. * @returns true if tile is an ocean tile */ register_method(vm, &grund_t::is_water, "is_water"); /** * Queries tile type. * @returns true if tile is an bridge tile (including bridge starts) */ register_method(vm, &grund_t::ist_bruecke, "is_bridge"); /** * Queries tile type. * @returns true if tile is an tunnel tile (including tunnel mouths) */ register_method(vm, &grund_t::ist_tunnel, "is_tunnel"); /** * Queries tile type. * @returns true if tile is empty (no ways, buildings, powerlines, halts, not water tile) */ register_method(vm, &grund_t::ist_natur, "is_empty"); /** * Queries tile type. * @returns true if tile on ground (not bridge/elevated, not tunnel) */ register_method(vm, &grund_t::ist_karten_boden, "is_ground"); /** * Checks for crossing object. * @returns true if there is a crossing on the tile. */ register_method(vm, &is_crossing, "is_crossing", true); /** * Returns encoded slope of tile, zero means flat tile. * @returns slope */ register_method(vm, &get_slope, "get_slope", true); /** * Returns text of a sign on this tile (station sign, city name, label). * @returns text */ register_method(vm, &grund_t::get_text, "get_text"); /** * Queries ways on the tile. * @param wt waytype * @returns true if there is a way with the given waytype on the tile. */ register_method(vm, &grund_t::hat_weg, "has_way"); /** * Queries ways on the tile. * @returns true if there is at least one way on the tile */ register_method(vm, &grund_t::hat_wege, "has_ways"); /** * Queries ways on the tile. * @returns true if there is are two ways on the tile */ register_method(vm, &grund_t::has_two_ways, "has_two_ways"); /** * Returns way_x object on this tile of way type @p wt if present * @param wt waytype * @returns way object or null */ register_method(vm, &grund_t::get_weg, "get_way"); /** * Return directions in which ways on this tile go. One-way signs are ignored here. * @param wt waytype * @returns direction * @typemask dir(waytypes) */ register_function_fv(vm, &get_way_ribi, "get_way_dirs", 2, "xi", freevariable(false) ); /** * Return directions in which ways on this tile go. Some signs restrict available directions. * @param wt waytype * @returns direction * @typemask dir(waytypes) */ register_function_fv(vm, &get_way_ribi, "get_way_dirs_masked", 2, "xi", freevariable(true) ); /** * Return directions in which canals branch off from water tiles. * Used for jps pathfinding on water tiles. * @returns direction * @typemask dir() */ register_function(vm, &get_canal_ribi, "get_canal_ribi", 1, "x"); /** * Returns neighbour if one follows way in the given direction. * @param wt waytype, if equal to @ref wt_all then ways are ignored. * @param d direction * @return neighbour tile or null */ register_method(vm, &get_neighbour, "get_neighbour", true); /** * Returns depot_x object on this tile if any depot is present. * @returns depot object or null */ register_method(vm, &grund_t::get_depot, "get_depot"); /** * Checks whether player can delete all objects on the tile. * @param pl player * @return error message or null if player can delete everything */ register_method(vm, &grund_t::kann_alle_obj_entfernen, "can_remove_all_objects"); /** @name Functions to mark tiles. * Methods to mark, unmark, and check mark-status of tiles. * Mark flag can be reset by cursor movement. * @warning In network games, they only work on server. */ //@{ /// Check if tile is marked. register_method_fv(vm, &grund_t::get_flag, "is_marked", freevariable(grund_t::marked)); /// Unmark tile. register_method_fv(vm, &grund_t::clear_flag, "unmark", freevariable(grund_t::marked)); /// Mark tile. register_method_fv(vm, &grund_t::set_flag, "mark", freevariable(grund_t::marked)); //@} /** * @return convoy list by tile */ register_method(vm, &get_convoy_list, "get_convoys", true); #ifdef SQAPI_DOC // document members /** * List to iterate through all objects on this tile. * @code * t= tile_x(47,11) * foreach(obj in t.get_objects()) { * ... * } * @endcode */ tile_object_list_x get_objects(); #endif end_class(vm); /** * Class that holds an iterator through the list of objects on a particular tile. * * For an example see tile_x. */ begin_class(vm, "tile_object_list_x", "coord3d"); /** * Meta-method to be used in foreach loops to loop over all objects on the tile. Do not call it directly. */ register_function(vm, get_next_object, "_nexti", 2, "x o|i"); /** * Meta-method to be used in foreach loops to loop over all objects on the tile. Do not call it directly. */ register_function(vm, get_object_index, "_get", 2, "x i|s"); /** * Returns number of objects on the tile. * @typemask integer() */ register_function(vm, get_object_count, "get_count", 1, "x"); end_class(vm); /** * Class to map squares, which holds all the tiles on one particular coordinate. */ begin_class(vm, "square_x", "coord,extend_get,ingame_object"); /** * Constructor. Returns map square at particular 2d coordinate. * Raises error, if (@p x, @p y) coordinate are out-of-range. * @param x x-coordinate * @param y z-coordinate * @typemask (integer,integer) */ // actually defined in simutrans/script/script_base.nut // register_function(..., "constructor", ...); /** * @returns if object is still valid. */ export_is_valid(vm); //register_function("is_valid") /** * Access some halt at this square. * @deprecated Use square_x::get_player_halt or tile_x::get_halt instead! * @returns halt_x instance or null/false if no halt is present */ register_method(vm, &get_first_halt_on_square, "get_halt", true); /** * Access halt of this player at this map position. * @param pl potential owner of halt * @returns halt_x instance or null/false if no halt is present */ register_method(vm, &planquadrat_t::get_halt, "get_player_halt"); /** * Access tile at specified height. * @param z height * @returns tile_x or null/false if nothing found */ register_method(vm, &planquadrat_t::get_boden_in_hoehe, "get_tile_at_height"); /** * Access ground tile. * @returns tile_x instance */ register_method(vm, &planquadrat_t::get_kartenboden, "get_ground_tile"); /** * Returns list of stations that cover this tile. * @typemask array */ register_method(vm, &square_get_halt_list, "get_halt_list", true); /** * Returns climate of ground tile. */ register_method(vm, &planquadrat_t::get_climate, "get_climate"); end_class(vm); } simutrans-124.5/src/simutrans/script/api/api_world.cc000066400000000000000000000431671520675460500227570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api.h" /** @file api_world.cc exports world-map functions. */ #include "api_obj_desc_base.h" #include "api_simple.h" #include "get_next.h" #include "../api_class.h" #include "../api_function.h" #include "../../world/simworld.h" #include "../../simversion.h" #include "../../player/simplay.h" #include "../../obj/gebaeude.h" #include "../../obj/label.h" #include "../../descriptor/ground_desc.h" #include "../../simware.h" #include "../../builder/goods_manager.h" #include "../../simhalt.h" #include "../../simfab.h" using namespace script_api; mytime_ticks_t world_get_time(karte_t*) { return mytime_ticks_t(welt->get_current_month(), welt->get_ticks(), welt->scale_with_month_length(1<<18), welt->get_next_month_ticks()); } vector_tpl const& get_world_stat(karte_t* welt, bool monthly, sint32 INDEX) { static vector_tpl v; v.clear(); if (0<=INDEX && INDEXget_finance_history_month(i, INDEX) ); } else { v.append( welt->get_finance_history_year(i, INDEX) ); } } } return v; } bool world_remove_player(karte_t *welt, player_t *player) { if (player == NULL) { return false; } // first test bool ok = welt->change_player_tool(karte_t::delete_player, player->get_player_nr(), 0, true /*unlocked*/, false /*exec*/); if (!ok) { return false; } // now call - will not have immediate effect in network games welt->call_change_player_tool(karte_t::delete_player, player->get_player_nr(), 0, true /*scripted*/); return true; } uint32 world_generate_goods(karte_t *welt, koord from, koord to, const goods_desc_t *desc, uint32 count) { if (count == 0 || count >= 1<<23) { return 0; } planquadrat_t *fromplan = welt->access(from); planquadrat_t *toplan = welt->access(to); if (!fromplan || !toplan) { return haltestelle_t::NO_ROUTE; } else if (fromplan->get_haltlist_count() == 0) { // not reachable return haltestelle_t::NO_ROUTE; } ware_t good(desc); good.set_target_pos(to); good.amount = count; good.to_factory = fabrik_t::get_fab(to) != NULL; ware_t return_good(desc); const halthandle_t *haltlist = fromplan->get_haltlist(); const uint8 hl_count = fromplan->get_haltlist_count(); const bool no_route_over_overcrowded = welt->get_settings().is_no_routing_over_overcrowding(); assert(hl_count > 0); const int route_result = haltestelle_t::search_route(haltlist, hl_count, no_route_over_overcrowded, good, &return_good); switch (route_result) { case haltestelle_t::ROUTE_WALK: if (good.get_desc() == goods_manager_t::passengers) { haltlist[0]->add_pax_walked(count); } break; case haltestelle_t::ROUTE_OVERCROWDED: if (good.get_desc() == goods_manager_t::passengers) { haltlist[0]->add_pax_unhappy(count); } break; case haltestelle_t::NO_ROUTE: if (good.get_desc() == goods_manager_t::passengers) { haltlist[0]->add_pax_no_route(count); } break; case haltestelle_t::ROUTE_OK: { assert(route_result == haltestelle_t::ROUTE_OK); const halthandle_t start_halt = return_good.get_target_halt(); start_halt->starte_mit_route(good); if (good.get_desc() == goods_manager_t::passengers) { start_halt->add_pax_happy(good.amount); } else { start_halt->recalc_status(); } } break; } return route_result; } // returns index of attraction base tile with index > start uint32 get_next_attraction_base(uint32 start) { const weighted_vector_tpl& attractions = welt->get_attractions(); for(uint32 i = start+1; i < attractions.get_count(); i++) { gebaeude_t *gb = attractions[i]; if (gb != NULL && gb->get_first_tile() == gb) { return i; } } return attractions.get_count(); } SQInteger world_attraction_list_next(HSQUIRRELVM vm) { return generic_get_next_f(vm, welt->get_attractions().get_count(), get_next_attraction_base); } namespace script_api { declare_fake_param(attraction_list_t, "attraction_list_x"); declare_fake_param(label_list_t, "label_list_x"); } gebaeude_t* world_attraction_list_get(attraction_list_t, uint32 index) { const weighted_vector_tpl& attractions = welt->get_attractions(); return (index < attractions.get_count()) ? attractions[index] : NULL; } SQInteger world_get_attraction_list(HSQUIRRELVM vm) { return push_instance(vm, "attraction_list_x"); } SQInteger world_get_attraction_count(HSQUIRRELVM vm) { return param::push(vm, welt->get_attractions().get_count()); } SQInteger world_get_label_list(HSQUIRRELVM vm) { return push_instance(vm, "label_list_x"); } uint32 world_get_label_count(label_list_t) { return welt->get_label_list().get_count(); } SQInteger world_label_list_next(HSQUIRRELVM vm) { return generic_get_next(vm, welt->get_label_list().get_count()); } label_t* world_label_list_get(label_list_t, uint32 index) { auto list = welt->get_label_list(); if (index < list.get_count()) { koord k = list[index]; if (grund_t *gr = welt->lookup_kartenboden(k)) { return gr->find(); } } return NULL; } SQInteger world_get_convoy_list(HSQUIRRELVM vm) { push_instance(vm, "convoy_list_x"); set_slot(vm, "use_world", true); return 1; } SQInteger world_get_size(HSQUIRRELVM vm) { koord k = welt->get_size(); if (coordinate_transform_t::get_rotation() & 1) { return push_instance(vm, "coord", k.y, k.x); } else { return push_instance(vm, "coord", k.x, k.y); } } const char* get_pakset_name() { return ground_desc_t::outside->get_copyright(); } const char* get_version_number() { return VERSION_NUMBER; } void export_world(HSQUIRRELVM vm, bool scenario) { /** * Table with methods to access the world, the universe, and everything. */ begin_class(vm, "world", "extend_get"); /** * Checks whether given coordinate is valid. * @param k coordinate * @returns true if coordinate is valid */ STATIC register_method< bool(karte_t::*)(koord) const>(vm, &karte_t::is_within_limits, "is_coord_valid"); /** * Searches city next to the given coordinate. * @param k coordinate * @returns city if there is any */ STATIC register_method(vm, &karte_t::find_nearest_city, "find_nearest_city"); /** * Current season. * @returns season (0=winter, 1=spring, 2=summer, 3=autumn) */ STATIC register_method(vm, &karte_t::get_season, "get_season"); if (scenario) { /** * Removes player company: removes all assets. Use with care. * * If pl is the first player (nr == 0) it is restarted immediately. * Public player (nr == 1) cannot be removed. * * In network games, there will be a delay between the call to this function and the removal of the player. * * @param pl player to be removed * @ingroup scen_only * @returns whether operation was successful */ STATIC register_method(vm, &world_remove_player, "remove_player", true); /** * Generates goods (passengers, mail or freight) that want to travel from @p from to @p to. * Updates halt statistics (happy, unhappy, no route) for passengers if possible. * * @param from start position for good * @param to destination position for good * @param desc Good descriptor * @param count Number of goods to generate * @retval 0 No route to destination or start position not valid * @retval 1 Passengers/mail/freight successfully generated * @retval 2 Destination is within station catchment area * @retval 8 Route is overcrowded (if no_routing_over_overcrowded is enabled) * @ingroup scen_only */ STATIC register_method(vm, &world_generate_goods, "generate_goods", true); } /** * Returns player number @p pl. If player does not exist, returns null. * @param pl player number */ STATIC register_method(vm, &karte_t::get_player, "get_player", true); /** * @returns current in-game time. */ STATIC register_local_method(vm, world_get_time, "get_time"); /** * Get monthly statistics of total number of citizens. * @returns array, index [0] corresponds to current month */ STATIC register_method_fv(vm, &get_world_stat, "get_citizens", freevariable(true, karte_t::WORLD_CITIZENS), true ); /** * Get monthly statistics of total city growth. * @returns array, index [0] corresponds to current month */ STATIC register_method_fv(vm, &get_world_stat, "get_growth", freevariable(true, karte_t::WORLD_GROWTH), true ); /** * Get monthly statistics of total number of towns. * @returns array, index [0] corresponds to current month */ STATIC register_method_fv(vm, &get_world_stat, "get_towns", freevariable(true, karte_t::WORLD_TOWNS), true ); /** * Get monthly statistics of total number of factories. * @returns array, index [0] corresponds to current month */ STATIC register_method_fv(vm, &get_world_stat, "get_factories", freevariable(true, karte_t::WORLD_FACTORIES), true ); /** * Get monthly statistics of total number of convoys. * @returns array, index [0] corresponds to current month */ STATIC register_method_fv(vm, &get_world_stat, "get_convoys", freevariable(true, karte_t::WORLD_CONVOIS), true ); /** * Get monthly statistics of total number of citycars. * @returns array, index [0] corresponds to current month */ STATIC register_method_fv(vm, &get_world_stat, "get_citycars", freevariable(true, karte_t::WORLD_CITYCARS), true ); /** * Get monthly statistics of ratio transported to generated passengers. * @returns array, index [0] corresponds to current month */ STATIC register_method_fv(vm, &get_world_stat, "get_ratio_pax", freevariable(true, karte_t::WORLD_PAS_RATIO), true ); /** * Get monthly statistics of total number of generated passengers. * @returns array, index [0] corresponds to current month * @see city_x::get_generated_pax city_x::get_transported_pax */ STATIC register_method_fv(vm, &get_world_stat, "get_generated_pax", freevariable(true, karte_t::WORLD_PAS_GENERATED), true ); /** * Get monthly statistics of ratio transported to generated mail. * @returns array, index [0] corresponds to current month */ STATIC register_method_fv(vm, &get_world_stat, "get_ratio_mail", freevariable(true, karte_t::WORLD_MAIL_RATIO), true ); /** * Get monthly statistics of total number of generated mail. * @returns array, index [0] corresponds to current month * @see city_x::get_generated_mail city_x::get_transported_mail */ STATIC register_method_fv(vm, &get_world_stat, "get_generated_mail", freevariable(true, karte_t::WORLD_MAIL_GENERATED), true ); /** * Get monthly statistics of ratio of factories that got supplied. * @returns array, index [0] corresponds to current month * @see city_x::get_generated_mail city_x::get_transported_mail */ STATIC register_method_fv(vm, &get_world_stat, "get_ratio_goods", freevariable(true, karte_t::WORLD_GOODS_RATIO), true ); /** * Get monthly statistics of total number of transported goods. * @returns array, index [0] corresponds to current month * @see city_x::get_generated_mail city_x::get_transported_mail */ STATIC register_method_fv(vm, &get_world_stat, "get_transported_goods", freevariable(true, karte_t::WORLD_TRANSPORTED_GOODS), true ); /** * Get per year statistics of total number of citizens. * @returns array, index [0] corresponds to current year */ STATIC register_method_fv(vm, &get_world_stat, "get_year_citizens", freevariable(false, karte_t::WORLD_CITIZENS), true ); /** * Get per year statistics of total city growth. * @returns array, index [0] corresponds to current year */ STATIC register_method_fv(vm, &get_world_stat, "get_year_growth", freevariable(false, karte_t::WORLD_GROWTH), true ); /** * Get per year statistics of total number of towns. * @returns array, index [0] corresponds to current year */ STATIC register_method_fv(vm, &get_world_stat, "get_year_towns", freevariable(false, karte_t::WORLD_TOWNS), true ); /** * Get per year statistics of total number of factories. * @returns array, index [0] corresponds to current year */ STATIC register_method_fv(vm, &get_world_stat, "get_year_factories", freevariable(false, karte_t::WORLD_FACTORIES), true ); /** * Get per year statistics of total number of convoys. * @returns array, index [0] corresponds to current year */ STATIC register_method_fv(vm, &get_world_stat, "get_year_convoys", freevariable(false, karte_t::WORLD_CONVOIS), true ); /** * Get per year statistics of total number of citycars. * @returns array, index [0] corresponds to current year */ STATIC register_method_fv(vm, &get_world_stat, "get_year_citycars", freevariable(false, karte_t::WORLD_CITYCARS), true ); /** * Get per year statistics of ratio transported to generated passengers. * @returns array, index [0] corresponds to current year */ STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_pax", freevariable(false, karte_t::WORLD_PAS_RATIO), true ); /** * Get per year statistics of total number of generated passengers. * @returns array, index [0] corresponds to current year * @see city_x::get_generated_pax city_x::get_transported_pax */ STATIC register_method_fv(vm, &get_world_stat, "get_year_generated_pax", freevariable(false, karte_t::WORLD_PAS_GENERATED), true ); /** * Get per year statistics of ratio transported to generated mail. * @returns array, index [0] corresponds to current year */ STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_mail", freevariable(false, karte_t::WORLD_MAIL_RATIO), true ); /** * Get per year statistics of total number of generated mail. * @returns array, index [0] corresponds to current year * @see city_x::get_generated_mail city_x::get_transported_mail */ STATIC register_method_fv(vm, &get_world_stat, "get_year_generated_mail", freevariable(false, karte_t::WORLD_MAIL_GENERATED), true ); /** * Get per year statistics of ratio of factories that got supplied. * @returns array, index [0] corresponds to current year * @see city_x::get_generated_mail city_x::get_transported_mail */ STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_goods", freevariable(false, karte_t::WORLD_GOODS_RATIO), true ); /** * Get per year statistics of total number of transported goods. * @returns array, index [0] corresponds to current year * @see city_x::get_generated_mail city_x::get_transported_mail */ STATIC register_method_fv(vm, &get_world_stat, "get_year_transported_goods", freevariable(false, karte_t::WORLD_TRANSPORTED_GOODS), true ); /** * @returns true if timeline play is active */ STATIC register_method(vm, &karte_t::use_timeline, "use_timeline"); /** * Returns iterator through the list of attractions on the map. * @returns iterator class. * @typemask attraction_list_x() */ STATIC register_function(vm, world_get_attraction_list, "get_attraction_list", 1, "."); /** * Returns iterator through the list of labels on the map. * @returns iterator class. * @typemask label_list_x() */ STATIC register_function(vm, world_get_label_list, "get_label_list", 1, "."); /** * Returns list of convoys on the map. * @returns convoy list * @typemask convoy_list_x() */ STATIC register_function(vm, world_get_convoy_list, "get_convoy_list", 1, "."); /** * Returns size of the map. * @typemask coord() */ STATIC register_function(vm, world_get_size, "get_size", 1, "."); end_class(vm); /** * Implements iterator to iterate through the list of all attractions on the map. * * Usage: * @code * local list = world.get_attraction_list() * // list is now of type attraction_list_x * foreach(att in list) { * ... // att is an instance of the building_x class * } * @endcode */ create_class(vm, "attraction_list_x"); /** * Meta-method to be used in foreach loops to loop over all attractions on the map. Do not call it directly. */ register_function(vm, world_attraction_list_next, "_nexti", 2, ". o|i"); /** * Meta-method to be used in foreach loops to loop over all attractions on the map. Do not call it directly. */ register_method(vm, world_attraction_list_get, "_get", true); /** * Returns number of attractions in the list. * @typemask integer() */ register_function(vm, world_get_attraction_count, "get_count", 1, "x"); end_class(vm); /** * Implements iterator to iterate through the list of all labels on the map. * * Usage: * @code * local list = world.get_label_list() * // list is now of type label_list_x * foreach(label in list) { * ... // label is an instance of the label_x class * } * @endcode */ create_class(vm, "label_list_x"); /** * Meta-method to be used in foreach loops to loop over all labels on the map. Do not call it directly. */ register_function(vm, world_label_list_next, "_nexti", 2, ". o|i"); /** * Meta-method to be used in foreach loops to loop over all labels on the map. Do not call it directly. */ register_method(vm, world_label_list_get, "_get", true); /** * Returns number of labels in the list. * @typemask integer() */ register_method(vm, world_get_label_count, "get_count", true); end_class(vm); /** * Returns pakset name as set in ground.outside.pak */ register_method(vm, get_pakset_name, "get_pakset_name"); /** * Returns simutrans version number */ register_method(vm, get_version_number, "get_version_number"); } simutrans-124.5/src/simutrans/script/api/changelog.h000066400000000000000000000173201520675460500225600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /** @file changelog.h Documenting the timeline of api evolution */ /** @page changelog Changelog * * @section api-trunk Current trunk * * - Added @ref command_x::grid_lower, @ref command_x::grid_raise * - Added @ref settings::has_double_slopes, @ref settings::get_way_height_clearance * - Added @ref tile_x::is_crossing * - Added @ref world_x::generate_goods to generate passengers, mail or freight (scenario only) * - Added @ref change_climate_at * - Added @ref convoy_x::change_schedule * - Added tool_ids::tool_build_groundobj * - Changed building_desc_x::get_available_stations to accept wt_all * - Added @ref bridge_x, @ref tunnel_x * - Added @ref factory_x::get_fields_list, @ref world::get_label_list * - Added @ref schedule_x::current. * * @section api-123 Release 123.0 * * - Changed scripted tools: work, do_work, mark_tiles have additional parameter to send state of ctrl/shift keys * - Added @ref factory_desc_x * - Added @ref way_x::get_max_speed * - Added @ref wayobj_desc_x, @ref wayobj_x, @ref command_x::build_wayobj to work with way-objects * - Added @ref factory_x::get_raw_name * - Added @ref tree_desc_x::get_price, @ref command_x::slope_get_price * - Added @ref way_x::get_transported_goods, @ref way_x::get_convoys_passed * - Added @ref tile_x::get_way, @ref tile_x::get_depot * - Added @ref get_pakset_name, @ref player_x::get_type * * @section api-122 Release 122.0 * * - Feature: scripted tools * - Added @ref debug::pause, @ref debug::set_pause_on_error * - Added more tool ids * - Added @ref convoy_x::is_waiting, @ref convoy_x::is_loading * - Added @ref rules::gui_needs_update * - Added @ref convoy_x::get_tile_length * - Added @ref settings::get_pay_for_total_distance_mode, @ref settings::get_drive_on_left * - Added @ref halt_x::get_connections * - Added @ref command_x::can_set_slope * - Added @ref halt_x::get_halt * - Changed possible direction parameter of @ref command_x::build_station (useful for flat docks) * - Added @ref depot_x::get_depot_list * - Added @ref vehicle_desc_x::needs_electrification * - Added @ref command_x::build_road * - Added fields: @ref field_x * - Added powerlines: @ref powerline_x, @ref transformer_x, @ref factory_x::get_transformer, @ref factory_x::is_transformer_connected * * @section api-121 Release 121.0 * * - Added functions to mark tiles, see @ref tile_x::mark * - Added @ref convoy_x::is_schedule_editor_open * * @section api-120-3 Release 120.3 * * - Changed enable scenario scripts to open info window also on clients, see @ref gui::open_info_win_client * - Added sign_x::can_pass * - Added methods is_valid to check whether in-game object is still present, see @ref ingame_object * - Added tile_x::can_remove_all_objects * - Added @ref coord_to_dir, @ref coord.to_dir, @ref dir.to_coord * - Added @ref simple_heap_x, @ref way_planner_x, @ref bridge_planner_x * - Added map_object_x::is_marked * - Added gui::open_info_win_at * - Added ::is_convoy_allowed * - Added convoy_x::is_in_depot * - Added scriptable hyperlinks in scenario info window * - Added @ref slope * - Added functions to build stuff: command_x::build_way, command_x::build_depot, command_x::build_station, command_x::build_bridge, command_x::build_bridge_at, command_x::set_slope, command_x::restore_slope * - Added sign_x, sign_desc_x, command_x::build_sign_at * - Added line_x::destroy * * @section api-120-2-2 Release 120.2.2 * * - Added map_object_x::mark and unmark * * @section api-120-2 Release 120.2 * * - Feature: scripted AI players * - Feature: script calls can be suspended and waken up, less strict checks of 'script take too long' * - Feature: add commands to modify game state, see @ref game_cmd * - Added @ref climates, square_x::get_climate, world::get_size * - Added new classes command_x, vehicle_desc_x, tunnel_desc_x, bridge_desc_x * - Added tool_ids::tool_set_climate, tool_ids::tool_change_water_height * - Added string::toalnum (converts strings to strings that can be used as table keys) * - Changed gui::add_message to take additional player_x parameter * * @section api-120-1-2 Release 120.1.2 * * - Added label_x::get_text, tile_x::get_text * - Added factory_x::get_halt_list, halt_x::get_tile_list, halt_x::get_factory_list, square_x::get_halt_list * - Added world.get_player * - Removed player_x::is_active * * @section api-120-1 Release 120.1 * * - Added depot_x, depot_x::get_convoy_list * - Added line_x::get_waytype, tile_x::get_slope * - Added factory_production_x::get_base_production, factory_production_x::get_base_consumption, factory_x::get_tile_list * - Added coord::href, coord3d::href * - Added label_x, tile_x::remove_object * - Added ::get_debug_text, debug::get_forbidden_text * - Added operators and conversion to string to ::coord and ::coord3d classes * - Added ::coord_to_string, ::coord3d_to_string * - Added settings::get_station_coverage, settings::get_passenger_factor * - Added settings::get_factory_worker_radius, settings::get_factory_worker_minimum_towns, settings::get_factory_worker_maximum_towns * - Added settings::avoid_overcrowding, settings::no_routing_over_overcrowding, settings::separate_halt_capacities * * @section api-120-0-1 Release 120.0.1 * * - Added building_desc_x::get_building_list, building_desc_x::get_headquarter_level * - Added factory_production_x::get_in_transit * * @section api-120-0 Release 120.0 * * - Added line_x, line_list_x * - Deprecated get_convoy_list() * - Added world::get_convoy_list, halt_x::get_convoy_list * - Added @ref scenario.api to manage backward compatibility of scripts with the changing api * - Added ::include * - Changed return type of world::get_time to time_ticks_x * - Added building_x::is_same_building. * - Added attraction_list_x, world::get_attraction_list * - Added ::new_month, ::new_year * - Added halt_list_x * - Added possibility to attach scenario to an already running game, set map.file to "". * - Added building_desc_x::get_type, convoy_x::get_distance_traveled_total, obj_desc_time_x::is_available * - Added obj_desc_time_x, obj_desc_transport_x, building_desc_x, tree_desc_x, way_desc_x * - Added time_x * - Added @ref way_system_types * - Added square_x::get_player_halt * - Added way_x::get_dirs,way_x::get_dirs_masked * - Deprecated square_x::get_halt, use square_x::get_player_halt or tile_x::get_halt instead! * * @section api-112-3 Release 112.3 * * - Added good_desc_list_x * - Added tile_x::is_bridge, tile_x::is_empty, tile_x::is_ground, tile_x::is_tunnel, tile_x::is_water * - Added tile_x::has_way, tile_x::has_ways, tile_x::has_two_ways * - Added map_objects, map_object_x, building_x, tree_x, way_x * - Added iterator tile_x::objects to loop over all objects on the tile * - Added ::dir * - Added tile_x::get_way_dirs, tile_x::get_way_dirs_masked, tile_x::get_neighbour * * @section api-112-2 Release 112.2 * * - Added rules::forbid_way_tool_cube, rules::allow_way_tool_cube * - Added settings::get_start_time * - Added player_x::book_cash * - Added settings::get_traffic_level, settings::set_traffic_level, tool_set_traffic_level * - Added gui::add_message, gui::add_message_at * - Added hyperlinks to scenario window tabs and map positions are accepted in scenario info window * - Added ::double_to_string, ::integer_to_string, ::money_to_string, ::get_month_name * - Added factory_list_x * - Added factory_x::get_name * - Added halt_x::get_name, halt_x::get_owner, halt_x::accepts_good * - Added player_x::is_active, world::remove_player * - Added schedule_x, schedule_entry_x, ::is_schedule_allowed * - Added halt_x::is_connected * * @section api-112-1 Releases 112.0 and 112.1 * * Initial release and minor changes. * */ simutrans-124.5/src/simutrans/script/api/export.awk000066400000000000000000000110661520675460500225060ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # Implements filter to automatically generate documentation of # the interface to squirrel. BEGIN { within_doxygen_comment = 0 within_class = "" param_count = 0 returns = "void" indent = "" within_apidoc = 0 mask = "" ai_only = 0 } # match beginning of SQAPI_DOC block /^#ifdef.*SQAPI_DOC/ { within_sqapi_doc = 1 } # match end of SQAPI_DOC block /^#(endif|else)/ { if (within_sqapi_doc == 1) { within_sqapi_doc = 0 param_count = 0 delete params delete ptypes mask = "" returns = "void" } } # ignore preprocessor directives /^#/ { next } /ingroup.*ai_only/ { ai_only = 1 } function split_params(string) { count = split(string, types, / *, */) for(i=1; i<=count;i++) { ptypes[i] = types[i]; } } /@typemask/ { match($0, "@typemask *(.*)\\((.*)\\)", data) returns = data[1] split_params(data[2]) next } # match beginning of doxgen comment block /\/\*\*/ { within_doxygen_comment = 1 } # print everything in a doxygen comment block # and everything in a SQAPI_DOC block { if (within_doxygen_comment==1) { print gensub( /^[[:space:]]*([ \\/])\\*/, indent "\\1", 1) } else if (within_sqapi_doc == 1) { print gensub( /^[[:space:]]*(.*)/, indent "\\1", 1) } } # match end of doxygen block /\*\// { within_doxygen_comment = 0 } # print doxygen brief commands, also //@ /\/\/[\/@]/ { if (within_doxygen_comment!=1 && within_sqapi_doc != 1) { print gensub( /^[[:space:]]*(\\.*)/, indent "\\1", 1) } } # beginning of class definition # begin_class("factory_x", "extend_get"); /(begin|create)_.*class/ { # ignore class name inside error messages as in 'dbg->error("create_class", "failed ..")' if ( /dbg->(message|warning|error|fatal)/ ) { next } # class with parent class if ( /_class[^"]*"([^"]*)"[^"]*"([^"]*)".*/ ) { match($0, /_class[^"]*"([^"]*)"[^"]*"([^"]*)".*/, data) if (mode == "sq") print "class " data[1] " extends " data[2] " {" else { print "class " data[1] " : public " data[2] " {" print "public:" } } # class without parent class else if ( /_class[^"]*"([^"]*)"/ ) { match($0, /_class[^"]*"([^"]*)"/, data) if (mode == "sq") print "class " data[1] " {" else { print "class " data[1] " {" print "public:" } } within_class = data[1] indent = "\t" param_count = 0 delete params delete ptypes } /end_.*class/ { if (mode == "sq") print "}" else print "};" within_class = "" indent = "" } # beginning of enum constant definition /begin_enum/ { match($0, /^[[:space:]]*begin_enum[^"]*"([^"]*)"/, data) if (mode == "sq") { #print "class " data[1] " {" } else { print "enum " data[1] " {" } indent = "\t" } # end of enum /end_enum/ { if (mode == "sq") { #print "class " data[1] " {" } else { print "};" } indent = "" } # @param names /@param/ { if (within_doxygen_comment==1) { param_count++ params[param_count] = $3 } } # now the actual methods # first string enclosed by ".." is method name /register_function/ || /register_method/ || /register_local_method/{ match($0, /"([^"]*)"/, data) method = data[1] if (ai_only == 1) { suffix = "ai" } else { suffix = "scenario" } # check for param types if (ai_only && ((within_class "::" method) in export_types_ai)) { mask = export_types_ai[(within_class "::" method)] } else if ((within_class "::" method) in export_types_scenario) { mask = export_types_scenario[(within_class "::" method)] } if (mask != "") { match(mask, " *(.*)\\((.*)\\)", data) returns = data[1] split_params(data[2]) for (t in ptypes) { if (!(t in params)) { params[t]="" param_count++ } } } # build the output fname = "" if ( /STATIC/ ) { fname = fname "static " } if (mode == "sq") { if (method != "constructor") { fname = fname "function " } fname = fname method "(" } else { if (method ~ /^_/) { print "private:" } if (method == "constructor") { fname = fname within_class "(" } else { fname = fname returns " " method "(" } } for (param = 1; param <= param_count; param++) { if (mode != "sq") { if (!(param in ptypes)) ptypes[param] = "any_x" fname = fname ptypes[param] } if (params[param] != "") { fname = fname " " params[param] } if (param < param_count) fname = fname ", " } fname = fname ");" print indent fname if ( (mode == "cpp") && (method ~ /^_/)) { print "public:" } param_count = 0 delete params delete ptypes mask = "" returns = "void" ai_only = 0 } # enum constants /enum_slot/ { match($0, /"([^"]*)"/, data) name = data[1] print indent name "," } simutrans-124.5/src/simutrans/script/api/export_desc.cc000066400000000000000000000023551520675460500233100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "export_desc.h" #include "../script.h" #include "../api_class.h" #include "../api_function.h" #include "../../tpl/vector_tpl.h" #include "../../utils/plainstring.h" using namespace script_api; static vector_tpl registered_desc_functions; SQInteger get_desc_pointer(HSQUIRRELVM vm) { void *tag = NULL; // get type tag of class sq_gettypetag(vm, 1, &tag); plainstring name = param::get(vm, 2); if (tag && registered_desc_functions.is_contained((GETDESCFUNC)tag) ) { // call method const void *desc = ((GETDESCFUNC)tag)(name.c_str()); // set userpointer of class instance sq_setinstanceup(vm, 1, const_cast(desc) ); return 0; } return -1; } void begin_desc_class(HSQUIRRELVM vm, const char* name, const char* base, GETDESCFUNC func) { SQInteger res = create_class(vm, name, base); assert( SQ_SUCCEEDED(res) ); (void)res; // store method to retrieve desc in typetag pointer sq_settypetag(vm, -1, (void*)func); registered_desc_functions.append_unique( func ); // register constructor register_function(vm, get_desc_pointer, "constructor", 2, "xs"); // now functions can be registered } simutrans-124.5/src/simutrans/script/api/export_desc.h000066400000000000000000000014421520675460500231460ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_EXPORT_DESC_H #define SCRIPT_API_EXPORT_DESC_H #include "../../../squirrel/squirrel.h" /** @file export_desc.h Helper functions to export desc-classes */ /** * Creates class @p name in top of the stack. * * Defines constructor. Call has to be complemented by call to end_class. * @param name name of new class * @param parent name of base class (or NULL) * @param func function pointer to a function that retrieves pointer-to-desc by name */ void begin_desc_class(HSQUIRRELVM vm, const char* name, const char* parent, const void* (*func)(const char*)); /// Function signature to retrieve desc-pointer from name typedef const void* (*GETDESCFUNC)(const char*); #endif simutrans-124.5/src/simutrans/script/api/filter_cpp.sh000077500000000000000000000003521520675460500231430ustar00rootroot00000000000000#!/bin/bash # # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # Filter for Doxygen input files gawk -f squirrel_types_scenario.awk -f squirrel_types_ai.awk -f export.awk -v mode=cpp $1 simutrans-124.5/src/simutrans/script/api/filter_sq.sh000077500000000000000000000003621520675460500230050ustar00rootroot00000000000000#!/bin/bash # # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # Filter for Doxygen source code browsing gawk -f squirrel_types_scenario.awk -f squirrel_types_ai.awk -f export.awk -v mode=sq $1 simutrans-124.5/src/simutrans/script/api/get_next.cc000066400000000000000000000013341520675460500226020ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "get_next.h" SQInteger generic_get_next_f(HSQUIRRELVM vm, uint32 count, uint32 F(uint32) ) { SQInteger index; if (SQ_SUCCEEDED(sq_getinteger(vm, -1, &index))) { // now increase index if ( index >= 0 ) { // call the custom function uint32 new_index = F(index); if (new_index < count) { sq_pushinteger(vm, new_index); return 1; } } } else { if ( count>0 ) { sq_pushinteger(vm, 0); return 1; } } sq_pushnull(vm); return 1; } static uint32 inc(uint32 i) { return i+1; } SQInteger generic_get_next(HSQUIRRELVM vm, uint32 count) { return generic_get_next_f(vm, count, inc); } simutrans-124.5/src/simutrans/script/api/get_next.h000066400000000000000000000014501520675460500224430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_GET_NEXT_H #define SCRIPT_API_GET_NEXT_H #include "../../simtypes.h" #include "../../../squirrel/squirrel.h" /** * Implements custom function to realize foreach-iterators. * Can be used to export _nexti method. * Takes argument of _nexti from top of the stack. * Pushes null or the new index to the stack. * @pre assumed to be bound to _nexti meta method. * @param count total number of elements in iterated list */ SQInteger generic_get_next(HSQUIRRELVM vm, uint32 count); /** * Same as generic_get_next. * @param F custom function to increase index. Takes old index as parameter. */ SQInteger generic_get_next_f(HSQUIRRELVM vm, uint32 count, uint32 F(uint32) ); #endif simutrans-124.5/src/simutrans/script/api/squirrel_types_ai.awk000066400000000000000000000654321520675460500247360ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # file used to generate doxygen documentation of squirrel API # needs to be copied to trunk/script/api BEGIN { export_types_ai["city_list_x::_get"] = "city_x(integer)" export_types_ai["city_x::is_valid"] = "bool()" export_types_ai["city_x::get_name"] = "string()" export_types_ai["city_x::set_name"] = "void(string)" export_types_ai["city_x::get_citizens"] = "array()" export_types_ai["city_x::get_growth"] = "array()" export_types_ai["city_x::get_buildings"] = "array()" export_types_ai["city_x::get_citycars"] = "array()" export_types_ai["city_x::get_transported_pax"] = "array()" export_types_ai["city_x::get_generated_pax"] = "array()" export_types_ai["city_x::get_transported_mail"] = "array()" export_types_ai["city_x::get_generated_mail"] = "array()" export_types_ai["city_x::get_year_citizens"] = "array()" export_types_ai["city_x::get_year_growth"] = "array()" export_types_ai["city_x::get_year_buildings"] = "array()" export_types_ai["city_x::get_year_citycars"] = "array()" export_types_ai["city_x::get_year_transported_pax"] = "array()" export_types_ai["city_x::get_year_generated_pax"] = "array()" export_types_ai["city_x::get_year_transported_mail"] = "array()" export_types_ai["city_x::get_year_generated_mail"] = "array()" export_types_ai["city_x::get_citygrowth_enabled"] = "bool()" export_types_ai["city_x::get_pos"] = "coord()" export_types_ai["city_x::get_pos_nw"] = "coord()" export_types_ai["city_x::get_pos_se"] = "coord()" export_types_ai["city_x::change_size"] = "string(integer)" export_types_ai["city_x::set_citygrowth_enabled"] = "void(bool)" export_types_ai["::get_ops_total"] = "integer()" export_types_ai["::get_ops_remaining"] = "integer()" export_types_ai["debug::pause"] = "bool()" export_types_ai["debug::is_paused"] = "bool()" export_types_ai["debug::set_pause_on_error"] = "void(bool)" export_types_ai["convoy_x::is_valid"] = "bool()" export_types_ai["convoy_x::needs_electrification"] = "bool()" export_types_ai["convoy_x::get_name"] = "string()" export_types_ai["convoy_x::set_name"] = "void(string)" export_types_ai["convoy_x::get_pos"] = "coord3d()" export_types_ai["convoy_x::get_owner"] = "player_x()" export_types_ai["convoy_x::get_goods_catg_index"] = "array()" export_types_ai["convoy_x::get_waytype"] = "way_types()" export_types_ai["convoy_x::get_schedule"] = "schedule_x()" export_types_ai["convoy_x::get_capacity"] = "array()" export_types_ai["convoy_x::get_transported_goods"] = "array()" export_types_ai["convoy_x::get_revenue"] = "array()" export_types_ai["convoy_x::get_cost"] = "array()" export_types_ai["convoy_x::get_profit"] = "array()" export_types_ai["convoy_x::get_traveled_distance"] = "array()" export_types_ai["convoy_x::get_way_tolls"] = "array()" export_types_ai["convoy_x::get_distance_traveled_total"] = "integer()" export_types_ai["convoy_x::get_line"] = "line_x()" export_types_ai["convoy_x::set_line"] = "void(player_x, line_x)" export_types_ai["convoy_x::get_vehicles"] = "array()" export_types_ai["convoy_x::get_speed"] = "integer()" export_types_ai["convoy_x::get_loading_limit"] = "integer()" export_types_ai["convoy_x::get_loading_level"] = "integer()" export_types_ai["convoy_x::get_home_depot"] = "coord3d()" export_types_ai["convoy_x::has_obsolete_vehicles"] = "bool()" export_types_ai["convoy_x::toggle_withdraw"] = "void(player_x)" export_types_ai["convoy_x::is_withdrawn"] = "bool()" export_types_ai["convoy_x::is_in_depot"] = "bool()" export_types_ai["convoy_x::is_waiting"] = "bool()" export_types_ai["convoy_x::is_loading"] = "bool()" export_types_ai["convoy_x::destroy"] = "void(player_x)" export_types_ai["convoy_x::is_schedule_editor_open"] = "bool()" export_types_ai["convoy_x::get_tile_length"] = "integer()" export_types_ai["convoy_x::change_schedule"] = "void(player_x, schedule_x)" export_types_ai["convoy_x::calc_max_speed"] = "integer(integer, integer, integer)" export_types_ai["convoy_x::speed_to_tiles_per_month"] = "integer(integer)" export_types_ai["factory_x::is_valid"] = "bool()" export_types_ai["factory_x::get_consumers"] = "array()" export_types_ai["factory_x::get_suppliers"] = "array()" export_types_ai["factory_x::get_name"] = "string()" export_types_ai["factory_x::get_raw_name"] = "string()" export_types_ai["factory_x::set_name"] = "void(string)" export_types_ai["factory_x::get_production"] = "array()" export_types_ai["factory_x::get_power"] = "array()" export_types_ai["factory_x::get_boost_electric"] = "array()" export_types_ai["factory_x::get_boost_pax"] = "array()" export_types_ai["factory_x::get_boost_mail"] = "array()" export_types_ai["factory_x::get_pax_generated"] = "array()" export_types_ai["factory_x::get_pax_departed"] = "array()" export_types_ai["factory_x::get_pax_arrived"] = "array()" export_types_ai["factory_x::get_mail_generated"] = "array()" export_types_ai["factory_x::get_mail_departed"] = "array()" export_types_ai["factory_x::get_mail_arrived"] = "array()" export_types_ai["factory_x::get_tile_list"] = "array()" export_types_ai["factory_x::get_fields_list"] = "array()" export_types_ai["factory_x::get_halt_list"] = "array()" export_types_ai["factory_x::is_transformer_connected"] = "bool()" export_types_ai["factory_x::get_transformer"] = "powerline_x()" export_types_ai["factory_x::get_field_count"] = "integer()" export_types_ai["factory_x::get_min_field_count"] = "integer()" export_types_ai["factory_x::get_desc"] = "factory_desc_x()" export_types_ai["factory_production_x::get_storage"] = "array()" export_types_ai["factory_production_x::get_received"] = "array()" export_types_ai["factory_production_x::get_consumed"] = "array()" export_types_ai["factory_production_x::get_in_transit"] = "array()" export_types_ai["factory_production_x::get_delivered"] = "array()" export_types_ai["factory_production_x::get_produced"] = "array()" export_types_ai["factory_production_x::get_consumption_factor"] = "integer()" export_types_ai["factory_production_x::get_production_factor"] = "integer()" export_types_ai["obj_desc_x::get_name"] = "string()" export_types_ai["obj_desc_x::is_equal"] = "bool(obj_desc_x)" export_types_ai["obj_desc_x::is_valid"] = "bool()" export_types_ai["obj_desc_time_x::get_intro_date"] = "time_x()" export_types_ai["obj_desc_time_x::get_retire_date"] = "time_x()" export_types_ai["obj_desc_time_x::is_future"] = "bool(time_x)" export_types_ai["obj_desc_time_x::is_retired"] = "bool(time_x)" export_types_ai["obj_desc_time_x::is_available"] = "bool(time_x)" export_types_ai["obj_desc_transport_x::get_maintenance"] = "integer()" export_types_ai["obj_desc_transport_x::get_cost"] = "integer()" export_types_ai["obj_desc_transport_x::get_waytype"] = "way_types()" export_types_ai["obj_desc_transport_x::get_topspeed"] = "integer()" export_types_ai["vehicle_desc_x::can_be_first"] = "bool()" export_types_ai["vehicle_desc_x::can_be_last"] = "bool()" export_types_ai["vehicle_desc_x::get_successors"] = "array()" export_types_ai["vehicle_desc_x::get_predecessors"] = "array()" export_types_ai["vehicle_desc_x::get_available_vehicles"] = "array(way_types)" export_types_ai["vehicle_desc_x::get_power"] = "integer()" export_types_ai["vehicle_desc_x::needs_electrification"] = "bool()" export_types_ai["vehicle_desc_x::get_freight"] = "good_desc_x()" export_types_ai["vehicle_desc_x::get_capacity"] = "integer()" export_types_ai["vehicle_desc_x::get_running_cost"] = "integer()" export_types_ai["vehicle_desc_x::get_maintenance"] = "integer()" export_types_ai["vehicle_desc_x::get_weight"] = "integer()" export_types_ai["vehicle_desc_x::get_length"] = "integer()" export_types_ai["vehicle_desc_x::is_coupling_allowed"] = "bool(vehicle_desc_x, vehicle_desc_x)" export_types_ai["tree_desc_x::get_price"] = "integer()" export_types_ai["building_desc_x::is_attraction"] = "bool()" export_types_ai["building_desc_x::get_maintenance"] = "integer()" export_types_ai["building_desc_x::get_cost"] = "integer()" export_types_ai["building_desc_x::get_capacity"] = "integer()" export_types_ai["building_desc_x::can_be_built_underground"] = "bool()" export_types_ai["building_desc_x::can_be_built_aboveground"] = "bool()" export_types_ai["building_desc_x::enables_pax"] = "bool()" export_types_ai["building_desc_x::enables_mail"] = "bool()" export_types_ai["building_desc_x::enables_freight"] = "bool()" export_types_ai["building_desc_x::get_type"] = "building_desc_x::building_type()" export_types_ai["building_desc_x::get_waytype"] = "way_types()" export_types_ai["building_desc_x::get_headquarter_level"] = "integer()" export_types_ai["building_desc_x::get_building_list"] = "array(building_desc_x::building_type)" export_types_ai["building_desc_x::get_available_stations"] = "array(building_desc_x::building_type, way_types, good_desc_x)" export_types_ai["building_desc_x::is_terminus"] = "bool()" export_types_ai["factory_desc_x::get_name"] = "string()" export_types_ai["factory_desc_x::get_building_desc"] = "building_desc_x()" export_types_ai["factory_desc_x::is_electricity_producer"] = "bool()" export_types_ai["factory_desc_x::get_productivity_base"] = "integer()" export_types_ai["factory_desc_x::get_productivity_range"] = "integer()" export_types_ai["factory_desc_x::get_list"] = "table()" export_types_ai["way_desc_x::has_double_slopes"] = "bool()" export_types_ai["way_desc_x::get_system_type"] = "way_system_types()" export_types_ai["way_desc_x::get_available_ways"] = "array(way_types, way_system_types)" export_types_ai["way_desc_x::get_default_way_desc"] = "way_desc_x(integer)" export_types_ai["tunnel_desc_x::get_available_tunnels"] = "array(way_types)" export_types_ai["bridge_desc_x::has_double_ramp"] = "bool()" export_types_ai["bridge_desc_x::has_double_start"] = "bool()" export_types_ai["bridge_desc_x::get_max_length"] = "integer()" export_types_ai["bridge_desc_x::get_max_height"] = "integer()" export_types_ai["bridge_desc_x::get_available_bridges"] = "array(way_types)" export_types_ai["good_desc_x::get_catg_index"] = "integer()" export_types_ai["good_desc_x::is_interchangeable"] = "bool(good_desc_x)" export_types_ai["good_desc_x::get_weight_per_unit"] = "integer()" export_types_ai["good_desc_x::get_metric"] = "string()" export_types_ai["good_desc_x::calc_revenue"] = "integer(way_types, integer)" export_types_ai["sign_desc_x::is_one_way"] = "bool()" export_types_ai["sign_desc_x::is_private_way"] = "bool()" export_types_ai["sign_desc_x::is_traffic_light"] = "bool()" export_types_ai["sign_desc_x::is_choose_sign"] = "bool()" export_types_ai["sign_desc_x::is_signal"] = "bool()" export_types_ai["sign_desc_x::is_pre_signal"] = "bool()" export_types_ai["sign_desc_x::is_priority_signal"] = "bool()" export_types_ai["sign_desc_x::is_longblock_signal"] = "bool()" export_types_ai["sign_desc_x::is_end_choose_signal"] = "bool()" export_types_ai["sign_desc_x::get_available_signs"] = "array(way_types)" export_types_ai["wayobj_desc_x::is_overhead_line"] = "bool()" export_types_ai["wayobj_desc_x::get_available_wayobjs"] = "array(way_types)" export_types_ai["gui::add_message_at"] = "string(player_x, string, coord3d)" export_types_ai["halt_x::is_valid"] = "bool()" export_types_ai["halt_x::get_name"] = "string()" export_types_ai["halt_x::set_name"] = "void(string)" export_types_ai["halt_x::get_owner"] = "player_x()" export_types_ai["halt_x::_cmp"] = "integer(halt_x)" export_types_ai["halt_x::is_connected"] = "integer(halt_x, good_desc_x)" export_types_ai["halt_x::accepts_good"] = "bool(good_desc_x)" export_types_ai["halt_x::get_arrived"] = "array()" export_types_ai["halt_x::get_departed"] = "array()" export_types_ai["halt_x::get_waiting"] = "array()" export_types_ai["halt_x::get_happy"] = "array()" export_types_ai["halt_x::get_unhappy"] = "array()" export_types_ai["halt_x::get_noroute"] = "array()" export_types_ai["halt_x::get_convoys"] = "array()" export_types_ai["halt_x::get_walked"] = "array()" export_types_ai["halt_x::get_tile_list"] = "array()" export_types_ai["halt_x::get_factory_list"] = "array()" export_types_ai["halt_x::get_freight_to_dest"] = "integer(good_desc_x, coord)" export_types_ai["halt_x::get_freight_to_halt"] = "integer(good_desc_x, halt_x)" export_types_ai["halt_x::get_capacity"] = "integer(good_desc_x)" export_types_ai["halt_x::get_connections"] = "array(good_desc_x)" export_types_ai["halt_x::get_halt"] = "halt_x(coord3d, player_x)" export_types_ai["line_x::is_valid"] = "bool()" export_types_ai["line_x::get_name"] = "string()" export_types_ai["line_x::set_name"] = "void(string)" export_types_ai["line_x::get_owner"] = "player_x()" export_types_ai["line_x::get_schedule"] = "schedule_x()" export_types_ai["line_x::get_goods_catg_index"] = "array()" export_types_ai["line_x::get_capacity"] = "array()" export_types_ai["line_x::get_transported_goods"] = "array()" export_types_ai["line_x::get_convoy_count"] = "array()" export_types_ai["line_x::get_revenue"] = "array()" export_types_ai["line_x::get_cost"] = "array()" export_types_ai["line_x::get_profit"] = "array()" export_types_ai["line_x::get_traveled_distance"] = "array()" export_types_ai["line_x::get_way_tolls"] = "array()" export_types_ai["line_x::get_waytype"] = "way_types()" export_types_ai["line_x::change_schedule"] = "void(player_x, schedule_x)" export_types_ai["line_x::destroy"] = "void(player_x)" export_types_ai["map_object_x::is_valid"] = "bool()" export_types_ai["map_object_x::get_owner"] = "player_x()" export_types_ai["map_object_x::get_name"] = "string()" export_types_ai["map_object_x::get_waytype"] = "way_types()" export_types_ai["map_object_x::get_pos"] = "coord3d()" export_types_ai["map_object_x::is_removable"] = "string(player_x)" export_types_ai["map_object_x::get_type"] = "map_objects()" export_types_ai["map_object_x::mark"] = "void()" export_types_ai["map_object_x::unmark"] = "void()" export_types_ai["map_object_x::is_marked"] = "bool()" export_types_ai["tree_x::get_age"] = "integer()" export_types_ai["tree_x::get_desc"] = "tree_desc_x()" export_types_ai["building_x::get_factory"] = "factory_x()" export_types_ai["building_x::get_city"] = "city_x()" export_types_ai["building_x::is_townhall"] = "bool()" export_types_ai["building_x::is_headquarter"] = "bool()" export_types_ai["building_x::is_monument"] = "bool()" export_types_ai["building_x::get_passenger_level"] = "integer()" export_types_ai["building_x::get_mail_level"] = "integer()" export_types_ai["building_x::get_desc"] = "building_desc_x()" export_types_ai["building_x::get_tile_list"] = "array()" export_types_ai["building_x::is_same_building"] = "bool(building_x)" export_types_ai["depot_x::append_vehicle"] = "void(player_x, convoy_x, vehicle_desc_x)" export_types_ai["depot_x::start_convoy"] = "void(player_x, convoy_x)" export_types_ai["depot_x::start_all_convoys"] = "void(player_x)" export_types_ai["depot_x::get_convoy_list"] = "array()" export_types_ai["depot_x::get_depot_list"] = "array(player_x, way_types)" export_types_ai["way_x::has_sidewalk"] = "bool()" export_types_ai["way_x::is_electrified"] = "bool()" export_types_ai["way_x::has_sign"] = "bool()" export_types_ai["way_x::has_signal"] = "bool()" export_types_ai["way_x::has_wayobj"] = "bool()" export_types_ai["way_x::is_crossing"] = "bool()" export_types_ai["way_x::get_desc"] = "way_desc_x()" export_types_ai["way_x::get_max_speed"] = "integer()" export_types_ai["way_x::get_transported_goods"] = "array()" export_types_ai["way_x::get_convoys_passed"] = "array()" export_types_ai["label_x::create"] = "string(coord, player_x, string)" export_types_ai["label_x::set_text"] = "void(string)" export_types_ai["label_x::get_text"] = "string()" export_types_ai["sign_x::get_desc"] = "sign_desc_x()" export_types_ai["sign_x::can_pass"] = "bool(player_x)" export_types_ai["powerline_x::is_connected"] = "bool(powerline_x)" export_types_ai["powerline_x::get_factory"] = "factory_x()" export_types_ai["field_x::is_deletable"] = "bool()" export_types_ai["field_x::get_factory"] = "factory_x()" export_types_ai["wayobj_x::get_desc"] = "wayobj_desc_x()" export_types_ai["bridge_x::get_desc"] = "bridge_desc_x()" export_types_ai["tunnel_x::get_desc"] = "tunnel_desc_x()" export_types_ai["player_x::is_valid"] = "bool()" export_types_ai["player_x::get_headquarter_level"] = "integer()" export_types_ai["player_x::get_headquarter_pos"] = "coord()" export_types_ai["player_x::get_name"] = "string()" export_types_ai["player_x::set_name"] = "void(string)" export_types_ai["player_x::get_construction"] = "array()" export_types_ai["player_x::get_vehicle_maint"] = "array()" export_types_ai["player_x::get_new_vehicles"] = "array()" export_types_ai["player_x::get_income"] = "array()" export_types_ai["player_x::get_maintenance"] = "array()" export_types_ai["player_x::get_assets"] = "array()" export_types_ai["player_x::get_cash"] = "array()" export_types_ai["player_x::get_net_wealth"] = "array()" export_types_ai["player_x::get_profit"] = "array()" export_types_ai["player_x::get_operating_profit"] = "array()" export_types_ai["player_x::get_margin"] = "array()" export_types_ai["player_x::get_transported"] = "array()" export_types_ai["player_x::get_powerline"] = "array()" export_types_ai["player_x::get_transported_pax"] = "array()" export_types_ai["player_x::get_transported_mail"] = "array()" export_types_ai["player_x::get_transported_goods"] = "array()" export_types_ai["player_x::get_convoys"] = "array()" export_types_ai["player_x::get_way_tolls"] = "array()" export_types_ai["player_x::get_current_cash"] = "float()" export_types_ai["player_x::get_current_net_wealth"] = "integer()" export_types_ai["player_x::get_current_maintenance"] = "integer()" export_types_ai["player_x::create_line"] = "void(way_types)" export_types_ai["player_x::get_type"] = "integer()" export_types_ai["schedule_entry_x::constructor"] = "void(coord3d, integer, integer)" export_types_ai["coord3d::get_halt"] = "halt_x(player_x)" export_types_ai["settings::get_industry_increase_every"] = "integer()" export_types_ai["settings::set_industry_increase_every"] = "void(integer)" export_types_ai["settings::get_traffic_level"] = "integer()" export_types_ai["settings::set_traffic_level"] = "void(integer)" export_types_ai["settings::get_start_time"] = "time_x()" export_types_ai["settings::get_station_coverage"] = "integer()" export_types_ai["settings::get_passenger_factor"] = "integer()" export_types_ai["settings::get_factory_worker_radius"] = "integer()" export_types_ai["settings::get_factory_worker_minimum_towns"] = "integer()" export_types_ai["settings::get_factory_worker_maximum_towns"] = "integer()" export_types_ai["settings::avoid_overcrowding"] = "bool()" export_types_ai["settings::no_routing_over_overcrowding"] = "bool()" export_types_ai["settings::separate_halt_capacities"] = "bool()" export_types_ai["settings::obsolete_vehicles_allowed"] = "bool()" export_types_ai["settings::get_max_road_convoi_length"] = "integer()" export_types_ai["settings::get_max_rail_convoi_length"] = "integer()" export_types_ai["settings::get_max_ship_convoi_length"] = "integer()" export_types_ai["settings::get_max_air_convoi_length"] = "integer()" export_types_ai["settings::get_drive_on_left"] = "bool()" export_types_ai["settings::get_way_height_clearance"] = "integer()" export_types_ai["settings::has_double_slopes"] = "bool()" export_types_ai["settings::get_pay_for_total_distance_mode"] = "integer()" export_types_ai["settings::get_underground_view_level"] = "integer()" export_types_ai["settings::get_make_public_months"] = "integer()" export_types_ai["dir::is_single"] = "bool(dir)" export_types_ai["dir::is_twoway"] = "bool(dir)" export_types_ai["dir::is_threeway"] = "bool(dir)" export_types_ai["dir::is_curve"] = "bool(dir)" export_types_ai["dir::is_straight"] = "bool(dir)" export_types_ai["dir::double"] = "dir(dir)" export_types_ai["dir::backward"] = "dir(dir)" export_types_ai["dir::to_slope"] = "slope(dir)" export_types_ai["slope::to_dir"] = "dir(slope)" export_types_ai["::translate"] = "string(string)" export_types_ai["::double_to_string"] = "string(float, integer)" export_types_ai["::integer_to_string"] = "string(integer)" export_types_ai["::money_to_string"] = "string(integer)" export_types_ai["::coord_to_string"] = "string(coord)" export_types_ai["::coord3d_to_string"] = "string(coord3d)" export_types_ai["::get_month_name"] = "string(integer)" export_types_ai["::difftick_to_string"] = "string(integer)" export_types_ai["tile_x::is_valid"] = "bool()" export_types_ai["tile_x::find_object"] = "map_object_x(map_objects)" export_types_ai["tile_x::remove_object"] = "string(player_x, map_objects)" export_types_ai["tile_x::get_halt"] = "halt_x()" export_types_ai["tile_x::is_water"] = "bool()" export_types_ai["tile_x::is_bridge"] = "bool()" export_types_ai["tile_x::is_tunnel"] = "bool()" export_types_ai["tile_x::is_empty"] = "bool()" export_types_ai["tile_x::is_ground"] = "bool()" export_types_ai["tile_x::is_crossing"] = "bool()" export_types_ai["tile_x::get_slope"] = "slope()" export_types_ai["tile_x::get_text"] = "string()" export_types_ai["tile_x::has_way"] = "bool(way_types)" export_types_ai["tile_x::has_ways"] = "bool()" export_types_ai["tile_x::has_two_ways"] = "bool()" export_types_ai["tile_x::get_way"] = "way_x(way_types)" export_types_ai["tile_x::get_neighbour"] = "tile_x(way_types, dir)" export_types_ai["tile_x::get_depot"] = "depot_x()" export_types_ai["tile_x::can_remove_all_objects"] = "string(player_x)" export_types_ai["tile_x::is_marked"] = "bool()" export_types_ai["tile_x::unmark"] = "void()" export_types_ai["tile_x::mark"] = "void()" export_types_ai["tile_x::get_convoys"] = "array()" export_types_ai["square_x::is_valid"] = "bool()" export_types_ai["square_x::get_halt"] = "halt_x()" export_types_ai["square_x::get_player_halt"] = "halt_x(player_x)" export_types_ai["square_x::get_tile_at_height"] = "tile_x(integer)" export_types_ai["square_x::get_ground_tile"] = "tile_x()" export_types_ai["square_x::get_halt_list"] = "array()" export_types_ai["square_x::get_climate"] = "climates()" export_types_ai["world::is_coord_valid"] = "bool(coord)" export_types_ai["world::find_nearest_city"] = "city_x(coord)" export_types_ai["world::get_season"] = "integer()" export_types_ai["world::get_player"] = "player_x(integer)" export_types_ai["world::get_time"] = "time_ticks_x()" export_types_ai["world::get_citizens"] = "array()" export_types_ai["world::get_growth"] = "array()" export_types_ai["world::get_towns"] = "array()" export_types_ai["world::get_factories"] = "array()" export_types_ai["world::get_convoys"] = "array()" export_types_ai["world::get_citycars"] = "array()" export_types_ai["world::get_ratio_pax"] = "array()" export_types_ai["world::get_generated_pax"] = "array()" export_types_ai["world::get_ratio_mail"] = "array()" export_types_ai["world::get_generated_mail"] = "array()" export_types_ai["world::get_ratio_goods"] = "array()" export_types_ai["world::get_transported_goods"] = "array()" export_types_ai["world::get_year_citizens"] = "array()" export_types_ai["world::get_year_growth"] = "array()" export_types_ai["world::get_year_towns"] = "array()" export_types_ai["world::get_year_factories"] = "array()" export_types_ai["world::get_year_convoys"] = "array()" export_types_ai["world::get_year_citycars"] = "array()" export_types_ai["world::get_year_ratio_pax"] = "array()" export_types_ai["world::get_year_generated_pax"] = "array()" export_types_ai["world::get_year_ratio_mail"] = "array()" export_types_ai["world::get_year_generated_mail"] = "array()" export_types_ai["world::get_year_ratio_goods"] = "array()" export_types_ai["world::get_year_transported_goods"] = "array()" export_types_ai["world::use_timeline"] = "bool()" export_types_ai["attraction_list_x::_get"] = "building_x(integer)" export_types_ai["label_list_x::_get"] = "label_x(integer)" export_types_ai["label_list_x::get_count"] = "integer()" export_types_ai["::get_pakset_name"] = "string()" export_types_ai["::get_version_number"] = "string()" export_types_ai["simple_heap_x::clear"] = "void()" export_types_ai["simple_heap_x::len"] = "integer()" export_types_ai["simple_heap_x::is_empty"] = "bool()" export_types_ai["simple_heap_x::insert"] = "void(integer, integer)" export_types_ai["way_planner_x::set_build_types"] = "void(way_desc_x)" export_types_ai["way_planner_x::is_allowed_step"] = "bool(tile_x, tile_x)" export_types_ai["bridge_planner_x::find_end"] = "coord3d(player_x, coord3d, dir, bridge_desc_x, integer)" export_types_ai["command_x::get_flags"] = "integer()" export_types_ai["command_x::set_flags"] = "void(integer)" export_types_ai["command_x::build_way"] = "string(player_x, coord3d, coord3d, way_desc_x, bool)" export_types_ai["command_x::build_road"] = "string(player_x, coord3d, coord3d, way_desc_x, bool, bool)" export_types_ai["command_x::build_depot"] = "string(player_x, coord3d, building_desc_x)" export_types_ai["command_x::build_station"] = "string(player_x, coord3d, building_desc_x, dir)" export_types_ai["command_x::build_bridge"] = "string(player_x, coord3d, coord3d, bridge_desc_x)" export_types_ai["command_x::build_bridge_at"] = "string(player_x, coord3d, bridge_desc_x)" export_types_ai["command_x::set_slope"] = "string(player_x, coord3d, slope)" export_types_ai["command_x::restore_slope"] = "string(player_x, coord3d)" export_types_ai["command_x::can_set_slope"] = "string(player_x, coord3d, slope)" export_types_ai["command_x::slope_get_price"] = "integer(slope)" export_types_ai["command_x::build_sign_at"] = "string(player_x, coord3d, sign_desc_x)" export_types_ai["command_x::build_wayobj"] = "string(player_x, coord3d, coord3d, wayobj_desc_x)" export_types_ai["command_x::change_climate_at"] = "string(player_x, coord3d, integer)" export_types_ai["command_x::grid_lower"] = "string(player_x, coord3d)" export_types_ai["command_x::grid_raise"] = "string(player_x, coord3d)" } simutrans-124.5/src/simutrans/script/api/squirrel_types_scenario.awk000066400000000000000000000755241520675460500261530ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # file used to generate doxygen documentation of squirrel API # needs to be copied to trunk/script/api BEGIN { export_types_scenario["city_list_x::_get"] = "city_x(integer)" export_types_scenario["city_x::is_valid"] = "bool()" export_types_scenario["city_x::get_name"] = "string()" export_types_scenario["city_x::set_name"] = "void(string)" export_types_scenario["city_x::get_citizens"] = "array()" export_types_scenario["city_x::get_growth"] = "array()" export_types_scenario["city_x::get_buildings"] = "array()" export_types_scenario["city_x::get_citycars"] = "array()" export_types_scenario["city_x::get_transported_pax"] = "array()" export_types_scenario["city_x::get_generated_pax"] = "array()" export_types_scenario["city_x::get_transported_mail"] = "array()" export_types_scenario["city_x::get_generated_mail"] = "array()" export_types_scenario["city_x::get_year_citizens"] = "array()" export_types_scenario["city_x::get_year_growth"] = "array()" export_types_scenario["city_x::get_year_buildings"] = "array()" export_types_scenario["city_x::get_year_citycars"] = "array()" export_types_scenario["city_x::get_year_transported_pax"] = "array()" export_types_scenario["city_x::get_year_generated_pax"] = "array()" export_types_scenario["city_x::get_year_transported_mail"] = "array()" export_types_scenario["city_x::get_year_generated_mail"] = "array()" export_types_scenario["city_x::get_citygrowth_enabled"] = "bool()" export_types_scenario["city_x::get_pos"] = "coord()" export_types_scenario["city_x::get_pos_nw"] = "coord()" export_types_scenario["city_x::get_pos_se"] = "coord()" export_types_scenario["city_x::change_size"] = "string(integer)" export_types_scenario["city_x::set_citygrowth_enabled"] = "void(bool)" export_types_scenario["::get_ops_total"] = "integer()" export_types_scenario["::get_ops_remaining"] = "integer()" export_types_scenario["debug::pause"] = "bool()" export_types_scenario["debug::is_paused"] = "bool()" export_types_scenario["debug::set_pause_on_error"] = "void(bool)" export_types_scenario["convoy_x::is_valid"] = "bool()" export_types_scenario["convoy_x::needs_electrification"] = "bool()" export_types_scenario["convoy_x::get_name"] = "string()" export_types_scenario["convoy_x::set_name"] = "void(string)" export_types_scenario["convoy_x::get_pos"] = "coord3d()" export_types_scenario["convoy_x::get_owner"] = "player_x()" export_types_scenario["convoy_x::get_goods_catg_index"] = "array()" export_types_scenario["convoy_x::get_waytype"] = "way_types()" export_types_scenario["convoy_x::get_schedule"] = "schedule_x()" export_types_scenario["convoy_x::get_capacity"] = "array()" export_types_scenario["convoy_x::get_transported_goods"] = "array()" export_types_scenario["convoy_x::get_revenue"] = "array()" export_types_scenario["convoy_x::get_cost"] = "array()" export_types_scenario["convoy_x::get_profit"] = "array()" export_types_scenario["convoy_x::get_traveled_distance"] = "array()" export_types_scenario["convoy_x::get_way_tolls"] = "array()" export_types_scenario["convoy_x::get_distance_traveled_total"] = "integer()" export_types_scenario["convoy_x::get_line"] = "line_x()" export_types_scenario["convoy_x::set_line"] = "void(player_x, line_x)" export_types_scenario["convoy_x::get_vehicles"] = "array()" export_types_scenario["convoy_x::get_speed"] = "integer()" export_types_scenario["convoy_x::get_loading_limit"] = "integer()" export_types_scenario["convoy_x::get_loading_level"] = "integer()" export_types_scenario["convoy_x::get_home_depot"] = "coord3d()" export_types_scenario["convoy_x::has_obsolete_vehicles"] = "bool()" export_types_scenario["convoy_x::toggle_withdraw"] = "void(player_x)" export_types_scenario["convoy_x::is_withdrawn"] = "bool()" export_types_scenario["convoy_x::is_in_depot"] = "bool()" export_types_scenario["convoy_x::is_waiting"] = "bool()" export_types_scenario["convoy_x::is_followed"] = "bool()" export_types_scenario["convoy_x::set_followed"] = "void()" export_types_scenario["convoy_x::is_loading"] = "bool()" export_types_scenario["convoy_x::destroy"] = "void(player_x)" export_types_scenario["convoy_x::is_schedule_editor_open"] = "bool()" export_types_scenario["convoy_x::get_tile_length"] = "integer()" export_types_scenario["convoy_x::change_schedule"] = "void(player_x, schedule_x)" export_types_scenario["convoy_x::calc_max_speed"] = "integer(integer, integer, integer)" export_types_scenario["convoy_x::speed_to_tiles_per_month"] = "integer(integer)" export_types_scenario["factory_x::is_valid"] = "bool()" export_types_scenario["factory_x::get_consumers"] = "array()" export_types_scenario["factory_x::get_suppliers"] = "array()" export_types_scenario["factory_x::get_name"] = "string()" export_types_scenario["factory_x::get_raw_name"] = "string()" export_types_scenario["factory_x::set_name"] = "void(string)" export_types_scenario["factory_x::get_production"] = "array()" export_types_scenario["factory_x::get_power"] = "array()" export_types_scenario["factory_x::get_boost_electric"] = "array()" export_types_scenario["factory_x::get_boost_pax"] = "array()" export_types_scenario["factory_x::get_boost_mail"] = "array()" export_types_scenario["factory_x::get_pax_generated"] = "array()" export_types_scenario["factory_x::get_pax_departed"] = "array()" export_types_scenario["factory_x::get_pax_arrived"] = "array()" export_types_scenario["factory_x::get_mail_generated"] = "array()" export_types_scenario["factory_x::get_mail_departed"] = "array()" export_types_scenario["factory_x::get_mail_arrived"] = "array()" export_types_scenario["factory_x::get_tile_list"] = "array()" export_types_scenario["factory_x::get_fields_list"] = "array()" export_types_scenario["factory_x::get_halt_list"] = "array()" export_types_scenario["factory_x::is_transformer_connected"] = "bool()" export_types_scenario["factory_x::get_transformer"] = "powerline_x()" export_types_scenario["factory_x::get_field_count"] = "integer()" export_types_scenario["factory_x::get_min_field_count"] = "integer()" export_types_scenario["factory_x::get_desc"] = "factory_desc_x()" export_types_scenario["factory_production_x::get_storage"] = "array()" export_types_scenario["factory_production_x::get_received"] = "array()" export_types_scenario["factory_production_x::get_consumed"] = "array()" export_types_scenario["factory_production_x::get_in_transit"] = "array()" export_types_scenario["factory_production_x::get_delivered"] = "array()" export_types_scenario["factory_production_x::get_produced"] = "array()" export_types_scenario["factory_production_x::get_consumption_factor"] = "integer()" export_types_scenario["factory_production_x::get_production_factor"] = "integer()" export_types_scenario["obj_desc_x::get_name"] = "string()" export_types_scenario["obj_desc_x::is_equal"] = "bool(obj_desc_x)" export_types_scenario["obj_desc_x::is_valid"] = "bool()" export_types_scenario["obj_desc_time_x::get_intro_date"] = "time_x()" export_types_scenario["obj_desc_time_x::get_retire_date"] = "time_x()" export_types_scenario["obj_desc_time_x::is_future"] = "bool(time_x)" export_types_scenario["obj_desc_time_x::is_retired"] = "bool(time_x)" export_types_scenario["obj_desc_time_x::is_available"] = "bool(time_x)" export_types_scenario["obj_desc_transport_x::get_maintenance"] = "integer()" export_types_scenario["obj_desc_transport_x::get_cost"] = "integer()" export_types_scenario["obj_desc_transport_x::get_waytype"] = "way_types()" export_types_scenario["obj_desc_transport_x::get_topspeed"] = "integer()" export_types_scenario["vehicle_desc_x::can_be_first"] = "bool()" export_types_scenario["vehicle_desc_x::can_be_last"] = "bool()" export_types_scenario["vehicle_desc_x::get_successors"] = "array()" export_types_scenario["vehicle_desc_x::get_predecessors"] = "array()" export_types_scenario["vehicle_desc_x::get_available_vehicles"] = "array(way_types)" export_types_scenario["vehicle_desc_x::get_power"] = "integer()" export_types_scenario["vehicle_desc_x::needs_electrification"] = "bool()" export_types_scenario["vehicle_desc_x::get_freight"] = "good_desc_x()" export_types_scenario["vehicle_desc_x::get_capacity"] = "integer()" export_types_scenario["vehicle_desc_x::get_running_cost"] = "integer()" export_types_scenario["vehicle_desc_x::get_maintenance"] = "integer()" export_types_scenario["vehicle_desc_x::get_weight"] = "integer()" export_types_scenario["vehicle_desc_x::get_length"] = "integer()" export_types_scenario["vehicle_desc_x::is_coupling_allowed"] = "bool(vehicle_desc_x, vehicle_desc_x)" export_types_scenario["tree_desc_x::get_price"] = "integer()" export_types_scenario["building_desc_x::is_attraction"] = "bool()" export_types_scenario["building_desc_x::get_maintenance"] = "integer()" export_types_scenario["building_desc_x::get_cost"] = "integer()" export_types_scenario["building_desc_x::get_capacity"] = "integer()" export_types_scenario["building_desc_x::can_be_built_underground"] = "bool()" export_types_scenario["building_desc_x::can_be_built_aboveground"] = "bool()" export_types_scenario["building_desc_x::enables_pax"] = "bool()" export_types_scenario["building_desc_x::enables_mail"] = "bool()" export_types_scenario["building_desc_x::enables_freight"] = "bool()" export_types_scenario["building_desc_x::get_type"] = "building_desc_x::building_type()" export_types_scenario["building_desc_x::get_waytype"] = "way_types()" export_types_scenario["building_desc_x::get_headquarter_level"] = "integer()" export_types_scenario["building_desc_x::get_building_list"] = "array(building_desc_x::building_type)" export_types_scenario["building_desc_x::get_available_stations"] = "array(building_desc_x::building_type, way_types, good_desc_x)" export_types_scenario["building_desc_x::is_terminus"] = "bool()" export_types_scenario["factory_desc_x::get_name"] = "string()" export_types_scenario["factory_desc_x::get_building_desc"] = "building_desc_x()" export_types_scenario["factory_desc_x::is_electricity_producer"] = "bool()" export_types_scenario["factory_desc_x::get_productivity_base"] = "integer()" export_types_scenario["factory_desc_x::get_productivity_range"] = "integer()" export_types_scenario["factory_desc_x::get_list"] = "table()" export_types_scenario["way_desc_x::has_double_slopes"] = "bool()" export_types_scenario["way_desc_x::get_system_type"] = "way_system_types()" export_types_scenario["way_desc_x::get_available_ways"] = "array(way_types, way_system_types)" export_types_scenario["way_desc_x::get_default_way_desc"] = "way_desc_x(integer)" export_types_scenario["tunnel_desc_x::get_available_tunnels"] = "array(way_types)" export_types_scenario["bridge_desc_x::has_double_ramp"] = "bool()" export_types_scenario["bridge_desc_x::has_double_start"] = "bool()" export_types_scenario["bridge_desc_x::get_max_length"] = "integer()" export_types_scenario["bridge_desc_x::get_max_height"] = "integer()" export_types_scenario["bridge_desc_x::get_available_bridges"] = "array(way_types)" export_types_scenario["good_desc_x::get_catg_index"] = "integer()" export_types_scenario["good_desc_x::is_interchangeable"] = "bool(good_desc_x)" export_types_scenario["good_desc_x::get_weight_per_unit"] = "integer()" export_types_scenario["good_desc_x::get_metric"] = "string()" export_types_scenario["good_desc_x::calc_revenue"] = "integer(way_types, integer)" export_types_scenario["sign_desc_x::is_one_way"] = "bool()" export_types_scenario["sign_desc_x::is_private_way"] = "bool()" export_types_scenario["sign_desc_x::is_traffic_light"] = "bool()" export_types_scenario["sign_desc_x::is_choose_sign"] = "bool()" export_types_scenario["sign_desc_x::is_signal"] = "bool()" export_types_scenario["sign_desc_x::is_pre_signal"] = "bool()" export_types_scenario["sign_desc_x::is_priority_signal"] = "bool()" export_types_scenario["sign_desc_x::is_longblock_signal"] = "bool()" export_types_scenario["sign_desc_x::is_end_choose_signal"] = "bool()" export_types_scenario["sign_desc_x::get_available_signs"] = "array(way_types)" export_types_scenario["wayobj_desc_x::is_overhead_line"] = "bool()" export_types_scenario["wayobj_desc_x::get_available_wayobjs"] = "array(way_types)" export_types_scenario["gui::open_info_win"] = "void()" export_types_scenario["gui::open_info_win_at"] = "void(string)" export_types_scenario["gui::open_info_win_client"] = "void(string, integer)" export_types_scenario["gui::add_message_at"] = "string(string, coord3d)" export_types_scenario["gui::add_message"] = "string(player_x, string)" export_types_scenario["halt_x::is_valid"] = "bool()" export_types_scenario["halt_x::get_name"] = "string()" export_types_scenario["halt_x::set_name"] = "void(string)" export_types_scenario["halt_x::get_owner"] = "player_x()" export_types_scenario["halt_x::_cmp"] = "integer(halt_x)" export_types_scenario["halt_x::is_connected"] = "integer(halt_x, good_desc_x)" export_types_scenario["halt_x::accepts_good"] = "bool(good_desc_x)" export_types_scenario["halt_x::get_arrived"] = "array()" export_types_scenario["halt_x::get_departed"] = "array()" export_types_scenario["halt_x::get_waiting"] = "array()" export_types_scenario["halt_x::get_happy"] = "array()" export_types_scenario["halt_x::get_unhappy"] = "array()" export_types_scenario["halt_x::get_noroute"] = "array()" export_types_scenario["halt_x::get_convoys"] = "array()" export_types_scenario["halt_x::get_walked"] = "array()" export_types_scenario["halt_x::get_tile_list"] = "array()" export_types_scenario["halt_x::get_factory_list"] = "array()" export_types_scenario["halt_x::get_freight_to_dest"] = "integer(good_desc_x, coord)" export_types_scenario["halt_x::get_freight_to_halt"] = "integer(good_desc_x, halt_x)" export_types_scenario["halt_x::get_capacity"] = "integer(good_desc_x)" export_types_scenario["halt_x::get_connections"] = "array(good_desc_x)" export_types_scenario["halt_x::get_halt"] = "halt_x(coord3d, player_x)" export_types_scenario["line_x::is_valid"] = "bool()" export_types_scenario["line_x::get_name"] = "string()" export_types_scenario["line_x::set_name"] = "void(string)" export_types_scenario["line_x::get_owner"] = "player_x()" export_types_scenario["line_x::get_schedule"] = "schedule_x()" export_types_scenario["line_x::get_goods_catg_index"] = "array()" export_types_scenario["line_x::get_capacity"] = "array()" export_types_scenario["line_x::get_transported_goods"] = "array()" export_types_scenario["line_x::get_convoy_count"] = "array()" export_types_scenario["line_x::get_revenue"] = "array()" export_types_scenario["line_x::get_cost"] = "array()" export_types_scenario["line_x::get_profit"] = "array()" export_types_scenario["line_x::get_traveled_distance"] = "array()" export_types_scenario["line_x::get_way_tolls"] = "array()" export_types_scenario["line_x::get_waytype"] = "way_types()" export_types_scenario["line_x::change_schedule"] = "void(player_x, schedule_x)" export_types_scenario["line_x::destroy"] = "void(player_x)" export_types_scenario["map_object_x::is_valid"] = "bool()" export_types_scenario["map_object_x::get_owner"] = "player_x()" export_types_scenario["map_object_x::get_name"] = "string()" export_types_scenario["map_object_x::get_waytype"] = "way_types()" export_types_scenario["map_object_x::get_pos"] = "coord3d()" export_types_scenario["map_object_x::is_removable"] = "string(player_x)" export_types_scenario["map_object_x::get_type"] = "map_objects()" export_types_scenario["map_object_x::mark"] = "void()" export_types_scenario["map_object_x::unmark"] = "void()" export_types_scenario["map_object_x::is_marked"] = "bool()" export_types_scenario["tree_x::get_age"] = "integer()" export_types_scenario["tree_x::get_desc"] = "tree_desc_x()" export_types_scenario["building_x::get_factory"] = "factory_x()" export_types_scenario["building_x::get_city"] = "city_x()" export_types_scenario["building_x::is_townhall"] = "bool()" export_types_scenario["building_x::is_headquarter"] = "bool()" export_types_scenario["building_x::is_monument"] = "bool()" export_types_scenario["building_x::get_passenger_level"] = "integer()" export_types_scenario["building_x::get_mail_level"] = "integer()" export_types_scenario["building_x::get_desc"] = "building_desc_x()" export_types_scenario["building_x::get_tile_list"] = "array()" export_types_scenario["building_x::is_same_building"] = "bool(building_x)" export_types_scenario["depot_x::append_vehicle"] = "void(player_x, convoy_x, vehicle_desc_x)" export_types_scenario["depot_x::start_convoy"] = "void(player_x, convoy_x)" export_types_scenario["depot_x::start_all_convoys"] = "void(player_x)" export_types_scenario["depot_x::get_convoy_list"] = "array()" export_types_scenario["depot_x::get_depot_list"] = "array(player_x, way_types)" export_types_scenario["way_x::has_sidewalk"] = "bool()" export_types_scenario["way_x::is_electrified"] = "bool()" export_types_scenario["way_x::has_sign"] = "bool()" export_types_scenario["way_x::has_signal"] = "bool()" export_types_scenario["way_x::has_wayobj"] = "bool()" export_types_scenario["way_x::is_crossing"] = "bool()" export_types_scenario["way_x::get_desc"] = "way_desc_x()" export_types_scenario["way_x::get_max_speed"] = "integer()" export_types_scenario["way_x::get_transported_goods"] = "array()" export_types_scenario["way_x::get_convoys_passed"] = "array()" export_types_scenario["label_x::create"] = "string(coord, player_x, string)" export_types_scenario["label_x::set_text"] = "void(string)" export_types_scenario["label_x::get_text"] = "string()" export_types_scenario["sign_x::get_desc"] = "sign_desc_x()" export_types_scenario["sign_x::can_pass"] = "bool(player_x)" export_types_scenario["powerline_x::is_connected"] = "bool(powerline_x)" export_types_scenario["powerline_x::get_factory"] = "factory_x()" export_types_scenario["field_x::is_deletable"] = "bool()" export_types_scenario["field_x::get_factory"] = "factory_x()" export_types_scenario["wayobj_x::get_desc"] = "wayobj_desc_x()" export_types_scenario["bridge_x::get_desc"] = "bridge_desc_x()" export_types_scenario["tunnel_x::get_desc"] = "tunnel_desc_x()" export_types_scenario["player_x::is_valid"] = "bool()" export_types_scenario["player_x::get_headquarter_level"] = "integer()" export_types_scenario["player_x::get_headquarter_pos"] = "coord()" export_types_scenario["player_x::get_name"] = "string()" export_types_scenario["player_x::set_name"] = "void(string)" export_types_scenario["player_x::get_construction"] = "array()" export_types_scenario["player_x::get_vehicle_maint"] = "array()" export_types_scenario["player_x::get_new_vehicles"] = "array()" export_types_scenario["player_x::get_income"] = "array()" export_types_scenario["player_x::get_maintenance"] = "array()" export_types_scenario["player_x::get_assets"] = "array()" export_types_scenario["player_x::get_cash"] = "array()" export_types_scenario["player_x::get_net_wealth"] = "array()" export_types_scenario["player_x::get_profit"] = "array()" export_types_scenario["player_x::get_operating_profit"] = "array()" export_types_scenario["player_x::get_margin"] = "array()" export_types_scenario["player_x::get_transported"] = "array()" export_types_scenario["player_x::get_powerline"] = "array()" export_types_scenario["player_x::get_transported_pax"] = "array()" export_types_scenario["player_x::get_transported_mail"] = "array()" export_types_scenario["player_x::get_transported_goods"] = "array()" export_types_scenario["player_x::get_convoys"] = "array()" export_types_scenario["player_x::get_way_tolls"] = "array()" export_types_scenario["player_x::book_cash"] = "void(integer)" export_types_scenario["player_x::get_current_cash"] = "float()" export_types_scenario["player_x::get_current_net_wealth"] = "integer()" export_types_scenario["player_x::get_current_maintenance"] = "integer()" export_types_scenario["player_x::create_line"] = "void(way_types)" export_types_scenario["player_x::get_type"] = "integer()" export_types_scenario["::load_language_file"] = "string(string)" export_types_scenario["rules::forbid_tool"] = "void(integer, integer)" export_types_scenario["rules::allow_tool"] = "void(integer, integer)" export_types_scenario["rules::forbid_way_tool"] = "void(integer, integer, way_types)" export_types_scenario["rules::allow_way_tool"] = "void(integer, integer, way_types)" export_types_scenario["rules::forbid_way_tool_rect"] = "void(integer, integer, way_types, coord, coord, string)" export_types_scenario["rules::allow_way_tool_rect"] = "void(integer, integer, way_types, coord, coord)" export_types_scenario["rules::forbid_way_tool_cube"] = "void(integer, integer, way_types, coord3d, coord3d, string)" export_types_scenario["rules::allow_way_tool_cube"] = "void(integer, integer, way_types, coord3d, coord3d)" export_types_scenario["rules::clear"] = "void()" export_types_scenario["rules::gui_needs_update"] = "void()" export_types_scenario["debug::get_forbidden_text"] = "string()" export_types_scenario["schedule_entry_x::constructor"] = "void(coord3d, integer, integer)" export_types_scenario["coord3d::get_halt"] = "halt_x(player_x)" export_types_scenario["settings::get_industry_increase_every"] = "integer()" export_types_scenario["settings::set_industry_increase_every"] = "void(integer)" export_types_scenario["settings::get_traffic_level"] = "integer()" export_types_scenario["settings::set_traffic_level"] = "void(integer)" export_types_scenario["settings::get_start_time"] = "time_x()" export_types_scenario["settings::get_station_coverage"] = "integer()" export_types_scenario["settings::get_passenger_factor"] = "integer()" export_types_scenario["settings::get_factory_worker_radius"] = "integer()" export_types_scenario["settings::get_factory_worker_minimum_towns"] = "integer()" export_types_scenario["settings::get_factory_worker_maximum_towns"] = "integer()" export_types_scenario["settings::avoid_overcrowding"] = "bool()" export_types_scenario["settings::no_routing_over_overcrowding"] = "bool()" export_types_scenario["settings::separate_halt_capacities"] = "bool()" export_types_scenario["settings::obsolete_vehicles_allowed"] = "bool()" export_types_scenario["settings::get_max_road_convoi_length"] = "integer()" export_types_scenario["settings::get_max_rail_convoi_length"] = "integer()" export_types_scenario["settings::get_max_ship_convoi_length"] = "integer()" export_types_scenario["settings::get_max_air_convoi_length"] = "integer()" export_types_scenario["settings::get_drive_on_left"] = "bool()" export_types_scenario["settings::get_way_height_clearance"] = "integer()" export_types_scenario["settings::has_double_slopes"] = "bool()" export_types_scenario["settings::get_pay_for_total_distance_mode"] = "integer()" export_types_scenario["settings::get_underground_view_level"] = "integer()" export_types_scenario["settings::get_make_public_months"] = "integer()" export_types_scenario["dir::is_single"] = "bool(dir)" export_types_scenario["dir::is_twoway"] = "bool(dir)" export_types_scenario["dir::is_threeway"] = "bool(dir)" export_types_scenario["dir::is_curve"] = "bool(dir)" export_types_scenario["dir::is_straight"] = "bool(dir)" export_types_scenario["dir::double"] = "dir(dir)" export_types_scenario["dir::backward"] = "dir(dir)" export_types_scenario["dir::to_slope"] = "slope(dir)" export_types_scenario["slope::to_dir"] = "dir(slope)" export_types_scenario["::translate"] = "string(string)" export_types_scenario["::double_to_string"] = "string(float, integer)" export_types_scenario["::integer_to_string"] = "string(integer)" export_types_scenario["::money_to_string"] = "string(integer)" export_types_scenario["::coord_to_string"] = "string(coord)" export_types_scenario["::coord3d_to_string"] = "string(coord3d)" export_types_scenario["::get_month_name"] = "string(integer)" export_types_scenario["::difftick_to_string"] = "string(integer)" export_types_scenario["tile_x::is_valid"] = "bool()" export_types_scenario["tile_x::find_object"] = "map_object_x(map_objects)" export_types_scenario["tile_x::remove_object"] = "string(player_x, map_objects)" export_types_scenario["tile_x::get_halt"] = "halt_x()" export_types_scenario["tile_x::is_water"] = "bool()" export_types_scenario["tile_x::is_bridge"] = "bool()" export_types_scenario["tile_x::is_tunnel"] = "bool()" export_types_scenario["tile_x::is_empty"] = "bool()" export_types_scenario["tile_x::is_ground"] = "bool()" export_types_scenario["tile_x::is_crossing"] = "bool()" export_types_scenario["tile_x::get_slope"] = "slope()" export_types_scenario["tile_x::get_text"] = "string()" export_types_scenario["tile_x::has_way"] = "bool(way_types)" export_types_scenario["tile_x::has_ways"] = "bool()" export_types_scenario["tile_x::has_two_ways"] = "bool()" export_types_scenario["tile_x::get_way"] = "way_x(way_types)" export_types_scenario["tile_x::get_neighbour"] = "tile_x(way_types, dir)" export_types_scenario["tile_x::get_depot"] = "depot_x()" export_types_scenario["tile_x::can_remove_all_objects"] = "string(player_x)" export_types_scenario["tile_x::is_marked"] = "bool()" export_types_scenario["tile_x::unmark"] = "void()" export_types_scenario["tile_x::mark"] = "void()" export_types_scenario["tile_x::get_convoys"] = "array()" export_types_scenario["square_x::is_valid"] = "bool()" export_types_scenario["square_x::get_halt"] = "halt_x()" export_types_scenario["square_x::get_player_halt"] = "halt_x(player_x)" export_types_scenario["square_x::get_tile_at_height"] = "tile_x(integer)" export_types_scenario["square_x::get_ground_tile"] = "tile_x()" export_types_scenario["square_x::get_halt_list"] = "array()" export_types_scenario["square_x::get_climate"] = "climates()" export_types_scenario["world::is_coord_valid"] = "bool(coord)" export_types_scenario["world::find_nearest_city"] = "city_x(coord)" export_types_scenario["world::get_season"] = "integer()" export_types_scenario["world::remove_player"] = "bool(player_x)" export_types_scenario["world::generate_goods"] = "integer(coord, coord, good_desc_x, integer)" export_types_scenario["world::get_player"] = "player_x(integer)" export_types_scenario["world::get_time"] = "time_ticks_x()" export_types_scenario["world::get_citizens"] = "array()" export_types_scenario["world::get_growth"] = "array()" export_types_scenario["world::get_towns"] = "array()" export_types_scenario["world::get_factories"] = "array()" export_types_scenario["world::get_convoys"] = "array()" export_types_scenario["world::get_citycars"] = "array()" export_types_scenario["world::get_ratio_pax"] = "array()" export_types_scenario["world::get_generated_pax"] = "array()" export_types_scenario["world::get_ratio_mail"] = "array()" export_types_scenario["world::get_generated_mail"] = "array()" export_types_scenario["world::get_ratio_goods"] = "array()" export_types_scenario["world::get_transported_goods"] = "array()" export_types_scenario["world::get_year_citizens"] = "array()" export_types_scenario["world::get_year_growth"] = "array()" export_types_scenario["world::get_year_towns"] = "array()" export_types_scenario["world::get_year_factories"] = "array()" export_types_scenario["world::get_year_convoys"] = "array()" export_types_scenario["world::get_year_citycars"] = "array()" export_types_scenario["world::get_year_ratio_pax"] = "array()" export_types_scenario["world::get_year_generated_pax"] = "array()" export_types_scenario["world::get_year_ratio_mail"] = "array()" export_types_scenario["world::get_year_generated_mail"] = "array()" export_types_scenario["world::get_year_ratio_goods"] = "array()" export_types_scenario["world::get_year_transported_goods"] = "array()" export_types_scenario["world::use_timeline"] = "bool()" export_types_scenario["attraction_list_x::_get"] = "building_x(integer)" export_types_scenario["label_list_x::_get"] = "label_x(integer)" export_types_scenario["label_list_x::get_count"] = "integer()" export_types_scenario["::get_pakset_name"] = "string()" export_types_scenario["::get_version_number"] = "string()" export_types_scenario["simple_heap_x::clear"] = "void()" export_types_scenario["simple_heap_x::len"] = "integer()" export_types_scenario["simple_heap_x::is_empty"] = "bool()" export_types_scenario["simple_heap_x::insert"] = "void(integer, integer)" export_types_scenario["way_planner_x::set_build_types"] = "void(way_desc_x)" export_types_scenario["way_planner_x::is_allowed_step"] = "bool(tile_x, tile_x)" export_types_scenario["bridge_planner_x::find_end"] = "coord3d(player_x, coord3d, dir, bridge_desc_x, integer)" export_types_scenario["command_x::get_flags"] = "integer()" export_types_scenario["command_x::set_flags"] = "void(integer)" export_types_scenario["command_x::build_way"] = "string(player_x, coord3d, coord3d, way_desc_x, bool)" export_types_scenario["command_x::build_road"] = "string(player_x, coord3d, coord3d, way_desc_x, bool, bool)" export_types_scenario["command_x::build_depot"] = "string(player_x, coord3d, building_desc_x)" export_types_scenario["command_x::build_station"] = "string(player_x, coord3d, building_desc_x, dir)" export_types_scenario["command_x::build_bridge"] = "string(player_x, coord3d, coord3d, bridge_desc_x)" export_types_scenario["command_x::build_bridge_at"] = "string(player_x, coord3d, bridge_desc_x)" export_types_scenario["command_x::set_slope"] = "string(player_x, coord3d, slope)" export_types_scenario["command_x::restore_slope"] = "string(player_x, coord3d)" export_types_scenario["command_x::can_set_slope"] = "string(player_x, coord3d, slope)" export_types_scenario["command_x::slope_get_price"] = "integer(slope)" export_types_scenario["command_x::build_sign_at"] = "string(player_x, coord3d, sign_desc_x)" export_types_scenario["command_x::build_wayobj"] = "string(player_x, coord3d, coord3d, wayobj_desc_x)" export_types_scenario["command_x::change_climate_at"] = "string(player_x, coord3d, integer)" export_types_scenario["command_x::grid_lower"] = "string(player_x, coord3d)" export_types_scenario["command_x::grid_raise"] = "string(player_x, coord3d)" } simutrans-124.5/src/simutrans/script/api_class.cc000066400000000000000000000041671520675460500221610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api_class.h" #include "api_function.h" /** * creates class, leaves it at the top of the stack * * @param vm */ SQInteger script_api::create_class(HSQUIRRELVM vm, const char* classname, const char* baseclass) { script_api::set_squirrel_type_class(classname); sq_pushroottable(vm); if (baseclass) { sq_pushstring(vm, baseclass, -1); if(!SQ_SUCCEEDED(sq_get(vm, -2))) { sq_raise_error(vm, "base class %s to derive %s from it not found", baseclass, classname); // sq error will not be reported most of the time, so report to dbg, too dbg->error("script_api::create_class", "base class '%s' to derive %s from it not found", baseclass, classname); // good luck with any script baseclass = NULL; } } sq_newclass(vm, baseclass!=NULL); sq_pushstring(vm, classname, -1); sq_push(vm, -2); // stack: root, class, classname, class sq_newslot(vm, -4, false); sq_remove(vm, -2); // remove root table return SQ_OK; } SQInteger script_api::begin_class(HSQUIRRELVM vm, const char* classname, const char* /* baseclasses - dummy */) { script_api::set_squirrel_type_class(classname); if(!SQ_SUCCEEDED(push_class(vm, classname))) { // push a dummy class on the stack to prevent failed assertions down the road sq_newclass(vm, false); return SQ_ERROR; } return SQ_OK; } void script_api::end_class(HSQUIRRELVM vm) { script_api::set_squirrel_type_class(""); sq_pop(vm,1); } /** * pushes class * * @param vm */ SQInteger script_api::push_class(HSQUIRRELVM vm, const char* classname) { sq_pushroottable(vm); sq_pushstring(vm, classname, -1); if(!SQ_SUCCEEDED(sq_get(vm, -2))) { sq_pop(vm, 1); return sq_raise_error(vm, "class %s not found", classname); } sq_remove(vm, -2); // remove root table return SQ_OK; } /** * pushes constructor of class: closure (the constructor) and environment (the class) * * @param vm */ SQInteger script_api::prepare_constructor(HSQUIRRELVM vm, const char* classname) { if (!SQ_SUCCEEDED(push_class(vm, classname))) { return SQ_ERROR; } sq_pushnull(vm); return SQ_OK; } simutrans-124.5/src/simutrans/script/api_class.h000066400000000000000000000133041520675460500220140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_CLASS_H #define SCRIPT_API_CLASS_H #include "api_param.h" #include "../../squirrel/squirrel.h" #include "../../squirrel/sq_extensions.h" // sq_call_restricted #include "../tpl/stringhashtable_tpl.h" #include /** @file api_class.h handling classes and instances */ namespace script_api { /** * Creates squirrel class on the stack. Inherits from @p baseclass. * Has to be complemented by call to end_class. * * @param vm * @param classname name of squirrel class * @param baseclass name of base class (or NULL) * @return SQ_OK or SQ_ERROR */ SQInteger create_class(HSQUIRRELVM vm, const char* classname, const char* baseclass = NULL); /** * Creates squirrel class on the stack. Inherits from @p baseclass. * Has to be complemented by call to end_class. * Assigns the tag from param::tag() to the class. * * @param vm * @tparam C assigns tag from C::tag() to the new class * @return SQ_OK or SQ_ERROR */ template SQInteger create_class(HSQUIRRELVM vm, const char* classname, const char* baseclass = NULL) { SQInteger res = create_class(vm, classname, baseclass); if (SQ_SUCCEEDED(res)) { sq_settypetag(vm, -1, param::tag()); } return res; } /** * Pushes class on stack. * Has to be complemented by call to end_class. * * @param vm * @param classname name of squirrel class, must exist prior to calling this function * @param baseclasses dummy string containing base classes - to create nice doxygen output * @return SQ_OK or SQ_ERROR */ SQInteger begin_class(HSQUIRRELVM vm, const char* classname, const char* baseclasses = NULL); /** * Pops class from stack. */ void end_class(HSQUIRRELVM vm); /** * Pushes the squirrel class onto the stack. * Has to be complemented by call to end_class. * * @param vm * @param classname name of squirrel class * @return SQ_OK or SQ_ERROR */ SQInteger push_class(HSQUIRRELVM vm, const char* classname); /** * Prepares call to class to instantiate. * Pushes class and dummy object. * * @param vm * @param classname name of squirrel class * @return SQ_OK or SQ_ERROR */ SQInteger prepare_constructor(HSQUIRRELVM vm, const char* classname); template SQInteger push_instance(HSQUIRRELVM vm, const char* classname, const As &... as) { // push constructor if (!SQ_SUCCEEDED(prepare_constructor(vm, classname)) ) { return -1; } // push parameters int nparam = push_param(vm, as...); if (!SQ_SUCCEEDED(nparam)) { return -1; } // call constructor bool ok = SQ_SUCCEEDED(sq_call_restricted(vm, nparam+1, true, false)); sq_remove(vm, ok ? -2 : -1); /* remove closure */ return ok ? 1 : -1; } /** * Create instance, set userpointer. * Does NOT call constructor. */ template inline SQInteger push_instance_up(HSQUIRRELVM vm, const C* ptr) { if (!SQ_SUCCEEDED(push_class(vm, param::squirrel_type()) )) { return -1; } sq_createinstance(vm, -1); sq_setinstanceup(vm, -1, (void*)const_cast(ptr)); sq_remove(vm, -2); // remove class return 1; } /** * Implementation of quickstone_tpl specialization */ template struct param< quickstone_tpl > { /** * Assumes that constructor of corresponding squirrel class * accepts one parameter (the id). * * @param vm * @return positive value for success, negative for failure */ static SQInteger push(HSQUIRRELVM vm, quickstone_tpl const& h) { if (h.is_bound()) { return push_instance(vm, param::squirrel_type(), h.get_id()); } else { sq_pushnull(vm); return 1; } } static const quickstone_tpl get(HSQUIRRELVM vm, SQInteger index) { uint16 id = 0; get_slot(vm, "id", id, index); quickstone_tpl h; if (id < quickstone_tpl::get_size()) { h.set_id(id); } else { sq_raise_error(vm, "Invalid id %d, too large", id); } return h; } static const char* squirrel_type() { return param::squirrel_type(); } static const char* typemask() { return param::typemask(); } }; /** * Implementation of stringhashtable_tpl specialization */ template struct param< stringhashtable_tpl > { /** * Creates table, uses string-keys as table keys. */ static SQInteger push(HSQUIRRELVM vm, stringhashtable_tpl const& v) { sq_newtable(vm); for(auto const&i : v) { create_slot(vm, i.key, i.value); } return 1; } /// squirrel_type corresponding to the c++ type/class static const char* squirrel_type() { static cbuffer_t buf; buf.clear(); buf.printf("table<%s>", param::squirrel_type() ); return buf; } }; // forward declaration template SQInteger command_release_hook(SQUserPointer up, SQInteger); /** * Stores pointer to C++ object as userpointer of squirrel class instance. * C++ object will be deleted when squirrel class instance gets released. */ template void attach_instance(HSQUIRRELVM vm, SQInteger index, C* o) { // set userpointer of class instance sq_setinstanceup(vm, index, o ); sq_setreleasehook(vm, index, command_release_hook); } /** * Retrieves pointer to stored C++ object. */ template C* get_attached_instance(HSQUIRRELVM vm, SQInteger index, SQUserPointer tag) { SQUserPointer up; if (SQ_SUCCEEDED(sq_getinstanceup(vm, index, &up, tag))) { return (C*)up; } return NULL; } /** * Releases memory allocated by attach_instance. * Do not call directly! */ template SQInteger command_release_hook(SQUserPointer up, SQInteger) { C* p = (C *)up; delete p; return 1; } }; // end of namespace #endif simutrans-124.5/src/simutrans/script/api_function.cc000066400000000000000000000035411520675460500226740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api_function.h" #include #include "../sys/simsys.h" #include "../dataobj/environment.h" /** * Auxiliary function to register function in table/class at stack top */ void script_api::register_function(HSQUIRRELVM vm, SQFUNCTION funcptr, const char *name, int nparamcheck, const char* typemask, bool staticmethod) { sq_pushstring(vm, name, -1); sq_newclosure(vm, funcptr, 0); //create a new function sq_setnativeclosurename(vm, -1, name); sq_setparamscheck(vm, nparamcheck, typemask); sq_newslot(vm, -3, staticmethod); } static FILE* file = NULL; static const char* suffix = NULL; void script_api::start_squirrel_type_logging(const char* s) { if (env_t::verbose_debug < 2) { return; } suffix = s; cbuffer_t buf; buf.printf("squirrel_types_%s.awk", suffix); file = dr_fopen(buf, "w"); if (file) { fprintf(file, "#\n"); fprintf(file, "# This file is part of the Simutrans project under the Artistic License.\n"); fprintf(file, "# (see LICENSE.txt)\n"); fprintf(file, "#\n"); fprintf(file, "# file used to generate doxygen documentation of squirrel API\n"); fprintf(file, "# needs to be copied to trunk/script/api\n"); fprintf(file, "BEGIN {\n"); } } void script_api::end_squirrel_type_logging() { if (file) { fprintf(file, "}\n"); fclose(file); file = NULL; suffix = NULL; } } static plainstring current_class; void script_api::set_squirrel_type_class(const char* classname) { current_class = classname; } void script_api::log_squirrel_type(std::string classname, const char* name, std::string squirrel_type) { if (file) { fprintf(file, "\texport_types_%s[\"%s::%s\"] = \"%s\"\n", suffix ? suffix : "", classname.empty() ? current_class.c_str() : classname.c_str(), name, squirrel_type.c_str() ); } } simutrans-124.5/src/simutrans/script/api_function.h000066400000000000000000000420261520675460500225370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_FUNCTION_H #define SCRIPT_API_FUNCTION_H #include "api_param.h" #include "../../squirrel/squirrel.h" #include #include #include #include // mark class members static in doxygen documentation of api #define STATIC /** @file api_function.h templates to export c++ function to and call from squirrel */ namespace script_api { // forward declarations template SQInteger generic_squirrel_callback(HSQUIRRELVM vm); template struct embed_call_t; template struct func_signature_t; /// @{ /// @name Function to log squirrel-side type of exported functions void start_squirrel_type_logging(const char* suffix); void end_squirrel_type_logging(); // sets current class name, does not work with nested classes void set_squirrel_type_class(const char* classname); void log_squirrel_type(std::string classname, const char* name, std::string squirrel_type); /// @} /** * Registers custom SQFUNCTION @p funcptr. * * @pre Assumes that there is a table/class at the top of the stack to * attach the function to it. * * @param funcptr function pointer to custom implementation * @param name name of the method as visible from squirrel * @param nparamcheck for squirrel parameter checking * @param typemask for squirrel parameter checking * @param staticmethod if true then register as static method */ void register_function(HSQUIRRELVM vm, SQFUNCTION funcptr, const char *name, int nparamcheck, const char* typemask, bool staticmethod = false); /** * Registers custom SQFUNCTION @p funcptr. * * Same as non-templated register_function. Typemask und paramcheck will be deduced from the template parameter. * * @param vm * @tparam F function pointer signature of c++ method * @param funcptr function pointer to custom implementation * @param name name of the method as visible from squirrel * @param staticmethod if true then register as static method */ template void register_function(HSQUIRRELVM vm, SQFUNCTION funcptr, const char *name, bool staticmethod = false) { std::string typemask = func_signature_t::get_typemask(false); int nparamcheck = func_signature_t::get_nparams(); register_function(vm, funcptr, name, nparamcheck, typemask.c_str(), staticmethod); log_squirrel_type(func_signature_t::get_squirrel_class(false), name, func_signature_t::get_squirrel_type(false, 0)); } /** * Registers custom SQFUNCTION @p funcptr with templated free variables (default parameters). * * @pre Assumes that there is a table/class at the top of the stack to * attach the function to it. * * @param vm * @tparam F type for free variables, @see freevariable * @param funcptr function pointer to custom implementation * @param name name of the method as visible from squirrel * @param nparamcheck for squirrel parameter checking * @param typemask for squirrel parameter checking * @param freevariables contains values of free variables * @param staticmethod if true then register as static method */ template void register_function_fv(HSQUIRRELVM vm, SQFUNCTION funcptr, const char *name, int nparamcheck, const char* typemask, F const& freevariables, bool staticmethod = false) { sq_pushstring(vm, name, -1); SQInteger count = freevariables.push(vm); sq_newclosure(vm, funcptr, count); //create a new function sq_setnativeclosurename(vm, -1, name); sq_setparamscheck(vm, nparamcheck, typemask); sq_newslot(vm, -3, staticmethod); } // auxiliary struct template struct function_info_t { F funcptr; // pointer to c++ function bool act_as_member; function_info_t(F f, bool d) : funcptr(f), act_as_member(d) {} }; /** * Registers native c++ method to be called from squirrel. * Squirrel calls generic_squirrel_callback, the pointer to the actual * c++ function is attached in form of userdata. * * @pre Assumes that there is a table/class at the top of the stack to * attach the function to it. * * @param vm * @tparam F function pointer signature of c++ method * @param funcptr pointer to the c++ method * @param name name of the method as visible from squirrel * @param act_as_member if true then a global (non-member) function can be called * as if it would be a member function of the class instance * provided as first argument * @param staticmethod if true then register as static method */ template void register_method(HSQUIRRELVM vm, F funcptr, const char* name, bool act_as_member = false, bool staticmethod = false) { sq_pushstring(vm, name, -1); // pointer to function info as free variable function_info_t fi(funcptr, act_as_member); SQUserPointer up = sq_newuserdata(vm, sizeof(function_info_t)); memcpy(up, &fi, sizeof(function_info_t)); // create function sq_newclosure(vm, generic_squirrel_callback, 1); // ..associate its name for debugging sq_setnativeclosurename(vm, -1, name); std::string typemask = func_signature_t::get_typemask(act_as_member); sq_setparamscheck(vm, func_signature_t::get_nparams() - act_as_member, typemask.c_str()); sq_newslot(vm, -3, staticmethod); log_squirrel_type(func_signature_t::get_squirrel_class(act_as_member), name, func_signature_t::get_squirrel_type(act_as_member, 0)); } /** * Registers native c++ method to be called from squirrel. * Squirrel calls generic_squirrel_callback, the pointer to the actual * c++ function is attached in form of userdata. * * @pre Assumes that there is a table/class at the top of the stack to * attach the function to it. * * @param vm * @tparam F function pointer signature of c++ method * @tparam V class to push default parameters as free variables * @param funcptr pointer to the c++ method * @param name name of the method as visible from squirrel * @param freevariables values of default parameters to the c++ function (not the squirrel function) * @param act_as_member if true then a global (non-member) function can be called * as if it would be a member function of the class instance * provided as first argument * @param staticmethod if true then register as static method */ template void register_method_fv(HSQUIRRELVM vm, F funcptr, const char* name, V const& freevariables, bool act_as_member = false, bool staticmethod = false) { sq_pushstring(vm, name, -1); // pointer to function info as free variable function_info_t fi(funcptr, act_as_member); SQUserPointer up = sq_newuserdata(vm, sizeof(function_info_t)); memcpy(up, &fi, sizeof(function_info_t)); // more free variables SQInteger count = freevariables.push(vm); // create function sq_newclosure(vm, generic_squirrel_callback, count+1); // ..associate its name for debugging sq_setnativeclosurename(vm, -1, name); std::string typemask = func_signature_t::get_typemask(act_as_member); sq_setparamscheck(vm, func_signature_t::get_nparams() - act_as_member - count, typemask.c_str()); sq_newslot(vm, -3, staticmethod); log_squirrel_type(func_signature_t::get_squirrel_class(act_as_member), name, func_signature_t::get_squirrel_type(act_as_member, count)); } /** * Registers native c++ method to be called from squirrel. * This allows to register a non-member function to be called as * member function of squirrel classes: The c++ class is provided as * first argument. * * @pre Assumes that there is a table/class at the top of the stack to * attach the function to it. * @see register_method * * @param vm * @tparam F function pointer signature of c++ method * @param funcptr pointer to the c++ method * @param name name of the method as visible from squirrel */ template void register_local_method(HSQUIRRELVM vm, F funcptr, const char* name) { register_method(vm, funcptr, name, true); } /** * The general purpose callback method to be called from squirrel. * @tparam F function pointer signature of c++ method */ template SQInteger generic_squirrel_callback(HSQUIRRELVM vm) { SQUserPointer up = NULL; // get pointer to function function_info_t fi(0,false); sq_getuserdata(vm, -1, &up, NULL); memcpy(&fi, up, sizeof(function_info_t)); // call the template that automatically fetches right number of parameters return embed_call_t::call_function(vm, fi.funcptr, fi.act_as_member); } /** * Template to split parameter lists */ template struct parameter_pack_t { using first = A1; using back = void (*)(As...); }; /** * Template to generate strings related to argument lists */ template struct parameter_list_t; template struct parameter_list_t< void (*)(As...) > { /// @returns get typemask of first @p nparams parameters only static std::string get_param_typemask() { std::string res = param::first>::typemask() + parameter_list_t::back >::get_param_typemask(); return res; } /// @returns type list of first @p nparams arguments (without parentheses) static std::string get_param_squirrel_type(int nparams) { if (nparams > 0) { std::string res = param::first>::squirrel_type(); std::string add = parameter_list_t< typename parameter_pack_t::back >::get_param_squirrel_type(nparams-1); return res + (add.empty() ? "" : ", " + add); } return ""; } }; // specialization for empty parameter list template<> struct parameter_list_t< void (*)() > { static std::string get_param_typemask() { return ""; } static std::string get_param_squirrel_type(int) { return ""; } }; /** * Templates to generate squirrel typemasks, * wrapped in a one-parameter template struct, * which is specialized per function-signature. */ template struct func_signature_t; /** * Non-member functions. * May act as member functions if @p act_as_member is true. */ template struct func_signature_t { /// @returns typemask taking into account class argument is taken from 0th or 1st parameter static std::string get_typemask(bool act_as_member) { return (act_as_member ? "" : "." ) + parameter_list_t::get_param_typemask(); } /// @returns number of parameters static int get_nparams() { return sizeof...(As) + 1; } /// @returns squirrel class name of class argument of F ("" if not a member function) static std::string get_squirrel_class(bool act_as_member) { if (act_as_member) { // type of first parameter - if there is none then "" return parameter_list_t::get_param_squirrel_type(1); } return ""; } /// @returns function signature if interpreted as squirrel function static std::string get_squirrel_type(bool act_as_member, int freevars) { std::string res = get_return_squirrel_type() + "("; // remaining number of parameters (including class name if act_as_member == true) int nparams = get_nparams() - freevars - 1; std::string params = parameter_list_t< void (*)(As...) >::get_param_squirrel_type(nparams); if (act_as_member) { // ignore class parameter size_t n = params.find(", "); if (n == std::string::npos || n+2 > params.size()) { // not found params = ""; } else { // drop first parameter params = params.substr(n+2); } } return res += params + ")"; } /// @return squirrel type of return value static std::string get_return_squirrel_type() { return param::squirrel_type(); // will resolve R = void } }; /** * Member functions */ template struct func_signature_t { // reduce it to the case of an non-member function with act_as_member == true using function = R (*)(C*,As...); static int get_nparams() { return func_signature_t::get_nparams() - 1; } static std::string get_typemask(bool) { return func_signature_t::get_typemask(true); } static std::string get_squirrel_class(bool) { return param::squirrel_type(); } static std::string get_squirrel_type(bool, int freevars) { return func_signature_t::get_squirrel_type(true, freevars); } }; template struct func_signature_t : public func_signature_t { }; /** * Class to check first parameters to be not NULL. * Actually do the check only for pointer types. */ template struct param_chk_t { static bool is_null(T) { return false; } }; template struct param_chk_t { static bool is_null(T* t) { return t == NULL; } }; /** * Classes to implement the actual calls. * Calls to function pointers and member functions are turned into std::function calls. */ template class call_std_function_t; // specialization for functions with at least one argument: we might have to check for null-pointers template class call_std_function_t< R(A1,As...) > { template static SQInteger call_function_helper(HSQUIRRELVM vm, std::function const& func, bool act_as_member, std::index_sequence) { A1 a1 = param::get(vm, 2-act_as_member); if (act_as_member && param_chk_t::is_null(a1)) { return -1; } // non-void return value return param::push(vm, func(a1, param::get(vm, is+2 /* index on stack */ +1 /* correct for A1 */ -act_as_member)...) ); // index on stack: at +1 `this` argument, then parameters starting from +2 // if act_as_member then `this` is first argument to function, results in offset -1 } public: static SQInteger call_function(HSQUIRRELVM vm, std::function const& func, bool act_as_member) { return call_function_helper(vm, func, act_as_member, std::index_sequence_for{}); } }; // specialization for void functions with at least one argument: we might have to check for null-pointers template class call_std_function_t< void(A1,As...) > { template static SQInteger call_function_helper(HSQUIRRELVM vm, std::function const& func, bool act_as_member, std::index_sequence) { A1 a1 = param::get(vm, 2-act_as_member); if (act_as_member && param_chk_t::is_null(a1)) { return -1; } // void function func(a1, param::get(vm, is+2 /* index on stack */ +1 /* correct for A1 */ -act_as_member)...); return 0; } public: static SQInteger call_function(HSQUIRRELVM vm, std::function const& func, bool act_as_member) { return call_function_helper(vm, func, act_as_member, std::index_sequence_for{}); } }; // functions with no parameters template class call_std_function_t< R() > { public: static SQInteger call_function(HSQUIRRELVM vm, std::function const& func, bool) { return param::push(vm, func()); } }; template<> class call_std_function_t< void() > { public: static SQInteger call_function(HSQUIRRELVM, std::function const& func, bool) { func(); return 0; } }; /** * Templates to call functions with automatically fetching the right parameters, * wrapped in a one-parameter template struct, * which is specialized per function-signature. */ template struct embed_call_t; // Non-member functions template struct embed_call_t { static SQInteger call_function(HSQUIRRELVM vm, R (*func_ptr)(As...), bool act_as_member) { std::function< R(As...)> func = func_ptr; return call_std_function_t< R(As...) >::call_function(vm, func, act_as_member); } }; // Non-const member functions template struct embed_call_t { static SQInteger call_function(HSQUIRRELVM vm, R (C::*member_func_ptr)(As...), bool) { std::function< R(C*,As...)> func = member_func_ptr; return call_std_function_t< R(C*,As...) >::call_function(vm, func, true); } }; // Const member functions template struct embed_call_t { static SQInteger call_function(HSQUIRRELVM vm, R (C::*member_func_ptr)(As...) const, bool) { std::function< R(const C*,As...)> func = member_func_ptr; return call_std_function_t< R(const C*,As...) >::call_function(vm, func, true); } }; /** * Exports function to check whether pointer to in-game object is not null */ template SQInteger is_ptr_valid(HSQUIRRELVM vm) { P ptr = param

::get(vm, 1); sq_pushbool(vm, ptr != NULL); return 1; } template void export_is_valid(HSQUIRRELVM vm) { register_function(vm, is_ptr_valid

, "is_valid", 1, param

::typemask()); log_squirrel_type(param

::squirrel_type(), "is_valid", "bool()"); } }; // end of namespace #endif simutrans-124.5/src/simutrans/script/api_param.cc000066400000000000000000000357411520675460500221560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "api_param.h" #include "api_class.h" #include "../world/simcity.h" #include "../simfab.h" #include "../builder/goods_manager.h" #include "../dataobj/schedule.h" #include "../dataobj/loadsave.h" #include "../dataobj/scenario.h" #include "../player/simplay.h" #include "../utils/plainstring.h" #include "api/api_command.h" // script_api::my_tool_t #include "api/api_simple.h" // my_ribi_t namespace script_api { karte_ptr_t welt; // rotation handling void rotate90() { coordinate_transform_t::rotate90(); } void new_world() { coordinate_transform_t::new_world(); } uint8 coordinate_transform_t::rotation = 4; void coordinate_transform_t::initialize() { if (rotation == 4) { rotation = welt->get_settings().get_rotation(); } } void coordinate_transform_t::rdwr(loadsave_t *file) { file->rdwr_byte(rotation); } void coordinate_transform_t::koord_w2sq(koord &k) { // do not transform koord::invalid if (k.x == -1 && k.y == -1) { return; } switch( rotation ) { // 0: do nothing case 1: k = koord(k.y, welt->get_size().x-1 - k.x); break; case 2: k = koord(welt->get_size().x-1 - k.x, welt->get_size().y-1 - k.y); break; case 3: k = koord(welt->get_size().y-1 - k.y, k.x); break; default: break; } } void coordinate_transform_t::koord_sq2w(koord &k) { // do not transform koord::invalid if (k.x == -1 && k.y == -1) { return; } switch( rotation ) { // 0: do nothing case 1: k = koord(welt->get_size().x-1 - k.y, k.x); break; case 2: k = koord(welt->get_size().x-1 - k.x, welt->get_size().y-1 - k.y); break; case 3: k = koord(k.y, welt->get_size().y-1 - k.x); break; default: break; } } void coordinate_transform_t::ribi_w2sq(ribi_t::ribi &r) { if (rotation) { r = ( ( (r << 4) | r) >> rotation) & 15; } } void coordinate_transform_t::ribi_sq2w(ribi_t::ribi &r) { if (rotation) { r = ( ( (r << 4) | r) << rotation) >> 4 & 15; } } void coordinate_transform_t::slope_w2sq(slope_t::type &s) { if (s < slope_t::max_number) { for(uint8 i=1; i <= 4-rotation; i++) { s = slope_t::rotate90(s); } } } void coordinate_transform_t::slope_sq2w(slope_t::type &s) { if (s < slope_t::max_number) { for(uint8 i=1; i <= rotation; i++) { s = slope_t::rotate90(s); } } } // integer arguments uint8 param::get(HSQUIRRELVM vm, SQInteger index) { SQInteger i = 0; sq_getinteger(vm, index, &i); return clamp(i, 0, 255); } SQInteger param::push(HSQUIRRELVM vm, uint8 const& v) { sq_pushinteger(vm, v); return 1; } sint8 param::get(HSQUIRRELVM vm, SQInteger index) { SQInteger i = 0; sq_getinteger(vm, index, &i); return clamp(i, -128, 127); } SQInteger param::push(HSQUIRRELVM vm, sint8 const& v) { sq_pushinteger(vm, v); return 1; } uint16 param::get(HSQUIRRELVM vm, SQInteger index) { SQInteger i = 0; sq_getinteger(vm, index, &i); return clamp(i, 0, 0xffff); } SQInteger param::push(HSQUIRRELVM vm, uint16 const& v) { sq_pushinteger(vm, v); return 1; } sint16 param::get(HSQUIRRELVM vm, SQInteger index) { SQInteger i = 0; sq_getinteger(vm, index, &i); return clamp(i, -32768, 0x7fff); } SQInteger param::push(HSQUIRRELVM vm, sint16 const& v) { sq_pushinteger(vm, v); return 1; } uint32 param::get(HSQUIRRELVM vm, SQInteger index) { SQInteger i = 0; sq_getinteger(vm, index, &i); return i>=0 ? i : 0; } SQInteger param::push(HSQUIRRELVM vm, uint32 const& v) { sq_pushinteger(vm, v); return 1; } sint32 param::get(HSQUIRRELVM vm, SQInteger index) { SQInteger i = 0; sq_getinteger(vm, index, &i); return i; } SQInteger param::push(HSQUIRRELVM vm, sint32 const& v) { sq_pushinteger(vm, v); return 1; } uint64 param::get(HSQUIRRELVM vm, SQInteger index) { SQInteger i = 0; sq_getinteger(vm, index, &i); return i>=0 ? i : 0; } SQInteger param::push(HSQUIRRELVM vm, uint64 const& v) { sq_pushinteger(vm, (SQInteger)v); return 1; } sint64 param::get(HSQUIRRELVM vm, SQInteger index) { SQInteger i = 0; sq_getinteger(vm, index, &i); return i; } SQInteger param::push(HSQUIRRELVM vm, sint64 const& v) { sq_pushinteger(vm, (SQInteger)v); return 1; } // floats double param::get(HSQUIRRELVM vm, SQInteger index) { SQFloat d = 0.0; sq_getfloat(vm, index, &d); return d; } SQInteger param::push(HSQUIRRELVM vm, double const& v) { sq_pushfloat(vm, (SQFloat)v); return 1; } // strings const char* param::get(HSQUIRRELVM vm, SQInteger index) { const char* str = NULL; if (!SQ_SUCCEEDED(sq_getstring(vm, index, &str))) { sq_raise_error(vm, "Supplied string parameter is null"); return NULL; } return str; } SQInteger param::push(HSQUIRRELVM vm, const char* const& v) { if (v) { sq_pushstring(vm, v, -1); } else { sq_pushnull(vm); } return 1; } plainstring param::get(HSQUIRRELVM vm, SQInteger index) { if (sq_gettype(vm, index) == OT_NULL) { return NULL; } plainstring ret; const char* str = NULL; if (!SQ_SUCCEEDED(sq_getstring(vm, index, &str))) { // try tostring if (SQ_SUCCEEDED(sq_tostring(vm, index))) { sq_getstring(vm, -1, &str); ret = str; sq_pop(vm, 1); } } else { ret = str; } return ret; } SQInteger param::push(HSQUIRRELVM vm, plainstring const& v) { return param::push(vm, v.c_str()); } // bool bool param::get(HSQUIRRELVM vm, SQInteger index) { SQBool b = false; sq_tobool(vm, index, &b); return b; } SQInteger param::push(HSQUIRRELVM vm, bool const& v) { sq_pushbool(vm, v); return 1; } // coordinates koord param::get(HSQUIRRELVM vm, SQInteger index) { sint16 x=-1, y=-1; get_slot(vm, "x", x, index); get_slot(vm, "y", y, index); koord k(x,y); if (k.x != -1 && k.y != -1) { // transform coordinates coordinate_transform_t::koord_sq2w(k); } else { k = koord::invalid; } return k; } SQInteger param::push(HSQUIRRELVM vm, koord const& v) { koord k(v); if (k.x != -1 && k.y != -1) { // transform coordinates coordinate_transform_t::koord_w2sq(k); } else { k = koord::invalid; } return push_instance(vm, "coord", k.x, k.y); } koord3d param::get(HSQUIRRELVM vm, SQInteger index) { sint8 z = -1; if (!SQ_SUCCEEDED(get_slot(vm, "z", z, index))) { return koord3d::invalid; } koord k = param::get(vm, index); return koord3d(k, z); } SQInteger param::push(HSQUIRRELVM vm, koord3d const& v) { koord k(v.get_2d()); if (k.x != -1 && k.y != -1) { // transform coordinates coordinate_transform_t::koord_w2sq(k); } else { k = koord::invalid; } return push_instance(vm, "coord3d", k.x, k.y, v.z); } my_koord3d param::get(HSQUIRRELVM vm, SQInteger index) { koord3d k3 = koord3d::invalid; sint8 z = 0; // dummy if (!SQ_SUCCEEDED(get_slot(vm, "z", z, index))) { // 2d, try kartenboden koord k = param::get(vm,index); if (grund_t *gr = welt->lookup_kartenboden(k)) { k3 = gr->get_pos(); } // else: return invalid } else { // 3d k3 = param::get(vm, index); } return k3; } // directions / ribis SQInteger param::push(HSQUIRRELVM vm, my_ribi_t const& v) { ribi_t::ribi ribi = v; coordinate_transform_t::ribi_w2sq(ribi); return param::push(vm, ribi); } my_ribi_t param::get(HSQUIRRELVM vm, SQInteger index) { ribi_t::ribi ribi = param::get(vm, index) & ribi_t::all; coordinate_transform_t::ribi_sq2w(ribi); return ribi; } // slopes SQInteger param::push(HSQUIRRELVM vm, my_slope_t const& v) { slope_t::type slope = v; coordinate_transform_t::slope_w2sq(slope); return param::push(vm, slope); } my_slope_t param::get(HSQUIRRELVM vm, SQInteger index) { slope_t::type slope = param::get(vm, index); coordinate_transform_t::slope_sq2w(slope); return slope; } // pointers to classes convoi_t* param::get(HSQUIRRELVM vm, SQInteger index) { convoihandle_t cnv = param::get(vm, index); if (!cnv.is_bound()) { sq_raise_error(vm, "Invalid convoi id %d", cnv.get_id()); } return cnv.get_rep(); } fabrik_t* param::get(HSQUIRRELVM vm, SQInteger index) { koord pos = param::get(vm, index); fabrik_t* fab = fabrik_t::get_fab(pos); if (fab==NULL) { sq_raise_error(vm, "no factory at position (%s)", pos.get_str()); } return fab; } const fabrik_t* param::get(HSQUIRRELVM vm, SQInteger index) { return param::get(vm, index); } SQInteger param::push(HSQUIRRELVM vm, const fabrik_t* const& fab) { if (fab == NULL) { sq_pushnull(vm); return 1; } koord pos(fab->get_pos().get_2d()); coordinate_transform_t::koord_w2sq(pos); return push_instance(vm, "factory_x", pos.x, pos.y); } SQInteger param::push(HSQUIRRELVM vm, fabrik_t* const& fab) { return param::push(vm, fab); } const ware_production_t* param::get(HSQUIRRELVM vm, SQInteger index) { fabrik_t* fab = param::get(vm, index); if (fab == NULL) { return NULL; } // obtain index into wareproduction_t arrays SQInteger i = -1; if (SQ_SUCCEEDED(get_slot(vm, "index", i, index))) { if (i>=0) { if ( (uint32)iget_input().get_count()) { return &fab->get_input()[i]; } else { i -= fab->get_input().get_count(); if ( (uint32)iget_output().get_count()) { return &fab->get_output()[i]; } } } } sq_raise_error(vm, "No production slot [%d] in factory at (%s)", i, fab->get_pos().get_str()); return NULL; } const factory_supplier_desc_t* param::get(HSQUIRRELVM vm, SQInteger index) { fabrik_t* fab = param::get(vm, index); if (fab == NULL) { return NULL; } // obtain index into wareproduction_t arrays SQInteger i = -1; if (SQ_SUCCEEDED(get_slot(vm, "index", i, index))) { if (i>=0 && (uint32)iget_input().get_count()) { const ware_production_t& in = fab->get_input()[i]; const factory_supplier_desc_t* desc = fab->get_desc()->get_supplier(i); // sanity check if (desc && desc->get_input_type() == in.get_typ()) { return desc; } } } sq_raise_error(vm, "No input slot [%d] in factory at (%s)", i, fab->get_pos().get_str()); return NULL; } const factory_product_desc_t* param::get(HSQUIRRELVM vm, SQInteger index) { fabrik_t* fab = param::get(vm, index); if (fab == NULL) { return NULL; } // obtain index into wareproduction_t arrays SQInteger i = -1; if (SQ_SUCCEEDED(get_slot(vm, "index", i, index))) { i -= fab->get_input().get_count(); if (i>=0 && (uint32)iget_output().get_count()) { const ware_production_t& out = fab->get_output()[i]; const factory_product_desc_t* desc = fab->get_desc()->get_product(i); // sanity check if (desc && desc->get_output_type() == out.get_typ()) { return desc; } } } sq_raise_error(vm, "No output slot [%d] in factory at (%s)", i, fab->get_pos().get_str()); return NULL; } player_t* param::get(HSQUIRRELVM vm, SQInteger index) { uint8 plnr = 0; get_slot(vm, "nr", plnr, index); if(plnr < 15) { return welt->get_player(plnr); } else { sq_raise_error(vm, "Invalid player index %d", plnr); return NULL; } } SQInteger param::push(HSQUIRRELVM vm, player_t* const& player) { return push_instance(vm, "player_x", player ? player->get_player_nr() : 16); } player_t* get_my_player(HSQUIRRELVM vm) { sq_pushregistrytable(vm); uint8 player_nr = PLAYER_UNOWNED; player_t *pl = NULL; if (SQ_SUCCEEDED(get_slot(vm, "my_player_nr", player_nr)) && player_nr < 15) { pl = welt->get_player(player_nr); } sq_poptop(vm); return pl; } const haltestelle_t* param::get(HSQUIRRELVM vm, SQInteger index) { halthandle_t halt = param::get(vm, index); if (!halt.is_bound()) { sq_raise_error(vm, "Invalid halt id %d", halt.get_id()); } return halt.get_rep(); } planquadrat_t* param::get(HSQUIRRELVM vm, SQInteger index) { koord pos = param::get(vm, index); planquadrat_t *plan = welt->access(pos); if (plan==NULL) { sq_raise_error(vm, "Coordinate out of range (%s)", pos.get_str()); } return plan; } grund_t* param::get(HSQUIRRELVM vm, SQInteger index) { koord3d pos = param::get(vm, index); grund_t *gr = welt->lookup(pos); if (gr==NULL) { gr = welt->lookup_kartenboden(pos.get_2d()); } if (gr==NULL) { sq_raise_error(vm, "Coordinate out of range (%s)", pos.get_str()); } return gr; } SQInteger param::push(HSQUIRRELVM vm, grund_t* const& v) { if (v) { koord k = v->get_pos().get_2d(); // transform coordinates coordinate_transform_t::koord_w2sq(k); return push_instance(vm, "tile_x", k.x, k.y, v->get_pos().z); } else { sq_pushnull(vm); return 1; } } scenario_t* param::get(HSQUIRRELVM, SQInteger) { return welt->get_scenario(); } SQInteger param::push(HSQUIRRELVM vm, schedule_entry_t const& v) { return push_instance(vm, "schedule_entry_x", v.pos, v.minimum_loading, v.waiting_time); } SQInteger param::push(HSQUIRRELVM vm, schedule_t* const& v) { return param::push(vm, v); } SQInteger param::push(HSQUIRRELVM vm, const schedule_t* const& v) { if (v) { return push_instance(vm, "schedule_x", v->get_waytype(), v->entries); } else { sq_pushnull(vm); return 1; } } settings_t* param::get(HSQUIRRELVM, SQInteger) { return &welt->get_settings(); } simline_t* param::get(HSQUIRRELVM vm, SQInteger index) { linehandle_t line = param::get(vm, index); if (!line.is_bound()) { sq_raise_error(vm, "Invalid line id %d", line.get_id()); } return line.get_rep(); } stadt_t* param::get(HSQUIRRELVM vm, SQInteger index) { koord pos = param::get(vm, index); return welt->find_nearest_city(pos); } SQInteger param::push(HSQUIRRELVM vm, stadt_t* const& v) { if (v) { koord k = v->get_pos(); // transform coordinates coordinate_transform_t::koord_w2sq(k); return push_instance(vm, "city_x", k.x, k.y); } else { sq_pushnull(vm); return 1; } } karte_t* param::get(HSQUIRRELVM, SQInteger) { return welt; } SQInteger param::push(HSQUIRRELVM vm, karte_t* const&) { sq_pushnull(vm); return 1; } tool_t* param::get(HSQUIRRELVM vm, SQInteger index) { my_tool_t *mtool = get_attached_instance(vm, index, param::tag()); if (mtool) { return mtool->tool; } return NULL; } }; simutrans-124.5/src/simutrans/script/api_param.h000066400000000000000000000404471520675460500220170ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_API_PARAM_H #define SCRIPT_API_PARAM_H /** @file api_param.h templates for transfer of function call parameters */ #include "../../squirrel/squirrel.h" #include "../obj/simobj.h" #include "../simtypes.h" #include "../tpl/quickstone_tpl.h" #include "../utils/cbuffer.h" #include "../gui/gui_theme.h" class baum_t; class bruecke_t; class convoi_t; class fabrik_t; class factory_supplier_desc_t; class factory_product_desc_t; class field_t; class gebaeude_t; class grund_t; class haltestelle_t; class karte_t; class karte_ptr_t; class koord; class koord3d; class label_t; class leitung_t; class loadsave_t; struct schedule_entry_t; struct my_ribi_t; struct my_slope_t; struct my_koord3d; class planquadrat_t; class plainstring; class scenario_t; class schedule_t; class settings_t; class simline_t; class player_t; class stadt_t; class tunnel_t; class tool_t; class ware_production_t; class weg_t; class wayobj_t; class way_builder_t; /** * @namespace script_api The namespace contains all functions necessary to communicate * between simutrans and squirrel: transfer parameters, export classes and functions. * * @brief API simutrans <-> squirrel */ namespace script_api { /// pointer to the world extern karte_ptr_t welt; // forward declaration struct mytime_t; struct mytime_ticks_t; struct mytool_data_t; /** * Templated interface to transfer variables from / to squirrel. */ template struct param { /** * Gets parameters for calls to c++ functions from stack. * @param index on stack * @return value of parameter */ #ifdef DOXYGEN static T get(HSQUIRRELVM vm, SQInteger index); #endif /** * Pushes parameters for calls to squirrel functions on stack. * @return positive value for success, negative for failure */ #ifdef DOXYGEN static SQInteger push(HSQUIRRELVM vm, T const& v); #endif /// typemask: this squirrel type needs to be provided when calling c++ function static const char* typemask() { return "."; } /// squirrel_type corresponding to the c++ type/class static const char* squirrel_type() { return "any_x"; } }; /** * Create slots in table/class on the stack: * it has the same effect as 'table.name <- value'. * * @tparam T type of the new value * @param name name of the slot to be created * @param value value to be set * @param static_ true if this should be a static class member * @param index of table/instance/etc on the stack * @returns positive value on success, negative on failure */ template SQInteger create_slot(HSQUIRRELVM vm, const char* name, T const& value, bool static_ = false, SQInteger index = -1) { sq_pushstring(vm, name, -1); if (SQ_SUCCEEDED(param::push(vm, value))) { SQInteger new_index = index > 0 ? index : index-2; return sq_newslot(vm, new_index, static_); } else { sq_pop(vm, 1); /* pop name */ return SQ_ERROR; } } /** * Sets value to existing variable in table, instance etc at index @p index. * @tparam T type of the new value * @param name name of the slot to be created * @param value value to be set * @param index of table/instance/etc on the stack * @returns positive value on success, negative on failure */ template SQInteger set_slot(HSQUIRRELVM vm, const char* name, T const& value, SQInteger index = -1) { sq_pushstring(vm, name, -1); if (SQ_SUCCEEDED(param::push(vm, value))) { SQInteger new_index = index > 0 ? index : index-2; return sq_set(vm, new_index); } else { sq_pop(vm, 1); /* pop name */ return SQ_ERROR; } } /** * Gets value to existing variable in table, instance etc at index @p index. * @tparam T type of the new value * @param name name of the slot to be created * @param value will be set upon success * @param index of table/instance/etc on the stack * @returns positive value on success, negative on failure */ template SQInteger get_slot(HSQUIRRELVM vm, const char* name, T& value, SQInteger index = -1) { sq_pushstring(vm, name, -1); SQInteger new_index = index > 0 ? index : index-1; if (SQ_SUCCEEDED(sq_get(vm, new_index))) { value = param::get(vm, -1); sq_pop(vm, 1); return SQ_OK; } return SQ_ERROR; } /** * partial specialization for 'const T*' types */ template struct param { /** * Gets parameters for calls to c++ functions from stack. * * @param vm * @param index on stack * @return value of parameter */ static const T* get(HSQUIRRELVM vm, SQInteger index) { return param::get(vm, index); } /** * Pushes parameters for calls to squirrel functions on stack. * * @param vm * @return positive value for success, negative for failure */ static SQInteger push(HSQUIRRELVM vm, const T* const& v) { return param::push(vm, v); } /// typemask: this squirrel type needs to be provided when calling c++ function static const char* typemask() { return param::typemask(); } /// squirrel_type corresponding to the c++ type/class static const char* squirrel_type() { return param::squirrel_type(); } }; /** * partial specialization for 'const T' types */ template struct param { /** * Gets parameters for calls to c++ functions from stack. * * @param vm * @param index on stack * @return value of parameter */ static const T get(HSQUIRRELVM vm, SQInteger index) { return param::get(vm, index); } /** * Pushes parameters for calls to squirrel functions on stack. * @return positive value for success, negative for failure */ static SQInteger push(HSQUIRRELVM vm, T const& v) { return param::push(vm, v); } /// typemask: this squirrel type needs to be provided when calling c++ function static const char* typemask() { return param::typemask(); } /// squirrel_type corresponding to the c++ type/class static const char* squirrel_type() { return param::squirrel_type(); } }; /** * partial specialization for 'const T&' types */ template struct param { /** * Pushes parameters for calls to squirrel functions on stack. * @return positive value for success, negative for failure */ static SQInteger push(HSQUIRRELVM vm, const T& v) { return param::push(vm, v); } /// typemask: this squirrel type needs to be provided when calling c++ function static const char* typemask() { return param::typemask(); } /// squirrel_type corresponding to the c++ type/class static const char* squirrel_type() { return param::squirrel_type(); } }; /** * partial specialization for container types */ template< template class vector, class T> struct param< vector > { /** * Pushes parameters for calls to squirrel functions on stack. * @return positive value for success, negative for failure */ static SQInteger push(HSQUIRRELVM vm, vector const& v) { sq_newarray(vm, 0); for(auto const&i : v) { if (SQ_SUCCEEDED(param::push(vm, i))) { sq_arrayappend(vm, -2); } else { // error sq_poptop(vm); return SQ_ERROR; } } return 1; } /// squirrel_type corresponding to the c++ type/class static const char* squirrel_type() { static cbuffer_t buf; buf.clear(); buf.printf("array<%s>", param::squirrel_type() ); return buf; } }; /** * partial specialization for *handle_t types */ // declared here, implementation in api_class.h, // which has to be included if necessary template struct param< quickstone_tpl >; /** * partial specialization for function pointers, * used in api_function.h */ template struct param { // return type of return value static const char* squirrel_type() { return param::squirrel_type(); } static const char* typemask() { return param::typemask(); } }; template<> struct param { static const char* squirrel_type() { return "void"; } static const char* typemask() { return ""; } }; #define declare_types(mask, sqtype) \ static const char* typemask() { return mask; } \ static const char* squirrel_type() \ { \ return sqtype; \ } /// macro to declare specialized param template #define declare_specialized_param(T, mask, sqtype) \ template<> struct param { \ static T get(HSQUIRRELVM vm, SQInteger index); \ static SQInteger push(HSQUIRRELVM vm, T const& v);\ static void* tag(); \ declare_types(mask, sqtype); \ }; /// macro to only define typemask for specialized param template /// if only 'const class*' is defined then parameter mask must be defined for 'class *' too. #define declare_param_mask(T, mask, sqtype) \ template<> struct param { \ declare_types(mask, sqtype) \ }; /// macro to declare fake types /// for documentation purposes #define declare_fake_param(T, sqtype) \ class T { public: T() {}; }; \ template<> struct param { \ static T get(HSQUIRRELVM, SQInteger) { return T(); } \ declare_types(".", sqtype); \ }; // macro to declare enums #define declare_enum_param(T, inttype, sqtype) \ template<> struct param { \ static T get(HSQUIRRELVM vm, SQInteger index) { return (T)param::get(vm, index); } \ static SQInteger push(HSQUIRRELVM vm, T const& v) { return param::push(vm, v); } \ declare_types("i", sqtype); \ }; // no typemask, as we call to_bool declare_specialized_param(bool, ".", "bool"); declare_specialized_param(uint8, "i", "integer"); declare_specialized_param(sint8, "i", "integer"); declare_specialized_param(uint16, "i", "integer"); declare_specialized_param(sint16, "i", "integer"); declare_specialized_param(uint32, "i", "integer"); declare_specialized_param(sint32, "i", "integer"); declare_specialized_param(uint64, "i", "integer"); declare_specialized_param(sint64, "i", "integer"); declare_enum_param(waytype_t, sint16, "way_types"); declare_enum_param(systemtype_t, uint8, "way_system_types"); declare_enum_param(obj_t::typ, uint8, "map_objects"); declare_enum_param(climate, uint8, "climates"); declare_enum_param(gadget_t, uint8, "gadgets"); declare_specialized_param(my_ribi_t, "i", "dir"); declare_specialized_param(my_slope_t, "i", "slope"); declare_specialized_param(double, "i|f", "float"); declare_specialized_param(const char*, ".", "string"); // no string typemask, as we call to_string declare_specialized_param(plainstring, ".", "string"); declare_specialized_param(koord, "t|x|y", "coord"); declare_specialized_param(koord3d, "t|x|y", "coord3d"); declare_specialized_param(my_koord3d, "t|x|y", "coord3d"); declare_specialized_param(convoi_t*, "t|x|y", "convoy_x"); declare_specialized_param(fabrik_t*, "t|x|y", "factory_x"); declare_specialized_param(const fabrik_t*, "t|x|y", "factory_x"); declare_specialized_param(grund_t*, "t|x|y", "tile_x"); declare_specialized_param(const haltestelle_t*, "t|x|y", "halt_x"); declare_param_mask(haltestelle_t*, "t|x|y", "halt_x"); declare_specialized_param(karte_t*, ".", "world"); declare_specialized_param(planquadrat_t*, "t|x|y", "square_x"); declare_specialized_param(settings_t*, "t|x|y", "settings"); declare_specialized_param(schedule_t*, "t|x|y", "schedule_x"); declare_specialized_param(const schedule_t*, "t|x|y", "schedule_x"); declare_specialized_param(schedule_entry_t, "t|x|y", "schedule_entry_x"); declare_specialized_param(mytime_t, "i|t|x|y", "time_x"); declare_specialized_param(mytime_ticks_t, "i|t|x|y", "time_ticks_x"); declare_specialized_param(mytool_data_t, "i|t|x|y", "tool_data_x"); declare_specialized_param(scenario_t*, "t|x|y", ""); declare_specialized_param(simline_t*, "t|x|y", "line_x"); declare_specialized_param(player_t*, "t|x|y", "player_x"); declare_specialized_param(stadt_t*, "t|x|y", "city_x"); declare_specialized_param(const ware_production_t*, "t|x|y", "factory_production_x"); declare_specialized_param(const factory_supplier_desc_t*, "t|x|y", "factory_production_x"); declare_specialized_param(const factory_product_desc_t*, "t|x|y", "factory_production_x"); declare_param_mask(ware_production_t*, "t|x|y", "factory_production_x"); declare_specialized_param(tool_t*, "x", "command_x"); declare_specialized_param(way_builder_t*, "t|x|y", "way_planner_x"); // export of obj_t derived classes in api/map_objects.cc declare_specialized_param(obj_t*, "t|x|y", "map_object_x"); declare_specialized_param(baum_t*, "t|x|y", "tree_x"); declare_specialized_param(gebaeude_t*, "t|x|y", "building_x"); declare_specialized_param(label_t*, "t|x|y", "label_x"); declare_specialized_param(leitung_t*, "t|x|y", "powerline_x"); declare_specialized_param(weg_t*, "t|x|y", "way_x"); declare_specialized_param(field_t*, "t|x|y", "field_x"); declare_specialized_param(wayobj_t*, "t|x|y", "wayobj_x"); declare_specialized_param(bruecke_t*, "t|x|y", "bridge_x"); declare_specialized_param(tunnel_t*, "t|x|y", "tunnel_x"); /** * Returns the player associated to the script * (or NULL for scenarios) */ player_t* get_my_player(HSQUIRRELVM vm); // trivial specialization for zero arguments inline SQInteger push_param(HSQUIRRELVM) { return 0; } /** * Pushes (recursively) all arguments on the stack. * First argument is pushed first. * @returns number of arguments (or SQ_ERROR) */ template SQInteger push_param(HSQUIRRELVM vm, const A1 & a1, const As &... as) { // push first parameter if (SQ_SUCCEEDED(script_api::param::push(vm, a1))) { // push the other parameters int res = push_param(vm, as...); if (SQ_SUCCEEDED(res)) { return res+1; } else { // error: pop our parameter sq_poptop(vm); } } return -1; } /** * Templated interface to declare free variables for * c++ function calls */ template struct freevariable : freevariable { using base = freevariable; A1 arg1; freevariable(const A1 & a1, const As &... as) : base(as...), arg1(a1) {} /** * Pushes the free variables. Lifo. * @returns number of pushed parameters */ SQInteger push(HSQUIRRELVM vm) const { int res = base::push(vm); if (SQ_SUCCEEDED(res)) { // first parameter is pushed last if (SQ_SUCCEEDED(script_api::param::push(vm, arg1))) { return res+1; } else { sq_pop(vm, res); // pop other parameters in case of failure } } return -1; } }; template struct freevariable { A1 arg1; freevariable(const A1 & a1) : arg1(a1) {} /** * Pushes the free variables * @returns number of pushed parameters */ SQInteger push(HSQUIRRELVM vm) const { return push_param(vm, arg1); } }; /** * Static class to handle the translation of world coordinates (which are sensible to rotation) * to script coordinates (that are independent of rotation). */ class coordinate_transform_t { private: /// Stores how many times initial map was rotated. /// Scripts do not take care of rotated maps. /// Coordinates will be translated between in-game coordinates and script coordinates. /// First v.m. to be started sets this value static uint8 rotation; public: /// called if a new world is initialized static void new_world() { rotation=4; /*invalid*/ } /// inits rotation from karte_t::settings static void initialize(); /// keep track of rotation static void rotate90() { if (rotation<4) rotation = (rotation+1)&3; } /// read/save rotation to stay consistent after saving & loading static void rdwr(loadsave_t*); /** * rotate actual world coordinates back, * coordinates after transform are like in the * scenario's original savegame */ static void koord_w2sq(koord &); /** * rotate original coordinates to actual world coordinates */ static void koord_sq2w(koord &); /** * rotate actual world coordinates direction to original direction */ static void ribi_w2sq(ribi_t::ribi &r); /** * rotate original direction to actual world coordinates direction */ static void ribi_sq2w(ribi_t::ribi &r); /** * rotate actual slope to original slope */ static void slope_w2sq(slope_t::type &s); /** * rotate original slope to actual slope */ static void slope_sq2w(slope_t::type &s); static uint8 get_rotation() { return rotation; } }; /// called by karte_t directly void rotate90(); /// called by karte_t directly void new_world(); }; // end of namespace #endif simutrans-124.5/src/simutrans/script/dynamic_string.cc000066400000000000000000000160721520675460500232330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "dynamic_string.h" #include "script.h" #include "../sys/simsys.h" #include "../world/simworld.h" #include "../network/network.h" #include "../network/network_cmd_scenario.h" #include "../dataobj/environment.h" #include "../dataobj/loadsave.h" #include "../player/simplay.h" #include "../tpl/plainstringhashtable_tpl.h" #include "../utils/cbuffer.h" #include "../utils/simstring.h" uint32 dynamic_string::CACHE_TIME = 1000; // 10s struct cached_string_t { plainstring result; uint32 time; dynamic_string *listener; cached_string_t(const char* str, uint32 t, dynamic_string *l) : result(str), time(t), listener(l) {} cached_string_t(loadsave_t* file) { time = dr_time() - 10000; listener = NULL; rdwr(file); } void rdwr(loadsave_t* file) { file->rdwr_str(result); } }; static plainstringhashtable_tpl cached_results; cached_string_t* get_cached_result(const char* function, uint32 cache_time) { cached_string_t *entry = cached_results.get(function); if (entry) { if (dr_time() - entry->time > cache_time) { entry = NULL; } } return entry; } dynamic_string::~dynamic_string() { for(auto &iter : cached_results) { if (iter.value->listener == this) { iter.value->listener = NULL; } } } void dynamic_string::init(script_vm_t *script) { while(!cached_results.empty()) { delete cached_results.remove_first(); } script->register_callback(&dynamic_string::record_result, "dynamicstring_record_result"); } void dynamic_string::rdwr_cache(loadsave_t *file) { uint32 count = cached_results.get_count(); file->rdwr_long(count); if (file->is_loading()) { // clear list while(!cached_results.empty()) { delete cached_results.remove_first(); } for(uint32 i=0; irdwr_str(key); cached_results.set(key, new cached_string_t(file)); } } else { for(auto &iter : cached_results) { file->rdwr_str(iter.key); iter.value->rdwr(file); } } } void dynamic_string::update(script_vm_t *script, player_t *player, bool force_update) { cbuffer_t buf; buf.printf("%s(%d)", method, player ? player->get_player_nr() : PLAYER_UNOWNED); const char* function = (const char*)buf; plainstring s; if (script) { cached_string_t *entry = NULL; if (!force_update) { entry = get_cached_result(function, CACHE_TIME); } if (entry) { // valid cache entry s = entry->result.c_str(); } else { script->prepare_callback("dynamicstring_record_result", 2, (const char*)buf, (const char*)""); // call script; if QUEUE, it may continue to run another script in the background which alters then the map const char* err = script->call_function(script_vm_t::QUEUE, method, s, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED)); if (!script_vm_t::is_call_suspended(err)) { if ( s != (const char*)str) { changed = true; str = s; } } else { // activate listener cached_string_t *entry = cached_results.get((const char*)buf); if (entry == NULL) { cached_results.set((const char*)buf, new cached_string_t(NULL, dr_time(), this)); } else { entry->listener = this; // return cached value for now; will be changed after callback returns s = entry->result.c_str(); } } script->clear_pending_callback(); // .. and store result record_result(function, s); } } else { if (env_t::networkmode && !env_t::server) { s = dynamic_string::fetch_result( function, NULL, this, force_update); if ( s == NULL) { s = "Waiting for server response..."; } } } if ( s != (const char*)str) { str = s; changed = true; } } const char* dynamic_string::fetch_result(const char* function, script_vm_t *script, dynamic_string *listener, bool force_update) { //dbg->warning("dynamic_string::fetch_result", "function = '%s'", function); cached_string_t *entry = cached_results.get(function); bool const needs_update = entry == NULL || force_update || (dr_time() - entry->time > CACHE_TIME); if (needs_update) { if (script) { // suspended calls have to be caught by script callbacks plainstring str; if(call_script(function, script, str)) { record_result(function, str); } } else { if (entry == NULL) { cached_string_t *old = cached_results.set(function, new cached_string_t(NULL, dr_time(), listener)); assert(old == NULL); (void)old; } // send request nwc_scenario_t *nwc = new nwc_scenario_t(); nwc->function = function; nwc->what = nwc_scenario_t::CALL_SCRIPT_ANSWER; network_send_server(nwc); } // entry maybe invalid now, fetch again entry = cached_results.get(function); } return entry ? entry->result.c_str() : NULL; } bool dynamic_string::record_result(const char* function, plainstring result) { cached_string_t *new_entry = new cached_string_t(result, dr_time(), NULL); cached_string_t *entry = cached_results.set(function, new_entry); if (entry == NULL) { return false; } if (dynamic_string *dyn = entry->listener) { new_entry->listener = dyn; if (dyn->str != (const char*) result) { dyn->str = result; dyn->changed = true; } } delete entry; return true; } /// Struct for checking number of parameters struct method_param_t { const char* name; int nparam; }; /// Static table with script methods that can be called static const method_param_t scenario_methods[] = { { "is_scenario_completed", 1 }, { "get_info_text", 1 }, { "get_goal_text", 1 }, { "get_rule_text", 1 }, { "get_result_text", 1 }, { "get_about_text", 1 }, { "get_short_description", 1} }; void get_scenario_completion(plainstring &res, int player_nr) { cbuffer_t buf; sint32 percent = script_api::welt->get_scenario()->get_completion(player_nr); buf.printf("%d", percent); res = (const char*)buf; } bool dynamic_string::call_script(const char* function, script_vm_t* script, plainstring& str) { int nparams = -1; size_t n = strcspn(function, "("); char *method = new char[n+1]; tstrncpy(method, function, n+1); // check method name for(uint i=0; i< lengthof(scenario_methods); i++) { if (strcmp(method, scenario_methods[i].name)==0) { nparams = scenario_methods[i].nparam; break; } } if (nparams == -1) { // unknown method delete [] method; return false; } // scan for params int params[] = {0, 0, 0, 0}; assert( nparams+1 < (int)lengthof(params) ); const char* p = function+n; if (*p) p++; bool ok = true; for(int i=0; icall_function(script_vm_t::QUEUE, method, str); break; case 1: if ( strcmp(method, "is_scenario_completed")==0 ) { get_scenario_completion(str, params[0]); } else { err = script->call_function(script_vm_t::QUEUE, method, str, params[0]); } break; default: assert(0); } if (script_vm_t::is_call_suspended(err)) { ok = false; } } // cleanup delete [] method; return ok; } simutrans-124.5/src/simutrans/script/dynamic_string.h000066400000000000000000000055721520675460500231000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_DYNAMIC_STRING_H #define SCRIPT_DYNAMIC_STRING_H /** @file dynamic_string.h strings that can be asynchronously changed by scripts */ #include "../utils/plainstring.h" class script_vm_t; class player_t; class loadsave_t; /** * Class of strings to hold result of text-returning script functions. * * Can communicate with server to update scenario text on clients, which * do not know the script. Used in scenario_t and scenario_info_t. * * Results will be cached, frequent calls to update() will only update * the text if some time threshold is exceeded. */ class dynamic_string { private: /// holds result of script function plainstring str; /// true if text changed since last call to clear_changed() bool changed; /// script method to be called to retrieve text const char* method; public: // time between updates to strings static uint32 CACHE_TIME; dynamic_string(const char *method_) : str(NULL), changed(false), method(method_) { } ~dynamic_string(); /** * Update the string, calls fetch_result(). * * @param script the scripting engine, if script == NULL then this needs server communication * @param player * @param force_update will force to update even if cache is still valid */ void update(script_vm_t *script, player_t *player, bool force_update=false); operator const plainstring& () const { return str; } operator const char* () const { return (const char*)str; } bool has_changed() const { return changed; } void clear_changed() { changed = false; } /** * At client: fetch result from server. * At server: call script to get immediate result. * * Results will be stored into a hashtable, fetch requests will be send only if * some time threshold is exceeded (or force_update is true). * * @param function script function incl arguments to be called * @param script * @param listener will be updated should the result change in future * @param force_update force an update, do not use cached string * @returns string or NULL if nothing available (yet) */ static const char* fetch_result(const char* function, script_vm_t *script, dynamic_string *listener=NULL, bool force_update=false); /** * Clear internal cache. Registers callback method. */ static void init(script_vm_t *script); /** * Cache result of script at server, * immediately update the listening dynamic_string. * @returns dummy value */ static bool record_result(const char* function, plainstring result); static void rdwr_cache(loadsave_t *file); private: /** * Calls a script * * @param function is the full function call including integer parameters * @param script * @param result * @returns true if call was successfull */ static bool call_script(const char* function, script_vm_t* script, plainstring& result); }; #endif simutrans-124.5/src/simutrans/script/export_objs.cc000066400000000000000000000016201520675460500225500ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "export_objs.h" #include #include "api_function.h" #include "api_class.h" #include "api/api.h" void register_export_function(HSQUIRRELVM vm, bool scenario) { script_api::start_squirrel_type_logging(scenario ? "scenario" : "ai"); sq_pushroottable(vm); export_city(vm); export_control(vm); export_convoy(vm); export_factory(vm); export_goods_desc(vm); export_gui(vm, scenario); export_halt(vm); export_line(vm); export_map_objects(vm); export_player(vm, scenario); if (scenario) { export_scenario(vm); } export_schedule(vm); export_settings(vm); export_simple(vm); export_string_methods(vm); export_tiles(vm); export_world(vm, scenario); export_pathfinding(vm); export_commands(vm); sq_pop(vm, 1); // root table script_api::end_squirrel_type_logging(); } simutrans-124.5/src/simutrans/script/export_objs.h000066400000000000000000000006221520675460500224130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_EXPORT_OBJS_H #define SCRIPT_EXPORT_OBJS_H #include "../../squirrel/squirrel.h" /** * Registers the complete export interface. * * @param vm * @param scenario true if exporting is for scenario scripting */ void register_export_function(HSQUIRRELVM vm, bool scenario); #endif simutrans-124.5/src/simutrans/script/script.cc000066400000000000000000000521561520675460500215300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "script.h" #include #include #include "../../squirrel/sqstdaux.h" // for error handlers #include "../../squirrel/sqstdio.h" // for loadfile #include "../../squirrel/sqstdstring.h" // export for scripts #include "../../squirrel/sqstdmath.h" // export for scripts #include "../../squirrel/sqstdsystem.h" // export for scripts #include "../../squirrel/sq_extensions.h" // for sq_call_restricted #include "../utils/log.h" #include "../tpl/inthashtable_tpl.h" #include "../tpl/vector_tpl.h" // for error popups #include "../gui/help_frame.h" #include "../gui/simwin.h" #include "../utils/cbuffer.h" #include "../utils/plainstring.h" namespace script_api { bool pause_game(); // api_control.cc } // debug: store stack pointer #define BEGIN_STACK_WATCH(v) int stack_top = sq_gettop(v); // debug: compare stack pointer with expected stack pointer, raise warning if failure #define END_STACK_WATCH(v, delta) if ( (stack_top+(delta)) != sq_gettop(v)) { dbg->warning( __FUNCTION__, "(%d) stack in %d expected %d out %d", __LINE__,stack_top,stack_top+(delta),sq_gettop(v)); } void script_vm_t::printfunc(HSQUIRRELVM vm, const SQChar *s, ...) { va_list vl; va_start(vl, s); if (script_vm_t *script = (script_vm_t*)sq_getforeignptr(vm)) { script->log->vmessage("Script", "Print", s, vl); } va_end(vl); } void script_vm_t::errorfunc(HSQUIRRELVM vm, const SQChar *s_, ...) { char *s = strdup(s_); char *s_dup = s; // remove these linebreaks while (s && *s=='\n') s++; while (s && s[strlen(s)-1]=='\n') s[strlen(s)-1]=0; // get failed script script_vm_t *script = (script_vm_t*)sq_getforeignptr(vm); if (script) { va_list vl; va_start(vl, s_); script->log->vmessage("Script", "Error", s, vl); va_end(vl); } // only one error message at a time - hopefully // collect into static buffer static cbuffer_t buf; if (strcmp(s, "")==0) { // start of error message buf.clear(); buf.printf("An error occurred within a script!
\n"); } else if (strcmp(s, "
")==0) { // end of error message help_frame_t *win = dynamic_cast(win_get_magic(magic_script_error)); if (win == NULL) { win = new help_frame_t(); create_win( win, w_info, magic_script_error); } win->set_text(buf); win->set_name("Script error occurred"); if (script) { script->set_error(buf); // pause game if (script->pause_on_error) { script_api::pause_game(); } } } else { va_list vl; va_start(vl, s_); buf.vprintf(s, vl); va_end(vl); buf.append("
"); } free(s_dup); } void export_include(HSQUIRRELVM vm, const char* include_path); // api_include.cc // virtual machine script_vm_t::script_vm_t(const char* include_path_, const char* log_name) { pause_on_error = false; vm = sq_open(1024); sqstd_seterrorhandlers(vm); sq_setprintfunc(vm, printfunc, errorfunc); register_vm(vm); log = new log_t(log_name, true, true, true, "script engine started.\n"); // store ptr to us in vm sq_setforeignptr(vm, this); // create thread, and put it into registry-table sq_pushregistrytable(vm); sq_pushstring(vm, "thread", -1); thread = sq_newthread(vm, 1024); sq_newslot(vm, -3, false); register_vm(thread); // create queue array sq_pushstring(vm, "queue", -1); sq_newarray(vm, 0); sq_newslot(vm, -3, false); // create queued-callbacks array sq_pushstring(vm, "queued_callbacks", -1); sq_newarray(vm, 0); sq_newslot(vm, -3, false); // pop registry sq_pop(vm, 1); // store ptr to us in vm sq_setforeignptr(vm, this); sq_setforeignptr(thread, this); error_msg = NULL; include_path = include_path_; // register libraries sq_pushroottable(vm); sqstd_register_stringlib(vm); sqstd_register_mathlib(vm); sqstd_register_systemlib(vm); sq_pop(vm, 1); // export include command export_include(vm, include_path); // initialize coordinate and rotation handling script_api::coordinate_transform_t::initialize(); } script_vm_t::~script_vm_t() { unregister_vm(thread); unregister_vm(vm); // remove from suspended calls list suspended_scripts_t::remove_vm(thread); suspended_scripts_t::remove_vm(vm); // close vm, also closes thread sq_close(vm); delete log; } const char* script_vm_t::call_script(const char* filename, uint32 ops) { // load script if (!SQ_SUCCEEDED(sqstd_loadfile(vm, filename, true))) { return "Reading / compiling script failed"; } // call it sq_pushroottable(vm); if (!SQ_SUCCEEDED(sq_call_restricted(vm, 1, SQFalse, SQTrue, ops))) { sq_pop(vm, 1); // pop script return "Call script failed"; } if (sq_getvmstate(vm) == SQ_VMSTATE_SUSPENDED) { set_error("Calling scriptfile took too long"); return get_error(); } sq_pop(vm, 1); // pop script return NULL; } const char* script_vm_t::eval_string(const char* squirrel_string) { if (squirrel_string == NULL) { return NULL; } HSQUIRRELVM &job = thread; // log string printfunc(job, "String to compile:\n%s\n<<<\n", squirrel_string); // compile string if (!SQ_SUCCEEDED(sq_compilebuffer(job, squirrel_string, strlen(squirrel_string), "userdefinedstringmethod", true))) { set_error("Error compiling string buffer"); return get_error(); } // execute sq_pushroottable(job); // stack: closure, root table (1st param) return intern_finish_call(job, QUEUE, 1, true); } bool script_vm_t::is_call_suspended(const char* err) { return (err != NULL) && ( strcmp(err, "suspended") == 0); } const char* script_vm_t::intern_prepare_call(HSQUIRRELVM &job, call_type_t ct, const char* function) { const char* err = NULL; switch (ct) { case FORCE: case FORCEX: job = vm; // block calls to suspendable functions sq_block_suspend(job, function); break; case TRY: if (sq_getvmstate(thread) == SQ_VMSTATE_SUSPENDED) { return "suspended"; } // fall through case QUEUE: job = thread; break; } dbg->message("script_vm_t::intern_prepare_call", "ct=%d function=%s stack=%d", ct, function, sq_gettop(job)); sq_pushroottable(job); sq_pushstring(job, function, -1); // fetch function from root if (SQ_SUCCEEDED(sq_get(job,-2))) { sq_remove(job, -2); // root table sq_pushroottable(job); } else { err = "Function not found"; sq_poptop(job); // array, root table } return err; // stack: closure, root table (1st param) } const char* script_vm_t::intern_finish_call(HSQUIRRELVM job, call_type_t ct, int nparams, bool retvalue) { BEGIN_STACK_WATCH(job); // stack: closure, nparams*objects const char* err = NULL; // only call the closure if vm is idle (maybe in RUN state) bool suspended = sq_getvmstate(job) != SQ_VMSTATE_IDLE; // check queue, if not empty resume first job in queue if (!suspended && ct != FORCE && ct != FORCEX) { sq_pushregistrytable(job); sq_pushstring(job, "queue", -1); sq_get(job, -2); // stack: registry, queue if (sq_getsize(job, -1) > 0) { suspended = true; } sq_pop(job, 2); } // queue function call? if (suspended && ct == QUEUE) { intern_queue_call(job, nparams, retvalue); err = "suspended"; // stack: clean } if (suspended && ct != FORCE && ct != FORCEX) { intern_resume_call(job); } if (!suspended || ct == FORCE || ct == FORCEX) { // set active callback if call could be suspended if (ct == QUEUE) { intern_make_pending_callback_active(); } // mark as not queued sq_pushregistrytable(job); script_api::create_slot(job, "was_queued", false); sq_poptop(job); END_STACK_WATCH(job,0); err = intern_call_function(job, ct, nparams, retvalue); } return err; } /** * Calls function. If it was a queued call, also calls callbacks. * Stack(job): expects closure, nparam*objects, on exit: return value (or clean stack if failure). * @returns NULL on success, error message or "suspended" otherwise. */ const char* script_vm_t::intern_call_function(HSQUIRRELVM job, call_type_t ct, int nparams, bool retvalue) { BEGIN_STACK_WATCH(job); dbg->message("script_vm_t::intern_call_function", "start: stack=%d nparams=%d ret=%d", sq_gettop(job), nparams, retvalue); const char* err = NULL; uint32 opcodes = ct == FORCEX ? 100000 : 10000; // call the script if (!SQ_SUCCEEDED(sq_call_restricted(job, nparams, retvalue, ct == FORCE || ct == FORCEX, opcodes))) { err = "Call function failed"; retvalue = false; } // call not suspended if (sq_getvmstate(job) != SQ_VMSTATE_SUSPENDED) { // remove closure sq_remove(job, retvalue ? -2 : -1); if (ct == QUEUE && retvalue) { sq_pushregistrytable(job); // notify call backs only if call was queued bool notify = false; script_api::get_slot(job, "was_queued", notify, -1); sq_pop(job, 1); if (notify) { intern_call_callbacks(job); } } END_STACK_WATCH(job, -nparams-1 + retvalue); } else { // call suspended: pop dummy return value if (retvalue) { sq_poptop(job); } // throw error in FORCEd mode if (ct == FORCE || ct == FORCEX) { // not allowed for ct == FORCE sq_raise_error(job, "cannot suspend call to %s", sq_get_suspend_blocker(job)); // wake up to throw error sq_wakeupvm(job, false, retvalue, true /*raise_error*/, true /* throw error*/); retvalue = false; // pop closure and params sq_pop(job, nparams+1); } else { // save retvalue flag, number of parameters sq_pushregistrytable(job); script_api::create_slot(job, "retvalue", retvalue); script_api::create_slot(job, "nparams", nparams); sq_poptop(job); } err = "suspended"; } if (ct == FORCE || ct == FORCEX) { END_STACK_WATCH(job, -nparams-1 + retvalue); sq_block_suspend(job, NULL); } dbg->message("script_vm_t::intern_call_function", "stack=%d err=%s", sq_gettop(job)-retvalue, err); return err; } void script_vm_t::intern_resume_call(HSQUIRRELVM job) { BEGIN_STACK_WATCH(job); // stack: clean // get retvalue flag sq_pushregistrytable(job); bool retvalue = false; script_api::get_slot(job, "retvalue", retvalue, -1); int nparams = 0; script_api::get_slot(job, "nparams", nparams, -1); bool wait = false; script_api::get_slot(job, "wait_external", wait, -1); sq_pop(job, 1); END_STACK_WATCH(job, 0); if (wait) { dbg->message("script_vm_t::intern_resume_call", "waits for external call to be able to proceed"); return; } if (!sq_canresumevm(job)) { // vm waits for return value to suspended call dbg->message("script_vm_t::intern_resume_call", "waiting for return value"); return; } // vm suspended, but not from call to our methods if (nparams < 0) { retvalue = false; } // resume v.m. if (!SQ_SUCCEEDED(sq_resumevm(job, retvalue, 10000))) { retvalue = false; } // if finished, clear stack if (sq_getvmstate(job) != SQ_VMSTATE_SUSPENDED) { if (nparams >=0 ) { BEGIN_STACK_WATCH(job); // remove closure & args for(int i=0; imessage("script_vm_t::intern_resume_call", "in between stack=%d", sq_gettop(job)); END_STACK_WATCH(job, -1-nparams-retvalue); } else { // brute force clean stack sq_settop(job, 0); } // make nparams invalid sq_pushregistrytable(job); script_api::create_slot(job, "nparams", -1); sq_poptop(job); // proceed with next call in queue if (intern_prepare_queued(job, nparams, retvalue)) { const char* err = intern_call_function(job, QUEUE, nparams, retvalue); if (err == NULL && retvalue) { // remove return value: call was queued thus remove return value from stack sq_poptop(job); } } } else { if (retvalue) { sq_poptop(job); } } dbg->message("script_vm_t::intern_resume_call", "stack=%d", sq_gettop(job)); } /** * Stack(job): expects closure, nparams*objects, clean on exit. * Put call into registry.queue, callback into registry.queued_callbacks. * Cleans registry.pending_callback. * @param nparams number of parameter of the queued call * @param retvalue whether the call would return something */ void script_vm_t::intern_queue_call(HSQUIRRELVM job, int nparams, bool retvalue) { BEGIN_STACK_WATCH(job); // stack: closure, nparams*objects // queue call: put closure and parameters in array script_api::param::push(job, retvalue); sq_newarray(job, 0); // to hold parameters for queued calls // stack: closure, nparams*objects, retvalue, array for(int i=nparams+2; i>0; i--) { sq_push(job, -2); sq_arrayappend(job, -2); sq_remove(job, -2); } // stack: array sq_pushregistrytable(job); sq_pushstring(job, "queue", -1); sq_get(job, -2); // stack: array, registry, queue // search queue whether our call is already there sint32 size = sq_getsize(job, -1); bool equal = false; for(sint32 i=0; (imessage("script_vm_t::intern_queue_call", "NOT QUEUED stack=%d", sq_gettop(job)); } sq_pop(job, 3); END_STACK_WATCH(job, -nparams-1); dbg->message("script_vm_t::intern_queue_call", "stack=%d", sq_gettop(job)); // stack: clean } /** * Removes queue(0) and puts it on stack. * Activates callback by intern_make_queued_callback_active(). * Stack(job): on success - closure, nparams*objects, on failure - unchanged. * @param nparams number of parameters of the call on the stack * @param retvalue whether queued call returns something * @returns true if a new queued call is on the stack */ bool script_vm_t::intern_prepare_queued(HSQUIRRELVM job, int &nparams, bool &retvalue) { BEGIN_STACK_WATCH(job); // queued calls sq_pushregistrytable(job); sq_pushstring(job, "queue", -1); sq_get(job, -2); sq_pushinteger(job, 0); if (SQ_SUCCEEDED(sq_get(job, -2))) { // there are suspended jobs // stack: registry, queue, array[ retvalue nparams*objects closure] nparams = -2; while( SQ_SUCCEEDED(sq_arraypop(job, -3-nparams, true))) { nparams++; } retvalue = script_api::param::get(job, -1); sq_poptop(job); intern_make_queued_callback_active(); // stack: registry, queue, array, closure, nparams*objects END_STACK_WATCH(job, nparams+4); // mark this call as queued sq_pushstring(job, "was_queued", -1); script_api::param::push(job, true); sq_createslot(job, -6-nparams); // cleanup sq_remove(job, -2-nparams); sq_arrayremove(job, -2-nparams, 0); sq_remove(job, -2-nparams); sq_remove(job, -2-nparams); // stack: closure, nparams*objects dbg->message("script_vm_t::intern_prepare_queued", "stack=%d nparams=%d ret=%d", sq_gettop(job), nparams, retvalue); END_STACK_WATCH(job, nparams+1); return true; } else { // no suspended jobs: clean stack sq_pop(job, 2); } END_STACK_WATCH(job, 0); return false; } /** * Prepare function call. * Stack(vm): pushes registry, nret, closure, root. */ bool script_vm_t::intern_prepare_pending_callback(const char* function, sint32 nret) { BEGIN_STACK_WATCH(vm); sq_pushregistrytable(vm); script_api::param::push(vm, nret+1); // +1 to account for root table sq_pushstring(vm, function, -1); bool ok = SQ_SUCCEEDED(sq_get(vm, -3)); if (ok) { // stack: registry, nret, closure, root sq_pushroottable(vm); } else { // stack: clear sq_pop(vm, 2); } END_STACK_WATCH(vm, ok ? 4 : 0); return ok; } /** * Sets registry.pending_callback. * Stack(vm): expects registry, nret, closure, nparams*objects; clears upon exit. */ void script_vm_t::intern_store_pending_callback(sint32 nparams) { BEGIN_STACK_WATCH(vm); // stack: registry, nret, closure, nparams*objects // put closure and parameters in array sq_newarray(vm, 0); // stack: registry, nret, closure, nparams*objects, array for(int i=0; ipending_callback ] sq_createslot(vm, -3); } else { // stack: registry, "..", array[] sq_poptop(vm); // delete active_callbacks slot sq_deleteslot(vm, -2, false); } sq_poptop(vm); END_STACK_WATCH(vm,0); } /** * * * Stack(job): return value, unchanged */ void script_vm_t::intern_call_callbacks(HSQUIRRELVM job) { BEGIN_STACK_WATCH(job); sq_pushregistrytable(job); sq_pushstring(job, "active_callbacks", -1); if (SQ_SUCCEEDED( sq_deleteslot(job, -2, true) ) ) { BEGIN_STACK_WATCH(job); if (SQ_SUCCEEDED( sq_arraypop(job, -1, true))) { if (SQ_SUCCEEDED(sq_arraypop(job, -1, true))) { sint32 nret = script_api::param::get(job, -1); sq_poptop(job); BEGIN_STACK_WATCH(job); // stack: retval, registry, active_cb, active_cb[end]=array[ nparams*objects closure] int nparams = -1; while( SQ_SUCCEEDED(sq_arraypop(job, -2-nparams, true))) { nparams++; // replace parameter by return value if (nret > 0 && nret == nparams) { sq_poptop(job); sq_push(job, -4-nparams); } } // stack: retval, registry, active_cb, active_cb[end], closure, nparams*objects sq_call_restricted(job, nparams, false, true, 100); // remove closure and finished callback sq_pop(job, 1); END_STACK_WATCH(job,0); } sq_poptop(job); } END_STACK_WATCH(job,0); sq_poptop(job); } sq_poptop(job); END_STACK_WATCH(job,0); } void script_vm_t::set_my_player(uint8 player_nr) { sq_pushregistrytable(vm); script_api::create_slot(vm, "my_player_nr", player_nr); sq_poptop(vm); } /* -------- management of suspended scripts that wait for return value ----------- */ inthashtable_tpl suspended_scripts_t::suspended_scripts; uint32 suspended_scripts_t::get_unique_key(void* ptr) { uint32 key = (uint32)(size_t)ptr; while (key == 0 || suspended_scripts.get(key)) { key ++; } return key; } void suspended_scripts_t::register_suspended_script(uint32 key, HSQUIRRELVM vm) { suspended_scripts.set(key, vm); // mark vm to wait for external call to allow for wake-up sq_pushregistrytable(vm); bool wait = true; script_api::create_slot(vm, "wait_external", wait); sq_poptop(vm); } HSQUIRRELVM suspended_scripts_t::remove_suspended_script(uint32 key) { return suspended_scripts.remove(key); } void suspended_scripts_t::remove_vm(HSQUIRRELVM vm) { inthashtable_tpl::iterator iter=suspended_scripts.begin(), end=suspended_scripts.end(); for(; iter != end; ) { if ( (*iter).value == vm) { iter = suspended_scripts.erase(iter); } else { ++iter; } } } simutrans-124.5/src/simutrans/script/script.h000066400000000000000000000166571520675460500214000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_SCRIPT_H #define SCRIPT_SCRIPT_H /** @file script.h handles the virtual machine, interface to squirrel */ #include "api_function.h" #include "api_param.h" #include "../simtypes.h" #include "../../squirrel/squirrel.h" #include "../utils/plainstring.h" #include class log_t; template class inthashtable_tpl; void sq_setwakeupretvalue(HSQUIRRELVM v); //sq_extensions /** * Class providing interface to squirrel's virtual machine. * * Logs output to script.log. * Opens error window in case of script errors. */ class script_vm_t { public: script_vm_t(const char* include_path, const char* log_name); ~script_vm_t(); /** * loads file, calls script * @returns error msg (or NULL if succeeded) */ const char* call_script(const char* filename, uint32 ops=100000); /** * compiles and executes given string * @returns error msg (or NULL if succeeded) */ const char* eval_string(const char* squirrel_string); bool uses_vm(HSQUIRRELVM other) const { return vm == other || thread == other; } const HSQUIRRELVM& get_vm() const { return vm; } const char* get_error() const { return error_msg.c_str(); } /** * The script can only act as a certain player. * @param player_nr the number of the player (PLAYER_UNOWNED for scenarios) */ void set_my_player(uint8 player_nr); /// priority of function call enum call_type_t { FORCE, ///< function has to return, raise error if not FORCEX, ///< function has to return, raise error if not, give more opcodes QUEUE, ///< function call can be queued, return value can be propagated by call back TRY ///< function call will not be queued, if virtual machine is suspended just return }; /** * @param err error string returned by call_script * @return whether the call was suspended */ static bool is_call_suspended(const char* err); /** * calls scripted function * @param function function name of squirrel function * @returns error msg (or NULL if succeeded) */ const char* call_function(call_type_t ct, const char* function) { // choose correct vm, push function HSQUIRRELVM job; const char* err = intern_prepare_call(job, ct, function); if (err) { return err; } // now call return intern_finish_call(job, ct, 1, false); } /** * calls scripted function * * @tparam R type of return value * @tparam As types of arguments * @param function function name of squirrel function * @param as any number of arguments passed to squirrel function * @param ret return value of script function is stored here * @returns error msg (or NULL if succeeded), if call was suspended ret is invalid */ template const char* call_function(call_type_t ct, const char* function, R& ret, const As &... as) { // choose correct vm, push function HSQUIRRELVM job; const char* err = intern_prepare_call(job, ct, function); if (err) { return err; } // push parameters int nparam = script_api::push_param(job, as...); if (!SQ_SUCCEEDED(nparam)) { return "error pushing argument"; } // now call err = intern_finish_call(job, ct, nparam+1, true); if (err == NULL) { ret = script_api::param::get(job, -1); sq_poptop(job); } return err; } /** * Registers a c++ function to be available as callback. * A callback is called when a function call got suspended, resumed, and returned something. * @tparam F function signature * @param funcptr function pointer * @param name register callback under this name */ template void register_callback(F funcptr, const char* name) { sq_pushregistrytable(vm); script_api::register_method(vm, funcptr, name); sq_poptop(vm); } /** * Prepares a callback call. * @param function name of function to be called as callback * @param nret the nret-th parameter will be replaced by return value of suspended function. */ template void prepare_callback(const char* function, int nret, const As &... as) { if (intern_prepare_pending_callback(function, nret)) { int nparam = script_api::push_param(vm, as...); assert(SQ_SUCCEEDED(nparam)); // FIXME intern_store_pending_callback(nparam+1); } } /** * Clears pending callback initialized by prepare_callback. */ void clear_pending_callback(); private: /// virtual machine running everything HSQUIRRELVM vm; /// thread in the virtual machine, used to run functions that can be suspended HSQUIRRELVM thread; /// our log file log_t* log; plainstring error_msg; /// path to files to #include plainstring include_path; public: bool pause_on_error; private: /// @{ /// @name Helper functions to call, suspend, queue calls to scripted functions /// prepare function call, used in templated call_function(), sets job to vm that should run function const char* intern_prepare_call(HSQUIRRELVM &job, call_type_t ct, const char* function); /// actually call function, used in templated call_function(), /// does also: resume suspended call, queue current call const char* intern_finish_call(HSQUIRRELVM job, call_type_t ct, int nparams, bool retvalue); /// queues current call static void intern_queue_call(HSQUIRRELVM job, int nparams, bool retvalue); /// resumes a suspended call. calls callbacks. void intern_resume_call(HSQUIRRELVM job); /// calls function. If it was a queued call, also calls callbacks. static const char* intern_call_function(HSQUIRRELVM job, call_type_t ct, int nparams, bool retvalue); /// pops an queued call and puts it on the stack, also activates corresponding callbacks bool intern_prepare_queued(HSQUIRRELVM job, int &nparams, bool &retvalue); /// prepare function call to callback, used in templated prepare_callback() bool intern_prepare_pending_callback(const char* function, sint32 nret); /// saves the callback call (with arguments), used in templated prepare_callback() void intern_store_pending_callback(sint32 nparams); /// pops callback from queued callbacks, and makes it active void intern_make_queued_callback_active(); /// takes pending callback, and makes it active void intern_make_pending_callback_active(); /// calls all active callbacks static void intern_call_callbacks(HSQUIRRELVM job); /// @} /// custom error handler for compile and runtime errors of squirrel scripts static void errorfunc(HSQUIRRELVM vm, const SQChar *s_,...); /// set error message, used in errorhandlers void set_error(const char* error) { error_msg = error; } /// custom print handler static void printfunc(HSQUIRRELVM, const SQChar *s, ...); }; /** * Class to manage all vm's that are suspended and waiting for the return of * a call to a tool. */ class suspended_scripts_t { private: static inthashtable_tpl suspended_scripts; static HSQUIRRELVM remove_suspended_script(uint32 key); public: // generates key from a pointer static uint32 get_unique_key(void* key); static void register_suspended_script(uint32 key, HSQUIRRELVM vm); // remove any reference to given vm static void remove_vm(HSQUIRRELVM vm); template static void tell_return_value(uint32 key, R& ret) { HSQUIRRELVM vm = remove_suspended_script(key); if (vm) { script_api::param::push(vm, ret); sq_setwakeupretvalue(vm); // this vm can be woken up now sq_pushregistrytable(vm); bool wait = false; script_api::create_slot(vm, "wait_external", wait); sq_poptop(vm); } } }; #endif simutrans-124.5/src/simutrans/script/script_loader.cc000066400000000000000000000041041520675460500230440ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "script_loader.h" #include "script.h" #include "api/api.h" #include "export_objs.h" #include "../dataobj/environment.h" #include "../utils/cbuffer.h" bool script_loader_t::load_base_script(script_vm_t *script, const char* base) { cbuffer_t buf; buf.printf("%sscript/%s", env_t::base_dir, base); if (const char* err = script->call_script(buf)) { // should not happen dbg->error("load_base_script", "error [%s] calling %s", err, (const char*)buf); return false; } return true; } script_vm_t* script_loader_t::start_vm(const char* base_file_name, const char* logfile_name, const char* include_path, bool is_scenario) { script_vm_t* script = new script_vm_t(include_path, logfile_name); // load global stuff // constants must be known compile time export_global_constants(script->get_vm()); // load scripting base definitions bool ok = load_base_script(script, "script_base.nut"); // load specific base definitions ok = ok && load_base_script(script, base_file_name); // register api functions if (ok) { register_export_function(script->get_vm(), is_scenario); if (script->get_error()) { dbg->error("start_vm", "error [%s] calling register_export_function", script->get_error()); ok = false; } } if (ok) { return script; } else { delete script; return NULL; } } void script_loader_t::load_compatibility_script(script_vm_t *script) { // check api version plainstring api_version; if (const char* err = script->call_function(script_vm_t::FORCE, "get_api_version", api_version)) { dbg->warning("scenario_t::init", "error [%s] calling get_api_version", err); api_version = "120.1"; } if (api_version != "*") { // load scenario compatibility script if (load_base_script(script, "script_compat.nut")) { plainstring dummy; // call compatibility function if (const char* err = script->call_function(script_vm_t::FORCE, "compat", dummy, api_version) ) { dbg->warning("scenario_t::init", "error [%s] calling compat", err); } } } } simutrans-124.5/src/simutrans/script/script_loader.h000066400000000000000000000014061520675460500227100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_SCRIPT_LOADER_H #define SCRIPT_SCRIPT_LOADER_H class script_vm_t; struct script_loader_t { /** * Starts vm, loads the scripting interface, loads script/[base_file_name]. * @returns vm or NULL in case of error */ static script_vm_t* start_vm(const char* base_file_name, const char* logfile_name, const char* include_path, bool is_scenario); /** * Loads base script files: env_t::base_dir/script/script_base.nut and env_t::base_dir/script/[base]. */ static bool load_base_script(script_vm_t *vm, const char* base); /** * loads necessary compatibility scripts */ static void load_compatibility_script(script_vm_t *script); }; #endif simutrans-124.5/src/simutrans/script/script_tool_manager.cc000066400000000000000000000073451520675460500242570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "script_tool_manager.h" #include "../dataobj/tabfile.h" #include "../simdebug.h" #include "../tool/simtool-scripted.h" #include "../simskin.h" #include "../descriptor/skin_desc.h" #include "../sys/simsys.h" #include "../gui/tool_selector.h" #include "../utils/cbuffer.h" #include "../utils/searchfolder.h" vector_tpl script_tool_manager_t::one_click_script_tools; vector_tpl script_tool_manager_t::two_click_script_tools; bool script_tool_manager_t::check_file( const char *path ) { cbuffer_t buf; buf.printf("%s/tool.nut", path ); if (FILE* const f = dr_fopen(buf, "r")) { fclose(f); return true; } return false; } const scripted_tool_info_t* script_tool_manager_t::get_script_info(const char* path) { scripted_tool_info_t* info = new scripted_tool_info_t(); info->path = path; // read description.tab cbuffer_t buf; buf.printf("%s/description.tab", path); tabfile_t file; if ( file.open(buf) ) { tabfileobj_t contents; file.read( contents ); info->title = translator::translate(contents.get_string("title", path)); info->menu_arg = contents.get_string("menu", ""); info->tooltip = translator::translate(contents.get_string("tooltip", "")); info->cursor_area = contents.get_koord("cursor_area", koord(1,1)); info->cursor_offset = contents.get_koord("cursor_offset", koord(0,0)); if( info->cursor_area.x<1 || info->cursor_area.y<1 ) { dbg->warning("script_tool_manager_t::get_script_info()", "The cursor area of scripted tool %s must have at least 1 tile length. Sanitized to (1,1).", info->title.c_str()); info->cursor_area = koord(1,1); } const char* skin_name = contents.get_string("icon", ""); info->desc = skinverwaltung_t::get_extra(skin_name, strlen(skin_name), skinverwaltung_t::cursor); info->restart = contents.get_int("restart", false); info->is_one_click = !( strcmp(contents.get_string("type", "one_click"), "two_click")==0 ); } else { // no description.tab, use default values info->title = path; } return info; } tool_t* script_tool_manager_t::load_tool(char const* path, tool_t* tool) { if (!check_file(path)) { return tool; } const scripted_tool_info_t* info = get_script_info(path); if (tool) { // reinitialize existing tool exec_script_base_t* esb = dynamic_cast(tool); assert(esb); esb->set_info(info); esb->init_images(tool); } else { // create new tool if (info->is_one_click) { tool = new tool_exec_script_t(info); } else { tool = new tool_exec_two_click_script_t(info); } } return tool; } void script_tool_manager_t::load_scripts(char const* path) { searchfolder_t find; find.search(path, "", searchfolder_t::SF_ONLYDIRS); for(const char* const &name : find) { cbuffer_t fullname; fullname.printf("%s%s",path,name); tool_t *tool = load_tool(fullname); if (tool_exec_script_t* ot = dynamic_cast(tool)) { one_click_script_tools.append(ot); } else if (tool_exec_two_click_script_t* tt = dynamic_cast(tool)) { two_click_script_tools.append(tt); } } } void script_tool_manager_t::fill_menu(tool_selector_t* tool_selector, char const* arg, sint16 /*sound_ok*/) { for(uint32 i=0; iget_menu_arg(), arg)==0 ) { tool_selector->add_tool_selector(tool); } } for(uint32 i=0; iget_menu_arg(), arg)==0 ) { tool_selector->add_tool_selector(tool); } } } simutrans-124.5/src/simutrans/script/script_tool_manager.h000066400000000000000000000027101520675460500241100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCRIPT_SCRIPT_TOOL_MANAGER_H #define SCRIPT_SCRIPT_TOOL_MANAGER_H #include "../tpl/vector_tpl.h" class exec_script_base_t; struct scripted_tool_info_t; class tool_t; class tool_selector_t; class tool_exec_script_t; class tool_exec_two_click_script_t; /** * There's no need to construct an instance since everything is static here. */ class script_tool_manager_t { private: /// All one-click script tools static vector_tpl one_click_script_tools; /// All two-click script tools static vector_tpl two_click_script_tools; public: /// Looks for tool.nut in path static bool check_file(char const* path); /** * Reads description.tab at path. * If no description is found, use default values. * @returns filled info structure. */ static const scripted_tool_info_t* get_script_info(const char* path); /** * Loads tool from path. * If @p tool is not NULL it is reinitialized with the scripted tool. * @returns tool */ static tool_t* load_tool(char const* path, tool_t* tool = NULL); /** * Reads all tools from directory @p path. */ static void load_scripts(char const* path); /** * Fills toolbar with scripted tools. * Select only tools with menu entry equal to @p arg. */ static void fill_menu(tool_selector_t* tool_selector, char const* arg, sint16 sound_ok); }; #endif simutrans-124.5/src/simutrans/scrolltext.h000066400000000000000000000064741520675460500207670ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SCROLLTEXT_H #define SCROLLTEXT_H /* first part is left, second part right side */ "","", "","", "","", "","", "","", "","", "","", "Current Team:","", " Markus Pristovsek","developer", " Roboron", "developer, Steam, Android, flatpack", " Daniel Wachsmuth", "developer", " ceeac", "developer", " TurfIt", "developer", "","", "Additional Programming:","", "","", " Hj. Malthaner","founder, Ironbite", " Volker Meyer","big patches", " Hendrik Siegeln","ex-developer", " Niels Roest","GUI", " Markus Weber","GUI changes", " Owen Rudge","MIDI, patches", " Tomas Kubes","patches", " Rainer Bertram","patches", " Andreas Röver","patches", " Tilman Raatz","patches", " Pasi Ruhanen","BeOS, SDL", " Mat Hounsell","GUI", " Kieron Green","skinned grounds, climates", " z9999","patches", " Gerd Wachsmuth", "patches", " Knightly", "ex-developer", " Markohs", "3D branch, ex-developer", " Christoph Mallon", "ex-developer, SVN", " Frank Penz", "Scripting AI", " Yona-TYT", "Tutorials", " Ranran","GUI-patches, chat", "","", "Graphic Paks:","", "","", " Peter J. Dobrovka","", " Tomas Kubes","pak128", " Vladimir Slavik", "pak128", " Frank Penz","pak.german", " Yoshihiro Shishido","pak128.japan", " Sven Osada","pak96.comic", " The Hood", "pak128.britain", " Flemmbrav", "pak192.comic", " Leartin", "pak192.comic", " Makie", "pak128.german", "","", "and many more", "", "", "", "", "", "Graphics:","", "","", " Flor Wauters","", " Georg 'MHz' Moers","", " Hansjörg Malthaner","", " James Starr","", " Manfred Tomaschitz","", " Oskari 'TPI' Saarekas","", " Patrick Nesbitt","", " Peter J. Dobrovka","", " Rick 'Trikky' McGreal","", " Sergiu Croitor","", " Seth C. Triggs","", " Tomas Kubes","", " Guillermo 'raven' Mateo","", " Alexander Brose","", "","", "and many more","", "","", "","", "Translation:","", "","", " Alberto Beccaria","Italian", " George Dragon","Hungarian", " Hansjörg Malthaner","German", " Marcal Guardiola","Spanish", " Niels Roest","Dutch", " Pasi Ruhanen","Finnish", " Rémi Denis-Courmont","French", " Ferencz Szekely","Hungary & Romanian", " Alex Revo","Russian", " Roman Schwörer","French & German", " Yannis Kaskamanid", "Greek", " Igor Eliezer", "Portuguese", " Joan-Josep Bargues", "Catalan", " Susanna Björverud", "Swedish", " Xavier Paz", "Spanish", " Wim Vreeswijk", "Dutch", " Paul Color", "Swedish", " Ignas Brasiskis", "Lithuanian", "","", "and many more","", "","", "","", "Additional Thanks go to:","", "","", " Bernhard Mecl","", "","the very first Simutrans beta tester", " Bram Schoenmakers","", "","the most critical tester", " Gerhard Hafner","", "","for providing tons of railway information", " Marcus Haupt","", "","for lots of help with tricky problems", " Vern Moorhouse", "", "","for some valuable technical hints", " Isaac Eiland-Hall","", "","hosting simutrans.com", " Dirk Burkholz","", "","hosting german forum until his dead", " Frank Penz", "", "", "Simutranslator, Wiki, and more", " Christoph Mallon", "", "", "long time hosting the SVN", " werniman", "", "", "long time hosting the nightly builds", " makie", "", "", "hosting of the simutranslator", "","", "", "", " And many thanks to all testers and","", " players, and all who supported the","", " project!","", "","", "","", "","", "","", "","", "","", "","", 0 #endif simutrans-124.5/src/simutrans/simachenum.h000066400000000000000000000005241520675460500207030ustar00rootroot00000000000000// IMPORTANT! CHANGE THIS ON NEW ACHIEVEMENTS ADDITION #define NUM_ACHIEVEMENTS 4 // Why a macro for this? Check steam/achievements.cc #define ACHIEVEMENTS \ X(ACH_LOAD_PAK192_COMIC) \ X(ACH_QUERY_DICTACTOR) \ X(ACH_PROD_INK) \ X(ACH_TOOL_REMOVE_BUSY_BRIDGE) #define X(id) id, enum simachievements_enum { ACHIEVEMENTS }; #undef Xsimutrans-124.5/src/simutrans/simachievements.cc000066400000000000000000000046561520675460500221060ustar00rootroot00000000000000#include "simachievements.h" #include "./dataobj/environment.h" #include "./simdebug.h" #include "./simfab.h" #include "./utils/simstring.h" #ifdef STEAM_BUILT #include "../steam/steam.h" #endif void simachievements_t::check_pakset_ach() { std::string pak_name = env_t::pak_name; pak_name.erase(pak_name.length() - 1); if ( STRICMP(pak_name.c_str(), "pak192.comic") == 0 ) { set_achievement(ACH_LOAD_PAK192_COMIC); } } void simachievements_t::check_query_ach(const char* object_name) { std::string pak_name = env_t::pak_name; pak_name.erase(pak_name.length() - 1); if ( (STRICMP(pak_name.c_str(), "pak128") == 0) && strstr(object_name, "rmax_dictator_statue") ) { set_achievement(ACH_QUERY_DICTACTOR); } } /** * Given a factory and a name, checks if the factory has the given name * If so, checks if the provided product meets the production target requirement (given as a percentage of maximum production) * @returns true if the yearly production is greater than the target production */ bool check_yearly_production(fabrik_t* fab, const char* name, uint32 product_index, double target_percent = 0.70) { if ( strcmp(fab->get_name(), name) != 0 || product_index >= fab->get_output().get_count() ) { return false; } sint64 yearly_production = 0; sint32 target_production = fab->get_current_production() * 12 * target_percent; for (int i = 0; i < MAX_MONTH; i++) { yearly_production += convert_goods(fab->get_output()[product_index].get_stats()[i * MAX_FAB_GOODS_STAT + FAB_GOODS_PRODUCED]); } // dbg->message("check_state_ach()", "TARGET PROD: %s", std::to_string(target_production).c_str()); // dbg->message("check_state_ach()", "PROD: %s", std::to_string(yearly_production).c_str()); return yearly_production >= target_production; } void simachievements_t::check_state_ach(karte_t* world) { if ( env_t::networkmode ) return; // We don't allow these kind of achievements in multiplayer games std::string pak_name = env_t::pak_name; pak_name.erase(pak_name.length() - 1); for ( fabrik_t* fab : world->get_fab_list() ) { if ( STRICMP(pak_name.c_str(), "pak192.comic") == 0 ) { if ( check_yearly_production(fab, "Mr. Kraken", 0) ) { set_achievement(ACH_PROD_INK); } } } } void simachievements_t::set_achievement(simachievements_enum ach) { dbg->message("simachievements_t::set_achievement()", "Unlocking achievement %d", ach); #ifdef STEAM_BUILT steam_t::get_instance()->get_achievements()->set_achievement(ach); #endif }simutrans-124.5/src/simutrans/simachievements.h000066400000000000000000000014251520675460500217370ustar00rootroot00000000000000#include "./simachenum.h" #include "./world/simworld.h" /** * Manages the achievement system. This currently only works for Steam achievements. * TODO: It would be cool to have our own achievement system! */ class simachievements_t { private: public: // Check achievements for loading the current pakset static void check_pakset_ach(); // Check achievements for querying a specific object static void check_query_ach(const char* object_name); // Check achievements that depend on game state static void check_state_ach(karte_t* world); // Set the given achievement. No check is made, use after the condition is fullfilled! static void set_achievement(simachievements_enum ach); // TODO: A function to set achievement from script tools (for scenarios, tutorial, etc...) };simutrans-124.5/src/simutrans/simcolor.h000066400000000000000000000162151520675460500204050ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMCOLOR_H #define SIMCOLOR_H #include "simtypes.h" #define LIGHT_COUNT (15) #define SPECIAL_COLOR_COUNT (224) // this is a player color => use different table for conversion #define PLAYER_FLAG (0x800000) #define TRANSPARENT_FLAGS (0x780000) #define TRANSPARENT25_FLAG (0x200000) #define TRANSPARENT50_FLAG (0x400000) #define TRANSPARENT75_FLAG (0x600000) #define OUTLINE_FLAG (0x080000) typedef uint8 palette_index_t; // pixels stored in system type (uint16) typedef unsigned short PIXVAL; // PIXVAL with above flags (eg. transparent) (uint32) typedef unsigned int FLAGGED_PIXVAL; // Menu colours (they don't change between day and night) #define MN_GREY0 229 #define MN_GREY1 230 #define MN_GREY2 231 #define MN_GREY3 232 #define MN_GREY4 233 // fixed colors #define COL_BLACK 240 #define COL_WHITE 215 #define COL_RED 131 #define COL_DARK_RED 128 #define COL_LIGHT_RED 134 #define COL_YELLOW 171 #define COL_DARK_YELLOW 168 #define COL_LIGHT_YELLOW 175 #define COL_LEMON_YELLOW 31 #define COL_BLUE 147 #define COL_DARK_BLUE 144 #define COL_SOFT_BLUE 100 #define COL_LIGHT_BLUE 103 #define COL_GREEN 140 #define COL_DARK_GREEN 136 #define COL_LIGHT_GREEN 143 #define COL_ORANGE 155 #define COL_DARK_ORANGE 153 #define COL_LIGHT_ORANGE 158 #define COL_BRIGHT_ORANGE 133 #define COL_LILAC 221 #define COL_MAGENTA 63 #define COL_PURPLE 76 #define COL_DARK_PURPLE 73 #define COL_LIGHT_PURPLE 79 #define COL_TURQUOISE 53 #define COL_LIGHT_TURQUOISE 55 #define COL_DARK_TURQUOISE 50 #define COL_LIGHT_BROWN 191 #define COL_BROWN 189 #define COL_DARK_BROWN 178 // message colors #define CITY_KI gfx->palette_lookup(209) #define NEW_VEHICLE gfx->palette_lookup(COL_PURPLE) // by niels #define COL_GREY1 208 #define COL_GREY2 210 #define COL_GREY3 212 #define COL_GREY4 11 #define COL_GREY5 213 #define COL_GREY6 15 // Way colours for the map #define COL_ROAD gfx->palette_lookup(COL_GREY1) #define COL_RAIL gfx->palette_lookup(185) #define COL_CANAL gfx->palette_lookup(23) #define COL_MONORAIL gfx->palette_lookup(COL_ORANGE) #define COL_RUNWAY gfx->palette_lookup(28) #define COL_POWERLINE gfx->palette_lookup(COL_LIGHT_TURQUOISE) #define COL_HALT gfx->palette_lookup(COL_RED) #define COL_BUILDING gfx->palette_lookup(COL_GREY3) #define COL_VEHICLE gfx->palette_lookup(COL_YELLOW) // used in many dialogues graphs #define COL_REVENUE 142 #define COL_OPERATION 132 #define COL_MAINTENANCE COL_LIGHT_RED #define COL_TOLL 157 #define COL_POWERLINES 46 #define COL_OPS_PROFIT 87 #define COL_NEW_VEHICLES COL_LIGHT_PURPLE #define COL_CONSTRUCTION 110 #define COL_PROFIT 6 #define COL_TRANSPORTED COL_YELLOW #define COL_MAXSPEED COL_TURQUOISE #define COL_CASH 52 #define COL_VEHICLE_ASSETS COL_MAGENTA #define COL_MARGIN COL_LIGHT_YELLOW #define COL_WEALTH 95 #define COL_CONVOI_COUNT COL_LIGHT_TURQUOISE #define COL_FREE_CAPACITY COL_TOLL #define COL_DISTANCE COL_OPS_PROFIT #define COL_CITIZENS COL_WHITE #define COL_GROWTH 122 #define COL_HAPPY COL_WHITE #define COL_UNHAPPY COL_RED #define COL_NO_ROUTE COL_BLUE #define COL_WAITING COL_YELLOW #define COL_ARRIVED COL_DARK_ORANGE #define COL_DEPARTED COL_DARK_YELLOW #define SYSCOL_TEXT gui_theme_t::gui_color_text #define SYSCOL_TEXT_HIGHLIGHT gui_theme_t::gui_color_text_highlight #define SYSCOL_TEXT_SHADOW gui_theme_t::gui_color_text_shadow #define SYSCOL_TEXT_TITLE gui_theme_t::gui_color_text_title #define SYSCOL_TEXT_STRONG gui_theme_t::gui_color_text_strong #define MONEY_MINUS gui_theme_t::gui_color_text_minus #define MONEY_PLUS gui_theme_t::gui_color_text_plus #define SYSCOL_TEXT_UNUSED gui_theme_t::gui_color_text_unused #define SYSCOL_EDIT_TEXT gui_theme_t::gui_color_edit_text #define SYSCOL_EDIT_TEXT_SELECTED gui_theme_t::gui_color_edit_text_selected #define SYSCOL_EDIT_TEXT_DISABLED gui_theme_t::gui_color_edit_text_disabled #define SYSCOL_EDIT_BACKGROUND_SELECTED gui_theme_t::gui_color_edit_background_selected #define SYSCOL_CURSOR_BEAM gui_theme_t::gui_color_edit_beam #define SYSCOL_CHART_BACKGROUND gui_theme_t::gui_color_chart_background #define SYSCOL_CHART_LINES_ZERO gui_theme_t::gui_color_chart_lines_zero #define SYSCOL_CHART_LINES_ODD gui_theme_t::gui_color_chart_lines_odd #define SYSCOL_CHART_LINES_EVEN gui_theme_t::gui_color_chart_lines_even #define SYSCOL_LIST_TEXT_SELECTED_FOCUS gui_theme_t::gui_color_list_text_selected_focus #define SYSCOL_LIST_TEXT_SELECTED_NOFOCUS gui_theme_t::gui_color_list_text_selected_nofocus #define SYSCOL_LIST_BACKGROUND_SELECTED_F gui_theme_t::gui_color_list_background_selected_f #define SYSCOL_LIST_BACKGROUND_SELECTED_NF gui_theme_t::gui_color_list_background_selected_nf #define SYSCOL_BUTTON_TEXT gui_theme_t::gui_color_button_text #define SYSCOL_BUTTON_TEXT_DISABLED gui_theme_t::gui_color_button_text_disabled #define SYSCOL_BUTTON_TEXT_SELECTED gui_theme_t::gui_color_button_text_selected #define SYSCOL_COLORED_BUTTON_TEXT gui_theme_t::gui_color_colored_button_text #define SYSCOL_COLORED_BUTTON_TEXT_SELECTED gui_theme_t::gui_color_colored_button_text_selected #define SYSCOL_CHECKBOX_TEXT gui_theme_t::gui_color_checkbox_text #define SYSCOL_CHECKBOX_TEXT_DISABLED gui_theme_t::gui_color_checkbox_text_disabled #define SYSCOL_TICKER_BACKGROUND gui_theme_t::gui_color_ticker_background #define SYSCOL_TICKER_DIVIDER gui_theme_t::gui_color_ticker_divider #define SYSCOL_STATUSBAR_TEXT gui_theme_t::gui_color_statusbar_text #define SYSCOL_STATUSBAR_BACKGROUND gui_theme_t::gui_color_statusbar_background #define SYSCOL_STATUSBAR_DIVIDER gui_theme_t::gui_color_statusbar_divider #define SYSCOL_HIGHLIGHT gui_theme_t::gui_highlight_color #define SYSCOL_SHADOW gui_theme_t::gui_shadow_color #define SYSCOL_LOADINGBAR_INNER gui_theme_t::gui_color_loadingbar_inner #define SYSCOL_LOADINGBAR_PROGRESS gui_theme_t::gui_color_loadingbar_progress #define SYSCOL_OBSOLETE gui_theme_t::gui_color_obsolete #define SYSCOL_EMPTY gui_theme_t::gui_color_empty #define SYSCOL_IMAGE_TRANSPARENCY gui_theme_t::gui_color_image_transparency #define SYSCOL_OBJECT_HIGHLIGHT gui_theme_t::gui_color_object_highlight struct rgb888_t { uint8 r, g, b; }; #endif simutrans-124.5/src/simutrans/simconst.h000066400000000000000000000067421520675460500204210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMCONST_H #define SIMCONST_H /* * all defines that can change the compiling */ // color depth (default assumes RGB565), undefine only for rgb555 builds! //#define RGB555 // some clients had no goof IP6 support (or maybe useful for debugging) //#define USE_IP4_ONLY // server can try a lookup of the own name (may get wrong one if more than one IP for this host) //#define LOOKUP_OWN_IP_NAME // number of player #define MAX_PLAYER_COUNT (16) #define PLAYER_UNOWNED (15) /* Flag for non Intel byte order * SET THIS IN YOUR COMPILER COMMAND LINE! */ //#define SIM_BIG_ENDIAN /* crossconnect industry and half heights like openTTD */ //#define OTTD_LIKE /* construct automatic bridges also as active player */ //#define AUTOMATIC_BRIDGES /* construct automatic tunnels also as active player */ //#define AUTOMATIC_TUNNELS /* citycars have a destination; if they could get near, they dissolve */ //#define DESTINATION_CITYCARS /* need to emulate the mouse pointer with a graphic */ //#define USE_SOFTPOINTER /* Use assembly imepmentation of routines if available * Some routines, eg for drawing, can have assembly implementations for certaint platforms that may perform better */ #define USE_ASSEMBLER /* Use low level C/C++ implementations of routines * Some routines, eg for drawing, can have low level C++ implementations that might perform better on certain platforms */ #define LOW_LEVEL // define this for automatically joining stations next to a public stop with it //#define AUTOJOIN_PUBLIC // allow minspeed and private way signs on waterways (imho pointless) //#define ENABLE_WATERWAY_SIGNS // Maximum number of threads #define MAX_THREADS (12) // Use own routines for downloading paks and installing (requires libzip and libcurl) //#define USE_OWN_PAKINSTALL // if defined, if overrides the packing of the structures and allows for 255 ground pre planquadrat_t (default 15) // DO NOT USE THIS UNLESS YOU REALLY NEED IT!!! //#define MAX_PLAN_SIZE 255 /*********************** Useful things for debugging ... ********************/ /* will highlight marked areas and convoi will leave traces */ //#define DEBUG_ROUTES /* shows which tiles are drawn as dings (used in ground/grund.cc) */ //#define SHOW_FORE_GRUND /* shows with block needed update and which not */ //#define DEBUG_FLUSH_BUFFER /* inserts debugging code into freelist_tpl/freelist_iter_tpl to aid with finding memory corruptions */ //#define DEBUG_FREELIST /* define USE_VALGRIND_MEMCHECK to make valgrind aware of the freelist memory pool */ //#define USE_VALGRIND_MEMCHECK /* uses the actual tools from the city to build cityroads. May result in heavy terraforming */ //#define USE_TOWN_ROAD_BUILDER_TOOL /**************************** automatic stuff ********************************/ // inclination types // constants used in tools tool_setslope / tool_restoreslope_t #define ALL_UP_SLOPE (82) #define ALL_DOWN_SLOPE (83) #define RESTORE_SLOPE (84) #define ALL_UP_SLOPE_SINGLE (16) #define ALL_DOWN_SLOPE_SINGLE (17) #define RESTORE_SLOPE_SINGLE (18) // 16 internal pixels per tile, for purposes of object visual offsets. #define OBJECT_OFFSET_STEPS (16) // These must be changed (along with lots of other code) if OBJECT_OFFSET_STEPS is changed. #define tile_raster_scale_x(v, rw) (((v)*(rw)) >> 6) #define tile_raster_scale_y(v, rh) (((v)*(rh)) >> 6) // offsets for mouse pointer #define Z_PLAN (4) #define Z_GRID (0) #endif simutrans-124.5/src/simutrans/simconvoi.cc000066400000000000000000003442711520675460500207300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "simdebug.h" #include "simunits.h" #include "world/simworld.h" #include "simware.h" #include "player/finance.h" // convert_money #include "player/simplay.h" #include "simconvoi.h" #include "simhalt.h" #include "obj/depot.h" #include "gui/simwin.h" #include "tool/simmenu.h" #include "simcolor.h" #include "simmesg.h" #include "simintr.h" #include "simlinemgmt.h" #include "simline.h" #include "freight_list_sorter.h" #include "gui/minimap.h" #include "gui/convoi_info.h" #include "gui/depot_frame.h" #include "gui/messagebox.h" #include "gui/convoi_detail.h" #include "ground/grund.h" #include "obj/way/schiene.h" // for railblocks #include "descriptor/citycar_desc.h" #include "descriptor/roadsign_desc.h" #include "descriptor/vehicle_desc.h" #include "dataobj/schedule.h" #include "dataobj/route.h" #include "dataobj/loadsave.h" #include "dataobj/translator.h" #include "dataobj/environment.h" #include "display/viewport.h" #include "obj/crossing.h" #include "obj/roadsign.h" #include "obj/wayobj.h" #include "utils/simrandom.h" #include "utils/simstring.h" #include "utils/cbuffer.h" #include "vehicle/air_vehicle.h" #include "vehicle/overtaker.h" #include "vehicle/rail_vehicle.h" #include "vehicle/road_vehicle.h" #include "vehicle/simroadtraffic.h" #include "vehicle/water_vehicle.h" /* * Waiting time for loading (ms) */ #define WTT_LOADING 2000 karte_ptr_t convoi_t::welt; /* * Debugging helper - translate state value to human readable name */ static const char * state_names[convoi_t::MAX_STATES] = { "INITIAL", "EDIT_SCHEDULE", "ROUTING_1", "", "", "NO_ROUTE", "DRIVING", "LOADING", "WAITING_FOR_CLEARANCE", "WAITING_FOR_CLEARANCE_ONE_MONTH", "CAN_START", "CAN_START_ONE_MONTH", "SELF_DESTRUCT", "WAITING_FOR_CLEARANCE_TWO_MONTHS", "CAN_START_TWO_MONTHS", "LEAVING_DEPOT", "ENTERING_DEPOT" }; /** * Calculates speed of slowest vehicle in the given array */ static int calc_min_top_speed(const array_tpl& fahr, uint8 vehicle_count) { int min_top_speed = SPEED_UNLIMITED; for(uint8 i=0; iget_desc()->get_topspeed() ) ); } return min_top_speed; } void convoi_t::init(player_t *player) { owner = player; needs_electric = false; sum_gesamtweight = sum_weight = 0; sum_running_costs = sum_fixed_costs = sum_gear_and_power = previous_delta_v = 0; sum_gear_and_power_non_electric = sum_gear_and_power_electric = 0; sum_power = 0; min_top_speed = SPEED_UNLIMITED; speedbonus_kmh = SPEED_UNLIMITED; // speed_to_kmh() not needed schedule = NULL; schedule_target = koord3d::invalid; line = linehandle_t(); vehicle_count = 0; steps_driven = -1; withdraw = false; has_obsolete = false; no_load = false; wait_lock = 0; arrived_time = 0; jahresgewinn = 0; total_distance_traveled = 0; distance_since_last_stop = 0; sum_speed_limit = 0; maxspeed_average_count = 0; next_reservation_index = 0; alte_richtung = ribi_t::none; next_wolke = 0; state = INITIAL; unloading_state = false; *name_and_id = 0; name_offset = 0; old_sort_mode = 255; loading_level = 0; loading_limit = 0; speed_limit = SPEED_UNLIMITED; max_record_speed = 0; brake_speed_soll = SPEED_UNLIMITED; akt_speed_soll = 0; // target speed akt_speed = 0; // current speed sp_soll = 0; next_stop_index = 65535; last_load_tick = 0; line_update_pending = linehandle_t(); home_depot = koord3d::invalid; recalc_data_front = true; recalc_data = true; recalc_speed_limit = true; } convoi_t::convoi_t(loadsave_t* file) : fahr(default_vehicle_length, NULL) { self = convoihandle_t(); init(0); rdwr(file); } convoi_t::convoi_t(player_t* player) : fahr(default_vehicle_length, NULL) { self = convoihandle_t(this); player->book_convoi_number(1); init(player); set_name( "Unnamed" ); welt->add_convoi( self ); init_financial_history(); } convoi_t::~convoi_t() { owner->book_convoi_number( -1); assert(self.is_bound()); assert(vehicle_count==0); // close windows destroy_win( magic_convoi_info+self.get_id() ); DBG_MESSAGE("convoi_t::~convoi_t()", "destroying %d, %p", self.get_id(), this); // stop following if(welt->get_viewport()->get_follow_convoi()==self) { welt->get_viewport()->set_follow_convoi( convoihandle_t() ); } welt->sync.remove( this ); welt->rem_convoi( self ); // if lineless convoy -> unregister from stops if( !line.is_bound() ) { unregister_stops(); } // force asynchronous recalculation if(schedule) { if(!schedule->is_editing_finished()) { destroy_win((ptrdiff_t)schedule); } if (!schedule->empty() && !line.is_bound()) { welt->set_schedule_counter(); } delete schedule; } // deregister from line (again) unset_line(); self.detach(); } // waypoint: no stop, resp. for airplanes in air (i.e. no air strip below) bool convoi_t::is_waypoint( koord3d ziel ) const { if( fahr[0]->get_waytype() == air_wt ) { // separate logic for airplanes, since the can have waypoints over stops etc. grund_t *gr = welt->lookup_kartenboden(ziel.get_2d()); if( gr == NULL || gr->get_weg(air_wt) == NULL ) { // during flight always a waypoint return true; } else if( gr->get_depot() ) { // but a depot is not a waypoint return false; } // so we are on a taxiway/runway here ... } if (grund_t* gr = welt->lookup(ziel)) { if (depot_t* dp = gr->get_depot()) { // don't stop in other peoples depot return !(dp->get_owner() == owner && fahr[0] && dp->get_waytype() == fahr[0]->get_waytype()); } } return !haltestelle_t::get_halt(ziel, owner, fahr[0]->get_waytype()==water_wt).is_bound(); } /** * unreserves the whole remaining route */ void convoi_t::unreserve_route() { // need a route, vehicles, and vehicles must belong to this convoi // (otherwise crash during loading when fahr[0]->convoi is not initialized yet if( !route.empty() && vehicle_count>0 && fahr[0]->get_convoi() == this ) { rail_vehicle_t* lok = dynamic_cast(fahr[0]); if (lok) { // free all reserved blocks route_t::index_t dummy; lok->block_reserver(get_route(), back()->get_route_index(), dummy, dummy, 100000, false, true); } } } /** * reserves route until next_reservation_index */ void convoi_t::reserve_route() { if( !route.empty() && vehicle_count>0 && (is_waiting() || state==DRIVING || state==LEAVING_DEPOT) ) { for( route_t::index_t idx = back()->get_route_index(); idx < next_reservation_index /*&& idx < route.get_count()*/; idx++ ) { if( grund_t *gr = welt->lookup( route.at(idx) ) ) { if( schiene_t *sch = (schiene_t *)gr->get_weg( front()->get_waytype() ) ) { sch->reserve( self, ribi_type( route.at(max(1u,idx)-1u), route.at(min(route.get_count()-1u,idx+1u)) ) ); } } } } } /** * Sets route_index of all vehicles to startindex. * Puts all vehicles on tile at this position in the route. * Convoy stills needs to be pushed that the convoy is right on track. * @returns length of convoy minus last vehicle */ uint32 convoi_t::move_to(route_t::index_t const start_index) { steps_driven = -1; koord3d k = route.at(start_index); grund_t* gr = welt->lookup(k); uint32 train_length = 0; for (unsigned i = 0; i != vehicle_count; ++i) { vehicle_t& v = *fahr[i]; if( grund_t const* gr = welt->lookup(v.get_pos()) ) { v.mark_image_dirty(v.get_image(), 0); v.leave_tile(); // maybe unreserve this if( schiene_t* const rails = obj_cast(gr->get_weg(v.get_waytype())) ) { rails->unreserve(&v); } } // propagate new index to vehicle, will set all movement related variables, in particular pos v.initialise_journey(start_index, true); // now put vehicle on the tile if (gr) { v.enter_tile(gr); } if (i != vehicle_count - 1U) { train_length += v.get_desc()->get_length(); } } return train_length; } void convoi_t::finish_rd() { if(schedule==NULL) { if( state!=INITIAL ) { grund_t *gr = welt->lookup(home_depot); if(gr && gr->get_depot()) { dbg->warning( "convoi_t::finish_rd()","No schedule during loading convoi %i: State will be initial!", self.get_id() ); for( uint8 i=0; iset_pos(home_depot); } state = INITIAL; } else { dbg->error( "convoi_t::finish_rd()","No schedule during loading convoi %i: Convoi will be destroyed!", self.get_id() ); for( uint8 i=0; iset_pos(koord3d::invalid); } destroy(); return; } } // anyway reassign convoi pointer ... for( uint8 i=0; iset_convoi(this); if( state!=INITIAL && welt->lookup(v->get_pos()) ) { // mark vehicle as used v->set_driven(); } } return; } else { // restore next schedule target for non-stop waypoint handling const koord3d ziel = schedule->get_current_entry().pos; if( vehicle_count>0 && is_waypoint(ziel) ) { schedule_target = ziel; } } bool realign_position = false; if( vehicle_count>0 ) { DBG_MESSAGE("convoi_t::finish_rd()","state=%s, next_stop_index=%d", state_names[state], next_stop_index ); // only realign convois not leaving depot to avoid jumps through signals if( steps_driven!=-1 ) { for( uint8 i=0; iset_leading( i==0 ); v->set_last( i+1==vehicle_count ); v->calc_height(); // this sets the convoi and will renew the block reservation, if needed! v->set_convoi(this); } } else { // test also for realignment sint16 step_pos = 0; koord3d drive_pos; uint8 const diagonal_vehicle_steps_per_tile = (uint8)(130560U / welt->get_settings().get_pak_diagonal_multiplier()); for( uint8 i=0; iset_leading( i==0 ); v->set_last( i+1==vehicle_count ); v->calc_height(); // this sets the convoi and will renew the block reservation, if needed! v->set_convoi(this); // wrong alignment here => must relocate if(v->need_realignment()) { // diagonal => convoi must restart realign_position |= ribi_t::is_bend(v->get_direction()) && (state==DRIVING || is_waiting()); } // if version is 99.17 or lower, some convois are broken, i.e. had too large gaps between vehicles if( !realign_position && state!=INITIAL && state!=LEAVING_DEPOT ) { if( i==0 ) { step_pos = v->get_steps(); } else { if( drive_pos!=v->get_pos() ) { // with long vehicles on diagonals, vehicles need not to be on consecutive tiles // do some guessing here uint32 dist = koord_distance(drive_pos, v->get_pos()); if (dist>1) { step_pos += (dist-1) * diagonal_vehicle_steps_per_tile; } step_pos += ribi_t::is_bend(v->get_direction()) ? diagonal_vehicle_steps_per_tile : VEHICLE_STEPS_PER_TILE; } dbg->message("convoi_t::finish_rd()", "v: pos(%s) steps(%d) len=%d ribi=%d prev (%s) step(%d)", v->get_pos().get_str(), v->get_steps(), v->get_desc()->get_length()*16, v->get_direction(), drive_pos.get_2d().get_str(), step_pos); if( abs( v->get_steps() - step_pos )>15 ) { // not where it should be => realign realign_position = true; dbg->warning( "convoi_t::finish_rd()", "convoi (%s) is broken => realign", get_name() ); } } step_pos -= v->get_desc()->get_length_in_steps(); drive_pos = v->get_pos(); } } } DBG_MESSAGE("convoi_t::finish_rd()","next_stop_index=%d", next_stop_index ); linehandle_t new_line = line; if( !new_line.is_bound() ) { // if there is a line with id=0 in the savegame try to assign cnv to this line new_line = get_owner()->simlinemgmt.get_line_with_id_zero(); } if( new_line.is_bound() ) { if ( !schedule->matches( welt, new_line->get_schedule() ) ) { // 101 version produced broken line ids => we have to find our line the hard way ... vector_tpl lines; get_owner()->simlinemgmt.get_lines(schedule->get_type(), &lines); new_line = linehandle_t(); for(linehandle_t const l : lines) { if( schedule->matches( welt, l->get_schedule() ) ) { // if a line is assigned, set line! new_line = l; break; } } } // now the line should match our schedule or else ... if(new_line.is_bound()) { line = new_line; line->add_convoy(self); DBG_DEBUG("convoi_t::finish_rd()","%s registers for %d", name_and_id, line.get_id()); } else { line = linehandle_t(); } } } else { // no vehicles in this convoi?!? dbg->error( "convoi_t::finish_rd()","No vehicles in Convoi %i: will be destroyed!", self.get_id() ); destroy(); return; } // put convoi again right on track? if(realign_position && vehicle_count>1) { // display just a warning dbg->warning("convoi_t::finish_rd()","cnv %i is currently too long.",self.get_id()); if (route.empty()) { // realigning needs a route state = NO_ROUTE; owner->report_vehicle_problem( self, koord3d::invalid ); dbg->error( "convoi_t::finish_rd()", "No valid route, but needs realignment at (%s)!", fahr[0]->get_pos().get_str() ); } else { // since start may have been changed route_t::index_t start_index = max(1,fahr[vehicle_count-1]->get_route_index())-1; if (start_index >= route.get_count()) { dbg->error( "convoi_t::finish_rd()", "Routeindex of last vehicle of (%s) too large (%hu > %hu)!", get_name(), start_index, route.get_count() ); start_index = 0; } uint32 train_length = move_to(start_index) + 1; const koord3d last_start = fahr[0]->get_pos(); // now advance all convoi until it is completely on the track fahr[0]->set_leading(false); // switches off signal checks ... for(unsigned i=0; iset_smoke_enabled(false); fahr[i]->do_drive( (VEHICLE_STEPS_PER_CARUNIT*train_length)<get_desc()->get_length(); v->set_smoke_enabled(true); // eventually reserve this again grund_t *gr=welt->lookup(v->get_pos()); // airplanes may have no ground ... if (schiene_t* const sch0 = obj_cast(gr->get_weg(fahr[i]->get_waytype()))) { sch0->reserve(self,ribi_t::none); } } fahr[0]->set_leading(true); if( state != INITIAL && state != EDIT_SCHEDULE && fahr[0]->get_pos() != last_start ) { state = WAITING_FOR_CLEARANCE; } } } if( state==LOADING ) { // the fully the shorter => register again as older convoi wait_lock = max(0, 2000-loading_level*20); if (distance_since_last_stop > 0) { calc_gewinn(); } } // when saving with open window, this can happen if( state==EDIT_SCHEDULE ) { if (env_t::networkmode) { wait_lock = 30000; // 30s to drive on, if the client in question had left } schedule->finish_editing(); } // remove wrong freight check_freight(); // some convois had wrong old direction in them if( stateget_direction(); } // if lineless convoy -> register itself with stops if( !line.is_bound() ) { register_stops(); } calc_speedbonus_kmh(); } // since now convoi states go via tool_t void convoi_t::call_convoi_tool( const char function, const char *extra ) const { tool_t *tmp_tool = create_tool( TOOL_CHANGE_CONVOI | SIMPLE_TOOL ); cbuffer_t param; param.printf("%c,%u", function, self.get_id()); if( extra && *extra ) { param.printf(",%s", extra); } tmp_tool->set_default_param(param); welt->set_tool( tmp_tool, get_owner() ); // since init always returns false, it is safe to delete immediately delete tmp_tool; } void convoi_t::rotate90( const sint16 y_size ) { record_pos.rotate90( y_size ); home_depot.rotate90( y_size ); route.rotate90( y_size ); if( schedule_target!=koord3d::invalid ) { schedule_target.rotate90( y_size ); } if(schedule) { schedule->rotate90( y_size ); } for( int i=0; irotate90_freight_destinations( y_size ); } // eventually correct freight destinations (and remove all stale freight) check_freight(); } /** * Return the convoi position. * @return Convoi position */ koord3d convoi_t::get_pos() const { if(vehicle_count > 0 && fahr[0]) { return state==INITIAL ? home_depot : fahr[0]->get_pos(); } else { return koord3d::invalid; } } /** * Sets the name. Creates a copy of name. */ void convoi_t::set_name(const char *name, bool with_new_id) { if( with_new_id ) { char buf[128]; name_offset = sprintf(buf,"(%i) ",self.get_id() ); tstrncpy(buf + name_offset, translator::translate(name, welt->get_settings().get_name_language_id()), lengthof(buf) - name_offset); tstrncpy(name_and_id, buf, lengthof(name_and_id)); } else { char buf[128]; // check if there is a id in the name string name_offset = sprintf(buf,"(%i) ",self.get_id() ); if( strlen(name) < name_offset || strncmp(buf,name,name_offset)!=0) { name_offset = 0; } tstrncpy(buf+name_offset, name+name_offset, sizeof(buf)-name_offset); tstrncpy(name_and_id, buf, lengthof(name_and_id)); } // now tell the windows that we were renamed convoi_info_t *info = dynamic_cast(win_get_magic( magic_convoi_info+self.get_id())); if (info) { info->update_data(); } if( in_depot() ) { const grund_t *const ground = welt->lookup( get_home_depot() ); if( ground ) { const depot_t *const depot = ground->get_depot(); if( depot ) { depot_frame_t *const frame = dynamic_cast( win_get_magic( (ptrdiff_t)depot ) ); if( frame ) { frame->update_data(); } } } } } // length of convoi (16 is one tile) uint32 convoi_t::get_length() const { uint32 len = 0; for( uint8 i=0; iget_desc()->get_length(); } return len; } /** * convoi add their running cost for traveling one tile */ void convoi_t::add_running_cost( const weg_t *weg ) { jahresgewinn += sum_running_costs; if( weg && weg->get_owner()!=get_owner() && weg->get_owner()!=NULL ) { // running on non-public way costs toll (since running costs are positive => invert) sint64 toll = -(sum_running_costs*welt->get_settings().get_way_toll_runningcost_percentage())/100l; sint64 toll_wayobj = 0; if( welt->get_settings().get_way_toll_waycost_percentage() ) { if( weg->is_electrified() && (needs_electrification() || can_use_electrification())) { // toll for using electricity sum_gear_and_power = sum_gear_and_power_non_electric + sum_gear_and_power_electric; grund_t *gr = welt->lookup(weg->get_pos()); for( int i=1; iobj_count(); i++ ) { obj_t *d=gr->obj_bei(i); if( wayobj_t const* const wo = obj_cast(d) ) { if( wo->get_waytype()==weg->get_waytype() ) { toll_wayobj += (wo->get_desc()->get_maintenance()*welt->get_settings().get_way_toll_waycost_percentage())/100l; wo->get_owner()->book_toll_received( toll_wayobj, get_schedule()->get_waytype() ); break; } } } } else { // can run with non-electric power sum_gear_and_power = sum_gear_and_power_non_electric; } // now add normal way toll be maintenance toll += (weg->get_desc()->get_maintenance()*welt->get_settings().get_way_toll_waycost_percentage())/100l; } weg->get_owner()->book_toll_received( toll, get_schedule()->get_waytype() ); get_owner()->book_toll_paid( -toll-toll_wayobj, get_schedule()->get_waytype() ); book( -toll-toll_wayobj, CONVOI_WAYTOLL); book( -toll-toll_wayobj, CONVOI_PROFIT); } get_owner()->book_running_costs( sum_running_costs, get_schedule()->get_waytype()); book( sum_running_costs, CONVOI_OPERATIONS ); book( sum_running_costs, CONVOI_PROFIT ); total_distance_traveled ++; distance_since_last_stop++; sum_speed_limit += speed_to_kmh( min( min_top_speed, speed_limit )); book( 1, CONVOI_DISTANCE ); } /** * Returns residual power given power, weight, and current speed. * @param speed (in internal speed unit) * @param total_power sum of power times gear (see calculation of sum_gear_and_power) * @param friction_weight weight including friction of the convoy * @param total_weight weight of the convoy * @returns residual power */ static inline sint32 res_power(sint64 speed, sint32 total_power, sint64 friction_weight, sint64 total_weight) { sint32 res = total_power - (sint32)( ( (sint64)speed * ( (friction_weight * (sint64)speed ) / 3125ll + 1ll) ) / 2048ll + (total_weight * 64ll) / 1000ll); return res; } /* Calculates (and sets) new akt_speed * needed for driving, entering and leaving a depot) */ void convoi_t::calc_acceleration(uint32 delta_t) { if( !recalc_data && !recalc_speed_limit && !recalc_data_front && ( (sum_friction_weight == sum_gesamtweight && akt_speed_soll <= akt_speed && akt_speed_soll+24 >= akt_speed) || (sum_friction_weight > sum_gesamtweight && akt_speed_soll == akt_speed) ) ) { // at max speed => go with max speed and finish calculation here // at slopes/curves, only do this if there is absolutely now change akt_speed = akt_speed_soll; return; } // only compute this if a vehicle in the convoi hopped if( recalc_data || recalc_speed_limit ) { // calculate total friction and lowest speed limit const vehicle_t* v = front(); speed_limit = min( min_top_speed, v->get_speed_limit() ); if (recalc_data) { sum_gesamtweight = v->get_total_weight(); sum_friction_weight = v->get_frictionfactor() * sum_gesamtweight; } for( unsigned i=1; iget_speed_limit() ); if (recalc_data) { int total_vehicle_weight = v->get_total_weight(); sum_friction_weight += v->get_frictionfactor() * total_vehicle_weight; sum_gesamtweight += total_vehicle_weight; } } recalc_data = recalc_speed_limit = false; akt_speed_soll = min( speed_limit, brake_speed_soll ); } if( recalc_data_front ) { // brake at the end of stations/in front of signals and crossings const uint32 tiles_left = 1 + get_next_stop_index() - front()->get_route_index(); brake_speed_soll = SPEED_UNLIMITED; if( tiles_left < 4 ) { static sint32 brake_speed_countdown[4] = { kmh_to_speed(25), kmh_to_speed(50), kmh_to_speed(100), kmh_to_speed(200) }; brake_speed_soll = brake_speed_countdown[tiles_left]; } akt_speed_soll = min( speed_limit, brake_speed_soll ); recalc_data_front = false; } // more pleasant and a little more "physical" model // try to simulate quadratic friction if(sum_gesamtweight != 0) { /* * The parameter consist of two parts (optimized for good looking): * - every vehicle in a convoi has a the friction of its weight * - the dynamic friction is calculated that way, that v^2*weight*frictionfactor = 200 kW * This means that if a vehicle is loaded heavier and/or travels faster, less * power for acceleration is available. * since delta_t can have any value, we have to scale the step size by this value. * However, there is a quadratic friction term => if delta_t is too large the calculation may get weird results * * but for integer, we have to use the order below and calculate actually 64*deccel, like the sum_gear_and_power * since akt_speed=10/128 km/h and we want 64*200kW=(100km/h)^2*100t, we must multiply by (128*2)/100 * But since the acceleration was too fast, we just decelerate 4x more => >>6 instead >>8 */ //sint32 deccel = ( ( (akt_speed*sum_friction_weight)>>6 )*(akt_speed>>2) ) / 25 + (sum_gesamtweight*64); // this order is needed to prevent overflows! //sint32 deccel = (sint32)( ( (sint64)akt_speed * (sint64)sum_friction_weight * (sint64)akt_speed ) / (25ll*256ll) + sum_gesamtweight * 64ll) / 1000ll; // intermediate still overflows so sint64 //sint32 deccel = (sint32)( ( (sint64)akt_speed * ( (sum_friction_weight * (sint64)akt_speed ) / 3125ll + 1ll) ) / 2048ll + (sum_gesamtweight * 64ll) / 1000ll); // note: result can overflow sint32 and double so we use sint64. Planes are ok. //sint32 delta_v = (sint32)( ( (double)( (akt_speed>akt_speed_soll?0l:sum_gear_and_power) - deccel)*(double)delta_t)/(double)sum_gesamtweight); sint64 residual_power = res_power(akt_speed, akt_speed>akt_speed_soll? 0l : sum_gear_and_power, sum_friction_weight, sum_gesamtweight); // we normalize delta_t to 1/64th and check for speed limit */ //sint32 delta_v = ( ( (akt_speed>akt_speed_soll?0l:sum_gear_and_power) - deccel) * delta_t)/sum_gesamtweight; sint64 delta_v = ( residual_power * (sint64)delta_t * 1000ll) / (sint64)sum_gesamtweight; // we need more accurate arithmetic, so we store the previous value delta_v += previous_delta_v; previous_delta_v = (uint16) (delta_v & 0x00000FFFll); // and finally calculate new speed akt_speed = max(akt_speed_soll>>4, akt_speed+(sint32)(delta_v>>12l) ); } else { // very old vehicle ... akt_speed += 16; } // obey speed maximum with additional const brake ... if(akt_speed > akt_speed_soll) { if (akt_speed > akt_speed_soll + 24) { akt_speed -= 24; if(akt_speed > akt_speed_soll+kmh_to_speed(20)) { akt_speed = akt_speed_soll+kmh_to_speed(20); } } else { akt_speed = akt_speed_soll; } } // new record? if(akt_speed > max_record_speed) { max_record_speed = akt_speed; record_pos = fahr[0]->get_pos(); } } /** * Calculates maximal possible speed. * Uses iterative technique to take care of integer arithmetic. */ sint32 convoi_t::calc_max_speed(uint64 total_power, uint64 total_weight, sint32 speed_limit) { // precision is 0.5 km/h const sint32 tol = kmh_to_speed(1)/2; // bisection to find max speed sint32 pl,pr,pm; sint64 sl,sr,sm; // test speed_limit sr = speed_limit; pr = res_power(sr, (sint32)total_power, total_weight, total_weight); if (pr >= 0) { return (sint32)sr; // convoy can travel at speed given by speed_limit } sl = 1; pl = res_power(sl, (sint32)total_power, total_weight, total_weight); if (pl <= 0) { return 0; // no power to move at all } // bisection algorithm to find speed for which residual power is zero while (sr - sl > tol) { sm = (sl + sr)/2; if (sm == sl) break; pm = res_power(sm, (sint32)total_power, total_weight, total_weight); if (((sint64)pl)*pm <= 0) { pr = pm; sr = sm; } else { pl = pm; sl = sm; } } return (sint32)sl; } int convoi_t::get_vehicle_at_length(uint16 length) { int current_length = 0; for( int i=0; iget_desc()->get_length(); if(length 0) { return SYNC_OK; } switch(state) { case INITIAL: // in depot, should not be in sync list, remove return SYNC_REMOVE; case EDIT_SCHEDULE: case ROUTING_1: case DUMMY4: case DUMMY5: case NO_ROUTE: case CAN_START: case CAN_START_ONE_MONTH: case CAN_START_TWO_MONTHS: // this is an async task, see step() break; case ENTERING_DEPOT: break; case LEAVING_DEPOT: { // ok, so we will accelerate akt_speed_soll = max( akt_speed_soll, kmh_to_speed(30) ); calc_acceleration(delta_t); sp_soll += (akt_speed*delta_t); // now actually move the units while(sp_soll>>12) { // Attempt to move one step. uint32 sp_hat = fahr[0]->do_drive(1<0 ) { steps_driven++; } int v_nr = get_vehicle_at_length(steps_driven>>4); // stop when depot reached if (state==INITIAL) { return SYNC_REMOVE; } if (state==ROUTING_1) { break; } if( v_nr==vehicle_count ) { // all are moving steps_driven = -1; state = DRIVING; return SYNC_OK; } else if( sp_hat==0 ) { // something went wrong. wait for next sync_step() return SYNC_OK; } // now only the right numbers for(int i=1; i<=v_nr; i++) { fahr[i]->do_drive(sp_hat); } sp_soll -= sp_hat; } // smoke for the engines next_wolke += delta_t; if(next_wolke>500) { next_wolke = 0; for(int i=0; imake_smoke(); } } } break; // LEAVING_DEPOT case DRIVING: { calc_acceleration(delta_t); // now actually move the units sp_soll += (akt_speed*delta_t); uint32 sp_hat = fahr[0]->do_drive(sp_soll); // stop when depot reached ... if(state==INITIAL) { return SYNC_REMOVE; } // now move the rest (so all vehicles are moving synchronously) for(unsigned i=1; ido_drive(sp_hat); } // maybe we have been stopped by something => avoid wide jumps sp_soll = (sp_soll-sp_hat) & 0x0FFF; // smoke for the engines next_wolke += delta_t; if(next_wolke>500) { next_wolke = 0; for(int i=0; imake_smoke(); } } } break; // DRIVING case LOADING: // loading is an async task, see laden() break; case WAITING_FOR_CLEARANCE: case WAITING_FOR_CLEARANCE_ONE_MONTH: case WAITING_FOR_CLEARANCE_TWO_MONTHS: // waiting is asynchronous => fixed waiting order and route search break; case SELF_DESTRUCT: // see step, since destruction during a screen update may give strange effects break; default: dbg->fatal("convoi_t::sync_step()", "Wrong state %d!\n", state); } return SYNC_OK; } /** * Berechne route von Start- zu Zielkoordinate */ bool convoi_t::drive_to() { if( vehicle_count>0 ) { // unreserve all tiles that are covered by the train but do not contain one of the wagons, // otherwise repositioning of the train drive_to may lead to stray reserved tiles if (dynamic_cast(fahr[0])!=NULL && vehicle_count > 1) { // route-index points to next position in route // it is completely off when convoi leaves depot route_t::index_t index0 = min(fahr[0]->get_route_index()-1, route.get_count()); for(uint8 i=1; iget_route_index(); for(route_t::index_t j = index1; jlookup(route.at(j)); if (schiene_t *track = (schiene_t *)gr->get_weg( front()->get_waytype() ) ) { track->unreserve(self); } } index0 = min(index1-1, route.get_count()); } } koord3d start = fahr[0]->get_pos(); koord3d ziel = schedule->get_current_entry().pos; // avoid stopping mid-halt if( start==ziel ) { halthandle_t halt = haltestelle_t::get_halt(ziel, owner, fahr[0]->get_waytype() == water_wt); if( halt.is_bound() && route.is_contained(start) ) { for( uint32 i=route.index_of(start); ilookup(route.at(i)); if( gr && gr->get_halt()==halt ) { ziel = gr->get_pos(); } else { break; } } } } if( !fahr[0]->calc_route( start, ziel, speed_to_kmh(min_top_speed), &route ) ) { if( state != NO_ROUTE ) { state = NO_ROUTE; get_owner()->report_vehicle_problem( self, ziel ); } // wait 25s before next attempt wait_lock = 25000; } else { bool route_ok = true; const uint8 current_stop = schedule->get_current_stop(); if( fahr[0]->get_waytype() != water_wt ) { air_vehicle_t *const plane = dynamic_cast(fahr[0]); uint32 takeoff = 0, search = 0, landing = 0; air_vehicle_t::flight_state plane_state = air_vehicle_t::taxiing; if( plane ) { // due to the complex state system of aircrafts, we have to save index and state plane->get_event_index( plane_state, takeoff, search, landing ); } // set next schedule target position if next is a waypoint if( is_waypoint(ziel) ) { schedule_target = ziel; } // continue route search until the destination is a station while( is_waypoint(ziel) ) { start = ziel; schedule->advance(); ziel = schedule->get_current_entry().pos; if( schedule->get_current_stop() == current_stop ) { // looped around without finding a halt => entire schedule is waypoints. break; } route_t next_segment; if( !fahr[0]->calc_route( start, ziel, speed_to_kmh(min_top_speed), &next_segment ) ) { // do we still have a valid route to proceed => then go until there if( route.get_count()>1 ) { break; } // we are stuck on our first routing attempt => give up if( state != NO_ROUTE ) { state = NO_ROUTE; get_owner()->report_vehicle_problem( self, ziel ); } // wait 25s before next attempt wait_lock = 25000; route_ok = false; break; } else { bool looped = false; if( fahr[0]->get_waytype() != air_wt ) { // check if the route circles back on itself (only check the first tile, should be enough) looped = route.is_contained(next_segment.at(1)); #if 0 // this will forbid an eight figure, which might be clever to avoid a problem of reserving one own track for( unsigned i = 1; iget_event_index( dummy1, new_takeoff, new_search, new_landing ); if( takeoff == 0x7FFFFFFF && new_takeoff != 0x7FFFFFFF ) { takeoff = new_takeoff + count_offset; } if( landing == 0x7FFFFFFF && new_landing != 0x7FFFFFFF ) { landing = new_landing + count_offset; } if( search == 0x7FFFFFFF && new_search != 0x7FFFFFFF ) { search = new_search + count_offset; } } } } } if( plane ) { // due to the complex state system of aircrafts, we have to restore index and state plane->set_event_index( plane_state, takeoff, search, landing ); } } schedule->set_current_stop(current_stop); if( route_ok ) { vorfahren(); return true; } } } return false; } /** * Ein Fahrzeug hat ein Problem erkannt und erzwingt die * Berechnung einer neuen Route */ void convoi_t::suche_neue_route() { state = ROUTING_1; wait_lock = 0; } /** * Asynchrne step methode des Convois */ void convoi_t::step() { if( wait_lock > 0 ) { return; } // moved check to here, as this will apply the same update // logic/constraints convois have for manual schedule manipulation if (line_update_pending.is_bound()) { check_pending_updates(); } if( state==LOADING ) { // handled seperately, since otherwise departures are delayed laden(); } switch(state) { case LOADING: // handled above break; case DUMMY4: case DUMMY5: break; case EDIT_SCHEDULE: // schedule window closed? if(schedule!=NULL && schedule->is_editing_finished()) { set_schedule(schedule); schedule_target = koord3d::invalid; if( schedule->empty() ) { // no entry => no route ... state = NO_ROUTE; owner->report_vehicle_problem( self, koord3d::invalid ); } else { // Schedule changed at station // this station? then complete loading task else drive on halthandle_t h = haltestelle_t::get_halt( get_pos(), owner, fahr[0]->get_waytype() == water_wt); if( h.is_bound() && h==haltestelle_t::get_halt( schedule->get_current_entry().pos, owner, fahr[0]->get_waytype() == water_wt) ) { if (route.get_count() > 0) { koord3d const& pos = route.back(); if (h == haltestelle_t::get_halt(pos, owner, fahr[0]->get_waytype() == water_wt)) { state = get_pos() == pos ? LOADING : DRIVING; break; } } else { if( drive_to() ) { state = DRIVING; break; } } } if( schedule->get_current_entry().pos==get_pos() ) { // position in depot: waiting grund_t *gr = welt->lookup(schedule->get_current_entry().pos); if( gr && gr->get_depot() ) { betrete_depot( gr->get_depot() ); } else { state = ROUTING_1; } } else { // go to next state = ROUTING_1; } } } break; case ROUTING_1: { vehicle_t* v = fahr[0]; if( schedule->empty() ) { state = NO_ROUTE; owner->report_vehicle_problem( self, koord3d::invalid ); } else { // check first, if we are already there: assert( schedule->get_current_stop()get_count() ); if( v->get_pos()==schedule->get_current_entry().pos ) { schedule->advance(); } // now calculate a new route drive_to(); // finally, was there a record last time? if(max_record_speed>welt->get_record_speed(fahr[0]->get_waytype())) { welt->notify_record(self, max_record_speed, record_pos); } } } break; case NO_ROUTE: // stuck vehicles if (schedule->empty()) { // no entries => no route ... } else { // now calculate a new route drive_to(); } break; case CAN_START: case CAN_START_ONE_MONTH: case CAN_START_TWO_MONTHS: { vehicle_t* v = fahr[0]; sint32 restart_speed = -1; if( v->can_enter_tile( restart_speed, 0 ) ) { // can reserve new block => drive on state = (steps_driven>=0) ? LEAVING_DEPOT : DRIVING; if(haltestelle_t::get_halt(v->get_pos(), owner, fahr[0]->get_waytype() == water_wt).is_bound()) { v->play_sound(); } } else if( steps_driven==0 ) { // on rail depot tile, do not reserve this if( grund_t *gr = welt->lookup(fahr[0]->get_pos()) ) { if (schiene_t* const sch0 = obj_cast(gr->get_weg(fahr[0]->get_waytype()))) { sch0->unreserve(fahr[0]); } } } if(restart_speed>=0) { akt_speed = restart_speed; } if(state==CAN_START || state==CAN_START_ONE_MONTH) { set_tiles_overtaking( 0 ); } } break; case WAITING_FOR_CLEARANCE_ONE_MONTH: case WAITING_FOR_CLEARANCE_TWO_MONTHS: case WAITING_FOR_CLEARANCE: { sint32 restart_speed = -1; if( fahr[0]->can_enter_tile( restart_speed, 0 ) ) { state = (steps_driven>=0) ? LEAVING_DEPOT : DRIVING; } if(restart_speed>=0) { akt_speed = restart_speed; } if(state!=DRIVING) { set_tiles_overtaking( 0 ); } } break; // must be here; may otherwise confuse window management case SELF_DESTRUCT: welt->set_dirty(); destroy(); return; // must not continue method after deleting this object default: /* keeps compiler silent*/ break; } // calculate new waiting time switch( state ) { // handled by routine case LOADING: break; // immediate action needed case SELF_DESTRUCT: case LEAVING_DEPOT: case ENTERING_DEPOT: case DRIVING: case DUMMY4: case DUMMY5: wait_lock = 0; break; // just waiting for action here case INITIAL: welt->sync.remove(this); // FALLTHROUGH case EDIT_SCHEDULE: case NO_ROUTE: wait_lock = max( wait_lock, 25000 ); break; // Same as waiting for free way case ROUTING_1: // immediate start required if (wait_lock == 0) break; // FALLTHROUGH case CAN_START: case WAITING_FOR_CLEARANCE: // unless a longer wait is requested if (wait_lock > 2500) { break; } // FALLTHROUGH // waiting for free way, not too heavy, not to slow case CAN_START_ONE_MONTH: case WAITING_FOR_CLEARANCE_ONE_MONTH: case CAN_START_TWO_MONTHS: case WAITING_FOR_CLEARANCE_TWO_MONTHS: // to avoid having a convoi stuck at a heavy traffic intersection/signal, the waiting time is randomized wait_lock = simrand(5000)+1; break; default: ; } } void convoi_t::new_year() { jahresgewinn = 0; } void convoi_t::new_month() { // should not happen: leftover convoi without vehicles ... if(vehicle_count==0) { DBG_DEBUG("convoi_t::new_month()","no vehicles => self destruct!"); self_destruct(); return; } // update statistics of average speed if( maxspeed_average_count==0 ) { financial_history[0][CONVOI_MAXSPEED] = distance_since_last_stop>0 ? get_speedbonus_kmh() : 0; } maxspeed_average_count = 0; // everything normal: update histroy for (int j = 0; j0; k--) { financial_history[k][j] = financial_history[k-1][j]; } financial_history[0][j] = 0; } // remind every new month again if( state==NO_ROUTE ) { get_owner()->report_vehicle_problem( self, get_pos() ); } // check for traffic jam if(state==WAITING_FOR_CLEARANCE) { state = WAITING_FOR_CLEARANCE_ONE_MONTH; // check, if now free ... // might also reset the state! sint32 restart_speed = -1; if( fahr[0]->can_enter_tile( restart_speed, 0 ) ) { state = DRIVING; } if(restart_speed>=0) { akt_speed = restart_speed; } } else if(state==WAITING_FOR_CLEARANCE_ONE_MONTH) { // make sure, not another vehicle with same line is loading in front bool notify = true; // check, if we are not waiting for load if( line.is_bound() && loading_level==0 ) { for( uint i=0; i < line->count_convoys(); i++ ) { convoihandle_t cnv = line->get_convoy(i); if( cnv.is_bound() && cnv->get_state()==LOADING && cnv->get_loading_level() < cnv->get_loading_limit() ) { // convoi on this line is waiting for load => assume we are waiting behind notify = false; break; } } } if( notify ) { get_owner()->report_vehicle_problem( self, koord3d::invalid ); } state = WAITING_FOR_CLEARANCE_TWO_MONTHS; } // check for traffic jam if(state==CAN_START) { state = CAN_START_ONE_MONTH; } else if(state==CAN_START_ONE_MONTH || state==CAN_START_TWO_MONTHS ) { get_owner()->report_vehicle_problem( self, koord3d::invalid ); state = CAN_START_TWO_MONTHS; } // check for obsolete vehicles in the convoi if(!has_obsolete && welt->use_timeline()) { // convoi has obsolete vehicles? const int month_now = welt->get_timeline_year_month(); has_obsolete = false; for(unsigned j=0; jget_desc()->is_retired(month_now)) { has_obsolete = true; break; } } } // book fixed cost as running cost book( sum_fixed_costs, CONVOI_OPERATIONS ); book( sum_fixed_costs, CONVOI_PROFIT ); // since convois can be in the depot, the waytypewithout schedule is determined from the vechile (if there) // one can argue that convois in depots should not cost money ... waytype_t wtyp = ignore_wt; if( schedule_t* s = get_schedule() ) { wtyp = s->get_waytype(); } else if( !fahr.empty() ) { wtyp = fahr[0]->get_waytype(); } get_owner()->book_running_costs( sum_fixed_costs, wtyp ); jahresgewinn += sum_fixed_costs; } void convoi_t::betrete_depot(depot_t *dep) { // first remove reservation, if train is still on track unreserve_route(); // remove vehicles from world data structure for(unsigned i=0; ilookup(v->get_pos()); if(gr) { // remove from blockstrecke v->set_last(true); v->leave_tile(); v->set_flag( obj_t::not_on_map ); } } dep->convoi_arrived(self, get_schedule()); destroy_win( magic_convoi_info+self.get_id() ); maxspeed_average_count = 0; state = INITIAL; } void convoi_t::start() { if( state == EDIT_SCHEDULE && schedule && schedule->is_editing_finished() ) { // go to defined starting state wait_lock = 0; step(); } if(state == INITIAL || state == ROUTING_1) { // set home depot to location of depot convoi is leaving if(route.empty()) { home_depot = fahr[0]->get_pos(); } else { home_depot = route.front(); fahr[0]->set_pos( home_depot ); } // put the convoi on the depot ground, to get automatic rotation // (vorfahren() will remove it anyway again.) grund_t *gr = welt->lookup( home_depot ); assert(gr); gr->obj_add( fahr[0] ); // put into sync list welt->sync.add(this); alte_richtung = ribi_t::none; no_load = false; state = ROUTING_1; // recalc weight and image // also for any vehicle entered a depot, set_letztes is true! => reset it correctly sint64 restwert_delta = 0; for(unsigned i=0; iset_leading( false ); fahr[i]->set_last( false ); restwert_delta -= fahr[i]->calc_sale_value(); fahr[i]->set_driven(); restwert_delta += fahr[i]->calc_sale_value(); fahr[i]->clear_flag( obj_t::not_on_map ); } fahr[0]->set_leading( true ); fahr[vehicle_count-1]->set_last( true ); // do not show the vehicle - it will be wrong positioned -vorfahren() will correct this fahr[0]->set_image(IMG_EMPTY); // update finances for used vehicle reduction when first driven owner->update_assets( restwert_delta, get_schedule()->get_waytype()); // calc state for convoi calc_loading(); calc_speedbonus_kmh(); maxspeed_average_count = 0; if(line.is_bound()) { // might have changed the vehicles in this car ... line->recalc_catg_index(); } else { welt->set_schedule_counter(); } wait_lock = 0; DBG_MESSAGE("convoi_t::start()", "Convoi %s wechselt von INITIAL nach ROUTING_1", name_and_id); } else { dbg->warning("convoi_t::start()", "called with state=%s\n", state_names[state]); } } /* called, when at a destination * can be waypoint, depot or a stop * called from the first vehicle_t of a convoi */ void convoi_t::ziel_erreicht() { const vehicle_t* v = fahr[0]; alte_richtung = v->get_direction(); // check, what is at destination! const grund_t* gr = welt->lookup(v->get_pos()); depot_t* dp = gr->get_depot(); if (dp) { // ok, we are entering a depot cbuffer_t buf; // we still book the money for the trip; however, the freight will be deleted (by the vehicle in the depot itself) calc_gewinn(); akt_speed = 0; buf.printf(translator::translate("%s has entered a depot."), get_name()); welt->get_message()->add_message(buf, v->get_pos(), message_t::warnings, PLAYER_FLAG | get_owner()->get_player_nr(), IMG_EMPTY); betrete_depot(dp); } else { // no depot reached, check for stop! halthandle_t halt = haltestelle_t::get_halt(schedule->get_current_entry().pos, owner, fahr[0]->get_waytype() == water_wt); if (halt.is_bound() && gr->get_weg_ribi(v->get_waytype()) != 0) { // seems to be a stop, so book the money for the trip calc_gewinn(); akt_speed = 0; halt->book(1, HALT_CONVOIS_ARRIVED); state = LOADING; arrived_time = welt->get_ticks(); last_load_tick = arrived_time; } else { // Neither depot nor station: waypoint schedule->advance(); state = ROUTING_1; } } wait_lock = 0; } /** * Wartet bis Fahrzeug 0 freie Fahrt meldet */ void convoi_t::warten_bis_weg_frei(sint32 restart_speed) { if (!is_waiting()) { state = WAITING_FOR_CLEARANCE; wait_lock = 0; } if (restart_speed >= 0) { // langsam anfahren akt_speed = restart_speed; } } bool convoi_t::add_vehicle(vehicle_t* v, bool infront) { DBG_MESSAGE("convoi_t::add_vehicle()", "at pos %i of %i total vehicles.", vehicle_count, fahr.get_count()); // extend array if requested if (vehicle_count == fahr.get_count()) { fahr.resize(vehicle_count + 1, NULL); DBG_MESSAGE("convoi_t::add_vehicle()", "extend array_tpl to %i totals.", fahr.get_count()); } // now append if (vehicle_count < fahr.get_count()) { v->set_convoi(this); if (infront) { for (unsigned i = vehicle_count; i > 0; i--) { fahr[i] = fahr[i - 1]; } fahr[0] = v; } else { fahr[vehicle_count] = v; } vehicle_count++; const vehicle_desc_t* info = v->get_desc(); if (info->get_power()) { if (info->get_engine_type() == vehicle_desc_t::electric) { sum_gear_and_power_electric += info->get_power() * info->get_gear(); } else { sum_gear_and_power_non_electric += info->get_power() * info->get_gear(); } sum_power += info->get_power(); sum_gear_and_power += info->get_power() * info->get_gear(); } sum_weight += info->get_weight(); sum_running_costs -= info->get_running_cost(); sum_fixed_costs -= welt->scale_with_month_length( info->get_fixed_cost() ); min_top_speed = min( min_top_speed, kmh_to_speed( v->get_desc()->get_topspeed() ) ); sum_gesamtweight = sum_weight; calc_loading(); // Add good_catg_index: if(v->get_cargo_max() != 0) { const goods_desc_t *ware=v->get_cargo_type(); if(ware!=goods_manager_t::none ) { goods_catg_index.append_unique( ware->get_catg_index() ); } } // check for obsolete if(!has_obsolete && welt->use_timeline()) { has_obsolete = info->is_retired( welt->get_timeline_year_month() ); } // cannot run without catenary needs_electric = sum_gear_and_power_electric > 0 && sum_gear_and_power_non_electric == 0; } else { return false; } // der convoi hat jetzt ein neues ende set_erstes_letztes(); DBG_MESSAGE("convoi_t::add_vehicle()","now %i of %i total vehicles.",vehicle_count,fahr.get_count()); return true; } vehicle_t *convoi_t::remove_vehicle_at(uint16 i) { vehicle_t *v = NULL; if(iset_convoi(NULL); --vehicle_count; fahr[vehicle_count] = NULL; const vehicle_desc_t *info = v->get_desc(); sum_power -= info->get_power(); sum_gear_and_power -= info->get_power()*info->get_gear(); if (info->get_engine_type() == vehicle_desc_t::electric) { sum_gear_and_power_electric -= info->get_power() * info->get_gear(); } else { sum_gear_and_power_non_electric -= info->get_power() * info->get_gear(); } sum_power += info->get_power(); sum_gear_and_power += info->get_power() * info->get_gear(); sum_weight -= info->get_weight(); sum_running_costs += info->get_running_cost(); sum_fixed_costs += welt->scale_with_month_length( info->get_fixed_cost() ); } sum_gesamtweight = sum_weight; calc_loading(); // der convoi hat jetzt ein neues ende if(vehicle_count > 0) { set_erstes_letztes(); } // calculate new minimum top speed min_top_speed = calc_min_top_speed(fahr, vehicle_count); // check for obsolete if(has_obsolete) { has_obsolete = false; const int month_now = welt->get_timeline_year_month(); for(unsigned i=0; iget_desc()->is_retired(month_now); } } recalc_catg_index(); // still requires electrifications? // cannot run without catenary needs_electric = sum_gear_and_power_electric > 0 && sum_gear_and_power_non_electric == 0; } return v; } // recalc what good this convoy is moving void convoi_t::recalc_catg_index() { goods_catg_index.clear(); for( uint8 i = 0; i < get_vehicle_count(); i++ ) { // Only consider vehicles that really transport something // this helps against routing errors through passenger // trains pulling only freight wagons if(get_vehicle(i)->get_cargo_max() == 0) { continue; } const goods_desc_t *ware=get_vehicle(i)->get_cargo_type(); if(ware!=goods_manager_t::none ) { goods_catg_index.append_unique( ware->get_catg_index() ); } } /* since during composition of convois all kinds of composition could happen, * we do not enforce schedule recalculation here; it will be done anyway all times when leaving the INTI state ... */ } void convoi_t::set_erstes_letztes() { // vehicle_count muss korrekt init sein if(vehicle_count>0) { fahr[0]->set_leading(true); for(unsigned i=1; iset_leading(false); fahr[i - 1]->set_last(false); } fahr[vehicle_count - 1]->set_last(true); } else { dbg->warning("convoi_t::set_erstes_letzes()", "called with vehicle_count==0!"); } } // remove wrong freight when schedule changes etc. void convoi_t::check_freight() { for(unsigned i=0; iremove_stale_cargo(); } calc_loading(); } bool convoi_t::set_schedule(schedule_t * f) { if( state==SELF_DESTRUCT ) { return false; } DBG_DEBUG("convoi_t::set_schedule()", "new=%p, old=%p", f, schedule); assert(f != NULL); // happens to be identical? if(schedule!=f) { // now check, we we have been bond to a line we are about to lose: bool changed = false; if( line.is_bound() ) { if( !f->matches( welt, line->get_schedule() ) ) { // change from line to individual schedule // -> unset line now and register stops from new schedule later changed = true; unset_line(); } } else { if( !f->matches( welt, schedule ) ) { // merely change schedule and do not involve line // -> unregister stops from old schedule now and register stops from new schedule later changed = true; unregister_stops(); } } #ifdef BAD_IDEA // destroy a possibly open schedule window if( schedule && !schedule->is_editing_finished() ) { destroy_win((ptrdiff_t)schedule); delete schedule; } #endif schedule = f; if( changed ) { // if line is unset or schedule is changed // -> register stops from new schedule register_stops(); welt->set_schedule_counter(); // must trigger refresh } } // remove wrong freight check_freight(); // ok, now we have a schedule if(state != INITIAL) { state = EDIT_SCHEDULE; } // to avoid jumping trains alte_richtung = fahr[0]->get_direction(); wait_lock = 0; return true; } schedule_t *convoi_t::create_schedule() { if(schedule == NULL) { const vehicle_t* v = fahr[0]; if (v != NULL) { schedule = v->generate_new_schedule(); schedule->finish_editing(); } } return schedule; } /* checks, if we go in the same direction; * true: convoy prepared * false: must recalculate position * on all error we better use the normal starting procedure ... */ bool convoi_t::can_go_alte_richtung() { // invalid route? nothing to test, must start new if(route.empty()) { return false; } // going backwards? then recalculate all ribi_t::ribi neue_richtung_rwr = ribi_t::backward(fahr[0]->calc_direction(route.front(), route.at(min(2, route.get_count() - 1)))); // DBG_MESSAGE("convoi_t::go_alte_richtung()","neu=%i,rwr_neu=%i,alt=%i",neue_richtung_rwr,ribi_t::backward(neue_richtung_rwr),alte_richtung); if(neue_richtung_rwr&alte_richtung) { akt_speed = 8; return false; } // now get the actual length and the tile length uint16 convoi_length = 15; uint16 tile_length = 24; unsigned i; // for visual C++ const vehicle_t* pred = NULL; for(i=0; ilookup(v->get_pos()); // not last vehicle? // the length of last vehicle does not matter when it comes to positioning of vehicles if ( i+1 < vehicle_count) { convoi_length += v->get_desc()->get_length(); } if(gr==NULL || (pred!=NULL && (abs(v->get_pos().x-pred->get_pos().x)>=2 || abs(v->get_pos().y-pred->get_pos().y)>=2)) ) { // ending here is an error! // this is an already broken train => restart dbg->warning("convoi_t::go_alte_richtung()","broken convoy (id %i) found => fixing!",self.get_id()); akt_speed = 8; return false; } // now check, if ribi is straight and train is not ribi_t::ribi weg_ribi = gr->get_weg_ribi_unmasked(v->get_waytype()); if(ribi_t::is_straight(weg_ribi) && (weg_ribi|v->get_direction())!=weg_ribi) { dbg->warning("convoi_t::go_alte_richtung()","convoy with wrong vehicle directions (id %i) found => fixing!",self.get_id()); akt_speed = 8; return false; } if( pred && pred->get_pos()!=v->get_pos() ) { tile_length += (ribi_t::is_straight(welt->lookup(pred->get_pos())->get_weg_ribi_unmasked(pred->get_waytype())) ? 16 : 8192/vehicle_t::get_diagonal_multiplier())*koord_distance(pred->get_pos(),v->get_pos()); } pred = v; } // check if convoi is way too short (even for diagonal tracks) tile_length += (ribi_t::is_straight(welt->lookup(fahr[vehicle_count-1]->get_pos())->get_weg_ribi_unmasked(fahr[vehicle_count-1]->get_waytype())) ? 16 : 8192/vehicle_t::get_diagonal_multiplier()); if( convoi_length>tile_length ) { dbg->warning("convoi_t::go_alte_richtung()","convoy too short (id %i) => fixing!",self.get_id()); akt_speed = 8; return false; } uint16 length = min((convoi_length/16u)+4u,route.get_count()); // maximum length in tiles to check // we just check, whether we go back (i.e. route tiles other than zero have convoi vehicles on them) for( int index=1; indexlookup(route.at(index)); // now check, if we are already here ... for(unsigned i=0; iobj_ist_da(fahr[i])) { // we are turning around => start slowly and rebuilt train akt_speed = 8; return false; } } } //DBG_MESSAGE("convoi_t::go_alte_richtung()","alte=%d, neu_rwr=%d",alte_richtung,neue_richtung_rwr); // we continue our journey; however later cars need also a correct route entry // eventually we need to add their positions to the convois route koord3d pos = fahr[0]->get_pos(); assert(pos == route.front()); if(welt->lookup(pos)->get_depot()) { return false; } else { for(i=0; iget_pos() && route.at(1) != v->get_pos()) { route.insert(v->get_pos()); } } } // since we need the route for every vehicle of this convoi, // we must set the current route index (instead assuming 1) length = min((convoi_length/8u),route.get_count()-1); // maximum length in tiles to check bool ok=false; for(i=0; iget_pos(); for( route_t::index_t idx = 0; idx<=length; idx++ ) { if(route.at(idx)==vehicle_start_pos) { // set route index, no recalculations necessary v->initialise_journey(idx, false ); ok = true; // check direction uint8 richtung = v->get_direction(); uint8 neu_richtung = v->calc_direction( route.at(max(idx-1,0)), v->get_pos_next()); // we need to move to this place ... if(neu_richtung!=richtung && (i!=0 || vehicle_count==1 || ribi_t::is_bend(neu_richtung)) ) { // 90 deg bend! return false; } break; } } // too short?!? (rather broken then!) if(!ok) { return false; } } return true; } // put the convoi on its way void convoi_t::vorfahren() { // init speed settings sp_soll = 0; set_tiles_overtaking( 0 ); recalc_data_front = true; recalc_data = true; koord3d k0 = route.front(); grund_t *gr = welt->lookup(k0); bool at_dest = false; if(gr && gr->get_depot()) { // start in depot for(unsigned i=0; ilookup(v->get_pos()); if(gr) { gr->obj_remove(v); if(crossing_t *cr = gr->get_crossing()) { ; cr->release_crossing(v); } // eventually unreserve this if( schiene_t* const sch0 = obj_cast(gr->get_weg(fahr[i]->get_waytype())) ) { sch0->unreserve(v); } } v->initialise_journey(0, true); // set at new position gr = welt->lookup(v->get_pos()); assert(gr); v->enter_tile(gr); } // just advances the first vehicle vehicle_t* v0 = fahr[0]; v0->set_leading(false); // switches off signal checks ... v0->set_smoke_enabled(false); steps_driven = 0; // drive half a tile: for(int i=0; ido_drive( (VEHICLE_STEPS_PER_TILE/2)<set_smoke_enabled(true); v0->set_leading(true); // switches on signal checks to reserve the next route // until all other are on the track state = CAN_START; } else { // still leaving depot (steps_driven!=0) or going in other direction or misalignment? if( steps_driven>0 || !can_go_alte_richtung() ) { // start route from the beginning at index 0, place everything on start uint32 train_length = move_to(0); // move one train length to the start position ... // in north/west direction, we leave the vehicle away to start as much back as possible ribi_t::ribi neue_richtung = fahr[0]->get_direction(); if(neue_richtung==ribi_t::south || neue_richtung==ribi_t::east) { // drive the convoi to the same position, but do not hop into next tile! if( train_length%16==0 ) { // any space we need => just add train_length += fahr[vehicle_count-1]->get_desc()->get_length(); } else { // limit train to front of tile train_length += min( (train_length%CARUNITS_PER_TILE)-1, fahr[vehicle_count-1]->get_desc()->get_length() ); } } else { train_length += 1; } train_length = max(1,train_length); // now advance all convoi until it is completely on the track fahr[0]->set_leading(fahr[0]->get_waytype()==air_wt); // switches off signal checks ... uint32 dist = (VEHICLE_STEPS_PER_CARUNIT*train_length) << YARDS_PER_VEHICLE_STEP_SHIFT; for(unsigned i=0; iset_smoke_enabled(false); uint32 const driven = fahr[i]->do_drive( dist ); if (i==0 && driven < dist) { // we are already at our destination at_dest = true; } // this gives the length in carunits, 1/CARUNITS_PER_TILE of a full tile => all cars closely coupled! v->set_smoke_enabled(true); uint32 const vlen = (VEHICLE_STEPS_PER_CARUNIT*v->get_desc()->get_length()) << YARDS_PER_VEHICLE_STEP_SHIFT; if (vlen > dist) { break; } dist = driven - vlen; } fahr[0]->set_leading(true); } if (!at_dest) { state = CAN_START; // to advance more smoothly sint32 restart_speed = -1; if( fahr[0]->can_enter_tile( restart_speed, 0 ) ) { // can reserve new block => drive on if(haltestelle_t::get_halt(k0, owner, fahr[0]->get_waytype() == water_wt).is_bound()) { fahr[0]->play_sound(); } state = DRIVING; } } else { ziel_erreicht(); } } // finally reserve route (if needed) if( fahr[0]->get_waytype()!=air_wt && !at_dest ) { // do not pre-reserve for airplanes for(unsigned i=0; i(welt->lookup(v.get_pos())->get_weg(v.get_waytype()))) { sch0->reserve(self,ribi_t::none); } else { break; } } } wait_lock = 0; INT_CHECK("simconvoi 711"); } void convoi_t::rdwr_convoihandle_t(loadsave_t *file, convoihandle_t &cnv) { if( file->is_version_atleast(112, 3) ) { uint16 id = (file->is_saving() && cnv.is_bound()) ? cnv.get_id() : 0; file->rdwr_short( id ); if (file->is_loading()) { cnv.set_id( id ); } } } void convoi_t::rdwr(loadsave_t *file) { xml_tag_t t( file, "convoi_t" ); sint32 dummy; sint32 owner_n = welt->sp2num(owner); if(file->is_saving()) { if( file->is_version_less(101, 0) ) { file->wr_obj_id("Convoi"); // the matching read is in karte_t::laden(loadsave*)... } } // do the update, otherwise we might lose the line after save & reload if(file->is_saving() && line_update_pending.is_bound()) { check_pending_updates(); } simline_t::rdwr_linehandle_t(file, line); // we want persistent convoihandles so we can keep dialogues open in network games if( file->is_loading() ) { if( file->is_version_less(112, 3) ) { self = convoihandle_t( this ); } else { uint16 id; file->rdwr_short( id ); self = convoihandle_t( this, id ); } } else if( file->is_version_atleast(112, 3) ) { uint16 id = self.get_id(); file->rdwr_short( id ); } dummy = vehicle_count; file->rdwr_long(dummy); vehicle_count = (uint8)dummy; if(file->is_version_less(99, 14)) { // was anz_ready file->rdwr_long(dummy); } sint32 wl = wait_lock; file->rdwr_long(wl); wait_lock = clamp(wl, 0, 0xFFFF); bool dummy_bool=false; file->rdwr_bool(dummy_bool); file->rdwr_long(owner_n); file->rdwr_long(akt_speed); file->rdwr_long(akt_speed_soll); file->rdwr_long(sp_soll); file->rdwr_enum(state); file->rdwr_enum(alte_richtung); // read the yearly income (which has since then become a 64 bit value) // will be recalculated later directly from the history if(file->is_version_less(89, 4)) { file->rdwr_long(dummy); } route.rdwr(file); if(file->is_loading()) { // extend array if requested (only needed for trains) if(vehicle_count > fahr.get_count()) { fahr.resize(vehicle_count, NULL); } owner = welt->get_player_or_create( owner_n ); // sanity check for values ... plus correction if(sp_soll < 0) { sp_soll = 0; } } file->rdwr_str(name_and_id + name_offset, lengthof(name_and_id) - name_offset); if(file->is_loading()) { set_name(name_and_id+name_offset); // will add id automatically } koord3d dummy_pos; if(file->is_saving()) { for(unsigned i=0; iwr_obj_id( fahr[i]->get_typ() ); fahr[i]->rdwr_from_convoi(file); } } else { bool override_monorail = false; needs_electric = false; for( uint8 i=0; ird_obj_id(); vehicle_t *v = 0; const bool first = (i==0); const bool last = (i==vehicle_count-1u); if(override_monorail) { // ignore type for ancient monorails v = new monorail_vehicle_t(file, first, last); } else { switch(typ) { case obj_t::old_automobil: case obj_t::road_vehicle: v = new road_vehicle_t(file, first, last); break; case obj_t::old_waggon: case obj_t::rail_vehicle: v = new rail_vehicle_t(file, first, last); break; case obj_t::old_schiff: case obj_t::water_vehicle: v = new water_vehicle_t(file, first, last); break; case obj_t::old_aircraft: case obj_t::air_vehicle: v = new air_vehicle_t(file, first, last); break; case obj_t::old_monorailwaggon: case obj_t::monorail_vehicle: v = new monorail_vehicle_t(file, first, last); break; case obj_t::maglev_vehicle: v = new maglev_vehicle_t(file, first, last); break; case obj_t::narrowgauge_vehicle: v = new narrowgauge_vehicle_t(file, first, last); break; default: dbg->fatal("convoi_t::convoi_t()","Can't load vehicle type %d", typ); } } // no matching vehicle found? if(v->get_desc()==NULL) { // will create orphan object, but better than crashing at deletion ... dbg->error("convoi_t::convoi_t()","Can't load vehicle and no replacement found!"); i --; vehicle_count --; continue; } // in very old games, monorail was a railway // so we need to convert this // freight will be lost, but game will be loadable if(i==0 && v->get_desc()->get_waytype()==monorail_wt && v->get_typ()==obj_t::rail_vehicle) { override_monorail = true; vehicle_t *v_neu = new monorail_vehicle_t( v->get_pos(), v->get_desc(), v->get_owner(), NULL ); v->discard_cargo(); delete v; v = v_neu; } if(file->is_version_less(99, 4)) { dummy_pos.rdwr(file); } const vehicle_desc_t *info = v->get_desc(); assert(info); // if we load a game from a file which was saved from a // game with a different vehicle.tab, there might be no vehicle // info if(info) { sum_power += info->get_power(); sum_gear_and_power += info->get_power()*info->get_gear(); if (info->get_engine_type() == vehicle_desc_t::electric) { sum_gear_and_power_electric += info->get_power() * info->get_gear(); } else { sum_gear_and_power_non_electric += info->get_power() * info->get_gear(); } sum_weight += info->get_weight(); sum_running_costs -= info->get_running_cost(); sum_fixed_costs -= welt->scale_with_month_length( info->get_fixed_cost() ); has_obsolete |= welt->use_timeline() && info->is_retired( welt->get_timeline_year_month() ); // we do not add maintenance here, the fixed costs are booked as running costs } // some versions save vehicles after leaving depot with koord3d::invalid if(v->get_pos()==koord3d::invalid) { state = INITIAL; } if(state!=INITIAL) { grund_t *gr; gr = welt->lookup(v->get_pos()); if(!gr) { gr = welt->lookup_kartenboden(v->get_pos().get_2d()); if(gr) { dbg->error("convoi_t::rdwr()", "invalid position %s for vehicle %s in state %d (setting to %i,%i,%i)", v->get_pos().get_str(), v->get_name(), state, gr->get_pos().x, gr->get_pos().y, gr->get_pos().z ); v->set_pos( gr->get_pos() ); } else { dbg->fatal("convoi_t::rdwr()", "invalid position %s for vehicle %s in state %d", v->get_pos().get_str(), v->get_name(), state); } state = INITIAL; } // add to blockstrecke if(schiene_t* sch = dynamic_cast(gr->get_weg(v->get_waytype()))) { sch->reserve(self,ribi_t::none); } // add to crossing if(crossing_t *cr = gr->get_crossing()) { cr->finish_rd(); // add crossing logic if needed (as finish_rd() was not yet processed cr->add_to_crossing(v); } if( gr->obj_count()>253 ) { dbg->warning( "convoi_t::rdwr()", "cannot put vehicle on ground at (%s)", gr->get_pos().get_str() ); } gr->obj_add(v); v->clear_flag(obj_t::not_on_map); } else { v->set_flag(obj_t::not_on_map); } // add to convoi fahr[i] = v; } sum_gesamtweight = sum_weight; // cannot run without catenary needs_electric = sum_gear_and_power_electric > 0 && sum_gear_and_power_non_electric == 0; } bool has_schedule = (schedule != NULL); file->rdwr_bool(has_schedule); if(has_schedule) { //DBG_MESSAGE("convoi_t::rdwr()","convoi has a schedule, state %s!",state_names[state]); const vehicle_t* v = fahr[0]; if(file->is_loading() && v) { schedule = v->generate_new_schedule(); } // hack to load corrupted games -> there is a schedule // but no vehicle so we can't determine the exact type of // schedule needed. This hack is safe because convois // without vehicles get deleted right after loading. // Since generic schedules are not allowed, we use a train_schedule_t if(schedule == 0) { schedule = new train_schedule_t(); } // now read the schedule, we have one for sure here schedule->rdwr( file ); } if(file->is_loading()) { next_wolke = 0; calc_loading(); } // calculate new minimum top speed min_top_speed = calc_min_top_speed(fahr, vehicle_count); // since sp_ist became obsolete, sp_soll is used modulo 65536 sp_soll &= 65535; if(file->is_version_less(88, 4)) { // load statistics int j; for (j = 0; j<3; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (j = 2; j<5; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (size_t k = MAX_MONTHS; k-- != 0;) { financial_history[k][CONVOI_DISTANCE] = 0; financial_history[k][CONVOI_MAXSPEED] = 0; financial_history[k][CONVOI_WAYTOLL] = 0; } } else if( file->is_version_less(102, 3) ){ // load statistics for (int j = 0; j<5; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (size_t k = MAX_MONTHS; k-- != 0;) { financial_history[k][CONVOI_DISTANCE] = 0; financial_history[k][CONVOI_MAXSPEED] = 0; financial_history[k][CONVOI_WAYTOLL] = 0; } } else if( file->is_version_less(111, 1) ){ // load statistics for (int j = 0; j<6; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (size_t k = MAX_MONTHS; k-- != 0;) { financial_history[k][CONVOI_MAXSPEED] = 0; financial_history[k][CONVOI_WAYTOLL] = 0; } } else if( file->is_version_less(112, 8) ){ // load statistics for (int j = 0; j<7; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (size_t k = MAX_MONTHS; k-- != 0;) { financial_history[k][CONVOI_WAYTOLL] = 0; } } else { // load statistics for (int j = 0; jrdwr_longlong(financial_history[k][j]); } } } // the convoi odometer if( file->is_version_atleast(102, 3) ){ file->rdwr_longlong( total_distance_traveled); } // since it was saved as an signed int // we recalc it anyhow if(file->is_loading()) { jahresgewinn = 0; for(int i=welt->get_last_month()%12; i>=0; i-- ) { jahresgewinn += financial_history[i][CONVOI_PROFIT]; } } // save/restore pending line updates if(file->is_version_atleast(84, 9) && file->is_version_less(99, 13)) { file->rdwr_long(dummy); // ignore } if(file->is_loading()) { line_update_pending = linehandle_t(); } if(file->is_version_atleast(84, 10)) { home_depot.rdwr(file); } // Old versions recorded last_stop_pos in convoi, not in vehicle koord3d last_stop_pos_convoi = koord3d(0,0,0); if (vehicle_count !=0) { last_stop_pos_convoi = fahr[0]->last_stop_pos; } if(file->is_version_atleast(87, 1)) { last_stop_pos_convoi.rdwr(file); } else { last_stop_pos_convoi = !route.empty() ? route.front() : vehicle_count != 0 ? fahr[0]->get_pos() : koord3d(0, 0, 0); } // for leaving the depot routine if(file->is_version_less(99, 14)) { steps_driven = -1; } else { file->rdwr_short(steps_driven); } // waiting time left ... if(file->is_version_atleast(99, 17)) { if(file->is_saving()) { if( has_schedule && schedule->get_current_entry().waiting_time > 0 ) { uint32 diff_ticks = arrived_time + schedule->get_current_entry().get_waiting_ticks() - welt->get_ticks(); file->rdwr_long(diff_ticks); } else { uint32 diff_ticks = 0xFFFFFFFFu; // write old WAIT_INFINITE value for backwards compatibility file->rdwr_long(diff_ticks); } } else { uint32 diff_ticks = 0; file->rdwr_long(diff_ticks); arrived_time = has_schedule ? welt->get_ticks() - schedule->get_current_entry().get_waiting_ticks() + diff_ticks : 0; } } // since 99015, the last stop will be maintained by the vehicles themselves if(file->is_version_less(99, 15)) { for(unsigned i=0; ilast_stop_pos = last_stop_pos_convoi; } } // overtaking status if(file->is_version_less(100, 1)) { set_tiles_overtaking( 0 ); } else { file->rdwr_byte(tiles_overtaking); set_tiles_overtaking( tiles_overtaking ); } // no_load, withdraw if(file->is_version_less(102, 1)) { no_load = false; withdraw = false; } else { bool value = no_load; file->rdwr_bool(value); no_load = value; value = withdraw; file->rdwr_bool(value); withdraw = value; } if(file->is_version_atleast(111, 1)) { file->rdwr_long( distance_since_last_stop ); file->rdwr_long( sum_speed_limit ); } if( file->is_version_atleast(111, 2) ) { file->rdwr_long( maxspeed_average_count ); } if( file->is_version_atleast(111, 3) ) { file->rdwr_short( next_stop_index ); file->rdwr_short( next_reservation_index ); } if( file->is_loading() ) { reserve_route(); recalc_catg_index(); unloading_state = false; } } void convoi_t::open_info_window() { if( in_depot() ) { // if ownership matches, we can try to open the depot dialog if( get_owner()==welt->get_active_player() ) { grund_t *const ground = welt->lookup( get_home_depot() ); if( ground ) { depot_t *const depot = ground->get_depot(); if( depot ) { depot->show_info(); // try to activate this particular convoy in the depot depot_frame_t *const frame = dynamic_cast( win_get_magic( (ptrdiff_t)depot ) ); if( frame ) { frame->activate_convoi(self); } } } } } else { if( env_t::verbose_debug >= log_t::LEVEL_ERROR ) { dump(); } create_win( new convoi_info_t(self), w_info, magic_convoi_info+self.get_id() ); } } void convoi_t::info(cbuffer_t & buf) const { const vehicle_t* v = fahr[0]; if (v != NULL) { char tmp[128]; buf.printf("\n %d/%dkm/h (%1.2f$/km)\n", speed_to_kmh(min_top_speed), v->get_desc()->get_topspeed(), get_running_cost() / 100.0); buf.printf(" %s: %ikW\n", translator::translate("Leistung"), sum_power); buf.printf(" %s: %ld (%ld) t\n", translator::translate("Gewicht"), (long)sum_weight, (long)(sum_gesamtweight - sum_weight)); buf.printf(" %s: ", translator::translate("Gewinn")); money_to_string(tmp, (double)jahresgewinn); buf.append(tmp); buf.append("\n"); } } // caches the last info; resorts only when needed void convoi_t::get_freight_info(cbuffer_t & buf) { if( old_sort_mode != env_t::default_sortmode ) { old_sort_mode = env_t::default_sortmode; // rebuilt the list with goods ... vector_tpl total_fracht; size_t const n = goods_manager_t::get_count(); ALLOCA(uint32, max_loaded_waren, n); MEMZERON(max_loaded_waren, n); for( uint32 i = 0; i != vehicle_count; ++i ) { const vehicle_t* v = fahr[i]; // first add to capacity indicator const goods_desc_t* ware_desc = v->get_desc()->get_freight_type(); const uint16 menge = v->get_desc()->get_capacity(); if(menge>0 && ware_desc!=goods_manager_t::none) { max_loaded_waren[ware_desc->get_index()] += menge; } // then add the actual load for(ware_t ware : v->get_cargo()) { for(ware_t & tmp : total_fracht) { // could this be joined with existing freight? // for pax: join according next stop // for all others we *must* use target coordinates if( ware.same_destination(tmp) ) { tmp.amount += ware.amount; ware.amount = 0; break; } } // if != 0 we could not join it to existing => load it if(ware.amount != 0) { total_fracht.append(ware); } } INT_CHECK("simconvoi 2643"); } buf.clear(); // apend info on total capacity slist_tpl capacity; for (uint16 i = 0; i != n; ++i) { if(max_loaded_waren[i]>0 && i!=goods_manager_t::INDEX_NONE) { ware_t ware(goods_manager_t::get_info(i)); ware.amount = max_loaded_waren[i]; // append to category? slist_tpl::iterator j = capacity.begin(); slist_tpl::iterator end = capacity.end(); while (j != end && j->get_desc()->get_catg_index() < ware.get_desc()->get_catg_index()) ++j; if (j != end && j->get_desc()->get_catg_index() == ware.get_desc()->get_catg_index()) { j->amount += max_loaded_waren[i]; } else { // not yet there capacity.insert(j, ware); } } } // show new info freight_list_sorter_t::sort_freight(total_fracht, buf, (freight_list_sorter_t::sort_mode_t)env_t::default_sortmode, &capacity, "loaded",halthandle_t()); } } void convoi_t::open_schedule_window( bool show ) { DBG_MESSAGE("convoi_t::open_schedule_window()","Id = %hu, State = %d, Lock = %d", self.get_id(), (int)state, wait_lock); // manipulation of schedule not allowed while: // - just starting // - a line update is pending if( (state==EDIT_SCHEDULE || line_update_pending.is_bound()) && get_owner()==welt->get_active_player() ) { if (show) { create_win( new news_img("Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later!"), w_time_delete, magic_none ); } return; } if(state==DRIVING) { // book the current value of goods calc_gewinn(); } akt_speed = 0; // stop the train ... if(state!=INITIAL) { state = EDIT_SCHEDULE; } wait_lock = 25000; alte_richtung = fahr[0]->get_direction(); ptrdiff_t magic = (ptrdiff_t)(magic_convoi_info + self.get_id()); if( show ) { // Open schedule dialog if( convoi_info_t *info = dynamic_cast(win_get_magic( magic )) ) { info->change_schedule(); top_win(info); } else { create_schedule(); create_win( new convoi_info_t(self,true), w_info, magic ); } // TODO: what happens if no client opens the window?? } schedule->start_editing(); } /** * Check validity of convoi with respect to vehicle constraints */ bool convoi_t::pruefe_alle() { bool ok = vehicle_count == 0 || fahr[0]->get_desc()->can_follow(NULL); unsigned i; const vehicle_t* pred = fahr[0]; for(i = 1; ok && i < vehicle_count; i++) { const vehicle_t* v = fahr[i]; ok = pred->get_desc()->can_lead(v->get_desc()) && v->get_desc()->can_follow(pred->get_desc()); pred = v; } if(ok) { ok = pred->get_desc()->can_lead(NULL); } return ok; } /** * Kontrolliert Be- und Entladen * * minimum_loading is now stored in the object (not returned) */ void convoi_t::laden() { if( state == EDIT_SCHEDULE ) { return; } // just wait a little longer if this is a non-bound halt wait_lock = (WTT_LOADING*2)+(self.get_id())%1024; // find station (ours or public) halthandle_t halt = haltestelle_t::get_halt(schedule->get_current_entry().pos, owner, fahr[0]->get_waytype()); if( halt.is_bound() ) { // queue for (un)loading, does (un)loading once per step halt->request_loading( self ); } } /** * calculate income for last hop */ void convoi_t::calc_gewinn() { sint64 gewinn = 0; for(unsigned i=0; icalc_revenue(v->last_stop_pos, v->get_pos() ); // get_schedule is needed as v->get_waytype() returns track_wt for trams (instead of tram_wt owner->book_revenue(tmp, fahr[0]->get_pos().get_2d(), get_schedule()->get_waytype(), v->get_cargo_type()->get_index() ); v->last_stop_pos = v->get_pos(); } // update statistics of average speed if( distance_since_last_stop ) { financial_history[0][CONVOI_MAXSPEED] *= maxspeed_average_count; financial_history[0][CONVOI_MAXSPEED] += get_speedbonus_kmh(); maxspeed_average_count ++; financial_history[0][CONVOI_MAXSPEED] /= maxspeed_average_count; } distance_since_last_stop = 0; sum_speed_limit = 0; if(gewinn) { jahresgewinn += gewinn; book(gewinn, CONVOI_PROFIT); book(gewinn, CONVOI_REVENUE); } } uint32 convoi_t::get_departure_ticks(uint32 ticks_at_arrival) const { // we need to make it this complicated, otherwise times versus the end of a month could be missed uint32 arrived_month_tick = ticks_at_arrival & ~(welt->ticks_per_world_month - 1); uint32 arrived_ticks = ticks_at_arrival - arrived_month_tick; uint32 delta = schedule->get_current_entry().get_absolute_departures(); uint32 departure_ticks = schedule->get_current_entry().get_waiting_ticks(); // there could be more than one departure per month => find the next one for( uint i = 0; iticks_per_world_month/delta)); if( next_depature_slot > arrived_ticks ) { return arrived_month_tick+next_depature_slot; } } // nothing there => depart slot is first one in next month return arrived_month_tick+welt->ticks_per_world_month+departure_ticks; } /** * convoi an haltestelle anhalten * * minimum_loading is now stored in the object (not returned) */ void convoi_t::hat_gehalten(halthandle_t halt) { // now find out station length uint16 vehicles_loading = 0; if (fahr[0]->get_desc()->get_waytype() == water_wt) { // harbour and river stop load any size vehicles_loading = vehicle_count; } else if (vehicle_count == 1 && CARUNITS_PER_TILE >= fahr[0]->get_desc()->get_length()) { vehicles_loading = 1; // one vehicle, which fits into one tile } else { // difference between actual station length and vehicle lenghts sint16 station_length = -fahr[vehicles_loading]->get_desc()->get_length(); // iterate through tiles in straight line and look for station koord zv = koord(ribi_t::backward(fahr[0]->get_direction())); koord3d pos = fahr[0]->get_pos(); grund_t* gr = welt->lookup(pos); // start on bridge? pos.z += gr->get_weg_yoff() / TILE_HEIGHT_STEP; do { // advance one station tile station_length += CARUNITS_PER_TILE; while (station_length >= 0) { vehicles_loading++; if (vehicles_loading < vehicle_count) { station_length -= fahr[vehicles_loading]->get_desc()->get_length(); } else { // all vehicles fit into station goto station_tile_search_ready; } } // search for next station tile pos += zv; gr = welt->lookup(pos); if (gr == NULL) { gr = welt->lookup(pos - koord3d(0, 0, 1)); if (gr == NULL) { gr = welt->lookup(pos - koord3d(0, 0, 2)); } if (gr && (pos.z != gr->get_hoehe() + gr->get_weg_yoff() / TILE_HEIGHT_STEP)) { // not end/start of bridge break; } } } while (gr && gr->get_halt() == halt); // finished station_tile_search_ready:; } // next stop in schedule will be a depot bool next_depot = false; // check if there is a convoi infront of us bool all_served_this_stop = true; // prepare a list of all destination halts in the schedule vector_tpl destination_halts(schedule->get_count()); if (!no_load) { const uint8 count = schedule->get_count(); bool first_entry = true; for (uint8 i = 1; i < count; i++) { const uint8 wrap_i = (i + schedule->get_current_stop()) % count; const halthandle_t plan_halt = haltestelle_t::get_halt(schedule->entries[wrap_i].pos, owner, fahr[0]->get_waytype()); if (plan_halt == halt) { // we will come later here again ... // the following halt is the same => there will never be a halt to serve all_served_this_stop &= first_entry; break; } else if (!plan_halt.is_bound()) { if (grund_t* gr = welt->lookup(schedule->entries[wrap_i].pos)) { if (gr->get_depot()) { // on our way to depot => there will never be a halt to serve all_served_this_stop &= !first_entry; next_depot = first_entry; // otherwise do not load for stops after a depot break; } } continue; } for (uint8 const idx : goods_catg_index) { if (!halt->get_halt_served_this_step()[idx].is_contained(plan_halt)) { // if overcroding mode, do add overcrowded steps if (!welt->get_settings().is_avoid_overcrowding() || !plan_halt->is_overcrowded(idx)) { // not somebody loaded before us in the queue destination_halts.append(plan_halt); all_served_this_stop = false; break; } } first_entry = false; } } } // only load vehicles in station // don't load when vehicle is being withdrawn bool changed_loading_level = false; unloading_state = true; uint32 time = 0; // min time for loading/unloading uint32 current_tick = welt->get_ticks(); if (last_load_tick == 0) { // first stop here, so no time passed yet last_load_tick = current_tick; } const uint32 loading_ms = current_tick - last_load_tick; last_load_tick = current_tick; // cargo type of previous vehicle that could not be filled const goods_desc_t* cargo_type_prev = NULL; bool wants_more = false; for (unsigned i = 0; i < vehicles_loading; i++) { vehicle_t* v = fahr[i]; // has no freight ... if (fahr[i]->get_cargo_max() == 0) { continue; } const uint32 ms_for_full_load = v->get_desc()->get_loading_time(); uint32 min_loading_step_time = ms_for_full_load / v->get_cargo_max() + 1; time = max(time, min_loading_step_time); uint16 max_amount = next_depot ? 32767 : (v->get_cargo_max() * loading_ms) / (ms_for_full_load + 1) + 1; uint16 amount = v->unload_cargo(halt, next_depot, max_amount); if (amount > 0) { // could unload something, so we are open for new stuff cargo_type_prev = NULL; } if (!all_served_this_stop && max_amount > amount && v->get_total_cargo() < v->get_cargo_max()) { // load if: not unloaded something first or not filled and not forbidden (no_load or next is depot) or that stop has been served already if (cargo_type_prev == NULL || !cargo_type_prev->is_interchangeable(v->get_cargo_type())) { // load amount += v->load_cargo(halt, destination_halts, max_amount - amount); unloading_state &= amount == 0; } if (v->get_total_cargo() < v->get_cargo_max() && amount < max_amount) { // not full, but running out of goods cargo_type_prev = v->get_cargo_type(); } } if (amount) { v->mark_image_dirty(v->get_image(), 0); v->calc_image(); changed_loading_level = true; } wants_more |= (amount == max_amount) && (v->get_total_cargo() < v->get_cargo_max()); } // any unloading/loading went on? if (changed_loading_level) { halt->recalc_status(); calc_loading(); } else { // nothing to unload or load: waiting for cargo unloading_state = false; } loading_limit = schedule->get_current_entry().minimum_loading; if(!no_load || wants_more) { // only check for further waiting, in not no_load is set assert(time > 0 || !wants_more); // time == 0 => wants_more == false, can only happen if all vehicles in station have no transport capacity (i.e., locomotives) wait_lock = min(time, 0xFFFF); // check if departure time is reached if (schedule->get_current_entry().get_absolute_departures()) { sint32 ticks_until_departure = get_departure_ticks() - welt->get_ticks(); if (ticks_until_departure <= 0) { // depart if nothing to load (wants_more==false) if (wants_more) { return; } } else { // else continue loading (even if full) until departure time reached if (wait_lock == 0) { // no loading time imposed, make sure we do not wait too long if the departure is imminent wait_lock = min(WTT_LOADING, ticks_until_departure); } return; } } else { // check for maximum load if (wants_more) { return; } if(loading_level < loading_limit) { // not reached maximum load: do we have to wait more? if (schedule->get_current_entry().waiting_time > 0) { if ((welt->get_ticks() - arrived_time) < schedule->get_current_entry().get_waiting_ticks()) { // continue waiting return; } } else { return; } } } } // loading is finished => maybe drive on if (withdraw && (loading_level == 0 || goods_catg_index.empty())) { // destroy when empty self_destruct(); return; } calc_speedbonus_kmh(); // add available capacity after loading(!) to statistics for (unsigned i = 0; iget_cargo_max()-get_vehicle(i)->get_total_cargo(), CONVOI_CAPACITY); } // Advance schedule schedule->advance(); state = ROUTING_1; loading_limit = 0; INT_CHECK("convoi_t::hat_gehalten"); } sint64 convoi_t::calc_restwert() const { sint64 result = 0; for(uint i=0; icalc_sale_value(); } return result; } /** * Calculate loading_level and loading_limit. This depends on current state (loading or not). */ void convoi_t::calc_loading() { int fracht_max = 0; int fracht_menge = 0; for(unsigned i=0; iget_cargo_max(); fracht_menge += v->get_total_cargo(); } loading_level = fracht_max > 0 ? (fracht_menge*100)/fracht_max : 100; loading_limit = 0; // will be set correctly from hat_gehalten() routine // since weight has changed recalc_data = true; old_sort_mode = 255; } void convoi_t::calc_speedbonus_kmh() { // init with default const sint32 cnv_min_top_kmh = speed_to_kmh( min_top_speed ); speedbonus_kmh = cnv_min_top_kmh; // flying aircraft have 0 friction --> speed not limited by power, so just use top_speed if( front()!=NULL && front()->get_waytype() != air_wt ) { sint32 total_max_weight = 0; sint32 total_weight = 0; for( unsigned i=0; iget_desc(); total_max_weight += desc->get_weight(); total_weight += fahr[i]->get_total_weight(); // convoi_t::sum_gesamweight may not be updated yet when this method is called... if( desc->get_freight_type() == goods_manager_t::none ) { ; // nothing } else if( desc->get_freight_type()->get_catg() == 0 ) { // use full weight for passengers, mail, and special goods total_max_weight += desc->get_freight_type()->get_weight_per_unit() * desc->get_capacity(); } else { // use actual weight for regular goods total_max_weight += fahr[i]->get_cargo_weight(); } } // very old vehicles have zero weight ... if( total_weight>0 ) { speedbonus_kmh = speed_to_kmh( calc_max_speed(sum_gear_and_power, total_max_weight, min_top_speed) ); // convoi overtakers use current actual weight for achievable speed if( front()->get_overtaker() ) { max_power_speed = calc_max_speed(sum_gear_and_power, total_weight, min_top_speed); } } } } // return the current bonus speed sint32 convoi_t::get_speedbonus_kmh() const { if( distance_since_last_stop > 0 && front()!=NULL && front()->get_waytype() != air_wt ) { return min( speedbonus_kmh, (sint32)(sum_speed_limit / distance_since_last_stop) ); } return speedbonus_kmh; } // return the current bonus speed uint32 convoi_t::get_average_kmh() const { if( distance_since_last_stop > 0 ) { return sum_speed_limit / distance_since_last_stop; } return speedbonus_kmh; } /** * Schedule convois for self destruction. Will be executed * upon next sync step */ void convoi_t::self_destruct() { line_update_pending = linehandle_t(); // does not bother to add it to a new line anyway ... // convois in depot are not contained in the map array! if(state==INITIAL) { destroy(); } else { state = SELF_DESTRUCT; wait_lock = 0; } } /** * Helper method to remove convois from the map that cannot * removed normally (i.e. by sending to a depot) anymore. * This is a workaround for bugs in the game. */ void convoi_t::destroy() { // can be only done here, with a valid convoihandle ... if(fahr[0]) { fahr[0]->set_convoi(NULL); } if( state == INITIAL ) { // in depot => not on map for( uint8 i = vehicle_count; i-- != 0; ) { fahr[i]->set_flag( obj_t::not_on_map ); } } state = SELF_DESTRUCT; if(schedule!=NULL && !schedule->is_editing_finished()) { destroy_win((ptrdiff_t)schedule); } if( line.is_bound() ) { // needs to be done here to remove correctly ware catg from lines unset_line(); delete schedule; schedule = NULL; } // pay the current value owner->book_new_vehicle( calc_restwert(), get_pos().get_2d(), fahr[0] ? fahr[0]->get_desc()->get_waytype() : ignore_wt ); for( uint8 i = vehicle_count; i-- != 0; ) { if( !fahr[i]->get_flag( obj_t::not_on_map ) ) { // remove from rails/roads/crossings fahr[i]->set_last( true ); fahr[i]->set_pos_next(koord3d::invalid); // to removed from crossings and reservations fahr[i]->leave_tile(); fahr[i]->set_flag( obj_t::not_on_map ); } // no need to substract maintenance, since it is booked as running costs fahr[i]->discard_cargo(); fahr[i]->cleanup(owner); delete fahr[i]; } vehicle_count = 0; delete this; } /** * Debug info nach stderr */ void convoi_t::dump() const { dbg->debug("convoi::dump()", "\nvehicle_count = %d\n" "wait_lock = %d\n" "owner_n = %d\n" "akt_speed = %d\n" "akt_speed_soll = %d\n" "sp_soll = %d\n" "state = %d\n" "statename = %s\n" "alte_richtung = %d\n" "jahresgewinn = %ld\n" // %lld crashes mingw now, cast gewinn to long ... "name = '%s'\n" "line_id = '%d'\n" "schedule = '%p'", (int)vehicle_count, (int)wait_lock, (int)welt->sp2num(owner), (int)akt_speed, (int)akt_speed_soll, (int)sp_soll, (int)state, (const char *)(state_names[state]), (int)alte_richtung, (long)(jahresgewinn/100), (const char *)name_and_id, line.is_bound() ? line.get_id() : 0, (const void *)schedule ); } void convoi_t::book(sint64 amount, int cost_type) { assert( cost_typebook( amount, simline_t::convoi_to_line_catgory(cost_type) ); } } void convoi_t::init_financial_history() { for (int j = 0; jget_desc()->get_price(); } return purchase_cost; } /** * set line * since convoys must operate on a copy of the route's schedule, we apply a fresh copy */ void convoi_t::set_line(linehandle_t org_line) { // to remove a convoi from a line, call unset_line() if(!org_line.is_bound()) { unset_line(); } if( line.is_bound() ) { unset_line(); } else { // originally a lineless convoy -> unregister itself from stops as it now belongs to a line unregister_stops(); // must trigger refresh if old schedule was not empty if (schedule && !schedule->empty()) { welt->set_schedule_counter(); } } line_update_pending = org_line; check_pending_updates(); } /** * unset line * removes convoy from route without destroying its schedule * => no need to recalculate connections! */ void convoi_t::unset_line() { if( line.is_bound() ) { DBG_DEBUG("convoi_t::unset_line()", "removing old destinations from line=%d, schedule=%p",line.get_id(),schedule); line->remove_convoy(self); line = linehandle_t(); line_update_pending = linehandle_t(); } } void convoi_t::set_update_line(linehandle_t l) { line_update_pending = l; if (state == EDIT_SCHEDULE) { // save, since it is in a step only mode check_pending_updates(); } } // matches two halts; if the pos is not identical, maybe the halt still is the same bool convoi_t::matches_halt( const koord3d pos1, const koord3d pos2 ) { halthandle_t halt1 = haltestelle_t::get_halt(pos1, owner, fahr[0]->get_waytype()); return pos1==pos2 || (halt1.is_bound() && halt1==haltestelle_t::get_halt( pos2, owner, fahr[0]->get_waytype())); } // updates a line schedule and tries to find the best next station to go void convoi_t::check_pending_updates() { if( line_update_pending.is_bound() ) { // create dummy schedule if( schedule==NULL ) { schedule = create_schedule(); } schedule_t* new_schedule = line_update_pending->get_schedule(); int current_stop = schedule->get_current_stop(); // save current position of schedule bool is_same = false; bool is_depot = false; koord3d current = koord3d::invalid, depot = koord3d::invalid; if (schedule->empty() || new_schedule->empty()) { // was no entry or is no entry => goto 1st stop current_stop = 0; } else { // something to check for ... current = schedule->get_current_entry().pos; if( current_stopget_count() && current==new_schedule->entries[current_stop].pos ) { // next pos is the same => keep the convoi state is_same = true; } else { // check depot first (must also keept this state) is_depot = (welt->lookup(current) && welt->lookup(current)->get_depot() != NULL); if(is_depot) { // depot => current_stop+1 (depot will be restore later before this) depot = current; schedule->remove(); current = schedule->get_current_entry().pos; } /* there could be only one entry that matches best: * we try first same sequence as in old schedule; * if not found, we try for same nextnext station * (To detect also places, where only the platform * changed, we also compare the halthandle) */ const koord3d next = schedule->entries[(current_stop+1)%schedule->get_count()].pos; const koord3d nextnext = schedule->entries[(current_stop+2)%schedule->get_count()].pos; const koord3d nextnextnext = schedule->entries[(current_stop+3)%schedule->get_count()].pos; int how_good_matching = 0; const uint8 new_count = new_schedule->get_count(); for( uint8 i=0; ientries[i].pos)*3 + matches_halt(next,new_schedule->entries[(i+1)%new_count].pos)*4 + matches_halt(nextnext,new_schedule->entries[(i+2)%new_count].pos)*2 + matches_halt(nextnextnext,new_schedule->entries[(i+3)%new_count].pos); if( quality>how_good_matching ) { // better match than previous: but depending of distance, the next number will be different if( matches_halt(current,new_schedule->entries[i].pos) ) { current_stop = i; } else if( matches_halt(next,new_schedule->entries[(i+1)%new_count].pos) ) { current_stop = i+1; } else if( matches_halt(nextnext,new_schedule->entries[(i+2)%new_count].pos) ) { current_stop = i+2; } else if( matches_halt(nextnextnext,new_schedule->entries[(i+3)%new_count].pos) ) { current_stop = i+3; } current_stop %= new_count; how_good_matching = quality; } } if(how_good_matching==0) { // nothing matches => take the one from the line current_stop = new_schedule->get_current_stop(); } // if we go to same, then we do not need route recalculation ... is_same = matches_halt(current,new_schedule->entries[current_stop].pos); } } // we may need to update the line and connection tables if( !line.is_bound() ) { line_update_pending->add_convoy(self); } line = line_update_pending; line_update_pending = linehandle_t(); // destroy old schedule and all related windows if(!schedule->is_editing_finished()) { schedule->copy_from( new_schedule ); schedule->set_current_stop(current_stop); // set new schedule current position to best match schedule->start_editing(); } else { schedule->copy_from( new_schedule ); schedule->set_current_stop(current_stop); // set new schedule current position to one before best match } if(is_depot) { // next was depot. restore it schedule->insert(welt->lookup(depot)); schedule->set_current_stop( (schedule->get_current_stop()+schedule->get_count()-1)%schedule->get_count() ); } if (state == EDIT_SCHEDULE) { if (convoi_info_t* info = dynamic_cast(win_get_magic(magic_convoi_info + self.get_id()))) { info->update_schedule(); } } if (state != INITIAL) { // remove wrong freight check_freight(); if(is_same || is_depot) { /* same destination * We are already there => keep current state */ } else { // need re-routing state = EDIT_SCHEDULE; } // make this change immediately if( state!=LOADING ) { wait_lock = 0; } } } } /** * Register the convoy with the stops in the schedule */ void convoi_t::register_stops() { if( schedule ) { for(schedule_entry_t const& i : schedule->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, owner, fahr[0]->get_waytype()==water_wt); if( halt.is_bound() ) { halt->add_convoy(self); } } } } /** * Unregister the convoy from the stops in the schedule */ void convoi_t::unregister_stops() { if( schedule ) { for(schedule_entry_t const& i : schedule->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, owner, schedule->get_waytype()==water_wt); if( halt.is_bound() ) { halt->remove_convoy(self); } } } } // set next stop before breaking will occur (or route search etc.) // currently only used for tracks void convoi_t::set_next_stop_index(route_t::index_t n) { // stop at station or signals, not at waypoints if( n==route_t::INVALID_INDEX ) { // find out if stop or waypoint, waypoint: do not brake at waypoints grund_t const* const gr = welt->lookup(route.back()); if( gr && gr->is_halt() ) { n = route.get_count()-1-1; // extra -1 to brake 1 tile earlier when entering station } } next_stop_index = n+1; } /* including this route_index, the route was reserved the last time * currently only used for tracks */ void convoi_t::set_next_reservation_index(route_t::index_t n) { // stop at station or signals, not at waypoints if( n==route_t::INVALID_INDEX ) { n = route.get_count()-1; } next_reservation_index = n; } /* * the current state saved as color * Meanings are BLACK (ok), WHITE (no convois), YELLOW (no vehicle moved), RED (last month income minus), BLUE (at least one convoi vehicle is obsolete) */ PIXVAL convoi_t::get_status_color() const { if(state==INITIAL) { // in depot/under assembly return SYSCOL_TEXT_HIGHLIGHT; } else if (state == WAITING_FOR_CLEARANCE_ONE_MONTH || state == CAN_START_ONE_MONTH || get_state() == NO_ROUTE) { // stuck or no route return gfx->palette_lookup(COL_ORANGE); } else if(financial_history[0][CONVOI_PROFIT]+financial_history[1][CONVOI_PROFIT]<0) { // ok, not performing best return MONEY_MINUS; } else if((financial_history[0][CONVOI_OPERATIONS]|financial_history[1][CONVOI_OPERATIONS])==0) { // nothing moved return SYSCOL_TEXT_UNUSED; } else if(has_obsolete) { return SYSCOL_OBSOLETE; } // normal state return SYSCOL_TEXT; } // returns station tiles needed for this convoi uint16 convoi_t::get_tile_length() const { uint16 carunits=0; for(uint8 i=0; iget_desc()->get_length(); } // the last vehicle counts differently in stations and for reserving track // (1) add 8 = 127/256 tile to account for the driving in stations in north/west direction // see at the end of vehicle_t::hop() // carunits += max(CARUNITS_PER_TILE/2, fahr[vehicle_count-1]->get_desc()->get_length()); // (2) for length of convoi for loading in stations the length of the last vehicle matters // see convoi_t::hat_gehalten carunits += fahr[vehicle_count-1]->get_desc()->get_length(); uint16 tiles = (carunits + CARUNITS_PER_TILE - 1) / CARUNITS_PER_TILE; return tiles; } // if withdraw and empty, then self destruct void convoi_t::set_withdraw(bool new_withdraw) { withdraw = new_withdraw; if( withdraw && (loading_level==0 || goods_catg_index.empty())) { // test if convoi in depot and not driving grund_t *gr = welt->lookup( get_pos()); if( gr && gr->get_depot() && state == INITIAL ) { #if 1 // do not touch line bound convois in depots withdraw = false; no_load = false; #else // disassemble also line bound convois in depots gr->get_depot()->disassemble_convoi(self, true); #endif } else { self_destruct(); } } } /** * conditions for a city car to overtake another overtaker. * The city car is not overtaking/being overtaken. */ bool convoi_t::can_overtake(overtaker_t *other_overtaker, sint32 other_speed, sint16 steps_other) { if(fahr[0]->get_waytype()!=road_wt) { return false; } if (!other_overtaker->can_be_overtaken()) { return false; } if( other_speed == 0 ) { /* overtaking a loading convoi * => we can do a lazy check, since halts are always straight */ grund_t *gr = welt->lookup(get_pos()); if( gr==NULL ) { // should never happen, since there is a vehicle in front of us ... return false; } weg_t *str = gr->get_weg(road_wt); if( str==0 ) { // also this is not possible, since a car loads in front of is!?! return false; } route_t::index_t idx = fahr[0]->get_route_index(); const sint32 tiles = other_speed == 0 ? 2 : (steps_other-1)/(CARUNITS_PER_TILE*VEHICLE_STEPS_PER_CARUNIT) + get_tile_length() + 1; if( tiles > 0 && idx+(uint32)tiles >= route.get_count() ) { // needs more space than there return false; } for( sint32 i=0; ilookup( route.at( idx+i ) ); if( gr==NULL ) { return false; } // not overtaking on railroad crossings or normal crossings ... if (gr->get_crossing()) { return false; } weg_t *str = gr->get_weg(road_wt); if( str==0 ) { return false; } if( ribi_t::is_threeway(str->get_ribi()) ) { return false; } // Check for other vehicles on the next tile const uint8 top = gr->obj_count(); for( uint8 j=1; j(gr->obj_bei(j)) ) { // check for other traffic on the road const overtaker_t *ov = v->get_overtaker(); if(ov) { if(this!=ov && other_overtaker!=ov) { return false; } } else if( v->get_waytype()==road_wt && v->get_typ()!=obj_t::pedestrian ) { return false; } } } } set_tiles_overtaking( tiles ); return true; } int diff_speed = akt_speed - other_speed; if( diff_speed < kmh_to_speed(5) ) { return false; } // Number of tiles overtaking will take int n_tiles = 0; // Distance it takes overtaking (unit: vehicle_steps) = my_speed * time_overtaking // time_overtaking = tiles_to_overtake/diff_speed // tiles_to_overtake = convoi_length + current pos within tile + (pos_other_convoi within tile + length of other convoi) - one tile sint32 distance = akt_speed*(fahr[0]->get_steps()+get_length_in_steps()+steps_other-VEHICLE_STEPS_PER_TILE)/diff_speed; sint32 time_overtaking = 0; // Conditions for overtaking: // Flat tiles, with no stops, no crossings, no signs, no change of road speed limit // First phase: no traffic except me and my overtaken car in the dangerous zone route_t::index_t route_index = fahr[0]->get_route_index()+1; koord3d pos = fahr[0]->get_pos(); koord3d pos_prev = route_index > 2 ? route.at(route_index-2) : pos; koord3d pos_next; while( distance > 0 ) { if( route_index >= route.get_count() ) { return false; } pos_next = route.at(route_index++); grund_t *gr = welt->lookup(pos); // no ground, or slope => about if( gr==NULL || gr->get_weg_hang()!=slope_t::flat || gr->get_crossing() ) { // no overtaking on slope or system crossings return false; } weg_t *str = gr->get_weg(road_wt); if( str==NULL ) { return false; } if (ribi_t::is_threeway(str->get_ribi())) { // no overtaking on normal way crossings ... return false; } // the only roadsign we must account for are choose points and traffic lights if( str->has_sign() ) { const roadsign_t *rs = gr->find(1); if(rs) { const roadsign_desc_t *rb = rs->get_desc(); if(rb->is_choose_sign() || rb->is_traffic_light() ) { // because we may need to stop here ... return false; } } } // street gets too slow (TODO: should be able to be correctly accounted for) if( akt_speed > kmh_to_speed(str->get_max_speed()) ) { return false; } int d = ribi_t::is_straight(str->get_ribi()) ? VEHICLE_STEPS_PER_TILE : vehicle_base_t::get_diagonal_vehicle_steps_per_tile(); distance -= d; time_overtaking += d; // Check for other vehicles const uint8 top = gr->obj_count(); for( uint8 j=1; j(gr->obj_bei(j))) { // check for other traffic on the road const overtaker_t *ov = v->get_overtaker(); if(ov) { if(this!=ov && other_overtaker!=ov) { return false; } } else if( v->get_waytype()==road_wt && v->get_typ()!=obj_t::pedestrian ) { // sheeps etc. return false; } } } n_tiles++; pos_prev = pos; pos = pos_next; } // Second phase: only facing traffic is forbidden // the original routine was checking using the maximum road speed. // However, we can tolerate slower vehicles if they are closer // Furthermore, if we reach the end of the route for a vehcile as fast as us, // we simply assume it to be ok too sint32 overtaking_distance = time_overtaking; distance = 0; // distance to needed traveled to crash int us from this point time_overtaking = (time_overtaking << 16)/akt_speed; while( time_overtaking > 0 ) { if( route_index >= route.get_count() ) { return distance>=time_overtaking; // we assume ok, if there is enough distance when we would face ourselves } pos_next = route.at(route_index++); grund_t *gr= welt->lookup(pos); if( gr==NULL ) { // will cause a route search, but is ok break; } weg_t *str = gr->get_weg(road_wt); if( str==NULL ) { break; } // cannot check for oncoming traffic over crossings if( ribi_t::is_threeway(str->get_ribi()) ) { return false; } if( ribi_t::is_straight(str->get_ribi()) ) { time_overtaking -= (VEHICLE_STEPS_PER_TILE<<16) / kmh_to_speed(str->get_max_speed()); distance -= VEHICLE_STEPS_PER_TILE; } else { time_overtaking -= (vehicle_base_t::get_diagonal_vehicle_steps_per_tile()<<16) / kmh_to_speed(str->get_max_speed()); distance -= vehicle_base_t::get_diagonal_vehicle_steps_per_tile(); } // Check for other vehicles in facing direction ribi_t::ribi their_direction = ribi_t::backward( fahr[0]->calc_direction(pos_prev, pos_next) ); const uint8 top = gr->obj_count(); for( uint8 j=1; j(gr->obj_bei(j)); if( v && v->get_direction() == their_direction && v->get_overtaker() ) { // tolerated distance us>them: total_distance*akt_speed > current_distance*other_speed if( road_vehicle_t const* const car = obj_cast(v) ) { convoi_t* const ocnv = car->get_convoi(); if( ocnv && ocnv->get_max_power_speed()*distance > akt_speed*overtaking_distance ) { return false; } } else if( private_car_t* const caut = obj_cast(v) ) { if( caut->get_desc()->get_topspeed()*distance > akt_speed*overtaking_distance ) { return false; } } } } pos_prev = pos; pos = pos_next; } set_tiles_overtaking( 1+n_tiles ); other_overtaker->set_tiles_overtaking( -1-(n_tiles*(akt_speed-diff_speed))/akt_speed ); return true; } sint64 convoi_t::get_stat_converted(int month, int cost_type) const { sint64 value = financial_history[month][cost_type]; switch(cost_type) { case CONVOI_REVENUE: case CONVOI_OPERATIONS: case CONVOI_PROFIT: case CONVOI_WAYTOLL: value = convert_money(value); break; default: ; } return value; } const char* convoi_t::send_to_depot(bool local) { // iterate over all depots and try to find shortest route route_t *shortest_route = new route_t(); route_t *route = new route_t(); koord3d home = koord3d::invalid; vehicle_t *v = front(); for(depot_t* const depot : depot_t::get_depot_list()) { if (depot->get_waytype() != v->get_desc()->get_waytype() || depot->get_owner() != get_owner()) { continue; } koord3d pos = depot->get_pos(); if(!shortest_route->empty() && koord_distance(pos, get_pos()) >= shortest_route->get_count()-1) { // the current route is already shorter, no need to search further continue; } if (v->calc_route(get_pos(), pos, 50, route)) { // do not care about speed if( route->get_count() < shortest_route->get_count() || shortest_route->empty() ) { // just swap the pointers sim::swap(shortest_route, route); home = pos; } } } delete route; DBG_MESSAGE("shortest route has ", "%i hops", shortest_route->get_count()-1); if (local) { if (convoi_info_t *info = dynamic_cast(win_get_magic( magic_convoi_info+self.get_id()))) { info->route_search_finished(); } } // if route to a depot has been found, update the convoi's schedule const char *txt; if( !shortest_route->empty() ) { schedule_t *schedule = get_schedule()->copy(); schedule->insert(welt->lookup(home)); schedule->set_current_stop( (schedule->get_current_stop()+schedule->get_count()-1)%schedule->get_count() ); set_schedule(schedule); txt = "Convoi has been sent\nto the nearest depot\nof appropriate type.\n"; } else { txt = "Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually."; } delete shortest_route; return txt; } simutrans-124.5/src/simutrans/simconvoi.h000066400000000000000000000467071520675460500205750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMCONVOI_H #define SIMCONVOI_H #include "simtypes.h" #include "simunits.h" #include "simcolor.h" #include "linehandle.h" #include "dataobj/route.h" #include "vehicle/overtaker.h" #include "tpl/array_tpl.h" #include "tpl/minivec_tpl.h" #include "convoihandle.h" #include "halthandle.h" #define MAX_MONTHS 12 // Max history class weg_t; class depot_t; class karte_ptr_t; class player_t; class vehicle_t; class vehicle_desc_t; class schedule_t; class cbuffer_t; /** * Base class for all vehicle consists. Convoys can be referenced by handles, see halthandle_t. */ class convoi_t : public overtaker_t { public: enum { CONVOI_CAPACITY = 0, // the amount of ware that could be transported, theoretically CONVOI_TRANSPORTED_GOODS, // the amount of ware that has been transported CONVOI_REVENUE, // the income this CONVOI generated CONVOI_OPERATIONS, // the cost of operations this CONVOI generated CONVOI_PROFIT, // total profit of this convoi CONVOI_DISTANCE, // total distance traveled this month CONVOI_MAXSPEED, // average max. possible speed CONVOI_WAYTOLL, MAX_CONVOI_COST // Total number of cost items }; /** Constants */ enum { default_vehicle_length = 4 }; enum states : uint8 { INITIAL, EDIT_SCHEDULE, ROUTING_1, DUMMY4, DUMMY5, NO_ROUTE, DRIVING, LOADING, WAITING_FOR_CLEARANCE, WAITING_FOR_CLEARANCE_ONE_MONTH, CAN_START, CAN_START_ONE_MONTH, SELF_DESTRUCT, WAITING_FOR_CLEARANCE_TWO_MONTHS, CAN_START_TWO_MONTHS, LEAVING_DEPOT, ENTERING_DEPOT, MAX_STATES }; private: /// Current map static karte_ptr_t welt; /* The data is laid out such that the most important variables for sync_step and step are * concentrated at the beginning of the structure. * All computations are for 64bit builds. * * We start with a 16 bytes header from data of overtaker_t :( */ /** * The convoi is not processed every sync step for various actions * (like waiting before signals, loading etc.) Such action will only * continue after a waiting time larger than wait_lock milliseconds */ uint16 wait_lock; /// holds id of line with pending update, or -1 if no pending update linehandle_t line_update_pending; /** * The convoi is gradually loaded for each vehicle, i.e. there is a constant loading speed. * Hence the tick value */ uint32 last_load_tick; // 24 bytes uint16 unloading_state : 1; ///< if state == LOADING, this is true while unloading uint16 recalc_data_front : 1; ///< true, when front vehicle has to recalculate braking uint16 recalc_data : 1; ///< true, when convoy has to recalculate weights and speed limits uint16 recalc_speed_limit : 1; ///< true, when convoy has to recalculate speed limits uint16 previous_delta_v :12; ///< 12 bit! // Stores the previous delta_v value; otherwise these digits are lost during calculation and vehicle do not accelerate uint8 withdraw : 1; ///< the convoi is being withdrawn from service uint8 no_load : 1; ///< nothing will be loaded onto this convoi uint8 has_obsolete : 1; ///< true, if at least one vehicle of a convoi is obsolete bool needs_electric : 1; ///< true, if needs catenary // 5 bits free uint8 old_sort_mode; ///< the convoi caches its freight info; only recalculate after loading or resorting states state; /** * Overall performance with Gear. * Used in movement calculations. */ sint32 sum_gear_and_power; uint32 sum_gear_and_power_non_electric, sum_gear_and_power_electric; // 32 bytes /** * sum_weight: unloaded weight of all vehicles * sum_gesamtweight: total weight of all vehicles * Not stored, but calculated from individual weights * when loading/driving. */ sint64 sum_gesamtweight; sint64 sum_friction_weight; // 48 bytes sint32 akt_speed_soll; ///< target speed sint32 akt_speed; ///< current speed sint32 min_top_speed; ///< Lowest top speed of all vehicles. Doesn't get saved, but calculated from the vehicles data sint32 sp_soll; ///< steps to go // 64 bytes // needed for speed control/calculation sint32 brake_speed_soll; // brake target speed sint32 speed_limit; sint32 max_record_speed; // current convois fastest speed ever /// Number of steps the current convoi did already (only needed for leaving/entering depot) sint16 steps_driven; /** * this gives the index of the next signal or the end of the route * convois will slow down before it, if this is not a waypoint or the cannot pass * The slowdown is done by the vehicle routines */ route_t::index_t next_stop_index; // 80 bytes /// loading_level was minimum_loading before. Actual percentage loaded for loadable vehicles (station length!). /// needed as int, since used by the gui sint32 loading_level; /// At which loading level is the train allowed to start? 0 during driving. /// needed as int, since used by the gui sint32 loading_limit; // things for the world record koord3d record_pos; /// Number of vehicles in this convoi. uint8 vehicle_count; // 96 bytes /// The vehicles of this convoi array_tpl fahr; /** * Route of this convoi - a sequence of coordinates. Actually * the path of the first vehicle */ route_t route; // 128 bytes /// a list of all catg_index, which can be transported by this convoy. minivec_tpl goods_catg_index; /// assigned line linehandle_t line; /// Time when convoi arrived at the current stop /// Used to calculate when it should depart due to the 'month wait time' uint32 arrived_time; // 144 bytes /// All vehicle-schedule pointers point here schedule_t *schedule; /// Convoi owner player_t *owner; // 160 bytes koord3d schedule_target; /// this give the index until which the route has been reserved. /// It is used for restoring reservations after loading a game. route_t::index_t next_reservation_index; /// caches the running costs sint32 sum_running_costs; sint32 sum_fixed_costs; uint32 next_wolke; // time to next smoke /// Overall performance. /// Not used in movement code. uint32 sum_power; /// sum_weight: unloaded weight of all vehicles sint64 sum_weight; // 192 bytes /// accumulated profit over a year sint64 jahresgewinn; /* the odometer */ sint64 total_distance_traveled; uint32 distance_since_last_stop; // number of tiles entered since last stop uint32 sum_speed_limit; // sum of the speed limits encountered since the last stop sint32 speedbonus_kmh; // speed used for speedbonus calculation in km/h sint32 maxspeed_average_count; // just a simple count to average for statistics // 224 bytes /** * the koordinate of the home depot of this convoi * the last depot visited is considered being the home depot */ koord3d home_depot; ribi_t::ribi alte_richtung; /** * Name of the convoi. * @see set_name */ uint8 name_offset; char name_and_id[128]; /// struct holds new financial history for convoi sint64 financial_history[MAX_MONTHS][MAX_CONVOI_COST]; private: /** * Initialize all variables with default values. * Each constructor must call this method first! */ void init(player_t *player); /** * Calculate route from Start to Target Coordinate */ bool drive_to(); /** * Setup vehicles for moving in same direction than before * if the direction is the same as before */ bool can_go_alte_richtung(); /** * remove all track reservations (trains only) */ void unreserve_route(); // reserve route until next_reservation_index void reserve_route(); /** * Mark first and last vehicle. */ void set_erstes_letztes(); // returns the index of the vehicle at position length (16=1 tile) int get_vehicle_at_length(uint16); /** * calculate income for last hop * only used for entering depot or recalculating routes when a schedule window is opened */ void calc_gewinn(); /** * Recalculates loading level and limit. * While driving loading_limit will be set to 0. */ void calc_loading(); /* Calculates (and sets) akt_speed * needed for driving, entering and leaving a depot) */ void calc_acceleration(uint32 delta_t); /** * initialize the financial history */ void init_financial_history(); // matches two halts; if the pos is not identical, maybe the halt still is bool matches_halt( const koord3d pos1, const koord3d pos2 ); /** * Register the convoy with the stops in the schedule */ void register_stops(); /** * Unregister the convoy from the stops in the schedule */ void unregister_stops(); uint32 move_to(route_t::index_t start_index); public: uint32 get_arrival_ticks() const { return arrived_time; } uint32 get_departure_ticks(uint32) const; uint32 get_departure_ticks() const { return get_departure_ticks(arrived_time); } /** * Convoi haelt an Haltestelle und setzt quote fuer Fracht */ void hat_gehalten(halthandle_t halt); const route_t* get_route() const { return &route; } route_t* access_route() { return &route; } const koord3d get_schedule_target() const { return schedule_target; } void set_schedule_target( koord3d t ) { schedule_target = t; } /** * get line */ linehandle_t get_line() const {return line;} /* true, if electrification needed for this convoi */ bool needs_electrification() const { return needs_electric; } /* true, if electrification needed for this convoi */ bool can_use_electrification() const { return sum_gear_and_power_electric!=0; } /** * set line */ void set_line(linehandle_t ); /** * unset line -> remove cnv from line */ void unset_line(); void set_update_line(linehandle_t l); // updates a line schedule and tries to find the best next station to go void check_pending_updates(); // true if this is a waypoint bool is_waypoint( koord3d ) const; /* changes the state of a convoi via tool_t; mandatory for networkmode! * for list of commands and parameter see tool_t::tool_change_convoi_t */ void call_convoi_tool( const char function, const char *extra ) const; /** * set state: only use by tool_t::tool_change_convoi_t */ void set_state( uint16 new_state ) { assert(new_state=WAITING_FOR_CLEARANCE && state<=CAN_START_TWO_MONTHS) && state!=SELF_DESTRUCT; } bool is_unloading() { return state==LOADING && unloading_state; } /** * reset state to no error message */ void reset_waiting() { state=WAITING_FOR_CLEARANCE; } /** * The handle for ourselves. In Anlehnung an 'this' aber mit * allen checks beim Zugriff. */ convoihandle_t self; /** * The profit in this year */ const sint64 & get_jahresgewinn() const {return jahresgewinn;} const sint64 & get_total_distance_traveled() const { return total_distance_traveled; } /** * @return the total monthly fix cost for all vehicles in convoi */ sint64 get_fixed_cost() const { return -sum_fixed_costs; } /** * returns the total running cost for all vehicles in convoi */ sint32 get_running_cost() const { return -sum_running_costs; } /** * returns the total new purchase cost for all vehicles in convoy */ sint64 get_purchase_cost() const; /** * Constructor for loading from file, */ convoi_t(loadsave_t *file); convoi_t(player_t* player); virtual ~convoi_t(); /** * Load or save this convoi data */ void rdwr(loadsave_t *file); /** * method to load/save convoihandle_t */ static void rdwr_convoihandle_t(loadsave_t *file, convoihandle_t &cnv); void finish_rd(); void rotate90( const sint16 y_size ); /** * Called if a vehicle enters a depot */ void betrete_depot(depot_t *dep); /** * Return the internal name of the convois * @return Name of the convois */ const char *get_internal_name() const {return name_and_id+name_offset;} /** * Allows editing ... * @return Name of the Convois */ char *access_internal_name() {return name_and_id+name_offset;} /** * Return the name of the convois * @return Name of the convois */ const char *get_name() const {return name_and_id;} /** * Sets the name. Copies name into this->name and translates it. */ void set_name(const char *name, bool with_new_id = true); /** * Return the position of the convois. * @return Position of the convois */ koord3d get_pos() const; /** * @return current speed, this might be different from topspeed * actual currently set speed. */ const sint32& get_akt_speed() const { return akt_speed; } /** * @return total power of this convoi */ const uint32 & get_sum_power() const {return sum_power;} const sint32 & get_min_top_speed() const {return min_top_speed;} const sint32 & get_speed_limit() const {return speed_limit;} void set_speed_limit(sint32 s) { speed_limit = s;} /// @returns weight of the convoy's vehicles (excluding freight) const sint64 & get_sum_weight() const {return sum_weight;} /// @returns weight of convoy including freight const sint64 & get_sum_gesamtweight() const {return sum_gesamtweight;} /// changes sum_friction_weight, called when vehicle changed tile and friction changes as well. void update_friction_weight(sint64 delta_friction_weight) { sum_friction_weight += delta_friction_weight; } /// @returns theoretical max speed of a convoy with given @p total_power and @p total_weight static sint32 calc_max_speed(uint64 total_power, uint64 total_weight, sint32 speed_limit); uint32 get_length() const; /** * @return length of convoi in the correct units for movement */ uint32 get_length_in_steps() const { return get_length() * VEHICLE_STEPS_PER_CARUNIT; } /** * Add the costs for travelling one tile */ void add_running_cost( const weg_t *weg ); /** * moving the vehicles of a convoi and acceleration/deceleration * all other stuff => convoi_t::step() */ sync_result sync_step(uint32 delta_t); /** * All things like route search or loading, that may take a little */ void step(); /** * sets a new convoi in route */ void start(); void ziel_erreicht(); ///< Called, when the first vehicle reaches the target /** * When a vehicle has detected a problem * force calculate a new route */ void suche_neue_route(); /** * Wait until vehicle 0 reports free route * will be called during a hop_check, if the road/track is blocked */ void warten_bis_weg_frei(sint32 restart_speed); /** * @return Vehicle count */ uint8 get_vehicle_count() const { return vehicle_count; } /** * @return Vehicle at position i */ vehicle_t* get_vehicle(uint16 i) const { return fahr[i]; } vehicle_t* front() const { return fahr[0]; } vehicle_t* back() const { return fahr[vehicle_count - 1]; } /** * Adds a vehicle at the start or end of the convoi. */ bool add_vehicle(vehicle_t *v, bool infront = false); /** * Removes vehicles at position i */ vehicle_t * remove_vehicle_at(unsigned short i); const minivec_tpl &get_goods_catg_index() const { return goods_catg_index; } // recalculates the good transported by this convoy and (in case of changes) will start schedule recalculation void recalc_catg_index(); /** * Sets a schedule */ bool set_schedule(schedule_t *f); schedule_t* get_schedule() const { return schedule; } /** * Creates a new schedule if there isn't one already. */ schedule_t * create_schedule(); // remove wrong freight when schedule changes etc. void check_freight(); /** * @return Owner of this convoi */ player_t * get_owner() const { return owner; } /** * Opens an information window * @see simwin */ void open_info_window(); /** * @param[out] buf a description string for the object, der z.B. in einem * Beobachtungsfenster angezeigt wird. * @see simwin */ void info(cbuffer_t & buf) const; /** * @param[out] buf Filled with freight description */ void get_freight_info(cbuffer_t & buf); void reset_freight_info() { old_sort_mode = 255; } /** * Opens the schedule window * @see simwin */ void open_schedule_window( bool show ); /** * pruefe ob Beschraenkungen fuer alle Fahrzeuge erfuellt sind */ bool pruefe_alle(); /** * Control loading and unloading */ void laden(); /** * Setup vehicles before starting to move */ void vorfahren(); /** * Calculate the total value of the convoi as the sum of all vehicle values. */ sint64 calc_restwert() const; /** * Check if this convoi has entered a depot. */ bool in_depot() const { return state == INITIAL; } /** * loading_level was minimum_loading before. Actual percentage loaded of loadable * vehicles. */ const sint32 &get_loading_level() const { return loading_level; } /** * At which loading level is the train allowed to start? 0 during driving. */ const sint32 &get_loading_limit() const { return loading_limit; } /** * Schedule convois for self destruction. Will be executed * upon next sync step */ void self_destruct(); /** * Helper method to remove convois from the map that cannot * removed normally (i.e. by sending to a depot) anymore. * This is a workaround for bugs in the game. */ void destroy(); /** * Debug info to stderr */ void dump() const; /** * book a certain amount into the convois financial history * is called from vehicle during un/load */ void book(sint64 amount, int cost_type); /** * return a pointer to the financial history */ sint64* get_finance_history() { return *financial_history; } /** * return a specified element from the financial history */ sint64 get_finance_history(int month, int cost_type) const { return financial_history[month][cost_type]; } sint64 get_stat_converted(int month, int cost_type) const; /** * only purpose currently is to roll financial history */ void new_month(); /** * Method for yearly action */ void new_year(); void set_home_depot(koord3d hd) { home_depot = hd; } koord3d get_home_depot() { return home_depot; } /** * Sends convoi to nearest depot. * Has to be called synchronously on all clients in networkmode! * @returns success message */ const char* send_to_depot(bool local); /** * this give the index of the next signal or the end of the route * convois will slow down before it, if this is not a waypoint or the cannot pass * The slowdown is done by the vehicle routines */ route_t::index_t get_next_stop_index() const { return next_stop_index; } void set_next_stop_index(route_t::index_t n); /* including this route_index, the route was reserved the last time * currently only used for tracks */ route_t::index_t get_next_reservation_index() { return next_reservation_index; } void set_next_reservation_index(route_t::index_t n); /* the current state of the convoi */ PIXVAL get_status_color() const; // returns station tiles needed for this convoi uint16 get_tile_length() const; bool has_obsolete_vehicles() const { return has_obsolete; } bool get_withdraw() const { return withdraw; } void set_withdraw(bool new_withdraw); bool get_no_load() const { return no_load; } void set_no_load(bool new_no_load) { no_load = new_no_load; } void must_recalc_data() { recalc_data = true; } void must_recalc_data_front() { recalc_data_front = true; } void must_recalc_speed_limit() { recalc_speed_limit = true; } // calculates the speed used for the speedbonus base, and the max achievable speed at current power/weight for overtakers void calc_speedbonus_kmh(); sint32 get_speedbonus_kmh() const; // just a guess of the speed uint32 get_average_kmh() const; // Overtaking for convois bool can_overtake(overtaker_t *other_overtaker, sint32 other_speed, sint16 steps_other) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/simdebug.cc000066400000000000000000000013061520675460500205060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "simdebug.h" #include "utils/log.h" /** * The log for all messages */ log_t *dbg = NULL; /** * Inits logging facility. */ void init_logging(const char* logname, bool force_flush, bool log_debug, const char* greeting, const char* syslogtag ) { dbg = new log_t( logname, force_flush, log_debug, true, greeting, syslogtag ); } #if (MSG_LEVEL >= 1) #ifdef _MSC_VER int __cdecl _purecall() #else extern "C" NORETURN void __cxa_pure_virtual() #endif { dbg->fatal("unknown", "pure virtual function call"); #ifdef _MSC_VER return -1; #endif } #endif simutrans-124.5/src/simutrans/simdebug.h000066400000000000000000000025101520675460500203460ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMDEBUG_H #define SIMDEBUG_H // do not check assertions //#define NDEBUG 1 // check assertions //#undef NDEBUG //#define NDEBUG #include #include "utils/log.h" /** * Logger instance, this is a globally exported object. */ extern log_t *dbg; /** * Inits logging facility. */ void init_logging(const char *logname, bool force_flush, bool log_debug, const char *greeting, const char* syslogtag ); #define DO_EXPAND(VAL) VAL ## 1 #define EXPAND(VAL) DO_EXPAND(VAL) #if !defined(MSG_LEVEL) || (EXPAND(MSG_LEVEL) == 1) #undef MSG_LEVEL #if defined(DEBUG) #define MSG_LEVEL 4 #else #define MSG_LEVEL 0 #endif #endif #undef DO_EXPAND #undef EXPAND #if MSG_LEVEL >0 #define PAKSET_INFO dbg->pakset #endif #if MSG_LEVEL >= 4 #define DBG_DEBUG4 dbg->debug #define DBG_MESSAGE dbg->message #define DBG_DEBUG dbg->message #elif MSG_LEVEL >= 3 #define DBG_DEBUG4(i,...) ; #define DBG_MESSAGE dbg->message #define DBG_DEBUG dbg->message #elif MSG_LEVEL >= 1 #define DBG_DEBUG4(i,...) ; #define DBG_MESSAGE(i,...) ; #define DBG_DEBUG dbg->message #else // nothing to debug -> then ignore #define PAKSET_INFO(i,...) ; #define DBG_DEBUG4(i,...) ; #define DBG_MESSAGE(i,...) ; #define DBG_DEBUG(i,...) ; #endif #endif simutrans-124.5/src/simutrans/simevent.cc000066400000000000000000000164621520675460500205520ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simevent.h" #include "sys/simsys.h" #include "tpl/slist_tpl.h" // system-independent event handling routines static int cx = -1; // coordinates of last mouse click event static int cy = -1; // initialised to "nowhere" static bool is_dragging = false; static int control_shift_state = 0; // none pressed static event_t meta_event(EVENT_NONE); // for storing meta-events like double-clicks and triple-clicks static event_class_t last_meta_class = EVENT_NONE; static slist_tpl queued_events; event_t::event_t(event_class_t event_class) : ev_class(event_class), ev_code(0) { } void event_t::move_origin(scr_coord delta) { mouse_pos -= delta; click_pos -= delta; } int event_get_last_control_shift() { return control_shift_state & (SIM_KEYMOD_SHIFT | SIM_KEYMOD_CTRL); } event_class_t last_meta_event_get_class() { return last_meta_class; } /** * each drag event contains the origin of the first click. * if the window is being dragged, it is convenient to change this * so the origin keeps pointing to the window top bar. * Mainly to prevent copied, double code. */ void change_drag_start(scr_coord offset) { cx += offset.x; cy += offset.y; } // since finger events work with absolute coordinates void set_click_xy(scr_coord_val x, scr_coord_val y) { cx = x; cy = y; } static void fill_event(event_t* const ev) { // variables for detecting double-clicks and triple-clicks const uint32 interval = 400; static unsigned int prev_ev_class = EVENT_NONE; static uint16 prev_ev_code = 0; static uint32 prev_ev_time = 0; static uint8 repeat_count = 0; // number of consecutive sequences of click-release // for autorepeat buttons we track button state, press time and a repeat time static int pressed_buttons = 0; // assume: at startup no button pressed (needed for some backends) ev->ev_class = EVENT_NONE; static scr_coord last_mpos; ev->mouse_pos.x = sys_event.mx; ev->mouse_pos.y = sys_event.my; ev->click_pos.x = cx; ev->click_pos.y = cy; // always put key mod code into event ev->ev_key_mod = sys_event.key_mod; control_shift_state = sys_event.key_mod; switch (sys_event.type) { case SIM_NOEVENT: case SIM_IGNORE_EVENT: break; case SIM_KEYBOARD: ev->ev_class = EVENT_KEYDOWN; ev->ev_code = sys_event.code; break; case SIM_STRING: ev->ev_class = EVENT_STRING; ev->ev_ptr = sys_event.ptr; break; case SIM_MOUSE_BUTTONS: // press only acknowledged when no buttons are pressed pressed_buttons = sys_event.mb; switch (sys_event.code) { case SIM_MOUSE_LEFTBUTTON: ev->ev_class = EVENT_CLICK; pressed_buttons |= MOUSE_LEFTBUTTON; ev->ev_code = MOUSE_LEFTBUTTON; ev->click_pos.x = cx = sys_event.mx; ev->click_pos.y = cy = sys_event.my; is_dragging = true; break; case SIM_MOUSE_RIGHTBUTTON: ev->ev_class = EVENT_CLICK; pressed_buttons |= MOUSE_RIGHTBUTTON; ev->ev_code = MOUSE_RIGHTBUTTON; ev->click_pos.x = cx = sys_event.mx; ev->click_pos.y = cy = sys_event.my; is_dragging = true; break; case SIM_MOUSE_MIDBUTTON: ev->ev_class = EVENT_CLICK; pressed_buttons |= MOUSE_MIDBUTTON; ev->ev_code = MOUSE_MIDBUTTON; ev->click_pos.x = cx = sys_event.mx; ev->click_pos.y = cy = sys_event.my; is_dragging = true; break; case SIM_MOUSE_WHEELUP: ev->ev_class = EVENT_CLICK; ev->ev_code = MOUSE_WHEELUP; ev->click_pos.x = cx = sys_event.mx; ev->click_pos.y = cy = sys_event.my; break; case SIM_MOUSE_WHEELDOWN: ev->ev_class = EVENT_CLICK; ev->ev_code = MOUSE_WHEELDOWN; ev->click_pos.x = cx = sys_event.mx; ev->click_pos.y = cy = sys_event.my; break; case SIM_MOUSE_LEFTUP: ev->ev_class = EVENT_RELEASE; ev->ev_code = MOUSE_LEFTBUTTON; pressed_buttons &= ~MOUSE_LEFTBUTTON; is_dragging = false; break; case SIM_MOUSE_RIGHTUP: ev->ev_class = EVENT_RELEASE; ev->ev_code = MOUSE_RIGHTBUTTON; pressed_buttons &= ~MOUSE_RIGHTBUTTON; is_dragging = false; break; case SIM_MOUSE_MIDUP: ev->ev_class = EVENT_RELEASE; ev->ev_code = MOUSE_MIDBUTTON; pressed_buttons &= ~MOUSE_MIDBUTTON; is_dragging = false; break; } break; case SIM_MOUSE_MOVE: if (last_mpos == ev->mouse_pos) { ev->ev_class = EVENT_NONE; ev->ev_code = 0; break; } if (sys_event.mb) { // drag ev->ev_class = EVENT_DRAG; ev->ev_code = sys_event.mb; if( !is_dragging) { // we just start dragging, since the click was not delivered it seems ev->click_pos.x = cx = sys_event.mx; ev->click_pos.y = cy = sys_event.my; is_dragging = true; } } else { // move ev->ev_class = EVENT_MOVE; ev->ev_code = 0; is_dragging = false; } break; case SIM_SYSTEM: ev->ev_class = EVENT_SYSTEM; ev->ev_code = sys_event.code; ev->new_window_size = scr_size(sys_event.new_window_size_w, sys_event.new_window_size_h); break; } // check for double-clicks and triple-clicks const uint32 curr_time = dr_time(); if( ev->ev_class==EVENT_CLICK ) { if( prev_ev_class==EVENT_RELEASE && prev_ev_code==ev->ev_code && curr_time-prev_ev_time<=interval ) { // case : a mouse click which forms an unbroken sequence with the previous clicks and releases prev_ev_class = EVENT_CLICK; prev_ev_time = curr_time; } else { // case : initial click or broken click-release sequence -> prepare for the start of a new sequence prev_ev_class = EVENT_CLICK; prev_ev_code = ev->ev_code; prev_ev_time = curr_time; repeat_count = 0; } } else if( ev->ev_class==EVENT_RELEASE && prev_ev_class==EVENT_CLICK && prev_ev_code==ev->ev_code && curr_time-prev_ev_time<=interval ) { // case : a mouse release which forms an unbroken sequence with the previous clicks and releases prev_ev_class = EVENT_RELEASE; prev_ev_time = curr_time; ++repeat_count; // create meta-events where necessary if( repeat_count==2 ) { // case : double-click meta_event = *ev; meta_event.ev_class = EVENT_DOUBLE_CLICK; } else if( repeat_count==3 ) { // case : triple-click meta_event = *ev; meta_event.ev_class = EVENT_TRIPLE_CLICK; repeat_count = 0; // reset -> start over again } } else if( ev->ev_class!=EVENT_NONE && prev_ev_class!=EVENT_NONE ) { // case : broken click-release sequence -> simply reset prev_ev_class = EVENT_NONE; prev_ev_code = 0; prev_ev_time = 0; repeat_count = 0; } ev->mouse_button_state = pressed_buttons; last_mpos = ev->mouse_pos; } void display_poll_event(event_t* const ev) { if( !queued_events.empty() ) { // We have a queued (injected programatically) event, return it. event_t *elem = queued_events.remove_first(); *ev = *elem; delete elem; } else if( meta_event.ev_class!=EVENT_NONE ) { // if there is any pending meta-event, consume it instead of fetching a new event from the system *ev = meta_event; last_meta_class = meta_event.ev_class; meta_event.ev_class = EVENT_NONE; } else { last_meta_class = EVENT_NONE; GetEvents(); fill_event(ev); // prepare for next event sys_event.type = SIM_NOEVENT; sys_event.code = 0; } } void queue_event(event_t *events) { queued_events.append(events); } simutrans-124.5/src/simutrans/simevent.h000066400000000000000000000210371520675460500204060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMEVENT_H #define SIMEVENT_H #include "display/scr_coord.h" /* Messageverarbeitung */ /* Event Classes */ enum event_class_t { EVENT_NONE = 0, EVENT_KEYDOWN = 1, EVENT_KEYUP = 2, EVENT_STRING = 3, ///< instead of a single character a ev_ptr points to an utf8 string EVENT_CLICK = 4, EVENT_DOUBLE_CLICK = 5, ///< 2 consecutive sequences of click-release EVENT_TRIPLE_CLICK = 6, ///< 3 consecutive sequences of click-release EVENT_RELEASE = 7, EVENT_MOVE = 8, EVENT_DRAG = 9, INFOWIN = 10, ///< window event, i.e. WIN_OPEN, WIN_CLOSE WINDOW_RESIZE = 11, WINDOW_MAKE_MIN_SIZE = 12, WINDOW_CHOOSE_NEXT = 13, EVENT_SYSTEM = 254, IGNORE_EVENT = 255 }; /* Event Codes */ #define MOUSE_LEFTBUTTON 1 #define MOUSE_RIGHTBUTTON 2 #define MOUSE_MIDBUTTON 4 #define MOUSE_WHEELUP 8 #define MOUSE_WHEELDOWN 16 #define WIN_OPEN 1 #define WIN_CLOSE 2 #define WIN_TOP 3 #define WIN_UNTOP 4 // losing focus #define NEXT_WINDOW 1 #define PREV_WINDOW 2 #define SYSTEM_QUIT 1 #define SYSTEM_RESIZE 2 #define SYSTEM_RELOAD_WINDOWS 3 #define SYSTEM_THEME_CHANGED 4 /* normal keys have range 0-255, special key follow above 255 */ /* other would be better for true unicode support :( */ enum sim_keycode_t { SIM_KEYCODE_BACKSPACE = 8, SIM_KEYCODE_TAB = 9, SIM_KEYCODE_ENTER = 13, SIM_KEYCODE_ESCAPE = 27, SIM_KEYCODE_SPACE = 32, SIM_KEYCODE_DELETE = 127, #define PRIVATE_KEY (0xE000u) /* Function keys */ SIM_KEYCODE_F1 = PRIVATE_KEY + 256, SIM_KEYCODE_F2 = PRIVATE_KEY + 257, SIM_KEYCODE_F3 = PRIVATE_KEY + 258, SIM_KEYCODE_F4 = PRIVATE_KEY + 259, SIM_KEYCODE_F5 = PRIVATE_KEY + 260, SIM_KEYCODE_F6 = PRIVATE_KEY + 261, SIM_KEYCODE_F7 = PRIVATE_KEY + 262, SIM_KEYCODE_F8 = PRIVATE_KEY + 263, SIM_KEYCODE_F9 = PRIVATE_KEY + 264, SIM_KEYCODE_F10 = PRIVATE_KEY + 265, SIM_KEYCODE_F11 = PRIVATE_KEY + 266, SIM_KEYCODE_F12 = PRIVATE_KEY + 267, SIM_KEYCODE_F13 = PRIVATE_KEY + 268, SIM_KEYCODE_F14 = PRIVATE_KEY + 269, SIM_KEYCODE_F15 = PRIVATE_KEY + 270, /* other navigation keys */ SIM_KEYCODE_HOME = PRIVATE_KEY + 275, SIM_KEYCODE_END = PRIVATE_KEY + 276, SIM_KEYCODE_PGUP = PRIVATE_KEY + 277, SIM_KEYCODE_PGDN = PRIVATE_KEY + 278, SIM_KEYCODE_SCROLLLOCK = PRIVATE_KEY + 279, SIM_KEYCODE_PAUSE = PRIVATE_KEY + 279, /* arrow (direction) keys */ SIM_KEYCODE_NUMPAD_BASE = PRIVATE_KEY + 280, // 0 on keypad SIM_KEYCODE_DOWNLEFT, SIM_KEYCODE_DOWN, SIM_KEYCODE_DOWNRIGHT, SIM_KEYCODE_LEFT, SIM_KEYCODE_CENTER, SIM_KEYCODE_RIGHT, SIM_KEYCODE_UPLEFT, SIM_KEYCODE_UP, SIM_KEYCODE_UPRIGHT, }; enum sim_keymod_t { SIM_KEYMOD_NONE = 0, SIM_KEYMOD_SHIFT = 1 << 0, SIM_KEYMOD_CTRL = 1 << 1, SIM_KEYMOD_ALT = 1 << 2, ///< Currently unused }; /** * Slight explanation of event_t structure: * ev_class and ev_code speak for itself. * ev_class = EVENT_NONE: nothing defined * ev_class = EVENT_KEYBOARD: code = key pressed (released key is ignored) * ev_class = EVENT_CLICK: mouse_pos/click_pos point to mouse click place, * code = pressed mouse button * ev_class = EVENT_RELEASE: click_pos points to mouse click place, * mouse_pos points to mouse release place, * code = mouse release button * ev_class = EVENT_MOVE: click_pos is last click place, mouse_pos is to. * ev_class = EVENT_DRAG: click_pos is last click place, mouse_pos is to, * code = mouse button * ev_class = EVENT_REPEAT: code = button pressed */ struct event_t { public: explicit event_t(event_class_t event_class = EVENT_NONE); public: /** * Move event origin. Useful when transferring events to sub-components. * @param delta position of new origin relative to the old origin. */ void move_origin(scr_coord delta); public: event_class_t ev_class = EVENT_NONE; union { unsigned int ev_code; void *ev_ptr; }; // Mouse position scr_coord mouse_pos = { 0, 0 }; /// position of last mouse click scr_coord click_pos = { 0, 0 }; /// new window size for SYSTEM_RESIZE scr_size new_window_size = { 0, 0 }; /// bitset indicating which mouse buttons are pressed int mouse_button_state = 0; /// mod key (SHIFT, CTRL etc) pressed while event as triggered unsigned int ev_key_mod = SIM_KEYMOD_NONE; }; static inline bool IS_KEYDOWN (const event_t *ev) { return ev->ev_class == EVENT_KEYDOWN; } static inline bool IS_KEYUP (const event_t *ev) { return ev->ev_class == EVENT_KEYUP; } static inline bool IS_KEYBOARD (const event_t *ev) { return IS_KEYDOWN(ev) || IS_KEYUP(ev); } static inline bool IS_MOUSE(const event_t *ev) { return ev->ev_class >= EVENT_CLICK && ev->ev_class <= EVENT_DRAG; } static inline bool IS_LEFTCLICK (const event_t *ev) { return ev->ev_class == EVENT_CLICK && ev->ev_code == MOUSE_LEFTBUTTON; } static inline bool IS_LEFTRELEASE(const event_t *ev) { return ev->ev_class == EVENT_RELEASE && ev->ev_code == MOUSE_LEFTBUTTON; } static inline bool IS_LEFTDRAG (const event_t *ev) { return ev->ev_class == EVENT_DRAG && ev->ev_code == MOUSE_LEFTBUTTON; } static inline bool IS_LEFTDBLCLK (const event_t *ev) { return ev->ev_class == EVENT_DOUBLE_CLICK && ev->ev_code == MOUSE_LEFTBUTTON; } static inline bool IS_LEFTTPLCLK (const event_t *ev) { return ev->ev_class == EVENT_TRIPLE_CLICK && ev->ev_code == MOUSE_LEFTBUTTON; } static inline bool IS_RIGHTCLICK (const event_t *ev) { return ev->ev_class == EVENT_CLICK && ev->ev_code == MOUSE_RIGHTBUTTON; } static inline bool IS_RIGHTRELEASE(const event_t *ev) { return ev->ev_class == EVENT_RELEASE && ev->ev_code == MOUSE_RIGHTBUTTON; } static inline bool IS_RIGHTDRAG (const event_t *ev) { return ev->ev_class == EVENT_DRAG && ev->ev_code == MOUSE_RIGHTBUTTON; } static inline bool IS_RIGHTDBLCLK (const event_t *ev) { return ev->ev_class == EVENT_DOUBLE_CLICK && ev->ev_code == MOUSE_RIGHTBUTTON; } static inline bool IS_RIGHTTPLCLK (const event_t *ev) { return ev->ev_class == EVENT_TRIPLE_CLICK && ev->ev_code == MOUSE_RIGHTBUTTON; } static inline bool IS_WHEELUP (const event_t *ev) { return ev->ev_class == EVENT_CLICK && ev->ev_code == MOUSE_WHEELUP; } static inline bool IS_WHEELDOWN (const event_t *ev) { return ev->ev_class == EVENT_CLICK && ev->ev_code == MOUSE_WHEELDOWN; } static inline bool IS_WINDOW_RESIZE (const event_t *ev) { return ev->ev_class == WINDOW_RESIZE; } static inline bool IS_WINDOW_MAKE_MIN_SIZE(const event_t *ev) { return ev->ev_class == WINDOW_MAKE_MIN_SIZE; } static inline bool IS_WINDOW_CHOOSE_NEXT (const event_t *ev) { return ev->ev_class == WINDOW_CHOOSE_NEXT; } // This function is to determine if the event should be also handled by children of containers. static inline bool DOES_WINDOW_CHILDREN_NEED(const event_t *ev) { return ev->ev_class == INFOWIN || ev->ev_class == WINDOW_RESIZE || ev->ev_class == WINDOW_MAKE_MIN_SIZE; } static inline bool IS_WINDOW_TOP(const event_t *ev) { return ev->ev_class == INFOWIN || ev->ev_code == WIN_TOP; } static inline bool IS_LEFT_BUTTON_PRESSED (const event_t *ev) { return ev->mouse_button_state & MOUSE_LEFTBUTTON; } static inline bool IS_RIGHT_BUTTON_PRESSED (const event_t *ev) { return ev->mouse_button_state & MOUSE_RIGHTBUTTON; } static inline bool IS_MIDDLE_BUTTON_PRESSED(const event_t *ev) { return ev->mouse_button_state & MOUSE_MIDBUTTON; } static inline bool IS_SHIFT_PRESSED (const event_t *ev) { return ev->ev_key_mod & SIM_KEYMOD_SHIFT; } static inline bool IS_CONTROL_PRESSED(const event_t *ev) { return ev->ev_key_mod & SIM_KEYMOD_CTRL; } /// Return one event. Does *not* wait. void display_poll_event(event_t *event); void change_drag_start(scr_coord offset); void set_click_xy(scr_coord_val x, scr_coord_val y); int event_get_last_control_shift(); event_class_t last_meta_event_get_class(); /// Get mouse pointer position. Implementation in simsys.cc scr_coord get_mouse_pos(); /// Adds new events to be processed. void queue_event(event_t *event); #endif simutrans-124.5/src/simutrans/simfab.cc000066400000000000000000003324271520675460500201630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include "simdebug.h" #include "display/simimg.h" #include "simcolor.h" #include "ground/grund.h" #include "ground/boden.h" #include "ground/fundament.h" #include "simfab.h" #include "world/simcity.h" #include "simhalt.h" #include "simware.h" #include "world/simworld.h" #include "descriptor/building_desc.h" #include "descriptor/goods_desc.h" #include "descriptor/sound_desc.h" #include "player/simplay.h" #include "simintr.h" #include "obj/wolke.h" #include "obj/gebaeude.h" #include "obj/field.h" #include "obj/leitung2.h" #include "dataobj/settings.h" #include "dataobj/environment.h" #include "dataobj/translator.h" #include "dataobj/loadsave.h" #include "dataobj/pakset_manager.h" #include "descriptor/factory_desc.h" #include "builder/hausbauer.h" #include "builder/goods_manager.h" #include "builder/fabrikbauer.h" #include "gui/fabrik_info.h" #include "utils/simrandom.h" #include "utils/cbuffer.h" #include "gui/simwin.h" #include "display/simgraph.h" // Fabrik_t static const int FAB_MAX_INPUT = 15000; // Half a display unit (0.5). static const sint64 FAB_DISPLAY_UNIT_HALF = ((sint64)1 << (fabrik_t::precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS - 1)); // Half a production factor unit (0.5). static const sint32 FAB_PRODFACT_UNIT_HALF = ((sint32)1 << (DEFAULT_PRODUCTION_FACTOR_BITS - 1)); karte_ptr_t fabrik_t::welt; /** * Convert internal values to displayed values */ sint64 convert_goods(sint64 value) { return ((value + FAB_DISPLAY_UNIT_HALF) >> (fabrik_t::precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS) ); } sint64 convert_power(sint64 value) { return ( value >> POWER_TO_MW ); } sint64 convert_boost(sint64 value) { return ( (value * 100 + (DEFAULT_PRODUCTION_FACTOR>>1)) >> DEFAULT_PRODUCTION_FACTOR_BITS ); } /** * Ordering based on relative distance to a fixed point `origin'. */ class RelativeDistanceOrdering { private: const koord m_origin; public: RelativeDistanceOrdering(const koord& origin) : m_origin(origin) { /* nothing */ } /** * Returns true if `a' is closer to the origin than `b', otherwise false. */ bool operator()(const koord& a, const koord& b) const { return koord_distance(m_origin, a) < koord_distance(m_origin, b); } }; /** * Produce a scaled production amount from a production amount and work factor. */ sint32 work_scale_production(sint64 prod, sint64 work){ // compute scaled production, rounding up return ((prod * work) + (1 << WORK_BITS) - 1) >> WORK_BITS; } /** * Produce a work factor from a production amount and scaled production amount. */ sint32 work_from_production(sint64 prod, sint64 scaled){ // compute work, rounding up return prod ? ((scaled << WORK_BITS) + prod - 1) / prod : 0; } void ware_production_t::init_stats() { for( int m=0; m0 ) { set_stat( weighted_sum_storage / aggregate_weight, FAB_GOODS_STORAGE ); } for( int s=0; s0; --m ) { statistics[m][s] = statistics[m-1][s]; } if( s==FAB_GOODS_TRANSIT ) { // keep the current amount in transit statistics[0][s] = statistics[1][s]; } else { statistics[0][s] = 0; } } weighted_sum_storage = 0; // restore current storage level set_stat( (sint64)menge * (sint64)factor, FAB_GOODS_STORAGE ); } void ware_production_t::rdwr(loadsave_t *file) { if( file->is_loading() ) { init_stats(); } // we use a temporary variable to save/load old data correctly sint64 statistics_buf[MAX_MONTH][MAX_FAB_GOODS_STAT]; memcpy( statistics_buf, statistics, sizeof(statistics_buf) ); if( file->is_saving() && file->is_version_less(120, 1) ) { for( int m=0; m> DEFAULT_PRODUCTION_FACTOR_BITS); statistics_buf[m][2] = (statistics[m][2] >> DEFAULT_PRODUCTION_FACTOR_BITS); } } if( file->is_version_atleast(112, 1) ) { for( int s=0; srdwr_longlong( statistics_buf[m][s] ); } } file->rdwr_longlong( weighted_sum_storage ); } else if( file->is_version_atleast(110, 5) ) { // save/load statistics for( int s=0; s<3; ++s ) { for( int m=0; mrdwr_longlong( statistics_buf[m][s] ); } } file->rdwr_longlong( weighted_sum_storage ); } if( file->is_loading() ) { memcpy( statistics, statistics_buf, sizeof(statistics_buf) ); // Apply correction for output production graphs which have had their precision changed for factory normalization. // Also apply a fix for corrupted in-transit values caused by a logical error. if(file->is_version_less(120, 1)){ for( int m=0; mis_version_atleast(122, 1)) { file->rdwr_long(index_offset); } else if (file->is_loading()) { index_offset = 0; } } void ware_production_t::book_weighted_sum_storage(uint32 factor, sint64 delta_time) { const sint64 amount = (sint64)menge * (sint64)factor; weighted_sum_storage += amount * delta_time; set_stat( amount, FAB_GOODS_STORAGE ); } sint32 ware_production_t::calculate_output_production_rate() const { return fabrik_t::calculate_work_rate_ramp(menge, min_shipment * OUTPUT_SCALE_RAMPDOWN_MULTIPLYER, max); } sint32 ware_production_t::calculate_demand_production_rate() const { return fabrik_t::calculate_work_rate_ramp(demand_buffer, max / 2, max); } void fabrik_t::arrival_statistics_t::init() { for( uint32 s=0; sis_version_atleast(110, 5) ) { if( file->is_loading() ) { aggregate_arrival = 0; for( uint32 s=0; srdwr_short( slots[s] ); aggregate_arrival += slots[s]; } scaled_demand = 0; } else { for( uint32 s=0; srdwr_short( slots[s] ); } } file->rdwr_short( current_slot ); file->rdwr_short( active_slots ); } else if( file->is_loading() ) { init(); } } sint32 fabrik_t::arrival_statistics_t::advance_slot() { sint32 result = 0; // advance to the next slot ++current_slot; if( current_slot>=SLOT_COUNT ) { current_slot = 0; } // handle expiration of past arrivals and reset slot to 0 if( slots[current_slot]>0 ) { aggregate_arrival -= slots[current_slot]; slots[current_slot] = 0; if( aggregate_arrival==0 ) { // reset slot count to 0 as all previous arrivals have expired active_slots = 0; } result |= ARRIVALS_CHANGED; } // count the number of slots covered since aggregate arrival last increased from 0 to +ve if( active_slots>0 && active_slots start counting slots active_slots = 1; } // increment current slot and aggregate arrival slots[current_slot] += amount; aggregate_arrival += amount; } void fabrik_t::update_transit( const ware_t *ware, bool add ) { if( ware->index > goods_manager_t::INDEX_NONE ) { // only for freights fabrik_t *fab = get_fab( ware->get_target_pos() ); if( fab ) { fab->update_transit_intern( ware, add ); } } } void fabrik_t::apply_transit( const ware_t *ware ) { if( ware->index > goods_manager_t::INDEX_NONE ) { // only for freights fabrik_t *fab = get_fab( ware->get_target_pos() ); if( fab ) { for( uint32 input = 0; input < fab->input.get_count(); input++ ){ ware_production_t& w = fab->input[input]; if( w.get_typ()->get_index() == ware->index ) { // It is now in transit. w.book_stat((sint64)ware->amount, FAB_GOODS_TRANSIT ); // If using JIT2, must decrement demand buffers, activating if required. if( welt->get_settings().get_just_in_time() >= 2 ){ const uint32 prod_factor = fab->desc->get_supplier(input)->get_consumption(); const sint32 prod_delta = (sint32)((((sint64)(ware->amount) << (DEFAULT_PRODUCTION_FACTOR_BITS + precision_bits)) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); const sint32 demand = w.demand_buffer; w.demand_buffer -= prod_delta; if( demand >= w.max && w.demand_buffer < w.max ) { fab->inactive_demands --; } } // ours is on its way, no need to handle the other return; } } } } } // just for simplicity ... void fabrik_t::update_transit_intern( const ware_t *ware, bool add ) { for(ware_production_t &w : input ) { if( w.get_typ()->get_index() == ware->index ) { w.book_stat(add ? (sint64)ware->amount : -(sint64)ware->amount, FAB_GOODS_TRANSIT ); return; } } } void fabrik_t::init_stats() { for( int m=0; mget_supplier(in)->get_consumption(), delta_time); } for( uint32 out = 0; out < output.get_count(); out++ ){ output[out].book_weighted_sum_storage(desc->get_product(out)->get_factor(), delta_time); } // production level const sint32 current_prod = get_current_production(); weighted_sum_production += current_prod * delta_time; set_stat( current_prod, FAB_PRODUCTION ); // electricity, pax and mail boosts weighted_sum_boost_electric += prodfactor_electric * delta_time; set_stat( prodfactor_electric, FAB_BOOST_ELECTRIC ); weighted_sum_boost_pax += prodfactor_pax * delta_time; weighted_sum_boost_mail += prodfactor_mail * delta_time; // power produced or consumed sint64 power = get_power(); weighted_sum_power += power * delta_time; set_stat( power, FAB_POWER ); } void fabrik_t::update_scaled_electric_demand() { if( desc->get_electric_demand()==65535 ) { // demand not specified in pak, use old fixed demands scaled_electric_demand = prodbase * PRODUCTION_DELTA_T; if( desc->is_electricity_producer() ) { scaled_electric_demand *= 4; } return; } const sint64 prod = desc->get_productivity(); scaled_electric_demand = (uint32)( (( (sint64)(desc->get_electric_demand()) * (sint64)prodbase + (prod >> 1) ) / prod) << POWER_TO_MW ); if( scaled_electric_demand == 0 ) { prodfactor_electric = 0; } } void fabrik_t::update_scaled_pax_demand() { // first, scaling based on current production base const sint64 prod = desc->get_productivity(); const sint64 desc_pax_demand = ( desc->get_pax_demand()==65535 ? desc->get_pax_level() : desc->get_pax_demand() ); // formula : desc_pax_demand * (current_production_base / desc_production_base); (prod >> 1) is for rounding const uint32 pax_demand = (uint32)( ( desc_pax_demand * (sint64)prodbase + (prod >> 1) ) / prod ); // then, scaling based on month length scaled_pax_demand = min( (uint32)welt->scale_with_month_length(pax_demand), 932067 ); if( scaled_pax_demand == 0 && desc_pax_demand > 0 ) { scaled_pax_demand = 1; // since desc pax demand > 0 -> ensure no less than 1 } // pax demand for fixed period length arrival_stats_pax.set_scaled_demand( pax_demand ); } void fabrik_t::update_scaled_mail_demand() { // first, scaling based on current production base const sint64 prod = desc->get_productivity(); const sint64 desc_mail_demand = ( desc->get_mail_demand()==65535 ? (desc->get_pax_level()>>2) : desc->get_mail_demand() ); // formula : desc_mail_demand * (current_production_base / desc_production_base); (prod >> 1) is for rounding const uint32 mail_demand = (uint32)( ( desc_mail_demand * (sint64)prodbase + (prod >> 1) ) / prod ); // then, scaling based on month length scaled_mail_demand = min( (uint32)welt->scale_with_month_length(mail_demand), 932067); if( scaled_mail_demand == 0 && desc_mail_demand > 0 ) { scaled_mail_demand = 1; // since desc mail demand > 0 -> ensure no less than 1 } // mail demand for fixed period length arrival_stats_mail.set_scaled_demand( mail_demand ); } void fabrik_t::update_prodfactor_pax() { // calculate pax boost based on arrival data and demand of the fixed-length period const uint32 periods = welt->get_settings().get_factory_arrival_periods(); const uint32 slots = arrival_stats_pax.get_active_slots(); const uint32 pax_demand = ( periods==1 || slots*periods<=(uint32)SLOT_COUNT ? arrival_stats_pax.get_scaled_demand() : ( slots==(uint32)SLOT_COUNT ? arrival_stats_pax.get_scaled_demand() * periods : (arrival_stats_pax.get_scaled_demand() * periods * slots) >> SLOT_BITS ) ); const uint32 pax_arrived = arrival_stats_pax.get_aggregate_arrival(); if( pax_demand==0 || pax_arrived==0 || desc->get_pax_boost()==0 ) { prodfactor_pax = 0; } else if( pax_arrived>=pax_demand ) { // maximum boost prodfactor_pax = desc->get_pax_boost(); } else { // pro-rata boost : (pax_arrived / pax_demand) * desc_pax_boost; (pax_demand >> 1) is for rounding prodfactor_pax = (sint32)( ( (sint64)pax_arrived * (sint64)(desc->get_pax_boost()) + (sint64)(pax_demand >> 1) ) / (sint64)pax_demand ); } set_stat(prodfactor_pax, FAB_BOOST_PAX); } void fabrik_t::update_prodfactor_mail() { // calculate mail boost based on arrival data and demand of the fixed-length period const uint32 periods = welt->get_settings().get_factory_arrival_periods(); const uint32 slots = arrival_stats_mail.get_active_slots(); const uint32 mail_demand = ( periods==1 || slots*periods<=(uint32)SLOT_COUNT ? arrival_stats_mail.get_scaled_demand() : ( slots==(uint32)SLOT_COUNT ? arrival_stats_mail.get_scaled_demand() * periods : (arrival_stats_mail.get_scaled_demand() * periods * slots) >> SLOT_BITS ) ); const uint32 mail_arrived = arrival_stats_mail.get_aggregate_arrival(); if( mail_demand==0 || mail_arrived==0 || desc->get_mail_boost()==0 ) { prodfactor_mail = 0; } else if( mail_arrived>=mail_demand ) { // maximum boost prodfactor_mail = desc->get_mail_boost(); } else { // pro-rata boost : (mail_arrived / mail_demand) * desc_mail_boost; (mail_demand >> 1) is for rounding prodfactor_mail = (sint32)( ( (sint64)mail_arrived * (sint64)(desc->get_mail_boost()) + (sint64)(mail_demand >> 1) ) / (sint64)mail_demand ); } set_stat(prodfactor_mail, FAB_BOOST_MAIL); } void fabrik_t::recalc_demands_at_target_cities() { if (!welt->get_settings().get_factory_enforce_demand()) { // demand not enforced -> no splitting of demands for(stadt_t* const c : target_cities) { c->access_target_factories_for_pax().update_factory( this, scaled_pax_demand << DEMAND_BITS); c->access_target_factories_for_mail().update_factory(this, scaled_mail_demand << DEMAND_BITS); } return; } if (target_cities.empty()) { // nothing to do return; } else if( target_cities.get_count()==1 ) { // only 1 target city -> no need to apportion pax/mail demand target_cities[0]->access_target_factories_for_pax().update_factory(this, (scaled_pax_demand << DEMAND_BITS)); target_cities[0]->access_target_factories_for_mail().update_factory(this, (scaled_mail_demand << DEMAND_BITS)); } else { // more than 1 target cities -> need to apportion pax/mail demand among the cities static vector_tpl weights(8); weights.clear(); uint32 sum_of_weights = 0; // first, calculate the weights for( uint32 c=0; cget_einwohner(), shortest_distance( get_pos().get_2d(), target_cities[c]->get_center() ) ) ); sum_of_weights += weights[c]; } // finally, apportion the pax/mail demand; formula : demand * (city_weight / aggregate_city_weight); (sum_of_weights >> 1) is for rounding for( uint32 c=0; c> 1) ) / (sint64)sum_of_weights); target_cities[c]->access_target_factories_for_pax().update_factory(this, pax_amount); const uint32 mail_amount = (uint32)(( (sint64)(scaled_mail_demand << DEMAND_BITS) * (sint64)weights[c] + (sint64)(sum_of_weights >> 1) ) / (sint64)sum_of_weights); target_cities[c]->access_target_factories_for_mail().update_factory(this, mail_amount); } } } void fabrik_t::recalc_storage_capacities() { if( desc->get_field_group() ) { // with fields -> calculate based on capacities contributed by fields const uint32 ware_types = input.get_count() + output.get_count(); if( ware_types>0 ) { // calculate total storage capacity contributed by fields const field_group_desc_t *const field_group = desc->get_field_group(); sint32 field_capacities = 0; for(field_data_t const& f : fields) { field_capacities += field_group->get_field_class(f.field_class_index)->get_storage_capacity(); } const sint32 share = (sint32)( ( (sint64)field_capacities << precision_bits ) / (sint64)ware_types ); // first, for input goods for(ware_production_t & g : input) { for( int b=0; bget_supplier_count(); ++b ) { const factory_supplier_desc_t *const input = desc->get_supplier(b); if (g.get_typ() == input->get_input_type()) { // Inputs are now normalized to factory production. uint32 prod_factor = input->get_consumption(); g.max = (sint32)((((sint64)((input->get_capacity() << precision_bits) + share) << DEFAULT_PRODUCTION_FACTOR_BITS) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); } } } // then, for output goods for(ware_production_t & g : output) { for( uint b=0; bget_product_count(); ++b ) { const factory_product_desc_t *const output = desc->get_product(b); if (g.get_typ() == output->get_output_type()) { // Outputs are now normalized to factory production. uint32 prod_factor = output->get_factor(); g.max = (sint32)((((sint64)((output->get_capacity() << precision_bits) + share) << DEFAULT_PRODUCTION_FACTOR_BITS) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); } } } } } else { // without fields -> scaling based on prodbase // first, for input goods for(ware_production_t & g : input) { for( int b=0; bget_supplier_count(); ++b ) { const factory_supplier_desc_t *const input = desc->get_supplier(b); if (g.get_typ() == input->get_input_type()) { // Inputs are now normalized to factory production. uint32 prod_factor = input->get_consumption(); g.max = (sint32)(((((sint64)input->get_capacity() * (sint64)prodbase) << (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)) + (sint64)(prod_factor - 1)) / ((sint64)desc->get_productivity() * (sint64)prod_factor)); } } } // then, for output goods for(ware_production_t & g : output) { for( uint b=0; bget_product_count(); ++b ) { const factory_product_desc_t *const output = desc->get_product(b); if (g.get_typ() == output->get_output_type()) { // Outputs are now normalized to factory production. uint32 prod_factor = output->get_factor(); g.max = (sint32)(((((sint64)output->get_capacity() * (sint64)prodbase) << (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)) + (sint64)(prod_factor - 1)) / ((sint64)desc->get_productivity() * (sint64)prod_factor)); } } } } // Now that the maximum is known, work out the recommended shipment size for outputs in normalized units. for( uint32 out = 0; out < output.get_count(); out++ ){ const uint32 prod_factor = desc->get_product(out)->get_factor(); // Determine the maximum number of whole units the out can store. const uint32 unit_size = (uint32)(((sint64)output[out].max * (sint64)prod_factor) >> ( precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS )); // Determine the number of units to ship. Prefer 10 units although in future a more dynamic choice may be appropiate. uint32 shipment_size; // Maximum shipment size. if( unit_size >= SHIPMENT_MAX_SIZE * SHIPMENT_NUM_MIN ) { shipment_size = SHIPMENT_MAX_SIZE; } // Dynamic shipment size. else if( unit_size > SHIPMENT_NUM_MIN ) { shipment_size = unit_size / SHIPMENT_NUM_MIN; } // Minimum shipment size. else { shipment_size = 1; } // Now convert it into the prefered shipment size. Always round up to prevent "off by 1" error. output[out].min_shipment = (sint32)((((sint64)shipment_size << (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); } if( welt->get_settings().get_just_in_time() >= 2 ) { rebuild_inactive_cache(); } } void fabrik_t::add_target_city(stadt_t *const city) { if( target_cities.append_unique(city) ) { recalc_demands_at_target_cities(); } } void fabrik_t::remove_target_city(stadt_t *const city) { if( target_cities.is_contained(city) ) { target_cities.remove(city); city->access_target_factories_for_pax().remove_factory(this); city->access_target_factories_for_mail().remove_factory(this); recalc_demands_at_target_cities(); } } void fabrik_t::clear_target_cities() { for(stadt_t* const c : target_cities) { c->access_target_factories_for_pax().remove_factory(this); c->access_target_factories_for_mail().remove_factory(this); } target_cities.clear(); } void fabrik_t::set_base_production(sint32 p) { prodbase = p; recalc_storage_capacities(); update_scaled_electric_demand(); update_scaled_pax_demand(); update_scaled_mail_demand(); update_prodfactor_pax(); update_prodfactor_mail(); recalc_demands_at_target_cities(); } fabrik_t *fabrik_t::get_fab(const koord &pos) { const grund_t *gr = welt->lookup_kartenboden(pos); if(gr) { gebaeude_t *gb = gr->find(); if(gb) { return gb->get_fabrik(); } } return NULL; } void fabrik_t::link_halt(halthandle_t halt) { welt->access(pos.get_2d())->add_to_haltlist(halt); } void fabrik_t::unlink_halt(halthandle_t halt) { planquadrat_t *plan=welt->access(pos.get_2d()); if(plan) { plan->remove_from_haltlist(halt); } } // returns true, if there is a freight halt serving us bool fabrik_t::is_within_players_network( const player_t* player ) const { if( const planquadrat_t *plan = welt->access( pos.get_2d() ) ) { if( plan->get_haltlist_count() > 0 ) { const halthandle_t *const halt_list = plan->get_haltlist(); for( int h = 0; h < plan->get_haltlist_count(); h++ ) { halthandle_t halt = halt_list[h]; if( halt.is_bound() && halt->is_enabled(haltestelle_t::WARE) && halt->has_available_network(player) ) { return true; } } } } return false; } void fabrik_t::add_consumer(koord ziel) { if( !consumer.is_contained(ziel) ) { consumer.insert_ordered( ziel, RelativeDistanceOrdering(pos.get_2d()) ); // now tell factory too fabrik_t * fab = fabrik_t::get_fab(ziel); if (fab) { fab->add_supplier(get_pos().get_2d()); } } } void fabrik_t::remove_consumer(koord ziel) { consumer.remove(ziel); } fabrik_t::fabrik_t(loadsave_t* file) { owner = NULL; prodfactor_electric = 0; consumer_active_last_month = 0; pos = koord3d::invalid; transformers.clear(); rdwr(file); if( desc == NULL ) { dbg->warning( "fabrik_t::fabrik_t()", "No pak-file for factory at (%s) - will not be built!", pos_origin.get_str() ); return; } else if( !welt->is_within_limits(pos_origin.get_2d()) ) { dbg->warning( "fabrik_t::fabrik_t()", "%s is not a valid position! (Will not be built!)", pos_origin.get_str() ); desc = NULL; // to get rid of this broken factory later... } else { build(rotate, false, false); // now get rid of construction image for( sint16 y=0; yget_building()->get_y(rotate); y++ ) { for( sint16 x=0; xget_building()->get_x(rotate); x++ ) { gebaeude_t *gb = welt->lookup_kartenboden( pos_origin.get_2d()+koord(x,y) )->find(); if( gb ) { gb->add_alter(10000); } } } } last_sound_ms = welt->get_ticks(); } fabrik_t::fabrik_t(koord3d pos_, player_t* owner, const factory_desc_t* factory_desc, sint32 initial_prod_base) : desc(factory_desc), pos(pos_) { this->pos.z = welt->max_hgt(pos.get_2d()); pos_origin = pos; this->owner = owner; prodfactor_electric = 0; prodfactor_pax = 0; prodfactor_mail = 0; if (initial_prod_base < 0) { prodbase = desc->get_productivity() + simrand(desc->get_range()); } else { prodbase = initial_prod_base; } delta_t_sum = 0; delta_amount = 0; delta_amount_remainder = 0; activity_count = 0; currently_requiring_power = false; currently_producing = false; total_input = total_transit = total_output = 0; status = STATUS_NOTHING; consumer_active_last_month = 0; // create input information input.resize( factory_desc->get_supplier_count() ); for( int g=0; gget_supplier_count(); ++g ) { const factory_supplier_desc_t *const supp = factory_desc->get_supplier(g); input[g].set_typ( supp->get_input_type() ); } // create output information output.resize( factory_desc->get_product_count() ); for( uint g=0; gget_product_count(); ++g ) { const factory_product_desc_t *const product = factory_desc->get_product(g); output[g].set_typ( product->get_output_type() ); } recalc_storage_capacities(); if( welt->get_settings().get_just_in_time() >= 2 ){ inactive_inputs = inactive_outputs = inactive_demands = 0; if( input.empty() ){ // All sources start out with maximum product. for( uint32 out = 0; out < output.get_count(); out++ ){ output[out].menge = output[out].max; inactive_outputs ++; } } else { for( uint32 out = 0; out < output.get_count(); out++ ){ output[out].menge = 0; } // A consumer of sorts so output and input starts out empty but with a full demand buffer. for( uint32 in = 0; in < input.get_count(); in++ ){ input[in].menge = 0; input[in].demand_buffer = input[in].max; inactive_inputs++; inactive_demands++; } } } else { if( input.empty() ) { for(ware_production_t & g : output ) { if( g.max > 0 ) { // if source then start with full storage, so that AI will build line(s) immediately g.menge = g.max - 1; } } } } last_sound_ms = welt->get_ticks(); init_stats(); arrival_stats_pax.init(); arrival_stats_mail.init(); delta_slot = 0; times_expanded = 0; update_scaled_electric_demand(); update_scaled_pax_demand(); update_scaled_mail_demand(); } fabrik_t::~fabrik_t() { while(!fields.empty()) { planquadrat_t *plan = welt->access( fields.back().location ); // if destructor is called when world is destroyed, plan is already invalid if (plan) { grund_t *gr = plan->get_kartenboden(); if (field_t* f = gr->find()) { delete f; // implicitly removes the field from fields plan->boden_ersetzen( gr, new boden_t(gr->get_pos(), slope_t::flat ) ); plan->get_kartenboden()->calc_image(); continue; } } fields.pop_back(); } // destroy chart window, if present destroy_win((ptrdiff_t)this); } void fabrik_t::build(sint32 rotate, bool build_fields, bool force_initial_prodbase) { this->rotate = rotate; pos_origin = welt->lookup_kartenboden(pos_origin.get_2d())->get_pos(); gebaeude_t *gb = hausbauer_t::build(owner, pos_origin.get_2d(), rotate, desc->get_building(), this); pos = gb->get_pos(); pos_origin.z = pos.z; if(desc->get_field_group()) { // if there are fields if( !fields.empty() ) { for( uint16 i=0; ilookup_kartenboden(k); if( gr->ist_natur() ) { // first make foundation below grund_t *gr2 = new fundament_t(gr->get_pos(), gr->get_grund_hang()); welt->access(k)->boden_ersetzen(gr, gr2); gr2->obj_add( new field_t(gr2->get_pos(), owner, desc->get_field_group()->get_field_class( fields[i].field_class_index ), this ) ); } else { // there was already a building at this position => do not restore! fields.remove_at(i); i--; } } } else if( build_fields ) { // make sure not to exceed initial prodbase too much sint32 org_prodbase = prodbase; // we will start with a minimum number and try to get closer to start_fields const uint16 spawn_fields = desc->get_field_group()->get_min_fields() + simrand( desc->get_field_group()->get_start_fields()-desc->get_field_group()->get_min_fields() ); while( fields.get_count() < spawn_fields && add_random_field(10000u) ) { if (fields.get_count() > desc->get_field_group()->get_min_fields() && prodbase >= 2*org_prodbase) { // too much productivity, no more fields needed break; } } sint32 field_prod = prodbase - org_prodbase; // adjust prodbase if (force_initial_prodbase) { set_base_production( max(field_prod, org_prodbase) ); } } } else { fields.clear(); } /// Determine control logic if( welt->get_settings().get_just_in_time() >= 2 ) { // Does it both consume and produce? if( !output.empty() && !input.empty() ) { control_type = CL_FACT_MANY; } // Does it produce? else if( !output.empty() ) { control_type = CL_PROD_MANY; } // Does it consume? else if( !input.empty() ) { control_type = CL_CONS_MANY; } // No I/O? else { control_type = desc->is_electricity_producer() ? CL_ELEC_PROD : CL_NONE; } } else{ // Classic logic. if( !output.empty() && !input.empty() ) { control_type = CL_FACT_CLASSIC; } else if( !output.empty() ) { control_type = CL_PROD_CLASSIC; } else if( !input.empty() ) { control_type = CL_CONS_CLASSIC; } else { control_type = CL_ELEC_CLASSIC; } } // Boost logic determines what factors boost factory production. if( welt->get_settings().get_just_in_time() >= 2 ) { if( !desc->is_electricity_producer() && desc->get_electric_demand() > 0 ) { boost_type = BL_POWER; } else if( desc->get_pax_demand() || desc->get_mail_demand() ) { boost_type = BL_PAXM; } else { boost_type = BL_NONE; } } else { boost_type = BL_CLASSIC; } if( welt->get_settings().get_just_in_time() >= 2 ) { if( input.empty() ) { demand_type = DL_NONE; } else if( output.empty() ) { demand_type = DL_ASYNC; } else { demand_type = DL_SYNC; } } else { demand_type = input.empty() ? DL_NONE : DL_OLD; } } bool fabrik_t::can_build_field_on_ground(const grund_t *gr, climate cl) const { if (!gr) return false; if (gr->get_typ() != grund_t::boden || !gr->ist_natur()) return false; if (gr->get_grund_hang() != slope_t::flat) return false; if (!this->get_desc()->get_building()->is_allowed_climate(cl)) return false; // Do not remove transformers of solar farms etc. if (gr->find() || gr->find()) return false; // allow building fields below power lines if (gr->kann_alle_obj_entfernen(NULL) != NULL && !gr->find()) return false; return true; } /* field generation code */ bool fabrik_t::add_random_field(uint16 probability) { // has fields, and not yet too many? const field_group_desc_t *fd = desc->get_field_group(); if(fd==NULL || fd->get_max_fields() <= fields.get_count()) { return false; } // we are lucky and are allowed to generate a field if( simrand(10000)>=probability ) { return false; } // we start closest to the factory, and check for valid tiles as we move out uint8 radius = 1; // pick a coordinate to use - create a list of valid locations and choose a random one vector_tpl build_locations; do { for(sint32 xoff = -radius; xoff < radius + get_desc()->get_building()->get_size().x ; xoff++) { for(sint32 yoff =-radius ; yoff < radius + get_desc()->get_building()->get_size().y; yoff++) { // if we can build on this tile then add it to the list const planquadrat_t *plan = welt->access(pos.get_2d() + koord(xoff, yoff)); grund_t *kb = NULL; if (plan && (kb = plan->get_kartenboden())!=NULL && this->can_build_field_on_ground(kb, plan->get_climate())) { // only on same height => climate will match! build_locations.append(kb); assert(kb->find() == NULL); } // skip inside of rectangle (already checked earlier) if(radius > 1 && yoff == -radius && (xoff > -radius && xoff < radius + get_desc()->get_building()->get_size().x - 1)) { yoff = radius + get_desc()->get_building()->get_size().y - 2; } } } if (build_locations.empty()) { radius++; } } while (radius < 10 && build_locations.empty()); // build on one of the positions while (!build_locations.empty()) { grund_t *gr = build_locations[simrand(build_locations.get_count())]; leitung_t* lt = gr->find(); if(lt) { gr->obj_remove(lt); } gr->obj_loesche_alle(NULL); // first make foundation below const koord k = gr->get_pos().get_2d(); field_data_t new_field(k); assert(!fields.is_contained(new_field)); // fetch a random field class desc based on spawn weights const weighted_vector_tpl &field_class_indices = fd->get_field_class_indices(); new_field.field_class_index = pick_any_weighted(field_class_indices); const field_class_desc_t *const field_class = fd->get_field_class( new_field.field_class_index ); fields.append(new_field); grund_t *gr2 = new fundament_t(gr->get_pos(), gr->get_grund_hang()); welt->access(k)->boden_ersetzen(gr, gr2); gr2->obj_add( new field_t(gr2->get_pos(), owner, field_class, this ) ); // adjust production base and storage capacities set_base_production( prodbase + field_class->get_field_production() ); if(lt) { gr2->obj_add( lt ); } gr2->calc_image(); return true; } return false; } void fabrik_t::remove_field_at(koord pos) { field_data_t field(pos); assert(fields.is_contained( field )); field = fields[ fields.index_of(field) ]; const field_class_desc_t *const field_class = desc->get_field_group()->get_field_class( field.field_class_index ); fields.remove(field); // revert the field's effect on production base and storage capacities set_base_production( prodbase - field_class->get_field_production() ); } vector_tpl &fabrik_t::sind_da_welche(koord min_pos, koord max_pos) { static vector_tpl factory_list(16); factory_list.clear(); for(int y=min_pos.y; y<=max_pos.y; y++) { for(int x=min_pos.x; x<=max_pos.x; x++) { fabrik_t *fab=get_fab(koord(x,y)); if(fab) { if (factory_list.append_unique(fab)) { //DBG_MESSAGE("fabrik_t::sind_da_welche()","appended factory %s at (%i,%i)",gr->first_obj()->get_fabrik()->get_desc()->get_name(),x,y); } } } } return factory_list; } /** * if name==NULL translate desc factory name in game language */ char const* fabrik_t::get_name() const { return name ? name.c_str() : translator::translate(desc->get_name(), welt->get_settings().get_name_language_id()); } void fabrik_t::set_name(const char *new_name) { if(new_name==NULL || strcmp(new_name, translator::translate(desc->get_name(), welt->get_settings().get_name_language_id()))==0) { // new name is equal to name given by descriptor/translation -> set name to NULL name = NULL; } else { name = new_name; } fabrik_info_t *win = dynamic_cast(win_get_magic((ptrdiff_t)this)); if (win) { win->update_info(); } } void fabrik_t::rdwr(loadsave_t *file) { xml_tag_t f( file, "fabrik_t" ); sint32 i; sint32 owner_n; sint32 input_count; sint32 output_count; sint32 anz_lieferziele; if( file->is_saving() ) { input_count = input.get_count(); output_count = output.get_count(); anz_lieferziele = consumer.get_count(); const char *s = desc->get_name(); file->rdwr_str(s); } else { char s[256]; file->rdwr_str(s, lengthof(s)); DBG_DEBUG("fabrik_t::rdwr()","loading factory '%s'",s); desc = factory_builder_t::get_desc(s); if( desc==NULL ) { // maybe it was only renamed? desc = factory_builder_t::get_desc(translator::compatibility_name(s)); } if( desc==NULL ) { dbg->warning( "fabrik_t::rdwr()", "Pak-file for factory '%s' missing!", s ); // we continue loading even if desc==NULL pakset_manager_t::add_missing_paks( s, MISSING_FACTORY ); } } pos_origin.rdwr(file); // pos will be assigned after call to hausbauer_t::build file->rdwr_byte(rotate); if (file->is_version_atleast(122, 1)) { file->rdwr_long(delta_t_sum); file->rdwr_long(delta_amount); file->rdwr_long(delta_amount_remainder); file->rdwr_long(prodfactor_electric); file->rdwr_bool(currently_requiring_power); file->rdwr_long(total_input); file->rdwr_long(total_transit); file->rdwr_long(total_output); file->rdwr_byte(status); } else if (file->is_loading()) { delta_t_sum = 0; delta_amount = 0; delta_amount_remainder = 0; prodfactor_electric = 0; currently_requiring_power = false; total_input = 0; total_transit = 0; total_output = 0; status = STATUS_NOTHING; } // now rebuild information for received goods file->rdwr_long(input_count); if( file->is_loading() ) { input.resize( input_count ); } bool mismatch = false; for( i=0; iis_saving() ) { ware_name = ware.get_typ()->get_name(); if( file->is_version_less(120, 1) ) { // correct for older saves menge = (sint32)(((sint64)ware.menge * (sint64)desc->get_supplier(i)->get_consumption() ) >> DEFAULT_PRODUCTION_FACTOR_BITS); } } file->rdwr_str(ware_name); file->rdwr_long(menge); if( file->is_version_less(110, 5) ) { // max storage is only loaded/saved for older versions file->rdwr_long(ware.max); } // JIT2 needs to store input demand buffer if( welt->get_settings().get_just_in_time() >= 2 && file->is_version_atleast(120, 1) ){ file->rdwr_long(ware.demand_buffer); } ware.rdwr( file ); if( file->is_loading() ) { if (!ware_name) { dbg->fatal("fabrik_t::rdwr", "Invalid ware at input slot %d of factory at %s", i, pos.get_fullstr()); } ware.set_typ( goods_manager_t::get_info(ware_name) ); // Maximum in-transit is always 0 on load. if( welt->get_settings().get_just_in_time() < 2 ) { ware.max_transit = 0; } if( !desc || !desc->get_supplier(i) ) { if (desc) { dbg->warning( "fabrik_t::rdwr()", "Factory at %s requested producer for %s but has none!", pos_origin.get_fullstr(), ware_name); } ware.menge = 0; } else { // Inputs used to be with respect to actual units of production. They now are normalized with respect to factory production so require conversion. const uint32 prod_factor = desc ? desc->get_supplier(i)->get_consumption() : 1; if( file->is_version_less(120, 1) ) { ware.menge = (sint32)(((sint64)menge << DEFAULT_PRODUCTION_FACTOR_BITS) / (sint64)prod_factor); } else { ware.menge = menge; } // repair files that have 'insane' values const sint32 max = (sint32)((((sint64)FAB_MAX_INPUT << (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); if( ware.menge < 0 ) { ware.menge = 0; } if( ware.menge > max ) { ware.menge = max; } if (ware.get_typ() != desc->get_supplier(i)->get_input_type()) { mismatch = true; dbg->warning("fabrik_t::rdwr", "Factory at %s: producer[%d] mismatch in savegame=%s/%s, in pak=%s", pos_origin.get_fullstr(), i, ware_name, ware.get_typ()->get_name(), desc->get_supplier(i)->get_input_type()->get_name()); } } free(const_cast(ware_name)); } } if( desc && input_count != desc->get_supplier_count() ) { dbg->warning("fabrik_t::rdwr", "Mismatch of input slot count for factory %s at %s: savegame = %d, pak = %d", get_name(), pos_origin.get_fullstr(), input_count, desc->get_supplier_count()); // resize input to match the descriptor input.resize( desc->get_supplier_count() ); mismatch = true; } if (mismatch) { array_tpl dummy; dummy.resize(desc->get_supplier_count()); for(uint16 i=0; iget_supplier_count(); i++) { dummy[i] = input[i]; } for(uint16 i=0; iget_supplier_count(); i++) { // search for matching type bool missing = true; const goods_desc_t* goods = desc->get_supplier(i)->get_input_type(); for(uint16 j=0; jget_supplier_count() && missing; j++) { if (dummy[j].get_typ() == goods) { input[i] = dummy[j]; dummy[j].set_typ(NULL); missing = false; } } if (missing) { input[i].set_typ(goods); } } } // now rebuilt information for produced goods file->rdwr_long(output_count); if( file->is_loading() ) { output.resize( output_count ); } mismatch = false; for( i=0; iis_saving() ) { ware_name = ware.get_typ()->get_name(); // correct scaling for older saves if( file->is_version_less(120, 1) ){ menge = (sint32)(((sint64)ware.menge * desc->get_product(i)->get_factor() ) >> DEFAULT_PRODUCTION_FACTOR_BITS); } } file->rdwr_str(ware_name); file->rdwr_long(menge); if( file->is_version_less(110, 5) ) { // max storage is only loaded/saved for older versions file->rdwr_long(ware.max); // obsolete variables -> statistics already contain records on goods delivered sint32 abgabe_sum = (sint32)(ware.get_stat(0, FAB_GOODS_DELIVERED)); sint32 abgabe_letzt = (sint32)(ware.get_stat(1, FAB_GOODS_DELIVERED)); file->rdwr_long(abgabe_sum); file->rdwr_long(abgabe_letzt); } ware.rdwr( file ); if( file->is_loading() ) { if (ware_name && goods_manager_t::get_info(ware_name)) { ware.set_typ( goods_manager_t::get_info(ware_name)); } else { dbg->fatal("fabrik_t::rdwr", "Invalid ware %s at output slot %d of factory at %s", ware_name ? ware_name : "", i, pos.get_fullstr()); } if( !desc || !desc->get_product(i) ) { if (desc) { dbg->warning( "fabrik_t::rdwr()", "Factory at %s requested consumer for %s but has none!", pos_origin.get_fullstr(), ware_name ); } ware.menge = 0; } else { // Outputs used to be with respect to actual units of production. They now are normalized with respect to factory production so require conversion. if( file->is_version_less(120, 1) ){ const uint32 prod_factor = desc ? desc->get_product(i)->get_factor() : 1; ware.menge = (sint32)(((sint64)menge << DEFAULT_PRODUCTION_FACTOR_BITS) / (sint64)prod_factor); } else { ware.menge = menge; } // repair files that have 'insane' values if( ware.menge < 0 ) { ware.menge = 0; } if (ware.get_typ() != desc->get_product(i)->get_output_type()) { mismatch = true; dbg->warning("fabrik_t::rdwr", "Factory at %s: consumer[%d] mismatch in savegame=%s/%s, in pak=%s", pos_origin.get_fullstr(), i, ware_name, ware.get_typ()->get_name(), desc->get_product(i)->get_output_type()->get_name()); } } free(const_cast(ware_name)); } } if( desc && output_count != desc->get_product_count()) { dbg->warning("fabrik_t::rdwr", "Mismatch of output slot count for factory %s at %s: savegame = %d, pak = %d", get_name(), pos_origin.get_fullstr(), output_count, desc->get_product_count()); // resize output to match the descriptor output.resize( desc->get_product_count() ); mismatch = true; } if (mismatch) { array_tpl dummy; dummy.resize(desc->get_product_count()); for(uint16 i=0; iget_product_count(); i++) { dummy[i] = output[i]; } for(uint16 i=0; iget_product_count(); i++) { // search for matching type bool missing = true; const goods_desc_t* goods = desc->get_product(i)->get_output_type(); for(uint16 j=0; jget_product_count() && missing; j++) { if (dummy[j].get_typ() == goods) { output[i] = dummy[j]; dummy[j].set_typ(NULL); missing = false; } } if (missing) { output[i].set_typ(goods); } } } // restore other information owner_n = welt->sp2num(owner); file->rdwr_long(owner_n); file->rdwr_long(prodbase); if( file->is_version_less(110, 5) ) { // prodfactor saving no longer required sint32 adjusted_value = (prodfactor_electric / 16) + 16; file->rdwr_long(adjusted_value); } // no longer save power at factories if( file->is_version_atleast(99, 17) && file->is_version_less(120, 4) ) { sint32 power = 0; file->rdwr_long(power); } if( file->is_version_atleast(120, 1) && file->is_version_less(120, 4) ) { sint32 power_demand = 0; file->rdwr_long(power_demand); } // owner stuff if( file->is_loading() ) { // take care of old files if( file->is_version_less(86, 1) ) { koord k = desc ? desc->get_building()->get_size() : koord(1,1); DBG_DEBUG("fabrik_t::rdwr()","correction of production by %i",k.x*k.y); // since we step from 86.01 per factory, not per tile! prodbase *= k.x*k.y*2; } // restore factory owner // Due to a omission in Volkers changes, there might be savegames // in which factories were saved without an owner. In this case // set the owner to the default of player 1 if(owner_n == -1) { // Use default owner = welt->get_public_player(); } else { // Restore owner pointer owner = welt->get_player(owner_n); } } file->rdwr_long(anz_lieferziele); // connect/save consumer for(int i=0; iis_loading()) { consumer.append(koord::invalid); } consumer[i].rdwr(file); } if( file->is_version_atleast(112, 2) ) { file->rdwr_long( consumer_active_last_month ); } // suppliers / consumers will be recalculated in finish_rd if (file->is_loading() && welt->get_settings().is_crossconnect_factories()) { consumer.clear(); } // information on fields ... if( file->is_version_atleast(99, 10) ) { if( file->is_saving() ) { uint16 nr=fields.get_count(); file->rdwr_short(nr); if( file->is_version_atleast(102, 3) ) { // each field stores location and a field class index for( uint16 i=0 ; irdwr_short(idx); } } else { // each field only stores location for( uint16 i=0 ; irdwr_short(nr); fields.reserve(nr); if( file->is_version_atleast(102, 3) ) { // each field stores location and a field class index for( uint16 i=0 ; irdwr_short(idx); if( desc && desc->get_field_group() ) { // set class index to 0 if it is out of range, if there fields at all fields.append( field_data_t(k, idx >= desc->get_field_group()->get_field_class_count() ? 0 : idx ) ); } } } else { // each field only stores location for( uint16 i=0 ; iget_field_group() ) { // oald add fields if there are any defined fields.append( field_data_t(k, 0) ); } } } } } // restore city pointer here if( file->is_version_atleast(99, 14) ) { sint32 nr = target_cities.get_count(); file->rdwr_long(nr); for( int i=0; iis_saving()) { city_index = welt->get_cities().index_of( target_cities[i] ); } file->rdwr_long(city_index); if( file->is_loading() ) { // will also update factory information target_cities.append( welt->get_cities()[city_index] ); } } } else if( file->is_loading() ) { // will be handled by the city after reloading target_cities.clear(); } if( file->is_version_atleast(110, 5) ) { file->rdwr_short(times_expanded); // statistics for( int s=0; srdwr_longlong( statistics[m][s] ); } } file->rdwr_longlong( weighted_sum_production ); file->rdwr_longlong( weighted_sum_boost_electric ); file->rdwr_longlong( weighted_sum_boost_pax ); file->rdwr_longlong( weighted_sum_boost_mail ); file->rdwr_longlong( weighted_sum_power ); file->rdwr_longlong( aggregate_weight ); file->rdwr_long( delta_slot ); } else if( file->is_loading() ) { times_expanded = 0; init_stats(); delta_slot = 0; } arrival_stats_pax.rdwr( file ); arrival_stats_mail.rdwr( file ); if( file->is_version_atleast(110, 7) ) { file->rdwr_byte(activity_count); } else if( file->is_loading() ) { activity_count = 0; } // save name if( file->is_version_atleast(110, 7) ) { if( file->is_saving() && !name ) { char const* fullname = desc->get_name(); file->rdwr_str(fullname); } else { file->rdwr_str(name); if( file->is_loading() && desc != NULL && name == desc->get_name() ) { // equal to desc name name = 0; } } } } /** * let the chimney smoke, if there is something to produce */ void fabrik_t::smoke() { const smoke_desc_t *rada = desc->get_smoke(); if(rada) { const uint8 rot = (4-rotate)%desc->get_building()->get_all_layouts(); grund_t *gr = welt->lookup_kartenboden(pos_origin.get_2d()+desc->get_smoketile( rot )); const koord offset = ( desc->get_smokeoffset(rot)*OBJECT_OFFSET_STEPS)/16; wolke_t *smoke = new wolke_t(gr->get_pos(), offset.x, 0, offset.y, desc->get_smokelifetime(), desc->get_smokeuplift(), rada->get_images() ); gr->obj_add(smoke); } // maybe sound? if( desc->get_sound()!=NO_SOUND && welt->get_ticks()>last_sound_ms+desc->get_sound_interval_ms() ) { last_sound_ms = welt->get_ticks(); welt->play_sound_area_clipped( get_pos().get_2d(), desc->get_sound(), FACTORY_SOUND ); } } uint32 fabrik_t::scale_output_production(const uint32 product, uint32 menge) const { // prorate production based upon amount of product in storage // but allow full production rate for storage amounts less than twice the minimum shipment size. const uint32 maxi = output[product].max; const uint32 actu = output[product].menge; if( actu= (uint32)(2 * output[product].min_shipment) ) { if( menge>(0x7FFFFFFFu/maxi) ) { // avoid overflow menge = (((maxi-actu)>>5)*(menge>>5))/(maxi>>10); } else { // and that is the simple formula menge = (menge*(maxi-actu)) / maxi; } } } else { // overfull? No production menge = 0; } return menge; } /************** TODO: properly handle more than one transformer! *******************************/ void fabrik_t::set_power_supply(uint32 supply) { if( transformers.empty() ) { return; } pumpe_t *const trans = dynamic_cast(transformers.front()); if( trans == NULL ) { return; } trans->set_power_supply(supply); } uint32 fabrik_t::get_power_supply() const { if( transformers.empty() ) { return 0; } pumpe_t *const trans = dynamic_cast(transformers.front()); if( trans == NULL ) { return 0; } return trans->get_power_supply(); } sint32 fabrik_t::get_power_consumption() const { if( transformers.empty() ) { return 0; } pumpe_t *const trans = dynamic_cast(transformers.front()); if( trans == NULL ) { return 0; } return trans->get_power_consumption(); } void fabrik_t::set_power_demand(uint32 demand) { if( transformers.empty() ) { return; } senke_t *const trans = dynamic_cast(transformers.front()); if( trans == NULL ) { return; } trans->set_power_demand(demand); } uint32 fabrik_t::get_power_demand() const { if( transformers.empty() ) { return 0; } senke_t *const trans = dynamic_cast(transformers.front()); if( trans == NULL ) { return 0; } return trans->get_power_demand(); } sint32 fabrik_t::get_power_satisfaction() const { if( transformers.empty() ) { return 0; } senke_t *const trans = dynamic_cast(transformers.front()); if( trans == NULL ) { return 0; } return trans->get_power_satisfaction(); } sint64 fabrik_t::get_power() const { if( desc->is_electricity_producer() ) { return ((sint64)get_power_supply() * (sint64)get_power_consumption()) >> leitung_t::FRACTION_PRECISION; } else { return -(((sint64)get_power_demand() * (sint64)get_power_satisfaction() + (((sint64)1 << leitung_t::FRACTION_PRECISION) - 1)) >> leitung_t::FRACTION_PRECISION); } } sint32 fabrik_t::get_input_stock(const goods_desc_t *typ) { sint32 menge = -1; for(ware_production_t const& i : input) { if (typ == i.get_typ()) { menge = i.menge >> precision_bits; break; } } return menge; } sint32 fabrik_t::get_output_stock(const goods_desc_t *typ) { sint32 menge = -1; for(ware_production_t const& i : output) { if (typ == i.get_typ()) { menge = i.menge >> precision_bits; break; } } return menge; } sint32 fabrik_t::liefere_an(const goods_desc_t *typ, sint32 menge) { if( typ==goods_manager_t::passengers ) { // book pax arrival and recalculate pax boost book_stat(menge, FAB_PAX_ARRIVED); arrival_stats_pax.book_arrival(menge); update_prodfactor_pax(); return menge; } else if( typ==goods_manager_t::mail ) { // book mail arrival and recalculate mail boost book_stat(menge, FAB_MAIL_ARRIVED); arrival_stats_mail.book_arrival(menge); update_prodfactor_mail(); return menge; } else { // case : freight for( uint32 in = 0; in < input.get_count(); in++ ){ ware_production_t& ware = input[in]; if( ware.get_typ() == typ ) { ware.book_stat( -menge, FAB_GOODS_TRANSIT ); // Resolve how much normalized production arrived, rounding up (since we rounded up when we removed it). const uint32 prod_factor = desc->get_supplier(in)->get_consumption(); const sint32 prod_delta = (sint32)((((sint64)menge << (DEFAULT_PRODUCTION_FACTOR_BITS + precision_bits)) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); // Activate inactive inputs. if( ware.menge <= 0 ) { inactive_inputs --; } ware.menge += prod_delta; if( welt->get_settings().get_just_in_time() >= 2 ){ // In JIT2 it is illegal to exceed input storage limit. All production that does so is discarded. if( ware.menge > ware.max ){ const sint32 prod_comp = ware.menge - ware.max; ware.menge = ware.max; // Apply demand penalty. Reactivate inactive demands. sint32 demand_old = ware.demand_buffer; ware.demand_buffer-= prod_comp; if( demand_old >= ware.max && ware.demand_buffer < ware.max ) inactive_demands-= 1; // Resolve how many goods we actually used (rounding to nearest) menge -= (sint32)(((sint64)prod_comp * (sint64)prod_factor + (1 << (DEFAULT_PRODUCTION_FACTOR_BITS + precision_bits - 1))) >> (DEFAULT_PRODUCTION_FACTOR_BITS + precision_bits)); } } else{ // avoid overflow const sint32 max = (sint32)((((sint64)FAB_MAX_INPUT << (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); if( ware.menge >= max ) { menge -= (sint32)(((sint64)menge * (sint64)(ware.menge - max) + (sint64)(prod_delta >> 1)) / (sint64)prod_delta); ware.menge = max - 1; } } ware.book_stat(menge, FAB_GOODS_RECEIVED); return menge; } } } // ware "typ" wird hier nicht verbraucht return -1; } sint8 fabrik_t::is_needed(const goods_desc_t *typ) const { for(ware_production_t const& i : input) { if( i.get_typ() == typ ) { // Ordering logic is done in ticks. This means that every supplier is given a chance to fulfill an order (which they do so fairly). return i.placing_orders; } } return -1; // not needed here } bool fabrik_t::is_active_consumer( koord k ) const { if ( consumer.is_contained(k) ) { return 0 < ( ( 1 << consumer.index_of(k) ) & consumer_active_last_month ); } return false; } sint32 fabrik_t::get_jit2_power_boost() const { if (!is_transformer_connected()) { return 0; } const sint32 power_satisfaction = get_power_satisfaction(); if( power_satisfaction >= ((sint32)1 << leitung_t::FRACTION_PRECISION) ) { // all demand fulfilled return (sint32)desc->get_electric_boost(); } else { // calculate bonus, rounding down return (sint32)(((sint64)desc->get_electric_boost() * (sint64)power_satisfaction) >> leitung_t::FRACTION_PRECISION); } } void fabrik_t::step(uint32 delta_t) { // Only do something if advancing in time. if( delta_t==0 ) { return; } /// Declare production control variables. // The production effort of the factory. sint32 prod; // Actual production effort done. sint32 work = 0; // Desired production effort of factory. sint32 want = 0; /// Boost logic. // Solve boosts. switch( boost_type ) { case BL_CLASSIC: { // JIT1 implementation for power bonus. if( !desc->is_electricity_producer() && scaled_electric_demand > 0 ) { // one may be thinking of linking this to actual production only prodfactor_electric = (sint32)(((sint64)desc->get_electric_boost() * (sint64)get_power_satisfaction() + (sint64)((sint64)1 << (leitung_t::FRACTION_PRECISION - 1))) >> leitung_t::FRACTION_PRECISION); } break; } case BL_POWER: { // get desired power boost amount sint32 const prodfactor_want = get_jit2_power_boost(); // calculate maximum change delta from change rate scaled by time const sint32 prodfactor_change = max(BOOST_POWER_CHANGE_RATE * delta_t / PRODUCTION_DELTA_T, 1); // limit rate of change of electricity boost (improve stability) const sint32 prodfactor_delta = prodfactor_want - prodfactor_electric; if( prodfactor_delta > prodfactor_change ) { // limit increase rate prodfactor_electric += prodfactor_change; } // limit decrease rate, off because of possible exploit // else if( prodfactor_delta < -prodfactor_change ) { // prodfactor_electric -= prodfactor_change; // } else { // no limit prodfactor_electric = prodfactor_want; } break; } default: { // No boost amounts to solve. break; } }; // Compute boost amount. const sint32 boost = get_prodfactor(); /// Compute base production. { // Calculate actual production. A remainder is used for extra precision. const uint32 remainder_bits = (PRODUCTION_DELTA_T_BITS + DEFAULT_PRODUCTION_FACTOR_BITS + DEFAULT_PRODUCTION_FACTOR_BITS - fabrik_t::precision_bits); const uint64 want_prod_long = (uint64)prodbase * (uint64)boost * (uint64)delta_t + (uint64)delta_amount_remainder; prod = (uint32)(want_prod_long >> remainder_bits); delta_amount_remainder = (uint32)(want_prod_long & ((1 << remainder_bits) - 1 )); } /// Perform the control logic. { // Common logic locals go here. // Amount of production change. sint32 prod_delta; // The amount of production available. sint32 prod_comp; // The amount of consumption available. sint32 cons_comp = 0; switch( control_type ) { case CL_PROD_CLASSIC: { // Classic producer logic. currently_requiring_power = false; currently_producing = false; uint32 power = 0; // produces something for( uint32 product = 0; product < output.get_count(); product++ ) { // source producer const uint32 menge_out = scale_output_production( product, prod ); if( menge_out > 0 ) { const sint32 p = (sint32)menge_out; // compute work factor const sint32 work_fact = work_from_production(prod, p); // work done is work done of maximum output if( work_fact > work ) { work = work_fact; } // produce if( output[product].menge < output[product].max ) { // to find out, if storage changed delta_amount += p; output[product].menge += p; output[product].book_stat((sint64)p * (sint64)desc->get_product(product)->get_factor(), FAB_GOODS_PRODUCED); // if less than 3/4 filled we neary always consume power currently_requiring_power |= (output[product].menge*4 < output[product].max*3); currently_producing = true; if( desc->is_electricity_producer() ) { // power station => produce power power += (uint32)( ((sint64)scaled_electric_demand * (sint64)(DEFAULT_PRODUCTION_FACTOR + prodfactor_pax + prodfactor_mail)) >> DEFAULT_PRODUCTION_FACTOR_BITS ); } } else { output[product].book_stat((sint64)(output[product].max - 1 - output[product].menge) * (sint64)desc->get_product(product)->get_factor(), FAB_GOODS_PRODUCED); output[product].menge = output[product].max - 1; } } } set_power_supply(power); break; } case CL_PROD_MANY: { // A producer with many outputs. if( inactive_outputs == output.get_count() ) { break; } currently_requiring_power = false; for( uint32 product = 0; product < output.get_count(); product++ ) { prod_comp = output[product].max - output[product].menge; // Ignore inactive outputs. if( prod_comp <= 0 ) { continue; } // get desired work factor const sint32 work_fact = output[product].calculate_output_production_rate(); // compute desired production prod_delta = work_scale_production(prod, work_fact); // Cannot produce more than can be stored. if( prod_delta > prod_comp ) { prod_delta = prod_comp; } currently_requiring_power = (prod_delta>0); delta_amount += prod_delta; output[product].menge += prod_delta; output[product].book_stat((sint64)prod_delta * (sint64)desc->get_product(product)->get_factor(), FAB_GOODS_PRODUCED); work += work_fact; } currently_producing = currently_requiring_power; // normalize work with respect to output number work /= output.get_count(); if( desc->is_electricity_producer() ) { // compute power production uint64 pp = ((uint64)scaled_electric_demand * (uint64)boost * (uint64)work) >> (DEFAULT_PRODUCTION_FACTOR_BITS + WORK_BITS); set_power_supply((uint32)pp); } break; } case CL_FACT_CLASSIC: { // Classic factory logic, work and want determined by the maximum output production rate. currently_requiring_power = false; currently_producing = false; // ok, calulate maximum allowed consumption. { sint32 min_menge = input[0].menge; sint32 consumed_menge = 0; for( uint32 index = 1; index < input.get_count(); index++ ) { if( input[index].menge < min_menge ) { min_menge = input[index].menge; } } // produces something for( uint32 product = 0; product < output.get_count(); product++ ) { // calculate production const sint32 p_menge = (sint32)scale_output_production( product, prod ); // Want the maximum production rate, this is so correct amount is ordered. if( p_menge > want ) { want = p_menge; } const sint32 menge_out = p_menge < min_menge ? p_menge : min_menge; // production smaller than possible due to consumption if( menge_out > consumed_menge ) { consumed_menge = menge_out; } if( menge_out > 0 ) { const sint32 p = menge_out; // produce if( output[product].menge < output[product].max ) { // to find out, if storage changed delta_amount += p; output[product].menge += p; output[product].book_stat((sint64)p * (sint64)desc->get_product(product)->get_factor(), FAB_GOODS_PRODUCED); // if less than 3/4 filled we neary always consume power currently_requiring_power |= (output[product].menge*4 < output[product].max*3); currently_producing = true; } else { output[product].book_stat((sint64)(output[product].max - 1 - output[product].menge) * (sint64)desc->get_product(product)->get_factor(), FAB_GOODS_PRODUCED); output[product].menge = output[product].max - 1; } } } uint32 power = 0; // and finally consume stock for( uint32 index = 0; index < input.get_count(); index++ ) { const uint32 v = consumed_menge; if( (uint32)input[index].menge > v + 1 ) { input[index].menge -= v; input[index].book_stat((sint64)v * (sint64)desc->get_supplier(index)->get_consumption(), FAB_GOODS_CONSUMED); if( desc->is_electricity_producer() ) { // power station => produce power power += (uint32)( ((sint64)scaled_electric_demand * (sint64)(DEFAULT_PRODUCTION_FACTOR + prodfactor_pax + prodfactor_mail)) >> DEFAULT_PRODUCTION_FACTOR_BITS ); } } else { input[index].book_stat((sint64)input[index].menge * (sint64)desc->get_supplier(index)->get_consumption(), FAB_GOODS_CONSUMED); input[index].menge = 0; } } // work done is consumption rate work = work_from_production(prod, consumed_menge); set_power_supply(power); } break; } case CL_FACT_MANY: { // Logic for a factory with many outputs. Work and want are based on weighted output rate so is more complicated. currently_requiring_power = false; currently_producing = false; if( inactive_outputs == output.get_count() ) { break; } { const bool no_input = inactive_inputs > 0; // Determine minimum input in form of production from all outputs. This limits production. // Determine minimum demand work rate from all inputs. This limits production rate. sint32 demand_work_limt = input[0].calculate_demand_production_rate(); cons_comp = input[0].menge; for( uint32 index = 1; index < input.get_count(); index++ ) { if( input[index].menge < cons_comp ) { cons_comp = input[index].menge; } sint32 const work_limit = input[index].calculate_demand_production_rate(); if( work_limit < demand_work_limt ) { demand_work_limt = work_limit; } } cons_comp *= output.get_count(); for( uint32 product = 0; product < output.get_count(); product++ ) { prod_comp = output[product].max - output[product].menge; // Ignore inactive outputs. if( prod_comp <= 0 ) { continue; } // get desired work factor const sint32 work_fact = min(output[product].calculate_output_production_rate(), demand_work_limt); // compute desired production prod_delta = work_scale_production(prod, work_fact); // limit to maximum storage if( prod_delta > prod_comp ) { prod_delta = prod_comp; } // credit desired production for want want += prod_delta; // skip producing anything if no input if( no_input ) { continue; } // enforce input limits on production if( prod_delta > cons_comp ) { // not enough input prod_delta = cons_comp; cons_comp = 0; } else { // enough input cons_comp -= prod_delta; } // register inactive outputs if( prod_delta == prod_comp ) { inactive_outputs++; if( inactive_outputs == output.get_count() ) { currently_requiring_power = false; } } // credit output work done work += prod_delta; currently_requiring_power = true; currently_producing = true; // Produce output delta_amount += prod_delta; output[product].menge += prod_delta; output[product].book_stat((sint64)prod_delta * (sint64)desc->get_product(product)->get_factor(), FAB_GOODS_PRODUCED); } // normalize want with respect to output number want /= output.get_count(); // skip consuming anything if no input if( no_input ) { break; } // compute work done const sint32 consumed = work / output.get_count(); work = work_from_production(prod, consumed); // consume inputs for( uint32 index = 0; index < input.get_count(); index++ ) { input[index].menge -= consumed; input[index].book_stat((sint64)consumed * (sint64)desc->get_supplier(index)->get_consumption(), FAB_GOODS_CONSUMED); // register inactive inputs if( input[index].menge <= 0 ) { currently_requiring_power = false; input[index].menge = 0; inactive_inputs ++; } } if( desc->is_electricity_producer() ) { // compute power production uint64 pp = ((uint64)scaled_electric_demand * (uint64)boost * (uint64)work) >> (DEFAULT_PRODUCTION_FACTOR_BITS + WORK_BITS); set_power_supply((uint32)pp); } } break; } case CL_CONS_CLASSIC: { uint32 power = 0; // Classic consumer logic. currently_requiring_power = false; currently_producing = false; if( desc->is_electricity_producer() ) { // power station => start with no production power = 0; } // Always want to consume at rate of prod. want = prod; // finally consume stock for( uint32 index = 0; index < input.get_count(); index++ ) { const uint32 v = prod; if( (uint32)input[index].menge > v + 1 ) { input[index].menge -= v; input[index].book_stat((sint64)v * (sint64)desc->get_supplier(index)->get_consumption(), FAB_GOODS_CONSUMED); currently_requiring_power = true; currently_producing = true; if( desc->is_electricity_producer() ) { // power station => produce power power += (uint32)( ((sint64)scaled_electric_demand * (sint64)(DEFAULT_PRODUCTION_FACTOR + prodfactor_pax + prodfactor_mail)) >> DEFAULT_PRODUCTION_FACTOR_BITS ); } work += 1 << WORK_BITS; // to find out, if storage changed delta_amount += v; } else { if( desc->is_electricity_producer() ) { // power station => produce power power += (uint32)( (((sint64)scaled_electric_demand * (sint64)(DEFAULT_PRODUCTION_FACTOR + prodfactor_pax + prodfactor_mail)) >> DEFAULT_PRODUCTION_FACTOR_BITS) * input[index].menge / (v + 1) ); } delta_amount += input[index].menge; work += work_from_production(prod, input[index].menge); input[index].book_stat((sint64)input[index].menge * (sint64)desc->get_supplier(index)->get_consumption(), FAB_GOODS_CONSUMED); input[index].menge = 0; } } // normalize work with respect to input number work /= input.get_count(); set_power_supply(power); break; } case CL_CONS_MANY: { // Consumer logic for many inputs. Work done is based on the average consumption of all inputs. currently_requiring_power = false; currently_producing = false; // always consume prod want = prod; // Do nothing if we cannot consume anything. if( inactive_inputs == input.get_count() ) { break; } for( uint32 index = 0; index < input.get_count(); index++ ) { // Only process active inputs; if( input[index].menge <= 0 ) { continue; } currently_requiring_power = true; currently_producing = true; sint32 const consumption_prod = work_scale_production(prod, input[index].calculate_demand_production_rate()); // limit consumption to minimum of production or storage prod_delta = input[index].menge > consumption_prod ? consumption_prod : input[index].menge; // add to work done work += prod_delta; // consume input delta_amount += prod_delta; input[index].menge -= prod_delta; input[index].book_stat((sint64)prod_delta * (sint64)desc->get_supplier(index)->get_consumption(), FAB_GOODS_CONSUMED); // register inactive input if( input[index].menge <= 0 ) { inactive_inputs++; if( inactive_inputs == input.get_count() ) { currently_requiring_power = false; } } } // normalise and convert work into work factor work = work_from_production(prod, work / input.get_count()); if( desc->is_electricity_producer() ) { // compute power production uint64 pp = ((uint64)scaled_electric_demand * (uint64)boost * (uint64)work) >> (DEFAULT_PRODUCTION_FACTOR_BITS + WORK_BITS); set_power_supply((uint32)pp); } break; } case CL_ELEC_PROD: { // A simple no input electricity producer, like a solar array. // always maximum work currently_requiring_power = true; currently_producing = true; work = 1 << WORK_BITS; delta_amount += prod; // compute power production uint64 pp = ((uint64)scaled_electric_demand * (uint64)boost) >> DEFAULT_PRODUCTION_FACTOR_BITS; set_power_supply((uint32)pp); break; } case CL_ELEC_CLASSIC: { // Classic no input power producer. currently_requiring_power = false; work = 1 << WORK_BITS; // power station? => produce power if( desc->is_electricity_producer() ) { currently_requiring_power = true; currently_producing = true; set_power_supply((uint32)( ((sint64)scaled_electric_demand * (sint64)(DEFAULT_PRODUCTION_FACTOR + prodfactor_pax + prodfactor_mail)) >> DEFAULT_PRODUCTION_FACTOR_BITS )); } break; } case CL_NONE: default: { currently_producing = false; currently_requiring_power = false; // None always produces maximum for whatever reason. Also default. work = 1 << WORK_BITS; break; } } } /// Ordering logic. switch( demand_type ) { case DL_SYNC: { // Synchronous ordering. All buffers are filled the same amount in parallel based on desired work factor. if( want == 0 || inactive_demands > 0 ) { // No ordering if any are overfull or there is no want. break; } for( uint32 index = 0; index < input.get_count(); index++ ) { input[index].demand_buffer += want; // register inactive demand buffer if( input[index].demand_buffer >= input[index].max ) { inactive_demands++; } } break; } case DL_ASYNC: { // Asynchronous ordering. Each buffer tries to order scaled back by the demand work factor. if( want == 0 || inactive_demands == input.get_count() ) { // No ordering if all are overfull or no want. break; } for( uint32 index = 0; index < input.get_count(); index++ ) { // Skip inactive demand buffers. if( input[index].demand_buffer >= input[index].max ) { continue; } input[index].demand_buffer += max(work_scale_production(want, input[index].calculate_demand_production_rate()), 1); // register inactive demand buffer if( input[index].demand_buffer >= input[index].max ) { inactive_demands++; } } break; } default: { // Nothing to order. break; } } /// Book the weighted sums for statistics. book_weighted_sums( delta_t ); /// Power ordering logic. switch( boost_type ) { case BL_CLASSIC: { // draw a fixed amount of power when working sufficiently, otherwise draw no power if( !desc->is_electricity_producer() ) { if( currently_requiring_power ) { set_power_demand(scaled_electric_demand); } else { set_power_demand(0); } } break; } case BL_POWER: { // compute power demand uint64 pd = ((uint64)scaled_electric_demand * (uint64)boost * (uint64)work) >> (DEFAULT_PRODUCTION_FACTOR_BITS + WORK_BITS); set_power_demand((uint32)pd); break; } default: { // No power is ordered. break; } }; /// Periodic tasks. delta_t_sum += delta_t; if( delta_t_sum > PRODUCTION_DELTA_T ) { delta_t_sum = delta_t_sum % PRODUCTION_DELTA_T; // distribute, if min shipment waiting. for( uint32 product = 0; product < output.get_count(); product++ ) { // either more than ten or nearly full (if there are less than ten output) if( output[product].menge >= output[product].min_shipment ) { verteile_waren( product ); INT_CHECK("simfab 636"); } } // Order required inputs. for( uint32 index = 0; index < input.get_count(); index++ ) { switch( demand_type ) { case DL_SYNC: case DL_ASYNC: { // Orders based on demand buffer. input[index].placing_orders = (input[index].demand_buffer > 0); break; } case DL_OLD: { // Orders based on storage and maximum transit. input[index].placing_orders = (input[index].menge < input[index].max && (input[index].max_transit == 0 || input[index].get_in_transit() < input[index].max_transit) ); break; } default: { // Unknown order logic? break; } } } recalc_factory_status(); // rescale delta_amount here: all products should be produced at least once // (if consumer only: all supplements should be consumed once) const uint32 min_change = output.empty() ? input.get_count() : output.get_count(); if( (delta_amount >> fabrik_t::precision_bits) > min_change ) { // we produced some real quantity => smoke smoke(); // chance to expand every 256 rounds of activities, after which activity count will return to 0 (overflow behaviour) if( (++activity_count)==0 ) { if( desc->get_field_group() ) { if( fields.get_count()get_field_group()->get_max_fields() ) { // spawn new field with given probability add_random_field(desc->get_field_group()->get_probability()); } } else { if( times_expanded < desc->get_expand_times() ) { if( simrand(10000) < desc->get_expand_probability() ) { set_base_production( prodbase + desc->get_expand_minimum() + simrand( desc->get_expand_range() ) ); ++times_expanded; } } } } INT_CHECK("simfab 558"); // reset for next cycle delta_amount = 0; } } /// advance arrival slot at calculated interval and recalculate boost where necessary delta_slot += delta_t; const sint32 periods = welt->get_settings().get_factory_arrival_periods(); const sint32 slot_interval = (1 << (PERIOD_BITS - SLOT_BITS)) * periods; while( delta_slot>slot_interval ) { delta_slot -= slot_interval; const sint32 pax_result = arrival_stats_pax.advance_slot(); if( pax_result&ARRIVALS_CHANGED || (periods>1 && pax_result&ACTIVE_SLOTS_INCREASED && arrival_stats_pax.get_active_slots()*periods>SLOT_COUNT ) ) { update_prodfactor_pax(); } const sint32 mail_result = arrival_stats_mail.advance_slot(); if( mail_result&ARRIVALS_CHANGED || (periods>1 && mail_result&ACTIVE_SLOTS_INCREASED && arrival_stats_mail.get_active_slots()*periods>SLOT_COUNT ) ) { update_prodfactor_mail(); } } } class distribute_ware_t { public: ware_t ware; /// goods to be routed to consumer halthandle_t halt; /// potential start halt sint32 space_left; /// free space at halt sint32 amount_waiting; /// waiting goods at halt for same destination as ware private: sint32 ratio_free_space; /// ratio of free space at halt (=0 for overflowing station) public: distribute_ware_t( halthandle_t h, sint32 l, sint32 t, sint32 a, ware_t w ) { halt = h; space_left = l; amount_waiting = a; ware = w; // ensure overfull stations compare equal allowing tie breaker clause (amount waiting) sint32 space_total = t > 0 ? t : 1; ratio_free_space = space_left > 0 ? ((sint64)space_left << fabrik_t::precision_bits) / space_total : 0; } distribute_ware_t() {} static bool compare(const distribute_ware_t &dw1, const distribute_ware_t &dw2) { return (dw1.ratio_free_space > dw2.ratio_free_space) || (dw1.ratio_free_space == dw2.ratio_free_space && dw1.amount_waiting <= dw2.amount_waiting); } }; /** * distribute stuff to all best destination */ void fabrik_t::verteile_waren(const uint32 product) { // wohin liefern ? if( consumer.empty() ) { return; } // not connected? const planquadrat_t *plan = welt->access(pos.get_2d()); if( plan == NULL ) { dbg->fatal("fabrik_t::verteile_waren", "%s has not distribution target", get_name() ); } if( plan->get_haltlist_count() == 0 ) { return; } static vector_tpl dist_list(16); dist_list.clear(); // to distribute to all target equally, we use this counter, for the source hald, and target factory, to try first output[product].index_offset++; /* distribute goods to factory * that has not an overflowing input storage * also prevent stops from overflowing, if possible * Since we can called with menge>max/2 are at least 10 are there, we must first limit the amount we distribute */ // We already know the distribution amount. However it has to be converted from factory units into real units. const uint32 prod_factor = desc->get_product(product)->get_factor(); sint32 menge = (sint32)(((sint64)output[product].min_shipment * (sint64)(prod_factor)) >> (DEFAULT_PRODUCTION_FACTOR_BITS + precision_bits)); // ok, first generate list of possible destinations const halthandle_t *haltlist = plan->get_haltlist(); for( unsigned i=0; iget_haltlist_count(); i++ ) { halthandle_t halt = haltlist[(i + output[product].index_offset) % plan->get_haltlist_count()]; if( !halt->get_ware_enabled() ) { continue; } for( uint32 n=0; nis_needed(output[product].get_typ()); if( needed>=0 ) { ware_t ware(output[product].get_typ()); ware.amount = menge; ware.to_factory = 1; ware.set_target_pos( lieferziel ); unsigned w; // find the index in the target factory for( w = 0; w < ziel_fab->get_input().get_count() && ziel_fab->get_input()[w].get_typ() != ware.get_desc(); w++ ) { // empty } // if only overflown factories found => deliver to first // else deliver to non-overflown factory if( !(welt->get_settings().get_just_in_time() != 0) ) { // without production stop when target overflowing, distribute to least overflow target const sint32 fab_left = ziel_fab->get_input()[w].max - ziel_fab->get_input()[w].menge; dist_list.insert_ordered( distribute_ware_t( halt, fab_left, ziel_fab->get_input()[w].max, (sint32)halt->get_ware_fuer_zielpos(output[product].get_typ(),ware.get_target_pos()), ware ), distribute_ware_t::compare ); } else if( needed > 0 ) { // we are not overflowing: Station can only store up to a maximum amount of goods per square const sint32 halt_left = (sint32)halt->get_capacity(2) - (sint32)halt->get_ware_summe(ware.get_desc()); dist_list.insert_ordered( distribute_ware_t( halt, halt_left, halt->get_capacity(2), (sint32)halt->get_ware_fuer_zielpos(output[product].get_typ(),ware.get_target_pos()), ware ), distribute_ware_t::compare ); } } } } } // Auswertung der Ergebnisse if( !dist_list.empty() ) { distribute_ware_t *best = NULL; for(distribute_ware_t & i : dist_list) { // now search route int const result = haltestelle_t::search_route(&i.halt, 1U, welt->get_settings().is_no_routing_over_overcrowding(), i.ware); if( result == haltestelle_t::ROUTE_OK || result == haltestelle_t::ROUTE_WALK ) { // we can deliver to this destination best = &i; break; } } if( best == NULL ) { return; // no route for any destination } halthandle_t &best_halt = best->halt; ware_t &best_ware = best->ware; // now process found route const sint32 space_left = (welt->get_settings().get_just_in_time() != 0 ) ? best->space_left : (sint32)best_halt->get_capacity(2) - (sint32)best_halt->get_ware_summe(best_ware.get_desc()); menge = min( menge, 9 + space_left ); // ensure amount is not negative ... if( menge<0 ) { menge = 0; } // since it is assigned here to an unsigned variable! best_ware.amount = menge; if( space_left<0 ) { // find, what is most waiting here from us ware_t most_waiting(output[product].get_typ()); most_waiting.amount = 0; for(koord const& n : consumer) { uint32 const amount = best_halt->get_ware_fuer_zielpos(output[product].get_typ(), n); if( amount > most_waiting.amount ) { most_waiting.set_target_pos(n); most_waiting.amount = amount; } } // we will reroute some goods if( best->amount_waiting==0 && most_waiting.amount>0 ) { // remove something from the most waiting goods if( best_halt->recall_ware( most_waiting, min((sint32)(most_waiting.amount/2), 1 - space_left) ) ) { best_ware.amount += most_waiting.amount; } else { // overcrowded with other stuff (not from us) return; } // refund JIT2 demand buffers for rerouted goods if( welt->get_settings().get_just_in_time() >= 2 ) { // locate destination factory fabrik_t *fab = get_fab( most_waiting.get_target_pos() ); if( fab ) { for( uint32 input = 0; input < fab->input.get_count(); input++ ) { ware_production_t& w = fab->input[input]; if ( w.get_typ()->get_index() == most_waiting.get_index() ) { // correct ware to refund found // refund demand buffers, deactivating if required const uint32 prod_factor = fab->desc->get_supplier(input)->get_consumption(); const sint32 prod_delta = (sint32)((((sint64)(most_waiting.amount) << (DEFAULT_PRODUCTION_FACTOR_BITS + precision_bits)) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); const sint32 demand = w.demand_buffer; w.demand_buffer += prod_delta; if( demand < w.max && w.demand_buffer >= w.max ) { fab->inactive_demands++; } // refund successful break; } } } } } else { return; } } // Since menge might have been mutated, it must be converted back. This might introduce some error with some prod factors which is always rounded up. const sint32 prod_delta = (sint32)((((sint64)menge << (DEFAULT_PRODUCTION_FACTOR_BITS + precision_bits)) + (sint64)(prod_factor - 1)) / (sint64)prod_factor); // If the output is inactive, reactivate it if( output[product].menge == output[product].max && prod_delta > 0 ) inactive_outputs-= 1; output[product].menge -= prod_delta; best_halt->starte_mit_route(best_ware); best_halt->recalc_status(); fabrik_t::apply_transit( &best_ware ); // add as active destination consumer_active_last_month |= (1 << consumer.index_of(best_ware.get_target_pos())); output[product].book_stat(best_ware.amount, FAB_GOODS_DELIVERED); } } void fabrik_t::new_month() { // calculate weighted averages if( aggregate_weight > 0 ) { set_stat( weighted_sum_production / aggregate_weight, FAB_PRODUCTION ); set_stat( weighted_sum_boost_electric / aggregate_weight, FAB_BOOST_ELECTRIC ); set_stat( weighted_sum_boost_pax / aggregate_weight, FAB_BOOST_PAX ); set_stat( weighted_sum_boost_mail / aggregate_weight, FAB_BOOST_MAIL ); set_stat( weighted_sum_power / aggregate_weight, FAB_POWER ); } // update statistics for input and output goods for( uint32 in = 0; in < input.get_count(); in++ ){ input[in].roll_stats( desc->get_supplier(in)->get_consumption(), aggregate_weight ); } for( uint32 out = 0; out < output.get_count(); out++ ){ output[out].roll_stats( desc->get_product(out)->get_factor(), aggregate_weight ); } consumer_active_last_month = 0; // advance statistics a month for( int s = 0; s < MAX_FAB_STAT; ++s ) { for( int m = MAX_MONTH - 1; m > 0; --m ) { statistics[m][s] = statistics[m-1][s]; } statistics[0][s] = 0; } weighted_sum_production = 0; weighted_sum_boost_electric = 0; weighted_sum_boost_pax = 0; weighted_sum_boost_mail = 0; weighted_sum_power = 0; aggregate_weight = 0; // restore the current values set_stat( get_current_production(), FAB_PRODUCTION ); set_stat( prodfactor_electric, FAB_BOOST_ELECTRIC ); set_stat( prodfactor_pax, FAB_BOOST_PAX ); set_stat( prodfactor_mail, FAB_BOOST_MAIL ); set_stat( get_power(), FAB_POWER ); // since target cities' population may be increased -> re-apportion pax/mail demand recalc_demands_at_target_cities(); } // static ! uint8 fabrik_t::status_to_color[5] = {COL_RED, COL_ORANGE, COL_GREEN, COL_YELLOW, COL_WHITE }; //#define FL_WARE_NULL 1 #define FL_WARE_ALLENULL 2 #define FL_WARE_LIMIT 4 #define FL_WARE_ALLELIMIT 8 #define FL_WARE_UEBER75 16 #define FL_WARE_ALLEUEBER75 32 #define FL_WARE_FEHLT_WAS 64 /* returns the status of the current factory, as well as output */ void fabrik_t::recalc_factory_status() { uint64 warenlager; char status_ein; char status_aus; int haltcount=welt->access(pos.get_2d())->get_haltlist_count(); // set bits for input warenlager = 0; total_transit = 0; status_ein = FL_WARE_ALLELIMIT; uint32 i = 0; for(ware_production_t const& j : input ) { if( j.menge >= j.max ) { status_ein |= FL_WARE_LIMIT; } else { status_ein &= ~FL_WARE_ALLELIMIT; } warenlager+= (uint64)j.menge * (uint64)(desc->get_supplier(i++)->get_consumption()); total_transit += j.get_in_transit(); if( (j.menge >> fabrik_t::precision_bits) == 0 ) { status_ein |= FL_WARE_FEHLT_WAS; } } warenlager >>= fabrik_t::precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS; if( warenlager==0 ) { status_ein |= FL_WARE_ALLENULL; } total_input = (uint32)warenlager; // one ware missing, but producing if( status_ein & FL_WARE_FEHLT_WAS && !output.empty() && haltcount > 0 ) { status = STATUS_BAD; return; } // set bits for output warenlager = 0; status_aus = FL_WARE_ALLEUEBER75|FL_WARE_ALLENULL; i = 0; for(ware_production_t const& j : output ) { if( j.menge > 0 ) { status_aus &= ~FL_WARE_ALLENULL; if( j.menge >= 0.75 * j.max ) { status_aus |= FL_WARE_UEBER75; } else { status_aus &= ~FL_WARE_ALLEUEBER75; } warenlager += (uint64)j.menge * (uint64)(desc->get_product(i)->get_factor()); status_aus &= ~FL_WARE_ALLENULL; } else { // menge = 0 status_aus &= ~FL_WARE_ALLEUEBER75; } } warenlager >>= fabrik_t::precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS; total_output = (uint32)warenlager; // now calculate status bar if( input.empty() ) { // does not consume anything, should just produce if( output.empty() ) { // does also not produce anything status = STATUS_NOTHING; } else if( status_aus&FL_WARE_ALLEUEBER75 || status_aus&FL_WARE_UEBER75 ) { status = STATUS_INACTIVE; // not connected? if(haltcount>0) { if(status_aus&FL_WARE_ALLEUEBER75) { status = STATUS_BAD; // connect => needs better service } else { status = STATUS_MEDIUM; // connect => needs better service for at least one product } } } else { status = STATUS_GOOD; } } else if( output.empty() ) { // nothing to produce if(status_ein&FL_WARE_ALLELIMIT) { // we assume not served status = STATUS_BAD; } else if(status_ein&FL_WARE_LIMIT) { // served, but still one at limit status = STATUS_MEDIUM; } else if(status_ein&FL_WARE_ALLENULL) { status = STATUS_INACTIVE; // assume not served if(haltcount>0) { // there is a halt => needs better service status = STATUS_BAD; } } else { status = STATUS_GOOD; } } else { // produces and consumes if((status_ein&FL_WARE_ALLELIMIT)!=0 && (status_aus&FL_WARE_ALLEUEBER75)!=0) { status = STATUS_BAD; } else if((status_ein&FL_WARE_ALLELIMIT)!=0 || (status_aus&FL_WARE_ALLEUEBER75)!=0) { status = STATUS_MEDIUM; } else if((status_ein&FL_WARE_ALLENULL)!=0 && (status_aus&FL_WARE_ALLENULL)!=0) { // not producing status = STATUS_INACTIVE; } else if(haltcount>0 && ((status_ein&FL_WARE_ALLENULL)!=0 || (status_aus&FL_WARE_ALLENULL)!=0)) { // not producing but out of supply status = STATUS_MEDIUM; } else { status = STATUS_GOOD; } } } void fabrik_t::open_info_window() { gebaeude_t *gb = welt->lookup(pos)->find(); create_win(new fabrik_info_t(this, gb), w_info, (ptrdiff_t)this ); } void fabrik_t::info_prod(cbuffer_t& buf) const { buf.clear(); buf.append( translator::translate("Durchsatz") ); buf.append(get_current_production(), 0); buf.append( translator::translate("units/day") ); buf.printf(translator::translate("Produces: %.1f units/minute"), get_production_per_second() * 60.0 ); if (!output.empty()) { buf.append("\n\n"); buf.append(translator::translate("Produktion")); for (uint32 index = 0; index < output.get_count(); index++) { goods_desc_t const *const type = output[index].get_typ(); sint64 const pfactor = (sint64)desc->get_product(index)->get_factor(); buf.append("\n - "); if( welt->get_settings().get_just_in_time() >= 2 ) { double const pfraction = (double)pfactor / (double)DEFAULT_PRODUCTION_FACTOR; double const storage_unit = (double)(1 << fabrik_t::precision_bits); buf.printf(translator::translate("%s %.0f%% : %.0f/%.0f @ %.1f%%"), translator::translate(type->get_name()), pfraction * 100.0, (double)output[index].menge * pfraction / storage_unit, (double)output[index].max * pfraction / storage_unit, (double)output[index].calculate_output_production_rate() * 100.0 / (double)(1 << 16) ); } else { buf.printf("%s %u/%u%s", translator::translate(type->get_name()), (uint32) convert_goods( (sint64)output[index].menge * pfactor), (uint32) convert_goods( (sint64)output[index].max * pfactor), translator::translate(type->get_mass()) ); if(type->get_catg() != 0) { buf.append(", "); buf.append(translator::translate(type->get_catg_name())); } buf.printf(", %u%%", (uint32)((FAB_PRODFACT_UNIT_HALF + (sint32)pfactor * 100) >> DEFAULT_PRODUCTION_FACTOR_BITS)); } } } if (!input.empty()) { buf.append("\n\n"); buf.append(translator::translate("Verbrauch")); for (uint32 index = 0; index < input.get_count(); index++) { sint64 const pfactor = (sint64)desc->get_supplier(index)->get_consumption(); const char *cat_name = ""; const char *cat_seperator = ""; if( input[index].get_typ()->get_catg() > 0 ) { cat_name = translator::translate(input[index].get_typ()->get_catg_name()); cat_seperator = ", "; } buf.append("\n - "); if( welt->get_settings().get_just_in_time() >= 2 ) { double const pfraction = (double)pfactor / (double)DEFAULT_PRODUCTION_FACTOR; double const storage_unit = (double)(1 << fabrik_t::precision_bits); buf.printf(translator::translate("%s %.0f%% : %.0f+%u/%.0f%s, %s%s%.1f%%"), translator::translate(input[index].get_typ()->get_name()), pfraction * 100.0, (double)input[index].menge * pfraction / storage_unit, (uint32)input[index].get_in_transit(), (double)input[index].max * pfraction / storage_unit, translator::translate(input[index].get_typ()->get_mass()), cat_name, cat_seperator, (double)input[index].calculate_demand_production_rate() * 100.0 / (double)(1 << 16) ); } else if( welt->get_settings().get_factory_maximum_intransit_percentage() ) { buf.printf("%s %u/%i(%i)/%u%s, %s%s%u%%", translator::translate(input[index].get_typ()->get_name()), (uint32) convert_goods( (sint64)input[index].menge * pfactor), input[index].get_in_transit(), input[index].max_transit, (uint32) convert_goods( (sint64)input[index].max * pfactor), translator::translate(input[index].get_typ()->get_mass()), cat_name, cat_seperator, (uint32)((FAB_PRODFACT_UNIT_HALF + (sint32)pfactor * 100) >> DEFAULT_PRODUCTION_FACTOR_BITS) ); } else { buf.printf("%s %u/%i/%u%s, %s%s%u%%", translator::translate(input[index].get_typ()->get_name()), (uint32) convert_goods( (sint64)input[index].menge * pfactor), input[index].get_in_transit(), (uint32) convert_goods( (sint64)input[index].max * pfactor), translator::translate(input[index].get_typ()->get_mass()), cat_name, cat_seperator, (uint32)((FAB_PRODFACT_UNIT_HALF + (sint32)pfactor * 100) >> DEFAULT_PRODUCTION_FACTOR_BITS) ); } } } } void fabrik_t::info_conn(cbuffer_t& buf) const { buf.clear(); const planquadrat_t *plan = welt->access(get_pos().get_2d()); if(plan && plan->get_haltlist_count()>0) { buf.append(translator::translate("Connected stops")); for( uint i=0; iget_haltlist_count(); i++ ) { buf.printf("\n - %s", plan->get_haltlist()[i]->get_name() ); } } } void fabrik_t::finish_rd() { // adjust production base to be at least as large as fields productivity uint32 prodbase_adjust = 1; const field_group_desc_t *fd = desc->get_field_group(); if(fd) { for(uint32 i=0; iget_field_class( fields[i].field_class_index ); if (fc) { prodbase_adjust += fc->get_field_production(); } } } // set production, update all production related numbers set_base_production( max(prodbase, prodbase_adjust) ); // now we have a valid storage limit if( welt->get_settings().is_crossconnect_factories() ) { for(fabrik_t* const fab : welt->get_fab_list() ) { fab->add_supplier(this); } } else { // add as supplier to target(s) for(uint32 i=0; iadd_supplier(pos.get_2d()); consumer[i] = fab2->get_pos().get_2d(); } else { // remove this ... dbg->warning( "fabrik_t::finish_rd()", "No factory at expected position %s!", consumer[i].get_str() ); consumer.remove_at(i); i--; } } } // Now rebuild input/output activity information. if( welt->get_settings().get_just_in_time() >= 2 ){ inactive_inputs = inactive_outputs = inactive_demands = 0; for( uint32 in = 0; in < input.get_count(); in++ ){ if( input[in].menge > input[in].max ) { input[in].menge = input[in].max; } input[in].placing_orders = (input[in].demand_buffer > 0); // Also do a sanity check. People might try and force their saves to JIT2 mode via reloading so we need to catch any massive values. if( welt->get_settings().get_just_in_time() >= 2 && welt->get_settings().get_factory_maximum_intransit_percentage() != 0 ){ const sint32 demand_minmax = (sint32)((sint64)input[in].max * (sint64)welt->get_settings().get_factory_maximum_intransit_percentage() / (sint64)100); if( input[in].demand_buffer < (-demand_minmax) ) { input[in].demand_buffer = -demand_minmax; } else if( input[in].demand_buffer > (input[in].max + demand_minmax) ) { input[in].demand_buffer = input[in].max; } } } for( uint32 out = 0 ; out < output.get_count() ; out++ ){ if( output[out].menge >= output[out].max ) { output[out].menge = output[out].max; } } rebuild_inactive_cache(); } else { // Classic order logic. for( uint32 in = 0; in < input.get_count(); in++ ){ input[in].placing_orders = (input[in].menge < input[in].max && input[in].get_in_transit() < input[in].max_transit); } } // set initial power boost if ( boost_type == BL_POWER ) { prodfactor_electric = get_jit2_power_boost(); } } void fabrik_t::rotate90( const sint16 y_size ) { rotate = (rotate+3)%desc->get_building()->get_all_layouts(); pos_origin.rotate90( y_size ); pos_origin.x -= desc->get_building()->get_x(rotate)-1; pos.rotate90( y_size ); for(koord & i : consumer) { i.rotate90(y_size); } for(koord & i : suppliers) { i.rotate90(y_size); } for(field_data_t & i : fields) { i.location.rotate90(y_size); } } void fabrik_t::add_supplier(koord ziel) { // Updated maximum in-transit. Only used for classic support. if( welt->get_settings().get_factory_maximum_intransit_percentage() && !suppliers.is_contained(ziel) && welt->get_settings().get_just_in_time() < 2 ) { if( fabrik_t *fab = get_fab( ziel ) ) { for( uint32 i=0; i < fab->get_output().get_count(); i++ ) { const ware_production_t &w_out = fab->get_output()[i]; // now update transit limits for(ware_production_t &w : input ) { if( w_out.get_typ() == w.get_typ() ) { #ifdef TRANSIT_DISTANCE sint64 distance = koord_distance( fab->get_pos(), get_pos() ); // calculate next mean by the following formula: average + (next - average)/(n+1) w.count_suppliers ++; sint64 next = 1 + ( distance * welt->get_settings().get_factory_maximum_intransit_percentage() * (w.max >> fabrik_t::precision_bits) ) / 131072; w.max_transit += (next - w.max_transit)/(w.count_suppliers); #else const sint32 max_storage = (sint32)(1 + ( ((sint64)w_out.max * (sint64)welt->get_settings().get_factory_maximum_intransit_percentage() ) >> fabrik_t::precision_bits) / 100); const sint32 old_max_transit = w.max_transit; w.max_transit += max_storage; if( w.max_transit < old_max_transit ) { // we have overflown, so we use the max value w.max_transit = 0x7fffffff; } #endif break; } } } // since there could be more than one good, we have to iterate over all of them } } suppliers.insert_unique_ordered( ziel, RelativeDistanceOrdering(pos.get_2d()) ); } void fabrik_t::remove_supplier(koord pos) { suppliers.remove(pos); // Updated maximum in-transit. Only used for classic support. if( welt->get_settings().get_factory_maximum_intransit_percentage() && welt->get_settings().get_just_in_time() < 2 ) { // set to zero for(ware_production_t &w : input ) { w.max_transit = 0; } // unfourtunately we have to bite the bullet and recalc the values from scratch ... for(koord ziel : suppliers ) { if( fabrik_t *fab = get_fab( ziel ) ) { for( uint32 i=0; i < fab->get_output().get_count(); i++ ) { const ware_production_t &w_out = fab->get_output()[i]; // now update transit limits for(ware_production_t &w : input ) { if( w_out.get_typ() == w.get_typ() ) { #ifdef TRANSIT_DISTANCE sint64 distance = koord_distance( fab->get_pos(), get_pos() ); // calculate next mean by the following formula: average + (next - average)/(n+1) w.count_suppliers ++; sint64 next = 1 + ( distance * welt->get_settings().get_factory_maximum_intransit_percentage() * (w.max >> fabrik_t::precision_bits) ) / 131072; w.max_transit += (next - w.max_transit)/(w.count_suppliers); #else const sint32 max_storage = (sint32)(1 + ( ((sint64)w_out.max * (sint64)welt->get_settings().get_factory_maximum_intransit_percentage() ) >> fabrik_t::precision_bits) / 100); const sint32 old_max_transit = w.max_transit; w.max_transit += max_storage; if( w.max_transit < old_max_transit ) { // we have overflown, so we use the max value w.max_transit = 0x7fffffff; } #endif break; } } } // since there could be more than one good, we have to iterate over all of them } } } } /** crossconnect everything possible */ void fabrik_t::add_all_suppliers() { for(int i=0; i < desc->get_supplier_count(); i++) { const factory_supplier_desc_t *supplier = desc->get_supplier(i); const goods_desc_t *ware = supplier->get_input_type(); for(fabrik_t* const fab : welt->get_fab_list()) { // connect to an existing one, if this is an producer if(fab!=this && fab->get_output_stock(ware) > -1) { // add us to this factory // will also add to our suppliers list fab->add_consumer(pos.get_2d()); } } } } /* adds a new supplier to this factory * fails if no matching goods are there */ bool fabrik_t::add_supplier(fabrik_t* fab) { for(int i=0; i < desc->get_supplier_count(); i++) { const factory_supplier_desc_t *supplier = desc->get_supplier(i); const goods_desc_t *ware = supplier->get_input_type(); // connect to an existing one, if this is an producer if( fab!=this && fab->get_output_stock(ware) > -1 ) { // add us to this factory fab->add_consumer(pos.get_2d()); return true; } } return false; } void fabrik_t::get_tile_list( vector_tpl &tile_list ) const { gebaeude_t *gb = welt->lookup( pos )->find(); koord size = get_desc()->get_building()->get_size( gb->get_tile()->get_layout() ); const koord3d pos0 = gb->get_pos() - gb->get_tile()->get_offset(); // get origin koord k; tile_list.clear(); // add all tiles for( k.y = 0; k.y < size.y; k.y++ ) { for( k.x = 0; k.x < size.x; k.x++ ) { if( grund_t* gr = welt->lookup( pos0+k ) ) { if( gebaeude_t* const add_gb = obj_cast(gr->first_no_way_obj()) ) { if( add_gb->get_fabrik()==this ) { tile_list.append( (pos0+k).get_2d() ); } } } } } } void fabrik_t::get_fields_list( vector_tpl &fields_list ) const { fields_list.clear(); if(desc->get_field_group()) { // if there are fields for(auto fd : fields) { const koord k = fd.location; grund_t *gr=welt->lookup_kartenboden(k); fields_list.append( gr ); } } } // Returns a list of goods produced by this factory. The caller must delete // the list when done slist_tpl *fabrik_t::get_produced_goods() const { slist_tpl *goods = new slist_tpl(); for(ware_production_t const& i : output) { goods->append(i.get_typ()); } return goods; } void fabrik_t::rebuild_inactive_cache() { inactive_inputs = inactive_outputs = inactive_demands = 0; for( uint32 i = 0; i < input.get_count(); i++ ){ if( input[i].menge <= 0 ) { inactive_inputs++; } if( input[i].demand_buffer >= input[i].max ) { inactive_demands++; } } for( uint32 i = 0 ; i < output.get_count() ; i++ ){ if( output[i].menge >= output[i].max ) { inactive_outputs++; } } } double fabrik_t::get_production_per_second() const { return (double)prodbase * (double)get_prodfactor() / (double)DEFAULT_PRODUCTION_FACTOR / (double)DEFAULT_PRODUCTION_FACTOR; } sint32 fabrik_t::calculate_work_rate_ramp(sint32 const amount, sint32 const minimum, sint32 const maximum, uint32 const precision) { sint32 production_rate; if( minimum >= maximum || minimum >= amount ) { // full production production_rate = 1 << precision; } else if( amount < maximum ) { // reduction production production_rate = (sint32)(((sint64)(maximum - amount) << precision) / (sint64)(maximum - minimum)); // apply work factor minimum limit if( production_rate < WORK_SCALE_MINIMUM_FRACTION ){ production_rate = WORK_SCALE_MINIMUM_FRACTION; } } else { // no production production_rate = 0; } return production_rate; } /** * Draws some nice colored bars giving some status information */ void fabrik_t::display_status(sint16 xpos, sint16 ypos) { const sint16 count = input.get_count()+output.get_count(); ypos += -D_WAITINGBAR_WIDTH - LINESPACE/6; xpos -= (count * D_WAITINGBAR_WIDTH - gfx->get_tile_raster_width()) / 2; if( input.get_count() ) { if( currently_producing ) { gfx->draw_box3d_clipped(xpos-2, ypos-1, D_WAITINGBAR_WIDTH*input.get_count()+4, 6, gfx->palette_lookup(10), gfx->palette_lookup(12)); } gfx->draw_rect_clipped(xpos-1, ypos, D_WAITINGBAR_WIDTH*input.get_count()+2, 4, gfx->palette_lookup(150), true CLIP_NUM_DEFAULT); for(uint32 i=0; i < input.get_count(); i++) { ware_production_t const& goods = input[i]; if (!desc->get_supplier(i)) { continue; } const sint64 pfactor = (sint64)desc->get_supplier(i)->get_consumption(); const uint32 storage_capacity = (uint32)((FAB_DISPLAY_UNIT_HALF + (sint64)goods.max * pfactor) >> (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)); if (storage_capacity) { const uint32 stock_quantity = (uint32)((FAB_DISPLAY_UNIT_HALF + (sint64)goods.menge * pfactor) >> (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)); const PIXVAL goods_color = goods.get_typ()->get_color(); const uint16 v = min(25, (uint16)(25 * stock_quantity / storage_capacity)) + 2; if (currently_producing) { gfx->draw_rect_clipped(xpos, ypos - v - 1, 1, v, gfx->palette_lookup(COL_GREY4), true CLIP_NUM_DEFAULT); gfx->draw_rect_clipped(xpos + 1, ypos - v - 1, D_WAITINGBAR_WIDTH - 2, v, goods_color, true CLIP_NUM_DEFAULT); gfx->draw_rect_clipped(xpos + D_WAITINGBAR_WIDTH - 1, ypos - v - 1, 1, v, gfx->palette_lookup(COL_GREY1), true CLIP_NUM_DEFAULT); } else { gfx->tint_rect(xpos + 1, ypos - v - 1, D_WAITINGBAR_WIDTH - 2, v, goods_color, 60); gfx->mark_rect_dirty_wc(xpos + 1, ypos - v - 1, xpos + D_WAITINGBAR_WIDTH - 1, ypos - 1); } } xpos += D_WAITINGBAR_WIDTH; } xpos += 4; } if( output.get_count() ) { if( currently_producing ) { gfx->draw_box3d_clipped(xpos-2, ypos-1, D_WAITINGBAR_WIDTH*output.get_count()+4, 6, gfx->palette_lookup(10), gfx->palette_lookup(12)); } gfx->draw_rect_clipped(xpos-1, ypos, D_WAITINGBAR_WIDTH*output.get_count()+2, 4, gfx->palette_lookup(COL_ORANGE), true CLIP_NUM_DEFAULT); for(uint32 i=0; i < output.get_count(); i++) { ware_production_t const& goods = output[i]; const sint64 pfactor = (sint64)desc->get_product(i)->get_factor(); const uint32 storage_capacity = (uint32)((FAB_DISPLAY_UNIT_HALF + (sint64)goods.max * pfactor) >> (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)); if (storage_capacity) { const uint32 stock_quantity = (uint32)((FAB_DISPLAY_UNIT_HALF + (sint64)goods.menge * pfactor) >> (precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)); const PIXVAL goods_color = goods.get_typ()->get_color(); const uint16 v = min(25, (uint16)(25 * stock_quantity / storage_capacity)) + 2; // the blended bars are too faint for me if (currently_producing) { gfx->draw_rect_clipped(xpos, ypos - v - 1, 1, v, gfx->palette_lookup(COL_GREY4), true CLIP_NUM_DEFAULT); gfx->draw_rect_clipped(xpos + 1, ypos - v - 1, D_WAITINGBAR_WIDTH - 2, v, goods_color, true CLIP_NUM_DEFAULT); gfx->draw_rect_clipped(xpos + D_WAITINGBAR_WIDTH - 1, ypos - v - 1, 1, v, gfx->palette_lookup(COL_GREY1), true CLIP_NUM_DEFAULT); } else { gfx->tint_rect(xpos + 1, ypos - v - 1, D_WAITINGBAR_WIDTH - 2, v, goods_color, 60); gfx->mark_rect_dirty_wc(xpos + 1, ypos - v - 1, xpos + D_WAITINGBAR_WIDTH - 1, ypos - 1); } } xpos += D_WAITINGBAR_WIDTH; } } } simutrans-124.5/src/simutrans/simfab.h000066400000000000000000000534651520675460500200270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMFAB_H #define SIMFAB_H #include "dataobj/koord3d.h" #include "dataobj/translator.h" #include "tpl/slist_tpl.h" #include "tpl/vector_tpl.h" #include "tpl/array_tpl.h" #include "descriptor/factory_desc.h" #include "halthandle.h" #include "world/simworld.h" #include "utils/plainstring.h" class player_t; class stadt_t; class ware_t; class leitung_t; /** * Factory statistics */ #define MAX_MONTH (12) #define FAB_PRODUCTION (0) #define FAB_POWER (1) #define FAB_BOOST_ELECTRIC (2) #define FAB_BOOST_PAX (3) #define FAB_BOOST_MAIL (4) #define FAB_PAX_GENERATED (5) #define FAB_PAX_DEPARTED (6) #define FAB_PAX_ARRIVED (7) #define FAB_MAIL_GENERATED (8) #define FAB_MAIL_DEPARTED (9) #define FAB_MAIL_ARRIVED (10) #define MAX_FAB_STAT (11) // reference lines #define FAB_REF_MAX_BOOST_ELECTRIC (0) #define FAB_REF_MAX_BOOST_PAX (1) #define FAB_REF_MAX_BOOST_MAIL (2) #define FAB_REF_DEMAND_ELECTRIC (3) #define FAB_REF_DEMAND_PAX (4) #define FAB_REF_DEMAND_MAIL (5) #define MAX_FAB_REF_LINE (6) // statistics for goods #define MAX_FAB_GOODS_STAT (4) // common to both input and output goods #define FAB_GOODS_STORAGE (0) // input goods #define FAB_GOODS_RECEIVED (1) #define FAB_GOODS_CONSUMED (2) #define FAB_GOODS_TRANSIT (3) // output goods #define FAB_GOODS_DELIVERED (1) #define FAB_GOODS_PRODUCED (2) // production happens in every second #define PRODUCTION_DELTA_T_BITS (10) #define PRODUCTION_DELTA_T (1 << PRODUCTION_DELTA_T_BITS) // default production factor #define DEFAULT_PRODUCTION_FACTOR_BITS (8) #define DEFAULT_PRODUCTION_FACTOR (1 << DEFAULT_PRODUCTION_FACTOR_BITS) // precision of apportioned demand (i.e. weights of factories at target cities) #define DEMAND_BITS (4) /** * JIT2 output scale constants. */ // The fixed point precision for work done fraction. static const uint32 WORK_BITS = 16; // The minimum allowed production rate for a factory. This is to limit the time outputs take to fill completly (so factories idle sooner). // Fixed point form range must be between 0.0 and 1.0. static const sint32 WORK_SCALE_MINIMUM_FRACTION = (5 << WORK_BITS) / 100; // ~5%, 1/20 of the full production rate. // The number of times minimum_shipment must be in current storage before rampdown starts. // Must be at least 2 to allow for full production as shipment is not instant. static const sint32 OUTPUT_SCALE_RAMPDOWN_MULTIPLYER = 2; // Two shipments must be ready. // The maximum rate at which power boost change change per second. // This limit is required to help stiffen overloaded networks to combat oscilations caused by feedback. static const sint32 BOOST_POWER_CHANGE_RATE = (5 << DEFAULT_PRODUCTION_FACTOR_BITS) / 100; // ~5% /** * Shipment size constants. */ // The maximum shipment size in whole units. // Must be greater than 0. static const uint32 SHIPMENT_MAX_SIZE = 10; // Traditional value. // The minimum number of whole shipments a facotry can store. // Must be greater than 0 to prevent division by 0. static const uint32 SHIPMENT_NUM_MIN = 4; // Quarters should allow reasonably fair distribution. /** * Convert internal values to displayed values */ sint64 convert_goods(sint64 value); sint64 convert_power(sint64 value); sint64 convert_boost(sint64 value); class ware_production_t { private: const goods_desc_t *type; // statistics for each goods sint64 statistics[MAX_MONTH][MAX_FAB_GOODS_STAT]; sint64 weighted_sum_storage; /// clears statistics, transit, and weighted_sum_storage void init_stats(); public: ware_production_t() : type(NULL), menge(0), max(0)/*, transit(statistics[0][FAB_GOODS_TRANSIT])*/, max_transit(0), placing_orders(false), index_offset(0) { #ifdef TRANSIT_DISTANCE count_suppliers = 0; #endif init_stats(); } const goods_desc_t* get_typ() const { return type; } void set_typ(const goods_desc_t *t) { type=t; } // functions for manipulating goods statistics void roll_stats(uint32 factor, sint64 aggregate_weight); void rdwr(loadsave_t *file); const sint64* get_stats() const { return *statistics; } void book_stat(sint64 value, int stat_type) { assert(stat_type consumer; uint32 consumer_active_last_month; /** * suppliers to this factory */ vector_tpl suppliers; /** * fields of this factory (only for farms etc.) */ struct field_data_t { koord location; uint16 field_class_index; field_data_t() : field_class_index(0) {} explicit field_data_t(const koord loc) : location(loc), field_class_index(0) {} field_data_t(const koord loc, const uint16 class_index) : location(loc), field_class_index(class_index) {} bool operator==(const field_data_t &other) const { return location==other.location; } }; vector_tpl fields; /** * Distribute products to connected stops */ void verteile_waren(const uint32 product); // List of target cities vector_tpl target_cities; player_t *owner; static karte_ptr_t welt; const factory_desc_t *desc; /** * Is construction site rotated? */ uint8 rotate; /** * production base amount */ sint32 prodbase; /** * multipliers for the production base amount */ sint32 prodfactor_electric; sint32 prodfactor_pax; sint32 prodfactor_mail; array_tpl input; ///< array for input/consumed goods array_tpl output; ///< array for output/produced goods /// Accumulated time since last production uint32 delta_t_sum; uint32 delta_amount; // production remainder when scaled to PRODUCTION_DELTA_T. added back next step to eliminate cumulative error uint32 delta_amount_remainder; // number of rounds where there is active production or consumption uint8 activity_count; // The adjacent connected transformer, if any. vector_tpltransformers; // true, if the factory did produce enough in the last step to require power bool currently_requiring_power; // there is input or output and we do something with it ... bool currently_producing; uint32 last_sound_ms; uint32 total_input, total_transit, total_output; uint8 status; /** * Inactive caches, used to speed up logic when dealing with inputs and outputs. */ uint8 inactive_outputs; uint8 inactive_inputs; uint8 inactive_demands; /// Position of a building of the factory. koord3d pos; /// Position of the nw-corner tile of the factory. koord3d pos_origin; /** * Number of times the factory has expanded so far * Only for factories without fields */ uint16 times_expanded; /** * Electricity amount scaled with prodbase */ uint32 scaled_electric_demand; /** * Pax/mail demand scaled with prodbase and month length */ uint32 scaled_pax_demand; uint32 scaled_mail_demand; /** * Update scaled electricity amount */ void update_scaled_electric_demand(); /** * Update scaled pax/mail demand */ void update_scaled_pax_demand(); void update_scaled_mail_demand(); /** * Update production multipliers for pax and mail */ void update_prodfactor_pax(); void update_prodfactor_mail(); /** * Re-calculate the pax/mail demands of factory at target cities */ void recalc_demands_at_target_cities(); /** * Recalculate storage capacities based on prodbase or capacities contributed by fields */ void recalc_storage_capacities(); /** * Class for collecting arrival data and calculating pax/mail boost with fixed period length */ #define PERIOD_BITS (18) // determines period length on which boost calculation is based #define SLOT_BITS (6) // determines the number of time slots available #define SLOT_COUNT (1<& get_consumer() const { return consumer; } bool is_active_consumer( koord k ) const; const vector_tpl& get_suppliers() const { return suppliers; } /** * Functions for manipulating the list of connected cities */ void add_target_city(stadt_t *const city); void remove_target_city(stadt_t *const city); void clear_target_cities(); const vector_tpl& get_target_cities() const { return target_cities; } void add_consumer(koord ziel); void remove_consumer(koord pos); /** * adds a supplier */ void add_supplier(koord pos); void remove_supplier(koord pos); /** * @return counts amount of ware of typ * -1 not produced/used here * 0>= actual amount */ sint32 get_input_stock(const goods_desc_t *ware); sint32 get_output_stock(const goods_desc_t *ware); // true, if there was production requiring power in the last step bool is_currently_producing() const { return currently_requiring_power; } /** * True if a transformer is connected to this factory. */ bool is_transformer_connected() const { return !transformers.empty(); } vector_tpl const &get_transformers() { return transformers; } /** * Connect transformer to this factory. */ void add_transformer_connected(leitung_t *transformer) { transformers.append_unique(transformer); } void remove_transformer_connected( leitung_t* transformer ) { transformers.remove( transformer ); } /** * @return 1 wenn consumption, * 0 wenn Produktionsstopp, * -1 wenn Ware nicht verarbeitet wird */ sint8 is_needed(const goods_desc_t *) const; sint32 liefere_an(const goods_desc_t *, sint32 menge); /** * Calculate the JIT2 logic power boost amount using the currently attached transformer. */ sint32 get_jit2_power_boost() const; void step(uint32 delta_t); // factory muss auch arbeiten void new_month(); char const* get_name() const; void set_name( const char *name ); PIXVAL get_color() const { return desc->get_color(); } player_t *get_owner() const { grund_t const* const p = welt->lookup(pos); return p ? p->first_no_way_obj()->get_owner() : 0; } void open_info_window(); // infostring on production void info_prod(cbuffer_t& buf) const; // infostring on targets/sources void info_conn(cbuffer_t& buf) const; void rdwr(loadsave_t *file); /* * Fills the vector with the koords of the tiles. */ void get_tile_list( vector_tpl &tile_list ) const; /* * Fills the vector with the tiles of the fields. */ void get_fields_list( vector_tpl &fields_list ) const; /// @returns a vector of factories within a rectangle static vector_tpl & sind_da_welche(koord min, koord max); // hier die methoden zum parametrisieren der Fabrik /// Builds buildings (gebaeude_t) for the factory. void build(sint32 rotate, bool build_fields, bool force_initial_prodbase); uint8 get_rotate() const { return rotate; } void set_rotate( uint8 r ) { rotate = r; } /* field generation code * spawns a field for sure if probability>=1000 */ bool add_random_field(uint16 probability); void remove_field_at(koord pos); uint32 get_field_count() const { return fields.get_count(); } uint32 get_min_field_count() const { const field_group_desc_t *fg = get_desc()->get_field_group(); return fg ? fg->get_min_fields() : 0; } /** * total and current procduction/storage values */ const array_tpl& get_input() const { return input; } const array_tpl& get_output() const { return output; } /** * Production multipliers */ sint32 get_prodfactor_electric() const { return prodfactor_electric; } sint32 get_prodfactor_pax() const { return prodfactor_pax; } sint32 get_prodfactor_mail() const { return prodfactor_mail; } sint32 get_prodfactor() const { return DEFAULT_PRODUCTION_FACTOR + prodfactor_electric + prodfactor_pax + prodfactor_mail; } /* does not takes month length into account */ sint32 get_base_production() const { return prodbase; } void set_base_production(sint32 p); sint32 get_current_production() const { return (sint32)welt->scale_with_month_length( ((sint64)prodbase * (sint64)get_prodfactor())>>8 ); } /* returns the status of the current factory, as well as output */ enum { STATUS_BAD, STATUS_MEDIUM, STATUS_GOOD, STATUS_INACTIVE, STATUS_NOTHING }; static uint8 status_to_color[5]; uint8 get_status() const { return status; } uint32 get_total_in() const { return total_input; } uint32 get_total_transit() const { return total_transit; } uint32 get_total_out() const { return total_output; } /** * Draws some nice colored bars giving some status information */ void display_status(sint16 xpos, sint16 ypos); /** * Crossconnects all factories */ void add_all_suppliers(); /* adds a new supplier to this factory * fails if no matching goods are there */ bool add_supplier(fabrik_t* fab); /** * Return the scaled electricity amount and pax/mail demand */ uint32 get_scaled_electric_demand() const { return scaled_electric_demand; } uint32 get_scaled_pax_demand() const { return scaled_pax_demand; } uint32 get_scaled_mail_demand() const { return scaled_mail_demand; } bool is_end_consumer() const { return (output.empty() && !desc->is_electricity_producer()); } // Returns a list of goods produced by this factory. slist_tpl *get_produced_goods() const; // Rebuild the factory inactive caches. void rebuild_inactive_cache(); double get_production_per_second() const; /* Calculate work rate using a ramp function. * amount: The current amount. * minimum: Minimum amount before work rate starts ramp down. * maximum: Maximum before production stops. * (opt) precision: Work rate fixed point fractional precision. * returns: Work rate in fixed point form. */ static sint32 calculate_work_rate_ramp(sint32 const amount, sint32 const minimum, sint32 const maximum, uint32 const precision = WORK_BITS); private: bool can_build_field_on_ground(const grund_t *gr, climate cl) const; }; #endif simutrans-124.5/src/simutrans/simhalt.cc000066400000000000000000003247461520675460500203700ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "freight_list_sorter.h" #include "world/simcity.h" #include "simcolor.h" #include "simconvoi.h" #include "simdebug.h" #include "simfab.h" #include "simhalt.h" #include "simintr.h" #include "simline.h" #include "simmem.h" #include "simmesg.h" #include "world/simplan.h" #include "player/simplay.h" #include "gui/simwin.h" #include "world/simworld.h" #include "simware.h" #include "builder/hausbauer.h" #include "builder/goods_manager.h" #include "descriptor/goods_desc.h" #include "descriptor/tunnel_desc.h" #include "ground/boden.h" #include "ground/grund.h" #include "ground/wasser.h" #include "obj/way/strasse.h" #include "dataobj/settings.h" #include "dataobj/schedule.h" #include "dataobj/loadsave.h" #include "dataobj/translator.h" #include "dataobj/environment.h" #include "obj/gebaeude.h" #include "obj/label.h" #include "obj/tunnel.h" #include "obj/wayobj.h" #include "gui/halt_info.h" #include "gui/minimap.h" #include "utils/simrandom.h" #include "utils/simstring.h" #include "tpl/binary_heap_tpl.h" #include "vehicle/vehicle.h" #include "vehicle/pedestrian.h" karte_ptr_t haltestelle_t::welt; vector_tpl haltestelle_t::alle_haltestellen; stringhashtable_tpl haltestelle_t::all_names; // hash table only used during loading inthashtable_tpl *haltestelle_t::all_koords = NULL; // since size_x*size_y < 0x1000000, we have just to shift the high bits #define get_halt_key(k,width) ( ((k).x*(width)+(k).y) /*+ ((k).z << 25)*/ ) uint8 haltestelle_t::status_step = 0; uint8 haltestelle_t::reconnect_counter = 0; static vector_tplstale_convois; static vector_tplstale_lines; void haltestelle_t::reset_routing() { reconnect_counter = welt->get_schedule_counter()-1; } void haltestelle_t::step_all() { // tell all stale convois to reroute their goods if( !stale_convois.empty() ) { convoihandle_t cnv = stale_convois.pop_back(); if( cnv.is_bound() ) { cnv->check_freight(); } } // same for stale lines if( !stale_lines.empty() ) { linehandle_t line = stale_lines.pop_back(); if( line.is_bound() ) { line->check_freight(); } } static uint32 next_halt_to_step=0; if (alle_haltestellen.empty()) { next_halt_to_step = 0; status_step = 0; return; } // reset served stops of all halts for (auto halt : alle_haltestellen) { for (int i = 0; i < goods_manager_t::get_max_catg_index(); i++) { halt->halt_served_this_step[i].clear(); } } // currently no reconnection/rerouting in progress? if (status_step == 0) { // than maybe start a reconnection (we start only when finished to avoid an eternal reconenction loop without ever rerouting) const uint8 schedule_counter = welt->get_schedule_counter(); if (reconnect_counter != schedule_counter) { // start with reconnection, re-routing will happen after complete reconnection status_step = RECONNECTING; reconnect_counter = schedule_counter; } else { // nothing to step if there is no rerouting/reconnection return; } } // we iterate in charges sint16 units_remaining = 1024; while (units_remaining > 0 && next_halt_to_step < alle_haltestellen.get_count()) { halthandle_t halt = alle_haltestellen[next_halt_to_step++]; halt->step(status_step, units_remaining); } // finished iteration, so we can proceed to next step if(next_halt_to_step >= alle_haltestellen.get_count()) { next_halt_to_step = 0; if( status_step == RECONNECTING ) { // reconnecting finished, compute connected components in one sweep rebuild_connected_components(); // reroute in next call status_step = REROUTING; } else if( status_step == REROUTING ) { // rerouting finished status_step = 0; } } } void haltestelle_t::start_load_game() { all_koords = new inthashtable_tpl; } void haltestelle_t::end_load_game() { delete all_koords; all_koords = NULL; for (halthandle_t h : alle_haltestellen) { h->recalc_status(); } } /** * return an index to a halt; it is only used for old games * by default create a new halt if none found */ halthandle_t haltestelle_t::get_halt_koord_index(koord k) { if(!welt->is_within_limits(k)) { return halthandle_t(); } // check in hashtable halthandle_t h; const sint32 n = get_halt_key(koord3d(k,-128), welt->get_size().y); assert(all_koords); h = all_koords->get( n ); if( !h.is_bound() ) { // No halts found => create one h = haltestelle_t::create(k, NULL ); all_koords->set( n, h ); } return h; } /* we allow only for a single stop per grund * this will only return something if this stop belongs to same player or is public or its permissions allow access, or is a dock (when on water) */ halthandle_t haltestelle_t::get_halt(const koord3d pos, const player_t *player, bool is_water ) { const grund_t *gr = welt->lookup(pos); if(gr) { if (gr->is_halt()) { halthandle_t h = gr->get_halt(); if (h.is_bound()) { if (h->can_use_halt(player)) { return h; } return halthandle_t(); } } // no halt? => we do the water check if(gr->is_water() && is_water) { // may catch bus stops close to water so we need to check type ... const planquadrat_t *plan = welt->access(pos.get_2d()); const uint8 cnt = plan->get_haltlist_count(); for( uint8 i=0; iget_haltlist()[i]; if( halt->can_use_halt(player) && halt->get_station_type()&dock ) { return halt; } } } } return halthandle_t(); } koord haltestelle_t::get_basis_pos() const { return get_basis_pos3d().get_2d(); } koord3d haltestelle_t::get_basis_pos3d() const { if (tiles.empty()) { return koord3d::invalid; } return tiles.front().grund->get_pos(); } // returns tile closest to this coordinate grund_t *haltestelle_t::get_ground_closest_to( const koord here ) const { uint32 distance = 0x7FFFFFFFu; grund_t *closest = NULL; for(tile_t const& i : tiles) { uint32 dist = koord_distance( i.grund->get_pos(), here ); if( dist < distance ) { distance = dist; closest = i.grund; if( distance == 0 ) { break; } } } return closest; } // and the same in 3D grund_t* haltestelle_t::get_ground_closest_to(const koord3d here, bool only_overground) const { uint32 distance = 0x7FFFFFFFu; grund_t* closest = NULL; for (tile_t const& i : tiles) { if (only_overground && i.grund->ist_im_tunnel()) { continue; } uint32 dist = koord_distance(i.grund->get_pos(), here); if (dist < distance || ( dist == distance && abs(i.grund->get_pos().z-here.z) < abs(closest->get_pos().z-here.z) ) ) { distance = dist; closest = i.grund; if (distance == 0) { break; } } } return closest; } /** * return the closest square that belongs to this halt */ koord haltestelle_t::get_next_pos( koord start ) const { if( grund_t *gr=get_ground_closest_to(start) ) { return gr->get_pos().get_2d(); } return koord::invalid; } /* Calculate and set basis position of this station * It is the avarage of all tiles' coordinate weighed by level of the building * Tries to put it on the map ground */ void haltestelle_t::recalc_basis_pos() { sint64 cent_x = 0, cent_y = 0, cent_z = 0; uint64 level_sum; level_sum = 0; bool has_above = false; for (tile_t const& i : tiles) { has_above = !i.grund->ist_im_tunnel(); if (has_above) { break; } } for (tile_t const& i : tiles) { if (has_above && i.grund->ist_im_tunnel()) { // use overground tiles only for center calculation unless its all tunnel continue; } if (gebaeude_t* const gb = i.grund->find()) { sint32 lv; lv = gb->get_tile()->get_desc()->get_level() + 1; cent_x += gb->get_pos().x * lv; cent_y += gb->get_pos().y * lv; cent_z += gb->get_pos().z * lv; level_sum += lv; } } koord3d cent; cent = koord3d((sint16)(cent_x / level_sum), (sint16)(cent_y / level_sum), (sint8)(cent_z / level_sum)); if (!welt->lookup(cent)) { // no ground for label? Put on surface cent = welt->lookup_kartenboden(cent.get_2d())->get_pos(); } // save old name plainstring name = get_name(); // clear name at old place (and the book-keeping) set_name(NULL); if (level_sum > 0) { grund_t* new_center = get_ground_closest_to(cent, has_above); if (new_center != tiles.front().grund && new_center->get_text() == NULL) { // move to new center, if there is not yet a name on it tiles.remove(new_center); tiles.insert(new_center); init_pos = new_center->get_pos().get_2d(); } } // .. and set name again (and do all the book-keeping) set_name(name); return; } /** * Station factory method. Returns handles instead of pointers. */ halthandle_t haltestelle_t::create(koord pos, player_t *player) { haltestelle_t * p = new haltestelle_t(pos, player); return p->self; } /** * removes a ground tile from a station */ bool haltestelle_t::remove(player_t *player, koord3d pos) { grund_t *bd = welt->lookup(pos); // wrong ground? if(bd==NULL) { dbg->error("haltestelle_t::remove()","illegal ground at %d,%d,%d", pos.x, pos.y, pos.z); return false; } halthandle_t halt = bd->get_halt(); if(!halt.is_bound()) { dbg->error("haltestelle_t::remove()","no halt at %d,%d,%d", pos.x, pos.y, pos.z); return false; } DBG_MESSAGE("haltestelle_t::remove()","removing segment from %d,%d,%d", pos.x, pos.y, pos.z); // otherwise there will be marked tiles left ... halt->mark_unmark_coverage(false); // only try to remove connected buildings, when still in list to avoid infinite loops if( halt->rem_grund(bd) ) { // remove station building? gebaeude_t* gb = bd->find(); if(gb) { DBG_MESSAGE("haltestelle_t::remove()", "removing building" ); hausbauer_t::remove( player, gb ); bd = NULL; // no need to recalc image // removing the building could have destroyed this halt already if (!halt.is_bound()){ return true; } } } if(!halt->existiert_in_welt()) { DBG_DEBUG("haltestelle_t::remove()","remove last"); // all deleted? DBG_DEBUG("haltestelle_t::remove()","destroy"); haltestelle_t::destroy( halt ); } else { halt->recalc_basis_pos(); } // if building was removed this is false! if(bd) { bd->calc_image(); minimap_t::get_instance()->calc_map_pixel(pos.get_2d()); } return true; } /** * Station factory method. Returns handles instead of pointers. */ halthandle_t haltestelle_t::create(loadsave_t *file) { haltestelle_t *p = new haltestelle_t(file); return p->self; } /** * Station destruction method. */ void haltestelle_t::destroy(halthandle_t const halt) { delete halt.get_rep(); } /** * Station destruction method. * Da destroy() alle_haltestellen modifiziert kann kein Iterator benutzt * werden! */ void haltestelle_t::destroy_all() { while (!alle_haltestellen.empty()) { halthandle_t halt = alle_haltestellen.back(); destroy(halt); } delete all_koords; all_koords = NULL; status_step = 0; } haltestelle_t::haltestelle_t(loadsave_t* file) { last_loading_step = welt->get_steps(); cargo = (vector_tpl **)calloc( goods_manager_t::get_max_catg_index(), sizeof(vector_tpl *) ); all_links = new link_t[ goods_manager_t::get_max_catg_index() ]; halt_served_this_step = new vector_tpl[goods_manager_t::get_max_catg_index()]; status_color = SYSCOL_TEXT_UNUSED; last_status_color = gfx->palette_lookup(COL_PURPLE); last_bar_count = 0; last_permissions = 0; last_player_count = 0; reconnect_counter = welt->get_schedule_counter()-1; enables = NOT_ENABLED; old_sort_mode = 255; rdwr(file); markers[ self.get_id() ] = current_marker; alle_haltestellen.append(self); } haltestelle_t::haltestelle_t(koord k, player_t* player) { self = halthandle_t(this); assert( !alle_haltestellen.is_contained(self) ); alle_haltestellen.append(self); markers[ self.get_id() ] = current_marker; last_loading_step = welt->get_steps(); this->init_pos = k; owner = player; enables = NOT_ENABLED; // force total re-routing reconnect_counter = welt->get_schedule_counter()-1; last_catg_index = 255; cargo = (vector_tpl **)calloc( goods_manager_t::get_max_catg_index(), sizeof(vector_tpl *) ); all_links = new link_t[ goods_manager_t::get_max_catg_index() ]; halt_served_this_step = new vector_tpl[goods_manager_t::get_max_catg_index()]; status_color = SYSCOL_TEXT_UNUSED; last_status_color = gfx->palette_lookup(COL_PURPLE); last_bar_count = 0; init_financial_history(); set_permissions(0); } haltestelle_t::~haltestelle_t() { assert(self.is_bound()); // first: remove halt from all lists int i=0; while(alle_haltestellen.is_contained(self)) { alle_haltestellen.remove(self); i++; } if (i != 1) { dbg->error("haltestelle_t::~haltestelle_t()", "handle %i found %i times in haltlist!", self.get_id(), i ); } // free name set_name(NULL); // remove from ground and planquadrat (tile) haltlists koord ul(32767,32767); koord lr(0,0); while( !tiles.empty() ) { grund_t *gr = tiles.remove_first().grund; koord pos = gr->get_pos().get_2d(); gr->set_halt( halthandle_t() ); // bounding box for adjustments ul.clip_max(pos); lr.clip_min(pos); } // remove from all haltlists uint16 const cov = welt->get_settings().get_station_coverage(); ul.x = max(0, ul.x - cov); ul.y = max(0, ul.y - cov); lr.x = min(welt->get_size().x, lr.x + 1 + cov); lr.y = min(welt->get_size().y, lr.y + 1 + cov); for( int y=ul.y; yaccess(x,y); if(plan->get_haltlist_count()>0) { plan->remove_from_haltlist(self); } } } destroy_win( magic_halt_info + self.get_id() ); // finally detach handle // before it is needed for clearing up the planqudrat and tiles self.detach(); for(unsigned i=0; i& warray = *cargo[i]; for (size_t j = warray.get_count(); j-- != 0;) { ware_t& ware = warray[j]; if(ware.amount>0) { ware.rotate90(y_size); } else { // empty => remove warray.remove_at(j); } } } } // re-linking factories reconnect_factories(); } // add other players to share void haltestelle_t::set_permissions(uint16 perms) { uint16 old_perm = permissions; if (!owner || owner->is_public_service()) { // old style public stops permissions = 0xFFFF; } else { // share player stops permissions = perms | (1 << owner->get_player_nr()); } if (rebuilt_schedule_registration(old_perm & ~permissions, permissions & ~old_perm)) { // convois/lines changed welt->set_schedule_counter(); } } const char* haltestelle_t::get_name() const { const char *name = "Unknown"; if (tiles.empty()) { name = "Unnamed"; } else { grund_t* bd = welt->lookup(get_basis_pos3d()); if(bd && bd->get_flag(grund_t::has_text)) { name = bd->get_text(); } } return name; } /** * Sets the name. Creates a copy of name. */ void haltestelle_t::set_name(const char *new_name) { grund_t *gr = welt->lookup(get_basis_pos3d()); if(gr) { if (gr->get_flag(grund_t::has_text)) { const char* old_name = gr->get_text(); halthandle_t h = all_names.remove(old_name); if(h!=self) { DBG_MESSAGE("haltestelle_t::set_name()","removing name %s already used!",gr->get_text()); } } if (label_t *lb=gr->find()) { delete lb; // very old games had labels for names } gr->set_text(new_name); if(new_name && all_names.set(gr->get_text(),self).is_bound() ) { DBG_MESSAGE("haltestelle_t::set_name()","name %s already used!",new_name); } halt_info_t *const info_frame = dynamic_cast( win_get_magic( magic_halt_info + self.get_id() ) ); if( info_frame ) { info_frame->set_name( get_name() ); } } } // returns the number of % in a printf string .... static int count_printf_param( const char *str ) { int count = 0; while( *str!=0 ) { if( *str=='%' ) { str++; if( *str!='%' ) { count++; } } str ++; } return count; } // creates stops with unique! names char* haltestelle_t::create_name(koord const k, char const* const typ) { int const lang = welt->get_settings().get_name_language_id(); stadt_t *stadt = welt->find_nearest_city(k); const char *stop = translator::translate(typ,lang); cbuffer_t buf; // this fails only, if there are no towns at all! if(stadt==NULL) { for( uint32 i=1; i<65536; i++ ) { // get a default name buf.printf( translator::translate("land stop %i %s",lang), i, stop ); if( !all_names.get(buf).is_bound() ) { return strdup(buf); } buf.clear(); } return strdup("Unnamed"); } // now we have a city const char *city_name = stadt->get_name(); sint16 li_gr = stadt->get_linksoben().x - 2; sint16 re_gr = stadt->get_rechtsunten().x + 2; sint16 ob_gr = stadt->get_linksoben().y - 2; sint16 un_gr = stadt->get_rechtsunten().y + 2; // strings for intown / outside of town const bool inside = (li_gr < k.x && re_gr > k.x && ob_gr < k.y && un_gr > k.y); const bool suburb = !inside && (li_gr - 6 < k.x && re_gr + 6 > k.x && ob_gr - 6 < k.y && un_gr + 6 > k.y); if (!welt->get_settings().get_numbered_stations()) { static const koord next_building[24] = { koord( 0, -1), // north koord( 1, 0), // east koord( 0, 1), // south koord(-1, 0), // west koord( 1, -1), // northeast koord( 1, 1), // southeast koord(-1, 1), // southwest koord(-1, -1), // northwest koord( 0, -2), // double nswo koord( 2, 0), koord( 0, 2), koord(-2, 0), koord( 1, -2), // all the remaining 3s koord( 2, -1), koord( 2, 1), koord( 1, 2), koord(-1, 2), koord(-2, 1), koord(-2, -1), koord(-1, -2), koord( 2, -2), // and now all buildings with distance 4 koord( 2, 2), koord(-2, 2), koord(-2, -2) }; // standard names: // order: factory, attraction, direction, normal name // first we try a factory name // is there a translation for factory defined? const char *fab_base_text = "%s factory %s %s"; const char *fab_base = translator::translate(fab_base_text,lang); if( fab_base_text != fab_base ) { slist_tplfabs; if (self.is_bound()) { // first factories (so with same distance, they have priority) int this_distance = 999; for(fabrik_t* const f : get_fab_list()) { int distance = koord_distance(f->get_pos().get_2d(), k); if( distance < this_distance ) { fabs.insert(f); this_distance = distance; } else { fabs.append(f); } } } else { // since the distance are presorted, we can just append for a good choice ... for( int test=0; test<24; test++ ) { fabrik_t *fab = fabrik_t::get_fab(k+next_building[test]); if(fab && fabs.is_contained(fab)) { fabs.append(fab); } } } // are there fabs? for(fabrik_t* const f : fabs) { // with factories buf.printf(fab_base, city_name, f->get_name(), stop); if( !all_names.get(buf).is_bound() ) { return strdup(buf); } buf.clear(); } } // no fabs or all names used up already // is there a translation for buildings defined? const char *building_base_text = "%s building %s %s"; const char *building_base = translator::translate(building_base_text,lang); if( building_base_text != building_base ) { // check for other special building (townhall, monument, tourist attraction) for (int i=0; i<24; i++) { grund_t *gr = welt->lookup_kartenboden( next_building[i] + k); if(gr==NULL || gr->get_typ()!=grund_t::fundament) { // no building here continue; } // since closes coordinates are tested first, we do not need to not sort this const char *building_name = NULL; const gebaeude_t* gb = gr->find(); if(gb==NULL) { // field may have foundations but no building continue; } // now we have a building here if (gb->is_monument()) { building_name = translator::translate(gb->get_name(),lang); } else if (gb->is_townhall() || gb->get_tile()->get_desc()->get_type() == building_desc_t::attraction_land || // land attraction gb->get_tile()->get_desc()->get_type() == building_desc_t::attraction_city) { // town attraction building_name = make_single_line_string(translator::translate(gb->get_tile()->get_desc()->get_name(),lang), 2); } else { // normal town house => not suitable for naming continue; } // now we have a name: try it buf.printf( building_base, city_name, building_name, stop ); if( !all_names.get(buf).is_bound() ) { return strdup(buf); } buf.clear(); } } // if there are street names, use them if( inside || suburb ) { const vector_tpl& street_names( translator::get_street_name_list() ); // make sure we do only ONE random call regardless of how many names are available (to avoid desyncs in network games) if( const uint32 count = street_names.get_count() ) { uint32 idx = simrand( count ); static const uint32 some_primes[] = { 19, 31, 109, 199, 409, 571, 631, 829, 1489, 1999, 2341, 2971, 3529, 4621, 4789, 7039, 7669, 8779, 9721 }; // find prime that does not divide count uint32 offset = 1; for(uint8 i=0; i then try the normal naming scheme ... char numbername[10]; if(inside) { strcpy( numbername, "0center" ); } else if(suburb) { // close to the city we use a different scheme, with suburbs strcpy( numbername, "0suburb" ); } else { strcpy( numbername, "0extern" ); } const char *dirname = NULL; static const char *diagonal_name[4] = { "nordwest", "nordost", "suedost", "suedwest" }; static const char *direction_name[4] = { "nord", "ost", "sued", "west" }; uint8 const rot = welt->get_settings().get_rotation(); if (k.y < ob_gr || (inside && k.y*3 < (un_gr+ob_gr+ob_gr)) ) { if (k.x < li_gr) { dirname = diagonal_name[(4 - rot) % 4]; } else if (k.x > re_gr) { dirname = diagonal_name[(5 - rot) % 4]; } else { dirname = direction_name[(4 - rot) % 4]; } } else if (k.y > un_gr || (inside && k.y*3 > (un_gr+un_gr+ob_gr)) ) { if (k.x < li_gr) { dirname = diagonal_name[(3 - rot) % 4]; } else if (k.x > re_gr) { dirname = diagonal_name[(6 - rot) % 4]; } else { dirname = direction_name[(6 - rot) % 4]; } } else { if (k.x <= stadt->get_pos().x) { dirname = direction_name[(3 - rot) % 4]; } else { dirname = direction_name[(5 - rot) % 4]; } } dirname = translator::translate(dirname,lang); // Try everything to get a unique name while(true) { // well now try them all from "0..." over "9..." to "A..." to "Z..." for( int i=0; i<10+26; i++ ) { numbername[0] = i<10 ? '0'+i : 'A'+i-10; const char *base_name = translator::translate(numbername,lang); if(base_name==numbername) { // not translated ... try next continue; } // allow for names without direction uint8 count_s = count_printf_param( base_name ); if(count_s==3) { if (cbuffer_t::check_and_repair_format_strings("%s %s %s", base_name) ) { // ok, try this name, if free ... buf.printf( base_name, city_name, dirname, stop ); } } else { if (cbuffer_t::check_and_repair_format_strings("%s %s", base_name) ) { // ok, try this name, if free ... buf.printf( base_name, city_name, stop ); } } if( buf.len()>0 && !all_names.get(buf).is_bound() ) { return strdup(buf); } buf.clear(); } // here we did not find a suitable name ... // ok, no suitable city names, try the suburb ones ... if( strcmp(numbername+1,"center")==0 ) { strcpy( numbername, "0suburb" ); } // ok, no suitable suburb names, try the external ones (if not inside city) ... else if( strcmp(numbername+1,"suburb")==0 && !inside ) { strcpy( numbername, "0extern" ); } else { // no suitable unique name found at all ... break; } } } /* so far we did not found a matching station name * as a last resort, we will try numbered names * (or the user requested this anyway) */ // strings for intown / outside of town const char *base_name = translator::translate( inside ? "%s city %d %s" : "%s land %d %s", lang); // finally: is there a stop with this name already? for( uint32 i=1; i<65536; i++ ) { buf.printf( base_name, city_name, i, stop ); if( !all_names.get(buf).is_bound() ) { return strdup(buf); } buf.clear(); } // emergency measure: But before we should run out of handles anyway ... assert(0); return strdup("Unnamed"); } // add convoi to loading void haltestelle_t::request_loading( convoihandle_t cnv ) { loading_here.append_unique( cnv ); if( last_loading_step != welt->get_steps() ) { last_loading_step = welt->get_steps(); // now iterate over all convois for (uint32 i = 0; iget_state() == convoi_t::LOADING) { // now we load into convoi c->hat_gehalten(self); } if (c.is_bound() && c->get_state() == convoi_t::LOADING) { ++i; } else { loading_here.remove_at(i); } } } } bool haltestelle_t::has_available_network(const player_t* player, uint8 catg_index) const { if(!player_t::check_owner( player, owner )) { return false; } if( catg_index != goods_manager_t::INDEX_NONE && !is_enabled(catg_index) ) { return false; } // Check if there is a player's line for(linehandle_t const l : registered_lines) { if( l->get_owner() == player && l->count_convoys()>0 ) { if( catg_index == goods_manager_t::INDEX_NONE ) { return true; } else if( l->get_goods_catg_index().is_contained(catg_index)) { return true; } } } // Check lineless convoys for(convoihandle_t cnv : registered_convoys ) { if( cnv->get_owner() == player && cnv->get_state() != convoi_t::INITIAL ) { if( catg_index == goods_manager_t::INDEX_NONE ) { return true; } else if( cnv->get_goods_catg_index().is_contained(catg_index) ) { return true; } } }; return false; } // steps is currently only called during reconnectiong and reoruting // any change need to change step_all() bool haltestelle_t::step(uint8 what, sint16 &units_remaining) { switch(what) { case RECONNECTING: units_remaining -= (rebuild_connections()/256)+2; break; case REROUTING: reroute_goods(units_remaining); recalc_status(); break; default: dbg->fatal("haltestelle_t::step()","Unknown step mode %i",what); break; } return true; } /** * Called every month */ void haltestelle_t::new_month() { if( welt->get_active_player()==owner && status_color==gfx->palette_lookup(COL_RED) ) { cbuffer_t buf; buf.printf( translator::translate("%s\nis crowded."), get_name() ); welt->get_message()->add_message(buf, get_basis_pos3d(),message_t::full|message_t::EXPIRE_AFTER_ONE_MONTH_MSG, PLAYER_FLAG|owner->get_player_nr(), IMG_EMPTY ); enables &= (PAX|POST|WARE); } // roll financial history for (int j = 0; j0; k--) { financial_history[k][j] = financial_history[k-1][j]; } financial_history[0][j] = 0; } // number of waiting should be constant ... financial_history[0][HALT_WAITING] = financial_history[1][HALT_WAITING]; } /** * Called after schedule calculation of all stations is finished * will distribute the goods to changed routes (if there are any) */ void haltestelle_t::reroute_goods(sint16 &units_remaining) { if( last_catg_index==255 ) { last_catg_index = 0; } for( ; last_catg_index * warray = cargo[last_catg_index]; vector_tpl * new_warray = new vector_tpl(warray->get_count()); for (size_t j = warray->get_count(); j-- != 0;) { ware_t & ware = (*warray)[j]; if(ware.amount==0) { continue; } // since also the factory halt list is added to the ground, we can use just this ... if( welt->access(ware.get_target_pos())->is_connected(self) ) { // we are already there! if( ware.to_factory ) { liefere_an_fabrik(ware); } continue; } // add to new array new_warray->append( ware ); } // delete, if nothing connects here if( new_warray->empty() ) { if( all_links[last_catg_index].connections.empty() ) { // no connections from here => delete delete new_warray; new_warray = NULL; } } // replace the array delete cargo[last_catg_index]; cargo[last_catg_index] = new_warray; // if something left // re-route goods to adapt to changes in world layout, // remove all goods whose destination was removed from the map if (cargo[last_catg_index] && !cargo[last_catg_index]->empty()) { vector_tpl &warray = *cargo[last_catg_index]; uint32 last_goods_index = 0; units_remaining -= warray.get_count(); while( last_goods_indexget_desc()->get_placement() != factory_desc_t::Water || (station_type & dock) > 0 ) { // do no link to oil rigs via stations ... fab_list.insert(fab); return true; } } return false; } void haltestelle_t::reconnect_factories() { // unlink all (because the processing order may change with additional tiles) for(fabrik_t* const f : fab_list) { f->unlink_halt(self); } fab_list.clear(); // then reconnect for(tile_t const& i : tiles) { koord const p = i.grund->get_pos().get_2d(); uint16 const cov = welt->get_settings().get_station_coverage(); for(fabrik_t* const fab : fabrik_t::sind_da_welche(p - koord(cov, cov), p + koord(cov, cov))) { if (connect_factory(fab)) { // also connect the factory to this halt fab->link_halt(self); } } } } /* * removes factory to a halt */ void haltestelle_t::remove_fabriken(fabrik_t *fab) { fab_list.remove(fab); } /** * Rebuilds the list of connections to directly reachable halts * Returns the number of stops considered */ #define WEIGHT_WAIT (8) #define WEIGHT_HALT (1) // the minimum weight of a connection from a transfer halt #define WEIGHT_MIN (WEIGHT_WAIT+WEIGHT_HALT) sint32 haltestelle_t::rebuild_connections() { // halts which either immediately precede or succeed self halt in serving schedules static vector_tpl consecutive_halts[256]; // halts which either immediately precede or succeed self halt in currently processed schedule static vector_tpl consecutive_halts_schedule[256]; // remember max number of consecutive halts for one schedule uint8 max_consecutive_halts_schedule[256]; MEMZERON(max_consecutive_halts_schedule, goods_manager_t::get_max_catg_index()); // previous halt supporting the ware categories of the serving line static halthandle_t previous_halt[256]; // first, remove all old entries for( uint8 i=0; i *goods_catg_index; minivec_tpl supported_catg_index(32); /* * In the first loops: * lines==true => search for lines * After this: * lines==false => search for single convoys without lines */ bool lines = true; uint32 current_index = 0; while( lines || current_index < registered_convoys.get_count() ) { // Now, collect the "schedule", "owner" and "add_catg_index" from line resp. convoy. if( lines ) { if( current_index >= registered_lines.get_count() ) { // We have looped over all lines. lines = false; current_index = 0; // start over for registered lineless convoys continue; } const linehandle_t line = registered_lines[current_index]; ++current_index; owner = line->get_owner(); schedule = line->get_schedule(); goods_catg_index = &line->get_goods_catg_index(); } else { const convoihandle_t cnv = registered_convoys[current_index]; ++current_index; owner = cnv->get_owner(); schedule = cnv->get_schedule(); goods_catg_index = &cnv->get_goods_catg_index(); } // find the index from which to start processing uint8 start_index = 0; while( start_index < schedule->get_count() && get_halt( schedule->entries[start_index].pos, owner, true ) != self ) { ++start_index; } ++start_index; // the next index after self halt; it's okay to be out-of-range // determine goods category indices supported by this halt supported_catg_index.clear(); for(uint8 const catg_index : *goods_catg_index) { if( is_enabled(catg_index) ) { supported_catg_index.append(catg_index); previous_halt[catg_index] = self; consecutive_halts_schedule[catg_index].clear(); } } if( supported_catg_index.empty() ) { // this halt does not support the goods categories handled by the line/lineless convoy continue; } INT_CHECK("simhalt.cc 612"); // now we add the schedule to the connection array uint16 aggregate_weight = WEIGHT_WAIT; for( uint8 j=0; jget_count(); ++j ) { halthandle_t current_halt = get_halt(schedule->entries[(start_index+j)%schedule->get_count()].pos, owner, schedule->get_waytype()==water_wt ); if( !current_halt.is_bound() ) { // ignore way points continue; } if( current_halt == self ) { // check for consecutive halts which precede self halt for(uint8 const catg_index : supported_catg_index) { if( previous_halt[catg_index]!=self ) { consecutive_halts[catg_index].append_unique(previous_halt[catg_index]); consecutive_halts_schedule[catg_index].append_unique(previous_halt[catg_index]); previous_halt[catg_index] = self; } } // reset aggregate weight aggregate_weight = WEIGHT_WAIT; continue; } aggregate_weight += WEIGHT_HALT; for(uint8 const catg_index : supported_catg_index) { if( current_halt->is_enabled(catg_index) ) { // check for consecutive halts which succeed self halt if( previous_halt[catg_index] == self ) { consecutive_halts[catg_index].append_unique(current_halt); consecutive_halts_schedule[catg_index].append_unique(current_halt); } previous_halt[catg_index] = current_halt; // either add a new connection or update the weight of an existing connection where necessary connection_t *const existing_connection = all_links[catg_index].connections.insert_unique_ordered( connection_t( current_halt, aggregate_weight ), connection_t::compare ); if( existing_connection && aggregate_weightweight ) { existing_connection->weight = aggregate_weight; } } } } for(uint8 const catg_index : supported_catg_index) { if( consecutive_halts_schedule[catg_index].get_count() > max_consecutive_halts_schedule[catg_index] ) { max_consecutive_halts_schedule[catg_index] = consecutive_halts_schedule[catg_index].get_count(); } } connections_searched += schedule->get_count(); } for( uint8 i=0; i this is not transfer halt } else { all_links[i].is_transfer = true; } } } return connections_searched; } bool haltestelle_t::rebuilt_schedule_registration(bool remove,bool add) { bool change = false; if (remove) { // remove all old connections for (int i = registered_lines.get_count()-1; i >= 0; i--) { linehandle_t l = registered_lines[i]; // wrong owner if (!can_use_halt(l->get_owner())) { stale_lines.append_unique(l); registered_lines.remove_at(i); change = true; continue; } // no convoys anymore if (l->count_convoys() == 0) { registered_lines.remove_at(i); continue; } // now check for changed stops const bool water = l->get_schedule()->get_waytype() == water_wt; bool still_stops_here = false; for (schedule_entry_t const& k : l->get_schedule()->entries) { if (get_halt(k.pos, l->get_owner(), water) == self) { still_stops_here = true; break; } } if (!still_stops_here) { stale_lines.append_unique(l); registered_lines.remove_at(i); change = true; } } // remove old convois for (int i = registered_convoys.get_count()-1; i >= 0; i--) { // wrong owner if (!can_use_halt(registered_convoys[i]->get_owner())) { stale_convois.append_unique(registered_convoys[i]); registered_convoys.remove_at(i); change = true; continue; } // not stopping any more if (const schedule_t* const schedule = registered_convoys[i]->get_schedule()) { const bool water = schedule->get_waytype() == water_wt; bool still_stops_here = false; for (schedule_entry_t const& k : schedule->entries) { if (get_halt(k.pos, registered_convoys[i]->get_owner(), water) == self) { still_stops_here = true; break; } } if (!still_stops_here) { stale_convois.append_unique(registered_convoys[i]); registered_convoys.remove_at(i); change = true; } } } } if (add) { vector_tpl check_line; // iterate over all lines (public halt: all lines, other: only player's lines) for (uint8 i = 0; i < MAX_PLAYER_COUNT; i++) { if (permissions & (1 << i)) { if (player_t* player = welt->get_player(i)) { player->simlinemgmt.get_lines(simline_t::line, &check_line); for (linehandle_t const j : check_line) { // only add unknown lines if (!registered_lines.is_contained(j) && j->count_convoys() > 0) { bool water = j->get_schedule()->get_waytype() == water_wt; for (schedule_entry_t const& k : j->get_schedule()->entries) { if (get_halt(k.pos, player, water) == self) { registered_lines.append(j); change = true; break; } } } } } } } // iterate over all convoys for (convoihandle_t const cnv : welt->convoys()) { // only check lineless convoys which have matching ownership and which are not yet registered if (!cnv->get_line().is_bound() && can_use_halt(cnv->get_owner()) && !registered_convoys.is_contained(cnv)) { if (const schedule_t* const schedule = cnv->get_schedule()) { bool water = schedule->get_waytype() == water_wt; for (schedule_entry_t const& k : schedule->entries) { if (get_halt(k.pos, cnv->get_owner(), water) == self) { registered_convoys.append(cnv); change = true; break; } } } } } } return change; } void haltestelle_t::rebuild_linked_connections() { vector_tpl all; // all halts connected to this halt for( uint8 i=0; i& connections = all_links[i].connections; for(connection_t &c : connections) { all.append_unique( c.halt ); } } for(halthandle_t h : all) { h->rebuild_connections(); } } void haltestelle_t::fill_connected_component(uint8 catg_idx, uint16 comp) { if (all_links[catg_idx].catg_connected_component != UNDECIDED_CONNECTED_COMPONENT) { // already connected return; } all_links[catg_idx].catg_connected_component = comp; for(connection_t &c : all_links[catg_idx].connections) { if (c.halt.is_bound()) { c.halt->fill_connected_component(catg_idx, comp); // cache the is_transfer value c.is_transfer = c.halt->is_transfer(catg_idx); } } } void haltestelle_t::rebuild_connected_components() { for(uint8 catg_idx = 0; catg_idxall_links[catg_idx].catg_connected_component == UNDECIDED_CONNECTED_COMPONENT) { // start recursion halt->fill_connected_component(catg_idx, halt.get_id()); } } } } sint8 haltestelle_t::is_connected(halthandle_t halt, uint8 catg_index) const { if (!halt.is_bound()) { return 0; // not connected } if (halt == self) { return 1; } const link_t& linka = all_links[catg_index]; const link_t& linkb = halt->all_links[catg_index]; if (linka.connections.empty() || linkb.connections.empty()) { return 0; // empty connections -> not connected } if (linka.catg_connected_component == UNDECIDED_CONNECTED_COMPONENT || linkb.catg_connected_component == UNDECIDED_CONNECTED_COMPONENT) { return -1; // undecided - try later } // now check whether both halts are in the same component return linka.catg_connected_component == linkb.catg_connected_component ? 1 : 0; } /** * Helper class that combines a vector_tpl * with WEIGHT_HEAP elements and a binary_heap_tpl. * If inserted node has weight less than WEIGHT_HEAP, * then node is inserted in one of the vectors. * If weight is larger, then node goes into the heap. * * WEIGHT_HEAP = 200 should be safe for even the largest games. */ template class bucket_heap_tpl { #define WEIGHT_HEAP (200) vector_tpl *buckets; ///< array of vectors binary_heap_tpl heap; ///< the heap uint16 min_weight; ///< current min_weight of nodes in the buckets uint32 node_count; ///< total count of nodes public: bucket_heap_tpl() : heap(128) { min_weight = WEIGHT_HEAP; node_count = 0; buckets = new vector_tpl [WEIGHT_HEAP]; } ~bucket_heap_tpl() { delete [] buckets; } void insert(const T item) { node_count++; uint16 weight = *item; if (weight < WEIGHT_HEAP) { if (weight < min_weight) { min_weight = weight; } buckets[weight].append(item); } else { heap.insert(item); } } T pop() { assert(!empty()); node_count--; if (min_weight < WEIGHT_HEAP) { T ret = buckets[min_weight].pop_back(); while(min_weight < WEIGHT_HEAP && buckets[min_weight].empty()) { min_weight++; } return ret; } else { return heap.pop(); } } void clear() { for(uint16 i=min_weight; i haltestelle_t::open_list; uint8 haltestelle_t::markers[65536]; uint8 haltestelle_t::current_marker = 0; /** * Data for resumable route search */ halthandle_t haltestelle_t::last_search_origin; uint8 haltestelle_t::last_search_ware_catg_idx = 255; /** * This routine tries to find a route for a good packet (ware) * it will be called for * - new goods (either from simcity.cc or simfab.cc) * - goods that transfer and cannot be joined with other goods * - during re-routing * Therefore this routine eats up most of the performance in * later games. So all changes should be done with this in mind! * * If no route is found, ziel and zwischenziel are unbound handles. * If next_to_ziel in not NULL, it will get the koordinate of the stop * previous to target. Can be used to create passengers/mail back the * same route back * * if USE_ROUTE_SLIST_TPL is defined, the list template will be used. * However, this is about 50% slower. * * @param start_halts * @param start_halt_count * @param no_routing_over_overcrowding * @param return_ware * @param[out] ware */ int haltestelle_t::search_route( const halthandle_t *const start_halts, const uint16 start_halt_count, const bool no_routing_over_overcrowding, ware_t &ware, ware_t *const return_ware ) { const uint8 ware_catg_idx = ware.get_desc()->get_catg_index(); const uint8 ware_idx = ware.get_desc()->get_index(); // since also the factory halt list is added to the ground, we can use just this ... const planquadrat_t *const plan = welt->access( ware.get_target_pos() ); const halthandle_t *const halt_list = plan->get_haltlist(); // but we can only use a subset of these static vector_tpl end_halts(16); end_halts.clear(); // target halts are in these connected components // we start from halts only in the same components static vector_tpl end_conn_comp(16); end_conn_comp.clear(); // if one target halt is undefined, we have to start search from all halts bool end_conn_comp_undefined = false; for( uint32 h=0; hget_haltlist_count(); ++h ) { halthandle_t halt = halt_list[h]; if( halt.is_bound() && halt->is_enabled(ware_catg_idx) ) { // check if this is present in the list of start halts for( uint16 s=0; s within walking distance ware.set_target_halt( start_halts[s] ); ware.set_via_halt( halthandle_t() ); if( return_ware ) { return_ware->set_target_halt( start_halts[s] ); return_ware->set_via_halt( halthandle_t() ); } return ROUTE_WALK; } } end_halts.append(halt); // check connected component of target halt uint16 endhalt_conn_comp = halt->all_links[ware_catg_idx].catg_connected_component; if (endhalt_conn_comp == UNDECIDED_CONNECTED_COMPONENT) { // undefined: all start halts are probably connected to this target end_conn_comp_undefined = true; } else { // store connected component if (!end_conn_comp_undefined) { end_conn_comp.append_unique( endhalt_conn_comp ); } } } } if( end_halts.empty() ) { // no end halt found ware.set_target_halt( halthandle_t() ); ware.set_via_halt( halthandle_t() ); if( return_ware ) { return_ware->set_target_halt( halthandle_t() ); return_ware->set_via_halt( halthandle_t() ); } return NO_ROUTE; } // invalidate search history last_search_origin = halthandle_t(); // set current marker ++current_marker; if( current_marker==0 ) { MEMZERON(markers, halthandle_t::get_size()); current_marker = 1u; } // initialisations for end halts => save some checking inside search loop for(halthandle_t const e : end_halts) { uint16 const halt_id = e.get_id(); halt_data[ halt_id ].best_weight = 65535u; halt_data[ halt_id ].destination = 1u; halt_data[ halt_id ].depth = 1u; // to distinct them from start halts markers[ halt_id ] = current_marker; } uint16 const max_transfers = welt->get_settings().get_max_transfers(); uint16 const max_hops = welt->get_settings().get_max_hops(); uint16 allocation_pointer = 0; uint16 best_destination_weight = 65535u; // best weight among all destinations open_list.clear(); uint32 overcrowded_nodes = 0; // initialise the origin node(s) for( ; allocation_pointerall_links[ware_catg_idx].catg_connected_component; if (!end_conn_comp_undefined && start_conn_comp != UNDECIDED_CONNECTED_COMPONENT && !end_conn_comp.is_contained( start_conn_comp )){ // this start halt will not lead to any target continue; } open_list.insert( route_node_t(start_halt, 0) ); halt_data_t & start_data = halt_data[ start_halt.get_id() ]; start_data.best_weight = 65535u; start_data.destination = 0; start_data.depth = 0; start_data.overcrowded = false; // start halt overcrowding is handled by routines calling this one start_data.transfer = halthandle_t(); markers[ start_halt.get_id() ] = current_marker; } // here the normal routing with overcrowded stops is done while (!open_list.empty()) { if( overcrowded_nodes == open_list.get_count() ) { // all unexplored routes go over overcrowded stations return ROUTE_OVERCROWDED; } // take node out of open list route_node_t current_node = open_list.pop(); // do not use aggregate_weight as it is _not_ the weight of the current_node // there might be a heuristic weight added const uint16 current_halt_id = current_node.halt.get_id(); halt_data_t & current_halt_data = halt_data[ current_halt_id ]; overcrowded_nodes -= current_halt_data.overcrowded; if( current_halt_data.destination ) { // destination found ware.set_target_halt( current_node.halt ); assert(current_halt_data.transfer.get_id() != 0); if( return_ware ) { // next transfer for the reverse route // if the end halt and its connections contain more than one transfer halt then // the transfer halt may not be the last transfer of the forward route // (the re-routing will happen in haltestelle_t::fetch_goods) return_ware->set_via_halt(current_halt_data.transfer); // count the connected transfer halts (including end halt) uint8 t = current_node.halt->is_transfer(ware_catg_idx); for(connection_t const& i : current_node.halt->all_links[ware_catg_idx].connections) { if (t > 1) { break; } t += i.halt.is_bound() && i.is_transfer; } return_ware->set_via_halt( t<=1 ? current_halt_data.transfer : halthandle_t()); } // find the next transfer halthandle_t transfer_halt = current_node.halt; while( halt_data[ transfer_halt.get_id() ].depth > 1 ) { transfer_halt = halt_data[ transfer_halt.get_id() ].transfer; } ware.set_via_halt(transfer_halt); if( return_ware ) { // return ware's destination halt is the start halt of the forward trip assert( halt_data[ transfer_halt.get_id() ].transfer.get_id() ); return_ware->set_target_halt( halt_data[ transfer_halt.get_id() ].transfer ); } return current_halt_data.overcrowded ? ROUTE_OVERCROWDED : ROUTE_OK; } // check if the current halt is already in closed list if( current_halt_data.best_weight==0 ) { // shortest path to the current halt has already been found earlier continue; } if( current_halt_data.depth > max_transfers ) { // maximum transfer limit is reached -> do not add reachable halts to open list continue; } for(connection_t const& current_conn : current_node.halt->all_links[ware_catg_idx].connections) { // halt may have been deleted or joined => test if still valid if( !current_conn.halt.is_bound() ) { // removal seems better though ... continue; } // since these are pre-calculated, they should be always pointing to a valid ground // (if not, we were just under construction, and will be fine after 16 steps) const uint16 reachable_halt_id = current_conn.halt.get_id(); if( markers[ reachable_halt_id ]!=current_marker ) { // Case : not processed before // indicate that this halt has been processed markers[ reachable_halt_id ] = current_marker; if( current_conn.halt.is_bound() && current_conn.is_transfer && allocation_pointeris_overcrowded( ware_idx ) ); halt_data[ reachable_halt_id ].best_weight = total_weight; halt_data[ reachable_halt_id ].destination = 0; halt_data[ reachable_halt_id ].depth = current_halt_data.depth + 1u; halt_data[ reachable_halt_id ].transfer = current_node.halt; halt_data[ reachable_halt_id ].overcrowded = overcrowded_transfer; overcrowded_nodes += overcrowded_transfer; allocation_pointer++; // as the next halt is not a destination add WEIGHT_MIN open_list.insert( route_node_t(current_conn.halt, total_weight + WEIGHT_MIN) ); } else { // Case: non-optimal transfer halt -> put in closed list halt_data[ reachable_halt_id ].best_weight = 0; } } else { // Case: halt is removed / no transfer halt -> put in closed list halt_data[ reachable_halt_id ].best_weight = 0; } } // if not processed before else if( halt_data[ reachable_halt_id ].best_weight!=0 && halt_data[ reachable_halt_id ].depth>0) { // Case : processed before but not in closed list : that is, in open list // --> can only be destination halt or transfer halt // or start halt (filter the latter out with the condition depth>0) uint16 total_weight = current_halt_data.best_weight + current_conn.weight; if( total_weight create new node and update halt data const bool overcrowded_transfer = no_routing_over_overcrowding && ( current_halt_data.overcrowded || ( !halt_data[reachable_halt_id].destination && current_conn.halt->is_overcrowded( ware_idx ) ) ); halt_data[ reachable_halt_id ].best_weight = total_weight; // no need to update destination, as halt nature (as destination or transfer) will not change halt_data[ reachable_halt_id ].depth = current_halt_data.depth + 1u; halt_data[ reachable_halt_id ].transfer = current_node.halt; halt_data[ reachable_halt_id ].overcrowded = overcrowded_transfer; overcrowded_nodes += overcrowded_transfer; if( halt_data[reachable_halt_id].destination ) { best_destination_weight = total_weight; } else { // as the next halt is not a destination add WEIGHT_MIN total_weight += WEIGHT_MIN; } allocation_pointer++; open_list.insert( route_node_t(current_conn.halt, total_weight) ); } } // else if not in closed list } // for each connection entry // indicate that the current halt is in closed list current_halt_data.best_weight = 0; } // if the loop ends, nothing was found ware.set_target_halt( halthandle_t() ); ware.set_via_halt( halthandle_t() ); if( return_ware ) { return_ware->set_target_halt( halthandle_t() ); return_ware->set_via_halt( halthandle_t() ); } return NO_ROUTE; } void haltestelle_t::search_route_resumable( ware_t &ware ) { const uint8 ware_catg_idx = ware.get_desc()->get_catg_index(); // continue search if start halt and good category did not change const bool resume_search = last_search_origin == self && ware_catg_idx == last_search_ware_catg_idx; if (!resume_search) { last_search_origin = self; last_search_ware_catg_idx = ware_catg_idx; open_list.clear(); // set current marker ++current_marker; if( current_marker==0 ) { MEMZERON(markers, halthandle_t::get_size()); current_marker = 1u; } } // remember destination nodes, to reset them before returning static vector_tpl dest_indices(16); dest_indices.clear(); uint16 best_destination_weight = 65535u; // reset next transfer and destination halt to null -> if they remain null after search, no route can be found ware.set_target_halt( halthandle_t() ); ware.set_via_halt( halthandle_t() ); // find suitable destination halts for the ware packet's target position const planquadrat_t *const plan = welt->access( ware.get_target_pos() ); const halthandle_t *const halt_list = plan->get_haltlist(); // check halt list for presence of current halt for( uint8 h = 0; hget_haltlist_count(); ++h ) { if (halt_list[h] == self) { // a destination halt is the same as the current halt -> no route searching is necessary ware.set_target_halt( self ); return; } } // check explored connection if (resume_search) { for( uint8 h=0; hget_haltlist_count(); ++h ) { const halthandle_t halt = halt_list[h]; if (markers[ halt.get_id() ]==current_marker && halt_data[ halt.get_id() ].best_weight < best_destination_weight && halt.is_bound()) { best_destination_weight = halt_data[ halt.get_id() ].best_weight; ware.set_target_halt( halt ); ware.set_via_halt( halt_data[ halt.get_id() ].transfer ); } } // for all halts with halt_data.weight < explored_weight one of the best routes is found const uint16 explored_weight = open_list.empty() ? 65535u : open_list.front().aggregate_weight; if (best_destination_weight <= explored_weight && best_destination_weight < 65535u) { // we explored best route to this destination in last run // (any other not yet explored connection will have larger weight) // no need to search route for this ware return; } } // we start in this connected component uint16 const conn_comp = all_links[ ware_catg_idx ].catg_connected_component; // find suitable destination halt(s), if any for( uint8 h=0; hget_haltlist_count(); ++h ) { const halthandle_t halt = halt_list[h]; if( halt.is_bound() && halt->is_enabled(ware_catg_idx) ) { // test for connected component uint16 const dest_comp = halt->all_links[ ware_catg_idx ].catg_connected_component; if (dest_comp != UNDECIDED_CONNECTED_COMPONENT && conn_comp != UNDECIDED_CONNECTED_COMPONENT && conn_comp != dest_comp) { continue; } // initialisations for destination halts => save some checking inside search loop if( markers[ halt.get_id() ]!=current_marker ) { // first time -> initialise marker and all halt data markers[ halt.get_id() ] = current_marker; halt_data[ halt.get_id() ].best_weight = 65535u; halt_data[ halt.get_id() ].destination = true; } else { // initialised before -> only update destination bit set halt_data[ halt.get_id() ].destination = true; } dest_indices.append(halt.get_id()); } } if( dest_indices.empty() ) { // no destination halt found or current halt is the same as (all) the destination halt(s) return; } uint16 const max_transfers = welt->get_settings().get_max_transfers(); uint16 const max_hops = welt->get_settings().get_max_hops(); static uint16 allocation_pointer; if (!resume_search) { // initialise the origin node allocation_pointer = 1u; open_list.insert( route_node_t(self, 0) ); halt_data_t & start_data = halt_data[ self.get_id() ]; start_data.best_weight = 0; start_data.destination = 0; start_data.depth = 0; start_data.transfer = halthandle_t(); markers[ self.get_id() ] = current_marker; } while( !open_list.empty() ) { if (best_destination_weight <= open_list.front().aggregate_weight) { // best route to destination found already break; } route_node_t current_node = open_list.pop(); const uint16 current_halt_id = current_node.halt.get_id(); const uint16 current_weight = current_node.aggregate_weight; halt_data_t & current_halt_data = halt_data[ current_halt_id ]; // check if the current halt is already in closed list (or removed) if( !current_node.halt.is_bound() ) { continue; } else if( current_halt_data.best_weight < current_weight) { // shortest path to the current halt has already been found earlier // assert(markers[ current_halt_id ]==current_marker); continue; } else { // no need to update weight, as it is already the right one // assert(current_halt_data.best_weight == current_weight); } if( current_halt_data.destination ) { // destination found ware.set_target_halt( current_node.halt ); ware.set_via_halt( current_halt_data.transfer ); // update best_destination_weight to leave loop due to first check above best_destination_weight = current_weight; // if this destination halt is not a transfer halt -> do not proceed to process its reachable halt(s) if( !current_node.halt->is_transfer(ware_catg_idx) ) { continue; } } // not start halt, not transfer halt -> do not expand further if( current_halt_data.depth > 0 && !current_node.halt->is_transfer(ware_catg_idx) ) { continue; } if( current_halt_data.depth > max_transfers ) { // maximum transfer limit is reached -> do not add reachable halts to open list continue; } for(connection_t const& current_conn : current_node.halt->all_links[ware_catg_idx].connections) { const uint16 reachable_halt_id = current_conn.halt.get_id(); const uint16 total_weight = current_weight + current_conn.weight; if( !current_conn.halt.is_bound() ) { // Case: halt removed -> make sure we never visit it again markers[ reachable_halt_id ] = current_marker; halt_data[ reachable_halt_id ].best_weight = 0; } else if( markers[ reachable_halt_id ]!=current_marker ) { // Case : not processed before and not destination // indicate that this halt has been processed markers[ reachable_halt_id ] = current_marker; // update data halt_data[ reachable_halt_id ].best_weight = total_weight; halt_data[ reachable_halt_id ].destination = false; // reset necessary if this was set by search_route halt_data[ reachable_halt_id ].depth = current_halt_data.depth + 1u; halt_data[ reachable_halt_id ].transfer = current_halt_data.transfer.get_id() ? current_halt_data.transfer : current_conn.halt; if( current_conn.is_transfer && allocation_pointer need to check whether we can reach it with smaller weight if( total_weight update halt data halt_data[ reachable_halt_id ].best_weight = total_weight; halt_data[ reachable_halt_id ].transfer = current_halt_data.transfer.get_id() ? current_halt_data.transfer : current_conn.halt; // for transfer/destination nodes create new node if ( (halt_data[ reachable_halt_id ].destination || current_conn.is_transfer ) && allocation_pointer reset marker --markers[i]; } } } /** * Found route and station uncrowded */ void haltestelle_t::add_pax_happy(int n) { book(n, HALT_HAPPY); recalc_status(); } /** * Station in walking distance */ void haltestelle_t::add_pax_walked(int n) { book(n, HALT_WALKED); } /** * Station crowded */ void haltestelle_t::add_pax_unhappy(int n) { book(n, HALT_UNHAPPY); recalc_status(); } /** * Found no route */ void haltestelle_t::add_pax_no_route(int n) { book(n, HALT_NOROUTE); } void haltestelle_t::liefere_an_fabrik(const ware_t& ware) const { fabrik_t *const factory = fabrik_t::get_fab(ware.get_target_pos() ); if( factory ) { factory->liefere_an(ware.get_desc(), ware.amount); } } /* retrieves a ware packet for any destination in the list * needed, if the factory in question wants to remove something */ bool haltestelle_t::recall_ware( ware_t& w, uint32 menge ) { w.amount = 0; vector_tpl *warray = cargo[w.get_desc()->get_catg_index()]; if(warray!=NULL) { for(ware_t & tmp : *warray) { // skip empty entries if(tmp.amount==0 || w.get_index()!=tmp.get_index() || w.get_target_pos()!=tmp.get_target_pos()) { continue; } // not too much? if(tmp.amount > menge) { // not all can be loaded tmp.amount -= menge; w.amount = menge; } else { // leave an empty entry => joining will more often work w.amount = tmp.amount; tmp.amount = 0; } book(w.amount, HALT_ARRIVED); fabrik_t::update_transit( &w, false ); old_sort_mode = 255; return true; } } // nothing to take out return false; } void haltestelle_t::fetch_goods( slist_tpl &load, const goods_desc_t *good_category, uint32 requested_amount, const vector_tpl& destination_halts) { // first iterate over the next stop, then over the ware // might be a little slower, but ensures that passengers to nearest stop are served first // this allows for separate high speed and normal service vector_tpl *warray = cargo[good_category->get_catg_index()]; if( warray && !warray->empty() ) { for( uint32 i=0; i < destination_halts.get_count(); i++ ) { halthandle_t plan_halt = destination_halts[i]; // mark this stop as served, even if I do not load to avoid stealing transfer freight by later processed convois halt_served_this_step[good_category->get_catg_index()].append_unique(plan_halt); // The random offset will ensure that all goods have an equal chance to be loaded. uint32 offset = simrand(warray->get_count()); for( uint32 i=0; iget_count(); i++ ) { ware_t &tmp = (*warray)[ i+offset ]; // prevent overflow (faster than division) if( i+offset+1>=warray->get_count() ) { offset -= warray->get_count(); } // skip empty entries if(tmp.amount==0) { continue; } // goods without route -> returning passengers/mail if( !tmp.get_via_halt().is_bound() ) { search_route_resumable(tmp); if (!tmp.get_target_halt().is_bound()) { // no route anymore tmp.amount = 0; continue; } } // compatible car and right target stop? if( tmp.get_via_halt()==plan_halt ) { if( plan_halt->is_overcrowded( tmp.get_index() ) ) { if (welt->get_settings().is_avoid_overcrowding() && tmp.get_target_halt() != plan_halt) { // do not go for transfer to overcrowded transfer stop continue; } } // not too much? ware_t neu(tmp); if( tmp.amount > requested_amount ) { // not all can be loaded neu.amount = requested_amount; tmp.amount -= requested_amount; requested_amount = 0; } else { requested_amount -= tmp.amount; // leave an empty entry => joining will more often work tmp.amount = 0; } load.insert(neu); book(neu.amount, HALT_DEPARTED); old_sort_mode = 255; if (requested_amount==0) { return; } } } // nothing there to load } } } uint32 haltestelle_t::get_ware_summe(const goods_desc_t *wtyp) const { int sum = 0; const vector_tpl * warray = cargo[wtyp->get_catg_index()]; if(warray!=NULL) { for(ware_t const& i : *warray) { if (wtyp->get_index() == i.get_index()) { sum += i.amount; } } } return sum; } uint32 haltestelle_t::get_ware_fuer_zielpos(const goods_desc_t *wtyp, const koord zielpos) const { const vector_tpl * warray = cargo[wtyp->get_catg_index()]; if(warray!=NULL) { for(ware_t const& ware : *warray) { if(wtyp->get_index()==ware.get_index() && ware.get_target_pos()==zielpos) { return ware.amount; } } } return 0; } uint32 haltestelle_t::get_ware_fuer_zwischenziel(const goods_desc_t *wtyp, const halthandle_t zwischenziel) const { uint32 sum = 0; const vector_tpl * warray = cargo[wtyp->get_catg_index()]; if(warray!=NULL) { for(unsigned i=0; iget_count(); i++ ) { const ware_t &ware = (*warray)[i]; if(wtyp->get_index()==ware.get_index() && ware.get_via_halt()==zwischenziel) { sum += ware.amount; } } } return sum; } bool haltestelle_t::vereinige_waren(const ware_t &ware) { // pruefen ob die ware mit bereits wartender ware vereinigt werden kann vector_tpl * warray = cargo[ware.get_desc()->get_catg_index()]; if(warray!=NULL) { for(ware_t & tmp : *warray) { // join packets with same destination if(ware.same_destination(tmp)) { if( ware.get_via_halt().is_bound() && ware.get_via_halt()!=self ) { // update route if there is newer route tmp.set_via_halt( ware.get_via_halt() ); } tmp.amount += ware.amount; old_sort_mode = 255; return true; } } } return false; } // put the ware into the internal storage // take care of all allocation necessary void haltestelle_t::add_ware_to_halt(ware_t ware) { // now we have to add the ware to the stop vector_tpl * warray = cargo[ware.get_desc()->get_catg_index()]; if(warray==NULL) { // this type was not stored here before ... warray = new vector_tpl(4); cargo[ware.get_desc()->get_catg_index()] = warray; } // the ware will be put into the first entry with menge==0 old_sort_mode = 255; for(ware_t & i : *warray) { if (i.amount == 0) { i = ware; return; } } // here, if no free entries found warray->append(ware); } /* same as liefere an, but there will be no route calculated, * since it hase be calculated just before * (execption: route contains us as intermediate stop) */ uint32 haltestelle_t::starte_mit_route(ware_t ware) { if(ware.get_target_halt()==self) { if( ware.to_factory ) { // muss an factory geliefert werden liefere_an_fabrik(ware); } // already there: finished (may be happen with overlapping areas and returning passengers) return ware.amount; } // no valid next stops? Or we are the next stop? if(ware.get_via_halt()==self) { dbg->error("haltestelle_t::starte_mit_route()","route cannot contain us as first transfer stop => recalc route!"); if( search_route( &self, 1u, false, ware )==NO_ROUTE ) { // no route found? dbg->error("haltestelle_t::starte_mit_route()","no route found!"); return ware.amount; } } // passt das zu bereits wartender ware ? if(vereinige_waren(ware)) { // dann sind wir schon fertig; return ware.amount; } // add to internal storage add_ware_to_halt(ware); return ware.amount; } uint32 haltestelle_t::liefere_an(ware_t ware) { // no valid next stops? if(!ware.get_target_halt().is_bound() || !ware.get_via_halt().is_bound()) { // write a log entry and discard the goods dbg->warning("haltestelle_t::liefere_an()","%d %s delivered to %s have no longer a route to their destination!", ware.amount, translator::translate(ware.get_name()), get_name() ); return ware.amount; } // did we arrived? if( welt->access(ware.get_target_pos())->is_connected(self) ) { if( ware.to_factory ) { // muss an factory geliefert werden liefere_an_fabrik(ware); } else if( ware.is_passenger() ) { // arriving passenger may create pedestrians if(env_t::stop_pedestrians) { int menge = ware.amount; for(tile_t const& i : tiles ) { if (menge <= 0) { break; } if( (station_type & (loadingbay | busstop)) == 0 || i.grund->get_weg(road_wt) ) { // if the station has road tiles, then start passenger generation there to speed things up menge = pedestrian_t::generate_pedestrians_near(i.grund, menge); } } } } return ware.amount; } // do we have already something going in this direction here? if( vereinige_waren(ware) ) { return ware.amount; } // not near enough => we need to do a re-routing halthandle_t old_target = ware.get_target_halt(); search_route_resumable(ware); if (!ware.get_target_halt().is_bound()) { // target halt no longer there => delete and remove from fab in transit fabrik_t::update_transit( &ware, false ); return ware.amount; } // try to join with existing freight only if target has changed if(old_target != ware.get_target_halt() && vereinige_waren(ware)) { return ware.amount; } // add to internal storage add_ware_to_halt(ware); return ware.amount; } /** * @param[out] buf Goods description text (buf) */ void haltestelle_t::get_freight_info(cbuffer_t & buf) { if(old_sort_mode != env_t::default_sortmode) { // resort only inf absolutely needed ... old_sort_mode = env_t::default_sortmode; buf.clear(); for(unsigned i=0; i * warray = cargo[i]; if(warray) { freight_list_sorter_t::sort_freight(*warray, buf, (freight_list_sorter_t::sort_mode_t)env_t::default_sortmode, NULL, "waiting", this->self); } } } } void haltestelle_t::get_short_freight_info(cbuffer_t & buf) const { bool got_one = false; for(unsigned int i=0; i0) { if(got_one) { buf.append(", "); } int max = get_capacity( i>2?2:i ); buf.printf("%d(%d)%s %s", summe, max, translator::translate(wtyp->get_mass()), translator::translate(wtyp->get_name())); got_one = true; } } } if(got_one) { buf.append(" "); buf.append(translator::translate("waiting")); buf.append("\n"); } else { buf.append(translator::translate("no goods waiting")); buf.append("\n"); } } void haltestelle_t::open_info_window() { create_win( new halt_info_t(self), w_info, magic_halt_info + self.get_id() ); } sint64 haltestelle_t::calc_maintenance() const { sint64 maintenance = 0; for(tile_t const& i : tiles ) { if( gebaeude_t* const gb = i.grund->find() ) { maintenance += welt->get_settings().maint_building * gb->get_tile()->get_desc()->get_level(); } } return maintenance; } // changes this to a public transfer exchange stop void haltestelle_t::change_owner( player_t *player ) { // check if already public if( owner == player ) { return; } // process every tile of stop slist_tpl joining; for(tile_t const& i : tiles) { grund_t* const gr = i.grund; if( gebaeude_t* gb = gr->find() ) { // change ownership player_t *gbplayer =gb->get_owner(); gb->set_owner(player); gb->set_flag(obj_t::dirty); sint64 const monthly_costs = welt->get_settings().maint_building * gb->get_tile()->get_desc()->get_level(); waytype_t const costs_type = gb->get_waytype(); player_t::add_maintenance(gbplayer, -monthly_costs, costs_type); player_t::add_maintenance(player, monthly_costs, costs_type); // cost is computed as cst_make_public_months sint64 const cost = -welt->scale_with_month_length(monthly_costs * welt->get_settings().cst_make_public_months); player_t::book_construction_costs(gbplayer, cost, get_basis_pos(), costs_type); player_t::book_construction_costs(player, -cost, koord::invalid, costs_type); } // change way ownership bool has_been_announced = false; for( int j=0; j<2; j++ ) { if( weg_t *w=gr->get_weg_nr(j) ) { // change ownership of way... player_t *wplayer = w->get_owner(); if( owner==wplayer ) { w->set_owner( player ); w->set_flag(obj_t::dirty); sint64 cost = w->get_desc()->get_maintenance(); // of tunnel... if( tunnel_t *t=gr->find() ) { t->set_owner( player ); t->set_flag(obj_t::dirty); cost = t->get_desc()->get_maintenance(); } waytype_t const financetype = w->get_desc()->get_finance_waytype(); player_t::add_maintenance( wplayer, -cost, financetype); player_t::add_maintenance( player, cost, financetype); // multiplayer notification message if( owner != welt->get_public_player() && env_t::networkmode && !has_been_announced ) { cbuffer_t buf; buf.printf( translator::translate("(%s) now public way."), w->get_pos().get_str() ); welt->get_message()->add_message( buf, w->get_pos(), message_t::ai, PLAYER_FLAG|player->get_player_nr(), IMG_EMPTY ); has_been_announced = true; // one message is enough } cost = -welt->scale_with_month_length(cost * (player==welt->get_public_player())*welt->get_settings().cst_make_public_months ); player_t::book_construction_costs(wplayer, cost, koord::invalid, financetype); } } } // make way object public if any suitable for( uint8 i = 1; i < gr->obj_count(); i++ ) { if( wayobj_t *const wo = obj_cast(gr->obj_bei(i)) ) { player_t *woplayer = wo->get_owner(); if( owner==woplayer ) { sint64 const cost = wo->get_desc()->get_maintenance(); // change ownership wo->set_owner( player ); wo->set_flag(obj_t::dirty); waytype_t const financetype = wo->get_desc()->get_waytype(); player_t::add_maintenance( woplayer, -cost, financetype); player_t::add_maintenance( player, cost, financetype); player_t::book_construction_costs( woplayer, cost, koord::invalid, financetype); } } } } // now finally change owner owner = player; // Old player is still allowed to serve this stop, no breaking networks // Should not matter as stops can only be made public (for now) set_permissions(permissions); rebuild_connections(); rebuild_linked_connections(); rebuild_connected_components(); // tell the world of it ... if( player == welt->get_public_player() && env_t::networkmode ) { cbuffer_t buf; buf.printf( translator::translate("%s at (%i,%i) now public stop."), get_name(), get_basis_pos().x, get_basis_pos().y ); welt->get_message()->add_message( buf, get_basis_pos3d(), message_t::ai, PLAYER_FLAG|player->get_player_nr(), IMG_EMPTY ); } } // merge stop void haltestelle_t::merge_halt( halthandle_t halt_merged ) { if( halt_merged == self || !halt_merged.is_bound() ) { return; } halt_merged->change_owner( owner ); // take the name from the larger halt if (get_capacity(0) + get_capacity(1) + get_capacity(2) < halt_merged->get_capacity(0) + halt_merged->get_capacity(1) + halt_merged->get_capacity(2)) { // the other halt was bigger => keep the old name plainstring bigger_name = halt_merged->get_name(); halt_merged->set_name(" "); set_name(bigger_name); } // add statistics for( int month=0; monthfinancial_history[month][type]; halt_merged->financial_history[month][type] = 0; } } slist_tpl tiles_to_join; for(tile_t const& i : halt_merged->get_tiles()) { tiles_to_join.append(i); } while( !tiles_to_join.empty() ) { // ATTENTION: Anz convoi reservation to this tile will be lost! grund_t *gr = tiles_to_join.remove_first().grund; // transfer tiles to us halt_merged->rem_grund(gr); add_grund(gr, tiles_to_join.empty()); // just reconnect on last tile to merge } assert(!halt_merged->existiert_in_welt()); // Allow everyone who could serve either halt to continue serving it set_permissions(halt_merged->get_permissions() | permissions); // transfer goods halt_merged->transfer_goods(self); destroy(halt_merged); recalc_basis_pos(); // also rebuild our connections recalc_station_type(); rebuild_connections(); rebuild_linked_connections(); rebuild_connected_components(); } void haltestelle_t::transfer_goods(halthandle_t halt) { if (!self.is_bound() || !halt.is_bound()) { return; } // transfer goods to halt for(uint8 i=0; i * warray = cargo[i]; if (warray) { for(ware_t const& j : *warray) { halt->add_ware_to_halt(j); } delete cargo[i]; cargo[i] = NULL; } } } // private helper function for recalc_station_type() void haltestelle_t::add_to_station_type( grund_t *gr ) { // init in any case ... if( tiles.empty() ) { capacity[0] = 0; capacity[1] = 0; capacity[2] = 0; enables = 0; station_type = invalid; } const gebaeude_t* gb = gr->find(); const building_desc_t *desc=gb?gb->get_tile()->get_desc():NULL; if( gr->is_water() && gb ) { // may happen around oil rigs and so on station_type |= dock; // for water factories if(desc) { // enabled the matching types enables |= desc->get_enabled(); if (welt->get_settings().is_separate_halt_capacities()) { if(desc->get_enabled()&1) { capacity[0] += desc->get_capacity(); } if(desc->get_enabled()&2) { capacity[1] += desc->get_capacity(); } if(desc->get_enabled()&4) { capacity[2] += desc->get_capacity(); } } else { // no separate capacities: sum up all capacity[0] += desc->get_capacity(); capacity[2] = capacity[1] = capacity[0]; } } return; } if( desc==NULL ) { // no desc, but solid ground?!? dbg->error("haltestelle_t::get_station_type()","ground belongs to halt but no desc?"); return; } // there is only one loading bay ... switch (desc->get_type()) { case building_desc_t::dock: case building_desc_t::flat_dock: station_type |= dock; break; // two ways on ground can only happen for tram tracks on streets, there buses and trams can stop case building_desc_t::generic_stop: switch (desc->get_extra()) { case road_wt: station_type |= (desc->get_enabled()&3)!=0 ? busstop : loadingbay; if (gr->has_two_ways()) { // tram track on street station_type |= tramstop; } break; case water_wt: station_type |= dock; break; case air_wt: station_type |= airstop; break; case monorail_wt: station_type |= monorailstop; break; case track_wt: station_type |= railstation; break; case tram_wt: station_type |= tramstop; if (gr->has_two_ways()) { // tram track on street station_type |= (desc->get_enabled()&3)!=0 ? busstop : loadingbay; } break; case maglev_wt: station_type |= maglevstop; break; case narrowgauge_wt: station_type |= narrowgaugestop; break; default: ; } break; default: ; } // enabled the matching types enables |= desc->get_enabled(); if (welt->get_settings().is_separate_halt_capacities()) { if(desc->get_enabled()&1) { capacity[0] += desc->get_capacity(); } if(desc->get_enabled()&2) { capacity[1] += desc->get_capacity(); } if(desc->get_enabled()&4) { capacity[2] += desc->get_capacity(); } } else { // no separate capacities: sum up all capacity[0] += desc->get_capacity(); capacity[2] = capacity[1] = capacity[0]; } } /* * recalculated the station type(s) * since it iterates over all ground, this is better not done too often, because line management and station list * queries this information regularly; Thus, we do this, when adding new ground * This recalculates also the capacity from the building levels ... */ void haltestelle_t::recalc_station_type() { capacity[0] = 0; capacity[1] = 0; capacity[2] = 0; enables = 0; station_type = invalid; // iterate over all tiles for(tile_t const& i : tiles) { grund_t* const gr = i.grund; add_to_station_type( gr ); } // and set halt info again for(tile_t const& i : tiles) { i.grund->set_halt( self ); } recalc_status(); } // necessary to load pre0.99.13 savegames void warenziel_rdwr(loadsave_t *file) { koord ziel; ziel.rdwr(file); char tn[256]; file->rdwr_str(tn, lengthof(tn)); } void haltestelle_t::rdwr(loadsave_t *file) { xml_tag_t h( file, "haltestelle_t" ); sint32 owner_n; koord3d k; // will restore halthandle_t after loading if(file->is_version_atleast(110, 6)) { if(file->is_saving()) { uint16 halt_id = self.is_bound() ? self.get_id() : 0; file->rdwr_short(halt_id); } else { uint16 halt_id; file->rdwr_short(halt_id); self.set_id(halt_id); self = halthandle_t(this, halt_id); } } else { if (file->is_loading()) { self = halthandle_t(this); } } if(file->is_saving()) { owner_n = welt->sp2num( owner ); } if(file->is_version_less(99, 8)) { init_pos.rdwr( file ); } file->rdwr_long(owner_n); if(file->is_version_less(88, 6)) { bool dummy; file->rdwr_bool(dummy); // pax file->rdwr_bool(dummy); // mail file->rdwr_bool(dummy); // ware } if(file->is_loading()) { owner = welt->get_player_or_create(owner_n); permissions = 0; /* will iterate later */ k.rdwr( file ); while(k!=koord3d::invalid) { grund_t *gr = welt->lookup(k); if(!gr) { dbg->error("haltestelle_t::rdwr()", "invalid position %s", k.get_str() ); gr = welt->lookup_kartenboden(k.get_2d()); dbg->error("haltestelle_t::rdwr()", "setting to %s", gr->get_pos().get_str() ); } // during loading and saving halts will be referred by their base position // so we may already be defined ... if(gr->get_halt().is_bound()) { dbg->warning( "haltestelle_t::rdwr()", "bound to ground twice at (%i,%i)!", k.x, k.y ); } // now check, if there is a building -> we allow no longer ground without building! const gebaeude_t* gb = gr->find(); const building_desc_t *desc=gb?gb->get_tile()->get_desc():NULL; if(desc) { add_grund( gr, false /*do not relink factories now*/ ); // verbinde_fabriken will be called in finish_rd } else { dbg->warning("haltestelle_t::rdwr()", "will no longer add ground without building at %s!", k.get_str() ); } k.rdwr( file ); } } else { for(tile_t const& i : tiles) { k = i.grund->get_pos(); k.rdwr( file ); } k = koord3d::invalid; k.rdwr( file ); } init_pos = tiles.empty() ? koord::invalid : tiles.front().grund->get_pos().get_2d(); if(file->is_saving()) { const char *s; for(unsigned i=0; i *warray = cargo[i]; if(warray) { s = "y"; // needs to be non-empty file->rdwr_str(s); if( file->is_version_less(112, 3) ) { uint16 count = warray->get_count(); file->rdwr_short(count); } else { uint32 count = warray->get_count(); file->rdwr_long(count); } for(ware_t & ware : *warray) { ware.rdwr(file); } } } s = ""; file->rdwr_str(s); } else { // restoring all goods in the station char s[256]; file->rdwr_str(s, lengthof(s)); while(*s) { uint32 count; if( file->is_version_less(112, 3) ) { uint16 scount; file->rdwr_short(scount); count = scount; } else { file->rdwr_long(count); } if(count>0) { for( uint32 i = 0; i < count; i++ ) { // add to internal storage (use this function, since the old categories were different) ware_t ware(file); if( ware.get_desc() && ware.amount>0 && welt->is_within_limits(ware.get_target_pos()) ) { add_ware_to_halt(ware); // restore in-transit information fabrik_t::update_transit( &ware, true ); } else if( ware.amount>0 ) { if( ware.get_desc() ) { dbg->error( "haltestelle_t::rdwr()", "%i of %s to %s ignored!", ware.amount, ware.get_name(), ware.get_target_pos().get_str() ); } else { dbg->error( "haltestelle_t::rdwr()", "%i of unknown to %s ignored!", ware.amount, ware.get_target_pos().get_str() ); } } } } file->rdwr_str(s, lengthof(s)); } // old games save the list with stations // however, we have to rebuilt them anyway for the new format if(file->is_version_less(99, 13)) { uint16 count; file->rdwr_short(count); for(int i=0; iis_version_atleast(111, 1) ) { for (int j = 0; jrdwr_longlong(financial_history[k][j]); } } } else { // old history did not know about walked pax for (int j = 0; j<7; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (size_t k = MAX_MONTHS; k-- != 0;) { financial_history[k][HALT_WALKED] = 0; } } if (file->is_version_atleast(124, 5)) { file->rdwr_short(permissions); set_permissions(permissions); } else if (file->is_loading()) { permissions = owner_n == 1 ? 0xFFFFu : (1 << owner_n); set_permissions(permissions); } } void haltestelle_t::finish_rd() { reconnect_factories(); stale_convois.clear(); stale_lines.clear(); // fix good destination coordinates for(unsigned i=0; i * warray = cargo[i]; for(ware_t & j : *warray) { j.finish_rd(welt); } // merge identical entries (should only happen with old games) for(unsigned j=0; jget_count(); j++) { if( (*warray)[j].amount==0 ) { continue; } for(unsigned k=j+1; kget_count(); k++) { if( (*warray)[k].amount>0 && (*warray)[j].same_destination( (*warray)[k] ) ) { (*warray)[j].amount += (*warray)[k].amount; (*warray)[k].amount = 0; } } } } } // handle name for old stations which don't exist in kartenboden // also recover from stations without tiles (from broken savegames) grund_t* bd = welt->lookup(get_basis_pos3d()); if(bd!=NULL) { // what kind of station here? recalc_station_type(); if( !bd->get_flag(grund_t::has_text) ) { // restore label and bridges grund_t* bd_old = welt->lookup_kartenboden(get_basis_pos()); if(bd_old) { // transfer name (if there) const char *name = bd->get_text(); if(name) { set_name( name ); bd_old->set_text( NULL ); } else { set_name( "Unknown" ); } } } else { const char *current_name = bd->get_text(); if( all_names.get(current_name).is_bound() && fabrik_t::get_fab(get_basis_pos())==NULL ) { // try to get a new name ... const char *new_name; if( station_type & airstop ) { new_name = create_name( get_basis_pos(), "Airport" ); } else if( station_type & dock ) { new_name = create_name( get_basis_pos(), "Dock" ); } else if( station_type & (railstation|monorailstop|maglevstop|narrowgaugestop) ) { new_name = create_name( get_basis_pos(), "BF" ); } else { new_name = create_name( get_basis_pos(), "H" ); } dbg->warning("haltestelle_t::set_name()","name already used: \'%s\' -> \'%s\'", current_name, new_name ); bd->set_text( new_name ); current_name = new_name; } all_names.set( current_name, self ); } } // no need to recalc status here, as this is done in haltestelle_t::end_load_game // (after convois have been loaded) recalc_basis_pos(); reconnect_counter = welt->get_schedule_counter()-1; last_search_origin = halthandle_t(); } void haltestelle_t::book(sint64 amount, int cost_type) { assert(cost_type <= MAX_HALT_COST); financial_history[0][cost_type] += amount; } void haltestelle_t::init_financial_history() { for (int j = 0; jpalette_lookup(financial_history[0][HALT_CONVOIS_ARRIVED] > 0 ? COL_GREEN : COL_YELLOW); // since the status is ordered ... uint8 status_bits = 0; MEMZERO(overcrowded); uint64 total_sum = 0; if (get_pax_enabled()) { const uint32 pax_capacity = get_capacity(0); const uint32 pax_count = get_ware_summe(goods_manager_t::passengers); total_sum += pax_count; const uint32 num_happy = get_pax_happy(); const uint32 num_unhappy = get_pax_unhappy(); const uint32 num_routed = num_happy + num_unhappy; const bool is_overcrowded = pax_count > pax_capacity; const bool is_seriously_overcrowded = pax_count > pax_capacity + 200; const uint32 satisfaction_percent = num_routed > 0 ? (num_happy * 100) / num_routed : 100; if (is_overcrowded) { overcrowded[0] |= 1; status_bits |= is_seriously_overcrowded ? 2 : 1; } if (num_unhappy >= 200 && is_overcrowded && satisfaction_percent < 75) { status_bits |= 2; } else if (num_unhappy >= 40 && satisfaction_percent < 90) { status_bits |= 1; } } if(get_mail_enabled()) { const uint32 mail_capacity = get_capacity(1); const uint32 mail_count = get_ware_summe(goods_manager_t::mail); total_sum += mail_count; const bool is_overcrowded = mail_count > mail_capacity; const bool is_seriously_overcrowded = mail_count > mail_capacity + 200; if(is_overcrowded) { status_bits |= is_seriously_overcrowded ? 2 : 1; overcrowded[0] |= 2; } } // now for all goods if(status_color!=gfx->palette_lookup(COL_RED) && get_ware_enabled()) { const uint8 count = goods_manager_t::get_count(); const uint32 max_ware = get_capacity(2); for( uint32 i = 3; i < count; i++ ) { goods_desc_t const* const wtyp = goods_manager_t::get_info(i); const uint32 ware_sum = get_ware_summe(wtyp); total_sum += ware_sum; if(ware_sum>max_ware) { status_bits |= ware_sum > max_ware + 32 ? 2 : 1; // for now report only serious overcrowding on transfer stops overcrowded[wtyp->get_index()/8] |= 1<<(wtyp->get_index()%8); } } } const bool has_any_activity = (financial_history[0][HALT_WAITING] + financial_history[0][HALT_DEPARTED] + financial_history[0][HALT_ARRIVED]) > 0; const bool is_connected = !registered_convoys.empty() || !registered_lines.empty(); if (!is_connected) { status_color = gfx->palette_lookup(COL_NO_ROUTE); } else if (status_bits != 0) { // take the worst color for status status_color = gfx->palette_lookup(status_bits&2 ? COL_RED : COL_ORANGE); } else { status_color = gfx->palette_lookup(has_any_activity ? COL_GREEN : COL_YELLOW); } financial_history[0][HALT_WAITING] = total_sum; } /** * Draws some nice colored bars giving some status information */ void haltestelle_t::display_status(sint16 xpos, sint16 ypos) { // Do we need to display permissions? uint16 player_count = 0; for( uint16 i = 0; i < PLAYER_UNOWNED; i++ ) { if( (permissions&(1<get_player(i) && !welt->get_player(i)->is_public_service() ) { player_count += 1; } } // ignore freight that cannot reach to this station sint16 count = 0; for( uint16 i = 0; i < goods_manager_t::get_count(); i++ ) { if( i == 2 ) { continue; // ignore freight none } if( gibt_ab( goods_manager_t::get_info(i) ) ) { count++; } } ypos += -D_WAITINGBAR_WIDTH - LINESPACE/6; bool players_dirty = false; if( permissions != last_permissions ) { if( last_player_count ) { // erase old permissions display const sint16 x = xpos - (last_player_count * 17 - gfx->get_tile_raster_width()) / 2; gfx->mark_rect_dirty_wc( x, ypos, x + last_player_count * 17, ypos + D_WAITINGBAR_WIDTH ); } last_permissions = permissions; last_player_count = player_count; players_dirty = true; } if( player_count > 1 ) { uint8 old_count = 0; sint16 x = xpos - (player_count * 17 - gfx->get_tile_raster_width()) / 2; for( uint16 i = 0; i get_player(i) && !welt->get_player(i)->is_public_service() ) { const PIXVAL color = gfx->palette_lookup(welt->get_player(i)->get_player_color1()+4); gfx->draw_rect_clipped( x, ypos, 16, D_WAITINGBAR_WIDTH, color, false CLIP_NUM_DEFAULT); x += 17; } } ypos += -D_WAITINGBAR_WIDTH - 1; } if( count != last_bar_count || players_dirty ) { // bars will shift x positions, extra players may shift y positions, // mark entire station bar region dirty scr_coord_val max_bar_height = 0; for( sint16 i = 0; i < last_bar_count; i++ ) { if( last_bar_height[i] > max_bar_height ) { max_bar_height = last_bar_height[i]; } } const scr_coord_val x = xpos - (last_bar_count * D_WAITINGBAR_WIDTH - gfx->get_tile_raster_width()) / 2; gfx->mark_rect_dirty_wc( x - 1 - D_WAITINGBAR_WIDTH, ypos, x + last_bar_count * D_WAITINGBAR_WIDTH + 12 - 2, ypos - 11 ); // reset bar heights for new count last_bar_height.clear(); last_bar_height.reserve( count ); for( sint16 i = 0; i < count; i++ ) { last_bar_height.append(0); } last_bar_count = count; } xpos -= (count * D_WAITINGBAR_WIDTH - gfx->get_tile_raster_width()) / 2; const int x = xpos; sint16 bar_height_index = 0; uint32 max_capacity; for( uint8 i = 0; i < goods_manager_t::get_count(); i++ ) { if( i == 2 ) { continue; // ignore freight none } const goods_desc_t *wtyp = goods_manager_t::get_info(i); if( gibt_ab( wtyp ) ) { if( i < 2 ) { max_capacity = get_capacity(i); } else { max_capacity = get_capacity(2); } const uint32 sum = get_ware_summe( wtyp ); uint32 v = min( sum, max_capacity ); if( max_capacity > 512 ) { v = 2 + (v * 128) / max_capacity; } else { v = (v / 4) + 2; } gfx->draw_rect_clipped( xpos, ypos - v - 1, 1, v, gfx->palette_lookup( COL_GREY4 ), false CLIP_NUM_DEFAULT); gfx->draw_rect_clipped( xpos + 1, ypos - v - 1, D_WAITINGBAR_WIDTH - 2, v, wtyp->get_color(), false CLIP_NUM_DEFAULT); gfx->draw_rect_clipped( xpos + D_WAITINGBAR_WIDTH - 1, ypos - v - 1, 1, v, gfx->palette_lookup( COL_GREY1 ), false CLIP_NUM_DEFAULT); // show up arrow for capped values if( sum > max_capacity ) { gfx->draw_rect_clipped( xpos + (D_WAITINGBAR_WIDTH / 2) - 1, ypos - v - 6, 2, 4, gfx->palette_lookup( COL_WHITE ), false CLIP_NUM_DEFAULT); gfx->draw_rect_clipped( xpos + (D_WAITINGBAR_WIDTH / 2) - 2, ypos - v - 5, 4, 1, gfx->palette_lookup( COL_WHITE ), false CLIP_NUM_DEFAULT); v += 5; // for marking dirty } if( last_bar_height[bar_height_index] != (scr_coord_val)v ) { if( (scr_coord_val)v > last_bar_height[bar_height_index] ) { // bar will be longer, mark new height dirty gfx->mark_rect_dirty_wc( xpos, ypos - v - 1, xpos + D_WAITINGBAR_WIDTH, ypos - 1 ); } else { // bar will be shorter, mark old height dirty gfx->mark_rect_dirty_wc( xpos, ypos - last_bar_height[ bar_height_index ] - 1, xpos + D_WAITINGBAR_WIDTH, ypos - 1 ); } last_bar_height[bar_height_index] = v; } bar_height_index++; xpos += D_WAITINGBAR_WIDTH; } } // status color box below bool dirty = false; if( get_status_farbe() != last_status_color ) { last_status_color = get_status_farbe(); dirty = true; } gfx->draw_rect_clipped( x - 1 - 4, ypos, count * D_WAITINGBAR_WIDTH + 12 - 2, D_WAITINGBAR_WIDTH, get_status_farbe(), dirty CLIP_NUM_DEFAULT); } bool haltestelle_t::add_grund(grund_t *gr, bool relink_factories) { assert(gr!=NULL); // new halt? if( tiles.is_contained(gr) ) { return false; } koord pos = gr->get_pos().get_2d(); add_to_station_type( gr ); gr->set_halt( self ); tiles.append( gr ); // add to hashtable if (all_koords) { sint32 n = get_halt_key( gr->get_pos(), welt->get_size().y ); all_koords->set( n, self ); } // appends this to the ground // after that, the surrounding ground will know of this station bool insert_unsorted = !relink_factories; uint16 const cov = welt->get_settings().get_station_coverage(); for (int y = -cov; y <= cov; y++) { for (int x = -cov; x <= cov; x++) { koord p=pos+koord(x,y); planquadrat_t *plan = welt->access(p); if(plan) { plan->add_to_haltlist( self, insert_unsorted); plan->get_kartenboden()->set_flag(grund_t::dirty); } } } // since suddenly other factories may be connect to us too if (relink_factories) { reconnect_factories(); } // This entire loop is just for the assertion below. // Consider deleting the assertion --neroden bool grund_is_where_it_should_be = false; const planquadrat_t* plan = welt->access(pos); for( uint8 i=0; i < plan->get_boden_count(); i++ ) { const grund_t* found_gr = plan->get_boden_bei(i); if (found_gr == gr) { grund_is_where_it_should_be = true; break; } } if ( !grund_is_where_it_should_be || gr->get_halt() != self || !gr->is_halt() ) { dbg->error( "haltestelle_t::add_grund()", "no ground added to (%s)", gr->get_pos().get_str() ); } init_pos = tiles.front().grund->get_pos().get_2d(); // need to add lines or convois? if (relink_factories && rebuilt_schedule_registration(false,true)) { // we may more convois serving us welt->set_schedule_counter(); } return true; } bool haltestelle_t::rem_grund(grund_t *gr) { // namen merken if(!gr) { return false; } slist_tpl::iterator i = std::find(tiles.begin(), tiles.end(), gr); if (i == tiles.end()) { // was not part of station => do nothing dbg->error("haltestelle_t::rem_grund()","removed illegal ground from halt"); return false; } // first tile => remove name from this tile ... char buf[256]; const char* station_name_to_transfer = NULL; if (i == tiles.begin() && i->grund->get_name()) { tstrncpy(buf, get_name(), lengthof(buf)); station_name_to_transfer = buf; set_name(NULL); } // now remove tile from list tiles.erase(i); welt->set_schedule_counter(); init_pos = tiles.empty() ? koord::invalid : tiles.front().grund->get_pos().get_2d(); // re-add name if (station_name_to_transfer != NULL && !tiles.empty()) { label_t *lb = tiles.front().grund->find(); delete lb; // very old version had labels for names set_name( station_name_to_transfer ); } bool remove_halt = true; planquadrat_t *pl = welt->access( gr->get_pos().get_2d() ); if(pl) { // no longer present on this level gr->set_halt(halthandle_t()); // still connected elsewhere? for(unsigned i=0; iget_boden_count(); i++ ) { if(pl->get_boden_bei(i)->get_halt()==self) { // still connected with other ground => do not remove from plan ... remove_halt = false; break; } } } if (remove_halt) { // otherwise remove from plan ... if (pl) { pl->get_kartenboden()->set_flag(grund_t::dirty); } uint16 const cov = welt->get_settings().get_station_coverage(); for (int y = -cov; y <= cov; y++) { for (int x = -cov; x <= cov; x++) { planquadrat_t *pl = welt->access( gr->get_pos().get_2d()+koord(x,y) ); if(pl) { pl->remove_from_haltlist(self); pl->get_kartenboden()->set_flag(grund_t::dirty); } } } // factory reach may have been changed ... reconnect_factories(); } // needs to be done, if this was a dock recalc_station_type(); rebuilt_schedule_registration(true,false); return true; } bool haltestelle_t::existiert_in_welt() const { return !tiles.empty(); } /* marks a coverage area */ void haltestelle_t::mark_unmark_coverage(const bool mark) const { // iterate over all tiles uint16 const cov = welt->get_settings().get_station_coverage(); koord const size(cov * 2 + 1, cov * 2 + 1); for(tile_t const& i : tiles) { welt->mark_area(i.grund->get_pos() - size / 2, size, mark); } } /* Find a tile where this type of vehicle could stop */ const grund_t *haltestelle_t::find_matching_position(const waytype_t w) const { // iterate over all tiles for(tile_t const& i : tiles) { if (i.grund->hat_weg(w)) { return i.grund; } } return NULL; } /* checks, if there is an unoccupied loading bay for this kind of thing */ bool haltestelle_t::find_free_position(const waytype_t w,convoihandle_t cnv,const obj_t::typ d) const { // iterate over all tiles for(tile_t const& i : tiles) { if (i.reservation == cnv || !i.reservation.is_bound()) { // not reserved grund_t* const gr = i.grund; assert(gr); // found a stop for this waytype but without object d ... if(gr->hat_weg(w) && gr->suche_obj(d)==NULL) { // not occupied return true; } } } return false; } /* reserves a position (caution: railblocks work differently! */ bool haltestelle_t::reserve_position(grund_t *gr,convoihandle_t cnv) { slist_tpl::iterator i = std::find(tiles.begin(), tiles.end(), gr); if (i != tiles.end()) { if (i->reservation == cnv) { //DBG_MESSAGE("haltestelle_t::reserve_position()","gr=%d,%d already reserved by cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id()); return true; } // not reserved if (!i->reservation.is_bound()) { grund_t* gr = i->grund; if(gr) { // found a stop for this waytype but without object d ... vehicle_t const& v = *cnv->front(); if (gr->hat_weg(v.get_waytype()) && !gr->suche_obj(v.get_typ())) { // not occupied //DBG_MESSAGE("haltestelle_t::reserve_position()","success for gr=%i,%i cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id()); i->reservation = cnv; return true; } } } } //DBG_MESSAGE("haltestelle_t::reserve_position()","failed for gr=%i,%i, cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id()); return false; } /** frees a reserved position (caution: railblocks work differently! */ bool haltestelle_t::unreserve_position(grund_t *gr, convoihandle_t cnv) { slist_tpl::iterator i = std::find(tiles.begin(), tiles.end(), gr); if (i != tiles.end()) { if (i->reservation == cnv) { i->reservation = convoihandle_t(); return true; } } DBG_MESSAGE("haltestelle_t::unreserve_position()","failed for gr=%p",gr); return false; } /** can a convoi reserve this position? */ bool haltestelle_t::is_reservable(const grund_t *gr, convoihandle_t cnv) const { for(tile_t const& i : tiles) { if (gr == i.grund) { if (i.reservation == cnv) { DBG_MESSAGE("haltestelle_t::is_reservable()","gr=%d,%d already reserved by cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id()); return true; } // not reserved if (!i.reservation.is_bound()) { // found a stop for this waytype but without object d ... vehicle_t const& v = *cnv->front(); if (gr->hat_weg(v.get_waytype()) && !gr->suche_obj(v.get_typ())) { // not occupied return true; } } return false; } } DBG_MESSAGE("haltestelle_t::reserve_position()","failed for gr=%i,%i, cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id()); return false; } /* deletes factory references so map rotation won't segfault */ void haltestelle_t::release_factory_links() { for(fabrik_t* const f : fab_list) { f->unlink_halt(self); } fab_list.clear(); } /* check if the station given is covered by this station */ bool haltestelle_t::is_halt_covered(const halthandle_t &halt) const { uint16 const cov = welt->get_settings().get_station_coverage(); for(tile_t const& i : halt->get_tiles()) { if( gebaeude_t* const gb = i.grund->find() ) { if ( koord_distance( gb->get_pos().get_2d(), get_next_pos( gb->get_pos().get_2d() )) <= cov ) { return true; } } } return false; } simutrans-124.5/src/simutrans/simhalt.h000066400000000000000000000547521520675460500202270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMHALT_H #define SIMHALT_H #include "convoihandle.h" #include "linehandle.h" #include "halthandle.h" #include "obj/simobj.h" #include "display/simgraph.h" #include "simtypes.h" #include "builder/goods_manager.h" #include "descriptor/goods_desc.h" #include "dataobj/koord.h" #include "player/simplay.h" #include "tpl/inthashtable_tpl.h" #include "tpl/slist_tpl.h" #include "tpl/vector_tpl.h" #include "tpl/array_tpl.h" #define RECONNECTING (1) #define REROUTING (2) #define MAX_HALT_COST 8 // Total number of cost items #define MAX_MONTHS 12 // Max history #define MAX_HALT_NON_MONEY_TYPES 7 // number of non money types in HALT's financial statistic #define HALT_ARRIVED 0 // the amount of ware that arrived here #define HALT_DEPARTED 1 // the amount of ware that has departed from here #define HALT_WAITING 2 // the amount of ware waiting #define HALT_HAPPY 3 // number of happy passengers #define HALT_UNHAPPY 4 // number of unhappy passengers #define HALT_NOROUTE 5 // number of no-route passengers #define HALT_CONVOIS_ARRIVED 6 // number of convois arrived this month #define HALT_WALKED 7 // could walk to destination class cbuffer_t; class grund_t; class fabrik_t; class karte_t; class karte_ptr_t; class koord3d; class loadsave_t; class schedule_t; class player_t; class ware_t; template class bucket_heap_tpl; // -------------------------- Haltestelle ---------------------------- /** * Haltestellen in Simutrans. Diese Klasse managed das Routing und Verladen * von Waren. Eine Haltestelle ist somit auch ein Warenumschlagplatz. * @see stadt_t * @see fabrik_t * @see convoi_t */ class haltestelle_t { public: enum station_flags { NOT_ENABLED = 0, PAX = 1 << 0, POST = 1 << 1, WARE = 1 << 2 }; // can be combined with or! enum stationtyp { invalid = 0, loadingbay = 1 << 0, railstation = 1 << 1, dock = 1 << 2, busstop = 1 << 3, airstop = 1 << 4, monorailstop = 1 << 5, tramstop = 1 << 6, maglevstop = 1 << 7, narrowgaugestop = 1 << 8 }; private: /// List of all halts in the game. static vector_tpl alle_haltestellen; /** * finds a stop by its name */ static stringhashtable_tpl all_names; /** * Finds a stop by coordinate. * only used during loading. */ static inthashtable_tpl *all_koords; vector_tpl* halt_served_this_step; /** * struct holds new financial history for line */ sint64 financial_history[MAX_MONTHS][MAX_HALT_COST]; /** * initialize the financial history */ void init_financial_history(); PIXVAL status_color, last_status_color; uint16 last_permissions; uint16 last_player_count; sint16 last_bar_count; vector_tpl last_bar_height; // caches the last height of the station bar for each good type drawn in display_status(). used for dirty tile management uint32 capacity[3]; // passenger, mail, goods uint8 overcrowded[256/8]; ///< bit field for each goods type (max 256) static uint8 status_step; // NONE or SCHEDULING or REROUTING vector_tpl loading_here; sint32 last_loading_step; koord init_pos; // for halt without grounds, created during game initialisation /** * Handle for ourselves. Can be used like the 'this' pointer */ halthandle_t self; public: /** * List of convois currently loading at this station. * May contain invalid handles! */ const vector_tpl &get_loading_convois() const { return loading_here; } /** * All destinations which received goods in this step. hat_gehalten will exclude them, to load the first convoi for those first */ const vector_tpl *get_halt_served_this_step() const { return halt_served_this_step; } // add convoi to loading queue void request_loading( convoihandle_t cnv ); /* recalculates the station bar */ void recalc_status(); /** * Handles changes of schedules and the resulting re-routing. */ static void step_all(); static uint8 get_rerouting_status() { return status_step; } /** * Resets reconnect_counter. * The next call to step_all() will start complete reconnecting. */ static void reset_routing(); /** * Returns an index to a halt at koord k * by default create a new halt if none found * Only used during loading. */ static halthandle_t get_halt_koord_index(koord k); /* * this will only return something if this stop belongs to same player or is public, or is a dock (when on water and allowed) */ static halthandle_t get_halt(const koord3d pos, const player_t *player, bool allow_water=true ); static const vector_tpl& get_alle_haltestellen() { return alle_haltestellen; } /** * Station factory method. Returns handles instead of pointers. */ static halthandle_t create(koord pos, player_t *player); /** * Station factory method. Returns handles instead of pointers. */ static halthandle_t create(loadsave_t *file); /** * removes a ground tile from a station, deletes the building and, if last tile, also the halthandle */ static bool remove(player_t *player, koord3d pos); /** * Station destruction method. */ static void destroy(halthandle_t); /** * destroys all stations */ static void destroy_all(); /** * List of all tiles (grund_t) that belong to this halt. */ struct tile_t { tile_t(grund_t* grund_) : grund(grund_) {} bool operator ==(const tile_t& o) { return grund == o.grund; } bool operator !=(const tile_t& o) { return grund != o.grund; } grund_t* grund; convoihandle_t reservation; }; const slist_tpl &get_tiles() const { return tiles; } /** * directly reachable halt with its connection weight */ struct connection_t { /// directly reachable halt halthandle_t halt; /// best connection weight to reach this destination uint16 weight:15; /// is halt a transfer halt bool is_transfer:1; connection_t() : weight(0), is_transfer(false) { } connection_t(halthandle_t _halt, uint16 _weight=0) : halt(_halt), weight(_weight), is_transfer(false) { } bool operator == (const connection_t &other) const { return halt == other.halt; } bool operator != (const connection_t &other) const { return halt != other.halt; } static bool compare(const connection_t &a, const connection_t &b) { return a.halt.get_id() < b.halt.get_id(); } }; bool is_transfer(const uint8 catg) const { return all_links[catg].is_transfer; } private: slist_tpl tiles; /** * Stores information about link to cargo network of a certain category */ struct link_t { /// List of all directly reachable halts with their respective connection weights vector_tpl connections; /** * A transfer/interchange is a halt whereby ware can change line or lineless convoy. * Thus, if a halt is served by 2 or more schedules (of lines or lineless convoys) * for a particular ware type, it is a transfer/interchange for that ware type. * Route searching is accelerated by differentiating transfer and non-transfer halts. */ bool is_transfer; /** * Id of connected component in link graph. * Two halts are connected if and only if they belong to the same connected component. * Exception: if value == UNDECIDED_CONNECTED_COMPONENT, then we are in the middle of * recalculating the link graph. * * The id of the component has to be equal to the halt-id of one of its halts. * This ensures that we always have unique component ids. */ uint16 catg_connected_component; # define UNDECIDED_CONNECTED_COMPONENT (0xffff) link_t() { clear(); } void clear() { connections.clear(); is_transfer = false; catg_connected_component = UNDECIDED_CONNECTED_COMPONENT; } }; /// All links to networks of all freight categories, filled by rebuild_connected_components. link_t* all_links; /** * Fills in catg_connected_component values for all halts and all categories. * Uses depth-first search. */ static void rebuild_connected_components(); /** * Helper method: This halt (and all its connected neighbors) belong * to the same component. * Also sets connection_t::is_transfer. * @param catg category of cargo network * @param comp number of component */ void fill_connected_component(uint8 catg, uint16 comp); // Array with different categories that contains all waiting goods at this stop vector_tpl **cargo; /** * Liste der angeschlossenen Fabriken */ slist_tpl fab_list; player_t *owner; static karte_ptr_t welt; /** * What players are allowed to stop here */ uint16 permissions; /** * What is that for a station (for the image) */ stationtyp station_type; // private helper function for recalc_station_type() void add_to_station_type( grund_t *gr ); /** * Reconnect and reroute if counter different from welt->get_schedule_counter() */ static uint8 reconnect_counter; // since we do partial routing, we remember the last offset uint8 last_catg_index; /* station flags (most what enabled) */ uint8 enables; /** * versucht die ware mit beriets wartender ware zusammenzufassen */ bool vereinige_waren(const ware_t &ware); // add the ware to the internal storage, called only internally void add_ware_to_halt(ware_t ware); /** * liefert wartende ware an eine Fabrik */ void liefere_an_fabrik(const ware_t& ware) const; /** * transfers all goods to given station */ void transfer_goods(halthandle_t halt); /** * parameter to force resorting of freight list */ uint8 old_sort_mode; haltestelle_t(loadsave_t *file); haltestelle_t(koord pos, player_t *player); ~haltestelle_t(); public: /** * Called after schedule calculation of all stations is finished * will distribute the goods to changed routes (if there are any) */ void reroute_goods(sint16 &units_remaining); /** * Calculates a status color for status bars */ PIXVAL get_status_farbe() const { return status_color; } /** * Draws some nice colored bars giving some status information */ void display_status(sint16 xpos, sint16 ypos); /** * sucht umliegende, erreichbare fabriken und baut daraus die * Fabrikliste auf. */ void reconnect_factories(); /** * Connects factory to this halt if not already connected and * reachability check for oil rigs passed. * No station coverage checked. * @returns true if succeeded */ bool connect_factory(fabrik_t *fab); void remove_fabriken(fabrik_t *fab); /** * registers all convois using the current permissions (full check also removes) * @return true if there was a change */ bool rebuilt_schedule_registration( bool remove_check, bool add_check ); /** * Rebuilds the list of connections to reachable halts * returns the search number of connections */ sint32 rebuild_connections(); /** * Rebuilds connections of all halts connected to this halt. * Prepares deletion of this halt without losing connections and routed freight. */ void rebuild_linked_connections(); static uint8 get_reconnect_counter() { return reconnect_counter; } void rotate90( const sint16 y_size ); player_t *get_owner() const {return owner;} // just for info so far sint64 calc_maintenance() const; void merge_halt( halthandle_t halt_to_join ); void change_owner( player_t *player ); vector_tpl const& get_pax_connections() const { return all_links[goods_manager_t::INDEX_PAS].connections; } vector_tpl const& get_mail_connections() const { return all_links[goods_manager_t::INDEX_MAIL].connections; } // returns the matching warenziele (goods objectives/destinations) vector_tpl const& get_connections(uint8 const catg_index) const { return all_links[catg_index].connections; } /** * Checks if there is connection for certain freight to the other halt. * @param halt the other halt * @param catg_index freight category index * @return 0 - not connected, 1 - connected, -1 - undecided (call again later...) */ sint8 is_connected(halthandle_t halt, uint8 catg_index) const; bool has_available_network( const player_t* player, uint8 catg_index = goods_manager_t::INDEX_NONE ) const; const slist_tpl& get_fab_list() const { return fab_list; } /** * called regularly to update status and reroute stuff */ bool step(uint8 what, sint16 &units_remaining); /** * Called every month/every 24 game hours */ void new_month(); private: /* Node used during route search */ struct route_node_t { halthandle_t halt; uint16 aggregate_weight; route_node_t() : aggregate_weight(0) {} route_node_t(halthandle_t h, uint16 w) : halt(h), aggregate_weight(w) {} // dereferencing to be used in binary_heap_tpl inline uint16 operator * () const { return aggregate_weight; } }; // open_list needs access to route_node_t template friend class bucket_heap_tpl; /* Extra data for route search */ struct halt_data_t { // transfer halt: // in static function search_route(): previous transfer halt (to track back route) // in member function search_route_resumable(): first transfer halt to get there halthandle_t transfer; uint16 best_weight; uint16 depth:14; bool destination:1; bool overcrowded:1; }; // store the best weight so far for a halt, and indicate whether it is a destination static halt_data_t halt_data[65536]; // for efficient retrieval of the node with the smallest weight static bucket_heap_tpl open_list; /** * Markers used in route searching to avoid processing the same halt more than once */ static uint8 markers[65536]; static uint8 current_marker; /** * Remember last route search start and catg to resume search */ static halthandle_t last_search_origin; static uint8 last_search_ware_catg_idx; public: enum routing_result_flags { NO_ROUTE = 0, ROUTE_OK = 1, ROUTE_WALK = 2, ROUTE_OVERCROWDED = 8 }; /** * Kann die Ware nicht zum Ziel geroutet werden (keine Route), dann werden * Ziel und Zwischenziel auf koord::invalid gesetzt. * * @param ware die zu routende Ware * * for reverse routing, also the next to last stop can be added, if next_to_ziel!=NULL * * if avoid_overcrowding is set, a valid route in only found when there is no overflowing stop in between */ static int search_route( const halthandle_t *const start_halts, const uint16 start_halt_count, const bool no_routing_over_overcrowding, ware_t &ware, ware_t *const return_ware=NULL ); /** * A separate version of route searching code for re-calculating routes * Search is resumable, that is if called for the same halt and same goods category * it reuses search history from last search * It is faster than calling the above version on each packet, and is used for re-routing packets from the same halt. */ void search_route_resumable( ware_t &ware ); bool get_pax_enabled() const { return enables & PAX; } bool get_mail_enabled() const { return enables & POST; } bool get_ware_enabled() const { return enables & WARE; } // check, if we accepts this good // often called, thus inline ... bool is_enabled( const goods_desc_t *wtyp ) const { return is_enabled(wtyp->get_catg_index()); } // a separate version for checking with goods category index bool is_enabled( const uint8 catg_index ) const { if (catg_index == goods_manager_t::INDEX_PAS) { return enables&PAX; } else if(catg_index == goods_manager_t::INDEX_MAIL) { return enables&POST; } return enables&WARE; } /** * Found route and station uncrowded */ void add_pax_happy(int n); /** * Station in walking distance */ void add_pax_walked(int n); /** * Found no route */ void add_pax_no_route(int n); /** * Station crowded */ void add_pax_unhappy(int n); int get_pax_happy() const { return (int)financial_history[0][HALT_HAPPY]; } int get_pax_no_route() const { return (int)financial_history[0][HALT_NOROUTE]; } int get_pax_unhappy() const { return (int)financial_history[0][HALT_UNHAPPY]; } /** * Add tile to list of station tiles. * * @param gb * @param relink_factories if true call verbinde_fabriken, if not true take care of factory connections yourself */ bool add_grund(grund_t *gb, bool relink_factories = true); bool rem_grund(grund_t *gb); uint32 get_capacity(uint8 typ) const { return capacity[typ]; } bool existiert_in_welt() const; koord get_init_pos() const { return init_pos; } koord get_basis_pos() const; koord3d get_basis_pos3d() const; public: void recalc_basis_pos(); // returns ground closest to this coordinate (and may exclude tunnel tiles) grund_t* get_ground_closest_to(const koord here) const; // returns ground closest to this coordinate (and may exclude tunnel tiles) grund_t* get_ground_closest_to(const koord3d here, bool use_above = false) const; /** * return the closest square that belongs to this halt */ koord get_next_pos( koord start ) const; /// true, if this station is overcrowded for this ware bool is_overcrowded( const uint8 idx ) const { return (overcrowded[idx/8] & (1<<(idx%8)))!=0; } /// @returns total amount of the good waiting at this halt. uint32 get_ware_summe(const goods_desc_t *warentyp) const; /** * returns total number for a certain position (since more than one factory might connect to a stop) */ uint32 get_ware_fuer_zielpos(const goods_desc_t *warentyp, const koord zielpos) const; /** * total amount of freight with specified next hop */ uint32 get_ware_fuer_zwischenziel(const goods_desc_t *warentyp, const halthandle_t zwischenziel) const; /// true, if we accept/deliver this kind of good bool gibt_ab(const goods_desc_t *warentyp) const { return cargo[warentyp->get_catg_index()] != NULL; } /** * retrieves a ware packet for any destination in the list * needed, if the factory in question wants to remove something */ bool recall_ware( ware_t& w, uint32 menge ); /** * Fetches goods from this halt * * @param load Output parameter. Goods will be put into this list, the vehicle has to load them. * @param good_category Specifies the kind of good (or compatible goods) we are requesting to fetch from this stop. * @param requested_amount How many units of the cargo we can fetch. * @param destination_halts */ void fetch_goods( slist_tpl &load, const goods_desc_t *good_category, uint32 requested_amount, const vector_tpl& destination_halts); /** * Delivers goods (ware_t) to this halt. * if no route is found, the good will be removed. * @returns amount of goods */ uint32 liefere_an(ware_t ware); /** * Delivers goods (ware_t) to this halt. * Will not recalculate the route * This is used for inital passenger, since they already know a route * @returns amount of goods */ uint32 starte_mit_route(ware_t ware); const grund_t *find_matching_position(waytype_t wt) const; /** * checks, if there is an unoccupied loading bay for this kind of thing */ bool find_free_position(const waytype_t w ,convoihandle_t cnv,const obj_t::typ d) const; /** * reserves a position (caution: railblocks work differently! */ bool reserve_position(grund_t *gr,convoihandle_t cnv); /** * frees a reserved position (caution: railblocks work differently! */ bool unreserve_position(grund_t *gr, convoihandle_t cnv); /** true, if this can be reserved */ bool is_reservable(const grund_t *gr, convoihandle_t cnv) const; /** * @param[out] buf Goods description text */ void get_freight_info(cbuffer_t & buf); void reset_freight_info() { old_sort_mode = 255; } /** * @param[out] buf short list of the waiting goods (i.e. 110 Wood, 15 Coal) */ void get_short_freight_info(cbuffer_t & buf) const; /** * Opens an information window for this station. */ void open_info_window(); /** * @return the type of a station * (combination of: railstation, loading bay, dock) */ stationtyp get_station_type() const { return station_type; } void recalc_station_type(); /** * fragt den namen der Haltestelle ab. * Der Name ist der text des ersten Untergrundes der Haltestelle * @return der Name der Haltestelle. */ const char *get_name() const; void set_name(const char *name); /** * will crash on NULL pointer, but no unowned convois ever ... * @return true if player is allowed to stop here */ inline bool can_use_halt(const player_t* player) const { return !player || (permissions & (1 << player->get_player_nr()))!=0; } /** * Sets permissions * Owner and public service are always allowed */ void set_permissions(uint16 perms); uint16 get_permissions() const { return permissions; } // create an unique name: better to be called with valid handle, although it will work without char* create_name(koord k, char const* typ); void rdwr(loadsave_t *file); void finish_rd(); /** * Called before savegame will be loaded. * Creates all_koords table. */ static void start_load_game(); /** * Called after loading of savegame almost finished, * i.e. after finish_rd is finished. * Deletes all_koords table. */ static void end_load_game(); /** * called, if a line serves this stop */ void add_line(linehandle_t line) { registered_lines.append_unique(line); } /** * called, if a line removes this stop from it's schedule */ void remove_line(linehandle_t line) { registered_lines.remove(line); } /** * list of line ids that serve this stop */ vector_tpl registered_lines; /** * Register a lineless convoy which serves this stop */ void add_convoy(convoihandle_t convoy) { registered_convoys.append_unique(convoy); } /** * Unregister a lineless convoy */ void remove_convoy(convoihandle_t convoy) { registered_convoys.remove(convoy); } /** * A list of lineless convoys serving this stop */ vector_tpl registered_convoys; /** * book a certain amount into the halt's financial history */ void book(sint64 amount, int cost_type); /** * return a pointer to the financial history */ sint64* get_finance_history() { return *financial_history; } /** * return a specified element from the financial history */ sint64 get_finance_history(int month, int cost_type) const { return financial_history[month][cost_type]; } /** marks a coverage area */ void mark_unmark_coverage(const bool mark) const; /* * deletes factory references so map rotation won't segfault */ void release_factory_links(); /** * Initialise the markers to zero */ static void init_markers(); /* * check if it is in the station coverage */ bool is_halt_covered (const halthandle_t &halt) const; }; ENUM_BITSET(haltestelle_t::stationtyp) #endif simutrans-124.5/src/simutrans/siminteraction.cc000066400000000000000000000303051520675460500217400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "siminteraction.h" #include "simversion.h" #include "dataobj/environment.h" #include "gui/gui_frame.h" #include "gui/help_frame.h" #include "network/network_cmd_ingame.h" #include "dataobj/scenario.h" #include "simevent.h" #include "utils/simrandom.h" #include "tool/simmenu.h" #include "player/simplay.h" #include "simsound.h" #include "sys/simsys.h" #include "simticker.h" #include "gui/simwin.h" #include "world/simworld.h" #include "descriptor/sound_desc.h" #include "obj/zeiger.h" #include "display/viewport.h" karte_ptr_t interaction_t::world; void interaction_t::move_view( const event_t &ev ) { koord new_ij = viewport->get_world_position(); sint16 new_xoff = viewport->get_x_off() - (ev.mouse_pos.x - ev.click_pos.x) * env_t::scroll_multi; sint16 new_yoff = viewport->get_y_off() - (ev.mouse_pos.y - ev.click_pos.y) * env_t::scroll_multi; // this sets the new position and mark screen dirty // => with next refresh we will be at a new location viewport->change_world_position( new_ij, new_xoff, new_yoff ); // move the mouse pointer back to starting location => infinite mouse movement if ((ev.mouse_pos.x - ev.click_pos.x) != 0 || (ev.mouse_pos.y - ev.click_pos.y) != 0) { if(!env_t::scroll_infinite || !move_pointer(ev.click_pos.x, ev.click_pos.y)) { // fails in finger mode => we have to reset start ourselves change_drag_start(ev.mouse_pos - ev.click_pos); } } } void interaction_t::move_cursor( const event_t &ev ) { zeiger_t *zeiger = world->get_zeiger(); if(! zeiger ) { // No cursor to move, exit return; } static int mb_alt=0; tool_t *tool = world->get_tool(world->get_active_player_nr()); const koord3d pos = viewport->get_new_cursor_position(scr_coord(ev.mouse_pos.x,ev.mouse_pos.y), tool->is_grid_tool()); if( pos == koord3d::invalid ) { zeiger->change_pos(pos); return; } // move cursor const koord3d prev_pos = zeiger->get_pos(); if( (prev_pos != pos || ev.mouse_button_state != mb_alt) ) { mb_alt = ev.mouse_button_state; zeiger->change_pos(pos); if (!tool->move_has_effects()) { is_dragging = false; } else { tool->flags = (event_get_last_control_shift() ^ tool_t::control_invert) | tool_t::WFL_LOCAL; if(tool->check_pos( world->get_active_player(), zeiger->get_pos() )==NULL) { if( ev.mouse_button_state == 0 ) { is_dragging = false; } else if(ev.ev_class==EVENT_DRAG) { if(!is_dragging && prev_pos != koord3d::invalid && tool->check_pos( world->get_active_player(), prev_pos )==NULL) { is_dragging = true; } } if (is_dragging) { world->set_deferred_move_to(pos, tool->flags); } } tool->flags = 0; } if( (ev.mouse_button_state&7)==0 ) { // time, since mouse got here world->set_mouse_rest_time(dr_time()); world->set_sound_wait_time(AMBIENT_SOUND_INTERVALL); // 13s no movement: play sound } } } void interaction_t::interactive_event( const event_t &ev ) { if (IS_KEYDOWN(&ev)) { DBG_MESSAGE("interaction_t::interactive_event()", "Key-down event with code %d '%c'", ev.ev_code, (ev.ev_code>=32 && ev.ev_code<=126) ? ev.ev_code : '?' ); switch(ev.ev_code) { // cursor movements case SIM_KEYCODE_UPRIGHT: viewport->change_world_position(viewport->get_world_position() + koord::north); world->set_dirty(); break; case SIM_KEYCODE_DOWNLEFT: viewport->change_world_position(viewport->get_world_position() + koord::south); world->set_dirty(); break; case SIM_KEYCODE_UPLEFT: viewport->change_world_position(viewport->get_world_position() + koord::west); world->set_dirty(); break; case SIM_KEYCODE_DOWNRIGHT: viewport->change_world_position(viewport->get_world_position() + koord::east); world->set_dirty(); break; case SIM_KEYCODE_RIGHT: viewport->change_world_position(viewport->get_world_position() + koord(+1,-1)); world->set_dirty(); break; case SIM_KEYCODE_DOWN: viewport->change_world_position(viewport->get_world_position() + koord(+1,+1)); world->set_dirty(); break; case SIM_KEYCODE_UP: viewport->change_world_position(viewport->get_world_position() + koord(-1,-1)); world->set_dirty(); break; case SIM_KEYCODE_LEFT: viewport->change_world_position(viewport->get_world_position() + koord(-1,+1)); world->set_dirty(); break; // closing windows case 27: case 127: if( !IS_CONTROL_PRESSED( &ev ) && !IS_SHIFT_PRESSED( &ev ) ) { // close topmost win destroy_win( win_get_top() ); } break; case SIM_KEYCODE_F1: if( gui_frame_t *win = win_get_top() ) { if( const char *helpfile = win->get_help_filename() ) { help_frame_t::open_help_on( helpfile ); break; } } world->set_tool( tool_t::dialog_tool[DIALOG_HELP], world->get_active_player() ); break; // just ignore the key case 0: break; // distinguish between backspace and ctrl-H (both keycode==8), and enter and ctrl-M (both keycode==13) case 8: case 13: if( !IS_CONTROL_PRESSED(&ev) && !IS_SHIFT_PRESSED(&ev) ) { // Control is _not_ pressed => Backspace or Enter pressed. if( ev.ev_code == 8 ) { // Backspace sound_play(SFX_SELECT,255,TOOL_SOUND); destroy_all_win(false); } // Ignore Enter and Backspace but not Ctrl-H and Ctrl-M break; } /* FALLTHROUGH */ default: { bool ok=false; for(tool_t* const i : tool_t::char_to_tool) { if( i->command_key == ev.ev_code ) { if( i->command_flags == 0 || (ev.ev_key_mod & (SIM_KEYMOD_SHIFT|SIM_KEYMOD_CTRL)) == i->command_flags ) { world->set_tool(i, world->get_active_player()); ok = true; break; } } } #ifdef STEAM_BUILT // Block F12 from bringing up Keyboard Help (for Steam Screenshot) - but still allow F12 to be used if defined in pakset if (ev.ev_code==SIM_KEYCODE_F12) { ok=true; } #endif if(!ok) { help_frame_t::open_help_on( "keys.txt" ); } } break; } } if( !is_dragging && IS_LEFTRELEASE(&ev) && ev.mouse_pos.y < gfx->get_screen_size().h -16 -(TICKER_HEIGHT*ticker::empty()) ) { DBG_MESSAGE("interaction_t::interactive_event(event_t &ev)", "calling a tool"); koord3d pos = world->get_zeiger()->get_pos(); if(world->is_within_grid_limits(pos.get_2d())) { bool suspended = false; // true if execution was suspended, i.e. sent to server tool_t *tool = world->get_tool(world->get_active_player_nr()); player_t *player = world->get_active_player(); tool->flags = event_get_last_control_shift() ^ tool_t::control_invert; // first check for visibility etc (needs already right flags) const char *err = tool->check_pos( player, pos ); if (err==NULL) { err = world->call_work_api(tool, player, pos, suspended); } if (!suspended) { // play sound / error message world->get_active_player()->tell_tool_result(tool, pos, err); // Check if we need to update pointer(zeiger) position. if( err == NULL && tool->update_pos_after_use() ) { // Cursor might need movement (screen has changed, we get a new one under the mouse pointer) const koord3d pos_new = viewport->get_new_cursor_position(scr_coord(ev.mouse_pos.x,ev.mouse_pos.y), tool->is_grid_tool()); world->get_zeiger()->set_pos(pos_new); } } tool->flags = 0; } } // mouse wheel scrolled -> rezoom if (ev.ev_class == EVENT_CLICK) { // first, we need to check cursor is valid, won't zoom otherwise const koord3d cursor_pos = world->get_zeiger()->get_pos(); if( cursor_pos == koord3d::invalid) { //ignore event return; } bool zoom_successful = false; // store old screen position of centered tile scr_coord s = viewport->get_screen_coord(cursor_pos, koord(0,0)); if(ev.ev_code==MOUSE_WHEELUP) { if(win_change_zoom_factor(true)) { zoom_successful = true; } } else if(ev.ev_code==MOUSE_WHEELDOWN) { if(win_change_zoom_factor(false)) { zoom_successful = true; } } // zoom can fail if we are max zoomed in/out, so: if (zoom_successful) { // calculate offsets such that tile under cursor is still on the same screen position viewport->change_world_position(cursor_pos, koord(0,0), s); //and move cursor to the new position under the mouse move_cursor(ev); world->set_dirty(); } } } bool interaction_t::process_event( event_t &ev ) { if(ev.ev_class==EVENT_SYSTEM && ev.ev_code==SYSTEM_QUIT) { // since we run in a sync_step, quitting may be needed to be delagated to a tool if( (LOAD_RANDOM | MAP_CREATE_RANDOM | MODAL_RANDOM) & get_random_mode() ) { // next sync step would take tool long world->stop(true); } else { // we call the proper tool for quitting world->set_tool(tool_t::simple_tool[TOOL_QUIT], NULL); } return true; } if(ev.ev_class==IGNORE_EVENT) { // ignore it return false; } DBG_DEBUG4("interaction_t::process_event", "calling check_pos_win"); if(check_pos_win(&ev,false)){ // The event is shallowed by the GUI, next. return false; } DBG_DEBUG4("interaction_t::process_event", "after check_pos_win"); // Handle map drag with right-click if(IS_RIGHTCLICK(&ev)) { gfx->set_cursor_visible(false); } else if(IS_RIGHTRELEASE(&ev)) { gfx->set_cursor_visible(true); } else if(IS_RIGHTDRAG(&ev)) { // unset following world->get_viewport()->set_follow_convoi( convoihandle_t() ); catch_dragging(); move_view(ev); } else if( IS_LEFTDRAG(&ev) && IS_LEFT_BUTTON_PRESSED(&ev) && (is_world_dragging || (!world->get_tool(world->get_active_player_nr())->move_has_effects() && !IS_CONTROL_PRESSED(&ev)) ) ) { /* ok, we have a general tool selected, and we have a left drag or left release event with an actual difference * => move the map, if we are beyond a threshold */ if( is_world_dragging || abs(ev.click_pos.x-ev.mouse_pos.x)+abs(ev.click_pos.y-ev.mouse_pos.y)>=max(1,(env_t::scroll_threshold * gfx->get_tile_raster_width()) / gfx->get_base_tile_raster_width()) ) { if (!is_world_dragging) { gfx->set_cursor_visible(false); is_world_dragging = true; } world->get_viewport()->set_follow_convoi(convoihandle_t()); catch_dragging(); move_view(ev); ev.ev_code = IGNORE_EVENT; } } if( !IS_LEFT_BUTTON_PRESSED(&ev) && is_world_dragging ) { // show the mouse and swallow this event if we were dragging before ev.ev_code = IGNORE_EVENT; gfx->set_cursor_visible(true); is_world_dragging = false; } DBG_DEBUG4("interaction_t::process_event", "check if cursor needs movement"); if( (ev.ev_class==EVENT_DRAG && ev.ev_code==MOUSE_LEFTBUTTON) || (ev.mouse_button_state==0 && ev.ev_class==EVENT_MOVE) || ev.ev_class==EVENT_RELEASE) { move_cursor(ev); } DBG_DEBUG4("interaction_t::process_event", "calling interactive_event"); interactive_event(ev); DBG_DEBUG4("interaction_t::process_event", "end of event handling"); return false; } void interaction_t::check_events() { event_t ev; win_poll_event(&ev); event_t deferred_ev; deferred_ev.ev_class = EVENT_NONE; for( int swallow_dummies=0; swallow_dummies<4; swallow_dummies++ ) { /* there are always dummy events in the queue, usually less than 4 * => retry a few times until we get a useful event * Otherwise very laggy response at low fps */ if (ev.ev_class != EVENT_NONE) { DBG_DEBUG4("interaction_t::check_events", "event %d after %d dummies", ev.ev_class, swallow_dummies); swallow_dummies = 0; if (ev.ev_class == EVENT_DRAG) { // defer processing, since there might be many triggered at once // Otherwise mark tiles could be alled twice duing one step deferred_ev = ev; } else { // still one drag left in queue? if (deferred_ev.ev_class == EVENT_DRAG) { // do this first process_event(deferred_ev); deferred_ev.ev_class = EVENT_NONE; } if (process_event(ev)) { // We have been asked to stop processing, exit. return; } } } win_poll_event(&ev); } if (deferred_ev.ev_class == EVENT_DRAG) { // process pending drag events process_event(deferred_ev); deferred_ev.ev_class = EVENT_NONE; } } interaction_t::interaction_t(viewport_t *viewport) : viewport(viewport), is_dragging(false), is_world_dragging(false) { assert(viewport); } simutrans-124.5/src/simutrans/siminteraction.h000066400000000000000000000025371520675460500216100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMINTERACTION_H #define SIMINTERACTION_H class karte_ptr_t; class viewport_t; struct event_t; /** * User interaction class, it collects and processes all user and system events from the OS. * @brief Event manager of the game. * @see karte_t::interactive */ class interaction_t { private: /** * The associated world. */ static karte_ptr_t world; /** * Associated viewport of the world, this is a cached value. * @note Since a world in our project just has one viewport, we cache its value. */ viewport_t *viewport; bool is_dragging; bool is_world_dragging; private: /** * Processes a mouse event that's moving the camera. */ void move_view(const event_t &ev); /** * Processes a cursor movement event, related to the tool pointer in-map. * @see zeiger_t */ void move_cursor(const event_t &ev); /** * Processes a user event on the map, like a keyclick, or a mouse event. */ void interactive_event(const event_t &ev); /** * Processes a single event. * @return true if we need to stop processing events. */ bool process_event(event_t &ev); public: /** * Processes all the pending system events. * @brief Message Pump */ void check_events(); explicit interaction_t(viewport_t *viewport); }; #endif simutrans-124.5/src/simutrans/simintr.cc000066400000000000000000000164541520675460500204060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "simdebug.h" #include "sys/simsys.h" #include "simintr.h" #include "gui/simwin.h" #include "player/simplay.h" #include "player/finance.h" #include "dataobj/environment.h" #include "dataobj/translator.h" #include "world/simworld.h" #include "display/simview.h" #include "ground/wasser.h" static main_view_t *main_view = NULL; static uint32 last_time; static bool enabled = false; #define FRAME_TIME_MULTI (16) // pause between two frames static uint32 frame_time = 36*FRAME_TIME_MULTI; bool reduce_frame_time() { if( frame_time > 150*FRAME_TIME_MULTI ) { // < ~6.6fps frame_time -= 8; return true; } else if(frame_time > (FRAME_TIME_MULTI*1000)/env_t::max_fps) { frame_time -= 1; return true; } else { frame_time = (FRAME_TIME_MULTI*1000)/env_t::max_fps; return false; } } bool increase_frame_time() { if(frame_time > (FRAME_TIME_MULTI*1000)/5) { // < 5 fps return false; } else { frame_time ++; return true; } } uint32 get_frame_time() { return frame_time/FRAME_TIME_MULTI; } void set_frame_time(uint32 time) { frame_time = clamp( time, 1000/env_t::max_fps, 1000/env_t::min_fps )*FRAME_TIME_MULTI; } void intr_refresh_display(bool dirty) { #if COLOUR_DEPTH != 0 wasser_t::prepare_for_refresh(); dr_prepare_flush(); main_view->display( dirty ); if( env_t::player_finance_display_account ) { win_display_flush( (double)world()->get_active_player()->get_finance()->get_account_balance()/100.0 ); } else { win_display_flush( (double)world()->get_active_player()->get_finance()->get_netwealth()/100.0 ); } // with a switch statement more types could be supported ... dr_flush(); #else (void)dirty; #endif } // debug version with caller information void interrupt_check(const char* caller_info) { DBG_DEBUG4("interrupt_check", "called from (%s)", caller_info); (void)caller_info; if( !enabled ) { return; } static uint32 last_ms = 0; if( !world()->is_fast_forward() || world()->get_ticks() != last_ms ) { const uint32 now = dr_time(); if((now-last_time)*FRAME_TIME_MULTI < frame_time) { return; } const sint32 diff = (( (sint32)now - (sint32)last_time)*world()->get_time_multiplier())/16; if( diff>0 ) { enabled = false; last_time = now; if (!world()->is_fast_forward()) { world()->sync_step( diff ); } world()->display(diff); // since pause maz have been activated in this sync_step enabled = !world()->is_paused(); } } last_ms = world()->get_ticks(); } void intr_set_view(main_view_t *view) { main_view = view; last_time = dr_time(); enabled = true; } void intr_set_last_time(uint32 time) { last_time = time; } void intr_disable() { enabled = false; } void intr_enable() { enabled = true; } char const *tick_to_string( uint32 ticks, bool only_DDMMHHMM ) { static sint32 tage_per_month[12]={31,28,31,30,31,30,31,31,30,31,30,31}; static char const* const seasons[] = { "q2", "q3", "q4", "q1" }; static char time [128]; time[0] = 0; if (world()== NULL) { return time; } sint32 month = world()->get_last_month(); sint32 year = world()->get_last_year(); // calculate right month first const uint32 ticks_this_month = ticks % world()->ticks_per_world_month; month += ticks_this_month / world()->ticks_per_world_month; while( month>11 ) { month -= 12; year ++; } while( month<0 ) { month += 12; year --; } uint32 tage, hours, minuten; if (env_t::show_month > env_t::DATE_FMT_MONTH) { tage = (((sint64)ticks_this_month*tage_per_month[month]) >> world()->ticks_per_world_month_shift) + 1; hours = (((sint64)ticks_this_month*tage_per_month[month]) >> (world()->ticks_per_world_month_shift-16)); minuten = (((hours*3) % 8192)*60)/8192; hours = ((hours*3) / 8192)%24; } else { tage = 0; hours = (ticks_this_month * 24) >> world()->ticks_per_world_month_shift; minuten = ((ticks_this_month * 24 * 60) >> world()->ticks_per_world_month_shift)%60; } //DBG_MESSAGE("env_t::show_month","%d",env_t::show_month); // since seasons 0 is always summer for backward compatibility char const* const date = only_DDMMHHMM ? translator::get_day_date(tage) : translator::get_date(year, month, tage, translator::translate(seasons[world()->get_season()])); switch (env_t::show_month) { case env_t::DATE_FMT_US: case env_t::DATE_FMT_US_NO_SEASON: { uint32 hours_ = hours % 12; if (hours_ == 0) hours_ = 12; sprintf(time, "%s%2d:%02d%s", date, hours_, minuten, hours < 12 ? "am" : "pm"); break; } case env_t::DATE_FMT_SEASON: sprintf(time, "%s", date); break; default: sprintf(time, "%s%2d:%02dh", date, hours, minuten); break; } return time; } char const *difftick_to_string( sint32 ticks, bool round_to_quaters ) { static char time [128]; time[0] = 0; // World model might not be initalized if this is called while reading saved windows. if ( world() == NULL) { return time; } // suppress as much as possible, assuming this is an relative offset to the current month sint32 num_days = ( ticks * (env_t::show_month==env_t::DATE_FMT_MONTH? 1 : 31) ) >> world()->ticks_per_world_month_shift; char days[64]; days[0] = 0; if( num_days!=0 ) { sprintf( days, "%+i ", num_days ); } uint32 hours, minuten; if( env_t::show_month > env_t::DATE_FMT_MONTH ) { if( world()->ticks_per_world_month_shift>=16 ) { hours = (((sint64)ticks*31) >> (world()->ticks_per_world_month_shift-16)); } else { hours = (((sint64)ticks*31) << (16-world()->ticks_per_world_month_shift)); } } else { if( world()->ticks_per_world_month_shift>=16 ) { uint32 precision = round_to_quaters ? 0 : 15; hours = (ticks >> (world()->ticks_per_world_month_shift-16)) + precision; } else { uint32 precision = round_to_quaters ? 0 : 15 >> (16-world()->ticks_per_world_month_shift); hours = (((sint64)ticks) << (16-world()->ticks_per_world_month_shift)) + precision; } } minuten = (((hours * 3) % 8192) * 60) / 8192; hours = ((hours * 3) / 8192) % 24; // maybe round minutes if( round_to_quaters ) { int switchtick = world()->ticks_per_world_month_shift; if( env_t::show_month == env_t::DATE_FMT_MONTH ) { // since a month is then just three days instead of about 30 ... switchtick += 3; } if( switchtick <= 19 ) { minuten = ( (minuten + 30 ) / 60 ) * 60; hours += minuten /60; if( switchtick < 18 ) { // four hour intervals hours = (hours + 3 ) & 0xFFFFC; } else if( switchtick == 18 ) { // two hour intervals hours = (hours + 1 ) & 0xFFFFE; } } else if( switchtick == 20 ) { minuten = ( (minuten + 15) / 30 ) * 30; } else if( switchtick == 21 ) { minuten = ( (minuten + 7) / 15 ) * 15; } else if( switchtick == 22 ) { minuten = ( (minuten + 2) / 5 ) * 5; } } // take care of overflow hours += (minuten / 60); minuten %= 60; hours %= 24; switch(env_t::show_month) { case env_t::DATE_FMT_GERMAN: case env_t::DATE_FMT_GERMAN_NO_SEASON: case env_t::DATE_FMT_US: case env_t::DATE_FMT_US_NO_SEASON: sprintf(time, "%s%2d:%02dh", days, hours, minuten); break; case env_t::DATE_FMT_JAPANESE: case env_t::DATE_FMT_JAPANESE_NO_SEASON: sprintf(time, "%s%2d:%02dh", days, hours, minuten); break; case env_t::DATE_FMT_MONTH: sprintf(time, "%s%2d:%02dh", days, hours, minuten); break; } return time; } simutrans-124.5/src/simutrans/simintr.h000066400000000000000000000026661520675460500202500ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMINTR_H #define SIMINTR_H #include "macros.h" class karte_t; class main_view_t; /// Try to increase fps bool reduce_frame_time(); /// Try to decrease fps bool increase_frame_time(); uint32 get_frame_time(); void set_frame_time(uint32 ms); void intr_refresh_display(bool dirty); void intr_set_view(main_view_t *view); void intr_set_last_time(uint32 time); void intr_enable(); void intr_disable(); void interrupt_check(const char* caller_info = "0"); #if COLOUR_DEPTH == 0 && defined PROFILE // 0 bit graphic + profiling: no interrupt_check. #define INT_CHECK(info) #else #ifndef DEBUG // standard version #define INT_CHECK(info) do { interrupt_check(); } while(false) #else // debug version #define INT_CHECK(info) do { interrupt_check( __FILE__ ":" QUOTEME(__LINE__) ); } while(false) #endif #endif // returns a time string in the desired format // Returns an empty string if called before the world model defining time is initalized. // reutrns a shorter date (without season and year if second parameter true) char const *tick_to_string( uint32 ticks, bool omit_season_year=false ); // returns a time difference string in the desired format // assume the month has 31 days // round will reduce presion to qrater or less (depending on month length) char const *difftick_to_string( sint32 ticks, bool round ); #endif simutrans-124.5/src/simutrans/simio.cc000066400000000000000000000004111520675460500200230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include char *read_line(char *s, int size, FILE *file) { char *r; do { r = fgets(s, size, file); } while(r != NULL && *s == '#'); return r; } simutrans-124.5/src/simutrans/simio.h000066400000000000000000000004461520675460500176750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMIO_H #define SIMIO_H #include /** * Reads a line from a file. Skips lines starting with # * @see fgets */ char *read_line(char *s, int size, FILE *stream); #endif simutrans-124.5/src/simutrans/simline.cc000066400000000000000000000323071520675460500203540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simtypes.h" #include "simline.h" #include "simhalt.h" #include "world/simworld.h" #include "utils/simstring.h" #include "dataobj/schedule.h" #include "dataobj/translator.h" #include "dataobj/loadsave.h" #include "gui/gui_theme.h" #include "player/simplay.h" #include "player/finance.h" // convert_money #include "vehicle/vehicle.h" #include "simconvoi.h" #include "convoihandle.h" #include "simlinemgmt.h" #include "gui/simwin.h" #include "gui/gui_frame.h" uint8 convoi_to_line_catgory_[convoi_t::MAX_CONVOI_COST] = { LINE_CAPACITY, LINE_TRANSPORTED_GOODS, LINE_REVENUE, LINE_OPERATIONS, LINE_PROFIT, LINE_DISTANCE, LINE_MAXSPEED, LINE_WAYTOLL }; uint8 simline_t::convoi_to_line_catgory(uint8 cnv_cost) { assert(cnv_cost < convoi_t::MAX_CONVOI_COST); return convoi_to_line_catgory_[cnv_cost]; } karte_ptr_t simline_t::welt; simline_t::simline_t(player_t* player, linetype type) { self = linehandle_t(this); char printname[128]; sprintf(printname, "(%i) %s", self.get_id(), translator::translate("Line", welt->get_settings().get_name_language_id())); name = printname; init_financial_history(); this->type = type; this->schedule = NULL; this->player = player; withdraw = false; state_color = SYSCOL_TEXT; create_schedule(); } simline_t::simline_t(player_t* player, linetype type, loadsave_t *file) { // id will be read and assigned during rdwr self = linehandle_t(); this->type = type; this->schedule = NULL; this->player = player; withdraw = false; create_schedule(); rdwr(file); // now self has the right id but the this-pointer is not assigned to the quickstone handle yet // do this explicitly // some savegames have line_id=0, resolve that in finish_rd if (self.get_id()!=0) { self = linehandle_t(this, self.get_id()); } } simline_t::~simline_t() { DBG_DEBUG("simline_t::~simline_t()", "deleting schedule=%p", schedule); assert(count_convoys()==0); unregister_stops(); delete schedule; self.detach(); DBG_MESSAGE("simline_t::~simline_t()", "line %d (%p) destroyed", self.get_id(), this); } simline_t::linetype simline_t::waytype_to_linetype(const waytype_t wt) { switch (wt) { case road_wt: return simline_t::truckline; case track_wt: return simline_t::trainline; case water_wt: return simline_t::shipline; case monorail_wt: return simline_t::monorailline; case maglev_wt: return simline_t::maglevline; case tram_wt: return simline_t::tramline; case narrowgauge_wt: return simline_t::narrowgaugeline; case air_wt: return simline_t::airline; default: return simline_t::line; } } const char *simline_t::get_linetype_name(const simline_t::linetype lt) { static const char *lt2name[MAX_LINE_TYPE] = {"All", "Truck", "Train", "Ship", "Aircraft", "Monorail", "Tram", "Maglev", "Narrowgauge" }; return translator::translate( lt2name[lt] ); } waytype_t simline_t::linetype_to_waytype(const linetype lt) { static const waytype_t wt2lt[MAX_LINE_TYPE] = { invalid_wt, road_wt, track_wt, water_wt, air_wt, monorail_wt, tram_wt, maglev_wt, narrowgauge_wt }; return wt2lt[lt]; } void simline_t::set_schedule(schedule_t* schedule) { if (this->schedule) { unregister_stops(); delete this->schedule; } this->schedule = schedule; } void simline_t::set_name(const char *new_name) { name = new_name; /// Update window title if window is open gui_frame_t *line_info = win_get_magic((ptrdiff_t)self.get_rep()); if (line_info) { line_info->set_name(name); } } void simline_t::create_schedule() { switch(type) { case simline_t::truckline: set_schedule(new truck_schedule_t()); break; case simline_t::trainline: set_schedule(new train_schedule_t()); break; case simline_t::shipline: set_schedule(new ship_schedule_t()); break; case simline_t::airline: set_schedule(new airplane_schedule_t()); break; case simline_t::monorailline: set_schedule(new monorail_schedule_t()); break; case simline_t::tramline: set_schedule(new tram_schedule_t()); break; case simline_t::maglevline: set_schedule(new maglev_schedule_t()); break; case simline_t::narrowgaugeline: set_schedule(new narrowgauge_schedule_t()); break; default: dbg->fatal( "simline_t::create_schedule()", "Cannot create default schedule!" ); } } void simline_t::add_convoy(convoihandle_t cnv) { if (line_managed_convoys.empty() && self.is_bound()) { // first convoi -> ok, now we can announce this connection to the stations // unbound self can happen during loading if this line had line_id=0 register_stops(schedule); } // first convoi may change line type if (type == trainline && line_managed_convoys.empty() && cnv.is_bound()) { // check, if needed to convert to tram/monorail line if (vehicle_t const* const v = cnv->front()) { switch (v->get_desc()->get_waytype()) { case tram_wt: type = simline_t::tramline; break; // elevated monorail were saved with wrong coordinates for some versions. // We try to recover here case monorail_wt: type = simline_t::monorailline; break; default: break; } } } // only add convoy if not already member of line line_managed_convoys.append_unique(cnv); // what goods can this line transport? bool update_schedules = false; if( cnv->get_state()!=convoi_t::INITIAL ) { for(uint8 const catg_index : cnv->get_goods_catg_index()) { if( !goods_catg_index.is_contained( catg_index ) ) { goods_catg_index.append( catg_index, 1 ); update_schedules = true; } } } // will not hurt ... financial_history[0][LINE_CONVOIS] = count_convoys(); recalc_status(); // do we need to tell the world about our new schedule? if( update_schedules ) { welt->set_schedule_counter(); } } void simline_t::remove_convoy(convoihandle_t cnv) { if(line_managed_convoys.is_contained(cnv)) { line_managed_convoys.remove(cnv); recalc_catg_index(); financial_history[0][LINE_CONVOIS] = count_convoys(); recalc_status(); } if(line_managed_convoys.empty()) { unregister_stops(); } } // invalid line id prior to 110.0 #define INVALID_LINE_ID_OLD ((uint16)(-1)) // invalid line id from 110.0 on #define INVALID_LINE_ID ((uint16)(0)) void simline_t::rdwr_linehandle_t(loadsave_t *file, linehandle_t &line) { uint16 id; if (file->is_saving()) { id = line.is_bound() ? line.get_id() : (file->is_version_less(110, 0) ? INVALID_LINE_ID_OLD : INVALID_LINE_ID); } else { // to avoid undefined errors during loading id = 0; } if(file->is_version_less(88, 3)) { sint32 dummy=id; file->rdwr_long(dummy); id = (uint16)dummy; } else { file->rdwr_short(id); } if (file->is_loading()) { // invalid line_id's: 0 and 65535 if (id == INVALID_LINE_ID_OLD) { id = 0; } line.set_id(id); } } void simline_t::rdwr(loadsave_t *file) { xml_tag_t s( file, "simline_t" ); assert(schedule); file->rdwr_str(name); rdwr_linehandle_t(file, self); schedule->rdwr(file); //financial history if( file->is_version_less(102, 3) ) { for (int j = 0; j<6; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (size_t k = MAX_MONTHS; k-- != 0;) { financial_history[k][LINE_DISTANCE] = 0; financial_history[k][LINE_MAXSPEED] = 0; financial_history[k][LINE_WAYTOLL] = 0; } } else if( file->is_version_less(111, 1) ) { for (int j = 0; j<7; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (size_t k = MAX_MONTHS; k-- != 0;) { financial_history[k][LINE_MAXSPEED] = 0; financial_history[k][LINE_WAYTOLL] = 0; } } else if( file->is_version_less(112, 8) ) { for (int j = 0; j<8; j++) { for (size_t k = MAX_MONTHS; k-- != 0;) { file->rdwr_longlong(financial_history[k][j]); } } for (size_t k = MAX_MONTHS; k-- != 0;) { financial_history[k][LINE_WAYTOLL] = 0; } } else { for (int j = 0; jrdwr_longlong(financial_history[k][j]); } } } if(file->is_version_atleast(102, 2)) { file->rdwr_bool(withdraw); } // otherwise initialized to zero if loading ... financial_history[0][LINE_CONVOIS] = count_convoys(); } void simline_t::finish_rd() { if( !self.is_bound() ) { // get correct handle self = player->simlinemgmt.get_line_with_id_zero(); assert( self.get_rep() == this ); DBG_MESSAGE("simline_t::finish_rd", "assigned id=%d to line %s", self.get_id(), get_name()); } if (!line_managed_convoys.empty()) { register_stops(schedule); } recalc_status(); } void simline_t::register_stops(schedule_t * schedule) { DBG_DEBUG("simline_t::register_stops()", "%d schedule entries in schedule %p", schedule->get_count(),schedule); for(schedule_entry_t const& i : schedule->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, player, schedule->get_waytype()==water_wt); if(halt.is_bound()) { //DBG_DEBUG("simline_t::register_stops()", "halt not null"); halt->add_line(self); } else { DBG_DEBUG("simline_t::register_stops()", "halt null"); } } } void simline_t::unregister_stops() { unregister_stops(schedule); } void simline_t::unregister_stops(schedule_t * schedule) { for(schedule_entry_t const& i : schedule->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, player, schedule->get_waytype() == water_wt); if(halt.is_bound()) { halt->remove_line(self); } } } void simline_t::renew_stops() { if (!line_managed_convoys.empty()) { register_stops( schedule ); DBG_DEBUG("simline_t::renew_stops()", "Line id=%d, name='%s'", self.get_id(), name.c_str()); } } void simline_t::check_freight() { for(convoihandle_t const i : line_managed_convoys) { i->check_freight(); } } void simline_t::new_month() { recalc_status(); // then calculate maxspeed sint64 line_max_speed = 0, line_max_speed_count = 0; for(convoihandle_t const i : line_managed_convoys) { if (!i->in_depot()) { // since convoi stepped first, our history is in month 1 ... line_max_speed += i->get_finance_history(1, convoi_t::CONVOI_MAXSPEED); line_max_speed_count ++; } } // to avoid div by zero if( line_max_speed_count ) { line_max_speed /= line_max_speed_count; } financial_history[0][LINE_MAXSPEED] = line_max_speed; // now roll history for (int j = 0; j0; k--) { financial_history[k][j] = financial_history[k-1][j]; } financial_history[0][j] = 0; } financial_history[0][LINE_CONVOIS] = count_convoys(); } void simline_t::init_financial_history() { MEMZERO(financial_history); } /* * the current state saved as color * Meanings are BLACK (ok), WHITE (no convois), YELLOW (no vehicle moved), RED (last month income minus), BLUE (at least one convoi vehicle is obsolete) */ void simline_t::recalc_status() { if(financial_history[0][LINE_CONVOIS]==0) { // no convois assigned to this line state_color = SYSCOL_EMPTY; withdraw = false; } else if(financial_history[0][LINE_PROFIT]<0) { // ok, not performing best state_color = MONEY_MINUS; } else if((financial_history[0][LINE_OPERATIONS]|financial_history[1][LINE_OPERATIONS])==0) { // nothing moved state_color = SYSCOL_TEXT_UNUSED; } else if(welt->use_timeline()) { // convois has obsolete vehicles? bool has_obsolete = false; for(convoihandle_t const i : line_managed_convoys) { has_obsolete = i->has_obsolete_vehicles(); if (has_obsolete) break; } // now we have to set it state_color = has_obsolete ? SYSCOL_OBSOLETE : SYSCOL_TEXT; } else { // normal state state_color = SYSCOL_TEXT; } } // recalc what good this line is moving void simline_t::recalc_catg_index() { // first copy old minivec_tpl old_goods_catg_index(goods_catg_index.get_count()); for(uint8 const i : goods_catg_index) { old_goods_catg_index.append(i); } goods_catg_index.clear(); withdraw = !line_managed_convoys.empty(); // then recreate current for(convoihandle_t const i : line_managed_convoys) { // what goods can this line transport? convoi_t const& cnv = *i; withdraw &= cnv.get_withdraw(); for(uint8 const catg_index : cnv.get_goods_catg_index()) { goods_catg_index.append_unique( catg_index ); } } // if different => schedule need recalculation if( goods_catg_index.get_count()!=old_goods_catg_index.get_count() ) { // surely changed welt->set_schedule_counter(); } else { // maybe changed => must test all entries for(uint8 const i : goods_catg_index) { if (!old_goods_catg_index.is_contained(i)) { // different => recalc welt->set_schedule_counter(); break; } } } } void simline_t::set_withdraw( bool yes_no ) { withdraw = yes_no && !line_managed_convoys.empty(); // convois in depots will be immediately destroyed, thus we go backwards for (size_t i = line_managed_convoys.get_count(); i-- != 0;) { line_managed_convoys[i]->set_no_load(yes_no); // must be first, since set withdraw might destroy convoi if in depot! line_managed_convoys[i]->set_withdraw(yes_no); } } sint64 simline_t::get_stat_converted(int month, int cost_type) const { sint64 value = financial_history[month][cost_type]; switch(cost_type) { case LINE_REVENUE: case LINE_OPERATIONS: case LINE_PROFIT: case LINE_WAYTOLL: value = convert_money(value); break; default: ; } return value; } simutrans-124.5/src/simutrans/simline.h000066400000000000000000000116521520675460500202160ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMLINE_H #define SIMLINE_H #include "simtypes.h" #include "simcolor.h" #include "convoihandle.h" #include "linehandle.h" #include "tpl/minivec_tpl.h" #include "tpl/vector_tpl.h" #include "utils/plainstring.h" #define MAX_MONTHS 12 // Max history #define LINE_CAPACITY 0 // the amount of ware that could be transported, theoretically #define LINE_TRANSPORTED_GOODS 1 // the amount of ware that has been transported #define LINE_CONVOIS 2 // number of convois for this line #define LINE_REVENUE 3 // the income this line generated #define LINE_OPERATIONS 4 // the cost of operations this line generated #define LINE_PROFIT 5 // total profit of line #define LINE_DISTANCE 6 // distance covered by all convois #define LINE_MAXSPEED 7 // maximum speed for bonus calculation of all convois #define LINE_WAYTOLL 8 // way toll paid by vehicles of line #define MAX_LINE_COST 9 // Total number of cost items class karte_ptr_t; class loadsave_t; class player_t; class schedule_t; class simline_t { public: enum linetype { line = 0, truckline = 1, trainline = 2, shipline = 3, airline = 4, monorailline = 5, tramline = 6, maglevline = 7, narrowgaugeline = 8, MAX_LINE_TYPE }; protected: schedule_t * schedule; player_t *player; linetype type; bool withdraw; private: static karte_ptr_t welt; plainstring name; /** * Handle for ourselves. Can be used like the 'this' pointer * Initialized by constructors */ linehandle_t self; /** * the current state saved as color * Meanings are BLACK (ok), WHITE (no convois), YELLOW (no vehicle moved), RED (last month income minus), BLUE (at least one convoi vehicle is obsolete) */ PIXVAL state_color; /** * a list of all convoys assigned to this line */ vector_tpl line_managed_convoys; /** * a list of all catg_index, which can be transported by this line. */ minivec_tpl goods_catg_index; /** * struct holds new financial history for line */ sint64 financial_history[MAX_MONTHS][MAX_LINE_COST]; /** * creates empty schedule with type depending on line-type */ void create_schedule(); void init_financial_history(); void recalc_status(); public: simline_t(player_t *player, linetype type); simline_t(player_t *player, linetype type, loadsave_t *file); ~simline_t(); linehandle_t get_handle() const { return self; } /** * add convoy to route */ void add_convoy(convoihandle_t cnv); /** * remove convoy from route */ void remove_convoy(convoihandle_t cnv); /** * get convoy */ convoihandle_t get_convoy(int i) const { return line_managed_convoys[i]; } /** * return number of manages convoys in this line */ uint32 count_convoys() const { return line_managed_convoys.get_count(); } vector_tpl const& get_convoys() const { return line_managed_convoys; } /** * returns the state of the line */ PIXVAL get_state_color() const { return state_color; } /** * return the schedule of the line */ schedule_t * get_schedule() const { return schedule; } void set_schedule(schedule_t* schedule); /** * get name of line */ char const* get_name() const { return name; } void set_name(const char *str); /* * load or save the line */ void rdwr(loadsave_t * file); /** * method to load/save linehandles */ static void rdwr_linehandle_t(loadsave_t *file, linehandle_t &line); /* * register line with stop */ void register_stops(schedule_t * schedule); void finish_rd(); /* * unregister line from stop */ void unregister_stops(schedule_t * schedule); void unregister_stops(); /* * renew line registration for stops */ void renew_stops(); // called after tiles are removed from stations to change the load of connected convois void check_freight(); sint64* get_finance_history() { return *financial_history; } sint64 get_finance_history(int month, int cost_type) const { return financial_history[month][cost_type]; } sint64 get_stat_converted(int month, int cost_type) const; void book(sint64 amount, int cost_type) { financial_history[0][cost_type] += amount; } static uint8 convoi_to_line_catgory(uint8 convoi_cost_type); void new_month(); linetype get_linetype() { return type; } static waytype_t linetype_to_waytype( const linetype lt ); static linetype waytype_to_linetype( const waytype_t wt ); static const char *get_linetype_name( const linetype lt ); const minivec_tpl &get_goods_catg_index() const { return goods_catg_index; } // recalculates the good transported by this line and (in case of changes) will start schedule recalculation void recalc_catg_index(); void set_withdraw( bool yes_no ); bool get_withdraw() const { return withdraw; } player_t *get_owner() const {return player;} }; #endif simutrans-124.5/src/simutrans/simlinemgmt.cc000066400000000000000000000126001520675460500212330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "simlinemgmt.h" #include "simline.h" #include "simconvoi.h" #include "gui/simwin.h" #include "world/simworld.h" #include "simtypes.h" #include "simintr.h" #include "dataobj/schedule.h" #include "dataobj/loadsave.h" #include "gui/line_management_gui.h" #include "player/simplay.h" simlinemgmt_t::~simlinemgmt_t() { destroy_win((ptrdiff_t)this); // and delete all lines ... while (!all_managed_lines.empty()) { linehandle_t line = all_managed_lines.back(); all_managed_lines.pop_back(); delete line.get_rep(); // detaching handled by line itself } } void simlinemgmt_t::add_line(linehandle_t new_line) { all_managed_lines.append(new_line); } void simlinemgmt_t::delete_line(linehandle_t line) { if (line.is_bound()) { while(line->count_convoys() ) { line->get_convoy(0)->set_line( linehandle_t() ); } all_managed_lines.remove(line); //destroy line object delete line.get_rep(); } } void simlinemgmt_t::update_line(linehandle_t line) { // when a line is updated, all managed convoys must get the new schedule! const int count = line->count_convoys(); for( int i = 0; iget_convoy(i); cnv->set_update_line(line); if( cnv->in_depot() ) { cnv->check_pending_updates(); // apply new schedule immediately for convoys in depot } } // finally de/register all stops line->renew_stops(); if( count>0 ) { world()->set_schedule_counter(); } } void simlinemgmt_t::rdwr(loadsave_t *file, player_t *player) { xml_tag_t l( file, "simlinemgmt_t" ); if(file->is_saving()) { // on old versions if( file->is_version_less(101, 0) ) { file->wr_obj_id("Linemanagement"); } // ensure that lines are saved in the same order on all clients sort_lines(); uint32 count = all_managed_lines.get_count(); file->rdwr_long(count); for(linehandle_t const i : all_managed_lines) { simline_t::linetype lt = i->get_linetype(); file->rdwr_enum(lt); i->rdwr(file); } } else { // on old versions if( file->is_version_less(101, 0) ) { char buf[80]; file->rd_obj_id(buf, 79); all_managed_lines.clear(); if(strcmp(buf, "Linemanagement")!=0) { dbg->fatal( "simlinemgmt_t::rdwr", "Broken Savegame" ); } } sint32 totalLines = 0; file->rdwr_long(totalLines); DBG_MESSAGE("simlinemgmt_t::rdwr()","number of lines=%i",totalLines); simline_t *unbound_line = NULL; for (int i = 0; irdwr_enum(lt); if(lt < simline_t::truckline || lt > simline_t::narrowgaugeline) { dbg->fatal( "simlinemgmt_t::rdwr()", "Cannot create default line!" ); } simline_t *line = new simline_t(player, lt, file); if (!line->get_handle().is_bound()) { // line id was saved as zero ... if (unbound_line) { dbg->fatal( "simlinemgmt_t::rdwr()", "More than one line with unbound id read" ); } else { unbound_line = line; } } else { add_line( line->get_handle() ); } } if( unbound_line ) { // linehandle will be corrected in simline_t::finish_rd line_with_id_zero = linehandle_t(unbound_line,true); add_line( line_with_id_zero ); } } } static bool compare_lines(linehandle_t const a, linehandle_t const b) { int diff = 0; char const* const na = a->get_name(); char const* const nb = b->get_name(); if (na[0] == '(' && nb[0] == '(') { diff = atoi(na + 1) - atoi(nb + 1); } if( diff==0 ) { diff = strcmp(na, nb); } if(diff==0) { diff = a.get_id() - b.get_id(); } return diff < 0; } void simlinemgmt_t::sort_lines() { std::sort(all_managed_lines.begin(), all_managed_lines.end(), compare_lines); } void simlinemgmt_t::finish_rd() { for(linehandle_t const i : all_managed_lines) { i->finish_rd(); } sort_lines(); } void simlinemgmt_t::rotate90( sint16 y_size ) { for(linehandle_t const i : all_managed_lines) { if (schedule_t* const schedule = i->get_schedule()) { schedule->rotate90( y_size ); } } } void simlinemgmt_t::new_month() { for(linehandle_t const i : all_managed_lines) { i->new_month(); } } linehandle_t simlinemgmt_t::create_line(int ltype, player_t * player) { if(ltype < simline_t::truckline || ltype > simline_t::narrowgaugeline) { dbg->fatal( "simlinemgmt_t::create_line()", "Cannot create default line!" ); } simline_t * line = new simline_t(player, (simline_t::linetype)ltype); add_line( line->get_handle() ); sort_lines(); return line->get_handle(); } linehandle_t simlinemgmt_t::create_line(int ltype, player_t * player, schedule_t * schedule) { linehandle_t line = create_line( ltype, player ); if(schedule) { line->get_schedule()->copy_from(schedule); } return line; } void simlinemgmt_t::get_lines(int type, vector_tpl* lines) const { lines->clear(); for(linehandle_t const line : all_managed_lines) { if (type == simline_t::line || line->get_linetype() == simline_t::line || line->get_linetype() == type) { lines->append(line); } } } void simlinemgmt_t::show_lineinfo(player_t *player, linehandle_t line, int which_tab) { if( line.is_bound() ) { gui_frame_t *line_info = win_get_magic( (ptrdiff_t)line.get_rep() ); if( line_info ) { top_win( line_info ); } else { create_win( new line_management_gui_t(line, player, which_tab), w_info, (ptrdiff_t)line.get_rep() ); } } } simutrans-124.5/src/simutrans/simlinemgmt.h000066400000000000000000000036171520675460500211050ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMLINEMGMT_H #define SIMLINEMGMT_H #include "linehandle.h" #include "simtypes.h" #include "tpl/vector_tpl.h" class loadsave_t; class schedule_t; class player_t; class schedule_list_gui_t; class simlinemgmt_t { public: ~simlinemgmt_t(); /** * add a line */ void add_line(linehandle_t new_line); /** * delete a line */ void delete_line(linehandle_t line); /** * update a line -> apply updated schedule to all convoys */ void update_line(linehandle_t line); /* * load or save the linemanagement */ void rdwr(loadsave_t * file, player_t * player); /* * sort the lines by name */ void sort_lines(); /* * will register all stops for all lines */ void register_all_stops(); /* * called after game is fully loaded; */ void finish_rd(); void rotate90( sint16 y_size ); void new_month(); /** * creates a line with an empty schedule */ linehandle_t create_line(int ltype, player_t * player); /** * Creates a line and sets its schedule */ linehandle_t create_line(int ltype, player_t * player, schedule_t * schedule); /** * If there was a line with id=0 map it to non-zero handle */ linehandle_t get_line_with_id_zero() const {return line_with_id_zero; } /* * fill the list with all lines of a certain type * type == simline_t::line will return all lines */ void get_lines(int type, vector_tpl* lines) const; uint32 get_line_count() const { return all_managed_lines.get_count(); } /** * Will open the line management window and offer information about the line */ void show_lineinfo(player_t *player, linehandle_t line, int which_tab ); vector_tpl const& get_line_list() const { return all_managed_lines; } private: vector_tpl all_managed_lines; linehandle_t line_with_id_zero; }; #endif simutrans-124.5/src/simutrans/simloadingscreen.cc000066400000000000000000000111611520675460500222350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simloadingscreen.h" #include "sys/simsys.h" #include "descriptor/image.h" #include "descriptor/skin_desc.h" #include "simskin.h" #include "display/simgraph.h" #include "simevent.h" #include "dataobj/environment.h" #include "simticker.h" #include "gui/simwin.h" #include "gui/gui_theme.h" #include "tpl/slist_tpl.h" loadingscreen_t::loadingscreen_t( const char *w, uint32 max_p, bool logo, bool continueflag ) { what = w; info = NULL; progress = 0; max_progress = max_p; last_bar_len = -1; show_logo = logo; if( !gfx->is_display_init() || continueflag ) { return; } // darkens the current screen const scr_size screen = gfx->get_screen_size(); gfx->tint_rect(0, 0, screen.w, screen.h, gfx->palette_lookup(COL_BLACK), 50 ); gfx->mark_screen_dirty(); display_logo(); } // show the logo if requested and there void loadingscreen_t::display_logo() { if( show_logo && skinverwaltung_t::biglogosymbol ) { const scr_size screen = gfx->get_screen_size(); const image_t *image0 = skinverwaltung_t::biglogosymbol->get_image(0); const int w = image0->w; const int h = image0->h + image0->y; int x = screen.w/2-w; int y = screen.h/4-w; if(y<0) { y = 1; } gfx->draw_color_img(skinverwaltung_t::biglogosymbol->get_image_id(0), x, y, 0, false, true CLIP_NUM_DEFAULT); gfx->draw_color_img(skinverwaltung_t::biglogosymbol->get_image_id(1), x+w, y, 0, false, true CLIP_NUM_DEFAULT); gfx->draw_color_img(skinverwaltung_t::biglogosymbol->get_image_id(2), x, y+h, 0, false, true CLIP_NUM_DEFAULT); gfx->draw_color_img(skinverwaltung_t::biglogosymbol->get_image_id(3), x+w, y+h, 0, false, true CLIP_NUM_DEFAULT); } } // show everything but the logo void loadingscreen_t::display() { const scr_size screen = gfx->get_screen_size(); const int half_width = screen.w / 2; const int quarter_width = screen.w / 4; const int half_height = screen.h / 2; scr_coord_val const bar_height = max(LINESPACE + 10, 20); scr_coord_val const bar_y = half_height - bar_height / 2 + 1; scr_coord_val const bar_text_y = half_height - LINESPACE / 2 + 1; const int bar_len = max_progress>0 ? (int) ( ((double)progress*(double)half_width)/(double)max_progress ) : 0; if( bar_len != last_bar_len ) { last_bar_len = bar_len; dr_prepare_flush(); if( info ) { gfx->draw_text( half_width, bar_y - LINESPACE - 2, info, ALIGN_CENTER_H, gfx->palette_lookup(COL_WHITE), true ); } // outline gfx->draw_box3d(quarter_width-2, bar_y, half_width+4, bar_height, gfx->palette_lookup(COL_GREY6), gfx->palette_lookup(COL_GREY4), true); gfx->draw_box3d(quarter_width-1, bar_y + 1, half_width+2, bar_height - 2, gfx->palette_lookup(COL_GREY4), gfx->palette_lookup(COL_GREY6), true); // inner gfx->draw_rect( quarter_width, bar_y + 2, half_width, bar_height - 4, SYSCOL_LOADINGBAR_INNER, true); // progress gfx->draw_rect( quarter_width, bar_y + 4, bar_len, bar_height - 8, SYSCOL_LOADINGBAR_PROGRESS, true ); if( what ) { gfx->draw_text( half_width, bar_text_y, what, ALIGN_CENTER_H, SYSCOL_TEXT_HIGHLIGHT, false ); } dr_flush(); } } void loadingscreen_t::set_progress( uint32 progress ) { if (!gfx->is_display_init() && (progress != this->progress || progress == 0) ) { return; } // first: handle events event_t *ev = new event_t; display_poll_event(ev); if( ev->ev_class == EVENT_SYSTEM ) { if( ev->ev_code == SYSTEM_RESIZE ) { // main window resized gfx->on_window_resized( ev->new_window_size ); gfx->draw_rect( 0, 0, ev->mouse_pos.x, ev->mouse_pos.y, gfx->palette_lookup(COL_BLACK), true ); display_logo(); // queue the event anyway, so the viewport is correctly updated on world resume (screen will be resized again). queued_events.append(ev); } else if( ev->ev_code == SYSTEM_QUIT ) { // since the world is suspended (or may not even exists) when the loading screen is active // we can quit the quick way env_t::quit_simutrans = true; delete ev; } else { delete ev; } } else if( IS_KEYBOARD(ev) ) { queued_events.append(ev); } else { // neither a keyboard or system event -> not of interest delete ev; } this->progress = progress; display(); if (progress > max_progress) { dbg->error("loadingscreen_t::set_progress", "too much progress: actual = %d max = %d", progress, max_progress); } } loadingscreen_t::~loadingscreen_t() { if (gfx->is_display_init()) { win_redraw_world(); gfx->mark_screen_dirty(); ticker::set_redraw_all(true); } while( !queued_events.empty() ) { queue_event( queued_events.remove_first() ); } } simutrans-124.5/src/simutrans/simloadingscreen.h000066400000000000000000000022051520675460500220760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMLOADINGSCREEN_H #define SIMLOADINGSCREEN_H #include "simtypes.h" #include "tpl/slist_tpl.h" struct event_t; /** * Implements the loading screen related routines, in the aim of centralize * all its code and make it more modular, as it was scattered across all code * before. * * @note The functions are safe on non-initialized displays, it won't try to write * on a not existent buffer. */ class loadingscreen_t { private: const char *what, *info; uint32 progress, max_progress; int last_bar_len; bool show_logo; slist_tpl queued_events; // show the logo if requested and there void display_logo(); // show everything but the logo void display(); public: loadingscreen_t( const char *what, uint32 max_progress, bool show_logo = false, bool continueflag = false ); ~loadingscreen_t(); void set_progress( uint32 progress ); void set_max( uint32 max ) { max_progress = max; } void set_info( const char *info ) { this->info = info; } void set_what( const char *what ) { this->what = what; } }; #endif simutrans-124.5/src/simutrans/simmain.cc000066400000000000000000001504551520675460500203560ustar00rootroot00000000000000/* This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #if defined(_M_X64) || defined(__x86_64__) #if __GNUC__ #warning "Simutrans is preferably compiled as 32 bit binary!" #endif #endif #if defined(_MSC_VER) && defined(DEBUG) // Console window on MSVC debug builds #pragma comment(linker, "/SUBSYSTEM:CONSOLE") #endif #include #include #include #include "pathes.h" #include "simmain.h" #include "world/simworld.h" #include "simware.h" #include "display/simview.h" #include "gui/simwin.h" #include "gui/gui_theme.h" #include "gui/messagebox.h" #include "simhalt.h" #include "display/simimg.h" #include "simcolor.h" #include "simskin.h" #include "simconst.h" #include "ground/boden.h" #include "ground/wasser.h" #include "world/simcity.h" #include "player/simplay.h" #include "simsound.h" #include "simintr.h" #include "simloadingscreen.h" #include "simticker.h" #include "simmesg.h" #include "tool/simmenu.h" #include "siminteraction.h" #include "simtypes.h" #include "simachievements.h" #include "sys/simsys.h" #include "display/simgraph.h" #include "simevent.h" #include "simversion.h" #include "gui/banner.h" #include "gui/pakselector.h" #include "gui/pakinstaller.h" #include "gui/welt.h" #include "gui/help_frame.h" #include "gui/sprachen.h" #include "gui/climates.h" #include "gui/messagebox.h" #include "gui/loadsave_frame.h" #include "gui/loadfont_frame.h" #include "gui/load_relief_frame.h" #include "gui/scenario_frame.h" #include "obj/baum.h" #include "obj/wolke.h" #include "utils/simstring.h" #include "utils/searchfolder.h" #include "io/rdwr/compare_file_rd_stream.h" #include "network/network.h" // must be before any "windows.h" is included via bzlib2.h ... #include "dataobj/loadsave.h" #include "dataobj/environment.h" #include "dataobj/tabfile.h" #include "dataobj/scenario.h" #include "dataobj/settings.h" #include "dataobj/translator.h" #include "network/pakset_info.h" #include "descriptor/reader/obj_reader.h" #include "descriptor/sound_desc.h" #include "descriptor/ground_desc.h" #include "music/music.h" #include "sound/sound.h" #include "utils/cbuffer.h" #include "utils/simrandom.h" #include "utils/unicode.h" #include "builder/vehikelbauer.h" #include "script/script_tool_manager.h" #include "vehicle/vehicle.h" #include "vehicle/simroadtraffic.h" #ifdef STEAM_BUILT #include "../steam/steam.h" #endif using std::string; #ifdef DEBUG /* diagnostic routine: * show the size of several internal structures */ static void show_sizes() { log_t::level_t old_level = env_t::verbose_debug; env_t::verbose_debug = log_t::LEVEL_MSG; DBG_MESSAGE("Debug", "size of structures"); DBG_MESSAGE("sizes", "size of pointer %i", sizeof(void*)); DBG_MESSAGE("sizes", "koord: %d", sizeof(koord)); DBG_MESSAGE("sizes", "koord3d: %d", sizeof(koord3d)); DBG_MESSAGE("sizes", "ribi_t::ribi: %d", sizeof(ribi_t::ribi)); DBG_MESSAGE("sizes", "halthandle_t: %d\n", sizeof(halthandle_t)); DBG_MESSAGE("sizes", "obj_t: %d", sizeof(obj_t)); DBG_MESSAGE("sizes", "gebaeude_t: %d", sizeof(gebaeude_t)); DBG_MESSAGE("sizes", "baum_t: %d", sizeof(baum_t)); DBG_MESSAGE("sizes", "wolke_t: %d", sizeof(wolke_t)); DBG_MESSAGE("sizes", "weg_t: %d", sizeof(weg_t)); DBG_MESSAGE("sizes", "private_car_t: %d\n", sizeof(private_car_t)); DBG_MESSAGE("sizes", "objlist_t: %d", sizeof(objlist_t)); DBG_MESSAGE("sizes", "grund_t: %d", sizeof(grund_t)); DBG_MESSAGE("sizes", "boden_t: %d", sizeof(boden_t)); DBG_MESSAGE("sizes", "wasser_t: %d", sizeof(wasser_t)); DBG_MESSAGE("sizes", "planquadrat_t: %d\n", sizeof(planquadrat_t)); DBG_MESSAGE("sizes", "planquadrat_t[100]: %d\n", sizeof(planquadrat_t[100])); DBG_MESSAGE("sizes", "ware_t: %d", sizeof(ware_t)); DBG_MESSAGE("sizes", "vehicle_t: %d", sizeof(vehicle_t)); DBG_MESSAGE("sizes", "haltestelle_t: %d\n", sizeof(haltestelle_t)); DBG_MESSAGE("sizes", "karte_t: %d", sizeof(karte_t)); DBG_MESSAGE("sizes", "player_t: %d\n", sizeof(player_t)); env_t::verbose_debug = old_level; } #endif #if defined DEBUG || defined PROFILE // render tests ... static void show_times(karte_t *welt, main_view_t *view) { intr_set_view(view); welt->set_fast_forward(true); intr_disable(); dbg->message( "show_times()", "simple profiling of drawing routines" ); int i; image_id img = ground_desc_t::outside->get_image(0,0); gfx->draw_color_img(img, 120, 100, 1, 0, 1 CLIP_NUM_DEFAULT); uint32 ms = dr_time(); for (i = 0; i < 6000000; i++) { gfx->draw_img_aux( img, 50, 50, 1, 0, true CLIP_NUM_DEFAULT); } dbg->message("show_times()", "draw_img_aux() %i iterations took %li ms", i, dr_time() - ms ); image_id player_img = skinverwaltung_t::color_options->get_image_id(0); ms = dr_time(); for (i = 0; i < 1000000; i++) { gfx->draw_color_img( player_img, 120, 100, i%15, 0, 1 CLIP_NUM_DEFAULT); } dbg->message("show_times()", "draw_color_img() with recolor %i iterations took %li ms", i, dr_time() - ms ); ms = dr_time(); for (i = 0; i < 100000; i++) { gfx->draw_color_img( img, 120, 100, 0, 1, 1 CLIP_NUM_DEFAULT); gfx->draw_color_img( player_img, 160, 150, 16, 1, 1 CLIP_NUM_DEFAULT); } dbg->message("show_times()", "draw_color_img() 3x %i iterations took %li ms", i, dr_time() - ms ); #if !defined(MULTI_THREAD) ms = dr_time(); for (i = 0; i < 600000; i++) { dr_prepare_flush(); dr_flush(); } dbg->message("show_times()", "dr_prepare_flush()/dr_flush() %i iterations took %li ms", i, dr_time() - ms ); #endif ms = dr_time(); for (i = 0; i < 300000; i++) { gfx->draw_text_clipped_n(100, 120, "Dies ist ein kurzer Textetxt ...", 0, 0, false, -1 CLIP_NUM_DEFAULT); } dbg->message("show_times()", "draw_text_clipped_n() %i iterations took %li ms", i, dr_time() - ms ); ms = dr_time(); for (i = 0; i < 300000; i++) { gfx->draw_rect(100, 120, 300, 50, 0, false); } dbg->message("show_times()", "draw_rect() %i iterations took %li ms", i, dr_time() - ms ); ms = dr_time(); for (i = 0; i < 2000; i++) { view->display(true); } dbg->message("show_times()", "view->display(true) %i iterations took %li ms", i, dr_time() - ms ); ms = dr_time(); for (i = 0; i < 2000; i++) { view->display(true); win_display_flush(0.0); } dbg->message("show_times()", "view->display(true) and flush %i iterations took %li ms", i, dr_time() - ms ); ms = dr_time(); for (i = 0; i < 40000000/(int)weg_t::get_alle_wege().get_count(); i++) { for(weg_t * const w : weg_t::get_alle_wege() ) { grund_t *dummy; welt->lookup( w->get_pos() )->get_neighbour( dummy, invalid_wt, ribi_t::north ); } } dbg->message("show_times()", "grund_t::get_neighbour() %i iterations took %li ms", i*weg_t::get_alle_wege().get_count(), dr_time() - ms ); ms = dr_time(); for (i = 0; i < 1000; i++) { welt->sync_step(100); welt->sync_step(100); welt->sync_step(100); welt->sync_step(100); welt->sync_step(100); welt->sync_step(100); welt->sync_step(100); welt->sync_step(100); welt->sync_step(100); welt->sync_step(100); welt->display(1000); welt->step(); } dbg->message("show_times()", "welt->sync_step(200)*10/display(1000)/step %i iterations took %li ms", i, dr_time() - ms); ms = dr_time(); for (i = 0; i < 2000; i++) { welt->sync_step(200); welt->display(200); welt->step(); } dbg->message("show_times()", "welt->sync_step(200)/display(200)/step %i iterations took %li ms", i, dr_time() - ms ); } #endif // some routines for the modal display static bool never_quit() { return false; } static bool no_language() { return translator::get_language()!=-1; } static utf16 testfor_this_character = 0; static bool no_font() { return gfx->font_has_character(testfor_this_character); } #if COLOUR_DEPTH != 0 static bool empty_objfilename() { return !env_t::pak_name.empty() || pakinstaller_t::finish_install; } static bool finish_install() { return pakinstaller_t::finish_install; } #endif #if COLOUR_DEPTH != 0 /** * Show pak installer */ static void install_objfilename() { DBG_DEBUG("", "env_t::reload_and_save_on_quit=%d", env_t::reload_and_save_on_quit); #if !defined(USE_OWN_PAKINSTALL) && defined(_WIN32) dr_download_pakset( env_t::base_dir, strcmp( env_t::base_dir, env_t::user_dir)==0 ); // windows #else pakinstaller_t* sel = new pakinstaller_t(); // notify gui to load list of paksets event_t ev; ev.ev_class = INFOWIN; ev.ev_code = WIN_OPEN; sel->infowin_event(&ev); destroy_all_win(true); // since eventually the successful load message is still there .... modal_dialogue(sel, magic_none, NULL, finish_install); #endif } /** * Show pak selector */ static sint16 ask_objfilename() { pakselector_t* sel = new pakselector_t(); // notify gui to load list of paksets event_t ev; ev.ev_class = INFOWIN; ev.ev_code = WIN_OPEN; sel->infowin_event(&ev); sint16 entries = sel->get_entries_count(); if(sel->has_pak()) { destroy_all_win(true); // since eventually the successful load message is still there .... modal_dialogue( sel, magic_none, NULL, empty_objfilename ); } else { delete sel; } return entries; } #endif /** * Show language selector */ static void ask_language() { if( gfx->get_screen_size().w==0 ) { // only console available ... => choose english for the moment dbg->warning( "ask_language", "No language selected, will use english!" ); translator::set_language( "en" ); } else { sprachengui_t* sel = new sprachengui_t(); destroy_all_win(true); // since eventually the successful load message is still there .... modal_dialogue( sel, magic_none, NULL, no_language ); destroy_win( sel ); } } /** * This function will be set in the main function as the handler the runtime environment will * call in the case it lacks memory for new() */ NORETURN static void sim_new_handler() { dbg->fatal("sim_new_handler()", "OUT OF MEMORY"); } /// Helper class for retrieving command line arguments struct args_t { public: args_t(int argc, char **argv) : argc(argc), argv(argv) {} public: const char *gimme_arg(const char *arg, int off) const { for( int i = 1; i < argc; i++ ) { if(strcmp(argv[i], arg) == 0 && i < argc - off ) { return argv[i + off]; } } return NULL; } bool has_arg(const char *arg) const { return gimme_arg(arg, 0) != NULL; } private: int argc; char **argv; }; void print_help() { printf( "\n" "---------------------------------------\n" " Simutrans " VERSION_NUMBER "\n" " released " VERSION_DATE "\n" " developed\n" " by the Simutrans team.\n" "\n" " Send feedback and questions to:\n" " \n" "\n" " Based on Simutrans 0.84.21.2\n" " by Hansjörg Malthaner et. al.\n" "---------------------------------------\n" "command line parameters available: \n" " -addons loads also addons (with -objects)\n" " -async asynchronous images, only for SDL\n" " -borderless emulate fullscreen as borderless window\n" " -use_hw hardware double buffering, only for SDL\n" " -debug NUM enables debugging (1..5) Append p to show pak details\n" " -easyserver set up every for server (query own IP, port forwarding)\n" " -freeplay play with endless money\n" " -fullscreen starts simutrans in fullscreen mode\n" " -fps COUNT framerate (from 5 to 100)\n" " -h | -help | --help displays this help\n" " -lang CODE starts with specified language\n" " -load NAME loads savegame with name 'NAME' from Simutrans 'save' directory\n" " -log enables logging to file 'simu.log'\n" #ifdef SYSLOG " -syslog enable logging to syslog\n" " mutually exclusive with -log\n" " -tag TAG sets syslog tag (default 'simutrans')\n" #endif " -mute mute all sounds\n" " -noaddons does not load any addon (default)\n" " -nomidi turns off background music\n" " -nosound turns off ambient sounds\n" " -objects DIR/ loads the pakset in specified directory\n" " -set_pakdir DIR loads the pakset in specified directory\n" " -pause starts game with paused after loading\n" " a server will pause if there are no clients\n" " -res N starts in specified resolution: \n" " 1=640x480, 2=800x600, 3=1024x768, 4=1280x1024\n" " -scenario NAME Load scenario NAME\n" " -screensize WxH set screensize to width W and height H\n" " -server [PORT] starts program as server (for network game)\n" " without port specified uses 13353\n" " -announce Enable server announcements\n" " -autodpi Automatic screen scaling for high DPI screens\n" " -screen_scale N Manual screen scaling to N percent (0=off)\n" " Ignored when -autodpi is specified\n" " -server_dns FQDN/IP FQDN or IP address of server for announcements\n" " -server_name NAME Name of server for announcements\n" " -server_admin_pw PW password for server administration\n" " -heavy NUM enables heavy-mode debugging for network games. VERY SLOW!\n" " -set_basedir WD Use WD as directory containing all constant data.\n" " -set_installdir WD Use WD as directory for pakset download.\n" " -set_userdir WD Use WD as directory for local user data.\n" " -singleuser Save everything in data directory (portable version)\n" #ifdef DEBUG " -sizes Show current size of some structures\n" #endif " -startyear N start in year N\n" " -theme N user directory containing theme files\n" #ifdef MULTI_THREAD " -threads N use N threads if possible\n" #endif " -timeline enables timeline\n" #if defined DEBUG || defined PROFILE " -times does some simple profiling\n" #endif " -until YEAR.MONTH quits when MONTH of YEAR starts\n" ); } void setup_logging(const args_t &args) { const char *version = "Simutrans version " VERSION_NUMBER " from " VERSION_DATE #ifdef REVISION " r" QUOTEME(REVISION) #endif #ifdef GIT_HASH " hash " QUOTEME(GIT_HASH) #endif "\n"; #ifdef SYSLOG bool cli_syslog_enabled = args.has_arg("-syslog"); const char* cli_syslog_tag = args.gimme_arg("-tag", 1); #else bool cli_syslog_enabled = false; const char* cli_syslog_tag = NULL; #endif env_t::verbose_debug = log_t::LEVEL_FATAL; if( args.has_arg("-debug") ) { const char *s = args.gimme_arg("-debug", 1); log_t::level_t level = log_t::LEVEL_DEBUG; if(s!=NULL && s[0]>='0' && s[0]<='9' ) { env_t::pakset_debug = (s[1] == 'p'); level = (log_t::level_t)clamp(atoi(s), (int)log_t::LEVEL_FATAL, (int)log_t::LEVEL_DEBUG); } env_t::verbose_debug = level; } if ( cli_syslog_enabled ) { printf("syslog enabled\n"); if ( cli_syslog_tag ) { printf("Init logging with syslog tag: %s\n", cli_syslog_tag); init_logging( "syslog", true, true, version, cli_syslog_tag ); } else { printf("Init logging with default syslog tag\n"); init_logging( "syslog", true, true, version, "simutrans" ); } } else if (args.has_arg("-log")) { dr_chdir( env_t::user_dir ); char temp_log_name[256]; const char *logname = "simu.log"; if( args.has_arg("-server") ) { const char *p = args.gimme_arg("-server", 1); int portadress = p ? atoi( p ) : 13353; sprintf( temp_log_name, "simu-server%d.log", portadress==0 ? 13353 : portadress ); logname = temp_log_name; } init_logging( logname, true, true, version, NULL ); } else if (args.has_arg("-debug")) { init_logging( "stderr", true, true, version, NULL ); } else { init_logging(NULL, false, false, version, NULL); } } // search for this in all possible pakset locations for the directory chkdir and take the first match bool set_pakdir( const char *chkdir ) { if( !chkdir || !*chkdir ) { return false; } char tmp[PATH_MAX]; dr_chdir( env_t::base_dir ); if( !dr_chdir( chkdir ) ) { dr_getcwd(tmp, lengthof(tmp)); env_t::pak_dir = tmp; env_t::pak_dir += PATH_SEPARATOR; const char* new_name = strrchr(tmp, *PATH_SEPARATOR); env_t::pak_name = new_name+1; env_t::pak_name += PATH_SEPARATOR; return true; } dr_chdir( env_t::install_dir ); if( !dr_chdir( chkdir ) ) { dr_getcwd(tmp, lengthof(tmp)); env_t::pak_dir = tmp; env_t::pak_dir += PATH_SEPARATOR; const char* new_name = strrchr(tmp, *PATH_SEPARATOR); env_t::pak_name = new_name+1; env_t::pak_name += PATH_SEPARATOR; return true; } dr_chdir( env_t::user_dir ); if(!dr_chdir( USER_PAK_PATH ) ) { if( !dr_chdir( chkdir ) ) { dr_getcwd(tmp, lengthof(tmp)); env_t::pak_dir = tmp; env_t::pak_dir += PATH_SEPARATOR; const char* new_name = strrchr(tmp, *PATH_SEPARATOR); env_t::pak_name = new_name+1; env_t::pak_name += PATH_SEPARATOR; return true; } } return false; } int simu_main(int argc, char** argv) { std::set_new_handler(sim_new_handler); args_t args(argc, argv); #ifdef DEBUG if (args.has_arg("-debug")) { // debug init messages env_t::verbose_debug = log_t::LEVEL_DEBUG; init_logging("stderr", true, true, "Preinit", NULL); } #endif env_t::init(); // you really want help with this? if (args.has_arg("-h") || args.has_arg("-?") || args.has_arg("-help") || args.has_arg("--help")) { print_help(); return EXIT_SUCCESS; } // only the specified pak conf should override this! uint16 pak_diagonal_multiplier = env_t::default_settings.get_pak_diagonal_multiplier(); sint8 pak_tile_height = TILE_HEIGHT_STEP; sint8 pak_height_conversion_factor = env_t::pak_height_conversion_factor; if( !dr_set_basedir(args.gimme_arg( "-set_basedir", 1 ), argv[0]) ) { dr_fatal_notify("Absolutely no base installation found.\nPlease download/install the complete base set!"); return EXIT_FAILURE; } dr_chdir( env_t::base_dir ); // parsing config/simuconf.tab to find out single user install bool found_settings = false; bool found_simuconf = false; bool not_portable = !args.has_arg("-singleuser"); // only parse global config, if not force to portable by switch tabfile_t simuconf; char path_to_simuconf[24]; // was config/simuconf.tab sprintf( path_to_simuconf, "config%ssimuconf.tab", PATH_SEPARATOR ); if( simuconf.open( path_to_simuconf ) ) { tabfileobj_t contents; simuconf.read( contents ); // use different save directories not_portable = !(contents.get_int( "singleuser_install", !not_portable )==1 || !not_portable); found_simuconf = true; simuconf.close(); } if( !check_and_set_dir( args.gimme_arg( "-set_installdir", 1 ), "-set_installdir", env_t::install_dir ) ) { if( !check_and_set_dir( getenv( "SIMUTRANS_INSTALLDIR" ), "SIMUTRANS_INSTALLDIR", env_t::install_dir ) ) { if( !not_portable ) { // portable installation strcpy( env_t::install_dir, env_t::base_dir ); } else { strcpy( env_t::install_dir, dr_query_installdir() ); } } } if( !check_and_set_dir( args.gimme_arg( "-set_userdir", 1 ), "-set_userdir", env_t::user_dir ) ) { if( !check_and_set_dir( getenv( "SIMUTRANS_USERDIR" ), "SIMUTRANS_USERDIR", env_t::user_dir ) ) { if( !not_portable ) { strcpy( env_t::user_dir, env_t::base_dir ); } else { strcpy( env_t::user_dir, dr_query_homedir() ); } } } // create directory and subdirectories dr_mkdir(env_t::install_dir); dr_mkdir(env_t::user_dir); // set portable, if base dir and user dir were same not_portable &= strcmp( env_t::user_dir, env_t::base_dir )!=0; dr_chdir( env_t::user_dir ); dr_mkdir("maps"); dr_mkdir(SAVE_PATH); dr_mkdir(SCREENSHOT_PATH); setup_logging(args); // now read last setting (might be overwritten by the tab-files) { loadsave_t settings_file; if( settings_file.rd_open("settings.xml") == loadsave_t::FILE_STATUS_OK ) { if( settings_file.get_version_int()>loadsave_t::int_version(SAVEGAME_VER_NR, NULL ) ) { // too new => remove it settings_file.close(); } else { found_settings = true; env_t::rdwr(&settings_file); env_t::default_settings.rdwr(&settings_file); settings_file.close(); // reset to false (otherwise these settings will persist) env_t::default_settings.reset_after_global_settings_reload(); env_t::server_announce = 0; } } } sint16 disp_width = 0; sint16 disp_height = 0; sint16 fullscreen = WINDOWED; // continue parsing dr_chdir( env_t::base_dir ); if( found_simuconf ) { if(simuconf.open(path_to_simuconf)) { // we do not allow to change the global font name std::string old_fontname = env_t::fontname; std::string old_soundfont_filename = env_t::soundfont_filename; dbg->message("simu_main()", "Parsing %s%s", env_t::base_dir, path_to_simuconf); env_t::default_settings.parse_simuconf( simuconf, disp_width, disp_height, fullscreen, true ); simuconf.close(); if( (old_soundfont_filename.length() > 0) && (strcmp( old_soundfont_filename.c_str(), "Error" ) != 0) ) { // We had a valid soundfont saved by the user, let's restore it env_t::soundfont_filename = old_soundfont_filename; } env_t::fontname = old_fontname; } } // a portable installation could have a personal simuconf.tab in the main dir of simutrans // otherwise it is in ~/simutrans/simuconf.tab env_t::pak_dir.clear(); env_t::pak_name.clear(); string obj_conf = string(env_t::user_dir) + "simuconf.tab"; if (simuconf.open(obj_conf.c_str())) { dbg->message("simu_main()", "Parsing %s", obj_conf.c_str()); env_t::default_settings.parse_simuconf( simuconf, disp_width, disp_height, fullscreen, true ); simuconf.close(); } // env: override previous settings if( args.has_arg("-freeplay") ) { env_t::default_settings.set_freeplay( true ); } #ifdef __ANDROID__ // always save and reload on Android env_t::reload_and_save_on_quit = true; #elif defined(STEAM_BUILT) && STEAM_BUILT != 0 steam_t::get_instance()->install_workshop_items(); #endif // now set the desired objectfilename (override all previous settings) if( const char *fn = args.gimme_arg("-set_pakdir", 1) ) { if(!set_pakdir(fn)) { // try as absolute path char tmp[PATH_MAX]; if( check_and_set_dir( fn, "-set_pakdir", tmp, "config/menuconf.tab" ) ) { env_t::pak_dir = tmp; // to be done => extrat pak name!!! const char *last_pathsep=0; for( char *c=tmp; c[0]>0; c++ ) { if( *c==*PATH_SEPARATOR ) { if( c[1]==0 ) { c[0] = 0; break; } last_pathsep = c+1; } } set_pakdir(last_pathsep); } } else { set_pakdir(fn); } } if( env_t::pak_dir.empty() ) { // old style (deprecated) if( const char* pak = args.gimme_arg( "-objects", 1 ) ) { set_pakdir(pak); } } if( env_t::pak_name.empty() ) { if( const char *filename = args.gimme_arg("-load", 1) ) { // try to get a pak file path from a savegame file // read pak_extension from file loadsave_t test; std::string fn = env_t::user_dir; fn += "save/"; fn += filename; if( test.rd_open(fn.c_str()) == loadsave_t::FILE_STATUS_OK ) { // add pak extension const char *pak = test.get_pak_extension(); if( strcmp(pak,"(unknown)")!=0 ) { set_pakdir(pak); } } } } dr_chdir( env_t::base_dir ); // starting a server? if( args.has_arg("-easyserver") ) { const char *p = args.gimme_arg("-easyserver", 1); int portadress = p ? atoi( p ) : 13353; if( portadress!=0 ) { env_t::server_port = portadress; } // will fail fatal on the opening routine ... dbg->message( "simu_main()", "Server started on port %i", env_t::server_port ); env_t::networkmode = network_init_server( env_t::server_port, env_t::listen ); // query IP and try to open ports on router char IP[256], altIP[256]; altIP[0] = 0; if( prepare_for_server( IP, altIP, env_t::server_port ) ) { // we have forwarded a port in router, so we can continue env_t::server_dns = IP; if( env_t::server_name.empty() ) { env_t::server_name = std::string("Server at ")+IP; } env_t::server_announce = 1; env_t::easy_server = 1; env_t::server_dns = IP; env_t::server_alt_dns = altIP; } } // starting a server? if( !env_t::server ) { if( args.has_arg("-server") ) { const char *p = args.gimme_arg("-server", 1); int portadress = p ? atoi( p ) : 13353; if( portadress!=0 ) { env_t::server_port = portadress; } // will fail fatal on the opening routine ... dbg->message( "simu_main()", "Server started on port %i", env_t::server_port ); env_t::networkmode = network_init_server( env_t::server_port, env_t::listen ); } else { // no announce for clients ... env_t::server_announce = 0; } } // enabling heavy-mode for debugging network games env_t::network_heavy_mode = 0; if( args.has_arg("-heavy") ) { int heavy = atoi(args.gimme_arg("-heavy", 1)); env_t::network_heavy_mode = clamp(heavy, 0, 2); } DBG_MESSAGE("simu_main()", "Version: " VERSION_NUMBER " Date: " VERSION_DATE); DBG_MESSAGE("simu_main()", "Debuglevel: %i", env_t::verbose_debug); DBG_MESSAGE("simu_main()", "base_dir: %s", env_t::base_dir); DBG_MESSAGE("simu_main()", "install_dir: %s", env_t::install_dir); DBG_MESSAGE("simu_main()", "user_dir: %s", env_t::user_dir); DBG_MESSAGE("simu_main()", "locale: %s", dr_get_locale_string()); #ifdef DEBUG if (args.has_arg("-sizes")) { // show the size of some structures ... show_sizes(); } #endif static const sint16 resolutions[][2] = { { 640, 480 }, { 800, 600 }, { 1024, 768 }, { 1280, 1024 }, { 704, 560 } // try to force window mode with allegro }; // likely only the program without graphics was downloaded if (args.has_arg("-res")) { const char* res_str = args.gimme_arg("-res", 1); const int res = *res_str - '1'; switch (res) { case 0: case 1: case 2: case 3: case 4: case 5: fullscreen = (res<=4); disp_width = resolutions[res][0]; disp_height = resolutions[res][1]; break; default: fprintf(stderr, "Invalid resolution, argument must be in 1..5\n" "1=640x480, 2=800x600, 3=1024x768, 4=1280x1024, 5=windowed\n" ); return EXIT_FAILURE; } } if ( !fullscreen ) { fullscreen = args.has_arg("-fullscreen") ? FULLSCREEN : WINDOWED; } if ( !fullscreen ) { fullscreen = args.has_arg("-borderless") ? BORDERLESS : WINDOWED; } if ( !fullscreen ) { fullscreen = env_t::fullscreen; } if(args.has_arg("-screensize")) { const char* res_str = args.gimme_arg("-screensize", 1); int n = 0; if (res_str != NULL) { n = sscanf(res_str, "%hdx%hd", &disp_width, &disp_height); } if (n != 2) { fprintf(stderr, "Invalid argument for -screensize option\n" "Argument must be of format like 800x600\n" ); return EXIT_FAILURE; } } int parameter[2]; parameter[0] = args.has_arg("-async"); parameter[1] = args.has_arg("-use_hw"); if (!dr_os_init(parameter)) { dr_fatal_notify("Failed to initialize backend.\n"); return EXIT_FAILURE; } if (args.has_arg("-autodpi")) { dr_set_screen_scale(-1); } else if (const char* scaling = args.gimme_arg("-screen_scale", 1)) { if (scaling[0] >= '0' && scaling[0] <= '9') { dr_set_screen_scale(atoi(scaling)); } } // Get optimal resolution. if (disp_width == 0 || disp_height == 0) { resolution const res = dr_query_screen_resolution(); if (fullscreen != WINDOWED) { disp_width = res.w; disp_height = res.h; } else { disp_width = min(704, res.w); disp_height = min(560, res.h); } } simgraph_type_t preferred_renderer_type = #if COLOUR_DEPTH == 0 SIMGRAPH_TYPE_NULL; #else SIMGRAPH_TYPE_SOFTWARE; #endif gfx = simgraph_select(preferred_renderer_type); DBG_MESSAGE("simu_main()", "simgraph_init disp_width=%d, disp_height=%d, fullscreen=%d", disp_width, disp_height, fullscreen); if (!gfx || !gfx->init(scr_size(disp_width, disp_height), fullscreen)) { dbg->error("simu_main()", "Failed to initialize graphics system."); return EXIT_FAILURE; } const scr_size screen = gfx->get_screen_size(); DBG_MESSAGE("simu_main()", ".. results in disp_width=%d, disp_height=%d", screen.w, screen.h); // now that the graphics system has already started // the saved colours can be converted to the system format gfx->env_t_rgb_to_system_colors(); // parse colours now that the graphics system has started // default simuconf.tab if( found_simuconf ) { if(simuconf.open(path_to_simuconf)) { dbg->message("simu_main()", "Loading colours from %sconfig/simuconf.tab", env_t::base_dir); // we do not allow to change the global font name also from the pakset ... std::string old_fontname = env_t::fontname; env_t::default_settings.parse_colours( simuconf ); simuconf.close(); env_t::fontname = old_fontname; } } // a portable installation could have a personal simuconf.tab in the main dir of simutrans // otherwise it is in ~/simutrans/simuconf.tab obj_conf = string(env_t::user_dir) + "simuconf.tab"; if (simuconf.open(obj_conf.c_str())) { dbg->message("simu_main()", "Parsing %s", obj_conf.c_str() ); env_t::default_settings.parse_colours( simuconf ); simuconf.close(); } // prepare skins first bool themes_ok = false; if (const char* themestr = args.gimme_arg("-theme", 1)) { dr_chdir(env_t::user_dir); dr_chdir("themes"); themes_ok = gui_theme_t::themes_init(themestr, true, false); if (!themes_ok) { dr_chdir(env_t::base_dir); dr_chdir("themes"); themes_ok = gui_theme_t::themes_init(themestr, true, false); } } // next try the last used theme skinverwaltung_t::save_all_skins(); // save empty to have the pakset default if( !themes_ok && env_t::default_theme.c_str()!=NULL ) { dr_chdir( env_t::user_dir ); dr_remove("settings_old.xml"); dr_rename("settings.xml", "settings_old.xml"); // or we are stuck on a broken theme forever dr_chdir( "themes" ); themes_ok = gui_theme_t::themes_init( env_t::default_theme, true, false ); if( !themes_ok ) { dr_chdir( env_t::base_dir ); dr_chdir( "themes" ); themes_ok = gui_theme_t::themes_init( env_t::default_theme, true, false ); } dr_rename("settings_old.xml", "settings.xml"); // or we are stuck on a broken theme forever } // specified themes not found => try default themes #if COLOUR_DEPTH != 0 if( !themes_ok ) { dr_chdir( env_t::base_dir ); dr_chdir( "themes" ); #ifndef __ANDROID__ themes_ok = gui_theme_t::themes_init("themes.tab",true,false); #else themes_ok = gui_theme_t::themes_init("theme-large.tab", true, false); #endif } if( !themes_ok ) { dbg->fatal( "simu_main()", "No GUI themes found! Please re-install!" ); } dr_chdir( env_t::base_dir ); // The loading screen needs to be initialized gfx->set_cursor_visible(true); // if no object files given, we ask the user int retries=0; while( env_t::pak_name.empty() && retries < 2 ) { if( ask_objfilename() == 0 ) { retries++; } if( env_t::quit_simutrans ) { gfx->exit(); return EXIT_SUCCESS; } else if (env_t::pak_name.empty()) { // try to download missing paks install_objfilename(); // all other } } #else // headless server dr_chdir( env_t::base_dir ); if( env_t::pak_name.empty() ) { dr_fatal_notify( "*** No pak set found ***\n" "\n" "Please install a pak set and select it using the '-objects'\n" "command line parameter or the 'pak_file_path' simuconf.tab entry."); gfx->exit(); return EXIT_FAILURE; } #endif // check for valid pak path { FILE* const f = dr_fopen((env_t::pak_dir+"ground.Outside.pak").c_str(), "r"); if( !f ) { cbuffer_t errmsg; errmsg.printf( "The file 'ground.Outside.pak' was not found in\n" "'%s'.\n" "This file is required for a valid pak set (graphics).\n" "Please install and select a valid pak set.", env_t::pak_dir.c_str()); dr_fatal_notify(errmsg); gfx->exit(); return EXIT_FAILURE; } fclose(f); } // now find the pak specific tab file ... obj_conf = env_t::pak_dir + "config" + PATH_SEPARATOR + "simuconf.tab"; if (simuconf.open(obj_conf.c_str())) { env_t::default_settings.set_way_height_clearance( 0 ); uint8 show_month = env_t::env_t::show_month; dbg->message("simu_main()", "Parsing %s", obj_conf.c_str()); env_t::default_settings.parse_simuconf( simuconf ); env_t::default_settings.parse_colours( simuconf ); if (show_month != env_t::show_month) { dbg->warning("Parsing simuconf.tab", "Pakset %s defines show_month will be ignored!", env_t::pak_name.c_str()); } env_t::show_month = show_month; pak_diagonal_multiplier = env_t::default_settings.get_pak_diagonal_multiplier(); pak_height_conversion_factor = env_t::pak_height_conversion_factor; pak_tile_height = TILE_HEIGHT_STEP; if( env_t::default_settings.get_way_height_clearance() == 0 ) { // ok, set default as conversion factor env_t::default_settings.set_way_height_clearance( pak_height_conversion_factor ); } simuconf.close(); } // and parse again the user settings obj_conf = string(env_t::user_dir) + "simuconf.tab"; if (simuconf.open(obj_conf.c_str())) { dbg->message("simu_main()", "Parsing %s", obj_conf.c_str()); env_t::default_settings.parse_simuconf( simuconf ); env_t::default_settings.parse_colours( simuconf ); simuconf.close(); } // load with private addons (now in addons/pak-name either in simutrans main dir or in userdir) if( args.gimme_arg("-objects", 1) || args.gimme_arg("-set_pakdir", 1) ) { if(args.has_arg("-addons")) { env_t::default_settings.set_with_private_paks( true ); } if(args.has_arg("-noaddons")) { env_t::default_settings.set_with_private_paks( false ); } } // parse ~/simutrans/pakxyz/config.tab" if( env_t::default_settings.get_with_private_paks() ) { obj_conf = string(env_t::user_dir) + "addons/" + env_t::pak_name + "config/simuconf.tab"; if (simuconf.open(obj_conf.c_str())) { dbg->message("simu_main()", "Parsing %s", obj_conf.c_str()); env_t::default_settings.parse_simuconf( simuconf ); env_t::default_settings.parse_colours( simuconf ); simuconf.close(); } // and parse user settings again ... obj_conf = string(env_t::user_dir) + "simuconf.tab"; if (simuconf.open(obj_conf.c_str())) { dbg->message("simu_main()", "Parsing %s", obj_conf.c_str()); env_t::default_settings.parse_simuconf( simuconf ); env_t::default_settings.parse_colours( simuconf ); simuconf.close(); } } // now (re)set the correct length and other pak set only settings env_t::default_settings.set_pak_diagonal_multiplier( pak_diagonal_multiplier ); vehicle_base_t::set_diagonal_multiplier( pak_diagonal_multiplier, pak_diagonal_multiplier ); env_t::pak_height_conversion_factor = pak_height_conversion_factor; TILE_HEIGHT_STEP = pak_tile_height; convoihandle_t::init( 1024 ); linehandle_t::init( 1024 ); halthandle_t::init( 1024 ); #ifdef MULTI_THREAD // set number of threads if( const char *ref_str = args.gimme_arg("-threads", 1) ) { uint8 want_threads = atoi(ref_str); env_t::num_threads = clamp( want_threads, (uint8)1u, min(dr_get_max_threads(), MAX_THREADS) ); env_t::num_threads = min( env_t::num_threads, MAX_THREADS ); dbg->message("simu_main()","Requested %d threads.", env_t::num_threads ); } #else if( env_t::num_threads > 1 ) { env_t::num_threads = 1; dbg->message("simu_main()","Multithreading not enabled: threads = %d ignored.", env_t::num_threads ); } #endif // just check before loading objects if( !args.has_arg("-nosound") && dr_init_sound() ) { dbg->message("simu_main()","Reading sound data ..."); sound_desc_t::init(env_t::pak_dir, std::string("addons") + PATH_SEPARATOR + env_t::pak_name); } else { sound_set_mute(true); } // Adam - Moved away loading from simu_main() and placed into translator for better modularization if( !translator::load() ) { // installation error: likely only program started dbg->fatal("simu_main()", "Unable to load any language files\n" "*** PLEASE INSTALL PROPER BASE FILES ***\n" "\n" "either run ./get_lang_files.sh\n" "\n" "or\n" "\n" "download a complete simutrans archive and put the text/ folder here." ); } // use requested language (if available) if( args.gimme_arg("-lang", 1) ) { const char *iso = args.gimme_arg("-lang", 1); if( strlen(iso)>=2 ) { translator::set_language( iso ); } if( translator::get_language()==-1 ) { dbg->fatal("simu_main()", "Illegal language definition \"%s\"", iso ); } env_t::language_iso = translator::get_lang()->iso_base; } else if( found_settings ) { translator::set_language( env_t::language_iso ); } // simgraph_init loads default fonts, now we need to load (if not set otherwise) // sprachengui_t::init_font_from_lang( strcmp(env_t::fontname.c_str(), FONT_PATH_X "prop.fnt")==0 ); dr_chdir(env_t::base_dir); dbg->message("simu_main()","Reading city configuration ..."); stadt_t::cityrules_init(); dbg->message("simu_main()","Reading speedbonus configuration ..."); vehicle_builder_t::speedbonus_init(); dbg->message("simu_main()","Reading menu configuration ..."); tool_t::init_menu(); // loading all objects in the pak skinverwaltung_t::restore_all_skins(); // restore empty skins to have the pakset default #if DEBUG uint32 pakload_time = dr_time(); #endif pakset_manager_t::load_pakset(env_t::default_settings.get_with_private_paks()); #if DEBUG pakload_time = dr_time() - pakload_time; DBG_DEBUG("simmain", "Loading paksets took %u ms", pakload_time); #endif skinverwaltung_t::save_all_skins(); // save pakset default // load tool scripts dbg->message("simu_main()","Reading tool scripts ..."); dr_chdir( env_t::base_dir ); script_tool_manager_t::load_scripts((env_t::pak_dir + "tool/").c_str()); if( env_t::default_settings.get_with_private_paks() ) { dr_chdir( env_t::user_dir ); script_tool_manager_t::load_scripts(("addons/" + env_t::pak_name + "tool/").c_str()); } dbg->message("simu_main()","Reading menu configuration ..."); dr_chdir( env_t::base_dir ); if (!tool_t::read_menu(env_t::pak_dir + "config/menuconf.tab")) { // Fatal error while reading menuconf.tab, we cannot continue! dbg->fatal( "Could not read %sconfig/menuconf.tab.\n" "This file is required for a valid pak set (graphics).\n" "Please install and select a valid pak set.", env_t::pak_dir.c_str()); } #if COLOUR_DEPTH != 0 // reread theme dr_chdir( env_t::user_dir ); dr_chdir( "themes" ); themes_ok = gui_theme_t::themes_init( env_t::default_theme, true, false ); if( !themes_ok ) { dr_chdir( env_t::base_dir ); dr_chdir( "themes" ); themes_ok = gui_theme_t::themes_init( env_t::default_theme, true, false ); } #endif dr_chdir( env_t::base_dir ); if( translator::get_language()==-1 ) { // try current language const char* loc = dr_get_locale(); if( loc==NULL || !translator::set_language( loc ) ) { loc = dr_get_locale_string(); if( loc==NULL || !translator::set_language( loc ) ) { ask_language(); } else { sprachengui_t::init_font_from_lang(); } } else { sprachengui_t::init_font_from_lang(); } } #if COLOUR_DEPTH != 0 // now find out if there is a valid font { const utf8 *new_world = (const utf8 *)translator::translate("Beenden"); size_t len; testfor_this_character = utf8_decoder_t::decode(new_world,len); if (!gfx->font_has_character(testfor_this_character)) { // not valid => show font selector loadfont_frame_t* sel = new loadfont_frame_t(); destroy_all_win(true); // since eventually the successful load message is still there .... modal_dialogue(sel, magic_none, NULL, no_font); destroy_win(sel); } } #endif bool new_world = true; std::string loadgame; bool pause_after_load = false; if( args.has_arg("-pause") ) { if( env_t::server ) { env_t::pause_server_no_clients = true; } else { pause_after_load = true; } } if( args.has_arg("-load") ) { cbuffer_t buf; dr_chdir( env_t::user_dir ); /** * Added automatic adding of extension */ const char *name = args.gimme_arg("-load", 1); if (strstart(name, "net:")) { buf.append( name ); } else { buf.printf( SAVE_PATH_X "%s", searchfolder_t::complete(name, "sve").c_str() ); } dbg->message("simu_main()", "Loading savegame \"%s\"", name ); loadgame = buf; new_world = false; } // compare two savegames if (!new_world && strstart(loadgame.c_str(), "net:")==NULL && args.has_arg("-compare")) { cbuffer_t buf; dr_chdir( env_t::user_dir ); const char *name = args.gimme_arg("-compare", 1); buf.printf( SAVE_PATH_X "%s", searchfolder_t::complete(name, "sve").c_str() ); // open both files loadsave_t file1, file2; if (file1.rd_open(loadgame.c_str()) == loadsave_t::FILE_STATUS_OK && file2.rd_open(buf) == loadsave_t::FILE_STATUS_OK) { karte_t *welt = new karte_t(); compare_loadsave_t compare(&file1, &file2); welt->load(&compare); delete welt; file1.close(); file2.close(); } } // recover last server game if( new_world && env_t::server ) { dr_chdir( env_t::user_dir ); loadsave_t file; static char servername[128]; sprintf( servername, "server%d-network.sve", env_t::server ); // try recover with the latest savegame if( file.rd_open(servername) == loadsave_t::FILE_STATUS_OK ) { // compare pakset (objfilename has trailing path separator, pak_extension not) if (strstart(env_t::pak_name.c_str(), file.get_pak_extension())) { // same pak directory - load this loadgame = servername; new_world = false; } file.close(); } } bool old_restore_UI = env_t::restore_UI; if( new_world && env_t::reload_and_save_on_quit ) { // construct from pak name an autosave if requested std::string pak_name( "autosave-" ); pak_name.append( env_t::pak_name ); pak_name.erase( pak_name.length()-1 ); pak_name.append( ".sve" ); dr_chdir( env_t::user_dir ); unlink( "temp-load.sve" ); if( dr_rename(pak_name.c_str(), "temp-load.sve") == 0 ) { loadgame = "temp-load.sve"; new_world = false; } else { // test if rejoin server game pak_name.erase(pak_name.length() - 3); pak_name.append("net"); if (dr_rename(pak_name.c_str(), "temp-load.sve") == 0) { if (FILE *f = dr_fopen("temp-load.sve", "r")) { char servername[2048]; fgets(servername, 2048, f); fclose(f); loadgame = servername; new_world = false; } } } env_t::restore_UI = true; } // still nothing to be loaded => search for demo games if( new_world ) { dr_chdir( env_t::base_dir ); const std::string path = env_t::pak_dir + "demo.sve"; // access did not work! if( FILE *const f = dr_fopen(path.c_str(), "rb") ) { // there is a demo game to load loadgame = path; fclose(f); DBG_MESSAGE("simu_main()","loadgame file found at %s",path.c_str()); } } if( args.has_arg("-timeline") ) { const char* ref_str = args.gimme_arg("-timeline", 1); if( ref_str != NULL ) { env_t::default_settings.set_use_timeline( atoi(ref_str) ); } } if( args.has_arg("-startyear") ) { const char * ref_str = args.gimme_arg("-startyear", 1); //1930 if( ref_str != NULL ) { env_t::default_settings.set_starting_year( clamp(atoi(ref_str),1,2999) ); } } // now always writing in user dir (which points to the data dir in multiuser mode) dr_chdir( env_t::user_dir ); // init midi before loading sounds if( dr_init_midi() ) { dbg->message("simu_main()","Reading midi data ..."); if( !midi_init( env_t::pak_dir.c_str() ) && !midi_init( env_t::user_dir ) && !midi_init( env_t::base_dir ) ) { midi_set_mute(true); dbg->message("simu_main()","Midi disabled ..."); } if(args.has_arg("-nomidi")) { midi_set_mute(true); } #ifdef USE_FLUIDSYNTH_MIDI // Audio is ok, but we failed to find a soundfont if( strcmp( env_t::soundfont_filename.c_str(), "Error" ) == 0 ) { midi_set_mute(true); } #endif } else { dbg->message("simu_main()","Midi disabled ..."); midi_set_mute(true); } if( args.has_arg("-mute") ) { sound_set_mute(true); midi_set_mute(true); } // restore previous sound settings ... sound_set_mute( env_t::global_mute_sound || sound_get_mute() ); midi_set_mute( env_t::mute_midi || midi_get_mute() ); sound_set_shuffle_midi( env_t::shuffle_midi!=0 ); sound_set_global_volume( env_t::global_volume ); sound_set_midi_volume( env_t::midi_volume ); if( !midi_get_mute() ) { // not muted => play random song midi_play( env_t::shuffle_midi ? -1 : 0 ); // reset volume after first play call else no/low sound or music with win32 and sdl sound_set_midi_volume( env_t::midi_volume ); } karte_t *welt = new karte_t(); main_view_t *view = new main_view_t(welt); welt->set_view( view ); interaction_t *eventmanager = new interaction_t(welt->get_viewport()); welt->set_eventmanager( eventmanager ); // some messages about old vehicle may appear ... welt->get_message()->set_message_flags(0, 0, 0, 0); // set the frame per second if( const char *ref_str = args.gimme_arg("-fps", 1) ) { const int want_refresh = atoi(ref_str); env_t::fps = clamp(want_refresh, (int)env_t::min_fps, (int)env_t::max_fps); } // query server stuff // Enable server announcements if( args.has_arg("-announce") ) { env_t::server_announce = 1; DBG_DEBUG( "simu_main()", "Server will be announced." ); } if( const char *ref_str = args.gimme_arg("-server_dns", 1) ) { env_t::server_dns = ref_str; DBG_DEBUG( "simu_main()", "Server IP set to '%s'.", ref_str ); } if( const char *ref_str = args.gimme_arg("-server_altdns", 1) ) { env_t::server_alt_dns = ref_str; DBG_DEBUG( "simu_main()", "Server IP set to '%s'.", ref_str ); } if( const char *ref_str = args.gimme_arg("-server_name", 1) ) { env_t::server_name = ref_str; DBG_DEBUG( "simu_main()", "Server name set to '%s'.", ref_str ); } if( const char *ref_str = args.gimme_arg("-server_admin_pw", 1) ) { env_t::server_admin_pw = ref_str; } if( env_t::server_dns.empty() && !env_t::server_alt_dns.empty() ) { dbg->warning( "simu_main()", "server_altdns but not server_dns set. Please use server_dns first!" ); env_t::server_dns = env_t::server_alt_dns; env_t::server_alt_dns.clear(); } dr_chdir(env_t::user_dir); // reset random counter to true randomness setsimrand(dr_time(), dr_time()); clear_random_mode( 7 ); // allow all scenario_t *scen = NULL; if( const char *scen_name = args.gimme_arg("-scenario", 1) ) { scen = new scenario_t(welt); intr_set_view(view); win_set_world(welt); const char *err = ""; if (env_t::default_settings.get_with_private_paks()) { // try addon directory first err = scen->init(("addons/" + env_t::pak_name + "scenario/").c_str(), scen_name, welt); } if (err) { // no addon scenario, look in pakset err = scen->init((env_t::pak_dir + "scenario/").c_str(), scen_name, welt); } if( err ) { dbg->error("simu_main()", "Could not load scenario %s%s: %s", env_t::pak_name.c_str(), scen_name, err); delete scen; scen = NULL; } else { new_world = false; } } if( scen == NULL && (loadgame=="" || !welt->load(loadgame.c_str())) ) { // create a default map DBG_MESSAGE("simu_main()", "Init with default map (failing will be a pak error!)"); // no autosave on initial map during the first six months loadgame = ""; new_world = true; sint32 old_autosave = env_t::autosave; env_t::autosave = false; settings_t sets; sets.copy_city_road( env_t::default_settings ); sets.set_default_climates(); sets.set_use_timeline( 1 ); sets.set_size(64,64); sets.set_city_count(1); sets.set_factory_count(3); sets.set_tourist_attractions(1); sets.set_traffic_level(7); welt->init(&sets,0); // start in June ... intr_set_view(view); win_set_world(welt); tool_t::toolbar_tool[0]->init(welt->get_active_player()); welt->set_fast_forward(true); welt->sync_step(5000); welt->step_month(5); welt->step(); welt->step(); env_t::autosave = old_autosave; } else { // override freeplay setting when provided on command line if( (args.has_arg("-freeplay")) ) { welt->get_settings().set_freeplay( true ); } if (scen == NULL) { // just init view (world was loaded from file) intr_set_view(view); win_set_world(welt); welt->type_of_generation = karte_t::RESTORED_WORLD; } tool_t::toolbar_tool[0]->init(welt->get_active_player()); if (env_t::networkmode) { // try to restore windows loadsave_t file; std::string name("autosave-"); name.append(env_t::pak_name); name.erase(name.length() - 1); name.append(".net.sve"); dr_remove("temp-load.sve"); // we load under tempary name if there was a crash => no reloading next time if (!dr_rename(name.c_str(),"temp-load.sve") && file.rd_open("temp-load.sve") == loadsave_t::FILE_STATUS_OK) { uint8 pn; intr_disable(); file.rdwr_byte(pn); welt->switch_active_player(pn, true); rdwr_all_win(&file); intr_enable(); } // rename back, so we could get the same windows after a desync (when using the commandline to join) dr_rename( "temp-load.sve", name.c_str() ); } } welt->set_fast_forward(false); baum_t::recalc_outline_color(); uint32 quit_month = 0x7FFFFFFFu; #if defined DEBUG || defined PROFILE // do a render test? if (args.has_arg("-times")) { show_times(welt, view); } #endif // finish after a certain month? (must be entered decimal, i.e. 12*year+month if( args.has_arg("-until") ) { const char *until = args.gimme_arg("-until", 1); int year = -1, month = -1; if ( sscanf(until, "%i.%i", &year, &month) == 2) { quit_month = month+year*12-1; } else { quit_month = atoi(until); } welt->set_fast_forward(true); } welt->reset_timer(); if( !env_t::networkmode && !env_t::server ) { #ifdef display_in_main view->display(true); intr_refresh_display(true); #endif intr_enable(); } else { intr_disable(); } #ifdef USE_SOFTPOINTER // give user a mouse to work with if (skinverwaltung_t::mouse_cursor != NULL) { // we must use our softpointer (only Allegro!) display_set_pointer(skinverwaltung_t::mouse_cursor->get_image_id(0)); } #endif gfx->set_cursor_visible(true); gfx->set_show_load_cursor(false); welt->set_dirty(); // simgraph_init loads default fonts, now we need to load // the real fonts for the current language, if not set otherwise sprachengui_t::init_font_from_lang(); if( !(env_t::reload_and_save_on_quit && !new_world) ) { destroy_all_win(true); } env_t::restore_UI = old_restore_UI; if( !env_t::networkmode && !env_t::server && new_world ) { welt->get_message()->clear(); welt->get_chat_message()->clear(); } #ifdef USE_FLUIDSYNTH_MIDI if( strcmp( env_t::soundfont_filename.c_str(), "Error" ) == 0 ) { create_win({ 0,0 }, new news_img("No soundfont found!\n\nMusic won't play until you load a soundfont from the sound options menu."), w_info, magic_none ); } #endif simachievements_t::check_pakset_ach(); while( !env_t::quit_simutrans ) { // play next tune? check_midi(); #if COLOUR_DEPTH != 0 if( new_world ) { dbg->message("simu_main()", "Show banner ... " ); ticker::add_msg("Welcome to Simutrans", koord3d::invalid, PLAYER_FLAG | 1); modal_dialogue( new banner_t(), magic_none, welt, never_quit, true ); // only show new world, if no other dialogue is active ... new_world = win_get_open_count()==0; } #endif // to purge all previous old messages welt->get_message()->set_message_flags(env_t::message_flags[0], env_t::message_flags[1], env_t::message_flags[2], env_t::message_flags[3]); if (pakset_manager_t::needs_doubled_warning_message()) { welt->get_message()->add_message(pakset_manager_t::get_doubled_warning_message(), koord3d::invalid, message_t::general | message_t::DO_NOT_SAVE_MSG); } if( !env_t::networkmode && !env_t::server ) { welt->set_pause( pause_after_load ); pause_after_load = false; } dbg->message("simu_main()", "Running world, pause=%i, fast forward=%i ... ", welt->is_paused(), welt->is_fast_forward() ); loadgame = ""; // only first time // run the loop welt->interactive(quit_month); new_world = true; welt->get_message()->get_message_flags(&env_t::message_flags[0], &env_t::message_flags[1], &env_t::message_flags[2], &env_t::message_flags[3]); welt->set_fast_forward(false); welt->set_pause(false); setsimrand(dr_time(), dr_time()); dbg->message("simu_main()", "World finished ..." ); } intr_disable(); // save settings { dr_chdir( env_t::user_dir ); loadsave_t settings_file; if( settings_file.wr_open("settings.xml",loadsave_t::xml,0,"settings only/",SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK ) { env_t::rdwr(&settings_file); env_t::default_settings.rdwr(&settings_file); settings_file.close(); } } destroy_all_win( true ); tool_t::exit_menu(); delete welt; welt = NULL; delete view; view = NULL; delete eventmanager; eventmanager = NULL; remove_port_forwarding( env_t::server ); network_core_shutdown(); gfx->exit(); close_midi(); #if 0 // free all list memories (not working, since there seems to be unitialized list still waiting for automated destruction) freelist_t::free_all_nodes(); #endif #ifdef STEAM_BUILT steam_t::get_instance()->shutdown(); #endif return EXIT_SUCCESS; } simutrans-124.5/src/simutrans/simmain.h000066400000000000000000000005071520675460500202100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMMAIN_H #define SIMMAIN_H // search for this in all possible pakset locations for the directory chkdir and take the first match bool set_pakdir(const char* chkdir); int simu_main(int argc, char** argv); #endif simutrans-124.5/src/simutrans/simmem.cc000066400000000000000000000013431520675460500201770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include // since BeOS needs size_t from there ... #include #include "simmem.h" #include "simdebug.h" void* xmalloc(size_t const size) { void* const p = malloc(size); if (!p) { // use unified error handler instead, since BeOS need this as C style file! dbg->fatal("xmalloc()", "Could not alloc %li bytes.", (long)size ); } return p; } void* xrealloc(void* const ptr, size_t const size) { void* const p = realloc(ptr, size); if (!p) { // use unified error handler instead, since BeOS need this as C style file! dbg->fatal("realloc()", "Could not alloc %li bytes.", (long)size ); } return p; } simutrans-124.5/src/simutrans/simmem.h000066400000000000000000000014141520675460500200400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMMEM_H #define SIMMEM_H #include void* xmalloc(size_t size); // Throws std::bad_alloc on failure void* xrealloc(void * const ptr, size_t size); // Throws std::bad_alloc on failure #define MALLOC(type) ((type*)xmalloc(sizeof(type))) // Allocate an object of a certain type #define MALLOCN(type, n) ((type*)xmalloc(sizeof(type) * (n))) // Allocate n objects of a certain type #define MALLOCF(type, member, n) ((type*)xmalloc(offsetof(type, member) + sizeof(*((type*)0)->member) * (n))) #define REALLOC(ptr, type, n) (type*)xrealloc((void *)ptr, sizeof(type) * (n)) // Reallocate n objects of a certain type #endif simutrans-124.5/src/simutrans/simmesg.cc000066400000000000000000000323421520675460500203570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "macros.h" #include "simdebug.h" #include "simmesg.h" #include "simticker.h" #include "display/simgraph.h" #include "simcolor.h" #include "gui/chat_frame.h" #include "gui/simwin.h" #include "world/simworld.h" #include "dataobj/loadsave.h" #include "dataobj/translator.h" #include "dataobj/environment.h" #include "dataobj/pakset_manager.h" #include "network/network_socket_list.h" #include "player/simplay.h" #include "utils/simstring.h" #include "tpl/slist_tpl.h" #include "gui/messagebox.h" #include #include #include "simsound.h" #include "descriptor/sound_desc.h" #define MAX_SAVED_MESSAGES (2000) static karte_ptr_t welt; static vector_tplclients; uint32 message_node_t::get_type_shifted() const { return 1<<(type & message_t::MESSAGE_TYPE_MASK); } void message_node_t::rdwr(loadsave_t *file) { file->rdwr_str( msg, lengthof(msg) ); file->rdwr_long( type ); if (file->is_version_less(123, 2)) { // only 2d coordinates koord k = pos.get_2d(); k.rdwr(file); if (file->is_loading()) { pos = koord3d::invalid; if (k != koord::invalid) { // query ground tile (world is loaded at this point) if (grund_t *gr = welt->lookup_kartenboden(k)) { pos = gr->get_pos(); } } } } else { pos.rdwr(file); } if (file->is_version_less(120, 5)) { // color was 16bit, with 0x8000 indicating player colors uint16 c = color & PLAYER_FLAG ? 0x8000 + (color&(~PLAYER_FLAG)) : MN_GREY0; file->rdwr_short(c); color = c & 0x8000 ? PLAYER_FLAG + (c&(~0x8000)) : gfx->palette_lookup(c); } else { file->rdwr_long( color ); } file->rdwr_long( time ); if( file->is_loading() ) { image = IMG_EMPTY; } } PIXVAL message_node_t::get_player_color(karte_t *welt) const { // correct for player color FLAGGED_PIXVAL colorval = color; if( color&PLAYER_FLAG ) { player_t *player = welt->get_player(color&(~PLAYER_FLAG)); colorval = player ? PLAYER_FLAG+gfx->palette_lookup(player->get_player_color1()+env_t::gui_player_color_dark) : gfx->palette_lookup(MN_GREY0); } return (PIXVAL)colorval; } void message_node_t::open_msg_window(bool open_as_autoclose) const { // show window with the complete warning if (strcmp(msg, pakset_manager_t::get_doubled_warning_message()) == 0) { pakset_manager_t::open_doubled_warning_window(); return; } // message window again news_window* news; if( pos==koord3d::invalid ) { news = new news_img( msg, image, get_player_color(welt) ); } else { news = new news_loc( msg, pos, get_player_color(welt) ); } create_win(news, open_as_autoclose ? w_time_delete /* autoclose window */ : w_info /* normal window */, magic_none); } message_t::message_t() { ticker_flags = 0xFF7F; // everything on the ticker only win_flags = 0; auto_win_flags = 0; ignore_flags = 0; win_flags = 256+8; auto_win_flags = 128+512; } message_t::~message_t() { clear(); } void message_t::clear() { while (!list.empty()) { delete list.remove_first(); } ticker::clear_messages(); clients.clear(); clients.append(env_t::nickname.c_str()); } /* get flags for message routing */ void message_t::get_message_flags( sint32 *t, sint32 *w, sint32 *a, sint32 *i) { *t = ticker_flags; *w = win_flags; *a = auto_win_flags; *i = ignore_flags; } /* set flags for message routing */ void message_t::set_message_flags( sint32 t, sint32 w, sint32 a, sint32 i) { ticker_flags = t; win_flags = w; auto_win_flags = a; ignore_flags = i; } /** * Add a message to the message list * * @param text * @param pos position of the event * @param color message color * @param what_flags type of message * @param image image associated with message (will be ignored if pos!=koord3d::invalid) */ void message_t::add_message(const char *text, koord3d pos, uint16 what_flags, FLAGGED_PIXVAL color, image_id image ) { DBG_MESSAGE("message_t::add_msg()","%40s (at %i,%i,%i)", text, pos.x, pos.y, pos.z ); sint32 what_bit = 1<<(what_flags & MESSAGE_TYPE_MASK); if( what_bit&ignore_flags ) { // wants us to ignore this completely return; } /* we will not add traffic jam messages two times to the list * if it was within the last 20 messages * or within last months * and is not a general (BLACK) message */ if( what_bit == (1<get_current_month()-2; uint32 i = 0; for(message_node_t* const iter : list) { message_node_t const& n = *iter; if (n.time >= now && strcmp(n.msg, text) == 0 && (n.pos.x & 0xFFF0) == (pos.x & 0xFFF0) && // positions need not 100% match ... (n.pos.y & 0xFFF0) == (pos.y & 0xFFF0)) { // we had exactly this message already return; } if (++i == 20) break; } } // filter out AI messages for a similar area to recent activity messages if( what_bit == (1<msg, text, lengthof(n->msg)); n->type = what_flags; n->pos = pos; n->color = color; n->time = welt->get_current_month(); n->image = image; // should we send this message to a ticker? if( what_bit&ticker_flags ) { ticker::add_msg_node(*n); } // insert at the top list.insert(n); // if we are not current player, do not open windows if( (what_bit&(1<get_active_player_nr() != (color&(~PLAYER_FLAG)) ) { return; } // check if some window has focus gui_frame_t *old_top = win_get_top(); // should we open a window? if ( what_bit & (auto_win_flags | win_flags) ) { n->open_msg_window( (what_bit & win_flags)==0 ); } // restore focus if( old_top ) { top_win( old_top, true ); } } koord3d message_t::get_coord_from_text(const char* text) { koord3d pos(koord3d::invalid); const char *str = text; // scan until either @ or ( are found while( *(str += strcspn(str, "@(")) ) { str += 1; int x=-1, y=-1, z=0; int read_coords = sscanf(str, "%d,%d,%d", &x, &y, &z); if (read_coords >= 2 && welt->is_within_limits(x,y)) { if (read_coords == 3) { pos.x = x; pos.y = y; pos.z = z; } else { pos = welt->lookup_kartenboden(x,y)->get_pos(); } break; // success } } return pos; } void message_t::rotate90( sint16 size_w ) { for(message_node_t* const i : list) { i->pos.rotate90(size_w); } } void message_t::rdwr(loadsave_t* file) { uint16 msg_count; if (file->is_saving()) { msg_count = 0; vector_tplmsg_to_save; msg_to_save.reserve(list.get_count()); if (env_t::server) { // do not save local messages and expired messages uint32 current_time = world()->get_current_month(); for (message_node_t* const m : list) { if (m->type & DO_NOT_SAVE_MSG || (m->type & EXPIRE_AFTER_ONE_MONTH_MSG && current_time - m->time > 1)) { continue; } msg_to_save.append(m); if (msg_to_save.get_count() == MAX_SAVED_MESSAGES) { // just the newest messages (which are at the front) break; } } } else { // do not save discardable messages (like changing player) for (message_node_t* const m : list) { if (!(m->type & DO_NOT_SAVE_MSG)) { msg_to_save.append(m); if (msg_to_save.get_count() == MAX_SAVED_MESSAGES) { // just the newest messages (which are at the front) break; } } } } // save only the last MAX_SAVED_MESSAGES msg_count = (uint16)msg_to_save.get_count(); file->rdwr_short(msg_count); for (message_node_t* const m : msg_to_save) { m->rdwr(file); } } else { // loading clear(); file->rdwr_short(msg_count); if (file->is_version_less(124, 1)) { welt->get_chat_message()->clear(); } while ((msg_count--) > 0) { message_node_t* n = new message_node_t(); n->rdwr(file); // maybe add this rather to the chat if (file->is_version_less(124, 1)) { if (n->get_type_shifted() == (1 << message_t::chat)) { char name[256]; char msg_no_name[256]; sint8 player_nr = 0; if (char* c = strchr(n->msg, ']')) { *c = 0; strcpy(name, n->msg + 1); strcpy(msg_no_name, c + 1); } else { strcpy(name, "Unknown"); tstrncpy(msg_no_name, n->msg, 256); } if (n->color & PLAYER_FLAG) { player_t* player = welt->get_player(n->color & (~PLAYER_FLAG)); if (player) { player_nr = player->get_player_nr(); } } welt->get_chat_message()->convert_old_message(msg_no_name, name, player_nr, n->time, n->pos.get_2d()); continue; // remove the chat message } } list.append(n); } } } chat_message_t::~chat_message_t() { clear(); } void chat_message_t::clear() { while (!list.empty()) { delete list.remove_first(); } } void chat_message_t::convert_old_message(const char* text, const char* name, sint8 player_nr, sint32 time, koord pos) { chat_node* const n = new chat_node(); tstrncpy(n->msg, text, lengthof(n->msg)); n->flags = chat_message_t::NONE; n->pos = pos; n->player_nr = player_nr; n->channel_nr = -1; n->sender = name; n->destination = 0; n->time = time; n->local_time = 0; list.append(n); } void chat_message_t::add_chat_message(const char* text, sint8 channel, sint8 sender_nr, plainstring sender_nick, plainstring recipient, koord pos, uint8 flags) { cbuffer_t buf; // Output which will be printed to ticker // send this message to a ticker if public channel message if (channel >= -1) { // and not mine if (sender_nr >= 0) { // not mine? if(strcmp(sender_nick, env_t::nickname.c_str()) != 0){ bool company_msg = channel == world()->get_active_player_nr(); bool private_msg = recipient && strcmp(recipient, env_t::nickname.c_str()) == 0; bool show_message = channel == -1 // message for all? || company_msg // company message for us? || private_msg; // private chat for us? if(show_message) { buf.printf("%s: %s", sender_nick.c_str(), text); int type = message_t::chat_common + company_msg + private_msg*2; ticker::add_msg(buf, koord3d::invalid, PLAYER_FLAG|sender_nr, type ); if (company_msg) { env_t::chat_unread_company++; } else if (private_msg) { env_t::chat_unread_whisper++; } else { env_t::chat_unread_public++; } sound_play(sound_desc_t::message_sound, 255, TOOL_SOUND); } } } } else if (channel == -2) { // text contains the current nicks, separated by TAB clients.clear(); std::string temp = text; // strtok alters the string, so make a copy to be safe char* nick = strtok(const_cast(temp.data()), "\t"); while (nick) { clients.append(nick); nick = strtok(NULL, "\t"); } flags |= DO_NO_LOG_MSG|DO_NOT_SAVE_MSG; // not saving this message if (chat_frame_t* cf = (chat_frame_t*)win_get_magic(magic_chatframe)) { cf->fill_list(); } } if (!(flags & DO_NO_LOG_MSG)) { // we do not allow messages larger than 256 bytes chat_node* const n = new chat_node(); tstrncpy(n->msg, text, lengthof(n->msg)); n->flags = flags; n->pos = pos; n->player_nr = sender_nr; n->channel_nr = channel; n->sender = sender_nick; n->destination = recipient; n->time = world()->get_current_month(); time_t ltime; time(<ime); n->local_time = ltime; list.append(n); } } void chat_message_t::rename_client(plainstring old_name, plainstring new_name) { for (chat_node* i : list) { if (i->sender && strcmp(i->sender, old_name) == 0) { i->sender = new_name; } if (i->destination && strcmp(i->destination, old_name) == 0) { i->destination = new_name; } } } void chat_message_t::chat_node::rdwr(loadsave_t* file) { file->rdwr_str(msg, lengthof(msg)); file->rdwr_byte(flags); file->rdwr_byte(player_nr); file->rdwr_byte(channel_nr); file->rdwr_str(sender); file->rdwr_str(destination); file->rdwr_long(time); sint64 tmp = local_time; file->rdwr_longlong(tmp); local_time = tmp; pos.rdwr(file); } void chat_message_t::rotate90(sint16 size_w) { for (chat_node* const i : list) { i->pos.rotate90(size_w); } } void chat_message_t::rdwr(loadsave_t* file) { uint16 msg_count; if (file->is_saving()) { vector_tplmsg_to_save; msg_to_save.reserve(list.get_count()); // do not save discardable messages for (chat_node* const i : list) { if (!(i->flags & DO_NOT_SAVE_MSG)) { msg_to_save.append(i); } } // save only the last MAX_SAVED_MESSAGES msg_count = (uint16)min(MAX_SAVED_MESSAGES, msg_to_save.get_count()); file->rdwr_short(msg_count); for (uint32 i = msg_to_save.get_count() - msg_count; i < msg_to_save.get_count(); i++) { msg_to_save[i]->rdwr(file); } } else { // loading clear(); file->rdwr_short(msg_count); while ((msg_count--) > 0) { chat_node* n = new chat_node(); n->rdwr(file); list.append(n); } } } // return the number of connected clients const vector_tpl& chat_message_t::get_online_nicks() { return clients; } simutrans-124.5/src/simutrans/simmesg.h000066400000000000000000000070441520675460500202220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMMESG_H #define SIMMESG_H #include "simtypes.h" #include "gui/gui_theme.h" #include "display/simimg.h" #include "dataobj/koord.h" #include "dataobj/koord3d.h" #include "tpl/slist_tpl.h" #include "utils/plainstring.h" class karte_t; class karte_ptr_t; /** * class for a simple message * this way they are stored in a list */ struct message_node_t { char msg[256]; sint32 type; koord3d pos; FLAGGED_PIXVAL color; image_id image; sint32 time; void rdwr(loadsave_t *file); uint32 get_type_shifted() const; // { return 1<<(type & MESSAGE_TYPE_MASK); } PIXVAL get_player_color(karte_t*) const; void open_msg_window(bool open_as_autoclose) const; }; class message_t { public: enum msg_typ { general = 0, ai = 1, city = 2, problems = 3, industry = 4, chat = 5, new_vehicle = 6, full = 7, warnings = 8, traffic_jams = 9, scenario = 10, chat_common = 11, chat_company = 12, chat_private = 13, MAX_MESSAGE_TYPE, MESSAGE_TYPE_MASK = 0xf, EXPIRE_AFTER_ONE_MONTH_MSG = 1 << 13, DO_NOT_SAVE_MSG = 1 << 14, PLAYER_MSG = 1 << 15 }; void add_message( const char *text, koord3d pos, uint16 what, FLAGGED_PIXVAL color=SYSCOL_TEXT, image_id image=IMG_EMPTY ); /* determines, which message is displayed where */ void get_message_flags( sint32 *t, sint32 *w, sint32 *a, sint32 *i); void set_message_flags( sint32, sint32, sint32, sint32 ); message_t(); ~message_t(); private: // bitfields that contains the messages sint32 ticker_flags; sint32 win_flags; sint32 auto_win_flags; sint32 ignore_flags; slist_tpl list; public: const slist_tpl &get_list() const { return list; } void clear(); void rotate90( sint16 size_w ); void rdwr( loadsave_t *file ); /** * Returns first valid coordinate from text (or koord::invalid if none is found). * syntax: either @x,y or (x,y) */ static koord3d get_coord_from_text(const char* text); }; class chat_message_t { public: class chat_node { public: char msg[256]; uint8 flags; sint8 player_nr; sint8 channel_nr=-1; // player(company) number, -1 = public chat plainstring sender; // NULL = system message plainstring destination; // for whisper koord pos; sint32 time; // game date time_t local_time; void rdwr(loadsave_t* file); }; enum { NONE = 0, DO_NO_LOG_MSG = 1<<1, // send system message via tool, but not logged chat window, send only ticker DO_NOT_SAVE_MSG = 1<<2 // send system message via tool, logged but not saved // TODO: We can consider flags such as messages pinned to the top by an admin, messages that remain for a certain amount of time, etc. }; void add_chat_message(const char* text, sint8 channel=-1, sint8 sender_player_nr=1 /*PLAYER_PUBLIC_NR*/, plainstring sender=NULL, plainstring recipient=NULL, koord pos = koord::invalid, uint8 flags=chat_message_t::NONE); chat_message_t() {}; ~chat_message_t(); static const vector_tpl &get_online_nicks(); private: slist_tpl list; public: const slist_tpl& get_list() const { return list; } // for converting old log void convert_old_message(const char* text, const char* name, sint8 player_nr, sint32 time, koord pos); // call from nwc_nick_t void rename_client(plainstring old_name, plainstring new_name); void clear(); void rotate90(sint16 size_w); void rdwr(loadsave_t* file); }; #endif simutrans-124.5/src/simutrans/simskin.cc000066400000000000000000000252011520675460500203640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "descriptor/skin_desc.h" #include "descriptor/spezial_obj_tpl.h" #include "simskin.h" #include "tpl/slist_tpl.h" #include "tpl/vector_tpl.h" /* * Skins, mostly GUI elements */ // menus const skin_desc_t* skinverwaltung_t::tool_icons_general = NULL; const skin_desc_t* skinverwaltung_t::tool_icons_simple = NULL; const skin_desc_t* skinverwaltung_t::tool_icons_dialoge = NULL; const skin_desc_t* skinverwaltung_t::tool_icons_toolbars = NULL; const skin_desc_t* skinverwaltung_t::toolbar_background = NULL; /* Window skin images are menus too! */ const skin_desc_t* skinverwaltung_t::button = NULL; const skin_desc_t* skinverwaltung_t::round_button = NULL; const skin_desc_t* skinverwaltung_t::check_button = NULL; const skin_desc_t* skinverwaltung_t::posbutton = NULL; const skin_desc_t* skinverwaltung_t::back = NULL; const skin_desc_t* skinverwaltung_t::scrollbar = NULL; const skin_desc_t* skinverwaltung_t::divider = NULL; const skin_desc_t* skinverwaltung_t::editfield = NULL; const skin_desc_t* skinverwaltung_t::listbox = NULL; const skin_desc_t* skinverwaltung_t::gadget= NULL; // symbol images const skin_desc_t* skinverwaltung_t::biglogosymbol = NULL; const skin_desc_t* skinverwaltung_t::logosymbol = NULL; const skin_desc_t* skinverwaltung_t::neujahrsymbol = NULL; const skin_desc_t* skinverwaltung_t::neueweltsymbol = NULL; const skin_desc_t* skinverwaltung_t::flaggensymbol = NULL; const skin_desc_t* skinverwaltung_t::meldungsymbol = NULL; const skin_desc_t* skinverwaltung_t::zughaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::autohaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::schiffshaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::airhaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::monorailhaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::maglevhaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::narrowgaugehaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::bushaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::tramhaltsymbol = NULL; const skin_desc_t* skinverwaltung_t::networksymbol = NULL; const skin_desc_t* skinverwaltung_t::timelinesymbol = NULL; const skin_desc_t* skinverwaltung_t::fastforwardsymbol = NULL; const skin_desc_t* skinverwaltung_t::pausesymbol = NULL; const skin_desc_t* skinverwaltung_t::electricity = NULL; const skin_desc_t* skinverwaltung_t::intown = NULL; const skin_desc_t* skinverwaltung_t::passengers = NULL; const skin_desc_t* skinverwaltung_t::mail = NULL; const skin_desc_t* skinverwaltung_t::goods = NULL; const skin_desc_t* skinverwaltung_t::happy = NULL; const skin_desc_t* skinverwaltung_t::unhappy = NULL; const skin_desc_t* skinverwaltung_t::no_route = NULL; const skin_desc_t* skinverwaltung_t::station_type = NULL; const skin_desc_t* skinverwaltung_t::seasons_icons = NULL; const skin_desc_t* skinverwaltung_t::message_options = NULL; const skin_desc_t* skinverwaltung_t::color_options = NULL; const skin_desc_t* skinverwaltung_t::compass_iso = NULL; const skin_desc_t* skinverwaltung_t::compass_map = NULL; // compass for minimap // cursors const skin_desc_t* skinverwaltung_t::cursor_general = NULL; // new cursors const skin_desc_t* skinverwaltung_t::bauigelsymbol = NULL; const skin_desc_t* skinverwaltung_t::belegtzeiger = NULL; const skin_desc_t* skinverwaltung_t::mouse_cursor = NULL; // misc images const skin_desc_t* skinverwaltung_t::construction_site = NULL; const skin_desc_t* skinverwaltung_t::fussweg = NULL; const skin_desc_t* skinverwaltung_t::pumpe = NULL; const skin_desc_t* skinverwaltung_t::senke = NULL; const skin_desc_t* skinverwaltung_t::tunnel_texture = NULL; slist_tplskinverwaltung_t::extra_menu_obj; slist_tplskinverwaltung_t::extra_cursor_obj; static special_obj_tpl const misc_objekte[] = { { &skinverwaltung_t::senke, "PowerDest" }, { &skinverwaltung_t::pumpe, "PowerSource" }, { &skinverwaltung_t::construction_site, "Construction" }, { &skinverwaltung_t::fussweg, "Sidewalk" }, { &skinverwaltung_t::tunnel_texture, "TunnelTexture"}, { NULL, NULL } }; static special_obj_tpl const menu_objekte[] = { // new menu system { &skinverwaltung_t::button, "Button" }, { &skinverwaltung_t::round_button, "Roundbutton" }, { &skinverwaltung_t::check_button, "Checkbutton" }, { &skinverwaltung_t::posbutton, "Posbutton" }, { &skinverwaltung_t::scrollbar, "Scrollbar" }, { &skinverwaltung_t::divider, "Divider" }, { &skinverwaltung_t::editfield, "Editfield" }, { &skinverwaltung_t::listbox, "Listbox" }, { &skinverwaltung_t::back, "Back" }, { &skinverwaltung_t::gadget, "Gadget" }, { &skinverwaltung_t::tool_icons_general, "GeneralTools" }, { &skinverwaltung_t::tool_icons_simple, "SimpleTools" }, { &skinverwaltung_t::tool_icons_dialoge, "DialogeTools" }, { &skinverwaltung_t::tool_icons_toolbars,"BarTools" }, { NULL, NULL } }; static special_obj_tpl const symbol_objekte[] = { { &skinverwaltung_t::seasons_icons, "Seasons" }, { &skinverwaltung_t::message_options, "MessageOptions" }, { &skinverwaltung_t::color_options, "ColorOptions" }, { &skinverwaltung_t::logosymbol, "Logo" }, { &skinverwaltung_t::neujahrsymbol, "NewYear" }, { &skinverwaltung_t::neueweltsymbol, "NewWorld" }, { &skinverwaltung_t::flaggensymbol, "Flags" }, { &skinverwaltung_t::meldungsymbol, "Message" }, { &skinverwaltung_t::electricity, "Electricity" }, { &skinverwaltung_t::intown, "InTown" }, { &skinverwaltung_t::passengers, "Passagiere" }, { &skinverwaltung_t::mail, "Post" }, { &skinverwaltung_t::goods, "Waren" }, { NULL, NULL } }; static const skin_desc_t* restore_symbol_objekte[lengthof(symbol_objekte)]; // simutrans will work without those static special_obj_tpl const fakultative_objekte[] = { { &skinverwaltung_t::biglogosymbol, "BigLogo" }, { &skinverwaltung_t::mouse_cursor, "Mouse" }, { &skinverwaltung_t::zughaltsymbol, "TrainStop" }, { &skinverwaltung_t::autohaltsymbol, "CarStop" }, { &skinverwaltung_t::schiffshaltsymbol, "ShipStop" }, { &skinverwaltung_t::bushaltsymbol, "BusStop" }, { &skinverwaltung_t::airhaltsymbol, "AirStop" }, { &skinverwaltung_t::monorailhaltsymbol, "MonorailStop" }, { &skinverwaltung_t::maglevhaltsymbol, "MaglevStop" }, { &skinverwaltung_t::narrowgaugehaltsymbol,"NarrowgaugeStop"}, { &skinverwaltung_t::tramhaltsymbol, "TramStop" }, { &skinverwaltung_t::networksymbol, "networksym" }, { &skinverwaltung_t::timelinesymbol, "timelinesym" }, { &skinverwaltung_t::fastforwardsymbol, "fastforwardsym" }, { &skinverwaltung_t::pausesymbol, "pausesym" }, { &skinverwaltung_t::station_type, "station_type" }, { &skinverwaltung_t::toolbar_background, "ToolsBackground"}, { &skinverwaltung_t::compass_iso, "CompassIso" }, { &skinverwaltung_t::compass_map, "CompassMap" }, { &skinverwaltung_t::happy, "Happy" }, { &skinverwaltung_t::unhappy, "Unhappy" }, { &skinverwaltung_t::no_route, "NoRoute" }, { NULL, NULL } }; static const skin_desc_t *restore_fakultative_objekte[lengthof(fakultative_objekte)]; static special_obj_tpl const cursor_objekte[] = { // old cursors { &skinverwaltung_t::bauigelsymbol, "Builder" }, { &skinverwaltung_t::cursor_general, "GeneralTools" }, { &skinverwaltung_t::belegtzeiger, "Marked" }, { NULL, NULL } }; bool skinverwaltung_t::successfully_loaded(skintyp_t type) { special_obj_tpl const* sd; switch (type) { case menu: return true; // skins will be handled elsewhere case cursor: sd = cursor_objekte; break; case symbol: sd = symbol_objekte; break; case misc: sd = misc_objekte+2; // for compatibility: use sidewalk as tunneltexture if (tunnel_texture==NULL) { tunnel_texture = fussweg; } break; case nothing: return true; default: return false; } return ::successfully_loaded(sd); } bool skinverwaltung_t::register_desc(skintyp_t type, const skin_desc_t* desc) { special_obj_tpl const* sd; switch (type) { case menu: sd = menu_objekte; break; case cursor: sd = cursor_objekte; break; case symbol: sd = symbol_objekte; break; case misc: sd = misc_objekte; break; case nothing: return true; default: return false; } if( ::register_desc(sd, desc) ) { return true; } else if( type==cursor || type==symbol ) { if( ::register_desc( fakultative_objekte, desc ) ) { return true; } } // currently no misc objects allowed ... if( type==cursor || type==menu ) { if( type==cursor ) { extra_cursor_obj.insert( desc ); } else { extra_menu_obj.insert( desc ); } dbg->message( "skinverwaltung_t::register_desc()","Extra object %s added.", desc->get_name() ); } else { dbg->warning("skinverwaltung_t::register_desc()","Spurious object '%s' loaded (will not be referenced anyway)!", desc->get_name() ); } return true; } // return the extra_obj with this name const skin_desc_t *skinverwaltung_t::get_extra( const char *str, int len, skintyp_t type ) { if(!str || len == 0) { return NULL; } if( type!=menu && type!=cursor ) { // illegal type return NULL; } for(skin_desc_t const* const s : (type==menu ? skinverwaltung_t::extra_menu_obj : skinverwaltung_t::extra_cursor_obj)) { if ( strncmp(str, s->get_name(), len) == 0 ) { return s; } } return NULL; } void skinverwaltung_t::save_all_skins() { for (int i = 0; symbol_objekte[i].name != 0; i++) { restore_symbol_objekte[i] = *(symbol_objekte[i].desc); } for (int i = 0; fakultative_objekte[i].name != 0; i++) { restore_fakultative_objekte[i]= *(fakultative_objekte[i].desc); } } void skinverwaltung_t::restore_all_skins() { // only saving the original pak objects once for (int i = 0; symbol_objekte[i].name != 0; i++) { *symbol_objekte[i].desc = restore_symbol_objekte[i]; } for (int i = 0; fakultative_objekte[i].name != 0; i++) { *fakultative_objekte[i].desc = restore_fakultative_objekte[i]; } } simutrans-124.5/src/simutrans/simskin.h000066400000000000000000000126461520675460500202370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMSKIN_H #define SIMSKIN_H #include "simcolor.h" // For test purposes themes can be disabled or an alternative theme.tab file can be used. // -1 = No theme, use internal fallback // 0 = Normal use, read theme.tab // n>0 = Use alternative file named theme_n.tab template class slist_tpl; class skin_desc_t; class skinverwaltung_t { public: enum skintyp_t { nothing, menu, cursor, symbol, misc }; /// @name icons used in the toolbars /// @{ /// icon images for advanced tools (everything that involves clicking on the map) static const skin_desc_t *tool_icons_general; /// icon images for simple tools (eg pause, fast forward) static const skin_desc_t *tool_icons_simple; /// icon images for GUI tools (which open windows) static const skin_desc_t *tool_icons_dialoge; /// icon images for toolbars static const skin_desc_t *tool_icons_toolbars; /// icon to skin toolbar background static const skin_desc_t *toolbar_background; /// @} /** * Different GUI elements */ static const skin_desc_t* button; static const skin_desc_t* round_button; static const skin_desc_t* check_button; static const skin_desc_t* posbutton; static const skin_desc_t* back; static const skin_desc_t* scrollbar; static const skin_desc_t* divider; static const skin_desc_t* editfield; static const skin_desc_t* listbox; static const skin_desc_t* gadget; /// @name pictures used in the GUI /// @{ /// image shown in welcome screen @see banner.cc static const skin_desc_t *logosymbol; /// image shown when loading pakset static const skin_desc_t *biglogosymbol; /// image shown with 'happy new year' message static const skin_desc_t *neujahrsymbol; /// image shown when creating new world static const skin_desc_t *neueweltsymbol; /// image shown in language selection static const skin_desc_t *flaggensymbol; /// image shown in message boxes @see gui/messagebox.h static const skin_desc_t *meldungsymbol; /// image shown in message options window static const skin_desc_t *message_options; /// image shown in color selection window static const skin_desc_t *color_options; // isometric compass for main map (evt. minimap) static const skin_desc_t *compass_iso; // normal staight compass for minimap static const skin_desc_t *compass_map; /// @} /// @name icons used for the tabs in the line management window /// @{ static const skin_desc_t *zughaltsymbol; static const skin_desc_t *autohaltsymbol; static const skin_desc_t *schiffshaltsymbol; static const skin_desc_t *airhaltsymbol; static const skin_desc_t *monorailhaltsymbol; static const skin_desc_t *maglevhaltsymbol; static const skin_desc_t *narrowgaugehaltsymbol; static const skin_desc_t *bushaltsymbol; static const skin_desc_t *tramhaltsymbol; ///@} /// @name icons shown in status bar at the bottom of the screen /// @{ static const skin_desc_t *networksymbol; static const skin_desc_t *timelinesymbol; static const skin_desc_t *fastforwardsymbol; static const skin_desc_t *pausesymbol; static const skin_desc_t *seasons_icons; /// @} /// @name icons used to show which halt serves passengers / mail / freight /// @{ static const skin_desc_t *passengers; static const skin_desc_t *mail; static const skin_desc_t *goods; /// @} /// @name icons used to passenger evaluations happy / unhappy / no route /// @{ static const skin_desc_t *happy; static const skin_desc_t *unhappy; static const skin_desc_t *no_route; /// @} /// images shown in display of lines in mini-map static const skin_desc_t *station_type; /// image to indicate power supply of factories static const skin_desc_t *electricity; /// image to indicate that an attraction is inside a town (attraction list window) static const skin_desc_t *intown; /// @name cursors /// @{ /// cursors for tools static const skin_desc_t *cursor_general; /// for allegro: emulate mouse cursor static const skin_desc_t *mouse_cursor; /// symbol to mark tiles by AI players and text labels static const skin_desc_t *belegtzeiger; /// icon to mark start tiles for construction (ie the bulldozer image) static const skin_desc_t *bauigelsymbol; /// @} /// shown in hidden-buildings mode instead of buildings images static const skin_desc_t *construction_site; /// texture to be shown beneath city roads to indicate pavements static const skin_desc_t *fussweg; /// transformer image: supply static const skin_desc_t *pumpe; /// transformer image: consumer static const skin_desc_t *senke; /// texture to be shown beneath ways in tunnel static const skin_desc_t *tunnel_texture; static bool register_desc(skintyp_t type, const skin_desc_t *desc); static bool successfully_loaded(skintyp_t type); /** * retrieves objects with type=menu and given name * * @param str pointer to beginning of name string (not null-terminated) * @param len length of string * @param type * @return pointer to skin object or NULL if nothing found */ static const skin_desc_t *get_extra( const char *str, int len, skintyp_t type = menu ); // called after all paksets are loaded to restore icons overlaid by skin paks static void save_all_skins(); // restore pakset originals static void restore_all_skins(); private: /// holds objects from paks with type 'menu' and 'cursor' static slist_tplextra_menu_obj; static slist_tplextra_cursor_obj; }; #endif simutrans-124.5/src/simutrans/simsound.cc000066400000000000000000000121251520675460500205510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "macros.h" #include "music/music.h" #include "descriptor/sound_desc.h" #include "sound/sound.h" #include "simsound.h" #include "sys/simsys.h" #include "simio.h" #include "simdebug.h" #include "dataobj/environment.h" #include "utils/plainstring.h" #include "utils/simrandom.h" #include "utils/simstring.h" static bool new_midi = false; static struct midi_info_t midi_list[MAX_MIDI]; static int max_midi = -1; // number of MIDI files static int current_midi = -1; // init with error condition, reset during loading void sound_set_global_volume(int volume) { env_t::global_volume = volume; } void sound_set_specific_volume( int volume, sound_type_t t) { env_t::specific_volume[t] = volume; } int sound_get_global_volume() { return env_t::global_volume; } int sound_get_specific_volume( sound_type_t t ) { return env_t::specific_volume[t]; } void sound_set_mute(bool f) { env_t::global_mute_sound = f; } bool sound_get_mute() { return ( env_t::global_mute_sound ); } void sound_play(uint16 const idx, uint8 const v, sound_type_t t) { uint32 volume = v; if( idx != (uint16)NO_SOUND && !env_t::global_mute_sound ) { dr_play_sample(idx, ( (volume * env_t::global_volume * env_t::specific_volume[t] ) >> 16) ); } } bool sound_get_shuffle_midi() { return env_t::shuffle_midi; } void sound_set_shuffle_midi( bool shuffle ) { env_t::shuffle_midi = shuffle; } void sound_set_midi_volume(int volume) { if( !env_t::mute_midi && max_midi > -1 ) { dr_set_midi_volume(volume); } env_t::midi_volume = volume; } int sound_get_midi_volume() { return env_t::midi_volume; } /** * gets midi title */ struct midi_info_t sound_get_midi_info(int index) { if ( index >= 0 && index <= max_midi ) { return midi_list[index]; } return { "Invalid MIDI Index!", "-", "-" }; } /** * gets current midi number */ int get_current_midi() { return current_midi; } /** * Load MIDI files */ int midi_init(const char *directory) { // read a list of soundfiles std::string full_path = std::string(directory) + "music" + PATH_SEPARATOR + "music.tab"; if( FILE* const file = dr_fopen(full_path.c_str(), "rb") ) { while(!feof(file)) { char buf[256], title[256], composer[256], arranger[256]; size_t len; read_line(buf, sizeof(buf), file); read_line(title, sizeof(title), file); read_line(composer, sizeof(composer), file); read_line(arranger, sizeof(arranger), file); if( !feof(file) ) { clear_invalid_ending_chars(buf); len = strlen(buf); if( len > 1 ) { full_path = std::string(directory) + buf; dbg->message("midi_init()", " Reading MIDI file '%s' - %s", full_path.c_str(), title); max_midi = dr_load_midi(full_path.c_str()); if( max_midi >= 0 ) { midi_list[max_midi].title = (std::string) clear_invalid_ending_chars(title); midi_list[max_midi].composer = (std::string) clear_invalid_ending_chars(composer); midi_list[max_midi].arranger = (std::string) clear_invalid_ending_chars(arranger); } } } } fclose(file); } else { dbg->warning("midi_init()","can't open file '%s' for reading.", full_path.c_str() ); } if( max_midi >= 0 ) { current_midi = 0; } // success? return ( max_midi >= 0 ); } void midi_play(const int no) { if( no > max_midi ) { dbg->warning("midi_play()", "MIDI index %d too high (total loaded: %d)", no, max_midi); } else if( !midi_get_mute() ) { current_midi = (no < 0) ? sim_async_rand( max_midi ) : no; dr_play_midi( current_midi ); } } void midi_stop() { if( !midi_get_mute() ) { dr_stop_midi(); } } void midi_set_mute(bool on) { on |= ( max_midi == -1 ); if( on ) { if( !env_t::mute_midi ) { dr_stop_midi(); } env_t::mute_midi = true; } else { if( env_t::mute_midi ) { env_t::mute_midi = false; midi_play(current_midi); } dr_set_midi_volume(env_t::midi_volume); } } bool midi_get_mute() { return ( env_t::mute_midi || max_midi == -1 ); } /* * Check if need to play new MIDI * Max Kielland: * Made it possible to get next song * even if we are muted. */ void check_midi() { // Check for next sound if (new_midi || (!midi_get_mute() && dr_midi_pos() < 0)) { if( env_t::shuffle_midi && max_midi > 1 ) { // shuffle songs (must not use simrand()!) int new_song = sim_async_rand(max_midi); if( new_song >= current_midi ) { new_song ++; } current_midi = new_song; } else { current_midi++; if( current_midi > max_midi ) { current_midi = 0; } } // Are we in playing mode? if( false == midi_get_mute() ) { midi_play(current_midi); DBG_MESSAGE("check_midi()", "Playing MIDI %d", current_midi); } } new_midi = false; } /** * shuts down midi playing */ void close_midi() { if( max_midi > -1 ) { dr_destroy_midi(); } } void midi_next_track() { new_midi = true; } void midi_last_track() { if ( current_midi == 0 ) { current_midi = max_midi - 1; } else { current_midi = current_midi - 2; } new_midi = true; } simutrans-124.5/src/simutrans/simsound.h000066400000000000000000000033311520675460500204120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMSOUND_H #define SIMSOUND_H #include #include "simtypes.h" #include "utils/plainstring.h" /// sound can be selectively muted (but volume is not touched) void sound_set_mute(bool new_flag); bool sound_get_mute(); /// @param volume in range 0..255 void sound_set_global_volume(int volume); /// @returns volume in range 0..255 int sound_get_global_volume(); /// Sets volume for a specific type of sound. /// @param volume in range 0..255 /// @param t void sound_set_specific_volume( int volume, sound_type_t t ); /// @param t /// @returns volume in range 0..255 int sound_get_specific_volume( sound_type_t t ); /** * Play a sound. * * @param idx Index of the sound * @param volume in range 0..255 * @param t */ void sound_play(uint16 idx, uint8 volume, sound_type_t t ); /// shuffle enable/disable for midis bool sound_get_shuffle_midi(); void sound_set_shuffle_midi( bool shuffle ); /// @param volume in range 0..255 void sound_set_midi_volume(int volume); /// @returns volume in range 0..255 int sound_get_midi_volume(); struct midi_info_t { std::string title; std::string composer; std::string arranger; }; /// gets midi title struct midi_info_t sound_get_midi_info(int index); /** * gets curent midi number */ int get_current_midi(); // when muted, midi is not used void midi_set_mute(bool on); bool midi_get_mute(); /* MIDI routines */ extern int midi_init(const char *path); extern void midi_play(const int no); extern void check_midi(); /** * shuts down midi playing */ extern void close_midi(); extern void midi_next_track(); extern void midi_last_track(); extern void midi_stop(); #endif simutrans-124.5/src/simutrans/simticker.cc000066400000000000000000000134101520675460500207000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "dataobj/koord3d.h" #include "dataobj/environment.h" #include "simticker.h" #include "display/simgraph.h" #include "simcolor.h" #include "tpl/slist_tpl.h" #include "utils/simstring.h" #include "gui/gui_theme.h" #include "gui/simwin.h" #include "gui/chat_frame.h" #include "world/simworld.h" #include "simmesg.h" #include "display/viewport.h" #define X_DIST (2) // how much scrolling per update call? #define X_SPACING (18) // spacing between messages, in pixels static karte_ptr_t welt; uint16 win_get_statusbar_height(); // simwin.h struct node : public message_node_t { node() {} node(const message_node_t& msg) : message_node_t(msg), xpos(0), w(0) {} sint16 xpos; sint32 w; }; static slist_tpl list; static bool redraw_all = false; ///< true, if also trigger background need redraw static int next_pos; ///< Next x offset of new message. Always greater or equal to display_width static int dx_since_last_draw = 0; ///< Increased during update(); positive values move messages to the left bool ticker::empty() { return list.empty(); } void ticker::clear_messages() { list.clear(); set_redraw_all(true); } void ticker::set_redraw_all(bool redraw) { redraw_all = redraw; } void ticker::add_msg_node(const message_node_t& msg) { const int count = list.get_count(); if(count==0) { redraw_all = true; next_pos = gfx->get_screen_size().w; } const char* txt = msg.msg; // don't store more than 4 messages, it's useless. if(count >= 4) { return; } // Don't repeat messages else if (count == 0 || !strstart(list.back().msg, txt)) { node n(msg); int i=0; // copy message text // remove breaks for( int j=0; i<250 && txt[j]!=0; j++ ) { if( txt[j]=='\n' ) { if( i==0 || n.msg[i-1]==' ' ) { continue; } n.msg[i++] = ' '; } else { if( txt[j]==' ' && (i==0 || n.msg[i-1]==' ') ) { // avoid double or leading spaces continue; } n.msg[i++] = txt[j]; } } n.msg[i++] = 0; n.xpos = next_pos; n.w = gfx->calc_text_width(n.msg); next_pos += n.w + X_SPACING; list.append(n); } } void ticker::add_msg(const char* txt, koord3d pos, FLAGGED_PIXVAL color, int type) { node n; tstrncpy(n.msg, txt, lengthof(n.msg)); n.pos = pos; n.color = color; // set to default values n.type = message_t::general; n.time = 0; n.image = IMG_EMPTY; n.type = type; add_msg_node(n); } void ticker::update() { const int dx = X_DIST; const scr_size screen = gfx->get_screen_size(); for(node & n : list) { n.xpos -= dx; } dx_since_last_draw += dx; next_pos = std::max(next_pos - dx, screen.w); // remove old news while (!list.empty() && list.front().xpos + list.front().w < 0) { list.remove_first(); } if (list.empty()) { set_redraw_all(true); } } void ticker::draw() { const scr_size screen = gfx->get_screen_size(); const int start_y = env_t::menupos == MENU_BOTTOM ? win_get_statusbar_height() : screen.h - TICKER_HEIGHT - win_get_statusbar_height(); if (redraw_all) { redraw(); return; } else if (list.empty()) { // ticker not visible // mark everything at the bottom as dirty to clear also tooltips and compass gfx->mark_rect_dirty_wc(0, env_t::menupos == MENU_BOTTOM ? 0 : start_y - 128, screen.w, start_y + 128 + TICKER_HEIGHT); return; } if (dx_since_last_draw <=0) { return; } if (screen.w <= 0) { return; } // do partial redraw gfx->move_scroll_band(start_y, dx_since_last_draw, TICKER_HEIGHT); gfx->draw_rect(screen.w-dx_since_last_draw, start_y, dx_since_last_draw, TICKER_HEIGHT, SYSCOL_TICKER_BACKGROUND, true); gfx->mark_rect_dirty_wc(0, start_y, screen.w, start_y + TICKER_HEIGHT); // ok, ready for the text PUSH_CLIP( screen.w-dx_since_last_draw, start_y, dx_since_last_draw, TICKER_HEIGHT ); for(node & n : list) { if (n.xpos < screen.w) { gfx->draw_text_clipped(n.xpos, start_y + TICKER_V_SPACE, n.msg, ALIGN_LEFT, n.get_player_color(welt), true); } } POP_CLIP(); dx_since_last_draw = 0; } void ticker::redraw() { const scr_size screen = gfx->get_screen_size(); set_redraw_all(false); dx_since_last_draw = 0; const int start_y = env_t::menupos == MENU_BOTTOM ? win_get_statusbar_height() : screen.h - TICKER_HEIGHT - win_get_statusbar_height(); if (list.empty()) { // mark everything at the bottom as dirty to clear also tooltips and compass gfx->mark_rect_dirty_wc(0, env_t::menupos == MENU_BOTTOM ? 0 : start_y - 128, screen.h, start_y + 128 + TICKER_HEIGHT); world()->set_background_dirty(); return; } // just draw the ticker in its colour ... (to be sure ... ) gfx->draw_rect(0, start_y, screen.w, TICKER_HEIGHT, SYSCOL_TICKER_BACKGROUND, true); for(node & n : list) { if (n.xpos < screen.w) { gfx->draw_text_clipped(n.xpos, start_y + TICKER_V_SPACE, n.msg, ALIGN_LEFT, n.color, true); } } } void ticker::process_click(int x) { node *clicked = NULL; if (list.empty()) { return; } clicked = &list.front(); if (list.get_count() > 1) { for(node & n : list) { if (n.xpos <= x && x < n.xpos + n.w ) { clicked = &n; break; } } } if ((clicked->type & message_t::MESSAGE_TYPE_MASK) >= message_t::chat_common && (clicked->type & message_t::MESSAGE_TYPE_MASK) <= message_t::chat_private) { chat_frame_t* si = (chat_frame_t*)win_get_magic(magic_chatframe); if (si == NULL) { si = new chat_frame_t(); create_win({ 0, 200 }, si, w_info, magic_chatframe); } si->activate_tab((clicked->type & message_t::MESSAGE_TYPE_MASK) - message_t::chat_common); } else if (clicked->pos != koord3d::invalid) { if(welt->is_within_limits(clicked->pos.get_2d())) { welt->get_viewport()->change_world_position(clicked->pos); } } else { clicked->open_msg_window(false); } } simutrans-124.5/src/simutrans/simticker.h000066400000000000000000000025331520675460500205460ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMTICKER_H #define SIMTICKER_H #include "simcolor.h" #include "display/simgraph.h" #define TICKER_V_SPACE (2) // Vertical offset of ticker text #define TICKER_HEIGHT (LINESPACE+2*TICKER_V_SPACE) class koord3d; struct message_node_t; /** * A very simple scrolling news ticker. */ namespace ticker { bool empty(); /** * Add a message to the message list * * @param txt * @param pos position of the event * @param color message color * @param type what kind of message */ void add_msg(const char* txt, koord3d pos, FLAGGED_PIXVAL color, sint32 type=0); /** * Add a message in message_node_t format */ void add_msg_node(const message_node_t& msg); /** * Remove all messages and mark for redraw */ void clear_messages(); /** * Update message positions and remove old messages */ void update(); /** * Redraw the ticker partially or fully (if set_redraw_all() was called) */ void draw(); /** * Set true if ticker has to be redrawn fully * @sa redraw */ void set_redraw_all(bool redraw); /** * Force a ticker redraw (e.g. after a window resize) */ void redraw(); /** * Process click into ticker bar: jump to coordinate or open message window. */ void process_click(int x); }; #endif simutrans-124.5/src/simutrans/simtypes.h000066400000000000000000000142721520675460500204340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMTYPES_H #define SIMTYPES_H #include #ifndef PATH_MAX #ifdef MAX_PATH #define PATH_MAX (MAX_PATH) #else #define PATH_MAX 1024 #endif #endif #if defined _MSC_VER # include # define ALLOCA(type, name, count) type* name = static_cast(alloca(sizeof(type) * (count))) #elif defined __clang__ # include # define ALLOCA(type, name, count) type* name = static_cast(alloca(sizeof(type) * (count))) #else # define ALLOCA(type, name, count) type name[count] #endif #if defined DEBUG # include # define NOT_REACHED abort(); #else # define NOT_REACHED #endif #define OVERRIDE override #include // intptr_t (standard) #include // ptrdiff_t, intptr_t (Microsoft) #define ENUM_BITSET(T) \ static inline T operator ~ (T a) { return (T)~(unsigned)a; } \ static inline T operator & (T a, T b) { return (T)((unsigned)a & (unsigned)b); } \ static inline T operator &= (T& a, T b) { return a = (T)((unsigned)a & (unsigned)b); } \ static inline T operator | (T a, T b) { return (T)((unsigned)a | (unsigned)b); } \ static inline T operator |= (T& a, T b) { return a = (T)((unsigned)a | (unsigned)b); } /* divers enums: * better defined here than scattered in thousand files ... */ enum climate { water_climate = 0, desert_climate, tropic_climate, mediterran_climate, temperate_climate, tundra_climate, rocky_climate, arctic_climate, MAX_CLIMATES }; enum climate_bits { water_climate_bit = 1 << water_climate, desert_climate_bit = 1 << desert_climate, tropic_climate_bit = 1 << tropic_climate, mediterran_climate_bit = 1 << mediterran_climate, temperate_climate_bit = 1 << temperate_climate, tundra_climate_bit = 1 << tundra_climate, rocky_climatebit = 1 << rocky_climate, arctic_climate_bit = 1 << arctic_climate, ALL_CLIMATES = (1 << MAX_CLIMATES) - 1, all_but_water_climate = ALL_CLIMATES & ~water_climate_bit, all_but_arctic_climate = ALL_CLIMATES & ~arctic_climate_bit }; enum sound_type_t { TOOL_SOUND = 0, TRAFFIC_SOUND = 1, AMBIENT_SOUND = 2, FACTORY_SOUND = 3, CROSSING_SOUND = 4, CASH_SOUND = 5, MAX_SOUND_TYPES, ALL_SOUND = 0xFF }; /** * Vordefinierte Wegtypen. */ enum waytype_t { invalid_wt = -1, ignore_wt = 0, // fixme: many places assume invalid_wt stands for building any_wt road_wt = 1, track_wt = 2, water_wt = 3, overheadlines_wt = 4, monorail_wt = 5, maglev_wt = 6, tram_wt = 7, narrowgauge_wt = 8, air_wt = 16, powerline_wt = 128, decoration_wt = 255 }; /** * System types for ways */ enum systemtype_t { type_flat = 0, ///< flat track type_elevated = 1, ///< flag for elevated ways type_runway = 1, ///< flag for runway (only aircrafts) type_tram = 7, ///< tram track (waytype = track_wt) type_river = 255, ///< flag for river type_all = 255 ///< special ? }; /// Result of calling sync_step for sync_step-able object enum sync_result { SYNC_OK, ///< object remains in list SYNC_REMOVE, ///< remove object from list SYNC_DELETE ///< delete object and remove from list }; // define machine independent types typedef unsigned int uint; typedef signed char sint8; typedef unsigned char uint8; typedef signed short sint16; typedef unsigned short uint16; #ifndef __BEOS__ typedef signed int sint32; #ifndef NO_UINT32_TYPES typedef unsigned int uint32; #endif #else // BeOS: int!=long (even though both 32 bit) typedef signed long sint32; #ifndef NO_UINT32_TYPES typedef unsigned long uint32; #endif #endif typedef signed long long sint64; #ifndef NO_UINT64_TYPES typedef unsigned long long uint64; #endif #ifdef _MSC_VER # define GCC_PACKED # define GCC_ALIGN(a) # define MSVC_ALIGN(a) __declspec(align(a)) # define NORETURN __declspec(noreturn) # pragma warning(disable: 4200 4311 4800 4996) #else # define GCC_PACKED __attribute__ ((__packed__)) # define GCC_ALIGN(a) __attribute__ ((aligned (a))) # define MSVC_ALIGN(a) # define NORETURN __attribute__ ((noreturn)) #endif template static inline int sgn(T x) { if (x < 0) return -1; if (x > 0) return 1; return 0; } static inline int min(const int a, const int b) { return a < b ? a : b; } static inline int max(const int a, const int b) { return a > b ? a : b; } // endian conversion routines static inline uint16 endian(uint16 v) { #ifdef SIM_BIG_ENDIAN v = (v << 8) | (v >> 8); // 0x0011 #endif return v; } static inline uint32 endian(uint32 v) { #ifdef SIM_BIG_ENDIAN v = (v << 16) | (v >> 16); // 0x22330011 v = ( (v << 8) & 0xFF00FF00 ) | ( (v >> 8) & 0x00FF00FF ); // 0x33221100 #endif return v; } static inline uint64 endian(uint64 v) { #ifdef SIM_BIG_ENDIAN v = (v << 32) | (v >> 32); // 0x4455667700112233 v = ( (v << 16) & 0xFFFF0000FFFF0000ULL ) | ( (v >> 16) & 0x0000FFFF0000FFFFULL ); // 0x6677445522330011 v = ( (v << 8) & 0xFF00FF00FF00FF00ULL ) | ( (v >> 8) & 0x00FF00FF00FF00FFULL ); // 0x7766554433221100 #endif return v; } static inline sint16 endian(sint16 const v) { return sint16(endian(uint16(v))); } static inline sint32 endian(sint32 const v) { return sint32(endian(uint32(v))); } static inline sint64 endian(sint64 const v) { return sint64(endian(uint64(v))); } /** * Sometimes we need to pass pointers as well as integers through a * standardized interface (i.e. via a function pointer). This union is used as * a helper type to avoid cast operations. This isn't very clean, but if used * with care it seems better than using "long" and casting to a pointer type. * In all cases it ensures that no bits are lost. */ union value_t { value_t() : p(0) {} value_t(long itg) : i(itg) {} value_t(const void* ptr) : p(ptr) {} const void* p; long i; }; // Unicode type large enough to hold every single possible Unicode code point. typedef uint32 utf32; typedef unsigned char utf8; typedef unsigned short utf16; #endif simutrans-124.5/src/simutrans/simunits.h000066400000000000000000000102121520675460500204200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMUNITS_H #define SIMUNITS_H /* * This file is designed to contain the unit conversion routines * which were previously scattered all over the Simutrans code. * * Some are still scattered all over the code. So this file also documents * where the other conversion routines are. * * I have tried to leave out stuff which should be internal to the display * and have no gameplay consequences. * * DISTANCE units: * 1 -- tiles * 2 -- "internal pixels" -- 16 per tile. Old steps per tile. * -- OBJECT_OFFSET_STEPS, located in simconst.h * -- important to the internal rendering engine * -- used for locating images such as trees within a tile * -- same value as carunits, but conceptually different.... * 3 -- "carunits" or vehicle length units -- same as old steps per tile * -- Length of trains & other vehicles (as specified in paks) * -- is measured in this unit. Currently this is very hard to alter. * 4 -- vehicle steps * -- trains can be on one of 2^8 = 256 locations along a tile horizontally (not * diagonally). * 5 -- "yards" -- tiny distance units used internally * -- 2^12 per vehicle steps, by definition * -- 2^16 per vehicle steps in older code * -- chosen to maximize precision of certain interfaces * 6 -- km -- 1/tile in standard * * TIME units: * 1 -- ticks -- referred to as ms or milliseconds in old code * 2 -- months -- determined by karte_t::ticks_per_world_month * -- you may also use karte_t::ticks_per_world_month_shift * 3 -- days -- derived from months * 4 -- years -- derived from months * 5 -- hours & minutes -- NOT derived from months, implied by vehicle speed * 6 -- actual real-time milliseconds -- only for game speed setting * -- no meaning in-game * * SPEED units: * 1 -- "internal" speed -- yards per tick. * -- this is multiplied by delta_t, which is in ticks (in convoi_t::sync_step) * -- to get distance travelled in yards (passed to vehikel_t:fahre_basis) * 2 -- km/h -- core setting here is VEHICLE_SPEED_FACTOR * 3 -- tiles per tick * 4 -- steps per tick * * ELECTRICITY units: * 1 -- "internal" units * 2 -- megawatts * This conversion still lives in dings/leitung2.h, not here */ /** * Distance units: conversion between "vehicle steps" and "yards" * In bitshift form */ #define YARDS_PER_VEHICLE_STEP_SHIFT (12) /** * Mask, applied to yards, to eliminate anything smaller than a vehicle step * Assumes yards are in uint32 variables.... derivative of YARDS_PER_VEHICLE_STEP_SHIFT * \def YARDS_VEHICLE_STEP_MASK (0xFFFFF000) */ #define YARDS_VEHICLE_STEP_MASK ~((1<> 6) /** * Converts km/h value to speed * this is speed * 1024 / 80 = speed * 64 / 5 */ #define kmh_to_speed(speed) (((speed) << 6) / VEHICLE_SPEED_FACTOR) /* * Converts speed (yards per tick) into tiles per month */ // Done in world/simworld.h: speed_to_tiles_per_month #endif simutrans-124.5/src/simutrans/simutrans.svg000066400000000000000000000051651520675460500211550ustar00rootroot00000000000000 simutrans-124.5/src/simutrans/simversion.h000066400000000000000000000053331520675460500207530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMVERSION_H #define SIMVERSION_H #ifndef REVISION // include external generated revision file #include "revision.h" #endif #define SIM_BUILD_NIGHTLY 0 #define SIM_BUILD_RELEASE_CANDIDATE 1 #define SIM_BUILD_RELEASE 2 #define SIM_VERSION_MAJOR 124 #define SIM_VERSION_MINOR 5 #define SIM_VERSION_PATCH 0 #define SIM_VERSION_BUILD SIM_BUILD_RELEASE // Beware: SAVEGAME minor is often ahead of version minor when there were patches. // ==> These have no direct connection at all! #define SIM_SAVE_MINOR 5 #define SIM_SERVER_MINOR 5 // NOTE: increment before next release to enable save/load of new features #define MAKEOBJ_VERSION "60.11" #ifndef QUOTEME # define QUOTEME_(x) #x # define QUOTEME(x) QUOTEME_(x) #endif #if SIM_VERSION_PATCH != 0 # define SIM_VERSION_PATCH_STRING "." QUOTEME(SIM_VERSION_PATCH) #else # define SIM_VERSION_PATCH_STRING #endif #if SIM_VERSION_BUILD == SIM_BUILD_NIGHTLY # define SIM_VERSION_BUILD_STRING " Nightly" #elif SIM_VERSION_BUILD == SIM_BUILD_RELEASE_CANDIDATE # define SIM_VERSION_BUILD_STRING " Release Candidate" #elif SIM_VERSION_BUILD == SIM_BUILD_RELEASE # define SIM_VERSION_BUILD_STRING "" #else # error invalid SIM_VERSION_BUILD #endif #define VERSION_NUMBER QUOTEME(SIM_VERSION_MAJOR) "." QUOTEME(SIM_VERSION_MINOR) SIM_VERSION_PATCH_STRING SIM_VERSION_BUILD_STRING #define VERSION_DATE __DATE__ #define SAVEGAME_PREFIX "Simutrans " #define XML_SAVEGAME_PREFIX "" #define SAVEGAME_VER_NR "0." QUOTEME(SIM_VERSION_MAJOR) "." QUOTEME(SIM_SAVE_MINOR) #define SERVER_SAVEGAME_VER_NR "0." QUOTEME(SIM_VERSION_MAJOR) "." QUOTEME(SIM_SERVER_MINOR) #define RES_VERSION_NUMBER 0, SIM_VERSION_MAJOR, SIM_VERSION_MINOR, SIM_VERSION_PATCH #ifdef REVISION # define SIM_TITLE_REVISION_STRING " - r" QUOTEME(REVISION) #else # define SIM_TITLE_REVISION_STRING #endif # define SIM_TITLE SAVEGAME_PREFIX VERSION_NUMBER SIM_TITLE_REVISION_STRING /*********************** Settings related to network games ********************/ /* Server to announce status to (up to three alternative servers possible) */ #define ANNOUNCE_SERVER1 "servers.simutrans-forum.de:80" #define ANNOUNCE_SERVER2 "servers.simutrans.org:80" /* Relative URL of the announce function on server */ #define ANNOUNCE_URL "/announce" /* Relative URL of the list function on server */ #define ANNOUNCE_LIST_URL "/list?format=csv" /* url for obtaining the external IP for easz servers */ #define QUERY_ADDR_IP "simutrans-forum.de:80" #define QUERY_ADDR_IPv4_ONLY "ipv4.simutrans-forum.de:80" /* Relative URL of the IP function on server */ #define QUERY_ADDR_URL "/get_IP.php" #endif simutrans-124.5/src/simutrans/simware.cc000066400000000000000000000112161520675460500203570ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "simmem.h" #include "simdebug.h" #include "simfab.h" #include "simhalt.h" #include "simtypes.h" #include "simware.h" #include "world/simworld.h" #include "dataobj/loadsave.h" #include "dataobj/koord.h" #include "descriptor/goods_desc.h" #include "builder/goods_manager.h" const goods_desc_t *ware_t::index_to_desc[256]; ware_t::ware_t() : target_halt() , via_halt() , target_pos(-1, -1) { amount = 0; index = 0; to_factory = 0; } ware_t::ware_t(const goods_desc_t *desc) : target_halt() , via_halt() , target_pos(-1, -1) { amount = 0; index = desc->get_index(); to_factory = 0; } ware_t::ware_t(loadsave_t *file) { rdwr(file); } void ware_t::rdwr(loadsave_t *file) { sint32 amt = amount; file->rdwr_long(amt); amount = amt; if(file->is_version_less(99, 8)) { sint32 max; file->rdwr_long(max); } if( file->is_version_atleast(110, 5) ) { uint8 factory_going = to_factory; file->rdwr_byte(factory_going); to_factory = factory_going; } else if( file->is_loading() ) { to_factory = 0; } uint8 catg = 0; if(file->is_version_atleast(88, 5)) { file->rdwr_byte(catg); } if(file->is_saving()) { const char *typ = NULL; typ = get_desc()->get_name(); file->rdwr_str(typ); } else { char typ[256]; file->rdwr_str(typ, lengthof(typ)); const goods_desc_t *type = goods_manager_t::get_info(typ); if(type==NULL) { dbg->warning("ware_t::rdwr()","unknown ware of catg %d!",catg); index = goods_manager_t::get_info_catg(catg)->get_index(); amount = 0; } else { index = type->get_index(); } } // convert coordinate to halt indices if(file->is_version_atleast(110, 6)) { // save halt id directly if(file->is_saving()) { uint16 halt_id = target_halt.is_bound() ? target_halt.get_id() : 0; file->rdwr_short(halt_id); halt_id = via_halt.is_bound() ? via_halt.get_id() : 0; file->rdwr_short(halt_id); } else { uint16 halt_id; file->rdwr_short(halt_id); target_halt.set_id(halt_id); file->rdwr_short(halt_id); via_halt.set_id(halt_id); } } else { // save halthandles via coordinates koord target_halt_pos = (file->is_saving() && target_halt.is_bound()) ? target_halt->get_basis_pos() : koord::invalid; koord via_halt_pos = (file->is_saving() && via_halt.is_bound()) ? via_halt->get_basis_pos() : koord::invalid; target_halt_pos.rdwr(file); via_halt_pos.rdwr(file); if (file->is_loading()) { target_halt = haltestelle_t::get_halt_koord_index(target_halt_pos); via_halt = haltestelle_t::get_halt_koord_index(via_halt_pos); } } target_pos.rdwr(file); // restore factory-flag if (file->is_loading() && file->is_version_less(110, 5)) { if (fabrik_t::get_fab(target_pos)) { to_factory = 1; } } } void ware_t::finish_rd(karte_t *welt) { if( welt->load_version<=111005 ) { // since some halt was referred by with several koordinates // this routine will correct it if(target_halt.is_bound()) { target_halt = haltestelle_t::get_halt_koord_index(target_halt->get_init_pos()); } if(via_halt.is_bound()) { via_halt = haltestelle_t::get_halt_koord_index(via_halt->get_init_pos()); } } update_factory_target(); } void ware_t::rotate90(sint16 y_size ) { target_pos.rotate90( y_size ); update_factory_target(); } void ware_t::update_factory_target() { if (to_factory) { // assert that target coordinates are unique for cargo going to the same factory // as new cargo will be generated with possibly new factory coordinates fabrik_t *fab = fabrik_t::get_fab(target_pos ); if (fab) { target_pos = fab->get_pos().get_2d(); } } } sint64 ware_t::calc_revenue(const goods_desc_t* desc, waytype_t wt, sint32 speedkmh) { static karte_ptr_t welt; const sint32 ref_kmh = welt->get_average_speed( wt ); const sint32 kmh_base = (100 * speedkmh) / ref_kmh - 100; const sint64 grundwert128 = welt->get_settings().get_bonus_basefactor(); // minimal bonus factor const sint64 grundwert_bonus = 1000 + kmh_base*desc->get_speed_bonus(); // speed bonus factor return desc->get_value() * std::max(grundwert128, grundwert_bonus); } ware_t::goods_amount_t ware_t::add_goods(goods_amount_t const to_add) { const goods_amount_t limit = GOODS_AMOUNT_LIMIT - amount; if (to_add > limit) { amount = GOODS_AMOUNT_LIMIT; return to_add - limit; } amount += to_add; return 0; } ware_t::goods_amount_t ware_t::remove_goods(goods_amount_t const to_remove) { if (to_remove > amount) { goods_amount_t const remainder = to_remove - amount; amount = 0; return remainder; } amount -= to_remove; return 0; } simutrans-124.5/src/simutrans/simware.h000066400000000000000000000103001520675460500202120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SIMWARE_H #define SIMWARE_H #include "halthandle.h" #include "dataobj/koord.h" #include "descriptor/goods_desc.h" class goods_manager_t; class karte_t; class player_t; /// Class to handle goods packets (and their destinations) class ware_t { friend class goods_manager_t; private: /// private lookup table to speedup static const goods_desc_t *index_to_desc[256]; public: /// Type used to specify an amount of goods. Always positive. typedef uint32 goods_amount_t; /// Maximum number of goods per ware package. Limited by the bit field used. static goods_amount_t const GOODS_AMOUNT_LIMIT = (1 << 23) - 1; /// type of good, used as index into goods-types array uint32 index : 8; /// amount of goods goods_amount_t amount : 23; /// To indicate that the ware's destination is a factory/consumer store uint32 to_factory : 1; private: /// Final destination halt. halthandle_t target_halt; /// Halt where the packet has to leave the convoy. halthandle_t via_halt; /// Target position (factory, etc) koord target_pos; /// Update target (zielpos) for factory-going goods (after loading or rotating) void update_factory_target(); public: ware_t(); ware_t(const goods_desc_t *desc); ware_t(loadsave_t *file); public: const halthandle_t &get_target_halt() const { return target_halt; } void set_target_halt(const halthandle_t &halt) { target_halt = halt; } const halthandle_t &get_via_halt() const { return via_halt; } void set_via_halt(const halthandle_t &halt) { via_halt = halt; } /// @returns the next halt of a ware const halthandle_t get_next_halt() const { return via_halt.is_bound() ? via_halt : target_halt; } koord get_target_pos() const { return target_pos; } void set_target_pos(const koord new_pos) { target_pos = new_pos; } /// @returns the non-translated name of the ware. const char *get_name() const { return get_desc()->get_name(); } const char *get_mass() const { return get_desc()->get_mass(); } uint8 get_catg() const { return get_desc()->get_catg(); } uint8 get_catg_index() const { return get_desc()->get_catg_index(); } uint8 get_index() const { return index; } const goods_desc_t *get_desc() const { return index_to_desc[index]; } void rdwr(loadsave_t *file); void finish_rd(karte_t *welt); // find out the category ... bool is_passenger() const { return index==0; } bool is_mail() const { return index==1; } bool is_freight() const { return index>2; } bool operator==(const ware_t &other) const { return index == other.index && amount == other.amount && to_factory == other.to_factory && target_halt == other.target_halt && via_halt == other.via_halt && target_pos == other.target_pos; } bool operator!=(const ware_t &w) const { return !(*this == w); } /// Mail and passengers just care about target station /// Freight needs to obey coordinates (since more than one factory might by connected) inline bool same_destination(const ware_t &w) const { return index == w.index && target_halt == w.target_halt && to_factory == w.to_factory && (!to_factory || target_pos==w.target_pos); } /** * Adjust target coordinates. * Must be called after factories have been rotated! */ void rotate90( sint16 y_size ); /** * Calculates transport revenue per tile and freight unit. * Takes speedbonus into account! * @param desc the freight * @param wt waytype of vehicle * @param speedkmh actual achieved speed in km/h */ static sint64 calc_revenue(const goods_desc_t *desc, waytype_t wt, sint32 speedkmh); /// Adds the number of goods to this goods packet. /// @return Any excess goods that could not be added, eg due to logical limits. goods_amount_t add_goods(goods_amount_t const to_add); /// Removes the number of goods from this goods packet. /// @return Any excess goods that could not be removed, eg due to logical limits. goods_amount_t remove_goods(goods_amount_t const to_remove); /// @returns True if goods amount is maxed out so no more goods can be added to this packet bool is_goods_amount_maxed() const { return amount == GOODS_AMOUNT_LIMIT; } }; #endif simutrans-124.5/src/simutrans/sound/000077500000000000000000000000001520675460500175305ustar00rootroot00000000000000simutrans-124.5/src/simutrans/sound/AVF_core-audio_sound.mm000066400000000000000000000023551520675460500240230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* * Apple OSX Core Audio MIDI routine added by Leopard */ #include "sound.h" #import #import #import #import static NSMutableArray* players_WAV; bool dr_init_sound() { printf("Sound system Initialise\n"); printf("Wave File database\n"); players_WAV = [NSMutableArray arrayWithCapacity: 128]; printf("Sound system Initialisation complete\n"); return true; } int dr_load_sample(char const* const filename) { NSURL* const url = [NSURL fileURLWithPath: [NSString stringWithUTF8String: filename]]; AVAudioPlayer* const player = [[AVAudioPlayer alloc] initWithContentsOfURL: url error: nil]; if (!player) { printf("** Warning, unable to open wav file %s\n", filename); return -1; } // Preload the file into memory. [player setVolume: 0]; [player play]; [players_WAV addObject: player]; int const i = [players_WAV count] - 1; printf("Load WAV (%d): %s\n", i, filename); return i; } void dr_play_sample(int const key, int const volume) { AVAudioPlayer* const m = [players_WAV objectAtIndex: key]; [m setVolume: volume / 255.f]; [m play]; } simutrans-124.5/src/simutrans/sound/core-audio_sound.mm000066400000000000000000000022071520675460500233230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* * Apple OSX Core Audio MIDI routine added by Leopard */ #include "sound.h" #import #import #import #import static NSMutableArray* movies_WAV; bool dr_init_sound() { printf("Sound system Initialise\n"); printf("Wave File database\n"); movies_WAV = [NSMutableArray arrayWithCapacity: 128]; printf("Sound system Initialisation complete\n"); return true; } int dr_load_sample(char const* const filename) { NSString* const s = [NSString stringWithUTF8String: filename]; QTMovie* const m = [QTMovie movieWithFile: s error: nil]; if (!m) { printf("** Warning, unable to open wav file %s\n", filename); return -1; } // Preload the file into memory. [m setVolume: 0]; [m play]; [movies_WAV addObject: m]; int const i = [movies_WAV count] - 1; printf("Load WAV (%d): %s\n", i, filename); return i; } void dr_play_sample(int const key, int const volume) { QTMovie* const m = [movies_WAV objectAtIndex: key]; [m setVolume: volume / 255.f]; [m play]; } simutrans-124.5/src/simutrans/sound/no_sound.cc000066400000000000000000000007061520675460500216660ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /// stub for no sound #include "sound.h" bool dr_init_sound(void) { return false; } /** * loads a sample * @return a handle for that sample or -1 on failure */ int dr_load_sample(const char *) { return -1; } /** * plays a sample * * @param key the key for the sample to be played * @param volume */ void dr_play_sample(int, int) { } simutrans-124.5/src/simutrans/sound/sdl2_sound.cc000066400000000000000000000124461520675460500221220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /// sdl-sound without SDL_mixer.dll #if !defined __APPLE__ && !defined __ANDROID__ #include #else #include #endif #include "sound.h" #include "../simmem.h" #include "../simdebug.h" #include #include /// flag if sound module should be used /// -1: error during initialization /// 0: not initialized /// 1: successfully initialized static int use_sound = 0; /// defines the number of channels available #define NUM_AUDIO_CHANNELS 4 /// Used to indicate that a channel is empty #define NO_SAMPLE (0xFF) /// this structure contains the data for one sample struct sample { // the buffer containing the data for the sample, the format // must always be identical to the one of the system output // format Uint8 *audio_data; Uint32 audio_len; // number of samples in the audio data }; /// this list contains all the samples static sample samples[64]; /// all samples are stored chronologically there static int samplenumber = 0; /// this structure contains the information about one channel struct channel { Uint32 sample_pos; ///< the current position inside this sample Uint8 sample; ///< which sample is played, or NO_SAMPLE if there is none Uint8 volume; ///< the volume this channel should be played }; /// this array contains all the information of the currently played samples static channel channels[NUM_AUDIO_CHANNELS]; /// the format of the output audio channel in use /// all loaded waved need to be converted to this format static SDL_AudioSpec output_audio_format; static SDL_AudioDeviceID audio_device; void sdl_sound_callback(void *, Uint8 * stream, int len) { memset(stream, 0, len); // SDL2 requires the output stream to be fully written on every callback. // add all the sample that need to be played for( int c = 0; c < NUM_AUDIO_CHANNELS; c++ ) { if (channels[c].sample == NO_SAMPLE) { // only do something if the channel is used continue; } sample *smp = &samples[channels[c].sample]; // add sample if (len + channels[c].sample_pos >= smp->audio_len ) { // SDL_MixAudio(stream, smp->audio_data + channels[c].sample_pos, smp->audio_len - channels[c].sample_pos, channels[c].volume); channels[c].sample = NO_SAMPLE; } else { SDL_MixAudioFormat(stream, smp->audio_data + channels[c].sample_pos, output_audio_format.format, len, channels[c].volume); channels[c].sample_pos += len; } } } bool dr_init_sound() { // avoid init twice if (use_sound != 0) { return use_sound > 0; } // initialize SDL sound subsystem if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) { dbg->error("dr_init_sound(SDL2)", "Could not initialize sound system. Muting."); use_sound = -1; return false; } // open an audio channel SDL_AudioSpec desired; desired.freq = 22050; desired.channels = 1; desired.format = AUDIO_S16SYS; desired.samples = 1024; desired.userdata = NULL; desired.callback = sdl_sound_callback; // allow any change as loaded .wav files are converted to output format upon loading audio_device = SDL_OpenAudioDevice( NULL, 0, &desired, &output_audio_format, SDL_AUDIO_ALLOW_ANY_CHANGE ); if( !audio_device ) { dbg->error("dr_init_sound(SDL2)", "Could not open required audio channel. Muting."); SDL_QuitSubSystem(SDL_INIT_AUDIO); use_sound = -1; return false; } // finished initializing for (int i = 0; i < NUM_AUDIO_CHANNELS; i++) { channels[i].sample = NO_SAMPLE; } // start playing sounds SDL_PauseAudioDevice(audio_device, 0); use_sound = 1; return true; } int dr_load_sample(const char *filename) { if(use_sound<=0 || samplenumber>=64) { return -1; } SDL_AudioSpec wav_spec; SDL_AudioCVT wav_cvt; Uint8 *wav_data; Uint32 wav_length; sample smp; // load the sample if (SDL_LoadWAV(filename, &wav_spec, &wav_data, &wav_length) == NULL) { dbg->warning("dr_load_sample", "Could not load wav: %s", SDL_GetError()); return -1; } /* convert the loaded wav to the internally used sound format */ if (SDL_BuildAudioCVT(&wav_cvt, wav_spec.format, wav_spec.channels, wav_spec.freq, output_audio_format.format, output_audio_format.channels, output_audio_format.freq) < 0) { dbg->error("dr_load_sample", "Could not create conversion structure"); SDL_FreeWAV(wav_data); return -1; } wav_cvt.buf = MALLOCN(Uint8, wav_length * wav_cvt.len_mult); wav_cvt.len = wav_length; memcpy(wav_cvt.buf, wav_data, wav_length); SDL_FreeWAV(wav_data); if (SDL_ConvertAudio(&wav_cvt) < 0) { dbg->error("dr_load_sample", "Could not convert wav to output format"); return -1; } // save the data smp.audio_data = wav_cvt.buf; smp.audio_len = wav_cvt.len_cvt; samples[samplenumber] = smp; dbg->message("dr_load_sample", "Loaded '%s' to sample %i.", filename, samplenumber); return samplenumber++; } /* * plays a sample * * @param sample_number the key for the sample to be played * @param volume */ void dr_play_sample(int sample_number, int volume) { if (use_sound <= 0 || sample_number == -1) { return; } // find an empty channel, and play for (int c = 0; c < NUM_AUDIO_CHANNELS; c++) { if (channels[c].sample == NO_SAMPLE) { channels[c].sample = sample_number; channels[c].sample_pos = 0; channels[c].volume = volume * SDL_MIX_MAXVOLUME >> 8; break; } } } simutrans-124.5/src/simutrans/sound/sdl_mixer_sound.cc000066400000000000000000000051601520675460500232370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ // Sound with SDL_mixer.dll (not changing the volume of other programs) #include #include #include #include "sound.h" #include "../simdebug.h" /* * flag if sound module should be used */ static int use_sound = 0; /* this list contains all the samples */ static Mix_Chunk *samples[64]; /* all samples are stored chronologically there */ static int samplenumber = 0; /** * Sound initialisation routine */ bool dr_init_sound() { int sound_ok = 0; if(use_sound!=0) { return true; // avoid init twice } use_sound = 1; // initialize SDL sound subsystem if (SDL_InitSubSystem(SDL_INIT_AUDIO) != -1) { // open an audio channel int freq = 22050; int channels = 1; unsigned short int format = AUDIO_S16SYS; int samples = 1024; if (Mix_OpenAudio(freq, format, channels, samples) != -1) { Mix_QuerySpec(&freq, &format, &channels); // check if we got the right audio format if (format == AUDIO_S16SYS) { // finished initializing sound_ok = 1; // allocate 16 mixing channels Mix_AllocateChannels(16); // start playing sounds Mix_ResumeMusic(); } else { dbg->error("dr_init_sound()","Open audio channel doesn't meet requirements. Muting"); Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); } } else { dbg->error("dr_init_sound()","Could not open required audio channel. Muting"); SDL_QuitSubSystem(SDL_INIT_AUDIO); } } else { dbg->error("dr_init_sound()","Could not initialize sound system. Muting"); } use_sound = sound_ok ? 1: -1; return sound_ok; } /* * loads a sample * * @param filename * @return a handle for that sample or -1 on failure */ int dr_load_sample(const char *filename) { if(use_sound>0 && samplenumber<64) { Mix_Chunk *smp; /* load the sample */ smp = Mix_LoadWAV(filename); if (smp == NULL) { dbg->warning("dr_load_sample()", "could not load wav (%s)", SDL_GetError()); return -1; } samples[samplenumber] = smp; dbg->message("dr_load_sample()", "Loaded %s to sample %i.", filename, samplenumber); return samplenumber++; } return -1; } /** * plays a sample * * @param sample_nummer the key for the sample to be played * @param volume */ void dr_play_sample(int sample_number, int volume) { // sound enabled and a valid sample if(use_sound>0 && sample_number != -1) { // sdl_mixer finds free channel, we then play at correct volume int play_channel = Mix_PlayChannel(-1, samples[sample_number], 0); Mix_Volume(play_channel,(volume*MIX_MAX_VOLUME)/256); } } simutrans-124.5/src/simutrans/sound/sdl_sound.cc000066400000000000000000000125321520675460500220340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /// sdl-sound without SDL_mixer.dll #include #include #include "sound.h" #include "../simmem.h" #include "../simdebug.h" #include /* * flag if sound module should be used */ static int use_sound = 0; /* * defines the number of channels available */ #define NUM_AUDIO_CHANNELS 4 /* * this structure contains the data for one sample */ struct sample { /* the buffer containing the data for the sample, the format * must always be identical to the one of the system output * format */ Uint8 *audio_data; Uint32 audio_len; /* number of samples in the audio data */ }; /* this list contains all the samples */ static sample samples[64]; /* all samples are stored chronologically there */ static int samplenumber = 0; /* this structure contains the information about one channel */ struct channel { Uint32 sample_pos; /* the current position inside this sample */ Uint8 sample; /* which sample is played, 255 for no sample */ Uint8 volume; /* the volume this channel should be played */ }; /* this array contains all the information of the currently played samples */ static channel channels[NUM_AUDIO_CHANNELS]; /* the format of the output audio channel in use * all loaded waved need to be converted to this format */ static SDL_AudioSpec output_audio_format; void sdl_sound_callback(void *, Uint8 * stream, int len) { memset(stream, 0, len); // SDL2 requires the output stream to be fully written on every callback. /* * add all the sample that need to be played */ for( int c = 0; c < NUM_AUDIO_CHANNELS; c++ ) { /* * only do something, if the channel is used */ if (channels[c].sample != 255) { sample * smp = &samples[channels[c].sample]; /* * add sample */ if (len + channels[c].sample_pos >= smp->audio_len ) { // SDL_MixAudio(stream, smp->audio_data + channels[c].sample_pos, smp->audio_len - channels[c].sample_pos, channels[c].volume); channels[c].sample = 255; } else { SDL_MixAudio(stream, smp->audio_data + channels[c].sample_pos, len, channels[c].volume); channels[c].sample_pos += len; } } } } /** * Sound initialisation routine */ bool dr_init_sound() { int sound_ok = 0; if(use_sound!=0) { return use_sound; // avoid init twice } use_sound = 1; // initialize SDL sound subsystem if (SDL_InitSubSystem(SDL_INIT_AUDIO) != -1) { // open an audio channel SDL_AudioSpec desired; desired.freq = 22050; desired.channels = 1; desired.format = AUDIO_S16SYS; desired.samples = 1024; desired.userdata = NULL; desired.callback = sdl_sound_callback; if (SDL_OpenAudio(&desired, &output_audio_format) != -1) { // check if we got the right audio format if (output_audio_format.format == AUDIO_S16SYS) { int i; // finished initializing sound_ok = 1; for (i = 0; i < NUM_AUDIO_CHANNELS; i++) channels[i].sample = 255; // start playing sounds SDL_PauseAudio(0); } else { dbg->error("dr_init_sound()", "Open audio channel doesn't meet requirements. Muting"); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); } } else { dbg->error("dr_init_sound()", "Could not open required audio channel. Muting"); SDL_QuitSubSystem(SDL_INIT_AUDIO); } } else { dbg->error("dr_init_sound()", "Could not initialize sound system. Muting"); } use_sound = sound_ok ? 1: -1; return sound_ok; } /** * loads a sample * * @param filename * @return a handle for that sample or -1 on failure */ int dr_load_sample(const char *filename) { if(use_sound>0 && samplenumber<64) { SDL_AudioSpec wav_spec; SDL_AudioCVT wav_cvt; Uint8 *wav_data; Uint32 wav_length; sample smp; /* load the sample */ if (SDL_LoadWAV(filename, &wav_spec, &wav_data, &wav_length) == NULL) { dbg->warning("dr_load_sample", "could not load wav (%s)", SDL_GetError()); return -1; } /* convert the loaded wav to the internally used sound format */ if (SDL_BuildAudioCVT(&wav_cvt, wav_spec.format, wav_spec.channels, wav_spec.freq, output_audio_format.format, output_audio_format.channels, output_audio_format.freq) < 0) { dbg->warning("dr_load_sample", "could not create conversion structure"); SDL_FreeWAV(wav_data); return -1; } wav_cvt.buf = MALLOCN(Uint8, wav_length * wav_cvt.len_mult); wav_cvt.len = wav_length; memcpy(wav_cvt.buf, wav_data, wav_length); SDL_FreeWAV(wav_data); if (SDL_ConvertAudio(&wav_cvt) < 0) { dbg->warning("dr_load_sample", "could not convert wav to output format"); return -1; } /* save the data */ smp.audio_data = wav_cvt.buf; smp.audio_len = wav_cvt.len_cvt; samples[samplenumber] = smp; dbg->message("dr_load_sample", "Loaded %s to sample %i.",filename,samplenumber); return samplenumber++; } return -1; } /* * plays a sample * * @param sample_number the key for the sample to be played * @param volume */ void dr_play_sample(int sample_number, int volume) { if(use_sound>0) { int c; if (sample_number == -1) { return; } /* find an empty channel, and play */ for (c = 0; c < NUM_AUDIO_CHANNELS; c++) { if (channels[c].sample == 255) { channels[c].sample = sample_number; channels[c].sample_pos = 0; channels[c].volume = volume * SDL_MIX_MAXVOLUME >> 8; break; } } } } simutrans-124.5/src/simutrans/sound/sound.h000066400000000000000000000007721520675460500210370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SOUND_SOUND_H #define SOUND_SOUND_H /** * Sound initialisation routine */ bool dr_init_sound(); /** * loads a sample * * @param filename * @return a handle for that sample or -1 on failure */ int dr_load_sample(const char* filename); /** * plays a sample * * @param key the handle for the sample to be played * @param volume */ void dr_play_sample(int key, int volume); #endif simutrans-124.5/src/simutrans/sound/win32_sound.cc000066400000000000000000000035111520675460500222110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "sound.h" #include "../sys/simsys.h" /* * flag if sound module should be used * with Win32 the number+1 of the device used */ static int use_sound = 0; /* this list contains all the samples */ static void *samples[64]; static int sample_number = 0; /** * Sound initialisation routine */ bool dr_init_sound() { use_sound = 1; return true; } /** * loads a single sample * * @param filename * @return a handle for that sample or -1 on failure */ int dr_load_sample(char const* filename) { if(use_sound && sample_number>=0 && sample_number<64) { if (FILE* const fIn = dr_fopen(filename, "rb")) { long len; fseek( fIn, 0, SEEK_END ); len = ftell( fIn ); if(len>0) { samples[sample_number] = GlobalLock( GlobalAlloc( GMEM_MOVEABLE, (len+4)&0x7FFFFFFCu ) ); rewind( fIn ); fread( samples[sample_number], len, 1, fIn ); fclose( fIn ); return sample_number++; } } } return -1; } /* * plays a sample * * @param sample_number the key for the sample to be played * @param volume */ void dr_play_sample(int sample_number, int volume) { if(use_sound!=0 && sample_number>=0 && sample_number<64 && volume>1) { static int oldvol = -1; volume = (volume<<8)-1; if(oldvol!=volume) { DWORD vol = (volume<<16)|volume; waveOutSetVolume( 0, vol ); oldvol = volume; } UINT flags = SND_MEMORY | SND_ASYNC | SND_NODEFAULT; // Terminate the current sound, if not already the requested one. static int last_sample_nr = -1; if( last_sample_nr == sample_number ) { flags |= SND_NOSTOP; } last_sample_nr = sample_number; sndPlaySound(static_cast(samples[sample_number]), flags); } } simutrans-124.5/src/simutrans/sound/win32_sound_xa.cc000066400000000000000000000120421520675460500227000ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ /* The code is mostly copied and slightly adapted straight from the microsoft documetnation. * There was no clear indicating of license, so public domain is assumed. */ #include #if defined (__GNUC__) #include #else #include #endif #include "../tpl/vector_tpl.h" vector_tpl wfs; vector_tpl bufs; static IXAudio2* pXAudio2 = NULL; static IXAudio2MasteringVoice* pMasterVoice = NULL; /** * Sound initialisation routine */ bool dr_init_sound() { if( XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR) < 0) { return false; } if( pXAudio2->CreateMasteringVoice(&pMasterVoice) < 0 ) { return false; } return true; } #ifdef SIM_BIG_ENDIAN #define fourccRIFF 'RIFF' #define fourccDATA 'data' #define fourccFMT 'fmt ' #define fourccWAVE 'WAVE' #define fourccXWMA 'XWMA' #define fourccDPDS 'dpds' #else #define fourccRIFF 'FFIR' #define fourccDATA 'atad' #define fourccFMT ' tmf' #define fourccWAVE 'EVAW' #define fourccXWMA 'AMWX' #define fourccDPDS 'sdpd' #endif HRESULT FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition) { HRESULT hr = S_OK; if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN)) return HRESULT_FROM_WIN32(GetLastError()); DWORD dwChunkType; DWORD dwChunkDataSize; DWORD dwRIFFDataSize = 0; DWORD dwFileType; DWORD bytesRead = 0; DWORD dwOffset = 0; while (hr == S_OK) { DWORD dwRead; if (0 == ReadFile(hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL)) hr = HRESULT_FROM_WIN32(GetLastError()); if (0 == ReadFile(hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL)) hr = HRESULT_FROM_WIN32(GetLastError()); switch (dwChunkType) { case fourccRIFF: dwRIFFDataSize = dwChunkDataSize; dwChunkDataSize = 4; if (0 == ReadFile(hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL)) hr = HRESULT_FROM_WIN32(GetLastError()); break; default: if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, dwChunkDataSize, NULL, FILE_CURRENT)) return HRESULT_FROM_WIN32(GetLastError()); } dwOffset += sizeof(DWORD) * 2; if (dwChunkType == fourcc) { dwChunkSize = dwChunkDataSize; dwChunkDataPosition = dwOffset; return S_OK; } dwOffset += dwChunkDataSize; if (bytesRead >= dwRIFFDataSize) return S_FALSE; } return S_OK; } static HRESULT ReadChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset) { HRESULT hr = S_OK; if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, bufferoffset, NULL, FILE_BEGIN)) return HRESULT_FROM_WIN32(GetLastError()); DWORD dwRead; if (0 == ReadFile(hFile, buffer, buffersize, &dwRead, NULL)) hr = HRESULT_FROM_WIN32(GetLastError()); return hr; } /* * loads a single sample * * @param strFileName * @return a handle for that sample or -1 on failure */ int dr_load_sample(char const* strFileName) { WAVEFORMATEXTENSIBLE wfx = { 0 }; XAUDIO2_BUFFER buffer = { 0 }; // Open the file HANDLE hFile = CreateFileA(strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == hFile) { return -1; } if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN)) { return -1; } DWORD dwChunkSize; DWORD dwChunkPosition; //check the file type, should be fourccWAVE or 'XWMA' FindChunk(hFile, fourccRIFF, dwChunkSize, dwChunkPosition); DWORD filetype; ReadChunkData(hFile, &filetype, sizeof(DWORD), dwChunkPosition); if (filetype != fourccWAVE) { return -1; } FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition); ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition); //fill out the audio data buffer with the contents of the fourccDATA chunk FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition); BYTE* pDataBuffer = new BYTE[dwChunkSize]; ReadChunkData(hFile, pDataBuffer, dwChunkSize, dwChunkPosition); buffer.AudioBytes = dwChunkSize; //size of the audio buffer in bytes buffer.pAudioData = pDataBuffer; //buffer containing audio data buffer.Flags = XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after this buffer wfs.append(wfx); bufs.append(buffer); return wfs.get_count() - 1; } /* * plays a sample * * @param sample_number the key for the sample to be played * @param volume */ void dr_play_sample(int sample_number, int volume) { IXAudio2SourceVoice* pSourceVoice; if (pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&(wfs[sample_number]))<0 ||pSourceVoice->SubmitSourceBuffer(&(bufs[sample_number])) < 0) { return; } pSourceVoice->SetVolume(volume/256.0); pSourceVoice->Start(0); } simutrans-124.5/src/simutrans/sys/000077500000000000000000000000001520675460500172165ustar00rootroot00000000000000simutrans-124.5/src/simutrans/sys/clipboard_internal.cc000066400000000000000000000035501520675460500233630ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "simsys.h" #include "../utils/unicode.h" #define MAX_SIZE (4096) static char content[MAX_SIZE] = ""; static size_t content_length = 0; /** * Copy text to the clipboard * @param source : pointer to the start of the source text * @param length : number of character bytes to copy */ void dr_copy(const char *source, size_t length) { assert( length0 ) { *content_ptr++ = *source++; } } /** * Paste text from the clipboard * @param target : pointer to the insertion position in the target text * @param max_length : maximum number of character bytes which could be inserted * @return number of character bytes actually inserted -> for cursor advancing */ size_t dr_paste(char *target, size_t max_length) { // determine the number of bytes to be pasted if( content_length<=max_length ) { max_length = content_length; } else { // ensure that max_length aligns with logical character boundaries of clipboard content size_t tmp_length = 0; size_t byte_count; while( tmp_length+(byte_count=utf8_get_next_char(content+tmp_length,0u))<=max_length ) { tmp_length += byte_count; } max_length = tmp_length; } const size_t inserted_length = max_length; // move the text to free up space for inserting the clipboard content char *target_old_end = target + strlen(target); char *target_new_end = target_old_end + max_length; while( target_old_end>=target ) { *target_new_end-- = *target_old_end--; } // insert the clipboard content const char *content_ptr = content; while( (max_length--)>0 ) { *target++ = *content_ptr++; } // return the inserted length for cursor advancing return inserted_length; } simutrans-124.5/src/simutrans/sys/clipboard_s2.cc000066400000000000000000000026451520675460500220770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simsys.h" #include "../display/simgraph.h" #include "../simdebug.h" #include "../dataobj/translator.h" #include "../utils/unicode.h" #if !defined __APPLE__ && !defined __ANDROID__ #include #else #include #endif #include /** * Copy text to the clipboard * @param source : pointer to the start of the source text * @param length : number of character bytes to copy */ void dr_copy(const char *source, size_t length) { char *text = (char *)malloc( length+1 ); strncpy( text, source, length ); text[length] = 0; SDL_SetClipboardText( text ); free( text ); } /** * Paste text from the clipboard * @param target : pointer to the insertion position in the target text * @param max_length : maximum number of character bytes which could be inserted * @return number of character bytes actually inserted -> for cursor advancing */ size_t dr_paste(char *target, size_t max_length) { size_t insert_len = 0; if( SDL_HasClipboardText() ) { char *str = SDL_GetClipboardText(); // Insert string. size_t const str_len = strlen(str); insert_len = str_len <= max_length ? str_len : (size_t)utf8_get_prev_char((utf8 const *)str, max_length); memmove(target + insert_len, target, strlen(target) + 1); memcpy(target, str, insert_len); SDL_free( str ); } return insert_len; } simutrans-124.5/src/simutrans/sys/clipboard_w32.cc000066400000000000000000000041031520675460500221550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simsys.h" #include "../display/simgraph.h" #include "../simdebug.h" #include "../dataobj/translator.h" #include "../utils/unicode.h" #include #include /** * Copy text to the clipboard * @param source : pointer to the start of the source text * @param length : number of character bytes to copy */ void dr_copy(const char *source, size_t length) { if( OpenClipboard(NULL) ) { if( EmptyClipboard() ) { // Allocate clipboard space. int const len = (int)length; int const wsize = MultiByteToWideChar(CP_UTF8, 0, source, len, NULL, 0) + 1; HANDLE hglbCopy = GlobalAlloc(GMEM_MOVEABLE, wsize * sizeof(WCHAR)); LPWSTR const wstr = (LPWSTR)GlobalLock(hglbCopy); if (wstr != NULL) { // Convert string. NUL appended implicitly by 0ed memory. MultiByteToWideChar(CP_UTF8, 0, source, len, wstr, wsize); } GlobalUnlock(hglbCopy); SetClipboardData(CF_UNICODETEXT, hglbCopy); } CloseClipboard(); } } /** * Paste text from the clipboard * @param target : pointer to the insertion position in the target text * @param max_length : maximum number of character bytes which could be inserted * @return number of character bytes actually inserted -> for cursor advancing */ size_t dr_paste(char *target, size_t max_length) { size_t insert_len = 0; if( OpenClipboard(NULL) ) { LPWSTR const wstr = (LPWSTR)GetClipboardData(CF_UNICODETEXT); if( wstr != NULL ) { // Convert entire clipboard to UTF-8. int const size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); char *const str = new char[size]; WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, size, NULL, NULL); // Insert string. size_t const str_len = strlen(str); insert_len = str_len <= max_length ? str_len : (size_t)utf8_get_prev_char((utf8 const *)str, max_length); memmove(target + insert_len, target, strlen(target) + 1); memcpy(target, str, insert_len); delete[] str; } CloseClipboard(); } return insert_len; } simutrans-124.5/src/simutrans/sys/simsys.cc000066400000000000000000001127221520675460500210610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include #ifdef __HAIKU__ #include #include #include #include #include #define NO_UINT32_TYPES #define NO_UINT64_TYPES #endif #include "../dataobj/environment.h" #include "../macros.h" #include "../simmain.h" #include "simsys.h" #include "../pathes.h" #include "../simevent.h" #include "../utils/simstring.h" #include "../simdebug.h" #include "../simevent.h" #include "../simversion.h" #ifdef _WIN32 # include # include # include # include # include # if !defined(__CYGWIN__) # include # else # include # endif # include "../simdebug.h" #else # include # include # if !defined __AMIGA__ && !defined __BEOS__ # include # endif # ifdef __ANDROID__ # include "../utils/searchfolder.h" # include # include # include # endif #endif #ifdef USE_FONTCONFIG #include #endif #ifdef MULTI_THREAD #include #endif #ifdef _OPTIMIZED #define L_DEBUG_TEXT " (optimized)" #elif defined DEBUG # define L_DEBUG_TEXT " (debug)" #else # define L_DEBUG_TEXT #endif // Windows does not define the S_ISREG macro in stat.h. // We have to define _CRT_INTERNAL_NONSTDC_NAMES 1 before #including sys/stat.h // in order for Microsoft's stat.h to define names like S_IFMT, S_IFREG, // rather than just defining _S_IFMT and _S_IFREG as it normally does. #define _CRT_INTERNAL_NONSTDC_NAMES 1 #include #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif sys_event_t sys_event; #ifdef _WIN32 /** * Utility class to handle conversion of UTF-8 to UTF-16 for use by Windows APIs. * Constructs a UTF-16 view of the current contents of a UTF-8 C string. */ class U16View { private: LPCWSTR cu16str; public: /** * Constructs a UTF-16 view of a UTF-8 C string. */ U16View(char const *const u8str) { // Convert UTF-8 to UTF-16. int const size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u8str, -1, NULL, 0); if(size == 0) { // It should never fail, since all file IO UTF8 comes either via keyborad input or filenames, i.e. a win32 api. // log may not even initialising at this point, so we just fail. dr_fatal_notify( "Unknown unicode character encountered!" ); exit(0); } LPWSTR const u16str = new WCHAR[size]; MultiByteToWideChar(CP_UTF8, 0, u8str, -1, u16str, size); cu16str = u16str; } /** * Copy constructor to allow safe movement. * Should also have move constructor to allow efficient movement but that requires C++11. */ U16View(U16View const& from) { LPWSTR const u16str = new WCHAR[wcslen(from.cu16str) + 1]; wcscpy(u16str, from.cu16str); cu16str = u16str; } ~U16View() { delete[] cu16str; } /** * Typecast operator to allow implicit use as a LPCWSTR. */ operator LPCWSTR() const { return cu16str; } }; #endif // Platform specific path separators. #ifdef _WIN32 char const PATH_SEPARATOR[] = "\\"; #else char const PATH_SEPARATOR[] = "/"; #endif scr_coord get_mouse_pos() { return { sys_event.mx, sys_event.my }; } uint8 dr_get_max_threads() { uint8 max_threads = 0; #ifdef MULTI_THREAD #if 0 // does not work when crosscompile with mingw! max_threads = std::thread::hardware_concurrency(); #endif if(max_threads==0) { #ifdef _WIN32 SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); max_threads = (uint8)sysinfo.dwNumberOfProcessors; #else max_threads = sysconf(_SC_NPROCESSORS_ONLN); #endif } #endif return max(max_threads,1); } // create a directory with all subdirectories needed int dr_mkdir(char const* const path) { if (path == 0 || path[0]==0) { // always succed on empty string (should never happen) return 0; } #ifdef _WIN32 const char* new_dir_with_path = path; // Perform operation needs absolute path if (path[1] != ':' && path[1] != '\\') { // so let get the absolute path char abs_buf[MAX_PATH]; dr_getcwd(abs_buf, MAX_PATH); if (strlen(abs_buf) + strlen(path) + 2 >= MAX_PATH) { return -1; } strcat(abs_buf, "\\"); strcat(abs_buf, path); new_dir_with_path = abs_buf; } int result = SHCreateDirectory(NULL, U16View(new_dir_with_path)) ? 0 : -1; // Translate error. if (result != ERROR_SUCCESS) { DWORD const error = GetLastError(); if (error == ERROR_ALREADY_EXISTS || error == ERROR_FILE_EXISTS) { errno = EEXIST; } else if (error == ERROR_PATH_NOT_FOUND) { errno = ENOENT; } } return result; #else // create also intermediate directories char tmp[PATH_MAX]; size_t len; snprintf(tmp, sizeof(tmp), "%s", path); // remove trailing director separator len = strnlen(tmp, sizeof(tmp)); if (tmp[len - 1] == *PATH_SEPARATOR) { tmp[len - 1] = 0; } // now walk the path for (char *p = tmp + 1; *p; p++ ) { if (*p == '/') { *p = '\0'; // mkdir mail fail if already existing, but we continue anyway mkdir(tmp, 0777); *p = '/'; } } return mkdir(path, 0777); #endif } bool dr_movetotrash(const char *path) { #ifdef _WIN32 // Convert to full path name to allow use of recycle bin. // Must be double null terminated for SHFILESTRUCT U16View const wpath(path); int const full_size = GetFullPathNameW(wpath, 0, NULL, NULL); wchar_t *const full_wpath = new wchar_t[full_size + 1]; GetFullPathNameW(wpath, full_size, full_wpath, NULL); full_wpath[full_size] = L'\0'; // Initalize file operation structure. SHFILEOPSTRUCTW FileOp; FileOp.hwnd = GetActiveWindow(); FileOp.wFunc = FO_DELETE; FileOp.pFrom = full_wpath; FileOp.pTo = NULL; FileOp.fFlags = FOF_ALLOWUNDO|FOF_NOCONFIRMATION; // Perform operation. int success = SHFileOperationW(&FileOp); delete[] full_wpath; BringWindowToTop(FileOp.hwnd); return success; #else return remove(path); #endif } bool dr_cantrash() { #ifdef _WIN32 return true; #else return false; #endif } int dr_remove(const char *path) { #ifdef _WIN32 // Perform operation. bool success = DeleteFileW(U16View(path)); // Translate error. if(!success) { DWORD error = GetLastError(); if(error == ERROR_FILE_NOT_FOUND) { errno = ENOENT; } else if(error == ERROR_ACCESS_DENIED) { errno = EACCES; } } // try to remove directory if this is a directory and empty ... success = RemoveDirectoryW(U16View(path)); return success ? 0 : -1; #else return remove(path); #endif } int dr_rename(const char *existing_utf8, const char *new_utf8) { #ifdef _WIN32 // Perform operation. bool success = MoveFileExW(U16View(existing_utf8), U16View(new_utf8), MOVEFILE_REPLACE_EXISTING); // Translate error. if(!success) { DWORD error = GetLastError(); if (error == ERROR_FILE_NOT_FOUND) { errno = ENOENT; } else if(error == ERROR_ACCESS_DENIED) { errno = EACCES; } } return success ? 0 : -1; #else remove( new_utf8 ); return rename( existing_utf8, new_utf8 ); #endif } int dr_chdir(const char *path) { #ifdef _WIN32 // Perform operation. bool success = SetCurrentDirectoryW(U16View(path)); // Translate error. if(!success) { DWORD error = GetLastError(); if (error == ERROR_FILE_NOT_FOUND) { errno = ENOENT; } else if(error == ERROR_ACCESS_DENIED) { errno = EACCES; } } return success ? 0 : -1; #else return chdir(path); #endif } char *dr_getcwd(char *buf, size_t size) { #ifdef _WIN32 DWORD wsize = GetCurrentDirectoryW(0, NULL); WCHAR *const wpath = new WCHAR[wsize]; DWORD success = GetCurrentDirectoryW(wsize, wpath); // Translate error. if(!success) { delete[] wpath; return NULL; } // Convert UTF-16 to UTF-8. int const convert_size = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, buf, (int)size, NULL, NULL); delete[] wpath; if(convert_size == 0) { return NULL; } return buf; #else return getcwd(buf, size); #endif } int dr_stat(const char *path, struct stat *buf) { #ifdef _WIN32 return _wstat(U16View(path), (struct _stat*)buf); #else return stat(path, buf); #endif } static inline bool is_regular_file(FILE *f) { struct stat s; #ifdef _WIN32 if (_fstat(_fileno(f), (struct _stat*)&s) != 0) { return false; } #else if (fstat(fileno(f), (struct stat *)&s) != 0) { return false; } #endif return S_ISREG(s.st_mode); } FILE *dr_fopen(const char *filename, const char *mode) { #ifdef _WIN32 FILE *f = _wfopen(U16View(filename), U16View(mode)); #else FILE *f = fopen(filename, mode); #endif if (!f) { return NULL; } // Try not to open a directory. POSIX does not specify what happens // when trying to fopen a directory *for reading*. We disallow this explicitly. if (!is_regular_file(f)) { fclose(f); return NULL; } return f; } gzFile dr_gzopen(const char *path, const char *mode) { #ifdef _WIN32 return gzopen_w(U16View(path), mode); #else return gzopen(path, mode); #endif } bool check_and_set_dir( const char *path, const char *info, char *result, const char *testfile) { if( path && *path ) { bool ok = !dr_chdir(path); // Attempt to create it (if we aren't testing for an existing directory containing a file). if(!ok && !testfile) { dr_mkdir(path); ok = !dr_chdir(path); } else if(testfile) { FILE* testf = fopen(testfile,"r"); ok = ok && testf; if(testf) { fclose(testf); } } if(!ok) { printf("WARNING: Objects not found in %s \"%s\"!\n", info, path); } else { dr_getcwd( result, PATH_MAX-1 ); strcat( result, PATH_SEPARATOR ); return true; } } return false; } /** * Simutrans has three directories: * base_dir: directory with default data (may be write protected) * install_dir: global writable directory where paksets are installed * user_dir: a user writable directory * * The directory will be determined in the following order * -set_XXXdir Path (command line option) * SIMUTRANS_XXXDIR (environment variable) * (in case of base_dir current path, then executable path) * (for user_dir and install_dir: machine dependent default directories) * * The pak_dir contains the complete path to the current pak, since it could be in different locations * */ bool dr_set_basedir(const char * base_dir_arg, char * executable_path) { bool found_basedir = false; #ifdef __ANDROID__ found_basedir = check_and_set_dir( SDL_AndroidGetInternalStoragePath(), "Android Internal Storage", env_t::base_dir, "config/simuconf.tab"); #else found_basedir = check_and_set_dir( base_dir_arg, "-set_basedir", env_t::base_dir, "config/simuconf.tab"); if( !found_basedir ) { found_basedir = check_and_set_dir( getenv("SIMUTRANS_BASEDIR"), "SIMUTRANS_BASEDIR", env_t::base_dir, "config/simuconf.tab"); if( !found_basedir ) { dr_getcwd(env_t::base_dir, lengthof(env_t::base_dir)); strcat( env_t::base_dir, PATH_SEPARATOR ); // test if base installation if (FILE* f = dr_fopen("config/simuconf.tab", "r")) { fclose(f); found_basedir = true; } else { char testpath[PATH_MAX]; tstrncpy(testpath, executable_path, lengthof(testpath)); char* c = strrchr(testpath, *PATH_SEPARATOR); if(c) { *c = 0; // remove program name found_basedir = check_and_set_dir(testpath, "program dir", env_t::base_dir, "config/simuconf.tab"); if(!found_basedir) { #ifdef __APPLE__ // Detect if the binary is started inside an application bundle // Change working dir from MacOS to Resources dir strcpy(env_t::base_dir, testpath); if( strstr(env_t::base_dir, ".app/Contents/MacOS") != NULL ) { while (env_t::base_dir[strlen(env_t::base_dir) - 1] != 's') { env_t::base_dir[strlen(env_t::base_dir) - 1] = 0; } strcat(env_t::base_dir, "/Resources/simutrans/"); found_basedir = true; } #else // Detect if simutrans has been installed by the system and try to locate the installation relative to the binary location char *c = strrchr(testpath, *PATH_SEPARATOR); if( c && strcmp(c+1,"bin")==0 ) { // replace bin with other paths strcpy( c+1, "share/simutrans/" ); found_basedir = check_and_set_dir(testpath, "program dir", env_t::base_dir, "config/simuconf.tab"); if (!found_basedir) { strcpy( c+1 , "share/games/simutrans/" ); found_basedir = check_and_set_dir(testpath, "share/games/simutrans", env_t::base_dir, "config/simuconf.tab"); } } #endif } } } } } #endif if (!found_basedir) { // try the installer dir next found_basedir = check_and_set_dir(dr_query_installdir(), "install_dir", env_t::base_dir, "config/simuconf.tab"); } return found_basedir; } char const *dr_query_homedir() { static char buffer[PATH_MAX + 24]; #if defined _WIN32 WCHAR whomedir[MAX_PATH]; if(FAILED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, whomedir))) { DWORD len = sizeof(whomedir); HKEY hHomeDir; if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_READ, &hHomeDir) != ERROR_SUCCESS) { return NULL; } LONG const status = RegQueryValueExW(hHomeDir, L"Personal", NULL, NULL, (LPBYTE)whomedir, &len); RegCloseKey(hHomeDir); if(status != ERROR_SUCCESS) { return NULL; } } // Convert UTF-16 to UTF-8. int const convert_size = WideCharToMultiByte(CP_UTF8, 0, whomedir, -1, buffer, sizeof(buffer), NULL, NULL); if(convert_size == 0) { return NULL; } // Append Simutrans folder. char const foldername[] = "Simutrans"; if(lengthof(buffer) < strlen(buffer) + strlen(foldername) + 2 * strlen(PATH_SEPARATOR) + 1){ return NULL; } strcat(buffer, PATH_SEPARATOR); strcat(buffer, foldername); #elif defined __APPLE__ int maxlen = PATH_MAX + 22; unsigned n = snprintf(buffer, maxlen, "%s/Library/Simutrans", getenv("HOME")); if (n >= maxlen) { return NULL; } #elif defined __HAIKU__ BPath userDir; find_directory(B_USER_DIRECTORY, &userDir); sprintf(buffer, "%s/simutrans", userDir.Path()); #elif defined __ANDROID__ buffer[0] = 0; int state = SDL_AndroidGetExternalStorageState(); if (state & SDL_ANDROID_EXTERNAL_STORAGE_WRITE) { const char* p = SDL_AndroidGetExternalStoragePath(); if (p) { strcpy(buffer, p); dr_mkdir("/sdcard/simutrans"); } } if (!*buffer) { dbg->warning("dr_query_homedir()", "SDL_AndroidGetExternalStoragePath() failed."); // no permission tstrncpy(buffer, SDL_GetPrefPath("Simutrans Team", "simutrans"), lengthof(buffer)); } #else int maxlen = PATH_MAX + 22; int n; if( getenv("XDG_DATA_HOME") == NULL ) { n = snprintf(buffer, maxlen, "%s/simutrans", getenv("HOME")); } else { n = snprintf(buffer, maxlen, "%s/simutrans", getenv("XDG_DATA_HOME")); } if (n >= maxlen) { return NULL; } #endif strcat(buffer, PATH_SEPARATOR); return buffer; } char const *dr_query_installdir() { static char buffer[PATH_MAX + 24]; #if defined _WIN32 WCHAR whomedir[MAX_PATH]; if(FAILED(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, whomedir))) { return NULL; } // Convert UTF-16 to UTF-8. int const convert_size = WideCharToMultiByte(CP_UTF8, 0, whomedir, -1, buffer, sizeof(buffer), NULL, NULL); if(convert_size == 0) { return NULL; } // Append Simutrans folder. char const foldername[] = "Simutrans"; if(lengthof(buffer) < strlen(buffer) + strlen(foldername) + 2 * strlen(PATH_SEPARATOR) + 1){ return NULL; } strcat(buffer, PATH_SEPARATOR); strcat(buffer, foldername); #elif defined __APPLE__ int maxlen = PATH_MAX + 22; unsigned n = snprintf(buffer, maxlen, "%s/Library/Simutrans/paksets", getenv("HOME")); if (n >= maxlen) { return NULL; } #elif defined __HAIKU__ BPath userDir; find_directory(B_USER_DIRECTORY, &userDir); sprintf(buffer, "%s/simutrans/paksets", userDir.Path()); #elif defined __ANDROID__ tstrncpy(buffer,SDL_AndroidGetExternalStoragePath(),lengthof(buffer)); #else int maxlen = PATH_MAX + 22; int n; if( getenv("XDG_DATA_HOME") == NULL ) { n = snprintf(buffer, maxlen, "%s/simutrans/paksets", getenv("HOME")); } else { n = snprintf(buffer, maxlen, "%s/simutrans/paksets", getenv("XDG_DATA_HOME")); } if (n >= maxlen) { return NULL; } #endif strcat(buffer, PATH_SEPARATOR); return buffer; } const char *dr_query_fontpath(int which) { #ifdef _WIN32 if (!which) { // Build full font file path static CHAR winDir[MAX_PATH]; GetWindowsDirectoryA(winDir, MAX_PATH); strcat(winDir, "\\Fonts\\"); return winDir; } return NULL; #else // linux has more than one path // sometimes there is the file "/etc/fonts/fonts.conf" and we can read it // however, since we cannot rely on it, we just try a few candiates // Apple only uses three locals, and Haiku has no standard ... const char *trypaths[] = { #ifdef __APPLE__ "~/Library/", "/Library/Fonts/", "/System/Library/Fonts/", #elif defined __HAIKU__ "/boot/system/non-packaged/data/fonts/", "/boot/home/config/non-packaged/data/fonts/", "~/config/non-packaged/data/fonts/", "/boot/system/data/fonts/ttfonts/", #elif defined __ANDROID__ "/system/fonts/", #else "~/.fonts/", "~/.local/share/fonts/", "/usr/share/fonts/", "/usr/X11R6/lib/X11/fonts/", "/usr/local/share/fonts/", #endif NULL }; if( trypaths[which] != NULL ) { static char fontpath[PATH_MAX]; if( trypaths[which][0] == '~' ) { // prepace with homedirectory snprintf( fontpath, PATH_MAX, "%s/%s", getenv("HOME"), trypaths[which]+2 ); } else { tstrncpy( fontpath, trypaths[which], PATH_MAX ); } return fontpath; } return NULL; #endif } std::string dr_get_system_font() { #if COLOUR_DEPTH != 0 #ifdef WIN32 #define DEFAULT_FONT "arial.ttf" NONCLIENTMETRICSW ncm; ncm.cbSize = sizeof(NONCLIENTMETRICSW); SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0); std::wstring wsFaceName = ncm.lfMessageFont.lfFaceName; LPCWSTR fontRegistryPath = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; HKEY hKey; LONG result; // Open Windows font registry key result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, fontRegistryPath, 0, KEY_READ, &hKey); if (result != ERROR_SUCCESS) { return DEFAULT_FONT; } DWORD maxValueNameSize, maxValueDataSize; result = RegQueryInfoKeyW(hKey, 0, 0, 0, 0, 0, 0, 0, &maxValueNameSize, &maxValueDataSize, 0, 0); if (result != ERROR_SUCCESS) { return DEFAULT_FONT; } DWORD valueIndex = 0; LPWSTR valueName = new WCHAR[maxValueNameSize]; LPBYTE valueData = new BYTE[maxValueDataSize]; DWORD valueNameSize, valueDataSize, valueType; std::wstring wsFontFile; // So far best matching font name std::wstring wsBestMatch; do { wsFontFile.clear(); valueDataSize = maxValueDataSize; valueNameSize = maxValueNameSize; result = RegEnumValueW(hKey, valueIndex, valueName, &valueNameSize, 0, &valueType, valueData, &valueDataSize); valueIndex++; if (result != ERROR_SUCCESS || valueType != REG_SZ) { continue; } std::wstring wsValueName(valueName, valueNameSize); // Found a match if (_wcsnicmp(wsFaceName.c_str(), wsValueName.c_str(), wsFaceName.length()) == 0) { // full match wsFontFile.assign((LPWSTR)valueData, valueDataSize/2); break; } // Sometimes the face name is a family name; then only a partial match will be possible if (wcsstr(wsValueName.c_str(), wsFaceName.c_str())) { wsBestMatch.assign((LPWSTR)valueData, valueDataSize/2); } } while (result != ERROR_NO_MORE_ITEMS); delete[] valueName; delete[] valueData; RegCloseKey(hKey); if (wsFontFile.empty()) { if (wsBestMatch.empty()) { dbg->warning("dr_get_system_font()", "%s not found!", std::string(wsFaceName.begin(), wsFaceName.end()).c_str()); return DEFAULT_FONT; } wsFontFile = wsBestMatch; dbg->warning("dr_get_system_font()", "Using %s for %s", std::string(wsFontFile.begin(), wsFontFile.end()).c_str(), std::string(wsFaceName.begin(), wsFaceName.end()).c_str()); } // Build full font file path CHAR winDir[MAX_PATH]; GetWindowsDirectoryA(winDir, MAX_PATH); strcat(winDir, "\\Fonts\\"); // luckily any TTF font in windows has an ASCII name ... DBG_MESSAGE("dr_get_system_font()", "Using %s", std::string(wsFontFile.begin(), wsFontFile.end()).c_str()); return (std::string)winDir + std::string(wsFontFile.begin(), wsFontFile.end()); #elif defined(ANDROID) #if __ANDROID_API__>28 // öж田た불 const unsigned char teststr[] = { 0xc3, 0xb6, 0xd0, 0xb6, 0xe7, 0x94, 0xb0, 0xe3, 0x81, 0x9f, 0xeb, 0xb6, 0x88, 0 }; AFontMatcher* AFM = AFontMatcher_create(); AFont* AF = AFontMatcher_match(AFM, "serif", teststr, lengthof(teststr), NULL); const char* fp = AFont_getFontFilePath(AF); AFont_close(AF); AFontMatcher_destroy(AFM); #else const char* addpath; searchfolder_t fonts; for (int i = 0; (addpath = dr_query_fontpath(i)); i++) { fonts.search(addpath, "*.ttf", searchfolder_t::SF_NOADDONS | searchfolder_t::SF_PREPEND_PATH, 4); for (const char* filename : fonts) { return filename; } } return "/system/fonts/DroidSans.ttf"; #endif #elif defined(USE_FONTCONFIG) std::string fontFile = FONT_PATH_X "cyr.bdf"; FcInit(); FcConfig* config = FcInitLoadConfigAndFonts(); FcPattern* pat = FcNameParse((const FcChar8*)"Sans"); FcConfigSubstitute(config, pat, FcMatchPattern); FcDefaultSubstitute(pat); FcResult result; FcPattern* font = FcFontMatch(config, pat, &result); if (font) { FcChar8* file = NULL; if ( FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch ) { fontFile = (char*)file; } } FcPatternDestroy(font); FcPatternDestroy(pat); FcConfigDestroy(config); FcFini(); return fontFile; #else return FONT_PATH_X "cyr.bdf"; #endif #else return ""; #endif } /* this retrieves the 2 byte string for the default language * I hope AmigaOS uses something like Linux for this ... */ #ifdef _WIN32 struct id2str_t { uint16 id; const char *name; }; const char *dr_get_locale_string() { static id2str_t id2str[] = { { 0x0001, "ar" }, { 0x0002, "bg" }, { 0x0003, "ca" }, { 0x0004, "zh\0Hans" }, { 0x0005, "cs" }, { 0x0006, "da" }, { 0x0007, "de" }, { 0x0008, "el" }, { 0x0009, "en" }, { 0x000a, "es" }, { 0x000b, "fi" }, { 0x000c, "fr" }, { 0x000d, "he" }, { 0x000e, "hu" }, { 0x000f, "is" }, { 0x0010, "it" }, { 0x0011, "ja" }, { 0x0012, "ko" }, { 0x0013, "nl" }, { 0x0014, "no" }, { 0x0015, "pl" }, { 0x0016, "pt" }, { 0x0017, "rm" }, { 0x0018, "ro" }, { 0x0019, "ru" }, { 0x001a, "hr" }, { 0x001b, "sk" }, { 0x001c, "sq" }, { 0x001d, "sv" }, { 0x001e, "th" }, { 0x001f, "tr" }, { 0x0020, "ur" }, { 0x0021, "id" }, { 0x0022, "uk" }, { 0x0023, "be" }, { 0x0024, "sl" }, { 0x0025, "et" }, { 0x0026, "lv" }, { 0x0027, "lt" }, { 0x0028, "tg" }, { 0x0029, "fa" }, { 0x002a, "vi" }, { 0x002b, "hy" }, { 0x002c, "az" }, { 0x002d, "eu" }, { 0x002e, "hsb" }, { 0x002f, "mk" }, { 0x0030, "st" }, { 0x0031, "ts" }, { 0x0032, "tn" }, { 0x0033, "ve" }, { 0x0034, "xh" }, { 0x0035, "zu" }, { 0x0036, "af" }, { 0x0037, "ka" }, { 0x0038, "fo" }, { 0x0039, "hi" }, { 0x003a, "mt" }, { 0x003b, "se" }, { 0x003c, "ga" }, { 0x003d, "yi" }, { 0x003e, "ms" }, { 0x003f, "kk" }, { 0x0040, "ky" }, { 0x0041, "sw" }, { 0x0042, "tk" }, { 0x0043, "uz" }, { 0x0044, "tt" }, { 0x0045, "bn" }, { 0x0046, "pa" }, { 0x0047, "gu" }, { 0x0048, "or" }, { 0x0049, "ta" }, { 0x004a, "te" }, { 0x004b, "kn" }, { 0x004c, "ml" }, { 0x004d, "as" }, { 0x004e, "mr" }, { 0x004f, "sa" }, { 0x0050, "mn" }, { 0x0051, "bo" }, { 0x0052, "cy" }, { 0x0053, "km" }, { 0x0054, "lo" }, { 0x0055, "my" }, { 0x0056, "gl" }, { 0x0057, "kok" }, { 0x0058, "mni" }, { 0x0059, "sd" }, { 0x005a, "syr" }, { 0x005b, "si" }, { 0x005c, "chr" }, { 0x005d, "iu" }, { 0x005e, "am" }, { 0x005f, "tzm" }, { 0x0060, "ks" }, { 0x0061, "ne" }, { 0x0062, "fy" }, { 0x0063, "ps" }, { 0x0064, "fil" }, { 0x0065, "dv" }, { 0x0066, "bin" }, { 0x0067, "ff" }, { 0x0068, "ha" }, { 0x0069, "ibb" }, { 0x006a, "yo" }, { 0x006b, "quz" }, { 0x006c, "nso" }, { 0x006d, "ba" }, { 0x006e, "lb" }, { 0x006f, "kl" }, { 0x0070, "ig" }, { 0x0071, "kr" }, { 0x0072, "om" }, { 0x0073, "ti" }, { 0x0074, "gn" }, { 0x0075, "haw" }, { 0x0076, "la" }, { 0x0077, "so" }, { 0x0078, "ii" }, { 0x0079, "pap" }, { 0x007a, "arn" }, { 0x007c, "moh" }, { 0x007e, "br" }, { 0x0080, "ug" }, { 0x0081, "mi" }, { 0x0082, "oc" }, { 0x0083, "co" }, { 0x0084, "gsw" }, { 0x0085, "sah" }, { 0x0086, "qut" }, { 0x0087, "rw" }, { 0x0088, "wo" }, { 0x008c, "prs" }, { 0x0091, "gd" }, { 0x0092, "ku" }, { 0x0093, "quc" }, { 0x0401, "ar\0SA" }, { 0x0402, "bg\0BG" }, { 0x0403, "ca\0ES" }, { 0x0404, "zh\0TW" }, { 0x0405, "cs\0CZ" }, { 0x0406, "da\0DK" }, { 0x0407, "de\0DE" }, { 0x0408, "el\0GR" }, { 0x0409, "en\0US" }, { 0x040a, "es\0ES_tradnl" }, { 0x040b, "fi\0FI" }, { 0x040c, "fr\0FR" }, { 0x040d, "he\0IL" }, { 0x040e, "hu\0HU" }, { 0x040f, "is\0IS" }, { 0x0410, "it\0IT" }, { 0x0411, "ja\0JP" }, { 0x0412, "ko\0KR" }, { 0x0413, "nl\0NL" }, { 0x0414, "nb\0NO" }, { 0x0415, "pl\0PL" }, { 0x0416, "pt\0BR" }, { 0x0417, "rm\0CH" }, { 0x0418, "ro\0RO" }, { 0x0419, "ru\0RU" }, { 0x041a, "hr\0HR" }, { 0x041b, "sk\0SK" }, { 0x041c, "sq\0AL" }, { 0x041d, "sv\0SE" }, { 0x041e, "th\0TH" }, { 0x041f, "tr\0TR" }, { 0x0420, "ur\0PK" }, { 0x0421, "id\0ID" }, { 0x0422, "uk\0UA" }, { 0x0423, "be\0BY" }, { 0x0424, "sl\0SI" }, { 0x0425, "et\0EE" }, { 0x0426, "lv\0LV" }, { 0x0427, "lt\0LT" }, { 0x0428, "tg\0Cyrl\0TJ" }, { 0x0429, "fa\0IR" }, { 0x042a, "vi\0VN" }, { 0x042b, "hy\0AM" }, { 0x042c, "az\0Latn\0AZ" }, { 0x042d, "eu\0ES" }, { 0x042e, "hsb\0DE" }, { 0x042f, "mk\0MK" }, { 0x0430, "st\0ZA" }, { 0x0431, "ts\0ZA" }, { 0x0432, "tn\0ZA" }, { 0x0433, "ve\0ZA" }, { 0x0434, "xh\0ZA" }, { 0x0435, "zu\0ZA" }, { 0x0436, "af\0ZA" }, { 0x0437, "ka\0GE" }, { 0x0438, "fo\0FO" }, { 0x0439, "hi\0IN" }, { 0x043a, "mt\0MT" }, { 0x043b, "se\0NO" }, { 0x043d, "yi\0Hebr" }, { 0x043e, "ms\0MY" }, { 0x043f, "kk\0KZ" }, { 0x0440, "ky\0KG" }, { 0x0441, "sw\0KE" }, { 0x0442, "tk\0TM" }, { 0x0443, "uz\0Latn\0UZ" }, { 0x0444, "tt\0RU" }, { 0x0445, "bn\0IN" }, { 0x0446, "pa\0IN" }, { 0x0447, "gu\0IN" }, { 0x0448, "or\0IN" }, { 0x0449, "ta\0IN" }, { 0x044a, "te\0IN" }, { 0x044b, "kn\0IN" }, { 0x044c, "ml\0IN" }, { 0x044d, "as\0IN" }, { 0x044e, "mr\0IN" }, { 0x044f, "sa\0IN" }, { 0x0450, "mn\0MN" }, { 0x0451, "bo\0CN" }, { 0x0452, "cy\0GB" }, { 0x0453, "km\0KH" }, { 0x0454, "lo\0LA" }, { 0x0455, "my\0MM" }, { 0x0456, "gl\0ES" }, { 0x0457, "kok\0IN" }, { 0x0458, "mni\0IN" }, { 0x0459, "sd\0Deva\0IN" }, { 0x045a, "syr\0SY" }, { 0x045b, "si\0LK" }, { 0x045c, "chr\0Cher\0US" }, { 0x045d, "iu\0Cans\0CA" }, { 0x045e, "am\0ET" }, { 0x045f, "tzm\0Arab\0MA" }, { 0x0460, "ks\0Arab" }, { 0x0461, "ne\0NP" }, { 0x0462, "fy\0NL" }, { 0x0463, "ps\0AF" }, { 0x0464, "fil\0PH" }, { 0x0465, "dv\0MV" }, { 0x0466, "bin\0NG" }, { 0x0467, "fuv\0NG" }, { 0x0468, "ha\0Latn\0NG" }, { 0x0469, "ibb\0NG" }, { 0x046a, "yo\0NG" }, { 0x046b, "quz\0BO" }, { 0x046c, "nso\0ZA" }, { 0x046d, "ba\0RU" }, { 0x046e, "lb\0LU" }, { 0x046f, "kl\0GL" }, { 0x0470, "ig\0NG" }, { 0x0471, "kr\0NG" }, { 0x0472, "om\0Ethi\0ET" }, { 0x0473, "ti\0ET" }, { 0x0474, "gn\0PY" }, { 0x0475, "haw\0US" }, { 0x0476, "la\0Latn" }, { 0x0477, "so\0SO" }, { 0x0478, "ii\0CN" }, { 0x0479, "pap\0029" }, { 0x047a, "arn\0CL" }, { 0x047c, "moh\0CA" }, { 0x047e, "br\0FR" }, { 0x0480, "ug\0CN" }, { 0x0481, "mi\0NZ" }, { 0x0482, "oc\0FR" }, { 0x0483, "co\0FR" }, { 0x0484, "gsw\0FR" }, { 0x0485, "sah\0RU" }, { 0x0486, "qut\0GT" }, { 0x0487, "rw\0RW" }, { 0x0488, "wo\0SN" }, { 0x048c, "prs\0AF" }, { 0x048d, "plt\0MG" }, { 0x048e, "zh\0yue\0HK" }, { 0x048f, "tdd\0Tale\0CN" }, { 0x0490, "khb\0Talu\0CN" }, { 0x0491, "gd\0GB" }, { 0x0492, "ku\0Arab\0IQ" }, { 0x0493, "quc\0CO" }, { 0x0501, "qps\0ploc" }, { 0x05fe, "qps\0ploca" }, { 0x0801, "ar\0IQ" }, { 0x0803, "ca\0ES\0valencia" }, { 0x0804, "zh\0CN" }, { 0x0807, "de\0CH" }, { 0x0809, "en\0GB" }, { 0x080a, "es\0MX" }, { 0x080c, "fr\0BE" }, { 0x0810, "it\0CH" }, { 0x0811, "ja\0Ploc\0JP" }, { 0x0813, "nl\0BE" }, { 0x0814, "nn\0NO" }, { 0x0816, "pt\0PT" }, { 0x0818, "ro\0MO" }, { 0x0819, "ru\0MO" }, { 0x081a, "sr\0Latn\0CS" }, { 0x081d, "sv\0FI" }, { 0x0820, "ur\0IN" }, { 0x082c, "az\0Cyrl\0AZ" }, { 0x082e, "dsb\0DE" }, { 0x0832, "tn\0BW" }, { 0x083b, "se\0SE" }, { 0x083c, "ga\0IE" }, { 0x083e, "ms\0BN" }, { 0x0843, "uz\0Cyrl\0UZ" }, { 0x0845, "bn\0BD" }, { 0x0846, "pa\0Arab\0PK" }, { 0x0849, "ta\0LK" }, { 0x0850, "mn\0Mong\0CN" }, { 0x0851, "bo\0BT" }, { 0x0859, "sd\0Arab\0PK" }, { 0x085d, "iu\0Latn\0CA" }, { 0x085f, "tzm\0Latn\0DZ" }, { 0x0860, "ks\0Deva" }, { 0x0861, "ne\0IN" }, { 0x0867, "ff\0Latn\0SN" }, { 0x086b, "quz\0EC" }, { 0x0873, "ti\0ER" }, { 0x09ff, "qps\0plocm" }, { 0x0c01, "ar\0EG" }, { 0x0c04, "zh\0HK" }, { 0x0c07, "de\0AT" }, { 0x0c09, "en\0AU" }, { 0x0c0a, "es\0ES" }, { 0x0c0c, "fr\0CA" }, { 0x0c1a, "sr\0Cyrl\0CS" }, { 0x0c3b, "se\0FI" }, { 0x0c5f, "tmz\0MA" }, { 0x0c6b, "quz\0PE" }, { 0x1001, "ar\0LY" }, { 0x1004, "zh\0SG" }, { 0x1007, "de\0LU" }, { 0x1009, "en\0CA" }, { 0x100a, "es\0GT" }, { 0x100c, "fr\0CH" }, { 0x101a, "hr\0BA" }, { 0x103b, "smj\0NO" }, { 0x1401, "ar\0DZ" }, { 0x1404, "zh\0MO" }, { 0x1407, "de\0LI" }, { 0x1409, "en\0NZ" }, { 0x140a, "es\0CR" }, { 0x140c, "fr\0LU" }, { 0x141a, "bs\0Latn\0BA" }, { 0x143b, "smj\0SE" }, { 0x1801, "ar\0MA" }, { 0x1809, "en\0IE" }, { 0x180a, "es\0PA" }, { 0x180c, "fr\0MC" }, { 0x181a, "sr\0Latn\0BA" }, { 0x183b, "sma\0NO" }, { 0x1c01, "ar\0TN" }, { 0x1c09, "en\0ZA" }, { 0x1c0a, "es\0DO" }, { 0x1c1a, "sr\0Cyrl\0BA" }, { 0x1c3b, "sma\0SE" }, { 0x2001, "ar\0OM" }, { 0x2009, "en\0JM" }, { 0x200a, "es\0VE" }, { 0x200c, "fr\0RE" }, { 0x201a, "bs\0Cyrl\0BA" }, { 0x203b, "sms\0FI" }, { 0x2401, "ar\0YE" }, { 0x2409, "en\0029" }, { 0x240a, "es\0CO" }, { 0x240c, "fr\0CG" }, { 0x241a, "sr\0Latn\0RS" }, { 0x243b, "smn\0FI" }, { 0x2801, "ar\0SY" }, { 0x2809, "en\0BZ" }, { 0x280a, "es\0PE" }, { 0x280c, "fr\0SN" }, { 0x281a, "sr\0Cyrl\0RS" }, { 0x2c01, "ar\0JO" }, { 0x2c09, "en\0TT" }, { 0x2c0a, "es\0AR" }, { 0x2c0c, "fr\0CM" }, { 0x2c1a, "sr\0Latn\0ME" }, { 0x3001, "ar\0LB" }, { 0x3009, "en\0ZW" }, { 0x300a, "es\0EC" }, { 0x300c, "fr\0CI" }, { 0x301a, "sr\0Cyrl\0ME" }, { 0x3401, "ar\0KW" }, { 0x3409, "en\0PH" }, { 0x340a, "es\0CL" }, { 0x340c, "fr\0ML" }, { 0x3801, "ar\0AE" }, { 0x3809, "en\0ID" }, { 0x380a, "es\0UY" }, { 0x380c, "fr\0MA" }, { 0x3c01, "ar\0BH" }, { 0x3c09, "en\0HK" }, { 0x3c0a, "es\0PY" }, { 0x3c0c, "fr\0HT" }, { 0x4001, "ar\0QA" }, { 0x4009, "en\0IN" }, { 0x400a, "es\0BO" }, { 0x4401, "ar\0Ploc\0SA" }, { 0x4409, "en\0MY" }, { 0x440a, "es\0SV" }, { 0x4801, "ar\0145" }, { 0x4809, "en\0SG" }, { 0x480a, "es\0HN" }, { 0x4c09, "en\0AE" }, { 0x4c0a, "es\0NI" }, { 0x5009, "en\0BH" }, { 0x500a, "es\0PR" }, { 0x5409, "en\0EG" }, { 0x540a, "es\0US" }, { 0x5809, "en\0JO" }, { 0x5c09, "en\0KW" }, { 0x6009, "en\0TR" }, { 0x6409, "en\0YE" }, { 0x641a, "bs\0Cyrl" }, { 0x681a, "bs\0Latn" }, { 0x6c1a, "sr\0Cyrl" }, { 0x701a, "sr\0Latn" }, { 0x703b, "smn" }, { 0x742c, "az\0Cyrl" }, { 0x743b, "sms" }, { 0x7804, "zh" }, { 0x7814, "nn" }, { 0x781a, "bs" }, { 0x782c, "az\0Latn" }, { 0x783b, "sma" }, { 0x7843, "uz\0Cyrl" }, { 0x7850, "mn\0Cyrl" }, { 0x785d, "iu\0Cans" }, { 0x7c04, "zh\0Hant" }, { 0x7c14, "nb" }, { 0x7c1a, "sr" }, { 0x7c28, "tg\0Cyrl" }, { 0x7c2e, "dsb" }, { 0x7c3b, "smj" }, { 0x7c43, "uz\0Latn" }, { 0x7c46, "pa\0Arab" }, { 0x7c50, "mn\0Mong" }, { 0x7c59, "sd\0Arab" }, { 0x7c5c, "chr\0Cher" }, { 0x7c5d, "iu\0Latn" }, { 0x7c5f, "tzm\0Latn" }, { 0x7c67, "ff\0Latn" }, { 0x7c68, "ha\0Latn" }, { 0x7c92, "ku\0Arab" } }; const uint16 current_id = (0x000000FFFFul & GetThreadLocale()); for( size_t i=0; i #include const char *dr_get_locale_string() { static char code[4]; BMessage result; const char *str; code[0] = 0; if( B_OK == BLocaleRoster::Default()->GetPreferredLanguages( &result ) ) { result.FindString( (const char *)"language", &str ); for( int i=0; i const char *dr_get_locale_string() { static char code[4]; char *ptr; setlocale( LC_ALL, "" ); ptr = setlocale( LC_ALL, NULL ); code[0] = 0; for( int i=0; i<(int)lengthof(code)-1 && isalpha(ptr[i]); i++ ) { code[i] = tolower(ptr[i]); code[i+1] = 0; } setlocale( LC_ALL, "C" ); // or the number output may be broken return code[0] ? code : NULL; } #endif void dr_fatal_notify(char const* const msg) { fprintf(stderr, "dr_fatal_notify: ERROR: %s\n", msg); #ifdef _WIN32 MessageBoxA(0, msg, "Fatal Error", MB_ICONEXCLAMATION); #endif #ifdef __ANDROID__ dbg->error(__FUNCTION__, msg); #endif } /* * Open a program/starts a script to download pak sets from sourceforge * * @param data_dir : actual simutrans pakfile directory * @param portable * @return false, if nothing was downloaded */ bool dr_download_pakset( const char *data_dir, bool portable ) { #ifdef _WIN32 int old_fullscreen = dr_suspend_fullscreen(); U16View const wpath_to_program(data_dir); WCHAR wparam[1024]; swprintf(wparam, portable ? L"/P /D=%ls" : L"/D=%ls", wpath_to_program); SHELLEXECUTEINFOW shExInfo; shExInfo.cbSize = sizeof(shExInfo); shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS; shExInfo.hwnd = 0; /* since the installer will run again a child process * using "runas" will make sure it is already privileged. * Otherwise the waiting for installation will fail! * (no idea how this works on XP though) */ // shExInfo.lpVerb = L"runas"; we do not need afmin rights any more shExInfo.lpVerb = L"open"; shExInfo.lpFile = L"download-paksets.exe"; shExInfo.lpParameters = wparam; shExInfo.lpDirectory = wpath_to_program; shExInfo.nShow = SW_SHOW; shExInfo.hInstApp = 0; if( ShellExecuteExW(&shExInfo) ) { WaitForSingleObject( shExInfo.hProcess, INFINITE ); CloseHandle( shExInfo.hProcess ); } dr_restore_fullscreen(old_fullscreen); return true; #else (void)portable; char command[2048]; dr_chdir( data_dir ); sprintf(command, "%s/get_pak.sh", data_dir); const int retval = system( command ); dbg->debug("dr_download_pakset", "Command '%s' returned %d", command, retval); return true; #endif } const char* get_version() { return "Version " VERSION_NUMBER " " VERSION_DATE #ifdef REVISION " r" QUOTEME(REVISION) #endif #ifdef GIT_HASH " hash " QUOTEME(GIT_HASH) #endif L_DEBUG_TEXT; } /** * Copy argv because some systems (e.g. Android) do not allow to write them */ char **copy_argv(int argc, char *argv[]) { char** new_argv = new char*[argc+1]; for(int i=0; i < argc; i++) { int len = strlen(argv[i]) + 1; new_argv[i] = new char[len]; strcpy(new_argv[i], argv[i]); } new_argv[argc] = NULL; return new_argv; } int sysmain(int const argc, char** const argv) { char ** argv_copy = copy_argv(argc,argv); sys_event.type = SIM_NOEVENT; sys_event.code = 0; #ifdef _WIN32 // Guess required buffer length, if too small then dynamically expand up to around 65536 characters. // It is unlikely this loop will ever run more than once in practice but is required to cover flaws with the API. size_t buffersize = MAX_PATH; WCHAR *wpathname; while(true) { wpathname = new WCHAR[buffersize]; DWORD result = GetModuleFileNameW(GetModuleHandleW(0), wpathname, buffersize); if(result < buffersize || buffersize >= 1 << 16) { break; } delete[] wpathname; buffersize<<= 1; } // Convert UTF-16 to UTF-8 int const pathnamesize = WideCharToMultiByte(CP_UTF8, 0, wpathname, -1, NULL, 0, NULL, NULL); char *const pathname = new char[pathnamesize]; WideCharToMultiByte(CP_UTF8, 0, wpathname, -1, pathname, pathnamesize, NULL, NULL); delete[] wpathname; argv_copy[0] = pathname; #elif !defined __BEOS__ # if defined __GLIBC__ && !defined __AMIGA__ /* glibc has a non-standard extension */ char* buffer2 = 0; # else char buffer2[PATH_MAX]; # endif # ifndef __AMIGA__ char buffer[PATH_MAX]; ssize_t const length = readlink("/proc/self/exe", buffer, lengthof(buffer) - 1); if (length != -1) { buffer[length] = '\0'; /* readlink() does not NUL-terminate */ argv_copy[0] = buffer; } else if (strchr(argv_copy[0], '/') == NULL) { // no /proc, no '/' in argv[0] => search PATH const char* path = getenv("PATH"); if (path != NULL) { size_t flen = strlen(argv_copy[0]); for (;;) { /* for each directory in PATH */ size_t dlen = strcspn(path, ":"); if (dlen > 0 && dlen + flen + 2 < lengthof(buffer)) { // buffer = dir '/' argv[0] '\0' memcpy(buffer, path, dlen); buffer[dlen] = '/'; memcpy(buffer + dlen + 1, argv_copy[0], flen + 1); if (access(buffer, X_OK) == 0) { argv_copy[0] = buffer; break; /* found it! */ } } if (path[dlen] == '\0') break; path += dlen + 1; /* skip ':' */ } } } # endif // no process file system => need to parse argv[0] /* should work on most unix or gnu systems */ argv_copy[0] = realpath(argv_copy[0], buffer2); #endif setlocale( LC_CTYPE, "" ); setlocale( LC_NUMERIC, "en_US.UTF-8" ); return simu_main(argc, argv_copy); #ifdef _WIN32 // Cleanup for dynamic allocation, probably unnescescary. delete[] pathname; #endif } simutrans-124.5/src/simutrans/sys/simsys.h000066400000000000000000000173211520675460500207220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef SYS_SIMSYS_H #define SYS_SIMSYS_H #include "../simtypes.h" #include "../display/scr_coord.h" #include "../simcolor.h" #ifndef NETTOOL #include #endif #include // Provide chdir(). #if defined(_WIN32) && !defined(__CYGWIN__) # include #else # include #endif enum sim_event_type_t : uint32 { SIM_NOEVENT = 0, SIM_MOUSE_BUTTONS = 1, SIM_KEYBOARD = 2, SIM_MOUSE_MOVE = 3, SIM_STRING = 4, SIM_SYSTEM = 254, SIM_IGNORE_EVENT = 255 }; /// Mouse actions enum sim_mouse_action_t : unsigned long { SIM_MOUSE_LEFTUP = 1, SIM_MOUSE_RIGHTUP = 2, SIM_MOUSE_MIDUP = 3, SIM_MOUSE_LEFTBUTTON = 4, SIM_MOUSE_RIGHTBUTTON = 5, SIM_MOUSE_MIDBUTTON = 6, SIM_MOUSE_MOVED = 7, SIM_MOUSE_WHEELUP = 8, SIM_MOUSE_WHEELDOWN = 9 }; struct sys_event_t { sim_event_type_t type; union { unsigned long code; void *ptr; }; // mouse position; negative coords are possible scr_coord_val mx; scr_coord_val my; uint16 mb; // Combination of MOUSE_* flags from simevent.h /// new window size for SYSTEM_RESIZE uint16 new_window_size_w; uint16 new_window_size_h; /// pressed modifiers, combination of SIM_MOD_* flags from simevent.h unsigned int key_mod; }; enum { WINDOWED, FULLSCREEN, BORDERLESS }; /// Global Variable for message processing extern sys_event_t sys_event; extern char const PATH_SEPARATOR[]; /// @param scale_percent /// Possible values: /// -1: auto (scale according to screen DPI setting) /// 0: off (default 1:1 scale) /// other: specific scaling, in percent /// < -1: invalid bool dr_set_screen_scale(sint16 scale_percent); /// @returns Relative size of the virtual display, in percent sint16 dr_get_screen_scale(); bool dr_os_init(int const* parameter); /* maximum size possible (if there) */ struct resolution { scr_coord_val w; scr_coord_val h; }; resolution dr_query_screen_resolution(); int dr_os_open(scr_size window_size, sint16 fullscreen); void dr_os_close(); // returns the locale; NULL if unknown const char *dr_get_locale_string(); // Functions the same as normal mkdir except path must be UTF-8 encoded and a default mode of 0777 is assumed. int dr_mkdir(char const* path); /** * Moves the specified file to the system's trash bin. * If trash is not available on the platform, removes file. * @param path UTF-8 path to the file to delete. * @return False on success. */ bool dr_movetotrash(const char *path); /** * Returns true if platform supports recycle bin, otherwise false. * Used to control which UI tooltip is shown for deletion. */ bool dr_cantrash(); // Functions the same as cstdio remove except path must be UTF-8 encoded. int dr_remove(const char *path); // rename a file and delete eventually existing file new_utf8 int dr_rename( const char *existing_utf8, const char *new_utf8 ); // Functions the same as chdir except path must be UTF-8 encoded. int dr_chdir(const char *path); // Functions the same as getcwd except path must be UTF-8 encoded. char *dr_getcwd(char *buf, size_t size); /// Opens a regular file specified by @p filename. The filename must be UTF-8 encoded. /// Returns NULL on failure (e.g. the path is a directory, or does not exist). /// @param mode How to open the file (reading, writing). Format is the same as for @ref fopen. FILE *dr_fopen(const char *filename, const char *mode); #ifndef NETTOOL /// Opens a regular compressed file specified by @p filename. The filename must be UTF-8 encoded. /// Returns NULL on failure (e.g. the path is a directory, or does not exist). /// @param mode How to open the file (reading, writing). Format is the same as for @ref gzopen. gzFile dr_gzopen(const char *path, const char *mode); #endif /// Functions the same as @ref stat except @p path must be UTF-8 encoded. int dr_stat(const char *path, struct stat *buf); /** * Check if the directory exists and if so set the result variable to it * If the directory doesn't exist previously, it will attempt to create it if testfile is not provided * @param path : Path to directory to check * @param info : String to provide extra log info * @param result : Variable to store the path to directory if it exists * @param testfile : Optional file to check for existence */ bool check_and_set_dir(const char *path, const char *info, char *result, const char *testfile=NULL); /** * Sets the base_dir * @param base_dir_arg : command line argument "-set_basedir" * @param executable_path : Path to executable (as stored in argv[0]) */ bool dr_set_basedir(const char *base_dir_arg, char *executable_path); /* query home directory */ char const* dr_query_homedir(); /* query install directory (all user writable) */ char const* dr_query_installdir(); unsigned short* dr_textur_init(); // returns the file path to a font file (or more than one, if used with number higher than zero) const char *dr_query_fontpath( int ); // return a default TTF (windows/android) or a BDF for now std::string dr_get_system_font(); void dr_textur(int xp, int yp, int w, int h); /* returns the actual width (might be larger than requested! */ int dr_textur_resize(unsigned short** textur, int w, int h); // needed for screen update void dr_prepare_flush(); // waits, if previous update not yet finished void dr_flush(); // copy to screen (eventually multithreaded) /** * Transform a 24 bit RGB color into the system format. * @return converted color value */ PIXVAL get_system_color(rgb888_t rgb); void show_pointer(int yesno); void set_pointer(int loading); bool move_pointer(int x, int y); scr_coord get_mouse_pos(); void ex_ord_update_mx_my(); void GetEvents(); uint8 dr_get_max_threads(); uint32 dr_time(); void dr_sleep(uint32 millisec); // error message in case of fatal events void dr_fatal_notify(char const* msg); /** * Copy text to the clipboard * @param source : pointer to the start of the source text * @param length : number of character bytes to copy */ void dr_copy(const char *source, size_t length); /** * Paste text from the clipboard * @param target : pointer to the insertion position in the target text * @param max_length : maximum number of character bytes which could be inserted * @return number of character bytes actually inserted -> for cursor advancing */ size_t dr_paste(char *target, size_t max_length); /** * Open a program/starts a script to download pak sets. * @param data_dir : The current simutrans data directory (usually the same as env_t::base_dir) * @param portable : true if local files to be save in simutransdir * @return false, if nothing was downloaded */ bool dr_download_pakset( const char *data_dir, bool portable ); /** * Shows the touch keyboard when using systems without a hardware keyboard. * Will be ignored if there is an hardware keyboard available. */ void dr_start_textinput(); /** * Hides the touch keyboard when using systems without a hardware keyboard. * Will be ignored it there is no on-display keyboard shown. */ void dr_stop_textinput(); /** * Inform the IME of a ideal place to open its popup. */ void dr_notify_input_pos(scr_coord pos); /// returns current two byte languange ID const char* dr_get_locale(); /// true, if there is a hardware fullcreen mode bool dr_has_fullscreen(); /** * @return * 0: if windowed * 1: if fullscreen * 2: if borderless fullscreen */ sint16 dr_get_fullscreen(); /** * Toggle between borderless and windowed mode * @return the fullscreen state after the toggle */ sint16 dr_toggle_borderless(); /* temparily minimizes window and restore it */ sint16 dr_suspend_fullscreen(); void dr_restore_fullscreen(sint16 old_fullscreen); const char* get_version(); int sysmain(int argc, char** argv); #endif simutrans-124.5/src/simutrans/sys/simsys_posix.cc000066400000000000000000000053701520675460500223030ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifdef _WIN32 #include #endif #ifndef _MSC_VER #include #include #else // need timeGetTime #include #endif #include #include "simsys.h" #include "../macros.h" #include "../simdebug.h" #include "../simevent.h" static bool sigterm_received = false; #if COLOUR_DEPTH != 0 #error "Posix only compiles with color depth=0" #endif bool dr_set_screen_scale(sint16) { // no autoscaling as we have no display ... return false; } sint16 dr_get_screen_scale() { return 100; } bool dr_os_init(const int*) { // prepare for next event sys_event.type = SIM_NOEVENT; sys_event.code = 0; return true; } resolution dr_query_screen_resolution() { resolution const res = { 0, 0 }; return res; } // open the window int dr_os_open(int, int, sint16) { return 1; } void dr_os_close() { } // resizes screen int dr_textur_resize(unsigned short** const textur, int, int) { *textur = NULL; return 1; } unsigned short *dr_textur_init() { return NULL; } PIXVAL get_system_color(rgb888_t) { return 1; } void dr_prepare_flush() { } void dr_flush() { } void dr_textur(int, int, int, int) { } bool move_pointer(int, int) { return false; } void set_pointer(int) { } void GetEvents() { if( sigterm_received ) { sys_event.type = SIM_SYSTEM; sys_event.code = SYSTEM_QUIT; } } void show_pointer(int) { } void ex_ord_update_mx_my() { } #ifndef _MSC_VER static timeval first; #endif uint32 dr_time() { #ifndef _MSC_VER timeval second; gettimeofday(&second,NULL); if (first.tv_usec > second.tv_usec) { // since those are often unsigned second.tv_usec += 1000000; second.tv_sec--; } return (second.tv_sec - first.tv_sec)*1000ul + (second.tv_usec - first.tv_usec)/1000ul; #else return timeGetTime(); #endif } void dr_sleep(uint32 msec) { /* // this would be 100% POSIX but is usually not very accurate ... if( msec>0 ) { struct timeval tv; tv.sec = 0; tv.usec = msec*1000; select(0, 0, 0, 0, &tv); } */ #ifdef _WIN32 Sleep( msec ); #else usleep( 1000u * msec ); #endif } void dr_start_textinput() { } void dr_stop_textinput() { } void dr_notify_input_pos(scr_coord pos) { } static void posix_sigterm(int) { DBG_MESSAGE("Received SIGTERM", "exiting..."); sigterm_received = 1; } const char* dr_get_locale() { return ""; } bool dr_has_fullscreen() { return false; } sint16 dr_get_fullscreen() { return 0; } sint16 dr_toggle_borderless() { return 0; } sint16 dr_suspend_fullscreen() { return 0; } void dr_restore_fullscreen(sint16) {} int main(int argc, char **argv) { signal( SIGTERM, posix_sigterm ); #ifndef _MSC_VER gettimeofday(&first,NULL); #endif return sysmain(argc, argv); } simutrans-124.5/src/simutrans/sys/simsys_s2.cc000066400000000000000000001113101520675460500214550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #if !defined __APPLE__ && !defined __ANDROID__ #include #else #include #endif #ifdef _WIN32 #include #endif #include #include #ifdef __CYGWIN__ extern int __argc; extern char **__argv; #endif #include "simsys.h" #include "../macros.h" #include "../simversion.h" #include "../simevent.h" #include "../display/simgraph.h" #include "../simdebug.h" #include "../dataobj/environment.h" #include "../gui/simwin.h" #include "../gui/components/gui_component.h" #include "../gui/components/gui_textinput.h" #include "../simintr.h" #include "../world/simworld.h" #include "../music/music.h" #include "../utils/unicode.h" // Maybe Linux is not fine too, had critical bugs... #if !defined(__linux__) #define USE_SDL_TEXTEDITING #else #endif // threshold for zooming in/out with multitouch #define DELTA_PINCH (0.033) static Uint8 hourglass_cursor[] = { 0x3F, 0xFE, // ************* 0x30, 0x06, // ** ** 0x3F, 0xFE, // ************* 0x10, 0x04, // * * 0x10, 0x04, // * * 0x12, 0xA4, // * * * * * 0x11, 0x44, // * * * * * 0x18, 0x8C, // ** * ** 0x0C, 0x18, // ** ** 0x06, 0xB0, // ** * ** 0x03, 0x60, // ** ** 0x03, 0x60, // **H** 0x06, 0x30, // ** * ** 0x0C, 0x98, // ** ** 0x18, 0x0C, // ** * ** 0x10, 0x84, // * * * 0x11, 0x44, // * * * * 0x12, 0xA4, // * * * * * 0x15, 0x54, // * * * * * * 0x3F, 0xFE, // ************* 0x30, 0x06, // ** ** 0x3F, 0xFE // ************* }; static Uint8 hourglass_cursor_mask[] = { 0x3F, 0xFE, // ************* 0x3F, 0xFE, // ************* 0x3F, 0xFE, // ************* 0x1F, 0xFC, // *********** 0x1F, 0xFC, // *********** 0x1F, 0xFC, // *********** 0x1F, 0xFC, // *********** 0x1F, 0xFC, // *********** 0x0F, 0xF8, // ********* 0x07, 0xF0, // ******* 0x03, 0xE0, // ***** 0x03, 0xE0, // **H** 0x07, 0xF0, // ******* 0x0F, 0xF8, // ********* 0x1F, 0xFC, // *********** 0x1F, 0xFC, // *********** 0x1F, 0xFC, // *********** 0x1F, 0xFC, // *********** 0x1F, 0xFC, // *********** 0x3F, 0xFE, // ************* 0x3F, 0xFE, // ************* 0x3F, 0xFE // ************* }; static Uint8 blank_cursor[] = { 0x0, 0x0, }; static SDL_Window *window; static SDL_Renderer *renderer; static SDL_Texture *screen_tx; static SDL_Surface *screen; static int sync_blit = 0; static int use_dirty_tiles = 1; static sint16 fullscreen = WINDOWED; static SDL_Cursor *arrow; static SDL_Cursor *hourglass; static SDL_Cursor *blank; // Number of fractional bits for screen scaling #define SCALE_SHIFT_X 5 #define SCALE_SHIFT_Y 5 #define SCALE_NEUTRAL_X (1 << SCALE_SHIFT_X) #define SCALE_NEUTRAL_Y (1 << SCALE_SHIFT_Y) // Multiplier when converting from texture to screen coords, fixed point format // Example: If x_scale==2*SCALE_NEUTRAL_X && y_scale==2*SCALE_NEUTRAL_Y, // then things on screen are 2*2 = 4 times as big by area sint32 x_scale = SCALE_NEUTRAL_X; sint32 y_scale = SCALE_NEUTRAL_Y; // When using -autodpi, attempt to scale things on screen to this DPI value #ifdef __ANDROID__ #define TARGET_DPI (192) #else #define TARGET_DPI (96) #endif // make sure we have at least so much pixel in y-direction #define MIN_SCALE_HEIGHT (640) // Most Android devices are underpowered to handle larger screens #define MAX_AUTOSCALE_WIDTH (1280) // screen -> texture coords #define SCREEN_TO_TEX_X(x) (((x) * SCALE_NEUTRAL_X) / x_scale) #define SCREEN_TO_TEX_Y(y) (((y) * SCALE_NEUTRAL_Y) / y_scale) // texture -> screen coords #define TEX_TO_SCREEN_X(x) (((x) * x_scale) / SCALE_NEUTRAL_X) #define TEX_TO_SCREEN_Y(y) (((y) * y_scale) / SCALE_NEUTRAL_Y) bool has_soft_keyboard = false; bool dr_set_screen_scale(sint16 scale_percent) { const sint32 old_x_scale = x_scale; const sint32 old_y_scale = y_scale; if (scale_percent == -1) { float hdpi, vdpi; SDL_DisplayMode mode; SDL_GetCurrentDisplayMode(0, &mode); DBG_MESSAGE("dr_auto_scale", "screen resolution width=%d, height=%d", mode.w, mode.h); #if SDL_VERSION_ATLEAST(2,0,4) // auto scale only for high enough screens if (mode.h > 1.5 * MIN_SCALE_HEIGHT && SDL_GetDisplayDPI(0, NULL, &hdpi, &vdpi) == 0) { x_scale = ((sint64)hdpi * SCALE_NEUTRAL_X + 1) / TARGET_DPI; y_scale = ((sint64)vdpi * SCALE_NEUTRAL_Y + 1) / TARGET_DPI; DBG_MESSAGE("auto_dpi_scaling", "x=%i, y=%i", x_scale, y_scale); } #ifdef __ANDROID__ // most Android are underpowered to run more than 1280 pixel horizontal sint32 current_x = SCREEN_TO_TEX_X(mode.w); if (current_x > MAX_AUTOSCALE_WIDTH) { sint32 new_x_scale = ((sint64)mode.w * SCALE_NEUTRAL_X + 1) / MAX_AUTOSCALE_WIDTH; y_scale = (y_scale * new_x_scale) / x_scale; x_scale = new_x_scale; DBG_MESSAGE("new scaling (max 1280)", "x=%i, y=%i", x_scale, y_scale); } #endif // ensure minimum height sint32 current_y = SCREEN_TO_TEX_Y(mode.h); if (current_y < MIN_SCALE_HEIGHT) { DBG_MESSAGE("dr_auto_scale", "virtual height=%d < %d", current_y, MIN_SCALE_HEIGHT); x_scale = (x_scale * current_y) / MIN_SCALE_HEIGHT; y_scale = (y_scale * current_y) / MIN_SCALE_HEIGHT; DBG_MESSAGE("new scaling (min 640)", "x=%i, y=%i", x_scale, y_scale); } #else #pragma message "SDL version must be at least 2.0.4 to support autoscaling." // 1.5 scale up by default x_scale = (150*SCALE_NEUTRAL_X)/100; y_scale = (150*SCALE_NEUTRAL_Y)/100; #endif } else if (scale_percent == 0) { x_scale = SCALE_NEUTRAL_X; y_scale = SCALE_NEUTRAL_Y; } else { x_scale = (scale_percent*SCALE_NEUTRAL_X)/100; y_scale = (scale_percent*SCALE_NEUTRAL_Y)/100; } if (window && (x_scale != old_x_scale || y_scale != old_y_scale) ) { // force window resize int w, h; SDL_GetWindowSize(window, &w, &h); SDL_Event ev; ev.type = SDL_WINDOWEVENT; ev.window.event = SDL_WINDOWEVENT_SIZE_CHANGED; ev.window.data1 = w; ev.window.data2 = h; if (SDL_PushEvent(&ev) != 1) { return false; } } return true; } sint16 dr_get_screen_scale() { return (x_scale*100)/SCALE_NEUTRAL_X; } static int SDLCALL my_event_filter(void* /*userdata*/, SDL_Event* event) { DBG_DEBUG4("my_event_filter", "%i", event->type); switch (event->type) { case SDL_APP_DIDENTERBACKGROUND: intr_disable(); // save settings { dr_chdir(env_t::user_dir); loadsave_t settings_file; if (settings_file.wr_open("settings.xml", loadsave_t::xml, 0, "settings only/", SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK) { env_t::rdwr(&settings_file); env_t::default_settings.rdwr(&settings_file); settings_file.close(); } } dr_stop_midi(); return 0; case SDL_APP_TERMINATING: // quitting immediate, save settings and game without visual feedback intr_disable(); DBG_DEBUG("SDL_APP_TERMINATING", "env_t::reload_and_save_on_quit=%d", env_t::reload_and_save_on_quit); world()->stop(true); // save settings { dr_chdir(env_t::user_dir); loadsave_t settings_file; if (settings_file.wr_open("settings.xml", loadsave_t::xml, 0, "settings only/", SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK) { env_t::rdwr(&settings_file); env_t::default_settings.rdwr(&settings_file); settings_file.close(); } } // at this point there is no UI active anymore, and we have no time to die, so just exit and leeve the cleanup to the OS dr_stop_midi(); SDL_Quit(); dr_os_close(); exit(0); // we never reach here tough ... return 0; } return 1; // let all events be added to the queue since we always return 1. } /* * Hier sind die Basisfunktionen zur Initialisierung der * Schnittstelle untergebracht * -> init,open,close */ bool dr_os_init(const int* parameter) { if( SDL_Init( SDL_INIT_VIDEO ) != 0 ) { dbg->error("dr_os_init(SDL2)", "Could not initialize SDL: %s", SDL_GetError() ); return false; } SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight Portrait PortraitUpsideDown"); dbg->message("dr_os_init(SDL2)", "SDL Driver: %s", SDL_GetCurrentVideoDriver() ); // disable event types not interested in #ifndef USE_SDL_TEXTEDITING SDL_EventState( SDL_TEXTEDITING, SDL_DISABLE ); #endif SDL_EventState( SDL_FINGERDOWN, SDL_ENABLE ); SDL_EventState( SDL_FINGERUP, SDL_ENABLE ); SDL_EventState( SDL_FINGERMOTION, SDL_ENABLE ); SDL_EventState( SDL_DOLLARGESTURE, SDL_DISABLE ); SDL_EventState( SDL_DOLLARRECORD, SDL_DISABLE ); SDL_EventState( SDL_MULTIGESTURE, SDL_ENABLE ); SDL_EventState( SDL_CLIPBOARDUPDATE, SDL_DISABLE ); SDL_EventState( SDL_DROPFILE, SDL_DISABLE ); // SDL_SetHint(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1"); // termination event: save current map and settings SDL_SetEventFilter(my_event_filter, 0); has_soft_keyboard = SDL_HasScreenKeyboardSupport(); if (has_soft_keyboard && !env_t::hide_keyboard) { env_t::hide_keyboard = true; } if (!env_t::hide_keyboard) { SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE); } sync_blit = parameter[0]; // hijack SDL1 -async flag for SDL2 vsync use_dirty_tiles = !parameter[1]; // hijack SDL1 -use_hw flag to turn off dirty tile updates (force fullscreen updates) // prepare for next event sys_event.type = SIM_NOEVENT; sys_event.code = 0; if (!env_t::hide_keyboard) { SDL_StartTextInput(); DBG_MESSAGE("SDL_StartTextInput", ""); } atexit( SDL_Quit ); // clean up on exit return true; } resolution dr_query_screen_resolution() { resolution res; SDL_DisplayMode mode; SDL_GetCurrentDisplayMode( 0, &mode ); DBG_MESSAGE("dr_query_screen_resolution(SDL2)", "screen resolution width=%d, height=%d", mode.w, mode.h ); res.w = SCREEN_TO_TEX_X(mode.w); res.h = SCREEN_TO_TEX_Y(mode.h); return res; } bool internal_create_surfaces(int tex_width, int tex_height) { // The pixel format needs to match the graphics code within simgraph16.cc. // Note that alpha is handled by simgraph16, not by SDL. const Uint32 pixel_format = SDL_PIXELFORMAT_RGB565; #ifdef MSG_LEVEL // List all render drivers and their supported pixel formats. const int num_rend = SDL_GetNumRenderDrivers(); std::string formatStrBuilder; for( int i = 0; i < num_rend; i++ ) { SDL_RendererInfo ri; SDL_GetRenderDriverInfo( i, &ri ); formatStrBuilder.clear(); for( Uint32 j = 0; j < ri.num_texture_formats; j++ ) { formatStrBuilder += ", "; formatStrBuilder += SDL_GetPixelFormatName(ri.texture_formats[j]); } DBG_DEBUG( "internal_create_surfaces(SDL2)", "Renderer: %s, Max_w: %d, Max_h: %d, Flags: %d, Formats: %d%s", ri.name, ri.max_texture_width, ri.max_texture_height, ri.flags, ri.num_texture_formats, formatStrBuilder.c_str() ); } #endif Uint32 flags = SDL_RENDERER_ACCELERATED; if( sync_blit ) { flags |= SDL_RENDERER_PRESENTVSYNC; } renderer = SDL_CreateRenderer( window, -1, flags ); if( renderer == NULL ) { dbg->warning( "internal_create_surfaces(SDL2)", "Couldn't create accelerated renderer: %s", SDL_GetError() ); flags &= ~SDL_RENDERER_ACCELERATED; flags |= SDL_RENDERER_SOFTWARE; renderer = SDL_CreateRenderer( window, -1, flags ); if( renderer == NULL ) { dbg->error( "internal_create_surfaces(SDL2)", "No suitable SDL2 renderer found!" ); return false; } dbg->warning( "internal_create_surfaces(SDL2)", "Using fallback software renderer instead of accelerated: Performance may be low!"); } SDL_RendererInfo ri; SDL_GetRendererInfo( renderer, &ri ); DBG_DEBUG( "internal_create_surfaces(SDL2)", "Using: Renderer: %s, Max_w: %d, Max_h: %d, Flags: %d, Formats: %d, %s", ri.name, ri.max_texture_width, ri.max_texture_height, ri.flags, ri.num_texture_formats, SDL_GetPixelFormatName(pixel_format) ); // Non-integer scaling -> enable bilinear filtering (must be done before texture creation) const bool integer_scaling = (x_scale & (SCALE_NEUTRAL_X - 1)) == 0 && (y_scale & (SCALE_NEUTRAL_Y - 1)) == 0; SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, integer_scaling ? "0" : "1" ); // 0=none, 1=bilinear, 2=anisotropic (DirectX only) screen_tx = SDL_CreateTexture( renderer, pixel_format, SDL_TEXTUREACCESS_STREAMING, tex_width, tex_height ); if( screen_tx == NULL ) { dbg->error( "internal_create_surfaces(SDL2)", "Couldn't create texture: %s", SDL_GetError() ); return false; } // Color component bitmasks for the RGB565 pixel format used by simgraph16.cc int bpp; Uint32 rmask, gmask, bmask, amask; if( !SDL_PixelFormatEnumToMasks( pixel_format, &bpp, &rmask, &gmask, &bmask, &amask ) ) { dbg->error( "internal_create_surfaces(SDL2)", "Pixel format error. Couldn't generate masks: %s", SDL_GetError() ); return false; } else if( bpp != COLOUR_DEPTH || amask != 0 ) { dbg->error( "internal_create_surfaces(SDL2)", "Pixel format error. Bpp got %d, needed %d. Amask got %d, needed 0.", bpp, COLOUR_DEPTH, amask ); return false; } screen = SDL_CreateRGBSurface( 0, tex_width, tex_height, bpp, rmask, gmask, bmask, amask ); if( screen == NULL ) { dbg->error( "internal_create_surfaces(SDL2)", "Couldn't get the window surface: %s", SDL_GetError() ); return false; } return true; } // open the window int dr_os_open(const scr_size window_size, sint16 fs) { // scale up resolution res = dr_query_screen_resolution(); const int tex_w = clamp( res.w, 1, SCREEN_TO_TEX_X(window_size.w) ); const int tex_h = clamp( res.h, 1, SCREEN_TO_TEX_Y(window_size.h) ); DBG_MESSAGE("dr_os_open()", "Screen requested %i,%i, available max %i,%i", tex_w, tex_h, res.w, res.h); fullscreen = fs ? BORDERLESS : WINDOWED; // SDL2 has no real fullscreen mode // some cards need those alignments // especially 64bit want a border of 8bytes const int tex_pitch = (tex_w + 15) & 0x7FF0; // SDL2 only works with borderless fullscreen (SDL_WINDOW_FULLSCREEN_DESKTOP) Uint32 flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE; flags |= SDL_WINDOW_ALLOW_HIGHDPI; // apparently needed for Apple retina displays #ifdef __ANDROID__ // needed for landscape apparently flags |= SDL_WINDOW_RESIZABLE; #endif window = SDL_CreateWindow( SIM_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, window_size.w, window_size.h, flags ); if( window == NULL ) { dbg->error("dr_os_open(SDL2)", "Could not open the window: %s", SDL_GetError() ); return 0; } if( !internal_create_surfaces( tex_pitch, tex_h ) ) { return 0; } DBG_MESSAGE("dr_os_open(SDL2)", "SDL realized screen size width=%d, height=%d (internal w=%d, h=%d)", window_size.w, window_size.h, screen->w, screen->h ); SDL_ShowCursor(0); arrow = SDL_GetCursor(); hourglass = SDL_CreateCursor( hourglass_cursor, hourglass_cursor_mask, 16, 22, 8, 11 ); blank = SDL_CreateCursor( blank_cursor, blank_cursor, 8, 2, 0, 0 ); SDL_ShowCursor(1); #if SDL_VERSION_ATLEAST(2, 0, 10) SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); // no mouse emulation for touch #endif assert(tex_pitch <= screen->pitch / (int)sizeof(PIXVAL)); assert(tex_h <= screen->h); assert(tex_w <= tex_pitch); gfx->set_screen_actual_width( tex_w ); gfx->set_screen_height( tex_h ); return tex_pitch; } // shut down SDL void dr_os_close() { SDL_FreeCursor( blank ); SDL_FreeCursor( hourglass ); SDL_DestroyRenderer( renderer ); SDL_DestroyWindow( window ); SDL_StopTextInput(); } // resizes screen int dr_textur_resize(unsigned short** const textur, int tex_w, int const tex_h) { // enforce multiple of 16 pixels, or there are likely mismatches const int tex_pitch = max((tex_w + 15) & 0x7FF0, 16); SDL_UnlockTexture( screen_tx ); if( tex_pitch != screen->w || tex_h != screen->h ) { // Recreate the SDL surfaces at the new resolution. // First free surface and then renderer. SDL_FreeSurface( screen ); screen = NULL; // This destroys texture as well. SDL_DestroyRenderer( renderer ); renderer = NULL; screen_tx = NULL; internal_create_surfaces( tex_pitch, tex_h ); if( screen ) { DBG_MESSAGE("dr_textur_resize(SDL2)", "SDL realized screen size width=%d, height=%d (internal w=%d, h=%d)", tex_w, tex_h, screen->w, screen->h ); } else { dbg->error("dr_textur_resize(SDL2)", "screen is NULL. Good luck!"); } fflush( NULL ); } *textur = dr_textur_init(); assert(tex_pitch <= screen->pitch / (int)sizeof(PIXVAL)); assert(tex_h <= screen->h); assert(tex_w <= tex_pitch); gfx->set_screen_actual_width( tex_w ); return tex_pitch; } unsigned short *dr_textur_init() { // SDL_LockTexture modifies pixels, so copy it first void *pixels = screen->pixels; int pitch = screen->pitch; SDL_LockTexture( screen_tx, NULL, &pixels, &pitch ); return (unsigned short*)screen->pixels; } /** * Transform a 24 bit RGB color into the system format. * @return converted color value */ PIXVAL get_system_color(rgb888_t col) { SDL_PixelFormat *fmt = SDL_AllocFormat( SDL_PIXELFORMAT_RGB565 ); unsigned int ret = SDL_MapRGB(fmt, col.r, col.g, col.b); SDL_FreeFormat( fmt ); assert((ret & 0xFFFF0000u) == 0); return ret; } void dr_prepare_flush() { return; } void dr_flush() { gfx->flush_framebuffer(); if( !use_dirty_tiles ) { SDL_UpdateTexture( screen_tx, NULL, screen->pixels, screen->pitch ); } const scr_rect screen = gfx->get_screen_size(); SDL_Rect rSrc = { 0, 0, screen.w, screen.h }; SDL_RenderCopy( renderer, screen_tx, &rSrc, NULL ); SDL_RenderPresent( renderer ); } void dr_textur(int xp, int yp, int w, int h) { if( use_dirty_tiles ) { SDL_Rect r; r.x = xp; r.y = yp; r.w = xp + w > screen->w ? screen->w - xp : w; r.h = yp + h > screen->h ? screen->h - yp : h; SDL_UpdateTexture( screen_tx, &r, (uint8 *)screen->pixels + yp * screen->pitch + xp * sizeof(PIXVAL), screen->pitch ); } } static bool in_finger_handling = false; // move cursor to the specified location bool move_pointer(int x, int y) { if (in_finger_handling) { return false; } SDL_WarpMouseInWindow( window, TEX_TO_SCREEN_X(x), TEX_TO_SCREEN_Y(y) ); return true; } // set the mouse cursor (pointer/load) void set_pointer(int loading) { SDL_SetCursor( loading ? hourglass : arrow ); } /* * Hier sind die Funktionen zur Messageverarbeitung */ static inline unsigned int ModifierKeys() { const SDL_Keymod mod = SDL_GetModState(); return ((mod & KMOD_SHIFT) ? SIM_KEYMOD_SHIFT : SIM_KEYMOD_NONE) | ((mod & KMOD_CTRL) ? SIM_KEYMOD_CTRL : SIM_KEYMOD_NONE) #ifdef __APPLE__ // Treat the Command key as a control key. | ((mod & KMOD_GUI) ? SIM_KEYMOD_CTRL : SIM_KEYMOD_NONE) #endif ; } static uint16 conv_mouse_buttons(Uint8 const state) { return (state & SDL_BUTTON_LMASK ? MOUSE_LEFTBUTTON : 0) | (state & SDL_BUTTON_MMASK ? MOUSE_MIDBUTTON : 0) | (state & SDL_BUTTON_RMASK ? MOUSE_RIGHTBUTTON : 0); } static void internal_GetEvents() { // Apparently Cocoa SDL posts key events that meant to be used by IM... // Ignoring SDL_KEYDOWN during preedit seems to work fine. static bool composition_is_underway = false; static bool ignore_previous_number = false; static int previous_multifinger_touch = 0; static SDL_FingerID FirstFingerId = 0; static double dLastDist = 0.0; static bool has_queued_finger_release = false; static bool has_queued_zero_mouse_move = false; static sint32 last_mx, last_my; // last finger down pos if (has_queued_finger_release) { // we need to send a finger release, which was not done yet has_queued_finger_release = false; sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_LEFTUP; sys_event.mb = 0; sys_event.mx = last_mx; sys_event.my = last_my; sys_event.key_mod = ModifierKeys(); DBG_MESSAGE("SDL_FINGERUP for queue", "SIM_MOUSE_LEFTUP at %i,%i", sys_event.mx, sys_event.my); return; } if (has_queued_zero_mouse_move) { // we need to send a finger release, which was not done yet has_queued_zero_mouse_move = false; sys_event.type = SIM_MOUSE_MOVE; sys_event.code = 0; sys_event.mb = 0; sys_event.mx = last_mx; sys_event.my = last_my; sys_event.key_mod = ModifierKeys(); DBG_MESSAGE("SDL_FINGERUP for queue", "SIM_MOUSE_MOVE at %i,%i", sys_event.mx, sys_event.my); return; } SDL_Event event; event.type = 1; if (SDL_PollEvent(&event) == 0) { return; } static char textinput[SDL_TEXTINPUTEVENT_TEXT_SIZE]; DBG_DEBUG("SDL_EVENT", "0x%X", event.type); if (in_finger_handling && event.type == SDL_FINGERMOTION) { // swallow the millons of fingermotion events do { } while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FINGERMOTION, SDL_FINGERMOTION) == 1); } switch( event.type ) { case SDL_APP_DIDENTERFOREGROUND: dr_stop_textinput(); intr_enable(); //reenable midi break; case SDL_WINDOWEVENT: if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ) { sys_event.new_window_size_w = max(1, SCREEN_TO_TEX_X(event.window.data1)); sys_event.new_window_size_h = max(1, SCREEN_TO_TEX_Y(event.window.data2)); sys_event.type = SIM_SYSTEM; sys_event.code = SYSTEM_RESIZE; } // Ignore other window events. break; case SDL_MOUSEBUTTONDOWN: dLastDist = 0.0; if (event.button.which != SDL_TOUCH_MOUSEID) { sys_event.type = SIM_MOUSE_BUTTONS; switch( event.button.button ) { case SDL_BUTTON_LEFT: sys_event.code = SIM_MOUSE_LEFTBUTTON; break; case SDL_BUTTON_MIDDLE: sys_event.code = SIM_MOUSE_MIDBUTTON; break; case SDL_BUTTON_RIGHT: sys_event.code = SIM_MOUSE_RIGHTBUTTON; break; case SDL_BUTTON_X1: sys_event.code = SIM_MOUSE_WHEELUP; break; case SDL_BUTTON_X2: sys_event.code = SIM_MOUSE_WHEELDOWN; break; } sys_event.mx = SCREEN_TO_TEX_X(event.button.x); sys_event.my = SCREEN_TO_TEX_Y(event.button.y); sys_event.mb = conv_mouse_buttons( SDL_GetMouseState(0, 0) ); sys_event.key_mod = ModifierKeys(); } break; case SDL_MOUSEBUTTONUP: if (!previous_multifinger_touch && !in_finger_handling) { // we try to only handle mouse events sys_event.type = SIM_MOUSE_BUTTONS; switch( event.button.button ) { case SDL_BUTTON_LEFT: sys_event.code = SIM_MOUSE_LEFTUP; break; case SDL_BUTTON_MIDDLE: sys_event.code = SIM_MOUSE_MIDUP; break; case SDL_BUTTON_RIGHT: sys_event.code = SIM_MOUSE_RIGHTUP; break; } sys_event.mx = SCREEN_TO_TEX_X(event.button.x); sys_event.my = SCREEN_TO_TEX_Y(event.button.y); sys_event.mb = conv_mouse_buttons( SDL_GetMouseState(0, 0) ); sys_event.key_mod = ModifierKeys(); previous_multifinger_touch = false; } break; case SDL_MOUSEWHEEL: if (event.wheel.y != 0) { const bool is_up = (event.wheel.y > 0) ^ (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED); sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = is_up ? SIM_MOUSE_WHEELUP : SIM_MOUSE_WHEELDOWN; sys_event.key_mod = ModifierKeys(); } break; case SDL_MOUSEMOTION: if (!in_finger_handling) { sys_event.type = SIM_MOUSE_MOVE; sys_event.code = SIM_MOUSE_MOVED; sys_event.mx = SCREEN_TO_TEX_X(event.motion.x); sys_event.my = SCREEN_TO_TEX_Y(event.motion.y); sys_event.mb = conv_mouse_buttons( SDL_GetMouseState(0, 0) ); sys_event.key_mod = ModifierKeys(); } break; case SDL_FINGERDOWN: /* just reset scroll state, since another finger may touch down next * The button down events will be from fingr move and the coordinate will be set from mouse up: enough */ DBG_MESSAGE("SDL_FINGERDOWN", "fingerID=%x FirstFingerId=%x Finger %i", (int)event.tfinger.fingerId, (int)FirstFingerId, SDL_GetNumTouchFingers(event.tfinger.touchId)); if (!in_finger_handling) { dLastDist = 0.0; FirstFingerId = event.tfinger.fingerId; DBG_MESSAGE("SDL_FINGERDOWN", "FirstfingerID=%x", FirstFingerId); in_finger_handling = true; previous_multifinger_touch = 0; } else if (FirstFingerId != event.tfinger.fingerId) { previous_multifinger_touch = 2; } break; case SDL_FINGERMOTION: // move whatever if( screen && previous_multifinger_touch==0 && FirstFingerId==event.tfinger.fingerId) { const scr_size screen_size = gfx->get_screen_size(); if (dLastDist == 0.0) { // not yet a finger down event before => we send one dLastDist = 1e-99; sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_LEFTBUTTON; sys_event.mx = event.tfinger.x * screen_size.w; sys_event.my = event.tfinger.y * screen_size.h; previous_multifinger_touch = 0; DBG_MESSAGE("SDL_FINGERMOTION", "SIM_MOUSE_LEFTBUTTON at %i,%i", sys_event.mx, sys_event.my); } else { sys_event.type = SIM_MOUSE_MOVE; sys_event.code = SIM_MOUSE_MOVED; sys_event.mx = event.tfinger.x * screen_size.w; sys_event.my = event.tfinger.y * screen_size.h; DBG_MESSAGE("SDL_FINGERMOTION", "SIM_MOUSE_MOVED at %i,%i", sys_event.mx, sys_event.my); } sys_event.mb = MOUSE_LEFTBUTTON; sys_event.key_mod = ModifierKeys(); } in_finger_handling = true; break; case SDL_FINGERUP: if (screen && in_finger_handling) { if (FirstFingerId==event.tfinger.fingerId || SDL_GetNumTouchFingers(event.tfinger.touchId)==0) { const scr_size screen_size = gfx->get_screen_size(); if(!previous_multifinger_touch) { if (dLastDist == 0.0) { dLastDist = 1e-99; // return a press event sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_LEFTBUTTON; sys_event.mb = MOUSE_LEFTBUTTON; sys_event.key_mod = ModifierKeys(); last_mx = sys_event.mx = event.tfinger.x * screen_size.w; last_my = sys_event.my = event.tfinger.y * screen_size.h; // not yet moved -> set click origin or click will be at last position ... set_click_xy(sys_event.mx, sys_event.my); has_queued_finger_release = true; DBG_MESSAGE("SDL_FINGERUP", "SIM_MOUSE_LEFTDOWN+UP at %i,%i", sys_event.mx, sys_event.my); } else { sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_LEFTUP; sys_event.mb = 0; sys_event.mx = (event.tfinger.x + event.tfinger.dx) * screen_size.w; sys_event.my = (event.tfinger.y + event.tfinger.dy) * screen_size.h; sys_event.key_mod = ModifierKeys(); DBG_MESSAGE("SDL_FINGERUP", "SIM_MOUSE_LEFTUP at %i,%i", sys_event.mx, sys_event.my); } } else { DBG_MESSAGE("SDL_FINGERUP", "ignored at %i,%i because previous_multifinger_touch>0", sys_event.mx, sys_event.my); } previous_multifinger_touch = 0; in_finger_handling = 0; FirstFingerId = 0xFFFF; } else { DBG_MESSAGE("SDL_FINGERUP", "ignored at %i,%i beacuse FirstFingerId(%xd)!=event.tfinger.fingerId(%xd) && SDL_GetNumTouchFinger()=%d", sys_event.mx, sys_event.my,FirstFingerId, event.tfinger.fingerId,SDL_GetNumTouchFingers(event.tfinger.touchId)); } } break; case SDL_MULTIGESTURE: DBG_MESSAGE("SDL_FINGERUP", "Finger %i", SDL_GetNumTouchFingers(event.tfinger.touchId)); in_finger_handling = true; if( event.mgesture.numFingers == 2 ) { int num_events; do { dLastDist += event.mgesture.dDist; num_events = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MULTIGESTURE, SDL_MULTIGESTURE); } while (num_events == 1); // any multitouch is intepreted as pinch zoom if (dLastDist < -DELTA_PINCH) { sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_WHEELDOWN; sys_event.key_mod = ModifierKeys(); //dLastDist += DELTA_PINCH; dLastDist = 0; } else if (dLastDist > DELTA_PINCH) { sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_WHEELUP; sys_event.key_mod = ModifierKeys(); //dLastDist -= DELTA_PINCH; dLastDist = 0; } previous_multifinger_touch = 2; } else if (event.mgesture.numFingers == 3 && screen) { // any three finger touch is scrolling the map if (previous_multifinger_touch != 3) { // just started scrolling set_click_xy(SCREEN_TO_TEX_X(event.mgesture.x* screen->w), SCREEN_TO_TEX_Y(event.mgesture.y* screen->h)); } do { } while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MULTIGESTURE, SDL_MULTIGESTURE) == 1); sys_event.type = SIM_MOUSE_MOVE; sys_event.code = SIM_MOUSE_MOVED; sys_event.mb = MOUSE_RIGHTBUTTON; sys_event.key_mod = ModifierKeys(); sys_event.mx = SCREEN_TO_TEX_X(event.mgesture.x * screen->w); sys_event.my = SCREEN_TO_TEX_Y(event.mgesture.y * screen->h); previous_multifinger_touch = 3; } break; case SDL_KEYDOWN: { // Hack: when 2 byte character composition is under way, we have to leave the key processing with the IME // BUT: if not, we have to do it ourselves, or the cursor or return will not be recognised if( composition_is_underway ) { if( gui_component_t *c = win_get_focus() ) { if( gui_textinput_t *tinp = dynamic_cast(c) ) { if( tinp->get_composition()[0] ) { // pending string, handled by IME break; } } } } unsigned long code; #ifdef _WIN32 // SDL doesn't set numlock state correctly on startup. Revert to win32 function as workaround. const bool key_numlock = ((GetKeyState( VK_NUMLOCK ) & 1) != 0); #else const bool key_numlock = (SDL_GetModState() & KMOD_NUM); #endif const bool numlock = key_numlock || (env_t::numpad_always_moves_map && !win_is_textinput()); sys_event.key_mod = ModifierKeys(); SDL_Keycode sym = event.key.keysym.sym; bool np = false; // to indicate we converted a numpad key switch( sym ) { case SDLK_BACKSPACE: code = SIM_KEYCODE_BACKSPACE; break; case SDLK_TAB: code = SIM_KEYCODE_TAB; break; case SDLK_RETURN: code = SIM_KEYCODE_ENTER; break; case SDLK_ESCAPE: code = SIM_KEYCODE_ESCAPE; break; case SDLK_AC_BACK: case SDLK_DELETE: code = SIM_KEYCODE_DELETE; break; case SDLK_DOWN: code = SIM_KEYCODE_DOWN; break; case SDLK_END: code = SIM_KEYCODE_END; break; case SDLK_HOME: code = SIM_KEYCODE_HOME; break; case SDLK_F1: code = SIM_KEYCODE_F1; break; case SDLK_F2: code = SIM_KEYCODE_F2; break; case SDLK_F3: code = SIM_KEYCODE_F3; break; case SDLK_F4: code = SIM_KEYCODE_F4; break; case SDLK_F5: code = SIM_KEYCODE_F5; break; case SDLK_F6: code = SIM_KEYCODE_F6; break; case SDLK_F7: code = SIM_KEYCODE_F7; break; case SDLK_F8: code = SIM_KEYCODE_F8; break; case SDLK_F9: code = SIM_KEYCODE_F9; break; case SDLK_F10: code = SIM_KEYCODE_F10; break; case SDLK_F11: code = SIM_KEYCODE_F11; break; case SDLK_F12: code = SIM_KEYCODE_F12; break; case SDLK_F13: code = SIM_KEYCODE_F13; break; case SDLK_F14: code = SIM_KEYCODE_F14; break; case SDLK_F15: code = SIM_KEYCODE_F15; break; case SDLK_KP_0: np = true; code = (numlock ? '0' : (unsigned long)SIM_KEYCODE_NUMPAD_BASE); break; case SDLK_KP_1: np = true; code = (numlock ? '1' : (unsigned long)SIM_KEYCODE_DOWNLEFT); break; case SDLK_KP_2: np = true; code = (numlock ? '2' : (unsigned long)SIM_KEYCODE_DOWN); break; case SDLK_KP_3: np = true; code = (numlock ? '3' : (unsigned long)SIM_KEYCODE_DOWNRIGHT); break; case SDLK_KP_4: np = true; code = (numlock ? '4' : (unsigned long)SIM_KEYCODE_LEFT); break; case SDLK_KP_5: np = true; code = (numlock ? '5' : (unsigned long)SIM_KEYCODE_CENTER); break; case SDLK_KP_6: np = true; code = (numlock ? '6' : (unsigned long)SIM_KEYCODE_RIGHT); break; case SDLK_KP_7: np = true; code = (numlock ? '7' : (unsigned long)SIM_KEYCODE_UPLEFT); break; case SDLK_KP_8: np = true; code = (numlock ? '8' : (unsigned long)SIM_KEYCODE_UP); break; case SDLK_KP_9: np = true; code = (numlock ? '9' : (unsigned long)SIM_KEYCODE_UPRIGHT); break; case SDLK_KP_ENTER: code = SIM_KEYCODE_ENTER; break; case SDLK_LEFT: code = SIM_KEYCODE_LEFT; break; case SDLK_PAGEDOWN: code = '<'; break; case SDLK_PAGEUP: code = '>'; break; case SDLK_RIGHT: code = SIM_KEYCODE_RIGHT; break; case SDLK_UP: code = SIM_KEYCODE_UP; break; case SDLK_PAUSE: code = SIM_KEYCODE_PAUSE; break; case SDLK_SCROLLLOCK: code = SIM_KEYCODE_SCROLLLOCK; break; default: { // Handle CTRL-keys. SDL_TEXTINPUT event handles regular input if( (sys_event.key_mod & SIM_KEYMOD_CTRL) && SDLK_a <= sym && sym <= SDLK_z ) { code = event.key.keysym.sym & 31; } else { code = 0; } break; } } ignore_previous_number = (np && key_numlock); sys_event.type = SIM_KEYBOARD; sys_event.code = code; break; } case SDL_TEXTINPUT: { size_t in_pos = 0; utf32 uc = utf8_decoder_t::decode((utf8 const*)event.text.text, in_pos); if( event.text.text[in_pos]==0 ) { // single character if( ignore_previous_number ) { ignore_previous_number = false; break; } sys_event.type = SIM_KEYBOARD; sys_event.code = (unsigned long)uc; } else { // string strcpy( textinput, event.text.text ); sys_event.type = SIM_STRING; sys_event.ptr = (void*)textinput; } sys_event.key_mod = ModifierKeys(); composition_is_underway = false; break; } #ifdef USE_SDL_TEXTEDITING case SDL_TEXTEDITING: { //printf( "SDL_TEXTEDITING {timestamp=%d, \"%s\", start=%d, length=%d}\n", event.edit.timestamp, event.edit.text, event.edit.start, event.edit.length ); strcpy( textinput, event.edit.text ); if( !textinput[0] ) { composition_is_underway = false; } int i = 0; int start = 0; for( ; i(c) ) { tinp->set_composition_status( textinput, start, end-start ); } } composition_is_underway = true; break; } #endif case SDL_KEYUP: { sys_event.type = SIM_KEYBOARD; sys_event.code = 0; break; } case SDL_QUIT: { sys_event.type = SIM_SYSTEM; sys_event.code = SYSTEM_QUIT; break; } default: { sys_event.type = SIM_IGNORE_EVENT; sys_event.code = 0; break; } } } void GetEvents() { internal_GetEvents(); } void show_pointer(int yesno) { SDL_SetCursor( (yesno != 0) ? arrow : blank ); } void ex_ord_update_mx_my() { SDL_PumpEvents(); } uint32 dr_time() { return SDL_GetTicks(); } void dr_sleep(uint32 usec) { SDL_Delay( usec ); } void dr_start_textinput() { if( env_t::hide_keyboard ) { SDL_StartTextInput(); DBG_MESSAGE("SDL_StartTextInput", ""); } } void dr_stop_textinput() { if( env_t::hide_keyboard ) { SDL_StopTextInput(); DBG_MESSAGE("SDL_StoptTextInput", ""); } else { SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE); } } void dr_notify_input_pos(scr_coord pos) { SDL_Rect rect = { TEX_TO_SCREEN_X(pos.x), TEX_TO_SCREEN_Y(pos.y + LINESPACE), 1, 1}; SDL_SetTextInputRect( &rect ); } const char* dr_get_locale() { #if SDL_VERSION_ATLEAST(2, 0, 14) static char LanguageCode[5] = ""; SDL_Locale *loc = SDL_GetPreferredLocales(); if( loc ) { strncpy( LanguageCode, loc->language, 2 ); LanguageCode[2] = 0; DBG_MESSAGE( "dr_get_locale()", "%2s", LanguageCode ); return LanguageCode; } #endif return NULL; } bool dr_has_fullscreen() { return false; } sint16 dr_get_fullscreen() { return fullscreen ? BORDERLESS : WINDOWED; } sint16 dr_toggle_borderless() { if ( fullscreen ) { SDL_SetWindowFullscreen(window, 0); SDL_SetWindowPosition(window, 10, 10); fullscreen = WINDOWED; } else { SDL_SetWindowPosition(window, 0, 0); SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); fullscreen = BORDERLESS; } return fullscreen; } sint16 dr_suspend_fullscreen() { int was_fullscreen = fullscreen; if (fullscreen) { SDL_SetWindowFullscreen(window, 0); fullscreen = WINDOWED; } SDL_MinimizeWindow(window); return was_fullscreen; } void dr_restore_fullscreen(sint16 was_fullscreen) { SDL_RestoreWindow(window); if(was_fullscreen) { SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); fullscreen = BORDERLESS; } } #ifdef _MSC_VER // Needed for MS Visual C++ with /SUBSYSTEM:CONSOLE to work , if /SUBSYSTEM:WINDOWS this function is compiled but unreachable #undef main int main() { return WinMain(NULL,NULL,NULL,NULL); } #endif #ifdef _WIN32 int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int) #else int main(int argc, char **argv) #endif { #ifdef _WIN32 int const argc = __argc; char** const argv = __argv; #endif return sysmain(argc, argv); } simutrans-124.5/src/simutrans/sys/simsys_w.cc000066400000000000000000000667541520675460500214240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef _WIN32 #error "Only Windows has GDI!" #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef __CYGWIN__ extern int __argc; extern char **__argv; #endif #include "simsys.h" #include "../macros.h" #include "../simconst.h" #include "../simdebug.h" #include "../simmem.h" #include "../simversion.h" #include "../simevent.h" #include "../dataobj/environment.h" #include "../display/simgraph.h" #include "../gui/simwin.h" #include "../gui/gui_frame.h" #include "../gui/components/gui_component.h" #include "../gui/components/gui_textinput.h" #include "../utils/unicode.h" // needed for wheel #ifndef WM_MOUSEWHEEL # define WM_MOUSEWHEEL 0x020A #endif #ifndef GET_WHEEL_DELTA_WPARAM # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam)) #endif /* * The class name used to configure the main window. */ static const LPCWSTR WINDOW_CLASS_NAME = L"Simu"; static volatile HWND hwnd; static sint16 fullscreen = WINDOWED; static bool is_not_top = false; static MSG msg; static RECT WindowSize = { 0, 0, 0, 0 }; static RECT MaxSize; static HINSTANCE hInstance; static BITMAPINFO* AllDib = NULL; static PIXVAL* AllDibData; volatile HDC hdc = NULL; #ifdef MULTI_THREAD HANDLE hFlushThread=0; CRITICAL_SECTION redraw_underway; // forward deceleration DWORD WINAPI dr_flush_screen(LPVOID lpParam); #endif static long x_scale = 32; static long y_scale = 32; // scale automatically bool dr_set_screen_scale(sint16 percent) { bool scale_ok = false; long old_scale_x = x_scale, old_scale_y= y_scale; HDC hdc = GetDC(NULL); if( percent == -1 && hdc ) { x_scale = (GetDeviceCaps(hdc, LOGPIXELSX)*32)/96; y_scale = (GetDeviceCaps(hdc, LOGPIXELSY)*32)/96; scale_ok = true; } else if (percent == 0) { x_scale = y_scale = 32; scale_ok = true; } else if (percent < 0) { dbg->error("dr_set_screen_scale(Win32)", "Invalid screen scaling %d (Must be >= -1)", percent); return false; } else { x_scale = (percent * 32) / 100; y_scale = (percent * 32) / 100; } ReleaseDC(NULL, hdc); if( (x_scale!=old_scale_x || y_scale!=old_scale_y) && hwnd) { // force window size update RECT TempRect; GetWindowRect(hwnd, &TempRect); const LRESULT res = PostMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(TempRect.right - TempRect.left, TempRect.bottom - TempRect.top)); } return true; } sint16 dr_get_screen_scale() { return (x_scale * 100) / 32; } /* * Hier sind die Basisfunktionen zur Initialisierung der * Schnittstelle untergebracht * -> init,open,close */ bool dr_os_init(int const* /*parameter*/) { // prepare for next event sys_event.type = SIM_NOEVENT; sys_event.code = 0; return true; } resolution dr_query_screen_resolution() { resolution res; res.w = (GetSystemMetrics(SM_CXSCREEN)*32)/x_scale; res.h = (GetSystemMetrics(SM_CYSCREEN)*32)/y_scale; return res; } static void create_window(DWORD const ex_style, DWORD const style, int const x, int const y, int const w, int const h) { RECT r = { 0, 0, w, h }; AdjustWindowRectEx(&r, style, false, ex_style); // Convert UTF-8 to UTF-16. int const size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, SIM_TITLE, -1, NULL, 0); LPWSTR const wSIM_TITLE = new WCHAR[size]; MultiByteToWideChar(CP_UTF8, 0, SIM_TITLE, -1, wSIM_TITLE, size); hwnd = CreateWindowExW(ex_style, WINDOW_CLASS_NAME, wSIM_TITLE, style, x, y, r.right - r.left, r.bottom - r.top, 0, 0, hInstance, 0); delete[] wSIM_TITLE; ShowWindow(hwnd, SW_SHOW); SetTimer( hwnd, 0, 1111, NULL ); // HACK: so windows thinks we are not dead when processing a timer every 1111 ms ... } static DEVMODE settings; // open the window int dr_os_open(const scr_size window_size, sint16 fs) { MaxSize.right = ((window_size.w*x_scale)/32+15) & 0x7FF0; MaxSize.bottom = (window_size.h*y_scale)/32; fullscreen = fs; #ifdef MULTI_THREAD InitializeCriticalSection( &redraw_underway ); hFlushThread = CreateThread( NULL, 0, dr_flush_screen, 0, CREATE_SUSPENDED, NULL ); #endif MEMZERO(settings); // fake fullscreen if (fullscreen) { // try to force display mode and size settings.dmSize = sizeof(settings); settings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; settings.dmBitsPerPel = COLOUR_DEPTH; settings.dmPelsWidth = MaxSize.right; settings.dmPelsHeight = MaxSize.bottom; settings.dmDisplayFrequency = 0; if( ChangeDisplaySettings(&settings, CDS_TEST)!=DISP_CHANGE_SUCCESSFUL ) { // evt. try again in 32 bit if( COLOUR_DEPTH<32 ) { settings.dmBitsPerPel = 32; } dbg->warning("dr_os_open(w32)", "Could not reduce color depth to 16 Bit in fullscreen." ); } if( ChangeDisplaySettings(&settings, CDS_TEST)!=DISP_CHANGE_SUCCESSFUL ) { ChangeDisplaySettings( NULL, 0 ); fullscreen = WINDOWED; } else { ChangeDisplaySettings(&settings, CDS_FULLSCREEN); } } if( fullscreen ) { create_window(WS_EX_TOPMOST, WS_POPUP, 0, 0, MaxSize.right, MaxSize.bottom); } else { create_window(0, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, MaxSize.right, MaxSize.bottom); } WindowSize.right = MaxSize.right; WindowSize.bottom = MaxSize.bottom; AllDib = MALLOCF(BITMAPINFO, bmiColors, 3); BITMAPINFOHEADER& header = AllDib->bmiHeader; header.biSize = sizeof(BITMAPINFOHEADER); header.biWidth = (window_size.w+15) & 0x7FF0; header.biHeight = window_size.h; header.biPlanes = 1; header.biBitCount = COLOUR_DEPTH; header.biCompression = BI_RGB; header.biSizeImage = 0; header.biXPelsPerMeter = 0; header.biYPelsPerMeter = 0; header.biClrUsed = 0; header.biClrImportant = 0; DWORD* const masks = (DWORD*)AllDib->bmiColors; #ifdef RGB555 masks[0] = 0x01; masks[1] = 0x02; masks[2] = 0x03; #else header.biCompression = BI_BITFIELDS; masks[0] = 0x0000F800; masks[1] = 0x000007E0; masks[2] = 0x0000001F; #endif MaxSize.right = header.biWidth; MaxSize.bottom = header.biHeight; return header.biWidth; } void dr_os_close() { #ifdef MULTI_THREAD if( hFlushThread ) { TerminateThread( hFlushThread, 0 ); LeaveCriticalSection( &redraw_underway ); DeleteCriticalSection( &redraw_underway ); } #endif if (hwnd != NULL) { DestroyWindow(hwnd); } free(AllDibData); AllDibData = NULL; free(AllDib); AllDib = NULL; if( fullscreen == FULLSCREEN ) { ChangeDisplaySettings(NULL, 0); } } // resizes screen int dr_textur_resize(unsigned short** const textur, int w, int const h) { #ifdef MULTI_THREAD EnterCriticalSection( &redraw_underway ); #endif // some cards need those alignments w = (w + 15) & 0x7FF0; if( w<=0 ) { w = 16; } int img_w = w; int img_h = h; if( w > MaxSize.right || h >= MaxSize.bottom ) { // since the query routines that return the desktop data do not take into account a change of resolution free(AllDibData); AllDibData = NULL; MaxSize.right = w; MaxSize.bottom = h; AllDibData = MALLOCN(PIXVAL, img_w * img_h); *textur = (unsigned short*)AllDibData; } AllDib->bmiHeader.biWidth = img_w; AllDib->bmiHeader.biHeight = img_h; WindowSize.right = w; WindowSize.bottom = h; #ifdef MULTI_THREAD LeaveCriticalSection( &redraw_underway ); #endif return w; } unsigned short *dr_textur_init() { size_t const n = AllDib->bmiHeader.biWidth * AllDib->bmiHeader.biHeight; AllDibData = MALLOCN(PIXVAL, n); // start with black MEMZERON(AllDibData, n); return (unsigned short*)AllDibData; } /** * Transform a 24 bit RGB color into the system format. * @return converted color value */ PIXVAL get_system_color(rgb888_t col) { #ifdef RGB555 return ((col.r & 0xF8) << 7) | ((col.g & 0xF8) << 2) | (col.b >> 3); // 15 Bit #else return ((col.r & 0xF8) << 8) | ((col.g & 0xFC) << 3) | (col.b >> 3); #endif } #ifdef MULTI_THREAD // multithreaded screen copy ... DWORD WINAPI dr_flush_screen(LPVOID /*lpParam*/) { while(1) { // wait for finish of thread EnterCriticalSection( &redraw_underway ); hdc = GetDC(hwnd); gfx->flush_framebuffer(); ReleaseDC(hwnd, hdc); hdc = NULL; LeaveCriticalSection( &redraw_underway ); // suspend myself after one update SuspendThread( hFlushThread ); } return 0; } #endif void dr_prepare_flush() { #ifdef MULTI_THREAD // now the thread is finished ... EnterCriticalSection( &redraw_underway ); #endif } void dr_flush() { #ifdef MULTI_THREAD // just let the thread do its work LeaveCriticalSection( &redraw_underway ); ResumeThread( hFlushThread ); #else assert(hdc==NULL); hdc = GetDC(hwnd); gfx->flush_framebuffer(); ReleaseDC(hwnd, hdc); hdc = NULL; #endif } void dr_textur(int xp, int yp, int w, int h) { if( x_scale==32 && y_scale==32 ) { // make really sure we are not beyond screen coordinates w = min( xp+w, AllDib->bmiHeader.biWidth ) - xp; h = min( yp+h, AllDib->bmiHeader.biHeight ) - yp; if( h>1 && w>0 ) { StretchDIBits(hdc, xp, yp, w, h, xp, yp+h+1, w, -h, AllDibData, AllDib, DIB_RGB_COLORS, SRCCOPY); } } else { // align on 32 border to avoid rounding errors w += (xp % 32); h += (yp % 32); w = (w+31) & 0xFFE0; h = (h+31) & 0xFFE0; xp &= 0xFFE0; yp &= 0xFFE0; int xscr = (xp/32)*x_scale; int yscr = (yp/32)*y_scale; // make really sure we are not beyond screen coordinates w = min( xp+w, AllDib->bmiHeader.biWidth ) - xp; h = min( yp+h, AllDib->bmiHeader.biHeight ) - yp; if( h>1 && w>0 ) { SetStretchBltMode( hdc, HALFTONE ); SetBrushOrgEx( hdc, 0, 0, NULL ); StretchDIBits(hdc, xscr, yscr, (w*x_scale)/32, (h*y_scale)/32, xp, yp+h+1, w, -h, AllDibData, AllDib, DIB_RGB_COLORS, SRCCOPY); } } } bool dr_has_fullscreen() { return true; } sint16 dr_get_fullscreen() { return fullscreen; } sint16 dr_toggle_borderless() { if (fullscreen == WINDOWED) { SetWindowLongPtr(hwnd, GWL_STYLE, WS_POPUP); SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW); fullscreen = BORDERLESS; } else if (fullscreen == BORDERLESS) { SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); SetWindowPos(hwnd, HWND_NOTOPMOST, 10, 10, GetSystemMetrics(SM_CXSCREEN)-20, GetSystemMetrics(SM_CYSCREEN)-20, SWP_SHOWWINDOW); fullscreen = WINDOWED; } return fullscreen; } sint16 dr_suspend_fullscreen() { int was_fullscreen = 0; if( hwnd && fullscreen ) { SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW); was_fullscreen = fullscreen; fullscreen = WINDOWED; } else { ShowWindow(hwnd, SW_SHOWMINNOACTIVE); } return was_fullscreen; } void dr_restore_fullscreen(sint16 was_fullscreen) { if( hwnd && was_fullscreen ) { SetWindowLongPtr(hwnd, GWL_STYLE, WS_EX_TOPMOST); SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW); was_fullscreen = fullscreen; } else { ShowWindow(hwnd, SW_RESTORE); } } // move cursor to the specified location bool move_pointer(int x, int y) { POINT pt = { ((long)x*x_scale+16)/32, ((long)y*y_scale+16)/32 }; ClientToScreen(hwnd, &pt); SetCursorPos(pt.x, pt.y); return true; } // set the mouse cursor (pointer/load) void set_pointer(int loading) { SetCursor(LoadCursor(NULL, loading != 0 ? IDC_WAIT : IDC_ARROW)); } /* * Hier sind die Funktionen zur Messageverarbeitung */ static inline unsigned int ModifierKeys() { return (GetKeyState(VK_SHIFT) < 0 ? 1 : 0) | (GetKeyState(VK_CONTROL) < 0 ? 2 : 0); // highest bit set or return value<0 -> key is pressed } /* Windows eventhandler: does most of the work */ LRESULT WINAPI WindowProc(HWND this_hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { bool update_mouse = false; // Used for IME handling. static utf8 *u8buf = NULL; static size_t u8bufsize; static int last_mb = 0; // last mouse button state switch (msg) { case WM_TIMER: // dummy timer even to keep windows thinking we are still active return 0; case WM_ACTIVATE: // may check, if we have to restore color depth if(fullscreen) { // avoid double calls static bool while_handling = false; if(while_handling) { break; } while_handling = true; if(LOWORD(wParam)!=WA_INACTIVE && is_not_top) { #ifdef MULTI_THREAD // no updating while deleting a window please ... EnterCriticalSection( &redraw_underway ); #endif // try to force display mode and size // should be always successful, since it worked as least once ... ChangeDisplaySettings(&settings, CDS_FULLSCREEN); is_not_top = false; // must reshow window, otherwise startbar will be topmost ... create_window(WS_EX_TOPMOST, WS_POPUP, 0, 0, MaxSize.right, MaxSize.bottom); DestroyWindow( this_hwnd ); while_handling = false; #ifdef MULTI_THREAD LeaveCriticalSection( &redraw_underway ); #endif return true; } else if(LOWORD(wParam)==WA_INACTIVE && !is_not_top) { // restore default CloseWindow( hwnd ); ChangeDisplaySettings( NULL, 0 ); is_not_top = true; } while_handling = false; } break; case WM_GETMINMAXINFO: if(fullscreen) { LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam; lpmmi->ptMaxPosition.x = 0; lpmmi->ptMaxPosition.y = 0; } break; case WM_LBUTTONDOWN: /* originally ButtonPress */ SetCapture(this_hwnd); sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_LEFTBUTTON; update_mouse = true; break; case WM_LBUTTONUP: /* originally ButtonRelease */ ReleaseCapture(); sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_LEFTUP; update_mouse = true; break; case WM_MBUTTONDOWN: /* because capture or release may not start with the expected button */ case WM_XBUTTONDOWN: SetCapture(this_hwnd); break; case WM_MBUTTONUP: /* because capture or release may not start with the expected button */ case WM_XBUTTONUP: ReleaseCapture(); break; case WM_RBUTTONDOWN: /* originally ButtonPress */ SetCapture(this_hwnd); sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_RIGHTBUTTON; update_mouse = true; break; case WM_RBUTTONUP: /* originally ButtonRelease */ ReleaseCapture(); sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = SIM_MOUSE_RIGHTUP; update_mouse = true; break; case WM_MOUSEMOVE: sys_event.type = SIM_MOUSE_MOVE; sys_event.code = SIM_MOUSE_MOVED; update_mouse = true; break; case WM_MOUSEWHEEL: sys_event.type = SIM_MOUSE_BUTTONS; sys_event.code = GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? SIM_MOUSE_WHEELUP : SIM_MOUSE_WHEELDOWN; sys_event.key_mod = ModifierKeys(); /* the returned coordinate in LPARAM are absolute coordinates, which will deeply confuse simutrans * we just reuse the coordinates we used the last time by not changing mx/my ... */ return 0; #ifdef WM_DPICHANGED case WM_DPICHANGED: { RECT *r = (LPRECT)lParam; x_scale = (LOWORD(wParam)*32)/96; y_scale = (HIWORD(wParam)*32)/96; SetWindowPos( hwnd, NULL, r->left, r->top, r->right-r->left, r->bottom-r->top, SWP_NOZORDER ); } break; #endif case WM_SIZE: // resize client area if(lParam!=0) { sys_event.type = SIM_SYSTEM; sys_event.code = SYSTEM_RESIZE; sys_event.new_window_size_w = (LOWORD(lParam)*32)/x_scale; if (sys_event.new_window_size_w <= 0) { sys_event.new_window_size_w = 4; } sys_event.new_window_size_h = (HIWORD(lParam)*32)/y_scale; if (sys_event.new_window_size_h <= 1) { sys_event.new_window_size_h = 64; } } break; case WM_PAINT: { if (AllDib != NULL) { PAINTSTRUCT ps; HDC hdcp; hdcp = BeginPaint(hwnd, &ps); AllDib->bmiHeader.biHeight = (WindowSize.bottom*32)/y_scale; StretchDIBits(hdcp, 0, 0, WindowSize.right, WindowSize.bottom, 0, AllDib->bmiHeader.biHeight + 1, AllDib->bmiHeader.biWidth, -AllDib->bmiHeader.biHeight, AllDibData, AllDib, DIB_RGB_COLORS, SRCCOPY); EndPaint(this_hwnd, &ps); } break; } case WM_KEYDOWN: { /* originally KeyPress */ // check for not numlock! sys_event.type = SIM_KEYBOARD; sys_event.code = 0; sys_event.key_mod = ModifierKeys(); sint16 code = lParam >> 16; if( code >= 0x47 && code <= 0x52 && code != 0x4A && code != 0x4e ) { if( (GetKeyState( VK_NUMLOCK ) & 1) == 0 || (env_t::numpad_always_moves_map && !win_is_textinput()) ) { // numlock off? switch( code ) { case 0x47: code = SIM_KEYCODE_UPLEFT; break; case 0x48: code = SIM_KEYCODE_UP; break; case 0x49: code = SIM_KEYCODE_UPRIGHT; break; case 0x4B: code = SIM_KEYCODE_LEFT; break; case 0x4C: code = SIM_KEYCODE_CENTER; break; case 0x4D: code = SIM_KEYCODE_RIGHT; break; case 0x4F: code = SIM_KEYCODE_DOWNLEFT; break; case 0x50: code = SIM_KEYCODE_DOWN; break; case 0x51: code = SIM_KEYCODE_DOWNRIGHT; break; case 0x52: code = SIM_KEYCODE_NUMPAD_BASE; break; } if( code>=SIM_KEYCODE_NUMPAD_BASE ) { // ok found something sys_event.code = code; break; } } } // do low level special stuff here switch (wParam) { case VK_SCROLL: sys_event.code = SIM_KEYCODE_SCROLLLOCK; break; case VK_PAUSE: sys_event.code = SIM_KEYCODE_PAUSE; break; case VK_LEFT: sys_event.code = SIM_KEYCODE_LEFT; break; case VK_RIGHT: sys_event.code = SIM_KEYCODE_RIGHT; break; case VK_UP: sys_event.code = SIM_KEYCODE_UP; break; case VK_DOWN: sys_event.code = SIM_KEYCODE_DOWN; break; case VK_PRIOR: sys_event.code = '>'; break; case VK_NEXT: sys_event.code = '<'; break; case VK_DELETE: sys_event.code = 127; break; case VK_HOME: sys_event.code = SIM_KEYCODE_HOME; break; case VK_END: sys_event.code = SIM_KEYCODE_END; break; } // check for F-Keys! if (sys_event.code == 0 && wParam >= VK_F1 && wParam <= VK_F15) { sys_event.code = wParam - VK_F1 + SIM_KEYCODE_F1; //printf("WindowsEvent: Key %i, (state %i)\n", sys_event.code, sys_event.key_mod); } // some result? if (sys_event.code != 0) return 0; sys_event.type = SIM_NOEVENT; sys_event.code = 0; break; } case WM_CHAR: /* originally KeyPress */ { sint16 code = lParam >> 16; if( code >= 0x47 && code <= 0x52 && code != 0x4A && code != 0x4e ) { if( (GetKeyState( VK_NUMLOCK ) & 1) == 0 || (env_t::numpad_always_moves_map && !win_is_textinput()) ) { // numlock off? // we handled this numpad keys already above ... sys_event.type = SIM_NOEVENT; sys_event.code = 0; break; } } sys_event.type = SIM_KEYBOARD; sys_event.code = wParam; sys_event.key_mod = ModifierKeys(); break; } case WM_IME_SETCONTEXT: // attempt to avoid crash at windows 1809> just not call DefWinodwsProc seems to work for SDL2 ... // DefWindowProc( this_hwnd, msg, wParam, lParam&~ISC_SHOWUICOMPOSITIONWINDOW ); lParam = 0; return 0; case WM_IME_STARTCOMPOSITION: break; case WM_IME_REQUEST: if( wParam == IMR_QUERYCHARPOSITION ) { if( gui_component_t *c = win_get_focus() ) { if( gui_textinput_t *tinp = dynamic_cast(c) ) { IMECHARPOSITION *icp = (IMECHARPOSITION *)lParam; icp->cLineHeight = LINESPACE; scr_coord pos = tinp->get_pos(); scr_coord gui_xy = win_get_pos( win_get_top() ); LONG x = pos.x + gui_xy.x + tinp->get_current_cursor_x(); const char *composition = tinp->get_composition(); for( DWORD i=0; idwCharPos; ++i ) { if( !*composition ) { break; } unsigned char pixel_width; unsigned char unused; gfx->get_next_char_with_metrics( composition, unused, pixel_width ); x += pixel_width; } icp->pt.x = x; icp->pt.y = pos.y + gui_xy.y + D_TITLEBAR_HEIGHT; ClientToScreen( this_hwnd, &icp->pt ); icp->rcDocument.left = 0; icp->rcDocument.right = 0; icp->rcDocument.top = 0; icp->rcDocument.bottom = 0; //printf("IMECHARPOSITION {dwCharPos=%d, pt {x=%d, y=%d} }\n", icp->dwCharPos, icp->pt.x, icp->pt.y); return TRUE; } } break; } return DefWindowProcW( this_hwnd, msg, wParam, lParam ); case WM_IME_COMPOSITION: { HIMC immcx = 0; if( lParam & (GCS_RESULTSTR|GCS_COMPSTR) ) { immcx = ImmGetContext( this_hwnd ); } if( lParam & GCS_RESULTSTR ) { // Retrieve the composition result. size_t u16size = ImmGetCompositionStringW(immcx, GCS_RESULTSTR, NULL, 0); utf16 *u16buf = (utf16*)malloc(u16size + 2); size_t copied = ImmGetCompositionStringW(immcx, GCS_RESULTSTR, u16buf, u16size + 2); // clear old composition if( gui_component_t *c = win_get_focus() ) { if( gui_textinput_t *tinp = dynamic_cast(c) ) { tinp->set_composition_status( NULL, 0, 0 ); } } // add result if( u16size>2 ) { u16buf[copied/2] = 0; // Grow the buffer as needed. size_t u8size = u16size + u16size/2; if( !u8buf ) { u8bufsize = u8size + 1; u8buf = (utf8 *)malloc( u8bufsize ); } else if( u8size >= u8bufsize ) { u8bufsize = max( u8bufsize*2, u8size+1 ); free( u8buf ); u8buf = (utf8 *)malloc( u8bufsize ); } // Convert UTF-16 to UTF-8. utf16 *s = u16buf; int i = 0; while( *s ) { i += utf16_to_utf8( *s++, u8buf+i ); } u8buf[i] = 0; free( u16buf ); sys_event.type = SIM_STRING; sys_event.ptr = (void*)u8buf; } else { // single key sys_event.type = SIM_KEYBOARD; sys_event.code = u16buf[0]; } sys_event.key_mod = ModifierKeys(); } else if( lParam & (GCS_COMPSTR|GCS_COMPATTR) ) { if( gui_component_t *c = win_get_focus() ) { if( gui_textinput_t *tinp = dynamic_cast(c) ) { // Query current conversion status int num_attr = ImmGetCompositionStringW( immcx, GCS_COMPATTR, NULL, 0 ); if( num_attr <= 0 ) { // This shouldn't happen... just in case. break; } char *attrs = (char *)malloc( num_attr ); ImmGetCompositionStringW( immcx, GCS_COMPATTR, attrs, num_attr ); int start = 0; for( ; start < num_attr; ++start ) { if( attrs[start] == ATTR_TARGET_CONVERTED || attrs[start] == ATTR_TARGET_NOTCONVERTED ) { break; } } int end = start; for( ; end < num_attr; ++end ) { if( attrs[end] != ATTR_TARGET_CONVERTED && attrs[end] != ATTR_TARGET_NOTCONVERTED ) { break; } } free( attrs ); // Then retrieve the composition text size_t u16size = ImmGetCompositionStringW( immcx, GCS_COMPSTR, NULL, 0 ); utf16 *u16buf = (utf16 *)malloc( u16size+2 ); size_t copied = ImmGetCompositionStringW( immcx, GCS_COMPSTR, u16buf, u16size+2 ); u16buf[copied/2] = 0; // Grow the buffer as needed. size_t u8size = u16size + u16size/2; if( !u8buf ) { u8bufsize = u8size + 1; u8buf = (utf8 *)malloc( u8bufsize ); } else if( u8size >= u8bufsize ) { u8bufsize = max( u8bufsize*2, u8size+1 ); free( u8buf ); u8buf = (utf8 *)malloc( u8bufsize ); } // Convert UTF-16 to UTF-8. utf16 *s = u16buf; int offset = 0; int i = 0; int u8start = 0, u8end = 0; while( true ) { if( i == start ) u8start = offset; if( i == end ) u8end = offset; if( !*s ) { break; } offset += utf16_to_utf8( *s, u8buf + offset ); ++s; ++i; } u8buf[offset] = 0; free( u16buf ); tinp->set_composition_status( (char *)u8buf, u8start, u8end-u8start ); } } } if( immcx ) { ImmReleaseContext( this_hwnd, immcx ); } break; } case WM_IME_ENDCOMPOSITION: if( gui_component_t *c = win_get_focus() ) { if( gui_textinput_t *tinp = dynamic_cast(c) ) { tinp->set_composition_status( NULL, 0, 0 ); } } break; case WM_CLOSE: if (AllDibData != NULL) { sys_event.type = SIM_SYSTEM; sys_event.code = SYSTEM_QUIT; } break; case WM_DESTROY: free( u8buf ); if( hwnd==this_hwnd || AllDibData == NULL ) { sys_event.type = SIM_SYSTEM; sys_event.code = SYSTEM_QUIT; if( AllDibData == NULL ) { PostQuitMessage(0); hwnd = NULL; } } break; default: return DefWindowProcW(this_hwnd, msg, wParam, lParam); } if( update_mouse ) { sys_event.key_mod = ModifierKeys(); sys_event.mb = last_mb = (wParam&3); sint16 x = LOWORD(lParam); sys_event.mx = (x * 32l)/x_scale; sint16 y = HIWORD(lParam); sys_event.my = (y * 32l)/y_scale; } return 0; } static void internal_GetEvents(bool const wait) { do { // wait for keybord/mouse event GetMessage(&msg, NULL, 0, 0); TranslateMessage(&msg); DispatchMessage(&msg); } while(wait && sys_event.type == SIM_NOEVENT); } void GetEvents() { if (sys_event.type==SIM_NOEVENT && PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { internal_GetEvents(false); } } void show_pointer(int yesno) { static int state=true; if(state^yesno) { ShowCursor(yesno); state = yesno; } } void ex_ord_update_mx_my() { // evt. called before update } uint32 dr_time() { return timeGetTime(); } void dr_sleep(uint32 millisec) { Sleep(millisec); } void dr_start_textinput() { } void dr_stop_textinput() { HIMC immcx = ImmGetContext( hwnd ); ImmNotifyIME( immcx, NI_COMPOSITIONSTR, CPS_CANCEL, 0 ); ImmReleaseContext( hwnd, immcx ); } void dr_notify_input_pos(scr_coord pos) { const int x = (pos.x * x_scale)/32; const int y = (pos.y * y_scale)/32; COMPOSITIONFORM co; co.dwStyle = CFS_POINT; co.ptCurrentPos.x = (LONG)x; co.ptCurrentPos.y = (LONG)y; CANDIDATEFORM ca; ca.dwIndex = 0; ca.dwStyle = CFS_CANDIDATEPOS; ca.ptCurrentPos.x = (LONG)x; ca.ptCurrentPos.y = (LONG)y + D_TITLEBAR_HEIGHT; HIMC immcx = ImmGetContext( hwnd ); ImmSetCompositionWindow( immcx, &co ); ImmSetCandidateWindow( immcx, &ca ); ImmReleaseContext( hwnd, immcx ); } #ifdef _MSC_VER // Needed for MS Visual C++ with /SUBSYSTEM:CONSOLE to work , if /SUBSYSTEM:WINDOWS this function is compiled but unreachable int main() { HINSTANCE const hInstance = (HINSTANCE)GetModuleHandle(NULL); return WinMain(hInstance,NULL,NULL,NULL); } #endif const char* dr_get_locale() { static char LanguageCode[5]=""; GetLocaleInfoA( GetUserDefaultUILanguage(), LOCALE_SISO639LANGNAME, LanguageCode, lengthof( LanguageCode ) ); return LanguageCode; } int CALLBACK WinMain(HINSTANCE const hInstance, HINSTANCE, LPSTR, int) { WNDCLASSW wc; bool timer_is_set = false; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); wc.lpszMenuName = NULL; wc.lpszClassName = WINDOW_CLASS_NAME; RegisterClassW(&wc); GetWindowRect(GetDesktopWindow(), &MaxSize); // maybe set timer to 1ms interval on Win2k upwards ... { OSVERSIONINFO osinfo; osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&osinfo) && osinfo.dwPlatformId==VER_PLATFORM_WIN32_NT) { timeBeginPeriod(1); timer_is_set = true; } } int const res = sysmain(__argc, __argv); if( timer_is_set ) { timeEndPeriod(1); } #ifdef MULTI_THREAD if( hFlushThread ) { TerminateThread( hFlushThread, 0 ); } #endif return res; } simutrans-124.5/src/simutrans/tool/000077500000000000000000000000001520675460500173555ustar00rootroot00000000000000simutrans-124.5/src/simutrans/tool/simmenu.cc000066400000000000000000001400431520675460500213430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../utils/unicode.h" #include "../simevent.h" #include "../world/simworld.h" #include "../gui/simwin.h" #include "../player/simplay.h" #include "../tool/simmenu.h" #include "../tool/simtool.h" #include "../tool/simtool-dialogs.h" #include "../tool/simtool-scripted.h" #include "../simskin.h" #include "../simsound.h" #include "../builder/hausbauer.h" #include "../builder/wegbauer.h" #include "../builder/brueckenbauer.h" #include "../builder/tunnelbauer.h" #include "../script/script_tool_manager.h" #include "../descriptor/building_desc.h" #include "../descriptor/bridge_desc.h" #include "../descriptor/tunnel_desc.h" #include "../ground/grund.h" #include "../obj/way/strasse.h" #include "../dataobj/environment.h" #include "../dataobj/tabfile.h" #include "../dataobj/scenario.h" #include "../obj/roadsign.h" #include "../obj/wayobj.h" #include "../obj/zeiger.h" #include "../gui/tool_selector.h" #include "../utils/simstring.h" #include "../network/memory_rw.h" karte_ptr_t tool_t::welt; // emulate control key by tool uint8 tool_t::control_invert = 0; // for key lookup; is always sorted during the game vector_tpltool_t::char_to_tool(0); // here are the default values, icons, cursor, sound definitions ... vector_tpltool_t::general_tool(GENERAL_TOOL_COUNT); vector_tpltool_t::simple_tool(SIMPLE_TOOL_COUNT); vector_tpltool_t::dialog_tool(DIALOGE_TOOL_COUNT); // the number of toolbars is not know yet vector_tpltool_t::toolbar_tool(0); char tool_t::toolstr[1024]; toolbar_last_used_t* toolbar_last_used_t::last_used_tools = NULL; const char* tool_t::id_to_string(uint16 id) { #define CASE_TO_STRING(entry) case entry: return #entry if (id & GENERAL_TOOL) { switch (id & ~GENERAL_TOOL) { CASE_TO_STRING(TOOL_QUERY); CASE_TO_STRING(TOOL_REMOVER); CASE_TO_STRING(TOOL_RAISE_LAND); CASE_TO_STRING(TOOL_LOWER_LAND); CASE_TO_STRING(TOOL_SETSLOPE); CASE_TO_STRING(TOOL_RESTORESLOPE); CASE_TO_STRING(TOOL_MARKER); CASE_TO_STRING(TOOL_CLEAR_RESERVATION); CASE_TO_STRING(TOOL_TRANSFORMER); CASE_TO_STRING(TOOL_ADD_CITY); CASE_TO_STRING(TOOL_CHANGE_CITY_SIZE); CASE_TO_STRING(TOOL_PLANT_TREE); CASE_TO_STRING(TOOL_SCHEDULE_ADD); CASE_TO_STRING(TOOL_SCHEDULE_INS); CASE_TO_STRING(TOOL_BUILD_WAY); CASE_TO_STRING(TOOL_BUILD_BRIDGE); CASE_TO_STRING(TOOL_BUILD_TUNNEL); CASE_TO_STRING(TOOL_WAYREMOVER); CASE_TO_STRING(TOOL_BUILD_WAYOBJ); CASE_TO_STRING(TOOL_BUILD_STATION); CASE_TO_STRING(TOOL_BUILD_ROADSIGN); CASE_TO_STRING(TOOL_BUILD_DEPOT); CASE_TO_STRING(TOOL_BUILD_HOUSE); CASE_TO_STRING(TOOL_BUILD_LAND_CHAIN); CASE_TO_STRING(TOOL_CITY_CHAIN); CASE_TO_STRING(TOOL_BUILD_FACTORY); CASE_TO_STRING(TOOL_LINK_FACTORY); CASE_TO_STRING(TOOL_HEADQUARTER); CASE_TO_STRING(TOOL_LOCK_GAME); CASE_TO_STRING(TOOL_ADD_CITYCAR); CASE_TO_STRING(TOOL_FOREST); CASE_TO_STRING(TOOL_STOP_MOVER); CASE_TO_STRING(TOOL_MAKE_STOP_PUBLIC); CASE_TO_STRING(TOOL_REMOVE_WAYOBJ); CASE_TO_STRING(TOOL_SLICED_AND_UNDERGROUND_VIEW); CASE_TO_STRING(TOOL_BUY_HOUSE); CASE_TO_STRING(TOOL_BUILD_CITYROAD); CASE_TO_STRING(TOOL_ERROR_MESSAGE); CASE_TO_STRING(TOOL_CHANGE_WATER_HEIGHT); CASE_TO_STRING(TOOL_SET_CLIMATE); CASE_TO_STRING(TOOL_ROTATE_BUILDING); CASE_TO_STRING(TOOL_MERGE_STOP); CASE_TO_STRING(TOOL_EXEC_SCRIPT); CASE_TO_STRING(TOOL_EXEC_TWO_CLICK_SCRIPT); CASE_TO_STRING(TOOL_PLANT_GROUNDOBJ); CASE_TO_STRING(TOOL_REMOVE_SIGNAL); CASE_TO_STRING(TOOL_GENERATE_SCRIPT); CASE_TO_STRING(TOOL_PIPETTE); CASE_TO_STRING(TOOL_SET_OWNER); } } else if (id & SIMPLE_TOOL) { switch (id & ~SIMPLE_TOOL) { CASE_TO_STRING(TOOL_PAUSE); CASE_TO_STRING(TOOL_FASTFORWARD); CASE_TO_STRING(TOOL_SCREENSHOT); CASE_TO_STRING(TOOL_INCREASE_INDUSTRY); CASE_TO_STRING(TOOL_UNDO); CASE_TO_STRING(TOOL_SWITCH_PLAYER); CASE_TO_STRING(TOOL_STEP_YEAR); CASE_TO_STRING(TOOL_CHANGE_GAME_SPEED); CASE_TO_STRING(TOOL_ZOOM_IN); CASE_TO_STRING(TOOL_ZOOM_OUT); CASE_TO_STRING(TOOL_SHOW_COVERAGE); CASE_TO_STRING(TOOL_SHOW_NAME); CASE_TO_STRING(TOOL_SHOW_GRID); CASE_TO_STRING(TOOL_SHOW_TREES); CASE_TO_STRING(TOOL_SHOW_HOUSES); CASE_TO_STRING(TOOL_SHOW_UNDERGROUND); CASE_TO_STRING(TOOL_ROTATE90); CASE_TO_STRING(TOOL_QUIT); CASE_TO_STRING(TOOL_FILL_TREES); CASE_TO_STRING(TOOL_DAYNIGHT_LEVEL); CASE_TO_STRING(TOOL_VEHICLE_TOOLTIPS); CASE_TO_STRING(TOOL_TOOGLE_PAX); CASE_TO_STRING(TOOL_TOOGLE_PEDESTRIANS); CASE_TO_STRING(TOOL_TRAFFIC_LEVEL); CASE_TO_STRING(TOOL_CHANGE_CONVOI); CASE_TO_STRING(TOOL_CHANGE_LINE); CASE_TO_STRING(TOOL_CHANGE_DEPOT); CASE_TO_STRING(UNUSED_WKZ_PWDHASH_TOOL); CASE_TO_STRING(TOOL_CHANGE_PLAYER); CASE_TO_STRING(TOOL_CHANGE_TRAFFIC_LIGHT); CASE_TO_STRING(TOOL_CHANGE_CITY); CASE_TO_STRING(TOOL_RENAME); CASE_TO_STRING(UNUSED_TOOL_ADD_MESSAGE); CASE_TO_STRING(TOOL_TOGGLE_RESERVATION); CASE_TO_STRING(TOOL_VIEW_OWNER); CASE_TO_STRING(TOOL_HIDE_UNDER_CURSOR); CASE_TO_STRING(TOOL_MOVE_MAP); CASE_TO_STRING(TOOL_ROLLUP_ALL_WIN); CASE_TO_STRING(TOOL_RECOLOUR_TOOL); CASE_TO_STRING(TOOL_SHOW_FACTORY_STORAGE); CASE_TO_STRING(TOOL_TOGGLE_CONTROL); CASE_TO_STRING(TOOL_LOAD_SCENARIO); CASE_TO_STRING(TOOL_DAY_NIGHT_TOGGLE); CASE_TO_STRING(TOOL_WORK_WORLD); CASE_TO_STRING(TOOL_HALT_PERMISSION); } } else if (id & DIALOGE_TOOL) { switch (id & ~DIALOGE_TOOL) { CASE_TO_STRING(DIALOG_HELP); CASE_TO_STRING(DIALOG_OPTIONS); CASE_TO_STRING(DIALOG_MINIMAP); CASE_TO_STRING(DIALOG_LINEOVERVIEW); CASE_TO_STRING(DIALOG_MESSAGES); CASE_TO_STRING(DIALOG_FINANCES); CASE_TO_STRING(DIALOG_PLAYERS); CASE_TO_STRING(DIALOG_DISPLAYOPTIONS); CASE_TO_STRING(DIALOG_SOUND); CASE_TO_STRING(DIALOG_LANGUAGE); CASE_TO_STRING(DIALOG_PLAYERCOLOR); CASE_TO_STRING(DIALOG_JUMP); CASE_TO_STRING(DIALOG_LOAD); CASE_TO_STRING(DIALOG_SAVE); CASE_TO_STRING(DIALOG_LIST_HALT); CASE_TO_STRING(DIALOG_LIST_CONVOI); CASE_TO_STRING(DIALOG_LIST_TOWN); CASE_TO_STRING(DIALOG_LIST_GOODS); CASE_TO_STRING(DIALOG_LIST_FACTORY); CASE_TO_STRING(DIALOG_LIST_CURIOSITY); CASE_TO_STRING(DIALOG_EDIT_FACTORY); CASE_TO_STRING(DIALOG_EDIT_ATTRACTION); CASE_TO_STRING(DIALOG_EDIT_HOUSE); CASE_TO_STRING(DIALOG_EDIT_TREE); CASE_TO_STRING(DIALOG_ENLARGE_MAP); CASE_TO_STRING(DIALOG_LIST_LABEL); CASE_TO_STRING(DIALOG_CLIMATES); CASE_TO_STRING(DIALOG_SETTINGS); CASE_TO_STRING(DIALOG_GAMEINFO); CASE_TO_STRING(DIALOG_THEMES); CASE_TO_STRING(DIALOG_SCENARIO); CASE_TO_STRING(DIALOG_SCENARIO_INFO); CASE_TO_STRING(DIALOG_LIST_DEPOT); CASE_TO_STRING(DIALOG_LIST_VEHICLE); CASE_TO_STRING(DIALOG_SCRIPT_TOOL); CASE_TO_STRING(DIALOG_EDIT_GROUNDOBJ); CASE_TO_STRING(DIALOG_CHAT); CASE_TO_STRING(DIALOG_PLAYER_RANKING); } } return ""; } // separator in toolbars class tool_dummy_t : public tool_t { public: tool_dummy_t() : tool_t(dummy_id) {} bool init(player_t*) OVERRIDE { return false; } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; tool_t* tool_t::dummy = new tool_dummy_t(); tool_t* create_general_tool(int toolnr) { tool_t* tool = NULL; switch (toolnr) { case TOOL_QUERY: tool = new tool_query_t(); break; case TOOL_REMOVER: tool = new tool_remover_t(); break; case TOOL_RAISE_LAND: tool = new tool_raise_t(); break; case TOOL_LOWER_LAND: tool = new tool_lower_t(); break; case TOOL_SETSLOPE: tool = new tool_setslope_t(); break; case TOOL_RESTORESLOPE: tool = new tool_restoreslope_t(); break; case TOOL_MARKER: tool = new tool_marker_t(); break; case TOOL_CLEAR_RESERVATION: tool = new tool_clear_reservation_t(); break; case TOOL_TRANSFORMER: tool = new tool_transformer_t(); break; case TOOL_ADD_CITY: tool = new tool_add_city_t(); break; case TOOL_CHANGE_CITY_SIZE: tool = new tool_change_city_size_t(); break; case TOOL_PLANT_TREE: tool = new tool_plant_tree_t(); break; case TOOL_SCHEDULE_ADD: tool = new tool_schedule_add_t(); break; case TOOL_SCHEDULE_INS: tool = new tool_schedule_ins_t(); break; case TOOL_BUILD_WAY: tool = new tool_build_way_t(); break; case TOOL_BUILD_BRIDGE: tool = new tool_build_bridge_t(); break; case TOOL_BUILD_TUNNEL: tool = new tool_build_tunnel_t(); break; case TOOL_WAYREMOVER: tool = new tool_wayremover_t(); break; case TOOL_BUILD_WAYOBJ: tool = new tool_build_wayobj_t(); break; case TOOL_BUILD_STATION: tool = new tool_build_station_t(); break; case TOOL_BUILD_ROADSIGN: tool = new tool_build_roadsign_t(); break; case TOOL_BUILD_DEPOT: tool = new tool_build_depot_t(); break; case TOOL_BUILD_HOUSE: tool = new tool_build_house_t(); break; case TOOL_BUILD_LAND_CHAIN: tool = new tool_build_land_chain_t(); break; case TOOL_CITY_CHAIN: tool = new tool_city_chain_t(); break; case TOOL_BUILD_FACTORY: tool = new tool_build_factory_t(); break; case TOOL_LINK_FACTORY: tool = new tool_link_factory_t(); break; case TOOL_HEADQUARTER: tool = new tool_headquarter_t(); break; case TOOL_LOCK_GAME: tool = new tool_lock_game_t(); break; case TOOL_ADD_CITYCAR: tool = new tool_add_citycar_t(); break; case TOOL_FOREST: tool = new tool_forest_t(); break; case TOOL_STOP_MOVER: tool = new tool_stop_mover_t(); break; case TOOL_MAKE_STOP_PUBLIC: tool = new tool_make_stop_public_t(); break; case TOOL_REMOVE_WAYOBJ: tool = new tool_remove_wayobj_t(); break; case TOOL_SLICED_AND_UNDERGROUND_VIEW: tool = new tool_show_underground_t(); break; case TOOL_BUY_HOUSE: tool = new tool_buy_house_t(); break; case TOOL_BUILD_CITYROAD: tool = new tool_build_cityroad(); break; case TOOL_ERROR_MESSAGE: tool = new tool_error_message_t(); break; case TOOL_CHANGE_WATER_HEIGHT: tool = new tool_change_water_height_t(); break; case TOOL_SET_CLIMATE: tool = new tool_set_climate_t(); break; case TOOL_ROTATE_BUILDING: tool = new tool_rotate_building_t(); break; case TOOL_MERGE_STOP: tool = new tool_merge_stop_t(); break; case TOOL_EXEC_SCRIPT: tool = new tool_exec_script_t(); break; case TOOL_EXEC_TWO_CLICK_SCRIPT: tool = new tool_exec_two_click_script_t(); break; case TOOL_PLANT_GROUNDOBJ: tool = new tool_plant_groundobj_t(); break; case TOOL_ADD_MESSAGE: tool = new tool_add_message_t(); break; case TOOL_REMOVE_SIGNAL: tool = new tool_remove_signal_t(); break; case TOOL_GENERATE_SCRIPT: tool = new tool_generate_script_t(); break; case TOOL_PIPETTE: tool = new tool_pipette_t(); break; case TOOL_SET_OWNER: tool = new tool_change_owner_t(); break; default: dbg->error("create_general_tool()", "cannot satisfy request for general_tool[%i]!", toolnr); return NULL; } // check for right id (exception: TOOL_SLICED_AND_UNDERGROUND_VIEW) assert(tool->get_id() == (toolnr | GENERAL_TOOL) || toolnr == TOOL_SLICED_AND_UNDERGROUND_VIEW); return tool; } tool_t* create_simple_tool(int toolnr) { tool_t* tool = NULL; switch (toolnr) { case TOOL_PAUSE: tool = new tool_pause_t(); break; case TOOL_FASTFORWARD: tool = new tool_fastforward_t(); break; case TOOL_SCREENSHOT: tool = new tool_screenshot_t(); break; case TOOL_INCREASE_INDUSTRY: tool = new tool_increase_industry_t(); break; case TOOL_UNDO: tool = new tool_undo_t(); break; case TOOL_SWITCH_PLAYER: tool = new tool_switch_player_t(); break; case TOOL_STEP_YEAR: tool = new tool_step_year_t(); break; case TOOL_CHANGE_GAME_SPEED: tool = new tool_change_game_speed_t(); break; case TOOL_ZOOM_IN: tool = new tool_zoom_in_t(); break; case TOOL_ZOOM_OUT: tool = new tool_zoom_out_t(); break; case TOOL_SHOW_COVERAGE: tool = new tool_show_coverage_t(); break; case TOOL_SHOW_NAME: tool = new tool_show_name_t(); break; case TOOL_SHOW_GRID: tool = new tool_show_grid_t(); break; case TOOL_SHOW_TREES: tool = new tool_show_trees_t(); break; case TOOL_SHOW_HOUSES: tool = new tool_show_houses_t(); break; case TOOL_SHOW_UNDERGROUND: tool = new tool_show_underground_t(); break; case TOOL_ROTATE90: tool = new tool_rotate90_t(); break; case TOOL_QUIT: tool = new tool_quit_t(); break; case TOOL_FILL_TREES: tool = new tool_fill_trees_t(); break; case TOOL_DAYNIGHT_LEVEL: tool = new tool_daynight_level_t(); break; case TOOL_VEHICLE_TOOLTIPS: tool = new tool_vehicle_tooltips_t(); break; case TOOL_TOOGLE_PAX: tool = new tool_toggle_pax_station_t(); break; case TOOL_TOOGLE_PEDESTRIANS: tool = new tool_toggle_pedestrians_t(); break; case TOOL_TRAFFIC_LEVEL: tool = new tool_traffic_level_t(); break; case TOOL_CHANGE_CONVOI: tool = new tool_change_convoi_t(); break; case TOOL_CHANGE_LINE: tool = new tool_change_line_t(); break; case TOOL_CHANGE_DEPOT: tool = new tool_change_depot_t(); break; case TOOL_CHANGE_PLAYER: tool = new tool_change_player_t(); break; case TOOL_CHANGE_TRAFFIC_LIGHT: tool = new tool_change_traffic_light_t(); break; case TOOL_CHANGE_CITY: tool = new tool_change_city_t(); break; case TOOL_RENAME: tool = new tool_rename_t(); break; case TOOL_TOGGLE_RESERVATION: tool = new tool_toggle_reservation_t(); break; case TOOL_VIEW_OWNER: tool = new tool_view_owner_t(); break; case TOOL_HIDE_UNDER_CURSOR: tool = new tool_hide_under_cursor_t(); break; case TOOL_MOVE_MAP: tool = new tool_move_map_t(); break; case TOOL_ROLLUP_ALL_WIN: tool = new tool_rollup_all_win_t(); break; case TOOL_RECOLOUR_TOOL: tool = new tool_recolour_t(); break; case TOOL_SHOW_FACTORY_STORAGE: tool = new tool_show_factory_storage_t(); break; case TOOL_TOGGLE_CONTROL: tool = new tool_toggle_control_t(); break; case TOOL_LOAD_SCENARIO: tool = new tool_load_scenario_t(); break; case TOOL_DAY_NIGHT_TOGGLE: tool = new tool_day_night_toggle_t(); break; case TOOL_SINGLE_WAY_TOOGLE: tool = new tool_show_single_ways_t(); break; case TOOL_WORK_WORLD: tool = new tool_work_world_t(); break; case TOOL_HALT_PERMISSION: tool = new tool_change_permission_t(); break; case UNUSED_TOOL_ADD_MESSAGE: // fall-through - intended!!!111elf case UNUSED_WKZ_PWDHASH_TOOL: dbg->warning("create_simple_tool()", "Deprecated tool [%i] requested", toolnr); return NULL; default: dbg->error("create_simple_tool()", "cannot satisfy request for simple_tool[%i]!", toolnr); return NULL; } assert(tool->get_id() == (toolnr | SIMPLE_TOOL)); return tool; } tool_t* create_dialog_tool(int toolnr) { tool_t* tool = NULL; switch (toolnr) { case DIALOG_HELP: tool = new dialog_help_t(); break; case DIALOG_OPTIONS: tool = new dialog_options_t(); break; case DIALOG_MINIMAP: tool = new dialog_minimap_t(); break; case DIALOG_LINEOVERVIEW: tool = new dialog_lines_t(); break; case DIALOG_MESSAGES: tool = new dialog_messages_t(); break; case DIALOG_FINANCES: tool = new dialog_finances_t(); break; case DIALOG_PLAYERS: tool = new dialog_players_t(); break; case DIALOG_DISPLAYOPTIONS: tool = new dialog_displayoptions_t(); break; case DIALOG_SOUND: tool = new dialog_sound_t(); break; case DIALOG_LANGUAGE: tool = new dialog_language_t(); break; case DIALOG_PLAYERCOLOR: tool = new dialog_playercolor_t(); break; case DIALOG_JUMP: tool = new dialog_jump_t(); break; case DIALOG_LOAD: tool = new dialog_load_t(); break; case DIALOG_SAVE: tool = new dialog_save_t(); break; case DIALOG_LIST_HALT: tool = new dialog_list_halt_t(); break; case DIALOG_LIST_CONVOI: tool = new dialog_list_convoi_t(); break; case DIALOG_LIST_TOWN: tool = new dialog_list_town_t(); break; case DIALOG_LIST_GOODS: tool = new dialog_list_goods_t(); break; case DIALOG_LIST_FACTORY: tool = new dialog_list_factory_t(); break; case DIALOG_LIST_CURIOSITY: tool = new dialog_list_curiosity_t(); break; case DIALOG_EDIT_FACTORY: tool = new dialog_edit_factory_t(); break; case DIALOG_EDIT_ATTRACTION: tool = new dialog_edit_attraction_t(); break; case DIALOG_EDIT_HOUSE: tool = new dialog_edit_house_t(); break; case DIALOG_EDIT_TREE: tool = new dialog_edit_tree_t(); break; case DIALOG_ENLARGE_MAP: tool = new dialog_enlarge_map_t(); break; case DIALOG_LIST_LABEL: tool = new dialog_list_label_t(); break; case DIALOG_CLIMATES: tool = new dialog_climates_t(); break; case DIALOG_SETTINGS: tool = new dialog_settings_t(); break; case DIALOG_GAMEINFO: tool = new dialog_gameinfo_t(); break; case DIALOG_THEMES: tool = new dialog_themes_t(); break; case DIALOG_SCENARIO: tool = new dialog_scenario_t(); break; case DIALOG_SCENARIO_INFO: tool = new dialog_scenario_info_t(); break; case DIALOG_LIST_DEPOT: tool = new dialog_list_depot_t(); break; case DIALOG_LIST_VEHICLE: tool = new dialog_list_vehicle_t(); break; case DIALOG_SCRIPT_TOOL: tool = new dialog_script_tool_t(); break; case DIALOG_EDIT_GROUNDOBJ: tool = new dialog_edit_groundobj_t(); break; case DIALOG_CHAT: tool = new dialog_chat_t(); break; case DIALOG_PLAYER_RANKING: tool = new dialog_player_ranking_t(); break; default: dbg->error("create_dialog_tool()", "cannot satisfy request for dialog_tool[%i]!", toolnr); return NULL; } assert(tool->get_id() == (toolnr | DIALOGE_TOOL)); return tool; } tool_t* create_tool(int toolnr) { tool_t* tool = NULL; if (toolnr & GENERAL_TOOL) { tool = create_general_tool(toolnr & 0xFFF); } else if (toolnr & SIMPLE_TOOL) { tool = create_simple_tool(toolnr & 0xFFF); } else if (toolnr & DIALOGE_TOOL) { tool = create_dialog_tool(toolnr & 0xFFF); } if (tool == NULL) { dbg->error("create_tool()", "cannot satisfy request for tool with id %i!", toolnr); } return tool; } /** * Returns desc and tool pointer corresponding to the * general toolid with name @p param_str. */ void general_tool_get_desc_builder(uint16 id, const char* param_str, const obj_desc_timelined_t*& desc, tool_t*& tool) { if (id & ((uint16)SIMPLE_TOOL | (uint16)DIALOGE_TOOL)) { return; } id = id & (~GENERAL_TOOL); const obj_desc_transport_infrastructure_t* desc1 = NULL; if (param_str) { switch (id) { case TOOL_BUILD_WAY: desc1 = way_builder_t::get_desc(param_str); break; case TOOL_BUILD_BRIDGE: desc1 = bridge_builder_t::get_desc(param_str); break; case TOOL_BUILD_TUNNEL: desc1 = tunnel_builder_t::get_desc(param_str); break; case TOOL_BUILD_ROADSIGN: desc1 = roadsign_t::find_desc(param_str); break; case TOOL_BUILD_WAYOBJ: desc1 = wayobj_t::find_desc(param_str); break; // The following 3's descriptions are registered by hausbauer_t. case TOOL_BUILD_DEPOT: case TOOL_BUILD_STATION: case TOOL_HEADQUARTER: { const building_desc_t* desc2 = hausbauer_t::get_desc(param_str); desc = desc2; tool = desc2 ? desc2->get_builder() : NULL; return; } default:; } } if (desc1) { desc = desc1; tool = desc1->get_builder(); } } /** * Set the defaults of a newly created general tool. */ void set_defaults_general_tool(tool_t* tool, const char* param_str) { if (tool == NULL) { return; } tool_t* copy_from = NULL; const obj_desc_timelined_t* desc = NULL; general_tool_get_desc_builder(tool->get_id(), param_str, desc, copy_from); if (copy_from) { *tool = *copy_from; } } /** * Checks whether a tool is available in the current timeline. * * Note that this function would return true on error. It is done so * if no description was found, the previous toolbar button logic * will still be applied - show buttons with icons regardless of * whether the objects they build are available or not. */ bool check_tool_availability(const tool_t* tool, uint16 time) { if (tool == NULL) { return true; } tool_t* dummy = NULL; const obj_desc_timelined_t* desc = NULL; general_tool_get_desc_builder(tool->get_id(), tool->get_default_param(), desc, dummy); return desc ? desc->is_available(time) : true; } static utf32 str_to_key(const char* str, uint8* modifier) { *modifier = 0; // default no modufier check if (str[1] == ',' || str[1] <= ' ') { return (uint8)*str; } else { // check for control char if (str[0] == '^') { if (str[1] == 0 || str[1] == '^') { return str[1]; } else { *modifier = 2; // only single character following =>make is 1..26 value if (isalpha(str[1])) { return tolower(str[1]) - 'a' + 1; } str++; } } // add shift as requested modifier? if (str[0] == '+') { if (str[1] == '+' || str[1] == 0) { return '+'; } *modifier = 1; str++; } // direct value (decimal) if (str[0] == '#') { return (str[1] == '#') ? str[1] : atoi(str + 1); } // check for utf8 if (127 < (uint8)*str) { size_t len = 0; utf32 const c = utf8_decoder_t::decode((utf8 const*)str, len); if (str[len] == ',') { return c; } } // Function key? if (str[0] == 'F') { uint8 function = atoi(str + 1); if (function > 0) { return SIM_KEYCODE_F1 + function - 1; } } // COMMA if (strstart(str, "COMMA")) { return ','; } // Scroll lock if (strstart(str, "SCROLLLOCK")) { return SIM_KEYCODE_SCROLLLOCK; } // break/pause key if (strstart(str, "PAUSE")) { return SIM_KEYCODE_PAUSE; } // HOME if (strstart(str, "HOME")) { return SIM_KEYCODE_HOME; } // END if (strstart(str, "END")) { return SIM_KEYCODE_END; } // SPACE if (strstart(str, "SPACE")) { return SIM_KEYCODE_SPACE; } // ENTER //if (strstart(str, "ENTER")) { // return SIM_KEYCODE_ENTER //} // ESC if (strstart(str, "ESC")) { // but currently fixed binding! return SIM_KEYCODE_ESCAPE; } if (strstart(str, "DELETE")) { // but currently fixed binding! return SIM_KEYCODE_DELETE; } if (strstart(str, "BACKSPACE")) { // but currently fixed binding! return SIM_KEYCODE_BACKSPACE; } // NUMPAD if (const char* c = strstart(str, "NUM_")) { return SIM_KEYCODE_NUMPAD_BASE + atoi(c); } } // invalid key return 0xFFFF; } // just fills the default tables before other tools are added void tool_t::init_menu() { for (uint16 i = 0; i < GENERAL_TOOL_COUNT; i++) { tool_t* tool = create_general_tool(i); general_tool.append(tool); } for (uint16 i = 0; i < SIMPLE_TOOL_COUNT; i++) { // To squelch warning on startup if (i == UNUSED_TOOL_ADD_MESSAGE || i == UNUSED_WKZ_PWDHASH_TOOL) { simple_tool.append(NULL); continue; } tool_t* tool = create_simple_tool(i); simple_tool.append(tool); } for (uint16 i = 0; i < DIALOGE_TOOL_COUNT; i++) { tool_t* tool = create_dialog_tool(i); dialog_tool.append(tool); } } void tool_t::exit_menu() { clear_ptr_vector(general_tool); clear_ptr_vector(simple_tool); clear_ptr_vector(dialog_tool); } // for sorting: compare tool key static bool compare_tool(tool_t const* const a, tool_t const* const b) { uint16 const ac = a->command_key & ~32; uint16 const bc = b->command_key & ~32; return ac != bc ? ac < bc : a->command_key < b->command_key; } // read a tab file to add images, cursors and sound to the tools bool tool_t::read_menu(const std::string& menuconf_path) { char_to_tool.clear(); tabfile_t menuconf; // only use pak specific menus, since otherwise images may be missing if (!menuconf.open(menuconf_path.c_str())) { return false; } tabfileobj_t contents; menuconf.read(contents); // structure to hold information for iterating through different tool types struct tool_class_info_t { const char* type; uint16 count; vector_tpl& tools; const skin_desc_t* icons; const skin_desc_t* cursor; bool with_sound; }; tool_class_info_t info[] = { { "general_tool", GENERAL_TOOL_COUNT, general_tool, skinverwaltung_t::tool_icons_general, skinverwaltung_t::cursor_general, true }, { "simple_tool", SIMPLE_TOOL_COUNT, simple_tool, skinverwaltung_t::tool_icons_simple, NULL, false}, { "dialog_tool", DIALOGE_TOOL_COUNT, dialog_tool, skinverwaltung_t::tool_icons_dialoge, NULL, false } }; // first init all tools DBG_MESSAGE("tool_t::read_menu()", "Reading general menu"); for (uint16 t = 0; t < 3; t++) { for (uint16 i = 0; i < info[t].count; i++) { char id[256]; sprintf(id, "%s[%i]", info[t].type, i); const char* str = contents.get(id); /* Format of str: * for general tools: icon,cursor,sound,key * icon is image number in menu.GeneralTools, cursor image number in cursor.GeneralTools * for simple and dialog tools: icon,key * icon is image number in menu.SimpleTools and menu.DialogeTools * -1 will disable any of them */ tool_t* tool = info[t].tools[i]; if (!tool) { if (str && strcmp(str, "") != 0) { // this key is present in the tab file dbg->warning("tool_t::read_menu", "Ignoring deprecated %s[%i] (%s)", info[t].type, i, tool_t::id_to_string((1 << (t + 12)) | i)); } continue; } while (*str == ' ') { str++; } if (*str && *str != ',') { // ok, first comes icon uint16 icon = (uint16)atoi(str); if (icon == 0 && *str != '0') { // check, if file name ... int i = 0; while (str[i] != 0 && str[i] != ',') { i++; } const skin_desc_t* s = skinverwaltung_t::get_extra(str, i - 1); tool->icon = s ? s->get_image_id(0) : IMG_EMPTY; } else { if (icon >= info[t].icons->get_count()) { dbg->warning("tool_t::init_menu()", "wrong icon (%i) given for %s[%i]", icon, info[t].type, i); } tool->icon = info[t].icons->get_image_id(icon); } do { str++; } while (*str && *str != ','); } if (info[t].cursor) { if (*str == ',') { // next comes cursor str++; if (*str && *str != ',') { uint16 cursor = (uint16)atoi(str); if (cursor >= info[t].cursor->get_count()) { dbg->warning("tool_t::init_menu()", "wrong cursor (%i) given for %s[%i]", cursor, info[t].type, i); } tool->cursor = info[t].cursor->get_image_id(cursor); do { str++; } while (*str && *str != ','); } } } if (info[t].with_sound) { if (*str == ',') { // ok_sound str++; if (*str && *str != ',') { int sound = atoi(str); if (sound > 0) { tool->ok_sound = sound_desc_t::get_compatible_sound_id(sound); } do { str++; } while (*str && *str != ','); } } } if (*str == ',') { // key str++; while (*str == ' ') { str++; } if (*str >= ' ') { tool->command_key = str_to_key(str, &tool->command_flags); char_to_tool.append(tool); } } } } // now the toolbar tools DBG_MESSAGE("tool_t::read_menu()", "Reading toolbars"); toolbar_last_used_t::last_used_tools = new toolbar_last_used_t(TOOL_LAST_USED | TOOLBAR_TOOL, "Last used tools", "last_used.txt"); // first: add main menu toolbar_tool.reserve(skinverwaltung_t::tool_icons_toolbars->get_count()); toolbar_tool.append(new toolbar_t(TOOLBAR_TOOL, "", "")); for (uint16 i = 0; i < toolbar_tool.get_count(); i++) { char id[256]; for (int j = 0; ; j++) { /* str should now contain something like 1,2,-1 * first parameter is the image number in "GeneralTools" * next is the cursor in "GeneralTools" * final is the sound * -1 will disable any of them */ sprintf(id, "toolbar[%i][%i]", i, j); const char* str = contents.get(id); if (*str == 0) { // empty entry => toolbar finished ... break; } tool_t* addtool = NULL; /* first, parse the string; we could have up to four parameters */ const char* toolname = str; image_id icon = IMG_EMPTY; const char* key_str = NULL; const char* param_str = NULL; // in case of toolbars, it will also contain the tooltip // parse until next zero-level comma uint level = 0; while (*str) { if (*str == ')') { level++; } else if (*str == '(') { level--; } else if (*str == ',' && level == 0) { break; } str++; } // icon if (*str == ',') { str++; if (*str != ',') { // ok, first come icon while (*str == ' ') { str++; } icon = (uint16)atoi(str); if (icon == 0 && *str != '0') { // check, if file name ... int i = 0; while (str[i] != 0 && str[i] != ',') { i++; } const skin_desc_t* s = skinverwaltung_t::get_extra(str, i - 1); icon = s ? s->get_image_id(0) : IMG_EMPTY; } else { if (icon >= skinverwaltung_t::tool_icons_toolbars->get_count()) { dbg->warning("tool_t::read_menu()", "wrong icon (%i) given for toolbar_tool[%i][%i]", icon, i, j); icon = 0; } icon = skinverwaltung_t::tool_icons_toolbars->get_image_id(icon); } while (*str && *str != ',') { str++; } } } // key if (*str == ',') { str++; while (*str == ' ' && *str) { str++; } if (*str != ',' && *str) { key_str = str; } while (*str != ',' && *str) { str++; } } // parameter if (*str == ',') { str++; if (*str >= ' ') { param_str = str; } } bool create_tool = icon != IMG_EMPTY || key_str || param_str; if (char const* const c = strstart(toolname, "general_tool[")) { uint8 toolnr = atoi(c); if (toolnr < GENERAL_TOOL_COUNT) { if (create_tool) { // compatibility mode: tool_cityroad is used for tool_wegebau with defaultparam 'cityroad' if (toolnr == TOOL_BUILD_WAY && param_str && strcmp(param_str, "city_road") == 0) { toolnr = TOOL_BUILD_CITYROAD; dbg->warning("tool_t::read_menu()", "toolbar[%i][%i]: replaced way-builder(id=14) with default param=cityroad by cityroad builder(id=36)", i, j); } // now create tool addtool = create_general_tool(toolnr); // copy defaults *addtool = *(general_tool[toolnr]); set_defaults_general_tool(addtool, param_str); general_tool.append(addtool); } else { addtool = general_tool[toolnr]; } } else { dbg->warning("tool_t::read_menu()", "When parsing menuconf.tab: General tool id is not valid (%hhu >= %i). Tool ignored.", toolnr, (int)GENERAL_TOOL_COUNT); } } else if (char const* const c = strstart(toolname, "simple_tool[")) { uint8 const toolnr = atoi(c); if (toolnr < SIMPLE_TOOL_COUNT) { if (create_tool) { addtool = create_simple_tool(toolnr); *addtool = *(simple_tool[toolnr]); simple_tool.append(addtool); } else { addtool = simple_tool[toolnr]; } } else { dbg->warning("tool_t::read_menu()", "When parsing menuconf.tab: Simple tool id is not valid (%hhu >= %i). Tool ignored.", toolnr, (int)SIMPLE_TOOL_COUNT); } } else if (char const* const c = strstart(toolname, "dialog_tool[")) { uint8 const toolnr = atoi(c); if (toolnr < DIALOGE_TOOL_COUNT) { if (create_tool) { addtool = create_dialog_tool(toolnr); *addtool = *(dialog_tool[toolnr]); dialog_tool.append(addtool); } else { addtool = dialog_tool[toolnr]; } } else { dbg->warning("tool_t::read_menu()", "When parsing menuconf.tab: Dialog tool id is not valid (%hhu >= %i). Tool ignored.", toolnr, (int)DIALOGE_TOOL_COUNT); } } else if (char const* const c = strstart(toolname, "toolbar[")) { uint8 const toolnr = atoi(c); if (toolnr == 0) { if (strstr(c, "LAST_USED")) { toolbar_last_used_t::last_used_tools->icon = icon; addtool = toolbar_last_used_t::last_used_tools; } else { dbg->error("Error in menuconf: toolbar cannot call main toolbar", "%s", toolname); return false; } } if (toolbar_tool.get_count() == toolnr) { if (param_str == NULL) { param_str = "Unnamed toolbar"; dbg->warning("tool_t::read_menu()", "Missing title for toolbar[%hhu]", toolnr); } char* c = strdup(param_str); const char* title = c; c += strcspn(c, ","); if (*c != '\0') { *c++ = '\0'; } toolbar_t* const tb = new toolbar_t(toolbar_tool.get_count() | TOOLBAR_TOOL, title, c); toolbar_tool.append(tb); addtool = tb; } } else { // make a default tool to add the parameter here addtool = new tool_dummy_t(); addtool->default_param = strdup(toolname); addtool->command_key = 1; } if (addtool) { if (icon != IMG_EMPTY) { addtool->icon = icon; } if (key_str != NULL) { addtool->command_key = str_to_key(key_str, &(addtool->command_flags)); char_to_tool.append(addtool); } if (param_str != NULL && ((addtool->get_id() & TOOLBAR_TOOL) == 0)) { addtool->default_param = strdup(param_str); } toolbar_tool[i]->append(addtool); } } } toolbar_tool.append(toolbar_last_used_t::last_used_tools); // sort characters std::sort(char_to_tool.begin(), char_to_tool.end(), compare_tool); return true; } void tool_t::update_toolbars() { // renew toolbar // iterate twice, to get correct icons if a toolbar changes between empty and non-empty for (uint j = 0; j < 2; j++) { bool change = false; for (toolbar_t* const i : toolbar_tool) { bool old_icon_empty = i->get_icon(welt->get_active_player()) == IMG_EMPTY; i->update(welt->get_active_player()); change |= old_icon_empty ^ (i->get_icon(welt->get_active_player()) == IMG_EMPTY); } if (!change) { // no toolbar changes between empty and non-empty, no need to loop again break; } } } void tool_t::draw_after(scr_coord pos, bool dirty) const { // default action: grey corner if selected image_id id = get_icon(welt->get_active_player()); if (id != IMG_EMPTY && is_selected()) { gfx->draw_img_blend(id, pos.x, pos.y, TRANSPARENT50_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_BLACK), false, dirty); } } bool tool_t::is_selected() const { return welt->get_tool(welt->get_active_player_nr()) == this; } const char* tool_t::check_pos(player_t*, koord3d pos) { grund_t* gr = welt->lookup(pos); return (gr && !gr->is_visible()) ? "" : NULL; } const char *tool_t::work(player_t *, koord3d) { return NULL; } const char *tool_t::move(player_t *, uint16_t, koord3d) { return ""; } bool tool_t::check_valid_pos(koord k) const { if (is_grid_tool()) { return welt->is_within_grid_limits(k); } return welt->is_within_limits(k); }; /** * Initializes cursor object: image, y-offset, size of marked area, * has to be called after init(). * @param zeiger cursor object */ void tool_t::init_cursor(zeiger_t* zeiger) const { zeiger->set_image(cursor); zeiger->set_yoff(offset); zeiger->set_area(cursor_area, cursor_centered, cursor_offset); } const char* kartenboden_tool_t::check_pos(player_t*, koord3d pos) { grund_t* gr = welt->lookup_kartenboden(pos.get_2d()); return (gr && !gr->is_visible()) ? "" : NULL; } image_id toolbar_t::get_icon(player_t* player) const { // no image for edit tools => do not open if (icon == IMG_EMPTY || (player != NULL && strcmp(default_param, "EDITTOOLS") == 0 && player->get_player_nr() != PLAYER_PUBLIC_NR)) { return IMG_EMPTY; } // now have we a least one visible tool? if (tool_selector && !tool_selector->empty(player)) { return icon; } return IMG_EMPTY; } // simply true, if visible bool toolbar_t::is_selected() const { return win_get_magic(magic_toolbar + toolbar_tool.index_of(const_cast(this))); } // just returns sound info after bracket static sint16 get_sound(const char* c) { while (*c && *c != ')') { c++; } while (*c && *c != ',') { c++; } return (*c ? atoi(c + 1) - 2 : NO_SOUND); } // fills and displays a toolbar void toolbar_t::update(player_t* player) { const bool create = (tool_selector == NULL); if (create) { DBG_MESSAGE("toolbar_t::update()", "create toolbar %s", default_param); tool_selector = new tool_selector_t(default_param, helpfile, toolbar_tool.index_of(this), this != tool_t::toolbar_tool[0]); } else { DBG_MESSAGE("toolbar_t::update()", "update toolbar %s", default_param); } tool_selector->reset_tools(); // now (re)fill it for (tool_t* const w : tools) { // no way to call this tool? => then it is most likely a metatool if (w->command_key == 1 && w->get_icon(player) == IMG_EMPTY) { if (char const* const param = w->get_default_param()) { if (create) { DBG_DEBUG("toolbar_t::update()", "add metatool (param=%s)", param); } if (char const* c = strstart(param, "ways(")) { waytype_t way = (waytype_t)atoi(c); while (*c && *c != ',' && *c != ')') { c++; } systemtype_t subtype = (systemtype_t)(*c != 0 ? atoi(++c) : 0); way_builder_t::fill_menu(tool_selector, way, subtype, get_sound(c)); } else if (char const* const c = strstart(param, "bridges(")) { waytype_t const way = (waytype_t)atoi(c); bridge_builder_t::fill_menu(tool_selector, way, get_sound(c)); } else if (char const* const c = strstart(param, "tunnels(")) { waytype_t const way = (waytype_t)atoi(c); tunnel_builder_t::fill_menu(tool_selector, way, get_sound(c)); } else if (char const* const c = strstart(param, "signs(")) { waytype_t const way = (waytype_t)atoi(c); roadsign_t::fill_menu(tool_selector, way, get_sound(c)); } else if (char const* const c = strstart(param, "wayobjs(")) { waytype_t const way = (waytype_t)atoi(c); wayobj_t::fill_menu(tool_selector, way, get_sound(c)); } else if (char const* c = strstart(param, "buildings(")) { building_desc_t::btype const utype = (building_desc_t::btype)atoi(c); while (*c && *c != ',' && *c != ')') { c++; } waytype_t way = (waytype_t)(*c != 0 ? atoi(++c) : 0); hausbauer_t::fill_menu(tool_selector, utype, way, get_sound(c)); } else if (char const* const c = strstart(param, "scripts(")) { const char* end = strchr(c, '\0'); char buf[1000]; size_t len = end ? min(lengthof(buf) - 1, end - c) : lengthof(buf) - 1; tstrncpy(buf, c, len); script_tool_manager_t::fill_menu(tool_selector, buf, get_sound(c)); } else if (param[0] == '-') { // add dummy tool_t as seperator tool_selector->add_tool_selector(dummy); } } } else if (w->get_icon(player) != IMG_EMPTY) { // get the right city_road if (w->get_id() == (TOOL_BUILD_CITYROAD | GENERAL_TOOL)) { w->flags = 0; w->init(player); } if (create) { DBG_DEBUG("toolbar_t::update()", "add tool %i (param=%s)", w->get_id(), w->get_default_param()); } scenario_t* scen = welt->get_scenario(); if (scen->is_scripted() && !scen->is_tool_allowed(player, w->get_id(), w->get_waytype(), w->get_default_param())) { continue; } if (!check_tool_availability(w, welt->get_timeline_year_month())) { continue; } w->enabled = welt->get_scenario()->is_tool_enabled(welt->get_active_player(), w->get_id(), w->get_waytype(), w->get_default_param()); // now add it to the toolbar gui tool_selector->add_tool_selector(w); } } if ((strcmp(this->default_param, "EDITTOOLS") == 0 && player != welt->get_public_player())) { destroy_win(tool_selector); return; } } // fills and displays a toolbar bool toolbar_t::init(player_t* player) { update(player); bool close = (strcmp(this->default_param, "EDITTOOLS") == 0 && player != welt->get_public_player()); // show/create window if (close) { destroy_win(tool_selector); return false; } if (this != tool_t::toolbar_tool[0]) { if (env_t::single_toolbar_mode) { for (uint16 i = 1; i < toolbar_tool.get_count(); i++) { if (this != toolbar_tool[i]) { // make sure only one tool is visibile destroy_win(magic_toolbar + i); } } // open toolbar centered at top or bottom position (bottom, when menubar at bottom) const scr_size screen = gfx->get_screen_size(); scr_coord_val w = screen.w - (env_t::menupos == MENU_LEFT ? env_t::iconsize.w : 0) - (env_t::menupos == MENU_RIGHT ? env_t::iconsize.w : 0); scr_coord_val x = (w - tool_selector->get_windowsize().w) / 2 + (env_t::menupos == MENU_LEFT ? env_t::iconsize.w : 0); scr_coord_val y = (env_t::menupos == MENU_BOTTOM ? screen.h - tool_selector->get_windowsize().h - env_t::iconsize.h : 0); create_win({ x, y }, tool_selector, w_do_not_delete, magic_toolbar + toolbar_tool.index_of(this)); } else { // not main menu => open random create_win(tool_selector, w_info | w_do_not_delete | w_no_overlap, magic_toolbar + toolbar_tool.index_of(this)); } DBG_MESSAGE("toolbar_t::init()", "ID=%id", get_id()); } return false; } bool toolbar_t::exit(player_t*) { if (win_get_magic(magic_toolbar + toolbar_tool.index_of(this))) { destroy_win(tool_selector); } return false; } // from here on last used toolbar tools (for each player!) void toolbar_last_used_t::update(player_t* sp) { tools.clear(); if (sp) { for (slist_tpl::iterator iter = all_tools[sp->get_player_nr()].begin(); iter != all_tools[sp->get_player_nr()].end(); ++iter) { tools.append(*iter); } } toolbar_t::update(sp); } void toolbar_last_used_t::clear() { for (int i = 0; i < MAX_PLAYER_COUNT; i++) { all_tools[i].clear(); } tools.clear(); } // currently only needed for last used tools void toolbar_last_used_t::append(tool_t* t, player_t* sp) { static int exclude_from_adding[9] = { TOOL_SCHEDULE_ADD | GENERAL_TOOL, TOOL_SCHEDULE_INS | GENERAL_TOOL, TOOL_CHANGE_CONVOI | SIMPLE_TOOL, TOOL_CHANGE_LINE | SIMPLE_TOOL, TOOL_CHANGE_DEPOT | SIMPLE_TOOL, UNUSED_WKZ_PWDHASH_TOOL | SIMPLE_TOOL, TOOL_CHANGE_PLAYER | SIMPLE_TOOL, TOOL_RENAME | SIMPLE_TOOL, TOOL_HALT_PERMISSION | SIMPLE_TOOL }; if (!sp || t->get_icon(sp) == IMG_EMPTY) { return; } // do not add certain tools for (uint i = 0; i < lengthof(exclude_from_adding); i++) { if (t->get_id() == exclude_from_adding[i]) { return; } } slist_tpl& players_tools = all_tools[sp->get_player_nr()]; if (players_tools.is_contained(t)) { players_tools.remove(t); } else { while (players_tools.get_count() >= MAX_LAST_TOOLS) { players_tools.remove(players_tools.back()); } } players_tools.insert(t); // if current => update if (sp == world()->get_active_player()) { update(sp); } } bool two_click_tool_t::init(player_t*) { first_click_var = true; start = koord3d::invalid; if (is_local_execution()) { welt->show_distance = koord3d::invalid; } cleanup(true); return true; } void two_click_tool_t::rdwr_custom_data(memory_rw_t* packet) { packet->rdwr_bool(first_click_var); sint16 posx = start.x; packet->rdwr_short(posx); start.x = posx; sint16 posy = start.y; packet->rdwr_short(posy); start.y = posy; sint8 posz = start.z; packet->rdwr_byte(posz); start.z = posz; } bool two_click_tool_t::is_first_click() const { return first_click_var; } bool two_click_tool_t::is_work_here_keeps_game_state(player_t* player, koord3d pos) { if (!is_first_click()) { return false; } const char* error = ""; //default: nosound uint8 value = is_valid_pos(player, pos, error, koord3d::invalid); DBG_MESSAGE("two_click_tool_t::is_work_here_keeps_game_state", "Position %s valid=%d", pos.get_str(), value); if (value == 0) { // cannot work here at all -> safe return true; } // work directly if possible and ctrl is NOT pressed if ((value & 1) && !((value & 2) && is_ctrl_pressed())) { // would work here directly. return false; } else { // set starting position only return true; } } const char* two_click_tool_t::work(player_t* player, koord3d pos) { if (!is_first_click() && start_marker) { start = start_marker->get_pos(); // if map was rotated. } // remove marker cleanup(true); const char* error = NULL; uint8 value = is_valid_pos(player, pos, error, !is_first_click() ? start : koord3d::invalid); DBG_MESSAGE("two_click_tool_t::work", "Position %s valid=%d", pos.get_str(), value); if (value == 0) { if (error == NULL) { error = ""; // propagate errors } flags &= ~(WFL_SHIFT | WFL_CTRL); init(player); return error; } if (is_first_click()) { // work directly if possible and ctrl is NOT pressed if ((value & 1) && !((value & 2) && is_ctrl_pressed())) { // Work here directly. DBG_MESSAGE("two_click_tool_t::work", "Call tool at %s", pos.get_str()); error = do_work(player, pos, koord3d::invalid); } else { // set starting position. DBG_MESSAGE("two_click_tool_t::work", "Setting start to %s", pos.get_str()); start_at(pos); error = NULL; } } else { if (value & 2) { DBG_MESSAGE("two_click_tool_t::work", "Setting end to %s", pos.get_str()); error = do_work(player, start, pos); } flags &= ~(WFL_SHIFT | WFL_CTRL); init(player); // Do the cleanup stuff after(!) do_work (otherwise start==koord3d::invalid). } return error; } const char* two_click_tool_t::move(player_t* player, uint16 buttonstate, koord3d pos) { DBG_MESSAGE("two_click_tool_t::move", "Button: %d, Pos: %s", buttonstate, pos.get_str()); if (buttonstate == 0) { return ""; } if (start == pos) { init(player); } const char* error = NULL; if (start == koord3d::invalid) { // start dragging. cleanup(true); uint8 value = is_valid_pos(player, pos, error, koord3d::invalid); if (error || value == 0) { return error; } if (value & 2) { start_at(pos); } } else { // continue dragging. cleanup(false); if (start_marker) { start = start_marker->get_pos(); // if map was rotated. } uint8 value = is_valid_pos(player, pos, error, start); if (error || value == 0) { return error; } if (value & 2) { gfx->set_show_load_cursor(true); mark_tiles(player, start, pos); gfx->set_show_load_cursor(false); } } return ""; } void two_click_tool_t::start_at(koord3d& new_start) { first_click_var = false; start = new_start; if (is_local_execution()) { welt->show_distance = new_start; start_marker = new zeiger_t(start, NULL); start_marker->set_image(get_marker_image()); grund_t* gr = welt->lookup(start); if (gr) { gr->obj_add(start_marker); } } DBG_MESSAGE("two_click_tool_t::start_at", "Setting start to %s", start.get_str()); } void two_click_tool_t::cleanup(bool delete_start_marker) { // delete marker. if (start_marker != NULL && delete_start_marker) { start_marker->mark_image_dirty(start_marker->get_image(), 0); delete start_marker; start_marker = NULL; } // delete old route. while (!marked.empty()) { zeiger_t* z = marked.remove_first(); z->mark_image_dirty(z->get_image(), 0); z->mark_image_dirty(z->get_front_image(), 0); koord3d pos = z->get_pos(); grund_t* gr = welt->lookup(pos); delete z; // Remove dummy ground (placed by tool_build_tunnel_t and tool_build_way_t): if (gr && gr->is_dummy_ground()) { welt->access(pos.get_2d())->boden_entfernen(gr); delete gr; assert(!welt->lookup(pos)); } } // delete tooltip. win_set_static_tooltip(NULL); } image_id two_click_tool_t::get_marker_image() const { return skinverwaltung_t::bauigelsymbol->get_image_id(0); } simutrans-124.5/src/simutrans/tool/simmenu.h000066400000000000000000000325461520675460500212150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TOOL_SIMMENU_H #define TOOL_SIMMENU_H #include #include "../descriptor/sound_desc.h" #include "../dataobj/koord3d.h" #include "../dataobj/translator.h" #include "../simtypes.h" #include "../display/simimg.h" /// New configurable OOP tool system template class vector_tpl; template class slist_tpl; class scr_coord; class tool_selector_t; class player_t; class toolbar_t; class memory_rw_t; class karte_ptr_t; class zeiger_t; enum { // general tools (one click) TOOL_QUERY=0, TOOL_REMOVER, TOOL_RAISE_LAND, TOOL_LOWER_LAND, TOOL_SETSLOPE, TOOL_RESTORESLOPE, TOOL_MARKER, TOOL_CLEAR_RESERVATION, TOOL_TRANSFORMER, TOOL_ADD_CITY, TOOL_CHANGE_CITY_SIZE, TOOL_PLANT_TREE, TOOL_SCHEDULE_ADD, TOOL_SCHEDULE_INS, TOOL_BUILD_WAY, TOOL_BUILD_BRIDGE, TOOL_BUILD_TUNNEL, TOOL_WAYREMOVER, TOOL_BUILD_WAYOBJ, TOOL_BUILD_STATION, TOOL_BUILD_ROADSIGN, TOOL_BUILD_DEPOT, TOOL_BUILD_HOUSE, TOOL_BUILD_LAND_CHAIN, TOOL_CITY_CHAIN, TOOL_BUILD_FACTORY, TOOL_LINK_FACTORY, TOOL_HEADQUARTER, TOOL_LOCK_GAME, TOOL_ADD_CITYCAR, TOOL_FOREST, TOOL_STOP_MOVER, TOOL_MAKE_STOP_PUBLIC, TOOL_REMOVE_WAYOBJ, TOOL_SLICED_AND_UNDERGROUND_VIEW, TOOL_BUY_HOUSE, TOOL_BUILD_CITYROAD, TOOL_ERROR_MESSAGE, TOOL_CHANGE_WATER_HEIGHT, TOOL_SET_CLIMATE, TOOL_ROTATE_BUILDING, TOOL_MERGE_STOP, TOOL_EXEC_SCRIPT, TOOL_EXEC_TWO_CLICK_SCRIPT, TOOL_PLANT_GROUNDOBJ, TOOL_ADD_MESSAGE, TOOL_REMOVE_SIGNAL, TOOL_GENERATE_SCRIPT, TOOL_PIPETTE, TOOL_SET_OWNER, GENERAL_TOOL_COUNT, GENERAL_TOOL = 0x1000 }; enum { // simple no click tools TOOL_PAUSE = 0, TOOL_FASTFORWARD, TOOL_SCREENSHOT, TOOL_INCREASE_INDUSTRY, TOOL_UNDO, TOOL_SWITCH_PLAYER, TOOL_STEP_YEAR, TOOL_CHANGE_GAME_SPEED, TOOL_ZOOM_IN, TOOL_ZOOM_OUT, TOOL_SHOW_COVERAGE, TOOL_SHOW_NAME, TOOL_SHOW_GRID, TOOL_SHOW_TREES, TOOL_SHOW_HOUSES, TOOL_SHOW_UNDERGROUND, TOOL_ROTATE90, TOOL_QUIT, TOOL_FILL_TREES, TOOL_DAYNIGHT_LEVEL, TOOL_VEHICLE_TOOLTIPS, TOOL_TOOGLE_PAX, TOOL_TOOGLE_PEDESTRIANS, TOOL_TRAFFIC_LEVEL, TOOL_CHANGE_CONVOI, TOOL_CHANGE_LINE, TOOL_CHANGE_DEPOT, UNUSED_WKZ_PWDHASH_TOOL, TOOL_CHANGE_PLAYER, TOOL_CHANGE_TRAFFIC_LIGHT, TOOL_CHANGE_CITY, TOOL_RENAME, UNUSED_TOOL_ADD_MESSAGE, TOOL_TOGGLE_RESERVATION, TOOL_VIEW_OWNER, TOOL_HIDE_UNDER_CURSOR, TOOL_MOVE_MAP, TOOL_ROLLUP_ALL_WIN, TOOL_RECOLOUR_TOOL, TOOL_SHOW_FACTORY_STORAGE, TOOL_TOGGLE_CONTROL, TOOL_LOAD_SCENARIO, TOOL_DAY_NIGHT_TOGGLE, TOOL_SINGLE_WAY_TOOGLE, TOOL_WORK_WORLD, TOOL_HALT_PERMISSION, SIMPLE_TOOL_COUNT, SIMPLE_TOOL = 0x2000 }; enum { // dialogue tools DIALOG_HELP = 0, DIALOG_OPTIONS, DIALOG_MINIMAP, DIALOG_LINEOVERVIEW, DIALOG_MESSAGES, DIALOG_FINANCES, DIALOG_PLAYERS, DIALOG_DISPLAYOPTIONS, DIALOG_SOUND, DIALOG_LANGUAGE, DIALOG_PLAYERCOLOR, DIALOG_JUMP, DIALOG_LOAD, DIALOG_SAVE, DIALOG_LIST_HALT, DIALOG_LIST_CONVOI, DIALOG_LIST_TOWN, DIALOG_LIST_GOODS, DIALOG_LIST_FACTORY, DIALOG_LIST_CURIOSITY, DIALOG_EDIT_FACTORY, DIALOG_EDIT_ATTRACTION, DIALOG_EDIT_HOUSE, DIALOG_EDIT_TREE, DIALOG_ENLARGE_MAP, DIALOG_LIST_LABEL, DIALOG_CLIMATES, DIALOG_SETTINGS, DIALOG_GAMEINFO, DIALOG_THEMES, DIALOG_SCENARIO, DIALOG_SCENARIO_INFO, DIALOG_LIST_DEPOT, DIALOG_LIST_VEHICLE, DIALOG_SCRIPT_TOOL, DIALOG_EDIT_GROUNDOBJ, DIALOG_CHAT, DIALOG_PLAYER_RANKING, DIALOGE_TOOL_COUNT, DIALOGE_TOOL = 0x4000 }; enum { // toolbars TOOL_MAINMENU = 0, TOOL_LAST_USED = 1022, TOOLBAR_TOOL = 0x8000u }; class tool_t { protected: image_id icon; private: /* value to trigger this command (see documentation) */ uint16 id; protected: static karte_ptr_t welt; const char *default_param; public: uint16 get_id() const { return id; } const char *get_name() const { return id_to_string(id); } static const char *id_to_string(uint16 id); static tool_t *dummy; // for key lookup static vector_tplchar_to_tool; // true, if the control key should be inverted static uint8 control_invert; /// disabled icons are greyed out bool enabled; /// cursor centered at marked area? default: false bool cursor_centered; /// cursor image image_id cursor; /// cursor marks this area koord cursor_area; /// cursor offset within marked area (only effective if cursor_centered != false) koord cursor_offset; /// z-offset of cursor, possible values: Z_PLAN and Z_GRID sint8 offset; sint16 ok_sound; /// a script is waiting for a call-back uint32 callback_id; enum { WFL_SHIFT = 1 << 0, ///< shift-key was pressed when mouse-click happened WFL_CTRL = 1 << 1, ///< ctrl-key was pressed when mouse-click happened WFL_LOCAL = 1 << 2, ///< tool call was issued by local client (and will not be sent to server) WFL_SCRIPT = 1 << 3, ///< tool call was issued by script WFL_NO_CHK = 1 << 4 ///< tool call needs no password or scenario checks }; uint8 flags; // flags are set before init/work/move is called bool is_ctrl_pressed() const { return flags & WFL_CTRL; } bool is_shift_pressed() const { return flags & WFL_SHIFT; } bool is_local_execution() const { return flags & WFL_LOCAL; } bool is_scripted() const { return flags & WFL_SCRIPT; } bool no_check() const { return flags & WFL_NO_CHK; } bool can_use_gui() const { return is_local_execution() && !is_scripted(); } uint8 command_flags; // only shift and control uint16 command_key;// key to toggle action for this function static vector_tpl general_tool; static vector_tpl simple_tool; static vector_tpl dialog_tool; static vector_tpl toolbar_tool; static void update_toolbars(); // since only a single toolstr a time can be visible ... static char toolstr[1024]; static void init_menu(); static void exit_menu(); /// Read tool, toolbar configuration and tool shortcuts from @p menuconf /// @param menuconf Path to file to read static bool read_menu(const std::string &menuconf); static uint16 const dummy_id = 0xFFFFU; tool_t(uint16 const id) : id(id), cursor_area(1,1) { cursor = icon = IMG_EMPTY; ok_sound = NO_SOUND; offset = Z_PLAN; default_param = NULL; command_key = 0; cursor_centered = false; enabled = true; flags = 0; callback_id = 0; } virtual ~tool_t() {} virtual image_id get_icon(player_t *) const { return icon; } void set_icon(image_id i) { icon = i; } // returns default_param of this tool for player // if player==NULL returns default_param that was used to create the tool virtual const char* get_default_param(player_t* = NULL) const { return default_param; } void set_default_param(const char* str) { default_param = str; } // transfer additional information in networkgames virtual void rdwr_custom_data(memory_rw_t*) { } // this will draw the tool with some indication, if active virtual bool is_selected() const; // when true, local execution would do no harm virtual bool is_init_keeps_game_state() const { return false; } // if is_work_keeps_game_state()==false // and is_work_here_keeps_game_state(...)==false // then work-command is sent over network or must execute outside a sync_step virtual bool is_work_keeps_game_state() const { return false; } virtual bool is_work_here_keeps_game_state(player_t *, koord3d) { return false; } // will draw a dark frame, if selected virtual void draw_after(scr_coord pos, bool dirty) const; virtual const char *get_tooltip(const player_t *) const { return NULL; } /** * @return true if this tool operates over the grid, not the map tiles. */ virtual bool is_grid_tool() const {return false;} /** * Returning false on init will automatically invoke previous tool. * Returning true will select tool and will make it possible to call work. */ virtual bool init( player_t * ) { return true; } /// initializes cursor (icon, marked area) void init_cursor( zeiger_t * ) const; // returning true on exit will have tool_selector resets to query-tool on right-click virtual bool exit( player_t * ) { return true; } /// Checks if @p pos is suitable for applying this tool. /// @returns NULL if OK, else untranslated error message virtual const char *check_pos( player_t *pl, koord3d pos); /// Apply the tool to the world at @p pos. /// @note This function should depend on undergroundmode for not network safe tools /// @returns NULL if OK, else untranslated error message virtual const char *work( player_t *pl, koord3d pos); /// Called when the tool is dragged across the map. /// If we are in network mode, the effects of the tool must be queued, /// else the effects of the tool can just be applied directly. /// @note This function should depend on undergroundmode for not network safe tools /// @returns NULL if OK, else untranslated error message virtual const char *move( player_t *pl, uint16 buttonstate, koord3d new_pos); /** * Should be overloaded if derived class implements move, * move will only be called, if this function returns true. */ virtual bool move_has_effects() const { return false;} /** * Returns whether the 2d koordinate passed it's a valid position for this tool to highlight a tile, * just takes into account is_grid_tool. It does not check if work is allowed there, that's check_pos() work. * @see check_pos * @return true is the coordinate it's found valid, false otherwise. */ bool check_valid_pos( koord k ) const; /** * Specifies if the cursor will need a position update after this tool takes effect (ie: changed the height of the tile) * @note only used on lower_raise tools atm. * @return true if the cursor has to be moved. */ virtual bool update_pos_after_use() const { return false; } virtual waytype_t get_waytype() const { return invalid_wt; } }; /* * Class for tools that work only on ground (kartenboden) */ class kartenboden_tool_t : public tool_t { public: kartenboden_tool_t(uint16 const id) : tool_t(id) {} char const* check_pos(player_t*, koord3d) OVERRIDE; }; /** * Class for tools needing two clicks (e.g. building ways). * Dragging is also possible. */ class two_click_tool_t : public tool_t { public: two_click_tool_t(uint16 const id) : tool_t(id) { MEMZERO(start_marker); first_click_var = true; } void rdwr_custom_data(memory_rw_t*) OVERRIDE; bool init(player_t*) OVERRIDE; bool exit(player_t* const player) OVERRIDE { return init(player); } char const* work(player_t*, koord3d) OVERRIDE; char const* move(player_t*, uint16 /* buttonstate */, koord3d) OVERRIDE; bool move_has_effects() const OVERRIDE { return true; } bool is_work_here_keeps_game_state(player_t *, koord3d) OVERRIDE; /** * @returns true if cleanup() needs to be called before another tool can be executed * necessary for all tools that create dummy tiles for preview */ virtual bool remove_preview_necessary() const { return false; } bool is_first_click() const; /** * Remove dummy grounds, remove start_marker if @p delete_start_marker is true. */ void cleanup(bool delete_start_marker = true); const koord3d& get_start_pos() const { return start; } private: /* * This routine should fill marked_tiles. */ virtual void mark_tiles( player_t *, const koord3d &start, const koord3d &end ) = 0; /* * This routine is called, if the real work should be done. * If the tool supports single clicks, end is sometimes == koord3d::invalid. * Returned string is passed by work/move. */ virtual const char *do_work( player_t *, const koord3d &start, const koord3d &end ) = 0; /* * Can the tool start/end on pos? If it is the second click, start is the position of the first click * 0 = no * 1 = This tool can work on this tile (with single click) * 2 = On this tile can dragging start/end * 3 = Both (1 and 2) * error will contain an error message (if this is != NULL, return value should be 0). */ virtual uint8 is_valid_pos( player_t *, const koord3d &pos, const char *&error, const koord3d &start ) = 0; virtual image_id get_marker_image() const; bool first_click_var; koord3d start; zeiger_t *start_marker; protected: virtual void start_at( koord3d &new_start ); slist_tpl< zeiger_t* > marked; }; /* toolbar are a new overclass */ class toolbar_t : public tool_t { protected: const char *helpfile; tool_selector_t *tool_selector; slist_tpltools; public: toolbar_t(uint16 const id, char const* const t, char const* const h) : tool_t(id) { default_param = t; helpfile = h; tool_selector = NULL; } char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate(default_param); } tool_selector_t *get_tool_selector() const { return tool_selector; } image_id get_icon(player_t*) const OVERRIDE; bool is_selected() const OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } // show this toolbar bool init(player_t*) OVERRIDE; // close this toolbar bool exit(player_t*) OVERRIDE; virtual void update(player_t *); // just refresh content void append(tool_t *tool) { tools.append(tool); } }; #define MAX_LAST_TOOLS (10) class toolbar_last_used_t : public toolbar_t { private: slist_tplall_tools[MAX_PLAYER_COUNT]; public: toolbar_last_used_t(uint16 const id, char const* const t, char const* const h) : toolbar_t(id,t,h) {} static toolbar_last_used_t *last_used_tools; void update(player_t *) OVERRIDE; // just refresh content void append(tool_t *, player_t *); void clear(); }; // create new instance of tool tool_t *create_tool(int toolnr); #endif simutrans-124.5/src/simutrans/tool/simtool-dialogs.h000066400000000000000000000736321520675460500226470ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TOOL_SIMTOOL_DIALOGS_H #define TOOL_SIMTOOL_DIALOGS_H #include "../tool/simmenu.h" #include "../gui/simwin.h" #include "../dataobj/translator.h" #include "../dataobj/scenario.h" #include "../dataobj/environment.h" #include "../gui/baum_edit.h" #include "../gui/chat_frame.h" #include "../gui/citybuilding_edit.h" #include "../gui/citylist_frame.h" #include "../gui/climates.h" #include "../gui/convoi_frame.h" #include "../gui/curiosity_edit.h" #include "../gui/curiositylist_frame.h" #include "../gui/depotlist_frame.h" #include "../gui/display_settings.h" #include "../gui/enlarge_map_frame.h" #include "../gui/factory_edit.h" #include "../gui/factorylist_frame.h" #include "../gui/goods_frame.h" #include "../gui/groundobj_edit.h" #include "../gui/halt_list_frame.h" #include "../gui/help_frame.h" #include "../gui/jump_frame.h" #include "../gui/kennfarbe.h" #include "../gui/labellist_frame.h" #include "../gui/loadsave_frame.h" #include "../gui/map_frame.h" #include "../gui/message_frame.h" #include "../gui/messagebox.h" #include "../gui/money_frame.h" #include "../gui/optionen.h" #include "../gui/player_frame.h" #include "../gui/player_ranking_frame.h" #include "../gui/scenario_frame.h" #include "../gui/scenario_info.h" #include "../gui/schedule_list.h" #include "../gui/script_tool_frame.h" #include "../gui/server_frame.h" #include "../gui/settings_frame.h" #include "../gui/sound_frame.h" #include "../gui/sprachen.h" #include "../gui/themeselector.h" #include "../gui/vehiclelist_frame.h" #include "../obj/baum.h" #include "../obj/groundobj.h" #include "../sys/simsys.h" class player_t; /********************** dialog tools *****************************/ // general help class dialog_help_t : public tool_t { public: dialog_help_t() : tool_t(DIALOG_HELP | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Help"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_mainhelp); } bool init(player_t*) OVERRIDE{ help_frame_t::open_help_on("general.txt"); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_mainhelp); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open info/quit dialog class dialog_options_t : public tool_t { public: dialog_options_t() : tool_t(DIALOG_OPTIONS | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Einstellungen aendern"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_optionen_gui_t); } bool init(player_t*) OVERRIDE{ create_win({ -1, -1 }, new optionen_gui_t(), w_info, magic_optionen_gui_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_optionen_gui_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open minimap class dialog_minimap_t : public tool_t { public: dialog_minimap_t() : tool_t(DIALOG_MINIMAP | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Reliefkarte"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_reliefmap); } bool init(player_t*) OVERRIDE{ create_win(new map_frame_t(), w_info, magic_reliefmap); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_reliefmap); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open line management class dialog_lines_t : public tool_t { public: dialog_lines_t() : tool_t(DIALOG_LINEOVERVIEW | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Line Management"); } image_id get_icon(player_t* player) const OVERRIDE{ return !player || player->get_player_nr() == PLAYER_PUBLIC_NR ? IMG_EMPTY : icon; } bool is_selected() const OVERRIDE{ return win_get_magic(magic_line_management_t + welt->get_active_player_nr()); } bool init(player_t* player) OVERRIDE{ if (!player->is_public_service()) { create_win(new schedule_list_gui_t(player), w_info, magic_line_management_t + player->get_player_nr()); } return false; } bool exit(player_t* const player) OVERRIDE{ destroy_win(win_get_magic(magic_line_management_t + player->get_player_nr())); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open messages class dialog_messages_t : public tool_t { public: dialog_messages_t() : tool_t(DIALOG_MESSAGES | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Mailbox"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_messageframe); } bool init(player_t*) OVERRIDE{ create_win(new message_frame_t(), w_info, magic_messageframe); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_messageframe); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open finance window class dialog_finances_t : public tool_t { public: dialog_finances_t() : tool_t(DIALOG_FINANCES | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Finanzen"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_finances_t + welt->get_active_player_nr()); } bool init(player_t* player) OVERRIDE{ create_win(new money_frame_t(player), w_info, magic_finances_t + player->get_player_nr()); return false; } bool exit(player_t* const player) OVERRIDE{ destroy_win(magic_finances_t + player->get_player_nr()); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open player dialog class dialog_players_t : public tool_t { public: dialog_players_t() : tool_t(DIALOG_PLAYERS | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Spielerliste"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_ki_kontroll_t); } bool init(player_t*) OVERRIDE{ create_win({ 272, 160 }, new ki_kontroll_t(), w_info, magic_ki_kontroll_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_ki_kontroll_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open display options class dialog_displayoptions_t : public tool_t { public: dialog_displayoptions_t() : tool_t(DIALOG_DISPLAYOPTIONS | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Display settings"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_color_gui_t); } bool init(player_t*) OVERRIDE{ create_win(new color_gui_t(), w_info, magic_color_gui_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_color_gui_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open sound dialog class dialog_sound_t : public tool_t { public: dialog_sound_t() : tool_t(DIALOG_SOUND | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Sound"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_sound_kontroll_t); } bool init(player_t*) OVERRIDE{ create_win(new sound_frame_t(), w_info, magic_sound_kontroll_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_sound_kontroll_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open language dialog class dialog_language_t : public tool_t { public: dialog_language_t() : tool_t(DIALOG_LANGUAGE | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Sprache"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_sprachengui_t); } bool init(player_t*) OVERRIDE{ create_win(new sprachengui_t(), w_info, magic_sprachengui_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_sprachengui_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open player color dialog class dialog_playercolor_t : public tool_t { public: dialog_playercolor_t() : tool_t(DIALOG_PLAYERCOLOR | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Farbe"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_farbengui_t); } bool init(player_t* player) OVERRIDE{ create_win(new farbengui_t(player), w_info, magic_farbengui_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_farbengui_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // jump to position dialog class dialog_jump_t : public tool_t { public: dialog_jump_t() : tool_t(DIALOG_JUMP | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Jump to"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_jump); } bool init(player_t*) OVERRIDE{ create_win(new jump_frame_t(), w_info, magic_jump); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_jump); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // load game dialog class dialog_load_t : public tool_t { public: dialog_load_t() : tool_t(DIALOG_LOAD | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Laden"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_load_t); } bool init(player_t*) OVERRIDE{ if( !env_t::server ) { dr_chdir(env_t::user_dir); create_win(new loadsave_frame_t(true), w_info, magic_load_t); } else { destroy_win(magic_save_t); create_win( new loadsave_frame_t(true), w_info, magic_load_t); const scr_coord pos = win_get_pos( win_get_magic(magic_load_t) ); create_win( pos + scr_coord{ 20, 20 }, new news_img("Loading a new game will end the current server session!"), w_no_overlap, magic_none); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_load_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } }; // save game dialog class dialog_save_t : public tool_t { public: dialog_save_t() : tool_t(DIALOG_SAVE | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Speichern"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_save_t); } bool init(player_t*) OVERRIDE{ dr_chdir(env_t::user_dir); create_win(new loadsave_frame_t(false), w_info, magic_save_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_save_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } }; // open scripted-tools dialog class dialog_script_tool_t : public tool_t { public: dialog_script_tool_t() : tool_t(DIALOG_SCRIPT_TOOL | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Load tool script"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_load_script); } bool init(player_t*) OVERRIDE{ destroy_win(magic_save_t); create_win( new script_tool_frame_t(), w_info, magic_load_script ); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_load_script); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } }; // open scenario dialog class dialog_scenario_t : public tool_t { public: dialog_scenario_t() : tool_t(DIALOG_SCENARIO | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Load scenario"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_load_scenario); } bool init(player_t*) OVERRIDE{ destroy_win(magic_save_t); if( !env_t::server ) { create_win( new scenario_frame_t(), w_info, magic_load_scenario ); } else { create_win( new scenario_frame_t(), w_info, magic_load_scenario ); const scr_coord pos = win_get_pos( win_get_magic(magic_load_scenario) ); create_win( pos + scr_coord{ 20, 20 }, new news_img("Loading a new game will end the current server session!"), w_no_overlap, magic_none); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_load_scenario); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } }; // open scenario info dialog class dialog_scenario_info_t : public tool_t { public: dialog_scenario_info_t() : tool_t(DIALOG_SCENARIO_INFO | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Scenario"); } image_id get_icon(player_t *) const OVERRIDE { return world()->get_scenario()->is_scripted() ? icon : IMG_EMPTY; } bool is_selected() const OVERRIDE{ return win_get_magic(magic_scenario_info); } bool init(player_t*) OVERRIDE{ create_win( new scenario_info_t(), w_info, magic_scenario_info ); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_scenario_info); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } }; /* open the list of halt */ class dialog_list_halt_t : public tool_t { public: dialog_list_halt_t() : tool_t(DIALOG_LIST_HALT | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("hl_title"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_halt_list + welt->get_active_player_nr()); } bool init(player_t* player) OVERRIDE{ create_win(new halt_list_frame_t(), w_info, magic_halt_list + player->get_player_nr()); return false; } bool exit(player_t* player) OVERRIDE{ destroy_win(magic_halt_list + player->get_player_nr()); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open the list of vehicle */ class dialog_list_convoi_t : public tool_t { public: dialog_list_convoi_t() : tool_t(DIALOG_LIST_CONVOI | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("cl_title"); } image_id get_icon(player_t*) const OVERRIDE{ return welt->get_active_player_nr() == PLAYER_PUBLIC_NR ? IMG_EMPTY : icon; } bool is_selected() const OVERRIDE{ return win_get_magic(magic_convoi_list + welt->get_active_player_nr()); } bool init(player_t* player) OVERRIDE{ create_win(new convoi_frame_t(), w_info, magic_convoi_list + player->get_player_nr()); return false; } bool exit(player_t* const player) OVERRIDE{ destroy_win(magic_convoi_list + player->get_player_nr()); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open the list of depots */ class dialog_list_depot_t : public tool_t { public: dialog_list_depot_t() : tool_t(DIALOG_LIST_DEPOT | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("dp_title"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_depotlist + welt->get_active_player_nr()); } image_id get_icon(player_t*) const OVERRIDE{ return welt->get_active_player_nr() == PLAYER_PUBLIC_NR ? IMG_EMPTY : icon; } bool init(player_t* player) OVERRIDE{ create_win(new depotlist_frame_t(player), w_info, magic_depotlist + player->get_player_nr()); return false; } bool exit(player_t* player) OVERRIDE{ destroy_win(magic_depotlist + player->get_player_nr()); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open the list of vehicles */ class dialog_list_vehicle_t : public tool_t { public: dialog_list_vehicle_t() : tool_t(DIALOG_LIST_VEHICLE | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("vh_title"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_vehiclelist); } bool init(player_t*) OVERRIDE{ create_win(new vehiclelist_frame_t(), w_info, magic_vehiclelist); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_vehiclelist); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open the list of towns */ class dialog_list_town_t : public tool_t { public: dialog_list_town_t() : tool_t(DIALOG_LIST_TOWN | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("tl_title"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_citylist_frame_t); } bool init(player_t*) OVERRIDE{ create_win(new citylist_frame_t(), w_info, magic_citylist_frame_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_citylist_frame_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open the list of goods */ class dialog_list_goods_t : public tool_t { public: dialog_list_goods_t() : tool_t(DIALOG_LIST_GOODS | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("gl_title"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_goodslist); } bool init(player_t*) OVERRIDE{ create_win(new goods_frame_t(), w_info, magic_goodslist); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_goodslist); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open the list of factories */ class dialog_list_factory_t : public tool_t { public: dialog_list_factory_t() : tool_t(DIALOG_LIST_FACTORY | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("fl_title"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_factorylist); } bool init(player_t*) OVERRIDE{ create_win(new factorylist_frame_t(), w_info, magic_factorylist); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_factorylist); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open the list of attraction */ class dialog_list_curiosity_t : public tool_t { public: dialog_list_curiosity_t() : tool_t(DIALOG_LIST_CURIOSITY | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("curlist_title"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_curiositylist); } bool init(player_t*) OVERRIDE{ create_win(new curiositylist_frame_t(), w_info, magic_curiositylist); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_curiositylist); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* factory building dialog */ class dialog_edit_factory_t : public tool_t { public: dialog_edit_factory_t() : tool_t(DIALOG_EDIT_FACTORY | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("factorybuilder"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_edit_factory); } bool init(player_t* player) OVERRIDE{ if (!is_selected()) { create_win(new factory_edit_frame_t(player), w_info, magic_edit_factory); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_edit_factory); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* attraction building dialog */ class dialog_edit_attraction_t : public tool_t { public: dialog_edit_attraction_t() : tool_t(DIALOG_EDIT_ATTRACTION | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("curiosity builder"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_edit_attraction); } bool init(player_t* player) OVERRIDE{ if (!is_selected()) { create_win(new curiosity_edit_frame_t(player), w_info, magic_edit_attraction); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_edit_attraction); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* house building dialog */ class dialog_edit_house_t : public tool_t { public: dialog_edit_house_t() : tool_t(DIALOG_EDIT_HOUSE | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("citybuilding builder"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_edit_house); } bool init(player_t* player) OVERRIDE{ if (!is_selected()) { create_win(new citybuilding_edit_frame_t(player), w_info, magic_edit_house); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_edit_house); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* tree placing dialog */ class dialog_edit_tree_t : public tool_t { public: dialog_edit_tree_t() : tool_t(DIALOG_EDIT_TREE | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("baum builder"); } image_id get_icon(player_t *) const OVERRIDE { return tree_builder_t::has_trees() ? icon : IMG_EMPTY; } bool is_selected() const OVERRIDE{ return win_get_magic(magic_edit_tree); } bool init(player_t* player) OVERRIDE{ if( tree_builder_t::has_trees() && !is_selected() ) { create_win(new baum_edit_frame_t(player), w_info, magic_edit_tree); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_edit_tree); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* groundobj placing dialog */ class dialog_edit_groundobj_t : public tool_t { public: dialog_edit_groundobj_t() : tool_t(DIALOG_EDIT_GROUNDOBJ | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("groundobj builder"); } image_id get_icon(player_t *) const OVERRIDE { return groundobj_t::get_count() > 0 ? icon : IMG_EMPTY; } bool is_selected() const OVERRIDE{ return win_get_magic(magic_edit_groundobj); } bool init(player_t* player) OVERRIDE{ if (!is_selected()) { create_win(new groundobj_edit_frame_t(player), w_info, magic_edit_groundobj); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_edit_groundobj); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // to increase map-size class dialog_enlarge_map_t : public tool_t{ public: dialog_enlarge_map_t() : tool_t(DIALOG_ENLARGE_MAP | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return env_t::networkmode ? translator::translate("deactivated in online mode") : translator::translate("enlarge map"); } image_id get_icon(player_t *) const OVERRIDE { return env_t::networkmode ? IMG_EMPTY : icon; } bool is_selected() const OVERRIDE{ return win_get_magic(magic_bigger_map); } bool init(player_t*) OVERRIDE{ if (!env_t::networkmode) { destroy_all_win(true); create_win(new enlarge_map_frame_t(), w_info, magic_bigger_map); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_bigger_map); return false; } }; /* open the list of label */ class dialog_list_label_t : public tool_t { public: dialog_list_label_t() : tool_t(DIALOG_LIST_LABEL | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("labellist_title"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_labellist); } bool init(player_t*) OVERRIDE{ create_win(new labellist_frame_t(), w_info, magic_labellist); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_labellist); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open climate settings */ class dialog_climates_t : public tool_t { public: dialog_climates_t() : tool_t(DIALOG_CLIMATES | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return (!env_t::networkmode || env_t::server) ? translator::translate("Climate Control") : translator::translate("deactivated in online mode"); } image_id get_icon(player_t *) const OVERRIDE { return (!env_t::networkmode || env_t::server) ? icon : IMG_EMPTY; } bool is_selected() const OVERRIDE{ return win_get_magic(magic_climate); } bool init(player_t*) OVERRIDE{ if (!env_t::networkmode || env_t::server) { create_win(new climate_gui_t(&welt->get_settings()), w_info, magic_climate); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_climate); return false; } }; /* open all game settings */ class dialog_settings_t : public tool_t { public: dialog_settings_t() : tool_t(DIALOG_SETTINGS | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return (!env_t::networkmode || env_t::server) ? translator::translate("Setting") : translator::translate("deactivated in online mode"); } image_id get_icon(player_t *) const OVERRIDE { return (!env_t::networkmode || env_t::server) ? icon : IMG_EMPTY; } bool is_selected() const OVERRIDE{ return win_get_magic(magic_settings_frame_t); } bool init(player_t*) OVERRIDE{ if (!env_t::networkmode || env_t::server) { create_win(new settings_frame_t(&welt->get_settings()), w_info, magic_settings_frame_t); } return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_settings_frame_t); return false; } }; /* server info and join dialog */ class dialog_gameinfo_t : public tool_t { public: dialog_gameinfo_t() : tool_t(DIALOG_GAMEINFO | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Game info"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_server_frame_t); } bool init(player_t*) OVERRIDE{ create_win(new server_frame_t(), w_info, magic_server_frame_t); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_server_frame_t); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; /* open themes selector settings */ class dialog_themes_t : public tool_t { public: dialog_themes_t() : tool_t(DIALOG_THEMES | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Select a theme for display"); } bool is_selected() const OVERRIDE{ return win_get_magic(magic_themes); } bool init(player_t*) OVERRIDE{ create_win(new themeselector_t(), w_info, magic_themes); return false; } bool exit(player_t*) OVERRIDE{ destroy_win(magic_themes); return false; } bool is_init_keeps_game_state() const OVERRIDE{ return true; } bool is_work_keeps_game_state() const OVERRIDE{ return true; } }; // open chat window class dialog_chat_t : public tool_t { public: dialog_chat_t() : tool_t(DIALOG_CHAT | DIALOGE_TOOL) {} image_id get_icon(player_t*) const OVERRIDE { return env_t::networkmode || !welt->get_chat_message()->get_list().empty() ? icon : IMG_EMPTY; } char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Chat"); } bool is_selected() const OVERRIDE { return win_get_magic(magic_chatframe); } bool init(player_t*) OVERRIDE { create_win(new chat_frame_t(), w_info, magic_chatframe); return false; } bool exit(player_t*) OVERRIDE { destroy_win(magic_chatframe); return false; } void draw_after(scr_coord pos, bool dirty) const OVERRIDE { if (icon != IMG_EMPTY) { if (is_selected()) { gfx->draw_img_blend(icon, pos.x, pos.y, TRANSPARENT50_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_BLACK), false, dirty); } uint16 unread_count = env_t::chat_unread_public + env_t::chat_unread_company + env_t::chat_unread_whisper; if (unread_count > 99) { unread_count = 99; } if (unread_count) { char str[16]; sprintf(str, "%i", unread_count); scr_coord_val txt_width = gfx->calc_text_width(str); scr_coord_val width = max(LINEASCENT, txt_width + 2); scr_coord_val xoff = env_t::iconsize.w - width - 1; scr_coord_val yoff = env_t::iconsize.h - LINESPACE - 1; gfx->draw_rounded_rect_clipped(pos.x + xoff, pos.y + yoff, width, LINESPACE, gfx->palette_lookup(COL_RED + 1), dirty); gfx->draw_text(pos.x + xoff + D_GET_CENTER_ALIGN_OFFSET(txt_width, width), pos.y + yoff + 1, str, ALIGN_LEFT, gfx->palette_lookup(COL_WHITE), dirty); } } } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; // open player ranking dialog class dialog_player_ranking_t : public tool_t { public: dialog_player_ranking_t() : tool_t(DIALOG_PLAYER_RANKING | DIALOGE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Player ranking"); } image_id get_icon(player_t*) const OVERRIDE { int nr = 0; for (int i = 0; i < MAX_PLAYER_COUNT; i++) { if (welt->get_player(i)) { nr++; } } return nr>2 ? icon : IMG_EMPTY; } bool is_selected() const OVERRIDE { return win_get_magic(magic_player_ranking); } bool init(player_t */*player*/) OVERRIDE { create_win(new player_ranking_frame_t(), w_info, magic_player_ranking); return false; } bool exit(player_t*) OVERRIDE { destroy_win(magic_player_ranking); return false; } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; #endif simutrans-124.5/src/simutrans/tool/simtool-script-generator.cc000066400000000000000000000550401520675460500246440ustar00rootroot00000000000000 #include "simtool.h" #include "../tpl/vector_tpl.h" #include "../simhalt.h" #include "../dataobj/environment.h" #include "../dataobj/koord3d.h" #include "../descriptor/building_desc.h" #include "../descriptor/ground_desc.h" #include "../gui/messagebox.h" #include "../gui/script_generator_frame.h" #include "../gui/simwin.h" #include "../obj/wayobj.h" #include "../obj/bruecke.h" #include "../obj/depot.h" #include "../obj/gebaeude.h" #include "../obj/signal.h" #include "../obj/zeiger.h" #include "../sys/simsys.h" #include "../world/simcity.h" #include "../world/simworld.h" #include "../world/simplan.h" void tool_generate_script_t::mark_tiles(player_t*, const koord3d& start, const koord3d& end) { koord k1, k2; k1.x = start.x < end.x ? start.x : end.x; k1.y = start.y < end.y ? start.y : end.y; k2.x = start.x + end.x - k1.x; k2.y = start.y + end.y - k1.y; koord k; for (k.x = k1.x; k.x <= k2.x; k.x++) { for (k.y = k1.y; k.y <= k2.y; k.y++) { if (planquadrat_t* plan = welt->access(k.x, k.y)) { for (uint8 i = 0; i < plan->get_boden_count(); i++) { if (grund_t* gr = plan->get_boden_bei(i)) { if (gr->ist_karten_boden() || gr->get_pos().z > plan->get_boden_bei(0)->get_pos().z) { zeiger_t* marker = new zeiger_t(gr->get_pos(), NULL); const uint8 grund_hang = gr->get_grund_hang(); #if 0 // this would use the way slope, not the ground slope const uint8 weg_hang = gr->get_weg_hang(); const uint8 hang = max(corner_sw(grund_hang), corner_sw(weg_hang)) + 3 * max(corner_se(grund_hang), corner_se(weg_hang)) + 9 * max(corner_ne(grund_hang), corner_ne(weg_hang)) + 27 * max(corner_nw(grund_hang), corner_nw(weg_hang)); uint8 back_hang = (hang % 3) + 3 * ((uint8)(hang / 9)) + 27; #else uint8 back_hang = (grund_hang % 3) + 3 * ((uint8)(grund_hang / 9)) + 27; #endif marker->set_foreground_image(ground_desc_t::marker->get_image(grund_hang % 27)); marker->set_image(ground_desc_t::marker->get_image(back_hang)); marker->mark_image_dirty(marker->get_image(), 0); gr->obj_add(marker); marked.insert(marker); } } } } } } } static void write_owner_at(player_t* pl, cbuffer_t& buf, const koord3d pos, const koord3d origin) { if (pl->is_public_service()) { if (const grund_t* gr = world()->lookup(pos)) { if (obj_t* obj = gr->obj_bei(0)) { if (obj->get_owner() != pl) { koord3d diff = pos - origin; buf.printf("\thm_set_owner_tl(%hu,[%d,%d,%d])\n", obj->get_owner_nr(), diff.x, diff.y, diff.z); } } } } } static void write_city_at(player_t* pl, cbuffer_t& buf, const koord3d pos, const koord3d origin) { if (pl->is_public_service()) { if (const grund_t* gr = world()->lookup(pos)) { if (const gebaeude_t* gb = gr->find()) { if (gb->get_tile()->get_offset() == koord(0, 0)) { if (gb->is_townhall()) { koord3d diff = pos - origin; buf.printf("\thm_city_set_population_tl(%ld,[%d,%d,%d])\n", gb->get_stadt()->get_finance_history_month(0, HIST_CITIZENS), diff.x, diff.y, diff.z); } } } } } } static void write_townhall_at(player_t */*pl*/, cbuffer_t& buf, const koord3d pos, const koord3d origin) { if (const grund_t* gr = world()->lookup(pos)) { if (const gebaeude_t* gb = gr->find()) { if (gb->get_tile()->get_offset() == koord(0, 0)) { // we have a start tile here => more checks const building_desc_t* desc = gb->get_tile()->get_desc(); if (gb->is_townhall()) { koord3d diff = pos - origin; buf.printf("\thm_city_tl(0,\"%s\",[%d,%d,%d],%d)\n", desc->get_name(), diff.x, diff.y, diff.z, gb->get_tile()->get_layout()); } } } } } static void write_house_at(player_t* pl, cbuffer_t& buf, const koord3d pos, const koord3d origin) { if (const grund_t* gr = world()->lookup(pos)) { if (const gebaeude_t* gb = gr->find()) { if (gb->get_tile()->get_offset() == koord(0, 0)) { // we have a start tile here => more checks const building_desc_t* desc = gb->get_tile()->get_desc(); if (!desc->is_transport_building()) { sint16 rotation = gb->get_tile()->get_layout(); if (pl->is_public_service()) { if (desc->is_headquarters()) { // skipping } else if (desc->is_factory()) { // koord3d diff = pos - origin; // buf.printf("\thm_factor_tl(0,\"%s\",[%d,%d,%d],%d)\n", desc->get_name(), diff.x, diff.y, diff.z, rotation); } else if (desc->is_attraction()) { koord3d diff = pos - origin; buf.printf("\thm_house_tl(\"%s\",[%d,%d,%d],%d)\n", desc->get_name(), diff.x, diff.y, diff.z, rotation); } else if (gb->is_townhall()) { // has been hopefully already written ... } else if (desc->is_connected_with_town()) { koord3d diff = pos - origin; buf.printf("\thm_house_tl(\"%s\",[%d,%d,%d],%d)\n", desc->get_name(), diff.x, diff.y, diff.z, rotation); } } else if (gb->get_owner() == pl) { koord3d diff = pos - origin; if (desc->is_headquarters()) { // headquarter has a level instead desc buf.printf("\thm_headquarter_tl(%d,[%d,%d,%d],%d)\n", pl->get_headquarter_level(), diff.x, diff.y, diff.z, rotation); } else { buf.printf("\thm_house_tl(\"%s\",[%d,%d,%d],%d)\n", desc->get_name(), diff.x, diff.y, diff.z, rotation); } } } } } } } static void write_depot_at(player_t* pl, cbuffer_t& buf, const koord3d pos, const koord3d origin) { if (const grund_t* gr = world()->lookup(pos)) { if (const depot_t* obj = gr->get_depot()) { if (obj->get_owner() == pl) { const building_desc_t* desc = obj->get_tile()->get_desc(); koord3d diff = pos - origin; buf.printf("\thm_depot_tl(\"%s\",[%d,%d,%d],%d)\n", desc->get_name(), diff.x, diff.y, diff.z, desc->get_finance_waytype()); } } } } // taking all roadsigns for now static void write_sign_at(player_t* , cbuffer_t& buf, const koord3d pos, const koord3d origin) { const grund_t* gr = world()->lookup(pos); const weg_t* weg = gr ? gr->get_weg_nr(0) : NULL; if (!weg || (!weg->has_sign() && !weg->has_signal())) { return; } // now this tile should have a sign. roadsign_t* sign = NULL; if (signal_t* s = gr->find()) { sign = s; } else { // a sign should exists. sign = gr->find(); } if (sign) { // now this pos has a stop. uint8 cnt = 1; const ribi_t::ribi d = sign->get_dir(); const bool ow = sign->get_desc()->is_single_way(); if (!ribi_t::is_single(d)) { cnt = 1; } else if (ribi_t::is_straight(weg->get_ribi_unmasked())) { cnt = (d == ribi_t::north || d == ribi_t::east) ^ ow ? 2 : 3; } else if (ribi_t::is_bend(weg->get_ribi_unmasked())) { cnt = (d == ribi_t::north || d == ribi_t::south) ^ ow ? 2 : 3; } koord3d diff = pos - origin; buf.printf("\thm_sign_tl(\"%s\",%d,[%d,%d,%d],%d,%u)\n", sign->get_desc()->get_name(), cnt, diff.x, diff.y, diff.z, sign->get_desc()->get_waytype(), sign->get_desc()->is_traffic_light() ? 512 : sign->get_desc()->get_flags()); } } static void write_slope_at(player_t* pl, cbuffer_t& buf, const koord3d pos, const koord3d origin) { const grund_t* gr = world()->lookup(pos); if (!gr || gr->is_water() || !gr->ist_karten_boden()) { return; } if(!pl->is_public_service()) { // only save used tiles unless public service if (gr->ist_natur() && gr->get_typ()==grund_t::boden) { // do not touch unless its supports an elevated way if (grund_t* el = world()->lookup(pos + koord3d(0, 0, world()->get_settings().get_way_height_clearance()))) { if (el->get_typ() != grund_t::monorailboden) { return; } } else { return; } } if (origin.z > pos.z && (gr->obj_count()==0 || gr->obj_bei(0)->get_owner() != pl)) { // do not save tiles below the start unless is mine return; } } const koord3d pb = pos - origin; sint8 diff = pb.z; while (diff != 0) { if (diff > 0) { // raise the land buf.printf("\thm_slope_tl(hm_slope.UP,[%d,%d,%d])\n", pb.x, pb.y, pb.z - diff); diff -= 1; } else { // lower the land buf.printf("\thm_slope_tl(hm_slope.DOWN,[%d,%d,%d])\n", pb.x, pb.y, pb.z - diff); diff += 1; } } // check slopes const slope_t::type slp = gr->get_grund_hang(); if (slp > 0) { buf.printf("\thm_slope_tl(%d,[%d,%d,%d])\n", slp, pb.x, pb.y, pb.z); } } static void write_ground_at(player_t* pl, cbuffer_t& buf, const koord3d pos, const koord3d origin) { const grund_t* gr = world()->lookup(pos); if (!gr || gr->is_water() || !gr->ist_karten_boden()) { return; } if (!pl->is_public_service()) { // only save used tiles unless public service if (gr->ist_natur() && gr->get_typ() == grund_t::boden) { // do not touch unless its supports an elevated way if (grund_t* el = world()->lookup(pos + koord3d(0, 0, world()->get_settings().get_way_height_clearance()))) { if (el->get_typ() != grund_t::monorailboden) { return; } } else { return; } } if (origin.z > pos.z && (gr->obj_count() == 0 || gr->obj_bei(0)->get_owner() != pl)) { // do not save tiles below the start unless is mine return; } } const koord3d pb = pos - origin; buf.printf("\thm_ground_tl(%d,[%d,%d,%d])\n", gr->get_grund_hang(), pb.x, pb.y, pb.z); } // we only write bridges inside the marked area (ignoring ownership) static void write_command_bridges(player_t*, cbuffer_t& buf, const koord start, const koord end, const koord3d origin) { vector_tpl all_bridgepos;; karte_t* welt = world(); for (sint16 x = start.x; x <= end.x; x++) { for (sint16 y = start.y; y <= end.y; y++) { if (grund_t* gr = welt->lookup_kartenboden(x, y)) { if (gr->ist_bruecke()) { koord3d bstart = gr->get_pos(); bruecke_t* br = gr->find(); assert(br); if (all_bridgepos.is_contained(bstart)) { // already handled continue; } all_bridgepos.append(bstart); // find end of bridge koord zv = (gr->get_grund_hang() != slope_t::flat) ? -koord(gr->get_grund_hang()) : koord(gr->get_weg_hang()); koord3d checkpos(zv,slope_t::max_diff(max(gr->get_weg_hang(),gr->get_grund_hang()))); checkpos += bstart; bstart -= origin; while(checkpos.x>=start.x && checkpos.y>=start.y && checkpos.x<=end.x && checkpos.y<=end.y) { gr = welt->lookup(checkpos); if (!gr) { // nothing here => must be the end gr = welt->lookup_kartenboden(checkpos.get_2d()); if (gr) { koord3d bend = gr->get_pos(); all_bridgepos.append(bend); bend -= origin; // write bridge building command buf.printf("\thm_bridge_tl(\"%s\",[%d,%d,%d],[%d,%d,%d],%d)\n", br->get_desc()->get_name(), bstart.x, bstart.y, bstart.z, bend.x, bend.y, bend.z, br->get_waytype()); } // or the end of the map ... break; } else if (!gr->ist_auf_bruecke()) { koord3d bend = gr->get_pos(); all_bridgepos.append(bend); bend -= origin; // write bridge building command buf.printf("\thm_bridge_tl(\"%s\",[%d,%d,%d],[%d,%d,%d],%d)\n", br->get_desc()->get_name(), bstart.x, bstart.y, bstart.z, bend.x, bend.y, bend.z, br->get_waytype()); break; } checkpos += zv; } } } } } } // since the building order is important, we build the station in the same tile order as the original // May be still fail if there have been connecting tiles that have been deleted ... static void write_command_halt(player_t* pl, cbuffer_t& buf, const koord start, const koord end, const koord3d origin) { vector_tpl all_halt; karte_t *welt = world(); for (sint16 x = start.x; x <= end.x; x++) { for (sint16 y = start.y; y <= end.y; y++) { if (planquadrat_t* plan = welt->access(x, y)) { for (uint8 i = 0; i < plan->get_boden_count(); i++) { halthandle_t h = plan->get_boden_bei(i)->get_halt(); if (h.is_bound() && (h->get_owner()==pl || h->get_owner()->get_player_nr()==PLAYER_PUBLIC_NR)) { all_halt.append_unique(h); } } } } } for (const halthandle_t &halt : all_halt) { for (const haltestelle_t::tile_t& t : halt->get_tiles()) { koord p = t.grund->get_pos().get_2d(); if (start.x <= p.x && p.x <= end.x && start.y <= p.y && p.y <= end.y) { if (const gebaeude_t* gb = t.grund->find()) { sint16 rotation = gb->get_tile()->get_layout(); if (gb->get_tile()->get_offset() == koord(0, 0)) { // only for left top tile save const building_desc_t* desc = gb->get_tile()->get_desc(); koord3d diff = t.grund->get_pos() - origin; buf.printf("\thm_station_tl(\"%s\",[%d,%d,%d],%d,%d,%d)\n", desc->get_name(), diff.x, diff.y, diff.z, gb->get_waytype(), rotation, desc->get_type()); } } } } } } static void write_command(player_t *pl, cbuffer_t& buf, void (*func)(player_t*, cbuffer_t&, const koord3d, const koord3d), const koord start, const koord end, const koord3d origin) { karte_t* welt = world(); for (sint16 x = start.x; x <= end.x; x++) { for (sint16 y = start.y; y <= end.y; y++) { if (planquadrat_t* plan = welt->access(x, y)) { for (uint8 i = 0; i < plan->get_boden_count(); i++) { if (grund_t* gr = plan->get_boden_bei(i)) { if (!gr->ist_im_tunnel()) { func(pl, buf, plan->get_boden_bei(i)->get_pos(), origin); } } } } } } } // for functions which need concatenation class write_path_command_t { protected: struct { koord3d start; koord3d end; const char* desc_name; waytype_t way_type; sint16 system_type; } typedef script_cmd; vector_tpl commands; const player_t* player; cbuffer_t& buf; const char* cmd_str; koord start, end; koord3d origin; virtual void append_command(koord3d pos, const ribi_t::ribi(&dirs)[2]) = 0; virtual bool can_concatnate(script_cmd& a, script_cmd& b) = 0; public: write_path_command_t(player_t* pl, cbuffer_t& b, koord s, koord e, koord3d o) : player(pl), buf(b), start(s), end(e), origin(o) { }; void write() { for (sint8 z = 127; z > -127; z--) { // iterate for all height for (sint16 x = start.x; x <= end.x; x++) { for (sint16 y = start.y; y <= end.y; y++) { ribi_t::ribi dirs[2]; dirs[0] = x > start.x ? ribi_t::west : ribi_t::none; dirs[1] = y > start.y ? ribi_t::north : ribi_t::none; append_command(koord3d(x, y, z), dirs); } } } // concatenate the command while (!commands.empty()) { script_cmd cmd = commands.pop_back(); bool adjacent_found = true; while (adjacent_found) { adjacent_found = false; for (uint32 i = 0; i < commands.get_count(); i++) { if (!can_concatnate(commands[i], cmd)) { continue; } if (cmd.end == commands[i].start) { cmd.end = commands[i].end; adjacent_found = true; } else if (cmd.start == commands[i].end) { cmd.start = commands[i].start; adjacent_found = true; } if (adjacent_found) { commands.remove_at(i); break; } } } // all adjacent entries were concatenated. if (cmd.system_type >= 0) { buf.printf("\t%s(\"%s\",[%d,%d,%d],[%d,%d,%d],%d,%d)\n", cmd_str, cmd.desc_name, cmd.start.x, cmd.start.y, cmd.start.z, cmd.end.x, cmd.end.y, cmd.end.z, cmd.way_type, cmd.system_type); } else { buf.printf("\t%s(\"%s\",[%d,%d,%d],[%d,%d,%d],%d)\n", cmd_str, cmd.desc_name, cmd.start.x, cmd.start.y, cmd.start.z, cmd.end.x, cmd.end.y, cmd.end.z, cmd.way_type); } } } }; class write_way_command_t : public write_path_command_t { protected: bool first_pass; waytype_t waytype; void append_command(koord3d pos, const ribi_t::ribi(&dirs)[2]) OVERRIDE { if (!first_pass && waytype != air_wt) { // only air needs two passes for correct order return; } const grund_t* gr = world()->lookup(pos); if (!gr) { return; } if (gr->ist_bruecke() && gr->find()->get_desc()->get_waytype() == waytype) { // bridge way part of desc return; } const weg_t* weg = gr->get_weg(waytype); if (!weg) { return; } koord3d pb = pos - origin; // relative base pos if (gr->get_typ() == grund_t::monorailboden) { pb.z -= world()->get_settings().get_way_height_clearance(); } for (uint8 i = 0; i < 2; i++) { if (dirs[i] == ribi_t::none) { continue; } grund_t* to = NULL; gr->get_neighbour(to, waytype, dirs[i]); if (to) { if (const weg_t* to_weg = to->get_weg(waytype)) { const way_desc_t* d = weg->get_desc(); if(waytype==air_wt) { if(first_pass) { if (weg->get_desc()->get_styp() + to_weg->get_desc()->get_styp() > 1) { // we connect in this round only to runways for one step continue; } if (to_weg->get_desc()->get_styp() == 0) { d = to_weg->get_desc(); } } else if ((weg->get_desc()->get_styp() + to_weg->get_desc()->get_styp()) != 2) { // in second pass we keep on runway continue; } } koord3d tp = to->get_pos() - origin; if (to->get_typ() == grund_t::monorailboden) { tp = tp - koord3d(0, 0, world()->get_settings().get_way_height_clearance()); } commands.append(script_cmd{ pb, tp, d->get_name(), d->get_waytype(), (sint16)d->get_styp() }); } } } } bool can_concatnate(script_cmd& a, script_cmd& b) OVERRIDE { return strcmp(a.desc_name, b.desc_name) == 0 && ribi_type(a.start, a.end) == ribi_type(b.start, b.end); } public: write_way_command_t(player_t* pl, cbuffer_t& b, koord s, koord e, koord3d o, bool fp, waytype_t wt) : write_path_command_t(pl, b, s, e, o) { cmd_str = "hm_way_tl"; first_pass = fp; waytype = wt; } }; class write_wayobj_command_t : public write_path_command_t { protected: void append_command(koord3d pos, const ribi_t::ribi(&dirs)[2]) OVERRIDE { const grund_t* gr = world()->lookup(pos); const weg_t* weg0 = gr ? gr->get_weg_nr(0) : NULL; const wayobj_t* wayobj = weg0 ? gr->get_wayobj(weg0->get_waytype()) : NULL; if (!wayobj) { return; } const waytype_t type = weg0->get_waytype(); for (uint8 i = 0; i < 2; i++) { if (dirs[i] == ribi_t::none) { continue; } grund_t* to = NULL; gr->get_neighbour(to, type, dirs[i]); const wayobj_t* t_obj = to ? to->get_wayobj(type) : NULL; if (t_obj && t_obj->get_desc() == wayobj->get_desc()) { commands.append(script_cmd{ pos - origin, to->get_pos() - origin, wayobj->get_desc()->get_name(), wayobj->get_waytype(),-1 }); } } } bool can_concatnate(script_cmd& a, script_cmd& b) OVERRIDE { return strcmp(a.desc_name, b.desc_name) == 0; } public: write_wayobj_command_t(player_t *pl, cbuffer_t& b, koord s, koord e, koord3d o) : write_path_command_t(pl, b, s, e, o) { cmd_str = "hm_wayobj_tl"; } }; char const* tool_generate_script_t::do_work(player_t* pl, const koord3d& s, const koord3d& end) { grund_t * gr = welt->lookup_kartenboden(s.get_2d()); if(!gr) { // not on the map return ""; } const koord3d start = gr->get_pos(); koord3d e = end == koord3d::invalid ? start : end; koord k1 = koord(min(start.x, e.x), min(start.y, e.y)); koord k2 = koord(max(start.x, e.x), max(start.y, e.y)); koord area( k2.x-k1.x+1, k2.y-k1.y+1 ); cbuffer_t generated_script_buf; generated_script_buf.printf("// automated rebuild scrip with size (%d,%d)\n\n", area.x, area.y); // comment generated_script_buf.append("include(\"hm_toolkit_v3\")\n\nfunction hm_build() {\n"); // header int cmdlen = generated_script_buf.len(); koord3d begin(k1, start.z); if (pl->is_public_service()) { // ensure this is run by public service generated_script_buf.append("\tif(this.player.nr != 1) {\n\t\treturn \"Must be run as public player!\"\n\t}\n\n"); } write_command(pl, generated_script_buf, write_ground_at, k1, k2, begin); // write all used tiles if (pl->is_public_service()) { write_command(pl, generated_script_buf, write_townhall_at, k1, k2, begin); } write_command_bridges(pl, generated_script_buf, k1, k2, begin); write_way_command_t(pl, generated_script_buf, k1, k2, begin, true, road_wt).write(); write_way_command_t(pl, generated_script_buf, k1, k2, begin, true, track_wt).write(); write_way_command_t(pl, generated_script_buf, k1, k2, begin, true, water_wt).write(); write_way_command_t(pl, generated_script_buf, k1, k2, begin, true, monorail_wt).write(); write_way_command_t(pl, generated_script_buf, k1, k2, begin, true, maglev_wt).write(); write_way_command_t(pl, generated_script_buf, k1, k2, begin, true, narrowgauge_wt).write(); write_way_command_t(pl, generated_script_buf, k1, k2, begin, true, air_wt).write(); write_way_command_t(pl, generated_script_buf, k1, k2, begin, false, air_wt).write(); write_way_command_t(pl, generated_script_buf, k1, k2, begin, true, decoration_wt).write(); write_wayobj_command_t(pl, generated_script_buf, k1, k2, begin).write(); write_command_halt(pl, generated_script_buf, k1, k2, begin); write_command(pl, generated_script_buf, write_depot_at, k1, k2, begin); write_command(pl, generated_script_buf, write_sign_at, k1, k2, begin); write_command(pl, generated_script_buf, write_house_at, k1, k2, begin); if (pl->is_public_service()) { write_command(pl, generated_script_buf, write_owner_at, k1, k2, begin); // write_command(pl, generated_script_buf, write_city_at, k1, k2, begin); } if (cmdlen == generated_script_buf.len()) { return NULL; } generated_script_buf.append("}\n"); // footer cbuffer_t dir_buf; dir_buf.printf("%saddons%s%stool%s", env_t::user_dir, PATH_SEPARATOR, env_t::pak_name.c_str(), PATH_SEPARATOR); dr_mkdir(dir_buf.get_str()); create_win(new script_generator_frame_t(this, dir_buf.get_str(), generated_script_buf, area ), w_info, magic_script_generator); if (can_use_gui() && pl == welt->get_active_player()) { welt->set_tool(general_tool[TOOL_QUERY], pl); } return NULL; } bool tool_generate_script_t::save_script(const char* fullpath, const char *command, koord area) const { dr_mkdir(fullpath); cbuffer_t fname; cbuffer_t short_name; if (const char* p = strrchr(fullpath, *PATH_SEPARATOR)) { short_name.append(p + 1); } fname.printf("%s%stool.nut",fullpath, PATH_SEPARATOR); if (FILE* file = dr_fopen(fname, "w")) { fprintf(file, "%s", command); fclose(file); fname.clear(); fname.printf("%s%sdescription.tab", fullpath, PATH_SEPARATOR); if ((file = dr_fopen(fname, "w"))) { fprintf(file, "title=Building %s\n", short_name.get_str()); fprintf(file, "cursor_area=%d,%d\n", area.x, area.y); fprintf(file, "type=one_click\ntooltip=Building %s created by Simutrans\nrestart=1\nicon=BuilderScript\n", short_name.get_str()); fclose(file); } create_win(new news_img("The generated script was saved!\n"), w_time_delete, magic_none); return true; } dbg->error("tool_generate_script_t::save_script()", "cannot save file %s", fullpath); create_win(new news_img("The script cannot be saved!\n"), w_time_delete, magic_none); return false; } simutrans-124.5/src/simutrans/tool/simtool-scripted.cc000066400000000000000000000257151520675460500231770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../tool/simtool-scripted.h" #include "../player/simplay.h" #include "../script/script_tool_manager.h" #include "../script/script.h" #include "../script/script_loader.h" #include "../script/api/api.h" #include "../script/api_class.h" #include "../script/api_param.h" #include "../simskin.h" #include "../world/simworld.h" #include "../obj/zeiger.h" #include "../sys/simsys.h" #include "../dataobj/environment.h" void export_scripted_tools(HSQUIRRELVM vm); // -- callback to receive error messages from work/do_work calls static bool exec_script_base_work_callback(exec_script_base_t *esb, player_t* player, const char* err) { tool_t *tool = esb ? dynamic_cast(esb) : NULL; if (tool && player) { player->tell_tool_result(tool, koord3d::invalid, err); } if (tool_exec_two_click_script_t* tct = dynamic_cast(esb)) { tct->waiting_for_do_work = false; // we cannot call init() here directly, because it may destroy the vm that called us tct->needs_call_to_init = true; } return true; } // -- transfer pointers to tools between squirrel and c++ namespace script_api { /** * Mechanic to transfer exec_script_base_t* pointers between squirrel and c++, * used for callbacks. */ declare_specialized_param(exec_script_base_t*, "x", "scripted_tool_t"); SQInteger param::push(HSQUIRRELVM vm, exec_script_base_t* const& v) { return push_instance_up(vm, v); } exec_script_base_t* param::get(HSQUIRRELVM vm, SQInteger index) { return get_attached_instance(vm, index, param::tag()); } void* param::tag() { return (void*)¶m::get; } /** * Mechanic to transfer tool_exec_two_click_script_t* pointers between squirrel and c++, * used for marking tiles in two-click tools. */ template<> struct param { static tool_exec_two_click_script_t* get(HSQUIRRELVM vm, SQInteger index) { tool_t* tool = param::get(vm, index); return tool ? dynamic_cast(tool) : NULL; } static SQInteger push(HSQUIRRELVM vm, tool_exec_two_click_script_t* const& tool) { SQInteger res = push_instance(vm, "command_x", (uint32)(TOOL_EXEC_TWO_CLICK_SCRIPT | GENERAL_TOOL) ); if (SQ_SUCCEEDED(res)) { my_tool_t* mtool = new my_tool_t(tool); attach_instance(vm, -1, mtool); } return res; } }; }; // -- basic script handling -- exec_script_base_t::~exec_script_base_t() { delete info; delete script; } void exec_script_base_t::set_info(const scripted_tool_info_t *i) { delete info; info = i; } bool exec_script_base_t::init_vm(player_t* player) { if (get_info() == NULL) { // tool probably read from menuconf.tab // path in default_param, initialize if (tool_t *tool = dynamic_cast(this)) { script_tool_manager_t::load_tool(tool->get_default_param(), tool); } } if( script==NULL || (info && info->restart) ) { load_script(info->path, player); } return script != NULL; } void exec_script_base_t::load_script(const char* path, player_t* player) { cbuffer_t buf; buf.printf("script-exec-%d.log", player->get_player_nr()); if( script ) { // if vm already exists, delete it. delete script; script = NULL; } // start vm dr_chdir(path); script = script_loader_t::start_vm("tool_base.nut", buf, ".", false); if (script == NULL) { return; } // set my player number script->set_my_player(player->get_player_nr()); // export tool-pointer handling export_scripted_tools(script->get_vm()); // callback script->register_callback(exec_script_base_work_callback, "exec_script_base_work_callback"); // call script to initialize it buf.clear(); buf.printf( "%s/tool.nut", path ); if (const char* err = script->call_script("tool.nut")) { if (strcmp(err, "suspended")) { dbg->error("tool_exec_script_t::load_script", "error [%s] calling %s/tool.nut", err, (const char*)path); delete script; script = NULL; return; } } dr_chdir(env_t::user_dir); // older versions did not support the flags parameter - correct with helper function if (const char* err = script->call_function(script_vm_t::QUEUE, "correct_missing_flags_argument")) { if (strcmp(err, "suspended")) { dbg->error("tool_exec_script_t::load_script", "error [%s] calling correct_missing_flags_argument", err); delete script; script = NULL; } } } void export_scripted_tools(HSQUIRRELVM vm) { script_api::create_class(vm, script_api::param::squirrel_type()); script_api::end_class(vm); } template const char* exec_script_base_t::call_function(script_vm_t::call_type_t ct, const char* function, player_t* player, R& ret, const As &... as) { if (!script) { dbg->warning("tool_exec_script_t::call_function", "script vm is not available."); return ""; } const char* err = script->call_function(ct, function, ret, player, as...); if (err && strcmp(err, "suspended")) { dbg->warning("tool_exec_script_t::call_function", "error calling %s: %s", function, err); } return err; } void exec_script_base_t::step(player_t* player) { if (script) { script->call_function(script_vm_t::QUEUE, "step"); if (tool_exec_two_click_script_t *tt = dynamic_cast(this)) { if (tt->needs_call_to_init) { tt->init(player); } } } } void exec_script_base_t::init_images(tool_t *tool) const { if (info && info->desc) { if (info->desc->get_image_id(0) != IMG_EMPTY) { tool->cursor = info->desc->get_image_id(0); } tool->set_icon(info->desc->get_image_id(1)); } } tool_exec_script_t::tool_exec_script_t(const scripted_tool_info_t *info) : tool_t(TOOL_EXEC_SCRIPT | GENERAL_TOOL), exec_script_base_t(info) { init_images(this); } bool tool_exec_script_t::init(player_t* player) { bool res = false; cursor_area = get_cursor_area(); cursor_offset = get_cursor_offset(); // rotate area and offset for(uint8 i=0; iget_settings().get_rotation(); i++) { koord old_area = cursor_area; koord old_offset = cursor_offset; cursor_area = koord(old_area.y, old_area.x); cursor_offset = koord(old_area.y-1-old_offset.y, old_offset.x); } return init_vm(player) && call_function(script_vm_t::FORCE, "init", player, res)== NULL && res; } bool tool_exec_script_t::exit(player_t* player) { bool res, res2 = false; // exit script res = call_function(script_vm_t::FORCE, "exit", player, res2) == NULL; // shut down vm delete script; script = NULL; return res && res2; } const char* tool_exec_script_t::work(player_t* player, koord3d pos) { static plainstring res; res = NULL; // callback script->prepare_callback("exec_script_base_work_callback", 3, (exec_script_base_t*)this, player, (const char*)""); // now call uint8 keys = flags & (tool_t::WFL_SHIFT | tool_t::WFL_CTRL); const char* err = call_function(script_vm_t::QUEUE, "work", player, res, pos, keys); if (err && strcmp(err, "suspended")==0) { // suspended } else { // no callback necessary script->clear_pending_callback(); } return res.c_str(); } tool_exec_two_click_script_t::tool_exec_two_click_script_t(const scripted_tool_info_t *info) : two_click_tool_t(TOOL_EXEC_TWO_CLICK_SCRIPT | GENERAL_TOOL), exec_script_base_t(info) { set_marker(IMG_EMPTY); waiting_for_do_work = false; needs_call_to_init = true; init_images(this); if (info && info->desc) { set_marker(info->desc->get_image(2) ? info->desc->get_image_id(2) : cursor); } } SQInteger script_mark_tile(HSQUIRRELVM vm); // see below bool tool_exec_two_click_script_t::init(player_t* player) { // remove marker images bool res = two_click_tool_t::init(player); if (waiting_for_do_work) { return res; } needs_call_to_init = false; res = res && init_vm(player); if (res) { HSQUIRRELVM vm = script->get_vm(); // put pointer to this tool into registry sq_pushregistrytable(vm); script_api::create_slot(vm, "my_two_click_tool", this); sq_poptop(vm); // export marker function sq_pushroottable(vm); script_api::register_function(vm, script_mark_tile, "mark_tile", 2, ". t|x|y", false /* static */); sq_poptop(vm); } return res && call_function(script_vm_t::FORCE, "init", player, res)== NULL && res; } bool tool_exec_two_click_script_t::exit(player_t* player) { bool res, res2 = false; // exit script res = two_click_tool_t::exit(player) && call_function(script_vm_t::FORCE, "exit", player, res2) == NULL; // shut down vm delete script; script = NULL; waiting_for_do_work = false; return res && res2; } const char* tool_exec_two_click_script_t::do_work(player_t* player, const koord3d &start, const koord3d &end) { if (waiting_for_do_work) { return ""; } static plainstring res; // callback script->prepare_callback("exec_script_base_work_callback", 3, (exec_script_base_t*)this, player, (const char*)""); // now call uint8 keys = flags & (tool_t::WFL_SHIFT | tool_t::WFL_CTRL); res = NULL; const char* err = call_function(script_vm_t::QUEUE, "do_work", player, res, start, end, keys); if (err && strcmp(err, "suspended")==0) { // suspended waiting_for_do_work = true; } else { // no callback necessary script->clear_pending_callback(); } return res.c_str(); } void tool_exec_two_click_script_t::mark_tiles(player_t* player, const koord3d &start, const koord3d &end) { if (waiting_for_do_work) { return; } bool dummy; // try to mark; if script is busy, do nothing uint8 keys = flags & (tool_t::WFL_SHIFT | tool_t::WFL_CTRL); call_function(script_vm_t::TRY, "mark_tiles", player, dummy, start, end, keys); } uint8 tool_exec_two_click_script_t::is_valid_pos(player_t* player, const koord3d &pos, const char *&error, const koord3d &start) { error = NULL; uint8 res = 2; // allow dragging const char* err = call_function(script_vm_t::FORCEX, "is_valid_pos", player, res, pos, start); // script error? signal 'start dragging is allowed here' if (err) { res = 2; // allow dragging } return res; } // mark_tile(pos) SQInteger script_mark_tile(HSQUIRRELVM vm) { koord3d pos = script_api::param::get(vm, 2); player_t* player = script_api::get_my_player(vm); // tool sq_pushregistrytable(vm); tool_exec_two_click_script_t* tool = NULL; if (!SQ_SUCCEEDED(script_api::get_slot(vm, "my_two_click_tool", tool)) || tool == NULL) { return SQ_ERROR; } sq_poptop(vm); // now call the method return script_api::param::push(vm, tool->mark_tile(player, pos) ); } bool tool_exec_two_click_script_t::mark_tile(player_t* player, const koord3d &pos) { grund_t *gr = welt->lookup(pos); if (gr) { zeiger_t *mark = new zeiger_t(pos, player ); gr->obj_add(mark); mark->set_image(get_marker_image()); marked.insert(mark); } return gr != NULL; } image_id tool_exec_two_click_script_t::get_marker_image() const { return marker != IMG_EMPTY ? marker : (cursor != IMG_EMPTY ? cursor : skinverwaltung_t::bauigelsymbol->get_image_id(0)); } simutrans-124.5/src/simutrans/tool/simtool-scripted.h000066400000000000000000000110731520675460500230310ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TOOL_SIMTOOL_SCRIPTED_H #define TOOL_SIMTOOL_SCRIPTED_H #include "../tool/simmenu.h" #include "../script/script.h" #include "../utils/plainstring.h" class script_vm_t; class skin_desc_t; /** * Information about scripted tools, extracted from description.tab. * - data from description.tab: * * plainstring path * plainstring title=tool for test 2 * bool type=one_click or two_click * bool restart=1 * plainstring menu=my_script * skin_desc_t* icon=one_click_test: cursor, icon, marker * * @ingroup squirrel-tool-api * */ struct scripted_tool_info_t { plainstring path; ///< path to files plainstring title; ///< name of tool (used for dialog) plainstring tooltip; plainstring menu_arg; ///< menu name, where this tool should appear (used in menuconf.tab) const skin_desc_t* desc; ///< skin object used for cursor (0), icon (1), and maybe marker (2) bool restart; ///< true, if script vm has to be restarted after work() bool is_one_click; ///< true, if tool is one-click (otherwise needs two clicks/coordinates to work) koord cursor_area; ///< size of cursor defined in tool_t koord cursor_offset; ///< cursor offset defined in tool_t /// sets default values scripted_tool_info_t() { desc = NULL; restart = true; is_one_click = true; cursor_area = koord(1,1); cursor_offset = koord(0,0); } }; class exec_script_base_t { private: /// information about tool, take ownership of pointer const scripted_tool_info_t *info; void load_script(const char* path, player_t* player); protected: /// the vm, will be initialized in init() script_vm_t *script; /// starts vm, sets our_player, returns true if successful bool init_vm(player_t* player); template const char* call_function(script_vm_t::call_type_t ct, const char* function, player_t* player, R& ret, const As &... as); public: exec_script_base_t(const scripted_tool_info_t *i) : info(i), script(NULL) {} virtual ~exec_script_base_t(); void set_info(const scripted_tool_info_t *i); const scripted_tool_info_t* get_info() const { return info; } void init_images(tool_t *tool) const; const char* get_menu_arg() const { return info ? info->menu_arg.c_str() : ""; } /// has to be called if the tool is active, to resume script if a work-command gets suspended void step(player_t* player); const char *get_tooltip(const player_t *) const { return info ? info->tooltip.c_str() : ""; } koord get_cursor_area() const { return info->cursor_area; } koord get_cursor_offset() const { return info->cursor_offset; } }; class tool_exec_script_t : public tool_t, public exec_script_base_t { protected: public: tool_exec_script_t(const scripted_tool_info_t *info = NULL); /// is network-safe, as calls to work-commands will be properly handled in network mode bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } bool init(player_t* player) OVERRIDE; bool exit(player_t* player) OVERRIDE; const char *work(player_t* player, koord3d pos) OVERRIDE; const char *get_tooltip(const player_t *pl) const OVERRIDE { return exec_script_base_t::get_tooltip(pl); } }; class tool_exec_two_click_script_t : public two_click_tool_t, public exec_script_base_t { image_id marker; public: tool_exec_two_click_script_t(const scripted_tool_info_t *info = NULL); /// is network-safe, as calls to work-commands will be properly handled in network mode bool is_work_keeps_game_state() const OVERRIDE { return true; } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool init(player_t* player) OVERRIDE; bool exit(player_t* player) OVERRIDE; uint8 is_valid_pos(player_t* player, const koord3d &pos, const char *&error, const koord3d &start) OVERRIDE; const char *do_work(player_t* player, const koord3d &start, const koord3d &end) OVERRIDE; void mark_tiles(player_t* player, const koord3d &start, const koord3d &end) OVERRIDE; bool mark_tile(player_t* player, const koord3d &start); // two_click_tool_t calls init() after do_work(), // because it assumes all work is done. // We have to block this. bool waiting_for_do_work; // instead, init() will be called in step() // if necessary bool needs_call_to_init; void set_marker(image_id m) { marker = m; } virtual image_id get_marker_image() const OVERRIDE; const char *get_tooltip(const player_t *pl) const OVERRIDE { return exec_script_base_t::get_tooltip(pl); } }; #endif simutrans-124.5/src/simutrans/tool/simtool.cc000066400000000000000000010126551520675460500213640ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "../simdebug.h" #include "../simevent.h" #include "../world/simcity.h" #include "../simmesg.h" #include "../simconvoi.h" #include "../gui/simwin.h" #include "../display/viewport.h" #include "../builder/fabrikbauer.h" #include "../builder/vehikelbauer.h" #include "../ground/grund.h" #include "../ground/wasser.h" #include "../obj/way/schiene.h" #include "../ground/tunnelboden.h" #include "../ground/monorailboden.h" #include "../obj/depot.h" #include "../simfab.h" #include "../display/simimg.h" #include "../simintr.h" #include "../simhalt.h" #include "../simskin.h" #include "../descriptor/ground_desc.h" #include "../descriptor/building_desc.h" #include "../descriptor/roadsign_desc.h" #include "../descriptor/tunnel_desc.h" #include "../vehicle/air_vehicle.h" #include "../vehicle/simroadtraffic.h" #include "../vehicle/pedestrian.h" #include "../gui/line_management_gui.h" #include "../gui/tool_selector.h" #include "../gui/station_building_select.h" #include "../gui/minimap.h" // to update map after construction of new industry #include "../gui/depot_frame.h" #include "../gui/player_frame.h" #include "../gui/scenario_info.h" #include "../gui/schedule_list.h" #include "../gui/signal_spacing.h" #include "../gui/city_info.h" #include "../gui/trafficlight_info.h" #include "../gui/privatesign_info.h" #include "../gui/signal_info.h" #include "../gui/messagebox.h" #include "../network/network_socket_list.h" #include "../obj/zeiger.h" #include "../obj/bruecke.h" #include "../obj/tunnel.h" #include "../obj/groundobj.h" #include "../obj/signal.h" #include "../obj/crossing.h" #include "../obj/roadsign.h" #include "../obj/wayobj.h" #include "../obj/leitung2.h" #include "../obj/baum.h" #include "../obj/field.h" #include "../obj/label.h" #include "../dataobj/koord.h" #include "../dataobj/settings.h" #include "../dataobj/environment.h" #include "../dataobj/schedule.h" #include "../dataobj/route.h" #include "../dataobj/scenario.h" #include "../network/network_cmd_ingame.h" // for dragging raise / lower tools #include "../builder/tunnelbauer.h" #include "../builder/brueckenbauer.h" #include "../builder/wegbauer.h" #include "../builder/hausbauer.h" #include "../descriptor/way_desc.h" #include "../descriptor/roadsign_desc.h" #include "../tpl/vector_tpl.h" #include "../network/memory_rw.h" #include "../sys/simsys.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "../tool/simtool.h" #include "../player/finance.h" #define is_scenario() welt->get_scenario()->is_scripted() #define CHECK_FUNDS() \ /* do not allow, if out of money */ \ if( !welt->get_settings().is_freeplay() && player->get_player_nr()!=1 && !player->has_money_or_assets() ) {\ return "Out of funds";\ }\ /****************************************** notification strings **************************************/ /** * Translated notification text identifiers used by tools are placed here. * This is because they are not simple well structured internal identifiers. * Instead they can be complex sentences intended to be read untranslated. * Using these constants assues a valid and correct text identifier is choosen. */ /** * Message returned when a player cannot afford to complete an action. */ static const char *const NOTICE_INSUFFICIENT_FUNDS = "Insufficient funds!"; /** * Message returned when a player tries to place trees when trees are disabled. */ static const char *const NOTICE_NO_TREES = "Trees disabled!"; /** * Message returned when valid terrain cannot be found for a tool to use. */ static const char *const NOTICE_UNSUITABLE_GROUND = "No suitable ground!"; /** * Message returned when a tool fails due to the target tile being occupied. */ static const char *const NOTICE_TILE_FULL = "Tile not empty."; /** * Message returned when a company tries to make a public way when public ways are disabled. */ static const char *const NOTICE_DISABLED_PUBLIC_WAY = "Not allowed to make publicly owned ways!"; /****************************************** static helper functions **************************************/ size_t strncopy_to_break( char *dest, const char *src, size_t n ) { const char *p = src; for( size_t i=0; ((uint8)*p)>=32 && iscale_with_month_length(maintenance) ) / 100.0); strcat(tool_t::toolstr, translator::translate("/month")); } return tool_t::toolstr; } /** * Creates a tooltip from tip text and money value */ static char const* tooltip_with_price_maintenance_capacity(karte_t* const welt, char const* const tip, sint64 const price, sint64 const maintenance, uint32 const capacity, uint8 const enables) { int n = strncopy_to_break( tool_t::toolstr, translator::translate( tip ), 256 ); memcpy( tool_t::toolstr+n, ", ", 2 ); money_to_string(tool_t::toolstr+n+2, (double)price/-100.0); strcat( tool_t::toolstr, "+" ); n = strlen(tool_t::toolstr); money_to_string(tool_t::toolstr+n, (double)(welt->scale_with_month_length(maintenance) ) / 100.0); strcat(tool_t::toolstr, translator::translate("/month")); n = strlen(tool_t::toolstr); if((enables&7)!=0) { n += sprintf( tool_t::toolstr+n, ", %d", capacity ); if(enables&1) { n += sprintf( tool_t::toolstr+n, " %s", translator::translate("Passagiere") ); } if(enables&2) { n += sprintf( tool_t::toolstr+n, " %s", translator::translate("Post") ); } if(enables&4) { n += sprintf( tool_t::toolstr+n, " %s", translator::translate("Fracht") ); } } else if (!welt->get_settings().is_separate_halt_capacities()) { n += sprintf( tool_t::toolstr+n, ", %s %d", translator::translate("Storage capacity"), capacity ); } return tool_t::toolstr; } void open_error_msg_win(const char* error) { koord3d pos = message_t::get_coord_from_text(error); if (pos != koord3d::invalid) { create_win( new news_loc(error, pos), w_time_delete, magic_none); } else { create_win( new news_img(error), w_time_delete, magic_none); } } /** * sucht Haltestelle um Umkreis +1/-1 um (pos, b, h) * extended to search first in our direction */ static halthandle_t suche_nahe_haltestelle(player_t *player, karte_t *welt, koord3d pos, sint16 b=1, sint16 h=1) { koord k(pos.get_2d()); // any other ground with a valid stop here? halthandle_t my_halt; if( planquadrat_t* plan=welt->access(pos.get_2d()) ) { my_halt = plan->get_halt( player ); if( my_halt.is_bound() ) { return my_halt; } } grund_t *bd = welt->lookup(pos); if( bd==NULL ) { bd = welt->lookup_kartenboden(k); } // first we try to connect to a stop straight in our direction; otherwise our station may break during construction if( bd->hat_wege() ) { ribi_t::ribi ribi = bd->get_weg_nr(0)->get_ribi_unmasked(); for( int i=0; i<4; i++ ) { if( ribi_t::nesw[i] & ribi ) { if( planquadrat_t* plan=welt->access(k+koord::nesw[i]) ) { my_halt = plan->get_halt( player ); if( my_halt.is_bound() ) { return my_halt; } } } } } // now just search all neighbours for( sint16 y=-1; y<=h; y++ ) { for( sint16 x=-1; x<=b; (x==-1 && y>-1 && yaccess(k+koord(x,y)) ) { my_halt = plan->get_halt( player ); if( my_halt.is_bound() ) { return my_halt; } } } } #ifdef AUTOJOIN_PUBLIC // now search everything for public stops for( int i=0; i<8; i++ ) { if( planquadrat_t* plan=welt->access(k+koord::neighbours[i]) ) { my_halt = plan->get_halt( welt->get_public_player() ); if( my_halt.is_bound() ) { return my_halt; } } } #endif // here we reach only with a non-found station, i.e. a non-bounded handle return halthandle_t(); } // converts a 2d koord to a suitable ground pointer static grund_t *tool_intern_koord_to_weg_grund(player_t *player, karte_t *welt, koord3d pos, waytype_t wt, const bool check_for_wayobj=false) { // check for valid ground grund_t *gr=welt->lookup(pos); if (gr==NULL) { return NULL; } if( wt==powerline_wt && gr->get_leitung() ) { // check for ownership if(gr->get_leitung()->get_removal_error(player)!=NULL) { return NULL; } // ok else { return gr; } } // tram if(wt==tram_wt) { weg_t *way = gr->get_weg(track_wt); if (way && way->get_desc()->get_styp() == type_tram && way->get_removal_error(player)==NULL) { return gr; } else { return NULL; } } // has some rail or monorail? if( !gr->hat_weg(wt) ) { return NULL; } // check for ownership if(gr->get_weg(wt)->get_removal_error(player)!=NULL){ // the way owner is not me, but we must check wayobj owner in some cases: if( check_for_wayobj && gr->get_wayobj(wt)&&gr->get_wayobj(wt)->get_removal_error(player) == NULL ) { // active player's wayobj found! return gr; } return NULL; } // ok, now we have a valid ground return gr; } /****************************************** now the actual tools **************************************/ const char *tool_query_t::work( player_t *, koord3d pos ) { grund_t *gr = welt->lookup(pos); minivec_tplinfo; if(gr) { // not single_info: show least important first minivec_tplconvois; const bool reverse = is_ctrl_pressed(); // iterate through different stages of importance const uint8 max_stages = 5; for(uint8 stage = 0; stage<5; stage++) { uint32 old_count = win_get_open_count(); switch (reverse ? max_stages-1-stage: stage) { case 0: // halts if( gr->get_halt().is_bound() ) { gr->get_halt()->open_info_window(); if (old_count < win_get_open_count()) { info.append(win_get_top()); } } break; case 1: // labels if( gr->get_flag(grund_t::marked) ) { if(label_t *lb = gr->find()) { lb->show_info(); if( old_count < win_get_open_count() ) { info.append(win_get_top()); } } } break; case 2: // convois for (uint8 n = gr->obj_count(); n-- != 0;) { obj_t* obj = gr->obj_bei(reverse ? gr->obj_count() - 1 - n : n); if (vehicle_t* veh = dynamic_cast(obj)) { convois.append_unique(veh->get_convoi()->self); } } for (uint8 n = 0; n < convois.get_count(); n++) { convois[n]->open_info_window(); if (old_count < win_get_open_count()) { if (env_t::single_info) { return NULL; } info.append(win_get_top()); } } break; case 3: // objects for (uint8 n = gr->obj_count(); n-- != 0;) { obj_t* obj = gr->obj_bei(reverse ? gr->obj_count() - 1 - n : n); if (dynamic_cast(obj)) { // already openend them continue; } if (obj && obj->get_typ() != obj_t::wayobj && obj->get_typ() != obj_t::pillar && obj->get_typ() != obj_t::label) { DBG_MESSAGE("tool_query_t()", "index %u", (unsigned)n); obj->show_info(); // did some new window open? if (old_count < win_get_open_count()) { if (env_t::single_info) { return NULL; } info.append(win_get_top()); } old_count = win_get_open_count(); // click may have closed a window, open a new one if possible } } break; case 4: default: // ground gr->open_info_window(); if (old_count < win_get_open_count()) { info.append(win_get_top()); } break; } if( env_t::single_info && !info.empty()) { return NULL; } if(stage==2 && !info.empty()) { // we have a station, label or convois => do not show roads until next click break; } } } if (info.get_count() == 2) { scr_coord pos = welt->get_viewport()->get_screen_coord(gr->get_pos()); // try to tile the windows win_set_pos(info[0], pos - scr_size(info[0]->get_windowsize().w,info[0]->get_windowsize().h/2)); win_set_pos(info[1], pos - scr_size(0, info[1]->get_windowsize().h/2)); } else if (info.get_count() > 2) { scr_coord pos = welt->get_viewport()->get_screen_coord(gr->get_pos()); // try to tile the windows win_set_pos(info[0], pos - info[0]->get_windowsize()); win_set_pos(info[1], pos - scr_size(0, info[1]->get_windowsize().h)); win_set_pos(info[2], pos - scr_size(info[2]->get_windowsize().w,0)); if (info.get_count() > 3) { win_set_pos(info[3], pos); } // the rest will sit on top of each other } return NULL; } /* delete things from a tile * citycars and pedestrian first and then go up to queue to more important objects */ bool tool_remover_t::tool_remover_intern(player_t *player, koord3d pos, sint8 type, const char *&msg) { DBG_MESSAGE("tool_remover_intern()","at (%s)", pos.get_str()); // check if there is something to remove from here ... grund_t *gr = welt->lookup(pos); if (!gr || gr->obj_count()==0) { msg = ""; return false; } // marker? if (type == obj_t::label || type == obj_t::undefined) { if (label_t* l = gr->find()) { msg = l->get_removal_error(player); if(msg==NULL) { delete l; return true; } else if( gr->obj_count()==1 || type == obj_t::label ) { // only complain if this is the last object on this tile ... return false; } msg = NULL; // not deletable: skip it } } // citycar? (we allow always) if (type == obj_t::road_user || type == obj_t::undefined) { if (private_car_t* citycar = gr->find()) { delete citycar; return true; } } // pedestrians only delete on demand! if (type == obj_t::pedestrian) { if (pedestrian_t* pedestrian = gr->find()) { delete pedestrian; return true; } } koord k(pos.get_2d()); // check powerline (can cross ground of another player) leitung_t* lt = gr->get_leitung(); // check whether powerline related stuff should be removed, and if there is any to remove if ( (type == obj_t::leitung || type == obj_t::pumpe || type == obj_t::senke || type == obj_t::undefined) && lt != NULL && lt->get_removal_error(player) == NULL) { if ( gr->get_typ() == grund_t::monorailboden && !gr->get_weg_nr(0) ) { // totally remove elevated powerline with ground lt->cleanup(player); delete lt; gr->obj_loesche_alle(player); gr->mark_image_dirty(); if (!gr->get_flag(grund_t::is_kartenboden)) { welt->access(gr->get_pos().get_2d())->boden_entfernen(gr); welt->set_dirty(); delete gr; } return true; } if( gr->ist_bruecke() ) { bruecke_t* br = gr->find(); if( br == NULL ) { // no bridge? most likely transformer on a former bridge tile... grund_t *gr_new = new boden_t(pos, gr->get_grund_hang()); gr_new->take_obj_from( gr ); welt->access(k)->kartenboden_setzen( gr_new ); gr = gr_new; } else if( br->get_desc()->get_waytype() == powerline_wt ) { msg = bridge_builder_t::remove(player, gr->get_pos(), powerline_wt ); return msg == NULL; } } if(gr->ist_tunnel() && gr->ist_karten_boden()) { if (gr->find()->get_desc()->get_waytype()==powerline_wt) { msg = tunnel_builder_t::remove(player, gr->get_pos(), powerline_wt, is_ctrl_pressed() ); return msg == NULL; } } if( gr->ist_im_tunnel() ) { lt->cleanup(player); delete lt; // now everything gone? if( gr->obj_count() == 1 ) { // delete tunnel too tunnel_t *t = gr->find(); t->cleanup(player); delete t; } // unmark kartenboden (is marked during underground mode deletion) welt->lookup_kartenboden(k)->clear_flag(grund_t::marked); // remove upper or lower ground welt->access(k)->boden_entfernen(gr); delete gr; } else { lt->cleanup(player); delete lt; } return true; } // check for signal roadsign_t* rs = gr->find(); if (rs == NULL) rs = gr->find(); if ( (type == obj_t::signal || type == obj_t::roadsign || type == obj_t::undefined) && rs!=NULL) { msg = rs->get_removal_error(player); if(msg) { return false; } DBG_MESSAGE("tool_remover()", "removing roadsign at (%s)", pos.get_str()); weg_t *weg = gr->get_weg(rs->get_desc()->get_wtyp()); if( weg==NULL && rs->get_desc()->get_wtyp()==tram_wt ) { weg = gr->get_weg(track_wt); } rs->cleanup(player); delete rs; // no need to update way if there is none // may happen when public player builds a signal on a company track, // the company goes bankrupt and the public player tries to remove the signal if (weg) { weg->count_sign(); } return true; } // check stations halthandle_t halt = gr->get_halt(); DBG_MESSAGE("tool_remover()", "bound=%i",halt.is_bound()); if (gr->is_halt() && halt.is_bound() && fabrik_t::get_fab(k)==NULL && type == obj_t::undefined) { // halt and not a factory (oil rig etc.) const player_t* owner = halt->get_owner(); if( player_t::check_owner( owner, player ) ) { return haltestelle_t::remove(player, gr->get_pos()); } } // catenary or something like this wayobj_t* wo = gr->find(); if(wo && (type == obj_t::wayobj || type == obj_t::undefined)) { msg = wo->get_removal_error(player); if(msg) { return false; } wo->cleanup(player); delete wo; depot_t *dep = gr->get_depot(); if( dep ) { dep->update_win(); } return true; } DBG_MESSAGE("tool_remover()", "check tunnel/bridge"); // bridge? if(gr->ist_bruecke() && (type == obj_t::bruecke || type == obj_t::undefined)) { DBG_MESSAGE("tool_remover()", "removing bridge from %d,%d,%d",gr->get_pos().x, gr->get_pos().y, gr->get_pos().z); bruecke_t* br = gr->find(); msg = bridge_builder_t::remove(player, gr->get_pos(), br->get_desc()->get_waytype()); return msg == NULL; } // beginning/end of tunnel if(gr->ist_tunnel() && gr->ist_karten_boden() && (type == obj_t::tunnel || type == obj_t::undefined)) { DBG_MESSAGE("tool_remover()", "removing tunnel from %d,%d,%d",gr->get_pos().x, gr->get_pos().y, gr->get_pos().z); waytype_t wegtyp = gr->get_leitung() ? powerline_wt : gr->get_weg_nr(0)->get_waytype(); msg = tunnel_builder_t::remove(player, gr->get_pos(), wegtyp, is_ctrl_pressed()); return msg == NULL; } // fields field_t* f = gr->find(); if (f && (type == obj_t::field || type == obj_t::undefined)) { msg = f->get_removal_error(player); if(msg==NULL) { f->cleanup(player); delete f; // fields have foundations ... sint8 dummy; welt->access(k)->boden_ersetzen( gr, new boden_t(gr->get_pos(), welt->recalc_natural_slope(k,dummy) ) ); welt->lookup_kartenboden(k)->calc_image(); welt->lookup_kartenboden(k)->set_flag( grund_t::dirty ); } return msg == NULL; } // depots depot_t* dep = gr->get_depot(); if (dep && (type == obj_t::bahndepot || type == obj_t::undefined)) { // type == bahndepot to remove any type of depot msg = dep->get_removal_error(player); if(msg) { return false; } dep->cleanup(player); delete dep; return true; } // since buildings can have more than one tile, we must handle them together gebaeude_t* gb = gr->find(); if(gb != NULL && (type == obj_t::gebaeude || type == obj_t::undefined)) { gb = gb->get_first_tile(); msg = gb->get_removal_error(player); if(msg) { return false; } if(!gb->get_tile()->get_desc()->can_rotate() && welt->cannot_save()) { msg = "Not possible in this rotation!"; return false; } DBG_MESSAGE("tool_remover()", "removing building" ); // remove town? (when removing townhall) if(gb->is_townhall()) { stadt_t *stadt = gb->get_stadt(); if(!welt->remove_city( stadt )) { msg = "Das Feld gehoert\neinem anderen Spieler\n"; return false; } } else { // townhall is also removed during town removal hausbauer_t::remove( player, gb ); } return true; } // if type is given, then last check and leave here. Below other stuff and ways gets removed. if (type != obj_t::undefined) { for (uint i = 0; i < gr->obj_count(); i++) { obj_t* obj = gr->obj_bei(i); if (obj->get_typ() == type && player_t::check_owner(obj->get_owner(),player)) { msg = obj->get_removal_error(player); if (msg) { return false; } delete obj; return true; } } msg = "Requested object not found."; return false; } // there is a powerline above this tile, but we do not own it // so we take it out and add it later again if(lt) { DBG_MESSAGE("tool_remover()", "took out powerline"); gr->obj_remove(lt); } // do not delete crossing, labels, pointer and flying airplanes, so we store them minivec_tplstuff_to_keep; if (obj_t* obj = gr->find()) { stuff_to_keep.append(obj); gr->obj_remove(obj); } if (obj_t* obj = gr->find()) { stuff_to_keep.append(obj); gr->obj_remove(obj); } if (obj_t* obj = gr->find()) { stuff_to_keep.append(obj); gr->obj_remove(obj); } while(obj_t* obj = gr->find()) { stuff_to_keep.append(obj); gr->obj_remove(obj); } // remove all other stuff (clouds, ...) bool return_ok = false; uint8 num_obj = gr->obj_count(); if(num_obj>0) { // cout all pedestrians and ignore them int num_pedestrians = 0; for (int i = 1; i < num_obj; i++) { if (gr->obj_bei(i)->get_typ() == obj_t::pedestrian) { num_pedestrians++; } } // only delete everything if there is more than pedestrians to delete if (num_obj > num_pedestrians) { msg = gr->kann_alle_obj_entfernen(player); return_ok = (msg == NULL && !(gr->get_typ() == grund_t::brueckenboden || gr->get_typ() == grund_t::tunnelboden) && gr->obj_loesche_alle(player)); DBG_MESSAGE("tool_remover()", "removing everything from %d,%d,%d", gr->get_pos().x, gr->get_pos().y, gr->get_pos().z); } } for (int i=0; i < stuff_to_keep.get_count(); i++) { gr->obj_add(stuff_to_keep[i]); } // could not delete everything if(msg) { if (lt) gr->obj_add(lt); return false; } if(return_ok) { // no sound msg = ""; if (lt) gr->obj_add(lt); return true; } // ok, now we remove every object that should be removed - one by one. // the following objects will be removed together DBG_MESSAGE("tool_remover()", "removing way"); waytype_t wt = ignore_wt; if(gr->get_typ()!=grund_t::tunnelboden || gr->has_two_ways()) { weg_t *w = gr->get_weg_nr(1); if(gr->get_typ()==grund_t::brueckenboden && w==NULL) { // do not delete the middle of a bridge if (lt) gr->obj_add(lt); return false; } if( w && w->get_waytype()==water_wt ) { // remove the other way first w = NULL; } if(w==NULL || w->get_removal_error(player)!=NULL) { w = gr->get_weg_nr(0); if(w==NULL) { if (lt) { gr->obj_add(lt); msg = lt->get_removal_error(player); return false; } else { // no way at all ... return true; } } if(const char *err = w->get_removal_error(player)){ msg = err; if (lt) gr->obj_add(lt); return false; } } wt = w->get_desc()->get_finance_waytype(); sint32 cost_sum = gr->weg_entfernen(w->get_waytype(), true); player_t::book_construction_costs(player, -cost_sum, k, wt); } else { // remove ways and tunnel if( weg_t *weg = gr->get_weg_nr(0) ) { gr->remove_everything_from_way(player, weg->get_waytype(), ribi_t::none); } // tunnel without way: delete anything else if( !gr->hat_wege() ) { gr->obj_loesche_alle(player); if (gr->get_typ() == boden_t::tunnelboden && gr->ist_karten_boden()) { grund_t* gr_new = new boden_t(gr->get_pos(), gr->get_grund_hang()); welt->access(gr_new->get_pos().get_2d())->kartenboden_setzen(gr_new); gr = gr_new; } } } if (lt) gr->obj_add(lt); // remove empty tile if( !gr->ist_karten_boden() && gr->obj_count()==0 ) { // unmark kartenboden (is marked during underground mode deletion) welt->lookup_kartenboden(k)->clear_flag(grund_t::marked); // remove upper or lower ground welt->access(k)->boden_entfernen(gr); delete gr; } return true; } const char *tool_remover_t::work( player_t *player, koord3d pos ) { DBG_MESSAGE("tool_remover()","at %d,%d", pos.x, pos.y); obj_t::typ type = obj_t::undefined; if (default_param) { int t = atoi(default_param); if (t != 0 && -1 <= t && t <= 200) { type = (obj_t::typ)t; } } const char *fail = NULL; if(!tool_remover_intern(player, pos, type, fail)) { return fail; } // must recalc neighbourhood for slopes etc. if(pos.x>1) { welt->lookup_kartenboden(pos.get_2d()+koord::west)->calc_image(); } if(pos.y>1) { welt->lookup_kartenboden(pos.get_2d()+koord::north)->calc_image(); } if(pos.xget_size().x-1) { welt->lookup_kartenboden(pos.get_2d()+koord::east)->calc_image(); } if(pos.yget_size().y-1) { welt->lookup_kartenboden(pos.get_2d()+koord::south)->calc_image(); } return NULL; } const char *tool_change_owner_t::work( player_t *pl, koord3d pos ) { grund_t *gr = welt->lookup(pos); minivec_tplinfo; if(gr) { int idx=0, new_pl_id; if(!default_param || !*default_param) { // needs default string with "new_player_id[,tile_index]" return ""; } else { new_pl_id = atoi(default_param); if (const char* c = strchr(default_param, 'r')) { idx = atoi(c+1); } } player_t* new_pl = welt->get_player(new_pl_id); if( obj_t *obj = gr->obj_bei(idx)) { if(obj->is_moving()) { // cannot change ownership of moving stuff return "Der Besitzer erlaubt das Entfernen nicht"; } if(player_t::check_owner(pl,obj->get_owner())) { // we may have mainenance waytype_t wt = obj->get_waytype(); sint64 maintenance = 0; if (bruecke_t* b = dynamic_cast(obj)) { maintenance = b->get_desc()->get_maintenance(); wt = b->get_desc()->get_finance_waytype(); } else if (gebaeude_t* g = dynamic_cast(obj)) { // incudes depots maintenance = g->get_tile()->get_desc()->get_maintenance(welt); } else if (tunnel_t* t = dynamic_cast(obj)) { maintenance = t->get_desc()->get_maintenance(); wt = t->get_desc()->get_finance_waytype(); } else if (weg_t* w = dynamic_cast(obj)) { maintenance = w->get_desc()->get_maintenance(); wt = w->get_desc()->get_finance_waytype(); } else if (roadsign_t *rs = dynamic_cast(obj)) { maintenance = rs->get_desc()->get_maintenance(); wt = w->get_desc()->get_finance_waytype(); } if(maintenance) { // transfer maintenance player_t::add_maintenance(obj->get_owner(), -maintenance, wt); player_t::add_maintenance(new_pl, maintenance, wt); } obj->set_owner(new_pl); return NULL; } return "Der Besitzer erlaubt das Entfernen nicht"; } return ""; } return "No suitable ground!"; } const char *tool_raise_lower_base_t::move( player_t *player, uint16 buttonstate, koord3d pos ) { CHECK_FUNDS(); const char *result = NULL; if( buttonstate==1 ) { char buf[16]; if(!is_dragging) { drag_height = get_drag_height(pos.get_2d()); } is_dragging = true; sprintf( buf, "%i", drag_height ); default_param = buf; if (env_t::networkmode) { // queue tool for network nwc_tool_t *nwc = new nwc_tool_t(player, this, pos, welt->get_steps(), welt->get_map_counter(), false); network_send_server(nwc); } else { result = work( player, pos ); } default_param = NULL; } return result; } const char* tool_raise_lower_base_t::drag(player_t *player, koord k, sint16 height, int &n) { if( !welt->is_within_grid_limits(k) ) { return ""; } const char* err = NULL; // dragging may be going up or down! while( welt->lookup_hgt(k) < height && height <= welt->get_max_allowed_height() ) { int diff = welt->grid_raise( player, k, err ); if( diff == 0 ) { break; } n += diff; } // when going down need to check here we will not be going below sea level // cannot rely on check within lower as water height can be recalculated while( height >= welt->get_water_hgt(k) && welt->lookup_hgt(k) > height && height >= welt->get_min_allowed_height() ) { int diff = welt->grid_lower( player, k, err ); if( diff == 0 ) { break; } n += diff; } return err; //height == welt->lookup_hgt(k); } bool tool_raise_lower_base_t::check_dragging() { // reset dragging if( is_dragging && strempty(default_param) ) { is_dragging = false; return false; } return true; } sint16 tool_raise_t::get_drag_height(koord k) { const grund_t *gr = welt->lookup_kartenboden_gridcoords(k); return gr->get_hoehe(welt->get_corner_to_operate(k)) + 1; } const char *tool_raise_t::check_pos(player_t *, koord3d pos ) { // check for underground mode if( is_dragging && drag_height-1 > grund_t::underground_level ) { is_dragging = false; return ""; } if( !welt->is_within_grid_limits(pos.get_2d()) ) { return ""; } sint8 h = (sint8) get_drag_height(pos.get_2d()); if( h > grund_t::underground_level ) { return "Terraforming not possible\nhere in underground view"; } return NULL; } const char *tool_raise_t::work(player_t* player, koord3d pos ) { if (!check_dragging()) { return NULL; } const char* err = NULL; koord k = pos.get_2d(); CHECK_FUNDS(); if(welt->is_within_grid_limits(k)) { const sint8 hgt = (sint8) get_drag_height(k); if( hgt <= welt->get_max_allowed_height() ) { int n = 0; // tiles changed if( !strempty(default_param) ) { // called by dragging or by AI err = drag(player, k, atoi(default_param), n); } else { n = welt->grid_raise(player, k, err); } if(n>0) { player_t::book_construction_costs(player, welt->get_settings().cst_alter_land * n, k, ignore_wt); } return err == NULL ? (n ? NULL : "") : (*err == 0 ? NOTICE_TILE_FULL : err); } else { // no mountains higher than welt->get_maximumheight() ... return "Maximum tile height difference reached."; } } return "Zu nah am Kartenrand"; } sint16 tool_lower_t::get_drag_height(koord k) { const grund_t *gr = welt->lookup_kartenboden_gridcoords(k); return gr->get_hoehe(welt->get_corner_to_operate(k)) - 1; } const char *tool_lower_t::check_pos( player_t *, koord3d pos ) { // check for underground mode if (is_dragging && drag_height+1 > grund_t::underground_level) { is_dragging = false; return ""; } if (! welt->is_within_grid_limits(pos.get_2d())) { return ""; } sint8 h = (sint8) get_drag_height(pos.get_2d()); if (h > grund_t::underground_level) { return "Terraforming not possible\nhere in underground view"; } return NULL; } const char *tool_lower_t::work( player_t *player, koord3d pos ) { if (!check_dragging()) { return NULL; } const char* err = NULL; koord k = pos.get_2d(); CHECK_FUNDS(); if(welt->is_within_grid_limits(k)) { const sint8 hgt = (sint8) get_drag_height(k); if( hgt >= welt->get_water_hgt( k ) ) { int n = 0; // tiles changed if (!strempty(default_param)) { // called by dragging or by AI err = drag(player, k, atoi(default_param), n); } else { n = welt->grid_lower(player, k, err); } if(n>0) { player_t::book_construction_costs(player, welt->get_settings().cst_alter_land * n, k, ignore_wt); } return err == NULL ? (n ? NULL : "") : (*err == 0 ? NOTICE_TILE_FULL : err); } else { // below water level return ""; } } return "Zu nah am Kartenrand"; } const char *tool_setslope_t::check_pos( player_t *, koord3d pos) { grund_t *gr1 = welt->lookup(pos); if(gr1) { // check for underground mode if( grund_t::underground_mode == grund_t::ugm_all && !gr1->ist_tunnel() ) { return "Terraforming not possible\nhere in underground view"; } } else { return ""; } return NULL; } const char *tool_restoreslope_t::check_pos( player_t *, koord3d pos) { grund_t *gr1 = welt->lookup(pos); if(gr1) { // check for underground mode if( grund_t::underground_mode == grund_t::ugm_all && !gr1->ist_tunnel() ) { return "Terraforming not possible\nhere in underground view"; } } else { return ""; } return NULL; } void tool_setslope_t::rdwr_custom_data(memory_rw_t *packet) { tool_t::rdwr_custom_data(packet); packet->rdwr_bool(old_slope_compatibility_mode); } const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, int new_slope, bool old_slope_compatibility, bool just_check ) { if( !ground_desc_t::double_grounds && !old_slope_compatibility ) { // do not build double slopes if no graphics are available if (slope_t::max_diff(new_slope) > 1) { return ""; // invalid parameter } } if( !ground_desc_t::double_grounds && old_slope_compatibility ) { // translate old single slope parameter to new double slope if( 0 < new_slope && new_slope < ALL_UP_SLOPE_SINGLE ) { new_slope = slope_from_slope4(slope4_t(new_slope), 1); } else { switch( new_slope ) { case ALL_UP_SLOPE: case ALL_UP_SLOPE_SINGLE: new_slope = ALL_UP_SLOPE; break; case ALL_DOWN_SLOPE: case ALL_DOWN_SLOPE_SINGLE: new_slope = ALL_DOWN_SLOPE; break; case RESTORE_SLOPE: case RESTORE_SLOPE_SINGLE: new_slope = RESTORE_SLOPE; break; default: return ""; // invalid parameter } } } bool ok = false; grund_t *gr1 = welt->lookup(pos); if( gr1 ) { if( gr1->ist_im_tunnel() ) { switch( new_slope ) { case ALL_UP_SLOPE: case ALL_UP_SLOPE_SINGLE: case ALL_DOWN_SLOPE: case ALL_DOWN_SLOPE_SINGLE: case RESTORE_SLOPE: case RESTORE_SLOPE_SINGLE: break; default: return "Only up and down movement in the underground!"; // invalid parameter } } koord k(pos.get_2d()); sint8 water_hgt = welt->get_water_hgt( k ); const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; if(new_slope==ALL_DOWN_SLOPE && pos.z-1get_min_allowed_height() ) { return "Maximum tile height difference reached."; } // at least a pixel away from the border? if( pos.z < water_hgt && !gr1->ist_tunnel() ) { return "Maximum tile height difference reached."; } if( new_slope==RESTORE_SLOPE && !(gr1->get_typ()==grund_t::boden || gr1->get_typ()==grund_t::wasser) ) { return NOTICE_UNSUITABLE_GROUND; } // finally: empty enough if( gr1->get_grund_hang()!=gr1->get_weg_hang() || gr1->get_halt().is_bound() || gr1->kann_alle_obj_entfernen(player) || gr1->find() || gr1->get_depot() || (gr1->get_leitung() && gr1->hat_wege()) || gr1->get_weg(air_wt) || gr1->find() || gr1->get_typ()==grund_t::brueckenboden) { return NOTICE_TILE_FULL; } if( !welt->is_within_limits(k+koord(1,1)) || !welt->is_within_limits(k+koord(-1,-1))) { return "Zu nah am Kartenrand"; } // slopes may affect the position and the total height! koord3d new_pos = pos; if( gr1->hat_wege() || gr1->get_leitung() ) { // check the resulting slope ribi_t::ribi ribis = 0; if( gr1->hat_wege()) { ribis |= gr1->get_weg_nr(0)->get_ribi_unmasked(); if( gr1->get_weg_nr(1) ) { ribis |= gr1->get_weg_nr(1)->get_ribi_unmasked(); } } if( gr1->get_leitung()) { ribis |= gr1->get_leitung()->get_ribi(); } if( new_slope==RESTORE_SLOPE || !ribi_t::is_single(ribis) || (new_slopeget_weg_hang()==slope_t::flat ) { new_slope = slope_type(ribis); } else if( gr1->get_weg_hang() == slope_type(ribis) ) { // check that way_desc supports such steep slopes if( (gr1->get_weg_nr(0) && !gr1->get_weg_nr(0)->get_desc()->has_double_slopes()) || (gr1->get_weg_nr(1) && !gr1->get_weg_nr(1)->get_desc()->has_double_slopes()) || (gr1->get_leitung() && !gr1->get_leitung()->get_desc()->has_double_slopes()) ) { return NOTICE_TILE_FULL; } new_slope = slope_type(ribis) * 2; } else if( gr1->get_weg_hang() == slope_type( ribi_t::backward(ribis) ) * 2 ) { new_pos.z++; if( welt->lookup(new_pos) ) { return NOTICE_TILE_FULL; } new_slope = slope_type( ribi_t::backward(ribis) ); } else if( gr1->get_weg_hang() != slope_type( ribi_t::backward(ribis) ) ) { return "Maximum tile height difference reached."; } } else if( new_slope==ALL_DOWN_SLOPE ) { if( gr1->get_grund_hang()==slope_type(ribis) ) { // do not lower tiles to sea if( pos.z == water_hgt && !gr1->ist_tunnel() ) { return NOTICE_TILE_FULL; } } else if( gr1->get_grund_hang() == slope_type(ribis) * 2 ) { if( pos.z == water_hgt && !gr1->ist_tunnel() ) { return NOTICE_TILE_FULL; } new_slope = slope_type(ribis); } else if( gr1->get_grund_hang() == slope_t::flat ) { new_slope = slope_type( ribi_t::backward(ribis) ); new_pos.z--; if( welt->lookup(new_pos) ) { return NOTICE_TILE_FULL; } } else if( gr1->get_grund_hang() == slope_type( ribi_t::backward(ribis) ) ) { // check that way_desc supports such steep slopes if( (gr1->get_weg_nr(0) && !gr1->get_weg_nr(0)->get_desc()->has_double_slopes()) || (gr1->get_weg_nr(1) && !gr1->get_weg_nr(1)->get_desc()->has_double_slopes()) || (gr1->get_leitung() && !gr1->get_leitung()->get_desc()->has_double_slopes()) ) { return NOTICE_TILE_FULL; } new_slope = slope_type( ribi_t::backward(ribis) ) * 2; new_pos.z--; if( welt->lookup(new_pos) ) { return NOTICE_TILE_FULL; } } else { return "Maximum tile height difference reached."; } } } if( new_slope == ALL_DOWN_SLOPE || new_slope == RESTORE_SLOPE ) { if( new_slope == RESTORE_SLOPE ) { // special action: set to natural slope sint8 min_hgt; new_slope = welt->recalc_natural_slope( k, min_hgt ); new_pos = koord3d( k, min_hgt ); DBG_MESSAGE("natural_slope","%i",new_slope); } else { new_slope = slope_t::flat; // is more intuitive: if there is a slope, first downgrade it if( gr1->get_grund_hang() == 0 ) { new_pos.z--; } } // now prevent being lowered below neighbouring water sint8 water_table = (water_hgt >= (gr1->get_hoehe() + (gr1->get_grund_hang() ? 1 : 0))) ? water_hgt : welt->get_groundwater() - 4; sint8 min_neighbour_height = gr1->get_hoehe(); for( sint16 i = 0 ; i < 8 ; i++ ) { const koord neighbour = k + koord::neighbours[i]; if( welt->is_within_grid_limits( neighbour ) ) { grund_t *gr2 = welt->lookup_kartenboden( neighbour ); const sint8 water_hgt_neighbour = welt->get_water_hgt( neighbour ); if( gr2 && (water_hgt_neighbour >= (gr2->get_hoehe() + (gr2->get_grund_hang() ? 1 : 0))) ) { water_table = max( water_table, water_hgt_neighbour ); } if( gr2 && gr2->get_hoehe() < min_neighbour_height ) { min_neighbour_height = gr2->get_hoehe(); } } } if( water_table>new_pos.z || (water_table == new_pos.z && min_neighbour_height < new_pos.z) ) { // do not lower tiles when it will be below water level return NOTICE_TILE_FULL; } welt->set_water_hgt_nocheck( k, water_table ); water_hgt = water_table; } else if( new_slope == ALL_UP_SLOPE ) { new_slope = slope_t::flat; new_pos.z++; } // already some ground here (tunnel, bridge, monorail?) if( new_pos.z != pos.z && welt->lookup(new_pos) != NULL ) { return NOTICE_TILE_FULL; } // check for grounds above / below if( new_pos.z >= pos.z ) { grund_t *gr2 = welt->lookup( new_pos + koord3d(0, 0, 1) ); if( !gr2 ) { gr2 = welt->lookup( new_pos + koord3d(0, 0, 2) ); } if( !gr2 && welt->get_settings().get_way_height_clearance()==2 && (gr1->hat_wege() || gr1->get_leitung()) ) { gr2 = welt->lookup( new_pos + koord3d(0, 0, 3) ); } // slope may alter amount of clearance required if( gr2 && gr2->get_pos().z - new_pos.z + slope_t::min_diff( gr2->get_weg_hang(), new_slope ) < welt->get_settings().get_way_height_clearance() ) { return NOTICE_TILE_FULL; } } if( new_pos.z <= pos.z ) { grund_t *gr2 = welt->lookup( new_pos + koord3d(0, 0, -1) ); if( !gr2 ) { gr2 = welt->lookup( new_pos + koord3d(0, 0, -2) ); } if( !gr2 && welt->get_settings().get_way_height_clearance()==2 ) { gr2 = welt->lookup( new_pos + koord3d(0, 0, -3) ); } // slope may alter amount of clearance required if( gr2 && new_pos.z - gr2->get_pos().z + slope_t::min_diff( new_slope, gr2->get_weg_hang() ) < welt->get_settings().get_way_height_clearance() ) { return NOTICE_TILE_FULL; } } // check, if action is valid ... const sint16 hgt=new_pos.z; // maximum difference check with tiles to north, south east and west const sint8 test_hgt = hgt+(new_slope!=0); if( gr1->get_typ()==grund_t::boden ) { for( sint16 i = 0 ; i < 4 ; i++ ) { const koord neighbour = k + koord::nesw[i]; const grund_t *gr_neighbour=welt->lookup_kartenboden(neighbour); if(gr_neighbour) { const sint16 gr_neighbour_hgt=gr_neighbour->get_hoehe() + (new_slope==ALL_DOWN_SLOPE && gr_neighbour->get_grund_hang()? 1 : 0); const sint8 diff_from_ground = abs(gr_neighbour_hgt-test_hgt); if( diff_from_ground > 2 * max_hdiff ) { return "Maximum tile height difference reached."; } } } } // ok, now we set the slope ... ok = (new_pos!=pos); bool slope_changed = new_slope!=gr1->get_grund_hang(); ok |= slope_changed; if(ok) { // check if clear if( gr1->kann_alle_obj_entfernen(player) ) { return NOTICE_TILE_FULL; } // check way ownership if(gr1->hat_wege()) { if(gr1->get_weg_nr(0)->get_removal_error(player)!=NULL) { return NOTICE_TILE_FULL; } if(gr1->has_two_ways() && gr1->get_weg_nr(1)->get_removal_error(player)!=NULL) { return NOTICE_TILE_FULL; } } // check funds settings_t const& s = welt->get_settings(); sint64 const cost = new_slope == RESTORE_SLOPE ? s.cst_alter_land : s.cst_set_slope; if( !player->can_afford(cost) ) { return NOTICE_INSUFFICIENT_FUNDS; } // one last check if ( gr1->is_water() && (new_pos.z > water_hgt || new_slope != 0) ) { // we have to build underwater hill first if( !welt->can_flatten_tile( player, k, water_hgt, false, true ) ) { return NOTICE_TILE_FULL; } } // all checks passed if (just_check) { return NULL; } // ok, it was a success if( !gr1->is_water() && new_slope == 0 && hgt == water_hgt && gr1->get_typ() != grund_t::tunnelboden ) { // now water gr1->obj_loesche_alle(player); welt->access(k)->kartenboden_setzen( new wasser_t(new_pos) ); gr1 = welt->lookup_kartenboden(k); } else if( gr1->is_water() && (new_pos.z > water_hgt || new_slope != 0) ) { // build underwater hill first if( !welt->flatten_tile( player, k, water_hgt, false, true ) ) { return NOTICE_TILE_FULL; } gr1->obj_loesche_alle(player); welt->access(k)->kartenboden_setzen( new boden_t(new_pos,new_slope) ); gr1 = welt->lookup_kartenboden(k); welt->set_water_hgt_nocheck(k, welt->get_groundwater()-4); } else { gr1->set_grund_hang(new_slope); gr1->set_pos(new_pos); gr1->clear_flag(grund_t::marked); gr1->set_flag(grund_t::dirty); // update new positions if changed if( new_pos!=pos ) { for( int i=0; iobj_count(); i++ ) { gr1->obj_bei(i)->set_pos( new_pos ); } } // correct tree offsets if slope has changed if( slope_changed ) { for( int i=0; iobj_count(); i++ ) { baum_t *tree = obj_cast(gr1->obj_bei(i)); if (tree) { tree->recalc_off(); } } } if( !gr1->ist_karten_boden() ) { gr1->calc_image(); } } // if there is a powerline here we need to treat it as newly built as it may connect to neighbours leitung_t *lt = gr1->get_leitung(); if( lt ) { // remove maintenance for existing powerline player_t::add_maintenance(lt->get_owner(), -lt->get_desc()->get_maintenance(), powerline_wt); lt->finish_rd(); } if( gr1->ist_karten_boden() ) { if( new_slope!=slope_t::flat ) { // no lakes on slopes ... groundobj_t *obj = gr1->find(); if( obj && obj->get_desc()->get_phases()!=16 ) { obj->cleanup(player); delete obj; } // connect canals to sea if( gr1->get_hoehe() == water_hgt && gr1->hat_weg(water_wt) ) { grund_t *sea = welt->lookup_kartenboden(k - koord( ribi_type(new_slope ) )); if (sea && sea->is_water()) { gr1->weg_erweitern(water_wt, ribi_t::backward(ribi_type(new_slope))); sea->calc_image(); } } } // recalc slope walls on neighbours for(int y=-1; y<=1; y++) { for(int x=-1; x<=1; x++) { grund_t *gr = welt->lookup_kartenboden(k+koord(x,y)); gr->calc_image(); } } // correct the grid height if( gr1->is_water() ) { sint8 grid_hgt = min( water_hgt, welt->lookup_hgt( k ) ); welt->set_grid_hgt_nocheck(k, grid_hgt ); } else { welt->set_grid_hgt_nocheck(k, gr1->get_hoehe()+ corner_nw(gr1->get_grund_hang()) ); } minimap_t::get_instance()->calc_map_pixel(k); welt->calc_climate( k, true ); } player_t::book_construction_costs(player, cost, k, ignore_wt); } // update limits if( welt->min_height > gr1->get_hoehe() ) { welt->min_height = gr1->get_hoehe(); } else if( welt->max_height < gr1->get_hoehe() ) { welt->max_height = gr1->get_hoehe(); } } return ok ? NULL : ""; } // set marker const char *tool_marker_t::work( player_t *player, koord3d pos ) { grund_t *gr = welt->lookup_kartenboden(pos.get_2d()); if (gr) { if(!gr->get_text()) { const obj_t* thing = gr->obj_bei(0); if(thing == NULL || player == NULL || thing->get_owner() == player || (player_t::check_owner(thing->get_owner(), player) && thing->get_typ() != obj_t::gebaeude)) { gr->obj_add(new label_t(gr->get_pos(), player, default_param ? default_param : "\0")); if (can_use_gui()) { gr->find()->show_info(); } return NULL; } } } return "Das Feld gehoert\neinem anderen Spieler\n"; } // show/repair blocks bool tool_clear_reservation_t::init( player_t * ) { if (can_use_gui()) { schiene_t::show_reservations = true; welt->set_dirty(); } return true; } bool tool_clear_reservation_t::exit( player_t * ) { if (can_use_gui()) { schiene_t::show_reservations = false; welt->set_dirty(); } return true; } bool tool_clear_reservation_t::is_selected() const { if (welt->get_tool(welt->get_active_player_nr())==this) { return true; } return false; } const char *tool_clear_reservation_t::work( player_t *, koord3d pos ) { grund_t *gr = welt->lookup(pos); if(gr && gr->hat_wege()) { // first start route search of the convoi here for( uint8 i = 0; i < gr->obj_count(); i++ ) { uint8 typ = gr->obj_bei(i)->get_typ(); if( typ >= obj_t::road_vehicle && typ <= obj_t::air_vehicle ) { vehicle_t *veh = dynamic_cast(gr->obj_bei( i )); if( veh->get_convoi() ) { uint16 state = veh->get_convoi()->get_state(); if( state > convoi_t::EDIT_SCHEDULE ) { veh->get_convoi()->set_state(convoi_t::ROUTING_1); } } } } // then unreserve the current tile for(unsigned wnr=0; wnr<2; wnr++ ) { schiene_t const* const w = obj_cast(gr->get_weg_nr(wnr)); // is this a reserved track? if(w!=NULL && w->is_reserved()) { /* now we do a very crude procedure: * - we search all ways for reservations of this convoi and remove them * - we set the convoi state to ROUTING_1; it must reserve again its ways then */ const waytype_t waytype = w->get_waytype(); const convoihandle_t cnv = w->get_reserved_convoi(); if(cnv->get_state()==convoi_t::DRIVING) { // reset driving state cnv->suche_neue_route(); } for(weg_t* const w : weg_t::get_alle_wege()) { if (w->get_waytype() == waytype) { schiene_t* const sch = obj_cast(w); if (sch->get_reserved_convoi() == cnv) { vehicle_t& v = *cnv->front(); if (!gr->suche_obj(v.get_typ())) { // force free sch->unreserve(&v); } } } } } } } return NULL; } // transformer for electricity supply const char* tool_transformer_t::get_tooltip(const player_t *) const { settings_t const& s = welt->get_settings(); return tooltip_with_price_maintenance(welt, "Build drain", s.cst_transformer, -s.cst_maintain_transformer); } image_id tool_transformer_t::get_icon(player_t*) const { return way_builder_t::waytype_available( powerline_wt, welt->get_timeline_year_month() ) ? icon : IMG_EMPTY; } bool tool_transformer_t::init( player_t *) { return way_builder_t::waytype_available( powerline_wt, welt->get_timeline_year_month() ); } const char *tool_transformer_t::check_pos( player_t *, koord3d pos ) { if(grund_t::underground_mode == grund_t::ugm_all && env_t::networkmode) { // clients cannot guess at which height transformer should be build return "Cannot built this station/building\nin underground mode here."; } if(grund_t::underground_mode == grund_t::ugm_level) { // only above or directly under surface // taking into account way clearance requirements grund_t *gr = welt->lookup_kartenboden(pos.get_2d()); return (gr->get_pos() == pos || gr->get_hoehe() == grund_t::underground_level + welt->get_settings().get_way_height_clearance()) ? NULL : ""; } return NULL; } const char *tool_transformer_t::work( player_t *player, koord3d pos ) { DBG_MESSAGE("tool_transformer_t()","called on %d,%d", pos.x, pos.y); koord k(pos.get_2d()); grund_t *gr = welt->lookup_kartenboden(k); if( !welt->get_settings().get_allow_underground_transformers() && pos.z!=gr->get_hoehe() ) { // no underground transformers allowed return "Cannot built this station/building\nin underground mode here."; } bool underground = false; fabrik_t *fab = NULL; // full underground mode: coordinate is on ground, adjust it to one level below ground // not possible in network mode! if (!env_t::networkmode && grund_t::underground_mode == grund_t::ugm_all) { pos = gr->get_pos() - koord3d( 0, 0, welt->get_settings().get_way_height_clearance() ); } // search for factory // must be independent of network mode if (gr->get_pos().z <= pos.z) { fab = leitung_t::suche_fab_4(k); } else if( gr->get_pos().z == pos.z+welt->get_settings().get_way_height_clearance() ) { fab = fabrik_t::get_fab( k); underground = true; } if( !fab ) { return "Transformer only next to factory!"; } if( fab->is_transformer_connected() ) { return "Only one transformer per factory!"; } // underground: first build tunnel tile at coordinate pos if(underground) { if(gr->is_water()) { return "Transformers cannot connect to factories on water."; } if(welt->lookup(pos)) { return NOTICE_TILE_FULL; } if( welt->get_settings().get_way_height_clearance()==2 && welt->lookup(pos + koord3d( 0, 0, 1 )) ) { return NOTICE_TILE_FULL; } const tunnel_desc_t *tunnel_desc = tunnel_builder_t::get_tunnel_desc(powerline_wt, 0, 0); if( tunnel_desc==NULL ) { return "Cannot built this station/building\nin underground mode here."; } tunnelboden_t* tunnel = new tunnelboden_t(pos, slope_t::flat); welt->access(k)->boden_hinzufuegen(tunnel); tunnel->obj_add(new tunnel_t(pos, player, tunnel_desc)); player_t::add_maintenance( player, tunnel_desc->get_maintenance(), tunnel_desc->get_finance_waytype() ); gr = tunnel; } else { // above ground: check for clear tile if(gr->get_grund_hang()!=0 || !gr->ist_natur()) { return "Transformer only on flat bare land!"; } // remove everything on that spot if(const char *fail = gr->kann_alle_obj_entfernen(player)) { return fail; } gr->obj_loesche_alle(player); } // transformer will be build on tile pointed to by gr // build source or drain depending on factory type if(fab->get_desc()->is_electricity_producer()) { pumpe_t *p = new pumpe_t(gr->get_pos(), player); gr->obj_add( p ); p->finish_rd(); } else { senke_t *s = new senke_t(gr->get_pos(), player); gr->obj_add(s); s->finish_rd(); } return NULL; // ok } /** * found a new city */ const char *tool_add_city_t::work( player_t *player, koord3d pos ) { // check funds const sint64 cost = welt->get_settings().cst_found_city; if( !player->can_afford(cost) ) { return NOTICE_INSUFFICIENT_FUNDS; } koord k(pos.get_2d()); grund_t *gr = welt->lookup_kartenboden(k); if (!gr) { return ""; } if(!gr->ist_natur() || gr->is_water() || gr->get_grund_hang() != slope_t::flat) { return "Cities can only be built on flat empty ground!"; } if (hausbauer_t::get_special(0, building_desc_t::townhall, welt->get_timeline_year_month(), false, welt->get_climate( k )) == NULL ) { return "No suitable townhall available for this climate!"; } gebaeude_t const* const gb = obj_cast(gr->first_no_way_obj()); if (gb && gb->is_townhall()) { dbg->warning("tool_add_city()", "Already a city here"); return NOTICE_TILE_FULL; } // always start with 1/10 citizens int citizens = (int)(welt->get_settings().get_mean_citizen_count() * 0.09); const building_desc_t* desc = NULL; sint16 rotation = -1; if (!strempty(default_param)) { char* building = strdup(default_param); citizens = atoi(building); char* p = strchr(building, ','); if (p) { p++; char* desc_name = p; if ((p = strchr(p, ',')) != NULL) { *p++ = 0; if (isdigit(*p)) { rotation = atoi(p); } } *p = 0; desc = hausbauer_t::get_desc(desc_name); } free(building); } stadt_t *stadt = welt->create_city(k, citizens, desc, rotation); if (!stadt) { return NOTICE_UNSUITABLE_GROUND; } stadt->finish_rd(); stadt->verbinde_fabriken(); player_t::book_construction_costs(player, cost, k, ignore_wt); minimap_t::get_instance()->calc_map(); return NULL; } // buy a house const char *tool_buy_house_t::work( player_t *player, koord3d pos) { if ( player == welt->get_public_player() ) { return ""; } grund_t* gr = welt->lookup_kartenboden(pos.get_2d()); if(!gr || gr->hat_wege() || gr->get_halt().is_bound()) { return ""; } // since buildings can have more than one tile, we must handle them together gebaeude_t* gb = gr->find(); if( gb==NULL ){ return ""; } gb = gb->get_first_tile(); if( !gb->is_city_building() || !player_t::check_owner(gb->get_owner(),player) ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } if( gb->get_owner()==player ) { // I bought this already ... return ""; } player_t* old_owner = gb->get_owner(); const building_desc_t* bdsc = gb->get_tile()->get_desc(); vector_tpl gb_tiles; gb->get_tile_list( gb_tiles ); for(grund_t* gr : gb_tiles ) { gebaeude_t* gb_part = gr->find(); sint32 const maint = welt->get_settings().maint_building * bdsc->get_level(); player_t::add_maintenance( old_owner, -maint, gb_part->get_waytype() ); player_t::add_maintenance( player, +maint, gb_part->get_waytype() ); gb_part->set_owner( player ); player_t::book_construction_costs( player, -maint, gr->get_pos().get_2d(), gb_part->get_waytype() ); } return NULL; } /** * change city size */ bool tool_change_city_size_t::init( player_t * ) { int delta = 0; if (!default_param || sscanf(default_param, "%d", &delta) != 1) { return false; } cursor = delta > 0 ? tool_t::general_tool[TOOL_RAISE_LAND]->cursor : tool_t::general_tool[TOOL_LOWER_LAND]->cursor; return true; } const char *tool_change_city_size_t::work( player_t *, koord3d pos ) { stadt_t* city = NULL; if (grund_t *gr=welt->lookup_kartenboden(pos.get_2d())) { if (gebaeude_t* gb = gr->find()) { city = gb->get_stadt(); } } if (!city) { city = welt->find_nearest_city(pos.get_2d()); } if(city) { const int delta = std::atoi(default_param); city->change_size( delta ); // update the links from other cities to this city for(stadt_t* const c : welt->get_cities()) { c->remove_target_city(city); c->add_target_city(city); } return NULL; } return ""; } /** * change climate */ const char *tool_set_climate_t::get_tooltip(player_t const*) const { char temp[1024]; sprintf( temp, translator::translate( "Set tile climate %s" ), translator::translate( ground_desc_t::get_climate_name_from_bit((climate)atoi(default_param)) ) ); return tooltip_with_price( temp, welt->get_settings().cst_alter_climate ); } uint8 tool_set_climate_t::is_valid_pos(player_t *player, const koord3d &, const char *& error, const koord3d &) { error = NULL; // no dragging in networkmode but for admin return env_t::networkmode && !player->is_public_service() ? 1 /*no dragging*/ : 2 /*dragging allowed*/; } void tool_set_climate_t::mark_tiles(player_t *, const koord3d &start, const koord3d &end) { koord k1, k2; k1.x = start.x < end.x ? start.x : end.x; k1.y = start.y < end.y ? start.y : end.y; k2.x = start.x + end.x - k1.x; k2.y = start.y + end.y - k1.y; koord k; for( k.x = k1.x; k.x <= k2.x; k.x++ ) { for( k.y = k1.y; k.y <= k2.y; k.y++ ) { grund_t *gr = welt->lookup_kartenboden( k ); zeiger_t *marker = new zeiger_t(gr->get_pos(), NULL ); const uint8 grund_hang = gr->get_grund_hang(); const uint8 weg_hang = gr->get_weg_hang(); const uint8 hang = max( corner_sw(grund_hang), corner_sw(weg_hang) ) + 3 * max( corner_se(grund_hang), corner_se(weg_hang) ) + 9 * max( corner_ne(grund_hang), corner_ne(weg_hang) ) + 27 * max( corner_nw(grund_hang), corner_nw(weg_hang) ); uint8 back_hang = (hang % 3) + 3 * ((uint8)(hang / 9)) + 27; marker->set_foreground_image( ground_desc_t::marker->get_image( grund_hang % 27 ) ); marker->set_image( ground_desc_t::marker->get_image( back_hang ) ); marker->mark_image_dirty( marker->get_image(), 0 ); gr->obj_add( marker ); marked.insert( marker ); } } } const char *tool_set_climate_t::do_work( player_t *player, const koord3d &start, const koord3d &end ) { int value = 0; if (!default_param || sscanf(default_param, "%d", &value) != 1) { return ""; } else if (value < water_climate || value >= MAX_CLIMATES) { return ""; } int n = 0; // tiles altered climate cl = (climate)value; koord k1, k2; if( end == koord3d::invalid ) { k1.x = k2.x = start.x; k1.y = k2.y = start.y; } else { k1.x = start.x < end.x ? start.x : end.x; k1.y = start.y < end.y ? start.y : end.y; k2.x = start.x + end.x - k1.x; k2.y = start.y + end.y - k1.y; } koord k; for( k.x = k1.x; k.x <= k2.x; k.x++ ) { for( k.y = k1.y; k.y <= k2.y; k.y++ ) { if( grund_t *gr=welt->lookup_kartenboden(k) ) { if( cl != water_climate ) { bool ok = true; if( gr->is_water() ) { const sint8 hgt = welt->lookup_hgt(k); ok = welt->get_water_hgt(k) == hgt && welt->is_plan_height_changeable( k.x, k.y ); // check s, se, e - these must not be deep water! for( int i = 3 ; i < 6 && ok ; i++ ) { koord k_neighbour(k + koord::neighbours[i]); if( welt->is_within_grid_limits(k_neighbour) ) { ok = welt->lookup_hgt(k_neighbour) >= hgt; } } if( ok ) { gr->obj_loesche_alle( NULL ); welt->set_water_hgt_nocheck( k, hgt - 1 ); welt->access(k)->correct_water(); } } if( ok ) { welt->set_climate( k, cl, true ); minimap_t::get_instance()->calc_map_pixel( k ); n ++; } } else if( !gr->is_water() && gr->get_grund_hang() == slope_t::flat && welt->is_plan_height_changeable( k.x, k.y ) ) { bool ok = true; for( int i = 0 ; i < 8; i++ ) { grund_t *gr2 = welt->lookup_kartenboden( k + koord::neighbours[i] ); if( gr2 && ok ) { ok = gr2->get_pos().z >= gr->get_pos().z; } } if( ok ) { gr->obj_loesche_alle( NULL ); welt->set_water_hgt_nocheck( k, gr->get_pos().z ); welt->access(k)->correct_water(); welt->set_climate( k, water_climate, true ); minimap_t::get_instance()->calc_map_pixel( k ); n ++; } } } } } if(n>0) { player_t::book_construction_costs(player, welt->get_settings().cst_alter_climate * n, k, ignore_wt); } return NULL; } /** * change water height */ bool tool_change_water_height_t::init( player_t *player ) { int delta = 0; if (!default_param || sscanf(default_param, "%d", &delta) != 1) { return false; } cursor = delta > 0 ? tool_t::general_tool[TOOL_RAISE_LAND]->cursor : tool_t::general_tool[TOOL_LOWER_LAND]->cursor; return !env_t::networkmode || player->is_public_service(); } const char *tool_change_water_height_t::work( player_t *, koord3d pos ) { if( pos == koord3d::invalid ) { return "Cannot alter water"; } // calculate new height to use: bool raising = atoi(default_param) > 0; koord k = pos.get_2d(); sint8 new_water_height; grund_t *gr = welt->lookup_kartenboden(k); if( gr->is_water() ) { // lower + control removes shallow water only. If this tile is deep water this will fail if( !raising && is_ctrl_pressed() && welt->min_hgt(k)!=gr->get_hoehe() ) { return "Cannot alter water"; } // if currently water, raise = +1, lower = -1 new_water_height = gr->get_hoehe() + (raising ? 1 : -1); } // if not water then raise = set water height to ground height, lower = error else if( raising ) { slope_t::type slope = gr->get_grund_hang(); new_water_height = gr->get_hoehe() + max( max( corner_sw(slope), corner_se(slope) ),max( corner_ne(slope), corner_nw(slope) ) ); } else { return "Cannot alter water"; } if( new_water_height < welt->get_groundwater() - 3 ) { return "Cannot alter water"; } sint8 test_height = max( new_water_height, gr->get_hoehe() ); // make a list of tiles to change // cannot use a recursive method as stack is not large enough! sint8 *from_dir = new sint8[welt->get_size().x * welt->get_size().y]; sint8 *stage = new sint8[welt->get_size().x * welt->get_size().y]; memset( from_dir, -1, sizeof(sint8) * welt->get_size().x * welt->get_size().y ); memset( stage, -1, sizeof(sint8) * welt->get_size().x * welt->get_size().y ); #define array_koord(px,py) (px + py * welt->get_size().x) stage[array_koord(k.x,k.y)]=0; do { // firstly we must be able to change ground height bool ok = welt->is_plan_height_changeable( k.x, k.y ) && k.x > 0 && k.y > 0 && k.x < welt->get_size().x - 1 && k.y < welt->get_size().y - 1; const planquadrat_t *plan = welt->access(k); // next there cannot be any grounds directly above this tile sint8 h = plan->get_kartenboden()->get_hoehe() + 1; while( ok && h < new_water_height + welt->get_settings().get_way_height_clearance() ) { if( plan->get_boden_in_hoehe(h) ) { ok = false; } h++; } if( !ok ) { delete [] from_dir; delete [] stage; return "Cannot alter water"; } // get neighbour corner heights sint8 neighbour_heights[8][4]; welt->get_neighbour_heights( k, neighbour_heights ); for( int i = stage[array_koord(k.x,k.y)]; i < 8; i++ ) { koord k_neighbour = k + koord::neighbours[i]; grund_t *gr2 = welt->lookup_kartenboden(k_neighbour); if( gr2 ) { sint8 neighbour_height = gr2->get_hoehe(); // move onto this tile if it hasn't been processed yet bool ok = stage[array_koord(k_neighbour.x, k_neighbour.y)] == -1; if( raising ) { // test whether points adjacent to current tile will be flooded // if control key modifier pressed, level ground will be left alone, but then need to check for spills // for neighbour i test corners adjacent to tile // nw (i = 0), test se (corner 1) // w (i = 1), test se (corner 1) and ne (corner 2) // sw (i = 2), test ne (corner 2) // s (i = 3), test ne (corner 2) and nw (corner 3) // se (i = 4), test nw (corner 3) // e (i = 5), test nw (corner 3) and sw (corner 0) // ne (i = 6), test sw (corner 0) // n (i = 7), test sw (corner 0) and se (corner 1) if( is_ctrl_pressed() ) { ok = ok && ( (gr2->get_grund_hang()!=slope_t::flat && welt->max_hgt(k_neighbour) <= test_height) || neighbour_heights[i][((i >> 1) + 1) & 3] < test_height || ( (i & 1) && neighbour_heights[i][((i >> 1) + 2) & 3] < test_height) ); } else { ok = ok && (neighbour_heights[i][((i >> 1) + 1) & 3] <= test_height || ( (i & 1) && neighbour_heights[i][((i >> 1) + 2) & 3] <= test_height)); } // move onto this tile unless it already has water at new level, or the land level is above new level ok = ok && welt->get_water_hgt(k_neighbour) < new_water_height; } else { if( is_ctrl_pressed() ) { ok = ok && welt->min_hgt(k_neighbour) == test_height; } else { ok = ok && neighbour_height <= test_height; } // move onto this tile unless it already has water at new level, or the land level is above new level ok = ok && welt->get_water_hgt(k_neighbour) > new_water_height; } if( ok ) { //move on to next tile from_dir[array_koord(k_neighbour.x,k_neighbour.y)] = i; stage[array_koord(k_neighbour.x,k_neighbour.y)] = 0; stage[array_koord(k.x,k.y)] = i; k = k_neighbour; break; } } //return back to previous tile if( i==7 ) { stage[array_koord(k.x,k.y)] = 8; if( from_dir[array_koord(k.x,k.y)] != -1 ) { k = k - koord::neighbours[from_dir[array_koord(k.x,k.y)]]; } } } } while( from_dir[array_koord(k.x,k.y)] != -1 || stage[array_koord(k.x,k.y)] < 7 ); delete [] from_dir; // loop over map to find marked tiles for( int y = 1; yget_size().y - 1; y++ ) { for( int x = 1; xget_size().x - 1; x++ ) { if( stage[array_koord(x,y)] > -1 ) { // calculate new height, slope and climate and set water height grund_t *gr2 =welt->lookup_kartenboden(x, y); // remove any objects on this tile gr2->obj_loesche_alle( NULL ); const sint8 h0 = gr2->get_hoehe(); const sint8 min_grid_hgt = welt->min_hgt( koord( x, y ) ); sint8 h0_nw, h0_ne, h0_se, h0_sw; if( gr2->is_water() ) { // water - maximum existing height can be is old water height no matter what surrounding grids are h0_nw = min(h0, welt->lookup_hgt(x, y)); h0_ne = min(h0, welt->lookup_hgt(x+1, y)); h0_se = min(h0, welt->lookup_hgt(x+1, y+1)); h0_sw = min(h0, welt->lookup_hgt(x, y+1)); } else if( h0 > min_grid_hgt ) { // if min grid height here is less than ground height it will be because we are partially water h0_nw = welt->lookup_hgt(x, y); h0_ne = welt->lookup_hgt(x+1, y); h0_se = welt->lookup_hgt(x+1, y+1); h0_sw = welt->lookup_hgt(x, y+1); if( !gr2->is_water() ) { // while this appears to be a single height slope actually it is a double height slope half underwater const sint8 water_hgt = welt->get_water_hgt(x, y); h0_nw >= water_hgt ? h0_nw = h0 + corner_nw( gr2->get_grund_hang() ) : 0; h0_ne >= water_hgt ? h0_ne = h0 + corner_ne( gr2->get_grund_hang() ) : 0; h0_se >= water_hgt ? h0_se = h0 + corner_se( gr2->get_grund_hang() ) : 0; h0_sw >= water_hgt ? h0_sw = h0 + corner_sw( gr2->get_grund_hang() ) : 0; } } else { // fully land h0_nw = h0 + corner_nw( gr2->get_grund_hang() ); h0_ne = h0 + corner_ne( gr2->get_grund_hang() ); h0_se = h0 + corner_se( gr2->get_grund_hang() ); h0_sw = h0 + corner_sw( gr2->get_grund_hang() ); } const sint8 hneu_nw = max( new_water_height, h0_nw ); const sint8 hneu_ne = max( new_water_height, h0_ne ); const sint8 hneu_se = max( new_water_height, h0_se ); const sint8 hneu_sw = max( new_water_height, h0_sw ); const sint8 hneu = min( min( hneu_nw, hneu_ne ), min( hneu_se, hneu_sw ) ); gr2->set_hoehe( hneu ); const uint8 sneu = (hneu_sw - hneu > 2 ? 2 : hneu_sw - hneu) + ((hneu_se - hneu > 2 ? 2 : hneu_se-hneu) * 3) + ((hneu_ne - hneu > 2 ? 2 : hneu_ne - hneu) * 9) + ((hneu_nw - hneu > 2 ? 2 : hneu_nw - hneu) * 27); gr2->set_grund_hang( sneu ); welt->set_water_hgt_nocheck(x, y, new_water_height ); welt->access(x, y)->correct_water(); welt->calc_climate( koord( x, y ), true ); } } } delete [] stage; return NULL; } char const* tool_plant_tree_t::move(player_t* const player, uint16 const b, koord3d const pos) { if (b==0) { return NULL; } if (env_t::networkmode) { // queue tool for network nwc_tool_t *nwc = new nwc_tool_t(player, this, pos, welt->get_steps(), welt->get_map_counter(), false); network_send_server(nwc); return NULL; } else { return work( player, pos ); } } bool tool_plant_tree_t::init(player_t*) { if (!tree_builder_t::has_trees()) { return false; } else if (default_param == NULL || default_param[0] == 0) { return true; } char ignore_cl,random_age,dummy; if (std::sscanf(default_param, "%c%c,%c", &ignore_cl, &random_age, &dummy) != 3) { return false; } return (ignore_cl == '0' || ignore_cl == '1') && (random_age == '0' || random_age == '1'); } const char *tool_plant_tree_t::work( player_t *player, koord3d pos ) { koord k(pos.get_2d()); grund_t *gr = welt->lookup_kartenboden(k); if(gr) { // check if trees are allowed if( welt->get_settings().get_tree_distribution()==settings_t::TREE_DIST_NONE && !player->is_public_service() ) { return NOTICE_NO_TREES; } // check funds sint64 const cost = welt->get_settings().cst_remove_tree; if( !player->can_afford(cost) ) { return NOTICE_INSUFFICIENT_FUNDS; } const tree_desc_t *desc = NULL; bool check_climates = true; bool random_age = true; if(default_param==NULL || strlen(default_param)<5 ) { // just a random tree to the limit if( tree_builder_t::plant_tree_on_coordinate( k, welt->get_settings().get_max_no_of_trees_on_square(), 1 ) ) { player_t::book_construction_costs( player, cost, k, ignore_wt ); return NULL; } } else { // parse default_param: bbdesc_nr b=1 ignore climate b=1 random age check_climates = default_param[0]=='0'; random_age = default_param[1]=='1'; desc = tree_builder_t::find_tree( default_param+3 ); if( !desc ) { desc = tree_builder_t::random_tree_for_climate( welt->get_climate( k ) ); } if( desc && tree_builder_t::plant_tree_on_coordinate( k, desc, check_climates, random_age ) ) { player_t::book_construction_costs( player, cost, k, ignore_wt ); return NULL; } } return ""; } return NULL; } char const* tool_plant_groundobj_t::move(player_t* const player, uint16 const b, koord3d const pos) { if (b==0) { return NULL; } if (env_t::networkmode) { // queue tool for network nwc_tool_t *nwc = new nwc_tool_t(player, this, pos, welt->get_steps(), welt->get_map_counter(), false); network_send_server(nwc); return NULL; } else { return work( player, pos ); } } bool tool_plant_groundobj_t::init(player_t *) { if (groundobj_t::get_count() == 0) { return false; } else if (strempty(default_param)) { return true; } else if (strlen(default_param) < 4) { return false; } return default_param[0] == '0' || default_param[0] == '1'; } const char *tool_plant_groundobj_t::work( player_t *player, koord3d pos ) { koord k(pos.get_2d()); grund_t *gr = welt->lookup_kartenboden(k); if(gr) { const groundobj_desc_t *desc = NULL; bool check_climates = true; if (strempty(default_param)) { desc = groundobj_t::random_groundobj_for_climate( welt->get_climate( k ) ); if (desc == NULL) { return NULL; } } else { check_climates = default_param[0]=='0'; desc = groundobj_t::find_groundobj(default_param+3); } // disable placing groundobj on slopes unless they have extra phases (=moving or for slopes) if( gr->get_grund_hang() != slope_t::flat && desc->get_phases() == 2 ) { return NULL; } // check funds sint64 const cost = -desc->get_price(); if( !player->can_afford(cost) ) { return NOTICE_INSUFFICIENT_FUNDS; } if(desc && groundobj_t::plant_groundobj_on_coordinate( k, desc, check_climates ) ) { player_t::book_construction_costs(player, cost, k, ignore_wt); return NULL; } return ""; } return NULL; } /** * the following routines add waypoints/halts to a schedule * because we do not like to stop at AIs stop, but we still want to force the truck to use AI roads * So if there is a halt, then it must be either public or ours! * (Except if permission has been explicitely granted.) */ static const char *tool_schedule_insert_aux(karte_t *welt, player_t *player, koord3d pos, schedule_t *schedule, bool append) { if(schedule == NULL) { dbg->warning("tool_schedule_insert_aux()","Schedule is (null), doing nothing"); return 0; } grund_t *bd = welt->lookup(pos); if (bd) { // now just for error messages, we're assuming a valid ground // check for right way type if(!schedule->is_stop_allowed(bd)) { return schedule->get_error_msg(); } // stored in minivec, so we have to avoid adding too many if( schedule->entries.get_count()>=254 ) { return "Maximum 254 stops\nin a schedule!\n"; } // and check for ownership if( !bd->is_halt() ) { weg_t *w = bd->get_weg( schedule->get_waytype() ); if( w==NULL && schedule->get_waytype()==tram_wt ) { w = bd->get_weg( track_wt ); } if( w!=NULL && w->get_owner()!=welt->get_public_player() && !player_t::check_owner(w->get_owner(),player) ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } if( bd->get_depot() && !player_t::check_owner( bd->get_depot()->get_owner(), player ) ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } } if( bd->is_halt() && !bd->get_halt()->can_use_halt(player)) { return "Das Feld gehoert\neinem anderen Spieler\n"; } // ok, now we have a valid ground if(append) { schedule->append(bd); } else { schedule->insert(bd); } } return NULL; } const char *tool_schedule_add_t::work( player_t *player, koord3d pos ) { return tool_schedule_insert_aux( welt, player, pos, (schedule_t*)const_cast(default_param), true ); } const char *tool_schedule_ins_t::work( player_t *player, koord3d pos ) { return tool_schedule_insert_aux( welt, player, pos, (schedule_t*)const_cast(default_param), false ); } /* way construction */ const way_desc_t *tool_build_way_t::defaults[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; const way_desc_t *tool_build_way_t::get_desc() const { const way_desc_t* desc; const char* n; if (default_param && (n = strchr(default_param, ','))) { char name[256]; long len = (long)(n-default_param); tstrncpy( name, default_param, min(255,len)); desc = way_builder_t::get_desc(default_param, 0); } else { desc = default_param ? way_builder_t::get_desc(default_param, 0) : NULL; } if( desc==NULL && default_param ) { waytype_t wt = (waytype_t)atoi(default_param); // powerline is mapped to 0! if (wt > ignore_wt) { return get_default_desc(wt); } } return desc; } const way_desc_t* tool_build_way_t::get_default_desc(waytype_t wt) { const way_desc_t* desc; if ((wt & 63) < lengthof(defaults)) { desc = defaults[wt & 63]; if (desc == NULL || !desc->is_available(welt->get_timeline_year_month())) { // search fastest way, may fail if not available (but ok) desc = way_builder_t::weg_search(wt, 0xffffffff, welt->get_timeline_year_month(), type_flat); } return desc; } return NULL; } image_id tool_build_way_t::get_icon(player_t *) const { const way_desc_t *desc = way_builder_t::get_desc(default_param,0); image_id image = icon; bool is_tram = false; if( desc ) { is_tram = (desc->get_wtyp()==tram_wt) || (desc->get_styp() == type_tram); if( image == IMG_EMPTY ) { image = desc->get_cursor()->get_image_id(1); } if( !desc->is_available( world()->get_timeline_year_month() ) ) { return IMG_EMPTY; } } if( grund_t::underground_mode==grund_t::ugm_all && !is_tram ) { return IMG_EMPTY; } return image; } const char* tool_build_way_t::get_tooltip(const player_t *) const { const way_desc_t *desc = get_desc(); if (desc == NULL) { return ""; } tooltip_with_price_maintenance( welt, desc->get_name(), -desc->get_price(), desc->get_maintenance() ); size_t n= strlen(toolstr); sprintf(toolstr+n, ", %dkm/h", desc->get_topspeed() ); return toolstr; } // default ways are not initialized synchronously for different clients // always return the name of a way, never the string containing the waytype const char* tool_build_way_t::get_default_param(player_t *player) const { if (player==NULL) { return default_param; } if (desc) { return desc->get_name(); } else { if (default_param == NULL) { // no chance to guess anything sensible return NULL; } const way_desc_t* test_desc = get_desc(); if (test_desc) { return test_desc->get_name(); } else { return default_param; } } } bool tool_build_way_t::is_selected() const { tool_t const* const tool = welt->get_tool(welt->get_active_player_nr()); if (tool->get_id() != get_id()) { return false; } tool_build_way_t const* const selected = dynamic_cast(tool); return selected && selected->get_desc()==desc; } bool tool_build_way_t::init( player_t *player ) { two_click_tool_t::init( player ); if( ok_sound == NO_SOUND ) { ok_sound = SFX_CASH; } // now get current desc desc = get_desc(); if( desc && desc->get_cursor()->get_image_id(0) != IMG_EMPTY ) { cursor = desc->get_cursor()->get_image_id(0); } const char* n; if (default_param && (n = strchr(default_param, ','))!=NULL) { automatic_tunnel_and_bridges = n[1] == '1'; if (n[2] == ',') { max_length = atol(n + 3); } } if( desc && !desc->is_available(welt->get_timeline_year_month()) && player!=NULL && player!=welt->get_public_player() ) { // non available way => fail if not public player return false; } return desc!=NULL; } waytype_t tool_build_way_t::get_waytype() const { const way_desc_t *desc = get_desc(); if (desc) { return desc->is_tram() ? tram_wt : desc->get_wtyp(); } return invalid_wt; } void tool_build_way_t::start_at( koord3d &new_start ) { if( is_shift_pressed() && (desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt) ) { grund_t *gr=welt->lookup(new_start); if( gr->get_weg( desc->get_waytype() ) ) { new_start.z -= welt->get_settings().get_way_height_clearance(); } } // elevated ways with SHIFT will selected the current layer, when already on an elevated way two_click_tool_t::start_at( new_start ); } uint8 tool_build_way_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d & ) { error = NULL; grund_t *gr=welt->lookup(pos); if( gr && slope_t::is_way(gr->get_weg_hang()) ) { // ignore tunnel tiles (for tram track building where we forbind trams on water ... ) if( gr->get_typ() == grund_t::tunnelboden && !gr->ist_karten_boden() && !(desc->is_tram() && !gr->hat_weg(water_wt)) ) { return 0; } bool const elevated = desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt; // ignore water if( desc->get_wtyp() != water_wt && gr->get_typ() == grund_t::wasser ) { if( !elevated || welt->lookup_hgt( pos.get_2d() ) < welt->get_water_hgt( pos.get_2d() ) ) { return 0; } // here either channel or elevated way over not too deep water } // elevated ways have to check tile above if( elevated ) { gr = welt->lookup( pos + koord3d( 0, 0, welt->get_settings().get_way_height_clearance() ) ); if( gr == NULL ) { return 2; } if( gr->get_typ() != grund_t::monorailboden ) { return 0; } } // test if way already exists on the way and if we are allowed to connect weg_t *way = gr->get_weg(desc->get_wtyp()); if( way ) { // allow to connect to any road or waterway if( desc->get_wtyp() == road_wt || desc->get_wtyp() == water_wt ) { return 2; } // for others check ownership error = way->get_removal_error(player); return error==NULL ? 2 : 0; } // check for ownership but ignore moving things if(player!=NULL) { for(uint8 i=0; iobj_count(); i++) { obj_t* dt = gr->obj_bei(i); if (!dt->is_moving() && dt->get_removal_error(player)!=NULL && dt->get_typ()!=obj_t::label && (desc->get_wtyp() == powerline_wt || dt->get_typ()!=obj_t::leitung) ) { error = dt->get_removal_error(player); // "Das Feld gehoert\neinem anderen Spieler\n"; return 0; } } } } else { return 0; } return 2; } const char *tool_build_way_t::calc_route( way_builder_t &bauigel, const koord3d &start, const koord3d &end ) { // recalc type of construction way_builder_t::bautyp_t bautyp = (way_builder_t::bautyp_t)desc->get_wtyp(); if(desc->is_tram()) { bautyp = way_builder_t::schiene_tram; } // elevated track? if(desc->get_styp()==type_elevated && desc->get_wtyp()!=air_wt) { bautyp |= way_builder_t::elevated_flag; } const tunnel_desc_t* tunnel = NULL; const bridge_desc_t* br = NULL; if (automatic_tunnel_and_bridges) { // automatich selecting tunnel and bridges bautyp |= way_builder_t::terraform_flag; br = bridge_builder_t::find_bridge(desc->get_wtyp(), desc->get_topspeed(), welt->get_timeline_year_month()); tunnel = tunnel_builder_t::get_tunnel_desc(desc->get_wtyp(), desc->get_topspeed(), welt->get_timeline_year_month()); } bauigel.init_builder(bautyp, desc, tunnel, br); if( is_ctrl_pressed() && !is_shift_pressed()) { bauigel.set_keep_existing_ways( false ); } else { bauigel.set_keep_existing_faster_ways( true ); } // if shift pressed, keep city roads if (is_shift_pressed() && desc->get_styp() == type_flat && desc->get_wtyp() == road_wt) { bauigel.set_keep_city_roads(true); } koord3d my_end = end; // ending point is applied that elevated ways with SHIFT selects the current layer, when already on an elevated way if( is_shift_pressed() && (desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt) ) { grund_t *gr=welt->lookup(my_end); if( gr->get_weg( desc->get_waytype() ) ) { my_end.z -= welt->get_settings().get_way_height_clearance(); } } // Find out if this might be intended to become a parallel way bool assume_parallel = false; if (grund_t *gr=welt->lookup(start)) { if (weg_t *w=gr->get_weg(desc->get_wtyp())) { // we start on a way; but if it is an end, maybe continue building a parallel way if (ribi_t::is_single(w->get_ribi_unmasked())) { koord zv = (koord)(ribi_t::rotate90(w->get_ribi_unmasked())); if (grund_t* gr = welt->lookup(start + zv)) { // is there a parallel way? assume_parallel = gr->get_weg(desc->get_wtyp()); } if (grund_t* gr = welt->lookup(start - zv)) { // is there a parallel way? assume_parallel |= gr->get_weg(desc->get_wtyp())!=0; } } } else { // find out if there is a way close by koord3d delta_z(0, 0, (bautyp & way_builder_t::elevated_flag) != 0); for (int i = 0; i < 8; i++) { if (grund_t* gr = welt->lookup(start + koord::neighbours[i] + delta_z)) { if (gr->get_weg(desc->get_wtyp())) { assume_parallel = true; break; } } } } } if (assume_parallel) { bool assume_parallel2 = false; if (grund_t* gr = welt->lookup(my_end)) { if (!gr->get_weg(desc->get_wtyp())) { koord3d delta_z(0, 0, (bautyp & way_builder_t::elevated_flag) != 0); for (int i = 0; i < 8; i++) { if (grund_t* gr = welt->lookup(my_end + koord::neighbours[i] + delta_z)) { if (gr->get_weg(desc->get_wtyp())) { assume_parallel2 = true; break; } } } } } // both start and end are one tile from a way assume_parallel &= assume_parallel2; } bauigel.set_prefer_parallel(assume_parallel); // and continue as normal ... const char *err; if (is_ctrl_pressed() || (env_t::straight_way_without_control && !env_t::networkmode && !is_scripted()) #ifdef USE_TOWN_ROAD_BUILDER_TOOL || get_id() == (TOOL_BUILD_CITYROAD | GENERAL_TOOL) #endif ) { DBG_MESSAGE("tool_build_way_t()", "try straight route"); err = bauigel.calc_straight_route(start, my_end); } else { err = bauigel.calc_route(start, my_end); } DBG_MESSAGE("tool_build_way_t()", "builder found route with %d squares length.", bauigel.get_count()); return err; } const char* tool_build_way_t::do_work(player_t* player, const koord3d& start, const koord3d& end) { way_builder_t bauigel(player); const char* err = calc_route(bauigel, start, end); if (bauigel.get_route().get_count() > 1) { welt->mute_sound(true); bauigel.build(); welt->mute_sound(false); // set default newly constructed type if (can_use_gui()) { if (desc->get_styp() == type_tram) { defaults[tram_wt] = desc; } else { defaults[desc->get_wtyp() & 63] = desc; } } return NULL; } return err ? err : ""; } void tool_build_way_t::mark_tiles(player_t* player, const koord3d& start, const koord3d& end) { way_builder_t bauigel(player); const char* err = calc_route(bauigel, start, end); bool keep_city_roads = is_shift_pressed() && desc->get_styp() == type_flat && desc->get_wtyp() == road_wt; uint8 offset = (desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt) ? welt->get_settings().get_way_height_clearance() : 0; if (bauigel.get_count() > 1) { // Set tooltip first (no dummygrounds, if bauigel.calc_casts() is called). win_set_static_tooltip(tooltip_with_price_length("Building costs estimates", bauigel.calc_costs(), bauigel.get_count())); // make dummy route from bauigel for (uint32 j = 0; j < bauigel.get_count(); j++) { koord3d pos = bauigel.get_route()[j] + koord3d(0, 0, offset); grund_t* gr = welt->lookup(pos); if (!gr) { gr = new monorailboden_t(pos, slope_t::flat); // should only be here when elevated/monorail, therefore will be at height offset above ground gr->set_grund_hang(welt->lookup(pos - koord3d(0, 0, offset))->get_grund_hang()); welt->access(pos.get_2d())->boden_hinzufuegen(gr); } if (gr->is_water()) { continue; } ribi_t::ribi zeige = gr->get_weg_ribi_unmasked(desc->get_wtyp()) | bauigel.get_route().get_ribi(j); // do not replace city roads if requested if (keep_city_roads && gr->get_weg(road_wt) && gr->get_weg(road_wt)->hat_gehweg()) { continue; } zeiger_t* way = new zeiger_t(pos, player); if (gr->get_weg_hang()) { way->set_image(desc->get_slope_image_id(gr->get_weg_hang(), 0)); } else if (desc->get_wtyp() != powerline_wt && ribi_t::is_bend(zeige) && desc->has_diagonal_image()) { way->set_image(desc->get_diagonal_image_id(zeige, 0)); } else { way->set_image(desc->get_image_id(zeige, 0)); } gr->obj_add(way); way->set_yoff(-gr->get_weg_yoff()); marked.insert(way); way->mark_image_dirty(way->get_image(), 0); } } else if (err) { win_set_static_tooltip(translator::translate(err)); } } /* city road construction */ const way_desc_t* tool_build_cityroad::get_desc() const { return welt->get_city_road(); } #ifdef USE_TOWN_ROAD_BUILDER_TOOL uint8 tool_build_cityroad::is_valid_pos(player_t* player, const koord3d& pos, const char*& error, const koord3d&) { if (stadt_t* city = welt->find_nearest_city(pos.get_2d())) { if (grund_t* gr = welt->lookup(pos)) { if (gr->ist_natur() || gr->hat_weg(road_wt)) { return 3; } } } return 0; } #endif const char* tool_build_cityroad::do_work(player_t* player, const koord3d& start, const koord3d& end) { #ifdef USE_TOWN_ROAD_BUILDER_TOOL if (stadt_t* city = welt->find_nearest_city(start.get_2d())) { if (end == koord3d::invalid) { if (city->build_road(start.get_2d(), NULL, true)) { return NULL; } } else if(city->test_and_build_cityroad(start.get_2d(), end.get_2d()) ) { return NULL; } } return ""; #else way_builder_t bauigel(player); bauigel.set_build_sidewalk(true); calc_route( bauigel, start, end ); if( bauigel.get_route().get_count()>1 ) { welt->mute_sound(true); bauigel.build(); welt->mute_sound(false); return NULL; } return ""; #endif } /* bridge construction */ const char* tool_build_bridge_t::get_tooltip(const player_t *) const { const bridge_desc_t * desc = bridge_builder_t::get_desc(default_param); if (desc == NULL) { return ""; } tooltip_with_price_maintenance( welt, desc->get_name(), -desc->get_price(), desc->get_maintenance() ); size_t n= strlen(toolstr); if(desc->get_waytype()!=powerline_wt) { n += sprintf(toolstr+n, ", %dkm/h", desc->get_topspeed()); } if(desc->get_max_length()>0) { n += sprintf(toolstr+n, ", %dkm", desc->get_max_length()); } return toolstr; } waytype_t tool_build_bridge_t::get_waytype() const { const bridge_desc_t * desc = bridge_builder_t::get_desc(default_param); return desc ? desc->get_waytype() : invalid_wt; } bool tool_build_bridge_t::init( player_t *player ) { two_click_tool_t::init( player ); // now get current desc const bridge_desc_t *desc = bridge_builder_t::get_desc(default_param); if( desc && !desc->is_available(welt->get_timeline_year_month()) && player!=NULL && player!=welt->get_public_player() ) { return false; } return desc!=NULL; } const char *tool_build_bridge_t::do_work( player_t *player, const koord3d &start, const koord3d &end ) { const bridge_desc_t *desc = bridge_builder_t::get_desc(default_param); if (end!=koord3d::invalid) { const koord zv(ribi_type(end-start)); sint8 bridge_height; if (const char* error=bridge_builder_t::can_build_bridge(player, start, end, bridge_height, desc)) { // cannot build bridge here return error; } bridge_builder_t::build_bridge( player, start, end, zv, bridge_height, desc, way_builder_t::weg_search(desc->get_waytype(), desc->get_topspeed(), welt->get_timeline_year_month(), type_flat)); return NULL; // all checks are performed before building. } else { grund_t *gr = welt->lookup(start); if (gr && gr->ist_bruecke()) { // Renovate whole bridge weg_t *w = gr->get_weg_nr(0); if (w && w->get_waytype() == desc->get_waytype()) { return bridge_builder_t::renovate(player, start, w->get_waytype(), desc); } } } return ""; } void tool_build_bridge_t::rdwr_custom_data(memory_rw_t *packet) { two_click_tool_t::rdwr_custom_data(packet); uint8 i = ribi; packet->rdwr_byte(i); ribi = (ribi_t::ribi)i; } void tool_build_bridge_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end ) { const ribi_t::ribi ribi_mark = ribi_type(end-start); const koord zv(ribi_mark); const bridge_desc_t *desc = bridge_builder_t::get_desc(default_param); sint8 bridge_height; if (bridge_builder_t::can_build_bridge(player, start, end, bridge_height, desc)) { // cannot build bridge here return; } sint64 costs = 0; // start grund_t *gr = welt->lookup(start); // get initial height of bridge from start tile // flat -> height is 1 if conversion factor 1, 2 if conversion factor 2 // single height -> height is 1 // double height -> height is 2 const slope_t::type slope = gr->get_weg_hang(); uint8 max_height = slope ? slope_t::max_diff(slope) : bridge_height-start.z; zeiger_t *way = new zeiger_t(start, player ); const bridge_desc_t::img_t img0 = desc->get_end( slope, slope, slope_type(zv)*max_height ); gr->obj_add( way ); way->set_image( desc->get_background( img0, 0 ) ); way->set_foreground_image( desc->get_foreground( img0, 0 ) ); if( gr->get_grund_hang() != 0 ) { way->set_yoff( -TILE_HEIGHT_STEP * max_height ); } // eventually we have to remove trees on start tile if (desc->get_waytype() != powerline_wt) { for( uint8 i=0; iobj_count(); i++ ) { obj_t *obj = gr->obj_bei(i); switch(obj->get_typ()) { case obj_t::baum: costs -= welt->get_settings().cst_remove_tree; break; case obj_t::groundobj: costs += ((groundobj_t *)obj)->get_desc()->get_price(); break; default: break; } } } marked.insert( way ); way->mark_image_dirty( way->get_image(), 0 ); // loop koord3d pos( start.get_2d()+zv, bridge_height ); while (pos.get_2d()!=end.get_2d()) { grund_t *gr = welt->lookup( pos ); if( !gr ) { gr = new monorailboden_t(pos, slope_t::flat); gr->set_grund_hang( slope_t::flat ); welt->access(pos.get_2d())->boden_hinzufuegen(gr); } zeiger_t *way = new zeiger_t(pos, player ); gr->obj_add( way ); grund_t *kb = welt->lookup_kartenboden(pos.get_2d()); sint16 height = pos.z - kb->get_pos().z; way->set_image(desc->get_background(desc->get_straight(ribi_mark,height-slope_t::max_diff(kb->get_grund_hang())),0)); way->set_foreground_image(desc->get_foreground(desc->get_straight(ribi_mark,height-slope_t::max_diff(kb->get_grund_hang())), 0)); marked.insert( way ); way->mark_image_dirty( way->get_image(), 0 ); pos = pos + zv; } costs += desc->get_price() * koord_distance(start, pos); // end gr = welt->lookup(end); // get initial height of bridge from start tile // flat -> height is 1 if conversion factor 1, 2 if conversion factor 2 // single height -> height is 1 // double height -> height is 2 const slope_t::type end_slope = gr->get_weg_hang(); const uint8 end_max_height = end_slope ? (is_one_high(end_slope) ? 1 : 2) : (pos.z-end.z); if( gr->ist_karten_boden() && end.z + end_max_height == start.z + max_height ) { zeiger_t *way = new zeiger_t(end, player ); const bridge_desc_t::img_t img1 = desc->get_end( end_slope, end_slope, end_slope?0:(pos.z-end.z)*slope_type(-zv) ); gr->obj_add( way ); way->set_image(desc->get_background(img1, 0)); way->set_foreground_image(desc->get_foreground(img1, 0)); if( gr->get_grund_hang() != 0 ) { way->set_yoff( -TILE_HEIGHT_STEP * end_max_height ); } marked.insert( way ); way->mark_image_dirty( way->get_image(), 0 ); costs += desc->get_price(); } else { // should be elevated ground if (desc->get_waytype() == powerline_wt ? !gr->find() : !gr->hat_weg(desc->get_waytype())) { const way_desc_t *way_desc = way_builder_t::weg_search(desc->get_waytype(), desc->get_topspeed(), welt->get_timeline_year_month(), type_flat); costs += way_desc->get_price(); } } // eventually we have to remove trees on end tile if (desc->get_waytype() != powerline_wt) { for( uint8 i=0; iobj_count(); i++ ) { obj_t *obj = gr->obj_bei(i); switch(obj->get_typ()) { case obj_t::baum: costs -= welt->get_settings().cst_remove_tree; break; case obj_t::groundobj: costs += ((groundobj_t *)obj)->get_desc()->get_price(); break; default: break; } } } win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", costs, koord_distance(start, pos)+1 ) ); } uint8 tool_build_bridge_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d &start ) { const bridge_desc_t *desc = bridge_builder_t::get_desc(default_param); error = NULL; grund_t *gr = welt->lookup(pos); if (gr == NULL) { return 0; } bool diagonal = (pos.x != start.x) && (pos.y != start.y); if (!is_first_click() && diagonal) { return 0; } if (!bridge_builder_t::check_start_tile(player, gr, is_first_click()?0:ribi_type(pos-start), desc)) { return 2; } if (gr->get_typ() == grund_t::brueckenboden) { // Possibly a bridge to update here, but check waytype weg_t *w = gr->get_weg_nr(0); if (w && w->get_waytype() == desc->get_waytype()) { return 1; } } return 0; } /* more difficult, since this builds also underground ways */ const char* tool_build_tunnel_t::get_tooltip(const player_t *) const { const tunnel_desc_t * desc = tunnel_builder_t::get_desc(default_param); tooltip_with_price_maintenance( welt, desc->get_name(), -desc->get_price(), desc->get_maintenance() ); size_t n= strlen(toolstr); if(desc->get_waytype()!=powerline_wt) { n += sprintf(toolstr+n, ", %dkm/h", desc->get_topspeed()); } return toolstr; } waytype_t tool_build_tunnel_t::get_waytype() const { const tunnel_desc_t * desc = tunnel_builder_t::get_desc(default_param); return desc ? desc->get_waytype() : invalid_wt; } bool tool_build_tunnel_t::init( player_t *player ) { two_click_tool_t::init( player ); // now get current desc const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param); if( desc && !desc->is_available(welt->get_timeline_year_month()) && player!=NULL && player!=welt->get_public_player() ) { return false; } return desc!=NULL; } const char *tool_build_tunnel_t::check_pos( player_t *player, koord3d pos) { if (grund_t::underground_mode == grund_t::ugm_all) { return NULL; } else { if( grund_t *gr=welt->lookup( pos ) ) { if( !gr->is_visible() ) { // not visible return ""; } if (gr->find()) { // there is tunnel present -> allow, no chance to guess building cost. return NULL; } if( gr->ist_karten_boden() ) { win_set_static_tooltip( translator::translate("No suitable ground!") ); slope_t::type sl = gr->get_grund_hang(); if( sl == slope_t::flat || !slope_t::is_way( sl ) ) { // cannot start a tunnel here, wrong slope return ""; } if( welt->get_settings().get_way_height_clearance() != slope_t::max_diff(sl) ) { win_set_static_tooltip( translator::translate("The gradient does not fit a tunnel") ); return ""; } const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param); // first check for building portal only if( is_ctrl_pressed() ) { // estimate costs for tunnel portal win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", (-(sint64)desc->get_price())*2, 1 ) ); return NULL; } // Now check, if we can built a tunnel here and display costs koord3d end = tunnel_builder_t::find_end_pos(player,pos, koord(gr->get_grund_hang()), desc, true ); if( end == koord3d::invalid || end == pos ) { // no end found return ""; } // estimate costs for full tunnel win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", (-(sint64)desc->get_price())*koord_distance(pos,end), koord_distance(pos,end) ) ); return NULL; } } return two_click_tool_t::check_pos(player, pos); } } void tool_build_tunnel_t::calc_route( way_builder_t &bauigel, const koord3d &start, const koord3d &end) { const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param); way_builder_t::bautyp_t bt = (way_builder_t::bautyp_t)(desc->get_waytype()); const way_desc_t *wb = desc->get_way_desc(); if(wb==NULL) { // ignore timeline to get consistent results wb = way_builder_t::weg_search( desc->get_waytype(), desc->get_topspeed(), 0, type_flat ); } bauigel.init_builder(bt | way_builder_t::tunnel_flag, wb, desc); bauigel.set_keep_existing_faster_ways( !is_ctrl_pressed() ); // wegbauer (way builder) tries to find route to 3d coordinate if no ground at end exists or is not kartenboden (map ground) bauigel.calc_straight_route(start,end); } const char *tool_build_tunnel_t::do_work( player_t *player, const koord3d &start, const koord3d &end ) { if( end == koord3d::invalid ) { // Build tunnel mouths if( grund_t *gr=welt->lookup( start ) ) { if( gr->ist_karten_boden() ) { const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param); const char *err = NULL; // first check for building portal only if( is_ctrl_pressed() ) { // estimate costs for tunnel portal if( !player->can_afford((-(sint64)desc->get_price())*2) ) { return NOTICE_INSUFFICIENT_FUNDS; } } else { // Now check, if we can built a tunnel here and display costs koord3d end = tunnel_builder_t::find_end_pos(player, start, koord(gr->get_grund_hang()), desc, true, &err ); if( end == koord3d::invalid || end == start ) { // no end found return err ? err : "Tunnel must start on single way!"; } if( !player->can_afford((-(sint64)desc->get_price())*koord_distance(start,end)) ) { return NOTICE_INSUFFICIENT_FUNDS; } } return tunnel_builder_t::build( player, start.get_2d(), desc, !is_ctrl_pressed() ); } } return "Tunnel must start on single way!"; } else { // Build tunnels way_builder_t bauigel(player); calc_route( bauigel, start, end ); welt->mute_sound(true); bauigel.build(); welt->mute_sound(false); welt->lookup_kartenboden(end.get_2d())->clear_flag(grund_t::marked); return NULL; } } uint8 tool_build_tunnel_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d & ) { if( !is_first_click() ) { error = NULL; // All pos are valid for the second click! return 2; } // search for ground // start needs valid tile! grund_t *gr = welt->lookup(pos); if( gr ) { if( gr->hat_wege() ) { const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param); // use the check_owner routine of way_builder_t (not player_t!), needs an instance weg_t *w = gr->get_weg_nr(0); if( w==NULL || w->get_desc()->get_wtyp()!=desc->get_waytype() ) { error = NOTICE_UNSUITABLE_GROUND; return 0; } way_builder_t bauigel(player); if(!bauigel.check_owner( w->get_owner(), player )) { error = "Das Feld gehoert\neinem anderen Spieler\n"; return 0; } } } else { error = NOTICE_UNSUITABLE_GROUND; return 0; } // if starting tile is tunnel .. build underground tracks error = NULL; if(gr->ist_tunnel()) { return 2; } // .. otherwise build tunnel mouths (and tunnel behind) else { return 1; } } void tool_build_tunnel_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end ) { way_builder_t bauigel(player); calc_route( bauigel, start, end ); const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param); // now we search a matching way for the tunnels top speed const way_desc_t *wb = desc->get_way_desc(); if(wb==NULL) { // ignore timeline to get consistent results wb = way_builder_t::weg_search( desc->get_waytype(), desc->get_topspeed(), 0, type_flat ); } welt->lookup_kartenboden(end.get_2d())->clear_flag(grund_t::marked); if( bauigel.get_count()>1 ) { // Set tooltip first (no dummygrounds, if bauigel.calc_casts() is called). win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", -bauigel.calc_costs(), bauigel.get_count() ) ); // make dummy route from bauigel for( uint32 j=0; jlookup(pos); if( !gr ) { // We need to create a dummy ground. gr = new tunnelboden_t(pos, slope_t::flat); welt->access(pos.get_2d())->boden_hinzufuegen(gr); } ribi_t::ribi zeige = gr->get_weg_ribi_unmasked(wb->get_wtyp()) | bauigel.get_route().get_ribi( j ); zeiger_t *way = new zeiger_t(pos, player ); if(gr->get_weg_hang()) { way->set_image( wb->get_slope_image_id(gr->get_weg_hang(),0) ); } else if(wb->get_wtyp()!=powerline_wt && ribi_t::is_bend(zeige) && wb->has_diagonal_image()) { way->set_image( wb->get_diagonal_image_id(zeige,0) ); } else { way->set_image( wb->get_image_id(zeige,0) ); } gr->obj_add( way ); marked.insert( way ); way->mark_image_dirty( way->get_image(), 0 ); } welt->lookup(end)->set_flag(grund_t::marked); } } /* removes a way like a driving car ... */ char const* tool_wayremover_t::get_tooltip(player_t const*) const { switch(atoi(default_param)) { case road_wt: return translator::translate("remove roads"); case tram_wt: case track_wt: return translator::translate("remove tracks"); case maglev_wt: return translator::translate("remove maglev tracks"); case narrowgauge_wt: return translator::translate("remove narrowgauge tracks"); case monorail_wt: return translator::translate("remove monorails"); case water_wt: return translator::translate("remove channels"); case air_wt: return translator::translate("remove airstrips"); case powerline_wt: return translator::translate("remove powerlines"); } return NULL; } image_id tool_wayremover_t::get_icon(player_t *) const { if (default_param && way_builder_t::waytype_available((waytype_t)atoi(default_param), welt->get_timeline_year_month())) { return icon; } return IMG_EMPTY; } waytype_t tool_wayremover_t::get_waytype() const { return default_param ? (waytype_t)atoi(default_param) : invalid_wt; } class electron_t : public test_driver_t { bool check_next_tile(const grund_t* gr) const OVERRIDE { return gr->get_leitung()!=NULL; } ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return gr->get_leitung()->get_ribi(); } waytype_t get_waytype() const OVERRIDE { return invalid_wt; } int get_cost(const grund_t *, const weg_t *, const sint32, ribi_t::ribi) const OVERRIDE { return 1; } bool is_target(const grund_t *,const grund_t *) const OVERRIDE { return false; } }; class way_checker_t : public test_driver_t { private: waytype_t wt; public: way_checker_t(waytype_t w) : wt(w) {} bool check_next_tile(const grund_t* gr) const OVERRIDE { return gr->hat_weg(wt); } ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return gr->get_weg_ribi(wt); } waytype_t get_waytype() const OVERRIDE { return wt; } int get_cost(const grund_t*, const weg_t*, const sint32, ribi_t::ribi) const OVERRIDE { return 1; } bool is_target(const grund_t*, const grund_t*) const OVERRIDE { return false; } }; class scenario_checker_t : public test_driver_t { public: test_driver_t *other; scenario_t *scenario; uint16 id; player_t *player; ~scenario_checker_t() { delete other; } /** * checks for active scenario, * @returns scenario_checker_t if scenario active, the supplied test_driver otherwise */ static test_driver_t* apply(test_driver_t *test_driver, player_t *player, tool_t *tool) { karte_t *welt = world(); if (is_scenario()) { scenario_checker_t *td2 = new scenario_checker_t(); td2->other = test_driver; td2->scenario = welt->get_scenario(); td2->id = tool->get_id(); td2->player = player; return td2; } return test_driver; } private: bool check_next_tile(const grund_t* gr) const OVERRIDE { return other->check_next_tile(gr) && scenario->is_work_allowed_here(player, id, other->get_waytype(), 0, gr->get_pos())==NULL;} ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return other->get_ribi(gr); } waytype_t get_waytype() const OVERRIDE { return other->get_waytype(); } int get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const OVERRIDE { return other->get_cost(gr, w, max_speed, from); } bool is_target(const grund_t *gr,const grund_t *gr2) const OVERRIDE { return other->is_target(gr,gr2); } }; void tool_wayremover_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end ) { route_t verbindung; bool can_built = calc_route( verbindung, player, start, end ); if( can_built ) { for(koord3d const& pos : verbindung.get_route()) { zeiger_t *marker = new zeiger_t(pos, NULL ); marker->set_image( cursor ); marker->mark_image_dirty( marker->get_image(), 0 ); marked.insert( marker ); welt->lookup(pos)->obj_add( marker ); } } } uint8 tool_wayremover_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d & ) { // search for starting ground waytype_t wt = get_waytype(); grund_t *gr=tool_intern_koord_to_weg_grund(player,welt,pos,wt); if(gr==NULL) { DBG_MESSAGE("tool_wayremover_t()", "no ground on %i,%i",pos.x, pos.y); // wrong ground or not this way here => exit return 0; } // do not remove ground from depot if(gr->get_depot()) { error = NOTICE_UNSUITABLE_GROUND; return 0; } if(is_scenario()) { error = welt->get_scenario()->is_work_allowed_here(player, get_id(), wt, 0, pos); if (error) { dbg->warning("tool_wayremover_t::is_within_limits()", error); return 0; } } error = NULL; return 2; } bool tool_wayremover_t::calc_route( route_t &verbindung, player_t *player, const koord3d &start, const koord3d &end ) { waytype_t wt = get_waytype(); if (wt == tram_wt) { wt = track_wt; } if( start == end ) { verbindung.clear(); grund_t *gr=welt->lookup(start); if( gr && (wt!=powerline_wt ? gr->get_weg(wt)!=NULL : gr->get_leitung()!=NULL) ) { verbindung.append( start ); } } else { // get a default vehicle test_driver_t* test_driver; if( wt!=powerline_wt ) { test_driver = new way_checker_t(wt); } else { test_driver = new electron_t(); } test_driver = scenario_checker_t::apply(test_driver, player, this); verbindung.calc_route(welt, start, end, test_driver, 0, 0); delete test_driver; } DBG_MESSAGE("tool_wayremover_t()","route with %d tile found",verbindung.get_count()); calc_route_error = NULL; bool can_delete = start == end || verbindung.get_count()>1; if( can_delete ) { // found a route => check if I can delete anything on it for(koord3d const& i : verbindung.get_route()) { if (!can_delete) break; grund_t const* const gr = welt->lookup(i); if( wt!=powerline_wt ) { // no way found if( gr==NULL || gr->get_weg(wt)==NULL ) { can_delete = false; break; } // check all if we want to delete the first on a no-ground tile bool check_all = !gr->ist_karten_boden() && gr->has_two_ways() && gr->get_weg_nr(0)->get_waytype()==wt; // we have to do a fine check for( uint i=0; iobj_count() && can_delete; i++ ) { obj_t *obj = gr->obj_bei(i); const uint8 type = obj->get_typ(); // ignore pillars, powerlines if (type == obj_t::pillar || type==obj_t::leitung) { continue; } // ignore flying aircraft if (type == obj_t::air_vehicle && !(static_cast(obj)->is_on_ground())) { continue; } const waytype_t obj_wt = obj->get_waytype(); // way-related things if (obj_wt != invalid_wt) { // check this thing if it has the same waytype or if we want to remove the whole bridge/tunnel tile // special case: stations - take care not to produce station without any way const bool lonely_station = type==obj_t::gebaeude && !gr->has_two_ways(); if (check_all || obj_wt == wt || lonely_station) { can_delete = (calc_route_error = obj->get_removal_error(player)) == NULL; } } // all other stuff else { can_delete = (calc_route_error = obj->get_removal_error(player)) == NULL; } } } else { // for powerline: only a ground and a powerline to remove if( gr==NULL || gr->get_leitung()==NULL || (calc_route_error = gr->get_leitung()->get_removal_error(player))!=NULL ) { can_delete = false; break; } } } } DBG_MESSAGE("tool_wayremover_t()", "route search returned %d", can_delete); return can_delete; } const char *tool_wayremover_t::do_work( player_t *player, const koord3d &start, const koord3d &end ) { waytype_t wt = get_waytype(); route_t verbindung; if( !calc_route( verbindung, player, start, end ) ) { DBG_MESSAGE("tool_wayremover_t()","no route found"); if (calc_route_error && *calc_route_error) { return calc_route_error; } else { return "Ways not connected"; } } bool can_delete = true; // assume success // if successful => delete everything for( uint32 i=0; ilookup(verbindung.at(i)); // ground can be missing after deleting a bridge ... if(gr && !gr->is_water()) { if(gr->ist_bruecke()) { if(gr->find()->get_desc()->get_waytype()==wt) { if( bridge_builder_t::is_start_of_bridge(gr) ) { const char *err = NULL; err = bridge_builder_t::remove(player,verbindung.at(i),wt); if(err) { return err; } gr = welt->lookup(verbindung.at(i)); if( !gr ) { // happens with bridges without ramps continue; } } else { // do not remove asphalt from a bridge ... continue; } } } // now the tricky part: delete just part of a way (or everything, if possible) // calculate remaining directions ribi_t::ribi rem = 15 ^ ( verbindung.get_route().get_ribi(i) ); // if start=end tile then delete every direction if( verbindung.get_count() <= 1 ) { rem = 0; } if( wt!=powerline_wt ) { if(!gr->get_flag(grund_t::is_kartenboden) && (gr->get_typ()==grund_t::tunnelboden || gr->get_typ()==grund_t::monorailboden) && gr->get_weg_nr(0)->get_waytype()==wt) { can_delete &= gr->remove_everything_from_way(player,wt,rem); if(can_delete && gr->get_weg(wt)==NULL) { if(gr->get_weg_nr(0)!=0) { gr->remove_everything_from_way(player,gr->get_weg_nr(0)->get_waytype(),ribi_t::none); } gr->obj_loesche_alle(player); gr->mark_image_dirty(); if (gr->is_visible() && gr->get_typ()==grund_t::tunnelboden && i>0) { // visibility test does not influence execution grund_t *bd = welt->access(verbindung.at(i-1).get_2d())->get_kartenboden(); bd->calc_image(); bd->set_flag(grund_t::dirty); } // delete tunnel/monorail ground too, if empty welt->access(gr->get_pos().get_2d())->boden_entfernen(gr); delete gr; } } else { can_delete &= gr->remove_everything_from_way(player,wt,rem); if (gr->get_typ() == grund_t::tunnelboden && !gr->hat_wege() ) { // tunnel portal has been removed grund_t* gr_new = new boden_t(gr->get_pos(), gr->get_grund_hang()); welt->access(gr->get_pos().get_2d())->kartenboden_setzen(gr_new); gr = gr_new; } } } else { leitung_t *lt = gr->get_leitung(); if( lt && (rem<->get_ribi())==0 ) { // remove only single connections lt->cleanup(player); delete lt; // delete tunnel/monorail ground too, if empty if ( (gr->get_typ()==grund_t::tunnelboden || gr->get_typ() == grund_t::monorailboden) && !gr->hat_wege() ) { gr->obj_loesche_alle(player); gr->mark_image_dirty(); if (!gr->get_flag(grund_t::is_kartenboden)) { welt->access(gr->get_pos().get_2d())->boden_entfernen(gr); delete gr; } else { grund_t *gr_new = new boden_t(gr->get_pos(), gr->get_grund_hang()); welt->access(gr->get_pos().get_2d())->boden_ersetzen(gr, gr_new); gr_new->calc_image(); } } } // otherwise it is a crossing ... } } // ok, now everything removed ... } // return success return can_delete ? NULL : ""; } /* add catenary during construction */ const way_obj_desc_t *tool_build_wayobj_t::default_electric = NULL; const char* tool_build_wayobj_t::get_tooltip(const player_t *) const { if( build ) { const way_obj_desc_t *desc = get_desc(); if(desc) { tooltip_with_price_maintenance( welt, desc->get_name(), -desc->get_price(), desc->get_maintenance() ); size_t n= strlen(toolstr); if (desc->is_overhead_line()) { // only overheadlines impose topspeed sprintf(toolstr+n, ", %dkm/h", desc->get_topspeed()); } return toolstr; } return NULL; } else { waytype_t wt = (waytype_t)atoi( default_param ); sprintf( toolstr, translator::translate("Remove wayobj %s"), translator::translate(weg_t::waytype_to_string(wt)) ); return toolstr; } } const way_obj_desc_t *tool_build_wayobj_t::get_desc() const { const way_obj_desc_t *desc = default_param ? wayobj_t::find_desc(default_param) : NULL; if(desc==NULL) { desc = default_electric; if(desc==NULL) { desc = wayobj_t::get_overhead_line( track_wt, welt->get_timeline_year_month() ); } } return desc; } waytype_t tool_build_wayobj_t::get_waytype() const { if( build ) { const way_obj_desc_t *desc = get_desc(); return desc ? desc->get_wtyp() : invalid_wt; } else { sint16 wt; return (default_param && std::sscanf(default_param, "%hi", &wt) == 1) ? (waytype_t)wt : invalid_wt; } } bool tool_build_wayobj_t::is_selected() const { const tool_build_wayobj_t *selected = dynamic_cast(welt->get_tool(welt->get_active_player_nr())); return (selected && selected->build==build && selected->get_desc() == get_desc()); } bool tool_build_wayobj_t::init( player_t *player ) { two_click_tool_t::init( player ); if( build ) { desc = get_desc(); if( desc ) { cursor = desc->get_cursor()->get_image_id(0); wt = desc->get_wtyp(); default_electric = desc; } return desc!=NULL; } else { desc = NULL; wt = (waytype_t)atoi( default_param ); return wt != 0; } } bool tool_build_wayobj_t::calc_route( route_t &verbindung, player_t *player, const koord3d& start, const koord3d& to ) { waytype_t waytype = wt; if( waytype == decoration_wt ) { waytype = welt->lookup(start)->get_weg(wt)->get_waytype(); } // special treatment for deports, since track electrication cannot "drive" into tram depot if( waytype == track_wt ) { if( depot_t *dp = welt->lookup(start)->get_depot() ) { waytype = dp->get_waytype(); } else if( depot_t *dp = welt->lookup(to)->get_depot() ) { waytype = dp->get_waytype(); } } // get a default vehicle test_driver_t * test_driver = new way_checker_t(waytype == tram_wt ? track_wt : waytype); test_driver = scenario_checker_t::apply(test_driver, player, this); bool can_built; if( start != to ) { can_built = verbindung.calc_route(welt, start, to, test_driver, 0, 0); } else { verbindung.clear(); verbindung.append( start ); can_built = true; } delete test_driver; return can_built; } uint8 tool_build_wayobj_t::is_valid_pos( player_t* player, const koord3d& pos, const char *&error, const koord3d & ) { // search for starting ground grund_t *gr = tool_intern_koord_to_weg_grund( player, welt, pos, wt, true ); if( gr == NULL ) { DBG_MESSAGE("tool_build_wayobj_t::is_within_limits()", "no ground on %s",pos.get_str()); // wrong ground or not this way here => exit return 0; } error = NULL; return 2; } void tool_build_wayobj_t::mark_tiles( player_t* player, const koord3d &start, const koord3d &end ) { route_t verbindung; bool can_built = calc_route( verbindung, player, start, end ); if( can_built ) { sint32 cost_estimate = 0; bool keep_existing_faster_ways = !is_ctrl_pressed(); for( uint32 j = 0; j < verbindung.get_count(); j++ ) { koord3d pos = verbindung.at(j); grund_t *gr = welt->lookup(pos); ribi_t::ribi show = verbindung.get_route().get_ribi(j); // Search a matching catenary on gr. wayobj_t *wayobj = gr->get_wayobj( wt ); if( build ) { cost_estimate += desc->get_price(); if( wayobj ) { show = show | wayobj->get_dir(); // Already a catenary here -> costs only, if new catenary is faster if( (wayobj->get_desc()->get_topspeed() >= desc->get_topspeed() && keep_existing_faster_ways) || wayobj->get_desc() == desc ) { cost_estimate -= desc->get_price(); } } } else if( wayobj ) { cost_estimate += wayobj->get_desc()->get_price(); } zeiger_t *way_obj = NULL; if( build ) { way_obj = new zeiger_t(pos, player ); if( gr->get_weg_hang() ) { way_obj->set_foreground_image( desc->get_front_slope_image_id(gr->get_weg_hang()) ); way_obj->set_image( desc->get_back_slope_image_id(gr->get_weg_hang()) ); } else if( ribi_t::is_bend(show) && desc->has_diagonal_image() ) { way_obj->set_foreground_image( desc->get_front_diagonal_image_id(show) ); way_obj->set_image( desc->get_back_diagonal_image_id(show) ); } else { way_obj->set_foreground_image( desc->get_front_image_id(show) ); way_obj->set_image( desc->get_back_image_id(show) ); } } else { if( gr->get_wayobj( wt ) ) { way_obj = new zeiger_t(pos, player ); way_obj->set_image( cursor ); //skinverwaltung_t::bauigelsymbol->get_image_id(0)); } } if( way_obj ) { way_obj->mark_image_dirty( way_obj->get_image(), 0 ); gr->obj_add( way_obj ); marked.insert( way_obj ); } } win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", -cost_estimate, verbindung.get_count() ) ); } } const char *tool_build_wayobj_t::do_work( player_t* player, const koord3d &start, const koord3d &end ) { route_t verbindung; bool can_built = calc_route( verbindung, player, start, end ); DBG_MESSAGE("tool_build_wayobj_t::work()","route search returned %d",can_built); const char *err = NULL; if(!can_built) { return "Ways not connected"; } bool keep_existing_faster_ways = !is_ctrl_pressed(); // build wayobj ... koord3d_vector_t const& r = verbindung.get_route(); for(uint32 i=0; ilookup(r[i]); for(int n=0; nobj_count(); n++ ) { obj_t *obj = gr->obj_bei(n); if( obj && obj->get_typ()==obj_t::wayobj ) { wayobj_t *wo = static_cast(obj); if( wo->get_waytype() == wt ) { // only remove matching waytype const char *err = wo->get_removal_error( player ); if( !err ) { wo->cleanup( player ); delete wo; } else { break; } } } } } } // Update depots (maybe remove electric tab?). Depots can only be on first and last tile. if( depot_t *dep = welt->lookup(r[0])->get_depot() ) { dep->update_win(); } if( depot_t *dep = welt->lookup(r.back())->get_depot() ) { dep->update_win(); } return err; } image_id tool_remove_wayobj_t::get_icon(player_t*) const { if (default_param && way_builder_t::waytype_available((waytype_t)atoi(default_param), welt->get_timeline_year_month())) { return icon; } return IMG_EMPTY; } /* build all kind of station extension buildings */ const char *tool_build_station_t::tool_station_building_aux(player_t *player, bool extend_public_halt, koord3d pos, const building_desc_t *desc, sint8 rotation ) { koord k = pos.get_2d(); // need kartenboden (map ground) if (welt->lookup_kartenboden(k)->get_hoehe() != pos.z) { return ""; } DBG_MESSAGE("tool_station_building_aux()", "building mail office/station building on square %d,%d", k.x, k.y); // Player player pays for the construction // but we try to extend stations of Player new_owner that may be the public player player_t *new_owner = extend_public_halt ? welt->get_public_player() : player; koord offsets; halthandle_t halt; const char *msg = NOTICE_TILE_FULL; if( rotation==-1 ) { //no predefined rotation int best_halt = 0; int any_halt = 0; // find valid rotations (since halt extensions are symmetric, we need to check only two) bool any_ok = false; for( int r=0; r<2; r++ ) { koord testsize = desc->get_size(r); for( sint8 j=3; j>=0; j-- ) { bool ok = true; koord offset(((j&1)^1)*(testsize.x-1),((j>>1)&1)*(testsize.y-1)); if(welt->square_is_free(k-offset, testsize.x, testsize.y, NULL, desc->get_allowed_climate_bits())) { // first we must check over/under halt halthandle_t last_halt; for( sint16 x=0; xaccess( k-offset+koord(x,y) ); if (pl) { for( uint8 i=0; i < pl->get_boden_count(); i++ ) { halthandle_t test_halt = pl->get_boden_bei(i)->get_halt(); if(test_halt.is_bound()) { if(!player_t::check_owner( new_owner, test_halt->get_owner())) { // there is another player's halt ok = false; msg = "Das Feld gehoert\neinem anderen Spieler\n"; } else if(!last_halt.is_bound()) { last_halt = test_halt; } else if(last_halt != test_halt) { // there are several halts ok = false; msg = "Several halts found."; } } } } } } if(!ok) { continue; } // well, at least this is theoretical possible here any_ok = true; if(rotation==-1) { // we can build it. reserve this one // This needs to build a building at under/over a halt. rotation = r; offsets = offset; } koord test_start = k-offset; // find all surrounding tiles with a stop // for following section of code arrays are arranged such that // 0 - facing north // 1 - facing west // 2 - facing south // 3 - facing east int neighbour_halts[4] = { 0, 0, 0, 0 }; int best_halts[4] = { 0, 0, 0, 0 }; // test also diagonal corners (that is why from -1 to size!) for( sint16 y=-1; y<=testsize.y; y++ ) { for( sint16 x=-1; x<=testsize.x; (x==-1 && y>-1 && yaccess( test_start+koord(x,y) ); if( pl ) { for( uint b=0; b < pl->get_boden_count(); b++ ) { grund_t *gr = pl->get_boden_bei(b); if( gr->is_halt() && gr->get_halt().is_bound() && new_owner == gr->get_halt()->get_owner() ) { halt = gr->get_halt(); gebaeude_t *gb = gr->find(); bool best = gr->hat_wege() && gb && gb->get_tile()->get_desc()->get_extra()==desc->get_extra(); // north if( y==-1 ) { neighbour_halts[0] ++; if( best ) { best_halts[0] ++; } } // west if( x==-1 ) { neighbour_halts[1] ++; if( best ) { best_halts[1] ++; } } // south if( y==testsize.y ) { neighbour_halts[2] ++; if( best ) { best_halts[2] ++; } } // east if( x==testsize.x ) { neighbour_halts[3] ++; if( best ) { best_halts[3] ++; } } } } } } } // now find out, if this offset/rotation is better ... (i.e. matches more fitting buildings) // for r=0 we check north and south, for r=1 we check east and west for( int i=r; i<4; i+=2 ) { if( best_halts[i]>best_halt || (best_halt==0 && neighbour_halts[i]>any_halt) ) { best_halt = best_halts[i]; any_halt = neighbour_halts[i]; rotation = i; offsets = offset; } } } } } // no suitable ground here ... if( !any_ok ) { return msg; } // check over/under halt again for( sint16 x=0; xget_x(rotation); x++ ) { for( sint16 y=0; yget_y(rotation); y++ ) { const planquadrat_t *pl = welt->access( k-offsets+koord(x,y) ); for( uint8 i=0; i < pl->get_boden_count(); i++ ) { halthandle_t test_halt = pl->get_boden_bei(i)->get_halt(); if( test_halt.is_bound() && player_t::check_owner( new_owner, test_halt->get_owner()) ) { halt = test_halt; break; } } } } // is there no halt to connect? if( !halt.is_bound() ) { return "Post muss neben\nHaltestelle\nliegen!\n"; } } else { // rotation was pre-selected; just search for stop now if (rotation < desc->get_all_layouts()) { dbg->warning("tool_station_building_aux()", "%s rotation larger than %d", this->default_param, desc->get_all_layouts()); rotation %= desc->get_all_layouts(); } koord testsize = desc->get_size(rotation); offsets = koord(0,0); if( !welt->square_is_free(k, testsize.x, testsize.y, NULL, desc->get_allowed_climate_bits()) ) { return NOTICE_TILE_FULL; } // check over/under halt for( sint16 x=0; xaccess(k+koord(x,y)); for( uint8 i=0; i < pl->get_boden_count(); i++ ) { halthandle_t test_halt = pl->get_boden_bei(i)->get_halt(); if(test_halt.is_bound()) { if(!player_t::check_owner( new_owner, test_halt->get_owner())) { return "Das Feld gehoert\neinem anderen Spieler\n"; } else if(!halt.is_bound()) { halt = test_halt; } else if(halt != test_halt) { return "Several halts found."; } } } } } if(!halt.is_bound()) { halt = suche_nahe_haltestelle(new_owner, welt, welt->lookup_kartenboden(k)->get_pos(), desc->get_x(rotation), desc->get_y(rotation) ); // is there no halt to connect? if( !halt.is_bound() ) { return "Post muss neben\nHaltestelle\nliegen!\n"; } } } if( rotation>desc->get_all_layouts() ) { rotation %= desc->get_all_layouts(); } hausbauer_t::build(halt->get_owner(), pos.get_2d()-offsets, rotation, desc, &halt); sint32 const factor = desc->get_x() * desc->get_y(); sint64 cost = -desc->get_price(welt) * factor; if( player!=halt->get_owner() && halt->get_owner()==welt->get_public_player() ) { // public stops are expensive! cost -= (desc->get_maintenance(welt) * factor * 60); } // difficult to distinguish correctly most suitable waytype player_t::book_construction_costs(player, cost, k, desc->get_finance_waytype()); halt->recalc_station_type(); halt->recalc_basis_pos(); return NULL; } /* build a dock either small or large */ const char *tool_build_station_t::tool_station_dock_aux(player_t *player, koord3d pos, const building_desc_t *desc) { // the cursor cannot be outside the map from here on koord k = pos.get_2d(); grund_t *gr = welt->lookup_kartenboden(k); if (gr->get_hoehe()!= pos.z) { return ""; } slope_t::type hang = gr->get_grund_hang(); // first get the size int len = desc->get_y()-1; koord dx(hang); koord last_k = k - dx*len; halthandle_t halt; // check, if we can build here ... if(!slope_t::is_single(hang)) { return "Dock must be built on single slope!"; } else { // iterate up to max(len,1) to ensure that there is at least one tile of water // in front of the dock for(int i=0; i<=max(len,1); i++ ) { if(!welt->is_within_limits(k-dx*i)) { // need at least a single tile to navigate ... return "Zu nah am Kartenrand"; } // search for nearby stops const planquadrat_t* pl = welt->access(k-dx*i); for( uint8 j=0; j < pl->get_boden_count(); j++ ) { halthandle_t test_halt = pl->get_boden_bei(j)->get_halt(); if(test_halt.is_bound()) { if(!player_t::check_owner( player, test_halt->get_owner())) { return "Das Feld gehoert\neinem anderen Spieler\n"; } else if(!halt.is_bound()) { halt = test_halt; } else if(halt != test_halt) { return "Several halts found."; } } } // check whether we can build something const grund_t *gr=welt->lookup_kartenboden(k-dx*i); if (gr->get_hoehe() != pos.z) { return NOTICE_UNSUITABLE_GROUND; } if (i <= len) { if (const char *msg = gr->kann_alle_obj_entfernen(player)) { return msg; } } if (i==0) { // start tile on slope near water if(gr->hat_wege() || gr->get_typ()!=grund_t::boden || gr->is_halt()) { return NOTICE_TILE_FULL; } } else { // all other tiles in water (allowing one-tile docks on rivers) if (!gr->is_water() && !(len==0 && i==1 && gr->hat_weg(water_wt))) { return NOTICE_UNSUITABLE_GROUND; } if (gr->find() || gr->get_depot() || gr->is_halt()) { return NOTICE_TILE_FULL; } } } } // remove everything from tile gr->obj_loesche_alle(player); koord bau_pos = k; int layout = 0; koord dx2; switch(hang) { case slope_t::south: case slope_t::south*2: layout = 0; dx2 = koord::west; break; case slope_t::east: case slope_t::east*2: layout = 1; dx2 = koord::north; break; case slope_t::north: case slope_t::north*2: layout = 2; dx2 = koord::west; bau_pos = last_k; break; case slope_t::west: case slope_t::west*2: layout = 3; dx2 = koord::north; bau_pos = last_k; break; } // handle 16 layouts bool change_layout = false; if(desc->get_all_layouts()==16) { if( layout<2 ) { layout = 15-layout; } else { layout = 9-layout; } change_layout = true; } // oriented buildings here - get neighbouring layouts const grund_t* gr_neigh = welt->lookup_kartenboden(k+dx2); // find out if middle end or start tile if(gr_neigh && gr_neigh->is_halt() && player_t::check_owner( player, gr_neigh->get_halt()->get_owner() )) { gebaeude_t *gb = gr_neigh->find(); if(gb && (gb->get_tile()->get_desc()->get_type()==building_desc_t::dock || gb->get_tile()->get_desc()->get_type()==building_desc_t::flat_dock) ) { if(change_layout) { layout -= 4; } if(gb->get_tile()->get_desc()->get_all_layouts()==16) { sint8 ly = gb->get_tile()->get_layout(); if(ly&0x02) { gb->set_tile( gb->get_tile()->get_desc()->get_tile(ly&0xFD,0,0), false ); } } } } gr_neigh = welt->lookup_kartenboden(k-dx2); if(gr_neigh && gr_neigh->is_halt() && player_t::check_owner( player, gr_neigh->get_halt()->get_owner() )) { gebaeude_t *gb = gr_neigh->find(); if(gb && (gb->get_tile()->get_desc()->get_type()==building_desc_t::dock || gb->get_tile()->get_desc()->get_type()==building_desc_t::flat_dock) ) { if(change_layout) { layout -= 2; } if(gb->get_tile()->get_desc()->get_all_layouts()==16) { sint8 ly = gb->get_tile()->get_layout(); if(ly&0x04) { gb->set_tile( gb->get_tile()->get_desc()->get_tile(ly&0xFB,0,0), false ); } } } } if(!halt.is_bound()) { halt = suche_nahe_haltestelle(player, welt, welt->lookup_kartenboden(k)->get_pos() ); } bool neu = !halt.is_bound(); if(neu) { if( gr && gr->get_halt().is_bound() ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } // ok, really new stop on this tile then halt = haltestelle_t::create(k, player); } hausbauer_t::build(halt->get_owner(), bau_pos, layout, desc, &halt); sint64 costs = -desc->get_price(welt); if( player!=halt->get_owner() && player != welt->get_public_player() ) { // public stops are expensive! // (Except for the public player itself) costs -= (desc->get_maintenance(welt) * 60); } for( int i=0; i<=len; i++ ) { koord p=k-dx*i; player_t::book_construction_costs(player, costs, p, water_wt); } halt->recalc_station_type(); halt->recalc_basis_pos(); if( env_t::station_coverage_show && welt->get_zeiger()->get_pos().get_2d()==k ) { // since we are larger now ... halt->mark_unmark_coverage( true ); } if(neu) { char* const name = halt->create_name(k, "Dock"); halt->set_name( name ); free(name); } return NULL; } /* build a dock either small or large */ const char *tool_build_station_t::tool_station_flat_dock_aux(player_t *player, koord3d pos, const building_desc_t *desc, sint8 rotation) { // the cursor cannot be outside the map from here on const koord k = pos.get_2d(); grund_t *gr = welt->lookup_kartenboden(k); if (gr->get_hoehe()!= pos.z) { return ""; } // first get the size int len = desc->get_size().y-1; // check, if we can build here ... if( !gr->ist_natur() || gr->get_grund_hang() != slope_t::flat ) { return NOTICE_UNSUITABLE_GROUND; } // now find the direction // first: find the next water ribi_t::ribi water_dir = 0; uint8 total_dir = 0; for( uint8 i=0; i<4; i++ ) { if( grund_t *gr = welt->lookup_kartenboden(k+koord::nesw[i]) ) { if( gr->is_water() && gr->get_hoehe() == pos.z) { water_dir |= ribi_t::nesw[i]; total_dir ++; } } } // not surrounded by water => fail if( total_dir == 0 ) { return NOTICE_UNSUITABLE_GROUND; } // prefer layouts that reach an existing halt ribi_t::ribi halt_dir = 0; halthandle_t test_halt[4]; for( uint8 ii=0; ii<4; ii++ ) { if( (water_dir & ribi_t::nesw[ii]) == 0 ) { continue; } const koord dx = koord::nesw[ii]; const char *last_error = NULL; for(int i=0; i<=len; i++ ) { // check whether we can build something const grund_t *gr = welt->lookup_kartenboden(k+dx*i); if( !gr ) { // need at least a single tile to navigate ... last_error = "Zu nah am Kartenrand"; break; } if (gr->get_hoehe() != pos.z) { last_error = NOTICE_UNSUITABLE_GROUND; break; } // search for nearby stops const planquadrat_t* pl = welt->access(k+dx*i); for( uint8 j=0; j < pl->get_boden_count() && !test_halt[ii].is_bound(); j++ ) { halthandle_t halt = pl->get_boden_bei(j)->get_halt(); if (halt.is_bound() && player_t::check_owner( player, halt->get_owner()) ) { test_halt[ii] = halt; halt_dir |= ribi_t::nesw[ii]; } } if( (last_error = gr->kann_alle_obj_entfernen(player)) ) { break; } if (i>0) { // all other tiles in water if (!gr->is_water() || gr->find() || gr->get_depot() || gr->is_halt()) { last_error = NOTICE_TILE_FULL; } } } // error: then remove this direction if( last_error ) { water_dir &= ~ribi_t::nesw[ii]; if( --total_dir == 0 ) { // no duitable directions found return last_error; } } } // now we may have more than one dir left if (rotation == -1 && total_dir > 1 && !ribi_t::is_single(water_dir & halt_dir) ) { return "More than one possibility to build this dock found."; } // remove everything from tile gr->obj_loesche_alle(player); koord dx = koord::invalid; koord last_k; uint8 layout = 0; // building orientation halthandle_t halt; koord bau_pos = k; for( uint8 i=0; i<4; i++ ) { if( water_dir & ribi_t::nesw[i] ) { dx = koord::nesw[i]; halt = test_halt[i]; koord last_k = k + dx*len; // layout: north 2, west 3, south 0, east 1 static const uint8 nesw_to_layout[4] = { 2, 1, 0, 3 }; layout = nesw_to_layout[i]; if( layout>=2 ) { // reverse construction in these directions bau_pos = last_k; } if (rotation == layout) { // desired rotation works break; } if (rotation != -1) { // desired rotation not possible, try others if( --total_dir == 0 ) { return NOTICE_UNSUITABLE_GROUND; } } } } // handle 16 layouts bool change_layout = false; if(desc->get_all_layouts()==16) { if( layout<2 ) { layout = 15-layout; } else { layout = 9-layout; } change_layout = true; } // oriented buildings here - get neighbouring layouts koord dx2 = dx; dx2.rotate90(0); gr = welt->lookup_kartenboden(k+dx2); // find out if middle end or start tile if(gr && gr->is_halt() && player_t::check_owner( player, gr->get_halt()->get_owner() )) { gebaeude_t *gb = gr->find(); if(gb && (gb->get_tile()->get_desc()->get_type()==building_desc_t::dock || gb->get_tile()->get_desc()->get_type()==building_desc_t::flat_dock) ) { if(change_layout) { layout -= 4; } if(gb->get_tile()->get_desc()->get_all_layouts()==16) { sint8 ly = gb->get_tile()->get_layout(); if(ly&0x02) { gb->set_tile( gb->get_tile()->get_desc()->get_tile(ly&0xFD,0,0), false ); } } } } gr = welt->lookup_kartenboden(k-dx2); if(gr && gr->is_halt() && player_t::check_owner( player, gr->get_halt()->get_owner() )) { gebaeude_t *gb = gr->find(); if(gb && (gb->get_tile()->get_desc()->get_type()==building_desc_t::dock || gb->get_tile()->get_desc()->get_type()==building_desc_t::flat_dock) ) { if(change_layout) { layout -= 2; } if(gb->get_tile()->get_desc()->get_all_layouts()==16) { sint8 ly = gb->get_tile()->get_layout(); if(ly&0x04) { gb->set_tile( gb->get_tile()->get_desc()->get_tile(ly&0xFB,0,0), false ); } } } } DBG_MESSAGE("tool_station_flat_dock_aux()","building dock from square (%d,%d) to (%d,%d) layout=%i", k.x, k.y, last_k.x, last_k.y, layout ); if(!halt.is_bound()) { halt = suche_nahe_haltestelle(player, welt, welt->lookup_kartenboden(k)->get_pos() ); } bool neu = !halt.is_bound(); if(neu) { if( gr && gr->get_halt().is_bound() ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } // ok, really new stop on this tile then halt = haltestelle_t::create(k, player); } hausbauer_t::build(halt->get_owner(), bau_pos, layout, desc, &halt); sint64 costs = -desc->get_price(welt); if( player!=halt->get_owner() && player != welt->get_public_player() ) { // public stops are expensive! // (Except for the public player itself) costs -= (desc->get_maintenance(welt) * 60); } for( int i=0; i<=len; i++ ) { koord p=k-dx*i; player_t::book_construction_costs(player, costs, p, water_wt); } halt->recalc_station_type(); halt->recalc_basis_pos(); if( env_t::station_coverage_show && welt->get_zeiger()->get_pos().get_2d()==k ) { // since we are larger now ... halt->mark_unmark_coverage( true ); } if(neu) { char* const name = halt->create_name(k, "Dock"); halt->set_name( name ); free(name); } else { halt->recalc_basis_pos(); } return NULL; } // build all types of stops but sea harbours const char *tool_build_station_t::tool_station_aux(player_t *player, koord3d pos, const building_desc_t *desc, sint8 layout, waytype_t wegtype, const char *type_name ) { koord k = pos.get_2d(); DBG_MESSAGE("tool_station_aux()", "building %s on square %d,%d for waytype %x", desc->get_name(), k.x, k.y, wegtype); const char *p_error=(desc->get_all_layouts()==4) ? "No terminal station here!" : "No through station here!"; // underground is checked in work(); if underground only simple stations are allowed // get valid ground grund_t *bd = tool_intern_koord_to_weg_grund(player, welt, pos, wegtype); if( !bd || bd->get_weg_hang()!=slope_t::flat ) { // only flat tiles, only one stop per map square return "No suitable way on the ground!"; } if( bd->ist_tunnel() && bd->ist_karten_boden() ) { // do not build on tunnel entries return "No suitable way on the ground!"; } if( bd->get_depot() ) { // not on depots return NOTICE_UNSUITABLE_GROUND; } if( bd->hat_weg(air_wt) && bd->get_weg(air_wt)->get_desc()->get_styp()!=type_flat ) { return "Flugzeughalt muss auf\nRunway liegen!\n"; } if( layout <= -1 || layout > desc->get_all_layouts() ) { // find out orientation ... ribi_t::ribi ribi = ribi_t::none; if( desc->get_all_layouts()==2 || desc->get_all_layouts()==8 || desc->get_all_layouts()==16 ) { // through station if( bd->has_two_ways() ) { // a crossing or maybe just a tram track on a road ... ribi = bd->get_weg_nr(0)->get_ribi_unmasked() | bd->get_weg_nr(1)->get_ribi_unmasked(); } else if( bd->hat_wege() ) { ribi = bd->get_weg_nr(0)->get_ribi_unmasked(); } // not straight: sorry cannot build here ... if( !ribi_t::is_straight(ribi) ) { return p_error; } layout = (ribi & ribi_t::northsouth)?0 :1; } else if( desc->get_all_layouts()==4 ) { // terminal station if( bd->hat_wege() ) { ribi = bd->get_weg_nr(0)->get_ribi_unmasked(); } // sorry cannot build here ... (not a terminal tile) if( !ribi_t::is_single(ribi) ) { return p_error; } switch(ribi) { case ribi_t::south: layout = 0; break; case ribi_t::east: layout = 1; break; case ribi_t::north: layout = 2; break; case ribi_t::west: layout = 3; break; } } else { // something wrong with station number of layouts dbg->fatal( "tool_station_t::tool_station_aux", "%s has wrong number of layouts (must be 2,4,8,16!)", desc->get_name() ); } if( desc->get_all_layouts() == 8 || desc->get_all_layouts() == 16 ) { // through station - complex layout // bits // 1 = north south/east west (as simple layout) // 2 = use far end image \ can be combined // 3 = use near end image / to use both end image // 4 = platform face - 0 = far, 1 = near // bit 1 has already been set ribi_t::ribi next_own = ribi_t::none; sint8 offset = bd->get_hoehe()+bd->get_weg_yoff()/TILE_HEIGHT_STEP; grund_t *gr; sint32 neighbour_layout[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; for( unsigned i=0; i<4; i++ ) { // oriented buildings here - get neighbouring layouts gr = welt->lookup(koord3d(k+koord::nesw[i],offset)); if(!gr) { // check whether bridge end tile grund_t * gr_tmp = welt->lookup(koord3d(k+koord::nesw[i],offset-1)); if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 1) { gr = gr_tmp; } else { grund_t * gr_tmp = welt->lookup(koord3d(k+koord::nesw[i],offset-2)); if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 2) { gr = gr_tmp; } } } if( gr && gr->get_halt().is_bound() ) { // check, if there is an oriented stop const gebaeude_t* gb = gr->find(); if(gb && gb->get_tile()->get_desc()->get_all_layouts()>4 && (gb->get_tile()->get_desc()->get_type()>building_desc_t::dock || gb->get_tile()->get_desc()->get_type()>building_desc_t::flat_dock) ) { next_own |= ribi_t::nesw[i]; neighbour_layout[ribi_t::nesw[i]] = gb->get_tile()->get_layout(); } } } // now for the details ribi_t::ribi senkrecht = ~ribi_t::doubles(ribi); ribi_t::ribi waagerecht = ribi_t::doubles(ribi); if(next_own!=ribi_t::none) { // oriented buildings here if(ribi_t::is_single(ribi & next_own)) { // only a single next neighbour on the same track layout |= neighbour_layout[ribi & next_own] & 8; } else if(ribi_t::is_straight(ribi & next_own)) { // two neighbours on the same track, use the north/west one layout |= neighbour_layout[ribi & next_own & ribi_t::northwest] & 8; } else if(ribi_t::is_single((~ribi) & waagerecht & next_own)) { // neighbour across break in track layout |= neighbour_layout[(~ribi) & waagerecht & next_own] & 8; } else { // no buildings left and right // oriented buildings left and right if(neighbour_layout[senkrecht & next_own & ribi_t::northwest] != -1) { // just rotate layout layout |= 8-(neighbour_layout[senkrecht & next_own & ribi_t::northwest]&8); } else { if(neighbour_layout[senkrecht & next_own & ribi_t::southeast] != -1) { layout |= 8-(neighbour_layout[senkrecht & next_own & ribi_t::southeast]&8); } } } } assert(layout >= 0); // avoid orientation on 8 tiled buildings layout &= (desc->get_all_layouts()-1); } } else { // obey predefined layout // todo: check way ribis } halthandle_t old_halt = bd->get_halt(); sint64 old_cost = 0; bool recalc_schedule = false; halthandle_t halt; if( old_halt.is_bound() ) { gebaeude_t* gb = bd->find(); const building_desc_t *old_desc = gb->get_tile()->get_desc(); if( old_desc == desc ) { // already has the same station return NULL; } if( old_desc->get_capacity() >= desc->get_capacity() && !is_ctrl_pressed() ) { return "Upgrade must have\na higher level"; } old_cost = old_desc->get_price(welt)*old_desc->get_x()*old_desc->get_y(); gb->cleanup( NULL ); delete gb; halt = old_halt; if( old_desc->get_enabled() != desc->get_enabled() ) { recalc_schedule = true; } } else { halt = suche_nahe_haltestelle(player,welt,bd->get_pos()); } // seems everything ok, lets build bool neu = !halt.is_bound(); if(neu) { if( bd->get_halt().is_bound() ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } halt = haltestelle_t::create(k, player); } hausbauer_t::build_station_extension_depot(halt->get_owner(), bd->get_pos(), layout, desc, &halt); halt->recalc_station_type(); halt->recalc_basis_pos(); if(neu) { char* const name = halt->create_name(k, type_name); halt->set_name(name); free(name); } else { halt->recalc_basis_pos(); } // cost to build new station sint64 cost = -desc->get_price(welt)*desc->get_x()*desc->get_y(); // discount for existing station cost += old_cost/2; if( player!=halt->get_owner() && player != welt->get_public_player() ) { // public stops are expensive! // (Except for the public player itself) cost -= (desc->get_maintenance(welt) * desc->get_x() * desc->get_y() * 60); } player_t::book_construction_costs(player, cost, k, wegtype); if( env_t::station_coverage_show && welt->get_zeiger()->get_pos().get_2d()==k ) { // since we are larger now ... halt->mark_unmark_coverage( true ); } // the new station (after upgrading) might accept different goods => needs new schedule if( recalc_schedule ) { welt->set_schedule_counter(); } return NULL; } // gives the description and sets the rotation value const building_desc_t *tool_build_station_t::get_desc( sint8 &rotation ) const { if (strempty(default_param)) { return NULL; } char *building = strdup( default_param ); const building_tile_desc_t *tdsc = NULL; rotation = -1; if( building ) { char *p = strrchr( building, ',' ); if( p ) { *p++ = 0; if (std::sscanf(p, "%hhd", &rotation) != 1 || rotation < -1 || rotation > 15) { free(building); return NULL; } } tdsc = hausbauer_t::find_tile(building, 0); free( building ); } return tdsc ? tdsc->get_desc() : NULL; } bool tool_build_station_t::init( player_t * ) { sint8 rotation = -1; const building_desc_t *bdsc = get_desc( rotation ); if( bdsc==NULL || bdsc->get_cursor()==NULL) { return false; } cursor = bdsc->get_cursor()->get_image_id(0); if( !can_use_gui() ) { // do not change cursor return true; } if( (bdsc->get_type()==building_desc_t::generic_extension || bdsc->get_type()==building_desc_t::flat_dock) && bdsc->get_all_layouts()>1 ) { if( is_ctrl_pressed() && rotation==-1 ) { // call station dialog instead destroy_win( magic_station_building_select ); create_win( new station_building_select_t(bdsc), w_info, magic_station_building_select); // we do not activate building yet; else uncomment the return statement return false; } else if( rotation>=0 ) { // rotation is already fixed cursor_area = koord( bdsc->get_x(rotation), bdsc->get_y(rotation) ); cursor_centered = false; cursor_offset = koord(0,0); if (bdsc->get_type()==building_desc_t::flat_dock && rotation >= 2 ) { cursor_offset = cursor_area - koord(1,1); } } else { goto set_area_cov; } } else { set_area_cov: uint16 const cov = welt->get_settings().get_station_coverage() * 2 + 1; cursor_area = koord(cov, cov); cursor_centered = true; cursor_offset = koord(0,0); } return true; } image_id tool_build_station_t::get_icon( player_t * ) const { sint8 dummy; const building_desc_t *desc=get_desc(dummy); if (desc == NULL) { return IMG_EMPTY; } if( grund_t::underground_mode==grund_t::ugm_all ) { // in underground mode, buildings will be done invisible above ground => disallow such confusion if( desc->get_type()!=building_desc_t::generic_stop || desc->get_extra()==air_wt) { return IMG_EMPTY; } if( desc->get_type()==building_desc_t::generic_stop && !desc->can_be_built_underground()) { return IMG_EMPTY; } } if( grund_t::underground_mode==grund_t::ugm_none ) { if( desc->get_type()==building_desc_t::generic_stop && !desc->can_be_built_aboveground()) { return IMG_EMPTY; } } return icon; } const char* tool_build_station_t::get_tooltip(const player_t *) const { sint8 dummy; building_desc_t const* desc = get_desc(dummy); if (desc == NULL) { return ""; } sint64 price = 0; sint64 maint = 0; uint32 cap = desc->get_capacity(); // This is always correct in the desc object. maint = desc->get_maintenance(welt); if( desc->get_type()==building_desc_t::generic_stop || desc->get_type()==building_desc_t::generic_extension || desc->get_type()==building_desc_t::dock || desc->get_type()==building_desc_t::flat_dock ) { price = -desc->get_price(welt); } else { return "Illegal description"; } if(desc->get_type()==building_desc_t::generic_extension || desc->get_type()==building_desc_t::dock || desc->get_type()==building_desc_t::flat_dock) { const sint16 size_multiplier = desc->get_size().x * desc->get_size().y; price *= size_multiplier; cap *= size_multiplier; maint *= size_multiplier; } return tooltip_with_price_maintenance_capacity(welt, desc->get_name(), price, maint, cap, desc->get_enabled()); } waytype_t tool_build_station_t::get_waytype() const { sint8 dummy; building_desc_t const* desc = get_desc(dummy); switch (desc ? desc->get_type() : building_desc_t::generic_extension) { case building_desc_t::generic_stop: return (waytype_t)desc->get_extra(); case building_desc_t::dock: case building_desc_t::flat_dock: return water_wt; case building_desc_t::generic_extension: default: return ignore_wt; } } const char *tool_build_station_t::check_pos( player_t*, koord3d pos ) { if( grund_t *gr = welt->lookup( pos ) ) { sint8 rotation; const building_desc_t *desc = get_desc(rotation); if(desc == NULL) { // tool is in bad state, eg due to invalid tool parameters DBG_DEBUG("tool_build_station_t::check_pos()", "Cannot resolve building descriptor, default_param=\"%s\".", default_param); return "ENGINE ERROR: Build station tool cannot resolve a building descriptor."; } if( grund_t *bd = welt->lookup_kartenboden( pos.get_2d() ) ) { const bool underground = bd->get_hoehe()>gr->get_hoehe(); if( underground ) { // in underground mode, buildings will be done invisible above ground => disallow such confusion if( desc->get_type()!=building_desc_t::generic_stop || desc->get_extra()==air_wt) { return "Cannot built this station/building\nin underground mode here."; } if( desc->get_type()==building_desc_t::generic_stop && !desc->can_be_built_underground()) { return "Cannot built this station/building\nin underground mode here."; } } else if( desc->get_type()==building_desc_t::generic_stop && !desc->can_be_built_aboveground()) { return "This station/building\ncan only be built underground."; } return NULL; } } // no ground here??? return "Missing ground (fatal!)"; } char const* tool_build_station_t::move(player_t* const player, uint16 const b, koord3d const pos) { if (b == 0) { return NULL; } if (env_t::networkmode) { // queue tool for network nwc_tool_t* nwc = new nwc_tool_t(player, this, pos, welt->get_steps(), welt->get_map_counter(), false); network_send_server(nwc); return NULL; } else { return work(player, pos); } } const char *tool_build_station_t::work( player_t *player, koord3d pos ) { const grund_t *gr = welt->lookup(pos); if(!gr) { return ""; } // ownership allowed? halthandle_t halt = gr->get_halt(); if(halt.is_bound() && !player_t::check_owner( player, halt->get_owner())) { return "Das Feld gehoert\neinem anderen Spieler\n"; } // check underground / above ground if (const char* msg = check_pos(player, pos)) { return msg; } // remove old labels on station tiles if (label_t* l = gr->find()) { if (const char *msg = l->get_removal_error(player)) { return msg; } delete l; } sint8 rotation = 0; const building_desc_t *desc=get_desc(rotation); const char *msg = NULL; switch (desc->get_type()) { case building_desc_t::dock: msg = tool_build_station_t::tool_station_dock_aux(player, pos, desc ); break; case building_desc_t::flat_dock: msg = tool_build_station_t::tool_station_flat_dock_aux(player, pos, desc, rotation ); break; case building_desc_t::generic_extension: msg = tool_build_station_t::tool_station_building_aux(player, false, pos, desc, rotation ); if (msg) { // try to build near a public halt msg = tool_build_station_t::tool_station_building_aux(player, true, pos, desc, rotation ); } break; case building_desc_t::generic_stop: { switch(desc->get_extra()) { case road_wt: msg = tool_build_station_t::tool_station_aux(player, pos, desc, rotation, road_wt, "H"); break; case track_wt: case monorail_wt: case maglev_wt: case narrowgauge_wt: case tram_wt: msg = tool_build_station_t::tool_station_aux(player, pos, desc, rotation, (waytype_t)desc->get_extra(), "BF"); break; case water_wt: msg = tool_build_station_t::tool_station_aux(player, pos, desc, rotation, water_wt, "Dock"); break; case air_wt: msg = tool_build_station_t::tool_station_aux(player, pos, desc, rotation, air_wt, "Airport"); break; } break; } default: dbg->warning("tool_build_station_t::work()","tool called for illegal desc \"%s\"", default_param ); msg = "Illegal station tool"; } return msg; } const char *tool_rotate_building_t::work( player_t *player, koord3d pos ) { const grund_t *gr = welt->lookup(pos); if(!gr) { return ""; } if( gebaeude_t* gb = gr->find() ) { if( !player_t::check_owner( gb->get_owner(), player ) ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } // check for harbour (must not rotate) const building_desc_t *desc = gb->get_tile()->get_desc(); if( desc->get_all_layouts() == 1 ) { // non rotatable => finish return NULL; } if( desc->get_type() == building_desc_t::dock ) { // cannot rotate a harbour return "Cannot rotate this building!"; } if( desc->get_all_layouts()==2 && desc->get_x()!=desc->get_y() ) { // cannot rotate an asymmetric building with only two rotations return "Cannot rotate this building!"; } if( gr->hat_wege() ) { // this is almost certainly a station ... if( desc->get_all_layouts()<16 ) { // either symmetrical (==2, ==8) or freight loading station, so do not rotate! return "Cannot rotate this building!"; } int layout = gb->get_tile()->get_layout(); gb->set_tile( gb->get_tile()->get_desc()->get_tile( layout^8, 0, 0 ), false ); } else { // single and multitile buildings from here, include factories with holes etc. bool rotate180 = desc->get_x() != desc->get_y(); if( desc->get_x() != desc->get_y() && desc->get_all_layouts()==2 ) { // asymmetrical with only one rotation so do not rotate! return "Cannot rotate this building!"; } uint8 layout = gb->get_tile()->get_layout(); uint8 newlayout = (layout+1+rotate180) % desc->get_all_layouts(); vector_tpl gb_tiles; gb->get_tile_list( gb_tiles ); // first test if all tiles are present (check for holes) if( gb_tiles.get_count() < (uint32)desc->get_x( layout ) * (uint32)desc->get_y( layout ) ) { // there are holes ... return "Cannot rotate this building!"; } if( fabrik_t* fab = gb->get_fabrik() ) { fab->set_rotate( (fab->get_rotate() + 1) % fab->get_desc()->get_building()->get_all_layouts() ); } // ok, we can rotate it for(grund_t* gr : gb_tiles ) { gebaeude_t* gb_part = gr->find(); koord k = gr->get_pos().get_2d()-gb_tiles.front()->get_pos().get_2d(); const building_tile_desc_t* tile = desc->get_tile( newlayout, k.x, k.y ); gb_part->set_tile( tile, false ); } } } else if (gr->hat_wege()) { // roate switch graphics for (uint i=0; i < 2; i++) { if (weg_t* w =gr->get_weg_nr(i)) { if (w->get_waytype()!=road_wt && ribi_t::is_threeway(w->get_ribi_unmasked())) { bool sw = w->has_switched(); w->set_switched(!sw); w->set_images(weg_t::image_switch, w->get_ribi_unmasked(), w->is_snow(), !sw); gr->mark_image_dirty(); } } } } return NULL; } tool_build_roadsign_t::tool_build_roadsign_t() : two_click_tool_t(TOOL_BUILD_ROADSIGN | GENERAL_TOOL), desc(NULL) { } const char *tool_build_roadsign_t::get_tooltip(const player_t *) const { const roadsign_desc_t *desc = roadsign_t::find_desc(default_param); return desc ? tooltip_with_price_maintenance(welt, desc->get_name(), -desc->get_price(), desc->get_maintenance()) : NULL; } bool tool_build_roadsign_t::init(player_t *player) { desc = roadsign_t::find_desc(default_param); // take default values from players settings current = signal[player->get_player_nr()]; if (is_ctrl_pressed() && can_use_gui()) { create_win(new signal_spacing_frame_t(player, this), w_info, (ptrdiff_t)this); } return two_click_tool_t::init(player) && desc!=NULL; } bool tool_build_roadsign_t::exit(player_t *player) { destroy_win((ptrdiff_t)this); return two_click_tool_t::exit(player); } void tool_build_roadsign_t::set_values(player_t *player, uint8 spacing, bool remove, bool replace) { signal_info_t &s = signal[player->get_player_nr()]; s.spacing = spacing; s.remove_intermediate = remove; s.replace_other = replace; current = s; } void tool_build_roadsign_t::get_values(player_t *player, uint8 &spacing, bool &remove, bool &replace) { const signal_info_t &s = signal[player->get_player_nr()]; spacing = s.spacing; remove = s.remove_intermediate; replace = s.replace_other; } void tool_build_roadsign_t::draw_after(scr_coord k, bool dirty) const { if( icon!=IMG_EMPTY && is_selected() ) { gfx->draw_img_blend(icon, k.x, k.y, TRANSPARENT50_FLAG|OUTLINE_FLAG|gfx->palette_lookup(COL_BLACK), false, dirty ); char level_str[16]; sprintf(level_str, "%i", signal[welt->get_active_player_nr()].spacing); gfx->draw_text( k.x+4, k.y+4, level_str, ALIGN_LEFT, gfx->palette_lookup(COL_YELLOW), true ); } } void tool_build_roadsign_t::rdwr_custom_data(memory_rw_t *packet) { two_click_tool_t::rdwr_custom_data(packet); packet->rdwr_byte(current.spacing); packet->rdwr_bool(current.remove_intermediate); packet->rdwr_bool(current.replace_other); } waytype_t tool_build_roadsign_t::get_waytype() const { return desc ? desc->get_wtyp() : invalid_wt; } void tool_build_roadsign_t::mark_tiles(player_t *player, const koord3d &start, const koord3d &end) { route_t route; if (!calc_route(route, player, start, end)) { return; } const bool single_ribi = desc->is_signal_type() || desc->is_single_way() || desc->is_choose_sign(); const signal_info_t &s = current; const uint8 signal_density = 2 * s.spacing; //< measured in half tiles (straight track count as 2, diagonal as 1, since sqrt(1/2) = 1/2 ;) uint8 next_signal = signal_density + 1; //< to place a sign asap sint32 cost = 0; directions.clear(); // dummy roadsign to get images for preview roadsign_t *dummy_rs = desc->is_signal_type() ? new signal_t(player, koord3d::invalid, ribi_t::none, desc, true) : new roadsign_t(player, koord3d::invalid, ribi_t::none, desc, true); dummy_rs->set_flag(obj_t::not_on_map); for( route_t::index_t i = 0; i < route.get_count(); i++ ) { grund_t *gr = welt->lookup( route.at(i) ); weg_t *weg = gr->get_weg(desc->get_wtyp()); ribi_t::ribi ribi = weg->get_ribi_unmasked(); // set full ribi when signal is on a crossing. if( single_ribi ) { if(i>0) { // take backward direction ribi = ribi_type(route[i], route[i-1]); } else { // clear one direction bit to get single direction for signal ribi &= ~ribi_type(route[i], route[i+1]); } } roadsign_t *rs = gr->find(); if (rs==NULL) { rs = gr->find(); } if (rs && rs->get_waytype() != desc->get_waytype()) { // do not delete signs from other ways continue; } // check owner .. other signals... const bool straight = (i == 0) || (i == route.get_count()-1) || ribi_t::is_straight(ribi_type(route[i-1], route[i+1])); next_signal += straight ? 2 : 1; if( next_signal >= signal_density ) { // can we place signal here? if(check_pos_intern(player, route[i]) == NULL || (s.replace_other && rs && !rs->get_removal_error(player))) { zeiger_t *zeiger = new zeiger_t(gr->get_pos(), player ); marked.append(zeiger); zeiger->set_image( skinverwaltung_t::bauigelsymbol->get_image_id(0) ); gr->obj_add( zeiger ); directions.append(ribi /* !=0 -> place sign*/); next_signal = 0; dummy_rs->set_pos(gr->get_pos()); dummy_rs->set_dir(ribi); // calls calc_image() zeiger->set_foreground_image(dummy_rs->get_front_image()); zeiger->set_image(dummy_rs->get_image()); cost += rs ? (rs->get_desc()==desc ? 0 : desc->get_price()+rs->get_desc()->get_price()) : desc->get_price(); } } else if(s.remove_intermediate && rs && !rs->get_removal_error(player)) { zeiger_t *zeiger = new zeiger_t(gr->get_pos(), player ); marked.append(zeiger); zeiger->set_image( tool_t::general_tool[TOOL_REMOVER]->cursor ); gr->obj_add( zeiger ); directions.append(ribi_t::none /*remove sign*/); cost += rs->get_desc()->get_price(); } } delete dummy_rs; win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", cost, route.get_count() ) ); } const char *tool_build_roadsign_t::do_work(player_t *player, const koord3d &start, const koord3d &end) { // read data from string desc = roadsign_t::find_desc(default_param); // single click -> place signal if (end == koord3d::invalid || start == end) { grund_t *gr = welt->lookup(start); return place_sign_intern( player, gr ); } // mark tiles to calculate positions of signals mark_tiles(player, start, end); // only search the marked tiles uint32 j=0; for (zeiger_t *const i : marked) { grund_t* const gr = welt->lookup(i->get_pos()); weg_t *weg = gr->get_weg(desc->get_wtyp()); ribi_t::ribi dir = directions[j++]; if (dir) { // try to place signal const char *error_text = place_sign_intern( player, gr ); if( error_text ) { if (signal[player->get_player_nr()].replace_other) { roadsign_t* rs = gr->find(); if(rs == NULL) rs = gr->find(); if( rs != NULL && rs->get_removal_error(player) == NULL ) { rs->cleanup(player); delete rs; error_text = place_sign_intern( player, gr ); } } } if( error_text ) { return error_text; } roadsign_t* rs = gr->find(); if(rs == NULL) rs = gr->find(); assert(rs); rs->set_dir(dir); } else { // Place no signal -> remove existing signal roadsign_t* rs = gr->find(); if(rs == NULL) rs = gr->find(); if( rs != NULL && rs->get_removal_error(player) == NULL ) { rs->cleanup(player); delete rs; }; } weg->count_sign(); gr->calc_image(); } cleanup(); directions.clear(); return NULL; } uint8 tool_build_roadsign_t::is_valid_pos(player_t *player, const koord3d &pos, const char *&error, const koord3d &start) { // first click if (start==koord3d::invalid) { error = check_pos_intern(player, pos); return (error==NULL ? 3 : 0); } // second click else { error = NULL; return 2; } } const char *tool_build_roadsign_t::check_pos_intern(player_t *player, koord3d pos) { static const char *error = "Hier kann kein\nSignal aufge-\nstellt werden!\n"; if (desc==NULL) { // read data from string desc = roadsign_t::find_desc(default_param); } if (desc==NULL) { return error; } // search for starting ground const grund_t *gr = tool_intern_koord_to_weg_grund(player, welt, pos, desc->get_wtyp()); if(!gr) { return error; } // do not replace roadsigns by signals and vice versa (e.g. on level crossings) if ((desc->is_signal_type() && gr->find()) || (!desc->is_signal_type() && gr->find())) { return error; } // get the sign direction const weg_t *weg = gr->get_weg( desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt); const ribi_t::ribi way_ribi = weg->get_ribi_unmasked(); // no signs on runways if( weg->get_waytype() == air_wt && weg->get_desc()->get_styp() == type_runway ) { return error; } // no signals on switches if( ribi_t::is_threeway(way_ribi) && desc->is_signal_type() ) { return error; } if( desc->is_private_way() && !ribi_t::is_straight(way_ribi) ) { // only on straight tiles return error; } const bool needs_two_ways = desc->is_single_way() || desc->is_signal_type(); const bool valid_signal_pos = (!needs_two_ways && !desc->is_traffic_light()) || ( needs_two_ways && ribi_t::is_twoway(way_ribi)) || (desc->is_traffic_light() && ribi_t::is_threeway(way_ribi)); if(!valid_signal_pos) { return error; } const roadsign_t *rs = desc->is_signal_type() ? gr->find() : gr->find(); if (rs && !player_t::check_owner(rs->get_owner(), player)) { return "Das Feld gehoert\neinem anderen Spieler\n"; } return NULL; } const char *tool_build_roadsign_t::place_sign_intern(player_t *player, grund_t *gr) { static const char *error = "Hier kann kein\nSignal aufge-\nstellt werden!\n"; // search for starting ground if(!gr) { return error; } // get the sign direction weg_t *weg = gr->get_weg( desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt); roadsign_t *s = gr->find(); if(s==NULL) { s = gr->find(); } if(s && (!s->get_desc()->is_signal_type() && s->get_desc() != desc)) { // can only replace signals return error; } ribi_t::ribi dir = weg->get_ribi_unmasked(); const bool two_way = desc->is_single_way() || desc->is_signal_type(); const bool valid_sign_location = !(desc->is_traffic_light() || two_way) || (two_way && ribi_t::is_twoway(dir)) || (desc->is_traffic_light() && ribi_t::is_threeway(dir)); if(!valid_sign_location) { return error; } roadsign_t *rs = NULL; if (desc->is_signal_type()) { // if there is already a signal, we might need to inverse the direction rs = gr->find(); if (!rs) { // add a new signal at position zero! rs = new signal_t(player, gr->get_pos(), dir, desc); DBG_MESSAGE("tool_roadsign()", "new signal, dir is %i", dir); goto built_sign; } if( !player_t::check_owner( rs->get_owner(), player ) ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } if (rs->get_desc() == desc) { // signals have three options ribi_t::ribi sig_dir = rs->get_dir(); uint8 i = 0; if (!ribi_t::is_twoway(sig_dir)) { // inverse first dir for (; i < 4; i++) { if ((dir & ribi_t::nesw[i]) == sig_dir) { i++; break; } } } // find the second dir ... for (; i < 4; i++) { if ((dir & ribi_t::nesw[i]) != 0) { dir = ribi_t::nesw[i]; } } // if nothing found, we have two ways again ... rs->set_dir(dir); } // different desc -> replace signal else if (rs->get_removal_error(player) == NULL) { ribi_t::ribi old_dir = rs->get_dir(); rs->cleanup(player); delete rs; rs = new signal_t(player, gr->get_pos(), dir, desc); rs->set_dir(old_dir); goto built_sign; } } else { // if there is already a sign, we might need to invert the direction rs = gr->find(); if (rs) { if( !player_t::check_owner( rs->get_owner(), player ) ) { return "Das Feld gehoert\neinem anderen Spieler\n"; } // reverse only if single way sign if (desc->is_single_way() || desc->is_choose_sign()) { dir = ~rs->get_dir() & weg->get_ribi_unmasked(); rs->set_dir(dir); DBG_MESSAGE("tool_roadsign()", "reverse ribi %i", dir); } } else { // add a new roadsign at position zero! // if single way, we need to reduce the allowed ribi to one if (desc->is_single_way() || desc->is_choose_sign()) { for( int i=0; i<4; i++ ) { if ((dir & ribi_t::nesw[i]) != 0) { dir = ribi_t::nesw[i]; break; } } } DBG_MESSAGE("tool_roadsign()", "new roadsign, dir is %i", dir); rs = new roadsign_t(player, gr->get_pos(), dir, desc); built_sign: gr->obj_add(rs); rs->finish_rd(); // to make them visible, and add maintenance weg->count_sign(); player_t::book_construction_costs(player, -desc->get_price(), gr->get_pos().get_2d(), weg->get_waytype()); } } return NULL; } bool tool_build_roadsign_t::calc_route(route_t &route, player_t *player, const koord3d &from, const koord3d &to) { // get a default vehicle waytype_t waytype = desc->get_waytype() == tram_wt ? track_wt : desc->get_waytype(); test_driver_t *test_driver = new way_checker_t(waytype); test_driver = scenario_checker_t::apply(test_driver, player, this); bool can_build = false; if( from != to ) { can_build = route.calc_route(welt, from, to, test_driver, 0, 0); // prevent building of many signals if start and to are adjacent // but the step start->to is now allowed if (can_build && koord_distance(from, to)==1 && route.get_count()>2) { grund_t *gr, *grto = welt->lookup(to); if( welt->lookup(from)->get_neighbour(gr, desc->get_wtyp(), ribi_type(to-from) ) && gr==grto) { can_build = false; } } } else { route.clear(); route.append(from); can_build = true; } delete test_driver; return can_build; } // build all types of depots const char* tool_build_depot_t::tool_depot_aux(player_t* player, koord3d pos, const building_desc_t* desc, waytype_t wegtype) { if (!welt->is_within_limits(pos.get_2d())) { return ""; } grund_t* bd = NULL; // special for the Seven Seas ... if (wegtype == water_wt) { bd = welt->lookup_kartenboden(pos.get_2d()); if (!bd->is_water()) { if (!bd->hat_weg(water_wt)) { return "Ship depots must be built on water!"; } bd = NULL; } } if (!bd) { bd = tool_intern_koord_to_weg_grund(player, welt, pos, wegtype); } if (!bd) { return "Depots must be built on flat dead-end way tiles!"; } else if (bd->has_two_ways() || bd->is_halt() || bd->get_depot() != NULL) { // avoid building over a stop or on a second way return "Tile not empty."; } // no depots on runways! if (desc->get_extra() == air_wt && bd->get_weg(air_wt)->get_desc()->get_styp() != type_flat) { return "Depots cannot be built on runways!"; } if (const char* errmsg = bd->kann_alle_obj_entfernen(player)) { return errmsg; } ribi_t::ribi ribi; if (bd->is_water()) { // assume one orientation with water ribi = ribi_t::south; } else { ribi = bd->get_weg_ribi_unmasked(wegtype); } int layout = 0; if (bd->get_weg_hang() != slope_t::flat) { return "Depots must be built on flat dead-end way tiles!"; } if (desc->get_all_layouts() == 4) { // classical four rotation depot if (!ribi_t::is_single(ribi)) { return "Depots must be built on flat dead-end way tiles!"; } switch (ribi) { //case ribi_t::south:layout = 0; break; case ribi_t::east: layout = 1; break; case ribi_t::north: layout = 2; break; case ribi_t::west: layout = 3; break; } } else if (desc->get_all_layouts() == 2) { // two rotation through depot if (!ribi_t::is_straight(ribi)) { return "Depots must be built on flat dead-end way tiles!"; } layout = ribi_t::is_straight_ew(ribi); } else { dbg->warning("tool_build_depot_t::tool_depot_aux()", "Broken depot name \"%s\"", desc->get_name()); } hausbauer_t::build_station_extension_depot(player, bd->get_pos(), layout, desc ); player_t::book_construction_costs(player, -desc->get_price(welt), pos.get_2d(), desc->get_finance_waytype()); if(can_use_gui() && player == welt->get_active_player()) { welt->set_tool( general_tool[TOOL_QUERY], player ); } return NULL; } image_id tool_build_depot_t::get_icon(player_t *player) const { if( player && !player->is_public_service() ) { const building_desc_t *desc = hausbauer_t::find_tile(default_param,0)->get_desc(); const uint16 time = welt->get_timeline_year_month(); if( desc && desc->is_available(time) ) { return desc->get_cursor()->get_image_id(1); } } return IMG_EMPTY; } bool tool_build_depot_t::init( player_t *player ) { if (default_param == NULL) { return false; } const building_tile_desc_t *tile_desc = hausbauer_t::find_tile(default_param, 0); building_desc_t const* desc = tile_desc ? tile_desc->get_desc() : NULL; if (desc == NULL || desc->get_cursor()==NULL) { return false; } // no depots for player 1 if(!player->is_public_service()) { cursor = desc->get_cursor()->get_image_id(0); return true; } return false; } const char* tool_build_depot_t::get_tooltip(const player_t *) const { settings_t const& settings = welt->get_settings(); building_desc_t const* desc = hausbauer_t::find_tile(default_param, 0)->get_desc(); if (desc == NULL) { return NULL; } char const* tip; switch (desc->get_extra()) { case road_wt: tip = "Build road depot"; break; case track_wt: tip = "Build train depot"; break; case monorail_wt: tip = "Build monorail depot"; break; case maglev_wt: tip = "Build maglev depot"; break; case narrowgauge_wt: tip = "Build narrowgauge depot"; break; case tram_wt: tip = "Build tram depot"; break; case water_wt: tip = "Build ship depot"; break; case air_wt: tip = "Build air depot"; break; default: return 0; } return tooltip_with_price_maintenance(welt, tip, -desc->get_price(welt), settings.maint_building * desc->get_level()); } waytype_t tool_build_depot_t::get_waytype() const { const building_desc_t *desc = hausbauer_t::find_tile(default_param,0)->get_desc(); return desc ? (waytype_t)desc->get_extra() : invalid_wt; } const char *tool_build_depot_t::work( player_t *player, koord3d pos ) { if(player==welt->get_public_player()) { // no depots for player 1 return 0; } building_desc_t const* const desc = hausbauer_t::find_tile(default_param,0)->get_desc(); switch(desc->get_extra()) { case road_wt: case track_wt: case water_wt: case air_wt: case maglev_wt: case narrowgauge_wt: return tool_build_depot_t::tool_depot_aux(player, pos, desc, (waytype_t)desc->get_extra()); case monorail_wt: { // since it needs also a foundation, this is slightly more complex ... char const* const err = tool_build_depot_t::tool_depot_aux(player, pos, desc, monorail_wt); if(err==NULL) { grund_t *bd = welt->lookup_kartenboden(pos.get_2d()); if(hausbauer_t::elevated_foundation_desc && pos.z-bd->get_pos().z==1 && bd->ist_natur()) { hausbauer_t::build(player, pos.get_2d(), 0, hausbauer_t::elevated_foundation_desc ); } } return err; } case tram_wt: return tool_build_depot_t::tool_depot_aux(player, pos, desc, track_wt); default: dbg->warning("tool_build_depot()","called with unknown desc %s",desc->get_name() ); return "Unknown depot object"; } return NULL; } /** * builds (random) tourist attraction and maybe adds it to the next city * the parameter string is a follow: * 1#theater * first letter: ignore climates * second letter: rotation (0,1,2,3,#=random) * finally building name */ bool tool_build_house_t::init( player_t * ) { if (default_param && strlen(default_param) < 3) { return false; } if (can_use_gui() && !strempty(default_param)) { const char *c = default_param+2; const building_tile_desc_t *tile = hausbauer_t::find_tile(c,0); if(tile!=NULL) { const int rotation = (default_param[1]-'0') % tile->get_desc()->get_all_layouts(); cursor_area = tile->get_desc()->get_size(rotation); } } return true; } const char *tool_build_house_t::work( player_t *player, koord3d pos ) { koord k(pos.get_2d()); const grund_t* gr = welt->lookup_kartenboden(k); if(gr==NULL) { return ""; } // Parsing parameter (if there) const building_desc_t *desc = NULL; if (!strempty(default_param)) { const char *c = default_param+2; const building_tile_desc_t *tile = hausbauer_t::find_tile(c,0); if(tile) { desc = tile->get_desc(); } } else { desc = hausbauer_t::get_random_attraction( welt->get_timeline_year_month(), false, welt->get_climate( k ) ); } if(desc==NULL) { return ""; } int rotation; if( !default_param || default_param[1]=='#' ) { rotation = simrand(desc->get_all_layouts()); } else if( default_param[1]=='A' ) { if( desc->get_type()!=building_desc_t::attraction_land && desc->get_type()!=building_desc_t::attraction_city ) { // auto rotation only valid for city buildings koord max_size(max(desc->get_size().x, desc->get_size().y), max(desc->get_size().x, desc->get_size().y)); rotation = stadt_t::orient_city_building( k, desc, max_size); if( rotation < 0 ) { return NOTICE_UNSUITABLE_GROUND; } } else { rotation = simrand(desc->get_all_layouts()); } } else { rotation = (default_param[1]-'0') % desc->get_all_layouts(); } koord size = desc->get_size(rotation); if (stadt_t* city = welt->find_nearest_city(k)) { // we need a city for it // process ignore climates switch climate_bits cl = (default_param && default_param[0]=='1') ? ALL_CLIMATES : desc->get_allowed_climate_bits(); bool hat_platz = welt->square_is_free( k, desc->get_x(rotation), desc->get_y(rotation), NULL, cl ); if(!hat_platz && size.y!=size.x && desc->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#' || default_param[1]=='A')) { // try other rotation too ... rotation = (rotation+1) % desc->get_all_layouts(); hat_platz = welt->square_is_free( k, desc->get_x(rotation), desc->get_y(rotation), NULL, cl ); } // Place found... if(hat_platz) { player_t *gb_player = desc->is_city_building() ? NULL : welt->get_public_player(); gebaeude_t *gb = hausbauer_t::build(gb_player, k, rotation, desc, city); if(gb) { // building successful if( desc->get_type()==building_desc_t::monument ) { city->add_gebaeude_to_stadt(gb); } player_t::book_construction_costs(player, -desc->get_price(welt) * size.x * size.y, k, gb->get_waytype()); return NULL; } } } return NOTICE_UNSUITABLE_GROUND; } // show industry size in cursor (in known) bool tool_build_land_chain_t::init( player_t * ) { if (can_use_gui() && !strempty(default_param)) { const char *c = default_param+2; while(*c && *c++!=',') { /* do nothing */ } const factory_desc_t *fab = factory_builder_t::get_desc(c); if(fab==NULL) { // wrong tool! return false; } int rotation = (default_param[1]-'0') % fab->get_building()->get_all_layouts(); cursor_area = fab->get_building()->get_size(rotation); } return true; } /* builds a (if param=NULL random) industry chain starting here * * the parameter string is a follow: * 1#34,oelfeld * first letter: ignore climates * second letter: rotation (0,1,2,3,#=random) * next number is production value * finally industry name */ const char *tool_build_land_chain_t::work( player_t *player, koord3d pos ) { const grund_t* gr = welt->lookup_kartenboden(pos.get_2d()); if(gr==NULL) { return ""; } const factory_desc_t *fab = NULL; if (!strempty(default_param)) { const char *c = default_param+2; while(*c && *c++!=',') { /* do nothing */ } fab = factory_builder_t::get_desc(c); } else { fab = factory_builder_t::get_random_consumer( false, (climate_bits)(1 << welt->get_climate( pos.get_2d() )), welt->get_timeline_year_month() ); } if(fab==NULL) { return ""; } int rotation = (default_param && default_param[1]!='#') ? (default_param[1]-'0') % fab->get_building()->get_all_layouts() : simrand(fab->get_building()->get_all_layouts()-1); koord size = fab->get_building()->get_size(rotation); // process ignore climates switch bool ignore_climates = default_param && default_param[0]=='1'; climate_bits cl = ignore_climates ? ALL_CLIMATES : fab->get_building()->get_allowed_climate_bits(); bool hat_platz = false; if(fab->get_placement()==factory_desc_t::Water) { // at sea hat_platz = welt->is_water( pos.get_2d(), fab->get_building()->get_size(rotation) ); if(!hat_platz && size.y!=size.x && fab->get_building()->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#')) { // try other rotation too ... rotation = (rotation+1) % fab->get_building()->get_all_layouts(); hat_platz = welt->is_water( pos.get_2d(), fab->get_building()->get_size(rotation) ); } } else { // and on solid ground hat_platz = welt->square_is_free( pos.get_2d(), fab->get_building()->get_x(rotation), fab->get_building()->get_y(rotation), NULL, cl ); if(!hat_platz && size.y!=size.x && fab->get_building()->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#')) { // try other rotation too ... rotation = (rotation+1) % fab->get_building()->get_all_layouts(); hat_platz = welt->square_is_free( pos.get_2d(), fab->get_building()->get_x(rotation), fab->get_building()->get_y(rotation), NULL, cl ); } } if(hat_platz) { // eventually adjust production sint32 initial_prod = -1; if (!strempty(default_param)) { initial_prod = welt->inverse_scale_with_month_length( atol(default_param+2) ); } koord3d build_pos = gr->get_pos(); int count = factory_builder_t::build_link(NULL, fab, initial_prod, rotation, &build_pos, welt->get_public_player(), 10000, ignore_climates); if(count>0) { // at least one factory has been built welt->get_viewport()->change_world_position( build_pos ); player_t::book_construction_costs(player, count * welt->get_settings().cst_multiply_found_industry, build_pos.get_2d(), ignore_wt); // crossconnect all? if (welt->get_settings().is_crossconnect_factories()) { for(fabrik_t* const f : welt->get_fab_list()) { f->add_all_suppliers(); } } return NULL; } } return NOTICE_UNSUITABLE_GROUND; } // show industry size in cursor (in known) bool tool_city_chain_t::init( player_t * ) { if (can_use_gui() && !strempty(default_param)) { const char *c = default_param+2; while(*c && *c++!=',') { /* do nothing */ } const factory_desc_t *fab = factory_builder_t::get_desc(c); if(fab==NULL) { // wrong tool! return false; } int rotation = (default_param[1]-'0') % fab->get_building()->get_all_layouts(); cursor_area = fab->get_building()->get_size(rotation); } return true; } /* builds a industry chain in the next town * default_param see previous function */ const char *tool_city_chain_t::work( player_t *player, koord3d pos ) { const grund_t* gr = welt->lookup_kartenboden(pos.get_2d()); if(gr==NULL) { return ""; } const factory_desc_t *fab = NULL; if (!strempty(default_param)) { const char *c = default_param+2; while(*c && *c++!=',') { /* do nothing */ } fab = factory_builder_t::get_desc(c); } else { fab = factory_builder_t::get_random_consumer( false, (climate_bits)(1 << welt->get_climate( pos.get_2d() )), welt->get_timeline_year_month() ); } if(fab==NULL) { return ""; } // eventually adjust production sint32 initial_prod = -1; if (!strempty(default_param)) { initial_prod = welt->inverse_scale_with_month_length( atol(default_param+2) ); } // process ignore climates switch bool ignore_climates = default_param && default_param[0]=='1'; pos = gr->get_pos(); int count = factory_builder_t::build_link(NULL, fab, initial_prod, 0, &pos, welt->get_public_player(), 10000, ignore_climates); if(count>0) { // at least one factory has been built welt->get_viewport()->change_world_position( pos ); // crossconnect all? if (welt->get_settings().is_crossconnect_factories()) { for(fabrik_t* const f : welt->get_fab_list()) { f->add_all_suppliers(); } } // ain't going to be cheap player_t::book_construction_costs(player, count * welt->get_settings().cst_multiply_found_industry, pos.get_2d(), ignore_wt); return NULL; } return NOTICE_UNSUITABLE_GROUND; } // show industry size in cursor (must be known!) bool tool_build_factory_t::init( player_t * ) { if (can_use_gui() && !strempty(default_param)) { const char *c = default_param+2; while(*c && *c++!=',') { /* do nothing */ } const factory_desc_t *fab = factory_builder_t::get_desc(c); if(fab==NULL) { // wrong tool! return false; } int rotation = (default_param[1]-'0') % fab->get_building()->get_all_layouts(); cursor_area = fab->get_building()->get_size(rotation); return true; } return true; } bool tool_build_factory_t::can_build_factory_here(const factory_desc_t* fab, koord pos, int rotation, climate_bits cl) { const koord rotated_size = fab->get_building()->get_size(rotation); if (fab->get_placement() == factory_desc_t::Water) { if (!welt->is_water(pos, rotated_size)) { // this als checks if all coords are inside the map return false; } for (sint16 y = 0; y < rotated_size.x; ++y) { for (sint16 x = 0; x < rotated_size.y; ++x) { if (welt->lookup_kartenboden_nocheck(pos + koord(x,y))->find()) { return false; // some other water-building (e.g. factory) in the way } } } } // solid ground else if (!welt->square_is_free(pos, rotated_size.x, rotated_size.y, NULL, cl )) { return false; } return true; } /* builds an industry next to the cursor (default_param see above) */ const char *tool_build_factory_t::work( player_t *player, koord3d pos ) { const grund_t* gr = welt->lookup_kartenboden(pos.get_2d()); if(gr==NULL) { return ""; } const factory_desc_t *fab = NULL; if (!strempty(default_param)) { const char *c = default_param+2; while(*c && *c++!=',') { /* do nothing */ } fab = factory_builder_t::get_desc(c); } else { fab = factory_builder_t::get_random_consumer( false, (climate_bits)(1 << welt->get_climate( pos.get_2d() )), welt->get_timeline_year_month() ); } if(fab==NULL) { return ""; } int rotation = (default_param && default_param[1]!='#') ? (default_param[1]-'0') % fab->get_building()->get_all_layouts() : simrand(fab->get_building()->get_all_layouts()); // process ignore climates switch climate_bits cl = (default_param && default_param[0]=='1') ? ALL_CLIMATES : fab->get_building()->get_allowed_climate_bits(); bool hat_platz = this->can_build_factory_here(fab, pos.get_2d(), rotation, cl); if (!hat_platz && fab->get_building()->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#')) { // try other rotation rotation = (rotation +1) % fab->get_building()->get_all_layouts(); hat_platz = this->can_build_factory_here(fab, pos.get_2d(), rotation, cl); } if(hat_platz) { // eventually adjust production sint32 initial_prod = -1; if (!strempty(default_param)) { initial_prod = welt->inverse_scale_with_month_length( atol(default_param+2) ); } fabrik_t *f = factory_builder_t::build_factory(NULL, fab, initial_prod, rotation, gr->get_pos(), welt->get_public_player()); if(f) { // at least one factory has been built // === removed on player request === welt->get_viewport()->change_world_position( pos ); player_t::book_construction_costs(player, welt->get_settings().cst_multiply_found_industry, pos.get_2d(), ignore_wt); // crossconnect all? if (welt->get_settings().is_crossconnect_factories()) { for(fabrik_t* const f : welt->get_fab_list()) { f->add_all_suppliers(); } } return NULL; } } return NOTICE_UNSUITABLE_GROUND; } /** * link tool: links products of factory one with factory two (if possible) */ image_id tool_link_factory_t::get_marker_image() const { return cursor; } uint8 tool_link_factory_t::is_valid_pos( player_t *, const koord3d &pos, const char *&error, const koord3d & ) { fabrik_t *fab = fabrik_t::get_fab( pos.get_2d() ); if (fab == NULL) { error = ""; return 0; } return 2; } const char *tool_link_factory_t::do_work( player_t *, const koord3d &start, const koord3d &pos ) { fabrik_t *last_fab = fabrik_t::get_fab( start.get_2d() ); fabrik_t *fab = fabrik_t::get_fab( pos.get_2d() ); if (world()->get_settings().is_crossconnect_factories()) { // make no sense when set return ""; } if(fab!=NULL && last_fab!=NULL && last_fab!=fab) { // It's a factory if(!is_ctrl_pressed()) { if(fab->add_supplier(last_fab) || last_fab->add_supplier(fab)) { //ok! they are connected return NULL; } } else { // remove connections fab->remove_supplier(last_fab->get_pos().get_2d()); fab->remove_consumer(last_fab->get_pos().get_2d()); last_fab->remove_supplier(fab->get_pos().get_2d()); last_fab->remove_consumer(fab->get_pos().get_2d()); return NULL; } } return ""; } const building_desc_t *tool_headquarter_t::next_level( const player_t *player ) const { return hausbauer_t::get_headquarters(player->get_headquarter_level(), welt->get_timeline_year_month()); } const char* tool_headquarter_t::get_tooltip(const player_t *player) const { static char my_toolstr[1024]; if (building_desc_t const* const desc = next_level(player)) { char old_toolstr[1024]; /* since there is one static toolstr for all tools uing tooltip_with_... * but we are also called every frame from finance window, we need to restore old content * or all tooltips generated before will only show our message */ tstrncpy(old_toolstr, tool_t::toolstr, 1024); settings_t const& s = welt->get_settings(); char const* const tip = player->get_headquarter_level() == 0 ? "build HQ" : "upgrade HQ"; sint64 const factor = desc->get_x() * desc->get_y(); strcpy( my_toolstr, tooltip_with_price_maintenance(welt, tip, -factor * desc->get_price(welt), factor * desc->get_level() * s.maint_building) ); tstrncpy(tool_t::toolstr, old_toolstr, 1024); return my_toolstr; } return NULL; } bool tool_headquarter_t::init( player_t *player ) { // do no use this, if there is no next level to build ... const building_desc_t *desc = next_level(player); if (desc) { if (can_use_gui()) { const int rotation = 0; cursor_area = desc->get_size(rotation); } return true; } return false; } const char *tool_headquarter_t::work( player_t *player, koord3d pos ) { bool ok=false; bool built = false; DBG_MESSAGE("tool_headquarter()", "building headquarters at (%d,%d)", pos.x, pos.y); const building_desc_t* desc = next_level(player); if(desc==NULL) { // no further headquarters level dbg->message( "tool_headquarter()", "Already at maximum level!" ); return ""; } // check funds koord size = desc->get_size(); sint64 const cost = -desc->get_price(welt) * size.x * size.y; if( !player->can_afford(cost) ) { return NOTICE_INSUFFICIENT_FUNDS; } koord k(pos.get_2d()); grund_t *gr = welt->lookup_kartenboden(k); if(gr) { gebaeude_t *hq = NULL; // check for current head quarter koord previous = player->get_headquarter_pos(); if(previous!=koord::invalid) { grund_t *gr_hq = welt->lookup_kartenboden(previous); gebaeude_t *prev_hq = gr_hq->find(); // check if upgrade should be built at same place as current one gebaeude_t *gb = gr->find(); if (gb && gb->get_owner()==player && prev_hq->get_tile()->get_desc()==gb->get_tile()->get_desc()) { const building_desc_t* prev_desc = prev_hq->get_tile()->get_desc(); // check if sizes fit uint8 prev_layout = prev_hq->get_tile()->get_layout(); uint8 layout = prev_layout % desc->get_all_layouts(); koord size = desc->get_size(layout); if (prev_desc->get_size(prev_layout) == size) { // check for same tile structure ok = true; for (sint16 x=0; xget_tile(prev_layout, x, y)==NULL)==(desc->get_tile(layout, x, y)==NULL); } } hq = gb; if( ok ) { // upgrade the tiles koord k_hq = k - gb->get_tile()->get_offset(); for( sint16 x = 0; x < size.x; x++ ) { for( sint16 y = 0; y < size.y; y++ ) { if( const building_tile_desc_t *tile = desc->get_tile(layout, x, y) ) { if( grund_t *gr2 = welt->lookup_kartenboden(k_hq + koord(x, y)) ) { if( gebaeude_t *gb = gr2->find() ) { if( gb && gb->get_owner() == player && prev_desc == gb->get_tile()->get_desc() ) { player_t::add_maintenance( player, -prev_desc->get_maintenance(welt), prev_desc->get_finance_waytype() ); gb->set_tile( tile, true ); gb->calc_image(); player_t::add_maintenance( player, desc->get_maintenance(welt), desc->get_finance_waytype() ); } } } } } } built = true; } } } // did not upgrade old one, need to remove it if( !built ) { // remove previous one hausbauer_t::remove( player, prev_hq ); // resize cursor init(player); } } // build new one if (!built) { int rotate = 0; if(welt->square_is_free(k, size.x, size.y, NULL, desc->get_allowed_climate_bits())) { ok = true; } if(!ok && desc->get_all_layouts()>1 && size.y != size.x && welt->square_is_free(k, size.y, size.x, NULL, desc->get_allowed_climate_bits())) { rotate = 1; ok = true; } if(ok) { // then built it hq = hausbauer_t::build(player, k, rotate, desc, NULL); stadt_t *city = welt->find_nearest_city( k ); if(city) { city->add_gebaeude_to_stadt( hq ); } built = true; } else { return NOTICE_UNSUITABLE_GROUND; } } if( built ) { // sometimes those are not correct after rotation ... player->add_headquarter( desc->get_extra() + 1, hq->get_pos().get_2d() - hq->get_tile()->get_offset() ); player_t::book_construction_costs(player, cost, k, ignore_wt); // tell the world of it ... cbuffer_t buf; buf.printf( translator::translate("%s s\nheadquarter now\nat (%i,%i)."), player->get_name(), pos.x, pos.y ); welt->get_message()->add_message( buf, hq->get_pos(), message_t::ai, PLAYER_FLAG|player->get_player_nr(), hq->get_tile()->get_background(0,0,0) ); // reset to query tool, since costly relocations should be avoided if(can_use_gui() && player == welt->get_active_player()) { welt->set_tool( tool_t::general_tool[TOOL_QUERY], player ); } return NULL; } } return ""; } const char *tool_lock_game_t::work( player_t *, koord3d ) { // tool can never be executed in network mode if (env_t::networkmode) { return ""; } // as the result depends on the local locked state of public player if (welt->get_public_player()->is_locked() || !welt->get_settings().get_allow_player_change()) { return "Only public player can lock games!"; } welt->clear_player_password_hashes(); if( !welt->get_public_player()->is_locked() ) { return "In order to lock the game, you have to protect the public player by password!"; } destroy_all_win( true ); welt->switch_active_player( 0, true ); welt->get_settings().set_allow_player_change(false); welt->set_tool( general_tool[TOOL_QUERY], welt->get_player(0) ); return NULL; } const char *tool_add_citycar_t::work( player_t *player, koord3d pos ) { if( private_car_t::list_empty() ) { // No citycar return ""; } grund_t *gr = tool_intern_koord_to_weg_grund( player, welt, pos, road_wt ); if( gr != NULL && ribi_t::is_twoway(gr->get_weg_ribi_unmasked(road_wt)) && gr->find() == NULL) { // add citycar private_car_t* vt = new private_car_t(gr, koord::invalid, default_param); gr->obj_add(vt); return NULL; } return ""; } uint8 tool_forest_t::is_valid_pos( player_t *, const koord3d &, const char *&, const koord3d & ) { // do really nothing ... return 2; } void tool_forest_t::mark_tiles( player_t *, const koord3d &start, const koord3d &end ) { koord k1, k2; k1.x = start.x < end.x ? start.x : end.x; k1.y = start.y < end.y ? start.y : end.y; k2.x = start.x + end.x - k1.x; k2.y = start.y + end.y - k1.y; koord k; for( k.x = k1.x; k.x <= k2.x; k.x++ ) { for( k.y = k1.y; k.y <= k2.y; k.y++ ) { grund_t *gr = welt->lookup_kartenboden_nocheck( k ); // use grund_hang only and not weg_hang since the tool only operates on kartenboden. // In particular, dragging the tool across a hill in sliced underground mode // affects the invisible tiles at the top of the hill and not any tunnel tiles. const slope_t::type grund_hang = gr->get_grund_hang(); zeiger_t *marker = new zeiger_t(gr->get_pos(), NULL ); marker->set_foreground_image(ground_desc_t::get_marker_image(grund_hang, false)); marker->set_image( ground_desc_t::get_marker_image(grund_hang, true )); marker->mark_image_dirty( marker->get_image(), 0 ); gr->obj_add( marker ); marked.insert( marker ); } } } const char *tool_forest_t::do_work( player_t *player, const koord3d &start, const koord3d &end ) { koord wh, nw; wh.x = abs(end.x-start.x)+1; wh.y = abs(end.y-start.y)+1; nw.x = min(start.x, end.x)+(wh.x/2); nw.y = min(start.y, end.y)+(wh.y/2); sint64 costs = tree_builder_t::create_forest( nw, wh, 0, 0, welt->get_size().x, welt->get_size().y ); player_t::book_construction_costs(player, costs * welt->get_settings().cst_remove_tree, end.get_2d(), ignore_wt); return NULL; } image_id tool_stop_mover_t::get_marker_image() const { return cursor; } void tool_stop_mover_t::read_start_position(player_t *player, const koord3d &pos) { waytype[0] = invalid_wt; waytype[1] = invalid_wt; last_halt = halthandle_t(); grund_t *bd = welt->lookup(pos); if (bd==NULL) { return; } // now assign waytypes if(bd->is_water()) { waytype[0] = water_wt; } else { waytype[0] = bd->get_weg_nr(0)->get_waytype(); if(bd->get_weg_nr(1)) { waytype[1] = bd->get_weg_nr(1)->get_waytype(); } } // .. and halt last_halt = haltestelle_t::get_halt(pos,player); } uint8 tool_stop_mover_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d &start) { grund_t *bd = welt->lookup(pos); if (bd==NULL) { error = ""; return 0; } // check halt ownership halthandle_t h = haltestelle_t::get_halt(pos,player); if( bd->is_halt() && !h.is_bound() ) { error = "Das Feld gehoert\neinem anderen Spieler\n"; return 0; } // check for halt on the tile if( h.is_bound() && !(bd->is_halt() || (h->get_station_type()&haltestelle_t::dock && bd->is_water()) ) ) { error = NOTICE_UNSUITABLE_GROUND; return 0; } if (start==koord3d::invalid) { // check for existing ways if (bd->is_water() || bd->hat_wege()) { return 2; } else { error = NOTICE_UNSUITABLE_GROUND; return 0; } } else { // read conditions at start point read_start_position(player, start); // check halts vs waypoints if(h.is_bound() ^ last_halt.is_bound()) { error = "Can only move from halt to halt or waypoint to waypoint."; return 0; } // check waytypes if( (waytype[0] == water_wt && bd->is_water()) || bd->hat_weg(waytype[0]) || bd->hat_weg(waytype[1]) ) { // ok return 2; } else { error = NOTICE_UNSUITABLE_GROUND; return 0; } } } const char *tool_stop_mover_t::do_work( player_t *player, const koord3d &last_pos, const koord3d &pos) { // read conditions at start point read_start_position(player, last_pos); // second click grund_t *bd = welt->lookup(pos); halthandle_t h = haltestelle_t::get_halt(pos,player); if (bd) { const halthandle_t new_halt = h; // depending on the waytype we simply build replacements lists // in the worst case we have to iterate over all tiles twice ... for( uint i=0; i<2; i++ ) { const waytype_t wt = waytype[i]; slist_tpl old_platform; if(bd->is_water()) { if(wt!=water_wt) { break; } } else if(!bd->hat_weg(wt)) { continue; } // platform, stop or just tile moving? const bool catch_all_halt = (wt==water_wt || wt==air_wt) && last_halt.is_bound(); if(!last_halt.is_bound()) { old_platform.append(last_pos); } else if(!catch_all_halt) { // builds a coordinate list if(wt==road_wt) { old_platform.append(last_pos); } else { // all connected tiles for start pos uint8 ribi = welt->lookup(last_pos)->get_weg_ribi_unmasked(wt); koord delta = ribi_t::is_straight_ns(ribi) ? koord(0,1) : koord(1,0); koord3d start_pos=last_pos; while(ribi&12) { koord3d test_pos = start_pos+delta; grund_t *gr = welt->lookup(test_pos); if(!gr || !gr->is_halt() || (ribi=gr->get_weg_ribi_unmasked(wt))==0) { break; } start_pos = test_pos; } // now add all of them while(ribi&3) { koord3d test_pos = start_pos-delta; grund_t *gr = welt->lookup(test_pos); old_platform.append(start_pos); if(!gr || !gr->is_halt() || (ribi=gr->get_weg_ribi_unmasked(wt))==0) { break; } start_pos = test_pos; } } } // first, check convoi without line for(convoihandle_t const cnv : welt->convoys()) { // check line and owner if(!cnv->get_line().is_bound() && cnv->get_owner()==player) { schedule_t *schedule = cnv->get_schedule(); // check waytype if(schedule && schedule->is_stop_allowed(bd)) { bool updated = false; for(schedule_entry_t & k : schedule->entries) { if ((catch_all_halt && haltestelle_t::get_halt( k.pos, cnv->get_owner()) == last_halt) || old_platform.is_contained(k.pos)) { k.pos = pos; updated = true; } } if(updated) { schedule->make_valid(); if( !schedule->is_editing_finished() ) { // schedule is not owned by schedule window ... // ... thus we can set this schedule cnv->set_schedule(schedule); // otherwise the schedule window will reset it } } } } } // next, check lines serving old_halt (no owner check needed for own lines ... vector_tpllines; player->simlinemgmt.get_lines(simline_t::line,&lines); for(linehandle_t const line : lines) { schedule_t *schedule = line->get_schedule(); // check waytype if(schedule->is_stop_allowed(bd)) { bool updated = false; for(schedule_entry_t & k : schedule->entries) { // ok! if ((catch_all_halt && haltestelle_t::get_halt( k.pos, line->get_owner()) == last_halt) || old_platform.is_contained(k.pos)) { k.pos = pos; updated = true; } } // update line if(updated) { schedule->make_valid(); } } } } if (new_halt != last_halt) { // we handle it here to properly remove stale freight which could otherwise linger and cause wrong routing bool changed = false; if (last_halt.is_bound()) { changed = last_halt->rebuilt_schedule_registration(true, false); } if (new_halt.is_bound()) { changed |= new_halt->rebuilt_schedule_registration(false, true); } if (changed) { // needs rerouting as something was newly assigned/removed welt->set_schedule_counter(); } } } return NULL; } char const* tool_daynight_level_t::get_tooltip(player_t const*) const { if (!strempty(default_param)) { if(default_param[0]=='+' || default_param[0]=='-') { sprintf(toolstr, "%s %s", translator::translate("1LIGHT_CHOOSE"), &default_param[0]); return toolstr; } else { return translator::translate("Toggle day/night view"); } } else { return ""; } } bool tool_daynight_level_t::init( player_t * ) { if(grund_t::underground_mode==grund_t::ugm_all || env_t::night_shift) { return false; } if (!strempty(default_param)) { if(default_param[0]=='+' && env_t::daynight_level > 0) { // '+': fade in one level env_t::daynight_level = env_t::daynight_level-1; } else if (default_param[0]=='-') { // '-': fade out one level env_t::daynight_level = env_t::daynight_level+1; } else { // number: toggle number/0. 4 or 5 is good for night const sint8 level = atoi(default_param); env_t::daynight_level = (env_t::daynight_level==0) ? level : 0; } } return false; } bool tool_rollup_all_win_t::init( player_t * ) { rollup_all_win(); return false; } /* make all tiles of this player a public stop * if this player is public, make all connected tiles a public stop */ bool tool_make_stop_public_t::init( player_t * ) { win_set_static_tooltip( NULL ); return true; } const char* tool_make_stop_public_t::get_tooltip(const player_t *) const { sprintf(toolstr, translator::translate("Make way or stop public (will join with neighbours), %i times maintainance"), welt->get_settings().cst_make_public_months); return toolstr; } const char *tool_make_stop_public_t::move( player_t *player, uint16, koord3d p ) { // queue tool for network if (env_t::networkmode) { nwc_tool_t *nwc = new nwc_tool_t(player, this, p, welt->get_steps(), welt->get_map_counter(), false); network_send_server(nwc); return NULL; } return work( player, p ); } const char *tool_make_stop_public_t::work( player_t *player, koord3d p ) { // target halt must exist halthandle_t halt = haltestelle_t::get_halt(p,player,false); if (!halt.is_bound() && player == welt->get_public_player()) { // public player can make halts of other players public grund_t *gr = welt->lookup(p); if (gr) { halt = gr->get_halt(); } } if( !halt.is_bound() ) { if(welt->get_settings().get_disable_make_way_public()) { return NOTICE_DISABLED_PUBLIC_WAY; } // not a halt to merge => just change way ownership if (grund_t* gr = welt->lookup(p)) { if (gr->get_typ() == grund_t::brueckenboden) { return NOTICE_UNSUITABLE_GROUND; } for (int j = 0; j < 2; j++) { if (weg_t* w = gr->get_weg_nr(j)) { // no public way with signs if (w->has_sign()) { return NOTICE_UNSUITABLE_GROUND; } } } bool has_been_announced = false; for (int j = 0; j < 2; j++) { if (weg_t* w = gr->get_weg_nr(j)) { // change ownership of way... player_t* wplayer = w->get_owner(); if (player == wplayer) { w->set_owner(welt->get_public_player()); w->set_flag(obj_t::dirty); sint32 cost = w->get_desc()->get_maintenance(); // of tunnel... if (tunnel_t* t = gr->find()) { t->set_owner(welt->get_public_player()); t->set_flag(obj_t::dirty); cost = t->get_desc()->get_maintenance(); } waytype_t const financetype = w->get_desc()->get_finance_waytype(); player_t::add_maintenance(wplayer, -cost, financetype); player_t::add_maintenance(welt->get_public_player(), cost, financetype); // multiplayer notification message if (env_t::networkmode && !has_been_announced) { cbuffer_t buf; buf.printf(translator::translate("(%s) now public way."), w->get_pos().get_str()); welt->get_message()->add_message(buf, w->get_pos(), message_t::ai, PLAYER_FLAG | player->get_player_nr(), IMG_EMPTY); has_been_announced = true; // one message is enough } cost = -welt->scale_with_month_length(cost * welt->get_settings().cst_make_public_months); player_t::book_construction_costs(wplayer, cost, koord::invalid, financetype); } } } // make way object public if any suitable for (uint8 i = 1; i < gr->obj_count(); i++) { if (wayobj_t* const wo = obj_cast(gr->obj_bei(i))) { player_t* woplayer = wo->get_owner(); if (player == woplayer) { sint32 const cost = wo->get_desc()->get_maintenance(); // change ownership wo->set_owner(welt->get_public_player()); wo->set_flag(obj_t::dirty); waytype_t const financetype = wo->get_desc()->get_waytype(); player_t::add_maintenance(woplayer, -cost, financetype); player_t::add_maintenance(welt->get_public_player(), cost, financetype); player_t::book_construction_costs(woplayer, cost, koord::invalid, financetype); } } } return 0; } // no ground found?!? return NOTICE_UNSUITABLE_GROUND; } // check funds sint64 workcost = -welt->scale_with_month_length(halt->calc_maintenance() * welt->get_settings().cst_make_public_months); // check waycost and some forbidden cases too for(haltestelle_t::tile_t const& i : halt->get_tiles()) { // make way public if any suitable for( int j=0; j<2; j++ ) { if( weg_t *w=i.grund->get_weg_nr(0) ) { if( player_t::check_owner( player, w->get_owner() ) && w->get_owner() ) { // no public ways? if( welt->get_settings().get_disable_make_way_public() ) { return NOTICE_DISABLED_PUBLIC_WAY; } // no public way with signs if( w->has_sign() ) { return NOTICE_UNSUITABLE_GROUND; } // compute maintainance cost sint32 cost = w->get_desc()->get_maintenance(); // tunnel cost overwrites way cost if( tunnel_t *t = i.grund->find() ) { cost = t->get_desc()->get_maintenance(); } workcost -= welt->scale_with_month_length(cost * welt->get_settings().cst_make_public_months); } } } } if( !player->can_afford(workcost) ) { return NOTICE_INSUFFICIENT_FUNDS; } // now find an adjacent stop vector_tplchecked_halts; checked_halts.append(halt); halthandle_t merge_to; for(haltestelle_t::tile_t const& i : halt->get_tiles()) { const koord pos = i.grund->get_pos().get_2d(); planquadrat_t *pl = welt->access( pos ); for( int i=0; iget_haltlist_count(); i++ ) { halthandle_t h = pl->get_haltlist()[i]; if( !checked_halts.is_contained(h) && h->get_owner()==welt->get_public_player() ) { koord testpos = h->get_ground_closest_to( pos )->get_pos().get_2d(); if( abs(pos.x-testpos.x) <= 1 && abs(pos.y-testpos.y) <= 1 ) { merge_to = h; break; } } } if( merge_to.is_bound() ) { break; } } // change ownership halt->change_owner( welt->get_public_player() ); halt->merge_halt( merge_to ); return NULL; } /* merge stop */ image_id tool_merge_stop_t::get_marker_image() const { return cursor; } uint8 tool_merge_stop_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d &) { grund_t *bd = welt->lookup(pos); if (bd==NULL) { error = ""; return 0; } // check halt ownership halthandle_t h = haltestelle_t::get_halt(pos,player,false); if( h.is_bound() ) { // allow to merge two public stops too // so no need to check for ownership // check for halt on the tile if( bd->is_halt() || (h->get_station_type()&haltestelle_t::dock && bd->is_water()) ) { return 2; } } // not a halt at all ... error = NOTICE_UNSUITABLE_GROUND; return 0; } void tool_merge_stop_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end ) { // only seens when dragging halt_be_merged_from = haltestelle_t::get_halt(start,player,false); halt_be_merged_to = haltestelle_t::get_halt(end,player,false); sint64 workcost = 0; uint32 distance = 0x7FFFFFFFu; if( halt_be_merged_from == halt_be_merged_to ) { // same halt => do nothing win_set_static_tooltip( NULL ); } // check ownership if (!halt_be_merged_from->get_owner()->is_public_service() || halt_be_merged_from->get_owner() != halt_be_merged_to->get_owner()) { win_set_static_tooltip( "Das Feld gehoert\neinem anderen Spieler\n" ); return; } for(haltestelle_t::tile_t const& i : halt_be_merged_from->get_tiles()) { for(haltestelle_t::tile_t const& j : halt_be_merged_to->get_tiles()) { uint32 dist = koord_distance( i.grund->get_pos(), j.grund->get_pos() ); if( dist < distance ) { distance = dist; if( distance <= 1 ) { break; } } } if( distance <= 1 ) { break; } } if( distance < welt->get_settings().allow_merge_distant_halt ) { distance = clamp(distance,2u,33u)-2; workcost = welt->scale_with_month_length( (1<get_settings().cst_multiply_merge_halt ); win_set_static_tooltip( tooltip_with_price("Building costs estimates", workcost) ); } else { win_set_static_tooltip( "Too far away to merge stations!" ); } } const char *tool_merge_stop_t::do_work( player_t *player, const koord3d &last_pos, const koord3d &pos) { halt_be_merged_from = haltestelle_t::get_halt(pos,player,false); halt_be_merged_to = haltestelle_t::get_halt(last_pos,player,false); sint64 workcost = 0; uint32 distance = 0x7FFFFFFFu; if( halt_be_merged_from == halt_be_merged_to ) { // same halt => do nothing return NULL; } // check ownership if(!player->is_public_service() && halt_be_merged_from->get_owner() != halt_be_merged_to->get_owner()) { return "Das Feld gehoert\neinem anderen Spieler\n"; } for(haltestelle_t::tile_t const& i : halt_be_merged_from->get_tiles()) { for(haltestelle_t::tile_t const& j : halt_be_merged_to->get_tiles()) { uint32 dist = koord_distance( i.grund->get_pos(), j.grund->get_pos() ); if( dist < distance ) { distance = dist; if( distance <= 1 ) { break; } } } if( distance <= 1 ) { break; } } if( distance < welt->get_settings().allow_merge_distant_halt ) { distance = clamp(distance,2u,33u)-2; workcost = welt->scale_with_month_length( (1<get_settings().cst_multiply_merge_halt ); win_set_static_tooltip( tooltip_with_price("Building costs estimates", workcost) ); if( player != welt->get_public_player() && !player->can_afford(workcost) ) { return NOTICE_INSUFFICIENT_FUNDS; } } else { return "Too far away to merge stations!"; } // and now just do it ... halt_be_merged_to->merge_halt(halt_be_merged_from); player_t::book_construction_costs( player, workcost, halt_be_merged_to->get_basis_pos(), ignore_wt ); // nothing to do return NULL; } image_id tool_remove_signal_t::get_icon(player_t*) const { // do not show if there are no matching ways on the map if (default_param && way_builder_t::waytype_available((waytype_t)atoi(default_param), welt->get_timeline_year_month())) { return icon; } return IMG_EMPTY; } const char* tool_remove_signal_t::work( player_t* player, koord3d pos ) { if( grund_t *gr=welt->lookup(pos) ) { if( signal_t *rs=gr->find() ) { const char *msg = rs->get_removal_error(player); if(msg) { return msg; } DBG_MESSAGE("tool_remove_signal_t()", "removing roadsign at (%s)", pos.get_str()); weg_t *weg = gr->get_weg(rs->get_desc()->get_wtyp()); if( weg==NULL && rs->get_desc()->get_wtyp()==tram_wt ) { weg = gr->get_weg(track_wt); } rs->cleanup(player); delete rs; // no need to update way if there is none // may happen when public player builds a signal on a company track, // the company goes bankrupt and the public player tries to remove the signal if (weg) { weg->count_sign(); } return NULL; } } // fail silent return ""; } // only public player can copy public objects const char* tool_pipette_t::allow_tool_check(const obj_t* obj, const obj_desc_timelined_t *desc, const player_t* pl) const { if (!pl->is_public_service()) { if (obj->get_owner() == NULL || obj->get_owner() != pl) { return "Not allowed to copy object."; } if (!desc->is_available(welt->get_timeline_year_month())) { return "Not allowed to copy object."; } } return NULL; } #define select_and_check(typ) \ if(typ *obj=gr->find()) {\ if (const char *err = allow_tool_check(obj, obj->get_desc(), pl)) {\ return err;\ }\ welt->set_tool(obj->get_desc()->get_builder(), pl);\ return NULL;\ } // create tool for object under the cursor const char* tool_pipette_t::work(player_t* pl, koord3d pos) { static cbuffer_t param_str; const grund_t* gr = welt->lookup(pos); if (!gr) { return NULL; } select_and_check(tunnel_t); select_and_check(bruecke_t); if (!gr->get_weg_nr(0) || !is_ctrl_pressed()) { if (gebaeude_t* gb = gr->find()) { if (const char* err = allow_tool_check(gb, gb->get_tile()->get_desc(), pl)) { return err; } const building_desc_t* desc = gb->get_tile()->get_desc(); if (!pl->is_public_service()) { // since we are not allowed to create public infrastructure as normal player, we must forbid this too if (desc->is_city_building() || desc->is_attraction() || desc->is_townhall() || desc->is_monument()) { return "Not allowed to copy object."; } } else { if (desc->is_depot() || desc->is_headquarters() || desc->is_townhall()) { return "Not allowed to copy object."; } } // here on factories, monuments, town halls, city buildings and more if (gb->get_fabrik()) { static tool_build_factory_t t = tool_build_factory_t(); param_str.clear(); param_str.printf("%i%i%i,%s", 1, // with climate gb->get_tile()->get_layout(), /*rotation*/ gb->get_fabrik()->get_base_production(), gb->get_fabrik()->get_desc()->get_name()); t.set_default_param(param_str); t.cursor = tool_t::general_tool[TOOL_BUILD_FACTORY]->cursor; welt->set_tool(&t, pl); return NULL; } else if (desc->is_attraction() || desc->is_city_building()) { static tool_build_house_t t = tool_build_house_t(); t.cursor = tool_t::general_tool[TOOL_BUILD_HOUSE]->cursor; param_str.clear(); param_str.printf("0%d%s", gb->get_tile()->get_layout(), /*rotation*/ gb->get_tile()->get_desc()->get_name()); t.set_default_param(param_str); welt->set_tool(&t, pl); return NULL; } else if (tool_t* t = gb->get_tile()->get_desc()->get_builder()) { welt->set_tool(t, pl); return NULL; } return "Not allowed to copy object."; } } if (!is_ctrl_pressed()) { // we do not check depot if ctrl pressed(ctrl -> get way info directly) if (depot_t* depot = gr->get_depot()) { if (pl->is_public_service()) { return "Not allowed to copy object."; } tool_t* t = depot->get_tile()->get_desc()->get_builder(); welt->set_tool(t, pl); return NULL; } } if (gr->get_weg_nr(1) && is_shift_pressed()) { // we check weg_nr(1) only when shift pressed if (!is_ctrl_pressed()) { // signals > wayobjs > ways // if ctrl pressed, we only see way. select_and_check(signal_t); select_and_check(roadsign_t); if (gr->get_wayobj(gr->get_weg_nr(1)->get_waytype())) { if (tool_t* wayobj_builder = gr->get_wayobj(gr->get_weg_nr(1)->get_waytype())->get_desc()->get_builder()) { if (const char* err = allow_tool_check(gr->get_wayobj(gr->get_weg_nr(1)->get_waytype()), gr->get_wayobj(gr->get_weg_nr(1)->get_waytype())->get_desc(), pl)) { return err; } welt->set_tool(wayobj_builder, pl); return NULL; } } } if (tool_t* way_builder = gr->get_weg_nr(1)->get_desc()->get_builder()) { if (const char* err = allow_tool_check(gr->get_weg_nr(1), gr->get_weg_nr(1)->get_desc(), pl)) { return err; } welt->set_tool(way_builder, pl); return NULL; } return "Not allowed to copy object."; // here on rivers and city roads } if (gr->get_weg_nr(0)) { if (!is_ctrl_pressed()) { // signals > wayobjs > ways // if ctrl pressed, we only see way. select_and_check(signal_t); select_and_check(roadsign_t); select_and_check(wayobj_t); } if (tool_t* way_builder = gr->get_weg_nr(0)->get_desc()->get_builder()) { if (const char* err = allow_tool_check(gr->get_weg_nr(0), gr->get_weg_nr(0)->get_desc(), pl)) { return err; } welt->set_tool(way_builder, pl); return NULL; } return "Not allowed to copy object."; // here on rivers and city roads } select_and_check(leitung_t); if (gr->find() || gr->find()) { // missing: check for ownership welt->set_tool(tool_t::general_tool[TOOL_TRANSFORMER], pl); return NULL; } if (baum_t* b = gr->find()) { static tool_plant_tree_t t; param_str.clear(); param_str.printf("%i%i,%s", 1, 0, b->get_desc()->get_name()); t.set_default_param(param_str); t.cursor = tool_t::general_tool[TOOL_PLANT_TREE]->cursor; welt->set_tool(&t, pl); return NULL; } if (groundobj_t* b = gr->find()) { if (pl->is_public_service()) { static tool_plant_groundobj_t t; param_str.clear(); param_str.printf("%i%i,%s", 1, 0, b->get_desc()->get_name()); t.set_default_param(param_str); t.cursor = tool_t::general_tool[TOOL_PLANT_GROUNDOBJ]->cursor; welt->set_tool(&t, pl); return NULL; } } return gr->obj_count() > 0 ? "Not allowed to copy object." : NULL; } // pause tool also to unpause server games char const* tool_pause_t::get_tooltip(player_t const*) const { if (env_t::networkmode) { if (env_t::server) { return translator::translate("Pause"); } return translator::translate("deactivated in online mode"); } return translator::translate("Pause"); } bool tool_pause_t::init(player_t*) { if (!env_t::networkmode) { welt->set_fast_forward(0); welt->set_pause(welt->is_paused() ^ 1); } else { env_t::pause_server_no_clients = !env_t::pause_server_no_clients; if (socket_list_t::get_playing_clients() == 0) { welt->set_pause(env_t::pause_server_no_clients); } } return false; } bool tool_show_trees_t::init( player_t* ) { env_t::hide_trees = !env_t::hide_trees; baum_t::recalc_outline_color(); welt->set_dirty(); return false; } sint8 tool_show_underground_t::save_underground_level = -128; bool tool_show_underground_t::init( player_t * ) { koord3d zpos = welt->get_zeiger()->get_pos(); // move zeiger (pointer) to invalid position -> unmark tiles welt->get_zeiger()->change_pos( koord3d::invalid); sint8 old_underground_level = grund_t::underground_level; // map needs update? bool ok = true; // need an extra click? bool needs_click = false; // default default-param = U for backward compatibility if (default_param == NULL) { default_param = strdup("U"); } // now check the default parameter switch(default_param[0]) { // toggle sliced view by toolbar - height taken from extra mouse click case 'C': if(grund_t::underground_mode==grund_t::ugm_level) { grund_t::set_underground_mode( grund_t::ugm_none, 0); } else if(grund_t::underground_mode==grund_t::ugm_none) { needs_click = true; ok = false; } else { ok = false; } break; // decrease slice level case 'D': if(grund_t::underground_mode==grund_t::ugm_level) { if( grund_t::underground_level > welt->min_height ) { grund_t::underground_level --; } } else { ok = false; } break; // increase slice level case 'I': if(grund_t::underground_mode==grund_t::ugm_level) { if( grund_t::underground_level < welt->max_height ) { grund_t::underground_level ++; } } else { ok = false; } break; // toggle sliced view by keyboard - height taken from cursor case 'K': if(grund_t::underground_mode==grund_t::ugm_level) { // switch to normal or full-underground grund_t::set_underground_mode( grund_t::ugm_none, 0); } else if(grund_t::underground_mode==grund_t::ugm_none) { grund_t::set_underground_mode( grund_t::ugm_level, zpos.z); } else { ok = false; } break; // switch between full underground or normal/sliced view case 'U': if (grund_t::underground_mode==grund_t::ugm_all) { // check if the old level is valid then switch back to sliced view if (-128error( "tool_show_underground_t::init()", "Unknown command string \"%s\"", default_param ); } // move zeiger (pointer) back welt->get_zeiger()->change_pos( zpos); if (ok) { save_underground_level = old_underground_level; // renew toolbar tool_t::update_toolbars(); // recalc all images on map welt->update_underground(); } return needs_click; } bool tool_show_underground_t::exit( player_t* ) { if( grund_t::underground_mode != grund_t::ugm_none ) { // reset no normal view on deselect grund_t::set_underground_mode( grund_t::ugm_none, 0 ); // renew toolbar tool_t::update_toolbars(); // recalc all images on map welt->update_underground(); } return false; } const char *tool_show_underground_t::work( player_t *player, koord3d pos) { koord3d zpos = welt->get_zeiger()->get_pos(); // move zeiger (pointer) to invalid position -> unmark tiles welt->get_zeiger()->change_pos( koord3d::invalid); save_underground_level = grund_t::underground_level; grund_t::set_underground_mode( grund_t::ugm_level, pos.z); // move zeiger (pointer) back welt->get_zeiger()->change_pos( zpos); // renew toolbar tool_t::update_toolbars(); // recalc all images on map welt->update_underground(); if(player == welt->get_active_player()) { welt->set_tool( general_tool[TOOL_QUERY], player ); } return NULL; } char const* tool_show_underground_t::get_tooltip(player_t const*) const { // no default-param == U for backward compatibility if( default_param == NULL ) { return translator::translate("underground mode"); } // now check the default parameter switch(default_param[0]) { // toggle sliced view by toolbar - height taken from extra mouse click case 'C': return translator::translate("sliced underground mode"); // decrease slice level case 'D': return translator::translate("decrease underground view level"); // increase slice level case 'I': return translator::translate("increase underground view level"); // toggle sliced view by keyboard - height taken from cursor case 'K': return translator::translate("sliced underground mode"); // switch between full underground or normal/sliced view case 'U': default: return translator::translate("underground mode"); } } bool tool_show_underground_t::is_selected() const { // default default-param = U for backward compatibility if( default_param == NULL ) { return grund_t::underground_mode==grund_t::ugm_all; } // now check the default parameter switch(default_param[0]) { // toggle sliced view by toolbar - height taken from extra mouse click case 'C': return grund_t::underground_mode==grund_t::ugm_level; // decrease slice level case 'D': return false; // increase slice level case 'I': return false; // toggle sliced view by keyboard - height taken from cursor case 'K': return grund_t::underground_mode==grund_t::ugm_level; // switch between full underground or normal/sliced view case 'U': return grund_t::underground_mode==grund_t::ugm_all; } return false; } void tool_show_underground_t::draw_after(scr_coord k, bool dirty) const { if( icon!=IMG_EMPTY && is_selected() ) { gfx->draw_img_blend( icon, k.x, k.y, TRANSPARENT50_FLAG|OUTLINE_FLAG|gfx->palette_lookup(COL_BLACK), false, dirty ); // additionally show level in sliced mode if( default_param!=NULL && grund_t::underground_mode==grund_t::ugm_level ) { char level_str[16]; sprintf( level_str, "%i", grund_t::underground_level ); gfx->draw_text( k.x+4, k.y+4, level_str, ALIGN_LEFT, gfx->palette_lookup(COL_YELLOW), true ); } } } void tool_rotate90_t::draw_after(scr_coord pos, bool dirty) const { if( !env_t::networkmode ) { if( skinverwaltung_t::compass_map ) { gfx->draw_img_aligned( skinverwaltung_t::compass_map->get_image_id( welt->get_settings().get_rotation()+4 ), scr_rect(pos,env_t::iconsize), ALIGN_CENTER_V|ALIGN_CENTER_H, false ); } tool_t::draw_after( pos, dirty ); } } bool tool_rotate90_t::init( player_t * ) { if( !env_t::networkmode ) { welt->rotate90(); welt->update_map(); } return false; } bool tool_quit_t::init( player_t * ) { if (!strempty(default_param)) { // new world destroy_all_win(true); welt->stop(false); } else { // totally quit welt->stop(true); } if (env_t::networkmode) { welt->network_disconnect(); } return false; } bool tool_toggle_reservation_t::is_selected() const { if (tool_t* t = welt->get_tool(welt->get_active_player_nr())) { if (t->get_id() != (TOOL_CLEAR_RESERVATION | GENERAL_TOOL)) { return schiene_t::show_reservations; } } return false; } bool tool_screenshot_t::init( player_t * ) { bool ok; const scr_rect screen_area = { { 0, 0 }, gfx->get_screen_size() }; const gui_frame_t *topwin = win_get_top(); if( is_ctrl_pressed() && topwin != NULL ) { ok = gfx->take_screenshot(scr_rect(win_get_pos(topwin), topwin->get_windowsize())); } else { ok = gfx->take_screenshot(screen_area); } if (ok) { create_win( new news_img("Screenshot\ngespeichert.\n"), w_time_delete, magic_none); } else { create_win( new news_img("Could not\ncreate screenshot!\n"), w_time_delete, magic_none); } return false; } bool tool_undo_t::init( player_t *player ) { if(!player->undo() && can_use_gui()) { create_win( new news_img("UNDO failed!"), w_time_delete, magic_none); } return false; } bool tool_increase_industry_t::init( player_t * ) { factory_builder_t::increase_industry_density( false ); return false; } bool tool_zoom_in_t::init( player_t * ) { win_change_zoom_factor(true); welt->set_dirty(); return false; } bool tool_zoom_out_t::init( player_t * ) { win_change_zoom_factor(false); welt->set_dirty(); return false; } /************************* internal tools, only need for networking ***************/ static bool scenario_check_schedule(karte_t *welt, player_t *player, schedule_t *schedule, bool local) { if (!is_scenario()) { return true; } const char* err = welt->get_scenario()->is_schedule_allowed(player, schedule); if (err) { if (*err && local) { open_error_msg_win(err); } return false; } return true; } bool scenario_check_convoy(karte_t *welt, player_t *player, convoihandle_t cnv, depot_t* depot, bool local) { if (!is_scenario()) { return true; } const char* err = welt->get_scenario()->is_convoy_allowed(player, cnv, depot); if (err) { if (*err && local) { open_error_msg_win(err); } return false; } return true; } /* Handles all action of convois in depots. Needs a default param: * [function],[convoi_id],addition stuff * following simple command exists: * 'x' : self destruct * 'f' : open the schedule window * 'g' : apply a schedule * 'n' : toggle 'no load' * 'w' : toggle withdraw * 's' : change state to [number] (and maybe set open schedule flag) * 'l' : apply new line [number] * 'd' : go to nearest depot */ bool tool_change_convoi_t::init( player_t *player ) { char tool=0; uint16 convoi_id = 0; // skip the rest of the command const char *p = default_param; while( *p && *p<=' ' ) { p++; } sscanf( p, "%c,%hi", &tool, &convoi_id ); // skip to the commands ... for( int z = 2; *p && z>0; p++ ) { if( *p==',' ) { z--; } } convoihandle_t cnv; cnv.set_id( convoi_id ); // double click on remove button will send two such commands // the first will delete the convoi, the second should not trigger an assertion // catch such commands here if( !cnv.is_bound()) { #if MSG_LEVEL>=4 if (can_use_gui()) { create_win( new news_img("Convoy already deleted!"), w_time_delete, magic_none); } #endif dbg->warning("tool_change_convoi_t::init", "no convoy with id=%d found", convoi_id); return false; } // ownership check for network games if (env_t::networkmode && !player_t::check_owner(cnv->get_owner(), player)) { return false; } // first letter is now the actual command switch( tool ) { case 'x': // self destruction ... if (cnv->get_state()==convoi_t::INITIAL) { // delete cnv in depot if (grund_t *gr = welt->lookup(cnv->get_pos())) { if (depot_t *dep = gr->get_depot()) { dep->disassemble_convoi(cnv, true); return false; } } } cnv->self_destruct(); return false; case 'f': // open schedule { // we open the window only when executed on the same client that triggered the tool // but the all clients must call the function anyway cnv->open_schedule_window( can_use_gui() ); } break; case 'g': // change schedule { schedule_t *schedule = cnv->create_schedule()->copy(); schedule->finish_editing(); if (schedule->sscanf_schedule( p ) && (no_check() || scenario_check_schedule(welt, player, schedule, can_use_gui())) ) { cnv->set_schedule( schedule ); } else { // could not read schedule, do not assign delete schedule; // but clear editing flag cnv->get_schedule()->finish_editing(); cnv->set_schedule( cnv->get_schedule() ); } } break; case 'l': // change line { // read out id and new current_stop index uint16 id=0, current_stop=0; int count=sscanf( p, "%hi,%hi", &id, ¤t_stop ); linehandle_t l; l.set_id( id ); if( l.is_bound() ) { // sanity check for right line-type (compare schedule types ..) schedule_t *schedule = cnv->create_schedule(); if( schedule && l->get_schedule() && schedule->get_type()!=l->get_schedule()->get_type() ) { dbg->warning("tool_change_convoi_t::init", "types of convoi and line do not match"); return false; } if( count==1 ) { // current_stop was not supplied -> take it from line schedule current_stop = l->get_schedule()->get_current_stop(); } cnv->set_line( l ); cnv->get_schedule()->set_current_stop((uint8)current_stop); cnv->get_schedule()->finish_editing(); } } break; case 'n': // change no_load cnv->set_no_load( !cnv->get_no_load() ); if( !cnv->get_no_load() ) { cnv->set_withdraw( false ); } break; case 's': // change state { int new_state = atoi(p); if( new_state>0 ) { cnv->set_state( new_state ); if( new_state==convoi_t::EDIT_SCHEDULE ) { cnv->get_schedule()->start_editing(); } } } break; case 'w': // change withdraw cnv->set_withdraw( !cnv->get_withdraw() ); cnv->set_no_load( cnv->get_withdraw() ); break; case 'd': // goto depot { const char* msg = cnv->send_to_depot(is_local_execution()); if (is_local_execution()) { create_win( new news_img(msg), w_time_delete, magic_none); } } } if( cnv->in_depot() && (tool=='g' || tool=='l') ) { const grund_t *const ground = welt->lookup( cnv->get_home_depot() ); if( ground ) { const depot_t *const depot = ground->get_depot(); if( depot ) { depot_frame_t *const frame = dynamic_cast( win_get_magic( (ptrdiff_t)depot ) ); if( frame ) { frame->update_data(); } } } } return false; // no related work tool ... } /* Handles all action of lines. Needs a default param: * [function],[line_id],addition stuff * following simple command exists: * 'g' : apply new schedule to line [schedule follows] */ bool tool_change_line_t::init( player_t *player ) { uint16 line_id = 0; // skip the rest of the command const char *p = default_param; while( *p && *p<=' ' ) { p++; } char tool=*p++; while( *p && *p++!=',' ) { } if( *p==0 ) { dbg->warning( "tool_change_line_t::init()", "too short command \"%s\"", default_param ); return false; } line_id = atoi(p); while( *p && *p++!=',' ) { } linehandle_t line; line.set_id( line_id ); // ownership check for network games if (line.is_bound() && env_t::networkmode && !player_t::check_owner(line->get_owner(), player)) { return false; } // first letter is now the actual command switch( tool ) { case 'c': // create line, next parameter line type and magic of schedule window (only right window gets updated) { int ltype = atoi(p); if(ltype < simline_t::truckline || ltype > simline_t::narrowgaugeline) { // invalid line type break; } line = player->simlinemgmt.create_line( ltype, player ); while( *p && *p++!=',' ) { } long t; sscanf( p, "%ld", &t ); while( *p && *p++!=',' ) { } line->get_schedule()->sscanf_schedule( p ); // check scenario conditions if (!no_check() && !scenario_check_schedule(welt, player, line->get_schedule(), can_use_gui())) { player->simlinemgmt.delete_line(line); break; } line->get_schedule()->finish_editing(); // just in case ... if( can_use_gui() ) { player->simlinemgmt.show_lineinfo( player, line, 0 ); } } break; case 'd': // delete line { if (line.is_bound()) { // close a schedule window, if still active gui_frame_t *w = win_get_magic( (ptrdiff_t)line.get_rep() ); if(w) { destroy_win( w ); } player->simlinemgmt.delete_line(line); } } break; case 'g': // change schedule { if (line.is_bound()) { schedule_t *schedule = line->get_schedule()->copy(); if (schedule->sscanf_schedule( p ) && (no_check() || scenario_check_schedule(welt, player, schedule, can_use_gui())) ) { schedule->finish_editing(); line->set_schedule( schedule ); line->get_owner()->simlinemgmt.update_line(line); } else { // could not read schedule, do not assign delete schedule; } } } break; case 't': // trims away convois on all lines of linetype with this default parameter { vector_tpl const& lines = player->simlinemgmt.get_line_list(); // what kind of lines to trim const simline_t::linetype linetype = (simline_t::linetype)atoi(p); while( *p && *p++!=',' ) { } // how much as target sint32 percentage = *p ? atoi(p) : 10; if( percentage == 0 ) { break; } for(linehandle_t line : lines) { if( line->get_linetype() == linetype && line->get_convoys().get_count() > 2 ) { // correct waytpe and more than one,n now some up usage for the last six months sint64 transported = 0, capacity = 0; for( int i=0; i<6; i++ ) { capacity += line->get_finance_history( i , LINE_CAPACITY ); transported += line->get_finance_history( i , LINE_TRANSPORTED_GOODS ); } // sanity check for non-moving lines if( capacity == 0 ) { continue; } if( (transported*100) / capacity < percentage ) { // less than 33 % usage => remove concois vector_tpl const& cnvs = line->get_convoys(); sint64 old_sum_capacity = 0; for(convoihandle_t cnv : cnvs) { for( int i=0; iget_vehicle_count(); i++ ) { old_sum_capacity += cnv->get_vehicle(i)->get_desc()->get_capacity(); } } /* now we have the total capacity. We will now remove convois until this capacity * is reduced by the ration suggested from transported = 1/3 of total capacity * x is the percentage of used capacity * Then is the new target sum_capacity = x*old_sum_capacity */ sint64 new_sum_capacity = (transported * 1000 * old_sum_capacity) / (capacity * percentage * 10); // first we remove the totally empty convois (nowbody will miss them) int destroyed = 0; const int initial = line->get_convoys().get_count(); const int max_left = (initial+2) / 2; for( int j=initial-1; j >= 0 && initial-destroyed > max_left && new_sum_capacity < old_sum_capacity; j-- ) { convoihandle_t cnv = line->get_convoy(j); if( cnv->get_state() == convoi_t::INITIAL || cnv->get_state() >= convoi_t::WAITING_FOR_CLEARANCE_ONE_MONTH ) { for( int i=0; iget_vehicle_count(); i++ ) { old_sum_capacity -= cnv->get_vehicle(i)->get_desc()->get_capacity(); } cnv->self_destruct(); destroyed ++; } } // not enough? Then remove from the end ... for( uint j=0; j < line->get_convoys().get_count() && initial-destroyed > max_left && new_sum_capacity < old_sum_capacity; j++ ) { convoihandle_t cnv = line->get_convoy(j); if( cnv->get_state() != convoi_t::SELF_DESTRUCT ) { for( int i=0; iget_vehicle_count(); i++ ) { old_sum_capacity -= cnv->get_vehicle(i)->get_desc()->get_capacity(); } cnv->self_destruct(); destroyed ++; } } // done if( destroyed ) { dbg->message( "tool_change_line_t::init", "trim line %s: Reduced from %d to %d convois", line->get_name(), initial, initial-destroyed ); } } } } } break; case 'u': // unite all lineless convois with similar schedules { array_tpl > cnvs(welt->convoys().get_count()); uint32 max_cnvs=0; for(convoihandle_t cnv : welt->convoys()) { // only check lineless convoys if( !cnv->get_line().is_bound() ) { bool found = false; // check, if already matches existing convois schedule for( uint32 i=0; iget_schedule()->matches( welt, cnv->get_schedule() ) ) { found = true; cnvs[i].append( cnv ); break; } } } // not added: then may be new line for this? if( !found ) { cnvs[max_cnvs++].append( cnv ); } } } // now we have an array of one or more lineless convois for( uint32 i=0; i new line if( cnvs[i].get_count()>1 ) { line = player->simlinemgmt.create_line( cnvs[i][0]->get_schedule()->get_type(), player, cnvs[i][0]->get_schedule() ); for(convoihandle_t cnv : cnvs[i] ) { line->add_convoy( cnv ); cnv->set_line( line ); } } } } break; case 'w': // change widthdraw { if (line.is_bound()) { line->set_withdraw( atoi(p) ); } } break; } return false; } /* Handles all action of convois in depots. Needs a default param: * [function],[depot_pos_3d],[convoi_id],addition stuff * following simple command exists: * 'l' : creates a new line (convoi_id might be invalid) (+printf'd initial schedule) * 'b' : starts the convoi * 'B' : starts all convoys * 'c' : copies this convoi * 'd' : disassembles convoi * 's' : sells convoi * 'a' : appends a vehicle (+vehikel_name) uses the oldest * 'i' : inserts a vehicle in front (+vehikel_name) uses the oldest * 's' : sells a vehicle (+vehikel_name) uses the newest * 'r' : removes a vehicle (+number in convoi) * 'R' : removes all vehicles including (+number in convoi) to end */ bool tool_change_depot_t::init( player_t *player ) { char tool=0; koord pos2d; sint8 z; uint16 convoi_id; // skip the rest of the command const char *p = default_param; while( *p && *p<=' ' ) { p++; } sscanf( p, "%c,%hi,%hi,%hhi,%hi", &tool, &pos2d.x, &pos2d.y, &z, &convoi_id ); koord3d pos(pos2d, z); // skip to the commands ... z = 5; while( *p && z>0 ) { if( *p==',' ) { z--; } p++; } grund_t *gr = welt->lookup(pos); depot_t *depot = gr ? gr->get_depot() : NULL; if( depot==NULL ){ dbg->warning("tool_change_depot_t::init", "no depot found at (%s)", pos.get_str()); return false; } if( !player_t::check_owner( depot->get_owner(), player) ) { dbg->warning("tool_change_depot_t::init", "depot at (%s) belongs to another player", pos.get_str()); return false; } convoihandle_t cnv; cnv.set_id( convoi_id ); // ok now do our stuff switch( tool ) { case 'l': { // create line schedule window linehandle_t selected_line = depot->get_owner()->simlinemgmt.create_line(depot->get_line_type(),depot->get_owner()); // no need to check schedule for scenario conditions, as schedule is only copied selected_line->get_schedule()->sscanf_schedule( p ); depot_frame_t *depot_frame = dynamic_cast(win_get_magic( (ptrdiff_t)depot )); if( can_use_gui() ) { player->simlinemgmt.show_lineinfo( player, selected_line, 0 ); } if( depot_frame ) { if( can_use_gui() ) { depot_frame->set_selected_line( selected_line ); depot_frame->apply_line(); } depot_frame->update_data(); } DBG_MESSAGE("depot_frame_t::new_line()","id=%d",selected_line.get_id() ); break; } case 'b': { // start a convoi from the depot if( cnv.is_bound() ) { depot->start_convoi(cnv, can_use_gui()); } break; } case 'B': { // start all convoys depot->start_all_convoys(); break; } case 'd': // disassemble convoi case 'v': { // sell convoi depot->disassemble_convoi( cnv, tool=='v' ); break; } case 'V': { // sell all convoys depot->disassemble_all_convois( tool=='V' ); break; } case 'c': { // copy this convoi if( cnv.is_bound() ) { if( convoihandle_t::is_exhausted() ) { if( can_use_gui() ) { create_win( new news_img("Convoi handles exhausted!"), w_time_delete, magic_none ); } return false; } depot->copy_convoi( cnv, can_use_gui() ); } break; } case 'a': // append a vehicle case 'i': // insert a vehicle in front case 's': // sells a vehicle case 'r': // removes a vehicle (assumes a valid depot) case 'R': { // removes all vehicles to end (assumes a valid depot) if( tool=='r' || tool=='R' ) { // test may fail after double-click on the button: // two remove cmds are sent, only the first will remove, the second should not trigger assertion failure if ( cnv.is_bound() ) { int start_nr = atoi(p); int nr = start_nr; // find end while(nrget_vehicle_count()) { const vehicle_desc_t *info = cnv->get_vehicle(nr)->get_desc(); nr ++; if(info->get_trailer_count()!=1) { break; } } // now remove the vehicles if( cnv->get_vehicle_count()==nr-start_nr || (tool=='R' && start_nr==0) ) { depot->disassemble_convoi(cnv, false); } else if( tool=='R' ) { depot->remove_vehicles_to_end( cnv, start_nr ); } else { for( int i=start_nr; iremove_vehicle(cnv, start_nr); } } } } else { // create and append it const vehicle_desc_t *info = vehicle_builder_t::get_info( p ); // we have a valid vehicle there => now check for details if( info ) { // we buy/sell all vehicles together! slist_tplnew_vehicle_info; const vehicle_desc_t *start_info = info; if(tool!='a') { // start of composition while( info->get_leader_count() == 1 && info->get_leader(0) != NULL && info->get_leader(0) != vehicle_desc_t::any_vehicle && !new_vehicle_info.is_contained(info->get_leader(0))) { info = info->get_leader(0); new_vehicle_info.insert(info); } info = start_info; } new_vehicle_info.append( info ); if (tool != 'i') { while(info->get_trailer_count() == 1 && info->get_trailer(0) != NULL && info->get_trailer(0) != vehicle_desc_t::any_vehicle && !new_vehicle_info.is_contained(info->get_trailer(0))) { info = info->get_trailer(0); new_vehicle_info.append(info); } } // now we have a valid composition together if( tool=='s' ) { while( new_vehicle_info.get_count() ) { // We sell the newest vehicle - gives most money back. vehicle_t* veh = depot->find_oldest_newest(new_vehicle_info.remove_first(), false); if(veh != NULL) { depot->sell_vehicle(veh); } } } else { // now check if we are allowed to buy this (we test only leading vehicle, so one can still buy hidden stuff) info = new_vehicle_info.front(); if (!info->is_available(welt->get_timeline_year_month())) { if (info->is_future(welt->get_timeline_year_month())) { // not allowed return false; } else if (!welt->get_settings().get_allow_buying_obsolete_vehicles()) { // only allow append/insert, if in depot do not create new obsolete vehicles if( !depot->find_oldest_newest(info, true) ) { // just fail silent return false; } } // obsolete vehicle found or buying of obsolete allowed } // append/insert into convoi; create one if needed depot->clear_command_pending(); if( !cnv.is_bound() ) { if( convoihandle_t::is_exhausted() ) { if( can_use_gui() ) { create_win( new news_img("Convoi handles exhausted!"), w_time_delete, magic_none); } return false; } // create a new convoi cnv = depot->add_convoi( can_use_gui() ); cnv->set_name( new_vehicle_info.front()->get_name() ); } // now we have a valid cnv if( cnv->get_vehicle_count()+new_vehicle_info.get_count() <= depot->get_max_convoi_length() ) { for( unsigned i=0; ifind_oldest_newest(vb, true); if( veh == NULL ) { // nothing there => we buy it veh = depot->buy_vehicle(vb); } depot->append_vehicle( cnv, veh, tool=='i', can_use_gui() ); } } } } } depot->update_win(); break; } } return false; } /* Handles all player stuff default_param: * [function],[player_id],[state] * following command exists: * 'a' : activate/deactivate player (depends on state) * 'c' : change player color * '$' : change bank account */ bool tool_change_player_t::init( player_t *player_in) { if( default_param==NULL ) { dbg->warning( "tool_change_player_t::init()", "nothing to do!" ); return false; } // skip the rest of the command const char *p = default_param; while( *p && *p<=' ' ) { p++; } char tool = 0; int id = 0; if (sscanf( p, "%c,%i", &tool, &id ) < 2) { return false; // invalid command } player_t *player = welt->get_player(id); // ok now do our stuff switch( tool ) { case 'c': // change player color // unused if( player && player==player_in ) { int c1, c2, dummy; sscanf( p, "%c,%i,%i,%i", &tool, &dummy, &c1, &c2 ); player->set_player_color( c1, c2 ); } break; case '$': // change bank account if( player && player_in==welt->get_public_player() ) { int delta; if (sscanf(p, "%c,%i,%i", &tool, &id, &delta) == 3) { player->get_finance()->book_account(delta); } } break; case 'a': // WAS: activate/deactivate AI case 'n': // WAS: new player with type state case 'f': // WAS: activate/deactivate freeplay dbg->error( "tool_change_player_t::init()", "deprecated command called" ); break; } // update the window ki_kontroll_t* playerwin = (ki_kontroll_t*)win_get_magic(magic_ki_kontroll_t); if (playerwin) { playerwin->update_data(); } return false; } /* Sets traffic light phases via default_param: * [pos],[ns_flag],[ticks] */ bool tool_change_traffic_light_t::init( player_t *player ) { koord pos2d; sint8 z; uint16 ns, ticks; if( 5!=sscanf( default_param, "%hi,%hi,%hhi,%hu,%hu", &pos2d.x, &pos2d.y, &z, &ns, &ticks ) ) { return false; } koord3d pos(pos2d, z); if( grund_t *gr = welt->lookup(pos) ) { if( roadsign_t *rs = gr->find() ) { if( ( rs->get_desc()->is_traffic_light() || rs->get_desc()->is_private_way() ) && player_t::check_owner(rs->get_owner(),player) ) { if( ns == 1 ) { rs->set_ticks_ns( (uint8)ticks ); } else if( ns == 0 ) { rs->set_ticks_ow( (uint8)ticks ); } else if( ns == 2 ) { rs->set_ticks_offset( (uint8)ticks ); } else if( ns == 4 ) { rs->set_ticks_yellow_ns( (uint8)ticks ); } else if( ns == 3 ) { rs->set_ticks_yellow_ow( (uint8)ticks ); } // update the window if( rs->get_desc()->is_traffic_light() ) { trafficlight_info_t* trafficlight_win = (trafficlight_info_t*)win_get_magic((ptrdiff_t)rs); if (trafficlight_win) { trafficlight_win->update_data(); } } else { privatesign_info_t* trafficlight_win = (privatesign_info_t*)win_get_magic((ptrdiff_t)rs); if (trafficlight_win) { trafficlight_win->update_data(); } } } } else if ( signal_t *sig = gr->find() ) { if (player_t::check_owner(sig->get_owner(), player)) { if (ns == 1) { sig->set_two_ways(ticks); } } } } return false; } /** * change city: * g[x],[y],[allow_city_growth] */ bool tool_change_city_t::init( player_t *player ) { if (player != welt->get_public_player()) { return false; } koord k; sint16 allow_growth; if( 3!=sscanf( default_param, "g%hi,%hi,%hi", &k.x, &k.y, &allow_growth ) ) { return false; } grund_t *gr = welt->lookup_kartenboden(k); if (gr) { gebaeude_t *gb = gr->find(); if (gb) { stadt_t *st = gb->get_stadt(); if (st) { st->set_citygrowth_yesno(allow_growth); city_info_t *stinfo = dynamic_cast(win_get_magic((ptrdiff_t)st)); if (stinfo) { stinfo->update_data(); } } } } return false; } /* Handles renaming of ingame entities. Needs a default param: * [object='c|h|l|m|t|p|f'][id|pos],[name] * c=convoi, h=halt, l=line, m=marker, t=town, p=player, f=factory * in case of marker / factory, id is a pos3d string */ bool tool_rename_t::init(player_t *player) { uint16 id = 0; koord3d pos = koord3d::invalid; // skip the rest of the command const char *p = default_param; const char what = *p++; switch( what ) { case 'h': case 'l': case 'c': case 't': case 'p': id = atoi(p); while( *p>0 && *p++!=',' ) { } break; case 'm': case 'f': { koord pos2d; sint8 z; if( 3!=sscanf( p, "%hi,%hi,%hhi", &pos2d.x, &pos2d.y, &z ) ) { dbg->error( "tool_rename_t::init", "no position given for marker/factory! (%s)", default_param ); return false; } while( *p>0 && *p++!=',' ) { } while( *p>0 && *p++!=',' ) { } while( *p>0 && *p++!=',' ) { } pos = koord3d(pos2d, z); id = 0; break; } default: dbg->error( "tool_rename_t::init", "illegal request! (%s)", default_param ); return false; } // now for action ... switch( what ) { case 'h': { halthandle_t halt; halt.set_id( id ); if( halt.is_bound() && (!env_t::networkmode || player_t::check_owner(halt->get_owner(), player)) ) { halt->set_name( p ); return false; } break; } case 'l': { linehandle_t line; line.set_id( id ); if( line.is_bound() && (!env_t::networkmode || player_t::check_owner(line->get_owner(), player)) ) { line->set_name( p ); return false; } break; } case 'c': { convoihandle_t cnv; cnv.set_id( id ); if( cnv.is_bound() && (!env_t::networkmode || player_t::check_owner(cnv->get_owner(), player)) ) { // set name without ID cnv->set_name( p, false ); return false; } break; } case 't': { if( player == welt->get_public_player() && idget_cities().get_count() ) { welt->get_cities()[id]->set_name( p ); return false; } break; } case 'm': if( grund_t *gr = welt->lookup(pos) ) { label_t *label = gr->find(); if (label && (!env_t::networkmode || player_t::check_owner(label->get_owner(), player)) ) { gr->set_text(p); } return false; } break; case 'p': { player_t *other = welt->get_player((uint8)id); if( other && other == player ) { other->set_name(p); return false; } break; } case 'f': { if( player == welt->get_public_player()) { if( grund_t *gr = welt->lookup(pos) ) { if( gebaeude_t* gb = gr->find() ) { if ( fabrik_t *fab = gb->get_fabrik() ) { fab->set_name(p); return false; } } } } } } // we are only getting here, if we could not process this request dbg->warning( "tool_rename_t::init", "could not perform (%s)", default_param ); return false; } bool tool_change_permission_t::init(player_t *player) { uint16 id = 0, perms = 0; const char *p = default_param; id = atoi(p); while( *p>0 && *p++!=',' ); perms = atoi(p); halthandle_t halt; halt.set_id(id); if( halt.is_bound() && player_t::check_owner(halt->get_owner(), player) ) { halt->set_permissions(perms); } return false; } bool tool_recolour_t::init(player_t *) { // skip the rest of the command const char *p = default_param; const char what = *p++; switch( what ) { case '1': case '2': { const uint8 player_nr = atoi(p); player_t *target_pl = player_nr < 16 ? welt->get_player(player_nr) : NULL; if (target_pl) { while (*p>0 && *p++!=',') {} if (*p) { const uint8 new_colour = atoi(p); const uint8 col1 = what == '1' ? new_colour : target_pl->get_player_color1(); const uint8 col2 = what == '2' ? new_colour : target_pl->get_player_color2(); target_pl->set_player_color(col1, col2); return false; } } } // fallthrough default: dbg->error( "tool_recolour_t::init", "Illegal request for player colour change! (%s)", default_param ); return false; } } bool tool_load_scenario_t::init(player_t*) { if (strempty(default_param) || strlen(default_param)<3 || default_param[1]!=',' ) { // no valid scenario parameter return false; } bool easy_server = default_param[0] == '1'; // since loading a scenario may not init the world welt->switch_server(easy_server, true); scenario_t* scn = new scenario_t(welt); const char* err = scn->init(str_get_basename(default_param+2).c_str(), str_get_filename(default_param + 2,true).c_str(), welt); if (err == NULL) { // start the game welt->set_pause(false); // open scenario info window destroy_win(magic_scenario_info); create_win(new scenario_info_t(), w_info, magic_scenario_info); tool_t::update_toolbars(); if (env_t::server) { welt->announce_server(karte_t::SERVER_ANNOUNCE_HELLO); } } else { if (env_t::server) { welt->switch_server(false, true); } create_win(new news_img(err), w_info, magic_none); delete scn; } // do not call work(), it's a no-op anyway return false; } // create (n) load (lxfilename, x!=0 start easy server), or save (sfilename) bool tool_work_world_t::init(player_t*) { if (strempty(default_param)) { return false; } char what = default_param[0]; if (what == 'n') { destroy_all_win(true); // new map translator::set_language(translator::get_language()); // reset also ingame names create_win({ 200, 100 }, new news_img("Erzeuge neue Karte.\n", skinverwaltung_t::neueweltsymbol->get_image_id(0)), w_info, magic_none); if (!env_t::default_settings.heightfield.empty()) { welt->load_heightfield(&env_t::default_settings); } else { env_t::default_settings.heightfield = ""; welt->init(&env_t::default_settings, 0); } welt->step_month(env_t::default_settings.get_starting_month()); welt->set_pause(false); destroy_all_win(true); welt->type_of_generation = karte_t::NEW_WORLD; env_t::default_settings.reset_after_global_settings_reload(); return true; } else if (what == 'l') { destroy_all_win(true); int easy_server = default_param[1]!='0'; const char* filename = default_param + 2; welt->switch_server(easy_server != 0, true); dr_chdir(env_t::user_dir); if (!welt->load(filename)) { // failed to load ... welt->switch_server(false, true); return false; } else { if (env_t::server) { welt->announce_server(karte_t::SERVER_ANNOUNCE_HELLO); } welt->type_of_generation = strstart(filename, "net:") ? karte_t::CLIENT_WORLD : karte_t::LOADED_WORLD; return true; } } else if (what == 's') { // saving a game if (env_t::server || socket_list_t::get_playing_clients() > 0) { network_reset_server(); #if 0 // TODO: saving without kicking all clients off ... // we have connected clients, so we do a sync const uint32 new_map_counter = welt->generate_new_map_counter(); nwc_sync_t* nw_sync = new nwc_sync_t(welt->get_sync_steps() + 1, welt->get_map_counter(), -1, new_map_counter); network_send_all(nw_sync, false); // and now we need to copy the servergame to the map ... #endif } dr_chdir(env_t::user_dir); long start_save = dr_time(); welt->save(default_param+1, false, env_t::savegame_version_str, false); DBG_MESSAGE("loadsave_frame_t::item_action", "save world %li ms", dr_time() - start_save); welt->set_dirty(); welt->reset_timer(); return true; } return false; } /* * Add a message to the message queue */ const char* tool_add_message_t::work(player_t* player, koord3d pos ) { if (default_param && *default_param ) { uint32 type = atoi(default_param); const char* text = strchr(default_param, ','); if ((type & message_t::MESSAGE_TYPE_MASK) >= message_t::MAX_MESSAGE_TYPE || text == NULL) { return ""; } welt->get_message()->add_message( text+1, pos, type, player == NULL || ( (type & message_t::PLAYER_MSG) != 0) ? gfx->palette_lookup(COL_BLACK) : PLAYER_FLAG|player->get_player_nr(), IMG_EMPTY ); } return NULL; } simutrans-124.5/src/simutrans/tool/simtool.h000066400000000000000000001523601520675460500212230ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TOOL_SIMTOOL_H #define TOOL_SIMTOOL_H /// New OO tool system #include "../simtypes.h" #include "../world/simworld.h" #include "../tool/simmenu.h" #include "../obj/simobj.h" #include "../obj/way/schiene.h" #include "../obj/baum.h" #include "../obj/groundobj.h" #include "../dataobj/environment.h" #include "../dataobj/translator.h" #include "../display/viewport.h" #include "../player/simplay.h" class koord3d; class koord; class way_builder_t; class building_desc_t; class roadsign_desc_t; class factory_desc_t; class way_desc_t; class route_t; class way_obj_desc_t; /****************************** helper functions: *****************************/ char *tooltip_with_price(const char * tip, sint64 price); char *tooltip_with_price_length(const char * tip, sint64 price, sint64 length); void open_error_msg_win(const char* error); /************************ general tool *******************************/ // query tile info: default tool class tool_query_t : public tool_t { public: tool_query_t() : tool_t(TOOL_QUERY | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Abfrage"); } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; // remove uppermost object from tile class tool_remover_t : public tool_t { private: bool tool_remover_intern(player_t *player, koord3d pos, sint8 type, const char *&msg); public: tool_remover_t() : tool_t(TOOL_REMOVER | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Abriss"); } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; // alter land height tools class tool_raise_lower_base_t : public tool_t { protected: bool is_dragging; sint16 drag_height; const char* drag(player_t*, koord k, sint16 h, int &n); virtual sint16 get_drag_height(koord k) = 0; bool check_dragging(); public: tool_raise_lower_base_t(uint16 id) : tool_t(id | GENERAL_TOOL), is_dragging(false), drag_height(0) { offset = Z_GRID; } image_id get_icon(player_t*) const OVERRIDE { return grund_t::underground_mode==grund_t::ugm_all ? IMG_EMPTY : icon; } bool init(player_t*) OVERRIDE { is_dragging = false; return true; } bool exit(player_t*) OVERRIDE { is_dragging = false; return true; } /** * technically move is not network safe, however its implementation is: * it sends work commands over network itself */ char const* move(player_t*, uint16 /* buttonstate */, koord3d) OVERRIDE; bool move_has_effects() const OVERRIDE { return true; } bool is_init_keeps_game_state() const OVERRIDE { return true; } /** * work() is only called when not dragging * if work() is called with is_dragging==true then is_dragging is reset */ bool is_work_keeps_game_state() const OVERRIDE { return is_dragging;} /** * @return true if this tool operates over the grid, not the map tiles. */ bool is_grid_tool() const OVERRIDE {return true;} bool update_pos_after_use() const OVERRIDE { return true; } }; class tool_raise_t : public tool_raise_lower_base_t { public: tool_raise_t() : tool_raise_lower_base_t(TOOL_RAISE_LAND) {} char const* get_tooltip(player_t const*) const OVERRIDE { return tooltip_with_price("Anheben", welt->get_settings().cst_alter_land); } char const* check_pos(player_t*, koord3d) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; sint16 get_drag_height(koord k) OVERRIDE; }; class tool_lower_t : public tool_raise_lower_base_t { public: tool_lower_t() : tool_raise_lower_base_t(TOOL_LOWER_LAND) {} char const* get_tooltip(player_t const*) const OVERRIDE { return tooltip_with_price("Absenken", welt->get_settings().cst_alter_land); } char const* check_pos(player_t*, koord3d) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; sint16 get_drag_height(koord k) OVERRIDE; }; /* slope tool definitions */ class tool_setslope_t : public tool_t { public: tool_setslope_t() : tool_t(TOOL_SETSLOPE | GENERAL_TOOL), old_slope_compatibility_mode(true) {} // if true then slope by default_param will be translated to new double-height system // true by default, can be set to false (used for scripts) bool old_slope_compatibility_mode; /** * Create an artificial slope * * @param player the player doing the task * @param pos position where the slope will be generated * @param slope the slope type * @param old_slope_compatibility * @param just_check */ static const char *tool_set_slope_work( player_t *player, koord3d pos, int slope, bool old_slope_compatibility, bool just_check = false); void rdwr_custom_data(memory_rw_t*) OVERRIDE; char const* get_tooltip(player_t const*) const OVERRIDE { return tooltip_with_price("Built artifical slopes", welt->get_settings().cst_set_slope); } bool is_init_keeps_game_state() const OVERRIDE { return true; } char const* check_pos(player_t*, koord3d) OVERRIDE; char const* work(player_t* const player, koord3d const k) OVERRIDE { return tool_set_slope_work(player, k, default_param ? atoi(default_param) : 0, old_slope_compatibility_mode); } bool init(player_t*) OVERRIDE { return default_param != NULL; } }; class tool_restoreslope_t : public tool_t { public: tool_restoreslope_t() : tool_t(TOOL_RESTORESLOPE | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return tooltip_with_price("Restore natural slope", welt->get_settings().cst_set_slope); } bool is_init_keeps_game_state() const OVERRIDE { return true; } char const* check_pos(player_t*, koord3d) OVERRIDE; char const* work(player_t* const player, koord3d const k) OVERRIDE { return tool_setslope_t::tool_set_slope_work(player, k, RESTORE_SLOPE, true); } }; class tool_marker_t : public kartenboden_tool_t { public: tool_marker_t() : kartenboden_tool_t(TOOL_MARKER | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return tooltip_with_price("Marker", welt->get_settings().cst_buy_land); } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; class tool_clear_reservation_t : public tool_t { public: tool_clear_reservation_t() : tool_t(TOOL_CLEAR_RESERVATION | GENERAL_TOOL) {} bool is_selected() const OVERRIDE; char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Clear block reservation"); } bool init(player_t*) OVERRIDE; bool exit(player_t*) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; class tool_transformer_t : public kartenboden_tool_t { private: bool is_powerline_available() const; public: tool_transformer_t() : kartenboden_tool_t(TOOL_TRANSFORMER | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE; image_id get_icon(player_t*) const OVERRIDE; bool init(player_t*) OVERRIDE; char const* check_pos(player_t*, koord3d) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE { return powerline_wt; } }; // default_string (optional) population,townhallname,rotation class tool_add_city_t : public kartenboden_tool_t { public: tool_add_city_t() : kartenboden_tool_t(TOOL_ADD_CITY | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return tooltip_with_price("Found new city", welt->get_settings().cst_found_city); } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; // buy a house to protect it from renovating class tool_buy_house_t : public kartenboden_tool_t { public: tool_buy_house_t() : kartenboden_tool_t(TOOL_BUY_HOUSE | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Haus kaufen"); } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /************** the following tools need a valid default_param ************************/ // step size by default_param class tool_change_city_size_t : public tool_t { public: tool_change_city_size_t() : tool_t(TOOL_CHANGE_CITY_SIZE | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate( atoi(default_param)>=0 ? "Grow city" : "Shrink city" ); } bool init(player_t*) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; // height change by default_param class tool_change_water_height_t : public tool_t { public: tool_change_water_height_t() : tool_t(TOOL_CHANGE_WATER_HEIGHT | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate( atoi(default_param)>=0 ? "Increase water height" : "Decrease water height" ); } bool init(player_t*) OVERRIDE; image_id get_icon(player_t *player) const OVERRIDE { return (!env_t::networkmode || player->is_public_service()) ? icon : IMG_EMPTY; } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; // height change by default_param class tool_set_climate_t : public two_click_tool_t { public: tool_set_climate_t() : two_click_tool_t(TOOL_SET_CLIMATE | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE; private: char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; class tool_plant_tree_t : public kartenboden_tool_t { public: tool_plant_tree_t() : kartenboden_tool_t(TOOL_PLANT_TREE | GENERAL_TOOL) {} image_id get_icon(player_t *) const OVERRIDE { return tree_builder_t::get_num_trees() > 0 ? icon : IMG_EMPTY; } char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate( "Plant tree" ); } bool init(player_t*) OVERRIDE; char const* move(player_t* const player, uint16 const b, koord3d const k) OVERRIDE; bool move_has_effects() const OVERRIDE { return true; } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; class tool_plant_groundobj_t : public kartenboden_tool_t { public: tool_plant_groundobj_t() : kartenboden_tool_t(TOOL_PLANT_GROUNDOBJ | GENERAL_TOOL) {} image_id get_icon(player_t *) const OVERRIDE { return groundobj_t::get_count() > 0 ? icon : IMG_EMPTY; } char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate( "Plant groundobj" ); } bool init(player_t*) OVERRIDE; char const* move(player_t* const player, uint16 const b, koord3d const k) OVERRIDE; bool move_has_effects() const OVERRIDE { return true; } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /* only called directly from schedule => no tooltip! * default_param must point to a schedule! */ class tool_schedule_add_t : public tool_t { public: tool_schedule_add_t() : tool_t(TOOL_SCHEDULE_ADD | GENERAL_TOOL) {} char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } // bool is_work_keeps_game_state() const OVERRIDE { return true; } bool is_work_here_keeps_game_state(player_t*, koord3d) OVERRIDE { return true; } }; class tool_schedule_ins_t : public tool_t { public: tool_schedule_ins_t() : tool_t(TOOL_SCHEDULE_INS | GENERAL_TOOL) {} char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } // bool is_work_keeps_game_state() const OVERRIDE { return true; } bool is_work_here_keeps_game_state(player_t*, koord3d) OVERRIDE { return true; } }; class tool_build_way_t : public two_click_tool_t { private: static const way_desc_t *defaults[17]; // default ways for all types char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; protected: const way_desc_t *desc; bool automatic_tunnel_and_bridges; uint32 max_length; virtual way_desc_t const* get_desc() const; const char *calc_route( way_builder_t &bauigel, const koord3d &, const koord3d & ); void start_at( koord3d &new_start ) OVERRIDE; public: tool_build_way_t(uint16 const id = TOOL_BUILD_WAY | GENERAL_TOOL) : two_click_tool_t(id), desc() { automatic_tunnel_and_bridges = false; max_length = 0x7FFFFFFFul; } image_id get_icon(player_t*) const OVERRIDE; static const way_desc_t* get_default_desc(waytype_t wt); char const* get_tooltip(player_t const*) const OVERRIDE; char const* get_default_param(player_t*) const OVERRIDE; bool is_selected() const OVERRIDE; bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE; // remove preview necessary while building elevated ways bool remove_preview_necessary() const OVERRIDE { return !is_first_click() && (desc && (desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt)); } }; class tool_build_cityroad : public tool_build_way_t { private: char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; #ifdef USE_TOWN_ROAD_BUILDER_TOOL uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; #endif public: tool_build_cityroad() : tool_build_way_t(TOOL_BUILD_CITYROAD | GENERAL_TOOL) {} way_desc_t const* get_desc() const OVERRIDE; image_id get_icon(player_t* const player) const OVERRIDE { return tool_t::get_icon(player); } bool is_selected() const OVERRIDE { return tool_t::is_selected(); } bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE { return road_wt; } }; class tool_build_bridge_t : public two_click_tool_t { private: ribi_t::ribi ribi; char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; public: tool_build_bridge_t() : two_click_tool_t(TOOL_BUILD_BRIDGE | GENERAL_TOOL) {} image_id get_icon(player_t*) const OVERRIDE { return grund_t::underground_mode==grund_t::ugm_all ? IMG_EMPTY : icon; } char const* get_tooltip(player_t const*) const OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE; bool remove_preview_necessary() const OVERRIDE { return !is_first_click(); } void rdwr_custom_data(memory_rw_t*) OVERRIDE; bool init(player_t*) OVERRIDE; }; class tool_build_tunnel_t : public two_click_tool_t { private: void calc_route( way_builder_t &bauigel, const koord3d &, const koord3d &); char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; public: tool_build_tunnel_t() : two_click_tool_t(TOOL_BUILD_TUNNEL | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE; char const* check_pos(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE; bool remove_preview_necessary() const OVERRIDE { return !is_first_click(); } bool init(player_t*) OVERRIDE; }; class tool_wayremover_t : public two_click_tool_t { private: bool calc_route( route_t &, player_t *, const koord3d& start, const koord3d &to ); char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; // error message to be returned by calc_route const char *calc_route_error; public: tool_wayremover_t() : two_click_tool_t(TOOL_WAYREMOVER | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE; image_id get_icon(player_t*) const OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE; }; class tool_build_wayobj_t : public two_click_tool_t { protected: const bool build; private: static const way_obj_desc_t *default_electric; const way_obj_desc_t *desc; const way_obj_desc_t *get_desc() const; waytype_t wt; bool calc_route( route_t &, player_t *, const koord3d& start, const koord3d &to ); char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; public: tool_build_wayobj_t(uint16 const id = TOOL_BUILD_WAYOBJ | GENERAL_TOOL, bool b = true) : two_click_tool_t(id), build(b) {} char const* get_tooltip(player_t const*) const OVERRIDE; bool is_selected() const OVERRIDE; bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE; }; class tool_remove_wayobj_t : public tool_build_wayobj_t { public: tool_remove_wayobj_t() : tool_build_wayobj_t(TOOL_REMOVE_WAYOBJ | GENERAL_TOOL, false) {} image_id get_icon(player_t*) const OVERRIDE; bool is_selected() const OVERRIDE { return tool_t::is_selected(); } bool is_init_keeps_game_state() const OVERRIDE { return true; } }; class tool_build_station_t : public tool_t { private: const char *tool_station_building_aux(player_t *, bool, koord3d, const building_desc_t *, sint8 rotation ); const char *tool_station_dock_aux(player_t *, koord3d, const building_desc_t * ); const char *tool_station_flat_dock_aux(player_t *, koord3d, const building_desc_t *, sint8 ); const char *tool_station_aux(player_t *, koord3d, const building_desc_t *, sint8, waytype_t, const char *halt_suffix ); const building_desc_t *get_desc( sint8 &rotation ) const; public: tool_build_station_t() : tool_t(TOOL_BUILD_STATION | GENERAL_TOOL) {} bool move_has_effects() const OVERRIDE { return true; } image_id get_icon(player_t*) const OVERRIDE; char const* get_tooltip(player_t const*) const OVERRIDE; bool init(player_t*) OVERRIDE; char const* check_pos(player_t*, koord3d) OVERRIDE; char const* move(player_t* const player, uint16 const b, koord3d const pos) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE; }; class tool_rotate_building_t : public tool_t { private: const char *tool_rotate_platform(koord3d); const char *tool_rotate_building(koord3d); public: tool_rotate_building_t() : tool_t(TOOL_ROTATE_BUILDING | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Rotate Building"); } char const* work(player_t *, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /// builds roadsigns and signals class tool_build_roadsign_t : public two_click_tool_t { struct signal_info_t { uint8 spacing = 5; // place signals every n tiles (smaller than 4 will hut maximum speed if not all clear some tiles ahead) bool remove_intermediate = true; bool replace_other = true; }; const roadsign_desc_t* desc = NULL; /// default values for this tool per player signal_info_t signal[MAX_PLAYER_COUNT]; /// values that will be used to build signal_info_t current; /// save direction of new signs vector_tpl directions; public: tool_build_roadsign_t(); public: /// @copydoc tool_t::get_tooltip const char *get_tooltip(const player_t *player) const OVERRIDE; /// @copydoc tool_t::init bool init(player_t*) OVERRIDE; /// @copydoc tool_t::exit bool exit(player_t*) OVERRIDE; /// Called by the GUI (gui/signal_spacing.*) void set_values(player_t *player, uint8 spacing, bool remove, bool replace); void get_values(player_t *player, uint8 &spacing, bool &remove, bool &replace); bool is_init_keeps_game_state() const OVERRIDE { return true; } void draw_after(scr_coord, bool dirty) const OVERRIDE; void rdwr_custom_data(memory_rw_t*) OVERRIDE; waytype_t get_waytype() const OVERRIDE; private: /// @copydoc two_click_tool_t::mark_tiles void mark_tiles(player_t *player, const koord3d &start, const koord3d &end) OVERRIDE; /// @copydoc two_click_tool_t::do_work const char *do_work(player_t *player, const koord3d &start, const koord3d &end) OVERRIDE; /// @copydoc two_click_tool_t::is_valid_pos uint8 is_valid_pos(player_t *player, const koord3d &builder, char const *&error, const koord3d &start) OVERRIDE; const char *check_pos_intern(player_t *, koord3d); const char *place_sign_intern(player_t *player, grund_t *gr); bool calc_route(route_t &route, player_t *player, const koord3d &from, const koord3d &to); }; class tool_build_depot_t : public tool_t { private: const char *tool_depot_aux(player_t *player, koord3d pos, const building_desc_t *desc, waytype_t wegtype); public: tool_build_depot_t() : tool_t(TOOL_BUILD_DEPOT | GENERAL_TOOL) {} image_id get_icon(player_t*) const OVERRIDE; char const* get_tooltip(player_t const*) const OVERRIDE; bool init(player_t*) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } waytype_t get_waytype() const OVERRIDE; }; /** * builds (random) tourist attraction (default_param==NULL) and maybe adds it to the next city * the parameter string is a follow (or NULL): * 1#theater * first letter: ignore climates * second letter: rotation (0,1,2,3,#=random) * finally building name */ class tool_build_house_t : public kartenboden_tool_t { public: tool_build_house_t() : kartenboden_tool_t(TOOL_BUILD_HOUSE | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Built random attraction"); } bool init(player_t*) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /** * builds an (if param=NULL random) industry chain starting here * * the parameter string is a follow (or NULL): * 1#34,oelfeld * first letter: ignore climates * second letter: rotation (0,1,2,3,#=random) * next number is production value * finally industry name * NULL means random chain! */ class tool_build_land_chain_t : public kartenboden_tool_t { public: tool_build_land_chain_t() : kartenboden_tool_t(TOOL_BUILD_LAND_CHAIN | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Build land consumer"); } bool init(player_t*) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; class tool_city_chain_t : public kartenboden_tool_t { public: tool_city_chain_t() : kartenboden_tool_t(TOOL_CITY_CHAIN | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Build city market"); } bool init(player_t*) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; class tool_build_factory_t : public kartenboden_tool_t { public: tool_build_factory_t() : kartenboden_tool_t(TOOL_BUILD_FACTORY | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Build city market"); } bool init(player_t*) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } private: bool can_build_factory_here(const factory_desc_t *desc, koord topleft, int rotation, climate_bits cl); }; class tool_link_factory_t : public two_click_tool_t { public: tool_link_factory_t() : two_click_tool_t(TOOL_LINK_FACTORY | GENERAL_TOOL) {} image_id get_icon(player_t*) const OVERRIDE { return world()->get_settings().is_crossconnect_factories() ? IMG_EMPTY : icon; } char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Connect factory"); } bool is_init_keeps_game_state() const OVERRIDE { return true; } private: bool init(player_t*pl) OVERRIDE { if (world()->get_settings().is_crossconnect_factories()) return false; return two_click_tool_t::init(pl); } char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE {} uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; image_id get_marker_image() const OVERRIDE; }; class tool_headquarter_t : public kartenboden_tool_t { private: const building_desc_t *next_level( const player_t *player ) const; public: tool_headquarter_t() : kartenboden_tool_t(TOOL_HEADQUARTER | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE; bool init(player_t*) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /* protects map from further change (here because two clicks to confirm it!) */ class tool_lock_game_t : public tool_t { public: tool_lock_game_t() : tool_t(TOOL_LOCK_GAME | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return env_t::networkmode ? translator::translate("deactivated in online mode") : translator::translate("Lock game"); } image_id get_icon(player_t*) const OVERRIDE { return env_t::networkmode ? IMG_EMPTY : icon; } // deactivate in network mode bool init(player_t *) OVERRIDE { return !env_t::networkmode; } const char *work( player_t *, koord3d ) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /* add random citycar if no default is set; else add a certain city car */ class tool_add_citycar_t : public tool_t { public: tool_add_citycar_t() : tool_t(TOOL_ADD_CITYCAR | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Add random citycar"); } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /* make forest */ class tool_forest_t : public two_click_tool_t { public: tool_forest_t() : two_click_tool_t(TOOL_FOREST | GENERAL_TOOL) {} image_id get_icon(player_t *) const OVERRIDE { return tree_builder_t::has_trees() ? icon : IMG_EMPTY; } char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Add forest"); } bool init( player_t *player) OVERRIDE { return tree_builder_t::has_trees() && two_click_tool_t::init(player); } private: char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /* stop moving tool */ class tool_stop_mover_t : public two_click_tool_t { private: waytype_t waytype[2]; halthandle_t last_halt; public: tool_stop_mover_t() : two_click_tool_t(TOOL_STOP_MOVER | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("replace stop"); } bool is_init_keeps_game_state() const OVERRIDE { return true; } private: char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE {} uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; image_id get_marker_image() const OVERRIDE; void read_start_position(player_t *player, const koord3d &pos); }; /* make all tiles of this player a public stop * if this player is public, make all connected tiles a public stop */ class tool_make_stop_public_t : public tool_t { public: tool_make_stop_public_t() : tool_t(TOOL_MAKE_STOP_PUBLIC | GENERAL_TOOL) {} bool init(player_t * ) OVERRIDE; bool exit(player_t *s ) OVERRIDE { return init(s); } char const* get_tooltip(player_t const*) const OVERRIDE; char const* move(player_t*, uint16 /* buttonstate */, koord3d) OVERRIDE; bool move_has_effects() const OVERRIDE { return true; } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; /* merge stop */ class tool_merge_stop_t : public two_click_tool_t { private: halthandle_t halt_be_merged_from; halthandle_t halt_be_merged_to; public: tool_merge_stop_t() : two_click_tool_t(TOOL_MERGE_STOP | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("merge stop"); } bool is_init_keeps_game_state() const OVERRIDE { return true; } private: char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; image_id get_marker_image() const OVERRIDE; }; // removes signal from tile class tool_remove_signal_t : public tool_t { public: tool_remove_signal_t() : tool_t(TOOL_REMOVE_SIGNAL | GENERAL_TOOL) {} image_id get_icon(player_t*) const OVERRIDE; char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("remove signal"); } char const* work(player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } }; // saves the infrostructure of an area as a building script class tool_generate_script_t : public two_click_tool_t { public: tool_generate_script_t() : two_click_tool_t(TOOL_GENERATE_SCRIPT | GENERAL_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("generate script"); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } bool save_script(const char *fullpath, const char *command, koord area) const; private: char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE { return 3; }; }; // Copies item under cursor into cursor class tool_pipette_t : public tool_t { public: tool_pipette_t() : tool_t(TOOL_PIPETTE | GENERAL_TOOL) {} public: const char *get_tooltip(const player_t *) const OVERRIDE { return translator::translate("Pipette"); } const char *work(player_t *, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } private: const char* allow_tool_check(const obj_t* obj, const obj_desc_timelined_t* desc, const player_t* pl) const; }; // internal tool: show error message at specific coordinate // used for scenario error messages send by server class tool_error_message_t : public tool_t { public: tool_error_message_t() : tool_t(TOOL_ERROR_MESSAGE | GENERAL_TOOL) {} bool init(player_t*) OVERRIDE { return true; } bool is_init_keeps_game_state() const OVERRIDE { return true; } char const* work(player_t*, koord3d) OVERRIDE { return default_param ? default_param : ""; } }; /********************* one click tools ****************************/ class tool_pause_t : public tool_t { public: tool_pause_t() : tool_t(TOOL_PAUSE | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE; image_id get_icon(player_t*) const OVERRIDE { return (env_t::networkmode && !env_t::server) ? IMG_EMPTY : icon; } bool is_selected() const OVERRIDE { return env_t::networkmode ? env_t::pause_server_no_clients : welt->is_paused(); } bool init(player_t*) OVERRIDE; bool exit(player_t *s) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return !env_t::networkmode; } bool is_work_keeps_game_state() const OVERRIDE { return !env_t::networkmode; } }; class tool_fastforward_t : public tool_t { public: tool_fastforward_t() : tool_t(TOOL_FASTFORWARD | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return env_t::networkmode ? translator::translate("deactivated in online mode") : translator::translate("Fast forward"); } image_id get_icon(player_t*) const OVERRIDE { return env_t::networkmode ? IMG_EMPTY : icon; } bool is_selected() const OVERRIDE { return welt->is_fast_forward(); } bool init( player_t * ) OVERRIDE { if( !env_t::networkmode ) { if( welt->is_fast_forward() && env_t::simple_drawing_fast_forward ) { welt->set_dirty(); } welt->set_pause(0); welt->set_fast_forward( welt->is_fast_forward()^1 ); } return false; } bool exit(player_t *s ) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return !env_t::networkmode; } bool is_work_keeps_game_state() const OVERRIDE { return !env_t::networkmode; } }; class tool_screenshot_t : public tool_t { public: tool_screenshot_t() : tool_t(TOOL_SCREENSHOT | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Screenshot"); } bool init(player_t * ) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; // builds next chain class tool_increase_industry_t : public tool_t { public: tool_increase_industry_t() : tool_t(TOOL_INCREASE_INDUSTRY | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Increase Industry density"); } bool init( player_t * ) OVERRIDE; }; /* undo building */ class tool_undo_t : public tool_t { public: tool_undo_t() : tool_t(TOOL_UNDO | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Undo last ways construction"); } bool init(player_t *player ) OVERRIDE; }; /* switch to next player */ class tool_switch_player_t : public tool_t { public: tool_switch_player_t() : tool_t(TOOL_SWITCH_PLAYER | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Change player"); } bool init( player_t * ) OVERRIDE { welt->switch_active_player( welt->get_active_player_nr()+1, true ); return false; } // since it is handled internally bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; // step one year forward class tool_step_year_t : public tool_t { public: tool_step_year_t() : tool_t(TOOL_STEP_YEAR | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Step timeline one year"); } bool init( player_t *player ) OVERRIDE { if( !env_t::networkmode || player->is_public_service() ) { // in networkmode only for public player welt->step_year(); } return false; } }; class tool_change_game_speed_t : public tool_t { public: tool_change_game_speed_t() : tool_t(TOOL_CHANGE_GAME_SPEED | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { int factor = atoi(default_param); return factor>0 ? translator::translate("Accelerate time") : translator::translate("Deccelerate time"); } bool init( player_t *player ) OVERRIDE { if( !env_t::networkmode || player->is_public_service() ) { // in networkmode only for public player welt->change_time_multiplier( atoi(default_param) ); } return false; } }; class tool_zoom_in_t : public tool_t { public: tool_zoom_in_t() : tool_t(TOOL_ZOOM_IN | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("zooming in"); } bool init( player_t * ) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_zoom_out_t : public tool_t { public: tool_zoom_out_t() : tool_t(TOOL_ZOOM_OUT | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("zooming out"); } bool init( player_t * ) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_show_coverage_t : public tool_t { public: tool_show_coverage_t() : tool_t(TOOL_SHOW_COVERAGE | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("show station coverage"); } bool is_selected() const OVERRIDE { return env_t::station_coverage_show; } bool init( player_t * ) OVERRIDE { env_t::station_coverage_show = !env_t::station_coverage_show; welt->set_dirty(); return false; } bool exit(player_t *s ) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_show_factory_storage_t : public tool_t { public: tool_show_factory_storage_t() : tool_t(TOOL_SHOW_FACTORY_STORAGE | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("switch the industry storage display mode"); } bool init(player_t *) OVERRIDE { env_t::show_factory_storage_bar = (env_t::show_factory_storage_bar+1) % 4; welt->set_dirty(); return false; } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_show_name_t : public tool_t { public: tool_show_name_t() : tool_t(TOOL_SHOW_NAME | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate( (env_t::show_names>>2)==2 ? "hide station names" : (env_t::show_names&1) ? "show waiting bars" : "show station names"); } bool init( player_t * ) OVERRIDE { if( env_t::show_names>=11 ) { if( (env_t::show_names&3)==3 ) { env_t::show_names = 0; } else { env_t::show_names = 2; } } else if( env_t::show_names==2 ) { env_t::show_names = 3; } else if( env_t::show_names==0 ) { env_t::show_names = 1; } else { env_t::show_names += 4; } welt->set_dirty(); return false; } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_show_grid_t : public tool_t { public: tool_show_grid_t() : tool_t(TOOL_SHOW_GRID | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("show grid"); } bool is_selected() const OVERRIDE { return grund_t::show_grid; } bool init( player_t * ) OVERRIDE { grund_t::toggle_grid(); welt->set_dirty(); return false; } bool exit(player_t *s ) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_show_trees_t : public tool_t { public: tool_show_trees_t() : tool_t(TOOL_SHOW_TREES | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("hide trees"); } bool is_selected() const OVERRIDE {return env_t::hide_trees; } bool init( player_t * ) OVERRIDE; bool exit(player_t *s ) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_show_houses_t : public tool_t { public: tool_show_houses_t() : tool_t(TOOL_SHOW_HOUSES | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate( env_t::hide_buildings==0 ? "hide city building" : (env_t::hide_buildings==1) ? "hide all building" : "show all building"); } bool is_selected() const OVERRIDE { return env_t::hide_buildings>0; } bool init( player_t * ) OVERRIDE { env_t::hide_buildings ++; if(env_t::hide_buildings>env_t::ALL_HIDDEN_BUILDING) { env_t::hide_buildings = env_t::NOT_HIDE; } welt->set_dirty(); return false; } bool exit(player_t* s) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_show_underground_t : public tool_t { public: tool_show_underground_t() : tool_t(TOOL_SHOW_UNDERGROUND | SIMPLE_TOOL) {} static sint8 save_underground_level; char const* get_tooltip(player_t const*) const OVERRIDE; bool is_selected() const OVERRIDE; void draw_after(scr_coord, bool dirty) const OVERRIDE; bool init(player_t *) OVERRIDE; char const* work(player_t*, koord3d) OVERRIDE; bool exit(player_t *) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_toggle_control_t : public tool_t { public: tool_toggle_control_t() : tool_t(TOOL_TOGGLE_CONTROL | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Lock Control key"); } bool is_selected() const OVERRIDE { return tool_t::control_invert; } bool init(player_t*) OVERRIDE { tool_t::control_invert = WFL_CTRL; return false; } bool exit(player_t *) OVERRIDE { tool_t::control_invert = 0; return false; } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_load_scenario_t : public tool_t { // internal tool to start a scenario // command i.filename // if i==1, start as easyserver public: tool_load_scenario_t() : tool_t(TOOL_LOAD_SCENARIO | SIMPLE_TOOL) { flags = WFL_LOCAL | WFL_NO_CHK; } bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } bool is_work_keeps_game_state() const OVERRIDE { return false; } }; class tool_work_world_t : public tool_t { // internal tool to start a scenario // command i.filename // if i==1, start as easyserver public: tool_work_world_t() : tool_t(TOOL_WORK_WORLD | SIMPLE_TOOL) { flags = WFL_LOCAL | WFL_NO_CHK; } bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } bool is_work_keeps_game_state() const OVERRIDE { return false; } }; class tool_rotate90_t : public tool_t { public: tool_rotate90_t() : tool_t(TOOL_ROTATE90 | SIMPLE_TOOL) {} image_id get_icon(player_t*) const OVERRIDE { return env_t::networkmode ? IMG_EMPTY : icon; } void draw_after(scr_coord pos, bool dirty) const OVERRIDE; /* may draw a compass on top */ char const* get_tooltip(player_t const*) const OVERRIDE { return env_t::networkmode ? translator::translate("deactivated in online mode") : translator::translate("Rotate map"); } bool init( player_t * ) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } bool is_work_keeps_game_state() const OVERRIDE { return false; } }; class tool_quit_t : public tool_t { // default_parameter not empty: start new game public: tool_quit_t() : tool_t(TOOL_QUIT | SIMPLE_TOOL) { flags = WFL_LOCAL | WFL_NO_CHK; } char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate( (default_param && *default_param) ? "Neue Welt" : "Beenden"); } bool init( player_t * ) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } bool is_work_keeps_game_state() const OVERRIDE { return false; } }; // step size by default_param class tool_fill_trees_t : public tool_t { public: tool_fill_trees_t() : tool_t(TOOL_FILL_TREES | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Fill trees"); } image_id get_icon(player_t *) const OVERRIDE { return tree_builder_t::has_trees() ? icon : IMG_EMPTY; } bool init(player_t * ) OVERRIDE { if( tree_builder_t::has_trees() && default_param ) { tree_builder_t::fill_trees( atoi(default_param), 0, 0, welt->get_size().x, welt->get_size().y ); } return false; } }; /* change day/night view manually */ class tool_daynight_level_t : public tool_t { public: tool_daynight_level_t() : tool_t(TOOL_DAYNIGHT_LEVEL | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE; bool init(player_t * ) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_vehicle_tooltips_t : public tool_t { public: tool_vehicle_tooltips_t() : tool_t(TOOL_VEHICLE_TOOLTIPS | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Toggle vehicle tooltips"); } bool init( player_t * ) OVERRIDE { env_t::show_vehicle_states = (env_t::show_vehicle_states+1)%4; welt->set_dirty(); return false; } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_toggle_pax_station_t : public tool_t { public: tool_toggle_pax_station_t() : tool_t(TOOL_TOOGLE_PAX | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("5LIGHT_CHOOSE"); } bool is_selected() const OVERRIDE { return env_t::stop_pedestrians; } bool init( player_t * ) OVERRIDE { env_t::stop_pedestrians = !env_t::stop_pedestrians; return false; } bool exit(player_t *s ) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_toggle_pedestrians_t : public tool_t { public: tool_toggle_pedestrians_t() : tool_t(TOOL_TOOGLE_PEDESTRIANS | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("6LIGHT_CHOOSE"); } bool is_selected() const OVERRIDE { return env_t::random_pedestrians; } bool init( player_t * ) OVERRIDE { env_t::random_pedestrians = !env_t::random_pedestrians; return false; } bool exit(player_t *s ) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_toggle_reservation_t : public tool_t { public: tool_toggle_reservation_t() : tool_t(TOOL_TOGGLE_RESERVATION | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("show/hide block reservations"); } bool is_selected() const OVERRIDE; bool init( player_t * ) OVERRIDE { schiene_t::show_reservations = !schiene_t::show_reservations; env_t::show_single_ways = false; welt->set_dirty(); return false; } bool exit(player_t* s) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_view_owner_t : public tool_t { public: tool_view_owner_t() : tool_t(TOOL_VIEW_OWNER | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("show/hide object owner"); } bool is_selected() const OVERRIDE { return obj_t::show_owner; } bool init( player_t * ) OVERRIDE { obj_t::show_owner ^= 1; welt->set_dirty(); return false; } bool exit(player_t* s) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_hide_under_cursor_t : public tool_t { public: tool_hide_under_cursor_t() : tool_t(TOOL_HIDE_UNDER_CURSOR | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("hide objects under cursor"); } bool is_selected() const OVERRIDE { return env_t::hide_under_cursor; } bool init( player_t * ) OVERRIDE { env_t::hide_under_cursor = !env_t::hide_under_cursor && env_t::cursor_hide_range>0; welt->set_dirty(); return false; } bool exit(player_t *s ) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_move_map_t : public tool_t { public: tool_move_map_t() : tool_t(TOOL_MOVE_MAP | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Move the map"); } bool is_selected() const OVERRIDE { return false; } bool init( player_t * ) OVERRIDE { assert( default_param ); if( const char *c = strchr( default_param, '|' ) ) { koord delta( atoi( default_param ), atoi( c+1 ) ); welt->get_viewport()->change_world_position(welt->get_viewport()->get_world_position() + delta); welt->set_dirty(); } return false; } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; class tool_rollup_all_win_t : public tool_t { public: tool_rollup_all_win_t() : tool_t(TOOL_ROLLUP_ALL_WIN | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("Hide/open all windows"); } bool is_selected() const OVERRIDE { return false; } bool init( player_t * ) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; // on off of night mode class tool_day_night_toggle_t : public tool_t { public: tool_day_night_toggle_t() : tool_t(TOOL_DAY_NIGHT_TOGGLE | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("8WORLD_CHOOSE"); } bool is_selected() const OVERRIDE { return env_t::night_shift; } bool init(player_t*) OVERRIDE { env_t::night_shift = !env_t::night_shift; welt->set_dirty(); return false; } bool exit(player_t* s) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; // highlight line single way tiles class tool_show_single_ways_t : public tool_t { public: tool_show_single_ways_t() : tool_t(TOOL_SINGLE_WAY_TOOGLE | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("highlight single ways"); } bool is_selected() const OVERRIDE { return env_t::show_single_ways; } bool init(player_t*) OVERRIDE { env_t::show_single_ways = !env_t::show_single_ways; schiene_t::show_reservations = false; welt->set_dirty(); return false; } bool exit(player_t* s) OVERRIDE { return init(s); } bool is_init_keeps_game_state() const OVERRIDE { return true; } bool is_work_keeps_game_state() const OVERRIDE { return true; } }; /******************************** Internal tools ***********/ /* internal simple tools needed for network synchronisation */ class tool_traffic_level_t : public tool_t { public: tool_traffic_level_t() : tool_t(TOOL_TRAFFIC_LEVEL | SIMPLE_TOOL) {} char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("6WORLD_CHOOSE"); } bool is_selected() const OVERRIDE { return false; } bool init( player_t * ) OVERRIDE { assert( default_param ); sint16 level = clamp(atoi(default_param), 0, 16); welt->get_settings().set_traffic_level(level); return false; } bool is_init_keeps_game_state() const OVERRIDE { return false; } }; class tool_change_convoi_t : public tool_t { public: tool_change_convoi_t() : tool_t(TOOL_CHANGE_CONVOI | SIMPLE_TOOL) {} bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } }; class tool_change_line_t : public tool_t { public: tool_change_line_t() : tool_t(TOOL_CHANGE_LINE | SIMPLE_TOOL) {} bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } }; class tool_change_depot_t : public tool_t { public: tool_change_depot_t() : tool_t(TOOL_CHANGE_DEPOT | SIMPLE_TOOL) {} bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } }; // adds a new player of certain type to the map class tool_change_player_t : public tool_t { public: tool_change_player_t() : tool_t(TOOL_CHANGE_PLAYER | SIMPLE_TOOL) {} bool init(player_t*) OVERRIDE; }; // change timing of traffic light class tool_change_traffic_light_t : public tool_t { public: tool_change_traffic_light_t() : tool_t(TOOL_CHANGE_TRAFFIC_LIGHT | SIMPLE_TOOL) {} bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } }; // change city: (dis)allow growth class tool_change_city_t : public tool_t { public: tool_change_city_t() : tool_t(TOOL_CHANGE_CITY | SIMPLE_TOOL) {} bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } }; // internal tool: rename stuff class tool_rename_t : public tool_t { public: tool_rename_t() : tool_t(TOOL_RENAME | SIMPLE_TOOL) {} bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } }; // internal tool: change player colours class tool_recolour_t : public tool_t { public: tool_recolour_t() : tool_t(TOOL_RECOLOUR_TOOL | SIMPLE_TOOL) {} virtual bool init(player_t * ) OVERRIDE; virtual bool is_init_network_save() const { return false; } }; // internal tool: send message, with additional coordinate information class tool_add_message_t : public tool_t { public: tool_add_message_t() : tool_t(TOOL_ADD_MESSAGE | GENERAL_TOOL) {} const char *work( player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } // work is not safe, has to be send over network }; // internal tool: set owner class tool_change_owner_t : public tool_t { public: tool_change_owner_t() : tool_t(TOOL_SET_OWNER | GENERAL_TOOL) {} const char *work( player_t*, koord3d) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return true; } // work is not safe, has to be send over network }; // internal tool: set permissions class tool_change_permission_t : public tool_t { public: tool_change_permission_t() : tool_t(TOOL_HALT_PERMISSION | SIMPLE_TOOL) {} bool init(player_t*) OVERRIDE; bool is_init_keeps_game_state() const OVERRIDE { return false; } virtual bool is_init_network_save() const { return false; } }; #endif simutrans-124.5/src/simutrans/tpl/000077500000000000000000000000001520675460500171775ustar00rootroot00000000000000simutrans-124.5/src/simutrans/tpl/array2d_tpl.h000066400000000000000000000057341520675460500216040ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_ARRAY2D_TPL_H #define TPL_ARRAY2D_TPL_H #include #include #include "../dataobj/koord.h" #include "../simdebug.h" /** * A template class for bounds checked 2-dimensional arrays. * This is kept as simple as possible. Does not use exceptions * for error handling. */ template class array2d_tpl { private: T* data; unsigned w, h; public: array2d_tpl(unsigned _w, unsigned _h) : w(_w), h(_h) { data = new T[w*h]; } array2d_tpl(const array2d_tpl &other) { w = other.w; h = other.h; data = new T[w*h]; memcpy(data, other.data, sizeof(T)*w*h); } ~array2d_tpl() { delete [] data; } unsigned get_width() const { return w; } unsigned get_height() const { return h; } void clear() { delete [] data; data = 0; w = h = 0; } void init( T value ) { if(sizeof(T)==1) { memset( data, value, w*h ); } else { unsigned i=(w*h); while( i>0 ) { data[--i] = value; } } } // all informations in the array are lost void resize(unsigned resize_x, unsigned resize_y ) { if( w*h != resize_x*resize_y ) { T* new_data = new T[resize_x*resize_y]; delete [] data; data = new_data; } w = resize_x; h = resize_y; } void rotate90() { if( w*h > 0 ) { T *new_data = new T[w*h]; for( unsigned y=0; yfatal("array2d_tpl::at()","index out of bounds: (%d,%d) not in (0..%d, 0..%d)", x, y, w-1, h-1); } return data[y*w + x]; } T& at(koord k) { return at((unsigned)k.x, (unsigned)k.y); } /* * use this with care, you'll lose all checks! */ const T* to_array() const { return data; } array2d_tpl & operator = (const array2d_tpl &other) { if( this != &other ) {// protect against invalid self-assignment if( h != other.h && w != other.w ) { if( h*w!=0 ) { dbg->error("array2d_tpl::=()","source has different size!"); } } delete [] data; w = other.w; h = other.h; data = new T[w*h]; memcpy(data, other.data, sizeof(T)*w*h); } return *this; } }; #endif simutrans-124.5/src/simutrans/tpl/array_tpl.h000066400000000000000000000041721520675460500213510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_ARRAY_TPL_H #define TPL_ARRAY_TPL_H #include #include "../simdebug.h" #include "../simtypes.h" /** * A template class for bounds checked 1-dimensional arrays. * This is kept as simple as possible. Does not use exceptions * for error handling. */ template class array_tpl { public: typedef const T* const_iterator; typedef T* iterator; typedef uint32 index; explicit array_tpl() : data(NULL), size(0) {} explicit array_tpl(index s) : data(new T[s]), size(s) {} explicit array_tpl(index s, const T& value) : data(new T[s]), size(s) { for (index i = 0; i < size; i++) { data[i] = value; } } ~array_tpl() { delete [] data; } index get_count() const { return size; } bool empty() const { return size == 0; } void clear() { delete [] data; data = 0; size = 0; } void resize(index resize) { if (size < resize) { // extend if needed T* new_data = new T[resize]; for (index i = 0; i < size; i++) { new_data[i] = data[i]; } delete [] data; data = new_data; } size = resize; } void resize(index resize, const T& value) { if (size < resize) { T* new_data = new T[resize]; index i; for (i = 0; i < size; i++) { new_data[i] = data[i]; } for (; i < resize; i++) { new_data[i] = value; } delete [] data; data = new_data; size = resize; } } T& operator [](index i) { if (i >= size) { dbg->fatal("array_tpl::[]", "index out of bounds: %d not in 0..%d, T=%s", i, size - 1, typeid(T).name()); } return data[i]; } const T& operator [](index i) const { if (i >= size) { dbg->fatal("array_tpl::[]", "index out of bounds: %d not in 0..%d, T=%s", i, size - 1, typeid(T).name()); } return data[i]; } iterator begin() { return data; } iterator end() { return data + size; } const_iterator begin() const { return data; } const_iterator end() const { return data + size; } private: array_tpl(const array_tpl&); array_tpl& operator=( array_tpl const& other ); T* data; index size; }; #endif simutrans-124.5/src/simutrans/tpl/binary_heap_tpl.h000066400000000000000000000053401520675460500225120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_BINARY_HEAP_TPL_H #define TPL_BINARY_HEAP_TPL_H #include "../simmem.h" #include "../simtypes.h" /** * my try on a binary heap template, * inspired by the pathfinder of OTTD written by kuDr * * For information about Binary Heap algorithm, * see: https://en.wikipedia.org/wiki/Binary_heap */ template class binary_heap_tpl { private: T *nodes; public: uint32 node_count; uint32 node_size; binary_heap_tpl(uint32 size = 4096) { nodes = MALLOCN(T, size); node_size = size; node_count = 0; } ~binary_heap_tpl() { free( nodes ); } /** * Inserts an element into the queue. * in such a way that the lowest is at the top of this tree in an array * parts inspired from OTTD */ void insert(const T item) { node_count ++; // need to enlarge? (must be 2^x) if(node_count==node_size) { T *tmp=nodes; node_size *= 2; nodes = MALLOCN(T, node_size); memcpy( nodes, tmp, sizeof(T)*(node_size/2) ); free( tmp ); } // now we have to move it to the right position int gap = node_count; for (int parent = gap/2; (parent>0) && (*item <= *nodes[parent]); ) { nodes[gap] = nodes[parent]; gap = parent; parent /= 2; } nodes[gap] = item; } /** * unfortunately, the removing is as much effort as the insertion ... */ T pop() { assert(!empty()); T result = nodes[1]; // at index 1 is the lowest one uint32 gap = 1; // this is the last one T item = nodes[node_count--]; // now we must maintain relation between parent and its children: // parent <= any child // from head down to the tail uint32 child = 2; // first child is at [parent * 2] // while children are valid while(child<=node_count) { // choose the smaller child if( child we are done break; } // if smaller child is smaller than parent, it will become new parent nodes[gap] = nodes[child]; gap = child; // where do we have our new children? child = gap * 2; } // move last item to the proper place if (node_count>0) { nodes[gap] = item; } return result; } /** * Recycles all nodes. Doesn't delete the objects. * Leaves the list empty. */ void clear() { node_count = 0; } uint32 get_count() const { return node_count; } bool empty() const { return node_count == 0; } const T& front() { assert(!empty()); return nodes[1]; } private: binary_heap_tpl(const binary_heap_tpl& other); binary_heap_tpl& operator=( binary_heap_tpl const& other ); }; #endif simutrans-124.5/src/simutrans/tpl/freelist_iter_tpl.h000066400000000000000000000152441520675460500230750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_FREELIST_ITER_TPL_H #define TPL_FREELIST_ITER_TPL_H #include "../simmem.h" #include "../simconst.h" #include #include #ifdef MULTI_THREADx #include "../utils/simthread.h" #endif // define USE_VALGRIND_MEMCHECK to make // valgrind aware of the freelist memory pool #ifdef USE_VALGRIND_MEMCHECK #include #endif /** * A template class for const sized memory pool * Must be a static member! Does not use exceptions */ template class freelist_iter_tpl { private: struct nodelist_node_t { #ifdef DEBUG_FREELIST char canary[4]; #endif nodelist_node_t* next; }; const char *canary_free = "\xAA\x55\xAA"; const char *canary_used = "\x55\xAA\x55"; #ifdef MULTI_THREADx pthread_mutex_t freelist_mutex = PTHREAD_MUTEX_INITIALIZER;; #endif // next free node (or NULL) nodelist_node_t* freelist; // number of currently allocated node size_t nodecount; // we aim for near 32 kB chunks, hoping that the system will allocate them on each page // and they fit the L1 cache static constexpr size_t NODE_SIZE = (sizeof(T) + sizeof(nodelist_node_t)-sizeof(nodelist_node_t *)); static constexpr size_t new_chuck_size = (32250*8) / (NODE_SIZE*8+1); struct chunklist_node_t { chunklist_node_t *chunk_next; // marking empty and allocated tiles for fast interation std::bitset<(32250*8) / (NODE_SIZE*8+1)> allocated_mask; }; // list of all allocated memory chunklist_node_t* chunk_list; void change_obj(char *p,bool b) { char *c_list = (char *)chunk_list; const size_t chunk_mem_size = sizeof(chunklist_node_t) + (NODE_SIZE * new_chuck_size); while (c_list && (p continue c_list = (char *)((chunklist_node_t *)c_list)->chunk_next; } // we have found us (or we crash on error) size_t index = ((p - c_list) - sizeof(chunklist_node_t)) / NODE_SIZE; assert(index < new_chuck_size); ((chunklist_node_t*)c_list)->allocated_mask.set(index, b); } // clears all list memories void free_all_nodes() { while (chunk_list) { chunklist_node_t* p = chunk_list; chunk_list = chunk_list->chunk_next; // now release memory #ifdef USE_VALGRIND_MEMCHECK VALGRIND_DESTROY_MEMPOOL(p); #endif free(p); } freelist = 0; chunk_list = 0; nodecount = 0; } public: freelist_iter_tpl() : freelist(0), nodecount(0), chunk_list(0) {} void sync_step(uint32 delta_t) { chunklist_node_t* c_list = chunk_list; while (c_list) { char *p = ((char *)c_list)+sizeof(chunklist_node_t); for (unsigned i = 0; i < new_chuck_size; i++) { if (c_list->allocated_mask.test(i)) { // is active object T *obj = (T *)&(((nodelist_node_t*)(p + (i * NODE_SIZE)))->next); if (sync_result result = obj->sync_step(delta_t)) { // remove from sync c_list->allocated_mask.set(i, false); // and maybe delete if (result == SYNC_DELETE) { delete obj; if (nodecount == 0) { return; // since even the main chunk list became invalid } } } } } c_list = c_list->chunk_next; } } // switch on off sync handling void add_sync(T* p) { change_obj((char*)p,true); }; void remove_sync(T* p) { change_obj((char*)p,false); }; size_t get_nodecout() const { return nodecount; } void *gimme_node() { #ifdef MULTI_THREADx pthread_mutex_lock(&freelist_mutex); #endif nodelist_node_t *tmp; if (freelist == NULL) { char* p = (char*)xmalloc(new_chuck_size*NODE_SIZE + sizeof(chunklist_node_t)); memset(p, 0, sizeof(chunklist_node_t)); // clear allocation bits and next pointer #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we still cannot access the pool p VALGRIND_MAKE_MEM_NOACCESS(p, new_chuck_size * sizeof(T) + sizeof(chunklist_node_t)); #endif // put the memory into the chunklist for free it chunklist_node_t* chunk = (chunklist_node_t *)p; #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we reserved space for one nodelist_node_t VALGRIND_CREATE_MEMPOOL(chunk, 0, false); VALGRIND_MEMPOOL_ALLOC(chunk, chunk, sizeof(*chunk)); VALGRIND_MAKE_MEM_DEFINED(chunk, sizeof(*chunk)); #endif chunk->chunk_next = chunk_list; chunk_list = chunk; p += sizeof(chunklist_node_t); // then enter nodes into nodelist for (size_t i = 0; i < new_chuck_size; i++) { nodelist_node_t* tmp = (nodelist_node_t*)(p + i * NODE_SIZE); #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we reserved space for one nodelist_node_t VALGRIND_CREATE_MEMPOOL(tmp, 0, false); VALGRIND_MEMPOOL_ALLOC(tmp, tmp, sizeof(*tmp)); VALGRIND_MAKE_MEM_UNDEFINED(tmp, sizeof(*tmp)); #endif #ifdef DEBUG_FREELIST tmp->canary[0] = canary_free[0]; tmp->canary[1] = canary_free[1]; tmp->canary[2] = canary_free[2]; tmp->canary[3] = sizeof(T); #endif tmp->next = freelist; freelist = tmp; } } // return first node of list tmp = freelist; freelist = tmp->next; change_obj((char *)(&(tmp->next)),true); #ifdef MULTI_THREADx pthread_mutex_unlock(&freelist_mutex); #endif #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we now have access to a chunk of size bytes VALGRIND_MEMPOOL_CHANGE(tmp, tmp, tmp, sizeof(T)); VALGRIND_MAKE_MEM_UNDEFINED(tmp, sizeof(T)); #endif #ifdef DEBUG_FREELIST assert(tmp->canary[0] == canary_free[0] && tmp->canary[1] == canary_free[1] && tmp->canary[2] == canary_free[2] && tmp->canary[3] == sizeof(T)); tmp->canary[0] = canary_used[0]; tmp->canary[1] = canary_used[1]; tmp->canary[2] = canary_used[2]; #endif nodecount++; return (void *)(&(tmp->next)); } void putback_node(void* p) { #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we keep access to a nodelist_node_t within the memory chunk VALGRIND_MEMPOOL_CHANGE(p, p, p, sizeof(nodelist_node_t)); VALGRIND_MAKE_MEM_NOACCESS(p, sizeof(T)); VALGRIND_MAKE_MEM_UNDEFINED(p, sizeof(nodelist_node_t)); #endif #ifdef MULTI_THREADx pthread_mutex_unlock(&freelist_mutex); #endif // putback to first node nodelist_node_t* tmp = (nodelist_node_t*)p; #ifdef DEBUG_FREELIST size_t min_size = sizeof(nodelist_node_t) - sizeof(void*); tmp = (nodelist_node_t*)((char*)p - min_size); assert(tmp->canary[0] == canary_used[0] && tmp->canary[1] == canary_used[1] && tmp->canary[2] == canary_used[2] && tmp->canary[3] == sizeof(T)); tmp->canary[0] = canary_free[0]; tmp->canary[1] = canary_free[1]; tmp->canary[2] = canary_free[2]; #endif tmp->next = freelist; freelist = tmp; change_obj((char *)p,0); nodecount--; if (nodecount == 0) { free_all_nodes(); } #ifdef MULTI_THREADx pthread_mutex_unlock(&freelist_mutex); #endif } }; #undef NODE_SIZE #endif simutrans-124.5/src/simutrans/tpl/freelist_tpl.h000066400000000000000000000121471520675460500220510ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_FREELIST_TPL_H #define TPL_FREELIST_TPL_H #include #include "../simmem.h" #include "../simdebug.h" #include "../simconst.h" #ifdef MULTI_THREAD #include "../utils/simthread.h" #endif // define USE_VALGRIND_MEMCHECK to make // valgrind aware of the freelist memory pool #ifdef USE_VALGRIND_MEMCHECK #include #endif /** * A template class for const sized memory pool * Must be a static member! Does not use exceptions */ class freelist_size_t { private: struct nodelist_node_t { #ifdef DEBUG_FREELIST char canary[4]; #endif nodelist_node_t* next; }; char canary_free[4] = "\xAA\x55\xAA"; char canary_used[4] = "\x55\xAA\x55"; size_t NODE_SIZE; size_t new_chunk_size; // next free node (or NULL) nodelist_node_t* freelist; // number of currently allocated node size_t nodecount; // list of all allocated memory nodelist_node_t* chunk_list; #ifdef MULTI_THREAD pthread_mutex_t freelist_mutex = PTHREAD_MUTEX_INITIALIZER;; #endif public: freelist_size_t(size_t size) : freelist(0), nodecount(0), chunk_list(0) { NODE_SIZE = (size + sizeof(nodelist_node_t) - sizeof(nodelist_node_t*)); new_chunk_size = ((32768 - sizeof(void*)) / NODE_SIZE); canary_free[3] = canary_used[3] = NODE_SIZE; } ~freelist_size_t() { free_all_nodes(); } void *gimme_node() { #ifdef MULTI_THREAD pthread_mutex_lock(&freelist_mutex); #endif nodelist_node_t *tmp; if (freelist == NULL) { char* p = (char*)xmalloc(new_chunk_size*NODE_SIZE + sizeof(nodelist_node_t)); #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we still cannot access the pool p VALGRIND_MAKE_MEM_NOACCESS(p, new_chunk_size*NODE_SIZE + sizeof(nodelist_node_t)); #endif // put the memory into the chunklist for free it nodelist_node_t* chunk = (nodelist_node_t *)p; #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we reserved space for one nodelist_node_t VALGRIND_CREATE_MEMPOOL(chunk, 0, false); VALGRIND_MEMPOOL_ALLOC(chunk, chunk, sizeof(*chunk)); VALGRIND_MAKE_MEM_UNDEFINED(chunk, sizeof(*chunk)); #endif chunk->next = chunk_list; chunk_list = chunk; p += sizeof(nodelist_node_t); // then enter nodes into nodelist for (size_t i = 0; i < new_chunk_size; i++) { nodelist_node_t* tmp = (nodelist_node_t*)(p + i*NODE_SIZE); #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we reserved space for one nodelist_node_t VALGRIND_CREATE_MEMPOOL(tmp, 0, false); VALGRIND_MEMPOOL_ALLOC(tmp, tmp, sizeof(*tmp)); VALGRIND_MAKE_MEM_UNDEFINED(tmp, sizeof(*tmp)); #endif #ifdef DEBUG_FREELIST tmp->canary[0] = canary_free[0]; tmp->canary[1] = canary_free[1]; tmp->canary[2] = canary_free[2]; tmp->canary[3] = canary_free[3]; #endif tmp->next = freelist; freelist = tmp; } } // return first node of list tmp = freelist; freelist = tmp->next; #ifdef DEBUG_FREELIST assert(tmp->canary[0] == canary_free[0] && tmp->canary[1] == canary_free[1] && tmp->canary[2] == canary_free[2] && tmp->canary[3] == canary_free[3]); tmp->canary[0] = canary_used[0]; tmp->canary[1] = canary_used[1]; tmp->canary[2] = canary_used[2]; #endif nodecount++; #ifdef MULTI_THREAD pthread_mutex_unlock(&freelist_mutex); #endif #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we now have access to a chunk of size bytes VALGRIND_MEMPOOL_CHANGE(tmp, tmp, tmp, NODE_SIZE); VALGRIND_MAKE_MEM_UNDEFINED(tmp, NODE_SIZE); #endif return (void *)(&(tmp->next)); } // clears all list memories void free_all_nodes() { while (chunk_list) { nodelist_node_t* p = chunk_list; chunk_list = chunk_list->next; // now release memory #ifdef USE_VALGRIND_MEMCHECK VALGRIND_DESTROY_MEMPOOL(p); #endif free(p); } freelist = 0; nodecount = 0; } void putback_node(void* p) { #ifdef USE_VALGRIND_MEMCHECK // tell valgrind that we keep access to a nodelist_node_t within the memory chunk VALGRIND_MEMPOOL_CHANGE(p, p, p, sizeof(nodelist_node_t)); VALGRIND_MAKE_MEM_NOACCESS(p, NODE_SIZE); VALGRIND_MAKE_MEM_UNDEFINED(p, sizeof(nodelist_node_t)); #endif #ifdef MULTI_THREAD pthread_mutex_lock(&freelist_mutex); #endif // putback to first node nodelist_node_t* tmp = (nodelist_node_t*)p; #ifdef DEBUG_FREELIST size_t min_size = sizeof(nodelist_node_t) - sizeof(void*); tmp = (nodelist_node_t*)((char*)p - min_size); assert(tmp->canary[0] == canary_used[0] && tmp->canary[1] == canary_used[1] && tmp->canary[2] == canary_used[2] && tmp->canary[3] == canary_used[3]); tmp->canary[0] = canary_free[0]; tmp->canary[1] = canary_free[1]; tmp->canary[2] = canary_free[2]; #endif tmp->next = freelist; freelist = tmp; nodecount--; if (nodecount == 0) { free_all_nodes(); } #ifdef MULTI_THREAD pthread_mutex_unlock(&freelist_mutex); #endif } }; template class freelist_tpl { private: freelist_size_t fli; public: freelist_tpl() : fli(sizeof(T)) {} T *gimme_node() { return (T *)fli.gimme_node(); } void putback_node(void* p) { return fli.putback_node(p); } void free_all_nodes() { fli.free_all_nodes(); } }; #endif simutrans-124.5/src/simutrans/tpl/hashtable_tpl.h000066400000000000000000000223261520675460500221670ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_HASHTABLE_TPL_H #define TPL_HASHTABLE_TPL_H #include "slist_tpl.h" #include "../macros.h" #define STHT_BAGSIZE 101 #define STHT_BAG_COUNTER_T uint8 /* * Generic hashtable, which maps key_t to value_t. key_t depended functions * like the hash generation is implemented by the third template parameter * hash_t (see ifc/hash_tpl.h) */ template class hashtable_tpl { protected: struct node_t { public: key_t key; value_t value; int operator == (const node_t &x) const { return key == x.key; } }; // the entires in the lists are sorted according to their keys slist_tpl bags[STHT_BAGSIZE]; uint32 count; /* * assigning hashtables seems also not sound */ private: hashtable_tpl(const hashtable_tpl&); hashtable_tpl& operator=( hashtable_tpl const&); public: hashtable_tpl() { count = 0; } public: STHT_BAG_COUNTER_T get_hash(const key_t key) const { return (STHT_BAG_COUNTER_T)(hash_t::hash(key) % STHT_BAGSIZE); } class iterator { friend class hashtable_tpl; public: typedef std::forward_iterator_tag iterator_category; typedef node_t value_type; typedef ptrdiff_t difference_type; typedef node_t* pointer; typedef node_t& reference; iterator() : bag_i(), bag_end(), node_i() {} iterator(slist_tpl* const bag_i, slist_tpl* const bag_end, typename slist_tpl::iterator const& node_i) : bag_i(bag_i), bag_end(bag_end), node_i(node_i) {} pointer operator ->() const { return &*node_i; } reference operator *() const { return *node_i; } iterator& operator ++() { if (++node_i == bag_i->end()) { for (;;) { if (++bag_i == bag_end) { node_i = typename slist_tpl::iterator(); break; } if (!bag_i->empty()) { node_i = bag_i->begin(); break; } } } return *this; } bool operator ==(iterator const& o) const { return bag_i == o.bag_i && node_i == o.node_i; } bool operator !=(iterator const& o) const { return !(*this == o); } private: slist_tpl* bag_i; slist_tpl* bag_end; typename slist_tpl::iterator node_i; }; /* Erase element at pos * pos is invalid after this method * An iterator pointing to the successor of the erased element is returned */ iterator erase(iterator old) { iterator pos(old); pos.bag_end = old.bag_end; pos.bag_i = old.bag_i; pos.node_i = old.bag_i->erase( old.node_i ); if( pos.node_i == pos.bag_i->end() ) { for (;;) { if (++pos.bag_i == pos.bag_end) { pos.node_i = typename slist_tpl::iterator(); break; } if (!pos.bag_i->empty()) { pos.node_i = pos.bag_i->begin(); break; } } } count --; return pos; } class const_iterator { public: typedef std::forward_iterator_tag iterator_category; typedef node_t value_type; typedef ptrdiff_t difference_type; typedef node_t const* pointer; typedef node_t const& reference; const_iterator() : bag_i(), bag_end(), node_i() {} const_iterator(slist_tpl const* const bag_i, slist_tpl const* const bag_end, typename slist_tpl::const_iterator const& node_i) : bag_i(bag_i), bag_end(bag_end), node_i(node_i) {} pointer operator ->() const { return &*node_i; } reference operator *() const { return *node_i; } const_iterator& operator ++() { if (++node_i == bag_i->end()) { for (;;) { if (++bag_i == bag_end) { node_i = typename slist_tpl::const_iterator(); break; } if (!bag_i->empty()) { node_i = bag_i->begin(); break; } } } return *this; } bool operator ==(const_iterator const& o) const { return bag_i == o.bag_i && node_i == o.node_i; } bool operator !=(const_iterator const& o) const { return !(*this == o); } private: slist_tpl const* bag_i; slist_tpl const* bag_end; typename slist_tpl::const_iterator node_i; }; iterator begin() { for (slist_tpl* i = bags; i != endof(bags); ++i) { if (!i->empty()) { return iterator(i, endof(bags), i->begin()); } } return end(); } iterator end() { return iterator(endof(bags), endof(bags), typename slist_tpl::iterator()); } const_iterator begin() const { for (slist_tpl const* i = bags; i != endof(bags); ++i) { if (!i->empty()) { return const_iterator(i, endof(bags), i->begin()); } } return end(); } const_iterator end() const { return const_iterator(endof(bags), endof(bags), typename slist_tpl::iterator()); } void clear() { for(STHT_BAG_COUNTER_T i=0; i faster retrieval (we only have to check half of the lists) const value_t &get(const key_t key) const { static value_t nix; for(auto const& node : bags[get_hash(key)]) { typename hash_t::diff_type diff = hash_t::comp(node.key, key); if( diff == 0 ) { return node.value; } if( diff > 0 ) { // not contained break; } } return nix; } // the elements are inserted with increasing key // => faster retrieval, but never ever change a key later!!! value_t *access(const key_t key) { slist_tpl& bag = bags[get_hash(key)]; for(auto & node : bag) { typename hash_t::diff_type diff = hash_t::comp(node.key, key); if( diff == 0 ) { return &node.value; } if( diff > 0 ) { // not contained break; } } return NULL; } /// Inserts a new value - failure if key exists in table bool put(const key_t key, value_t object) { slist_tpl& bag = bags[get_hash(key)]; /* Duplicate values are hard to debug, so better check here. * we also enter it sorted, saving lookout time for large lists ... */ for( typename slist_tpl::iterator iter = bag.begin(), end = bag.end(); iter != end; ++iter ) { typename hash_t::diff_type diff = hash_t::comp(iter->key, key); if( diff>0 ) { node_t n; n.key = key; n.value = object; bag.insert( iter, n ); count ++; return true; } if( diff == 0 ) { dbg->error( "hashtable_tpl::put", "Duplicate hash!" ); return false; } } // here only for empty lists or everything was smaller node_t n; n.key = key; n.value = object; bag.append( n ); count ++; return true; } // // Inserts a new instantiated value - failure, if key exists in table // mostly used with value_t = slist_tpl // bool put(const key_t key) { slist_tpl& bag = bags[get_hash(key)]; /* Duplicate values are hard to debug, so better check here. * we also enter it sorted, saving lookout time for large lists ... */ for( typename slist_tpl::iterator iter = bag.begin(), end = bag.end(); iter != end; ++iter ) { typename hash_t::diff_type diff = hash_t::comp(iter->key, key); if( diff>0 ) { iter = bag.insert( iter ); iter->key = key; count ++; return true; } if( diff == 0 ) { // already initialized return false; } } // here only for empty lists or everything was smaller bag.append(); bag.back().key = key; count ++; return true; } // // Insert or replace a value - if a value is replaced, the old value is // returned, otherwise a nullvalue. This may be useful if you need to delete it // afterwards. // value_t set(const key_t key, value_t object) { slist_tpl& bag = bags[get_hash(key)]; for( typename slist_tpl::iterator iter = bag.begin(), end = bag.end(); iter != end; ++iter ) { typename hash_t::diff_type diff = hash_t::comp(iter->key, key); if( diff == 0 ) { value_t value = iter->value; iter->value = object; return value; } if( diff > 0 ) { node_t node; node.key = key; node.value = object; bag.insert( iter, node ); count ++; return value_t(); } } // empty list or really last one ... node_t node; node.key = key; node.value = object; bag.append(node); count ++; return value_t(); } // Remove an entry - if the entry is not there, return a nullvalue // otherwise the value that was associated to the key. value_t remove(const key_t key) { slist_tpl& bag = bags[get_hash(key)]; for( typename slist_tpl::iterator iter = bag.begin(), end = bag.end(); iter != end; ++iter ) { typename hash_t::diff_type diff = hash_t::comp(iter->key, key); if( diff == 0 ) { value_t v = iter->value; bag.erase(iter); count --; return v; } if( diff > 0 ) { // not in list break; } } return value_t(); } value_t remove_first() { for(STHT_BAG_COUNTER_T i = 0; i < STHT_BAGSIZE; i++) { if( !bags[i].empty() ) { count --; return bags[i].remove_first().value; } } dbg->fatal( "hashtable_tpl::remove_first()", "Hashtable already empty!" ); return value_t(); } uint32 get_count() const { return count; } bool empty() const { return get_count()==0; } }; #endif simutrans-124.5/src/simutrans/tpl/inthashtable_tpl.h000066400000000000000000000024301520675460500226740ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_INTHASHTABLE_TPL_H #define TPL_INTHASHTABLE_TPL_H #include "hashtable_tpl.h" /** * Define type for differences of integers. * Default is int. * Obvious specializations for 64bit integers. */ template struct int_diff_type { typedef int diff_type; }; template<> struct int_diff_type { typedef sint64 diff_type; }; template<> struct int_diff_type { typedef sint64 diff_type; }; /** * Define the key characteristics for hashing integer types */ template class inthash_tpl { public: typedef typename int_diff_type::diff_type diff_type; static uint32 hash(const key_t key) { return (uint32)key; } static diff_type comp(key_t key1, key_t key2) { return key1 - key2; } }; /* * Ready to use class for hashing integer types. Note that key can be of any * integer type including enums. */ template class inthashtable_tpl : public hashtable_tpl > { public: inthashtable_tpl() : hashtable_tpl >() {} private: inthashtable_tpl(const inthashtable_tpl&); inthashtable_tpl& operator=( inthashtable_tpl const&); }; #endif simutrans-124.5/src/simutrans/tpl/minivec_tpl.h000066400000000000000000000073501520675460500216660ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_MINIVEC_TPL_H #define TPL_MINIVEC_TPL_H #include "../simdebug.h" #include "../simtypes.h" /** A template class for a simple vector type */ template class minivec_tpl { public: typedef const T* const_iterator; typedef T* iterator; minivec_tpl() : data(NULL), size(0), count(0) {} /** Construct a vector for cap elements */ explicit minivec_tpl(uint8 cap) : data(cap > 0 ? new T[cap] : NULL), size(cap), count(0) {} ~minivec_tpl() { delete [] data; } /** sets the vector to empty */ void clear() { count = 0; } /** * Resizes the maximum data that can be hold by this vector. * Existing entries are preserved, new_size must be big enough to hold them */ void resize(uint new_size) { if (new_size > 255) { dbg->fatal("minivec_tpl::resize()", "new size %u too large (>255).", new_size); } // not yet used, but resize may be called anyway if(size<=0) { size = new_size; data = new T[size]; return; } if (new_size <= size) return; // do nothing T* new_data = new T[new_size]; for (uint i = 0; i < count; i++) new_data[i] = data[i]; delete [] data; size = new_size; data = new_data; } /** * Checks if element elem is contained in vector. * Uses the == operator for comparison. */ bool is_contained(T elem) const { for (uint i = 0; i < count; i++) { if (data[i] == elem) return true; } return false; } /** * Appends the element at the end of the vector. * if out of space, extend it */ void append(T elem, uint8 extend = 1) { if (count >= size) { resize( count > 255-extend ? 255 : count+extend); } data[count++] = elem; } /** Checks if element is contained. Appends only new elements. */ bool append_unique(T elem) { if (is_contained(elem)) return false; append(elem); return true; } /** Removes element, if contained */ void remove(T elem) { uint i, j; for (i = j = 0; i < count; i++, j++) { if (data[i] == elem) { // skip this one j++; count--; } // maybe we copy too often ... if (j < size) { data[i] = data[j]; } } } /** Inserts data at a certain pos */ void insert_at(uint8 pos, T elem) { if (pos > count) { dbg->fatal("minivec_tpl::append()", "cannot insert at %i! Only %i elements.", pos, count); } if (pos < count) { if (count == size) resize(count + 1); for (uint i = count; i > pos; i--) data[i] = data[i - 1]; data[pos] = elem; count++; } else { append(elem, 1); } } /** Removes element at position */ bool remove_at(uint8 pos) { if (pos < count) { for (uint i = pos+1; i < count; i++) { data[i-1] = data[i]; } count--; return true; } return false; } T& operator [](uint8 i) { if (i >= count) dbg->fatal("minivec_tpl::[]", "index out of bounds: %i not in 0..%d", i, count - 1); return data[i]; } const T& operator [](uint8 i) const { if (i >= count) dbg->fatal("minivec_tpl::[]", "index out of bounds: %i not in 0..%d", i, count - 1); return data[i]; } T& back() { return data[count - 1]; } const T& back() const { return data[count - 1]; } iterator begin() { return data; } iterator end() { return data + count; } const_iterator begin() const { return data; } const_iterator end() const { return data + count; } /** Get the number of elements in the vector */ uint8 get_count() const { return count; } /** Get the capacity */ uint8 get_size() const { return size; } bool empty() const { return count == 0; } private: minivec_tpl(const minivec_tpl&); minivec_tpl& operator=( minivec_tpl const& other ); T* data; uint8 size; ///< Capacity uint8 count; ///< Number of elements in vector } GCC_PACKED; #endif simutrans-124.5/src/simutrans/tpl/plainstringhashtable_tpl.h000066400000000000000000000013371520675460500244410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_PLAINSTRINGHASHTABLE_TPL_H #define TPL_PLAINSTRINGHASHTABLE_TPL_H #include "stringhashtable_tpl.h" #include "../utils/plainstring.h" /** * Hashtable with plainstring keys. * * Keys will be copied when creating entries, * unlike stringhashtable_tpl */ template class plainstringhashtable_tpl : public hashtable_tpl { public: plainstringhashtable_tpl() : hashtable_tpl() {} private: plainstringhashtable_tpl(const plainstringhashtable_tpl&); plainstringhashtable_tpl& operator=( plainstringhashtable_tpl const&); }; #endif simutrans-124.5/src/simutrans/tpl/ptrhashtable_tpl.h000066400000000000000000000021721520675460500227120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_PTRHASHTABLE_TPL_H #define TPL_PTRHASHTABLE_TPL_H #include "hashtable_tpl.h" #include "../simtypes.h" /* * Define the key characteristics for hashing pointers. For hashing the * direct value is used. */ template class ptrhash_tpl { public: // define NO_INTPTR_T if it does not compile #ifndef NO_INTPTR_T typedef intptr_t cast_ptr_to_t; typedef intptr_t diff_type; #else typedef ptrdiff_t diff_type; typedef const char* cast_ptr_to_t; #endif static uint32 hash(const key_t key) { return (uint32)(size_t)key; } static diff_type comp(key_t key1, key_t key2) { return (cast_ptr_to_t)key1 - (cast_ptr_to_t)key2; } }; /** * Implements a hashtable with pointer keys */ template class ptrhashtable_tpl : public hashtable_tpl > { public: ptrhashtable_tpl() : hashtable_tpl >() {} private: ptrhashtable_tpl(const ptrhashtable_tpl&); ptrhashtable_tpl& operator=( ptrhashtable_tpl const&); }; #endif simutrans-124.5/src/simutrans/tpl/quickstone_tpl.h000066400000000000000000000133761520675460500224260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_QUICKSTONE_TPL_H #define TPL_QUICKSTONE_TPL_H #include "../simtypes.h" #include "../simdebug.h" /** * An implementation of the tombstone pointer checking method. * It uses a table of pointers and indices into that table to * implement the tombstone system. Unlike real tombstones, this * template reuses entries from the tombstone table, but it tries * to leave freed tombstones untouched as long as possible, to * detect most of the dangling pointers. * * This templates goal is to be efficient and fairly safe. */ template class quickstone_tpl { private: /** * Array of pointers. The first entry is always NULL! */ static T ** data; /** * Next entry to check */ static uint16 next; /** * Size of tombstone table */ static uint16 size; /** * The index in the table for this handle. * (only this variable is actually saved, since the rest is static!) */ uint16 entry; private: /** * Retrieves next free tombstone index */ static uint16 find_next() { uint16 i; // scan rest of array for( i=next; ifatal("quickstone::find_next()","no free index found (size=%i)",size); return 0; //dummy for compiler } else if (size >= 32768) { // max out on handles, don't overflow uint16 newsize = 65535; } else { newsize = 2*size; } // Move data to new extended array T ** newdata = new T* [newsize]; memcpy( newdata, data, sizeof(T*)*size ); for( uint16 i=size; i0; i++ ) { if( data[i] == 0 ) { entry = i; data[entry] = p; return; } } enlarge(); // repeat for( i=size-1; i>0; i++ ) { if( data[i] == 0 ) { entry = i; data[entry] = p; return; } } dbg->fatal( "quickstone_tpl(bool)", "No more handles!\nShould have already failed with enlarge!" ); } // creates handle with id, fails if already taken quickstone_tpl(T* p, uint16 id) { if(p) { if( id == 0 ) { dbg->fatal("quickstone::quickstone_tpl(T*,uint16)","wants to assign non-null pointer to null index"); } while( id >= size ) { enlarge(); } if( data[id]!=NULL && data[id]!=p ) { dbg->fatal("quickstone::quickstone_tpl(T*,uint16)","slot (%d) already taken", id); } entry = id; data[entry] = p; } else { if( id!=0 ) { dbg->fatal("quickstone::quickstone_tpl(T*,uint16)","wants to assign null pointer to non-null index"); } // all NULL pointers are mapped to entry 0 entry = 0; } } // returns true, if no handles left static bool is_exhausted() { if( size==65535 ) { // scan array for( uint16 i = 1; i cannot extent return true; } // can extent in any case => ok return false; } inline bool is_bound() const { return data[entry] != 0; } /** * Removes the object from the tombstone table - this affects all * handles to the object! */ T* detach() { T* p = data[entry]; data[entry] = 0; return p; } /** * Danger - use with care. * Useful to hand the underlying pointer to subsystems * that don't know about quickstones - but take care that such pointers * are never ever deleted or that by some means detach() is called * upon deletion, i.e. from the ~T() destructor!!! */ T* get_rep() const { return data[entry]; } /** * @return the index into the tombstone table. May be used as * an ID for the referenced object. */ uint16 get_id() const { return entry; } /** * Sets the current id: Needed to recreate stuff via network. * ATTENTION: This may be harmful. DO not use unless really really needed! */ void set_id(uint16 e) { entry=e; } /** * Overloaded dereference operator. With this, quickstones can * be used as if they were pointers. */ T* operator->() const { return data[entry]; } T& operator *() const { return *data[entry]; } bool operator== (const quickstone_tpl &other) const { return entry == other.entry; } bool operator!= (const quickstone_tpl &other) const { return entry != other.entry; } static uint16 get_size() { return size; } /** * For checking the consistency of handle allocation * among the server and the clients in network mode */ static uint16 get_next_check() { return next; } }; template T** quickstone_tpl::data = 0; template uint16 quickstone_tpl::next = 1; template uint16 quickstone_tpl::size = 0; #endif simutrans-124.5/src/simutrans/tpl/slist_tpl.h000066400000000000000000000226661520675460500214010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_SLIST_TPL_H #define TPL_SLIST_TPL_H #include #include #include "../dataobj/freelist.h" #include "../simdebug.h" #include // for ptrdiff_t #ifdef _MSC_VER #pragma warning(disable:4786) #endif /** * A template class for a single linked list. Insert() and append() * work in fixed time. Maintains a list of free nodes to reduce calls * to new and delete. * * Must NOT be used with things with copy constructor like button_t or std::string!!! */ template class slist_tpl { private: struct node_t { node_t(const T& data_, node_t* next_) : next(next_), data(data_) {} node_t(node_t* next_) : next(next_), data() {} void* operator new(size_t) { return freelist_t::gimme_node(sizeof(node_t)); } void operator delete(void* p) { freelist_t::putback_node(sizeof(node_t), p); } node_t* next; T data; }; node_t *head; node_t *tail; uint32 node_count; public: class const_iterator; /** * Iterator class: can be used to erase nodes and to modify nodes * Usage: @see utils/for.h */ class iterator { public: typedef std::forward_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef value_type* pointer; typedef value_type& reference; iterator() : ptr(), pred() {} pointer operator ->() const { return &ptr->data; } reference operator *() const { return ptr->data; } iterator& operator ++() { pred = ptr; ptr = ptr->next; return *this; } iterator operator ++(int) { iterator const old = *this; ++*this; return old; } bool operator ==(iterator const& o) const { return ptr == o.ptr; } bool operator !=(iterator const& o) const { return ptr != o.ptr; } private: iterator(node_t* ptr_, node_t* pred_) : ptr(ptr_), pred(pred_) {} node_t* ptr; node_t* pred; private: friend class slist_tpl; friend class const_iterator; }; /** * Iterator class: neither nodes nor the list can be modified * Usage: @see utils/for.h */ class const_iterator { public: typedef std::forward_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T const* pointer; typedef T const& reference; const_iterator() : ptr() {} const_iterator(const iterator& o) : ptr(o.ptr) {} pointer operator ->() const { return &ptr->data; } reference operator *() const { return ptr->data; } const_iterator& operator ++() { ptr = ptr->next; return *this; } const_iterator operator ++(int) { const_iterator const old = *this; ++*this; return old; } bool operator ==(const_iterator const& o) const { return ptr == o.ptr; } bool operator !=(const_iterator const& o) const { return ptr != o.ptr; } private: explicit const_iterator(node_t* ptr_) : ptr(ptr_) {} const node_t* ptr; private: friend class slist_tpl; }; /** * Creates a new empty list. */ slist_tpl() { head = 0; // empty list tail = 0; node_count = 0; } ~slist_tpl() { clear(); } /** * Inserts an element at the beginning of the list. */ void insert(const T& data) { node_t* tmp = new node_t(data, head); head = tmp; if( tail == NULL ) { tail = tmp; } node_count++; } /** * Inserts an element initialized by standard constructor * at the beginning of the lists * (avoid the use of copy constructor) */ void insert() { node_t* tmp = new node_t(head); head = tmp; if( tail == NULL ) { tail = tmp; } node_count++; } /** * Appends an element to the end of the list. */ void append(const T& data) { if (tail == 0) { insert(data); } else { node_t* tmp = new node_t(data, 0); tail->next = tmp; tail = tmp; node_count++; } } /** * Append an zero/empty element * mostly used for T=slist_tpl<...> */ void append() { if (tail == 0) { insert(); } else { node_t* tmp = new node_t(0); tail->next = tmp; tail = tmp; node_count++; } } /** * Appends an element to the end of the list. */ void append_unique(const T& data) { if (tail == 0) { insert(data); } else if( !is_contained(data) ) { node_t* tmp = new node_t(data, 0); tail->next = tmp; tail = tmp; node_count++; } } /** * Appends the nodes of another list * empties other list * -> no memory allocation involved */ void append_list(slist_tpl& other) { if (tail) { tail->next = other.head; } else { head = other.head; } if (other.tail) { tail = other.tail; } node_count += other.node_count; // empty other list other.tail = NULL; other.head = NULL; other.node_count = 0; } /** * Checks if the given element is already contained in the list. */ bool is_contained(const T &data) const { node_t *p = head; while(p != 0 && p->data != data) { p = p->next; } return p != 0; // is NULL when not found } /** * Removes an element from the list */ bool remove(const T &data) { if (empty()) { //MESSAGE("slist_tpl::remove()", "data not in list!"); return false; } if(head->data == data) { node_t *tmp = head->next; delete head; head = tmp; if(head == NULL) { tail = NULL; } } else { node_t *p = head; while(p->next != 0 && !(p->next->data == data)) { p = p->next; } if(p->next == 0) { //MESSAGE("slist_tpl::remove()", "data not in list!"); return false; } node_t *tmp = p->next->next; delete p->next; p->next = tmp; if(tmp == 0) { tail = p; } } node_count--; return true; } /** * Retrieves the first element from the list. This element is * deleted from the list. Useful for some queuing tasks. */ T remove_first() { if (empty()) { dbg->fatal("slist_tpl::remove_first()","List of <%s> is empty",typeid(T).name()); } T tmp = head->data; node_t *p = head; head = head->next; delete p; node_count--; if(head == 0) { // list is empty now tail = 0; } return tmp; } /** * Recycles all nodes. * Leaves the list empty. */ void clear() { node_t* p = head; while (p != NULL) { node_t* tmp = p; p = p->next; delete tmp; } head = 0; tail = 0; node_count = 0; } uint32 get_count() const { return node_count; } bool empty() const { return head == 0; } T& at(uint32 pos) const { if (pos >= node_count) { dbg->fatal("slist_tpl::at()", "<%s> index %d is out of bounds", typeid(T).name(), pos); } node_t* p = head; while (pos--) { p = p->next; } return p->data; } T& front() const { return head->data; } T& back() const { return tail->data; } iterator begin() { return iterator(head, NULL); } iterator end() { return iterator(NULL, tail); } const_iterator begin() const { return const_iterator(head); } const_iterator end() const { return const_iterator(NULL); } /* Erase element at pos * pos is invalid after this method * An iterator pointing to the successor of the erased element is returned */ iterator erase(iterator pos) { node_t* pred = pos.pred; node_t* succ = pos.ptr->next; if (pred == NULL) { head = succ; } else { pred->next = succ; } if (succ == NULL) { tail = pred; } delete pos.ptr; --node_count; return iterator(succ, pred); } /* Add x before pos * pos is invalid after this method * An iterator pointing to the new element is returned */ iterator insert(iterator pos, const T& x) { node_t* tmp = new node_t(x, pos.ptr); if (pos.pred == NULL) { head = tmp; } else { pos.pred->next = tmp; } if (pos.ptr == NULL) { tail = tmp; } ++node_count; return iterator(tmp, pos.pred); } /* Add an empty node * pos is invalid after this method * An iterator pointing to the new element is returned */ iterator insert(iterator pos) { node_t* tmp = new node_t(pos.ptr); if (pos.pred == NULL) { head = tmp; } else { pos.pred->next = tmp; } if (pos.ptr == NULL) { tail = tmp; } ++node_count; return iterator(tmp, pos.pred); } int index_of(T data) const { node_t *t = head; int index = 0; while(t && t->data != data) { t = t->next; index++; } return t ? index : -1; } /** * sorts list using specified comparator * returns true if list was changed */ bool sort( int (*compare)(const T &l, const T &r) ) { if( NULL == head || head == tail ) { return false; } bool needed_work = false; for( uint i=1; i < node_count; ++i ) { int changes = 0; if( compare( head->data, head->next->data ) > 0 ) { node_t * tmp = head; head = head->next; tmp->next = head->next; head->next = tmp; ++changes; if( head == tail ) { tail = tail->next; needed_work = true; break; } } for( node_t *node = head; node != tail && node->next != tail; node = node->next ) { if( compare( node->next->data, node->next->next->data ) > 0 ) { node_t * tmp = node->next; node->next = node->next->next; tmp->next = node->next->next; node->next->next = tmp; ++changes; if( node->next == tail ){ tail = tail->next; break; } } } if( changes == 0 ) { break; } needed_work = true; } return needed_work; } private: slist_tpl(const slist_tpl& slist_tpl); slist_tpl& operator=( slist_tpl const& other ); }; #endif simutrans-124.5/src/simutrans/tpl/sparse_tpl.h000066400000000000000000000116221520675460500215260ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_SPARSE_TPL_H #define TPL_SPARSE_TPL_H #include "../dataobj/koord.h" #include "../macros.h" #include "../simtypes.h" template class sparse_tpl; template void swap(sparse_tpl& a, sparse_tpl& b); /** * A template class for spares 2-dimensional arrays. * It's using Compressed Row Storage (CRS). * @see array2d_tpl */ template class sparse_tpl { private: koord size; T* data; uint16* col_ind; uint16* row_ptr; uint16 data_size; uint16 data_count; public: sparse_tpl( koord _size ) { size = _size; data_size = 0; data_count = 0; data = NULL; col_ind = NULL; row_ptr = new uint16[ size.y + 1]; for( uint16 i = 0; i < size.y + 1; i++ ) { row_ptr[i] = 0; } } ~sparse_tpl() { delete[] data; data = NULL; delete[] col_ind; col_ind = NULL; delete[] row_ptr; row_ptr = NULL; } void clear() { data_count = 0; for( uint16 i = 0; i < size.y + 1; i++ ) { row_ptr[i] = 0; } resize_data(0); } const koord& get_size() const { return size; } uint16 get_data_size() const { return data_size; } uint16 get_data_count() const { return data_count; } T get( koord pos ) const { if( pos.x >= 0 && pos.y >= 0 && pos.x < size.x && pos.y < size.y ) { return get_unsafe( pos ); } return 0; } T get( uint16 i, uint16 j ) const { return get(koord(i,j)); } /* * Access to the nonzero elements. Result is saved in pos and value! */ void get_nonzero( uint16 index, koord& pos, T& value ) const { if( index > data_count ) { pos = koord::invalid; value = T(0); return; } value = data[index]; pos = koord(col_ind[index], 0); while( row_ptr[ pos.y+1 ] <= index ) { pos.y++; } } void set( koord pos, T value ) { if( pos.x >= 0 && pos.y >= 0 && pos.x < size.x && pos.y < size.y ) { set_unsafe( pos, value ); } } void set( uint16 i, uint16 j, T value ) { set(koord(i,j),value); } private: T get_unsafe( koord pos ) const { uint16 index = pos_to_index( pos ); if( index < row_ptr[pos.y+1] && col_ind[index] == pos.x ) { return data[index]; } else { return 0; } } void set_unsafe( koord pos, T value ) { uint16 index = pos_to_index( pos ); if( index < row_ptr[pos.y+1] && col_ind[index] == pos.x ) { if( value == 0 ) { move_data(index+1, data_count, -1); for( uint16 i = pos.y+1; i < size.y+1; i++ ){ row_ptr[i]--; } data_count--; } else { data[index] = value; } } else { if( value == 0 ) { // Don't add 0 to data! return; } if( data_count == data_size ) { resize_data(data_size==0 ? 1 : 2*data_size); } move_data(index, data_count, 1); data[index] = value; col_ind[index] = pos.x; for( uint16 i = pos.y+1; i < size.y+1; i++ ){ row_ptr[i]++; } data_count++; } } /* * Moves the elements data[start_index]..data[end_index-1] to * data[start_index+offset]..data[end_index-1+offset] */ void move_data(uint16 start_index, uint16 end_index, sint8 offset) { uint16 num = end_index - start_index; if( offset < 0 ) { for( uint16 i=0; i 0 ) { for( uint16 i=num ; i>0 ; i-- ){ data[start_index+i+offset-1] = data[start_index+i-1]; col_ind[start_index+i+offset-1] = col_ind[start_index+i-1]; } } } void resize_data( uint16 new_size ) { if( new_size < data_count || new_size == data_size ) { // new_size is too small or equal our current size. return; } T* new_data; uint16* new_col_ind; if( new_size > 0 ) { new_data = new T[new_size]; new_col_ind = new uint16[new_size]; } else { new_data = NULL; new_col_ind = NULL; } if( data_size > 0 ) { for( uint16 i = 0; i < data_count; i++ ) { new_data[i] = data[i]; new_col_ind[i] = col_ind[i]; } delete[] data; delete[] col_ind; } data_size = new_size; data = new_data; col_ind = new_col_ind; } /* * Returns an index i * - to the pos (if pos already in array) * - to the index, where pos can be inserted. */ uint16 pos_to_index( koord pos ) const { uint16 row_start = row_ptr[ pos.y ]; uint16 row_end = row_ptr[ pos.y + 1 ]; for( uint16 i = row_start; i < row_end; i++ ) { if( col_ind[i] >= pos.x ) { return i; } } return row_end; } private: friend void swap<>(sparse_tpl& a, sparse_tpl& b); sparse_tpl(const sparse_tpl& other); sparse_tpl& operator=( sparse_tpl const& other ); }; template void swap(sparse_tpl& a, sparse_tpl& b) { sim::swap(a.size, b.size); sim::swap(a.data, b.data); sim::swap(a.col_ind, b.col_ind); sim::swap(a.row_ptr, b.row_ptr); sim::swap(a.data_size, b.data_size); sim::swap(a.data_count, b.data_count); } #endif simutrans-124.5/src/simutrans/tpl/stringhashtable_tpl.h000066400000000000000000000021551520675460500234140ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_STRINGHASHTABLE_TPL_H #define TPL_STRINGHASHTABLE_TPL_H #include "hashtable_tpl.h" #include /* * a template class which implements a hashtable with string keys */ /* * Define the key characteristics for hashing "const char *". */ class stringhash_t { public: typedef int diff_type; static uint32 hash(const char *key) { uint32 hash = 0; #if 1 for( sint8 i=16; i*key[0]!=0; i-- ) { hash += (uint8)(*key++); } #else while (*key != '\0') { hash = hash * 33 + *key++; } #endif return hash; } static diff_type comp(const char *key1, const char *key2) { return strcmp(key1, key2); } }; /* * Ready to use class for hashing strings. */ template class stringhashtable_tpl : public hashtable_tpl { public: stringhashtable_tpl() : hashtable_tpl() {} private: stringhashtable_tpl(const stringhashtable_tpl&); stringhashtable_tpl& operator=( stringhashtable_tpl const&); }; #endif simutrans-124.5/src/simutrans/tpl/vector_tpl.h000066400000000000000000000142721520675460500215370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_VECTOR_TPL_H #define TPL_VECTOR_TPL_H #include #include "../macros.h" #include "../simtypes.h" #include "../simdebug.h" template class vector_tpl; template inline void swap(vector_tpl& a, vector_tpl& b); /** A template class for a simple vector type */ template class vector_tpl { public: typedef const T *const_iterator; typedef T *iterator; /** Construct a vector for cap elements */ vector_tpl() : data(NULL), cap(0), count(0) {} explicit vector_tpl(const uint32 cap) : data(cap > 0 ? new T[cap] : NULL), cap(cap), count(0) {} vector_tpl(const vector_tpl& copy_from) : data( copy_from.cap > 0 ? new T[ copy_from.cap ] : 0 ), cap( copy_from.cap ), count( copy_from.count ) { for( uint32 i = 0; i < count; i++ ) { data[i] = copy_from.data[i]; } } ~vector_tpl() { delete[] data; } /** sets the vector to empty */ void clear() { count = 0; } /** * Increases the capacity of this vector to @p new_capacity. Existing entries are preserved. * If @p new_capacity is smaller than the current capacity, the vector is not modified. */ void reserve(uint32 new_capacity) { if (new_capacity <= cap) return; // do nothing T *new_data = new T[new_capacity]; if (cap>0) { for (uint32 i = 0; i < count; i++) { new_data[i] = data[i]; } delete [] data; } cap = new_capacity; data = new_data; } /** * Checks if element elem is contained in vector. * Uses the == operator for comparison. */ bool is_contained(const T& elem) const { for (uint32 i = 0; i < count; i++) { if (data[i] == elem) { return true; } } return false; } /** * Checks if element elem is contained in vector. * Uses the == operator for comparison. */ uint32 index_of(const T& elem) const { for (uint32 i = 0; i < count; i++) { if (data[i] == elem) { return i; } } assert(false); return 0xFFFFFFFFu; } void append(const T& elem) { if( count == cap ) { reserve(cap == 0 ? 1 : cap * 2); } data[count++] = elem; } /** * Checks if element is contained. Appends only new elements. * extend vector if necessary */ bool append_unique(const T& elem) { if (is_contained(elem)) { return false; } append(elem); return true; } /** insert data at a certain pos */ void insert_at(const uint32 pos, const T& elem) { if (pos < count) { if (count == cap) { reserve(cap == 0 ? 1 : cap * 2); } for (uint i = count; i > pos; i--) { data[i] = data[i - 1]; } data[pos] = elem; count++; } else { append(elem); } } /** * Insert `elem' with respect to ordering. */ template void insert_ordered(const T& elem, StrictWeakOrdering comp) { sint32 low = -1, high = count; while( high - low>1 ) { const sint32 mid = ((uint32) (low + high)) >> 1; T &mid_elem = data[mid]; if( comp(elem, mid_elem) ) { high = mid; } else { low = mid; } } insert_at(high, elem); } /** * Only insert `elem' if not already contained in this vector. * Respects the ordering and assumes the vector is ordered. * Returns NULL if insertion is successful; * otherwise return the address of the element in conflict */ template T* insert_unique_ordered(const T& elem, StrictWeakOrdering comp) { sint32 low = -1, high = count; while( high - low>1 ) { const sint32 mid = ((uint32) (low + high)) >> 1; T &mid_elem = data[mid]; if( elem==mid_elem ) { return &mid_elem; } else if( comp(elem, mid_elem) ) { high = mid; } else { low = mid; } } insert_at(high, elem); return NULL; } /** * Put the data at a certain position. * Possibly re-allocates vector (and hence creates default objects). * @param pos index * @param elem this element will be copied */ void store_at(const uint32 pos, const T& elem) { if (pos >= cap) { uint32 new_cap = cap == 0 ? 1 : cap * 2; while (pos >= new_cap) { new_cap *= 2; } reserve(new_cap); } data[pos] = elem; if (pos >= count) { count = pos + 1; } } /** removes element, if contained */ bool remove(const T& elem) { for (uint32 i = 0; i < count; i++) { if (data[i] == elem) { return remove_at(i); } } return false; } /** removes element at position */ bool remove_at(const uint32 pos) { assert(pos0); --count; return data[count]; } T& operator [](uint i) { if (i >= count) { dbg->fatal("vector_tpl::[]", "%s: index out of bounds: %i not in 0..%d", typeid(T).name(), i, count - 1); } return data[i]; } const T& operator [](uint i) const { if (i >= count) { dbg->fatal("vector_tpl::[]", "%s: index out of bounds: %i not in 0..%d", typeid(T).name(), i, count - 1); } return data[i]; } T& front() const { return data[0]; } T& back() const { return data[count - 1]; } iterator begin() { return data; } iterator end() { return data + count; } iterator erase(iterator i) { remove_at( i-data ); return i; } const_iterator begin() const { return data; } const_iterator end() const { return data + count; } /** Get the number of elements in the vector */ uint32 get_count() const { return count; } /** Get the capacity */ uint32 get_capacity() const { return cap; } bool empty() const { return count == 0; } private: T* data; uint32 cap; ///< Capacity uint32 count; ///< Number of elements in vector vector_tpl& operator=( vector_tpl const& other ) { vector_tpl tmp(other); swap(tmp, *this); return *this; } private: friend void swap<>(vector_tpl& a, vector_tpl& b); }; template void swap(vector_tpl& a, vector_tpl& b) { sim::swap(a.data, b.data); sim::swap(a.cap, b.cap); sim::swap(a.count, b.count); } /** * Clears vectors of the type vector_tpl * Deletes all objects pointed to by pointers in the vector */ template void clear_ptr_vector(vector_tpl& v) { for(auto const i : v) { delete i; } v.clear(); } #endif simutrans-124.5/src/simutrans/tpl/weighted_vector_tpl.h000066400000000000000000000232431520675460500234150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef TPL_WEIGHTED_VECTOR_TPL_H #define TPL_WEIGHTED_VECTOR_TPL_H #include #include #include "../macros.h" #include "../simdebug.h" template class weighted_vector_tpl; template void swap(weighted_vector_tpl&, weighted_vector_tpl&); /// A template class for a simple vector type. template class weighted_vector_tpl { private: struct nodestruct { // expands to T if T is memcpy-able, else compile error typename std::enable_if_t::value, T> data; uint32 weight; }; public: class const_iterator; class iterator { public: typedef std::forward_iterator_tag iterator_category; typedef std::ptrdiff_t difference_type; typedef T const* pointer; typedef T const& reference; typedef T value_type; T& operator *() const { return ptr->data; } iterator& operator ++() { ++ptr; return *this; } bool operator !=(const iterator& o) { return ptr != o.ptr; } private: explicit iterator(nodestruct* ptr_) : ptr(ptr_) {} nodestruct* ptr; private: friend class weighted_vector_tpl; friend class const_iterator; }; class const_iterator { public: typedef std::forward_iterator_tag iterator_category; typedef std::ptrdiff_t difference_type; typedef T const* pointer; typedef T const& reference; typedef T value_type; const_iterator(const iterator& o) : ptr(o.ptr) {} const T& operator *() const { return ptr->data; } const_iterator& operator ++() { ++ptr; return *this; } bool operator !=(const const_iterator& o) { return ptr != o.ptr; } private: explicit const_iterator(nodestruct* ptr_) : ptr(ptr_) {} const nodestruct* ptr; private: friend class weighted_vector_tpl; }; weighted_vector_tpl() : nodes(NULL), cap(0), count(0), total_weight(0) {} /// Construct a vector with a capacity for @p capacity elements explicit weighted_vector_tpl(uint32 capacity) { this->cap = capacity; nodes = (capacity > 0 ? new nodestruct[capacity] : NULL); count = 0; total_weight = 0; } ~weighted_vector_tpl() { delete [] nodes; } /// Clears all elements from this vector void clear() { count = 0; total_weight = 0; } /// Ensures that the capacity of this vector is at least @p new_capacity elements. /// Existing entries are preserved if the vector is re-allocated /// (but their address may change). void reserve(uint32 new_capacity) { if (new_capacity <= cap) return; // do nothing nodestruct *new_nodes = new nodestruct[new_capacity]; for (uint32 i = 0; i < count; i++) new_nodes[i] = nodes[i]; delete [] nodes; nodes = new_nodes; cap = new_capacity; } /** * Checks if element elem is contained in vector. * Uses the == operator for comparison. */ bool is_contained(T elem) const { for (uint32 i = 0; i < count; i++) { if (nodes[i].data == elem) return true; } return false; } /** * Returns the index of @p elem. in this vector. * Uses the == operator for comparison. * @param elem must be contained in this vector. */ template uint32 index_of(U elem) const { for (uint32 i = 0; i < count; i++) { if (nodes[i].data == elem) return i; } dbg->fatal("weighted_vector_tpl::index_of()", "not contained" ); } /** * Appends the element at the end of the vector. * Extend if necessary. */ bool append(T elem, uint32 weight) { #ifdef IGNORE_ZERO_WEIGHT if (weight == 0) { // ignore unused entries ... return false; } #endif if( count == cap ) { reserve(cap == 0 ? 1 : cap * 2); } nodes[count].data = elem; nodes[count].weight = total_weight; count++; total_weight += weight; return true; } /** * Append (@p elem, @p weight) to this vector, if not contained. */ bool append_unique(T elem, uint32 weight) { return is_contained(elem) || append(elem, weight); } /** inserts data at a certain pos */ bool insert_at(uint32 pos, T elem, uint32 weight) { #ifdef IGNORE_ZERO_WEIGHT if (weight == 0) { // ignore unused entries ... return false; } #endif if (pos < count) { if( count==cap ) { reserve(cap == 0 ? 1 : cap * 2); } for (uint32 i = count; i > pos; i--) { nodes[i].data = nodes[i - 1].data; nodes[i].weight = nodes[i - 1].weight + weight; } nodes[pos].data = elem; total_weight += weight; count++; return true; } else { return append(elem, weight); } } /** * Insert `elem' with respect to ordering. */ template void insert_ordered(const T& elem, uint32 weight, StrictWeakOrdering comp) { if( count==cap ) { reserve(cap == 0 ? 1 : cap * 2); } sint32 high = count, low = -1; while( high-low>1 ) { const sint32 mid = ((uint32)(high + low)) >> 1; if( comp(elem, nodes[mid].data) ) { high = mid; } else { low = mid; } } insert_at(high, elem, weight); } /** * Only insert `elem' if not already contained in this vector. * Respects the ordering and assumes the vector is ordered. * Returns NULL if insertion is successful; * Otherwise return the address of the element in conflict. */ template T* insert_unique_ordered(const T& elem, uint32 weight, StrictWeakOrdering comp) { if( count==cap ) { reserve(cap == 0 ? 1 : cap * 2); } sint32 high = count, low = -1; while( high-low>1 ) { const sint32 mid = ((uint32)(high + low)) >> 1; T &mid_elem = nodes[mid].data; if( elem==mid_elem ) { return &mid_elem; } else if( comp(elem, mid_elem) ) { high = mid; } else { low = mid; } } insert_at(high, elem, weight); return NULL; } /** * Update the weights of all elements. The new weight of each element is * retrieved from get_weight(). */ template void update_weights(U& get_weight) { uint32 sum = 0; for (nodestruct* i = nodes, * const end = i + count; i != end; ++i) { i->weight = sum; sum += get_weight(i->data); } total_weight = sum; } /** removes element, if contained */ bool remove(T elem) { for (uint32 i = 0; i < count; i++) { if (nodes[i].data == elem) return remove_at(i); } return false; } /** removes element at position */ bool remove_at(uint32 pos) { if (pos >= count) return false; // get the change in the weight; must check, if it isn't the last element const uint32 diff_weight = ( pos + 1 < count ? nodes[pos + 1].weight : total_weight ) - nodes[pos].weight; for (uint32 i = pos; i < count - 1; i++) { nodes[i].data = nodes[i + 1].data; nodes[i].weight = nodes[i + 1].weight - diff_weight; } count--; total_weight -= diff_weight; return true; } T& pop_back() { assert(count>0); --count; total_weight = nodes[count].weight; return nodes[count].data; } T& operator [](uint32 i) { if (i >= count) dbg->fatal("weighted_vector_tpl::get()", "index out of bounds: %i not in 0..%d", i, count - 1); return nodes[i].data; } const T& operator [](uint32 i) const { if (i >= count) dbg->fatal("weighted_vector_tpl::get()", "index out of bounds: %i not in 0..%d", i, count - 1); return nodes[i].data; } T& front() { return nodes[0].data; } /** returns the weight at a position */ uint32 weight_at(uint32 pos) const { return (pos < count) ? nodes[pos].weight : total_weight + 1; } /** Accesses the element at position i by weight */ T& at_weight(const uint32 target_weight) const { if (target_weight > total_weight) { dbg->fatal("weighted_vector_tpl::at_weight()", "weight out of bounds: %i not in 0..%d", target_weight, total_weight); } #if 0 // that is the main idea (but slower, the more entries are in the list) uint32 pos; uint32 current_weight = 0; for (pos = 0; pos < count - 1 && current_weight + nodes[pos + 1].weight < target_weight; pos++) { current_weight += nodes[pos + 1].weight; } #else // ... and that the much faster binary search uint32 diff = 1; sint8 counter = 0; // now make sure, diff is 2^n while (diff <= count) { diff <<= 1; counter++; } diff >>= 1; uint pos = diff; // now search while (counter-- > 0) { diff = (diff + 1) >> 1; if (pos < count && weight_at(pos) <= target_weight) { if (weight_at(pos + 1) > target_weight) break; pos += diff; } else { pos -= diff; } } #endif //DBG_DEBUG("at_weight()", "target_weight=%i found_weight=%i at pos %i", target_weight, weight_at(pos), pos); return nodes[pos].data; } /** Gets the number of elements in the vector */ uint32 get_count() const { return count; } /** Gets the capacity */ uint32 get_capacity() const { return cap; } /** Gets the total weight */ uint32 get_sum_weight() const { return total_weight; } bool empty() const { return count == 0; } iterator begin() { return iterator(nodes); } iterator end() { return iterator(nodes + count); } const_iterator begin() const { return const_iterator(nodes); } const_iterator end() const { return const_iterator(nodes + count); } private: nodestruct* nodes; uint32 cap; ///< Capacity uint32 count; ///< Number of elements in vector uint32 total_weight; ///< Sum of all weights weighted_vector_tpl(const weighted_vector_tpl& other); weighted_vector_tpl& operator=( weighted_vector_tpl const& other ); private: friend void swap<>(weighted_vector_tpl&, weighted_vector_tpl&); }; template void swap(weighted_vector_tpl& a, weighted_vector_tpl& b) { sim::swap(a.nodes, b.nodes); sim::swap(a.cap, b.cap); sim::swap(a.count, b.count); sim::swap(a.total_weight, b.total_weight); } #endif simutrans-124.5/src/simutrans/utils/000077500000000000000000000000001520675460500175405ustar00rootroot00000000000000simutrans-124.5/src/simutrans/utils/cbuffer.cc000066400000000000000000000325641520675460500214750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include #include #include "cbuffer.h" #include "simstring.h" #include "../macros.h" #include "../simdebug.h" #include "../simtypes.h" cbuffer_t::cbuffer_t() : capacity(256), size(0), buf(new char[capacity]) { buf[0] = '\0'; } cbuffer_t::~cbuffer_t() { free(); } void cbuffer_t::clear() { buf[0] = '\0'; size = 0; } cbuffer_t::cbuffer_t (const cbuffer_t& cbx) { copy( cbx ); } cbuffer_t::cbuffer_t(const char *txt) { capacity = (txt ? strlen(txt)+1 : 256); buf = new char[capacity]; size = 0; if (txt) { size = capacity-1; strcpy(buf, txt); } } cbuffer_t& cbuffer_t::operator= (const cbuffer_t& cbx) { if ( this != &cbx ) { free(); copy( cbx ); } return *this; } void cbuffer_t::copy (const cbuffer_t& cbx) { capacity = cbx.capacity; size = cbx.size; buf = new char[capacity]; memcpy( buf, cbx.buf, size + 1 ); } void cbuffer_t::free () { delete [] buf; } void cbuffer_t::append(const char * text) { size_t const n = strlen( text ); extend( n ); memcpy( buf + size, text, n + 1); size += n; } void cbuffer_t::append(const char* text, size_t maxchars) { size_t const n = min( strlen( text ), maxchars ); extend( n ); memcpy( buf + size, text, n ); size += n; buf[size] = '\0'; // Ensure buffer is null terminated } void cbuffer_t::append(double n,int decimals) { char tmp[128]; number_to_string( tmp, n, decimals ); append(tmp); } void cbuffer_t::append_money(double money) { char tmp[128]; money_to_string(tmp, money, true); append(tmp); } void cbuffer_t::rtrim() { // remove all trailing whichspaces, i.e. ASCII <= 32 while (size > 0 && buf[size - 1] <= ' ') { size--; } } const char* cbuffer_t::get_str() const { return buf; } /** * Checks the format specifier. * No flags, precision modifiers, n, * are allowed. * @param format points to percent sign * @param position positional parameter (if larger than zero) * @param mask format mask for checking ('?' in case of error) * @returns whether specifier is correct */ static bool check_format_specifier(const char*& format, int& position, char& mask) { mask = '?'; // skip % assert(*format == '%'); format++; // %[n$][flags][width][.precision][length]specifier int n = atoi(format); // skip numbers while(*format && ('0'<=*format && *format<='9') ) format++; // positional argument? position = *format == '$' ? n : -1; // skip $ if (*format == '$') format++; // no flags // skip numbers (width) while(*format && ('0'<=*format && *format<='9') ) format++; // skip . if (*format == '.') format++; // no precision modifiers allowed // skip numbers (precision) while(*format && ('0'<=*format && *format<='9') ) format++; // now the specifier static const char* all_types = "cdiouxXeEfFgGaAsp%"; // no n static const char* all_masks = "ciiiiiiffffffffsp%"; if (*format) { if (const char* type = strchr(all_types, *format)) { mask = *(all_masks + (type-all_types)); if (mask == '%') { // previous char has to be % as well if ( *(format-1) != '%') { mask = '?'; } } // skip specifier format++; } } return mask != '?'; } /** * Parses string @p format, and puts type identifiers into @p typemask. * If an error occurs, an error message is printed into @p error. * Checks for positional parameters: either all or no parameter have to be positional as eg %1$d. * If positional parameter %[n]$ is specified then all up to n have to be present in the string as well. * Treats all integer parameters %i %u %d etc the same. * Ignores positional width parameters as *[n]. * Positional parameters start with 1. * * @param format format string * @param typemask pointer to array of length @p max_params * @param max_params length of typemask * @param error receives error message */ static void get_format_mask(const char* format, char *typemask, int max_params, cbuffer_t &error) { MEMZERON(typemask, max_params); // count found parameters int found = 0; bool positional = false; while(format && *format) { // skip until percent sign while(*format && *format!='%') format++; if (*format == 0) { break; } int pos; char mask; if (check_format_specifier(format, pos, mask)) { // mixed positional / non-positional arguments? if (found>0 && (positional ^ (pos > 0) )) { goto err_mix_pos_nopos; } // has positional parameters? // allowed range 1 .. max_params if (found == 0) { positional = pos > 0; } if (pos > max_params) { error.printf("Positional parameter too large (pos = %d). ", pos); return; } if (mask != '%') { // ignore %% // no positional parameter // positional parameter is 1-based, indexing is 0-based int idx = pos > 0 ? pos-1 : found; // found valid format typemask[idx] = mask; found++; } } else { error.printf("Format specifier %d contains invalid characters. ", found); return; } } // check whether positional parameters are continuous if (positional) { for(uint16 i=0; i 0) { // broken master string ?! // cannot contain single % signs, use %% instead dbg->warning("cbuffer_t::check_format_strings", "Broken master string '%s': %s", master, (const char*) buf); return false; } // no format specifiers or only %%? Replace %% by %, otherwise leave translated alone. bool has_format = false; for(uint i=0; (master_tm[i]) && (i 0 && repaired_p) { // try to repair repair_format_string(translated, *repaired_p); // and check again buf.clear(); get_format_mask(*repaired_p, translated_tm, lengthof(translated_tm), buf); if (buf.len() == 0) { // acceptable now dbg->warning("cbuffer_t::check_format_strings", "Repaired broken translation string '%.15s' of '%s'.", translated, master); } } if (buf.len() > 0) { // broken translated string dbg->warning("cbuffer_t::check_format_strings", "Broken translation string '%.15s' of '%s': %s", translated, master, (const char*) buf); return false; } // check for consistency for(uint i=0; (translated_tm[i]) && (iwarning("cbuffer_t::check_format_strings", "Translation string '%.15s' has more parameters than master string '%s'", translated, master); return false; } else if (master_tm[i]!=translated_tm[i]) { // wrong type dbg->warning("cbuffer_t::check_format_strings", "Parameter %d in translation string '%.15s' of '%s' has to be of type '%%%c' instead of '%%%c', Typemasks: Master = %s vs Translated = %s", i+1, translated, master, master_tm[i], translated_tm[i], master_tm,translated_tm); return false; } } return true; } /* this is a vsnprintf which can always process positional parameters * like "%2$i: %1$s" * WARNING: posix specification as well as this function always assumes that * ALL parameter are either positional or not!!! * * When numbered argument specifications are used, specifying the Nth argument requires that all the * leading arguments, from the first to the (N-1)th, are specified in the format string. * * ATTENTION: no support for positional precision (which are not used in simutrans anyway! */ static int my_vsnprintf(char *buf, size_t n, const char* fmt, va_list ap ) { #if defined _MSC_FULL_VER && _MSC_FULL_VER >= 140050727 && !defined __WXWINCE__ // this MSC function can handle positional parameters since 2008 return _vsprintf_p(buf, n, fmt, ap); #else #if !defined(HAVE_UNIX98_PRINTF) // this function cannot handle positional parameters if( const char *c=strstr( fmt, "%1$" ) ) { // but they are requested here ... // our routine can only handle max. 9 parameters char pos[14]; static char format_string[256]; char *cfmt = format_string; static char buffer[16000]; // the longest possible buffer ... int count = 0; for( ; c && count<9; count++ ) { sprintf( pos, "%%%i$", count+1 ); c = strstr( fmt, pos ); if( c ) { // extend format string, using 1 as mark between strings if( count ) { *cfmt++ = '\01'; } *cfmt++ = '%'; // now find the end c += 3; int len = strspn( c, "+-0123456789 #.hlI" )+1; while( len-->0 ) { *cfmt++ = *c++; } } } *cfmt = 0; // now printf into buffer int result = vsnprintf( buffer, 16000, format_string, ap ); if( result<0 || result>=16000 ) { *buf = 0; return 0; } // check the length result += strlen(fmt); if( (size_t)result > n ) { // increase the size please ... return result; } // we have enough size: copy everything together *buf = 0; char *cbuf = buf; cfmt = const_cast(fmt); // cast is save, as the string is not modified while( *cfmt!=0 ) { while( *cfmt!='%' && *cfmt ) { *cbuf++ = *cfmt++; } if( *cfmt==0 ) { break; } // get the nth argument char *carg = buffer; int current = cfmt[1]-'1'; for( int j=0; j= capacity - size ) { unsigned int by_amount = min_free_space + 1 - (capacity - size); if( by_amount < capacity ) { // At least double the size of the buffer. by_amount = capacity; } unsigned int new_capacity = capacity + by_amount; char *new_buf = new char [new_capacity]; memcpy( new_buf, buf, capacity ); delete [] buf; buf = new_buf; capacity = new_capacity; } } // remove whitespace and unprinatable characters void cbuffer_t::trim() { while (size > 0) { const unsigned char c = buf[size - 1]; if (c >= 33) { break; } size--; } } simutrans-124.5/src/simutrans/utils/cbuffer.h000066400000000000000000000045011520675460500213250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_CBUFFER_H #define UTILS_CBUFFER_H #include #include /** * A character buffer. Main operation is 'append' */ class cbuffer_t { private: size_t capacity; /** * Number of characters without(!) trailing '\0' */ size_t size; char *buf; /** * Implementation for copy constructor and copy assignment operator */ void copy( const cbuffer_t& cbx ); /** * Implementation for destructor and copy assignment operator */ void free(); public: /** * Number of characters without(!) trailing '\0' */ int len() const { return size; } /** * Creates a new cbuffer with capacity cap */ cbuffer_t(); ~cbuffer_t(); /** * Copy constructor */ cbuffer_t(const cbuffer_t& cbx); cbuffer_t(const char *); /** * Copy assignment operator */ cbuffer_t& operator= (const cbuffer_t& cbx); /** * Clears the buffer */ void clear(); /** * Appends text. Buffer will be extended if it does not have enough capacity. */ void append(const char * text); /** * Appends text, at most n characters worth. Buffer will be extended if needed. * maxchars should NOT include the null at the end of the string! * (e.g. it should be equivalent to the output of strlen()) */ void append(const char* text, size_t maxchars); /** * Removes all trailing whitespaces */ void rtrim(); /** * Return contents of buffer */ const char* get_str() const; /** * Appends a number. Buffer will be extended if it does not have enough capacity. */ void append(double n, int precision); /** * appends formatted money string */ void append_money(double money); /* Append formatted text to the buffer */ void printf(const char *fmt, ...); void vprintf(const char *fmt, va_list args ); /* enlarge the buffer if needed (i.e. size+by_amount larger than capacity) */ void extend(unsigned int by_amount); // removes trailing whitespaces void trim(); /** * Automagic conversion to a const char* for backwards compatibility */ operator const char *() const {return buf;} /// checks whether format specifiers in @p translated match those in @p master static bool check_and_repair_format_strings(const char* master, const char* translated, char **repaired = NULL); }; #endif simutrans-124.5/src/simutrans/utils/checklist.cc000066400000000000000000000026201520675460500220200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "checklist.h" #include "../network/memory_rw.h" #include "../utils/cbuffer.h" checklist_t::checklist_t() : hash(0), random_seed(0), halt_entry(0), line_entry(0), convoy_entry(0) { } checklist_t::checklist_t(const uint32 &hash) : hash(hash), random_seed(0), halt_entry(0), line_entry(0), convoy_entry(0) { } checklist_t::checklist_t(uint32 _random_seed, uint16 _halt_entry, uint16 _line_entry, uint16 _convoy_entry) : hash(0), random_seed(_random_seed), halt_entry(_halt_entry), line_entry(_line_entry), convoy_entry(_convoy_entry) { } bool checklist_t::operator==(const checklist_t& other) const { return hash == other.hash && random_seed==other.random_seed && halt_entry==other.halt_entry && line_entry==other.line_entry && convoy_entry==other.convoy_entry; } bool checklist_t::operator!=(const checklist_t& other) const { return !(*this==other); } void checklist_t::rdwr(memory_rw_t *buffer) { buffer->rdwr_long(hash); buffer->rdwr_long(random_seed); buffer->rdwr_short(halt_entry); buffer->rdwr_short(line_entry); buffer->rdwr_short(convoy_entry); } void checklist_t::print(cbuffer_t &buffer, const char *entity) const { buffer.printf("%s=[adler32=%08x rand=%u halt=%u line=%u cnvy=%u] ", entity, hash, random_seed, halt_entry, line_entry, convoy_entry); } simutrans-124.5/src/simutrans/utils/checklist.h000066400000000000000000000013141520675460500216610ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_CHECKLIST_H #define UTILS_CHECKLIST_H #include "../simtypes.h" class memory_rw_t; class cbuffer_t; struct checklist_t { public: checklist_t(); explicit checklist_t(const uint32 &hash); checklist_t(uint32 _random_seed, uint16 _halt_entry, uint16 _line_entry, uint16 _convoy_entry); bool operator==(const checklist_t &other) const; bool operator!=(const checklist_t &other) const; void rdwr(memory_rw_t *buffer); void print(cbuffer_t &buffer, const char *entity) const; private: uint32 hash; uint32 random_seed; uint16 halt_entry; uint16 line_entry; uint16 convoy_entry; }; #endif simutrans-124.5/src/simutrans/utils/csv.cc000066400000000000000000000132021520675460500206400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include "csv.h" #include "simstring.h" #include "../macros.h" #include "../simtypes.h" CSV_t::CSV_t (const char *csvdata) : lines(1), offset(0), first_field(true) { const char* pos = csvdata; cbuffer_t tmp; int ret; do { // Puts the field pointed to by pos into the cbuffer_t tmp // (While ret is not failure case or end of file - check these!) tmp.clear(); ret = (int)decode( pos, tmp ); switch ( ret ) { case -1: new_line(); pos++; break; case -2: // finished break; case -4: case -5: case -9: // Encountered invalid data, stop parsing break; default: // Encode contents of tmp and add them to our contents pos += ret; add_field( tmp.get_str() ); } } while( ret > -2 ); } int CSV_t::get_next_field (cbuffer_t& buf) { // Sanity check - offset must be less than or equal to the length of contents string if ( strlen( contents.get_str() ) < offset ) { return -5; } // Start at contents + offset (position of next field) const char *pos = contents.get_str() + offset; // Decode into cbuffer_t int ret = (int)decode( pos, buf ); if ( ret >= 0 ) { // Return value is amount to increase offset by // (Number of characters consumed) offset += ret; } return ret; } bool CSV_t::next_line () { // Call get_next_field until EOL, then move forward to next field past newline int r; cbuffer_t tmp; while ( (r = get_next_field( tmp )) >= 0 ) {}; if ( r != -1 ) { // Return code from get_next_field() indicates that there are // no more lines, return false return false; } offset++; return true; } void CSV_t::reset () { offset = 0; } const char *CSV_t::get_str () const { return contents.get_str(); } int CSV_t::get_lines () const { return lines; } void CSV_t::add_field (int newfield) { char tmp[32]; sprintf( tmp, "%i", newfield ); add_field( tmp ); } void CSV_t::add_field (const char *newfield) { // Add comma if this isn't the first field on a line if ( !first_field ) { // If offset is pointing to the EOF character // (e.g. if offset == strlen(contents)) // then advance it by one to point at the start of // the new field // If we don't do this after a call to add_field() // the next call to get_next_field() would always // return an empty string, as offset would point to // the comma preceding the last field, rather than // the last field itself if ( offset == strlen( contents.get_str() ) ) { offset++; } contents.append( "," ); } // CSV encode string and add to buffer encode( newfield, contents ); first_field = false; } void CSV_t::new_line () { // Add newline to buffer contents.append( "\n" ); lines++; first_field = true; } int CSV_t::encode( const char *text, cbuffer_t& output ) { char *n; bool wrap = false; // not empty? if( *text ) { // If text contains a comma, quote, or a line break, needs to be wrapped in quote marks wrap = strpbrk( text, ",\"\r\n" )!=0; // If text has leading or trailing spaces, must be wrapped in quotes if( *text == ' ' || text[strlen( text )-1] == ' ' ) { wrap = true; } } int len = output.len(); if( wrap ) { char* cpy = strdup(text); char* tmp = cpy; output.append( "\"" ); // Replace all '"' with '""' while ( (n = strchr( tmp, '"' )) != NULL ) { *n = '\0'; output.append( tmp ); output.append( "\"\"" ); tmp = n + 1; } output.append( tmp ); output.append( "\"" ); tmp = NULL; free(cpy); } else { output.append( text ); } return output.len() - len; } int CSV_t::decode (const char *start, cbuffer_t& output) { const char *s = start; switch ( *s ) { case '"': { const char *c = s + 1; const char *n; // Start of quoted field, may contain commas and newlines while( 1 ) { n = strchr( c, '"' ); if( n == NULL || *n == '\0' ) { // No matching '"' - malformed field return -9; } switch( *(n + 1) ) { case '"': // Copy everything up to and including the first quote output.append( c, (n - c) + 1 ); c = n + 2; break; case ',': // Copy everything up to the quotes before the comma // Needs to be +1 since output.append puts a '\0' on the last position copied output.append( c, (n - c) ); // Move offset to character after ',' return (int)((n - s) + 2); case '\0': case '\n': case '\r': // Copy everything up to the quotes before the newline/null output.append( c, (n - c) ); // Move offset to last character return (int)((n - s) + 1); default: // Single '"' in the middle of a field - malformed field return -9; } } // one will never reach here ... assert(0); } case '\r': case '\n': return -1; case '\0': return -2; default: { const char *newline = strchr( s, '\n' ); const char *comma = strchr( s, ',' ); if( newline == NULL && comma == NULL ) { // Copy to '\0' + leave offset on '\0' char // Copy all to '\0' into buffer output.append( s, strlen( s ) ); // Offset to point at '\0' return (int)strlen( s ); } if( newline != NULL && (comma == NULL || newline < comma) ) { // Copy up to newline + leave offset on newline char output.append( s, (newline - s) ); return (int)(newline - s); } if( comma != NULL && (newline == NULL || comma < newline) ) { // Copy up to comma + leave offset at next char after output.append( s, (comma - s) ); return (int)((comma - s) + 1); } return -9; } } } simutrans-124.5/src/simutrans/utils/csv.h000066400000000000000000000065311520675460500205110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_CSV_H #define UTILS_CSV_H #include "cbuffer.h" /* Provides functionality for building a buffer containing CSV encoded data. This is done in a very simple way, building up the block of data field-by-field and line-by-line - this is not intended as a way of representing data for editing! This class is NOT thread safe! Writing: Each string added will be CSV encoded and may contain commas, quotation marks and newlines. The end of a line of data should be indicated by calling the new_line() method. Each new field is added using the add_field() method. There is a constructor which takes a char array containing CSV formatted data to parse, which can then be read as normal. Reading: get_next_field(), next_line() and reset() are used to parse each field in order. Example: Create a new, blank, CSV_t Use the add_field() and add_line() functions to build up the data Then finally read its contents with get_str() Example: CSV_t csv(); csv.add_field("Field1 Header"); csv.add_field("Field2 Header"); csv.new_line(); csv.add_field("field1 data"); csv.add_field("field2, data"); printf(csv.get_str()); // Output would be: Field1 Header,Field2 Header field1 data,"field2, data" */ class CSV_t { // The raw data itself (stored encoded) cbuffer_t contents; // Count of number of lines of CSV data // Note: This is not the same as the number of newlines in the file // since CSV fields can contain encoded newlines int lines; // Offset into cbuffer for current item size_t offset; bool first_field; /* * Escape the input text so it is appropriate to make up a single CSV field * Writes output into the supplied cbuffer_t (appending to the end) * Return length of output string */ int encode( const char *, cbuffer_t& ); /* * Parse the next field from the CSV formatted data pointed to by field * Returns the number of characters consumed from input string * this may be more than the length of output, as it includes CSV formatting * stripped during decoding * Returns -1 for end of line (call next_line()) * Returns -2 for end of data * Returns -9 for corrupt input data (unable to parse any further) */ int decode( const char *, cbuffer_t& ); public: /* Constructors */ CSV_t() : lines(1), offset(0), first_field(true) {} CSV_t( const char * ); /* Methods for reading */ /* * Parse the next field of the current line * Returns -4 if contents are NULL * Returns -5 if offset exceeds contents size * Else returns either the error code output of decode() * or the length of the data consumed from the input string * Field is appended to the cbuffer_t supplied */ int get_next_field( cbuffer_t& ); /* * Move onto the next line * Return true on success, false if there are no more lines */ bool next_line(); /* * Reset parse position to start of data */ void reset(); /* * Return CSV formatted contents */ const char *get_str() const; /* * Return number of lines of CSV data */ int get_lines() const; /* Methods for writing */ /* * Add a field to the current line */ void add_field(const char *); /* * Add a field to the current line (number) */ void add_field(int); /* * Terminate a line of CSV data, and move to the next line */ void new_line(); }; #endif simutrans-124.5/src/simutrans/utils/fetchopt.cc000066400000000000000000000022731520675460500216670ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "fetchopt.h" Fetchopt_t::Fetchopt_t(int argc, char **argv, const char *optstring) { optarg = NULL; optind = 1; optstr = optstring; ac = argc; av = argv; pos = 1; } char *Fetchopt_t::get_optarg() { return optarg; } int Fetchopt_t::get_optind() { return optind; } int Fetchopt_t::next() { optarg = NULL; if (optind >= ac || av[optind][0] != '-') { return -1; } int optchar = av[optind][pos]; const char *offset = strchr(optstr, optchar); if (offset == NULL || optchar == ':') { // Invalid option return '?'; } if (*(offset+1) == ':') { // Option with argument if (av[optind][pos+1] == '\0') { // Use next argument for option's argument if (ac < optind+2) { // Missing argument return '?'; } else { optarg = av[optind+1]; optind += 2; } } else { // Use rest of current argument for option's argument optarg = av[optind]+pos+1; optind++; } pos = 1; return optchar; } else { // Simple option pos++; if (av[optind][pos] == '\0') { // Next argument pos = 1; optind++; } return optchar; } } simutrans-124.5/src/simutrans/utils/fetchopt.h000066400000000000000000000055261520675460500215350ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_FETCHOPT_H #define UTILS_FETCHOPT_H /** This class can be used to parse unix-style single character command line options. It is intended to provide a similar interface to the POSIX getopt() function. The constructor takes the number of arguments (typically the argc value), the array of arguments and an options string describing the options that should be searched for. The value of the options string is constructed from the single-character options flags you wish to search for. A colon ':' following the option indicates that the option should take an argument. It is an error if an option is indicated as having an argument and no argument is found. E.g. the options string "a:bc" indicates that three flags should be searched for: -a (which takes an argument), -b and -c (which do not). The next() method will return the character representing the next option found. When no further options are found it will return -1. If an error condition is encountered the character '?' is returned. The get_optarg() method returns the argument value of the current option (or NULL if the current option does not take an argument). When all option arguments have been parsed the value of get_optind() will be the index into the argv array of the next non-option argument. A return value of '?' indicates an error, program usage ought to be printed if this is encountered. Example: // Parsing the following command line: // mycommand -a override -bc somecommand Fetchopt_t fetchopt(argc, argv, "a:bc"); const char default_arg_a[] = "default"; char *arg_a = default_arg_a; bool flag_b = true; bool flag_c = false; int ch; while ((ch = fetchopt.next()) != -1) { switch (ch) { case 'a': arg_a = fetchopt.get_optarg(); break; case 'b': flag_b = false; break; case 'c': flag_c = true; break; case '?': case 'h': default: usage(); } } // Values will be: // arg_a = "override" // flag_b = false // flag_c i = true // fetchopt.get_optind() = 4 // argv[fetchopt.get_optind()] = "somecommand" */ class Fetchopt_t { const char *optstr; // Options definition string int ac; // argc char **av; // argv char *optarg; // Current option's argument string int optind; // Index of current option in argv int pos; // Position of option within current argument public: Fetchopt_t(int, char **, const char *optstring); char *get_optarg(); int get_optind(); int next(); }; #endif simutrans-124.5/src/simutrans/utils/int_math.h000066400000000000000000000017551520675460500215240ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_INT_MATH_H #define UTILS_INT_MATH_H #include "../simtypes.h" inline int log2(uint64 n) { #define S(k) do { if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; } } while(false) int i = -(n == 0); S(32); S(16); S(8); S(4); S(2); S(1); return i; #undef S } inline int log2(uint32 n) { #define S(k) do { if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; } } while (false) int i = -(n == 0); S(16); S(8); S(4); S(2); S(1); return i; #undef S } inline int log2(uint16 n) { #define S(k) do { if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; } } while(false) int i = -(n == 0); S(8); S(4); S(2); S(1); return i; #undef S } inline int log10(uint64 n) { #define S(k, m) do { if (n >= UINT64_C(m)) { i += k; n /= UINT64_C(m); } } while(false) int i = -(n == 0); S(16,10000000000000000); S(8,100000000); S(4,10000); S(2,100); S(1,10); return i; #undef S } #endif simutrans-124.5/src/simutrans/utils/log.cc000066400000000000000000000250561520675460500206400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #ifdef SYSLOG #include #endif #include "log.h" #include "../simdebug.h" #include "../sys/simsys.h" #ifdef MAKEOBJ //debuglevel is global variable # define dr_fopen fopen #else # ifdef NETTOOL # define debuglevel (0) # define dr_fopen fopen # else # define debuglevel (env_t::verbose_debug) // for display # include "../gui/messagebox.h" # include "../display/simgraph.h" # include "../gui/simwin.h" # include "../dataobj/environment.h" # endif #endif #ifdef __ANDROID__ # include # include "cbuffer.h" # define LOG_TAG "com.simutrans" #endif /** * writes a message into the log. */ void log_t::pakset(const char* who, const char* format, ...) { // never spam the Android buffer #if !defined(__ANDROID__) && !defined (MAKEOBJ) && !defined(NETTOOL) if (env_t::pakset_debug) { va_list argptr; va_start(argptr, format); if (log) { /* only log when a log */ fprintf(log, "%s", who); /* is already open */ vfprintf(log, format, argptr); fprintf(log, "\n"); if (force_flush) { fflush(log); } } va_end(argptr); va_start(argptr, format); if (tee) { /* only log when a log */ fprintf(tee, "%s", who); /* is already open */ vfprintf(tee, format, argptr); fprintf(tee, "\n"); } va_end(argptr); #ifdef SYSLOG va_start(argptr, format); if (syslog) { // Replace with dynamic memory allocation char buffer[4096]; sprintf(buffer, "%s%s", who, format); vsyslog(LOG_INFO, buffer, argptr); } va_end(argptr); #endif } #else (void)who; (void)format; #endif } /** * writes a debug message into the log. */ void log_t::debug(const char *who, const char *format, ...) { if(log_debug && debuglevel >= log_t::LEVEL_DEBUG) { va_list argptr; va_start(argptr, format); if( log ) { /* only log when a log */ fprintf(log ,"Debug: %s:\t",who); /* is already open */ vfprintf(log, format, argptr); fprintf(log,"\n"); if( force_flush ) { fflush(log); } } va_end(argptr); va_start(argptr, format); if( tee ) { /* only log when a log */ fprintf(tee, "Debug: %s:\t",who); /* is already open */ vfprintf(tee, format, argptr); fprintf(tee,"\n"); } va_end(argptr); #ifdef SYSLOG va_start( argptr, format ); if ( syslog ) { // Replace with dynamic memory allocation char buffer[4096]; sprintf( buffer, "Debug: %s\t%s", who, format ); vsyslog( LOG_DEBUG, buffer, argptr ); } va_end( argptr ); #endif #ifdef __ANDROID__ va_start(argptr, format); cbuffer_t buffer; buffer.printf("Debug: %s\t%s", who, format); __android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, buffer, argptr); va_end( argptr ); #endif } } /** * writes a message into the log. */ void log_t::message(const char *who, const char *format, ...) { if(debuglevel >= log_t::LEVEL_MSG) { va_list argptr; va_start(argptr, format); if( log ) { /* only log when a log */ fprintf(log ,"Message: %s:\t",who); /* is already open */ vfprintf(log, format, argptr); fprintf(log,"\n"); if( force_flush ) { fflush(log); } } va_end(argptr); va_start(argptr, format); if( tee ) { /* only log when a log */ fprintf(tee, "Message: %s:\t",who); /* is already open */ vfprintf(tee, format, argptr); fprintf(tee,"\n"); } va_end(argptr); #ifdef SYSLOG va_start( argptr, format ); if ( syslog ) { // Replace with dynamic memory allocation char buffer[4096]; sprintf( buffer, "Message: %s\t%s", who, format ); vsyslog( LOG_INFO, buffer, argptr ); } va_end( argptr ); #endif #ifdef __ANDROID__ va_start(argptr, format); cbuffer_t buffer; buffer.printf("Message: %s\t%s", who, format); __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, buffer, argptr); va_end( argptr ); #endif } } /** * writes a warning into the log. */ void log_t::warning(const char *who, const char *format, ...) { if(debuglevel >= log_t::LEVEL_WARN) { va_list argptr; va_start(argptr, format); if( log ) { /* only log when a log */ fprintf(log ,"Warning: %s:\t",who); /* is already open */ vfprintf(log, format, argptr); fprintf(log,"\n"); if( force_flush ) { fflush(log); } } va_end(argptr); va_start(argptr, format); if( tee ) { /* only log when a log */ fprintf(tee, "Warning: %s:\t",who); /* is already open */ vfprintf(tee, format, argptr); fprintf(tee,"\n"); } va_end(argptr); #ifdef SYSLOG va_start( argptr, format ); if ( syslog ) { // Replace with dynamic memory allocation char buffer[4096]; sprintf( buffer, "Warning: %s\t%s", who, format ); vsyslog( LOG_WARNING, buffer, argptr ); } va_end( argptr ); #endif #ifdef __ANDROID__ va_start(argptr, format); cbuffer_t buffer; buffer.printf("Debug: %s\t%s", who, format); __android_log_vprint(ANDROID_LOG_WARN, LOG_TAG, buffer, argptr); va_end( argptr ); #endif } } /** * writes an error into the log. */ void log_t::error(const char *who, const char *format, ...) { if(debuglevel >= log_t::LEVEL_ERROR) { va_list argptr; va_start(argptr, format); if( log ) { /* only log when a log */ fprintf(log ,"ERROR: %s:\t",who); /* is already open */ vfprintf(log, format, argptr); fprintf(log,"\n"); if( force_flush ) { fflush(log); } fprintf(log ,"For help with this error or to file a bug report please see the Simutrans forum:\n"); fprintf(log ,"https://forum.simutrans.com\n"); } va_end(argptr); va_start(argptr, format); if( tee ) { /* only log when a log */ fprintf(tee, "ERROR: %s:\t",who); /* is already open */ vfprintf(tee, format, argptr); fprintf(tee,"\n"); fprintf(tee ,"For help with this error or to file a bug report please see the Simutrans forum:\n"); fprintf(tee ,"https://forum.simutrans.com\n"); } va_end(argptr); #ifdef SYSLOG va_start( argptr, format ); if ( syslog ) { // Replace with dynamic memory allocation char buffer[4096]; sprintf( buffer, "ERROR: %s\t%s", who, format ); vsyslog( LOG_ERR, buffer, argptr ); } va_end( argptr ); #endif #ifdef __ANDROID__ va_start(argptr, format); cbuffer_t buffer; buffer.printf("ERROR: %s\t%s", who, format); __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, buffer, argptr); va_end( argptr ); #endif } } /** * writes an error into the log, aborts the program. */ void log_t::fatal(const char* who, const char* format, ...) { va_list argptr; va_start(argptr, format); static char formatbuffer[512]; sprintf(formatbuffer, "FATAL ERROR: %s - %s\n" "Aborting program execution ...\n" "\n" "For help with this error or to file a bug report please see the Simutrans forum at\n" "https://forum.simutrans.com\n", who, format); static char buffer[8192]; vsprintf(buffer, formatbuffer, argptr); va_end(argptr); custom_fatal(buffer); } void log_t::custom_fatal(char *buffer) { if( log ) { fputs( buffer, log ); if ( force_flush ) { fflush( log ); } } if( tee && log!=tee ) { fputs( buffer, tee ); } #ifdef SYSLOG if ( syslog ) { ::syslog( LOG_ERR, buffer ); } #endif if ( tee == NULL && log == NULL && tag == NULL ) { fputs( buffer, stderr ); } #if defined MAKEOBJ exit(EXIT_FAILURE); #elif defined NETTOOL // no display available puts( buffer ); exit(1); #else env_t::verbose_debug = log_t::LEVEL_FATAL; // no more window concerning messages if (gfx->is_display_init()) { // show notification destroy_all_win( true ); strcat( buffer, "PRESS ANY KEY\n" ); fatal_news* sel = new fatal_news(buffer); const scr_size screen = gfx->get_screen_size(); scr_coord xy( screen.w/2 - sel->get_windowsize().w/2, screen.h/2 - sel->get_windowsize().h/2 ); event_t ev; create_win( xy, sel, w_info, magic_none ); while(win_is_top(sel)) { // do not move, do not close it! dr_sleep(50); dr_prepare_flush(); sel->draw( xy, sel->get_windowsize() ); dr_flush(); display_poll_event(&ev); // main window resized check_pos_win(&ev,true); if (IS_KEYDOWN(&ev)) { break; } } } else { // use OS means, if there dr_fatal_notify(buffer); } abort(); #endif } void log_t::vmessage(const char *what, const char *who, const char *format, va_list args ) { if(debuglevel >= LEVEL_ERROR) { va_list args2; va_copy(args2, args); if( log ) { /* only log when a log */ fprintf(log ,"%s: %s:\t", what, who); /* is already open */ vfprintf(log, format, args); fprintf(log,"\n"); if( force_flush ) { fflush(log); } } if( tee ) { /* only log when a log */ fprintf(tee,"%s: %s:\t", what, who); /* is already open */ vfprintf(tee, format, args2); fprintf(tee,"\n"); } va_end(args2); } } // create a logfile for log_debug=true log_t::log_t( const char *logfilename, bool force_flush, bool log_debug, bool log_console, const char *greeting, const char* syslogtag ) : log(NULL), tee(NULL), force_flush(force_flush), // if true will always flush when an entry is written to the log log_debug(log_debug), tag(NULL) #ifdef SYSLOG , syslog(false) #endif { if(logfilename == NULL) { log = NULL; /* not a log */ tee = NULL; } else if(strcmp(logfilename,"stdio") == 0) { log = stdout; tee = NULL; } else if(strcmp(logfilename,"stderr") == 0) { log = stderr; tee = NULL; #ifdef SYSLOG } else if( strcmp( logfilename, "syslog" ) == 0 ) { syslog = true; if ( syslogtag ) { tag = syslogtag; // Set up syslog openlog( tag, LOG_PID, LOG_DAEMON ); } log = NULL; tee = NULL; #endif } else { log = dr_fopen(logfilename,"wb"); if(log == NULL) { fprintf(stderr,"log_t::log_t: can't open file '%s' for writing\n", logfilename); } tee = stderr; } if (!log_console) { tee = NULL; } if( greeting ) { if( log ) { fputs( greeting, log ); // message("log_t::log_t","Starting logging to %s", logfilename); } if( tee ) { fputs( greeting, tee ); } #ifdef SYSLOG if ( syslog ) { ::syslog( LOG_NOTICE, greeting ); } #else (void)syslogtag; #endif } } void log_t::close() { message("log_t::~log_t","stop logging, closing log file"); if( log ) { fclose(log); log = NULL; } } // close all logs during cleanup log_t::~log_t() { if( log ) { close(); } } simutrans-124.5/src/simutrans/utils/log.h000066400000000000000000000041641520675460500204770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_LOG_H #define UTILS_LOG_H #include #include #include #include "../simtypes.h" /** * Logging facility */ class log_t { public: enum level_t { LEVEL_FATAL = 0, LEVEL_ERROR = 1, LEVEL_WARN = 2, LEVEL_MSG = 3, LEVEL_DEBUG = 4 }; private: /** * Primary log file. */ FILE *log; /** * Secondary log file, currently fixed to stderr */ FILE * tee; bool force_flush; /** * Logging level - include debug messages ? */ bool log_debug; /** * Syslog tag */ const char* tag; #ifdef SYSLOG /** * Log to syslog? */ bool syslog; #endif public: /** * writes a pakset content message into the log. */ void pakset(const char* who, const char* format, ...); /** * writes a debug message into the log. */ void debug(const char *who, const char *format, ...); /** * writes a message into the log. */ void message(const char *who, const char *format, ...); /** * writes a warning into the log. */ void warning(const char *who, const char *format, ...); /** * writes an error into the log. */ void error(const char *who, const char *format, ...); /** * writes an error into the log, aborts the program. */ void NORETURN fatal(const char* who, const char* format, ...); /** * writes an error into the log, aborts the program, use own messsgae. */ void NORETURN custom_fatal(char* error_str); /** * writes an error into the log, aborts the program. */ void NORETURN custom_fatal(const char* error_str); /** * writes to log using va_lists */ void vmessage(const char *what, const char *who, const char *format, va_list args ); void close(); /** * @param logname * @param fource_flush * @param log_debug * @param log_console * @param greeting * @param syslogtag only used with syslog */ log_t(const char* logname, bool force_flush, bool log_debug, bool log_console, const char* greeting, const char* syslogtag=NULL); ~log_t(); }; #if defined(MAKEOBJ) extern log_t::level_t debuglevel; #endif #endif simutrans-124.5/src/simutrans/utils/plainstring.h000066400000000000000000000026201520675460500222430ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_PLAINSTRING_H #define UTILS_PLAINSTRING_H #include #include "../simtypes.h" class plainstring { public: plainstring() : str_(NULL) {} plainstring(char const* const s) : str_(copy_string(s)) {} plainstring(const plainstring &other) : str_(copy_string(other.c_str())) {} ~plainstring() { delete [] str_; } plainstring& operator =(char const* const o) { char* const s = copy_string(o); delete [] str_; str_ = s; return *this; } plainstring& operator =(plainstring const& o) { return *this = o.str_; } char const* c_str() const { return str_; } operator char const*() const { return str_; } operator char*() { return str_; } bool operator ==(const plainstring o) const { return *this==o.c_str(); } bool operator !=(const plainstring o) const { return !(*this == o.c_str()); } bool operator ==(char const* const o) const { return str_ && o ? std::strcmp(str_, o) == 0 : str_ == o; } bool operator !=(char const* const o) const { return !(*this == o); } private: static char* copy_string(char const* const s) { if (s) { size_t const n = std::strlen(s) + 1; return static_cast(std::memcpy(new char[n], s, n)); } else { return 0; } } char* str_; }; void free(plainstring const&) = delete; #endif simutrans-124.5/src/simutrans/utils/searchfolder.cc000066400000000000000000000154711520675460500225200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #ifndef _WIN32 # include # include # include #else # ifndef NOMINMAX # define NOMINMAX # endif # include # include #endif #include "../simdebug.h" #include "../simmem.h" #include "../simtypes.h" #include "simstring.h" #include "searchfolder.h" /* * * Description: * If filepath ends with a slash, search for all files in the * directory having the given extension. * If filepath does not end with a slash and it also doesn't contain * a dot after the last slash, then append extension to filepath and * search for it. * Otherwise searches directly for filepath. * * No wildcards please! */ void searchfolder_t::add_entry(const std::string &path, const char *entry, const bool prepend) { const size_t entry_len = strlen(entry); char *c = MALLOCN(char, path.length() + entry_len + 1); if(prepend) { sprintf(c,"%s%s",path.c_str(),entry); } else{ sprintf(c,"%s",entry); } files.append(c); } void searchfolder_t::clear_list() { for(char* const i : files) { free(i); } files.clear(); } int searchfolder_t::search(const std::string &filepath, const std::string &extension, const search_flags_t search_flags, const int max_depth ) { clear_list(); return prepare_search(filepath, extension, search_flags, max_depth); } int searchfolder_t::prepare_search(const std::string &filepath, const std::string &extension, const search_flags_t search_flags, const int max_depth ) { std::string path(filepath); std::string name; std::string ext; #ifdef _WIN32 // since we assume hardcoded path are using / we need to correct this for windows for( uint i=0; i 0 ) ) { search_path(path + entry_name + '/', name, ext, search_flags, max_depth - 1); } } delete[] entry_name; } while(_wfindnext(hfind, &entry) == 0 ); _findclose(hfind); #else assert(path.empty() || path[path.length()-1] == '/'); lookfor = path + "."; if (DIR* const dir = opendir(lookfor.c_str())) { int dir_fd = dirfd(dir); if( dir_fd == -1 ) { goto close_dir; } lookfor = name + ext; while (dirent const* const entry = readdir(dir)) { if(entry->d_name[0]!='.' || (entry->d_name[1]!='.' && entry->d_name[1]!=0)) { bool is_dir = false; if( entry->d_type == DT_DIR ) { is_dir = true; } else if( entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK ) { struct stat st; if( fstatat(dir_fd, entry->d_name, &st, 0) == -1 ) { continue; } is_dir = S_ISDIR(st.st_mode); } if( is_dir && (search_flags&SF_NOADDONS) && !STRICMP(entry->d_name, "addons") ) { continue; } if (only_directories) { if( is_dir ) { add_entry(path, entry->d_name, prepend_path); } } else if (filename_matches_pattern(entry->d_name, lookfor.c_str())) { add_entry(path, entry->d_name, prepend_path); } if( is_dir && max_depth > 0 ) { search_path(path + entry->d_name + '/', name, ext, search_flags, max_depth - 1); } } } close_dir: closedir(dir); } #endif } #ifdef _WIN32 std::string searchfolder_t::complete(const std::string &filepath_raw, const std::string &extension) { std::string filepath(filepath_raw); // since we assume hardcoded path are using / we need to correct this for windows for( uint i=0; i #include "../tpl/vector_tpl.h" /** * Searches a disk folder for files matching certain restrictions. */ class searchfolder_t { public: enum search_flags_t : uint8 { SF_NONE = 0, SF_ONLYDIRS = 1 << 0, ///< Extra restriction, will only consider directory entries. If not specified, will search both file and directory entries. SF_PREPEND_PATH = 1 << 1, ///< Will force prepending the base path to the output on each entry. SF_NOADDONS = 1 << 2 ///< If set, subdrectories named "addons" will not be searched. }; public: ~searchfolder_t(); /** * Executes the requested file search. It will look in subdirectories recursively when a max_depth is given. * * @param filepath Base path to search in. * @param extension Extension of files to search for. Input a empty string to not enforce the restriction. * * @param only_directories Extra restriction, will only consider directory entries. * @param prepend_path Will force prepending the base path to the output on each entry. * * @param flags * @param max_depth Maximum depth of recursive search (0=search only the directory given by @p filepath). * @returns Number of files that matched the search parameters. */ int search(const std::string &filepath, const std::string &extension, search_flags_t flags = SF_PREPEND_PATH, const int max_depth = 0); /** * Appends the extension to the file name if it's not a directory * @param filepath Path to the file * @param extension File name extension to append if the conditions are meet */ static std::string complete(const std::string &filepath, const std::string &extension); /** * Function results will be accessible only in a const iterator fashion. */ typedef vector_tpl::const_iterator const_iterator; /** * Gets the first entry iterator. */ const_iterator begin() const { return files.begin(); } /** * Gets the last entry iterator. */ const_iterator end() const { return files.end(); } private: /** * Make the preparations for the search and then call search_path to do the actual search. * * @param filepath Base path to search in. (This path can have either / or \ as deliminator on windows. * @param extension Extension of files to search for. Input a empty string to not enforce the restriction. * * @param only_directories Extra restriction, will only consider directory entries. * @param prepend_path Will force prepending the base path to the output on each entry. * * @param search_flag * @param max_depth Maximum depth of recursive search. */ int prepare_search(const std::string &filepath, const std::string &extension, search_flags_t search_flags, const int max_depth = 0); /** * Real implementation of the search. It will call itself recursively to search in subdirectories when a max_depth is given. * * @param path Path to search in. (This path can have either / or \ as deliminator on windows. * @param name Name of file to search. It will be "*" if not searching for an specific file. * @param ext Extension of files to search for. * * @param only_directories Extra restriction, will only consider directory entries. * @param prepend_path Will force prepending the base path to the output on each entry. * * @param search_flag * @param max_depth Maximum depth of recursive search. */ void search_path(const std::string path, const std::string name, const std::string ext, search_flags_t search_flags, const int max_depth = 0 ); /** * We store the result of the search on this list */ vector_tpl files; /** * Adds one entry to the list. * @param path Qualified path to the directory of the entry. * @param entry Filename of the file to add. * @param prepend Add the full path to the file or just the file name. */ void add_entry(const std::string &path, const char *entry, const bool prepend ); /** * Checks if @p fn matches @p pattern, case insensitive. * @param pattern may start with an * as a wildcard that matches 0 or more characters. */ bool filename_matches_pattern(const char *fn, const char *pattern); /** * Clears the search results. */ void clear_list(); }; ENUM_BITSET(searchfolder_t::search_flags_t); #endif simutrans-124.5/src/simutrans/utils/sha1.cc000066400000000000000000000201741520675460500207070ustar00rootroot00000000000000/* * sha1.cpp * * Copyright (C) 1998, 2009 * Paul E. Jones * All Rights Reserved. * ***************************************************************************** * $Id: sha1.cpp 12 2009-06-22 19:34:25Z paulej $ ***************************************************************************** * * Description: * This class implements the Secure Hashing Standard as defined * in FIPS PUB 180-1 published April 17, 1995. * * The Secure Hashing Standard, which uses the Secure Hashing * Algorithm (SHA), produces a 160-bit message digest for a * given data stream. In theory, it is highly improbable that * two messages will produce the same message digest. Therefore, * this algorithm can serve as a means of providing a "fingerprint" * for a message. * * Caveats: * SHA-1 is designed to work with messages less than 2^64 bits long. * Although SHA-1 allows a message digest to be generated for * messages of any number of bits less than 2^64, this implementation * only works with messages with a length that is a multiple of 8 * bits. * */ #include #include "sha1.h" /* * Reset * * Description: * This function will initialize the sha1 class member variables * in preparation for computing a new message digest. * * Parameters: * None. * * Returns: * Nothing. * * Comments: * */ void SHA1::Reset() { Length_Low = 0; Length_High = 0; Message_Block_Index = 0; H[0] = 0x67452301; H[1] = 0xEFCDAB89; H[2] = 0x98BADCFE; H[3] = 0x10325476; H[4] = 0xC3D2E1F0; Computed = false; Corrupted = false; } /* * Result * * Description: * This function will return the 160-bit message digest into the * array provided. * * Parameters: * message_digest_array: [out] * This is an array of five uint32 integers which will be filled * with the message digest that has been computed. * * Returns: * True if successful, false if it failed. * * Comments: * */ bool SHA1::Result(sha1_hash_t &result_digest) { if (Corrupted) { return false; } if (!Computed) { PadMessage(); Computed = true; } #ifdef SIM_BIG_ENDIAN uint32 H_little_endian[5]; for(int i = 0; i < 5; i++) { H_little_endian[i] = endian( H[i] ); } result_digest = sha1_hash_t((const uint8 *)H_little_endian); #else result_digest = sha1_hash_t((const uint8 *)H); #endif return true; } /* * Input * * Description: * This function accepts an array of octets as the next portion of * the message. * * Parameters: * message_array: [in] * An array of characters representing the next portion of the * message. * * Returns: * Nothing. * * Comments: * */ void SHA1::Input( const char *message_array, uint32 length) { if (!length) { return; } if (Computed || Corrupted) { Corrupted = true; return; } while(length-- && !Corrupted) { Message_Block[Message_Block_Index++] = (*message_array & 0xFF); Length_Low += 8; Length_Low &= 0xFFFFFFFF; // Force it to 32 bits if (Length_Low == 0) { Length_High++; Length_High &= 0xFFFFFFFF; // Force it to 32 bits if (Length_High == 0) { Corrupted = true; // Message is too long } } if (Message_Block_Index == 64) { ProcessMessageBlock(); } message_array++; } } /* * Input * * Description: * This function accepts a single octets as the next message element. * * Parameters: * message_element: [in] * The next octet in the message. * * Returns: * Nothing. * * Comments: * */ void SHA1::Input(char message_element) { Input(&message_element, 1); } /* * ProcessMessageBlock * * Description: * This function will process the next 512 bits of the message * stored in the Message_Block array. * * Parameters: * None. * * Returns: * Nothing. * * Comments: * Many of the variable names in this function, especially the single * character names, were used because those were the names used * in the publication. * */ void SHA1::ProcessMessageBlock() { const uint32 K[] = { // Constants defined for SHA-1 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; int t; // Loop counter uint32 temp; // Temporary word value uint32 W[80]; // Word sequence uint32 A, B, C, D, E; // Word buffers /* * Initialize the first 16 words in the array W */ for(t = 0; t < 16; t++) { W[t] = ((uint32) Message_Block[t * 4]) << 24; W[t] |= ((uint32) Message_Block[t * 4 + 1]) << 16; W[t] |= ((uint32) Message_Block[t * 4 + 2]) << 8; W[t] |= ((uint32) Message_Block[t * 4 + 3]); } for(t = 16; t < 80; t++) { W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); } A = H[0]; B = H[1]; C = H[2]; D = H[3]; E = H[4]; for(t = 0; t < 20; t++) { temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; temp &= 0xFFFFFFFF; E = D; D = C; C = CircularShift(30,B); B = A; A = temp; } for(t = 20; t < 40; t++) { temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; temp &= 0xFFFFFFFF; E = D; D = C; C = CircularShift(30,B); B = A; A = temp; } for(t = 40; t < 60; t++) { temp = CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; temp &= 0xFFFFFFFF; E = D; D = C; C = CircularShift(30,B); B = A; A = temp; } for(t = 60; t < 80; t++) { temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; temp &= 0xFFFFFFFF; E = D; D = C; C = CircularShift(30,B); B = A; A = temp; } H[0] = (H[0] + A) & 0xFFFFFFFF; H[1] = (H[1] + B) & 0xFFFFFFFF; H[2] = (H[2] + C) & 0xFFFFFFFF; H[3] = (H[3] + D) & 0xFFFFFFFF; H[4] = (H[4] + E) & 0xFFFFFFFF; Message_Block_Index = 0; } /* * PadMessage * * Description: * According to the standard, the message must be padded to an even * 512 bits. The first padding bit must be a '1'. The last 64 bits * represent the length of the original message. All bits in between * should be 0. This function will pad the message according to those * rules by filling the message_block array accordingly. It will also * call ProcessMessageBlock() appropriately. When it returns, it * can be assumed that the message digest has been computed. * * Parameters: * None. * * Returns: * Nothing. * * Comments: * */ void SHA1::PadMessage() { /* * Check to see if the current message block is too small to hold * the initial padding bits and length. If so, we will pad the * block, process it, and then continue padding into a second block. */ if (Message_Block_Index > 55) { Message_Block[Message_Block_Index++] = 0x80; while(Message_Block_Index < 64) { Message_Block[Message_Block_Index++] = 0; } ProcessMessageBlock(); while(Message_Block_Index < 56) { Message_Block[Message_Block_Index++] = 0; } } else { Message_Block[Message_Block_Index++] = 0x80; while(Message_Block_Index < 56) { Message_Block[Message_Block_Index++] = 0; } } /* * Store the message length as the last 8 octets */ Message_Block[56] = (Length_High >> 24) & 0xFF; Message_Block[57] = (Length_High >> 16) & 0xFF; Message_Block[58] = (Length_High >> 8) & 0xFF; Message_Block[59] = (Length_High) & 0xFF; Message_Block[60] = (Length_Low >> 24) & 0xFF; Message_Block[61] = (Length_Low >> 16) & 0xFF; Message_Block[62] = (Length_Low >> 8) & 0xFF; Message_Block[63] = (Length_Low) & 0xFF; ProcessMessageBlock(); } /* * CircularShift * * Description: * This member function will perform a circular shifting operation. * * Parameters: * bits: [in] * The number of bits to shift (1-31) * word: [in] * The value to shift (assumes a 32-bit integer) * * Returns: * The shifted value. * * Comments: * */ uint32 SHA1::CircularShift(int bits, uint32 word) { return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits)); } simutrans-124.5/src/simutrans/utils/sha1.h000066400000000000000000000040251520675460500205460ustar00rootroot00000000000000/* * sha1.h * * Copyright (C) 1998, 2009 * Paul E. Jones * All Rights Reserved. * ***************************************************************************** * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $ ***************************************************************************** * * Description: * This class implements the Secure Hashing Standard as defined * in FIPS PUB 180-1 published April 17, 1995. * * Many of the variable names in this class, especially the single * character names, were used because those were the names used * in the publication. * * Please read the file sha1.cpp for more information. * */ #ifndef UTILS_SHA1_H #define UTILS_SHA1_H #include "sha1_hash.h" class SHA1 { public: SHA1() { Reset(); } /* * Re-initialize the class */ void Reset(); /* * Returns the message digest */ bool Result(sha1_hash_t &result_digest); /* * Provide input to SHA1 */ void Input( const char *message_array, uint32 length); void Input( char message_element); private: /* * Process the next 512 bits of the message */ void ProcessMessageBlock(); /* * Pads the current message block to 512 bits */ void PadMessage(); /* * Performs a circular left shift operation */ inline unsigned CircularShift(int bits, unsigned word); uint32 H[5]; // Message digest buffers uint32 Length_Low; // Message length in bits uint32 Length_High; // Message length in bits uint8 Message_Block[64]; // 512-bit message blocks int Message_Block_Index; // Index into message block array bool Computed; // Is the digest computed? bool Corrupted; // Is the message digest corrupted? }; #endif simutrans-124.5/src/simutrans/utils/sha1_hash.cc000066400000000000000000000015711520675460500217120ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "sha1_hash.h" #include "sha1.h" #include "../macros.h" #include sha1_hash_t::sha1_hash_t() { clear(); } sha1_hash_t::sha1_hash_t(const uint8 *value) { memcpy(hash, value, 20); } void sha1_hash_t::clear() { MEMZERO(hash); } bool sha1_hash_t::empty() const { for (uint8 const* i = hash; i != endof(hash); ++i) { if (*i != 0) { return false; } } return true; } uint8 & sha1_hash_t::operator[](const size_t i) { assert(i < 20); return hash[i]; } const uint8 & sha1_hash_t::operator[](size_t i) const { assert(i < 20); return hash[i]; } bool sha1_hash_t::operator==(const sha1_hash_t &o) const { return memcmp(hash, o.hash, 20) == 0; } bool sha1_hash_t::operator!=(const sha1_hash_t& o) const { return memcmp(hash, o.hash, 20) != 0; } simutrans-124.5/src/simutrans/utils/sha1_hash.h000066400000000000000000000012751520675460500215550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_SHA1_HASH_H #define UTILS_SHA1_HASH_H #include "../simtypes.h" /** * Stores the value of a SHA-1 message digest. */ class sha1_hash_t { public: /// @warning @p raw_value must be at least 20 bytes long sha1_hash_t(const uint8 *raw_value); /// Initializes hash to all zeroes sha1_hash_t(); public: void clear(); bool empty() const; uint8 &operator[](size_t i); const uint8 &operator[](size_t i) const; bool operator==(const sha1_hash_t &o) const; bool operator!=(const sha1_hash_t &o) const; private: uint8 hash[20]; }; typedef sha1_hash_t pwd_hash_t; #endif simutrans-124.5/src/simutrans/utils/simrandom.cc000066400000000000000000000215221520675460500220420ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "simrandom.h" #include "../sys/simsys.h" #include "../dataobj/loadsave.h" /* This is the mersenne random generator: More random and faster! */ /* Period parameters */ #define MERSENNE_TWISTER_N 624 #define M 397 #define MATRIX_A 0x9908b0dfUL /* constant vector a */ #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ static uint32 mersenne_twister[MERSENNE_TWISTER_N]; // the array for the state vector static uint32 mersenne_twister_index = MERSENNE_TWISTER_N + 1; // mersenne_twister_index==N+1 means mersenne_twister[N] is not initialized static uint8 random_origin = 0; /* initializes mersenne_twister[N] with a seed */ static void init_genrand(uint32 s) { mersenne_twister[0]= s & 0xffffffffUL; for (mersenne_twister_index=1; mersenne_twister_index> 30)) + mersenne_twister_index); /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ /* only MSBs of the array mersenne_twister[]. */ /* 2002/01/09 modified by Makoto Matsumoto */ mersenne_twister[mersenne_twister_index] &= 0xffffffffUL; /* for >32 bit machines */ } } /* generate N words at one time */ static void MTgenerate() { static uint32 mag01[2]={0x0UL, MATRIX_A}; uint32 y; int kk; if (mersenne_twister_index == MERSENNE_TWISTER_N+1) /* if init_genrand() has not been called, */ init_genrand(5489UL); /* a default initial seed is used */ for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; } for (;kk> 1) ^ mag01[y & 0x1UL]; } y = (mersenne_twister[MERSENNE_TWISTER_N-1]&UPPER_MASK)|(mersenne_twister[0]&LOWER_MASK); mersenne_twister[MERSENNE_TWISTER_N-1] = mersenne_twister[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mersenne_twister_index = 0; } /* returns current seed value */ uint32 get_random_seed() { if (mersenne_twister_index >= MERSENNE_TWISTER_N) { /* generate N words at one time */ MTgenerate(); } return mersenne_twister[mersenne_twister_index]; } /* generates a random number on [0,0xffffffff]-interval */ uint32 simrand_plain() { uint32 y; if (mersenne_twister_index >= MERSENNE_TWISTER_N) { /* generate N words at one time */ MTgenerate(); } y = mersenne_twister[mersenne_twister_index++]; /* Tempering */ y ^= (y >> 11); y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 15) & 0xefc60000UL; y ^= (y >> 18); return y; } /* generates a random number on [0,max-1]-interval */ uint32 simrand(const uint32 max) { assert( (random_origin&INTERACTIVE_RANDOM) == 0 ); if(max<=1) { // may rather assert this? return 0; } return simrand_plain() % max; } void simrand_rdwr(loadsave_t *file) { xml_tag_t t(file, "simrand"); // rdwr index file->rdwr_long(mersenne_twister_index); // rdwr state vector for (uint32 i=0; irdwr_long(mersenne_twister[i]); } } void clear_random_mode( uint16 mode ) { random_origin &= ~mode; } void set_random_mode( uint16 mode ) { random_origin |= mode; } uint16 get_random_mode() { return random_origin; } static uint32 async_rand_seed = 12345678 + (uint32)time( NULL ); // Do not use dr_time(). It returns 0 on program startup for some platforms (SDL). /* simpler simrand for anything not game critical (like UI) */ uint32 sim_async_rand( uint32 max ) { if( max==0 ) { return 0; } async_rand_seed *= 3141592621u; async_rand_seed ++; return (async_rand_seed >> 8) % max; } static uint32 noise_seed = 0; uint32 setsimrand(uint32 seed,uint32 ns) { uint32 old_noise_seed = noise_seed; if(seed!=0xFFFFFFFF) { init_genrand( seed ); async_rand_seed = seed + dr_time(); // dr_time() ok here. re comment ^^^. setsimrand not called immediately on program startup. random_origin = 0; } if(noise_seed!=0xFFFFFFFF) { noise_seed = ns*15731; } return old_noise_seed; } static double int_noise(const sint32 x, const sint32 y) { uint32 n = (uint32)x + (uint32)y*101U + noise_seed; n = (n<<13) ^ n; return 1.0 - (double)((n * (n * n * 15731U + 789221U) + 1376312589U) & 0x7fffffff) / 1073741824.0; } static float *map = 0; static sint32 map_w=0; void init_perlin_map( sint32 w, sint32 h ) { map_w = w+2; map = new float[map_w*(h+2)]; for( sint32 y=0; y> 12; // 1 / log_2(10) ~~ 1233 / 4096 } uint32 log2(uint32 n) { uint32 i = (n & 0xffff0000) ? 16 : 0; if( (n >>= i) & 0xff00 ) { i |= 8; n >>= 8; } if( n & 0xf0 ) { i |= 4; n >>= 4; } if( n & 0xC ) { i |= 2; n >>= 2; } return i | (n >> 1); } /* compute integer sqrt */ uint32 sqrt_i32(uint32 num) { // taken from https://en.wikipedia.org/wiki/Methods_of_computing_square_roots uint32 res = 0; uint32 bit = 1 << 30; // The second-to-top bit is set: 1<<14 for short // "bit" starts at the highest power of four <= the argument. while( bit > num ) { bit >>= 2; } while( bit != 0 ) { if( num >= res + bit ) { num -= res + bit; res = (res >> 1) + bit; } else { res >>= 1; } bit >>= 2; } return res; } /* compute integer sqrt */ uint64 sqrt_i64(uint64 num) { // taken from https://en.wikipedia.org/wiki/Methods_of_computing_square_roots uint64 res = 0; uint64 bit = (uint64)1 << 62; // The second-to-top bit is set: 1<<14 for short // "bit" starts at the highest power of four <= the argument. while( bit > num ) { bit >>= 2; } while( bit != 0 ) { if( num >= res + bit ) { num -= res + bit; res = (res >> 1) + bit; } else { res >>= 1; } bit >>= 2; } return res; } simutrans-124.5/src/simutrans/utils/simrandom.h000066400000000000000000000047751520675460500217170ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_SIMRANDOM_H #define UTILS_SIMRANDOM_H #include "../simtypes.h" #include class loadsave_t; uint32 get_random_seed(); uint32 setsimrand(uint32 seed, uint32 noise_seed); /* generates a random number on [0,max-1]-interval * without affecting the game state * Use this for UI etc. */ uint32 sim_async_rand(const uint32 max); /* generates a random number on [0,max-1]-interval */ uint32 simrand(const uint32 max); /* generates a random number on [0,0xFFFFFFFFu]-interval */ uint32 simrand_plain(); /// reads/writes the sate of the random number generator void simrand_rdwr(loadsave_t *file); double perlin_noise_2D(const double x, const double y, const double persistence); // for network debugging, i.e. finding hidden simrands in wrong places enum { INTERACTIVE_RANDOM = 1 << 0, STEP_RANDOM = 1 << 1, SYNC_STEP_RANDOM = 1 << 2, LOAD_RANDOM = 1 << 3, MAP_CREATE_RANDOM = 1 << 4, MODAL_RANDOM = 1 << 5 }; void set_random_mode( uint16 ); void clear_random_mode( uint16 ); uint16 get_random_mode(); // just more speed with those (generate a precalculated map, which needs only smoothing) void init_perlin_map( sint32 w, sint32 h ); void exit_perlin_map(); /* Randomly select an entry from the given array. */ template T const& pick_any(T const (&array)[N]) { return array[simrand(N)]; } /* Randomly select an entry from the given container. */ template class U> T const& pick_any(U const& container) { return container[simrand(container.get_count())]; } /* Randomly select an entry from the given array, not affecting game state. */ template T const& pick_any_async(T const (&array)[N]) { return array[sim_async_rand(N)]; } /* Randomly select an entry from the given weighted container. */ template class U> T const& pick_any_weighted(U const& container) { return container.at_weight(simrand(container.get_sum_weight())); } /* Randomly select an entry from the given weighted container, not affecting game state. */ template class U> T const& pick_any_weighted_async(U const& container) { return container.at_weight(sim_async_rand(container.get_sum_weight())); } // compute integer log10 uint32 log10( uint32 v ); uint32 log2( uint32 i ); // compute integer sqrt uint32 sqrt_i32(uint32 num); uint64 sqrt_i64(uint64 num); #endif simutrans-124.5/src/simutrans/utils/simstring.cc000066400000000000000000000230371520675460500220730ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "simstring.h" #include #include #include static char thousand_sep = ','; static char fraction_sep = '.'; static const char *large_number_string = "M"; static double large_number_factor = 1e99; // off static int thousand_sep_exponent = 3; static const char *currency_string = "$"; static bool currency_left = false; /** * Set thousand separator, used in money_to_string and * number_to_string */ void set_thousand_sep(char c) { thousand_sep = c; } /** * Set thousand exponent (3=1000, 4=10000), used in money_to_string and * number_to_string */ void set_thousand_sep_exponent(int new_thousand_sep_exponent) { thousand_sep_exponent = new_thousand_sep_exponent>0 ? new_thousand_sep_exponent : 3; } /** * Set fraction separator, used in money_to_string and * number_to_string */ void set_fraction_sep(char c) { fraction_sep = c; } /** * Set fraction separator, used in money_to_string and * number_to_string */ char get_fraction_sep() { return fraction_sep; } const char *get_large_money_string() { return large_number_string; } void set_currency_string(const char* c, bool left) { currency_string = c; currency_left = left; } const char *get_currency_string() { return currency_string; } bool get_currency_left() { return currency_left; } /** * Set large money abbreviation, used in money_to_string and * number_to_string */ void set_large_amount(const char *s, const double v) { large_number_string = s; large_number_factor = v; } /** * Formats a money value. Uses thousand separator. Two digits precision. * Concludes format with $ sign. Buffer must be large enough, no checks * are made! */ void money_to_string(char *p, double f, const bool show_decimal) { char tmp[128]; char *tp = tmp; int i,l; if (currency_left) { p += sprintf(p, "%s", currency_string); } if( f>1000.0*large_number_factor ) { sprintf( tp, "%.1f", f/large_number_factor ); } else { sprintf( tp, "%.2f", f ); } // skip sign if(*tp == '-') { *p ++ = *tp++; } // format string l = (long)(size_t)(strchr(tp,'.') - tp); i = l % thousand_sep_exponent; if(i != 0) { memcpy(p, tp, i); p += i; *p++ = thousand_sep; } while(i < l) { for( int j=0; j1000.0*large_number_factor ) { // only decimals for smaller numbers; add large number string instead for( i=0; large_number_string[i]!=0; i++ ) { *p++ = large_number_string[i]; } } else if( show_decimal ) { i = l+1; // only decimals for smaller numbers *p++ = fraction_sep; // since it might be longer due to unicode characters while( tp[i]!=0 ) { *p++ = tp[i++]; } } *p = 0; if (!currency_left) { strcpy(p, currency_string); } } int number_to_string(char * p, double f, int decimals ) { char tmp[128]; char *tp = tmp; long i,l; bool has_decimals; if( decimals>0 ) { sprintf(tp,"%.*f",decimals,f); has_decimals = true; } else { sprintf(tp,"%.0f", f); // some compilers produce trailing dots then ... has_decimals = strchr(tp,'.')!=NULL; } // skip sign if(*tp == '-') { *p ++ = *tp++; } // format string l = has_decimals ? (long)(size_t)(strchr(tp,'.') - tp) : strlen(tp); i = l % thousand_sep_exponent; if(i != 0) { memcpy(p, tp, i); p += i; *p++ = thousand_sep; } while(i < l) { for( int j=0; j 0 && len <= max_length+decimals+1 ) { tstrncpy( ret, result, len-(decimals) ); return; } // still not fitting: Then we have to really shorten the string number_to_string( result, f/large_number_factor, 2 ); len = strlen( result ); const int llen = strlen(large_number_string); // not fitting: then strip those remaining decimals if( len+llen > max_length ) { result[len-3] = 0; } strcat( result, large_number_string ); strcpy( ret, result ); } /// copies a n into a single line and maximum 128 characters char *make_single_line_string(const char *in,int number_of_lines) { static char buf[64]; int pos; // skip leading whitespaces while(*in=='\n' || *in==' ') { in++; } // start copying for(pos=0; pos<62 && *in!=0 && number_of_lines>0; ) { if((unsigned)(*in)>' ') { buf[pos++] = *in++; } else { // replace new lines by space while(*in=='\n' || *in==' ') { if(*in=='\n') { number_of_lines--; } in++; } buf[pos++] = ' '; } } // trim trailing spaces while(pos>0 && buf[pos-1]==' ') { pos--; } // end mark! buf[pos] = 0; return buf; } /** * Terminated, length limited string copy. Copies at most * n characters. Terminates dest string always by 0. * @return dest */ char *tstrncpy(char *dest, const char *src, size_t n) { if (dest == src) { // source and destination are the same // avoid copying overlapping memory regions return dest; } strncpy(dest, src, n); dest[n-1] = '\0'; return dest; } /** * strcasestr implementation. Search for a pattern in a given string, ignoring case. * @param str: String to search in * @param pattern: The pattern we are looking for. * @returns: A char pointer to the start of the pattern we are looking for in the given string, or NULL if not found. * Original code written by Clifford and shared at StackOverflow: https://stackoverflow.com/a/27304609 */ char *tstrcasestr(const char* str, const char* pattern) { const char* p1 = str; const char* p2 = pattern; const char* r = *p2 == 0 ? str : 0; while( *p1 != 0 && *p2 != 0 ) { if( tolower( (unsigned char)*p1 ) == tolower( (unsigned char)*p2 ) ) { if( r == 0 ) { r = p1; } p2++; } else { p2 = pattern; if( r != 0 ) { p1 = r + 1; } if( tolower( (unsigned char)*p1 ) == tolower( (unsigned char)*p2 ) ) { r = p1; p2++; } else { r = 0; } } p1++; } return *p2 == 0 ? const_cast(r) : NULL; } /** * Removes whitespace from the end of the string. * Modifies the argument! */ void rtrim(char * buf) { for (size_t l = strlen(buf); l-- != 0 && 0 < buf[l] && buf[l] <= 32;) { buf[l] = '\0'; } } /** * Hands back a pointer to the first non-whitespace character * of the argument. The argument must be 0 terminated */ const char * ltrim(const char *p) { while(*p != '\0' && *p > 0 && *p <= 32) { p ++; } return p; } /** * Trims a std::string by removing any beginning and ending space/tab characters. * (Move to simstring?) * @retval std::string The trimmed string. */ std::string trim(const std::string &str_) { std::string str(str_); // left trim std::string::size_type pos = str.find_first_not_of(" \t"); if( pos && pos != std::string::npos ) { str = str.substr(pos); } // right trim pos = str.find_last_not_of(" \t"); if( pos != str.length()-1 && pos != std::string::npos ) { str = str.erase(pos+1); } return str; } char const* strstart(char const* str, char const* start) { while (*start != '\0') { if (*str++ != *start++) return 0; } return str; } /** * Returns the file name without extension (optional) of a qualified filename * including path. * * @param fullpath A nul terminated string with a full qualified file name. * @param with_extension If true, the extension is removed from the filename. * * @retval std::string The filename without extension. */ std::string str_get_filename(const char* fullpath, const bool with_extension) { std::string path = fullpath; // Remove until last \ or / size_t last = path.find_last_of("\\/"); if (last != std::string::npos) { path = path.erase(0, last + 1); } // Remove extension if it's present, will remove from '.' till the end. if (!with_extension) { last = path.find_last_of("."); if (last != std::string::npos) { path = path.erase(last); } } return path; } /** * Returns the path portion of a qualified filename including path. * * @param fullpath A null terminated string with a full qualified file name. */ std::string str_get_basename(const char* fullpath) { std::string path = fullpath; size_t last = path.find_last_of("\\/"); if (last == std::string::npos) { return path; } return path.substr(0, last + 1); } /** * Removes ASCII control characters and the space character from the end of the string * See https://www.ascii-code.com/ * * @param string * @returns the string without control chars at the end */ char* clear_invalid_ending_chars(char* string) { static int MAX_ASCII_CHAR = 32; size_t len = strlen(string); while( len>0 && string[--len] <= MAX_ASCII_CHAR ) { string[len] = 0; } return string; } void str_to_lowercase(std::string& s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); } simutrans-124.5/src/simutrans/utils/simstring.h000066400000000000000000000102731520675460500217330ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_SIMSTRING_H #define UTILS_SIMSTRING_H #include #include #include #ifdef __HAIKU__ #include #endif #ifndef STRICMP #ifdef _MSC_VER #define STRICMP stricmp #define STRNICMP strnicmp #else #define STRICMP strcasecmp #define STRNICMP strncasecmp #endif #endif #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) #endif /** * Set thousand separator, used in money_to_string and * number_to_string */ void set_thousand_sep(char c); #define MULTIPLIER 37 inline unsigned int string_to_hash(const char* p, size_t maxlen) { unsigned int hash = 0; if (p) { const char* start = p; for (; *p && (size_t)(p - start) < maxlen; p++) { hash = MULTIPLIER * hash + (unsigned char)*p; } } return hash; } inline unsigned int string_to_hash(const char* p) { unsigned int hash = 0; if (p) { for (; *p; p++) { hash = MULTIPLIER * hash + (unsigned char)*p; } } return hash; } #undef MULTIPLIER /** * Set fraction separator, used in money_to_string and * number_to_string */ void set_fraction_sep(char c); char get_fraction_sep(); const char *get_large_money_string(); // currency (default "$") void set_currency_string(const char* c, bool left); const char* get_currency_string(); bool get_currency_left(); /** * Set thousand exponent (3=1000, 4=10000), used in money_to_string and * number_to_string */ void set_thousand_sep_exponent(int new_thousand_sep_exponent); /** * Set abbreviation and the amount by which large money amounts will be shortened */ void set_large_amount( const char *, const double v ); /** * copies n lines of the source into a buffer * @return a temporary buffer with the result */ char *make_single_line_string(const char *in,int number_of_lines); /** * Formats a money value. Uses thousand separator. Two digits precision. * Concludes format with $ sign. Buffer must be large enough, no checks * are made! */ void money_to_string(char * buf, double f, const bool show_decimal = true); /** * Formats a number value. Uses thousand separator. Buffer must be large enough, * no checks are made! */ int number_to_string(char * buf, double f, int decimal_places ); /* tries to squeeze the number into this amount of given digits * BUT: too large number will still exceed the space */ void number_to_string_fit(char *ret, double f, int decimals, int max_length ); /** * Terminated, length limited string copy. Copies at most * n characters. Terminates dest string always by 0. * @return dest */ char *tstrncpy(char *dest, const char *src, size_t n); /** * strcasestr implementation. Search for a pattern in a given string, ignoring case. * @param str: String to search in * @param pattern: The pattern we are looking for. * @returns: A char pointer to the start of the pattern we are looking for in the given string, or NULL if not found. * Original code written by Clifford and shared at StackOverflow: https://stackoverflow.com/a/27304609 */ char* tstrcasestr(const char* str, const char* pattern); /** * Removes whitespace from the end of the string. * Modifies the argument! */ void rtrim(char *); /** * Hands back a pointer to the first non-whitespace character * of the argument. The argument must be 0 terminated. */ const char * ltrim(const char *); /** * Trim function for std::strings */ std::string trim (const std::string &str ); /** * Returns a pointer to the rest of str if str starts with start. */ char const* strstart(char const* str, char const* start); /** * Returns whether s is a null pointer or the empty string. */ static inline bool strempty(char const* const s) { return !s || s[0] == '\0'; } std::string str_get_filename(const char* fullpath, const bool with_extension); std::string str_get_basename(const char* fullpath); /** * Removes ASCII control characters (and the space character) from the end of the string * See https://www.ascii-code.com/ * * @param string * @returns string without control chars at the end */ char* clear_invalid_ending_chars(char* string); void str_to_lowercase(std::string& s); #endif simutrans-124.5/src/simutrans/utils/simthread.cc000066400000000000000000000036441520675460500220360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "simthread.h" #ifdef MULTI_THREAD // do not try to compile this file for non-multithreaded builds #ifndef _USE_POSIX_BARRIERS // implement barriers using other pthread primitives #include int simthread_barrier_init(simthread_barrier_t *barrier, const simthread_barrierattr_t */*attr*/, unsigned int count) { if( count == 0 ) { errno = EINVAL; return -1; } if( pthread_mutex_init( &barrier->mutex, 0 ) < 0 ) { return -1; } if( pthread_cond_init( &barrier->cond, 0 ) < 0 ) { pthread_mutex_destroy( &barrier->mutex ); return -1; } barrier->tripCount = count; barrier->count = 0; return 0; } int simthread_barrier_destroy(simthread_barrier_t *barrier) { pthread_cond_destroy( &barrier->cond ); pthread_mutex_destroy( &barrier->mutex ); return 0; } int simthread_barrier_wait(simthread_barrier_t *barrier) { pthread_mutex_lock( &barrier->mutex ); barrier->count++; if( barrier->count >= barrier->tripCount ) { barrier->count = 0; pthread_cond_broadcast( &barrier->cond ); pthread_mutex_unlock( &barrier->mutex ); return 1; } else { pthread_cond_wait( &barrier->cond, &barrier->mutex ); pthread_mutex_unlock( &barrier->mutex ); return 0; } } #endif #ifndef _SIMTHREAD_R_MUTEX_I #include "../simdebug.h" // initialize a recursive mutex by calling pthread_mutex_init() recursive_mutex_maker_t::recursive_mutex_maker_t(pthread_mutex_t &mutex) { pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr) != 0 || pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0 || pthread_mutex_init(&mutex, &attr) != 0 || pthread_mutexattr_destroy(&attr) != 0) { // Can't call dbg->error(), because this constructor // may run before simu_main() calls init_logging(). dbg->fatal("recursive_mutex_maker_t","Can't make a recursive mutex!"); } } #endif #endif simutrans-124.5/src/simutrans/utils/simthread.h000066400000000000000000000045231520675460500216750ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_SIMTHREAD_H #define UTILS_SIMTHREAD_H #ifdef MULTI_THREAD #if !defined(__APPLE__) && ( !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600 ) // On Posix systems, this enables barriers. // On OS X, barriers are not supported anyway, and defining this would // cause PTHREAD_RECURSIVE_MUTEX_INITIALIZER to not get defined. #define _XOPEN_SOURCE 600 #endif #ifndef _MSC_VER #include // _POSIX_BARRIERS macro // Visual C++ does not supply unistd.h #endif #if defined _MSC_VER && _MSC_VER >= 1900 // MSVC 2015 with Windows 10 SDK has struct timespec #define HAVE_STRUCT_TIMESPEC #endif #include #if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP // Linux glibc #define _SIMTHREAD_R_MUTEX_I PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP #elif defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER // Mac OS X #define _SIMTHREAD_R_MUTEX_I PTHREAD_RECURSIVE_MUTEX_INITIALIZER #endif struct recursive_mutex_maker_t { #ifdef _SIMTHREAD_R_MUTEX_I recursive_mutex_maker_t(pthread_mutex_t &mutex) { // initializer can only be used for initialization, not assignment pthread_mutex_t dummy = _SIMTHREAD_R_MUTEX_I; mutex = dummy; } #else recursive_mutex_maker_t(pthread_mutex_t &mutex); #endif }; // use our implementation if no posix barriers are available #if defined(_POSIX_BARRIERS) && (_POSIX_BARRIERS > 0) #define _USE_POSIX_BARRIERS #endif #ifdef _USE_POSIX_BARRIERS // redirect simthread functions to use supported pthread barriers typedef pthread_barrierattr_t simthread_barrierattr_t; typedef pthread_barrier_t simthread_barrier_t; #define simthread_barrier_init pthread_barrier_init #define simthread_barrier_destroy pthread_barrier_destroy #define simthread_barrier_wait pthread_barrier_wait #else // add barrier support using other pthread primitives typedef int simthread_barrierattr_t; typedef struct { pthread_mutex_t mutex; pthread_cond_t cond; int count; int tripCount; } simthread_barrier_t; // needed because our signature doesn't match the one from simthread_barrier_init(3) int simthread_barrier_init(simthread_barrier_t *barrier, const simthread_barrierattr_t *attr, unsigned int count); int simthread_barrier_destroy(simthread_barrier_t *barrier); int simthread_barrier_wait(simthread_barrier_t *barrier); #endif #endif #endif simutrans-124.5/src/simutrans/utils/unicode.cc000066400000000000000000000155261520675460500215060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../utils/unicode.h" #include "../simtypes.h" #include utf32 const UNICODE_NUL = 0; utf8_decoder_t::utf8_decoder_t(utf8 const *const str) { utf8str = str; } utf32 utf8_decoder_t::decode(utf8 const *&buff) { size_t len = 0; utf32 const code_point = decode(buff, len); buff += len; return code_point; } utf32 utf8_decoder_t::decode(utf8 const *const buff, size_t &len) { // Implementation derived from RFC 3629. // Process character byte. size_t i = 0; len = 0; utf8 const character = buff[i++]; utf32 cp = 0; if( character <= 0x7F ) { // ASCII character. cp = character; len = 1; } else if( character < 0xC2 ) { // Invalid character. } else if( character <= 0xDF ) { // 2 byte character. cp = character & 0x1F; len = 2; } else if( character <= 0xEF ) { // 3 byte character. if( !((character == 0xE0 && buff[i] < 0xA0) || (character == 0xED && buff[i] > 0x9F)) ) { cp = character & 0xF; len = 3; } } else if( character <= 0xF4 ) { // 4 byte character. if( !((character == 0xF0 && buff[i] < 0x90) || (character == 0xF4 && buff[i] > 0x8F)) ) { cp = character & 0x7; len = 4; } } else { // Invalid character. } // Process tail bytes. for( ; i < len ; i++ ) { utf8 const tail = buff[i]; if( 0x80 <= tail && tail <= 0xBF ) { cp <<= 6; cp |= tail & 0x3F; } else { // Invalid tail. len = 0; } } if( len == 0 ) { // Replace invalid sequences with code point of the single decoded character (ISO-8859-1). len = 1; cp = character; } return cp; } bool utf8_decoder_t::has_next() const { return utf8str[0] != UNICODE_NUL; } utf32 utf8_decoder_t::next() { return has_next() ? decode(utf8str) : UNICODE_NUL; } utf8 const *utf8_decoder_t::get_position() { return utf8str; } size_t utf8_get_next_char(const utf8* text, size_t pos) { // go right one character // the bytes in a sequence have always the format 10xxxxxx, thus we use is_cont_char() // this will always work, even if we do not start on a sequence starting character do { pos++; } while (is_cont_char(text[pos])); return pos; } sint32 utf8_get_prev_char(const utf8* text, sint32 pos) { if (pos <= 0) { return 0; } // go left one character // the bytes in a sequence have always the format 10xxxxxx, thus we use is_cont_char() do { pos--; } while (pos>0 && is_cont_char(text[pos])); return pos; } size_t utf8_get_next_char(const char* text, size_t pos) { return utf8_get_next_char((const utf8*)text, pos); } sint32 utf8_get_prev_char(const char* text, sint32 pos) { return utf8_get_prev_char((const utf8*)text, pos); } int utf16_to_utf8(utf16 c, utf8* out) { if (c < 0x80) { out[0] = (utf8)c; return 1; } else if (c < 0x800) { out[0] = 0xC0 | (c >> 6); out[1] = 0x80 | (c >> 0 & 0x3F); return 2; } else /* if (c < 0x10000) */ { // Assume always a 3 byte sequence, since we do not support 4 byte UTF32 out[0] = 0xE0 | (c >> 12); out[1] = 0x80 | (c >> 6 & 0x3F); out[2] = 0x80 | (c >> 0 & 0x3F); return 3; } } // helper function to convert unicode into latin2 static utf16 latin2_to_unicode_lookup[96] = { 0x00A0, // char 0xA0 0x0104, // char 0xA1 0x02D8, // char 0xA2 0x0141, // char 0xA3 0x00A4, // char 0xA4 0x013D, // char 0xA5 0x015A, // char 0xA6 0x00A7, // char 0xA7 0x00A8, // char 0xA8 0x0160, // char 0xA9 0x015E, // char 0xAA 0x0164, // char 0xAB 0x0179, // char 0xAC 0x00AD, // char 0xAD 0x017D, // char 0xAE 0x017B, // char 0xAF 0x00B0, // char 0xB0 0x0105, // char 0xB1 0x02DB, // char 0xB2 0x0142, // char 0xB3 0x00B4, // char 0xB4 0x013E, // char 0xB5 0x015B, // char 0xB6 0x02C7, // char 0xB7 0x00B8, // char 0xB8 0x0161, // char 0xB9 0x015F, // char 0xBA 0x0165, // char 0xBB 0x017A, // char 0xBC 0x02DD, // char 0xBD 0x017E, // char 0xBE 0x017C, // char 0xBF 0x0154, // char 0xC0 0x00C1, // char 0xC1 0x00C2, // char 0xC2 0x0102, // char 0xC3 0x00C4, // char 0xC4 0x0139, // char 0xC5 0x0106, // char 0xC6 0x00C7, // char 0xC7 0x010C, // char 0xC8 0x00C9, // char 0xC9 0x0118, // char 0xCA 0x00CB, // char 0xCB 0x011A, // char 0xCC 0x00CD, // char 0xCD 0x00CE, // char 0xCE 0x010E, // char 0xCF 0x0110, // char 0xD0 0x0143, // char 0xD1 0x0147, // char 0xD2 0x00D3, // char 0xD3 0x00D4, // char 0xD4 0x0150, // char 0xD5 0x00D6, // char 0xD6 0x00D7, // char 0xD7 0x0158, // char 0xD8 0x016E, // char 0xD9 0x00DA, // char 0xDA 0x0170, // char 0xDB 0x00DC, // char 0xDC 0x00DD, // char 0xDD 0x0162, // char 0xDE 0x00DF, // char 0xDF 0x0155, // char 0xE0 0x00E1, // char 0xE1 0x00E2, // char 0xE2 0x0103, // char 0xE3 0x00E4, // char 0xE4 0x013A, // char 0xE5 0x0107, // char 0xE6 0x00E7, // char 0xE7 0x010D, // char 0xE8 0x00E9, // char 0xE9 0x0119, // char 0xEA 0x00EB, // char 0xEB 0x011B, // char 0xEC 0x00ED, // char 0xED 0x00EE, // char 0xEE 0x010F, // char 0xEF 0x0111, // char 0xF0 0x0144, // char 0xF1 0x0148, // char 0xF2 0x00F3, // char 0xF3 0x00F4, // char 0xF4 0x0151, // char 0xF5 0x00F6, // char 0xF6 0x00F7, // char 0xF7 0x0159, // char 0xF8 0x016F, // char 0xF9 0x00FA, // char 0xFA 0x0171, // char 0xFB 0x00FC, // char 0xFC 0x00FD, // char 0xFD 0x0163, // char 0xFE 0x02D9 // char 0xFF }; uint8 unicode_to_latin2( utf16 chr ) { for( utf8 i=0; i<96; i++ ) { if( latin2_to_unicode_lookup[i]==chr ) { return (i+0xA0); } } return 0; } utf16 latin2_to_unicode( uint8 chr ) { return chr >= 0xA0 ? latin2_to_unicode_lookup[chr-0xA0] : chr; } // inspired from Stackoverflow https://stackoverflow.com/questions/27303062/strstr-function-like-that-ignores-upper-or-lower-case // Handles somewhat umlaute as well (may depend a little on the locale though) const utf8 *utf8caseutf8(const utf8 *haystack_start, const utf8 *needle_start) { const utf8 *needle_p = needle_start; const utf8 *haystack_p = haystack_start; utf32 c = towlower( utf8_decoder_t::decode(needle_p )); needle_start = needle_p; if( c=='\0' ) { return haystack_start; } const utf8 *haystack_current = haystack_p; utf32 hs = utf8_decoder_t::decode( haystack_p ); while( hs != 0 ) { if( towlower( hs ) == c ) { const utf8 *haystack_next = haystack_p; while(true) { sint32 nc = utf8_decoder_t::decode( needle_p ); if( nc == 0 ) { return haystack_current; } hs = utf8_decoder_t::decode( haystack_p ); if( towlower( hs ) != towlower( nc ) ) { needle_p = needle_start; break; } } // advance one character haystack_p = haystack_next; } haystack_current = haystack_p; hs = utf8_decoder_t::decode( haystack_p ); } return NULL; } // defining it in the include did not work for whatever reason const char *utf8caseutf8( const char *haystack, const char *needle ) { return (const char *)utf8caseutf8( (const utf8 *)haystack, (const utf8 *)needle ); } simutrans-124.5/src/simutrans/utils/unicode.h000066400000000000000000000050241520675460500213400ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef UTILS_UNICODE_H #define UTILS_UNICODE_H #include #include "../simtypes.h" extern utf32 const UNICODE_NUL; // the bytes in a sequence have always the format 10xxxxxx inline int is_cont_char(utf8 c) { return (c & 0xC0) == 0x80; } /** * UTF-8 string decoder that can be used to iterate through all code points. */ class utf8_decoder_t { private: // Pointer to UTF-8 formated C string. utf8 const *utf8str; public: // Constructs a UTF-8 decoder for the given C string. utf8_decoder_t(utf8 const *str); /** * Decodes a Unicode code point from the byte sequence pointed to by buff. * On return buff has been advanced to point at the beginning of the next Unicode code point. * Does not respect NUL terminator character, care should be taken to detect the emmited UNICODE_NUL when decoding C strings to avoid buffer over run errors. * Invalid Unicode sequences are intepreted using ISO-8859-1 and advance buff 1 byte. */ static utf32 decode(utf8 const *&buff); /** * Decodes a Unicode code point from the byte sequence pointed to by buff. * On return len contains the length of the Unicode character in bytes. * Does not respect NUL terminator character, care should be taken to detect the emmited UNICODE_NUL when decoding C strings to avoid buffer over run errors. * Invalid Unicode sequences are intepreted using ISO-8859-1 with a len of 1. */ static utf32 decode(utf8 const *const buff, size_t &len); /** * Returns true if there are more code points left to decode. * Returns false if at end of string. */ bool has_next() const; /** * Returns the next Unicode code point value in the string. * Returns UNICODE_NUL if has_next returns false. */ utf32 next(); /** * Returns the current position of the decoder. * This is a pointer to the next character. */ utf8 const *get_position(); }; // unicode save moving in strings size_t utf8_get_next_char(const utf8 *text, size_t pos); size_t utf8_get_next_char(const char* text, size_t pos); sint32 utf8_get_prev_char(const utf8 *text, sint32 pos); sint32 utf8_get_prev_char(const char* text, sint32 pos); int utf16_to_utf8(utf16 unicode, utf8 *out); // returns latin2 or 0 for error uint8 unicode_to_latin2( utf16 chr ); utf16 latin2_to_unicode( uint8 chr ); // caseless strstr with utf8 const utf8 *utf8caseutf8( const utf8 *haystack_start, const utf8 *needle_start ); const char *utf8caseutf8( const char *haystack, const char *needle ); #endif simutrans-124.5/src/simutrans/vehicle/000077500000000000000000000000001520675460500200175ustar00rootroot00000000000000simutrans-124.5/src/simutrans/vehicle/air_vehicle.cc000066400000000000000000000770571520675460500226200ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "air_vehicle.h" #include "../obj/way/runway.h" #include "../simhalt.h" #include "../simconvoi.h" #include "../world/simworld.h" #include "../dataobj/environment.h" #include "../simware.h" #include "../builder/vehikelbauer.h" #include "../dataobj/schedule.h" // for flying things, everywhere is good ... // another function only called during route searching ribi_t::ribi air_vehicle_t::get_ribi(const grund_t *gr) const { switch(state) { case taxiing: case looking_for_parking: return gr->get_weg_ribi(air_wt); case taxiing_to_halt: { // we must invert all one way signs here, since we start from the target position here! weg_t *w = gr->get_weg(air_wt); if(w) { ribi_t::ribi r = w->get_ribi_unmasked(); if( ribi_t::ribi mask = w->get_ribi_maske() ) { r &= mask; } return r; } return ribi_t::none; } case landing: case departing: { ribi_t::ribi dir = gr->get_weg_ribi(air_wt); if(dir==0) { return ribi_t::all; } return dir; } case flying: case circling: return ribi_t::all; } return ribi_t::none; } // how expensive to go here (for way search) int air_vehicle_t::get_cost(const grund_t *, const weg_t *w, const sint32, ribi_t::ribi) const { // first favor faster ways int costs = 0; if(state==flying) { if(w==NULL) { costs += 1; } else { if(w->get_desc()->get_styp()==type_flat) { costs += 25; } } } else { // only, if not flying ... const runway_t *rw =(const runway_t *)w; // if we are on a runway, then take into account how many convois are already going there if( rw->get_desc()->get_styp()==1 ) { costs += rw->get_reservation_count()*9; // encourage detours even during take off } if(w->get_desc()->get_styp()==type_flat) { costs += 3; } else { costs += 2; } } return costs; } // whether the ground is drivable or not depends on the current state of the airplane bool air_vehicle_t::check_next_tile(const grund_t *bd) const { switch (state) { case taxiing: case taxiing_to_halt: case looking_for_parking: //DBG_MESSAGE("check_next_tile()","at %i,%i",bd->get_pos().x,bd->get_pos().y); return (bd->hat_weg(air_wt) && bd->get_weg(air_wt)->get_max_speed()>0); case landing: case departing: case flying: case circling: { //DBG_MESSAGE("air_vehicle_t::check_next_tile()","(cnv %i) in idx %i",cnv->self.get_id(),route_index ); // here a height check could avoid too high mountains return true; } } return false; } // this routine is called by find_route, to determined if we reached a destination bool air_vehicle_t::is_target(const grund_t *gr,const grund_t *) const { if(state!=looking_for_parking || !target_halt.is_bound()) { // search for the end of the runway const weg_t *w=gr->get_weg(air_wt); if(w && w->get_desc()->get_styp()==type_runway) { // ok here is a runway ribi_t::ribi ribi= w->get_ribi_unmasked(); if(ribi_t::is_single(ribi) && (ribi&approach_dir)!=0) { // pointing in our direction // here we should check for length, but we assume everything is ok return true; } } } else { // otherwise we just check, if we reached a free stop position of this halt if(gr->get_halt()==target_halt && target_halt->is_reservable(gr,cnv->self)) { return true; } } return false; } /* finds a free stop, calculates a route and reserve the position * else return false */ bool air_vehicle_t::find_route_to_stop_position() { if(target_halt.is_bound()) { //DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","bound! (cnv %i)",cnv->self.get_id()); return true; // already searched with success } // check for skipping circle route_t *rt=cnv->access_route(); //DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","can approach? (cnv %i)",cnv->self.get_id()); grund_t const* const last = welt->lookup(rt->back()); target_halt = last ? last->get_halt() : halthandle_t(); if(!target_halt.is_bound()) { return true; // no halt to search } // then: check if the search point is still on a runway (otherwise just proceed) grund_t const* const target = welt->lookup(rt->at(search_for_stop)); if(target==NULL || !target->hat_weg(air_wt)) { target_halt = halthandle_t(); block_reserver( search_for_stop, 0xFFFFu, false ); // unreserve all tiles DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","no runway found at (%s)",rt->at(search_for_stop).get_str()); return true; // no runway any more ... } // is our target occupied? // DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","state %i",state); if(!target_halt->find_free_position(air_wt,cnv->self,obj_t::air_vehicle) ) { target_halt = halthandle_t(); DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","no free position found!"); return false; } else { // calculate route to free position: // if we fail, we will wait in a step, much more simulation friendly // and the route finder is not re-entrant! if(!cnv->is_waiting()) { target_halt = halthandle_t(); return false; } // now search a route //DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","some free: find route from index %i",suchen); route_t target_rt; flight_state prev_state = state; state = looking_for_parking; if(!target_rt.find_route( welt, rt->at(search_for_stop), this, 500, ribi_t::all, welt->get_settings().get_max_choose_route_steps() )) { DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","found no route to free one"); // just make sure, that there is a route at all, otherwise start route search again for( uint32 i=search_for_stop; iget_count(); i++ ) { grund_t const* const target = welt->lookup( rt->at( i ) ); if( target == NULL || !target->hat_weg( air_wt ) ) { DBG_MESSAGE( "aircraft_t::find_route_to_stop_position()", "no runway found at (%s)", rt->at( search_for_stop ).get_str() ); get_convoi()->set_state(convoi_t::ROUTING_1); block_reserver( search_for_stop, 0xFFFFu, false ); // unreserve all tiles return false; // find new route } } // circle slowly another round ... target_halt = halthandle_t(); state = prev_state; return false; } state = prev_state; // now reserve our choice ... target_halt->reserve_position(welt->lookup(target_rt.back()), cnv->self); //DBG_MESSAGE("aircraft_t::find_route_to_stop_position()", "found free stop near %i,%i,%i", target_rt.back().x, target_rt.back().y, target_rt.back().z); rt->remove_koord_from(search_for_stop); rt->append( &target_rt ); return true; } } // main routine: searches the new route in up to three steps // must also take care of stops under traveling and the like bool air_vehicle_t::calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route) { //DBG_MESSAGE("aircraft_t::calc_route()","search route from %i,%i,%i to %i,%i,%i",start.x,start.y,start.z,ziel.x,ziel.y,ziel.z); if(leading && cnv) { // free target reservation if( target_halt.is_bound() ) { if (grund_t* const target = welt->lookup(cnv->get_route()->back())) { target_halt->unreserve_position(target,cnv->self); } } // free runway reservation block_reserver( route_index, route->get_count(), false ); } target_halt = halthandle_t(); // no block reserved const weg_t *w=welt->lookup(start)->get_weg(air_wt); bool start_in_the_air = (w==NULL) || state==flying || flying_height>0; bool end_in_air=false; search_for_stop = takeoff = touchdown = 0x7ffffffful; if(!start_in_the_air) { // see, if we find a direct route: We are finished state = taxiing; if(route->calc_route( welt, start, ziel, this, max_speed, 0 )) { // ok, we can taxi to our location return true; } } if(start_in_the_air || (w->get_desc()->get_styp()==type_runway && ribi_t::is_single(w->get_ribi())) ) { // we start here, if we are in the air or at the end of a runway search_start = start; start_in_the_air = true; route->clear(); //DBG_MESSAGE("aircraft_t::calc_route()","start in air at %i,%i,%i",search_start.x,search_start.y,search_start.z); } else { // not found and we are not on the takeoff tile (where the route search will fail too) => we try to calculate a complete route, starting with the way to the runway // second: find start runway end state = taxiing; approach_dir = welt->get_settings().get_approach_dir(); // reverse DBG_MESSAGE("aircraft_t::calc_route()","search runway start near (%s)",start.get_str()); if(!route->find_route( welt, start, this, max_speed, ribi_t::all, 100 )) { DBG_MESSAGE("aircraft_t::calc_route()","failed"); return false; } // save the route search_start = route->back(); //DBG_MESSAGE("aircraft_t::calc_route()","start at ground (%s)",search_start.get_str()); } // second: find target runway end state = taxiing_to_halt; // only used for search approach_dir = ~welt->get_settings().get_approach_dir(); // reverse //DBG_MESSAGE("aircraft_t::calc_route()","search runway target near %i,%i,%i in corners %x",ziel.x,ziel.y,ziel.z); route_t end_route; if(!end_route.find_route( welt, ziel, this, max_speed, ribi_t::all, welt->get_settings().get_max_choose_route_steps() )) { // well, probably this is a waypoint if( grund_t *target = welt->lookup(ziel) ) { if( !target->get_weg(air_wt) ) { end_in_air = true; search_end = ziel; } else { // we have a taxiway/illegal runway here we cannot reach return false; // no route! } } else { // illegal coordinates? return false; } } else { // save target route search_end = end_route.back(); } //DBG_MESSAGE("aircraft_t::calc_route()","end at ground (%s)",search_end.get_str()); // create target route if(!start_in_the_air) { takeoff = route->get_count()-1; koord start_dir(welt->lookup(search_start)->get_weg_ribi(air_wt)); if(start_dir!=koord(0,0)) { // add the start ribi_t::ribi start_ribi = ribi_t::backward(ribi_type(start_dir)); const grund_t *gr=NULL; // add the start int endi = 1; int over = 3; // now add all runway + 3 ... do { if(!welt->is_within_limits(search_start.get_2d()+(start_dir*endi)) ) { break; } gr = welt->lookup_kartenboden(search_start.get_2d()+(start_dir*endi)); if(over<3 || (gr->get_weg_ribi(air_wt)&start_ribi)==0) { over --; } endi ++; route->append(gr->get_pos()); } while( over>0 ); // out of map if(gr==NULL) { dbg->error("aircraft_t::calc_route()","out of map!"); return false; } // need some extra step to avoid 180 deg turns if( start_dir.x!=0 && sgn(start_dir.x)!=sgn(search_end.x-search_start.x) ) { route->append( welt->lookup_kartenboden(gr->get_pos().get_2d()+koord(0,(search_end.y>search_start.y) ? 1 : -1 ) )->get_pos() ); route->append( welt->lookup_kartenboden(gr->get_pos().get_2d()+koord(0,(search_end.y>search_start.y) ? 2 : -2 ) )->get_pos() ); } else if( start_dir.y!=0 && sgn(start_dir.y)!=sgn(search_end.y-search_start.y) ) { route->append( welt->lookup_kartenboden(gr->get_pos().get_2d()+koord((search_end.x>search_start.x) ? 1 : -1 ,0) )->get_pos() ); route->append( welt->lookup_kartenboden(gr->get_pos().get_2d()+koord((search_end.x>search_start.x) ? 2 : -2 ,0) )->get_pos() ); } } else { // init with startpos dbg->error("aircraft_t::calc_route()","Invalid route calculation: start is on a single direction field ..."); } state = taxiing; flying_height = 0; target_height = (sint16)get_pos().z*TILE_HEIGHT_STEP; } else { // init with current pos (in air ... ) route->clear(); route->append( start ); state = flying; if(flying_height==0) { flying_height = 3*TILE_HEIGHT_STEP; } takeoff = 0; target_height = ((sint16)get_pos().z+3)*TILE_HEIGHT_STEP; } //DBG_MESSAGE("aircraft_t::calc_route()","take off ok"); koord3d landing_start=search_end; if(!end_in_air) { // now find way to start of landing pos ribi_t::ribi end_ribi = welt->lookup(search_end)->get_weg_ribi(air_wt); koord end_dir(end_ribi); end_ribi = ribi_t::backward(end_ribi); if(end_dir!=koord(0,0)) { // add the start const grund_t *gr; int endi = 1; int over = 3; // now add all runway + 3 ... do { if(!welt->is_within_limits(search_end.get_2d()+(end_dir*endi)) ) { break; } gr = welt->lookup_kartenboden(search_end.get_2d()+(end_dir*endi)); if(over<3 || (gr->get_weg_ribi(air_wt)&end_ribi)==0) { over --; } endi ++; landing_start = gr->get_pos(); } while( over>0 ); } } else { search_for_stop = touchdown = 0x7FFFFFFFul; } // just some straight routes ... if(!route->append_straight_route(welt,landing_start)) { // should never fail ... dbg->error( "aircraft_t::calc_route()", "No straight route found!" ); return false; } if(!end_in_air) { // find starting direction int offset = 0; switch(welt->lookup(search_end)->get_weg_ribi(air_wt)) { case ribi_t::north: offset = 0; break; case ribi_t::west: offset = 4; break; case ribi_t::south: offset = 8; break; case ribi_t::east: offset = 12; break; } // now make a curve koord circlepos=landing_start.get_2d(); static const koord circle_koord[16]={ koord(0,1), koord(0,1), koord(1,0), koord(0,1), koord(1,0), koord(1,0), koord(0,-1), koord(1,0), koord(0,-1), koord(0,-1), koord(-1,0), koord(0,-1), koord(-1,0), koord(-1,0), koord(0,1), koord(-1,0) }; // circle to the left for( int i=0; i<16; i++ ) { circlepos += circle_koord[(offset+i+16)%16]; if(welt->is_within_limits(circlepos)) { route->append( welt->lookup_kartenboden(circlepos)->get_pos() ); } else { // could only happen during loading old savegames; // in new versions it should not possible to build a runway here route->clear(); dbg->error("aircraft_t::calc_route()","airport too close to the edge! (Cannot go to %i,%i!)",circlepos.x,circlepos.y); return false; } } touchdown = route->get_count()+2; route->append_straight_route(welt,search_end); // now the route reach point (+1, since it will check before entering the tile ...) search_for_stop = route->get_count()-1; // now we just append the rest for( int i=end_route.get_count()-2; i>=0; i-- ) { route->append(end_route.at(i)); } } //DBG_MESSAGE("aircraft_t::calc_route()","departing=%i touchdown=%i suchen=%i total=%i state=%i",takeoff, touchdown, suchen, route->get_count()-1, state ); return true; } /* reserves runways (reserve true) or removes the reservation * finishes when reaching end tile or leaving the ground (end of runway) * @return true if the reservation is successful */ bool air_vehicle_t::block_reserver( uint32 start, uint32 end, bool reserve ) const { bool start_now = false; bool success = true; const route_t *route = cnv->get_route(); if(route->empty()) { return false; } bool clear_take_off = start<=takeoff; for( uint32 i=start; success && iget_count(); i++) { grund_t *gr = welt->lookup(route->at(i)); runway_t *sch1 = gr ? (runway_t *)gr->get_weg(air_wt) : NULL; if( !sch1 ) { if(reserve) { if(!start_now) { // touched down here start = i; } else { // most likely left the ground here ... end = i; break; } } } else { // we un-reserve also nonexistent tiles! (may happen during deletion) if(reserve) { start_now = true; sch1->add_convoi_reservation(cnv->self); if( !sch1->reserve(cnv->self,ribi_t::none) ) { if (clear_take_off) { // we check if the runway is really reserved if (air_vehicle_t *v = gr->find()) { if (v->get_flying_state() <= departing) { // unsuccessful => must un-reserve all success = false; end = i; break; } } } else { // unsuccessful => must un-reserve all success = false; end = i; break; } } // end of runway? if( i > start && (ribi_t::is_single( sch1->get_ribi_unmasked() ) || sch1->get_desc()->get_styp() != type_runway) ) { end = i; break; } } else { // we always unreserve everything sch1->unreserve(cnv->self); } } } // un-reserve if not successful if( !success && reserve ) { for( uint32 i=start; ilookup(route->at(i)); if (gr) { runway_t* sch1 = (runway_t *)gr->get_weg(air_wt); if (sch1) { sch1->unreserve(cnv->self); } } } return false; } if( reserve && endget_count(); i++ ) { if( grund_t *gr = welt->lookup(route->at(i)) ) { if( runway_t* sch1 = (runway_t *)gr->get_weg(air_wt) ) { if( sch1->get_desc()->get_styp()!=type_runway ) { break; } sch1->add_convoi_reservation( cnv->self ); } } } } return success; } // handles all the decisions on the ground an in the air bool air_vehicle_t::can_enter_tile(const grund_t *gr, sint32 &restart_speed, uint8) { restart_speed = -1; assert(gr); if(gr->obj_count()>250) { // too many objects here return false; } if (!leading) { return true; } if( route_index < takeoff && route_index > 1 && takeoffget_route()->get_count()-1 ) { // check, if tile occupied by a plane on ground if( route_index > 1 ) { for( uint8 i = 1; iobj_count(); i++ ) { obj_t *obj = gr->obj_bei(i); // we drive through other planes not taxiing for now ... if( obj->get_typ() == obj_t::air_vehicle && ((air_vehicle_t*)obj)->get_convoi()!=cnv && ((air_vehicle_t*)obj)->get_flying_state()==taxiing) { restart_speed = 0; return false; } } } // need to reserve runway? runway_t *rw = (runway_t *)gr->get_weg(air_wt); if(rw==NULL) { cnv->suche_neue_route(); return false; } // next tile a runway => then reserve if(rw->get_desc()->get_styp()==type_runway) { // try to reserve the runway if(!block_reserver(takeoff,takeoff+100,true)) { // runway already blocked ... restart_speed = 0; return false; } } return true; } if( state == taxiing ) { // enforce on ground for taxiing flying_height = 0; // we may need to unreserve the runway after leaving it if( route_index >= touchdown ) { runway_t *rw = (runway_t *)gr->get_weg(air_wt); // next tile a not runway => then unreserve if( rw == NULL || rw->get_desc()->get_styp() != type_runway || gr->is_halt() ) { block_reserver( touchdown, search_for_stop+1, false ); } } } if( route_index == takeoff && state == taxiing ) { // try to reserve the runway if not already done if(route_index==2 && !block_reserver(takeoff,takeoff+100,true)) { // runway blocked, wait at start of runway restart_speed = 0; return false; } // stop shortly at the end of the runway state = departing; restart_speed = 0; return false; } //DBG_MESSAGE("aircraft_t::can_enter_tile()","index %i<>%i",route_index,touchdown); // check for another circle ... if( route_index==(touchdown-3) ) { if( !block_reserver( touchdown, search_for_stop+1, true ) ) { route_index -= 16; return true; } state = landing; return true; } if( route_index==touchdown-16-3 && state!=circling ) { // just check, if the end of runway is free; we will wait there if( block_reserver( touchdown, search_for_stop+1, true ) ) { route_index += 16; // can land => set landing height state = landing; } else { // circle slowly next round state = circling; cnv->must_recalc_data(); if( leading ) { cnv->must_recalc_data_front(); } } } if(route_index==search_for_stop && state==landing && !target_halt.is_bound()) { // if we fail, we will wait in a step, much more simulation friendly // and the route finder is not re-entrant! if(!cnv->is_waiting()) { return false; } // nothing free here? if(find_route_to_stop_position()) { // stop reservation successful state = taxiing; return true; } restart_speed = 0; return false; } if(state==looking_for_parking) { state = taxiing; } if(state == taxiing && gr->is_halt() && gr->find()) { // the next step is a parking position. We do not enter, if occupied! restart_speed = 0; return false; } return true; } // this must also change the internal modes for the calculation void air_vehicle_t::enter_tile(grund_t* gr) { vehicle_t::enter_tile(gr); if( this->is_on_ground() ) { runway_t *w=(runway_t *)gr->get_weg(air_wt); if(w) { const int cargo = get_total_cargo(); w->book(cargo, WAY_STAT_GOODS); if (leading) { w->book(1, WAY_STAT_CONVOIS); } } } } air_vehicle_t::air_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : vehicle_t() { rdwr_from_convoi(file); if( file->is_loading() ) { static const vehicle_desc_t *last_desc = NULL; if(is_first) { last_desc = NULL; } // try to find a matching vehicle if(desc==NULL) { dbg->warning("aircraft_t::aircraft_t()", "try to find a fitting vehicle for %s.", !fracht.empty() ? fracht.front().get_name() : "passengers"); desc = vehicle_builder_t::get_best_matching(air_wt, 0, 101, 1000, 800, !fracht.empty() ? fracht.front().get_desc() : goods_manager_t::passengers, true, last_desc, is_last ); if(desc) { calc_image(); } } // update last desc if( desc ) { last_desc = desc; } } } air_vehicle_t::air_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cn) : vehicle_t(pos, desc, player) { cnv = cn; state = taxiing; flying_height = 0; target_height = pos.z; } air_vehicle_t::~air_vehicle_t() { // mark aircraft (after_image) dirty, since we have no "real" image const int raster_width = gfx->get_current_tile_raster_width(); sint16 yoff = tile_raster_scale_y(-flying_height-get_hoff()-2, raster_width); mark_image_dirty( image, yoff); mark_image_dirty( image, 0 ); } void air_vehicle_t::set_convoi(convoi_t *c) { DBG_MESSAGE("aircraft_t::set_convoi()","%p",c); if(leading && (uintptr_t)cnv > 1) { // free stop reservation route_t const& r = *cnv->get_route(); if(target_halt.is_bound()) { target_halt->unreserve_position(welt->lookup(r.back()), cnv->self); target_halt = halthandle_t(); } if (!r.empty()) { // free runway reservation if(route_index>=takeoff && route_index=touchdown-1 && state!=taxiing) { block_reserver( touchdown, search_for_stop+1, false ); } } } // maybe need to restore state? if(c!=NULL) { bool target=(bool)cnv; vehicle_t::set_convoi(c); if(leading) { if(target) { // reinitialize the target halt grund_t* const target=welt->lookup(cnv->get_route()->back()); target_halt = target->get_halt(); if(target_halt.is_bound()) { target_halt->reserve_position(target,cnv->self); } } // restore reservation if( grund_t *gr = welt->lookup(get_pos()) ) { if( weg_t *weg = gr->get_weg(air_wt) ) { if( weg->get_desc()->get_styp()==type_runway ) { // but only if we are on a runway ... if( route_index>=takeoff && route_index=touchdown-1 && state!=taxiing ) { block_reserver( touchdown, search_for_stop+1, true ); } } } } } } else { vehicle_t::set_convoi(NULL); } } schedule_t *air_vehicle_t::generate_new_schedule() const { return new airplane_schedule_t(); } void air_vehicle_t::rdwr_from_convoi(loadsave_t *file) { xml_tag_t t( file, "aircraft_t" ); // initialize as vehicle_t::rdwr_from_convoi calls get_image() if (file->is_loading()) { state = taxiing; flying_height = 0; } vehicle_t::rdwr_from_convoi(file); file->rdwr_enum(state); file->rdwr_short(flying_height); flying_height &= ~(TILE_HEIGHT_STEP-1); file->rdwr_short(target_height); file->rdwr_long(search_for_stop); file->rdwr_long(touchdown); file->rdwr_long(takeoff); } void air_vehicle_t::hop(grund_t* gr) { sint32 new_speed_limit = SPEED_UNLIMITED; sint32 new_friction = 0; // take care of in-flight height ... const sint16 h_cur = (sint16)get_pos().z*TILE_HEIGHT_STEP; const sint16 h_next = (sint16)pos_next.z*TILE_HEIGHT_STEP; // just mimick vehicle in front of me if (cnv->get_vehicle(0)!=this) { // get number in convoi uint16 previous = 1; while (cnv->get_vehicle(previous) != this) { previous++; assert(previous < cnv->get_vehicle_count()); } previous--; // hop to next tile vehicle_t::hop(gr); air_vehicle_t* before_veh = dynamic_cast(cnv->get_vehicle(previous)); target_height = before_veh->get_target_height(); current_friction = before_veh->get_frictionfactor(); speed_limit = before_veh->get_speed_limit(); flying_height = before_veh->get_flyingheight() + before_veh->get_hoff() + 2; state = before_veh->get_flying_state(); route_index = before_veh->get_route_index(); // friction factors and speed limit may have changed // TODO use the same logic as in vehicle_t::hop cnv->must_recalc_data(); return; } switch(state) { case departing: { flying_height = 0; target_height = h_cur; new_friction = max( 1, 28/(1+(route_index-takeoff)*2) ); // 9 5 4 3 2 2 1 1... // take off, when a) end of runway or b) last tile of runway or c) fast enough weg_t *weg=welt->lookup(get_pos())->get_weg(air_wt); if( (weg==NULL || // end of runway (broken runway) weg->get_desc()->get_styp()!=type_runway || // end of runway (grass now ... ) (route_index>takeoff+1 && ribi_t::is_single(weg->get_ribi_unmasked())) ) || // single ribi at end of runway cnv->get_akt_speed()>kmh_to_speed(desc->get_topspeed())/3 // fast enough ) { state = flying; new_friction = 1; block_reserver( takeoff, touchdown-1, false ); flying_height = h_cur - h_next; target_height = h_cur+TILE_HEIGHT_STEP*3; } break; } case circling: { new_speed_limit = kmh_to_speed(desc->get_topspeed())/3; new_friction = 4; // do not change height any more while circling flying_height += h_cur; flying_height -= h_next; break; } case flying: { // since we are at a tile border, round up to the nearest value flying_height += h_cur; if (flying_height < target_height) { flying_height = (flying_height + TILE_HEIGHT_STEP) & ~(TILE_HEIGHT_STEP - 1); } else if (flying_height > target_height) { flying_height = (flying_height - TILE_HEIGHT_STEP); } flying_height -= h_next; // did we have to change our flight height? if (target_height - h_next > TILE_HEIGHT_STEP * 5) { // Move down target_height -= TILE_HEIGHT_STEP * 2; } else if (target_height - h_next < TILE_HEIGHT_STEP * 2) { // Move up target_height += TILE_HEIGHT_STEP * 2; } break; } case landing: { new_speed_limit = kmh_to_speed(desc->get_topspeed())/3; // ==approach speed new_friction = 8; flying_height += h_cur; if( flying_height < target_height ) { flying_height = (flying_height+TILE_HEIGHT_STEP) & ~(TILE_HEIGHT_STEP-1); } else if( flying_height > target_height ) { flying_height = (flying_height-TILE_HEIGHT_STEP); } if (route_index >= touchdown) { // come down, now! target_height = h_next; // touchdown! if (flying_height==h_next) { const sint32 taxi_speed = kmh_to_speed( min( 60, desc->get_topspeed()/4 ) ); if( cnv->get_akt_speed() <= taxi_speed ) { new_speed_limit = taxi_speed; new_friction = 16; } else { const sint32 runway_left = search_for_stop - route_index; new_speed_limit = min( new_speed_limit, runway_left*runway_left*taxi_speed ); // ...approach 540 240 60 60 const sint32 runway_left_fr = max( 0, 6-runway_left ); new_friction = max( new_friction, min( desc->get_topspeed()/12, 4 + 4*(runway_left_fr*runway_left_fr+1) )); // ...8 8 12 24 44 72 108 152 } } } else { // runway is on this height const sint16 runway_height = cnv->get_route()->at(touchdown).z*TILE_HEIGHT_STEP; // we are too low, ascent asap if (flying_height < runway_height + TILE_HEIGHT_STEP) { target_height = runway_height + TILE_HEIGHT_STEP; } // too high, descent else if (flying_height + h_next - h_cur > runway_height + (sint16)(touchdown-route_index-1)*TILE_HEIGHT_STEP) { target_height = runway_height + (touchdown-route_index-1)*TILE_HEIGHT_STEP; } } flying_height -= h_next; break; } default: { new_speed_limit = kmh_to_speed( min( 60, desc->get_topspeed()/4 ) ); new_friction = 16; flying_height = 0; target_height = h_next; break; } } // hop to next tile vehicle_t::hop(gr); speed_limit = new_speed_limit; current_friction = new_friction; // friction factors and speed limit may have changed // TODO use the same logic as in vehicle_t::hop cnv->must_recalc_data(); } // some less acidentally reserved tiles void air_vehicle_t::leave_tile() { vehicle_t::leave_tile(); if (state == taxiing && cnv) { if (grund_t* gr = welt->lookup(get_pos())) { if(weg_t *w=gr->get_weg(air_wt)) { if (w->get_desc()->get_styp() != type_runway) { ((runway_t*)w)->unreserve(cnv->self); } } } } } // this routine will display the aircraft (if in flight) #ifdef MULTI_THREAD void air_vehicle_t::display_after(int xpos_org, int ypos_org, const sint8 clip_num) const #else void air_vehicle_t::display_after(int xpos_org, int ypos_org, bool is_global) const #endif { if( image != IMG_EMPTY && !is_on_ground() ) { int xpos = xpos_org, ypos = ypos_org; const int raster_width = gfx->get_current_tile_raster_width(); const sint16 z = get_pos().z; if( z + flying_height/TILE_HEIGHT_STEP - 1 > grund_t::underground_level ) { return; } const sint16 target = target_height - ((sint16)z*TILE_HEIGHT_STEP); sint16 current_flughohe = flying_height; if( current_flughohe < target ) { current_flughohe += (steps*TILE_HEIGHT_STEP) >> 8; } else if( current_flughohe > target ) { current_flughohe -= (steps*TILE_HEIGHT_STEP) >> 8; } sint16 hoff = get_hoff(); ypos += tile_raster_scale_y(get_yoff()-current_flughohe-hoff-2, raster_width); xpos += tile_raster_scale_x(get_xoff(), raster_width); get_screen_offset( xpos, ypos, raster_width ); gfx->swap_clip_rect(CLIP_NUM_VAR); // will be dirty // the aircraft!!! gfx->draw_color( image, xpos, ypos, get_owner_nr(), true, true/*get_flag(obj_t::dirty)*/ CLIP_NUM_PAR); #ifndef MULTI_THREAD vehicle_t::display_after( xpos_org, ypos_org - tile_raster_scale_y( current_flughohe - hoff - 2, raster_width ), is_global ); #endif gfx->swap_clip_rect(CLIP_NUM_VAR); } #ifdef MULTI_THREAD } void air_vehicle_t::display_overlay(int xpos_org, int ypos_org) const { if( image != IMG_EMPTY && !is_on_ground() ) { const int raster_width = gfx->get_current_tile_raster_width(); const sint16 z = get_pos().z; if( z + flying_height/TILE_HEIGHT_STEP - 1 > grund_t::underground_level ) { return; } const sint16 target = target_height - ((sint16)z*TILE_HEIGHT_STEP); sint16 current_flughohe = flying_height; if( current_flughohe < target ) { current_flughohe += (steps*TILE_HEIGHT_STEP) >> 8; } else if( current_flughohe > target ) { current_flughohe -= (steps*TILE_HEIGHT_STEP) >> 8; } vehicle_t::display_overlay( xpos_org, ypos_org - tile_raster_scale_y( current_flughohe - get_hoff() - 2, raster_width ) ); } #endif else if( is_on_ground() ) { // show loading tooltips on ground #ifdef MULTI_THREAD vehicle_t::display_overlay( xpos_org, ypos_org ); #else vehicle_t::display_after( xpos_org, ypos_org, is_global ); #endif } } const char *air_vehicle_t::get_removal_error(const player_t *player) { return is_on_ground() ? vehicle_t::get_removal_error(player) : NULL; } simutrans-124.5/src/simutrans/vehicle/air_vehicle.h000066400000000000000000000102121520675460500224360ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_AIR_VEHICLE_H #define VEHICLE_AIR_VEHICLE_H #include "vehicle.h" /** * A class for aircrafts. Manages the look of the vehicles * and the navigability of tiles. * @see vehicle_t */ class air_vehicle_t : public vehicle_t { public: enum flight_state { taxiing = 0, departing = 1, flying = 2, landing = 3, looking_for_parking = 4, circling = 5, taxiing_to_halt = 6 }; private: // only used for is_target() (do not need saving) ribi_t::ribi approach_dir; // only used for route search and approach vectors of get_ribi() (do not need saving) koord3d search_start; koord3d search_end; flight_state state; // functions needed for the search without destination from find_route sint16 flying_height; sint16 target_height; uint32 search_for_stop, touchdown, takeoff; protected: // jumps to next tile and correct the height ... void hop(grund_t*) OVERRIDE; bool check_next_tile(const grund_t *bd) const OVERRIDE; void enter_tile(grund_t*) OVERRIDE; void leave_tile() OVERRIDE; bool block_reserver( uint32 start, uint32 end, bool reserve ) const; // find a route and reserve the stop position bool find_route_to_stop_position(); public: air_vehicle_t(loadsave_t *file, bool is_first, bool is_last); air_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cnv); // start and schedule // to shift the events around properly void get_event_index( flight_state &state_, uint32 &takeoff_, uint32 &stopsearch_, uint32 &landing_ ) { state_ = state; takeoff_ = takeoff; stopsearch_ = search_for_stop; landing_ = touchdown; } void set_event_index( flight_state state_, uint32 takeoff_, uint32 stopsearch_, uint32 landing_ ) { state = state_; takeoff = takeoff_; search_for_stop = stopsearch_; touchdown = landing_; } // since we are drawing ourselves, we must mark ourselves dirty during deletion ~air_vehicle_t(); waytype_t get_waytype() const OVERRIDE { return air_wt; } // returns true for the way search to an unknown target. bool is_target(const grund_t *,const grund_t *) const OVERRIDE; // return valid direction ribi_t::ribi get_ribi(const grund_t* ) const OVERRIDE; // how expensive to go here (for way search) int get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const OVERRIDE; bool can_enter_tile(const grund_t *gr_next, sint32 &restart_speed, uint8) OVERRIDE; void set_convoi(convoi_t *c) OVERRIDE; bool calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route) OVERRIDE; typ get_typ() const OVERRIDE { return air_vehicle; } schedule_t *generate_new_schedule() const OVERRIDE; void rdwr_from_convoi(loadsave_t *file) OVERRIDE; int get_flyingheight() const {return flying_height-get_hoff()-2;} sint16 get_target_height() const { return target_height; } flight_state get_flying_state() const { return state; } // image: when flying empty, on ground the plane image_id get_image() const OVERRIDE {return !is_on_ground() ? IMG_EMPTY : image;} // image: when flying the shadow, on ground empty image_id get_outline_image() const OVERRIDE {return !is_on_ground() ? image : IMG_EMPTY;} // shadow has black color (when flying) FLAGGED_PIXVAL get_outline_colour() const OVERRIDE {return !is_on_ground() ? TRANSPARENT75_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_BLACK) : 0;} #ifdef MULTI_THREAD // this draws the "real" aircrafts (when flying) void display_after(int xpos, int ypos, const sint8 clip_num) const OVERRIDE; // this routine will display a tooltip for lost, on depot order, and stuck vehicles void display_overlay(int xpos, int ypos) const OVERRIDE; #else // this draws the "real" aircrafts (when flying) void display_after(int xpos, int ypos, bool dirty) const OVERRIDE; #endif void calc_friction(const grund_t*) OVERRIDE {} bool is_on_ground() const { return flying_height==0 && !(state==circling || state==flying); } const char *get_removal_error(const player_t *player) OVERRIDE; bool is_flying() const OVERRIDE { return !is_on_ground(); } }; #endif simutrans-124.5/src/simutrans/vehicle/movingobj.cc000066400000000000000000000223641520675460500223270ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include "../simdebug.h" #include "../world/simworld.h" #include "../obj/simobj.h" #include "../display/simimg.h" #include "../player/simplay.h" #include "../simtypes.h" #include "../simunits.h" #include "../ground/grund.h" #include "../descriptor/groundobj_desc.h" #include "../utils/cbuffer.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../dataobj/environment.h" #include "../obj/baum.h" #include "movingobj.h" /******************************** static stuff: desc management ****************************************************************/ freelist_iter_tpl movingobj_t::fl; vector_tpl movingobj_t::movingobj_typen(0); stringhashtable_tpl movingobj_t::desc_table; bool compare_groundobj_desc(const groundobj_desc_t* a, const groundobj_desc_t* b); bool movingobj_t::successfully_loaded() { movingobj_typen.reserve(desc_table.get_count()); for(auto const& i : desc_table) { movingobj_typen.insert_ordered(i.value, compare_groundobj_desc); } // iterate again to assign the index for(auto const& i : desc_table) { i.value->index = movingobj_typen.index_of(i.value); } if(desc_table.empty()) { movingobj_typen.append( NULL ); DBG_MESSAGE("movingobj_t", "No movingobj found - feature disabled"); } return true; } bool movingobj_t::register_desc(groundobj_desc_t *desc) { // remove duplicates if (groundobj_desc_t *old = desc_table.remove( desc->get_name() ) ) { delete old; } desc_table.put(desc->get_name(), desc ); return true; } /** also checks for distribution values */ const groundobj_desc_t *movingobj_t::random_movingobj_for_climate(climate cl) { // none there if( desc_table.empty() ) { return NULL; } int weight = 0; for(groundobj_desc_t const* const i : movingobj_typen) { if (i->is_allowed_climate(cl) ) { weight += i->get_distribution_weight(); } } // now weight their distribution if (weight > 0) { const int w=simrand(weight); weight = 0; for(groundobj_desc_t const* const i : movingobj_typen) { if (i->is_allowed_climate(cl)) { weight += i->get_distribution_weight(); if(weight>=w) { return i; } } } } return NULL; } /******************************* end of static ******************************************/ // recalculates only the seasonal image void movingobj_t::calc_image() { const groundobj_desc_t *desc=get_desc(); const uint8 seasons = desc->get_seasons()-1; uint8 season = 0; switch( seasons ) { case 0: { // summer only season = 0; break; } case 1: { // summer, snow season = welt->get_snowline() <= get_pos().z || welt->get_climate( get_pos().get_2d() ) == arctic_climate; break; } case 2: { // summer, winter, snow season = welt->get_snowline() <= get_pos().z || welt->get_climate( get_pos().get_2d() ) == arctic_climate ? 2 : welt->get_season() == 1; break; } default: { if( welt->get_snowline() <= get_pos().z || welt->get_climate( get_pos().get_2d() ) == arctic_climate ) { season = seasons; } else { // resolution 1/8th month (0..95) const uint32 yearsteps = (welt->get_current_month()%12)*8 + ((welt->get_ticks()>>(welt->ticks_per_world_month_shift-3))&7) + 1; season = (seasons * yearsteps - 1) / 96; } break; } } set_image( get_desc()->get_image( season, ribi_t::get_dir(get_direction()) )->get_id() ); } movingobj_t::movingobj_t(loadsave_t *file) : vehicle_base_t() { rdwr(file); } movingobj_t::movingobj_t(koord3d pos, const groundobj_desc_t *b ) : vehicle_base_t(pos) { movingobjtype = movingobj_typen.index_of(b); weg_next = 0; timetochange = 0; // will do random direct change anyway during next step direction = calc_set_direction( koord3d(0,0,0), koord3d(koord::west,0) ); calc_image(); } bool movingobj_t::check_season(const bool) { const image_id old_image = get_image(); calc_image(); if( get_image() != old_image ) { mark_image_dirty( get_image(), 0 ); } return true; } void movingobj_t::rdwr(loadsave_t *file) { xml_tag_t d( file, "movingobj_t" ); vehicle_base_t::rdwr(file); file->rdwr_enum(direction); if (file->is_loading()) { // restore dxdy information dx = dxdy[ ribi_t::get_dir(direction)*2]; dy = dxdy[ ribi_t::get_dir(direction)*2+1]; } file->rdwr_byte(steps); file->rdwr_byte(steps_next); pos_next.rdwr(file); koord p = pos_next_next.get_2d(); p.rdwr(file); if(file->is_loading()) { pos_next_next = koord3d(p, 0); // z-coordinate will be restored in hop_check } file->rdwr_short(timetochange); if(file->is_saving()) { const char *s = get_desc()->get_name(); file->rdwr_str(s); } else { char bname[128]; file->rdwr_str(bname, lengthof(bname)); groundobj_desc_t *desc = desc_table.get(bname); if( desc_table.empty() || desc==NULL ) { movingobjtype = simrand(movingobj_typen.get_count()); } else { movingobjtype = (uint8)desc->get_index(); } // if not there, desc will be zero use_calc_height = true; } if (file->is_version_atleast(122, 1)) { file->rdwr_long(weg_next); } else if (file->is_loading()) { weg_next = 0; } } void movingobj_t::show_info() { if(env_t::tree_info) { obj_t::show_info(); } } void movingobj_t::info(cbuffer_t & buf) const { obj_t::info(buf); buf.append(translator::translate(get_desc()->get_name())); if (char const* const maker = get_desc()->get_copyright()) { buf.append("\n"); buf.printf(translator::translate("Constructed by %s"), maker); } buf.append("\n"); buf.append(translator::translate("cost for removal")); char buffer[128]; money_to_string( buffer, get_desc()->get_price()/100.0 ); buf.append( buffer ); } void movingobj_t::cleanup(player_t *player) { player_t::book_construction_costs(player, -get_desc()->get_price(), get_pos().get_2d(), ignore_wt); mark_image_dirty( get_image(), 0 ); } sync_result movingobj_t::sync_step(uint32 delta_t) { weg_next += get_desc()->get_speed() * delta_t; weg_next -= do_drive( weg_next ); return SYNC_OK; } /* essential to find out about next step * returns true, if we can go here * (identical to fahrer) */ bool movingobj_t::check_next_tile( const grund_t *gr ) const { if(gr==NULL) { // no ground => we cannot check further return false; } const groundobj_desc_t *desc = get_desc(); if( !desc->is_allowed_climate( welt->get_climate(gr->get_pos().get_2d()) ) ) { // not an allowed climate zone! return false; } if(desc->get_waytype()==road_wt) { // can cross roads if(gr->get_typ()!=grund_t::boden || !slope_t::is_way(gr->get_grund_hang())) { return false; } // only on roads, do not walk in cities if(gr->hat_wege() && (!gr->hat_weg(road_wt) || gr->get_weg(road_wt)->hat_gehweg())) { return false; } if(!desc->can_build_trees_here()) { return gr->find()==NULL; } } else if(desc->get_waytype()==air_wt) { // avoid towns to avoid flying through houses return gr->get_typ()==grund_t::boden || gr->get_typ()==grund_t::wasser; } else if(desc->get_waytype()==water_wt) { // floating object return gr->get_typ()==grund_t::wasser || gr->hat_weg(water_wt); } else if(desc->get_waytype()==ignore_wt) { // crosses nothing if(!gr->ist_natur() || !slope_t::is_way(gr->get_grund_hang())) { return false; } if(!desc->can_build_trees_here()) { return gr->find()==NULL; } } return true; } grund_t* movingobj_t::hop_check() { /* since we may be going diagonal without any road * determining the next koord is a little tricky: * If it is a diagonal, pos_next_next is calculated from current pos, * Else pos_next_next is a single step from pos_next. * otherwise objects would jump left/right on some diagonals */ if (timetochange != 0) { koord k(direction); if(k.x&k.y) { pos_next_next = get_pos() + k; } else { pos_next_next = pos_next + k; } grund_t *gr = welt->lookup_kartenboden(pos_next_next.get_2d()); if (check_next_tile(gr)) { pos_next_next = gr->get_pos(); } else { timetochange = 0; } } if (timetochange==0) { // direction change needed timetochange = simrand(speed_to_kmh(get_desc()->get_speed())/3); const koord pos=pos_next.get_2d(); const grund_t *to[4]; uint8 until=0; // find all tiles we can go for( int i=0; i<4; i++ ) { const grund_t *check = welt->lookup_kartenboden(pos+koord::nesw[i]); if(check_next_tile(check) && check->get_pos()!=get_pos()) { to[until++] = check; } } // if nothing found, return if(until==0) { pos_next_next = get_pos(); // (better would be destruction?) } else { // else prepare for direction change const grund_t *next = to[simrand(until)]; pos_next_next = next->get_pos(); } } else { timetochange--; } return welt->lookup(pos_next); } void movingobj_t::hop(grund_t* gr) { leave_tile(); if(pos_next.get_2d()==get_pos().get_2d()) { direction = ribi_t::backward(direction); dx = -dx; dy = -dy; calc_image(); } else { ribi_t::ribi old_dir = direction; direction = calc_set_direction( get_pos(), pos_next_next ); if(old_dir!=direction) { calc_image(); } } set_pos(pos_next); enter_tile(gr); // next position pos_next = pos_next_next; } simutrans-124.5/src/simutrans/vehicle/movingobj.h000066400000000000000000000044551520675460500221720ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_MOVINGOBJ_H #define VEHICLE_MOVINGOBJ_H #include "../tpl/stringhashtable_tpl.h" #include "../tpl/vector_tpl.h" #include "../tpl/freelist_iter_tpl.h" #include "../descriptor/groundobj_desc.h" #include "../simcolor.h" #include "vehicle_base.h" /** * moving stuff like sheep or birds */ class movingobj_t : public vehicle_base_t { private: /// distance to move uint32 weg_next; /// direction will change after this time uint16 timetochange; /// type of object, index into movingobj_typen uint8 movingobjtype; koord3d pos_next_next; /// static table to find desc by name static stringhashtable_tpl desc_table; /// static vector for fast lookup of desc static vector_tpl movingobj_typen; static freelist_iter_tpl fl; // if not declared static, it would consume 4 bytes due to empty class nonzero rules protected: void rdwr(loadsave_t *file) OVERRIDE; void calc_image() OVERRIDE; public: static bool register_desc(groundobj_desc_t *desc); static bool successfully_loaded(); static const groundobj_desc_t *random_movingobj_for_climate(climate cl); movingobj_t(loadsave_t *file); movingobj_t(koord3d pos, const groundobj_desc_t *); sync_result sync_step(uint32 delta_t); static void sync_handler(uint32 delta_t) { fl.sync_step(delta_t); } void* operator new(size_t) { return fl.gimme_node(); } void operator delete(void* p) { return fl.putback_node(p); } // always free virtual bool check_next_tile(const grund_t *) const; grund_t* hop_check() OVERRIDE; void hop(grund_t* gr) OVERRIDE; waytype_t get_waytype() const OVERRIDE { return get_desc()->get_waytype(); } const char *get_name() const OVERRIDE {return "Movingobj";} typ get_typ() const OVERRIDE { return movingobj; } /** * Called whenever the season or snowline height changes * return false and the obj_t will be deleted */ bool check_season(const bool) OVERRIDE; void show_info() OVERRIDE; void info(cbuffer_t & buf) const OVERRIDE; void cleanup(player_t *player) OVERRIDE; const groundobj_desc_t* get_desc() const { return movingobj_typen[movingobjtype]; } bool is_flying() const OVERRIDE { return get_desc()->get_waytype()==air_wt; } }; #endif simutrans-124.5/src/simutrans/vehicle/overtaker.h000066400000000000000000000026521520675460500221770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_OVERTAKER_H #define VEHICLE_OVERTAKER_H #include "../simtypes.h" #include "../simunits.h" /** * Class dealing with overtaking * It is the superclass of convois and city cars (private_car_t) */ class overtaker_t { protected: /* if >0, number of tiles for overtaking left * if <0, number of tiles that is prevented being overtaken * (other vehicle is overtaking us) */ sint8 tiles_overtaking; sint8 diff; sint32 max_power_speed; // max achievable speed at current power/weight public: overtaker_t():tiles_overtaking(0), diff(0), max_power_speed(SPEED_UNLIMITED) {} virtual ~overtaker_t() {} bool is_overtaking() const { return diff < 0; } bool is_overtaken() const { return diff > 0; } bool can_be_overtaken() const { return tiles_overtaking == 0; } void set_tiles_overtaking(sint8 v) { tiles_overtaking = v; diff = 0; if(tiles_overtaking>0) { diff --; } else if(tiles_overtaking<0) { diff ++; } } // change counter for overtaking void update_tiles_overtaking() { tiles_overtaking += diff; if(tiles_overtaking==0) { diff = 0; } } // since citycars and convois can react quite different virtual bool can_overtake(overtaker_t *other_overtaker, sint32 other_speed, sint16 steps_other) = 0; sint32 get_max_power_speed() const { return max_power_speed; } }; #endif simutrans-124.5/src/simutrans/vehicle/pedestrian.cc000066400000000000000000000230331520675460500224650ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "pedestrian.h" #include "../simdebug.h" #include "../world/simworld.h" #include "../display/simimg.h" #include "../utils/simrandom.h" #include "../ground/grund.h" #include "../dataobj/loadsave.h" #include "../dataobj/translator.h" #include "../utils/cbuffer.h" #include "../descriptor/pedestrian_desc.h" #include freelist_iter_tpl pedestrian_t::fl; static uint32 const strecke[] = { 6000, 11000, 15000, 20000, 25000, 30000, 35000, 40000 }; static weighted_vector_tpl list_timeline; stringhashtable_tpl pedestrian_t::table; bool pedestrian_t::register_desc(const pedestrian_desc_t *desc) { if(const pedestrian_desc_t* old = table.remove(desc->get_name()) ) { delete old; } table.put(desc->get_name(), desc); return true; } bool pedestrian_t::successfully_loaded() { if (table.empty()) { DBG_MESSAGE("pedestrian_t", "No pedestrians found - feature disabled"); } return true; } static bool compare_pedestrian_desc(const pedestrian_desc_t* a, const pedestrian_desc_t* b) { int diff = a->get_intro_year_month() - b->get_intro_year_month(); if (diff == 0) { /* same Level - we introduce an artificial, but unique resort * on the induced name. */ diff = strcmp(a->get_name(), b->get_name()); } return diff < 0; } pedestrian_t::pedestrian_t(loadsave_t *file) : road_user_t() { animation_steps = 0; on_left = false; steps_offset = 0; rdwr(file); if(desc) { ped_offset = desc->get_offset(); } calc_disp_lane(); } void pedestrian_t::build_timeline_list(karte_t *welt) { // this list will contain all citycars list_timeline.clear(); vector_tpl temp_liste(0); if( !table.empty() ) { const int month_now = welt->get_current_month(); // check for every citycar, if still ok ... for(auto const& i : table) { pedestrian_desc_t const* const info = i.value; const int intro_month = info->get_intro_year_month(); const int retire_month = info->get_retire_year_month(); if (!welt->use_timeline() || (intro_month <= month_now && month_now < retire_month)) { temp_liste.insert_ordered( info, compare_pedestrian_desc ); } } } list_timeline.reserve( temp_liste.get_count() ); for(pedestrian_desc_t const* const i : temp_liste) { list_timeline.append(i, i->get_distribution_weight()); } } bool pedestrian_t::list_empty() { return list_timeline.empty(); } pedestrian_t::pedestrian_t(grund_t *gr) : road_user_t(gr, sim_async_rand(65535)), desc(pick_any_weighted_async(list_timeline)) { animation_steps = 0; on_left = sim_async_rand(2) > 0; steps_offset = 0; time_to_life = pick_any_async(strecke); ped_offset = desc->get_offset(); calc_image(); calc_disp_lane(); } void pedestrian_t::calc_image() { set_image(desc->get_image_id(ribi_t::get_dir(get_direction()))); } image_id pedestrian_t::get_image() const { if (desc->get_steps_per_frame() > 0) { uint16 frame = ((animation_steps + steps) / desc->get_steps_per_frame()) % desc->get_animation_count(ribi_t::get_dir(direction)); return desc->get_image_id(ribi_t::get_dir(get_direction()), frame); } else { return image; } } void pedestrian_t::rdwr(loadsave_t *file) { xml_tag_t f( file, "fussgaenger_t" ); road_user_t::rdwr(file); if(!file->is_loading()) { const char *s = desc->get_name(); file->rdwr_str(s); } else { char s[256]; file->rdwr_str(s, lengthof(s)); desc = table.get(s); // unknown pedestrian => create random new one if(desc == NULL && !list_timeline.empty() ) { desc = pick_any_weighted_async(list_timeline); } } if(file->is_version_less(89, 4)) { time_to_life = pick_any_async(strecke); } if (file->is_version_atleast(120, 6)) { file->rdwr_short(steps_offset); file->rdwr_bool(on_left); } if (file->is_loading()) { calc_disp_lane(); } } void pedestrian_t::calc_disp_lane() { // walking in the back or the front disp_lane = on_left ? 0 : 4; if ((direction & ribi_t::south) || direction == ribi_t::west) disp_lane ^= 4; } void pedestrian_t::rotate90() { road_user_t::rotate90(); calc_disp_lane(); } // create a number (count) of pedestrians (if possible) void pedestrian_t::generate_pedestrians_at(grund_t *bd, int &count) { if (list_timeline.empty()) { return; } // allow also pedestrians on any road including crossings // the complex bus center in front of the station is almost empty ... if (bd->get_weg_ribi(road_wt)) { // we create maximal 4 pedestrians here for performance reasons for (int i = 0; i < 4 && count > 0; i++) { if (bd->obj_count() >= 120) { // tile too full return; } pedestrian_t* fg = new pedestrian_t(bd); if (fg->time_to_life > 0 && bd->obj_add(fg) != 0) { fg->calc_height(bd); uint32 separate_pedestrians = (bd->obj_count()*23) << YARDS_PER_VEHICLE_STEP_SHIFT; // walk a little fg->sync_step( (separate_pedestrians % (255 << YARDS_PER_VEHICLE_STEP_SHIFT))/128 ); count--; } else { // delete it, if we could not put it on the map fg->set_flag(obj_t::not_on_map); // do not try to delete it from sync-list fg->time_to_life = 0; return; // it is pointless to try for more here } } } } int pedestrian_t::generate_pedestrians_near(grund_t *gr, int count) { generate_pedestrians_at(gr, count); for (int i = 0; i < 4 && count>0; i++) { if (grund_t *gr_next = welt->lookup_kartenboden(gr->get_pos().get_2d() + koord::nesw[i])) { generate_pedestrians_at(gr_next, count); } } return count; } sync_result pedestrian_t::sync_step(uint32 delta_t) { time_to_life -= delta_t; if (time_to_life>0) { weg_next += 128*delta_t; weg_next -= do_drive( weg_next ); return time_to_life>0 ? SYNC_OK : SYNC_DELETE; } return SYNC_DELETE; } grund_t* pedestrian_t::hop_check() { grund_t *from = welt->lookup(pos_next); if(!from) { time_to_life = 0; return NULL; } // find the allowed directions const weg_t *weg = from->get_weg(road_wt); if(weg==NULL) { // no road anymore: destroy it time_to_life = 0; return NULL; } if (from->obj_count() >= 240) { time_to_life = 0; return NULL; // target tile full, just die } return from; } void pedestrian_t::hop(grund_t *gr) { const koord3d from = get_pos(); // hop leave_tile(); set_pos(gr->get_pos()); // no need to call enter_tile() // if this fails, the target tile is full, but this should already have been checked in hop_check const bool ok = gr->obj_add(this); assert(ok); (void)ok; // determine pos_next const weg_t *weg = gr->get_weg(road_wt); // new target grund_t *to = NULL; // current single direction ribi_t::ribi current_direction = ribi_type(from, get_pos()); // ribi opposite to current direction ribi_t::ribi reverse_direction = ribi_t::reverse_single( current_direction ); // all possible directions ribi_t::ribi ribi = weg->get_ribi_unmasked() & (~reverse_direction); // randomized offset const uint8 offset = (ribi > 0 && ribi_t::is_single(ribi)) ? 0 : sim_async_rand(4); ribi_t::ribi new_direction = ribi_t::none; for(uint r = 0; r < 4; r++) { new_direction = ribi_t::nesw[ (r+offset) & 3]; if( (ribi & new_direction)!=0 && gr->get_neighbour(to, road_wt, new_direction) ) { // this is our next target break; } } steps_offset = 0; if (to) { pos_next = to->get_pos(); if ( ribi_t::is_twoway(weg->get_ribi_unmasked()) || new_direction == current_direction ) { // going straight direction = calc_set_direction(from, pos_next); } else { ribi_t::ribi turn_ribi = on_left ? ribi_t::rotate90l(current_direction) : ribi_t::rotate90(current_direction); if (turn_ribi == new_direction) { // short diagonal (turn but do not cross street) direction = calc_set_direction(from, pos_next); steps_next = (ped_offset*181) / 128; // * sqrt(2) steps_offset = 0; } else { // do not cross street diagonally, change side on_left = !on_left; direction = calc_set_direction(get_pos(), pos_next); } } } else { // dead end, turn pos_next = from; direction = calc_set_direction(get_pos(), pos_next); steps_offset = VEHICLE_STEPS_PER_TILE - ped_offset; steps_next = ped_offset; on_left = !on_left; } calc_disp_lane(); // carry over remainder to next tile for continuous animation during straight movement uint16 steps_per_animation = desc->get_steps_per_frame() * desc->get_animation_count(ribi_t::get_dir(direction)); if (steps_per_animation > 0) { animation_steps = (animation_steps + steps_next + 1) % steps_per_animation; } else { animation_steps = 0; } calc_image(); } void pedestrian_t::get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const { // vehicles needs finer steps to appear smoother sint32 display_steps = (uint32)(steps + steps_offset)*(uint16)raster_width; if( dx && dy ) { display_steps &= 0xFFFFFC00; } else { display_steps = (display_steps*diagonal_multiplier)>>10; } xoff += (display_steps*dx) >> 10; yoff += ((display_steps*dy) >> 10) + (get_hoff(raster_width))/(4*16); if (on_left) { sint32 left_off_steps = ( (VEHICLE_STEPS_PER_TILE - 2*ped_offset)*(uint16)raster_width ) & 0xFFFFFC00; if (dx*dy==0) { // diagonal left_off_steps /= 2; } // turn left (dx,dy) increments xoff += (left_off_steps*2*dy) >> 10; yoff -= (left_off_steps*dx) >> (10+1); } } void pedestrian_t::info(cbuffer_t & buf) const { char const* const owner = translator::translate("Kein Besitzer\n"); buf.append(owner); if (char const* const maker = get_desc()->get_copyright()) { buf.printf(translator::translate("Constructed by %s"), maker); } } simutrans-124.5/src/simutrans/vehicle/pedestrian.h000066400000000000000000000043631520675460500223340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_PEDESTRIAN_H #define VEHICLE_PEDESTRIAN_H #include "simroadtraffic.h" #include "../tpl/freelist_iter_tpl.h" class pedestrian_desc_t; class grund_t; /** * Pedestrians also are road users. * @see road_user_t */ class pedestrian_t : public road_user_t { private: static stringhashtable_tpl table; private: const pedestrian_desc_t *desc; uint16 animation_steps; uint16 steps_offset; uint16 ped_offset; bool on_left; bool list_empty(); static freelist_iter_tpl fl; // if not declared static, it would consume 4 bytes due to empty class nonzero rules static void generate_pedestrians_at(grund_t *gr, int& count); protected: void rdwr(loadsave_t *file) OVERRIDE; void calc_image() OVERRIDE; /** * Creates pedestrian at position given by @p gr. * Does not add pedestrian to the tile! */ pedestrian_t(grund_t *gr); public: pedestrian_t(loadsave_t *file); static void sync_handler(uint32 delta_t) { fl.sync_step(delta_t); } const pedestrian_desc_t *get_desc() const { return desc; } const char *get_name() const OVERRIDE {return "Fussgaenger";} typ get_typ() const OVERRIDE { return pedestrian; } void info(cbuffer_t & buf) const OVERRIDE; sync_result sync_step(uint32 delta_t); ///@ returns true if pedestrian walks on the left side of the road bool is_on_left() const { return on_left; } void calc_disp_lane(); void rotate90() OVERRIDE; // overloaded to enable animations image_id get_image() const OVERRIDE; void get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const OVERRIDE; grund_t* hop_check() OVERRIDE; void hop(grund_t* gr) OVERRIDE; void* operator new(size_t) { return fl.gimme_node(); } void operator delete(void* p) { return fl.putback_node(p); } // class register functions static bool register_desc(const pedestrian_desc_t *desc); static bool successfully_loaded(); /** * Tries to generate some pedestrians on the square and the * adjacent squares. Return actual number of generated * pedestrians. */ static int generate_pedestrians_near(grund_t *gr, int count); static void build_timeline_list( karte_t *welt ); }; #endif simutrans-124.5/src/simutrans/vehicle/rail_vehicle.cc000066400000000000000000000735771520675460500227770ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "rail_vehicle.h" #include "../builder/goods_manager.h" #include "../builder/vehikelbauer.h" #include "../simware.h" #include "../simconvoi.h" #include "../world/simworld.h" #include "../obj/way/schiene.h" #include "../obj/depot.h" #include "../obj/roadsign.h" #include "../obj/signal.h" #include "../dataobj/schedule.h" #include "../obj/crossing.h" /* from now on rail vehicles (and other vehicles using blocks) */ rail_vehicle_t::rail_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : vehicle_t() { vehicle_t::rdwr_from_convoi(file); if( file->is_loading() ) { static const vehicle_desc_t *last_desc = NULL; if(is_first) { last_desc = NULL; } // try to find a matching vehicle if(desc==NULL) { int power = (is_first || fracht.empty() || fracht.front() == goods_manager_t::none) ? 500 : 0; const goods_desc_t* w = fracht.empty() ? goods_manager_t::none : fracht.front().get_desc(); dbg->warning("rail_vehicle_t::rail_vehicle_t()","try to find a fitting vehicle for %s.", power>0 ? "engine": w->get_name() ); if(last_desc!=NULL && last_desc->can_follow(last_desc) && last_desc->get_freight_type()==w && (!is_last || last_desc->get_trailer(0)==NULL)) { // same as previously ... desc = last_desc; } else { // we have to search desc = vehicle_builder_t::get_best_matching(get_waytype(), 0, w!=goods_manager_t::none?5000:0, power, speed_to_kmh(speed_limit), w, false, last_desc, is_last ); } if(desc) { DBG_MESSAGE("rail_vehicle_t::rail_vehicle_t()","replaced by %s",desc->get_name()); calc_image(); } else { dbg->error("rail_vehicle_t::rail_vehicle_t()","no matching desc found for %s!",w->get_name()); } if (!fracht.empty() && fracht.front().amount == 0) { // this was only there to find a matching vehicle fracht.remove_first(); } } // update last desc if( desc ) { last_desc = desc; } } } rail_vehicle_t::rail_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cn) : vehicle_t(pos, desc, player) { cnv = cn; } rail_vehicle_t::~rail_vehicle_t() { if (cnv && leading) { route_t const& r = *cnv->get_route(); if (!r.empty() && route_index < r.get_count()) { // free all reserved blocks route_t::index_t dummy; block_reserver(&r, cnv->back()->get_route_index(), dummy, dummy, target_halt.is_bound() ? 100000 : 1, false, false); } } grund_t *gr = welt->lookup(get_pos()); if(gr) { schiene_t * sch = (schiene_t *)gr->get_weg(get_waytype()); if(sch) { sch->unreserve(this); } } } void rail_vehicle_t::set_convoi(convoi_t *c) { if(c!=cnv) { DBG_MESSAGE("rail_vehicle_t::set_convoi()","new=%p old=%p",c,cnv); if(leading) { if(cnv!=NULL && cnv!=(convoi_t *)1) { // free route from old convoi route_t const& r = *cnv->get_route(); if( !r.empty() && route_index + 1U < r.get_count() - 1 ) { route_t::index_t dummy; block_reserver(&r, cnv->back()->get_route_index(), dummy, dummy, 100000, false, false); target_halt = halthandle_t(); } } else if( c->get_next_reservation_index()==0 ) { assert(c!=NULL); // eventually search new route route_t const& r = *c->get_route(); if( (r.get_count()<=route_index || r.empty() || get_pos()==r.back()) && c->get_state()!=convoi_t::INITIAL && c->get_state()!=convoi_t::LOADING && c->get_state()!=convoi_t::SELF_DESTRUCT ) { check_for_finish = true; dbg->warning("rail_vehicle_t::set_convoi()", "convoi %i had a too high route index! (%i of max %i)", c->self.get_id(), route_index, r.get_count() - 1); } // set default next stop index c->set_next_stop_index( max(route_index,1)-1 ); // need to reserve new route? if( !check_for_finish && c->get_state()!=convoi_t::SELF_DESTRUCT && (c->get_state()==convoi_t::DRIVING || c->get_state()>=convoi_t::LEAVING_DEPOT) ) { sint32 num_index = cnv==(convoi_t *)1 ? 1001 : 0; // only during loadtype: cnv==1 indicates, that the convoi did reserve a stop route_t::index_t next_signal, next_crossing; cnv = c; if( block_reserver(&r, max(route_index,1)-1, next_signal, next_crossing, num_index, true, false) ) { c->set_next_stop_index( next_signal>next_crossing ? next_crossing : next_signal ); } } } } vehicle_t::set_convoi(c); } } // need to reset halt reservation (if there was one) bool rail_vehicle_t::calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route) { if(leading && route_indexget_route()->get_count()) { // free all reserved blocks route_t::index_t dummy; block_reserver(cnv->get_route(), cnv->back()->get_route_index(), dummy, dummy, target_halt.is_bound() ? 100000 : 1, false, true); } cnv->set_next_reservation_index( 0 ); // nothing to reserve target_halt = halthandle_t(); // no block reserved // use length 8888 tiles to advance to the end of all stations const uint16 convoy_length = world()->get_settings().get_stop_halt_as_scheduled() ? cnv->get_tile_length() : 8888; return route->calc_route(welt, start, ziel, this, max_speed, convoy_length ); } bool rail_vehicle_t::check_next_tile(const grund_t *bd) const { schiene_t const* const sch = obj_cast(bd->get_weg(get_waytype())); if( !sch ) { return false; } if (sch->get_max_speed() == 0) { // city walls etc. return false; } // check for electrification const bool needs_electric = cnv != NULL && cnv->needs_electrification(); if( needs_electric && !sch->is_electrified() ) { return false; } if (depot_t* depot = bd->get_depot()) { if (depot->get_waytype() != desc->get_waytype() || depot->get_owner() != get_owner()) { return false; } } // now check for special signs if (sch->has_sign()) { const roadsign_t* rs = bd->find(); if (rs->get_desc()->get_wtyp() == get_waytype()) { if (cnv != NULL && rs->get_desc()->get_min_speed() > 0 && rs->get_desc()->get_min_speed() > cnv->get_min_top_speed()) { // below speed limit return false; } if (rs->get_desc()->is_private_way() && (rs->get_player_mask() & (1 << get_owner_nr())) == 0) { // private road return false; } } } if (target_halt.is_bound() && cnv->is_waiting()) { // we are searching a stop here: // ok, we can go where we already are ... if (bd->get_pos() == get_pos()) { return true; } // we cannot pass an end of choose area if (sch->has_sign()) { const roadsign_t* rs = bd->find(); if (rs->get_desc()->get_wtyp() == get_waytype()) { if (rs->get_desc()->get_flags() & roadsign_desc_t::END_OF_CHOOSE_AREA) { return false; } } } // but we can only use empty blocks ... // now check, if we could enter here return sch->can_reserve(cnv->self); } return true; } // how expensive to go here (for way search) int rail_vehicle_t::get_cost(const grund_t* gr, const weg_t* w, const sint32 max_speed, ribi_t::ribi from) const { // first favor faster ways if (w == NULL) { // only occurs when deletion during way search return 999; } // add cost for going (with maximum speed, cost is 1) const sint32 max_tile_speed = w->get_max_speed(); int costs = (max_speed <= max_tile_speed) ? 1 : 4 - (3 * max_tile_speed) / max_speed; // effect of slope if (gr->get_weg_hang() != 0) { // check if the slope is upwards, relative to the previous tile // 25 hardcoded, see get_cost_upslope() costs += 25 * get_sloping_upwards(gr->get_weg_hang(), from); } return costs; } // this routine is called by find_route, to determined if we reached a destination bool rail_vehicle_t::is_target(const grund_t* gr, const grund_t* prev_gr) const { const schiene_t* sch1 = (const schiene_t*)gr->get_weg(get_waytype()); // first check blocks, if we can go there if (sch1->can_reserve(cnv->self)) { // just check, if we reached a free stop position of this halt if (gr->is_halt() && gr->get_halt() == target_halt) { // now we must check the predecessor ... if (prev_gr != NULL) { const koord dir = gr->get_pos().get_2d() - prev_gr->get_pos().get_2d(); const ribi_t::ribi ribi = ribi_type(dir); if (gr->get_weg(get_waytype())->get_ribi_maske() & ribi) { // signal/one way sign wrong direction return false; } // make sure that we are at the end of the platform or usable area const bool needs_electric = cnv != NULL && cnv->needs_electrification(); grund_t* to; if (gr->get_neighbour(to, get_waytype(), ribi)) { weg_t* w = to->get_weg(get_waytype()); if (to->get_halt() == target_halt) { // not end of platform yet // the following line would allow for more than one train on a platform, if there is a signal! // if (!w->has_signal() && w->get_max_speed() > 0 && (!needs_electric || w->is_electrified())) { if (w->get_max_speed() > 0 && (!needs_electric || w->is_electrified())) { // and we can go further return false; } } } // check for electrifiction needed? (we have checked this tile on route already) // end of stop: Is it long enough? sint16 tiles = cnv->get_tile_length(); while (true) { schiene_t* w = dynamic_cast(gr->get_weg(get_waytype())); // check electrification or illegal tracks (i.e. city walls with speed equals zero) if (w->get_max_speed() == 0 || (needs_electric && !w->is_electrified())) { return (tiles <= 1); } if (w->is_reserved()) { // only one train per platform (unless there are signals) return false; } if (!gr->get_neighbour(to, get_waytype(), ribi_t::backward(ribi)) || to->get_halt() != target_halt) { return (tiles <= 1); } gr = to; tiles--; } return true; } } } return false; } bool rail_vehicle_t::is_longblock_signal_clear(signal_t *sig, route_t::index_t next_block, sint32 &restart_speed) { // longblock signal: first check, whether there is a signal coming up on the route => just like normal signal route_t::index_t next_signal, next_crossing; if( !block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false ) ) { // not even the "Normal" signal route part is free => no bother checking further on sig->set_state( roadsign_t::STATE_RED ); restart_speed = 0; return false; } if( next_signal != route_t::INVALID_INDEX ) { // success, and there is a signal before end of route => finished sig->set_state( roadsign_t::STATE_GREEN ); cnv->set_next_stop_index( min( next_crossing, next_signal ) ); return true; } // unreserve this stretch (has been successful!) route_t::index_t dummy; block_reserver(cnv->get_route(), next_block+1, dummy, dummy, 0, false, false); // no signal before end_of_route => need to do route search in a step if( !cnv->is_waiting() ) { restart_speed = -1; return false; } // now we can use the route search array, since we are in step mode // (route until end is already reserved at this point!) uint8 schedule_index = cnv->get_schedule()->get_current_stop()+1; route_t target_rt; koord3d cur_pos = cnv->get_route()->back(); route_t::index_t next_next_signal = route_t::INVALID_INDEX; if(schedule_index >= cnv->get_schedule()->get_count()) { schedule_index = 0; } while( schedule_index != cnv->get_schedule()->get_current_stop() ) { // now search search for route of next segment if ( !target_rt.calc_route(welt, cur_pos, cnv->get_schedule()->entries[schedule_index].pos, this, speed_to_kmh(cnv->get_min_top_speed()), 8888 /*cnv->get_tile_length()*/)) { // broken route => no meaning to continue // but we go on to make sure that the convoi drives as far as possible break; } if( target_rt.is_contained(get_pos()) ) { // do not reserve route going through my current position => finished with checking break; } // reserver and unreserve again bool success = block_reserver( &target_rt, 1, next_next_signal, dummy, 0, true, false ); block_reserver( &target_rt, 1, dummy, dummy, 0, false, false ); if( success ) { // ok, would be free if( next_next_signal finished checking break; } } else { // route not reservable sig->set_state( roadsign_t::STATE_RED ); restart_speed = 0; return false; } // prepare for next leg of schedule cur_pos = target_rt.back(); schedule_index ++; if(schedule_index >= cnv->get_schedule()->get_count()) { schedule_index = 0; } } // we need to renew intial reservation (cannot fail) block_reserver(cnv->get_route(), next_block + 1, next_signal, next_crossing, 0, true, false); sig->set_state(roadsign_t::STATE_GREEN); cnv->set_next_stop_index(min(min(next_crossing, next_signal), cnv->get_route()->get_count() - 1 - 1)); return true; } bool rail_vehicle_t::is_choose_signal_clear(signal_t *sig, const route_t::index_t start_block, sint32 &restart_speed) { bool choose_ok = false; target_halt = halthandle_t(); route_t::index_t next_signal, next_crossing; grund_t const* const target = welt->lookup(cnv->get_route()->back()); if( cnv->get_schedule_target()!=koord3d::invalid ) { // destination is a waypoint! goto skip_choose; } if( target==NULL ) { cnv->suche_neue_route(); return false; } // first check, if we are not heading to a waypoint if( !target->get_halt().is_bound() ) { goto skip_choose; } // now we might choose something at least choose_ok = true; // check, if there is another choose signal or end_of_choose on the route for( uint32 idx=start_block+1; choose_ok && idxget_route()->get_count(); idx++ ) { grund_t *gr = welt->lookup(cnv->get_route()->at(idx)); if( gr==0 ) { choose_ok = false; break; } if( gr->get_halt()==target->get_halt() ) { target_halt = gr->get_halt(); break; } weg_t *way = gr->get_weg(get_waytype()); if( way==0 ) { choose_ok = false; break; } if( way->has_sign() ) { roadsign_t *rs = gr->find(1); if( rs && rs->get_desc()->get_wtyp()==get_waytype() ) { if( rs->get_desc()->get_flags() & roadsign_desc_t::END_OF_CHOOSE_AREA ) { // end of choose on route => not choosing here choose_ok = false; } } } if( way->has_signal() ) { signal_t *sig = gr->find(1); if( sig && sig->get_desc()->is_choose_sign() && !( sig->get_two_ways() && !(ribi_type(cnv->get_route()->at(idx), cnv->get_route()->at(idx-1)) & sig->get_dir()) )) { // second choose signal on route => not choosing here choose_ok = false; } } } skip_choose: if( !choose_ok ) { // just act as normal signal if( block_reserver( cnv->get_route(), start_block+1, next_signal, next_crossing, 0, true, false ) ) { sig->set_state( roadsign_t::STATE_GREEN ); cnv->set_next_stop_index( min( next_crossing, next_signal ) ); return true; } // not free => wait here if directly in front sig->set_state( roadsign_t::STATE_RED ); restart_speed = 0; return false; } target_halt = target->get_halt(); if( !block_reserver( cnv->get_route(), start_block+1, next_signal, next_crossing, 100000, true, false ) ) { // no free route to target! // note: any old reservations should be invalid after the block reserver call. // => We can now start freshly all over if(!cnv->is_waiting()) { // we are driving and cannot search yet restart_speed = -1; target_halt = halthandle_t(); return false; } // now we are in a step and can use the route search array route_t target_rt; const int richtung = ribi_type(cnv->get_route()->at(start_block), cnv->get_route()->at(start_block + 1)); // to avoid confusion at diagonals if( !target_rt.find_route( welt, cnv->get_route()->at(start_block), this, speed_to_kmh(cnv->get_min_top_speed()), richtung, welt->get_settings().get_max_choose_route_steps() ) ) { // nothing empty or not route with less than get_max_choose_route_steps() tiles target_halt = halthandle_t(); sig->set_state( roadsign_t::STATE_RED ); restart_speed = 0; return false; } else { // try to alloc the whole route cnv->access_route()->remove_koord_from(start_block); cnv->access_route()->append( &target_rt ); if( !block_reserver( cnv->get_route(), start_block+1, next_signal, next_crossing, 100000, true, false ) ) { dbg->error( "rail_vehicle_t::is_choose_signal_clear()", "could not reserved route after find_route!" ); target_halt = halthandle_t(); sig->set_state( roadsign_t::STATE_RED ); restart_speed = 0; return false; } } // reserved route to target } sig->set_state( roadsign_t::STATE_GREEN ); cnv->set_next_stop_index( min( next_crossing, next_signal ) ); return true; } bool rail_vehicle_t::is_pre_signal_clear(signal_t *sig, route_t::index_t next_block, sint32 &restart_speed) { // parse to next signal; if needed recurse, since we allow cascading route_t::index_t next_signal, next_crossing; if( block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false ) ) { if( next_signal == route_t::INVALID_INDEX || cnv->get_route()->at(next_signal) == cnv->get_route()->back() || is_signal_clear( next_signal, restart_speed ) ) { // ok, end of route => we can go sig->set_state( roadsign_t::STATE_GREEN ); cnv->set_next_stop_index( min( next_signal, next_crossing ) ); return true; } // when we reached here, the way is apparently not free => release reservation and set state to next free sig->set_state( roadsign_t::STATE_YELLOW ); block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, false, false ); restart_speed = 0; return false; } // if we end up here, there was not even the next block free sig->set_state( roadsign_t::STATE_RED ); restart_speed = 0; return false; } bool rail_vehicle_t::is_priority_signal_clear(signal_t *sig, route_t::index_t next_block, sint32 &restart_speed) { // parse to next signal; if needed recurse, since we allow cascading route_t::index_t next_signal, next_crossing; if( block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false ) ) { if( next_signal == route_t::INVALID_INDEX || cnv->get_route()->at(next_signal) == cnv->get_route()->back() || is_signal_clear( next_signal, restart_speed ) ) { // ok, end of route => we can go sig->set_state( roadsign_t::STATE_GREEN ); cnv->set_next_stop_index( min( next_signal, next_crossing ) ); return true; } // when we reached here, the way after the last signal is not free though the way before is => we can still go if( cnv->get_next_stop_index()<=next_signal+1 ) { // only show third aspect on last signal of cascade sig->set_state( roadsign_t::STATE_YELLOW ); } else { sig->set_state( roadsign_t::STATE_GREEN ); } cnv->set_next_stop_index( min( next_signal, next_crossing ) ); return false; } // if we end up here, there was not even the next block free sig->set_state( roadsign_t::STATE_RED ); restart_speed = 0; return false; } bool rail_vehicle_t::is_signal_clear(route_t::index_t next_block, sint32 &restart_speed) { // called, when there is a signal; will call other signal routines if needed grund_t *gr_next_block = welt->lookup(cnv->get_route()->at(next_block)); signal_t *sig = gr_next_block->find(); if( sig==NULL ) { dbg->error( "rail_vehicle_t::is_signal_clear()", "called at %s without a signal!", cnv->get_route()->at(next_block).get_str() ); return true; } // action depend on the next signal const roadsign_desc_t *sig_desc=sig->get_desc(); // simple signal: fail, if next block is not free if( sig_desc->is_simple_signal() ) { route_t::index_t next_signal, next_crossing; if( block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false ) ) { sig->set_state( roadsign_t::STATE_GREEN ); cnv->set_next_stop_index( min( next_crossing, next_signal ) ); return true; } // not free => wait here if directly in front sig->set_state( roadsign_t::STATE_RED ); restart_speed = 0; return false; } if( sig_desc->is_pre_signal() ) { return is_pre_signal_clear( sig, next_block, restart_speed ); } if ( sig_desc->is_priority_signal() ) { return is_priority_signal_clear( sig, next_block, restart_speed ); } if( sig_desc->is_longblock_signal() ) { return is_longblock_signal_clear( sig, next_block, restart_speed ); } if( sig_desc->is_choose_sign() ) { return is_choose_signal_clear( sig, next_block, restart_speed ); } dbg->error( "rail_vehicle_t::is_signal_clear()", "felt through at signal at %s", cnv->get_route()->at(next_block).get_str() ); return false; } bool rail_vehicle_t::can_enter_tile(const grund_t *gr, sint32 &restart_speed, uint8) { assert(leading); route_t::index_t next_signal, next_crossing; if( cnv->get_state()==convoi_t::CAN_START || cnv->get_state()==convoi_t::CAN_START_ONE_MONTH || cnv->get_state()==convoi_t::CAN_START_TWO_MONTHS ) { // reserve first block at the start until the next signal if (grund_t* gr_current = welt->lookup(get_pos())) { if (weg_t* w = gr_current->get_weg(get_waytype())) { if (!(w->has_signal() || gr_current->get_crossing())) { // free track => reserve up to next signal if (!block_reserver(cnv->get_route(), max(route_index, 1) - 1, next_signal, next_crossing, 0, true, false)) { restart_speed = 0; return false; } cnv->set_next_stop_index(next_crossing < next_signal ? next_crossing : next_signal); return true; } } } cnv->set_next_stop_index( max(route_index,1)-1 ); if( steps can drive to signal safely return true; } // we start with a signal/crossing => use stuff below ... } assert(gr); if(gr->obj_count()>250) { // too many objects here return false; } schiene_t *w = (schiene_t *)gr->get_weg(get_waytype()); if(w==NULL) { return false; } /* this should happen only before signals ... * but if it is already reserved, we can save lots of other checks later */ if( !w->can_reserve(cnv->self) ) { restart_speed = 0; return false; } // is there any signal/crossing to be reserved? route_t::index_t next_block = cnv->get_next_stop_index()-1; if( next_block >= cnv->get_route()->get_count() ) { // no obstacle in the way => drive on ... return true; } // signal disappeared, train passes the tile of former signal if( next_block+1 < route_index ) { // we need to reserve the next block even if there is no signal present anymore bool ok = block_reserver( cnv->get_route(), route_index, next_signal, next_crossing, 0, true, false ); if (ok) { cnv->set_next_stop_index( min( next_crossing, next_signal ) ); } return ok; // if reservation was not possible the train will wait on the track until block is free } if( next_block <= route_index+3 ) { koord3d block_pos=cnv->get_route()->at(next_block); grund_t *gr_next_block = welt->lookup(block_pos); const schiene_t *sch1 = gr_next_block ? (const schiene_t *)gr_next_block->get_weg(get_waytype()) : NULL; if(sch1==NULL) { // way (weg) not existent (likely destroyed) cnv->suche_neue_route(); return false; } // Is a crossing? // note: crossing and signal might exist on same tile // so first check crossing if(crossing_t* cr = gr_next_block->get_crossing()) { // ok, here is a draw/turnbridge ... bool ok = cr->request_crossing(this); if(!ok) { // cannot cross => wait here restart_speed = 0; return cnv->get_next_stop_index()>route_index+1; } else if( !sch1->has_signal() ) { // can reserve: find next place to do something and drive on if( block_pos == cnv->get_route()->back() ) { // is also last tile => go on ... cnv->set_next_stop_index( route_t::INVALID_INDEX ); return true; } else if( !block_reserver( cnv->get_route(), cnv->get_next_stop_index(), next_signal, next_crossing, 0, true, false ) ) { dbg->error( "rail_vehicle_t::can_enter_tile()", "block not free but was reserved!" ); return false; } cnv->set_next_stop_index( next_crossinghas_signal() ) { if( !is_signal_clear( next_block, restart_speed ) ) { // only return false, if we are directly in front of the signal return cnv->get_next_stop_index()>route_index; } } } return true; } /** * reserves or un-reserves all blocks and returns the handle to the next block (if there) * if count is larger than 1, (and defined) maximum MAX_CHOOSE_BLOCK_TILES tiles will be checked * (freeing or reserving a choose signal path) * if (!reserve && force_unreserve) then un-reserve everything till the end of the route * return the last checked block */ bool rail_vehicle_t::block_reserver(const route_t *route, route_t::index_t start_index, route_t::index_t &next_signal_index, route_t::index_t &next_crossing_index, int count, bool reserve, bool force_unreserve ) const { bool success=true; #ifdef MAX_CHOOSE_BLOCK_TILES int max_tiles=2*MAX_CHOOSE_BLOCK_TILES; // max tiles to check for choosesignals #endif slist_tpl signs; // switch all signals on their way too ... if(start_index>=route->get_count()) { cnv->set_next_reservation_index( max(route->get_count(),1)-1 ); return 0; } if(route->at(start_index)==get_pos() && reserve) { start_index++; } if( !reserve ) { cnv->set_next_reservation_index( start_index ); } // find next block segment en route route_t::index_t i = start_index; next_signal_index = route_t::INVALID_INDEX; next_crossing_index = route_t::INVALID_INDEX; bool unreserve_now = false; for ( ; success && count>=0 && iget_count(); i++) { koord3d pos = route->at(i); grund_t *gr = welt->lookup(pos); schiene_t * sch1 = gr ? (schiene_t *)gr->get_weg(get_waytype()) : NULL; if(sch1==NULL && reserve) { // reserve until the end of track break; } // we un-reserve also nonexistent tiles! (may happen during deletion) #ifdef MAX_CHOOSE_BLOCK_TILES max_tiles--; if(max_tiles<0 && count>1) { break; } #endif if(reserve) { if( sch1->has_signal() && iget_count()-1 ) { signal_t* signal = gr->find(); // Maybe we can ignore the signal if (!( signal->get_two_ways() && i>start_index && !(ribi_type(pos, route->at(i-1)) & signal->get_dir()) )) { if (count) { signs.append(gr); } count --; next_signal_index = i; } } if (!sch1->reserve(cnv->self, ribi_type(route->at(max(1u,i)-1u), route->at(min(route->get_count()-1u, i+1u))))) { success = false; } if (gr->has_two_ways()) { // we may need to reserve the other way as well if (schiene_t* sch0 = dynamic_cast(gr->get_weg_nr(gr->get_weg_nr(0) == sch1))) { // the other way is reservable too => try to reserve it if (!sch0->reserve(cnv->self, ribi_type(route->at(max(1u,i)-1u), route->at(min(route->get_count()-1u, i+1u))))) { success = false; } } } if(next_crossing_index==route_t::INVALID_INDEX && gr->get_crossing()) { next_crossing_index = i; } } else if(sch1) { if(!sch1->unreserve(cnv->self)) { if(unreserve_now) { // reached an reserved or free track => finished return false; } } else { // un-reserve from here (used during sale, since there might be reserved tiles not freed) unreserve_now = !force_unreserve; } if(sch1->has_signal()) { signal_t* signal = gr->find(); if(signal) { signal->set_state(roadsign_t::STATE_RED); } } if(crossing_t* cr = gr->get_crossing()) { cr->release_crossing(this); } } } if(!reserve) { return false; } // here we go only with reserve //DBG_MESSAGE("block_reserver()","signals at %i, success=%d",next_signal_index,success); // free, in case of un-reserve or no success in reservation if(!success) { // free reservation for ( route_t::index_t j=start_index; jlookup(route->at(j))) { schiene_t* sch1 = (schiene_t*)gr->get_weg(get_waytype()); if(sch1) { sch1->unreserve(cnv->self); if (gr->has_two_ways()) { // we may need to reserve the other way as well if (schiene_t* sch0 = dynamic_cast(gr->get_weg_nr(gr->get_weg_nr(0) == sch1))) { sch0->unreserve(cnv->self); } } } } } cnv->set_next_reservation_index( start_index ); return false; } // ok, switch everything green ... for(grund_t* const g : signs) { if (signal_t* const signal = g->find()) { signal->set_state(roadsign_t::STATE_GREEN); } } cnv->set_next_reservation_index( i ); return true; } /* beware: we must un-reserve rail blocks... */ void rail_vehicle_t::leave_tile() { vehicle_t::leave_tile(); // fix counters if(last) { if(grund_t* gr = welt->lookup(get_pos())) { if(schiene_t* sch0 = (schiene_t*)gr->get_weg(get_waytype())) { sch0->unreserve(this); // tell next signal? // and switch to red if(sch0->has_signal()) { if(signal_t* sig = gr->find(1)) { sig->set_state(roadsign_t::STATE_RED); } } if (gr->has_two_ways()) { // we may need to reserve the other way as well if (schiene_t* sch1 = dynamic_cast(gr->get_weg_nr(gr->get_weg_nr(0) == sch0))) { // the other way is reservable too => unreserve it sch1->unreserve(this); } } } } } } void rail_vehicle_t::enter_tile(grund_t* gr) { vehicle_t::enter_tile(gr); if( schiene_t *sch0 = (schiene_t *) gr->get_weg(get_waytype()) ) { // way statistics const int cargo = get_total_cargo(); sch0->book(cargo, WAY_STAT_GOODS); if(leading) { sch0->book(1, WAY_STAT_CONVOIS); sch0->reserve( cnv->self, get_direction() ); } } } schedule_t * rail_vehicle_t::generate_new_schedule() const { return desc->get_waytype()==tram_wt ? new tram_schedule_t() : new train_schedule_t(); } schedule_t * monorail_vehicle_t::generate_new_schedule() const { return new monorail_schedule_t(); } schedule_t * maglev_vehicle_t::generate_new_schedule() const { return new maglev_schedule_t(); } schedule_t * narrowgauge_vehicle_t::generate_new_schedule() const { return new narrowgauge_schedule_t(); } simutrans-124.5/src/simutrans/vehicle/rail_vehicle.h000066400000000000000000000102731520675460500226210ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_RAIL_VEHICLE_H #define VEHICLE_RAIL_VEHICLE_H #include "vehicle.h" /** * A class for rail vehicles (trains). Manages the look of the vehicles * and the navigability of tiles. * @see vehicle_t */ class rail_vehicle_t : public vehicle_t { protected: bool check_next_tile(const grund_t *bd) const OVERRIDE; void enter_tile(grund_t*) OVERRIDE; bool is_signal_clear(route_t::index_t start_index, sint32 &restart_speed); bool is_pre_signal_clear(signal_t *sig, route_t::index_t start_index, sint32 &restart_speed); bool is_priority_signal_clear(signal_t *sig, route_t::index_t start_index, sint32 &restart_speed); bool is_longblock_signal_clear(signal_t *sig, route_t::index_t start_index, sint32 &restart_speed); bool is_choose_signal_clear(signal_t *sig, route_t::index_t start_index, sint32 &restart_speed); public: waytype_t get_waytype() const OVERRIDE { return track_wt; } // since we might need to un-reserve previously used blocks, we must do this before calculation a new route bool calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route) OVERRIDE; // how expensive to go here (for way search) int get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const OVERRIDE; uint32 get_cost_upslope() const OVERRIDE { return 25; } // returns true for the way search to an unknown target. bool is_target(const grund_t *,const grund_t *) const OVERRIDE; // handles all block stuff and route choosing ... bool can_enter_tile(const grund_t *gr_next, sint32 &restart_speed, uint8) OVERRIDE; // reserves or un-reserves all blocks and returns the handle to the next block (if there) // returns true on successful reservation bool block_reserver(const route_t *route, route_t::index_t start_index, route_t::index_t &next_signal, route_t::index_t &next_crossing, int signal_count, bool reserve, bool force_unreserve ) const; void leave_tile() OVERRIDE; typ get_typ() const OVERRIDE { return rail_vehicle; } rail_vehicle_t(loadsave_t *file, bool is_first, bool is_last); rail_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t *cnv); ~rail_vehicle_t(); void set_convoi(convoi_t *c) OVERRIDE; schedule_t * generate_new_schedule() const OVERRIDE; }; /** * very similar to normal railroad, so we can implement it here completely * @see vehicle_t */ class monorail_vehicle_t : public rail_vehicle_t { public: waytype_t get_waytype() const OVERRIDE { return monorail_wt; } // all handled by rail_vehicle_t monorail_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : rail_vehicle_t(file,is_first, is_last) {} monorail_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cnv) : rail_vehicle_t(pos, desc, player, cnv) {} typ get_typ() const OVERRIDE { return monorail_vehicle; } schedule_t * generate_new_schedule() const OVERRIDE; }; /** * very similar to normal railroad, so we can implement it here completely * @see vehicle_t */ class maglev_vehicle_t : public rail_vehicle_t { public: waytype_t get_waytype() const OVERRIDE { return maglev_wt; } // all handled by rail_vehicle_t maglev_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : rail_vehicle_t(file, is_first, is_last) {} maglev_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cnv) : rail_vehicle_t(pos, desc, player, cnv) {} typ get_typ() const OVERRIDE { return maglev_vehicle; } schedule_t * generate_new_schedule() const OVERRIDE; }; /** * very similar to normal railroad, so we can implement it here completely * @see vehicle_t */ class narrowgauge_vehicle_t : public rail_vehicle_t { public: waytype_t get_waytype() const OVERRIDE { return narrowgauge_wt; } // all handled by rail_vehicle_t narrowgauge_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : rail_vehicle_t(file, is_first, is_last) {} narrowgauge_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cnv) : rail_vehicle_t(pos, desc, player, cnv) {} typ get_typ() const OVERRIDE { return narrowgauge_vehicle; } schedule_t * generate_new_schedule() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/vehicle/road_vehicle.cc000066400000000000000000000472211520675460500227600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "road_vehicle.h" #include "simroadtraffic.h" #include "../builder/goods_manager.h" #include "../builder/vehikelbauer.h" #include "../obj/way/strasse.h" #include "../dataobj/schedule.h" #include "../dataobj/translator.h" #include "../descriptor/citycar_desc.h" #include "../obj/crossing.h" #include "../obj/roadsign.h" #include "../player/simplay.h" #include "../simware.h" #include "../world/simworld.h" #include "../simconvoi.h" #include "../simhalt.h" #include "../simmesg.h" #include "../utils/cbuffer.h" road_vehicle_t::road_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cn) : vehicle_t(pos, desc, player) { cnv = cn; } road_vehicle_t::road_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : vehicle_t() { rdwr_from_convoi(file); if( file->is_loading() ) { static const vehicle_desc_t *last_desc = NULL; if(is_first) { last_desc = NULL; } // try to find a matching vehicle if(desc==NULL) { const goods_desc_t* w = (!fracht.empty() ? fracht.front().get_desc() : goods_manager_t::passengers); dbg->warning("road_vehicle_t::road_vehicle_t()","try to find a fitting vehicle for %s.", w->get_name() ); desc = vehicle_builder_t::get_best_matching(road_wt, 0, (fracht.empty() ? 0 : 50), is_first?50:0, speed_to_kmh(speed_limit), w, true, last_desc, is_last ); if(desc) { DBG_MESSAGE("road_vehicle_t::road_vehicle_t()","replaced by %s",desc->get_name()); // still wrong load ... calc_image(); } if(!fracht.empty() && fracht.front().amount == 0) { // this was only there to find a matching vehicle fracht.remove_first(); } } if( desc ) { last_desc = desc; } calc_disp_lane(false); // we do not know right now, if we are overtaking ... } } void road_vehicle_t::rotate90() { vehicle_t::rotate90(); calc_disp_lane(cnv && cnv->is_overtaking()); } void road_vehicle_t::calc_disp_lane(bool is_overtaking) { disp_lane = welt->get_settings().is_drive_left() ? 1 : 3; ribi_t::ribi dir = get_direction(); /* disp_lane is valid for vehicles moving to the right side of the screen, must be mirrored if SE <= heading < NW, and also if overtaking as there are five "display lanes" in simutrans which determine their drawing order. */ bool heading_left = (dir & ribi_t::south) || dir == ribi_t::west; if (heading_left ^ is_overtaking) { disp_lane ^= 2; } } // need to reset halt reservation (if there was one) bool road_vehicle_t::calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route) { assert(cnv); // free target reservation if(leading && previous_direction!=ribi_t::none && cnv && target_halt.is_bound() ) { // now reserve our choice (beware: might be longer than one tile!) for( uint32 length=0; lengthget_tile_length() && length+1get_route()->get_count(); length++ ) { target_halt->unreserve_position( welt->lookup( cnv->get_route()->at( cnv->get_route()->get_count()-length-1) ), cnv->self ); } } target_halt = halthandle_t(); // no block reserved route_t::route_result_t r = route->calc_route(welt, start, ziel, this, max_speed, cnv->get_tile_length() ); if( r == route_t::valid_route_halt_too_short ) { cbuffer_t buf; buf.printf( translator::translate("Vehicle %s cannot choose because stop too short!"), cnv->get_name()); welt->get_message()->add_message( (const char *)buf, ziel, message_t::traffic_jams, PLAYER_FLAG | cnv->get_owner()->get_player_nr(), cnv->front()->get_base_image() ); } return r; } bool road_vehicle_t::check_next_tile(const grund_t *bd) const { strasse_t *str=(strasse_t *)bd->get_weg(road_wt); if(str==NULL || str->get_max_speed()==0) { return false; } bool electric = cnv!=NULL && cnv->needs_electrification(); if(electric && !str->is_electrified()) { return false; } // check for signs if(str->has_sign()) { const roadsign_t* rs = bd->find(); if(rs!=NULL) { if( rs->get_desc()->get_min_speed()>0 && rs->get_desc()->get_min_speed()>kmh_to_speed(get_desc()->get_topspeed()) ) { return false; } if( rs->get_desc()->is_private_way() && (rs->get_player_mask() & (1<is_waiting() && rs->get_desc()->get_flags()&roadsign_desc_t::END_OF_CHOOSE_AREA) { return false; } } } return true; } // how expensive to go here (for way search) int road_vehicle_t::get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const { // first favor faster ways if(!w) { return 0xFFFF; } // max_speed? sint32 max_tile_speed = w->get_max_speed(); // add cost for going (with maximum speed, cost is 1) int costs = (max_speed<=max_tile_speed) ? 1 : 4-(3*max_tile_speed)/max_speed; // assume all traffic is not good ... (otherwise even smoke counts ... ) costs += (w->get_statistics(WAY_STAT_CONVOIS) > ( 2 << (welt->get_settings().get_bits_per_month()-16) ) ); // effect of slope if( gr->get_weg_hang()!=0 ) { // check if the slope is upwards, relative to the previous tile // 15 hardcoded, see get_cost_upslope() costs += 15 * get_sloping_upwards( gr->get_weg_hang(), from ); } return costs; } // this routine is called by find_route, to determined if we reached a destination bool road_vehicle_t::is_target(const grund_t *gr, const grund_t *prev_gr) const { // just check, if we reached a free stop position of this halt if(gr->is_halt() && gr->get_halt()==target_halt && target_halt->is_reservable(gr,cnv->self)) { // now we must check the predecessor => try to advance as much as possible if(prev_gr!=NULL) { const koord dir=gr->get_pos().get_2d()-prev_gr->get_pos().get_2d(); ribi_t::ribi ribi = ribi_type(dir); if( gr->get_weg(get_waytype())->get_ribi_maske() & ribi ) { // one way sign wrong direction return false; } grund_t *to; if( !gr->get_neighbour(to,road_wt,ribi) || !(to->get_halt()==target_halt) || (gr->get_weg(get_waytype())->get_ribi_maske() & ribi_type(dir))!=0 || !target_halt->is_reservable(to,cnv->self) ) { // end of stop: Is it long enough? uint16 tiles = cnv->get_tile_length(); while( tiles>1 ) { if( !gr->get_neighbour(to,get_waytype(),ribi_t::backward(ribi)) || !(to->get_halt()==target_halt) || !target_halt->is_reservable(to,cnv->self) ) { return false; } gr = to; tiles --; } return true; } // can advance more return false; } //DBG_MESSAGE("is_target()","success at %i,%i",gr->get_pos().x,gr->get_pos().y); // return true; } return false; } // to make smaller steps than the tile granularity, we have to use this trick void road_vehicle_t::get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const { vehicle_base_t::get_screen_offset( xoff, yoff, raster_width ); if( welt->get_settings().is_drive_left() ) { const int drive_left_dir = ribi_t::get_dir(get_direction()); xoff += tile_raster_scale_x( driveleft_base_offsets[drive_left_dir][0], raster_width ); yoff += tile_raster_scale_y( driveleft_base_offsets[drive_left_dir][1], raster_width ); } // eventually shift position to take care of overtaking if(cnv) { if( cnv->is_overtaking() ) { xoff += tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][0], raster_width); yoff += tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][1], raster_width); } else if( cnv->is_overtaken() ) { xoff -= tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][0], raster_width)/5; yoff -= tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][1], raster_width)/5; } } } // chooses a route at a choose sign; returns true on success bool road_vehicle_t::choose_route(sint32 &restart_speed, ribi_t::ribi start_direction, uint16 index) { if( cnv->get_schedule_target()!=koord3d::invalid ) { // destination is a waypoint! return true; } // are we heading to a target? route_t *rt = cnv->access_route(); target_halt = haltestelle_t::get_halt( rt->back(), get_owner(), false ); if( target_halt.is_bound() ) { // since convois can long than one tile, check is more difficult bool can_go_there = true; bool original_route = (rt->back() == cnv->get_schedule()->get_current_entry().pos); for( uint32 length=0; can_go_there && lengthget_tile_length() && length+1get_count(); length++ ) { if( grund_t *gr = welt->lookup( rt->at( rt->get_count()-length-1) ) ) { if (gr->get_halt().is_bound()) { can_go_there &= target_halt->is_reservable( gr, cnv->self ); } else { // if this is the original stop, it is too short! can_go_there |= original_route; } } } if( can_go_there ) { // then reserve it ... for( uint32 length=0; lengthget_tile_length() && length+1get_count(); length++ ) { target_halt->reserve_position( welt->lookup( rt->at( rt->get_count()-length-1) ), cnv->self ); } } else { // cannot go there => need slot search // if we fail, we will wait in a step, much more simulation friendly if(!cnv->is_waiting()) { restart_speed = -1; target_halt = halthandle_t(); return false; } // check if there is a free position // this is much faster than waysearch if( !target_halt->find_free_position(road_wt,cnv->self,obj_t::road_vehicle )) { restart_speed = 0; target_halt = halthandle_t(); return false; } // now it make sense to search a route route_t target_rt; koord3d next3d = rt->at(index); if( !target_rt.find_route( welt, next3d, this, speed_to_kmh(cnv->get_min_top_speed()), start_direction, welt->get_settings().get_max_choose_route_steps() ) ) { // nothing empty or not route with less than 33 tiles target_halt = halthandle_t(); restart_speed = 0; return false; } // now reserve our choice (beware: might be longer than one tile!) for( uint32 length=0; lengthget_tile_length() && length+1reserve_position( welt->lookup( target_rt.at( target_rt.get_count()-length-1) ), cnv->self ); } rt->remove_koord_from( index ); rt->append( &target_rt ); } } return true; } bool road_vehicle_t::can_enter_tile(const grund_t *gr, sint32 &restart_speed, uint8 second_check_count) { // check for traffic lights (only relevant for the first car in a convoi) if( leading ) { // no further check, when already entered a crossing (to allow leaving it) if( !second_check_count ) { if( const grund_t *gr_current = welt->lookup(get_pos()) ) { if( gr_current && gr_current->get_crossing() ) { // always allow to leave a crossing return true; } // always allow to leave traffic lights (avoid vehicles stuck on crossings directly after though) if( const roadsign_t *rs = gr_current->find(1) ) { if( rs && rs->get_desc()->is_traffic_light() && !gr->get_crossing() ) { // unless next tile is a crossing ... return true; } } } } assert(gr); const strasse_t *str = (strasse_t *)gr->get_weg(road_wt); if( !str || gr->obj_count() > 250 ) { // too many cars here or no street return false; } // first: check roadsigns const roadsign_t* rs = NULL; if( str->has_sign() ) { if((rs = gr->find(1)) != NULL) { route_t const& r = *cnv->get_route(); if( route_index + 1u < r.get_count() ) { // since at the corner, our direction may be diagonal, we make it straight uint8 direction90 = ribi_type(get_pos(), pos_next); if( rs->get_desc()->is_traffic_light() && (rs->get_dir()&direction90) == 0 ) { // wait here restart_speed = 16; return false; } // check, if we reached a choose point else { // route position after road sign const koord pos_next_next = r.at(route_index + 1u).get_2d(); // since at the corner, our direction may be diagonal, we make it straight direction90 = ribi_type( pos_next, pos_next_next ); if( rs->is_free_route(direction90) && !target_halt.is_bound() ) { if( second_check_count ) { return false; } if( !choose_route( restart_speed, direction90, route_index ) ) { return false; } } } } } } vehicle_base_t *obj = NULL; uint32 test_index = route_index + 1u; // way should be clear for overtaking: we checked previously if( !cnv->is_overtaking() ) { // calculate new direction route_t const& r = *cnv->get_route(); koord3d next = route_index < r.get_count() - 1u ? r.at(route_index + 1u) : pos_next; ribi_t::ribi curr_direction = get_direction(); ribi_t::ribi curr_90direction = calc_direction(get_pos(), pos_next); ribi_t::ribi next_direction = calc_direction(get_pos(), next); ribi_t::ribi next_90direction = calc_direction(pos_next, next); obj = no_cars_blocking( gr, cnv, curr_direction, next_direction, next_90direction ); // do not block intersections const bool drives_on_left = welt->get_settings().is_drive_left(); bool int_block = ribi_t::is_threeway(str->get_ribi_unmasked()) && (((drives_on_left ? ribi_t::rotate90l(curr_90direction) : ribi_t::rotate90(curr_90direction)) & str->get_ribi_unmasked()) || curr_90direction != next_90direction || (rs && rs->get_desc()->is_traffic_light())); // check exit from crossings and intersections, allow to proceed after 4 consecutive while( !obj && (int_block || gr->get_crossing()) && test_index < r.get_count() && test_index < route_index + 4u ) { if (crossing_t* cr = gr->get_crossing()) { if( !cr->request_crossing(this) ) { restart_speed = 0; return false; } } // test next position gr = welt->lookup(r.at(test_index)); if( !gr ) { // way (weg) not existent (likely destroyed) if( !second_check_count ) { cnv->suche_neue_route(); } return false; } str = (strasse_t *)gr->get_weg(road_wt); if( !str || gr->obj_count() > 250 ) { // too many cars here or no street if( !second_check_count && !str) { cnv->suche_neue_route(); } return false; } // check cars curr_direction = next_direction; curr_90direction = next_90direction; if( test_index + 1u < r.get_count() ) { next = r.at(test_index + 1u); next_direction = calc_direction(r.at(test_index - 1u), next); next_90direction = calc_direction(r.at(test_index), next); obj = no_cars_blocking( gr, cnv, curr_direction, next_direction, next_90direction ); } else { next = r.at(test_index); next_90direction = calc_direction(r.at(test_index - 1u), next); if( curr_direction == next_90direction || !gr->is_halt() ) { // check cars but allow to enter intersection if we are turning even when a car is blocking the halt on the last tile of our route // preserves old bus terminal behaviour obj = no_cars_blocking( gr, cnv, curr_direction, next_90direction, ribi_t::none ); } } // check roadsigns if( str->has_sign() ) { rs = gr->find(); if( rs ) { // check, if we reached a choose point if( rs->is_free_route(curr_90direction) && !target_halt.is_bound() ) { if( second_check_count ) { return false; } if( !choose_route( restart_speed, curr_90direction, test_index ) ) { return false; } } } } else { rs = NULL; } // check for blocking intersection int_block = ribi_t::is_threeway(str->get_ribi_unmasked()) && (((drives_on_left ? ribi_t::rotate90l(curr_90direction) : ribi_t::rotate90(curr_90direction)) & str->get_ribi_unmasked()) || curr_90direction != next_90direction || (rs && rs->get_desc()->is_traffic_light())); test_index++; } if( obj && test_index > route_index + 1u && !gr->get_crossing() && !int_block ) { // found a car blocking us after checking at least 1 intersection or crossing // and the car is in a place we could stop. So if it can move, assume it will, so we will too. // but check only upto 8 cars ahead to prevent infinite recursion on roundabouts. if( second_check_count >= 8 ) { return false; } if( road_vehicle_t const* const car = obj_cast(obj) ) { const convoi_t* const ocnv = car->get_convoi(); sint32 dummy; if( ocnv->front()->get_route_index() < ocnv->get_route()->get_count() && ocnv->front()->can_enter_tile( dummy, second_check_count + 1 ) ) { return true; } } } } // stuck message ... if( obj && !second_check_count ) { if( obj->is_stuck() ) { // end of traffic jam, but no stuck message, because previous vehicle is stuck too restart_speed = 0; cnv->set_tiles_overtaking(0); cnv->reset_waiting(); } else { if( test_index == route_index + 1u ) { // no intersections or crossings, we might be able to overtake this one ... overtaker_t *over = obj->get_overtaker(); if( over && !over->is_overtaken() ) { if( over->is_overtaking() ) { // otherwise we would stop every time being overtaken return true; } // not overtaking/being overtake: we need to make a more thought test! if( road_vehicle_t const* const car = obj_cast(obj) ) { convoi_t* const ocnv = car->get_convoi(); if( cnv->can_overtake( ocnv, (ocnv->get_state()==convoi_t::LOADING ? 0 : over->get_max_power_speed()), ocnv->get_length_in_steps()+ocnv->get_vehicle(0)->get_steps()) ) { return true; } } else if( private_car_t* const caut = obj_cast(obj) ) { if( cnv->can_overtake(caut, caut->get_desc()->get_topspeed(), VEHICLE_STEPS_PER_TILE) ) { return true; } } } } // we have to wait ... restart_speed = (cnv->get_akt_speed()*3)/4; cnv->set_tiles_overtaking(0); } } return obj==NULL; } return true; } overtaker_t* road_vehicle_t::get_overtaker() { return cnv; } void road_vehicle_t::enter_tile(grund_t* gr) { if (leading) { cnv->update_tiles_overtaking(); } calc_disp_lane(cnv && cnv->is_overtaking()); vehicle_t::enter_tile(gr); const int cargo = get_total_cargo(); weg_t *str = gr->get_weg(road_wt); if (str) { str->book(cargo, WAY_STAT_GOODS); if (leading) { str->book(1, WAY_STAT_CONVOIS); } } } schedule_t * road_vehicle_t::generate_new_schedule() const { return new truck_schedule_t(); } void road_vehicle_t::set_convoi(convoi_t *c) { DBG_MESSAGE("road_vehicle_t::set_convoi()","%p",c); if(c!=NULL) { bool target=(bool)cnv; // only during loadtype: cnv==1 indicates, that the convoi did reserve a stop vehicle_t::set_convoi(c); if(target && leading && c->get_route()->empty()) { // reinitialize the target halt const route_t *rt = cnv->get_route(); target_halt = haltestelle_t::get_halt( rt->back(), get_owner(), false ); if( target_halt.is_bound() ) { for( uint32 i=0; iget_tile_length() && i+1get_count(); i++ ) { target_halt->reserve_position( welt->lookup( rt->at(rt->get_count()-i-1) ), cnv->self ); } } } } else { if( cnv && leading && target_halt.is_bound() ) { // now reserve our choice (beware: might be longer than one tile!) for( uint32 length=0; lengthget_tile_length() && length+1get_route()->get_count(); length++ ) { target_halt->unreserve_position( welt->lookup( cnv->get_route()->at( cnv->get_route()->get_count()-length-1) ), cnv->self ); } target_halt = halthandle_t(); } cnv = NULL; } } simutrans-124.5/src/simutrans/vehicle/road_vehicle.h000066400000000000000000000035031520675460500226150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_ROAD_VEHICLE_H #define VEHICLE_ROAD_VEHICLE_H #include "vehicle.h" /** * A class for road vehicles. Manages the look of the vehicles * and the navigability of tiles. * @see vehicle_t */ class road_vehicle_t : public vehicle_t { private: // called internally only from can_enter_tile() // returns true on success bool choose_route(sint32 &restart_speed, ribi_t::ribi start_direction, uint16 index); protected: bool check_next_tile(const grund_t *bd) const OVERRIDE; public: void enter_tile(grund_t*) OVERRIDE; void rotate90() OVERRIDE; void calc_disp_lane(bool is_overtaking); waytype_t get_waytype() const OVERRIDE { return road_wt; } road_vehicle_t(loadsave_t *file, bool first, bool last); road_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cnv); // start and schedule void set_convoi(convoi_t *c) OVERRIDE; // how expensive to go here (for way search) int get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const OVERRIDE; uint32 get_cost_upslope() const OVERRIDE { return 15; } bool calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route) OVERRIDE; bool can_enter_tile(const grund_t *gr_next, sint32 &restart_speed, uint8 second_check_count) OVERRIDE; // returns true for the way search to an unknown target. bool is_target(const grund_t *,const grund_t *) const OVERRIDE; // since we must consider overtaking, we use this for offset calculation void get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const OVERRIDE; obj_t::typ get_typ() const OVERRIDE { return road_vehicle; } schedule_t * generate_new_schedule() const OVERRIDE; overtaker_t* get_overtaker() OVERRIDE; }; #endif simutrans-124.5/src/simutrans/vehicle/simroadtraffic.cc000066400000000000000000000752351520675460500233370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "../display/simgraph.h" #include "../simmesg.h" #include "../world/simworld.h" #include "../utils/simrandom.h" #include "../display/simimg.h" #include "../simunits.h" #include "../simtypes.h" #include "../simconvoi.h" #include "simroadtraffic.h" #include "vehicle.h" #ifdef DESTINATION_CITYCARS // for final citycar destinations #include "simpeople.h" #endif #include "../dataobj/translator.h" #include "../dataobj/loadsave.h" #include "../dataobj/environment.h" #include "../obj/crossing.h" #include "../obj/roadsign.h" #include "../ground/grund.h" #include "../obj/way/weg.h" #include "../descriptor/citycar_desc.h" #include "../descriptor/roadsign_desc.h" #include "../utils/cbuffer.h" #include "road_vehicle.h" /**********************************************************************************************************************/ /* Road users (private cars and pedestrians) basis class from here on */ road_user_t::road_user_t() : vehicle_base_t() { set_owner( welt->get_public_player() ); time_to_life = 0; weg_next = 0; } /** * Ensures that this object is removed correctly from the list * of sync step-able things! */ road_user_t::~road_user_t() { mark_image_dirty( get_image(), 0 ); } road_user_t::road_user_t(grund_t* bd, uint16 random) : vehicle_base_t(bd ? bd->get_pos() : koord3d::invalid) { assert(bd); ribi_t::ribi road_ribi = bd->get_weg_ribi(road_wt); weg_next = random; // randomized offset uint8 offset = random & 3; direction = ribi_t::nesw[offset]; grund_t *to = NULL; for(uint8 r = 0; r < 4; r++) { ribi_t::ribi ribi = ribi_t::nesw[ (r + offset) &3]; if( (ribi & road_ribi)!=0 && bd->get_neighbour(to, road_wt, ribi)) { direction = ribi; break; } } switch(direction) { case ribi_t::north: dx = 2; dy = -1; break; case ribi_t::south: dx = -2; dy = 1; break; case ribi_t::east: dx = 2; dy = 1; break; case ribi_t::west: dx = -2; dy = -1; break; } set_xoff( (dx<0) ? OBJECT_OFFSET_STEPS : -OBJECT_OFFSET_STEPS ); set_yoff( (dy<0) ? OBJECT_OFFSET_STEPS/2 : -OBJECT_OFFSET_STEPS/2 ); if(to) { pos_next = to->get_pos(); } else { pos_next = bd->get_pos(); // we have nowhere to go time_to_life = 0; } set_owner( welt->get_public_player() ); } /** * Open a new observation window for the object. */ void road_user_t::show_info() { if(env_t::road_user_info) { obj_t::show_info(); } } void road_user_t::rdwr(loadsave_t *file) { xml_tag_t t( file, "verkehrsteilnehmer_t" ); sint8 hoff = file->is_saving() ? get_hoff() : 0; // correct old offsets ... REMOVE after savegame increase ... if(file->is_version_less(99, 18) && file->is_saving()) { dx = dxdy[ ribi_t::get_dir(direction)*2 ]; dy = dxdy[ ribi_t::get_dir(direction)*2+1 ]; sint8 i = steps/16; set_xoff( get_xoff() + i*dx ); set_yoff( get_yoff() + i*dy + hoff ); } vehicle_base_t::rdwr(file); if(file->is_version_less(86, 6)) { sint32 l; file->rdwr_long(l); file->rdwr_long(l); file->rdwr_long(weg_next); file->rdwr_long(l); dx = (sint8)l; file->rdwr_long(l); dy = (sint8)l; file->rdwr_enum(direction); file->rdwr_long(l); hoff = (sint8)l; } else { if(file->is_version_less(99, 5)) { sint32 dummy32; file->rdwr_long(dummy32); } file->rdwr_long(weg_next); if(file->is_version_less(99, 18)) { file->rdwr_byte(dx); file->rdwr_byte(dy); } else { file->rdwr_byte(steps); file->rdwr_byte(steps_next); } file->rdwr_enum(direction); dx = dxdy[ ribi_t::get_dir(direction)*2]; dy = dxdy[ ribi_t::get_dir(direction)*2+1]; if(file->is_version_less(99, 5) || file->is_version_atleast(99, 17)) { sint16 dummy16 = ((16*(sint16)hoff)/OBJECT_OFFSET_STEPS); file->rdwr_short(dummy16); hoff = (sint8)((OBJECT_OFFSET_STEPS*(sint16)dummy16)/16); } else { file->rdwr_byte(hoff); } } pos_next.rdwr(file); // convert steps to position if(file->is_version_less(99, 18)) { sint8 ddx=get_xoff(), ddy=get_yoff()-hoff; sint8 i=0; while( !is_about_to_hop(ddx+dx*i,ddy+dy*i ) && i<16 ) { i++; } set_xoff( ddx-(16-i)*dx ); set_yoff( ddy-(16-i)*dy ); if(file->is_loading()) { if(dx && dy) { steps = min( VEHICLE_STEPS_PER_TILE - 1, VEHICLE_STEPS_PER_TILE - 1 - (i*16) ); steps_next = VEHICLE_STEPS_PER_TILE - 1; } else { steps = min( VEHICLE_STEPS_PER_TILE/2 - 1, VEHICLE_STEPS_PER_TILE / 2 -(i*16) ); steps_next = VEHICLE_STEPS_PER_TILE/2 ; } } } // the lifetime in ms if(file->is_version_atleast(89, 5)) { file->rdwr_long(time_to_life); } // there might be crashes if world is destroyed after loading // without a sync-step being performed if(file->is_loading() && time_to_life<=0) { time_to_life = 1; } // avoid endless growth of the values // this causes lockups near 2**32 weg_next &= 65535; } void road_user_t::finish_rd() { calc_height(NULL); calc_image(); } /**********************************************************************************************************************/ /* private_car_t (city cars) from here on */ freelist_iter_tpl private_car_t::fl; static weighted_vector_tpl liste_timeline; stringhashtable_tpl private_car_t::table; bool private_car_t::register_desc(const citycar_desc_t *desc) { if(const citycar_desc_t *old = table.remove(desc->get_name()) ) { delete old; } table.put(desc->get_name(), desc); return true; } bool private_car_t::successfully_loaded() { if(table.empty()) { DBG_MESSAGE("private_car_t", "No citycars found - feature disabled"); } return true; } static bool compare_stadtauto_desc(const citycar_desc_t* a, const citycar_desc_t* b) { int diff = a->get_intro_year_month() - b->get_intro_year_month(); if (diff == 0) { diff = a->get_topspeed() - b->get_topspeed(); } if (diff == 0) { /* same Level - we introduce an artificial, but unique resort * on the induced name. */ diff = strcmp(a->get_name(), b->get_name()); } return diff < 0; } void private_car_t::build_timeline_list(karte_t *welt) { // this list will contain all citycars liste_timeline.clear(); vector_tpl temp_liste(0); if( !table.empty() ) { const int month_now = welt->get_current_month(); //DBG_DEBUG("private_car_t::built_timeline_liste()","year=%i, month=%i", month_now/12, month_now%12+1); // check for every citycar, if still ok ... for(auto const& i : table) { citycar_desc_t const* const info = i.value; const int intro_month = info->get_intro_year_month(); const int retire_month = info->get_retire_year_month(); if (!welt->use_timeline() || (intro_month <= month_now && month_now < retire_month)) { temp_liste.insert_ordered( info, compare_stadtauto_desc ); } } } liste_timeline.reserve( temp_liste.get_count() ); for(citycar_desc_t const* const i : temp_liste) { liste_timeline.append(i, i->get_distribution_weight()); } } bool private_car_t::list_empty() { return liste_timeline.empty(); } private_car_t::~private_car_t() { pos_next = koord3d::invalid; // will remove car from corssings etc. leave_tile(); welt->buche( -1, karte_t::WORLD_CITYCARS ); } private_car_t::private_car_t(loadsave_t *file) : road_user_t() { rdwr(file); ms_traffic_jam = 0; calc_disp_lane(); welt->buche( +1, karte_t::WORLD_CITYCARS ); } private_car_t::private_car_t(grund_t* gr, koord const target, const char* name) : road_user_t(gr, simrand(65535)) { desc = name ? table.get(name) : NULL; if (desc == NULL) { desc = liste_timeline.empty() ? 0 : pick_any_weighted(liste_timeline); } pos_next_next = koord3d::invalid; time_to_life = welt->get_settings().get_stadtauto_duration() << 20; // ignore welt->ticks_per_world_month_shift; current_speed = 48; ms_traffic_jam = 0; #ifdef DESTINATION_CITYCARS this->target = target; #else (void)target; #endif calc_image(); calc_disp_lane(); welt->buche( +1, karte_t::WORLD_CITYCARS ); } sync_result private_car_t::sync_step(uint32 delta_t) { time_to_life -= delta_t; if( time_to_life<=0 ) { return SYNC_DELETE; } if( current_speed==0 ) { // stuck in traffic jam uint32 old_ms_traffic_jam = ms_traffic_jam; ms_traffic_jam += delta_t; // check only every 1.024 s if stopped if( (ms_traffic_jam>>10) != (old_ms_traffic_jam>>10) ) { pos_next_next = koord3d::invalid; if( hop_check() ) { ms_traffic_jam = 0; current_speed = 48; } else { if( ms_traffic_jam > welt->ticks_per_world_month && old_ms_traffic_jam<=welt->ticks_per_world_month ) { // message after two month, reset waiting timer welt->get_message()->add_message( translator::translate("To heavy traffic\nresults in traffic jam.\n"), get_pos(), message_t::traffic_jams|message_t::EXPIRE_AFTER_ONE_MONTH_MSG, gfx->palette_lookup(COL_ORANGE) ); } } } weg_next = 0; } else { weg_next += current_speed*delta_t; const uint32 distance = do_drive( weg_next ); // hop_check could have set weg_next to zero, check for possible underflow here if (weg_next > distance) { weg_next -= distance; } else { weg_next = 0; } } return time_to_life>0 ? SYNC_OK : SYNC_DELETE; } void private_car_t::rdwr(loadsave_t *file) { xml_tag_t s( file, "stadtauto_t" ); road_user_t::rdwr(file); if(file->is_saving()) { const char *s = desc->get_name(); file->rdwr_str(s); } else { char s[256]; file->rdwr_str(s, lengthof(s)); desc = table.get(s); if( desc == 0 && !liste_timeline.empty() ) { dbg->warning("private_car_t::rdwr()", "Object '%s' not found in table, trying random stadtauto object type",s); desc = pick_any_weighted(liste_timeline); } if(desc == 0) { dbg->warning("private_car_t::rdwr()", "loading game with private cars, but no private car objects found in PAK files."); } else { set_image(desc->get_image_id(ribi_t::get_dir(get_direction()))); } } if(file->is_version_less(86, 2)) { time_to_life = simrand(1000000)+10000; } else if(file->is_version_less(89, 5)) { file->rdwr_long(time_to_life); time_to_life *= 10000; // converting from hops left to ms since start } if(file->is_version_less(86, 5)) { // default starting speed for old games if(file->is_loading()) { current_speed = 48; } } else { sint32 dummy32=current_speed; file->rdwr_long(dummy32); current_speed = dummy32; } if(file->is_version_less(99, 10)) { pos_next_next = koord3d::invalid; } else { pos_next_next.rdwr(file); } // overtaking status if(file->is_version_less(100, 1)) { set_tiles_overtaking( 0 ); } else { file->rdwr_byte(tiles_overtaking); set_tiles_overtaking( tiles_overtaking ); } } bool private_car_t::can_enter_tile(grund_t *gr) { if(gr->obj_count()>200) { // already too many things here return false; } // road still there? weg_t * str = gr->get_weg(road_wt); if(str==NULL) { time_to_life = 0; return false; } // calculate new direction // are we just turning around? const uint8 this_direction = get_direction(); bool frei = false; if( get_pos()==pos_next_next ) { // turning around => single check const uint8 next_direction = ribi_t::backward(this_direction); frei = (NULL == no_cars_blocking( gr, NULL, next_direction, next_direction, next_direction )); // do not block railroad crossing if(frei && gr->get_crossing()) { // look back for turning const grund_t *gr_current = welt->lookup(get_pos()); frei = (NULL == no_cars_blocking(gr_current, NULL, next_direction, next_direction, next_direction )); } } else { // driving on: check for crossings etc. too const uint8 next_direction = this->calc_direction(get_pos(), pos_next_next); // do not block this crossing (if possible) if(ribi_t::is_threeway(str->get_ribi_unmasked())) { // but leaving from railroad crossing is more important if (grund_t* gr_current = welt->lookup(get_pos())) { if (gr_current->get_crossing() != NULL) { return true; } } grund_t *test = welt->lookup(pos_next_next); if( test ) { uint8 next_90direction = this->calc_direction(pos_next, pos_next_next); frei = (NULL == no_cars_blocking( gr, NULL, this_direction, next_direction, next_90direction )); if( frei ) { // check, if it can leave this crossings frei = (NULL == no_cars_blocking( test, NULL, next_direction, next_90direction, next_90direction )); } } // this fails with two crossings together; however, I see no easy way out here ... } else { // not a crossing => skip 90 degrees check! frei = true; // Overtaking vehicles shouldn't have anything blocking them if( !is_overtaking() ) { // not a crossing => skip 90 degrees check! vehicle_base_t *dt = no_cars_blocking( gr, NULL, this_direction, next_direction, next_direction ); if( dt ) { if(dt->is_stuck()) { // previous vehicle is stuck => end of traffic jam ... frei = false; } else { overtaker_t *over = dt->get_overtaker(); if(over) { if(!over->is_overtaking()) { // otherwise the overtaken car would stop for us ... if( road_vehicle_t const* const car = obj_cast(dt) ) { convoi_t* const ocnv = car->get_convoi(); if( ocnv==NULL || !can_overtake( ocnv, (ocnv->get_state()==convoi_t::LOADING ? 0 : over->get_max_power_speed()), ocnv->get_length_in_steps()+ocnv->get_vehicle(0)->get_steps()) ) { frei = false; } } else if( private_car_t* const caut = obj_cast(dt) ) { if( !can_overtake(caut, caut->get_desc()->get_topspeed(), VEHICLE_STEPS_PER_TILE) ) { frei = false; } } } } else { // movingobj ... block road totally frei = false; } } } } } // do not block railroad crossing if( frei && gr->get_crossing() ) { // can we cross? crossing_t* cr = gr->get_crossing(); if(!cr->request_crossing(this)) { // approaching railway crossing: check if empty return false; } // but leaving from railroad crossing is more important if (grund_t* gr_current = welt->lookup(get_pos())) { if (gr_current->get_crossing()) { return true; } } // ok, now check for free exit koord dir = pos_next.get_2d()-get_pos().get_2d(); koord3d checkpos = pos_next+dir; uint8 number_reversed = 0; while( number_reversed<2 ) { const grund_t *test = welt->lookup(checkpos); if(!test) { // should not reach here! break; } const uint8 next_direction = ribi_type(dir); const uint8 nextnext_direction = ribi_type(dir); // test next field after way crossing if(no_cars_blocking( test, NULL, next_direction, nextnext_direction, nextnext_direction )) { return false; } // ok, left the crossing if(!test->get_crossing()) { // approaching railway crossing if empty return cr->request_crossing( this ); } else { // seems to be a dead-end. if( (test->get_weg_ribi(road_wt)&next_direction) == 0 ) { // will be going back pos_next_next=get_pos(); // check also opposite direction are free dir = -dir; number_reversed ++; } } checkpos += dir; } } } if(frei && current_speed==0) { ms_traffic_jam = 0; current_speed = 48; } if(!frei) { // not free => stop overtaking this->set_tiles_overtaking(0); } return frei; } void private_car_t::enter_tile(grund_t* gr) { #ifdef DESTINATION_CITYCARS if( target!=koord::invalid && koord_distance(pos_next.get_2d(),target)<10 ) { // delete it ... time_to_life = 0; // was generating pedestrians gere, but not possible with new sync system } #endif update_tiles_overtaking(); calc_disp_lane(); vehicle_base_t::enter_tile(gr); gr->get_weg(road_wt)->book(1, WAY_STAT_CONVOIS); } grund_t* private_car_t::hop_check() { grund_t *const from = welt->lookup(pos_next); if(from==NULL) { // nothing to go? => destroy ... time_to_life = 0; return NULL; } // find the allowed directions const weg_t *weg = from->get_weg(road_wt); if(weg==NULL) { // nothing to go? => destroy ... time_to_life = 0; return NULL; } // traffic light phase check (since this is on next tile, it will always be necessary!) const ribi_t::ribi direction90 = ribi_type(get_pos(), pos_next); if( weg->has_sign() ) { const roadsign_t* rs = from->find(); const roadsign_desc_t* rs_desc = rs->get_desc(); if( rs_desc->is_traffic_light() && (rs->get_dir()&direction90)==0 ) { // red traffic light, but we go on, if we are already on a traffic light bool go_on = false; if( const grund_t *gr_current = welt->lookup(get_pos()) ) { if( const roadsign_t *rs = gr_current->find() ) { go_on = rs && rs->get_desc()->is_traffic_light() && !from->get_crossing(); } } if( !go_on ) { direction = direction90; calc_image(); // wait here current_speed = 48; weg_next = 0; return NULL; } } } // next tile unknown => find next tile if(pos_next_next==koord3d::invalid) { // ok, nobody did delete the road in front of us // so we can check for valid directions ribi_t::ribi ribi = weg->get_ribi() & (~ribi_t::backward(direction90)); // cul de sac: return if(ribi==0) { pos_next_next = get_pos(); return can_enter_tile(from) ? from : NULL; } #ifdef DESTINATION_CITYCARS static weighted_vector_tpl posliste(4); posliste.clear(); const uint8 offset = ribi_t::is_single(ribi) ? 0 : simrand(4); for(uint8 r = 0; r < 4; r++) { if( get_pos().get_2d()==koord::nesw[r]+pos_next.get_2d() ) { continue; } #else const uint8 offset = ribi_t::is_single(ribi) ? 0 : simrand(4); for(uint8 i = 0; i < 4; i++) { const uint8 r = (i+offset)&3; #endif if( (ribi&ribi_t::nesw[r])!=0 ) { grund_t *to; if( from->get_neighbour(to, road_wt, ribi_t::nesw[r]) ) { // check, if this is just a single tile deep after a crossing weg_t *w = to->get_weg(road_wt); if( ribi_t::is_single(w->get_ribi()) && (w->get_ribi()&ribi_t::nesw[r])==0 && !ribi_t::is_single(ribi) ) { ribi &= ~ribi_t::nesw[r]; continue; } // check, if roadsign forbid next step ... if(w->has_sign()) { const roadsign_t* rs = to->find(); const roadsign_desc_t* rs_desc = rs->get_desc(); if(rs_desc->get_min_speed()>desc->get_topspeed() || (rs_desc->is_private_way() && (rs->get_player_mask()&2)==0) ) { // not allowed to go here ribi &= ~ribi_t::nesw[r]; continue; } } #ifdef DESTINATION_CITYCARS uint32 dist=koord_distance( to->get_pos().get_2d(), target ); posliste.append( to->get_pos(), dist*dist ); #else // ok, now check if we are allowed to go here (i.e. no cars blocking) pos_next_next = to->get_pos(); if(can_enter_tile(from)) { // ok, this direction is fine! ms_traffic_jam = 0; if(current_speed<48) { current_speed = 48; } return from; } else { pos_next_next = koord3d::invalid; } #endif } else { // not connected?!? => ribi likely wrong ribi &= ~ribi_t::nesw[r]; } } } #ifdef DESTINATION_CITYCARS if (!posliste.empty()) { pos_next_next = pick_any_weighted(posliste); } else { pos_next_next = get_pos(); } if(can_enter_tile(from)) { // ok, this direction is fine! ms_traffic_jam = 0; if(current_speed<48) { current_speed = 48; } return from; } #else // only stumps at single way crossing, all other blocked => turn around if(ribi==0) { pos_next_next = get_pos(); return can_enter_tile(from) ? from : NULL; } #endif } else { if(from && can_enter_tile(from)) { // ok, this direction is fine! ms_traffic_jam = 0; if(current_speed<48) { current_speed = 48; } return from; } } // no free tiles => assume traffic jam ... pos_next_next = koord3d::invalid; current_speed = 0; set_tiles_overtaking( 0 ); return NULL; } void private_car_t::hop(grund_t* to) { leave_tile(); if(pos_next_next==get_pos()) { direction = calc_set_direction( pos_next, pos_next_next ); steps_next = 0; // mark for starting at end of tile! } else { direction = calc_set_direction( get_pos(), pos_next_next ); } calc_image(); set_pos(pos_next); enter_tile(to); calc_current_speed(to); if(crossing_t* cr = to->get_crossing()) { cr->add_to_crossing(this); } pos_next = pos_next_next; pos_next_next = koord3d::invalid; } void private_car_t::calc_image() { set_image(desc->get_image_id(ribi_t::get_dir(get_direction()))); } void private_car_t::calc_current_speed(grund_t* gr) { const weg_t * weg = gr->get_weg(road_wt); const sint32 max_speed = desc->get_topspeed(); const sint32 speed_limit = weg ? kmh_to_speed(weg->get_max_speed()) : max_speed; current_speed += max_speed>>2; if(current_speed > max_speed) { current_speed = max_speed; } if(current_speed > speed_limit) { current_speed = speed_limit; } } void private_car_t::info(cbuffer_t & buf) const { buf.printf(translator::translate("%s\nspeed %i\nmax_speed %i\ndx:%i dy:%i"), translator::translate(desc->get_name()), speed_to_kmh(current_speed), speed_to_kmh(desc->get_topspeed()), dx, dy); if (char const* const maker = desc->get_copyright()) { buf.printf(translator::translate("Constructed by %s"), maker); } } // to make smaller steps than the tile granularity, we have to use this trick void private_car_t::get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const { vehicle_base_t::get_screen_offset( xoff, yoff, raster_width ); if( welt->get_settings().is_drive_left() ) { const int drive_left_dir = ribi_t::get_dir(get_direction()); xoff += tile_raster_scale_x( driveleft_base_offsets[drive_left_dir][0], raster_width ); yoff += tile_raster_scale_y( driveleft_base_offsets[drive_left_dir][1], raster_width ); } // eventually shift position to take care of overtaking if( is_overtaking() ) { xoff += tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][0], raster_width); yoff += tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][1], raster_width); } else if( is_overtaken() ) { xoff -= tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][0], raster_width)/5; yoff -= tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][1], raster_width)/5; } } void private_car_t::calc_disp_lane() { disp_lane = welt->get_settings().is_drive_left() ? 1 : 3; ribi_t::ribi dir = get_direction(); /* disp_lane is valid for vehicles moving to the right side of the screen, must be mirrored if SE <= heading < NW, and also if overtaking as there are five "display lanes" in simutrans which determine their drawing order. */ bool heading_left = (dir & ribi_t::south) || dir == ribi_t::west; if (heading_left ^ is_overtaking()) { disp_lane ^= 2; } } void private_car_t::rotate90() { road_user_t::rotate90(); calc_disp_lane(); } /** * conditions for a city car to overtake another overtaker. * The city car is not overtaking/being overtaken. */ bool private_car_t::can_overtake( overtaker_t *other_overtaker, sint32 other_speed, sint16 steps_other) { if( !other_overtaker->can_be_overtaken() ) { return false; } if( other_speed == 0 ) { /* overtaking a loading convoi * => we can do a lazy check, since halts are always straight */ grund_t *gr = welt->lookup(get_pos()); if( gr==NULL ) { // should never happen, since there is a vehicle in front of us ... return false; } weg_t *str = gr->get_weg(road_wt); if( str==0 ) { // also this is not possible, since a car loads in front of is!?! return false; } const ribi_t::ribi direction = get_direction() & str->get_ribi(); koord3d check_pos = get_pos()+koord((ribi_t::ribi)(str->get_ribi()&direction)); for( int tiles=1+(steps_other-1)/(CARUNITS_PER_TILE*VEHICLE_STEPS_PER_CARUNIT); tiles>=0; tiles-- ) { grund_t *gr = welt->lookup(check_pos); if( gr==NULL ) { return false; } // not overtaking on railroad crossings ... if (gr->get_crossing()) { return false; } weg_t *str = gr->get_weg(road_wt); if( str==0 ) { return false; } if( ribi_t::is_threeway(str->get_ribi()) ) { return false; } // Check for other vehicles on the next tile const uint8 top = gr->obj_count(); for( uint8 j=1; j(gr->obj_bei(j)) ) { // check for other traffic on the road const overtaker_t *ov = v->get_overtaker(); if(ov) { if(this!=ov && other_overtaker!=ov) { return false; } } else if( v->get_waytype()==road_wt && v->get_typ()!=obj_t::pedestrian ) { return false; } } } check_pos += koord(direction); } set_tiles_overtaking( 3+(steps_other-1)/(CARUNITS_PER_TILE*VEHICLE_STEPS_PER_CARUNIT) ); return true; } sint32 diff_speed = (sint32)current_speed - other_speed; if( diff_speed < kmh_to_speed( 5 ) ) { // not fast enough to overtake return false; } // Number of tiles overtaking will take int n_tiles = 0; /* Distance it takes overtaking (unit: vehicle steps) = my_speed * time_overtaking * time_overtaking = tiles_to_overtake/diff_speed * tiles_to_overtake = convoi_length + pos_other_convoi * convoi_length for city cars? ==> a bit over half a tile (10) */ sint32 time_overtaking = 0; sint32 distance = current_speed*((10<<4)+steps_other)/diff_speed; // Conditions for overtaking: // Flat tiles, with no stops, no crossings, no signs, no change of road speed limit koord3d check_pos = get_pos(); koord pos_prev = check_pos.get_2d(); grund_t *gr = welt->lookup(check_pos); if( gr==NULL ) { return false; } weg_t *str = gr->get_weg(road_wt); if( str==0 ) { return false; } // we need 90 degree ribi ribi_t::ribi direction = get_direction(); direction = str->get_ribi() & direction; while( distance > 0 ) { // we allow stops and slopes, since empty stops and slopes cannot affect us // (citycars do not slow down on slopes!) // start of bridge is one level deeper if(gr->get_weg_yoff()>0) { check_pos.z ++; } // not overtaking on railroad crossings ... if (gr->get_crossing()) { return false; } // special signs if( str->has_sign() && str->get_ribi()==str->get_ribi_unmasked() ) { const roadsign_t *rs = gr->find(1); if(rs) { const roadsign_desc_t *rb = rs->get_desc(); if(rb->get_min_speed()>desc->get_topspeed() || rb->is_private_way() || rb->is_traffic_light() ) { // do not overtake when road is closed for cars, there is a traffic light or a too high min speed limit return false; } } } // street gets too slow (TODO: should be able to be correctly accounted for) if( current_speed > kmh_to_speed(str->get_max_speed()) ) { return false; } int d = ribi_t::is_straight(str->get_ribi()) ? VEHICLE_STEPS_PER_TILE : diagonal_vehicle_steps_per_tile; distance -= d; time_overtaking += d; n_tiles++; /* Now we must check for next position: * crossings are ok, as long as we cannot exit there due to one way signs * much cheaper calculation: only go on in the direction of before (since no slopes allowed anyway ... ) */ grund_t *to = welt->lookup( check_pos + koord((ribi_t::ribi)(str->get_ribi()&direction)) ); if( ribi_t::is_threeway(str->get_ribi()) || to==NULL) { // check for entries/exits/bridges, if necessary ribi_t::ribi rib = str->get_ribi(); bool found_one = false; for( int r=0; r<4; r++ ) { if( (rib&ribi_t::nesw[r])==0 || check_pos.get_2d()+koord::nesw[r]==pos_prev) { continue; } if(gr->get_neighbour(to, road_wt, ribi_t::nesw[r])) { if(found_one) { // two directions to go: unexpected cars may occurs => abort return false; } found_one = true; } } } pos_prev = check_pos.get_2d(); // nowhere to go => nobody can come against us ... if(to==NULL || (str=to->get_weg(road_wt))==NULL) { return false; } // Check for other vehicles on the next tile const uint8 top = gr->obj_count(); for( uint8 j=1; j(gr->obj_bei(j)) ) { // check for other traffic on the road const overtaker_t *ov = v->get_overtaker(); if(ov) { if(this!=ov && other_overtaker!=ov) { return false; } } else if( v->get_waytype()==road_wt && v->get_typ()!=obj_t::pedestrian ) { return false; } } } gr = to; check_pos = to->get_pos(); direction = ~ribi_type( check_pos, pos_prev ) & str->get_ribi(); } // Second phase: only facing traffic is forbidden // Since street speed can change, we do the calculation with time. // Each empty tile will subtract tile_dimension/max_street_speed. // If time is exhausted, we are guaranteed that no facing traffic will // invade the dangerous zone. time_overtaking = (time_overtaking << 16)/(sint32)current_speed; do { // we can allow crossings or traffic lights here, since they will stop also oncoming traffic if( ribi_t::is_straight(str->get_ribi()) ) { time_overtaking -= (VEHICLE_STEPS_PER_TILE<<16) / kmh_to_speed(str->get_max_speed()); } else { time_overtaking -= (diagonal_vehicle_steps_per_tile<<16) / kmh_to_speed(str->get_max_speed()); } // start of bridge is one level deeper if(gr->get_weg_yoff()>0) { check_pos.z ++; } // much cheaper calculation: only go on in the direction of before ... grund_t *to = welt->lookup( check_pos + koord((ribi_t::ribi)(str->get_ribi()&direction)) ); if( ribi_t::is_threeway(str->get_ribi()) || to==NULL ) { // check for crossings/bridges, if necessary bool found_one = false; for( int r=0; r<4; r++ ) { if(check_pos.get_2d()+koord::nesw[r]==pos_prev) { continue; } if(gr->get_neighbour(to, road_wt, ribi_t::nesw[r])) { if(found_one) { return false; } found_one = true; } } } koord pos_prev_prev = pos_prev; pos_prev = check_pos.get_2d(); // nowhere to go => nobody can come against us ... if(to==NULL || (str=to->get_weg(road_wt))==NULL) { break; } // Check for other vehicles in facing direction // now only I know direction on this tile ... ribi_t::ribi their_direction = ribi_t::backward(calc_direction( pos_prev_prev, to->get_pos())); const uint8 top = gr->obj_count(); for( uint8 j=1; j(gr->obj_bei(j)); if( v && v->get_direction() == their_direction ) { // check for car if(v->get_overtaker()) { return false; } } } gr = to; check_pos = to->get_pos(); direction = ~ribi_type( check_pos, pos_prev ) & str->get_ribi(); } while( time_overtaking > 0 ); set_tiles_overtaking( 1+n_tiles ); other_overtaker->set_tiles_overtaking( -1-(n_tiles/2) ); return true; } simutrans-124.5/src/simutrans/vehicle/simroadtraffic.h000066400000000000000000000075641520675460500232010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_SIMROADTRAFFIC_H #define VEHICLE_SIMROADTRAFFIC_H #include "vehicle_base.h" #include "overtaker.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/freelist_iter_tpl.h" class citycar_desc_t; class karte_t; /** * Base class for traffic participants with random movement * * Transport vehicles are defined in vehicle.h, because they greatly * differ from the vehicles defined herein for the individual traffic * (pedestrians, citycars, movingobj aka flock of sheep). */ class road_user_t : public vehicle_base_t { protected: /** * Distance count */ uint32 weg_next; /* ms until destruction */ sint32 time_to_life; protected: waytype_t get_waytype() const OVERRIDE { return road_wt; } road_user_t(); /** * Creates thing at position given by @p gr. * Does not add it to the tile! * * @param gr * @param random number to compute initial direction. */ road_user_t(grund_t* gr, uint16 random); public: virtual ~road_user_t(); const char *get_name() const OVERRIDE = 0; typ get_typ() const OVERRIDE = 0; /** * Open a new observation window for the object. */ void show_info() OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; // finalizes direction void finish_rd() OVERRIDE; // we allow to remove all cars etc. const char *get_removal_error(const player_t *) OVERRIDE { return NULL; } }; class private_car_t : public road_user_t, public overtaker_t { private: static stringhashtable_tpl table; const citycar_desc_t *desc; // time to life in blocks #ifdef DESTINATION_CITYCARS koord target; #endif koord3d pos_next_next; /** * Actual speed */ uint16 current_speed; uint32 ms_traffic_jam; grund_t* hop_check() OVERRIDE; void calc_disp_lane(); bool can_enter_tile(grund_t *gr); static freelist_iter_tpl fl; // if not declared static, it would consume 4 bytes due to empty class nonzero rules protected: void rdwr(loadsave_t *file) OVERRIDE; void calc_image() OVERRIDE; public: private_car_t(loadsave_t *file); /** * Creates citycar at position given by @p gr. * Does not add car to the tile! * If @p name == NULL then a random car is created. */ private_car_t(grund_t* gr, koord target, const char* name = NULL); virtual ~private_car_t(); sync_result sync_step(uint32 delta_t); void* operator new(size_t) { return fl.gimme_node(); } void operator delete(void* p) { return fl.putback_node(p); } static void sync_handler(uint32 delta_t) { fl.sync_step(delta_t); } void rotate90() OVERRIDE; const citycar_desc_t* get_desc() const { return desc; } void hop(grund_t *gr) OVERRIDE; void enter_tile(grund_t* gr) OVERRIDE; void calc_current_speed(grund_t*); uint16 get_current_speed() const {return current_speed;} const char *get_name() const OVERRIDE {return "Verkehrsteilnehmer";} typ get_typ() const OVERRIDE { return road_user; } /** * @param[out] buf a description string for the object * e.g. for the observation window/dialog * @see simwin */ void info(cbuffer_t & buf) const OVERRIDE; // true, if this vehicle did not moved for some time bool is_stuck() OVERRIDE { return current_speed==0;} /** this function builds the list of the allowed citycars * it should be called every month and in the beginning of a new game */ static void build_timeline_list(karte_t *welt); static bool list_empty(); static bool register_desc(const citycar_desc_t *desc); static bool successfully_loaded(); // since we must consider overtaking, we use this for offset calculation void get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const OVERRIDE; overtaker_t *get_overtaker() OVERRIDE { return this; } // Overtaking for city cars bool can_overtake(overtaker_t *other_overtaker, sint32 other_speed, sint16 steps_other) OVERRIDE; }; #endif simutrans-124.5/src/simutrans/vehicle/simtestdriver.h000066400000000000000000000024241520675460500230760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_SIMTESTDRIVER_H #define VEHICLE_SIMTESTDRIVER_H #include "../simtypes.h" #include "../dataobj/ribi.h" class grund_t; class weg_t; /** * Interface to connect the vehicle with its route */ class test_driver_t { public: virtual ~test_driver_t() {} virtual bool check_next_tile(const grund_t* ) const = 0; /** * Determine the direction bits (ribi) for the applicable vehicle, * Depends of the ground type. */ virtual ribi_t::ribi get_ribi(const grund_t* ) const = 0; virtual waytype_t get_waytype() const = 0; /** * How expensive to go here (for way search). * @param gr tile to check * @param w way on the tile (can be NULL) * @param max_speed the maximum convoi speed as second parameter * @param from direction in which we enter the tile */ virtual int get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const = 0; // returns true for the way search to an unknown target. // first is current ground, second is starting ground virtual bool is_target(const grund_t *,const grund_t *) const = 0; // return the cost of a single step upwards virtual uint32 get_cost_upslope() const { return 0; } }; #endif simutrans-124.5/src/simutrans/vehicle/vehicle.cc000066400000000000000000001044351520675460500217540ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "vehicle.h" #include "../simintr.h" #include "../world/simworld.h" #include "../simware.h" #include "../simconvoi.h" #include "../simhalt.h" #include "../dataobj/translator.h" #include "../simfab.h" #include "../player/simplay.h" #include "../dataobj/schedule.h" #include "../gui/minimap.h" #include "../obj/crossing.h" #include "../obj/wolke.h" #include "../utils/cbuffer.h" #include "../dataobj/environment.h" #include "../dataobj/pakset_manager.h" #include "../builder/vehikelbauer.h" #include "../obj/zeiger.h" #include "../utils/simstring.h" #include void vehicle_t::rotate90() { vehicle_base_t::rotate90(); previous_direction = ribi_t::rotate90( previous_direction ); last_stop_pos.rotate90( welt->get_size().y-1 ); } void vehicle_t::rotate90_freight_destinations(const sint16 y_size) { // now rotate the freight for(ware_t & tmp : fracht) { tmp.rotate90(y_size ); } } void vehicle_t::set_convoi(convoi_t *c) { /* cnv can have three values: * NULL: not previously assigned * 1 (only during loading): convoi wants to reserve the whole route * other: previous convoi (in this case, currently always c==cnv) * * if c is NULL, then the vehicle is removed from the convoi * (the rail_vehicle_t::set_convoi etc. routines must then remove a * possibly pending reservation of stops/tracks) */ assert( c==NULL || cnv==NULL || cnv==(convoi_t *)1 || c==cnv); cnv = c; if(cnv) { // we need to re-establish the finish flag after loading if(leading) { route_t const& r = *cnv->get_route(); check_for_finish = r.empty() || route_index >= r.get_count() || get_pos() == r.at(route_index); } if( pos_next != koord3d::invalid ) { route_t const& r = *cnv->get_route(); if (!r.empty() && route_index < r.get_count() - 1) { grund_t const* const gr = welt->lookup(pos_next); if (!gr || !gr->get_weg(get_waytype())) { if (!(water_wt == get_waytype() && gr && gr->is_water())) { // ships on the open sea are valid pos_next = r.at(route_index + 1U); } } } } // just correct freight destinations for(ware_t & c : fracht) { c.finish_rd(welt); } } } /** * Unload freight to halt * @return sum of unloaded goods */ uint16 vehicle_t::unload_cargo(halthandle_t halt, bool unload_all, uint16 max_amount ) { uint16 sum_menge = 0, sum_delivered = 0, index = 0; if( !halt.is_bound() ) { return 0; } if( halt->is_enabled( get_cargo_type() ) ) { if( !fracht.empty() ) { for( slist_tpl::iterator i = fracht.begin(), end = fracht.end(); i != end && max_amount > 0; ) { ware_t& tmp = *i; halthandle_t end_halt = tmp.get_target_halt(); halthandle_t via_halt = tmp.get_via_halt(); // check if destination or transfer is still valid if( !end_halt.is_bound() || !via_halt.is_bound() ) { // target halt no longer there => delete and remove from fab in transit fabrik_t::update_transit( &tmp, false ); DBG_MESSAGE("vehicle_t::unload_freight()", "destination of %d %s is no longer reachable",tmp.amount,translator::translate(tmp.get_name())); total_freight -= tmp.amount; sum_weight -= tmp.amount * tmp.get_desc()->get_weight_per_unit(); i = fracht.erase( i ); } else if( end_halt == halt || via_halt == halt || unload_all ) { // here, only ordinary goods should be processed uint32 org_menge = tmp.amount; if (tmp.amount > max_amount) { tmp.amount = max_amount; } // since the max capacity of a vehicle is an uint16 uint16 menge = (uint16)halt->liefere_an(tmp); max_amount -= menge; sum_menge += menge; total_freight -= menge; sum_weight -= tmp.amount * tmp.get_desc()->get_weight_per_unit(); index = tmp.get_index(); if(end_halt==halt) { sum_delivered += menge; } // in case of partial unlaoding tmp.amount = org_menge-tmp.amount; if (tmp.amount == 0) { i = fracht.erase(i); } } else { ++i; } } } } if( sum_menge ) { // book transported goods get_owner()->book_transported( sum_menge, get_desc()->get_waytype(), index ); if( sum_delivered ) { // book delivered goods to destination get_owner()->book_delivered( sum_delivered, get_desc()->get_waytype(), index ); } // add delivered goods to statistics cnv->book( sum_menge, convoi_t::CONVOI_TRANSPORTED_GOODS ); // add delivered goods to halt's statistics halt->book( sum_menge, HALT_ARRIVED ); } return sum_menge; } /** * Load freight from halt * @return amount loaded */ uint16 vehicle_t::load_cargo(halthandle_t halt, const vector_tpl& destination_halts, uint16 max_amount ) { if( !halt.is_bound() || !halt->gibt_ab(desc->get_freight_type()) ) { return 0; } const uint16 total_freight_start = total_freight; const uint16 capacity_left = min(desc->get_capacity() - total_freight, max_amount); if (capacity_left > 0) { slist_tpl freight_add; halt->fetch_goods( freight_add, desc->get_freight_type(), capacity_left, destination_halts); if( freight_add.empty() ) { // now empty, but usually, we can get it here ... return 0; } for( slist_tpl::iterator iter_z = freight_add.begin(); iter_z != freight_add.end(); ) { ware_t &ware = *iter_z; total_freight += ware.amount; sum_weight += ware.amount * ware.get_desc()->get_weight_per_unit(); // could this be joined with existing freight? for(ware_t & tmp : fracht ) { // for pax: join according next stop // for all others we *must* use target coordinates if( ware.same_destination(tmp) ) { tmp.amount += ware.amount; ware.amount = 0; break; } } // if != 0 we could not join it to existing => load it if( ware.amount != 0 ) { ++iter_z; // we add list directly } else { iter_z = freight_add.erase(iter_z); } } if( !freight_add.empty() ) { fracht.append_list(freight_add); } } return total_freight - total_freight_start; } /** * Remove freight that no longer can reach it's destination * i.e. because of a changed schedule */ void vehicle_t::remove_stale_cargo() { DBG_DEBUG("vehicle_t::remove_stale_cargo()", "called"); // and now check every piece of ware on board, // if its target is somewhere on // the new schedule, if not -> remove slist_tpl kill_queue; total_freight = 0; if (!fracht.empty()) { for(ware_t & tmp : fracht) { bool found = false; if( tmp.get_via_halt().is_bound() ) { // the original halt exists, but does we still go there? for(schedule_entry_t const& i : cnv->get_schedule()->entries) { if( haltestelle_t::get_halt( i.pos, cnv->get_owner(), get_waytype()==water_wt) == tmp.get_via_halt() ) { found = true; break; } } } if( !found ) { // the target halt may have been joined or there is a closer one now, thus our original target is no longer valid const int offset = cnv->get_schedule()->get_current_stop(); const int max_count = cnv->get_schedule()->entries.get_count(); for( int i=0; iget_schedule()->entries[ (i+offset)%max_count ].pos, cnv->get_owner(), get_waytype() == water_wt); if( halt.is_bound() ) { if( halt->is_enabled(tmp.get_index()) ) { // ok, lets change here, since goods are accepted here tmp.set_via_halt(halt); if (!tmp.get_target_halt().is_bound()) { // set target, to prevent that unload_freight drops cargo tmp.set_target_halt( halt ); } found = true; break; } } } } if( !found ) { kill_queue.insert(tmp); } else { // since we need to point at factory (0,0), we recheck this too koord k = tmp.get_target_pos(); fabrik_t *fab = fabrik_t::get_fab( k ); tmp.set_target_pos( fab ? fab->get_pos().get_2d() : k ); total_freight += tmp.amount; } } for(ware_t const& c : kill_queue) { fabrik_t::update_transit( &c, false ); fracht.remove(c); } } sum_weight = get_cargo_weight() + desc->get_weight(); } void vehicle_t::play_sound() const { if( desc->get_sound() >= 0 && !welt->is_fast_forward() ) { welt->play_sound_area_clipped(get_pos().get_2d(), desc->get_sound(), TRAFFIC_SOUND ); } } /** * Prepare vehicle for new ride. * Sets route_index, pos_next, steps_next. * If @p recalc is true this sets position and recalculates/resets movement parameters. */ void vehicle_t::initialise_journey(route_t::index_t start_route_index, bool recalc) { route_index = start_route_index+1; check_for_finish = false; use_calc_height = true; if(welt->is_within_limits(get_pos().get_2d())) { mark_image_dirty( get_image(), 0 ); } route_t const& r = *cnv->get_route(); if(!recalc) { // always set pos_next pos_next = r.at(route_index); assert(get_pos() == r.at(start_route_index)); } else { // set pos_next if (route_index < r.get_count()) { pos_next = r.at(route_index); } else { // already at end of route check_for_finish = true; } set_pos(r.at(start_route_index)); // recalc directions previous_direction = direction; direction = calc_set_direction( get_pos(), pos_next ); zoff_start = zoff_end = 0; steps = 0; set_xoff( (dx<0) ? OBJECT_OFFSET_STEPS : -OBJECT_OFFSET_STEPS ); set_yoff( (dy<0) ? OBJECT_OFFSET_STEPS/2 : -OBJECT_OFFSET_STEPS/2 ); calc_image(); } if ( ribi_t::is_single(direction) ) { steps_next = VEHICLE_STEPS_PER_TILE - 1; } else { steps_next = diagonal_vehicle_steps_per_tile - 1; } } vehicle_t::vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player) : vehicle_base_t(pos) { this->desc = desc; set_owner( player ); purchase_time = welt->get_current_month(); cnv = NULL; speed_limit = SPEED_UNLIMITED; route_index = 1; smoke = true; direction = ribi_t::none; current_friction = 4; total_freight = 0; sum_weight = desc->get_weight(); leading = last = false; check_for_finish = false; use_calc_height = true; has_driven = false; previous_direction = direction = ribi_t::none; target_halt = halthandle_t(); } vehicle_t::vehicle_t() : vehicle_base_t() { smoke = true; desc = NULL; cnv = NULL; route_index = 1; current_friction = 4; sum_weight = 10; total_freight = 0; leading = last = false; check_for_finish = false; use_calc_height = true; previous_direction = direction = ribi_t::none; } bool vehicle_t::calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route) { return route->calc_route(welt, start, ziel, this, max_speed, 0 ); } grund_t* vehicle_t::hop_check() { // the leading vehicle will do all the checks if(leading) { if(check_for_finish) { // so we are there yet? cnv->ziel_erreicht(); if(cnv->get_state()==convoi_t::INITIAL) { // to avoid crashes with airplanes use_calc_height = false; } return NULL; } // now check, if we can go here grund_t *bd = welt->lookup(pos_next); if(bd==NULL || !check_next_tile(bd) || cnv->get_route()->empty()) { // way (weg) not existent (likely destroyed) or no route ... cnv->suche_neue_route(); return NULL; } // check for one-way sign etc. const waytype_t wt = get_waytype(); if( air_wt != wt && route_index < cnv->get_route()->get_count()-1 ) { uint8 dir = get_ribi(bd); koord3d nextnext_pos = cnv->get_route()->at(route_index+1); if ( nextnext_pos == get_pos() ) { dbg->error("vehicle_t::hop_check", "route contains point (%s) twice for %s", nextnext_pos.get_str(), cnv->get_name()); } uint8 new_dir = ribi_type(nextnext_pos - pos_next); if((dir&new_dir)==0) { // new one way sign here? cnv->suche_neue_route(); return NULL; } // check for recently built bridges/tunnels or reverse branches (really slows down the game, so we do this only on slopes) if( bd->get_weg_hang() ) { grund_t *from; if( !bd->get_neighbour( from, get_waytype(), ribi_type( get_pos(), pos_next ) ) ) { // way likely destroyed or altered => reroute cnv->suche_neue_route(); return NULL; } } } sint32 restart_speed = -1; // can_enter_tile() wll compute restart_speed if( !can_enter_tile( bd, restart_speed, 0 ) ) { // stop convoi, when the way is not free cnv->warten_bis_weg_frei(restart_speed); // don't continue return NULL; } // we cache it here, hop() will use it to save calls to karte_t::lookup return bd; } else { // this is needed since in convoi_t::vorfahren the flag 'leading' is set to null if(check_for_finish) { return NULL; } } return welt->lookup(pos_next); } bool vehicle_t::can_enter_tile(sint32 &restart_speed, uint8 second_check_count) { grund_t *gr = welt->lookup( pos_next ); if( gr ) { return can_enter_tile( gr, restart_speed, second_check_count ); } else { if( !second_check_count ) { cnv->suche_neue_route(); } return false; } } void vehicle_t::leave_tile() { vehicle_base_t::leave_tile(); #ifndef DEBUG_ROUTES if(last && minimap_t::is_visible) { minimap_t::get_instance()->calc_map_pixel(get_pos().get_2d()); } #endif } /** this routine add a vehicle to a tile and will insert it in the correct sort order to prevent overlaps */ void vehicle_t::enter_tile(grund_t* gr) { vehicle_base_t::enter_tile(gr); if(leading && minimap_t::is_visible ) { minimap_t::get_instance()->calc_map_pixel( get_pos().get_2d() ); } } void vehicle_t::hop(grund_t* gr) { leave_tile(); koord3d pos_prev = get_pos(); set_pos( pos_next ); // next field if(route_indexget_route()->get_count()-1) { route_index ++; pos_next = cnv->get_route()->at(route_index); } else { route_index ++; check_for_finish = true; } previous_direction = direction; // check if arrived at waypoint, and update schedule to next destination // route search through the waypoint is already complete if( get_pos()==cnv->get_schedule_target() ) { if( route_index >= cnv->get_route()->get_count() ) { // we end up here after loading a game or when a waypoint is reached which crosses next itself cnv->set_schedule_target( koord3d::invalid ); } else { cnv->get_schedule()->advance(); const koord3d ziel = cnv->get_schedule()->get_current_entry().pos; cnv->set_schedule_target( cnv->is_waypoint(ziel) ? ziel : koord3d::invalid ); } } // this is a required hack for aircrafts! Aircrafts can turn on a single square, and this confuses the previous calculation! if(!check_for_finish && pos_prev==pos_next) { direction = calc_set_direction( get_pos(), pos_next); steps_next = 0; } else { if( pos_next!=get_pos() ) { direction = calc_set_direction( pos_prev, pos_next ); } else if( ( check_for_finish && welt->lookup(pos_next) && ribi_t::is_straight(welt->lookup(pos_next)->get_weg_ribi_unmasked(get_waytype())) ) || welt->lookup(pos_next)->is_halt()) { // allow diagonal stops at waypoints on diagonal tracks but avoid them on halts and at straight tracks... direction = calc_set_direction( pos_prev, pos_next ); } } // change image if direction changes if (previous_direction != direction) { calc_image(); } sint32 old_speed_limit = speed_limit; enter_tile(gr); const weg_t *weg = gr->get_weg(get_waytype()); if( weg ) { speed_limit = kmh_to_speed( weg->get_max_speed() ); if (crossing_t* cr = gr->get_crossing()) { cr->add_to_crossing(this); } } else { speed_limit = SPEED_UNLIMITED; } if( leading ) { if( check_for_finish && (direction==ribi_t::north || direction==ribi_t::west) ) { steps_next = (steps_next/2)+1; } cnv->add_running_cost( weg ); cnv->must_recalc_data_front(); } // update friction and friction weight of convoy sint16 old_friction = current_friction; calc_friction(gr); if (old_friction != current_friction) { cnv->update_friction_weight( (current_friction-old_friction) * (sint64)sum_weight); } // if speed limit changed, then cnv must recalc if (speed_limit != old_speed_limit) { if (speed_limit < old_speed_limit) { if (speed_limit < cnv->get_speed_limit()) { // update cnv->set_speed_limit(speed_limit); } } else { if (old_speed_limit == cnv->get_speed_limit()) { // convoy's speed limit may be larger now cnv->must_recalc_speed_limit(); } } } } /** calculates the current friction coefficient based on the current track * flat, slope, curve ... */ void vehicle_t::calc_friction(const grund_t *gr) { // assume straight flat track current_friction = 1; // curve: higher friction if(previous_direction != direction) { current_friction = 8; } // or a hill? const slope_t::type hang = gr->get_weg_hang(); if( hang != slope_t::flat ) { const uint slope_height = is_one_high(hang) ? 1 : 2; if( ribi_type(hang) == direction ) { // hill up, since height offsets are negative: heavy decelerate current_friction += 15 * slope_height * slope_height; } else { // hill down: accelerate current_friction += -7 * slope_height * slope_height; } } } void vehicle_t::make_smoke() const { // does it smoke at all? if( smoke && desc->get_smoke() ) { // only produce smoke when heavily accelerating or steam engine if( cnv->get_akt_speed() < (sint32)((cnv->get_speed_limit() * 7u) >> 3) || desc->get_engine_type() == vehicle_desc_t::steam ) { grund_t* const gr = welt->lookup( get_pos() ); if( gr ) { wolke_t* const abgas = new wolke_t( get_pos(), get_xoff() + ((dx * (sint16)((uint16)steps * OBJECT_OFFSET_STEPS)) >> VEHICLE_STEPS_PER_TILE_SHIFT), get_yoff() + ((dy * (sint16)((uint16)steps * OBJECT_OFFSET_STEPS)) >> VEHICLE_STEPS_PER_TILE_SHIFT), get_hoff() - LEGACY_SMOKE_YOFFSET, DEFAULT_EXHAUSTSMOKE_TIME, DEFAULT_SMOKE_UPLIFT, desc->get_smoke() ); if( !gr->obj_add( abgas ) ) { abgas->set_flag( obj_t::not_on_map ); delete abgas; } } } } } /** * Payment is done per hop. It iterates all goods and calculates * the income for the last hop. This method must be called upon * every stop. * @return income total for last hop */ sint64 vehicle_t::calc_revenue(const koord3d& start, const koord3d& end) const { // may happen when waiting in station if (start == end || fracht.empty()) { return 0; } // cnv_kmh = lesser of min_top_speed, power limited top speed, and average way speed limits on trip, except aircraft which are not power limited and don't have speed limits sint32 cnv_kmh = cnv->get_speedbonus_kmh(); sint64 value = 0; // cache speedbonus price const goods_desc_t* last_freight = NULL; sint64 freight_revenue = 0; sint32 dist = 0; if( welt->get_settings().get_pay_for_total_distance_mode() == settings_t::TO_PREVIOUS ) { // pay distance traveled dist = koord_distance( start, end ); } for(ware_t const& ware : fracht) { if( ware.amount==0 ) { continue; } // which distance will be paid? switch(welt->get_settings().get_pay_for_total_distance_mode()) { case settings_t::TO_TRANSFER: { // pay distance traveled to next transfer stop // now only use the real gain in difference for the revenue (may as well be negative!) if (ware.get_via_halt().is_bound()) { const koord &zwpos = ware.get_via_halt()->get_basis_pos(); // cast of koord_distance to sint32 is necessary otherwise the r-value would be interpreted as unsigned, leading to overflows dist = (sint32)koord_distance( zwpos, start ) - (sint32)koord_distance( end, zwpos ); } else { dist = koord_distance( end, start ); } break; } case settings_t::TO_DESTINATION: { // pay only the distance, we get closer to our destination // now only use the real gain in difference for the revenue (may as well be negative!) const koord &zwpos = ware.get_target_pos(); // cast of koord_distance to sint32 is necessary otherwise the r-value would be interpreted as unsigned, leading to overflows dist = (sint32)koord_distance( zwpos, start ) - (sint32)koord_distance( end, zwpos ); break; } default: ; // no need to recompute } // calculate freight revenue incl. speed-bonus if (ware.get_desc() != last_freight) { freight_revenue = ware_t::calc_revenue(ware.get_desc(), get_desc()->get_waytype(), cnv_kmh); last_freight = ware.get_desc(); } const sint64 price = freight_revenue * (sint64)dist * (sint64)ware.amount; // sum up new price value += price; } // Rounded value, in cents return (value+1500ll)/3000ll; } const char *vehicle_t::get_cargo_mass() const { return get_cargo_type()->get_mass(); } /** * Calculate transported cargo total weight in KG */ uint32 vehicle_t::get_cargo_weight() const { uint32 weight = 0; for(ware_t const& c : fracht) { weight += c.amount * c.get_desc()->get_weight_per_unit(); } return weight; } void vehicle_t::get_cargo_info(cbuffer_t & buf) const { if (fracht.empty()) { buf.append(" "); buf.append(translator::translate("leer")); buf.append("\n"); } else { for(ware_t const& ware : fracht) { const char * name = "Error in Routing"; halthandle_t halt = ware.get_target_halt(); if(halt.is_bound()) { name = halt->get_name(); } buf.printf(" %u%s %s > %s\n", ware.amount, translator::translate(ware.get_mass()), translator::translate(ware.get_name()), name); } } } /** * Delete all vehicle load */ void vehicle_t::discard_cargo() { for(ware_t w : fracht ) { fabrik_t::update_transit( &w, false ); } fracht.clear(); sum_weight = desc->get_weight(); } void vehicle_t::calc_image() { image_id old_image=get_image(); if (fracht.empty()) { set_image(desc->get_image_id(ribi_t::get_dir(get_direction()),NULL)); } else { set_image(desc->get_image_id(ribi_t::get_dir(get_direction()), fracht.front().get_desc())); } if(old_image!=get_image()) { set_flag(obj_t::dirty); } } image_id vehicle_t::get_loaded_image() const { return desc->get_image_id(ribi_t::dir_south, fracht.empty() ? NULL : fracht.front().get_desc()); } // true, if this vehicle did not moved for some time bool vehicle_t::is_stuck() { return cnv==NULL || cnv->is_waiting(); } void vehicle_t::rdwr(loadsave_t *file) { // this is only called from objlist => we save nothing ... assert( file->is_saving() ); (void)file; } void vehicle_t::rdwr_from_convoi(loadsave_t *file) { xml_tag_t r( file, "vehikel_t" ); sint32 fracht_count = 0; if(file->is_saving()) { fracht_count = fracht.get_count(); // we try to have one freight count to guess the right freight // when no desc is given if(fracht_count==0 && desc->get_freight_type()!=goods_manager_t::none && desc->get_capacity()>0) { fracht_count = 1; } } obj_t::rdwr(file); // since obj_t does no longer save positions if( file->is_version_atleast(101, 0) ) { koord3d pos = get_pos(); pos.rdwr(file); set_pos(pos); } sint8 hoff = file->is_saving() ? get_hoff() : 0; if(file->is_version_less(86, 6)) { sint32 l; file->rdwr_long(purchase_time); file->rdwr_long(l); dx = (sint8)l; file->rdwr_long(l); dy = (sint8)l; file->rdwr_long(l); hoff = (sint8)(l*TILE_HEIGHT_STEP/16); file->rdwr_long(speed_limit); file->rdwr_enum(direction); file->rdwr_enum(previous_direction); file->rdwr_long(fracht_count); file->rdwr_long(l); route_index = (uint16)l; purchase_time = (purchase_time >> welt->ticks_per_world_month_shift) + welt->get_settings().get_starting_year(); DBG_MESSAGE("vehicle_t::rdwr_from_convoi()","bought at %i/%i.",(purchase_time%12)+1,purchase_time/12); } else { // changed several data types to save runtime memory file->rdwr_long(purchase_time); if(file->is_version_less(99, 18)) { file->rdwr_byte(dx); file->rdwr_byte(dy); } else { file->rdwr_byte(steps); file->rdwr_byte(steps_next); if(steps_next==old_diagonal_vehicle_steps_per_tile - 1 && file->is_loading()) { // reset diagonal length (convoi will be reset anyway, if game diagonal is different) steps_next = diagonal_vehicle_steps_per_tile - 1; } } sint16 dummy16 = ((16*(sint16)hoff)/TILE_HEIGHT_STEP); file->rdwr_short(dummy16); hoff = (sint8)((TILE_HEIGHT_STEP*(sint16)dummy16)/16); file->rdwr_long(speed_limit); file->rdwr_enum(direction); file->rdwr_enum(previous_direction); file->rdwr_long(fracht_count); file->rdwr_short(route_index); // restore dxdy information dx = dxdy[ ribi_t::get_dir(direction)*2]; dy = dxdy[ ribi_t::get_dir(direction)*2+1]; } // convert steps to position if(file->is_version_less(99, 18)) { sint8 ddx=get_xoff(), ddy=get_yoff()-hoff; sint8 i=1; dx = dxdy[ ribi_t::get_dir(direction)*2]; dy = dxdy[ ribi_t::get_dir(direction)*2+1]; while( !is_about_to_hop(ddx+dx*i,ddy+dy*i ) && i<16 ) { i++; } i--; set_xoff( ddx-(16-i)*dx ); set_yoff( ddy-(16-i)*dy ); if(file->is_loading()) { if(dx && dy) { steps = min( VEHICLE_STEPS_PER_TILE - 1, VEHICLE_STEPS_PER_TILE - 1-(i*16) ); steps_next = VEHICLE_STEPS_PER_TILE - 1; } else { // will be corrected anyway, if in a convoi steps = min( diagonal_vehicle_steps_per_tile - 1, diagonal_vehicle_steps_per_tile - 1-(uint8)(((uint16)i*(uint16)(diagonal_vehicle_steps_per_tile - 1))/8) ); steps_next = diagonal_vehicle_steps_per_tile - 1; } } } // information about the target halt if(file->is_version_atleast(88, 7)) { bool target_info; if(file->is_loading()) { file->rdwr_bool(target_info); cnv = (convoi_t *)target_info; // will be checked during convoi reassignment } else { target_info = target_halt.is_bound(); file->rdwr_bool(target_info); } } else { if(file->is_loading()) { cnv = NULL; // no reservation too } } if(file->is_version_less(112, 9)) { koord3d pos_prev(koord3d::invalid); pos_prev.rdwr(file); } if(file->is_version_less(99, 5)) { koord3d dummy; dummy.rdwr(file); // current pos (is already saved as ding => ignore) } pos_next.rdwr(file); if(file->is_saving()) { const char *s = desc->get_name(); file->rdwr_str(s); } else { char s[256]; file->rdwr_str(s, lengthof(s)); desc = vehicle_builder_t::get_info(s); if(desc==NULL) { desc = vehicle_builder_t::get_info(translator::compatibility_name(s)); } if(desc==NULL) { pakset_manager_t::add_missing_paks( s, MISSING_VEHICLE ); dbg->warning("vehicle_t::rdwr_from_convoi()","no vehicle pak for '%s' search for something similar", s); } } if(file->is_saving()) { if (fracht.empty() && fracht_count>0) { // create dummy freight for savegame compatibility ware_t ware( desc->get_freight_type() ); ware.amount = 0; ware.set_target_halt( halthandle_t() ); ware.set_via_halt( halthandle_t() ); ware.set_target_pos( get_pos().get_2d() ); ware.rdwr(file); } else { for(ware_t ware : fracht) { ware.rdwr(file); } } } else { for(int i=0; i0) && welt->is_within_limits(ware.get_target_pos()) && ware.get_desc() ) { // also add, of the desc is unknown to find matching replacement fracht.append(ware); #ifdef CACHE_TRANSIT if( file->is_version_less(112, 1) ) #endif // restore in-transit information fabrik_t::update_transit( &ware, true ); } else if( ware.amount>0 ) { if( ware.get_desc() ) { dbg->error( "vehicle_t::rdwr_from_convoi()", "%i of %s to %s ignored!", ware.amount, ware.get_name(), ware.get_target_pos().get_str() ); } else { dbg->error( "vehicle_t::rdwr_from_convoi()", "%i of unknown to %s ignored!", ware.amount, ware.get_target_pos().get_str() ); } } } } // skip first last info (the convoi will know this better than we!) if(file->is_version_less(88, 7)) { bool dummy = 0; file->rdwr_bool(dummy); file->rdwr_bool(dummy); } // koordinate of the last stop if(file->is_version_atleast(99, 15)) { // This used to be 2d, now it's 3d. if(file->is_version_less(112, 8)) { if(file->is_saving()) { koord last_stop_pos_2d = last_stop_pos.get_2d(); last_stop_pos_2d.rdwr(file); } else { // loading. Assume ground level stop (could be wrong, but how would we know?) koord last_stop_pos_2d = koord::invalid; last_stop_pos_2d.rdwr(file); const grund_t* gr = welt->lookup_kartenboden(last_stop_pos_2d); if (gr) { last_stop_pos = koord3d(last_stop_pos_2d, gr->get_hoehe()); } else { // no ground?!? last_stop_pos = koord3d::invalid; } } } else { // current version, 3d last_stop_pos.rdwr(file); } } if(file->is_loading()) { leading = last = false; // dummy, will be set by convoi afterwards if(desc) { calc_image(); // full weight after loading sum_weight = get_cargo_weight() + desc->get_weight(); } // recalc total freight total_freight = 0; for(ware_t const& c : fracht) { total_freight += c.amount; } } if( file->is_version_atleast(110, 0) ) { bool hd = has_driven; file->rdwr_bool( hd ); has_driven = hd; } else { if (file->is_loading()) { has_driven = false; } } } uint32 vehicle_t::calc_sale_value() const { // if already used, there is a general price reduction double value = (double)desc->get_price(); if( has_driven ) { value *= (1000 - welt->get_settings().get_used_vehicle_reduction()) / 1000.0; } // after 20 year, it has only half value return (uint32)( value * pow(0.997, (int)(welt->get_current_month() - get_purchase_time()))); } void vehicle_t::show_info() { if( cnv != NULL ) { cnv->open_info_window(); } else { dbg->warning("vehicle_t::show_info()","cnv is null, can't open convoi window!"); } } void vehicle_t::info(cbuffer_t & buf) const { if(cnv) { cnv->info(buf); } } const char *vehicle_t::get_removal_error(const player_t *) { return "Fahrzeuge koennen so nicht entfernt werden"; } vehicle_t::~vehicle_t() { // remove vehicle's marker from the minimap minimap_t::get_instance()->calc_map_pixel(get_pos().get_2d()); } #ifdef MULTI_THREAD void vehicle_t::display_overlay(int xpos, int ypos) const { if( cnv && leading ) { #else void vehicle_t::display_after(int xpos, int ypos, bool is_global) const { if( is_global && cnv && leading ) { #endif PIXVAL color = 0; // not used, but stop compiler warning about uninitialized char tooltip_text[1024]; tooltip_text[0] = 0; uint8 state = env_t::show_vehicle_states; if( state==1 || state==2 ) { // mouse over check bool mo_this_convoy = false; const koord3d mouse_pos = world()->get_zeiger()->get_pos(); if( mouse_pos == get_pos() ) { mo_this_convoy = true; } else if( grund_t* mo_gr = world()->lookup(mouse_pos) ) { if( vehicle_t* mo_veh = (vehicle_t *)mo_gr->get_convoi_vehicle() ) { mo_this_convoy = mo_veh->get_convoi() == get_convoi(); } } // only show when mouse over vehicle if( mo_this_convoy ) { state = 3; } else { state = 0; } } if( state != 3 ) { // nothing to show return; } // now find out what has happened switch(cnv->get_state()) { case convoi_t::WAITING_FOR_CLEARANCE_ONE_MONTH: case convoi_t::WAITING_FOR_CLEARANCE: case convoi_t::CAN_START: case convoi_t::CAN_START_ONE_MONTH: if( state>=3 ) { snprintf( tooltip_text, lengthof(tooltip_text), "%s (%s)", translator::translate("Waiting for clearance!"), cnv->get_schedule()->get_current_entry().pos.get_str() ); color = gfx->palette_lookup(COL_YELLOW); } break; case convoi_t::LOADING: if( state>=3 ) { if( cnv->is_unloading() ) { sprintf( tooltip_text, translator::translate( "Unloading (%i%%)!" ), cnv->get_loading_level() ); } else if( cnv->get_schedule()->get_current_entry().minimum_loading == 0 && cnv->get_schedule()->get_current_entry().waiting_time >0 ) { // is on a schedule sprintf(tooltip_text, translator::translate("Loading (%i%%) departure %s!"), cnv->get_loading_level(), tick_to_string(cnv->get_departure_ticks(),true)); } else if( cnv->get_loading_limit()==0 ){ sprintf( tooltip_text, translator::translate( "Loading (%i%%)!" ), cnv->get_loading_level(), cnv->get_loading_limit() ); } else { sprintf( tooltip_text, translator::translate( "Loading (%i->%i%%)!" ), cnv->get_loading_level(), cnv->get_loading_limit() ); } color = gfx->palette_lookup(COL_YELLOW); } break; case convoi_t::EDIT_SCHEDULE: // case convoi_t::ROUTING_1: if( state>=3 ) { tstrncpy( tooltip_text, translator::translate("Schedule changing!"), lengthof(tooltip_text) ); color = gfx->palette_lookup(COL_YELLOW); } break; case convoi_t::DRIVING: if( state>=3 ) { grund_t const* const gr = welt->lookup(cnv->get_route()->back()); if( gr && gr->get_depot() ) { tstrncpy( tooltip_text, translator::translate("go home"), lengthof(tooltip_text) ); color = gfx->palette_lookup(COL_GREEN); } else if( cnv->get_no_load() ) { tstrncpy( tooltip_text, translator::translate("no load"), lengthof(tooltip_text) ); color = gfx->palette_lookup(COL_GREEN); } } break; case convoi_t::LEAVING_DEPOT: if( state>=2 ) { tstrncpy( tooltip_text, translator::translate("Leaving depot!"), lengthof(tooltip_text) ); color = gfx->palette_lookup(COL_GREEN); } break; case convoi_t::WAITING_FOR_CLEARANCE_TWO_MONTHS: case convoi_t::CAN_START_TWO_MONTHS: snprintf( tooltip_text, lengthof(tooltip_text), "%s (%s)", translator::translate("clf_chk_stucked"), cnv->get_schedule()->get_current_entry().pos.get_str() ); color = gfx->palette_lookup(COL_ORANGE); break; case convoi_t::NO_ROUTE: tstrncpy( tooltip_text, translator::translate("clf_chk_noroute"), lengthof(tooltip_text) ); color = gfx->palette_lookup(COL_RED); break; } if (env_t::show_vehicle_states >= 2 && !tooltip_text[0]) { // show line name or simply convoi name color = gfx->palette_lookup( cnv->get_owner()->get_player_color1() + 7 ); if( cnv->get_line().is_bound() ) { snprintf( tooltip_text, lengthof( tooltip_text ), "%s - %s", cnv->get_line()->get_name(), cnv->get_name() ); } else { snprintf( tooltip_text, lengthof( tooltip_text ), "%s", cnv->get_name() ); } } // something to show? if( tooltip_text[0] ) { const int raster_width = gfx->get_current_tile_raster_width(); get_screen_offset( xpos, ypos, raster_width ); xpos += tile_raster_scale_x(get_xoff(), raster_width); ypos += tile_raster_scale_y(get_yoff(), raster_width)+14; if(ypos>LINESPACE+32 && ypos+LINESPACE < gfx->get_clip_rect(CLIP_NUM_DEFAULT_VALUE).yy) { gfx->draw_textbox3d_clipped( xpos, ypos, color, gfx->palette_lookup(COL_BLACK), tooltip_text, true CLIP_NUM_DEFAULT); } } } } simutrans-124.5/src/simutrans/vehicle/vehicle.h000066400000000000000000000163111520675460500216110ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_VEHICLE_H #define VEHICLE_VEHICLE_H #include "vehicle_base.h" #include "../ground/grund.h" #include "../descriptor/vehicle_desc.h" #include "../vehicle/simtestdriver.h" #include "../tpl/slist_tpl.h" #include "../dataobj/route.h" class schedule_t; class signal_t; class ware_t; class route_t; class goods_desc_t; /** * Class for all vehicles with route */ class vehicle_t : public vehicle_base_t, public test_driver_t { private: /** * Date of purchase in months */ sint32 purchase_time; /** * For the more physical acceleration model friction is introduced * frictionforce = gamma*speed*weight * since the total weight is needed a lot of times, we save it */ uint32 sum_weight; grund_t* hop_check() OVERRIDE; /** * Calculate friction caused by slopes and curves. */ virtual void calc_friction(const grund_t *gr); protected: void hop(grund_t*) OVERRIDE; // current limit (due to track etc.) sint32 speed_limit; ribi_t::ribi previous_direction; // for target reservation and search halthandle_t target_halt; /** The friction is calculated new every step, so we save it too */ sint16 current_friction; /// Current index on the route route_t::index_t route_index; uint16 total_freight; // since the sum is needed quite often, it is cached slist_tpl fracht; // list of goods being transported const vehicle_desc_t *desc; convoi_t *cnv; // != NULL if the vehicle is part of a Convoi bool leading:1; // true, if vehicle is first vehicle of a convoi bool last:1; // true, if vehicle is last vehicle of a convoi bool smoke:1; bool check_for_finish:1; // true, if on the last tile bool has_driven:1; bool check_next_tile(const grund_t* ) const OVERRIDE {return false;} public: void calc_image() OVERRIDE; // the coordinates, where the vehicle was loaded the last time koord3d last_stop_pos; convoi_t *get_convoi() const { return cnv; } void rotate90() OVERRIDE; /** * Method checks whether next tile is free to move on. * Looks up next tile, and calls @ref can_enter_tile(const grund_t*, sint32&, uint8). */ bool can_enter_tile(sint32 &restart_speed, uint8 second_check_count); /** * Method checks whether next tile is free to move on. * * @param second_check_count * @param[out] gr_next next tile, must not be NULL * @param[out] restart_speed */ virtual bool can_enter_tile(const grund_t *gr_next, sint32 &restart_speed, uint8 second_check_count) = 0; void enter_tile(grund_t*) OVERRIDE; void leave_tile() OVERRIDE; waytype_t get_waytype() const OVERRIDE = 0; /** * Determine the direction bits for this kind of vehicle. */ ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return gr->get_weg_ribi(get_waytype()); } sint32 get_purchase_time() const {return purchase_time;} void set_smoke_enabled(bool yesno ) { smoke = yesno;} virtual bool calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route); route_t::index_t get_route_index() const { return route_index; } /** * Get the base image. */ image_id get_base_image() const { return desc->get_base_image(); } /** * @return image with base direction and freight image taken from loaded cargo */ image_id get_loaded_image() const; /** * @return vehicle description object */ const vehicle_desc_t *get_desc() const {return desc; } /** * @return die running_cost in Cr/100Km */ int get_operating_cost() const { return desc->get_running_cost(); } /** * Play sound, when the vehicle is visible on screen */ void play_sound() const; /** * Prepare vehicle for new ride. * Sets route_index, pos_next, steps_next. * If @p recalc is true this sets position and recalculates/resets movement parameters. */ void initialise_journey( route_t::index_t start_route_index, bool recalc ); vehicle_t(); vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player); ~vehicle_t(); void make_smoke() const; void show_info() OVERRIDE; void info(cbuffer_t & buf) const OVERRIDE; /** * return friction constant: changes in hill and curves; may even negative downhill * */ inline sint16 get_frictionfactor() const { return current_friction; } /** * Return total weight including freight (in kg!) */ inline uint32 get_total_weight() const { return sum_weight; } // returns speedlimit of ways (and if convoi enters station etc) // the convoi takes care of the max_speed of the vehicle sint32 get_speed_limit() const { return speed_limit; } const slist_tpl & get_cargo() const { return fracht;} // list of goods being transported /** * Rotate freight target coordinates, has to be called after rotating factories. */ void rotate90_freight_destinations(const sint16 y_size); /** * Calculate the total quantity of goods moved */ uint16 get_total_cargo() const { return total_freight; } /** * Calculate transported cargo total weight in KG */ uint32 get_cargo_weight() const; /** * get the type of cargo this vehicle can transport */ const goods_desc_t* get_cargo_type() const { return desc->get_freight_type(); } /** * Get the maximum capacity */ uint16 get_cargo_max() const {return desc->get_capacity(); } const char * get_cargo_mass() const; /** * create an info text for the freight * e.g. to display in a info window */ void get_cargo_info(cbuffer_t & buf) const; /** * Delete all vehicle load */ void discard_cargo(); /** * Payment is done per hop. It iterates all goods and calculates * the income for the last hop. This method must be called upon * every stop. * @return income total for last hop */ sint64 calc_revenue(const koord3d& start, const koord3d& end) const; // sets or query begin and end of convois void set_leading(bool janein) {leading = janein;} bool is_leading() {return leading;} void set_last(bool janein) {last = janein;} bool is_last() {return last;} // marks the vehicle as really used void set_driven() { has_driven = true; } virtual void set_convoi(convoi_t *c); /** * Unload freight to halt * @return sum of unloaded goods */ uint16 unload_cargo(halthandle_t halt, bool all, uint16 max_amount ); /** * Load freight from halt * @return amount loaded */ uint16 load_cargo(halthandle_t halt, const vector_tpl& destination_halts, uint16 max_amount ); /** * Remove freight that no longer can reach it's destination * i.e. because of a changed schedule */ void remove_stale_cargo(); /** * Generate a matching schedule for the vehicle type */ virtual schedule_t *generate_new_schedule() const = 0; /// @copydoc obj_t::get_removal_error const char *get_removal_error(const player_t *player) OVERRIDE; void rdwr(loadsave_t *file) OVERRIDE; virtual void rdwr_from_convoi(loadsave_t *file); uint32 calc_sale_value() const; // true, if this vehicle did not moved for some time bool is_stuck() OVERRIDE; // this routine will display a tooltip for lost, on depot order, and stuck vehicles #ifdef MULTI_THREAD void display_overlay(int xpos, int ypos) const OVERRIDE; #else void display_after(int xpos, int ypos, bool dirty) const OVERRIDE; #endif }; template<> inline vehicle_t* obj_cast(obj_t* const d) { return dynamic_cast(d); } #endif simutrans-124.5/src/simutrans/vehicle/vehicle_base.cc000066400000000000000000000415001520675460500227370ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "vehicle_base.h" #include "road_vehicle.h" #include "simroadtraffic.h" #include "vehicle.h" #include "../dataobj/environment.h" #include "../simunits.h" #include "../world/simworld.h" #include "../obj/crossing.h" #include "../simconvoi.h" /* get dx and dy from dir (just to remind you) * any vehicle (including city cars and pedestrians) * will go this distance per sync step. * (however, the real dirs are only calculated during display, these are the old ones) */ sint8 vehicle_base_t::dxdy[ 8*2 ] = { -2, 1, // s -2, -1, // w -4, 0, // sw 0, 2, // se 2, -1, // n 2, 1, // e 4, 0, // ne 0, -2 // nw }; // Constants uint8 vehicle_base_t::old_diagonal_vehicle_steps_per_tile = 128; uint8 vehicle_base_t::diagonal_vehicle_steps_per_tile = 181; uint16 vehicle_base_t::diagonal_multiplier = 724; // set only once, before loading! void vehicle_base_t::set_diagonal_multiplier( uint32 multiplier, uint32 old_diagonal_multiplier ) { diagonal_multiplier = (uint16)multiplier; diagonal_vehicle_steps_per_tile = (uint8)(130560u/diagonal_multiplier) + 1; old_diagonal_vehicle_steps_per_tile = (uint8)(130560u/max(old_diagonal_multiplier, 1u)) + 1; } // if true, convoi, must restart! bool vehicle_base_t::need_realignment() const { return old_diagonal_vehicle_steps_per_tile!=diagonal_vehicle_steps_per_tile && ribi_t::is_bend(direction); } // [0]=xoff [1]=yoff sint8 vehicle_base_t::driveleft_base_offsets[8][2] = { { 12, 6 }, { -12, 6 }, { 0, 6 }, { 12, 0 }, { -12, -6 }, { 12, -6 }, { 0, -6 }, { -12, 0 } }; // [0]=xoff [1]=yoff sint8 vehicle_base_t::overtaking_base_offsets[8][2]; // recalc offsets for overtaking void vehicle_base_t::set_overtaking_offsets( bool driving_on_the_left ) { const sint8 sign = driving_on_the_left ? -1 : 1; // a tile has the internal size of const sint8 XOFF = 12; const sint8 YOFF = 6; overtaking_base_offsets[0][0] = sign * XOFF; overtaking_base_offsets[1][0] = -sign * XOFF; overtaking_base_offsets[2][0] = 0; overtaking_base_offsets[3][0] = sign * XOFF; overtaking_base_offsets[4][0] = -sign * XOFF; overtaking_base_offsets[5][0] = sign * XOFF; overtaking_base_offsets[6][0] = 0; overtaking_base_offsets[7][0] = sign * (-XOFF-YOFF); overtaking_base_offsets[0][1] = sign * YOFF; overtaking_base_offsets[1][1] = sign * YOFF; overtaking_base_offsets[2][1] = sign * YOFF; overtaking_base_offsets[3][1] = 0; overtaking_base_offsets[4][1] = -sign * YOFF; overtaking_base_offsets[5][1] = -sign * YOFF; overtaking_base_offsets[6][1] = -sign * YOFF; overtaking_base_offsets[7][1] = 0; } /** * Checks if this vehicle must change the square upon next move * THIS IS ONLY THERE FOR LOADING OLD SAVES! */ bool vehicle_base_t::is_about_to_hop( const sint8 neu_xoff, const sint8 neu_yoff ) const { const sint8 y_off_2 = 2*neu_yoff; const sint8 c_plus = y_off_2 + neu_xoff; const sint8 c_minus = y_off_2 - neu_xoff; return ! (c_plus < OBJECT_OFFSET_STEPS*2 && c_minus < OBJECT_OFFSET_STEPS*2 && c_plus > -OBJECT_OFFSET_STEPS*2 && c_minus > -OBJECT_OFFSET_STEPS*2); } vehicle_base_t::vehicle_base_t(): obj_t() { image = IMG_EMPTY; set_flag( obj_t::is_vehicle ); steps = 0; steps_next = VEHICLE_STEPS_PER_TILE - 1; use_calc_height = true; dx = 0; dy = 0; zoff_start = zoff_end = 0; disp_lane = 2; } vehicle_base_t::vehicle_base_t(koord3d pos): obj_t(pos) { image = IMG_EMPTY; set_flag( obj_t::is_vehicle ); pos_next = pos; steps = 0; steps_next = VEHICLE_STEPS_PER_TILE - 1; use_calc_height = true; dx = 0; dy = 0; zoff_start = zoff_end = 0; disp_lane = 2; } void vehicle_base_t::rotate90() { obj_t::rotate90(); // directions are counterclockwise to ribis! direction = ribi_t::rotate90( direction ); pos_next.rotate90( welt->get_size().y-1 ); // new offsets sint8 new_dx = -dy*2; dy = dx/2; dx = new_dx; } void vehicle_base_t::leave_tile() { grund_t* gr = welt->lookup(get_pos()); // first: release crossing if (gr) { if (crossing_t* cr = gr->get_crossing()) { grund_t* gr2 = welt->lookup(pos_next); bool release = gr2==NULL || gr2 == gr; if (!release) { // check if we stay on crossing if (crossing_t* cr2 = gr2->get_crossing()) { // only release if new crossing ahead release = (cr->get_logic() != cr2->get_logic()); } } if (release) { cr->release_crossing(this); } else { // release when no longer a crossing cr->release_crossing(this); } } } // then remove from ground (or search whole map, if failed) if(!get_flag(not_on_map) && (gr==NULL || !gr->obj_remove(this)) ) { // was not removed (not found?) dbg->error("vehicle_base_t::leave_tile()", "%s %p could not be removed from (%s)", get_name(), (void *)this, get_pos().get_str()); DBG_MESSAGE("vehicle_base_t::leave_tile()", "checking all plan squares"); // check, whether it is on another height ... const planquadrat_t *pl = welt->access( get_pos().get_2d() ); if( pl ) { gr = pl->get_boden_von_obj(this); if( gr ) { gr->obj_remove(this); dbg->warning("vehicle_base_t::leave_tile()", "Removed %s %p from (%s)", get_name(), (void *)this, get_pos().get_str()); } return; } koord k; bool ok = false; for(k.y=0; k.yget_size().y; k.y++) { for(k.x=0; k.xget_size().x; k.x++) { grund_t *gr = welt->access( k )->get_boden_von_obj(this); if(gr && gr->obj_remove(this)) { dbg->warning("vehicle_base_t::leave_tile()", "Removed %s %p from (%s)", get_name(), (void *)this, k.get_str()); ok = true; } } } if(!ok) { dbg->error("vehicle_base_t::leave_tile()", "%s %p was not found on any map square!", get_name(), (void *)this); } } } void vehicle_base_t::enter_tile(grund_t* gr) { if(!gr) { dbg->error("vehicle_base_t::enter_tile()","'%s' new position (%i,%i,%i)!",get_name(), get_pos().x, get_pos().y, get_pos().z ); gr = welt->lookup_kartenboden(get_pos().get_2d()); set_pos( gr->get_pos() ); } gr->obj_add(this); } /* THE routine for moving vehicles * it will drive on as log as it can * @return the distance actually traveled */ uint32 vehicle_base_t::do_drive(uint32 distance) { uint32 steps_to_do = distance >> YARDS_PER_VEHICLE_STEP_SHIFT; if( steps_to_do == 0 ) { // ok, we will not move in this steps return 0; } // ok, so moving ... if( !get_flag(obj_t::dirty) ) { mark_image_dirty( image, 0 ); set_flag( obj_t::dirty ); } grund_t *gr = NULL; // if hopped, then this is new position uint32 steps_target = steps_to_do + steps; uint32 distance_travelled; // Return value if( steps_target > steps_next ) { // We are going far enough to hop. // We'll be adding steps_next+1 for each hop, as if we // started at the beginning of this tile, so for an accurate // count of steps done we must subtract the location we started with. sint32 steps_done = -steps; // Hop as many times as possible. while( steps_target > steps_next && (gr = hop_check()) ) { // now do the update for hopping steps_target -= steps_next+1; steps_done += steps_next+1; koord pos_prev(get_pos().get_2d()); hop(gr); use_calc_height = true; // set offsets set_xoff( (dx<0) ? OBJECT_OFFSET_STEPS : -OBJECT_OFFSET_STEPS ); set_yoff( (dy<0) ? OBJECT_OFFSET_STEPS/2 : -OBJECT_OFFSET_STEPS/2 ); if(dx*dy==0) { if(dx==0) { if(dy>0) { set_xoff( pos_prev.x!=get_pos().x ? -OBJECT_OFFSET_STEPS : OBJECT_OFFSET_STEPS ); } else { set_xoff( pos_prev.x!=get_pos().x ? OBJECT_OFFSET_STEPS : -OBJECT_OFFSET_STEPS ); } } else { if(dx>0) { set_yoff( pos_prev.y!=get_pos().y ? OBJECT_OFFSET_STEPS/2 : -OBJECT_OFFSET_STEPS/2 ); } else { set_yoff( pos_prev.y!=get_pos().y ? -OBJECT_OFFSET_STEPS/2 : OBJECT_OFFSET_STEPS/2 ); } } } } if( steps_next == 0 ) { // only needed for aircrafts, which can turn on the same tile // the indicate the turn with this here steps_next = VEHICLE_STEPS_PER_TILE - 1; steps_target = VEHICLE_STEPS_PER_TILE - 1; steps_done -= VEHICLE_STEPS_PER_TILE - 1; } // Update internal status, how far we got within the tile. if( steps_target <= steps_next ) { steps = steps_target; } else { // could not go as far as we wanted (hop_check failed) => stop at end of tile steps = steps_next; } steps_done += steps; distance_travelled = steps_done << YARDS_PER_VEHICLE_STEP_SHIFT; } else { // Just travel to target, it's on same tile steps = steps_target; distance_travelled = distance & YARDS_VEHICLE_STEP_MASK; // round down to nearest step } if(use_calc_height) { calc_height(gr); } // remaining steps return distance_travelled; } // to make smaller steps than the tile granularity, we have to use this trick void vehicle_base_t::get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const { // vehicles needs finer steps to appear smoother sint32 display_steps = (uint32)steps*(uint16)raster_width; if(dx && dy) { display_steps &= 0xFFFFFC00; } else { display_steps = (display_steps*diagonal_multiplier)>>10; } xoff += (display_steps*dx) >> 10; yoff += ((display_steps*dy) >> 10) + (get_hoff(raster_width))/(4*16); } // calcs new direction and applies it to the vehicles ribi_t::ribi vehicle_base_t::calc_set_direction(const koord3d& start, const koord3d& ende) { ribi_t::ribi direction = ribi_t::none; const sint8 di = ende.x - start.x; const sint8 dj = ende.y - start.y; if(dj < 0 && di == 0) { direction = ribi_t::north; dx = 2; dy = -1; steps_next = VEHICLE_STEPS_PER_TILE - 1; } else if(dj > 0 && di == 0) { direction = ribi_t::south; dx = -2; dy = 1; steps_next = VEHICLE_STEPS_PER_TILE - 1; } else if(di < 0 && dj == 0) { direction = ribi_t::west; dx = -2; dy = -1; steps_next = VEHICLE_STEPS_PER_TILE - 1; } else if(di >0 && dj == 0) { direction = ribi_t::east; dx = 2; dy = 1; steps_next = VEHICLE_STEPS_PER_TILE - 1; } else if(di > 0 && dj > 0) { direction = ribi_t::southeast; dx = 0; dy = 2; steps_next = diagonal_vehicle_steps_per_tile - 1; } else if(di < 0 && dj < 0) { direction = ribi_t::northwest; dx = 0; dy = -2; steps_next = diagonal_vehicle_steps_per_tile - 1; } else if(di > 0 && dj < 0) { direction = ribi_t::northeast; dx = 4; dy = 0; steps_next = diagonal_vehicle_steps_per_tile - 1; } else { direction = ribi_t::southwest; dx = -4; dy = 0; steps_next = diagonal_vehicle_steps_per_tile - 1; } // we could artificially make diagonals shorter: but this would break existing game behaviour return direction; } // this routine calculates the new height // beware of bridges, tunnels, slopes, ... void vehicle_base_t::calc_height(grund_t *gr) { use_calc_height = false; // assume, we are only needed after next hop zoff_start = zoff_end = 0; // assume flat way if(gr==NULL) { gr = welt->lookup(get_pos()); } if(gr==NULL) { // slope changed below a moving thing?!? return; } else if( gr->ist_tunnel() && gr->ist_karten_boden() && !is_flying() ) { use_calc_height = true; // to avoid errors if underground mode is switched if( grund_t::underground_mode == grund_t::ugm_none || (grund_t::underground_mode == grund_t::ugm_level && gr->get_hoehe() < grund_t::underground_level) ) { // need hiding? One of the few uses of XOR: not half driven XOR exiting => not hide! ribi_t::ribi hang_ribi = ribi_type( gr->get_grund_hang() ); if((steps<(steps_next/2)) ^ ((hang_ribi&direction)!=0) ) { set_image(IMG_EMPTY); } else { calc_image(); } } } else { // force a valid image above ground, with special handling of tunnel entraces if( get_image()==IMG_EMPTY ) { if( !gr->ist_tunnel() && gr->ist_karten_boden() ) { calc_image(); } } // will not work great with ways, but is very short! slope_t::type hang = gr->get_weg_hang(); if( hang ) { const uint slope_height = is_one_high(hang) ? 1 : 2; ribi_t::ribi hang_ribi = ribi_type(hang); if( ribi_t::doubles(hang_ribi) == ribi_t::doubles(direction)) { zoff_start = hang_ribi & direction ? 2*slope_height : 0; // 0 .. 4 zoff_end = hang_ribi & ribi_t::backward(direction) ? 2*slope_height : 0; // 0 .. 4 } else { // only for shadows and movingobjs ... zoff_start = hang_ribi & direction ? slope_height+1 : 0; // 0 .. 3 zoff_end = hang_ribi & ribi_t::backward(direction) ? slope_height+1 : 0; // 0 .. 3 } } else { zoff_start = (gr->get_weg_yoff() * 2) / TILE_HEIGHT_STEP; zoff_end = zoff_start; } } } sint16 vehicle_base_t::get_hoff(const sint16 raster_width) const { sint16 h_start = -(sint8)TILE_HEIGHT_STEP * (sint8)zoff_start; sint16 h_end = -(sint8)TILE_HEIGHT_STEP * (sint8)zoff_end; return ((h_start*steps + h_end*(256-steps))*raster_width) >> 9; } /* true, if one could pass through this field * also used for citycars, thus defined here */ vehicle_base_t *vehicle_base_t::no_cars_blocking( const grund_t *gr, const convoi_t *cnv, const uint8 current_direction, const uint8 next_direction, const uint8 next_90direction ) { // Search vehicle for( uint8 pos=1; pos<(uint8)gr->obj_count(); pos++ ) { if( vehicle_base_t* const v = obj_cast(gr->obj_bei(pos)) ) { if( v->get_typ()==obj_t::pedestrian ) { continue; } // check for car uint8 other_direction=255; bool other_moving = false; if( road_vehicle_t const* const at = obj_cast(v) ) { // ignore ourself if( cnv == at->get_convoi() ) { continue; } other_direction = at->get_direction(); other_moving = at->get_convoi()->get_akt_speed() > kmh_to_speed(1); } // check for city car else if( v->get_waytype() == road_wt ) { other_direction = v->get_direction(); if( private_car_t const* const sa = obj_cast(v) ){ other_moving = sa->get_current_speed() > 1; } } // ok, there is another car ... if( other_direction != 255 ) { if( next_direction == other_direction && !ribi_t::is_threeway(gr->get_weg_ribi(road_wt)) ) { // cars going in the same direction and no crossing => that mean blocking ... return v; } const ribi_t::ribi other_90direction = (gr->get_pos().get_2d() == v->get_pos_next().get_2d()) ? other_direction : calc_direction(gr->get_pos(), v->get_pos_next()); if( other_90direction == next_90direction ) { // Want to exit in same as other ~50% of the time return v; } const bool drives_on_left = welt->get_settings().is_drive_left(); const bool across = next_direction == (drives_on_left ? ribi_t::rotate45l(next_90direction) : ribi_t::rotate45(next_90direction)); // turning across the opposite directions lane const bool other_across = other_direction == (drives_on_left ? ribi_t::rotate45l(other_90direction) : ribi_t::rotate45(other_90direction)); // other is turning across the opposite directions lane if( other_direction == next_direction && !(other_across || across) ) { // entering same straight waypoint as other ~18% return v; } const bool straight = next_direction == next_90direction; // driving straight const ribi_t::ribi current_90direction = straight ? ribi_t::backward(next_90direction) : (~(next_direction|ribi_t::backward(next_90direction)))&0x0F; const bool other_straight = other_direction == other_90direction; // other is driving straight const bool other_exit_same_side = current_90direction == other_90direction; // other is exiting same side as we're entering const bool other_exit_opposite_side = ribi_t::backward(current_90direction) == other_90direction; // other is exiting side across from where we're entering if( across && ((ribi_t::is_perpendicular(current_90direction,other_direction) && other_moving) || (other_across && other_exit_opposite_side) || ((other_across || other_straight) && other_exit_same_side && other_moving) ) ) { // other turning across in front of us from orth entry dir'n ~4% return v; } const bool headon = ribi_t::backward(current_direction) == other_direction; // we're meeting the other headon const bool other_exit_across = (drives_on_left ? ribi_t::rotate90l(next_90direction) : ribi_t::rotate90(next_90direction)) == other_90direction; // other is exiting by turning across the opposite directions lane if( straight && (ribi_t::is_perpendicular(current_90direction,other_direction) || (other_across && other_moving && (other_exit_across || (other_exit_same_side && !headon))) ) ) { // other turning across in front of us, but allow if other is stopped - duplicating historic behaviour ~2% return v; } else if( other_direction == current_direction && current_90direction == ribi_t::none ) { // entering same diagonal waypoint as other ~1% return v; } // else other car is not blocking ~25% } } } // way is free return NULL; } simutrans-124.5/src/simutrans/vehicle/vehicle_base.h000066400000000000000000000116201520675460500226010ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_VEHICLE_BASE_H #define VEHICLE_VEHICLE_BASE_H #include "../obj/simobj.h" class convoi_t; class grund_t; class overtaker_t; /** * Base class for all moving things (vehicles, pedestrians, movingobj) */ class vehicle_base_t : public obj_t { protected: // offsets for different directions static sint8 dxdy[16]; // to make the length on diagonals configurable // Number of vehicle steps along a diagonal... // remember to subtract one when stepping down to 0 static uint8 diagonal_vehicle_steps_per_tile; static uint8 old_diagonal_vehicle_steps_per_tile; static uint16 diagonal_multiplier; // [0]=xoff [1]=yoff static sint8 driveleft_base_offsets[8][2]; static sint8 overtaking_base_offsets[8][2]; /** * Actual travel direction in screen coordinates */ ribi_t::ribi direction; // true on slope (make calc_height much faster) uint8 use_calc_height:1; /** * Thing is moving on this lane. * Possible values: * (Back) * 0 - sidewalk (going on the right side to w/sw/s) * 1 - road (going on the right side to w/sw/s) * 2 - middle (everything with waytype != road) * 3 - road (going on the right side to se/e/../nw) * 4 - sidewalk (going on the right side to se/e/../nw) * (Front) */ uint8 disp_lane:3; sint8 dx, dy; // number of steps in this tile (255 per tile) uint8 steps, steps_next; /** * Next position on our path */ koord3d pos_next; /** * Offsets for uphill/downhill. * Have to be multiplied with -TILE_HEIGHT_STEP/2. * To obtain real z-offset, interpolate using steps, steps_next. */ uint8 zoff_start:4, zoff_end:4; // cached image image_id image; /** * Vehicle movement: check whether this vehicle can enter the next tile (pos_next). * @returns NULL if check fails, otherwise pointer to the next tile */ virtual grund_t* hop_check() = 0; /** * Vehicle movement: change tiles, calls leave_tile and enter_tile. * @param gr pointer to ground of new position (never NULL) */ virtual void hop(grund_t* gr) = 0; void calc_image() OVERRIDE = 0; // check for road vehicle, if next tile is free vehicle_base_t *no_cars_blocking( const grund_t *gr, const convoi_t *cnv, const uint8 current_direction, const uint8 next_direction, const uint8 next_90direction ); // only needed for old way of moving vehicles to determine position at loading time bool is_about_to_hop( const sint8 neu_xoff, const sint8 neu_yoff ) const; public: // only called during load time: set some offsets static void set_diagonal_multiplier( uint32 multiplier, uint32 old_multiplier ); static uint16 get_diagonal_multiplier() { return diagonal_multiplier; } static uint8 get_diagonal_vehicle_steps_per_tile() { return diagonal_vehicle_steps_per_tile; } static void set_overtaking_offsets( bool driving_on_the_left ); // if true, this convoi needs to restart for correct alignment bool need_realignment() const; uint32 do_drive(uint32 dist); // basis movement code inline void set_image( image_id b ) { image = b; } image_id get_image() const OVERRIDE {return image;} sint16 get_hoff(const sint16 raster_width=1) const; uint8 get_steps() const {return steps;} uint8 get_disp_lane() const { return disp_lane; } // to make smaller steps than the tile granularity, we have to calculate our offsets ourselves! virtual void get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const; /** * Vehicle movement: calculates z-offset of vehicles on slopes, * handles vehicles that are invisible in tunnels. * @param gr vehicle is on this ground * @note has to be called after loading to initialize z-offsets */ void calc_height(grund_t *gr = NULL); void rotate90() OVERRIDE; template static ribi_t::ribi calc_direction(const K1& from, const K2& to) { return ribi_type(from, to); } ribi_t::ribi calc_set_direction(const koord3d& start, const koord3d& ende); ribi_t::ribi get_direction() const {return direction;} koord3d get_pos_next() const {return pos_next;} void set_pos_next(koord3d pn) { pos_next = pn; } waytype_t get_waytype() const OVERRIDE = 0; // true, if this vehicle did not moved for some time virtual bool is_stuck() { return true; } /** * Vehicle movement: enter tile, add this to the ground. * @pre position (obj_t::pos) needs to be updated prior to calling this functions * @param gr pointer to ground (never NULL) */ virtual void enter_tile(grund_t *gr); /** * Vehicle movement: leave tile, release reserved crossing, remove vehicle from the ground. */ virtual void leave_tile(); virtual overtaker_t *get_overtaker() { return NULL; } vehicle_base_t(); vehicle_base_t(koord3d pos); virtual bool is_flying() const { return false; } }; template<> inline vehicle_base_t* obj_cast(obj_t* const d) { return d->is_moving() ? static_cast(d) : 0; } #endif simutrans-124.5/src/simutrans/vehicle/water_vehicle.cc000066400000000000000000000061631520675460500231550ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "water_vehicle.h" #include "../simware.h" #include "../builder/goods_manager.h" #include "../builder/vehikelbauer.h" #include "../obj/crossing.h" #include "../dataobj/schedule.h" #include "../obj/roadsign.h" #include "../simconvoi.h" water_vehicle_t::water_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cn) : vehicle_t(pos, desc, player) { cnv = cn; } water_vehicle_t::water_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : vehicle_t() { vehicle_t::rdwr_from_convoi(file); if( file->is_loading() ) { static const vehicle_desc_t *last_desc = NULL; if(is_first) { last_desc = NULL; } // try to find a matching vehicle if(desc==NULL) { dbg->warning("water_vehicle_t::water_vehicle_t()", "try to find a fitting vehicle for %s.", !fracht.empty() ? fracht.front().get_name() : "passengers"); desc = vehicle_builder_t::get_best_matching(water_wt, 0, fracht.empty() ? 0 : 30, 100, 40, !fracht.empty() ? fracht.front().get_desc() : goods_manager_t::passengers, true, last_desc, is_last ); if(desc) { calc_image(); } } // update last desc if( desc ) { last_desc = desc; } } } void water_vehicle_t::enter_tile(grund_t* gr) { vehicle_t::enter_tile(gr); if( weg_t *ch = gr->get_weg(water_wt) ) { // we are in a channel, so book statistics ch->book(get_total_cargo(), WAY_STAT_GOODS); if (leading) { ch->book(1, WAY_STAT_CONVOIS); } } } bool water_vehicle_t::check_next_tile(const grund_t *bd) const { if( bd->is_water() ) { return true; } // channel can have more stuff to check const weg_t *w = bd->get_weg(water_wt); #ifdef ENABLE_WATERWAY_SIGNS if( w && w->has_sign() ) { const roadsign_t* rs = bd->find(); if( rs->get_desc()->get_wtyp()==get_waytype() ) { if( cnv !=NULL && rs->get_desc()->get_min_speed() > 0 && rs->get_desc()->get_min_speed() > cnv->get_min_top_speed() ) { // below speed limit return false; } if( rs->get_desc()->is_private_way() && (rs->get_player_mask() & (1<get_max_speed()>0); } /** Since slopes are handled different for ships */ void water_vehicle_t::calc_friction(const grund_t *gr) { // or a hill? if(gr->get_weg_hang()) { // hill up or down => in lock => decelerate current_friction = 16; } else { // flat track current_friction = 1; } if(previous_direction != direction) { // curve: higher friction current_friction *= 2; } } bool water_vehicle_t::can_enter_tile(const grund_t *gr, sint32 &restart_speed, uint8) { restart_speed = -1; if(leading) { assert(gr); if( gr->obj_count()>251 ) { // too many ships already here .. return false; } if(crossing_t* cr = gr->get_crossing()) { // ok, here is a draw/turn-bridge ... if(!cr->request_crossing(this)) { restart_speed = 0; return false; } } } return true; } schedule_t * water_vehicle_t::generate_new_schedule() const { return new ship_schedule_t(); } simutrans-124.5/src/simutrans/vehicle/water_vehicle.h000066400000000000000000000023341520675460500230130ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef VEHICLE_WATER_VEHICLE_H #define VEHICLE_WATER_VEHICLE_H #include "vehicle.h" /** * A class for naval vehicles. Manages the look of the vehicles * and the navigability of tiles. * @see vehicle_t */ class water_vehicle_t : public vehicle_t { protected: // how expensive to go here (for way search) int get_cost(const grund_t *, const weg_t*, const sint32, ribi_t::ribi) const OVERRIDE { return 1; } void calc_friction(const grund_t *gr) OVERRIDE; bool check_next_tile(const grund_t *bd) const OVERRIDE; void enter_tile(grund_t*) OVERRIDE; public: waytype_t get_waytype() const OVERRIDE { return water_wt; } bool can_enter_tile(const grund_t *gr_next, sint32 &restart_speed, uint8) OVERRIDE; // returns true for the way search to an unknown target. bool is_target(const grund_t *,const grund_t *) const OVERRIDE {return 0;} water_vehicle_t(loadsave_t *file, bool is_first, bool is_last); water_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cnv); obj_t::typ get_typ() const OVERRIDE { return water_vehicle; } schedule_t * generate_new_schedule() const OVERRIDE; }; #endif simutrans-124.5/src/simutrans/world/000077500000000000000000000000001520675460500175275ustar00rootroot00000000000000simutrans-124.5/src/simutrans/world/building_placefinder.h000066400000000000000000000011701520675460500240300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef WORLD_BUILDING_PLACEFINDER_H #define WORLD_BUILDING_PLACEFINDER_H #include "placefinder.h" #include "simworld.h" /** * building_placefinder_t: * * Search a free site for building using function find_place(). */ class building_placefinder_t : public placefinder_t { public: building_placefinder_t(karte_t *welt, sint16 radius = -1) : placefinder_t(welt, radius) {} bool is_area_ok(koord pos, sint16 w, sint16 h, climate_bits cl) const OVERRIDE { return welt->square_is_free(pos, w, h, NULL, cl); } }; #endif simutrans-124.5/src/simutrans/world/placefinder.cc000066400000000000000000000112361520675460500223150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include "simworld.h" #include "../simintr.h" #include "../simtypes.h" #include "placefinder.h" pos_list_t::pos_list_t(sint16 max_radius) { this->max_radius = max_radius + 1; columns = new sint16[this->max_radius]; restart(); } pos_list_t::~pos_list_t() { delete [] columns; } void pos_list_t::restart() { radius = 1; columns[0] = 0; row = 0; quadrant = 0; } sint16 pos_list_t::find_best_row() { sint16 best_dist = -1; sint16 best_row = -1; for(sint16 i = 0; i < radius; i++) { if(columns[i] < radius) { int dist = (int)i * i + columns[i] * columns[i]; if(best_dist == -1 || dist < best_dist) { best_dist = dist; best_row = i; } } } if(radius < max_radius && best_dist > radius * radius) { return -1; } else { return best_row; } } bool pos_list_t::get_next_pos(koord &k) { if(row != -1) { if(quadrant++ == 4) { quadrant = 1; columns[row]++; row = find_best_row(); if(row == -1) { if(radius < max_radius) { columns[radius++] = 0; } row = find_best_row(); } } } if(row != -1) { if(quadrant == 1 && !row) { quadrant += 2; // skip second 0, +/-y } if(quadrant % 2 == 1 && !columns[row]) { quadrant ++; // skip second +/-x, 0 } return get_pos(k); } return false; } bool pos_list_t::get_pos(koord &k) { if(row != -1) { switch(quadrant) { case 1: k = koord(row, columns[row]); return true; case 2: k = koord(row, (short)-columns[row]); return true; case 3: k = koord((short)-row, columns[row]); return true; case 4: k = koord(-row, -columns[row]); return true; } } return false; } pos_list_wh_t::pos_list_wh_t(sint16 max_radius, sint16 w, sint16 h) : pos_list_t(max_radius) { restart(w, h); } void pos_list_wh_t::restart(sint16 w, sint16 h) { this->w = w; this->h = h; dx = dy = 0; pos_list_t::restart(); } bool pos_list_wh_t::get_next_pos(koord &k) { get_pos(k); if(k.x == 0 && k.y == 0 && (dx > 0 || dy > 0)) { if(dx > 0) { dx--; } else if(dy > 0) { dy--; dx = w - 1; } k.x -= dx; k.y -= dy; } else if(dx > 0) { k.x -= --dx; if(k.y <= 0) { k.y -= h - 1; } } else if(dy > 0) { k.y -= --dy; if(k.x <= 0) { k.x -= w - 1; } } else { if(pos_list_t::get_next_pos(k)) { if(k.y == 0) { dy = h - 1; } if(k.x == 0) { dx = w - 1; } if(k.y <= 0) { k.y -= h - 1; } if(k.x <= 0) { k.x -= w - 1; } } else { return false; } } return true; } /* * Checks if all tiles are suitable for the building to be built * Includes boundary tiles around the building */ bool placefinder_t::is_area_ok(koord pos, sint16 w, sint16 h, climate_bits cl) const { if(!welt->is_within_limits(pos)) { return false; } koord k(w, h); while(k.y-- > 0) { k.x = w; while(k.x-- > 0) { if(!is_tile_ok(pos, k, cl)) { return false; } } } return true; } /* * Checks if the tile is part of the building or if it's a boundary tile * @return false, when tile is part of the building * @return true, when tile is the boundary tile of the building */ bool placefinder_t::is_boundary_tile(koord d) const { return d.x == 0 || d.x == w - 1 || d.y == 0 || d.y == h - 1; } /* * Checks if the tile is suitable for building, used by is_area_ok() * This function is replaced for building types that require rules */ bool placefinder_t::is_tile_ok(koord /*pos*/, koord /*d*/, climate_bits /*cl*/) const { return true; } koord placefinder_t::find_place(koord start, sint16 w, sint16 h, climate_bits cl, bool *r) { sint16 radius = max_radius > 0 ? max_radius : welt->get_size_max(); pos_list_wh_t results_straight(radius, w, h); this->w = w; this->h = h; koord rel1, rel2; if((r && *r) && w != h) { // // Here we also search for the rotated positions. // pos_list_wh_t results_rotated(radius, h, w); if(is_area_ok(start, w, h, cl)) { *r = false; return start; } if(is_area_ok(start, h, w, cl)) { *r = true; return start; } while(results_straight.get_next_pos(rel1) && results_rotated.get_next_pos(rel2)) { if(is_area_ok(start + rel1, w, h, cl)) { *r = false; return start + rel1; } if(is_area_ok(start + rel2, h, w, cl)) { *r = true; return start + rel2; } INT_CHECK("simcity 1313"); } } else { // // Search without rotated positions. // if(is_area_ok(start, w, h, cl)) { return start; } while(results_straight.get_next_pos(rel1)) { if(is_area_ok(start + rel1, w, h, cl)) { if(r) { *r = false; } return start + rel1; } INT_CHECK("simcity 1314"); } } return koord::invalid; } simutrans-124.5/src/simutrans/world/placefinder.h000066400000000000000000000033061520675460500221560ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef WORLD_PLACEFINDER_H #define WORLD_PLACEFINDER_H #include "../dataobj/koord.h" class karte_t; /** * pos_list_t: * * Provides a list of all coordinates from (0, 0) sorted by distance in increasing * order with x and y in range [-max_xy; max_xy] except for (0, 0) itself. * (0, 0) is used as the end marker. */ class pos_list_t { sint16 max_radius; sint16 *columns; sint16 radius; sint16 row; sint16 quadrant; sint16 find_best_row(); public: /** * @param max_xy (Maximum value for x and y position) */ pos_list_t(sint16 max_xy); virtual ~pos_list_t(); void restart(); bool get_pos(koord &k); virtual bool get_next_pos(koord &k); }; /** * pos_list_wh_t: * * Extended version of pos_list_t. Provides surrounding positions * for a range of size h by w. * (0, 0) is used as the end marker again. */ class pos_list_wh_t : public pos_list_t { sint16 w; sint16 h; sint16 dx; sint16 dy; public: pos_list_wh_t(sint16 max_radius, sint16 w, sint16 h); void restart(sint16 w, sint16 h); void restart() { pos_list_t::restart(); } bool get_next_pos(koord &k) OVERRIDE; }; class placefinder_t { protected: karte_t *welt; sint16 w; sint16 h; sint16 max_radius; virtual bool is_area_ok(koord pos, sint16 w, sint16 h, climate_bits cl) const; virtual bool is_tile_ok(koord pos, koord d, climate_bits cl) const; bool is_boundary_tile(koord d) const; placefinder_t(karte_t *welt, sint16 _max_radius = - 1) { this->welt = welt; max_radius = _max_radius; } virtual ~placefinder_t() {} public: koord find_place(koord start, sint16 w, sint16 h, climate_bits cl, bool *r = NULL); }; #endif simutrans-124.5/src/simutrans/world/simcity.cc000066400000000000000000004203251520675460500215250ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include #include "simcity.h" #include "simplan.h" #include "simworld.h" #include "../builder/brueckenbauer.h" #include "../builder/fabrikbauer.h" #include "../builder/hausbauer.h" #include "../builder/wegbauer.h" #include "../dataobj/environment.h" #include "../dataobj/loadsave.h" #include "../dataobj/settings.h" #include "../dataobj/tabfile.h" #include "../dataobj/translator.h" #include "../descriptor/building_desc.h" #include "../display/simimg.h" #include "../ground/boden.h" #include "../ground/fundament.h" #include "../ground/grund.h" #include "../gui/city_info.h" #include "../gui/minimap.h" #include "../gui/simwin.h" #include "../obj/depot.h" #include "../obj/gebaeude.h" #include "../obj/way/strasse.h" #include "../player/simplay.h" #include "../simcolor.h" #include "../simdebug.h" #include "../simfab.h" #include "../simhalt.h" #include "../simintr.h" #include "../simmesg.h" #include "../simware.h" #include "../utils/cbuffer.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "../vehicle/pedestrian.h" #include "../vehicle/simroadtraffic.h" #include "building_placefinder.h" #define PACKET_SIZE (7) /** * This variable is used to control the fractional precision of growth to prevent loss when quantities are small. * Growth calculations use 64 bit signed integers. * Although this is actually scale factor, a power of two is recommended for optimization purposes. */ static sint64 const CITYGROWTH_PER_CITIZEN = 1ll << 32; // Q31.32 fractional form. karte_ptr_t stadt_t::welt; // one is enough ... /********************************* From here on cityrules stuff *****************************************/ /** * in this fixed interval, construction will happen * 21s = 21000 per house */ const uint32 stadt_t::city_growth_step = 21000; /** * this is the default factor to prefer clustering */ uint32 stadt_t::cluster_factor = 10; /* * chance to do renovation instead new building (in percent) */ static uint32 renovation_percentage = 12; /* * minimum ratio of city area to building area to allow expansion * the higher this value, the slower the city expansion if there are still "holes" */ static uint32 min_building_density = 25; // the following are the scores for the different building types static sint16 ind_start_score = 0; static sint16 com_start_score = -10; static sint16 res_start_score = 0; // order: res, com, ind static sint16 ind_neighbour_score[] = { -8, 0, 8 }; static sint16 com_neighbour_score[] = { 1, 8, 1 }; static sint16 res_neighbour_score[] = { 8, 0, -8 }; /** * Rule data structure * maximum 7x7 rules */ class rule_entry_t { public: uint8 x,y; char flag; rule_entry_t(uint8 x_=0, uint8 y_=0, char f_='.') : x(x_), y(y_), flag(f_) {} void rdwr(loadsave_t* file) { file->rdwr_byte(x); file->rdwr_byte(y); uint8 c = flag; file->rdwr_byte(c); flag = c; } }; class rule_t { public: sint16 chance; vector_tpl rule; rule_t(uint32 count=0) : chance(0), rule(count) {} void rdwr(loadsave_t* file) { file->rdwr_short(chance); if (file->is_loading()) { rule.clear(); } uint32 count = rule.get_count(); file->rdwr_long(count); for(uint32 i=0; iis_loading()) { rule.append(rule_entry_t()); } rule[i].rdwr(file); } } }; // house rules static vector_tpl house_rules; // and road rules static vector_tpl road_rules; /** * Symbols in rules: * S = not a road * s = is a road * n = is nature/empty * H = not a house * h = is a house * T = not a stop // added in 88.03.3 * t = is a stop // added in 88.03.3 * u = good slope for way * U = not a slope for ways * . = beliebig */ // here '.' is ignored, since it will not be tested anyway static char const* const allowed_chars_in_rule = "SsnHhTtUu"; /** * @param pos position to check * @param regel the rule to evaluate * @return true on match, false otherwise */ bool stadt_t::bewerte_loc(const koord pos, const rule_t ®el, int rotation) { //printf("Test for (%s) in rotation %d\n", pos.get_str(), rotation); koord k; for(rule_entry_t const& r : regel.rule) { uint8 x,y; switch (rotation) { default: case 0: x=r.x; y=r.y; break; case 90: x=r.y; y=6-r.x; break; case 180: x=6-r.x; y=6-r.y; break; case 270: x=6-r.y; y=r.x; break; } const koord k(pos.x+x-3, pos.y+y-3); const grund_t* gr = welt->lookup_kartenboden(k); if (gr == NULL) { // outside of the map => cannot apply this rule return false; } switch (r.flag) { case 's': // road? if (!gr->hat_weg(road_wt)) return false; break; case 'S': // not road? if (gr->hat_weg(road_wt)) return false; break; case 'h': // is house if (gr->get_typ() != grund_t::fundament || gr->obj_bei(0)->get_typ()!=obj_t::gebaeude) return false; break; case 'H': // no house if (gr->get_typ() == grund_t::fundament) return false; break; case 'n': // nature/empty if (!gr->ist_natur()) return false; if (!gr->kann_alle_obj_entfernen(NULL)) return false; break; case 'U': // unbuildable for road if (!slope_t::is_way(gr->get_grund_hang())) return false; break; case 'u': // road may be buildable if (slope_t::is_way(gr->get_grund_hang())) return false; break; case 't': // here is a stop/extension building if (!gr->is_halt()) return false; break; case 'T': // no stop if (gr->is_halt()) return false; break; default: ; // ignore } } return true; } /** * Check rule in all transformations at given position * @note but the rules should explicitly forbid building then?!? */ sint32 stadt_t::bewerte_pos(const koord pos, const rule_t ®el) { const grund_t* gr = welt->lookup_kartenboden(pos); if (!gr || gr->kann_alle_obj_entfernen(NULL)) { // cannot built on empty tiles or tiles with an other owner's object return 0; } // will be called only a single time, so we can stop after a single match if(bewerte_loc(pos, regel, 0) || bewerte_loc(pos, regel, 90) || bewerte_loc(pos, regel, 180) || bewerte_loc(pos, regel, 270)) { return 1; } return 0; } void stadt_t::bewerte_strasse(koord k, sint32 rd, const rule_t ®el) { if (simrand(rd) == 0) { best_strasse.check(k, bewerte_pos(k, regel)); } } void stadt_t::bewerte_haus(koord k, sint32 rd, const rule_t ®el) { if (simrand(rd) == 0) { best_haus.check(k, bewerte_pos(k, regel)); } } /** * Reads city configuration data */ bool stadt_t::cityrules_init() { tabfile_t cityconf; // first take user data, then user global data const std::string user_dir=env_t::user_dir; if (!cityconf.open((user_dir+"cityrules.tab").c_str())) { if (!cityconf.open((user_dir+"addons/"+env_t::pak_name+"config/cityrules.tab").c_str())) { if (!cityconf.open((env_t::pak_dir+"config/cityrules.tab").c_str())) { dbg->fatal("stadt_t::init()", "Can't read cityrules.tab" ); } } } tabfileobj_t contents; cityconf.read(contents); char buf[128]; cluster_factor = (uint32)contents.get_int("cluster_factor", 10); renovation_percentage = (uint32)contents.get_int("renovation_percentage", 25); // to keep compatible with the typo, here both are ok min_building_density = (uint32)contents.get_int("minimum_building_desity", 25); min_building_density = (uint32)contents.get_int("minimum_building_density", min_building_density); // init the building value tables ind_start_score = contents.get_int("ind_start_score", 0); ind_neighbour_score[0] = contents.get_int("ind_near_res", -8); ind_neighbour_score[1] = contents.get_int("ind_near_com", 0); ind_neighbour_score[2] = contents.get_int("ind_near_ind", 8); com_start_score = contents.get_int("com_start_score", -10); com_neighbour_score[0] = contents.get_int("com_near_res", 1); com_neighbour_score[1] = contents.get_int("com_near_com", 8); com_neighbour_score[2] = contents.get_int("com_near_ind", 1); res_start_score = contents.get_int("res_start_score", 0); res_neighbour_score[0] = contents.get_int("res_near_res", 8); res_neighbour_score[1] = contents.get_int("res_near_com", 0); res_neighbour_score[2] = contents.get_int("res_near_ind", -8); uint32 num_house_rules = 0; for (;;) { sprintf(buf, "house_%d", num_house_rules + 1); if (contents.get_string(buf, 0)) { num_house_rules++; } else { break; } } DBG_MESSAGE("stadt_t::init()", "Read %d house building rules", num_house_rules); uint32 num_road_rules = 0; for (;;) { sprintf(buf, "road_%d", num_road_rules + 1); if (contents.get_string(buf, 0)) { num_road_rules++; } else { break; } } DBG_MESSAGE("stadt_t::init()", "Read %d road building rules", num_road_rules); clear_ptr_vector( house_rules ); for (uint32 i = 0; i < num_house_rules; i++) { house_rules.append(new rule_t()); sprintf(buf, "house_%d.chance", i + 1); house_rules[i]->chance = contents.get_int(buf, 0); sprintf(buf, "house_%d", i + 1); const char* rule = contents.get_string(buf, ""); // skip leading spaces (use . for padding) while (*rule == ' ') { rule++; } // find out rule size size_t size = 0; size_t maxlen = strlen(rule); while (size < maxlen && rule[size]!=' ') { size++; } if (size > 7 || maxlen < size * (size + 1) - 1 || (size & 1) == 0 || size <= 2 ) { dbg->fatal("stadt_t::cityrules_init()", "house rule %d has bad format!", i + 1); } // put rule into memory const uint8 offset = (7 - (uint8)size) / 2; for (uint y = 0; y < size; y++) { for (uint x = 0; x < size; x++) { const char flag = rule[x + y * (size + 1)]; // check for allowed characters; ignore '.'; // leave midpoint out, should be 'n', which is checked in build() anyway if ((x+offset!=3 || y+offset!=3) && (flag!=0 && strchr(allowed_chars_in_rule, flag))) { house_rules[i]->rule.append(rule_entry_t(x+offset,y+offset,flag)); } else { if ((x+offset!=3 || y+offset!=3) && flag!='.') { dbg->warning("stadt_t::cityrules_init()", "house rule %d entry (%d,%d) is '%c' and will be ignored", i + 1, x+offset, y+offset, flag); } } } } } clear_ptr_vector( road_rules ); for (uint32 i = 0; i < num_road_rules; i++) { road_rules.append(new rule_t()); sprintf(buf, "road_%d.chance", i + 1); road_rules[i]->chance = contents.get_int(buf, 0); sprintf(buf, "road_%d", i + 1); const char* rule = contents.get_string(buf, ""); // skip leading spaces (use . for padding) while (*rule == ' ') { rule++; } // find out rule size size_t size = 0; size_t maxlen = strlen(rule); while (size < maxlen && rule[size] != ' ') { size++; } if ( size > 7 || maxlen < size * (size + 1) - 1 || (size & 1) == 0 || size <= 2 ) { dbg->fatal("stadt_t::cityrules_init()", "road rule %d has bad format!", i + 1); } // put rule into memory const uint8 offset = (7 - (uint8)size) / 2; for (uint y = 0; y < size; y++) { for (uint x = 0; x < size; x++) { const char flag = rule[x + y * (size + 1)]; // check for allowed characters; ignore '.'; // leave midpoint out, should be 'n', which is checked in build() anyway if ((x+offset!=3 || y+offset!=3) && (flag!=0 && strchr(allowed_chars_in_rule, flag))) { road_rules[i]->rule.append(rule_entry_t(x+offset,y+offset,flag)); } else { if ((x+offset!=3 || y+offset!=3) && flag!='.') { dbg->warning("stadt_t::cityrules_init()", "road rule %d entry (%d,%d) is '%c' and will be ignored", i + 1, x+offset, y+offset, flag); } } } } } return true; } /** * Reads/writes city configuration data from/to a savegame * called from karte_t::speichern and karte_t::laden * only written for networkgames */ void stadt_t::cityrules_rdwr(loadsave_t *file) { if( file->is_version_atleast(112, 8) ) { file->rdwr_long( cluster_factor ); } file->rdwr_long(renovation_percentage); file->rdwr_long(min_building_density); file->rdwr_short(ind_start_score); file->rdwr_short(ind_neighbour_score[0]); file->rdwr_short(ind_neighbour_score[1]); file->rdwr_short(ind_neighbour_score[2]); file->rdwr_short(com_start_score); file->rdwr_short(com_neighbour_score[0]); file->rdwr_short(com_neighbour_score[1]); file->rdwr_short(com_neighbour_score[2]); file->rdwr_short(res_start_score); file->rdwr_short(res_neighbour_score[0]); file->rdwr_short(res_neighbour_score[1]); file->rdwr_short(res_neighbour_score[2]); // house rules if (file->is_loading()) { clear_ptr_vector( house_rules ); } uint32 count = house_rules.get_count(); file->rdwr_long(count); for(uint32 i=0; iis_loading()) { house_rules.append(new rule_t()); } house_rules[i]->rdwr(file); } // road rules if (file->is_loading()) { clear_ptr_vector( road_rules ); } count = road_rules.get_count(); file->rdwr_long(count); for(uint32 i=0; iis_loading()) { road_rules.append(new rule_t()); } road_rules[i]->rdwr(file); } } /** * Search a free place for a monument building * Im Gegensatz zum building_placefinder_t werden Strassen auf den Raendern * toleriert. */ class monument_placefinder_t : public placefinder_t { public: monument_placefinder_t(karte_t* welt, sint16 radius) : placefinder_t(welt, radius) {} bool is_tile_ok(koord pos, koord d, climate_bits cl) const OVERRIDE { const planquadrat_t* plan = welt->access(pos + d); // can't build here if (plan == NULL) { return false; } const grund_t* gr = plan->get_kartenboden(); if( ((1 << welt->get_climate( gr->get_pos().get_2d() )) & cl) == 0 ) { return false; } if (is_boundary_tile(d)) { return gr->get_grund_hang() == slope_t::flat && // Flat gr->get_typ() == grund_t::boden && // Boden -> no building (!gr->hat_wege() || gr->hat_weg(road_wt)) && // only roads gr->kann_alle_obj_entfernen(NULL) == NULL; // Irgendwas verbaut den Platz? } else { return gr->get_grund_hang() == slope_t::flat && gr->get_typ() == grund_t::boden && gr->ist_natur() && // No way here gr->kann_alle_obj_entfernen(NULL) == NULL; // Irgendwas verbaut den Platz? } } }; /** * townhall_placefinder_t: */ class townhall_placefinder_t : public placefinder_t { public: townhall_placefinder_t(karte_t* welt, uint8 dir_) : placefinder_t(welt), dir(dir_) {} bool is_tile_ok(koord pos, koord d, climate_bits cl) const OVERRIDE { const grund_t* gr = welt->lookup_kartenboden(pos + d); if (gr == NULL || gr->get_grund_hang() != slope_t::flat) { return false; } if( ((1 << welt->get_climate( gr->get_pos().get_2d() )) & cl) == 0 ) { return false; } if (d.x > 0 || d.y > 0) { if (welt->lookup_kartenboden(pos)->get_hoehe() != gr->get_hoehe()) { // height wrong! return false; } } if ( ((dir & ribi_t::south)!=0 && d.y == h - 1) || ((dir & ribi_t::west)!=0 && d.x == 0) || ((dir & ribi_t::north)!=0 && d.y == 0) || ((dir & ribi_t::east)!=0 && d.x == w - 1)) { // we want to build a road here: return gr->get_typ() == grund_t::boden && (!gr->hat_wege() || (gr->hat_weg(road_wt) && !gr->has_two_ways())) && // build only on roads, no other ways !gr->is_halt() && gr->kann_alle_obj_entfernen(NULL) == NULL; } else { // we want to build the townhall here: maybe replace existing buildings return ((gr->get_typ()==grund_t::boden && gr->ist_natur()) || gr->get_typ()==grund_t::fundament) && gr->kann_alle_obj_entfernen(NULL) == NULL; } } private: uint8 dir; }; static bool compare_gebaeude_pos(const gebaeude_t* a, const gebaeude_t* b) { const uint32 pos_a = (a->get_pos().y<<16)+a->get_pos().x; const uint32 pos_b = (b->get_pos().y<<16)+b->get_pos().x; return pos_a gb_tiles; if (gb != NULL) { uint16 level = gb->get_tile()->get_desc()->get_level()+1; gb->get_tile_list( gb_tiles ); for(grund_t* gr : gb_tiles ) { gebaeude_t* add_gb = gr->find(); assert(!buildings.is_contained(add_gb)); if( ordered ) { buildings.insert_ordered( add_gb, level, compare_gebaeude_pos ); } else { buildings.append( add_gb, level ); } add_gb->set_stadt( this ); if( add_gb->get_tile()->get_desc()->is_townhall() ) { has_townhall = true; } } // no update of city limits // as has_high_density may depend on the order the buildings list is filled if (!ordered) { // check borders recalc_city_size(); } } } // this function removes all house tiles from the city house list void stadt_t::remove_gebaeude_from_stadt(gebaeude_t* gb) { static vector_tpl gb_tiles; gb->get_tile_list(gb_tiles); for (grund_t* gr : gb_tiles) { gebaeude_t* remove_gb = gr->find(); remove_gb->set_stadt(NULL); bool ok = buildings.remove(remove_gb); assert(ok); } recalc_city_size(); } // just updates the weight count of this building (after a renovation) void stadt_t::update_gebaeude_from_stadt(gebaeude_t* gb) { bool ok = buildings.remove(gb); assert(ok); buildings.append(gb, gb->get_tile()->get_desc()->get_level() + 1); } // returns true, if there is a halt serving at least one building of the town. bool stadt_t::is_within_players_network(const player_t* player) const { vector_tpl halts; // Find all stations whose coverage affects this city for( weighted_vector_tpl::const_iterator i = buildings.begin(); i != buildings.end(); ++i ) { gebaeude_t* gb = *i; if( const planquadrat_t *plan = welt->access( gb->get_pos().get_2d() ) ) { if( plan->get_haltlist_count() > 0 ) { const halthandle_t *const halt_list = plan->get_haltlist(); for( int h = 0; h < plan->get_haltlist_count(); h++ ) { if( halt_list[h].is_bound() && (halt_list[h]->get_pax_enabled() || halt_list[h]->get_mail_enabled() ) && halt_list[h]->has_available_network(player) ) { return true; } } } } } return false; } // recalculate the spreading of a city // will be updated also after house deletion void stadt_t::recalc_city_size() { // WARNING: do not call this during multithreaded loading, // as has_high_density may depend on the order the buildings list is filled lo = pos; ur = pos; for(gebaeude_t* const i : buildings) { if (i->get_tile()->get_desc()->get_type() != building_desc_t::headquarters) { koord const& gb_pos = i->get_pos().get_2d(); lo.clip_max(gb_pos); ur.clip_min(gb_pos+i->get_tile()->get_desc()->get_size(i->get_tile()->get_layout()) ); } } has_high_density = (buildings.get_count()<10 || get_homeless() > 2500 || get_unemployed() > 2500 || (buildings.get_count()*100l)/((ur.x-lo.x)*(ur.y-lo.y)+1) > min_building_density); if( has_high_density ) { // wider borders for faster growth of sparse small towns lo -= koord(2,2); ur += koord(2,2); } else { lo -= koord(1, 1); ur += koord(1, 1); } lo.clip_min(koord(0,0)); ur.clip_max(koord(welt->get_size().x-1,welt->get_size().y-1)); } void stadt_t::init_pax_destinations() { pax_destinations_old.clear(); pax_destinations_new.clear(); pax_destinations_new_change = 0; } void stadt_t::factory_entry_t::rdwr(loadsave_t *file) { if( file->is_version_atleast(110, 5) ) { koord factory_pos; if( file->is_saving() ) { factory_pos = factory->get_pos().get_2d(); } factory_pos.rdwr( file ); if( file->is_loading() ) { // position will be resolved back into fabrik_t* later factory_pos_x = factory_pos.x; factory_pos_y = factory_pos.y; } file->rdwr_long( demand ); file->rdwr_long( supply ); file->rdwr_long( remaining ); } } void stadt_t::factory_entry_t::resolve_factory() { factory = fabrik_t::get_fab( koord(factory_pos_x, factory_pos_y) ); } const stadt_t::factory_entry_t* stadt_t::factory_set_t::get_entry(const fabrik_t *const factory) const { for(factory_entry_t const& e : entries) { if (e.factory == factory) { return &e; } } return NULL; // not found } stadt_t::factory_entry_t* stadt_t::factory_set_t::get_random_entry() { if( total_remaining>0 ) { sint32 weight = simrand(total_remaining); for(factory_entry_t & entry : entries) { if( entry.remaining>0 ) { if( weight0 ) { average_generated += month_generated; ++months; } if( months>1 ) { average_generated /= months; } /* ratio formula -> ((supply * 100) / total) shifted by RATIO_BITS */ // we supply 1/8 more than demand const sint32 target_supply = (total_demand * SUPPLY_FACTOR + ((1<<(DEMAND_BITS+SUPPLY_BITS))-1)) >> (DEMAND_BITS+SUPPLY_BITS); if( total_demand==0 ) { // no demand -> zero ratio generation_ratio = 0; } else if( !welt->get_settings().get_factory_enforce_demand() || average_generated == 0 ) { // demand not enforced or no pax generation data from previous month(s) -> simply use default ratio generation_ratio = (uint32)default_percent << RATIO_BITS; } else { // ratio of target supply (plus allowances for rounding up) to previous months' average generated pax (less 6.25% or 1/16 allowance for fluctuation), capped by default ratio const sint64 default_ratio = (sint64)default_percent << RATIO_BITS; const sint64 supply_ratio = ((sint64)((target_supply+(sint32)entries.get_count())*100)<>4)+1); generation_ratio = (uint32)( default_ratioget_settings().get_factory_enforce_demand() && (generation_ratio >> RATIO_BITS) == (uint32)default_percent && average_generated > 0 && total_demand > 0 ) { const sint64 supply_promille = ( ( (average_generated << 10) * (sint64)default_percent ) / 100 ) / (sint64)target_supply; if( supply_promille < 1024 ) { // expected supply is really smaller than target supply for(factory_entry_t & entry : entries) { const sint32 new_supply = (sint32)( ( (sint64)entry.demand * SUPPLY_FACTOR * supply_promille + ((1<<(DEMAND_BITS+SUPPLY_BITS+10))-1) ) >> (DEMAND_BITS+SUPPLY_BITS+10) ); const sint32 delta_supply = new_supply - entry.supply; if( delta_supply==0 ) { continue; } else if( delta_supply>0 || (entry.remaining+delta_supply)>=0 ) { // adjust remaining figures by the change in supply total_remaining += delta_supply; entry.remaining += delta_supply; } else { // avoid deducting more than allowed total_remaining -= entry.remaining; entry.remaining = 0; } entry.supply = new_supply; } return; } } // expected supply is unknown or sufficient to meet target supply for(factory_entry_t & entry : entries) { const sint32 new_supply = ( entry.demand * SUPPLY_FACTOR + ((1<<(DEMAND_BITS+SUPPLY_BITS))-1) ) >> (DEMAND_BITS+SUPPLY_BITS); const sint32 delta_supply = new_supply - entry.supply; if( delta_supply==0 ) { continue; } else if( delta_supply>0 || (entry.remaining+delta_supply)>=0 ) { // adjust remaining figures by the change in supply total_remaining += delta_supply; entry.remaining += delta_supply; } else { // avoid deducting more than allowed total_remaining -= entry.remaining; entry.remaining = 0; } entry.supply = new_supply; } } void stadt_t::factory_set_t::new_month() { for(factory_entry_t & e : entries) { e.new_month(); } total_remaining = 0; total_generated = 0; ratio_stale = true; } void stadt_t::factory_set_t::rdwr(loadsave_t *file) { if( file->is_version_atleast(110, 5) ) { uint32 entry_count = entries.get_count(); file->rdwr_long(entry_count); if( file->is_loading() ) { entries.reserve( entry_count ); factory_entry_t entry; for( uint32 e=0; erdwr_long( total_generated ); } } void stadt_t::factory_set_t::resolve_factories() { uint32 remove_count = 0; for(factory_entry_t & e : entries) { e.resolve_factory(); if (!e.factory) { remove_count ++; } } for( uint32 e=0; eremove_factory( NULL ); } } stadt_t::~stadt_t() { // close info win destroy_win((ptrdiff_t)this); if( minimap_t::get_instance()->is_city_selected(this) ) { minimap_t::get_instance()->set_selected_city(NULL); } // only if there is still a world left to delete from if( welt->get_size().x > 1 ) { welt->lookup_kartenboden(pos)->set_text(NULL); // avoid the bookkeeping if world gets destroyed if (!welt->is_destroying()) { stadt_t* city = NULL; // headquarters will be belong to the same town with all tiles ... // remove city info and houses while (!buildings.empty()) { gebaeude_t* const gb = buildings[0]; if(gb->get_tile()->get_desc()->get_type()==building_desc_t::headquarters) { if (!city) { city = welt->find_nearest_city(gb->get_pos().get_2d()); } gb->set_stadt( city ); if(city) { city->buildings.append(gb, gb->get_passagier_level()); } buildings.remove_at(0); } else { hausbauer_t::remove(welt->get_public_player(),gb); } } } } } static bool name_used(weighted_vector_tpl const& cities, char const* const name) { for(stadt_t* const i : cities) { if (strcmp(i->get_name(), name) == 0) { return true; } } return false; } stadt_t::stadt_t(player_t* player, koord pos, sint32 citizens, const building_desc_t* th, sint16 rotation) : buildings(16), pax_destinations_old(koord(PAX_DESTINATIONS_SIZE, PAX_DESTINATIONS_SIZE)), pax_destinations_new(koord(PAX_DESTINATIONS_SIZE, PAX_DESTINATIONS_SIZE)) { assert(welt->is_within_limits(pos)); step_count = 0; pax_destinations_new_change = 0; next_step = 0; step_interval = 1; next_growth_step = 0; has_high_density = false; has_townhall = false; stadtinfo_options = 3; // citizen and growth owner = player; this->pos = pos; last_center = koord::invalid; bev = 0; arb = 0; won = 0; lo = ur = pos; /* get a unique cityname */ char const* n = "simcity"; weighted_vector_tpl const& staedte = welt->get_cities(); const vector_tpl& city_names = translator::get_city_name_list(); // make sure we do only ONE random call regardless of how many names are available (to avoid desyncs in network games) if( const uint32 count = city_names.get_count() ) { uint32 idx = simrand( count ); static const uint32 some_primes[] = { 19, 31, 109, 199, 409, 571, 631, 829, 1489, 1999, 2341, 2971, 3529, 4621, 4789, 7039, 7669, 8779, 9721 }; // find prime that does not divide count uint32 offset = 1; for( uint8 i=0; i < lengthof(some_primes); i++ ) { if( count % some_primes[i] != 0 ) { offset = some_primes[i]; break; } } // as count % offset != 0 we are guaranteed to test all city names for(uint32 i=0; i0) { change_size( citizens, true ); } // fill with start citizen ... sint64 bew = get_einwohner(); for (uint year = 0; year < MAX_CITY_HISTORY_YEARS; year++) { city_history_year[year][HIST_CITIZENS] = bew; } for (uint month = 0; month < MAX_CITY_HISTORY_MONTHS; month++) { city_history_month[month][HIST_CITIZENS] = bew; } // initialize history array for (uint year = 0; year < MAX_CITY_HISTORY_YEARS; year++) { for (uint hist_type = 1; hist_type < MAX_CITY_HISTORY; hist_type++) { city_history_year[year][hist_type] = 0; } } for (uint month = 0; month < MAX_CITY_HISTORY_YEARS; month++) { for (uint hist_type = 1; hist_type < MAX_CITY_HISTORY; hist_type++) { city_history_month[month][hist_type] = 0; } } city_history_year[0][HIST_CITIZENS] = get_einwohner(); city_history_month[0][HIST_CITIZENS] = get_einwohner(); #ifdef DESTINATION_CITYCARS number_of_cars = 0; #endif } stadt_t::stadt_t(loadsave_t* file) : buildings(16), pax_destinations_old(koord(PAX_DESTINATIONS_SIZE, PAX_DESTINATIONS_SIZE)), pax_destinations_new(koord(PAX_DESTINATIONS_SIZE, PAX_DESTINATIONS_SIZE)) { step_count = 0; next_step = 0; step_interval = 1; next_growth_step = 0; has_high_density = false; has_townhall = false; unsupplied_city_growth = 0; stadtinfo_options = 3; rdwr(file); } void stadt_t::rdwr(loadsave_t* file) { sint32 owner_n; if (file->is_saving()) { owner_n = welt->sp2num(owner); } file->rdwr_str(name); pos.rdwr(file); uint32 lli = lo.x; uint32 lob = lo.y; uint32 lre = ur.x; uint32 lun = ur.y; file->rdwr_long(lli); file->rdwr_long(lob); file->rdwr_long(lre); file->rdwr_long(lun); lo.x = lli; lo.y = lob; ur.x = lre; ur.y = lun; file->rdwr_long(owner_n); file->rdwr_long(bev); file->rdwr_long(arb); file->rdwr_long(won); if( file->is_version_atleast(112, 9) ) { // Must record the partial (less than 1 citizen) growth factor // Otherwise we will get network desyncs // Also allows accumulation of small growth factors file->rdwr_longlong(unsupplied_city_growth); } else if( file->is_loading() ) { unsupplied_city_growth = 0; } // old values zentrum_namen_cnt : aussen_namen_cnt if(file->is_version_less(99, 18)) { sint32 dummy=0; file->rdwr_long(dummy); file->rdwr_long(dummy); } if (file->is_loading()) { owner = welt->get_player(owner_n); } if(file->is_loading()) { // initialize history array for (uint year = 0; year < MAX_CITY_HISTORY_YEARS; year++) { for (uint hist_type = 0; hist_type < MAX_CITY_HISTORY; hist_type++) { city_history_year[year][hist_type] = 0; } } for (uint month = 0; month < MAX_CITY_HISTORY_MONTHS; month++) { for (uint hist_type = 0; hist_type < MAX_CITY_HISTORY; hist_type++) { city_history_month[month][hist_type] = 0; } } city_history_year[0][HIST_CITIZENS] = get_einwohner(); city_history_year[0][HIST_CITIZENS] = get_einwohner(); } // we probably need to load/save the city history if (file->is_version_less(86, 0)) { DBG_DEBUG("stadt_t::rdwr()", "is old version: No history!"); } else if(file->is_version_less(99, 16)) { // 86.00.0 introduced city history for (uint year = 0; year < MAX_CITY_HISTORY_YEARS; year++) { for (uint hist_type = 0; hist_type < 2; hist_type++) { file->rdwr_longlong(city_history_year[year][hist_type]); } for (uint hist_type = 4; hist_type < 6; hist_type++) { file->rdwr_longlong(city_history_year[year][hist_type]); } } for (uint month = 0; month < MAX_CITY_HISTORY_MONTHS; month++) { for (uint hist_type = 0; hist_type < 2; hist_type++) { file->rdwr_longlong(city_history_month[month][hist_type]); } for (uint hist_type = 4; hist_type < 6; hist_type++) { file->rdwr_longlong(city_history_month[month][hist_type]); } } // not needed any more sint32 dummy = 0; file->rdwr_long(dummy); file->rdwr_long(dummy); file->rdwr_long(dummy); file->rdwr_long(dummy); } else if( file->is_version_less(120, 1) ) { // 99.17.0 extended city history for (uint year = 0; year < MAX_CITY_HISTORY_YEARS; year++) { for (uint hist_type = 0; hist_type < MAX_CITY_HISTORY; hist_type++) { if( hist_type==HIST_PAS_WALKED || hist_type==HIST_MAIL_WALKED ) { continue; } file->rdwr_longlong(city_history_year[year][hist_type]); } } for (uint month = 0; month < MAX_CITY_HISTORY_MONTHS; month++) { for (uint hist_type = 0; hist_type < MAX_CITY_HISTORY; hist_type++) { if( hist_type==HIST_PAS_WALKED || hist_type==HIST_MAIL_WALKED ) { continue; } file->rdwr_longlong(city_history_month[month][hist_type]); } } // save button settings for this town file->rdwr_long( stadtinfo_options); } else { // 120,001 with walking (direct connections) recored seperately for (uint year = 0; year < MAX_CITY_HISTORY_YEARS; year++) { for (uint hist_type = 0; hist_type < MAX_CITY_HISTORY; hist_type++) { file->rdwr_longlong(city_history_year[year][hist_type]); } } for (uint month = 0; month < MAX_CITY_HISTORY_MONTHS; month++) { for (uint hist_type = 0; hist_type < MAX_CITY_HISTORY; hist_type++) { file->rdwr_longlong(city_history_month[month][hist_type]); } } // save button settings for this town file->rdwr_long( stadtinfo_options); } // differential history if ( file->is_version_less(120, 1) ) { if ( file->is_loading() ) { // Initalize differential statistics assuming a differential of 0. city_growth_get_factors(city_growth_factor_previous, 0); } } else { // load/save differential statistics. for (uint32 i = 0; i < GROWTH_FACTOR_NUMBER; i++) { file->rdwr_longlong(city_growth_factor_previous[i].demand); file->rdwr_longlong(city_growth_factor_previous[i].supplied); } } if(file->is_version_atleast(99, 15) && file->is_version_less(99, 16)) { sint32 dummy = 0; file->rdwr_long(dummy); file->rdwr_long(dummy); } // since 102.2 there are static cities if(file->is_version_atleast(102, 2)) { file->rdwr_bool(allow_citygrowth); } else if( file->is_loading() ) { allow_citygrowth = true; } // save townhall road position if(file->is_version_atleast(102, 3)) { townhall_road.rdwr(file); } else if( file->is_loading() ) { townhall_road = koord::invalid; } // data related to target factories target_factories_pax.rdwr( file ); target_factories_mail.rdwr( file ); if (file->is_version_atleast(122, 1)) { file->rdwr_long(step_count); file->rdwr_long(next_step); file->rdwr_long(next_growth_step); } else if (file->is_loading()) { step_count = 0; next_step = 0; next_growth_step = 0; } if(file->is_loading()) { // Due to some bugs in the special buildings/town hall // placement code, li,re,ob,un could've gotten irregular values // If a game is loaded, the game might suffer from such an mistake // and we need to correct it here. DBG_MESSAGE("stadt_t::rdwr()", "borders (%i,%i) -> (%i,%i)", lo.x, lo.y, ur.x, ur.y); // recalculate borders recalc_city_size(); } } void stadt_t::finish_rd() { // there might be broken savegames if (!name) { set_name( "simcity" ); } if (!has_townhall) { dbg->warning("stadt_t::finish_rd()", "City %s has no valid townhall after loading the savegame, try to build a new one.", get_name()); check_bau_townhall(true,NULL,-1); } // new city => need to grow if (buildings.empty()) { step_grow_city(true); } // clear the minimaps init_pax_destinations(); // init step counter with meaningful value step_interval = (2 << 18u) / (buildings.get_count() * 4 + 1); if (step_interval < 1) { step_interval = 1; } if(townhall_road==koord::invalid) { // guess road tile based on current orientation gebaeude_t const* const gb = obj_cast(welt->lookup_kartenboden(pos)->first_no_way_obj()); if( gb && gb->is_townhall() ) { koord k(gb->get_tile()->get_desc()->get_size(gb->get_tile()->get_layout())); switch (gb->get_tile()->get_layout()) { default: case 0: townhall_road = pos + koord(0, k.y); break; case 1: townhall_road = pos + koord(k.x, 0); break; case 2: townhall_road = pos + koord(0, -1); break; case 3: townhall_road = pos + koord(-1, 0); break; } } } recalc_city_size(); // resolve target factories target_factories_pax.resolve_factories(); target_factories_mail.resolve_factories(); } void stadt_t::rotate90( const sint16 y_size ) { // rotate town origin pos.rotate90( y_size ); townhall_road.rotate90( y_size ); // rotate an rectangle lo.rotate90( y_size ); ur.rotate90( y_size ); sint16 lox = lo.x; lo.x = ur.x; ur.x = lox; // reset building search best_strasse.reset(pos); best_haus.reset(pos); // townhall position may be changed a little! sparse_tpl pax_destinations_temp(koord( PAX_DESTINATIONS_SIZE, PAX_DESTINATIONS_SIZE )); koord pos; for( uint16 i = 0; i < pax_destinations_new.get_data_count(); i++ ) { pax_dest_status_t dst_status; pax_destinations_new.get_nonzero(i, pos, dst_status); assert( dst_status != 0 ); pax_destinations_temp.set( PAX_DESTINATIONS_SIZE-1-pos.y, pos.x, dst_status ); } swap( pax_destinations_temp, pax_destinations_new ); pax_destinations_temp.clear(); for( uint16 i = 0; i < pax_destinations_old.get_data_count(); i++ ) { pax_dest_status_t dst_status; pax_destinations_old.get_nonzero(i, pos, dst_status); assert( dst_status != 0 ); pax_destinations_temp.set( PAX_DESTINATIONS_SIZE-1-pos.y, pos.x, dst_status ); } swap( pax_destinations_temp, pax_destinations_old ); pax_destinations_new_change ++; } void stadt_t::set_name(const char *new_name) { if (new_name == NULL) { return; } name = new_name; grund_t *gr = welt->lookup_kartenboden(pos); if(gr) { gr->set_text( new_name ); } city_info_t *win = dynamic_cast(win_get_magic((ptrdiff_t)this)); if (win) { win->update_data(); } } /* show city info dialogue */ void stadt_t::open_info_window() { create_win( new city_info_t(this), w_info, (ptrdiff_t)this ); } /* calculates the factories which belongs to certain cities */ void stadt_t::verbinde_fabriken() { DBG_MESSAGE("stadt_t::verbinde_fabriken()", "search factories near %s (center at %i,%i)", get_name(), pos.x, pos.y); assert( target_factories_pax.get_entries().empty() ); assert( target_factories_mail.get_entries().empty() ); for(fabrik_t* const fab : welt->get_fab_list()) { const uint32 count = fab->get_target_cities().get_count(); if( count < welt->get_settings().get_factory_worker_maximum_towns() && koord_distance(fab->get_pos(), pos) < welt->get_settings().get_factory_worker_radius() ) { fab->add_target_city(this); } } DBG_MESSAGE("stadt_t::verbinde_fabriken()", "is connected with %i/%i factories (total demand=%i/%i) for pax/mail.", target_factories_pax.get_entries().get_count(), target_factories_mail.get_entries().get_count(), target_factories_pax.total_demand, target_factories_mail.total_demand); } /* change size of city */ void stadt_t::change_size( sint64 delta_citizen, bool new_town) { DBG_MESSAGE("stadt_t::change_size()", "%i + %i", bev, delta_citizen); if( delta_citizen > 0 ) { unsupplied_city_growth += delta_citizen * CITYGROWTH_PER_CITIZEN; step_grow_city(new_town); } if( delta_citizen < 0 ) { if( bev > -delta_citizen ) { bev += (sint32)delta_citizen; } else { // remove_city(); bev = 1; } step_grow_city(new_town); } DBG_MESSAGE("stadt_t::change_size()", "%i+%i", bev, delta_citizen); } void stadt_t::step(uint32 delta_t) { // recalculate factory going ratios where necessary const sint16 factory_worker_percentage = welt->get_settings().get_factory_worker_percentage(); if( target_factories_pax.ratio_stale ) { target_factories_pax.recalc_generation_ratio( factory_worker_percentage, *city_history_month, MAX_CITY_HISTORY, HIST_PAS_GENERATED); } if( target_factories_mail.ratio_stale ) { target_factories_mail.recalc_generation_ratio( factory_worker_percentage, *city_history_month, MAX_CITY_HISTORY, HIST_MAIL_GENERATED); } // is it time for the next step? next_step += delta_t; next_growth_step += delta_t; step_interval = (1 << 21U) / (buildings.get_count() * welt->get_settings().get_passenger_factor() + 1); if (step_interval < 1) { step_interval = 1; } while(next_growth_step > stadt_t::city_growth_step) { calc_growth(); step_grow_city(); next_growth_step -= stadt_t::city_growth_step; } // create passenger rate proportional to town size while(next_step > step_interval) { step_passagiere(); step_count++; next_step -= step_interval; } // update history (might be changed do to construction/destroying of houses) city_history_month[0][HIST_CITIZENS] = get_einwohner(); // total number city_history_year[0][HIST_CITIZENS] = get_einwohner(); city_history_month[0][HIST_GROWTH] = city_history_month[0][HIST_CITIZENS]-city_history_month[1][HIST_CITIZENS]; // growth city_history_year[0][HIST_GROWTH] = city_history_year[0][HIST_CITIZENS]-city_history_year[1][HIST_CITIZENS]; city_history_month[0][HIST_BUILDING] = buildings.get_count(); city_history_year[0][HIST_BUILDING] = buildings.get_count(); } /* updates the city history */ void stadt_t::roll_history() { // roll months for (int i = MAX_CITY_HISTORY_MONTHS - 1; i > 0; i--) { for (int hist_type = 0; hist_type < MAX_CITY_HISTORY; hist_type++) { city_history_month[i][hist_type] = city_history_month[i - 1][hist_type]; } } // init this month for (int hist_type = 1; hist_type < MAX_CITY_HISTORY; hist_type++) { city_history_month[0][hist_type] = 0; } city_history_month[0][HIST_CITIZENS] = get_einwohner(); city_history_month[0][HIST_BUILDING] = buildings.get_count(); city_history_month[0][HIST_GOODS_NEEDED] = 0; //need to roll year too? if (welt->get_last_month() == 0) { for (int i = MAX_CITY_HISTORY_YEARS - 1; i > 0; i--) { for (int hist_type = 0; hist_type < MAX_CITY_HISTORY; hist_type++) { city_history_year[i][hist_type] = city_history_year[i - 1][hist_type]; } } // init this year for (int hist_type = 1; hist_type < MAX_CITY_HISTORY; hist_type++) { city_history_year[0][hist_type] = 0; } city_history_year[0][HIST_CITIZENS] = get_einwohner(); city_history_year[0][HIST_BUILDING] = buildings.get_count(); city_history_year[0][HIST_GOODS_NEEDED] = 0; } } void stadt_t::city_growth_get_factors(city_growth_factor_t(&factors)[GROWTH_FACTOR_NUMBER], uint32 const month) const { // optimize view of history for convenience sint64 const (&h)[MAX_CITY_HISTORY] = city_history_month[month]; // go through each index one at a time uint32 index = 0; // passenger growth factors factors[index ].demand = h[HIST_PAS_GENERATED]; factors[index++].supplied = h[HIST_PAS_TRANSPORTED] + h[HIST_PAS_WALKED]; // mail growth factors factors[index ].demand = h[HIST_MAIL_GENERATED]; factors[index++].supplied = h[HIST_MAIL_TRANSPORTED] + h[HIST_MAIL_WALKED]; // goods growth factors factors[index ].demand = h[HIST_GOODS_NEEDED]; factors[index++].supplied = h[HIST_GOODS_RECEIVED]; assert(index == GROWTH_FACTOR_NUMBER); } sint32 stadt_t::city_growth_base(uint32 const rprec, uint32 const cprec) { // Resolve constant references. settings_t const & s = welt->get_settings(); sint32 const weight[GROWTH_FACTOR_NUMBER] = { s.get_passenger_multiplier(), s.get_mail_multiplier(), s.get_goods_multiplier() }; sint32 acc = 0; // The weighted satisfaction accululator. sint32 div = 0; // The weight dividend. sint32 total = 0; // The total weight. // initalize const growth array city_growth_factor_t growthfactors[GROWTH_FACTOR_NUMBER]; city_growth_get_factors(growthfactors, 0); // Loop through each growth factor and compute it. for( uint32 i = 0; i < GROWTH_FACTOR_NUMBER; i += 1 ) { // Resolve the weight. total += weight[i]; // Compute the differentials. sint64 const had = growthfactors[i].demand - city_growth_factor_previous[i].demand; sint64 const got = growthfactors[i].supplied - city_growth_factor_previous[i].supplied; city_growth_factor_previous[i].demand = growthfactors[i].demand; city_growth_factor_previous[i].supplied = growthfactors[i].supplied; // If we had anything to satisfy add it to weighting otherwise skip. if( had == 0 ) { continue; } // Compute fractional satisfaction. sint32 const frac = (sint32)((got << cprec) / had); // Add to weight and div. acc += frac * weight[i]; div += weight[i]; } // If there was not anything to satisfy then use last month averages. if ( div == 0 ) { // initalize growth factor array city_growth_factor_t prev_growthfactors[GROWTH_FACTOR_NUMBER]; city_growth_get_factors(prev_growthfactors, 1); for( uint32 i = 0; i < GROWTH_FACTOR_NUMBER; i += 1 ){ // Extract the values of growth. sint64 const had = prev_growthfactors[i].demand; sint64 const got = prev_growthfactors[i].supplied; // If we had anything to satisfy add it to weighting otherwise skip. if( had == 0 ) { continue; } // Compute fractional satisfaction. sint32 const frac = (sint32)((got << cprec) / had); // Add to weight and div. acc += frac * weight[i]; div += weight[i]; } } // Return computed result. If still no demand then assume no growth to prevent self-growing new hamlets. return div != 0 ? (total * (acc / div)) >> (cprec - rprec) : 0; } void stadt_t::city_growth_monthly(uint32 const month) { // initalize growth factor array city_growth_factor_t growthfactors[GROWTH_FACTOR_NUMBER]; city_growth_get_factors(growthfactors, month); // Perform roll over. for( uint32 i = 0; i < GROWTH_FACTOR_NUMBER; i += 1 ){ // Compute the differentials. sint64 const had = growthfactors[i].demand - city_growth_factor_previous[i].demand; sint64 const got = growthfactors[i].supplied - city_growth_factor_previous[i].supplied; city_growth_factor_previous[i].demand = -had; city_growth_factor_previous[i].supplied = -got; } } void stadt_t::new_month( bool recalc_destinations ) { swap( pax_destinations_old, pax_destinations_new ); pax_destinations_new.clear(); pax_destinations_new_change = 0; city_growth_monthly(0); roll_history(); target_factories_pax.new_month(); target_factories_mail.new_month(); const sint16 factory_worker_percentage = welt->get_settings().get_factory_worker_percentage(); // settings_t const& s = welt->get_settings(); target_factories_pax.recalc_generation_ratio( factory_worker_percentage, *city_history_month, MAX_CITY_HISTORY, HIST_PAS_GENERATED); target_factories_mail.recalc_generation_ratio( factory_worker_percentage, *city_history_month, MAX_CITY_HISTORY, HIST_MAIL_GENERATED); recalc_target_cities(); // center has moved => change attraction weights if( recalc_destinations || last_center != get_center() ) { last_center = get_center(); recalc_target_attractions(); } if( !private_car_t::list_empty() && welt->get_settings().get_traffic_level() > 0 ) { // spawn eventual citycars // the more transported, the less are spawned // the larger the city, the more spawned ... /* original implementation that is replaced by integer-only version below double pfactor = (double)(city_history_month[1][HIST_PAS_TRANSPORTED]) / (double)(city_history_month[1][HIST_PAS_GENERATED]+1); double mfactor = (double)(city_history_month[1][HIST_MAIL_TRANSPORTED]) / (double)(city_history_month[1][HIST_MAIL_GENERATED]+1); double gfactor = (double)(city_history_month[1][HIST_GOODS_RECEIVED]) / (double)(city_history_month[1][HIST_GOODS_NEEDED]+1); double factor = pfactor > mfactor ? (gfactor > pfactor ? gfactor : pfactor ) : mfactor; factor = (1.0-factor)*city_history_month[1][HIST_CITIZENS]; factor = log10( factor ); */ // placeholder for fractions # define decl_stat(name, i0, i1) sint64 name##_stat[2]; name##_stat[0] = city_history_month[1][i0]; name##_stat[1] = city_history_month[1][i1]+1; // defines and initializes local sint64[2] arrays decl_stat(pax, HIST_PAS_TRANSPORTED, HIST_PAS_GENERATED); decl_stat(mail, HIST_MAIL_TRANSPORTED, HIST_MAIL_GENERATED); decl_stat(good, HIST_GOODS_RECEIVED, HIST_GOODS_NEEDED); // true if s1[0] / s1[1] > s2[0] / s2[1] # define comp_stats(s1,s2) ( s1[0]*s2[1] > s2[0]*s1[1] ) // computes (1.0 - s[0]/s[1]) * city_history_month[1][HIST_CITIZENS] # define comp_factor(s) (city_history_month[1][HIST_CITIZENS] *( s[1]-s[0] )) / s[1] uint32 factor = (uint32)( comp_stats(pax_stat, mail_stat) ? (comp_stats(good_stat, pax_stat) ? comp_factor(good_stat) : comp_factor(pax_stat)) : comp_factor(mail_stat) ); factor = log10(factor); #ifndef DESTINATION_CITYCARS uint16 number_of_cars = simrand( factor * welt->get_settings().get_traffic_level() ) / 16; city_history_month[0][HIST_CITYCARS] = number_of_cars; city_history_year[0][HIST_CITYCARS] += number_of_cars; koord k; koord pos = get_zufallspunkt(); for (k.y = pos.y - 3; k.y < pos.y + 3; k.y++) { for (k.x = pos.x - 3; k.x < pos.x + 3; k.x++) { if(number_of_cars==0) { return; } if( grund_t* gr = welt->lookup_kartenboden(k) ) { if( weg_t* w = gr->get_weg(road_wt) ) { // do not spawn on privte roads or if there is already a car if( ribi_t::is_twoway(w->get_ribi_unmasked()) && player_t::check_owner(NULL,w->get_owner()) && gr->find() == NULL ) { private_car_t* vt = new private_car_t(gr, koord::invalid); gr->obj_add(vt); number_of_cars--; } } } } } // correct statistics for ungenerated cars city_history_month[0][HIST_CITYCARS] -= number_of_cars; city_history_year[0][HIST_CITYCARS] -= number_of_cars; #else city_history_month[0][HIST_CITYCARS] = 0; number_of_cars = simrand( factor * welt->get_settings().get_traffic_level() ) / 16; #endif } } void stadt_t::calc_growth() { // now iterate over all factories to get the ratio of producing version non-producing factories // we use the incoming storage as a measure and we will only look for end consumers (power stations, markets) for(factory_entry_t const& i : target_factories_pax.get_entries()) { fabrik_t *const fab = i.factory; if (fab->get_consumer().empty() && !fab->get_suppliers().empty()) { // consumer => check for it storage const factory_desc_t *const desc = fab->get_desc(); for( int i=0; iget_supplier_count(); i++ ) { city_history_month[0][HIST_GOODS_NEEDED] ++; city_history_year[0][HIST_GOODS_NEEDED] ++; if( fab->get_input_stock( desc->get_supplier(i)->get_input_type() )>0 ) { city_history_month[0][HIST_GOODS_RECEIVED] ++; city_history_year[0][HIST_GOODS_RECEIVED] ++; } } } } // maybe this town should stay static if( !allow_citygrowth ) { unsupplied_city_growth = 0; return; } // Compute base growth. sint32 const total_supply_percentage = city_growth_base(); // By construction, this is a percentage times 2^6 (Q25.6). // Although intended to be out of 100, it can be more or less. // We will divide it by 2^4=16 for traditional reasons, which means // it generates 2^2 (=4) or fewer people at 100%. // smaller towns should grow slower to have villages for a longer time sint32 const weight_factor = bev < 1000 ? welt->get_settings().get_growthfactor_small() : bev < 10000 ? welt->get_settings().get_growthfactor_medium() : welt->get_settings().get_growthfactor_large(); // now compute the growth for this step sint32 growth_factor = weight_factor > 0 ? total_supply_percentage / weight_factor : 0; // Scale up growth to have a larger fractional component. This allows small growth units to accumulate in the case of long months. sint64 new_unsupplied_city_growth = growth_factor * (CITYGROWTH_PER_CITIZEN / 16); // Growth is scaled down by month length. // The result is that ~ the same monthly growth will occur independent of month length. new_unsupplied_city_growth = welt->inverse_scale_with_month_length( new_unsupplied_city_growth ); // Add the computed growth to the growth accumulator. // Future growth scale factors can be applied here. unsupplied_city_growth += new_unsupplied_city_growth; } // does constructions ... void stadt_t::step_grow_city( bool new_town ) { // Try harder to build if this is a new town int num_tries = new_town ? 1000 : 30; // since we use internally a finer value ... const sint64 growth_steps = unsupplied_city_growth / CITYGROWTH_PER_CITIZEN; if( growth_steps > 0 ) { unsupplied_city_growth %= CITYGROWTH_PER_CITIZEN; } // let city grow in steps of 1 for( sint64 n = 0; n < growth_steps; n++ ) { bev++; for( int i = 0; i < num_tries && bev * 2 > won + arb + 100; i++ ) { build(); } check_bau_spezial(new_town); check_bau_townhall(new_town,NULL,-1); check_bau_factory(new_town); // add industry? (not during creation) INT_CHECK("simcity 275"); } } /* this creates passengers and mail for everything. It is therefore one of the CPU hogs of the machine * think trice, before adding here ... */ void stadt_t::step_passagiere() { // decide whether to generate passengers or mail const bool ispass = simrand(GENERATE_RATIO_PASS + GENERATE_RATIO_MAIL) < GENERATE_RATIO_PASS; const goods_desc_t *const wtyp = ispass ? goods_manager_t::passengers : goods_manager_t::mail; const uint32 history_type = ispass ? HIST_BASE_PASS : HIST_BASE_MAIL; factory_set_t &target_factories = ispass ? target_factories_pax : target_factories_mail; // restart at first building? if (step_count >= buildings.get_count()) { step_count = 0; } if (buildings.empty()) { return; } const gebaeude_t* gb = buildings[step_count]; // since now backtravels occur, we damp the numbers a little const uint32 num_pax = (ispass) ? (gb->get_tile()->get_desc()->get_level() + 6) >> 2 : (gb->get_tile()->get_desc()->get_mail_level() + 8) >> 3 ; // create pedestrians in the near area? if (env_t::random_pedestrians && ispass) { pedestrian_t::generate_pedestrians_near(welt->lookup_kartenboden(gb->get_pos().get_2d()), num_pax); } // suitable start search const koord origin_pos = gb->get_pos().get_2d(); const planquadrat_t *const plan = welt->access(origin_pos); const halthandle_t *const halt_list = plan->get_haltlist(); // suitable start search static vector_tpl start_halts(16); start_halts.clear(); for (uint h = 0; h < plan->get_haltlist_count(); h++) { halthandle_t halt = halt_list[h]; if( halt.is_bound() && halt->is_enabled(wtyp) && !halt->is_overcrowded(wtyp->get_index()) ) { start_halts.append(halt); } } // track number of generated passengers. city_history_year [0][history_type + HIST_OFFSET_GENERATED] += num_pax; city_history_month[0][history_type + HIST_OFFSET_GENERATED] += num_pax; // only continue, if this is a good start halt if( !start_halts.empty() ) { // Find passenger destination for( uint32 pax_routed=0, pax_left_to_do=0; pax_routed < num_pax; pax_routed += pax_left_to_do ) { // number of passengers that want to travel // for efficiency we try to route not every single pax, but packets. // If possible, we do 7 passengers at a time // the last packet might have less than 7 pax pax_left_to_do = min(PACKET_SIZE, num_pax - pax_routed); // search target for the passenger pax_return_type will_return; factory_entry_t *factory_entry = NULL; stadt_t *dest_city = NULL; const koord dest_pos = find_destination(target_factories, city_history_month[0][history_type + HIST_OFFSET_GENERATED], &will_return, factory_entry, dest_city); if( factory_entry ) { if (welt->get_settings().get_factory_enforce_demand()) { // ensure no more than remaining amount pax_left_to_do = min( pax_left_to_do, factory_entry->remaining ); factory_entry->remaining -= pax_left_to_do; target_factories.total_remaining -= pax_left_to_do; } target_factories.total_generated += pax_left_to_do; factory_entry->factory->book_stat(pax_left_to_do, ispass ? FAB_PAX_GENERATED : FAB_MAIL_GENERATED); } ware_t pax(wtyp); pax.set_target_pos(dest_pos); pax.amount = pax_left_to_do; pax.to_factory = ( factory_entry ? 1 : 0 ); ware_t return_pax(wtyp); // now, finally search a route; this consumes most of the time int const route_result = haltestelle_t::search_route( &start_halts[0], start_halts.get_count(), welt->get_settings().is_no_routing_over_overcrowding(), pax, &return_pax); halthandle_t start_halt = return_pax.get_target_halt(); if( route_result==haltestelle_t::ROUTE_OK ) { // so we have happy traveling passengers start_halt->starte_mit_route(pax); start_halt->add_pax_happy(pax.amount); // people were transported so are logged city_history_year[0][history_type + HIST_OFFSET_TRANSPORTED] += pax_left_to_do; city_history_month[0][history_type + HIST_OFFSET_TRANSPORTED] += pax_left_to_do; // destination logged merke_passagier_ziel(dest_pos, PAX_DEST_STATUS_REACHABLE); } else if( route_result==haltestelle_t::ROUTE_WALK ) { if( factory_entry ) { // workers and mail delivered instantly to factory factory_entry->factory->liefere_an(wtyp, pax_left_to_do); } // log walked at stop start_halt->add_pax_walked(pax_left_to_do); // people who walk or deliver by hand logged as walking city_history_year[0][history_type + HIST_OFFSET_WALKED] += pax_left_to_do; city_history_month[0][history_type + HIST_OFFSET_WALKED] += pax_left_to_do; // probably not a good idea to mark them as player only cares about remote traffic //merke_passagier_ziel(dest_pos, gfx->palette_lookup(COL_YELLOW)); } else if( route_result==haltestelle_t::ROUTE_OVERCROWDED ) { // overcrowded routes cause unhappiness to be logged if( start_halt.is_bound() ) { start_halt->add_pax_unhappy(pax_left_to_do); } else { // all routes to goal are overcrowded -> register at first stop (closest) if (!start_halts.empty()) { start_halts[0]->add_pax_unhappy(pax_left_to_do); } } // destination logged merke_passagier_ziel(dest_pos, PAX_DEST_STATUS_ROUTE_OVERCROWDED); } else if ( route_result == haltestelle_t::NO_ROUTE ) { // since there is no route from any start halt -> register no route at first halts (closest) if (!start_halts.empty()) { start_halts[0]->add_pax_no_route(pax_left_to_do); } merke_passagier_ziel(dest_pos, PAX_DEST_STATUS_NO_ROUTE); #ifdef DESTINATION_CITYCARS //citycars with destination generate_private_cars( origin_pos, dest_pos ); #endif } // return passenger traffic if( will_return != no_return ) { // compute return amount uint32 pax_return = pax_left_to_do; // apply return modifiers if( will_return != city_return && wtyp == goods_manager_t::mail ) { // attractions and factories return more mail than they receive pax_return *= MAIL_RETURN_MULTIPLIER_PRODUCERS; } // log potential return passengers at destination city dest_city->city_history_year[0][history_type + HIST_OFFSET_GENERATED] += pax_return; dest_city->city_history_month[0][history_type + HIST_OFFSET_GENERATED] += pax_return; // factories generate return traffic if ( factory_entry ) { factory_entry->factory->book_stat(pax_return, (ispass ? FAB_PAX_GENERATED : FAB_MAIL_GENERATED)); } // route type specific logic if( route_result == haltestelle_t::ROUTE_OK ) { // send return packet halthandle_t return_halt = pax.get_target_halt(); if( !return_halt->is_overcrowded(wtyp->get_index()) ) { // stop can receive passengers // register departed pax/mail at factory if (factory_entry) { factory_entry->factory->book_stat(pax_return, ispass ? FAB_PAX_DEPARTED : FAB_MAIL_DEPARTED); } // setup ware packet return_pax.amount = pax_return; return_pax.set_target_pos(origin_pos); return_halt->starte_mit_route(return_pax); // log departed at stop return_halt->add_pax_happy(pax_return); // log departed at destination city dest_city->city_history_year[0][history_type + HIST_OFFSET_TRANSPORTED] += pax_return; dest_city->city_history_month[0][history_type + HIST_OFFSET_TRANSPORTED] += pax_return; } else { // stop is crowded return_halt->add_pax_unhappy(pax_return); } } else if( route_result == haltestelle_t::ROUTE_WALK ) { // walking can produce return flow as a result of commuters to industry, monuments or stupidly big stops // register departed pax/mail at factory if ( factory_entry ) { factory_entry->factory->book_stat(pax_return, ispass ? FAB_PAX_DEPARTED : FAB_MAIL_DEPARTED); } // log walked at stop (source and destination stops are the same) start_halt->add_pax_walked(pax_return); // log people who walk or deliver by hand dest_city->city_history_year[0][history_type + HIST_OFFSET_WALKED] += pax_return; dest_city->city_history_month[0][history_type + HIST_OFFSET_WALKED] += pax_return; } else if( route_result == haltestelle_t::ROUTE_OVERCROWDED ) { // overcrowded routes cause unhappiness to be logged if (pax.get_target_halt().is_bound()) { pax.get_target_halt()->add_pax_unhappy(pax_return); } else { // the unhappy passengers will be added to the first stops near destination (might be none) const planquadrat_t *const dest_plan = welt->access(dest_pos); const halthandle_t *const dest_halt_list = dest_plan->get_haltlist(); for (uint h = 0; h < dest_plan->get_haltlist_count(); h++) { halthandle_t halt = dest_halt_list[h]; if (halt->is_enabled(wtyp)) { halt->add_pax_unhappy(pax_return); break; } } } } else if (route_result == haltestelle_t::NO_ROUTE) { // passengers who cannot find a route will be added to the first stops near destination (might be none) const planquadrat_t *const dest_plan = welt->access(dest_pos); const halthandle_t *const dest_halt_list = dest_plan->get_haltlist(); for (uint h = 0; h < dest_plan->get_haltlist_count(); h++) { halthandle_t halt = dest_halt_list[h]; if (halt->is_enabled(wtyp)) { halt->add_pax_no_route(pax_return); break; } } } } INT_CHECK( "simcity 1579" ); } } else { // assume no free stop to start at all halthandle_t src_halt; // the unhappy passengers will be added to the first stop if any for( uint h=0; hget_haltlist_count(); h++ ) { halthandle_t halt = plan->get_haltlist()[h]; if( halt->is_enabled(wtyp) ) { halt->add_pax_unhappy(num_pax); src_halt = halt; // only overcrowded break; } } // all passengers without suitable start: // fake one ride to get a proper display of destinations (although there may be more) ... pax_return_type will_return; factory_entry_t *factory_entry = NULL; stadt_t *dest_city = NULL; const koord ziel = find_destination(target_factories, city_history_month[0][history_type + HIST_OFFSET_GENERATED], &will_return, factory_entry, dest_city); if( factory_entry ) { // consider at most 1 packet's amount as factory-going sint32 amount = min(PACKET_SIZE, num_pax); if( welt->get_settings().get_factory_enforce_demand() ) { // ensure no more than remaining amount amount = min( amount, factory_entry->remaining ); factory_entry->remaining -= amount; target_factories.total_remaining -= amount; } target_factories.total_generated += amount; factory_entry->factory->book_stat( amount, ( ispass ? FAB_PAX_GENERATED : FAB_MAIL_GENERATED ) ); } // log reverse flow at destination city for accurate tally if( will_return != no_return ) { uint32 pax_return = num_pax; // apply return modifiers if( will_return != city_return && wtyp == goods_manager_t::mail ) { // attractions and factories return more mail than they receive pax_return *= MAIL_RETURN_MULTIPLIER_PRODUCERS; } // log potential return passengers at destination city dest_city->city_history_year[0][history_type + HIST_OFFSET_GENERATED] += pax_return; dest_city->city_history_month[0][history_type + HIST_OFFSET_GENERATED] += pax_return; // factories generate return traffic if ( factory_entry ) { factory_entry->factory->book_stat(pax_return, (ispass ? FAB_PAX_GENERATED : FAB_MAIL_GENERATED)); } // passengers with no route will be added to the first stops near destination (might be none) const planquadrat_t *const dest_plan = welt->access(ziel); const halthandle_t *const dest_halt_list = dest_plan->get_haltlist(); for (uint h = 0; h < dest_plan->get_haltlist_count(); h++) { halthandle_t dst_halt = dest_halt_list[h]; if ( dst_halt->is_enabled(wtyp) ) { // Check if the two networks are actually connected. // If we have 2 networks: // [N1] with halts A,B (both overcrowded) // [N2] with halts C,D (none overcrowded) // and a route is calculated from A to D, // the return pax (from D to A) must not be logged as unhappy // because no halts in N2 are overcrowded. if( src_halt.is_bound() && dst_halt->is_connected(src_halt, wtyp->get_catg_index())==1 ) { dst_halt->add_pax_unhappy(pax_return); // "just" overcrowded } else { dst_halt->add_pax_no_route(pax_return); // no connection at all } break; } } } #ifdef DESTINATION_CITYCARS //citycars with destination generate_private_cars( origin_pos, ziel ); #endif merke_passagier_ziel(ziel, PAX_DEST_STATUS_ROUTE_OVERCROWDED); // we show unhappy instead no route for destination stop } } koord stadt_t::get_zufallspunkt() const { if(!buildings.empty()) { gebaeude_t* const gb = pick_any_weighted(buildings); koord k = gb->get_pos().get_2d(); if(!welt->is_within_limits(k)) { // this building should not be in this list, since it has been already deleted! dbg->error("stadt_t::get_zufallspunkt()", "illegal building in city list of %s: %p removing!", this->get_name(), gb); const_cast(this)->buildings.remove(gb); k = koord(0, 0); } return k; } // might happen on slow computers during creation of new cities or start of map return koord(0,0); } void stadt_t::add_target_city(stadt_t *const city) { target_cities.append( city, weight_by_distance( city->get_einwohner()+1, shortest_distance( get_center(), city->get_center() ) ) ); } void stadt_t::recalc_target_cities() { target_cities.clear(); for(stadt_t* const c : welt->get_cities()) { add_target_city(c); } } void stadt_t::add_target_attraction(gebaeude_t *const attraction) { assert( attraction != NULL ); target_attractions.append( attraction, weight_by_distance( attraction->get_passagier_level() << 4, shortest_distance( get_center(), attraction->get_pos().get_2d() ) ) ); } void stadt_t::recalc_target_attractions() { target_attractions.clear(); for(gebaeude_t* const a : welt->get_attractions()) { add_target_attraction(a); } } /* this function generates a random target for passenger/mail * changing this strongly affects selection of targets and thus game strategy */ koord stadt_t::find_destination(factory_set_t &target_factories, const sint64 generated, pax_return_type* will_return, factory_entry_t* &factory_entry, stadt_t* &dest_city) { // generate factory traffic when required if( target_factories.total_remaining>0 && (sint64)target_factories.generation_ratio>((sint64)(target_factories.total_generated*100)<factory->get_pos().get_2d(); } // chance to generate tourist traffic const sint16 rand = simrand(100 - (target_factories.generation_ratio >> RATIO_BITS)); if( rand < welt->get_settings().get_tourist_percentage() && target_attractions.get_sum_weight() > 0 ) { *will_return = tourist_return; // tourists will return gebaeude_t *const &attraction = pick_any_weighted(target_attractions); dest_city = attraction->get_stadt(); // unsure if return value always valid if (dest_city == NULL) { // if destination city was invalid assume this city is the source dest_city = this; } return attraction->get_pos().get_2d(); } // generate general traffic between buildings // since the locality is already taken into account for us, we just use the random weight stadt_t *const selected_city = pick_any_weighted( target_cities ); // no return trip if the destination is inside the same city *will_return = selected_city == this ? no_return : city_return; dest_city = selected_city; // find a random spot inside the selected city return selected_city->get_zufallspunkt(); } void stadt_t::merke_passagier_ziel(koord k, pax_dest_status_t status) { const koord p = koord( ((k.x * PAX_DESTINATIONS_SIZE) / welt->get_size().x) & (PAX_DESTINATIONS_SIZE-1), ((k.y * PAX_DESTINATIONS_SIZE) / welt->get_size().y) & (PAX_DESTINATIONS_SIZE-1) ); pax_destinations_new_change ++; pax_destinations_new.set(p, status); } /** * building_place_with_road_finder: * Search a free place for a building using function suche_platz() (search place). * added: Minimum distance between monuments */ class building_place_with_road_finder: public building_placefinder_t { public: /// if false, this will the check 'do not build next other to special buildings' bool big_city; building_place_with_road_finder(karte_t* welt, sint16 radius, bool big) : building_placefinder_t(welt, radius), big_city(big) {} // get distance to next special building int find_dist_next_special(koord pos) const { const weighted_vector_tpl& attractions = welt->get_attractions(); int dist = welt->get_size().x * welt->get_size().y; for(gebaeude_t* const i : attractions ) { int const d = koord_distance(i->get_pos(), pos); if( d < dist ) { dist = d; } } for(stadt_t * const city : welt->get_cities() ) { int const d = koord_distance(city->get_pos(), pos); if( d < dist ) { dist = d; } } return dist; } bool is_area_ok(koord pos, sint16 w, sint16 h, climate_bits cl) const OVERRIDE { if( !building_placefinder_t::is_area_ok(pos, w, h, cl) ) { return false; } bool next_to_road = false; // not direct next to factories or townhalls for (sint16 x = -1; x < w; x++) { for (sint16 y = -1; y < h; y++) { grund_t *gr = welt->lookup_kartenboden(pos + koord(x,y)); if (!gr) { return false; } if ( 0 <= x && x < w-1 && 0 <= y && y < h-1 ) { // inside: nothing on top like elevated monorails and not powerlines etc.? if( gr->kann_alle_obj_entfernen(NULL) || welt->lookup(gr->get_pos()+koord3d(0,0,1)) ) { // something on top (monorail or powerlines) return false; } } else { // border: not direct next to special buildings if (big_city) { if( gebaeude_t *gb=gr->find() ) { const building_desc_t::btype utyp = gb->get_tile()->get_desc()->get_type(); if( building_desc_t::attraction_city <= utyp && utyp <= building_desc_t::headquarters) { return false; } } } // but near a road if possible if (!next_to_road) { next_to_road = gr->hat_weg(road_wt); } } } } if (!next_to_road) { return false; } // try to built a little away from previous ones if (big_city && find_dist_next_special(pos) < w + h + welt->get_settings().get_special_building_distance() ) { return false; } return true; } }; void stadt_t::check_bau_spezial(bool new_town) { // tourist attraction buildings const building_desc_t* desc = hausbauer_t::get_special( bev, building_desc_t::attraction_city, welt->get_timeline_year_month(), new_town, welt->get_climate(pos) ); if (desc != NULL) { if (simrand(100) < (uint)desc->get_distribution_weight()) { bool big_city = buildings.get_count() >= 10; bool is_rotate = desc->get_all_layouts() > 1; sint16 radius = koord_distance( get_rechtsunten(), get_linksoben() )/2 + 10; // find place koord best_pos = building_place_with_road_finder(welt, radius, big_city).find_place(pos, desc->get_x(), desc->get_y(), desc->get_allowed_climate_bits(), &is_rotate); if (best_pos != koord::invalid) { // then built it int rotate = 0; if (desc->get_all_layouts() > 1) { rotate = (simrand(20) & 2) + is_rotate; } gebaeude_t* monument = hausbauer_t::build( owner, best_pos, rotate, desc ); add_gebaeude_to_stadt(monument); // tell the player, if not during initialization if (!new_town) { cbuffer_t buf; buf.printf( translator::translate("To attract more tourists\n%s built\na %s\nwith the aid of\n%i tax payers."), get_name(), make_single_line_string(translator::translate(desc->get_name()), 2), get_einwohner()); welt->get_message()->add_message(buf, monument->get_pos(), message_t::city, CITY_KI, desc->get_tile(0)->get_background(0, 0, 0)); } } } } if ((bev & 511) == 0) { // Build a monument desc = hausbauer_t::get_random_monument(welt->get_timeline_year_month()); if (desc) { koord total_size = koord(2 + desc->get_x(), 2 + desc->get_y()); sint16 radius = koord_distance( get_rechtsunten(), get_linksoben() )/2 + 10; koord best_pos(monument_placefinder_t(welt, radius).find_place(pos, total_size.x, total_size.y, desc->get_allowed_climate_bits())); if (best_pos != koord::invalid) { // check if borders around the monument are inside the map limits const bool pre_ok = welt->is_within_limits( koord(best_pos) - koord(1, 1) ) && \ welt->is_within_limits( koord(best_pos) + total_size + koord(1, 1) ); if (!pre_ok){ return; } bool ok=false; // We build the monument only if there is already at least one road for (int i = 0; i < total_size.x && !ok; i++) { ok = ok || welt->access(best_pos + koord(i, -1))->get_kartenboden()->hat_weg(road_wt) || welt->access(best_pos + koord(i, total_size.y))->get_kartenboden()->hat_weg(road_wt); } for (int i = 0; i < total_size.y && !ok; i++) { ok = ok || welt->access(best_pos + koord(total_size.x, i))->get_kartenboden()->hat_weg(road_wt) || welt->access(best_pos + koord(-1, i))->get_kartenboden()->hat_weg(road_wt); } if (ok) { // build roads around the monument sint16 h=welt->lookup_kartenboden(best_pos)->get_hoehe(); for (int i = 0; i < total_size.y; i++) { // only build in same height and not on slopes... const grund_t *gr = welt->lookup_kartenboden(best_pos + koord(0, i)); if(gr->get_hoehe()==h && gr->get_grund_hang()==0) { build_road(best_pos + koord(0, i), NULL, true); } gr = welt->lookup_kartenboden(best_pos + koord(total_size.x - 1, i)); if(gr->get_hoehe()==h && gr->get_grund_hang()==0) { build_road(best_pos + koord(total_size.x - 1, i), NULL, true); } } for (int i = 0; i < total_size.x; i++) { // only build in same height and not on slopes... const grund_t *gr = welt->lookup_kartenboden(best_pos + koord(i, 0)); if(gr->get_hoehe()==h && gr->get_grund_hang()==0) { build_road(best_pos + koord(i, 0), NULL, true); } gr = welt->lookup_kartenboden(best_pos + koord(i, total_size.y - 1)); if(gr->get_hoehe()==h && gr->get_grund_hang()==0) { build_road(best_pos + koord(i, total_size.y - 1), NULL, true); } } // and then build it const gebaeude_t* gb = hausbauer_t::build(owner, best_pos + koord(1, 1), 0, desc); hausbauer_t::monument_erected(desc); add_gebaeude_to_stadt(gb); // tell the player, if not during initialization if (!new_town) { cbuffer_t buf; buf.printf( translator::translate("With a big festival\n%s built\na new monument.\n%i citicens rejoiced."), get_name(), get_einwohner() ); welt->get_message()->add_message(buf, gb->get_pos() + koord3d(1, 1, 0), message_t::city, CITY_KI, desc->get_tile(0)->get_background(0, 0, 0)); } } } } } } void stadt_t::check_bau_townhall(bool new_town, const building_desc_t* desc, sint16 rotation) { if (desc == NULL) { desc = hausbauer_t::get_special(has_townhall ? bev : 0, building_desc_t::townhall, welt->get_timeline_year_month(), (bev == 0) || !has_townhall, welt->get_climate(pos)); } if (desc != NULL) { grund_t* gr = welt->lookup_kartenboden(pos); gebaeude_t* gb = obj_cast(gr->first_no_way_obj()); const bool neugruendung = !has_townhall || !gb || !gb->is_townhall(); bool umziehen = !neugruendung; koord alte_str(koord::invalid); koord best_pos(pos); koord k; int old_layout = rotation; DBG_MESSAGE("check_bau_townhall()", "bev=%d, new=%d name=%s", bev, neugruendung, name.c_str()); if (umziehen) { const building_desc_t* desc_old = gb->get_tile()->get_desc(); if (desc_old->get_level() == desc->get_level()) { DBG_MESSAGE("check_bau_townhall()", "town hall already ok."); return; } old_layout = gb->get_tile()->get_layout(); const sint8 old_z = gb->get_pos().z; koord pos_alt = best_pos = gr->get_pos().get_2d() - gb->get_tile()->get_offset(); // guess layout for broken townhall's if (desc_old->get_x() != desc_old->get_y() && desc_old->get_all_layouts() == 1) { // test all layouts koord corner_offset(desc_old->get_x() - 1, desc_old->get_y() - 1); for (uint8 test_layout = 0; test_layout < 4; test_layout++) { // is there a part of our townhall in this corner grund_t* gr0 = welt->lookup_kartenboden(pos + corner_offset); gebaeude_t const* const gb0 = gr0 ? obj_cast(gr0->first_no_way_obj()) : 0; if (gb0 && gb0->is_townhall() && gb0->get_tile()->get_desc() == desc_old && gb0->get_stadt() == this) { old_layout = test_layout; pos_alt = best_pos = gr->get_pos().get_2d() + koord(test_layout % 3 != 0 ? corner_offset.x : 0, test_layout & 2 ? corner_offset.y : 0); break; } corner_offset = koord(-corner_offset.y, corner_offset.x); } } koord groesse_alt = desc_old->get_size(old_layout); // do we need to move if (old_layout <= desc->get_all_layouts() && desc->get_x(old_layout) <= groesse_alt.x && desc->get_y(old_layout) <= groesse_alt.y) { // no, the size is ok // still need to check whether the existing townhall is not broken in some way umziehen = false; for (k.y = 0; !umziehen && k.y < groesse_alt.y; k.y++) { for (k.x = 0; !umziehen && k.x < groesse_alt.x; k.x++) { // for buildings with holes the hole could be on a different height ->gr==NULL bool ok = false; if (grund_t* gr = welt->lookup_kartenboden(k + pos)) { if (gebaeude_t* gb_part = gr->find()) { // there may be buildings with holes, so we only remove our building! if (gb_part->get_tile() == desc_old->get_tile(old_layout, k.x, k.y)) { ok = true; } } } umziehen |= !ok; } } if (!umziehen) { // correct position if new townhall is smaller than old if (old_layout == 0) { best_pos.y -= desc->get_y(old_layout) - groesse_alt.y; } else if (old_layout == 1) { best_pos.x -= desc->get_x(old_layout) - groesse_alt.x; } } } // Needs bounds checks for the edge case where a townhall is at the very top of the map and the townhall road is demolished // (pos - whatever) could result in negative (or invalid) map coordinates given that pos can be zero and given that zero is a valid pos in the world if (umziehen) { // we need to built a new road, thus we will use the old as a starting point (if found) if (welt->is_within_limits(townhall_road) && welt->lookup_kartenboden(townhall_road)->hat_weg(road_wt)) { alte_str = townhall_road; } else { koord k = pos + (old_layout == 0 ? koord(0, desc_old->get_y()) : koord(desc_old->get_x(), 0)); if (welt->is_within_limits(k) && welt->lookup_kartenboden(k)->hat_weg(road_wt)) { alte_str = k; } else { k = pos - (old_layout == 0 ? koord(0, desc_old->get_y()) : koord(desc_old->get_x(), 0)); if (welt->is_within_limits(k) && welt->lookup_kartenboden(k)->hat_weg(road_wt)) { alte_str = k; } } } } // remove old townhall if (gb) { DBG_MESSAGE("stadt_t::check_bau_townhall()", "delete townhall at (%s)", pos_alt.get_str()); hausbauer_t::remove(welt->get_public_player(), gb); } // replace old space by normal houses level 0 (must be 1x1!) if (umziehen) { for (k.x = 0; k.x < groesse_alt.x; k.x++) { for (k.y = 0; k.y < groesse_alt.y; k.y++) { // we iterate over all tiles, since the townhalls are allowed sizes bigger than 1x1 const koord pos = pos_alt + k; gr = welt->lookup_kartenboden(pos); if (gr && gr->ist_natur() && gr->kann_alle_obj_entfernen(NULL) == NULL && (gr->get_grund_hang() == slope_t::flat || welt->lookup(koord3d(k, welt->max_hgt(k))) == NULL)) { DBG_MESSAGE("stadt_t::check_bau_townhall()", "fill empty spot at (%s)", pos.get_str()); build_city_building(pos); } } } } else { // make tiles flat, hausbauer_t::remove could have set some natural slopes for (k.x = 0; k.x < desc->get_x(old_layout); k.x++) { for (k.y = 0; k.y < desc->get_y(old_layout); k.y++) { gr = welt->lookup_kartenboden(best_pos + k); if (gr && gr->ist_natur()) { // make flat and use right height gr->set_grund_hang(slope_t::flat); gr->set_pos(koord3d(best_pos + k, old_z)); } } } } } // Now built the new townhall (remember old orientation) sint16 layout = old_layout; if (old_layout == -1 || neugruendung) { layout = simrand(desc->get_all_layouts()); } else { layout = old_layout % desc->get_all_layouts(); } // on which side should we place the road? uint8 dir = ribi_t::layout_to_ribi[layout & 3]; if (neugruendung || umziehen) { best_pos = townhall_placefinder_t(welt, dir).find_place(pos, desc->get_x(layout) + (dir & ribi_t::eastwest ? 1 : 0), desc->get_y(layout) + (dir & ribi_t::northsouth ? 1 : 0), desc->get_allowed_climate_bits()); // check, if the was something found if (best_pos == koord::invalid) { dbg->error("stadt_t::check_bau_townhall", "no better position found!"); return; } if (dir == ribi_t::west) { best_pos.x++; } if (dir == ribi_t::north) { best_pos.y++; } } gebaeude_t const* const new_gb = hausbauer_t::build(owner, best_pos, layout, desc); DBG_MESSAGE("new townhall", "use layout=%i", layout); add_gebaeude_to_stadt(new_gb); // sets has_townhall to true DBG_MESSAGE("stadt_t::check_bau_townhall()", "add townhall (bev=%i, ptr=%p)", buildings.get_sum_weight(),welt->lookup_kartenboden(best_pos)->first_no_way_obj()); // if not during initialization koord offset(dir == ribi_t::west, dir == ribi_t::north); if (!new_town) { cbuffer_t buf; buf.printf(translator::translate("%s wasted\nyour money with a\nnew townhall\nwhen it reached\n%i inhabitants."), name.c_str(), get_einwohner()); welt->get_message()->add_message(buf, new_gb->get_pos(), message_t::city, CITY_KI, desc->get_tile(layout, 0, 0)->get_background(0, 0, 0)); } else { welt->lookup_kartenboden(best_pos + offset)->set_text( name ); } if (neugruendung || umziehen) { // offset of building within searched place, start and end of road koord road0(0,0); koord road1(0,0); koord size = desc->get_size(layout); koord origin = new_gb->get_pos().get_2d(); townhall_road = koord::invalid; for (sint8 i = 0; i < 4; i++) { uint8 dir = ribi_t::layout_to_ribi[(layout + i)&3]; switch (dir) { case ribi_t::east: road0.x = road1.x = size.x; road0.y = -neugruendung; road1.y = size.y - umziehen; break; case ribi_t::west: road0.x = road1.x = -1; road0.y = -neugruendung; road1.y = size.y - umziehen; break; case ribi_t::north: road0.y = road1.y = -1; road0.x = -neugruendung; road1.x = size.x - umziehen; break; case ribi_t::south: default: road0.y = road1.y = size.y; road0.x = -neugruendung; road1.x = size.x - umziehen; break; } // build the road in front of the townhall, if possible if (test_and_build_cityroad(origin + road0, origin + road1)) { townhall_road = origin + road0; break; } } } if (umziehen && alte_str != koord::invalid && townhall_road != koord::invalid) { // Strasse vom ehemaligen Rathaus zum neuen verlegen. way_builder_t bauer(NULL); bauer.init_builder(way_builder_t::strasse | way_builder_t::terraform_flag, welt->get_city_road()); bauer.calc_route(welt->lookup_kartenboden(alte_str)->get_pos(), welt->lookup_kartenboden(townhall_road)->get_pos()); bauer.build(); } else if (neugruendung) { lo = best_pos+offset - koord(2, 2); ur = best_pos+offset + koord(desc->get_x(layout), desc->get_y(layout)) + koord(2, 2); } const koord new_pos = best_pos + offset; if( pos!=new_pos ) { // update position (where the name is) welt->lookup_kartenboden(pos)->set_text( NULL ); pos = new_pos; welt->lookup_kartenboden(pos)->set_text( name ); } } } /** * eventually adds a new industry * so with growing number of inhabitants the industry grows */ void stadt_t::check_bau_factory(bool new_town) { uint32 const inc = welt->get_settings().get_industry_increase_every(); if( !new_town && inc > 0 && bev % inc == 0 ) { uint32 const div = bev / inc; for( uint8 i = 0; i < 8; i++ ) { if( div == (1u<& exclude) { koord k; ind_score = ind_start_score; com_score = com_start_score; res_score = res_start_score; exclude.clear(); neighbor_building_clusters = 0; bool do_not_move_origin = false; uint32 ok_tiles = 0; // first check what is allowed and if we have to continue at all and get zero height sint8 zpos = -128; if (const grund_t* gr = welt->lookup_kartenboden(pos)) { zpos = gr->get_pos().z + slope_t::max_diff(gr->get_grund_hang()); if (gr->get_typ() == grund_t::fundament) { if (gebaeude_t const* const gb = gr->find()) { if (!gb->is_city_building() || gb->get_stadt() != this || gb->get_owner()) { // cannot build here return koord::invalid; } const building_desc_t* desc = gb->get_tile()->get_desc(); if ( desc->no_renovation_month() < welt->get_current_month()) { // cannot renovate here return koord::invalid; } exclude.append(desc); ok_tiles = (1 << 12); // 1x1 already ok if (desc->get_area() > 1) { do_not_move_origin = true; koord size = desc->get_size(gb->get_tile()->get_layout()); for (int y = 0; y < size.y; y++) { for (int x = 0; x < size.x; x++) { ok_tiles |= (1 << ((y * 5) + x + 12)); } } } } } else if (!gr->ist_natur()) { return koord::invalid; } } else { return koord::invalid; } // now finally check the rest int shift = 0; for (k.y = -2; k.y <= 2; k.y++) { for (k.x = -2; k.x <= 2; k.x++) { // assert(shift == k.y * 5 + k.x + 12); // already tested? if (ok_tiles & (1<lookup_kartenboden(k+pos)) { if (gr->get_typ() == grund_t::fundament) { if (gebaeude_t const* const gb = gr->find()) { bool building_ok = gb->is_city_building() && gb->get_stadt() == this; if (building_ok) { const building_desc_t* desc = gb->get_tile()->get_desc(); int t = desc->get_type() - building_desc_t::city_res; ind_score += ind_neighbour_score[t]; com_score += com_neighbour_score[t]; res_score += res_neighbour_score[t]; neighbor_building_clusters |= desc->get_clusters(); exclude.append_unique(desc); tile_ok = desc->get_area() == 1 && gb->get_pos().z == zpos && !gb->get_owner() && desc->no_renovation_month() >= welt->get_timeline_year_month(); } } } else { tile_ok = gr->ist_natur() && gr->get_pos().z + slope_t::max_diff(gr->get_grund_hang()) == zpos && (gr->get_pos().z == zpos || !welt->lookup(koord3d(gr->get_pos().get_2d(), zpos))); } } if (tile_ok) { ok_tiles |= (1 << shift); } shift++; } } const sint16 la = hausbauer_t::get_largest_city_building_area(); rotations = 3; if (la == 9) { static const uint32 pattern3x3 = ((1 << 12) | (1 << 17) | (1 << 22) | (1 << 13) | (1 << 18) | (1 << 23) | (1 << 14) | (1 << 19) | (1 << 24)); if ((pattern3x3 & (ok_tiles >> 1)) == pattern3x3) { max_area = 9; return pos; } } if (la >= 6) { static const uint32 pattern2x3 = ((1 << 12) | (1 << 17) | (1 << 22) | (1 << 13) | (1 << 18) | (1 << 23)); if ((pattern2x3 & ok_tiles) == pattern2x3) { rotations = 1; max_area = 6; return pos; } static const uint32 pattern3x2 = ((1 << 12) | (1 << 17) | (1 << 13) | (1 << 18) | (1 << 14) | (1 << 19)); if ((pattern3x2 & ok_tiles) == pattern3x2) { rotations = 2; max_area = 6; return pos; } } if (la >= 4) { static const uint32 pattern2x2 = ((1 << 12) | (1 << 17) | (1 << 13) | (1 << 18)); if ((pattern2x2 & ok_tiles) == pattern2x2) { max_area = 4; return pos; } if (!do_not_move_origin) { static const uint32 pattern2x2m0 = ((1 << 11) | (1 << 16) | (1 << 12) | (1 << 17)); // no fit, now try with offset for 2x2 if ((pattern2x2m0 & ok_tiles) == pattern2x2m0) { max_area = 4; return pos + koord(-1, 0); } static const uint32 pattern2x20m = ((1 << 7) | (1 << 12) | (1 << 8) | (1 << 13)); if ((pattern2x20m & ok_tiles) == pattern2x20m) { max_area = 4; return pos + koord(0, -1); } static const uint32 pattern2x2mm = ((1 << 6) | (1 << 11) | (1 << 7) | (1 << 12)); if ((pattern2x2mm & ok_tiles) == pattern2x2mm) { max_area = 4; return pos + koord(-1, -1); } } // no match, contine with smaller size } if (la >= 3) { static const uint32 pattern1x3 = ((1 << 12) | (1 << 17) | (1 << 22)); if ((pattern1x3 & ok_tiles) == pattern1x3) { rotations = 1; max_area = 3; return pos; } static const uint32 pattern3x1 = ((1 << 12) | (1 << 13) | (1 << 14)); if ((pattern3x1 & ok_tiles) == pattern3x1) { rotations = 2; max_area = 3; return pos; } } if(la>=2) { static const uint32 pattern1x2 = ((1 << 12) | (1 << 17)); static const uint32 pattern2x1 = ((1 << 12) | (1 << 13)); bool ok_1x2 = (pattern1x2 & ok_tiles) == pattern1x2; bool ok_2x1 = (pattern2x1 & ok_tiles) == pattern2x1; if (ok_2x1 && ok_1x2) { max_area = 2; return pos; } if (ok_1x2) { rotations = 1; max_area = 2; return pos; } if (ok_2x1) { rotations = 2; max_area = 2; return pos; } if (!do_not_move_origin) { // try to shift pattern static const uint32 pattern1x20m = ((1 << 7) | (1 << 12)); if ((pattern1x20m & ok_tiles) == pattern1x20m) { rotations = 1; max_area = 2; return pos + koord(0, -1); } static const uint32 pattern2x1m0 = ((1 << 11) | (1 << 12)); if ((pattern2x1m0 & ok_tiles) == pattern2x1m0) { rotations = 2; max_area = 2; return pos + koord(-1, 0); } } } max_area = 1; return pos; } // return the eight neighbors: // orthogonal before diagonal static koord const neighbors[] = { koord( 0, 1), koord( 1, 0), koord( 0, -1), koord(-1, 0), // now the diagonals koord( 1, 1), koord( 1, -1), koord(-1, 1), koord(-1, -1) }; /* static koord const area3x3[] = { koord( 0, 1), // 1x2 koord( 1, 0), // 2x1 koord( 1, 1), // 2x2 koord( 2, 0), // 3x1 koord( 2, 1), // 3x2 koord( 0, 2), // 1x3 koord( 1, 2), // 2x3 koord( 2, 2) // 3x3 }; */ // updates one surrounding road with current city road bool update_city_street(koord pos) { // !!! We should take the bulding size into consideration, missing!!! const way_desc_t* cr = world()->get_city_road(); for( int i=0; i<8; i++ ) { koord check_pos = pos + neighbors[i]; if( grund_t *gr = world()->lookup_kartenboden(check_pos)) { if( weg_t* const weg = gr->get_weg(road_wt) ) { // Check if any changes are needed. if( !weg->hat_gehweg() || weg->get_desc() != cr ) { player_t *sp = weg->get_owner(); if( sp ){ player_t::add_maintenance(sp, -weg->get_desc()->get_maintenance(), road_wt); weg->set_owner(NULL); // make public } weg->set_gehweg(true); weg->set_desc(cr); gr->calc_image(); minimap_t::get_instance()->calc_map_pixel(check_pos); return true; // update only one road per renovation } } } } return false; } // return layout #define CHECK_NEIGHBOUR (128) static int const building_layout[] = { CHECK_NEIGHBOUR | 0, 0, 1, 4, 2, 0, 5, CHECK_NEIGHBOUR | 1, 3, 7, 1, CHECK_NEIGHBOUR | 0, 6, CHECK_NEIGHBOUR | 3, CHECK_NEIGHBOUR | 2, CHECK_NEIGHBOUR | 0 }; inline sint8 check_layout(const building_desc_t* h, koord max_size, sint8 rotation2) { if (h->get_x(rotation2) > max_size.x || h->get_y(rotation2) > max_size.y) { assert(false); } return rotation2; } #define RC(i) check_layout(h,maxarea,(i)) // calculates the "best" orientation of a citybuilding int stadt_t::orient_city_building(const koord k, const building_desc_t *h, koord maxarea ) { /******************************************************* * these are the layout possible for city buildings ******************************************************** dims=1,1,1 +---+ |000| |0 0| |000| +---+ dims=1,1,2 +---+ |001| |1 1| |100| +---+ dims=1,1,4 +---+ |221| |3 1| |300| +---+ dims=1,1,8 +---+ |625| |3 1| |704| +---+ ********************************************************/ // we have something to built here ... if( h == NULL ) { return -1; } grund_t* gr = welt->lookup_kartenboden(k); if (!gr) { return -1; } // old, highly sophisticated routines for symmetric buildings if( h->get_x() == h->get_y() ) { int rotation = 0; int max_layout = h->get_all_layouts()-1; if( max_layout ) { // check for pavement int streetdir = 0; for( int i = 0; i < 4; i++ ) { // Neighbors goes through these in 'preferred' order, orthogonal first koord offset = ((neighbors[i] + koord(1, 1)) / 2) * (h->get_x(0)-1); // symmetric building gr = welt->lookup_kartenboden(k + offset + neighbors[i]); if( gr && gr->get_weg_hang() == gr->get_grund_hang() && gr->hat_weg(road_wt) ){ streetdir += (1 << i); } } // not completely unique layout, see if any of the neighbouring building gives a hint rotation = building_layout[streetdir] & ~CHECK_NEIGHBOUR; // for four rotation stop here ... if( max_layout<7 ) { return rotation & max_layout; } // now this is an eight roation building, so we must put in more effort bool unique_orientation = !(building_layout[streetdir] & CHECK_NEIGHBOUR); if( !unique_orientation ) { // no unique answer, check nearby buildings (more likely to fail) int gb_dir = 0; for( int i = 0; i < 4; i++ ) { // look for adjacent buildings gr = welt->lookup_kartenboden(k + neighbors[i]); if( gr && gr->get_typ()==grund_t::fundament ){ if( gr->find() ) { gb_dir |= (1<get_all_layouts()-1; if (max_layout) { // attention x and y for buildigns and in game swapped!!!! bool fit1 = maxarea.x >= h->get_x(0) && maxarea.y >= h->get_y(0); bool fit0 = maxarea.x >= h->get_x(1) && maxarea.y >= h->get_y(1); if (!(fit0 || fit1)) { // too large return -1; } if (fit1 != fit0 && max_layout == 1) { // only one orientation return RC(fit0); } sint16 street_counter_ew = 0; sint16 street_counter_ew_ns = 0; sint16 roads_ew = 0; if (fit0) { roads_ew++; sint16 extra_offset = h->get_x(1); for (int offset = -1; offset <= h->get_y(1); offset++) { gr = welt->lookup_kartenboden(k + koord(extra_offset, offset)); if (gr && gr->hat_weg(road_wt)) { street_counter_ew++; // east roads_ew++; } // Neighbors goes through these in 'preferred' order, orthogonal first gr = welt->lookup_kartenboden(k + koord(-1, offset)); if (gr && gr->hat_weg(road_wt)) { street_counter_ew--; //west roads_ew++; } } extra_offset = h->get_y(1); for (int offset = 0; offset < h->get_x(1); offset++) { gr = welt->lookup_kartenboden(k + koord(offset, extra_offset)); if (gr && gr->hat_weg(road_wt)) { street_counter_ew_ns++; // south roads_ew++; } gr = welt->lookup_kartenboden(k + koord(offset,-1)); if (gr && gr->hat_weg(road_wt)) { street_counter_ew_ns--; // south roads_ew++; } } } sint16 street_counter_ns = 0; sint16 street_counter_ns_ew = 0; sint16 roads_ns = 0; if (fit1) { roads_ns++; sint16 extra_offset = h->get_y(0); for (int offset = -1; offset <= h->get_x(0); offset++) { gr = welt->lookup_kartenboden(k + koord(offset, extra_offset)); if (gr && gr->hat_weg(road_wt)) { street_counter_ns++; // south roads_ns++; } gr = welt->lookup_kartenboden(k + koord(offset, -1)); if (gr && gr->hat_weg(road_wt)) { street_counter_ns--; // north roads_ns++; } } extra_offset = h->get_x(0); for (int offset = 0; offset < h->get_y(0); offset++) { gr = welt->lookup_kartenboden(k + koord(extra_offset, offset)); if (gr && gr->hat_weg(road_wt)) { street_counter_ns_ew++; // south roads_ns++; } gr = welt->lookup_kartenboden(k + koord(-1,offset)); if (gr && gr->hat_weg(road_wt)) { street_counter_ns_ew--; // south roads_ns++; } } } if (roads_ns > roads_ew) { // more NS roads if (street_counter_ns) { return RC((street_counter_ns > 0 ? 0 : 2) & max_layout); } // equal ... return RC((simrand(2) * 2) & max_layout); } else if (roads_ns < roads_ew) { // more EW roads if (street_counter_ew) { return RC((street_counter_ew > 0 ? 3 : 1) & max_layout); } return RC((simrand(2) * 2 + 1) & max_layout); } if (fit1 != fit0) { // only one orientation fits if (fit0) { return RC((simrand(2) * 2 + 1) & max_layout); } else { return RC((simrand(2) * 2) & max_layout); } } return RC(simrand(4) & max_layout); } // we landed here but maxlayout == 1, so we have only to test for fit if( maxarea.x >= h->get_x(0) && maxarea.y >= h->get_y(0) ) { return RC(0); } return -1; } // helper function to make sure we could remove this house or build here bool stadt_t::check_ground_tile_for_house(grund_t *gr, sint8 zpos) const { if (gr) { if (gr->ist_natur() && gr->get_pos().z + slope_t::max_diff(gr->get_grund_hang()) == zpos) { // we can of course also build on nature if there is nothing on top return gr->get_pos().z==zpos || !welt->lookup(koord3d(gr->get_pos().get_2d(),zpos)); } if(gr->get_hoehe() == zpos && gr->get_typ() == grund_t::fundament) { // a building found gebaeude_t const* const testgb = gr->find(); if (testgb->get_owner()==NULL) { const building_desc_t* neighbor_building = testgb->get_tile()->get_desc(); if ( neighbor_building->is_city_building() && neighbor_building->get_area() == 1 && testgb->get_stadt()==this ) { // also in right height and citybuilding => we could remove it if needed return true; } } } } return false; } // much faster to use our own routine and also preserves the bookeeping gebaeude_t* stadt_t::build_city_house(koord3d base_pos, const building_desc_t* h, uint8 rotation, uint32 cl, vector_tpl *exclude_desc) { bool recalc_foundations = false;; koord min_size(0, 0); sint16 level = 1; grund_t* gr = welt->lookup_kartenboden_nocheck(base_pos.get_2d()); gebaeude_t* org = gr->find(); if (org) { min_size = org->get_tile()->get_desc()->get_size(org->get_tile()->get_layout()); level = org->get_tile()->get_desc()->get_level(); } // ok now build and possibly replace for (int x = 0; x < h->get_x(rotation); x++) { for (int y = 0; y < h->get_y(rotation); y++) { koord kpos = base_pos.get_2d() + koord(x, y); grund_t* gr = welt->lookup_kartenboden_nocheck(kpos); if (gr->get_typ()==grund_t::fundament) { gebaeude_t* oldgb = gr->find(); switch (oldgb->get_tile()->get_desc()->get_type()) { case building_desc_t::city_res: won -= oldgb->get_tile()->get_desc()->get_level() * 10; break; case building_desc_t::city_com: arb -= oldgb->get_tile()->get_desc()->get_level() * 20; break; case building_desc_t::city_ind: arb -= oldgb->get_tile()->get_desc()->get_level() * 20; break; default: assert(false); break; } // exchange building; try to face it to street in front oldgb->mark_images_dirty(); oldgb->set_tile(h->get_tile(rotation, x, y), true); update_gebaeude_from_stadt(oldgb); org = oldgb; } else { // nature here if (gr->hat_wege()) { dbg->error("stadt_t::build_city_house", "Has way on tile (%s)", gr->get_pos().get_str()); } koord3d new_pos = base_pos + koord(x, y); recalc_foundations = new_pos.z != gr->get_pos().z; gebaeude_t* gb = new gebaeude_t(new_pos, NULL, h->get_tile(rotation, x, y)); gr->obj_loesche_alle(welt->get_public_player()); grund_t* gr2 = new fundament_t(new_pos, slope_t::flat); welt->access(kpos)->boden_ersetzen(gr, gr2); gb->set_stadt(this); buildings.append(gb, gb->get_tile()->get_desc()->get_level() + 1); gr2->obj_add(gb); org = gb; } welt->lookup_kartenboden(kpos)->calc_image(); switch (h->get_type()) { case building_desc_t::city_res: won += h->get_level() * 10; break; case building_desc_t::city_com: arb += h->get_level() * 20; break; case building_desc_t::city_ind: arb += h->get_level() * 20; break; default: assert(false); break; } } } // we may need to recalculate vertical walls if (recalc_foundations) { // need to calc the grounds to south x+1 and east y+1 and the diagonal for (int x = 0; x < h->get_x(rotation); x++) { koord k = base_pos.get_2d() + koord(x, h->get_y(rotation)); if (grund_t* gr = welt->lookup_kartenboden(k)) { gr->calc_image(); } } for (int y = 0; y < h->get_y(rotation); y++) { koord k = base_pos.get_2d() + koord(h->get_x(rotation),y); if (grund_t* gr = welt->lookup_kartenboden(k)) { gr->calc_image(); } } koord k = base_pos.get_2d() + h->get_size(rotation); if (grund_t* gr = welt->lookup_kartenboden(k)) { gr->calc_image(); } } // if new building is smaller than old one => convert remaining tiles for (int x = 0; x < min_size.x; x++) { for (int y = 0; y < min_size.y; y++) { if (x < h->get_x(rotation) && y < h->get_y(rotation)) { continue; } koord kpos = base_pos.get_2d() + koord(x, y); grund_t* gr = welt->lookup_kartenboden_nocheck(kpos); gebaeude_t* oldgb = gr->find(); const building_desc_t* hr = NULL; switch (oldgb->get_tile()->get_desc()->get_type()) { case building_desc_t::city_res: won -= level * 10; hr = hausbauer_t::get_residential(level, welt->get_timeline_year_month(), welt->get_climate(kpos), cl, 1, 1, exclude_desc); won += hr->get_level(); break; case building_desc_t::city_com: arb -= level * 20; hr = hausbauer_t::get_commercial(level, welt->get_timeline_year_month(), welt->get_climate(kpos), cl, 1, 1, exclude_desc); arb += hr->get_level() * 20; break; case building_desc_t::city_ind: arb -= level * 20; hr = hausbauer_t::get_industrial(level, welt->get_timeline_year_month(), welt->get_climate(kpos), cl, 1, 1, exclude_desc); arb += hr->get_level() * 20; break; default: assert(false); break; } // for now we just remove it to avoid half buildings left exclude_desc->append(hr); oldgb->set_tile(hr->get_tile(0), true); gr->calc_image(); update_gebaeude_from_stadt(oldgb); switch (hr->get_type()) { case building_desc_t::city_res: won += hr->get_level() * 10; break; case building_desc_t::city_com: arb += hr->get_level() * 20; break; case building_desc_t::city_ind: arb += hr->get_level() * 20; break; default: break; } } } return org; } void stadt_t::build_city_building(koord k_org) { if (grund_t* gr = welt->lookup_kartenboden(k_org)) { if (gebaeude_t* gb = gr->find()) { k_org = gb->get_first_tile()->get_pos().get_2d(); } } else { return; } // Divide unemployed by 4, because it counts towards commercial and industrial, // and both of those count 'double' for population relative to residential. int employment_wanted = get_unemployed() / 4; int housing_wanted = get_homeless(); int industrial_suitability, commercial_suitability, residential_suitability; static vector_tpl exclude_desc; exclude_desc.clear(); uint32 neighbor_building_clusters = 0; sint16 max_area; uint8 rotation; koord k = evaluate_size_res_com_ind(k_org, industrial_suitability, commercial_suitability, residential_suitability, max_area, rotation, neighbor_building_clusters, exclude_desc); if (k == koord::invalid) { return; } const int sum_industrial = industrial_suitability + employment_wanted; const int sum_commercial = commercial_suitability + employment_wanted; const int sum_residential = residential_suitability + housing_wanted; // does the timeline allow this building? const uint16 current_month = welt->get_timeline_year_month(); const climate cl = welt->get_climate(k); // Find a house to build const building_desc_t* h = NULL; if (sum_commercial > sum_industrial && sum_commercial >= sum_residential) { h = hausbauer_t::get_commercial(0, current_month, cl, neighbor_building_clusters, 1, max_area, &exclude_desc); } if (h == NULL && sum_industrial > sum_residential && sum_industrial >= sum_commercial) { h = hausbauer_t::get_industrial(0, current_month, cl, neighbor_building_clusters, 1, max_area, &exclude_desc); } if (h == NULL && sum_residential > sum_industrial && sum_residential >= sum_commercial) { h = hausbauer_t::get_residential(0, current_month, cl, neighbor_building_clusters, 1, max_area, &exclude_desc); } if (h == NULL) { return; } // recalculate allowed extension koord max_size(1, 1); if (h->get_area() > 1) { if (max_area == 2) { max_size.y += (rotation & 1); max_size.x += (rotation>>1) & 1; } else if (max_area == 3) { max_size.y += (rotation & 1)*2; max_size.x += (rotation & 2); } else if (max_area == 4) { max_size = koord(2, 2); } else if (max_area == 6) { max_size = (rotation & 1) ? koord(2, 3) : koord(3, 2); } else if (max_area == 9) { max_size = koord(3, 3); } } else if(k_org!=k) { // 1x1 house but origin moved => move back k = k_org; } // so we found at least one suitable building for this place rotation = orient_city_building( k, h, max_size ); grund_t *gr = welt->lookup_kartenboden_nocheck(k); koord3d base_pos = koord3d(k, gr->get_hoehe() + slope_t::max_diff(gr->get_grund_hang())); const gebaeude_t* gb = build_city_house(base_pos, h, rotation, cl, &exclude_desc); if (gb) { // more tests for larger building ... koord sz = gb->get_tile()->get_desc()->get_size(gb->get_tile()->get_layout()); for (sint16 x = 0; x < sz.x; x++) { for (sint16 y = 0; y < sz.y; y++) { update_city_street(gb->get_pos().get_2d() + koord(x, y)); } } } else { // just check city roads update_city_street(k); } } bool stadt_t::renovate_city_building(gebaeude_t *gb) { if( !gb->is_city_building() || gb->get_first_tile() != gb) { return false; // only renovate res, com, ind } const building_desc_t::btype alt_typ = gb->get_tile()->get_desc()->get_type(); // Now we are sure that this is a city building const building_desc_t *gb_desc = gb->get_tile()->get_desc(); const int level = gb_desc->get_level(); if( welt->get_timeline_year_month() > gb_desc->no_renovation_month() ) { // this is a historic city building (as defined by the pak set author), so do not renovate return false; } const koord3d base_pos = gb->get_pos(); // Divide unemployed by 4, because it counts towards commercial and industrial, // and both of those count 'double' for population relative to residential. const int employment_wanted = get_unemployed() / 4; const int housing_wanted = get_homeless() / 4; int industrial_suitability, commercial_suitability, residential_suitability; static vector_tpl exclude_desc; exclude_desc.clear(); uint32 neighbor_building_clusters = 0; sint16 max_area; uint8 rotation; koord k = evaluate_size_res_com_ind(base_pos.get_2d(), industrial_suitability, commercial_suitability, residential_suitability, max_area, rotation, neighbor_building_clusters, exclude_desc); if (k == koord::invalid) { return false; } const int sum_industrial = industrial_suitability + employment_wanted; const int sum_commercial = commercial_suitability + employment_wanted; const int sum_residential = residential_suitability + housing_wanted; // does the timeline allow this building? const uint16 current_month = welt->get_timeline_year_month(); const climate cl = welt->get_climate( k ); building_desc_t::btype want_to_have = building_desc_t::unknown; int sum = 0; // try to build const building_desc_t* h = NULL; if (sum_commercial > sum_industrial && sum_commercial > sum_residential) { // we must check, if we can really update to higher level ... h = hausbauer_t::get_commercial(level+1, current_month, cl, neighbor_building_clusters, 1, max_area, &exclude_desc); if( h != NULL && h->get_level() >= level+1 ) { want_to_have = building_desc_t::city_com; sum = sum_commercial; } } // check for industry, also if we wanted com, but there was no com good enough ... if( (sum_industrial > sum_commercial && sum_industrial > sum_residential) || (sum_commercial > sum_residential && want_to_have == building_desc_t::unknown) ) { // we must check, if we can really update to higher level ... h = hausbauer_t::get_industrial(level+1 , current_month, cl, neighbor_building_clusters, 1, max_area, &exclude_desc); if( h != NULL && h->get_level() >= level+1 ) { want_to_have = building_desc_t::city_ind; sum = sum_industrial; } } // check for residence // (sum_wohnung>sum_industrie && sum_wohnung>sum_gewerbe if( want_to_have == building_desc_t::unknown ) { // we must check, if we can really update to higher level ... h = hausbauer_t::get_residential(level+1, current_month, cl, neighbor_building_clusters, 1, max_area, &exclude_desc); if( h != NULL && h->get_level() >= level+1 ) { want_to_have = building_desc_t::city_res; sum = sum_residential; } else { h = NULL; } } if (h == NULL) { // no matching building found ... return false; } // only renovate, if there is a change in level if (h->get_level() < gb_desc->get_level() && h->get_type() == gb_desc->get_type()) { // same level, no reason to replace return false; } if (alt_typ != want_to_have) { // penalty for changing sum -= level * 10; } // good enough replacement to renovate? if( sum > 0 ) { // recalculate allowed extension koord max_size(1, 1); if (h->get_area() > 1) { if (max_area == 2) { max_size.y += (rotation & 1); max_size.x += (rotation >> 1) & 1; } else if (max_area == 3) { max_size.y += (rotation & 1) * 2; max_size.x += (rotation & 2); } else if (max_area == 4) { max_size = koord(2, 2); } else if (max_area == 6) { max_size = (rotation & 1) ? koord(2, 3) : koord(3, 2); } else if (max_area == 9) { max_size = koord(3, 3); } } else if (base_pos.get_2d() != k) { // 1x1 house but origin moved => move back k = base_pos.get_2d(); } int rotation2 = orient_city_building(k, h, max_size); const gebaeude_t *gb= build_city_house(koord3d(k, base_pos.z), h, rotation2, cl, &exclude_desc); if (gb) { // more tests for larger building ... koord sz = gb->get_tile()->get_desc()->get_size(gb->get_tile()->get_layout()); for (sint16 x = 0; x < sz.x; x++) { for (sint16 y = 0; y < sz.y; y++) { update_city_street(gb->get_pos().get_2d() + koord(x, y)); } } } else { // just check city roads update_city_street(k); } recalc_city_size(); } return true; } #ifdef DESTINATION_CITYCARS void stadt_t::generate_private_cars(koord pos, koord target) { if (!private_car_t::list_empty() && number_of_cars>0 ) { koord k; for (k.y = pos.y - 1; k.y <= pos.y + 1; k.y++) { for (k.x = pos.x - 1; k.x <= pos.x + 1; k.x++) { if( grund_t* gr = welt->lookup_kartenboden(k) ) { const weg_t* weg = gr->get_weg(road_wt); if (weg != NULL && ( weg->get_ribi_unmasked(road_wt) == ribi_t::northsouth || weg->get_ribi_unmasked(road_wt) == ribi_t::eastwest) && player_t::check_owner(NULL, w->get_owner() ) { // already a car here => avoid congestion if (gr->obj_bei(gr->get_top() - 1)->is_moving()) { continue; } private_car_t* vt = new private_car_t(gr, target); gr->obj_add(vt); city_history_month[0][HIST_CITYCARS]++; city_history_year[0][HIST_CITYCARS]++; number_of_cars--; return; } } } } } } #endif /** * built/extends a road and maybe changes the neighbouring tile to continue it as far as possible * @param k Bauposition */ bool stadt_t::build_road(const koord k, player_t* player_, bool forced) { grund_t* bd = welt->lookup_kartenboden(k); if (!bd || bd->get_typ() != grund_t::boden) { // not on water, monorails, foundations, tunnel or bridges return false; } // we must not built on water or runways etc. if (bd->hat_wege() && !bd->hat_weg(road_wt)) { if (weg_t* w = bd->get_weg(track_wt)) { if (w->get_desc()->get_styp() != type_tram) { return false; } } else { return false; } } // now try single tile road conenction ribi_t::ribi connection_roads = 0; if (!bd->hat_weg(road_wt) && bd->get_typ() == grund_t::boden) { // somebody else's things on it? if (bd->kann_alle_obj_entfernen(NULL)) { return false; } // try artificial slope. For this, we need to know the height of the tile with the connecting road for (sint8 r = 0; r < 4; r++) { if (grund_t* gr = welt->lookup_kartenboden(k + koord::nesw[r])) { if (gr->hat_weg(road_wt)) { // Do we want to connect to nextnext tile? bool terraform_allowed = !connection_roads; if (gr->find() && ribi_t::doubles(gr->get_weg_ribi_unmasked(road_wt)) != ribi_t::doubles(ribi_t::nesw[r])) { // these all force to continue in on direction continue; } if (gr->get_typ() == grund_t::brueckenboden || gr->get_typ() == grund_t::tunnelboden) { // must be a ramp or portal, since it is kartenboden if (ribi_t::doubles(gr->get_weg_ribi_unmasked(road_wt)) != ribi_t::doubles(ribi_t::nesw[r])) { // wrong dir continue; } terraform_allowed = false; // vmove will check for height } else if (gr->get_typ() != grund_t::boden) { continue; } if (terraform_allowed && !bd->get_weg_hang() && gr->get_hoehe() != bd->get_hoehe()) { sint8 dir = bd->get_hoehe() < gr->get_hoehe() ? +1 : -1; // check height clearance (no bridges or tunnels would be blocked by this when becoming a slope) // (This is only possible near articial sloes, but the check is not expensive) if (welt->lookup(koord3d(k + koord::nesw[r], bd->get_hoehe() + dir))) { continue; } if (welt->get_settings().get_way_height_clearance() == 2 && welt->lookup(koord3d(k + koord::nesw[r], bd->get_hoehe() + 2*dir))) { continue; } } // try to connect: check if other tile on slope if (gr->get_weg_hang() && ribi_t::doubles(ribi_type(gr->get_weg_hang())) != ribi_t::doubles(ribi_t::nesw[r])) { // this is on a slope => we can only connect in straight direction continue; } if (!terraform_allowed && bd->get_weg_hang() && ribi_t::doubles(ribi_type(bd->get_weg_hang())) != ribi_t::doubles(ribi_t::nesw[r])) { // we are on a slope => we can only connect in straight direction continue; } if (!terraform_allowed && bd->get_vmove(ribi_t::nesw[r]) != gr->get_vmove(ribi_t::backward(ribi_t::nesw[r]))) { // connection height do not match and we cannot terraform continue; } // Next: other tile stop/depot with a direction if (const gebaeude_t* gb = gr->find()) { // there is a building on it halt => we can only go in the allow ribis if (gb->get_tile()->get_desc()->get_all_layouts() == 4 && ribi_t::backward(ribi_t::nesw[r]) != ribi_t::layout_to_ribi[gb->get_tile()->get_layout()]) { // single way => only one dir allowed continue; } else if (gb->get_tile()->get_desc()->get_all_layouts() && ribi_t::doubles(ribi_t::nesw[r]) != ribi_t::doubles(ribi_t::layout_to_ribi[gb->get_tile()->get_layout() & 1])) { // wrong direction of through way continue; } } // Finally: same for depot if (const depot_t* dp = gr->get_depot()) { if(ribi_t::backward(ribi_t::nesw[r]) != ribi_t::layout_to_ribi[dp->get_tile()->get_layout()]) { // single way => only one dir allowed continue; } } // so we should be able to connect sint8 target_h = gr->get_vmove(ribi_t::backward(ribi_t::nesw[r])); if (terraform_allowed) { // only try to alter to accomodate bool connected_across = false; if (const grund_t* gr2 = welt->lookup_kartenboden(k - koord::nesw[r])) { if (gr2->hat_weg(road_wt)) { // we may want to connect or give up, if this is impossible if (gr2->get_grund_hang() == slope_t::flat || ribi_t::is_straight(ribi_t::doubles(ribi_t::nesw[r]) | gr2->get_weg_ribi_unmasked(road_wt))) { // we want also to connect to this target slope since it can go in our direction sint8 target_h2 = gr2->get_vmove(ribi_t::nesw[r]); if (abs(target_h - target_h2) > 2) { // too big difference => no road ending at slope please continue; } // we can connect both connected_across = true; if (target_h == target_h2) { // flat tile here bd->set_grund_hang(slope_t::flat); bd->set_pos(koord3d(k, target_h)); for (int i = 0; i < bd->obj_count(); i++) { bd->obj_bei(i)->set_pos(bd->get_pos()); } } else { // slope and height if (target_h > target_h2) { bd->set_pos(koord3d(k, target_h2)); bd->set_grund_hang(slope_type(ribi_t::nesw[r]) * (target_h - target_h2)); } else { bd->set_pos(koord3d(k, target_h)); bd->set_grund_hang(slope_type(ribi_t::backward(ribi_t::nesw[r])) * (target_h2 - target_h)); } } } else { // road on a slope but wrong direction continue; } } } if (!connected_across) { // not already sucessful slope_t::type this_slope = bd->get_grund_hang(); if ((slope_t::is_way(this_slope) && (bd->get_hoehe() == target_h && (this_slope == slope_t::flat || (ribi_type(this_slope) & ribi_t::northwest & ribi_t::nesw[r]) != 0))) || (((ribi_type(this_slope) & ribi_t::southeast & ribi_t::nesw[r]) != 0) && target_h == bd->get_hoehe() + slope_t::max_diff(this_slope)) ) { // we can connect => no further preparations needed } else { // slopes do not match, and we have not yet connected // try to level the land if (welt->can_flatten_tile(NULL, k, target_h, true)) { // sucessful, so now we can build welt->flatten_tile(NULL, k, target_h, true); bd = welt->lookup_kartenboden(k); // since ground has changed } // now we try articial slopes else if (abs(bd->get_hoehe() - target_h) > 2) { // ground too deep => give up continue; } else if (bd->get_hoehe() < target_h) { // down or flat slope could connect sint8 max_h = bd->get_hoehe() + slope_t::max_diff(bd->get_grund_hang()); slope_t::type sl = bd->get_grund_hang(); if (max_h > gr->get_hoehe() || (max_h == gr->get_hoehe() && 3 == corner_ne(sl) + corner_se(sl) + corner_nw(sl) + corner_sw(sl))) { // in between OR three corners at target height => we raise the entire tile bd->set_grund_hang(slope_t::flat); bd->set_pos(koord3d(k, target_h)); for (int i = 0; i < bd->obj_count(); i++) { bd->obj_bei(i)->set_pos(bd->get_pos()); } } else { // we make a down slope bd->set_grund_hang(slope_type(ribi_t::nesw[r]) * (target_h - bd->get_hoehe())); } } else if (bd->get_hoehe() == target_h) { // find out height of nextnext tile sint8 next_h = bd->get_hoehe(); if (grund_t* gr2 = welt->lookup_kartenboden(k - koord::nesw[r])) { next_h = gr2->get_hoehe(); } if (next_h>=bd->get_hoehe()) { // at least three raised corners => up slope bd->set_grund_hang(slope_type(ribi_t::backward(ribi_t::nesw[r]))* min(next_h - bd->get_hoehe(), 2)); } else { bd->set_grund_hang(slope_t::flat); } } else { // bd height higher than target // lower with up slope sint8 max_h = bd->get_hoehe() + slope_t::max_diff(bd->get_grund_hang()); // in between, so we raise the entire tile bd->set_grund_hang(slope_type(ribi_t::backward(ribi_t::nesw[r])) * min(max_h - target_h, 2)); bd->set_pos(koord3d(k, target_h)); for (int i = 0; i < bd->obj_count(); i++) { bd->obj_bei(i)->set_pos(bd->get_pos()); } } } } } if (slope_t::is_way(bd->get_grund_hang())) { if (!connection_roads) { // now all slopes should be correct, we can build // city roads should not belong to any player => so we can ignore any construction costs ... strasse_t* weg = new strasse_t(); weg->set_desc(welt->get_city_road()); weg->set_gehweg(true); bd->neuen_weg_bauen(weg, ribi_t::nesw[r], player_); bd->calc_image(); gr->weg_erweitern(road_wt, ribi_t::backward(ribi_t::nesw[r])); connection_roads |= ribi_t::nesw[r]; } else { // just extend ways bd->weg_erweitern(road_wt, ribi_t::nesw[r]); gr->weg_erweitern(road_wt, ribi_t::backward(ribi_t::nesw[r])); } // assume that we have terraformed ... bd->calc_image(); if (grund_t* gr = welt->lookup_kartenboden(k + koord(1, 0))) { gr->calc_image(); } if (grund_t* gr = welt->lookup_kartenboden(k + koord(0, 1))) { gr->calc_image(); } } } } } } if (!slope_t::is_way(bd->get_weg_hang())) { // not a way slope => could not even connect with terraform return false; } if (!bd->hat_weg(road_wt) && forced) { strasse_t* weg = new strasse_t(); weg->set_desc(welt->get_city_road()); weg->set_gehweg(true); bd->neuen_weg_bauen(weg, ribi_t::none, player_); bd->calc_image(); return true; } // check to bridge a river if(ribi_t::is_single(connection_roads) && !bd->has_two_ways() ) { koord zv = koord(ribi_t::backward(connection_roads)); grund_t *bd_next = welt->lookup_kartenboden( k + zv ); if(bd_next && (bd_next->is_water() || (bd_next->hat_weg(water_wt) && bd_next->get_weg(water_wt)->get_desc()->get_styp()== type_river))) { // ok there is a river const bridge_desc_t *bridge = bridge_builder_t::find_bridge(road_wt, welt->get_city_road()->get_topspeed(), welt->get_timeline_year_month() ); if( bridge==NULL ) { // does not have a bridge available ... return false; } // find the first tile which is not a river for (int i = 2; i < 5; i++) { bd_next = welt->lookup_kartenboden(k + zv * i); if (!bd_next) { return false; } if (bd_next->is_water() || (bd_next->hat_weg(water_wt) && bd_next->get_weg(water_wt)->get_desc()->get_styp() == type_river)) { bd_next = NULL; continue; } // first tile after the river else if (bd->get_typ() != grund_t::boden) { return false; } break; } if (!bd_next) { return false; } sint8 bridge_height; const char* err = bridge_builder_t::can_build_bridge(NULL, bd->get_pos(), bd_next->get_pos(), bridge_height, bridge); // if the river is navigable, we need a two hight slope, so we have to start on a flat tile koord end = bd_next->get_pos().get_2d(); if (err) { if(bd->get_grund_hang() != slope_t::flat || bd_next->get_grund_hang() != slope_t::flat) { slope_t::type try_flat_start = bd->get_grund_hang(); if (try_flat_start != slope_t::flat) { sint8 h_diff = slope_t::max_diff(try_flat_start); // raise up the tile bd->set_grund_hang(slope_t::flat); bd->set_hoehe(bd->get_hoehe() + h_diff); // transfer objects to on new grund for (int i = 0; i < bd->obj_count(); i++) { bd->obj_bei(i)->set_pos(bd->get_pos()); } } slope_t::type try_flat_end = bd_next->get_grund_hang(); if (try_flat_end != slope_t::flat) { sint8 h_diff = slope_t::max_diff(try_flat_end); // raise up the tile bd_next->set_grund_hang(slope_t::flat); bd_next->set_hoehe(bd_next->get_hoehe() + h_diff); // transfer objects to on new grund for (int i = 0; i < bd_next->obj_count(); i++) { bd_next->obj_bei(i)->set_pos(bd_next->get_pos()); } } err = bridge_builder_t::can_build_bridge(NULL, bd->get_pos(), bd_next->get_pos(), bridge_height, bridge); if (err) { // still impossible => restore slope if (try_flat_start != slope_t::flat) { sint8 h_diff = slope_t::max_diff(try_flat_start); bd->set_grund_hang(try_flat_start); bd->set_hoehe(bd->get_hoehe() - h_diff); // transfer objects to on new grund for (int i = 0; i < bd->obj_count(); i++) { bd->obj_bei(i)->set_pos(bd->get_pos()); } } if (try_flat_end != slope_t::flat) { sint8 h_diff = slope_t::max_diff(try_flat_end); // raise up the tile bd_next->set_grund_hang(try_flat_start); bd_next->set_hoehe(bd_next->get_hoehe() - h_diff); // transfer objects to on new grund for (int i = 0; i < bd_next->obj_count(); i++) { bd_next->obj_bei(i)->set_pos(bd_next->get_pos()); } } return false; // give up } // update slope graphics on tile and tile in front if( grund_t *bd_recalc = welt->lookup_kartenboden( k + koord( 0, 1 ) ) ) { bd_recalc->check_update_underground(); } if( grund_t *bd_recalc = welt->lookup_kartenboden( k + koord( 1, 0 ) ) ) { bd_recalc->check_update_underground(); } if( grund_t *bd_recalc = welt->lookup_kartenboden( k + koord( 1, 1 ) ) ) { bd_recalc->check_update_underground(); } bd->mark_image_dirty(); // update slope graphics on tile and tile in front if (grund_t* bd_recalc = welt->lookup_kartenboden(end + koord(0, 1))) { bd_recalc->check_update_underground(); } if (grund_t* bd_recalc = welt->lookup_kartenboden(end + koord(1, 0))) { bd_recalc->check_update_underground(); } if (grund_t* bd_recalc = welt->lookup_kartenboden(end + koord(1, 1))) { bd_recalc->check_update_underground(); } } else { // err and not a good starting position return false; } } bridge_builder_t::build_bridge(NULL, bd->get_pos(), bd_next->get_pos(), zv, bridge_height, bridge, welt->get_city_road()); build_road( end+zv, NULL, false); // try to build a house near the bridge end uint32 old_count = buildings.get_count(); for(uint8 i=0; iis_within_limits(c)) { build_city_building(end+zv+koord::neighbours[i]); } } } } // we could build something return connection_roads; } // build a piece of straight road in front of a townhall and take care of flattening the ground etc bool stadt_t::test_and_build_cityroad(koord start, koord end) { if (start == end) { return build_road(start, NULL, true); } const sint16 length = koord_distance(start, end); const koord dir = (end - start) / length; if (dir.x + dir.y != 1) { // was a diagonal return false; } minivec_tplheights(length); for (sint8 i = 0; i <= length; i++) { grund_t* gr = welt->lookup_kartenboden(start + dir * i); if (!gr || gr->get_typ() != grund_t::boden) { return false; } heights.append(gr->get_hoehe()); if (i > 0 && abs(heights[i - 1] - heights[i]) > 2) { return false; } } // now we have to planarise the slopes to have a way for (sint8 i = 0; i <= length; i++) { grund_t* gr = welt->lookup_kartenboden(start +dir*i); if (i < length && heights[i] < heights[i + 1]) { gr->set_grund_hang(slope_type(dir)*(heights[i+1]- heights[i])); gr->calc_image(); } else if (i > 0 && heights[i - 1] > heights[i]) { gr->set_grund_hang(slope_type(-dir)*(heights[i-1] - heights[i])); gr->calc_image(); } else if(gr->get_grund_hang()) { // any other tile: just planarise it gr->set_grund_hang(slope_t::flat); gr->calc_image(); } } // now all slopes should be correct, we can build way_builder_t bauigel(NULL); bauigel.init_builder(way_builder_t::strasse | way_builder_t::terraform_flag, welt->get_city_road(), NULL, NULL); bauigel.set_build_sidewalk(true); if (bauigel.calc_straight_route(welt->lookup_kartenboden(start)->get_pos(), welt->lookup_kartenboden(end)->get_pos())) { return false; } bauigel.build(); return true; } // will check a single random pos in the city, then build will be called void stadt_t::build() { const koord k = koord(lo + koord::koord_random(ur.x - lo.x, ur.y - lo.y)); // do not build on any border tile if( !welt->is_within_limits(k+koord(1,1)) || k.x<=0 || k.y<=0 ) { return; } // ATTENTION: the building position IS NOT this position; the is merely where the rule search starts // since only a single location is checked, we can stop after we have found a positive rule best_strasse.reset(k); const uint32 num_road_rules = road_rules.get_count(); uint32 offset = simrand(num_road_rules); // start with random rule for (uint32 i = 0; i < num_road_rules && !best_strasse.found(); i++) { uint32 rule = ( i+offset ) % num_road_rules; bewerte_strasse(k, 8 + road_rules[rule]->chance, *road_rules[rule]); } // ok => then built road if (best_strasse.found()) { build_road(best_strasse.get_pos(), NULL, false); INT_CHECK("simcity 1156"); return; } // since only a single location is checked, we can stop after we have found a positive rule best_haus.reset(k); const uint32 num_house_rules = house_rules.get_count(); offset = simrand(num_house_rules); // start with random rule for( uint32 i = 0; i < num_house_rules && !best_haus.found(); i++ ) { uint32 rule = ( i+offset ) % num_house_rules; bewerte_haus(k, 8 + house_rules[rule]->chance, *house_rules[rule]); } // one rule applied? if( best_haus.found() ) { if (grund_t* gr = welt->lookup_kartenboden(best_haus.get_pos())) { if (gr->ist_natur()) { build_city_building(best_haus.get_pos()); INT_CHECK("simcity 1163"); return; } } } // renovation (not done every step) if (!buildings.empty() && simrand(100) <= renovation_percentage) { // try to find a public owned building for (uint8 i = 0; i < 4; i++) { gebaeude_t* gb = pick_any(buildings); if (player_t::check_owner(gb->get_owner(), NULL)) { if (gb->get_tile()->get_offset() != koord(0, 0)) { // go to tile origin to make sure we replace all tiles of a multitle building grund_t* gr = welt->lookup_kartenboden(gb->get_pos().get_2d() - gb->get_tile()->get_offset()); gb = gr->find(); } if (renovate_city_building(gb)) { INT_CHECK("simcity 876"); return; } } } } } // find suitable places for cities vector_tpl* stadt_t::random_place(const sint32 count, sint16 old_x, sint16 old_y) { int cl = 0; for (int i = 0; i < MAX_CLIMATES; i++) { if (hausbauer_t::get_special(0, building_desc_t::townhall, welt->get_timeline_year_month(), false, (climate)i)) { cl |= (1 << i); } } DBG_DEBUG("karte_t::init()", "get random places in climates %x", cl); // search at least places which are 5x5 squares large slist_tpl* list = welt->find_squares( 5, 5, (climate_bits)cl, old_x, old_y); DBG_DEBUG("karte_t::init()", "found %i places", list->get_count()); vector_tpl* result = new vector_tpl(count); // pre processed array: max 1 city from each square can be built // each entry represents a cell of minimum_city_distance/2 length and width const uint32 minimum_city_distance = welt->get_settings().get_minimum_city_distance(); const uint32 xmax = (2*welt->get_size().x)/minimum_city_distance+1; const uint32 ymax = (2*welt->get_size().y)/minimum_city_distance+1; array2d_tpl< vector_tpl > places(xmax, ymax); while (!list->empty()) { const koord k = list->remove_first(); places.at( (2*k.x)/minimum_city_distance, (2*k.y)/minimum_city_distance).append(k); } // weighted index vector into places array weighted_vector_tpl index_to_places(xmax*ymax); for(uint32 i=0; i const& p = places.at(i, j); if (!p.empty()) { index_to_places.append(koord(i,j), p.get_count()); } } } // post-processing array: // each entry represents a cell of minimum_city_distance length and width // to limit the search for neighboring cities const uint32 xmax2 = welt->get_size().x/minimum_city_distance+1; const uint32 ymax2 = welt->get_size().y/minimum_city_distance+1; array2d_tpl< vector_tpl > result_places(xmax2, ymax2); for (int i = 0; i < count; i++) { // check distances of all cities to their respective neighbours while (!index_to_places.empty()) { // find a random cell koord const ip = pick_any_weighted(index_to_places); // remove this cell from index list index_to_places.remove(ip); vector_tpl& p = places.at(ip); // get random place in the cell if (p.empty()) continue; uint32 const j = simrand(p.get_count()); koord const k = p[j]; const koord k2mcd = koord( k.x/minimum_city_distance, k.y/minimum_city_distance ); for (sint32 i = k2mcd.x - 1; i <= k2mcd.x + 1; ++i) { for (sint32 j = k2mcd.y - 1; j <= k2mcd.y + 1; ++j) { if (i>=0 && i<(sint32)xmax2 && j>=0 && j<(sint32)ymax2) { for(koord const& l : result_places.at(i, j)) { if (koord_distance(k, l) < minimum_city_distance) { goto too_close; } } } } } // all cities are far enough => ok, find next place result->append(k); result_places.at(k2mcd).append(k); break; too_close: // remove the place from the list p.remove_at(j); // re-insert in index list with new weight if (!p.empty()) { index_to_places.append(ip, p.get_count()); } // if we reached here, the city was not far enough => try again } if (index_to_places.empty() && i < count - 1) { dbg->warning("stadt_t::random_place()", "Not enough places found!"); break; } } delete list; return result; } simutrans-124.5/src/simutrans/world/simcity.h000066400000000000000000000464241520675460500213730ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef WORLD_SIMCITY_H #define WORLD_SIMCITY_H #include "../obj/simobj.h" #include "../obj/gebaeude.h" #include "../tpl/vector_tpl.h" #include "../tpl/weighted_vector_tpl.h" #include "../tpl/sparse_tpl.h" #include "../utils/plainstring.h" #include class building_desc_t; class karte_ptr_t; class player_t; class rule_t; #define MAX_CITY_HISTORY_YEARS (12) // number of years to keep history #define MAX_CITY_HISTORY_MONTHS (12) // number of months to keep history #define PAX_DESTINATIONS_SIZE (256) // size of the minimap (sparse array) enum city_cost { HIST_CITIZENS = 0, // total people HIST_GROWTH, // growth (just for convenience) HIST_BUILDING, // number of buildings HIST_CITYCARS, // number of citycars generated HIST_PAS_TRANSPORTED, // number of passengers who could start their journey HIST_PAS_WALKED, // direct transfer HIST_PAS_GENERATED, // total number generated HIST_MAIL_TRANSPORTED, // letters that could be sent HIST_MAIL_WALKED, // direct handover HIST_MAIL_GENERATED, // all letters generated HIST_GOODS_RECEIVED, // times all storages were not empty HIST_GOODS_NEEDED, // times storages checked HIST_POWER_RECEIVED, // power consumption (not used at the moment!) MAX_CITY_HISTORY // Total number of items in array }; // The base offset for passenger statistics. static const uint32 HIST_BASE_PASS = HIST_PAS_TRANSPORTED; // The base offset for mail statistics. static const uint32 HIST_BASE_MAIL = HIST_MAIL_TRANSPORTED; // The offset for transported statistic for passengers and mail. static const uint32 HIST_OFFSET_TRANSPORTED = 0; // The offset for walked statistic for passengers and mail. static const uint32 HIST_OFFSET_WALKED = 1; // The offset for generated statistic for passengers and mail. static const uint32 HIST_OFFSET_GENERATED = 2; // The number of growth factors kept track of. static const uint32 GROWTH_FACTOR_NUMBER = 3; // Mail return multiplier used by mail producers (attractions and factories). static const uint32 MAIL_RETURN_MULTIPLIER_PRODUCERS = 3; // Passenger generation ratio. This many parts to mail generation ratio parts. static const uint32 GENERATE_RATIO_PASS = 3; // Mail generation ratio. This many parts to passenger generation ratio parts. static const uint32 GENERATE_RATIO_MAIL = 1; /// When a passenger attempts to travel from a city, /// this is the reachability status of the desination /// (as shown in the city info window) /// @sa haltestelle_t::routing_result_flags enum pax_dest_status_t : uint8 { PAX_DEST_STATUS_REACHABLE = 1, PAX_DEST_STATUS_ROUTE_OVERCROWDED = 2, PAX_DEST_STATUS_NO_ROUTE = 3 }; static const uint8 pax_dest_status_colors[4] = { 0, COL_YELLOW, // OK/reachable COL_ORANGE, // route overcrowded COL_DARK_ORANGE, // no route }; /** * Die Objecte der Klasse stadt_t bilden die Staedte in Simu. Sie * wachsen automatisch. */ class stadt_t { /** * Kleine Hilfsklasse - speichert die beste Bewertung einer Position. */ class best_t { sint32 best_wert; koord best_pos; public: void reset(koord pos) { best_wert = 0; best_pos = pos; } void check(koord pos, sint32 wert) { if(wert > best_wert) { best_wert = wert; best_pos = pos; } } bool found() const { return best_wert > 0; } koord get_pos() const { return best_pos;} // sint32 get_wert() const { return best_wert; } }; public: /** * Reads city configuration data from config/cityrules.tab */ static bool cityrules_init(); /** * Reads/writes city configuration data from/to a savegame * called from settings_t::rdwr * only written for networkgames */ static void cityrules_rdwr(loadsave_t *file); static uint32 get_industry_increase(); static void set_industry_increase(uint32 ind_increase); static uint32 get_minimum_city_distance(); static void set_minimum_city_distance(uint32 s); static void set_cluster_factor( uint32 factor ) { stadt_t::cluster_factor = factor; } static uint32 get_cluster_factor() { return stadt_t::cluster_factor; } private: static karte_ptr_t welt; player_t *owner; plainstring name; weighted_vector_tpl buildings; sparse_tpl pax_destinations_old; sparse_tpl pax_destinations_new; // this counter will increment by one for every change => dialogs can question, if they need to update map uint32 pax_destinations_new_change; koord pos; // Gruendungsplanquadrat der City koord townhall_road; // road in front of townhall koord lo, ur; // max size of housing area koord last_center; bool has_high_density; // in this case extend borders by two bool allow_citygrowth; // town can be static and will grow (true by default) bool has_townhall; // this counter indicate which building will be processed next uint32 step_count; /** * step so every house is asked once per month * i.e. 262144/(number of houses) per step */ uint32 step_interval; /** * next passenger generation timer */ uint32 next_step; /** * in this fixed interval, construction will happen */ static const uint32 city_growth_step; /** * When to do growth next */ uint32 next_growth_step; static uint32 cluster_factor; // attribute for the population (Bevoelkerung) sint32 bev; // total population (bevoelkerung) sint32 arb; // with a job (arbeit) sint32 won; // with a residence (wohnung) /** * Un-supplied city growth needs * A value of 2^32 means 1 new resident */ sint64 unsupplied_city_growth; /** * City history * Current month stats are not appropiate to determine satisfaction for growth. */ sint64 city_history_year[MAX_CITY_HISTORY_YEARS][MAX_CITY_HISTORY]; sint64 city_history_month[MAX_CITY_HISTORY_MONTHS][MAX_CITY_HISTORY]; /* updates the city history */ void roll_history(); /* Members used to determine satisfaction for growth rate. * Satisfaction of this month cannot be used as it is an averaging filter for the entire month up to the present. * Instead the average over a number of growth ticks is used, defaulting to last month average if nothing is available. */ private: // The growth factor type in form of the amount demanded and what was received. struct city_growth_factor_t { // The wanted value. sint64 demand; // The received value. sint64 supplied; city_growth_factor_t() : demand(0), supplied(0){} }; // The previous values of the growth factors. Used to get delta between ticks and must be saved for determinism. city_growth_factor_t city_growth_factor_previous[GROWTH_FACTOR_NUMBER]; /* Method to generate comparable growth factor data. * This allows one to alter the logic which computes growth. * @param factors factor array. * @param month the month which is to be used for the growth factors. */ void city_growth_get_factors(city_growth_factor_t (&factors)[GROWTH_FACTOR_NUMBER], uint32 const month) const; /* Method to compute base growth using growth factors. * Logs differences in growth factors as well. * rprec : The returned fractional precision (out of sint32). * cprec : The computation fractional precision (out of sint32). */ sint32 city_growth_base(uint32 const rprec = 6, uint32 const cprec = 16); /* Method to roll previous growth factors at end of month, called before history rolls over. * Needed to prevent loss of data (not set to 0) and while keeping reasonable (no insane values). * month : The month index of what is now the "last month". */ void city_growth_monthly(uint32 const month); public: /** * Returns pointer to history for city */ sint64* get_city_history_year() { return *city_history_year; } sint64* get_city_history_month() { return *city_history_month; } const sint64* get_city_history_year() const { return *city_history_year; } const sint64* get_city_history_month() const { return *city_history_month; } uint32 stadtinfo_options; /* end of history related things */ private: sint32 best_haus_wert; sint32 best_strasse_wert; best_t best_haus; best_t best_strasse; public: // helper function to check for empty tile bool check_ground_tile_for_house(grund_t* gr, sint8 zpos) const; // returns random tile gebaeude_t* build_city_house(koord3d base_pos, const building_desc_t* h, uint8 rotation, uint32 cl, vector_tpl* exclude_desc); /** * Classes for storing and manipulating target factories and their data */ struct factory_entry_t { union { fabrik_t *factory; struct { sint16 factory_pos_x; sint16 factory_pos_y; }; }; sint32 demand; // amount demanded by the factory; shifted by DEMAND_BITS sint32 supply; // amount that the city can supply sint32 remaining; // portion of supply which has not realised yet; remaining <= supply factory_entry_t() : factory(NULL), demand(0), supply(0), remaining(0) { } factory_entry_t(fabrik_t *_factory) : factory(_factory), demand(0), supply(0), remaining(0) { } factory_entry_t(fabrik_t *_factory, sint32 _demand) : factory(_factory), demand(_demand), supply(0), remaining(0) { } bool operator == (const factory_entry_t &other) const { return ( this->factory==other.factory ); } void new_month() { supply = 0; remaining = 0; } void rdwr(loadsave_t *file); void resolve_factory(); }; #define RATIO_BITS (25) struct factory_set_t { vector_tpl entries; sint32 total_demand; // shifted by DEMAND_BITS sint32 total_remaining; sint32 total_generated; uint32 generation_ratio; bool ratio_stale; factory_set_t() : total_demand(0), total_remaining(0), total_generated(0), generation_ratio(0), ratio_stale(true) { } const vector_tpl& get_entries() const { return entries; } const factory_entry_t* get_entry(const fabrik_t *const factory) const; factory_entry_t* get_random_entry(); void update_factory(fabrik_t *const factory, const sint32 demand); void remove_factory(fabrik_t *const factory); void recalc_generation_ratio(const sint32 default_percent, const sint64 *city_stats, const int stats_count, const int stat_type); void new_month(); void rdwr(loadsave_t *file); void resolve_factories(); }; private: /** * Data of target factories for pax/mail */ factory_set_t target_factories_pax; factory_set_t target_factories_mail; /** * Initialization of pax_destinations_old/new */ void init_pax_destinations(); /** * Recalculates city borders (after loading and deletion). * @warning Do not call this during multithreaded loading! */ void recalc_city_size(); // calculates the growth rate for next growth_interval using all the different indicators void calc_growth(); /** * Build new buildings when growing city */ void step_grow_city(bool new_town = false); enum pax_return_type { no_return, factory_return, tourist_return, city_return }; /** * verteilt die Passagiere auf die Haltestellen */ void step_passagiere(); /** * ein Passagierziel in die Zielkarte eintragen */ void merke_passagier_ziel(koord ziel, pax_dest_status_t status); /** * baut Spezialgebaeude, z.B Stadion */ void check_bau_spezial(bool); /** * baut ein angemessenes Rathaus */ void check_bau_townhall(bool new_town, const building_desc_t* th, sint16 rotation); /** * constructs a new consumer */ void check_bau_factory(bool); // find out, what building matches best and return offset, if larger area needs offset // extend_y : swap axis from max_size koord evaluate_size_res_com_ind(const koord pos, int& ind_score, int& com_score, int& res_score, sint16& max_area, uint8& rotations, uint32& neighbor_building_clusters, vector_tpl& exclude); /** * Builds a city building at Planquadrat (tile) x,y */ void build_city_building(koord pos); /** * Renovates a city building at Planquadrat (tile) x,y * true, if somethign was renovated */ bool renovate_city_building(gebaeude_t *gb); #ifdef DESTINATION_CITYCARS sint16 number_of_cars; // allowed number of cars to spawn per month void generate_private_cars(koord pos, koord target); #endif public: /** * builds a piece of road * * @param k tile to build * @param player_ player * @param forced */ bool build_road(const koord k, player_t *player_, bool forced); // build a piece of straight road in front of a townhall and take care of flattening the ground etc bool test_and_build_cityroad(koord start, koord end); private: void build(); /** * @param pos position to check * @param regel the rule to evaluate * @param rotation * @return true on match, false otherwise */ static bool bewerte_loc(koord pos, const rule_t ®el, int rotation); /** * Check rule in all transformations at given position */ static sint32 bewerte_pos(koord pos, const rule_t ®el); void bewerte_strasse(koord pos, sint32 rd, const rule_t ®el); void bewerte_haus(koord pos, sint32 rd, const rule_t ®el); public: bool is_within_players_network( const player_t* player ) const; /// Connects factories to this city. void verbinde_fabriken(); /** * Returns the data set associated with the pax/mail target factories */ const factory_set_t& get_target_factories_for_pax() const { return target_factories_pax; } const factory_set_t& get_target_factories_for_mail() const { return target_factories_mail; } factory_set_t& access_target_factories_for_pax() { return target_factories_pax; } factory_set_t& access_target_factories_for_mail() { return target_factories_mail; } // calculated the "best" orietation of city buildings, also used by editor, thus public static int orient_city_building(const koord k, const building_desc_t *h, koord maxarea ); // this function removes houses from the city house list // (called when removed by player, or by town) void remove_gebaeude_from_stadt(gebaeude_t *gb); /** * This function adds houses to the city house list. * * @param gb building * @param ordered true for multithreaded loading, will insert buidings ordered, will not update city limits */ void add_gebaeude_to_stadt(const gebaeude_t *gb, bool ordered=false); // changes the weight; must be called if there is a new definition (tile) for that house void update_gebaeude_from_stadt(gebaeude_t *gb); /** * Returns the finance history for cities */ sint64 get_finance_history_year(int year, int type) { return city_history_year[year][type]; } sint64 get_finance_history_month(int month, int type) { return city_history_month[month][type]; } // growth number (smoothed!) sint32 get_wachstum() const {return ((sint32)city_history_month[0][HIST_GROWTH]*5) + (sint32)(city_history_month[1][HIST_GROWTH]*4) + (sint32)city_history_month[2][HIST_GROWTH]; } /** * ermittelt die Einwohnerzahl der City */ sint32 get_einwohner() const { const sint32 pop_from_buildings = 6 * (sint32)buildings.get_sum_weight(); const sint32 pop_from_citizens = (2 * bev - arb - won) / 2; const sint32 pop = pop_from_buildings + pop_from_citizens; return std::max(pop, 0); } uint32 get_buildings() const { return buildings.get_count(); } sint32 get_unemployed() const { return bev - arb; } sint32 get_homeless() const { return bev - won; } const char *get_name() const { return name; } void set_name( const char *name ); /// @returns a random point within city borders. koord get_zufallspunkt() const; /// @returns passenger destination statistics for the last month const sparse_tpl &get_pax_destinations_old() const { return pax_destinations_old; } /// @returns passenger destination statistics for the current month const sparse_tpl &get_pax_destinations_new() const { return pax_destinations_new; } /* this counter will increment by one for every change * => dialogs can question, if they need to update map */ uint32 get_pax_destinations_new_change() const { return pax_destinations_new_change; } /** * Creates a new city on grid square (x,y) that belongs to player sp. * * * @param player The owner of the city * @param pos Planquadratkoordinate * @param citizens number of citizens * @param th townhall object * @param rotation */ stadt_t(player_t* player, koord pos, sint32 citizens, const building_desc_t* th = NULL, sint16 rotation = -1); /** * Erzeugt eine neue City nach Angaben aus der Datei file. * * @param file Zeiger auf die Datei mit den Citybaudaten. * @see stadt_t::speichern() */ stadt_t(loadsave_t *file); // closes window and that stuff ~stadt_t(); /** * Speichert die Daten der City in der Datei file so, dass daraus * die City wieder erzeugt werden kann. Die Gebaude und strassen der * City werden nicht mit der City gespeichert sondern mit den * Planquadraten auf denen sie stehen. * * @see stadt_t::stadt_t() * @see planquadrat_t */ void rdwr(loadsave_t *file); /** * Called when loading of savegame is finished to correctly init data. */ void finish_rd(); void rotate90( const sint16 y_size ); /* change size of city */ void change_size( sint64 delta_citizens, bool new_town = false ); // when ng is false, no town growth any more void set_citygrowth_yesno( bool ng ) { allow_citygrowth = ng; } bool get_citygrowth() const { return allow_citygrowth; } void step(uint32 delta_t); void new_month( bool recalc_destinations ); private: /** * List of target cities weighted by both city size and distance */ weighted_vector_tpl target_cities; /** * List of target attractions weighted by both passenger level and distance */ weighted_vector_tpl target_attractions; public: /** * Functions for manipulating the list of target cities */ void add_target_city(stadt_t *const city); void remove_target_city(stadt_t *const city) { target_cities.remove( city ); } void recalc_target_cities(); /** * Functions for manipulating the list of target attractions */ void add_target_attraction(gebaeude_t *const attraction); void remove_target_attraction(gebaeude_t *const attraction) { target_attractions.remove( attraction ); } void recalc_target_attractions(); /** * Search for a possible Passenger or Mail destination. * * @param target_factories the factory set to use (eg passenger or mail). * @param generated number of passengers already generated, used to fairly distribute to factories. * @param will_return set to the jounrey return type on return. * @param factory_entry set to the destination factory, if any. * @param dest_city set to the destination city for return flow use. */ koord find_destination(factory_set_t &target_factories, const sint64 generated, pax_return_type* will_return, factory_entry_t* &factory_entry, stadt_t* &dest_city); /** * Gibt die Gruendungsposition der City zurueck. * @return die Koordinaten des Gruendungsplanquadrates */ inline koord get_pos() const {return pos;} inline koord get_townhall_road() const {return townhall_road;} inline koord get_linksoben() const { return lo;} inline koord get_rechtsunten() const { return ur;} koord get_center() const { return lo/2 + ur/2; } /** * Generates an array of random coordinates suitable for creating cities. * Do not consider coordinates in (0,0) - (old_x, old_y) * (leave @p old_x and @p old_y 0 to generate cities on the whole map). * * @param count how many cities to generate * @param old_x * @param old_y */ static vector_tpl *random_place(sint32 count, sint16 old_x, sint16 old_y); void open_info_window(); }; #endif simutrans-124.5/src/simutrans/world/simplan.cc000066400000000000000000000627571520675460500215220ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "../simdebug.h" #include "../obj/simobj.h" #include "../simfab.h" #include "../display/simgraph.h" #include "../tool/simmenu.h" #include "simplan.h" #include "simworld.h" #include "../simhalt.h" #include "../player/simplay.h" #include "../simconst.h" #include "../macros.h" #include "../descriptor/ground_desc.h" #include "../ground/grund.h" #include "../ground/boden.h" #include "../ground/fundament.h" #include "../ground/wasser.h" #include "../ground/tunnelboden.h" #include "../ground/brueckenboden.h" #include "../ground/monorailboden.h" #include "../obj/gebaeude.h" #include "../dataobj/loadsave.h" #include "../dataobj/environment.h" #include "../gui/minimap.h" karte_ptr_t planquadrat_t::welt; void swap(planquadrat_t& a, planquadrat_t& b) { // since sim::swap down not work for bitfields, we brute force copy memory char tmp[sizeof(planquadrat_t)]; memcpy(tmp, (void *)&a, sizeof(planquadrat_t)); memcpy((void *)&a, (void *)&b, sizeof(planquadrat_t)); memcpy((void *)&b, tmp, sizeof(planquadrat_t)); } // deletes also all grounds in this array! planquadrat_t::~planquadrat_t() { if(ground_size==0) { // empty } else if(ground_size==1) { delete data.one; } else { while(ground_size>0) { ground_size --; delete data.some[ground_size]; data.some[ground_size] = 0; } delete [] data.some; } delete [] halt_list; halt_list_count = 0; // to avoid access to this tile ground_size = 0; data.one = NULL; } grund_t *planquadrat_t::get_boden_von_obj(obj_t *obj) const { if(ground_size==1) { if(data.one && data.one->obj_ist_da(obj)) { return data.one; } } else { for(uint8 i=0; iobj_ist_da(obj)) { return data.some[i]; } } } return NULL; } void planquadrat_t::boden_hinzufuegen(grund_t *bd) { assert(!bd->ist_karten_boden()); if(ground_size==0) { // completely empty data.one = bd; ground_size = 1; minimap_t::get_instance()->calc_map_pixel(bd->get_pos().get_2d()); return; } else if(ground_size==1) { // needs to convert to array // assert(data.one->get_hoehe()!=bd->get_hoehe()); if(data.one->get_hoehe()==bd->get_hoehe()) { DBG_MESSAGE("planquadrat_t::boden_hinzufuegen()","addition ground %s at (%i,%i,%i) will be ignored!",bd->get_name(),bd->get_pos().x,bd->get_pos().y,bd->get_pos().z); return; } grund_t **tmp = new grund_t *[2]; tmp[0] = data.one; tmp[1] = bd; data.some = tmp; ground_size = 2; minimap_t::get_instance()->calc_map_pixel(bd->get_pos().get_2d()); return; } else { // insert into array uint8 i; for(i=1; iget_hoehe()>=bd->get_hoehe()) { break; } } if(iget_hoehe()==bd->get_hoehe()) { DBG_MESSAGE("planquadrat_t::boden_hinzufuegen()","addition ground %s at (%i,%i,%i) will be ignored!",bd->get_name(),bd->get_pos().x,bd->get_pos().y,bd->get_pos().z); return; } bd->set_kartenboden(false); if (ground_size == MAX_PLAN_SIZE) { dbg->fatal("", "Maximum %d grounds at %s exhausted", MAX_PLAN_SIZE, data.some[0]->get_pos().get_2d().get_str()); } // extend array if needed grund_t **tmp = new grund_t *[ground_size+1]; for( uint8 j=0; jcalc_map_pixel(bd->get_pos().get_2d()); } } bool planquadrat_t::boden_entfernen(grund_t *bd) { assert(!bd->ist_karten_boden() && ground_size>0); if(ground_size==1) { ground_size = 0; data.one = NULL; return true; } else { for(uint8 i=0; iset_kartenboden(true); } if (!startup) { // water tiles need neighbor tiles, which might not be initialized at startup bd->calc_image(); } minimap_t::get_instance()->calc_map_pixel(bd->get_pos().get_2d()); } /** * replaces the map solid ground (or water) and deletes the old one */ void planquadrat_t::boden_ersetzen(grund_t *alt, grund_t *neu) { assert(alt!=NULL && neu!=NULL && !alt->is_halt() ); if(ground_size<=1) { assert(data.one==alt || ground_size==0); data.one = neu; ground_size = 1; neu->set_kartenboden(true); } else { for(uint8 i=0; iset_kartenboden(i==0); break; } } } // transfer text and delete if(alt) { if(alt->get_flag(grund_t::has_text)) { neu->set_flag(grund_t::has_text); alt->clear_flag(grund_t::has_text); } // transfer all objects while( alt->obj_count()>0 ) { obj_t* obj = alt->obj_remove_top(); obj->set_pos(neu->get_pos()); neu->obj_add(obj); } // transfer way flags if(alt->get_flag(grund_t::has_way1)) { neu->set_flag(grund_t::has_way1); } if(alt->get_flag(grund_t::has_way2)) { neu->set_flag(grund_t::has_way2); } delete alt; } } void planquadrat_t::rdwr(loadsave_t *file, koord pos ) { xml_tag_t p( file, "planquadrat_t" ); if(file->is_saving()) { if(ground_size==1) { file->wr_obj_id(data.one->get_typ()); data.one->rdwr(file); } else { for(int i=0; iwr_obj_id(data.some[i]->get_typ()); data.some[i]->rdwr(file); } } file->wr_obj_id(-1); } else { grund_t *gr; sint8 hgt = welt->get_groundwater(); //DBG_DEBUG("planquadrat_t::rdwr()","Reading boden"); do { short gtyp = file->rd_obj_id(); switch(gtyp) { case -1: gr = NULL; break; case grund_t::boden: gr = new boden_t(file, pos); break; case grund_t::wasser: gr = new wasser_t(file, pos); break; case grund_t::fundament: gr = new fundament_t(file, pos); break; case grund_t::tunnelboden: gr = new tunnelboden_t(file, pos); break; case grund_t::brueckenboden: gr = new brueckenboden_t(file, pos); break; case grund_t::monorailboden: gr = new monorailboden_t(file, pos); break; default: gr = NULL; // keep compiler happy, fatal() never returns dbg->fatal("planquadrat_t::rdwr()","Error while loading game: Unknown ground type '%d'",gtyp); } // check if we have a matching building here, otherwise set to nothing if (gr && gtyp == grund_t::fundament && gr->find() == NULL) { koord3d pos = gr->get_pos(); // show normal ground here grund_t *neu = new boden_t(pos, 0); if(gr->get_flag(grund_t::has_text)) { neu->set_flag(grund_t::has_text); gr->clear_flag(grund_t::has_text); } // transfer all objects while( gr->obj_count()>0 ) { neu->obj_add( gr->obj_remove_top() ); } delete gr; gr = neu; //DBG_MESSAGE("planquadrat_t::rwdr", "unknown building (or prepare for factory) at %d,%d replaced by normal ground!", pos.x,pos.y); } // we should also check for ground below factories if(gr) { if(ground_size==0) { data.one = gr; ground_size = 1; gr->set_kartenboden(true); hgt = welt->lookup_hgt(pos); } else { boden_hinzufuegen(gr); // other ground must not change the grid height => reset it welt->set_grid_hgt_nocheck( pos, hgt ); } } } while(gr != NULL); // we must always have a kartenboden! if (get_kartenboden() == NULL) { dbg->fatal("planquadrat_t::rdwr", "No kartenboden found for tile at (%s)", pos.get_str()); } } } void planquadrat_t::check_season_snowline(const bool season_change, const bool snowline_change) { if( ground_size == 1 ) { data.one->check_season_snowline( season_change, snowline_change ); } else if( ground_size > 1 ) { for( uint8 i = 0; i < ground_size; i++ ) { data.some[i]->check_season_snowline( season_change, snowline_change ); } } } void planquadrat_t::correct_water() { grund_t *gr = get_kartenboden(); slope_t::type slope = gr->get_grund_hang(); sint8 max_height = gr->get_hoehe() + slope_t::max_diff( slope ); koord k = gr->get_pos().get_2d(); sint8 water_hgt = welt->get_water_hgt(k); if( gr && gr->get_typ() != grund_t::wasser && max_height <= water_hgt ) { // below water but ground => convert kartenboden_setzen( new wasser_t(koord3d( k, water_hgt ) ) ); } else if( gr && gr->get_typ() == grund_t::wasser && max_height > water_hgt ) { // water above ground => to ground kartenboden_setzen( new boden_t(gr->get_pos(), gr->get_disp_slope() ) ); } else if( gr && gr->get_typ() == grund_t::wasser && gr->get_hoehe() != water_hgt ) { // water at wrong height gr->set_hoehe( water_hgt ); } gr = get_kartenboden(); if( gr && gr->get_typ() != grund_t::wasser && gr->get_disp_height() < water_hgt && welt->max_hgt(k) > water_hgt ) { sint8 disp_hneu = water_hgt; sint8 disp_hn_sw = max( gr->get_hoehe() + corner_sw(slope), water_hgt ); sint8 disp_hn_se = max( gr->get_hoehe() + corner_se(slope), water_hgt ); sint8 disp_hn_ne = max( gr->get_hoehe() + corner_ne(slope), water_hgt ); sint8 disp_hn_nw = max( gr->get_hoehe() + corner_nw(slope), water_hgt ); const slope_t::type sneu = encode_corners(disp_hn_sw - disp_hneu, disp_hn_se - disp_hneu, disp_hn_ne - disp_hneu, disp_hn_nw - disp_hneu); gr->set_hoehe( disp_hneu ); gr->set_grund_hang( (slope_t::type)sneu ); } } void planquadrat_t::abgesenkt() { grund_t *gr = get_kartenboden(); if(gr) { const slope_t::type slope = gr->get_grund_hang(); gr->obj_loesche_alle(NULL); sint8 max_hgt = gr->get_hoehe() + (slope!=slope_t::flat ? 1 : 0); // only matters that not flat koord k(gr->get_pos().get_2d()); if( max_hgt <= welt->get_water_hgt( k ) && gr->get_typ() != grund_t::wasser ) { gr = new wasser_t(gr->get_pos()); kartenboden_setzen( gr ); // recalc water ribis of neighbors for(int r=0; r<4; r++) { grund_t *gr2 = welt->lookup_kartenboden(k + koord::nesw[r]); if (gr2 && gr2->is_water()) { gr2->calc_image(); } } } else { minimap_t::get_instance()->calc_map_pixel(k); } gr->set_grund_hang( slope ); } } void planquadrat_t::angehoben() { grund_t *gr = get_kartenboden(); if(gr) { const uint8 slope = gr->get_grund_hang(); gr->obj_loesche_alle(NULL); sint8 max_hgt = gr->get_hoehe() + (slope ? 1 : 0); // only matters that not flat koord k(gr->get_pos().get_2d()); if( max_hgt > welt->get_water_hgt( k ) && gr->get_typ() == grund_t::wasser ) { gr = new boden_t(gr->get_pos(), slope ); kartenboden_setzen( gr ); // recalc water ribis for(int r=0; r<4; r++) { grund_t *gr2 = welt->lookup_kartenboden(k + koord::nesw[r]); if( gr2 && gr2->is_water() ) { gr2->calc_image(); } } } else if( slope == 0 && gr->get_hoehe() == welt->get_water_hgt(k) && gr->get_typ() == grund_t::wasser ) { // water at zero level => make it land gr = new boden_t(gr->get_pos(), slope ); kartenboden_setzen( gr ); // recalc water ribis for(int r=0; r<4; r++) { grund_t *gr2 = welt->lookup_kartenboden(k + koord::nesw[r]); if( gr2 && gr2->is_water() ) { gr2->calc_image(); } } } else { minimap_t::get_instance()->calc_map_pixel(k); } } } void planquadrat_t::display_obj(const sint16 xpos, const sint16 ypos, const sint16 raster_tile_width, bool is_global, const sint8 hmin, const sint8 hmax CLIP_NUM_DEF) const { grund_t *gr0 = get_kartenboden(); if( gr0->get_flag( grund_t::dirty ) ) { gr0->set_all_obj_dirty(); // prevent artifacts with smart hide cursor } const sint8 h0 = gr0->get_disp_height(); uint8 i = 1; // tiles below ground drawing height (tunnels in full underground mode) if( hmin < h0 ) { for( ; i < ground_size; i++ ) { const grund_t* gr = data.some[i]; const sint8 h = gr->get_hoehe(); const sint8 htop = h + slope_t::max_diff( gr->get_grund_hang() ); // above ground if( h > h0 ) { break; } // not too low? if( htop >= hmin ) { const sint16 yypos = ypos - tile_raster_scale_y( (h - h0) * TILE_HEIGHT_STEP, raster_tile_width ); gr->display_boden( xpos, yypos, raster_tile_width CLIP_NUM_PAR ); gr->display_obj_all( xpos, yypos, raster_tile_width, is_global CLIP_NUM_PAR ); } } } //const bool kartenboden_dirty = gr->get_flag(grund_t::dirty); if( gr0->get_flag( grund_t::draw_as_obj ) || !gr0->is_karten_boden_visible() ) { gr0->display_boden( xpos, ypos, raster_tile_width CLIP_NUM_PAR ); } if( env_t::simple_drawing ) { // ignore trees going though bridges gr0->display_obj_all_quick_and_dirty( xpos, ypos, raster_tile_width, is_global CLIP_NUM_PAR ); } else { // clip everything at the next tile above if( i < ground_size ) { // there are grounds above => check height of height object below sint16 max_height = 0; sint16 rw = gfx->get_base_tile_raster_width(); const sint16 clip_h = rw / 2 + (env_t::pak_height_conversion_factor * rw / 8); for (uint8 i = 0; i < gr0->obj_count(); i++) { obj_t* o = gr0->obj_bei(i); if (o->get_typ() == obj_t::baum || dynamic_cast(o)) { image_id img = o->get_image(); if (img != IMG_EMPTY) { const scr_rect area = gfx->get_image_offset(img); max_height = max(max_height, (area.h - area.y) / clip_h); } } } if (max_height == 0) { // display without clipping gr0->display_obj_all(xpos, ypos, raster_tile_width, is_global CLIP_NUM_PAR); } else { // may still need clipping clip_dimension p_cr = gfx->get_clip_rect( CLIP_NUM_VAR ); bool must_pop_clip = false; for( uint8 j = i; j < ground_size; j++ ) { const sint8 h = data.some[j]->get_hoehe(); const sint8 htop = h + slope_t::max_diff(data.some[j]->get_grund_hang()); // still underground if( h < h0 ) { continue; } // too high? if( h > hmax ) { break; } // not too low? if( htop >= hmin) { // something on top: clip horizontally to prevent trees etc shining trough bridges // but not for powerlines or thin elevated ways if (obj_t *o = data.some[j]->obj_bei(0)) { if (!o->is_clipping_below_needed()) { continue; } } if (htop - hmin > max_height) { // lower than max height difference=> stop checking break; } // this needs clipping below must_pop_clip = true; const sint16 yh = ypos - tile_raster_scale_y( (h + corner_nw(data.some[j]->get_grund_hang()) - h0) * TILE_HEIGHT_STEP, raster_tile_width ) + ((3 * raster_tile_width) >> 2); if( yh >= p_cr.y ) { gfx->push_clip_rect(p_cr.x, yh, p_cr.w, p_cr.h + p_cr.y - yh CLIP_NUM_PAR); } break; } } gr0->display_obj_all( xpos, ypos, raster_tile_width, is_global CLIP_NUM_PAR ); if (must_pop_clip) { gfx->pop_clip_rect(CLIP_NUM_VAR); } } } else { gr0->display_obj_all( xpos, ypos, raster_tile_width, is_global CLIP_NUM_PAR ); } } // above ground drawing height for( ; i < ground_size; i++ ) { const grund_t* gr = data.some[i]; const sint8 h = gr->get_hoehe(); const sint8 htop = h + slope_t::max_diff( gr->get_grund_hang() ); // still underground if( h < h0 ) { if( grund_t::underground_mode != grund_t::ugm_level ) { continue; } // in level underground mode we show also the underground slope tiles one level down if( htop < h0 || data.some[0]->get_hoehe() == h0 ) { // but only if there is not just ground above and they would sine through continue; } } // too high? if( h > hmax ) { break; } // not too low? if( htop >= hmin ) { const sint16 yypos = ypos - tile_raster_scale_y( (h - h0) * TILE_HEIGHT_STEP, raster_tile_width ); gr->display_boden( xpos, yypos, raster_tile_width CLIP_NUM_PAR ); gr->display_obj_all( xpos, yypos, raster_tile_width, is_global CLIP_NUM_PAR ); } } } image_id overlay_img(grund_t *gr) { // only transparent outline image_id img; if( gr->get_typ()==grund_t::wasser ) { // water is always flat and does not return proper image_id img = ground_desc_t::outside->get_image(0); } else { img = gr->get_image(); if( img==IMG_EMPTY ) { // foundations or underground mode img = ground_desc_t::get_ground_tile( gr ); } } return img; } void planquadrat_t::display_overlay(const sint16 xpos, const sint16 ypos) const { grund_t *gr=get_kartenboden(); // building transformers - show outlines of factories /* // alternative method of finding selected tool - may be more useful in future but use simpler method for now tool_t *tool = welt->get_tool(welt->get_active_player_nr()); int tool_id = tool->get_id(); if(tool_id==(TOOL_TRANSFORMER|GENERAL_TOOL).... */ if( (grund_t::underground_mode == grund_t::ugm_all || (grund_t::underground_mode == grund_t::ugm_level && gr->get_hoehe() == grund_t::underground_level + welt->get_settings().get_way_height_clearance()) ) && gr->get_typ()==grund_t::fundament && tool_t::general_tool[TOOL_TRANSFORMER]->is_selected()) { gebaeude_t *gb = gr->find(); if(gb) { fabrik_t* fab=gb->get_fabrik(); if(fab) { FLAGGED_PIXVAL status = gfx->palette_lookup(COL_RED); if(fab->get_desc()->is_electricity_producer()) { status = gfx->palette_lookup(COL_LIGHT_BLUE); if(fab->is_transformer_connected()) { status = gfx->palette_lookup(COL_LIGHT_TURQUOISE); } } else { if(fab->is_transformer_connected()) { status = gfx->palette_lookup(COL_ORANGE); } if(fab->get_prodfactor_electric()>0) { status = gfx->palette_lookup(COL_GREEN); } } gfx->draw_img_blend( overlay_img(gr), xpos, ypos, status | OUTLINE_FLAG | TRANSPARENT50_FLAG, 0, true); } } } // display station owner boxes if(env_t::station_coverage_show && halt_list_count>0) { if(env_t::use_transparency_station_coverage) { // only transparent outline image_id img = overlay_img(gr); for(int halt_count = 0; halt_count < halt_list_count; halt_count++) { const FLAGGED_PIXVAL transparent = PLAYER_FLAG | OUTLINE_FLAG | gfx->palette_lookup(halt_list[halt_count]->get_owner()->get_player_color1() + 4); gfx->draw_img_blend( img, xpos, ypos, transparent | TRANSPARENT25_FLAG, 0, 0); } /* // unfortunately, too expensive for display // plot player outline colours - we always plot in order of players so that the order of the stations in halt_list // doesn't affect the colour displayed [since blend(col1,blend(col2,screen)) != blend(col2,blend(col1,screen))] for(int player_count = 0; player_countget_player(player_count); const FLAGGED_PIXVAL transparent = PLAYER_FLAG | OUTLINE_FLAG | gfx->palette_lookup(display_player->get_player_color1() * 4 + 4); for(int halt_count = 0; halt_count < halt_list_count; halt_count++) { if(halt_list[halt_count]->get_owner() == display_player) { display_img_blend( img, xpos, ypos, transparent | TRANSPARENT25_FLAG, 0, 0); } } } */ } else { const sint16 raster_tile_width = gfx->get_tile_raster_width(); // opaque boxes ( const sint16 r=raster_tile_width/8; const sint16 x=xpos+raster_tile_width/2-r; const sint16 y=ypos+(raster_tile_width*3)/4-r - (gr->get_grund_hang()? tile_raster_scale_y(8,raster_tile_width): 0); const bool kartenboden_dirty = gr->get_flag(grund_t::dirty); const sint16 off = (raster_tile_width>>5); // suitable start search for (size_t h = halt_list_count; h-- != 0;) { gfx->draw_rect_clipped(x - h * off, y + h * off, r, r, PLAYER_FLAG | gfx->palette_lookup(halt_list[h]->get_owner()->get_player_color1() + 4), kartenboden_dirty CLIP_NUM_DEFAULT); } } } gr->display_overlay( xpos, ypos ); if( ground_size > 1 ) { const sint16 raster_tile_width = gfx->get_tile_raster_width(); const sint8 h0 = gr->get_disp_height(); for( uint8 i = 1; i < ground_size; i++ ) { grund_t* gr = data.some[i]; const sint8 h = gr->get_disp_height(); const sint16 yypos = ypos - tile_raster_scale_y( (h - h0) * TILE_HEIGHT_STEP, raster_tile_width ); gr->display_overlay( xpos, yypos ); } } } /** * Finds halt belonging to a player * @param player owner of the halts we are interested in. */ halthandle_t planquadrat_t::get_halt(player_t *player) const { for( uint8 i=0; i < get_boden_count(); i++ ) { halthandle_t my_halt = get_boden_bei(i)->get_halt(); if( my_halt.is_bound() && (player == NULL || player == my_halt->get_owner()) ) { return my_halt; } } return halthandle_t(); } /** * The following haltlist routines takes at least 5 bytes of memory per tile but speed up passenger generation a lot */ // these functions are private helper functions for halt_list void planquadrat_t::halt_list_remove( halthandle_t halt ) { for( uint8 i=0; ihalt_list_count!!! void planquadrat_t::halt_list_insert_at( halthandle_t halt, uint8 pos ) { if (halt_list_count == MAX_PLAN_SIZE) { dbg->warning("planquadrat_t::halt_list_insert_at()", "Maximum %d haltlist at %s, kick out last", MAX_PLAN_SIZE, data.some[0]->get_pos().get_2d().get_str()); halt_list_count--; } // extend list? if((halt_list_count%4)==0) { // in the very rare cases of shrinking below 4 and growing again above 4, we do some unneccessary reallocation halthandle_t *tmp = new halthandle_t[halt_list_count+4]; if(halt_list!=NULL) { // now insert for( uint8 i=0; ipos; i-- ) { halt_list[i] = halt_list[i-1]; } halt_list[pos] = halt; halt_list_count ++; } void planquadrat_t::add_to_haltlist(halthandle_t halt, bool unsorted) { if(halt.is_bound()) { if (!unsorted) { // quick and dirty way to our 2d koodinates ... const koord pos = get_kartenboden()->get_pos().get_2d(); if( halt_list_count > 0 ) { // since only the first one gets all, we want the closest halt one to be first halt_list_remove(halt); uint32 dist = koord_distance(halt->get_next_pos(pos), pos); for(unsigned insert_pos=0; insert_posget_next_pos(pos), pos) > dist ) { halt_list_insert_at( halt, insert_pos ); return; } } // not found } // first just or just append to the end ... halt_list_insert_at( halt, halt_list_count ); } else { // insert only if not already present for(uint8 i = 0; i halts(halt_list_count); // sort with respect to distance to pos const koord pos = get_kartenboden()->get_pos().get_2d(); for(uint8 i = 0; iget_next_pos(pos), pos); halt_dist_node n(halt_list[i], dist); halts.insert_unique_ordered(n, halt_dist_node::comp); } // put back into halt_list for(uint8 i = 0; iget_pos().get_2d(); int const cov = welt->get_settings().get_station_coverage(); for (int y = -cov; y <= cov; y++) { for (int x = -cov; x <= cov; x++) { koord test_pos = pos+koord(x,y); const planquadrat_t *pl = welt->access(test_pos); if (pl) { for( uint i = 0; i < pl->get_boden_count(); i++ ) { if ( pl->get_boden_bei(i)->get_halt() == halt ) { // still connected // Reset distance computation add_to_haltlist(halt); } } } } } } /** * true, if this halt is reachable from here */ bool planquadrat_t::is_connected(halthandle_t halt) const { for( uint8 i=0; icheck_update_underground(); // update tunnel tiles for(unsigned int i=1; iist_tunnel()) { gr->check_update_underground(); } } } simutrans-124.5/src/simutrans/world/simplan.h000066400000000000000000000165241520675460500213530ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef WORLD_SIMPLAN_H #define WORLD_SIMPLAN_H #include "../simconst.h" #include "../halthandle.h" #include "../ground/grund.h" #if !defined(MAX_PLAN_SIZE) #if defined(_M_X64) || defined(__x86_64__) #define MAX_PLAN_SIZE 15 #elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) #define MAX_PLAN_SIZE 15 #else #define MAX_PLAN_SIZE 255 #endif #endif class karte_ptr_t; class grund_t; class obj_t; class planquadrat_t; void swap(planquadrat_t& a, planquadrat_t& b); /** * The map (karte_t) consists of map squares (planquadrat_t). * planquadrat_t objects consist of zero or more ground objects (grund_t). * As it is not that often accessed and wasting 6 bytes per field for 64 bit. * This is the only structure we pak and makes the sync_step even faster. * (I guess more cache hits.) * As I have no ARM machine to test if it dies with 32 bit ARM misalignment * no packing is done for ARM for now */ #if defined(__GNUC__) && MAX_PLAN_SIZE==15 class __attribute__((__packed__)) planquadrat_t #else class planquadrat_t #endif { static karte_ptr_t welt; private: /* list of stations that are reaching to this tile (saves lots of time for lookup) */ halthandle_t *halt_list; union DATA { grund_t ** some; // valid if capacity > 1 grund_t * one; // valid if capacity == 1 } data; #if MAX_PLAN_SIZE==15 uint8 ground_size:4; uint8 halt_list_count:4; uint8 climate_data:8; #else uint8 ground_size; uint8 halt_list_count; uint8 climate_data; #endif public: /** * Constructs a planquadrat (tile) with initial capacity of one ground */ planquadrat_t() { ground_size = 0; climate_data = 0; data.one = NULL; halt_list_count = 0; halt_list = NULL; } ~planquadrat_t(); private: planquadrat_t(planquadrat_t const&); planquadrat_t& operator=(planquadrat_t const&); friend void swap(planquadrat_t& a, planquadrat_t& b); public: /** * Setzen des "normalen" Bodens auf Kartenniveau */ void kartenboden_setzen(grund_t *bd, bool startup = false); /// Replaces old ground by new ground, deletes old ground. void boden_ersetzen(grund_t *old_ground, grund_t *new_ground); /// Adds a bridge, tunnel or monorail ground to this map square. void boden_hinzufuegen(grund_t *bd); /// Removes a bridge, tunnel or monorail ground from this map square. bool boden_entfernen(grund_t *bd); /** * Return either ground tile in this height or NULL if not existing * Inline, since called from karte_t::lookup() and thus extremely often * @return NULL if not ground in this height */ inline grund_t *get_boden_in_hoehe(const sint16 z) const { if(ground_size==1) { // must be valid ground at this point! if( data.one->get_hoehe() == z ) { return data.one; } } else { for( uint8 i = 0; i < ground_size; i++ ) { if( data.some[i]->get_hoehe() == z ) { return data.some[i]; } } } return NULL; } /** * returns normal ground (always first index) * @return not defined if no ground (must not happen!) */ inline grund_t *get_kartenboden() const { return (ground_size<=1) ? data.one : data.some[0]; } /** * find ground if thing is on this planquadrat (tile) * @return grund_t * with thing or NULL */ grund_t *get_boden_von_obj(obj_t *obj) const; /** * ground saved at index position idx (zero would be normal ground) * Since it is always called from loops or with other checks, no * range check is done => if only one ground, range is ignored! * @return ground at idx, undefined if ground_size==NULL */ inline grund_t *get_boden_bei(const unsigned idx) const { return (ground_size<=1 ? data.one : data.some[idx]); } /// @returns number of grounds on this map square. unsigned int get_boden_count() const { return ground_size; } /** * returns climate of plan (lowest 3 bits of climate byte) */ inline climate get_climate() const { return (climate)(climate_data & 7); } /** * sets plan climate */ void set_climate(climate cl) { climate_data = (climate_data & 0xf8) + (cl & 7); } /** * returns whether this is a transition to next climate (which will then use calculated image rather than overlay) */ inline bool get_climate_transition_flag() const { return (climate_data >> 3) & 1; } /** * set whether this is a transition to next climate (which will then use calculated image rather than overlay) */ void set_climate_transition_flag(bool flag) { climate_data = flag ? (climate_data | 0x08) : (climate_data & 0xf7); } /** * returns corners which transition to another climate * this has no meaning if tile is a slope with transition to next climate as these corners are fixed * therefore for this case to allow double heights 0 = first level transition, 1 = second level transition */ inline uint8 get_climate_corners() const { return (climate_data >> 4) & 15; } /** * sets climate transition corners * this has no meaning if tile is a slope with transition to next climate as these corners are fixed * therefore for this case to allow double heights 0 = first level transition, 1 = second level transition */ void set_climate_corners(uint8 corners) { climate_data = (climate_data & 0x0f) + (corners << 4); } /** * converts boden to correct type, land or water */ void correct_water(); /** * konvertiert Land zu Water wenn unter Grundwasserniveau abgesenkt */ void abgesenkt(); /** * Converts water to land when raised above the ground water level */ void angehoben(); /** * returns halthandle belonging to player if present */ halthandle_t get_halt(player_t *player) const; private: // these functions are private helper functions for halt_list corrections void halt_list_remove( halthandle_t halt ); void halt_list_insert_at( halthandle_t halt, uint8 pos ); public: /** * The following three functions takes about 4 bytes of memory per tile but speed up passenger generation * * @param halt * @param unsorted if true then halt list will be sorted later by call to sort_haltlist, see karte_t::plans_finish_rd. */ void add_to_haltlist(halthandle_t halt, bool unsorted = false); /** * removes the halt from a ground * however this function check, whether there is really no other part still reachable */ void remove_from_haltlist(halthandle_t halt); /** * sort list of connected halts, ascending wrt distance to this tile */ void sort_haltlist(); bool is_connected(halthandle_t halt) const; /** * returns the internal array of halts */ const halthandle_t *get_haltlist() const { return halt_list; } uint8 get_haltlist_count() const { return halt_list_count; } void rdwr(loadsave_t *file, koord pos ); /** * Updates season and/or snowline dependent graphics */ void check_season_snowline(const bool season_change, const bool snowline_change); void display_obj(const sint16 xpos, const sint16 ypos, const sint16 raster_tile_width, const bool is_global, const sint8 hmin, const sint8 hmax CLIP_NUM_DEF) const; void display_overlay(sint16 xpos, sint16 ypos) const; static void toggle_horizontal_clip(CLIP_NUM_DEF0); /** * @brief Update this square for underground view. * * Updates this square for underground view. This includes calculating * calculating new back will images as well as water depth texture. * * This method does not modify this square object, but does modify the * grounds it references. */ void update_underground() const; }; #endif simutrans-124.5/src/simutrans/world/simworld.cc000066400000000000000000006206151520675460500217100ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include #include #include #include "simcity.h" #include "../simcolor.h" #include "../simconvoi.h" #include "../simdebug.h" #include "../obj/depot.h" #include "../simfab.h" #include "../display/simgraph.h" #include "../display/viewport.h" #include "../simhalt.h" #include "../display/simimg.h" #include "../siminteraction.h" #include "../simintr.h" #include "../simlinemgmt.h" #include "../simloadingscreen.h" #include "../tool/simmenu.h" #include "../simmesg.h" #include "../simskin.h" #include "../simsound.h" #include "../simticker.h" #include "../simunits.h" #include "../simversion.h" #include "../display/simview.h" #include "../tool/simtool-scripted.h" #include "../gui/simwin.h" #include "simworld.h" #include "../sys/simsys.h" #include "../simachievements.h" #include "../tpl/vector_tpl.h" #include "../tpl/binary_heap_tpl.h" #include "../ground/boden.h" #include "../ground/wasser.h" #include "../old_blockmanager.h" #include "../vehicle/vehicle.h" #include "../vehicle/simroadtraffic.h" #include "../vehicle/pedestrian.h" #include "../vehicle/movingobj.h" #include "../obj/way/schiene.h" #include "../obj/zeiger.h" #include "../obj/baum.h" #include "../obj/signal.h" #include "../obj/roadsign.h" #include "../obj/wayobj.h" #include "../obj/groundobj.h" #include "../obj/gebaeude.h" #include "../obj/leitung2.h" #include "../obj/wolke.h" #include "../gui/password_frame.h" #include "../gui/messagebox.h" #include "../gui/help_frame.h" #include "../gui/minimap.h" #include "../gui/player_frame.h" #include "../gui/factorylist_frame.h" #include "../gui/curiositylist_frame.h" #include "../gui/labellist_frame.h" #include "../gui/vehiclelist_frame.h" #include "../network/network.h" #include "../network/network_file_transfer.h" #include "../network/network_socket_list.h" #include "../network/network_cmd_ingame.h" #include "../dataobj/height_map_loader.h" #include "../dataobj/ribi.h" #include "../dataobj/translator.h" #include "../dataobj/loadsave.h" #include "../dataobj/marker.h" #include "../dataobj/scenario.h" #include "../dataobj/settings.h" #include "../dataobj/environment.h" #include "../dataobj/powernet.h" #include "../dataobj/records.h" #include "../dataobj/pakset_manager.h" #include "../utils/cbuffer.h" #include "../utils/simrandom.h" #include "../utils/simstring.h" #include "../network/memory_rw.h" #include "../builder/brueckenbauer.h" #include "../builder/tunnelbauer.h" #include "../builder/fabrikbauer.h" #include "../builder/wegbauer.h" #include "../builder/hausbauer.h" #include "../builder/vehikelbauer.h" #include "../descriptor/ground_desc.h" #include "../descriptor/intro_dates.h" #include "../player/simplay.h" #include "../player/finance.h" #include "../player/ai_passenger.h" #include "../player/ai_goods.h" #include "../player/ai_scripted.h" #include "terraformer.h" #include "../io/rdwr/adler32_stream.h" #include "../pathes.h" #ifdef STEAM_BUILT #include "../../steam/steam.h" #endif // forward declaration - management of rotation for scripting namespace script_api { void rotate90(); void new_world(); }; static uint32 last_clients = -1; static uint8 last_active_player_nr = 0; static std::string last_network_game; karte_t* karte_t::world = NULL; #ifdef MULTI_THREAD #include "../utils/simthread.h" #include bool spawned_world_threads=false; // global job indicator array static simthread_barrier_t world_barrier_start; static simthread_barrier_t world_barrier_end; // to start a thread typedef struct{ karte_t *welt; int thread_num; sint16 x_step; sint16 x_world_max; sint16 y_min; sint16 y_max; sem_t* wait_for_previous; sem_t* signal_to_next; xy_loop_func function; bool keep_running; } world_thread_param_t; // now the parameters static world_thread_param_t world_thread_param[MAX_THREADS]; void *karte_t::world_xy_loop_thread(void *ptr) { world_thread_param_t *param = reinterpret_cast(ptr); bool keep_running = false; do { simthread_barrier_wait( &world_barrier_start ); // wait for all to start sint16 x_min = 0; sint16 x_max = param->x_step; while( x_min < param->x_world_max ) { // wait for predecessor to finish its block if( param->wait_for_previous ) { sem_wait( param->wait_for_previous ); } (param->welt->*(param->function))(x_min, x_max, param->y_min, param->y_max); // signal to next thread that we finished one block if( param->signal_to_next ) { sem_post( param->signal_to_next ); } x_min = x_max; x_max = min(x_max + param->x_step, param->x_world_max); } // the main thread writes to param->keep_running between world_barrier_end and world_barrier_start // so we have to copy the old value keep_running = param->keep_running; simthread_barrier_wait( &world_barrier_end ); // wait for all to finish } while (keep_running); return NULL; } #endif void karte_t::world_xy_loop(xy_loop_func function, uint8 flags) { const bool use_grids = (flags & GRIDS_FLAG) == GRIDS_FLAG; uint16 max_x = use_grids?(cached_grid_size.x+1):cached_grid_size.x; uint16 max_y = use_grids?(cached_grid_size.y+1):cached_grid_size.y; #ifdef MULTI_THREAD set_random_mode( INTERACTIVE_RANDOM ); // do not allow simrand() here! const bool sync_x_steps = (flags & SYNCX_FLAG) == SYNCX_FLAG; // semaphores to synchronize progress in x direction sem_t sems[MAX_THREADS-1]; for( int t = 0; t < env_t::num_threads; t++ ) { if( sync_x_steps && t < env_t::num_threads - 1 ) { sem_init(&sems[t], 0, 0); } world_thread_param[t].welt = this; world_thread_param[t].thread_num = t; world_thread_param[t].x_step = sync_x_steps ? min( 64, max_x / env_t::num_threads ) : max_x; world_thread_param[t].x_world_max = max_x; world_thread_param[t].y_min = (t * max_y) / env_t::num_threads; world_thread_param[t].y_max = ((t + 1) * max_y) / env_t::num_threads; world_thread_param[t].function = function; world_thread_param[t].wait_for_previous = sync_x_steps && t > 0 ? &sems[t-1] : NULL; world_thread_param[t].signal_to_next = sync_x_steps && t < env_t::num_threads - 1 ? &sems[t] : NULL; world_thread_param[t].keep_running = t < env_t::num_threads - 1; } if( !spawned_world_threads ) { // we can do the parallel display using posix threads ... pthread_t thread[MAX_THREADS]; /* Initialize and set thread detached attribute */ pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); // init barrier simthread_barrier_init( &world_barrier_start, NULL, env_t::num_threads ); simthread_barrier_init( &world_barrier_end, NULL, env_t::num_threads ); for( int t = 0; t < env_t::num_threads - 1; t++ ) { if( pthread_create( &thread[t], &attr, world_xy_loop_thread, (void *)&world_thread_param[t] ) ) { dbg->fatal( "karte_t::world_xy_loop()", "cannot multithread, error at thread #%i", t+1 ); } } spawned_world_threads = true; pthread_attr_destroy( &attr ); } // and start processing; the last we can run ourselves world_xy_loop_thread(&world_thread_param[env_t::num_threads-1]); // return from thread for( int t = 0; t < env_t::num_threads - 1; t++ ) { if( sync_x_steps ) { sem_destroy(&sems[t]); } } clear_random_mode( INTERACTIVE_RANDOM ); // do not allow simrand() here! #else // slow serial way of display (this->*function)( 0, max_x, 0, max_y ); #endif } void karte_t::recalc_season_snowline(bool set_pending) { static const sint8 mfactor[12] = { 99, 95, 80, 50, 25, 10, 0, 5, 20, 35, 65, 85 }; static const uint8 month_to_season[12] = { 2, 2, 2, 3, 3, 0, 0, 0, 0, 1, 1, 2 }; // calculate snowline with day precision // use linear interpolation const sint32 ticks_this_month = get_ticks() & (karte_t::ticks_per_world_month - 1); const sint32 factor = mfactor[last_month] + ( ( (mfactor[(last_month + 1) % 12] - mfactor[last_month]) * (ticks_this_month >> 12) ) >> (karte_t::ticks_per_world_month_shift - 12) ); // just remember them const uint8 old_season = season; const sint16 old_snowline = snowline; // and calculate new values season = month_to_season[last_month]; // (2+last_month/3)&3; // summer always zero if( old_season != season && set_pending ) { pending_season_change++; } const sint8 winterline = settings.get_winter_snowline(); const sint8 summerline = max(settings.get_climate_borders(arctic_climate,1),settings.get_climate_borders(arctic_climate,0)); snowline = summerline - (sint8)(((summerline-winterline)*factor)/100); if( old_snowline != snowline && set_pending ) { pending_snowline_change++; } } void karte_t::perlin_hoehe_loop( sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max ) { for( int y = y_min; y < y_max; y++ ) { for( int x = x_min; x < x_max; x++ ) { // loop all tiles koord k(x,y); sint16 const h = perlin_hoehe(&settings, k, koord(0, 0)); set_grid_hgt_nocheck( k, (sint8) h); } } } sint32 karte_t::perlin_hoehe(settings_t const* const sets, koord k, koord const size) { // replace the fixed values with your settings. Amplitude is the top highness of the mountains, // frequency is something like landscape 'roughness'; amplitude may not be greater than 160.0 !!! // please don't allow frequencies higher than 0.8, it'll break the AI's pathfinding. // Frequency values of 0.5 .. 0.7 seem to be ok, less is boring flat, more is too crumbled // the old defaults are given here: f=0.6, a=160.0 switch( sets->get_rotation() ) { // 0: do nothing case 1: k = koord(k.y,size.x-k.x); break; case 2: k = koord(size.x-k.x,size.y-k.y); break; case 3: k = koord(size.y-k.y,k.x); break; } // double perlin_noise_2D(double x, double y, double persistence); // return ((int)(perlin_noise_2D(x, y, 0.6)*160.0)) & 0xFFFFFFF0; k = k + koord(sets->get_origin_x(), sets->get_origin_y()); return ((int)(perlin_noise_2D(k.x, k.y, sets->get_map_roughness())*(double)sets->get_max_mountain_height())) / 16; } void karte_t::cleanup_grounds_loop( sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max ) { for( int y = y_min; y < y_max; y++ ) { for( int x = x_min; x < x_max; x++ ) { planquadrat_t *pl = access_nocheck(x,y); koord k(x,y); slope_t::type slope = calc_natural_slope(k); sint8 height = min_hgt_nocheck(k); sint8 water_hgt = get_water_hgt_nocheck(k); if( height < water_hgt) { const sint8 disp_hn_sw = max( height + corner_sw(slope), water_hgt ); const sint8 disp_hn_se = max( height + corner_se(slope), water_hgt ); const sint8 disp_hn_ne = max( height + corner_ne(slope), water_hgt ); const sint8 disp_hn_nw = max( height + corner_nw(slope), water_hgt ); height = water_hgt; slope = encode_corners(disp_hn_sw - height, disp_hn_se - height, disp_hn_ne - height, disp_hn_nw - height); } if( max_hgt_nocheck(k) <= water_hgt ) { // create water pl->kartenboden_setzen( new wasser_t(koord3d( k, height)), true /* do not calc_image for water tiles */ ); } else { // create ground pl->kartenboden_setzen( new boden_t(koord3d( k, height), slope ) ); } if( max_hgt_nocheck(k) > water_hgt ) { set_water_hgt_nocheck(k, groundwater-4); } } } } void karte_t::cleanup_karte( int xoff, int yoff ) { // we need a copy to smooth the map to a realistic level const sint32 grid_size = (get_size().x+1)*(sint32)(get_size().y+1); sint8 *grid_hgts_cpy = new sint8[grid_size]; memcpy( grid_hgts_cpy, grid_hgts, grid_size ); // the trick for smoothing is to raise each tile by one sint32 i,j; for(j=0; j<=get_size().y; j++) { for(i=j>=yoff?0:xoff; i<=get_size().x; i++) { raise_grid_to(i,j, grid_hgts_cpy[i+j*(get_size().x+1)] + 1); } } delete [] grid_hgts_cpy; // but to leave the map unchanged, we lower the height again for(j=0; j<=get_size().y; j++) { for(i=j>=yoff?0:xoff; i<=get_size().x; i++) { grid_hgts[i+j*(get_size().x+1)] --; } } if( xoff==0 && yoff==0 ) { // world_xy_loop(&karte_t::cleanup_grounds_loop, 0); cleanup_grounds_loop( 0, get_size().x, 0, get_size().y ); } else { cleanup_grounds_loop( 0, get_size().x, yoff, get_size().y ); cleanup_grounds_loop( xoff, get_size().x, 0, yoff ); } } void karte_t::destroy() { is_sound = false; // karte_t::play_sound_area_clipped needs valid zeiger (pointer/drawer) destroying = true; DBG_MESSAGE("karte_t::destroy()", "destroying world"); uint32 max_display_progress = 256+cities.get_count()*10 + haltestelle_t::get_alle_haltestellen().get_count() + convoi_array.get_count() + (cached_size.x*cached_size.y)*2; uint32 old_progress = 0; loadingscreen_t ls( translator::translate("Destroying map ..."), max_display_progress, true ); // rotate the map until it can be saved nosave_warning = false; if( nosave ) { max_display_progress += 256; for( int i=0; i<4 && nosave; i++ ) { DBG_MESSAGE("karte_t::destroy()", "rotating"); rotate90(); } old_progress += 256; ls.set_max( max_display_progress ); ls.set_progress( old_progress ); } if(nosave) { dbg->fatal( "karte_t::destroy()","Map cannot be cleanly destroyed in any rotation!" ); } goods_in_game.clear(); DBG_MESSAGE("karte_t::destroy()", "label clear"); labels.clear(); if(zeiger) { zeiger->set_pos(koord3d::invalid); delete zeiger; zeiger = NULL; } old_progress += 256; ls.set_progress( old_progress ); // removes all moving stuff from the sync_step sync.clear(); sync_buildings.clear(); sync_roadsigns.clear(); old_progress += cached_size.x*cached_size.y; ls.set_progress( old_progress ); DBG_MESSAGE("karte_t::destroy()", "sync list cleared"); // alle convois aufraeumen while (!convoi_array.empty()) { convoihandle_t cnv = convoi_array.back(); cnv->destroy(); old_progress ++; if( (old_progress&0x00FF) == 0 ) { ls.set_progress( old_progress ); } } convoi_array.clear(); DBG_MESSAGE("karte_t::destroy()", "convois destroyed"); // alle haltestellen aufraeumen old_progress += haltestelle_t::get_alle_haltestellen().get_count(); haltestelle_t::destroy_all(); DBG_MESSAGE("karte_t::destroy()", "stops destroyed"); ls.set_progress( old_progress ); // remove all target cities (we can skip recalculation anyway) for(fabrik_t* const f : all_factories) { f->clear_target_cities(); } // delete towns first (will also delete all their houses) // for the next game we need to remember the desired number ... sint32 const no_of_cities = settings.get_city_count(); for( uint32 i=0; !cities.empty(); i++ ) { remove_city(cities.front()); old_progress += 10; if( (i&0x00F) == 0 ) { ls.set_progress( old_progress ); } } settings.set_city_count(no_of_cities); DBG_MESSAGE("karte_t::destroy()", "towns destroyed"); ls.set_progress( old_progress ); // dinge aufraeumen cached_grid_size.x = cached_grid_size.y = 1; cached_size.x = cached_size.y = 0; delete [] plan; plan = NULL; DBG_MESSAGE("karte_t::destroy()", "planquadrat destroyed"); old_progress += (cached_size.x*cached_size.y)/2; ls.set_progress( old_progress ); // gitter aufraeumen delete [] grid_hgts; grid_hgts = NULL; delete [] water_hgts; water_hgts = NULL; // player cleanup for(int i=0; iget_einwohner() == 0) { delete new_city; return NULL; } settings.set_city_count(settings.get_city_count() + 1); cities.append(new_city, new_city->get_einwohner()); // add links between this city and other cities as well as attractions for(stadt_t *city : cities) { city->add_target_city(new_city); } new_city->recalc_target_cities(); new_city->recalc_target_attractions(); return new_city; } bool karte_t::remove_city(stadt_t *s) { if(s == NULL || cities.empty()) { // no town there to delete ... return false; } // reduce number of towns if(s->get_name()) { DBG_MESSAGE("karte_t::remove_city()", "%s", s->get_name()); } cities.remove(s); DBG_DEBUG4("karte_t::remove_city()", "reduce city to %i", settings.get_city_count() - 1); settings.set_city_count(settings.get_city_count() - 1); // remove links between this city and other cities for(stadt_t* const c : cities) { c->remove_target_city(s); } // remove all links from factories DBG_DEBUG4("karte_t::remove_city()", "fab_list %i", all_factories.get_count() ); for(fabrik_t* const f : all_factories) { f->remove_target_city(s); } // ok, we can delete this DBG_MESSAGE("karte_t::remove_city()", "delete" ); delete s; return true; } // just allocates space; void karte_t::init_tiles() { assert(plan==0); uint32 const x = get_size().x; uint32 const y = get_size().y; plan = new planquadrat_t[x * y]; grid_hgts = new sint8[(x + 1) * (y + 1)]; max_height = min_height = 0; MEMZERON(grid_hgts, (x + 1) * (y + 1)); water_hgts = new sint8[x * y]; MEMZERON(water_hgts, x * y); win_set_world( this ); minimap_t::get_instance()->init(); for(int i=0; iclear_speed_records(); // make timer loop invalid for( int i=0; i<32; i++ ) { last_frame_ms[i] = 0x7FFFFFFFu; last_step_nr[i] = 0xFFFFFFFFu; } last_frame_idx = 0; pending_season_change = 0; pending_snowline_change = 0; // init global history for (int year=0; yearwarning("karte_t::create_rivers()","There is no river defined!\n"); return; } // create a vector of the highest points vector_tpl lake_tiles; vector_tpl sea_tiles; weighted_vector_tpl mountain_tiles; sint8 last_height = 1; koord last_koord(0,0); const sint16 max_dist = cached_size.y+cached_size.x; // trunk of 16 will ensure that rivers are long enough apart ... for( sint16 y = 8; y < cached_size.y; y+=16 ) { for( sint16 x = 8; x < cached_size.x; x+=16 ) { koord k(x,y); grund_t *gr = lookup_kartenboden_nocheck(k); const sint8 h = gr->get_hoehe() - get_water_hgt_nocheck(k); if( gr->is_water() ) { // may be good to start a river here if( gr->get_hoehe() <= get_groundwater() ) { sea_tiles.append(k); } else { lake_tiles.append(k); } } else if( h>=last_height || koord_distance(last_koord,k)>simrand(max_dist) ) { // something worth to add here if( h>last_height ) { last_height = h; } last_koord = k; // using h*h as weight would give mountain sources more preferences // on the other hand most rivers do not string near summits ... mountain_tiles.append( k, h ); } } } vector_tpl water_tiles( sea_tiles.empty() ? lake_tiles : sea_tiles ); if (water_tiles.empty()) { dbg->message("karte_t::create_rivers()","There aren't any water tiles!\n"); return; } // now make rivers sint16 retrys = number*2; while( number > 0 && !mountain_tiles.empty() && retrys>0 ) { // start with random coordinates koord const start = pick_any_weighted(mountain_tiles); mountain_tiles.remove( start ); // build a list of matching targets vector_tpl valid_water_tiles; for( uint32 i=0; iget_pos(), lookup_kartenboden(start)->get_pos() ); if( riverbuilder.get_count() >= (uint32)settings.get_min_river_length() ) { // do not built too short rivers riverbuilder.build(); number --; retrys++; break; } } retrys--; } // we gave up => tell the user if( number>0 ) { dbg->warning( "karte_t::create_rivers()","Too many rivers requested! (%i not constructed)", number ); } } void karte_t::distribute_cities(int new_city_count, sint32 new_mean_citizen_count, sint16 old_x, sint16 old_y) { DBG_DEBUG("karte_t::distribute_cities()","prepare cities"); vector_tpl *pos = stadt_t::random_place(new_city_count, old_x, old_y); if( !pos->empty() ) { const sint32 old_city_count = cities.get_count(); new_city_count = pos->get_count(); DBG_DEBUG("karte_t::distribute_cities()", "Creating cities: %d", new_city_count); // if we could not generate enough positions ... settings.set_city_count(old_city_count); int old_progress = 16; loadingscreen_t ls( translator::translate( "distributing cities" ), 16 + 2 * (old_city_count + new_city_count) + 2 * new_city_count + (old_x == 0 ? settings.get_factory_count() : 0), true, true ); { // Loop only new cities: #ifdef DEBUG uint32 tbegin = dr_time(); #endif for( int i=0; iget_city_history_month())[HIST_CITIZENS] ); } } // center on first city if( old_x+old_y == 0 && cities.get_count()>0) { viewport->change_world_position( cities[0]->get_pos() ); } delete pos; #ifdef DEBUG dbg->message("karte_t::distribute_cities()","took %lu ms for all towns", dr_time()-tbegin ); #endif uint32 game_start = current_month; // townhalls available since? for(building_desc_t const* const desc : *hausbauer_t::get_list(building_desc_t::townhall)) { uint32 intro_year_month = desc->get_intro_year_month(); if( intro_year_monthget_intro_year_month() ); uint32 original_start_year = current_month; uint32 original_industry_growth = settings.get_industry_increase_every(); settings.set_industry_increase_every( 0 ); for( uint32 i=old_city_count; ifinish_rd(); // int citizens=(int)(new_mean_citizen_count*0.9); // citizens = citizens/10+simrand(2*citizens+1); const sint32 citizens = (2500l * new_mean_citizen_count) /(simrand(20000)+100); sint32 diff = (original_start_year-game_start)/2; sint32 growth = 32; sint32 current_bev = 1; /* grow gradually while aging * the difference to the current end year will be halved, * while the growth step is doubled */ current_month = game_start; bool not_updated = false; bool new_town = true; while( current_bev < citizens ) { growth = min( citizens-current_bev, growth*2 ); current_bev += growth; cities[i]->change_size( growth, new_town ); // Only "new" for the first change_size call new_town = false; if( current_bev > citizens/2 && not_updated ) { ls.set_progress( ++old_progress ); not_updated = true; } current_month += diff; diff >>= 1; } // the growth is slow, so update here the progress bar ls.set_progress( ++old_progress ); } current_month = original_start_year; settings.set_industry_increase_every( original_industry_growth ); msg->clear(); } finance_history_year[0][WORLD_TOWNS] = finance_history_month[0][WORLD_TOWNS] = cities.get_count(); finance_history_year[0][WORLD_CITIZENS] = finance_history_month[0][WORLD_CITIZENS] = last_month_bev; // connect some cities with roads way_desc_t const* desc = settings.get_intercity_road_type(get_timeline_year_month()); if(desc == 0) { // try some default (might happen with timeline ... ) desc = way_builder_t::weg_search(road_wt,80,get_timeline_year_month(),type_flat); } way_builder_t bauigel(get_public_player()); bauigel.init_builder(way_builder_t::strasse | way_builder_t::terraform_flag, desc, tunnel_builder_t::get_tunnel_desc(road_wt,15,get_timeline_year_month()), bridge_builder_t::find_bridge(road_wt,15,get_timeline_year_month()) ); bauigel.set_keep_existing_ways(true); bauigel.set_maximum(env_t::intercity_road_length); // **** intercity road construction int count = 0; sint32 const n_cities = settings.get_city_count(); int const max_count = n_cities * (n_cities - 1) / 2 - old_city_count * (old_city_count - 1) / 2; // something to do?? if( max_count > 0 ) { // print("Building intercity roads ...\n"); ls.set_max( 16 + 2 * (old_city_count + new_city_count) + 2 * new_city_count + (old_x == 0 ? settings.get_factory_count() : 0) ); // find townhall of city i and road in front of it vector_tpl k; for (int i = 0; i < settings.get_city_count(); ++i) { koord k1(cities[i]->get_townhall_road()); if (lookup_kartenboden(k1) && lookup_kartenboden(k1)->hat_weg(road_wt)) { k.append(lookup_kartenboden(k1)->get_pos()); } else { // look for a road near the townhall gebaeude_t const* const gb = obj_cast(lookup_kartenboden(cities[i]->get_pos())->first_no_way_obj()); bool ok = false; if( gb && gb->is_townhall() ) { koord k_check = cities[i]->get_pos() + koord(-1,-1); const koord size = gb->get_tile()->get_desc()->get_size(gb->get_tile()->get_layout()); koord inc(1,0); // scan all adjacent tiles, take the first that has a road for(sint32 i=0; i<2*size.x+2*size.y+4 && !ok; i++) { grund_t *gr = lookup_kartenboden(k_check); if (gr && gr->hat_weg(road_wt)) { k.append(gr->get_pos()); ok = true; } k_check = k_check + inc; if (i==size.x+1) { inc = koord(0,1); } else if (i==size.x+size.y+2) { inc = koord(-1,0); } else if (i==2*size.x+size.y+3) { inc = koord(0,-1); } } } if (!ok) { k.append( koord3d::invalid ); } } } // compute all distances uint8 conn_comp=1; // current connection component for phase 0 vector_tpl city_flag; // city already connected to the graph? >0 nr of connection component array2d_tpl city_dist(settings.get_city_count(), settings.get_city_count()); for (sint32 i = 0; i < settings.get_city_count(); ++i) { city_dist.at(i,i) = 0; for (sint32 j = i + 1; j < settings.get_city_count(); ++j) { city_dist.at(i,j) = koord_distance(k[i], k[j]); city_dist.at(j,i) = city_dist.at(i,j); // count unbuildable connections to new cities if( j>=old_city_count && city_dist.at(i,j) >= env_t::intercity_road_length ) { count++; } } city_flag.append( i < old_city_count ? conn_comp : 0 ); // progress bar stuff ls.set_progress( 16 + 2 * new_city_count + count * settings.get_city_count() * 2 / max_count ); } // mark first town as connected if (old_city_count==0) { city_flag[0]=conn_comp; } // get a default vehikel route_t verbindung; vehicle_t* test_driver; vehicle_desc_t test_drive_desc(road_wt, 500, vehicle_desc_t::diesel ); test_driver = vehicle_builder_t::build(koord3d(), get_public_player(), NULL, &test_drive_desc); test_driver->set_flag( obj_t::not_on_map ); bool ready=false; uint8 phase=0; // 0 - first phase: built minimum spanning tree (edge weights: city distance) // 1 - second phase: try to complete the graph, avoid edges that // == have similar length then already existing connection // == lead to triangles with an angle >90 deg while( phase < 2 ) { ready = true; koord conn = koord::invalid; sint32 best = env_t::intercity_road_length; if( phase == 0 ) { // loop over all unconnected cities for (int i = 0; i < settings.get_city_count(); ++i) { if( city_flag[i] == conn_comp ) { // loop over all connections to connected cities for (int j = old_city_count; j < settings.get_city_count(); ++j) { if( city_flag[j] == 0 ) { ready=false; if( city_dist.at(i,j) < best ) { best = city_dist.at(i,j); conn = koord(i,j); } } } } } // did we completed a connection component? if( !ready && best == env_t::intercity_road_length ) { // next component conn_comp++; // try the first not connected city ready = true; for( int i = old_city_count; i < settings.get_city_count(); ++i ) { if( city_flag[i] ==0 ) { city_flag[i] = conn_comp; ready = false; break; } } } } else { // loop over all unconnected cities for (int i = 0; i < settings.get_city_count(); ++i) { for (int j = max(old_city_count, i + 1); j < settings.get_city_count(); ++j) { if( city_dist.at(i,j) < best && city_flag[i] == city_flag[j] ) { bool ok = true; // is there a connection i..l..j ? forbid stumpfe winkel for (int l = 0; l < settings.get_city_count(); ++l) { if( city_flag[i] == city_flag[l] && city_dist.at(i,l) == env_t::intercity_road_length && city_dist.at(j,l) == env_t::intercity_road_length ) { // cosine < 0 ? koord3d d1 = k[i]-k[l]; koord3d d2 = k[j]-k[l]; if( d1.x*d2.x + d1.y*d2.y < 0 ) { city_dist.at(i,j) = env_t::intercity_road_length+1; city_dist.at(j,i) = env_t::intercity_road_length+1; ok = false; count ++; break; } } } if(ok) { ready = false; best = city_dist.at(i,j); conn = koord(i,j); } } } } } // valid connection? if( conn.x >= 0 ) { // is there a connection already const bool connected = ( phase==1 && verbindung.calc_route( this, k[conn.x], k[conn.y], test_driver, 0, 0 ) ); // build this connection? bool build = false; // set appropriate max length for way builder if( connected ) { if( 2*verbindung.get_count() > (uint32)city_dist.at(conn) ) { bauigel.set_maximum(verbindung.get_count() / 2); build = true; } } else { bauigel.set_maximum(env_t::intercity_road_length); build = true; } if( build ) { bauigel.calc_route(k[conn.x],k[conn.y]); } if( build && bauigel.get_count() >= 2 ) { bauigel.build(); if (phase==0) { city_flag[ conn.y ] = conn_comp; } // mark as built city_dist.at(conn) = env_t::intercity_road_length; city_dist.at(conn.y, conn.x) = env_t::intercity_road_length; count ++; } else { // do not try again city_dist.at(conn) = env_t::intercity_road_length+1; city_dist.at(conn.y, conn.x) = env_t::intercity_road_length+1; count ++; if( phase == 0 ) { // do not try to connect to this connected component again for( int i = 0; i < settings.get_city_count(); ++i ) { if( city_flag[i] == conn_comp && city_dist.at(i, conn.y) 0 ) { // add eyecandy like rocky, moles, flowers, ... koord k; sint32 queried = simrand(env_t::ground_object_probability*2-1); for( k.y=0; k.yget_typ()==grund_t::boden && !gr->hat_wege() ) { queried --; if( queried<0 ) { // test for beach bool neighbour_water = false; for(int i=0; i<8; i++) { if( is_within_limits(k + koord::neighbours[i]) && get_climate( k + koord::neighbours[i] ) == water_climate ) { neighbour_water = true; break; } } const climate_bits cl = neighbour_water ? water_climate_bit : (climate_bits)(1<get_grund_hang() ); queried = simrand(env_t::ground_object_probability*2-1); if(desc) { gr->obj_add( new groundobj_t( gr->get_pos(), desc ) ); } } } } } } } void karte_t::distribute_movingobjs(sint16 old_x, sint16 old_y) { DBG_DEBUG("karte_t::distribute_movingobj()","distributing movingobjs"); if( env_t::moving_object_probability > 0 ) { // add animals and so on (must be done after growing and all other objects, that could change ground coordinates) koord k; bool has_water = movingobj_t::random_movingobj_for_climate( water_climate )!=NULL; sint32 queried = simrand(env_t::moving_object_probability*2); // no need to test the borders, since they are mostly slopes anyway for(k.y=1; k.yobj_count()==0 && ( (gr->get_typ()==grund_t::boden && gr->get_grund_hang()==slope_t::flat) || (has_water && gr->is_water()) ) ) { queried --; if( queried<0 ) { const groundobj_desc_t *desc = movingobj_t::random_movingobj_for_climate( get_climate(k) ); if( desc && ( desc->get_waytype() != water_wt || gr->get_hoehe() <= get_water_hgt_nocheck(k) ) ) { if(desc->get_speed()!=0) { queried = simrand(env_t::moving_object_probability*2); gr->obj_add( new movingobj_t( gr->get_pos(), desc ) ); } } } } } } } } sint8 *humidity; void karte_t::init(settings_t* const sets, sint8 const* const h_field) { humidity = NULL; clear_random_mode( 7 ); mute_sound(true); if (env_t::networkmode) { if (env_t::server) { network_reset_server(); } else { network_core_shutdown(); } } step_mode = PAUSE_FLAG; intr_disable(); if(plan) { destroy(); } for( uint i=0; iis_display_init()) { gfx->set_cursor_visible(false); } viewport->change_world_position( koord(0,0), 0, 0 ); settings = *sets; // names during creation time settings.set_name_language_iso(env_t::language_names_iso); translator::init_custom_names(settings.get_name_language_id()); settings.set_use_timeline(settings.get_use_timeline() & 1); ticks = 0; last_step_ticks = ticks; schedule_counter = 0; // ticks = 0x7FFFF800; // Testing the 31->32 bit step last_month = 0; last_year = settings.get_starting_year(); current_month = last_month + (last_year*12); set_ticks_per_world_month_shift(settings.get_bits_per_month()); next_month_ticks = karte_t::ticks_per_world_month; season=(2+last_month/3)&3; // summer always zero steps = 0; network_frame_count = 0; sync_steps = 0; sync_steps_barrier = sync_steps; map_counter = 0; recalc_average_speed(); // resets timeline koord::locality_factor = settings.get_locality_factor( last_year ); max_allowed_height = sets->get_maximumheight(); min_allowed_height = sets->get_minimumheight(); groundwater = (sint8)sets->get_groundwater(); init_height_to_climate(); snowline = sets->get_winter_snowline() + groundwater; if(sets->get_beginner_mode()) { goods_manager_t::set_multiplier(settings.get_beginner_price_factor()); settings.set_just_in_time( 0 ); } else { goods_manager_t::set_multiplier( 1000 ); } recalc_season_snowline(false); cities.clear(); DBG_DEBUG("karte_t::init()","hausbauer_t::new_world()"); // Call this before building cities hausbauer_t::new_world(); cached_grid_size.x = 0; cached_grid_size.y = 0; DBG_DEBUG("karte_t::init()","init_tiles"); init_tiles(); enlarge_map(&settings, h_field); script_api::new_world(); DBG_DEBUG("karte_t::init()","built timeline"); private_car_t::build_timeline_list(this); pedestrian_t::build_timeline_list(this); nosave_warning = nosave = false; dbg->message("karte_t::init()", "Creating factories ..."); factory_builder_t::new_world(); int consecutive_build_failures = 0; loadingscreen_t ls( translator::translate("distributing factories"), 16 + settings.get_city_count() * 4 + settings.get_factory_count(), true, true ); while( all_factories.get_count() < (uint32)settings.get_factory_count() ) { if( !factory_builder_t::increase_industry_density( false ) ) { if( ++consecutive_build_failures > 3 ) { // Industry chain building starts failing consecutively as map approaches full. break; } } else { consecutive_build_failures = 0; } ls.set_progress( 16 + settings.get_city_count() * 4 + min(all_factories.get_count(),settings.get_factory_count()) ); } settings.set_factory_count( all_factories.get_count() ); finance_history_year[0][WORLD_FACTORIES] = finance_history_month[0][WORLD_FACTORIES] = all_factories.get_count(); // tourist attractions factory_builder_t::distribute_attractions(settings.get_tourist_attractions()); dbg->message("karte_t::init()", "Preparing startup ..."); if(zeiger == NULL) { zeiger = new zeiger_t(koord3d::invalid, NULL ); } // finishes the line preparation and sets id 0 to invalid ... players[PLAYER_HUMAN_NR]->simlinemgmt.finish_rd(); set_tool( tool_t::general_tool[TOOL_QUERY], get_active_player() ); recalc_average_speed(); for (int i = 0; i < MAX_PLAYER_COUNT; i++) { if( players[i] ) { players[i]->set_active(true); } } active_player_nr = PLAYER_HUMAN_NR; active_player = players[PLAYER_HUMAN_NR]; tool_t::update_toolbars(); msg->clear(); chat_msg->clear(); has_current_network_save = false; set_dirty(); step_mode = PAUSE_FLAG; simloops = 60; reset_timer(); if(gfx->is_display_init()) { gfx->set_cursor_visible(true); } mute_sound(false); } #define array_koord(px,py) (px + py * get_size().x) /* Lakes: * For each height from groundwater+1 to max_lake_height we loop over * all tiles in the map trying to increase water height to this value * To start with every tile in the map is checked - but when we fail for * a tile then it is excluded from subsequent checks */ sint8 *stage; sint8 *new_stage; sint8 *local_stage; sint8 *max_water_hgt; bool need_to_flood; bool global_lake_fill; sint8 h; void karte_t::create_lakes_loop( sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max ) { const uint16 size_x = get_size().x; const uint16 size_y = get_size().y; if( x_min < 1 ) x_min = 1; if( y_min < 1 ) y_min = 1; if( x_max > size_x-1 ) x_max = size_x-1; if( y_max > size_y-1 ) y_max = size_y-1; bool alternate_stage = false; // false = new data in new_stage, true = new data in stage sint8 *this_stage = stage; // local copy of pointer so we can swap round in thread sint8 *this_new_stage = new_stage; // local copy of pointer so we can swap round in thread for( uint16 y = y_min; y < y_max; y++ ) { for( uint16 x = x_min; x < x_max; x++ ) { uint32 offset = array_koord(x,y); if( h>max_water_hgt[offset] && max_water_hgt[offset]!=-126 && this_stage[offset]==-1 ) { koord k(x,y); if( global_lake_fill == true ) { memcpy( this_new_stage, this_stage, sizeof(sint8) * size_x * size_y ); } else { memcpy( this_new_stage + sizeof(sint8) * array_koord(0,y_min), this_stage + sizeof(sint8) * array_koord(0,y_min), sizeof(sint8) * size_x * (y_max-y_min) ); } bool success = false; if( global_lake_fill == true ) { success = can_flood_to_depth( k, h, this_new_stage, local_stage, 1, size_x-1, 1, size_y-1 ); } else { success = can_flood_to_depth( k, h, this_new_stage, local_stage, x_min, x_max, y_min, y_max ); } if( success ) { if( alternate_stage ) { this_new_stage = new_stage; this_stage = stage; } else { this_new_stage = stage; this_stage = new_stage; } alternate_stage = !alternate_stage; need_to_flood = true; } else { uint32 offset_end = array_koord(0,(global_lake_fill?size_y-1:y_max)); for( uint32 local_offset = array_koord(0,(global_lake_fill?1:y_min)); local_offset -1 ) { max_water_hgt[local_offset] = -126; } } } } } } // ensure global stage reflect local data if( alternate_stage ) { if( global_lake_fill == true ) { sint8 *tmp_stage = new_stage; new_stage = stage; stage = tmp_stage; } else { memcpy( stage + sizeof(sint8) * array_koord(0,y_min), new_stage + sizeof(sint8) * array_koord(0,y_min), sizeof(sint8) * size_x * (y_max-y_min) ); } } } void karte_t::create_lakes( int xoff, int yoff, sint8 max_lake_height ) { if( xoff > 0 || yoff > 0 ) { // too complicated to add lakes to an already existing world... return; } const uint16 size_x = get_size().x; const uint16 size_y = get_size().y; max_water_hgt = new sint8[size_x * size_y]; for( uint16 y = 0; y < size_y; y++ ) { for( uint16 x = 0; x < size_x; x++ ) { uint32 offset = array_koord(x,y); grund_t *gr = lookup_kartenboden_nocheck(x,y); sint8 hgt = gr->get_hoehe(); const sint8 water_hgt = water_hgts[offset]; // optimised <- get_water_hgt_nocheck(x, y); max_water_hgt[offset] = max(hgt, water_hgt); } } stage = new sint8[size_x * size_y]; new_stage = new sint8[size_x * size_y]; local_stage = new sint8[size_x * size_y]; for( h = groundwater+1; h < max_lake_height; h++ ) { need_to_flood = false; memset( stage, -1, sizeof(sint8) * size_x * size_y ); // run over seams first to separate regions global_lake_fill = true; for( int t = 1; t < env_t::num_threads; t++ ) { sint16 y_min = (t * size_y) / env_t::num_threads; create_lakes_loop( 0, size_x, y_min, y_min + 1 ); } global_lake_fill = (env_t::num_threads == 1); world_xy_loop(&karte_t::create_lakes_loop, 0); if(need_to_flood) { flood_to_depth( h, stage ); } else { break; } } delete [] max_water_hgt; delete [] stage; delete [] new_stage; delete [] local_stage; for (planquadrat_t *pl = plan; pl < (plan + size_x * size_y); pl++) { pl->correct_water(); } } void karte_t::create_beaches( int xoff, int yoff ) { const uint16 size_x = get_size().x; const uint16 size_y = get_size().y; // bays have wide beaches for( uint16 iy = 0; iy < size_y; iy++ ) { for( uint16 ix = (iy >= yoff - 19) ? 0 : max( xoff - 19, 0 ); ix < size_x; ix++ ) { grund_t *gr = lookup_kartenboden_nocheck(ix,iy); if( gr->is_water() && gr->get_hoehe()==groundwater && gr->kann_alle_obj_entfernen(NULL)==NULL) { koord k( ix, iy ); uint8 neighbour_water = 0; bool water[8] = {}; sint16 total_ground = 0; // check whether nearby tiles are water for( int i = 0; i < 8; i++ ) { if( grund_t *gr2 = lookup_kartenboden( k + koord::neighbours[i] ) ) { total_ground++; if( gr2->hat_weg( water_wt ) ) { // never ever make a beach near a river mound neighbour_water = 8; break; } if( gr2->is_water() ) { water[i] = 1; neighbour_water++; } } } // make a count of nearby tiles - where tiles on opposite (+-1 direction) sides are water these count much more so we don't block straits for( int i = 0; i < 8; i++ ) { if( water[i] ) { if( water[(i + 3) & 7] || water[(i + 4) & 7] || water[(i + 5) & 7] ) { neighbour_water++; } } } // if not much nearby water then turn into a beach if( neighbour_water < (3*total_ground)/4 ) { set_water_hgt_nocheck( k, gr->get_hoehe() - 1 ); raise_grid_to( ix, iy, gr->get_hoehe() ); raise_grid_to( ix + 1, iy, gr->get_hoehe() ); raise_grid_to( ix, iy + 1, gr->get_hoehe() ); raise_grid_to( ix + 1, iy + 1 , gr->get_hoehe() ); access_nocheck(k)->correct_water(); access_nocheck(k)->set_climate( desert_climate ); } } } } // headlands should not have beaches at all for( uint16 iy = 0; iy < size_y; iy++ ) { for( uint16 ix = (iy >= yoff - 19) ? 0 : max( xoff - 19, 0 ); ix < size_x; ix++ ) { koord k( ix, iy ); grund_t *gr = lookup_kartenboden_nocheck(k); if( !gr->is_water() && gr->get_pos().z == groundwater ) { uint8 neighbour_water = 0; for( int i = 0; i < 8; i++ ) { grund_t *gr2 = lookup_kartenboden( k + koord::neighbours[i] ); if( !gr2 || gr2->is_water() ) { neighbour_water++; } } // if a lot of water nearby we are a headland if( neighbour_water > 3 ) { access_nocheck(k)->set_climate( get_climate_at_height( groundwater + 1 ) ); } } } } // remove any isolated 1 tile beaches for( uint16 iy = 0; iy < size_y; iy++ ) { for( uint16 ix = (iy >= yoff - 19) ? 0 : max( xoff - 19, 0 ); ix < size_x; ix++ ) { koord k( ix, iy ); if( access_nocheck(k)->get_climate() == desert_climate ) { uint8 neighbour_beach = 0; //look up neighbouring climates climate neighbour_climate[8]; for( int i = 0; i < 8; i++ ) { koord k_neighbour = k + koord::neighbours[i]; if( !is_within_limits(k_neighbour) ) { k_neighbour = get_closest_coordinate(k_neighbour); } neighbour_climate[i] = get_climate( k_neighbour ); } // get transition climate - look for each corner in turn for( int i = 0; i < 4; i++ ) { climate transition_climate = (climate) max( max( neighbour_climate[(i * 2 + 1) & 7], neighbour_climate[(i * 2 + 3) & 7] ), neighbour_climate[(i * 2 + 2) & 7] ); climate min_climate = (climate) min( min( neighbour_climate[(i * 2 + 1) & 7], neighbour_climate[(i * 2 + 3) & 7] ), neighbour_climate[(i * 2 + 2) & 7] ); if( min_climate <= desert_climate && transition_climate == desert_climate ) { neighbour_beach++; } } if( neighbour_beach == 0 ) { access_nocheck(k)->set_climate( get_climate_at_height( groundwater + 1 ) ); } } } } } void karte_t::distribute_trees_region( sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom ) { // now distribute trees DBG_DEBUG("karte_t::init()","distributing trees"); switch (settings.get_tree_distribution()) { case settings_t::TREE_DIST_RAINFALL: if( humidity_map.get_height() != 0 ) { koord pos; for( pos.y=ytop; pos.yobj_count() == 0 && gr->get_typ() == grund_t::boden) { if(humidity_map.at(pos.x,pos.y)>75) { const uint32 tree_probability = (humidity_map.at(pos.x,pos.y) - 75)/5 + 38; uint8 number_to_plant = 0; uint8 const max_trees_here = min(get_settings().get_max_no_of_trees_on_square(), (tree_probability - 38 + 1) / 2); for (uint8 c2 = 0 ; c275) { // plant spare trees, (those with low preffered density) or in an entirely tree climate uint16 cl = 1 << get_climate(pos); settings_t const& s = get_settings(); if ((cl & s.get_no_tree_climates()) == 0 && ((cl & s.get_tree_climates()) != 0 || simrand(s.get_forest_inverse_spare_tree_density() * /*dichte*/3) < 100)) { tree_builder_t::plant_tree_on_coordinate(pos, 1, 1); } } } } } break; } // fall-through case settings_t::TREE_DIST_RANDOM: // no humidity data or on request tree_builder_t::distribute_trees(3, xtop, ytop, xbottom, ybottom ); break; case settings_t::TREE_DIST_NONE: // no trees break; case settings_t::TREE_DIST_COUNT: assert(false); // should not happen break; } } void karte_t::enlarge_map(settings_t const* sets, sint8 const* const h_field) { const koord new_size(sets->get_size_x(), sets->get_size_y()); assert(new_size.x >= 0); assert(new_size.y >= 0); if( cached_grid_size.y>0 && cached_grid_size.y!=new_size.y ) { // to keep the labels grund_t::enlarge_map( new_size.x, new_size.y ); } planquadrat_t *new_plan = new planquadrat_t[(uint32) new_size.x * (uint32) new_size.y]; sint8 *new_grid_hgts = new sint8 [(uint32)(new_size.x + 1) * (uint32)(new_size.y + 1)]; sint8 *new_water_hgts = new sint8 [(uint32) new_size.x * (uint32) new_size.y]; memset( new_grid_hgts, groundwater, sizeof(sint8) * (new_size.x + 1) * (new_size.y + 1) ); memset( new_water_hgts, groundwater, sizeof(sint8) * new_size.x * new_size.y ); const koord old_size = get_size(); const bool new_world = old_size.x == 0 && old_size.y == 0; settings.set_size_x(new_size.x); settings.set_size_y(new_size.y); cached_grid_size = new_size; cached_size_max = max(cached_grid_size.x,cached_grid_size.y); cached_size.x = cached_grid_size.x-1; cached_size.y = cached_grid_size.y-1; intr_disable(); const bool minimap_was_visible = minimap_t::is_visible; int max_display_progress; if( !new_world ) { mute_sound(true); minimap_t::is_visible = false; if (gfx->is_display_init()) { gfx->set_cursor_visible(false); } // Copy old values: for (sint16 iy = 0; iyget_city_count()*2 + cities.get_count()*4; } else { max_display_progress = 16 + sets->get_city_count() * 4 + settings.get_factory_count(); } loadingscreen_t ls( translator::translate( new_world ? "Init map ..." : "enlarge map"), max_display_progress, true, true ); delete [] plan; plan = new_plan; delete [] grid_hgts; grid_hgts = new_grid_hgts; delete [] water_hgts; water_hgts = new_water_hgts; if( new_world ) { // init max and min with defaults max_height = groundwater; min_height = groundwater; } setsimrand(0xFFFFFFFF, settings.get_map_number()); clear_random_mode( 0xFFFF ); set_random_mode( MAP_CREATE_RANDOM ); if( new_world && !settings.heightfield.empty() ) { // init from file for(int y=0; yget_rotation()==0 && sets->get_origin_x()==0 && sets->get_origin_y()==0) { // otherwise negative offsets may occur, so we cache only non-rotated maps init_perlin_map(new_size.x,new_size.y); } if ( old_size.x > 0 && old_size.y > 0 ) { // loop only new tiles: for( sint16 y = 0; y<=new_size.y; y++ ) { for( sint16 x = (y>old_size.y) ? 0 : old_size.x+1; x<=new_size.x; x++ ) { koord k(x,y); sint16 const h = perlin_hoehe(&settings, k, koord(old_size.x, old_size.y)); set_grid_hgt_nocheck( k, (sint8) h); } ls.set_progress( (y*16)/new_size.y ); } } else { world_xy_loop(&karte_t::perlin_hoehe_loop, GRIDS_FLAG); ls.set_progress(2); } exit_perlin_map(); } /** @note First we'll copy the border heights to the adjacent tile. * The best way I could find is raising the first new grid point to * the same height the adjacent old grid point was and lowering to the * same height again. This doesn't preserve the old area 100%, but it respects it * somehow. * * This does not work for water tiles as for them get_hoehe will return the * z-coordinate of the water surface, not the height of the underwater * landscape. */ sint32 i; grund_t *gr; sint8 h; if (!new_world) { for(i=0; iis_water()) { h = gr->get_hoehe(slope4_t::corner_SW); raise_grid_to(i, old_size.y+1, h); lower_grid_to(i, old_size.y+1, h ); } } for(i=0; iis_water()) { h = gr->get_hoehe(slope4_t::corner_NE); raise_grid_to(old_size.x+1, i, h); lower_grid_to(old_size.y+1, i, h); } } gr = lookup_kartenboden_nocheck(old_size.x-1, old_size.y -1); if (!gr->is_water()) { h = gr->get_hoehe(slope4_t::corner_SE); raise_grid_to(old_size.x+1, old_size.y+1, h); lower_grid_to(old_size.x+1, old_size.y+1, h); } } // smooth the new part, reassign slopes on new part cleanup_karte( old_size.x, old_size.y ); if ( new_world ) { ls.set_progress(10); } // update height bounds for( sint16 iy = 0; iy < new_size.y; iy++ ) { for( sint16 ix = (iy >= old_size.y) ? 0 : max( old_size.x, 0 ); ix < new_size.x; ix++ ) { sint8 hgt = lookup_kartenboden_nocheck(ix, iy)->get_hoehe(); if (hgt < min_height) { min_height = hgt; } if (hgt > max_height) { max_height = hgt; } } } if ( new_world ) { ls.set_progress(12); } DBG_DEBUG("karte_t::distribute_groundobjs_cities()","distributing rivers"); if( sets->get_lakeheight() > 0 ) { create_lakes( old_size.x, old_size.y, sets->get_lakeheight() ); } // so at least some rivers end or start in lakes if( env_t::river_types > 0 && settings.get_river_number() > 0 ) { create_rivers( settings.get_river_number() ); } if ( new_world ) { ls.set_progress(13); } // set climates in new area if( new_world ) { calc_climate_map_region( 0, 0, new_size.x, new_size.y ); } else { calc_climate_map_region( 0, old_size.y, old_size.x, new_size.y ); calc_climate_map_region( old_size.x, 0, new_size.x, new_size.y ); } if ( new_world ) { ls.set_progress(14); } create_beaches( old_size.x, old_size.y ); if ( new_world ) { ls.set_progress(15); } if ( new_world ) { // new world -> calculate all transitions world_xy_loop(&karte_t::recalc_transitions_loop, 0); ls.set_progress(16); } else { // and calculate transitions in a 1 tile larger area for( sint16 iy = 0; iy < new_size.y; iy++ ) { for( sint16 ix = (iy >= old_size.y - 20) ? 0 : max( old_size.x - 20, 0 ); ix < new_size.x; ix++ ) { recalc_transitions( koord( ix, iy ) ); } } } // now recalc the images of the old map near the seam ... for( sint16 y = 0; y < old_size.y - 20; y++ ) { for( sint16 x = max( old_size.x - 20, 0 ); x < old_size.x; x++ ) { lookup_kartenboden_nocheck(x,y)->calc_image(); } } for( sint16 y = max( old_size.y - 20, 0 ); y < old_size.y; y++) { for( sint16 x = 0; x < old_size.x; x++ ) { lookup_kartenboden_nocheck(x,y)->calc_image(); } } distribute_cities( sets->get_city_count(), sets->get_mean_citizen_count(), old_size.x, old_size.y ); if( new_world ) { distribute_trees_region( 0, 0, new_size.x, new_size.y ); } else { distribute_trees_region( 0, old_size.y, old_size.x, new_size.y ); distribute_trees_region( old_size.x, 0, new_size.x, new_size.y ); } humidity_map.clear(); // eventual update origin switch( settings.get_rotation() ) { case 1: { settings.set_origin_y( settings.get_origin_y() - new_size.y + old_size.y ); break; } case 2: { settings.set_origin_x( settings.get_origin_x() - new_size.x + old_size.x ); settings.set_origin_y( settings.get_origin_y() - new_size.y + old_size.y ); break; } case 3: { settings.set_origin_x( settings.get_origin_x() - new_size.y + old_size.y ); break; } } distribute_groundobjs(old_size.x, old_size.y); distribute_movingobjs(old_size.x, old_size.y); // hausbauer_t::new_world(); <- this would reinit monuments! do not do this! factory_builder_t::new_world(); set_schedule_counter(); // Refresh the haltlist for the affected tiles / stations. // It is enough to check the tile just at the border ... uint16 const cov = settings.get_station_coverage(); if( old_size.y < new_size.y ) { for( sint16 y=0; yget_boden_count(); i++ ) { // update halt halthandle_t h = pl->get_boden_bei(i)->get_halt(); if( h.is_bound() ) { for( sint16 xp=max(0,x-cov); xpadd_to_haltlist(h); } } } } } } } if( old_size.x < new_size.x ) { for( sint16 y=max(0,old_size.y-cov); yget_boden_count(); i++ ) { // update halt halthandle_t h = pl->get_boden_bei(i)->get_halt(); if( h.is_bound() ) { for( sint16 xp=x; xpadd_to_haltlist(h); } } } } } } } clear_random_mode( MAP_CREATE_RANDOM ); if ( !new_world ) { if (gfx->is_display_init()) { gfx->set_cursor_visible(true); } mute_sound(false); minimap_t::is_visible = minimap_was_visible; minimap_t::get_instance()->init(); minimap_t::get_instance()->calc_map(); minimap_t::get_instance()->set_display_mode( minimap_t::get_instance()->get_display_mode() ); set_dirty(); reset_timer(); } // update main menu tool_t::update_toolbars(); } karte_t::karte_t() : settings(env_t::default_settings), convoi_array(0), attractions(16), cities(0) { destroying = false; // length of day and other time stuff ticks_per_world_month_shift = 20; ticks_per_world_month = (1 << ticks_per_world_month_shift); last_step_ticks = 0; server_last_announce_time = 0; last_interaction = dr_time(); step_mode = PAUSE_FLAG; time_multiplier = 16; next_midi_time = next_step_time = next_misc_time = 0; fix_ratio_frame_time = 200; idle_time = 0; network_frame_count = 0; sync_steps = 0; sync_steps_barrier = sync_steps; for( uint i=0; imsg); // generate ground textures once ground_desc_t::init_ground_textures(this); // set single instance world = this; } karte_t::~karte_t() { is_sound = false; destroy(); // not deleting the tools of this map ... delete viewport; delete msg; delete records; // unset single instance if (world == this) { world = NULL; } } void karte_t::store_player_password_hash( uint8 player_nr, const pwd_hash_t& hash ) { player_password_hash[player_nr] = hash; } void karte_t::clear_player_password_hashes() { for(int i=0; icheck_unlock(player_password_hash[i]); } } } void karte_t::rdwr_player_password_hashes(loadsave_t *file) { pwd_hash_t dummy; for( int i=0; iaccess_password_hash() : &dummy; for( uint8 j=0; j<20; j++) { file->rdwr_byte( (*p)[j] ); } } } void karte_t::call_change_player_tool(uint8 cmd, uint8 player_nr, uint16 param, bool scripted_call) { if (env_t::networkmode) { nwc_chg_player_t *nwc = new nwc_chg_player_t(sync_steps, map_counter, cmd, player_nr, param, scripted_call); network_send_server(nwc); } else { clear_random_mode(INTERACTIVE_RANDOM); // beacue the AI will call random to check where it can run change_player_tool(cmd, player_nr, param, !get_public_player()->is_locked() || scripted_call, true); // update the window ki_kontroll_t* playerwin = (ki_kontroll_t*)win_get_magic(magic_ki_kontroll_t); if (playerwin) { playerwin->update_data(); } } } bool karte_t::change_player_tool(uint8 cmd, uint8 player_nr, uint16 param, bool public_player_unlocked, bool exec) { switch(cmd) { case new_player: { // only public player can start AI if( (param != player_t::HUMAN && !public_player_unlocked) || param >= player_t::MAX_AI ) { return false; } // range check, player already existent? if( player_nr >= PLAYER_UNOWNED || get_player(player_nr) ) { return false; } if(exec) { init_new_player( player_nr, (uint8) param ); // activate/deactivate AI immediately player_t *player = get_player(player_nr); if (param != player_t::HUMAN && player) { player->set_active(true); } } return true; } case toggle_player_active: { // range check, player existent? if ( player_nr <=1 || player_nr >= PLAYER_UNOWNED || get_player(player_nr)==NULL ) { return false; } // only public player can (de)activate other players if ( !public_player_unlocked ) { return false; } if (exec) { player_t *player = get_player(player_nr); player->set_active(param != 0); } return true; } case toggle_freeplay: { // only public player can change freeplay mode if (!public_player_unlocked || !settings.get_allow_player_change()) { return false; } if (exec) { settings.set_freeplay( !settings.is_freeplay() ); } return true; } case delete_player: { // range check, player existent? if ( player_nr >= PLAYER_UNOWNED || get_player(player_nr)==NULL ) { return false; } if (exec) { remove_player(player_nr); } return true; } // unknown command: delete default: ; } return false; } void karte_t::set_tool_api( tool_t *tool_in, player_t *player, bool& suspended) { suspended = false; if( get_random_mode()&LOAD_RANDOM ) { dbg->warning("karte_t::set_tool", "Ignored tool %s during loading.", tool_in->get_name() ); return; } bool needs_check = !tool_in->no_check(); // check for scenario conditions if( needs_check && !scenario->is_tool_allowed(player, tool_in->get_id(), tool_in->get_waytype(), tool_in->get_default_param()) ) { return; } // check for password-protected players if( (!tool_in->is_init_keeps_game_state() || !tool_in->is_work_keeps_game_state()) && needs_check && !(tool_in->get_id() == (DIALOG_LOAD | DIALOGE_TOOL) || tool_in->get_id() == (TOOL_CHANGE_PLAYER | SIMPLE_TOOL) || tool_in->get_id()==(TOOL_ADD_MESSAGE | GENERAL_TOOL)) && player && player->is_locked() ) { // player is currently password protected => request unlock first create_win(new password_frame_t(player), w_info, magic_pwd_t + player->get_player_nr() ); return; } tool_in->flags |= (event_get_last_control_shift() ^ tool_t::control_invert); if(!env_t::networkmode || tool_in->is_local_execution() || tool_in->is_init_keeps_game_state() ) { if (tool_in->is_init_keeps_game_state() || (get_random_mode() & INTERACTIVE_RANDOM) == 0) { // network safe or not during sync_step: call directly local_set_tool(tool_in, player); } else { // queue tool for execution nwc_tool_t* nwc = new nwc_tool_t(player, tool_in, zeiger->get_pos(), 0, map_counter, true); command_queue_append(nwc); suspended = true; } } else { // queue tool for network nwc_tool_t *nwc = new nwc_tool_t(player, tool_in, zeiger->get_pos(), steps, map_counter, true); network_send_server(nwc); suspended = true; } } // set a new tool on our client, calls init void karte_t::local_set_tool( tool_t *tool_in, player_t * player ) { tool_in->flags |= tool_t::WFL_LOCAL; // now call init bool init_result = tool_in->init(player); // for unsafe tools init() must return false assert(tool_in->is_init_keeps_game_state() || !init_result); if (player && init_result && !tool_in->is_scripted()) { set_dirty(); tool_t *sp_tool = selected_tool[player->get_player_nr()]; if(tool_in != sp_tool) { // reinit same tool => do not play sound twice sound_play(SFX_SELECT,255,TOOL_SOUND); // only exit, if it is not the same tool again ... sp_tool->flags |= tool_t::WFL_LOCAL; sp_tool->exit(player); sp_tool->flags =0; } if( player==active_player ) { // reset pointer koord3d zpos = zeiger->get_pos(); // remove marks zeiger->change_pos( koord3d::invalid ); // set new cursor properties tool_in->init_cursor(zeiger); // .. and mark again (if the position is acceptable for the tool) if( tool_in->check_valid_pos(zpos.get_2d())) { zeiger->change_pos( zpos ); } else { zeiger->change_pos( koord3d::invalid ); } } selected_tool[player->get_player_nr()] = tool_in; } tool_in->flags = 0; toolbar_last_used_t::last_used_tools->append( tool_in, player ); } planquadrat_t *rotate90_new_plan; sint8 *rotate90_new_water; void karte_t::rotate90_plans(sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max) { const int LOOP_BLOCK = 64; if( (loaded_rotation + settings.get_rotation()) & 1 ) { // 1 || 3 for( int yy = y_min; yy < y_max; yy += LOOP_BLOCK ) { for( int xx = x_min; xx < x_max; xx += LOOP_BLOCK ) { for( int y = yy; y < min(yy + LOOP_BLOCK, y_max); y++ ) { for( int x = xx; x < min(xx + LOOP_BLOCK, x_max); x++ ) { const int nr = x + (y * cached_grid_size.x); const int new_nr = (cached_size.y - y) + (x * cached_grid_size.y); // first rotate everything on the ground(s) for( uint i = 0; i < plan[nr].get_boden_count(); i++ ) { plan[nr].get_boden_bei(i)->rotate90(); } // rotate climate transitions rotate_transitions( koord( x, y ) ); // now: rotate all things on the map swap(rotate90_new_plan[new_nr], plan[nr]); } } } } } else { // first: rotate all things on the map for( int xx = x_min; xx < x_max; xx += LOOP_BLOCK ) { for( int yy = y_min; yy < y_max; yy += LOOP_BLOCK ) { for( int x = xx; x < min(xx + LOOP_BLOCK, x_max); x++ ) { for( int y=yy; y < min(yy + LOOP_BLOCK, y_max); y++ ) { // rotate climate transitions rotate_transitions( koord( x, y ) ); const int nr = x + (y * cached_grid_size.x); const int new_nr = (cached_size.y - y) + (x * cached_grid_size.y); swap(rotate90_new_plan[new_nr], plan[nr]); } } } } // now rotate everything on the ground(s) for( int xx = x_min; xx < x_max; xx += LOOP_BLOCK ) { for( int yy = y_min; yy < y_max; yy += LOOP_BLOCK ) { for( int x = xx; x < min(xx + LOOP_BLOCK, x_max); x++ ) { for( int y = yy; y < min(yy + LOOP_BLOCK, y_max); y++ ) { const int new_nr = (cached_size.y - y) + (x * cached_grid_size.y); for( uint i = 0; i < rotate90_new_plan[new_nr].get_boden_count(); i++ ) { rotate90_new_plan[new_nr].get_boden_bei(i)->rotate90(); } } } } } } // rotate water for( int xx = 0; xx < cached_grid_size.x; xx += LOOP_BLOCK ) { for( int yy = y_min; yy < y_max; yy += LOOP_BLOCK ) { for( int x = xx; x < min( xx + LOOP_BLOCK, cached_grid_size.x ); x++ ) { int nr = x + (yy * cached_grid_size.x); int new_nr = (cached_size.y - yy) + (x * cached_grid_size.y); for( int y = yy; y < min( yy + LOOP_BLOCK, y_max ); y++ ) { rotate90_new_water[new_nr] = water_hgts[nr]; nr += cached_grid_size.x; new_nr--; } } } } } void karte_t::rotate90() { DBG_MESSAGE( "karte_t::rotate90()", "called" ); // assume we can save this rotation nosave_warning = nosave = false; //announce current target rotation settings.rotate90(); // clear marked region zeiger->change_pos( koord3d::invalid ); // preprocessing, detach stops from factories to prevent crash for(halthandle_t const s : haltestelle_t::get_alle_haltestellen()) { s->release_factory_links(); } assert(cached_grid_size.x >= 0); assert(cached_grid_size.y >= 0); rotate90_new_plan = new planquadrat_t[(uint32_t)cached_grid_size.y * (uint32_t)cached_grid_size.x]; rotate90_new_water = new sint8 [(uint32_t)cached_grid_size.y * (uint32_t)cached_grid_size.x]; //rotate plans in parallel posix thread ... world_xy_loop(&karte_t::rotate90_plans, 0); grund_t::finish_rotate90(); delete [] plan; plan = rotate90_new_plan; delete[] water_hgts; water_hgts = rotate90_new_water; climate_map.rotate90(); // rotate heightmap sint8* new_hgts = new sint8[(cached_grid_size.x + 1) * (cached_grid_size.y + 1)]; const int LOOP_BLOCK = 64; for (int yy = 0; yy <= cached_grid_size.y; yy += LOOP_BLOCK) { for (int xx = 0; xx <= cached_grid_size.x; xx += LOOP_BLOCK) { for (int x = xx; x <= min(xx + LOOP_BLOCK, cached_grid_size.x); x++) { for (int y = yy; y <= min(yy + LOOP_BLOCK, cached_grid_size.y); y++) { const int nr = x + (y * (cached_grid_size.x + 1)); const int new_nr = (cached_grid_size.y - y) + (x * (cached_grid_size.y + 1)); new_hgts[new_nr] = grid_hgts[nr]; } } } } delete[] grid_hgts; grid_hgts = new_hgts; // rotate borders sint16 xw = cached_size.x; cached_size.x = cached_size.y; cached_size.y = xw; int wx = cached_grid_size.x; cached_grid_size.x = cached_grid_size.y; cached_grid_size.y = wx; // now step all towns (to generate passengers) for (stadt_t* const i : cities) { i->rotate90(cached_size.x); } // fixed order factory, halts, convois for (fabrik_t* const f : all_factories) { f->rotate90(cached_size.x); } // after rotation of factories, rotate everything that holds freight: stations and convoys for (halthandle_t const s : haltestelle_t::get_alle_haltestellen()) { s->rotate90(cached_size.x); } for (convoihandle_t const i : convoi_array) { i->rotate90(cached_size.x); } for (int i = 0; i < MAX_PLAYER_COUNT; i++) { if (players[i]) { players[i]->rotate90(cached_size.x); } } // rotate label texts for (koord& l : labels) { l.rotate90(cached_size.x); } // rotate view viewport->rotate90(cached_size.x); // rotate messages msg->rotate90(cached_size.x); // rotate view in dialog windows win_rotate90(cached_size.x); if (cached_grid_size.x != cached_grid_size.y) { // the map must be reinit minimap_t::get_instance()->init(); } // rotate map search array factory_builder_t::new_world(); // update minimap if (minimap_t::is_visible) { minimap_t::get_instance()->set_display_mode(minimap_t::get_instance()->get_display_mode()); } get_scenario()->rotate90(cached_size.x); script_api::rotate90(); // finally recalculate schedules for goods in transit ... set_schedule_counter(); set_dirty(); } // -------- Verwaltung von Fabriken ----------------------------- bool karte_t::add_fab(fabrik_t* fab) { //DBG_MESSAGE("karte_t::add_fab()","fab = %p",fab); assert(fab != NULL); all_factories.append(fab); goods_in_game.clear(); // Force rebuild of goods list if (factorylist_frame_t* f = (factorylist_frame_t*)win_get_magic(magic_factorylist)) { f->fill_list(); } return true; } // beware: must remove also links from stops and towns bool karte_t::rem_fab(fabrik_t *fab) { if(!all_factories.remove( fab )) { return false; } // Force rebuild of goods list goods_in_game.clear(); // now all the interwoven connections must be cleared koord k = fab->get_pos().get_2d(); planquadrat_t* plan = access(k); if(plan) { // we need a copy, since the verbinde fabriken is modifying the list halthandle_t list[16]; const uint8 count = plan->get_haltlist_count(); assert(count<16); assert((count == 0) || (plan->get_haltlist() != NULL)); if (count > 0) { memcpy( list, plan->get_haltlist(), count*sizeof(halthandle_t) ); for( uint8 i=0; iremove_from_haltlist( list[i] ); // then reconnect list[i]->reconnect_factories(); } } // remove all links from factories for(fabrik_t* const fab : all_factories) { fab->remove_consumer(k); fab->remove_supplier(k); } // remove all links to cities fab->clear_target_cities(); // finally delete it delete fab; // recalculate factory position map factory_builder_t::new_world(); } if (factorylist_frame_t* f = (factorylist_frame_t *)win_get_magic(magic_factorylist)) { f->fill_list(); } return true; } /*----------------------------------------------------------------------------------------------------------------------*/ /* same procedure for tourist attractions */ void karte_t::add_attraction(gebaeude_t *gb) { assert(gb != NULL); attractions.append( gb, gb->get_tile()->get_desc()->get_level() ); // add links between this attraction and all cities for(stadt_t* const c : cities) { c->add_target_attraction(gb); } if (curiositylist_frame_t *f = (curiositylist_frame_t*)win_get_magic(magic_curiositylist)) { f->fill_list(); } } void karte_t::remove_attraction(gebaeude_t *gb) { assert(gb != NULL); attractions.remove( gb ); // remove links between this attraction and all cities for(stadt_t* const c : cities) { c->remove_target_attraction(gb); } if (curiositylist_frame_t *f = (curiositylist_frame_t*)win_get_magic(magic_curiositylist)) { f->fill_list(); } } void karte_t::add_label(koord k) { labels.append_unique(k); if (labellist_frame_t* f = (labellist_frame_t*)win_get_magic(magic_labellist)) { f->fill_list(); } } void karte_t::remove_label(koord k) { labels.remove(k); if (labellist_frame_t* f = (labellist_frame_t*)win_get_magic(magic_labellist)) { f->fill_list(); } } // -------- Verwaltung von Staedten ----------------------------- stadt_t *karte_t::find_nearest_city(const koord k) const { uint32 min_dist = 99999999; bool contains = false; stadt_t *best = NULL; // within city limits if( is_within_limits(k) ) { for(stadt_t* const s : cities ) { if( k.x >= s->get_linksoben().x && k.y >= s->get_linksoben().y && k.x < s->get_rechtsunten().x && k.y < s->get_rechtsunten().y ) { const uint32 dist = koord_distance( k, s->get_center() ); if( !contains ) { // no city within limits => this is best best = s; min_dist = dist; } else if( dist < min_dist ) { best = s; min_dist = dist; } contains = true; } else if( !contains ) { // so far no cities found within its city limit const uint32 dist = koord_distance( k, s->get_center() ); if( dist < min_dist ) { best = s; min_dist = dist; } } } } return best; } /* * this routine is called before an image is displayed * it moves vehicles and pedestrians * only time consuming thing are done in step() * everything else is done here */ void karte_t::sync_step(uint32 delta_t) { set_random_mode( SYNC_STEP_RANDOM ); // only omitted, when called to display a new frame during fast forward // just for progress if( delta_t > 10000 ) { dbg->error( "karte_t::sync_step()", "delta_t (%u) too large, limiting to 10000", delta_t ); delta_t = 10000; } ticks += delta_t; /* animations do not require exact sync * foundations etc are added removed frequently during city growth */ sync_buildings.sync_step(delta_t); wolke_t::sync_handler(delta_t); pedestrian_t::sync_handler(delta_t); // the following sync_steps affect the game state sync_roadsigns.sync_step(delta_t); movingobj_t::sync_handler(delta_t); private_car_t::sync_handler(delta_t); senke_t::sync_handler(delta_t); sync.sync_step( delta_t ); ticker::update(); clear_random_mode( SYNC_STEP_RANDOM ); set_random_mode( INTERACTIVE_RANDOM ); eventmanager->check_events(); clear_random_mode( INTERACTIVE_RANDOM ); } void karte_t::display(uint32 delta_t) { set_random_mode( SYNC_STEP_RANDOM ); // only omitted in fast forward mode for the magic steps for(int x=0; xage_messages(delta_t); } } // change view due to following a convoi? convoihandle_t follow_convoi = viewport->get_follow_convoi(); if(follow_convoi.is_bound() && follow_convoi->get_vehicle_count()>0) { vehicle_t const& v = *follow_convoi->front(); koord3d const new_pos = v.get_pos(); if(new_pos!=koord3d::invalid) { const sint16 rw = gfx->get_tile_raster_width(); int new_xoff = 0; int new_yoff = 0; v.get_screen_offset( new_xoff, new_yoff, gfx->get_tile_raster_width() ); new_xoff -= tile_raster_scale_x(-v.get_xoff(), rw); new_yoff -= tile_raster_scale_y(-v.get_yoff(), rw) + tile_raster_scale_y(new_pos.z * TILE_HEIGHT_STEP, rw); viewport->change_world_position( new_pos.get_2d(), -new_xoff, -new_yoff ); // auto underground to follow convois if( env_t::follow_convoi_underground ) { grund_t *gr = lookup_kartenboden( new_pos.get_2d() ); bool redraw = false; if( new_pos.z < gr->get_hoehe() ) { redraw = (grund_t::underground_mode != grund_t::ugm_level) || (grund_t::underground_level != new_pos.z); grund_t::set_underground_mode( grund_t::ugm_level, new_pos.z ); } else { redraw = grund_t::underground_mode != grund_t::ugm_none; grund_t::set_underground_mode( grund_t::ugm_none, 0 ); } if( redraw ) { // recalc all images on map update_underground(); } } } } // display new frame with water animation intr_refresh_display( false ); update_frame_sleep_time(); clear_random_mode( SYNC_STEP_RANDOM ); } // does all the magic about frame timing void karte_t::update_frame_sleep_time() { // get average frame time uint32 frame_end_time = dr_time(); last_frame_ms[last_frame_idx] = frame_end_time; last_frame_idx = (last_frame_idx+1) % 32; sint32 ms_diff = (sint32)( frame_end_time - last_frame_ms[last_frame_idx] ); if(ms_diff > 0) { realFPS = (32000u*16) / ms_diff; } else { realFPS = env_t::fps*16; simloops = 60; } if( step_mode&PAUSE_FLAG ) { // calculate next frame start time based on last frame start // setting next_step_time = frame_end_time + 1000/env_t::fps would would sleep way too long next_step_time = std::max(frame_end_time, next_step_time + 1000 / env_t::fps); idle_time = 100; } else if( step_mode==FIX_RATIO) { simloops = realFPS/16; } else if(step_mode==NORMAL) { // calculate simloops uint16 last_step = (steps+31)%32; if(last_step_nr[last_step]>last_step_nr[steps%32]) { simloops = (10000*32l)/(last_step_nr[last_step]-last_step_nr[steps%32]); } // (de-)activate faster redraw env_t::simple_drawing = (env_t::simple_drawing_normal >= gfx->get_tile_raster_width()); // calculate and activate fast redraw .. if( realFPS > (env_t::fps*17/16) ) { // decrease fast tile zoom by one if( env_t::simple_drawing_normal > env_t::simple_drawing_default ) { env_t::simple_drawing_normal --; } } else if( realFPS < env_t::fps*16/2 ) { // activate simple redraw env_t::simple_drawing_normal = max( env_t::simple_drawing_normal, gfx->get_tile_raster_width()+1 ); } else if( realFPS < (env_t::fps*15) ) { // increase fast tile redraw by one if below current tile size if( env_t::simple_drawing_normal <= (gfx->get_tile_raster_width()*3)/2 ) { env_t::simple_drawing_normal ++; } } else if( idle_time > 0 ) { // decrease fast tile zoom by one if( env_t::simple_drawing_normal > env_t::simple_drawing_default ) { env_t::simple_drawing_normal --; } } env_t::simple_drawing = (env_t::simple_drawing_normal >= gfx->get_tile_raster_width()); // way too slow => try to increase time ... if( frame_end_time-last_interaction > 100 ) { if( frame_end_time-last_interaction > 500 ) { idle_time >>= 1; set_frame_time( 1+get_frame_time() ); // more than 1s since last zoom => check if zoom out is a way to improve it if( frame_end_time-last_interaction > 5000 && gfx->get_current_tile_raster_width() < 32 && realFPS <= (5*16) ) { gfx->zoom_factor_up(); viewport->metrics_updated(); set_dirty(); last_interaction = frame_end_time-1000; } } else { increase_frame_time(); increase_frame_time(); increase_frame_time(); increase_frame_time(); } } else { // change frame spacing ... (pause will be changed by step() directly) if(realFPS>(env_t::fps*17)) { increase_frame_time(); } else if(realFPSframe_intervall) { reduce_frame_time(); } else { increase_frame_time(); } // (de-)activate faster redraw env_t::simple_drawing = env_t::simple_drawing_fast_forward || (env_t::simple_drawing_normal >= gfx->get_tile_raster_width()); } } // add an amount to a subcategory void karte_t::buche(sint64 const betrag, player_cost const type) { assert(type < MAX_WORLD_COST); finance_history_year[0][type] += betrag; finance_history_month[0][type] += betrag; // to do: check for dependencies } inline sint32 get_population(stadt_t const* const c) { return c->get_einwohner(); } void karte_t::new_month() { bool need_locality_update = false; update_history(); // advance history ... last_month_bev = finance_history_month[ 0 ][ WORLD_CITIZENS ]; for( int hist = 0; hist < karte_t::MAX_WORLD_COST; hist++ ) { for( int y = MAX_WORLD_HISTORY_MONTHS - 1; y > 0; y-- ) { finance_history_month[ y ][ hist ] = finance_history_month[ y - 1 ][ hist ]; } } current_month++; last_month++; if( last_month > 11 ) { last_month = 0; // check for changed distance weight uint32 old_locality_factor = koord::locality_factor; koord::locality_factor = settings.get_locality_factor( last_year + 1 ); need_locality_update = (old_locality_factor != koord::locality_factor); if( current_month > DEFAULT_RETIRE_YEAR * 12 ) { // switch off timeline after 2999, since everything retires settings.set_use_timeline( 0 ); dbg->warning( "karte_t::new_month()", "Timeline disabled after the year 2999" ); } } DBG_MESSAGE( "karte_t::new_month()", "Month (%d/%d) has started", (last_month % 12) + 1, last_month / 12 ); // this should be done before a map update, since the map may want an update of the way usage for(weg_t* const w : weg_t::get_alle_wege() ) { w->new_month(); } // recalc old settings (and maybe update the stops with the current values) minimap_t::get_instance()->new_month(); INT_CHECK( "simworld 1701" ); // DBG_MESSAGE("karte_t::new_month()","factories"); for(fabrik_t* const fab : all_factories) { fab->new_month(); } INT_CHECK("simworld 1278"); // DBG_MESSAGE("karte_t::new_month()","cities"); cities.update_weights(get_population); for(stadt_t* const i : cities) { i->new_month(need_locality_update); } INT_CHECK("simworld 1282"); // player for(uint i=0; i=2 && last_month == 0 && !settings.is_freeplay() ) { // remove all player (but first and second) who went bankrupt during last year if( players[i] != NULL && players[i]->get_finance()->is_bancrupted() ) { remove_player(i); } } if( players[i] != NULL ) { // if returns false -> remove player if (!players[i]->new_month()) { remove_player(i); } } } // DBG_MESSAGE("karte_t::new_month()","convois"); // call new month for convois, must be after player, because fixed costs are booked here and to connected lines for(convoihandle_t const cnv : convoi_array) { cnv->new_month(); } INT_CHECK("simworld 1701"); // update the window if( ki_kontroll_t* playerwin = (ki_kontroll_t*)win_get_magic(magic_ki_kontroll_t) ) { playerwin->update_data(); } INT_CHECK("simworld 1289"); // DBG_MESSAGE("karte_t::new_month()","halts"); for(halthandle_t const s : haltestelle_t::get_alle_haltestellen()) { s->new_month(); INT_CHECK("simworld 1877"); } INT_CHECK("simworld 2522"); depot_t::new_month(); scenario->new_month(); // now switch year to get the right year for all timeline stuff ... if( last_month == 0 ) { new_year(); INT_CHECK("simworld 1299"); } way_builder_t::new_month(); INT_CHECK("simworld 1299"); recalc_average_speed(); INT_CHECK("simworld 1921"); // update toolbars (i.e. new waytypes tool_t::update_toolbars(); // no autosave in networkmode or when the new world dialogue is shown if( !env_t::networkmode && env_t::autosave>0 && last_month%env_t::autosave==0 && !win_get_magic(magic_welt_gui_t) ) { cbuffer_t buf; dr_chdir(env_t::user_dir); // make sure we are in the right directory buf.printf( SAVE_PATH_X "autosave%02i.sve", last_month+1 ); save( buf, true, env_t::savegame_version_str, true ); } } void karte_t::new_year() { last_year = current_month/12; // advance history ... for( int hist=0; hist0; y-- ) { finance_history_year[y][hist] = finance_history_year[y-1][hist]; } } DBG_MESSAGE("karte_t::new_year()","speedbonus for %d %i, %i, %i, %i, %i, %i, %i, %i", last_year, average_speed[0], average_speed[1], average_speed[2], average_speed[3], average_speed[4], average_speed[5], average_speed[6], average_speed[7] ); cbuffer_t buf; buf.printf( translator::translate("Year %i has started."), last_year ); msg->add_message(buf,koord3d::invalid,message_t::general,SYSCOL_TEXT,skinverwaltung_t::neujahrsymbol->get_image_id(0)); for(convoihandle_t const cnv : convoi_array) { cnv->new_year(); } for(int i=0; inew_year(); } } scenario->new_year(); } // recalculated speed bonus for different vehicles // and takes care of all timeline stuff void karte_t::recalc_average_speed() { // retire/allocate vehicles private_car_t::build_timeline_list(this); pedestrian_t::build_timeline_list(this); for(int i=road_wt; i<=narrowgauge_wt; i++) { const int typ = i==4 ? 3 : (i-1)&7; average_speed[typ] = vehicle_builder_t::get_speedbonus( this->get_timeline_year_month(), i==4 ? air_wt : (waytype_t)i ); } // DBG_MESSAGE("karte_t::recalc_average_speed()",""); if(use_timeline()) { for(int i=road_wt; i<=air_wt; i++) { const char *vehicle_type=NULL; switch(i) { case road_wt: vehicle_type = "road vehicle"; break; case track_wt: vehicle_type = "rail car"; break; case water_wt: vehicle_type = "water vehicle"; break; case monorail_wt: vehicle_type = "monorail vehicle"; break; case tram_wt: vehicle_type = "street car"; break; case air_wt: vehicle_type = "airplane"; break; case maglev_wt: vehicle_type = "maglev vehicle"; break; case narrowgauge_wt: vehicle_type = "narrowgauge vehicle"; break; default: // this is not a valid waytype continue; } vehicle_type = translator::translate( vehicle_type ); bool vehicle_changed = false; for(vehicle_desc_t const* const info : vehicle_builder_t::get_info((waytype_t)i)) { const uint16 intro_month = info->get_intro_year_month(); if(intro_month == current_month) { cbuffer_t buf; buf.printf( translator::translate("New %s now available:\n%s\n"), vehicle_type, translator::translate(info->get_name()) ); msg->add_message(buf,koord3d::invalid,message_t::new_vehicle,NEW_VEHICLE,info->get_base_image()); vehicle_changed = true; } const uint16 retire_month = info->get_retire_year_month(); if(retire_month == current_month) { cbuffer_t buf; buf.printf( translator::translate("Production of %s has been stopped:\n%s\n"), vehicle_type, translator::translate(info->get_name()) ); msg->add_message(buf,koord3d::invalid,message_t::new_vehicle,NEW_VEHICLE,info->get_base_image()); vehicle_changed = true; } } if (vehicle_changed) { if (vehiclelist_frame_t* f = (vehiclelist_frame_t*)win_get_magic(magic_vehiclelist)) { f->fill_list(); } } } // city road (try to use always a timeline) if (way_desc_t const* city_road_test = settings.get_city_road_type(current_month) ) { city_road = city_road_test; } else { DBG_MESSAGE("karte_t::new_month()","Month %d has started", last_month); city_road = way_builder_t::weg_search(road_wt,50,get_timeline_year_month(),type_flat); } weg_t::set_cityroad_speedlimit(settings.get_city_road_speed_limit(get_timeline_year_month() / 12)); } else { // defaults city_road = settings.get_city_road_type(0); if(city_road==NULL) { city_road = way_builder_t::weg_search(road_wt,50,0,type_flat); } weg_t::set_cityroad_speedlimit(50); } } // returns the current speed record sint32 karte_t::get_record_speed( waytype_t w ) const { return records->get_record_speed(w); } // sets the new speed record void karte_t::notify_record( convoihandle_t cnv, sint32 max_speed, koord3d k ) { records->notify_record(cnv, max_speed, k, current_month); } void karte_t::set_schedule_counter() { // do not call this from gui when playing in network mode! assert( (get_random_mode() & INTERACTIVE_RANDOM) == 0 ); schedule_counter++; } void karte_t::step() { DBG_DEBUG4("karte_t::step", "start step"); uint32 step_start_time = dr_time(); // calculate delta_t before handling overflow in ticks uint32 delta_t = ticks - last_step_ticks; // first: check for new month if(ticks > next_month_ticks) { if( next_month_ticks > next_month_ticks+karte_t::ticks_per_world_month ) { // avoid overflow here ... dbg->warning("karte_t::step()", "Ticks were overflowing => reset"); ticks %= karte_t::ticks_per_world_month; next_month_ticks %= karte_t::ticks_per_world_month; } next_month_ticks += karte_t::ticks_per_world_month; DBG_DEBUG4("karte_t::step", "calling new_month"); new_month(); } DBG_DEBUG4("karte_t::step", "time calculations"); if( step_mode==NORMAL ) { /* Try to maintain a decent pause, with a step every 170-250 ms (~5,5 simloops/s) * Also avoid too large or negative steps */ // needs plausibility check?!? if(delta_t>10000) { dbg->error( "karte_t::step()", "delta_t (%u) out of bounds!", delta_t ); last_step_ticks = ticks; next_step_time = step_start_time+10; return; } idle_time = 0; last_step_nr[steps%32] = ticks; next_step_time = step_start_time + (3200/get_time_multiplier()); } else if( step_mode==FAST_FORWARD ) { // fast forward first: get average simloops (i.e. calculate acceleration) last_step_nr[steps%32] = step_start_time; int last_5_simloops = simloops; if( last_step_nr[(steps+32-5)%32] < last_step_nr[steps%32] ) { // since 5 steps=1s last_5_simloops = (1000) / (last_step_nr[steps%32]-last_step_nr[(steps+32-5)%32]); } if( last_step_nr[(steps+1)%32] < last_step_nr[steps%32] ) { simloops = (10000*32) / (last_step_nr[steps%32]-last_step_nr[(steps+1)%32]); } // now try to approach the target speed if(last_5_simloops0) { idle_time --; } } else if(simloops>8u*env_t::max_acceleration) { if(idle_time + 10u < get_frame_time()) { idle_time ++; } } // cap it ... if( idle_time + 10u >= get_frame_time()) { idle_time = get_frame_time()-10; } next_step_time = step_start_time+idle_time; } else { // network mode } // now do the step ... last_step_ticks = ticks; steps ++; // to make sure the tick counter will be updated INT_CHECK("karte_t::step"); // check for pending seasons change const bool season_change = pending_season_change > 0; const bool snowline_change = pending_snowline_change > 0; if( season_change || snowline_change ) { DBG_DEBUG4("karte_t::step", "pending_season_change"); // process const uint32 end_count = min( cached_grid_size.x * cached_grid_size.y, tile_counter + max( 16384, cached_grid_size.x * cached_grid_size.y / 16 ) ); while( tile_counter < end_count ) { plan[tile_counter].check_season_snowline( season_change, snowline_change ); tile_counter++; if( (tile_counter & 0x3FF) == 0 ) { INT_CHECK("karte_t::step"); } } if( tile_counter >= (uint32)cached_grid_size.x * (uint32)cached_grid_size.y ) { if( season_change ) { pending_season_change--; } if( snowline_change ) { pending_snowline_change--; } tile_counter = 0; } } // to make sure the tick counter will be updated INT_CHECK("karte_t::step"); DBG_DEBUG4("karte_t::step", "step convois"); // since convois will be deleted during stepping, we need to step backwards for (sint32 i = (sint32)convoi_array.get_count(); i-- > 0; ) { convoihandle_t cnv = convoi_array[i]; cnv->step(); if((i&15)==0) { INT_CHECK("simworld 1947"); } } // now step all towns (to generate passengers) DBG_DEBUG4("karte_t::step", "step cities"); sint64 bev=0; for (int i = 0; i < cities.get_count(); i++ ) { stadt_t* const c = cities[i]; c->step(delta_t); bev += c->get_finance_history_month(0, HIST_CITIZENS); } // the inhabitants stuff finance_history_month[0][WORLD_CITIZENS] = bev; DBG_DEBUG4("karte_t::step", "step factories"); for (int i = 0; i < all_factories.get_count(); i++) { all_factories[i]->step(delta_t); } finance_history_year[0][WORLD_FACTORIES] = finance_history_month[0][WORLD_FACTORIES] = all_factories.get_count(); // step powerlines - required order: powernet, pumpe then senke DBG_DEBUG4("karte_t::step", "step poweline stuff"); powernet_t::step_all(delta_t); pumpe_t::sync_handler(delta_t); // senke_t::step_all(delta_t); // not needed, handeld by sunc_step already DBG_DEBUG4("karte_t::step", "step players"); // then step all players for( int i=0; istep(); } } DBG_DEBUG4("karte_t::step", "step halts"); haltestelle_t::step_all(); // ok, next step INT_CHECK("simworld 1975"); recalc_season_snowline(true); // number of playing clients changed if( env_t::server && last_clients!=socket_list_t::get_playing_clients() ) { if( env_t::server_announce ) { // inform the master server announce_server( karte_t::SERVER_ANNOUNCE_HEARTBEAT ); } // check if player has left and send message for(uint32 i=0; i < socket_list_t::get_count(); i++) { socket_info_t& info = socket_list_t::get_client(i); if (info.state == socket_info_t::has_left) { nwc_nick_t::server_tools(this, i, nwc_nick_t::FAREWELL, NULL); info.state = socket_info_t::inactive; } } last_clients = socket_list_t::get_playing_clients(); // add message via tool cbuffer_t buf; buf.printf("%d,", chat_message_t::DO_NOT_SAVE_MSG | chat_message_t::DO_NO_LOG_MSG); buf.printf(translator::translate("Now %u clients connected.", settings.get_name_language_id()), last_clients); tool_t *tmp_tool = create_tool( TOOL_ADD_MESSAGE | GENERAL_TOOL ); tmp_tool->set_default_param( buf ); bool suspended; call_work_api(tmp_tool, get_active_player(), koord3d::invalid, suspended); // work is done (or command sent), it is safe to delete immediately delete tmp_tool; } if( get_scenario()->is_scripted() ) { get_scenario()->step(); } if (selected_tool[active_player_nr]) { if (exec_script_base_t* esb = dynamic_cast(selected_tool[active_player_nr])) { esb->step(get_active_player()); } } DBG_DEBUG4("karte_t::step", "end"); } // recalculates world statistics for older versions void karte_t::restore_history(bool restore_transported_only) { // update total transported, including passenger and mail for( int m=min(MAX_WORLD_HISTORY_MONTHS,MAX_PLAYER_HISTORY_MONTHS)-1; m>0; m-- ) { sint64 transported = 0; for( uint i=0; iget_finance()->calc_finance_history(); transported += players[i]->get_finance()->get_history_veh_month( TT_ALL, m, ATV_TRANSPORTED ); } } finance_history_month[m][WORLD_TRANSPORTED_GOODS] = max(transported, finance_history_month[m][WORLD_TRANSPORTED_GOODS]); } for( int y=min(MAX_WORLD_HISTORY_YEARS,MAX_CITY_HISTORY_YEARS)-1; y>0; y-- ) { sint64 transported_year = 0; for( uint i=0; iget_finance()->get_history_veh_year( TT_ALL, y, ATV_TRANSPORTED ); } } finance_history_year[y][WORLD_TRANSPORTED_GOODS] = max(transported_year, finance_history_year[y][WORLD_TRANSPORTED_GOODS]); } if (restore_transported_only) { return; } last_month_bev = -1; for( int m=12-1; m>0; m-- ) { // now step all towns (to generate passengers) sint64 bev=0; sint64 total_pas = 1, trans_pas = 0; sint64 total_mail = 1, trans_mail = 0; sint64 total_goods = 1, supplied_goods = 0; for(stadt_t* const i : cities) { bev += i->get_finance_history_month(m, HIST_CITIZENS); trans_pas += i->get_finance_history_month(m, HIST_PAS_TRANSPORTED); total_pas += i->get_finance_history_month(m, HIST_PAS_GENERATED); trans_mail += i->get_finance_history_month(m, HIST_MAIL_TRANSPORTED); total_mail += i->get_finance_history_month(m, HIST_MAIL_GENERATED); supplied_goods += i->get_finance_history_month(m, HIST_GOODS_RECEIVED); total_goods += i->get_finance_history_month(m, HIST_GOODS_NEEDED); } // the inhabitants stuff if(last_month_bev == -1) { last_month_bev = bev; } finance_history_month[m][WORLD_GROWTH] = bev-last_month_bev; finance_history_month[m][WORLD_CITIZENS] = bev; last_month_bev = bev; // transportation ratio and total number finance_history_month[m][WORLD_PAS_RATIO] = (10000*trans_pas)/total_pas; finance_history_month[m][WORLD_PAS_GENERATED] = total_pas-1; finance_history_month[m][WORLD_MAIL_RATIO] = (10000*trans_mail)/total_mail; finance_history_month[m][WORLD_MAIL_GENERATED] = total_mail-1; finance_history_month[m][WORLD_GOODS_RATIO] = (10000*supplied_goods)/total_goods; } sint64 bev_last_year = -1; for( int y=min(MAX_WORLD_HISTORY_YEARS,MAX_CITY_HISTORY_YEARS)-1; y>0; y-- ) { // now step all towns (to generate passengers) sint64 bev=0; sint64 total_pas_year = 1, trans_pas_year = 0; sint64 total_mail_year = 1, trans_mail_year = 0; sint64 total_goods_year = 1, supplied_goods_year = 0; for(stadt_t* const i : cities) { bev += i->get_finance_history_year(y, HIST_CITIZENS); trans_pas_year += i->get_finance_history_year(y, HIST_PAS_TRANSPORTED); total_pas_year += i->get_finance_history_year(y, HIST_PAS_GENERATED); trans_mail_year += i->get_finance_history_year(y, HIST_MAIL_TRANSPORTED); total_mail_year += i->get_finance_history_year(y, HIST_MAIL_GENERATED); supplied_goods_year += i->get_finance_history_year(y, HIST_GOODS_RECEIVED); total_goods_year += i->get_finance_history_year(y, HIST_GOODS_NEEDED); } // the inhabitants stuff if(bev_last_year == -1) { bev_last_year = bev; } finance_history_year[y][WORLD_GROWTH] = bev-bev_last_year; finance_history_year[y][WORLD_CITIZENS] = bev; bev_last_year = bev; // transportation ratio and total number finance_history_year[y][WORLD_PAS_RATIO] = (10000*trans_pas_year)/total_pas_year; finance_history_year[y][WORLD_PAS_GENERATED] = total_pas_year-1; finance_history_year[y][WORLD_MAIL_RATIO] = (10000*trans_mail_year)/total_mail_year; finance_history_year[y][WORLD_MAIL_GENERATED] = total_mail_year-1; finance_history_year[y][WORLD_GOODS_RATIO] = (10000*supplied_goods_year)/total_goods_year; } // fix current month/year update_history(); } void karte_t::update_history() { finance_history_year[0][WORLD_CONVOIS] = finance_history_month[0][WORLD_CONVOIS] = convoi_array.get_count(); finance_history_year[0][WORLD_FACTORIES] = finance_history_month[0][WORLD_FACTORIES] = all_factories.get_count(); // now step all towns (to generate passengers) sint64 bev=0; sint64 total_pas = 1, trans_pas = 0; sint64 total_mail = 1, trans_mail = 0; sint64 total_goods = 1, supplied_goods = 0; sint64 total_pas_year = 1, trans_pas_year = 0; sint64 total_mail_year = 1, trans_mail_year = 0; sint64 total_goods_year = 1, supplied_goods_year = 0; for(stadt_t* const i : cities) { bev += i->get_finance_history_month(0, HIST_CITIZENS); trans_pas += i->get_finance_history_month(0, HIST_PAS_TRANSPORTED); total_pas += i->get_finance_history_month(0, HIST_PAS_GENERATED); trans_mail += i->get_finance_history_month(0, HIST_MAIL_TRANSPORTED); total_mail += i->get_finance_history_month(0, HIST_MAIL_GENERATED); supplied_goods += i->get_finance_history_month(0, HIST_GOODS_RECEIVED); total_goods += i->get_finance_history_month(0, HIST_GOODS_NEEDED); trans_pas_year += i->get_finance_history_year( 0, HIST_PAS_TRANSPORTED); total_pas_year += i->get_finance_history_year( 0, HIST_PAS_GENERATED); trans_mail_year += i->get_finance_history_year( 0, HIST_MAIL_TRANSPORTED); total_mail_year += i->get_finance_history_year( 0, HIST_MAIL_GENERATED); supplied_goods_year += i->get_finance_history_year( 0, HIST_GOODS_RECEIVED); total_goods_year += i->get_finance_history_year( 0, HIST_GOODS_NEEDED); } finance_history_month[0][WORLD_GROWTH] = bev-last_month_bev; finance_history_year[0][WORLD_GROWTH] = bev - (finance_history_year[1][WORLD_CITIZENS]==0 ? finance_history_month[0][WORLD_CITIZENS] : finance_history_year[1][WORLD_CITIZENS]); // the inhabitants stuff finance_history_year[0][WORLD_TOWNS] = finance_history_month[0][WORLD_TOWNS] = cities.get_count(); finance_history_year[0][WORLD_CITIZENS] = finance_history_month[0][WORLD_CITIZENS] = bev; finance_history_month[0][WORLD_GROWTH] = bev-last_month_bev; finance_history_year[0][WORLD_GROWTH] = bev - (finance_history_year[1][WORLD_CITIZENS]==0 ? finance_history_month[0][WORLD_CITIZENS] : finance_history_year[1][WORLD_CITIZENS]); // transportation ratio and total number finance_history_month[0][WORLD_PAS_RATIO] = (10000*trans_pas)/total_pas; finance_history_month[0][WORLD_PAS_GENERATED] = total_pas-1; finance_history_month[0][WORLD_MAIL_RATIO] = (10000*trans_mail)/total_mail; finance_history_month[0][WORLD_MAIL_GENERATED] = total_mail-1; finance_history_month[0][WORLD_GOODS_RATIO] = (10000*supplied_goods)/total_goods; finance_history_year[0][WORLD_PAS_RATIO] = (10000*trans_pas_year)/total_pas_year; finance_history_year[0][WORLD_PAS_GENERATED] = total_pas_year-1; finance_history_year[0][WORLD_MAIL_RATIO] = (10000*trans_mail_year)/total_mail_year; finance_history_year[0][WORLD_MAIL_GENERATED] = total_mail_year-1; finance_history_year[0][WORLD_GOODS_RATIO] = (10000*supplied_goods_year)/total_goods_year; // update total transported, including passenger and mail sint64 transported = 0; sint64 transported_year = 0; for( uint i=0; iget_finance()->calc_finance_history(); transported += players[i]->get_finance()->get_history_veh_month( TT_ALL, 0, ATV_TRANSPORTED_GOOD ); transported_year += players[i]->get_finance()->get_history_veh_year( TT_ALL, 0, ATV_TRANSPORTED_GOOD ); } } finance_history_month[0][WORLD_TRANSPORTED_GOODS] = transported; finance_history_year[0][WORLD_TRANSPORTED_GOODS] = transported_year; } slist_tpl *karte_t::find_squares(sint16 w, sint16 h, climate_bits cl, sint16 old_x, sint16 old_y) const { slist_tpl * list = new slist_tpl(); koord start; int last_y = -1; DBG_DEBUG("karte_t::finde_plaetze()","for size (%i,%i) in map (%i,%i)",w,h,get_size().x,get_size().y ); for(start.x=0; start.xinsert(start); } else { // Optimiert fuer groessere Felder, hehe! // Die Idee: wenn bei 2x2 die untere Reihe nicht geht, koennen // wir gleich 2 tiefer weitermachen! start.y = last_y; } } } return list; } /** * Play a sound, but only if near enough. * Sounds are muted by distance and clipped completely if too far away. * * @param type */ bool karte_t::play_sound_area_clipped(koord const k, uint16 const idx, sound_type_t type ) const { if(is_sound && zeiger!=NULL) { const uint32 dist = koord_distance( k, zeiger->get_pos() ); if(dist < 100) { uint8 const volume = (uint8)((255U * env_t::sound_distance_scaling) / (env_t::sound_distance_scaling + dist*dist)); if (volume > 8) { sound_play(idx, volume, type ); } } return dist < 25; } return false; } void karte_t::save(const char *filename, bool autosave, const char *version_str, bool silent ) { dbg->message("karte_t::save", "%s game to '%s', version=%s, ticks=%u", autosave ? "Auto-saving" : "Saving", filename, version_str, ticks); loadsave_t file; std::string savename = filename; savename[savename.length()-1] = '_'; gfx->set_show_load_cursor(true); const loadsave_t::mode_t mode = autosave ? loadsave_t::autosave_mode : loadsave_t::save_mode; const int save_level = autosave ? loadsave_t::autosave_level : loadsave_t::save_level; if( file.wr_open( savename.c_str(), mode, save_level, env_t::pak_name.c_str(), version_str ) != loadsave_t::FILE_STATUS_OK ) { create_win(new news_img("Kann Spielstand\nnicht speichern.\n"), w_info, magic_none); dbg->error("karte_t::save", "Cannot open file '%s' for writing! Check permissions!", savename.c_str()); } else { save(&file,silent); const char *save_err = file.close(); if(save_err) { static char err_str[512]; sprintf( err_str, translator::translate("Error during saving:\n%s"), save_err ); create_win( new news_img(err_str), w_time_delete, magic_none); } else { dr_rename( savename.c_str(), filename ); if(!silent) { create_win( new news_img("Spielstand wurde\ngespeichert!\n"), w_time_delete, magic_none); // update the filename, if no autosave settings.set_filename(filename); } } reset_interaction(); } gfx->set_show_load_cursor(false); } void karte_t::save(loadsave_t *file,bool silent) { bool needs_redraw = false; loadingscreen_t *ls = NULL; DBG_MESSAGE("karte_t::save(loadsave_t *file)", "start"); if(!silent) { ls = new loadingscreen_t( translator::translate("Saving map ..."), get_size().y ); } // rotate the map until it can be saved completely for( int i=0; i<4 && nosave_warning; i++ ) { rotate90(); needs_redraw = true; } // seems not successful if(nosave_warning) { // but then we try to rotate until only warnings => some buildings may be broken, but factories should be fine for( int i=0; i<4 && nosave; i++ ) { rotate90(); needs_redraw = true; } if( nosave ) { dbg->error( "karte_t::save()", "Map cannot be saved in any rotation!" ); create_win( new news_img("Map may be not saveable in any rotation!"), w_info, magic_none); // still broken, but we try anyway to save it ... } } // only broken buildings => just warn if(nosave_warning) { dbg->error( "karte_t::save()","Some buildings may be broken by saving!" ); } /* If the current tool is a two_click_tool_t, call cleanup() in order to delete dummy grounds (tunnel + monorail preview) * THIS MUST NOT BE DONE IN NETWORK MODE! */ for( uint8 sp_nr=0; sp_nr(selected_tool[sp_nr])) { tool->cleanup(); } } file->set_buffered(true); rdwr_gamestate(file, ls); for(int i=0; iis_version_less(101, 0)) { if( i<8 ) { if( players[i] ) { players[i]->rdwr(file); } else { // simulate old ones ... player_t *player = new player_t( i ); player->rdwr(file); delete player; } } } else if( players[i] ) { players[i]->rdwr(file); } } DBG_MESSAGE("karte_t::rdwr_gamestate()", "saved players"); // saving messages if( file->is_version_atleast(102, 5) ) { msg->rdwr(file); } DBG_MESSAGE("karte_t::save(loadsave_t *file)", "saved messages"); // centered on what? sint32 dummy = viewport->get_world_position().x; file->rdwr_long(dummy); dummy = viewport->get_world_position().y; file->rdwr_long(dummy); if(file->is_version_atleast(99, 18)) { // most recent version is 99018 for (int year = 0; yearrdwr_longlong(finance_history_year[year][cost_type]); } } for (int month = 0;monthrdwr_longlong(finance_history_month[month][cost_type]); } } } // finally a possible scenario scenario->rdwr( file ); if( file->is_version_atleast(112, 8) ) { xml_tag_t t( file, "motd_t" ); if (env_t::networkmode && settings.motd.size()>0) { const char* motd = settings.motd.c_str(); file->rdwr_str(motd); } else { // no message plainstring motd(""); file->rdwr_str( motd ); } } if (file->is_version_atleast(124, 1)) { chat_msg->rdwr(file); } if (file->is_version_atleast(124, 2)) { records->rdwr(file); } file->rdwr_byte( active_player_nr ); // save all open windows (upon request) rdwr_all_win(file); file->set_buffered(false); if(needs_redraw) { update_map(); } if(!silent) { delete ls; } } void karte_t::switch_server( bool start_server, bool port_forwarding ) { if( !start_server ) { // end current server session if( env_t::server ) { // take down server announce_server(karte_t::SERVER_ANNOUNCE_GOODBYE); remove_port_forwarding( env_t::server ); } network_core_shutdown(); env_t::easy_server = 0; clear_random_mode( INTERACTIVE_RANDOM ); step_mode = NORMAL; reset_timer(); clear_command_queue(); last_active_player_nr = active_player_nr; if( port_forwarding && env_t::fps<=15 ) { env_t::fps = 25; } } else { // convert current game into server game if( env_t::server ) { // kick all clients out network_reset_server(); } else { // now start a server with defaults env_t::networkmode = network_init_server( env_t::server_port, env_t::listen ); if( env_t::networkmode ) { // query IP and try to open ports on router char IP[256], altIP[256]; if( port_forwarding && prepare_for_server( IP, altIP, env_t::server_port ) ) { // we have forwarded a port in router, so we can continue env_t::server_dns = IP; if( env_t::server_name.empty() ) { env_t::server_name = std::string("Server at ")+IP; } env_t::server_alt_dns = altIP; env_t::server_announce = 1; env_t::easy_server = 1; if( env_t::fps>15 ) { env_t::fps = 15; } } reset_timer(); clear_command_queue(); // meaningless to use a locked map; there are passwords now settings.set_allow_player_change(true); // language of map becomes server language settings.set_name_language_iso(translator::get_lang()->iso_base); nwc_auth_player_t::init_player_lock_server(this); last_active_player_nr = active_player_nr; } } } } // just the preliminaries, opens the file, checks the versions ... bool karte_t::load(const char *filename) { cbuffer_t name; bool ok = false; bool restore_player_nr = false; bool server_reload_pwd_hashes = false; mute_sound(true); gfx->set_show_load_cursor(true); loadsave_t file; // clear hash table with missing paks (may cause some small memory loss though) pakset_manager_t::clear_missing_paks(); dbg->message("karte_t::load", "Loading game from '%s'", filename); // reloading same game? Remember pos const koord oldpos = settings.get_filename()[0]>0 && strncmp(filename,settings.get_filename(),strlen(settings.get_filename()))==0 ? viewport->get_world_position() : koord::invalid; if( strstart(filename, "net:") ) { // probably finish network mode? if( env_t::networkmode ) { network_core_shutdown(); } dr_chdir( env_t::user_dir ); const char *err = network_connect(filename+4, this); if(err) { create_win( new news_img(err), w_info, magic_none ); gfx->set_show_load_cursor(false); step_mode = NORMAL; return false; } else { env_t::networkmode = true; name.printf( "client%i-network.sve", network_get_client_id() ); restore_player_nr = strcmp( last_network_game.c_str(), filename )==0; if( !restore_player_nr ) { last_network_game = filename; } } } else { // probably finish network mode first? if( env_t::networkmode ) { if( env_t::server ) { char fn[256]; sprintf( fn, "server%d-network.sve", env_t::server ); if( strcmp(filename, fn) != 0 ) { // stay in networkmode, but disconnect clients dbg->warning("karte_t::load", "Disconnecting all clients"); } else { // read password hashes from separate file // as they are not in the savegame to avoid sending them over network server_reload_pwd_hashes = true; } } else { // check, if reload during sync char fn[256]; sprintf( fn, "client%i-network.sve", network_get_client_id() ); if( strcmp(filename,fn)!=0 ) { // no sync => finish network mode dbg->warning("karte_t::load","finished network mode"); network_disconnect(); // closing the socket will tell the server, I am away too } } } name.append(filename); } if(file.rd_open(name) != loadsave_t::FILE_STATUS_OK) { if( file.get_version_int()==0 || file.get_version_int()>loadsave_t::int_version(SAVEGAME_VER_NR, NULL ) ) { dbg->warning("karte_t::load()", translator::translate("WRONGSAVE") ); create_win( new news_img("WRONGSAVE"), w_info, magic_none ); } else { dbg->warning("karte_t::load()", translator::translate("Kann Spielstand\nnicht laden.\n") ); create_win(new news_img("Kann Spielstand\nnicht laden.\n"), w_info, magic_none); } } else if(file.is_version_less(84, 6)) { // too old dbg->warning("karte_t::load()", translator::translate("WRONGSAVE") ); create_win(new news_img("WRONGSAVE"), w_info, magic_none); } else { DBG_MESSAGE("karte_t::load()","Savegame version is %u", file.get_version_int()); file.set_buffered(true); load(&file); if( env_t::server ) { // since the sync should have been the last command on the clients due to tcp, only clear command queue on the server clear_command_queue(); step_mode = FIX_RATIO; // meaningless to use a locked map; there are passwords now settings.set_allow_player_change(true); // language of map becomes server language settings.set_name_language_iso(translator::get_lang()->iso_base); if( server_reload_pwd_hashes ) { char fn[256]; sprintf( fn, "server%d-pwdhash.sve", env_t::server ); loadsave_t pwdrdfile; if(pwdrdfile.rd_open(fn) == loadsave_t::FILE_STATUS_OK ) { rdwr_player_password_hashes( &pwdrdfile); // correct locking info nwc_auth_player_t::init_player_lock_server(this); pwdrdfile.close(); } } } else if( env_t::networkmode ) { step_mode = PAUSE_FLAG|FIX_RATIO; switch_active_player( last_active_player_nr, true ); if( is_within_limits(oldpos) ) { // go to position when last disconnected viewport->change_world_position( oldpos ); } } else { step_mode = NORMAL; // save current map settings only in non-networkmode env_t::default_settings = settings; } ok = true; file.close(); if( !scenario->rdwr_ok() ) { // error during loading of savegame of scenario const char* err = scenario->get_error_text(); if (err == NULL) { err = "Loading scenario failed."; } create_win( new news_img( err ), w_info, magic_none); delete scenario; scenario = new scenario_t(this); } else if( !env_t::networkmode || !env_t::restore_UI ) { // warning message about missing paks pakset_manager_t::warn_if_paks_missing(); // will not notify if we restore everything if( scenario->is_scripted() ) { scenario->open_info_win(); } create_win( new news_img("Spielstand wurde\ngeladen!\n"), w_time_delete, magic_none); } set_dirty(); reset_timer(); recalc_average_speed(); mute_sound(false); tool_t::update_toolbars(); toolbar_last_used_t::last_used_tools->clear(); set_tool( tool_t::general_tool[TOOL_QUERY], get_active_player() ); } settings.set_filename(filename); gfx->set_show_load_cursor(false); return ok; } #ifdef MULTI_THREAD static pthread_mutex_t height_mutex; static recursive_mutex_maker_t height_mutex_maker(height_mutex); #endif player_t* karte_t::get_player_or_create(uint8 n) { n &= 15; if (players[n]) { return players[n]; } dbg->error("get_player()", "No player %d in this game -> make a new one", n); init_new_player(n, player_t::HUMAN); return players[n]; } void karte_t::plans_finish_rd( sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max ) { sint8 min_h = 127, max_h = -128; for( int y = y_min; y < y_max; y++ ) { for( int x = x_min; x < x_max; x++ ) { planquadrat_t *plan = access_nocheck(x,y); plan->sort_haltlist(); const int boden_count = plan->get_boden_count(); for( int schicht = 0; schicht < boden_count; schicht++ ) { grund_t *gr = plan->get_boden_bei(schicht); if( min_h > gr->get_hoehe() ) { min_h = gr->get_hoehe(); } else if( max_h < gr->get_hoehe() ) { max_h = gr->get_hoehe(); } for( int n = 0; n < gr->obj_count(); n++ ) { obj_t *obj = gr->obj_bei(n); if(obj) { obj->finish_rd(); } } if( load_version<=111000 && gr->ist_natur() ) { gr->sort_trees(); } gr->calc_image(); } } } // update heights #ifdef MULTI_THREAD pthread_mutex_lock( &height_mutex ); if( min_height > min_h ) { min_height = min_h; } if( max_height < max_h ) { max_height = max_h; } pthread_mutex_unlock( &height_mutex ); #else min_height = min_h; max_height = max_h; #endif } void karte_t::load(loadsave_t *file) { intr_disable(); dbg->message("karte_t::load()", "Prepare for loading" ); for( uint8 sp_nr=0; sp_nr(selected_tool[sp_nr])) { tool->cleanup(); } } destroy_all_win(true); clear_random_mode(~LOAD_RANDOM); set_random_mode(LOAD_RANDOM); destroy(); loadingscreen_t ls(translator::translate("Loading map ..."), 1, true, true ); tile_counter = 0; simloops = 60; // jetzt geht das Laden los dbg->message("karte_t::load", "File version: %u", file->get_version_int()); rdwr_gamestate(file, &ls); // now the player can be loaded for(int i=0; irdwr(file); } ls.set_progress( (get_size().y*3)/2+128+8*i ); } DBG_MESSAGE("karte_t::rdwr_gamestate()", "players loaded"); // loading messages if( file->is_version_atleast(102, 5) ) { msg->rdwr(file); } else if( !env_t::networkmode ) { msg->clear(); chat_msg->clear(); } DBG_MESSAGE("karte_t::load()", "messages loaded"); // nachdem die welt jetzt geladen ist koennen die Blockstrecken neu // angelegt werden old_blockmanager_t::finish_rd(this); DBG_MESSAGE("karte_t::load()", "blocks loaded"); sint32 mi,mj; file->rdwr_long(mi); file->rdwr_long(mj); DBG_MESSAGE("karte_t::load()", "Setting view to %d,%d", mi,mj); viewport->change_world_position( koord3d(mi,mj,0) ); // right season for recalculations recalc_season_snowline(false); DBG_MESSAGE("karte_t::load()", "%d ways loaded",weg_t::get_alle_wege().get_count()); ls.set_progress( (get_size().y*3)/2+256 ); world_xy_loop(&karte_t::plans_finish_rd, SYNCX_FLAG); // update power nets with correct power powernet_t::step_all(1); if( file->is_version_less(112, 7) ) { // set transitions - has to be done after plans_finish_rd world_xy_loop(&karte_t::recalc_transitions_loop, 0); } ls.set_progress( (get_size().y*3)/2+256+get_size().y/8 ); DBG_MESSAGE("karte_t::load()", "laden_abschliesen for tiles finished" ); // must finish loading cities first before cleaning up factories weighted_vector_tpl new_cities(cities.get_count() + 1); for(stadt_t* const s : cities) { s->finish_rd(); s->recalc_target_cities(); new_cities.append(s, s->get_einwohner()); INT_CHECK("simworld 1278"); } swap(cities, new_cities); DBG_MESSAGE("karte_t::load()", "cities initialized"); ls.set_progress( (get_size().y*3)/2+256+get_size().y/4 ); DBG_MESSAGE("karte_t::load()", "clean up factories"); for(fabrik_t* const f : all_factories) { f->finish_rd(); } DBG_MESSAGE("karte_t::load()", "%d factories loaded", all_factories.get_count()); // old versions did not save factory connections if(file->is_version_less(99, 14)) { sint32 const temp_min = settings.get_factory_worker_minimum_towns(); sint32 const temp_max = settings.get_factory_worker_maximum_towns(); // this needs to avoid the first city to be connected to all town settings.set_factory_worker_minimum_towns(0); settings.set_factory_worker_maximum_towns(cities.get_count() + 1); for(stadt_t* const i : cities) { i->verbinde_fabriken(); } settings.set_factory_worker_minimum_towns(temp_min); settings.set_factory_worker_maximum_towns(temp_max); } ls.set_progress( (get_size().y*3)/2+256+get_size().y/3 ); // resolve dummy stops into real stops first ... for(halthandle_t const i : haltestelle_t::get_alle_haltestellen()) { if (i->get_owner() && i->existiert_in_welt()) { i->finish_rd(); } } // ... before removing dummy stops for( vector_tpl::const_iterator i=haltestelle_t::get_alle_haltestellen().begin(); i!=haltestelle_t::get_alle_haltestellen().end(); ) { halthandle_t const h = *i; if( !h->get_owner() || !h->existiert_in_welt() ) { // this stop was only needed for loading goods ... haltestelle_t::destroy(h); // remove from list } else { ++i; } } ls.set_progress( (get_size().y*3)/2+256+(get_size().y*3)/8 ); // adding lines and other stuff for convois for(unsigned i=0; ifinish_rd(); // was deleted during loading => use same position again if(!cnv.is_bound()) { i--; } } haltestelle_t::end_load_game(); // register all line stops and change line types, if needed for(int i=0; ifinish_rd(); } } #ifdef DEBUG uint32 dt = dr_time(); #endif // recalculate halt connections haltestelle_t::reset_routing(); do { haltestelle_t::step_all(); } while ( haltestelle_t::get_rerouting_status()==RECONNECTING ); #ifdef DEBUG dbg->message("karte_t::load()", "for all haltstellen_t took %ld ms", dr_time()-dt ); #endif #if 0 // reroute goods for benchmarking dt = dr_time(); for(halthandle_t const i : haltestelle_t::get_alle_haltestellen()) { sint16 dummy = 0x7FFF; i->reroute_goods(dummy); } DBG_MESSAGE("reroute_goods()","for all haltstellen_t took %ld ms", dr_time()-dt ); #endif // load history/create world history if(file->is_version_less(99, 18)) { restore_history(false); } else { for (int year = 0; yearrdwr_longlong(finance_history_year[year][cost_type]); } } for (int month = 0;monthrdwr_longlong(finance_history_month[month][cost_type]); } } last_month_bev = finance_history_month[1][WORLD_CITIZENS]; if (file->is_version_atleast(112, 5) && file->is_version_less(120, 6)) { restore_history(true); } } // finally: do we run a scenario? if(file->is_version_atleast(99, 18)) { scenario->rdwr(file); } // restore locked state // network game this will be done in nwc_sync_t::do_command if( !env_t::networkmode ) { for( uint8 i=0; icheck_unlock( player_password_hash[i] ); } } } // initialize lock info for local server player // if call from sync command, lock info will be corrected there if( env_t::server ) { nwc_auth_player_t::init_player_lock_server(this); } // show message about server if( file->is_version_atleast(112, 8) ) { xml_tag_t t( file, "motd_t" ); char msg[32766]; file->rdwr_str( msg, 32766 ); if (env_t::server) { // maybe show message about server dr_chdir(env_t::user_dir); DBG_MESSAGE("karte_t::save(loadsave_t *file)", "motd filename %s", env_t::server_motd_filename.c_str()); if (FILE* fmotd = dr_fopen(env_t::server_motd_filename.c_str(), "r")) { struct stat st; stat(env_t::server_motd_filename.c_str(), &st); sint32 len = min(32760, st.st_size + 1); char* motd = (char*)malloc(len); if (fread(motd, len - 1, 1, fmotd) != 1) { len = 1; } fclose(fmotd); motd[len - 1] = 0; settings.motd = motd; free(motd); } } else if (env_t::networkmode) { settings.motd = msg; if (*msg && !env_t::restore_UI) { // if not empty ... help_frame_t* win = new help_frame_t(); win->set_text(settings.motd.c_str()); create_win(win, w_info, magic_motd); } } else { settings.motd = ""; } } if (file->is_version_atleast(124, 1)) { chat_msg->rdwr(file); } else { // maybe move messages into chat_messages? } if (file->is_version_atleast(124, 2)) { records->rdwr(file); } if( file->is_version_atleast(102, 4) ) { if( env_t::restore_UI ) { file->rdwr_byte( active_player_nr ); active_player = players[active_player_nr]; /* restore all open windows * otherwise it will be ignored * which is save, since it is the end of file */ rdwr_all_win( file ); } } file->set_buffered(false); clear_random_mode(LOAD_RANDOM); // loading finished, reset savegame version to current load_version = loadsave_t::int_version( env_t::savegame_version_str, NULL ); dbg->warning("karte_t::load()","loaded savegame from %i/%i, next month=%i, ticks=%i (per month=1<<%i)",last_month,last_year,next_month_ticks,ticks,karte_t::ticks_per_world_month_shift); } void karte_t::rdwr_gamestate(loadsave_t *file, loadingscreen_t *ls) { if (file->is_loading()) { // zum laden vorbereiten -> tabelle loeschen powernet_t::new_world(); senke_t::new_world(); script_api::new_world(); settings = env_t::default_settings; } else { // do not set value for empty player and correct existing players for( int i=0; ierror("karte_t::rdwr_gamestate()", "State %d of non-existing player %d correct to 0 (EMPTY)", settings.get_player_type(i), i); settings.set_player_type(i, player_t::EMPTY); } } else if(settings.get_player_type(i)-1>= player_t::MAX_AI-1) { dbg->error("karte_t::rdwr_gamestate()", "State %d of player %d correct to 1 (HUMAN)", settings.get_player_type(i), i); settings.set_player_type(i, player_t::HUMAN); } } } settings.rdwr(file); if (file->is_loading()) { // some functions (finish_rd) need to know what version was loaded load_version = file->get_version_int(); loaded_rotation = settings.get_rotation(); } if (file->is_version_atleast(122, 1)) { // rdwr the entire RNG sate simrand_rdwr(file); } if (file->is_loading()) { if( env_t::networkmode ) { // To have games synchronized, transfer random counter too // Superseded by simrand_rdwr in newer versions if (file->is_version_less(122, 1)) { setsimrand(settings.get_random_counter(), 0xFFFFFFFFu ); } } // it will use the saved language of the game translator::init_custom_names(settings.get_name_language_id()); if( !env_t::networkmode || (env_t::server && socket_list_t::get_playing_clients()==0) ) { if (settings.get_allow_player_change() && env_t::default_settings.get_use_timeline() < 2) { // not locked => eventually switch off timeline settings, if explicitly stated settings.set_use_timeline(env_t::default_settings.get_use_timeline()); DBG_DEBUG("karte_t::rdwr_gamestate()", "timeline: reset to %i", env_t::default_settings.get_use_timeline() ); } } if (settings.get_beginner_mode()) { goods_manager_t::set_multiplier(settings.get_beginner_price_factor()); } else { goods_manager_t::set_multiplier( 1000 ); } max_allowed_height = settings.get_maximumheight(); min_allowed_height = settings.get_minimumheight(); groundwater = (sint8)(settings.get_groundwater()); min_height = max_height = groundwater; DBG_DEBUG("karte_t::rdwr_gamestate()","groundwater %i",groundwater); if( file->is_version_less(112, 7) ) { // r7930 fixed a bug in init_height_to_climate // recover old behavior to not mix up climate when loading old savegames groundwater = settings.get_climate_borders(0,0); init_height_to_climate(); groundwater = settings.get_groundwater(); } else { init_height_to_climate(); } // just an initialisation for the loading season = (2+last_month/3)&3; // summer always zero snowline = settings.get_winter_snowline() + groundwater; DBG_DEBUG("karte_t::rdwr_gamestate()", "settings loaded (size %i,%i) timeline=%i beginner=%i", settings.get_size_x(), settings.get_size_y(), settings.get_use_timeline(), settings.get_beginner_mode()); // wird gecached, um den Pointerzugriff zu sparen, da // die size _sehr_ oft referenziert wird cached_grid_size.x = settings.get_size_x(); cached_grid_size.y = settings.get_size_y(); cached_size_max = max(cached_grid_size.x,cached_grid_size.y); cached_size.x = cached_grid_size.x-1; cached_size.y = cached_grid_size.y-1; viewport->set_x_off(0); viewport->set_y_off(0); // Update minimap for new world minimap_t::get_instance()->init(); ls->set_max( get_size().y*2+256 ); init_tiles(); // reinit pointer with new pointer object and old values zeiger = new zeiger_t(koord3d::invalid, NULL ); hausbauer_t::new_world(); factory_builder_t::new_world(); DBG_DEBUG("karte_t::rdwr_gamestate()", "init felder ok"); } file->rdwr_long(ticks); file->rdwr_long(last_month); file->rdwr_long(last_year); if (file->is_loading()) { if(file->is_version_less(86, 6)) { last_year += env_t::default_settings.get_starting_year(); } if (last_year+1 >= INT32_MAX/12) { // prevent overflow dbg->fatal("karte_t::rdwr_gamestate", "Year out of range (%d >= %d)", last_year+1, INT_MAX/12); } // old game might have wrong month last_month %= 12; // set the current month count set_ticks_per_world_month_shift(settings.get_bits_per_month()); current_month = last_month + (last_year*12); season = (2+last_month/3)&3; // summer always zero next_month_ticks = ( (ticks >> karte_t::ticks_per_world_month_shift) + 1 ) << karte_t::ticks_per_world_month_shift; last_step_ticks = ticks; steps = 0; network_frame_count = 0; sync_steps = 0; sync_steps_barrier = sync_steps; step_mode = PAUSE_FLAG; DBG_MESSAGE("karte_t::rdwr_gamestate()","savegame loading at tick count %i",ticks); recalc_average_speed(); // resets timeline koord::locality_factor = settings.get_locality_factor( last_year ); // resets weight factor // recalc_average_speed may have opened message windows destroy_all_win(true); DBG_MESSAGE("karte_t::rdwr_gamestate()", "init player"); for(int i=0; iis_version_atleast(101, 0) ) { // since we have different kind of AIs delete players[i]; players[i] = NULL; init_new_player(i, settings.player_type[i]); } else if(i<8) { // get the old player ... if( players[i]==NULL ) { init_new_player( i, (i==3) ? player_t::AI_PASSENGER : player_t::AI_GOODS ); } settings.player_type[i] = players[i]->get_ai_id(); } } // so far, player 1 will be active (may change in future) active_player = players[PLAYER_HUMAN_NR]; active_player_nr = PLAYER_HUMAN_NR; } // rdwr tree ID mapping to restore tree IDs if (file->is_version_atleast(122, 2)) { DBG_MESSAGE("karte_t::rdwr_gamestate()", "rdwr tree IDs"); tree_builder_t::rdwr_tree_ids(file); } // rdwr static states senke_t::static_rdwr(file); // rdwr cityrules, speedbonus for network games if(file->is_version_atleast(102, 3)) { bool do_rdwr = env_t::networkmode; file->rdwr_bool(do_rdwr); if (do_rdwr) { stadt_t::cityrules_rdwr(file); if(file->is_version_atleast(102, 4)) { vehicle_builder_t::rdwr_speedbonus(file); } } } // rdwr cities if (file->is_loading()) { const sint32 num_cities = settings.get_city_count(); DBG_DEBUG("karte_t::rdwr_gamestate()", "init %i cities", num_cities); cities.clear(); cities.reserve(num_cities); for (int i = 0; i < num_cities; ++i) { stadt_t *s = new stadt_t(file); cities.append(s, s->get_einwohner()); } } else { for(stadt_t* const i : cities) { i->rdwr(file); if(!ls) { INT_CHECK("saving"); } } DBG_MESSAGE("karte_t::rdwr_gamestate()", "saved cities ok"); } // import rail blocks from old saves if (file->is_loading()) { DBG_MESSAGE("karte_t::rdwr_gamestate()","loading blocks"); old_blockmanager_t::rdwr(this, file); } // rdwr tiles if (file->is_loading()) { DBG_MESSAGE("karte_t::rdwr_gamestate()","loading tiles"); for (int y = 0; y < get_size().y; y++) { for (int x = 0; x < get_size().x; x++) { plan[x+y*cached_grid_size.x].rdwr(file, koord(x,y) ); } if(file->is_eof()) { dbg->fatal("karte_t::rdwr_gamestate()","Savegame file mangled (too short)!"); } ls->set_progress( y/2 ); } } else { for(int j=0; jset_progress(j); } } DBG_MESSAGE("karte_t::rdwr_gamestate()", "saved tiles"); } // rdwr terrain if (file->is_loading()) { if(file->is_version_less(99, 5)) { DBG_MESSAGE("karte_t::rdwr_gamestate()","loading grid for older versions"); for (int y = 0; y <= get_size().y; y++) { for (int x = 0; x <= get_size().x; x++) { sint32 hgt; file->rdwr_long(hgt); // old height step was 16! set_grid_hgt_nocheck(x, y, hgt/16 ); } } } else if( file->is_version_less(102, 2) ) { // hgt now bytes DBG_MESSAGE("karte_t::rdwr_gamestate()","loading grid for older versions"); for( sint32 i=0; i<(get_size().y+1)*(sint32)(get_size().x+1); i++ ) { file->rdwr_byte(grid_hgts[i]); } } if(file->is_version_less(88, 9)) { DBG_MESSAGE("karte_t::rdwr_gamestate()","loading slopes from older version"); // load slopes for older versions // now part of the grund_t structure for (int y = 0; y < get_size().y; y++) { for (int x = 0; x < get_size().x; x++) { sint8 slope; file->rdwr_byte(slope); // convert slopes from old single height saved game slope = slope_from_slope4(slope4_t(slope), env_t::pak_height_conversion_factor); access_nocheck(x, y)->get_kartenboden()->set_grund_hang(slope); } } } if(file->is_version_less(88, 1)) { // because from 88.01.4 on the foundations are handled differently for (int y = 0; y < get_size().y; y++) { for (int x = 0; x < get_size().x; x++) { koord k(x,y); grund_t *gr = access_nocheck(x, y)->get_kartenboden(); if( gr->get_typ()==grund_t::fundament ) { gr->set_hoehe( max_hgt_nocheck(k) ); gr->set_grund_hang( slope_t::flat ); // transfer object to on new grund for( int i=0; iobj_count(); i++ ) { gr->obj_bei(i)->set_pos( gr->get_pos() ); } } } } } } else { if( file->is_version_less(102, 2) ) { // not needed any more for(int j=0; j<(get_size().y+1)*(sint32)(get_size().x+1); j++) { file->rdwr_byte(grid_hgts[j]); } DBG_MESSAGE("karte_t::rdwr_gamestate()", "saved hgt"); } } // rdwr climate map if (file->is_loading()) { // init climates and default climate map DBG_MESSAGE("karte_t::rdwr_gamestate()", "init climates"); climate_map.resize( get_size().x, get_size().y ); if( file->is_version_atleast( 121, 1 ) ) { for( sint16 y = 0; y < get_size().y; y++ ) { for( sint16 x = 0; x < get_size().x; x++ ) { file->rdwr_byte( climate_map.at(x,y) ); } } } else if( file->is_version_less(112, 7) ) { // distribute climates calc_climate_map_region(0,0,get_size().x,get_size().y); } else { for( sint16 y = 0; y < get_size().y; y++ ) { for( sint16 x = 0; x < get_size().x; x++ ) { climate_map.at(x,y) = access(x,y)->get_climate(); if( climate_map.at( x, y ) > arctic_climate ) { dbg->warning( "karte_t::rdwr_gamestate()", "Wrong climate %i at (%i%i) set to temperate", climate_map.at( x, y ), x, y ); climate_map.at( x, y ) = temperate_climate; } } } } } else { // save default climate amp if( file->is_version_atleast( 121, 1 ) ) { for( sint16 y = 0; y < get_size().y; y++ ) { for( sint16 x = 0; x < get_size().x; x++ ) { file->rdwr_byte( climate_map.at(x,y) ); } } } DBG_MESSAGE("karte_t::rdwr_gamestate()", "saved default climates"); } if (file->is_loading()) { // Update minimap for new world DBG_MESSAGE("karte_t::rdwr_gamestate()", "init minimap"); win_set_world( this ); minimap_t::get_instance()->init(); } // rdwr factories if (file->is_loading()) { // load factories sint32 fabs; file->rdwr_long(fabs); DBG_MESSAGE("karte_t::rdwr_gamestate()", "Prepare for %i factories", fabs); for(sint32 i = 0; i < fabs; i++) { // list in gleicher reihenfolge wie vor dem speichern wieder aufbauen fabrik_t *fab = new fabrik_t(file); if(fab->get_desc()) { all_factories.append( fab ); } else { dbg->error("karte_t::rdwr_gamestate()","Unknown factory skipped!"); delete fab; } if(i&7) { ls->set_progress( get_size().y/2+(128*i)/fabs ); } } } else { sint32 fabs = all_factories.get_count(); file->rdwr_long(fabs); for(fabrik_t* const f : all_factories) { f->rdwr(file); if(!ls) { INT_CHECK("saving"); } } DBG_MESSAGE("karte_t::rdwr_gamestate()", "saved fabs"); } // import line management from old saves if (file->is_loading()) { // load linemanagement status (and lines) if (file->is_version_atleast(82, 4) && file->is_version_less(88, 3)) { DBG_MESSAGE("karte_t::rdwr_gamestate()", "load linemanagement"); get_player(0)->simlinemgmt.rdwr(file, get_player(0)); } // end load linemanagement } // rdwr stops if (file->is_loading()) { DBG_MESSAGE("karte_t::rdwr_gamestate()", "load stops"); // now load the stops // (the players will be load later and overwrite some values, // like the total number of stops build (for the numbered station feature) haltestelle_t::start_load_game(); if(file->is_version_atleast(99, 8)) { sint32 halt_count; file->rdwr_long(halt_count); DBG_MESSAGE("karte_t::rdwr_gamestate()","%d halts loaded",halt_count); for(int i=0; iexistiert_in_welt()) { dbg->warning("karte_t::rdwr_gamestate()", "could not restore stop near %i,%i", halt->get_init_pos().x, halt->get_init_pos().y ); } ls->set_progress( get_size().y/2+128+(get_size().y*i)/(2*halt_count) ); } } } else { sint32 haltcount=haltestelle_t::get_alle_haltestellen().get_count(); file->rdwr_long(haltcount); for(halthandle_t const s : haltestelle_t::get_alle_haltestellen()) { s->rdwr(file); } DBG_MESSAGE("karte_t::rdwr_gamestate()", "saved stops"); } // rdwr convois if (file->is_loading()) { DBG_MESSAGE("karte_t::rdwr_gamestate()", "load convois"); uint16 convoi_nr = 65535; uint16 max_convoi = 65535; if( file->is_version_atleast(101, 0) ) { file->rdwr_short(convoi_nr); max_convoi = convoi_nr; } char buf[80]; while( convoi_nr-->0 ) { if( file->is_version_less(101, 0) ) { file->rd_obj_id(buf, 79); if (strcmp(buf, "Ende Convois") == 0) { break; } } convoi_t *cnv = new convoi_t(file); convoi_array.append(cnv->self); if(cnv->in_depot()) { grund_t * gr = lookup(cnv->get_pos()); depot_t *dep = gr ? gr->get_depot() : 0; if(dep) { cnv->betrete_depot(dep); } else { dbg->error("karte_t::rdwr_gamestate()", "no depot for convoi, blocks may now be wrongly reserved!"); cnv->destroy(); } } else { sync.add( cnv ); } if( (convoi_array.get_count()&7) == 0 ) { ls->set_progress( get_size().y+(get_size().y*convoi_array.get_count())/(2*max_convoi)+128 ); } } DBG_MESSAGE("karte_t::rdwr_gamestate()", "%d convois/trains loaded", convoi_array.get_count()); } else { // save number of convois if( file->is_version_atleast(101, 0) ) { uint16 i=convoi_array.get_count(); file->rdwr_short(i); } for(convoihandle_t const cnv : convoi_array) { // one MUST NOT call INT_CHECK here or else the convoi will be broken during reloading! cnv->rdwr(file); } if( file->is_version_less(101, 0) ) { file->wr_obj_id("Ende Convois"); } if(!ls) { INT_CHECK("saving"); } DBG_MESSAGE("karte_t::rdwr_gamestate()", "saved %i convois",convoi_array.get_count()); } } // recalcs all ground tiles on the map void karte_t::update_map_intern(sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max) { if( (loaded_rotation + settings.get_rotation()) & 1 ) { // 1 || 3 // ~14% faster loop blocking rotations 1 and 3 const int LOOP_BLOCK = 128; for( int xx = x_min; xx < x_max; xx += LOOP_BLOCK ) { for( int yy = y_min; yy < y_max; yy += LOOP_BLOCK ) { for( int y = yy; y < min(yy + LOOP_BLOCK, y_max); y++ ) { for( int x = xx; x < min(xx + LOOP_BLOCK, x_max); x++ ) { const int nr = y * cached_grid_size.x + x; for( uint i = 0; i < plan[nr].get_boden_count(); i++ ) { plan[nr].get_boden_bei(i)->calc_image(); } } } } } } else { for( int y = y_min; y < y_max; y++ ) { for( int x = x_min; x < x_max; x++ ) { const int nr = y * cached_grid_size.x + x; for( uint i = 0; i < plan[nr].get_boden_count(); i++ ) { plan[nr].get_boden_bei(i)->calc_image(); } } } } } bool karte_t::can_flood_to_depth(koord k, sint8 new_water_height, sint8 *stage, sint8 *our_stage, sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max ) const { bool succeeded = true; if( k == koord::invalid ) { return false; } if( new_water_height < get_groundwater() - 3 ) { return false; } // make a list of tiles to change // cannot use a recursive method as stack is not large enough! sint8 *from_dir = new sint8[get_size().x * get_size().y]; bool local_stage = (our_stage==NULL); if( local_stage ) { our_stage = new sint8[get_size().x * get_size().y]; } memset( our_stage + sizeof(sint8) * array_koord(0,y_min), -1, sizeof(sint8) * get_size().x * (y_max-y_min) ); uint32 offset = array_koord(k.x,k.y); from_dir[offset]=-1; stage[offset]=0; our_stage[offset]=0; do { koord k_neighbour = k + koord::neighbours[our_stage[offset]]; if( k_neighbour.x < x_min || k_neighbour.x>=x_max) { succeeded = false; } else if( k_neighbour.y >= y_min && k_neighbour.y= new_water_height) { goto next_neighbour; } //move on to next tile from_dir[neighbour_offset] = our_stage[offset]; stage[neighbour_offset] = 0; our_stage[neighbour_offset] = 0; k = k_neighbour; offset = neighbour_offset; continue; } else if( succeeded && (global_lake_fill || k_neighbour.y < 1 || k_neighbour.y>=get_size().y-1) ) { // edge of map - we keep iterating so we can mark all connected tiles as failing succeeded = false; } next_neighbour: //return back to previous tile while( our_stage[offset]==7 ) { if( from_dir[offset] == -1 ) goto exit_iteration; // for natural maps only need to have checked one path out from original tile k = k - koord::neighbours[from_dir[offset]]; offset = array_koord(k.x,k.y); } our_stage[offset]++; } while( true ); exit_iteration: delete [] from_dir; if( local_stage ) { delete [] our_stage; } return succeeded; } void karte_t::flood_to_depth( sint8 new_water_height, sint8 *stage ) { const uint16 size_x = get_size().x; const uint16 size_y = get_size().y; uint32 offset_max = size_x*size_y; for( uint32 offset = 0; offset < offset_max; offset++ ) { if( stage[offset] == -1 ) { continue; } water_hgts[offset] = new_water_height; } } // recalcs all ground tiles on the map void karte_t::update_map() { DBG_MESSAGE( "karte_t::update_map()", "" ); world_xy_loop(&karte_t::update_map_intern, SYNCX_FLAG); set_dirty(); } void karte_t::update_underground() { DBG_MESSAGE( "karte_t::update_underground_map()", "" ); get_view()->clear_prepared(); world_view_t::invalidate_all(); set_dirty(); minimap_t::get_instance()->calc_map(); } void karte_t::prepare_tiles(rect_t const &new_area, rect_t const &old_area) { if (new_area == old_area) { // area already prepared return; } size_t const prepare_rects_capacity = rect_t::MAX_FRAGMENT_DIFFERENCE_COUNT; rect_t prepare_rects[prepare_rects_capacity]; size_t const prepare_rects_length = new_area.fragment_difference(old_area, prepare_rects, prepare_rects_capacity); // additional tiles to prepare for correct hiding behaviour sint16 const prefix_tiles_x = min(grund_t::MAXIMUM_HIDE_TEST_DISTANCE, new_area.origin.x); sint16 const prefix_tiles_y = min(grund_t::MAXIMUM_HIDE_TEST_DISTANCE, new_area.origin.y); for (size_t rect_index = 0 ; rect_index < prepare_rects_length ; rect_index++) { rect_t const &prepare_rect = prepare_rects[rect_index]; sint16 x_start = prepare_rect.origin.x; sint16 const x_end = x_start + prepare_rect.size.x; if (x_start == new_area.origin.x) { x_start-= prefix_tiles_x; } sint16 y_start = prepare_rect.origin.y; sint16 const y_end = y_start + prepare_rect.size.y; if (y_start == new_area.origin.y) { y_start-= prefix_tiles_y; } for (sint16 y = y_start ; y < y_end ; y++) { for (sint16 x = x_start ; x < x_end ; x++) { const planquadrat_t &tile = plan[y * cached_grid_size.x + x]; tile.update_underground(); } } } } // distributes climates in a rectangle void karte_t::calc_humidity_map_region( sint16 , sint16 , sint16 xbottom, sint16 ybottom ) { // always calculate the map for the entire map to have smooth transitions humidity_map.resize( xbottom, ybottom ); // some parameter to teaks: // artic height should relate to the gradient, like delta_h/artic_max_height ~ 1/16 change of humidity or temperature // also on smaller maps remoistering must be faster, since this parameter is kept with enlargement, it must be set externally const sint8 height_increase = min( 160 / settings.get_climate_borders(arctic_climate,1), 33 ); const ribi_t::ribi wind = settings.get_wind_dir(); if( wind == ribi_t::west || wind == ribi_t::east ) { const sint16 x0 = wind == ribi_t::west ? 0 : xbottom - 1; const sint16 xmax = wind == ribi_t::west ? xbottom : -1; const sint16 dx = wind == ribi_t::west ? 1 : -1; for( sint16 y = 0; y < ybottom; y++ ) { sint8 current_humidity = 50; // start value for each row for( sint16 x = x0; x < xmax; x+=dx ) { grund_t *gr = lookup_kartenboden(x,y); if( gr->is_water() || gr->hat_weg(water_wt) ) { // increase humidity over water and rivers current_humidity = current_humidity+settings.get_moisture_water(); if( gr->is_water() ) { climate_map.at( x, y ) = water_climate; } } else { sint8 gradient = lookup_hgt_nocheck( x+1, y )-lookup_hgt_nocheck( x, y ); current_humidity += settings.get_moisture() + gradient * height_increase; } current_humidity = clamp( current_humidity, 0, 100 ); humidity_map.at(x,y) = current_humidity; } } } else { const sint16 y0 = wind == ribi_t::north ? 0 : ybottom - 1; const sint16 ymax = wind == ribi_t::north ? ybottom : -1; const sint16 dy = wind == ribi_t::north ? 1 : -1; for( sint16 x = 0; x < xbottom; x++ ) { sint8 current_humidity = 50; // start value for each row for( sint16 y = y0; y < ymax; y+=dy ) { grund_t *gr = lookup_kartenboden(x,y); if( gr->is_water() || gr->hat_weg(water_wt) ) { // increase humidity over water and rivers current_humidity = current_humidity+settings.get_moisture_water(); if( gr->is_water() ) { climate_map.at( x, y ) = water_climate; } } else { sint8 gradient = lookup_hgt_nocheck( x+1, y )-lookup_hgt_nocheck( x, y ); current_humidity += settings.get_moisture() + gradient * height_increase; } current_humidity = clamp( current_humidity, 0, 100 ); humidity_map.at(x,y) = current_humidity; } } } } // distributes climates in a rectangle void karte_t::calc_climate_map_region( sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom ) { if( xtop == 0 && ytop == 0 ) { climate_map.clear(); } climate_map.resize( xbottom, ybottom, 0x7F ); switch(settings.get_climate_generator()) { case settings_t::HUMIDITY_BASED: { calc_humidity_map_region( xtop, ytop, xbottom, ybottom ); sint16 groundwater = settings.get_groundwater(); const sint16 winterline = settings.get_winter_snowline(); const sint16 summerline = settings.get_climate_borders( arctic_climate, 1 ); // annual mean temperature at summer snowline must be less than zero // assume annual mean temperature at winter snowline is 14 celcius const sint8 snowline_diff = summerline-winterline; const sint8 groundwater_temperature = 14+((winterline-groundwater)*14)/(snowline_diff!=0 ? snowline_diff:1); // first remove water all clear single tiles from effort // for the first passes, we only work on the grid, which is much faster for( sint16 y = 0; y < ybottom; y++ ) { for( sint16 x = 0; x < xbottom; x++ ) { if( climate_map.at(x,y)==0x7F ) { climate this_climate = arctic_climate; // fallthrough option sint8 hgt = lookup_hgt_nocheck(x,y) - groundwater; sint8 temperature = groundwater_temperature - (hgt-groundwater) - simrand(2); sint8 this_humidity = humidity_map.at(x,y) + simrand(5); if( temperature >= settings.get_climate_temperature_borders(0) ) { if( this_humidity > settings.get_tropic_humidity() ) { this_climate = tropic_climate; } else if( this_humidity > settings.get_desert_humidity() ) { this_climate = mediterran_climate; } else { this_climate = desert_climate; } } else if( temperature >= settings.get_climate_temperature_borders(1) ) { if( this_humidity > settings.get_desert_humidity() ) { this_climate = mediterran_climate; } else { this_climate = desert_climate; } } else if( temperature >= settings.get_climate_temperature_borders(2) ) { if( this_humidity > settings.get_desert_humidity() ) { this_climate = temperate_climate; } else { this_climate = mediterran_climate; } } else if( temperature >= settings.get_climate_temperature_borders(3) ) { if( this_humidity > settings.get_desert_humidity() ) { this_climate = tundra_climate; } else { this_climate = temperate_climate; } } else if( temperature >= settings.get_climate_temperature_borders(4) ) { this_climate = rocky_climate; } if( x >= xtop && y >= ytop ) { climate_map.at( x, y ) = this_climate; } } } } // smooth climates (this code needs a little cleanup, I think) const sint32 world_size = xbottom*(sint32)ybottom; climate *climate_smooth = new climate[world_size]; climate *climate_smooth_cpy = new climate[world_size]; for(uint16 y=0; y= 0; if( within_limits && climate_smooth_cpy[k_neighbour.x+k_neighbour.y*xbottom]!=water_climate ) { this_climate = climate_smooth_cpy[k_neighbour.x+k_neighbour.y*xbottom]; } else { this_climate = climate_smooth_cpy[x+y*xbottom]; } temp_climate += (i&1) ? (2 * (this_climate)) : (this_climate); } //if(s<8) temp_height += sets->get_map_roughness()*(simrand(8)); climate_smooth[x+y*xbottom]=(climate)((temp_climate)/16); } } } } for(uint16 y=ytop; y allowed( 8 ); { // find the next climateless tile for( sint16 y = ytop; y < ybottom; y++ ) { for( sint16 x = xtop; x < xbottom; x++ ) { if( climate_map.at( x, y ) > arctic_climate ) { // not assigned yet => start with a random allowed climate allowed.clear(); sint8 hgt = lookup_hgt_nocheck( x, y ); for( int cl=1; cl= settings.get_climate_borders( cl, 0 ) && hgt < settings.get_climate_borders( cl, 1 ) ) { allowed.append(cl); } } //assert( !allowed.empty() ); climate cl = !allowed.empty() ? (climate)pick_any(allowed) : temperate_climate; // now we do an ellipse with size wx and wy around the starting point const sint32 wx = 2+simrand( max_patchsize_x ); const sint32 wy = 2+simrand( max_patchsize_y ); for( sint16 j = 0; j < wx; j++) { for( sint16 i = 0; i < wy; i++) { const sint32 x_off = (j-(wx>>1)); const sint32 y_off = (i-(wy>>1)); if( x+x_off>=xtop && x+x_off=ytop && y+y_offarctic_climate ) { const uint64 distance = 1 + sqrt_i64( ((uint64)x_off*x_off*(wx*wx) + (uint64)y_off*y_off*(wy*wy))); const uint32 threshold = (uint32)( ( 8 * (uint32)((wx*wx)+(wy*wy)) ) / distance ); if( threshold > 40 ) { hgt = lookup_hgt_nocheck( x+x_off, y+y_off ); // find out if the climate is still allowed here if( hgt >= settings.get_climate_borders( cl, 0 ) && hgt <= settings.get_climate_borders( cl, 1 ) ) { climate_map.at( x+x_off, y+y_off ) = cl; } } } } } } } } } } break; case settings_t::MAP_BASED: { /* Essentially keep the climates from this map * we just have to set the tiles with water to water climate * and vice vers */ for( sint16 y = ytop; y < ybottom; y++ ) { for( sint16 x = xtop; x < xbottom; x++ ) { sint8 hgt = lookup_hgt_nocheck( x, y ); if( hgt < groundwater ) { climate_map.at( x, y ) = water_climate; } else if( climate_map.at( x, y )==water_climate ) { climate_map.at( x, y ) = height_to_climate[hgt-groundwater]; } } } } break; } assign_climate_map_region( xtop, ytop, xbottom, ybottom ); } void karte_t::assign_climate_map_region( sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom ) { for( sint16 y = ytop; y < ybottom; y++ ) { for( sint16 x = xtop; x < xbottom; x++ ) { if( planquadrat_t *pl = access( x, y ) ) { if( pl->get_kartenboden()->is_water() ) { // full water tile pl->set_climate( water_climate ); pl->set_climate_transition_flag(false); pl->set_climate_corners(0); climate_map.at( x, y ) = water_climate; } else { // test for beach tiles (if climate is not desert) bool beach = false; if( climate_map.at(x,y)!=desert_climate ) { grund_t *gr = pl->get_kartenboden(); if( gr->get_pos().z == groundwater ) { for( int i = 0; i < 8 && !beach; i++ ) { grund_t *gr2 = lookup_kartenboden( koord(x,y) + koord::neighbours[i] ); if( gr2 && gr2->is_water() ) { beach = true; } } } } // beach or only one climate uint8 cl = beach ? (uint8)desert_climate : climate_map.at(x,y); pl->set_climate( (climate)cl ); climate_map.at( x, y ) = cl; pl->set_climate_transition_flag(false); pl->set_climate_corners(0); } } } } } void karte_t::recalc_transitions_loop( sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max ) { for( int y = y_min; y < y_max; y++ ) { for( int x = x_min; x < x_max; x++ ) { recalc_transitions( koord( x, y ) ); } } } uint8 karte_t::sp2num(player_t *player) { if( player==NULL ) { return PLAYER_UNOWNED; } for(int i=0; ifatal( "karte_t::sp2num()", "called with an invalid player!" ); } void karte_t::load_heightfield(settings_t* const sets) { sint16 w, h; sint8 *h_field = NULL; const sint8 min_h = sets->get_minimumheight(); const sint8 max_h = sets->get_maximumheight(); height_map_loader_t hml(min_h, max_h, env_t::height_conv_mode); if(hml.get_height_data_from_file(sets->heightfield.c_str(), (sint8)(sets->get_groundwater()), h_field, w, h, false )) { sets->set_size(w,h); // create map init(sets,h_field); free(h_field); } else { dbg->error("karte_t::load_heightfield()","Cant open file '%s'", sets->heightfield.c_str()); create_win( new news_img("\nCan't open heightfield file.\n"), w_info, magic_none ); } } void karte_t::mark_area( const koord3d pos, const koord size, const bool mark ) const { for( sint16 y=pos.y; yset_flag(grund_t::marked); } else { gr->clear_flag(grund_t::marked); } gr->set_flag(grund_t::dirty); } } } } void karte_t::reset_timer() { // Reset timers uint32 last_tick_sync = dr_time(); mouse_rest_time = last_tick_sync; sound_wait_time = AMBIENT_SOUND_INTERVALL; intr_set_last_time(last_tick_sync); if( env_t::networkmode && (step_mode&PAUSE_FLAG)==0 ) { step_mode = FIX_RATIO; } last_interaction = last_tick_sync; last_step_ticks = ticks; // reinit simloop counter for( int i=0; i<32; i++ ) { last_step_nr[i] = steps; } if( step_mode&PAUSE_FLAG ) { intr_disable(); } else if(step_mode==FAST_FORWARD) { next_step_time = last_tick_sync+1; idle_time = 0; set_frame_time( 1000 / env_t::ff_fps ); time_multiplier = 16; intr_enable(); } else if(step_mode==FIX_RATIO) { last_frame_idx = 0; fix_ratio_frame_time = 1000 / clamp(settings.get_frames_per_second(), env_t::min_fps, env_t::max_fps); next_step_time = last_tick_sync + fix_ratio_frame_time; set_frame_time( fix_ratio_frame_time ); intr_disable(); // other stuff needed to synchronize tile_counter = 0; pending_season_change = 1; pending_snowline_change = 1; } else { // make timer loop invalid for( int i=0; i<32; i++ ) { last_frame_ms[i] = dr_time(); } last_frame_idx = 0; simloops = 60; set_frame_time( 1000/env_t::fps ); next_step_time = last_tick_sync+(3200/get_time_multiplier() ); intr_enable(); } DBG_MESSAGE("karte_t::reset_timer()","called, mode=$%X", step_mode); } void karte_t::reset_interaction() { last_interaction = dr_time(); } void karte_t::set_map_counter(uint32 new_map_counter) { map_counter = new_map_counter; if( env_t::server ) { nwc_ready_t::append_map_counter(map_counter); } } uint32 karte_t::generate_new_map_counter() const { return dr_time(); } // jump one year ahead // (not updating history!) void karte_t::step_year() { DBG_MESSAGE("karte_t::step_year()","called"); current_month += 12; last_year ++; reset_timer(); recalc_average_speed(); koord::locality_factor = settings.get_locality_factor( last_year ); for(stadt_t* const i : cities) { i->recalc_target_cities(); i->recalc_target_attractions(); } } // jump one or more months ahead // (updating history!) void karte_t::step_month( sint16 months ) { while( months-->0 ) { new_month(); } reset_timer(); } sint32 karte_t::get_time_multiplier() const { return step_mode==FAST_FORWARD ? env_t::max_acceleration : time_multiplier; } void karte_t::change_time_multiplier(sint32 delta) { if( step_mode == FAST_FORWARD ) { if( env_t::max_acceleration+delta > 2 ) { env_t::max_acceleration += delta; } } else { time_multiplier += delta; if(time_multiplier<=0) { time_multiplier = 1; } if(step_mode!=NORMAL) { step_mode = NORMAL; reset_timer(); } } } void karte_t::set_pause(bool p) { bool pause = step_mode&PAUSE_FLAG; if(p!=pause) { step_mode ^= PAUSE_FLAG; if(p) { intr_disable(); } else { reset_timer(); } } } void karte_t::set_fast_forward(bool ff) { if( !env_t::networkmode ) { if( ff ) { if( step_mode==NORMAL ) { step_mode = FAST_FORWARD; reset_timer(); } } else { if( step_mode==FAST_FORWARD ) { step_mode = NORMAL; reset_timer(); } } } } /* creates a new player with this type */ const char *karte_t::init_new_player(uint8 new_player_in, uint8 type) { if( new_player_in>=PLAYER_UNOWNED || get_player(new_player_in)!=NULL ) { return "Id invalid/already in use!"; } switch( type ) { case player_t::EMPTY: break; case player_t::HUMAN: players[new_player_in] = new player_t(new_player_in); break; case player_t::AI_GOODS: players[new_player_in] = new ai_goods_t(new_player_in); break; case player_t::AI_PASSENGER: players[new_player_in] = new ai_passenger_t(new_player_in); break; case player_t::AI_SCRIPTED: players[new_player_in] = new ai_scripted_t(new_player_in); break; default: return "Unknown AI type!"; } settings.set_player_type(new_player_in, type); return NULL; } void karte_t::remove_player(uint8 player_nr) { if ( player_nr!=PLAYER_PUBLIC_NR && player_nrai_bankrupt(); delete players[player_nr]; players[player_nr] = NULL; nwc_chg_player_t::company_removed(player_nr); // if default human, create new instace of it (to avoid crashes) if( player_nr == PLAYER_HUMAN_NR ) { players[0] = new player_t( PLAYER_HUMAN_NR ); } // if currently still active => reset to default human if( player_nr == active_player_nr ) { active_player_nr = PLAYER_HUMAN_NR; active_player = players[PLAYER_HUMAN_NR]; if( !env_t::server ) { const scr_coord pos{ gfx->get_screen_size().w/2 - 128, 40 }; create_win( pos, new news_img("Bankrott:\n\nDu bist bankrott.\n"), w_info, magic_none); } } } } /* goes to next active player */ void karte_t::switch_active_player(uint8 new_player, bool silent) { for( uint8 i=0; iget_pos(); // no cheating allowed? if (!settings.get_allow_player_change() && get_public_player()->is_locked()) { active_player_nr = PLAYER_HUMAN_NR; active_player = players[PLAYER_HUMAN_NR]; if(new_player!=PLAYER_HUMAN_NR) { create_win( new news_img("On this map, you are not\nallowed to change player!\n"), w_time_delete, magic_none); } } else { zeiger->change_pos( koord3d::invalid ); // unmark area // exit active tool to remove pointers (for two_click_tool_t's, stop mover, factory linker) if(selected_tool[active_player_nr]) { selected_tool[active_player_nr]->exit(active_player); } active_player_nr = new_player; active_player = players[new_player]; zeiger->set_owner(active_player); if( !silent ) { // tell the player cbuffer_t buf; buf.printf( translator::translate("Now active as %s.\n"), get_active_player()->get_name() ); msg->add_message(buf, koord3d::invalid, message_t::ai | message_t::DO_NOT_SAVE_MSG, PLAYER_FLAG|get_active_player()->get_player_nr(), IMG_EMPTY); } // update menu entries tool_t::update_toolbars(); set_dirty(); } // init tool again selected_tool[active_player_nr]->flags = 0; selected_tool[active_player_nr]->init(active_player); // update pointer image / area selected_tool[active_player_nr]->init_cursor(zeiger); // set position / mark area zeiger->change_pos( old_zeiger_pos ); } void karte_t::stop(bool exit_game) { finish_loop = true; if (exit_game) { env_t::quit_simutrans = true; DBG_DEBUG("ev=SYSTEM_QUIT", "env_t::reload_and_save_on_quit=%d", env_t::reload_and_save_on_quit); // we may be requested to save the game before exit if (env_t::server && env_t::server_save_game_on_quit) { // to ensure only one attempt is made env_t::server_save_game_on_quit = false; // following code quite similar to nwc_sync_t::do_coomand dr_chdir(env_t::user_dir); // first save password hashes char fn[256]; sprintf(fn, "server%d-pwdhash.sve", env_t::server); loadsave_t file; if (file.wr_open(fn, loadsave_t::zipped, 1, "hashes", SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK) { world->rdwr_player_password_hashes(&file); file.close(); } // remove passwords before transfer on the server and set default client mask // they will be restored in karte_t::load for (uint8 i = 0; i < PLAYER_UNOWNED; i++) { player_t *player = world->get_player(i); if (player && !player->access_password_hash().empty()) { player->access_password_hash().clear(); } } // save game sprintf(fn, "server%d-restore.sve", env_t::server); bool old_restore_UI = env_t::restore_UI; env_t::restore_UI = true; world->save(fn, false, SAVEGAME_VER_NR, false); env_t::restore_UI = old_restore_UI; } else if (env_t::reload_and_save_on_quit) { if (env_t::networkmode) { // construct from pak name an autosave if requested std::string pak_name("autosave-"); pak_name.append(env_t::pak_name); pak_name.erase(pak_name.length() - 1); pak_name.append(".net"); FILE *f = dr_fopen(pak_name.c_str(), "w"); fputs(settings.get_filename(), f); fclose(f); // save windows loadsave_t file; pak_name.append(".sve"); if (file.wr_open(pak_name.c_str(), loadsave_t::autosave_mode, loadsave_t::autosave_level, env_t::pak_name.c_str(), SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK) { // we could open for writing file.rdwr_byte(active_player_nr); // save all open windows rdwr_all_win(&file); } } else { // save current game, if not online bool old_restore_UI = env_t::restore_UI; env_t::restore_UI = true; // construct from pak name an autosave if requested std::string pak_name("autosave-"); pak_name.append(env_t::pak_name); pak_name.erase(pak_name.length() - 1); pak_name.append(".sve"); dr_chdir(env_t::user_dir); world->save(pak_name.c_str(), true, SAVEGAME_VER_NR, false); env_t::restore_UI = old_restore_UI; } } destroy_all_win(true); } } void karte_t::network_game_set_pause(bool pause_, uint32 syncsteps_) { if (env_t::networkmode) { time_multiplier = 16; // reset to normal speed sync_steps = syncsteps_; sync_steps_barrier = sync_steps; steps = sync_steps / settings.get_frames_per_step(); network_frame_count = sync_steps % settings.get_frames_per_step(); dbg->warning("karte_t::network_game_set_pause", "steps=%d sync_steps=%d pause=%d", steps, sync_steps, pause_); if (pause_) { if (!env_t::server) { reset_timer(); step_mode = PAUSE_FLAG|FIX_RATIO; } else { // TODO } } else { step_mode = FIX_RATIO; reset_timer(); if( !env_t::server ) { // allow server to run ahead the specified number of frames, plus an extra 50%. Better to catch up than be ahead. next_step_time = dr_time() + (settings.get_server_frames_ahead() + (uint32)env_t::additional_client_frames_behind) * fix_ratio_frame_time * 3 / 2; } } } else { set_pause(pause_); } } const char* karte_t::call_work_api(tool_t *tool, player_t *player, koord3d pos, bool &suspended) { suspended = false; const char *err = NULL; bool network_safe_tool = tool->is_work_keeps_game_state() || tool->is_work_here_keeps_game_state(player, pos); // do the check for allowed (always done locally) tool->flags |= tool_t::WFL_LOCAL; // first allowance by scenario if( (tool->flags & tool_t::WFL_NO_CHK) == 0 && get_scenario()->is_scripted() ) { if( !get_scenario()->is_tool_allowed(player, tool->get_id(), tool->get_waytype(), tool->get_default_param()) ) { err = "Forbidden by scenario"; } else { err = get_scenario()->is_work_allowed_here(player, tool->get_id(), tool->get_waytype(), tool->get_default_param(), pos); } } if (err == NULL) { if( !env_t::networkmode || network_safe_tool ) { if( network_safe_tool || (get_random_mode() & INTERACTIVE_RANDOM) == 0 ) { // call work if it would not affect game state or if the call is not during sync_step err = tool->work(player, pos); suspended = false; } else { // queue tool for execution (will be only done when NOT in networkmode!) nwc_tool_t* nwc = new nwc_tool_t(player, tool, pos, get_steps(), get_map_counter(), false); command_queue_append(nwc); // reset tool tool->init(player); suspended = true; } } else { // queue tool for network nwc_tool_t *nwc = new nwc_tool_t(player, tool, pos, get_steps(), get_map_counter(), false); network_send_server(nwc); suspended = true; // reset tool tool->init(player); } } return err; } static slist_tpl command_queue; static koord3d next_deferred_move_to = koord3d::invalid; static uint8 next_deferred_move_flags = 0; void karte_t::set_deferred_move_to(koord3d k, uint8 f) { next_deferred_move_to = k; next_deferred_move_flags = f; } void karte_t::command_queue_append(network_world_command_t* nwc) const { slist_tpl::iterator i = command_queue.begin(); slist_tpl::iterator end = command_queue.end(); while(i != end && network_world_command_t::cmp(*i, nwc)) { ++i; } command_queue.insert(i, nwc); } void karte_t::clear_command_queue() const { while (!command_queue.empty()) { delete command_queue.remove_first(); } } static void encode_URI(cbuffer_t& buf, char const* const text) { for (char const* i = text; *i != '\0'; ++i) { char const c = *i; if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '-' || c == '.' || c == '_' || c == '~') { char const two[] = { c, '\0' }; buf.append(two); } else { buf.printf("%%%02X", (unsigned char)c); } } } void karte_t::process_network_commands(sint32 *ms_difference) { // did we receive a new command? uint32 ms = dr_time(); sint32 time_to_next_step = (sint32)next_step_time - (sint32)ms; network_command_t *nwc = network_check_activity(clamp(time_to_next_step, 0, 5)); if( nwc==NULL && !network_check_server_connection() ) { dbg->warning("karte_t::process_network_commands", "lost connection to server"); network_disconnect(); return; } // process the received command while( nwc ) { // check timing uint16 const nwcid = nwc->get_id(); if( nwcid == NWC_CHECK || nwcid == NWC_STEP ) { // pull out server sync step const uint32 server_sync_step = nwcid == NWC_CHECK ? dynamic_cast(nwc)->server_sync_step : dynamic_cast(nwc)->get_sync_step(); // are we on time? *ms_difference = 0; const uint32 timems = dr_time(); const sint32 time_to_next = (sint32)next_step_time - (sint32)timems; // +'ve - still waiting for next, -'ve - lagging const sint64 frame_timediff = ((sint64)server_sync_step - sync_steps - settings.get_server_frames_ahead() - env_t::additional_client_frames_behind) * fix_ratio_frame_time; // +'ve - server is ahead, -'ve - client is ahead const sint64 timediff = time_to_next + frame_timediff; dbg->warning("NWC_CHECK", "time difference to server %lli", frame_timediff ); if( frame_timediff < (0 - (sint64)settings.get_server_frames_ahead() - (sint64)env_t::additional_client_frames_behind) * (sint64)fix_ratio_frame_time / 2 ) { // running way ahead - more than half margin, simply set next_step_time ahead to where it should be next_step_time = (sint64)timems - frame_timediff; } else if( frame_timediff < 0 ) { // running ahead if( time_to_next > -frame_timediff ) { // already waiting longer than how far we're ahead, so set wait time shorter to the time ahead. next_step_time = (sint64)timems - frame_timediff; } else if( nwcid == NWC_CHECK ) { // gentle slowing down *ms_difference = timediff; } } else if( frame_timediff > 0 ) { // running behind if( time_to_next > (sint32)fix_ratio_frame_time / 4 ) { // behind but we're still waiting for the next step time - get going. next_step_time = timems; *ms_difference = frame_timediff; } else if( nwcid == NWC_CHECK ) { // gentle catching up *ms_difference = timediff; } } if( sync_steps_barrier < server_sync_step ) { sync_steps_barrier = server_sync_step; } } // check random number generator states if( env_t::server && nwcid == NWC_TOOL ) { nwc_tool_t *nwt = dynamic_cast(nwc); if( nwt->is_from_initiator() ) { if( nwt->last_sync_step>sync_steps ) { dbg->warning("karte_t::process_network_commands", "client was too fast (skipping command)" ); delete nwc; nwc = NULL; } // out of sync => drop client (but we can only compare if nwt->last_sync_step is not too old) else if( is_checklist_available(nwt->last_sync_step) && LCHKLST(nwt->last_sync_step)!=nwt->last_checklist ) { // lost synchronisation -> server kicks client out actively cbuffer_t buf; LCHKLST(nwt->last_sync_step).print(buf, "server"); nwt->last_checklist.print(buf, "initiator"); dbg->warning("karte_t::process_network_commands", "kicking client due to checklist mismatch : sync_step=%u %s", nwt->last_sync_step, buf.get_str()); socket_list_t::remove_client( nwc->get_sender() ); delete nwc; nwc = NULL; } } } // execute command, append to command queue if necessary if(nwc && nwc->execute(this)) { // network_world_command_t's will be appended to command queue in execute // all others have to be deleted here delete nwc; } // fetch the next command nwc = network_get_received_command(); } uint32 next_command_step = get_next_command_step(); // send data ms = dr_time(); network_process_send_queues( next_step_time>ms ? min( next_step_time-ms, 5) : 0 ); // process enqueued network world commands while( !command_queue.empty() && (next_command_step<=sync_steps/* || step_mode&PAUSE_FLAG*/) ) { network_world_command_t *nwc = command_queue.remove_first(); if (nwc) { do_network_world_command(nwc); delete nwc; } next_command_step = get_next_command_step(); } } void karte_t::do_network_world_command(network_world_command_t *nwc) { // want to execute something in the past? if (nwc->get_sync_step()!=0 && nwc->get_sync_step() < sync_steps) { if (!nwc->ignore_old_events()) { dbg->warning("karte_t:::do_network_world_command", "wanted to do_command(%s) in the past", nwc->get_name()); network_disconnect(); } } // check map counter else if (nwc->get_map_counter() != map_counter) { dbg->warning("karte_t:::do_network_world_command", "wanted to do_command(%s) from another world", nwc->get_name()); } // check random counter? else if( nwc->get_id()==NWC_CHECK ) { nwc_check_t* nwcheck = (nwc_check_t*)nwc; // this was the random number at the previous sync step on the server const checklist_t &server_checklist = nwcheck->server_checklist; const uint32 server_sync_step = nwcheck->server_sync_step; cbuffer_t buf; server_checklist.print(buf, "server"); LCHKLST(server_sync_step).print(buf, "client"); dbg->warning("karte_t:::do_network_world_command", "sync_step=%u %s", server_sync_step, buf.get_str()); if( LCHKLST(server_sync_step)!=server_checklist ) { network_disconnect(); // output warning / throw fatal error depending on heavy mode setting void (log_t::*outfn)(const char*, const char*, ...) = (env_t::network_heavy_mode == 2 ? &log_t::fatal : &log_t::warning); (dbg->*outfn)("karte_t:::do_network_world_command", "Disconnected due to checklist mismatch" ); } } else { if( nwc->get_id()==NWC_TOOL ) { nwc_tool_t *nwt = dynamic_cast(nwc); if( is_checklist_available(nwt->last_sync_step) && LCHKLST(nwt->last_sync_step)!=nwt->last_checklist ) { // lost synchronisation ... cbuffer_t buf; nwt->last_checklist.print(buf, "server"); LCHKLST(nwt->last_sync_step).print(buf, "executor"); dbg->warning("karte_t:::do_network_world_command", "skipping command due to checklist mismatch : sync_step=%u %s", nwt->last_sync_step, buf.get_str()); if( !env_t::server ) { network_disconnect(); } return; } } nwc->do_command(this); } } uint32 karte_t::get_next_command_step() { // when execute next command? if( !command_queue.empty() ) { return command_queue.front()->get_sync_step(); } else { return 0xFFFFFFFFu; } } sint16 karte_t::get_sound_id(grund_t *gr) { if( gr->ist_natur() || gr->is_water() ) { // first, find out if climates overlap for shore const koord k = gr->get_pos().get_2d(); uint8 climate_corners = access(k)->get_climate_corners(); if (climate_corners && sound_desc_t::beach_sound!=NO_SOUND) { for (int i = 0; i < 8; i++) { koord k_neighbour = k + koord::neighbours[i]; if (!is_within_limits(k_neighbour)) { k_neighbour = get_closest_coordinate(k_neighbour); } if(water_climate == get_climate(k_neighbour)) { // one croner water => shore return sound_desc_t::beach_sound; } } } // try forest if ( sound_desc_t::forest_sound!=NO_SOUND && gr->obj_count() > 0 && gr->obj_bei(0)->get_typ() == obj_t::baum ) { return sound_desc_t::forest_sound; } if( gr->get_pos().z >= get_snowline() ) { return sound_desc_t::climate_sounds[ arctic_climate ]; } else { return sound_desc_t::climate_sounds[get_climate( k )]; } } return NO_SOUND; } static void heavy_rotate_saves(const char *prefix, uint32 sync_steps, uint32 num_to_keep) { dr_chdir(env_t::user_dir); dr_mkdir( SAVE_PATH_X "heavy"); cbuffer_t name; name.printf(SAVE_PATH_X "heavy/heavy-%s-%04d.sve", prefix, sync_steps); world()->save(name, false, SERVER_SAVEGAME_VER_NR, true); if (sync_steps >= num_to_keep) { cbuffer_t old_name; old_name.printf(SAVE_PATH_X "heavy/heavy-%s-%04d.sve", prefix, sync_steps - num_to_keep); dr_remove(old_name); } } bool karte_t::interactive(uint32 quit_month) { finish_loop = false; sync_steps = 0; sync_steps_barrier = sync_steps; next_deferred_move_to = koord3d::invalid; network_frame_count = 0; vector_tplhashes_ok; // bit set: this client can do something with this player if( !scenario->rdwr_ok() ) { // error during loading of savegame of scenario create_win( new news_img( scenario->get_error_text() ), w_info, magic_none); scenario->stop(); } // only needed for network if( env_t::networkmode ) { // clear the checklist history for( int i=0; iget_pos()); if( gr ) { sint16 id = get_sound_id(gr); if( id!=NO_SOUND ) { sound_play(id,255,AMBIENT_SOUND); } } sound_wait_time *= 2; } DBG_DEBUG4("karte_t::interactive", "end of sound"); } // events are now checked during each screen update for quicker feedback on scrolling etc. if (env_t::quit_simutrans){ break; } if (next_deferred_move_to != koord3d::invalid) { // some tool movement is expensive (like route search) and must be done outsied sync_step // to avoid calling a the non-reentrant route search twice tool_t *tool = selected_tool[active_player_nr]; const char* err = scenario->is_work_allowed_here(active_player, tool->get_id(), tool->get_waytype(), tool->get_default_param(), next_deferred_move_to); if (err == NULL) { koord3d target = next_deferred_move_to; next_deferred_move_to = koord3d::invalid; tool->flags = next_deferred_move_flags; tool->move(active_player, true, target); tool->flags = 0; } else if (two_click_tool_t *tct = dynamic_cast(tool)) { // remove preview images if (!tct->is_first_click()) { tct->cleanup(false); } } next_deferred_move_to = koord3d::invalid; next_deferred_move_flags = 0; } if( env_t::networkmode ) { process_network_commands(&ms_difference); } else { // we wait here for maximum 9ms // average is 5 ms, so we usually // are quite responsive DBG_DEBUG4("karte_t::interactive", "can I get some sleep?"); INT_CHECK( "karte_t::interactive()" ); const sint32 wait_time = (sint32)next_step_time - (sint32)dr_time(); if(wait_time>2) { dr_sleep(2); INT_CHECK("karte_t::interactive()"); } DBG_DEBUG4("karte_t::interactive", "end of sleep"); // process enqueued network world commands while (!command_queue.empty()) { network_world_command_t* nwc = command_queue.remove_first(); if (nwc) { nwc->do_command(this); delete nwc; } } INT_CHECK("karte_t::interactive()"); } uint32 time = dr_time(); if( (sint32)next_misc_time - (sint32)time <=0 ) { #ifdef STEAM_BUILT steam_t::get_instance()->update_ui(get_last_year(), convoys().get_count()); #endif simachievements_t::check_state_ach(this); next_misc_time = time + 5000; // every 5s } // check midi if next songs needs to be started if( (sint32)next_midi_time - (sint32)time <= 0 ) { DBG_DEBUG4("karte_t::interactive", "checkmidi"); check_midi(); next_midi_time = time + 1500; // check every 1.5 s if we need to start next song } // time for the next step? if( (sint32)next_step_time - (sint32)time <= 0 ) { if( step_mode&PAUSE_FLAG ) { // only update display display(0); idle_time = 100; eventmanager->check_events(); } else if( env_t::networkmode && !env_t::server && sync_steps >= sync_steps_barrier ) { display(0); next_step_time = time + fix_ratio_frame_time; } else { if( step_mode==FAST_FORWARD ) { sync_step( 100 ); set_random_mode( STEP_RANDOM ); step(); clear_random_mode( STEP_RANDOM ); } else if( step_mode==FIX_RATIO ) { if( env_t::server ) { next_step_time += fix_ratio_frame_time; } else { const sint32 lag_time = (sint32)time - (sint32)next_step_time; if( lag_time > 0 ) { ms_difference += lag_time; next_step_time = time; } const sint32 nst_diff = clamp( ms_difference, -fix_ratio_frame_time * 2, fix_ratio_frame_time * 8 ) / 10; // allows timerate between 83% and 500% of normal next_step_time += fix_ratio_frame_time - nst_diff; ms_difference -= nst_diff; } const uint32 delta_t = (fix_ratio_frame_time*time_multiplier)/16; sync_step( delta_t ); display( delta_t ); if (++network_frame_count == settings.get_frames_per_step()) { set_random_mode( STEP_RANDOM ); step(); clear_random_mode( STEP_RANDOM ); network_frame_count = 0; } sync_steps = steps * settings.get_frames_per_step() + network_frame_count; switch(env_t::network_heavy_mode) { case 0: default: LCHKLST(sync_steps) = checklist_t(get_random_seed(), halthandle_t::get_next_check(), linehandle_t::get_next_check(), convoihandle_t::get_next_check()); break; case 2: heavy_rotate_saves(env_t::server ? "server" : "client", sync_steps, 10); // fall-through case 1: LCHKLST(sync_steps) = checklist_t(get_gamestate_hash()); } // some server side tasks if( env_t::networkmode && env_t::server ) { // broadcast sync info regularly and when lagged const sint64 timelag = (sint32)dr_time() - (sint32)next_step_time; if( (network_frame_count == 0 && timelag > fix_ratio_frame_time * settings.get_server_frames_ahead() / 2) || (sync_steps % env_t::server_sync_steps_between_checks) == 0 ) { const sint64 time_per_step = fix_ratio_frame_time * settings.get_frames_per_step(); static sint64 previous_lag = 0; if( timelag > 1*time_per_step ) { // log when server is lagged more than one step, but only if it gets worse (don't spam log) if (timelag/time_per_step > previous_lag/time_per_step) { dbg->warning("karte_t::interactive", "Server lagging by %lli ms (%d steps)", timelag, (sint32)(timelag/time_per_step) ); } } previous_lag = timelag; nwc_check_t* nwc = new nwc_check_t(sync_steps + 1, map_counter, LCHKLST(sync_steps), sync_steps); network_send_all(nwc, true); } else { // broadcast sync_step nwc_step_t* nwcstep = new nwc_step_t(sync_steps, map_counter); network_send_all(nwcstep, true); } } // no clients -> pause game if ( env_t::networkmode && env_t::pause_server_no_clients && socket_list_t::get_playing_clients() == 0 && !nwc_join_t::is_pending() ) { set_pause(true); // save the game in advance char fn[256]; // first save password hashes sprintf(fn, "server%d-pwdhash.sve", env_t::server); loadsave_t pwdwrfile; if (pwdwrfile.wr_open(fn, loadsave_t::zipped, 1, "hashes", SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK) { rdwr_player_password_hashes(&pwdwrfile); pwdwrfile.close(); } // remove passwords before transfer on the server and set default client mask // they will be restored in karte_t::laden for (int i = 0; i < PLAYER_UNOWNED; i++) { if (players[i] && !players[i]->access_password_hash().empty()) { players[i]->access_password_hash().clear(); } } // save game sprintf(fn, "server%d-network.sve", env_t::server); bool old_restore_UI = env_t::restore_UI; env_t::restore_UI = true; save(fn, false, SERVER_SAVEGAME_VER_NR, false); env_t::restore_UI = old_restore_UI; // restore password hashes for us sprintf(fn, "server%d-pwdhash.sve", env_t::server); loadsave_t pwdrdfile; if (pwdrdfile.rd_open(fn) == loadsave_t::FILE_STATUS_OK) { rdwr_player_password_hashes(&pwdrdfile); pwdrdfile.close(); } has_current_network_save = true; // now we have a current savegame to send immediately } } else { // normal ratio INT_CHECK( "karte_t::interactive()" ); set_random_mode( STEP_RANDOM ); step(); clear_random_mode( STEP_RANDOM ); uint32 cur_time = dr_time(); if (next_step_time > cur_time) { // slowly change idel time idle_time = ( (idle_time * 7) + (next_step_time - cur_time) ) / 8; } else { // but half if we are really far behind idle_time >>= 1; } INT_CHECK( "karte_t::interactive()" ); } } } // Interval-based server announcements if ( env_t::server && env_t::server_announce && env_t::server_announce_interval > 0 && dr_time() - server_last_announce_time >= (uint32)env_t::server_announce_interval * 1000 ) { announce_server( karte_t::SERVER_ANNOUNCE_HEARTBEAT ); } DBG_DEBUG4("karte_t::interactive", "point of loop return"); } while(!finish_loop && get_current_month()= quit_month ) { env_t::quit_simutrans = true; } // On quit announce server as being offline if( env_t::server && env_t::server_announce ) { announce_server( karte_t::SERVER_ANNOUNCE_GOODBYE ); } intr_enable(); gfx->set_cursor_visible(true); return finish_loop; #undef LRAND } // Announce server to central listing server // Status is one of: // 0 - startup // 1 - interval // 2 - shutdown void karte_t::announce_server(server_announce_type_t status) { DBG_DEBUG( "announce_server()", "status: %i", (int)status ); // Announce game info to server, format is: // st=on&dns=server.com&port=13353&rev=1234&pak=pak128&name=some+name&time=3,1923&size=256,256&active=[0-16]&locked=[0-16]&clients=[0-16]&towns=15&citizens=3245&factories=33&convoys=56&stops=17 // (This is the data part of an HTTP POST) if( env_t::server && env_t::server_announce ) { // in easy_server mode, we assume the IP may change frequently and thus query it before each announce cbuffer_t ip_buf, altip_buf; if( env_t::easy_server && status!=SERVER_ANNOUNCE_GOODBYE && get_external_IP(ip_buf,altip_buf) ) { // update IP just in case if( status == SERVER_ANNOUNCE_HEARTBEAT && (env_t::server_dns.compare( ip_buf )!=0 || env_t::server_alt_dns.compare( altip_buf )!=0) ) { announce_server( SERVER_ANNOUNCE_GOODBYE ); status = SERVER_ANNOUNCE_HELLO; // since starting with new IP // if we had uPnP, we may need to drill another hole in the firewall again; the delay is no problem, since all clients will be lost anyway char IP[256], altIP[256]; prepare_for_server( IP, altIP, env_t::server_port ); } // now update DNS info env_t::server_dns = (const char *)ip_buf; env_t::server_alt_dns = (const char *)altip_buf; } // Always send dns and port as these are used as the unique identifier for the server cbuffer_t buf; buf.append( "&dns=" ); encode_URI( buf, env_t::server_dns.c_str() ); buf.append( "&alt_dns=" ); encode_URI( buf, env_t::server_alt_dns.c_str() ); buf.printf( "&port=%u", env_t::server ); // Always send announce interval to allow listing server to predict next announce buf.printf( "&aiv=%u", env_t::server_announce_interval ); // Always send status, either online or offline if ( status == SERVER_ANNOUNCE_HELLO || status == SERVER_ANNOUNCE_HEARTBEAT ) { buf.append( "&st=1" ); } else { buf.append( "&st=0" ); } #ifndef REVISION # define REVISION 0 #endif // Simple revision used for matching (integer) buf.printf( "&rev=%d", (int)atol( QUOTEME(REVISION) )); // Complex version string used for display buf.printf( "&ver=Simutrans %s (r%s) built %s", QUOTEME(VERSION_NUMBER), QUOTEME(REVISION), QUOTEME(VERSION_DATE) ); // Pakset version buf.append( "&pak=" ); // Announce pak set, ideally get this from the copyright field of ground.Outside.pak char const* const copyright = ground_desc_t::outside->get_copyright(); if (copyright && STRICMP("none", copyright) != 0) { // construct from outside object copyright string encode_URI( buf, copyright ); } else { // construct from pak name std::string pak_name = env_t::pak_name; pak_name.erase( pak_name.length() - 1 ); encode_URI( buf, pak_name.c_str() ); } // TODO - change this to be the start date of the current map buf.printf( "&start=%u,%u", settings.get_starting_month() + 1, settings.get_starting_year() ); // Add server name for listing buf.append( "&name=" ); encode_URI( buf, env_t::server_name.c_str() ); // Add server comments for listing buf.append( "&comments=" ); encode_URI( buf, env_t::server_comments.c_str() ); // Add server maintainer email for listing buf.append( "&email=" ); encode_URI( buf, env_t::server_email.c_str() ); // Add server pakset URL for listing buf.append( "&pakurl=" ); encode_URI( buf, env_t::server_pakurl.c_str() ); // Add server info URL for listing buf.append( "&infurl=" ); encode_URI( buf, env_t::server_infurl.c_str() ); // Now add the game data part uint8 active = 0, locked = 0; for( uint8 i=0; iget_ai_id()!=player_t::EMPTY ) { active ++; if( players[i]->is_locked() ) { locked ++; } } } buf.printf( "&time=%u,%u", (get_current_month() % 12) + 1, get_current_month() / 12 ); buf.printf( "&size=%u,%u", get_size().x, get_size().y ); buf.printf( "&active=%u", active ); buf.printf( "&locked=%u", locked ); buf.printf( "&clients=%u", socket_list_t::get_playing_clients() ); buf.printf( "&towns=%u", cities.get_count() ); buf.printf( "&citizens=%u", cities.get_sum_weight() ); buf.printf( "&factories=%u", all_factories.get_count() ); buf.printf( "&convoys=%u", convoys().get_count()); buf.printf( "&stops=%u", haltestelle_t::get_alle_haltestellen().get_count() ); #if 0 // could transmit a very basic mini minimap (36x36) pxiel, only land or sea in 218 bytes char mapstr[6*36+1]; char* p = mapstr; uint16 bitcount = 1; for (scr_coord_val j = 0; j < 36; j++) { sint64 row = 0; for (scr_coord_val i = 0; i < 36; i++) { row <<= 1; const koord pos((i * get_size().x) / 36, (j * get_size().y) / 36); if (!lookup_kartenboden(pos)->is_water()) { row |= 1; } // uncode in URI letters if ((bitcount++ % 6) == 0) { if (row < 26) { *p++ = 'A' + row; } else if (row < 52) { *p++ = 'a' + row; } else if (row < 62) { *p++ = '0' + row; } else if (row == 63) { *p++ = '-'; } else { *p++ = '~'; } } } } *p = 0; buf.append("&map="); buf.append(p); #endif if (network_http_post(ANNOUNCE_SERVER1, ANNOUNCE_URL, buf, NULL)) { #ifdef ANNOUNCE_SERVER2 if (network_http_post(ANNOUNCE_SERVER2, ANNOUNCE_URL, buf, NULL)) #ifdef ANNOUNCE_SERVER3 if (network_http_post(ANNOUNCE_SERVER3, ANNOUNCE_URL, buf, NULL)) #endif #endif dbg->warning("announce_server", "All announce servers down!"); } // Record time of this announce server_last_announce_time = dr_time(); } } void karte_t::network_disconnect() { // force disconnect dbg->warning("karte_t::network_disconnect()", "Lost synchronisation with server."); network_core_shutdown(); destroy_all_win(true); clear_random_mode( INTERACTIVE_RANDOM ); step_mode = NORMAL; reset_timer(); clear_command_queue(); create_win({ gfx->get_screen_size().w/2 - 128, 40 }, new news_img("Lost synchronisation\nwith server."), w_info, magic_none); ticker::add_msg( translator::translate("Lost synchronisation\nwith server."), koord3d::invalid, SYSCOL_TEXT ); last_active_player_nr = active_player_nr; finish_loop = true; // kick me out to main screen } static bool sort_ware_by_name(const goods_desc_t* a, const goods_desc_t* b) { int diff = strcmp(translator::translate(a->get_name()), translator::translate(b->get_name())); return diff < 0; } // Returns a list of goods produced by factories that exist in current game const vector_tpl &karte_t::get_goods_list() { if (goods_in_game.empty()) { // Goods list needs to be rebuilt // Reset last vehicle filter in all depots, in case goods list has changed for(depot_t* const d : depot_t::get_depot_list()) { d->selected_filter = VEHICLE_FILTER_RELEVANT; } for(fabrik_t* const factory : get_fab_list()) { slist_tpl* const produced_goods = factory->get_produced_goods(); for(goods_desc_t const* const good : *produced_goods) { goods_in_game.insert_unique_ordered(good, sort_ware_by_name); } delete produced_goods; } goods_in_game.insert_at(0, goods_manager_t::passengers); goods_in_game.insert_at(1, goods_manager_t::mail); } return goods_in_game; } player_t *karte_t::get_public_player() const { return get_player(PLAYER_PUBLIC_NR); } uint32 karte_t::get_gamestate_hash() { adler32_stream_t *stream = new adler32_stream_t; stream_loadsave_t ls(stream); rdwr_gamestate(&ls, NULL); return stream->get_hash(); } simutrans-124.5/src/simutrans/world/simworld.h000066400000000000000000000767601520675460500215600ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef WORLD_SIMWORLD_H #define WORLD_SIMWORLD_H #include "../simconst.h" #include "../simtypes.h" #include "../simunits.h" #include "../convoihandle.h" #include "../halthandle.h" #include "../tpl/weighted_vector_tpl.h" #include "../tpl/array2d_tpl.h" #include "../tpl/vector_tpl.h" #include "../tpl/slist_tpl.h" #include "../dataobj/settings.h" #include "../dataobj/loadsave.h" #include "../dataobj/rect.h" #include "../utils/checklist.h" #include "../utils/sha1_hash.h" #include "simplan.h" #include "surface.h" #include "../simdebug.h" struct sound_info; class stadt_t; class fabrik_t; class gebaeude_t; class zeiger_t; class grund_t; class planquadrat_t; class main_view_t; class interaction_t; class tool_t; class scenario_t; class message_t; class chat_message_t; class way_desc_t; class network_world_command_t; class goods_desc_t; class memory_rw_t; class viewport_t; class records_t; class loadingscreen_t; class terraformer_t; class building_desc_t; /** * Threaded function caller. */ typedef void (karte_t::*xy_loop_func)(sint16, sint16, sint16, sint16); /** * The map is the central part of the simulation. It stores all data and objects. * @brief Stores all data and objects of the simulated world. */ class karte_t : public surface_t { friend karte_t* world(); // to access the single instance friend class karte_ptr_t; // to access the single instance static karte_t* world; ///< static single instance public: // true, if there is a current valid savegame for networkplay bool has_current_network_save; // kind of map enum { AUTO_GENERATED, SCENARIO_WORLD, NEW_WORLD, RESTORED_WORLD, CLIENT_WORLD, LOADED_WORLD } type_of_generation; /** * Height of a point of the map with "perlin noise". * Uses map roughness and mountain height from @p sets. */ static sint32 perlin_hoehe(settings_t const *sets, koord k, koord size); /** * Loops over tiles setting heights from perlin noise */ void perlin_hoehe_loop(sint16, sint16, sint16, sint16); enum player_cost { WORLD_CITIZENS = 0, ///< total people WORLD_GROWTH, ///< growth (just for convenience) WORLD_TOWNS, ///< number of all cities WORLD_FACTORIES, ///< number of all consuming only factories WORLD_CONVOIS, ///< total number of convois WORLD_CITYCARS, ///< number of citycars generated WORLD_PAS_RATIO, ///< percentage of passengers that started successful WORLD_PAS_GENERATED, ///< total number generated WORLD_MAIL_RATIO, ///< percentage of mail that started successful WORLD_MAIL_GENERATED, ///< all letters generated WORLD_GOODS_RATIO, ///< ratio of chain completeness WORLD_TRANSPORTED_GOODS, ///< all transported goods MAX_WORLD_COST }; #define MAX_WORLD_HISTORY_YEARS (12) // number of years to keep history #define MAX_WORLD_HISTORY_MONTHS (12) // number of months to keep history enum step_mode_t { NORMAL = 0, PAUSE_FLAG = 1 << 0, FAST_FORWARD = 1 << 1, FIX_RATIO = 1 << 2 }; private: /** * @name Map properties * Basic map properties are stored in this variables. * @{ */ /** * @brief Map settings are stored here. */ settings_t settings; /** @} */ /** * All cursor interaction goes via this function, it will call save_mouse_funk first with * init, then with the position and with exit, when another tool is selected without click * @see tool/simtool.cc for practical examples of such functions. */ tool_t *selected_tool[MAX_PLAYER_COUNT]; /** * Redraw whole map. */ bool dirty; /* * Redraw background. */ bool background_dirty; /** * True during destroying of the map. */ bool destroying; /** * The rotation of the map when first loaded. */ uint8 loaded_rotation; /** * The one and only camera looking at our world. */ viewport_t *viewport; /** * @name Mouse pointer and cursor management * @{ */ /** * @brief Map mouse cursor tool. */ zeiger_t *zeiger; /** @} */ /** * Time when last mouse moved to check for ambient sound events. */ uint32 mouse_rest_time; /** * Waiting time before next event. */ uint32 sound_wait_time; /** * Gets an ambient sound id appropriate for the given tile. * Returns NO_SOUND if no appropriate sound is found. */ sint16 get_sound_id(grund_t *gr); /** * If true, this map cannot be saved. */ bool nosave; bool nosave_warning; /** * Changes the season and/or snowline height */ void recalc_season_snowline(bool set_pending); /** * >0 means a season change is needed */ sint8 pending_season_change; sint8 pending_snowline_change; /** * Recalculates sleep time etc. */ void update_frame_sleep_time(); /** * Array containing the convois. */ vector_tpl convoi_array; /** * Array containing the factories. */ vector_tpl all_factories; /** * Stores a list of goods produced by factories currently in the game; */ vector_tpl goods_in_game; weighted_vector_tpl attractions; vector_tpl labels; /** * Stores the cities. */ weighted_vector_tpl cities; sint64 last_month_bev; /** * The recorded history so far. */ sint64 finance_history_year[MAX_WORLD_HISTORY_YEARS][MAX_WORLD_COST]; /** * The recorded history so far. */ sint64 finance_history_month[MAX_WORLD_HISTORY_MONTHS][MAX_WORLD_COST]; /** * World record speed manager. * Keeps track of the fastest vehicles in game. */ records_t *records; /** * Attached view to this world. */ main_view_t *view; /** * Event manager of this world. */ interaction_t *eventmanager; private: /** * The fractal generation of the map is not perfect. * cleanup_karte() eliminates errors. */ void cleanup_karte( int xoff, int yoff ); /** * @name Player management * Variables related to the player management in game. * @{ */ /** * The players of the game. * @note Standard human player has index 0, public player 1. */ player_t *players[MAX_PLAYER_COUNT]; /** * Active player. */ player_t *active_player; /** * Active player index. */ uint8 active_player_nr; /** * Locally stored password hashes, will be used after reconnect to a server. */ pwd_hash_t player_password_hash[MAX_PLAYER_COUNT]; /** @} */ /** * Counter for schedules. * If a new schedule is active, this counter will increment * stations check this counter and will reroute their goods if changed. */ uint8 schedule_counter; /** * @name Display timing and scheduling * These variables store system display timings in the past frames * and allow for adequate adjustments to adapt to the system performance * and available resources (also in network mode). * @{ */ /** * ms since creation. * @note The time is in ms (milliseconds) */ uint32 ticks; /** * Ticks counter at last steps. * @note The time is in ms (milliseconds) */ uint32 last_step_ticks; /** * From now on is next month. * @note The time is in ms (milliseconds) */ uint32 next_month_ticks; /// Default time stretching factor, 4 bits fractional part uint32 time_multiplier; /// @ref step_mode_t uint8 step_mode; /// @note variable used in interactive() uint32 sync_steps; // The maximum sync_steps that a client can safely advance to. uint32 sync_steps_barrier; #define LAST_CHECKLISTS_COUNT 64 /// @note variable used in interactive() checklist_t last_checklists[LAST_CHECKLISTS_COUNT]; #define LCHKLST(x) (last_checklists[(x) % LAST_CHECKLISTS_COUNT]) /// @note variable used in interactive() uint8 network_frame_count; /** * @note Variable used in interactive(). * @note Set in reset_timer(). */ sint32 fix_ratio_frame_time; /// Average number of calls to display() per second, 4 bits fractional part uint32 realFPS; /// Average number of calls to step() per second. /// Fractional factor of 10, so the real average number of steps is simloops/10 uint32 simloops; /// Index for last_frame_ms uint8 last_frame_idx; /// Real-world time when the last display() finished, indexed by @ref last_frame_idx uint32 last_frame_ms[32]; /// Time when the last step started, indexed by @ref steps counter (modulo array size) uint32 last_step_nr[32]; /** * ms, when the last time events were handled. * To calculate the fps and the simloops. */ uint32 last_interaction; /** * ms, when the next step is to be done. * To calculate the fps and the simloops. */ uint32 next_step_time; /** * ms, when the next check if we need to start next song */ uint32 next_midi_time; /** * Miscellaneus stuff that do not need to be updated frequently * Currently used for rich presence and achievements */ uint32 next_misc_time; /// To calculate the fps and the simloops. uint32 idle_time; /** @} */ /** * Current accumulated month number, counting January of year 0 as 0. * @note last_month + (last_year*12); */ sint32 current_month; /** * Last month 0..11 */ sint32 last_month; /** * Last year. */ sint32 last_year; /** * Number of steps since creation. */ sint32 steps; /** * Flag, that now no sound will play. */ bool is_sound; /** * Flag for ending simutrans (true -> end simutrans). */ bool finish_loop; /** * May change due to timeline. */ const way_desc_t *city_road; /** * What game objectives. */ scenario_t *scenario; /** * Holds all the text messages in the messagebox (chat, new vehicle etc). */ message_t *msg; chat_message_t* chat_msg; /** * Array indexed per way type. Used to determine the speedbonus. */ sint32 average_speed[8]; /** * Used to distribute the workload when changing seasons to several steps. */ uint32 tile_counter; /** * To identify different stages of the same game. */ uint32 map_counter; /** * Recalculated speed bonus for different vehicles. */ void recalc_average_speed(); /** * Monthly actions. */ void new_month(); /** * Yearly actions. */ void new_year(); /** * Internal saving method. */ void save(loadsave_t *file, bool silent); public: /** * Internal loading method. */ void load(loadsave_t *file); private: void rdwr_gamestate(loadsave_t *file, loadingscreen_t *ls); /** * Removes all objects, deletes all data structures and frees all accessible memory. */ void destroy(); /** * Restores history for older savegames. */ void restore_history(bool restore_transported_only); /** * Will create rivers. */ void create_rivers(sint16 number); /** * Will create lakes (multithreaded). */ void create_lakes_loop(sint16, sint16, sint16, sint16); /** * Will create lakes. * @param height absolute height below which lakes will be created. */ void create_lakes( int xoff, int yoff, sint8 height ); /** * Will create beaches. */ void create_beaches( int xoff, int yoff ); /** * Distribute groundobjs and cities on the map but not * in the rectangle from (0,0) till (old_x, old_y). * It's now an extra function so we don't need the code twice. */ void distribute_cities(int new_cities, sint32 new_mean_citizen_count, sint16 old_x, sint16 old_y ); void distribute_groundobjs(sint16 old_x, sint16 old_y); void distribute_movingobjs(sint16 old_x, sint16 old_y); /** * The last time when a server announce was performed (in ms). */ uint32 server_last_announce_time; enum { SYNCX_FLAG = 1 << 0, GRIDS_FLAG = 1 << 1 }; void world_xy_loop(xy_loop_func func, uint8 flags); static void *world_xy_loop_thread(void *); /** * Loops over plans after load. */ void plans_finish_rd(sint16, sint16, sint16, sint16); /** * Updates all images. */ void update_map_intern(sint16, sint16, sint16, sint16); bool can_flood_to_depth(koord k, sint8 new_water_height, sint8 *stage, sint8 *our_stage, sint16, sint16, sint16, sint16) const; void flood_to_depth(sint8 new_water_height, sint8 *stage); public: enum server_announce_type_t { SERVER_ANNOUNCE_HELLO = 0, ///< my server is now up SERVER_ANNOUNCE_HEARTBEAT = 1, ///< my server is still up SERVER_ANNOUNCE_GOODBYE = 2, ///< my server is now down }; /** * Announce server and current state to listserver. * @param status Specifies what information should be announced * or offline (the latter only in cases where it is shutting down) */ void announce_server(server_announce_type_t status); /** * Returns the messagebox message container. */ message_t *get_message() { return msg; } /** * Returns the messagebox message container. */ chat_message_t* get_chat_message() const { return chat_msg; } /** * Set to something useful, if there is a total distance != 0 to show in the bar below. */ koord3d show_distance; /** * Absolute month. */ inline uint32 get_last_month() const { return last_month; } /** * Returns last year. */ inline sint32 get_last_year() const { return last_year; } /** * dirty: redraw whole screen. */ void set_dirty() {dirty=true;} /** * dirty: redraw whole screen. */ void unset_dirty() {dirty=false;} /** * dirty: redraw whole screen. */ bool is_dirty() const {return dirty;} /** * background_dirty: redraw background. */ void set_background_dirty() { background_dirty = true; } /** * background_dirty: redraw whole screen. */ bool is_background_dirty() const { return background_dirty; } /** * background_dirty: redraw background. */ void unset_background_dirty() { background_dirty = false; } // do the internal accounting void buche(sint64 betrag, player_cost type); // calculates the various entries void update_history(); scenario_t *get_scenario() const { return scenario; } void set_scenario(scenario_t *s); /** * Returns the finance history for player. */ sint64 get_finance_history_year(int year, int type) const { return finance_history_year[year][type]; } /** * Returns the finance history for player. */ sint64 get_finance_history_month(int month, int type) const { return finance_history_month[month][type]; } /** * Returns pointer to finance history for player. */ const sint64* get_finance_history_year() const { return *finance_history_year; } /** * Returns pointer to finance history for player. */ const sint64* get_finance_history_month() const { return *finance_history_month; } /** * Recalcs all map images. */ void update_map(); /** * Recalcs images after change of underground mode. */ void update_underground(); /** * @brief Prepares an area of the map to be drawn. * * New area is the area that will be prepared. Old area is the area that was * already prepared. Only the difference between the two rects is prepared. */ void prepare_tiles(rect_t const& new_area, rect_t const& old_area); /** * @returns true if world gets destroyed */ bool is_destroying() const { return destroying; } /** * Gets the world view. */ main_view_t *get_view() const { return view; } /** * Gets the world viewport. */ viewport_t *get_viewport() const { return viewport; } /** * Sets the world view. */ void set_view(main_view_t *v) { view = v; } /** * Sets the world event manager. */ void set_eventmanager(interaction_t *em) { eventmanager = em; } settings_t const& get_settings() const { return settings; } settings_t& get_settings() { return settings; } /// returns current speed bonus sint32 get_average_speed(waytype_t typ) const { return average_speed[ (typ==16 ? 3 : (int)(typ-1)&7 ) ]; } /// speed record management sint32 get_record_speed( waytype_t w ) const; void notify_record( convoihandle_t cnv, sint32 max_speed, koord3d k ); /// time lapse mode ... bool is_paused() const { return step_mode&PAUSE_FLAG; } /// stops the game with interaction void set_pause( bool ); bool is_fast_forward() const { return step_mode == FAST_FORWARD; } void set_fast_forward(bool ff); bool is_step_mode_normal() const { return step_mode == NORMAL; } /** * (un)pause for network games. */ void network_game_set_pause(bool pause_, uint32 syncsteps_); /** * @return The active mouse cursor. */ zeiger_t * get_zeiger() const { return zeiger; } /** * Marks an area using the grund_t mark flag. */ void mark_area( const koord3d center, const koord radius, const bool mark ) const; /** * Player management here */ uint8 sp2num(player_t *player); player_t* get_player(uint8 n) const { return players[n&15]; } // return player or gives error and cerates player to recover broken savegames player_t* get_player_or_create(uint8 n); player_t* get_active_player() const { return active_player; } uint8 get_active_player_nr() const { return active_player_nr; } void switch_active_player(uint8 nr, bool silent); const char *init_new_player( uint8 nr, uint8 type ); void store_player_password_hash( uint8 player_nr, const pwd_hash_t& hash ); const pwd_hash_t& get_player_password_hash( uint8 player_nr ) const { return player_password_hash[player_nr]; } void clear_player_password_hashes(); void rdwr_player_password_hashes(loadsave_t *file); void remove_player(uint8 player_nr); /** * Get the default public service player. * @return the default public service player */ player_t *get_public_player() const; /** * Network safe initiation of new and deletion of players, change freeplay. * * @param cmd * @param player_nr * @param param Player type (human / C ai / script ai) of new players. * @param scripted_call */ void call_change_player_tool(uint8 cmd, uint8 player_nr, uint16 param, bool scripted_call=false); enum change_player_tool_cmds { new_player = 1, toggle_freeplay = 2, delete_player = 3, toggle_player_active = 4 }; /** * @param cmd * @param player_nr * @param param * @param public_player_unlocked * @param exec If false checks whether execution is allowed, if true executes tool. * @returns Whether execution is allowed. */ bool change_player_tool(uint8 cmd, uint8 player_nr, uint16 param, bool public_player_unlocked, bool exec); /** * If a schedule is changed, it will increment the schedule counter * every step the haltestelle will check and reroute the goods if needed. */ uint8 get_schedule_counter() const { return schedule_counter; } /** * If a schedule is changed, it will increment the schedule counter * every step the haltestelle will check and reroute the goods if needed. */ void set_schedule_counter(); /** * @note Often used, therefore found here. */ bool use_timeline() const { return settings.get_use_timeline(); } void reset_timer(); void reset_interaction(); void step_year(); /** * Jump one or more months ahead. * @note Updating history! */ void step_month( sint16 months=1 ); /** * @return Either 0 or the current year*12 + month */ uint16 get_timeline_year_month() const { return settings.get_use_timeline() ? current_month : 0; } /** * Number of ticks per day in bits. * @see ticks_per_world_month */ uint32 ticks_per_world_month_shift; /** * Number of ticks per MONTH! */ uint32 ticks_per_world_month; void set_ticks_per_world_month_shift(uint32 bits) {ticks_per_world_month_shift = bits; ticks_per_world_month = (1 << ticks_per_world_month_shift); } /** * Converts speed (yards per tick) into tiles per month * * A derived quantity: * speed * ticks_per_world_month / yards_per_tile * == speed << ticks_per_world_month_shift (pak-set dependent, 18 in old games) * >> yards_per_tile_shift (which is 12 + 8 = 20, see above) * This is hard to do with full generality, because shift operators * take positive numbers! */ uint32 speed_to_tiles_per_month(uint32 speed) const { const int left_shift = ticks_per_world_month_shift - YARDS_PER_TILE_SHIFT; if (left_shift >= 0) { return speed << left_shift; } else { const int right_shift = -left_shift; // round to nearest return (speed + (1<<(right_shift -1)) ) >> right_shift; } } /** * Scales value proportionally with month length. * Used to scale monthly maintenance costs and factory production. * @returns value << ( ticks_per_world_month_shift -18 ) */ sint64 scale_with_month_length(sint64 value) { const int left_shift = ticks_per_world_month_shift - 18; if(left_shift >= 0) { return value << left_shift; } else { return value >> (-left_shift); } } /** * Scales value inverse proportionally with month length. * Used to scale monthly maintenance costs, city growth, and factory production. * @returns value << ( 18 - ticks_per_world_month_shift ) */ sint64 inverse_scale_with_month_length(sint64 value) { const int left_shift = (int)ticks_per_world_month_shift-18; if( left_shift < 0 ) { return value << (-left_shift); } else { return value >> left_shift; } } sint32 get_time_multiplier() const; void change_time_multiplier( sint32 delta ); /** * @return 0=winter, 1=spring, 2=summer, 3=autumn * Here instead of in surface_t because this is required for the script API */ uint8 get_season() const { return season; } /** * Time since map creation or the last load in ms. */ uint32 get_ticks() const { return ticks; } uint32 get_next_month_ticks() const { return next_month_ticks; } /** * Absolute month (count start year zero). */ uint32 get_current_month() const { return current_month; } /** * current city road. * @note May change due to timeline. */ const way_desc_t* get_city_road() const { return city_road; } /** * Number of steps elapsed since the map was generated. */ sint32 get_steps() const { return steps; } /** * Idle time. Nur zur Anzeige verwenden! */ uint32 get_idle_time() const { return idle_time; } /** * Number of frames displayed in the last real time second. */ uint32 get_realFPS() const { return realFPS; } /** * Number of simulation loops in the last second. Can be very inaccurate! */ uint32 get_simloops() const { return simloops; } /** * Returns the current snowline height. */ sint8 get_snowline() const { return snowline; } void set_mouse_rest_time(uint32 new_val) { mouse_rest_time = new_val; } void set_sound_wait_time(uint32 new_val) { sound_wait_time = new_val; } public: /** * Set a new tool as current: calls local_set_tool or sends to server/queue (then suspend is true). */ void set_tool_api(tool_t* tool_in, player_t* player, bool& suspended); /** * Set a new tool as current without suspending parameter */ void set_tool(tool_t* tool_in, player_t* player) { bool b; set_tool_api(tool_in, player, b); } /** * Set a new tool on our client, calls init. */ void local_set_tool( tool_t *tool_in, player_t* player ); tool_t *get_tool(uint8 nr) const { return selected_tool[nr]; } /* since move can alter the map state * (e.g. route search for marking tiles) * we defer it to outside sync_step */ void set_deferred_move_to(koord3d pos, uint8 flags); /** * Calls the work method of the tool. * Takes network and scenarios into account. */ const char* call_work_api(tool_t *t, player_t *pl, koord3d pos, bool &suspended); /** * Initialize map. * * @param sets Game settings. * @param heights */ void init(settings_t *sets, sint8 const* heights); void init_tiles(); void enlarge_map(settings_t const*, sint8 const* h_field); karte_t(); ~karte_t(); /** * File version used when loading (or current if generated) * @note Useful for finish_rd */ uint32 load_version; public: // the convois are also handled each step => thus we keep track of them too void add_convoi(convoihandle_t); void rem_convoi(convoihandle_t); vector_tpl const& convoys() const { return convoi_array; } /** * To access the cities array. */ const weighted_vector_tpl& get_cities() const { return cities; } /// Try to add a new city at @p pos with @p citizens inhabitants. /// Returns NULL on failure. stadt_t *create_city(koord pos, sint32 citizens, const building_desc_t* th=NULL, sint16 rotation=-1); /// Removes town from map, houses will be left over. bool remove_city(stadt_t *s); /* tourist attraction list */ void add_attraction(gebaeude_t *gb); void remove_attraction(gebaeude_t *gb); const weighted_vector_tpl &get_attractions() const {return attractions; } void add_label(koord k); void remove_label(koord k); const vector_tpl& get_label_list() const { return labels; } bool add_fab(fabrik_t *fab); bool rem_fab(fabrik_t *fab); // int get_fab_index(fabrik_t* fab) const { return all_factories.index_of(fab); } fabrik_t* get_fab(unsigned index) const { return index < all_factories.get_count() ? all_factories[index]:NULL; } const vector_tpl& get_fab_list() const { return all_factories; } /** * Returns a list of goods produced by factories that exist in current game. */ const vector_tpl &get_goods_list(); /** * Searches and returns the closest city * but prefers even farther cities if within their city limits */ stadt_t *find_nearest_city(koord k) const; bool cannot_save() const { return nosave; } void set_nosave() { nosave = true; nosave_warning = true; } void set_nosave_warning() { nosave_warning = true; } /// rotate plans by 90 degrees void rotate90_plans(sint16 x_min, sint16 x_max, sint16 y_min, sint16 y_max); /// rotate map view by 90 degrees void rotate90(); template class sync_list_t { friend class karte_t; vector_tpl list; ///< list of sync-steppable objects T *currently_deleting; ///< deleted durign sync_step, safeguard calls to remove bool sync_step_running; public: sync_list_t() : currently_deleting(NULL), sync_step_running(false) {} void add(T *obj) { assert(!sync_step_running); list.append(obj); } void remove(T *obj) { if(sync_step_running) { if (obj == currently_deleting) { return; } assert(false); } else { list.remove(obj); } } private: void sync_step(uint32 delta_t) { sync_step_running = true; currently_deleting = NULL; for(uint32 i=0; isync_step(delta_t)) { case SYNC_OK: break; case SYNC_DELETE: currently_deleting = ss; delete ss; currently_deleting = NULL; /* fall-through */ case SYNC_REMOVE: ss = list.pop_back(); if (i < list.get_count()) { list[i] = ss; } } } sync_step_running = false; } /// clears list, does not delete the objects void clear() { list.clear(); currently_deleting = NULL; sync_step_running = false; } }; sync_list_t sync; ///< vehicles sync_list_t sync_buildings; ///< animated buildings sync_list_t sync_roadsigns; ///< traffic lights /** * Synchronous stepping of objects like vehicles. */ void sync_step(uint32 delta_t); // advance also the timer void display(uint32 delta_t); /** * Tasks that are more time-consuming, like route search of vehicles and production of factories. */ void step(); public: /** * Calculates appropriate climate for a region using elliptic areas for each */ void calc_climate_map_region( sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom ); /** * Calculates appropriate climate for a region using elliptic areas for each */ void calc_humidity_map_region( sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom ); /** * assign climated from the climate map to a region */ void assign_climate_map_region( sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom ); /** * Since the trees follow humidity, we have to redistribute them only in the new region */ void distribute_trees_region( sint16 xtop, sint16 ytop, sint16 xbottom, sint16 ybottom ); /** * Loop recalculating transitions - suitable for multithreading */ void recalc_transitions_loop(sint16, sint16, sint16, sint16); /** * Loop cleans grounds so that they have correct boden and slope - suitable for multithreading */ void cleanup_grounds_loop(sint16, sint16, sint16, sint16); /** * @return A list of all buildable squares with size w, h. * @note Only used for town creation at the moment. */ slist_tpl * find_squares(sint16 w, sint16 h, climate_bits cl, sint16 old_x, sint16 old_y) const; /** * Plays the sound when the position is inside the visible region. * The sound plays lower when the position is outside the visible region. * @param k Position at which the event took place. * @param idx Index of the sound * @param t is the type of sound (for selective muting etc.) */ bool play_sound_area_clipped(koord k, uint16 idx, sound_type_t t) const; void mute_sound( bool state ) { is_sound = !state; } /* if start is true, the current map will be used as servergame * Does not announce a new map! */ void switch_server( bool start_server, bool port_forwarding ); /** * Saves the map to a file. * * @param filename name of the file to write. * @param autosave * @param version * @param silent */ void save(const char *filename, bool autosave, const char *version, bool silent); /** * Loads a map from a file. * @param filename name of the file to read. */ bool load(const char *filename); /** * Creates a map from a heightfield. * @param sets game settings. */ void load_heightfield(settings_t *sets); /** * Stops simulation and optionally closes the game. * @param exit_game If true, the game will also close. */ void stop(bool exit_game); /** * Main loop with event handling. * @return false to exit. */ bool interactive(uint32 quit_month); uint32 get_sync_steps() const { return sync_steps; } /** * Checks whether checklist is available, ie given sync_step is not too far into past. */ bool is_checklist_available(const uint32 sync_step) const { return sync_step + LAST_CHECKLISTS_COUNT > sync_steps; } const checklist_t& get_checklist_at(const uint32 sync_step) const { return LCHKLST(sync_step); } void set_checklist_at(const uint32 sync_step, const checklist_t &chklst) { LCHKLST(sync_step) = chklst; } const checklist_t& get_last_checklist() const { return LCHKLST(sync_steps); } uint32 get_last_checklist_sync_step() const { return sync_steps; } void command_queue_append(network_world_command_t*) const; void clear_command_queue() const; void network_disconnect(); /** * To identify the current map. */ uint32 get_map_counter() const { return map_counter; } void set_map_counter(uint32 new_map_counter); /** * Called by the server before sending the sync commands. */ uint32 generate_new_map_counter() const; /** * Generates hash of game state by streaming a save to a hash function */ uint32 get_gamestate_hash(); private: void process_network_commands(sint32* ms_difference); void do_network_world_command(network_world_command_t *nwc); uint32 get_next_command_step(); }; /** * Returns pointer to single instance of the world. */ inline karte_t *world() { return karte_t::world; } /** * Class to access the pointer to the world. * No need to initialize it. */ class karte_ptr_t { public: /// dereference operator: karte_ptr_t can be used as it would be karte_t* karte_t& operator*() { assert( karte_t::world ); return *karte_t::world; } const karte_t& operator*() const { assert( karte_t::world ); return *karte_t::world; } karte_ptr_t() {} /// dereference operator: karte_ptr_t can be used as it would be karte_t* karte_t* operator->() { assert( karte_t::world ); return karte_t::world; } const karte_t* operator->() const { assert( karte_t::world ); return karte_t::world; } /// cast to karte_t* operator karte_t* () const { return karte_t::world; } private: karte_ptr_t(const karte_ptr_t&); karte_ptr_t& operator=(const karte_ptr_t&); // no cast to bool please operator bool () const; }; #endif simutrans-124.5/src/simutrans/world/surface.cc000066400000000000000000000533611520675460500214760ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "surface.h" #include "simworld.h" #include "terraformer.h" #include "../descriptor/ground_desc.h" #include "../ground/grund.h" #include "../player/simplay.h" #define array_koord(px,py) (px + py * get_size().x) static sint8 median( sint8 a, sint8 b, sint8 c ) { #if 0 if( a==b || a==c ) { return a; } else if( b==c ) { return b; } else { // noting matches // return (3*128+1 + a+b+c)/3-128; return -128; } #elif 0 if( a<=b ) { return b<=c ? b : max(a,c); } else { return b>c ? b : min(a,c); } #else return (6*128+3 + a+a+b+b+c+c)/6-128; #endif } surface_t::surface_t() : climate_map(0, 0), humidity_map(0, 0) { } surface_t::~surface_t() { } koord surface_t::get_closest_coordinate(koord outside_pos) { outside_pos.clip_min(koord(0,0)); outside_pos.clip_max(koord(get_size().x-1,get_size().y-1)); return outside_pos; } bool surface_t::is_water(koord pos, koord dim) const { koord k; for( k.y = pos.y; k.y < pos.y + dim.y; k.y++ ) { for( k.x = pos.x; k.x < pos.x + dim.x; k.x++ ) { if( !is_within_grid_limits( k + koord(1, 1) ) || max_hgt(k) > get_water_hgt(k) ) { return false; } } } return true; } bool surface_t::square_is_free(koord pos, sint16 w, sint16 h, int *last_y, climate_bits cl) const { if(pos.x < 0 || pos.y < 0 || pos.x+w > get_size().x || pos.y+h > get_size().y) { return false; } grund_t *gr = lookup_kartenboden(pos); const sint16 platz_base_h = gr->get_hoehe(); // remember the base height of the first tile const sint16 platz_max_h = gr->get_hoehe() + slope_t::max_diff( gr->get_grund_hang() ); // remember the max height of the first tile koord k_check; for(k_check.x=pos.x; k_check.x=pos.y; k_check.y--) { const grund_t *gr = lookup_kartenboden(k_check); // we can built, if: max height all the same, everything removable and no buildings there slope_t::type slope = gr->get_grund_hang(); sint8 max_height = gr->get_hoehe() + slope_t::max_diff(slope); climate test_climate = get_climate(k_check); if( cl & (1 << water_climate) && test_climate != water_climate ) { bool neighbour_water = false; for(int i=0; i<8 && !neighbour_water; i++) { if( is_within_limits(k_check + koord::neighbours[i]) && get_climate( k_check + koord::neighbours[i] ) == water_climate ) { neighbour_water = true; } } if( neighbour_water ) { test_climate = water_climate; } } if( (platz_max_h != max_height && platz_base_h != gr->get_hoehe()) || !gr->ist_natur() || gr->kann_alle_obj_entfernen(NULL) != NULL || (cl & (1 << test_climate)) == 0 || ( slope && (lookup( gr->get_pos()+koord3d(0,0,1) ) || (slope_t::max_diff(slope)==2 && lookup( gr->get_pos()+koord3d(0,0,2) )) )) ) { if( last_y ) { *last_y = k_check.y; } return false; } } } return true; } sint8 surface_t::min_hgt_nocheck(const koord k) const { // more optimised version of min_hgt code const sint8 * p = &grid_hgts[k.x + k.y*(sint32)(cached_grid_size.x+1)]; const int h1 = *p; const int h2 = *(p+1); const int h3 = *(p+get_size().x+2); const int h4 = *(p+get_size().x+1); return min(min(h1,h2), min(h3,h4)); } sint8 surface_t::max_hgt_nocheck(const koord k) const { // more optimised version of max_hgt code const sint8 * p = &grid_hgts[k.x + k.y*(sint32)(cached_grid_size.x+1)]; const int h1 = *p; const int h2 = *(p+1); const int h3 = *(p+get_size().x+2); const int h4 = *(p+get_size().x+1); return max(max(h1,h2), max(h3,h4)); } sint8 surface_t::min_hgt(const koord k) const { const sint8 h1 = lookup_hgt(k); const sint8 h2 = lookup_hgt(k+koord(1, 0)); const sint8 h3 = lookup_hgt(k+koord(1, 1)); const sint8 h4 = lookup_hgt(k+koord(0, 1)); return min(min(h1,h2), min(h3,h4)); } sint8 surface_t::max_hgt(const koord k) const { const sint8 h1 = lookup_hgt(k); const sint8 h2 = lookup_hgt(k+koord(1, 0)); const sint8 h3 = lookup_hgt(k+koord(1, 1)); const sint8 h4 = lookup_hgt(k+koord(0, 1)); return max(max(h1,h2), max(h3,h4)); } void surface_t::get_height_slope_from_grid(koord k, sint8 &hgt, slope_t::type &slope) { if( (k.x | k.y | (cached_grid_size.x - k.x-1) | (cached_grid_size.y - k.y-1)) >= 0 ) { // inside map, so without further checks const sint8 h_n = lookup_hgt_nocheck(k.x, k.y); const sint8 h_w = lookup_hgt_nocheck(k.x, k.y+1); const sint8 h_s = lookup_hgt_nocheck(k.x+1, k.y+1); const sint8 h_e = lookup_hgt_nocheck(k.x+1, k.y); hgt = min(min(h_n, h_e), min(h_s, h_w)); slope = slope_t::northwest * min(h_n - hgt, 2); slope |= slope_t::northeast * min(h_e - hgt, 2); slope |= slope_t::southeast * min(h_s - hgt, 2); slope |= slope_t::southwest * min(h_w - hgt, 2); } } // fills array with neighbour heights void surface_t::get_neighbour_heights(const koord k, sint8 neighbour_height[8][4]) const { for( int i = 0; i < 8; i++ ) { // 0 = nw, 1 = w etc. planquadrat_t *pl2 = access( k + koord::neighbours[i] ); if( pl2 ) { grund_t *gr2 = pl2->get_kartenboden(); slope_t::type slope_corner = gr2->get_grund_hang(); for( int j = 0; j < 4; j++ ) { neighbour_height[i][j] = gr2->get_hoehe() + corner_sw(slope_corner); slope_corner /= slope_t::southeast; } } else { switch(i) { case 0: // nw neighbour_height[i][0] = groundwater; neighbour_height[i][1] = max( lookup_hgt( k+koord(0,0) ), get_water_hgt( k ) ); neighbour_height[i][2] = groundwater; neighbour_height[i][3] = groundwater; break; case 1: // w neighbour_height[i][0] = groundwater; neighbour_height[i][1] = max( lookup_hgt( k+koord(0,1) ), get_water_hgt( k ) ); neighbour_height[i][2] = max( lookup_hgt( k+koord(0,0) ), get_water_hgt( k ) ); neighbour_height[i][3] = groundwater; break; case 2: // sw neighbour_height[i][0] = groundwater; neighbour_height[i][1] = groundwater; neighbour_height[i][2] = max( lookup_hgt( k+koord(0,1) ), get_water_hgt( k ) ); neighbour_height[i][3] = groundwater; break; case 3: // s neighbour_height[i][0] = groundwater; neighbour_height[i][1] = groundwater; neighbour_height[i][2] = max( lookup_hgt( k+koord(1,1) ), get_water_hgt( k ) ); neighbour_height[i][3] = max( lookup_hgt( k+koord(0,1) ), get_water_hgt( k ) ); break; case 4: // se neighbour_height[i][0] = groundwater; neighbour_height[i][1] = groundwater; neighbour_height[i][2] = groundwater; neighbour_height[i][3] = max( lookup_hgt( k+koord(1,1) ), get_water_hgt( k ) ); break; case 5: // e neighbour_height[i][0] = max( lookup_hgt( k+koord(1,1) ), get_water_hgt( k ) ); neighbour_height[i][1] = groundwater; neighbour_height[i][2] = groundwater; neighbour_height[i][3] = max( lookup_hgt( k+koord(1,0) ), get_water_hgt( k ) ); break; case 6: // ne neighbour_height[i][0] = max( lookup_hgt( k+koord(1,0) ), get_water_hgt( k ) ); neighbour_height[i][1] = groundwater; neighbour_height[i][2] = groundwater; neighbour_height[i][3] = groundwater; break; case 7: // n neighbour_height[i][0] = max( lookup_hgt( k+koord(0,0) ), get_water_hgt( k ) ); neighbour_height[i][1] = max( lookup_hgt( k+koord(1,0) ), get_water_hgt( k ) ); neighbour_height[i][2] = groundwater; neighbour_height[i][3] = groundwater; break; } /*neighbour_height[i][0] = groundwater; neighbour_height[i][1] = groundwater; neighbour_height[i][2] = groundwater; neighbour_height[i][3] = groundwater;*/ } } } bool surface_t::is_plan_height_changeable(sint16 x, sint16 y) const { const planquadrat_t *plan = access(x,y); bool ok = true; if(plan != NULL) { grund_t *gr = plan->get_kartenboden(); ok = (gr->ist_natur() || gr->is_water()) && !gr->hat_wege() && !gr->is_halt(); for( int i=0; ok && iobj_count(); i++ ) { const obj_t *obj = gr->obj_bei(i); assert(obj != NULL); ok = obj->get_typ() == obj_t::baum || obj->get_typ() == obj_t::zeiger || obj->get_typ() == obj_t::cloud || obj->get_typ() == obj_t::groundobj; } } return ok; } int surface_t::grid_raise(const player_t *player, koord k, const char*&err) { int n = 0; if(is_within_grid_limits(k)) { const grund_t *gr = lookup_kartenboden_gridcoords(k); const slope4_t corner_to_raise = get_corner_to_operate(k); const sint16 x = gr->get_pos().x; const sint16 y = gr->get_pos().y; const sint8 hgt = gr->get_hoehe(corner_to_raise); sint8 hsw, hse, hne, hnw; if( !gr->is_water() ) { const sint8 f = ground_desc_t::double_grounds ? 2 : 1; const sint8 o = ground_desc_t::double_grounds ? 1 : 0; hsw = hgt - o + scorner_sw( corner_to_raise ) * f; hse = hgt - o + scorner_se( corner_to_raise ) * f; hne = hgt - o + scorner_ne( corner_to_raise ) * f; hnw = hgt - o + scorner_nw( corner_to_raise ) * f; } else { hsw = hse = hne = hnw = hgt; } terraformer_t digger(terraformer_t::raise, world()); digger.add_node(x, y, hsw, hse, hne, hnw); digger.generate_affected_tile_list(); err = digger.can_raise_all(player); if (err) { return 0; } n = digger.apply(); // force world full redraw, or background could be dirty. world()->set_dirty(); if( max_height < lookup_kartenboden_gridcoords(k)->get_hoehe() ) { max_height = lookup_kartenboden_gridcoords(k)->get_hoehe(); } } return (n+3)>>2; } int surface_t::grid_lower(const player_t *player, koord k, const char*&err) { int n = 0; if(is_within_grid_limits(k)) { const grund_t *gr = lookup_kartenboden_gridcoords(k); const slope4_t corner_to_lower = get_corner_to_operate(k); const sint16 x = gr->get_pos().x; const sint16 y = gr->get_pos().y; const sint8 hgt = gr->get_hoehe(corner_to_lower); const sint8 f = ground_desc_t::double_grounds ? 2 : 1; const sint8 o = ground_desc_t::double_grounds ? 1 : 0; const sint8 hsw = hgt + o - scorner_sw( corner_to_lower ) * f; const sint8 hse = hgt + o - scorner_se( corner_to_lower ) * f; const sint8 hne = hgt + o - scorner_ne( corner_to_lower ) * f; const sint8 hnw = hgt + o - scorner_nw( corner_to_lower ) * f; terraformer_t digger(terraformer_t::lower, world()); digger.add_node(x, y, hsw, hse, hne, hnw); digger.generate_affected_tile_list(); err = digger.can_lower_all(player); if (err) { return 0; } n = digger.apply(); err = NULL; // force world full redraw, or background could be dirty. world()->set_dirty(); if( min_height > min_hgt_nocheck( koord(x,y) ) ) { min_height = min_hgt_nocheck( koord(x,y) ); } } return (n+3)>>2; } // raise height in the hgt-array void surface_t::raise_grid_to(sint16 x, sint16 y, sint8 h) { if(is_within_grid_limits(x,y)) { const sint32 offset = x + y*(cached_grid_size.x+1); if( grid_hgts[offset] < h ) { grid_hgts[offset] = h; const sint8 hh = h - (ground_desc_t::double_grounds ? 2 : 1); // set new height of neighbor grid points raise_grid_to(x-1, y-1, hh); raise_grid_to(x , y-1, hh); raise_grid_to(x+1, y-1, hh); raise_grid_to(x-1, y , hh); raise_grid_to(x+1, y , hh); raise_grid_to(x-1, y+1, hh); raise_grid_to(x , y+1, hh); raise_grid_to(x+1, y+1, hh); } } } void surface_t::lower_grid_to(sint16 x, sint16 y, sint8 h) { if(is_within_grid_limits(x,y)) { const sint32 offset = x + y*(cached_grid_size.x+1); if( grid_hgts[offset] > h ) { grid_hgts[offset] = h; sint8 hh = h + 2; // set new height of neighbor grid points lower_grid_to(x-1, y-1, hh); lower_grid_to(x , y-1, hh); lower_grid_to(x+1, y-1, hh); lower_grid_to(x-1, y , hh); lower_grid_to(x+1, y , hh); lower_grid_to(x-1, y+1, hh); lower_grid_to(x , y+1, hh); lower_grid_to(x+1, y+1, hh); } } } bool surface_t::can_flatten_tile(player_t *player, koord k, sint8 hgt, bool keep_water, bool make_underwater_hill) { return flatten_tile(player, k, hgt, keep_water, make_underwater_hill, true /* just check */); } // make a flat level at this position (only used for AI at the moment) bool surface_t::flatten_tile(player_t *player, koord k, sint8 hgt, bool keep_water, bool make_underwater_hill, bool justcheck) { int n = 0; bool ok = true; const grund_t *gr = lookup_kartenboden(k); const slope_t::type slope = gr->get_grund_hang(); const sint8 old_hgt = make_underwater_hill && gr->is_water() ? min_hgt(k) : gr->get_hoehe(); const sint8 max_hgt = old_hgt + slope_t::max_diff(slope); if( max_hgt > hgt ) { terraformer_t digger(terraformer_t::lower, world()); digger.add_node(k.x, k.y, hgt, hgt, hgt, hgt); digger.generate_affected_tile_list(); ok = digger.can_lower_all(player) == NULL; if (ok && !justcheck) { n += digger.apply(); } } if( ok && old_hgt < hgt ) { terraformer_t digger(terraformer_t::raise, world()); digger.add_node(k.x, k.y, hgt, hgt, hgt, hgt); digger.generate_affected_tile_list(); ok = digger.can_raise_all(player, keep_water) == NULL; if (ok && !justcheck) { n += digger.apply(); } } // was changed => pay for it if(n>0) { n = (n+3) / 4; player_t::book_construction_costs(player, n * world()->get_settings().cst_alter_land, k, ignore_wt); } return ok; } slope_t::type surface_t::calc_natural_slope( const koord k ) const { if(!is_within_grid_limits(k.x, k.y)) { return slope_t::flat; } const sint8 *p = &grid_hgts[k.x + k.y*(sint32)(get_size().x+1)]; const int h1 = *p; const int h2 = *(p+1); const int h3 = *(p+get_size().x+2); const int h4 = *(p+get_size().x+1); const int mini = min(min(h1,h2), min(h3,h4)); const int d1=h1-mini; const int d2=h2-mini; const int d3=h3-mini; const int d4=h4-mini; return encode_corners(d4, d3, d2, d1); } slope_t::type surface_t::recalc_natural_slope( const koord k, sint8 &new_height ) const { grund_t *gr = lookup_kartenboden(k); if(!gr) { return slope_t::flat; } else { const sint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; sint8 corner_height[4]; // get neighbour corner heights sint8 neighbour_height[8][4]; get_neighbour_heights( k, neighbour_height ); //check whether neighbours are foundations bool neighbour_fundament[8]; for( int i = 0; i < 8; i++ ) { grund_t *gr2 = lookup_kartenboden( k + koord::neighbours[i] ); neighbour_fundament[i] = (gr2 && gr2->get_typ() == grund_t::fundament); } for( uint8 i = 0; i < 4; i++ ) { // 0 = sw, 1 = se etc. // corner_sw (i=0): tests vs neighbour 1:w (corner 2 j=1),2:sw (corner 3) and 3:s (corner 4) // corner_se (i=1): tests vs neighbour 3:s (corner 3 j=2),4:se (corner 4) and 5:e (corner 1) // corner_ne (i=2): tests vs neighbour 5:e (corner 4 j=3),6:ne (corner 1) and 7:n (corner 2) // corner_nw (i=3): tests vs neighbour 7:n (corner 1 j=0),0:nw (corner 2) and 1:w (corner 3) sint16 median_height = 0; uint8 natural_corners = 0; for( int j = 1; j < 4; j++ ) { if( !neighbour_fundament[(i * 2 + j) & 7] ) { natural_corners++; median_height += neighbour_height[(i * 2 + j) & 7][(i + j) & 3]; } } switch( natural_corners ) { case 1: { corner_height[i] = (sint8)median_height; break; } case 2: { corner_height[i] = median_height >> 1; break; } default: { // take the average of all 3 corners (if no natural corners just use the artificial ones anyway) corner_height[i] = median( neighbour_height[(i * 2 + 1) & 7][(i + 1) & 3], neighbour_height[(i * 2 + 2) & 7][(i + 2) & 3], neighbour_height[(i * 2 + 3) & 7][(i + 3) & 3] ); break; } } } // new height of that tile ... sint8 min_height = min( min( corner_height[0], corner_height[1] ), min( corner_height[2], corner_height[3] ) ); sint8 max_height = max( max( corner_height[0], corner_height[1] ), max( corner_height[2], corner_height[3] ) ); /* check for an artificial slope on a steep sidewall */ bool not_ok = abs( max_height - min_height ) > max_hdiff || min_height == -128; sint8 old_height = gr->get_hoehe(); new_height = min_height; // now we must make clear, that there is no ground above/below the slope if( old_height!=new_height ) { not_ok |= lookup(koord3d(k,new_height))!=NULL; if( old_height > new_height ) { not_ok |= lookup(koord3d(k,old_height-1))!=NULL; } if( old_height < new_height ) { not_ok |= lookup(koord3d(k,old_height+1))!=NULL; } } if( not_ok ) { /* difference too high or ground above/below * we just keep it as it was ... */ new_height = old_height; return gr->get_grund_hang(); } const sint16 d1 = min( corner_height[0] - new_height, max_hdiff ); const sint16 d2 = min( corner_height[1] - new_height, max_hdiff ); const sint16 d3 = min( corner_height[2] - new_height, max_hdiff ); const sint16 d4 = min( corner_height[3] - new_height, max_hdiff ); return encode_corners(d1, d2, d3, d4); } } void surface_t::init_height_to_climate() { // mark unused as arctic memset( num_climates_at_height, 0, lengthof(num_climates_at_height) ); const settings_t &settings = world()->get_settings(); // now just add them, the later climates will win (we will do a fineer assessment later for( int cl=0; cl settings.get_climate_borders( arctic_climate, 1 ) ) { height_to_climate[ h ] = arctic_climate; } else { height_to_climate[ h ] = temperate_climate; } num_climates_at_height[ h ] = 1; } DBG_DEBUG( "init_height_to_climate()", "Height %i, climate %i, num_climates %i", h - groundwater, height_to_climate[ h ], num_climates_at_height[ h ] ); } } void surface_t::rotate_transitions(koord k) { planquadrat_t *pl = access(k); if( !pl ) { return; } uint8 climate_corners = pl->get_climate_corners(); if( climate_corners != 0 ) { climate_corners = (climate_corners >> 1) | ((climate_corners & 1) << 3); pl->set_climate_corners( climate_corners ); } } void surface_t::recalc_transitions(koord k) { planquadrat_t *pl = access(k); if( !pl ) { return; } grund_t *gr = pl->get_kartenboden(); if( !gr->is_water() ) { // get neighbour corner heights sint8 neighbour_height[8][4]; get_neighbour_heights( k, neighbour_height ); // look up neighbouring climates climate neighbour_climate[8]; for( int i = 0; i < 8; i++ ) { // 0 = nw, 1 = w etc. koord k_neighbour = k + koord::neighbours[i]; if( !is_within_limits(k_neighbour) ) { k_neighbour = get_closest_coordinate(k_neighbour); } neighbour_climate[i] = get_climate( k_neighbour ); } uint8 climate_corners = 0; climate climate0 = get_climate(k); slope_t::type slope_corner = gr->get_grund_hang(); for( uint8 i = 0; i < 4; i++ ) { // 0 = sw, 1 = se etc. // corner_sw (i=0): tests vs neighbour 1:w (corner 2 j=1),2:sw (corner 3) and 3:s (corner 4) // corner_se (i=1): tests vs neighbour 3:s (corner 3 j=2),4:se (corner 4) and 5:e (corner 1) // corner_ne (i=2): tests vs neighbour 5:e (corner 4 j=3),6:ne (corner 1) and 7:n (corner 2) // corner_nw (i=3): tests vs neighbour 7:n (corner 1 j=0),0:nw (corner 2) and 1:w (corner 3) sint8 corner_height = gr->get_hoehe() + corner_sw(slope_corner); climate transition_climate = water_climate; climate min_climate = arctic_climate; for( int j = 1; j < 4; j++ ) { if( corner_height == neighbour_height[(i * 2 + j) & 7][(i + j) & 3]) { climate climatej = neighbour_climate[(i * 2 + j) & 7]; climatej > transition_climate ? transition_climate = climatej : 0; climatej < min_climate ? min_climate = climatej : 0; } } if( min_climate == water_climate || transition_climate > climate0 ) { climate_corners |= 1 << i; } slope_corner /= slope_t::southeast; } pl->set_climate_transition_flag( climate_corners != 0 ); pl->set_climate_corners( climate_corners ); } gr->calc_image(); } void surface_t::calc_climate(koord k, bool recalc) { planquadrat_t *pl = access(k); if( !pl ) { return; } if( (unsigned)k.x >= climate_map.get_width() || (unsigned)k.y >= climate_map.get_height() ) { // not initialised yet (may happend during river creation) return; } grund_t *gr = pl->get_kartenboden(); if( gr ) { if( !gr->is_water() ) { bool beach = false; climate default_cl = (climate)climate_map.at( k.x, k.y ); if( gr->get_pos().z == groundwater ) { for( int i = 0; i < 8 && !beach; i++ ) { grund_t *gr2 = lookup_kartenboden( k + koord::neighbours[i] ); if( gr2 && gr2->is_water() ) { beach = true; } } } if( beach ) { pl->set_climate( desert_climate ); } else if( default_cl>water_climate && default_cl<=arctic_climate && world()->get_settings().get_climate_borders(default_cl,false)<=gr->get_pos().z && world()->get_settings().get_climate_borders(default_cl,true)>gr->get_pos().z ) { // if possible keep (or revert) to original climate pl->set_climate( default_cl ); } else { pl->set_climate( get_climate_at_height( max( gr->get_pos().z, groundwater + 1 ) ) ); } } else { pl->set_climate( water_climate ); } pl->set_climate_transition_flag(false); pl->set_climate_corners(0); } if( recalc ) { recalc_transitions(k); for( int i = 0; i < 8; i++ ) { recalc_transitions( k + koord::neighbours[i] ); } } } simutrans-124.5/src/simutrans/world/surface.h000066400000000000000000000402041520675460500213300ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef WORLD_SURFACE_H #define WORLD_SURFACE_H #include "simplan.h" #include "../dataobj/koord.h" #include "../dataobj/koord3d.h" #include "../tpl/array2d_tpl.h" class grund_t; class player_t; /// Contains all in-game objects like terrain, buildings and vehicles. /// Provides low-level access to the in-game world (e.g. for terraforming or map height lookup) /// Most functions come in two variants: a normal one and a *_nocheck one. /// The *_nocheck variant should be used if the 2d coordinate is already known to be valid. class surface_t { protected: /** * For performance reasons we have the map grid size cached locally, comes from the environment (Einstellungen) * @brief Cached map grid size. * @note Valid grid coords are within (0..x,0..y) */ koord cached_grid_size; /** * For performance reasons we have the map size cached locally, comes from the environment (Einstellungen). * @brief Cached map tile size. * @note Valid tile coords are (0..x,0..y) * @note These values are one less than the size values of the grid. */ koord cached_size; /// @brief The maximum of cached_size.x and cached_size.y. /// Maximum size for waiting bars etc. sint16 cached_size_max; /// the maximum allowed world height. No tile/grid point can be higher than this limit. sint8 max_allowed_height; /// the minimum allowed world height. No tile/grid point can be lower than this limit. sint8 min_allowed_height; // @note min_allowed_height <= min_height <= groundwater < max_height <= max_allowed_height public: /// cache the current maximum and minimum height on the map sint8 max_height, min_height; protected: /// Water level height. sint8 groundwater; /// Current snow line height, changes between winter and summer snow line height. sint8 snowline; /// Current season. /// @note 0=winter, 1=spring, 2=summer, 3=autumn uint8 season; /** * @name Map data structures * These variables represent the simulated map. * @{ */ /** * Array containing all the map tiles. * @see cached_size */ planquadrat_t *plan = NULL; /** * Array representing the height of each point of the grid. * @see cached_grid_size */ sint8 *grid_hgts = NULL; /** * Array representing the height of water on each point of the grid. * @see cached_grid_size */ sint8 *water_hgts = NULL; /** @} */ /// Table for fast conversion from height to climate. uint8 height_to_climate[256] = {}; uint8 num_climates_at_height[256] = {}; /// Contains the intended climate for a tile /// (needed to restore tiles after height changes) array2d_tpl climate_map; /// Contains the intended climate for a tile /// (needed to restore tiles after height changes) array2d_tpl humidity_map; public: surface_t(); ~surface_t(); public: /// Returns the number of tiles of the map in x and y direction. /// @note Valid tile coords are within (0..x-1, 0..y-1) inline koord get_size() const { return cached_grid_size; } /// Maximum size for waiting bars etc. inline sint16 get_size_max() const { return cached_size_max; } /** * @return True if the specified coordinate is inside the world tiles(planquadrat_t) limits, false otherwise. * @param x,y Position * @note Inline because called very frequently! */ inline bool is_within_limits(sint16 x, sint16 y) const { // since negative values will make the whole result negative, we can use bitwise or // This is faster, since pentiums and other long pipeline processors do not like jumps // return x>=0 && y>=0 && cached_size.x>=x && cached_size.y>=y; return (x|y|(cached_size.x-x)|(cached_size.y-y))>=0; } /** * @return True if the specified coordinate is inside the world tiles(planquadrat_t) limits, false otherwise. * @param k Position * @note Inline because called very frequently! */ inline bool is_within_limits(koord k) const { return is_within_limits(k.x, k.y); } /** * @return True if the specified coordinate is inside the world height grid limits, false otherwise. * @param x,y Position * @note Inline because called very frequently! */ inline bool is_within_grid_limits(sint16 x, sint16 y) const { // Since negative values will make the whole result negative, we can use bitwise or // This is faster, since pentiums and other long pipeline processors do not like jumps // return x>=0 && y>=0 && cached_grid_size.x>=x && cached_grid_size.y>=y; return (x|y|(cached_grid_size.x-x)|(cached_grid_size.y-y))>=0; } /** * @return True if the specified coordinate is inside the world height grid limits, false otherwise. * @param k (x,y) coordinate. * @note Inline because called very frequently! */ inline bool is_within_grid_limits(const koord &k) const { return is_within_grid_limits(k.x, k.y); } /** * @return True if the specified coordinate is inside the world height grid limits, false otherwise. * @param x X coordinate. * @param y Y coordinate. * @note Inline because called very frequently! */ inline bool is_within_grid_limits(uint16 x, uint16 y) const { return (x<=(unsigned)cached_grid_size.x && y<=(unsigned)cached_grid_size.y); } /// @returns the closest coordinate to @p outside_pos that is within the world koord get_closest_coordinate(koord outside_pos); public: /** * @return grund an pos/hoehe * @note Inline because called very frequently! */ inline grund_t *lookup(const koord3d &pos) const { const planquadrat_t *plan = access(pos.x, pos.y); return plan ? plan->get_boden_in_hoehe(pos.z) : NULL; } /** * @return grund an pos/hoehe * @note Inline because called very frequently! * For speed no checks are performed that 2d coordinates are valid * (This function may still return NULL if there is no ground at pos.z) */ inline grund_t *lookup_nocheck(const koord3d &pos) const { return access_nocheck(pos.x, pos.y)->get_boden_in_hoehe(pos.z); } /** * This function takes grid coordinates as a parameter and a desired height (koord3d). * Will return the ground_t object that intersects with it in its northern corner if possible. * If that tile doesn't exist, returns the one that intersects with it in another corner. * * @param pos Grid coordinates to check for, the z points to the desired height. * @see lookup_kartenboden_gridcoords * @see corner_to_operate * @note Inline because called very frequently! */ inline grund_t *lookup_gridcoords(const koord3d &pos) const { if ( ( pos.x != cached_grid_size.x ) && ( pos.y != cached_grid_size.y ) ){ return lookup(koord3d(pos.x, pos.y, pos.z)); } if ( ( pos.x == cached_grid_size.x ) && ( pos.y == cached_grid_size.y ) ) { return lookup(koord3d(pos.x-1, pos.y-1, pos.z)); } else if ( pos.x == cached_grid_size.x ) { return lookup(koord3d(pos.x-1, pos.y, pos.z)); } return lookup(koord3d(pos.x, pos.y-1, pos.z)); } /** * This function takes 2D grid coordinates as a parameter, will return the ground_t object that * intersects with it in it's north corner if possible. If that tile doesn't exist, returns * the one that intersects with it in other corner. * @param pos Grid coordinates to check for. * @see corner_to_operate * @see lookup_gridcoords * @return The requested tile, or the immediately adjacent in case of lower border. * @note Inline because called very frequently! */ inline grund_t *lookup_kartenboden_gridcoords(const koord &pos) const { if ( ( pos.x != cached_grid_size.x ) && ( pos.y != cached_grid_size.y ) ){ return lookup_kartenboden(pos); } if ( ( pos.x == cached_grid_size.x ) && ( pos.y == cached_grid_size.y ) ) { return access(koord(pos.x-1, pos.y-1))->get_kartenboden(); } else if ( pos.x == cached_grid_size.x ) { return access(koord(pos.x-1, pos.y))->get_kartenboden(); } return access(koord(pos.x, pos.y-1))->get_kartenboden(); } public: /** * @return grund at the bottom (where house will be build) * @note Inline because called very frequently! - nocheck for more speed */ inline grund_t *lookup_kartenboden_nocheck(const sint16 x, const sint16 y) const { return plan[x+y*cached_grid_size.x].get_kartenboden(); } inline grund_t *lookup_kartenboden_nocheck(const koord &pos) const { return lookup_kartenboden_nocheck(pos.x, pos.y); } /** * @return grund at the bottom (where house will be build) * @note Inline because called very frequently! */ inline grund_t *lookup_kartenboden(const sint16 x, const sint16 y) const { return is_within_limits(x, y) ? plan[x+y*cached_grid_size.x].get_kartenboden() : NULL; } inline grund_t *lookup_kartenboden(const koord &pos) const { return lookup_kartenboden(pos.x, pos.y); } public: inline planquadrat_t *access_nocheck(sint16 x, sint16 y) const { return &plan[x + y*cached_grid_size.x]; } inline planquadrat_t *access_nocheck(koord k) const { return access_nocheck(k.x, k.y); } inline planquadrat_t *access(sint16 x, sint16 y) const { return is_within_limits(x, y) ? &plan[x + y*cached_grid_size.x] : NULL; } inline planquadrat_t *access(koord k) const { return access(k.x, k.y); } public: /** * @return Height at the grid point x,y - versions without checks for speed */ inline sint8 lookup_hgt_nocheck(sint16 x, sint16 y) const { return grid_hgts[x + y*(cached_grid_size.x+1)]; } inline sint8 lookup_hgt_nocheck(koord k) const { return lookup_hgt_nocheck(k.x, k.y); } /** * @return Height at the grid point x,y */ inline sint8 lookup_hgt(sint16 x, sint16 y) const { return is_within_grid_limits(x, y) ? grid_hgts[x + y*(cached_grid_size.x+1)] : groundwater; } inline sint8 lookup_hgt(koord k) const { return lookup_hgt(k.x, k.y); } /** * Sets grid height. * Never set grid_hgts manually, always use this method! */ void set_grid_hgt_nocheck(sint16 x, sint16 y, sint8 hgt) { grid_hgts[x + y*(uint32)(cached_grid_size.x+1)] = hgt; } inline void set_grid_hgt_nocheck(koord k, sint8 hgt) { set_grid_hgt_nocheck(k.x, k.y, hgt); } public: /// @return water height - versions without checks for speed inline sint8 get_water_hgt_nocheck(sint16 x, sint16 y) const { return water_hgts[x + y * (cached_grid_size.x)]; } inline sint8 get_water_hgt_nocheck(koord k) const { return get_water_hgt_nocheck(k.x, k.y); } /// @return water height inline sint8 get_water_hgt(sint16 x, sint16 y) const { return is_within_limits( x, y ) ? water_hgts[x + y * (cached_grid_size.x)] : groundwater; } inline sint8 get_water_hgt(koord k) const { return get_water_hgt(k.x, k.y); } /** * Sets water height. * * @param x,y tile position * @param hgt */ void set_water_hgt_nocheck(sint16 x, sint16 y, sint8 hgt) { water_hgts[x + y * cached_grid_size.x] = hgt; } inline void set_water_hgt_nocheck(koord k, sint8 hgt) { water_hgts[k.x + k.y * cached_grid_size.x] = hgt; } public: /** * Returns the current waterline height. */ sint8 get_groundwater() const { return groundwater; } /// @returns the minimum allowed height on the map. sint8 get_min_allowed_height() const { return min_allowed_height; } /// @returns the maximum allowed world height. sint8 get_max_allowed_height() const { return max_allowed_height; } public: /// @return true, wenn Platz an Stelle @p pos mit Groesse @p dim dim Water ist bool is_water(koord pos, koord dim) const; /// @return true, if square in place (i,j) with size w, h is constructible. bool square_is_free(koord k, sint16 w, sint16 h, int *last_y, climate_bits cl) const; public: /// @returns Minimum height of the planquadrat (tile) at @p k. /// For speed no checks performed that coordinates are valid sint8 min_hgt_nocheck(koord k) const; /// @return Maximum height of the planquadrats (tile) at @p k. /// For speed no checks performed that coordinates are valid sint8 max_hgt_nocheck(koord k) const; /// @return Minimum height of the planquadrat (tile) at @p k. sint8 min_hgt(koord k) const; /// @return Maximum height of the planquadrat (tile) at @p k. sint8 max_hgt(koord k) const; public: void get_height_slope_from_grid(koord k, sint8 &hgt, slope_t::type &slope); /** * Fills array with corner heights of neighbours */ void get_neighbour_heights(const koord k, sint8 neighbour_height[8][4]) const; // // Terraforming related // public: /** * Checks if the whole planquadrat (tile) at coordinates (@p x, @p y) height can * be changed ( for example, water height can't be changed ). */ bool is_plan_height_changeable(sint16 x, sint16 y) const; /** * Increases the height of the grid coordinate (@p x, @p y) by one. * * @param player player * @param pos Grid coordinate. * @param[out] err */ int grid_raise(const player_t *player, koord pos, const char *&err); /** * Decreases the height of the grid coordinate (@p x, @p y) by one. * * @param player player * @param pos Grid coordinate. * @param[out] err */ int grid_lower(const player_t *player, koord pos, const char *&err); /** * Raise grid point (@p x,@p y). Changes @ref grid_hgts only, used during map creation/enlargement. * @see clean_up */ void raise_grid_to(sint16 x, sint16 y, sint8 h); /** * Lower grid point (@p x,@p y). Changes @ref grid_hgts only, used during map creation/enlargement. * @see clean_up */ void lower_grid_to(sint16 x, sint16 y, sint8 h); // mostly used by AI: Ask to flatten a tile bool can_flatten_tile(player_t *player, koord k, sint8 hgt, bool keep_water=false, bool make_underwater_hill=false); bool flatten_tile(player_t *player, koord k, sint8 hgt, bool keep_water=false, bool make_underwater_hill=false, bool justcheck=false); public: /** * Returns the natural slope a a position using the grid. * @note No checking, and only using the grind for calculation. */ slope_t::type calc_natural_slope( const koord k ) const; /** * @return The natural slope at a position. * @note Uses the corner height for the best slope. */ slope_t::type recalc_natural_slope( const koord k, sint8 &new_height ) const; /** * @return The corner that needs to be raised/lowered on the given coordinates. * @param pos Grid coordinate to check. * @note Inline because called very frequently! * @note Will always return north-west except on border tiles. * @pre pos has to be a valid grid coordinate, undefined otherwise. */ inline slope4_t get_corner_to_operate(const koord &pos) const { // Normal tile if ( ( pos.x != cached_grid_size.x ) && ( pos.y != cached_grid_size.y ) ){ return slope4_t::corner_NW; } // Border on south-east if ( is_within_limits(pos.x-1, pos.y) ) { return(slope4_t::corner_NE); } // Border on south-west if ( is_within_limits(pos.x, pos.y-1) ) { return(slope4_t::corner_SW); } // Border on south return (slope4_t::corner_SE); } protected: /** * Initializes the height_to_climate field from settings. */ void init_height_to_climate(); /// Rotates climate and water transitions for a tile void rotate_transitions(koord k); public: /// Recalculate climate and water transitions for a tile void recalc_transitions(koord k); public: /** * Returns the climate for a given height, ruled by world creation settings. * Used to determine climate when terraforming, loading old games, etc. */ climate get_climate_at_height(sint16 height) const { const sint16 h=height-groundwater; if(h<0) { return water_climate; } else if( (uint)h >= lengthof(height_to_climate) ) { return arctic_climate; } return (climate)height_to_climate[h]; } /** * returns the current climate for a given koordinate */ inline climate get_climate(koord k) const { const planquadrat_t *plan = access(k); return plan ? plan->get_climate() : water_climate; } /** * sets the current climate for a given koordinate */ inline void set_climate(koord k, climate cl, bool recalc) { planquadrat_t *plan = access(k); if( plan ) { plan->set_climate(cl); if( recalc ) { recalc_transitions(k); for( int i = 0; i < 8; i++ ) { recalc_transitions( k + koord::neighbours[i] ); } } } } /// Calculates appropriate climate for a tile void calc_climate(koord k, bool recalc); private: /** * Dummy method, to generate compiler error if someone tries to call get_climate( int ), * as the int parameter will silently be cast to koord... */ climate get_climate(sint16) const = delete; }; #endif simutrans-124.5/src/simutrans/world/terraformer.cc000066400000000000000000000527571520675460500224060ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "terraformer.h" #include "../dataobj/scenario.h" #include "../descriptor/ground_desc.h" #include "../tool/simmenu.h" #include "simworld.h" terraformer_t::node_t::node_t() : x(-1), y(-1), changed(0) { } terraformer_t::node_t::node_t(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw, uint8 c) : x(x), y(y), hsw(hsw), hse(hse), hne(hne), hnw(hnw), changed(c) { } bool terraformer_t::node_t::operator==(const terraformer_t::node_t &a) const { return (a.x==x) && (a.y==y); } terraformer_t::terraformer_t(operation_t op, karte_t *welt) : actual_flag(1), ready(false), op(op), welt(welt) { } bool terraformer_t::node_t::comp(const terraformer_t::node_t &a, const terraformer_t::node_t &b) { int diff = a.x - b.x; if (diff == 0) { diff = a.y - b.y; } return diff<0; } void terraformer_t::add_node(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) { if (!welt->is_within_limits(x,y)) { return; } const node_t test(x, y, hsw, hse, hne, hnw, actual_flag^3); node_t *other = list.insert_unique_ordered(test, node_t::comp); const sint8 factor = (op == terraformer_t::raise) ? +1 : -1; if (other) { if (factor*other->hsw < factor*test.hsw) { other->hsw = test.hsw; other->changed |= actual_flag ^ 3; ready = false; } if (factor*other->hse < factor*test.hse) { other->hse = test.hse; other->changed |= actual_flag ^ 3; ready = false; } if (factor*other->hne < factor*test.hne) { other->hne = test.hne; other->changed |= actual_flag ^ 3; ready = false; } if (factor*other->hnw < factor*test.hnw) { other->hnw = test.hnw; other->changed |= actual_flag ^ 3; ready = false; } } else { ready = false; } } void terraformer_t::generate_affected_tile_list() { while( !ready) { actual_flag ^= 3; // flip bits // clear new_flag bit for(node_t& i : list) { i.changed &= actual_flag; } // process nodes with actual_flag set ready = true; for(uint32 j=0; j < list.get_count(); j++) { node_t& i = list[j]; if (i.changed & actual_flag) { i.changed &= ~actual_flag; if (op == terraformer_t::raise) { prepare_raise(i); } else { prepare_lower(i); } } } } } const char *terraformer_t::can_raise_all(const player_t *player, bool keep_water) const { assert(op == terraformer_t::raise); assert(ready); for(node_t const& i : list) { if (const char *err = can_raise_tile_to(i, player, keep_water)) { return err; } } return NULL; } const char *terraformer_t::can_lower_all(const player_t *player) const { assert(op == terraformer_t::lower); assert(ready); for(node_t const& i : list) { if (const char *err = can_lower_tile_to(i, player)) { return err; } } return NULL; } int terraformer_t::apply() { assert(ready); int n = 0; if (op == terraformer_t::raise) { for(node_t const& i : list) { n += raise_to(i); } } else { for(node_t const& i : list) { n += lower_to(i); } } return n; } void terraformer_t::prepare_raise(const node_t node) { assert(welt->is_within_limits(node.x,node.y)); const grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); const sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); const sint8 h0 = gr->get_hoehe(); // old height const sint8 h0_sw = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); const sint8 h0_se = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x+1,node.y+1) ) : h0 + corner_se( gr->get_grund_hang() ); const sint8 h0_ne = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x+1,node.y ) ) : h0 + corner_ne( gr->get_grund_hang() ); const sint8 h0_nw = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x, node.y ) ) : h0 + corner_nw( gr->get_grund_hang() ); // new height const sint8 hn_sw = max(node.hsw, h0_sw); const sint8 hn_se = max(node.hse, h0_se); const sint8 hn_ne = max(node.hne, h0_ne); const sint8 hn_nw = max(node.hnw, h0_nw); // nothing to do? if( !gr->is_water() && h0_sw >= node.hsw && h0_se >= node.hse && h0_ne >= node.hne && h0_nw >= node.hnw ) { return; } // calc new height and slope const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; const bool ok = (hmaxneu - hneu <= max_hdiff); // may fail on water tiles since lookup_hgt might be modified from previous raise_to calls if( !ok && !gr->is_water() ) { assert(false); } // sw if (h0_sw < node.hsw) { add_node( node.x - 1, node.y + 1, node.hsw - max_hdiff, node.hsw - max_hdiff, node.hsw, node.hsw - max_hdiff ); } // s if (h0_sw < node.hsw || h0_se < node.hse) { const sint8 hs = max( node.hse, node.hsw ) - max_hdiff; add_node( node.x, node.y + 1, hs, hs, node.hse, node.hsw ); } // se if (h0_se < node.hse) { add_node( node.x + 1, node.y + 1, node.hse - max_hdiff, node.hse - max_hdiff, node.hse - max_hdiff, node.hse ); } // e if (h0_se < node.hse || h0_ne < node.hne) { const sint8 he = max( node.hse, node.hne ) - max_hdiff; add_node( node.x + 1, node.y, node.hse, he, he, node.hne ); } // ne if (h0_ne < node.hne) { add_node( node.x + 1,node.y - 1, node.hne, node.hne - max_hdiff, node.hne - max_hdiff, node.hne - max_hdiff ); } // n if (h0_nw < node.hnw || h0_ne < node.hne) { const sint8 hn = max( node.hnw, node.hne ) - max_hdiff; add_node( node.x, node.y - 1, node.hnw, node.hne, hn, hn ); } // nw if (h0_nw < node.hnw) { add_node( node.x - 1, node.y - 1, node.hnw - max_hdiff, node.hnw, node.hnw - max_hdiff, node.hnw - max_hdiff ); } // w if (h0_sw < node.hsw || h0_nw < node.hnw) { const sint8 hw = max( node.hnw, node.hsw ) - max_hdiff; add_node( node.x - 1, node.y, hw, node.hsw, node.hnw, hw ); } } void terraformer_t::prepare_lower(const node_t node) { assert(welt->is_within_limits(node.x,node.y)); const grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); const sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); const sint8 h0 = gr->get_hoehe(); // which corners have to be raised? const sint8 h0_sw = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); const sint8 h0_se = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x+1,node.y+1) ) : h0 + corner_se( gr->get_grund_hang() ); const sint8 h0_ne = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_ne( gr->get_grund_hang() ); const sint8 h0_nw = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y ) ) : h0 + corner_nw( gr->get_grund_hang() ); const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; // sw if (h0_sw > node.hsw) { add_node(node.x - 1, node.y + 1, node.hsw + max_hdiff, node.hsw + max_hdiff, node.hsw, node.hsw + max_hdiff); } // s if (h0_se > node.hse || h0_sw > node.hsw) { const sint8 hs = min( node.hse, node.hsw ) + max_hdiff; add_node( node.x, node.y + 1, hs, hs, node.hse, node.hsw); } // se if (h0_se > node.hse) { add_node( node.x + 1, node.y + 1, node.hse + max_hdiff, node.hse + max_hdiff, node.hse + max_hdiff, node.hse); } // e if (h0_se > node.hse || h0_ne > node.hne) { const sint8 he = max( node.hse, node.hne ) + max_hdiff; add_node( node.x + 1, node.y, node.hse, he, he, node.hne); } // ne if (h0_ne > node.hne) { add_node( node.x + 1, node.y - 1, node.hne, node.hne + max_hdiff, node.hne + max_hdiff, node.hne + max_hdiff); } // n if (h0_nw > node.hnw || h0_ne > node.hne) { const sint8 hn = min( node.hnw, node.hne ) + max_hdiff; add_node( node.x, node.y - 1, node.hnw, node.hne, hn, hn); } // nw if (h0_nw > node.hnw) { add_node( node.x - 1, node.y - 1, node.hnw + max_hdiff, node.hnw, node.hnw + max_hdiff, node.hnw + max_hdiff); } // w if (h0_nw > node.hnw || h0_sw > node.hsw) { const sint8 hw = min( node.hnw, node.hsw ) + max_hdiff; add_node( node.x - 1, node.y, hw, node.hsw, node.hnw, hw); } } const char *terraformer_t::can_raise_tile_to(const node_t &node, const player_t *player, bool keep_water) const { assert(welt->is_within_limits(node.x,node.y)); const grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); const sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); const sint8 max_hgt = max(max(node.hsw,node.hse),max(node.hne,node.hnw)); if( gr->is_water() && keep_water && max_hgt > water_hgt ) { return ""; } return can_raise_plan_to(player, node.x, node.y, max_hgt); } // lower plan // new heights for each corner given const char* terraformer_t::can_lower_tile_to(const node_t &node, const player_t *player) const { assert(welt->is_within_limits(node.x,node.y)); const sint8 hneu = min( min( node.hsw, node.hse ), min( node.hne, node.hnw ) ); if( hneu < welt->get_min_allowed_height() ) { return "Maximum tile height difference reached."; } // water heights // check if need to lower water height for higher neighbouring tiles for( sint16 i = 0 ; i < 8 ; i++ ) { const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; if( welt->is_within_limits(neighbour) && welt->get_water_hgt_nocheck(neighbour) > hneu ) { if (!welt->is_plan_height_changeable( neighbour.x, neighbour.y )) { return ""; } } } return can_lower_plan_to(player, node.x, node.y, hneu ); } int terraformer_t::raise_to(const node_t &node) { assert(welt->is_within_limits(node.x,node.y)); int n = 0; grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); const sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); const sint8 h0 = gr->get_hoehe(); // old height const sint8 h0_sw = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); const sint8 h0_se = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x+1, node.y+1) ) : h0 + corner_se( gr->get_grund_hang() ); const sint8 h0_ne = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x+1, node.y ) ) : h0 + corner_ne( gr->get_grund_hang() ); const sint8 h0_nw = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x, node.y ) ) : h0 + corner_nw( gr->get_grund_hang() ); // new height const sint8 hn_sw = max(node.hsw, h0_sw); const sint8 hn_se = max(node.hse, h0_se); const sint8 hn_ne = max(node.hne, h0_ne); const sint8 hn_nw = max(node.hnw, h0_nw); // nothing to do? if( !gr->is_water() && h0_sw >= node.hsw && h0_se >= node.hse && h0_ne >= node.hne && h0_nw >= node.hnw ) { return 0; } // calc new height and slope const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; const sint8 disp_hneu = max( hneu, water_hgt ); const sint8 disp_hn_sw = max( hn_sw, water_hgt ); const sint8 disp_hn_se = max( hn_se, water_hgt ); const sint8 disp_hn_ne = max( hn_ne, water_hgt ); const sint8 disp_hn_nw = max( hn_nw, water_hgt ); const slope_t::type sneu = encode_corners(disp_hn_sw - disp_hneu, disp_hn_se - disp_hneu, disp_hn_ne - disp_hneu, disp_hn_nw - disp_hneu); const bool ok = (hmaxneu - hneu <= max_hdiff); // may fail on water tiles since lookup_hgt might be modified from previous raise_to calls if (!ok && !gr->is_water()) { assert(false); } bool recalc_climate = gr-> is_water() || welt->get_settings().get_climate_generator() == settings_t::HEIGHT_BASED; // change height and slope, for water tiles only if they will become land if( !gr->is_water() || (hmaxneu > water_hgt || (hneu == water_hgt && hmaxneu == water_hgt) ) ) { gr->set_pos( koord3d( node.x, node.y, disp_hneu ) ); gr->set_grund_hang( sneu ); welt->access_nocheck(node.x,node.y)->angehoben(); welt->set_water_hgt_nocheck(node.x, node.y, welt->get_groundwater()-4); } // update north point in grid welt->set_grid_hgt_nocheck(node.x, node.y, hn_nw); if (recalc_climate) { welt->calc_climate(koord(node.x, node.y), true); } if ( node.x+1 == welt->get_size().x ) { // update eastern grid coordinates too if we are in the edge. welt->set_grid_hgt_nocheck(node.x+1, node.y, hn_ne); welt->set_grid_hgt_nocheck(node.x+1, node.y+1, hn_se); } if ( node.y+1 == welt->get_size().y ) { // update southern grid coordinates too if we are in the edge. welt->set_grid_hgt_nocheck(node.x, node.y+1, hn_sw); welt->set_grid_hgt_nocheck(node.x+1, node.y+1, hn_se); } n += hn_sw - h0_sw + hn_se - h0_se + hn_ne - h0_ne + hn_nw - h0_nw; welt->lookup_kartenboden_nocheck(node.x,node.y)->calc_image(); if ( (node.x+2) < welt->get_size().x ) { welt->lookup_kartenboden_nocheck(node.x+1,node.y)->calc_image(); } if ( (node.y+2) < welt->get_size().y ) { welt->lookup_kartenboden_nocheck(node.x,node.y+1)->calc_image(); } return n; } int terraformer_t::lower_to(const node_t &node) { assert(welt->is_within_limits(node.x,node.y)); int n = 0; grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); const sint8 h0 = gr->get_hoehe(); // old height const sint8 h0_sw = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); const sint8 h0_se = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x+1, node.y+1) ) : h0 + corner_se( gr->get_grund_hang() ); const sint8 h0_ne = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x+1, node.y ) ) : h0 + corner_ne( gr->get_grund_hang() ); const sint8 h0_nw = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y ) ) : h0 + corner_nw( gr->get_grund_hang() ); // new height const sint8 hn_sw = min(node.hsw, h0_sw); const sint8 hn_se = min(node.hse, h0_se); const sint8 hn_ne = min(node.hne, h0_ne); const sint8 hn_nw = min(node.hnw, h0_nw); // nothing to do? if( gr->is_water() ) { if( h0_nw <= node.hnw ) { return 0; } } else if( h0_sw <= node.hsw && h0_se <= node.hse && h0_ne <= node.hne && h0_nw <= node.hnw ) { return 0; } // calc new height and slope const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); if( hneu >= water_hgt ) { // calculate water table from surrounding tiles - start off with height on this tile sint8 water_table = water_hgt >= h0 ? water_hgt : welt->get_groundwater() - 4; /* * we test each corner in turn to see whether it is at the base height of the tile. * If it is we then mark the 3 surrounding tiles for that corner for checking. * Surrounding tiles are indicated by bits going anti-clockwise from * (binary) 00000001 for north-west through to (binary) 10000000 for north * as this is the order of directions used by koord::neighbours[] */ uint8 neighbour_flags = 0; if( hn_nw == hneu ) { neighbour_flags |= 0x83; } if( hn_ne == hneu ) { neighbour_flags |= 0xe0; } if( hn_se == hneu ) { neighbour_flags |= 0x38; } if( hn_sw == hneu ) { neighbour_flags |= 0x0e; } for( sint16 i = 0; i < 8 ; i++ ) { const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; // here we look at the bit in neighbour_flags for this direction // we shift it i bits to the right and test the least significant bit if( welt->is_within_limits( neighbour ) && ((neighbour_flags >> i) & 1) ) { grund_t *gr2 = welt->lookup_kartenboden_nocheck( neighbour ); const sint8 water_hgt_neighbour = welt->get_water_hgt_nocheck( neighbour ); if( gr2 && (water_hgt_neighbour >= gr2->get_hoehe()) && water_hgt_neighbour <= hneu ) { water_table = max( water_table, water_hgt_neighbour ); } } } for( sint16 i = 0; i < 8 ; i++ ) { const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; if( welt->is_within_limits( neighbour ) ) { grund_t *gr2 = welt->lookup_kartenboden_nocheck( neighbour ); if( gr2 && gr2->get_hoehe() < water_table ) { i = 8; water_table = welt->get_groundwater() - 4; } } } // only allow water table to be lowered (except for case of sea level) // this prevents severe (errors! if( water_table < welt->get_water_hgt_nocheck(node.x,node.y) ) { water_hgt = water_table; welt->set_water_hgt_nocheck(node.x, node.y, water_table ); } } // calc new height and slope const sint8 disp_hneu = max( hneu, water_hgt ); const sint8 disp_hn_sw = max( hn_sw, water_hgt ); const sint8 disp_hn_se = max( hn_se, water_hgt ); const sint8 disp_hn_ne = max( hn_ne, water_hgt ); const sint8 disp_hn_nw = max( hn_nw, water_hgt ); const slope_t::type sneu = encode_corners(disp_hn_sw - disp_hneu, disp_hn_se - disp_hneu, disp_hn_ne - disp_hneu, disp_hn_nw - disp_hneu); bool recalc_climate = welt->get_settings().get_climate_generator() == settings_t::HEIGHT_BASED; // change height and slope for land tiles only if( !gr->is_water() || (hmaxneu > water_hgt) ) { gr->set_pos( koord3d( node.x, node.y, disp_hneu ) ); gr->set_grund_hang( (slope_t::type)sneu ); welt->access_nocheck(node.x,node.y)->abgesenkt(); } // update north point in grid welt->set_grid_hgt_nocheck(node.x, node.y, hn_nw); if ( node.x+1 == welt->get_size().x ) { // update eastern grid coordinates too if we are in the edge. welt->set_grid_hgt_nocheck(node.x+1, node.y, hn_ne); welt->set_grid_hgt_nocheck(node.x+1, node.y+1, hn_se); } if ( node.y+1 == welt->get_size().y ) { // update southern grid coordinates too if we are in the edge. welt->set_grid_hgt_nocheck(node.x, node.y+1, hn_sw); welt->set_grid_hgt_nocheck(node.x+1, node.y+1, hn_se); } // water heights // lower water height for higher neighbouring tiles // find out how high water is for( sint16 i = 0; i < 8; i++ ) { const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; if( welt->is_within_limits( neighbour ) ) { const sint8 water_hgt_neighbour = welt->get_water_hgt_nocheck( neighbour ); if(water_hgt_neighbour > hneu ) { if( welt->min_hgt_nocheck( neighbour ) < water_hgt_neighbour ) { // convert to flat ground before lowering water level welt->raise_grid_to( neighbour.x, neighbour.y, water_hgt_neighbour ); welt->raise_grid_to( neighbour.x + 1, neighbour.y, water_hgt_neighbour ); welt->raise_grid_to( neighbour.x, neighbour.y + 1, water_hgt_neighbour ); welt->raise_grid_to( neighbour.x + 1, neighbour.y + 1, water_hgt_neighbour ); } welt->set_water_hgt_nocheck( neighbour, hneu ); welt->access_nocheck(neighbour)->correct_water(); recalc_climate = true; } } } if (recalc_climate) { welt->calc_climate( koord( node.x, node.y ), false ); for( sint16 i = 0; i < 8; i++ ) { const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; welt->calc_climate( neighbour, false ); } } // recalc landscape images - need to extend 2 in each direction for( sint16 j = node.y - 2; j <= node.y + 2; j++ ) { for( sint16 i = node.x - 2; i <= node.x + 2; i++ ) { if( welt->is_within_limits( i, j ) /*&& (i != x || j != y)*/ ) { welt->recalc_transitions( koord (i, j ) ); } } } n += h0_sw-hn_sw + h0_se-hn_se + h0_ne-hn_ne + h0_nw-hn_nw; welt->lookup_kartenboden_nocheck(node.x,node.y)->calc_image(); if( (node.x+2) < welt->get_size().x ) { welt->lookup_kartenboden_nocheck(node.x+1,node.y)->calc_image(); } if( (node.y+2) < welt->get_size().y ) { welt->lookup_kartenboden_nocheck(node.x,node.y+1)->calc_image(); } return n; } const char *terraformer_t::can_lower_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const { const planquadrat_t *plan = welt->access(x,y); const settings_t &settings = welt->get_settings(); if( plan==NULL ) { return ""; } if( h < welt->get_groundwater() - 3 ) { return "Watertable reached"; } const sint8 hmax = plan->get_kartenboden()->get_hoehe(); if( (hmax == h || hmax == h - 1) && (plan->get_kartenboden()->get_grund_hang() == 0 || welt->is_plan_height_changeable( x, y )) ) { return NULL; } if( !welt->is_plan_height_changeable(x, y) ) { return ""; } // tunnel slope below? const grund_t *gr = plan->get_boden_in_hoehe( h - 1 ); if( !gr ) { gr = plan->get_boden_in_hoehe( h - 2 ); } if( !gr && settings.get_way_height_clearance()==2 ) { gr = plan->get_boden_in_hoehe( h - 3 ); } if( gr && h < gr->get_pos().z + slope_t::max_diff( gr->get_weg_hang() ) + settings.get_way_height_clearance() ) { return ""; } // tunnel below? while(h < hmax) { if(plan->get_boden_in_hoehe(h)) { return ""; } h ++; } // check allowance by scenario if (welt->get_scenario()->is_scripted()) { return welt->get_scenario()->is_work_allowed_here(player, TOOL_LOWER_LAND|GENERAL_TOOL, ignore_wt, 0, plan->get_kartenboden()->get_pos()); } return NULL; } const char *terraformer_t::can_raise_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const { const planquadrat_t *plan = welt->access(x,y); if( plan == NULL || !welt->is_plan_height_changeable(x, y) ) { return ""; } // irgendwo eine Bruecke im Weg? const sint8 hmin = plan->get_kartenboden()->get_hoehe(); while(h > hmin) { if(plan->get_boden_in_hoehe(h)) { return ""; } h --; } // check allowance by scenario if (welt->get_scenario()->is_scripted()) { return welt->get_scenario()->is_work_allowed_here(player, TOOL_RAISE_LAND|GENERAL_TOOL, ignore_wt, 0, plan->get_kartenboden()->get_pos()); } return NULL; } simutrans-124.5/src/simutrans/world/terraformer.h000066400000000000000000000103731520675460500222340ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #ifndef WORLD_TERRAFORMER_H #define WORLD_TERRAFORMER_H #include "../simtypes.h" #include "../tpl/vector_tpl.h" class karte_t; class player_t; /** * Class to manage terraform operations. * Can be used for raise only or lower only operations, but not mixed. * * Usual order of calls: * 1. add_node() (can be repeated) * 2. generate_affected_tile_list() * 3. can_raise_all() / can_lower_all() * 4. apply() (to actually apply the changes to the terrain; optional) */ class terraformer_t { public: enum operation_t { lower = 0, raise = 1 }; private: /// Structure to save terraforming operations struct node_t { public: node_t(); node_t(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw, uint8 c); public: /// compares position bool operator==(const node_t &a) const; /// compares position static bool comp(const node_t &a, const node_t &b); public: sint16 x; ///< x-coordinate sint16 y; ///< y-coordinate sint8 hsw, hse, hne, hnw; uint8 changed; }; public: terraformer_t(terraformer_t::operation_t op, karte_t *welt); public: /// Add tile to be raised/lowered. void add_node(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); /// Generate list of all tiles that will be affected. void generate_affected_tile_list(); /// Check whether raise operation would succeed const char *can_raise_all(const player_t *player, bool keep_water = false) const; /// Check whether lower operation would succeed const char *can_lower_all(const player_t *player) const; /// Do the raise/lower operations int apply(); private: /// Internal functions to be used with terraformer_t to propagate terrain changes to neighbouring tiles void prepare_raise(const node_t node); void prepare_lower(const node_t node); /** * Checks whether the heights of the corners of the tile at (@p x, @p y) can be raised. * If the desired height of a corner is lower than its current height, this corner is ignored. * * @param node * @param player player who wants to lower * @param keep_water returns false if water tiles would be raised above water * @returns NULL if raise_to operation can be performed, an error message otherwise */ const char *can_raise_tile_to(const node_t &node, const player_t *player, bool keep_water) const; /** * Checks whether the heights of the corners of the tile at (@p x, @p y) can be lowered. * If the desired height of a corner is higher than its current height, this corner is ignored. * * @param node * @param player player who wants to lower * @returns NULL if lower_to operation can be performed, an error message otherwise */ const char *can_lower_tile_to(const node_t &node, const player_t *player) const; /** * Raises heights of the corners of the tile at (@p x, @p y). * New heights for each corner given. * * @pre can_raise_to should be called before this method. * @see can_raise_to * @returns count of full raise operations (4 corners raised one level) * @note Clear tile, reset water/land type, calc minimap pixel. */ int raise_to(const node_t &node); /** * Lowers heights of the corners of the tile at (@p x, @p y). * New heights for each corner given. * @pre can_lower_to should be called before this method. * @see can_lower_to * @returns count of full lower operations (4 corners lowered one level) * @note Clear tile, reset water/land type, calc minimap pixel. */ int lower_to(const node_t &node); /** * Checks if the planquadrat (tile) at coordinate (x,y) * can be lowered at the specified height. */ const char *can_lower_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const; /** * Checks if the planquadrat (tile) at coordinate (x,y) * can be raised at the specified height. */ const char *can_raise_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const; private: vector_tpl list; ///< list of affected tiles uint8 actual_flag; ///< internal flag to iterate through list bool ready; ///< internal flag to signal that the affected tile list is updated operation_t op; ///< Raise or lower? karte_t *welt; }; #endif simutrans-124.5/src/squirrel/000077500000000000000000000000001520675460500162215ustar00rootroot00000000000000simutrans-124.5/src/squirrel/sq_extensions.cc000066400000000000000000000051341520675460500214350ustar00rootroot00000000000000#include "sq_extensions.h" #include "squirrel/sqpcheader.h" // for declarations... #include "squirrel/sqvm.h" // for Raise_Error_vl #include #include "../simutrans/tpl/ptrhashtable_tpl.h" // store data associate to vm's here struct my_vm_info_t { const char* suspend_blocker; /// if not null then no suspendable functions should be called }; static ptrhashtable_tpl vm_info; void register_vm(HSQUIRRELVM v) { vm_info.put(v); } void unregister_vm(HSQUIRRELVM v) { vm_info.remove(v); } void* get_instanceup(HSQUIRRELVM vm, SQInteger index, void* tag, const char* type) { void* ptr = NULL; if(SQ_SUCCEEDED(sq_getinstanceup(vm, index, &ptr, tag))) { return ptr; } else { sq_raise_error(vm, "Expected instance of class %s.", type ? type : ""); return NULL; } } SQRESULT sq_raise_error(HSQUIRRELVM vm, const SQChar *s, ...) { va_list vl; va_start(vl, s); vm->Raise_Error_vl(s, vl); va_end(vl); return SQ_ERROR; } SQRESULT sq_call_restricted(HSQUIRRELVM v, SQInteger params, SQBool retval, SQBool throw_if_no_ops, SQInteger ops) { if(v->_ops_remaining < 4*ops && sq_getvmstate(v)==SQ_VMSTATE_IDLE) { v->_ops_remaining += ops; } bool save_throw_if_no_ops = v->_throw_if_no_ops; v->_throw_if_no_ops = throw_if_no_ops; SQRESULT ret = sq_call(v, params, retval, true /*raise_error*/); v->_throw_if_no_ops = save_throw_if_no_ops; return ret; } SQRESULT sq_resumevm(HSQUIRRELVM v, SQBool retval, SQInteger ops) { if(v->_ops_remaining < 4*ops) { v->_ops_remaining += ops; } bool save_throw_if_no_ops = v->_throw_if_no_ops; v->_throw_if_no_ops = false; SQRESULT ret = sq_wakeupvm(v, false, retval, true /*raise_error*/, false); v->_throw_if_no_ops = save_throw_if_no_ops; return ret; } // see sq_wakeupvm void sq_setwakeupretvalue(HSQUIRRELVM v) { if(!v->_suspended) { return; } SQInteger target = v->_suspended_target; if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target) = v->GetUp(-1); //retval } v->Pop(); // make target invalid v->_suspended_target = -1; } bool sq_canresumevm(HSQUIRRELVM v) { // true if not suspended or not waiting for any return value return (!v->_suspended || v->_suspended_target==-1); } SQRESULT sq_get_ops_total(HSQUIRRELVM v) { sq_pushinteger(v, v->_ops_total); return 1; } SQRESULT sq_get_ops_remaing(HSQUIRRELVM v) { sq_pushinteger(v, v->_ops_remaining); return 1; } void sq_block_suspend(HSQUIRRELVM v, const char* f) { if (my_vm_info_t* i = vm_info.access(v)) { i->suspend_blocker = f; } } const char* sq_get_suspend_blocker(HSQUIRRELVM v) { return vm_info.get(v).suspend_blocker; } simutrans-124.5/src/squirrel/sq_extensions.h000066400000000000000000000036671520675460500213100ustar00rootroot00000000000000#ifndef _SQ_EXTENSIONS_ #define _SQ_EXTENSIONS_ #include "squirrel.h" /** * Extensions to the squirrel engine * for simutrans */ /** * Register vm to store internal information. */ void register_vm(HSQUIRRELVM v); /** * Should be called if vm closes. */ void unregister_vm(HSQUIRRELVM v); /** * Returns instance userpointer of instance, checks type tag. * @param index of instance in stack * @param tag type tag of class * @param type squirrel-side class name - used for error message * @returns userpointer or NULL in case of error (tag does not match) */ void* get_instanceup(HSQUIRRELVM vm, SQInteger index, void* tag, const char* type); /** * Raises error. * @param s is format string analogue to printf-interface * @returns SQ_ERROR (-1) */ SQRESULT sq_raise_error(HSQUIRRELVM vm, const SQChar *s, ...); /** * calls a function with limited number of opcodes. * returns and suspends vm if opcode limit is exceeded */ SQRESULT sq_call_restricted(HSQUIRRELVM v, SQInteger params, SQBool retval, SQBool throw_if_no_ops, SQInteger ops = 1000); /** * if @r is not null, then no suspendable functions should be called in this vm */ void sq_block_suspend(HSQUIRRELVM v, const char* f); /** * @returns the name of the suspend-blocking function */ const char* sq_get_suspend_blocker(HSQUIRRELVM v); /** * resumes suspended vm. * returns and suspends (again) vm if opcode limit is exceeded. */ SQRESULT sq_resumevm(HSQUIRRELVM v, SQBool retval, SQInteger ops = 1000); /** * pops return value from stack and sets it for the suspended script. */ void sq_setwakeupretvalue(HSQUIRRELVM v); /** * checks whether vm can be resumed. in particular if script waits for return value on wakeup. */ bool sq_canresumevm(HSQUIRRELVM v); /// @returns total amount of opcodes executed by vm SQRESULT sq_get_ops_total(HSQUIRRELVM v); /// @returns amount of remaining opcodes until vm will be suspended SQRESULT sq_get_ops_remaing(HSQUIRRELVM v); #endif simutrans-124.5/src/squirrel/sqconfig.h000066400000000000000000000062621520675460500202110ustar00rootroot00000000000000#ifdef _SQ64 #ifdef _MSC_VER typedef __int64 SQInteger; typedef unsigned __int64 SQUnsignedInteger; typedef unsigned __int64 SQHash; /*should be the same size of a pointer*/ #else typedef long long SQInteger; typedef unsigned long long SQUnsignedInteger; typedef unsigned long long SQHash; /*should be the same size of a pointer*/ #endif typedef int SQInt32; typedef unsigned int SQUnsignedInteger32; #else typedef int SQInteger; typedef int SQInt32; /*must be 32 bits(also on 64bits processors)*/ typedef unsigned int SQUnsignedInteger32; /*must be 32 bits(also on 64bits processors)*/ typedef unsigned int SQUnsignedInteger; typedef unsigned int SQHash; /*should be the same size of a pointer*/ #endif #ifdef SQUSEDOUBLE typedef double SQFloat; #else typedef float SQFloat; #endif #if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) #ifdef _MSC_VER typedef __int64 SQRawObjectVal; //must be 64bits #else typedef long long SQRawObjectVal; //must be 64bits #endif #define SQ_OBJECT_RAWINIT() { _unVal.raw = 0; } #else typedef SQUnsignedInteger SQRawObjectVal; //is 32 bits on 32 bits builds and 64 bits otherwise #define SQ_OBJECT_RAWINIT() #endif #ifndef SQ_ALIGNMENT // SQ_ALIGNMENT shall be less than or equal to SQ_MALLOC alignments, and its value shall be power of 2. #if defined(SQUSEDOUBLE) || defined(_SQ64) #define SQ_ALIGNMENT 8 #else #define SQ_ALIGNMENT 4 #endif #endif typedef void* SQUserPointer; typedef SQUnsignedInteger SQBool; typedef SQInteger SQRESULT; #ifdef SQUNICODE #include #include typedef wchar_t SQChar; #define scstrcmp wcscmp #ifdef _WIN32 #define scsprintf _snwprintf #else #define scsprintf swprintf #endif #define scstrlen wcslen #define scstrtod wcstod #ifdef _SQ64 #define scstrtol wcstoll #else #define scstrtol wcstol #endif #define scstrtoul wcstoul #define scvsprintf vswprintf #define scstrstr wcsstr #define scprintf wprintf #ifdef _WIN32 #define WCHAR_SIZE 2 #define WCHAR_SHIFT_MUL 1 #define MAX_CHAR 0xFFFF #else #define WCHAR_SIZE 4 #define WCHAR_SHIFT_MUL 2 #define MAX_CHAR 0xFFFFFFFF #endif #define _SC(a) L##a #define scisspace iswspace #define scisdigit iswdigit #define scisprint iswprint #define scisxdigit iswxdigit #define scisalpha iswalpha #define sciscntrl iswcntrl #define scisalnum iswalnum #define sq_rsl(l) ((l)< #include #include void sqstd_printcallstack(HSQUIRRELVM v) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { SQStackInfos si; SQInteger i; SQFloat f; const SQChar *s; SQInteger level=1; //1 is to skip this function that is level 0 const SQChar *name=0; SQInteger seq=0; pf(v,_SC("\nCALLSTACK\n")); while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si))) { const SQChar *fn=_SC("unknown"); const SQChar *src=_SC("unknown"); if(si.funcname)fn=si.funcname; if(si.source)src=si.source; pf(v,_SC("* FUNCTION [%s()]
* %s"), fn,src); if (si.line > 0) { pf(v,_SC("* line [%d]"),si.line); } pf(v,_SC("
\n")); pf(v,_SC("- - LOCALS\n")); seq=0; while((name = sq_getlocal(v,level,seq))) { seq++; switch(sq_gettype(v,-1)) { case OT_NULL: pf(v,_SC("- - - [%s] NULL\n"),name); break; case OT_INTEGER: sq_getinteger(v,-1,&i); pf(v,_SC("- - - [%s] %d\n"),name,i); break; case OT_FLOAT: sq_getfloat(v,-1,&f); pf(v,_SC("- - - [%s] %.14g\n"),name,f); break; case OT_USERPOINTER: pf(v,_SC("- - - [%s] USERPOINTER\n"),name); break; case OT_STRING: sq_getstring(v,-1,&s); pf(v,_SC("- - - [%s] \"%s\"\n"),name,s); break; case OT_TABLE: pf(v,_SC("- - - [%s] TABLE (%d entries)\n"),name, sq_getsize(v, -1)); break; case OT_ARRAY: pf(v,_SC("- - - [%s] ARRAY (%d entries)\n"),name, sq_getsize(v, -1)); break; case OT_CLOSURE: pf(v,_SC("- - - [%s] CLOSURE\n"),name); break; case OT_NATIVECLOSURE: pf(v,_SC("- - - [%s] NATIVECLOSURE\n"),name); break; case OT_GENERATOR: pf(v,_SC("- - - [%s] GENERATOR\n"),name); break; case OT_USERDATA: pf(v,_SC("- - - [%s] USERDATA\n"),name); break; case OT_THREAD: pf(v,_SC("- - - [%s] THREAD\n"),name); break; case OT_CLASS: pf(v,_SC("- - - [%s] CLASS\n"),name); break; case OT_INSTANCE: { // try to obtain class name sq_getclass(v, -1); sq_pushnull(v); if (SQ_SUCCEEDED(sq_getattributes(v, -2))) { // stack: instance, class, attributes sq_pushstring(v, "classname", -1); if (SQ_SUCCEEDED(sq_get(v, -2))) { const char* cn; if (SQ_SUCCEEDED(sq_getstring(v, -1, &cn))) { pf(v,_SC("- - - [%s] INSTANCE(%s)\n"),name, cn); sq_pop(v, 3); break; } } sq_poptop(v); } sq_poptop(v); pf(v,_SC("- - - [%s] INSTANCE\n"),name); break; } case OT_WEAKREF: pf(v,_SC("- - - [%s] WEAKREF\n"),name); break; case OT_BOOL:{ SQBool bval; sq_getbool(v,-1,&bval); pf(v,_SC("- - - [%s] %s\n"),name,bval == SQTrue ? _SC("true"):_SC("false")); } break; default: assert(0); break; } sq_pop(v,1); } level++; } } } static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { pf(v,_SC("")); const SQChar *sErr = 0; if(sq_gettop(v)>=1) { if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) { pf(v,_SC("\nError: [%s]\n"),sErr); } else{ pf(v,_SC("\nError: [unknown]\n")); } sqstd_printcallstack(v); } pf(v,_SC("")); } return 0; } void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSource,SQInteger line,SQInteger column) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { pf(v,_SC("")); pf(v,_SC("%s line = (%d) column = (%d) : error %s\n"),sSource,line,column,sErr); pf(v,_SC("")); } } void sqstd_seterrorhandlers(HSQUIRRELVM v) { sq_setcompilererrorhandler(v,_sqstd_compiler_error); sq_newclosure(v,_sqstd_aux_printerror,0); sq_seterrorhandler(v); } SQRESULT sqstd_throwerrorf(HSQUIRRELVM v,const SQChar *err,...) { SQInteger n=256; va_list args; begin: va_start(args,err); SQChar *b=sq_getscratchpad(v,n); SQInteger r=scvsprintf(b,n,err,args); va_end(args); if (r>=n) { n=r+1;//required+null goto begin; } else if (r<0) { return sq_throwerror(v,_SC("@failed to generate formatted error message")); } else { return sq_throwerror(v,b); } } simutrans-124.5/src/squirrel/sqstdlib/sqstdblob.cc000066400000000000000000000146721520675460500223640ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include #include "../squirrel.h" #include "../sqstdio.h" #include #include "../sqstdblob.h" #include "sqstdstream.h" #include "sqstdblobimpl.h" #define SQSTD_BLOB_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000002)) //Blob #define SETUP_BLOB(v) \ SQBlob *self = NULL; \ { if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) \ return sq_throwerror(v,_SC("invalid type tag")); } \ if(!self || !self->IsValid()) \ return sq_throwerror(v,_SC("the blob is invalid")); static SQInteger _blob_resize(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger size; sq_getinteger(v,2,&size); if(!self->Resize(size)) return sq_throwerror(v,_SC("resize failed")); return 0; } static void __swap_dword(unsigned int *n) { *n=(unsigned int)(((*n&0xFF000000)>>24) | ((*n&0x00FF0000)>>8) | ((*n&0x0000FF00)<<8) | ((*n&0x000000FF)<<24)); } static void __swap_word(unsigned short *n) { *n=(unsigned short)((*n>>8)&0x00FF)| ((*n<<8)&0xFF00); } static SQInteger _blob_swap4(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger num=(self->Len()-(self->Len()%4))>>2; unsigned int *t=(unsigned int *)self->GetBuf(); for(SQInteger i = 0; i < num; i++) { __swap_dword(&t[i]); } return 0; } static SQInteger _blob_swap2(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger num=(self->Len()-(self->Len()%2))>>1; unsigned short *t = (unsigned short *)self->GetBuf(); for(SQInteger i = 0; i < num; i++) { __swap_word(&t[i]); } return 0; } static SQInteger _blob__set(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger idx,val; sq_getinteger(v,2,&idx); sq_getinteger(v,3,&val); if(idx < 0 || idx >= self->Len()) return sq_throwerror(v,_SC("index out of range")); ((unsigned char *)self->GetBuf())[idx] = (unsigned char) val; sq_push(v,3); return 1; } static SQInteger _blob__get(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger idx; if ((sq_gettype(v, 2) & SQOBJECT_NUMERIC) == 0) { sq_pushnull(v); return sq_throwobject(v); } sq_getinteger(v,2,&idx); if(idx < 0 || idx >= self->Len()) return sq_throwerror(v,_SC("index out of range")); sq_pushinteger(v,((unsigned char *)self->GetBuf())[idx]); return 1; } static SQInteger _blob__nexti(HSQUIRRELVM v) { SETUP_BLOB(v); if(sq_gettype(v,2) == OT_NULL) { sq_pushinteger(v, 0); return 1; } SQInteger idx; if(SQ_SUCCEEDED(sq_getinteger(v, 2, &idx))) { if(idx+1 < self->Len()) { sq_pushinteger(v, idx+1); return 1; } sq_pushnull(v); return 1; } return sq_throwerror(v,_SC("internal error (_nexti) wrong argument type")); } static SQInteger _blob__typeof(HSQUIRRELVM v) { sq_pushstring(v,_SC("blob"),-1); return 1; } static SQInteger _blob_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) { SQBlob *self = (SQBlob*)p; self->~SQBlob(); sq_free(self,sizeof(SQBlob)); return 1; } static SQInteger _blob_constructor(HSQUIRRELVM v) { SQInteger nparam = sq_gettop(v); SQInteger size = 0; if(nparam == 2) { sq_getinteger(v, 2, &size); } if(size < 0) return sq_throwerror(v, _SC("cannot create blob with negative size")); //SQBlob *b = new SQBlob(size); SQBlob *b = new (sq_malloc(sizeof(SQBlob)))SQBlob(size); if(SQ_FAILED(sq_setinstanceup(v,1,b))) { b->~SQBlob(); sq_free(b,sizeof(SQBlob)); return sq_throwerror(v, _SC("cannot create blob")); } sq_setreleasehook(v,1,_blob_releasehook); return 0; } static SQInteger _blob__cloned(HSQUIRRELVM v) { SQBlob *other = NULL; { if(SQ_FAILED(sq_getinstanceup(v,2,(SQUserPointer*)&other,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) return SQ_ERROR; } //SQBlob *thisone = new SQBlob(other->Len()); SQBlob *thisone = new (sq_malloc(sizeof(SQBlob)))SQBlob(other->Len()); memcpy(thisone->GetBuf(),other->GetBuf(),thisone->Len()); if(SQ_FAILED(sq_setinstanceup(v,1,thisone))) { thisone->~SQBlob(); sq_free(thisone,sizeof(SQBlob)); return sq_throwerror(v, _SC("cannot clone blob")); } sq_setreleasehook(v,1,_blob_releasehook); return 0; } #define _DECL_BLOB_FUNC(name,nparams,typecheck) {_SC(#name),_blob_##name,nparams,typecheck} static const SQRegFunction _blob_methods[] = { _DECL_BLOB_FUNC(constructor,-1,_SC("xn")), _DECL_BLOB_FUNC(resize,2,_SC("xn")), _DECL_BLOB_FUNC(swap2,1,_SC("x")), _DECL_BLOB_FUNC(swap4,1,_SC("x")), _DECL_BLOB_FUNC(_set,3,_SC("xnn")), _DECL_BLOB_FUNC(_get,2,_SC("x.")), _DECL_BLOB_FUNC(_typeof,1,_SC("x")), _DECL_BLOB_FUNC(_nexti,2,_SC("x")), _DECL_BLOB_FUNC(_cloned,2,_SC("xx")), {NULL,(SQFUNCTION)0,0,NULL} }; //GLOBAL FUNCTIONS static SQInteger _g_blob_swap2(HSQUIRRELVM v) { SQInteger i; sq_getinteger(v,2,&i); unsigned short s = (unsigned short)i; sq_pushinteger(v, ((s << 8) | ((s >> 8) & 0x00FFu)) & 0xFFFFu); return 1; } static SQInteger _g_blob_swap4(HSQUIRRELVM v) { SQInteger i; sq_getinteger(v,2,&i); unsigned int t4 = (unsigned int)i; __swap_dword(&t4); sq_pushinteger(v,(SQInteger)t4); return 1; } static SQInteger _g_blob_swapfloat(HSQUIRRELVM v) { SQFloat f; sq_getfloat(v,2,&f); __swap_dword((unsigned int *)&f); sq_pushfloat(v,f); return 1; } #define _DECL_GLOBALBLOB_FUNC(name,nparams,typecheck) {_SC(#name),_g_blob_##name,nparams,typecheck} static const SQRegFunction bloblib_funcs[]={ _DECL_GLOBALBLOB_FUNC(swap2,2,_SC(".n")), _DECL_GLOBALBLOB_FUNC(swap4,2,_SC(".n")), _DECL_GLOBALBLOB_FUNC(swapfloat,2,_SC(".n")), {NULL,(SQFUNCTION)0,0,NULL} }; SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr) { SQBlob *blob; if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) return -1; *ptr = blob->GetBuf(); return SQ_OK; } SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx) { SQBlob *blob; if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) return -1; return blob->Len(); } SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size) { SQInteger top = sq_gettop(v); sq_pushregistrytable(v); sq_pushstring(v,_SC("std_blob"),-1); if(SQ_SUCCEEDED(sq_get(v,-2))) { sq_remove(v,-2); //removes the registry sq_push(v,1); // push the this sq_pushinteger(v,size); //size SQBlob *blob = NULL; if(SQ_SUCCEEDED(sq_call(v,2,SQTrue,SQFalse)) && SQ_SUCCEEDED(sq_getinstanceup(v,-1,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) { sq_remove(v,-2); return blob->GetBuf(); } } sq_settop(v,top); return NULL; } SQRESULT sqstd_register_bloblib(HSQUIRRELVM v) { return declare_stream(v,_SC("blob"),(SQUserPointer)SQSTD_BLOB_TYPE_TAG,_SC("std_blob"),_blob_methods,bloblib_funcs); } simutrans-124.5/src/squirrel/sqstdlib/sqstdblobimpl.h000066400000000000000000000043671520675460500231100ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQSTD_BLOBIMPL_H_ #define _SQSTD_BLOBIMPL_H_ struct SQBlob : public SQStream { SQBlob(SQInteger size) { _size = size; _allocated = size; _buf = (unsigned char *)sq_malloc(size); memset(_buf, 0, _size); _ptr = 0; _owns = true; } virtual ~SQBlob() { sq_free(_buf, _allocated); } SQInteger Write(void *buffer, SQInteger size) { if(!CanAdvance(size)) { GrowBufOf(_ptr + size - _size); } memcpy(&_buf[_ptr], buffer, size); _ptr += size; return size; } SQInteger Read(void *buffer,SQInteger size) { SQInteger n = size; if(!CanAdvance(size)) { if((_size - _ptr) > 0) n = _size - _ptr; else return 0; } memcpy(buffer, &_buf[_ptr], n); _ptr += n; return n; } bool Resize(SQInteger n) { if(!_owns) return false; if(n != _allocated) { unsigned char *newbuf = (unsigned char *)sq_malloc(n); memset(newbuf,0,n); if(_size > n) memcpy(newbuf,_buf,n); else memcpy(newbuf,_buf,_size); sq_free(_buf,_allocated); _buf=newbuf; _allocated = n; if(_size > _allocated) _size = _allocated; if(_ptr > _allocated) _ptr = _allocated; } return true; } bool GrowBufOf(SQInteger n) { bool ret = true; if(_size + n > _allocated) { if(_size + n > _size * 2) ret = Resize(_size + n); else ret = Resize(_size * 2); } _size = _size + n; return ret; } bool CanAdvance(SQInteger n) { if(_ptr+n>_size)return false; return true; } SQInteger Seek(SQInteger offset, SQInteger origin) { switch(origin) { case SQ_SEEK_SET: if(offset > _size || offset < 0) return -1; _ptr = offset; break; case SQ_SEEK_CUR: if(_ptr + offset > _size || _ptr + offset < 0) return -1; _ptr += offset; break; case SQ_SEEK_END: if(_size + offset > _size || _size + offset < 0) return -1; _ptr = _size + offset; break; default: return -1; } return 0; } bool IsValid() { return _size == 0 || _buf?true:false; } bool EOS() { return _ptr == _size; } SQInteger Flush() { return 0; } SQInteger Tell() { return _ptr; } SQInteger Len() { return _size; } SQUserPointer GetBuf(){ return _buf; } private: SQInteger _size; SQInteger _allocated; SQInteger _ptr; unsigned char *_buf; bool _owns; }; #endif simutrans-124.5/src/squirrel/sqstdlib/sqstdio.cc000066400000000000000000000272621520675460500220540ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include #include #include "../squirrel.h" #include "../sqstdio.h" #include "sqstdstream.h" #include "../../simutrans/sys/simsys.h" #define SQSTD_FILE_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000001)) //basic API SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode) { #ifndef SQUNICODE return (SQFILE)fopen(filename,mode); #else return (SQFILE)_wfopen(filename,mode); #endif } SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file) { SQInteger ret = (SQInteger)fread(buffer,size,count,(FILE *)file); return ret; } SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file) { return (SQInteger)fwrite(buffer,size,count,(FILE *)file); } SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin) { SQInteger realorigin; switch(origin) { case SQ_SEEK_CUR: realorigin = SEEK_CUR; break; case SQ_SEEK_END: realorigin = SEEK_END; break; case SQ_SEEK_SET: realorigin = SEEK_SET; break; default: return -1; //failed } return fseek((FILE *)file,(long)offset,(int)realorigin); } SQInteger sqstd_ftell(SQFILE file) { return ftell((FILE *)file); } SQInteger sqstd_fflush(SQFILE file) { return fflush((FILE *)file); } SQInteger sqstd_fclose(SQFILE file) { return fclose((FILE *)file); } SQInteger sqstd_feof(SQFILE file) { return feof((FILE *)file); } //File struct SQFile : public SQStream { SQFile() { _handle = NULL; _owns = false;} SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;} virtual ~SQFile() { Close(); } bool Open(const SQChar *filename ,const SQChar *mode) { Close(); if( (_handle = sqstd_fopen(filename,mode)) ) { _owns = true; return true; } return false; } void Close() { if(_handle && _owns) { sqstd_fclose(_handle); _handle = NULL; _owns = false; } } SQInteger Read(void *buffer,SQInteger size) { return sqstd_fread(buffer,1,size,_handle); } SQInteger Write(void *buffer,SQInteger size) { return sqstd_fwrite(buffer,1,size,_handle); } SQInteger Flush() { return sqstd_fflush(_handle); } SQInteger Tell() { return sqstd_ftell(_handle); } SQInteger Len() { SQInteger prevpos=Tell(); Seek(0,SQ_SEEK_END); SQInteger size=Tell(); Seek(prevpos,SQ_SEEK_SET); return size; } SQInteger Seek(SQInteger offset, SQInteger origin) { return sqstd_fseek(_handle,offset,origin); } bool IsValid() { return _handle?true:false; } bool EOS() { return Tell()==Len()?true:false;} SQFILE GetHandle() {return _handle;} private: SQFILE _handle; bool _owns; }; static SQInteger _file__typeof(HSQUIRRELVM v) { sq_pushstring(v,_SC("file"),-1); return 1; } static SQInteger _file_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) { SQFile *self = (SQFile*)p; self->~SQFile(); sq_free(self,sizeof(SQFile)); return 1; } static SQInteger _file_constructor(HSQUIRRELVM v) { const SQChar *filename,*mode; bool owns = true; SQFile *f; SQFILE newf; if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) { sq_getstring(v, 2, &filename); sq_getstring(v, 3, &mode); newf = sqstd_fopen(filename, mode); if(!newf) return sq_throwerror(v, _SC("cannot open file")); } else if(sq_gettype(v,2) == OT_USERPOINTER) { owns = !(sq_gettype(v,3) == OT_NULL); sq_getuserpointer(v,2,&newf); } else { return sq_throwerror(v,_SC("wrong parameter")); } f = new (sq_malloc(sizeof(SQFile)))SQFile(newf,owns); if(SQ_FAILED(sq_setinstanceup(v,1,f))) { f->~SQFile(); sq_free(f,sizeof(SQFile)); return sq_throwerror(v, _SC("cannot create blob with negative size")); } sq_setreleasehook(v,1,_file_releasehook); return 0; } static SQInteger _file_close(HSQUIRRELVM v) { SQFile *self = NULL; if(SQ_SUCCEEDED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_FILE_TYPE_TAG)) && self != NULL) { self->Close(); } return 0; } //bindings #define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck} static const SQRegFunction _file_methods[] = { _DECL_FILE_FUNC(constructor,3,_SC("x")), _DECL_FILE_FUNC(_typeof,1,_SC("x")), _DECL_FILE_FUNC(close,1,_SC("x")), {NULL,(SQFUNCTION)0,0,NULL} }; SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own) { SQInteger top = sq_gettop(v); sq_pushregistrytable(v); sq_pushstring(v,_SC("std_file"),-1); if(SQ_SUCCEEDED(sq_get(v,-2))) { sq_remove(v,-2); //removes the registry sq_pushroottable(v); // push the this sq_pushuserpointer(v,file); //file if(own){ sq_pushinteger(v,1); //true } else{ sq_pushnull(v); //false } if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) { sq_remove(v,-2); return SQ_OK; } } sq_settop(v,top); return SQ_ERROR; } SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file) { SQFile *fileobj = NULL; if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) { *file = fileobj->GetHandle(); return SQ_OK; } return sq_throwerror(v,_SC("not a file")); } #define IO_BUFFER_SIZE 2048 struct IOBuffer { unsigned char buffer[IO_BUFFER_SIZE]; SQInteger size; SQInteger ptr; SQFILE file; }; SQInteger _read_byte(IOBuffer *iobuffer) { if(iobuffer->ptr < iobuffer->size) { SQInteger ret = iobuffer->buffer[iobuffer->ptr]; iobuffer->ptr++; return ret; } else { if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 ) { SQInteger ret = iobuffer->buffer[0]; iobuffer->ptr = 1; return ret; } } return 0; } SQInteger _read_two_bytes(IOBuffer *iobuffer) { if(iobuffer->ptr < iobuffer->size) { if(iobuffer->size < 2) return 0; SQInteger ret = *((const wchar_t*)&iobuffer->buffer[iobuffer->ptr]); iobuffer->ptr += 2; return ret; } else { if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 ) { if(iobuffer->size < 2) return 0; SQInteger ret = *((const wchar_t*)&iobuffer->buffer[0]); iobuffer->ptr = 2; return ret; } } return 0; } static SQInteger _io_file_lexfeed_PLAIN(SQUserPointer iobuf) { IOBuffer *iobuffer = (IOBuffer *)iobuf; return _read_byte(iobuffer); } #ifdef SQUNICODE static SQInteger _io_file_lexfeed_UTF8(SQUserPointer iobuf) { IOBuffer *iobuffer = (IOBuffer *)iobuf; #define READ(iobuf) \ if((inchar = (unsigned char)_read_byte(iobuf)) == 0) \ return 0; static const SQInteger utf8_lengths[16] = { 1,1,1,1,1,1,1,1, /* 0000 to 0111 : 1 byte (plain ASCII) */ 0,0,0,0, /* 1000 to 1011 : not valid */ 2,2, /* 1100, 1101 : 2 bytes */ 3, /* 1110 : 3 bytes */ 4 /* 1111 :4 bytes */ }; static const unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07}; unsigned char inchar; SQInteger c = 0; READ(iobuffer); c = inchar; // if(c >= 0x80) { SQInteger tmp; SQInteger codelen = utf8_lengths[c>>4]; if(codelen == 0) return 0; //"invalid UTF-8 stream"; tmp = c&byte_masks[codelen]; for(SQInteger n = 0; n < codelen-1; n++) { tmp<<=6; READ(iobuffer); tmp |= inchar & 0x3F; } c = tmp; } return c; } #endif static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer iobuf) { SQInteger ret; IOBuffer *iobuffer = (IOBuffer *)iobuf; if( (ret = _read_two_bytes(iobuffer)) > 0 ) return ret; return 0; } static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer iobuf) { SQInteger c; IOBuffer *iobuffer = (IOBuffer *)iobuf; if( (c = _read_two_bytes(iobuffer)) > 0 ) { c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00); return c; } return 0; } SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size) { SQInteger ret; if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret; return -1; } SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size) { return sqstd_fwrite(p,1,size,(SQFILE)file); } SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror) { SQFILE file = sqstd_fopen(filename,_SC("rb")); SQInteger ret; unsigned short us; unsigned char uc; SQLEXREADFUNC func = _io_file_lexfeed_PLAIN; if(file){ ret = sqstd_fread(&us,1,2,file); if(ret != 2) { //probably an empty file us = 0; } if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE sqstd_fseek(file,0,SQ_SEEK_SET); if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) { sqstd_fclose(file); return SQ_OK; } } else { //SCRIPT switch(us) { //gotta swap the next 2 lines on BIG endian machines case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian; case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian; case 0xBBEF: if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) { sqstd_fclose(file); return sq_throwerror(v,_SC("io error")); } if(uc != 0xBF) { sqstd_fclose(file); return sq_throwerror(v,_SC("Unrecognized encoding")); } #ifdef SQUNICODE func = _io_file_lexfeed_UTF8; #else func = _io_file_lexfeed_PLAIN; #endif break;//UTF-8 ; default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii } IOBuffer buffer; buffer.ptr = 0; buffer.size = 0; buffer.file = file; if(SQ_SUCCEEDED(sq_compile(v,func,&buffer,filename,printerror))){ sqstd_fclose(file); return SQ_OK; } } sqstd_fclose(file); return SQ_ERROR; } return sq_throwerror(v,_SC("cannot open the file")); } SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror) { //at least one entry must exist in order for us to push it as the environment if(sq_gettop(v) == 0) return sq_throwerror(v,_SC("environment table expected")); if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) { sq_push(v,-2); if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) { sq_remove(v,retval?-2:-1); //removes the closure return 1; } sq_pop(v,1); //removes the closure } return SQ_ERROR; } SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename) { SQFILE file = sqstd_fopen(filename,_SC("wb+")); if(!file) return sq_throwerror(v,_SC("cannot open the file")); if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) { sqstd_fclose(file); return SQ_OK; } sqstd_fclose(file); return SQ_ERROR; //forward the error } SQInteger _g_io_loadfile(HSQUIRRELVM v) { const SQChar *filename; SQBool printerror = SQFalse; sq_getstring(v,2,&filename); if(sq_gettop(v) >= 3) { sq_getbool(v,3,&printerror); } if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) return 1; return SQ_ERROR; //propagates the error } SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v) { const SQChar *filename; sq_getstring(v,2,&filename); if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename))) return 1; return SQ_ERROR; //propagates the error } SQInteger _g_io_dofile(HSQUIRRELVM v) { const SQChar *filename; SQBool printerror = SQFalse; sq_getstring(v,2,&filename); if(sq_gettop(v) >= 3) { sq_getbool(v,3,&printerror); } sq_push(v,1); //repush the this if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror))) return 1; return SQ_ERROR; //propagates the error } #define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck} static const SQRegFunction iolib_funcs[]={ _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")), _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")), _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")), {NULL,(SQFUNCTION)0,0,NULL} }; SQRESULT sqstd_register_iolib(HSQUIRRELVM v) { SQInteger top = sq_gettop(v); //create delegate declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs); sq_pushstring(v,_SC("stdout"),-1); sqstd_createfile(v,stdout,SQFalse); sq_newslot(v,-3,SQFalse); sq_pushstring(v,_SC("stdin"),-1); sqstd_createfile(v,stdin,SQFalse); sq_newslot(v,-3,SQFalse); sq_pushstring(v,_SC("stderr"),-1); sqstd_createfile(v,stderr,SQFalse); sq_newslot(v,-3,SQFalse); sq_settop(v,top); return SQ_OK; } simutrans-124.5/src/squirrel/sqstdlib/sqstdmath.cc000066400000000000000000000050641520675460500223720ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "../squirrel.h" #include #include #include "../sqstdmath.h" #define SINGLE_ARG_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \ SQFloat f; \ sq_getfloat(v,2,&f); \ sq_pushfloat(v,(SQFloat)_funcname(f)); \ return 1; \ } #define TWO_ARGS_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \ SQFloat p1,p2; \ sq_getfloat(v,2,&p1); \ sq_getfloat(v,3,&p2); \ sq_pushfloat(v,(SQFloat)_funcname(p1,p2)); \ return 1; \ } static SQInteger math_srand(HSQUIRRELVM v) { SQInteger i; if(SQ_FAILED(sq_getinteger(v,2,&i))) return sq_throwerror(v,_SC("invalid param")); srand((unsigned int)i); return 0; } static SQInteger math_rand(HSQUIRRELVM v) { sq_pushinteger(v,rand()); return 1; } static SQInteger math_abs(HSQUIRRELVM v) { SQInteger n; sq_getinteger(v,2,&n); sq_pushinteger(v,(SQInteger)abs((int)n)); return 1; } SINGLE_ARG_FUNC(sqrt) SINGLE_ARG_FUNC(fabs) SINGLE_ARG_FUNC(sin) SINGLE_ARG_FUNC(cos) SINGLE_ARG_FUNC(asin) SINGLE_ARG_FUNC(acos) SINGLE_ARG_FUNC(log) SINGLE_ARG_FUNC(log10) SINGLE_ARG_FUNC(tan) SINGLE_ARG_FUNC(atan) TWO_ARGS_FUNC(atan2) TWO_ARGS_FUNC(pow) SINGLE_ARG_FUNC(floor) SINGLE_ARG_FUNC(ceil) SINGLE_ARG_FUNC(exp) #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),math_##name,nparams,tycheck} static const SQRegFunction mathlib_funcs[] = { _DECL_FUNC(sqrt,2,_SC(".n")), _DECL_FUNC(sin,2,_SC(".n")), _DECL_FUNC(cos,2,_SC(".n")), _DECL_FUNC(asin,2,_SC(".n")), _DECL_FUNC(acos,2,_SC(".n")), _DECL_FUNC(log,2,_SC(".n")), _DECL_FUNC(log10,2,_SC(".n")), _DECL_FUNC(tan,2,_SC(".n")), _DECL_FUNC(atan,2,_SC(".n")), _DECL_FUNC(atan2,3,_SC(".nn")), _DECL_FUNC(pow,3,_SC(".nn")), _DECL_FUNC(floor,2,_SC(".n")), _DECL_FUNC(ceil,2,_SC(".n")), _DECL_FUNC(exp,2,_SC(".n")), _DECL_FUNC(srand,2,_SC(".n")), _DECL_FUNC(rand,1,NULL), _DECL_FUNC(fabs,2,_SC(".n")), _DECL_FUNC(abs,2,_SC(".n")), {NULL,(SQFUNCTION)0,0,NULL} }; #undef _DECL_FUNC #ifndef M_PI #define M_PI (3.14159265358979323846) #endif SQRESULT sqstd_register_mathlib(HSQUIRRELVM v) { SQInteger i=0; while(mathlib_funcs[i].name!=0) { sq_pushstring(v,mathlib_funcs[i].name,-1); sq_newclosure(v,mathlib_funcs[i].f,0); sq_setparamscheck(v,mathlib_funcs[i].nparamscheck,mathlib_funcs[i].typemask); sq_setnativeclosurename(v,-1,mathlib_funcs[i].name); sq_newslot(v,-3,SQFalse); i++; } sq_pushstring(v,_SC("RAND_MAX"),-1); sq_pushinteger(v,RAND_MAX); sq_newslot(v,-3,SQFalse); sq_pushstring(v,_SC("PI"),-1); sq_pushfloat(v,(SQFloat)M_PI); sq_newslot(v,-3,SQFalse); return SQ_OK; } simutrans-124.5/src/squirrel/sqstdlib/sqstdrex.cc000066400000000000000000000435731520675460500222460ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "../squirrel.h" #include #include #include #include "../sqstdstring.h" #ifdef _DEBUG #include static const SQChar *g_nnames[] = { _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"), _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"), _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"), _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB"),_SC("OP_MB") }; #endif #define OP_GREEDY (MAX_CHAR+1) // * + ? {n} #define OP_OR (MAX_CHAR+2) #define OP_EXPR (MAX_CHAR+3) //parentesis () #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:) #define OP_DOT (MAX_CHAR+5) #define OP_CLASS (MAX_CHAR+6) #define OP_CCLASS (MAX_CHAR+7) #define OP_NCLASS (MAX_CHAR+8) //negates class the [^ #define OP_RANGE (MAX_CHAR+9) #define OP_CHAR (MAX_CHAR+10) #define OP_EOL (MAX_CHAR+11) #define OP_BOL (MAX_CHAR+12) #define OP_WB (MAX_CHAR+13) #define OP_MB (MAX_CHAR+14) //match balanced #define SQREX_SYMBOL_ANY_CHAR ('.') #define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+') #define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*') #define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?') #define SQREX_SYMBOL_BRANCH ('|') #define SQREX_SYMBOL_END_OF_STRING ('$') #define SQREX_SYMBOL_BEGINNING_OF_STRING ('^') #define SQREX_SYMBOL_ESCAPE_CHAR ('\\') typedef int SQRexNodeType; typedef struct tagSQRexNode{ SQRexNodeType type; SQInteger left; SQInteger right; SQInteger next; }SQRexNode; struct SQRex{ const SQChar *_eol; const SQChar *_bol; const SQChar *_p; SQInteger _first; SQInteger _op; SQRexNode *_nodes; SQInteger _nallocated; SQInteger _nsize; SQInteger _nsubexpr; SQRexMatch *_matches; SQInteger _currsubexp; void *_jmpbuf; const SQChar **_error; }; static SQInteger sqstd_rex_list(SQRex *exp); static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type) { SQRexNode n; n.type = type; n.next = n.right = n.left = -1; if(type == OP_EXPR) n.right = exp->_nsubexpr++; if(exp->_nallocated < (exp->_nsize + 1)) { SQInteger oldsize = exp->_nallocated; exp->_nallocated *= 2; exp->_nodes = (SQRexNode *)sq_realloc(exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode)); } exp->_nodes[exp->_nsize++] = n; SQInteger newid = exp->_nsize - 1; return (SQInteger)newid; } static void sqstd_rex_error(SQRex *exp,const SQChar *error) { if(exp->_error) *exp->_error = error; longjmp(*((jmp_buf*)exp->_jmpbuf),-1); } static void sqstd_rex_expect(SQRex *exp, SQInteger n){ if((*exp->_p) != n) sqstd_rex_error(exp, _SC("expected paren")); exp->_p++; } static SQChar sqstd_rex_escapechar(SQRex *exp) { if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){ exp->_p++; switch(*exp->_p) { case 'v': exp->_p++; return '\v'; case 'n': exp->_p++; return '\n'; case 't': exp->_p++; return '\t'; case 'r': exp->_p++; return '\r'; case 'f': exp->_p++; return '\f'; default: return (*exp->_p++); } } else if(!scisprint(*exp->_p)) sqstd_rex_error(exp,_SC("letter expected")); return (*exp->_p++); } static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid) { SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS); exp->_nodes[n].left = classid; return n; } static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass) { SQChar t; if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) { exp->_p++; switch(*exp->_p) { case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n'); case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t'); case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r'); case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f'); case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v'); case 'a': case 'A': case 'w': case 'W': case 's': case 'S': case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': case 'p': case 'P': case 'l': case 'u': { t = *exp->_p; exp->_p++; return sqstd_rex_charclass(exp,t); } case 'm': { SQChar cb, ce; //cb = character begin match ce = character end match cb = *++exp->_p; //skip 'm' ce = *++exp->_p; exp->_p++; //points to the next char to be parsed if ((!cb) || (!ce)) sqstd_rex_error(exp,_SC("balanced chars expected")); if ( cb == ce ) sqstd_rex_error(exp,_SC("open/close char can't be the same")); SQInteger node = sqstd_rex_newnode(exp,OP_MB); exp->_nodes[node].left = cb; exp->_nodes[node].right = ce; return node; } case 0: sqstd_rex_error(exp,_SC("letter expected for argument of escape sequence")); break; case 'b': case 'B': if(!isclass) { SQInteger node = sqstd_rex_newnode(exp,OP_WB); exp->_nodes[node].left = *exp->_p; exp->_p++; return node; } //else default default: t = *exp->_p; exp->_p++; return sqstd_rex_newnode(exp,t); } } else if(!scisprint(*exp->_p)) { sqstd_rex_error(exp,_SC("letter expected")); } t = *exp->_p; exp->_p++; return sqstd_rex_newnode(exp,t); } static SQInteger sqstd_rex_class(SQRex *exp) { SQInteger ret = -1; SQInteger first = -1,chain; if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){ ret = sqstd_rex_newnode(exp,OP_NCLASS); exp->_p++; }else ret = sqstd_rex_newnode(exp,OP_CLASS); if(*exp->_p == ']') sqstd_rex_error(exp,_SC("empty class")); chain = ret; while(*exp->_p != ']' && exp->_p != exp->_eol) { if(*exp->_p == '-' && first != -1){ SQInteger r; if(*exp->_p++ == ']') sqstd_rex_error(exp,_SC("unfinished range")); r = sqstd_rex_newnode(exp,OP_RANGE); if(exp->_nodes[first].type>*exp->_p) sqstd_rex_error(exp,_SC("invalid range")); if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,_SC("cannot use character classes in ranges")); exp->_nodes[r].left = exp->_nodes[first].type; SQInteger t = sqstd_rex_escapechar(exp); exp->_nodes[r].right = t; exp->_nodes[chain].next = r; chain = r; first = -1; } else{ if(first!=-1){ SQInteger c = first; exp->_nodes[chain].next = c; chain = c; first = sqstd_rex_charnode(exp,SQTrue); } else{ first = sqstd_rex_charnode(exp,SQTrue); } } } if(first!=-1){ SQInteger c = first; exp->_nodes[chain].next = c; } /* hack? */ exp->_nodes[ret].left = exp->_nodes[ret].next; exp->_nodes[ret].next = -1; return ret; } static SQInteger sqstd_rex_parsenumber(SQRex *exp) { SQInteger ret = *exp->_p-'0'; SQInteger positions = 10; exp->_p++; while(isdigit(*exp->_p)) { ret = ret*10+(*exp->_p++-'0'); if(positions==1000000000) sqstd_rex_error(exp,_SC("overflow in numeric constant")); positions *= 10; }; return ret; } static SQInteger sqstd_rex_element(SQRex *exp) { SQInteger ret = -1; switch(*exp->_p) { case '(': { SQInteger expr; exp->_p++; if(*exp->_p =='?') { exp->_p++; sqstd_rex_expect(exp,':'); expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR); } else expr = sqstd_rex_newnode(exp,OP_EXPR); SQInteger newn = sqstd_rex_list(exp); exp->_nodes[expr].left = newn; ret = expr; sqstd_rex_expect(exp,')'); } break; case '[': exp->_p++; ret = sqstd_rex_class(exp); sqstd_rex_expect(exp,']'); break; case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break; case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break; default: ret = sqstd_rex_charnode(exp,SQFalse); break; } SQBool isgreedy = SQFalse; unsigned short p0 = 0, p1 = 0; switch(*exp->_p){ case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break; case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break; case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break; case '{': exp->_p++; if(!isdigit(*exp->_p)) sqstd_rex_error(exp,_SC("number expected")); p0 = (unsigned short)sqstd_rex_parsenumber(exp); /*******************************/ switch(*exp->_p) { case '}': p1 = p0; exp->_p++; break; case ',': exp->_p++; p1 = 0xFFFF; if(isdigit(*exp->_p)){ p1 = (unsigned short)sqstd_rex_parsenumber(exp); } sqstd_rex_expect(exp,'}'); break; default: sqstd_rex_error(exp,_SC(", or } expected")); } /*******************************/ isgreedy = SQTrue; break; } if(isgreedy) { SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY); exp->_nodes[nnode].left = ret; exp->_nodes[nnode].right = ((p0)<<16)|p1; ret = nnode; } if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) { SQInteger nnode = sqstd_rex_element(exp); exp->_nodes[ret].next = nnode; } return ret; } static SQInteger sqstd_rex_list(SQRex *exp) { SQInteger ret=-1,e; if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) { exp->_p++; ret = sqstd_rex_newnode(exp,OP_BOL); } e = sqstd_rex_element(exp); if(ret != -1) { exp->_nodes[ret].next = e; } else ret = e; if(*exp->_p == SQREX_SYMBOL_BRANCH) { SQInteger temp,tright; exp->_p++; temp = sqstd_rex_newnode(exp,OP_OR); exp->_nodes[temp].left = ret; tright = sqstd_rex_list(exp); exp->_nodes[temp].right = tright; ret = temp; } return ret; } static SQBool sqstd_rex_matchcclass(SQInteger cclass,SQChar c) { switch(cclass) { case 'a': return isalpha(c)?SQTrue:SQFalse; case 'A': return !isalpha(c)?SQTrue:SQFalse; case 'w': return (isalnum(c) || c == '_')?SQTrue:SQFalse; case 'W': return (!isalnum(c) && c != '_')?SQTrue:SQFalse; case 's': return isspace(c)?SQTrue:SQFalse; case 'S': return !isspace(c)?SQTrue:SQFalse; case 'd': return isdigit(c)?SQTrue:SQFalse; case 'D': return !isdigit(c)?SQTrue:SQFalse; case 'x': return isxdigit(c)?SQTrue:SQFalse; case 'X': return !isxdigit(c)?SQTrue:SQFalse; case 'c': return iscntrl(c)?SQTrue:SQFalse; case 'C': return !iscntrl(c)?SQTrue:SQFalse; case 'p': return ispunct(c)?SQTrue:SQFalse; case 'P': return !ispunct(c)?SQTrue:SQFalse; case 'l': return islower(c)?SQTrue:SQFalse; case 'u': return isupper(c)?SQTrue:SQFalse; } return SQFalse; /*cannot happen*/ } static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,SQChar c) { do { switch(node->type) { case OP_RANGE: if(c >= node->left && c <= node->right) return SQTrue; break; case OP_CCLASS: if(sqstd_rex_matchcclass(node->left,c)) return SQTrue; break; default: if(c == node->type)return SQTrue; } } while((node->next != -1) && (node = &exp->_nodes[node->next])); return SQFalse; } static const SQChar *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const SQChar *str,SQRexNode *next) { SQRexNodeType type = node->type; switch(type) { case OP_GREEDY: { //SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL; SQRexNode *greedystop = NULL; SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0; const SQChar *s=str, *good = str; if(node->next != -1) { greedystop = &exp->_nodes[node->next]; } else { greedystop = next; } while((nmaches == 0xFFFF || nmaches < p1)) { const SQChar *stop; if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop))) break; nmaches++; good=s; if(greedystop) { //checks that 0 matches satisfy the expression(if so skips) //if not would always stop(for instance if is a '?') if(greedystop->type != OP_GREEDY || (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0)) { SQRexNode *gnext = NULL; if(greedystop->next != -1) { gnext = &exp->_nodes[greedystop->next]; }else if(next && next->next != -1){ gnext = &exp->_nodes[next->next]; } stop = sqstd_rex_matchnode(exp,greedystop,s,gnext); if(stop) { //if satisfied stop it if(p0 == p1 && p0 == nmaches) break; else if(nmaches >= p0 && p1 == 0xFFFF) break; else if(nmaches >= p0 && nmaches <= p1) break; } } } if(s >= exp->_eol) break; } if(p0 == p1 && p0 == nmaches) return good; else if(nmaches >= p0 && p1 == 0xFFFF) return good; else if(nmaches >= p0 && nmaches <= p1) return good; return NULL; } case OP_OR: { const SQChar *asd = str; SQRexNode *temp=&exp->_nodes[node->left]; while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) { if(temp->next != -1) temp = &exp->_nodes[temp->next]; else return asd; } asd = str; temp = &exp->_nodes[node->right]; while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) { if(temp->next != -1) temp = &exp->_nodes[temp->next]; else return asd; } return NULL; break; } case OP_EXPR: case OP_NOCAPEXPR:{ SQRexNode *n = &exp->_nodes[node->left]; const SQChar *cur = str; SQInteger capture = -1; if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) { capture = exp->_currsubexp; exp->_matches[capture].begin = cur; exp->_currsubexp++; } SQInteger tempcap = exp->_currsubexp; do { SQRexNode *subnext = NULL; if(n->next != -1) { subnext = &exp->_nodes[n->next]; }else { subnext = next; } if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) { if(capture != -1){ exp->_matches[capture].begin = 0; exp->_matches[capture].len = 0; } return NULL; } } while((n->next != -1) && (n = &exp->_nodes[n->next])); exp->_currsubexp = tempcap; if(capture != -1) exp->_matches[capture].len = cur - exp->_matches[capture].begin; return cur; } case OP_WB: if((str == exp->_bol && !isspace(*str)) || (str == exp->_eol && !isspace(*(str-1))) || (!isspace(*str) && isspace(*(str+1))) || (isspace(*str) && !isspace(*(str+1))) ) { return (node->left == 'b')?str:NULL; } return (node->left == 'b')?NULL:str; case OP_BOL: if(str == exp->_bol) return str; return NULL; case OP_EOL: if(str == exp->_eol) return str; return NULL; case OP_DOT:{ if (str == exp->_eol) return NULL; str++; } return str; case OP_NCLASS: case OP_CLASS: if (str == exp->_eol) return NULL; if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) { str++; return str; } return NULL; case OP_CCLASS: if (str == exp->_eol) return NULL; if(sqstd_rex_matchcclass(node->left,*str)) { str++; return str; } return NULL; case OP_MB: { SQInteger cb = node->left; //char that opens a balanced expression if(*str != cb) return NULL; // string doesnt start with open char SQInteger ce = node->right; //char that closes a balanced expression SQInteger cont = 1; const SQChar *streol = exp->_eol; while (++str < streol) { if (*str == ce) { if (--cont == 0) { return ++str; } } else if (*str == cb) cont++; } } return NULL; // string ends out of balance default: /* char */ if (str == exp->_eol) return NULL; if(*str != node->type) return NULL; str++; return str; } return NULL; } /* public api */ SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error) { SQRex * volatile exp = (SQRex *)sq_malloc(sizeof(SQRex)); // "volatile" is needed for setjmp() exp->_eol = exp->_bol = NULL; exp->_p = pattern; exp->_nallocated = (SQInteger)scstrlen(pattern) * sizeof(SQChar); exp->_nodes = (SQRexNode *)sq_malloc(exp->_nallocated * sizeof(SQRexNode)); exp->_nsize = 0; exp->_matches = 0; exp->_nsubexpr = 0; exp->_first = sqstd_rex_newnode(exp,OP_EXPR); exp->_error = error; exp->_jmpbuf = sq_malloc(sizeof(jmp_buf)); if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) { SQInteger res = sqstd_rex_list(exp); exp->_nodes[exp->_first].left = res; if(*exp->_p!='\0') sqstd_rex_error(exp,_SC("unexpected character")); #ifdef _DEBUG { SQInteger nsize,i; SQRexNode *t; nsize = exp->_nsize; t = &exp->_nodes[0]; scprintf(_SC("\n")); for(i = 0;i < nsize; i++) { if(exp->_nodes[i].type>MAX_CHAR) scprintf(_SC("[%02d] %10s "), (SQInt32)i,g_nnames[exp->_nodes[i].type-MAX_CHAR]); else scprintf(_SC("[%02d] %10c "), (SQInt32)i,exp->_nodes[i].type); scprintf(_SC("left %02d right %02d next %02d\n"), (SQInt32)exp->_nodes[i].left, (SQInt32)exp->_nodes[i].right, (SQInt32)exp->_nodes[i].next); } scprintf(_SC("\n")); } #endif exp->_matches = (SQRexMatch *) sq_malloc(exp->_nsubexpr * sizeof(SQRexMatch)); memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch)); } else{ sqstd_rex_free(exp); return NULL; } return exp; } void sqstd_rex_free(SQRex *exp) { if(exp) { if(exp->_nodes) sq_free(exp->_nodes,exp->_nallocated * sizeof(SQRexNode)); if(exp->_jmpbuf) sq_free(exp->_jmpbuf,sizeof(jmp_buf)); if(exp->_matches) sq_free(exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch)); sq_free(exp,sizeof(SQRex)); } } SQBool sqstd_rex_match(SQRex* exp,const SQChar* text) { const SQChar* res = NULL; exp->_bol = text; exp->_eol = text + scstrlen(text); exp->_currsubexp = 0; res = sqstd_rex_matchnode(exp,exp->_nodes,text,NULL); if(res == NULL || res != exp->_eol) return SQFalse; return SQTrue; } SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end) { const SQChar *cur = NULL; SQInteger node = exp->_first; if(text_begin >= text_end) return SQFalse; exp->_bol = text_begin; exp->_eol = text_end; do { cur = text_begin; while(node != -1) { exp->_currsubexp = 0; cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,NULL); if(!cur) break; node = exp->_nodes[node].next; } text_begin++; } while(cur == NULL && text_begin != text_end); if(cur == NULL) return SQFalse; --text_begin; if(out_begin) *out_begin = text_begin; if(out_end) *out_end = cur; return SQTrue; } SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end) { return sqstd_rex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end); } SQInteger sqstd_rex_getsubexpcount(SQRex* exp) { return exp->_nsubexpr; } SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp) { if( n<0 || n >= exp->_nsubexpr) return SQFalse; *subexp = exp->_matches[n]; return SQTrue; } simutrans-124.5/src/squirrel/sqstdlib/sqstdstream.cc000066400000000000000000000162521520675460500227350ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include #include #include #include #include "../squirrel.h" #include "../sqstdio.h" #include "../sqstdblob.h" #include "sqstdstream.h" #include "sqstdblobimpl.h" #define SETUP_STREAM(v) \ SQStream *self = NULL; \ if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)))) \ return sq_throwerror(v,_SC("invalid type tag")); \ if(!self || !self->IsValid()) \ return sq_throwerror(v,_SC("the stream is invalid")); SQInteger _stream_readblob(HSQUIRRELVM v) { SETUP_STREAM(v); SQUserPointer data,blobp; SQInteger size,res; sq_getinteger(v,2,&size); if(size > self->Len()) { size = self->Len(); } data = sq_getscratchpad(v,size); res = self->Read(data,size); if(res <= 0) return sq_throwerror(v,_SC("no data left to read")); blobp = sqstd_createblob(v,res); memcpy(blobp,data,res); return 1; } #define SAFE_READN(ptr,len) { \ if(self->Read(ptr,len) != len) return sq_throwerror(v,_SC("io error")); \ } SQInteger _stream_readn(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger format; sq_getinteger(v, 2, &format); switch(format) { case 'l': { SQInteger i; SAFE_READN(&i, sizeof(i)); sq_pushinteger(v, i); } break; case 'i': { SQInt32 i; SAFE_READN(&i, sizeof(i)); sq_pushinteger(v, i); } break; case 's': { short s; SAFE_READN(&s, sizeof(short)); sq_pushinteger(v, s); } break; case 'w': { unsigned short w; SAFE_READN(&w, sizeof(unsigned short)); sq_pushinteger(v, w); } break; case 'c': { char c; SAFE_READN(&c, sizeof(char)); sq_pushinteger(v, c); } break; case 'b': { unsigned char c; SAFE_READN(&c, sizeof(unsigned char)); sq_pushinteger(v, c); } break; case 'f': { float f; SAFE_READN(&f, sizeof(float)); sq_pushfloat(v, f); } break; case 'd': { double d; SAFE_READN(&d, sizeof(double)); sq_pushfloat(v, (SQFloat)d); } break; default: return sq_throwerror(v, _SC("invalid format")); } return 1; } SQInteger _stream_writeblob(HSQUIRRELVM v) { SQUserPointer data; SQInteger size; SETUP_STREAM(v); if(SQ_FAILED(sqstd_getblob(v,2,&data))) return sq_throwerror(v,_SC("invalid parameter")); size = sqstd_getblobsize(v,2); if(self->Write(data,size) != size) return sq_throwerror(v,_SC("io error")); sq_pushinteger(v,size); return 1; } SQInteger _stream_writen(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger format, ti; SQFloat tf; sq_getinteger(v, 3, &format); switch(format) { case 'l': { SQInteger i; sq_getinteger(v, 2, &ti); i = ti; self->Write(&i, sizeof(SQInteger)); } break; case 'i': { SQInt32 i; sq_getinteger(v, 2, &ti); i = (SQInt32)ti; self->Write(&i, sizeof(SQInt32)); } break; case 's': { short s; sq_getinteger(v, 2, &ti); s = (short)ti; self->Write(&s, sizeof(short)); } break; case 'w': { unsigned short w; sq_getinteger(v, 2, &ti); w = (unsigned short)ti; self->Write(&w, sizeof(unsigned short)); } break; case 'c': { char c; sq_getinteger(v, 2, &ti); c = (char)ti; self->Write(&c, sizeof(char)); } break; case 'b': { unsigned char b; sq_getinteger(v, 2, &ti); b = (unsigned char)ti; self->Write(&b, sizeof(unsigned char)); } break; case 'f': { float f; sq_getfloat(v, 2, &tf); f = (float)tf; self->Write(&f, sizeof(float)); } break; case 'd': { double d; sq_getfloat(v, 2, &tf); d = tf; self->Write(&d, sizeof(double)); } break; default: return sq_throwerror(v, _SC("invalid format")); } return 0; } SQInteger _stream_seek(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger offset, origin = SQ_SEEK_SET; sq_getinteger(v, 2, &offset); if(sq_gettop(v) > 2) { SQInteger t; sq_getinteger(v, 3, &t); switch(t) { case 'b': origin = SQ_SEEK_SET; break; case 'c': origin = SQ_SEEK_CUR; break; case 'e': origin = SQ_SEEK_END; break; default: return sq_throwerror(v,_SC("invalid origin")); } } sq_pushinteger(v, self->Seek(offset, origin)); return 1; } SQInteger _stream_tell(HSQUIRRELVM v) { SETUP_STREAM(v); sq_pushinteger(v, self->Tell()); return 1; } SQInteger _stream_len(HSQUIRRELVM v) { SETUP_STREAM(v); sq_pushinteger(v, self->Len()); return 1; } SQInteger _stream_flush(HSQUIRRELVM v) { SETUP_STREAM(v); if(!self->Flush()) sq_pushinteger(v, 1); else sq_pushnull(v); return 1; } SQInteger _stream_eos(HSQUIRRELVM v) { SETUP_STREAM(v); if(self->EOS()) sq_pushinteger(v, 1); else sq_pushnull(v); return 1; } SQInteger _stream__cloned(HSQUIRRELVM v) { return sq_throwerror(v,_SC("this object cannot be cloned")); } static const SQRegFunction _stream_methods[] = { _DECL_STREAM_FUNC(readblob,2,_SC("xn")), _DECL_STREAM_FUNC(readn,2,_SC("xn")), _DECL_STREAM_FUNC(writeblob,-2,_SC("xx")), _DECL_STREAM_FUNC(writen,3,_SC("xnn")), _DECL_STREAM_FUNC(seek,-2,_SC("xnn")), _DECL_STREAM_FUNC(tell,1,_SC("x")), _DECL_STREAM_FUNC(len,1,_SC("x")), _DECL_STREAM_FUNC(eos,1,_SC("x")), _DECL_STREAM_FUNC(flush,1,_SC("x")), _DECL_STREAM_FUNC(_cloned,0,NULL), {NULL,(SQFUNCTION)0,0,NULL} }; void init_streamclass(HSQUIRRELVM v) { sq_pushregistrytable(v); sq_pushstring(v,_SC("std_stream"),-1); if(SQ_FAILED(sq_get(v,-2))) { sq_pushstring(v,_SC("std_stream"),-1); sq_newclass(v,SQFalse); sq_settypetag(v,-1,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)); SQInteger i = 0; while(_stream_methods[i].name != 0) { const SQRegFunction &f = _stream_methods[i]; sq_pushstring(v,f.name,-1); sq_newclosure(v,f.f,0); sq_setparamscheck(v,f.nparamscheck,f.typemask); sq_newslot(v,-3,SQFalse); i++; } sq_newslot(v,-3,SQFalse); sq_pushroottable(v); sq_pushstring(v,_SC("stream"),-1); sq_pushstring(v,_SC("std_stream"),-1); sq_get(v,-4); sq_newslot(v,-3,SQFalse); sq_pop(v,1); } else { sq_pop(v,1); //result } sq_pop(v,1); } SQRESULT declare_stream(HSQUIRRELVM v,const SQChar* name,SQUserPointer typetag,const SQChar* reg_name,const SQRegFunction *methods,const SQRegFunction *globals) { if(sq_gettype(v,-1) != OT_TABLE) return sq_throwerror(v,_SC("table expected")); SQInteger top = sq_gettop(v); //create delegate init_streamclass(v); sq_pushregistrytable(v); sq_pushstring(v,reg_name,-1); sq_pushstring(v,_SC("std_stream"),-1); if(SQ_SUCCEEDED(sq_get(v,-3))) { sq_newclass(v,SQTrue); sq_settypetag(v,-1,typetag); SQInteger i = 0; while(methods[i].name != 0) { const SQRegFunction &f = methods[i]; sq_pushstring(v,f.name,-1); sq_newclosure(v,f.f,0); sq_setparamscheck(v,f.nparamscheck,f.typemask); sq_setnativeclosurename(v,-1,f.name); sq_newslot(v,-3,SQFalse); i++; } sq_newslot(v,-3,SQFalse); sq_pop(v,1); i = 0; while(globals[i].name!=0) { const SQRegFunction &f = globals[i]; sq_pushstring(v,f.name,-1); sq_newclosure(v,f.f,0); sq_setparamscheck(v,f.nparamscheck,f.typemask); sq_setnativeclosurename(v,-1,f.name); sq_newslot(v,-3,SQFalse); i++; } //register the class in the target table sq_pushstring(v,name,-1); sq_pushregistrytable(v); sq_pushstring(v,reg_name,-1); sq_get(v,-2); sq_remove(v,-2); sq_newslot(v,-3,SQFalse); sq_settop(v,top); return SQ_OK; } sq_settop(v,top); return SQ_ERROR; } simutrans-124.5/src/squirrel/sqstdlib/sqstdstream.h000066400000000000000000000013741520675460500225760ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQSTD_STREAM_H_ #define _SQSTD_STREAM_H_ SQInteger _stream_readblob(HSQUIRRELVM v); SQInteger _stream_readline(HSQUIRRELVM v); SQInteger _stream_readn(HSQUIRRELVM v); SQInteger _stream_writeblob(HSQUIRRELVM v); SQInteger _stream_writen(HSQUIRRELVM v); SQInteger _stream_seek(HSQUIRRELVM v); SQInteger _stream_tell(HSQUIRRELVM v); SQInteger _stream_len(HSQUIRRELVM v); SQInteger _stream_eos(HSQUIRRELVM v); SQInteger _stream_flush(HSQUIRRELVM v); #define _DECL_STREAM_FUNC(name,nparams,typecheck) {_SC(#name),_stream_##name,nparams,typecheck} SQRESULT declare_stream(HSQUIRRELVM v,const SQChar* name,SQUserPointer typetag,const SQChar* reg_name,const SQRegFunction *methods,const SQRegFunction *globals); #endif simutrans-124.5/src/squirrel/sqstdlib/sqstdstring.cc000066400000000000000000000315301520675460500227440ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "../squirrel.h" #include "../sqstdstring.h" #include #include #include #include #include #include #define MAX_FORMAT_LEN 20 #define MAX_WFORMAT_LEN 3 #define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar)) static SQUserPointer rex_typetag = NULL; static SQBool isfmtchr(SQChar ch) { switch(ch) { case '-': case '+': case ' ': case '#': case '0': return SQTrue; } return SQFalse; } static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width) { SQChar *dummy; SQChar swidth[MAX_WFORMAT_LEN]; SQInteger wc = 0; SQInteger start = n; fmt[0] = '%'; while (isfmtchr(src[n])) n++; while (scisdigit(src[n])) { swidth[wc] = src[n]; n++; wc++; if(wc>=MAX_WFORMAT_LEN) return sq_throwerror(v,_SC("width format too long")); } swidth[wc] = '\0'; if(wc > 0) { width = scstrtol(swidth,&dummy,10); } else width = 0; if (src[n] == '.') { n++; wc = 0; while (scisdigit(src[n])) { swidth[wc] = src[n]; n++; wc++; if(wc>=MAX_WFORMAT_LEN) return sq_throwerror(v,_SC("precision format too long")); } swidth[wc] = '\0'; if(wc > 0) { width += scstrtol(swidth,&dummy,10); } } if (n-start > MAX_FORMAT_LEN ) return sq_throwerror(v,_SC("format too long")); memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar)); fmt[(n-start)+2] = '\0'; return n; } SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output) { const SQChar *format; SQChar *dest; SQChar fmt[MAX_FORMAT_LEN]; const SQRESULT res = sq_getstring(v,nformatstringidx,&format); if (SQ_FAILED(res)) { return res; // propagate the error } SQInteger format_size = sq_getsize(v,nformatstringidx); SQInteger allocated = (format_size+2)*sizeof(SQChar); dest = sq_getscratchpad(v,allocated); SQInteger n = 0,i = 0, nparam = nformatstringidx+1, w = 0; //while(format[n] != '\0') while(n < format_size) { if(format[n] != '%') { assert(i < allocated); dest[i++] = format[n]; n++; } else if(format[n+1] == '%') { //handles %% dest[i++] = '%'; n += 2; } else { n++; if( nparam > sq_gettop(v) ) return sq_throwerror(v,_SC("not enough parameters for the given format string")); n = validate_format(v,fmt,format,n,w); if(n < 0) return -1; SQInteger addlen = 0; SQInteger valtype = 0; const SQChar *ts = NULL; SQInteger ti = 0; SQFloat tf = 0; switch(format[n]) { case 's': if(SQ_FAILED(sq_getstring(v,nparam,&ts))) return sq_throwerror(v,_SC("string expected for the specified format")); addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar)); valtype = 's'; break; case 'i': case 'd': case 'o': case 'u': case 'x': case 'X': #ifdef _SQ64 { size_t flen = scstrlen(fmt); SQInteger fpos = flen - 1; SQChar f = fmt[fpos]; const SQChar *prec = (const SQChar *)_PRINT_INT_PREC; while(*prec != _SC('\0')) { fmt[fpos++] = *prec++; } fmt[fpos++] = f; fmt[fpos++] = _SC('\0'); } #endif case 'c': if(SQ_FAILED(sq_getinteger(v,nparam,&ti))) return sq_throwerror(v,_SC("integer expected for the specified format")); addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar)); valtype = 'i'; break; case 'f': case 'g': case 'G': case 'e': case 'E': if(SQ_FAILED(sq_getfloat(v,nparam,&tf))) return sq_throwerror(v,_SC("float expected for the specified format")); addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar)); valtype = 'f'; break; default: return sq_throwerror(v,_SC("invalid format")); } n++; allocated += addlen + sizeof(SQChar); dest = sq_getscratchpad(v,allocated); switch(valtype) { case 's': i += scsprintf(&dest[i],allocated,fmt,ts); break; case 'i': i += scsprintf(&dest[i],allocated,fmt,ti); break; case 'f': i += scsprintf(&dest[i],allocated,fmt,tf); break; }; nparam ++; } } *outlen = i; dest[i] = '\0'; *output = dest; return SQ_OK; } void sqstd_pushstringf(HSQUIRRELVM v,const SQChar *s,...) { SQInteger n=256; va_list args; begin: va_start(args,s); SQChar *b=sq_getscratchpad(v,n); SQInteger r=scvsprintf(b,n,s,args); va_end(args); if (r>=n) { n=r+1;//required+null goto begin; } else if (r<0) { sq_pushnull(v); } else { sq_pushstring(v,b,r); } } static SQInteger _string_printf(HSQUIRRELVM v) { SQChar *dest = NULL; SQInteger length = 0; if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) return -1; SQPRINTFUNCTION printfunc = sq_getprintfunc(v); if(printfunc) printfunc(v,_SC("%s"),dest); return 0; } static SQInteger _string_format(HSQUIRRELVM v) { SQChar *dest = NULL; SQInteger length = 0; if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) return -1; sq_pushstring(v,dest,length); return 1; } static void __strip_l(const SQChar *str,const SQChar **start) { const SQChar *t = str; while(((*t) != '\0') && scisspace(*t)){ t++; } *start = t; } static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end) { if(len == 0) { *end = str; return; } const SQChar *t = &str[len-1]; while(t >= str && scisspace(*t)) { t--; } *end = t + 1; } static SQInteger _string_strip(HSQUIRRELVM v) { const SQChar *str,*start,*end; sq_getstring(v,2,&str); SQInteger len = sq_getsize(v,2); __strip_l(str,&start); __strip_r(str,len,&end); sq_pushstring(v,start,end - start); return 1; } static SQInteger _string_lstrip(HSQUIRRELVM v) { const SQChar *str,*start; sq_getstring(v,2,&str); __strip_l(str,&start); sq_pushstring(v,start,-1); return 1; } static SQInteger _string_rstrip(HSQUIRRELVM v) { const SQChar *str,*end; sq_getstring(v,2,&str); SQInteger len = sq_getsize(v,2); __strip_r(str,len,&end); sq_pushstring(v,str,end - str); return 1; } static SQInteger _string_split(HSQUIRRELVM v) { const SQChar *str,*seps; SQInteger sepsize; SQBool skipempty = SQFalse; sq_getstring(v,2,&str); sq_getstringandsize(v,3,&seps,&sepsize); if(sepsize == 0) return sq_throwerror(v,_SC("empty separators string")); if(sq_gettop(v)>3) { sq_getbool(v,4,&skipempty); } const SQChar *start = str; const SQChar *end = str; sq_newarray(v,0); while(*end != '\0') { SQChar cur = *end; for(SQInteger i = 0; i < sepsize; i++) { if(cur == seps[i]) { if(!skipempty || (end != start)) { sq_pushstring(v,start,end-start); sq_arrayappend(v,-2); } start = end + 1; break; } } end++; } if(end != start) { sq_pushstring(v,start,end-start); sq_arrayappend(v,-2); } return 1; } static SQInteger _string_escape(HSQUIRRELVM v) { const SQChar *str; SQChar *dest,*resstr; SQInteger size; sq_getstring(v,2,&str); size = sq_getsize(v,2); if(size == 0) { sq_push(v,2); return 1; } #ifdef SQUNICODE #if WCHAR_SIZE == 2 const SQChar *escpat = _SC("\\x%04x"); const SQInteger maxescsize = 6; #else //WCHAR_SIZE == 4 const SQChar *escpat = _SC("\\x%08x"); const SQInteger maxescsize = 10; #endif #else const SQChar *escpat = _SC("\\x%02x"); const SQInteger maxescsize = 4; #endif SQInteger destcharsize = (size * maxescsize); //assumes every char could be escaped resstr = dest = (SQChar *)sq_getscratchpad(v,destcharsize * sizeof(SQChar)); SQChar c; SQChar escch; SQInteger escaped = 0; for(int n = 0; n < size; n++){ c = *str++; escch = 0; if(scisprint(c) || c == 0) { switch(c) { case '\a': escch = 'a'; break; case '\b': escch = 'b'; break; case '\t': escch = 't'; break; case '\n': escch = 'n'; break; case '\v': escch = 'v'; break; case '\f': escch = 'f'; break; case '\r': escch = 'r'; break; case '\\': escch = '\\'; break; case '\"': escch = '\"'; break; case '\'': escch = '\''; break; case 0: escch = '0'; break; } if(escch) { *dest++ = '\\'; *dest++ = escch; escaped++; } else { *dest++ = c; } } else { dest += scsprintf(dest, destcharsize, escpat, c); escaped++; } } if(escaped) { sq_pushstring(v,resstr,dest - resstr); } else { sq_push(v,2); //nothing escaped } return 1; } static SQInteger _string_startswith(HSQUIRRELVM v) { const SQChar *str,*cmp; sq_getstring(v,2,&str); sq_getstring(v,3,&cmp); SQInteger len = sq_getsize(v,2); SQInteger cmplen = sq_getsize(v,3); SQBool ret = SQFalse; if(cmplen <= len) { ret = memcmp(str,cmp,sq_rsl(cmplen)) == 0 ? SQTrue : SQFalse; } sq_pushbool(v,ret); return 1; } static SQInteger _string_endswith(HSQUIRRELVM v) { const SQChar *str,*cmp; sq_getstring(v,2,&str); sq_getstring(v,3,&cmp); SQInteger len = sq_getsize(v,2); SQInteger cmplen = sq_getsize(v,3); SQBool ret = SQFalse; if(cmplen <= len) { ret = memcmp(&str[len - cmplen],cmp,sq_rsl(cmplen)) == 0 ? SQTrue : SQFalse; } sq_pushbool(v,ret); return 1; } #define SETUP_REX(v) \ SQRex *self = NULL; \ if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer *)&self,rex_typetag))) { \ return sq_throwerror(v,_SC("invalid type tag")); \ } static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) { SQRex *self = ((SQRex *)p); sqstd_rex_free(self); return 1; } static SQInteger _regexp_match(HSQUIRRELVM v) { SETUP_REX(v); const SQChar *str; sq_getstring(v,2,&str); if(sqstd_rex_match(self,str) == SQTrue) { sq_pushbool(v,SQTrue); return 1; } sq_pushbool(v,SQFalse); return 1; } static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end) { sq_newtable(v); sq_pushstring(v,_SC("begin"),-1); sq_pushinteger(v,begin - str); sq_rawset(v,-3); sq_pushstring(v,_SC("end"),-1); sq_pushinteger(v,end - str); sq_rawset(v,-3); } static SQInteger _regexp_search(HSQUIRRELVM v) { SETUP_REX(v); const SQChar *str,*begin,*end; SQInteger start = 0; sq_getstring(v,2,&str); if(sq_gettop(v) > 2) sq_getinteger(v,3,&start); if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) { _addrexmatch(v,str,begin,end); return 1; } return 0; } static SQInteger _regexp_capture(HSQUIRRELVM v) { SETUP_REX(v); const SQChar *str,*begin,*end; SQInteger start = 0; sq_getstring(v,2,&str); if(sq_gettop(v) > 2) sq_getinteger(v,3,&start); if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) { SQInteger n = sqstd_rex_getsubexpcount(self); SQRexMatch match; sq_newarray(v,0); for(SQInteger i = 0;i < n; i++) { sqstd_rex_getsubexp(self,i,&match); if(match.len > 0) _addrexmatch(v,str,match.begin,match.begin+match.len); else _addrexmatch(v,str,str,str); //empty match sq_arrayappend(v,-2); } return 1; } return 0; } static SQInteger _regexp_subexpcount(HSQUIRRELVM v) { SETUP_REX(v); sq_pushinteger(v,sqstd_rex_getsubexpcount(self)); return 1; } static SQInteger _regexp_constructor(HSQUIRRELVM v) { SQRex *self = NULL; if (SQ_FAILED(sq_getinstanceup(v, 1, (SQUserPointer *)&self, rex_typetag))) { return sq_throwerror(v, _SC("invalid type tag")); } if (self != NULL) { return sq_throwerror(v, _SC("invalid regexp object")); } const SQChar *error,*pattern; sq_getstring(v,2,&pattern); SQRex *rex = sqstd_rex_compile(pattern,&error); if(!rex) return sq_throwerror(v,error); sq_setinstanceup(v,1,rex); sq_setreleasehook(v,1,_rexobj_releasehook); return 0; } static SQInteger _regexp__typeof(HSQUIRRELVM v) { sq_pushstring(v,_SC("regexp"),-1); return 1; } #define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask} static const SQRegFunction rexobj_funcs[]={ _DECL_REX_FUNC(constructor,2,_SC(".s")), _DECL_REX_FUNC(search,-2,_SC("xsn")), _DECL_REX_FUNC(match,2,_SC("xs")), _DECL_REX_FUNC(capture,-2,_SC("xsn")), _DECL_REX_FUNC(subexpcount,1,_SC("x")), _DECL_REX_FUNC(_typeof,1,_SC("x")), {NULL,(SQFUNCTION)0,0,NULL} }; #undef _DECL_REX_FUNC #define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask} static const SQRegFunction stringlib_funcs[]={ _DECL_FUNC(format,-2,_SC(".s")), _DECL_FUNC(printf,-2,_SC(".s")), _DECL_FUNC(strip,2,_SC(".s")), _DECL_FUNC(lstrip,2,_SC(".s")), _DECL_FUNC(rstrip,2,_SC(".s")), _DECL_FUNC(split,-3,_SC(".ssb")), _DECL_FUNC(escape,2,_SC(".s")), _DECL_FUNC(startswith,3,_SC(".ss")), _DECL_FUNC(endswith,3,_SC(".ss")), {NULL,(SQFUNCTION)0,0,NULL} }; #undef _DECL_FUNC SQInteger sqstd_register_stringlib(HSQUIRRELVM v) { sq_pushstring(v,_SC("regexp"),-1); sq_newclass(v,SQFalse); rex_typetag = (SQUserPointer)rexobj_funcs; sq_settypetag(v, -1, rex_typetag); SQInteger i = 0; while(rexobj_funcs[i].name != 0) { const SQRegFunction &f = rexobj_funcs[i]; sq_pushstring(v,f.name,-1); sq_newclosure(v,f.f,0); sq_setparamscheck(v,f.nparamscheck,f.typemask); sq_setnativeclosurename(v,-1,f.name); sq_newslot(v,-3,SQFalse); i++; } sq_newslot(v,-3,SQFalse); i = 0; while(stringlib_funcs[i].name!=0) { sq_pushstring(v,stringlib_funcs[i].name,-1); sq_newclosure(v,stringlib_funcs[i].f,0); sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask); sq_setnativeclosurename(v,-1,stringlib_funcs[i].name); sq_newslot(v,-3,SQFalse); i++; } return 1; } simutrans-124.5/src/squirrel/sqstdlib/sqstdsystem.cc000066400000000000000000000065441520675460500227710ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "../squirrel.h" #include #include #include #include "../sqstdsystem.h" #ifdef SQUNICODE #include #define scgetenv _wgetenv #define scsystem _wsystem #define scasctime _wasctime #define scremove _wremove #define screname _wrename #else #define scgetenv getenv #define scsystem system #define scasctime asctime #define scremove remove #define screname rename #endif #ifdef IOS #include extern char **environ; #endif static SQInteger _system_getenv(HSQUIRRELVM v) { const SQChar *s; if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ sq_pushstring(v,scgetenv(s),-1); return 1; } return 0; } static SQInteger _system_system(HSQUIRRELVM v) { const SQChar *s; if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ #ifdef IOS pid_t pid; posix_spawn(&pid, s, NULL, NULL, NULL, environ); sq_pushinteger(v, 0); #else sq_pushinteger(v,scsystem(s)); #endif return 1; } return sq_throwerror(v,_SC("wrong param")); } static SQInteger _system_clock(HSQUIRRELVM v) { sq_pushfloat(v,((SQFloat)clock())/(SQFloat)CLOCKS_PER_SEC); return 1; } static SQInteger _system_time(HSQUIRRELVM v) { SQInteger t = (SQInteger)time(NULL); sq_pushinteger(v,t); return 1; } static SQInteger _system_remove(HSQUIRRELVM v) { const SQChar *s; sq_getstring(v,2,&s); if(scremove(s)==-1) return sq_throwerror(v,_SC("remove() failed")); return 0; } static SQInteger _system_rename(HSQUIRRELVM v) { const SQChar *oldn,*newn; sq_getstring(v,2,&oldn); sq_getstring(v,3,&newn); if(screname(oldn,newn)==-1) return sq_throwerror(v,_SC("rename() failed")); return 0; } static void _set_integer_slot(HSQUIRRELVM v,const SQChar *name,SQInteger val) { sq_pushstring(v,name,-1); sq_pushinteger(v,val); sq_rawset(v,-3); } static SQInteger _system_date(HSQUIRRELVM v) { time_t t; SQInteger it; SQInteger format = 'l'; if(sq_gettop(v) > 1) { sq_getinteger(v,2,&it); t = it; if(sq_gettop(v) > 2) { sq_getinteger(v,3,(SQInteger*)&format); } } else { time(&t); } tm *date; if(format == 'u') date = gmtime(&t); else date = localtime(&t); if(!date) return sq_throwerror(v,_SC("crt api failure")); sq_newtable(v); _set_integer_slot(v, _SC("sec"), date->tm_sec); _set_integer_slot(v, _SC("min"), date->tm_min); _set_integer_slot(v, _SC("hour"), date->tm_hour); _set_integer_slot(v, _SC("day"), date->tm_mday); _set_integer_slot(v, _SC("month"), date->tm_mon); _set_integer_slot(v, _SC("year"), date->tm_year+1900); _set_integer_slot(v, _SC("wday"), date->tm_wday); _set_integer_slot(v, _SC("yday"), date->tm_yday); return 1; } #define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_system_##name,nparams,pmask} static const SQRegFunction systemlib_funcs[]={ _DECL_FUNC(getenv,2,_SC(".s")), _DECL_FUNC(system,2,_SC(".s")), _DECL_FUNC(clock,0,NULL), _DECL_FUNC(time,1,NULL), _DECL_FUNC(date,-1,_SC(".nn")), _DECL_FUNC(remove,2,_SC(".s")), _DECL_FUNC(rename,3,_SC(".ss")), {NULL,(SQFUNCTION)0,0,NULL} }; #undef _DECL_FUNC SQInteger sqstd_register_systemlib(HSQUIRRELVM v) { SQInteger i=0; while(systemlib_funcs[i].name!=0) { sq_pushstring(v,systemlib_funcs[i].name,-1); sq_newclosure(v,systemlib_funcs[i].f,0); sq_setparamscheck(v,systemlib_funcs[i].nparamscheck,systemlib_funcs[i].typemask); sq_setnativeclosurename(v,-1,systemlib_funcs[i].name); sq_newslot(v,-3,SQFalse); i++; } return 1; } simutrans-124.5/src/squirrel/sqstdmath.h000066400000000000000000000003611520675460500204020ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQSTD_MATH_H_ #define _SQSTD_MATH_H_ #ifdef __cplusplus extern "C" { #endif SQUIRREL_API SQRESULT sqstd_register_mathlib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif simutrans-124.5/src/squirrel/sqstdstring.h000066400000000000000000000022341520675460500207600ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQSTD_STRING_H_ #define _SQSTD_STRING_H_ #ifdef __cplusplus extern "C" { #endif typedef unsigned int SQRexBool; typedef struct SQRex SQRex; typedef struct { const SQChar *begin; SQInteger len; } SQRexMatch; SQUIRREL_API SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error); SQUIRREL_API void sqstd_rex_free(SQRex *exp); SQUIRREL_API SQBool sqstd_rex_match(SQRex* exp,const SQChar* text); SQUIRREL_API SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end); SQUIRREL_API SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end); SQUIRREL_API SQInteger sqstd_rex_getsubexpcount(SQRex* exp); SQUIRREL_API SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp); SQUIRREL_API SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output); SQUIRREL_API void sqstd_pushstringf(HSQUIRRELVM v,const SQChar *s,...); SQUIRREL_API SQRESULT sqstd_register_stringlib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif simutrans-124.5/src/squirrel/sqstdsystem.h000066400000000000000000000003761520675460500210030ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQSTD_SYSTEMLIB_H_ #define _SQSTD_SYSTEMLIB_H_ #ifdef __cplusplus extern "C" { #endif SQUIRREL_API SQInteger sqstd_register_systemlib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif simutrans-124.5/src/squirrel/squirrel.h000066400000000000000000000434261520675460500202510ustar00rootroot00000000000000/* Copyright (c) 2003-2017 Alberto Demichelis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _SQUIRREL_H_ #define _SQUIRREL_H_ #ifdef _SQ_CONFIG_INCLUDE #include _SQ_CONFIG_INCLUDE #endif #ifdef __cplusplus extern "C" { #endif #ifndef SQUIRREL_API #define SQUIRREL_API extern #endif #if (defined(_WIN64) || defined(_LP64)) #ifndef _SQ64 #define _SQ64 #endif #endif #define SQTrue (1) #define SQFalse (0) struct SQVM; struct SQTable; struct SQArray; struct SQString; struct SQClosure; struct SQGenerator; struct SQNativeClosure; struct SQUserData; struct SQFunctionProto; struct SQRefCounted; struct SQClass; struct SQInstance; struct SQDelegable; struct SQOuter; #ifdef _UNICODE // #define SQUNICODE #endif #include "sqconfig.h" #define SQUIRREL_VERSION _SC("Squirrel 3.1 stable") #define SQUIRREL_COPYRIGHT _SC("Copyright (C) 2003-2017 Alberto Demichelis") #define SQUIRREL_AUTHOR _SC("Alberto Demichelis") #define SQUIRREL_VERSION_NUMBER 310 #define SQ_VMSTATE_IDLE 0 #define SQ_VMSTATE_RUNNING 1 #define SQ_VMSTATE_SUSPENDED 2 #define SQUIRREL_EOB 0 #define SQ_BYTECODE_STREAM_TAG 0xFAFA #define SQOBJECT_REF_COUNTED 0x08000000 #define SQOBJECT_NUMERIC 0x04000000 #define SQOBJECT_DELEGABLE 0x02000000 #define SQOBJECT_CANBEFALSE 0x01000000 #define SQ_MATCHTYPEMASKSTRING (-99999) #define _RT_MASK 0x00FFFFFF #define _RAW_TYPE(type) (type&_RT_MASK) #define _RT_NULL 0x00000001 #define _RT_INTEGER 0x00000002 #define _RT_FLOAT 0x00000004 #define _RT_BOOL 0x00000008 #define _RT_STRING 0x00000010 #define _RT_TABLE 0x00000020 #define _RT_ARRAY 0x00000040 #define _RT_USERDATA 0x00000080 #define _RT_CLOSURE 0x00000100 #define _RT_NATIVECLOSURE 0x00000200 #define _RT_GENERATOR 0x00000400 #define _RT_USERPOINTER 0x00000800 #define _RT_THREAD 0x00001000 #define _RT_FUNCPROTO 0x00002000 #define _RT_CLASS 0x00004000 #define _RT_INSTANCE 0x00008000 #define _RT_WEAKREF 0x00010000 #define _RT_OUTER 0x00020000 typedef enum tagSQObjectType{ OT_NULL = (_RT_NULL|SQOBJECT_CANBEFALSE), OT_INTEGER = (_RT_INTEGER|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE), OT_FLOAT = (_RT_FLOAT|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE), OT_BOOL = (_RT_BOOL|SQOBJECT_CANBEFALSE), OT_STRING = (_RT_STRING|SQOBJECT_REF_COUNTED), OT_TABLE = (_RT_TABLE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE), OT_ARRAY = (_RT_ARRAY|SQOBJECT_REF_COUNTED), OT_USERDATA = (_RT_USERDATA|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE), OT_CLOSURE = (_RT_CLOSURE|SQOBJECT_REF_COUNTED), OT_NATIVECLOSURE = (_RT_NATIVECLOSURE|SQOBJECT_REF_COUNTED), OT_GENERATOR = (_RT_GENERATOR|SQOBJECT_REF_COUNTED), OT_USERPOINTER = _RT_USERPOINTER, OT_THREAD = (_RT_THREAD|SQOBJECT_REF_COUNTED) , OT_FUNCPROTO = (_RT_FUNCPROTO|SQOBJECT_REF_COUNTED), //internal usage only OT_CLASS = (_RT_CLASS|SQOBJECT_REF_COUNTED), OT_INSTANCE = (_RT_INSTANCE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE), OT_WEAKREF = (_RT_WEAKREF|SQOBJECT_REF_COUNTED), OT_OUTER = (_RT_OUTER|SQOBJECT_REF_COUNTED) //internal usage only }SQObjectType; #define ISREFCOUNTED(t) (t&SQOBJECT_REF_COUNTED) typedef union tagSQObjectValue { struct SQTable *pTable; struct SQArray *pArray; struct SQClosure *pClosure; struct SQOuter *pOuter; struct SQGenerator *pGenerator; struct SQNativeClosure *pNativeClosure; struct SQString *pString; struct SQUserData *pUserData; SQInteger nInteger; SQFloat fFloat; SQUserPointer pUserPointer; struct SQFunctionProto *pFunctionProto; struct SQRefCounted *pRefCounted; struct SQDelegable *pDelegable; struct SQVM *pThread; struct SQClass *pClass; struct SQInstance *pInstance; struct SQWeakRef *pWeakRef; SQRawObjectVal raw; }SQObjectValue; typedef struct tagSQObject { SQObjectType _type; SQObjectValue _unVal; }SQObject; typedef struct tagSQMemberHandle{ SQBool _static; SQInteger _index; }SQMemberHandle; typedef struct tagSQStackInfos{ const SQChar* funcname; const SQChar* source; SQInteger line; }SQStackInfos; typedef struct SQVM* HSQUIRRELVM; typedef SQObject HSQOBJECT; typedef SQMemberHandle HSQMEMBERHANDLE; typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM); typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size); typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,const SQChar * /*desc*/,const SQChar * /*source*/,SQInteger /*line*/,SQInteger /*column*/); typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,const SQChar * ,...); typedef void (*SQDEBUGHOOK)(HSQUIRRELVM /*v*/, SQInteger /*type*/, const SQChar * /*sourcename*/, SQInteger /*line*/, const SQChar * /*funcname*/); typedef SQInteger (*SQWRITEFUNC)(SQUserPointer,SQUserPointer,SQInteger); typedef SQInteger (*SQREADFUNC)(SQUserPointer,SQUserPointer,SQInteger); typedef SQInteger (*SQLEXREADFUNC)(SQUserPointer); typedef struct tagSQRegFunction{ const SQChar *name; SQFUNCTION f; SQInteger nparamscheck; const SQChar *typemask; }SQRegFunction; typedef struct tagSQFunctionInfo { SQUserPointer funcid; const SQChar *name; const SQChar *source; SQInteger line; }SQFunctionInfo; /*vm*/ SQUIRREL_API HSQUIRRELVM sq_open(SQInteger initialstacksize); SQUIRREL_API HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize); SQUIRREL_API void sq_seterrorhandler(HSQUIRRELVM v); SQUIRREL_API void sq_close(HSQUIRRELVM v); SQUIRREL_API void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p); SQUIRREL_API SQUserPointer sq_getforeignptr(HSQUIRRELVM v); SQUIRREL_API void sq_setsharedforeignptr(HSQUIRRELVM v,SQUserPointer p); SQUIRREL_API SQUserPointer sq_getsharedforeignptr(HSQUIRRELVM v); SQUIRREL_API void sq_setvmreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook); SQUIRREL_API SQRELEASEHOOK sq_getvmreleasehook(HSQUIRRELVM v); SQUIRREL_API void sq_setsharedreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook); SQUIRREL_API SQRELEASEHOOK sq_getsharedreleasehook(HSQUIRRELVM v); SQUIRREL_API void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc,SQPRINTFUNCTION errfunc); SQUIRREL_API SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v); SQUIRREL_API SQPRINTFUNCTION sq_geterrorfunc(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_suspendvm(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool resumedret,SQBool retval,SQBool raiseerror,SQBool throwerror); SQUIRREL_API SQInteger sq_getvmstate(HSQUIRRELVM v); SQUIRREL_API SQInteger sq_getversion(); /*compiler*/ SQUIRREL_API SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror); SQUIRREL_API SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror); SQUIRREL_API void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable); SQUIRREL_API void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable); SQUIRREL_API void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f); /*stack operations*/ SQUIRREL_API void sq_push(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_pop(HSQUIRRELVM v,SQInteger nelemstopop); SQUIRREL_API void sq_poptop(HSQUIRRELVM v); SQUIRREL_API void sq_remove(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQInteger sq_gettop(HSQUIRRELVM v); SQUIRREL_API void sq_settop(HSQUIRRELVM v,SQInteger newtop); SQUIRREL_API SQRESULT sq_reservestack(HSQUIRRELVM v,SQInteger nsize); SQUIRREL_API SQInteger sq_cmp(HSQUIRRELVM v); SQUIRREL_API void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx); /*object creation handling*/ SQUIRREL_API SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size); SQUIRREL_API void sq_newtable(HSQUIRRELVM v); SQUIRREL_API void sq_newtableex(HSQUIRRELVM v,SQInteger initialcapacity); SQUIRREL_API void sq_newarray(HSQUIRRELVM v,SQInteger size); SQUIRREL_API void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars); SQUIRREL_API SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask); SQUIRREL_API SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_setclosureroot(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getclosureroot(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len); SQUIRREL_API void sq_pushfloat(HSQUIRRELVM v,SQFloat f); SQUIRREL_API void sq_pushinteger(HSQUIRRELVM v,SQInteger n); SQUIRREL_API void sq_pushbool(HSQUIRRELVM v,SQBool b); SQUIRREL_API void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p); SQUIRREL_API void sq_pushnull(HSQUIRRELVM v); SQUIRREL_API void sq_pushthread(HSQUIRRELVM v, HSQUIRRELVM thread); SQUIRREL_API SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_typeof(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQInteger sq_getsize(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQHash sq_gethash(HSQUIRRELVM v, SQInteger idx); SQUIRREL_API SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQBool sq_instanceof(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_tostring(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b); SQUIRREL_API SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const SQChar **c,SQInteger *size); SQUIRREL_API SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c); SQUIRREL_API SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i); SQUIRREL_API SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f); SQUIRREL_API SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b); SQUIRREL_API SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread); SQUIRREL_API SQRESULT sq_getuserpointer(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p); SQUIRREL_API SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag); SQUIRREL_API SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag); SQUIRREL_API SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag); SQUIRREL_API void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook); SQUIRREL_API SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize); SQUIRREL_API SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi); SQUIRREL_API SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQInteger *nparams,SQInteger *nfreevars); SQUIRREL_API SQRESULT sq_getclosurename(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name); SQUIRREL_API SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p); SQUIRREL_API SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag); SQUIRREL_API SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize); SQUIRREL_API SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase); SQUIRREL_API SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_weakref(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t); SQUIRREL_API SQRESULT sq_getmemberhandle(HSQUIRRELVM v,SQInteger idx,HSQMEMBERHANDLE *handle); SQUIRREL_API SQRESULT sq_getbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle); SQUIRREL_API SQRESULT sq_setbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle); /*object manipulation*/ SQUIRREL_API void sq_pushroottable(HSQUIRRELVM v); SQUIRREL_API void sq_pushregistrytable(HSQUIRRELVM v); SQUIRREL_API void sq_pushconsttable(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_setroottable(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_setconsttable(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic); SQUIRREL_API SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval); SQUIRREL_API SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval); SQUIRREL_API SQRESULT sq_newmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic); SQUIRREL_API SQRESULT sq_rawnewmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic); SQUIRREL_API SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval); SQUIRREL_API SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize); SQUIRREL_API SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_arrayremove(HSQUIRRELVM v,SQInteger idx,SQInteger itemidx); SQUIRREL_API SQRESULT sq_arrayinsert(HSQUIRRELVM v,SQInteger idx,SQInteger destpos); SQUIRREL_API SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval); SQUIRREL_API SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx); /*calls*/ SQUIRREL_API SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror); SQUIRREL_API SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror); SQUIRREL_API const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx); SQUIRREL_API SQRESULT sq_getcallee(HSQUIRRELVM v); SQUIRREL_API const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval); SQUIRREL_API SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err); SQUIRREL_API SQRESULT sq_throwobject(HSQUIRRELVM v); SQUIRREL_API void sq_reseterror(HSQUIRRELVM v); SQUIRREL_API void sq_getlasterror(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams); /*raw object handling*/ SQUIRREL_API SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po); SQUIRREL_API void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj); SQUIRREL_API void sq_addref(HSQUIRRELVM v,HSQOBJECT *po); SQUIRREL_API SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po); SQUIRREL_API SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v,HSQOBJECT *po); SQUIRREL_API void sq_resetobject(HSQOBJECT *po); SQUIRREL_API const SQChar *sq_objtostring(const HSQOBJECT *o); SQUIRREL_API SQBool sq_objtobool(const HSQOBJECT *o); SQUIRREL_API SQInteger sq_objtointeger(const HSQOBJECT *o); SQUIRREL_API SQFloat sq_objtofloat(const HSQOBJECT *o); SQUIRREL_API SQUserPointer sq_objtouserpointer(const HSQOBJECT *o); SQUIRREL_API SQRESULT sq_getobjtypetag(const HSQOBJECT *o,SQUserPointer * typetag); SQUIRREL_API SQUnsignedInteger sq_getvmrefcount(HSQUIRRELVM v, const HSQOBJECT *po); /*GC*/ SQUIRREL_API SQInteger sq_collectgarbage(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_resurrectunreachable(HSQUIRRELVM v); /*serialization*/ SQUIRREL_API SQRESULT sq_writeclosure(HSQUIRRELVM vm,SQWRITEFUNC writef,SQUserPointer up); SQUIRREL_API SQRESULT sq_readclosure(HSQUIRRELVM vm,SQREADFUNC readf,SQUserPointer up); /*mem allocation*/ SQUIRREL_API void *sq_malloc(SQUnsignedInteger size); SQUIRREL_API void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize); SQUIRREL_API void sq_free(void *p,SQUnsignedInteger size); /*debug*/ SQUIRREL_API SQRESULT sq_stackinfos(HSQUIRRELVM v,SQInteger level,SQStackInfos *si); SQUIRREL_API void sq_setdebughook(HSQUIRRELVM v); SQUIRREL_API void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook); /*UTILITY MACRO*/ #define sq_isnumeric(o) ((o)._type&SQOBJECT_NUMERIC) #define sq_istable(o) ((o)._type==OT_TABLE) #define sq_isarray(o) ((o)._type==OT_ARRAY) #define sq_isfunction(o) ((o)._type==OT_FUNCPROTO) #define sq_isclosure(o) ((o)._type==OT_CLOSURE) #define sq_isgenerator(o) ((o)._type==OT_GENERATOR) #define sq_isnativeclosure(o) ((o)._type==OT_NATIVECLOSURE) #define sq_isstring(o) ((o)._type==OT_STRING) #define sq_isinteger(o) ((o)._type==OT_INTEGER) #define sq_isfloat(o) ((o)._type==OT_FLOAT) #define sq_isuserpointer(o) ((o)._type==OT_USERPOINTER) #define sq_isuserdata(o) ((o)._type==OT_USERDATA) #define sq_isthread(o) ((o)._type==OT_THREAD) #define sq_isnull(o) ((o)._type==OT_NULL) #define sq_isclass(o) ((o)._type==OT_CLASS) #define sq_isinstance(o) ((o)._type==OT_INSTANCE) #define sq_isbool(o) ((o)._type==OT_BOOL) #define sq_isweakref(o) ((o)._type==OT_WEAKREF) #define sq_type(o) ((o)._type) /* deprecated */ #define sq_createslot(v,n) sq_newslot(v,n,SQFalse) #define SQ_OK (0) #define SQ_ERROR (-1) #define SQ_FAILED(res) (res<0) #define SQ_SUCCEEDED(res) (res>=0) #ifdef XX__GNUC__ # define SQ_UNUSED_ARG(x) __attribute__((unused)) x #else # define SQ_UNUSED_ARG(x) #endif #ifdef __cplusplus } /*extern "C"*/ #endif #endif simutrans-124.5/src/squirrel/squirrel/000077500000000000000000000000001520675460500200675ustar00rootroot00000000000000simutrans-124.5/src/squirrel/squirrel/sqapi.cc000066400000000000000000001137651520675460500215300ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqstring.h" #include "sqtable.h" #include "sqarray.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "squserdata.h" #include "sqcompiler.h" #include "sqfuncstate.h" #include "sqclass.h" static bool sq_aux_gettypedarg(HSQUIRRELVM v,SQInteger idx,SQObjectType type,SQObjectPtr **o) { *o = &stack_get(v,idx); if(sq_type(**o) != type){ SQObjectPtr oval = v->PrintObjVal(**o); v->Raise_Error(_SC("wrong argument type, expected '%s' got '%.50s'"),IdType2Name(type),_stringval(oval)); return false; } return true; } #define _GETSAFE_OBJ(v,idx,type,o) { if(!sq_aux_gettypedarg(v,idx,type,&o)) return SQ_ERROR; } #define sq_aux_paramscheck(v,count) \ { \ if(sq_gettop(v) < count){ v->Raise_Error(_SC("not enough params in the stack")); return SQ_ERROR; }\ } SQInteger sq_aux_invalidtype(HSQUIRRELVM v,SQObjectType type) { SQUnsignedInteger buf_size = 100 *sizeof(SQChar); scsprintf(_ss(v)->GetScratchPad(buf_size), buf_size, _SC("unexpected type %s"), IdType2Name(type)); return sq_throwerror(v, _ss(v)->GetScratchPad(-1)); } HSQUIRRELVM sq_open(SQInteger initialstacksize) { SQSharedState *ss; SQVM *v; sq_new(ss, SQSharedState); ss->Init(); v = (SQVM *)SQ_MALLOC(sizeof(SQVM)); new (v) SQVM(ss); ss->_root_vm = v; if(v->Init(NULL, initialstacksize)) { return v; } else { sq_delete(v, SQVM); return NULL; } return v; } HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize) { SQSharedState *ss; SQVM *v; ss=_ss(friendvm); v= (SQVM *)SQ_MALLOC(sizeof(SQVM)); new (v) SQVM(ss); if(v->Init(friendvm, initialstacksize)) { friendvm->Push(v); return v; } else { sq_delete(v, SQVM); return NULL; } } SQInteger sq_getvmstate(HSQUIRRELVM v) { if(v->_suspended) return SQ_VMSTATE_SUSPENDED; else { if(v->_callsstacksize != 0) return SQ_VMSTATE_RUNNING; else return SQ_VMSTATE_IDLE; } } void sq_seterrorhandler(HSQUIRRELVM v) { SQObject o = stack_get(v, -1); if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) { v->_errorhandler = o; v->Pop(); } } void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook) { v->_debughook_native = hook; v->_debughook_closure.Null(); v->_debughook = hook?true:false; } void sq_setdebughook(HSQUIRRELVM v) { SQObject o = stack_get(v,-1); if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) { v->_debughook_closure = o; v->_debughook_native = NULL; v->_debughook = !sq_isnull(o); v->Pop(); } } void sq_close(HSQUIRRELVM v) { SQSharedState *ss = _ss(v); _thread(ss->_root_vm)->Finalize(); sq_delete(ss, SQSharedState); } SQInteger sq_getversion() { return SQUIRREL_VERSION_NUMBER; } SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror) { SQObjectPtr o; #ifndef NO_COMPILER if(Compile(v, read, p, sourcename, o, raiseerror?true:false, _ss(v)->_debuginfo)) { v->Push(SQClosure::Create(_ss(v), _funcproto(o), _table(v->_roottable)->GetWeakRef(OT_TABLE))); return SQ_OK; } return SQ_ERROR; #else return sq_throwerror(v,_SC("this is a no compiler build")); #endif } void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable) { _ss(v)->_debuginfo = enable?true:false; } void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable) { _ss(v)->_notifyallexceptions = enable?true:false; } void sq_addref(HSQUIRRELVM v,HSQOBJECT *po) { if(!ISREFCOUNTED(sq_type(*po))) return; #ifdef NO_GARBAGE_COLLECTOR __AddRef(po->_type,po->_unVal); #else _ss(v)->_refs_table.AddRef(*po); #endif } SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v,HSQOBJECT *po) { if(!ISREFCOUNTED(sq_type(*po))) return 0; #ifdef NO_GARBAGE_COLLECTOR return po->_unVal.pRefCounted->_uiRef; #else return _ss(v)->_refs_table.GetRefCount(*po); #endif } SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po) { if(!ISREFCOUNTED(sq_type(*po))) return SQTrue; #ifdef NO_GARBAGE_COLLECTOR bool ret = (po->_unVal.pRefCounted->_uiRef <= 1) ? SQTrue : SQFalse; __Release(po->_type,po->_unVal); return ret; //the ret val doesn't work(and cannot be fixed) #else return _ss(v)->_refs_table.Release(*po); #endif } SQUnsignedInteger sq_getvmrefcount(HSQUIRRELVM SQ_UNUSED_ARG(v), const HSQOBJECT *po) { if (!ISREFCOUNTED(sq_type(*po))) return 0; return po->_unVal.pRefCounted->_uiRef; } const SQChar *sq_objtostring(const HSQOBJECT *o) { if(sq_type(*o) == OT_STRING) { return _stringval(*o); } return NULL; } SQInteger sq_objtointeger(const HSQOBJECT *o) { if(sq_isnumeric(*o)) { return tointeger(*o); } return 0; } SQFloat sq_objtofloat(const HSQOBJECT *o) { if(sq_isnumeric(*o)) { return tofloat(*o); } return 0; } SQBool sq_objtobool(const HSQOBJECT *o) { if(sq_isbool(*o)) { return _integer(*o); } return SQFalse; } SQUserPointer sq_objtouserpointer(const HSQOBJECT *o) { if(sq_isuserpointer(*o)) { return _userpointer(*o); } return 0; } void sq_pushnull(HSQUIRRELVM v) { v->PushNull(); } void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len) { if(s) v->Push(SQObjectPtr(SQString::Create(_ss(v), s, len))); else v->PushNull(); } void sq_pushinteger(HSQUIRRELVM v,SQInteger n) { v->Push(n); } void sq_pushbool(HSQUIRRELVM v,SQBool b) { v->Push(b?true:false); } void sq_pushfloat(HSQUIRRELVM v,SQFloat n) { v->Push(n); } void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p) { v->Push(p); } void sq_pushthread(HSQUIRRELVM v, HSQUIRRELVM thread) { v->Push(thread); } SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size) { SQUserData *ud = SQUserData::Create(_ss(v), size + SQ_ALIGNMENT); v->Push(ud); return (SQUserPointer)sq_aligning(ud + 1); } void sq_newtable(HSQUIRRELVM v) { v->Push(SQTable::Create(_ss(v), 0)); } void sq_newtableex(HSQUIRRELVM v,SQInteger initialcapacity) { v->Push(SQTable::Create(_ss(v), initialcapacity)); } void sq_newarray(HSQUIRRELVM v,SQInteger size) { v->Push(SQArray::Create(_ss(v), size)); } SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase) { SQClass *baseclass = NULL; if(hasbase) { SQObjectPtr &base = stack_get(v,-1); if(sq_type(base) != OT_CLASS) return sq_throwerror(v,_SC("invalid base type")); baseclass = _class(base); } SQClass *newclass = SQClass::Create(_ss(v), baseclass); if(baseclass) v->Pop(); v->Push(newclass); return SQ_OK; } SQBool sq_instanceof(HSQUIRRELVM v) { SQObjectPtr &inst = stack_get(v,-1); SQObjectPtr &cl = stack_get(v,-2); if(sq_type(inst) != OT_INSTANCE || sq_type(cl) != OT_CLASS) return sq_throwerror(v,_SC("invalid param type")); return _instance(inst)->InstanceOf(_class(cl))?SQTrue:SQFalse; } SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx) { sq_aux_paramscheck(v,2); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); _array(*arr)->Append(v->GetUp(-1)); v->Pop(); return SQ_OK; } SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval) { sq_aux_paramscheck(v, 1); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); if(_array(*arr)->Size() > 0) { if(pushval != 0){ v->Push(_array(*arr)->Top()); } _array(*arr)->Pop(); return SQ_OK; } return sq_throwerror(v, _SC("empty array")); } SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize) { sq_aux_paramscheck(v,1); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); if(newsize >= 0) { _array(*arr)->Resize(newsize); return SQ_OK; } return sq_throwerror(v,_SC("negative size")); } SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx) { sq_aux_paramscheck(v, 1); SQObjectPtr *o; _GETSAFE_OBJ(v, idx, OT_ARRAY,o); SQArray *arr = _array(*o); if(arr->Size() > 0) { SQObjectPtr t; SQInteger size = arr->Size(); SQInteger n = size >> 1; size -= 1; for(SQInteger i = 0; i < n; i++) { t = arr->_values[i]; arr->_values[i] = arr->_values[size-i]; arr->_values[size-i] = t; } return SQ_OK; } return SQ_OK; } SQRESULT sq_arrayremove(HSQUIRRELVM v,SQInteger idx,SQInteger itemidx) { sq_aux_paramscheck(v, 1); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); return _array(*arr)->Remove(itemidx) ? SQ_OK : sq_throwerror(v,_SC("index out of range")); } SQRESULT sq_arrayinsert(HSQUIRRELVM v,SQInteger idx,SQInteger destpos) { sq_aux_paramscheck(v, 1); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); SQRESULT ret = _array(*arr)->Insert(destpos, v->GetUp(-1)) ? SQ_OK : sq_throwerror(v,_SC("index out of range")); v->Pop(); return ret; } void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars) { SQNativeClosure *nc = SQNativeClosure::Create(_ss(v), func,nfreevars); nc->_nparamscheck = 0; for(SQUnsignedInteger i = 0; i < nfreevars; i++) { nc->_outervalues[i] = v->Top(); v->Pop(); } v->Push(SQObjectPtr(nc)); } SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQInteger *nparams,SQInteger *nfreevars) { SQObject o = stack_get(v, idx); if(sq_type(o) == OT_CLOSURE) { SQClosure *c = _closure(o); SQFunctionProto *proto = c->_function; *nparams = proto->_nparameters; *nfreevars = proto->_noutervalues; return SQ_OK; } else if(sq_type(o) == OT_NATIVECLOSURE) { SQNativeClosure *c = _nativeclosure(o); *nparams = c->_nparamscheck; *nfreevars = (SQInteger)c->_noutervalues; return SQ_OK; } return sq_throwerror(v,_SC("the object is not a closure")); } SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name) { SQObject o = stack_get(v, idx); if(sq_isnativeclosure(o)) { SQNativeClosure *nc = _nativeclosure(o); nc->_name = SQString::Create(_ss(v),name); return SQ_OK; } return sq_throwerror(v,_SC("the object is not a nativeclosure")); } SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask) { SQObject o = stack_get(v, -1); if(!sq_isnativeclosure(o)) return sq_throwerror(v, _SC("native closure expected")); SQNativeClosure *nc = _nativeclosure(o); nc->_nparamscheck = nparamscheck; if(typemask) { SQIntVec res; if(!CompileTypemask(res, typemask)) return sq_throwerror(v, _SC("invalid typemask")); nc->_typecheck.copy(res); } else { nc->_typecheck.resize(0); } if(nparamscheck == SQ_MATCHTYPEMASKSTRING) { nc->_nparamscheck = nc->_typecheck.size(); } return SQ_OK; } SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v,idx); if(!sq_isnativeclosure(o) && !sq_isclosure(o)) return sq_throwerror(v,_SC("the target is not a closure")); SQObjectPtr &env = stack_get(v,-1); if(!sq_istable(env) && !sq_isarray(env) && !sq_isclass(env) && !sq_isinstance(env)) return sq_throwerror(v,_SC("invalid environment")); SQWeakRef *w = _refcounted(env)->GetWeakRef(sq_type(env)); SQObjectPtr ret; if(sq_isclosure(o)) { SQClosure *c = _closure(o)->Clone(); __ObjRelease(c->_env); c->_env = w; __ObjAddRef(c->_env); if(_closure(o)->_base) { c->_base = _closure(o)->_base; __ObjAddRef(c->_base); } ret = c; } else { //then must be a native closure SQNativeClosure *c = _nativeclosure(o)->Clone(); __ObjRelease(c->_env); c->_env = w; __ObjAddRef(c->_env); ret = c; } v->Pop(); v->Push(ret); return SQ_OK; } SQRESULT sq_getclosurename(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v,idx); if(!sq_isnativeclosure(o) && !sq_isclosure(o)) return sq_throwerror(v,_SC("the target is not a closure")); if(sq_isnativeclosure(o)) { v->Push(_nativeclosure(o)->_name); } else { //closure v->Push(_closure(o)->_function->_name); } return SQ_OK; } SQRESULT sq_setclosureroot(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &c = stack_get(v,idx); SQObject o = stack_get(v, -1); if(!sq_isclosure(c)) return sq_throwerror(v, _SC("closure expected")); if(sq_istable(o)) { _closure(c)->SetRoot(_table(o)->GetWeakRef(OT_TABLE)); v->Pop(); return SQ_OK; } return sq_throwerror(v, _SC("invalid type")); } SQRESULT sq_getclosureroot(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &c = stack_get(v,idx); if(!sq_isclosure(c)) return sq_throwerror(v, _SC("closure expected")); v->Push(_closure(c)->_root->_obj); return SQ_OK; } SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx) { SQObject &o=stack_get(v,idx); switch(sq_type(o)) { case OT_TABLE: _table(o)->Clear(); break; case OT_ARRAY: _array(o)->Resize(0); break; default: return sq_throwerror(v, _SC("clear only works on table and array")); break; } return SQ_OK; } void sq_pushroottable(HSQUIRRELVM v) { v->Push(v->_roottable); } void sq_pushregistrytable(HSQUIRRELVM v) { v->Push(_ss(v)->_registry); } void sq_pushconsttable(HSQUIRRELVM v) { v->Push(_ss(v)->_consts); } SQRESULT sq_setroottable(HSQUIRRELVM v) { SQObject o = stack_get(v, -1); if(sq_istable(o) || sq_isnull(o)) { v->_roottable = o; v->Pop(); return SQ_OK; } return sq_throwerror(v, _SC("invalid type")); } SQRESULT sq_setconsttable(HSQUIRRELVM v) { SQObject o = stack_get(v, -1); if(sq_istable(o)) { _ss(v)->_consts = o; v->Pop(); return SQ_OK; } return sq_throwerror(v, _SC("invalid type, expected table")); } void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p) { v->_foreignptr = p; } SQUserPointer sq_getforeignptr(HSQUIRRELVM v) { return v->_foreignptr; } void sq_setsharedforeignptr(HSQUIRRELVM v,SQUserPointer p) { _ss(v)->_foreignptr = p; } SQUserPointer sq_getsharedforeignptr(HSQUIRRELVM v) { return _ss(v)->_foreignptr; } void sq_setvmreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook) { v->_releasehook = hook; } SQRELEASEHOOK sq_getvmreleasehook(HSQUIRRELVM v) { return v->_releasehook; } void sq_setsharedreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook) { _ss(v)->_releasehook = hook; } SQRELEASEHOOK sq_getsharedreleasehook(HSQUIRRELVM v) { return _ss(v)->_releasehook; } void sq_push(HSQUIRRELVM v,SQInteger idx) { v->Push(stack_get(v, idx)); } SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx) { return sq_type(stack_get(v, idx)); } SQRESULT sq_typeof(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); SQObjectPtr res; if(!v->TypeOf(o,res)) { return SQ_ERROR; } v->Push(res); return SQ_OK; } SQRESULT sq_tostring(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); SQObjectPtr res; if(!v->ToString(o,res)) { return SQ_ERROR; } v->Push(res); return SQ_OK; } void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b) { SQObjectPtr &o = stack_get(v, idx); *b = SQVM::IsFalse(o)?SQFalse:SQTrue; } SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i) { SQObjectPtr &o = stack_get(v, idx); if(sq_isnumeric(o)) { *i = tointeger(o); return SQ_OK; } if(sq_isbool(o)) { *i = SQVM::IsFalse(o)?SQFalse:SQTrue; return SQ_OK; } return SQ_ERROR; } SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f) { SQObjectPtr &o = stack_get(v, idx); if(sq_isnumeric(o)) { *f = tofloat(o); return SQ_OK; } return SQ_ERROR; } SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b) { SQObjectPtr &o = stack_get(v, idx); if(sq_isbool(o)) { *b = _integer(o); return SQ_OK; } return SQ_ERROR; } SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const SQChar **c,SQInteger *size) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_STRING,o); *c = _stringval(*o); *size = _string(*o)->_len; return SQ_OK; } SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_STRING,o); *c = _stringval(*o); return SQ_OK; } SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_THREAD,o); *thread = _thread(*o); return SQ_OK; } SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v,idx); v->PushNull(); if(!v->Clone(o, stack_get(v, -1))){ v->Pop(); return SQ_ERROR; } return SQ_OK; } SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); SQObjectType type = sq_type(o); switch(type) { case OT_STRING: return _string(o)->_len; case OT_TABLE: return _table(o)->CountUsed(); case OT_ARRAY: return _array(o)->Size(); case OT_USERDATA: return _userdata(o)->_size; case OT_INSTANCE: return _instance(o)->_class->_udsize; case OT_CLASS: return _class(o)->_udsize; default: return sq_aux_invalidtype(v, type); } } SQHash sq_gethash(HSQUIRRELVM v, SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); return HashObj(o); } SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_USERDATA,o); (*p) = _userdataval(*o); if(typetag) *typetag = _userdata(*o)->_typetag; return SQ_OK; } SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag) { SQObjectPtr &o = stack_get(v,idx); switch(sq_type(o)) { case OT_USERDATA: _userdata(o)->_typetag = typetag; break; case OT_CLASS: _class(o)->_typetag = typetag; break; default: return sq_throwerror(v,_SC("invalid object type")); } return SQ_OK; } SQRESULT sq_getobjtypetag(const HSQOBJECT *o,SQUserPointer * typetag) { switch(sq_type(*o)) { case OT_INSTANCE: *typetag = _instance(*o)->_class->_typetag; break; case OT_USERDATA: *typetag = _userdata(*o)->_typetag; break; case OT_CLASS: *typetag = _class(*o)->_typetag; break; default: return SQ_ERROR; } return SQ_OK; } SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag) { SQObjectPtr &o = stack_get(v,idx); if(SQ_FAILED(sq_getobjtypetag(&o,typetag))) return SQ_ERROR;// this is not an error it should be a bool but would break backward compatibility return SQ_OK; } SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_USERPOINTER,o); (*p) = _userpointer(*o); return SQ_OK; } SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p) { SQObjectPtr &o = stack_get(v,idx); if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); _instance(o)->_userpointer = p; return SQ_OK; } SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize) { SQObjectPtr &o = stack_get(v,idx); if(sq_type(o) != OT_CLASS) return sq_throwerror(v,_SC("the object is not a class")); if(_class(o)->_locked) return sq_throwerror(v,_SC("the class is locked")); _class(o)->_udsize = udsize; return SQ_OK; } SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag) { SQObjectPtr &o = stack_get(v,idx); if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); (*p) = _instance(o)->_userpointer; if(typetag != 0) { SQClass *cl = _instance(o)->_class; do{ if(cl->_typetag == typetag) return SQ_OK; cl = cl->_base; }while(cl != NULL); return sq_throwerror(v,_SC("invalid type tag")); } return SQ_OK; } SQInteger sq_gettop(HSQUIRRELVM v) { return (v->_top) - v->_stackbase; } void sq_settop(HSQUIRRELVM v, SQInteger newtop) { SQInteger top = sq_gettop(v); if(top > newtop) sq_pop(v, top - newtop); else while(top++ < newtop) sq_pushnull(v); } void sq_pop(HSQUIRRELVM v, SQInteger nelemstopop) { assert(v->_top >= nelemstopop); v->Pop(nelemstopop); } void sq_poptop(HSQUIRRELVM v) { assert(v->_top >= 1); v->Pop(); } void sq_remove(HSQUIRRELVM v, SQInteger idx) { v->Remove(idx); } SQInteger sq_cmp(HSQUIRRELVM v) { SQInteger res; v->ObjCmp(stack_get(v, -1), stack_get(v, -2),res); return res; } SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) { sq_aux_paramscheck(v, 3); SQObjectPtr &self = stack_get(v, idx); if(sq_type(self) == OT_TABLE || sq_type(self) == OT_CLASS) { SQObjectPtr &key = v->GetUp(-2); if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); v->NewSlot(self, key, v->GetUp(-1),bstatic?true:false); v->Pop(2); } return SQ_OK; } SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) { sq_aux_paramscheck(v, 2); SQObjectPtr *self; _GETSAFE_OBJ(v, idx, OT_TABLE,self); SQObjectPtr &key = v->GetUp(-1); if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); SQObjectPtr res; if(!v->DeleteSlot(*self, key, res)){ v->Pop(); return SQ_ERROR; } if(pushval) v->GetUp(-1) = res; else v->Pop(); return SQ_OK; } SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self = stack_get(v, idx); if(v->Set(self, v->GetUp(-2), v->GetUp(-1),DONT_FALL_BACK)) { v->Pop(2); return SQ_OK; } return SQ_ERROR; } SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self = stack_get(v, idx); SQObjectPtr &key = v->GetUp(-2); if(sq_type(key) == OT_NULL) { v->Pop(2); return sq_throwerror(v, _SC("null key")); } switch(sq_type(self)) { case OT_TABLE: _table(self)->NewSlot(key, v->GetUp(-1)); v->Pop(2); return SQ_OK; break; case OT_CLASS: _class(self)->NewSlot(_ss(v), key, v->GetUp(-1),false); v->Pop(2); return SQ_OK; break; case OT_INSTANCE: if(_instance(self)->Set(key, v->GetUp(-1))) { v->Pop(2); return SQ_OK; } break; case OT_ARRAY: if(v->Set(self, key, v->GetUp(-1),false)) { v->Pop(2); return SQ_OK; } break; default: v->Pop(2); return sq_throwerror(v, _SC("rawset works only on array/table/class and instance")); } v->Raise_IdxError(v->GetUp(-2));return SQ_ERROR; } SQRESULT sq_newmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) { SQObjectPtr &self = stack_get(v, idx); if(sq_type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); SQObjectPtr &key = v->GetUp(-3); if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,false)) { v->Pop(3); return SQ_ERROR; } v->Pop(3); return SQ_OK; } SQRESULT sq_rawnewmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) { SQObjectPtr &self = stack_get(v, idx); if(sq_type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); SQObjectPtr &key = v->GetUp(-3); if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,true)) { v->Pop(3); return SQ_ERROR; } v->Pop(3); return SQ_OK; } SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self = stack_get(v, idx); SQObjectPtr &mt = v->GetUp(-1); SQObjectType type = sq_type(self); switch(type) { case OT_TABLE: if(sq_type(mt) == OT_TABLE) { if(!_table(self)->SetDelegate(_table(mt))) { return sq_throwerror(v, _SC("delegate cycle")); } v->Pop(); } else if(sq_type(mt)==OT_NULL) { _table(self)->SetDelegate(NULL); v->Pop(); } else return sq_aux_invalidtype(v,type); break; case OT_USERDATA: if(sq_type(mt)==OT_TABLE) { _userdata(self)->SetDelegate(_table(mt)); v->Pop(); } else if(sq_type(mt)==OT_NULL) { _userdata(self)->SetDelegate(NULL); v->Pop(); } else return sq_aux_invalidtype(v, type); break; default: return sq_aux_invalidtype(v, type); break; } return SQ_OK; } SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) { sq_aux_paramscheck(v, 2); SQObjectPtr *self; _GETSAFE_OBJ(v, idx, OT_TABLE,self); SQObjectPtr &key = v->GetUp(-1); SQObjectPtr t; if(_table(*self)->Get(key,t)) { _table(*self)->Remove(key); } if(pushval != 0) v->GetUp(-1) = t; else v->Pop(); return SQ_OK; } SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self=stack_get(v,idx); switch(sq_type(self)){ case OT_TABLE: case OT_USERDATA: if(!_delegable(self)->_delegate){ v->PushNull(); break; } v->Push(SQObjectPtr(_delegable(self)->_delegate)); break; default: return sq_throwerror(v,_SC("wrong type")); break; } return SQ_OK; } SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self=stack_get(v,idx); SQObjectPtr &obj = v->GetUp(-1); if(v->Get(self,obj,obj,false,DONT_FALL_BACK)) return SQ_OK; v->Pop(); return SQ_ERROR; } SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self=stack_get(v,idx); SQObjectPtr &obj = v->GetUp(-1); switch(sq_type(self)) { case OT_TABLE: if(_table(self)->Get(obj,obj)) return SQ_OK; break; case OT_CLASS: if(_class(self)->Get(obj,obj)) return SQ_OK; break; case OT_INSTANCE: if(_instance(self)->Get(obj,obj)) return SQ_OK; break; case OT_ARRAY:{ if(sq_isnumeric(obj)){ if(_array(self)->Get(tointeger(obj),obj)) { return SQ_OK; } } else { v->Pop(); return sq_throwerror(v,_SC("invalid index type for an array")); } } break; default: v->Pop(); return sq_throwerror(v,_SC("rawget works only on array/table/instance and class")); } // modified: call Raise_IdxError v->Raise_IdxError(v->GetUp(-1)); v->Pop(); return SQ_ERROR; } SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po) { *po=stack_get(v,idx); return SQ_OK; } const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx) { SQUnsignedInteger cstksize=v->_callsstacksize; SQUnsignedInteger lvl=(cstksize-level)-1; SQInteger stackbase=v->_stackbase; if(lvl_callsstack[(cstksize-i)-1]; stackbase-=ci._prevstkbase; } SQVM::CallInfo &ci=v->_callsstack[lvl]; if(sq_type(ci._closure)!=OT_CLOSURE) return NULL; SQClosure *c=_closure(ci._closure); SQFunctionProto *func=c->_function; if(func->_noutervalues > (SQInteger)idx) { v->Push(*_outer(c->_outervalues[idx])->_valptr); return _stringval(func->_outervalues[idx]._name); } idx -= func->_noutervalues; return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1); } return NULL; } void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj) { v->Push(SQObjectPtr(obj)); } void sq_resetobject(HSQOBJECT *po) { po->_unVal.pUserPointer=NULL;po->_type=OT_NULL; } SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err) { v->_lasterror=SQString::Create(_ss(v),err); return SQ_ERROR; } SQRESULT sq_throwobject(HSQUIRRELVM v) { v->_lasterror = v->GetUp(-1); v->Pop(); return SQ_ERROR; } void sq_reseterror(HSQUIRRELVM v) { v->_lasterror.Null(); } void sq_getlasterror(HSQUIRRELVM v) { v->Push(v->_lasterror); } SQRESULT sq_reservestack(HSQUIRRELVM v,SQInteger nsize) { if (((SQUnsignedInteger)v->_top + nsize) > v->_stack.size()) { if(v->_nmetamethodscall) { return sq_throwerror(v,_SC("cannot resize stack while in a metamethod")); } v->_stack.resize(v->_stack.size() + ((v->_top + nsize) - v->_stack.size())); } return SQ_OK; } SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror) { if (sq_type(v->GetUp(-1)) == OT_GENERATOR) { v->PushNull(); //retval if (!v->Execute(v->GetUp(-2), 0, v->_top, v->GetUp(-1), raiseerror, SQVM::ET_RESUME_GENERATOR)) {v->Raise_Error(v->_lasterror); return SQ_ERROR;} if(!retval) v->Pop(); return SQ_OK; } return sq_throwerror(v,_SC("only generators can be resumed")); } /* modified for our purposes to handle suspended scripts */ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror) { SQObjectPtr res; if(v->_suspended) { v->Pop(params);//pop closure and args return sq_throwerror(v,_SC("script took to long")); } // we can suspend call only when vm is not already running (thus when we are not called from squirrel) if(v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false, sq_getvmstate(v)==SQ_VMSTATE_IDLE)){ if(!v->_suspended) { v->Pop(params);//pop closure and args } if(retval){ v->Push(res); return SQ_OK; } return SQ_OK; } else { v->Pop(params); return SQ_ERROR; // sq_throwerror(v,_SC("call failed")); } } /* TODO check whether this works with our suspend logic; wont be missed SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) { SQObjectPtr &res = v->GetUp(-(nparams + 1)); if (sq_type(res) != OT_CLOSURE) { return sq_throwerror(v, _SC("only closure can be tail called")); } SQClosure *clo = _closure(res); if (clo->_function->_bgenerator) { return sq_throwerror(v, _SC("generators cannot be tail called")); } SQInteger stackbase = (v->_top - nparams) - v->_stackbase; if (!v->TailCall(clo, stackbase, nparams)) { return SQ_ERROR; } return SQ_TAILCALL_FLAG; } */ SQRESULT sq_suspendvm(HSQUIRRELVM v) { return v->Suspend(); } SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool raiseerror,SQBool throwerror) { SQObjectPtr ret; if(!v->_suspended) return sq_throwerror(v,_SC("cannot resume a vm that is not running any code")); SQInteger target = v->_suspended_target; if(wakeupret) { if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target)=v->GetUp(-1); //retval } v->Pop(); } else if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target).Null(); } SQObjectPtr dummy; if(!v->Execute(dummy,-1,-1,ret,raiseerror,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM, true /*can_suspend*/)) { return SQ_ERROR; } if(retval) v->Push(ret); return SQ_OK; } void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook) { SQObjectPtr &ud=stack_get(v,idx); switch(sq_type(ud) ) { case OT_USERDATA: _userdata(ud)->_hook = hook; break; case OT_INSTANCE: _instance(ud)->_hook = hook; break; case OT_CLASS: _class(ud)->_hook = hook; break; default: return; } } SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &ud=stack_get(v,idx); switch(sq_type(ud) ) { case OT_USERDATA: return _userdata(ud)->_hook; break; case OT_INSTANCE: return _instance(ud)->_hook; break; case OT_CLASS: return _class(ud)->_hook; break; default: return NULL; } } void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f) { _ss(v)->_compilererrorhandler = f; } SQRESULT sq_writeclosure(HSQUIRRELVM v,SQWRITEFUNC w,SQUserPointer up) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, -1, OT_CLOSURE,o); unsigned short tag = SQ_BYTECODE_STREAM_TAG; if(_closure(*o)->_function->_noutervalues) return sq_throwerror(v,_SC("a closure with free variables bound cannot be serialized")); if(w(up,&tag,2) != 2) return sq_throwerror(v,_SC("io error")); if(!_closure(*o)->Save(v,up,w)) return SQ_ERROR; return SQ_OK; } SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up) { SQObjectPtr closure; unsigned short tag; if(r(up,&tag,2) != 2) return sq_throwerror(v,_SC("io error")); if(tag != SQ_BYTECODE_STREAM_TAG) return sq_throwerror(v,_SC("invalid stream")); if(!SQClosure::Load(v,up,r,closure)) return SQ_ERROR; v->Push(closure); return SQ_OK; } SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize) { return _ss(v)->GetScratchPad(minsize); } SQRESULT sq_resurrectunreachable(HSQUIRRELVM v) { #ifndef NO_GARBAGE_COLLECTOR _ss(v)->ResurrectUnreachable(v); return SQ_OK; #else return sq_throwerror(v,_SC("sq_resurrectunreachable requires a garbage collector build")); #endif } SQInteger sq_collectgarbage(HSQUIRRELVM v) { #ifndef NO_GARBAGE_COLLECTOR return _ss(v)->CollectGarbage(v); #else return -1; #endif } SQRESULT sq_getcallee(HSQUIRRELVM v) { if(v->_callsstacksize > 1) { v->Push(v->_callsstack[v->_callsstacksize - 2]._closure); return SQ_OK; } return sq_throwerror(v,_SC("no closure in the calls stack")); } const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) { SQObjectPtr &self=stack_get(v,idx); const SQChar *name = NULL; switch(sq_type(self)) { case OT_CLOSURE:{ SQClosure *clo = _closure(self); SQFunctionProto *fp = clo->_function; if(((SQUnsignedInteger)fp->_noutervalues) > nval) { v->Push(*(_outer(clo->_outervalues[nval])->_valptr)); SQOuterVar &ov = fp->_outervalues[nval]; name = _stringval(ov._name); } } break; case OT_NATIVECLOSURE:{ SQNativeClosure *clo = _nativeclosure(self); if(clo->_noutervalues > nval) { v->Push(clo->_outervalues[nval]); name = _SC("@NATIVE"); } } break; default: break; //shutup compiler } return name; } SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) { SQObjectPtr &self=stack_get(v,idx); switch(sq_type(self)) { case OT_CLOSURE:{ SQFunctionProto *fp = _closure(self)->_function; if(((SQUnsignedInteger)fp->_noutervalues) > nval){ *(_outer(_closure(self)->_outervalues[nval])->_valptr) = stack_get(v,-1); } else return sq_throwerror(v,_SC("invalid free var index")); } break; case OT_NATIVECLOSURE: if(_nativeclosure(self)->_noutervalues > nval){ _nativeclosure(self)->_outervalues[nval] = stack_get(v,-1); } else return sq_throwerror(v,_SC("invalid free var index")); break; default: return sq_aux_invalidtype(v, sq_type(self)); } v->Pop(); return SQ_OK; } SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_CLASS,o); SQObjectPtr &key = stack_get(v,-2); SQObjectPtr &val = stack_get(v,-1); SQObjectPtr attrs; if(sq_type(key) == OT_NULL) { attrs = _class(*o)->_attributes; _class(*o)->_attributes = val; v->Pop(2); v->Push(attrs); return SQ_OK; }else if(_class(*o)->GetAttributes(key,attrs)) { _class(*o)->SetAttributes(key,val); v->Pop(2); v->Push(attrs); return SQ_OK; } return sq_throwerror(v,_SC("wrong index")); } SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_CLASS,o); SQObjectPtr &key = stack_get(v,-1); SQObjectPtr attrs; if(sq_type(key) == OT_NULL) { attrs = _class(*o)->_attributes; v->Pop(); v->Push(attrs); return SQ_OK; } else if(_class(*o)->GetAttributes(key,attrs)) { v->Pop(); v->Push(attrs); return SQ_OK; } return sq_throwerror(v,_SC("wrong index")); } SQRESULT sq_getmemberhandle(HSQUIRRELVM v,SQInteger idx,HSQMEMBERHANDLE *handle) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_CLASS,o); SQObjectPtr &key = stack_get(v,-1); SQTable *m = _class(*o)->_members; SQObjectPtr val; if(m->Get(key,val)) { handle->_static = _isfield(val) ? SQFalse : SQTrue; handle->_index = _member_idx(val); v->Pop(); return SQ_OK; } return sq_throwerror(v,_SC("wrong index")); } SQRESULT _getmemberbyhandle(HSQUIRRELVM v,SQObjectPtr &self,const HSQMEMBERHANDLE *handle,SQObjectPtr *&val) { switch(sq_type(self)) { case OT_INSTANCE: { SQInstance *i = _instance(self); if(handle->_static) { SQClass *c = i->_class; val = &c->_methods[handle->_index].val; } else { val = &i->_values[handle->_index]; } } break; case OT_CLASS: { SQClass *c = _class(self); if(handle->_static) { val = &c->_methods[handle->_index].val; } else { val = &c->_defaultvalues[handle->_index].val; } } break; default: return sq_throwerror(v,_SC("wrong type(expected class or instance)")); } return SQ_OK; } SQRESULT sq_getbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle) { SQObjectPtr &self = stack_get(v,idx); SQObjectPtr *val = NULL; if(SQ_FAILED(_getmemberbyhandle(v,self,handle,val))) { return SQ_ERROR; } v->Push(_realval(*val)); return SQ_OK; } SQRESULT sq_setbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle) { SQObjectPtr &self = stack_get(v,idx); SQObjectPtr &newval = stack_get(v,-1); SQObjectPtr *val = NULL; if(SQ_FAILED(_getmemberbyhandle(v,self,handle,val))) { return SQ_ERROR; } *val = newval; v->Pop(); return SQ_OK; } SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_CLASS,o); if(_class(*o)->_base) v->Push(SQObjectPtr(_class(*o)->_base)); else v->PushNull(); return SQ_OK; } SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_INSTANCE,o); v->Push(SQObjectPtr(_instance(*o)->_class)); return SQ_OK; } SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_CLASS,o); v->Push(_class(*o)->CreateInstance()); return SQ_OK; } void sq_weakref(HSQUIRRELVM v,SQInteger idx) { SQObject &o=stack_get(v,idx); if(ISREFCOUNTED(sq_type(o))) { v->Push(_refcounted(o)->GetWeakRef(sq_type(o))); return; } v->Push(o); } SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v,idx); if(sq_type(o) != OT_WEAKREF) { return sq_throwerror(v,_SC("the object must be a weakref")); } v->Push(_weakref(o)->_obj); return SQ_OK; } SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t) { SQSharedState *ss = _ss(v); switch(t) { case OT_TABLE: v->Push(ss->_table_default_delegate); break; case OT_ARRAY: v->Push(ss->_array_default_delegate); break; case OT_STRING: v->Push(ss->_string_default_delegate); break; case OT_INTEGER: case OT_FLOAT: v->Push(ss->_number_default_delegate); break; case OT_GENERATOR: v->Push(ss->_generator_default_delegate); break; case OT_CLOSURE: case OT_NATIVECLOSURE: v->Push(ss->_closure_default_delegate); break; case OT_THREAD: v->Push(ss->_thread_default_delegate); break; case OT_CLASS: v->Push(ss->_class_default_delegate); break; case OT_INSTANCE: v->Push(ss->_instance_default_delegate); break; case OT_WEAKREF: v->Push(ss->_weakref_default_delegate); break; default: return sq_throwerror(v,_SC("the type doesn't have a default delegate")); } return SQ_OK; } SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr o=stack_get(v,idx),&refpos = stack_get(v,-1),realkey,val; if(sq_type(o) == OT_GENERATOR) { return sq_throwerror(v,_SC("cannot iterate a generator")); } int faketojump; if(!v->FOREACH_OP(o,realkey,val,refpos,0,666,faketojump)) return SQ_ERROR; if(faketojump != 666) { v->Push(realkey); v->Push(val); return SQ_OK; } return SQ_ERROR; } struct BufState{ const SQChar *buf; SQInteger ptr; SQInteger size; }; SQInteger buf_lexfeed(SQUserPointer file) { BufState *buf=(BufState*)file; if(buf->size<(buf->ptr+1)) return 0; return buf->buf[buf->ptr++]; } SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror) { BufState buf; buf.buf = s; buf.size = size; buf.ptr = 0; return sq_compile(v, buf_lexfeed, &buf, sourcename, raiseerror); } void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx) { dest->Push(stack_get(src,idx)); } void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc,SQPRINTFUNCTION errfunc) { _ss(v)->_printfunc = printfunc; _ss(v)->_errorfunc = errfunc; } SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v) { return _ss(v)->_printfunc; } SQPRINTFUNCTION sq_geterrorfunc(HSQUIRRELVM v) { return _ss(v)->_errorfunc; } void *sq_malloc(SQUnsignedInteger size) { return SQ_MALLOC(size); } void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize) { return SQ_REALLOC(p,oldsize,newsize); } void sq_free(void *p,SQUnsignedInteger size) { SQ_FREE(p,size); } simutrans-124.5/src/squirrel/squirrel/sqarray.h000066400000000000000000000047121520675460500217260ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQARRAY_H_ #define _SQARRAY_H_ struct SQArray : public CHAINABLE_OBJ { private: SQArray(SQSharedState *ss,SQInteger nsize){_values.resize(nsize); INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);} ~SQArray() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } public: static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){ SQArray *newarray=(SQArray*)SQ_MALLOC(sizeof(SQArray)); new (newarray) SQArray(ss,nInitialSize); return newarray; } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); SQObjectType GetType() {return OT_ARRAY;} #endif void Finalize(){ _values.resize(0); } bool Get(const SQInteger nidx,SQObjectPtr &val) { if(nidx>=0 && nidx<(SQInteger)_values.size()){ SQObjectPtr &o = _values[nidx]; val = _realval(o); return true; } else return false; } bool Set(const SQInteger nidx,const SQObjectPtr &val) { if(nidx>=0 && nidx<(SQInteger)_values.size()){ _values[nidx]=val; return true; } else return false; } SQInteger Next(const SQObjectPtr &refpos,SQObjectPtr &outkey,SQObjectPtr &outval) { SQUnsignedInteger idx=TranslateIndex(refpos); while(idx<_values.size()){ //first found outkey=(SQInteger)idx; SQObjectPtr &o = _values[idx]; outval = _realval(o); //return idx for the next iteration return ++idx; } //nothing to iterate anymore return -1; } SQArray *Clone(){SQArray *anew=Create(_opt_ss(this),0); anew->_values.copy(_values); return anew; } SQInteger Size() const {return _values.size();} void Resize(SQInteger size) { SQObjectPtr _null; Resize(size,_null); } void Resize(SQInteger size,SQObjectPtr &fill) { _values.resize(size,fill); ShrinkIfNeeded(); } void Reserve(SQInteger size) { _values.reserve(size); } void Append(const SQObject &o){_values.push_back(o);} void Extend(const SQArray *a); SQObjectPtr &Top(){return _values.top();} void Pop(){_values.pop_back(); ShrinkIfNeeded(); } bool Insert(SQInteger idx,const SQObject &val){ if(idx < 0 || idx > (SQInteger)_values.size()) return false; _values.insert(idx,val); return true; } void ShrinkIfNeeded() { if(_values.size() <= _values.capacity()>>2) //shrink the array _values.shrinktofit(); } bool Remove(SQInteger idx){ if(idx < 0 || idx >= (SQInteger)_values.size()) return false; _values.remove(idx); ShrinkIfNeeded(); return true; } void Release() { sq_delete(this,SQArray); } SQObjectPtrVec _values; }; #endif simutrans-124.5/src/squirrel/squirrel/sqbaselib.cc000066400000000000000000001044551520675460500223540ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqstring.h" #include "sqtable.h" #include "sqarray.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqclass.h" #include #include #include static bool str2num(const SQChar *s,SQObjectPtr &res,SQInteger base) { SQChar *end; const SQChar *e = s; bool iseintbase = base > 13; //to fix error converting hexadecimals with e like 56f0791e bool isfloat = false; SQChar c; while((c = *e) != _SC('\0')) { if (c == _SC('.') || (!iseintbase && (c == _SC('E') || c == _SC('e')))) { //e and E is for scientific notation isfloat = true; break; } e++; } if(isfloat){ SQFloat r = SQFloat(scstrtod(s,&end)); if(s == end) return false; res = r; } else{ SQInteger r = SQInteger(scstrtol(s,&end,(int)base)); if(s == end) return false; res = r; } return true; } static SQInteger base_dummy(HSQUIRRELVM SQ_UNUSED_ARG(v)) { return 0; } #ifndef NO_GARBAGE_COLLECTOR static SQInteger base_collectgarbage(HSQUIRRELVM v) { sq_pushinteger(v, sq_collectgarbage(v)); return 1; } static SQInteger base_resurectureachable(HSQUIRRELVM v) { sq_resurrectunreachable(v); return 1; } #endif static SQInteger base_getroottable(HSQUIRRELVM v) { v->Push(v->_roottable); return 1; } static SQInteger base_getconsttable(HSQUIRRELVM v) { v->Push(_ss(v)->_consts); return 1; } static SQInteger base_setroottable(HSQUIRRELVM v) { SQObjectPtr o = v->_roottable; if(SQ_FAILED(sq_setroottable(v))) return SQ_ERROR; v->Push(o); return 1; } static SQInteger base_setconsttable(HSQUIRRELVM v) { SQObjectPtr o = _ss(v)->_consts; if(SQ_FAILED(sq_setconsttable(v))) return SQ_ERROR; v->Push(o); return 1; } static SQInteger base_seterrorhandler(HSQUIRRELVM v) { sq_seterrorhandler(v); return 0; } static SQInteger base_setdebughook(HSQUIRRELVM v) { sq_setdebughook(v); return 0; } static SQInteger base_enabledebuginfo(HSQUIRRELVM v) { SQObjectPtr &o=stack_get(v,2); sq_enabledebuginfo(v,SQVM::IsFalse(o)?SQFalse:SQTrue); return 0; } static SQInteger __getcallstackinfos(HSQUIRRELVM v,SQInteger level) { SQStackInfos si; SQInteger seq = 0; const SQChar *name = NULL; if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si))) { const SQChar *fn = _SC("unknown"); const SQChar *src = _SC("unknown"); if(si.funcname)fn = si.funcname; if(si.source)src = si.source; sq_newtable(v); sq_pushstring(v, _SC("func"), -1); sq_pushstring(v, fn, -1); sq_newslot(v, -3, SQFalse); sq_pushstring(v, _SC("src"), -1); sq_pushstring(v, src, -1); sq_newslot(v, -3, SQFalse); sq_pushstring(v, _SC("line"), -1); sq_pushinteger(v, si.line); sq_newslot(v, -3, SQFalse); sq_pushstring(v, _SC("locals"), -1); sq_newtable(v); seq=0; while ((name = sq_getlocal(v, level, seq))) { sq_pushstring(v, name, -1); sq_push(v, -2); sq_newslot(v, -4, SQFalse); sq_pop(v, 1); seq++; } sq_newslot(v, -3, SQFalse); return 1; } return 0; } static SQInteger base_getstackinfos(HSQUIRRELVM v) { SQInteger level; sq_getinteger(v, -1, &level); return __getcallstackinfos(v,level); } static SQInteger base_assert(HSQUIRRELVM v) { if(SQVM::IsFalse(stack_get(v,2))){ SQInteger top = sq_gettop(v); if (top>2 && SQ_SUCCEEDED(sq_tostring(v,3))) { const SQChar *str = 0; if (SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { return sq_throwerror(v, str); } } return sq_throwerror(v, _SC("assertion failed")); } return 0; } static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx,SQObjectPtr &o) { SQInteger top = sq_gettop(v); sidx=0; eidx=0; o=stack_get(v,1); if(top>1){ SQObjectPtr &start=stack_get(v,2); if(sq_type(start)!=OT_NULL && sq_isnumeric(start)){ sidx=tointeger(start); } } if(top>2){ SQObjectPtr &end=stack_get(v,3); if(sq_isnumeric(end)){ eidx=tointeger(end); } } else { eidx = sq_getsize(v,1); } return 1; } static SQInteger base_print(HSQUIRRELVM v) { const SQChar *str; if(SQ_SUCCEEDED(sq_tostring(v,2))) { if(SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,_SC("%s"),str); return 0; } } return SQ_ERROR; } static SQInteger base_error(HSQUIRRELVM v) { const SQChar *str; if(SQ_SUCCEEDED(sq_tostring(v,2))) { if(SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { if(_ss(v)->_errorfunc) _ss(v)->_errorfunc(v,_SC("%s"),str); return 0; } } return SQ_ERROR; } static SQInteger base_compilestring(HSQUIRRELVM v) { SQInteger nargs=sq_gettop(v); const SQChar *src=NULL,*name=_SC("unnamedbuffer"); SQInteger size; sq_getstring(v,2,&src); size=sq_getsize(v,2); if(nargs>2){ sq_getstring(v,3,&name); } if(SQ_SUCCEEDED(sq_compilebuffer(v,src,size,name,SQFalse))) return 1; else return SQ_ERROR; } static SQInteger base_newthread(HSQUIRRELVM v) { SQObjectPtr &func = stack_get(v,2); SQInteger stksize = (_closure(func)->_function->_stacksize << 1) +2; HSQUIRRELVM newv = sq_newthread(v, (stksize < MIN_STACK_OVERHEAD + 2)? MIN_STACK_OVERHEAD + 2 : stksize); sq_move(newv,v,-2); return 1; } static SQInteger base_suspend(HSQUIRRELVM v) { return sq_suspendvm(v); } static SQInteger base_array(HSQUIRRELVM v) { SQArray *a; SQObject &size = stack_get(v,2); if(sq_gettop(v) > 2) { a = SQArray::Create(_ss(v),0); a->Resize(tointeger(size),stack_get(v,3)); } else { a = SQArray::Create(_ss(v),tointeger(size)); } v->Push(a); return 1; } static SQInteger base_type(HSQUIRRELVM v) { SQObjectPtr &o = stack_get(v,2); v->Push(SQString::Create(_ss(v),GetTypeName(o),-1)); return 1; } static SQInteger base_callee(HSQUIRRELVM v) { if(v->_callsstacksize > 1) { v->Push(v->_callsstack[v->_callsstacksize - 2]._closure); return 1; } return sq_throwerror(v,_SC("no closure in the calls stack")); } static const SQRegFunction base_funcs[]={ //generic {_SC("seterrorhandler"),base_seterrorhandler,2, NULL}, {_SC("setdebughook"),base_setdebughook,2, NULL}, {_SC("enabledebuginfo"),base_enabledebuginfo,2, NULL}, {_SC("getstackinfos"),base_getstackinfos,2, _SC(".n")}, {_SC("getroottable"),base_getroottable,1, NULL}, {_SC("setroottable"),base_setroottable,2, NULL}, {_SC("getconsttable"),base_getconsttable,1, NULL}, {_SC("setconsttable"),base_setconsttable,2, NULL}, {_SC("assert"),base_assert,-2, NULL}, {_SC("print"),base_print,2, NULL}, {_SC("error"),base_error,2, NULL}, {_SC("compilestring"),base_compilestring,-2, _SC(".ss")}, {_SC("newthread"),base_newthread,2, _SC(".c")}, {_SC("suspend"),base_suspend,-1, NULL}, {_SC("array"),base_array,-2, _SC(".n")}, {_SC("type"),base_type,2, NULL}, {_SC("callee"),base_callee,0,NULL}, {_SC("dummy"),base_dummy,0,NULL}, #ifndef NO_GARBAGE_COLLECTOR {_SC("collectgarbage"),base_collectgarbage,0, NULL}, {_SC("resurrectunreachable"),base_resurectureachable,0, NULL}, #endif {NULL,(SQFUNCTION)0,0,NULL} }; void sq_base_register(HSQUIRRELVM v) { SQInteger i=0; sq_pushroottable(v); while(base_funcs[i].name!=0) { sq_pushstring(v,base_funcs[i].name,-1); sq_newclosure(v,base_funcs[i].f,0); sq_setnativeclosurename(v,-1,base_funcs[i].name); sq_setparamscheck(v,base_funcs[i].nparamscheck,base_funcs[i].typemask); sq_newslot(v,-3, SQFalse); i++; } sq_pushstring(v,_SC("_versionnumber_"),-1); sq_pushinteger(v,SQUIRREL_VERSION_NUMBER); sq_newslot(v,-3, SQFalse); sq_pushstring(v,_SC("_version_"),-1); sq_pushstring(v,SQUIRREL_VERSION,-1); sq_newslot(v,-3, SQFalse); sq_pushstring(v,_SC("_charsize_"),-1); sq_pushinteger(v,sizeof(SQChar)); sq_newslot(v,-3, SQFalse); sq_pushstring(v,_SC("_intsize_"),-1); sq_pushinteger(v,sizeof(SQInteger)); sq_newslot(v,-3, SQFalse); sq_pushstring(v,_SC("_floatsize_"),-1); sq_pushinteger(v,sizeof(SQFloat)); sq_newslot(v,-3, SQFalse); sq_pop(v,1); } static SQInteger default_delegate_len(HSQUIRRELVM v) { v->Push(SQInteger(sq_getsize(v,1))); return 1; } static SQInteger default_delegate_tofloat(HSQUIRRELVM v) { SQObjectPtr &o=stack_get(v,1); switch(sq_type(o)){ case OT_STRING:{ SQObjectPtr res; if(str2num(_stringval(o),res,10)){ v->Push(SQObjectPtr(tofloat(res))); break; }} return sq_throwerror(v, _SC("cannot convert the string")); break; case OT_INTEGER:case OT_FLOAT: v->Push(SQObjectPtr(tofloat(o))); break; case OT_BOOL: v->Push(SQObjectPtr((SQFloat)(_integer(o)?1:0))); break; default: v->PushNull(); break; } return 1; } static SQInteger default_delegate_tointeger(HSQUIRRELVM v) { SQObjectPtr &o=stack_get(v,1); SQInteger base = 10; if(sq_gettop(v) > 1) { sq_getinteger(v,2,&base); } switch(sq_type(o)){ case OT_STRING:{ SQObjectPtr res; if(str2num(_stringval(o),res,base)){ v->Push(SQObjectPtr(tointeger(res))); break; }} return sq_throwerror(v, _SC("cannot convert the string")); break; case OT_INTEGER:case OT_FLOAT: v->Push(SQObjectPtr(tointeger(o))); break; case OT_BOOL: v->Push(SQObjectPtr(_integer(o)?(SQInteger)1:(SQInteger)0)); break; default: v->PushNull(); break; } return 1; } static SQInteger default_delegate_tostring(HSQUIRRELVM v) { if(SQ_FAILED(sq_tostring(v,1))) return SQ_ERROR; return 1; } static SQInteger obj_delegate_weakref(HSQUIRRELVM v) { sq_weakref(v,1); return 1; } static SQInteger obj_clear(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_clear(v,-1)) ? 1 : SQ_ERROR; } static SQInteger number_delegate_tochar(HSQUIRRELVM v) { SQObject &o=stack_get(v,1); SQChar c = (SQChar)tointeger(o); v->Push(SQString::Create(_ss(v),(const SQChar *)&c,1)); return 1; } ///////////////////////////////////////////////////////////////// //TABLE DEFAULT DELEGATE static SQInteger table_rawdelete(HSQUIRRELVM v) { if(SQ_FAILED(sq_rawdeleteslot(v,1,SQTrue))) return SQ_ERROR; return 1; } static SQInteger container_rawexists(HSQUIRRELVM v) { if(SQ_SUCCEEDED(sq_rawget(v,-2))) { sq_pushbool(v,SQTrue); return 1; } sq_pushbool(v,SQFalse); return 1; } static SQInteger container_rawset(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_rawset(v,-3)) ? 1 : SQ_ERROR; } static SQInteger container_rawget(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_rawget(v,-2))?1:SQ_ERROR; } static SQInteger table_setdelegate(HSQUIRRELVM v) { if(SQ_FAILED(sq_setdelegate(v,-2))) return SQ_ERROR; sq_push(v,-1); // -1 because sq_setdelegate pops 1 return 1; } static SQInteger table_getdelegate(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_getdelegate(v,-1))?1:SQ_ERROR; } static SQInteger table_filter(HSQUIRRELVM v) { SQObject &o = stack_get(v,1); SQTable *tbl = _table(o); SQObjectPtr ret = SQTable::Create(_ss(v),0); SQObjectPtr itr, key, val; SQInteger nitr; while((nitr = tbl->Next(false, itr, key, val)) != -1) { itr = (SQInteger)nitr; v->Push(o); v->Push(key); v->Push(val); if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { return SQ_ERROR; } if(!SQVM::IsFalse(v->GetUp(-1))) { _table(ret)->NewSlot(key, val); } v->Pop(); } v->Push(ret); return 1; } #define TABLE_TO_ARRAY_FUNC(_funcname_,_valname_) static SQInteger _funcname_(HSQUIRRELVM v) \ { \ SQObject &o = stack_get(v, 1); \ SQTable *t = _table(o); \ SQObjectPtr itr, key, val; \ SQObjectPtr _null; \ SQInteger nitr, n = 0; \ SQInteger nitems = t->CountUsed(); \ SQArray *a = SQArray::Create(_ss(v), nitems); \ a->Resize(nitems, _null); \ if (nitems) { \ while ((nitr = t->Next(false, itr, key, val)) != -1) { \ itr = (SQInteger)nitr; \ a->Set(n, _valname_); \ n++; \ } \ } \ v->Push(a); \ return 1; \ } TABLE_TO_ARRAY_FUNC(table_keys, key) TABLE_TO_ARRAY_FUNC(table_values, val) const SQRegFunction SQSharedState::_table_default_delegate_funcz[]={ {_SC("len"),default_delegate_len,1, _SC("t")}, {_SC("rawget"),container_rawget,2, _SC("t")}, {_SC("rawset"),container_rawset,3, _SC("t")}, {_SC("rawdelete"),table_rawdelete,2, _SC("t")}, {_SC("rawin"),container_rawexists,2, _SC("t")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {_SC("clear"),obj_clear,1, _SC(".")}, {_SC("setdelegate"),table_setdelegate,2, _SC(".t|o")}, {_SC("getdelegate"),table_getdelegate,1, _SC(".")}, {_SC("filter"),table_filter,2, _SC("tc")}, {_SC("keys"),table_keys,1, _SC("t") }, {_SC("values"),table_values,1, _SC("t") }, {NULL,(SQFUNCTION)0,0,NULL} }; //ARRAY DEFAULT DELEGATE/////////////////////////////////////// static SQInteger array_append(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_arrayappend(v,-2)) ? 1 : SQ_ERROR; } static SQInteger array_extend(HSQUIRRELVM v) { _array(stack_get(v,1))->Extend(_array(stack_get(v,2))); sq_pop(v,1); return 1; } static SQInteger array_reverse(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_arrayreverse(v,-1)) ? 1 : SQ_ERROR; } static SQInteger array_pop(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_arraypop(v,1,SQTrue))?1:SQ_ERROR; } static SQInteger array_top(HSQUIRRELVM v) { SQObject &o=stack_get(v,1); if(_array(o)->Size()>0){ v->Push(_array(o)->Top()); return 1; } else return sq_throwerror(v,_SC("top() on a empty array")); } static SQInteger array_insert(HSQUIRRELVM v) { SQObject &o=stack_get(v,1); SQObject &idx=stack_get(v,2); SQObject &val=stack_get(v,3); if(!_array(o)->Insert(tointeger(idx),val)) return sq_throwerror(v,_SC("index out of range")); sq_pop(v,2); return 1; } static SQInteger array_remove(HSQUIRRELVM v) { SQObject &o = stack_get(v, 1); SQObject &idx = stack_get(v, 2); if(!sq_isnumeric(idx)) return sq_throwerror(v, _SC("wrong type")); SQObjectPtr val; if(_array(o)->Get(tointeger(idx), val)) { _array(o)->Remove(tointeger(idx)); v->Push(val); return 1; } return sq_throwerror(v, _SC("idx out of range")); } static SQInteger array_resize(HSQUIRRELVM v) { SQObject &o = stack_get(v, 1); SQObject &nsize = stack_get(v, 2); SQObjectPtr fill; if(sq_isnumeric(nsize)) { SQInteger sz = tointeger(nsize); if (sz<0) return sq_throwerror(v, _SC("resizing to negative length")); if(sq_gettop(v) > 2) fill = stack_get(v, 3); _array(o)->Resize(sz,fill); sq_settop(v, 1); return 1; } return sq_throwerror(v, _SC("size must be a number")); } static SQInteger __map_array(SQArray *dest,SQArray *src,HSQUIRRELVM v) { SQObjectPtr temp; SQInteger size = src->Size(); SQObject &closure = stack_get(v, 2); v->Push(closure); SQInteger nArgs = 0; if(sq_type(closure) == OT_CLOSURE) { nArgs = _closure(closure)->_function->_nparameters; } else if (sq_type(closure) == OT_NATIVECLOSURE) { SQInteger nParamsCheck = _nativeclosure(closure)->_nparamscheck; if (nParamsCheck > 0) nArgs = nParamsCheck; else // push all params when there is no check or only minimal count set nArgs = 4; } for(SQInteger n = 0; n < size; n++) { src->Get(n,temp); v->Push(src); v->Push(temp); if (nArgs >= 3) v->Push(SQObjectPtr(n)); if (nArgs >= 4) v->Push(src); if(SQ_FAILED(sq_call(v,nArgs,SQTrue,SQFalse))) { return SQ_ERROR; } dest->Set(n,v->GetUp(-1)); v->Pop(); } v->Pop(); return 0; } static SQInteger array_map(HSQUIRRELVM v) { SQObject &o = stack_get(v,1); SQInteger size = _array(o)->Size(); SQObjectPtr ret = SQArray::Create(_ss(v),size); if(SQ_FAILED(__map_array(_array(ret),_array(o),v))) return SQ_ERROR; v->Push(ret); return 1; } static SQInteger array_apply(HSQUIRRELVM v) { SQObject &o = stack_get(v,1); if(SQ_FAILED(__map_array(_array(o),_array(o),v))) return SQ_ERROR; sq_pop(v,1); return 1; } static SQInteger array_reduce(HSQUIRRELVM v) { SQObject &o = stack_get(v,1); SQArray *a = _array(o); SQInteger size = a->Size(); SQObjectPtr res; SQInteger iterStart; if (sq_gettop(v)>2) { res = stack_get(v,3); iterStart = 0; } else if (size==0) { return 0; } else { a->Get(0,res); iterStart = 1; } if (size > iterStart) { SQObjectPtr other; v->Push(stack_get(v,2)); for (SQInteger n = iterStart; n < size; n++) { a->Get(n,other); v->Push(o); v->Push(res); v->Push(other); if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { return SQ_ERROR; } res = v->GetUp(-1); v->Pop(); } v->Pop(); } v->Push(res); return 1; } static SQInteger array_filter(HSQUIRRELVM v) { SQObject &o = stack_get(v,1); SQArray *a = _array(o); SQObjectPtr ret = SQArray::Create(_ss(v),0); SQInteger size = a->Size(); SQObjectPtr val; for(SQInteger n = 0; n < size; n++) { a->Get(n,val); v->Push(o); v->Push(n); v->Push(val); if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { return SQ_ERROR; } if(!SQVM::IsFalse(v->GetUp(-1))) { _array(ret)->Append(val); } v->Pop(); } v->Push(ret); return 1; } static SQInteger array_find(HSQUIRRELVM v) { SQObject &o = stack_get(v,1); SQObjectPtr &val = stack_get(v,2); SQArray *a = _array(o); SQInteger size = a->Size(); SQObjectPtr temp; for(SQInteger n = 0; n < size; n++) { bool res = false; a->Get(n,temp); if(SQVM::IsEqual(temp,val,res) && res) { v->Push(n); return 1; } } return 0; } static bool _sort_compare(HSQUIRRELVM v, SQArray *arr, SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret) { if(func < 0) { if(!v->ObjCmp(a,b,ret)) return false; } else { SQInteger top = sq_gettop(v); sq_push(v, func); sq_pushroottable(v); v->Push(a); v->Push(b); SQObjectPtr *valptr = arr->_values._vals; SQUnsignedInteger precallsize = arr->_values.size(); if(SQ_FAILED(sq_call(v, 3, SQTrue, SQFalse))) { if(!sq_isstring( v->_lasterror)) v->Raise_Error(_SC("compare func failed")); return false; } if(SQ_FAILED(sq_getinteger(v, -1, &ret))) { v->Raise_Error(_SC("numeric value expected as return value of the compare function")); return false; } if (precallsize != arr->_values.size() || valptr != arr->_values._vals) { v->Raise_Error(_SC("array resized during sort operation")); return false; } sq_settop(v, top); return true; } return true; } static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bottom, SQInteger func) { SQInteger maxChild; SQInteger done = 0; SQInteger ret; SQInteger root2; while (((root2 = root * 2) <= bottom) && (!done)) { if (root2 == bottom) { maxChild = root2; } else { if(!_sort_compare(v,arr,arr->_values[root2],arr->_values[root2 + 1],func,ret)) return false; if (ret > 0) { maxChild = root2; } else { maxChild = root2 + 1; } } if(!_sort_compare(v,arr,arr->_values[root],arr->_values[maxChild],func,ret)) return false; if (ret < 0) { if (root == maxChild) { v->Raise_Error(_SC("inconsistent compare function")); return false; // We'd be swapping ourselve. The compare function is incorrect } _Swap(arr->_values[root],arr->_values[maxChild]); root = maxChild; } else { done = 1; } } return true; } static bool _hsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger SQ_UNUSED_ARG(l), SQInteger SQ_UNUSED_ARG(r),SQInteger func) { SQArray *a = _array(arr); SQInteger i; SQInteger array_size = a->Size(); for (i = (array_size / 2); i >= 0; i--) { if(!_hsort_sift_down(v,a, i, array_size - 1,func)) return false; } for (i = array_size-1; i >= 1; i--) { _Swap(a->_values[0],a->_values[i]); if(!_hsort_sift_down(v,a, 0, i-1,func)) return false; } return true; } static SQInteger array_sort(HSQUIRRELVM v) { SQInteger func = -1; SQObjectPtr &o = stack_get(v,1); if(_array(o)->Size() > 1) { if(sq_gettop(v) == 2) func = 2; if(!_hsort(v, o, 0, _array(o)->Size()-1, func)) return SQ_ERROR; } sq_settop(v,1); return 1; } static SQInteger array_slice(HSQUIRRELVM v) { SQInteger sidx,eidx; SQObjectPtr o; if(get_slice_params(v,sidx,eidx,o)==-1)return -1; SQInteger alen = _array(o)->Size(); if(sidx < 0)sidx = alen + sidx; if(eidx < 0)eidx = alen + eidx; if(eidx < sidx)return sq_throwerror(v,_SC("wrong indexes")); if(eidx > alen || sidx < 0)return sq_throwerror(v, _SC("slice out of range")); SQArray *arr=SQArray::Create(_ss(v),eidx-sidx); SQObjectPtr t; SQInteger count=0; for(SQInteger i=sidx;iGet(i,t); arr->Set(count++,t); } v->Push(arr); return 1; } const SQRegFunction SQSharedState::_array_default_delegate_funcz[]={ {_SC("len"),default_delegate_len,1, _SC("a")}, {_SC("append"),array_append,2, _SC("a")}, {_SC("extend"),array_extend,2, _SC("aa")}, {_SC("push"),array_append,2, _SC("a")}, {_SC("pop"),array_pop,1, _SC("a")}, {_SC("top"),array_top,1, _SC("a")}, {_SC("insert"),array_insert,3, _SC("an")}, {_SC("remove"),array_remove,2, _SC("an")}, {_SC("resize"),array_resize,-2, _SC("an")}, {_SC("reverse"),array_reverse,1, _SC("a")}, {_SC("sort"),array_sort,-1, _SC("ac")}, {_SC("slice"),array_slice,-1, _SC("ann")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {_SC("clear"),obj_clear,1, _SC(".")}, {_SC("map"),array_map,2, _SC("ac")}, {_SC("apply"),array_apply,2, _SC("ac")}, {_SC("reduce"),array_reduce,-2, _SC("ac.")}, {_SC("filter"),array_filter,2, _SC("ac")}, {_SC("find"),array_find,2, _SC("a.")}, {NULL,(SQFUNCTION)0,0,NULL} }; //STRING DEFAULT DELEGATE////////////////////////// static SQInteger string_slice(HSQUIRRELVM v) { SQInteger sidx,eidx; SQObjectPtr o; if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1; SQInteger slen = _string(o)->_len; if(sidx < 0)sidx = slen + sidx; if(eidx < 0)eidx = slen + eidx; if(eidx < sidx) return sq_throwerror(v,_SC("wrong indexes")); if(eidx > slen || sidx < 0) return sq_throwerror(v, _SC("slice out of range")); v->Push(SQString::Create(_ss(v),&_stringval(o)[sidx],eidx-sidx)); return 1; } static SQInteger string_find(HSQUIRRELVM v) { SQInteger top,start_idx=0; const SQChar *str,*substr,*ret; if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){ if(top>2)sq_getinteger(v,3,&start_idx); if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){ ret=scstrstr(&str[start_idx],substr); if(ret){ sq_pushinteger(v,(SQInteger)(ret-str)); return 1; } } return 0; } return sq_throwerror(v,_SC("invalid param")); } #define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \ {\ SQInteger sidx,eidx; \ SQObjectPtr str; \ if(SQ_FAILED(get_slice_params(v,sidx,eidx,str)))return -1; \ SQInteger slen = _string(str)->_len; \ if(sidx < 0)sidx = slen + sidx; \ if(eidx < 0)eidx = slen + eidx; \ if(eidx < sidx) return sq_throwerror(v,_SC("wrong indexes")); \ if(eidx > slen || sidx < 0) return sq_throwerror(v,_SC("slice out of range")); \ SQInteger len=_string(str)->_len; \ const SQChar *sthis=_stringval(str); \ SQChar *snew=(_ss(v)->GetScratchPad(sq_rsl(len))); \ memcpy(snew,sthis,sq_rsl(len));\ for(SQInteger i=sidx;iPush(SQString::Create(_ss(v),snew,len)); \ return 1; \ } static char toalnum(char c) { return isalnum(c) && c >=0 ? c : '_'; } STRING_TOFUNCZ(tolower) STRING_TOFUNCZ(toupper) STRING_TOFUNCZ(toalnum) const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ {_SC("len"),default_delegate_len,1, _SC("s")}, {_SC("tointeger"),default_delegate_tointeger,-1, _SC("sn")}, {_SC("tofloat"),default_delegate_tofloat,1, _SC("s")}, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {_SC("slice"),string_slice,-1, _SC("s n n")}, {_SC("find"),string_find,-2, _SC("s s n")}, {_SC("tolower"),string_tolower,-1, _SC("s n n")}, {_SC("toupper"),string_toupper,-1, _SC("s n n")}, {_SC("toalnum"),string_toalnum,1, _SC("s")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {NULL,(SQFUNCTION)0,0,NULL} }; //INTEGER DEFAULT DELEGATE////////////////////////// const SQRegFunction SQSharedState::_number_default_delegate_funcz[]={ {_SC("tointeger"),default_delegate_tointeger,1, _SC("n|b")}, {_SC("tofloat"),default_delegate_tofloat,1, _SC("n|b")}, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {_SC("tochar"),number_delegate_tochar,1, _SC("n|b")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {NULL,(SQFUNCTION)0,0,NULL} }; //CLOSURE DEFAULT DELEGATE////////////////////////// static SQInteger closure_pcall(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQFalse))?1:SQ_ERROR; } static SQInteger closure_call(HSQUIRRELVM v) { /* has to wait until sq_tailcall is enabled SQObjectPtr &c = stack_get(v, -1); if (sq_type(c) == OT_CLOSURE && (_closure(c)->_function->_bgenerator == false)) { return sq_tailcall(v, sq_gettop(v) - 1); } */ return SQ_SUCCEEDED(sq_call(v, sq_gettop(v) - 1, SQTrue, SQTrue)) ? 1 : SQ_ERROR; } static SQInteger _closure_acall(HSQUIRRELVM v,SQBool raiseerror) { SQArray *aparams=_array(stack_get(v,2)); SQInteger nparams=aparams->Size(); v->Push(stack_get(v,1)); for(SQInteger i=0;iPush(aparams->_values[i]); return SQ_SUCCEEDED(sq_call(v,nparams,SQTrue,raiseerror))?1:SQ_ERROR; } static SQInteger closure_acall(HSQUIRRELVM v) { return _closure_acall(v,SQTrue); } static SQInteger closure_pacall(HSQUIRRELVM v) { return _closure_acall(v,SQFalse); } static SQInteger closure_bindenv(HSQUIRRELVM v) { if(SQ_FAILED(sq_bindenv(v,1))) return SQ_ERROR; return 1; } static SQInteger closure_getroot(HSQUIRRELVM v) { if(SQ_FAILED(sq_getclosureroot(v,-1))) return SQ_ERROR; return 1; } static SQInteger closure_setroot(HSQUIRRELVM v) { if(SQ_FAILED(sq_setclosureroot(v,-2))) return SQ_ERROR; return 1; } static SQInteger closure_getinfos(HSQUIRRELVM v) { SQObject o = stack_get(v,1); SQTable *res = SQTable::Create(_ss(v),4); if(sq_type(o) == OT_CLOSURE) { SQFunctionProto *f = _closure(o)->_function; SQInteger nparams = f->_nparameters + (f->_varparams?1:0); SQObjectPtr params = SQArray::Create(_ss(v),nparams); SQObjectPtr defparams = SQArray::Create(_ss(v),f->_ndefaultparams); for(SQInteger n = 0; n_nparameters; n++) { _array(params)->Set((SQInteger)n,f->_parameters[n]); } for(SQInteger j = 0; j_ndefaultparams; j++) { _array(defparams)->Set((SQInteger)j,_closure(o)->_defaultparams[j]); } if(f->_varparams) { _array(params)->Set(nparams-1,SQString::Create(_ss(v),_SC("..."),-1)); } res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),false); res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),f->_name); res->NewSlot(SQString::Create(_ss(v),_SC("src"),-1),f->_sourcename); res->NewSlot(SQString::Create(_ss(v),_SC("parameters"),-1),params); res->NewSlot(SQString::Create(_ss(v),_SC("varargs"),-1),f->_varparams); res->NewSlot(SQString::Create(_ss(v),_SC("defparams"),-1),defparams); } else { //OT_NATIVECLOSURE SQNativeClosure *nc = _nativeclosure(o); res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),true); res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),nc->_name); res->NewSlot(SQString::Create(_ss(v),_SC("paramscheck"),-1),nc->_nparamscheck); SQObjectPtr typecheck; if(nc->_typecheck.size() > 0) { typecheck = SQArray::Create(_ss(v), nc->_typecheck.size()); for(SQUnsignedInteger n = 0; n_typecheck.size(); n++) { _array(typecheck)->Set((SQInteger)n,nc->_typecheck[n]); } } res->NewSlot(SQString::Create(_ss(v),_SC("typecheck"),-1),typecheck); } v->Push(res); return 1; } const SQRegFunction SQSharedState::_closure_default_delegate_funcz[]={ {_SC("call"),closure_call,-1, _SC("c")}, {_SC("pcall"),closure_pcall,-1, _SC("c")}, {_SC("acall"),closure_acall,2, _SC("ca")}, {_SC("pacall"),closure_pacall,2, _SC("ca")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {_SC("bindenv"),closure_bindenv,2, _SC("c x|y|t")}, {_SC("getinfos"),closure_getinfos,1, _SC("c")}, {_SC("getroot"),closure_getroot,1, _SC("c")}, {_SC("setroot"),closure_setroot,2, _SC("ct")}, {NULL,(SQFUNCTION)0,0,NULL} }; //GENERATOR DEFAULT DELEGATE static SQInteger generator_getstatus(HSQUIRRELVM v) { SQObject &o=stack_get(v,1); switch(_generator(o)->_state){ case SQGenerator::eSuspended:v->Push(SQString::Create(_ss(v),_SC("suspended")));break; case SQGenerator::eRunning:v->Push(SQString::Create(_ss(v),_SC("running")));break; case SQGenerator::eDead:v->Push(SQString::Create(_ss(v),_SC("dead")));break; } return 1; } const SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={ {_SC("getstatus"),generator_getstatus,1, _SC("g")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {NULL,(SQFUNCTION)0,0,NULL} }; //THREAD DEFAULT DELEGATE static SQInteger thread_call(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); if(sq_type(o) == OT_THREAD) { SQInteger nparams = sq_gettop(v); _thread(o)->Push(_thread(o)->_roottable); for(SQInteger i = 2; i<(nparams+1); i++) sq_move(_thread(o),v,i); if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQTrue))) { sq_move(v,_thread(o),-1); sq_pop(_thread(o),1); return 1; } v->_lasterror = _thread(o)->_lasterror; return SQ_ERROR; } return sq_throwerror(v,_SC("wrong parameter")); } static SQInteger thread_wakeup(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger state = sq_getvmstate(thread); if(state != SQ_VMSTATE_SUSPENDED) { switch(state) { case SQ_VMSTATE_IDLE: return sq_throwerror(v,_SC("cannot wakeup a idle thread")); break; case SQ_VMSTATE_RUNNING: return sq_throwerror(v,_SC("cannot wakeup a running thread")); break; } } SQInteger wakeupret = sq_gettop(v)>1?SQTrue:SQFalse; if(wakeupret) { sq_move(thread,v,2); } if(SQ_SUCCEEDED(sq_wakeupvm(thread,wakeupret,SQTrue,SQTrue,SQFalse))) { sq_move(v,thread,-1); sq_pop(thread,1); //pop retval if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) { sq_settop(thread,1); //pop roottable } return 1; } sq_settop(thread,1); v->_lasterror = thread->_lasterror; return SQ_ERROR; } return sq_throwerror(v,_SC("wrong parameter")); } static SQInteger thread_wakeupthrow(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger state = sq_getvmstate(thread); if(state != SQ_VMSTATE_SUSPENDED) { switch(state) { case SQ_VMSTATE_IDLE: return sq_throwerror(v,_SC("cannot wakeup a idle thread")); break; case SQ_VMSTATE_RUNNING: return sq_throwerror(v,_SC("cannot wakeup a running thread")); break; } } sq_move(thread,v,2); sq_throwobject(thread); SQBool rethrow_error = SQTrue; if(sq_gettop(v) > 2) { sq_getbool(v,3,&rethrow_error); } if(SQ_SUCCEEDED(sq_wakeupvm(thread,SQFalse,SQTrue,SQTrue,SQTrue))) { sq_move(v,thread,-1); sq_pop(thread,1); //pop retval if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) { sq_settop(thread,1); //pop roottable } return 1; } sq_settop(thread,1); if(rethrow_error) { v->_lasterror = thread->_lasterror; return SQ_ERROR; } return SQ_OK; } return sq_throwerror(v,_SC("wrong parameter")); } static SQInteger thread_getstatus(HSQUIRRELVM v) { SQObjectPtr &o = stack_get(v,1); switch(sq_getvmstate(_thread(o))) { case SQ_VMSTATE_IDLE: sq_pushstring(v,_SC("idle"),-1); break; case SQ_VMSTATE_RUNNING: sq_pushstring(v,_SC("running"),-1); break; case SQ_VMSTATE_SUSPENDED: sq_pushstring(v,_SC("suspended"),-1); break; default: return sq_throwerror(v,_SC("internal VM error")); } return 1; } static SQInteger thread_getstackinfos(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger threadtop = sq_gettop(thread); SQInteger level; sq_getinteger(v,-1,&level); SQRESULT res = __getcallstackinfos(thread,level); if(SQ_FAILED(res)) { sq_settop(thread,threadtop); if(sq_type(thread->_lasterror) == OT_STRING) { sq_throwerror(v,_stringval(thread->_lasterror)); } else { sq_throwerror(v,_SC("unknown error")); } } if(res > 0) { //some result sq_move(v,thread,-1); sq_settop(thread,threadtop); return 1; } //no result sq_settop(thread,threadtop); return 0; } return sq_throwerror(v,_SC("wrong parameter")); } const SQRegFunction SQSharedState::_thread_default_delegate_funcz[] = { {_SC("call"), thread_call, -1, _SC("v")}, {_SC("wakeup"), thread_wakeup, -1, _SC("v")}, {_SC("wakeupthrow"), thread_wakeupthrow, -2, _SC("v.b")}, {_SC("getstatus"), thread_getstatus, 1, _SC("v")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {_SC("getstackinfos"),thread_getstackinfos,2, _SC("vn")}, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {NULL,(SQFUNCTION)0,0,NULL} }; static SQInteger class_getattributes(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_getattributes(v,-2))?1:SQ_ERROR; } static SQInteger class_setattributes(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_setattributes(v,-3))?1:SQ_ERROR; } static SQInteger class_instance(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_createinstance(v,-1))?1:SQ_ERROR; } static SQInteger class_getbase(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_getbase(v,-1))?1:SQ_ERROR; } static SQInteger class_newmember(HSQUIRRELVM v) { SQInteger top = sq_gettop(v); SQBool bstatic = SQFalse; if(top == 5) { sq_tobool(v,-1,&bstatic); sq_pop(v,1); } if(top < 4) { sq_pushnull(v); } return SQ_SUCCEEDED(sq_newmember(v,-4,bstatic))?1:SQ_ERROR; } static SQInteger class_rawnewmember(HSQUIRRELVM v) { SQInteger top = sq_gettop(v); SQBool bstatic = SQFalse; if(top == 5) { sq_tobool(v,-1,&bstatic); sq_pop(v,1); } if(top < 4) { sq_pushnull(v); } return SQ_SUCCEEDED(sq_rawnewmember(v,-4,bstatic))?1:SQ_ERROR; } const SQRegFunction SQSharedState::_class_default_delegate_funcz[] = { {_SC("getattributes"), class_getattributes, 2, _SC("y.")}, {_SC("setattributes"), class_setattributes, 3, _SC("y..")}, {_SC("rawget"),container_rawget,2, _SC("y")}, {_SC("rawset"),container_rawset,3, _SC("y")}, {_SC("rawin"),container_rawexists,2, _SC("y")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {_SC("instance"),class_instance,1, _SC("y")}, {_SC("getbase"),class_getbase,1, _SC("y")}, {_SC("newmember"),class_newmember,-3, _SC("y")}, {_SC("rawnewmember"),class_rawnewmember,-3, _SC("y")}, {NULL,(SQFUNCTION)0,0,NULL} }; static SQInteger instance_getclass(HSQUIRRELVM v) { if(SQ_SUCCEEDED(sq_getclass(v,1))) return 1; return SQ_ERROR; } const SQRegFunction SQSharedState::_instance_default_delegate_funcz[] = { {_SC("getclass"), instance_getclass, 1, _SC("x")}, {_SC("rawget"),container_rawget,2, _SC("x")}, {_SC("rawset"),container_rawset,3, _SC("x")}, {_SC("rawin"),container_rawexists,2, _SC("x")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {NULL,(SQFUNCTION)0,0,NULL} }; static SQInteger weakref_ref(HSQUIRRELVM v) { if(SQ_FAILED(sq_getweakrefval(v,1))) return SQ_ERROR; return 1; } const SQRegFunction SQSharedState::_weakref_default_delegate_funcz[] = { {_SC("ref"),weakref_ref,1, _SC("r")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, {NULL,(SQFUNCTION)0,0,NULL} }; simutrans-124.5/src/squirrel/squirrel/sqclass.cc000066400000000000000000000121611520675460500220500ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqtable.h" #include "sqclass.h" #include "sqfuncproto.h" #include "sqclosure.h" SQClass::SQClass(SQSharedState *ss,SQClass *base) { _base = base; _typetag = 0; _hook = NULL; _udsize = 0; _locked = false; _constructoridx = -1; if(_base) { _constructoridx = _base->_constructoridx; _udsize = _base->_udsize; _defaultvalues.copy(base->_defaultvalues); _methods.copy(base->_methods); _COPY_VECTOR(_metamethods,base->_metamethods,MT_LAST); __ObjAddRef(_base); } _members = base?base->_members->Clone() : SQTable::Create(ss,0); __ObjAddRef(_members); INIT_CHAIN(); ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); } void SQClass::Finalize() { _attributes.Null(); _NULL_SQOBJECT_VECTOR(_defaultvalues,_defaultvalues.size()); _methods.resize(0); _NULL_SQOBJECT_VECTOR(_metamethods,MT_LAST); __ObjRelease(_members); if(_base) { __ObjRelease(_base); } } SQClass::~SQClass() { REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); Finalize(); } bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) { SQObjectPtr temp; bool belongs_to_static_table = sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE || bstatic; if(_locked && !belongs_to_static_table) return false; //the class already has an instance so cannot be modified if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value { _defaultvalues[_member_idx(temp)].val = val; return true; } if (_members->CountUsed() >= MEMBER_MAX_COUNT) { return false; } if(belongs_to_static_table) { SQInteger mmidx; if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) && (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) { _metamethods[mmidx] = val; } else { SQObjectPtr theval = val; if(_base && sq_type(val) == OT_CLOSURE) { theval = _closure(val)->Clone(); _closure(theval)->_base = _base; __ObjAddRef(_base); //ref for the closure } if(sq_type(temp) == OT_NULL) { bool isconstructor; SQVM::IsEqual(ss->_constructoridx, key, isconstructor); if(isconstructor) { _constructoridx = (SQInteger)_methods.size(); } SQClassMember m; m.val = theval; _members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size()))); _methods.push_back(m); } else { _methods[_member_idx(temp)].val = theval; } } return true; } SQClassMember m; m.val = val; _members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size()))); _defaultvalues.push_back(m); return true; } SQInstance *SQClass::CreateInstance() { if(!_locked) Lock(); return SQInstance::Create(_opt_ss(this),this); } SQInteger SQClass::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) { SQObjectPtr oval; SQInteger idx = _members->Next(false,refpos,outkey,oval); if(idx != -1) { if(_ismethod(oval)) { outval = _methods[_member_idx(oval)].val; } else { SQObjectPtr &o = _defaultvalues[_member_idx(oval)].val; outval = _realval(o); } } return idx; } bool SQClass::SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val) { SQObjectPtr idx; if(_members->Get(key,idx)) { if(_isfield(idx)) _defaultvalues[_member_idx(idx)].attrs = val; else _methods[_member_idx(idx)].attrs = val; return true; } return false; } bool SQClass::GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval) { SQObjectPtr idx; if(_members->Get(key,idx)) { outval = (_isfield(idx)?_defaultvalues[_member_idx(idx)].attrs:_methods[_member_idx(idx)].attrs); return true; } return false; } /////////////////////////////////////////////////////////////////////// void SQInstance::Init(SQSharedState *ss) { _userpointer = NULL; _hook = NULL; __ObjAddRef(_class); _delegate = _class->_members; INIT_CHAIN(); ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); } SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize) { _memsize = memsize; _class = c; SQUnsignedInteger nvalues = _class->_defaultvalues.size(); for(SQUnsignedInteger n = 0; n < nvalues; n++) { new (&_values[n]) SQObjectPtr(_class->_defaultvalues[n].val); } Init(ss); } SQInstance::SQInstance(SQSharedState *ss, SQInstance *i, SQInteger memsize) { _memsize = memsize; _class = i->_class; SQUnsignedInteger nvalues = _class->_defaultvalues.size(); for(SQUnsignedInteger n = 0; n < nvalues; n++) { new (&_values[n]) SQObjectPtr(i->_values[n]); } Init(ss); } void SQInstance::Finalize() { SQUnsignedInteger nvalues = _class->_defaultvalues.size(); __ObjRelease(_class); _NULL_SQOBJECT_VECTOR(_values,nvalues); } SQInstance::~SQInstance() { REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); if(_class){ Finalize(); } //if _class is null it was already finalized by the GC } bool SQInstance::GetMetaMethod(SQVM* SQ_UNUSED_ARG(v),SQMetaMethod mm,SQObjectPtr &res) { if(sq_type(_class->_metamethods[mm]) != OT_NULL) { res = _class->_metamethods[mm]; return true; } return false; } bool SQInstance::InstanceOf(SQClass *trg) { SQClass *parent = _class; while(parent != NULL) { if(parent == trg) return true; parent = parent->_base; } return false; } simutrans-124.5/src/squirrel/squirrel/sqclass.h000066400000000000000000000105451520675460500217160ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQCLASS_H_ #define _SQCLASS_H_ struct SQInstance; struct SQClassMember { SQObjectPtr val; SQObjectPtr attrs; void Null() { val.Null(); attrs.Null(); } }; typedef sqvector SQClassMemberVec; #define MEMBER_TYPE_METHOD 0x01000000 #define MEMBER_TYPE_FIELD 0x02000000 #define MEMBER_MAX_COUNT 0x00FFFFFF #define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD) #define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD) #define _make_method_idx(i) ((SQInteger)(MEMBER_TYPE_METHOD|i)) #define _make_field_idx(i) ((SQInteger)(MEMBER_TYPE_FIELD|i)) #define _member_type(o) (_integer(o)&0xFF000000) #define _member_idx(o) (_integer(o)&0x00FFFFFF) struct SQClass : public CHAINABLE_OBJ { SQClass(SQSharedState *ss,SQClass *base); public: static SQClass* Create(SQSharedState *ss,SQClass *base) { SQClass *newclass = (SQClass *)SQ_MALLOC(sizeof(SQClass)); new (newclass) SQClass(ss, base); return newclass; } ~SQClass(); bool NewSlot(SQSharedState *ss, const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic); bool Get(const SQObjectPtr &key,SQObjectPtr &val) { if(_members->Get(key,val)) { if(_isfield(val)) { SQObjectPtr &o = _defaultvalues[_member_idx(val)].val; val = _realval(o); } else { val = _methods[_member_idx(val)].val; } return true; } return false; } bool GetConstructor(SQObjectPtr &ctor) { if(_constructoridx != -1) { ctor = _methods[_constructoridx].val; return true; } return false; } bool SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val); bool GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval); void Lock() { _locked = true; if(_base) _base->Lock(); } void Release() { if (_hook) { _hook(_typetag,0);} sq_delete(this, SQClass); } void Finalize(); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable ** ); SQObjectType GetType() {return OT_CLASS;} #endif SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); SQInstance *CreateInstance(); SQTable *_members; SQClass *_base; SQClassMemberVec _defaultvalues; SQClassMemberVec _methods; SQObjectPtr _metamethods[MT_LAST]; SQObjectPtr _attributes; SQUserPointer _typetag; SQRELEASEHOOK _hook; bool _locked; SQInteger _constructoridx; SQInteger _udsize; }; #define calcinstancesize(_theclass_) \ (_theclass_->_udsize + sq_aligning(sizeof(SQInstance) + (sizeof(SQObjectPtr)*(_theclass_->_defaultvalues.size()>0?_theclass_->_defaultvalues.size()-1:0)))) struct SQInstance : public SQDelegable { void Init(SQSharedState *ss); SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize); SQInstance(SQSharedState *ss, SQInstance *c, SQInteger memsize); public: static SQInstance* Create(SQSharedState *ss,SQClass *theclass) { SQInteger size = calcinstancesize(theclass); SQInstance *newinst = (SQInstance *)SQ_MALLOC(size); new (newinst) SQInstance(ss, theclass,size); if(theclass->_udsize) { newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize); } return newinst; } SQInstance *Clone(SQSharedState *ss) { SQInteger size = calcinstancesize(_class); SQInstance *newinst = (SQInstance *)SQ_MALLOC(size); new (newinst) SQInstance(ss, this,size); if(_class->_udsize) { newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize); } return newinst; } ~SQInstance(); bool Get(const SQObjectPtr &key,SQObjectPtr &val) { if(_class->_members->Get(key,val)) { if(_isfield(val)) { SQObjectPtr &o = _values[_member_idx(val)]; val = _realval(o); } else { val = _class->_methods[_member_idx(val)].val; } return true; } return false; } bool Set(const SQObjectPtr &key,const SQObjectPtr &val) { SQObjectPtr idx; if(_class->_members->Get(key,idx) && _isfield(idx)) { _values[_member_idx(idx)] = val; return true; } return false; } void Release() { _uiRef++; if (_hook) { _hook(_userpointer,0);} _uiRef--; if(_uiRef > 0) return; SQInteger size = _memsize; this->~SQInstance(); SQ_FREE(this, size); } void Finalize(); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable ** ); SQObjectType GetType() {return OT_INSTANCE;} #endif bool InstanceOf(SQClass *trg); bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); SQClass *_class; SQUserPointer _userpointer; SQRELEASEHOOK _hook; SQInteger _memsize; SQObjectPtr _values[1]; }; #endif simutrans-124.5/src/squirrel/squirrel/sqclosure.h000066400000000000000000000137771520675460500222770ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQCLOSURE_H_ #define _SQCLOSURE_H_ #define _CALC_CLOSURE_SIZE(func) (sizeof(SQClosure) + (func->_noutervalues*sizeof(SQObjectPtr)) + (func->_ndefaultparams*sizeof(SQObjectPtr))) struct SQFunctionProto; struct SQClass; struct SQClosure : public CHAINABLE_OBJ { private: SQClosure(SQSharedState *ss,SQFunctionProto *func){_function = func; __ObjAddRef(_function); _base = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL; _root=NULL;} public: static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func,SQWeakRef *root){ SQInteger size = _CALC_CLOSURE_SIZE(func); SQClosure *nc=(SQClosure*)SQ_MALLOC(size); new (nc) SQClosure(ss,func); nc->_outervalues = (SQObjectPtr *)(nc + 1); nc->_defaultparams = &nc->_outervalues[func->_noutervalues]; nc->_root = root; __ObjAddRef(nc->_root); _CONSTRUCT_VECTOR(SQObjectPtr,func->_noutervalues,nc->_outervalues); _CONSTRUCT_VECTOR(SQObjectPtr,func->_ndefaultparams,nc->_defaultparams); return nc; } void Release(){ SQFunctionProto *f = _function; SQInteger size = _CALC_CLOSURE_SIZE(f); _DESTRUCT_VECTOR(SQObjectPtr,f->_noutervalues,_outervalues); _DESTRUCT_VECTOR(SQObjectPtr,f->_ndefaultparams,_defaultparams); __ObjRelease(_function); this->~SQClosure(); sq_vm_free(this,size); } void SetRoot(SQWeakRef *r) { __ObjRelease(_root); _root = r; __ObjAddRef(_root); } SQClosure *Clone() { SQFunctionProto *f = _function; SQClosure * ret = SQClosure::Create(_opt_ss(this),f,_root); ret->_env = _env; if(ret->_env) __ObjAddRef(ret->_env); _COPY_VECTOR(ret->_outervalues,_outervalues,f->_noutervalues); _COPY_VECTOR(ret->_defaultparams,_defaultparams,f->_ndefaultparams); return ret; } ~SQClosure(); bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize(){ SQFunctionProto *f = _function; _NULL_SQOBJECT_VECTOR(_outervalues,f->_noutervalues); _NULL_SQOBJECT_VECTOR(_defaultparams,f->_ndefaultparams); } SQObjectType GetType() {return OT_CLOSURE;} #endif SQWeakRef *_env; SQWeakRef *_root; SQClass *_base; SQFunctionProto *_function; SQObjectPtr *_outervalues; SQObjectPtr *_defaultparams; }; ////////////////////////////////////////////// struct SQOuter : public CHAINABLE_OBJ { private: SQOuter(SQSharedState *ss, SQObjectPtr *outer){_valptr = outer; _next = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } public: static SQOuter *Create(SQSharedState *ss, SQObjectPtr *outer) { SQOuter *nc = (SQOuter*)SQ_MALLOC(sizeof(SQOuter)); new (nc) SQOuter(ss, outer); return nc; } ~SQOuter() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } void Release() { this->~SQOuter(); sq_vm_free(this,sizeof(SQOuter)); } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize() { _value.Null(); } SQObjectType GetType() {return OT_OUTER;} #endif SQObjectPtr *_valptr; /* pointer to value on stack, or _value below */ SQInteger _idx; /* idx in stack array, for relocation */ SQObjectPtr _value; /* value of outer after stack frame is closed */ SQOuter *_next; /* pointer to next outer when frame is open */ }; ////////////////////////////////////////////// struct SQGenerator : public CHAINABLE_OBJ { enum SQGeneratorState{eRunning,eSuspended,eDead}; private: SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=NULL;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);} public: static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){ SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator)); new (nc) SQGenerator(ss,closure); return nc; } ~SQGenerator() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } void Kill(){ _state=eDead; _stack.resize(0); _closure.Null();} void Release(){ sq_delete(this,SQGenerator); } bool Yield(SQVM *v,SQInteger target); bool Resume(SQVM *v,SQObjectPtr &dest); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize(){_stack.resize(0);_closure.Null();} SQObjectType GetType() {return OT_GENERATOR;} #endif SQObjectPtr _closure; SQObjectPtrVec _stack; SQVM::CallInfo _ci; ExceptionsTraps _etraps; SQGeneratorState _state; }; #define _CALC_NATVIVECLOSURE_SIZE(noutervalues) (sizeof(SQNativeClosure) + (noutervalues*sizeof(SQObjectPtr))) struct SQNativeClosure : public CHAINABLE_OBJ { private: SQNativeClosure(SQSharedState *ss,SQFUNCTION func){_function=func;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL;} public: static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func,SQInteger nouters) { SQInteger size = _CALC_NATVIVECLOSURE_SIZE(nouters); SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(size); new (nc) SQNativeClosure(ss,func); nc->_outervalues = (SQObjectPtr *)(nc + 1); nc->_noutervalues = nouters; _CONSTRUCT_VECTOR(SQObjectPtr,nc->_noutervalues,nc->_outervalues); return nc; } SQNativeClosure *Clone() { SQNativeClosure * ret = SQNativeClosure::Create(_opt_ss(this),_function,_noutervalues); ret->_env = _env; if(ret->_env) __ObjAddRef(ret->_env); ret->_name = _name; _COPY_VECTOR(ret->_outervalues,_outervalues,_noutervalues); ret->_typecheck.copy(_typecheck); ret->_nparamscheck = _nparamscheck; return ret; } ~SQNativeClosure() { __ObjRelease(_env); REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } void Release(){ SQInteger size = _CALC_NATVIVECLOSURE_SIZE(_noutervalues); _DESTRUCT_VECTOR(SQObjectPtr,_noutervalues,_outervalues); this->~SQNativeClosure(); sq_free(this,size); } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize() { _NULL_SQOBJECT_VECTOR(_outervalues,_noutervalues); } SQObjectType GetType() {return OT_NATIVECLOSURE;} #endif SQInteger _nparamscheck; SQIntVec _typecheck; SQObjectPtr *_outervalues; SQUnsignedInteger _noutervalues; SQWeakRef *_env; SQFUNCTION _function; SQObjectPtr _name; }; #endif simutrans-124.5/src/squirrel/squirrel/sqcompiler.cc000066400000000000000000001315411520675460500225610ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #ifndef NO_COMPILER #include #include #include "sqopcodes.h" #include "sqstring.h" #include "sqfuncproto.h" #include "sqcompiler.h" #include "sqfuncstate.h" #include "sqlexer.h" #include "sqvm.h" #include "sqtable.h" #define EXPR 1 #define OBJECT 2 #define BASE 3 #define LOCAL 4 #define OUTER 5 struct SQExpState { SQInteger etype; /* expr. type; one of EXPR, OBJECT, BASE, OUTER or LOCAL */ SQInteger epos; /* expr. location on stack; -1 for OBJECT and BASE */ bool donot_get; /* signal not to deref the next value */ }; #define MAX_COMPILER_ERROR_LEN 256 struct SQScope { SQInteger outers; SQInteger stacksize; }; #define BEGIN_SCOPE() SQScope __oldscope__ = _scope; \ _scope.outers = _fs->_outers; \ _scope.stacksize = _fs->GetStackSize(); #define RESOLVE_OUTERS() if(_fs->GetStackSize() != _scope.stacksize) { \ if(_fs->CountOuters(_scope.stacksize)) { \ _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ } \ } #define END_SCOPE_NO_CLOSE() { if(_fs->GetStackSize() != _scope.stacksize) { \ _fs->SetStackSize(_scope.stacksize); \ } \ _scope = __oldscope__; \ } #define END_SCOPE() { SQInteger oldouters = _fs->_outers;\ if(_fs->GetStackSize() != _scope.stacksize) { \ _fs->SetStackSize(_scope.stacksize); \ if(oldouters != _fs->_outers) { \ _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ } \ } \ _scope = __oldscope__; \ } #define BEGIN_BREAKBLE_BLOCK() SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \ SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \ _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0); #define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \ __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \ if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \ if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \ _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();} class SQCompiler { public: SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo) { _vm=v; _lex.Init(_ss(v), rg, up,ThrowError,this); _sourcename = SQString::Create(_ss(v), sourcename); _lineinfo = lineinfo;_raiseerror = raiseerror; _scope.outers = 0; _scope.stacksize = 0; _compilererror[0] = _SC('\0'); } static void ThrowError(void *ud, const SQChar *s) { SQCompiler *c = (SQCompiler *)ud; c->Error(s); } void Error(const SQChar *s, ...) { va_list vl; va_start(vl, s); scvsprintf(_compilererror, MAX_COMPILER_ERROR_LEN, s, vl); va_end(vl); longjmp(_errorjmp,1); } void Lex(){ _token = _lex.Lex();} SQObject Expect(SQInteger tok) { if(_token != tok) { if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) { //do nothing } else { const SQChar *etypename; if(tok > 255) { switch(tok) { case TK_IDENTIFIER: etypename = _SC("IDENTIFIER"); break; case TK_STRING_LITERAL: etypename = _SC("STRING_LITERAL"); break; case TK_INTEGER: etypename = _SC("INTEGER"); break; case TK_FLOAT: etypename = _SC("FLOAT"); break; default: etypename = _lex.Tok2Str(tok); } Error(_SC("expected '%s'"), etypename); } Error(_SC("expected '%c'"), tok); } } SQObjectPtr ret; switch(tok) { case TK_IDENTIFIER: ret = _fs->CreateString(_lex._svalue); break; case TK_STRING_LITERAL: ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); break; case TK_INTEGER: ret = SQObjectPtr(_lex._nvalue); break; case TK_FLOAT: ret = SQObjectPtr(_lex._fvalue); break; } Lex(); return ret; } bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); } void OptionalSemicolon() { if(_token == _SC(';')) { Lex(); return; } if(!IsEndOfStatement()) { Error(_SC("end of statement expected (; or lf)")); } } void MoveIfCurrentTargetIsLocal() { SQInteger trg = _fs->TopTarget(); if(_fs->IsLocal(trg)) { trg = _fs->PopTarget(); //pops the target and moves it _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg); } } bool Compile(SQObjectPtr &o) { _debugline = 1; _debugop = 0; SQFuncState funcstate(_ss(_vm), NULL,ThrowError,this); funcstate._name = SQString::Create(_ss(_vm), _SC("main")); _fs = &funcstate; _fs->AddParameter(_fs->CreateString(_SC("this"))); _fs->AddParameter(_fs->CreateString(_SC("vargv"))); _fs->_varparams = true; _fs->_sourcename = _sourcename; SQInteger stacksize = _fs->GetStackSize(); if(setjmp(_errorjmp) == 0) { Lex(); while(_token > 0){ Statement(); if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); } _fs->SetStackSize(stacksize); _fs->AddLineInfos(_lex._currentline, _lineinfo, true); _fs->AddInstruction(_OP_RETURN, 0xFF); _fs->SetStackSize(0); o =_fs->BuildProto(); #ifdef _DEBUG_DUMP _fs->Dump(_funcproto(o)); #endif } else { if(_raiseerror && _ss(_vm)->_compilererrorhandler) { _ss(_vm)->_compilererrorhandler(_vm, _compilererror, sq_type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"), _lex._currentline, _lex._currentcolumn); } _vm->_lasterror = SQString::Create(_ss(_vm), _compilererror, -1); return false; } return true; } void Statements() { while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) { Statement(); if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); } } void Statement(bool closeframe = true) { _fs->AddLineInfos(_lex._currentline, _lineinfo); switch(_token){ case _SC(';'): Lex(); break; case TK_IF: IfStatement(); break; case TK_WHILE: WhileStatement(); break; case TK_DO: DoWhileStatement(); break; case TK_FOR: ForStatement(); break; case TK_FOREACH: ForEachStatement(); break; case TK_SWITCH: SwitchStatement(); break; case TK_LOCAL: LocalDeclStatement(); break; case TK_RETURN: case TK_YIELD: { SQOpcode op; if(_token == TK_RETURN) { op = _OP_RETURN; } else { op = _OP_YIELD; _fs->_bgenerator = true; } Lex(); if(!IsEndOfStatement()) { SQInteger retexp = _fs->GetCurrentPos()+1; CommaExpr(); if(op == _OP_RETURN && _fs->_traps > 0) _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0); _fs->_returnexp = retexp; _fs->AddInstruction(op, 1, _fs->PopTarget(),_fs->GetStackSize()); } else{ if(op == _OP_RETURN && _fs->_traps > 0) _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0); _fs->_returnexp = -1; _fs->AddInstruction(op, 0xFF,0,_fs->GetStackSize()); } break;} case TK_BREAK: if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block")); if(_fs->_breaktargets.top() > 0){ _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0); } RESOLVE_OUTERS(); _fs->AddInstruction(_OP_JMP, 0, -1234); _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos()); Lex(); break; case TK_CONTINUE: if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block")); if(_fs->_continuetargets.top() > 0) { _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0); } RESOLVE_OUTERS(); _fs->AddInstruction(_OP_JMP, 0, -1234); _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos()); Lex(); break; case TK_FUNCTION: FunctionStatement(); break; case TK_CLASS: ClassStatement(); break; case TK_ENUM: EnumStatement(); break; case _SC('{'):{ BEGIN_SCOPE(); Lex(); Statements(); Expect(_SC('}')); if(closeframe) { END_SCOPE(); } else { END_SCOPE_NO_CLOSE(); } } break; case TK_TRY: TryCatchStatement(); break; case TK_THROW: Lex(); CommaExpr(); _fs->AddInstruction(_OP_THROW, _fs->PopTarget()); break; case TK_CONST: { Lex(); SQObject id = Expect(TK_IDENTIFIER); Expect('='); SQObject val = ExpectScalar(); OptionalSemicolon(); SQTable *enums = _table(_ss(_vm)->_consts); SQObjectPtr strongid = id; enums->NewSlot(strongid,SQObjectPtr(val)); strongid.Null(); } break; default: CommaExpr(); _fs->DiscardTarget(); //_fs->PopTarget(); break; } _fs->SnoozeOpt(); } void EmitDerefOp(SQOpcode op) { SQInteger val = _fs->PopTarget(); SQInteger key = _fs->PopTarget(); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(op,_fs->PushTarget(),src,key,val); } void Emit2ArgsOP(SQOpcode op, SQInteger p3 = 0) { SQInteger p2 = _fs->PopTarget(); //src in OP_GET SQInteger p1 = _fs->PopTarget(); //key in OP_GET _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3); } void EmitCompoundArith(SQInteger tok, SQInteger etype, SQInteger pos) { /* Generate code depending on the expression type */ switch(etype) { case LOCAL:{ SQInteger p2 = _fs->PopTarget(); //src in OP_GET SQInteger p1 = _fs->PopTarget(); //key in OP_GET _fs->PushTarget(p1); //EmitCompArithLocal(tok, p1, p1, p2); _fs->AddInstruction(ChooseArithOpByToken(tok),p1, p2, p1, 0); _fs->SnoozeOpt(); } break; case OBJECT: case BASE: { SQInteger val = _fs->PopTarget(); SQInteger key = _fs->PopTarget(); SQInteger src = _fs->PopTarget(); /* _OP_COMPARITH mixes dest obj and source val in the arg1 */ _fs->AddInstruction(_OP_COMPARITH, _fs->PushTarget(), (src<<16)|val, key, ChooseCompArithCharByToken(tok)); } break; case OUTER: { SQInteger val = _fs->TopTarget(); SQInteger tmp = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, tmp, pos); _fs->AddInstruction(ChooseArithOpByToken(tok), tmp, val, tmp, 0); _fs->PopTarget(); _fs->PopTarget(); _fs->AddInstruction(_OP_SETOUTER, _fs->PushTarget(), pos, tmp); } break; } } void CommaExpr() { for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr()); } void Expression() { SQExpState es = _es; _es.etype = EXPR; _es.epos = -1; _es.donot_get = false; LogicalOrExp(); switch(_token) { case _SC('='): case TK_NEWSLOT: case TK_MINUSEQ: case TK_PLUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ:{ SQInteger op = _token; SQInteger ds = _es.etype; SQInteger pos = _es.epos; if(ds == EXPR) Error(_SC("can't assign expression")); else if(ds == BASE) Error(_SC("'base' cannot be modified")); Lex(); Expression(); switch(op){ case TK_NEWSLOT: if(ds == OBJECT || ds == BASE) EmitDerefOp(_OP_NEWSLOT); else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local Error(_SC("can't 'create' a local slot")); break; case _SC('='): //ASSIGN switch(ds) { case LOCAL: { SQInteger src = _fs->PopTarget(); SQInteger dst = _fs->TopTarget(); _fs->AddInstruction(_OP_MOVE, dst, src); } break; case OBJECT: case BASE: EmitDerefOp(_OP_SET); break; case OUTER: { SQInteger src = _fs->PopTarget(); SQInteger dst = _fs->PushTarget(); _fs->AddInstruction(_OP_SETOUTER, dst, pos, src); } } break; case TK_MINUSEQ: case TK_PLUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ: EmitCompoundArith(op, ds, pos); break; } } break; case _SC('?'): { Lex(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); SQInteger jzpos = _fs->GetCurrentPos(); SQInteger trg = _fs->PushTarget(); Expression(); SQInteger first_exp = _fs->PopTarget(); if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); SQInteger endfirstexp = _fs->GetCurrentPos(); _fs->AddInstruction(_OP_JMP, 0, 0); Expect(_SC(':')); SQInteger jmppos = _fs->GetCurrentPos(); Expression(); SQInteger second_exp = _fs->PopTarget(); if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); _fs->SetInstructionParam(jzpos, 1, endfirstexp - jzpos + 1); _fs->SnoozeOpt(); } break; } _es = es; } template void INVOKE_EXP(T f) { SQExpState es = _es; _es.etype = EXPR; _es.epos = -1; _es.donot_get = false; (this->*f)(); _es = es; } template void BIN_EXP(SQOpcode op, T f,SQInteger op3 = 0) { Lex(); INVOKE_EXP(f); SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget(); _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3); _es.etype = EXPR; } void LogicalOrExp() { LogicalAndExp(); for(;;) if(_token == TK_OR) { SQInteger first_exp = _fs->PopTarget(); SQInteger trg = _fs->PushTarget(); _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0); SQInteger jpos = _fs->GetCurrentPos(); if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); Lex(); INVOKE_EXP(&SQCompiler::LogicalOrExp); _fs->SnoozeOpt(); SQInteger second_exp = _fs->PopTarget(); if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); _fs->SnoozeOpt(); _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); _es.etype = EXPR; break; }else return; } void LogicalAndExp() { BitwiseOrExp(); for(;;) switch(_token) { case TK_AND: { SQInteger first_exp = _fs->PopTarget(); SQInteger trg = _fs->PushTarget(); _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0); SQInteger jpos = _fs->GetCurrentPos(); if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); Lex(); INVOKE_EXP(&SQCompiler::LogicalAndExp); _fs->SnoozeOpt(); SQInteger second_exp = _fs->PopTarget(); if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); _fs->SnoozeOpt(); _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); _es.etype = EXPR; break; } default: return; } } void BitwiseOrExp() { BitwiseXorExp(); for(;;) if(_token == _SC('|')) {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR); }else return; } void BitwiseXorExp() { BitwiseAndExp(); for(;;) if(_token == _SC('^')) {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR); }else return; } void BitwiseAndExp() { EqExp(); for(;;) if(_token == _SC('&')) {BIN_EXP(_OP_BITW, &SQCompiler::EqExp,BW_AND); }else return; } void EqExp() { CompExp(); for(;;) switch(_token) { case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::CompExp); break; case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::CompExp); break; case TK_3WAYSCMP: BIN_EXP(_OP_CMP, &SQCompiler::CompExp,CMP_3W); break; default: return; } } void CompExp() { ShiftExp(); for(;;) switch(_token) { case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break; case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break; case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break; case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break; case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::ShiftExp); break; case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::ShiftExp); break; default: return; } } void ShiftExp() { PlusExp(); for(;;) switch(_token) { case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break; case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break; case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break; default: return; } } SQOpcode ChooseArithOpByToken(SQInteger tok) { switch(tok) { case TK_PLUSEQ: case '+': return _OP_ADD; case TK_MINUSEQ: case '-': return _OP_SUB; case TK_MULEQ: case '*': return _OP_MUL; case TK_DIVEQ: case '/': return _OP_DIV; case TK_MODEQ: case '%': return _OP_MOD; default: assert(0); } return _OP_ADD; } SQInteger ChooseCompArithCharByToken(SQInteger tok) { SQInteger oper; switch(tok){ case TK_MINUSEQ: oper = '-'; break; case TK_PLUSEQ: oper = '+'; break; case TK_MULEQ: oper = '*'; break; case TK_DIVEQ: oper = '/'; break; case TK_MODEQ: oper = '%'; break; default: oper = 0; //shut up compiler assert(0); break; }; return oper; } void PlusExp() { MultExp(); for(;;) switch(_token) { case _SC('+'): case _SC('-'): BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::MultExp); break; default: return; } } void MultExp() { PrefixedExpr(); for(;;) switch(_token) { case _SC('*'): case _SC('/'): case _SC('%'): BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::PrefixedExpr); break; default: return; } } //if 'pos' != -1 the previous variable is a local variable void PrefixedExpr() { SQInteger pos = Factor(); for(;;) { switch(_token) { case _SC('.'): pos = -1; Lex(); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); if(_es.etype==BASE) { Emit2ArgsOP(_OP_GET); pos = _fs->TopTarget(); _es.etype = EXPR; _es.epos = pos; } else { if(NeedGet()) { Emit2ArgsOP(_OP_GET); } _es.etype = OBJECT; } break; case _SC('['): if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot break deref/or comma needed after [exp]=exp slot declaration")); Lex(); Expression(); Expect(_SC(']')); pos = -1; if(_es.etype==BASE) { Emit2ArgsOP(_OP_GET); pos = _fs->TopTarget(); _es.etype = EXPR; _es.epos = pos; } else { if(NeedGet()) { Emit2ArgsOP(_OP_GET); } _es.etype = OBJECT; } break; case TK_MINUSMINUS: case TK_PLUSPLUS: { if(IsEndOfStatement()) return; SQInteger diff = (_token==TK_MINUSMINUS) ? -1 : 1; Lex(); switch(_es.etype) { case EXPR: Error(_SC("can't '++' or '--' an expression")); break; case OBJECT: case BASE: if(_es.donot_get == true) { Error(_SC("can't '++' or '--' an expression")); break; } //mmh dor this make sense? Emit2ArgsOP(_OP_PINC, diff); break; case LOCAL: { SQInteger src = _fs->PopTarget(); _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, diff); } break; case OUTER: { SQInteger tmp1 = _fs->PushTarget(); SQInteger tmp2 = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, tmp2, _es.epos); _fs->AddInstruction(_OP_PINCL, tmp1, tmp2, 0, diff); _fs->AddInstruction(_OP_SETOUTER, tmp2, _es.epos, tmp2); _fs->PopTarget(); } } } return; break; case _SC('('): switch(_es.etype) { case OBJECT: { SQInteger key = _fs->PopTarget(); /* location of the key */ SQInteger table = _fs->PopTarget(); /* location of the object */ SQInteger closure = _fs->PushTarget(); /* location for the closure */ SQInteger ttarget = _fs->PushTarget(); /* location for 'this' pointer */ _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget); } break; case BASE: //Emit2ArgsOP(_OP_GET); _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); break; case OUTER: _fs->AddInstruction(_OP_GETOUTER, _fs->PushTarget(), _es.epos); _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); break; default: _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); } _es.etype = EXPR; Lex(); FunctionCallArgs(); break; default: return; } } } SQInteger Factor() { //_es.etype = EXPR; switch(_token) { case TK_STRING_LITERAL: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1))); Lex(); break; case TK_BASE: Lex(); _fs->AddInstruction(_OP_GETBASE, _fs->PushTarget()); _es.etype = BASE; _es.epos = _fs->TopTarget(); return (_es.epos); break; case TK_IDENTIFIER: case TK_CONSTRUCTOR: case TK_THIS:{ SQObject id; SQObject constant; switch(_token) { case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break; case TK_THIS: id = _fs->CreateString(_SC("this"),4); break; case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor"),11); break; } SQInteger pos = -1; Lex(); if((pos = _fs->GetLocalVariable(id)) != -1) { /* Handle a local variable (includes 'this') */ _fs->PushTarget(pos); _es.etype = LOCAL; _es.epos = pos; } else if((pos = _fs->GetOuterVariable(id)) != -1) { /* Handle a free var */ if(NeedGet()) { _es.epos = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, _es.epos, pos); /* _es.etype = EXPR; already default value */ } else { _es.etype = OUTER; _es.epos = pos; } } else if(_fs->IsConstant(id, constant)) { /* Handle named constant */ SQObjectPtr constval; SQObject constid; if(sq_type(constant) == OT_TABLE) { Expect('.'); constid = Expect(TK_IDENTIFIER); if(!_table(constant)->Get(constid, constval)) { constval.Null(); Error(_SC("invalid constant [%s.%s]"), _stringval(id), _stringval(constid)); } } else { constval = constant; } _es.epos = _fs->PushTarget(); /* generate direct or literal function depending on size */ SQObjectType ctype = sq_type(constval); switch(ctype) { case OT_INTEGER: EmitLoadConstInt(_integer(constval),_es.epos); break; case OT_FLOAT: EmitLoadConstFloat(_float(constval),_es.epos); break; case OT_BOOL: _fs->AddInstruction(_OP_LOADBOOL, _es.epos, _integer(constval)); break; default: _fs->AddInstruction(_OP_LOAD,_es.epos,_fs->GetConstant(constval)); break; } _es.etype = EXPR; } else { /* Handle a non-local variable, aka a field. Push the 'this' pointer on * the virtual stack (always found in offset 0, so no instruction needs to * be generated), and push the key next. Generate an _OP_LOAD instruction * for the latter. If we are not using the variable as a dref expr, generate * the _OP_GET instruction. */ _fs->PushTarget(0); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); if(NeedGet()) { Emit2ArgsOP(_OP_GET); } _es.etype = OBJECT; } return _es.epos; } break; case TK_DOUBLE_COLON: // "::" _fs->AddInstruction(_OP_LOADROOT, _fs->PushTarget()); _es.etype = OBJECT; _token = _SC('.'); /* hack: drop into PrefixExpr, case '.'*/ _es.epos = -1; return _es.epos; break; case TK_NULL: _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); Lex(); break; case TK_INTEGER: EmitLoadConstInt(_lex._nvalue,-1); Lex(); break; case TK_FLOAT: EmitLoadConstFloat(_lex._fvalue,-1); Lex(); break; case TK_TRUE: case TK_FALSE: _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0); Lex(); break; case _SC('['): { _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,0,NOT_ARRAY); SQInteger apos = _fs->GetCurrentPos(),key = 0; Lex(); while(_token != _SC(']')) { Expression(); if(_token == _SC(',')) Lex(); SQInteger val = _fs->PopTarget(); SQInteger array = _fs->TopTarget(); _fs->AddInstruction(_OP_APPENDARRAY, array, val, AAT_STACK); key++; } _fs->SetInstructionParam(apos, 1, key); Lex(); } break; case _SC('{'): _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); Lex();ParseTableOrClass(_SC(','),_SC('}')); break; case TK_FUNCTION: FunctionExp(_token);break; case _SC('@'): FunctionExp(_token,true);break; case TK_CLASS: Lex(); ClassExp();break; case _SC('-'): Lex(); switch(_token) { case TK_INTEGER: EmitLoadConstInt(-_lex._nvalue,-1); Lex(); break; case TK_FLOAT: EmitLoadConstFloat(-_lex._fvalue,-1); Lex(); break; default: UnaryOP(_OP_NEG); } break; case _SC('!'): Lex(); UnaryOP(_OP_NOT); break; case _SC('~'): Lex(); if(_token == TK_INTEGER) { EmitLoadConstInt(~_lex._nvalue,-1); Lex(); break; } UnaryOP(_OP_BWNOT); break; case TK_TYPEOF : Lex() ;UnaryOP(_OP_TYPEOF); break; case TK_RESUME : Lex(); UnaryOP(_OP_RESUME); break; case TK_CLONE : Lex(); UnaryOP(_OP_CLONE); break; case TK_RAWCALL: Lex(); Expect('('); FunctionCallArgs(true); break; case TK_MINUSMINUS : case TK_PLUSPLUS :PrefixIncDec(_token); break; case TK_DELETE : DeleteExpr(); break; case _SC('('): Lex(); CommaExpr(); Expect(_SC(')')); break; case TK___LINE__: EmitLoadConstInt(_lex._currentline,-1); Lex(); break; case TK___FILE__: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_sourcename)); Lex(); break; default: Error(_SC("expression expected")); } _es.etype = EXPR; return -1; } void EmitLoadConstInt(SQInteger value,SQInteger target) { if(target < 0) { target = _fs->PushTarget(); } if(value <= INT_MAX && value > INT_MIN) { //does it fit in 32 bits? _fs->AddInstruction(_OP_LOADINT, target,value); } else { _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); } } void EmitLoadConstFloat(SQFloat value,SQInteger target) { if(target < 0) { target = _fs->PushTarget(); } if(sizeof(SQFloat) == sizeof(SQInt32)) { _fs->AddInstruction(_OP_LOADFLOAT, target,*((SQInt32 *)&value)); } else { _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); } } void UnaryOP(SQOpcode op) { PrefixedExpr(); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(op, _fs->PushTarget(), src); } bool NeedGet() { switch(_token) { case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_MODEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: return false; case TK_PLUSPLUS: case TK_MINUSMINUS: if (!IsEndOfStatement()) { return false; } break; } return (!_es.donot_get || ( _es.donot_get && (_token == _SC('.') || _token == _SC('[')))); } void FunctionCallArgs(bool rawcall = false) { SQInteger nargs = 1;//this while(_token != _SC(')')) { Expression(); MoveIfCurrentTargetIsLocal(); nargs++; if(_token == _SC(',')){ Lex(); if(_token == ')') Error(_SC("expression expected, found ')'")); } } Lex(); if (rawcall) { if (nargs < 3) Error(_SC("rawcall requires at least 2 parameters (callee and this)")); nargs -= 2; //removes callee and this from count } for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget(); SQInteger stackbase = _fs->PopTarget(); SQInteger closure = _fs->PopTarget(); _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs); #if 0 /* * disabled as it breaks existing scripts like * * local a = [] * local b = a.len() * // open some new block * { * local c = 0 * } * * see https://github.com/albertodemichelis/squirrel/issues/177 */ if (_token == '{') { SQInteger retval = _fs->TopTarget(); SQInteger nkeys = 0; Lex(); while (_token != '}') { switch (_token) { case _SC('['): Lex(); CommaExpr(); Expect(_SC(']')); Expect(_SC('=')); Expression(); break; default: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); Expect(_SC('=')); Expression(); break; } if (_token == ',') Lex(); nkeys++; SQInteger val = _fs->PopTarget(); SQInteger key = _fs->PopTarget(); _fs->AddInstruction(_OP_SET, 0xFF, retval, key, val); } Lex(); } #endif } void ParseTableOrClass(SQInteger separator,SQInteger terminator) { SQInteger tpos = _fs->GetCurrentPos(),nkeys = 0; while(_token != terminator) { bool hasattrs = false; bool isstatic = false; //check if is an attribute if(separator == ';') { if(_token == TK_ATTR_OPEN) { _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); Lex(); ParseTableOrClass(',',TK_ATTR_CLOSE); hasattrs = true; } if(_token == TK_STATIC) { isstatic = true; Lex(); } } switch(_token) { case TK_FUNCTION: case TK_CONSTRUCTOR:{ SQInteger tk = _token; Lex(); SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor")); Expect(_SC('(')); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); CreateFunction(id); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); } break; case _SC('['): Lex(); CommaExpr(); Expect(_SC(']')); Expect(_SC('=')); Expression(); break; case TK_STRING_LITERAL: //JSON if(separator == ',') { //only works for tables _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_STRING_LITERAL))); Expect(_SC(':')); Expression(); break; } default : _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); Expect(_SC('=')); Expression(); } if(_token == separator) Lex();//optional comma/semicolon nkeys++; SQInteger val = _fs->PopTarget(); SQInteger key = _fs->PopTarget(); SQInteger attrs = hasattrs ? _fs->PopTarget():-1; ((void)attrs); assert((hasattrs && (attrs == key-1)) || !hasattrs); unsigned char flags = (hasattrs?NEW_SLOT_ATTRIBUTES_FLAG:0)|(isstatic?NEW_SLOT_STATIC_FLAG:0); SQInteger table = _fs->TopTarget(); //<AddInstruction(_OP_NEWSLOT, 0xFF, table, key, val); } else { _fs->AddInstruction(_OP_NEWSLOTA, flags, table, key, val); //this for classes only as it invokes _newmember } } if(separator == _SC(',')) //hack recognizes a table from the separator _fs->SetInstructionParam(tpos, 1, nkeys); Lex(); } void LocalDeclStatement() { SQObject varname; Lex(); if( _token == TK_FUNCTION) { Lex(); varname = Expect(TK_IDENTIFIER); Expect(_SC('(')); CreateFunction(varname,false); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); _fs->PopTarget(); _fs->PushLocalVariable(varname); return; } do { varname = Expect(TK_IDENTIFIER); if(_token == _SC('=')) { Lex(); Expression(); SQInteger src = _fs->PopTarget(); SQInteger dest = _fs->PushTarget(); if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src); } else{ _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); } _fs->PopTarget(); _fs->PushLocalVariable(varname); if(_token == _SC(',')) Lex(); else break; } while(1); } void IfBlock() { if (_token == _SC('{')) { BEGIN_SCOPE(); Lex(); Statements(); Expect(_SC('}')); if (true) { END_SCOPE(); } else { END_SCOPE_NO_CLOSE(); } } else { //BEGIN_SCOPE(); Statement(); if (_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); //END_SCOPE(); } } void IfStatement() { SQInteger jmppos; bool haselse = false; Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); SQInteger jnepos = _fs->GetCurrentPos(); IfBlock(); // /*static int n = 0; if (_token != _SC('}') && _token != TK_ELSE) { printf("IF %d-----------------------!!!!!!!!!\n", n); if (n == 5) { printf("asd"); } n++; //OptionalSemicolon(); }*/ SQInteger endifblock = _fs->GetCurrentPos(); if(_token == TK_ELSE){ haselse = true; //BEGIN_SCOPE(); _fs->AddInstruction(_OP_JMP); jmppos = _fs->GetCurrentPos(); Lex(); //Statement(); if(_lex._prevtoken != _SC('}')) OptionalSemicolon(); IfBlock(); //END_SCOPE(); _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); } _fs->SetInstructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0)); } void WhileStatement() { SQInteger jzpos, jmppos; jmppos = _fs->GetCurrentPos(); Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); BEGIN_BREAKBLE_BLOCK(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); BEGIN_SCOPE(); Statement(); END_SCOPE(); _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); END_BREAKBLE_BLOCK(jmppos); } void DoWhileStatement() { Lex(); SQInteger jmptrg = _fs->GetCurrentPos(); BEGIN_BREAKBLE_BLOCK() BEGIN_SCOPE(); Statement(); END_SCOPE(); Expect(TK_WHILE); SQInteger continuetrg = _fs->GetCurrentPos(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); _fs->AddInstruction(_OP_JZ, _fs->PopTarget(), 1); _fs->AddInstruction(_OP_JMP, 0, jmptrg - _fs->GetCurrentPos() - 1); END_BREAKBLE_BLOCK(continuetrg); } void ForStatement() { Lex(); BEGIN_SCOPE(); Expect(_SC('(')); if(_token == TK_LOCAL) LocalDeclStatement(); else if(_token != _SC(';')){ CommaExpr(); _fs->PopTarget(); } Expect(_SC(';')); _fs->SnoozeOpt(); SQInteger jmppos = _fs->GetCurrentPos(); SQInteger jzpos = -1; if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); } Expect(_SC(';')); _fs->SnoozeOpt(); SQInteger expstart = _fs->GetCurrentPos() + 1; if(_token != _SC(')')) { CommaExpr(); _fs->PopTarget(); } Expect(_SC(')')); _fs->SnoozeOpt(); SQInteger expend = _fs->GetCurrentPos(); SQInteger expsize = (expend - expstart) + 1; SQInstructionVec exp; if(expsize > 0) { for(SQInteger i = 0; i < expsize; i++) exp.push_back(_fs->GetInstruction(expstart + i)); _fs->PopInstructions(expsize); } BEGIN_BREAKBLE_BLOCK() Statement(); SQInteger continuetrg = _fs->GetCurrentPos(); if(expsize > 0) { for(SQInteger i = 0; i < expsize; i++) _fs->AddInstruction(exp[i]); } _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0); if(jzpos> 0) _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); END_BREAKBLE_BLOCK(continuetrg); END_SCOPE(); } void ForEachStatement() { SQObject idxname, valname; Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER); if(_token == _SC(',')) { idxname = valname; Lex(); valname = Expect(TK_IDENTIFIER); } else{ idxname = _fs->CreateString(_SC("@INDEX@")); } Expect(TK_IN); //save the stack size BEGIN_SCOPE(); //put the table in the stack(evaluate the table expression) Expression(); Expect(_SC(')')); SQInteger container = _fs->TopTarget(); //push the index local var SQInteger indexpos = _fs->PushLocalVariable(idxname); _fs->AddInstruction(_OP_LOADNULLS, indexpos,1); //push the value local var SQInteger valuepos = _fs->PushLocalVariable(valname); _fs->AddInstruction(_OP_LOADNULLS, valuepos,1); //push reference index SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible _fs->AddInstruction(_OP_LOADNULLS, itrpos,1); SQInteger jmppos = _fs->GetCurrentPos(); _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos); SQInteger foreachpos = _fs->GetCurrentPos(); _fs->AddInstruction(_OP_POSTFOREACH, container, 0, indexpos); //generate the statement code BEGIN_BREAKBLE_BLOCK() Statement(); _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); _fs->SetInstructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos); _fs->SetInstructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos); END_BREAKBLE_BLOCK(foreachpos - 1); //restore the local variable stack(remove index,val and ref idx) _fs->PopTarget(); END_SCOPE(); } void SwitchStatement() { Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); Expect(_SC('{')); SQInteger expr = _fs->TopTarget(); bool bfirst = true; SQInteger tonextcondjmp = -1; SQInteger skipcondjmp = -1; SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size(); _fs->_breaktargets.push_back(0); while(_token == TK_CASE) { if(!bfirst) { _fs->AddInstruction(_OP_JMP, 0, 0); skipcondjmp = _fs->GetCurrentPos(); _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); } //condition Lex(); Expression(); Expect(_SC(':')); SQInteger trg = _fs->PopTarget(); SQInteger eqtarget = trg; bool local = _fs->IsLocal(trg); if(local) { eqtarget = _fs->PushTarget(); //we need to allocate a extra reg } _fs->AddInstruction(_OP_EQ, eqtarget, trg, expr); _fs->AddInstruction(_OP_JZ, eqtarget, 0); if(local) { _fs->PopTarget(); } //end condition if(skipcondjmp != -1) { _fs->SetInstructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); } tonextcondjmp = _fs->GetCurrentPos(); BEGIN_SCOPE(); Statements(); END_SCOPE(); bfirst = false; } if(tonextcondjmp != -1) _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); if(_token == TK_DEFAULT) { Lex(); Expect(_SC(':')); BEGIN_SCOPE(); Statements(); END_SCOPE(); } Expect(_SC('}')); _fs->PopTarget(); __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__; if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__); _fs->_breaktargets.pop_back(); } void FunctionStatement() { SQObject id; Lex(); id = Expect(TK_IDENTIFIER); _fs->PushTarget(0); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); while(_token == TK_DOUBLE_COLON) { Lex(); id = Expect(TK_IDENTIFIER); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); } Expect(_SC('(')); CreateFunction(id); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); EmitDerefOp(_OP_NEWSLOT); _fs->PopTarget(); } void ClassStatement() { SQExpState es; Lex(); es = _es; _es.donot_get = true; PrefixedExpr(); if(_es.etype == EXPR) { Error(_SC("invalid class name")); } else if(_es.etype == OBJECT || _es.etype == BASE) { ClassExp(); EmitDerefOp(_OP_NEWSLOT); _fs->PopTarget(); } else { Error(_SC("cannot create a class in a local with the syntax(class )")); } _es = es; } SQObject ExpectScalar() { SQObject val; val._type = OT_NULL; val._unVal.nInteger = 0; //shut up GCC 4.x switch(_token) { case TK_INTEGER: val._type = OT_INTEGER; val._unVal.nInteger = _lex._nvalue; break; case TK_FLOAT: val._type = OT_FLOAT; val._unVal.fFloat = _lex._fvalue; break; case TK_STRING_LITERAL: val = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); break; case TK_TRUE: case TK_FALSE: val._type = OT_BOOL; val._unVal.nInteger = _token == TK_TRUE ? 1 : 0; break; case '-': Lex(); switch(_token) { case TK_INTEGER: val._type = OT_INTEGER; val._unVal.nInteger = -_lex._nvalue; break; case TK_FLOAT: val._type = OT_FLOAT; val._unVal.fFloat = -_lex._fvalue; break; default: Error(_SC("scalar expected : integer,float")); } break; default: Error(_SC("scalar expected : integer, float, or string")); } Lex(); return val; } void EnumStatement() { Lex(); SQObject id = Expect(TK_IDENTIFIER); Expect(_SC('{')); SQObject table = _fs->CreateTable(); SQInteger nval = 0; while(_token != _SC('}')) { SQObject key = Expect(TK_IDENTIFIER); SQObject val; if(_token == _SC('=')) { Lex(); val = ExpectScalar(); } else { val._type = OT_INTEGER; val._unVal.nInteger = nval++; } _table(table)->NewSlot(SQObjectPtr(key),SQObjectPtr(val)); if(_token == ',') Lex(); } SQTable *enums = _table(_ss(_vm)->_consts); SQObjectPtr strongid = id; enums->NewSlot(SQObjectPtr(strongid),SQObjectPtr(table)); strongid.Null(); Lex(); } void TryCatchStatement() { SQObject exid; Lex(); _fs->AddInstruction(_OP_PUSHTRAP,0,0); _fs->_traps++; if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++; if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++; SQInteger trappos = _fs->GetCurrentPos(); { BEGIN_SCOPE(); Statement(); END_SCOPE(); } _fs->_traps--; _fs->AddInstruction(_OP_POPTRAP, 1, 0); if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--; if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--; _fs->AddInstruction(_OP_JMP, 0, 0); SQInteger jmppos = _fs->GetCurrentPos(); _fs->SetInstructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos)); Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')')); { BEGIN_SCOPE(); SQInteger ex_target = _fs->PushLocalVariable(exid); _fs->SetInstructionParam(trappos, 0, ex_target); Statement(); _fs->SetInstructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); END_SCOPE(); } } void FunctionExp(SQInteger ftype,bool lambda = false) { Lex(); Expect(_SC('(')); SQObjectPtr dummy; CreateFunction(dummy,lambda); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1); } void ClassExp() { SQInteger base = -1; SQInteger attrs = -1; if(_token == TK_EXTENDS) { Lex(); Expression(); base = _fs->TopTarget(); } if(_token == TK_ATTR_OPEN) { Lex(); _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); ParseTableOrClass(_SC(','),TK_ATTR_CLOSE); attrs = _fs->TopTarget(); } Expect(_SC('{')); if(attrs != -1) _fs->PopTarget(); if(base != -1) _fs->PopTarget(); _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), base, attrs,NOT_CLASS); ParseTableOrClass(_SC(';'),_SC('}')); } void DeleteExpr() { SQExpState es; Lex(); es = _es; _es.donot_get = true; PrefixedExpr(); if(_es.etype==EXPR) Error(_SC("can't delete an expression")); if(_es.etype==OBJECT || _es.etype==BASE) { Emit2ArgsOP(_OP_DELETE); } else { Error(_SC("cannot delete an (outer) local")); } _es = es; } void PrefixIncDec(SQInteger token) { SQExpState es; SQInteger diff = (token==TK_MINUSMINUS) ? -1 : 1; Lex(); es = _es; _es.donot_get = true; PrefixedExpr(); if(_es.etype==EXPR) { Error(_SC("can't '++' or '--' an expression")); } else if(_es.etype==OBJECT || _es.etype==BASE) { Emit2ArgsOP(_OP_INC, diff); } else if(_es.etype==LOCAL) { SQInteger src = _fs->TopTarget(); _fs->AddInstruction(_OP_INCL, src, src, 0, diff); } else if(_es.etype==OUTER) { SQInteger tmp = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, tmp, _es.epos); _fs->AddInstruction(_OP_INCL, tmp, tmp, 0, diff); _fs->AddInstruction(_OP_SETOUTER, tmp, _es.epos, tmp); } _es = es; } void CreateFunction(SQObject &name,bool lambda = false) { SQFuncState *funcstate = _fs->PushChildState(_ss(_vm)); funcstate->_name = name; SQObject paramname; funcstate->AddParameter(_fs->CreateString(_SC("this"))); funcstate->_sourcename = _sourcename; SQInteger defparams = 0; while(_token!=_SC(')')) { if(_token == TK_VARPARAMS) { if(defparams > 0) Error(_SC("function with default parameters cannot have variable number of parameters")); funcstate->AddParameter(_fs->CreateString(_SC("vargv"))); funcstate->_varparams = true; Lex(); if(_token != _SC(')')) Error(_SC("expected ')'")); break; } else { paramname = Expect(TK_IDENTIFIER); funcstate->AddParameter(paramname); if(_token == _SC('=')) { Lex(); Expression(); funcstate->AddDefaultParam(_fs->TopTarget()); defparams++; } else { if(defparams > 0) Error(_SC("expected '='")); } if(_token == _SC(',')) Lex(); else if(_token != _SC(')')) Error(_SC("expected ')' or ','")); } } Expect(_SC(')')); for(SQInteger n = 0; n < defparams; n++) { _fs->PopTarget(); } SQFuncState *currchunk = _fs; _fs = funcstate; if(lambda) { Expression(); _fs->AddInstruction(_OP_RETURN, 1, _fs->PopTarget());} else { Statement(false); } funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true); funcstate->AddInstruction(_OP_RETURN, -1); funcstate->SetStackSize(0); SQFunctionProto *func = funcstate->BuildProto(); #ifdef _DEBUG_DUMP funcstate->Dump(func); #endif _fs = currchunk; _fs->_functions.push_back(func); _fs->PopChildState(); } void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve) { while(ntoresolve > 0) { SQInteger pos = funcstate->_unresolvedbreaks.back(); funcstate->_unresolvedbreaks.pop_back(); //set the jmp instruction funcstate->SetInstructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0); ntoresolve--; } } void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos) { while(ntoresolve > 0) { SQInteger pos = funcstate->_unresolvedcontinues.back(); funcstate->_unresolvedcontinues.pop_back(); //set the jmp instruction funcstate->SetInstructionParams(pos, 0, targetpos - pos, 0); ntoresolve--; } } private: SQInteger _token; SQFuncState *_fs; SQObjectPtr _sourcename; SQLexer _lex; bool _lineinfo; bool _raiseerror; SQInteger _debugline; SQInteger _debugop; SQExpState _es; SQScope _scope; SQChar _compilererror[MAX_COMPILER_ERROR_LEN]; jmp_buf _errorjmp; SQVM *_vm; }; bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo) { SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo); return p.Compile(out); } #endif simutrans-124.5/src/squirrel/squirrel/sqcompiler.h000066400000000000000000000034261520675460500224230ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQCOMPILER_H_ #define _SQCOMPILER_H_ struct SQVM; #define TK_IDENTIFIER 258 #define TK_STRING_LITERAL 259 #define TK_INTEGER 260 #define TK_FLOAT 261 #define TK_BASE 262 #define TK_DELETE 263 #define TK_EQ 264 #define TK_NE 265 #define TK_LE 266 #define TK_GE 267 #define TK_SWITCH 268 #define TK_ARROW 269 #define TK_AND 270 #define TK_OR 271 #define TK_IF 272 #define TK_ELSE 273 #define TK_WHILE 274 #define TK_BREAK 275 #define TK_FOR 276 #define TK_DO 277 #define TK_NULL 278 #define TK_FOREACH 279 #define TK_IN 280 #define TK_NEWSLOT 281 #define TK_MODULO 282 #define TK_LOCAL 283 #define TK_CLONE 284 #define TK_FUNCTION 285 #define TK_RETURN 286 #define TK_TYPEOF 287 #define TK_UMINUS 288 #define TK_PLUSEQ 289 #define TK_MINUSEQ 290 #define TK_CONTINUE 291 #define TK_YIELD 292 #define TK_TRY 293 #define TK_CATCH 294 #define TK_THROW 295 #define TK_SHIFTL 296 #define TK_SHIFTR 297 #define TK_RESUME 298 #define TK_DOUBLE_COLON 299 #define TK_CASE 300 #define TK_DEFAULT 301 #define TK_THIS 302 #define TK_PLUSPLUS 303 #define TK_MINUSMINUS 304 #define TK_3WAYSCMP 305 #define TK_USHIFTR 306 #define TK_CLASS 307 #define TK_EXTENDS 308 #define TK_CONSTRUCTOR 310 #define TK_INSTANCEOF 311 #define TK_VARPARAMS 312 #define TK___LINE__ 313 #define TK___FILE__ 314 #define TK_TRUE 315 #define TK_FALSE 316 #define TK_MULEQ 317 #define TK_DIVEQ 318 #define TK_MODEQ 319 #define TK_ATTR_OPEN 320 #define TK_ATTR_CLOSE 321 #define TK_STATIC 322 #define TK_ENUM 323 #define TK_CONST 324 #define TK_RAWCALL 325 typedef void(*CompilerErrorFunc)(void *ud, const SQChar *s); bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo); #endif simutrans-124.5/src/squirrel/squirrel/sqdebug.cc000066400000000000000000000072061520675460500220350ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include #include "sqvm.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqstring.h" SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi) { SQInteger cssize = v->_callsstacksize; if (cssize > level) { SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; if(sq_isclosure(ci._closure)) { SQClosure *c = _closure(ci._closure); SQFunctionProto *proto = c->_function; fi->funcid = proto; fi->name = sq_type(proto->_name) == OT_STRING?_stringval(proto->_name):_SC("unknown"); fi->source = sq_type(proto->_sourcename) == OT_STRING?_stringval(proto->_sourcename):_SC("unknown"); fi->line = proto->_lineinfos[0]._line; return SQ_OK; } } return sq_throwerror(v,_SC("the object is not a closure")); } SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si) { SQInteger cssize = v->_callsstacksize; if (cssize > level) { memset(si, 0, sizeof(SQStackInfos)); SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; switch (sq_type(ci._closure)) { case OT_CLOSURE:{ SQFunctionProto *func = _closure(ci._closure)->_function; if (sq_type(func->_name) == OT_STRING) si->funcname = _stringval(func->_name); if (sq_type(func->_sourcename) == OT_STRING) si->source = _stringval(func->_sourcename); si->line = func->GetLine(ci._ip); } break; case OT_NATIVECLOSURE: si->source = _SC("NATIVE"); si->funcname = _SC("unknown"); if(sq_type(_nativeclosure(ci._closure)->_name) == OT_STRING) si->funcname = _stringval(_nativeclosure(ci._closure)->_name); si->line = -1; break; default: break; //shutup compiler } return SQ_OK; } return SQ_ERROR; } void SQVM::Raise_Error(const SQChar *s, ...) { va_list vl; va_start(vl, s); Raise_Error_vl(s, vl); va_end(vl); } void SQVM::Raise_Error_vl(const SQChar *s, va_list vl) { SQInteger buffersize = (SQInteger)scstrlen(s)+(NUMBER_MAX_CHAR*2); scvsprintf(_sp(sq_rsl(buffersize)),buffersize, s, vl); _lasterror = SQString::Create(_ss(this),_spval,-1); } void SQVM::Raise_Error(const SQObjectPtr &desc) { _lasterror = desc; } SQString *SQVM::PrintObjVal(const SQObjectPtr &o) { switch(sq_type(o)) { case OT_STRING: return _string(o); case OT_INTEGER: scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR), _PRINT_INT_FMT, _integer(o)); return SQString::Create(_ss(this), _spval); break; case OT_FLOAT: scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)), sq_rsl(NUMBER_MAX_CHAR), _SC("%.14g"), _float(o)); return SQString::Create(_ss(this), _spval); break; default: return SQString::Create(_ss(this), GetTypeName(o)); } } void SQVM::Raise_IdxError(const SQObjectPtr &o) { SQObjectPtr oval = PrintObjVal(o); Raise_Error(_SC("the index '%.50s' does not exist"), _stringval(oval)); } void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2) { SQObjectPtr oval1 = PrintObjVal(o1), oval2 = PrintObjVal(o2); Raise_Error(_SC("comparison between '%.50s' and '%.50s'"), _stringval(oval1), _stringval(oval2)); } void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type) { SQObjectPtr exptypes = SQString::Create(_ss(this), _SC(""), -1); SQInteger found = 0; for(SQInteger i=0; i<16; i++) { SQInteger mask = ((SQInteger)1) << i; if(typemask & (mask)) { if(found>0) StringCat(exptypes,SQString::Create(_ss(this), _SC("|"), -1), exptypes); found ++; StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes); } } Raise_Error(_SC("parameter %d has an invalid type '%s' ; expected: '%s'"), nparam, IdType2Name((SQObjectType)type), _stringval(exptypes)); } simutrans-124.5/src/squirrel/squirrel/sqfuncproto.h000066400000000000000000000107101520675460500226220ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQFUNCTION_H_ #define _SQFUNCTION_H_ #include "sqopcodes.h" enum SQOuterType { otLOCAL = 0, otOUTER = 1 }; struct SQOuterVar { SQOuterVar(){} SQOuterVar(const SQObjectPtr &name,const SQObjectPtr &src,SQOuterType t) { _name = name; _src=src; _type=t; } SQOuterType _type; SQObjectPtr _name; SQObjectPtr _src; }; struct SQLocalVarInfo { SQLocalVarInfo():_start_op(0),_end_op(0),_pos(0){} SQObjectPtr _name; SQUnsignedInteger _start_op; SQUnsignedInteger _end_op; SQUnsignedInteger _pos; }; struct SQLineInfo { SQInteger _line;SQInteger _op; }; typedef sqvector SQOuterVarVec; typedef sqvector SQLocalVarInfoVec; typedef sqvector SQLineInfoVec; #define _FUNC_SIZE(ni,nl,nparams,nfuncs,nouters,nlineinf,localinf,defparams) (sizeof(SQFunctionProto) \ +((ni-1)*sizeof(SQInstruction))+(nl*sizeof(SQObjectPtr)) \ +(nparams*sizeof(SQObjectPtr))+(nfuncs*sizeof(SQObjectPtr)) \ +(nouters*sizeof(SQOuterVar))+(nlineinf*sizeof(SQLineInfo)) \ +(localinf*sizeof(SQLocalVarInfo))+(defparams*sizeof(SQInteger))) struct SQFunctionProto : public CHAINABLE_OBJ { private: SQFunctionProto(SQSharedState *ss); ~SQFunctionProto(); public: static SQFunctionProto *Create(SQSharedState *ss,SQInteger ninstructions, SQInteger nliterals,SQInteger nparameters, SQInteger nfunctions,SQInteger noutervalues, SQInteger nlineinfos,SQInteger nlocalvarinfos,SQInteger ndefaultparams) { SQFunctionProto *f; //I compact the whole class and members in a single memory allocation f = (SQFunctionProto *)sq_vm_malloc(_FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams)); new (f) SQFunctionProto(ss); f->_ninstructions = ninstructions; f->_literals = (SQObjectPtr*)&f->_instructions[ninstructions]; f->_nliterals = nliterals; f->_parameters = (SQObjectPtr*)&f->_literals[nliterals]; f->_nparameters = nparameters; f->_functions = (SQObjectPtr*)&f->_parameters[nparameters]; f->_nfunctions = nfunctions; f->_outervalues = (SQOuterVar*)&f->_functions[nfunctions]; f->_noutervalues = noutervalues; f->_lineinfos = (SQLineInfo *)&f->_outervalues[noutervalues]; f->_nlineinfos = nlineinfos; f->_localvarinfos = (SQLocalVarInfo *)&f->_lineinfos[nlineinfos]; f->_nlocalvarinfos = nlocalvarinfos; f->_defaultparams = (SQInteger *)&f->_localvarinfos[nlocalvarinfos]; f->_ndefaultparams = ndefaultparams; _CONSTRUCT_VECTOR(SQObjectPtr,f->_nliterals,f->_literals); _CONSTRUCT_VECTOR(SQObjectPtr,f->_nparameters,f->_parameters); _CONSTRUCT_VECTOR(SQObjectPtr,f->_nfunctions,f->_functions); _CONSTRUCT_VECTOR(SQOuterVar,f->_noutervalues,f->_outervalues); //_CONSTRUCT_VECTOR(SQLineInfo,f->_nlineinfos,f->_lineinfos); //not required are 2 integers _CONSTRUCT_VECTOR(SQLocalVarInfo,f->_nlocalvarinfos,f->_localvarinfos); return f; } void Release(){ _DESTRUCT_VECTOR(SQObjectPtr,_nliterals,_literals); _DESTRUCT_VECTOR(SQObjectPtr,_nparameters,_parameters); _DESTRUCT_VECTOR(SQObjectPtr,_nfunctions,_functions); _DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues); //_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers _DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos); SQInteger size = _FUNC_SIZE(_ninstructions,_nliterals,_nparameters,_nfunctions,_noutervalues,_nlineinfos,_nlocalvarinfos,_ndefaultparams); this->~SQFunctionProto(); sq_vm_free(this,size); } const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop); SQInteger GetLine(SQInstruction *curr); bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize(){ _NULL_SQOBJECT_VECTOR(_literals,_nliterals); } SQObjectType GetType() {return OT_FUNCPROTO;} #endif SQObjectPtr _sourcename; SQObjectPtr _name; SQInteger _stacksize; bool _bgenerator; SQInteger _varparams; SQInteger _nlocalvarinfos; SQLocalVarInfo *_localvarinfos; SQInteger _nlineinfos; SQLineInfo *_lineinfos; SQInteger _nliterals; SQObjectPtr *_literals; SQInteger _nparameters; SQObjectPtr *_parameters; SQInteger _nfunctions; SQObjectPtr *_functions; SQInteger _noutervalues; SQOuterVar *_outervalues; SQInteger _ndefaultparams; SQInteger *_defaultparams; SQInteger _ninstructions; SQInstruction _instructions[1]; }; #endif simutrans-124.5/src/squirrel/squirrel/sqfuncstate.cc000066400000000000000000000401611520675460500227400ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #ifndef NO_COMPILER #include "sqcompiler.h" #include "sqstring.h" #include "sqfuncproto.h" #include "sqtable.h" #include "sqopcodes.h" #include "sqfuncstate.h" #ifdef _DEBUG_DUMP SQInstructionDesc g_InstrDesc[]={ {_SC("_OP_LINE")}, {_SC("_OP_LOAD")}, {_SC("_OP_LOADINT")}, {_SC("_OP_LOADFLOAT")}, {_SC("_OP_DLOAD")}, {_SC("_OP_TAILCALL")}, {_SC("_OP_CALL")}, {_SC("_OP_PREPCALL")}, {_SC("_OP_PREPCALLK")}, {_SC("_OP_GETK")}, {_SC("_OP_MOVE")}, {_SC("_OP_NEWSLOT")}, {_SC("_OP_DELETE")}, {_SC("_OP_SET")}, {_SC("_OP_GET")}, {_SC("_OP_EQ")}, {_SC("_OP_NE")}, {_SC("_OP_ADD")}, {_SC("_OP_SUB")}, {_SC("_OP_MUL")}, {_SC("_OP_DIV")}, {_SC("_OP_MOD")}, {_SC("_OP_BITW")}, {_SC("_OP_RETURN")}, {_SC("_OP_LOADNULLS")}, {_SC("_OP_LOADROOT")}, {_SC("_OP_LOADBOOL")}, {_SC("_OP_DMOVE")}, {_SC("_OP_JMP")}, {_SC("_OP_JCMP")}, {_SC("_OP_JZ")}, {_SC("_OP_SETOUTER")}, {_SC("_OP_GETOUTER")}, {_SC("_OP_NEWOBJ")}, {_SC("_OP_APPENDARRAY")}, {_SC("_OP_COMPARITH")}, {_SC("_OP_INC")}, {_SC("_OP_INCL")}, {_SC("_OP_PINC")}, {_SC("_OP_PINCL")}, {_SC("_OP_CMP")}, {_SC("_OP_EXISTS")}, {_SC("_OP_INSTANCEOF")}, {_SC("_OP_AND")}, {_SC("_OP_OR")}, {_SC("_OP_NEG")}, {_SC("_OP_NOT")}, {_SC("_OP_BWNOT")}, {_SC("_OP_CLOSURE")}, {_SC("_OP_YIELD")}, {_SC("_OP_RESUME")}, {_SC("_OP_FOREACH")}, {_SC("_OP_POSTFOREACH")}, {_SC("_OP_CLONE")}, {_SC("_OP_TYPEOF")}, {_SC("_OP_PUSHTRAP")}, {_SC("_OP_POPTRAP")}, {_SC("_OP_THROW")}, {_SC("_OP_NEWSLOTA")}, {_SC("_OP_GETBASE")}, {_SC("_OP_CLOSE")}, }; #endif void DumpLiteral(SQObjectPtr &o) { switch(sq_type(o)){ case OT_STRING: scprintf(_SC("\"%s\""),_stringval(o));break; case OT_FLOAT: scprintf(_SC("{%f}"),_float(o));break; case OT_INTEGER: scprintf(_SC("{") _PRINT_INT_FMT _SC("}"),_integer(o));break; case OT_BOOL: scprintf(_SC("%s"),_integer(o)?_SC("true"):_SC("false"));break; default: scprintf(_SC("(%s %p)"),GetTypeName(o),(void*)_rawval(o));break; break; //shut up compiler } } SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed) { _nliterals = 0; _literals = SQTable::Create(ss,0); _strings = SQTable::Create(ss,0); _sharedstate = ss; _lastline = 0; _optimization = true; _parent = parent; _stacksize = 0; _traps = 0; _returnexp = 0; _varparams = false; _errfunc = efunc; _errtarget = ed; _bgenerator = false; _outers = 0; _ss = ss; } void SQFuncState::Error(const SQChar *err) { _errfunc(_errtarget,err); } #ifdef _DEBUG_DUMP void SQFuncState::Dump(SQFunctionProto *func) { SQUnsignedInteger n=0,i; SQInteger si; scprintf(_SC("SQInstruction sizeof %d\n"),(SQInt32)sizeof(SQInstruction)); scprintf(_SC("SQObject sizeof %d\n"), (SQInt32)sizeof(SQObject)); scprintf(_SC("--------------------------------------------------------------------\n")); scprintf(_SC("*****FUNCTION [%s]\n"),sq_type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown")); scprintf(_SC("-----LITERALS\n")); SQObjectPtr refidx,key,val; SQInteger idx; SQObjectPtrVec templiterals; templiterals.resize(_nliterals); while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) { refidx=idx; templiterals[_integer(val)]=key; } for(i=0;i>\n")); n=0; for(i=0;i<_parameters.size();i++){ scprintf(_SC("[%d] "), (SQInt32)n); DumpLiteral(_parameters[i]); scprintf(_SC("\n")); n++; } scprintf(_SC("-----LOCALS\n")); for(si=0;si_nlocalvarinfos;si++){ SQLocalVarInfo lvi=func->_localvarinfos[si]; scprintf(_SC("[%d] %s \t%d %d\n"), (SQInt32)lvi._pos,_stringval(lvi._name), (SQInt32)lvi._start_op, (SQInt32)lvi._end_op); n++; } scprintf(_SC("-----LINE INFO\n")); for(i=0;i<_lineinfos.size();i++){ SQLineInfo li=_lineinfos[i]; scprintf(_SC("op [%d] line [%d] \n"), (SQInt32)li._op, (SQInt32)li._line); n++; } scprintf(_SC("-----dump\n")); n=0; for(i=0;i<_instructions.size();i++){ SQInstruction &inst=_instructions[i]; if(inst.op==_OP_LOAD || inst.op==_OP_DLOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){ SQInteger lidx = inst._arg1; scprintf(_SC("[%03d] %15s %d "), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0); if(lidx >= 0xFFFFFFFF) scprintf(_SC("null")); else { SQInteger refidx; SQObjectPtr val,key,refo; while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) { refo = refidx; } DumpLiteral(key); } if(inst.op != _OP_DLOAD) { scprintf(_SC(" %d %d \n"),inst._arg2,inst._arg3); } else { scprintf(_SC(" %d "),inst._arg2); lidx = inst._arg3; if(lidx >= 0xFFFFFFFF) scprintf(_SC("null")); else { SQInteger refidx; SQObjectPtr val,key,refo; while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) { refo = refidx; } DumpLiteral(key); scprintf(_SC("\n")); } } } else if(inst.op==_OP_LOADFLOAT) { scprintf(_SC("[%03d] %15s %d %f %d %d\n"), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3); } /* else if(inst.op==_OP_ARITH){ scprintf(_SC("[%03d] %15s %d %d %d %c\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); }*/ else { scprintf(_SC("[%03d] %15s %d %d %d %d\n"), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); } n++; } scprintf(_SC("-----\n")); scprintf(_SC("stack size[%d]\n"), (SQInt32)func->_stacksize); scprintf(_SC("--------------------------------------------------------------------\n\n")); } #endif SQInteger SQFuncState::GetNumericConstant(const SQInteger cons) { return GetConstant(SQObjectPtr(cons)); } SQInteger SQFuncState::GetNumericConstant(const SQFloat cons) { return GetConstant(SQObjectPtr(cons)); } SQInteger SQFuncState::GetConstant(const SQObject &cons) { SQObjectPtr val; if(!_table(_literals)->Get(cons,val)) { val = _nliterals; _table(_literals)->NewSlot(cons,val); _nliterals++; if(_nliterals > MAX_LITERALS) { val.Null(); Error(_SC("internal compiler error: too many literals")); } } return _integer(val); } void SQFuncState::SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3) { _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0); _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1); _instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2); _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3); } void SQFuncState::SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val) { switch(arg){ case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break; case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break; case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break; case 3:_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&val);break; }; } SQInteger SQFuncState::AllocStackPos() { SQInteger npos=_vlocals.size(); _vlocals.push_back(SQLocalVarInfo()); if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) { if(_stacksize>MAX_FUNC_STACKSIZE) Error(_SC("internal compiler error: too many locals")); _stacksize=_vlocals.size(); } return npos; } SQInteger SQFuncState::PushTarget(SQInteger n) { if(n!=-1){ _targetstack.push_back(n); return n; } n=AllocStackPos(); _targetstack.push_back(n); return n; } SQInteger SQFuncState::GetUpTarget(SQInteger n){ return _targetstack[((_targetstack.size()-1)-n)]; } SQInteger SQFuncState::TopTarget(){ return _targetstack.back(); } SQInteger SQFuncState::PopTarget() { SQUnsignedInteger npos=_targetstack.back(); assert(npos < _vlocals.size()); SQLocalVarInfo &t = _vlocals[npos]; if(sq_type(t._name)==OT_NULL){ _vlocals.pop_back(); } _targetstack.pop_back(); return npos; } SQInteger SQFuncState::GetStackSize() { return _vlocals.size(); } SQInteger SQFuncState::CountOuters(SQInteger stacksize) { SQInteger outers = 0; SQInteger k = _vlocals.size() - 1; while(k >= stacksize) { SQLocalVarInfo &lvi = _vlocals[k]; k--; if(lvi._end_op == UINT_MINUS_ONE) { //this means is an outer outers++; } } return outers; } void SQFuncState::SetStackSize(SQInteger n) { SQInteger size=_vlocals.size(); while(size>n){ size--; SQLocalVarInfo lvi = _vlocals.back(); if(sq_type(lvi._name)!=OT_NULL){ if(lvi._end_op == UINT_MINUS_ONE) { //this means is an outer _outers--; } lvi._end_op = GetCurrentPos(); _localvarinfos.push_back(lvi); } _vlocals.pop_back(); } } bool SQFuncState::IsConstant(const SQObject &name,SQObject &e) { SQObjectPtr val; if(_table(_sharedstate->_consts)->Get(name,val)) { e = val; return true; } return false; } bool SQFuncState::IsLocal(SQUnsignedInteger stkpos) { if(stkpos>=_vlocals.size())return false; else if(sq_type(_vlocals[stkpos]._name)!=OT_NULL)return true; return false; } SQInteger SQFuncState::PushLocalVariable(const SQObject &name) { SQInteger pos=_vlocals.size(); SQLocalVarInfo lvi; lvi._name=name; lvi._start_op=GetCurrentPos()+1; lvi._pos=_vlocals.size(); _vlocals.push_back(lvi); if(_vlocals.size()>((SQUnsignedInteger)_stacksize))_stacksize=_vlocals.size(); return pos; } SQInteger SQFuncState::GetLocalVariable(const SQObject &name) { SQInteger locals=_vlocals.size(); while(locals>=1){ SQLocalVarInfo &lvi = _vlocals[locals-1]; if(sq_type(lvi._name)==OT_STRING && _string(lvi._name)==_string(name)){ return locals-1; } locals--; } return -1; } void SQFuncState::MarkLocalAsOuter(SQInteger pos) { SQLocalVarInfo &lvi = _vlocals[pos]; lvi._end_op = UINT_MINUS_ONE; _outers++; } SQInteger SQFuncState::GetOuterVariable(const SQObject &name) { SQInteger outers = _outervalues.size(); for(SQInteger i = 0; iGetLocalVariable(name); if(pos == -1) { pos = _parent->GetOuterVariable(name); if(pos != -1) { _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otOUTER)); //local return _outervalues.size() - 1; } } else { _parent->MarkLocalAsOuter(pos); _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otLOCAL)); //local return _outervalues.size() - 1; } } return -1; } void SQFuncState::AddParameter(const SQObject &name) { PushLocalVariable(name); _parameters.push_back(name); } void SQFuncState::AddLineInfos(SQInteger line,bool lineop,bool force) { if(_lastline!=line || force){ SQLineInfo li; li._line=line;li._op=(GetCurrentPos()+1); if(lineop)AddInstruction(_OP_LINE,0,line); if(_lastline!=line) { _lineinfos.push_back(li); } _lastline=line; } } void SQFuncState::DiscardTarget() { SQInteger discardedtarget = PopTarget(); SQInteger size = _instructions.size(); if(size > 0 && _optimization){ SQInstruction &pi = _instructions[size-1];//previous instruction switch(pi.op) { case _OP_SET:case _OP_NEWSLOT:case _OP_SETOUTER:case _OP_CALL: if(pi._arg0 == discardedtarget) { pi._arg0 = 0xFF; } } } } void SQFuncState::AddInstruction(SQInstruction &i) { SQInteger size = _instructions.size(); if(size > 0 && _optimization){ //simple optimizer SQInstruction &pi = _instructions[size-1];//previous instruction switch(i.op) { case _OP_JZ: if( pi.op == _OP_CMP && pi._arg1 < 0xFF) { pi.op = _OP_JCMP; pi._arg0 = (unsigned char)pi._arg1; pi._arg1 = i._arg1; return; } break; case _OP_SET: case _OP_NEWSLOT: if(i._arg0 == i._arg3) { i._arg0 = 0xFF; } break; case _OP_SETOUTER: if(i._arg0 == i._arg2) { i._arg0 = 0xFF; } break; case _OP_RETURN: if( _parent && i._arg0 != MAX_FUNC_STACKSIZE && pi.op == _OP_CALL && _returnexp < size-1) { pi.op = _OP_TAILCALL; } else if(pi.op == _OP_CLOSE){ pi = i; return; } break; case _OP_GET: if( pi.op == _OP_LOAD && pi._arg0 == i._arg2 && (!IsLocal(pi._arg0))){ pi._arg2 = (unsigned char)i._arg1; pi.op = _OP_GETK; pi._arg0 = i._arg0; return; } break; case _OP_PREPCALL: if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ pi.op = _OP_PREPCALLK; pi._arg0 = i._arg0; pi._arg2 = i._arg2; pi._arg3 = i._arg3; return; } break; case _OP_APPENDARRAY: { SQInteger aat = -1; switch(pi.op) { case _OP_LOAD: aat = AAT_LITERAL; break; case _OP_LOADINT: aat = AAT_INT; break; case _OP_LOADBOOL: aat = AAT_BOOL; break; case _OP_LOADFLOAT: aat = AAT_FLOAT; break; default: break; } if(aat != -1 && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ pi.op = _OP_APPENDARRAY; pi._arg0 = i._arg0; pi._arg2 = (unsigned char)aat; pi._arg3 = MAX_FUNC_STACKSIZE; return; } } break; case _OP_MOVE: switch(pi.op) { case _OP_GET: case _OP_ADD: case _OP_SUB: case _OP_MUL: case _OP_DIV: case _OP_MOD: case _OP_BITW: case _OP_LOADINT: case _OP_LOADFLOAT: case _OP_LOADBOOL: case _OP_LOAD: if(pi._arg0 == i._arg1) { pi._arg0 = i._arg0; _optimization = false; //_result_elimination = false; return; } } if(pi.op == _OP_MOVE) { pi.op = _OP_DMOVE; pi._arg2 = i._arg0; pi._arg3 = (unsigned char)i._arg1; return; } break; case _OP_LOAD: if(pi.op == _OP_LOAD && i._arg1 < 256) { pi.op = _OP_DLOAD; pi._arg2 = i._arg0; pi._arg3 = (unsigned char)i._arg1; return; } break; case _OP_EQ:case _OP_NE: if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0) )) { pi.op = i.op; pi._arg0 = i._arg0; pi._arg2 = i._arg2; pi._arg3 = MAX_FUNC_STACKSIZE; return; } break; case _OP_LOADNULLS: if((pi.op == _OP_LOADNULLS && pi._arg0+pi._arg1 == i._arg0)) { pi._arg1 = pi._arg1 + 1; pi.op = _OP_LOADNULLS; return; } break; case _OP_LINE: if(pi.op == _OP_LINE) { _instructions.pop_back(); _lineinfos.pop_back(); } break; } } _optimization = true; _instructions.push_back(i); } SQObject SQFuncState::CreateString(const SQChar *s,SQInteger len) { SQObjectPtr ns(SQString::Create(_sharedstate,s,len)); _table(_strings)->NewSlot(ns,(SQInteger)1); return ns; } SQObject SQFuncState::CreateTable() { SQObjectPtr nt(SQTable::Create(_sharedstate,0)); _table(_strings)->NewSlot(nt,(SQInteger)1); return nt; } SQFunctionProto *SQFuncState::BuildProto() { SQFunctionProto *f=SQFunctionProto::Create(_ss,_instructions.size(), _nliterals,_parameters.size(),_functions.size(),_outervalues.size(), _lineinfos.size(),_localvarinfos.size(),_defaultparams.size()); SQObjectPtr refidx,key,val; SQInteger idx; f->_stacksize = _stacksize; f->_sourcename = _sourcename; f->_bgenerator = _bgenerator; f->_name = _name; while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) { f->_literals[_integer(val)]=key; refidx=idx; } for(SQUnsignedInteger nf = 0; nf < _functions.size(); nf++) f->_functions[nf] = _functions[nf]; for(SQUnsignedInteger np = 0; np < _parameters.size(); np++) f->_parameters[np] = _parameters[np]; for(SQUnsignedInteger no = 0; no < _outervalues.size(); no++) f->_outervalues[no] = _outervalues[no]; for(SQUnsignedInteger nl = 0; nl < _localvarinfos.size(); nl++) f->_localvarinfos[nl] = _localvarinfos[nl]; for(SQUnsignedInteger ni = 0; ni < _lineinfos.size(); ni++) f->_lineinfos[ni] = _lineinfos[ni]; for(SQUnsignedInteger nd = 0; nd < _defaultparams.size(); nd++) f->_defaultparams[nd] = _defaultparams[nd]; memcpy(f->_instructions,&_instructions[0],_instructions.size()*sizeof(SQInstruction)); f->_varparams = _varparams; return f; } SQFuncState *SQFuncState::PushChildState(SQSharedState *ss) { SQFuncState *child = (SQFuncState *)sq_malloc(sizeof(SQFuncState)); new (child) SQFuncState(ss,this,_errfunc,_errtarget); _childstates.push_back(child); return child; } void SQFuncState::PopChildState() { SQFuncState *child = _childstates.back(); sq_delete(child,SQFuncState); _childstates.pop_back(); } SQFuncState::~SQFuncState() { while(_childstates.size() > 0) { PopChildState(); } } #endif simutrans-124.5/src/squirrel/squirrel/sqfuncstate.h000066400000000000000000000061461520675460500226070ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQFUNCSTATE_H_ #define _SQFUNCSTATE_H_ /////////////////////////////////// #include "squtils.h" struct SQFuncState { SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed); ~SQFuncState(); #ifdef _DEBUG_DUMP void Dump(SQFunctionProto *func); #endif void Error(const SQChar *err); SQFuncState *PushChildState(SQSharedState *ss); void PopChildState(); void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);} void AddInstruction(SQInstruction &i); void SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0); void SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val); SQInstruction &GetInstruction(SQInteger pos){return _instructions[pos];} void PopInstructions(SQInteger size){for(SQInteger i=0;i _childstates; SQInteger GetConstant(const SQObject &cons); private: CompilerErrorFunc _errfunc; void *_errtarget; SQSharedState *_ss; }; #endif simutrans-124.5/src/squirrel/squirrel/sqlexer.cc000066400000000000000000000327201520675460500220650ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include #include #include "sqtable.h" #include "sqstring.h" #include "sqcompiler.h" #include "sqlexer.h" #define CUR_CHAR (_currdata) #define RETURN_TOKEN(t) { _prevtoken = _curtoken; _curtoken = t; return t;} #define IS_EOB() (CUR_CHAR <= SQUIRREL_EOB) #define NEXT() {Next();_currentcolumn++;} #define INIT_TEMP_STRING() { _longstr.resize(0);} #define APPEND_CHAR(c) { _longstr.push_back(c);} #define TERMINATE_BUFFER() {_longstr.push_back(_SC('\0'));} #define ADD_KEYWORD(key,id) _keywords->NewSlot( SQString::Create(ss, _SC(#key)) ,SQInteger(id)) SQLexer::SQLexer(){} SQLexer::~SQLexer() { _keywords->Release(); } void SQLexer::Init(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up,CompilerErrorFunc efunc,void *ed) { _errfunc = efunc; _errtarget = ed; _sharedstate = ss; _keywords = SQTable::Create(ss, 37); ADD_KEYWORD(while, TK_WHILE); ADD_KEYWORD(do, TK_DO); ADD_KEYWORD(if, TK_IF); ADD_KEYWORD(else, TK_ELSE); ADD_KEYWORD(break, TK_BREAK); ADD_KEYWORD(continue, TK_CONTINUE); ADD_KEYWORD(return, TK_RETURN); ADD_KEYWORD(null, TK_NULL); ADD_KEYWORD(function, TK_FUNCTION); ADD_KEYWORD(local, TK_LOCAL); ADD_KEYWORD(for, TK_FOR); ADD_KEYWORD(foreach, TK_FOREACH); ADD_KEYWORD(in, TK_IN); ADD_KEYWORD(typeof, TK_TYPEOF); ADD_KEYWORD(base, TK_BASE); ADD_KEYWORD(delete, TK_DELETE); ADD_KEYWORD(try, TK_TRY); ADD_KEYWORD(catch, TK_CATCH); ADD_KEYWORD(throw, TK_THROW); ADD_KEYWORD(clone, TK_CLONE); ADD_KEYWORD(yield, TK_YIELD); ADD_KEYWORD(resume, TK_RESUME); ADD_KEYWORD(switch, TK_SWITCH); ADD_KEYWORD(case, TK_CASE); ADD_KEYWORD(default, TK_DEFAULT); ADD_KEYWORD(this, TK_THIS); ADD_KEYWORD(class,TK_CLASS); ADD_KEYWORD(extends,TK_EXTENDS); ADD_KEYWORD(constructor,TK_CONSTRUCTOR); ADD_KEYWORD(instanceof,TK_INSTANCEOF); ADD_KEYWORD(true,TK_TRUE); ADD_KEYWORD(false,TK_FALSE); ADD_KEYWORD(static,TK_STATIC); ADD_KEYWORD(enum,TK_ENUM); ADD_KEYWORD(const,TK_CONST); ADD_KEYWORD(__LINE__,TK___LINE__); ADD_KEYWORD(__FILE__,TK___FILE__); ADD_KEYWORD(rawcall, TK_RAWCALL); _readf = rg; _up = up; _lasttokenline = _currentline = 1; _currentcolumn = 0; _prevtoken = -1; _reached_eof = SQFalse; Next(); } void SQLexer::Error(const SQChar *err) { _errfunc(_errtarget,err); } void SQLexer::Next() { SQInteger t = _readf(_up); if(t > MAX_CHAR) Error(_SC("Invalid character")); if(t != 0) { _currdata = (LexChar)t; return; } _currdata = SQUIRREL_EOB; _reached_eof = SQTrue; } const SQChar *SQLexer::Tok2Str(SQInteger tok) { SQObjectPtr itr, key, val; SQInteger nitr; while((nitr = _keywords->Next(false,itr, key, val)) != -1) { itr = (SQInteger)nitr; if(((SQInteger)_integer(val)) == tok) return _stringval(key); } return NULL; } void SQLexer::LexBlockComment() { bool done = false; while(!done) { switch(CUR_CHAR) { case _SC('*'): { NEXT(); if(CUR_CHAR == _SC('/')) { done = true; NEXT(); }}; continue; case _SC('\n'): _currentline++; NEXT(); continue; case SQUIRREL_EOB: Error(_SC("missing \"*/\" in comment")); default: NEXT(); } } } void SQLexer::LexLineComment() { do { NEXT(); } while (CUR_CHAR != _SC('\n') && (!IS_EOB())); } SQInteger SQLexer::Lex() { _lasttokenline = _currentline; while(CUR_CHAR != SQUIRREL_EOB) { switch(CUR_CHAR){ case _SC('\t'): case _SC('\r'): case _SC(' '): NEXT(); continue; case _SC('\n'): _currentline++; _prevtoken=_curtoken; _curtoken=_SC('\n'); NEXT(); _currentcolumn=1; continue; case _SC('#'): LexLineComment(); continue; case _SC('/'): NEXT(); switch(CUR_CHAR){ case _SC('*'): NEXT(); LexBlockComment(); continue; case _SC('/'): LexLineComment(); continue; case _SC('='): NEXT(); RETURN_TOKEN(TK_DIVEQ); continue; case _SC('>'): NEXT(); RETURN_TOKEN(TK_ATTR_CLOSE); continue; default: RETURN_TOKEN('/'); } case _SC('='): NEXT(); if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('=') } else { NEXT(); RETURN_TOKEN(TK_EQ); } case _SC('<'): NEXT(); switch(CUR_CHAR) { case _SC('='): NEXT(); if(CUR_CHAR == _SC('>')) { NEXT(); RETURN_TOKEN(TK_3WAYSCMP); } RETURN_TOKEN(TK_LE) break; case _SC('-'): NEXT(); RETURN_TOKEN(TK_NEWSLOT); break; case _SC('<'): NEXT(); RETURN_TOKEN(TK_SHIFTL); break; case _SC('/'): NEXT(); RETURN_TOKEN(TK_ATTR_OPEN); break; } RETURN_TOKEN('<'); case _SC('>'): NEXT(); if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_GE);} else if(CUR_CHAR == _SC('>')){ NEXT(); if(CUR_CHAR == _SC('>')){ NEXT(); RETURN_TOKEN(TK_USHIFTR); } RETURN_TOKEN(TK_SHIFTR); } else { RETURN_TOKEN('>') } case _SC('!'): NEXT(); if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('!')} else { NEXT(); RETURN_TOKEN(TK_NE); } case _SC('@'): { SQInteger stype; NEXT(); if(CUR_CHAR != _SC('"')) { RETURN_TOKEN('@'); } if((stype=ReadString('"',true))!=-1) { RETURN_TOKEN(stype); } Error(_SC("error parsing the string")); } case _SC('"'): case _SC('\''): { SQInteger stype; if((stype=ReadString(CUR_CHAR,false))!=-1){ RETURN_TOKEN(stype); } Error(_SC("error parsing the string")); } case _SC('{'): case _SC('}'): case _SC('('): case _SC(')'): case _SC('['): case _SC(']'): case _SC(';'): case _SC(','): case _SC('?'): case _SC('^'): case _SC('~'): {SQInteger ret = CUR_CHAR; NEXT(); RETURN_TOKEN(ret); } case _SC('.'): NEXT(); if (CUR_CHAR != _SC('.')){ RETURN_TOKEN('.') } NEXT(); if (CUR_CHAR != _SC('.')){ Error(_SC("invalid token '..'")); } NEXT(); RETURN_TOKEN(TK_VARPARAMS); case _SC('&'): NEXT(); if (CUR_CHAR != _SC('&')){ RETURN_TOKEN('&') } else { NEXT(); RETURN_TOKEN(TK_AND); } case _SC('|'): NEXT(); if (CUR_CHAR != _SC('|')){ RETURN_TOKEN('|') } else { NEXT(); RETURN_TOKEN(TK_OR); } case _SC(':'): NEXT(); if (CUR_CHAR != _SC(':')){ RETURN_TOKEN(':') } else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); } case _SC('*'): NEXT(); if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MULEQ);} else RETURN_TOKEN('*'); case _SC('%'): NEXT(); if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MODEQ);} else RETURN_TOKEN('%'); case _SC('-'): NEXT(); if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);} else if (CUR_CHAR == _SC('-')){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);} else RETURN_TOKEN('-'); case _SC('+'): NEXT(); if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);} else if (CUR_CHAR == _SC('+')){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);} else RETURN_TOKEN('+'); case SQUIRREL_EOB: return 0; default:{ if (scisdigit(CUR_CHAR)) { SQInteger ret = ReadNumber(); RETURN_TOKEN(ret); } else if (scisalpha(CUR_CHAR) || CUR_CHAR == _SC('_')) { SQInteger t = ReadID(); RETURN_TOKEN(t); } else { SQInteger c = CUR_CHAR; if (sciscntrl((int)c)) Error(_SC("unexpected character(control)")); NEXT(); RETURN_TOKEN(c); } RETURN_TOKEN(0); } } } return 0; } SQInteger SQLexer::GetIDType(const SQChar *s,SQInteger len) { SQObjectPtr t; if(_keywords->GetStr(s,len, t)) { return SQInteger(_integer(t)); } return TK_IDENTIFIER; } #ifdef SQUNICODE #if WCHAR_SIZE == 2 SQInteger SQLexer::AddUTF16(SQUnsignedInteger ch) { if (ch >= 0x10000) { SQUnsignedInteger code = (ch - 0x10000); APPEND_CHAR((SQChar)(0xD800 | (code >> 10))); APPEND_CHAR((SQChar)(0xDC00 | (code & 0x3FF))); return 2; } else { APPEND_CHAR((SQChar)ch); return 1; } } #endif #else SQInteger SQLexer::AddUTF8(SQUnsignedInteger ch) { if (ch < 0x80) { APPEND_CHAR((char)ch); return 1; } if (ch < 0x800) { APPEND_CHAR((SQChar)((ch >> 6) | 0xC0)); APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); return 2; } if (ch < 0x10000) { APPEND_CHAR((SQChar)((ch >> 12) | 0xE0)); APPEND_CHAR((SQChar)(((ch >> 6) & 0x3F) | 0x80)); APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); return 3; } if (ch < 0x110000) { APPEND_CHAR((SQChar)((ch >> 18) | 0xF0)); APPEND_CHAR((SQChar)(((ch >> 12) & 0x3F) | 0x80)); APPEND_CHAR((SQChar)(((ch >> 6) & 0x3F) | 0x80)); APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); return 4; } return 0; } #endif SQInteger SQLexer::ProcessStringHexEscape(SQChar *dest, SQInteger maxdigits) { NEXT(); if (!isxdigit(CUR_CHAR)) Error(_SC("hexadecimal number expected")); SQInteger n = 0; while (isxdigit(CUR_CHAR) && n < maxdigits) { dest[n] = CUR_CHAR; n++; NEXT(); } dest[n] = 0; return n; } SQInteger SQLexer::ReadString(SQInteger ndelim,bool verbatim) { INIT_TEMP_STRING(); NEXT(); if(IS_EOB()) return -1; for(;;) { while(CUR_CHAR != ndelim) { SQInteger x = CUR_CHAR; switch (x) { case SQUIRREL_EOB: Error(_SC("unfinished string")); return -1; case _SC('\n'): if(!verbatim) Error(_SC("newline in a constant")); APPEND_CHAR(CUR_CHAR); NEXT(); _currentline++; break; case _SC('\\'): if(verbatim) { APPEND_CHAR('\\'); NEXT(); } else { NEXT(); switch(CUR_CHAR) { case _SC('x'): { const SQInteger maxdigits = sizeof(SQChar) * 2; SQChar temp[maxdigits + 1]; ProcessStringHexEscape(temp, maxdigits); SQChar *stemp; APPEND_CHAR((SQChar)scstrtoul(temp, &stemp, 16)); } break; case _SC('U'): case _SC('u'): { const SQInteger maxdigits = CUR_CHAR == 'u' ? 4 : 8; SQChar temp[8 + 1]; ProcessStringHexEscape(temp, maxdigits); SQChar *stemp; #ifdef SQUNICODE #if WCHAR_SIZE == 2 AddUTF16(scstrtoul(temp, &stemp, 16)); #else APPEND_CHAR((SQChar)scstrtoul(temp, &stemp, 16)); #endif #else AddUTF8(scstrtoul(temp, &stemp, 16)); #endif } break; case _SC('t'): APPEND_CHAR(_SC('\t')); NEXT(); break; case _SC('a'): APPEND_CHAR(_SC('\a')); NEXT(); break; case _SC('b'): APPEND_CHAR(_SC('\b')); NEXT(); break; case _SC('n'): APPEND_CHAR(_SC('\n')); NEXT(); break; case _SC('r'): APPEND_CHAR(_SC('\r')); NEXT(); break; case _SC('v'): APPEND_CHAR(_SC('\v')); NEXT(); break; case _SC('f'): APPEND_CHAR(_SC('\f')); NEXT(); break; case _SC('0'): APPEND_CHAR(_SC('\0')); NEXT(); break; case _SC('\\'): APPEND_CHAR(_SC('\\')); NEXT(); break; case _SC('"'): APPEND_CHAR(_SC('"')); NEXT(); break; case _SC('\''): APPEND_CHAR(_SC('\'')); NEXT(); break; default: Error(_SC("unrecognised escaper char")); break; } } break; default: APPEND_CHAR(CUR_CHAR); NEXT(); } } NEXT(); if(verbatim && CUR_CHAR == '"') { //double quotation APPEND_CHAR(CUR_CHAR); NEXT(); } else { break; } } TERMINATE_BUFFER(); SQInteger len = _longstr.size()-1; if(ndelim == _SC('\'')) { if(len == 0) Error(_SC("empty constant")); if(len > 1) Error(_SC("constant too long")); _nvalue = _longstr[0]; return TK_INTEGER; } _svalue = &_longstr[0]; return TK_STRING_LITERAL; } void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res) { *res = 0; while(*s != 0) { if(scisdigit(*s)) *res = (*res)*16+((*s++)-'0'); else if(scisxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10); else { assert(0); } } } void LexInteger(const SQChar *s,SQUnsignedInteger *res) { *res = 0; while(*s != 0) { *res = (*res)*10+((*s++)-'0'); } } SQInteger scisodigit(SQInteger c) { return c >= _SC('0') && c <= _SC('7'); } void LexOctal(const SQChar *s,SQUnsignedInteger *res) { *res = 0; while(*s != 0) { if(scisodigit(*s)) *res = (*res)*8+((*s++)-'0'); else { assert(0); } } } SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; } #define MAX_HEX_DIGITS (sizeof(SQInteger)*2) SQInteger SQLexer::ReadNumber() { #define TINT 1 #define TFLOAT 2 #define THEX 3 #define TSCIENTIFIC 4 #define TOCTAL 5 SQInteger type = TINT, firstchar = CUR_CHAR; SQChar *sTemp; INIT_TEMP_STRING(); NEXT(); if(firstchar == _SC('0') && (toupper(CUR_CHAR) == _SC('X') || scisodigit(CUR_CHAR)) ) { if(scisodigit(CUR_CHAR)) { type = TOCTAL; while(scisodigit(CUR_CHAR)) { APPEND_CHAR(CUR_CHAR); NEXT(); } if(scisdigit(CUR_CHAR)) Error(_SC("invalid octal number")); } else { NEXT(); type = THEX; while(isxdigit(CUR_CHAR)) { APPEND_CHAR(CUR_CHAR); NEXT(); } if(_longstr.size() > MAX_HEX_DIGITS) Error(_SC("too many digits for an Hex number")); } } else { APPEND_CHAR((int)firstchar); while (CUR_CHAR == _SC('.') || scisdigit(CUR_CHAR) || isexponent(CUR_CHAR)) { if(CUR_CHAR == _SC('.') || isexponent(CUR_CHAR)) type = TFLOAT; if(isexponent(CUR_CHAR)) { if(type != TFLOAT) Error(_SC("invalid numeric format")); type = TSCIENTIFIC; APPEND_CHAR(CUR_CHAR); NEXT(); if(CUR_CHAR == '+' || CUR_CHAR == '-'){ APPEND_CHAR(CUR_CHAR); NEXT(); } if(!scisdigit(CUR_CHAR)) Error(_SC("exponent expected")); } APPEND_CHAR(CUR_CHAR); NEXT(); } } TERMINATE_BUFFER(); switch(type) { case TSCIENTIFIC: case TFLOAT: _fvalue = (SQFloat)scstrtod(&_longstr[0],&sTemp); return TK_FLOAT; case TINT: LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue); return TK_INTEGER; case THEX: LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); return TK_INTEGER; case TOCTAL: LexOctal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); return TK_INTEGER; } return 0; } SQInteger SQLexer::ReadID() { SQInteger res; INIT_TEMP_STRING(); do { APPEND_CHAR(CUR_CHAR); NEXT(); } while(scisalnum(CUR_CHAR) || CUR_CHAR == _SC('_')); TERMINATE_BUFFER(); res = GetIDType(&_longstr[0],_longstr.size() - 1); if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) { _svalue = &_longstr[0]; } return res; } simutrans-124.5/src/squirrel/squirrel/sqlexer.h000066400000000000000000000023441520675460500217260ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQLEXER_H_ #define _SQLEXER_H_ #ifdef SQUNICODE typedef SQChar LexChar; #else typedef unsigned char LexChar; #endif struct SQLexer { SQLexer(); ~SQLexer(); void Init(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up,CompilerErrorFunc efunc,void *ed); void Error(const SQChar *err); SQInteger Lex(); const SQChar *Tok2Str(SQInteger tok); private: SQInteger GetIDType(const SQChar *s,SQInteger len); SQInteger ReadString(SQInteger ndelim,bool verbatim); SQInteger ReadNumber(); void LexBlockComment(); void LexLineComment(); SQInteger ReadID(); void Next(); #ifdef SQUNICODE #if WCHAR_SIZE == 2 SQInteger AddUTF16(SQUnsignedInteger ch); #endif #else SQInteger AddUTF8(SQUnsignedInteger ch); #endif SQInteger ProcessStringHexEscape(SQChar *dest, SQInteger maxdigits); SQInteger _curtoken; SQTable *_keywords; SQBool _reached_eof; public: SQInteger _prevtoken; SQInteger _currentline; SQInteger _lasttokenline; SQInteger _currentcolumn; const SQChar *_svalue; SQInteger _nvalue; SQFloat _fvalue; SQLEXREADFUNC _readf; SQUserPointer _up; LexChar _currdata; SQSharedState *_sharedstate; sqvector _longstr; CompilerErrorFunc _errfunc; void *_errtarget; }; #endif simutrans-124.5/src/squirrel/squirrel/sqmem.cc000066400000000000000000000005751520675460500215270ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #ifndef SQ_EXCLUDE_DEFAULT_MEMFUNCTIONS void *sq_vm_malloc(SQUnsignedInteger size){ return malloc(size); } void *sq_vm_realloc(void *p, SQUnsignedInteger SQ_UNUSED_ARG(oldsize), SQUnsignedInteger size){ return realloc(p, size); } void sq_vm_free(void *p, SQUnsignedInteger SQ_UNUSED_ARG(size)){ free(p); } #endif simutrans-124.5/src/squirrel/squirrel/sqobject.cc000066400000000000000000000473711520675460500222240ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqstring.h" #include "sqarray.h" #include "sqtable.h" #include "squserdata.h" #include "sqfuncproto.h" #include "sqclass.h" #include "sqclosure.h" const SQChar *IdType2Name(SQObjectType type) { switch(_RAW_TYPE(type)) { case _RT_NULL:return _SC("null"); case _RT_INTEGER:return _SC("integer"); case _RT_FLOAT:return _SC("float"); case _RT_BOOL:return _SC("bool"); case _RT_STRING:return _SC("string"); case _RT_TABLE:return _SC("table"); case _RT_ARRAY:return _SC("array"); case _RT_GENERATOR:return _SC("generator"); case _RT_CLOSURE: case _RT_NATIVECLOSURE: return _SC("function"); case _RT_USERDATA: case _RT_USERPOINTER: return _SC("userdata"); case _RT_THREAD: return _SC("thread"); case _RT_FUNCPROTO: return _SC("function"); case _RT_CLASS: return _SC("class"); case _RT_INSTANCE: return _SC("instance"); case _RT_WEAKREF: return _SC("weakref"); case _RT_OUTER: return _SC("outer"); default: return NULL; } } const SQChar *GetTypeName(const SQObjectPtr &obj1) { return IdType2Name(sq_type(obj1)); } SQString *SQString::Create(SQSharedState *ss,const SQChar *s,SQInteger len) { SQString *str=ADD_STRING(ss,s,len); return str; } void SQString::Release() { REMOVE_STRING(_sharedstate,this); } SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) { SQInteger idx = (SQInteger)TranslateIndex(refpos); while(idx < _len){ outkey = (SQInteger)idx; outval = (SQInteger)((SQUnsignedInteger)_val[idx]); //return idx for the next iteration return ++idx; } //nothing to iterate anymore return -1; } SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx) { switch(sq_type(idx)){ case OT_NULL: return 0; case OT_INTEGER: return (SQUnsignedInteger)_integer(idx); default: assert(0); break; } return 0; } SQWeakRef *SQRefCounted::GetWeakRef(SQObjectType type) { if(!_weakref) { sq_new(_weakref,SQWeakRef); #if defined(SQUSEDOUBLE) && !defined(_SQ64) _weakref->_obj._unVal.raw = 0; //clean the whole union on 32 bits with double #endif _weakref->_obj._type = type; _weakref->_obj._unVal.pRefCounted = this; } return _weakref; } SQRefCounted::~SQRefCounted() { if(_weakref) { _weakref->_obj._type = OT_NULL; _weakref->_obj._unVal.pRefCounted = NULL; } } void SQWeakRef::Release() { if(ISREFCOUNTED(_obj._type)) { _obj._unVal.pRefCounted->_weakref = NULL; } sq_delete(this,SQWeakRef); } bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) { if(_delegate) { return _delegate->Get((*_ss(v)->_metamethods)[mm],res); } return false; } bool SQDelegable::SetDelegate(SQTable *mt) { SQTable *temp = mt; if(temp == this) return false; while (temp) { if (temp->_delegate == this) return false; //cycle detected temp = temp->_delegate; } if (mt) __ObjAddRef(mt); __ObjRelease(_delegate); _delegate = mt; return true; } bool SQGenerator::Yield(SQVM *v,SQInteger target) { if(_state==eSuspended) { v->Raise_Error(_SC("internal vm error, yielding dead generator")); return false;} if(_state==eDead) { v->Raise_Error(_SC("internal vm error, yielding a dead generator")); return false; } SQInteger size = v->_top-v->_stackbase; _stack.resize(size); SQObject _this = v->_stack[v->_stackbase]; _stack._vals[0] = ISREFCOUNTED(sq_type(_this)) ? SQObjectPtr(_refcounted(_this)->GetWeakRef(sq_type(_this))) : _this; for(SQInteger n =1; n_stack[v->_stackbase+n]; } for(SQInteger j =0; j < size; j++) { v->_stack[v->_stackbase+j].Null(); } _ci = *v->ci; _ci._generator=NULL; for(SQInteger i=0;i<_ci._etraps;i++) { _etraps.push_back(v->_etraps.top()); v->_etraps.pop_back(); // store relative stack base and size in case of resume to other _top SQExceptionTrap &et = _etraps.back(); et._stackbase -= v->_stackbase; et._stacksize -= v->_stackbase; } _state=eSuspended; return true; } bool SQGenerator::Resume(SQVM *v,SQObjectPtr &dest) { if(_state==eDead){ v->Raise_Error(_SC("resuming dead generator")); return false; } if(_state==eRunning){ v->Raise_Error(_SC("resuming active generator")); return false; } SQInteger size = _stack.size(); SQInteger target = &dest - &(v->_stack._vals[v->_stackbase]); assert(target>=0 && target<=255); SQInteger newbase = v->_top; if(!v->EnterFrame(v->_top, v->_top + size, false)) return false; v->ci->_generator = this; v->ci->_target = (SQInt32)target; v->ci->_closure = _ci._closure; v->ci->_ip = _ci._ip; v->ci->_literals = _ci._literals; v->ci->_ncalls = _ci._ncalls; v->ci->_etraps = _ci._etraps; v->ci->_root = _ci._root; for(SQInteger i=0;i<_ci._etraps;i++) { v->_etraps.push_back(_etraps.top()); _etraps.pop_back(); SQExceptionTrap &et = v->_etraps.back(); // restore absolute stack base and size et._stackbase += newbase; et._stacksize += newbase; } SQObject _this = _stack._vals[0]; v->_stack[v->_stackbase] = sq_type(_this) == OT_WEAKREF ? _weakref(_this)->_obj : _this; for(SQInteger n = 1; n_stack[v->_stackbase+n] = _stack._vals[n]; _stack._vals[n].Null(); } _state=eRunning; if (v->_debughook) v->CallDebugHook(_SC('c')); return true; } void SQArray::Extend(const SQArray *a){ SQInteger xlen; if((xlen=a->Size())) for(SQInteger i=0;i_values[i]); } const SQChar* SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop) { SQUnsignedInteger nvars=_nlocalvarinfos; const SQChar *res=NULL; if(nvars>=nseq){ for(SQUnsignedInteger i=0;i=nop) { if(nseq==0){ vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]); res=_stringval(_localvarinfos[i]._name); break; } nseq--; } } } return res; } SQInteger SQFunctionProto::GetLine(SQInstruction *curr) { SQInteger op = (SQInteger)(curr-_instructions); SQInteger line=_lineinfos[0]._line; SQInteger low = 0; SQInteger high = _nlineinfos - 1; SQInteger mid = 0; while(low <= high) { mid = low + ((high - low) >> 1); SQInteger curop = _lineinfos[mid]._op; if(curop > op) { high = mid - 1; } else if(curop < op) { if(mid < (_nlineinfos - 1) && _lineinfos[mid + 1]._op >= op) { break; } low = mid + 1; } else { //equal break; } } while(mid > 0 && _lineinfos[mid]._op >= op) mid--; line = _lineinfos[mid]._line; return line; } SQClosure::~SQClosure() { __ObjRelease(_root); __ObjRelease(_env); __ObjRelease(_base); REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } #define _CHECK_IO(exp) { if(!exp)return false; } bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer dest,SQInteger size) { if(write(up,dest,size) != size) { v->Raise_Error(_SC("io error (write function failure)")); return false; } return true; } bool SafeRead(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size) { if(size && read(up,dest,size) != size) { v->Raise_Error(_SC("io error, read function failure, the origin stream could be corrupted/trucated")); return false; } return true; } bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUnsignedInteger32 tag) { return SafeWrite(v,write,up,&tag,sizeof(tag)); } bool CheckTag(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUnsignedInteger32 tag) { SQUnsignedInteger32 t; _CHECK_IO(SafeRead(v,read,up,&t,sizeof(t))); if(t != tag){ v->Raise_Error(_SC("invalid or corrupted closure stream")); return false; } return true; } bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o) { SQUnsignedInteger32 _type = (SQUnsignedInteger32)sq_type(o); _CHECK_IO(SafeWrite(v,write,up,&_type,sizeof(_type))); switch(sq_type(o)){ case OT_STRING: _CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger))); _CHECK_IO(SafeWrite(v,write,up,_stringval(o),sq_rsl(_string(o)->_len))); break; case OT_BOOL: case OT_INTEGER: _CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break; case OT_FLOAT: _CHECK_IO(SafeWrite(v,write,up,&_float(o),sizeof(SQFloat)));break; case OT_NULL: break; default: v->Raise_Error(_SC("cannot serialize a %s"),GetTypeName(o)); return false; } return true; } bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o) { SQUnsignedInteger32 _type; _CHECK_IO(SafeRead(v,read,up,&_type,sizeof(_type))); SQObjectType t = (SQObjectType)_type; switch(t){ case OT_STRING:{ SQInteger len; _CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger))); _CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(sq_rsl(len)),sq_rsl(len))); o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len); } break; case OT_INTEGER:{ SQInteger i; _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break; } case OT_BOOL:{ SQInteger i; _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o._type = OT_BOOL; o._unVal.nInteger = i; break; } case OT_FLOAT:{ SQFloat f; _CHECK_IO(SafeRead(v,read,up,&f,sizeof(SQFloat))); o = f; break; } case OT_NULL: o.Null(); break; default: v->Raise_Error(_SC("cannot serialize a %s"),IdType2Name(t)); return false; } return true; } bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) { _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD)); _CHECK_IO(WriteTag(v,write,up,sizeof(SQChar))); _CHECK_IO(WriteTag(v,write,up,sizeof(SQInteger))); _CHECK_IO(WriteTag(v,write,up,sizeof(SQFloat))); _CHECK_IO(_function->Save(v,up,write)); _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL)); return true; } bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) { _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD)); _CHECK_IO(CheckTag(v,read,up,sizeof(SQChar))); _CHECK_IO(CheckTag(v,read,up,sizeof(SQInteger))); _CHECK_IO(CheckTag(v,read,up,sizeof(SQFloat))); SQObjectPtr func; _CHECK_IO(SQFunctionProto::Load(v,up,read,func)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL)); ret = SQClosure::Create(_ss(v),_funcproto(func),_table(v->_roottable)->GetWeakRef(OT_TABLE)); //FIXME: load an root for this closure return true; } SQFunctionProto::SQFunctionProto(SQSharedState *ss) { _stacksize=0; _bgenerator=false; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } SQFunctionProto::~SQFunctionProto() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } bool SQFunctionProto::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) { SQInteger i,nliterals = _nliterals,nparameters = _nparameters; SQInteger noutervalues = _noutervalues,nlocalvarinfos = _nlocalvarinfos; SQInteger nlineinfos=_nlineinfos,ninstructions = _ninstructions,nfunctions=_nfunctions; SQInteger ndefaultparams = _ndefaultparams; _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(WriteObject(v,up,write,_sourcename)); _CHECK_IO(WriteObject(v,up,write,_name)); _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeWrite(v,write,up,&nliterals,sizeof(nliterals))); _CHECK_IO(SafeWrite(v,write,up,&nparameters,sizeof(nparameters))); _CHECK_IO(SafeWrite(v,write,up,&noutervalues,sizeof(noutervalues))); _CHECK_IO(SafeWrite(v,write,up,&nlocalvarinfos,sizeof(nlocalvarinfos))); _CHECK_IO(SafeWrite(v,write,up,&nlineinfos,sizeof(nlineinfos))); _CHECK_IO(SafeWrite(v,write,up,&ndefaultparams,sizeof(ndefaultparams))); _CHECK_IO(SafeWrite(v,write,up,&ninstructions,sizeof(ninstructions))); _CHECK_IO(SafeWrite(v,write,up,&nfunctions,sizeof(nfunctions))); _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); for(i=0;iSave(v,up,write)); } _CHECK_IO(SafeWrite(v,write,up,&_stacksize,sizeof(_stacksize))); _CHECK_IO(SafeWrite(v,write,up,&_bgenerator,sizeof(_bgenerator))); _CHECK_IO(SafeWrite(v,write,up,&_varparams,sizeof(_varparams))); return true; } bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) { SQInteger i, nliterals,nparameters; SQInteger noutervalues ,nlocalvarinfos ; SQInteger nlineinfos,ninstructions ,nfunctions,ndefaultparams ; SQObjectPtr sourcename, name; SQObjectPtr o; _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(ReadObject(v, up, read, sourcename)); _CHECK_IO(ReadObject(v, up, read, name)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeRead(v,read,up, &nliterals, sizeof(nliterals))); _CHECK_IO(SafeRead(v,read,up, &nparameters, sizeof(nparameters))); _CHECK_IO(SafeRead(v,read,up, &noutervalues, sizeof(noutervalues))); _CHECK_IO(SafeRead(v,read,up, &nlocalvarinfos, sizeof(nlocalvarinfos))); _CHECK_IO(SafeRead(v,read,up, &nlineinfos, sizeof(nlineinfos))); _CHECK_IO(SafeRead(v,read,up, &ndefaultparams, sizeof(ndefaultparams))); _CHECK_IO(SafeRead(v,read,up, &ninstructions, sizeof(ninstructions))); _CHECK_IO(SafeRead(v,read,up, &nfunctions, sizeof(nfunctions))); SQFunctionProto *f = SQFunctionProto::Create(_opt_ss(v),ninstructions,nliterals,nparameters, nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams); SQObjectPtr proto = f; //gets a ref in case of failure f->_sourcename = sourcename; f->_name = name; _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0;i < nliterals; i++){ _CHECK_IO(ReadObject(v, up, read, o)); f->_literals[i] = o; } _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0; i < nparameters; i++){ _CHECK_IO(ReadObject(v, up, read, o)); f->_parameters[i] = o; } _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0; i < noutervalues; i++){ SQUnsignedInteger type; SQObjectPtr name; _CHECK_IO(SafeRead(v,read,up, &type, sizeof(SQUnsignedInteger))); _CHECK_IO(ReadObject(v, up, read, o)); _CHECK_IO(ReadObject(v, up, read, name)); f->_outervalues[i] = SQOuterVar(name,o, (SQOuterType)type); } _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0; i < nlocalvarinfos; i++){ SQLocalVarInfo lvi; _CHECK_IO(ReadObject(v, up, read, lvi._name)); _CHECK_IO(SafeRead(v,read,up, &lvi._pos, sizeof(SQUnsignedInteger))); _CHECK_IO(SafeRead(v,read,up, &lvi._start_op, sizeof(SQUnsignedInteger))); _CHECK_IO(SafeRead(v,read,up, &lvi._end_op, sizeof(SQUnsignedInteger))); f->_localvarinfos[i] = lvi; } _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeRead(v,read,up, f->_lineinfos, sizeof(SQLineInfo)*nlineinfos)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeRead(v,read,up, f->_defaultparams, sizeof(SQInteger)*ndefaultparams)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeRead(v,read,up, f->_instructions, sizeof(SQInstruction)*ninstructions)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0; i < nfunctions; i++){ _CHECK_IO(_funcproto(o)->Load(v, up, read, o)); f->_functions[i] = o; } _CHECK_IO(SafeRead(v,read,up, &f->_stacksize, sizeof(f->_stacksize))); _CHECK_IO(SafeRead(v,read,up, &f->_bgenerator, sizeof(f->_bgenerator))); _CHECK_IO(SafeRead(v,read,up, &f->_varparams, sizeof(f->_varparams))); ret = f; return true; } #ifndef NO_GARBAGE_COLLECTOR #define START_MARK() if(!(_uiRef&MARK_FLAG)){ \ _uiRef|=MARK_FLAG; #define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \ AddToChain(chain, this); } void SQVM::Mark(SQCollectable **chain) { START_MARK() SQSharedState::MarkObject(_lasterror,chain); SQSharedState::MarkObject(_errorhandler,chain); SQSharedState::MarkObject(_debughook_closure,chain); SQSharedState::MarkObject(_roottable, chain); SQSharedState::MarkObject(temp_reg, chain); for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::MarkObject(_callsstack[k]._closure, chain); END_MARK() } void SQArray::Mark(SQCollectable **chain) { START_MARK() SQInteger len = _values.size(); for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain); END_MARK() } void SQTable::Mark(SQCollectable **chain) { START_MARK() if(_delegate) _delegate->Mark(chain); SQInteger len = _numofnodes; for(SQInteger i = 0; i < len; i++){ SQSharedState::MarkObject(_nodes[i].key, chain); SQSharedState::MarkObject(_nodes[i].val, chain); } END_MARK() } void SQClass::Mark(SQCollectable **chain) { START_MARK() _members->Mark(chain); if(_base) _base->Mark(chain); SQSharedState::MarkObject(_attributes, chain); for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) { SQSharedState::MarkObject(_defaultvalues[i].val, chain); SQSharedState::MarkObject(_defaultvalues[i].attrs, chain); } for(SQUnsignedInteger j =0; j< _methods.size(); j++) { SQSharedState::MarkObject(_methods[j].val, chain); SQSharedState::MarkObject(_methods[j].attrs, chain); } for(SQUnsignedInteger k =0; k< MT_LAST; k++) { SQSharedState::MarkObject(_metamethods[k], chain); } END_MARK() } void SQInstance::Mark(SQCollectable **chain) { START_MARK() _class->Mark(chain); SQUnsignedInteger nvalues = _class->_defaultvalues.size(); for(SQUnsignedInteger i =0; i< nvalues; i++) { SQSharedState::MarkObject(_values[i], chain); } END_MARK() } void SQGenerator::Mark(SQCollectable **chain) { START_MARK() for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); SQSharedState::MarkObject(_closure, chain); END_MARK() } void SQFunctionProto::Mark(SQCollectable **chain) { START_MARK() for(SQInteger i = 0; i < _nliterals; i++) SQSharedState::MarkObject(_literals[i], chain); for(SQInteger k = 0; k < _nfunctions; k++) SQSharedState::MarkObject(_functions[k], chain); END_MARK() } void SQClosure::Mark(SQCollectable **chain) { START_MARK() if(_base) _base->Mark(chain); SQFunctionProto *fp = _function; fp->Mark(chain); for(SQInteger i = 0; i < fp->_noutervalues; i++) SQSharedState::MarkObject(_outervalues[i], chain); for(SQInteger k = 0; k < fp->_ndefaultparams; k++) SQSharedState::MarkObject(_defaultparams[k], chain); END_MARK() } void SQNativeClosure::Mark(SQCollectable **chain) { START_MARK() for(SQUnsignedInteger i = 0; i < _noutervalues; i++) SQSharedState::MarkObject(_outervalues[i], chain); END_MARK() } void SQOuter::Mark(SQCollectable **chain) { START_MARK() /* If the valptr points to a closed value, that value is alive */ if(_valptr == &_value) { SQSharedState::MarkObject(_value, chain); } END_MARK() } void SQUserData::Mark(SQCollectable **chain){ START_MARK() if(_delegate) _delegate->Mark(chain); END_MARK() } void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; } #endif simutrans-124.5/src/squirrel/squirrel/sqobject.h000066400000000000000000000213461520675460500220600ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQOBJECT_H_ #define _SQOBJECT_H_ #include "squtils.h" #ifdef _SQ64 #define UINT_MINUS_ONE (0xFFFFFFFFFFFFFFFF) #else #define UINT_MINUS_ONE (0xFFFFFFFF) #endif #define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R')) #define SQ_CLOSURESTREAM_PART (('P'<<24)|('A'<<16)|('R'<<8)|('T')) #define SQ_CLOSURESTREAM_TAIL (('T'<<24)|('A'<<16)|('I'<<8)|('L')) struct SQSharedState; enum SQMetaMethod{ MT_ADD=0, MT_SUB=1, MT_MUL=2, MT_DIV=3, MT_UNM=4, MT_MODULO=5, MT_SET=6, MT_GET=7, MT_TYPEOF=8, MT_NEXTI=9, MT_CMP=10, MT_CALL=11, MT_CLONED=12, MT_NEWSLOT=13, MT_DELSLOT=14, MT_TOSTRING=15, MT_NEWMEMBER=16, MT_INHERITED=17, MT_LAST = 18 }; #define MM_ADD _SC("_add") #define MM_SUB _SC("_sub") #define MM_MUL _SC("_mul") #define MM_DIV _SC("_div") #define MM_UNM _SC("_unm") #define MM_MODULO _SC("_modulo") #define MM_SET _SC("_set") #define MM_GET _SC("_get") #define MM_TYPEOF _SC("_typeof") #define MM_NEXTI _SC("_nexti") #define MM_CMP _SC("_cmp") #define MM_CALL _SC("_call") #define MM_CLONED _SC("_cloned") #define MM_NEWSLOT _SC("_newslot") #define MM_DELSLOT _SC("_delslot") #define MM_TOSTRING _SC("_tostring") #define MM_NEWMEMBER _SC("_newmember") #define MM_INHERITED _SC("_inherited") #define _CONSTRUCT_VECTOR(type,size,ptr) { \ for(SQInteger n = 0; n < ((SQInteger)size); n++) { \ new (&ptr[n]) type(); \ } \ } #define _DESTRUCT_VECTOR(type,size,ptr) { \ for(SQInteger nl = 0; nl < ((SQInteger)size); nl++) { \ ptr[nl].~type(); \ } \ } #define _COPY_VECTOR(dest,src,size) { \ for(SQInteger _n_ = 0; _n_ < ((SQInteger)size); _n_++) { \ dest[_n_] = src[_n_]; \ } \ } #define _NULL_SQOBJECT_VECTOR(vec,size) { \ for(SQInteger _n_ = 0; _n_ < ((SQInteger)size); _n_++) { \ vec[_n_].Null(); \ } \ } #define MINPOWER2 4 struct SQRefCounted { SQUnsignedInteger _uiRef; struct SQWeakRef *_weakref; SQRefCounted() { _uiRef = 0; _weakref = NULL; } virtual ~SQRefCounted(); SQWeakRef *GetWeakRef(SQObjectType type); virtual void Release()=0; }; struct SQWeakRef : SQRefCounted { void Release(); SQObject _obj; }; #define _realval(o) (sq_type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj) struct SQObjectPtr; #define __AddRef(type,unval) if(ISREFCOUNTED(type)) \ { \ unval.pRefCounted->_uiRef++; \ } #define __Release(type,unval) if(ISREFCOUNTED(type) && ((--unval.pRefCounted->_uiRef)==0)) \ { \ unval.pRefCounted->Release(); \ } #define __ObjRelease(obj) { \ if((obj)) { \ (obj)->_uiRef--; \ if((obj)->_uiRef == 0) \ (obj)->Release(); \ (obj) = NULL; \ } \ } #define __ObjAddRef(obj) { \ (obj)->_uiRef++; \ } #define is_delegable(t) (sq_type(t)&SQOBJECT_DELEGABLE) #define raw_type(obj) _RAW_TYPE((obj)._type) #define _integer(obj) ((obj)._unVal.nInteger) #define _float(obj) ((obj)._unVal.fFloat) #define _string(obj) ((obj)._unVal.pString) #define _table(obj) ((obj)._unVal.pTable) #define _array(obj) ((obj)._unVal.pArray) #define _closure(obj) ((obj)._unVal.pClosure) #define _generator(obj) ((obj)._unVal.pGenerator) #define _nativeclosure(obj) ((obj)._unVal.pNativeClosure) #define _userdata(obj) ((obj)._unVal.pUserData) #define _userpointer(obj) ((obj)._unVal.pUserPointer) #define _thread(obj) ((obj)._unVal.pThread) #define _funcproto(obj) ((obj)._unVal.pFunctionProto) #define _class(obj) ((obj)._unVal.pClass) #define _instance(obj) ((obj)._unVal.pInstance) #define _delegable(obj) ((SQDelegable *)(obj)._unVal.pDelegable) #define _weakref(obj) ((obj)._unVal.pWeakRef) #define _outer(obj) ((obj)._unVal.pOuter) #define _refcounted(obj) ((obj)._unVal.pRefCounted) #define _rawval(obj) ((obj)._unVal.raw) #define _stringval(obj) (obj)._unVal.pString->_val #define _userdataval(obj) ((SQUserPointer)sq_aligning((obj)._unVal.pUserData + 1)) #define tofloat(num) ((sq_type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num)) #define tointeger(num) ((sq_type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num)) ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// #if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) #define SQ_REFOBJECT_INIT() SQ_OBJECT_RAWINIT() #else #define SQ_REFOBJECT_INIT() #endif #define _REF_TYPE_DECL(type,_class,sym) \ SQObjectPtr(_class * x) \ { \ SQ_OBJECT_RAWINIT() \ _type=type; \ _unVal.sym = x; \ assert(_unVal.pTable); \ _unVal.pRefCounted->_uiRef++; \ } \ inline SQObjectPtr& operator=(_class *x) \ { \ SQObjectType tOldType; \ SQObjectValue unOldVal; \ tOldType=_type; \ unOldVal=_unVal; \ _type = type; \ SQ_REFOBJECT_INIT() \ _unVal.sym = x; \ _unVal.pRefCounted->_uiRef++; \ __Release(tOldType,unOldVal); \ return *this; \ } #define _SCALAR_TYPE_DECL(type,_class,sym) \ SQObjectPtr(_class x) \ { \ SQ_OBJECT_RAWINIT() \ _type=type; \ _unVal.sym = x; \ } \ inline SQObjectPtr& operator=(_class x) \ { \ __Release(_type,_unVal); \ _type = type; \ SQ_OBJECT_RAWINIT() \ _unVal.sym = x; \ return *this; \ } struct SQObjectPtr : public SQObject { SQObjectPtr() { SQ_OBJECT_RAWINIT() _type=OT_NULL; _unVal.pUserPointer=NULL; } SQObjectPtr(const SQObjectPtr &o) { _type = o._type; _unVal = o._unVal; __AddRef(_type,_unVal); } SQObjectPtr(const SQObject &o) { _type = o._type; _unVal = o._unVal; __AddRef(_type,_unVal); } _REF_TYPE_DECL(OT_TABLE,SQTable,pTable) _REF_TYPE_DECL(OT_CLASS,SQClass,pClass) _REF_TYPE_DECL(OT_INSTANCE,SQInstance,pInstance) _REF_TYPE_DECL(OT_ARRAY,SQArray,pArray) _REF_TYPE_DECL(OT_CLOSURE,SQClosure,pClosure) _REF_TYPE_DECL(OT_NATIVECLOSURE,SQNativeClosure,pNativeClosure) _REF_TYPE_DECL(OT_OUTER,SQOuter,pOuter) _REF_TYPE_DECL(OT_GENERATOR,SQGenerator,pGenerator) _REF_TYPE_DECL(OT_STRING,SQString,pString) _REF_TYPE_DECL(OT_USERDATA,SQUserData,pUserData) _REF_TYPE_DECL(OT_WEAKREF,SQWeakRef,pWeakRef) _REF_TYPE_DECL(OT_THREAD,SQVM,pThread) _REF_TYPE_DECL(OT_FUNCPROTO,SQFunctionProto,pFunctionProto) _SCALAR_TYPE_DECL(OT_INTEGER,SQInteger,nInteger) _SCALAR_TYPE_DECL(OT_FLOAT,SQFloat,fFloat) _SCALAR_TYPE_DECL(OT_USERPOINTER,SQUserPointer,pUserPointer) SQObjectPtr(bool bBool) { SQ_OBJECT_RAWINIT() _type = OT_BOOL; _unVal.nInteger = bBool?1:0; } inline SQObjectPtr& operator=(bool b) { __Release(_type,_unVal); SQ_OBJECT_RAWINIT() _type = OT_BOOL; _unVal.nInteger = b?1:0; return *this; } ~SQObjectPtr() { __Release(_type,_unVal); } inline SQObjectPtr& operator=(const SQObjectPtr& obj) { SQObjectType tOldType; SQObjectValue unOldVal; tOldType=_type; unOldVal=_unVal; _unVal = obj._unVal; _type = obj._type; __AddRef(_type,_unVal); __Release(tOldType,unOldVal); return *this; } inline SQObjectPtr& operator=(const SQObject& obj) { SQObjectType tOldType; SQObjectValue unOldVal; tOldType=_type; unOldVal=_unVal; _unVal = obj._unVal; _type = obj._type; __AddRef(_type,_unVal); __Release(tOldType,unOldVal); return *this; } inline void Null() { SQObjectType tOldType = _type; SQObjectValue unOldVal = _unVal; _type = OT_NULL; _unVal.raw = (SQRawObjectVal)NULL; __Release(tOldType ,unOldVal); } private: SQObjectPtr(const SQChar *){} //safety }; inline void _Swap(SQObject &a,SQObject &b) { SQObjectType tOldType = a._type; SQObjectValue unOldVal = a._unVal; a._type = b._type; a._unVal = b._unVal; b._type = tOldType; b._unVal = unOldVal; } ///////////////////////////////////////////////////////////////////////////////////// #ifndef NO_GARBAGE_COLLECTOR #define MARK_FLAG 0x80000000 struct SQCollectable : public SQRefCounted { SQCollectable *_next; SQCollectable *_prev; SQSharedState *_sharedstate; virtual SQObjectType GetType()=0; virtual void Release()=0; virtual void Mark(SQCollectable **chain)=0; void UnMark(); virtual void Finalize()=0; static void AddToChain(SQCollectable **chain,SQCollectable *c); static void RemoveFromChain(SQCollectable **chain,SQCollectable *c); }; #define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj) #define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);} #define CHAINABLE_OBJ SQCollectable #define INIT_CHAIN() {_next=NULL;_prev=NULL;_sharedstate=ss;} #else #define ADD_TO_CHAIN(chain,obj) ((void)0) #define REMOVE_FROM_CHAIN(chain,obj) ((void)0) #define CHAINABLE_OBJ SQRefCounted #define INIT_CHAIN() ((void)0) #endif struct SQDelegable : public CHAINABLE_OBJ { bool SetDelegate(SQTable *m); virtual bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); SQTable *_delegate; }; SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx); typedef sqvector SQObjectPtrVec; typedef sqvector SQIntVec; const SQChar *GetTypeName(const SQObjectPtr &obj1); const SQChar *IdType2Name(SQObjectType type); #endif simutrans-124.5/src/squirrel/squirrel/sqopcodes.h000066400000000000000000000046071520675460500222470ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQOPCODES_H_ #define _SQOPCODES_H_ #define MAX_FUNC_STACKSIZE 0xFF #define MAX_LITERALS ((SQInteger)0x7FFFFFFF) enum BitWiseOP { BW_AND = 0, BW_OR = 2, BW_XOR = 3, BW_SHIFTL = 4, BW_SHIFTR = 5, BW_USHIFTR = 6 }; enum CmpOP { CMP_G = 0, CMP_GE = 2, CMP_L = 3, CMP_LE = 4, CMP_3W = 5 }; enum NewObjectType { NOT_TABLE = 0, NOT_ARRAY = 1, NOT_CLASS = 2 }; enum AppendArrayType { AAT_STACK = 0, AAT_LITERAL = 1, AAT_INT = 2, AAT_FLOAT = 3, AAT_BOOL = 4 }; enum SQOpcode { _OP_LINE= 0x00, _OP_LOAD= 0x01, _OP_LOADINT= 0x02, _OP_LOADFLOAT= 0x03, _OP_DLOAD= 0x04, _OP_TAILCALL= 0x05, _OP_CALL= 0x06, _OP_PREPCALL= 0x07, _OP_PREPCALLK= 0x08, _OP_GETK= 0x09, _OP_MOVE= 0x0A, _OP_NEWSLOT= 0x0B, _OP_DELETE= 0x0C, _OP_SET= 0x0D, _OP_GET= 0x0E, _OP_EQ= 0x0F, _OP_NE= 0x10, _OP_ADD= 0x11, _OP_SUB= 0x12, _OP_MUL= 0x13, _OP_DIV= 0x14, _OP_MOD= 0x15, _OP_BITW= 0x16, _OP_RETURN= 0x17, _OP_LOADNULLS= 0x18, _OP_LOADROOT= 0x19, _OP_LOADBOOL= 0x1A, _OP_DMOVE= 0x1B, _OP_JMP= 0x1C, //_OP_JNZ= 0x1D, _OP_JCMP= 0x1D, _OP_JZ= 0x1E, _OP_SETOUTER= 0x1F, _OP_GETOUTER= 0x20, _OP_NEWOBJ= 0x21, _OP_APPENDARRAY= 0x22, _OP_COMPARITH= 0x23, _OP_INC= 0x24, _OP_INCL= 0x25, _OP_PINC= 0x26, _OP_PINCL= 0x27, _OP_CMP= 0x28, _OP_EXISTS= 0x29, _OP_INSTANCEOF= 0x2A, _OP_AND= 0x2B, _OP_OR= 0x2C, _OP_NEG= 0x2D, _OP_NOT= 0x2E, _OP_BWNOT= 0x2F, _OP_CLOSURE= 0x30, _OP_YIELD= 0x31, _OP_RESUME= 0x32, _OP_FOREACH= 0x33, _OP_POSTFOREACH= 0x34, _OP_CLONE= 0x35, _OP_TYPEOF= 0x36, _OP_PUSHTRAP= 0x37, _OP_POPTRAP= 0x38, _OP_THROW= 0x39, _OP_NEWSLOTA= 0x3A, _OP_GETBASE= 0x3B, _OP_CLOSE= 0x3C, }; struct SQInstructionDesc { const SQChar *name; }; struct SQInstruction { SQInstruction(){}; SQInstruction(SQOpcode _op,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0) { op = (unsigned char)_op; _arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1; _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3; } SQInt32 _arg1; unsigned char op; unsigned char _arg0; unsigned char _arg2; unsigned char _arg3; }; #include "squtils.h" typedef sqvector SQInstructionVec; #define NEW_SLOT_ATTRIBUTES_FLAG 0x01 #define NEW_SLOT_STATIC_FLAG 0x02 #endif simutrans-124.5/src/squirrel/squirrel/sqpcheader.h000066400000000000000000000005541520675460500223630ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQPCHEADER_H_ #define _SQPCHEADER_H_ #if defined(_MSC_VER) && defined(_DEBUG) #include #endif #include #include #include #include #include #include //squirrel stuff #include "../squirrel.h" #include "sqobject.h" #include "sqstate.h" #endif simutrans-124.5/src/squirrel/squirrel/sqstate.cc000066400000000000000000000364411520675460500220720ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqopcodes.h" #include "sqvm.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqstring.h" #include "sqtable.h" #include "sqarray.h" #include "squserdata.h" #include "sqclass.h" SQSharedState::SQSharedState() { _compilererrorhandler = NULL; _printfunc = NULL; _errorfunc = NULL; _debuginfo = false; _notifyallexceptions = false; _foreignptr = NULL; _releasehook = NULL; } #define newsysstring(s) { \ _systemstrings->push_back(SQString::Create(this,s)); \ } #define newmetamethod(s) { \ _metamethods->push_back(SQString::Create(this,s)); \ _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \ } bool CompileTypemask(SQIntVec &res,const SQChar *typemask) { SQInteger i = 0; SQInteger mask = 0; while(typemask[i] != 0) { switch(typemask[i]){ case 'o': mask |= _RT_NULL; break; case 'i': mask |= _RT_INTEGER; break; case 'f': mask |= _RT_FLOAT; break; case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break; case 's': mask |= _RT_STRING; break; case 't': mask |= _RT_TABLE; break; case 'a': mask |= _RT_ARRAY; break; case 'u': mask |= _RT_USERDATA; break; case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break; case 'b': mask |= _RT_BOOL; break; case 'g': mask |= _RT_GENERATOR; break; case 'p': mask |= _RT_USERPOINTER; break; case 'v': mask |= _RT_THREAD; break; case 'x': mask |= _RT_INSTANCE; break; case 'y': mask |= _RT_CLASS; break; case 'r': mask |= _RT_WEAKREF; break; case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue; case ' ': i++; continue; //ignores spaces default: return false; } i++; if(typemask[i] == '|') { i++; if(typemask[i] == 0) return false; continue; } res.push_back(mask); mask = 0; } return true; } SQTable *CreateDefaultDelegate(SQSharedState *ss,const SQRegFunction *funcz) { SQInteger i=0; SQTable *t=SQTable::Create(ss,0); while(funcz[i].name!=0){ SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f,0); nc->_nparamscheck = funcz[i].nparamscheck; nc->_name = SQString::Create(ss,funcz[i].name); if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask)) return NULL; t->NewSlot(SQString::Create(ss,funcz[i].name),nc); i++; } return t; } void SQSharedState::Init() { _scratchpad=NULL; _scratchpadsize=0; #ifndef NO_GARBAGE_COLLECTOR _gc_chain=NULL; #endif _stringtable = (SQStringTable*)SQ_MALLOC(sizeof(SQStringTable)); new (_stringtable) SQStringTable(this); sq_new(_metamethods,SQObjectPtrVec); sq_new(_systemstrings,SQObjectPtrVec); sq_new(_types,SQObjectPtrVec); _metamethodsmap = SQTable::Create(this,MT_LAST-1); //adding type strings to avoid memory trashing //types names newsysstring(_SC("null")); newsysstring(_SC("table")); newsysstring(_SC("array")); newsysstring(_SC("closure")); newsysstring(_SC("string")); newsysstring(_SC("userdata")); newsysstring(_SC("integer")); newsysstring(_SC("float")); newsysstring(_SC("userpointer")); newsysstring(_SC("function")); newsysstring(_SC("generator")); newsysstring(_SC("thread")); newsysstring(_SC("class")); newsysstring(_SC("instance")); newsysstring(_SC("bool")); //meta methods newmetamethod(MM_ADD); newmetamethod(MM_SUB); newmetamethod(MM_MUL); newmetamethod(MM_DIV); newmetamethod(MM_UNM); newmetamethod(MM_MODULO); newmetamethod(MM_SET); newmetamethod(MM_GET); newmetamethod(MM_TYPEOF); newmetamethod(MM_NEXTI); newmetamethod(MM_CMP); newmetamethod(MM_CALL); newmetamethod(MM_CLONED); newmetamethod(MM_NEWSLOT); newmetamethod(MM_DELSLOT); newmetamethod(MM_TOSTRING); newmetamethod(MM_NEWMEMBER); newmetamethod(MM_INHERITED); _constructoridx = SQString::Create(this,_SC("constructor")); _registry = SQTable::Create(this,0); _consts = SQTable::Create(this,0); _table_default_delegate = CreateDefaultDelegate(this,_table_default_delegate_funcz); _array_default_delegate = CreateDefaultDelegate(this,_array_default_delegate_funcz); _string_default_delegate = CreateDefaultDelegate(this,_string_default_delegate_funcz); _number_default_delegate = CreateDefaultDelegate(this,_number_default_delegate_funcz); _closure_default_delegate = CreateDefaultDelegate(this,_closure_default_delegate_funcz); _generator_default_delegate = CreateDefaultDelegate(this,_generator_default_delegate_funcz); _thread_default_delegate = CreateDefaultDelegate(this,_thread_default_delegate_funcz); _class_default_delegate = CreateDefaultDelegate(this,_class_default_delegate_funcz); _instance_default_delegate = CreateDefaultDelegate(this,_instance_default_delegate_funcz); _weakref_default_delegate = CreateDefaultDelegate(this,_weakref_default_delegate_funcz); } SQSharedState::~SQSharedState() { if(_releasehook) { _releasehook(_foreignptr,0); _releasehook = NULL; } _constructoridx.Null(); _table(_registry)->Finalize(); _table(_consts)->Finalize(); _table(_metamethodsmap)->Finalize(); _registry.Null(); _consts.Null(); _metamethodsmap.Null(); while(!_systemstrings->empty()) { _systemstrings->back().Null(); _systemstrings->pop_back(); } _thread(_root_vm)->Finalize(); _root_vm.Null(); _table_default_delegate.Null(); _array_default_delegate.Null(); _string_default_delegate.Null(); _number_default_delegate.Null(); _closure_default_delegate.Null(); _generator_default_delegate.Null(); _thread_default_delegate.Null(); _class_default_delegate.Null(); _instance_default_delegate.Null(); _weakref_default_delegate.Null(); _refs_table.Finalize(); #ifndef NO_GARBAGE_COLLECTOR SQCollectable *t = _gc_chain; SQCollectable *nx = NULL; if(t) { t->_uiRef++; while(t) { t->Finalize(); nx = t->_next; if(nx) nx->_uiRef++; if(--t->_uiRef == 0) t->Release(); t = nx; } } assert(_gc_chain==NULL); //just to proove a theory while(_gc_chain){ _gc_chain->_uiRef++; _gc_chain->Release(); } #endif sq_delete(_types,SQObjectPtrVec); sq_delete(_systemstrings,SQObjectPtrVec); sq_delete(_metamethods,SQObjectPtrVec); sq_delete(_stringtable,SQStringTable); if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize); } SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) { if(sq_type(name) != OT_STRING) return -1; SQObjectPtr ret; if(_table(_metamethodsmap)->Get(name,ret)) { return _integer(ret); } return -1; } #ifndef NO_GARBAGE_COLLECTOR void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) { switch(sq_type(o)){ case OT_TABLE:_table(o)->Mark(chain);break; case OT_ARRAY:_array(o)->Mark(chain);break; case OT_USERDATA:_userdata(o)->Mark(chain);break; case OT_CLOSURE:_closure(o)->Mark(chain);break; case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break; case OT_GENERATOR:_generator(o)->Mark(chain);break; case OT_THREAD:_thread(o)->Mark(chain);break; case OT_CLASS:_class(o)->Mark(chain);break; case OT_INSTANCE:_instance(o)->Mark(chain);break; case OT_OUTER:_outer(o)->Mark(chain);break; case OT_FUNCPROTO:_funcproto(o)->Mark(chain);break; default: break; //shutup compiler } } void SQSharedState::RunMark(SQVM* SQ_UNUSED_ARG(vm),SQCollectable **tchain) { SQVM *vms = _thread(_root_vm); vms->Mark(tchain); _refs_table.Mark(tchain); MarkObject(_registry,tchain); MarkObject(_consts,tchain); MarkObject(_metamethodsmap,tchain); MarkObject(_table_default_delegate,tchain); MarkObject(_array_default_delegate,tchain); MarkObject(_string_default_delegate,tchain); MarkObject(_number_default_delegate,tchain); MarkObject(_generator_default_delegate,tchain); MarkObject(_thread_default_delegate,tchain); MarkObject(_closure_default_delegate,tchain); MarkObject(_class_default_delegate,tchain); MarkObject(_instance_default_delegate,tchain); MarkObject(_weakref_default_delegate,tchain); } SQInteger SQSharedState::ResurrectUnreachable(SQVM *vm) { SQInteger n=0; SQCollectable *tchain=NULL; RunMark(vm,&tchain); SQCollectable *resurrected = _gc_chain; SQCollectable *t = resurrected; _gc_chain = tchain; SQArray *ret = NULL; if(resurrected) { ret = SQArray::Create(this,0); SQCollectable *rlast = NULL; while(t) { rlast = t; SQObjectType type = t->GetType(); if(type != OT_FUNCPROTO && type != OT_OUTER) { SQObject sqo; sqo._type = type; sqo._unVal.pRefCounted = t; ret->Append(sqo); } t = t->_next; n++; } assert(rlast->_next == NULL); rlast->_next = _gc_chain; if(_gc_chain) { _gc_chain->_prev = rlast; } _gc_chain = resurrected; } t = _gc_chain; while(t) { t->UnMark(); t = t->_next; } if(ret) { SQObjectPtr temp = ret; vm->Push(temp); } else { vm->PushNull(); } return n; } SQInteger SQSharedState::CollectGarbage(SQVM *vm) { SQInteger n = 0; SQCollectable *tchain = NULL; RunMark(vm,&tchain); SQCollectable *t = _gc_chain; SQCollectable *nx = NULL; if(t) { t->_uiRef++; while(t) { t->Finalize(); nx = t->_next; if(nx) nx->_uiRef++; if(--t->_uiRef == 0) t->Release(); t = nx; n++; } } t = tchain; while(t) { t->UnMark(); t = t->_next; } _gc_chain = tchain; return n; } #endif #ifndef NO_GARBAGE_COLLECTOR void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c) { c->_prev = NULL; c->_next = *chain; if(*chain) (*chain)->_prev = c; *chain = c; } void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c) { if(c->_prev) c->_prev->_next = c->_next; else *chain = c->_next; if(c->_next) c->_next->_prev = c->_prev; c->_next = NULL; c->_prev = NULL; } #endif SQChar* SQSharedState::GetScratchPad(SQInteger size) { SQInteger newsize; if(size>0) { if(_scratchpadsize < size) { newsize = size + (size>>1); _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); _scratchpadsize = newsize; }else if(_scratchpadsize >= (size<<5)) { newsize = _scratchpadsize >> 1; _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); _scratchpadsize = newsize; } } return _scratchpad; } RefTable::RefTable() { AllocNodes(4); } void RefTable::Finalize() { RefNode *nodes = _nodes; for(SQUnsignedInteger n = 0; n < _numofslots; n++) { nodes->obj.Null(); nodes++; } } RefTable::~RefTable() { SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode))); } #ifndef NO_GARBAGE_COLLECTOR void RefTable::Mark(SQCollectable **chain) { RefNode *nodes = (RefNode *)_nodes; for(SQUnsignedInteger n = 0; n < _numofslots; n++) { if(sq_type(nodes->obj) != OT_NULL) { SQSharedState::MarkObject(nodes->obj,chain); } nodes++; } } #endif void RefTable::AddRef(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,true); ref->refs++; } SQUnsignedInteger RefTable::GetRefCount(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,true); return ref->refs; } SQBool RefTable::Release(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,false); if(ref) { if(--ref->refs == 0) { SQObjectPtr o = ref->obj; if(prev) { prev->next = ref->next; } else { _buckets[mainpos] = ref->next; } ref->next = _freelist; _freelist = ref; _slotused--; ref->obj.Null(); //<>test for shrink? return SQTrue; } } else { assert(0); } return SQFalse; } void RefTable::Resize(SQUnsignedInteger size) { RefNode **oldbucks = _buckets; RefNode *t = _nodes; SQUnsignedInteger oldnumofslots = _numofslots; AllocNodes(size); //rehash SQUnsignedInteger nfound = 0; for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) { if(sq_type(t->obj) != OT_NULL) { //add back; assert(t->refs != 0); RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj); nn->refs = t->refs; t->obj.Null(); nfound++; } t++; } assert(nfound == oldnumofslots); SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode))); } RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj) { RefNode *t = _buckets[mainpos]; RefNode *newnode = _freelist; newnode->obj = obj; _buckets[mainpos] = newnode; _freelist = _freelist->next; newnode->next = t; assert(newnode->refs == 0); _slotused++; return newnode; } RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add) { RefNode *ref; mainpos = ::HashObj(obj)&(_numofslots-1); *prev = NULL; for (ref = _buckets[mainpos]; ref; ) { if(_rawval(ref->obj) == _rawval(obj) && sq_type(ref->obj) == sq_type(obj)) break; *prev = ref; ref = ref->next; } if(ref == NULL && add) { if(_numofslots == _slotused) { assert(_freelist == 0); Resize(_numofslots*2); mainpos = ::HashObj(obj)&(_numofslots-1); } ref = Add(mainpos,obj); } return ref; } void RefTable::AllocNodes(SQUnsignedInteger size) { RefNode **bucks; RefNode *nodes; bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode))); nodes = (RefNode *)&bucks[size]; RefNode *temp = nodes; SQUnsignedInteger n; for(n = 0; n < size - 1; n++) { bucks[n] = NULL; temp->refs = 0; new (&temp->obj) SQObjectPtr; temp->next = temp+1; temp++; } bucks[n] = NULL; temp->refs = 0; new (&temp->obj) SQObjectPtr; temp->next = NULL; _freelist = nodes; _nodes = nodes; _buckets = bucks; _slotused = 0; _numofslots = size; } ////////////////////////////////////////////////////////////////////////// //SQStringTable /* * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) * http://www.lua.org/copyright.html#4 * http://www.lua.org/source/4.0.1/src_lstring.c.html */ SQStringTable::SQStringTable(SQSharedState *ss) { _sharedstate = ss; AllocNodes(4); _slotused = 0; } SQStringTable::~SQStringTable() { SQ_FREE(_strings,sizeof(SQString*)*_numofslots); _strings = NULL; } void SQStringTable::AllocNodes(SQInteger size) { _numofslots = size; _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots); memset(_strings,0,sizeof(SQString*)*_numofslots); } SQString *SQStringTable::Add(const SQChar *news,SQInteger len) { if(len<0) len = (SQInteger)scstrlen(news); SQHash newhash = ::_hashstr(news,len); SQHash h = newhash&(_numofslots-1); SQString *s; for (s = _strings[h]; s; s = s->_next){ if(s->_len == len && (!memcmp(news,s->_val,sq_rsl(len)))) return s; //found } SQString *t = (SQString *)SQ_MALLOC(sq_rsl(len)+sizeof(SQString)); new (t) SQString; t->_sharedstate = _sharedstate; memcpy(t->_val,news,sq_rsl(len)); t->_val[len] = _SC('\0'); t->_len = len; t->_hash = newhash; t->_next = _strings[h]; _strings[h] = t; _slotused++; if (_slotused > _numofslots) /* too crowded? */ Resize(_numofslots*2); return t; } void SQStringTable::Resize(SQInteger size) { SQInteger oldsize=_numofslots; SQString **oldtable=_strings; AllocNodes(size); for (SQInteger i=0; i_next; SQHash h = p->_hash&(_numofslots-1); p->_next = _strings[h]; _strings[h] = p; p = next; } } SQ_FREE(oldtable,oldsize*sizeof(SQString*)); } void SQStringTable::Remove(SQString *bs) { SQString *s; SQString *prev=NULL; SQHash h = bs->_hash&(_numofslots - 1); for (s = _strings[h]; s; ){ if(s == bs){ if(prev) prev->_next = s->_next; else _strings[h] = s->_next; _slotused--; SQInteger slen = s->_len; s->~SQString(); SQ_FREE(s,sizeof(SQString) + sq_rsl(slen)); return; } prev = s; s = s->_next; } assert(0);//if this fail something is wrong } simutrans-124.5/src/squirrel/squirrel/sqstate.h000066400000000000000000000102571520675460500217310ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQSTATE_H_ #define _SQSTATE_H_ #include "squtils.h" #include "sqobject.h" struct SQString; struct SQTable; //max number of character for a printed number #define NUMBER_MAX_CHAR 50 struct SQStringTable { SQStringTable(SQSharedState*ss); ~SQStringTable(); SQString *Add(const SQChar *,SQInteger len); void Remove(SQString *); private: void Resize(SQInteger size); void AllocNodes(SQInteger size); SQString **_strings; SQUnsignedInteger _numofslots; SQUnsignedInteger _slotused; SQSharedState *_sharedstate; }; struct RefTable { struct RefNode { SQObjectPtr obj; SQUnsignedInteger refs; struct RefNode *next; }; RefTable(); ~RefTable(); void AddRef(SQObject &obj); SQBool Release(SQObject &obj); SQUnsignedInteger GetRefCount(SQObject &obj); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); #endif void Finalize(); private: RefNode *Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add); RefNode *Add(SQHash mainpos,SQObject &obj); void Resize(SQUnsignedInteger size); void AllocNodes(SQUnsignedInteger size); SQUnsignedInteger _numofslots; SQUnsignedInteger _slotused; RefNode *_nodes; RefNode *_freelist; RefNode **_buckets; }; #define ADD_STRING(ss,str,len) ss->_stringtable->Add(str,len) #define REMOVE_STRING(ss,bstr) ss->_stringtable->Remove(bstr) struct SQObjectPtr; struct SQSharedState { SQSharedState(); ~SQSharedState(); void Init(); public: SQChar* GetScratchPad(SQInteger size); SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name); #ifndef NO_GARBAGE_COLLECTOR SQInteger CollectGarbage(SQVM *vm); void RunMark(SQVM *vm,SQCollectable **tchain); SQInteger ResurrectUnreachable(SQVM *vm); static void MarkObject(SQObjectPtr &o,SQCollectable **chain); #endif SQObjectPtrVec *_metamethods; SQObjectPtr _metamethodsmap; SQObjectPtrVec *_systemstrings; SQObjectPtrVec *_types; SQStringTable *_stringtable; RefTable _refs_table; SQObjectPtr _registry; SQObjectPtr _consts; SQObjectPtr _constructoridx; #ifndef NO_GARBAGE_COLLECTOR SQCollectable *_gc_chain; #endif SQObjectPtr _root_vm; SQObjectPtr _table_default_delegate; static const SQRegFunction _table_default_delegate_funcz[]; SQObjectPtr _array_default_delegate; static const SQRegFunction _array_default_delegate_funcz[]; SQObjectPtr _string_default_delegate; static const SQRegFunction _string_default_delegate_funcz[]; SQObjectPtr _number_default_delegate; static const SQRegFunction _number_default_delegate_funcz[]; SQObjectPtr _generator_default_delegate; static const SQRegFunction _generator_default_delegate_funcz[]; SQObjectPtr _closure_default_delegate; static const SQRegFunction _closure_default_delegate_funcz[]; SQObjectPtr _thread_default_delegate; static const SQRegFunction _thread_default_delegate_funcz[]; SQObjectPtr _class_default_delegate; static const SQRegFunction _class_default_delegate_funcz[]; SQObjectPtr _instance_default_delegate; static const SQRegFunction _instance_default_delegate_funcz[]; SQObjectPtr _weakref_default_delegate; static const SQRegFunction _weakref_default_delegate_funcz[]; SQCOMPILERERROR _compilererrorhandler; SQPRINTFUNCTION _printfunc; SQPRINTFUNCTION _errorfunc; bool _debuginfo; bool _notifyallexceptions; SQUserPointer _foreignptr; SQRELEASEHOOK _releasehook; private: SQChar *_scratchpad; SQInteger _scratchpadsize; }; #define _sp(s) (_sharedstate->GetScratchPad(s)) #define _spval (_sharedstate->GetScratchPad(-1)) #define _table_ddel _table(_sharedstate->_table_default_delegate) #define _array_ddel _table(_sharedstate->_array_default_delegate) #define _string_ddel _table(_sharedstate->_string_default_delegate) #define _number_ddel _table(_sharedstate->_number_default_delegate) #define _generator_ddel _table(_sharedstate->_generator_default_delegate) #define _closure_ddel _table(_sharedstate->_closure_default_delegate) #define _thread_ddel _table(_sharedstate->_thread_default_delegate) #define _class_ddel _table(_sharedstate->_class_default_delegate) #define _instance_ddel _table(_sharedstate->_instance_default_delegate) #define _weakref_ddel _table(_sharedstate->_weakref_default_delegate) bool CompileTypemask(SQIntVec &res,const SQChar *typemask); #endif simutrans-124.5/src/squirrel/squirrel/sqstring.h000066400000000000000000000013541520675460500221150ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQSTRING_H_ #define _SQSTRING_H_ inline SQHash _hashstr (const SQChar *s, size_t l) { SQHash h = (SQHash)l; /* seed */ size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */ for (; l>=step; l-=step) h = h ^ ((h<<5)+(h>>2)+(unsigned short)*(s++)); return h; } struct SQString : public SQRefCounted { SQString(){} ~SQString(){} public: static SQString *Create(SQSharedState *ss, const SQChar *, SQInteger len = -1 ); SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); void Release(); SQSharedState *_sharedstate; SQString *_next; //chain for the string table SQInteger _len; SQHash _hash; SQChar _val[1]; }; #endif simutrans-124.5/src/squirrel/squirrel/sqtable.cc000066400000000000000000000117171520675460500220400ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqtable.h" #include "sqfuncproto.h" #include "sqclosure.h" SQTable::SQTable(SQSharedState *ss,SQInteger nInitialSize) { SQInteger pow2size=MINPOWER2; while(nInitialSize>pow2size)pow2size=pow2size<<1; AllocNodes(pow2size); _usednodes = 0; _delegate = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_sharedstate->_gc_chain,this); } void SQTable::Remove(const SQObjectPtr &key) { _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); if (n) { n->val.Null(); n->key.Null(); _usednodes--; Rehash(false); } } void SQTable::AllocNodes(SQInteger nSize) { _HashNode *nodes=(_HashNode *)SQ_MALLOC(sizeof(_HashNode)*nSize); for(SQInteger i=0;i= oldsize-oldsize/4) /* using more than 3/4? */ AllocNodes(oldsize*2); else if (nelems <= oldsize/4 && /* less than 1/4? */ oldsize > MINPOWER2) AllocNodes(oldsize/2); else if(force) AllocNodes(oldsize); else return; _usednodes = 0; for (SQInteger i=0; ikey) != OT_NULL) NewSlot(old->key,old->val); } for(SQInteger k=0;k_nodes; _HashNode *src = _nodes; _HashNode *dst = nt->_nodes; SQInteger n = 0; for(n = 0; n < _numofnodes; n++) { dst->key = src->key; dst->val = src->val; if(src->next) { assert(src->next > basesrc); dst->next = basedst + (src->next - basesrc); assert(dst != dst->next); } dst++; src++; } assert(_firstfree > basesrc); assert(_firstfree != NULL); nt->_firstfree = basedst + (_firstfree - basesrc); nt->_usednodes = _usednodes; #else SQInteger ridx=0; SQObjectPtr key,val; while((ridx=Next(true,ridx,key,val))!=-1){ nt->NewSlot(key,val); } #endif nt->SetDelegate(_delegate); return nt; } bool SQTable::Get(const SQObjectPtr &key,SQObjectPtr &val) { if(sq_type(key) == OT_NULL) return false; _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); if (n) { val = _realval(n->val); return true; } return false; } bool SQTable::NewSlot(const SQObjectPtr &key,const SQObjectPtr &val) { assert(sq_type(key) != OT_NULL); SQHash h = HashObj(key) & (_numofnodes - 1); _HashNode *n = _Get(key, h); if (n) { n->val = val; return false; } _HashNode *mp = &_nodes[h]; n = mp; //key not found I'll insert it //main pos is not free if(sq_type(mp->key) != OT_NULL) { n = _firstfree; /* get a free place */ SQHash mph = HashObj(mp->key) & (_numofnodes - 1); _HashNode *othern; /* main position of colliding node */ if (mp > n && (othern = &_nodes[mph]) != mp){ /* yes; move colliding node into free position */ while (othern->next != mp){ assert(othern->next != NULL); othern = othern->next; /* find previous */ } othern->next = n; /* redo the chain with `n' in place of `mp' */ n->key = mp->key; n->val = mp->val;/* copy colliding node into free pos. (mp->next also goes) */ n->next = mp->next; mp->key.Null(); mp->val.Null(); mp->next = NULL; /* now `mp' is free */ } else{ /* new node will go into free position */ n->next = mp->next; /* chain new position */ mp->next = n; mp = n; } } mp->key = key; for (;;) { /* correct `firstfree' */ if (sq_type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) { mp->val = val; _usednodes++; return true; /* OK; table still has a free place */ } else if (_firstfree == _nodes) break; /* cannot decrement from here */ else (_firstfree)--; } Rehash(true); return NewSlot(key, val); } SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) { SQInteger idx = (SQInteger)TranslateIndex(refpos); while (idx < _numofnodes) { if(sq_type(_nodes[idx].key) != OT_NULL) { //first found _HashNode &n = _nodes[idx]; outkey = n.key; outval = getweakrefs?(SQObject)n.val:_realval(n.val); //return idx for the next iteration return ++idx; } ++idx; } //nothing to iterate anymore return -1; } bool SQTable::Set(const SQObjectPtr &key, const SQObjectPtr &val) { _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); if (n) { n->val = val; return true; } return false; } void SQTable::_ClearNodes() { for(SQInteger i = 0;i < _numofnodes; i++) { _HashNode &n = _nodes[i]; n.key.Null(); n.val.Null(); } } void SQTable::Finalize() { _ClearNodes(); SetDelegate(NULL); } void SQTable::Clear() { _ClearNodes(); _usednodes = 0; Rehash(true); } simutrans-124.5/src/squirrel/squirrel/sqtable.h000066400000000000000000000054341520675460500217010ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQTABLE_H_ #define _SQTABLE_H_ /* * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) * http://www.lua.org/copyright.html#4 * http://www.lua.org/source/4.0.1/src_ltable.c.html */ #include "sqstring.h" #define hashptr(p) ((SQHash)(((SQInteger)p) >> 3)) inline SQHash HashObj(const SQObject &key) { switch(sq_type(key)) { case OT_STRING: return _string(key)->_hash; case OT_FLOAT: return (SQHash)((SQInteger)_float(key)); case OT_BOOL: case OT_INTEGER: return (SQHash)((SQInteger)_integer(key)); default: return hashptr(key._unVal.pRefCounted); } } struct SQTable : public SQDelegable { private: struct _HashNode { _HashNode() { next = NULL; } SQObjectPtr val; SQObjectPtr key; _HashNode *next; }; _HashNode *_firstfree; _HashNode *_nodes; SQInteger _numofnodes; SQInteger _usednodes; /////////////////////////// void AllocNodes(SQInteger nSize); void Rehash(bool force); SQTable(SQSharedState *ss, SQInteger nInitialSize); void _ClearNodes(); public: static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize) { SQTable *newtable = (SQTable*)SQ_MALLOC(sizeof(SQTable)); new (newtable) SQTable(ss, nInitialSize); newtable->_delegate = NULL; return newtable; } void Finalize(); SQTable *Clone(); ~SQTable() { SetDelegate(NULL); REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); for (SQInteger i = 0; i < _numofnodes; i++) _nodes[i].~_HashNode(); SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode)); } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); SQObjectType GetType() {return OT_TABLE;} #endif inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash) { _HashNode *n = &_nodes[hash]; do{ if(_rawval(n->key) == _rawval(key) && sq_type(n->key) == sq_type(key)){ return n; } }while((n = n->next)); return NULL; } //for compiler use inline bool GetStr(const SQChar* key,SQInteger keylen,SQObjectPtr &val) { SQHash hash = _hashstr(key,keylen); _HashNode *n = &_nodes[hash & (_numofnodes - 1)]; _HashNode *res = NULL; do{ if(sq_type(n->key) == OT_STRING && (scstrcmp(_stringval(n->key),key) == 0)){ res = n; break; } }while((n = n->next)); if (res) { val = _realval(res->val); return true; } return false; } bool Get(const SQObjectPtr &key,SQObjectPtr &val); void Remove(const SQObjectPtr &key); bool Set(const SQObjectPtr &key, const SQObjectPtr &val); //returns true if a new slot has been created false if it was already present bool NewSlot(const SQObjectPtr &key,const SQObjectPtr &val); SQInteger Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); SQInteger CountUsed(){ return _usednodes;} void Clear(); void Release() { sq_delete(this, SQTable); } }; #endif simutrans-124.5/src/squirrel/squirrel/squserdata.h000066400000000000000000000017571520675460500224260ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQUSERDATA_H_ #define _SQUSERDATA_H_ struct SQUserData : SQDelegable { SQUserData(SQSharedState *ss){ _delegate = 0; _hook = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain, this); } ~SQUserData() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this); SetDelegate(NULL); } static SQUserData* Create(SQSharedState *ss, SQInteger size) { SQUserData* ud = (SQUserData*)SQ_MALLOC(sq_aligning(sizeof(SQUserData))+size); new (ud) SQUserData(ss); ud->_size = size; ud->_typetag = 0; return ud; } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize(){SetDelegate(NULL);} SQObjectType GetType(){ return OT_USERDATA;} #endif void Release() { if (_hook) _hook((SQUserPointer)sq_aligning(this + 1),_size); SQInteger tsize = _size; this->~SQUserData(); SQ_FREE(this, sq_aligning(sizeof(SQUserData)) + tsize); } SQInteger _size; SQRELEASEHOOK _hook; SQUserPointer _typetag; //SQChar _val[1]; }; #endif simutrans-124.5/src/squirrel/squirrel/squtils.h000066400000000000000000000057021520675460500217500ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQUTILS_H_ #define _SQUTILS_H_ void *sq_vm_malloc(SQUnsignedInteger size); void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size); void sq_vm_free(void *p,SQUnsignedInteger size); #define sq_new(__ptr,__type) {__ptr=(__type *)sq_vm_malloc(sizeof(__type));new (__ptr) __type;} #define sq_delete(__ptr,__type) {__ptr->~__type();sq_vm_free(__ptr,sizeof(__type));} #define SQ_MALLOC(__size) sq_vm_malloc((__size)); #define SQ_FREE(__ptr,__size) sq_vm_free((__ptr),(__size)); #define SQ_REALLOC(__ptr,__oldsize,__size) sq_vm_realloc((__ptr),(__oldsize),(__size)); #define sq_aligning(v) (((size_t)(v) + (SQ_ALIGNMENT-1)) & (~(SQ_ALIGNMENT-1))) //sqvector mini vector class, supports objects by value template class sqvector { public: sqvector() { _vals = NULL; _size = 0; _allocated = 0; } sqvector(const sqvector& v) { copy(v); } void copy(const sqvector& v) { if(_size) { resize(0); //destroys all previous stuff } //resize(v._size); if(v._size > _allocated) { _realloc(v._size); } for(SQUnsignedInteger i = 0; i < v._size; i++) { new ((void *)&_vals[i]) T(v._vals[i]); } _size = v._size; } ~sqvector() { if(_allocated) { for(SQUnsignedInteger i = 0; i < _size; i++) _vals[i].~T(); SQ_FREE(_vals, (_allocated * sizeof(T))); } } void reserve(SQUnsignedInteger newsize) { _realloc(newsize); } void resize(SQUnsignedInteger newsize, const T& fill = T()) { if(newsize > _allocated) _realloc(newsize); if(newsize > _size) { while(_size < newsize) { new ((void *)&_vals[_size]) T(fill); _size++; } } else{ for(SQUnsignedInteger i = newsize; i < _size; i++) { _vals[i].~T(); } _size = newsize; } } void shrinktofit() { if(_size > 4) { _realloc(_size); } } T& top() const { return _vals[_size - 1]; } inline SQUnsignedInteger size() const { return _size; } bool empty() const { return (_size <= 0); } inline T &push_back(const T& val = T()) { if(_allocated <= _size) _realloc(_size * 2); return *(new ((void *)&_vals[_size++]) T(val)); } inline void pop_back() { _size--; _vals[_size].~T(); } void insert(SQUnsignedInteger idx, const T& val) { resize(_size + 1); for(SQUnsignedInteger i = _size - 1; i > idx; i--) { _vals[i] = _vals[i - 1]; } _vals[idx] = val; } void remove(SQUnsignedInteger idx) { _vals[idx].~T(); if(idx < (_size - 1)) { memmove(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - idx - 1)); } _size--; } SQUnsignedInteger capacity() { return _allocated; } inline T &back() const { return _vals[_size - 1]; } inline T& operator[](SQUnsignedInteger pos) const{ return _vals[pos]; } T* _vals; private: void _realloc(SQUnsignedInteger newsize) { newsize = (newsize > 0)?newsize:4; _vals = (T*)SQ_REALLOC(_vals, _allocated * sizeof(T), newsize * sizeof(T)); _allocated = newsize; } SQUnsignedInteger _size; SQUnsignedInteger _allocated; }; #endif simutrans-124.5/src/squirrel/squirrel/sqvm.cc000066400000000000000000001454721520675460500214010ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include #include #include "sqopcodes.h" #include "sqvm.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqstring.h" #include "sqtable.h" #include "squserdata.h" #include "sqarray.h" #include "sqclass.h" #define TOP() (_stack._vals[_top-1]) #define TARGET _stack._vals[_stackbase+arg0] #define STK(a) _stack._vals[_stackbase+(a)] bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) { SQInteger res; if((sq_type(o1)| sq_type(o2)) == OT_INTEGER) { SQInteger i1 = _integer(o1), i2 = _integer(o2); switch(op) { case BW_AND: res = i1 & i2; break; case BW_OR: res = i1 | i2; break; case BW_XOR: res = i1 ^ i2; break; case BW_SHIFTL: res = i1 << i2; break; case BW_SHIFTR: res = i1 >> i2; break; case BW_USHIFTR:res = (SQInteger)(*((SQUnsignedInteger*)&i1) >> i2); break; default: { Raise_Error(_SC("internal vm error bitwise op failed")); return false; } } } else { Raise_Error(_SC("bitwise op between '%s' and '%s'"),GetTypeName(o1),GetTypeName(o2)); return false;} trg = res; return true; } #define _ARITH_(op,trg,o1,o2) \ { \ SQInteger tmask = sq_type(o1)|sq_type(o2); \ switch(tmask) { \ case OT_INTEGER: trg = _integer(o1) op _integer(o2);break; \ case (OT_FLOAT|OT_INTEGER): \ case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break;\ default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\ } \ } #define _ARITH_NOZERO(op,trg,o1,o2,err) \ { \ SQInteger tmask = sq_type(o1)|sq_type(o2); \ switch(tmask) { \ case OT_INTEGER: { SQInteger i2 = _integer(o2); if(i2 == 0) { Raise_Error(err); SQ_THROW(); } trg = _integer(o1) op i2; } break;\ case (OT_FLOAT|OT_INTEGER): \ case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break;\ default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\ } \ } bool SQVM::ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) { SQInteger tmask = sq_type(o1)| sq_type(o2); switch(tmask) { case OT_INTEGER:{ SQInteger res, i1 = _integer(o1), i2 = _integer(o2); switch(op) { case '+': res = i1 + i2; break; case '-': res = i1 - i2; break; case '/': if (i2 == 0) { Raise_Error(_SC("division by zero")); return false; } else if (i2 == -1 && i1 == INT_MIN) { Raise_Error(_SC("integer overflow")); return false; } res = i1 / i2; break; case '*': res = i1 * i2; break; case '%': if (i2 == 0) { Raise_Error(_SC("modulo by zero")); return false; } else if (i2 == -1 && i1 == INT_MIN) { res = 0; break; } res = i1 % i2; break; default: res = 0xDEADBEEF; } trg = res; } break; case (OT_FLOAT|OT_INTEGER): case (OT_FLOAT):{ SQFloat res, f1 = tofloat(o1), f2 = tofloat(o2); switch(op) { case '+': res = f1 + f2; break; case '-': res = f1 - f2; break; case '/': res = f1 / f2; break; case '*': res = f1 * f2; break; case '%': res = SQFloat(fmod((double)f1,(double)f2)); break; default: res = 0x0f; } trg = res; } break; default: if(op == '+' && (tmask & _RT_STRING)){ if(!StringCat(o1, o2, trg)) return false; } else if(!ArithMetaMethod(op,o1,o2,trg)) { return false; } } return true; } SQVM::SQVM(SQSharedState *ss) { _sharedstate=ss; _suspended = SQFalse; _suspended_target = -1; _suspended_root = SQFalse; _suspended_traps = -1; _foreignptr = NULL; _nnativecalls = 0; _nmetamethodscall = 0; _lasterror.Null(); _errorhandler.Null(); _debughook = false; _debughook_native = NULL; _debughook_closure.Null(); _openouters = NULL; ci = NULL; _releasehook = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _ops_remaining = 0; _ops_total = 0; _ops_grace_amount = 500; _throw_if_no_ops = true; } void SQVM::Finalize() { if(_releasehook) { _releasehook(_foreignptr,0); _releasehook = NULL; } if(_openouters) CloseOuters(&_stack._vals[0]); _roottable.Null(); _lasterror.Null(); _errorhandler.Null(); _debughook = false; _debughook_native = NULL; _debughook_closure.Null(); temp_reg.Null(); _callstackdata.resize(0); SQInteger size=_stack.size(); for(SQInteger i=0;i_gc_chain,this); } bool SQVM::ArithMetaMethod(SQInteger op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest) { SQMetaMethod mm; switch(op){ case _SC('+'): mm=MT_ADD; break; case _SC('-'): mm=MT_SUB; break; case _SC('/'): mm=MT_DIV; break; case _SC('*'): mm=MT_MUL; break; case _SC('%'): mm=MT_MODULO; break; default: mm = MT_ADD; assert(0); break; //shutup compiler } if(is_delegable(o1) && _delegable(o1)->_delegate) { SQObjectPtr closure; if(_delegable(o1)->GetMetaMethod(this, mm, closure)) { Push(o1);Push(o2); return CallMetaMethod(closure,mm,2,dest); } } Raise_Error(_SC("arith op %c on between '%s' and '%s'"),op,GetTypeName(o1),GetTypeName(o2)); return false; } bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o) { switch(sq_type(o)) { case OT_INTEGER: trg = -_integer(o); return true; case OT_FLOAT: trg = -_float(o); return true; case OT_TABLE: case OT_USERDATA: case OT_INSTANCE: if(_delegable(o)->_delegate) { SQObjectPtr closure; if(_delegable(o)->GetMetaMethod(this, MT_UNM, closure)) { Push(o); if(!CallMetaMethod(closure, MT_UNM, 1, temp_reg)) return false; _Swap(trg,temp_reg); return true; } } default:break; //shutup compiler } Raise_Error(_SC("attempt to negate a %s"), GetTypeName(o)); return false; } #define _RET_SUCCEED(exp) { result = (exp); return true; } bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) { SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); if(t1 == t2) { if(_rawval(o1) == _rawval(o2))_RET_SUCCEED(0); SQObjectPtr res; switch(t1){ case OT_STRING: _RET_SUCCEED(scstrcmp(_stringval(o1),_stringval(o2))); case OT_INTEGER: _RET_SUCCEED((_integer(o1)<_integer(o2))?-1:1); case OT_FLOAT: _RET_SUCCEED((_float(o1)<_float(o2))?-1:1); case OT_TABLE: case OT_USERDATA: case OT_INSTANCE: if(_delegable(o1)->_delegate) { SQObjectPtr closure; if(_delegable(o1)->GetMetaMethod(this, MT_CMP, closure)) { Push(o1);Push(o2); if(CallMetaMethod(closure,MT_CMP,2,res)) { if(sq_type(res) != OT_INTEGER) { Raise_Error(_SC("_cmp must return an integer")); return false; } _RET_SUCCEED(_integer(res)) } return false; } } //continues through (no break needed) default: _RET_SUCCEED( _userpointer(o1) < _userpointer(o2)?-1:1 ); } assert(0); //if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; } // _RET_SUCCEED(_integer(res)); } else{ if(sq_isnumeric(o1) && sq_isnumeric(o2)){ if((t1==OT_INTEGER) && (t2==OT_FLOAT)) { if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); } else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); } _RET_SUCCEED(1); } else{ if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); } else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); } _RET_SUCCEED(1); } } else if(t1==OT_NULL) {_RET_SUCCEED(-1);} else if(t2==OT_NULL) {_RET_SUCCEED(1);} else { Raise_CompareError(o1,o2); return false; } } assert(0); _RET_SUCCEED(0); //cannot happen } bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res) { SQInteger r; if(ObjCmp(o1,o2,r)) { switch(op) { case CMP_G: res = (r > 0); return true; case CMP_GE: res = (r >= 0); return true; case CMP_L: res = (r < 0); return true; case CMP_LE: res = (r <= 0); return true; case CMP_3W: res = r; return true; } assert(0); } return false; } bool SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res) { switch(sq_type(o)) { case OT_STRING: res = o; return true; case OT_FLOAT: scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR),_SC("%g"),_float(o)); break; case OT_INTEGER: scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR),_PRINT_INT_FMT,_integer(o)); break; case OT_BOOL: scsprintf(_sp(sq_rsl(6)),sq_rsl(6),_integer(o)?_SC("true"):_SC("false")); break; case OT_NULL: scsprintf(_sp(sq_rsl(5)),sq_rsl(5),_SC("null")); break; case OT_TABLE: case OT_USERDATA: case OT_INSTANCE: if(_delegable(o)->_delegate) { SQObjectPtr closure; if(_delegable(o)->GetMetaMethod(this, MT_TOSTRING, closure)) { Push(o); if(CallMetaMethod(closure,MT_TOSTRING,1,res)) { if(sq_type(res) == OT_STRING) return true; } else { return false; } } } default: scsprintf(_sp(sq_rsl((sizeof(void*)*2)+NUMBER_MAX_CHAR)),sq_rsl((sizeof(void*)*2)+NUMBER_MAX_CHAR),_SC("(%s : 0x%p)"),GetTypeName(o),(void*)_rawval(o)); } res = SQString::Create(_ss(this),_spval); return true; } bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest) { SQObjectPtr a, b; if(!ToString(str, a)) return false; if(!ToString(obj, b)) return false; SQInteger l = _string(a)->_len , ol = _string(b)->_len; SQChar *s = _sp(sq_rsl(l + ol + 1)); memcpy(s, _stringval(a), sq_rsl(l)); memcpy(s + l, _stringval(b), sq_rsl(ol)); dest = SQString::Create(_ss(this), _spval, l + ol); return true; } bool SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest) { if(is_delegable(obj1) && _delegable(obj1)->_delegate) { SQObjectPtr closure; if(_delegable(obj1)->GetMetaMethod(this, MT_TYPEOF, closure)) { Push(obj1); return CallMetaMethod(closure,MT_TYPEOF,1,dest); } } dest = SQString::Create(_ss(this),GetTypeName(obj1)); return true; } bool SQVM::Init(SQVM *friendvm, SQInteger stacksize) { _stack.resize(stacksize); _alloccallsstacksize = 4; _callstackdata.resize(_alloccallsstacksize); _callsstacksize = 0; _callsstack = &_callstackdata[0]; _stackbase = 0; _top = 0; if(!friendvm) { _roottable = SQTable::Create(_ss(this), 0); sq_base_register(this); } else { _roottable = friendvm->_roottable; _errorhandler = friendvm->_errorhandler; _debughook = friendvm->_debughook; _debughook_native = friendvm->_debughook_native; _debughook_closure = friendvm->_debughook_closure; } return true; } bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQInteger stackbase,bool tailcall) { SQFunctionProto *func = closure->_function; SQInteger paramssize = func->_nparameters; const SQInteger newtop = stackbase + func->_stacksize; SQInteger nargs = args; if(func->_varparams) { paramssize--; if (nargs < paramssize) { const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; const SQChar *name = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s:%s"), nargs, paramssize, src, name); return false; } //dumpstack(stackbase); SQInteger nvargs = nargs - paramssize; SQArray *arr = SQArray::Create(_ss(this),nvargs); SQInteger pbase = stackbase+paramssize; for(SQInteger n = 0; n < nvargs; n++) { arr->_values[n] = _stack._vals[pbase]; _stack._vals[pbase].Null(); pbase++; } _stack._vals[stackbase+paramssize] = arr; //dumpstack(stackbase); } else if (paramssize != nargs) { SQInteger ndef = func->_ndefaultparams; SQInteger diff; if(ndef && nargs < paramssize && (diff = paramssize - nargs) <= ndef) { for(SQInteger n = ndef - diff; n < ndef; n++) { _stack._vals[stackbase + (nargs++)] = closure->_defaultparams[n]; } } else { const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; const SQChar *name = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s:%s"), nargs, paramssize, src, name); return false; } } if(closure->_env) { _stack._vals[stackbase] = closure->_env->_obj; } if(!EnterFrame(stackbase, newtop, tailcall)) return false; ci->_closure = closure; ci->_literals = func->_literals; ci->_ip = func->_instructions; ci->_target = (SQInt32)target; if (_debughook) { CallDebugHook(_SC('c')); } if (closure->_function->_bgenerator) { SQFunctionProto *f = closure->_function; SQGenerator *gen = SQGenerator::Create(_ss(this), closure); if(!gen->Yield(this,f->_stacksize)) return false; SQObjectPtr temp; Return(1, target, temp); STK(target) = gen; } return true; } bool SQVM::Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval) { SQBool _isroot = ci->_root; SQInteger callerbase = _stackbase - ci->_prevstkbase; if (_debughook) { for(SQInteger i=0; i_ncalls; i++) { CallDebugHook(_SC('r')); } } SQObjectPtr *dest; if (_isroot) { dest = &(retval); } else if (ci->_target == -1) { dest = NULL; } else { dest = &_stack._vals[callerbase + ci->_target]; } if (dest) { if(_arg0 != 0xFF) { *dest = _stack._vals[_stackbase+_arg1]; } else { dest->Null(); } //*dest = (_arg0 != 0xFF) ? _stack._vals[_stackbase+_arg1] : _null_; } LeaveFrame(); return _isroot ? true : false; } #define _RET_ON_FAIL(exp) { if(!exp) return false; } bool SQVM::PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr) { SQObjectPtr trg; _RET_ON_FAIL(ARITH_OP( op , trg, a, incr)); target = a; a = trg; return true; } bool SQVM::DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix,SQInteger selfidx) { SQObjectPtr tmp, tself = self, tkey = key; if (!Get(tself, tkey, tmp, 0, selfidx)) { return false; } _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr)) if (!Set(tself, tkey, target,selfidx)) { return false; } if (postfix) target = tmp; return true; } #define arg0 (_i_._arg0) #define sarg0 ((SQInteger)*((const signed char *)&_i_._arg0)) #define arg1 (_i_._arg1) #define sarg1 (*((const SQInt32 *)&_i_._arg1)) #define arg2 (_i_._arg2) #define arg3 (_i_._arg3) #define sarg3 ((SQInteger)*((const signed char *)&_i_._arg3)) SQRESULT SQVM::Suspend() { if (_suspended) return sq_throwerror(this, _SC("cannot suspend an already suspended vm")); if (_nnativecalls!=2) return sq_throwerror(this, _SC("cannot suspend through native calls/metamethods")); return SQ_SUSPEND_FLAG; } #define _FINISH(howmuchtojump) {jump = howmuchtojump; return true; } bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,SQInteger SQ_UNUSED_ARG(arg_2),int exitpos,int &jump) { SQInteger nrefidx; switch(sq_type(o1)) { case OT_TABLE: if((nrefidx = _table(o1)->Next(false,o4, o2, o3)) == -1) _FINISH(exitpos); o4 = (SQInteger)nrefidx; _FINISH(1); case OT_ARRAY: if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(exitpos); o4 = (SQInteger) nrefidx; _FINISH(1); case OT_STRING: if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos); o4 = (SQInteger)nrefidx; _FINISH(1); case OT_CLASS: if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos); o4 = (SQInteger)nrefidx; _FINISH(1); case OT_USERDATA: case OT_INSTANCE: if(_delegable(o1)->_delegate) { SQObjectPtr itr; SQObjectPtr closure; if(_delegable(o1)->GetMetaMethod(this, MT_NEXTI, closure)) { Push(o1); Push(o4); if(CallMetaMethod(closure, MT_NEXTI, 2, itr)) { o4 = o2 = itr; if(sq_type(itr) == OT_NULL) _FINISH(exitpos); if(!Get(o1, itr, o3, 0, DONT_FALL_BACK)) { Raise_Error(_SC("_nexti returned an invalid idx")); // cloud be changed return false; } _FINISH(1); } else { return false; } } Raise_Error(_SC("_nexti failed")); return false; } break; case OT_GENERATOR: if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(exitpos); if(_generator(o1)->_state == SQGenerator::eSuspended) { SQInteger idx = 0; if(sq_type(o4) == OT_INTEGER) { idx = _integer(o4) + 1; } o2 = idx; o4 = idx; _generator(o1)->Resume(this, o3); _FINISH(0); } default: Raise_Error(_SC("cannot iterate %s"), GetTypeName(o1)); } return false; //cannot be hit(just to avoid warnings) } #define COND_LITERAL (arg3!=0?ci->_literals[arg1]:STK(arg1)) #define SQ_THROW() { goto exception_trap; } #define _GUARD(exp) { if(!exp) { SQ_THROW();} } bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func) { SQInteger nouters; SQClosure *closure = SQClosure::Create(_ss(this), func,_table(_roottable)->GetWeakRef(OT_TABLE)); if((nouters = func->_noutervalues)) { for(SQInteger i = 0; i_outervalues[i]; switch(v._type){ case otLOCAL: FindOuter(closure->_outervalues[i], &STK(_integer(v._src))); break; case otOUTER: closure->_outervalues[i] = _closure(ci->_closure)->_outervalues[_integer(v._src)]; break; } } } SQInteger ndefparams; if((ndefparams = func->_ndefaultparams)) { for(SQInteger i = 0; i < ndefparams; i++) { SQInteger spos = func->_defaultparams[i]; closure->_defaultparams[i] = _stack._vals[_stackbase + spos]; } } target = closure; return true; } bool SQVM::CLASS_OP(SQObjectPtr &target,SQInteger baseclass,SQInteger attributes) { SQClass *base = NULL; SQObjectPtr attrs; if(baseclass != -1) { if(sq_type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; } base = _class(_stack._vals[_stackbase + baseclass]); } if(attributes != MAX_FUNC_STACKSIZE) { attrs = _stack._vals[_stackbase+attributes]; } target = SQClass::Create(_ss(this),base); if(sq_type(_class(target)->_metamethods[MT_INHERITED]) != OT_NULL) { int nparams = 2; SQObjectPtr ret; Push(target); Push(attrs); if(!Call(_class(target)->_metamethods[MT_INHERITED],nparams,_top - nparams, ret, false)) { Pop(nparams); return false; } Pop(nparams); } _class(target)->_attributes = attrs; return true; } bool SQVM::IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res) { SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); if(t1 == t2) { if (t1 == OT_FLOAT) { res = (_float(o1) == _float(o2)); } else { res = (_rawval(o1) == _rawval(o2)); } } else { if(sq_isnumeric(o1) && sq_isnumeric(o2)) { res = (tofloat(o1) == tofloat(o2)); } else { res = false; } } return true; } bool SQVM::IsFalse(SQObjectPtr &o) { if(((sq_type(o) & SQOBJECT_CANBEFALSE) && ( ((sq_type(o) == OT_FLOAT) && (_float(o) == SQFloat(0.0))) )) #if !defined(SQUSEDOUBLE) || (defined(SQUSEDOUBLE) && defined(_SQ64)) || (_integer(o) == 0) ) //OT_NULL|OT_INTEGER|OT_BOOL #else || (((sq_type(o) != OT_FLOAT) && (_integer(o) == 0))) ) //OT_NULL|OT_INTEGER|OT_BOOL #endif { return true; } return false; } extern SQInstructionDesc g_InstrDesc[]; bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et, SQBool can_suspend) { if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; } _nnativecalls++; AutoDec ad(&_nnativecalls); SQInteger traps = 0; CallInfo *prevci = ci; switch(et) { case ET_CALL: { temp_reg = closure; if(!StartCall(_closure(temp_reg), _top - nargs, nargs, stackbase, false)) { //call the handler if there are no calls in the stack, if not relies on the previous node if(ci == NULL) CallErrorHandler(_lasterror); return false; } if(ci == prevci) { outres = STK(_top-nargs); return true; } ci->_root = SQTrue; } break; case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, outres); ci->_root = SQTrue; traps += ci->_etraps; break; case ET_RESUME_VM: case ET_RESUME_THROW_VM: traps = _suspended_traps; ci->_root = _suspended_root; _suspended = SQFalse; if(et == ET_RESUME_THROW_VM) { SQ_THROW(); } break; } exception_restore: // { for(;;) { _ops_total++; _ops_remaining --; if (_ops_remaining < 0) { // suspend vm if (can_suspend && !_throw_if_no_ops) { _suspended = SQTrue; _suspended_root = ci->_root; _suspended_traps = traps; _suspended_target = -1; return true; } else { // throw error (if within unsuspendable function give some grace opcodes) if (_throw_if_no_ops || (_ops_remaining + _ops_grace_amount < 0) ) { Raise_Error(_SC("script took too long: remain = %d grac = %d"), _ops_remaining, _ops_grace_amount); SQ_THROW(); } } } const SQInstruction &_i_ = *ci->_ip++; //dumpstack(_stackbase); //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-_closure(ci->_closure)->_function->_instructions,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3); switch(_i_.op) { case _OP_LINE: if (_debughook) CallDebugHook(_SC('l'),arg1); continue; case _OP_LOAD: TARGET = ci->_literals[arg1]; continue; case _OP_LOADINT: #ifndef _SQ64 TARGET = (SQInteger)arg1; continue; #else TARGET = (SQInteger)((SQInt32)arg1); continue; #endif case _OP_LOADFLOAT: SQFloat v; memcpy(&v, &arg1, sizeof(SQFloat)); TARGET = v; continue; case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue; case _OP_TAILCALL:{ SQObjectPtr &t = STK(arg1); if (sq_type(t) == OT_CLOSURE && (!_closure(t)->_function->_bgenerator)){ SQObjectPtr clo = t; SQInteger last_top = _top; if(_openouters) CloseOuters(&(_stack._vals[_stackbase])); for (SQInteger i = 0; i < arg3; i++) STK(i) = STK(arg2 + i); _GUARD(StartCall(_closure(clo), ci->_target, arg3, _stackbase, true)); if (last_top >= _top) { _top = last_top; } continue; } } case _OP_CALL: { SQObjectPtr clo = STK(arg1); switch (sq_type(clo)) { case OT_CLOSURE: _GUARD(StartCall(_closure(clo), sarg0, arg3, _stackbase+arg2, false)); continue; case OT_NATIVECLOSURE: { bool suspend; bool tailcall; _GUARD(CallNative(_nativeclosure(clo), arg3, _stackbase+arg2, clo, (SQInt32)sarg0, suspend, tailcall)); if(suspend){ _suspended = SQTrue; _suspended_target = sarg0; _suspended_root = ci->_root; _suspended_traps = traps; outres = clo; return true; } if(sarg0 != -1 && !tailcall) { STK(arg0) = clo; } } continue; case OT_CLASS:{ SQObjectPtr inst; _GUARD(CreateClassInstance(_class(clo),inst,clo)); if(sarg0 != -1) { STK(arg0) = inst; } SQInteger stkbase; switch(sq_type(clo)) { case OT_CLOSURE: stkbase = _stackbase+arg2; _stack._vals[stkbase] = inst; _GUARD(StartCall(_closure(clo), -1, arg3, stkbase, false)); break; case OT_NATIVECLOSURE: bool dummy; stkbase = _stackbase+arg2; _stack._vals[stkbase] = inst; _GUARD(CallNative(_nativeclosure(clo), arg3, stkbase, clo, -1, dummy, dummy)); break; default: break; //shutup GCC 4.x } } break; case OT_TABLE: case OT_USERDATA: case OT_INSTANCE:{ SQObjectPtr closure; if(_delegable(clo)->_delegate && _delegable(clo)->GetMetaMethod(this,MT_CALL,closure)) { Push(clo); for (SQInteger i = 0; i < arg3; i++) Push(STK(arg2 + i)); if(!CallMetaMethod(closure, MT_CALL, arg3+1, clo)) SQ_THROW(); if(sarg0 != -1) { STK(arg0) = clo; } break; } //Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo)); //SQ_THROW(); } default: Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo)); SQ_THROW(); } } continue; case _OP_PREPCALL: case _OP_PREPCALLK: { SQObjectPtr &key = _i_.op == _OP_PREPCALLK?(ci->_literals)[arg1]:STK(arg1); SQObjectPtr &o = STK(arg2); if (!Get(o, key, temp_reg,0,arg2)) { SQ_THROW(); } STK(arg3) = o; _Swap(TARGET,temp_reg);//TARGET = temp_reg; } continue; case _OP_GETK: if (!Get(STK(arg2), ci->_literals[arg1], temp_reg, 0,arg2)) { SQ_THROW();} _Swap(TARGET,temp_reg);//TARGET = temp_reg; continue; case _OP_MOVE: TARGET = STK(arg1); continue; case _OP_NEWSLOT: _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3),false)); if(arg0 != 0xFF) TARGET = STK(arg3); continue; case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue; case _OP_SET: if (!Set(STK(arg1), STK(arg2), STK(arg3),arg1)) { SQ_THROW(); } if (arg0 != 0xFF) TARGET = STK(arg3); continue; case _OP_GET: if (!Get(STK(arg1), STK(arg2), temp_reg, 0,arg1)) { SQ_THROW(); } _Swap(TARGET,temp_reg);//TARGET = temp_reg; continue; case _OP_EQ:{ bool res; if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); } TARGET = res?true:false; }continue; case _OP_NE:{ bool res; if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); } TARGET = (!res)?true:false; } continue; case _OP_ADD: _ARITH_(+,TARGET,STK(arg2),STK(arg1)); continue; case _OP_SUB: _ARITH_(-,TARGET,STK(arg2),STK(arg1)); continue; case _OP_MUL: _ARITH_(*,TARGET,STK(arg2),STK(arg1)); continue; case _OP_DIV: _ARITH_NOZERO(/,TARGET,STK(arg2),STK(arg1),_SC("division by zero")); continue; case _OP_MOD: ARITH_OP('%',TARGET,STK(arg2),STK(arg1)); continue; case _OP_BITW: _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue; case _OP_RETURN: if((ci)->_generator) { (ci)->_generator->Kill(); } if(Return(arg0, arg1, temp_reg)){ assert(traps==0); //outres = temp_reg; _Swap(outres,temp_reg); return true; } continue; case _OP_LOADNULLS:{ for(SQInt32 n=0; n < arg1; n++) STK(arg0+n).Null(); }continue; case _OP_LOADROOT: { SQWeakRef *w = _closure(ci->_closure)->_root; if(sq_type(w->_obj) != OT_NULL) { TARGET = w->_obj; } else { TARGET = _roottable; //shoud this be like this? or null } } continue; case _OP_LOADBOOL: TARGET = arg1?true:false; continue; case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue; case _OP_JMP: ci->_ip += (sarg1); continue; //case _OP_JNZ: if(!IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue; case _OP_JCMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg0),temp_reg)); if(IsFalse(temp_reg)) ci->_ip+=(sarg1); continue; case _OP_JZ: if(IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue; case _OP_GETOUTER: { SQClosure *cur_cls = _closure(ci->_closure); SQOuter *otr = _outer(cur_cls->_outervalues[arg1]); TARGET = *(otr->_valptr); } continue; case _OP_SETOUTER: { SQClosure *cur_cls = _closure(ci->_closure); SQOuter *otr = _outer(cur_cls->_outervalues[arg1]); *(otr->_valptr) = STK(arg2); if(arg0 != 0xFF) { TARGET = STK(arg2); } } continue; case _OP_NEWOBJ: switch(arg3) { case NOT_TABLE: TARGET = SQTable::Create(_ss(this), arg1); continue; case NOT_ARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue; case NOT_CLASS: _GUARD(CLASS_OP(TARGET,arg1,arg2)); continue; default: assert(0); continue; } case _OP_APPENDARRAY: { SQObject val; val._unVal.raw = 0; switch(arg2) { case AAT_STACK: val = STK(arg1); break; case AAT_LITERAL: val = ci->_literals[arg1]; break; case AAT_INT: val._type = OT_INTEGER; #ifndef _SQ64 val._unVal.nInteger = (SQInteger)arg1; #else val._unVal.nInteger = (SQInteger)((SQInt32)arg1); #endif break; case AAT_FLOAT: val._type = OT_FLOAT; memcpy(&val._unVal.fFloat, &arg1, sizeof(SQFloat)); break; case AAT_BOOL: val._type = OT_BOOL; val._unVal.nInteger = arg1; break; default: val._type = OT_INTEGER; assert(0); break; } _array(STK(arg0))->Append(val); continue; } case _OP_COMPARITH: { SQInteger selfidx = (((SQUnsignedInteger)arg1&0xFFFF0000)>>16); _GUARD(DerefInc(arg3, TARGET, STK(selfidx), STK(arg2), STK(arg1&0x0000FFFF), false, selfidx)); } continue; case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false, arg1));} continue; case _OP_INCL: { SQObjectPtr &a = STK(arg1); if(sq_type(a) == OT_INTEGER) { a._unVal.nInteger = _integer(a) + sarg3; } else { SQObjectPtr o(sarg3); //_GUARD(LOCAL_INC('+',TARGET, STK(arg1), o)); _ARITH_(+,a,a,o); } } continue; case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true, arg1));} continue; case _OP_PINCL: { SQObjectPtr &a = STK(arg1); if(sq_type(a) == OT_INTEGER) { TARGET = a; a._unVal.nInteger = _integer(a) + sarg3; } else { SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o)); } } continue; case _OP_CMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET)) continue; case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, GET_FLAG_DO_NOT_RAISE_ERROR | GET_FLAG_RAW, DONT_FALL_BACK) ? true : false; continue; case _OP_INSTANCEOF: if(sq_type(STK(arg1)) != OT_CLASS) {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();} TARGET = (sq_type(STK(arg2)) == OT_INSTANCE) ? (_instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?true:false) : false; continue; case _OP_AND: if(IsFalse(STK(arg2))) { TARGET = STK(arg2); ci->_ip += (sarg1); } continue; case _OP_OR: if(!IsFalse(STK(arg2))) { TARGET = STK(arg2); ci->_ip += (sarg1); } continue; case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue; case _OP_NOT: TARGET = IsFalse(STK(arg1)); continue; case _OP_BWNOT: if(sq_type(STK(arg1)) == OT_INTEGER) { SQInteger t = _integer(STK(arg1)); TARGET = SQInteger(~t); continue; } Raise_Error(_SC("attempt to perform a bitwise op on a %s"), GetTypeName(STK(arg1))); SQ_THROW(); case _OP_CLOSURE: { SQClosure *c = ci->_closure._unVal.pClosure; SQFunctionProto *fp = c->_function; if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); } continue; } case _OP_YIELD:{ if(ci->_generator) { if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1); if (_openouters) CloseOuters(&_stack._vals[_stackbase]); _GUARD(ci->_generator->Yield(this,arg2)); traps -= ci->_etraps; if(sarg1 != MAX_FUNC_STACKSIZE) _Swap(STK(arg1),temp_reg);//STK(arg1) = temp_reg; } else { Raise_Error(_SC("trying to yield a '%s',only genenerator can be yielded"), GetTypeName(ci->_generator)); SQ_THROW();} if(Return(arg0, arg1, temp_reg)){ assert(traps == 0); outres = temp_reg; return true; } } continue; case _OP_RESUME: if(sq_type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();} _GUARD(_generator(STK(arg1))->Resume(this, TARGET)); traps += ci->_etraps; continue; case _OP_FOREACH:{ int tojump; _GUARD(FOREACH_OP(STK(arg0),STK(arg2),STK(arg2+1),STK(arg2+2),arg2,sarg1,tojump)); ci->_ip += tojump; } continue; case _OP_POSTFOREACH: assert(sq_type(STK(arg0)) == OT_GENERATOR); if(_generator(STK(arg0))->_state == SQGenerator::eDead) ci->_ip += (sarg1 - 1); continue; case _OP_CLONE: _GUARD(Clone(STK(arg1), TARGET)); continue; case _OP_TYPEOF: _GUARD(TypeOf(STK(arg1), TARGET)) continue; case _OP_PUSHTRAP:{ SQInstruction *_iv = _closure(ci->_closure)->_function->_instructions; _etraps.push_back(SQExceptionTrap(_top,_stackbase, &_iv[(ci->_ip-_iv)+arg1], arg0)); traps++; ci->_etraps++; } continue; case _OP_POPTRAP: { for(SQInteger i = 0; i < arg0; i++) { _etraps.pop_back(); traps--; ci->_etraps--; } } continue; case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue; case _OP_NEWSLOTA: _GUARD(NewSlotA(STK(arg1),STK(arg2),STK(arg3),(arg0&NEW_SLOT_ATTRIBUTES_FLAG) ? STK(arg2-1) : SQObjectPtr(),(arg0&NEW_SLOT_STATIC_FLAG)?true:false,false)); continue; case _OP_GETBASE:{ SQClosure *clo = _closure(ci->_closure); if(clo->_base) { TARGET = clo->_base; } else { TARGET.Null(); } continue; } case _OP_CLOSE: if(_openouters) CloseOuters(&(STK(arg1))); continue; } } } exception_trap: { SQObjectPtr currerror = _lasterror; // dumpstack(_stackbase); // SQInteger n = 0; SQInteger last_top = _top; if(_ss(this)->_notifyallexceptions || (!traps && raiseerror)) CallErrorHandler(currerror); while( ci ) { if(ci->_etraps > 0) { SQExceptionTrap &et = _etraps.top(); ci->_ip = et._ip; _top = et._stacksize; _stackbase = et._stackbase; _stack._vals[_stackbase + et._extarget] = currerror; _etraps.pop_back(); traps--; ci->_etraps--; while(last_top >= _top) _stack._vals[last_top--].Null(); goto exception_restore; } else if (_debughook) { //notify debugger of a "return" //even if it really an exception unwinding the stack for(SQInteger i = 0; i < ci->_ncalls; i++) { CallDebugHook(_SC('r')); } } if(ci->_generator) ci->_generator->Kill(); bool mustbreak = ci && ci->_root; LeaveFrame(); if(mustbreak) break; } _lasterror = currerror; return false; } assert(0); } bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor) { inst = theclass->CreateInstance(); if(!theclass->GetConstructor(constructor)) { constructor.Null(); } return true; } void SQVM::CallErrorHandler(SQObjectPtr &error) { if(sq_type(_errorhandler) != OT_NULL) { SQObjectPtr out; Push(_roottable); Push(error); Call(_errorhandler, 2, _top-2, out,SQFalse); Pop(2); } } void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline) { _debughook = false; SQFunctionProto *func=_closure(ci->_closure)->_function; if(_debughook_native) { const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; const SQChar *fname = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; SQInteger line = forcedline?forcedline:func->GetLine(ci->_ip); _debughook_native(this,type,src,line,fname); } else { SQObjectPtr temp_reg; SQInteger nparams=5; Push(_roottable); Push(type); Push(func->_sourcename); Push(forcedline?forcedline:func->GetLine(ci->_ip)); Push(func->_name); Call(_debughook_closure,nparams,_top-nparams,temp_reg,SQFalse); Pop(nparams); } _debughook = true; } bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target,bool &suspend, bool &tailcall) { SQInteger nparamscheck = nclosure->_nparamscheck; SQInteger newtop = newbase + nargs + nclosure->_noutervalues; if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; } if(nparamscheck && (((nparamscheck > 0) && (nparamscheck != nargs)) || ((nparamscheck < 0) && (nargs < (-nparamscheck))))) { const SQChar *src = sq_type(nclosure->_name) == OT_STRING?_stringval(nclosure->_name):NULL; Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s"), nargs, nparamscheck, src); return false; } SQInteger tcs; SQIntVec &tc = nclosure->_typecheck; if((tcs = tc.size())) { for(SQInteger i = 0; i < nargs && i < tcs; i++) { if((tc._vals[i] != -1) && !(sq_type(_stack._vals[newbase+i]) & tc._vals[i])) { Raise_ParamTypeError(i,tc._vals[i], sq_type(_stack._vals[newbase+i])); return false; } } } if(!EnterFrame(newbase, newtop, false)) return false; ci->_closure = nclosure; ci->_target = target; SQInteger outers = nclosure->_noutervalues; for (SQInteger i = 0; i < outers; i++) { _stack._vals[newbase+nargs+i] = nclosure->_outervalues[i]; } if(nclosure->_env) { _stack._vals[newbase] = nclosure->_env->_obj; } _nnativecalls++; SQInteger ret = (nclosure->_function)(this); _nnativecalls--; suspend = false; tailcall = false; if (ret == SQ_TAILCALL_FLAG) { tailcall = true; return true; } else if (ret == SQ_SUSPEND_FLAG) { suspend = true; } else if (ret < 0) { LeaveFrame(); Raise_Error(_lasterror); return false; } if(ret) { retval = _stack._vals[_top-1]; } else { retval.Null(); } //retval = ret ? _stack._vals[_top-1] : _null_; LeaveFrame(); return true; } bool SQVM::TailCall(SQClosure *closure, SQInteger parambase,SQInteger nparams) { SQInteger last_top = _top; SQObjectPtr clo = closure; if (ci->_root) { Raise_Error("root calls cannot invoke tailcalls"); return false; } for (SQInteger i = 0; i < nparams; i++) STK(i) = STK(parambase + i); bool ret = StartCall(closure, ci->_target, nparams, _stackbase, true); if (last_top >= _top) { _top = last_top; } return ret; } #define FALLBACK_OK 0 #define FALLBACK_NO_MATCH 1 #define FALLBACK_ERROR 2 bool SQVM::Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags, SQInteger selfidx) { switch(sq_type(self)){ case OT_TABLE: if(_table(self)->Get(key,dest))return true; break; case OT_ARRAY: if (sq_isnumeric(key)) { if (_array(self)->Get(tointeger(key), dest)) { return true; } if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return false; } break; case OT_INSTANCE: if(_instance(self)->Get(key,dest)) return true; break; case OT_CLASS: if(_class(self)->Get(key,dest)) return true; break; case OT_STRING: if(sq_isnumeric(key)){ SQInteger n = tointeger(key); SQInteger len = _string(self)->_len; if (n < 0) { n += len; } if (n >= 0 && n < len) { dest = SQInteger(_stringval(self)[n]); return true; } if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return false; } break; default:break; //shut up compiler } if ((getflags & GET_FLAG_RAW) == 0) { switch(FallBackGet(self,key,dest)) { case FALLBACK_OK: return true; //okie case FALLBACK_NO_MATCH: break; //keep falling back case FALLBACK_ERROR: return false; // the metamethod failed } if(InvokeDefaultDelegate(self,key,dest)) { return true; } } //#ifdef ROOT_FALLBACK if(selfidx == 0) { SQWeakRef *w = _closure(ci->_closure)->_root; if(sq_type(w->_obj) != OT_NULL) { if(Get(*((const SQObjectPtr *)&w->_obj),key,dest,0,DONT_FALL_BACK)) return true; } } //#endif if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return false; } bool SQVM::InvokeDefaultDelegate(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) { SQTable *ddel = NULL; switch(sq_type(self)) { case OT_CLASS: ddel = _class_ddel; break; case OT_TABLE: ddel = _table_ddel; break; case OT_ARRAY: ddel = _array_ddel; break; case OT_STRING: ddel = _string_ddel; break; case OT_INSTANCE: ddel = _instance_ddel; break; case OT_INTEGER:case OT_FLOAT:case OT_BOOL: ddel = _number_ddel; break; case OT_GENERATOR: ddel = _generator_ddel; break; case OT_CLOSURE: case OT_NATIVECLOSURE: ddel = _closure_ddel; break; case OT_THREAD: ddel = _thread_ddel; break; case OT_WEAKREF: ddel = _weakref_ddel; break; default: return false; } return ddel->Get(key,dest); } SQInteger SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) { switch(sq_type(self)){ case OT_TABLE: case OT_USERDATA: //delegation if(_delegable(self)->_delegate) { if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,0,DONT_FALL_BACK)) return FALLBACK_OK; } else { return FALLBACK_NO_MATCH; } //go through case OT_INSTANCE: { SQObjectPtr closure; if(_delegable(self)->GetMetaMethod(this, MT_GET, closure)) { Push(self);Push(key); _nmetamethodscall++; AutoDec ad(&_nmetamethodscall); if(Call(closure, 2, _top - 2, dest, SQFalse)) { Pop(2); return FALLBACK_OK; } else { Pop(2); if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) return FALLBACK_ERROR; } } } } break; default: break;//shutup GCC 4.x } // no metamethod or no fallback type return FALLBACK_NO_MATCH; } bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,SQInteger selfidx) { switch(sq_type(self)){ case OT_TABLE: if(_table(self)->Set(key,val)) return true; break; case OT_INSTANCE: if(_instance(self)->Set(key,val)) return true; break; case OT_ARRAY: if(!sq_isnumeric(key)) { Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; } if(!_array(self)->Set(tointeger(key),val)) { Raise_IdxError(key); return false; } return true; case OT_USERDATA: break; // must fall back default: Raise_Error(_SC("trying to set '%s'"),GetTypeName(self)); return false; } switch(FallBackSet(self,key,val)) { case FALLBACK_OK: return true; //okie case FALLBACK_NO_MATCH: break; //keep falling back case FALLBACK_ERROR: return false; // the metamethod failed } if(selfidx == 0) { if(_table(_roottable)->Set(key,val)) return true; } Raise_IdxError(key); return false; } SQInteger SQVM::FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val) { switch(sq_type(self)) { case OT_TABLE: if(_table(self)->_delegate) { if(Set(_table(self)->_delegate,key,val,DONT_FALL_BACK)) return FALLBACK_OK; } //keps on going case OT_INSTANCE: case OT_USERDATA:{ SQObjectPtr closure; SQObjectPtr t; if(_delegable(self)->GetMetaMethod(this, MT_SET, closure)) { Push(self);Push(key);Push(val); _nmetamethodscall++; AutoDec ad(&_nmetamethodscall); if(Call(closure, 3, _top - 3, t, SQFalse)) { Pop(3); return FALLBACK_OK; } else { Pop(3); if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) return FALLBACK_ERROR; } } } } break; default: break;//shutup GCC 4.x } // no metamethod or no fallback type return FALLBACK_NO_MATCH; } bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target) { SQObjectPtr temp_reg; SQObjectPtr newobj; switch(sq_type(self)){ case OT_TABLE: newobj = _table(self)->Clone(); goto cloned_mt; case OT_INSTANCE: { newobj = _instance(self)->Clone(_ss(this)); cloned_mt: SQObjectPtr closure; if(_delegable(newobj)->_delegate && _delegable(newobj)->GetMetaMethod(this,MT_CLONED,closure)) { Push(newobj); Push(self); if(!CallMetaMethod(closure,MT_CLONED,2,temp_reg)) return false; } } target = newobj; return true; case OT_ARRAY: target = _array(self)->Clone(); return true; default: Raise_Error(_SC("cloning a %s"), GetTypeName(self)); return false; } } bool SQVM::NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,const SQObjectPtr &attrs,bool bstatic,bool raw) { if(sq_type(self) != OT_CLASS) { Raise_Error(_SC("object must be a class")); return false; } SQClass *c = _class(self); if(!raw) { SQObjectPtr &mm = c->_metamethods[MT_NEWMEMBER]; if(sq_type(mm) != OT_NULL ) { Push(self); Push(key); Push(val); Push(attrs); Push(bstatic); return CallMetaMethod(mm,MT_NEWMEMBER,5,temp_reg); } } if(!NewSlot(self, key, val,bstatic)) return false; if(sq_type(attrs) != OT_NULL) { c->SetAttributes(key,attrs); } return true; } bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) { if(sq_type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; } switch(sq_type(self)) { case OT_TABLE: { bool rawcall = true; if(_table(self)->_delegate) { SQObjectPtr res; if(!_table(self)->Get(key,res)) { SQObjectPtr closure; if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_NEWSLOT,closure)) { Push(self);Push(key);Push(val); if(!CallMetaMethod(closure,MT_NEWSLOT,3,res)) { return false; } rawcall = false; } else { rawcall = true; } } } if(rawcall) _table(self)->NewSlot(key,val); //cannot fail break;} case OT_INSTANCE: { SQObjectPtr res; SQObjectPtr closure; if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_NEWSLOT,closure)) { Push(self);Push(key);Push(val); if(!CallMetaMethod(closure,MT_NEWSLOT,3,res)) { return false; } break; } Raise_Error(_SC("class instances do not support the new slot operator")); return false; break;} case OT_CLASS: if(!_class(self)->NewSlot(_ss(this),key,val,bstatic)) { if(_class(self)->_locked) { Raise_Error(_SC("trying to modify a class that has already been instantiated")); return false; } else { SQObjectPtr oval = PrintObjVal(key); Raise_Error(_SC("the property '%s' already exists"),_stringval(oval)); return false; } } break; default: Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; break; } return true; } bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res) { switch(sq_type(self)) { case OT_TABLE: case OT_INSTANCE: case OT_USERDATA: { SQObjectPtr t; //bool handled = false; SQObjectPtr closure; if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_DELSLOT,closure)) { Push(self);Push(key); return CallMetaMethod(closure,MT_DELSLOT,2,res); } else { if(sq_type(self) == OT_TABLE) { if(_table(self)->Get(key,t)) { _table(self)->Remove(key); } else { Raise_IdxError((const SQObject &)key); return false; } } else { Raise_Error(_SC("cannot delete a slot from %s"),GetTypeName(self)); return false; } } res = t; } break; default: Raise_Error(_SC("attempt to delete a slot from a %s"),GetTypeName(self)); return false; } return true; } bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror, SQBool can_suspend) { #ifdef _DEBUG SQInteger prevstackbase = _stackbase; #endif switch(sq_type(closure)) { case OT_CLOSURE: return Execute(closure, nparams, stackbase, outres, raiseerror, ET_CALL, can_suspend); break; case OT_NATIVECLOSURE:{ bool dummy; return CallNative(_nativeclosure(closure), nparams, stackbase, outres, -1, dummy, dummy); } break; case OT_CLASS: { SQObjectPtr constr; SQObjectPtr temp; CreateClassInstance(_class(closure),outres,constr); SQObjectType ctype = sq_type(constr); if (ctype == OT_NATIVECLOSURE || ctype == OT_CLOSURE) { _stack[stackbase] = outres; return Call(constr,nparams,stackbase,temp,raiseerror); } return true; } break; default: Raise_Error(_SC("attempt to call '%s'"), GetTypeName(closure)); return false; } #ifdef _DEBUG if(!_suspended) { assert(_stackbase == prevstackbase); } #endif return true; } bool SQVM::CallMetaMethod(SQObjectPtr &closure,SQMetaMethod SQ_UNUSED_ARG(mm),SQInteger nparams,SQObjectPtr &outres) { //SQObjectPtr closure; _nmetamethodscall++; if(Call(closure, nparams, _top - nparams, outres, SQFalse)) { _nmetamethodscall--; Pop(nparams); return true; } _nmetamethodscall--; //} Pop(nparams); return false; } void SQVM::FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex) { SQOuter **pp = &_openouters; SQOuter *p; SQOuter *otr; while ((p = *pp) != NULL && p->_valptr >= stackindex) { if (p->_valptr == stackindex) { target = SQObjectPtr(p); return; } pp = &p->_next; } otr = SQOuter::Create(_ss(this), stackindex); otr->_next = *pp; otr->_idx = (stackindex - _stack._vals); __ObjAddRef(otr); *pp = otr; target = SQObjectPtr(otr); } bool SQVM::EnterFrame(SQInteger newbase, SQInteger newtop, bool tailcall) { if( !tailcall ) { if( _callsstacksize == _alloccallsstacksize ) { GrowCallStack(); } ci = &_callsstack[_callsstacksize++]; ci->_prevstkbase = (SQInt32)(newbase - _stackbase); ci->_prevtop = (SQInt32)(_top - _stackbase); ci->_etraps = 0; ci->_ncalls = 1; ci->_generator = NULL; ci->_root = SQFalse; } else { ci->_ncalls++; } _stackbase = newbase; _top = newtop; if(newtop + MIN_STACK_OVERHEAD > (SQInteger)_stack.size()) { if(_nmetamethodscall) { Raise_Error(_SC("stack overflow, cannot resize stack while in a metamethod")); return false; } _stack.resize(newtop + (MIN_STACK_OVERHEAD << 2)); RelocateOuters(); } return true; } void SQVM::LeaveFrame() { SQInteger last_top = _top; SQInteger last_stackbase = _stackbase; SQInteger css = --_callsstacksize; /* First clean out the call stack frame */ ci->_closure.Null(); _stackbase -= ci->_prevstkbase; _top = _stackbase + ci->_prevtop; ci = (css) ? &_callsstack[css-1] : NULL; if(_openouters) CloseOuters(&(_stack._vals[last_stackbase])); while (last_top >= _top) { _stack._vals[last_top--].Null(); } } void SQVM::RelocateOuters() { SQOuter *p = _openouters; while (p) { p->_valptr = _stack._vals + p->_idx; p = p->_next; } } void SQVM::CloseOuters(SQObjectPtr *stackindex) { SQOuter *p; while ((p = _openouters) != NULL && p->_valptr >= stackindex) { p->_value = *(p->_valptr); p->_valptr = &p->_value; _openouters = p->_next; __ObjRelease(p); } } void SQVM::Remove(SQInteger n) { n = (n >= 0)?n + _stackbase - 1:_top + n; for(SQInteger i = n; i < _top; i++){ _stack[i] = _stack[i+1]; } _stack[_top].Null(); _top--; } void SQVM::Pop() { _stack[--_top].Null(); } void SQVM::Pop(SQInteger n) { for(SQInteger i = 0; i < n; i++){ _stack[--_top].Null(); } } void SQVM::PushNull() { _stack[_top++].Null(); } void SQVM::Push(const SQObjectPtr &o) { _stack[_top++] = o; } SQObjectPtr &SQVM::Top() { return _stack[_top-1]; } SQObjectPtr &SQVM::PopGet() { return _stack[--_top]; } SQObjectPtr &SQVM::GetUp(SQInteger n) { return _stack[_top+n]; } SQObjectPtr &SQVM::GetAt(SQInteger n) { return _stack[n]; } #ifdef _DEBUG_DUMP void SQVM::dumpstack(SQInteger stackbase,bool dumpall) { SQInteger size=dumpall?_stack.size():_top; SQInteger n=0; scprintf(_SC("\n>>>>stack dump<<<<\n")); CallInfo &ci=_callsstack[_callsstacksize-1]; scprintf(_SC("IP: %p\n"),ci._ip); scprintf(_SC("prev stack base: %d\n"),ci._prevstkbase); scprintf(_SC("prev top: %d\n"),ci._prevtop); for(SQInteger i=0;i"));else scprintf(_SC(" ")); scprintf(_SC("[" _PRINT_INT_FMT "]:"),n); switch(sq_type(obj)){ case OT_FLOAT: scprintf(_SC("FLOAT %.3f"),_float(obj));break; case OT_INTEGER: scprintf(_SC("INTEGER " _PRINT_INT_FMT),_integer(obj));break; case OT_BOOL: scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break; case OT_STRING: scprintf(_SC("STRING %s"),_stringval(obj));break; case OT_NULL: scprintf(_SC("NULL")); break; case OT_TABLE: scprintf(_SC("TABLE %p[%p]"),_table(obj),_table(obj)->_delegate);break; case OT_ARRAY: scprintf(_SC("ARRAY %p"),_array(obj));break; case OT_CLOSURE: scprintf(_SC("CLOSURE [%p]"),_closure(obj));break; case OT_NATIVECLOSURE: scprintf(_SC("NATIVECLOSURE"));break; case OT_USERDATA: scprintf(_SC("USERDATA %p[%p]"),_userdataval(obj),_userdata(obj)->_delegate);break; case OT_GENERATOR: scprintf(_SC("GENERATOR %p"),_generator(obj));break; case OT_THREAD: scprintf(_SC("THREAD [%p]"),_thread(obj));break; case OT_USERPOINTER: scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break; case OT_CLASS: scprintf(_SC("CLASS %p"),_class(obj));break; case OT_INSTANCE: scprintf(_SC("INSTANCE %p"),_instance(obj));break; case OT_WEAKREF: scprintf(_SC("WEAKREF %p"),_weakref(obj));break; default: assert(0); break; }; scprintf(_SC("\n")); ++n; } } #endif simutrans-124.5/src/squirrel/squirrel/sqvm.h000066400000000000000000000204471520675460500212350ustar00rootroot00000000000000/* see copyright notice in squirrel.h */ #ifndef _SQVM_H_ #define _SQVM_H_ #include "sqopcodes.h" #include "sqobject.h" #define MAX_NATIVE_CALLS 100 #define MIN_STACK_OVERHEAD 15 #define SQ_SUSPEND_FLAG -666 #define SQ_TAILCALL_FLAG -777 #define DONT_FALL_BACK 666 //#define EXISTS_FALL_BACK -1 #define GET_FLAG_RAW 0x00000001 #define GET_FLAG_DO_NOT_RAISE_ERROR 0x00000002 //base lib void sq_base_register(HSQUIRRELVM v); struct SQExceptionTrap{ SQExceptionTrap() {} SQExceptionTrap(SQInteger ss, SQInteger stackbase,SQInstruction *ip, SQInteger ex_target){ _stacksize = ss; _stackbase = stackbase; _ip = ip; _extarget = ex_target;} SQInteger _stackbase; SQInteger _stacksize; SQInstruction *_ip; SQInteger _extarget; }; #define _INLINE typedef sqvector ExceptionsTraps; struct SQVM : public CHAINABLE_OBJ { struct CallInfo{ //CallInfo() { _generator = NULL;} SQInstruction *_ip; SQObjectPtr *_literals; SQObjectPtr _closure; SQGenerator *_generator; SQInt32 _etraps; SQInt32 _prevstkbase; SQInt32 _prevtop; SQInt32 _target; SQInt32 _ncalls; SQBool _root; }; typedef sqvector CallInfoVec; public: void DebugHookProxy(SQInteger type, const SQChar * sourcename, SQInteger line, const SQChar * funcname); static void _DebugHookProxy(HSQUIRRELVM v, SQInteger type, const SQChar * sourcename, SQInteger line, const SQChar * funcname); enum ExecutionType { ET_CALL, ET_RESUME_GENERATOR, ET_RESUME_VM,ET_RESUME_THROW_VM }; SQVM(SQSharedState *ss); ~SQVM(); bool Init(SQVM *friendvm, SQInteger stacksize); bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL, SQBool can_suspend = false); //starts a native call return when the NATIVE closure returns bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target, bool &suspend,bool &tailcall); bool TailCall(SQClosure *closure, SQInteger firstparam, SQInteger nparams); //starts a SQUIRREL call in the same "Execution loop" bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall); bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor); //call a generic closure pure SQUIRREL or NATIVE // @param can_suspend is by default FALSE, only set it to true if you handle the case that vm is suspended after call. bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror, SQBool can_suspend = false); SQRESULT Suspend(); void CallDebugHook(SQInteger type,SQInteger forcedline=0); void CallErrorHandler(SQObjectPtr &e); bool Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags, SQInteger selfidx); SQInteger FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest); bool InvokeDefaultDelegate(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest); bool Set(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val, SQInteger selfidx); SQInteger FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val); bool NewSlot(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val,bool bstatic); bool NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,const SQObjectPtr &attrs,bool bstatic,bool raw); bool DeleteSlot(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &res); bool Clone(const SQObjectPtr &self, SQObjectPtr &target); bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res); bool StringCat(const SQObjectPtr &str, const SQObjectPtr &obj, SQObjectPtr &dest); static bool IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res); bool ToString(const SQObjectPtr &o,SQObjectPtr &res); SQString *PrintObjVal(const SQObjectPtr &o); void Raise_Error(const SQChar *s, ...); void Raise_Error_vl(const SQChar *s, va_list vl); void Raise_Error(const SQObjectPtr &desc); void Raise_IdxError(const SQObjectPtr &o); void Raise_CompareError(const SQObject &o1, const SQObject &o2); void Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type); void FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex); void RelocateOuters(); void CloseOuters(SQObjectPtr *stackindex); bool TypeOf(const SQObjectPtr &obj1, SQObjectPtr &dest); bool CallMetaMethod(SQObjectPtr &closure, SQMetaMethod mm, SQInteger nparams, SQObjectPtr &outres); bool ArithMetaMethod(SQInteger op, const SQObjectPtr &o1, const SQObjectPtr &o2, SQObjectPtr &dest); bool Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval); //new stuff _INLINE bool ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2); _INLINE bool BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2); _INLINE bool NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o1); _INLINE bool CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res); bool CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func); bool CLASS_OP(SQObjectPtr &target,SQInteger base,SQInteger attrs); //return true if the loop is finished bool FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,SQInteger arg_2,int exitpos,int &jump); //_INLINE bool LOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr); _INLINE bool PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr); _INLINE bool DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix,SQInteger arg0); #ifdef _DEBUG_DUMP void dumpstack(SQInteger stackbase=-1, bool dumpall = false); #endif #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); SQObjectType GetType() {return OT_THREAD;} #endif void Finalize(); void GrowCallStack() { SQInteger newsize = _alloccallsstacksize*2; _callstackdata.resize(newsize); _callsstack = &_callstackdata[0]; _alloccallsstacksize = newsize; } bool EnterFrame(SQInteger newbase, SQInteger newtop, bool tailcall); void LeaveFrame(); void Release(){ sq_delete(this,SQVM); } //////////////////////////////////////////////////////////////////////////// //stack functions for the api void Remove(SQInteger n); static bool IsFalse(SQObjectPtr &o); void Pop(); void Pop(SQInteger n); void Push(const SQObjectPtr &o); void PushNull(); SQObjectPtr &Top(); SQObjectPtr &PopGet(); SQObjectPtr &GetUp(SQInteger n); SQObjectPtr &GetAt(SQInteger n); SQObjectPtrVec _stack; SQInteger _top; SQInteger _stackbase; SQOuter *_openouters; SQObjectPtr _roottable; SQObjectPtr _lasterror; SQObjectPtr _errorhandler; bool _debughook; SQDEBUGHOOK _debughook_native; SQObjectPtr _debughook_closure; SQObjectPtr temp_reg; CallInfo* _callsstack; SQInteger _callsstacksize; SQInteger _alloccallsstacksize; sqvector _callstackdata; ExceptionsTraps _etraps; CallInfo *ci; SQUserPointer _foreignptr; //VMs sharing the same state SQSharedState *_sharedstate; SQInteger _nnativecalls; SQInteger _nmetamethodscall; SQRELEASEHOOK _releasehook; //suspend infos SQBool _suspended; SQBool _suspended_root; SQInteger _suspended_target; SQInteger _suspended_traps; SQInteger _ops_remaining; /// number of ops the vm can do till break SQInteger _ops_grace_amount; /// raise error if _ops_remaining is less than -_ops_grace_amount for pure native calls SQInteger _ops_total; /// total number of ops performed bool _throw_if_no_ops; /// is no-ops an error or can call suspended? default: true }; struct AutoDec{ AutoDec(SQInteger *n) { _n = n; } ~AutoDec() { (*_n)--; } SQInteger *_n; }; inline SQObjectPtr &stack_get(HSQUIRRELVM v,SQInteger idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));} #define _ss(_vm_) (_vm_)->_sharedstate #ifndef NO_GARBAGE_COLLECTOR #define _opt_ss(_vm_) (_vm_)->_sharedstate #else #define _opt_ss(_vm_) NULL #endif #define PUSH_CALLINFO(v,nci){ \ SQInteger css = v->_callsstacksize; \ if(css == v->_alloccallsstacksize) { \ v->GrowCallStack(); \ } \ v->ci = &v->_callsstack[css]; \ *(v->ci) = nci; \ v->_callsstacksize++; \ } #define POP_CALLINFO(v){ \ SQInteger css = --v->_callsstacksize; \ v->ci->_closure.Null(); \ v->ci = css?&v->_callsstack[css-1]:NULL; \ } #endif simutrans-124.5/src/squirrel/squirrel_modifications.diff000066400000000000000000000514701520675460500236400ustar00rootroot00000000000000diff --git b/simutrans/trunk/squirrel/sqstdlib/sqstdaux.cc a/simutrans/trunk/squirrel/sqstdlib/sqstdaux.cc index e569acfcf..0d196d9d7 100644 --- b/simutrans/trunk/squirrel/sqstdlib/sqstdaux.cc +++ a/simutrans/trunk/squirrel/sqstdlib/sqstdaux.cc @@ -23,13 +23,14 @@ void sqstd_printcallstack(HSQUIRRELVM v) const SQChar *src=_SC("unknown"); if(si.funcname)fn=si.funcname; if(si.source)src=si.source; - pf(v,_SC("*FUNCTION [%s()] %s line [%d]\n"),fn,src,si.line); - level++; + pf(v,_SC("* FUNCTION [%s()]
* %s"), fn,src); + if (si.line > 0) { + pf(v,_SC("* line [%d]"),si.line); } - level=0; - pf(v,_SC("\nLOCALS\n")); + pf(v,_SC("
\n")); + + pf(v,_SC("- - LOCALS\n")); - for(level=0;level<10;level++){ seq=0; while((name = sq_getlocal(v,level,seq))) { @@ -37,63 +38,83 @@ void sqstd_printcallstack(HSQUIRRELVM v) switch(sq_gettype(v,-1)) { case OT_NULL: - pf(v,_SC("[%s] NULL\n"),name); + pf(v,_SC("- - - [%s] NULL\n"),name); break; case OT_INTEGER: sq_getinteger(v,-1,&i); - pf(v,_SC("[%s] %d\n"),name,i); + pf(v,_SC("- - - [%s] %d\n"),name,i); break; case OT_FLOAT: sq_getfloat(v,-1,&f); - pf(v,_SC("[%s] %.14g\n"),name,f); + pf(v,_SC("- - - [%s] %.14g\n"),name,f); break; case OT_USERPOINTER: - pf(v,_SC("[%s] USERPOINTER\n"),name); + pf(v,_SC("- - - [%s] USERPOINTER\n"),name); break; case OT_STRING: sq_getstring(v,-1,&s); - pf(v,_SC("[%s] \"%s\"\n"),name,s); + pf(v,_SC("- - - [%s] \"%s\"\n"),name,s); break; case OT_TABLE: - pf(v,_SC("[%s] TABLE\n"),name); + pf(v,_SC("- - - [%s] TABLE (%d entries)\n"),name, sq_getsize(v, -1)); break; case OT_ARRAY: - pf(v,_SC("[%s] ARRAY\n"),name); + pf(v,_SC("- - - [%s] ARRAY (%d entries)\n"),name, sq_getsize(v, -1)); break; case OT_CLOSURE: - pf(v,_SC("[%s] CLOSURE\n"),name); + pf(v,_SC("- - - [%s] CLOSURE\n"),name); break; case OT_NATIVECLOSURE: - pf(v,_SC("[%s] NATIVECLOSURE\n"),name); + pf(v,_SC("- - - [%s] NATIVECLOSURE\n"),name); break; case OT_GENERATOR: - pf(v,_SC("[%s] GENERATOR\n"),name); + pf(v,_SC("- - - [%s] GENERATOR\n"),name); break; case OT_USERDATA: - pf(v,_SC("[%s] USERDATA\n"),name); + pf(v,_SC("- - - [%s] USERDATA\n"),name); break; case OT_THREAD: - pf(v,_SC("[%s] THREAD\n"),name); + pf(v,_SC("- - - [%s] THREAD\n"),name); break; case OT_CLASS: - pf(v,_SC("[%s] CLASS\n"),name); + pf(v,_SC("- - - [%s] CLASS\n"),name); break; case OT_INSTANCE: - pf(v,_SC("[%s] INSTANCE\n"),name); + { + // try to obtain class name + sq_getclass(v, -1); + sq_pushnull(v); + if (SQ_SUCCEEDED(sq_getattributes(v, -2))) { + // stack: instance, class, attributes + sq_pushstring(v, "classname", -1); + if (SQ_SUCCEEDED(sq_get(v, -2))) { + const char* cn; + if (SQ_SUCCEEDED(sq_getstring(v, -1, &cn))) { + pf(v,_SC("- - - [%s] INSTANCE(%s)\n"),name, cn); + sq_pop(v, 3); break; + } + } + sq_poptop(v); + } + sq_poptop(v); + pf(v,_SC("- - - [%s] INSTANCE\n"),name); + break; + } case OT_WEAKREF: - pf(v,_SC("[%s] WEAKREF\n"),name); + pf(v,_SC("- - - [%s] WEAKREF\n"),name); break; case OT_BOOL:{ SQBool bval; sq_getbool(v,-1,&bval); - pf(v,_SC("[%s] %s\n"),name,bval == SQTrue ? _SC("true"):_SC("false")); + pf(v,_SC("- - - [%s] %s\n"),name,bval == SQTrue ? _SC("true"):_SC("false")); } break; default: assert(0); break; } sq_pop(v,1); } + level++; } } } @@ -102,16 +123,18 @@ static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { + pf(v,_SC("")); const SQChar *sErr = 0; if(sq_gettop(v)>=1) { if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) { - pf(v,_SC("\nAN ERROR HAS OCCURRED [%s]\n"),sErr); + pf(v,_SC("\nError: [%s]\n"),sErr); } else{ - pf(v,_SC("\nAN ERROR HAS OCCURRED [unknown]\n")); + pf(v,_SC("\nError: [unknown]\n")); } sqstd_printcallstack(v); } + pf(v,_SC("")); } return 0; } @@ -120,7 +143,9 @@ void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSourc { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { + pf(v,_SC("")); pf(v,_SC("%s line = (%d) column = (%d) : error %s\n"),sSource,line,column,sErr); + pf(v,_SC("")); } } diff --git b/simutrans/trunk/squirrel/sqstdlib/sqstdblob.cc a/simutrans/trunk/squirrel/sqstdlib/sqstdblob.cc index 13d68f0c0..dac94cc62 100644 --- b/simutrans/trunk/squirrel/sqstdlib/sqstdblob.cc +++ a/simutrans/trunk/squirrel/sqstdlib/sqstdblob.cc @@ -185,22 +185,6 @@ static const SQRegFunction _blob_methods[] = { //GLOBAL FUNCTIONS -static SQInteger _g_blob_casti2f(HSQUIRRELVM v) -{ - SQInteger i; - sq_getinteger(v,2,&i); - sq_pushfloat(v,*((const SQFloat *)&i)); - return 1; -} - -static SQInteger _g_blob_castf2i(HSQUIRRELVM v) -{ - SQFloat f; - sq_getfloat(v,2,&f); - sq_pushinteger(v,*((const SQInteger *)&f)); - return 1; -} - static SQInteger _g_blob_swap2(HSQUIRRELVM v) { SQInteger i; @@ -231,8 +215,6 @@ static SQInteger _g_blob_swapfloat(HSQUIRRELVM v) #define _DECL_GLOBALBLOB_FUNC(name,nparams,typecheck) {_SC(#name),_g_blob_##name,nparams,typecheck} static const SQRegFunction bloblib_funcs[]={ - _DECL_GLOBALBLOB_FUNC(casti2f,2,_SC(".n")), - _DECL_GLOBALBLOB_FUNC(castf2i,2,_SC(".n")), _DECL_GLOBALBLOB_FUNC(swap2,2,_SC(".n")), _DECL_GLOBALBLOB_FUNC(swap4,2,_SC(".n")), _DECL_GLOBALBLOB_FUNC(swapfloat,2,_SC(".n")), diff --git b/simutrans/trunk/squirrel/squirrel.h a/simutrans/trunk/squirrel/squirrel.h index 65df9c5bb..2fb201356 100644 --- b/simutrans/trunk/squirrel/squirrel.h +++ a/simutrans/trunk/squirrel/squirrel.h @@ -60,7 +60,7 @@ struct SQDelegable; struct SQOuter; #ifdef _UNICODE -#define SQUNICODE +// #define SQUNICODE #endif #include "sqconfig.h" @@ -398,10 +399,10 @@ SQUIRREL_API void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook); #define SQ_FAILED(res) (res<0) #define SQ_SUCCEEDED(res) (res>=0) -#ifdef __GNUC__ -# define SQ_UNUSED_ARG(x) x __attribute__((__unused__)) +#ifdef XX__GNUC__ +# define SQ_UNUSED_ARG(x) __attribute__((unused)) x #else -# define SQ_UNUSED_ARG(x) x +# define SQ_UNUSED_ARG(x) #endif #ifdef __cplusplus diff --git b/simutrans/trunk/squirrel/squirrel/sqapi.cc a/simutrans/trunk/squirrel/squirrel/sqapi.cc index 4ab6f00ba..a19cd0e39 100644 --- b/simutrans/trunk/squirrel/squirrel/sqapi.cc +++ a/simutrans/trunk/squirrel/squirrel/sqapi.cc @@ -1078,8 +1078,10 @@ SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx) v->Pop(); return sq_throwerror(v,_SC("rawget works only on array/table/instance and class")); } + // modified: call Raise_IdxError + v->Raise_IdxError(v->GetUp(-1)); v->Pop(); - return sq_throwerror(v,_SC("the index doesn't exist")); + return SQ_ERROR; } SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po) @@ -1172,20 +1174,33 @@ SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror) return sq_throwerror(v,_SC("only generators can be resumed")); } +/* modified for our purposes to handle suspended scripts */ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror) { SQObjectPtr res; - if(!v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false)){ - v->Pop(params); //pop args - return SQ_ERROR; + + if(v->_suspended) { + v->Pop(params);//pop closure and args + return sq_throwerror(v,_SC("script took to long")); + } + // we can suspend call only when vm is not already running (thus when we are not called from squirrel) + if(v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false, sq_getvmstate(v)==SQ_VMSTATE_IDLE)){ + + if(!v->_suspended) { + v->Pop(params);//pop closure and args + } + if(retval){ + v->Push(res); return SQ_OK; } - if(!v->_suspended) - v->Pop(params); //pop args - if(retval) - v->Push(res); // push result return SQ_OK; } + else { + v->Pop(params); + return SQ_ERROR; // sq_throwerror(v,_SC("call failed")); + } +} +/* TODO check whether this works with our suspend logic; wont be missed SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) { SQObjectPtr &res = v->GetUp(-(nparams + 1)); @@ -1204,6 +1219,7 @@ SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) } return SQ_TAILCALL_FLAG; } +*/ SQRESULT sq_suspendvm(HSQUIRRELVM v) { @@ -1223,7 +1239,7 @@ SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool raiseer v->Pop(); } else if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target).Null(); } SQObjectPtr dummy; - if(!v->Execute(dummy,-1,-1,ret,raiseerror,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM)) { + if(!v->Execute(dummy,-1,-1,ret,raiseerror,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM, true /*can_suspend*/)) { return SQ_ERROR; } if(retval) diff --git b/simutrans/trunk/squirrel/squirrel/sqbaselib.cc a/simutrans/trunk/squirrel/squirrel/sqbaselib.cc index aa9f07d5e..130f2a191 100644 --- b/simutrans/trunk/squirrel/squirrel/sqbaselib.cc +++ a/simutrans/trunk/squirrel/squirrel/sqbaselib.cc @@ -955,9 +955,14 @@ static SQInteger string_find(HSQUIRRELVM v) return 1; \ } +static char toalnum(char c) +{ + return isalnum(c) && c >=0 ? c : '_'; +} STRING_TOFUNCZ(tolower) STRING_TOFUNCZ(toupper) +STRING_TOFUNCZ(toalnum) const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ {_SC("len"),default_delegate_len,1, _SC("s")}, @@ -968,6 +973,7 @@ const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ {_SC("find"),string_find,-2, _SC("s s n")}, {_SC("tolower"),string_tolower,-1, _SC("s n n")}, {_SC("toupper"),string_toupper,-1, _SC("s n n")}, + {_SC("toalnum"),string_toalnum,1, _SC("s")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {NULL,(SQFUNCTION)0,0,NULL} }; @@ -990,11 +996,13 @@ static SQInteger closure_pcall(HSQUIRRELVM v) static SQInteger closure_call(HSQUIRRELVM v) { + /* has to wait until sq_tailcall is enabled SQObjectPtr &c = stack_get(v, -1); if (sq_type(c) == OT_CLOSURE && (_closure(c)->_function->_bgenerator == false)) { return sq_tailcall(v, sq_gettop(v) - 1); } + */ return SQ_SUCCEEDED(sq_call(v, sq_gettop(v) - 1, SQTrue, SQTrue)) ? 1 : SQ_ERROR; } diff --git b/simutrans/trunk/squirrel/squirrel/sqcompiler.cc a/simutrans/trunk/squirrel/squirrel/sqcompiler.cc index 3f23b9054..774d9cfbe 100644 --- b/simutrans/trunk/squirrel/squirrel/sqcompiler.cc +++ a/simutrans/trunk/squirrel/squirrel/sqcompiler.cc @@ -940,6 +940,19 @@ public: SQInteger stackbase = _fs->PopTarget(); SQInteger closure = _fs->PopTarget(); _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs); +#if 0 + /* + * disabled as it breaks existing scripts like + * + * local a = [] + * local b = a.len() + * // open some new block + * { + * local c = 0 + * } + * + * see https://github.com/albertodemichelis/squirrel/issues/177 + */ if (_token == '{') { SQInteger retval = _fs->TopTarget(); @@ -964,6 +977,7 @@ public: } Lex(); } +#endif } void ParseTableOrClass(SQInteger separator,SQInteger terminator) { diff --git b/simutrans/trunk/squirrel/squirrel/sqdebug.cc a/simutrans/trunk/squirrel/squirrel/sqdebug.cc index 06fcf4ddb..dcf35c090 100644 --- b/simutrans/trunk/squirrel/squirrel/sqdebug.cc +++ a/simutrans/trunk/squirrel/squirrel/sqdebug.cc @@ -60,9 +60,14 @@ void SQVM::Raise_Error(const SQChar *s, ...) { va_list vl; va_start(vl, s); + Raise_Error_vl(s, vl); + va_end(vl); +} + +void SQVM::Raise_Error_vl(const SQChar *s, va_list vl) +{ SQInteger buffersize = (SQInteger)scstrlen(s)+(NUMBER_MAX_CHAR*2); scvsprintf(_sp(sq_rsl(buffersize)),buffersize, s, vl); - va_end(vl); _lasterror = SQString::Create(_ss(this),_spval,-1); } diff --git b/simutrans/trunk/squirrel/squirrel/sqopcodes.h a/simutrans/trunk/squirrel/squirrel/sqopcodes.h index a74893ede..4cffe1393 100644 --- b/simutrans/trunk/squirrel/squirrel/sqopcodes.h +++ a/simutrans/trunk/squirrel/squirrel/sqopcodes.h @@ -99,7 +99,7 @@ enum SQOpcode _OP_THROW= 0x39, _OP_NEWSLOTA= 0x3A, _OP_GETBASE= 0x3B, - _OP_CLOSE= 0x3C + _OP_CLOSE= 0x3C, }; struct SQInstructionDesc { @@ -110,7 +110,8 @@ struct SQInstruction { SQInstruction(){}; SQInstruction(SQOpcode _op,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0) - { op = (unsigned char)_op; + { + op = (unsigned char)_op; _arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1; _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3; } diff --git b/simutrans/trunk/squirrel/squirrel/sqvm.cc a/simutrans/trunk/squirrel/squirrel/sqvm.cc index 61a65d877..82085aad2 100644 --- b/simutrans/trunk/squirrel/squirrel/sqvm.cc +++ a/simutrans/trunk/squirrel/squirrel/sqvm.cc @@ -126,6 +126,10 @@ SQVM::SQVM(SQSharedState *ss) ci = NULL; _releasehook = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); + _ops_remaining = 0; + _ops_total = 0; + _ops_grace_amount = 500; + _throw_if_no_ops = true; } void SQVM::Finalize() @@ -386,8 +388,9 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQIntege { paramssize--; if (nargs < paramssize) { - Raise_Error(_SC("wrong number of parameters (%d passed, at least %d required)"), - (int)nargs, (int)paramssize); + const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; + const SQChar *name = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; + Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s:%s"), nargs, paramssize, src, name); return false; } @@ -413,8 +416,9 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQIntege } } else { - Raise_Error(_SC("wrong number of parameters (%d passed, %d required)"), - (int)nargs, (int)paramssize); + const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; + const SQChar *name = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; + Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s:%s"), nargs, paramssize, src, name); return false; } } @@ -679,7 +683,8 @@ bool SQVM::IsFalse(SQObjectPtr &o) return false; } extern SQInstructionDesc g_InstrDesc[]; -bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et) + +bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et, SQBool can_suspend) { if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; } _nnativecalls++; @@ -717,6 +722,26 @@ exception_restore: { for(;;) { + _ops_total++; + _ops_remaining --; + if (_ops_remaining < 0) { + // suspend vm + if (can_suspend && !_throw_if_no_ops) { + _suspended = SQTrue; + _suspended_root = ci->_root; + _suspended_traps = traps; + _suspended_target = -1; + return true; + } + else { + // throw error (if within unsuspendable function give some grace opcodes) + if (_throw_if_no_ops || (_ops_remaining + _ops_grace_amount < 0) ) { + Raise_Error(_SC("script took too long: remain = %d grac = %d"), _ops_remaining, _ops_grace_amount); + SQ_THROW(); + } + } + } + const SQInstruction &_i_ = *ci->_ip++; //dumpstack(_stackbase); //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-_closure(ci->_closure)->_function->_instructions,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3); @@ -730,7 +755,11 @@ exception_restore: #else TARGET = (SQInteger)((SQInt32)arg1); continue; #endif - case _OP_LOADFLOAT: TARGET = *((const SQFloat *)&arg1); continue; + case _OP_LOADFLOAT: + SQFloat v; + memcpy(&v, &arg1, sizeof(SQFloat)); + TARGET = v; + continue; case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue; case _OP_TAILCALL:{ SQObjectPtr &t = STK(arg1); @@ -932,7 +961,7 @@ exception_restore: break; case AAT_FLOAT: val._type = OT_FLOAT; - val._unVal.fFloat = *((const SQFloat *)&arg1); + memcpy(&val._unVal.fFloat, &arg1, sizeof(SQFloat)); break; case AAT_BOOL: val._type = OT_BOOL; @@ -1165,7 +1194,8 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb if(nparamscheck && (((nparamscheck > 0) && (nparamscheck != nargs)) || ((nparamscheck < 0) && (nargs < (-nparamscheck))))) { - Raise_Error(_SC("wrong number of parameters")); + const SQChar *src = sq_type(nclosure->_name) == OT_STRING?_stringval(nclosure->_name):NULL; + Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s"), nargs, nparamscheck, src); return false; } @@ -1577,14 +1607,14 @@ bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr return true; } -bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror) +bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror, SQBool can_suspend) { #ifdef _DEBUG SQInteger prevstackbase = _stackbase; #endif switch(sq_type(closure)) { case OT_CLOSURE: - return Execute(closure, nparams, stackbase, outres, raiseerror); + return Execute(closure, nparams, stackbase, outres, raiseerror, ET_CALL, can_suspend); break; case OT_NATIVECLOSURE:{ bool dummy; diff --git b/simutrans/trunk/squirrel/squirrel/sqvm.h a/simutrans/trunk/squirrel/squirrel/sqvm.h index c82e44c26..e768220e0 100644 --- b/simutrans/trunk/squirrel/squirrel/sqvm.h +++ a/simutrans/trunk/squirrel/squirrel/sqvm.h @@ -55,7 +55,7 @@ public: SQVM(SQSharedState *ss); ~SQVM(); bool Init(SQVM *friendvm, SQInteger stacksize); - bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL); + bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL, SQBool can_suspend = false); //starts a native call return when the NATIVE closure returns bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target, bool &suspend,bool &tailcall); bool TailCall(SQClosure *closure, SQInteger firstparam, SQInteger nparams); @@ -63,7 +63,8 @@ public: bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall); bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor); //call a generic closure pure SQUIRREL or NATIVE - bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror); + // @param can_suspend is by default FALSE, only set it to true if you handle the case that vm is suspended after call. + bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror, SQBool can_suspend = false); SQRESULT Suspend(); void CallDebugHook(SQInteger type,SQInteger forcedline=0); @@ -85,6 +86,7 @@ public: void Raise_Error(const SQChar *s, ...); + void Raise_Error_vl(const SQChar *s, va_list vl); void Raise_Error(const SQObjectPtr &desc); void Raise_IdxError(const SQObjectPtr &o); void Raise_CompareError(const SQObject &o1, const SQObject &o2); @@ -177,6 +179,11 @@ public: SQBool _suspended_root; SQInteger _suspended_target; SQInteger _suspended_traps; + + SQInteger _ops_remaining; /// number of ops the vm can do till break + SQInteger _ops_grace_amount; /// raise error if _ops_remaining is less than -_ops_grace_amount for pure native calls + SQInteger _ops_total; /// total number of ops performed + bool _throw_if_no_ops; /// is no-ops an error or can call suspended? default: true }; struct AutoDec{ diff --git b/simutrans/trunk/squirrel/update_squirrel.sh a/simutrans/trunk/squirrel/update_squirrel.sh index e3f41794c..a23dc59be 100755 --- b/simutrans/trunk/squirrel/update_squirrel.sh +++ a/simutrans/trunk/squirrel/update_squirrel.sh @@ -41,11 +41,11 @@ for i in *.h; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done cd .. cd .. -git diff -w --ignore-blank-lines --ignore-space-at-eol -b -R > update_squirrel.diff -# -# git checkout -- squirrel/squirrel -# git checkout -- squirrel/sqstdlib -# git checkout -- squirrel/sq*.h -# git checkout -- simutrans/license_squirrel.txt -# -# patch --ignore-whitespace -p3 < update_squirrel.diff +git diff -w --ignore-blank-lines --ignore-space-at-eol -b > update_squirrel.diff + +git checkout -- squirrel/squirrel +git checkout -- squirrel/sqstdlib +git checkout -- squirrel/sq*.h +git checkout -- simutrans/license_squirrel.txt + +patch --ignore-whitespace -p3 < update_squirrel.diff simutrans-124.5/src/squirrel/update_squirrel.awk000066400000000000000000000006141520675460500221360ustar00rootroot00000000000000# gawk file to minimize patch size # strip trailing whitespace { sub(/[[:space:]]+$/, "") } # replace include by include "../sq*.h" /include.*\/, "\"../\\1\"", "g"); print a next } # replace four spaces by tabs / / { a = gensub(/ /, "\t", "g"); print a next } # replace stupid endif comments /endif.*_H_/ { print "#endif" next } { print $0 } simutrans-124.5/src/squirrel/update_squirrel.sh000077500000000000000000000031411520675460500217670ustar00rootroot00000000000000#!/bin/bash # # Script to update squirrel source code from SQUIRREL3 # # Call with ``update_squirrel PATH_TO_SQUIRREL'', where # PATH_TO_SQUIRREL is path of local clone of https://github.com/albertodemichelis/squirrel # # Call it within a git repo of simutrans. # # Still needs manual review, to make sure none of our modifications are reverted. SQPATH=$1 # copy and rename files echo $SQPATH/COPYRIGHT ../simutrans/license_squirrel.txt cp $SQPATH/COPYRIGHT ../simutrans/license_squirrel.txt cp $SQPATH/include/* . cp $SQPATH/squirrel/*.h squirrel/ cp $SQPATH/squirrel/*.h squirrel/ cp $SQPATH/squirrel/*.cpp squirrel/ for i in *.h; do gawk -f update_squirrel.awk "$i" > temp; mv temp "$i"; done cd squirrel # rename for i in *.cpp; do mv "$i" "${i/.cpp}".cc; done # replace include by include "../sq*.h" for i in *.cc; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done for i in *.h; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done cd .. cp $SQPATH/sqstdlib/*.h sqstdlib/ cp $SQPATH/sqstdlib/*.cpp sqstdlib/ cd sqstdlib # rename for i in *.cpp; do mv "$i" "${i/.cpp}".cc; done # replace include by include "../sq*.h" for i in *.cc; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done for i in *.h; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done cd .. cd .. git diff -w --ignore-blank-lines --ignore-space-at-eol -b > update_squirrel.diff git checkout -- squirrel/squirrel git checkout -- squirrel/sqstdlib git checkout -- squirrel/sq*.h git checkout -- simutrans/license_squirrel.txt patch --ignore-whitespace -p3 < update_squirrel.diff simutrans-124.5/src/steam/000077500000000000000000000000001520675460500154645ustar00rootroot00000000000000simutrans-124.5/src/steam/achievements.cc000066400000000000000000000062561520675460500204570ustar00rootroot00000000000000#include "achievements.h" #include "../simutrans/simachenum.h" #include "../simutrans/simdebug.h" #include "isteamuser.h" #include "isteamutils.h" // Generating the initial array from the enum. I love the X Macro, so powerful! #define X(id) { id, #id, "", "", 0, 0 }, achievement_t achievements_list[] = { ACHIEVEMENTS }; #undef X steam_achievements_t::steam_achievements_t() : app_id(0), stats_initialized(false), m_CallbackUserStatsReceived(this, &steam_achievements_t::on_user_stats_received), m_CallbackUserStatsStored(this, &steam_achievements_t::on_user_stats_stored), m_CallbackAchievementStored(this, &steam_achievements_t::on_achievement_stored) { app_id = SteamUtils()->GetAppID(); achievements = achievements_list; request_stats(); } bool steam_achievements_t::request_stats() { if (NULL == SteamUserStats() || NULL == SteamUser()) { return false; } if (!SteamUser()->BLoggedOn()) { return false; } return SteamUserStats()->RequestCurrentStats(); } void steam_achievements_t::on_user_stats_received(UserStatsReceived_t *callback) { if (app_id == callback->m_nGameID) { if (k_EResultOK == callback->m_eResult) { dbg->message("steam_achievements_t::on_user_stats_received", "Received stats and achievements from Steam\n"); stats_initialized = true; // load achievements for (int iAch = 0; iAch < NUM_ACHIEVEMENTS; ++iAch) { achievement_t &ach = achievements[iAch]; SteamUserStats()->GetAchievement(ach.m_pchAchievementID, &ach.m_bAchieved); snprintf(ach.m_rgchName, sizeof(ach.m_rgchName), "%s", SteamUserStats()->GetAchievementDisplayAttribute(ach.m_pchAchievementID, "name")); snprintf(ach.m_rgchDescription, sizeof(ach.m_rgchDescription), "%s", SteamUserStats()->GetAchievementDisplayAttribute(ach.m_pchAchievementID, "desc")); } // retry achievements sent before the callback completed for (int ach_id : achievements_queue) { set_achievement(ach_id); } achievements_queue.clear(); } else { dbg->error("steam_achievements_t::on_user_stats_received", "RequestStats - failed, %d\n", callback->m_eResult); } } } void steam_achievements_t::on_user_stats_stored(UserStatsStored_t *callback) { if (app_id == callback->m_nGameID) { if (k_EResultOK == callback->m_eResult) { dbg->message("steam_achievements_t::on_user_stats_stored", "Stored stats for Steam\n"); } else { dbg->error("steam_achievements_t::on_user_stats_received", "StatsStored - failed, %d\n", callback->m_eResult); } } } void steam_achievements_t::on_achievement_stored(UserAchievementStored_t *callback) { if (app_id == callback->m_nGameID) { dbg->message("steam_achievements_t::on_achievement_stored", "Stored Achievement for Steam\n"); } } bool steam_achievements_t::set_achievement(int ach_enum) { if (stats_initialized && !achievements[ach_enum].m_bAchieved) { dbg->message("steam_achievements_t::set_achievement", "Earned achievement %s\n", achievements[ach_enum].m_pchAchievementID); SteamUserStats()->SetAchievement(achievements[ach_enum].m_pchAchievementID); return SteamUserStats()->StoreStats(); } else { // Too early! Add them to the queue to retry on_user_stats_received achievements_queue.push_back(ach_enum); } return false; }simutrans-124.5/src/steam/achievements.h000066400000000000000000000017431520675460500203150ustar00rootroot00000000000000#include "isteamuserstats.h" #include "steam_api.h" #include struct achievement_t { int m_eAchievementID; const char *m_pchAchievementID; char m_rgchName[128]; char m_rgchDescription[256]; bool m_bAchieved; int m_iIconImage; }; class steam_achievements_t { private: uint64 app_id; achievement_t *achievements; bool stats_initialized; std::vector achievements_queue; public: steam_achievements_t(); bool request_stats(); // Note: not declaring type as "simachievements_enum" instead of "int" to avoid circular dependency issues bool set_achievement(int ach_enum); achievement_t *get_achievements() { return achievements; }; STEAM_CALLBACK(steam_achievements_t, on_user_stats_received, UserStatsReceived_t, m_CallbackUserStatsReceived); STEAM_CALLBACK(steam_achievements_t, on_user_stats_stored, UserStatsStored_t, m_CallbackUserStatsStored); STEAM_CALLBACK(steam_achievements_t, on_achievement_stored, UserAchievementStored_t, m_CallbackAchievementStored); };simutrans-124.5/src/steam/steam.cc000066400000000000000000000176411520675460500171150ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include "steam.h" #include #include #include "../simutrans/dataobj/environment.h" #include "../simutrans/simdebug.h" #include "isteamugc.h" #include "isteamutils.h" #include "steam_api.h" #define MAX_WORKSHOP_ITEMS 1000 // API fetch limit #define MAX_TAG_LENGTH 100 steam_t* steam_t::steam = nullptr; steam_t::steam_t() { if (!(is_api_initialized = SteamAPI_Init())) { dbg->warning("steam_t::steam_t()", "Steam API Initialization FAILED, Steam functionality won't be available!"); } else { steam_achievements = new steam_achievements_t(); } } steam_t* steam_t::get_instance() { if (steam == nullptr) { steam = new steam_t(); } return steam; } /** * 0. Get previously installed items (saved in wokshop.txt) * 1. Get subscribed items: https://partner.steamgames.com/doc/api/ISteamUGC#GetSubscribedItems * 2. Compare to ge new items and items to uninstall * 3. Uninstall old items that are no longer subscribed * 4. Get details of the new susbcribed items to fetch tags: * 5. Get the number of tags for each new item: https://partner.steamgames.com/doc/api/ISteamUGC#GetQueryUGCNumKeyValueTags * 6. Get the values of the tags for each new item: https://partner.steamgames.com/doc/api/ISteamUGC#GetQueryUGCKeyValueTag * 7. Install the new items in an appropiate location based on their tags */ void steam_t::install_workshop_items() { if (!is_api_initialized) return; std::vector previously_installed_items = steam_t::read_installed_items(); int num_subscribed_items = SteamUGC()->GetNumSubscribedItems(); PublishedFileId_t subscribed_items[MAX_WORKSHOP_ITEMS]; SteamUGC()->GetSubscribedItems(subscribed_items, num_subscribed_items); existing_items = steam_t::uninstall_old_items(previously_installed_items, subscribed_items, num_subscribed_items); new_items_ids = steam_t::get_new_items(existing_items, subscribed_items, num_subscribed_items); if (new_items_ids.size()) { UGCQueryHandle_t ugc_query = SteamUGC()->CreateQueryUGCDetailsRequest(new_items_ids.data(), new_items_ids.size()); if (ugc_query != k_UGCQueryHandleInvalid) { SteamUGC()->SetReturnMetadata(ugc_query, true); SteamAPICall_t hSteamAPICall = SteamUGC()->SendQueryUGCRequest(ugc_query); get_items_details_result.Set(hSteamAPICall, steam, &steam_t::on_get_items_details); dbg->message("steam_t::install_workshop_items()", "Fetching details of new items..."); is_installing = true; // Calls will resolve asynchronously, but we need to wait until paksets are installed! while (is_installing) { SteamAPI_RunCallbacks(); } } } else if (existing_items.size() != previously_installed_items.size()) { write_existing_items(); } } void steam_t::on_get_items_details(SteamUGCQueryCompleted_t* callback, bool failure) { std::vector installed_items; if (!failure && callback->m_eResult == 1) { int num_results = callback->m_unNumResultsReturned; int num_tags_vector[MAX_WORKSHOP_ITEMS]; for (int result_index = 0; result_index < num_results; result_index++) { num_tags_vector[result_index] = SteamUGC()->GetQueryUGCNumTags(callback->m_handle, result_index); } for (int result_index = 0; result_index < num_results; result_index++) { SteamUGCDetails_t pDetails = SteamUGCDetails_t(); SteamUGC()->GetQueryUGCResult(callback->m_handle, result_index, &pDetails); workshop_item_t item = workshop_item_t(new_items_ids[result_index], pDetails.m_rgchTitle); for (int tag_index = 0; tag_index < num_tags_vector[result_index]; tag_index++) { char tag[MAX_TAG_LENGTH]; SteamUGC()->GetQueryUGCTagDisplayName(callback->m_handle, result_index, tag_index, tag, MAX_TAG_LENGTH); if (!item.set_category_tag(tag) && !item.add_pakset_tag(tag)) { dbg->error("steam_t::on_get_items_details()", "Tag name '%s' is not a valid tag!", tag); } } char folder[1024]; SteamUGC()->GetItemInstallInfo(item.get_id(), NULL, folder, sizeof(folder), NULL); if (item.install(folder)) { installed_items.push_back(item); } } } steam_t::write_installed_items(installed_items); is_installing = false; SteamUGC()->ReleaseQueryUGCRequest(callback->m_handle); } /** * Get the list of installed items from a command-separated list in a file * First value should be the workshop item id, then files installed */ std::vector steam_t::read_installed_items() { std::string file_path = std::string(env_t::base_dir) + "workshop.txt"; std::vector installed_items; std::ifstream file(file_path); if (file.is_open()) { std::string line; std::string delimiter = ","; while (getline(file, line)) { // dbg->message("steam_t::read_installed_items()", "Reading line: '%s'", line.c_str()); std::string id = line.substr(0, line.find(delimiter)); line.erase(0, line.find(delimiter) + delimiter.length()); std::vector files; while (true) { std::string path = line.substr(0, line.find(delimiter)); files.push_back(path); // dbg->message("steam_t::read_installed_items()", "Reading line: '%s'", line.c_str()); if (line.find(delimiter) == line.npos) { break; } else { line.erase(0, line.find(delimiter) + delimiter.length()); } } installed_items.push_back({std::stoull(id), files}); } file.close(); } return installed_items; } void steam_t::write_installed_items(std::vector installed_items) { for (auto item : installed_items) { existing_items.push_back(item); } write_existing_items(); } void steam_t::write_existing_items() { std::string file_path = std::string(env_t::base_dir) + "workshop.txt"; std::remove(file_path.c_str()); std::ofstream file(file_path); if (file.is_open()) { for (auto item : existing_items) { std::string line = std::to_string(item.get_id()); for (auto item_file : item.get_files()) { line.append("," + item_file); } file << line + "\n"; } file.close(); } else { dbg->error("steam_t::write_existing_items()", "Could not open %s file for writing", file_path.c_str()); } } std::vector steam_t::get_new_items(std::vector installed_items, PublishedFileId_t subscribed_items[], int num_subscribed_items) { std::vector new_items; for (int i = 0; i < num_subscribed_items; i++) { bool found_item = false; for (auto item : installed_items) { if (item.get_id() == subscribed_items[i]) { found_item = true; break; } } if (!found_item) { new_items.push_back(subscribed_items[i]); dbg->message("steam_t::get_new_items()", "Found new workshop item: %lu", subscribed_items[i]); } } return new_items; } std::vector steam_t::uninstall_old_items(std::vector installed_items, PublishedFileId_t subscribed_items[], int num_subscribed_items) { std::vector old_items; std::vector current_items; for (auto item : installed_items) { bool found_item = false; for (int i = 0; i < num_subscribed_items; i++) { if (item.get_id() == subscribed_items[i]) { found_item = true; existing_items.push_back(item); break; } } if (!found_item) { old_items.push_back(item); } else { current_items.push_back(item); } } for (auto item : old_items) { item.uninstall(); } return current_items; } void steam_t::update_ui(uint32 year, uint32 total_convoys) { if (!is_api_initialized) return; std::string pakset = env_t::pak_name; pakset.pop_back(); // Remove path separator SteamFriends()->SetRichPresence("pakset", pakset.c_str()); SteamFriends()->SetRichPresence("year", std::to_string(year).c_str()); SteamFriends()->SetRichPresence("convoys", std::to_string(total_convoys).c_str()); SteamFriends()->SetRichPresence("steam_display", "#Playing"); SteamAPI_RunCallbacks(); } void steam_t::shutdown() { SteamAPI_Shutdown(); if (steam_achievements) delete steam_achievements; }simutrans-124.5/src/steam/steam.h000066400000000000000000000030201520675460500167410ustar00rootroot00000000000000/* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) */ #include #include #include #include "achievements.h" #include "isteamugc.h" #include "workshop_item.h" /** * Singleton class to prevent the Steam API from being initialized more than once */ class steam_t { private: steam_t(); steam_t(steam_t& other) = delete; steam_t& operator=(steam_t& other) = delete; static steam_t* steam; steam_achievements_t* steam_achievements = NULL; bool is_api_initialized = false; bool is_installing = false; std::vector new_items_ids; std::vector existing_items; CCallResult get_items_details_result; void on_get_items_details(SteamUGCQueryCompleted_t* callback, bool failure); std::vector read_installed_items(); void write_installed_items(std::vector installed_items); void write_existing_items(); std::vector get_new_items(std::vector installed_items, PublishedFileId_t subscribed_items[], int num_subscribed_items); std::vector uninstall_old_items(std::vector installed_items, PublishedFileId_t subscribed_items[], int num_subscribed_items); public: static steam_t* get_instance(); void install_workshop_items(); void update_ui(uint32 year, uint32 total_convoys); steam_achievements_t* get_achievements() { return steam_achievements; } void shutdown(); }; simutrans-124.5/src/steam/workshop_item.cc000066400000000000000000000063601520675460500206720ustar00rootroot00000000000000#include "workshop_item.h" #include #include #include "../simutrans/dataobj/environment.h" #include "../simutrans/simdebug.h" #include "../simutrans/sys/simsys.h" #include "../simutrans/utils/simstring.h" namespace fs = std::filesystem; // Current categories available on Steam Workshop static const std::set valid_paksets({"pak", "Pak64", "Pak128", "pak128.japan", "pak192.comic"}); static const std::set valid_categories({"pakset", "map", "addon", "Scenario"}); workshop_item_t::workshop_item_t(PublishedFileId_t id, std::vector files) { this->id = id; this->files = files; }; workshop_item_t::workshop_item_t(PublishedFileId_t id, std::string title) { this->id = id; str_to_lowercase(title); this->title = title; }; bool workshop_item_t::set_category_tag(std::string tag) { auto search = valid_categories.find(tag); if (search != valid_categories.end()) { category_tag = tag; return true; } return false; } bool workshop_item_t::add_pakset_tag(std::string tag) { auto search = valid_paksets.find(tag); if (search != valid_paksets.end()) { str_to_lowercase(tag); if (tag == "pak") { pakset_tags.push_back("pak64"); } else { pakset_tags.push_back(tag); } return true; } return false; } void workshop_item_t::fill_paths() { if (category_tag == "map") { paths.push_back("maps"); } else if (category_tag == "pakset") { paths.push_back("paksets" + std::string(PATH_SEPARATOR) + title); } else if (category_tag == "addon") { for (auto pakset_name : pakset_tags) { paths.push_back("addons" + std::string(PATH_SEPARATOR) + pakset_name); } } else if (category_tag == "Scenario") { for (auto pakset_name : pakset_tags) { paths.push_back("addons" + std::string(PATH_SEPARATOR) + pakset_name + std::string(PATH_SEPARATOR) + "scenario" + std::string(PATH_SEPARATOR) + this->title); } } } bool workshop_item_t::install(std::string origin) { this->fill_paths(); if (paths.size()) { for (auto path : paths) { fs::create_directories(env_t::user_dir + path); const auto copyOptions = std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive; std::filesystem::copy(origin, env_t::user_dir + path, copyOptions); dbg->message("workshop_item_t::install()", "Installed workshop item %lu from %s to %s", this->id, origin.c_str(), (env_t::user_dir + path).c_str()); } // For other categories we save the full list of files, we will need them when uninstalling to avoid deleting unrelated items if (this->category_tag == "map" || this->category_tag == "addon") { std::vector files; for (auto path : paths) { for (auto const& dir_entry : std::filesystem::directory_iterator{origin}) { files.push_back(env_t::user_dir + path + std::string(PATH_SEPARATOR) + dir_entry.path().filename().string()); } } this->files = files; } else { this->files = paths; } return true; } dbg->error("workshop_item_t::install()", "Could NOT install workshop item %lu: no valid paths!", this->id); return false; } void workshop_item_t::uninstall() { if(files.size()){ for(auto file : files ) { fs::remove_all(file); } } dbg->message("workshop_item_t::uninstall()", "Uninstalled workshop item %lu", this->id); }simutrans-124.5/src/steam/workshop_item.h000066400000000000000000000014611520675460500205310ustar00rootroot00000000000000 #include "isteamugc.h" #include #include class workshop_item_t { private: PublishedFileId_t id; std::string title; std::vector paths; std::vector files; std::string category_tag; std::vector pakset_tags; void fill_paths(); public: workshop_item_t(PublishedFileId_t id, std::vector files); workshop_item_t(PublishedFileId_t id, std::string title); /** * Sets the category tag. It must be "pakset", "scenario", "map", or "addon" * @returns false if it was unable to set category */ bool set_category_tag(std::string tag); bool add_pakset_tag(std::string tag); bool install(std::string origin); void uninstall(); PublishedFileId_t get_id() { return id; }; std::vector get_files() { return files; }; };simutrans-124.5/src/toolkit_text.dat000066400000000000000000000051331520675460500176000ustar00rootroot00000000000000obj=script_toolkit name=Way key %s is not defined. note=messages hm_way_tl - obj=script_toolkit name=No way was detected between %s. note=messages hm_way_tl - obj=script_toolkit name=Selected way %s is not available. note=messages hm_way_tl - obj=script_toolkit name=Way %s (%s) is not found! note=messages hm_way_tl - obj=script_toolkit name=Way building path from ($s) to (%s) is not found! note=messages hm_way_tl - obj=script_toolkit name=Bridge %s (%s) is not found! note=messages hm_bridge_tl - obj=script_toolkit name=Bridge building path from (%s) to (%s) is not found! note=messages hm_bridge_tl - obj=script_toolkit name=Depot key %s is not defined. note=messages hm_depot_tl - obj=script_toolkit name=No depot was detected between %s. note=messages hm_depot_tl - obj=script_toolkit name=Selected depot %s is not available. note=messages hm_depot_tl - obj=script_toolkit name=Depot %s (%s) is not found! note=messages hm_depot_tl - obj=script_toolkit name=No ways were found at %s. note=message hm_remove_tl - obj=script_toolkit name=Sign key %s is not defined. note=message hm_sign_tl - obj=script_toolkit name=No sign was detected between %s. note=message hm_sign_tl - obj=script_toolkit name=Selected sign %s is not available. note=message hm_sign_tl - obj=script_toolkit name=Sign %s (%s) is not found! note=message hm_sign_tl - obj=script_toolkit name=Error constructing signal on tile %s. note=message hm_sign_tl - obj=script_toolkit name=Tile %s is not a valid ground! note=message hm_slope_tl - obj=script_toolkit name=Station key %s is not defined. note=message hm_station_tl - obj=script_toolkit name=No station was detected between %s. note=message hm_station_tl - obj=script_toolkit name=Selected station %s is not available. note=message hm_station_tl - obj=script_toolkit name=Station %s (%s) is not found! note=message hm_station_tl - obj=script_toolkit name=Wayobj key %s is not defined. note=message hm_wayobj_tl - obj=script_toolkit name=No wayobj was detected between %s. note=message hm_wayobj_tl - obj=script_toolkit name=Selected wayobj %s is not available. note=message hm_wayobj_tl - obj=script_toolkit name=Wayobj %s (%s) is not found! note=message hm_wayobj_tl - obj=script_toolkit name=Wayobj building path from ($s) to (%s) is not found! note=message hm_wayobj_tl - obj=script_toolkit name=Selected area not free for build! note=message hm_test_area_tl - obj=script_toolkit name=The terrain is not suitable for construction. note=message hm_test_area_tl - #obj=script_toolkit #name= #note= #- simutrans-124.5/tests/000077500000000000000000000000001520675460500147265ustar00rootroot00000000000000simutrans-124.5/tests/all_tests.nut000066400000000000000000000171121520675460500174520ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // list containing all tests // include("tests/test_building") include("tests/test_city") include("tests/test_climate") include("tests/test_depot") include("tests/test_dir") include("tests/test_factory") include("tests/test_good") include("tests/test_groundobj") include("tests/test_halt") include("tests/test_headquarters") include("tests/test_label") include("tests/test_player") include("tests/test_powerline") include("tests/test_reservation") include("tests/test_scenario") include("tests/test_sign") include("tests/test_slope") include("tests/test_terraform") include("tests/test_transport") include("tests/test_trees") include("tests/test_way_bridge") include("tests/test_way_road") include("tests/test_way_runway") include("tests/test_way_tram") include("tests/test_way_tunnel") include("tests/test_wayobj") all_tests <- [ test_building_build_house_invalid_param, test_building_build_house_random, test_building_build_house_valid_desc, test_building_build_house_invalid_desc, test_building_build_house_auto_rotation_attraction, test_building_build_house_auto_rotation_citybuilding, test_building_build_multi_tile_sloped, test_building_buy_house_invalid_param, test_building_buy_house_from_public_player, test_building_buy_house_attraction, test_building_rotate_house, test_building_rotate_harbour, test_building_rotate_station, test_building_rotate_factory, test_city_add_invalid, test_city_add_cannot_afford, test_city_add_by_public_player, test_city_add_on_existing_townhall, test_city_add_near_map_border, test_city_change_size_invalid_params, test_city_change_size_to_minimum test_climate_invalid, test_climate_flat, test_climate_cliff, test_depot_build_invalid_params, test_depot_build_invalid_pos, test_depot_build_road, test_depot_build_road_on_tram_crossing, test_depot_build_water, test_depot_build_monorail, test_depot_build_tram, test_depot_build_sloped, test_depot_build_on_tunnel_entrance, test_depot_build_on_bridge_end, test_depot_build_on_halt, test_depot_convoy_add_normal, test_depot_convoy_add_nonelectrified, test_dir_is_single, test_dir_is_twoway, test_dir_is_threeway, test_dir_is_curve, test_dir_is_straight, test_dir_double, test_dir_backward, test_dir_to_slope, test_dir_to_coord, test_factory_build_pp, test_factory_build_with_fields, test_factory_build_climate, test_factory_build_on_water_occupied, test_factory_link, test_factory_desc, test_good_is_interchangeable, test_good_speed_bonus, test_groundobj_build_invalid_param, test_groundobj_build_invalid_pos, test_groundobj_build_random, test_groundobj_build_specific, test_groundobj_build_invalid_climate, test_groundobj_build_ignore_climate, test_groundobj_build_occupied, test_groundobj_build_on_trees, test_groundobj_build_on_slope, test_halt_build_rail_single_tile, test_halt_build_harbour, test_halt_build_flat_dock_near_water, test_halt_build_flat_dock_near_water_multiple_rotations, test_halt_build_flat_dock_near_water_fixed_rotation_valid, test_halt_build_flat_dock_near_water_fixed_rotation_invalid, test_halt_build_flat_dock_outside_map, test_halt_build_flat_dock_near_map_border_auto_rotation, test_halt_build_flat_dock_near_map_border_fixed_rotation test_halt_build_flat_dock_on_bridge, test_halt_build_flat_dock_on_slope, test_halt_build_flat_dock_near_cliff, test_halt_build_flat_dock_in_water, test_halt_build_flat_dock_occupied, test_halt_build_air, test_halt_build_multi_tile, test_halt_build_multi_mode, test_halt_build_multi_player, test_halt_build_separate, test_halt_build_near_factory, test_halt_build_near_factories, test_halt_build_on_tunnel_entrance, test_halt_build_on_bridge_end, test_halt_build_on_depot, test_halt_build_station_invalid_param, test_halt_build_station_extension, test_halt_upgrade_downgrade, test_halt_make_public_single, test_halt_make_public_multi_tile, test_halt_make_public_underground, test_halt_move_stop_invalid_param, test_headquarters_build_flat, test_label, test_player_cash, test_player_isactive, test_player_headquarters, test_player_name, test_player_lines, test_powerline_connect, test_powerline_build_below_powerbridge, test_powerline_build_powerbridge_above_powerline, test_powerline_build_transformer, test_powerline_build_over_transformer, test_powerline_build_transformer_multiple, test_powerline_remove_powerbridge, test_powerline_ways, test_reservation_clear_ground, test_reservation_clear_road, test_reservation_clear_rail, test_scenario_rules_allow_forbid_tool, test_scenario_rules_allow_forbid_way_tool_rect, test_scenario_rules_allow_forbid_way_tool_cube, test_scenario_rules_allow_forbid_tool_stacked_rect, test_scenario_rules_allow_forbid_tool_stacked_cube, test_sign_build_oneway, test_sign_build_trafficlight, test_sign_remove_trafficlight, test_sign_build_private_way, test_sign_build_signal, test_sign_build_signal_multiple, test_sign_replace_signal, test_sign_signal_when_player_removed, test_slope_to_dir, test_slope_can_set, test_slope_set_and_restore, test_slope_get_price, test_slope_set_near_map_border, test_slope_restore_on_foundation, test_slope_restore_on_bridge, test_slope_restore_on_label, test_slope_max_height_diff, test_terraform_raise_lower_land, test_terraform_raise_lower_land_at_map_border, test_terraform_raise_lower_land_at_water_center, test_terraform_raise_lower_land_at_water_corner, test_terraform_raise_lower_land_at_water_edge, test_terraform_raise_lower_land_below_way, test_terraform_raise_lower_water_level, test_transport_generate_pax_invalid_pos, test_transport_generate_pax_walked, test_transport_generate_pax_no_route, test_transport_pax_valid_route, test_transport_mail_valid_route, test_transport_freight_valid_route, test_trees_plant_single_invalid_pos, test_trees_plant_single_invalid_param, test_trees_plant_single_null_param, test_trees_plant_single_empty_param, test_trees_plant_single_invalid_desc, test_trees_plant_single_when_disabled, test_trees_plant_single_random_age, test_trees_plant_single_ignore_climate, test_trees_plant_single_max_per_square, test_trees_plant_single_occupied, test_trees_plant_forest, test_way_bridge_build_ground, test_way_bridge_build_at_slope, test_way_bridge_build_at_slope_stacked, test_way_bridge_build_above_way, test_way_bridge_build_above_runway, test_way_bridge_planner, test_way_road_build_single_tile, test_way_road_build_straight, test_way_road_build_bend, test_way_road_build_parallel, test_way_road_build_below_powerline, test_way_road_build_crossing, test_way_road_upgrade_crossing, test_way_road_upgrade_downgrade, test_way_road_upgrade_downgrade_across_bridge, test_way_road_cityroad_build, test_way_road_cityroad_upgrade_with_cityroad, test_way_road_cityroad_downgrade_with_cityroad, test_way_road_cityroad_replace_by_normal_road, test_way_road_cityroad_replace_keep_existing, test_way_road_has_double_slopes, test_way_road_make_public, test_way_runway_build_rw_flat, test_way_runway_build_tw_flat, test_way_runway_build_mixed_flat, test_way_tram_build_flat, test_way_tram_build_parallel, test_way_tram_build_on_road, test_way_tram_build_across_road_bridge, test_way_tram_build_across_crossing, test_way_tram_build_in_tunel, test_way_tram_has_double_slopes, test_way_tunnel_build_straight, test_way_tunnel_build_up_down, test_way_tunnel_build_above_tunnel_slope, test_way_tunnel_build_across_tunnel_slope, test_way_tunnel_make_public, test_wayobj_build_straight, test_wayobj_build_disconnected, test_wayobj_upgrade_downgrade, test_wayobj_upgrade_change_owner, test_wayobj_electrify_depot ] simutrans-124.5/tests/empty-16x16.sve000066400000000000000000000072671520675460500174020ustar00rootroot00000000000000 TN{IBJI*:($'8J) (*%RN9FEҡCDDۀ{λֽܻy޿Z{=^pâuF3]FNB;s(Xekmjjnn$ꞑdK.VۊM͟_2߿,*- :Բn[Sۤ)mS ht4Zڴ%¼tpj)'ttp^ȹUvvtv|&:ǷP#%YaG{^M?  ][H$ZVG\p^$&&$B$ iTwQhhH坎4%kbQDMڒK->][)i/|uu!~_~yi6|y.Nthٺl2Bך+.xroknږ5 YLJRX#H$G3K6ͥ/|# B_kYtZŌ.}kwII1HvW̷~S_NV|Vћ<(<>Fdd-Kߏ-,mzi•s/t~^iW;k3'+*Xborלjj}p^6VvQK.>]r;̉9/{t)$W,Ps=U!7W?URnj}TʸC-jpV^Jz>Hk} w]vKc> KλbcCDŽ_1pe>=(/WSw6әccUfR.Nѻ_DѰl~j/'{rґP۬.ɪ ?Ł^n;#iZ|Iv`[z.?y[uWTۙ;v`Գ4|Iӿ"ؕvPL+oG7zps̲FX_S[~tjV+{ٽOj8]]ג*zum c:,Z!`v8sSn~tw󭏿Gۚa+$:Wd֯\d\u@f_˷d،2=Մ϶gōqa䑙zPbei;&ܠ#d9) NƯ}WT? ({ٷii67͚h8|E-{;8w  |{n\Ei' U|EuϾw7QmP{P~v(1'#Vg^:ε5w"46Eٹzj}^f([zMcD7 ~5w1ʃ_|H~†Ke*,v|"oXm6,xY3^ l1qM|uf8 !p^(Y̗,ggݠNϞ_5@vc眭 љUQqZ䁧_G.PtlMzdGVYV. IVvx쁫O^f5^Ε.厶ҥR\N׍}ѭKpj5>:/hT1KwKXdCܾ1a)o 5Y#i:xgn:~=7>NG{&p7iAo^[!Q[I/Cn3fIIਏ:nARQ6W&xiZ792|P[mG'sAԷ{fsjhve>l=tf.J>]o6зXl=] ;7YaL m &DWvOpyiȻ\y%:efvq_fc:qƨ_lҐikz9Պr?\yߣMZ|UCaG\#j蜯Vo3:z{Ab=5V3wi&A#+%,;lqqY;^+1PyhuY#)U,7hH0MZWK̫&>u#uwaȉ5z[tϨ{Cݶk8{ n#;.S~yzoJ {/YVz~J_hvcfQ.hՔ *TНs NR8=c5Q[R:NȾHb0/_UX)?.~6WtLRKb7ůLw$i[Aaέg/tq7U3hTXGe^?VWX%Qw6ir 󚹪J3W. դ1vVrHjJO;Sˋ[54U}7zֆlL+,LӪ^OfȾz4G2zN_o߬z'Jq#]ӮiezQr!^O$ߴ6ʧ岇['1ͯx;0-燼 zOW3 cU_3:SL'-u}mraW|90zIwiי#/Ԙ4_w|βC}o֔ 3WuxLwWח1xԜtEM~ǝ.|ptx$I/1K[zǦUlfR/ %7!*:n;ykmFNorXF%Tsͦ`={5%{=w\1fiT|J1J~4aI rԚFYq:ûķ):wfZҸvHJ5jgʭLy^7IہQWĦ[qŁ퍃oV6^-];S^-x:c ɶr~iƉqC$|[6!rYeG#ݤj}lH1Ksimutrans-124.5/tests/scenario.nut000066400000000000000000000032621520675460500172640ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // map.file = "empty-16x16.sve" scenario.short_description = "Automated tests" scenario.author = "ceeac" scenario.version = "0.1" // // Includes // include("test_helpers") include("all_tests") // // test runner stuff // local num_tests_done = 0 local num_tests = all_tests.len() local error_msg = null function run_all_tests() { num_tests_done = 0 error_msg = null print("============================================================") print("== Running tests ... =======================================") print("============================================================") foreach (i,test_func in all_tests) { local func_name = test_func.getinfos().name print("[" + (num_tests_done+1) + "/" + num_tests + "] " + func_name) test_func() // run the test ++num_tests_done } print("Tests completed successfully.") } // // Required for scenario // function get_rule_text(pl) { return ttext("Don't touch.") } function get_goal_text(pl) { return ttext("Wait for all tests to pass.") } function get_info_text(pl) { if (error_msg != null) { // an error ocurred return error_msg } local msg = ttext("{ndone}/{ntests} completed.") msg.ndone = num_tests_done msg.ntests = num_tests return msg } function get_result_text(pl) { return get_info_text(pl) } function is_tool_allowed(pl, tool_id, wt, name) { return true } function start() { run_all_tests() } function resume_game() { run_all_tests() } function is_scenario_completed(pl) { if (error_msg != null) { return -1 } else if (num_tests == 0) { return 100 } return (100*num_tests_done) / num_tests } simutrans-124.5/tests/test_helpers.nut000066400000000000000000000072021520675460500201600ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Test helpers // function make_assertion_str(val) { if (typeof val == "string") { return "\"" + val + "\"" } else { return "" + val } } function ASSERT_EQUAL(act, exp) { if (!(act == exp)) { local err = ttext("Assertion failed, '{act} == {exp}' was not true") err.act = make_assertion_str(act) err.exp = make_assertion_str(exp) throw err.tostring() } } function ASSERT_TRUE(a) { if (!(a == true)) { local err = ttext("Assertion failed, '{a}' was not true") err.a = make_assertion_str(a) throw err.tostring() } } function ASSERT_FALSE(a) { if (!(a == false)) { local err = ttext("Assertion failed, '{a}' was not false") err.a = make_assertion_str(a) throw err.tostring() } } // Set player funds (incl. non-cash assets) to some specific amount (in credit-cents) function SET_PLAYER_FUNDS(pl, amount) { pl.book_cash(amount - pl.get_current_net_wealth()) } function RESET_ALL_PLAYER_FUNDS() { local default_cash = 200000 * 100 for (local i = 0; i < 8; ++i) { local pl = player_x(i) if (pl.is_valid()) { ASSERT_EQUAL(pl.get_current_maintenance(), 0) SET_PLAYER_FUNDS(pl, default_cash) } } } function char_to_dir(ch) { switch (ch) { case '.': return dir.none // '.' == dontcare case '0': return dir.none case '1': return dir.north case '2': return dir.east case '3': return dir.northeast case '4': return dir.south case '5': return dir.northsouth case '6': return dir.southeast case '7': return dir.northsoutheast case '8': return dir.west case '9': return dir.northwest case 'A': return dir.eastwest case 'B': return dir.northeastwest case 'C': return dir.southwest case 'D': return dir.northsouthwest case 'E': return dir.southeastwest case 'F': return dir.all } ASSERT_TRUE(false) // should not reach here } function ASSERT_WAY_PATTERN(waytype, lefttop, pattern) { local z = lefttop.z for (local y = 0; y < pattern.len(); ++y) { for (local x = 0; x < pattern[y].len(); ++x) { local tile = square_x(lefttop.x + x, lefttop.y + y).get_tile_at_height(z) local expected_dir = char_to_dir(pattern[y][x]) if (tile != null) { local actual_dir = 0 if (waytype != wt_power) { actual_dir = tile.get_way_dirs(waytype) } else { // powerlines connect to other powerlines automatically. local powerline = tile.find_object(mo_powerline) actual_dir = 0 if (powerline) { for (local i = 0; i < 4; ++i) { local offset = dir.to_coord(1< old_maint) ASSERT_TRUE(pl.get_cash()[0] < old_cash) } old_cash = pl.get_cash()[0] old_maint = pl.get_current_maintenance() // public player cannot buy buildings { ASSERT_EQUAL(command_x(tool_buy_house).work(public_pl, coord3d(0,0,0)), "") ASSERT_EQUAL(building_x(0,0,0).get_owner().nr, pl.nr) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) ASSERT_EQUAL(pl.get_cash()[0], old_cash) } // cannot buy from myself { ASSERT_EQUAL(command_x(tool_buy_house).work(pl, coord3d(0,0,0)), "") ASSERT_EQUAL(building_x(0,0,0).get_owner().nr, pl.nr) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) ASSERT_EQUAL(pl.get_cash()[0], old_cash) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0,0,0)), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(8,8,0)), null); // remove city ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_road), null); RESET_ALL_PLAYER_FUNDS(); } function test_building_buy_house_attraction() { local pl = player_x(0) local public_pl = player_x(1) // add the required city ASSERT_EQUAL(command_x(tool_add_city).work(public_pl, coord3d(8, 8, 0), "0"), null) local building_desc = building_desc_x("STADIUM2") local builder = command_x(tool_build_house) // build stadium using public player ASSERT_EQUAL(builder.work(public_pl, coord3d(0,0,0), "11" + building_desc.get_name()), null) { ASSERT_EQUAL(command_x(tool_buy_house).work(pl, coord3d(0,0,0)), "Das Feld gehoert\neinem anderen Spieler\n") ASSERT_EQUAL(building_x(0,0,0).get_owner().nr, public_pl.nr) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0,0,0)), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(8,8,0)), null); // remove city ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_road), null); RESET_ALL_PLAYER_FUNDS(); } function test_building_rotate_house() { local pl = player_x(0) local public_pl = player_x(1) local builder = command_x(tool_build_house) local rotator = command_x(tool_rotate_building) local remover = command_x(tool_remover) // add the required city ASSERT_EQUAL(command_x(tool_add_city).work(public_pl, coord3d(8, 8, 0), "0"), null) // invalid coordinate { ASSERT_EQUAL(rotator.work(pl, coord3d(-1, -1, 0)), "") } // valid coordinate, but no house to rotate { ASSERT_EQUAL(rotator.work(pl, coord3d(0, 0, 0)), null) } ASSERT_EQUAL(builder.work(public_pl, coord3d(0, 0, 0), "01A4_SCHLOSS2"), null) // cannot rotate buildings owned by other players { ASSERT_EQUAL(rotator.work(pl, coord3d(0, 0, 0)), "Das Feld gehoert\neinem anderen Spieler\n") } // rotate single-tile house { ASSERT_EQUAL(rotator.work(public_pl, coord3d(0, 0, 0)), null) } ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) ASSERT_EQUAL(builder.work(public_pl, coord3d(0, 0, 0), "01RUIN_0"), null) // rotate multi-tile house { ASSERT_EQUAL(rotator.work(public_pl, coord3d(0, 0, 0)), null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(8, 8, 0)), null); // remove city ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_road), null); RESET_ALL_PLAYER_FUNDS(); } function test_building_rotate_harbour() { local pl = player_x(0) local setslope = command_x.set_slope local setclimate = command_x(tool_set_climate) local harbours = building_desc_x.get_building_list(building_desc_x.harbour) harbours = harbours.filter(@(idx, val) val.get_type() == building_desc_x.harbour) local harbour = harbours[0] ASSERT_EQUAL(setclimate.work(pl, coord3d(4, 2, 0), coord3d(5, 2, 0), "" + cl_water), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.east), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 2, 0), harbour.get_name()), null) { ASSERT_EQUAL(command_x(tool_rotate_building).work(pl, coord3d(3, 2, 0)), "Cannot rotate this building!") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 2, 0)), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.flat), null) ASSERT_EQUAL(setclimate.work(pl, coord3d(4, 2, 0), coord3d(5, 2, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_building_rotate_station() { local pl = player_x(0) local rotator = command_x(tool_rotate_building) local wayremover = command_x(tool_remove_way) local stationbuilder = command_x(tool_build_station) local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local layout2_station = building_desc_x("SandStationMHz") // this one has only 2 layouts local layout16_station = building_desc_x("GCGTrainStop") // this one has 16 layouts ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), rail_desc, true), null) ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), layout2_station.get_name()), null) local old_cash = pl.get_current_cash() { ASSERT_EQUAL(rotator.work(pl, coord3d(4, 3, 0)), "Cannot rotate this building!") ASSERT_EQUAL(pl.get_current_cash(), old_cash) } stationbuilder.set_flags(2) // enable ctrl ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), layout16_station.get_name()), null) stationbuilder.set_flags(0) // and disable ctrl again old_cash = pl.get_current_cash() { ASSERT_EQUAL(rotator.work(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(pl.get_current_cash(), old_cash) } // clean up ASSERT_EQUAL(wayremover.work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_building_rotate_factory() { local public_pl = player_x(1) local rotator = command_x(tool_rotate_building) local remover = command_x(tool_remover) local production = 1024 // 1/s // non-square building with 2 rotations ASSERT_EQUAL(build_factory(public_pl, coord3d(3, 4, 0), 0, 1, production, "TANKE"), null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_building) != null) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_building) != null) { ASSERT_EQUAL(rotator.work(public_pl, coord3d(3, 4, 0)), "Cannot rotate this building!") ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_building) != null) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_building) != null) ASSERT_TRUE(tile_x(3, 5, 0).find_object(mo_building) == null) ASSERT_TRUE(tile_x(3, 3, 0).find_object(mo_building) == null) } // clean up ASSERT_EQUAL(remover.work(public_pl, coord3d(3, 4, 0)), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_city.nut000066400000000000000000000154651520675460500206420ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for adding, removing and deleting cities // function test_city_add_invalid() { ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(-1, -1, 0)), "") } function test_city_add_cannot_afford() { ASSERT_EQUAL(command_x(tool_add_city).work(player_x(0), coord3d(7, 8, 0)), "Insufficient funds!") // clean up RESET_ALL_PLAYER_FUNDS() } function test_city_add_by_public_player() { { ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(7, 8, 0)), null) ASSERT_TRUE(tile_x(7, 8, 0).find_object(mo_building) != null) ASSERT_TRUE(tile_x(6, 9, 0).get_way(wt_road) != null) ASSERT_TRUE(tile_x(7, 9, 0).get_way(wt_road) != null) ASSERT_TRUE(tile_x(8, 9, 0).get_way(wt_road) != null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(7, 8, 0)), null) // street ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(6, 9, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(7, 9, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(8, 9, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_city_add_on_existing_townhall() { ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(7, 8, 0)), null) { ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(7, 8, 0)), "Cities can only be built on flat empty ground!") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(7, 8, 0)), null) // street ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(6, 9, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(7, 9, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(8, 9, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_city_add_near_map_border() { { ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(0, 15, 0)), null) ASSERT_TRUE(tile_x(0, 14, 0).find_object(mo_building) != null) ASSERT_TRUE(tile_x(1, 13, 0).get_way(wt_road) != null) ASSERT_TRUE(tile_x(1, 14, 0).get_way(wt_road) != null) ASSERT_TRUE(tile_x(1, 15, 0).get_way(wt_road) != null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(0, 14, 0)), null) // street ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(1, 13, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(1, 14, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(1, 15, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_city_change_size_invalid_params() { local pl = player_x(0) // not a city { ASSERT_EQUAL(command_x(tool_change_city_size).work(pl, coord3d(1, 1, 0), "100"), "") } ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(1, 1, 0)), null) // invalid pos { ASSERT_EQUAL(command_x(tool_change_city_size).work(pl, coord3d(-1,-1,-1), "100"), "") } // Null default_param { local error_caught = false try { ASSERT_EQUAL(command_x(tool_change_city_size).work(pl, coord3d(1, 1, 0), null), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // Empty default_param { local error_caught = false try { ASSERT_EQUAL(command_x(tool_change_city_size).work(pl, coord3d(1, 1, 0), ""), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // Wrong default_param { local error_caught = false try { ASSERT_EQUAL(command_x(tool_change_city_size).work(pl, coord3d(1, 1, 0), "bla"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(1, 1, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(1, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } // Exercises the stadt_t::~stadt_t() multi-tile-building path: build a // city, grow it until check_bau_townhall picks a multi-tile townhall // (pak64 lands on 04_CITY 2x2 around bev=20000), tear down every // non-townhall building first to keep the destructor's iteration // focused on the multi-tile townhall, then remove the townhall. // Without the destructor fix in this commit, the cascade through // gebaeude_t::cleanup -> stadt_t::remove_gebaeude_from_stadt asserts // when it walks back to the already-popped head tile. function test_city_remove_with_multitile_townhall() { local pl = player_x(1) local townhall_pos = coord3d(8, 8, 0) ASSERT_EQUAL(command_x(tool_add_city).work(pl, townhall_pos, "0"), null) ASSERT_EQUAL(command_x(tool_change_city_size).work(player_x(0), townhall_pos, "20000"), null) // Tear down every non-townhall building. Capture the townhall's // position on the way through. local size = world.get_size() local th_corner = null for (local x = 0; x < size.x; x++) { for (local y = 0; y < size.y; y++) { local b = tile_x(x, y, 0).find_object(mo_building) if (b == null) continue if (b.is_townhall()) { if (th_corner == null) th_corner = coord3d(x, y, 0) continue } command_x(tool_remover).work(pl, coord3d(x, y, 0)) } } // Fail loudly if the pakset never upgraded the townhall to multi- // tile; otherwise the test would silently pass without exercising // the destructor path we care about. local th_size = tile_x(th_corner.x, th_corner.y, 0).find_object(mo_building).get_desc().get_size(0) ASSERT_TRUE(th_size.x > 1 || th_size.y > 1) ASSERT_EQUAL(command_x(tool_remover).work(pl, th_corner), null) // ~stadt_t() tears down everything in stadt::buildings (the // townhall, plus monuments/attractions added via // add_gebaeude_to_stadt) but not the auto-generated road network; // sweep what's left so RESET_ALL_PLAYER_FUNDS sees zero // maintenance. for (local x = 0; x < size.x; x++) { for (local y = 0; y < size.y; y++) { local tile = tile_x(x, y, 0) if (tile.get_way(wt_road) != null) { local p = coord3d(x, y, 0) command_x(tool_remove_way).work(pl, p, p, "" + wt_road) } } } RESET_ALL_PLAYER_FUNDS() } function test_city_change_size_to_minimum() { ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(1, 1, 0)), null) ASSERT_EQUAL(city_x(1, 1).get_citizens()[0], 126) { ASSERT_EQUAL(command_x(tool_change_city_size).work(player_x(0), coord3d(1, 1, 0), "-100"), null) ASSERT_EQUAL(city_x(1, 1).get_citizens()[0], 126) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(1, 1, 0)), null) // street ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(0, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(1, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(2, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_climate.nut000066400000000000000000000070711520675460500213020ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // test tools for setting climate // function test_climate_invalid() { local pl = player_x(0) local setclimate = command_x(tool_set_climate) local pos = coord3d(3, 3, 0) ASSERT_EQUAL(setclimate.work(pl, pos, pos, null), "") ASSERT_EQUAL(setclimate.work(pl, pos, pos, ""), "") ASSERT_EQUAL(setclimate.work(pl, pos, pos, ","), "") ASSERT_EQUAL(setclimate.work(pl, pos, pos, "42"), "") ASSERT_EQUAL(setclimate.work(pl, coord3d(-1, -1, 0), coord3d(-1, -1, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.change_climate_at(pl, pos, -1), "Invalid climate number provided") } function test_climate_flat() { local pl = player_x(0) local pos = coord3d(3, 2, 0) local setclimate = command_x(tool_set_climate) local climates = [ cl_water, cl_desert, cl_tropic, cl_mediterran, cl_temperate, cl_tundra, cl_rocky, cl_arctic ] // single tile { foreach (cl in climates) { ASSERT_EQUAL(command_x.change_climate_at(pl, pos, cl), null) ASSERT_EQUAL(square_x(pos.x, pos.y).get_climate(), cl) } } // multi-tile { // not implemented in change_climate_at // as multi-tile climate change is forbidden in multiplayer games foreach (cl in climates) { ASSERT_EQUAL(setclimate.work(pl, pos, pos + coord3d(2, 2, 0), "" + cl), null) ASSERT_EQUAL(square_x(pos.x, pos.y).get_climate(), cl) } } // multi-tile inverted { foreach (cl in climates) { ASSERT_EQUAL(setclimate.work(pl, pos, pos - coord3d(2, 2, 0), "" + cl), null) ASSERT_EQUAL(square_x(pos.x, pos.y).get_climate(), cl) } } // invalid second pos, same as single-tile setclimate { foreach (cl in climates) { ASSERT_EQUAL(setclimate.work(pl, pos, coord3d(-1, -1, -1), "" + cl), null) ASSERT_EQUAL(square_x(pos.x, pos.y).get_climate(), cl) } } ASSERT_EQUAL(command_x.change_climate_at(pl, pos, cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_climate_cliff() { local pl = player_x(0) local setslope = command_x.set_slope local pos = coord3d(3, 2, 0) local setclimate = command_x(tool_set_climate) local climates = [ cl_water, cl_desert, cl_tropic, cl_mediterran, cl_temperate, cl_tundra, cl_rocky, cl_arctic ] for (local h = 0; h < 4; ++h) { for (local y = 0; y < 3; ++y) { for (local x = 0; x < 3; ++x) { ASSERT_EQUAL(setslope(pl, pos + coord3d(x, y, h), slope.all_up_slope), null) } } } RESET_ALL_PLAYER_FUNDS() // set climate not next to cliff { foreach (cl in climates) { ASSERT_EQUAL(setclimate.work(pl, pos + coord3d(1, 1, 4), pos + coord3d(1, 1, 4), "" + cl), null) } ASSERT_EQUAL(setclimate.work(pl, pos + coord3d(1, 1, 4), pos + coord3d(1, 1, 4), "" + cl_mediterran), null) } // set climate next to cliff { foreach (cl in climates) { ASSERT_EQUAL(setclimate.work(pl, pos + coord3d(0, 0, 4), pos + coord3d(2, 2, 4), "" + cl), null) if (cl == cl_water) { // cannot build water next to cliff edge for (local y = 0; y < 3; ++y) { for (local x = 0; x < 3; ++x) { ASSERT_EQUAL(tile_x(pos.x + x, pos.y + y, 4).is_water(), x==1 && y ==1) } } } } ASSERT_EQUAL(setclimate.work(pl, pos + coord3d(0, 0, 4), pos + coord3d(2, 2, 4), "" + cl_mediterran), null) } // clean up RESET_ALL_PLAYER_FUNDS() for (local h = 4; h > 0; --h) { for (local y = 0; y < 3; ++y) { for (local x = 0; x < 3; ++x) { ASSERT_EQUAL(setslope(pl, pos + coord3d(x, y, h), slope.all_down_slope), null) } } } ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(15, 15, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_depot.nut000066400000000000000000000523371520675460500210040ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for depots // function test_depot_build_invalid_params() { local pl = player_x(0) local public_pl = player_x(1) // invalid default_param { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_depot).work(pl, coord3d(0, 0, 0), null), "") } catch (e) { error_caught = true; ASSERT_EQUAL(e, "Error during initializing tool") } local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_depot).work(pl, coord3d(0, 0, 0), ""), "") } catch (e) { error_caught = true; ASSERT_EQUAL(e, "Error during initializing tool") } local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_depot).work(pl, coord3d(0, 0, 0), "nonexistent"), "") } catch (e) { error_caught = true; ASSERT_EQUAL(e, "Error during initializing tool") } } // build as public player { local error_caught = false try { command_x.build_depot(public_pl, coord3d(0, 0, 0), get_depot_by_wt(wt_road)) } catch (e) { error_caught = true; ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_road).len(), 0) } RESET_ALL_PLAYER_FUNDS() } function test_depot_build_invalid_pos() { local pl = player_x(0) local public_pl = player_x(1) local road_depot = get_depot_by_wt(wt_road) local hangar = get_depot_by_wt(wt_air) local shipyard = get_depot_by_wt(wt_water) local runway_desc = way_desc_x.get_available_ways(wt_air, st_elevated)[0] local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] local road_descs = way_desc_x.get_available_ways(wt_road, st_flat) local rail_descs = way_desc_x.get_available_ways(wt_rail, st_flat) road_descs.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) rail_descs.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) local road_desc = road_descs[0] local rail_desc = rail_descs[0] ASSERT_TRUE(runway_desc != null) ASSERT_TRUE(road_desc != null) ASSERT_TRUE(rail_desc != null) // invalid pos { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(-1, -1, 0), road_depot), "") } // no shipyards on land { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), shipyard), "Ship depots must be built on water!") } // no land depots on water { ASSERT_EQUAL(command_x(tool_set_climate).work(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), hangar), "Depots must be built on flat dead-end way tiles!") ASSERT_EQUAL(command_x(tool_set_climate).work(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) } // no hangars on runways { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 5, 0), coord3d(5, 7, 0), runway_desc, true), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 5, 0), hangar), "Depots cannot be built on runways!") ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 5, 0), coord3d(5, 7, 0), "" + wt_air), null) } ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) // no depot in the middle of a road { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 3, 0), road_depot), "Depots must be built on flat dead-end way tiles!") } // no depot over a stop { ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 2, 0), station_desc, 0), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), road_depot), "Tile not empty.") ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) } // do not replace existing depots { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), road_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), road_depot), "Tile not empty.") ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) } ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), road_desc, true), null) // no depot on road-road crossings { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 3, 0), road_depot), "Depots must be built on flat dead-end way tiles!") } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), rail_desc, true), null) // no depot on road-rail crossings { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 3, 0), road_depot), "Tile not empty.") } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_rail), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_build_road() { local pl = player_x(0) local public_pl = player_x(1) local default_depot = get_depot_by_wt(wt_road) local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] // preconditions ASSERT_TRUE(default_depot != null) ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_road).len(), 0) ASSERT_TRUE(way_desc != null) ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), way_desc, true), null) // build depot as normal player, all 4 directions { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), default_depot), null) ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_road).len(), 4) ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) ASSERT_TRUE(depot_x(5, 3, 0).is_valid()) ASSERT_TRUE(depot_x(4, 4, 0).is_valid()) ASSERT_TRUE(depot_x(3, 3, 0).is_valid()) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_build_road_on_tram_crossing() { local pl = player_x(0) local public_pl = player_x(1) local default_depot = get_depot_by_wt(wt_road) local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local tramtrack = way_desc_x.get_available_ways(wt_rail, st_tram)[0] ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 2, 0), coord3d(4, 2, 0), tramtrack, true), null) { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), "Tile not empty.") } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 2, 0), coord3d(4, 2, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_build_water() { local pl = player_x(0) local public_pl = player_x(1) local default_depot = get_depot_by_wt(wt_water) // preconditions ASSERT_TRUE(default_depot != null) ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_water).len(), 0) ASSERT_EQUAL(command_x(tool_set_climate).work(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) // build depot as normal player { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), null) ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_water).len(), 1) ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_set_climate).work(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_build_monorail() { local pl = player_x(0) local public_pl = player_x(1) local default_depot = get_depot_by_wt(wt_monorail) local way_desc = way_desc_x.get_available_ways(wt_monorail, st_flat)[0] local elevated_monorail = way_desc_x.get_available_ways(wt_monorail, st_elevated)[0] // preconditions ASSERT_TRUE(default_depot != null) ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_monorail).len(), 0) ASSERT_TRUE(way_desc != null) ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), way_desc, true), null) // build depot as normal player, all 4 directions { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), default_depot), null) ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_monorail).len(), 4) ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) ASSERT_TRUE(depot_x(5, 3, 0).is_valid()) ASSERT_TRUE(depot_x(4, 4, 0).is_valid()) ASSERT_TRUE(depot_x(3, 3, 0).is_valid()) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_monorail), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_monorail), null) ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), elevated_monorail, true), null) ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), elevated_monorail, true), null) // build elevated monorail depot, with foundation, all 4 directions { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 1), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 1), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 1), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 1), default_depot), null) ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_monorail).len(), 4) ASSERT_TRUE(depot_x(4, 2, 1).is_valid()) ASSERT_TRUE(depot_x(5, 3, 1).is_valid()) ASSERT_TRUE(depot_x(4, 4, 1).is_valid()) ASSERT_TRUE(depot_x(3, 3, 1).is_valid()) ASSERT_FALSE(square_x(4, 2).get_tile_at_height(0).is_empty()) ASSERT_FALSE(square_x(5, 3).get_tile_at_height(0).is_empty()) ASSERT_FALSE(square_x(4, 4).get_tile_at_height(0).is_empty()) ASSERT_FALSE(square_x(3, 3).get_tile_at_height(0).is_empty()) // This leaves behind the depot foundations ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 1)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 1)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 1)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 1)), null) } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 1), coord3d(4, 4, 1), "" + wt_monorail), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 1), coord3d(5, 3, 1), "" + wt_monorail), null) ASSERT_FALSE(square_x(4, 2).get_tile_at_height(0).is_empty()) ASSERT_FALSE(square_x(5, 3).get_tile_at_height(0).is_empty()) ASSERT_FALSE(square_x(4, 4).get_tile_at_height(0).is_empty()) ASSERT_FALSE(square_x(3, 3).get_tile_at_height(0).is_empty()) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) // clean up RESET_ALL_PLAYER_FUNDS() } function test_depot_build_tram() { local pl = player_x(0) local public_pl = player_x(1) local default_depot = get_depot_by_wt(wt_tram) local way_desc = way_desc_x.get_available_ways(wt_rail, st_tram)[0] // preconditions ASSERT_TRUE(default_depot != null) ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_tram).len(), 0) ASSERT_TRUE(way_desc != null) ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), way_desc, true), null) // build depot as normal player, all 4 directions { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 0), default_depot), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), default_depot), null) ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_tram).len(), 4) ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) ASSERT_TRUE(depot_x(5, 3, 0).is_valid()) ASSERT_TRUE(depot_x(4, 4, 0).is_valid()) ASSERT_TRUE(depot_x(3, 3, 0).is_valid()) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) // note: wt_tram does not work ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_build_sloped() { local pl = player_x(0) local setslope = command_x.set_slope local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local wayremover = command_x(tool_remove_way) local pos = coord3d(4, 3, 0) { for (local sl = slope.flat+1; sl < slope.raised; ++sl) { ASSERT_EQUAL(setslope(pl, pos, sl), null) local d = slope.to_dir(sl) if (d != dir.none) { // only consider slopes we can build roads on RESET_ALL_PLAYER_FUNDS() local c = dir.to_coord(dir.backward(d)) local adjacent = pos + coord3d(c.x, c.y, 0) ASSERT_EQUAL(command_x.build_way(pl, adjacent, pos, road, true), null) local old_maintenance = pl.get_current_maintenance() ASSERT_EQUAL(command_x.build_depot(pl, pos, get_depot_by_wt(wt_road)), "Depots must be built on flat dead-end way tiles!") ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) ASSERT_EQUAL(tile_x(pos.x, pos.y, pos.z).find_object(mo_building), null) ASSERT_EQUAL(wayremover.work(pl, pos, adjacent, "" + wt_road), null) } } } // clean up ASSERT_EQUAL(setslope(pl, pos, slope.flat), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_build_on_tunnel_entrance() { local pl = player_x(0) local rail_tunnel = tunnel_desc_x.get_available_tunnels(wt_rail)[0] ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(4, 1, 0), rail_tunnel.get_name()), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(3, 2, 0), rail_tunnel.get_name()), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(5, 2, 0), rail_tunnel.get_name()), null) // Building depots on tunnel entrance works (contrary to stations) { for (local d = dir.north; d < dir.all; d = d*2) { local p = coord3d(4, 2, 0) + dir.to_coord(d) ASSERT_EQUAL(command_x.build_depot(pl, p, get_depot_by_wt(wt_rail)), null) } } local remover = command_x(tool_remover) remover.set_flags(2) ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0)), null) ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0)), null) ASSERT_EQUAL(remover.work(pl, coord3d(5, 2, 0)), null) ASSERT_EQUAL(remover.work(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 3, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_build_on_bridge_end() { local pl = player_x(0) local rail_bridge = bridge_desc_x.get_available_bridges(wt_rail)[0] local setslope = command_x.set_slope // north-south direction { ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.north), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(4, 2, 0), rail_bridge), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), get_depot_by_wt(wt_rail)), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 0), get_depot_by_wt(wt_rail)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.flat), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.flat), null) } // east-west direction { ASSERT_EQUAL(setslope(pl, coord3d(3, 3, 0), slope.east), null) ASSERT_EQUAL(setslope(pl, coord3d(5, 3, 0), slope.west), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 3, 0), rail_bridge), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), get_depot_by_wt(wt_rail)), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), get_depot_by_wt(wt_rail)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 3, 0), slope.flat), null) ASSERT_EQUAL(setslope(pl, coord3d(5, 3, 0), slope.flat), null) } RESET_ALL_PLAYER_FUNDS() } function test_depot_build_on_halt() { local pl = player_x(0) local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] local depot = get_depot_by_wt(wt_rail) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), rail_desc, true), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 2, 0), station_desc), null) { ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), depot), "Tile not empty.") } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_convoy_add_normal() { // build depot local pl = player_x(0) local public_pl = player_x(1) local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(0, 0, 0), coord3d(0, 1, 0), way_desc, true), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(0, 0, 0), get_depot_by_wt(wt_road)), null) ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_road).len(), 1) local the_depot = depot_x.get_depot_list(pl, wt_road)[0] ASSERT_EQUAL(the_depot.get_convoy_list().len(), 0) local vehicle_desc = vehicle_desc_x.get_available_vehicles(wt_road)[0] ASSERT_TRUE(vehicle_desc != null) { ASSERT_TRUE(the_depot.append_vehicle(pl, convoy_x(0), vehicle_desc)) ASSERT_EQUAL(the_depot.get_convoy_list().len(), 1) local cnv = the_depot.get_convoy_list()[0] ASSERT_EQUAL(cnv.get_vehicles().len(), 1) ASSERT_EQUAL(cnv.get_pos().tostring(), coord3d(0, 0, 0).tostring()) ASSERT_EQUAL(cnv.get_pos().tostring(), cnv.get_home_depot().tostring()) ASSERT_EQUAL(cnv.get_speed(), 0) ASSERT_EQUAL(cnv.get_traveled_distance()[0], 0) ASSERT_EQUAL(cnv.get_distance_traveled_total(), 0) ASSERT_TRUE(cnv.is_in_depot()) ASSERT_FALSE(cnv.is_waiting()) ASSERT_EQUAL(cnv.get_line(), null) ASSERT_TRUE(cnv.destroy(player_x(0))) ASSERT_EQUAL(the_depot.get_convoy_list().len(), 0) } // clean up local remover = command_x(tool_remover) ASSERT_EQUAL(remover.work(player_x(1), tile_x(0, 0, 0)), null) ASSERT_EQUAL(remover.work(player_x(1), tile_x(0, 0, 0)), null) ASSERT_EQUAL(remover.work(player_x(1), tile_x(0, 1, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_depot_convoy_add_nonelectrified() { local pl = player_x(0) local way_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local electro_loco = vehicle_desc_x.get_available_vehicles(wt_rail).filter(@(idx, v) ( v.needs_electrification() && v.can_be_first() ))[0] ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), get_depot_by_wt(wt_rail)), null) local the_depot = depot_x(4, 2, 0) { ASSERT_TRUE(the_depot.append_vehicle(pl, convoy_x(0), electro_loco)) // can add loco via script even if depot is not electrified ASSERT_EQUAL(the_depot.get_convoy_list().len(), 1) local cnv = the_depot.get_convoy_list()[0] ASSERT_EQUAL(cnv.get_vehicles().len(), 1) ASSERT_EQUAL(cnv.get_pos().tostring(), coord3d(4, 2, 0).tostring()) ASSERT_TRUE(cnv.destroy(pl)) } ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_dir.nut000066400000000000000000000201071520675460500204350ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for dir/coord // function test_dir_is_single() { ASSERT_FALSE(dir.is_single(dir.none)) ASSERT_TRUE(dir.is_single(dir.north)) ASSERT_TRUE(dir.is_single(dir.east)) ASSERT_TRUE(dir.is_single(dir.south)) ASSERT_TRUE(dir.is_single(dir.west)) ASSERT_FALSE(dir.is_single(dir.northeast)) ASSERT_FALSE(dir.is_single(dir.northsouth)) ASSERT_FALSE(dir.is_single(dir.northwest)) ASSERT_FALSE(dir.is_single(dir.southeast)) ASSERT_FALSE(dir.is_single(dir.eastwest)) ASSERT_FALSE(dir.is_single(dir.southwest)) ASSERT_FALSE(dir.is_single(dir.northsoutheast)) ASSERT_FALSE(dir.is_single(dir.northeastwest)) ASSERT_FALSE(dir.is_single(dir.northsouthwest)) ASSERT_FALSE(dir.is_single(dir.southeastwest)) ASSERT_FALSE(dir.is_single(dir.all)) } function test_dir_is_twoway() { ASSERT_FALSE(dir.is_twoway(dir.none)) ASSERT_FALSE(dir.is_twoway(dir.north)) ASSERT_FALSE(dir.is_twoway(dir.east)) ASSERT_FALSE(dir.is_twoway(dir.south)) ASSERT_FALSE(dir.is_twoway(dir.west)) ASSERT_TRUE(dir.is_twoway(dir.northeast)) ASSERT_TRUE(dir.is_twoway(dir.northsouth)) ASSERT_TRUE(dir.is_twoway(dir.northwest)) ASSERT_TRUE(dir.is_twoway(dir.southeast)) ASSERT_TRUE(dir.is_twoway(dir.eastwest)) ASSERT_TRUE(dir.is_twoway(dir.southwest)) ASSERT_FALSE(dir.is_twoway(dir.northsoutheast)) ASSERT_FALSE(dir.is_twoway(dir.northeastwest)) ASSERT_FALSE(dir.is_twoway(dir.northsouthwest)) ASSERT_FALSE(dir.is_twoway(dir.southeastwest)) ASSERT_FALSE(dir.is_twoway(dir.all)) } function test_dir_is_threeway() { ASSERT_FALSE(dir.is_threeway(dir.none)) ASSERT_FALSE(dir.is_threeway(dir.north)) ASSERT_FALSE(dir.is_threeway(dir.east)) ASSERT_FALSE(dir.is_threeway(dir.south)) ASSERT_FALSE(dir.is_threeway(dir.west)) ASSERT_FALSE(dir.is_threeway(dir.northeast)) ASSERT_FALSE(dir.is_threeway(dir.northsouth)) ASSERT_FALSE(dir.is_threeway(dir.northwest)) ASSERT_FALSE(dir.is_threeway(dir.southeast)) ASSERT_FALSE(dir.is_threeway(dir.eastwest)) ASSERT_FALSE(dir.is_threeway(dir.southwest)) ASSERT_TRUE(dir.is_threeway(dir.northsoutheast)) ASSERT_TRUE(dir.is_threeway(dir.northeastwest)) ASSERT_TRUE(dir.is_threeway(dir.northsouthwest)) ASSERT_TRUE(dir.is_threeway(dir.southeastwest)) ASSERT_TRUE(dir.is_threeway(dir.all)) } function test_dir_is_curve() { ASSERT_FALSE(dir.is_curve(dir.none)) ASSERT_FALSE(dir.is_curve(dir.north)) ASSERT_FALSE(dir.is_curve(dir.east)) ASSERT_FALSE(dir.is_curve(dir.south)) ASSERT_FALSE(dir.is_curve(dir.west)) ASSERT_TRUE(dir.is_curve(dir.northeast)) ASSERT_FALSE(dir.is_curve(dir.northsouth)) ASSERT_TRUE(dir.is_curve(dir.northwest)) ASSERT_TRUE(dir.is_curve(dir.southeast)) ASSERT_FALSE(dir.is_curve(dir.eastwest)) ASSERT_TRUE(dir.is_curve(dir.southwest)) ASSERT_FALSE(dir.is_curve(dir.northsoutheast)) ASSERT_FALSE(dir.is_curve(dir.northeastwest)) ASSERT_FALSE(dir.is_curve(dir.northsouthwest)) ASSERT_FALSE(dir.is_curve(dir.southeastwest)) ASSERT_FALSE(dir.is_curve(dir.all)) } function test_dir_is_straight() { ASSERT_FALSE(dir.is_straight(dir.none)) ASSERT_TRUE(dir.is_straight(dir.north)) ASSERT_TRUE(dir.is_straight(dir.east)) ASSERT_TRUE(dir.is_straight(dir.south)) ASSERT_TRUE(dir.is_straight(dir.west)) ASSERT_FALSE(dir.is_straight(dir.northeast)) ASSERT_TRUE(dir.is_straight(dir.northsouth)) ASSERT_FALSE(dir.is_straight(dir.northwest)) ASSERT_FALSE(dir.is_straight(dir.southeast)) ASSERT_TRUE(dir.is_straight(dir.eastwest)) ASSERT_FALSE(dir.is_straight(dir.southwest)) ASSERT_FALSE(dir.is_straight(dir.northsoutheast)) ASSERT_FALSE(dir.is_straight(dir.northeastwest)) ASSERT_FALSE(dir.is_straight(dir.northsouthwest)) ASSERT_FALSE(dir.is_straight(dir.southeastwest)) ASSERT_FALSE(dir.is_straight(dir.all)) } function test_dir_double() { ASSERT_EQUAL(dir.double(dir.none), dir.none) ASSERT_EQUAL(dir.double(dir.north), dir.northsouth) ASSERT_EQUAL(dir.double(dir.east), dir.eastwest) ASSERT_EQUAL(dir.double(dir.south), dir.northsouth) ASSERT_EQUAL(dir.double(dir.west), dir.eastwest) ASSERT_EQUAL(dir.double(dir.northeast), dir.none) ASSERT_EQUAL(dir.double(dir.northsouth), dir.northsouth) ASSERT_EQUAL(dir.double(dir.northwest), dir.none) ASSERT_EQUAL(dir.double(dir.southeast), dir.none) ASSERT_EQUAL(dir.double(dir.eastwest), dir.eastwest) ASSERT_EQUAL(dir.double(dir.southwest), dir.none) ASSERT_EQUAL(dir.double(dir.northsoutheast), dir.none) ASSERT_EQUAL(dir.double(dir.northeastwest), dir.none) ASSERT_EQUAL(dir.double(dir.northsouthwest), dir.none) ASSERT_EQUAL(dir.double(dir.southeastwest), dir.none) ASSERT_EQUAL(dir.double(dir.all), dir.none) } function test_dir_backward() { ASSERT_EQUAL(dir.backward(dir.none), dir.all) ASSERT_EQUAL(dir.backward(dir.north), dir.south) ASSERT_EQUAL(dir.backward(dir.east), dir.west) ASSERT_EQUAL(dir.backward(dir.south), dir.north) ASSERT_EQUAL(dir.backward(dir.west), dir.east) ASSERT_EQUAL(dir.backward(dir.northeast), dir.southwest) ASSERT_EQUAL(dir.backward(dir.northsouth), dir.northsouth) ASSERT_EQUAL(dir.backward(dir.northwest), dir.southeast) ASSERT_EQUAL(dir.backward(dir.southeast), dir.northwest) ASSERT_EQUAL(dir.backward(dir.eastwest), dir.eastwest) ASSERT_EQUAL(dir.backward(dir.southwest), dir.northeast) ASSERT_EQUAL(dir.backward(dir.northsoutheast), dir.west) ASSERT_EQUAL(dir.backward(dir.northeastwest), dir.south) ASSERT_EQUAL(dir.backward(dir.northsouthwest), dir.east) ASSERT_EQUAL(dir.backward(dir.southeastwest), dir.north) ASSERT_EQUAL(dir.backward(dir.all), dir.none) } function test_dir_to_slope() { ASSERT_EQUAL(dir.to_slope(dir.none), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.north), slope.south) ASSERT_EQUAL(dir.to_slope(dir.east), slope.west) ASSERT_EQUAL(dir.to_slope(dir.south), slope.north) ASSERT_EQUAL(dir.to_slope(dir.west), slope.east) ASSERT_EQUAL(dir.to_slope(dir.northeast), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.northsouth), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.northwest), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.southeast), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.eastwest), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.southwest), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.northsouthwest), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.northeastwest), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.northsouthwest), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.southeastwest), slope.flat) ASSERT_EQUAL(dir.to_slope(dir.all), slope.flat) local error_raised = false try { // out-of-range dir: ANDed with dir.all ASSERT_EQUAL(dir.to_slope(0x2A), dir.to_slope(0x0A)) } catch (e) { error_raised = true; } ASSERT_FALSE(error_raised) } function test_dir_to_coord() { ASSERT_EQUAL(dir.to_coord(dir.none).tostring(), coord( 0, 0).tostring()) ASSERT_EQUAL(dir.to_coord(dir.north).tostring(), coord( 0, -1).tostring()) ASSERT_EQUAL(dir.to_coord(dir.east).tostring(), coord( 1, 0).tostring()) ASSERT_EQUAL(dir.to_coord(dir.south).tostring(), coord( 0, 1).tostring()) ASSERT_EQUAL(dir.to_coord(dir.west).tostring(), coord(-1, 0).tostring()) ASSERT_EQUAL(dir.to_coord(dir.northeast).tostring(), coord( 1, -1).tostring()) ASSERT_EQUAL(dir.to_coord(dir.northsouth).tostring(), coord( 0, 0).tostring()) ASSERT_EQUAL(dir.to_coord(dir.southeast).tostring(), coord( 1, 1).tostring()) ASSERT_EQUAL(dir.to_coord(dir.northwest).tostring(), coord(-1, -1).tostring()) ASSERT_EQUAL(dir.to_coord(dir.eastwest).tostring(), coord( 0, 0).tostring()) ASSERT_EQUAL(dir.to_coord(dir.southwest).tostring(), coord(-1, 1).tostring()) ASSERT_EQUAL(dir.to_coord(dir.northsouthwest).tostring(), coord(-1, 0).tostring()) ASSERT_EQUAL(dir.to_coord(dir.southeastwest).tostring(), coord( 0, 1).tostring()) ASSERT_EQUAL(dir.to_coord(dir.northsoutheast).tostring(), coord( 1, 0).tostring()) ASSERT_EQUAL(dir.to_coord(dir.northeastwest).tostring(), coord( 0, -1).tostring()) ASSERT_EQUAL(dir.to_coord(dir.all).tostring(), coord( 0, 0).tostring()) local error_raised = false try { dir.to_coord(42) // out-of-range dir } catch (e) { ASSERT_EQUAL(e, "Invalid dir 42 (valid values are 0..15)") error_raised = true } ASSERT_TRUE(error_raised) } simutrans-124.5/tests/tests/test_factory.nut000066400000000000000000000316701520675460500213350ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for factories / factory links // function build_factory(pl, pos, ignore_climates, rotation, base_prod, name) { local factory_builder = command_x(tool_build_factory) return factory_builder.work(pl, pos, "" + ignore_climates.tointeger() + rotation + base_prod + "," + name) } function test_factory_build_pp() { local pl = player_x(0) local public_pl = player_x(1) { // FIXME Causes signed integer overflow even though < 2^30 // local production = 1024 * 1000 * 1000 local production = 1024 // 1/s ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), 0, 1, production, "Aufwindkraftwerk"), null) local factory = factory_x(3, 4) ASSERT_TRUE(factory_x(5, 6) != null) ASSERT_EQUAL(factory_list_x().get_count(), 1) local desc = factory.get_desc(); ASSERT_TRUE(desc != null) ASSERT_EQUAL(desc.get_name(), "Aufwindkraftwerk") // FIXME Max electricity production for PPs not queryable via script? ASSERT_EQUAL(factory.get_consumers().len(), 0) ASSERT_EQUAL(factory.get_suppliers().len(), 0) ASSERT_EQUAL(factory.get_production()[0], 0) ASSERT_EQUAL(factory.get_power()[0], 0) ASSERT_EQUAL(factory.get_boost_electric()[0], 0) ASSERT_EQUAL(factory.get_boost_pax()[0], 0) ASSERT_EQUAL(factory.get_boost_mail()[0], 0) ASSERT_EQUAL(factory.get_pax_generated()[0], 0) ASSERT_EQUAL(factory.get_pax_departed()[0], 0) ASSERT_EQUAL(factory.get_pax_arrived()[0], 0) ASSERT_EQUAL(factory.get_mail_generated()[0], 0) ASSERT_EQUAL(factory.get_tile_list().len(), 3 * 3) ASSERT_EQUAL(factory.get_halt_list().len(), 0) ASSERT_FALSE(factory.is_transformer_connected()) ASSERT_EQUAL(factory.get_transformer(), null) ASSERT_EQUAL(factory.get_field_count(), 0) ASSERT_EQUAL(factory.get_min_field_count(), 0) ASSERT_TRUE(factory.input.len() == 0) ASSERT_TRUE(factory.output.len() == 0) } { // and remove factory; only public player can remove them ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 4, 0)), "Der Besitzer erlaubt das Entfernen nicht") ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) } RESET_ALL_PLAYER_FUNDS() } function test_factory_build_climate() { local pl = player_x(0) local public_pl = player_x(1) local setclimate = command_x(tool_set_climate) local allowed_climates = [ cl_temperate, cl_tundra, cl_rocky, cl_arctic ] local all_climates = [ cl_water, cl_desert, cl_tropic, cl_mediterran, cl_temperate, cl_tundra, cl_rocky, cl_arctic ] { foreach (cl in all_climates) { ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl), null) local err = build_factory(pl, coord3d(3, 4, 0), false /*don't ignore climates*/, 1, 1024, "Kohlegrube") if (allowed_climates.find(cl) != null) { ASSERT_EQUAL(err, null) ASSERT_EQUAL(factory_list_x().get_count(), 1) // and remove it ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) } else { ASSERT_EQUAL(err, "No suitable ground!") ASSERT_EQUAL(factory_list_x().get_count(), 0) } RESET_ALL_PLAYER_FUNDS() } } // and ignore climates { foreach (cl in all_climates) { ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl), null) local err = build_factory(pl, coord3d(3, 4, 0), 1 /*ignore climates*/, 1, 1024, "Kohlegrube") if (cl != cl_water) { ASSERT_EQUAL(err, null) ASSERT_EQUAL(factory_list_x().get_count(), 1) // and remove it ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) } else { ASSERT_EQUAL(err, "No suitable ground!") ASSERT_EQUAL(factory_list_x().get_count(), 0) } RESET_ALL_PLAYER_FUNDS() } ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } // partially on disallowed climate { ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(3, 7, 0), "" + cl_temperate), null) ASSERT_EQUAL(setclimate.work(pl, coord3d(4, 0, 0), coord3d(7, 7, 0), "" + cl_mediterran), null) ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), false /*don't ignore climates*/, 1, 1024, "Kohlegrube"), "No suitable ground!") ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), true /*ignore climates*/, 1, 1024, "Kohlegrube"), null) ASSERT_EQUAL(factory_list_x().get_count(), 1) // and remove it ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) } // clean up ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_factory_build_on_water_occupied() { local dims = factory_desc_x("Oelbohrinsel").building_desc.get_size(0); ASSERT_TRUE(dims.x > 1) ASSERT_TRUE(dims.y > 1) // make water ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(1), coord3d(0,0,0), coord3d(8,8,0), "" + cl_water), null); // make factory ASSERT_EQUAL(build_factory(player_x(1), coord3d(4,4,0), 1, 1, 1000, "Oelbohrinsel"), null) { // check all 4 corners ASSERT_EQUAL(build_factory(player_x(1), coord3d(3,3,0), 1, 1, 1000, "Oelbohrinsel"), "No suitable ground!") ASSERT_EQUAL(build_factory(player_x(1), coord3d(5,3,0), 1, 1, 1000, "Oelbohrinsel"), "No suitable ground!") ASSERT_EQUAL(build_factory(player_x(1), coord3d(5,5,0), 1, 1, 1000, "Oelbohrinsel"), "No suitable ground!") ASSERT_EQUAL(build_factory(player_x(1), coord3d(3,5,0), 1, 1, 1000, "Oelbohrinsel"), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(4,4,0)), null) ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(1), coord3d(0,0,0), coord3d(8,8,0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_factory_build_with_fields() { local pl = player_x(0) local public_pl = player_x(1) local setclimate = command_x(tool_set_climate) // build normal { ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), 0, 1, 1024, "PVkraftwerk"), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) } // build in non-suitable climate { ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl_arctic), null) ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), 0, 1, 1024, "PVkraftwerk"), "No suitable ground!") } // Force building in non-suitable climate, factory should have no fields { ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), 1, 1, 1024, "PVkraftwerk"), null) for (local y = 0; y < 8; ++y) { for (local x = 0; x < 8; ++x) { ASSERT_EQUAL(tile_x(x, y, 0).find_object(mo_field), null) } } ASSERT_EQUAL(factory_list_x().get_count(), 1) // and remove it ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) } // clean up ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_factory_link() { local pl = player_x(0) local public_pl = player_x(1) local production = 1024 // 1/s // build coal mine + coal power plant ASSERT_EQUAL(build_factory(public_pl, coord3d(0, 0, 0), 1, 1, production, "Kohlegrube"), null) ASSERT_EQUAL(build_factory(public_pl, coord3d(6, 6, 0), 1, 1, production, "Kohlekraftwerk"), null) local mine = factory_x(0, 0) local pp = factory_x(6, 6) ASSERT_TRUE(mine != null) ASSERT_TRUE(pp != null) // test pre-conditions { ASSERT_EQUAL(mine.get_consumers().len(), 0) ASSERT_EQUAL(mine.get_suppliers().len(), 0) // ASSERT_EQUAL(mine.get_production()[0], 0) // FIXME this is 1024 when using the -scenario switch ASSERT_EQUAL(mine.get_power()[0], 0) ASSERT_TRUE(mine.input.len() == 0) ASSERT_TRUE(mine.output.len() == 1) local out = mine.output.Kohle ASSERT_TRUE(out != null) // ASSERT_EQUAL(out.get_storage()[0], 0) // FIXME this is 122 when using the -scenario switch ASSERT_EQUAL(out.get_received()[0], 0) ASSERT_EQUAL(out.get_consumed()[0], 0) ASSERT_EQUAL(out.get_in_transit()[0], 0) ASSERT_EQUAL(out.get_delivered()[0], 0) ASSERT_EQUAL(out.get_produced()[0], 0) ASSERT_EQUAL(out.get_base_production(), production) ASSERT_EQUAL(out.get_base_consumption(), production) ASSERT_EQUAL(out.get_production_factor(), 100) ASSERT_EQUAL(out.max_storage, 122) { local error_caught = false try { ASSERT_EQUAL(out.get_consumption_factor(), 0) } catch (e) { ASSERT_EQUAL(e, "No input slot [0] in factory at (0,0,0)") error_caught = true } ASSERT_TRUE(error_caught) } ASSERT_EQUAL(pp.get_consumers().len(), 0) ASSERT_EQUAL(pp.get_suppliers().len(), 0) ASSERT_EQUAL(pp.get_production()[0], 0) ASSERT_EQUAL(pp.get_power()[0], 0) ASSERT_TRUE(pp.input.len() == 1) ASSERT_TRUE(pp.output.len() == 0) local inp = pp.input.Kohle ASSERT_TRUE(inp != null) ASSERT_EQUAL(inp.get_storage()[0], 0) ASSERT_EQUAL(inp.get_received()[0], 0) ASSERT_EQUAL(inp.get_consumed()[0], 0) ASSERT_EQUAL(inp.get_in_transit()[0], 0) ASSERT_EQUAL(inp.get_delivered()[0], 0) ASSERT_EQUAL(inp.get_produced()[0], 0) ASSERT_EQUAL(inp.get_base_production(), production) ASSERT_EQUAL(inp.get_base_consumption(), production) ASSERT_EQUAL(inp.get_consumption_factor(), 100) ASSERT_EQUAL(inp.max_storage, 1153) { local error_caught = false try { ASSERT_EQUAL(inp.get_production_factor(), 0) } catch (e) { ASSERT_EQUAL(e, "No output slot [-1] in factory at (6,6,0)") error_caught = true } ASSERT_TRUE(error_caught) } } // link first factory with nothing - should fail { ASSERT_EQUAL(command_x(tool_link_factory).work(pl, coord3d(0, 0, 0), coord3d(15, 15, 0), ""), "") ASSERT_EQUAL(mine.get_consumers().len(), 0) ASSERT_EQUAL(mine.get_suppliers().len(), 0) ASSERT_EQUAL(pp.get_consumers().len(), 0) ASSERT_EQUAL(pp.get_suppliers().len(), 0) } // link factory with itself - should fail { ASSERT_EQUAL(command_x(tool_link_factory).work(pl, coord3d(0, 0, 0), coord3d(0, 0, 0), ""), "") ASSERT_EQUAL(mine.get_consumers().len(), 0) ASSERT_EQUAL(mine.get_suppliers().len(), 0) ASSERT_EQUAL(pp.get_consumers().len(), 0) ASSERT_EQUAL(pp.get_suppliers().len(), 0) } // link factories { // default_param is necessary even though it is not used ASSERT_EQUAL(command_x(tool_link_factory).work(pl, coord3d(0, 0, 0), coord3d(6, 6, 0), ""), null) ASSERT_EQUAL(mine.get_consumers().len(), 1) ASSERT_EQUAL(mine.get_suppliers().len(), 0) ASSERT_EQUAL(mine.get_consumers()[0].x, pp.x) ASSERT_EQUAL(mine.get_consumers()[0].y, pp.y) ASSERT_EQUAL(pp.get_consumers().len(), 0) ASSERT_EQUAL(pp.get_suppliers().len(), 1) ASSERT_EQUAL(pp.get_suppliers()[0].x, mine.x) ASSERT_EQUAL(pp.get_suppliers()[0].y, mine.y) } // second link should have no effect { ASSERT_EQUAL(command_x(tool_link_factory).work(pl, coord3d(0, 0, 0), coord3d(6, 6, 0), ""), null) ASSERT_EQUAL(mine.get_consumers().len(), 1) ASSERT_EQUAL(mine.get_suppliers().len(), 0) ASSERT_EQUAL(mine.get_consumers()[0].x, pp.x) ASSERT_EQUAL(mine.get_consumers()[0].y, pp.y) ASSERT_EQUAL(pp.get_consumers().len(), 0) ASSERT_EQUAL(pp.get_suppliers().len(), 1) ASSERT_EQUAL(pp.get_suppliers()[0].x, mine.x) ASSERT_EQUAL(pp.get_suppliers()[0].y, mine.y) } // unlink factories { local tool = command_x(tool_link_factory) tool.set_flags(2) // ctrl == unlink ASSERT_EQUAL(tool.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), ""), null) ASSERT_EQUAL(mine.get_consumers().len(), 0) ASSERT_EQUAL(mine.get_suppliers().len(), 0) ASSERT_EQUAL(pp.get_consumers().len(), 0) ASSERT_EQUAL(pp.get_suppliers().len(), 0) } // second unlink should have no effect { local tool = command_x(tool_link_factory) tool.set_flags(2) // ctrl == unlink ASSERT_EQUAL(tool.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), ""), null) ASSERT_EQUAL(mine.get_consumers().len(), 0) ASSERT_EQUAL(mine.get_suppliers().len(), 0) ASSERT_EQUAL(pp.get_consumers().len(), 0) ASSERT_EQUAL(pp.get_suppliers().len(), 0) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(7, 7, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_factory_desc() { local desc = factory_desc_x("Raffinerie") local inp = desc.get_inputs() local out = desc.get_outputs() ASSERT_EQUAL(desc.get_name(), "Raffinerie") ASSERT_EQUAL(desc.is_electricity_producer(), false) ASSERT_EQUAL(inp.len(), 1) ASSERT_EQUAL(out.len(), 3) { local inp_goods = [] for(local i=0; i= 3) ASSERT_TRUE(ore.get_catg_index() >= 3) } function test_good_speed_bonus() { local pax = good_desc_x.passenger local concrete = good_desc_x("Concrete") ASSERT_TRUE(concrete.calc_revenue(wt_road, 0) == concrete.calc_revenue(wt_road, 999)) // this has a speed bonus of 0 local wtypes = [ wt_road, wt_rail, wt_water, wt_monorail, wt_maglev, wt_narrowgauge ] local goods = [ pax, concrete ] foreach (g in goods) { foreach (wt in wtypes) { local last = 0 for (local speed = 0; speed <= 999; ++speed) { local current = g.calc_revenue(wt, speed) ASSERT_TRUE(current >= last) last = current } } } } simutrans-124.5/tests/tests/test_groundobj.nut000066400000000000000000000123571520675460500216600ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for groundobj/movingobj // function test_groundobj_build_invalid_param() { // not long enough { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(0, 0, 0), "0"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(0, 0, 0), "0a"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(0, 0, 0), "0ab"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // invalid default_param: first char not '0' or '1' { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(0, 0, 0), "afoo"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_groundobj_build_invalid_pos() { // invalid pos { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(-1, -1, 0), ""), null) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_groundobj_build_random() { { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), null), null) local go = tile_x(4, 2, 0).find_object(mo_groundobj) ASSERT_TRUE(go != null) ASSERT_EQUAL(go.type, mo_groundobj) ASSERT_EQUAL(go.owner.nr, 16) ASSERT_EQUAL(go.is_removable(player_x(0)), null) } { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(2, 4, 0), ""), null) local go = tile_x(2, 4, 0).find_object(mo_groundobj) ASSERT_TRUE(go != null) ASSERT_EQUAL(go.type, mo_groundobj) ASSERT_EQUAL(go.owner.nr, 16) ASSERT_EQUAL(go.is_removable(player_x(0)), null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(2, 4, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_groundobj_build_specific() { { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), "0__See1"), null) local go = tile_x(4, 2, 0).find_object(mo_groundobj) ASSERT_TRUE(go != null) ASSERT_EQUAL(go.type, mo_groundobj) ASSERT_EQUAL(go.owner.nr, 16) ASSERT_EQUAL(go.is_removable(player_x(0)), null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_groundobj_build_invalid_climate() { { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), "0__Hochsitz1"), "") ASSERT_EQUAL(tile_x(4, 2, 0).find_object(mo_groundobj), null) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_groundobj_build_ignore_climate() { { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), "1__Hochsitz1"), null) local go = tile_x(4, 2, 0).find_object(mo_groundobj) ASSERT_TRUE(go != null) ASSERT_EQUAL(go.type, mo_groundobj) ASSERT_EQUAL(go.owner.nr, 16) ASSERT_EQUAL(go.is_removable(player_x(0)), null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_groundobj_build_occupied() { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), "0__See1"), null) { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), "1__Hochsitz1"), "") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_groundobj_build_on_trees() { ASSERT_EQUAL(command_x(tool_plant_tree).work(player_x(1), coord3d(4, 2, 0), "11,Ahorn-1"), null) // trees_on_top = 0 -> fail { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), "1__See1"), "") ASSERT_EQUAL(tile_x(4, 2, 0).find_object(mo_groundobj), null) } // trees_on_top = 1 -> success { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), "0__moles"), null) local go = tile_x(4, 2, 0).find_object(mo_groundobj) ASSERT_TRUE(go != null) ASSERT_EQUAL(go.type, mo_groundobj) ASSERT_EQUAL(go.owner.nr, 16) ASSERT_EQUAL(go.is_removable(player_x(0)), null) local tree = tile_x(4, 2, 0).find_object(mo_tree) ASSERT_TRUE(tree != null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_groundobj_build_on_slope() { ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 2, 0), slope.south), null) { ASSERT_EQUAL(command_x(tool_build_groundobj).work(player_x(0), coord3d(4, 2, 0), "1__See1"), null) ASSERT_EQUAL(tile_x(4, 2, 0).find_object(mo_groundobj), null) } // clean up ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 2, 0), slope.flat), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_halt.nut000066400000000000000000001372431520675460500206210ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // test for halts/stops // function test_halt_build_rail_single_tile() { local pl = player_x(0) local setslope = command_x.set_slope local stationbuilder = command_x(tool_build_station) local wayremover = command_x(tool_remove_way) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] // road because it has double slopes available local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, {} )[0] local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] // preconditions ASSERT_TRUE(road_desc != null) ASSERT_TRUE(station_desc != null) ASSERT_TRUE(bridge_desc != null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) local pos = coord3d(4, 2, 0) { for (local sl = slope.flat; sl < slope.raised; ++sl) { ASSERT_EQUAL(setslope(pl, pos, sl), sl != slope.flat ? null : "") ASSERT_EQUAL(stationbuilder.work(pl, pos, station_desc.get_name()), "No suitable way on the ground!") ASSERT_EQUAL(pl.get_current_maintenance(), 0) ASSERT_EQUAL(tile_x(pos.x, pos.y, pos.z).find_object(mo_building), null) } } ASSERT_EQUAL(setslope(pl, pos, slope.flat), null) // cannot build on non-flat tile { for (local sl = slope.flat+1; sl < slope.raised; ++sl) { ASSERT_EQUAL(setslope(pl, pos, sl), null) local d = slope.to_dir(sl) if (d != dir.none) { // only consider slopes we can build roads on RESET_ALL_PLAYER_FUNDS() local c = dir.to_coord(dir.backward(d)) local adjacent = pos + coord3d(c.x, c.y, 0) ASSERT_EQUAL(command_x.build_way(pl, adjacent, pos, road_desc, true), null) local old_maintenance = pl.get_current_maintenance() ASSERT_EQUAL(stationbuilder.work(pl, pos, station_desc.get_name()), "No suitable way on the ground!") ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) ASSERT_EQUAL(tile_x(pos.x, pos.y, pos.z).find_object(mo_building), null) ASSERT_EQUAL(wayremover.work(pl, pos, adjacent, "" + wt_road), null) } } } ASSERT_EQUAL(setslope(pl, pos, slope.flat), null) // build on bridge { ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 4, 0), slope.north), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 2, 0), bridge_desc), null) local old_maintenance = pl.get_current_maintenance() ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 3, 1), station_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 1)), null) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) // note z = 0 instead of 1 ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 2, 0), station_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 2, 0)), null) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) // note z = 0 instead of 1 ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 4, 0), station_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 4, 0)), null) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 4, 0)), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.flat), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 4, 0), slope.flat), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_halt_build_harbour() { local pl = player_x(0) local stationbuilder = command_x(tool_build_station) local station_desc = building_desc_x.get_available_stations(building_desc_x.harbour, wt_water, good_desc_x.passenger)[0] // FIXME: null instead of pax fails local setclimate = command_x(tool_set_climate) // build harbour on flat land: should fail { ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), station_desc.get_name()), "Dock must be built on single slope!") ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) } // build harbour on sloped land: should fail { ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), station_desc.get_name()), "") ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 4, 0)), null) ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), station_desc.get_name()), "") ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) } // build harbour on sloped land adjacent to water: Should succeed { ASSERT_EQUAL(setclimate.work(pl, coord3d(4, 3, -1), coord3d(4, 3, -1), "" + cl_water), null) ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 2, -1), station_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 3, -1)), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) ASSERT_EQUAL(setclimate.work(pl, coord3d(4, 3, -1), coord3d(4, 3, -1), "" + cl_mediterran), null) } // clean up ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 4, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_near_water() { ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 3, 0), "LakeShipStop"), null) } // clean up ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 3, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_near_water_multiple_rotations() { ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(3, 3, 0), coord3d(3, 3, 0), "" + cl_water), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 3, 0), "LakeShipStop"), "More than one possibility to build this dock found.") } // clean up ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(3, 3, 0), coord3d(3, 3, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_near_water_fixed_rotation_valid() { ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 3, 0), "LakeShipStop,2"), null) } // clean up ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 3, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_near_water_fixed_rotation_invalid() { ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 3, 0), "LakeShipStop,1"), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_outside_map() { { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(-1, -1, 0), "LakeShipStop"), "") } // clean up RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_near_map_border_auto_rotation() { ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 1, 0), coord3d(4, 1, 0), "" + cl_water), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 0, 0), "LakeShipStop"), null) } // clean up ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 1, 0), coord3d(4, 0, 0), "" + cl_mediterran), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 0, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_near_map_border_fixed_rotation() { { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 0, 0), "LakeShipStop,2"), "No suitable ground!") } // clean up RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_on_bridge() { ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 2, 0), slope.south), null) ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 4, 0), slope.north), null) ASSERT_EQUAL(command_x(tool_build_bridge).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 4, 0), "Schiffhebewerk"), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 3, 1), "LakeShipStop"), "") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 2, 0), slope.flat), null) ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 4, 0), slope.flat), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_on_slope() { ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 2, 0), slope.north), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 2, 0), "LakeShipStop"), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 2, 0), slope.flat), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_near_cliff() { ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 3, 0), slope.all_up_slope), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 3, 1), "LakeShipStop"), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) ASSERT_EQUAL(command_x.set_slope(player_x(0), coord3d(4, 3, 1), slope.all_down_slope), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_in_water() { ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 2, 0), "LakeShipStop"), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x(tool_set_climate).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_flat_dock_occupied() { ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(8, 8, 0), "0"), null) ASSERT_EQUAL(command_x(tool_build_house).work(player_x(1), coord3d(4, 2, 0), "11RES_01_23"), null) { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 2, 0), "LakeShipStop"), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(8, 8, 0)), null); // remove city ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(1), coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_road), null); RESET_ALL_PLAYER_FUNDS(); } function test_halt_build_air() { local pl = player_x(0) local runway = way_desc_x.get_available_ways(wt_air, st_runway)[0] local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0] local airhalt = building_desc_x("AirStop") ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 5, 0), coord3d(5, 7, 0), runway, true), null) // air halts must be on taxiways (contrary to the untranslated error message) { ASSERT_EQUAL(command_x.build_station(pl, coord3d(5, 5, 0), airhalt), "Flugzeughalt muss auf\nRunway liegen!\n") ASSERT_EQUAL(command_x.build_station(pl, coord3d(5, 6, 0), airhalt), "Flugzeughalt muss auf\nRunway liegen!\n") ASSERT_EQUAL(command_x.build_station(pl, coord3d(5, 7, 0), airhalt), "Flugzeughalt muss auf\nRunway liegen!\n") } ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 6, 0), coord3d(3, 6, 0), taxiway, true), null) // not in the middle of a taxiway { ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 6, 0), airhalt), "No terminal station here!") } // end of taxiway -> success { ASSERT_EQUAL(command_x.build_station(pl, coord3d(3, 6, 0), airhalt), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 6, 0)), null) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 6, 0), coord3d(5, 6, 0), "" + wt_air), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 5, 0), coord3d(5, 7, 0), "" + wt_air), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_multi_tile() { local pl = player_x(0) local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, {})[0] local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] // 2 adjacent tiles { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), road, true), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(2, 2, 0), station_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 2, 0), station_desc.get_name()), null) local halt = halt_x.get_halt(coord3d(2, 2, 0), pl) ASSERT_TRUE(halt != null) ASSERT_EQUAL(tile_x(2, 2, 0).get_halt().get_name(), tile_x(3, 2, 0).get_halt().get_name()) // check that this is really the same halt ASSERT_EQUAL(halt.get_owner().get_name(), pl.get_name()) ASSERT_EQUAL(halt.is_connected(halt, good_desc_x.passenger), 1) ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) ASSERT_FALSE(halt.accepts_good(good_desc_x.mail)) ASSERT_EQUAL(halt.get_arrived()[0], 0) ASSERT_EQUAL(halt.get_departed()[0], 0) ASSERT_EQUAL(halt.get_waiting()[0], 0) ASSERT_EQUAL(halt.get_happy()[0], 0) ASSERT_EQUAL(halt.get_unhappy()[0], 0) ASSERT_EQUAL(halt.get_noroute()[0], 0) ASSERT_EQUAL(halt.get_convoys()[0], 0) ASSERT_EQUAL(halt.get_walked()[0], 0) ASSERT_EQUAL(halt.get_convoy_list().get_count(), 0) ASSERT_EQUAL(halt.get_line_list().get_count(), 0) ASSERT_EQUAL(halt.get_factory_list().len(), 0) local tile_list = halt.get_tile_list() local expected_tiles = [ tile_x(2, 2, 0), tile_x(3, 2, 0) ] foreach (t in tile_list) { ASSERT_TRUE(t.find_object(mo_building) != null) // expected_tiles.find(t) != null won't work ASSERT_EQUAL(expected_tiles.filter(@(idx, val) t.x == val.x && t.y == val.y && t.z == val.z).len(), 1) } ASSERT_EQUAL(halt.get_freight_to_dest(good_desc_x.passenger, coord(2, 2)), 0) ASSERT_EQUAL(halt.get_freight_to_halt(good_desc_x.passenger, halt), 0) // this also depends on the separate_halt_capacities setting ASSERT_EQUAL(halt.get_capacity(good_desc_x.passenger), 64) ASSERT_EQUAL(halt.get_capacity(good_desc_x.mail), 0) ASSERT_EQUAL(halt.get_capacity(good_desc_x("Kohle")), 0) ASSERT_EQUAL(halt.get_capacity(good_desc_x("nonexistent")), 0) ASSERT_EQUAL(halt.get_connections(good_desc_x.passenger).len(), 0) ASSERT_EQUAL(halt.get_connections(good_desc_x.mail).len(), 0) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } // 2 tiles on top of each other { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), road, true), null) ASSERT_EQUAL(command_x(tool_build_bridge).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), bridge_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 4, 0), station_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 4, 1), station_desc.get_name()), null) local lower_halt = halt_x.get_halt(coord3d(3, 4, 0), pl) local upper_halt = halt_x.get_halt(coord3d(3, 4, 1), pl) ASSERT_EQUAL(lower_halt.get_name(), upper_halt.get_name()) ASSERT_EQUAL(lower_halt.get_tile_list().len(), 2) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 2, 0), coord3d(3, 6, 0), "" + wt_road), null) } RESET_ALL_PLAYER_FUNDS() local old_cash = pl.get_cash()[0] ASSERT_EQUAL(old_cash, 200*1000) // 16x16 station { for (local x = 0; x < 16; ++x) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(x, 0, 0), coord3d(x, 15, 0), road, true), null) for (local y = 0; y < 16; ++y) { ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(x, y, 0), station_desc.get_name()), null) } } ASSERT_EQUAL(pl.get_current_maintenance(), 16*16*(road.get_maintenance() + station_desc.get_maintenance())) ASSERT_EQUAL(pl.get_cash()[0]*100, old_cash*100 - 16*16*(road.get_cost() + station_desc.get_cost())) local halt = halt_x.get_halt(coord3d(0, 0, 0), pl) ASSERT_TRUE(halt != null) ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) ASSERT_FALSE(halt.accepts_good(good_desc_x.mail)) ASSERT_EQUAL(halt.is_connected(halt, good_desc_x.passenger), 1) ASSERT_EQUAL(halt.is_connected(halt, good_desc_x.mail), 1) for (local x = 0; x < 16; ++x) { ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(x, 0, 0), coord3d(x, 15, 0), "" + wt_road), null) } ASSERT_EQUAL(pl.get_current_maintenance(), 0) } RESET_ALL_PLAYER_FUNDS() } function test_halt_build_multi_mode() { local pl = player_x(0) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local pax_stop = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] local pax_station = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] { ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(4, 4, 0), road_desc, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 3, 0), coord3d(5, 4, 0), rail_desc, true), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_stop.get_name()), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(5, 4, 0), pax_station.get_name()), null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 4, 0), pl).get_name(), halt_x.get_halt(coord3d(5, 4, 0), pl).get_name()) local halt = halt_x.get_halt(coord3d(4, 4, 0), pl) ASSERT_EQUAL(halt.get_capacity(good_desc_x.passenger), 64) ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 4, 0), coord3d(5, 3, 0), "" + wt_rail), null) } RESET_ALL_PLAYER_FUNDS() } function test_halt_build_multi_player() { local pl = player_x(0) local public_pl = player_x(1) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] { ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 4, 0), coord3d(3, 4, 0), road_desc, true), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_halt.get_name()), null) ASSERT_EQUAL(command_x(tool_build_station).work(public_pl, coord3d(3, 4, 0), pax_halt.get_name()), null) ASSERT_TRUE(halt_x.get_halt(coord3d(3, 4, 0), pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 4, 0), public_pl), null) local my_halt = halt_x.get_halt(coord3d(4, 4, 0), pl) local pub_halt = halt_x.get_halt(coord3d(3, 4, 0), public_pl) ASSERT_TRUE(my_halt != null) ASSERT_TRUE(pub_halt != null) ASSERT_TRUE(my_halt.get_name() != pub_halt.get_name()) ASSERT_EQUAL(my_halt.get_tile_list().len(), 1) ASSERT_EQUAL(pub_halt.get_tile_list().len(), 1) ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(4, 4, 0), coord3d(3, 4, 0), "" + wt_road), null) } RESET_ALL_PLAYER_FUNDS() } function test_halt_build_separate() { local pl = player_x(0) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 4, 0), coord3d(3, 4, 0), road_desc, true), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(5, 4, 0), pax_halt.get_name()), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 4, 0), pax_halt.get_name()), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_halt.get_name()), null) local halt3 = halt_x.get_halt(coord3d(3, 4, 0), pl) local halt4 = halt_x.get_halt(coord3d(4, 4, 0), pl) local halt5 = halt_x.get_halt(coord3d(5, 4, 0), pl) ASSERT_TRUE(halt3 != null) ASSERT_TRUE(halt4 != null) ASSERT_TRUE(halt5 != null) ASSERT_TRUE(halt3.get_name() != halt5.get_name()) ASSERT_TRUE(halt4.get_name() == halt5.get_name()) ASSERT_TRUE(halt4.get_name() != halt3.get_name()) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 4, 0), coord3d(3, 4, 0), "" + wt_road), null) } RESET_ALL_PLAYER_FUNDS() } function test_halt_build_near_factory() { local pl = player_x(0) local public_pl = player_x(1) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] local freight_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x("Kohle"))[0] local remover = command_x(tool_remover) // build coal mine + coal power plant, then link them ASSERT_EQUAL(build_factory(pl, coord3d(0, 0, 0), 1, 1, 1024, "Kohlegrube"), null) { // also depends on station catchment area size ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), road_desc, true), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_halt.get_name()), null) local halt = halt_x.get_halt(coord3d(4, 4, 0), pl) ASSERT_TRUE(halt != null) ASSERT_EQUAL(halt.get_factory_list().len(), 1) ASSERT_EQUAL(halt.get_factory_list()[0].get_name(), "Coal mine") ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), "" + wt_road), null) } ASSERT_EQUAL(remover.work(public_pl, coord3d(0, 0, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_near_factories() { local pl = player_x(0) local public_pl = player_x(1) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] local freight_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x("Kohle"))[0] local remover = command_x(tool_remover) // build coal mine + coal power plant, then link them ASSERT_EQUAL(build_factory(pl, coord3d(0, 0, 0), 1, 1, 1024, "Kohlegrube"), null) ASSERT_EQUAL(build_factory(pl, coord3d(6, 6, 0), 1, 1, 1024, "Kohlekraftwerk"), null) ASSERT_EQUAL(command_x(tool_link_factory).work(pl, coord3d(0, 0, 0), coord3d(6, 6, 0), ""), null) { // also depends on station catchment area size ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), road_desc, true), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_halt.get_name()), null) local halt = halt_x.get_halt(coord3d(4, 4, 0), pl) ASSERT_TRUE(halt != null) ASSERT_EQUAL(halt.get_factory_list().len(), 2) ASSERT_EQUAL(halt.get_factory_list()[0].get_name(), "Coal power station") ASSERT_EQUAL(halt.get_factory_list()[1].get_name(), "Coal mine") ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), "" + wt_road), null) } // this only works with catchment area size of 4 { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 7, 0), coord3d(1, 7, 0), road_desc, true), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(1, 7, 0), pax_halt.get_name()), null) local halt = halt_x.get_halt(coord3d(1, 7, 0), pl) ASSERT_TRUE(halt != null) ASSERT_EQUAL(halt.get_factory_list().len(), 0) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(0, 7, 0), coord3d(1, 7, 0), "" + wt_road), null) } ASSERT_EQUAL(remover.work(public_pl, coord3d(0, 0, 0)), null) ASSERT_EQUAL(remover.work(public_pl, coord3d(6, 6, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_on_tunnel_entrance() { local pl = player_x(0) local rail_tunnel = tunnel_desc_x.get_available_tunnels(wt_rail)[0] local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(4, 1, 0), rail_tunnel.get_name()), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(3, 2, 0), rail_tunnel.get_name()), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(5, 2, 0), rail_tunnel.get_name()), null) // Building station on tunnel entrance fails (contrary to depots) { for (local d = dir.north; d < dir.all; d = d*2) { local p = coord3d(4, 2, 0) + dir.to_coord(d) ASSERT_EQUAL(command_x.build_station(pl, p, station_desc), "No suitable way on the ground!") } } local remover = command_x(tool_remover) remover.set_flags(2) ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 3, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_on_bridge_end() { local pl = player_x(0) local rail_bridge = bridge_desc_x.get_available_bridges(wt_rail)[0] local setslope = command_x.set_slope local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] // north-south direction { ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.north), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(4, 2, 0), rail_bridge), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 2, 0), station_desc), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 4, 0), station_desc), null) // tool_remover removes halt first, then bridge, then depot (depot is automatically removed when destroying bridge) // So here we have to remove things on the tile twice (contrary to test_depot_build_on_bridge_end) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.flat), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.flat), null) } // east-west direction { ASSERT_EQUAL(setslope(pl, coord3d(3, 3, 0), slope.east), null) ASSERT_EQUAL(setslope(pl, coord3d(5, 3, 0), slope.west), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 3, 0), rail_bridge), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), get_depot_by_wt(wt_rail)), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), get_depot_by_wt(wt_rail)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 3, 0), slope.flat), null) ASSERT_EQUAL(setslope(pl, coord3d(5, 3, 0), slope.flat), null) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_halt_build_on_depot() { local pl = player_x(0) local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] local depot = get_depot_by_wt(wt_rail) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), rail_desc, true), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), depot), null) { ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 2, 0), station_desc), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_build_station_invalid_param() { // null default_param { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(0, 0, 0), null), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // empty default_param { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(0, 0, 0), ""), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // rotation information not a number { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(0, 0, 0), "foo,bar"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // rotation information number out of range 1 { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(0, 0, 0), "foo,16"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // rotation information number out of range 2 { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(0, 0, 0), "foo,-2"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // rotation information number out of range 3 (out of sint8 range) { local error_caught = false try { ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(0, 0, 0), "foo,1000"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_halt_build_station_extension() { local pl = player_x(0) local setslope = command_x.set_slope local stationbuilder = command_x(tool_build_station) local wayremover = command_x(tool_remove_way) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] // road because it has double slopes available local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, {})[0] local stext_desc = building_desc_x.get_available_stations(building_desc_x.station_extension, wt_rail, good_desc_x.passenger)[0] local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] ASSERT_TRUE(station_desc != null) ASSERT_TRUE(stext_desc != null) ASSERT_TRUE(bridge_desc != null) ASSERT_TRUE(road_desc != null) // build station extension without station: should fail { ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), stext_desc.get_name()), "Post muss neben\nHaltestelle\nliegen!\n") ASSERT_EQUAL(pl.get_current_maintenance(), 0) ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) } ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), station_desc.get_name()), null) local old_maintenance = pl.get_current_maintenance() // build station extension next to station: should succeed { ASSERT_EQUAL(stationbuilder.work(pl, coord3d(5, 3, 0), stext_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) // and diagonal ASSERT_EQUAL(stationbuilder.work(pl, coord3d(5, 2, 0), stext_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 2, 0)), null) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) } // build station extension on raised tile: should succeed { ASSERT_EQUAL(setslope(pl, coord3d(5, 3, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(5, 3, 1), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(5, 3, 2), slope.all_up_slope), null) ASSERT_EQUAL(stationbuilder.work(pl, coord3d(5, 3, 3), stext_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 3)), null) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) // up slope is automatically removed ASSERT_EQUAL(square_x(5, 3).get_tile_at_height(1), null) ASSERT_EQUAL(square_x(5, 3).get_tile_at_height(2), null) } { // diagonal too ASSERT_EQUAL(setslope(pl, coord3d(5, 2, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(5, 2, 1), slope.all_up_slope), null) ASSERT_EQUAL(stationbuilder.work(pl, coord3d(5, 2, 2), stext_desc.get_name()), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 2, 2)), null) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) // up slope is automatically removed ASSERT_EQUAL(square_x(5, 3).get_tile_at_height(1), null) ASSERT_EQUAL(square_x(5, 3).get_tile_at_height(2), null) } ASSERT_EQUAL(wayremover.work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) // clean up RESET_ALL_PLAYER_FUNDS() } function test_halt_upgrade_downgrade() { local pl = player_x(0) local stationbuilder = command_x(tool_build_station) local wayremover = command_x(tool_remove_way) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local pax_halts = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger) pax_halts.sort(@(a, b) a.get_capacity() <=> b.get_capacity()) ASSERT_TRUE(pax_halts.len() >= 2) local small_halt = pax_halts[0] local big_halt = pax_halts[1] local pos = coord3d(3, 3, 0) ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), road_desc, true), null) // upgrade station { ASSERT_EQUAL(stationbuilder.work(pl, pos, small_halt.get_name()), null) local halt = halt_x.get_halt(pos, pl) ASSERT_TRUE(halt != null) local old_capacity = halt.get_capacity(good_desc_x.passenger) local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(stationbuilder.work(pl, pos, big_halt.get_name()), null) ASSERT_TRUE(halt.is_valid()) ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) ASSERT_TRUE(halt.get_capacity(good_desc_x.passenger) > old_capacity) ASSERT_EQUAL(halt.get_tile_list().len(), 1) ASSERT_TRUE(pl.get_current_maintenance() > old_maint) ASSERT_EQUAL(pl.get_current_maintenance() - old_maint, big_halt.get_maintenance() - small_halt.get_maintenance()) } // upgrade station to same level { local halt = halt_x.get_halt(pos, pl) local old_capacity = halt.get_capacity(good_desc_x.passenger) local old_maint = pl.get_current_maintenance() local old_cash = pl.get_current_cash() ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 3, 0), big_halt.get_name()), null) ASSERT_TRUE(halt.is_valid()) ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) ASSERT_EQUAL(halt.get_capacity(good_desc_x.passenger), old_capacity) ASSERT_EQUAL(halt.get_tile_list().len(), 1) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } // downgrade without ctrl, should fail { local halt = halt_x.get_halt(pos, pl) local old_capacity = halt.get_capacity(good_desc_x.passenger) local old_maint = pl.get_current_maintenance() local old_cash = pl.get_current_cash() ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 3, 0), small_halt.get_name()), "Upgrade must have\na higher level") ASSERT_TRUE(halt.is_valid()) ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) ASSERT_EQUAL(halt.get_capacity(good_desc_x.passenger), old_capacity) ASSERT_EQUAL(halt.get_tile_list().len(), 1) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } // downgrade with ctrl { local halt = halt_x.get_halt(pos, pl) local old_capacity = halt.get_capacity(good_desc_x.passenger) local old_maint = pl.get_current_maintenance() local old_cash = pl.get_current_cash() stationbuilder.set_flags(2) // ctrl ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 3, 0), small_halt.get_name()), null) ASSERT_TRUE(halt.is_valid()) ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) ASSERT_TRUE(halt.get_capacity(good_desc_x.passenger) < old_capacity) ASSERT_EQUAL(halt.get_tile_list().len(), 1) ASSERT_TRUE(pl.get_current_maintenance() < old_maint) ASSERT_EQUAL(pl.get_current_maintenance() - old_maint, small_halt.get_maintenance() - big_halt.get_maintenance()) } // clean up ASSERT_EQUAL(wayremover.work(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_make_public_single() { local pl = player_x(0) local public_pl = player_x(1) local stationbuilder = command_x(tool_build_station) local wayremover = command_x(tool_remove_way) local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local makepublic = command_x(tool_make_stop_public) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) // invalid pos { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(-1, -1, -1)), "No suitable ground!") ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } // no stop under cursor { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(3, 2, 0)), null) ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } // valid stop - Making the stop public also makes the road under it public { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint - 1*pax_halt.get_maintenance() - 1*road_desc.get_maintenance()) ASSERT_EQUAL(pl.get_current_cash()*100, old_cash*100 - 60 * (1*pax_halt.get_maintenance() + 1*road_desc.get_maintenance()) ) ASSERT_EQUAL(public_pl.get_current_maintenance(), 1*pax_halt.get_maintenance() + 1*road_desc.get_maintenance()) } // already public - should have no effect { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) ASSERT_EQUAL(pl.get_current_cash(), old_cash) } // same for public player { local old_cash = public_pl.get_current_cash() local old_maint = public_pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(public_pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint) ASSERT_EQUAL(public_pl.get_current_cash(), old_cash) } ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) // If public player uses this tool, he pays for it { local old_cash = public_pl.get_current_cash() local old_maint = public_pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) // only halt maintenance because the road is already public ASSERT_EQUAL(public_pl.get_current_cash()*100, 100*old_cash + 60 * pax_halt.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint + pax_halt.get_maintenance()) } ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) // Public player can make halts of other players public even if road underneath is not public { local old_cash = public_pl.get_current_cash() local old_maint = public_pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) // halt + road maintenance ASSERT_EQUAL(public_pl.get_current_cash()*100, 100*old_cash + 60 * pax_halt.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint + pax_halt.get_maintenance() + road_desc.get_maintenance()) } ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_make_public_multi_tile() { local pl = player_x(0) local public_pl = player_x(1) local stationbuilder = command_x(tool_build_station) local wayremover = command_x(tool_remove_way) local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local makepublic = command_x(tool_make_stop_public) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 5, 0), road_desc, true), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 4, 0), pax_halt), null) // valid stop - Making the stop public also makes the road under it public { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint - 2*pax_halt.get_maintenance() - 2*road_desc.get_maintenance()) ASSERT_EQUAL(pl.get_current_cash()*100, old_cash*100 - 60 * (2*pax_halt.get_maintenance() + 2*road_desc.get_maintenance()) ) ASSERT_EQUAL(public_pl.get_current_maintenance(), 2*pax_halt.get_maintenance() + 2*road_desc.get_maintenance()) } // already public - should have no effect { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) ASSERT_EQUAL(pl.get_current_cash(), old_cash) } // same for public player { local old_cash = public_pl.get_current_cash() local old_maint = public_pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(public_pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint) ASSERT_EQUAL(public_pl.get_current_cash(), old_cash) } ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 5, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 5, 0), road_desc, true), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 4, 0), pax_halt), null) // If public player uses this tool, he pays for it { local old_cash = public_pl.get_current_cash() local old_maint = public_pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(public_pl.get_current_cash()*100, 100*old_cash + 60 * 2*pax_halt.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint + (2*pax_halt.get_maintenance() + 2*road_desc.get_maintenance())) } ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 5, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_halt_make_public_underground() { local pl = player_x(0) local public_pl = player_x(1) local tunnel_desc = tunnel_desc_x.get_available_tunnels(wt_road)[0] local pax_halt = building_desc_x("LongBusStop") local makepublic = command_x(tool_make_stop_public) local stationbuilder = command_x(tool_build_station) ASSERT_EQUAL(command_x.grid_raise(public_pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(public_pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(public_pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x.grid_raise(public_pl, coord3d(5, 4, 0)), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(4, 2, 0), tunnel_desc.get_name()), null) ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), pax_halt.get_name()), null) // valid underground stop { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint - 1*pax_halt.get_maintenance() - 1*tunnel_desc.get_maintenance()) ASSERT_EQUAL(pl.get_current_cash()*100, old_cash*100 - 60 * (1*pax_halt.get_maintenance() + 1*tunnel_desc.get_maintenance()) ) ASSERT_EQUAL(public_pl.get_current_maintenance(), 1*pax_halt.get_maintenance() + 1*tunnel_desc.get_maintenance()) } ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(5, 4, 0)), null) } function test_halt_move_stop_invalid_param() { // out of map limits { local error_caught = false try { ASSERT_EQUAL(command_x(tool_stop_mover).work(player_x(0), coord3d(-1, -1, 0), coord3d(-1, -1, 0)), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } // no way { local error_caught = false try { ASSERT_EQUAL(command_x(tool_stop_mover).work(player_x(0), coord3d(1, 1, 0), coord3d(3, 1, 0)), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } // clean up RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_headquarters.nut000066400000000000000000000050761520675460500223570ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Test building/upgrading/removing headquarters // function get_hq_by_level(level) { local list = building_desc_x.get_building_list(building_desc_x.headquarter) list.sort(@(a, b) a.get_headquarter_level() <=> b.get_headquarter_level()) return list[level] } function test_headquarters_build_flat() { local pl = player_x(0) local public_pl = player_x(1) local hq_builder = command_x(tool_headquarter) local pos1 = coord3d(2, 3, 0) local pos2 = coord3d(4, 1, 0) local pos1_2d = coord(2, 3) local pos2_2d = coord(4, 1) ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) // place normal { ASSERT_EQUAL(hq_builder.work(pl, pos1, get_hq_by_level(0).get_name()), null) ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos1_2d.tostring()) } // replace my existing HQ { ASSERT_EQUAL(hq_builder.work(pl, pos1, get_hq_by_level(1).get_name()), null) ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos1_2d.tostring()) } { // cannot downgrade headquarters FIXME this depends on whether timeline is enabled ?! ASSERT_EQUAL(hq_builder.work(pl, pos2, get_hq_by_level(0).get_name()), "Insufficient funds!") ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos1_2d.tostring()) pl.book_cash(1*1000*1000*100) ASSERT_EQUAL(hq_builder.work(pl, pos2, get_hq_by_level(0).get_name()), null) ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos2_2d.tostring()) } // cannot replace other people's headquarters (not even public player!) { ASSERT_EQUAL(hq_builder.work(public_pl, pos2, get_hq_by_level(1).get_name()), "No suitable ground!") ASSERT_EQUAL(public_pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos2_2d.tostring()) ASSERT_EQUAL(hq_builder.work(public_pl, pos1, get_hq_by_level(1).get_name()), null) ASSERT_EQUAL(public_pl.get_headquarter_pos().tostring(), pos1_2d.tostring()) ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos2_2d.tostring()) } // remove hq local remover = command_x(tool_remover) ASSERT_EQUAL(remover.work(pl, tile_x(2, 3, 0)), "Der Besitzer erlaubt das Entfernen nicht") // cannot remove HQ of other players ASSERT_EQUAL(remover.work(public_pl, tile_x(2, 3, 0)), null) ASSERT_EQUAL(remover.work(public_pl, tile_x(4, 1, 0)), null) ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) ASSERT_EQUAL(public_pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_label.nut000066400000000000000000000040761520675460500207450ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for labels // function test_label() { local remover = command_x(tool_remover) local pl = player_x(0) local public_pl = player_x(1) // build label on ground { ASSERT_EQUAL(label_x.create(coord3d(4, 1, 0), pl, "Foo2"), null) // test label list local count = 0 foreach(label in world.get_label_list()) { ASSERT_EQUAL(label.get_text(), "Foo2") count ++ } ASSERT_EQUAL(count, 1) ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0)), null) ASSERT_EQUAL(world.get_label_list().get_count(), 0) } // Build label on invalid ground, same as building on map ground { ASSERT_EQUAL(label_x.create(coord3d(3, 1, -2), pl, "Foo1"), null) local label = tile_x(3, 1, 0).find_object(mo_label) ASSERT_TRUE(label != null) ASSERT_TRUE(label.is_valid()) ASSERT_EQUAL(label.get_owner().get_name(), pl.get_name()) // TODO More secure way to check for player equality ASSERT_EQUAL(label.get_waytype(), wt_invalid) ASSERT_EQUAL(label.get_text(), "Foo1") ASSERT_EQUAL(label.set_text("Bar"), true) ASSERT_EQUAL(label.get_text(), "Bar") ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0)), null) ASSERT_FALSE(label.is_valid()) } // build label on existing label, should fail { ASSERT_EQUAL(label_x.create(coord3d(5, 1, 0), pl, "Foo3"), null) ASSERT_EQUAL(label_x.create(coord3d(5, 1, 0), pl, "Foo4"), "Das Feld gehoert\neinem anderen Spieler\n") ASSERT_EQUAL(remover.work(pl, coord3d(5, 1, 0)), null) } // players cannot remove labels of other players (except public player) { ASSERT_EQUAL(label_x.create(coord3d(5, 1, 0), pl, "Foo1"), null) ASSERT_EQUAL(label_x.create(coord3d(5, 2, 0), public_pl, "Foo2"), null) ASSERT_EQUAL(remover.work(pl, coord3d(5, 2, 0)), "Der Besitzer erlaubt das Entfernen nicht") ASSERT_TRUE(tile_x(5, 2, 0).find_object(mo_label) != null) ASSERT_EQUAL(remover.work(public_pl, coord3d(5, 2, 0)), null) ASSERT_EQUAL(remover.work(public_pl, coord3d(5, 1, 0)), null) } // clean up RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_player.nut000066400000000000000000000057101520675460500211560ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for player_x stuff // function test_player_cash() { local pl = player_x(0) ASSERT_EQUAL(pl.get_current_cash(), 200000) // get_current_cash is in credits (returns float) ASSERT_EQUAL(pl.get_current_net_wealth(), 200000*100) // get_current_net_wealth is in 1/100 credits ASSERT_EQUAL(pl.get_current_maintenance(), 0) local ok = pl.book_cash(0) ASSERT_EQUAL(ok, true) ASSERT_EQUAL(pl.get_current_cash(), 200000) ASSERT_EQUAL(pl.get_current_net_wealth(), 200000 * 100) ASSERT_EQUAL(pl.get_current_maintenance(), 0) ok = pl.book_cash(-200000 * 100) // book_cash is in 1/100 credits ASSERT_EQUAL(ok, true) ASSERT_EQUAL(pl.get_current_cash(), 0) ASSERT_EQUAL(pl.get_current_net_wealth(), 0) ASSERT_EQUAL(pl.get_current_maintenance(), 0) ok = pl.book_cash(-1) ASSERT_EQUAL(ok, true) ASSERT_EQUAL(pl.get_current_cash(), -0.01) ASSERT_EQUAL(pl.get_current_net_wealth(), -1) ASSERT_EQUAL(pl.get_current_maintenance(), 0) // clean up ok = pl.book_cash(200000 * 100 + 1) ASSERT_EQUAL(ok, true) ASSERT_EQUAL(pl.get_current_cash(), 200000) ASSERT_EQUAL(pl.get_current_net_wealth(), 200000 * 100) ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_player_isactive() { local pl = player_x(0) ASSERT_EQUAL(pl.is_valid(), true) ASSERT_EQUAL(pl.is_active(), true) local public_pl = player_x(1) ASSERT_EQUAL(public_pl.is_valid(), true) ASSERT_EQUAL(public_pl.is_active(), true) local nonexist = player_x(2) ASSERT_EQUAL(nonexist.is_valid(), false) ASSERT_EQUAL(nonexist.is_active(), false) RESET_ALL_PLAYER_FUNDS() } function test_player_headquarters() { local pl = player_x(0) ASSERT_EQUAL(pl.get_headquarter_level(), 0) ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) RESET_ALL_PLAYER_FUNDS() } function test_player_name() { local pl = player_x(0) ASSERT_EQUAL(pl.get_name(), "Human player") ASSERT_TRUE(pl.set_name("Foo")) ASSERT_EQUAL(pl.get_name(), "Foo") ASSERT_TRUE(pl.set_name("Foo")) ASSERT_EQUAL(pl.get_name(), "Foo") // clean up ASSERT_TRUE(pl.set_name("Human player")) RESET_ALL_PLAYER_FUNDS() } function test_player_lines() { local pl = player_x(0) // empty line list { local line_list = pl.get_line_list() ASSERT_TRUE(line_list != null) ASSERT_EQUAL(line_list.get_count(), 0) } // non-empty line list { ASSERT_TRUE(pl.create_line(wt_road)) local line_list = pl.get_line_list() ASSERT_TRUE(line_list != null) ASSERT_EQUAL(line_list.get_count(), 1) foreach (line in line_list) { ASSERT_TRUE(line != null) ASSERT_EQUAL(line.is_valid(), true) ASSERT_EQUAL(line.get_waytype(), wt_road) } foreach (line in line_list) { ASSERT_TRUE(line.destroy(pl)) } ASSERT_EQUAL(pl.get_line_list().get_count(), 0) } // TODO Destroy lines owned by other players // clean up RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_powerline.nut000066400000000000000000000451171520675460500216730ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for powerline stuff and transformers // function test_powerline_connect() { local pl = player_x(0) local public_pl = player_x(1) local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] local wayremover = command_x(tool_remove_way) ASSERT_TRUE(powerline != null) local center = coord3d(3, 4, 0) local north = center + dir.to_coord(dir.north) local east = center + dir.to_coord(dir.east) local south = center + dir.to_coord(dir.south) local west = center + dir.to_coord(dir.west) // preconditions ASSERT_EQUAL(pl.get_current_maintenance(), 0) { ASSERT_EQUAL(command_x.build_way(pl, center, south, powerline, true), null) ASSERT_EQUAL(wayremover.work(pl, south, south, "" + wt_power), null) ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(center.x, center.y, center.z))) ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "........", "...0....", "........", "........", "........" ]) } local maint_per_tile = pl.get_current_maintenance() ASSERT_TRUE(maint_per_tile > 0) { ASSERT_EQUAL(command_x.build_way(pl, center, north, powerline, true), null) ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(north.x, north.y, north.z))) ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "...4....", "...1....", "........", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), 2*maint_per_tile) } { ASSERT_EQUAL(command_x.build_way(pl, center, east, powerline, true), null) ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(east.x, east.y, east.z))) ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "...4....", "...38...", "........", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), 3*maint_per_tile) } { ASSERT_EQUAL(command_x.build_way(pl, center, south, powerline, true), null) ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(south.x, south.y, south.z))) ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "...4....", "...78...", "...1....", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), 4*maint_per_tile) } { ASSERT_EQUAL(command_x.build_way(pl, center, west, powerline, true), null) ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(west.x, west.y, west.z))) ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "...4....", "..2F8...", "...1....", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), 5*maint_per_tile) } // clean up ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_power), null) ASSERT_EQUAL(wayremover.work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_power), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_powerline_build_below_powerbridge() { local pl = player_x(0) local power_bridge = bridge_desc_x.get_available_bridges(wt_power)[0] local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), power_bridge), null) // build normal powerline below power bridge, should fail { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), powerline, true), null) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), "" + wt_power), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), "" + wt_power), null) RESET_ALL_PLAYER_FUNDS() } function test_powerline_build_powerbridge_above_powerline() { local pl = player_x(0) local power_bridge = bridge_desc_x.get_available_bridges(wt_power)[0] local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] // Note: Build way first, then bridge (see above for why) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), powerline, true), null) // build flat powerline bridge { ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), power_bridge), null) local bridge_part = powerline_x(3, 3, 1) local ground_part = powerline_x(3, 3, 0) ASSERT_TRUE(bridge_part != null) ASSERT_TRUE(ground_part != null) ASSERT_FALSE(bridge_part.is_connected(ground_part)) ASSERT_FALSE(ground_part.is_connected(bridge_part)) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), "" + wt_power), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), "" + wt_power), null) RESET_ALL_PLAYER_FUNDS() } function test_powerline_build_transformer() { local pl = player_x(0) local public_pl = player_x(1) local build_trafo = command_x(tool_build_transformer) local setclimate = command_x(tool_set_climate) local setslope = command_x.set_slope local power_tunnel = tunnel_desc_x.get_available_tunnels(wt_power)[0] // preconditions ASSERT_TRUE(power_tunnel != null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) // build coal mine + coal power plant, then link them ASSERT_EQUAL(build_factory(public_pl, coord3d(0, 0, 0), 1, 1, 1024, "Kohlegrube"), null) ASSERT_EQUAL(build_factory(public_pl, coord3d(6, 6, 0), 1, 1, 1024, "Kohlekraftwerk"), null) command_x(tool_link_factory).work(public_pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "") ASSERT_EQUAL(pl.get_current_maintenance(), 0) { // build transformer not adjacent to a factory ASSERT_EQUAL(build_trafo.work(pl, coord3d(0, 7, 0)), "Transformer only next to factory!") ASSERT_EQUAL(pl.get_current_maintenance(), 0) // diagonally adjacent to factory, not allowed ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 3, 0)), "Transformer only next to factory!") ASSERT_EQUAL(pl.get_current_maintenance(), 0) } { // cannot build transformer on water even if adjacent to factory ASSERT_EQUAL(setclimate.work(pl, coord3d(3, 2, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), "Transformer only on flat bare land!") ASSERT_EQUAL(pl.get_current_maintenance(), 0) ASSERT_EQUAL(setclimate.work(pl, coord3d(3, 2, 0), coord3d(3, 2, 0), "" + cl_mediterran), null) } { // build transformer in the air, same as building on map ground ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 1)), null) ASSERT_TRUE(pl.get_current_maintenance() > 0) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 2, 0)), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) } { // cannot build transformer on sloped tile ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.north), null) ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), "Transformer only on flat bare land!") ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 1)), "Transformer only on flat bare land!") ASSERT_EQUAL(pl.get_current_maintenance(), 0) } { // Cannot build transformer underground adjacent to factory // Depends also on allow_underground_transformers setting ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.all_up_slope), null) ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), "Transformer only next to factory!") // 2 height levels ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 1), slope.all_up_slope), null) ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), "Transformer only next to factory!") // However, building on flat ground on a different height level works ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 2)), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 2, 2)), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 2), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 1), slope.all_down_slope), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) } { // Build transformer underground below mine (senke_t) ASSERT_EQUAL(build_trafo.work(pl, coord3d(2, 2, -1)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 2, -1)), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) // And below power plant (pumpe_t) ASSERT_EQUAL(build_trafo.work(pl, coord3d(6, 6, -1)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(6, 6, -1)), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) } { // build underground transformer on underground powerline, should fail local digger = command_x(tool_build_tunnel) digger.set_flags(2) # Ctrl ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 4, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 4, 0)), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) ASSERT_EQUAL(digger.work(pl, coord3d(2, 3, -1), power_tunnel.get_name()), null) ASSERT_EQUAL(digger.work(pl, coord3d(2, 3, -1), coord3d(2, 2, -1), power_tunnel.get_name()), null) // test local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(build_trafo.work(pl, coord3d(2, 2, -1)), "Tile not empty.") ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 2, -1)), null) // and clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 2, -1)), "") // underground ??? ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 3, -1)), null) // above ground ??? ASSERT_EQUAL(pl.get_current_maintenance(), 0) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 4, -1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 4, -1)), null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(7, 7, 0)), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_powerline_build_over_transformer() { local pl = player_x(0) local public_pl = player_x(1) local build_trafo = command_x(tool_build_transformer) local production = 1024 // 1/s local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] ASSERT_EQUAL(build_factory(public_pl, coord3d(0, 0, 0), 1, 1, production, "Kohlegrube"), null) { ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 1, 0)), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 0, 0), coord3d(3, 2, 0), powerline, true), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 0, 0), coord3d(3, 2, 0), "" + wt_power), null) ASSERT_EQUAL(tile_x(3, 1, 0).find_object(mo_transformer_s), null) ASSERT_EQUAL(tile_x(3, 1, 0).find_object(mo_transformer_c), null) } ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_powerline_build_transformer_multiple() { local pl = player_x(0) local public_pl = player_x(1) local build_trafo = command_x(tool_build_transformer) local production = 1024 // 1/s local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] // build coal mine + coal power plant ASSERT_EQUAL(build_factory(public_pl, coord3d(0, 0, 0), 1, 1, production, "Kohlegrube"), null) ASSERT_EQUAL(build_factory(public_pl, coord3d(3, 0, 0), 1, 1, production, "Kohlekraftwerk"), null) // single transformer adjacent to multiple factories { ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), null) ASSERT_TRUE(tile_x(3, 2, 0).find_object(mo_transformer_s) != null) ASSERT_TRUE(tile_x(3, 2, 0).find_object(mo_transformer_c) == null) local trafo = transformer_x(3, 2, 0) local pp = factory_x(3, 0) local mine = factory_x(0, 0) ASSERT_EQUAL(trafo.get_factory().get_name(), pp.get_name()) ASSERT_TRUE(pp.is_transformer_connected()) ASSERT_FALSE(mine.is_transformer_connected()) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 2, 0)), null) } // multiple transformers next to single factory { ASSERT_EQUAL(build_trafo.work(pl, coord3d(0, 3, 0)), null) ASSERT_EQUAL(build_trafo.work(pl, coord3d(1, 3, 0)), "Only one transformer per factory!") ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(0, 3, 0)), null) } // same as before, but for electricity producers { ASSERT_EQUAL(build_trafo.work(pl, coord3d(5, 1, 0)), null) ASSERT_EQUAL(build_trafo.work(pl, coord3d(5, 0, 0)), "Only one transformer per factory!") } // remove factory, should remove transformer and disconnect power nets { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 0, 0), coord3d(6, 0, 0), powerline, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 2, 0), coord3d(6, 2, 0), powerline, true), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 0, 0)), null) ASSERT_EQUAL(tile_x(5, 1, 0).find_object(mo_transformer_s), null) ASSERT_EQUAL(tile_x(5, 1, 0).find_object(mo_transformer_c), null) ASSERT_FALSE(powerline_x(5, 0, 0).is_connected(powerline_x(5, 2, 0))) ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(5, 0, 0), coord3d(6, 0, 0), "" + wt_power), null) ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(5, 2, 0), coord3d(6, 2, 0), "" + wt_power), null) } ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_powerline_ways() { local pl = player_x(0) local public_pl = player_x(1) local wayremover = command_x(tool_remove_way) local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), road, true), null) { // road-powerline crossing ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), powerline, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", "..1.....", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", ".2A8....", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), "" + wt_power), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", "..1.....", "........", "........", "........", "........" ]) } { // build powerline ending on road ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(2, 2, 0), powerline, true), "") ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } { // build powerline over end of road ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(2, 3, 0), powerline, true), "") ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), powerline, true), "") ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } { // build powerline in the same direction as way below ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 0, 0), coord3d(2, 4, 0), powerline, true), "") ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // remove straight way, add diagonal way ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 0, 0), coord3d(0, 7, 0), road, true), null) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 4, 0), coord3d(6, 4, 0), powerline, true), "") ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) // and the other direction ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 1, 0), coord3d(4, 6, 0), powerline, true), "") ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } ASSERT_EQUAL(wayremover.work(pl, coord3d(0, 7, 0), coord3d(7, 0, 0), "" + wt_road), null) { local road_bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] local power_bridge = bridge_desc_x.get_available_bridges(wt_power)[0] ASSERT_TRUE(road_bridge_desc != null) ASSERT_TRUE(power_bridge != null) // build bridge on flat ground ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 4, 0), coord3d(3, 2, 0), road_bridge_desc), null) ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), power_bridge), "") ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(2, 3, 0), coord3d(3, 3, 0), power_bridge), "") ASSERT_EQUAL(wayremover.work(pl, coord3d(3, 5, 0), coord3d(3, 1, 0), "" + wt_road), null) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_powerline_remove_powerbridge() { local pl = player_x(0) local power_bridge = bridge_desc_x.get_available_bridges(wt_power)[0] ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 3, 0), slope.east), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(4, 3, 0), slope.west), null) ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), power_bridge), null) // try removing the bridge from its centre { ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 1)), "Cannot delete a bridge from its centre") ASSERT_TRUE(tile_x(2, 3, 0).find_object(mo_bridge) != null) } // try removing the bridge from one of its ends { ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 3, 0)), null) ASSERT_TRUE(tile_x(2, 3, 0).find_object(mo_bridge) == null) ASSERT_TRUE(tile_x(3, 3, 1).find_object(mo_bridge) == null) ASSERT_TRUE(tile_x(4, 3, 0).find_object(mo_bridge) == null) ASSERT_TRUE(tile_x(2, 3, 0).find_object(mo_powerline) != null) ASSERT_TRUE(tile_x(4, 3, 0).find_object(mo_powerline) != null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 3, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 3, 0), slope.flat), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(4, 3, 0), slope.flat), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_reservation.nut000066400000000000000000000030331520675460500222170ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests convoi reservation // function test_reservation_clear_ground() { local clear_reservation = command_x(tool_clear_reservation) local pl = player_x(0) // invalid coord { ASSERT_EQUAL(clear_reservation.work(pl, coord3d(-1, -1, 0)), null) } // flat ground { ASSERT_EQUAL(clear_reservation.work(pl, coord3d(0, 0, 0)), null) } RESET_ALL_PLAYER_FUNDS() } function test_reservation_clear_road() { local clear_reservation = command_x(tool_clear_reservation) local pl = player_x(0) local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road, true), null) { ASSERT_EQUAL(clear_reservation.work(pl, coord3d(4, 3, 0)), null) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_reservation_clear_rail() { local clear_reservation = command_x(tool_clear_reservation) local pl = player_x(0) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), rail, true), null) { ASSERT_EQUAL(clear_reservation.work(pl, coord3d(4, 3, 0)), null) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_reservation_clear_rail_occupied() { // TODO } simutrans-124.5/tests/tests/test_scenario.nut000066400000000000000000000255211520675460500214670ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for scenario rules/conditions // function test_scenario_rules_allow_forbid_tool() { local pl = player_x(0) { ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 2, 0)), null) // FIXME this should fail ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 2, 1)), null) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_scenario_rules_allow_forbid_way_tool() { local waybuilder = command_x(tool_build_way) local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local pl = player_x(0) rules.forbid_way_tool_rect(0, tool_build_way, wt_road, road.get_name(), coord(2, 2), coord(5, 5), "Foo Bar") // Fully in forbiden zone { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(5, 5, 0), road, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // Ending in forbidden zone { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 2, 0), coord3d(2, 2, 0), road, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // Starting in forbidden zone { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), road, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // make sure we can build other ways { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), rail, true), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "........", "2A8.....", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), "" + wt_rail), null) } // clean up rules.clear() RESET_ALL_PLAYER_FUNDS() } function test_scenario_rules_allow_forbid_way_tool_rect() { local waybuilder = command_x(tool_build_way) local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local pl = player_x(0) rules.forbid_way_tool_rect(0, tool_build_way, wt_road, "", coord(2, 2), coord(5, 5), "Foo Bar") // Fully in forbiden zone { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(5, 5, 0), road, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // Ending in forbidden zone { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 2, 0), coord3d(2, 2, 0), road, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // Starting in forbidden zone { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), road, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // make sure we can build other ways { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), rail, true), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "........", "2A8.....", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), "" + wt_rail), null) } // clean up rules.clear() RESET_ALL_PLAYER_FUNDS() } function test_scenario_rules_allow_forbid_way_tool_cube() { local waybuilder = command_x(tool_build_way) local setslope = command_x.set_slope local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local pl = player_x(0) rules.forbid_way_tool_cube(0, tool_build_way, wt_road, "", coord3d(2, 2, 1), coord3d(5, 5, 2), "Foo Bar") // build below { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), road, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "2A8.....", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), "" + wt_road), null) } // build into forbidden zone { ASSERT_EQUAL(setslope(pl, coord3d(3, 4, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 3, 0), slope.north), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 0, 0), coord3d(3, 4, 1), road, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(setslope(pl, coord3d(3, 4, 1), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 3, 0), slope.flat), null) } rules.clear() rules.forbid_way_tool_cube(0, tool_build_way, wt_road, "", coord3d(0, 0, 1), coord3d(0, 0, 1), "Foo Bar") // build double height slope through forbidden cube { ASSERT_EQUAL(setslope(pl, coord3d(1, 1, 0), (2*slope.east)), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(1, 1, 0), road, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", ".28.....", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(1, 1, 0), "" + wt_road), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 1, 0), slope.flat), null) } // clean up rules.clear() RESET_ALL_PLAYER_FUNDS() } function test_scenario_rules_allow_forbid_tool_stacked_rect() { local pl = player_x(0) local waybuilder = command_x(tool_build_way) local setslope = command_x.set_slope local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] rules.forbid_way_tool_rect(0, tool_build_way, wt_road, "", coord(1, 1), coord(14, 14), "Foo Bar 1") // build in outer allowed ring, near map border { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 0, 0), coord3d(0, 5, 0), road_desc, false), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5.......", "5.......", "5.......", "1.......", "........", "........" ]) } // build in outer forbidden ring { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 1, 0), coord3d(1, 5, 0), road_desc, false), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5.......", "5.......", "5.......", "1.......", "........", "........" ]) } rules.allow_way_tool_rect(0, tool_build_way, wt_road, "", coord(2, 2), coord(13, 13)) // try building in allowed ring, does work since introdcued allowed rules { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 2, 0), coord3d(2, 5, 0), road_desc, false), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5..6A8..", "5.69....", "5.5.....", "1.1.....", "........", "........" ]) } // try building from inside allowed ring to forbidden rect, must fail { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 1, 0), coord3d(2, 5, 0), road_desc, false), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5..6A8..", "5.69....", "5.5.....", "1.1.....", "........", "........" ]) } // try building from inside allowed ring to outside, must fail => we cannot cross border { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 0, 0), coord3d(2, 5, 0), road_desc, false), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5..6A8..", "5.69....", "5.5.....", "1.1.....", "........", "........" ]) } rules.clear() ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 0, 0), coord3d(0, 5, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 2, 0), coord3d(2, 5, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_scenario_rules_allow_forbid_tool_stacked_cube() { local pl = player_x(0) local waybuilder = command_x(tool_build_way) local setslope = command_x.set_slope local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] rules.forbid_way_tool_cube(0, tool_build_way, wt_road, "", coord3d(1, 1, 0), coord3d(14, 14, 0), "Foo Bar 1") // build in outer allowed ring, near map border { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 0, 0), coord3d(0, 5, 0), road_desc, false), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5.......", "5.......", "5.......", "1.......", "........", "........" ]) } // build in outer forbidden ring { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 1, 0), coord3d(1, 5, 0), road_desc, false), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5.......", "5.......", "5.......", "1.......", "........", "........" ]) } rules.allow_way_tool_rect(0, tool_build_way, wt_road, "", coord(2, 2), coord(13, 13)) // try building in allowed ring, does work since introdcued allowed rules { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 2, 0), coord3d(2, 5, 0), road_desc, false), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5..6A8..", "5.69....", "5.5.....", "1.1.....", "........", "........" ]) } // try building from inside allowed ring to forbidden rect, must fail { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 1, 0), coord3d(2, 5, 0), road_desc, false), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5..6A8..", "5.69....", "5.5.....", "1.1.....", "........", "........" ]) } // try building from inside allowed ring to outside, must fail => we cannot cross border { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 0, 0), coord3d(2, 5, 0), road_desc, false), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "6AAAA8..", "5.......", "5..6A8..", "5.69....", "5.5.....", "1.1.....", "........", "........" ]) } rules.clear() ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 0, 0), coord3d(0, 5, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 2, 0), coord3d(2, 5, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_sign.nut000066400000000000000000000657471520675460500206420ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for signs/signals // function test_sign_build_oneway() { local pl = player_x(0) local public_pl = player_x(1) local wayremover = command_x(tool_remove_way) local remover = command_x(tool_remover) local road = way_desc_x("dirt_road") local rail = way_desc_x("sand_track") local sign = sign_desc_x.get_available_signs(wt_road).filter(@(idx, sign) sign.is_one_way())[0] ASSERT_TRUE(road != null) ASSERT_TRUE(sign != null) ASSERT_TRUE(sign.is_one_way()) // on ground without way { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } // in the air { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 1), sign), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } // build way ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), road, true), null) { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_signal), null) local s = tile_x(2, 3, 0).find_object(mo_roadsign) ASSERT_TRUE(s != null) ASSERT_TRUE(s.is_valid()) ASSERT_TRUE(s.can_pass(pl)) ASSERT_TRUE(s.can_pass(public_pl)) local w = tile_x(2, 3, 0).find_object(mo_way) ASSERT_TRUE(w != null) ASSERT_EQUAL(w.get_dirs(), dir.northsouth) ASSERT_EQUAL(w.get_dirs_masked(), dir.south) // change direction of sign ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) ASSERT_TRUE(s.is_valid()) ASSERT_TRUE(s.can_pass(pl)) ASSERT_TRUE(s.can_pass(public_pl)) ASSERT_EQUAL(w.get_dirs(), dir.northsouth) ASSERT_EQUAL(w.get_dirs_masked(), dir.north) // change direction again, should have original direction ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) ASSERT_TRUE(s.is_valid()) ASSERT_TRUE(s.can_pass(pl)) ASSERT_TRUE(s.can_pass(public_pl)) ASSERT_EQUAL(w.get_dirs(), dir.northsouth) ASSERT_EQUAL(w.get_dirs_masked(), dir.south) } // build road/rail crossing over sign, should succeed if crossing is possible without sign { ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), rail, true), null) ASSERT_WAY_PATTERN_MASKED(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", "..4.....", "..1.....", "........", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "........", "........", "........", ".2A8....", "........", "........", "........", "........" ]) local w = tile_x(2, 3, 0).find_object(mo_way) ASSERT_TRUE(w.is_crossing()) // change direction of sign on rail-road crossing, should succeed ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) ASSERT_WAY_PATTERN_MASKED(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", "..1.....", "..1.....", "........", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "........", "........", "........", ".2A8....", "........", "........", "........", "........" ]) // and remove rail ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), "" + wt_rail), null) } // build road/road crossing over sign, should succeed { ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), road, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", ".2F8....", "..1.....", "........", "........", "........" ]) local w = tile_x(2, 3, 0).find_object(mo_way) ASSERT_FALSE(w.is_crossing()) ASSERT_WAY_PATTERN_MASKED(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", ".2B8....", "..1.....", "........", "........", "........" ]) // change direction of sign on road crossing, should fail local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) } catch (e) { ASSERT_EQUAL(e, "Tool has no effects") error_caught = true } ASSERT_TRUE(error_caught) } // remove sign, try to build again (should fail because of crossing) { ASSERT_EQUAL(remover.work(pl, coord3d(2, 3, 0)), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", ".2F8....", "..1.....", "........", "........", "........" ]) ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_roadsign), null) local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) } catch (e) { ASSERT_EQUAL(e, "Tool has no effects") error_caught = true } ASSERT_TRUE(error_caught) } ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), "" + wt_road), null) // Cannot remove signs of other players (except public player) { ASSERT_EQUAL(command_x.build_sign_at(public_pl, coord3d(2, 2, 0), sign), null) ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 2, 0)), "Der Besitzer erlaubt das Entfernen nicht") ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(2, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(2, 3, 0)), null) } // remove stuff ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_sign_build_trafficlight() { local pl = player_x(0) local public_pl = player_x(1) local wayremover = command_x(tool_remove_way) local remover = command_x(tool_remover) local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local trafficlight = sign_desc_x.get_available_signs(wt_road).filter(@(idx, sign) sign.is_traffic_light())[0] ASSERT_TRUE(trafficlight != null) // on ground { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), trafficlight), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), road, true), null) // end of way { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 1, 0), trafficlight), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } // 2-way { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), trafficlight), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(2, 3, 0), road, true), null) // 3-way { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), trafficlight), null) local tl = tile_x(2, 3, 0).find_object(mo_roadsign) ASSERT_TRUE(tl != null) ASSERT_TRUE(tl.can_pass(pl)) ASSERT_TRUE(tl.can_pass(public_pl)) ASSERT_TRUE(tl.get_desc().is_traffic_light()) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", ".2D.....", "..1.....", "........", "........", "........" ]) ASSERT_EQUAL(tile_x(2, 3, 0).get_way_dirs_masked(wt_road), dir.northsouthwest) } // Make 4-way from 3-way { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(3, 3, 0), road, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", ".2F8....", "..1.....", "........", "........", "........" ]) ASSERT_EQUAL(tile_x(2, 3, 0).get_way_dirs_masked(wt_road), dir.all) } // remove traffic light on crossing via wayremover { ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), "" + wt_road), null) ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_roadsign), null) } ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_sign_remove_trafficlight() { local pl = player_x(0) local public_pl = player_x(1) local wayremover = command_x(tool_remove_way) local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local trafficlight = sign_desc_x.get_available_signs(wt_road).filter(@(idx, sign) sign.is_traffic_light())[0] ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 4, 0), coord3d(5, 6, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 5, 0), coord3d(6, 5, 0), road, true), null) ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 2, 0), trafficlight), null) ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(5, 5, 0), trafficlight), null) // Note that both traffic lights must have the same direction for the test to work // So the second traffic light must not change direction between the two wayremover calls { ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_road), null) ASSERT_EQUAL(wayremover.work(pl, coord3d(4, 5, 0), coord3d(6, 5, 0), "" + wt_road), null) ASSERT_EQUAL(tile_x(2, 2, 0).find_object(mo_signal), null) ASSERT_EQUAL(tile_x(5, 5, 0).find_object(mo_signal), null) } ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), "" + wt_road), null) ASSERT_EQUAL(wayremover.work(pl, coord3d(5, 4, 0), coord3d(5, 6, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_sign_build_private_way() { local pl = player_x(0) local public_pl = player_x(1) local wayremover = command_x(tool_remove_way) local remover = command_x(tool_remover) local road = way_desc_x("dirt_road") local rail = way_desc_x("sand_track") local sign = sign_desc_x.get_available_signs(wt_road).filter(@(idx, sign) sign.is_private_way())[0] ASSERT_TRUE(road != null) ASSERT_TRUE(sign != null) ASSERT_TRUE(sign.is_private_way()) // on ground { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } // in the air { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 1), sign), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) } // build way ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), road, true), null) { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_signal), null) local s = tile_x(2, 3, 0).find_object(mo_roadsign) ASSERT_TRUE(s != null) ASSERT_TRUE(s.is_valid()) ASSERT_TRUE(s.can_pass(pl)) ASSERT_FALSE(s.can_pass(public_pl)) local w = tile_x(2, 3, 0).find_object(mo_way) ASSERT_TRUE(w != null) ASSERT_EQUAL(w.get_dirs(), dir.northsouth) ASSERT_EQUAL(w.get_dirs_masked(), dir.northsouth) // change direction of sign ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) ASSERT_TRUE(s.is_valid()) ASSERT_TRUE(s.can_pass(pl)) ASSERT_FALSE(s.can_pass(public_pl)) ASSERT_EQUAL(w.get_dirs(), dir.northsouth) ASSERT_EQUAL(w.get_dirs_masked(), dir.northsouth) // change direction again, should have original direction ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) ASSERT_TRUE(s.is_valid()) ASSERT_TRUE(s.can_pass(pl)) ASSERT_FALSE(s.can_pass(public_pl)) ASSERT_EQUAL(w.get_dirs(), dir.northsouth) ASSERT_EQUAL(w.get_dirs_masked(), dir.northsouth) } // build road/rail crossing over sign, should succeed if crossing is possible without sign { ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), rail, true), null) ASSERT_WAY_PATTERN_MASKED(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", "..5.....", "..1.....", "........", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "........", "........", "........", ".2A8....", "........", "........", "........", "........" ]) ASSERT_TRUE(tile_x(2, 3, 0).find_object(mo_way).is_crossing()) // and remove rail ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), "" + wt_rail), null) } // build road/road crossing over sign, should succeed { ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), road, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", ".2F8....", "..1.....", "........", "........", "........" ]) local w = tile_x(2, 3, 0).find_object(mo_way) ASSERT_FALSE(w.is_crossing()) ASSERT_EQUAL(w.get_dirs_masked(), dir.all) // change direction of sign on road crossing, should fail local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) } catch (e) { ASSERT_EQUAL(e, "Tool has no effects") error_caught = true } ASSERT_TRUE(error_caught) } // remove sign, try to build again (should fail because of crossing) { ASSERT_EQUAL(remover.work(pl, coord3d(2, 3, 0)), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", ".2F8....", "..1.....", "........", "........", "........" ]) ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_roadsign), null) local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) } catch (e) { ASSERT_EQUAL(e, "Tool has no effects") error_caught = true } ASSERT_TRUE(error_caught) } // remove stuff ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), "" + wt_road), null) ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_sign_build_signal() { local pl = player_x(0) local public_pl = player_x(1) local wayremover = command_x(tool_remove_way) local remover = command_x(tool_remover) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local signal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_signal())[0] local presignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_pre_signal())[0] local priosignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_priority_signal())[0] local lbsignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_longblock_signal())[0] local chsignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_choose_sign())[0] local eocsignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_end_choose_signal())[0] local all_signals = [ signal, presignal, priosignal, lbsignal, chsignal, eocsignal ] foreach (s in all_signals) { ASSERT_TRUE(s != null) } { // Build signs on flat ground, should fail foreach (s in all_signals) { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), s), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_signal), null) } } ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 0, 0), coord3d(2, 7, 0), road, true), null) { // Rail signals on road, should fail foreach (s in all_signals) { local error_caught = false try { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), s), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Tool has no effects") } ASSERT_TRUE(error_caught) ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_signal), null) } } ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 0, 0), coord3d(2, 7, 0), "" + wt_road), null) // build signals { foreach (i, s in all_signals) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(2, i, 0), rail, true), null) ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(1, i, 0), s), null) if (s == eocsignal) { ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_roadsign) != null) // for end-of-choose } else { ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_signal) != null) } } ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "2A8.....", "2A8.....", "2A8.....", "2A8.....", "2A8.....", "2A8.....", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "2A8.....", "2A8.....", "2A8.....", "2A8.....", "2A8.....", "2A8.....", "........", "........" ]) } // make signals directional { foreach (i, s in all_signals) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(2, i, 0), rail, true), null) ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(1, i, 0), s), null) if (s == eocsignal) { ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_roadsign) != null) // for end-of-choose } else { ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_signal) != null) } } ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "2A8.....", "2A8.....", "2A8.....", "2A8.....", "2A8.....", "2A8.....", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "288.....", "288.....", "288.....", "288.....", "288.....", "2A8.....", "........", "........" ]) } // signals into different direction { foreach (i, s in all_signals) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(2, i, 0), rail, true), null) ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(1, i, 0), s), null) if (s == eocsignal) { ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_roadsign) != null) // for end-of-choose } else { ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_signal) != null) } } ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "2A8.....", "2A8.....", "2A8.....", "2A8.....", "2A8.....", "2A8.....", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "228.....", "228.....", "228.....", "228.....", "228.....", "2A8.....", "........", "........" ]) } // build way across signal { ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 0, 0), coord3d(1, all_signals.len()-1, 0), rail, true), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "2E8.....", "2F8.....", "2F8.....", "2F8.....", "2F8.....", "2B8.....", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "268.....", "278.....", "278.....", "278.....", "278.....", "2B8.....", "........", "........" ]) } // remove everything { foreach (i, s in all_signals) { ASSERT_EQUAL(wayremover.work(pl, coord3d(0, i, 0), coord3d(2, i, 0), "" + wt_rail), null) } ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 0, 0), coord3d(1, all_signals.len()-1, 0), "" + wt_rail), null) } // build signals, dir 2 { foreach (i, s in all_signals) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(i, 0, 0), coord3d(i, 2, 0), rail, true), null) ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(i, 1, 0), s), null) if (s == eocsignal) { ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_roadsign) != null) // for end-of-choose } else { ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_signal) != null) } } ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "444444..", "555555..", "111111..", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "444444..", "555555..", "111111..", "........", "........", "........", "........", "........" ]) } // make signals directional, dir 2 { foreach (i, s in all_signals) { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(i, 1, 0), s), null) if (s == eocsignal) { ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_roadsign) != null) // for end-of-choose } else { ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_signal) != null) } } ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "444444..", "555555..", "111111..", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "444444..", "444445..", "111111..", "........", "........", "........", "........", "........" ]) } // signals into different direction, dir 2 { foreach (i, s in all_signals) { ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(i, 1, 0), s), null) if (s == eocsignal) { ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_roadsign) != null) // for end-of-choose } else { ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_signal) != null) } } ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "444444..", "555555..", "111111..", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "444444..", "111115..", "111111..", "........", "........", "........", "........", "........" ]) } // build way across signal { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 1, 0), coord3d(all_signals.len()-1, 1, 0), rail, true), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "444444..", "7FFFFD..", "111111..", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "444444..", "3BBBBD..", "111111..", "........", "........", "........", "........", "........" ]) } // remove everything again { foreach (i, s in all_signals) { ASSERT_EQUAL(wayremover.work(pl, coord3d(i, 2, 0), coord3d(i, 0, 0), "" + wt_rail), null) } ASSERT_EQUAL(wayremover.work(pl, coord3d(0, 1, 0), coord3d(all_signals.len()-1, 1, 0), "" + wt_rail), null) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_sign_build_signal_multiple() { local pl = player_x(0) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local signal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_signal())[0] // starts on way end, not possible { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), rail, true), null) ASSERT_EQUAL(command_x(tool_build_roadsign).work(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), signal.get_name()), "Hier kann kein\nSignal aufge-\nstellt werden!\n") for (local x = 2; x <= 7; ++x) { ASSERT_EQUAL(tile_x(x, 2, 0).find_object(mo_signal), null) } ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "........", "........", "2AAAAAA8", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), "" + wt_rail), null) } // FIXME click-and-drag for tools using script is not supported { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), rail, true), null) local error_caught = false try { command_x(tool_build_roadsign).work(pl, coord3d(1, 2, 0), coord3d(6, 2, 0), signal.get_name()) } catch (e) { ASSERT_EQUAL(e, "First click has side effects") error_caught = true } ASSERT_EQUAL(error_caught, true) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), "" + wt_rail), null) } RESET_ALL_PLAYER_FUNDS() } function test_sign_replace_signal() { local pl = player_x(0) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local signal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_signal())[0] local presignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_pre_signal())[0] // preconditions ASSERT_TRUE(signal != null) ASSERT_TRUE(presignal != null) ASSERT_TRUE(rail != null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(6, 3, 0), rail, true), null) ASSERT_EQUAL(command_x(tool_build_roadsign).work(pl, coord3d(5, 3, 0), signal.name), null) // replace two-directional signals { local old_cash = pl.current_cash * 100 local old_maint = pl.current_maintenance command_x(tool_build_roadsign).work(pl, coord3d(5, 3, 0), presignal.name) local s = sign_x(5, 3, 0) ASSERT_TRUE(s != null) ASSERT_EQUAL(s.desc.name, presignal.name) ASSERT_EQUAL(pl.current_cash * 100, old_cash - signal.cost - presignal.cost) ASSERT_EQUAL(pl.current_maintenance, old_maint + signal.maintenance - presignal.maintenance) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "........", "........", "........", "....2A8.", "........", "........", "........", "........" ]) } // make 2-way signal 1-way ASSERT_EQUAL(command_x(tool_build_roadsign).work(pl, coord3d(5, 3, 0), presignal.name), null) // and one-directional signals { local old_cash = pl.current_cash * 100 local old_maint = pl.current_maintenance command_x(tool_build_roadsign).work(pl, coord3d(5, 3, 0), signal.name) local s = sign_x(5, 3 , 0) ASSERT_TRUE(s != null) ASSERT_EQUAL(s.desc.name, signal.name) ASSERT_EQUAL(pl.current_cash * 100, old_cash - signal.cost - presignal.cost) ASSERT_EQUAL(pl.current_maintenance, old_maint - signal.maintenance + presignal.maintenance) ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), [ "........", "........", "........", "....288.", "........", "........", "........", "........" ]) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(6, 3, 0), coord3d(4, 3, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_sign_signal_when_player_removed() { local pl = player_x(0) local public_pl = player_x(1) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local signal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_signal())[0] ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(6, 3, 0), rail, true), null) ASSERT_EQUAL(command_x(tool_build_roadsign).work(public_pl, coord3d(5, 3, 0), signal.get_name()), null) { world.remove_player(pl) ASSERT_EQUAL(tile_x(5, 3, 0).find_object(mo_way), null) ASSERT_TRUE(tile_x(5, 3, 0).find_object(mo_signal) != null) } ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(5, 3, 0)), null) ASSERT_TRUE(tile_x(5, 3, 0).find_object(mo_signal) == null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_slope.nut000066400000000000000000000175231520675460500210110ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Test set_/can_set_slope // function test_slope_to_dir() { local slope_to_dir = {} slope_to_dir[slope.north] <- dir.south slope_to_dir[slope.east ] <- dir.west slope_to_dir[slope.south] <- dir.north slope_to_dir[slope.west ] <- dir.east slope_to_dir[2*slope.north] <- dir.south slope_to_dir[2*slope.east ] <- dir.west slope_to_dir[2*slope.south] <- dir.north slope_to_dir[2*slope.west ] <- dir.east for (local sl = 0; sl < 81; ++sl) { if (sl in slope_to_dir) { ASSERT_EQUAL(slope.to_dir(sl), slope_to_dir[sl]) } else { ASSERT_EQUAL(slope.to_dir(sl), dir.none) } } } function test_slope_can_set() { local pl = player_x(0) local pos = coord3d(2, 3, 0) for (local sl = 0; sl < slope.raised; ++sl) { for (local target_sl = 0; target_sl < slope.raised; ++target_sl) { command_x.set_slope(pl, pos, sl) ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), sl) RESET_ALL_PLAYER_FUNDS() local expected = "" ASSERT_EQUAL(command_x.can_set_slope(pl, pos, sl), expected) local sq = square_x(2, 3) ASSERT_TRUE(sq != null && sq.is_valid()) ASSERT_EQUAL(sq.get_climate(), cl_mediterran) local tile = sq.get_tile_at_height(0) ASSERT_TRUE(tile != null && tile.is_valid()) ASSERT_EQUAL(tile.get_slope(), sl) ASSERT_EQUAL(pl.get_current_cash(), 200000) // get_current_cash is in credits (returns float) ASSERT_EQUAL(pl.get_current_net_wealth(), 200000*100) // get_current_net_wealth is in 1/100 credits } } // reset to normal slope command_x.set_slope(pl, pos + coord3d(0, 0, 1), slope.all_down_slope) command_x.set_slope(pl, pos, slope.all_down_slope) RESET_ALL_PLAYER_FUNDS() ASSERT_EQUAL(command_x.can_set_slope(pl, pos + coord3d(0, 0, 1), slope.all_up_slope), "") ASSERT_EQUAL(command_x.can_set_slope(pl, pos - coord3d(0, 0, 1), slope.all_down_slope), "") ASSERT_EQUAL(pl.get_current_cash(), 200000) // get_current_cash is in credits (returns float) ASSERT_EQUAL(pl.get_current_net_wealth(), 200000*100) // get_current_net_wealth is in 1/100 credits RESET_ALL_PLAYER_FUNDS() } function test_slope_set_and_restore() { local pl = player_x(0) local setslope = command_x.set_slope local restoreslope = command_x(tool_restoreslope) { ASSERT_EQUAL(setslope(pl, coord3d(2, 3, 0), slope.north), null) ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), slope.north) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, 0), slope.south), null) ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), slope.south) ASSERT_EQUAL(restoreslope.work(pl, coord3d(2, 3, 0)), null) ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), slope.flat) } { ASSERT_EQUAL(setslope(pl, coord3d(2, 3, 0), slope.all_up_slope), null) ASSERT_TRUE(tile_x(2, 3, 0).is_valid()) ASSERT_TRUE(tile_x(2, 3, 1).is_valid()) ASSERT_EQUAL(tile_x(2, 3, 1).get_slope(), slope.flat) // fails as expected because ground is 1 unit higher ASSERT_EQUAL(setslope(pl, coord3d(2, 3, 0), slope.all_up_slope), "") // TODO check tile height ASSERT_EQUAL(restoreslope.work(pl, coord3d(2, 3, 0)), "") ASSERT_TRUE(tile_x(2, 3, 0).is_valid()) ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), slope.flat) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, 1), slope.all_down_slope), null) ASSERT_EQUAL(tile_x(2, 3, 1).get_slope(), slope.flat) } RESET_ALL_PLAYER_FUNDS() } function test_slope_set_near_map_border() { local pl = player_x(0) local setslope = command_x.set_slope // map edge { for (local sl = 0; sl < slope.raised; ++sl) { ASSERT_EQUAL(setslope(pl, coord3d(0, 3, 0), sl), "Zu nah am Kartenrand") } } // map corner { for (local sl = 0; sl < slope.raised; ++sl) { ASSERT_EQUAL(setslope(pl, coord3d(0, 0, 0), sl), "Zu nah am Kartenrand") } } RESET_ALL_PLAYER_FUNDS() } function test_slope_max_height_diff() { local pl = player_x(0) local setslope = command_x.set_slope // build upwards, height difference = 4 { ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 1), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 2), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 3), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 4), slope.all_up_slope), "Maximum tile height difference reached.") } // diagonally, the height difference is unlimited (technically limited to 2*max_diff) { ASSERT_EQUAL(setslope(pl, coord3d(2, 3, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, -1), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, -2), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, -3), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, -4), slope.all_down_slope), "Maximum tile height difference reached.") } // and clean up ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 4), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 3), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 2), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 1), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, -4), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, -3), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, -2), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(2, 3, -1), slope.all_up_slope), null) RESET_ALL_PLAYER_FUNDS() } function test_slope_get_price() { local pl = player_x(0) for (local sl = 0; sl < slope.raised; ++sl) { ASSERT_EQUAL(command_x.slope_get_price(slope.flat), 2000 * 100) } local restore_slope = 84 ASSERT_EQUAL(command_x.slope_get_price(restore_slope), 1500 * 100) ASSERT_EQUAL(pl.get_current_cash(), 200000) // get_current_cash is in credits (returns float) ASSERT_EQUAL(pl.get_current_net_wealth(), 200000*100) // get_current_net_wealth is in 1/100 credits RESET_ALL_PLAYER_FUNDS() } function test_slope_restore_on_foundation() { local pl = player_x(0) ASSERT_EQUAL(command_x(tool_add_city).work(player_x(1), coord3d(8, 8, 0), "0"), null) ASSERT_EQUAL(command_x(tool_build_house).work(pl, coord3d(4, 2, 0), "11RES_01_23"), null) { ASSERT_EQUAL(command_x(tool_restoreslope).work(pl, coord3d(4, 2, 0)), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(1), coord3d(8, 8, 0)), null); // remove city ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(1), coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_road), null); RESET_ALL_PLAYER_FUNDS(); } function test_slope_restore_on_bridge() { local pl = player_x(0) local rail_bridge = bridge_desc_x.get_available_bridges(wt_rail)[0] ASSERT_TRUE(rail_bridge != null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(4, 2, 0), slope.south), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(4, 4, 0), slope.north), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(4, 2, 0), rail_bridge), null) { ASSERT_EQUAL(command_x(tool_restoreslope).work(pl, coord3d(4, 2, 0)), "No suitable ground!") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(4, 2, 0), slope.flat), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(4, 4, 0), slope.flat), null) RESET_ALL_PLAYER_FUNDS() } function test_slope_restore_on_label() { local pl = player_x(0) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(4, 2, 0), slope.south), null) ASSERT_EQUAL(label_x.create(coord(4, 2), pl, "foo"), null) { ASSERT_EQUAL(command_x(tool_restoreslope).work(pl, coord3d(4, 2, 0)), "Tile not empty.") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(4, 2, 0), slope.flat), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_terraform.nut000066400000000000000000000517301520675460500216660ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for terraforming terrain // function test_terraform_raise_lower_land() { // TODO check terrain height local err = command_x.grid_raise(player_x(0), coord(3, 2)) ASSERT_EQUAL(err, "Zu nah am Kartenrand") // TODO Fix error message? ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(3, 2, 100)), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(3, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(3, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(3, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(3, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(3, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(3, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(3, 2, 1)), null) // clean up ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(3, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(3, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_terraform_raise_lower_land_at_map_border() { // TODO Check terrain height ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord(0, 0)), "Zu nah am Kartenrand") ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(0, 0, 100)), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(0, 0, 0)), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(0, 0, 0)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(0, 0, 0)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(0, 0, 1)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(0, 0, 1)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(0, 0, 1)), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(0, 0, 1)), null) // clean up ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(0, 0, 0)), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(0, 0, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_terraform_raise_lower_land_at_water_center() { local clim = command_x(tool_set_climate) { clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water) // raise center piece local err = command_x.grid_lower(player_x(0), coord3d(3, 3, 0)) ASSERT_EQUAL(err, "") ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) } { clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water) // lower center piece local err = command_x.grid_lower(player_x(0), coord3d(3, 3, 0)) ASSERT_EQUAL(err, "") ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) } RESET_ALL_PLAYER_FUNDS() } function test_terraform_raise_lower_land_at_water_corner() { local clim = command_x(tool_set_climate) // FIXME inconsistencies with raising/lowering depending on direction // raise north-west corner { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(2, 2, 0)), "") ASSERT_FALSE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) } // lower north-west corner { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(2, 2, 0)), "") ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) } // raise south-east corner { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(2, 4, 0)), null) ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_FALSE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(2, 4, 0)), null) } // lower south-east corner { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(2, 4, 0)), null) ASSERT_FALSE(tile_x(2, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(2, 3, 0).is_water()) ASSERT_FALSE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(2, 4, 0)), null) } // raise south-west corner { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(4, 4, 0)), null) ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(4, 4, 0)), null) } // lower south-west corner { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(4, 4, 0)), null) ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(2, 3, 0).is_water()) ASSERT_FALSE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(4, 4, 0)), null) } // raise north-west corner { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(4, 2, 0)), null) ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(4, 2, 0)), null) } // lower north-west corner { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(4, 2, 0)), null) ASSERT_FALSE(tile_x(2, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(2, 3, 0).is_water()) ASSERT_FALSE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(4, 2, 0)), null) } // clean up ASSERT_EQUAL(clim.work(player_x(0), coord3d(3, 2, 0), coord3d(2, 3, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_terraform_raise_lower_land_at_water_edge() { local clim = command_x(tool_set_climate) // FIXME inconsistencies with raising/lowering depending on direction // raise north edge { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(3, 2, 0)), "") ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) } // lower north edge { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(3, 2, 0)), "") ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) } // raise east edge { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(4, 3, 0)), null) ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_FALSE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(4, 3, 0)), null) } // lower east edge { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(4, 3, 0)), null) ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_FALSE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(4, 3, 0)), null) } // raise south edge { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(3, 4, 0)), null) ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(2, 3, 0).is_water()) ASSERT_FALSE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(3, 4, 0)), null) } // lower south edge { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(3, 4, 0)), null) ASSERT_FALSE(tile_x(2, 2, 0).is_water()) ASSERT_FALSE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(2, 3, 0).is_water()) ASSERT_FALSE(tile_x(3, 3, 0).is_water()) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(3, 4, 0)), null) } // raise west edge { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_raise(player_x(0), coord3d(2, 3, 0)), "") ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_FALSE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) } // lower west edge { ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) ASSERT_EQUAL(command_x.grid_lower(player_x(0), coord3d(2, 3, 0)), "") ASSERT_TRUE(tile_x(2, 2, 0).is_water()) ASSERT_TRUE(tile_x(3, 2, 0).is_water()) ASSERT_TRUE(tile_x(2, 3, 0).is_water()) ASSERT_TRUE(tile_x(3, 3, 0).is_water()) } // clean up ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS() } function test_terraform_raise_lower_land_below_way() { local pl = player_x(0) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local setslope = command_x.set_slope ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) // raise below way { ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 2, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 3, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 4, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 5, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 2, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 3, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 4, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(5, 5, 0)), "Tile not empty.") } // and lower { ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 2, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 3, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 4, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 5, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 2, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 3, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 4, 0)), "Tile not empty.") ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(5, 5, 0)), "Tile not empty.") } // set slope up { ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.south), "") ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), 2*slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.north), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.north), "") ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), 2*slope.north), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.all_down_slope), null) } // set slope down { ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 2, 0), slope.all_down_slope), "") ASSERT_EQUAL(setslope(pl, coord3d(4, 2, -1), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(4, 4, 0), slope.all_down_slope), "") ASSERT_EQUAL(setslope(pl, coord3d(4, 4, -1), slope.all_up_slope), null) } // non-dead-end, should fail for all slopes { for (local sl = slope.flat+1; sl <= slope.all_down_slope; ++sl) { if (sl != slope.raised && sl != 81) { ASSERT_EQUAL(setslope(pl, coord3d(4, 3, 0), sl), "Tile not empty.") } } } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } /// Helper function: Raise / lower land along the edge of a rectangular region function terraform_volcano(pl, pos, size, h) { local raise = h > 0 local tool = raise ? command_x.grid_raise : command_x.grid_lower for (local dz = 0; raise ? dzh; dz += raise?1:-1) { for (local i = 0; i 0) { sleep() } { ASSERT_EQUAL(from_halt.convoys[0], 1) ASSERT_EQUAL(from_halt.waiting[0], 0) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x.passenger, to_halt), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.passenger, coord3d(3, 2, 0)), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.passenger, coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 30) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 30) ASSERT_EQUAL(from_halt.arrived[0], 0) } while (to_halt.convoys[0] == 0) { sleep() } // make sure unloading has finished while (to_halt.arrived[0] < 30) { sleep() } { ASSERT_EQUAL(to_halt.waiting[0], 0) ASSERT_EQUAL(to_halt.happy[0], 0) ASSERT_EQUAL(to_halt.unhappy[0], 0) ASSERT_EQUAL(to_halt.noroute[0], 0) ASSERT_EQUAL(to_halt.walked[0], 0) ASSERT_EQUAL(to_halt.departed[0], 0) ASSERT_EQUAL(to_halt.arrived[0], 30) } // clean up cnv.destroy(pl) sleep() sleep() // make sure the convoy is destroyed ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 7, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 8, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 8, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_transport_mail_valid_route() { local pl = player_x(0) ASSERT_EQUAL(command_x(tool_build_way).work(pl, coord3d(4, 2, 0), coord3d(4, 8, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 2, 0), "PostStop"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 7, 0), "PostStop"), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 8, 0), building_desc_x("CarDepot")), null) local depot = depot_x(4, 8, 0) local from_halt = halt_x.get_halt(coord3d(4, 7, 0), pl) local to_halt = halt_x.get_halt(coord3d(4, 2, 0), pl) // create vehicle ... depot.append_vehicle(pl, convoy_x(0), vehicle_desc_x("Posttransporter")) local cnv = depot.get_convoy_list()[0] // ... with simple 2-entry schedule ... cnv.change_schedule(pl, create_simple_schedule(wt_road, [ coord3d(4, 7, 0), coord3d(4, 2, 0) ])) // and start the convoy depot.start_all_convoys(pl) // make sure that the graph linking and all halts is updated while(!halt_x.is_rerouting_finished()) { sleep() } { ASSERT_EQUAL(world.generate_goods(coord(3, 7), coord(3, 2), good_desc_x.mail, 30), 1) // 1 == OK } sleep() sleep() { ASSERT_EQUAL(from_halt.waiting[0], 30) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x.mail, to_halt), 30) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.mail, coord3d(3, 2, 0)), 30) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.mail, coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 0) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 0) ASSERT_EQUAL(from_halt.arrived[0], 0) ASSERT_EQUAL(from_halt.convoys[0], 0) } while (from_halt.convoys[0] == 0) { sleep() } // Loading is not instant, so we need to make sure loading has finished while (from_halt.waiting[0] > 0) { sleep() } { ASSERT_EQUAL(from_halt.convoys[0], 1) ASSERT_EQUAL(from_halt.waiting[0], 0) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x.mail, to_halt), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.mail, coord3d(3, 2, 0)), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.mail, coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 0) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 30) ASSERT_EQUAL(from_halt.arrived[0], 0) } while (to_halt.convoys[0] == 0) { sleep() } // make sure unloading has finished while (to_halt.arrived[0] < 30) { sleep() } { ASSERT_EQUAL(to_halt.waiting[0], 0) ASSERT_EQUAL(to_halt.happy[0], 0) ASSERT_EQUAL(to_halt.unhappy[0], 0) ASSERT_EQUAL(to_halt.noroute[0], 0) ASSERT_EQUAL(to_halt.walked[0], 0) ASSERT_EQUAL(to_halt.departed[0], 0) ASSERT_EQUAL(to_halt.arrived[0], 30) } // clean up cnv.destroy(pl) sleep() sleep() // make sure the convoy is destroyed ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 7, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 8, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 8, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_transport_freight_valid_route() { local pl = player_x(0) ASSERT_EQUAL(command_x(tool_build_way).work(pl, coord3d(4, 2, 0), coord3d(4, 8, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_way).work(pl, coord3d(4, 7, 0), coord3d(5, 7, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 2, 0), "CarStop"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(5, 7, 0), "CarStop"), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 8, 0), building_desc_x("CarDepot")), null) local depot = depot_x(4, 8, 0) local from_halt = halt_x.get_halt(coord3d(5, 7, 0), pl) local to_halt = halt_x.get_halt(coord3d(4, 2, 0), pl) // create vehicle ... depot.append_vehicle(pl, convoy_x(0), vehicle_desc_x("Kohletransporter")) local cnv = depot.get_convoy_list()[0] // ... with simple 2-entry schedule ... cnv.change_schedule(pl, create_simple_schedule(wt_road, [ coord3d(5, 7, 0), coord3d(4, 2, 0) ])) // and start the convoy depot.start_all_convoys(pl) // make sure that the graph linking and all halts is updated while(!halt_x.is_rerouting_finished()) { sleep() } { ASSERT_EQUAL(world.generate_goods(coord(3, 7), coord(3, 2), good_desc_x("Kohle"), 18), 1) // 1 == OK } sleep() sleep() { ASSERT_EQUAL(from_halt.waiting[0], 18) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x("Kohle"), to_halt), 18) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x("Kohle"), coord3d(3, 2, 0)), 18) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x("Kohle"), coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 0) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 0) ASSERT_EQUAL(from_halt.arrived[0], 0) ASSERT_EQUAL(from_halt.convoys[0], 0) } while (from_halt.convoys[0] == 0) { sleep() } // Loading is not instant, so we need to make sure loading has finished while (from_halt.waiting[0] > 0) { sleep() } { ASSERT_EQUAL(from_halt.convoys[0], 1) ASSERT_EQUAL(from_halt.waiting[0], 0) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x("Kohle"), to_halt), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x("Kohle"), coord3d(3, 2, 0)), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x("Kohle"), coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 0) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 18) ASSERT_EQUAL(from_halt.arrived[0], 0) } while (to_halt.convoys[0] == 0) { sleep() } // make sure unloading has finished while (to_halt.arrived[0] < 18) { sleep() } { ASSERT_EQUAL(to_halt.waiting[0], 0) ASSERT_EQUAL(to_halt.happy[0], 0) ASSERT_EQUAL(to_halt.unhappy[0], 0) ASSERT_EQUAL(to_halt.noroute[0], 0) ASSERT_EQUAL(to_halt.walked[0], 0) ASSERT_EQUAL(to_halt.departed[0], 0) ASSERT_EQUAL(to_halt.arrived[0], 18) } // clean up cnv.destroy(pl) sleep() sleep() // make sure the convoy is destroyed ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(5, 7, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 8, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 8, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(5, 7, 0), coord3d(4, 7, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } // // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // function create_simple_schedule(waytype, stop_positions) { return schedule_x(waytype, stop_positions.map(@(pos) schedule_entry_x(pos, 0, 0))) } // // Tests for transporting passengers, mail and goods // function test_transport_generate_pax_invalid_pos() { ASSERT_EQUAL(world.generate_goods(coord(-1, -1), coord(-1, -1), good_desc_x.passenger, 42), 0) ASSERT_EQUAL(world.generate_goods(coord( 0, 0), coord(-1, -1), good_desc_x.passenger, 42), 0) ASSERT_EQUAL(world.generate_goods(coord(-1, -1), coord( 0, 0), good_desc_x.passenger, 42), 0) } function test_transport_generate_pax_walked() { ASSERT_EQUAL(command_x(tool_build_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 3, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 2, 0), "BusStop"), null) { ASSERT_EQUAL(world.generate_goods(coord(3, 2), coord(5, 2), good_desc_x.passenger, 42), 2) // 2 == walked ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 2, 0), player_x(0)).get_walked()[0], 42) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 3, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_transport_generate_pax_no_route() { ASSERT_EQUAL(command_x(tool_build_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 7, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 2, 0), "BusStop"), null) ASSERT_EQUAL(command_x(tool_build_station).work(player_x(0), coord3d(4, 7, 0), "BusStop"), null) { ASSERT_EQUAL(world.generate_goods(coord(3, 2), coord(3, 7), good_desc_x.passenger, 42), 0) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 7, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 7, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_transport_pax_valid_route() { local pl = player_x(0) ASSERT_EQUAL(command_x(tool_build_way).work(pl, coord3d(4, 2, 0), coord3d(4, 8, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 2, 0), "BusStop"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 7, 0), "BusStop"), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 8, 0), building_desc_x("CarDepot")), null) local depot = depot_x(4, 8, 0) local from_halt = halt_x.get_halt(coord3d(4, 7, 0), pl) local to_halt = halt_x.get_halt(coord3d(4, 2, 0), pl) // create vehicle ... depot.append_vehicle(pl, convoy_x(0), vehicle_desc_x("Buessig")) local cnv = depot.get_convoy_list()[0] // ... with simple 2-entry schedule ... cnv.change_schedule(pl, create_simple_schedule(wt_road, [ coord3d(4, 7, 0), coord3d(4, 2, 0) ])) // and start the convoy depot.start_all_convoys(pl) // make sure that the graph linking all halts is updated // make sure that the graph linking and all halts is updated while(!halt_x.is_rerouting_finished()) { sleep() } { ASSERT_EQUAL(world.generate_goods(coord(3, 7), coord(3, 2), good_desc_x.passenger, 30), 1) // 1 == OK ASSERT_EQUAL(from_halt.waiting[0], 30) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x.passenger, to_halt), 30) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.passenger, coord3d(3, 2, 0)), 30) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.passenger, coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 30) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 0) ASSERT_EQUAL(from_halt.arrived[0], 0) ASSERT_EQUAL(from_halt.convoys[0], 0) } while (from_halt.convoys[0] == 0) { sleep() } // Loading is not instant, so we need to make sure loading has finished while (from_halt.waiting[0] > 0) { sleep() } { ASSERT_EQUAL(from_halt.convoys[0], 1) ASSERT_EQUAL(from_halt.waiting[0], 0) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x.passenger, to_halt), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.passenger, coord3d(3, 2, 0)), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.passenger, coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 30) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 30) ASSERT_EQUAL(from_halt.arrived[0], 0) } while (to_halt.convoys[0] == 0) { sleep() } // make sure unloading has finished while (to_halt.arrived[0] < 30) { sleep() } { ASSERT_EQUAL(to_halt.waiting[0], 0) ASSERT_EQUAL(to_halt.happy[0], 0) ASSERT_EQUAL(to_halt.unhappy[0], 0) ASSERT_EQUAL(to_halt.noroute[0], 0) ASSERT_EQUAL(to_halt.walked[0], 0) ASSERT_EQUAL(to_halt.departed[0], 0) ASSERT_EQUAL(to_halt.arrived[0], 30) } // clean up cnv.destroy(pl) sleep() sleep() // make sure the convoy is destroyed ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 7, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 8, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 8, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_transport_mail_valid_route() { local pl = player_x(0) ASSERT_EQUAL(command_x(tool_build_way).work(pl, coord3d(4, 2, 0), coord3d(4, 8, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 2, 0), "PostStop"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 7, 0), "PostStop"), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 8, 0), building_desc_x("CarDepot")), null) local depot = depot_x(4, 8, 0) local from_halt = halt_x.get_halt(coord3d(4, 7, 0), pl) local to_halt = halt_x.get_halt(coord3d(4, 2, 0), pl) // create vehicle ... depot.append_vehicle(pl, convoy_x(0), vehicle_desc_x("Posttransporter")) local cnv = depot.get_convoy_list()[0] // ... with simple 2-entry schedule ... cnv.change_schedule(pl, create_simple_schedule(wt_road, [ coord3d(4, 7, 0), coord3d(4, 2, 0) ])) // and start the convoy depot.start_all_convoys(pl) // make sure that the graph linking all halts is updated // make sure that the graph linking and all halts is updated while(!halt_x.is_rerouting_finished()) { sleep() } { ASSERT_EQUAL(world.generate_goods(coord(3, 7), coord(3, 2), good_desc_x.mail, 30), 1) // 1 == OK } sleep() sleep() { ASSERT_EQUAL(from_halt.waiting[0], 30) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x.mail, to_halt), 30) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.mail, coord3d(3, 2, 0)), 30) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.mail, coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 0) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 0) ASSERT_EQUAL(from_halt.arrived[0], 0) ASSERT_EQUAL(from_halt.convoys[0], 0) } while (from_halt.convoys[0] == 0) { sleep() } // Loading is not instant, so we need to make sure loading has finished while (from_halt.waiting[0] > 0) { sleep() } { ASSERT_EQUAL(from_halt.convoys[0], 1) ASSERT_EQUAL(from_halt.waiting[0], 0) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x.mail, to_halt), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.mail, coord3d(3, 2, 0)), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x.mail, coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 0) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 30) ASSERT_EQUAL(from_halt.arrived[0], 0) } while (to_halt.convoys[0] == 0) { sleep() } // make sure unloading has finished while (to_halt.arrived[0] < 30) { sleep() } { ASSERT_EQUAL(to_halt.waiting[0], 0) ASSERT_EQUAL(to_halt.happy[0], 0) ASSERT_EQUAL(to_halt.unhappy[0], 0) ASSERT_EQUAL(to_halt.noroute[0], 0) ASSERT_EQUAL(to_halt.walked[0], 0) ASSERT_EQUAL(to_halt.departed[0], 0) ASSERT_EQUAL(to_halt.arrived[0], 30) } // clean up cnv.destroy(pl) sleep() sleep() // make sure the convoy is destroyed ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 7, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 8, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 8, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_transport_freight_valid_route() { local pl = player_x(0) ASSERT_EQUAL(command_x(tool_build_way).work(pl, coord3d(4, 2, 0), coord3d(4, 8, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_way).work(pl, coord3d(4, 7, 0), coord3d(5, 7, 0), "cobblestone_road"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 2, 0), "CarStop"), null) ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(5, 7, 0), "CarStop"), null) ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 8, 0), building_desc_x("CarDepot")), null) local depot = depot_x(4, 8, 0) local from_halt = halt_x.get_halt(coord3d(5, 7, 0), pl) local to_halt = halt_x.get_halt(coord3d(4, 2, 0), pl) // create vehicle ... depot.append_vehicle(pl, convoy_x(0), vehicle_desc_x("Kohletransporter")) local cnv = depot.get_convoy_list()[0] // ... with simple 2-entry schedule ... cnv.change_schedule(pl, create_simple_schedule(wt_road, [ coord3d(5, 7, 0), coord3d(4, 2, 0) ])) // and start the convoy depot.start_all_convoys(pl) // make sure that the graph linking all halts is updated // make sure that the graph linking and all halts is updated while(!halt_x.is_rerouting_finished()) { sleep() } { ASSERT_EQUAL(world.generate_goods(coord(3, 7), coord(3, 2), good_desc_x("Kohle"), 18), 1) // 1 == OK } sleep() sleep() { ASSERT_EQUAL(from_halt.waiting[0], 18) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x("Kohle"), to_halt), 18) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x("Kohle"), coord3d(3, 2, 0)), 18) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x("Kohle"), coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 0) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 0) ASSERT_EQUAL(from_halt.arrived[0], 0) ASSERT_EQUAL(from_halt.convoys[0], 0) } while (from_halt.convoys[0] == 0) { sleep() } // Loading is not instant, so we need to make sure loading has finished while (from_halt.waiting[0] > 0) { sleep() } { ASSERT_EQUAL(from_halt.convoys[0], 1) ASSERT_EQUAL(from_halt.waiting[0], 0) ASSERT_EQUAL(from_halt.get_freight_to_halt(good_desc_x("Kohle"), to_halt), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x("Kohle"), coord3d(3, 2, 0)), 0) ASSERT_EQUAL(from_halt.get_freight_to_dest(good_desc_x("Kohle"), coord3d(4, 2, 0)), 0) ASSERT_EQUAL(from_halt.happy[0], 0) ASSERT_EQUAL(from_halt.unhappy[0], 0) ASSERT_EQUAL(from_halt.noroute[0], 0) ASSERT_EQUAL(from_halt.walked[0], 0) ASSERT_EQUAL(from_halt.departed[0], 18) ASSERT_EQUAL(from_halt.arrived[0], 0) } while (to_halt.convoys[0] == 0) { sleep() } // make sure unloading has finished while (to_halt.arrived[0] < 18) { sleep() } { ASSERT_EQUAL(to_halt.waiting[0], 0) ASSERT_EQUAL(to_halt.happy[0], 0) ASSERT_EQUAL(to_halt.unhappy[0], 0) ASSERT_EQUAL(to_halt.noroute[0], 0) ASSERT_EQUAL(to_halt.walked[0], 0) ASSERT_EQUAL(to_halt.departed[0], 0) ASSERT_EQUAL(to_halt.arrived[0], 18) } // clean up cnv.destroy(pl) sleep() sleep() // make sure the convoy is destroyed ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(5, 7, 0)), null) ASSERT_EQUAL(command_x(tool_remover).work(player_x(0), coord3d(4, 8, 0)), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(4, 2, 0), coord3d(4, 8, 0), "" + wt_road), null) ASSERT_EQUAL(command_x(tool_remove_way).work(player_x(0), coord3d(5, 7, 0), coord3d(4, 7, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_trees.nut000066400000000000000000000207711520675460500210100ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for building trees and forests // function build_tree(pl, pos, desc, ignore_climate = false, random_age = false) { return command_x(tool_plant_tree).work(pl, pos, "" + ignore_climate.tointeger() + random_age.tointeger() + "," + desc.get_name()) } function test_trees_plant_single_invalid_pos() { local pl = player_x(0) local public_pl = player_x(1) local tree_desc = tree_desc_x("Ahorn-1") ASSERT_TRUE(tree_desc != null) // build outside of map borders, should fail (obv) { ASSERT_EQUAL(build_tree(public_pl, coord3d(100, 100, 0), tree_desc), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_trees_plant_single_invalid_param() { local public_pl = player_x(1) // invalid default_param -> don't crash { local error_caught = false try { ASSERT_EQUAL(command_x(tool_plant_tree).work(public_pl, coord3d(4, 2, 0), "1"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) ASSERT_TRUE(tile_x(4, 2, 0).find_object(mo_tree) == null) } // invalid default_param -> don't crash { local error_caught = false try { ASSERT_EQUAL(command_x(tool_plant_tree).work(public_pl, coord3d(4, 2, 0), "11"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) ASSERT_TRUE(tile_x(4, 2, 0).find_object(mo_tree) == null) } // invalid default_param -> don't crash { local error_caught = false try { ASSERT_EQUAL(command_x(tool_plant_tree).work(public_pl, coord3d(4, 2, 0), "11,"), "") } catch (e) { error_caught = true ASSERT_EQUAL(e, "Error during initializing tool") } ASSERT_TRUE(error_caught) ASSERT_TRUE(tile_x(4, 2, 0).find_object(mo_tree) == null) } // clean up RESET_ALL_PLAYER_FUNDS() } function test_trees_plant_single_null_param() { local public_pl = player_x(1) // null default_param -> random tree { ASSERT_EQUAL(command_x(tool_plant_tree).work(public_pl, coord3d(4, 2, 0), null), null) ASSERT_TRUE(tile_x(4, 2, 0).find_object(mo_tree) != null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_trees_plant_single_empty_param() { local public_pl = player_x(1) // empty default_param -> random tree { ASSERT_EQUAL(command_x(tool_plant_tree).work(public_pl, coord3d(4, 2, 0), ""), null) ASSERT_TRUE(tile_x(4, 2, 0).find_object(mo_tree) != null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_trees_plant_single_invalid_desc() { local public_pl = player_x(1) // invalid desc name -> random tree { ASSERT_EQUAL(command_x(tool_plant_tree).work(public_pl, coord3d(4, 2, 0), "11,bla"), null) ASSERT_TRUE(tile_x(4, 2, 0).find_object(mo_tree) != null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 2, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_trees_plant_single_when_disabled() { local pl = player_x(0) local public_pl = player_x(1) local tree_desc = tree_desc_x("Ahorn-1") // build single tree, should fail because trees are disallowed { ASSERT_EQUAL(build_tree(pl, coord3d(4, 3, 0), tree_desc), "Trees disabled!") ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_tree), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) } // public player can build trees anyway { ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) local tree = tile_x(4, 3, 0).find_object(mo_tree) ASSERT_TRUE(tree != null) ASSERT_EQUAL(tree.get_desc().get_name(), tree_desc.get_name()) ASSERT_EQUAL(public_pl.get_current_maintenance(), 0) ASSERT_TRUE(tree.get_owner() != null) // tree is owned by everybody (player_all) but there is no way to check for player == player_all ASSERT_EQUAL(tree.get_age(), 0) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_trees_plant_single_random_age() { local public_pl = player_x(1) local tree_desc = tree_desc_x("Ahorn-1") // check planting with random age { ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc, false, true), null) local tree = tile_x(4, 3, 0).find_object(mo_tree) ASSERT_TRUE(tree != null) ASSERT_EQUAL(tree.get_desc().get_name(), tree_desc.get_name()) ASSERT_EQUAL(public_pl.get_current_maintenance(), 0) ASSERT_TRUE(tree.get_owner() != null) // tree is owned by everybody (player_all) but there is no way to check for player == player_all ASSERT_TRUE(tree.get_age() >= 0) ASSERT_TRUE(tree.get_age() < (1<<12)) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) RESET_ALL_PLAYER_FUNDS(); } function test_trees_plant_single_ignore_climate() { local public_pl = player_x(1) local tree_desc = tree_desc_x("Ahorn-1") ASSERT_EQUAL(command_x(tool_set_climate).work(public_pl, coord3d(4, 3, 0), coord3d(4, 3, 0), "" + cl_arctic), null) // Check planting with ignore_climate = true { ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc, false, false), "") ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_tree), null) ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc, true, false), null) ASSERT_TRUE(tile_x(4, 3, 0).find_object(mo_tree) != null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x(tool_set_climate).work(public_pl, coord3d(4, 3, 0), coord3d(4, 3, 0), "" + cl_mediterran), null) RESET_ALL_PLAYER_FUNDS(); } function test_trees_plant_single_max_per_square() { local public_pl = player_x(1) local tree_desc = tree_desc_x("Ahorn-1") ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) // Check building over max_no_of_trees_on_square limit (= 5) { ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), "") } // clean up ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) // removes all trees at once ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_tree), null) RESET_ALL_PLAYER_FUNDS() } function test_trees_plant_single_occupied() { local pl = player_x(0) local public_pl = player_x(1) local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] local tree_desc = tree_desc_x("Ahorn-1") ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), road, true), null) // Cannot build trees on ways { ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 4, 0), tree_desc), "") ASSERT_EQUAL(tile_x(4, 4, 0).find_object(mo_tree), null) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_trees_plant_forest() { local pl = player_x(0) local public_pl = player_x(1) local planter = command_x(tool_forest) local tree_desc = tree_desc_x("Busch_1") local topleft = coord3d(1, 1, 0) local bottomright = coord3d(6, 6, 0) // 1x1 { ASSERT_EQUAL(planter.work(public_pl, topleft, topleft, ""), null) ASSERT_EQUAL(tile_x(topleft.x, topleft.y, 0).find_object(mo_tree), null) } // 2x2 { ASSERT_EQUAL(planter.work(public_pl, topleft, topleft + coord(1, 1), ""), null) ASSERT_EQUAL(tile_x(topleft.x+0, topleft.y+0, 0).find_object(mo_tree), null) ASSERT_EQUAL(tile_x(topleft.x+1, topleft.y+0, 0).find_object(mo_tree), null) ASSERT_EQUAL(tile_x(topleft.x+0, topleft.y+1, 0).find_object(mo_tree), null) ASSERT_TRUE(tile_x(topleft.x+1, topleft.y+1, 0).find_object(mo_tree) != null) } // this works with player 0 even though trees are disabled { ASSERT_EQUAL(planter.work(pl, topleft, bottomright, ""), null) for (local y = 0; y < 8; ++y) { for (local x = 0; x < 8; ++x) { local outside = ((y+1)%8 < 3) || ((x+1)%8 < 3) // FIXME: this should fail for == 2 if (outside) { ASSERT_EQUAL(tile_x(x, y, 0).find_object(mo_tree), null) } } } ASSERT_EQUAL(pl.get_current_maintenance(), 0) } // clean up for (local y = 0; y < 8; ++y) { for (local x = 0; x < 8; ++x) { command_x(tool_remover).work(pl, coord3d(x, y, 0)) } } RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_way_bridge.nut000066400000000000000000000401641520675460500220000ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for bridge building / removal // function test_way_bridge_build_ground() { local pl = player_x(0) local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] local remover = command_x(tool_remove_way) ASSERT_TRUE(bridge_desc != null) // build bridge on flat ground ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 5, 0), coord3d(3, 2, 0), bridge_desc), null) { // test bridge object local t = tile_x(3, 5, 0) local bridge = t.find_object(mo_bridge) ASSERT_TRUE(bridge != null) ASSERT_EQUAL(bridge.get_desc().get_name(), bridge_desc.get_name()) } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "...4....", "...5....", "...0....", "...0....", "...5....", "...1....", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "...4....", "...5....", "...5....", "...5....", "...5....", "...1....", "........" ]) // build bridge over bridgehead (should fail) ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), bridge_desc), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "...4....", "...5....", "...5....", "...5....", "...5....", "...1....", "........" ]) // build bridgehead slope under bridge (should fail) ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), bridge_desc), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "...4....", "...5....", "...5....", "...5....", "...5....", "...1....", "........" ]) // build bridge crossing (should fail) ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), bridge_desc), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "...4....", "...5....", "...5....", "...5....", "...5....", "...1....", "........" ]) ASSERT_EQUAL(remover.work(pl, tile_x(3, 1, 0), tile_x(3, 6, 0), "" + wt_road), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } function test_way_bridge_build_at_slope() { local start_pos = coord3d(2, 1, 0) local end_pos = coord3d(2, 6, 0) local remover = command_x(tool_remove_way) local setslope = command_x.set_slope local pl = player_x(0) local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] ASSERT_EQUAL(setslope(pl, coord3d(2, 1, 0), slope.south), null) { // down slope local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) ASSERT_EQUAL(err, "Bridge is too long for this type!\n") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } { // wrong single-height slope ASSERT_EQUAL(setslope(pl, end_pos, slope.west), null) local err = command_x.build_bridge(pl, start_pos, end_pos, bridge_desc) ASSERT_EQUAL(err, "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } { // double height slope ASSERT_EQUAL(setslope(pl, end_pos, 2*slope.north), null) local err = command_x.build_bridge(pl, start_pos, end_pos, bridge_desc) ASSERT_EQUAL(err, "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } { // wrong double height slope ASSERT_EQUAL(setslope(pl, end_pos, 2*slope.west), null) local err = command_x.build_bridge(pl, start_pos, end_pos, bridge_desc) ASSERT_EQUAL(err, "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } { // flat double height slope (i.e. 2 stacked flat slopes) ASSERT_EQUAL(setslope(pl, end_pos, slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, end_pos + coord3d(0, 0, 1), slope.all_up_slope), null) local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) ASSERT_EQUAL(err, "Bridge is too long for this type!\n") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(setslope(pl, end_pos + coord3d(0, 0, 2), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, end_pos + coord3d(0, 0, 1), slope.all_down_slope), null) } { // flat single height slope ASSERT_EQUAL(setslope(pl, end_pos, slope.all_up_slope), null) local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) ASSERT_EQUAL(err, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "..4.....", "..5.....", "..5.....", "..5.....", "..5.....", "..1.....", "........" ]) // remove way ASSERT_EQUAL(remover.work(pl, start_pos, end_pos + coord3d(0, 0, 1), "" + wt_road), null) // remove slope ASSERT_EQUAL(setslope(pl, end_pos + coord3d(0, 0, 1), slope.all_down_slope), null) } { // correct slope setslope(pl, end_pos, slope.north) local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) ASSERT_EQUAL(err, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "..4.....", "..5.....", "..5.....", "..5.....", "..5.....", "..1.....", "........" ]) // remove way ASSERT_EQUAL(remover.work(pl, start_pos, end_pos, "" + wt_road), null) // remove slope ASSERT_EQUAL(setslope(pl, end_pos, slope.flat), null) } // clean up ASSERT_EQUAL(setslope(pl, start_pos, slope.flat), null) RESET_ALL_PLAYER_FUNDS() } function test_way_bridge_build_at_slope_stacked() { local remover = command_x(tool_remove_way) local setslope = command_x.set_slope local pl = player_x(0) local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] { ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.north), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 2, 0), bridge_desc), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "...4....", "...5....", "...5....", "...1....", "........", "........" ]) // second bridge layer ASSERT_EQUAL(setslope(pl, coord3d(3, 1, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 1, 1), slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 6, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 6, 1), slope.north), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 1, 1), bridge_desc), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "...4....", "...4....", "...5....", "...5....", "...1....", "...1....", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 2), [ "........", "...4....", "...5....", "...5....", "...5....", "...5....", "...1....", "........" ]) } { // remove lower bridge and rebuild it ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 2, 0), bridge_desc), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "...4....", "...4....", "...5....", "...5....", "...1....", "...1....", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 2), [ "........", "...4....", "...5....", "...5....", "...5....", "...5....", "...1....", "........" ]) ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 1), coord3d(3, 6, 1), "" + wt_road), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 1, 1), slope.flat), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 6, 1), slope.flat), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 1, 1), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 6, 1), slope.all_down_slope), null) } { // second bridge layer ASSERT_EQUAL(setslope(pl, coord3d(1, 3, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 3, 1), slope.east), null) ASSERT_EQUAL(setslope(pl, coord3d(6, 3, 0), slope.all_up_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(6, 3, 1), slope.west), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(1, 3, 1), bridge_desc), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "...4....", ".2.5..8.", "...5....", "...1....", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 2), [ "........", "........", "...4....", ".2AAAA8.", "........", "...1....", "........", "........" ]) } { // remove lower bridge and rebuild it ASSERT_EQUAL(remover.work(pl, coord3d(3, 5, 0), coord3d(3, 2, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 5, 0), bridge_desc), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "...4....", ".2.5..8.", "...5....", "...1....", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 2), [ "........", "........", "...4....", ".2AAAA8.", "........", "...1....", "........", "........" ]) } // clean up ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), "" + wt_road), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 3, 1), coord3d(6, 3, 1), "" + wt_road), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 3, 1), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 3, 1), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(6, 3, 1), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(6, 3, 1), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.all_down_slope), null) RESET_ALL_PLAYER_FUNDS() } // TODO Try to build bridge when way_height_clearance == 2 function test_way_bridge_build_above_way() { local remover = command_x(tool_remove_way) local setslope = command_x.set_slope local pl = player_x(0) local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] ASSERT_TRUE(bridge_desc != null) ASSERT_TRUE(way_desc != null) { ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.north), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), way_desc, true), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 2, 0), bridge_desc), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "..2A8...", "........", "...1....", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "...4....", "...5....", "...5....", "...1....", "........", "........" ]) } { // remove way, rebuild it under bridge ASSERT_EQUAL(remover.work(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), way_desc, true), null) } ASSERT_EQUAL(remover.work(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), "" + wt_road), null) ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), "" + wt_road), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.all_down_slope), null) RESET_ALL_PLAYER_FUNDS() } function test_way_bridge_build_above_runway() { local pl = player_x(0) local wayremover = command_x(tool_remove_way) local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0] local runway = way_desc_x.get_available_ways(wt_air, st_elevated)[0] local bridge = bridge_desc_x.get_available_bridges(wt_road)[0] local setslope = command_x.set_slope // preconditions // build bridge across taxiway { ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 7, 0), coord3d(7, 9, 0), taxiway, true), null) ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(6, 8, 0), coord3d(8, 8, 0), bridge), null) ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), [ "........", "........", "..4.....", "..5.....", "..1.....", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(5, 5, 0), [ "........", "........", "........", "2A.A8...", "........", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(7, 7, 0), coord3d(7, 9, 0), "" + wt_air), null) ASSERT_EQUAL(wayremover.work(pl, coord3d(5, 8, 0), coord3d(9, 8, 0), "" + wt_road), null) } // build bridge across runway, should fail { ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 7, 0), coord3d(7, 9, 0), runway, true), null) ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(6, 8, 0), coord3d(8, 8, 0), bridge), "No bridges over runways!") ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), [ "........", "........", "..4.....", "..5.....", "..1.....", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(7, 7, 0), coord3d(7, 9, 0), "" + wt_air), null) } // clean up ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_way_bridge_planner() { local pl = player_x(0) local start_pos = coord3d(12, 3, 0) local end_pos = coord3d(12, 6, 0) local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] local working_slopes = [ slope.north ] // maybe also slope.south in the future ASSERT_EQUAL(command_x.set_slope(pl, start_pos, slope.south), null) { for (local sl = slope.flat; sl < slope.raised; ++sl) { command_x.set_slope(pl, end_pos, sl) local end = bridge_planner_x.find_end(pl, start_pos, dir.south, bridge_desc, 0) local expected_end = (working_slopes.find(sl) != null) ? end_pos : coord3d(-1, -1, -1) ASSERT_EQUAL(end.tostring(), expected_end.tostring()) } ASSERT_EQUAL(command_x.set_slope(pl, end_pos, slope.all_up_slope), null) local end = bridge_planner_x.find_end(pl, start_pos, dir.south, bridge_desc, 0) ASSERT_EQUAL(end.tostring(), (end_pos + coord3d(0, 0, 1)).tostring()) // clean up ASSERT_EQUAL(command_x.set_slope(pl, start_pos, slope.flat), null) ASSERT_EQUAL(command_x.set_slope(pl, end_pos + coord3d(0, 0, 1), slope.all_down_slope), null) } // min length { ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 1, 0), slope.south), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(3, 1, 0), slope.south), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 2, 0), slope.north), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(3, 3, 0), slope.north), null) ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(2, 1, 0), dir.south, bridge_desc, 0).tostring(), coord3d(2, 2, 0).tostring()) ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(2, 1, 0), dir.south, bridge_desc, 1).tostring(), coord3d(2, 2, 0).tostring()) ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(3, 1, 0), dir.south, bridge_desc, 0).tostring(), coord3d(3, 3, 0).tostring()) ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(3, 1, 0), dir.south, bridge_desc, 1).tostring(), coord3d(3, 3, 0).tostring()) ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(3, 1, 0), dir.south, bridge_desc, 2).tostring(), coord3d(3, 3, 0).tostring()) ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(3, 1, 0), dir.south, bridge_desc, 3).tostring(), coord3d(-1, -1, -1).tostring()) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 1, 0), slope.flat), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(3, 1, 0), slope.flat), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 2, 0), slope.flat), null) ASSERT_EQUAL(command_x.set_slope(pl, coord3d(3, 3, 0), slope.flat), null) } ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_way_road.nut000066400000000000000000001016351520675460500214720ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for way building / removal // function test_way_road_build_single_tile() { local pl = player_x(0) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local default_cash = pl.get_current_cash() ASSERT_TRUE(road_desc != null) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), road_desc, true), "") ASSERT_EQUAL(pl.get_current_cash(), default_cash) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } { ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), road_desc, false), "") ASSERT_EQUAL(pl.get_current_cash(), default_cash) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } RESET_ALL_PLAYER_FUNDS() } function test_way_road_build_straight() { local default_cash = 200000 * 100 local current_cash = default_cash local pl = player_x(0) local desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] ASSERT_TRUE(desc != null) //prerequisites ASSERT_EQUAL(pl.get_current_maintenance(), 0) local tool_result = command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 6, 0), desc, true) ASSERT_EQUAL(tool_result, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", "..5.....", "..5.....", "..5.....", "..1.....", "........" ]) ASSERT_TRUE(pl.get_current_net_wealth() < current_cash) ASSERT_TRUE(pl.get_current_maintenance() > 0) ASSERT_TRUE(pl.get_current_cash() < default_cash / 100) current_cash = pl.get_current_net_wealth() local maintenance_per_tile = pl.get_current_maintenance() / 6 tool_result = command_x.build_way(pl, coord3d(2, 2, 0), coord3d(6, 2, 0), desc, true) ASSERT_EQUAL(tool_result, null) ASSERT_TRUE(pl.get_current_net_wealth() < current_cash) ASSERT_EQUAL(pl.get_current_maintenance(), maintenance_per_tile * (6 + 4)) ASSERT_TRUE(pl.get_current_cash() < default_cash / 100) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..7AAA8.", "..5.....", "..5.....", "..5.....", "..1.....", "........" ]) tool_result = command_x.build_way(pl, coord3d(16, 5, 0), coord3d(5, 5, 0), desc, true) ASSERT_EQUAL(tool_result, "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..7AAA8.", "..5.....", "..5.....", "..5.....", "..1.....", "........" ]) tool_result = command_x.build_way(pl, coord3d(1, 1, 1), coord3d(1, 5, 1), desc, true) ASSERT_EQUAL(tool_result, "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..7AAA8.", "..5.....", "..5.....", "..5.....", "..1.....", "........" ]) tool_result = command_x.build_way(pl, coord3d(1, 1, -1), coord3d(1, 5, -1), desc, true) ASSERT_EQUAL(tool_result, "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..7AAA8.", "..5.....", "..5.....", "..5.....", "..1.....", "........" ]) // clean up local remover = command_x(tool_remove_way) tool_result = remover.work(pl, tile_x(2, 2, 0), tile_x(6, 2, 0), "" + wt_road) ASSERT_EQUAL(tool_result, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", "..5.....", "..5.....", "..5.....", "..1.....", "........" ]) tool_result = remover.work(pl, tile_x(2, 3, 0), tile_x(2, 4, 0), "" + wt_road) ASSERT_EQUAL(tool_result, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", "..5.....", "..1.....", "..4.....", "..5.....", "..1.....", "........" ]) tool_result = remover.work(pl, tile_x(2, 1, 0), tile_x(2, 2, 0), "" + wt_road) ASSERT_EQUAL(tool_result, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "..4.....", "..1.....", "..4.....", "..5.....", "..1.....", "........" ]) tool_result = remover.work(pl, tile_x(2, 2, 0), tile_x(2, 6, 0), "" + wt_road) ASSERT_EQUAL(tool_result, "Ways not connected") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "..4.....", "..1.....", "..4.....", "..5.....", "..1.....", "........" ]) tool_result = remover.work(pl, tile_x(2, 2, 0), tile_x(2, 3, 0), "" + wt_road) ASSERT_EQUAL(tool_result, null) tool_result = remover.work(pl, tile_x(2, 4, 0), tile_x(2, 6, 0), "" + wt_road) ASSERT_EQUAL(tool_result, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) RESET_ALL_PLAYER_FUNDS() } function test_way_road_build_bend() { local pl = player_x(0) local desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local remover = command_x(tool_remove_way) ASSERT_TRUE(desc != null) { local tool_result = command_x.build_way(pl, coord3d(0, 1, 0), coord3d(1, 0, 0), desc, true) ASSERT_EQUAL(tool_result, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ ".4......", "29......", "........", "........", "........", "........", "........", "........" ]) remover.work(pl, tile_x(0, 1, 0), coord3d(1, 0, 0), "" + wt_road) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } { command_x.grid_raise(pl, coord3d(2, 2, 0)) local err = command_x.build_way(pl, coord3d(0, 1, 0), coord3d(1, 0, 0), desc, true) ASSERT_EQUAL(err, null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "68......", "1.......", "........", "........", "........", "........", "........", "........" ]) remover.work(pl, tile_x(0, 1, 0), coord3d(1, 0, 0), "" + wt_road) command_x.grid_lower(pl, coord3d(2, 2, 0)) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } RESET_ALL_PLAYER_FUNDS() } function test_way_road_build_parallel() { local pl = player_x(0) local desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local remover = command_x(tool_remove_way) { for (local i = 1; i < 16; ++i) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(i, i, 0), desc, true), null) } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "................", "28..............", "2A8.............", "2AA8............", "2AAA8...........", "2AAAA8..........", "2AAAAA8.........", "2AAAAAA8........", "2AAAAAAA8.......", "2AAAAAAAA8......", "2AAAAAAAAA8.....", "2AAAAAAAAAA8....", "2AAAAAAAAAAA8...", "2AAAAAAAAAAAA8..", "2AAAAAAAAAAAAA8.", "2AAAAAAAAAAAAAA8" ]) for (local i = 1; i < 16; ++i) { ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(i, i, 0), "" + wt_road), null) } } { for (local i = 1; i < 16; ++i) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(i, i, 0), desc, false), null) } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "................", "28..............", "2A8.............", "2AA8............", "2AAA8...........", "2AAAA8..........", "2AAAAA8.........", "2AAAAAA8........", "2AAAAAAA8.......", "2AAAAAAAA8......", "2AAAAAAAAA8.....", "2AAAAAAAAAA8....", "2AAAAAAAAAAA8...", "2AAAAAAAAAAAA8..", "2AAAAAAAAAAAAA8.", "2AAAAAAAAAAAAAA8" ]) for (local i = 1; i < 16; ++i) { ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(i, i, 0), "" + wt_road), null) } } { for (local i = 0; i < 16; ++i) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(15, i, 0), desc, false), null) } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8" ]) for (local i = 0; i < 16; ++i) { ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(15, i, 0), "" + wt_road), null) } } RESET_ALL_PLAYER_FUNDS() } function test_way_road_build_below_powerline() { local pl = player_x(0) local public_pl = player_x(1) local remover = command_x(tool_remove_way) local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] ASSERT_EQUAL(pl.get_current_maintenance(), 0) ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 1, 0), coord3d(3, 1, 0), powerline, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), powerline, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(1, 3, 0), powerline, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(1, 1, 0), powerline, true), null) ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", ".6AC....", ".5.5....", ".3A9....", "........", "........", "........", "........" ]) // build way along powerline above, should fail { local old_maintenance = pl.get_current_maintenance() ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 1, 0), coord3d(3, 1, 0), road, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) } // build way ending below power line, should succeed { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 1, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(3, 2, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 3, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(1, 2, 0), road, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "..4.....", ".2F8....", "..1.....", "........", "........", "........", "........" ]) } // remove ways ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_road), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), "" + wt_road), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) // build way not ending under power line, should succeed { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 0, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 4, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), road, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "..4.....", "..5.....", "2AFA8...", "..5.....", "..1.....", "........", "........", "........" ]) } // remove ways ASSERT_EQUAL(remover.work(pl, coord3d(2, 0, 0), coord3d(2, 4, 0), "" + wt_road), null) ASSERT_EQUAL(remover.work(pl, coord3d(0, 2, 0), coord3d(4, 2, 0), "" + wt_road), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 1, 0), coord3d(3, 3, 0), "" + wt_power), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 1, 0), coord3d(3, 3, 0), "" + wt_power), null) ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), 0) // build way under end of powerline, should fail ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), powerline, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), powerline, true), null) { local old_maintenance = pl.get_current_maintenance() ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 1, 0), coord3d(3, 1, 0), road, true), "") ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), road, true), "") ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(1, 3, 0), road, true), "") ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(1, 1, 0), road, true), "") ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "........", "..4.....", ".2F8....", "..1.....", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_power), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), "" + wt_power), null) ASSERT_EQUAL(pl.get_current_maintenance(), 0) // add diagonal power line ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), powerline, true), null) { // build way across diagonal power line, should fail ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 7, 0), coord3d(7, 0, 0), road, true), "") ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), [ "2C......", ".3C.....", "..3C....", "...3C...", "....3C..", ".....3C.", "......3C", ".......1" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // and remove power line ASSERT_EQUAL(remover.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + wt_power), null) // clean up ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_way_road_build_crossing() { local pl = player_x(0) local public_pl = player_x(1) local remover = command_x(tool_remove_way) local rail = way_desc_x("sand_track") local slow_road = way_desc_x("dirt_road") local fast_road = way_desc_x("asphalt_road") // road too fast, cannot build crossing { ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), rail, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), fast_road, true), translate("No suitable crossing")) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "...4....", "...5....", "...1....", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_FALSE(way_x(3, 2, 0).is_crossing()) ASSERT_FALSE(tile_x(3, 2, 0).has_two_ways()) } // road slow enough for crossing -> works { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), slow_road, true), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "...4....", "...5....", "...1....", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "..2A8...", "........", "........", "........", "........", "........" ]) ASSERT_TRUE(way_x(3, 2, 0).is_crossing()) ASSERT_EQUAL(way_x(3, 2, 0).get_max_speed(), slow_road.get_topspeed()) ASSERT_TRUE(tile_x(3, 2, 0).has_two_ways()) ASSERT_EQUAL(tile_x(3, 2, 0).get_way(wt_road).get_max_speed(), slow_road.get_topspeed()) ASSERT_EQUAL(tile_x(3, 2, 0).get_way(wt_rail).get_max_speed(), rail.get_topspeed()) } // clean up ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), "" + wt_rail), null) ASSERT_EQUAL(remover.work(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_road_upgrade_crossing() { local pl = player_x(0) local public_pl = player_x(1) local remover = command_x(tool_remove_way) local rail = way_desc_x("sand_track") local slow_road = way_desc_x("dirt_road") local fast_road = way_desc_x("asphalt_road") ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), rail, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), slow_road, true), null) // hard-coded because there is no crossing_desc_x/crossing_x local crossing_road_speed = 80 local crossing_rail_speed = 160 // upgrade road past max crossing speed; works, but road crossing speed is limited { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), fast_road, true), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "...4....", "...5....", "...1....", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "..2A8...", "........", "........", "........", "........", "........" ]) ASSERT_TRUE(way_x(3, 2, 0).is_crossing()) ASSERT_EQUAL(way_x(3, 2, 0).get_max_speed(), min(crossing_road_speed, fast_road.get_topspeed())) ASSERT_TRUE(tile_x(3, 2, 0).has_two_ways()) ASSERT_EQUAL(tile_x(3, 2, 0).get_way(wt_road).get_max_speed(), min(crossing_road_speed, fast_road.get_topspeed())) ASSERT_EQUAL(tile_x(3, 2, 0).get_way(wt_rail).get_max_speed(), min(crossing_rail_speed, rail.get_topspeed())) } // clean up ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), "" + wt_rail), null) ASSERT_EQUAL(remover.work(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), "" + wt_road), null) // upgrade rail with crossing; the crossing is upgraded { local fast_rail = way_desc_x("wooden_sleeper_track") ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), slow_road, true), null) // The crossing whose speed limit for rail is 80kph is built here. ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), rail, true), null) // upgrade rail. The crossing should be upgraded to the one whose speed limit for rail is 160kph. ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), fast_rail, true), null) ASSERT_EQUAL(tile_x(3, 2, 0).get_way(wt_road).get_max_speed(), min(crossing_road_speed, slow_road.get_topspeed())) ASSERT_EQUAL(tile_x(3, 2, 0).get_way(wt_rail).get_max_speed(), min(crossing_rail_speed, fast_rail.get_topspeed())) } // clean up ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), "" + wt_rail), null) ASSERT_EQUAL(remover.work(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_road_upgrade_downgrade() { local pl = player_x(0) local public_pl = player_x(1) local remover = command_x(tool_remove_way) local start_pos = coord3d(4, 3, 0) local end_pos = coord3d(4, 5, 0) local all_ways = way_desc_x.get_available_ways(wt_road, st_flat) all_ways.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) ASSERT_TRUE(all_ways.len() >= 2) local slow_road = all_ways[0] local fast_road = all_ways[1] ASSERT_TRUE(slow_road.get_name() != fast_road.get_name()) ASSERT_TRUE(slow_road.get_topspeed() < fast_road.get_topspeed()) // build road ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, slow_road, true), null) // upgrade road { ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, fast_road, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "....4...", "....5...", "....1...", "........", "........" ]) } // Replace road with same road, should incur no cost { local old_cash = pl.get_current_cash() ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, fast_road, true), null) ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "....4...", "....5...", "....1...", "........", "........" ]) } // downgrade road without ctrl; should fail // note that we cannot use command_x.buid_way for this, because there is no way to enable // ctrl-pressed behaviour (the way is replaced in any case) { local old_maintenance = pl.get_current_maintenance() local old_cash = pl.get_current_cash() local tool = command_x(tool_build_way) ASSERT_EQUAL(tool.work(pl, start_pos, end_pos, slow_road.get_name()), null) ASSERT_EQUAL(tile_x(4, 4, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "....4...", "....5...", "....1...", "........", "........" ]) } // downgrade road with ctrl { local tool = command_x(tool_build_way) tool.set_flags(2) // ctrl ASSERT_EQUAL(tool.work(pl, start_pos, end_pos, slow_road.get_name()), null) ASSERT_EQUAL(tile_x(4, 4, 0).find_object(mo_way).get_desc().get_name(), slow_road.get_name()) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "....4...", "....5...", "....1...", "........", "........" ]) } ASSERT_EQUAL(remover.work(pl, start_pos, end_pos, "" + wt_road), null) // clean up ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_way_road_upgrade_downgrade_across_bridge() { local pl = player_x(0) local public_pl = player_x(1) local remover = command_x(tool_remove_way) local start_pos = coord3d(3, 6, 0) local end_pos = coord3d(3, 2, 0) local bridge = bridge_desc_x.get_available_bridges(wt_road)[0] local all_ways = way_desc_x.get_available_ways(wt_road, st_flat) all_ways.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) ASSERT_TRUE(all_ways.len() >= 2) local slow_road = all_ways[0] local fast_road = all_ways[1] ASSERT_TRUE(slow_road.get_name() != fast_road.get_name()) ASSERT_TRUE(slow_road.get_topspeed() < fast_road.get_topspeed()) // build bridge ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 6, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 6, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 5, 0), bridge), null) // upgrade road { ASSERT_TRUE(tile_x(3, 6, 0).find_object(mo_way) != null) ASSERT_TRUE(tile_x(3, 2, 0).find_object(mo_way) != null) ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, fast_road, true), null) ASSERT_EQUAL(tile_x(3, 6, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) ASSERT_EQUAL(tile_x(3, 2, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "........", "...5....", "...1....", "........" ]) } // Replace road with same road, should incur no cost { local old_cash = pl.get_current_cash() ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, fast_road, true), null) ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "........", "...5....", "...1....", "........" ]) } // downgrade road without ctrl; should fail // note that we cannot use command_x.buid_way for this, because there is no way to enable // ctrl-pressed behaviour (the way is replaced in any case) { local old_maintenance = pl.get_current_maintenance() local old_cash = pl.get_current_cash() local tool = command_x(tool_build_way) ASSERT_EQUAL(tool.work(pl, start_pos, end_pos, slow_road.get_name()), null) ASSERT_EQUAL(tile_x(3, 6, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) ASSERT_EQUAL(tile_x(3, 2, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "........", "...5....", "...1....", "........" ]) } // downgrade road with ctrl { local tool = command_x(tool_build_way) tool.set_flags(2) // ctrl ASSERT_EQUAL(tool.work(pl, start_pos, end_pos, slow_road.get_name()), null) ASSERT_EQUAL(tile_x(3, 6, 0).find_object(mo_way).get_desc().get_name(), slow_road.get_name()) ASSERT_EQUAL(tile_x(3, 2, 0).find_object(mo_way).get_desc().get_name(), slow_road.get_name()) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "........", "...5....", "...1....", "........" ]) } // remove bridge ASSERT_EQUAL(remover.work(pl, start_pos, end_pos, "" + wt_road), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 6, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 6, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 3, 0)), null) // clean up ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_way_road_cityroad_build() { local public_pl = player_x(1) local start_pos = coord3d(3, 2, 0) local end_pos = coord3d(3, 6, 0) // build single tile -> should fail { ASSERT_EQUAL(command_x(tool_build_cityroad).work(public_pl, start_pos, start_pos, "city_road"), "") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // build city road { ASSERT_EQUAL(command_x(tool_build_cityroad).work(public_pl, start_pos, end_pos, "city_road"), null) for (local y = start_pos.y; y < end_pos.y; ++y) { local r = way_x(start_pos.x, y, start_pos.z) ASSERT_TRUE(r != null) ASSERT_TRUE(r.is_valid()) ASSERT_TRUE(r.has_sidewalk()) ASSERT_EQUAL(r.desc.name, "city_road") } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "...5....", "...5....", "...1....", "........" ]) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, start_pos, end_pos, "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_road_cityroad_upgrade_with_cityroad() { local public_pl = player_x(1) local start_pos = coord3d(3, 2, 0) local end_pos = coord3d(3, 6, 0) ASSERT_EQUAL(command_x(tool_build_cityroad).work(public_pl, start_pos, end_pos, "city_road"), null) // upgrade cityroad { ASSERT_EQUAL(command_x(tool_build_cityroad).work(public_pl, start_pos, end_pos, "cobblestone_road"), null) for (local y = start_pos.y; y < end_pos.y; ++y) { local r = way_x(start_pos.x, y, start_pos.z) ASSERT_TRUE(r != null) ASSERT_TRUE(r.is_valid()) ASSERT_TRUE(r.has_sidewalk()) ASSERT_EQUAL(r.desc.name, "city_road") // FIXME Should this not be cobblestone_road? } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "...5....", "...5....", "...1....", "........" ]) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, start_pos, end_pos, "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_road_cityroad_downgrade_with_cityroad() { local public_pl = player_x(1) local start_pos = coord3d(3, 2, 0) local end_pos = coord3d(3, 6, 0) ASSERT_EQUAL(command_x(tool_build_cityroad).work(public_pl, start_pos, end_pos, "cobblestone_road"), null) // downgrade cityroad { local builder = command_x(tool_build_cityroad) builder.set_flags(2) ASSERT_EQUAL(builder.work(public_pl, start_pos, end_pos, "city_road"), null) for (local y = start_pos.y; y < end_pos.y; ++y) { local r = way_x(start_pos.x, y, start_pos.y) ASSERT_TRUE(r != null) ASSERT_TRUE(r.is_valid()) ASSERT_TRUE(r.has_sidewalk()) ASSERT_EQUAL(r.desc.name, "city_road") } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "...5....", "...5....", "...1....", "........" ]) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, start_pos, end_pos, "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_road_cityroad_replace_by_normal_road() { local public_pl = player_x(1) local start_pos = coord3d(3, 2, 0) local end_pos = coord3d(3, 6, 0) ASSERT_EQUAL(command_x(tool_build_cityroad).work(public_pl, start_pos, end_pos, "city_road"), null) // replace cityroad by normal road { ASSERT_EQUAL(command_x.build_way(public_pl start_pos, end_pos, way_desc_x("cobblestone_road"), true), null) for (local y = start_pos.y; y < end_pos.y; ++y) { local r = way_x(start_pos.x, y, start_pos.z) ASSERT_TRUE(r != null) ASSERT_TRUE(r.is_valid()) ASSERT_FALSE(r.has_sidewalk()) ASSERT_EQUAL(r.desc.name, "cobblestone_road") } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "...5....", "...5....", "...1....", "........" ]) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, start_pos, end_pos, "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_road_cityroad_replace_keep_existing() { local public_pl = player_x(1) local start_pos = coord3d(3, 2, 0) local end_pos = coord3d(3, 6, 0) ASSERT_EQUAL(command_x(tool_build_cityroad).work(public_pl, start_pos, end_pos, "city_road"), null) // replace cityroad by normal road { local builder = command_x(tool_build_way) builder.set_flags(1) // enable shift -> keep city roads ASSERT_EQUAL(builder.work(public_pl, start_pos, end_pos, "cobblestone_road"), null) for (local y = start_pos.y; y < end_pos.y; ++y) { local r = way_x(start_pos.x, y, start_pos.z) ASSERT_TRUE(r != null) ASSERT_TRUE(r.is_valid()) ASSERT_TRUE(r.has_sidewalk()) ASSERT_EQUAL(r.desc.name, "city_road") } ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "...4....", "...5....", "...5....", "...5....", "...1....", "........" ]) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, start_pos, end_pos, "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_road_has_double_slopes() { local roads = way_desc_x.get_available_ways(wt_road, st_flat) foreach (r in roads) { ASSERT_TRUE(r.has_double_slopes()) } } function test_way_road_make_public() { local pl = player_x(0) local public_pl = player_x(1) local wayremover = command_x(tool_remove_way) local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] local makepublic = command_x(tool_make_stop_public) ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) // invalid coord { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(-1, -1, -1)), "No suitable ground!") ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } // nothing to make public { local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(3, 4, 0)), null) ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } // make road public { local old_pl_cash = pl.get_current_cash() local old_pl_maint = pl.get_current_maintenance() local old_public_cash = public_pl.get_current_cash() local old_public_maint = public_pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 2, 0)), null) ASSERT_TRUE(way_x(4, 2, 0).get_owner() != null) ASSERT_EQUAL(way_x(4, 2, 0).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(pl.get_current_cash()*100, old_pl_cash*100 - 60 * road_desc.get_maintenance()) // 60 == cst_make_public_months ASSERT_EQUAL(pl.get_current_maintenance(), old_pl_maint - road_desc.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_maintenance(), old_public_maint + road_desc.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_cash(), old_public_cash) } ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_way_runway.nut000066400000000000000000000144311520675460500220670ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // tests for runways/taxiways // function test_way_runway_build_rw_flat() { local pl = player_x(0) local wayremover = command_x(tool_remove_way) local runway = way_desc_x.get_available_ways(wt_air, st_elevated)[0] local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] // preconditions ASSERT_TRUE(runway != null) ASSERT_TRUE(road != null) // Build runway next to map border, should fail (causes glitches with planes taking off/landing) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), runway, true), "") ASSERT_WAY_PATTERN(wt_air, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) } // Straight runway away from map border { ASSERT_EQUAL(command_x.build_way(pl, coord3d(6, 6, 0), coord3d(6, 8, 0), runway, true), null) ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), [ "........", ".4......", ".5......", ".1......", "........", "........", "........", "........" ]) } // Cross runway with existing one { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 7, 0), coord3d(7, 7, 0), runway, true), null) ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), [ "........", ".4......", "2F8.....", ".1......", "........", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(5, 7, 0), coord3d(7, 7, 0), "" + wt_air), null) ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), [ "........", ".4......", ".5......", ".1......", "........", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(6, 6, 0), coord3d(6, 8, 0), "" + wt_air), null) } // no runways across ways { ASSERT_EQUAL(command_x.build_way(pl, coord3d(8, 8, 0), coord3d(10, 8, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(9, 7, 0), coord3d(9, 9, 0), runway, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(6, 6, 0), [ "........", "........", "..2A8...", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_air, coord3d(6, 6, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(8, 8, 0), coord3d(10, 8, 0), "" + wt_road), null) } ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_way_runway_build_tw_flat() { local pl = player_x(0) local wayremover = command_x(tool_remove_way) local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0] local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] // preconditions ASSERT_TRUE(taxiway != null) ASSERT_TRUE(road != null) // Build taxiway next to map border, should succeed (contrary to runway) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), taxiway, true), null) ASSERT_WAY_PATTERN(wt_air, coord3d(0, 0, 0), [ "........", "........", "........", "....4...", "....5...", "....1...", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), "" + wt_air), null) } // Straight taxiway away from map border { ASSERT_EQUAL(command_x.build_way(pl, coord3d(6, 6, 0), coord3d(6, 8, 0), taxiway, true), null) ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), [ "........", ".4......", ".5......", ".1......", "........", "........", "........", "........" ]) } // Cross taxiway with existing one { ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 7, 0), coord3d(7, 7, 0), taxiway, true), null) ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), [ "........", ".4......", "2F8.....", ".1......", "........", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(5, 7, 0), coord3d(7, 7, 0), "" + wt_air), null) ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), [ "........", ".4......", ".5......", ".1......", "........", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(6, 6, 0), coord3d(6, 8, 0), "" + wt_air), null) } // no taxiways across ways { ASSERT_EQUAL(command_x.build_way(pl, coord3d(8, 8, 0), coord3d(10, 8, 0), road, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(9, 7, 0), coord3d(9, 9, 0), taxiway, true), "") ASSERT_WAY_PATTERN(wt_road, coord3d(6, 6, 0), [ "........", "........", "..2A8...", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_air, coord3d(6, 6, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(8, 8, 0), coord3d(10, 8, 0), "" + wt_road), null) } ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } function test_way_runway_build_mixed_flat() { local pl = player_x(0) local wayremover = command_x(tool_remove_way) local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0] local runway = way_desc_x.get_available_ways(wt_air, st_flat)[0] // t junction { ASSERT_EQUAL(command_x.build_way(pl, coord3d(8, 8, 0), coord3d(8, 10, 0), runway, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 9, 0), coord3d(8, 9, 0), taxiway, true), null) ASSERT_WAY_PATTERN(wt_air, coord3d(6, 6, 0), [ "........", "........", "..4.....", ".2D.....", "..1.....", "........", "........", "........" ]) } // crossing { ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 9, 0), coord3d(9, 9, 0), taxiway, true), null) ASSERT_WAY_PATTERN(wt_air, coord3d(6, 6, 0), [ "........", "........", "..4.....", ".2F8....", "..1.....", "........", "........", "........" ]) ASSERT_EQUAL(wayremover.work(pl, coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_air), null) ASSERT_EQUAL(wayremover.work(pl, coord3d(8, 8, 0), coord3d(8, 10, 0), "" + wt_air), null) } ASSERT_EQUAL(pl.get_current_maintenance(), 0) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_way_tram.nut000066400000000000000000000236221520675460500215070ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for tramways // function test_way_tram_build_flat() { local pl = player_x(0) local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] // build straight { ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), tramway, true), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_rail), null) } RESET_ALL_PLAYER_FUNDS() } function test_way_tram_build_parallel() { local pl = player_x(0) local rail_desc = way_desc_x.get_available_ways(wt_rail, st_tram)[0] local remover = command_x(tool_remove_way) { for (local i = 0; i < 16; ++i) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(15, i, 0), rail_desc, true), null) } ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8", "2AAAAAAAAAAAAAA8" ]) for (local i = 0; i < 16; ++i) { ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(15, i, 0), "" + wt_rail), null) } } RESET_ALL_PLAYER_FUNDS() { for (local i = 0; i < 16; ++i) { ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(15, i, 0), rail_desc, false), null) } // FIXME this is different from the road pattern (which is all straight roads even without ctrl) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "6EEAAAAAAAAAAAE8", "7B9...........3C", "5..............5", "5..............5", "5..............5", "5..............5", "5..............5", "5..............5", "5..............5", "1..............1", "6EEAAAAAAAAAAAE8", "7B9...........3C", "5..............5", "5..............5", "5..............5", "1..............1" ]) ASSERT_EQUAL(remover.work(pl, coord3d(0, 9, 0), coord3d(15, 9, 0), "" + wt_rail), null) ASSERT_EQUAL(remover.work(pl, coord3d(0, 15, 0), coord3d(15, 15, 0), "" + wt_rail), null) for (local i = 0; i < 15; i = i + 10) { ASSERT_EQUAL(remover.work(pl, coord3d(1, i, 0), coord3d(0, 1+i, 0), "" + wt_rail), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, i+1, 0), coord3d(2, i, 0), "" + wt_rail), null) ASSERT_EQUAL(remover.work(pl, coord3d(14, i, 0), coord3d(15, i, 0), "" + wt_rail), null) } } RESET_ALL_PLAYER_FUNDS() } function test_way_tram_build_on_road() { local pl = player_x(0) local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), road, true), null) // build fully on existing road { ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), tramway, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "...4....", "...5....", "...1....", "........", "........" ]) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "........", "........", "...4....", "...5....", "...1....", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_rail), null) } // cross existing road { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), tramway, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "...4....", "...5....", "...1....", "........", "........" ]) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "........", "........", "........", "..2A8...", "........", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_rail), null) } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_tram_build_across_road_bridge() { local pl = player_x(0) local bridge = bridge_desc_x.get_available_bridges(wt_road)[0] local setslope = command_x.set_slope local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] // build bridge ASSERT_EQUAL(setslope(pl, coord3d(3, 3, 0), slope.south), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.north), null) ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 3, 0), bridge), null) // and build tram track { ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), tramway, true), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), [ "........", "........", "........", "...4....", "...5....", "...1....", "........", "........" ]) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 1), [ "........", "........", "........", "...4....", "...5....", "...1....", "........", "........" ]) // do not remove tram tracks here, this is done by tool_remover below } ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 3, 0), slope.flat), null) ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.flat), null) RESET_ALL_PLAYER_FUNDS() } function test_way_tram_build_across_crossing() { local pl = player_x(0) local tramways = way_desc_x.get_available_ways(wt_rail, st_tram) tramways.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) local tramway = tramways[0] local roads = way_desc_x.get_available_ways(wt_road, st_flat) roads.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) local road = roads[0] local rails = way_desc_x.get_available_ways(wt_rail, st_flat) rails.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) rails.filter(@(idx, desc) desc.get_topspeed() >= tramway.get_topspeed()) local rail = rails[0] // build crossing ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), rail, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), road, true), null) // build tram track { ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), tramway, true), null) // crossing should have been replaced by tramway ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_rail), null) } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_rail), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_road), null) RESET_ALL_PLAYER_FUNDS() } function test_way_tram_build_in_tunel() { local pl = player_x(0) local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] local road_tunnel = tunnel_desc_x.get_available_tunnels(wt_road)[0] local rail_tunnel = tunnel_desc_x.get_available_tunnels(wt_rail)[0] ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(1, 2, 0), road_tunnel.get_name()), null) // build tramway through road tunnel { ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), tramway, true), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), "" + wt_rail), null) } // cross road tunnel with tramway { local tunnel_builder = command_x(tool_build_tunnel) tunnel_builder.set_flags(2) // ctrl ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), tramway, true), null) ASSERT_EQUAL(tunnel_builder.work(pl, coord3d(2, 1, 0), rail_tunnel.get_name()), null) ASSERT_EQUAL(tunnel_builder.work(pl, coord3d(2, 3, 0), rail_tunnel.get_name()), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "..0.....", ".2AA8...", "..0.....", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), tramway, true), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "..4.....", ".2FA8...", "..1.....", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_rail), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ "........", "........", ".2AA8...", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", ".2AA8...", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), "" + wt_rail), null) } ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 3, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 3, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 3, 1)), null) RESET_ALL_PLAYER_FUNDS() } function test_way_tram_has_double_slopes() { local roads = way_desc_x.get_available_ways(wt_rail, st_tram) foreach (r in roads) { ASSERT_FALSE(r.has_double_slopes()) } } simutrans-124.5/tests/tests/test_way_tunnel.nut000066400000000000000000000405741520675460500220560ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for building and removal of tunnels // function test_way_tunnel_build_straight() { local digger = command_x(tool_build_tunnel) local remover = command_x(tool_remover) local default_tunnel = tunnel_desc_x.get_available_tunnels(wt_road)[0] local pl = player_x(0) ASSERT_TRUE(default_tunnel != null) { ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 2, 0)), null) digger.set_flags(2) ASSERT_EQUAL(digger.work(pl, tile_x(3, 1, 0), default_tunnel.get_name()), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "...0....", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(digger.work(pl, tile_x(3, 2, 0), default_tunnel.get_name()), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "...0....", "...0....", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0)), null) ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0)), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) digger.set_flags(0) } { ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(digger.work(pl, tile_x(3, 1, 0), default_tunnel.get_name()), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "...4....", "...5....", "...1....", "........", "........", "........", "........" ]) { // test tunnel object local t = tile_x(3, 1, 0) local tunnel = t.find_object(mo_tunnel) ASSERT_TRUE(tunnel != null) ASSERT_EQUAL(tunnel.get_desc().get_name(), default_tunnel.get_name()) } } { ASSERT_EQUAL(digger.work(pl, tile_x(2, 2, 0), default_tunnel.get_name()), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "...4....", "..2D....", "...1....", "........", "........", "........", "........" ]) // building with ctrl digger.set_flags(2) ASSERT_EQUAL(digger.work(pl, tile_x(4, 2, 0), default_tunnel.get_name()), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "...4....", "..2D0...", "...1....", "........", "........", "........", "........" ]) digger.set_flags(0) // remove the single tunnel entrance ASSERT_EQUAL(remover.work(pl, coord3d(4, 2, 0)), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "...4....", "..2D....", "...1....", "........", "........", "........", "........" ]) // remove tunnel network (more than 2 entrances) // should fail without ctrl local err = remover.work(pl, coord3d(3, 3, 0)) ASSERT_EQUAL(err, "This tunnel branches. You can try Control+Click to remove.") ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "...4....", "..2D....", "...1....", "........", "........", "........", "........" ]) remover.set_flags(2) // activate ctrl ASSERT_EQUAL(remover.work(pl, coord3d(3, 3, 0)), null) ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), [ "........", "........", "........", "........", "........", "........", "........", "........" ]) remover.set_flags(0) // deactivate ctrl } // clean up ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 3, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 2, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 3, 0)), null) RESET_ALL_PLAYER_FUNDS() } function test_way_tunnel_build_up_down() { local digger = command_x(tool_build_tunnel) local setslope = command_x.set_slope local remover = command_x(tool_remover) local default_tunnel = tunnel_desc_x.get_available_tunnels(wt_rail)[0] local pl = player_x(0) ASSERT_TRUE(default_tunnel != null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 1, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 1, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 2, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 3, 0)), null) digger.set_flags(2) // ctrl ASSERT_EQUAL(digger.work(pl, coord3d(1, 0, 0), default_tunnel.get_name()), null) ASSERT_EQUAL(digger.work(pl, coord3d(1, 0, 0), coord3d(1, 1, 0), default_tunnel.get_name()), null) // invalid param { ASSERT_EQUAL(setslope(pl, coord3d(1, 1, 0), 42), "Only up and down movement in the underground!") ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), // no change [ ".4......", ".1......", "........", "........", "........", "........", "........", "........" ]) } // Build up: Does not work: surface in the way { ASSERT_EQUAL(setslope(pl, coord3d(1, 1, 0), slope.all_up_slope), "Tile not empty.") ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), // no change [ ".4......", ".1......", "........", "........", "........", "........", "........", "........" ]) } // Build down { local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(setslope(pl, coord3d(1, 1, 0), slope.all_down_slope), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ ".4......", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, -1), [ "........", ".1......", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } // try building duble slope down, rail does not support double slopes { local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(setslope(pl, coord3d(1, 1, -1), slope.all_down_slope), "Tile not empty.") ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ ".4......", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, -1), [ "........", ".1......", "........", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } ASSERT_EQUAL(digger.work(pl, coord3d(1, 1, -1), coord3d(1, 2, -1), default_tunnel.get_name()), null) // Build up { local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(setslope(pl, coord3d(1, 2, -1), slope.all_up_slope), null) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ ".4......", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, -1), [ "........", ".5......", ".1......", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) } // try building double slope up, rail does not support double slopes { ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 2, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 2, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 3, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 3, 1)), null) local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(setslope(pl, coord3d(1, 1, 0), slope.all_up_slope), "") ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), [ ".4......", "........", "........", "........", "........", "........", "........", "........" ]) ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, -1), [ "........", ".5......", ".1......", "........", "........", "........", "........", "........" ]) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 2, 2)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 2, 2)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 3, 2)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 3, 2)), null) } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(1, 0, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 1, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 1, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 3, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 3, 1)), null) RESET_ALL_PLAYER_FUNDS() } function test_way_tunnel_build_above_tunnel_slope() { // Tests building on the tile above the top of a tunnel slope in pak64 // Should be allowed but, as of r11373, isn't. // If way_height_clearance is >= 2, this test DOES NOT APPLY. // FIXME: ignore this test if way_height_clearance >= 2 ? local pl = player_x(0) local default_tunnel = tunnel_desc_x.get_available_tunnels(wt_road)[0] local digger = command_x(tool_build_tunnel) local remover = command_x(tool_remover) local setslope = command_x.set_slope // Prepare area ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 1, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 1, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 2, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 2, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 3, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 3, 1)), null) { digger.set_flags(2) ASSERT_EQUAL(digger.work(pl, tile_x(1, 0, 0), default_tunnel.get_name()), null) ASSERT_EQUAL(digger.work(pl, tile_x(1, 3, 0), default_tunnel.get_name()), null) digger.set_flags(0) ASSERT_EQUAL(digger.work(pl, tile_x(1, 0, 0), tile_x(1, 1, 0), default_tunnel.get_name()), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 1, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 1, -1), slope.all_down_slope), null) ASSERT_EQUAL(digger.work(pl, tile_x(1, 1, -2), tile_x(1, 2, -2), default_tunnel.get_name()), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 2, -2), slope.all_up_slope), null) // Now for the real test: build at z=0 above simple slope from z=-2 to z=-1, should be allowed ASSERT_EQUAL(digger.work(pl, tile_x(1, 3, 0), tile_x(1, 2, 0), default_tunnel.get_name()), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 2, 0)), null) } // Clean up ASSERT_EQUAL(remover.work(pl, coord3d(1, 1, -2)), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 2, -2)), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 0, 0)), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 3, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 1, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 1, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 3, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 3, 1)), null) RESET_ALL_PLAYER_FUNDS() } function test_way_tunnel_build_across_tunnel_slope() { local pl = player_x(0) local default_tunnel = tunnel_desc_x.get_available_tunnels(wt_road)[0] local digger = command_x(tool_build_tunnel) local remover = command_x(tool_remover) local setslope = command_x.set_slope // Prepare area ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 1, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 1, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 2, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 2, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(1, 3, 1)), null) ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(2, 3, 1)), null) { // build lone tunnel mouths digger.set_flags(2) ASSERT_EQUAL(digger.work(pl, tile_x(1, 0, 0), default_tunnel.get_name()), null) ASSERT_EQUAL(digger.work(pl, tile_x(1, 3, 0), default_tunnel.get_name()), null) digger.set_flags(0) // make double slope ASSERT_EQUAL(digger.work(pl, tile_x(1, 0, 0), tile_x(1, 1, 0), default_tunnel.get_name()), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 1, 0), slope.all_down_slope), null) ASSERT_EQUAL(setslope(pl, coord3d(1, 1, -1), slope.all_down_slope), null) local net_wealth = pl.get_current_net_wealth() // try to tunnel across ASSERT_EQUAL(digger.work(pl, tile_x(1, 0, 0), tile_x(1, 3, 0), default_tunnel.get_name()), null) // nothing should be buid here ASSERT_EQUAL( net_wealth, pl.get_current_net_wealth() ) // remove lone tunnel mouth ASSERT_EQUAL(remover.work(pl, coord3d(1, 3, 0)), null) // try to build across it - should fail and not build anything since the tunnel builder // only builds straight tunnels with no elevation changes // however, the player cannot call it like this since before the find_end_pos will return an invalid coordinate local err = digger.work(pl, tile_x(1, 3, 0), default_tunnel.get_name()) ASSERT_EQUAL(err, "Tunnel must start on single way!") } // clean up ASSERT_EQUAL(remover.work(pl, coord3d(1, 1, -2)), null) ASSERT_EQUAL(remover.work(pl, coord3d(1, 0, 0)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 1, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 1, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 2, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(1, 3, 1)), null) ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(2, 3, 1)), null) RESET_ALL_PLAYER_FUNDS() } function test_way_tunnel_make_public() { local pl = player_x(0) local public_pl = player_x(1) local tunnel_desc = tunnel_desc_x.get_available_tunnels(wt_road)[0] local makepublic = command_x(tool_make_stop_public) ASSERT_EQUAL(command_x.grid_raise(public_pl, coord3d(4, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(public_pl, coord3d(5, 3, 0)), null) ASSERT_EQUAL(command_x.grid_raise(public_pl, coord3d(4, 4, 0)), null) ASSERT_EQUAL(command_x.grid_raise(public_pl, coord3d(5, 4, 0)), null) ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(4, 2, 0), tunnel_desc.get_name()), null) // make tunnel portal public { local old_pl_cash = pl.get_current_cash() local old_pl_maint = pl.get_current_maintenance() local old_public_cash = public_pl.get_current_cash() local old_public_maint = public_pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 2, 0)), null) ASSERT_TRUE(way_x(4, 2, 0).get_owner() != null) ASSERT_EQUAL(way_x(4, 2, 0).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(pl.get_current_cash()*100, old_pl_cash*100 - 60 * tunnel_desc.get_maintenance()) // 60 == cst_make_public_months ASSERT_EQUAL(pl.get_current_maintenance(), old_pl_maint - tunnel_desc.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_maintenance(), old_public_maint + tunnel_desc.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_cash(), old_public_cash) } // make tunnel inside public { local old_pl_cash = pl.get_current_cash() local old_pl_maint = pl.get_current_maintenance() local old_public_cash = public_pl.get_current_cash() local old_public_maint = public_pl.get_current_maintenance() ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) ASSERT_TRUE(way_x(4, 3, 0).get_owner() != null) ASSERT_EQUAL(way_x(4, 3, 0).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(pl.get_current_cash()*100, old_pl_cash*100 - 60 * tunnel_desc.get_maintenance()) // 60 == cst_make_public_months ASSERT_EQUAL(pl.get_current_maintenance(), old_pl_maint - tunnel_desc.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_maintenance(), old_public_maint + tunnel_desc.get_maintenance()) ASSERT_EQUAL(public_pl.get_current_cash(), old_public_cash) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(4, 3, 1)), null) ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(5, 3, 1)), null) ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(4, 4, 1)), null) ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(5, 4, 1)), null) RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/tests/tests/test_wayobj.nut000066400000000000000000000317031520675460500211560ustar00rootroot00000000000000// // This file is part of the Simutrans project under the Artistic License. // (see LICENSE.txt) // // // Tests for wayobj // function test_wayobj_build_straight() { local pl = player_x(0) local remover = command_x(tool_remove_wayobj) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local overhead_line = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line())[0] ASSERT_TRUE(overhead_line != null) { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 1, 0), coord3d(2, 1, 0), overhead_line), "") ASSERT_EQUAL(tile_x(2, 1, 0).find_object(mo_wayobj), null) ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 1, 0), coord3d(2, 1, 0), overhead_line), "") ASSERT_EQUAL(tile_x(3, 1, 0).find_object(mo_wayobj), null) ASSERT_EQUAL(tile_x(2, 1, 0).find_object(mo_wayobj), null) } ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(5, 1, 0), rail, true), null) // now build wayobj { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 1, 0), coord3d(2, 1, 0), overhead_line), null) local wo = tile_x(2, 1, 0).find_object(mo_wayobj) ASSERT_TRUE(wo != null) ASSERT_TRUE(wo.is_valid()) } { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 1, 0), coord3d(4, 1, 0), overhead_line), null) local wo = tile_x(3, 1, 0).find_object(mo_wayobj) ASSERT_TRUE(wo != null) ASSERT_TRUE(wo.is_valid()) wo = tile_x(4, 1, 0).find_object(mo_wayobj) ASSERT_TRUE(wo != null) ASSERT_TRUE(wo.is_valid()) // todo: check for wayobj ribis ? } { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 1, 0), coord3d(4, 1, 0), overhead_line), null) local wo = tile_x(3, 1, 0).find_object(mo_wayobj) ASSERT_TRUE(wo != null) ASSERT_TRUE(wo.is_valid()) } // remove wayobj { ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(2, 2, 0), "" + wt_rail), "") ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(3, 1, 0), "" + wt_rail), null) ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(3, 1, 0), "" + wt_rail), null) // returns success even if nothing was removed ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0), coord3d(4, 1, 0), "" + wt_rail), null) ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0), coord3d(4, 1, 0), "" + wt_rail), null) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(5, 1, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_wayobj_build_disconnected() { local pl = player_x(0) local remover = command_x(tool_remove_wayobj) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local overhead_line = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line())[0] ASSERT_TRUE(overhead_line != null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(5, 1, 0), rail, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 5, 0), coord3d(5, 5, 0), rail, true), null) // build disconnected { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 5, 0), coord3d(3, 1, 0), overhead_line), "Ways not connected") ASSERT_EQUAL(tile_x(3, 5, 0).find_object(mo_wayobj), null) ASSERT_EQUAL(tile_x(3, 1, 0).find_object(mo_wayobj), null) } // remove disconnected wayobj { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 1, 0), coord3d(5, 1, 0), overhead_line), null) ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 5, 0), coord3d(5, 5, 0), overhead_line), null) ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0), coord3d(3, 5, 0), "" + wt_rail), "Ways not connected") ASSERT_TRUE(tile_x(3, 1, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(3, 5, 0).find_object(mo_wayobj) != null) } ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 5, 0), rail, true), null) ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 1, 0), coord3d(5, 5, 0), rail, true), null) { ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0), coord3d(3, 5, 0), "" + wt_rail), null) ASSERT_TRUE(tile_x(3, 1, 0).find_object(mo_wayobj) == null) ASSERT_TRUE(tile_x(3, 5, 0).find_object(mo_wayobj) == null) ASSERT_TRUE(tile_x(4, 1, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(4, 5, 0).find_object(mo_wayobj) != null) } // clean up ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0), coord3d(4, 5, 0), "" + wt_rail), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(5, 5, 0), "" + wt_rail), null) ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(5, 5, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_wayobj_upgrade_downgrade() { local pl = player_x(0) local wobj_remover = command_x(tool_remove_wayobj) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local catenaries = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line()) catenaries.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) // FIXME need at least 2 catenaries ASSERT_TRUE(catenaries.len() >= 2) local slow_cat = catenaries[0] local fast_cat = catenaries[1] ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), rail, true), null) // upgrade wayobj { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat), null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) } // upgrade wayobj to same type, should incur no cost { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(slow_cat)) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(slow_cat)) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(slow_cat)) ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) } // upgrade wayobj to faster type { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat), null) ASSERT_TRUE(pl.get_current_cash() < old_cash) ASSERT_TRUE(pl.get_current_maintenance() > old_maint) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) } // downgrade wayobj without ctrl, should fail { ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat), null) local old_cash = pl.get_current_cash() local old_maint = pl.get_current_maintenance() ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) ASSERT_EQUAL(pl.get_current_cash(), old_cash) ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) } // downgrade wayobj with ctrl, should succeed { local builder = command_x(tool_build_wayobj) builder.set_flags(2) // enable ctrl ASSERT_EQUAL(builder.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat.get_name()), null) ASSERT_EQUAL(builder.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat.get_name()), null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_wayobj_upgrade_change_owner() { local pl = player_x(0) local public_pl = player_x(1) local wobj_remover = command_x(tool_remove_wayobj) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local catenaries = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line()) catenaries.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) // FIXME need at least 2 catenaries ASSERT_TRUE(catenaries.len() >= 1) local slow_cat = catenaries[0] local fast_cat = catenaries[1] ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), rail, true), null) // public player: replace catenary of normal player { local builder = command_x(tool_build_wayobj) ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) ASSERT_EQUAL(command_x.build_wayobj(public_pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat), null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_EQUAL(tile_x(3, 4, 0).find_object(mo_wayobj).get_owner().get_name(), public_pl.get_name()) ASSERT_EQUAL(wobj_remover.work(public_pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) } // normal player: replace caterary of public player { local builder = command_x(tool_build_wayobj) ASSERT_EQUAL(command_x.build_wayobj(public_pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat), null) ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) ASSERT_EQUAL(tile_x(3, 4, 0).find_object(mo_wayobj).get_owner().get_name(), pl.get_name()) ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) } // clean up ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) RESET_ALL_PLAYER_FUNDS() } function test_wayobj_electrify_depot() { local pl = player_x(0) local start_pos = coord3d(3, 4, 0) local end_pos = coord3d(5, 4, 0) local wayremover = command_x(tool_remove_way) local wobj_remover = command_x(tool_remove_wayobj) local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] local catenaries = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line()) catenaries.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) local slow_cat = catenaries[0] local fast_cat = catenaries[1] local depot = get_depot_by_wt(wt_rail) ASSERT_TRUE(slow_cat != null) ASSERT_TRUE(fast_cat != null) ASSERT_TRUE(depot != null) ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, rail, true), null) ASSERT_EQUAL(command_x.build_depot(pl, start_pos, depot), null) ASSERT_EQUAL(command_x.build_depot(pl, end_pos, depot), null) { ASSERT_EQUAL(command_x.build_wayobj(pl, start_pos, end_pos, slow_cat), null) ASSERT_TRUE(way_x(start_pos.x, start_pos.y, start_pos.z).is_electrified()) ASSERT_TRUE(way_x(end_pos.x, end_pos.y, end_pos.z).is_electrified()) // TODO check that depot is really electrified } // clean up ASSERT_EQUAL(command_x(tool_remover).work(pl, start_pos, "" + mo_depot_rail), null) ASSERT_EQUAL(command_x(tool_remover).work(pl, end_pos, "" + mo_depot_rail), null) ASSERT_EQUAL(wayremover.work(pl, start_pos, end_pos, "" + wt_rail), null) // also removes way with wayobj RESET_ALL_PLAYER_FUNDS() } simutrans-124.5/themes.src/000077500000000000000000000000001520675460500156375ustar00rootroot00000000000000simutrans-124.5/themes.src/aero/000077500000000000000000000000001520675460500165655ustar00rootroot00000000000000simutrans-124.5/themes.src/aero/aero.tab000066400000000000000000000131071520675460500202050ustar00rootroot00000000000000# # name of the theme (will be shown with file selector) name=Aero skin (standard size) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = #AFEEEE # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 # windows drop a little shadow (default off) # gui_drop_shadows = 1 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 #cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # Use the default height #gui_button_height = 22 # larger icons too icon_width = 32 #################################window stuff################################# # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like Windows) # window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #1E90FF # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window # window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) bottom_window_darkness = 20 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #C0C0C0 # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #A7D1EA #################################colour stuff################################# # color used for the default titlebars default_window_title_color = #338FCC # gui_color_text = #000000 gui_color_text_highlight = #005C9E gui_color_text_shadow = #BDD3DF gui_color_text_title = #338FCC gui_color_text_strong = #E46A0B gui_color_obsolete = #2878ff gui_color_empty = #868c86 gui_color_edit_text = #000000 gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #666666 gui_color_edit_background_selected = #72B2DD gui_color_edit_beam = #1E90FF gui_color_chart_background = #D6EBF7 gui_color_chart_lines_zero = #005C9E gui_color_chart_lines_odd = #FFFFFF gui_color_chart_lines_even = #336699 gui_color_list_text_selected_focus = #FAFAFA gui_color_list_text_selected_nofocus = #FAFAFA gui_color_list_background_selected_focus = #72B2DD gui_color_list_background_selected_nofocus = #72B2DD gui_color_button_text = #000000 gui_color_button_text_disabled = #666666 gui_color_button_text_selected = #FAFAFA gui_color_colored_button_text = #FAFAFA gui_color_colored_button_text_selected = #FAFAFA # gui_color_checkbox_text = #000000 gui_color_checkbox_text_disabled = #666666 gui_color_ticker_background = #D6ECF7 gui_color_ticker_divider = #6095B8 # gui_color_statusbar_text = #000000 gui_color_statusbar_background = #72B2DD gui_color_statusbar_divider = #6095B8 gui_highlight_color = #FAFAFA gui_shadow_color = #CCDDFF gui_color_text_minus = #C00000 gui_color_text_plus = #000000 gui_color_text_unused = #E46A0B # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #ff0000 # Colo loadbar, save game, load game, server... gui_color_loadingbar_progress = #6495ED gui_color_loadingbar_inner = #D6ECF7 # Color player brigth gui_player_color_dark = 1 gui_player_color_bright = 3 ##################################size stuff################################## # force main menu icon size icon_width = 32 # titlebar height gui_titlebar_height = 22 # safe margin around windows # gui_frame_left = 10 # gui_frame_top = 10 # gui_frame_right = 10 # gui_frame_bottom = 10 # horizontal and vertical margins between objects # gui_hspace = 4 # gui_vspace = 4 # tab minimum sizes gui_tab_header_vsize = 18 # input boxes height # minimum height is the height of the position button graphic # gui_edit_height = 22 # checkbox height, default sizes come from the checkbox graphic # gui_checkbox_width = 10 # gui_checkbox_height = 10 # sum of the top and bottom margins for divider lines # default is twice the size of gui_vspace # gui_divider_vsize = 8 # buttons sizes gui_button_height = 16 # for buttons with fixed widths gui_button_width = 100 # text margins inside the buttons (left, top, right) gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 icon_scaling = 100 ###################################pakstuff################################### # pak with the images for this theme themeimages=aerotheme.pak simutrans-124.5/themes.src/aero/alt_back.png000066400000000000000000000030561520675460500210370ustar00rootroot00000000000000PNG  IHDR gIDATxKNMa4H B]#&K [}ᆴ*Hާݗ7ף0!=Ƹ=./ۿj/y~Q JD (%@Q JD (%@Q JD (%@Q JD (%@Q JD (%@Q JD (%@Q JD (%@Q q s?ߞ??~=俼}v) u9z8Gv(Faߞ+Etnbv9s|(c|Zں(1 U?CPa5|f`a۬K@M=+jW?;:/?,B2v7==>|0dl>vcb<zJq_; ;o9/zz}}] nJq;=i[y.Xʜf[ 8܌ۦ,޽kW?/OK(S\[vx ;nzlEAܱW((f]H >>~&s lz9a;`DfI ==q< ϟ6ŽNu!y6boW:֮~7Zy/K >SI}⪠Ӱ#kż\zz(h1Ӯ~~@|MOMMlza0p<[ª5iW?[ʪyStU?m1ѣqͶ">PLe+^Aڦ?zmƬ`qmm J >v=*^.Y]4Yh C?M >f\Y E;!] n)G@ۼ`D >8+m~Ŀy\dZ5c9By1] Ex?m>faw, /t?,==V sG61.}vً6+xn`W`Q JD (%@Q JD (%@Q JD (%@Q JD (%@Q JD (%@Q JD (%@Q JD (%@Q JD (%@>`*IENDB`simutrans-124.5/themes.src/aero/back.png000066400000000000000000000012231520675460500201710ustar00rootroot00000000000000PNG  IHDRRlZIDATxQ 0 EQ/"D?w\mwgڜ?. ,AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHxLdq_ɭtӋ|zܻw//~!z{{Xz1"~:yeX_|&&&cpP?wd_ݻwǏӫW,GFFлwBYOOOtE ʢG_irr2MMMYy@D<ߑ lttrD\9@} 'ӧOE0}ayh@$ @p-ȯ"} ɓ'~@ @7?BcgϞpz*yQ3F88p}A@}sNq#)]!Gg&_}&&?WA@~%e&{@@8@@  @@   @@  @@ @  @@  @@@  @ORc=Z nP P@E@ lH9@~~rD?~rh7(Ӟ/@"+0  8  @@  @@@  @@   @@  @@ @  @@ zZoIENDB`simutrans-124.5/themes.src/aero/checkbutton.png000066400000000000000000000020331520675460500216020ustar00rootroot00000000000000PNG  IHDR@IDATxMHagu_wm,Y+B/DXЩ (S!*:JDE2셂C" u^\"urˬtߦΘ, >l8_;̪LSqi1I>O&gz{ſ`CѷosB5k;!̼%R=yKsk$i޻zvKD"U)scW, ڗȉû:}ݝG18Gh4:Esqҫ"jMW.{I84]JN79wr{FFFhO$(DI!co :`WҪ5'L&3<y=w\lf'''Y;f>9|"}ܹҫjڵ۷ovX!4Y UTڵh>2RzU shC=~\zBQݮNOoٲeǎKKK'իݻwW[V^T7o޼|2Jz7oذallswZՇvj/,, TKөj6UC=*=ꀗ!VQzM@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD[~HZ\\޾~sŋKKKNݻ=ZzQ= i]:U=UC' ~e/=^ܼRjl ,_JO TeTzNmy%WzqJ@\ +v7|DD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDuuzӧhh46^UCwvvNsůN*"'Ol۶6g7244TׯoiU[޼ysƍccc}}}кunܸ177ɓ688XzBoۭ~LM=x`bbbΝkbh~>z*T…{Ϗ/oΜĉCV[ɟWwgzރYKQٳgWޕ+Ww\EI3*s;IENDB`simutrans-124.5/themes.src/aero/gadget.png000066400000000000000000000067501520675460500205360ustar00rootroot00000000000000PNG  IHDR@@}g_H IDATx}p~4DĀq0UzukD[N'7wZ==p0,1l!:iol;f?BB} tvv UUUo>vUjmq%%%K׮U;k{~A׿tzGbG^̟?nU;:w8N.{f~Ȏ]yE߮_S.vSjOBye`Y/yEw͚UHνuU{r[BEA/Z4?t\F+ WVͯrZ$cJIQțUUU;jgNq.hf,,?kO, EG%Cj nz?.xh>3ݧ׬xh?>qLUCw}jc'ͮȬ)JEu͐Yc;.¾k jhP;vlBJð+/nشݟe!V*BËw?޽dT"az''}\d%Fc)4=K ; !g=~^EwL!P6Q>MȦPT F<2])-S;vlkL'O*pfvrMLf$yUP"*1.7`rz>hHGXsQfiqZnx,R]UH,hIoq[.]:1T]OAbNV3:=Mӊb~hqNK'vfƹOn_Yysxf2\@$';.zY$?>g10'>Уoy,? cyn̢^ϳpR6#XCg[.i5)+Nx,6p4"|:RѸ @&jNW/f_IV)3du)5%M,7(TZ+r8eΙ"z!KJŝi.vP."{wQ.xsV [.HF"'*~^oԋfh5VAsܥ10 0tQK?-եT2Eqz:.+ 3xݐ`D;sj&%  GijN~Jg1`4z;O4Kf̆.wdsf3e1Ӣ@@D~PD,E^OqŲں?[p;s([VVV]:1kM},/RL|&Z)VIeR"jNlnEWjm%G2s͢(t*&'cs.\ݺ{H4߸90x+0_:Nϳ7Kȅd4P\>S+^cFYFzrצ2K:n3V?O2Y}(e4Qf3m5+VoRɼ'{S?rFϑ}X sjNlզ-~W 侓[(-&nES0,oK'ǷH.[mhiS:fu)CG7jY1YveUgDOޝGc)r^1 :a]nȒE\ JEo[ɉWQ;TUzz;WDA1H鮿mӗ ط66vv lJ?\Hn*+ԧ-MDasO>i{{FƯ֏. &$>Cd,+ +&(`8ޗ?!TcH (Xjsy3K٨oӾ$+ Ð"24ys l?j7B:PzW/{5}}'n?kO3 U/˳þ]]g";jG^_Z4ol܌25neNk+fPet yI $$@@H $2$@HI $$@@H $$@HI $$@HH $Oh#QGIENDB`simutrans-124.5/themes.src/aero/posbutton.png000066400000000000000000000016671520675460500213420ustar00rootroot00000000000000PNG  IHDR@~IDATxؽkq{y&ikLH֩HPE(UWpPptRС8- .VBZk<]r9_8~{wӵZ-\:лBqC+pRzKյ5uѨHo  |~``ܜ t]zy]jLGӽnW@zP(R)UB꘮X C=gyEjS.V8mϕɫWW) ۚ}~x5ttu9too٬R,//onn^{Wzc|stĻx Eg]56;;;_z:1rbtjJz2___gX ^ZZ_8wؘ d2Y^:Ir,X `A@ ,X `A@ ,X `A@ ,X `A@ ,X `A@ ,X `A@]8VIENDB`simutrans-124.5/themes.src/aero/roundbutton.png000066400000000000000000000044431520675460500216630ustar00rootroot00000000000000PNG  IHDRݾPIDATxolgOI-JS벩+UXClL US!n+66ix i@Tڃ UcѭZf(jjJ+IC$vb}bMg~^R:; j188{k);r ćd7+{*οӧu >=d) QHg͆a\I$dO3hhhHцӲG럇uuumذ\.J%#n4ǀY8;;;;C4$):sSSSGGA\w,qWWWgYV|>o˞ɗl6+2MS4499){.{5::m۶\.EFV@ 7VkEbQ+G"Rb7u4 7ge9sfǎV8=Q?E \!e-b333Z\LD5v $֯_fq#Zb1q:22"%{!8 K/$G[Vn[5^[l.{N8q͟eG^Y0d@jc@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@V}y𫲧acm{IzyjU.~lce~˳dԟnngG7ecOC+8m.,{*:zȞ#OK,AʲGN~=#3Nwˍa}t36ܰo+q|W8 q^^~7 pc -jhW+XYJ5ǃO~) Ugktz*Ҷ Q9SLY\4 G?/~dY][sq硏7xv!ci7s+7_/hmZeˊ]t2 dCk"Cz8B'ؖ晖QݖH!*{"G#'>Kٔhڦj,65dzو+Km`iiQlɯ%NwFF39vDL-X$|睲'ljȞÏ7])6}V4wv&v'|=oPZs]MnܟyIo9r䅣x;W,AYV_#5oe{Ի5|ك -2]BGm;Un%PqˀQHQ-[dOȫVUv^~nTջ6yUꪟ4 CH.3dccfg|(0:*{G^.\8N<dOC$d2.ك꺲G,@ ]dK,m{*Rr,{Hh4*m~ H$=h1B N^Zeg3 E"ؘq;g++ŕ+WdrO*|[[ RIW  sq|S> q,4j:K*݈ *̵d2977Ң\M_L.|>+/{%bQ܈&jbtRV@--,t8m[<#X#Qq.vw7WZKDdz٬8IRI=\Nz@+oe蓓L oIT>̟=+f׭=H@u٬ +USSnk|gfeO\3k_^ xIǀ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€CIENDB`simutrans-124.5/themes.src/aero/scrollbar.png000066400000000000000000000124201520675460500212550ustar00rootroot00000000000000PNG  IHDR560IDATx[LU;ox&E^6klƴhz*XetT6r "9* Y"| ݟ{=8O7Oba޵o=ַv >1T'O,V^.YdPVg6֎WTcc+!\)] ϒrt޿(WJ>|캯+**Q1T3gΦMj SSS___7e~Z(EN+Z(EN+؈ et}*??mJ7# _ ￸ݭ^R~PԀ;;;_QʕA ѣJa߿>v`}e +f̙ŋͿrS__lKO8QQfAR.WB(3J )WJ70'*̈;reLҐGΩS:?,=22Pi3~WχJŋ5pp~5?@JeFQ~YRȏeFQ~YRȏeFQ~YR,SUU%wgppbJ@ٶm'N þ?+1>T\ܿĚUf8Qqb(ED?WBe^q_P2 pԩ#GLzv~{DDļy^(_M>5WP뉯 _ q2"?Jrt޿DeD~(J2%ĉʈ(QʕeΜ9SSSvKJJ-Z4yZ|d]vk֬0a?@j֬YեógϴQZZZ0TcWB)KJrt޿DşHYR+'*(Eʒ(W0]@.]1Ȕ΋3f̘2eJ_F ILLܿBBܹsǍ%eԞO:MNpqg˨JeFQ~ךGR _ q(9ʏR,irt޿DEq9{+ti&_>zH}klڴIT] iiiLT_-]AMSHcg['*ʌ HY҆W _ q(9P%m(WJCv(9ζ%J2]@566v_ =2WwW rt޿(WJ;Zʕe~ ?+2C~$  @@ @@ @@ @@ @@ @@-u)))Vxk\C7:?MN2Фα7{EGGgffVWWu.^v # aaW~ɮ?]R`o~NWvKIeG>ŝv5\[[k:-/(,XSt(hI]湦]f[6{텀srr}e hmm-++Cm PCِ=mE 5'nsvofZ9/ick׮I@---:/nnw^VV~`Ow^ Ϫuu2Ej*Eۗ9 VVV:ҕl {۷ٳqqqZeښs f>WQSk~vWR˼/›EnGƍ ._|ܼ֭y󉉉 `/_wa> 5\~z _^Z… uܹ#iCqI's3@}ȑuٝ}v} /Om]ͥ![6*Mn[:gf P j^ݱcu궼e='7eWO{} m͵ q]r^0uuuuzzzvss_III b%f*}{ ? 螽=CLm7/p޽N;qݻKX]A)nxXk<׵Q6'8&ޘuC_HIIٶm[YYd АMHK#llپ>.}y1bZ3.2:}~;6ސܹ1X[#V7n=?_֞ǒv8"r]=|{!*:ao ;90lgsozW/uJJJM|r\oeOE+Vp,;[uvz^,_o3 @@ @@@@ @@  @@ @@|<|ɓ'=_O:u̙K.|s Zdɂ ˃׮]br---MMM-jooն'''{<eرc OFFNJJRrss̞={…ŋO>}֬Y ҥK466:q\WWРPi wʺuFmmmUUUuuuMMCgxM6%#]JZl͛e'@*9(..N2JIIQ nSHyԱNrN!pi0o޼x'@ҐrD?\@O[S~Ǔ&Md۷o777A:(c%O[ /_ٲِm~s]罛O%~{^Wy_R`lP y_P~pM2\@ $$$\zU{k׮Jgh'Nqyyy޽{eee&}ЊZZx;*?[kV.X1??ѱKYxyj܄Sk>Ono? 4ڡ<϶mt*oݺ4TŶ8(11QKϟ?{lVVڸe{PJy'iY|wki߭XSVh~~wDz>sMse_hZ`mcvܩB02SSSv];vh4'?p{JM;>.} oeyew-᫂] ʩ]҂Ab޸Ovs7\ռ-ȏSe^IG4 jmmtIhXqFlP+{wwyqѾ:_pK.+NGnnn Ҭh=yl5! @@! x_x |x} !r1<F@`XVB@>L o `0F@.xU0ޙXW ƍ~R1F@x<ɿl# xsXZ @@J oLVVS0`F ۇ0&X! `! ,2< x ! @@* V! @@.xU0w`,b# 0]@Lۇ V`,*F@2< xWb .1<F@S@,*# 0]@,w' Ub ~j1F@a  d'X2<0F@ŋЌ`,b ޘܼ^uݻx`]NѧO7g}V= C ۂFIENDB`simutrans-124.5/themes.src/aero/skins_aero.dat000066400000000000000000000103041520675460500214120ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: prissi # # All image name must starts with "> " to have them unzoomeable # # This theme tries a macintosh-look # -- Obj=menu name=Button # unpressed left, middle, right Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will use the unpressed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.0.0 Image[28]=> button.0.2 Image[29]=> button.0.1 Image[30]=> button.0.3 Image[31]=> button.0.5 Image[32]=> button.0.4 Image[33]=> button.0.6 Image[34]=> button.0.8 Image[35]=> button.0.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.2 Image[2]=> editfield.0.1 Image[3]=> editfield.1.0 Image[4]=> editfield.1.2 Image[5]=> editfield.1.1 Image[6]=> editfield.2.0 Image[7]=> editfield.2.2 Image[8]=> editfield.2.1 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.2 Image[2]=> listbox.0.1 Image[3]=> listbox.1.0 Image[4]=> listbox.1.2 Image[5]=> listbox.1.1 Image[6]=> listbox.2.0 Image[7]=> listbox.2.2 Image[8]=> listbox.2.1 -- Obj=menu name=Roundbutton # unpressed left, middle, right # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 -- Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> scrollbar.0.0 Image[1]=> scrollbar.0.1 Image[2]=> scrollbar.0.2 Image[3]=> scrollbar.0.3 Image[4]=> scrollbar.0.4 Image[5]=> scrollbar.0.5 # scrollbar back left center right Image[6]=> scrollbar.1.0 Image[7]=> scrollbar.1.2 Image[8]=> scrollbar.1.1 # scrollbar knob left center right Image[9]=> scrollbar.1.3 Image[10]=> scrollbar.1.5 Image[11]=> scrollbar.1.4 # back up and down Image[12]=> scrollbar.2.0 Image[13]=> scrollbar.2.2 Image[14]=> scrollbar.2.1 # knob up and down Image[15]=> scrollbar.2.3 Image[16]=> scrollbar.2.5 Image[17]=> scrollbar.2.4 # scrollbar back up center down Image[18]=> scrollbar.3.0 Image[19]=> scrollbar.3.2 Image[20]=> scrollbar.3.1 # scrollbar knob up center down Image[21]=> scrollbar.3.3 Image[22]=> scrollbar.3.5 Image[23]=> scrollbar.3.4 -- # window background Obj=menu name=Back Image[0]=> alt_back.0.0 Image[1]=> alt_back.0.1 Image[2]=> alt_back.0.2 Image[3]=> alt_back.1.0 Image[4]=> alt_back.1.1 Image[5]=> alt_back.1.2 Image[6]=> alt_back.2.0 Image[7]=> alt_back.2.1 Image[8]=> alt_back.2.2 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7 # goto pos Image[8]=> gadget.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- --simutrans-124.5/themes.src/aero/skins_aero.png000066400000000000000000000242761520675460500214430ustar00rootroot00000000000000PNG  IHDR%H(IDATx tSeLrI/JA˳@Apأ.xuUqtƃ"0#:3yY38@yR-JySg&i޹{Cwi{/헤܄Thkk#AM6yP(4dyt:݀hѢ!6$=ΚUWW'oe }H i(1 UUN/k?tz?پt^@ACHfi$9_gVϟ?j mozlkgo9stԲPb"RR=]Gl wlجxEUWQH>hf($ż>}Bb3a-T.fy9s .OhWHZAXX3A \MH T5PEcߍ (khIP }[qc3K@Al_@{;g>TXr TɁ5Ћi<#KP0IGAPJHy" (Op&(Ur` vo\:uvv(Pz<U8m1/ (PP%@3pbxsYɲP)3'z\lU} ɲ7å O(kClOC* @*پ C:'3f @2f adhN1ޠ@aKyyJ (9dejƇe(2@ջe(PA+8s T5weVG?Jy @qaRFA*پR34 .C [(PP%@S>Փ改gh\G?J)=aGY  dh 3-aKzH] O^(PP%@/Wi^z~|}../c}DҔK'fIHgJ@o5N(//p5kZx+..d 8vW?ٺt߶mgƌ?\t5y'w}|t=i䔛Cx@VU9-⤉&{-Oo'_>~eI_, Ǐ۳gwIY%IG{L)XO(*)]woCG;wַSlB9ǗM."eYN$@ڳ#7?r}ʹSW8xbI{|3{nl;9uttf6kڼ]3fˣDdzw/N'0GQU9oS\N^q_xul-4yfQqUݑBj5 #biWu&O!3?ziqQ ŘDGXL$\5jc|Θ+gOp9(3Ya3,߬pZ:a"錽ay?IR|;X7dTfIHg(F\ϖQ̮^\P>n"DS{@7E{;9ѳϮ[jhoOt ?K- 騩}g̘gN:'y߬\DGG(2Ǐjv- 67V:g%n`K]{b;}}짻hwpwܹ^+v.N2ζ8-wW?S x63vw[;%6厙O!QNJܟ_Z%6o𘻳ssT.?,1|(}Ras0?W EHT+ex|L’ׯ L?<_m@:m G;Jva疽dŏ>}ŗ~4Kk\K:jJ6n7R9/8X 3'x-c QAA0nO[㤓bL8lTAl˽r4:WNGV`wtʒSbȣ|G(G+Gk Zd2X ZEk9Өr^ D:qnf~1Dn3 Z*D9VPt8'dF^4P4MʏWXHG뗷t5lֶGIK0{5:?1ֶ ˗%m{h/]teY>;;pvk&-Ewǔ/] #]IwG#æp1qao<3]wm:L){޳gt)P/\Zi4S=P6 m uoOV0YKKw,!1/}_|{NM#g;b ΘcKϿ%1&Ad{+Vh4 0ݺutu5}qKW/7B'SMw47#s}սFKJJX{ J7to0\.WvOrzG]@A_:x$M$}o p"ګ sEo 2zw9z3ݕ;9 DqG@?.κlj!wKuDtttDgƍ=غu̫%PP&u/Qs.b>.7xu̯)q\}b ꄱd{ R}O;3rYUv~nWwωd?t:H_<{3/v`ȩ<˿o>s.͡%2nll\lن *++KKKNu/ ʯQSHN[a^fa=7G]c,nK,bOxkjjVXw'ekkʕ: ͨ~\_/]tcǎwf5 EG}SO=5|-뮷@^8[jU'e;-agVyrޓ{WO!SRRV>mڴUV:t\o~0(744_$4݀dܹN<:7TQQFuۏb h߉<ў["YMmư%bPR 2:;UGHHe.GM-qPqx뵝w=ҭHڳIuyPoI 95)sVէ:S\Dl|)П/qeg\eՁS\ ?YίnWEFKGuyH~~aF:R|B0aܵh8q ߈^;'1حf9g.zWR'n':4{ÞA$RKK%\b*2\t>t ï;"V%Ǘ-vT?SUnz$]]]3lC;:N:BB>|"mpðYaDMu [%dٺ3w9_ U*ǽeəa` /6\~ȑiӦuvv:Q`P DAjMM+{nR2ںzZCn-ۘ@o9ij7tmU:e,-###--MthPTf`QCb:NtWZedaf~ۑb8RvۙjmvkKktNg~Suy?$_{3f#8N2}|CC8qx<#$֦^r+qtfFNM wZ ?)+:Q|49$Qg|ɓ'Gm6^K4 o۶@u>ؚ{-m4 .n/2770 vwuum߾ޥKUljS9S?=/9s&\9j9ٵS=Q'nb9$K(&Ҧq5i5&rmذ^ UH H5wbMWDu.Y#z~:V>uʰITWÇ +}__ ޡ~+~֕V-oMU᧥~dEI=B$ $Q I(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@ (Pd999]]]b0 p8RRRĥj⦛T<_TVK/yNWvݩ"|(immuDbI?"P Ç1b@ ꦳S,7xW>ڳg1 RPPPZZjD'Nb`Ҏ;Tgl6keffET'Ae7̈_|ŴiV:a 珪^n]8fYf͛7Ou8_rz+Ɯ|ݻb q)Xu_rFU\y睹ѕBuDW.iJ\Z* :a|m5gΜ:]u$~M,:thB#"}BCC.{"岲RPRR2}tC_~bH@"z ,hYCu7tFMee;wyhBu"? =PXSSAڵkU;`onnZ7E[bxbSAё]biΜ9NMx,\s9Mu"? n9}!ݞ/:ȼ9O_C-z#JD؏?z 6LsC  nU$hT50H.<3\HZ(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@ (PD$ $Q I(H@@~iFFƥ^z{exݼy۶m .t\^3" :mؽ{o3GuسXl7|WB!!.SRR/t:W^/ʗmє.؝:ҜYpDng[sf;\>ein#3pa؍M =LgwzþvCuYN-|5a}sCgQd#$]w:$j坪gVN.qÆ zkOh@ zS,_hooߵk̇R󿫵;x۾kR0ً9ڼscñP[WoZ>mݞ_W% ZlgZ-Cj5/QǏ/,,T36ѡb{ܹsOO޵h\?.?dUiǕHMq;)viKLv㈦#G6#.TBRxq! l`0̓b~>:m e<:Exc2wZ|!x='}ݰ \ݏNU4{/{o|[\7 }><3Xkuȏ?}1"]鑟L^:c&Κ%~T)@ qAch (P@sLա@@G>Ƥ'#`3$(pc3H(P@c -բ@@ h:bBīC*1,$8/ϟ  @A,mlP1P0zlDg^g $(s_9~xb̭}Q& Q%Adk4 MX%.X$yxwvYg+"2rhɆ4n} (4gPE 9Ɣ%|^D@1ǘ`,Phz#МC9 V, x{10xt\`Bsl9}vܜ-"acVC@Ythf &K@ @S `r4߉%aw`Ȋ(54O53VD@l,3fל>Mqvgi;#pd ʔ#ŵdXk6;r3ewu|ߜxhx^ |tгEw{ϺvgE) >rÕ&g=WUOq@$M XJ{J#3VE;inyja TyV&0?j%+ؕ<a#X@h>sOf /39cA7@ e&))Xr9M ,&׍wF(iV)RCƙK`faʍMaQq2ZUTjSUZ[Zq֚D60^:wkogC:ڰfH)SìM[J,^tՖmλl޲uݶ=PQ}Cרэ#V:D ê"3 qu1JIc'FLrĀ`YxO~[pK ?\pn~ig4 bؖbj#FSņd/`a7wg*X* Z4ۢ0qNlZvb]RO8:ݷ?q$.F#219dZ Q pR@Ía{a-AX sA8 3,:o_ЦNzNmpYp}+dki WN_y>Fl 8e<'hG'8ᘴRފSO)^6 oA z{6d TKbzTXtRaw profile type iptcx= @ LIl=a %%묓k) ]t,e:ƀCaޗL91뻪2{ XTiTXtXML:com.adobe.xmp .bKGD pHYs  tIME. IDATxQ 0 @L-@F\ ;m_{,icR^kxk! @@! B@B@ ! B@B@! @@! B@B@ Dc3y>@D޿! @@! B@! ! B@B@ ! @@! B@! ! ~e "\CZ! @@! B@! ! B@B@ ! @@! B@B@ ! ~|O;k KIENDB`simutrans-124.5/themes.src/flat/button.png000066400000000000000000000075301520675460500206130ustar00rootroot00000000000000PNG  IHDR@H^gAMA a :iCCPPhotoshop ICC profileHwTTϽwz0)C 7Da`(34!EDA"""` `QQy3Vt彗g}k=g}ֺtX 4Jc `23B=ÀH>nL"7w+7tI؂dPĩق }F1(1E";cX| v[="ޚ%qQ-["LqEVaf"+IĦ"&BD)+Rn|nbң2ޜT@`d0l[zZ ?KF\[fFf_nM{H? }_z=YQmv|c34 )[W%I Ȱ316rX7(ݝ ⺱SӅ|zfšyq_0sxpєqyv\7GSa؟8"Q>j1>s@7|8ՉŹ,߳e%9-$H*P*@#`l=p0VHiA>@ vjP @h'@8 .:n``a!2D UH 2!y@PAB&*: :]B=h~L2 p"΃ p\ u6<?g! DCJiA^&2L#PEGQި(j5jU:jGnFQ3Oh2Z mC#щlt݈nC_BF`0FcDa1k0Vy f 3bXl `{ǰCq[3yq<\ww7Zx;| ŗ]8~ M!8Ʉ*B !HT'\b8 q$C'bHBvay=+2Mv&G&Ec[ [bDDĐ I* Zc0&8(&iYH~Ho(%46h0װu wKDŽ7EGGDDōFG7FϮX{xULQ̝:+sV^]*uՙXXf8t\DѸ@f=s6'~_ ˍ̮`Oq8圉D]SINII\7n5ewrm\J`ᔅԈ4\Z\) /ד>aQ1n3|?~c&2S@L uYY5YoóOHrrsNy};_-cZuuk/\?kÑ)*0-(/x)bSWr±^$E[nEmnfmOk%%%JY׾1ꛅ ˬir]+wZiYYGgʿs{?T'U߮qiݧo۾C*זԾ?=xΫ^P֡ 2mjTl,ixwxHȑ&JG˚faԱc7sŨZr}wN>8(mP{nLGRHgT)S]]m?x3g]8wn| ƺc\x'ߥ+=/_u=wvWO]c\n}Ϫ'l:o\:xviMoܺ~{;˾;y/Ylx~XHQc?:b=rf}Icda)iDӤ)ϩV<|~W_}oοDΌ\«ï-_w>~f~#zGPQc'O%wu cHRMz&u0`:pQ<bKGD pHYs  tIME,+cIDATxQ"Aaf'CQXHȁ9@$'"0آޒP(OMӯ2m6ʱ?y}bɩjʚj(}~eZ%?Z ` `b ǨY ҟϳC% W @@@0000   @@@@000   @@@000    @@@0000   )a~Ǡ7?}͏000 n?vO[->^#`p_! ` ` ` ` ` ` ` ` ` ` ` ` ` ` `p;}_zӋ.krڽ8EMQPy'@ "[4T=Fͺđ 8}%ՅqxsoIJ?n?>E @@@0000   @@@@000   @@@000    @@@0000   )+Vu-d")p _! ` `pK@KяQӟ H.30   @@@@000   @@@000    @@@0000   @@@@000  n eOrIENDB`simutrans-124.5/themes.src/flat/checkbutton-small.png000066400000000000000000000057351520675460500227240ustar00rootroot00000000000000PNG  IHDR@gAMA a :iCCPPhotoshop ICC profileHwTTϽwz0)C 7Da`(34!EDA"""` `QQy3Vt彗g}k=g}ֺtX 4Jc `23B=ÀH>nL"7w+7tI؂dPĩق }F1(1E";cX| v[="ޚ%qQ-["LqEVaf"+IĦ"&BD)+Rn|nbң2ޜT@`d0l[zZ ?KF\[fFf_nM{H? }_z=YQmv|c34 )[W%I Ȱ316rX7(ݝ ⺱SӅ|zfšyq_0sxpєqyv\7GSa؟8"Q>j1>s@7|8ՉŹ,߳e%9-$H*P*@#`l=p0VHiA>@ vjP @h'@8 .:n``a!2D UH 2!y@PAB&*: :]B=h~L2 p"΃ p\ u6<?g! DCJiA^&2L#PEGQި(j5jU:jGnFQ3Oh2Z mC#щlt݈nC_BF`0FcDa1k0Vy f 3bXl `{ǰCq[3yq<\ww7Zx;| ŗ]8~ M!8Ʉ*B !HT'\b8 q$C'bHBvay=+2Mv&G&Ec[ [bDDĐ I* Zc0&8(&iYH~Ho(%46h0װu wKDŽ7EGGDDōFG7FϮX{xULQ̝:+sV^]*uՙXXf8t\DѸ@f=s6'~_ ˍ̮`Oq8圉D]SINII\7n5ewrm\J`ᔅԈ4\Z\) /ד>aQ1n3|?~c&2S@L uYY5YoóOHrrsNy};_-cZuuk/\?kÑ)*0-(/x)bSWr±^$E[nEmnfmOk%%%JY׾1ꛅ ˬir]+wZiYYGgʿs{?T'U߮qiݧo۾C*זԾ?=xΫ^P֡ 2mjTl,ixwxHȑ&JG˚faԱc7sŨZr}wN>8(mP{nLGRHgT)S]]m?x3g]8wn| ƺc\x'ߥ+=/_u=wvWO]c\n}Ϫ'l:o\:xviMoܺ~{;˾;y/Ylx~XHQc?:b=rf}Icda)iDӤ)ϩV<|~W_}oοDΌ\«ï-_w>~f~#zGPQc'O%wu cHRMz&u0`:pQ<bKGD pHYs  tIME $i IDATxر @a$Bbt={y}]BK0ĺ>z[uD12>o^ϗyڶ}Z)OB@0wo彤$){J8FB@B@ ! B@! B@! @@! B@B@ ! {ʣIENDB`simutrans-124.5/themes.src/flat/divider.png000066400000000000000000000003051520675460500207170ustar00rootroot00000000000000PNG  IHDR@@% bKGD pHYs  tIME )mRIDATh 00o"X:hL%뽋 ' =0WIENDB`simutrans-124.5/themes.src/flat/editfield.png000066400000000000000000000010251520675460500212220ustar00rootroot00000000000000PNG  IHDRݾPbKGD pHYs  tIME" wIDATxA ЉZ<Ȩ DC;;}?ߐcge͈{z! @@! B@! ! B@B@ ! @@! B@! Q9#zfJB@! B@! @@kV ! @@! B@B@ ! B@B@ u0#Al0! B@B@ ! @@! B@B@ ! B@B@ ! ğ4ڷ5IENDB`simutrans-124.5/themes.src/flat/flat-skin-large.tab000066400000000000000000000101731520675460500222370ustar00rootroot00000000000000# Flat Skin theme # Created by Phystam # # name of the theme (will be shown with file selector) name=Flat (large size) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 250 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 10000 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 #cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # in default theme white text gui_text_highlight= #FFFFFF # Use the default height #gui_button_height = 22 # larger icons too icon_width = 32 # windows drop a little shadow (default off) # gui_drop_shadows = 1 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) # Should be rather a user setting, if the theme does not absolutely require it #window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) # Should be rather a user setting, if the theme does not absolutely require it #second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #7b7b7b # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window #window_frame_active = 0 # second: draw the title with a dark overlay (0=none, 100=full black) bottom_window_darkness = 25 # third (best together with 2nd):use different text color for bar front_window_text_color = #FFFFFF bottom_window_text_color = #DDDDDD # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #CFCFCF #################################colour stuff################################# gui_color_text = #101010 gui_color_text_highlight = #fafafa gui_color_text_shadow = #202020 gui_color_text_title = #fafafa gui_color_text_strong = #ef4026 gui_color_obsolete = #00004a gui_color_empty = #FFFFFF gui_color_edit_text = #fafafa gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #333333 gui_color_edit_background_selected = #333333 gui_color_edit_beam = #333333 gui_color_chart_background = #a3a3a3 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #333333 gui_color_chart_lines_even = #646464 gui_color_button_text = #101010 gui_color_button_text_disabled = #333333 gui_color_button_text_selected = #FAFAFA gui_color_loadingbar_progress = #4d4d4d gui_color_loadingbar_inner = #cfcfcf ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 1 (to not occupy so much screen space) icon_scaling = 150 gui_titlebar_height = 22 gui_gadget_width = 22 gui_tab_header_vsize = 26 gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 toolbar_max_width = 0 toolbar_max_height = 0 gui_button_height = 24 gui_button_width = 100 ##################################pakstuff################################## # pak with the images for this theme themeimages=flat.pak simutrans-124.5/themes.src/flat/flat-skin-touch.dat000066400000000000000000000163471520675460500223020ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: Phystam # # All image names must start with "> " to have them non zoomable # # This theme has the scalable standard look # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab #!pak64 -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will use the unpressed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.0.0 Image[28]=> button.0.2 Image[29]=> button.0.1 Image[30]=> button.0.3 Image[31]=> button.0.5 Image[32]=> button.0.4 Image[33]=> button.0.6 Image[34]=> button.0.8 Image[35]=> button.0.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- # titlebar background Obj=menu name=Titlebar Image[0]=> titlebar.0.0 Image[1]=> titlebar.0.1 Image[2]=> titlebar.0.2 Image[3]=> titlebar.1.0 Image[4]=> titlebar.1.1 Image[5]=> titlebar.1.2 Image[6]=> titlebar.2.0 Image[7]=> titlebar.2.1 Image[8]=> titlebar.2.2 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[9]=> titlebar.3.0 Image[10]=> titlebar.3.1 Image[11]=> titlebar.3.2 Image[12]=> titlebar.4.0 Image[13]=> titlebar.4.1 Image[14]=> titlebar.4.2 Image[15]=> titlebar.5.0 Image[16]=> titlebar.5.1 Image[17]=> titlebar.5.2 -- # Gadgets background Obj=menu name=GadgetBack # all gadget-touch except close Image[0]=> titlebar.0.0 Image[1]=> titlebar.0.1 Image[2]=> titlebar.0.2 Image[3]=> titlebar.1.0 Image[4]=> titlebar.1.1 Image[5]=> titlebar.1.2 Image[6]=> titlebar.2.0 Image[7]=> titlebar.2.1 Image[8]=> titlebar.2.2 # color mask for above (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[9]=> titlebar.3.0 Image[10]=> titlebar.3.1 Image[11]=> titlebar.3.2 Image[12]=> titlebar.4.0 Image[13]=> titlebar.4.1 Image[14]=> titlebar.4.2 Image[15]=> titlebar.5.0 Image[16]=> titlebar.5.1 Image[17]=> titlebar.5.2 # close background when buttons at left Image[18]=> titlebar.0.0 Image[19]=> titlebar.0.1 Image[20]=> titlebar.0.3 Image[21]=> titlebar.1.0 Image[22]=> titlebar.1.1 Image[23]=> titlebar.1.2 Image[24]=> titlebar.2.0 Image[25]=> titlebar.2.1 Image[26]=> titlebar.2.2 # color mask for above (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> titlebar.3.0 Image[28]=> titlebar.3.1 Image[29]=> titlebar.3.3 Image[30]=> titlebar.4.0 Image[31]=> titlebar.4.1 Image[32]=> titlebar.4.2 Image[33]=> titlebar.5.0 Image[34]=> titlebar.5.1 Image[35]=> titlebar.5.2 # close background when buttons at right Image[36]=> titlebar.1.3 Image[37]=> titlebar.0.1 Image[38]=> titlebar.0.2 Image[39]=> titlebar.1.0 Image[40]=> titlebar.1.1 Image[41]=> titlebar.1.2 Image[42]=> titlebar.2.0 Image[43]=> titlebar.2.1 Image[44]=> titlebar.2.2 # color mask for above (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[45]=> titlebar.4.3 Image[46]=> titlebar.3.1 Image[47]=> titlebar.3.2 Image[48]=> titlebar.4.0 Image[49]=> titlebar.4.1 Image[50]=> titlebar.4.2 Image[51]=> titlebar.5.0 Image[52]=> titlebar.5.1 Image[53]=> titlebar.5.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton-small.0.0 # pressed Image[1]=> checkbutton-small.0.1 # disabled Image[2]=> checkbutton-small.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton-small.0.0 # pressed Image[1]=> posbutton-small.0.1 # disabled Image[2]=> posbutton-small.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> scrollbar-touch.0.0 Image[1]=> scrollbar-touch.0.1 Image[2]=> scrollbar-touch.0.2 Image[3]=> scrollbar-touch.0.3 Image[4]=> scrollbar-touch.0.4 Image[5]=> scrollbar-touch.0.5 # scrollbar back left center right Image[6]=- Image[7]=> scrollbar-touch.1.6 Image[8]=- # scrollbar knob left center right Image[9]=> scrollbar-touch.1.0 Image[10]=> scrollbar-touch.1.2 Image[11]=> scrollbar-touch.1.1 # back up and down Image[12]=> scrollbar-touch.2.0 Image[13]=> scrollbar-touch.2.2 Image[14]=> scrollbar-touch.2.1 # knob up and down Image[15]=> scrollbar-touch.2.3 Image[16]=> scrollbar-touch.2.5 Image[17]=> scrollbar-touch.2.4 # scrollbar back up center down Image[18]=- Image[19]=> scrollbar-touch.3.6 Image[20]=- # scrollbar knob up center down Image[21]=> scrollbar-touch.3.0 Image[22]=> scrollbar-touch.3.2 Image[23]=> scrollbar-touch.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget-touch.0.0 # help Image[1]=> gadget-touch.0.1 # minimize Image[2]=> gadget-touch.0.2 # previous Image[3]=> gadget-touch.0.3 # next Image[4]=> gadget-touch.0.4 # unsticky Image[5]=> gadget-touch.0.5 # sticky Image[6]=> gadget-touch.0.6 # resize corner Image[7]=> gadget-touch.0.7 # goto pos Image[8]=> gadget-touch.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- -- simutrans-124.5/themes.src/flat/flat-skin-touch.tab000066400000000000000000000101751520675460500222710ustar00rootroot00000000000000# Flat Skin theme # Created by Phystam # # name of the theme (will be shown with file selector) name=Flat (touch) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 250 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 10000 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 #cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # in default theme white text gui_text_highlight= #FFFFFF # Use the default height #gui_button_height = 22 # larger icons too icon_width = 32 # windows drop a little shadow (default off) # gui_drop_shadows = 1 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) # Should be rather a user setting, if the theme does not absolutely require it #window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) # Should be rather a user setting, if the theme does not absolutely require it #second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #7b7b7b # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window #window_frame_active = 0 # second: draw the title with a dark overlay (0=none, 100=full black) bottom_window_darkness = 25 # third (best together with 2nd):use different text color for bar front_window_text_color = #FFFFFF bottom_window_text_color = #DDDDDD # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #CFCFCF #################################colour stuff################################# gui_color_text = #101010 gui_color_text_highlight = #fafafa gui_color_text_shadow = #202020 gui_color_text_title = #fafafa gui_color_text_strong = #ef4026 gui_color_obsolete = #00004a gui_color_empty = #FFFFFF gui_color_edit_text = #fafafa gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #333333 gui_color_edit_background_selected = #333333 gui_color_edit_beam = #333333 gui_color_chart_background = #a3a3a3 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #333333 gui_color_chart_lines_even = #646464 gui_color_button_text = #101010 gui_color_button_text_disabled = #333333 gui_color_button_text_selected = #FAFAFA gui_color_loadingbar_progress = #4d4d4d gui_color_loadingbar_inner = #cfcfcf ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears #toolbar_max_width = 0 toolbar_max_height = 1 (to not occupy so much screen space) icon_scaling = 200 gui_titlebar_height = 28 gui_gadget_width = 28 gui_tab_header_vsize = 26 gui_button_text_offset = 7,0,7 gui_color_button_text_offset = 7,0,7 toolbar_max_width = 0 toolbar_max_height = 0 gui_button_height = 28 gui_button_width = 100 gui_scrollbar_width = 28 gui_scrollbar_height = 28 ##################################pakstuff################################## # pak with the images for this theme themeimages=flat-touch.pak simutrans-124.5/themes.src/flat/flat-skin.dat000066400000000000000000000162611520675460500211550ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: Phystam # # All image names must start with "> " to have them non zoomable # # This theme has the scalable standard look # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab #!pak64 -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will use the unpressed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.0.0 Image[28]=> button.0.2 Image[29]=> button.0.1 Image[30]=> button.0.3 Image[31]=> button.0.5 Image[32]=> button.0.4 Image[33]=> button.0.6 Image[34]=> button.0.8 Image[35]=> button.0.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- # titlebar background Obj=menu name=Titlebar Image[0]=> titlebar.0.0 Image[1]=> titlebar.0.1 Image[2]=> titlebar.0.2 Image[3]=> titlebar.1.0 Image[4]=> titlebar.1.1 Image[5]=> titlebar.1.2 Image[6]=> titlebar.2.0 Image[7]=> titlebar.2.1 Image[8]=> titlebar.2.2 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[9]=> titlebar.3.0 Image[10]=> titlebar.3.1 Image[11]=> titlebar.3.2 Image[12]=> titlebar.4.0 Image[13]=> titlebar.4.1 Image[14]=> titlebar.4.2 Image[15]=> titlebar.5.0 Image[16]=> titlebar.5.1 Image[17]=> titlebar.5.2 -- # Gadgets background Obj=menu name=GadgetBack # all gadget except close Image[0]=> titlebar.0.0 Image[1]=> titlebar.0.1 Image[2]=> titlebar.0.2 Image[3]=> titlebar.1.0 Image[4]=> titlebar.1.1 Image[5]=> titlebar.1.2 Image[6]=> titlebar.2.0 Image[7]=> titlebar.2.1 Image[8]=> titlebar.2.2 # color mask for above (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[9]=> titlebar.3.0 Image[10]=> titlebar.3.1 Image[11]=> titlebar.3.2 Image[12]=> titlebar.4.0 Image[13]=> titlebar.4.1 Image[14]=> titlebar.4.2 Image[15]=> titlebar.5.0 Image[16]=> titlebar.5.1 Image[17]=> titlebar.5.2 # close background when buttons at left Image[18]=> titlebar.0.0 Image[19]=> titlebar.0.1 Image[20]=> titlebar.0.3 Image[21]=> titlebar.1.0 Image[22]=> titlebar.1.1 Image[23]=> titlebar.1.2 Image[24]=> titlebar.2.0 Image[25]=> titlebar.2.1 Image[26]=> titlebar.2.2 # color mask for above (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> titlebar.3.0 Image[28]=> titlebar.3.1 Image[29]=> titlebar.3.3 Image[30]=> titlebar.4.0 Image[31]=> titlebar.4.1 Image[32]=> titlebar.4.2 Image[33]=> titlebar.5.0 Image[34]=> titlebar.5.1 Image[35]=> titlebar.5.2 # close background when buttons at right Image[36]=> titlebar.1.3 Image[37]=> titlebar.0.1 Image[38]=> titlebar.0.2 Image[39]=> titlebar.1.0 Image[40]=> titlebar.1.1 Image[41]=> titlebar.1.2 Image[42]=> titlebar.2.0 Image[43]=> titlebar.2.1 Image[44]=> titlebar.2.2 # color mask for above (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[45]=> titlebar.4.3 Image[46]=> titlebar.3.1 Image[47]=> titlebar.3.2 Image[48]=> titlebar.4.0 Image[49]=> titlebar.4.1 Image[50]=> titlebar.4.2 Image[51]=> titlebar.5.0 Image[52]=> titlebar.5.1 Image[53]=> titlebar.5.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton-small.0.0 # pressed Image[1]=> checkbutton-small.0.1 # disabled Image[2]=> checkbutton-small.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton-small.0.0 # pressed Image[1]=> posbutton-small.0.1 # disabled Image[2]=> posbutton-small.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> scrollbar-small.0.0 Image[1]=> scrollbar-small.0.1 Image[2]=> scrollbar-small.0.2 Image[3]=> scrollbar-small.0.3 Image[4]=> scrollbar-small.0.4 Image[5]=> scrollbar-small.0.5 # scrollbar back left center right Image[6]=- Image[7]=> scrollbar-small.1.6 Image[8]=- # scrollbar knob left center right Image[9]=> scrollbar-small.1.0 Image[10]=> scrollbar-small.1.2 Image[11]=> scrollbar-small.1.1 # back up and down Image[12]=> scrollbar-small.2.0 Image[13]=> scrollbar-small.2.2 Image[14]=> scrollbar-small.2.1 # knob up and down Image[15]=> scrollbar-small.2.3 Image[16]=> scrollbar-small.2.5 Image[17]=> scrollbar-small.2.4 # scrollbar back up center down Image[18]=- Image[19]=> scrollbar-small.3.6 Image[20]=- # scrollbar knob up center down Image[21]=> scrollbar-small.3.0 Image[22]=> scrollbar-small.3.2 Image[23]=> scrollbar-small.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7,-2,-2 # goto pos Image[8]=> gadget.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- -- simutrans-124.5/themes.src/flat/flat-skin.tab000066400000000000000000000077471520675460500211640ustar00rootroot00000000000000# Flat Skin theme # Created by Phystam # # name of the theme (will be shown with file selector) name=Flat # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 250 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 10000 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 #cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # in default theme white text gui_text_highlight= #FFFFFF # Use the default height #gui_button_height = 22 # larger icons too icon_width = 32 # windows drop a little shadow (default off) # gui_drop_shadows = 1 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) # Should be rather a user setting, if the theme does not absolutely require it #window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) # Should be rather a user setting, if the theme does not absolutely require it #second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # color used for the default titlebars default_window_title_color = #7b7b7b # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window #window_frame_active = 0 # second: draw the title with a dark overlay (0=none, 100=full black) bottom_window_darkness = 25 # third (best together with 2nd):use different text color for bar front_window_text_color = #FFFFFF bottom_window_text_color = #DDDDDD # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #CFCFCF #################################colour stuff################################# gui_color_text = #101010 gui_color_text_highlight = #fafafa gui_color_text_shadow = #202020 gui_color_text_title = #fafafa gui_color_text_strong = #ef4026 gui_color_obsolete = #00004a gui_color_empty = #FFFFFF gui_color_edit_text = #fafafa gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #333333 gui_color_edit_background_selected = #333333 gui_color_edit_beam = #333333 gui_color_chart_background = #a3a3a3 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #333333 gui_color_chart_lines_even = #646464 gui_color_button_text = #101010 gui_color_button_text_disabled = #333333 gui_color_button_text_selected = #FAFAFA gui_color_loadingbar_progress = #4d4d4d gui_color_loadingbar_inner = #cfcfcf ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears icon_scaling = 100 # maximum size of tool bars in tools (0 = no limit, negative relative to window width) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 ##################################pakstuff################################## # pak with the images for this theme themeimages=flat.pak simutrans-124.5/themes.src/flat/gadget-touch.png000066400000000000000000000025221520675460500216470ustar00rootroot00000000000000PNG  IHDR@@}g_HIDATxMN0 U7{TK 7@41OF0'z~}eʾ.`oJ.gb$#pH[ `~~`^OÍP*$VBb<~?ͯO  dTfczEdXtxr;Ei E:O+2gחvQJ?;{%,WD柫+ӻ"%f`Xt(Z_W^pCV-rlҨ)Ǯ{֍RHŠ6Gߋ:Pחʹ(lj{f\[dapkwonn.5ջVzbyVn2y>cu/5 ? w!3?f#?͊]ג&l;0W?Π{ Z1ºvŇ_Jj6=6dr$2, Kw^>7Chm>j 6Ysڄo!#aPup q 5;zr崬 2*\t|;K^O6fPO47ېk2!{2v~]yW 7<ǸL_i8AO7E--z|" F zC_7݈ƽVx[˯Iq1L VY4A75,@&en=@ZyUρAvcǬ ϡC|>Uj)ՖGEo'آZF@;գl`|8F۳_>xnPAɽ+ahyx٧r3]d(({hOlZYwJ%P N~I$he~lȴȥ弟;@h=!1Qa:sǸ𭯽vh}<2ZveJY׻qHzʴF<zh[l4hm| F[lKIg)^vjckܩQ~^>+wޟ^ڃb@sZ ?&< 2n4*M]u,ߎzێZ z7k>ev긟AY_..Nn_oJw?ޮ ԛ0eӇ'lގʤ>V:nNm P1k A|*2E/dz?*w/c`d쐲@?j?Rܼ {@++Oatw3HW-1(Wn@/u d t HGt HGt HGt HGt HGt HGt HGt HGt HGt HGt HGt HG bt IENDB`simutrans-124.5/themes.src/flat/listbox.png000066400000000000000000000141561520675460500207660ustar00rootroot00000000000000PNG  IHDRݾPzTXtRaw profile type exifxW[( gl06 `?R[m|~,CRK+7X~WK1{osѕЦ{~CoޒnAC`ښ%~~W˹ϤGsp :Rĵl-:N;W$:=}{!{By磻s^4 [myOp/걔sL終Cq aNwq@\ɩѢyA&fhgIHZ&j z 2hvL&#u]hO_.36rYS9=Gx7 qa-KDztpN'1x;"C BIPTf% 4X)s$Na 1޺ҙW7@H*IMM `,͆j$)bRTrRQMf-jZYlb̪5¤Z5(mvÌ:s^v뵷y(C:'G{q n^M2uڬ-J+/YeUW{v5zэF,yU"hӉl̀g@@,Yj$̓X={A[܂_B.lraCw#OPvvtY}-,hlO=SņY-~B ppO4n`YS.kXV諓4~YۢhG*l:HqB.#5- skҷCJu\ZoK/;>|tq0;>!(|HuFN{fߣ j⏠SR̀cUf*HQp'4 NF22g] ObKGDC pHYs  tIME01IDATxA !@ѥYѤT2X==jđ/ O݌! @@! B@! ! B@B@ ! @@! B@! 9zyJ9gB@! @@! p! @@! B@! ! B@B@ ! @@|ù^;y?V! @@! B@! ! B@B@ ! @@! B@! ! hUIENDB`simutrans-124.5/themes.src/flat/posbutton-small.png000066400000000000000000000061261520675460500224430ustar00rootroot00000000000000PNG  IHDR@gAMA a :iCCPPhotoshop ICC profileHwTTϽwz0)C 7Da`(34!EDA"""` `QQy3Vt彗g}k=g}ֺtX 4Jc `23B=ÀH>nL"7w+7tI؂dPĩق }F1(1E";cX| v[="ޚ%qQ-["LqEVaf"+IĦ"&BD)+Rn|nbң2ޜT@`d0l[zZ ?KF\[fFf_nM{H? }_z=YQmv|c34 )[W%I Ȱ316rX7(ݝ ⺱SӅ|zfšyq_0sxpєqyv\7GSa؟8"Q>j1>s@7|8ՉŹ,߳e%9-$H*P*@#`l=p0VHiA>@ vjP @h'@8 .:n``a!2D UH 2!y@PAB&*: :]B=h~L2 p"΃ p\ u6<?g! DCJiA^&2L#PEGQި(j5jU:jGnFQ3Oh2Z mC#щlt݈nC_BF`0FcDa1k0Vy f 3bXl `{ǰCq[3yq<\ww7Zx;| ŗ]8~ M!8Ʉ*B !HT'\b8 q$C'bHBvay=+2Mv&G&Ec[ [bDDĐ I* Zc0&8(&iYH~Ho(%46h0װu wKDŽ7EGGDDōFG7FϮX{xULQ̝:+sV^]*uՙXXf8t\DѸ@f=s6'~_ ˍ̮`Oq8圉D]SINII\7n5ewrm\J`ᔅԈ4\Z\) /ד>aQ1n3|?~c&2S@L uYY5YoóOHrrsNy};_-cZuuk/\?kÑ)*0-(/x)bSWr±^$E[nEmnfmOk%%%JY׾1ꛅ ˬir]+wZiYYGgʿs{?T'U߮qiݧo۾C*זԾ?=xΫ^P֡ 2mjTl,ixwxHȑ&JG˚faԱc7sŨZr}wN>8(mP{nLGRHgT)S]]m?x3g]8wn| ƺc\x'ߥ+=/_u=wvWO]c\n}Ϫ'l:o\:xviMoܺ~{;˾;y/Ylx~XHQc?:b=rf}Icda)iDӤ)ϩV<|~W_}oοDΌ\«ï-_w>~f~#zGPQc'O%wu cHRMz&u0`:pQ<bKGD pHYs  tIME 9QBaIDATxؽM@V=@-v`Hv`sZdOPBOz'ݫ|i)EnL"7w+7tI؂dPĩق }F1(1E";cX| v[="ޚ%qQ-["LqEVaf"+IĦ"&BD)+Rn|nbң2ޜT@`d0l[zZ ?KF\[fFf_nM{H? }_z=YQmv|c34 )[W%I Ȱ316rX7(ݝ ⺱SӅ|zfšyq_0sxpєqyv\7GSa؟8"Q>j1>s@7|8ՉŹ,߳e%9-$H*P*@#`l=p0VHiA>@ vjP @h'@8 .:n``a!2D UH 2!y@PAB&*: :]B=h~L2 p"΃ p\ u6<?g! DCJiA^&2L#PEGQި(j5jU:jGnFQ3Oh2Z mC#щlt݈nC_BF`0FcDa1k0Vy f 3bXl `{ǰCq[3yq<\ww7Zx;| ŗ]8~ M!8Ʉ*B !HT'\b8 q$C'bHBvay=+2Mv&G&Ec[ [bDDĐ I* Zc0&8(&iYH~Ho(%46h0װu wKDŽ7EGGDDōFG7FϮX{xULQ̝:+sV^]*uՙXXf8t\DѸ@f=s6'~_ ˍ̮`Oq8圉D]SINII\7n5ewrm\J`ᔅԈ4\Z\) /ד>aQ1n3|?~c&2S@L uYY5YoóOHrrsNy};_-cZuuk/\?kÑ)*0-(/x)bSWr±^$E[nEmnfmOk%%%JY׾1ꛅ ˬir]+wZiYYGgʿs{?T'U߮qiݧo۾C*זԾ?=xΫ^P֡ 2mjTl,ixwxHȑ&JG˚faԱc7sŨZr}wN>8(mP{nLGRHgT)S]]m?x3g]8wn| ƺc\x'ߥ+=/_u=wvWO]c\n}Ϫ'l:o\:xviMoܺ~{;˾;y/Ylx~XHQc?:b=rf}Icda)iDӤ)ϩV<|~W_}oοDΌ\«ï-_w>~f~#zGPQc'O%wu cHRMz&u0`:pQ<bKGD pHYs  tIME92IDATx1nYq)#+`Y490|4ohl7Hb!s  kc{m)?ٞts>gb4 />>?"eYpCxMW/_͏9Z] hJ,%y^UUA?/_X66z{{Rjfψo,~?zn٫Qi&qrʲ,f3 Y@'ɪA7sML&Pл{OT64Y^CyN@˲,"+i6N1UEQ0K)mwAW>C Зs8z]@ݮ}|F8Izeնm?ˏ^cclz vnۭ7ۙs:7DL@@@@PP@@@@@@PP@@@@@@PPP@@@@PPP@@@@PP@@ NUUqp1E_2?<ϐݪ*MPP@@@@Vs+$չY,\GxP@@@@@@PPP@@@@PPP@@@@PP@@@@PP@@@@PPֻyx/ |Ӿ[ϻN'v@,'u|O}^1?<}zOJ,˸/)mz5=}n) Rt;)ibAY) ^Mg׳ݸ@?|Xg47!|3lPDzqCϊxX;XoGxŀPU=WX ]sв,(Xm|Cӻ l?4G@GXo#CY罆ЪfzA\b9G(A׿lh_B.;h߼g~aLC A:#<OށQd(b~,wPP@@@@@@PPP@@@@PPP@@@@PP@@@@PP@@@@PPשj睏.F[P׵o}i~RGg~p@@@@PP@@@@@@PP@@@@@@PPP@@@@PPP@@@@PPu @@@@PPP@@@@?[mhIENDB`simutrans-124.5/themes.src/flat/scrollbar-touch.png000066400000000000000000000041431520675460500224000ustar00rootroot00000000000000PNG  IHDR1*IDATxQ[Wa)\IȊl܃X ~,2wDH94?LDC8mZsGh|'8iC+tЊ%i)Z騨'o:*WߊiC+t _6!deT_Qz+VA3QwNVF_*Wz+VWWcT8ί5ݬ/ç 'e}q P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P֛cc?v@hJr P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B Pn?<~{|?j~W?wz}nv6g|o/0i (@H@B P܀>ꇜ4 fzn{8깻-v_/C Up5TaжOOObCwj2Pe|%t%mkJZ꾠*j` ;T;g_q}Cc=渷GG'W{{ujo?g#S]e#t0PTiUz|'G}6tz66tz6T #{;/o˱:g'۩ Vԩy Jx#s3kϬ:'GfNCǬg3cֳ?c-ll;9ol7tz6sz/?=z54~JPCǯgs׳9>|uPZPZ~CԳ94#LbP/Ck/C82tcQ)?߉cS:J8D=-tC5Ψ7G+g8SкZw9Zqv\Cz6u٨(@H@B PzSgi>I}Y{C\ $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $f9x?`ݺ̟j翹u _]%<@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P 'wZ3x7-IENDB`simutrans-124.5/themes.src/flat/titlebar.png000066400000000000000000000021401520675460500210760ustar00rootroot00000000000000PNG  IHDR͠+bKGD pHYs  tIME"'EHZIDATx0@QZ\IH/\PF*y Pg1#e;|=r*̃k (/1J*[* m_K)7MſO>|@ @ @ @ @ @  #@ eխ~Ow}ƕutz?g\K\__Os@ @ @ @ @ @ @[yO/@Df/8"lcQ껿s@ @ @ @ @ @  #@ @ z֟Z?k)IWx,ǜ'@ @ @ @ @ @ @ .n־n{x5DDc[;=8wψ o ,ǜo@ @ @ @ @ @ 0@jzOu ?Y,ǜ@ @ @ @ @ @  @ . /dQPIENDB`simutrans-124.5/themes.src/highcontrast-large/000077500000000000000000000000001520675460500214245ustar00rootroot00000000000000simutrans-124.5/themes.src/highcontrast-large/back.png000066400000000000000000000007601520675460500230350ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe<IDATxԱ 0 EAe9oa$VpP~xLUu: rۿ ! @@! B@B@ ! B@B@! @@! B@B@)|:Z @! B@B@ ! @@! B@! ! B@B@ ! @@;3c.v! B@B@ ! @@! B@!  ! B@B@ !  Lk 6#EIENDB`simutrans-124.5/themes.src/highcontrast-large/button.png000066400000000000000000000030111520675460500234400ustar00rootroot00000000000000PNG  IHDR@H^tEXtSoftwareAdobe ImageReadyqe<IDATxܿK+Iq6VV2 *VXOeJKKԑ`moc*F n<ϗ)uoaw$/Yٜ*jME9hWrkOxswXȲϟ/'~]LQɊd>@t1U*@z0A000 ` ` `/)   @@@@000   @@@000    @@@0000   @@@@0nUz~bSn箟ﰐeI_9f|E= `pJVk]Vv0XA[AHװfff677xii{[W{ppv:N3SDn{}}VVFa7`777avwwQu:asssageeuFQ4, &82/dfd"%R000   @@@000    @@@0000   @@@@000   `lLw[2˝vx%ǟv= G#t~nb0`!PEc'44@RQ: am`[`Պm2BH g=Tx͍@'czg Vߛ@KQN8,,,99933vww/..|kkk 7:rɄ˽wh$ц=؃B 9Ut?9++Kz) 4d^]]IWWW'%%U=(?}i322T 8x\\\QQN\tB Ȟ{&?55h4&$$ Px ⦦<Ύf۠QpG uuujDt]]]cccX㥔L^%%%7,,&{ Na_4(;;5&==S՘Jh$]'(N===+++9"@$4ejj sVii)zd<5L-i'윘}R#sE&''!GaaVZ EI%ހzq-:ј2aO/uoooiiitt}{pzz {Qԍ?RfQ h4_!㑑EZZ*嫓'Ӫ# Q1aaEǯ|HimͿE~YV\ UUU]i333# ߨ[[[Cʰt|>ѯ $k?V/bjJ=UPxBUHp)"IENDB`simutrans-124.5/themes.src/highcontrast-large/divider.png000066400000000000000000000002601520675460500235560ustar00rootroot00000000000000PNG  IHDR@@% tEXtSoftwareAdobe ImageReadyqe<RIDATxA  gnЀNRVd󹙤,IENDB`simutrans-124.5/themes.src/highcontrast-large/editfield.png000066400000000000000000000007611520675460500240670ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe<IDATx @ E]",AA\}oӪ~k>~XhXweQܹsR~ Ё1ď19F`_1c(9tWaŐ8C΢Դ/?\YYY>}BRaݻ=Ç6z,>4wHz~m۶Be W>KٱcGl NyL_L$jAxB(τ %%%,<:Bx*u^~Cz,X0k,ǎ۳gaC?.Fn߾-< P 1 .Dp  &F3eʔǏ%F1 ºv:|{:˗/_njfaTBBB>}C0w!tnݺHl0S7MgggMߴk'N©?Daz XTT蟚8M쇛!ᨴʒ/n{W췱Q>zHT{qqȑ#333۴icB!Zz*2~8`W=U(Ea'ftI6SnnٲE{'L*&HWll$Ĝ)۷o;:W'P'e\~ȑ#tT| ]_΢ȳ15| 1CzANclӧ=<<0`A `Ah!ƍ[f<.ٳg>>>/_ƠYWWԌhK}VYYDrSSS#4/?0ڻ_fƛaI(@!K@ e t`oAIJJӚ0e8pP訨(ؗZ3 dgΜw2n~Ç'MKhĉ~ ؼy}Th*74T2<.ɧO`JJJ<==aC d|EQˆ#a , ֡a;vxmZZX#!Z1c^ èl؎0~ze.heNģxbR]v;mR߁?>88m۶'m۶ y󦬬 lb'f{`yyyrU؊T{0uEfVGa%ԏgK]y3y@M/-DT-~sssqmDDr皚yۗ,Y"ʕ+޿?1,x*%%Etǰa6ltݼy]\\wBlٲwP Xxx8yK{s]=Y}2ָkQ3~h'Oe/AksBn*eL!]ڧu~$nnnǎٳguu5r׮]y]GΝ;Al3-=(RU~ԯ0׈vV)**JKKCbAnݺ%;qc:9ХeV+!c?BT6~$* 大ϙ3GdCvv6ӿ}:''SN^P/W1)x WaQDAkAoEӵkWlVTTܸq#Rhz"T/t` ̌Ow4^ [sssvAċ}}}Ux_ w^~mccpgQQQڗhPogTVV3x} VL{(88X}2gAA}-_$߈P25#n]3MҲcǎ{쉈P6HGGDŽhX`ȟS(b)c#y0`9s^URR"^\;zo-Y?go B0~?sxBH3!BF!P! ! !B#B(`B(`BBB!0B!0B!F!P!P! !B#B#B(`BBBB!0B!F!F!P! ! !B#B(`BBBB!0B!0B!F!P!P! !B#B0I@/IENDB`simutrans-124.5/themes.src/highcontrast-large/high-contrast-large.tab000066400000000000000000000122221520675460500257550ustar00rootroot00000000000000# # # Read the wiki article for better explanation and full parameters list # https://wiki.simutrans.com/en_themeDef # # name of the theme (will be shown with file selector) name=High Contrast (large size) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = #000000 # tooltip text color (240=black, 215=white) tooltip_text_color = 215 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 # windows drop a little shadow (default off) # gui_drop_shadows = 1 #################################window stuff################################# # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like Windows) # window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window # window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) front_window_bar_color = 1 bottom_window_bar_color = 3 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #C0C0C0 # make the chat window transparent in network games gui_chat_window_network_transparency = 13 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #000000 #################################colour stuff################################# default_window_title_color = #194766 gui_color_text = #c98926 gui_color_text_highlight = #FAFAFA gui_color_text_shadow = #868c86 gui_color_text_title = #bee6c1 #gui_color_text_strong = #E46A0B gui_color_empty = #FAFAFA gui_color_obsolete = #2878ff gui_color_edit_text = #c98926 gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #868c86 gui_color_edit_background_selected = #5686a6 gui_color_edit_beam = #ffffff gui_color_chart_background = #333333 gui_color_chart_lines_zero = #FAFAFA gui_color_chart_lines_odd = #39a1e6 gui_color_chart_lines_even = #ffffff gui_color_list_text_selected_focus = #FAFAFA gui_color_list_text_selected_nofocus = #FAFAFA gui_color_list_background_selected_focus = #5686a6 gui_color_list_background_selected_nofocus = #5686a6 gui_color_button_text = #FAFAFA gui_color_button_text_disabled = #868c86 gui_color_button_text_selected = #b3b3b3 gui_color_colored_button_text = #FAFAFA gui_color_colored_button_text_selected = #b3b3b3 gui_color_checkbox_text = #c98926 gui_color_checkbox_text_disabled = #868c86 gui_color_ticker_background = #000000 gui_color_ticker_divider = #ffffff gui_color_statusbar_text = #c98926 gui_color_statusbar_background = #000000 gui_color_statusbar_divider = #ffffff gui_highlight_color = #ffffff gui_shadow_color = #ffffff # gui_color_text_minus = #C00000 gui_color_text_plus = #c98926 # gui_color_text_unused = #E46A0B # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #000000 # Color of load bar in loag game, load serve etc gui_color_loadingbar_progress = #000000 gui_color_loadingbar_inner = #868c86 # Transparency color in factories boost icons (mail, energy, passengers) gui_color_image_transparency = #ffffff gui_player_color_dark = 5 gui_player_color_bright = 5 ##################################size stuff################################## # force main menu icon size icon_scaling = 150 # titlebar height gui_titlebar_height = 22 gui_gadget_width = 22 # safe margin around windows # gui_frame_left = 10 # gui_frame_top = 10 # gui_frame_right = 10 # gui_frame_bottom = 10 # horizontal and vertical margins between objects # gui_hspace = 4 # gui_vspace = 4 # tab minimum sizes gui_tab_header_vsize = 26 # gui_label_size = 24 # input boxes height # minimum height is the height of the position button graphic # gui_edit_height = 24 # checkbox height, default sizes come from the checkbox graphic # gui_checkbox_width = 10 # gui_checkbox_height = 10 # sum of the top and bottom margins for divider lines # default is twice the size of gui_vspace # gui_divider_vsize = 8 # buttons sizes gui_button_height = 24 # for buttons with fixed widths gui_button_width = 100 # text margins inside the buttons (left, top, right) gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 ###################################pakstuff################################### # pak with the images for this theme themeimages=highcontrast-large.pak simutrans-124.5/themes.src/highcontrast-large/listbox.png000066400000000000000000000007551520675460500236250ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe<IDATxԱ 0 E1? ) w u+ `ՕB@! @@! B@B@ ! B@! B@! @@! B@xS%y>Ϊ >! @@! B@! ! B@B@ ! @@! B@! ! ~b$U]`\ ! B@B@ ! @@! B@B@ ! B@B@B@`N[:IENDB`simutrans-124.5/themes.src/highcontrast-large/posbutton.png000066400000000000000000000007021520675460500241660ustar00rootroot00000000000000PNG  IHDR@tEXtSoftwareAdobe ImageReadyqe<dIDATxi0XNC*ct zq.FP'"[$!YdT[o~k߀bh/  !nMKB.XmPy~~zz>.޻^| t.܏CuhQ56Yee)j"4bh@C)v:F3gdh(CrZ8ŧD=g4=PO z(,kl_Ɩ?S==Aǵ<gfv D=衧H > $@HH $$@@H $@HH $$@@zi ۍIENDB`simutrans-124.5/themes.src/highcontrast-large/roundbutton.png000066400000000000000000000017141520675460500245200ustar00rootroot00000000000000PNG  IHDR@H^tEXtSoftwareAdobe ImageReadyqe<nIDATx 0 @Ѹb bRCUE.\CdˑV9_Ye(=9/> kN   @@@@000   @@@000    @@@0000   @@@@000   @`g:Qzr2xl`K\sl` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `+m<ՙUғc]-`@@@0000   @@@@000   @@@000    @@@0000   @@@@000   @@@0000   @@@@000   @@@000    @@@0000   @@ y(FGfFIENDB`simutrans-124.5/themes.src/highcontrast-large/scrollbar.png000066400000000000000000000063701520675460500241230ustar00rootroot00000000000000PNG  IHDRJY0tEXtSoftwareAdobe ImageReadyqe< IDATxOYM/n'L G"4p٣̎'I&!M^LP=Q- l7~>HUWy]]{U]]___8uHگ|j>|jC_m?*#FGG;;;r|#jF|{{{=:>>`֦߿dLm,7/---,,ػqh0#<6f>n 0d:4111)zJokfI˅Mvs0>}:ǧwF,ߞ.EO-;0T3V> z*m'd Be{*Y5xGm!d>݇ҷ9|x/8;w4MyÇ[N{M.]jNM?M.^lNMccc===-iPU<|LE~?/9~-"Qtn7U.W}[D6I499YDYd] "RxKAp'9^ .IGbpyx1d:==jS\yا>J_~O{Sr\K_A_;@ @ @ @ 81 /nL .+++qQ)r ̙3qK*pg.Ovܶ{lwW6oɓ'?A w&쩩8=::Z^T-kg˫%}}}Ej7DM;781mV3}ʝ 5bZ*ٰBX- +M;Œ1l-q@d<ڴPWk˳pmy6Qr2\[M(; FQJ%΂2}qgA 굀:;;[l\VKt=Wkl\VKSœ>+õa6.}MLL/&Ow.&f2;;[v?P? Ox~k'~/|{~~P/*@ @ @ @ @ ϑS@/~6wqyS[nܹ^`̟Ka[@ @ @ @  @ @ ^@ xIENDB`simutrans-124.5/themes.src/highcontrast-large/theme.dat000066400000000000000000000120011520675460500232120ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: prissi, modified by Leartin # # All image name must starts with "> " to have them unzoomeable # # This theme has the scalable standard look for pak 192.comic # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will used the unporessed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # unpressed Image[0]=> roundbutton.0.0 Image[1]=> roundbutton.0.2 Image[2]=> roundbutton.0.1 Image[3]=> roundbutton.0.3 Image[4]=> roundbutton.0.5 Image[5]=> roundbutton.0.4 Image[6]=> roundbutton.0.6 Image[7]=> roundbutton.0.8 Image[8]=> roundbutton.0.7 # pressed Image[9]=> roundbutton.1.0 Image[10]=> roundbutton.1.2 Image[11]=> roundbutton.1.1 Image[12]=> roundbutton.1.3 Image[13]=> roundbutton.1.5 Image[14]=> roundbutton.1.4 Image[15]=> roundbutton.1.6 Image[16]=> roundbutton.1.8 Image[17]=> roundbutton.1.7 # disabled Image[18]=> roundbutton.2.0 Image[19]=> roundbutton.2.2 Image[20]=> roundbutton.2.1 Image[21]=> roundbutton.2.3 Image[22]=> roundbutton.2.5 Image[23]=> roundbutton.2.4 Image[24]=> roundbutton.2.6 Image[25]=> roundbutton.2.8 Image[26]=> roundbutton.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> scrollbar.0.0 Image[1]=> scrollbar.0.1 Image[2]=> scrollbar.0.2 Image[3]=> scrollbar.0.3 Image[4]=> scrollbar.0.4 Image[5]=> scrollbar.0.5 # scrollbar back left center right Image[6]=- Image[7]=> scrollbar.1.6 Image[8]=- # scrollbar knob left center right Image[9]=> scrollbar.1.0 Image[10]=> scrollbar.1.2 Image[11]=> scrollbar.1.1 # back up and down Image[12]=> scrollbar.2.0 Image[13]=> scrollbar.2.2 Image[14]=> scrollbar.2.1 # knob up and down Image[15]=> scrollbar.2.3 Image[16]=> scrollbar.2.5 Image[17]=> scrollbar.2.4 # scrollbar back up center down Image[18]=- Image[19]=> scrollbar.3.6 Image[20]=- # scrollbar knob up center down Image[21]=> scrollbar.3.0 Image[22]=> scrollbar.3.2 Image[23]=> scrollbar.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7 # goto pos Image[8]=> gadget.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- --simutrans-124.5/themes.src/highcontrast/000077500000000000000000000000001520675460500203345ustar00rootroot00000000000000simutrans-124.5/themes.src/highcontrast/back.png000066400000000000000000000007461520675460500217510ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe<IDATxQ @ D+A. 3ھM{;Vϕ,xk! @@! B@B@ ! B@B@! @@! B@B@ DIr7pT}^ ! @@! B@B@ ! B@B@! @@! B@B@ }$_B@! @@! B@B@ ! B@! B@! @@! d! B@_xnIENDB`simutrans-124.5/themes.src/highcontrast/button.png000066400000000000000000000030111520675460500223500ustar00rootroot00000000000000PNG  IHDR@H^tEXtSoftwareAdobe ImageReadyqe<IDATxܿK+Iq6VV2 *VXOeJKKԑ`moc*F n<ϗ)uoaw$/Yٜ*jME9hWrkOxswXȲϟ/'~]LQɊd>@t1U*@z0A000 ` ` `/)   @@@@000   @@@000    @@@0000   @@@@0nUz~bSn箟ﰐeI_9f|E= `pJVk]Vv0XA[AHװfff677xii{[W{ppv:N3SDn{}}VVFa7`777avwwQu:asssageeuFQ4, &82/dfd"%R000   @@@000    @@@0000   @@@@000   `lLw[2˝vx%ǟv=]?}˾&4ǫ,5]r:>`%;|ϻ4 p8Nlyt^h|:b<Ǜ;NKE6j*sf|תuc:od4ٹ| kUEC/qԓ1N@q܉t[D7Y@iY.>ԶJ//>x! B@B@ ! @@! B@! ! B@o ikcfJIENDB`simutrans-124.5/themes.src/highcontrast/divider.png000066400000000000000000000002601520675460500224660ustar00rootroot00000000000000PNG  IHDR@@% tEXtSoftwareAdobe ImageReadyqe<RIDATxA  gnЀNRVd󹙤,IENDB`simutrans-124.5/themes.src/highcontrast/editfield.png000066400000000000000000000007551520675460500230020ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe<IDATx 0 A)Pvͱs 0BIfV}~! @@! B@B@ ! B@B@! @@! B@B@ Ds5{W I@! B@B@ ! @@! B@! ! B@B@ ! @@'NI\̀|qC_B@ ! B@B@! @@! B@YB@B@ ! B@! B@0g \IENDB`simutrans-124.5/themes.src/highcontrast/gadget.png000066400000000000000000000037561520675460500223100ustar00rootroot00000000000000PNG  IHDR@@}g_HtEXtSoftwareAdobe ImageReadyqe<IDATxkHTi"2(kZa"GJE%ܲB:Lً( F&MР&r(hT".RF`Kogg-Z[ys9NJwwwk|?S?S_,NRRRzMS7^zz/NNjjllihh(..Z/EpH0%9`ݞx''gO܅ -ÇwڼygϮ_hѢ1c\jff#G B.eb߲(:ydFF 3W ؀t?|pGF7̬CF)ٓѣG7 ZI)ZK.uvvݻ7}ݹsl9k6lذk& %Y$q!r劚x,kÔ)Sܺ֏925e=&^w`&M:|}}uFx4KmԏJ h466]pŅ Lĉ˗]مj\7nܘ9sfjjjWW׃VX1p2LWVV[БD[[s2555W^]dJ4U㨖O/yIQQvͭӹGey6ygΞ=[k͍7@/Q?9s~ 6KKKD"a(7f 5Hٟ0a| N>ݣt+ Vvİ~":]A6b^ׯOl`/>rf2]tC.a2kg1лb]@Mp@N(:<_ڸzEQUUz ._BC=/Cd_~&$@H $@HH $$@@H $@HH $$@@H?: 0T6IENDB`simutrans-124.5/themes.src/highcontrast/roundbutton.png000066400000000000000000000017331520675460500234310ustar00rootroot00000000000000PNG  IHDR@H^tEXtSoftwareAdobe ImageReadyqe<}IDATx @E%ˠ'(Ò0c8^&Dfn[k_t4FX3Ts 000   @@@000    @@@0000   @@@@000   @@@000    Ά;8 OGce?blP_ ` ` ` ` ` ` ` ` ` ` ` ` `@osYTpƈ~?Ś)" ` ` ` ` ` ` ` ` ` ` ` ` ` ` `@]2}D H83000   @@@000    @@@0000   @@@@000   @@@000    ~ 4D^*!ynIENDB`simutrans-124.5/themes.src/highcontrast/scrollbar.png000066400000000000000000000061611520675460500230310ustar00rootroot00000000000000PNG  IHDRJY0tEXtSoftwareAdobe ImageReadyqe< IDATx]hTg34`#!Anl€J[QB[WjnZ]RheZ۴EtMۛX &m@Zofߙ֏n~sw;g&ѓdKnR#¿\.w[?hZ_<?䓍7F{ ɞ'o3yKf0HP &҇: 0T€T$aqǥuuu6lXxĉk׮ŵW&%뒦^ٳR]]`3fTUU7o~Ǐ_R8p`͝=%~!9|$`krBLcfѳ\rԩ#G:W^H;_N^{5'#V]wpҰg;w+ Iê?sGɞ#KpXTS[{۶cǎI&Z[[-[VYYYZw'O>|xӦM钰qHAɣcrtjժ9sTTTV]~ٳ~AKKKSS̙3njSƍ_bEO5&Ӧ'U lOg'#x.\8uVlȑ nݺu]veO6mZ([/m߾}e[gdʔ’O?K";vl!\ ~mGGGDs-Yd3gOQ>S z)s?ϝ;w }:Fv>盵/_~P8LP gm0e/^ kUEͻ} (6t[lPZQPZtPW\YS/ٳ'9C^L?n!p}}Qo7dvkkk8i$ٲi3R8i(NϐIUW^moo!)  /׋.Exn2XEe%@K" e_20imm;wn(:::VX.m 5P8k)YjՄ Bq]vƍǏc&pg7Hƍ+LKK;f_tѣC1x̄n>c;uuu˖- ummoLE¸Dg?^"~].?odǰG?Co S##P?<\0,@ @ @@ @ @ 5z2s}n;v<Ͼ׬Yso߾=k¨@Yw>[@O;9RY; a @ @ @  @ @ @ @ -Y,UIENDB`simutrans-124.5/themes.src/highcontrast/theme.dat000066400000000000000000000120011520675460500221220ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: prissi, modified by Leartin # # All image name must starts with "> " to have them unzoomeable # # This theme has the scalable standard look for pak 192.comic # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will used the unporessed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # unpressed Image[0]=> roundbutton.0.0 Image[1]=> roundbutton.0.2 Image[2]=> roundbutton.0.1 Image[3]=> roundbutton.0.3 Image[4]=> roundbutton.0.5 Image[5]=> roundbutton.0.4 Image[6]=> roundbutton.0.6 Image[7]=> roundbutton.0.8 Image[8]=> roundbutton.0.7 # pressed Image[9]=> roundbutton.1.0 Image[10]=> roundbutton.1.2 Image[11]=> roundbutton.1.1 Image[12]=> roundbutton.1.3 Image[13]=> roundbutton.1.5 Image[14]=> roundbutton.1.4 Image[15]=> roundbutton.1.6 Image[16]=> roundbutton.1.8 Image[17]=> roundbutton.1.7 # disabled Image[18]=> roundbutton.2.0 Image[19]=> roundbutton.2.2 Image[20]=> roundbutton.2.1 Image[21]=> roundbutton.2.3 Image[22]=> roundbutton.2.5 Image[23]=> roundbutton.2.4 Image[24]=> roundbutton.2.6 Image[25]=> roundbutton.2.8 Image[26]=> roundbutton.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> scrollbar.0.0 Image[1]=> scrollbar.0.1 Image[2]=> scrollbar.0.2 Image[3]=> scrollbar.0.3 Image[4]=> scrollbar.0.4 Image[5]=> scrollbar.0.5 # scrollbar back left center right Image[6]=- Image[7]=> scrollbar.1.6 Image[8]=- # scrollbar knob left center right Image[9]=> scrollbar.1.0 Image[10]=> scrollbar.1.2 Image[11]=> scrollbar.1.1 # back up and down Image[12]=> scrollbar.2.0 Image[13]=> scrollbar.2.2 Image[14]=> scrollbar.2.1 # knob up and down Image[15]=> scrollbar.2.3 Image[16]=> scrollbar.2.5 Image[17]=> scrollbar.2.4 # scrollbar back up center down Image[18]=- Image[19]=> scrollbar.3.6 Image[20]=- # scrollbar knob up center down Image[21]=> scrollbar.3.0 Image[22]=> scrollbar.3.2 Image[23]=> scrollbar.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7 # goto pos Image[8]=> gadget.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- --simutrans-124.5/themes.src/modern/000077500000000000000000000000001520675460500171235ustar00rootroot00000000000000simutrans-124.5/themes.src/modern/back.png000066400000000000000000000415271520675460500205420ustar00rootroot00000000000000PNG  IHDRRl pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T15:56:11-03:00 2018-05-20T15:56:11-03:00 image/png 3 * wsRGB xmp.iid:289933eb-34d7-7046-b4c2-bd44cb633cbd xmp.did:44001ed7-4aa3-1c4b-acc4-13a698be1c5a xmp.did:44001ed7-4aa3-1c4b-acc4-13a698be1c5a created xmp.iid:44001ed7-4aa3-1c4b-acc4-13a698be1c5a 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:c75c4423-b803-3044-952d-751cc1e76333 2014-04-03T22:11-03:00 Adobe Photoshop CC (Windows) / saved xmp.iid:289933eb-34d7-7046-b4c2-bd44cb633cbd 2018-05-20T15:56:11-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 192 192 c cHRMz'dv9~:DIDATxׯJQdV1z`% ؼe+‚KXD0,"ɊScPo;|sF)%P%#@ @ @ @ @ @ @ @ `Q4;a9ڨig6uxzA]w @ @ @ @ @ @  @ 'y>>Mr{൶u7+Gדfk#I2+ܽ$WOIr\q3hJt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T15:38:06-03:00 2018-05-20T15:38:06-03:00 image/png 3 * wsRGB xmp.did:a0d628f0-9eb0-dc4a-9a99-79196e93bd0d xmp.iid:30960daa-b92a-9546-b4c8-9b380812ce57 xmp.did:a0d628f0-9eb0-dc4a-9a99-79196e93bd0d xmp.did:a0d628f0-9eb0-dc4a-9a99-79196e93bd0d created xmp.iid:a0d628f0-9eb0-dc4a-9a99-79196e93bd0d 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:b3be8c66-9192-3940-ae87-1d0cac43c5d8 2014-04-03T23:51:10-03:00 Adobe Photoshop CC (Windows) / saved xmp.iid:30960daa-b92a-9546-b4c8-9b380812ce57 2018-05-20T15:38:06-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 576 256  s cHRMz'dv9~:IDATx?oT--clV HhH+ _ӄKKAoa423BBrjiW[i%@hx-f|`xn  :DyϹgLI]\j?_FI֒ W[)̹6;t/J_ݿ;YFwF:Sѯ?o'{ڸ;zNr"Uus|f|,d2|g{eʹcR7B7@֞/;Irr(K'9獻bTN988(pfIqo'<7}8ICY,}~0O6'dY=U /m637d/f>~$Cˡ,IJt^$(ɉ$7~n^Jqȑ٧O4M2 u}Ϳ m%[ٹ$b9d?IRUUz^~ æi2Ӷmxidh9e:R-~"n9eۃT4M,6lG 6O(@@  @@   @@  @@@  @@   @@  @_>'Uasо|{c>J?e7tފM%i2H6m sQrPM܏@j[@@  @@   @@  @@ @  @@  @@@  @@ w/mIENDB`simutrans-124.5/themes.src/modern/checkbutton-small.png000066400000000000000000000412241520675460500232530ustar00rootroot00000000000000PNG  IHDR@Llx pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T16:23:20-03:00 2018-05-20T16:23:20-03:00 image/png 3 * wsRGB xmp.iid:4a5bfd30-1efd-8a4f-b8c1-917b2d770753 xmp.did:5b23a10f-64a9-8444-afec-90050295a64f xmp.did:5b23a10f-64a9-8444-afec-90050295a64f created xmp.iid:5b23a10f-64a9-8444-afec-90050295a64f 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:67b2a5ab-bf60-8644-95be-a6a903eb1140 2017-03-15T21:02:51-03:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:4a5bfd30-1efd-8a4f-b8c1-917b2d770753 2018-05-20T16:23:20-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 192 64 x cHRMz'dv9~:zIDATx1NAOCB [J@Il+dm†Lb=<üprvdJUK}{=|>u]9 f\]^w~aC"7Fj8v.m[5MSII[Jz?ß$b_2۔ڂ?םPwˋ3 :v]o=m\ܭ; lX#3S7MOۭ ^A @ @ @ @ @ @ `DOIENDB`simutrans-124.5/themes.src/modern/checkbutton.png000066400000000000000000000421561520675460500221520ustar00rootroot00000000000000PNG  IHDR@Llx pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T20:31:16-03:00 2018-05-20T20:31:16-03:00 image/png 3 * wsRGB xmp.did:a0d628f0-9eb0-dc4a-9a99-79196e93bd0d xmp.iid:298d6879-51e3-714a-ab83-a90f8ff0e09a xmp.did:65acacb1-325c-6f4f-b675-de5fc347692c xmp.did:65acacb1-325c-6f4f-b675-de5fc347692c created xmp.iid:65acacb1-325c-6f4f-b675-de5fc347692c 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:89dcb680-cb9e-4d43-b08d-a765a5a1363e 2017-03-15T21:02:53-03:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:298d6879-51e3-714a-ab83-a90f8ff0e09a 2018-05-20T20:31:16-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 192 64 o cHRMz'dv9~:IDATxڿka'GJGRt)b+Y.Z8t ͐t)tU)\ X[\*IABZh|ֻw^%$[z&'_֫I^_{j@9 ̖' c=4[祝aW}8Jz؏0 INOOkq׀+ W~aa/H?"b(DQ`q<9'0zϟ&9|_Lw`}{qS4^`#*woenANN΋5?@m~(c F5dHNxk7{\dL׍^ ojn1y:f$Ϡcm,=L;C/;Z6l6^tf- n{T{mWx#0F~8i4$I$Ih4qs#9_C=_ RU $ @2$ @2$ @2$ @2$ @2$ @2$ @2$ @2$ @2$ @2$ @2$ @oF=s?IENDB`simutrans-124.5/themes.src/modern/divider.png000066400000000000000000000406141520675460500212640ustar00rootroot00000000000000PNG  IHDR@@iq pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T16:02:24-03:00 2018-05-20T16:02:24-03:00 image/png 3 * wsRGB xmp.iid:4e131d24-6645-074e-a50a-9fecd7612153 xmp.did:a171d045-6984-e64c-ba8b-157f6178a32b xmp.did:a171d045-6984-e64c-ba8b-157f6178a32b created xmp.iid:a171d045-6984-e64c-ba8b-157f6178a32b 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:18fb08cc-21d4-5340-8f6a-c1be75d0763b 2017-03-15T21:02:50-03:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:4e131d24-6645-074e-a50a-9fecd7612153 2018-05-20T16:02:24-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 64 64 W8 cHRMz'dv9~:sIDATx  9>w*U='`\nIENDB`simutrans-124.5/themes.src/modern/editfield.png000066400000000000000000000414721520675460500215720ustar00rootroot00000000000000PNG  IHDRRl pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T17:11:53-03:00 2018-05-20T17:11:53-03:00 image/png 3 * wsRGB xmp.iid:99a63b93-2601-cb48-86db-02ef834f38d3 xmp.did:c215b240-9b8f-7044-a264-acd98be88211 xmp.did:c215b240-9b8f-7044-a264-acd98be88211 created xmp.iid:c215b240-9b8f-7044-a264-acd98be88211 2014-01-31T17:15:40-02:00 Adobe Photoshop CC 2017 (Windows) saved xmp.iid:1c1ac3b5-241f-7f47-8433-56119550371a 2018-05-20T17:08:18-03:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:99a63b93-2601-cb48-86db-02ef834f38d3 2018-05-20T17:11:53-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 192 192 e cHRMz'dv9~:IDATxױQ@ߌk"2bR"Z%P;@:E1&q .!t$T}o }K2 OihJo$e>:~6quih}tIl_t`@ @ @ @ @ @ @ @pZI ?ͦLo@ @ @ @ @ @ 0@ ; `ׯϠ:y=wmt2 -0kK;ɱo* @ @ @ @ @ @ @ @ ܍?snIENDB`simutrans-124.5/themes.src/modern/gadget.png000066400000000000000000000421021520675460500210630ustar00rootroot00000000000000PNG  IHDR@@ pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T20:36:45-03:00 2018-05-20T20:36:45-03:00 image/png 3 * wsRGB xmp.iid:c252be1d-21d9-3e45-91bb-0e4b19085a83 xmp.did:5d2c9fca-00dd-1f41-a15e-af5d6a447761 xmp.did:5d2c9fca-00dd-1f41-a15e-af5d6a447761 created xmp.iid:5d2c9fca-00dd-1f41-a15e-af5d6a447761 2014-01-31T17:15:40-02:00 Adobe Photoshop CC 2017 (Windows) saved xmp.iid:518b804b-2044-c54c-b614-5c099de1caf6 2018-05-20T15:25:04-03:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:c252be1d-21d9-3e45-91bb-0e4b19085a83 2018-05-20T20:36:45-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 576 64 >ל, cHRMz'dv9~:#IDATxݱq0QRiXn5[ʅH{w7$A,Es)eT_~L4߮'k vN0sYqV#}.,e )ܻ/X{P'Ffߚ Rʿ7A&E@2t~Hy/=~vkQW&?AC?(~2#AzPh (Ep{ @< omNcY.6~^u}>4>ö@Y]͢<?6+ih(3~۝+d/z~yuo㡷S4\y'M>RPdcO[zu ޢw@#yQn@y8y 8^لz-\[$UQX;s>b`x:n6Q> ~ Q4x@  @@  @@@  @@   @@  @@@<XIENDB`simutrans-124.5/themes.src/modern/listbox.png000066400000000000000000000411111520675460500213130ustar00rootroot00000000000000PNG  IHDRRl pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T17:12:07-03:00 2018-05-20T17:12:07-03:00 image/png 3 * wsRGB xmp.did:c215b240-9b8f-7044-a264-acd98be88211 xmp.iid:91ec5a08-e1bd-ea4b-81ac-9c2430b68007 xmp.did:2cfaaaa4-9d95-d248-94af-63f562d61bec xmp.did:2cfaaaa4-9d95-d248-94af-63f562d61bec created xmp.iid:2cfaaaa4-9d95-d248-94af-63f562d61bec 2014-01-31T17:15:40-02:00 Adobe Photoshop CC 2017 (Windows) saved xmp.iid:91ec5a08-e1bd-ea4b-81ac-9c2430b68007 2018-05-20T17:12:07-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 192 192 ~ cHRMz'dv9~:IDATxױQ@ߌk"2bR"Z%P;@:E1&q .!t$T}o }K2 OihJo$e>:~6quih}tIl_t`@ @ @ @ @ @ @ @pZI ?ͦLo@ @ @ @ @ @ 0@ ; `ׯϠ:y=wmt2 -0kK;ɱo* @ @ @ @ @ @ @ @ ܍?snIENDB`simutrans-124.5/themes.src/modern/posbutton-small.png000066400000000000000000000427511520675460500230050ustar00rootroot00000000000000PNG  IHDR@Llx pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T19:19:58-03:00 2018-05-20T19:19:58-03:00 image/png 3 * wsRGB adobe:docid:photoshop:d11ea01d-5c7b-11e8-83d5-8b2a77c378fe xmp.did:5b23a10f-64a9-8444-afec-90050295a64f xmp.did:65acacb1-325c-6f4f-b675-de5fc347692c xmp.did:906000d6-0fcb-7d4a-92fd-6321c11bbc13 xmp.did:923e78b0-ad70-bf40-8908-229684da8ad1 xmp.iid:722741ec-42e5-0b42-ae10-ed9c1595e4cf xmp.did:906000d6-0fcb-7d4a-92fd-6321c11bbc13 xmp.did:906000d6-0fcb-7d4a-92fd-6321c11bbc13 created xmp.iid:906000d6-0fcb-7d4a-92fd-6321c11bbc13 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:32aa376c-7741-674f-a8e8-907fa9bac528 2017-03-15T21:02:43-03:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:722741ec-42e5-0b42-ae10-ed9c1595e4cf 2018-05-20T19:19:58-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 192 64 h# cHRMz'dv9~:IDATx=LaXH+`-0Li`qp$2c'B+$7'.`t JRLj[h#@}oy?kOdnǹJt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T19:38-03:00 2018-05-20T19:38-03:00 image/png 3 * wsRGB adobe:docid:photoshop:d11ea01d-5c7b-11e8-83d5-8b2a77c378fe xmp.did:5b23a10f-64a9-8444-afec-90050295a64f xmp.did:65acacb1-325c-6f4f-b675-de5fc347692c xmp.did:906000d6-0fcb-7d4a-92fd-6321c11bbc13 xmp.did:923e78b0-ad70-bf40-8908-229684da8ad1 xmp.iid:6068431f-fd8d-404e-ac81-da485fcbe2c6 xmp.did:d5d7b47a-7cd9-6c4d-b4e6-cf0493e304ec xmp.did:d5d7b47a-7cd9-6c4d-b4e6-cf0493e304ec created xmp.iid:d5d7b47a-7cd9-6c4d-b4e6-cf0493e304ec 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:2b5f03b0-5d94-1649-960f-dbf3c8c8cf70 2014-04-03T23:08:59-03:00 Adobe Photoshop CC (Windows) / saved xmp.iid:6068431f-fd8d-404e-ac81-da485fcbe2c6 2018-05-20T19:38-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 192 64 V cHRMz'dv9~:IDATxڱoe8ܐ0hGHЂTu!C3T,! T1 R%rԁ-R*1${=_z4T!w>;gK)KQf]+>Z^hI7zB¿81t V5\Nc0~Nn7q,!q^f~n!\E.b~`f83ce 6Lv;^vrovNjb~~Zx%ЯDϞ.Wܾ+A6?olln'^ t?ߋwW6G8;VCdh#/a_Ӷ4j8Q8<;ԭQ7_έ{+䫟p TFmaYʻL , i6X__G4/߄x?Oʦq||57FZ qRIM0=~ yO7:!R/yΎpp2}]R5r  x#x-|{?m#d2ۿzjy{o%.Zmkԕ,۵EJh3rϳC}8a~`?lVv=f(:^lU^ywzN4s[&1L~।D>VH @z! b~?ϡ92=1"@ bD 1"@ bD 1"@ bD 1"@ bD 1"@ bD 1"@ bDgRgIENDB`simutrans-124.5/themes.src/modern/scrollbar-small.png000066400000000000000000000543461520675460500227360ustar00rootroot00000000000000PNG  IHDR$ͦ pHYs  iCCPPhotoshop ICC profilexklSeƟs]Wu\/ۜ`D`- +vsZ_<{<E! "*JtA *! !\8 "K` D=<_ 3y]RW@Ga<1Xu-uy?8[_uu W.ɺ$d=U:Sۂ|Vu\5۴5TvQ Z`j]}p.cu5qc@Ƃ^v: lبEva <` J97GEn'WgLLMQ{ ʄXBk‰ĜD!%$$&M:megao Z6%[g$oXe֔̔öս,^SR7J9mNڅ>Jt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=; Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T19:19:17-03:00 2018-05-20T19:19:17-03:00 image/png 3 * wsRGB xmp.did:5b23a10f-64a9-8444-afec-90050295a64f xmp.did:65acacb1-325c-6f4f-b675-de5fc347692c xmp.did:906000d6-0fcb-7d4a-92fd-6321c11bbc13 xmp.did:923e78b0-ad70-bf40-8908-229684da8ad1 xmp.iid:51712e0b-289a-9a4a-bf72-296a67ebec46 adobe:docid:photoshop:d11ea01d-5c7b-11e8-83d5-8b2a77c378fe xmp.did:923e78b0-ad70-bf40-8908-229684da8ad1 created xmp.iid:923e78b0-ad70-bf40-8908-229684da8ad1 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:cfe2683b-4399-be4f-9e8b-8143cff4272c 2017-03-15T21:02:39-03:00 Adobe Photoshop CC 2017 (Windows) / derived converted from image/png to application/vnd.adobe.photoshop saved xmp.iid:f8cc9e9a-3887-0e44-9f99-28746f8b8ce3 2018-05-20T19:19:09-03:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:c6c71cf0-3aa3-4846-bea3-0604440224ca 2018-05-20T19:19:17-03:00 Adobe Photoshop CC 2017 (Windows) / converted from application/vnd.adobe.photoshop to image/png derived converted from application/vnd.adobe.photoshop to image/png saved xmp.iid:51712e0b-289a-9a4a-bf72-296a67ebec46 2018-05-20T19:19:17-03:00 Adobe Photoshop CC 2017 (Windows) / xmp.iid:c6c71cf0-3aa3-4846-bea3-0604440224ca xmp.did:f8cc9e9a-3887-0e44-9f99-28746f8b8ce3 xmp.did:923e78b0-ad70-bf40-8908-229684da8ad1 1 720000/10000 720000/10000 2 65535 448 256 p2: cHRMz'dv9~: IDATxoόW8cK! &.T +U !\ VJ.C,T!"*a7RJV $mqgb؞CN4Z-}y9>G:k*IV(ɝ ]۞ݫJ%uuɅxT]rntB!HC>">y^;ngx FJKBw4?k"}*WEBtuuEDDm[ǵ[7ڹq$!+son_%JY濫նe'=~hD5xev8jo{N7^]y ؛uv8r_?it- ](DDoӡΔpx<"ތ}C}'Rj'@_TO?J: -z>ʳ'Gçbn~~@@o#;Ѽ+ZZcLjwL\+cjr/y-=I0;^ز>yhiOnZG.jweGhlm\.+|>[ۣdoГB{#"H$g{CKk6OHo;0H;:O$8^*&57W_ O$Wf$f4"K$ç"R,g"LD>#}SWbfcfSW.S;?8ЁO8C}I-K]_>'},T⎩_y: \Z H@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@m+"zWx͑;4w P=ѳKF"╬mDΈ /SDIDfxogu`Dz'z{/8}ro+":{Z:#_WXa2ўٙҞ ކe7,]=ݾ?}m|]rOx?iX?9wqڶ AGll8uh[\gMOOϵmY>+/)~ Kd\tu4r\q߂?&?66/")T"w;'cT{\rlΖxbb"bnn.&''mOn\rwG.C;/llK{{ \.$T-0ݫMk޺X֔_*{>tfB?Eӟxquaqh*{3_.>MDXjb!z70 ѻ1Xލ1$> ؙg+>똯q䳎=s?3->F,>Me#>L>#},FJ1_,hĵfrڶ,#k50\Ji|{)Opo3A3ΔvG>3{GGGS>{k_ev8rD?㟴ܷ?T@@@@@@@@@@@@@@@@@@@@D!"pxCD숈Q6oFľY]_ꖆeʳC:2աex<:_Z:?,C;>VJ\2~әO/Touu6fr  /lY[6BJَ'G^~;73޷'GFH61rOWuECKku o|SW2?/[ۗX.MdzKhȯ ,Ry։Q–̣ŷZGqV{8xA/g౳SJfh^[xklmU]+c13y9%w>3cʘ' }ˆ_eCOxO#,.@߉S;~S5=¨d76Tb8c8fIJZZ kƿJt)7swHx鷾;C3n'ayf]ݕXܿ2~wo,\XTOqdK#KG m}kcwqCpU܉c&=:yiG=S.{2횟%׎kec34 ^Yx-xV@B6wtcAرӛ=GO>Yh,˖ qsV~u%+e[}WYnڬO%!unߴn/[[j#~;;`}Yq=;+iTXtXML:com.adobe.xmp Adobe Photoshop CC 2017 (Windows) 2014-01-31T17:15:40-02:00 2018-05-20T20:16:32-03:00 2018-05-20T20:16:32-03:00 image/png 3 * wsRGB adobe:docid:photoshop:d11ea01d-5c7b-11e8-83d5-8b2a77c378fe xmp.did:5b23a10f-64a9-8444-afec-90050295a64f xmp.did:65acacb1-325c-6f4f-b675-de5fc347692c xmp.did:906000d6-0fcb-7d4a-92fd-6321c11bbc13 xmp.did:923e78b0-ad70-bf40-8908-229684da8ad1 xmp.did:d5d7b47a-7cd9-6c4d-b4e6-cf0493e304ec xmp.iid:bd5e8eed-dd30-9648-97be-4f5f7f4beb00 xmp.did:87137f23-3e56-d144-8af1-3b329113d129 xmp.did:87137f23-3e56-d144-8af1-3b329113d129 created xmp.iid:87137f23-3e56-d144-8af1-3b329113d129 2014-01-31T17:15:40-02:00 Adobe Photoshop CC (Windows) saved xmp.iid:70f3d5fe-181b-534c-8457-57afa53f610b 2017-03-15T21:02:41-03:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:bd5e8eed-dd30-9648-97be-4f5f7f4beb00 2018-05-20T20:16:32-03:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 448 256 ʭL cHRMz'dv9~:9IDATxo\U޸r<8 q'h"$,i,"(,2l,a Y@ X "iyiD4082ʨ3@bL[;UWRY ){3uYNn=:u|xaIzFǒ3jpo^ZmߖyҥKZ\\VDbɓ?ouyO:\P9rEK01=g,90o~r[-v5r\{ʅNqnb1B!5&@oVޕٚ[@&~_mdwuu5o:zxkzhéf/Z Nڪ`0EaB)p(vj.ijQLΫXѪ lgD/鱀]k`0={XIuזmn㙔[a@- ov/r+^*h;n!p8+l\&t'ڷ.4>Vo4jN hB]nr w]˽xv]Wx\pX}t_^=ޭp*rw? U@@0=NRP~􁇎xXo>&e|+rwuv=߫1Zۜ@@wa}ot3Nޮ~&ݻkAo}|ۺN.Iѝ+u*r_(;^cB竽\5s΄\gg>G/NZw+?yA&$iԀ9-f[穔[]gԟdt.NЏwd,90cYX042~D3ۥPTcUƿ|V7/}muauO։cjX7N;U ][b Fr Fj2; lE.6=NPRNq9 V       BbDT MKX'{P%':i /OBuX&tMx'X(WtM]QZ:̊J|o;5i3+?4չΝKX5ç)8$/DLjq@queeU|/dZ<?ЎgN9                                       4IxaIg$tNXrVOVjq~[%n}`C%-..nD"qɓ  ?[C[% m՟H$L9ION;z*xBϧ%fͯG3ݥpT㑿{k**fH83F序WRQRQ>Ww?t纎^=m|N|_Nu[?AX(Wt~yKwxM;A8px                   $IC#㝒%VcLJ9IcɁ[>C#u++>M%~,9ݱK8\Vjq~_b4ږZx<w¾)D׶Ʀ%ihduOs{U+ JS߾uq[C#Gƒ3 'f~Su ][jqG3ۥPTZ[9}1{#r!`ABGs% ?h+wAN;U]57 s| ʖ._ s4J 5Xx$ݡ`4X1_̬Kֶp緦R6bfGk4Q JF uκ-`h9{Ps(r[k&82\\]{ A#s|g,fgm pN9                                        aoj)B{IENDB`simutrans-124.5/themes.src/modern/standard-large.dat000066400000000000000000000112511520675460500225050ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # # All image names must start with "> " to have them non zoomable # # This theme has the scalable standard look # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # normal Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will use the unpressed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # normal Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # normal Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # normal Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # normal/pressed/disabled left button Image[0]=> scrollbar.0.0 Image[1]=> scrollbar.0.1 Image[2]=> scrollbar.0.2 # normal/pressed/disabled right button Image[3]=> scrollbar.0.3 Image[4]=> scrollbar.0.4 Image[5]=> scrollbar.0.5 # left/center/right scrollbar background Image[6]=- Image[7]=> scrollbar.1.6 Image[8]=- # left/center/right scrollbar knob Image[9]=> scrollbar.1.0 Image[10]=> scrollbar.1.2 Image[11]=> scrollbar.1.1 # normal/pressed/disabled up button Image[12]=> scrollbar.2.0 Image[13]=> scrollbar.2.1 Image[14]=> scrollbar.2.2 # normal/pressed/disabled down button Image[15]=> scrollbar.2.3 Image[16]=> scrollbar.2.4 Image[17]=> scrollbar.2.5 # up/center/down scrollbar background Image[18]=- Image[19]=> scrollbar.3.6 Image[20]=- # up/center/down scrollbar knob Image[21]=> scrollbar.3.0 Image[22]=> scrollbar.3.2 Image[23]=> scrollbar.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7 # goto pos Image[8]=> gadget.0.8 -- # divider line Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- -- simutrans-124.5/themes.src/modern/standard.dat000066400000000000000000000115051520675460500214170ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # # All image names must start with "> " to have them non zoomable # # This theme has the scalable standard look # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # normal Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will use the unpressed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # normal Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # normal Image[0]=> checkbutton-small.0.0 # pressed Image[1]=> checkbutton-small.0.1 # disabled Image[2]=> checkbutton-small.0.2 -- Obj=menu name=Posbutton # normal Image[0]=> posbutton-small.0.0 # pressed Image[1]=> posbutton-small.0.1 # disabled Image[2]=> posbutton-small.0.2 -- Obj=menu name=Scrollbar # normal/pressed/disabled left button Image[0]=> scrollbar-small.0.0 Image[1]=> scrollbar-small.0.1 Image[2]=> scrollbar-small.0.2 # normal/pressed/disabled right button Image[3]=> scrollbar-small.0.3 Image[4]=> scrollbar-small.0.4 Image[5]=> scrollbar-small.0.5 # left/center/right scrollbar background Image[6]=- Image[7]=> scrollbar-small.1.6 Image[8]=- # left/center/right scrollbar knob Image[9]=> scrollbar-small.1.0 Image[10]=> scrollbar-small.1.2 Image[11]=> scrollbar-small.1.1 # normal/pressed/disabled up button Image[12]=> scrollbar-small.2.0 Image[13]=> scrollbar-small.2.1 Image[14]=> scrollbar-small.2.2 # normal/pressed/disabled down button Image[15]=> scrollbar-small.2.3 Image[16]=> scrollbar-small.2.4 Image[17]=> scrollbar-small.2.5 # up/center/down scrollbar background Image[18]=- Image[19]=> scrollbar-small.3.6 Image[20]=- # up/center/down scrollbar knob Image[21]=> scrollbar-small.3.0 Image[22]=> scrollbar-small.3.2 Image[23]=> scrollbar-small.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7 # goto pos Image[8]=> gadget.0.8 -- # divider line Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- -- simutrans-124.5/themes.src/modern/theme-large.tab000066400000000000000000000123551520675460500220130ustar00rootroot00000000000000# Simutrans default touch theme # # Read the wiki article for better explanation and full parameters list # https://wiki.simutrans.com/en_themeDef # # name of the theme (will be shown with file selector) name=Modern (large size) # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 # windows drop a little shadow (default off) # gui_drop_shadows = 1 #################################window stuff################################# # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like Windows) # window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window # window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) front_window_bar_color = 1 bottom_window_bar_color = 3 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #C0C0C0 #################################colour stuff################################# default_window_title_color = #D74700 # gui_color_text = #000000 gui_color_text_highlight = #005C9E gui_color_text_shadow = #BDD3DF gui_color_text_title = #338FCC gui_color_text_strong = #EC6E0C ########Test######## gui_color_empty = #868c86 gui_color_obsolete = #2878ff gui_color_edit_text = #000000 gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #666666 gui_color_edit_background_selected = #72B2DD gui_color_edit_beam = #000000 gui_color_chart_background = #FAFAFA gui_color_chart_lines_zero = #005C9E gui_color_chart_lines_odd = #338FCC gui_color_chart_lines_even = #338FCC gui_color_list_text_selected_focus = #FAFAFA gui_color_list_text_selected_nofocus = #FAFAFA gui_color_list_background_selected_focus = #72B2DD gui_color_list_background_selected_nofocus = #72B2DD gui_color_button_text = #FAFAFA gui_color_button_text_disabled = #666666 gui_color_button_text_selected = #FAFAFA gui_color_colored_button_text = #FAFAFA gui_color_colored_button_text_selected = #FAFAFA # gui_color_checkbox_text = #000000 gui_color_checkbox_text_disabled = #666666 gui_color_ticker_background = #D6ECF7 gui_color_ticker_divider = #6095B8 # gui_color_statusbar_text = #000000 gui_color_statusbar_background = #72B2DD gui_color_statusbar_divider = #6095B8 gui_highlight_color = #8FC5E6 gui_shadow_color = #8FC5E6 # gui_color_text_minus = #C00000 # gui_color_text_plus = #000000 gui_color_text_unused = #E46A0B # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #ff0000 # Colo loadbar, save game, load game, server... gui_color_loadingbar_progress = #76b9e6 gui_color_loadingbar_inner = #D6ECF7 # Color player brigth gui_player_color_dark = 1 gui_player_color_bright = 3 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #D6ECF7 ##################################size stuff################################## # force main menu icon size icon_scaling = 150 # titlebar height gui_titlebar_height = 22 # width of the window elements gui_gadget_width = 22 # safe margin around windows # gui_frame_left = 10 # gui_frame_top = 10 # gui_frame_right = 10 # gui_frame_bottom = 10 # horizontal and vertical margins between objects # gui_hspace = 4 # gui_vspace = 4 # tab minimum sizes gui_tab_header_vsize = 22 # input boxes height # minimum height is the height of the position button graphic # gui_edit_height = 22 # checkbox height, default sizes come from the checkbox graphic # gui_checkbox_width = 10 # gui_checkbox_height = 10 # sum of the top and bottom margins for divider lines # default is twice the size of gui_vspace # gui_divider_vsize = 8 # buttons sizes gui_button_height = 22 # for buttons with fixed widths gui_button_width = 100 # text margins inside the buttons (left, top, right) gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 ###################################pakstuff################################### # pak with the images for this theme themeimages=modern-large.pak simutrans-124.5/themes.src/modern/themes.tab000066400000000000000000000123171520675460500211040ustar00rootroot00000000000000# Simutrans default touch theme # # Read the wiki article for better explanation and full parameters list # https://wiki.simutrans.com/en_themeDef # # name of the theme (will be shown with file selector) name=Modern # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 4 # tooltip text color (240=black, 215=white) tooltip_text_color = 240 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 # windows drop a little shadow (default off) # gui_drop_shadows = 1 #################################window stuff################################# # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like Windows) # window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 0 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window # window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) front_window_bar_color = 1 bottom_window_bar_color = 3 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = #FFFFFF bottom_window_text_color = #C0C0C0 #################################colour stuff################################# default_window_title_color = #D74700 # gui_color_text = #000000 gui_color_text_highlight = #005C9E gui_color_text_shadow = #BDD3DF gui_color_text_title = #338FCC gui_color_text_strong = #E46A0B ########Test######## gui_color_empty = #868c86 gui_color_obsolete = #2878ff gui_color_edit_text = #000000 gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #666666 gui_color_edit_background_selected = #72B2DD gui_color_edit_beam = #000000 gui_color_chart_background = #FAFAFA gui_color_chart_lines_zero = #005C9E gui_color_chart_lines_odd = #338FCC gui_color_chart_lines_even = #338FCC gui_color_list_text_selected_focus = #FAFAFA gui_color_list_text_selected_nofocus = #FAFAFA gui_color_list_background_selected_focus = #72B2DD gui_color_list_background_selected_nofocus = #72B2DD gui_color_button_text = #FAFAFA gui_color_button_text_disabled = #666666 gui_color_button_text_selected = #FAFAFA gui_color_colored_button_text = #FAFAFA gui_color_colored_button_text_selected = #FAFAFA # gui_color_checkbox_text = #000000 gui_color_checkbox_text_disabled = #666666 gui_color_ticker_background = #D6ECF7 gui_color_ticker_divider = #6095B8 # gui_color_statusbar_text = #000000 gui_color_statusbar_background = #72B2DD gui_color_statusbar_divider = #6095B8 gui_highlight_color = #8FC5E6 gui_shadow_color = #8FC5E6 # gui_color_text_minus = #C00000 # gui_color_text_plus = #000000 gui_color_text_unused = #E46A0B # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #ff0000 # Colo loadbar, save game, load game, server... gui_color_loadingbar_progress = #76b9e6 gui_color_loadingbar_inner = #D6ECF7 # Color player brigth gui_player_color_dark = 1 gui_player_color_bright = 3 # make the chat window transparent in network games gui_chat_window_network_transparency = 25 # if defined, use plain color for chat window in network games gui_color_chat_window_network_transparency = #D6ECF7 ##################################size stuff################################## # force main menu icon size icon_scaling = 100 # titlebar height gui_titlebar_height = 22 # safe margin around windows # gui_frame_left = 10 # gui_frame_top = 10 # gui_frame_right = 10 # gui_frame_bottom = 10 # horizontal and vertical margins between objects # gui_hspace = 4 # gui_vspace = 4 # tab minimum sizes gui_tab_header_vsize = 18 # input boxes height # minimum height is the height of the position button graphic # gui_edit_height = 22 # checkbox height, default sizes come from the checkbox graphic # gui_checkbox_width = 10 # gui_checkbox_height = 10 # sum of the top and bottom margins for divider lines # default is twice the size of gui_vspace # gui_divider_vsize = 8 # buttons sizes gui_button_height = 16 # for buttons with fixed widths gui_button_width = 100 # text margins inside the buttons (left, top, right) gui_button_text_offset = 5,0,5 gui_color_button_text_offset = 5,0,5 # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 ###################################pakstuff################################### # pak with the images for this theme themeimages=modern.pak simutrans-124.5/themes.src/pak192comic/000077500000000000000000000000001520675460500176615ustar00rootroot00000000000000simutrans-124.5/themes.src/pak192comic/pak192comic.tab000066400000000000000000000115661520675460500224040ustar00rootroot00000000000000name=Pak192.Comic ##################################pakstuff################################## # pak with the images for this theme # These are under CC-BY-SA 3.0! themeimages=menu.pak192comic.pak ####sizes#### gui_indicator_size = 20,6 gui_waitingbar_width = 5 ####general#### font-size = 12 # Main text color, used everywhere. gui_color_text = #403022 # Color of text in tabs, finance window headlines, ware list bonus text, minimap zoom indicator, fps info in video options. gui_color_text_highlight = #e8d8ca #Color of the title text in the welcome screen and in help. gui_color_text_title = #FFFFFF #Color of strong/bold text in help. gui_color_text_strong = #e8d8ca #Background color of selected elements in lists. gui_color_list_background_selected_focus = #7b5942 gui_color_list_background_selected_f = #7b5942 #Text color of selected elements in lists. gui_color_list_text_selected_focus = #bd9263 #Background color of the elements in lists. gui_color_list_background_selected_nofocus = #7b5942 gui_color_list_background_selected_nf = #7b5942 #Text color of the elements in lists. gui_color_list_text_selected_nofocus = #bd9263 ####charts#### #Background color of charts. gui_color_chart_background = #cfb094 #Color for the horizontal zero line. gui_color_chart_lines_zero = #e8d8ca #Color of the odd vertical lines. gui_color_chart_lines_odd = #e8d8ca #Color of the even vertical lines. gui_color_chart_lines_even = #FFFFFF ####edit areas#### #Text color in edit areas. gui_color_edit_text = #e8d8ca #Text color in edit areas when the text is selected. gui_color_edit_text_selected = #bd9263 #Text color in disabled edit areas. gui_color_edit_text_disabled = #cfb094 #Background of the selected text in edit areas. gui_color_edit_background_selected = #7b5942 #Color of the editing beam/cursor. gui_color_edit_beam = #e8d8ca ####buttons#### #Text color for buttons. gui_color_button_text = #e8d8ca #Text color for disabled buttons. gui_color_button_text_disabled = #bd9263 #Text color for selected buttons. gui_color_button_text_selected = #e8d8ca #Text color for colored buttons, like in the finance window or charts. gui_color_colored_button_text = #FFFFFF #Text color for selected colored buttons. gui_color_colored_button_text_selected = #FFFFFF #Button Height gui_button_height = 18 ####checkboxes#### #Text color for checkboxes. gui_color_checkbox_text = #403022 #Text color for disabled checkboxes. gui_color_checkbox_text_disabled = #7b5942 ####tooltips###### # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = #bd9263 # tooltip text color (240=black, 215=white) tooltip_text_color = #e8d8ca # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 ####bottom#### #Background color for the ticker (bar shown above the statusbar with info)#B3B3B3 gui_color_ticker_background = #bd9263 #Color for the top divider of the ticker.#000000 gui_color_ticker_divider #cfb094 #Text color of the statusbar.#000000 gui_color_statusbar_text =#e8d8ca #Background color for the statusbar.#9B9B9B gui_color_statusbar_background = #bd9263 #Color for the top divider in the Statusbar.#DFDFDF gui_color_statusbar_divider = #cfb094 ####random#### # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #294ab9 # windows drop a little shadow (default off) # gui_drop_shadows = 1 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 12 # show the windows close etc. buttons on the right (like windows 98) window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 1 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # first: draw a frame with titlebar color around active window window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) front_window_bar_color = 0 bottom_window_bar_color = 0 # third (best together with 2nd):use different text color for bar front_window_text_color = #efd3b5 bottom_window_text_color = #bd9a84 #Title color (Newer) default_window_title_color = #422810 bottom_window_darkness = 30 gui_chat_window_network_transparency = 0 ##################################toolbar################################## # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 # icon size icon_scaling = 100 simutrans-124.5/themes.src/pak192comic/pak192comic/000077500000000000000000000000001520675460500217035ustar00rootroot00000000000000simutrans-124.5/themes.src/pak192comic/pak192comic/back.png000066400000000000000000000063251520675460500233170ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe< wIDATx?r{f˾u59rĩ"NJˮbꢫU IG Λ~u`Qbo7?~B7/6O߇իW/Mo߿o~Ч @ @!@!B!B!BB  @ @ @!B!B!B!BB  @ :_~?nM }d_ٴg5mďi-xnm0S8S,r(6g=Ryʣ&z3i'=1'lhpBk&+ѐ YQ%;i ɑ艏д) PbFMe|]CkC/V?s|Ze>D,FJgc=m7ѩMwȃ]HVir&s~X/.:&,xTURn]l ꃛNWxDP`_wi%>7?3ްmN;{vиmwl¢3(pZ7Rk ό`',%g2焫 ]|+rwsi4HrgeX6 .K ;vtEi|Z˧<:Tk3cRU;wCUXGǧgN O[b@i)-dN\PRcb#(&LybX.lvhi9ͧ_z{'EbBd ǧHfF O<ºIULŔfe9nC͛Z~BY!h}}߹]PeQwǣMe?:CPB`WV!lCΟ+[fe7\ن&ueeY0_jK\h\;JU!˜+Ӟ]Z^WTTCo1fj=vеCCrW:ygo#'V+3a!Oㅊ˺s}99Q#), q\LK&6D_2 WE.(G)7S?~x/KT[z/q+4e~ܱ꾺>>14S-_K~h,_hMi,svInngU:x @-רGsk/͊vXEMA%Δ,=;IG]#^.ۃ兇iYfQM;@~].ۼ9#FZ 霟^ީ٤LwvgeE9aBWebsIђtm՜t[kqag_rȎ~KI|h}Am6!,,IZxmM?1ðPvs$>.끷۫ƜXs!x#?28u;*ncwY{}uĞʱܦTs< omҸ~°ܠO$f9#jY+RM1i "/"ύ].2gnEiij%=9:ϕ@WgY3]ܷiK:8(^(%y߶U@jcG$|]QaXΞhQ}Y̍`p,R l^PrɉXtܱGlǨ5MUϲ :2QlMuR]ZM݅(s1xK_]i ,®$fqO=vγ􆋓2/׆q\6ˬ{LCR3\92mF;0B8sAEE <:in퉰Y ;0ո;G,"6s34ʋs?~AF,Π`aWÞZZ]!Q</WOgHta_b9yyrgbkĠLd0Mn"B>/ۚ9bgh:\UWvm64~?<38^~¶W/ Ë@;zj>ImGl?5&2j|RPG[t3h&`YLH3t[r)F9`8K NV=xy1ꍹme EZZdv> 9Wywj[9߶z ].G[h} ݥ!2c:*=q *vͳ:"`(YuE;5zVunI1J|su,y'ܴbƝMEECYq\]Lhj-eŤxL/BT€КmYv_hp]^67P7VuC:?֯ @+KQN_ @ @ @!@!B!B!BB  @ @!@!B!B!B!BB @zN_%O=IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/button.png000066400000000000000000000453051520675460500237330ustar00rootroot00000000000000PNG  IHDR@@TrëtEXtSoftwareAdobe ImageReadyqe<&iTXtXML:com.adobe.xmp [3G5IDATxy]}w^5 6g4V-q4#C3 Fف <0 8'`X[,ZH6V$*X k_XdT{5{{~ιrT*]/)}k_O ݝBl(}qi2_?޽'7CUU)<yh+ձBO{l a|?D!=vy.,ո]}J 7 _ҩ-[۷CCCCKٳG}u68O*O,R'd@11.,svv.-+uugaǎ1_u({cR9577bcrrS](g0/ m~߾>\.wûqLyt+ځ/[i81644Hwm۶m ]]d%KLLMMǓrs!!ԇqN|!V 1ƲCx3rٙ;sl\DﺻSoA9 ;::,2؎pAػwz߹x)ϽB^?F~*ߙ8)SoHQ*U%$C5;lB9JخY?"hmvK;~C!F03N+Z[g~cu&ΉfCE[QO%g(@9L}מ}[7n Uk74h5P ^˞S7FdPUS;}S΍[{|c|J'XP[F@xQ dzmhn 훶(U## #b=ͱ[mյ>u>ק7JmIظy/RHH۫B# ɾp';gϞCCC3eӮ]tdeǎ)VG#~!G qLSWRӧO'i#7x#>uШ:8?`Y6Js L)BFLgWbK}Ҳ7:*M~o4_ M}v>̬Q#±zީtɥ*sp3ty^QkL\VTȚ@To_´#ܽy-5j44L4x\Egr|(՝̻QVw`E{_8RF?$?ăcҕ$ۋ dY1\aA*޽dQ:sL~z8yd'D_%HY JYu`߳sAL"8 qE ݸq#- ab:]fɳc6rqHCCBODÃqU;9!m&kE.[.^adWH1AΝK@LP Yi"ՔUB A9,Gy>3eȪ 2,ԩS.CX_~7dwS4%!~5ycLؖb^]  irCkOt -Hf ` cbڰdJr9!k䰿n޼![fϔC0Q7Cy#D̃3fi,ظ.Ah( ١ b{$lܯmYiG8hjjS#AF#7 nEu5""HdX.!˨39BؾŶ$Rи B(rwC&r "x!BdPҝ)irV "ʺGYUy !L%#N},e/_۫Rb[d=$i9up?Kƅ U= "CcKJEdSȄG% b+c|n҃ N1CF Ivw?n*$tQQE gdo 3ta!CzrJGf!SE9y,w3 ;w߱^"fD;u]m]JKƐ&?x FmdP<@ٳ)kC 5H b H}E܆,WwoaAP(Cô*~| ;E݈E9YBfQ/o"<"fDց%?ϧ+lL~2JA>#HBf `zww0 Ӗ.ڐ2Dwn2Oy4\Q7SwSeKbBpSV~トaĊOe+%E Wt/? "O!3^ǥW~sb J~"H1=d( GW+9?MFⒻŐR?KYBYʃҩ,&͢<ˤ\ïb[DY W"teӊ ϦqK,$:x;0ߝЅ !E3Jd HS; a3/䈘#S=vxdq{=D1˷NHސrM*)EKxw s3ӡ>6XRAcD"7J )KL]IdI 21dh`B*'[t0~Fw!Qd{1a3-ÑH1 ib 9S6Če VnJ{ĉH6l6m ٳg.lܸ1LOOk׮ưs4> 'N;wu3eݨU?/^xiLۻwo*[UUFFFR=o[[[pmS[8|@uamaxh8ۿ/']*ȹ.߳)\M73?/ĿKE֙):6_?|?//#Ja}a(B?m)8Fc Áccca--c>VkKkglY T#'a箝aC/b6oubO㣎1LOM8!98t:_.ƸNܫcu]5ˁœ Q1mmq'BGhRytGnnPŞ59ɉW~빹=5MkRsLCÆ|/C[nI'Z>x2kA555uǎ1ٹ_7o^NK'&7CuuMB:ֶt⢱7,S ۰2NoNe8)6/ŗg|0|aִ= KΥF Gb/*<Oܘ`!N=b;DF3ɓ'P'X|LCBI81?|0-q:tP]>q uvvFyˉﺻSoA9 ;::,23X.u/'|$bMա3'P_+ScOwOQ/;btv+16xf,wׯXD_.̇ᧃ!gR\Ltmjċ'Ddl|,4D9ٺ=\P> !fR,W]xq$?QcnZãGKӇîxu3#QvpQ&q܋خYs|dPldcʢU(̆P[Zj@s0̧-N;;ǥ޽{I9 N9KͶ":b2==@\mۺQv6nܔN,HbA#H/A2۸=7<4.,z}NHԵ83?Y__QAxOyu55Ihԛɞyi9 8q`w{Gī8HP&6!7EukB-^ " KWU)BٳgO: ϔ vJӑNЙS2F[ >Z Q >}:sƈ7R:U:fff^rXW~G !^ǏOK)Uqw;;?u~o K)_%A;z\ظe[w OJELHOD;~a7+黜ZOQ杓4'ΜWz*]5}E}H+Ə?8o߾$dRʙ %˂̐DaA*޽dQ:sL~z8ydd.7.! H,s!8 Anܸ01.ߙ ֕zضn< ,e4vlԸ !up~ 'Hd>-AO*X.^2dWRcΝ;l ș< DVի)Ad<{))Cw I"rO:,Wb}'xdw(g, oi"CF落6.0x?}`[ >Y~X#YCoh+@X 0&L y J$gZ6HqsT"6#Lh|R2D&~!Ky4eYօ1= D١ b{$*Q$^W18TUU _OoHWE:'Q[WJ:F 21ddC [Bք  7%ʤLtAaZ!'B~>dzq<&2vԉ.42xd)>t!NǺT+$lel@fScDCmI~h(h 2N7P Ddg=EFȢ) A6(D!#C@|Ko^H uSY";C,"2 E~ 2B,Ċߒu>Cޗ/_~텨UBlYyor…jyBV.9nr~{2(dBrV `L`rAbn.$L s7Byrƈ: 9! >dFc=肻rJGf!SE9y,w3 ;w߱^"fDցƋƉFFƐ&?pىں=H啻b o{gϾzNR08`_"dըbR#NY{l_zVk*ødib2Ž`Qw҃,!dnԛ2DcxOdi?rECхFY~'bBD=|wc]b;b1X'.? u1$4v4 rJ+{ii| )l/ "@ذxN+ h (=B]OH#C2CɃU⌺ذԐB\rz)K#UY"2Ky`:Ӎd16lY&X/QDցgp<Еq48Em{$:@h;+?ˇϑ 1A8CId Hu!2 oLG?525H3n;zGցl#iH|;;1O~Z~5]sQD>b,Ɓ?#4FE&{Suvq~{)҃dbx@JȪ,0_u,C=w(KCL#By 4b Zԋ FB,]m#`:BXESD]PQ-S"""RqHDDDDQDDDD HDDDDQDDDD HDDDDQDDDD HDDDDQDDDDQDDDD HDDDDQDDDD HDDDDQDDDD HDDDDQDDDD QDDDD HDDDDQDDDD HDDDDQDDDD HDDDDQDDDD HDDD HDDDD)5MT_MҷP* -b6|Tr|i2_?޽'}9jjjBooohii ===)lݺ5χ8hooO7l6mFGGógC]]]ظqc ׮] aΝ˗iz}}}'NwIg˺Qq}qxūuX\\Lg޽{S٪022kkk[p?wKhjnJ;q>tttִRQ#;˵);FR;.%~2q<;;Ž;G%1'NJ7|3 yxA"5333 q`: ߝ&1L\=z4t#fgٵkWTe"b;:t([@|~N חN;K9'yAh$XgmelUœS9XGѹ й#l;?[<ػҚxIb`ZO$-|]]mXū_!Ʃcnn6]4nhz!JP{lR<77a1MNqN u3˭uΥNLLWP2+cf9wB\y/dyaɘ2l q޽'ك᧟tX 255U`vr,탁Gix??=L||i ?髀y64?Js=]Su_YP\x;\K _rpt[H՗y=}'Mybr,+@<jB԰qP cf:W W?E*sw8\<ٔU>Ol755D =N Hٳ'I1gSӑfX!>!cD!_B(C}ONҒ3Fod:h,8uf9 +q9UtL-7*+c.6_imTsgZ|00=#3kԈp5疧+O.- ap3ty~M[?VKvHȚ@To_=8  b[.q#K_F[^"pLv?No@<Ȳ;v,]I M!fҹ< ‚Tܽ{7t̙pp=]Q?]n\l "=d{!g,sss)&օ෻qFZ_z]n< |:m+wT:4$44L4SD" qG7<8c֘2!˜ !;tsD],|ձ-K1(@"wHРHqMCr[O4dh8HXiR0 ;5ad)A@<8YBbSLcgD exR,ˢedl\@M E\CtQ]g;rT}mHqˁ)7P4x\Pdh$ѰrEAf٠9@ $ b}(4!EC^iă "g$+/?ą)C 7Ad#^YMK*"#$Ywc9/}W^" xxN C=… 骞l I%{)SiM!3.czȮ Ave-Qz !fȨ!IMPL!P` qFa2B #uʕ$8BCRb^ wW%Ŷ +kҠ1)24444G)1@Y8Gz>P>{l#3$|E>آnCGPbSA ]`-u#^d %;DtnԋTRlYW WϟOW\5dbrVViɃ!D*|G2C\ %H˷ATpц!"=uyʃ䊺M.4Ŵ\>s;.ti"+֟2:VJlY@D^D"Cf=Y`JQ cost_YlDAzBczJ 4Պ䧝ԐB\rRJz)K#UY"2KyP:s>$YgwURl(@"4y+8Vdx6 v[i !HIt+Y &?ˇϑ 1A8Cg ?J)ߝ0r & { 8tgQ/:-z$ b>ogg a!?嚮9URl(@"pn8z\O#4FE&=of!9s"A@ * 21 A< %dUx*u 8L=w(m^!,@#V EH b@!+<6֑e0!F"pa)"y߶b[d=0DDD0$""" $"""(@"""" $"""(@"""" $"""(@"""" $"""$"""(@"""" $"""(@"""" $"""(@"""" $"""(@"""(@"""" $"""(@"""" $"""(@"""" $"""(@"""" (@"""" $"""RjRt)ro}ˡT~jZBl; nwyrZeraؽ{Ox$<zXh_?rgXhhlC' 1kviem#VB}}]=XNln߾q;oذ!lڴ)gϞq0==<]Ν;˗/0<<N8ܹօ,u>2ŋW밸ϴ{UUUadd$ֶ:?rܔw~|i[kGvkkkSևdY6;>Lq̺ǏVvNJ?'}a(8 = އ?{adh0t"[ ?2ybdG`ptW.- p$;tضm['-i{퉉D9'Q㼋/S#q?i֭A@ܹ# ~{{[:`&':pOB<*9pB8ˌO=o޼9 :Y$ɓ'XHtCbl׈ҪF"j$E8~|Djfffɑ*t2;'Mb8zh:G̳k׮wc/ D('w?tP[n%b,^]] ~'⹯/9vr4 ROԃ,Hά*=K'rl/SssGvw{(y8ws58y*I"Z[[yTJeSLsu]]}MWq$/qCSlhMа!B6 xnnnb^SS✸Ng[[[K띘L֡>meVvrͭ^Z’1e@޽{ Nh M-᭳O?~: djjpX}~~z"Wlh Xuy!<~ԕ {!8{@'ݿdw>) A"KA;з/5*{z1IOZ^*X.W$x6Ԅ(p]alM zt6(~TNpx)-(|njj +BA\{;b سgO bϔ! #H B|2CƈC686ؿԅ(QN>%g7x#>uXp|!2 ?rXW+r,dZnT8W_]mK}Ҳ7:۔'~1``S{Ff֨X=k-OWN\z[<gf |nJ1]5R߾zvq5&<臷>s\G6ۗٿz7܍:DE}Hߞxe?vXd{D 2C̤sy{nD̙3ɓ{~ܸ@,yEz!CYRL"8 qqowƍ,.._/}+=pT&5thHh$hhx j] E%@&)N.lŋS6 )<& 2=3/dyիWSV 1"Q$g2d'IBvKV]S"Od-yePH!>cVv}_'SYxxƀF<`"C b[i ,cfC&WD2%9ӂAr_7oL-b`3DЍg!UY!Cdݨᩤ1$4.#CS:ϟ?j.24< 3BT +.d@Jq=|o{' 0m )CDl!/|{&M#)u 1]h%.i|wc]ʃD #V?eu1$\ӽ+W44>E{{@Ɔߜ仿 #,t<#8" Ah棫 O;g9t!;n1Rn1G1DdKHcfQeR.WI-wĆ+wP䁮`Z44ypl %y1Cg1,n?G..,)BV"D(|wBeCh hpي )҃/ 2񀔐Un|20Q"zpDzPX!6")FXGt, -2OmJm (@"""" $"""(@"""" $"""(@"""" $"""(@"""" $""" $"""(@"""" $"""(@"""" $"""(@"""" $"""$"""(@"""" $"""(@"""" $"""(@"""" $"""(@"""(@"""" HAoJҥkʕ/REmh B쀸Kkʅa=AϓdYx?b٢mŲ2_m)O\yaa!|'wJ}6Ɔ0>~pcFlVj>b%ׅ݃Ty˖-ۡ!ÃӧOÑ#GBMMM ---'455[01 ¦MhxY8p@~ 7n ڵk1ܹ3|2MĉΝ;i]rY7knn~UǏË/^bZ>ݛVUUT_[[[p?wKhjnJ;q>tttִRQ#;˵);FR;.%~2q<;;Ž;G%1'NJ7|3 yxcWWWc߉xK'x rT< K43W>UœS9XGѹ й#l;?[<ػҚxIb`ZO$-|]]mXū_!Ʃcnn6]4nhz!JP{lR<77a1MNqN u3˭uΥNLLWP2+cf9wB\y/dyaɘ2l q޽'ك᧟tX 255U`vr,탁Gix??=L||i ?髀y64?Js=]Su_YP\x;\K _rpt[H՗y=}'Mybr,+@<jB԰qP c^K~g]%wzœMiYEvSScXH ~ ɾ#{ID2"t. d>⊌qlpl Q >}:IKoF}꠱Bd~֙.l9WWY1ܨp\s%rW_ٟߦOm?77$ a] ~7ne!8宇up!@?J=lo *)ιpǷzjRА:|2%@&)N˶;\x1eȮc0m!Ӄp 9B!An^z5e#9HAX]_|=.)Cv$dad55!Z/DFW֙eB1MdH=!7+?wAlоp>^h`JaA$3Ø21ҝ)ə䄬 yfl&n4>SD" !KĘ5LHc0'w"H\Qˤ<#aԇ#6#|;*Rv"Y e2@4 $4!gdL慱3YE+YAJ#NF#8dاӘDB(G#+9 Kil(eAPyB`$D,eE!Ulu}Խr,'*WD*VHP᢫HaJA7r8d"3H@TG ᡋHRD9l )"DHV^ uSY";o F.QlUDF!oTr_˯9]YIsZht1pUO ϻ' 4d3e* )dBr`Ld1>_6J8!? 5$ʓi>: 9! >Af3WPd\Ah]!@̋`1ٹp§b[ H%W\ӥAcHCRdhiP>{l#3$|E}E܆8g %˧ BgW[,FX/!=KvzfnԋTRlYW WϟOW\5dbrVViA""g@V] q Lz.NQaERB^M)FR+7Ab?(K\RsǺХvd|1XaֱRb[ :"rEO\ 2 eCV ~sb J~EF TӃPRA>V$$?MFⒻŐR?KYBYw,&͢<ˤ\ïb[DY W.]"óihJ AJc^b 0Y> C..,)BV"D(|wBeCh hpي )҃/ 2񀔐Un|20Q"z>ȲPX!6")FXGt, -2OmJm (@"""" $"""(@"""" $"""(@"""" $"""(@"""" $""" $"""(@"""" $"""(@"""" $"""(@"""" $"""$"""(@"""" $"""(@"""" $"""(@"""" $"""(@"""(@"""" HA.$vIENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/button_.png000066400000000000000000000031511520675460500240630ustar00rootroot00000000000000PNG  IHDR@H^tEXtSoftwareAdobe ImageReadyqe< IDATxڱKw{pHk@ qpn\tNK\ t(R IM>WDY!9o|~ըF⵼%7O`|v|kc"*S/_w߾K7~Ϥ4k(Y!ibiuY__q^y|ܽXz{|҆/fV\q覭Wǯ z' >OаO0'_a'^u/{[[07濃G. HO` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `0fѪxu;S,ο|In󭍯nfU^<+AY?kkks>'X? X족6,/'n;pW^q;`c^l'mX.`O:ܟ~>J0EңH5nN6c<z07M_!F('XS_t߾{?d 3_! `P쏐x@@@000    @@@0000   @@@000    @@@0000   4ˏVU먮@oΟNMnx}Ó̟J'J~>^Z? Xe %=!$mX3_,n dLрz&/ N,`j-,iWgq>iʳ ̦fʍ7^I?ztHW#We+>gW{vMd--fr߁B@T! ` ` ` ` ` ` ` ` ` ` ` ` ` `i[;ߜ:]?1cy44]M֯aˁV֯^Uv_aLv~[?@Wx@@@0000   @@@@000   @@@000    @@@0000   `-1ќX;IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/checkbutton.png000066400000000000000000000361251520675460500247310ustar00rootroot00000000000000PNG  IHDR@Llx pHYs  8%iTXtXML:com.adobe.xmp Adobe Photoshop CC 2017 (Windows) 2017-05-24T17:52:38+02:00 2017-12-10T15:33:25+01:00 2017-12-10T15:33:25+01:00 image/png 3 xmp.iid:2fb8790c-d83a-344e-965d-f6e79d1bce92 xmp.did:2fb8790c-d83a-344e-965d-f6e79d1bce92 xmp.did:2fb8790c-d83a-344e-965d-f6e79d1bce92 created xmp.iid:2fb8790c-d83a-344e-965d-f6e79d1bce92 2017-05-24T17:52:38+02:00 Adobe Photoshop CC 2017 (Windows) 1 720000/10000 720000/10000 2 65535 192 64 V` cHRMz%u0`:o_FIDATxٿoeݽ8v8J$"҅! B H0!QfHQ(Ha "0$ꋝ9 Dbb??9_/gEQeY#"o,ˊ> -0 fLMV N,+K,+}%+"H2 |aMף\.3[7Awd27[m0kߣ8&~|u໑_1OZ B!ssst:ύ]yy]1L:ͽY_[eey6{uGbc P(bYsor9f̥8<]q(y^/|#Vss*XYg}m/~S4'T*r'۱oz.mضM64'?? *~q-{$gX\X=c6obE<&JQww01 |?$R'LVjy|mh6]+l$Ggd@d oK~+"DFW @D( DQ" @D( DQ" @D( DQ" @D( DQ" @D( DQ" @D( DQ" @D(qFktX׎|IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/checkbutton_.png000066400000000000000000000011261520675460500250610ustar00rootroot00000000000000PNG  IHDR@tEXtSoftwareAdobe ImageReadyqe<IDATx?Kahh)phpŦ{-MB Ph ¡I^@ٝC@p˯{=un[v~}gVaʻ7T8>Zqh5ou@)ZzZ5j痋y~:g>}F-1GIrSE@@N./:GJdtPW@DƤOe]l ryUt[,tlLhUAi({U{`!fa>n?ϬD**}]65͟^pg a4.,;8h j6| 0TgZ6pi@ZMwh5b]GcT+צ=?d]MYi?ۀ @@ @@ @@ @@  @@@@ `=.0#IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/divider.png000066400000000000000000000003101520675460500240310ustar00rootroot00000000000000PNG  IHDR@@% tEXtSoftwareAdobe ImageReadyqe<jIDATx101ADw*`BFk籗d7:P|sFN@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%G TQRIENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/editfield.png000066400000000000000000000011171520675460500243420ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe<IDATx/nPkR,M:_Q9Bϐ+`v  bY1c%O׺.SJ?kiH]t5UwC~;_A?.z ! @@! B@B@ ! B@B@! @@! B@B@ D7=)kIENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/gadget.png000066400000000000000000000371421520675460500236530ustar00rootroot00000000000000PNG  IHDR@@}g_H pHYs  8%iTXtXML:com.adobe.xmp Adobe Photoshop CC 2017 (Windows) 2017-05-24T17:52:38+02:00 2017-12-10T15:28:22+01:00 2017-12-10T15:28:22+01:00 image/png 3 xmp.iid:3f6784cf-468b-8049-95ba-24e52cea075b xmp.did:3f6784cf-468b-8049-95ba-24e52cea075b xmp.did:3f6784cf-468b-8049-95ba-24e52cea075b created xmp.iid:3f6784cf-468b-8049-95ba-24e52cea075b 2017-05-24T17:52:38+02:00 Adobe Photoshop CC 2017 (Windows) 1 720000/10000 720000/10000 2 65535 576 64 9F cHRMz%u0`:o_FIDATx]HWB1f )H Rr5iG& +^Pn/VF;`tֵ!l%:։EBk3olâ"zaqvJ9''փUuo0DM>~*J<Ϸÿ ;g`n)+<Ѕ۶w999bnX[y}ƌuJtڶ օB!J9s q !7gځlUUU[֏|)w}/366vžwߌ~xx ې⃸Ě[ (ɞg.-+ gٶ~+7dd&N)Sv[>3Q p٣'.¿JPk-tS2esBvx m۔|M¶mK)ZAoY3\.>ZהXtT{{w緬>|aO/F/O?:PJ)ǣZYYd2&SPhvvV2 ^=-+2oXOdտ8BqY.{ 3^/nmێt'6>}q)|\]]p0܃ݻwK/pxyyYhz----,,dYYX:.Sqd콊i7977gYUDz!ר3'WXȑ#)ou '6S;v455 |>/a[XXPʼmnsX81d53Q4ag#Yn>&ߗxr*wYq({g?sۗkr|[4߲ %׆;H=gݒ{ #s5t˷L&l"R.s6"`+`+x*```   @@@@000 ````   @@@0000  saVu|)IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/listbox.png000066400000000000000000000011151520675460500240730ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe<IDATx!n`ៅCjLb*zwfz@M111݂8ȗq8ME?>! B@B@ ! @@! B@B@ ! B@B@H@! B@= *IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/posbutton.png000066400000000000000000000366471520675460500244660ustar00rootroot00000000000000PNG  IHDR@ pHYs  9iTXtXML:com.adobe.xmp Adobe Photoshop CC 2017 (Windows) 2017-05-24T17:52:38+02:00 2017-12-10T15:45:36+01:00 2017-12-10T15:45:36+01:00 image/png 3 xmp.iid:906552fc-e8e5-324e-a96e-e8656e67fa16 xmp.did:4b64aae8-28c4-4543-845c-fb6a8401d677 xmp.did:4b64aae8-28c4-4543-845c-fb6a8401d677 created xmp.iid:4b64aae8-28c4-4543-845c-fb6a8401d677 2017-05-24T17:52:38+02:00 Adobe Photoshop CC 2017 (Windows) saved xmp.iid:906552fc-e8e5-324e-a96e-e8656e67fa16 2017-12-10T15:45:36+01:00 Adobe Photoshop CC 2017 (Windows) / 1 720000/10000 720000/10000 2 65535 192 64 ?KL cHRMz%u0`:o_FBIDATxoHqv[Nc V (1@,ezb= zЃ1'Q$"HA$P,BC'y˶.\w|^}ߛ8PsV(PqtZ$lVI?Ǖosƺj_g\.ivB^&nWU|>ODMMMxJ&;0hݟ:,I>L o9xOM01<[騪J~?l4=uO"$ϛȤUvɯ/mD2(JVV*k79؁dYrNsaa4͵5QA(9|[     @@ @@ @@ @@!U(^IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/posbutton_.png000066400000000000000000000007641520675460500246140ustar00rootroot00000000000000PNG  IHDR@tEXtSoftwareAdobe ImageReadyqe<IDATxر+a#Ƞ(Q)E)eSExe?-+IEJBJ1p#eq&tsvyߦrV\ő`.ޫ-?ڙD<ɡ\RDF/_׿%]o ήe4>Qe6(ֶѾW\QiR{:ɗ0 ϿIENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/roundbutton.png000066400000000000000000000300201520675460500247670ustar00rootroot00000000000000PNG  IHDR@tEXtSoftwareAdobe ImageReadyqe<&iTXtXML:com.adobe.xmp B,IDATxYley߿N6f/}7FJ4#VHH8H` AyqN`,H6EdM4Mf&oίjFc@}~q=NUs__9]VJ,ߋm( yWWSG Ͽ| CNDO,>Jc=mע&JθqF455Ŷm޽{yGmmm<~8֮]=ظqcdʧ#nllhݻݻQ__֭سgO\t)+Ç͛7S]MԍZ[[_X\\|QT>vؑHʧhޞB|'bdt<|rjMNϞ=KQϟ?޵kWjQ$oc8?}R*d^TE7?{/o|s~|\7?-,_rtMME=Rٖxa߿Skg2B.7xEsqR26qxx$liƍb`` 5-1$h/Ҍ;'&'.5 h)͞{b~n>qӈh0--=x i&&&Sg 6D@TX|[ߏ[ݏRY?yxc59to?-W :e:1#uA`wtpooo8p ua3lݺq}iD("{M~$(#m0lْɓ'Ivȗt6 'EO[_^}= --]U݊폔n ||b7m\ZZ.9N|lk͚dS\d-!ِdOvX[S^XOB=b%=L6J VOMQTvllh] %qܼfgWB[[{j$.#Qywcaq)Z:J5O*em|~s~ѵmgܳ/fM0غ}g[Eؽ@tn'OE 14R8ʚؾcW4[b6cع{Ol+sc=~2Zۢ5;7mHu a8thl޲5u8}1*?X;rDLFڶ(Ssk{<+~74en(n)95ĚtOjl-um쓿ׁFGcsNX"! 㦓'BNh - JaN獸8Bپ} G144ǎKHɓ/"9tY5Ic(AEDK~70D|<$r=oQ QEj~gg]j3֭OgruoEdcB i\[[l2l1X? $~P "M{1 &K;\ۜnle/j{bĄ`1F8,!GNz kҐtb81AΙB#H̐7R3S<,(v R.O>G4ª4Ubt?7>}L[ֵK)wouUO$wp"@oU;/^|a\( td""F"l`DShȑ#i?ю?=)m:(5尝vXBP`W\Ie.mGù|q_/dV坯}3ʶ%󽋏*0R}G27:Ixh 762.3e^{-f"(&ԩSI0  8D^ $& RT aD4 EyL}7& I =ztuڡ>Br߈ZIst߈=b xcyzon d{/eeF 71GAЈ|% c8ju9E6!A51Db1DJrqBvZʇh LM:DUL!B1az AeeBZWԅ5=D ~"ȋ2I~DXEesH/%RvpvuuIdVpDk/5P+~ya ׆)$V%G^XB!*cȟczB4|GS~&?aPbĴr^@M @"JDD.#/Ψ;QG/w+ρH\!N΢²Ad4A0 #OW!xb"= QD: BgJ!Y<.MSzyQ5ˆOiV5@D8DGN~T>w܋i/e.)-^);艓qF=/'B$$*!G\^(A ~N&DIi(a ö*Q~E/E:Db 1Kt|~/>nވn##@"%^:L<0S_(ݽ'⃈`jH  J~ 1CtQ`{^þ;Q V^.ڔ"D%"IDHOOqD{?}ٜHp\= λipؼ-5 e(ɋs00Ap(B<3DH єBp י/6G~ p"_"5ԁh#QGyqv "JL͑./fR٦{E>C)=woߊٙh(VwR{z‹ܗ,)-"9L%%A`!B/&$wEMЈ2D%؏Fpٖ$D؆h\ȡNIEfC4Dz(Ma˹P.6ԟX$Rf*@DDDʆS`"""Q(DDDD@"""" HDDDD$"""Q(DDDD@"""" HDDD@"""" HDDDD$"""Q(DDDD@"""" HDDDD$"""QHDDDD$"""Q(DDDD@"""" HDDDD$"""Q(DDDD@"""" Q(DDDD@""""KRy8jZ)(λKe/ix' _I{WeW?U6Ş}++Rן8Z7nDSSSl۶-ݻ7oؿǏcڵѣhii7\LNN|:::X~}X޽;޽nݺ={ĥK9bii)mohh8|pܼy3ՅߔKȯEO>uX^^Nmǎ)mMMM|8}~~>݌6}53#w+{Lc{_խd>y|훯qdScx?W?y=OMM?ckϞ=Kvp_ d#Ǟ_kv>'&&R?2#m0vI#_@lڴ);;;AIϟt{єwOOOjOm?b||`K T6y޽v uww'~lrq죃'|I۷opEL:)ߍg?~bOlZ~m>}-EM Us0339Br`s;v– i/UǦm^8~xx$cic6tF{G{<.\ѯcRصkWaRԱ1mHiotmƆx\xD]{6zJLB B߅_A.c=Nmq8ǒɃ꺱SCñsΨYqh gJ%\RX DH Ё7G󽾾! DoMM%|2 ž"\<! z|" kMe |Π#}pPHcEc쭭-)2~E ?Ցn%C0ұtnn69΃Qf8oë>ܹ-#2 8l Q :Y6xlu2:ƾbSf}~Hرc+++L'O|ArćP:ÇɃrɏK=hnj~3&_R̦{5)޿OzwzSĥ 'b7nFbC14FSٳ3S׮PAOQPܻs;碱6gbH\rg_<}K-P)|{wR<鎾\w?NlNN@ ?Rt x]=*7rIILᓎ0@MM{vn,ݝMb|NjbT:Ѷ+m_XIe}#㠃fhhȓWӶۊU8+=+4Rg:mY^GQn / *i_|?L#հ,~>֟ $5fX0p8qD'86Quc;[n%4qȑi8Bx'0}"CZy]d;Dn9-_r%(IlGq^6lH^^DK&}wusHwƁb]4 J^OC_ rAVG^*AL}h'ݥ JaߡciD_ǥZo~UDBH@df`@P Ae1 XCT}C/QS#f:,e]S Exx6W" #y.䃠{^rRף\<&c(DX:l\p! DV0E4р@&jn]!R9uT3DBTA7B&P ڰ2uA兟"D\bnʑ&#ȏ)0>u?K@ I 8α.9(@0 & k~!JKiR;'ȓQ;(2A:#DGy r+DJ䌈4sg O}"; ,E~4x@𰐙&D/J)"Tp! %ZE#?:L$ ^RwʨsR@Q"hR%a]?~#-e?7^)ݽ'B$Yعsg;"A ~N"ADIaOB)*[yudz q`!zab&?p@dz:|8H@bq7 :sL'B)-8/N$,0Ctkx *!X)|G)+ 16&D "r8j䇸ʏpᄝA͡l,nf 6l2?Ž"o"':DLR7]ADw)uh)9SAeǞ_&iDSCDbNo-  }bc0!>;Dƈ񝼉vؑHʧhޞB|'bdt<|rjMNϞ=KQϟ?޵kWjQ$oc8?}RjW7?{_?{fq-,x20:Ʀ)lKaɰ)31Z~CLLGo__4.//%cGb˖1>>oܸ)R#R@2"xaS8 ybr"BQӀp<ϋޑٻ'SښJjD4Ԁ'tKĨAYιm ߻zȎΘlp?\UY|Bٺuk#SNO ȵ>l =l A r§ri)Rƾɟ{g;m< 76 CCCɟ`蠟 !NN 8mvjj*9炘c"tSw >G4 ~bpt"~|t_{xCP˪Cd$M`|HGL''attt&h_ Fv>'&&^h(8<"!ACA/" \F!ԃrɓ4\Q. i h4B?FO$}n |΋*מt|.n?-_~]lۢCvن-4i8#.霉 vi-Gpҷ`:q=6-ܾh3.V > ΋sx R յk@$V5sM[&;TjR&j}}C R_LVK.-1[ h˦Ƣ^(l}%RC;km][܇崽vM{p )s6?;9җ+4ψ=կ [ۢR._<\Gwm*UQX \91NLFcHt9ӱbT$Q)pI#@p4ȕNt@1>i(G9añPիW uھ}{#/SĨe5^U `)'`\ގΗ2]2E{?tGSz gS`\&bܲ`AH0Edy }E QDڵk)"E,lF0!@(t,CvIp b (ڸ.D}/ ~"+G؏HlD}@Ύu8 ' S&29[{(@0 vCT.+ƒ*%QB\Slm 4!# +9kJY "OFD$DKt uG!8?țȑ?_$Rbp.L9pP쟼玓$cea `qpb(AXy M"!^8S"D)~"p!oҰ: Ba'uӴDȟzdq/aN~T>w܋i/eOz ΁>N|+{8[2eh qyŗkPO""z?^"j$5a Bz"m䇀"cǎ%q`!z{MPR"RCZ h]!8nd;u̙TO")bH̍`ud()-͹q8Ύ2#(`5@D = yB )j䇸O-)? ((40a[~wD !EHA,!`/oŇԍ|<m$bH\F~ѕfw+o4H  J~ 1CtQ`{^>c1Q V^.ڔ"D%kO]!fw(OQBD|ȗmn( [V"RF( /ld:RdY!pe)"ڟ|oߜ !H)FDDDD$"""Q(DDDD@"""" HDDDD$"""Q(DDDD@"""" (DDDD@"""" HDDDD$"""Q(DDDD@"""" HDDDD$"""HDDDD$"""Q(DDDD@"""" HDDDD$"""Q(DDDD@"""Q(DDDD^D2ug܌IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/roundbutton_.png000066400000000000000000000031511520675460500251330ustar00rootroot00000000000000PNG  IHDR@H^tEXtSoftwareAdobe ImageReadyqe< IDATxڱKw{pHk@ qpn\tNK\ t(R IM>WDY!9o|~ըF⵼%7O`|v|kc"*S/_w߾K7~Ϥ4k(Y!ibiuY__q^y|ܽXz{|҆/fV\q覭Wǯ z' >OаO0'_a'^u/{[[07濃G. HO` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `0fѪxu;S,ο|In󭍯nfU^<+AY?kkks>'X? X족6,/'n;pW^q;`c^l'mX.`O:ܟ~>J0EңH5nN6c<z07M_!F('XS_t߾{?d 3_! `P쏐x@@@000    @@@0000   @@@000    @@@0000   4ˏVU먮@oΟNMnx}Ó̟J'J~>^Z? Xe %=!$mX3_,n dLрz&/ N,`j-,iWgq>iʳ ̦fʍ7^I?ztHW#We+>gW{vMd--fr߁B@T! ` ` ` ` ` ` ` ` ` ` ` ` ` `i[;ߜ:]?1cy44]M֯aˁV֯^Uv_aLv~[?@Wx@@@0000   @@@@000   @@@000    @@@0000   `-1ќX;IENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/scrollbar.png000066400000000000000000000561201520675460500244000ustar00rootroot00000000000000PNG  IHDRJY0 pHYs  8&iTXtXML:com.adobe.xmp Adobe Photoshop CC 2017 (Windows) 2017-05-24T17:52:38+02:00 2017-12-10T15:45:43+01:00 2017-12-10T15:45:43+01:00 image/png 3 xmp.iid:10dc57e4-e402-d54b-b2b7-df4d40added3 xmp.did:10dc57e4-e402-d54b-b2b7-df4d40added3 xmp.did:10dc57e4-e402-d54b-b2b7-df4d40added3 created xmp.iid:10dc57e4-e402-d54b-b2b7-df4d40added3 2017-05-24T17:52:38+02:00 Adobe Photoshop CC 2017 (Windows) 1 720000/10000 720000/10000 2 65535 512 256 %Jb cHRMz%u0`:o_F#IDATx}wy'_[KObɖ`>yI l8$&"EG݅pU.1/̽PHl,$[Zi;;3;==3z l iUh~j5gya*f*KGsxߝ@o'~>0_rS9jdYLKUUK 33fR--*8< 03I?v[Az0 STF PQc[j]UUAbzݞ;wTks3dՌDJ |LQ\fT}kXdfqqQuBC?Kj]cǎMyrNnϼx>WOy7rp T_ !ճV>{!o+AE^w32O|O?Swᆕj"粨8ʨ'Fo<5u^W9W*{KTjVVV>OvۏsGE3陟m{lF}oTqB.ò,ZV??x_ZzU?Ӓ'~A#??{.y^ӱ򈿰I=R=lj^}ORQӹGtAyO=LK]?E= !bC^{#hI ұ|Ɇ==o=z\h4Tn/ B/9Td:@xjsd{e@@:-5'N,`>q&'@w^`FV3 ; w~o'?@YYY!ϬN^$}IIPyMsss\m]?!^Z-DߏN9Kh4t:u@ 0(:Bh~~~]ǡX,1;;{d&j5*F#>۶-˚-5[R)A*t\CZ[[#v#]XVtkH;LitI 7\MӓɄZmpa*~ܞ6xR]~Q-jZrs2TO87T*)J쟞(H>#%ϗ$Dzl6nÑu{|t^666l4M$Zn 0_W_r*F_+߸Rw;WW~e<,Z @ @ @@ @ @ 'fR.)Uq]e{݉ t@azcvqgPkJiq^,0@ `mہ@^J(Pyei,)b0mw:l6[T Æa83 $Jqxj(y󼪪ia4⢪k<ϲl 8OF2eqWT4M j5x<0eYS#!>+ٙN#&#󳱱ՠ;k7.<3j {!Vw"-l,.tF2x~B&W#d~Rء}KbIF/'XB:mgTF Hty4eYkF#\]^^9$I9H$J$I XlY.^׶~-  ,˪jͻ^OAAVF8ZP(T.1qBAR$I,˺w{]QB !q]ϵ   +IΨ 83 u}L PTggҦa={i$I:uT.ܜlz] .Ίx酅];mdr4R-oYV,Teyy>n/,, C۶%Id23EmYZ _$bi({) [[[8S*mMNHIhFT*t*ec+0 2Y?Rgeyٶg~L X^uBeY|~Ϟ=i^f\.L6`vceYVkee0bx 2Ν+ ^L&FOo4ey<e4}91rsTϏΏcY&xDg46^G6Ax'`YvREZW333i%!iR)edi۹\β,M>˲GF1??8R^g& F#˲.\H$p8TEr< $IǦinnn...z(zkf$Iٳ,˖J%YEڶH$$IfB/FQX8$^ G"R @h B0 :u+˭V˲d2f B.[[[ B,rmY$XZT*Y7mj3 ;Q F3rvYڛ\Vۚ8)ٙ|6&VEv<^/99~T/\(Z7|!$ ]($Iu?EOq9l:( 3L#JS:?oeG_voNe!Dzrr8}7T*˲TUY\.$`0=O,$ zKl6k8nP(d2J2;; !مl Æa,:!_UTf Dz^0G>oCZ]FT@=Bu 9%`SwK}`> @ @ @ @ @ Z 񿩨z , n#bZ QQ'?v: v;L[(dY'{ձcpK_+[Zikqݡ$INswq&fF2wu]E񆛏׿L&#W;qW>MT?ӗ?~… _}i x?9>HهV쳩G;:Nףi~YwZ.*eywm?zj.p8L⶚{ѨSQ<#$y. !B=?xȲLh\Vοn z['"!__xg}SzdNϟh?99|,hYB׾wfPQ4}'onJl6}Rg2h}?upHv3 hAl8l9!K5Fc`$I><77G ,RUo{ }쮷41~^"Λo,KQblv}̣O 7⋓Ss_}^۪x `RW骟'LN5.ځli/TOG.y^"jV8.H9zK]\\ܬrfOgj?ykkk,={zwF B̀_+ WxBO~oxU0L4u\W .8:}HUUdN-ޱF1TUM&Ν"|q}o$ s[ Q<}yä}H\D 666KTԿ`fVs_v]׵mۡ+z0>⺗]zLM3 ?^M&Oo;.uD_U-ǎ F_'| @ @  @ @ @@^9xBHa^ם'f'tMw\geyٲڰ-90x) iBeٳje8%r*^#fb禣|LJ A۲fgg+h8'o#)=n9+ONG'&53+gMq/vӿH3C|ב$i4{ף(韦f^8N(mW*eB>CkxDQ(t]3Msu+bIU/>0v\W HH D'*G8RcT̏ Kd~Fc)KV ;8}m7jU*JG"=> SZ 0t}̏EQEѲ,^RQ@ lu83iQ6 #)L>xhmMUOg3{ML/>Bi.y^04 Vk2?d"*GL d;8=*meY˲,R ??OT n뺺̩sy*fugE|429Ei`0ss;8%09P^A333eY0LWCs`W4ӎ`03 R)kkC4v_ųiijCP4q栈:q $D'N9`vEI&R)LVL&S(2,x,^ &QE$B&ҟqMQNs݁(XL;ct*4M` ˲X ́nI$i׀z<džajd2qb~@;IdY4/--~A8?(9pep vmFݮja4{1W;O,=`bk5AdYvGee[ؕ(G"0 h'7ub /~6 \\{z=Q40-pJ$n{xtgggU,q~24p'Jy?, ׀ecN>!dk˞b& XL=M666,˒e^E1ˍcEQx:F0]?KGE` `T*eeyS\ /~Ѐ<O> BdP(0 j&_%*iNv\.j53y+1vL&%IˀS@~x$IBs`WpT*a~+*q'0lj.!daaaOsنYvӵ`PQxO&awظOӴh˲.\,t];p4~ !Tjyy0ʏO9Dz,4NEBH4mn;Y Bh,Esp4q??L&u}mĀ~`вr"d2$2 aJ[p$۶Dp( Ĕޠ/bCms8lÅ1,#nZzϬ PfX[J177 ^ ^okL*jި%WB.]KnIENDB`simutrans-124.5/themes.src/pak192comic/pak192comic/theme.dat000066400000000000000000000124211520675460500234770ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: prissi, modified by Leartin # # All image name must starts with "> " to have them unzoomeable # # This theme has the scalable standard look for pak 192.comic # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will used the unporessed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images Image[36]=> button.4.0 Image[37]=> button.4.2 Image[38]=> button.4.1 Image[39]=> button.4.3 Image[40]=> button.4.5 Image[41]=> button.4.4 Image[42]=> button.4.6 Image[43]=> button.4.8 Image[44]=> button.4.7 -- Obj=menu name=Roundbutton # unpressed Image[0]=> roundbutton.0.0 Image[1]=> roundbutton.0.2 Image[2]=> roundbutton.0.1 Image[3]=> roundbutton.0.3 Image[4]=> roundbutton.0.5 Image[5]=> roundbutton.0.4 Image[6]=> roundbutton.0.6 Image[7]=> roundbutton.0.8 Image[8]=> roundbutton.0.7 # pressed Image[9]=> roundbutton.1.0 Image[10]=> roundbutton.1.2 Image[11]=> roundbutton.1.1 Image[12]=> roundbutton.1.3 Image[13]=> roundbutton.1.5 Image[14]=> roundbutton.1.4 Image[15]=> roundbutton.1.6 Image[16]=> roundbutton.1.8 Image[17]=> roundbutton.1.7 # disabled Image[18]=> roundbutton.2.0 Image[19]=> roundbutton.2.2 Image[20]=> roundbutton.2.1 Image[21]=> roundbutton.2.3 Image[22]=> roundbutton.2.5 Image[23]=> roundbutton.2.4 Image[24]=> roundbutton.2.6 Image[25]=> roundbutton.2.8 Image[26]=> roundbutton.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> scrollbar.0.0 Image[1]=> scrollbar.0.1 Image[2]=> scrollbar.0.2 Image[3]=> scrollbar.0.3 Image[4]=> scrollbar.0.4 Image[5]=> scrollbar.0.5 # scrollbar back left center right Image[6]=> scrollbar.0.6 Image[7]=> scrollbar.1.6 Image[8]=> scrollbar.0.7 # scrollbar knob left center right Image[9]=> scrollbar.1.0 Image[10]=> scrollbar.1.2 Image[11]=> scrollbar.1.1 # back up and down Image[12]=> scrollbar.2.0 Image[13]=> scrollbar.2.2 Image[14]=> scrollbar.2.1 # knob up and down Image[15]=> scrollbar.2.3 Image[16]=> scrollbar.2.5 Image[17]=> scrollbar.2.4 # scrollbar back up center down Image[18]=> scrollbar.2.6 Image[19]=> scrollbar.3.6 Image[20]=> scrollbar.2.7 # scrollbar knob up center down Image[21]=> scrollbar.3.0 Image[22]=> scrollbar.3.2 Image[23]=> scrollbar.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7 # goto pos Image[8]=> gadget.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- --simutrans-124.5/themes.src/pak192comic/pak192comicxxl.tab000066400000000000000000000124001520675460500231240ustar00rootroot00000000000000name=Pak192.ComicXXL ##################################pakstuff################################## # pak with the images for this theme # These are under CC-BY-SA 3.0! themeimages=menu.pak192comicxxl.pak ####sizes#### gui_titlebar_height = 34 gui_frame_left = 20 gui_frame_top = 20 gui_frame_right = 20 gui_frame_bottom = 20 gui_hspace = 6 gui_vspace = 6 gui_tab_header_vsize = 32 gui_button_text_offset = 2,2,2 gui_checkbox_width =34 gui_checkbox_height = 34 gui_button_height = 34 gui_indicator_size = 20,10 gui_waitingbar_width = 10 ####general#### font-size = 20 # Main text color, used everywhere. gui_color_text = #403022 # Color of text in tabs, finance window headlines, ware list bonus text, minimap zoom indicator, fps info in video options. gui_color_text_highlight = #e8d8ca #Color of the title text in the welcome screen and in help. gui_color_text_title = #FFFFFF #Color of strong/bold text in help. gui_color_text_strong = #e8d8ca #Background color of selected elements in lists. gui_color_list_background_selected_focus = #7b5942 gui_color_list_background_selected_f = #7b5942 #Text color of selected elements in lists. gui_color_list_text_selected_focus = #bd9263 #Background color of the elements in lists. gui_color_list_background_selected_nofocus = #7b5942 gui_color_list_background_selected_nf = #7b5942 #Text color of the elements in lists. gui_color_list_text_selected_nofocus = #bd9263 ####charts#### #Background color of charts. gui_color_chart_background = #cfb094 #Color for the horizontal zero line. gui_color_chart_lines_zero = #e8d8ca #Color of the odd vertical lines. gui_color_chart_lines_odd = #e8d8ca #Color of the even vertical lines. gui_color_chart_lines_even = #FFFFFF ####edit areas#### #Text color in edit areas. gui_color_edit_text = #e8d8ca #Text color in edit areas when the text is selected. gui_color_edit_text_selected = #bd9263 #Text color in disabled edit areas. gui_color_edit_text_disabled = #cfb094 #Background of the selected text in edit areas. gui_color_edit_background_selected = #7b5942 #Color of the editing beam/cursor. gui_color_edit_beam = #e8d8ca ####buttons#### #Text color for buttons. gui_color_button_text = #e8d8ca #Text color for disabled buttons. gui_color_button_text_disabled = #bd9263 #Text color for selected buttons. gui_color_button_text_selected = #e8d8ca #Text color for colored buttons, like in the finance window or charts. gui_color_colored_button_text = #FFFFFF #Text color for selected colored buttons. gui_color_colored_button_text_selected = #FFFFFF ####checkboxes#### #Text color for checkboxes. gui_color_checkbox_text = #403022 #Text color for disabled checkboxes. gui_color_checkbox_text_disabled = #7b5942 ####tooltips###### # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = #bd9263 # tooltip text color (240=black, 215=white) tooltip_text_color = #e8d8ca # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 ####bottom#### #Background color for the ticker (bar shown above the statusbar with info)#B3B3B3 gui_color_ticker_background = #bd9263 #Color for the top divider of the ticker.#000000 gui_color_ticker_divider #cfb094 #Text color of the statusbar.#000000 gui_color_statusbar_text =#e8d8ca #Background color for the statusbar.#9B9B9B gui_color_statusbar_background = #bd9263 #Color for the top divider in the Statusbar.#DFDFDF gui_color_statusbar_divider = #cfb094 ####random#### # Color of cursor overlay, which is blended over marked ground tiles cursor_overlay_color = #294ab9 # windows drop a little shadow (default off) # gui_drop_shadows = 1 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 24 # show the windows close etc. buttons on the right (like windows 98) window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 1 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # first: draw a frame with titlebar color around active window window_frame_active = 0 # second: draw the title with a different brightness (0: dark ... 6: bright) front_window_bar_color = 0 bottom_window_bar_color = 0 # third (best together with 2nd):use different text color for bar front_window_text_color = #efd3b5 bottom_window_text_color = #bd9a84 #Title color (Newer) default_window_title_color = #422810 bottom_window_darkness = 30 gui_chat_window_network_transparency = 0 ##################################toolbar################################## # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears # this should be left for the user to customize #toolbar_max_width = 0 #toolbar_max_height = 0 # icon size icon_scaling = 200 simutrans-124.5/themes.src/pak192comic/pak192comicxxl/000077500000000000000000000000001520675460500224375ustar00rootroot00000000000000simutrans-124.5/themes.src/pak192comic/pak192comicxxl/back.png000066400000000000000000000063251520675460500240530ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe< wIDATx?r{f˾u59rĩ"NJˮbꢫU IG Λ~u`Qbo7?~B7/6O߇իW/Mo߿o~Ч @ @!@!B!B!BB  @ @ @!B!B!B!BB  @ :_~?nM }d_ٴg5mďi-xnm0S8S,r(6g=Ryʣ&z3i'=1'lhpBk&+ѐ YQ%;i ɑ艏д) PbFMe|]CkC/V?s|Ze>D,FJgc=m7ѩMwȃ]HVir&s~X/.:&,xTURn]l ꃛNWxDP`_wi%>7?3ްmN;{vиmwl¢3(pZ7Rk ό`',%g2焫 ]|+rwsi4HrgeX6 .K ;vtEi|Z˧<:Tk3cRU;wCUXGǧgN O[b@i)-dN\PRcb#(&LybX.lvhi9ͧ_z{'EbBd ǧHfF O<ºIULŔfe9nC͛Z~BY!h}}߹]PeQwǣMe?:CPB`WV!lCΟ+[fe7\ن&ueeY0_jK\h\;JU!˜+Ӟ]Z^WTTCo1fj=vеCCrW:ygo#'V+3a!Oㅊ˺s}99Q#), q\LK&6D_2 WE.(G)7S?~x/KT[z/q+4e~ܱ꾺>>14S-_K~h,_hMi,svInngU:x @-רGsk/͊vXEMA%Δ,=;IG]#^.ۃ兇iYfQM;@~].ۼ9#FZ 霟^ީ٤LwvgeE9aBWebsIђtm՜t[kqag_rȎ~KI|h}Am6!,,IZxmM?1ðPvs$>.끷۫ƜXs!x#?28u;*ncwY{}uĞʱܦTs< omҸ~°ܠO$f9#jY+RM1i "/"ύ].2gnEiij%=9:ϕ@WgY3]ܷiK:8(^(%y߶U@jcG$|]QaXΞhQ}Y̍`p,R l^PrɉXtܱGlǨ5MUϲ :2QlMuR]ZM݅(s1xK_]i ,®$fqO=vγ􆋓2/׆q\6ˬ{LCR3\92mF;0B8sAEE <:in퉰Y ;0ո;G,"6s34ʋs?~AF,Π`aWÞZZ]!Q</WOgHta_b9yyrgbkĠLd0Mn"B>/ۚ9bgh:\UWvm64~?<38^~¶W/ Ë@;zj>ImGl?5&2j|RPG[t3h&`YLH3t[r)F9`8K NV=xy1ꍹme EZZdv> 9Wywj[9߶z ].G[h} ݥ!2c:*=q *vͳ:"`(YuE;5zVunI1J|su,y'ܴbƝMEECYq\]Lhj-eŤxL/BT€КmYv_hp]^67P7VuC:?֯ @+KQN_ @ @ @!@!B!B!BB  @ @!@!B!B!B!BB @zN_%O=IENDB`simutrans-124.5/themes.src/pak192comic/pak192comicxxl/button.png000066400000000000000000000453051520675460500244670ustar00rootroot00000000000000PNG  IHDR@@TrëtEXtSoftwareAdobe ImageReadyqe<&iTXtXML:com.adobe.xmp [3G5IDATxy]}w^5 6g4V-q4#C3 Fف <0 8'`X[,ZH6V$*X k_XdT{5{{~ιrT*]/)}k_O ݝBl(}qi2_?޽'7CUU)<yh+ձBO{l a|?D!=vy.,ո]}J 7 _ҩ-[۷CCCCKٳG}u68O*O,R'd@11.,svv.-+uugaǎ1_u({cR9577bcrrS](g0/ m~߾>\.wûqLyt+ځ/[i81644Hwm۶m ]]d%KLLMMǓrs!!ԇqN|!V 1ƲCx3rٙ;sl\DﺻSoA9 ;::,2؎pAػwz߹x)ϽB^?F~*ߙ8)SoHQ*U%$C5;lB9JخY?"hmvK;~C!F03N+Z[g~cu&ΉfCE[QO%g(@9L}מ}[7n Uk74h5P ^˞S7FdPUS;}S΍[{|c|J'XP[F@xQ dzmhn 훶(U## #b=ͱ[mյ>u>ק7JmIظy/RHH۫B# ɾp';gϞCCC3eӮ]tdeǎ)VG#~!G qLSWRӧO'i#7x#>uШ:8?`Y6Js L)BFLgWbK}Ҳ7:*M~o4_ M}v>̬Q#±zީtɥ*sp3ty^QkL\VTȚ@To_´#ܽy-5j44L4x\Egr|(՝̻QVw`E{_8RF?$?ăcҕ$ۋ dY1\aA*޽dQ:sL~z8yd'D_%HY JYu`߳sAL"8 qE ݸq#- ab:]fɳc6rqHCCBODÃqU;9!m&kE.[.^adWH1AΝK@LP Yi"ՔUB A9,Gy>3eȪ 2,ԩS.CX_~7dwS4%!~5ycLؖb^]  irCkOt -Hf ` cbڰdJr9!k䰿n޼![fϔC0Q7Cy#D̃3fi,ظ.Ah( ١ b{$lܯmYiG8hjjS#AF#7 nEu5""HdX.!˨39BؾŶ$Rи B(rwC&r "x!BdPҝ)irV "ʺGYUy !L%#N},e/_۫Rb[d=$i9up?Kƅ U= "CcKJEdSȄG% b+c|n҃ N1CF Ivw?n*$tQQE gdo 3ta!CzrJGf!SE9y,w3 ;w߱^"fD;u]m]JKƐ&?x FmdP<@ٳ)kC 5H b H}E܆,WwoaAP(Cô*~| ;E݈E9YBfQ/o"<"fDց%?ϧ+lL~2JA>#HBf `zww0 Ӗ.ڐ2Dwn2Oy4\Q7SwSeKbBpSV~トaĊOe+%E Wt/? "O!3^ǥW~sb J~"H1=d( GW+9?MFⒻŐR?KYBYʃҩ,&͢<ˤ\ïb[DY W"teӊ ϦqK,$:x;0ߝЅ !E3Jd HS; a3/䈘#S=vxdq{=D1˷NHސrM*)EKxw s3ӡ>6XRAcD"7J )KL]IdI 21dh`B*'[t0~Fw!Qd{1a3-ÑH1 ib 9S6Če VnJ{ĉH6l6m ٳg.lܸ1LOOk׮ưs4> 'N;wu3eݨU?/^xiLۻwo*[UUFFFR=o[[[pmS[8|@uamaxh8ۿ/']*ȹ.߳)\M73?/ĿKE֙):6_?|?//#Ja}a(B?m)8Fc Áccca--c>VkKkglY T#'a箝aC/b6oubO㣎1LOM8!98t:_.ƸNܫcu]5ˁœ Q1mmq'BGhRytGnnPŞ59ɉW~빹=5MkRsLCÆ|/C[nI'Z>x2kA555uǎ1ٹ_7o^NK'&7CuuMB:ֶt⢱7,S ۰2NoNe8)6/ŗg|0|aִ= KΥF Gb/*<Oܘ`!N=b;DF3ɓ'P'X|LCBI81?|0-q:tP]>q uvvFyˉﺻSoA9 ;::,23X.u/'|$bMա3'P_+ScOwOQ/;btv+16xf,wׯXD_.̇ᧃ!gR\Ltmjċ'Ddl|,4D9ٺ=\P> !fR,W]xq$?QcnZãGKӇîxu3#QvpQ&q܋خYs|dPldcʢU(̆P[Zj@s0̧-N;;ǥ޽{I9 N9KͶ":b2==@\mۺQv6nܔN,HbA#H/A2۸=7<4.,z}NHԵ83?Y__QAxOyu55Ihԛɞyi9 8q`w{Gī8HP&6!7EukB-^ " KWU)BٳgO: ϔ vJӑNЙS2F[ >Z Q >}:sƈ7R:U:fff^rXW~G !^ǏOK)Uqw;;?u~o K)_%A;z\ظe[w OJELHOD;~a7+黜ZOQ杓4'ΜWz*]5}E}H+Ə?8o߾$dRʙ %˂̐DaA*޽dQ:sL~z8ydd.7.! H,s!8 Anܸ01.ߙ ֕zضn< ,e4vlԸ !up~ 'Hd>-AO*X.^2dWRcΝ;l ș< DVի)Ad<{))Cw I"rO:,Wb}'xdw(g, oi"CF落6.0x?}`[ >Y~X#YCoh+@X 0&L y J$gZ6HqsT"6#Lh|R2D&~!Ky4eYօ1= D١ b{$*Q$^W18TUU _OoHWE:'Q[WJ:F 21ddC [Bք  7%ʤLtAaZ!'B~>dzq<&2vԉ.42xd)>t!NǺT+$lel@fScDCmI~h(h 2N7P Ddg=EFȢ) A6(D!#C@|Ko^H uSY";C,"2 E~ 2B,Ċߒu>Cޗ/_~텨UBlYyor…jyBV.9nr~{2(dBrV `L`rAbn.$L s7Byrƈ: 9! >dFc=肻rJGf!SE9y,w3 ;w߱^"fDցƋƉFFƐ&?pىں=H啻b o{gϾzNR08`_"dըbR#NY{l_zVk*ødib2Ž`Qw҃,!dnԛ2DcxOdi?rECхFY~'bBD=|wc]b;b1X'.? u1$4v4 rJ+{ii| )l/ "@ذxN+ h (=B]OH#C2CɃU⌺ذԐB\rz)K#UY"2Ky`:Ӎd16lY&X/QDցgp<Еq48Em{$:@h;+?ˇϑ 1A8CId Hu!2 oLG?525H3n;zGցl#iH|;;1O~Z~5]sQD>b,Ɓ?#4FE&{Suvq~{)҃dbx@JȪ,0_u,C=w(KCL#By 4b Zԋ FB,]m#`:BXESD]PQ-S"""RqHDDDDQDDDD HDDDDQDDDD HDDDDQDDDD HDDDDQDDDDQDDDD HDDDDQDDDD HDDDDQDDDD HDDDDQDDDD QDDDD HDDDDQDDDD HDDDDQDDDD HDDDDQDDDD HDDD HDDDD)5MT_MҷP* -b6|Tr|i2_?޽'}9jjjBooohii ===)lݺ5χ8hooO7l6mFGGógC]]]ظqc ׮] aΝ˗iz}}}'NwIg˺Qq}qxūuX\\Lg޽{S٪022kkk[p?wKhjnJ;q>tttִRQ#;˵);FR;.%~2q<;;Ž;G%1'NJ7|3 yxA"5333 q`: ߝ&1L\=z4t#fgٵkWTe"b;:t([@|~N חN;K9'yAh$XgmelUœS9XGѹ й#l;?[<ػҚxIb`ZO$-|]]mXū_!Ʃcnn6]4nhz!JP{lR<77a1MNqN u3˭uΥNLLWP2+cf9wB\y/dyaɘ2l q޽'ك᧟tX 255U`vr,탁Gix??=L||i ?髀y64?Js=]Su_YP\x;\K _rpt[H՗y=}'Mybr,+@<jB԰qP cf:W W?E*sw8\<ٔU>Ol755D =N Hٳ'I1gSӑfX!>!cD!_B(C}ONҒ3Fod:h,8uf9 +q9UtL-7*+c.6_imTsgZ|00=#3kԈp5疧+O.- ap3ty~M[?VKvHȚ@To_=8  b[.q#K_F[^"pLv?No@<Ȳ;v,]I M!fҹ< ‚Tܽ{7t̙pp=]Q?]n\l "=d{!g,sss)&օ෻qFZ_z]n< |:m+wT:4$44L4SD" qG7<8c֘2!˜ !;tsD],|ձ-K1(@"wHРHqMCr[O4dh8HXiR0 ;5ad)A@<8YBbSLcgD exR,ˢedl\@M E\CtQ]g;rT}mHqˁ)7P4x\Pdh$ѰrEAf٠9@ $ b}(4!EC^iă "g$+/?ą)C 7Ad#^YMK*"#$Ywc9/}W^" xxN C=… 骞l I%{)SiM!3.czȮ Ave-Qz !fȨ!IMPL!P` qFa2B #uʕ$8BCRb^ wW%Ŷ +kҠ1)24444G)1@Y8Gz>P>{l#3$|E>آnCGPbSA ]`-u#^d %;DtnԋTRlYW WϟOW\5dbrVViɃ!D*|G2C\ %H˷ATpц!"=uyʃ䊺M.4Ŵ\>s;.ti"+֟2:VJlY@D^D"Cf=Y`JQ cost_YlDAzBczJ 4Պ䧝ԐB\rRJz)K#UY"2KyP:s>$YgwURl(@"4y+8Vdx6 v[i !HIt+Y &?ˇϑ 1A8Cg ?J)ߝ0r & { 8tgQ/:-z$ b>ogg a!?嚮9URl(@"pn8z\O#4FE&=of!9s"A@ * 21 A< %dUx*u 8L=w(m^!,@#V EH b@!+<6֑e0!F"pa)"y߶b[d=0DDD0$""" $"""(@"""" $"""(@"""" $"""(@"""" $"""$"""(@"""" $"""(@"""" $"""(@"""" $"""(@"""(@"""" $"""(@"""" $"""(@"""" $"""(@"""" (@"""" $"""RjRt)ro}ˡT~jZBl; nwyrZeraؽ{Ox$<zXh_?rgXhhlC' 1kviem#VB}}]=XNln߾q;oذ!lڴ)gϞq0==<]Ν;˗/0<<N8ܹօ,u>2ŋW밸ϴ{UUUadd$ֶ:?rܔw~|i[kGvkkkSևdY6;>Lq̺ǏVvNJ?'}a(8 = އ?{adh0t"[ ?2ybdG`ptW.- p$;tضm['-i{퉉D9'Q㼋/S#q?i֭A@ܹ# ~{{[:`&':pOB<*9pB8ˌO=o޼9 :Y$ɓ'XHtCbl׈ҪF"j$E8~|Djfffɑ*t2;'Mb8zh:G̳k׮wc/ D('w?tP[n%b,^]] ~'⹯/9vr4 ROԃ,Hά*=K'rl/SssGvw{(y8ws58y*I"Z[[yTJeSLsu]]}MWq$/qCSlhMа!B6 xnnnb^SS✸Ng[[[K띘L֡>meVvrͭ^Z’1e@޽{ Nh M-᭳O?~: djjpX}~~z"Wlh Xuy!<~ԕ {!8{@'ݿdw>) A"KA;з/5*{z1IOZ^*X.W$x6Ԅ(p]alM zt6(~TNpx)-(|njj +BA\{;b سgO bϔ! #H B|2CƈC686ؿԅ(QN>%g7x#>uXp|!2 ?rXW+r,dZnT8W_]mK}Ҳ7:۔'~1``S{Ff֨X=k-OWN\z[<gf |nJ1]5R߾zvq5&<臷>s\G6ۗٿz7܍:DE}Hߞxe?vXd{D 2C̤sy{nD̙3ɓ{~ܸ@,yEz!CYRL"8 qqowƍ,.._/}+=pT&5thHh$hhx j] E%@&)N.lŋS6 )<& 2=3/dyիWSV 1"Q$g2d'IBvKV]S"Od-yePH!>cVv}_'SYxxƀF<`"C b[i ,cfC&WD2%9ӂAr_7oL-b`3DЍg!UY!Cdݨᩤ1$4.#CS:ϟ?j.24< 3BT +.d@Jq=|o{' 0m )CDl!/|{&M#)u 1]h%.i|wc]ʃD #V?eu1$\ӽ+W44>E{{@Ɔߜ仿 #,t<#8" Ah棫 O;g9t!;n1Rn1G1DdKHcfQeR.WI-wĆ+wP䁮`Z44ypl %y1Cg1,n?G..,)BV"D(|wBeCh hpي )҃/ 2񀔐Un|20Q"zpDzPX!6")FXGt, -2OmJm (@"""" $"""(@"""" $"""(@"""" $"""(@"""" $""" $"""(@"""" $"""(@"""" $"""(@"""" $"""$"""(@"""" $"""(@"""" $"""(@"""" $"""(@"""(@"""" HAoJҥkʕ/REmh B쀸Kkʅa=AϓdYx?b٢mŲ2_m)O\yaa!|'wJ}6Ɔ0>~pcFlVj>b%ׅ݃Ty˖-ۡ!ÃӧOÑ#GBMMM ---'455[01 ¦MhxY8p@~ 7n ڵk1ܹ3|2MĉΝ;i]rY7knn~UǏË/^bZ>ݛVUUT_[[[p?wKhjnJ;q>tttִRQ#;˵);FR;.%~2q<;;Ž;G%1'NJ7|3 yxcWWWc߉xK'x rT< K43W>UœS9XGѹ й#l;?[<ػҚxIb`ZO$-|]]mXū_!Ʃcnn6]4nhz!JP{lR<77a1MNqN u3˭uΥNLLWP2+cf9wB\y/dyaɘ2l q޽'ك᧟tX 255U`vr,탁Gix??=L||i ?髀y64?Js=]Su_YP\x;\K _rpt[H՗y=}'Mybr,+@<jB԰qP c^K~g]%wzœMiYEvSScXH ~ ɾ#{ID2"t. d>⊌qlpl Q >}:IKoF}꠱Bd~֙.l9WWY1ܨp\s%rW_ٟߦOm?77$ a] ~7ne!8宇up!@?J=lo *)ιpǷzjRА:|2%@&)N˶;\x1eȮc0m!Ӄp 9B!An^z5e#9HAX]_|=.)Cv$dad55!Z/DFW֙eB1MdH=!7+?wAlоp>^h`JaA$3Ø21ҝ)ə䄬 yfl&n4>SD" !KĘ5LHc0'w"H\Qˤ<#aԇ#6#|;*Rv"Y e2@4 $4!gdL慱3YE+YAJ#NF#8dاӘDB(G#+9 Kil(eAPyB`$D,eE!Ulu}Խr,'*WD*VHP᢫HaJA7r8d"3H@TG ᡋHRD9l )"DHV^ uSY";o F.QlUDF!oTr_˯9]YIsZht1pUO ϻ' 4d3e* )dBr`Ld1>_6J8!? 5$ʓi>: 9! >Af3WPd\Ah]!@̋`1ٹp§b[ H%W\ӥAcHCRdhiP>{l#3$|E}E܆8g %˧ BgW[,FX/!=KvzfnԋTRlYW WϟOW\5dbrVViA""g@V] q Lz.NQaERB^M)FR+7Ab?(K\RsǺХvd|1XaֱRb[ :"rEO\ 2 eCV ~sb J~EF TӃPRA>V$$?MFⒻŐR?KYBYw,&͢<ˤ\ïb[DY W.]"óihJ AJc^b 0Y> C..,)BV"D(|wBeCh hpي )҃/ 2񀔐Un|20Q"z>ȲPX!6")FXGt, -2OmJm (@"""" $"""(@"""" $"""(@"""" $"""(@"""" $""" $"""(@"""" $"""(@"""" $"""(@"""" $"""$"""(@"""" $"""(@"""" $"""(@"""" $"""(@"""(@"""" HA.$vIENDB`simutrans-124.5/themes.src/pak192comic/pak192comicxxl/checkbutton.png000066400000000000000000000044141520675460500254610ustar00rootroot00000000000000PNG  IHDR@tEXtSoftwareAdobe ImageReadyqe<IDATx[hiǛs$=N4:tawU] {^x\aYPEAWe/<+8{θi96i4mӤi%/İqM^C?~yyg,[5ZA틚@7=ܻXFu:ہ@`cc+ MRNg++dB9H9dY-r@n𰵵T*ܿ|>: X,dGGGPЄT/o';f}3({n\i?5 G;PnG(hM bşvW/~eX[tv1AYnzmsH׷kr&WX@ycO^r8ZMs\_P(~}k3&[,RT2dN #Z6H,듗P8֤(tTYJ=lgʭ@&M& J/ɐA {:5frɧ$qZzY{y? EP(ԨIW|^ Q UPC)vff^/20CHB_}D"YE0}Og0)c? i! dNQ= a9!J}˗*IXӓ2r_^{ҙ }թ%w`j? i1Ɠ6&cw7.!NIcEZ|J@R)a~wJ=+ S+xZGdٞ+k\zd 4z8k-2c Z蝀f\ntjdQ0vݻ |OPcBO ylP3/}% MxFB@Xt#PUM)ӲE"֙"qꇩz?ummmXqE@7eJe:ŋǔ^r&~輸n2p;BX:z:{~Ϗׁ0=2 hN 5?ʞYm8œ{Xn^*TUgYI--lv`"X4y!kzfWS7vwi&I'n/^cw~f|%MMt`n &ngRS4t4490 Wq|ZyfG1l_A?.z ! @@! B@B@ ! B@B@! @@! B@B@ D7=)kIENDB`simutrans-124.5/themes.src/pak192comic/pak192comicxxl/gadget.png000066400000000000000000000021671520675460500244060ustar00rootroot00000000000000PNG  IHDR@@8-tEXtSoftwareAdobe ImageReadyqe<!PLTEy[Anвϰjw}v0IDATxv! E-n x &GA. $aUiSRt-5 /1cdfi%|׭k|Sas ?7 ^ok596,-[ ,a @[3n|*Вux\? a~=@:jp <|BYHB%)4?&g OX:A?$еxwDzC[ܠ? ğ;e@3HR)S{ʦj;V]M+e$j/Wn]M5dzi;8]HǞ@%~qϏN?^czg+٧F lO)S`E jgnO߾މ[3?dus9)ZqAvȻm¾[GDr,^/9MynX?j [5:@Nbi~P3~"0ڂ ]XCu^ g&@S%Ŗ kxK?u,iPT "E/k#9/0bP햐m0vaH׽.:7L`aD@ǫƏ28-FbQ;(!HB,C >+z S?$%T=572A+9! 2Ї⮶5{>$3" :J.M~ o7O{pUN;WZ }yu@ȕVR7HZqh&OQ 1%C2B!B!B!B!B!B!B!B!B!rZ0CWyG IENDB`simutrans-124.5/themes.src/pak192comic/pak192comicxxl/listbox.png000066400000000000000000000011151520675460500246270ustar00rootroot00000000000000PNG  IHDRݾPtEXtSoftwareAdobe ImageReadyqe<IDATx!n`ៅCjLb*zwfz@M111݂8ȗq8ME?>! B@B@ ! @@! B@B@ ! B@B@H@! B@= *IENDB`simutrans-124.5/themes.src/pak192comic/pak192comicxxl/posbutton.png000066400000000000000000000006531520675460500252060ustar00rootroot00000000000000PNG  IHDR@{tEXtSoftwareAdobe ImageReadyqe<PLTEgϰ|]A@0"\* )IDATx DQ+E}r&i2] [6ҾB]O~/pPK3CEpP"/P B!BpB`8pB7&tJp@`&Da 9``V/ `3 n܂'!n?` ?b+f+͘ Ǐ*.ێ;0@9?s 䎔~WJ)RJ)RJ)RJ){ 0F 'IENDB`simutrans-124.5/themes.src/pak192comic/pak192comicxxl/roundbutton.png000066400000000000000000000300201520675460500255230ustar00rootroot00000000000000PNG  IHDR@tEXtSoftwareAdobe ImageReadyqe<&iTXtXML:com.adobe.xmp B,IDATxYley߿N6f/}7FJ4#VHH8H` AyqN`,H6EdM4Mf&oίjFc@}~q=NUs__9]VJ,ߋm( yWWSG Ͽ| CNDO,>Jc=mע&JθqF455Ŷm޽{yGmmm<~8֮]=ظqcdʧ#nllhݻݻQ__֭سgO\t)+Ç͛7S]MԍZ[[_X\\|QT>vؑHʧhޞB|'bdt<|rjMNϞ=KQϟ?޵kWjQ$oc8?}R*d^TE7?{/o|s~|\7?-,_rtMME=Rٖxa߿Skg2B.7xEsqR26qxx$liƍb`` 5-1$h/Ҍ;'&'.5 h)͞{b~n>qӈh0--=x i&&&Sg 6D@TX|[ߏ[ݏRY?yxc59to?-W :e:1#uA`wtpooo8p ua3lݺq}iD("{M~$(#m0lْɓ'Ivȗt6 'EO[_^}= --]U݊폔n ||b7m\ZZ.9N|lk͚dS\d-!ِdOvX[S^XOB=b%=L6J VOMQTvllh] %qܼfgWB[[{j$.#Qywcaq)Z:J5O*em|~s~ѵmgܳ/fM0غ}g[Eؽ@tn'OE 14R8ʚؾcW4[b6cع{Ol+sc=~2Zۢ5;7mHu a8thl޲5u8}1*?X;rDLFڶ(Ssk{<+~74en(n)95ĚtOjl-um쓿ׁFGcsNX"! 㦓'BNh - JaN獸8Bپ} G144ǎKHɓ/"9tY5Ic(AEDK~70D|<$r=oQ QEj~gg]j3֭OgruoEdcB i\[[l2l1X? $~P "M{1 &K;\ۜnle/j{bĄ`1F8,!GNz kҐtb81AΙB#H̐7R3S<,(v R.O>G4ª4Ubt?7>}L[ֵK)wouUO$wp"@oU;/^|a\( td""F"l`DShȑ#i?ю?=)m:(5尝vXBP`W\Ie.mGù|q_/dV坯}3ʶ%󽋏*0R}G27:Ixh 762.3e^{-f"(&ԩSI0  8D^ $& RT aD4 EyL}7& I =ztuڡ>Br߈ZIst߈=b xcyzon d{/eeF 71GAЈ|% c8ju9E6!A51Db1DJrqBvZʇh LM:DUL!B1az AeeBZWԅ5=D ~"ȋ2I~DXEesH/%RvpvuuIdVpDk/5P+~ya ׆)$V%G^XB!*cȟczB4|GS~&?aPbĴr^@M @"JDD.#/Ψ;QG/w+ρH\!N΢²Ad4A0 #OW!xb"= QD: BgJ!Y<.MSzyQ5ˆOiV5@D8DGN~T>w܋i/e.)-^);艓qF=/'B$$*!G\^(A ~N&DIi(a ö*Q~E/E:Db 1Kt|~/>nވn##@"%^:L<0S_(ݽ'⃈`jH  J~ 1CtQ`{^þ;Q V^.ڔ"D%"IDHOOqD{?}ٜHp\= λipؼ-5 e(ɋs00Ap(B<3DH єBp י/6G~ p"_"5ԁh#QGyqv "JL͑./fR٦{E>C)=woߊٙh(VwR{z‹ܗ,)-"9L%%A`!B/&$wEMЈ2D%؏Fpٖ$D؆h\ȡNIEfC4Dz(Ma˹P.6ԟX$Rf*@DDDʆS`"""Q(DDDD@"""" HDDDD$"""Q(DDDD@"""" HDDD@"""" HDDDD$"""Q(DDDD@"""" HDDDD$"""QHDDDD$"""Q(DDDD@"""" HDDDD$"""Q(DDDD@"""" Q(DDDD@""""KRy8jZ)(λKe/ix' _I{WeW?U6Ş}++Rן8Z7nDSSSl۶-ݻ7oؿǏcڵѣhii7\LNN|:::X~}X޽;޽nݺ={ĥK9bii)mohh8|pܼy3ՅߔKȯEO>uX^^Nmǎ)mMMM|8}~~>݌6}53#w+{Lc{_խd>y|훯qdScx?W?y=OMM?ckϞ=Kvp_ d#Ǟ_kv>'&&R?2#m0vI#_@lڴ);;;AIϟt{єwOOOjOm?b||`K T6y޽v uww'~lrq죃'|I۷opEL:)ߍg?~bOlZ~m>}-EM Us0339Br`s;v– i/UǦm^8~xx$cic6tF{G{<.\ѯcRصkWaRԱ1mHiotmƆx\xD]{6zJLB B߅_A.c=Nmq8ǒɃ꺱SCñsΨYqh gJ%\RX DH Ё7G󽾾! DoMM%|2 ž"\<! z|" kMe |Π#}pPHcEc쭭-)2~E ?Ցn%C0ұtnn69΃Qf8oë>ܹ-#2 8l Q :Y6xlu2:ƾbSf}~Hرc+++L'O|ArćP:ÇɃrɏK=hnj~3&_R̦{5)޿OzwzSĥ 'b7nFbC14FSٳ3S׮PAOQPܻs;碱6gbH\rg_<}K-P)|{wR<鎾\w?NlNN@ ?Rt x]=*7rIILᓎ0@MM{vn,ݝMb|NjbT:Ѷ+m_XIe}#㠃fhhȓWӶۊU8+=+4Rg:mY^GQn / *i_|?L#հ,~>֟ $5fX0p8qD'86Quc;[n%4qȑi8Bx'0}"CZy]d;Dn9-_r%(IlGq^6lH^^DK&}wusHwƁb]4 J^OC_ rAVG^*AL}h'ݥ JaߡciD_ǥZo~UDBH@df`@P Ae1 XCT}C/QS#f:,e]S Exx6W" #y.䃠{^rRף\<&c(DX:l\p! DV0E4р@&jn]!R9uT3DBTA7B&P ڰ2uA兟"D\bnʑ&#ȏ)0>u?K@ I 8α.9(@0 & k~!JKiR;'ȓQ;(2A:#DGy r+DJ䌈4sg O}"; ,E~4x@𰐙&D/J)"Tp! %ZE#?:L$ ^RwʨsR@Q"hR%a]?~#-e?7^)ݽ'B$Yعsg;"A ~N"ADIaOB)*[yudz q`!zab&?p@dz:|8H@bq7 :sL'B)-8/N$,0Ctkx *!X)|G)+ 16&D "r8j䇸ʏpᄝA͡l,nf 6l2?Ž"o"':DLR7]ADw)uh)9SAeǞ_&iDSCDbNo-  }bc0!>;Dƈ񝼉vؑHʧhޞB|'bdt<|rjMNϞ=KQϟ?޵kWjQ$oc8?}RjW7?{_?{fq-,x20:Ʀ)lKaɰ)31Z~CLLGo__4.//%cGb˖1>>oܸ)R#R@2"xaS8 ybr"BQӀp<ϋޑٻ'SښJjD4Ԁ'tKĨAYιm ߻zȎΘlp?\UY|Bٺuk#SNO ȵ>l =l A r§ri)Rƾɟ{g;m< 76 CCCɟ`蠟 !NN 8mvjj*9炘c"tSw >G4 ~bpt"~|t_{xCP˪Cd$M`|HGL''attt&h_ Fv>'&&^h(8<"!ACA/" \F!ԃrɓ4\Q. i h4B?FO$}n |΋*מt|.n?-_~]lۢCvن-4i8#.霉 vi-Gpҷ`:q=6-ܾh3.V > ΋sx R յk@$V5sM[&;TjR&j}}C R_LVK.-1[ h˦Ƣ^(l}%RC;km][܇崽vM{p )s6?;9җ+4ψ=կ [ۢR._<\Gwm*UQX \91NLFcHt9ӱbT$Q)pI#@p4ȕNt@1>i(G9añPիW uھ}{#/SĨe5^U `)'`\ގΗ2]2E{?tGSz gS`\&bܲ`AH0Edy }E QDڵk)"E,lF0!@(t,CvIp b (ڸ.D}/ ~"+G؏HlD}@Ύu8 ' S&29[{(@0 vCT.+ƒ*%QB\Slm 4!# +9kJY "OFD$DKt uG!8?țȑ?_$Rbp.L9pP쟼玓$cea `qpb(AXy M"!^8S"D)~"p!oҰ: Ba'uӴDȟzdq/aN~T>w܋i/eOz ΁>N|+{8[2eh qyŗkPO""z?^"j$5a Bz"m䇀"cǎ%q`!z{MPR"RCZ h]!8nd;u̙TO")bH̍`ud()-͹q8Ύ2#(`5@D = yB )j䇸O-)? ((40a[~wD !EHA,!`/oŇԍ|<m$bH\F~ѕfw+o4H  J~ 1CtQ`{^>c1Q V^.ڔ"D%kO]!fw(OQBD|ȗmn( [V"RF( /ld:RdY!pe)"ڟ|oߜ !H)FDDDD$"""Q(DDDD@"""" HDDDD$"""Q(DDDD@"""" (DDDD@"""" HDDDD$"""Q(DDDD@"""" HDDDD$"""HDDDD$"""Q(DDDD@"""" HDDDD$"""Q(DDDD@"""Q(DDDD^D2ug܌IENDB`simutrans-124.5/themes.src/pak192comic/pak192comicxxl/scrollbar.png000066400000000000000000000365571520675460500251500ustar00rootroot00000000000000PNG  IHDRJY0tEXtSoftwareAdobe ImageReadyqe<=IDATxyyy 93αWdI(QP5\iEkhm_)R h\ JAaYVZs gx/fډR`E/9ދX'J%0|HO|?u 1j{Ϣ^g6Z]6q=DnqDvB.; Y;nRQZVRad2EA_(!TVu:]/tnc16`0`zff&b#T*Nl~ ϯ$-wW9L֨׌H%\.=''*|>\~|{>Jf ?s(~U(NXJABAuْN %a۰fK.[Qѐh4+++ɉ@`kk ! mT*Jj_-?2H>@|7>Ɲc~ϼ25=3Ύnwvv10ʟZ1 DX] BڏRCcCa?= yv-ɠCT Z #zt('>we2H6蔽=|d*)%rDuvN_GP]b0~?'/^?~&Tuϟq<؏?WWW?T^~eDnd?M\: X D?ƚA{<'ctZjt:s{!JH$ϳ0cҗ$޹sV dگT*0 ܉i8~y2Pxwww?J(ɶ j^d3 `~فE1쏰_r"_џ[vZ$P(H$?#/Byx?E?HO{'S˃ T(|>CCjHԫRG?Herh4@FM&F?T^Md/!cN ET46M}Tz)~Sk4Mzۀrڏ;-l&hB'l/Q4-1lL&/+*hiD cђw`?R~:?~PuttynnN3"G/ĸ1P 3nA#vn{ $ R9YBv;F_g .?ZF?4 Q<@q?{C޻wKONNհ[[["<`3khe.Gz=-JRXĜZEFaA!cMRj?`MqT2X ?`?~x:l6s\(Bę  ֭[C3"BLS?P `og^5}ݎ&rj^h!BCx[B$eIhviէMkP qb>a@׏[$ `?,جVCa$S)N9a= _0ڜV-=NP( ϕtV+R<[ G!Z%mF4?}/^2م%䵱ۿa9CB'ϵq!8؏EcQӳ/Z9s+|_q l6 /iڃ0jN+.j2MFXBr'b\ z>Jr"^DRivt2Nu8^ݕXmV;eҳK^wfa؅0zFCb&vʷ+?7M@T`ݹ~ҫ9ڊ%|d2zpZAl6 AE(6T*Սcj4zIP@bn鴺|6ݬWDheU!w~_yNkplN&CI]4]XX0 }OT??1Y*Ā@2 GD%ɵ}1 5! B!0Ba @! B!&B!LB!0Ba @! B!&B!LB!0Ba @! B!&B!L@! B!&B!LB!0Ba @! B!&B!LB!0Ba @!0xRN\>Pr~~DǷ&~ЍϢ~0zfYeSV\.W8|t:v` Y,N rtqq:]DBPxx/l6k4 łVm5`V[,k6XA&jbJ\Pl6o㉸T"U|n?+~g۝_uu:icTJl2͆hJSQ"ryh445vhV`^]YTeZa>L&BijWT*FPvrf2Vupm J[-J'34]Nl z'?`>>jw &m^ϭM~ӑJ&Ʃi?TdX &L.Sjtz~v^8dgz]o0ԛmBPM[ΙzU|)H]+59TjwfsŚN6nj_ TR(|d&,6;+rڤB)uR gP"N]YNxT*\(Dy8cr,BlxL.W%c6"A"jt|V+Vh4X$0DI.ĀOZ X K1(_`J&3``@ @0+QN7(O]d[P~DJ0&տs.#  W9uh2t9_D8]qX{j_;4b?XlF9 ?nW^f~Cg"G/Pp\&Nj E…\mTuD%&P+ BB1 !-d~?= [\.dJP^( >%`e|B<G{`uۢn766 Wd|T;;; Xyx̧_i*{3č\.{Q-($q! ɐK'671j;cy LTOP"DEQl ,jx j1V?ğ" !0Gu!>diDhA@则?>oȱE`>`pb%!x;ŽjDdDܼ^Ȥg?d@nNށ?՚  ۍ2Br |H Q:99Ъj&&^ۂ&QAXmmmMףCD"/^_ YN!ӐPu2"[x/D(B@'l}gp36!.&&e߾w"et;8j^__04v  uA?{]p ނEXR Ð"$C@8@ zsDpLo߾*> sb`nǧj;w . _RO$ILN1'r| ͨ/Ph4A鉳?@W׮]C) A3#.]B @£hy4J3hU؏ng4& TLczs|X "$ y L4A(!]DDreZr3w (ܰ†=ڡṹAx;&B88㙞m|`n=T9&n௃Pꓸn :==Em!A(a nB`>E`!ohA~X 5s,Em h( L&  ".&QB(S r_WLC# 1P $O/~3WX(C;;~6vIde&RA(4Qa)j(m( 1-q !X (| P/>߂jBE]ŨÒ7n;>\8J' F(@~oa?M#Q7'tbB$h^!!*k7)RzQzQmll@(ߠ1[𖵵5"D62ђTDo1L7oބPPA>o{{W^ŷbڣ\9?V~CF1'A=e\?ds0EPHSxbp'>rx(xJ6QB@R#q(I(?ҢbÌcNأ7p#dN(w͉UpZ uO<"971>|2B" ܠz'N##s t&nPWa+&q@^JA-0Bsl bAYX$Ú{{{ +4t:(tw( Xaf 9d"JKRR+XGxR1?Il_>CƜ z{qu8 = Ao‰5b_8N u=#;X ut7B'Vh`BT݊ m9>%bpA$>1Q28 c5Xo TT8 o@1'택MJRh6J"tB=jn!?l,7PxE-\YeB,ބGut>n~~!E8'@{4/W0B":pOP<GՆ*Ad`.?a^ PYwzF"?d hsD⇧bv^Ktg,CHsq zċ{-KD3xW sVl6 ~5jE\Xs T*zq]±E8 ᇟ 9ĩ_Bƃ;usD_bňck:>@*+rY'r:])>‡d*LK$w"+AOp֧Q5k͎$NnwR՗z6x+8c8F*㉸_Y-8pdup~i8&O8urQ?Xϫ5 NxpOaM~ᾳL& r8^Sk %k+I||ޓЌ=MVYBcdl0l6rq𛎃G)!b| FD U(?Q|AlV aZ?z6BCGk4jTpfEVA%!j63z|iQ)υМv7~yB'?s\j%e\!AbӪBffngw{5i</Bz\oݺ:ivL ; {899AVNO|t2j j*lixvn4r3`ɩCf1% Y.W]k[:.M7D"OwOt텞@zɅCi8{^z!q8dr~a\._t9rr2z]矛?>ϭωć`߮+J R)N[(z=4 1)R ah4l+KZѐ ]zussrA [[[0b@JCx>"Xe;Ip/~ɨ3Ύo_^^CwA355U4"DX]n596Յn8vvvNÏw\~|Mf. Wbקe`j hZ`?Ө4;<`?1bMD+?A ?OPqzJ9}mg};Qλ7ޖkL5jjw%ES"΄:B&>uydRl,){3{<r왫TR4Kr9$ <_]]Ew~D^oO|Do>3"?i+x4N6>睛H >d6rF0D|b(N}k8::BlBtıKPZMՈzon{c/dR `Bk%s*k,O.;K`wD_#c:L OB vjg Bv{yyl*JZ#( ڏ +Qj?z/dg$E?Hи u_\upzzpy0 z4t)Lp6zj2lzz{1WRj2"QhDpa5FM&-;1ΏDX٠E3"/ʣ~ϣ(&@ߏXs"Z-& H$rڵ aVeozA/m@9P(FNS t:3(<o8/-Ռq?uƝo $Dp*Jł%;f?@?QBlvpq2h`Ta|E?PχCAu)};O~Ds~?-?0Ŀ Cd??1Dz1ͻw:xĩ!zSSS^ZER*rA;B^'`PAd?Vrbix烑w2ڹ"W sxx/~aPH_zVi4F(У = =%0hoo@p J7d2 1lk6G`h~j?ʽz] oH$"~9s/!cN2L~pe ^qhZcBI0L"nTUjҢ(?~Tܐ8oG3f~qCz~! qM>?{*\i6[\ԣb1؏O$?B&DbfffZ8˗P8o8Șjx֛;D8nTq[!u#ٯ3Dr/V?,cp W>Iħ~I&:[[["<`L^bh9d#ys6\C$@*zTR$C(Fqa?bSXt:* ÜA955  ŭ[g$E?#/!cN{;jE=x|?+P7Drdi4ۿ~i=:xX,=EQtDC98ط5.gMwo}Ydwzzh%N=Ș<ĵ:q!8_TPs%x &xBjJ `i2q~/^2م%䵱ۿX(`9CB'OTFq:؜5JEzzZ}}s,JA&8s/6!lgV8}63fOKcV6jZL=D+KÄbJ˃ EhdEFIӰr{rrEGo;D*"Z SNEH6-N h,\ z4ʅ &i[ ,ͦr&FNIJj\ܗ&PTQ'HerF'=WL&`:;ڿ}MDkw, 74(0& b<*tx4|mEnQf"WZk"PaP:I^xxxx5!r}aa{H%\FlBX]xi8k.?|!:]?BsZ&?CPV2zl6[.[Ngz{!;?/:3Dˆ&x[ڮe2á`Z'j (| U&>uydRl}\L"cb%vy P!WAXE D𺹹$WlTxx#˞L%E>x /㙞mOz\4T޽{EiTZYY{nC绻 Z9xB!7#O/0>8cIObh4g0x˅FAEri۵ZmTQ*n@݅)P# 2F+ =F~aah4zB2LR*h4qL&0rȘDsU(@pt7ϹBp8Z$=??)Tʨ!F=ޓ^}rƿ H2t':zn^Z7KKKP/fb5AOp@=FE l%SYt(քb~$x*J"\xbiT=`xJ%hx11#Qd2Z2f `zzzKa -TnGрu8zO8< 4~2sd LC.<=M&O곳3F+QIGxƨAvgvn.ɢ.+ v=f^=fBې" %#J<"w&&hRAèζxw Z|>/hKOOOnXժ=g*e611j\. P,J6cx `L`&4\DR,t:@ƌ`8 7p~T d2(PL&{X,[&:zQQ^u|47h̥2(m$Ai6??mo4g/4j́5 G$?Q'PL*M¯A'vż^K}eAMIv;GkC11cShѡj+W@ϔooocpG>o[ Oz?#OQG2=>l42?mp[*8Ts^TUEX-Ljr#NTYZu@ 4nP_ۃlBreDR`6" .2TQ*S)d$C@im;:~Ʌu^XXz/\pttb~׭VI\.?t:\6ǡc c] T E{|Fzvww񺱱a41l; pQj* C@DvzzIł:=;l6 :HFYD^3͒$O<@T:V cLg2 nCbX*:#^/ZxՊN"bQG*b13(6sHTxbx|@ NC+ 䍲N&sF7?=&2f@LPDN'dJmmm Z5L|"@ۛH*wEE> HFG ip8 ICː1ZxthѶt[vBIgF a 0|(I@jҥK(n7 yi@i9z$=FiY9zO8< 4~*eZjG"l6ǯ]CVU&a>yݞ9͵-4ZM:`>9zLd-h6Вw\.'<[XDWTP;99ywX"Z/?XT?.hZ(VVVF p: rj. Zp8cG͖^d@J'$3x@z=+\\nvv޽{t^Aq#Vk$'?wu=JۃbR^pxx $It m6C.{G>I'#,GVtRvCw\2`0 LUa+ڂSPّ's$O iLd@):NRrkK IgYTphޑ6T*@$b?&IqȘnQA@ ?9GZԋzC^+4@h!lՊcM#f H&BG 'ǏZFe RTBE ay՟kkkMG yVk#O!_U8qvddYTm-9*HP( %dh:4 CXP([- L$fTQkԼ?(ӄoJ+++P^?;;Ї>T.lFqT*u:9~ѿ 4W>ᠮo;J›q&͛B!i4@Ν;8J9TpPAJ7J*c c ~ԩ[[[FC8\>롣2^b4-):< L$&QR?djqqC@bCEt+Ń Y0Fl)v$0hfBȸ1 hdss޽{(Ъooofh-<ԋitp8#+ ?AJ岣OP4ȸAZݿcV d2TmܧP(8zDTx<ޅHWK 7B^MDb@t:A/^Dtn߾-\!p2lx''jn3 pȘxtt433jSvP2FQj .@ψxGx ( ~15*T=&2f{vgps\ggg5 |*P(`M(8z^dQt F3,f yxwwWY^C" ={^t& #mp0=HOOOcXZM&իWa,E}"@'>ThJv斖Bh͛7|> s:y |TQUK< @ƍpp^Uzպq*G/_.7dldreT %ÓGr-hҽ^dppӏX.`٭Rn6[8zDnc$љl8z+8tZtDX+r9tGGGZ6 b[n hp JjzT&G 0D;1UIENDB`simutrans-124.5/themes.src/pak192comic/pak192comicxxl/theme.dat000066400000000000000000000120711520675460500242340ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: prissi, modified by Leartin # # All image name must starts with "> " to have them unzoomeable # # This theme has the scalable standard look for pak 192.comic # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will used the unporessed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # unpressed Image[0]=> roundbutton.0.0 Image[1]=> roundbutton.0.2 Image[2]=> roundbutton.0.1 Image[3]=> roundbutton.0.3 Image[4]=> roundbutton.0.5 Image[5]=> roundbutton.0.4 Image[6]=> roundbutton.0.6 Image[7]=> roundbutton.0.8 Image[8]=> roundbutton.0.7 # pressed Image[9]=> roundbutton.1.0 Image[10]=> roundbutton.1.2 Image[11]=> roundbutton.1.1 Image[12]=> roundbutton.1.3 Image[13]=> roundbutton.1.5 Image[14]=> roundbutton.1.4 Image[15]=> roundbutton.1.6 Image[16]=> roundbutton.1.8 Image[17]=> roundbutton.1.7 # disabled Image[18]=> roundbutton.2.0 Image[19]=> roundbutton.2.2 Image[20]=> roundbutton.2.1 Image[21]=> roundbutton.2.3 Image[22]=> roundbutton.2.5 Image[23]=> roundbutton.2.4 Image[24]=> roundbutton.2.6 Image[25]=> roundbutton.2.8 Image[26]=> roundbutton.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> scrollbar.0.0 Image[1]=> scrollbar.0.1 Image[2]=> scrollbar.0.2 Image[3]=> scrollbar.0.3 Image[4]=> scrollbar.0.4 Image[5]=> scrollbar.0.5 # scrollbar back left center right Image[6]=> scrollbar.0.6 Image[7]=> scrollbar.1.6 Image[8]=> scrollbar.0.7 # scrollbar knob left center right Image[9]=> scrollbar.1.0 Image[10]=> scrollbar.1.2 Image[11]=> scrollbar.1.1 # back up and down Image[12]=> scrollbar.2.0 Image[13]=> scrollbar.2.2 Image[14]=> scrollbar.2.1 # knob up and down Image[15]=> scrollbar.2.3 Image[16]=> scrollbar.2.5 Image[17]=> scrollbar.2.4 # scrollbar back up center down Image[18]=> scrollbar.2.6 Image[19]=> scrollbar.3.6 Image[20]=> scrollbar.2.7 # scrollbar knob up center down Image[21]=> scrollbar.3.0 Image[22]=> scrollbar.3.2 Image[23]=> scrollbar.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7 # goto pos Image[8]=> gadget.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- --simutrans-124.5/themes.src/pak64german/000077500000000000000000000000001520675460500177565ustar00rootroot00000000000000simutrans-124.5/themes.src/pak64german/files/000077500000000000000000000000001520675460500210605ustar00rootroot00000000000000simutrans-124.5/themes.src/pak64german/files/back.png000066400000000000000000000070031520675460500224660ustar00rootroot00000000000000PNG  IHDRݾP IDATxoyyfDr%ڔcl6l,ڠ-/( 7/P`fmZl(rp2(b2߳ A>oBޕ\ڿQ DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D"" 齴wqPh5[AXz)|/;wOwN:AFjXaz[VJ% aYՋ/d>lFCa5.&O߿N!_@N֪[erL>_./[Ë_ttݙ;9ij:Y.a4M#ͣ峋WK٨[V \P]/rx(aT*k[xt26 $p!ƱRyoZ!+أe5&ԒiBDz#!C8 Z3.?:;L&lsJ0UNJEgꔴ( < c Ӽ~2"O{(/P}\*E:SѬQ,m㑦iiyzάRi٠VQTk5Ǚbh\ ^?@h8D~0ly.͛|.7(M\VBd0o8ǯK4qBt=>N~}Q^ ATq^ rYoN%ɝ:kD4UsZ]z1k*(@r)W4ȁٴutQ맽R( Z+ZͲ,%' =*T]b E>Äz3}_ǶGD\=ݛZGzA맽RsuXze܃M>t8c U^Gn}Y4(A ~ś;w:#X ^yg<|V{!w]/(/ieg%yXl(%xf}0|S5hNٽtmup!V{~0;-JIIQ^ t':~j[LJb RM%" 2J^)cLf{ˋB.y< |CV맽RGǯ쵏3]1B_ Lh/_ +Y~s߼|Eה|/_맽Rhw5z}٢)1FTBߍNӋ'QPPeP\ozVB S0c0!SYv맽RDfmt).nӹ{{zH+)qiW0}/|]L|6K ̮$(/b1iޕM}|i S5 Dşf"tg}ߕߞG\Fڭi•*Ƶ"fAѽx:<]Eڝ;n5~/s ~O0Wk5D/WU -O{(/P2W/-9ھt]4h`8wK7/~zvv4M*5 h%~'E}0NW/УJ%/Vj5}TFpp۹sߝ:˷O_~!Kڞi%EJX맽R(pǍjutgS]`}) wzZ^lTM_`Z'~'EyP;ۢjAxszsJnoofLC}w>_}1|>ugnu?Ȉ-fy*?) >9qST gF0?KxdWMP(Lx0ƫ[Ӳ^? z߾2@ƃv3{|vo:'˧O>wƓ* ba\YN%mYofӶGJ9O{(\U^#U*৚^~y~{lu6R|'W#z\N!Ԇi,~3tIIQ^ VZT//UkUD4~5Ҩ7ZF;߂~:n sVv7繙lt&(/ۏǗϟO*5+ D&CѩVzWzٿmܚ r;gG%j}4ZF.Fn}OOzOYAer9"78=u&(6 *v/O1 U3vNѣ55*I|7tԂ=?)7цٸt1;3}??wc727jFՈoN]]m_OaͤO{(p/7`ᇾ|tQ>|tQ^ . Eyx>P(/J@颼@<(]|tQ^ . Eyx>P(/J@颼@<(]|tQ^ . Eyx>P/J@颼@<(]|tQ<(]@<(](REyx>P(/Jh.k;?s?ou'G%_=v@F ""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP ""&qIENDB`simutrans-124.5/themes.src/pak64german/files/button.png000066400000000000000000000040651520675460500231060ustar00rootroot00000000000000PNG  IHDR@[*gAMA a pHYsodIDATx^AI>+EE'tF1jf C x̴[v+{>/jF;{ݻwo^z|gg׌?[hg~?=>}<%X=+8νjV3% ?Ǐwv7<1;}B(@90ο;?qUBT=8f3TgQ˼^5;`ٛ%\_h!;`8Nsq1d[3/D A@ @"D A@ @"D A@ @"D A@ @"D A@ @"D A@ @z2A.o{xIENDB`simutrans-124.5/themes.src/pak64german/files/compass-iso.png000066400000000000000000000076031520675460500240310ustar00rootroot00000000000000PNG  IHDR@] pHYsod5IDATx][s_BeoL!- P˩8N" rp\"a$@(` aq }IەĘ?tkW3=3==!~;sYaCzzy-K 5VJ~pf6Ѕ //='HmXd(v4tS\ώ:1d]]6Uc b$eOψfq#׮D}ӆE=8'MK^N<1-L@0W-/Ű3WQ%Y+ʞϦ ^o-GM7t .-aIA{E1+Q~QN-c5NFzk!NqDItzMAe"NfY#ν'L>MPt*-`R;'@o w~ &J!8vqAh}x?"qKHt )OU񗕐O!;z`^*dP\IJ/JWfQ?J7;n*~{) jispA~9ٺxRrEߤVv%d4iRCk6ٝM8y~[Ninl+/Ɵ2~Ͽl ~)48xyo ѲN=$9EJp|H2~զUX_9o$ ;Xl jZF,e:2RA”yf3@۝.y 82=. 6a"XUڕEC~_wE&PI¯$N t]'{/ Al?t]|Xt}u' mS;K( ʜ@y*m3h;Hگ4\Vkԗk~FG.ΓK6IlT6~L^?mBK8s d? Sd[p Ʊ-;+Az$lQ%}ҧ9m#:ݲ ݗHٻϛ$%r\B~Ē~viaCH*+xEocjJ";:^M8*lj?!̬ *N2cQ28 Ա4}h$MWw)N"cGBXO. qfC>ޥ(ˆ-&E/X7vNF[2qYhE lS`e>)X'$M2gWInfoBeCt8cG? { vFn}sslLg۷Z2%ɷm%Ǣތo0K Xc6LQH5lc9ƫY WA?(諸7 P\ܯ89.X J:~:zLބ f0:VR]D;EzE lOawpˊK!Bhg 0Y +Bi[J:~D;ȊPoeoYщHKc+;Xmv\0ćg@PҔ6DCD&5I{*6B{f? `S1 B_1tq0+ǂԻHyKJ"EdG %oy,E_j!薛| rA+pr@IP&>ȩdOW\-@#\@r@I,P@P$ᇳvN[A? Js/OWe %iW'Hk'BzR%7! rKޫ47,h`QD!FSyqk %^FF kûJ]_8h6J4 ?QЖ nb4vsh4kYs\ , @o6 q`%# +*0ؘK`V ~C)1" #-zr0 A1*Ș0j?F<Lj$vh?_5,.'`I|]bn K:-.;P{&I%He(/'0+W:-u@_ 37c?TAȐπ9"Zkje4EytMBoäB@萪_i\L_¿д/i_MTP}/jIaN_EE% :Z꧚`VD:ݹ(6E'sZW:+ty$p-SR Nn 3i_clgH.ƏW~#."ąo[Sgƨ ?gLy3 j YU; nl,\E|twNb{ 4"3d޳NƏ{V dtQW*Xd6V BH!;KgƎ/;K7 ,a`e5W0~,2aQWyl1aMئBL*yE_ Dʿi@ݣ4̮Q{ xQVmT*+j+^igVS^_=Ƭ_bpv++E~Ă_ T?~z :_11yc|M^t H'~?l2+3_r<y=w+>L9T_<ԅu ,w"Ek0\``E{*(Pů!~=c'I_TWAeE+Ҍ KKqұ"`q_ X=k4傤M{/L& @U^wFZ֟n ;:׍}o ྗsf_t E{ 'lq0oϫI[$ym%s>51@ ֳ(ɳMi~QiYYʦ4և(43S̬dN-2Z;9j=˻ j)̩Z_! ̩Zg}x `R-iu>JRuPZ[>lHͩZg}ؐSkm!5j. ʩ--roK˩S=ij+ʩ--roK˩rj+\HDikN9R=QښSTqg#U9HDikN9R=QښST}O#U M FQ[N_2ɿEm9er4jdP˪R3PHEUn(ԢL7RjQUj )*5 ZTBJ-JtC!UfRmI5?84H5?84H5?84H5?84H5?84hޓɟ<5ny>}/  Bװ[ޗplϭ}d @?nwJBpV'wv 큏2r#`܆?oס"Od}Ax KK!:KZ|mPyJK++{Rk5G=Y m>'U-Zqab!~k Mal `S06) Mal `S06) Mal `S06) Mal `S0a"U`IENDB`simutrans-124.5/themes.src/pak64german/files/display_factory_label.png000066400000000000000000000177701520675460500261350ustar00rootroot00000000000000PNG  IHDRfzTXtRaw profile type exifxW[6 )r$8|V9~읱=YnUGA4+!Z.9 Wq~>ґi>ۿSc9Ǘ5*;VNvޘ{}5l=c5e:_mqSO`BkTעD*-{bXq9IʅF2+0r߰w_h/[jV&#u~ n= QǍЃzDoq @Ke[UFf[LuZqmoD JButˊi\֚,V춯Q}&IW XHV٣vuTne`q߲ FhNkkj!/l26OyXzPLc ;^m+AM8ɳihXxNdȂ 4SAцms&X4#Yا&ֆ)rq 0 @;}D4@BHuU͕jJ58* 3v)O%pΔ7H41݇K ͋ `'bBx (.#BNMti$am=Eȋt~C+Wf,”1JG@bhWۜ(֙Zh[φ'/TC,g.#Vpσ6@1w)`@#`"؈NEx|Y^'S#;O:?55UP @M &g2r]^g"w$'dNB1!?V%614m5;9]YoGկ?7C F $6SiCCPICC profilex};HP*vqP,B EjVLn& IZpXupqU .N.RIE'\{. JLMd<&3b+B}29IJ.ʳ5k1 2ô77m>qdxԤ?r]segTr8L,[XiaV05)∪/=V9oqJ8'a(,sX$PPA%؈_'Bc>A/K!W (Cl䄗/1 t|;N>Wz_3WZ.\Olʮ%r=S׼5qhVSϽ;[oOc~?^rGّ2iTXtXML:com.adobe.xmp c74bKGD}?[ pHYs  tIME ;$WIDATxסjaošB]db#cjG#j"&2"WG*B znw { a?~c&5 eM]n6dѷ% `ѷ@/CEw ]68N%o.@ڈƸs.<֑#yv;F?)7d%ǁ+@i y&嬦~ X) V?$Q٢sq";9zk [8EMסnG-k·$tA;PB۩P\ hS){iľLҊʅЌ7/-G{7΃05W,)+ l8r^bu Uo2H`I {@3& \HE"f`PCcT"PSl@D11g`cѷ=5JDw@?ΰ0B`$M’D$I 5SʚfU5-Z-X65bpRrbZgjmqKMBMjԥzucFu҄*4efuAt*v}5P)oWFܝcbH!BG0J):rQX"l#Ӥ(n؅x"nAnE> Q#捐[ `XUȪ'Wl-6DAG&H=$14J{j*KSlQ+?zkL`عW#gVG+g[i+FN:<'*?,|F/3ZwS8ǍpGe4Dky)q5gR|pW42՞V-Om1JMmpk_bu --嵐9'e}sゕ"=)τ@gߟ-DpAkpmc 6'MC*{J<h+gܲ"h-?3eL32G@栶F7 a+tr8B K"M_©(*|V_jIJM&u #>5~ܐۚBPzbD]ɠiO>Eyզܐ~TϴxG1ː8iw@Ќ3a#Ҿ8 70lOtM4>|ZK?#ΤrBvg;>;ZmUr2)lPY HZRdߍo^}b"iCCPICC profilex}=HPOS;8dNDE EjVL^MGbYWWAquqRtK -bxsx>@funD\dWWЃ~eIRuOTw1g9xMAv |Rf>Iз \\4e ٔ])HK3,0p tyskHӬ7!0Zuwwߞ~Trc-2iTXtXML:com.adobe.xmp Kh~bKGD}?[ pHYs  tIME  xIDATxؿm"AwOt{ #uLJ().!Rt =5󿓿IY|uIЧ&5xnfM֋i-7z1K]Ϳ> xpy\]kΏx<.q}OǯC%xg j dh;h6I`1epI&1xOMڶMF7l)Odx {re1ȡOb`Iϖ$|2LvӦl}}$Y/        /) @@@@@@@xEK'C+SiO{O-g?[n.h4j*,~d;$I֋'jwpuSp 8%nr{ c^LPXzo>#B3IENDB`simutrans-124.5/themes.src/pak64german/files/display_station_label.png000066400000000000000000000166761520675460500261530ustar00rootroot00000000000000PNG  IHDRRl zTXtRaw profile type exifx[8EYߏ倯,(ٕtVWuDd"\2WHٛJ-g+ؼpRl<5[~n7<p\,}3z{i{yN=Q' rVGI>FB9s?';7+`6 ' ]svݖwqzgzAy_7ъ>9?oޣrBaX3zeѧgY/$fBo3uХl JVPyלNv2p\A2m_B 3T@wmvZe9:g3V39f}5?ޟ_kQi_S~0A8@&W2 K'q@p}q@VL!.%?12PI,#ŝ>y.Cf BlZ1?%VrHRH1SI5$9Sιd%E)DSRɥZZjZjJ-@VZm00@zKuψ#<ʨ ~ <ˬMYnJ+*dj;wy]wD7Ps7j 5)$ "ċB@B{VW3۠m QEofJ|UB}jSN; !B Ts^ъH"i͞WT[4QC[ŭ9MOVȲCsJMVƶk]F@u: _KKjUx2 (-5՛s:NVCBI-,ܾVr^u%Ӣ^΢f`n׊8!Θ2QCɽ\Ȫ/@{玵+x@ Ҏaipcwod*OLe.y\'IӲػD?^/ֺ$++[Kjr'3gM.:/m"7C\Oj^ cA4NJ!*SFZq3+y=z/]ю0!mjq*~`7fqrv>X{c/^?}q^<@G}N7^| 1l)'Z/8!)zH: a>q汞VV Sy ,m۰ERhf[HS߬'] DPǡdTk*,6|#&kǃ@Tl&–zR7j NAj4X'T& zwIhQX_*'0^V }ꜩfoN `lM*H#o9czo`.k̆j;9(3fegyw$_#0tڷ!TȈoR@&<aeij蹘gGA~L seMaI=P]( 9JQӗM97?V 'RTLXq;Tlx[[,#!0JO3Ъʐ"G?KڭɌ, 5 R3R5illKM[!(Jw'i~nMEꬮ.e Dck鱿[%&j}赤.\O}\oX·/vȄz@(gE UmGG@eDrGg~DN7&fq3IջiCCPICC profilex}=HPOSEv鐡:Yq*BZu0y41$).kŪ "%ޗZx>λ}Ш2463˯WFdfo}S7]gYjb@@$aiOm}(+*9I$~όqX,uljēqU)_yrUkuOHA_^:RX"$PPCUHЮb!CIȥFyl@lĸI/1 vfqy+hӟZ.ۚ\Olʮ%}SzVqdiV)QϻÝs5ruiTXtXML:com.adobe.xmp @bKGD@-R/ pHYs  tIMEL|IDATxձMAE7ւ("'t*aXD~焓s5#_#]f$/jwo׶=! `g$e?x}M8.3w&jf@ tYk@ E$ٶvw@ @ @ @ @ @ @ Xd4?FYTn7.z%7aM$IENDB`simutrans-124.5/themes.src/pak64german/files/display_text_label.png000066400000000000000000000154001520675460500254360ustar00rootroot00000000000000PNG  IHDRRlzTXtRaw profile type exifxY8E^8\LjA-(ytiK&).4.^ފ3!JN%W(M>W{5qsp yӵ6nK.> ʗ$^'J7AF^5r܌KPywNK@=:RlBu,藗-.w7"޹?R_;8 G/|[ OWAorߏw7'qcl?u^yy_?kXQC25aBn CDD~ޙ6ƻb_6a]vk 7u`ď]N|g@]-G7{òYYTw_slPe bԸ|{WQ)Rpl;EhL7О.V%uDAԬ6Ckqdꎜi `ct%]>Mv5K]t !3>a*` cLQb%SH1$II`$J,Ej9Ssɵ!XRK)gErʂZkMKMZnNcO]zHCFei'4Ì3MyYYaŕʪw.X5{6RP1*raNb`T N1; )rQ?JFlXE ô..{θQEHx.rFEjr7BÓ}<}^z,VL1c.i3Uɨ^R^& ښ1ۚG,FJRa+I*,u4ֵ+[N0XMrWC~-.ԼդMxS'#Zs\ÜKJgSjݠ #1Wxi[>(~Zr荝ZOKN;<@i@Y;lZz,>wD`Lr< FNZ6q8m9~[(|2C U#zi3J2Ѷ& QlgN&gߒIeej}+vIOIÙ\Ã8f64Fv}T K} uF3O>@.N*sh̠b_i~~߮&kIg0 ]ȟVп6ax/Cnh!7_m+#ؑ?t{F-~O$s؆I1MgtŜ?Rzdvu<"]6vv ΗZiiktYyجOEh):b{ISΚ;͞|?mh޶uiSqjAS:V!L '_II$rrPq^,*pʎ.*PF#t΍pA;ZVMByzih9:{??AT5_FBiX`zTXtRaw profile type iptcx=I 0 g FHbuH?`a*-[>wٶ4t&#F'bG%CzJϩNhn9n0ݜ,9iCCPICC profilex}=HPOӊ"-qP,8jP! :4$).kŪ "%ޗZx>λ}Ш0 ndBVWB1 2$)n8"jb@@$eioOo}(+*9I$~ό!/K!W Bl䄗N]/1tͺ|;N>Wz_m3Z.ۚ\Olʮ% }Sz׼qdhV-Rϻ{:oOk~?1r{iTXtXML:com.adobe.xmp IzbKGD`I% pHYs  tIME 9 ȊIDATx֡QQ᳙54 p#:% PTAX\wC9tC$Z^襁}<{/\]PvdzP8ϐdlwtn,&jf@ tk E$}4ώ/@V{ͳ@ @ @ @ @ @ 1I.ׇת 8]ӾvͳrC&Ϡzp~w .hIENDB`simutrans-124.5/themes.src/pak64german/files/divider.png000066400000000000000000000002761520675460500232210ustar00rootroot00000000000000PNG  IHDR@@% IDATxA 0@[2DDžUpǟsk6luj@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐm=IENDB`simutrans-124.5/themes.src/pak64german/files/editfield.png000066400000000000000000000011721520675460500235200ustar00rootroot00000000000000PNG  IHDRݾPAIDATxIAQWĵ-!"a=zK Wk' U31se=aZo/'y?r۶M@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD|qq?^q?7HD" D@$"HD" D@$"HDܶm&"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"H~v?zB5 'D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD * ;"0 IENDB`simutrans-124.5/themes.src/pak64german/files/gadget.png000066400000000000000000000027151520675460500230260ustar00rootroot00000000000000PNG  IHDR@@}g_HgAMA a pHYsodoIDATx^/c[qD2A]&.$a(ƥ-{"^/$͜pZLy9֯|nɳW1e>>@x;Q\^nxQ\/[{r=? S(>Eqҙ:>>d2ggg###\Ls|M{4˟wd~^{9(ׄwxoç烃ڎbN&΢h-N䎒_pghkaa^͕&1<> GdJ˭יOãD"f B lollXvem5تÿ,Tc^0^+ff[cdj) ;,2)h}}Unooޒ){2 HC;<55xh~+`7TX9U`Z5}cxGOfw#i;UUU\nww.(?SPmM^ooSzxx?yOssssww&3t NpC{;>Lʩnxfo:Zoi,%Rko6ւ64 :==}{{Z_Dbbb)(٬_&C\\\.0=:XySH7t͸W WcFyySRx-]lnww|Ȕvm%_Wnas⯰(b<_\\4W2kc4|EEr:5w \]]ېj+Һrw abq‡:p=^sjj5:WY(V-\QUUUڎbCCC;ۖU(s__zxMƴDin5ƿ}?hUޏ|iA[[qkCCGcGJ MMSQrozOU+0CK&|nvέki;B eݽd؝|ӗ¿_pg\Z o?~77{>=a!(C3uGoW> G_>wˋq}L&Q`(0I$ `0DL&Q`(0I$ `0DL&Q`(0I$ `0DL&Q`(0I$ `0DL&Q`(0I$ `0DL&Q`(0I$ `0?~CeIENDB`simutrans-124.5/themes.src/pak64german/files/gadget_new.png000066400000000000000000000022231520675460500236710ustar00rootroot00000000000000PNG  IHDR@@}g_HsRGBgAMA a pHYsod(IDATx^ڽGPC[9PJB` 2HCa(Ш᰷U<|=y~v ౾Ry;@k8owKeSyswefs{Z`w..G|zse On/g>F<;{P&v;gơ>"a>wy7벇2Р'`mݑ eNgq/iOw`<1 EoBw*'6_QG}XWE9W*z_u ǿ)), gb/'vV— iGNX݈uEԼhOe׵+3Zy/Ky,D|2/!sMH9'f&r]efCvJ*n.?GKZ`KdͧIW),!.ÏȟQ'^ b ֡rbC{suMͺ—{=*G9S{_%5e]+.Fե8ZNy}zr2ꈴn{_*Ӟxv# J{nŠ?S9{t1`J+LP`\ל8gI0`$H #)0FR`I0`$H #)0FR`I0`$H #)0FR`I0`$H #)0FR`I0`$H #)0FR`I07ϝo{IENDB`simutrans-124.5/themes.src/pak64german/files/listbox.png000066400000000000000000000013031520675460500232470ustar00rootroot00000000000000PNG  IHDRݾPgAMA a pHYsKeIDATx^ݱmAE? e*,&jPή7w>ϟqCn:D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"H^gϷ>~"eZ68G@h-{#ecslq=в9Z68G@h-{#ecslq=в9Z68G@h<×D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"yzn.9Fga$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" |+AwIENDB`simutrans-124.5/themes.src/pak64german/files/ls-symbols.png000066400000000000000000001041271520675460500236770ustar00rootroot00000000000000PNG  IHDR@@TgAMA a pHYsodIDATx^\T޿}V{I^*4EiV+{%4Ƽ?ɹJQve=;;g̞9, ? AmxhH\b|BfJratRQHl~Hl(#soU)IMMņ#v`!c"ApD֢%s7NkvnjmԺ|׊G?uM;OOm)7@`C_%kW⤎n*͊.+ش`ٜӝsްp *×D6WGR_iIc.H:ʌҭ[*݊L[׮3BoFԱvّeyqu{5Kv5E *:7t(txv܌C=1D`$:`RM[eΥk:r}ʺSf&Nְ̫jNykZg-׶Ǯ>paC W-+DiadkOtXژ0!͡yv⪖ֺ Dž;͝<|Aڰճom{hMEToZp噽ǷTMr*3.th_3:ǵ hcf:͚]:}rsnmN&K#)#IuXgN~~~:o/VwPp"0Urӱi۵b۹sg\}I۲5WZb̺3UTmmlTeFuSMm뼖sl߹mu~q$ҽx,Y@wёveanKF.e%ա;VVjߑ֑W8?aT.}iv}ps{'\96s+MN*6T5%||)9nUylg /TY^f?9Vca%u`)**ټS}|ʍ{.^;zW7rtny`UsՔia9 kJ˗X;}fٶ-- *'N,1|y+W\lۖg-xBWu7V6Uꀺ[3sjKCGk}bǖup5nϹ!݋Olξvl=kf^;:+ .s$kFИR=?׵*av 3 =K3kK* =}t}|,4A" qRgl\RK= ݶ~#gN=x[':-oknniYg;VW!)xzIm3.n:uۢykm12&%%)avl;4C~թ?_ؤO?;=U֫fܽ0XzuQk=[ψ\vߦ-K ޭW7:fNڷqm[ʘ،ѩyYSS2{&`G "'%e`:kjLq f.qdDe_^\[K~6=ۃE2on[*G|{Ewl>Lm#L:Vr9-~, 'fF07\ň/c`D`@&tUF峷ܢ+"0»`-:uAˁ .\vmu˅UsN.R;ȜGg~NV7u9) 㒧ggO/(2Sa+.ػ|J턒̱SvB=kؤ)qJCc}x%LbPp'4ʄ Kr[f%Mpњ%{+巏~̋ok_|~/z/>{x/_o{'|gV~rcw=Z4#_~4퓾k{wBNN)mS]a*c`!IPC|?&C RlD]hL<@5G6lXx ,7"/u%{Ϯfw?^~U]Ϳ>Z?j~=1)LWGiܯ~ڒ%j"XW2mPIF=>6(蕺vӿH\rd}׏x y}) Z`GN:Wm9~՞n^hrEuFјQa>!n..Ξ<]} Y޲&($3(}tV)s++f3 k̲El͝Mͽ 5L"U}|rGʪzXŦSI7ᱝUB\t}tַoZ=hDC3ok_gʞW;xg7^>=|zœ]?]]i|rͿ}ӑIas֧ߵrp;>_vaf,'{OkfSP~LoIYHQ:u[pzaD`" Z*KrWgc{,ہd-,Ѻ Ӂ)Ң;B` GbÎ/z鋷w>xq{CҲČԈXW3SwKOM [};#Sk3'g+¬¤5gefdNm()6:#$e` Y]p#hCpH0^OmlѴR lmt#0g馟3zf"|5\OV=>^MxŞ/>}9_>ϧ@fſxoe]WS?nHQ rbi. >`̲bϒd˚<8+3PдFD` w؀6r_Hl3M3p^1*XnD-%1 |(X33O;~B'֔N/WP^;rrSYIm񄦉SZ*kZ9 t4t}j.z>mo0 +֬Y%}L="x+-iU캠&r߲ro}y܉~+oMxŘ/Uteo_oξga:d/>~UϟDͿ}$e%Ik{_7懝'urW6uMu%__T ؛S_od<2y?m*CV;j}Kܩ~aj ,s!&D`oHkr{8-࿭a4؆G^2m"0 }ϟNNK!]!c EZ*e*A`;tqOؔʥ3GďV8{IKZVN^n5[fDdeyz99x{zG$O۰n[̬֜Y%FWUׄC#2dȲU5]-sT|~.qB&aB7%N0S|"2S-ð,sdI˸)Gdz6WG-OطjعE^{#_?_{zϮN{颗/^>Ń?gןW^x{?jvg/&m̯S^<')?lTųs?XcKgGkN3=6۔Q  Xa7TU[0M֫1.Ty-mxm/,HA)0ZT4 0i5-z\'* ءL'/KSX6se󪧔6G$wbͩ㒷luӧ>HK͔hN-UN+17__9jd˼ճ m G8{̭ݢ-[}]Wn0ɑ'W~8W{ ΖSgo@_/ye[(}hŋo|ٳGnl(֒Qcr}SĴR'ϑE5nJ˜|O?싪geKR:ldܨW.[5cĉeK~'gOi9쁃wؾ}҄|59yO(am雕9iڴ9M- Zmhhh:s}6~^`c;?K[D%-/KWnR`, Imy"[WQRc L+Ko( 0j߲݋vsfqփ'W>rӋ=j=d?店~y۳}/}'zW~uҗ}_ }S3^|=q򶵎ˉ5imVO;~  wPp4dZA%jnl Z` *\ði_(+P[T=AF߱鿅ET u.=qsq&Z@ϒ~X)K6;\6<%4( :o'{_;~lNMM[⅍sj+<|deœsR3͋T-YPzGƌ̉Za+WL--FgFEgLY8{ gH)y=)yCiy >WǦg'.>-7hgg ϯС C%e` 8ch{iNs=vEڐgYF\2Uӥ>[ۧ~n4<ŷ['f|m;~u?oU9wWOxrv~/}}ܱ_}qu[9juYrlF`^-?;0 wPp4X$-TiD`HQRH`m[7 l_tn~lv.x:Dߠ v8 H B.t+@Rء@aEˢ '9P_dlnfc뚬>䁳|ًLXbC{پsUs.7k+'Mھ-;:~{B8g7<]\_}뿮X6N;5']u9p5%GG4/m崀+qY:3fB@1>F ,mKłIIH$$;Jϖu\5T`hh:J:*um ұ^ }G;,.N)0xD`; bnKHWnnWi{!uA!2w!0)`}fw$%L|Y!@{W]J 4^!>Tr3Ӿ|z~sb~mv鳗rss|DlE6^Fu9V\4wN,)hY}o۳y3ԔN\V;ţrf}ӲI޳[||GrVJ*Nڞ\{KC*O)-65J&W.nvsl|oL+(۴ Ҳr,[ca )QܱIs,m&̸ )W7YW馉7Čgoiħ6x+>~~pm_ucQyګh̺Sn+=P{yqQ|P`mKW^^] )͒tx 4 %%-!A 0;РFBZB%ת ]|r:dH`ˎ J# _Wݫ 6 A`}fw@`Vzp֙+08h:B:hE {ɇur#a}>WOe L7E6-:61=֮H*22u *M(p [~wPgPm>}IEfMZlɗSm{pGWNO\>vlnT\wHP|CcM{cG{K)k8iyKXP3M3lx[XStȈ9U֬>?&;]SС Yu9 [уf{y.obw'tlޯm'/o|wi[Ъhow']h?5o̶ce>ߢ1zae+(h X\َ ( y6y3|cyP 9t|=cƌ~QWW@`#h!^uBa BuawO%X}QUs MG8ӰfBu4tԡ^ǨA({.P*c:-!GKuzpJbJJJH%?(%~L -EY3nֿꢡ1>9 ^`[0yV]w-0t f`E fWw4`yE#Z``6B.@UúJ -ʪ95+vlǮiueeo-coys ! 1>5EՅǤ,k~Ov_5sxhc$p*\en_m YEKu/WRI1,1moCD5V,[6^&Ĝ1P KKbX <( VY1M;^_0F}pKX+m[n驡gF 13Y?iξR`o4}3͟^Qk5-4Iˋ_dגGS2DzXnl5XbVc4K+(*JK+ʱ$d$$%y`>%yc|cw(ku!HZ$0(ybB͢8 J:݀zioB]1o]e$ Z- })O0<`bM+PP+ɚ.iTiƺ}޻ʵ;59p%0:tTqzq+/(زtUƥ vpqt2ѷt7 e;YMu%M2dxRbsYsS`Kpe g?Y| f>tly}s(>zHI%eTd;^,׎˳p\y<_>'BPij > g:k/ ܐ{IK_~2JṶOf&=;s'~TxIg-3nsqڍO'ߝ8KWW]Iu4r{Ӆֺα+73WQ9W[D^LAQEV %͓u{#;pt;2AcGP)ku}~օ_}5B?^ 0 hMԅxCwf9$@u3rl~͕NV`rLFď QiA DOQt.va}ʲf9zyGhy!RCml |]]|'goHM3:qNfG״'DŽyX{y[:-k8qfގaA~qCU H465 3  #F tVʱ ,B톇650&=\I՝3t5Y,}ˁ|"4vL_uS)V©NZڂLX';51`ϳTt4%'|6qc3JpfOx<¬r-)S4|no5Mֆ;S‹4i(+W/6TLRҰJJH(J?+0= jsOdž/nS *׫~ .dxkvMՅxCez"0f?hj#DI`/K/ cUE}pZfOVo!˂L7=9-nE2(?}ZWr#tNrNc:+նStr48MGFZVRQ`Ô:JнV{]EW+{ϻug*(@uؾ0;j SЏ螠BH`R+W`nV`+0WU)SVD(;z$'W&$Mv[O[fg.ݿ}ꡃ뫦Ύ >|IzܜQiQNzA>|<L-Tu) erFVV,Y})i 52Tu.G٣#DM]_9+Y+5-o5"htӵZGkR+0j0sE+EE.׋ 5*r#&k6 fKqjۛ7gw,! W%>̋{b )̜ռڻeSɴ/JaI/jyܾbBNsm&ԛk + u'YVԘ֙D++*(b#% &΍ pR *W!].u5; <Uְ)["1`&n#o`7ҥ&aJ& :FaB=OS)s}ܬԸ;˼J)KijrVy\1Yf.pqXbɼXPS5]M)\ hnRffXjf\na2ZUP)tⰽUUޞh-0!ano@wjhmSo}l8=.^+O@HQ[⢮^4O>[y M^w^@%cv x m]Vˌ T*C/0P} XP * ~XeznN: ÂCb& Lάre`瑬n0%e[*mUmuL]+kάnt%Ikf̚Xfd榮㒞5m$H :TJ֐:F<EP)]<| Qv2&\emo}mV JNƁ~ypPFL^F VlOσx>7S;VY1W;Q_c:\s<[E]z0m}foíXr5nR1Or~W8){p|y%G_z,»l]Ne^Z4G6Iʂjf:j`Y/ [to@SZ| .ES`".+3Rglxݻ, `]yw(T_f &Λ+_ =h*b ޅޠz 18loc[gb2gqE VZ3=##.GrCuH < ˧Mfپ9 Mh_0'/sopEBF5T`SKJ҆rئeV/Z5o|lUM7)92f<5w7 B)iE5t -!"#-dtaf JJ1~/[S›Ԡ-VԚ-nkunuis5hw3^fx~OK.z|0ӑ_]M*vIQwS9n̍쬋;<\VZ/7]mo϶TD?GWRem6$CG'H7 CSmr?[Kvcnp׆}zAc!5Cpӏ|bCꪫ6XM\B"[?fc0x9 Wڈ1(UdaaAҵ2J^a;̘|ƌ֌13EE<{'_r}Ŷycr\#}F%'YXU~dբ_7&Ď:"i|hL.GAS[]Se:THUӝSWYyBMwi ,!OɎEw%)a6T0 pmMmc |* 8@!J*ɑUӒc+*rn|X¹,bPț/U6k;4fl6k}mz[>jذax8%N\yi_O9z 2ӮFquYcmR-oUaO5֟:TsK*yV:j#)j5բZoO`O{WoA鯎N;}l8pܹKi.GS`Nn".<%S] 0Z]Lw[ˣӀBuYk @u' <^xP6Z]hPM љ]z%mKԭ_v߅ 7Gd402r߱i7w޾;^9.yXo@rff={?xԞ͟}+wn:~^X\{dFVePXP݁+1TtO-0֐Q*+!k Ƕ,!/:j9Xz\j_8<(KVZm4D k:0l@;,)e)/x9 yiJ4k/\`8o%HÁGGw=|=:F҈q7c$Fݏ x<6㔘G~yeZ;ƚ t 8r&?`kHj)Z*;hh+ ]x*NBa+I/_h7}BI-yWc"tvz0E))|ǟ`IJ*jȱtY&6uqrs5kAwüNy;~4vσ'-V8cUi6HRPSyJ?@Y)BKR%TI7Ϸ1eg> ^]MWYL`hql8R]huTn.$0Z]觳ڽj:%(T_a.av@} tKs/7+D6Bq 45Ob0(H(P5u`2k֭֜+9pڽc4M"CٿÒy&MM+fMI[kh긆H S`9 :s?E/͓ccB-ؖBMwS_H3`s7D i= Y=c!e 9Ch(򜅺\?e4  FX;wD=V`(*ՒTTa!<^:;]YA]AK8KSuZf\3m&Zt6mv3ip] y3p+n>d_UMES]9jDWÌY|գ_=?^<ߟ/_|SmԄA#’GES,'oUv3rݼ~iJRUW5=?g16.qtC+*(:YI YW%ʩȱY lv03SR>ju 2\ne|M81rⲻGmL0YgebS+*Pd*1C?:V@/C`/,Їh/ lMKkjki w>wʪمSm%d M-CF9%66njT 56x¹?x??ط{ e%l u5-]V^P:zm[6v Ljf~U` zCcKtn+0I )0&:tc'N~7JgJJ,4`c|ٚܺzsvżaAY)g?/_/om]]Pli6lN㢊򚢂eKk*J&[X S a0JNr<{eW]p1*Zt1aHt|PAއ˱QP;^|;UiUU UE\tBVjum W6薫*qVV*+=hqv+fC 5*t"#|?LSPS8BE)@]_]݁V$iJ*FePGo#0(4@y \^vԅJ]D$ު B U])Ƽq\~Au 3P0A *+pB.mDWݹc-O HH?&L7ldꔱScbx ^b/62eRQMaAIM͌3Nÿ_|?9ĆU|` xZ13f64̙7gqv~ZYc*̍O22!O륨6LJٍk`78LJ* aKe2rrZƊ lE?4}H`\#0%%*ua@)˭ժNWV^d_%T1][LMXof#ݵ:z VS+4TRShqBahe^0.RR 4VTWR2pUU9 +nL,=U߁AB7;}l8N U(ZcԅU[@W3C$-Kǖ~5BVFλ8ؠvՕW. f;Q&0@`,^`A5XuFįj$fb¤U]8,qt^M- f/IN1&MG>v゜M;wkV8~ϟ}Ó>޽mٍ/v媚yNNfa^a<ږ8qtMqnɉum q9fnڰ"XYW4Lh;gl&9t(GRWTd[*,P¯0 xx6l6&NEe0M]=NITp853՚**q\@_ Y!P/LVв|+|pym7_fdzY gNKJ8ŋ~ƅmmKO9~#޺w_~}G?g_I,R|'5115 N_2va+VOwڼxIϞ`ESWoX~ӌy1ZFfƁ\A_0yI) [AUVlSc9q* <#gf**ƫ AfJ0D>/MC-AE0V[#J)T 9H>7UUP7UYXM'( 2T55B[APh诪bYs|+#魪–5TTTQ bނBT {p\X؁41L]hB uwfkرT'^?tsv|o-:uJp ^Li}_fq4{S֯ۼmsZ|o;|lڤܨ{֮]3aBmߴe[^ڍܽvZx傆E&Vņ&MȞ8,7cX\T8sa#}˧\͛رn'NXp-[ٰ֮tfX%o]|} OzP١Z,eYͶTPp|>NTUWQ G|:PoC篢RB>?YIiP8^UO „B-mY)+ dd\K_AZ'# K136OMUÅI`T3}xJF!^Hfr[u!T`̛5~k.a{7n`{ꀴ6%eHtuL[6 ̇e',[СKsrFs[n޸+5iҤƅW/3O\/\h[?teߎ]s2oݴue}uSf6%1,JSn{T숱M ZCGkZ^p'>uf3[jg,="DȰdddd4X,ٞq<JDnVRʑBo ECÏxg.Ow}xj|%p䤤,a奢 UWasd%UY lie˞WP_E`3@!ՙ¾› @/ 1 }l8n]P~u!Y`-l;b<@5 6# $be.,L `E~>oG*^@Ć+ضg. Ɇe56p_K/=m]G۾ȾnиhܪikV޾q)c'M+up5*17rبtkk[@M03+8pxmk=\#]ÒRK:IKHdeyҲ,GBFCNN.r9dyV|5|^7ISu"HUUp5UcJXM`0HOۙU'jr;6²4يl[_hqeeclEdO,RDŽ+4:SWP;o##ЀX!UM]76 y7!?A$%R Oz}(RA+sm]^yOf^X?e#_|e[)iVnn\eݦ5|\_=|Z~e۵eۇ>˗.wrIIʪrz]ȤEe{F:r 8TN1edL[83U$HpO@IF)3r Rjr,II&ǒc+Z(y(%~q8lEL.;8IMPgg rTyyZRSَFM+, YUcfi+؊ KNAVFQkȳ`M+H Sނ#C*P75?Fݼ)̖Cԁ)̖ pثL/B*jqc&]┓3um=wϡCΚK/]:sy-Y1sJѤ[ܽwnVZ\>mfUq|Bk;Tb u gYڇh:T$t)em8*7bO"Qjalu:ё+S?QQ1PP0W>x1\N8r\lev'zՆ;Yzf+-j5S9:*Gƫ BՄÕ} 4ՔAQf\N:଩n-HJta%KsF i7 Z<~;!P;}l84`}0 :oUE q@z(&NkZfh2vC665,͞n5qצ}N^8gڕ֮\rwo=~t|Z^y7ؽ &&\aϮ5U˛R^V;Rmi?*x|VyzʄwhKPĚSҽ$Xu Kɩʱ9\,KS;1G'N )Tg]>^r)I +j=Z[X>'V;IUǗr8g؆r[8N/kpY,_egR<JO& h;>ndžCxvA\0ǎ0Cv띡 }ob-h?3jΣfΝ]1.mR 5sׯ\͏=ŧ~׮INMOٷ_yę6mڴ͏=Sj_QqcS.\}OKZ7VL3#:TSg[1&$$x2B9y}6[]NN_AQIVΆM3r8N\-磪Zng8^K9Y](y :zJ*,]^|n`HSSBkLTo͍㩟a"(J˘(0ғ簩>u}1o@O/ۼE Y@n`FUӚ-4kֶ˗,[xÞo]y'.\=ջn޻k\>y>[7n޽w=yԲe[noWn.1-|aNV|5BEM(3=) Xc}i)[D`B6Q7 $tۅ < և7,@":POBd梕 ]lݎ;O:qҥ']pkw>uҍko~c^q~Oݽѭ[?{pCVϬiljhٽH[ kv-^;oN[UE յ3T+̟=2~`s 0OY,&ih[+* c ղ7fHUm(rKF`EI.< u?Tx+Wf+OqҽR6FGys׽SOVi 2t奥ee%HI*+9hR}""@_̛2K61?B\7ZpCzҽ?|X玝=̙c>{}pуW?8sԱ/_pS_?' /\кmޥ׮mߴwmnZmKܸrt齏޽?'_~у?u[޹уtQ;sc^qׂ5'&21sJz"0١R*rlg8x<>ߵ󗑳Ua<^Rj!J=P i*EV8i] 4Tي<=˓UP 4O-z&_(qdeLي2ΪJ&Rg^A5w~m_gz}dގ|AE )@/+_1vk~n>{v{<~?x+0eY9Bm:x`//>X_DPdqch%n @U`f| EmOn`{bb&/V3b)IHJ]>jʵz-EJ۱9 ,ycE yyNoIJ޷u1o*w֟ނ3pJYpюyK޲m+7>v뷁[Woܸto\~탫W_r<~p™gN:{Ymڝ^ziIR<㱃vOOəl.ז|`/Y^>_3ŊpyX|~ =MB\d~jD}zp.+ jR1Wi)h+OӘ?ą˵Qd{x!<h\?y?`w֫3P`l%U.߰ئǶ8zٵvޱK^x3=sSN?p?qġ}߷k 뷮\nm^ҲvƌܪVmn `RcX (rbt)d6(m$[1P L6ת jh/rUPPcq8Lyi<B[%WC y|~$//x{#jօ5Q ^ NF-hٸ~ˑ{[Wl\f?q{!9][l۰s]۷Xne-VoZQ2vTB|u\`顒I)%Y9Xe:F0+oY l^ ƒTPW jԋ9q2 #&csy=[r6loNr3~<= tp:1CK^Rw#w ֟ANZ.۹z՞U]{`j߾|+-ԶlӲi:uԩ 8:t{KFGfTǧRr IcI Q 𘩠X-1%ʂ%;~%3T-%ŕV+YIajr,A? % Rd8Ҳ R5ڶJP6?aBSM򺺵MM,Ҳek7YиjeKedLO5!((koM` H }'Cj-kf+(:_()XD`"0hal?2*z|bbIR4gh#~e/$cl0@FP*`;}@ b @ "0@ %D`@K A,!#XBF @ b @ "0@ %D`@K A,!#XBF @ b @ "0@ %D`@K A,!#XBF @ b @ "0@ %D`@K A,!#XBF @ b @ "0@ %D`@K A,!#XBF @ b @ "0mX^^+X9_AN0Vu?~|ٲetXRRR{g&NDxsP+ثNN/N@Z𘐐a`/Xxa^mGZWWŅnf<Aa6X#7t xl+*:?8l} vr&ϩEwC.bw^FAÇC  tPwt m@zCx@E S]...X;ɿ V_&4#p͢ Wgg5X#v>X z)]充-19MI,XQ`ngzd/P ŵk׆u(jNj6)XS"ݟ ~f ]ߐ%0j3P@FA=yUv hmQ[h wl7[v x[]93PB5^c3Poh/5" Q 6[3ۋCT,TmѸ~Ч`M ͤܞ킵3[:5 `!P};K,zo=խn ֈ(}DLv5@Z%s.%X;՛^kd:`c%WP! V  4 "0@ %D`@K A,!#XBF @ b @ "0@ %lGus'% m6p80\+*$%}lz+|c~7u]a SiwCqV< #0%KBч)^a 2C*c1Vz4rt V+v˙@tnm. h l!01&ʀJG:Lk 6^%u]鐀88`nH]L -7E*G Яݡ @;6p?A`^z"L|1z&xYE4dzT8&NXBOo]7^G9p8XWNX5SQ2 SK]W6+cCX1&:h'D;jɠI | sp`h SE}^B1}C *]J7B7EB51\&k3ang㡏L$ (m]`VAl77;cWa>IU@ 1a3NHl;fRH/qm2Π7&XeZQLF!iBqa **/x.R:NT 6(AO h[Xu!a/ B{U(%Mbzb &ث2VWj" S'=$6 aV`]]W`LK19 k ͵ՎUwgP sGؓлu(8`LC;x]e@!KaCC {ICzlw/S/w*Nonwf:hn&T:dC>9D */q`iȼ!?͛78q!~&;ܗ;C߯QF )=j`R?fE\!Q10\KNN:؎]Յ! ?-0z Yws~XaCC@T`=+WQ]j|RUaF{C5EC.B`}z`wjTu﮻w /luQ}Z0^'0%S؋)vr][@ -PDc:EDh\8]wHhh)D Q}zK1Ы4 j(mJ+ml*` 0 t n}A. +@GDP<*A- Bj=E/@ 0z)Ƭ@; 0< C L;D/ق^!{1a @՘t-[7ph] 4F/ST@< ZRgl*-p,qhy6r{c `230*GP;D`A  67D2+`/x*L`4  Ԏ?(EDG* P$` 9.aPCy@l A44Lw0ڀ ('99 t}-^E@kbj ;s/"0 i@| a 6WmPWhh(]H3Bò9tf5zw& 0)Z1NFԠ۴ΛJBZЯ9C!]OZU`?QC}ѡSɿFP`ysϺ^V *q! 4c}/t3Їxh E T=t}E ALNN56p[no߀0U пiaaL#]+]> -0PeШJV` 6콧*h:4tʴhuѠ vHFP`_{@N'h0a ۷x藺*[Um(6bl8 {@+:<56p"Z`f k/2zbBWc 6199Cuttx$# j(m/D`=LU O`/1b AWC5Q$лogesP9l/1A=!# j(m l/S]*THaX9n # v(mJi0rfحkz/ BPbl8B0jqsS2 ^ih{/1wDB_G!aPCy@l @L@ !ئ] h/&L`uTy K!bl8B!/ lAK╠:4o0^*XjЅQ> j(m^es1ACW`܃A T~},g` 6Wg`]A`n ZK4HW׵iu! tt bl8B$]Hk*gn HWuUd/<bl8B^`<ZSx?ueU?] hЎLi$%@P`!zEV`W \~Q;2"{!@P`!zS`̧hm Wi0ZV> T@;BeD`Ay@l @!6m )@J0UP F`tM aPCy@l @H`i%h.G0Յ&0, j(mE| CJ%]^Ѝ#@P`!z+Ot!. zޅ0huBL`t4PH. +a c!IK*F0Яv/Lv6 j(m,_GDi#7^t ^B6b:lvu)Rv$#^  6W`CA 0 P0!&jm# l@*3 iren#@P`!z Wr!:BWFO~PHQ22AhY6\A  6Wt& ,O[" Q!مX{" 0< C F !x Bʙb* ]{1@P`!z=ha CPͮqޅ&l @3*2m\~@wfX8qqZ+eN??gr(jCS$cT'4 .5ZmӸպiVM4Rߖ2[}2+Y( oa+hc QX>PQܩN}A6 V.gW_G؆˱yߩG?bDoM-KD7k;uյDd4 XWLQw--&txq.X~ѫ.X}ؤkɵ\[0q5V@aY5m7^ϸBnLcu-e20!E_`ab(H]\ Kя(~_)¤KN]-9xeҡiXM?յD!ȀВ Fm%-RPB/X}/A@k/Y@TLcяrzϽ^k ̱fyt?;z{;^A}X]KD" Rz;ªp\յD!t 骲LBlآ SZ. ?@G*\]Ӈw /^u%" e =*@o!"{8+" 6@ ( oa@i@|@ Jl<@$P`! Ҁ@  7x0H4 C A 6@ ( oa@i@|@ Jl<@$P`! Ҁ@  7x0H4 C A 6@ ( oa@i@|@ Jl<@$P`! Ҁ@  7x0H4 C A 6@ ( oa@i@|@ Jl<@$P`! Ҁ@  7x0H4 C A 6@ ( oa@i@|@ Jl<@$P`! Ҁ@  7x0H4 C A 6@ ( oa@i@|@ Jl<@$P`! Ҁ@  7x0H4 C A 6@ ( oa@i@|gW`5v/j` V_Ԡ;b5 [@Ij P0OBon?A޳c~iD2@A#CW*2 !k-&`=}E4"S-5E7J)7Pfϙ`,̮b0_YiD2@A^ra L HHSw~Uau 5H(LyePQs$$} v6D">w~U)tOQQc $$}}d1  4"SROHзHMY6DfΣz[4龇j P)BUe:@B `)tOQQc $$}"pۮ`E ]4"Si+*f,+iD2@2VuDA5|"z5H(=9 A.-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a-|"z5H(;a!C+89IENDB`simutrans-124.5/themes.src/pak64german/files/ls-symbols_stops.png000066400000000000000000000034601520675460500251250ustar00rootroot00000000000000PNG  IHDR@QgAMA aIDATx^148û^aO+8F !S,Od7Lyh5/g} @%m @@g7S 3 @OYj$@lX @xzY @@,  @<vR%@ 6h/bG~}U1F7x~Ot{u@ Ͼl/pDo>#E3lP@ 7_|kk؍5=Ok 4weIȊm"2cR_2oҸƯܱx+zƟ&_~K^tWgl!>.]E-cP(VEc~U1N3a4mLٚ]6.A ׋/bu:a_ly6Ҟs[gO1/D¾I?R>8vFXٷxUu;{n6PF  =nK{syilm8J V.AU0Y:f'\8?jϪ[^oBgQ-HduV7 O۷;|V&vYHߋms+^4 -Tr6C6M5b|>AG勃ouxp-uoՙo]\I^ǐjty#­:rjG l 5f/b&?.;%XTr|GԁqWβ;yZ|z/5]m׭3+Tu@ӹ;G@2uK5fx[h>]W`3,nk㽖׶oy+mǯcu T}ޑ2o,;O]AH6OZ~捧PS'0Tϱ`ixx h[ 8X9=16Rov]i_c8yKy_ڏ>?߅l| /ޙw8>6lz,;՝wmQl3lUݪl}׵[k{9evOOZrv5o8/g @}NC`LMN !@ ozʦ D@ ` 0@@@o2uRܴ_GlOIENDB`simutrans-124.5/themes.src/pak64german/files/ls-symbols_stops_new.png000066400000000000000000000031751520675460500260010ustar00rootroot00000000000000PNG  IHDR@QgAMA a pHYsodIDATx^;%EXCаV afU41 R͟4ʪ~~Q(@` P(@` P(@` P(@` P(@` P(@` P(@` P(@` P(@` P(@` PoNawf@-eóTwg_Y,jY`?XTq?p[˴{lvVl>6;9 p-*ؕKS;hӱ ^_j4;{{:-, Ȋ7GR^*z3N_4PhҌCE?QU`域knE+~~u֙da1VF;|ïZBH 5fӇew&:bXEn׫ۢC_PC|.#v)j'r%Tn/T|ٿY-gS}ݎ)l d[UOV?_onY Z1rO #vz)o+~:gvP[Ű/{:^2 Tgfo/ XabEHɨ>a?bp|.#v1}!_Jy||g"=+ ú̞2QI#Rs/wJLig? zw֡Zm;Uo1s}K(䠶K}ga?b#bn$y~CEfoxœC,AB?0;vm,*щ9e7׿}d?bKϳ(P(E#'=<ҫ+Io,dg3H~ mPfc1DZGYٱ-~o<jwXby,OufxKқv fǂ3NwgmDs{]n߾GiPGL86Ǜ}(~TD|)ORnmTS^~9ڔ0.kWKThm;vo}qT[$w}OLzê?V'<=>R[Lu{埣hcӺSFDٙ.˝ 1a[[f __w 7jx (oh1d8|Z:P ]f}Qn̯|/P._*^<|.GClj/ff@_uٿf@[7?f@{ᕾ*h֟; QeZU!V~ XF07el~7N 7i TUt޳ڮP19"r/͕+W>NV4 oJm+\#c.J5?9?zI4Mi)G5;tN5d !4B`]٬.W$bp4E_ x-{/Ziο,Hx/@]J W`㭍ښ ѿ3`)}ҩ! .wGw!"p ?1v;’N;{nǭ!|  r.sc7In%y:6ztl*忨gss.nc$4>;PHB:6pz엣#'*Λtܖd3v7 y εxjZr#yzI(Wcw<<3H;gUI?uklKcw(7`eL( (]vwQD`,Ԁ ? >/ݩ4e$`w&tEOQ-PUx&`!`&`!`&` ` ` ` ` ` ` ` ` ` ` ` ` ` `) `I=v{l[GU7~toiJ<ۀKcw(7`eL( (]v}\D`,Ԁ ? >/# hTHܟ%LUK0[?=x 00 0C@L000   @@@@000   @@@000    @@@@000   @@@000V6`?_ MU'O?~gfU剽?h;8E  PP$?^Cu7;tQd`QPlLd W|.-Q-#s3W p./qpo Tl-DLL0L 0C@@000   @@@000    @@@0000   @@@000    @@@X1O?HIENDB`simutrans-124.5/themes.src/pak64german/files/scrollbar.png000066400000000000000000000103751520675460500235570ustar00rootroot00000000000000PNG  IHDR1gAMA a pHYsodIDATx^OW񼋼;āIf8QA"$C!hblh4B4 " $;@fZUN7d>|(~ٿT:o/oA!oo͛}]z- [A!$laؒbvgzٴRf54nig¯Io٤&gzv~^rD &ͳ/A!`9H[ihtatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatatz4%=%A=[$^ͫWl)K v?g#Ze{`E4+w~YJB?@F_n:LHWI ͆nRC D+P i4 vϟ?O Ypֶ7h ֪$~REZpֶO (ZΆnIO^ZkBYJB?)b@kZH8I'Z?y]hU gm+ @' 7CM]6Z $s@fH ֞h! YJB?@F]-vZ֐]tfC7IߍUSe"i4 Wu$~J̀N(t@ (PD@' :QЉN(t@ (PD@' :QЉN(t@ (PD@' :QЉN(t@ (PD@' :QЉN(t@ (PD@' :QЉN(t@ (PD@' :QЉNk ʕ+׮]K o޼TaWu/_~Ǐ߻wΝ;_|~3g[rʡC>|ر{U>SNL}$e*o'cl_x1=(֡Aŋ-S9rHjVޞgϞMo}1G &sUq.\@=*ЇLӭ=OoOvzl=zfwD &se_7nHOI8q냻w獵g{viSV`yL76EzڲٝRԐn\~O>d[noGQbÒ]߂7ѣGjO'xBew濕rK.oY>%}[ΝKN.m'OXlmmWɎ;رԞiYzلt&omd=x7jlO,݈ ~lIv#ԵwK/PԐn$_|]g}6Mah9;"~ Z{w{IڻxPԐn$=-ڳ\mWLzE*>K욗5ҍaw+;6̬. `}]Wb/"tӶ~v{?53/ţ5׍- oO꟪S7ҷV_O˝;w|ѣid_ӎV`y^ϭ[ҳ~_ⅾztc3՞zwɎk?~^<B߽{ω'Snܸ)m! -!gm g+ӧOý ˗-iѺ?y򤭟 .UQ`v+#t5gϦ\?-GI3O{ŋ VA!/^Բo({QCi<~ ls-symbols.0.7 -------------------- Obj=symbol name=ColorOptions Image[0]=> ls-symbols.0.8 -------------------- Obj=symbol name=Flags Image[0]=> ls-symbols.0.2 -------------------- Obj=symbol name=Message Image[0]=> ls-symbols.0.0 -------------------- Obj=symbol name=NewYear Image[0]=> ls-symbols.0.4 -------------------- Obj=symbol name=BusStop #Image[0]=> ls-symbols.1.2 Image[0]=> ls-symbols_stops_new.0.2 -------------------- Obj=symbol name=TrainStop #Image[0]=> ls-symbols.1.1 Image[0]=> ls-symbols_stops_new.0.1 -------------------- Obj=symbol name=CarStop #Image[0]=> ls-symbols.1.0 Image[0]=> ls-symbols_stops_new.0.0 -------------------- Obj=symbol name=ShipStop #Image[0]=> ls-symbols.1.3 Image[0]=> ls-symbols_stops_new.0.3 -------------------- Obj=symbol name=AirStop #Image[0]=> ls-symbols.1.4 Image[0]=> ls-symbols_stops_new.0.4 -------------------- Obj=symbol name=MonorailStop #Image[0]=> ls-symbols.1.5 Image[0]=> ls-symbols_stops_new.0.5 -------------------- Obj=symbol name=TramStop #Image[0]=> ls-symbols.1.6 Image[0]=> ls-symbols_stops_new.0.6 -------------------- Obj=symbol name=MaglevStop #Image[0]=> ls-symbols.1.7 Image[0]=> ls-symbols_stops_new.0.7 -------------------- Obj=symbol name=NarrowgaugeStop #Image[0]=> ls-symbols.1.8 Image[0]=> ls-symbols_stops_new.0.9 -------------------- Obj=symbol name=Passagiere Image[0]=> stations_status.1.0 -------------------- Obj=symbol name=Post Image[0]=> stations_status.1.1 -------------------- Obj=symbol name=Waren Image[0]=> stations_status.1.2 -------------------- Obj=symbol name=Happy #copyright= Image[0]=> stations_status.0.0 -------------------- Obj=symbol name=Unhappy #copyright= Image[0]=> stations_status.0.1 -------------------- Obj=symbol name=NoRoute #copyright= Image[0]=> stations_status.0.2 -------------------- Obj=symbol name=MessageOptions Image[0]=> ls-symbols.2.3 -------------------- Obj=symbol name=Electricity Image[0]=> stations_status.2.0 -------------------- Obj=symbol name=InTown Image[0]=> stations_status.2.1 -------------------- Obj=symbol name=Seasons Image[3]=> ls-symbols.3.0 Image[0]=> ls-symbols.3.1 Image[1]=> ls-symbols.3.2 Image[2]=> ls-symbols.3.3 -------------------- Obj=symbol name=networksym Image[0]=> ls-symbols.3.4 -------------------- Obj=symbol name=timelinesym Image[0]=> ls-symbols.3.5 -------------------- Obj=symbol name=fastforwardsym Image[0]=> ls-symbols.3.6 -------------------- Obj=symbol name=pausesym Image[0]=> ls-symbols.3.7 -------------------- Obj=symbol name=CompassIso copyright=FrankP Image[0]=> compass-iso.0.0 Image[1]=> compass-iso.0.1 Image[2]=> compass-iso.0.2 Image[3]=> compass-iso.0.3 -------------------- Obj=symbol name=CompassMap copyright=prissi Image[0]=> compass-map.0.0 Image[1]=> compass-map.0.1 Image[2]=> compass-map.0.2 Image[3]=> compass-map.0.3 Image[4]=> - #compass-map.0.4 Image[5]=> - #compass-map.0.5 Image[6]=> - #compass-map.0.6 Image[7]=> - #compass-map.0.7 -------------------- Obj=symbol name=station_type # used in line display only currently # the number is the stationtype (emtpy images must be defined as "-" # loadingbay=0, railstation = 1, dock = 2, busstop = 3, airstop = 4, monorailstop = 5, tramstop = 6, maglevstop=7, narrowgaugestop=8 # maximum size please 11x11 Image[0]=- Image[1]=- Image[2]=> ls-symbols.4.0 Image[3]=- Image[4]=> ls-symbols.4.1 Image[5]=- Image[6]=- Image[7]=> ls-symbols.4.2 Image[8]=- -------------------- Obj=symbol name=MessageOptions #Image[0]=> ls-symbols.2.3 Image[0]=> singlesMessageSymbols.0.0 Image[1]=> singlesMessageSymbols.0.1 Image[2]=> singlesMessageSymbols.0.2 -------------------- simutrans-124.5/themes.src/pak64german/files/singlesMessageSymbols.png000066400000000000000000000012011520675460500261020ustar00rootroot00000000000000PNG  IHDR@gAMA a pHYsod#IDATx^AJ@m.x^%zf8i?;<$a_.~ AqsYٝ }߭0E!<#5}¼씫vvzoV1А͊ Ȅ?sķN|߭0;;)B|Db$ry"~pvuas!4PK߁~õI|I|r:yp4 :ɭ@/OϏmP/R5/|t1f#.nyǣ- s7/5~8vzKe+vVGJ V]U:Esp+䃀j0 þKu=?s+w T>>n&0AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$AU:#qIENDB`simutrans-124.5/themes.src/pak64german/files/skins_neu.png000066400000000000000000000262771520675460500236020ustar00rootroot00000000000000PNG  IHDRG/$gAMA a pHYsod,aIDATx^Os\y)e+.Zt7YD$J$Y% " &h@E"B0bZeY 9Ydq]u?=zzg0dz<Ө멮ӧ}̃g_{gwK,پ2Zo?> 5.G7-}5~[nzwkknٸ_>>~qB:tssGj֖Zn^MYG]:+jɣ̺031.#ì(f2vc:[@t{Vk4\]9Ĵs]J;W%{WnlvloNTpggkCR-ݮ,H{f̙KQwA`jbg4uףa3O!)i Yrã.Kv۹gg?koo洲,U5P3-S!d7m޻iK'uVo'f:0uhmqBV:F SwAVֲR .Rn=%D2jQ-*Cnk6)[xYs6ƕd]&La=^? SڙT~jZ+X[yO:y)W/Y6$r֭_K[9[\[vᝓ_tb<>3?L 60mdP3MK94h`{&m\@ݴ|޸,IRZhVn3\r>]tcMô3g߯ה0y>t`ΐ6ƭ$$,ɟx6GTC-=;~MJxyT%gIUғ6%,{Fvm)L)J|`<<洙^qa:0ghcDiG-[O^\ۦj;)~yJKipU[nKOGi-)VK-v/9J'ԛH&ͺ031n"!1#dxc lXe4.Np?u +ՕNzrkD[2jǓ_ɀ:W.)jPc՛ʉ?L 64$ɛβ{08Zd2pu0,ou%-ooɟP ky o)<:kYMj˞|ub/V7NYs 1Mi \eqӐ@u.zp?u$^d•kAmlm*P(5NoI\%w3?L mL{Fc7lhuȌ^[0u$&*[Zb?Ԟ˲۵!j% *UTnC&AO~{CJ %ճ1t` ƘbCOC-ˠ=eO)X.Mz0ܤs^)'mm\JT?[[yO;sKj>˩0-[=ݗ3I)LonvG%g˂K>t`ΐ6ƔQPN B1}S p?uG撉NxE۽#Lm]xg[v&UR^b"%gژRFB*kj,:Z^e< nO5;6nzFk՜'j> rC͟inj?mk]S8t` Ɣ>2[M=^;aS΍26$вп ȒTDXz'}f(-eה!&m?>~?=)J*vxkC3?L 6ƕT"Y],7@uw[%cݖ=ҧ_򑂵*Sgn{9 Jp'~OvSݴJڤDr#?L hcJuI-AYF;ý2ĤJ'ym:'a M"S{k+eݤYs6Mʺ׆uL@1ñ;KUŭu!]ZGDkGipܿHgJC.D [kfژRF,0ذwxFvUƆtt3Oݡow' ̍+J$ZnhZJtу='W*ԭ_V2sWο6ZqɉӁ9k11bzshx'0vβZA@Bjp?u$[Ϻoh'޵Xڔ'ʲZyOkMT\Ͽ%!ģ{*M/ia:0g@SZI<6p{Z4ZW6 Ԏ62QULҢXkj}%,TǏ2G#i=2`|AqsY]NPtL稍}>;ex]FCR'gVnZ,o\z_vYn B{풲ō2q;ҢļN{ے3 tA)e$\.Q\%C@͞=~5wvJ[rT%Yn5%=򡄥Q+qnsuE;%;%Zqk X(Դ6vKH=̺03WRdW֣Ȓe,){+ڗArO{ҙ%iu,1IaJL'R)&bD Q'IM37[F?~!m[) Y=j|D;9Y֎1Z;h;>)lᬝ_ew?NJҿLBjSvKrj8t9t` Ƹ )o.X.MG8P5:lzmX>-d)^m̥|.v]푼Ғt`hcDIehK2weu轕qm;.Im)[g<)L,9Xw>ݮ^TCjt䨵Ӂ9CژGI=֨ʺ Jt.щe`;$,s,ݿZKr+/ .75B;k6d7m޻iK'uV_'fژRFDVAm2uk^;T'=[$5e\:/0{J(KdR/@9)[T_k5mS()T3%?L mL$aPGC@xx=ѷ6.o2O&yJyՋrl% IuVײW~~}JΒ'֍e8RRBrC[k!'}76K3?L mL)#!U=_[ex, -p¦V;vb2흍ܖvۖvTRKv;ܗ GMYzfSLj`B]~DIK,#OaJoyo}psu%^ڸ");L_yM\d@+QbNMGYs@c=jZ^?;ҢF Y .~f孮esZk+5{<}V7֔W,ݤ6W'bz)DZua:0g@#Q2`Y?kr=y Ei; }(%t<%}Ydlm*P(5NoI\%w3?L my*&kTKF8rbY!3zpUCJvniR{/n.S=2$TQ3>)1S0*VLӁ9@c2}*:er=2M F%)SwHFry춵qE(S}`om=.M,rln}w_$2D-5QӁ9k1daS3~b8M:;-{Bv;զ)t&nKpZMfO)T(i[,?L hcGle$֣ͧ؏[;MTn9K&":csͫnǏ3vneRVJyIt:ua:0ghcDTdAVCjmF_yrooiY@-7oڙIݶ;?L mLUֆt e6SwjT;$#{\k-%dnOP[ )CLvZ|ۣ{jST&ڇՕg^mHYe`@ ψl((/k#O!Oܕ#kUr˃r*N.N1iSՕjIG f2Ƹ ɫG)Vơ:) hV;ý2ĤJ'ym:'a M"S{k+eݤYs6F*EVnA@zm4(= P2H*n yڅwL:'Z\w(?Js'+E:Pr*v&'9^Z4t` Ƹ ZieS_.p?uݝ/07,+Shj)wSG?<^LS~Yɠ]9h ~&'ʃ?L 6Md: ԆF2.QP(߂dYm~ĻRĔ^YA+i}-\Rj$DxtpO]%Ist3?L hcJ7}[{fδ031!Gr,HVX5{j֩f!~5wvJ[rT%Yn5%=򡄥Q+qnsuE;%;%Zqk X(Դ6vKH=̺03WRdW֣Ȓe,){+SڗArO{ҙ%iu,1IaJL'R)&bD Q'IM37[F?~!m[) Y=j|D;9Y֎1Z;h;>)lᬝ_ew?NJҿLBjSvKrj8t9t`,mxF(@%gMK?,(,,Ó6{nr -3]h{_\Kw}hs _{GP+'?Y?ZEFS,wXwO.c9`UmWlf.S țVF=xYƯ|F.w74b={)hB^"czT,% ÛHyqd2о!an%y4lӇ/NұbUZ. 6%\~02 3=:Ԧе@5nGc_^ ;l{Vhc5b =M7 =th~E/ Oash@zs,< g^uwm.=9h*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*k-(Yz W_}agGpzsL*~ ]d[HE Ô@}eB?z\_60yQc%Ҟ*2H)م@gYY '.ϲمccV0f=4 NFh M uΎ1&=fϒӲ >=cU4Z [EDTlR}sl6Z>T=z8BEÆjWXkأ_̓2-aO =uJ*KB攃Z  34p G{V5p9:¬A`O#LUags4c ;an PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% PwE Ǻ_;o_qԺ,Gt# ??p 4OKt*P瀮>30 Ap l )&~;p HNw8@;}23ygg'tY!mmSt6Ʈ| ж;%L;hc mSt6Ʈ| ж;%L;hc mSt6Ʈ| ж;%L;hc]~ LFM$P2c] {"І ж;%L;+ }bT@p w[tcIwuY|S0.K8л-a:T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*я~/v.2~W<|޽{6ry7_W_}5Phx/K/ /U.?2F*agG}E) @U)gL"P (Zd̞=GŒ,ρr/ ~OLB@- ?h(Biph`O=і%,9c,2@f K2-% ԞC]grhXƯG!4[XAp@9 1?e֗}DJ>G]nN}B Ph?c(ob4gt3zg$P>F !3I(^C]P?=͡eDt=U_v&P8I2vv?a'YeS[(lagGvUz B@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P dOǯÇw^]]]^^~7^{W^y?i8g`m4H Uְ5xMAt4)MM ,-*ǓpB@P[zozNY(hcz?jK[S ژǏ'xpBH]Ж\9,1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -K^±.K8л-z{ Sa蹄C]0 2Y_/wt0\¡.mQ%L u0n7uQ/).K8л-ZMKxl`z.P C f%L u0n7uQ/ g%<6]0 =p w[s `ze%<6]0 =p w[%L u0n7uQe" PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@% P T@*A PJ(@%k)b3:IENDB`simutrans-124.5/themes.src/pak64german/files/stations_status.png000066400000000000000000000107741520675460500250460ustar00rootroot00000000000000PNG  IHDRRlgAMA a pHYsodIDATx^wxUU;TJE@AA]%A iPuD8DФzMHB-~krܰ;gzOIpPPʎzv;rSs{{=sH~8UcNIUs]mkYv>7)_?9{ rLsH\uܪw7ΐĚ]{8nŞߧЫ{y6> c* ?ŕsW~ oݳ\j4~A&k UvjKY;ubϵ3+?.G31wp.d`ext[ m֣bv:#]kR@œ 5"vNVîcWWQo[ic}{\-]YS*=ur4m _@ ýՕjGZ'y?\~ӟ"ʟLh|  =hnicְ+.PcZ nxCEjtP\mWŦ@;@-`CmDt\ DēG2=^ i \WT.ÑjCpg7ѷQ#d!jUdV4lٵȵa$5Q'4ÍM@J#R %C4/>.h7;OA} \/`pHP\T7J/Gh0YUNX=v=16nD_#mzކ H;dУ4aײ#c+_5v8$7y(52ߡi4Os]a)vAA /6[=W71`doV0_vHxn ݠ^=<|S/ԯϮeG5*!#d'S΢m85{C"b+#ݑL.h7;Dfܤ/1\Lޏ@Hz^ & &׭Cڵ8j+q$ :uصƳ&*oX z!R/@,43־(]+.h7۞@WScOvHicW OCqu3J ,~5|\

jOFzAVs :kƍ:#0| vS4- ?xrw3cI.͙Gs5`+{@(2sq;>Hƿ<ȡno.P$l)鵆 o6W̞ow,RQH|>o̱uv a ."ܟ@CeyC6|_?AoiTл>"*Vbw;fƥGѩÎoVuWo,S;ϩKn^x9lBD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4  C^b1'1g6?oykFe׶+ IUҙps+|B^p% )?sy€@N:w&̮mG᧒Y7C?^Y;UX|1 ]sQ&yz?k.4os)&.x A$b0BM0s'#l8FDkQj r4γ+O^-WSС`P}x9Ep<|C>4't'H1ATs};2Da\ vm;z|4Hd0ηOF/O~-?eX<$''#33^';uK.!TjpO ^ќ4w}bKkۑywU(1ߒ;1 'xpcH |35aFܸq)))pNvtkFع/M{w'fߌѱ!͍;^;]ێLBH_hs~k­a'>o:#}Yc["77׺^';Atr w&5z9a8qx=Q_Gg#hDR_KR L(r>庅be(k;y#fRcpb>&K^$zv Cpv۷s;DyVUđ|5_m_śa;v (|cu@?Mu\ϡX(٨C ṇy/[N<f; o3j^)iy*2.c{Vrf&"Llھ3ic׾0޺k33g6 GMh9[ދQyp4hg51K7MX_;AǠQwWp(t5T݆x4c;Nb,$/E`p4-_f'v?J/8cBc;i~q7ޛ2g"0=b㼧c U4 #/Qp >Lk˾p E@]wl1 Cn"fл"K$}" covYkہil1!ӼFm9[~͂i/Ip1: [ ;}pҋ3sr"'k)'|>sfkň׭&w_͕lgY2mX#!=@^!/x90w^s cٵfV׶K<x;vc`S G3p0 \pϸrQoZ?hu7ͷ3O pp{&9F3m[h {z?;'vƨyX6IO6nm3y` YkRNM^_+d@)I4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(4J4 MD(>{Gzq)UcǝrMr+ꃀ/Za8pE1W88sssm I}<͟lSHEzW| pj3͵;)$"h.b1;cBEZ-jm[p] w:QgPT 4v˪־{kp] 4g`+5q>nYfmbWXEZ[35pk pEq3/No~#ė5NԏK)T;v^i?.\=:[nئ+n~ 6l]KK[gOXc%?d1s9Zv6$\Q )oR%c 5}^o1 pEFH.0fL@w^Als0w|?ll|w}%_ 7c-мIIpk ,H " to have them unzoomeable # # This theme has the scalable standard look for pak 192.comic # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will used the unporessed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # unpressed Image[0]=> roundbutton.0.0 Image[1]=> roundbutton.0.2 Image[2]=> roundbutton.0.1 Image[3]=> roundbutton.0.3 Image[4]=> roundbutton.0.5 Image[5]=> roundbutton.0.4 Image[6]=> roundbutton.0.6 Image[7]=> roundbutton.0.8 Image[8]=> roundbutton.0.7 # pressed Image[9]=> roundbutton.1.0 Image[10]=> roundbutton.1.2 Image[11]=> roundbutton.1.1 Image[12]=> roundbutton.1.3 Image[13]=> roundbutton.1.5 Image[14]=> roundbutton.1.4 Image[15]=> roundbutton.1.6 Image[16]=> roundbutton.1.8 Image[17]=> roundbutton.1.7 # disabled Image[18]=> roundbutton.2.0 Image[19]=> roundbutton.2.2 Image[20]=> roundbutton.2.1 Image[21]=> roundbutton.2.3 Image[22]=> roundbutton.2.5 Image[23]=> roundbutton.2.4 Image[24]=> roundbutton.2.6 Image[25]=> roundbutton.2.8 Image[26]=> roundbutton.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> skins_neu.0.2 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> skins_neu.2.2 Image[1]=> skins_neu.2.3 Image[2]=> scrollbar.0.2 Image[3]=> skins_neu.2.4 Image[4]=> skins_neu.2.5 Image[5]=> scrollbar.0.5 # scrollbar back left center right Image[6]=- Image[7]=> skins_neu.6.6 Image[8]=- # scrollbar knob left center right Image[9]=> skins_neu.6.0 Image[10]=> skins_neu.6.2 Image[11]=> skins_neu.6.1 # arrow buttons up unpressed pressed disabled Image[12]=> skins_neu.4.0 Image[13]=> skins_neu.4.1 Image[14]=> scrollbar.2.2 # arrow buttons down unpressed pressed disabled Image[15]=> skins_neu.4.2 Image[16]=> skins_neu.4.3 Image[17]=> scrollbar.2.5 # scrollbar back up center down Image[18]=- Image[19]=> skins_neu.5.6 Image[20]=- # scrollbar knob up center down Image[21]=> skins_neu.5.0 Image[22]=> skins_neu.5.2 Image[23]=> skins_neu.5.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget_new.0.0 # help Image[1]=> gadget_new.0.1 # minimize Image[2]=> gadget_new.0.2 # previous Image[3]=> gadget_new.0.3 # next Image[4]=> gadget_new.0.4 # unsticky Image[5]=> gadget_new.0.5 # sticky Image[6]=> gadget_new.0.6 # resize corner Image[7]=> gadget_new.0.7 # goto pos Image[8]=> gadget_new.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- -- ################################################### # text labels Obj=menu name=DisplayTextLabel Image[0]=> display_text_label.0.0 Image[1]=> display_text_label.0.1 Image[2]=> display_text_label.0.2 Image[3]=> display_text_label.1.0 Image[4]=> display_text_label.1.1 Image[5]=> display_text_label.1.2 Image[6]=> display_text_label.2.0 Image[7]=> display_text_label.2.1 Image[8]=> display_text_label.2.2 -- Obj=menu name=DisplayStationLabel Image[0]=> display_station_label.0.0 Image[1]=> display_station_label.0.1 Image[2]=> display_station_label.0.2 Image[3]=> display_station_label.1.0 Image[4]=> display_station_label.1.1 Image[5]=> display_station_label.1.2 Image[6]=> display_station_label.2.0 Image[7]=> display_station_label.2.1 Image[8]=> display_station_label.2.2 -- Obj=menu name=DisplayMarkerLabel Image[0]=> display_marker_label.0.0 Image[1]=> display_marker_label.0.1 Image[2]=> display_marker_label.0.2 Image[3]=> display_marker_label.1.0 Image[4]=> display_marker_label.1.1 Image[5]=> display_marker_label.1.2 Image[6]=> display_marker_label.2.0 Image[7]=> display_marker_label.2.1 Image[8]=> display_marker_label.2.2 Image[9]=> display_marker_label.0.3 -- Obj=menu name=DisplayFactoryLabel Image[0]=> display_factory_label.0.0 Image[1]=> display_factory_label.0.1 Image[2]=> display_factory_label.0.2 Image[3]=> display_factory_label.1.0 Image[4]=> display_factory_label.1.1 Image[5]=> display_factory_label.1.2 Image[6]=> display_factory_label.2.0 Image[7]=> display_factory_label.2.1 Image[8]=> display_factory_label.2.2 -- simutrans-124.5/themes.src/pak64german/files_large/000077500000000000000000000000001520675460500222325ustar00rootroot00000000000000simutrans-124.5/themes.src/pak64german/files_large/back.png000066400000000000000000000070031520675460500236400ustar00rootroot00000000000000PNG  IHDRݾP IDATxoyyfDr%ڔcl6l,ڠ-/( 7/P`fmZl(rp2(b2߳ A>oBޕ\ڿQ DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D"" 齴wqPh5[AXz)|/;wOwN:AFjXaz[VJ% aYՋ/d>lFCa5.&O߿N!_@N֪[erL>_./[Ë_ttݙ;9ij:Y.a4M#ͣ峋WK٨[V \P]/rx(aT*k[xt26 $p!ƱRyoZ!+أe5&ԒiBDz#!C8 Z3.?:;L&lsJ0UNJEgꔴ( < c Ӽ~2"O{(/P}\*E:SѬQ,m㑦iiyzάRi٠VQTk5Ǚbh\ ^?@h8D~0ly.͛|.7(M\VBd0o8ǯK4qBt=>N~}Q^ ATq^ rYoN%ɝ:kD4UsZ]z1k*(@r)W4ȁٴutQ맽R( Z+ZͲ,%' =*T]b E>Äz3}_ǶGD\=ݛZGzA맽RsuXze܃M>t8c U^Gn}Y4(A ~ś;w:#X ^yg<|V{!w]/(/ieg%yXl(%xf}0|S5hNٽtmup!V{~0;-JIIQ^ t':~j[LJb RM%" 2J^)cLf{ˋB.y< |CV맽RGǯ쵏3]1B_ Lh/_ +Y~s߼|Eה|/_맽Rhw5z}٢)1FTBߍNӋ'QPPeP\ozVB S0c0!SYv맽RDfmt).nӹ{{zH+)qiW0}/|]L|6K ̮$(/b1iޕM}|i S5 Dşf"tg}ߕߞG\Fڭi•*Ƶ"fAѽx:<]Eڝ;n5~/s ~O0Wk5D/WU -O{(/P2W/-9ھt]4h`8wK7/~zvv4M*5 h%~'E}0NW/УJ%/Vj5}TFpp۹sߝ:˷O_~!Kڞi%EJX맽R(pǍjutgS]`}) wzZ^lTM_`Z'~'EyP;ۢjAxszsJnoofLC}w>_}1|>ugnu?Ȉ-fy*?) >9qST gF0?KxdWMP(Lx0ƫ[Ӳ^? z߾2@ƃv3{|vo:'˧O>wƓ* ba\YN%mYofӶGJ9O{(\U^#U*৚^~y~{lu6R|'W#z\N!Ԇi,~3tIIQ^ VZT//UkUD4~5Ҩ7ZF;߂~:n sVv7繙lt&(/ۏǗϟO*5+ D&CѩVzWzٿmܚ r;gG%j}4ZF.Fn}OOzOYAer9"78=u&(6 *v/O1 U3vNѣ55*I|7tԂ=?)7цٸt1;3}??wc727jFՈoN]]m_OaͤO{(p/7`ᇾ|tQ>|tQ^ . Eyx>P(/J@颼@<(]|tQ^ . Eyx>P(/J@颼@<(]|tQ^ . Eyx>P/J@颼@<(]|tQ<(]@<(](REyx>P(/Jh.k;?s?ou'G%_=v@F ""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP ""&qIENDB`simutrans-124.5/themes.src/pak64german/files_large/button.png000066400000000000000000000040651520675460500242600ustar00rootroot00000000000000PNG  IHDR@[*gAMA a pHYsodIDATx^AI>+EE'tF1jf C x̴[v+{>/jF;{ݻwo^z|gg׌?[hg~?=>}<%X=+8νjV3% ?Ǐwv7<1;}B(@90ο;?qUBT=8f3TgQQGTB@&! !įtN=$, Hm^'įt`(⿗|:! %\ku#w'KJt;91H>Cvl'GFkWzIU?n|li^2 C@ &L 0A@` &L 0A@` &L 0A@` &L 0(`_DIENDB`simutrans-124.5/themes.src/pak64german/files_large/compass-iso.png000066400000000000000000000423541520675460500252050ustar00rootroot00000000000000PNG  IHDR}W zTXtRaw profile type exifxڭiv[;cr0S;-+$K)^ 322cS51[Ζb'>_v6/zMggoϣ{u?_Ӆ^7 ƶ^j .Пij8I}~~۽x1x/ Ǚ;Jy|'aT括IJ; /]:Ý|=^|U9]פSx`r~,]IN7b$JP|3XCn%B%^?}۽t/;bNP0B)!Pw獣tn+v<ɽ%\Y 0  dfsı΅RRQ׽Lq>e8DC!5-tr!6SbC=SJ9TSK=s9,%XRɥZZ5XS͵jj r+z禝+w>yCÏ0H#2hO3L32mWXʫmN;nv‰'|ʩ#kZNt32#E^9R橊eRrS`Χ~g~ɛ?ʛg(u/m fHe@8ℛqv:-^n9zNޒC]kCY{z"cͳ[5Ff'9b#ť99M1Wa&Ln5b7bio2Wzmjhf=u=e;C.1"^ǁ"]{3+Jl@pKTJX (7q>N-!\9(?~Xz.LɆf M< '>F Hs 륥PmZ0z);Bvf`9^+C3?޷IVsnn@s2c0-Mj!g y ʍbU>6 =bIF!u[ҙs"-lpW"x[ʼC#9r@Bۼ,GBA(Z &rT7Z)ڋ| q2ŴO&N ?a+26s!`7Gd)w S}CԖDT%pƠ-td@- M^cA8>OPqxnE#d֘R6}$2}6r;\n9Ӱ3(ODZ&6Me6O#ump2Thot?eB$O3Z"Yleȓ4jGHcb5ck6a7?NNvC sv2-"I0_?$upPrh(⩁4L)H"M#RZp)LU T)g-hKgRYXeP=].>15G /0"B@ ǠI`Qu톌`,;(rT@1W~? @VjYbIYoVMo ~9Ň%BjZ?Ĕ7w n5(##x+S qDch.kb #!H@ګ: a-Fg?A:&')zNQ|ȥ.'~yh xf"$K )='PqN?X|F\$:A~eKETJqtZ਒v^ ޱwFls)gH{ugm_6!zK FX:@ H ڰӰ"F_C4-X H 3`9DtY(PfC}[CD /24rFlz > tFꗍ`Uib"v#we7[6oA00A fDk(3`'PLp)s%i\,FL4Vd$:le b^Jgغav\ !ݡ+" lt!)Wt*|R`{o- cP쟀|DN2%MA`! q%rR,Q`i^'M L v61]&u5 rPυ@ sP  % d͆^RHە(]rє l'x|V&P $SleZ;AhsI2\%Df  d¯S{ (KA(4vJ[!m02!UVTshC/RS5ٍR舞Lΐc*D | {l"/"dP/Y͟AuOi>_Su !i~n9j4%H .4!=ElhUD.p6Fv:/&h>ĻnԹ]NP3@]s4Ls IsPy?Q/$+2Q\wugZz 1|XS0hN{Qک``8 @`AFED04H SCC@&tT@Zs8bg$K&͏9XyA*26k}bU { )nr^WJW%ǵxؐ拚CⶒMw5$C6(1c7Hkǃ%@gjrIBGvJ%qh.#@ ;‰j3CSj3-&pit28[H.ijUL\'@llUa~mJۼni65*)D@ՕgNikk 5T8:-zC! ƝDkziYUelÚFiPsgz%jZ7P& W6'EK[-~K6d  'I809]AwWn;.C! B'mAέ9ϣrnBM9xڊz2$SӓiS 1R삦<U+LŅNmJjmUk'JT-ӠD\I nKϻ;Kq`xy2DjEA2(2!$eZ T="Uk#-ڪL*tTҕuP~A;XgΤCxXVFVt/J3Hooi6 ׽4FL+ZPNP&H kK,mf9 u&Wkӵ<0F/>6 6^^%GJMhAI]D[&* ]vSHM aG"xzhcV+~Ի|}g&Fm\pD֚Ghx^/Iuo'=LkNglrVWaHI B ^XrJg%$#R W:@vxp6 DBt1$ey3/`qN>G4xOh;oGsNjPG@Kȡ,Pzg?Z^+_&"D%,hՅGhXTdLJ%S0;ɻ9eRĜ;|5>Y4К4 AUMk\'ӓ=KT(87ia<66Ug7׿[%1VF ظ,C`%8>~"{e? :s&t$E/=Yĝ+,7I⏹xV<;h}xr={-4nt= qF6Ia-X̶:[Xк'8etuL!V]Ϧ.6Y͜FZLՀ{"Y3g--ҒmRbվ(iQ@Fyje뚎L|q^:9"sfc6_kZbzh%w2%gTŠj+IōւuhEHb\;~; 3Ulu>F GX(kбxQ{X{w]Zio%.Ӵ:H0]\R{mGUj0a8=-Q<ē-jh*8IkyeFMZAq 5ɫA\{ (incV :4فѥot_Jݠqx; ;c-"AD=+-,Є] 2]4``o h>Q C&w w7uM[2Mܩ]{<5{AcZ'JE%N=P3W25 %aoS ؜8Sܰ{~wO'b q ҏ؀9VSi+.|έ R?Iг \\4ytɐOS(3w Wޚ8}2 pp){]{}r݆iTXtXML:com.adobe.xmp *KXbKGD pHYsodtIME L/IDATxyTյƿ@FBLOKf8&+&f׋1y>3KLt#1fE4ai깫?9U]kCUTu}{@!B!'Gg0p>v0+O5J%?q~ gRuJ0}x<%B+o|E%o~v.3,0Q3ob_\@3U7Q,_:c8| O 3B?gBa`c˗3 +d2z1?cQFzD+:o\Y BX0w 4pf2#LC"3d3 rX?ѕ;M<꟡DV5rC2;J@V!Vwyo~f.b'(,qWΟg YEѪ qWV!VL\ɩ VfnX],'8J =O?RP41nt-~s[S?gBaXc ʿ_,fo@aG[\zl.X?[Eh߮9m7~욛kfW8=O3@V!Vbk1yHXcov;{?g~9&U9ayCO~t&Ψu@!`_%`\u KOc&8 z1(?~hsznNp|-Sfwz7Z9?Wa\4 .OCY BXHܩ;+Yo,zN'9oe=K,8{nͺMtVj^h{Irk uD5D=OΫ[.V%Wq@ߤ?gBa ,HS7 Zg̮zĕL6*Z?|3!^܉R*/I9۫l3񟮫,\WyX% ]+AԟB!+cd,Ϋg{ vήa3NK?tK* uͥg}mNħ?=+_]oyyrn7Rtq?5`gB!$ߪըuzpf2χOamKFvGfڏzxb$iӭVMҿ%R=ۿ|Ri~ FYxꟁ@!x˳,= Y3zEYw 8WƞN)Y7O_y+6}T3LY?k 艹qJ2C7w}TI@!:v63Gm/2ԏ}qRϥ7zS['3M̜@Q{Ǖz۟77$LceՅխ3k7o e]~v?B;]E:jw_K8:eR:%Ro ! +Qd&UX >E]woc3Ͽ2QwպC[3?:cMc9a9?aw3&gS ! Xݬs9:z}I?|7}X WqQП !Bҥ=Ti2SziigoV 46|012)LY B+]7tPW 76$WfWrd@zJdQ?ݙU=ӝ;zհzUƮJ@[@tzV q{Hv?CՓ@!>[`G%`IPO@yd~z_q>mj٤9DV +@y?zW*rJeW{8"4~ O8w-V+|Q·;׋&qZ*oVWԉy7Jop\F_w :0 ?ҬV!b%=H8C/,8P?Tz:o8B>Tw2u&'c[WKOdg`(]W1Qs22\OlSn*=rr f~@;E!N9M 69EyaQ_X's*[L݌ )fׅ;Efk@!`z{ iK2ChC^Yf:#9Zپ@؋ 6,rTu=ǫ^7O_~cϪOSDq *UgHG$RNPQRki4 MQ$5ugHG-RDE&ڪZW狣9cLtCJ|ş[ {]9{D2Y B<0?ct*+`Qdb4R22q *tŽp駥 T2uB/W;AN9b˅E5sOJo{Np&|;;VkEZz pMRA8)x~\?E]`M^ d+ ngBaymu̩rL?_gfUlLJCӐ  +ʭxiQ3x392|G_?%rFԮ2Ɯj]@HeǍكJ~#,sewޤ ;^U*eҬ%E? SqJk%ĉ Yj@l ! {71`EmNd}mFJd%Szqiַq]&=ϙH7Psd5q=J/Ek'#1\%`2S-gO-(:_Y!sB.eVWe =B!dVZ"ctKk >:4)^W?⿼$1Gd%Qx!BdNSB!x*׿W^*qd'XvMϐaUj') u1 !MjeG32n_}5} rޗwFY}v\thN;꟦P%ŗvQhLtj@!xǟ_3>ң{f݄诳sGwcn?w]\$ǻY B8nٮ1^-GW9 d|p,GORnݮ9m,ei@!=zfYdqĘ~|㔏TO;S=Od?+whG oP? !B\aWoWX\ G=Α]=ҫ[g_gߦŒ-:. dB![+Ouw5{SRǷ% =!#Vьr^߮/X<'Ciz?+B籼]U%/{,Ǖ#{$׉Y7Lx];RG?gr}֮ߛojDY Bw !ARβT۝ξ{g.qpΚ/Ȭ P~8=}WM`U}Bw7[d>.w:B!s;^eygtq絢\2hgSg/:wU4e2vyfG=_y{˫/[@GIeT3'~Hſ~;qvt6?+B+y=ؚKFWMo.Qϩo\sԨ Xe$m 9t_?+B+ѳ3ez,{+g${vWtJOob͎ͧo\`OLߞ?+B+UcZҁ+7n?0O֩9yGWTI4MUs HzY B@U^wFZ֟n ;:׍}o ྗsf_t E{ 'lq0oϫI[$ym%s>51@ ֳ(ɳMi~QiYYʦ4և(43S̬dN-2Z;9j=˻ j)̩Z_! ̩Zg}x `R-iu>JRuPZ[>lHͩZg}ؐSkm!5j. ʩ--roK˩S=ij+ʩ--roK˩rj+\HDikN9R=QښSTqg#U9HDikN9R=QښST}O#U M FQ[N_2ɿEm9er4jdP˪R3PHEUn(ԢL7RjQUj )*5 ZTBJ-JtC!UfRmI5?84H5?84H5?84H5?84H5?84hޓɟ<5ny>}/  Bװ[ޗplϭ}d @?nwJBpV'wv 큏2r#`܆?oס"Od}Ax KK!:KZ|mPyJK++{Rk5G=Y m>'U-Zqab!~k Mal `S06) Mal `S06) Mal `S06) Mal `S0a"U`IENDB`simutrans-124.5/themes.src/pak64german/files_large/dat_128.txt000066400000000000000000000002551520675460500241370ustar00rootroot00000000000000Obj=symbol name=CompassIso copyright=FrankP Image[0]=> compass-iso.0.0 Image[1]=> compass-iso.0.1 Image[2]=> compass-iso.0.2 Image[3]=> compass-iso.0.3 -------------------- simutrans-124.5/themes.src/pak64german/files_large/divider.png000066400000000000000000000002761520675460500243730ustar00rootroot00000000000000PNG  IHDR@@% IDATxA 0@[2DDžUpǟsk6luj@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐ4 h@Ѐm=IENDB`simutrans-124.5/themes.src/pak64german/files_large/editfield.png000066400000000000000000000011721520675460500246720ustar00rootroot00000000000000PNG  IHDRݾPAIDATxIAQWĵ-!"a=zK Wk' U31se=aZo/'y?r۶M@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD|qq?^q?7HD" D@$"HD" D@$"HDܶm&"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"H~v?zB5 'D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD * ;"0 IENDB`simutrans-124.5/themes.src/pak64german/files_large/gadget.png000066400000000000000000000027151520675460500242000ustar00rootroot00000000000000PNG  IHDR@@}g_HgAMA a pHYsodoIDATx^/c[qD2A]&.$a(ƥ-{"^/$͜pZLy9֯|nɳW1e>>@x;Q\^nxQ\/[{r=? S(>Eqҙ:>>d2ggg###\Ls|M{4˟wd~^{9(ׄwxoç烃ڎbN&΢h-N䎒_pghkaa^͕&1<> GdJ˭יOãD"f B lollXvem5تÿ,Tc^0^+ff[cdj) ;,2)h}}Unooޒ){2 HC;<55xh~+`7TX9U`Z5}cxGOfw#i;UUU\nww.(?SPmM^ooSzxx?yOssssww&3t NpC{;>Lʩnxfo:Zoi,%Rko6ւ64 :==}{{Z_Dbbb)(٬_&C\\\.0=:XySH7t͸W WcFyySRx-]lnww|Ȕvm%_Wnas⯰(b<_\\4W2kc4|EEr:5w \]]ېj+Һrw abq‡:p=^sjj5:WY(V-\QUUUڎbCCC;ۖU(s__zxMƴDin5ƿ}?hUޏ|iA[[qkCCGcGJ MMSQrozOU+0CK&|nvέki;B eݽd؝|ӗ¿_pg\Z o?~77{>=a!(C3uGoW> G_>wˋq}L&Q`(0I$ `0DL&Q`(0I$ `0DL&Q`(0I$ `0DL&Q`(0I$ `0DL&Q`(0I$ `0DL&Q`(0I$ `0?~CeIENDB`simutrans-124.5/themes.src/pak64german/files_large/gadget_new.png000066400000000000000000000035421520675460500250500ustar00rootroot00000000000000PNG  IHDR@@}g_HgAMA a pHYsodIDATx^ADaØ=6Ā!K XƇiw+=}$/&~E|M 0h Z+lb@ G1^(&鵈lz~)NH+E61@sketg9XD]ѾD` (clow!qG =οߘX!i Q_(^Xj}M|<ɟBHW"mh&؆(^Xj}M|<ɟBHW"bVmlzc5IWT7Kh=>i8?.h, o&c}AwEAƟBTm4)uC46vh?ܝ&;&(\ݬNJK9khL%Q|'$z"οBh/󗟿}Ʋ`m+6]XqV % Н =޸1J>څ+cfq&<t |cMa cѬ&mvNy:|I1KLёhiH9g%Z yYTp\DDZ2Gc96]졮DY o9to0ث([0-<,X O8K.X""-fg.PW\,oXd ГUg}M(ҎG^""q/~ 9sJ Mb]q6E1$<$ڛP63DD ^4?~{]7x~'tyߗ~Pm: ~co\ 5Dkw љ%f$_TO[נ*>X 0'DgONe(~1SB<.ek=sz_!ʀz`̘J 'S`Cwxh? f̌$p2=9t= Qh_DAi(1f{gPQP""-`{U Sz}H

ϟqCn:D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"H^gϷ>~"eZ68G@h-{#ecslq=в9Z68G@h-{#ecslq=в9Z68G@h<×D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"yzn.9Fga$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" |+AwIENDB`simutrans-124.5/themes.src/pak64german/files_large/ls-symbols.png000066400000000000000000001026041520675460500250470ustar00rootroot00000000000000PNG  IHDR@@TgAMA a pHYsttfx&IDATx^\}V{IUJP@"" *{h,DfMiyܹ%>߻w̙3Μoβ`Adc QRSr'D%F%*;)tZXd*‹h $B'3]- csCet 犢[V-3llޛ;bAuҩqj:,x뇯_vjWMSu.JX1Я$fBXcM+ 8mty}EdiC Y&L+Hs cl"Ptwo<5dѢE1i͠IBjѸՋv/[ȱk7b´9U5OvƼ +g,|ţ'=ti# W/N)DWh`'(LXV2&cՍ9MR˳=V'ZQ0w|6Ƭ9kVWRrv_͖ɗV_a~r㔈"ɹ^%+s<ęOS[4j#&x\>|w~k|IB!OMtXͱG]NaaaZ_[ y= ͭXCW4Y…7v[viukV^rY3+-ng- j6k?w骵w;{N^70j/0&OUCE9zX슄l)blW8|~EҚ](Z=1X+mdb-ܹluҡ \=1}ބ]FOw*J6گ4hB 'uR0.uFYQ^Es|\MVX =P`=ݔm~{p\~-m^m+nmXtgҋKY|mŢg]vƌaI#L=2J'MYRU~% GW6Xv˷ϙ{VչyuO}{YSn̯PW~l]K*y$$gVTܿgV,::g,_߼vBE]QqubRCs&Pg lc:팉' (0:y޴3^;6lsE/.MBK/<:ΚoვT}yg5Yޱwlu^6QWe4zCQq9 6X̞>XSͬP`Aڪln: ֑֯4Zʪ4^Z=j3wVXIgM<01EкG甗/,05Wi㫚4-ؿrB혲 ݤ&!fQ^)V&M4<2zՇgҎz)(kh2)iNٴ4qFI~25]ުoY|7powߧz񇛳1{Z{ƿ{;K5_O6_sFW&z12=?5/o C{] 3"E7C`-նO y.ӎիX:Ѫ+}Ut|xfT;<#9/2l̠ʬq)iAQQ]|=JsF.9v„8EEY;|ԍUݝCS]$h臻fxy_kG`*$*jb0Wdײ>y 32 .(wq<8Y?z'O>[э'_zo{KOW~||`w~=55C?_Öi*05n{Ei#>ށ}}Cqq]DTr@`Dְܼ KVWOQ3|^Q+'{Y83okoMnsDRVɐ%C^qiG`J&"NiBIAƦ-FO+;|me鱷@*ɗxG7>w~'=}x{~>ZE?Yg~}?܁?l>ۦ_OaWg+3/M[[0qS#=,E oZ<Ձ˛l@熮rHpQl><ɚvp`̓=}'?~>2O}l/-Ҍo>6[~83MZ5빱_L/KtQ' VD]0|yUTmi`YԂуtj޼#ejN7D?S</l[PUl#~(0|4H$CcCv@5sLk.H|0/ʅk.9iْE+d$'D HqwJ /9shg؁CE&9 =xj䲑EEs7TՖi;tDO$̺iD,4Xbcc`90{u+V6fYmCB;OU}L;<{[\;wl؊cMY2[9U?]]WxO=X{ٓwT7;<oro/xjw|4C-|a>]=e}s5&x)~t1+ ^ٳ.uGytdE%D]d'"۰LP`2  oxwSFAn6҄E[{pPc?O2/M*VX6kϮ떎+)OopZbAYԘ~#)<1U9KgT7WΈI0cA뚖Ϛ80jŒr23jl | ,B>9{uOeff0t='Ϟ3iWX{`;O)d2cBƦ'vMpWiG~ᝍoʡ/~姿^߿;x<`V>`ks_󮼟6ez|krt@u)1kؘ1wbXv\kP`2Vp':1 ⪋ Emx a:؆Fȱdyd"0\tm|XkF ,¸%ZLV+W rԩ_~sןPltɣg,yƥkJoܰu3DxyGHL\=kӆM rre*6bVԘXľ?Myv<UCDplU:gԺ{OKlh'djBpt -v 2LNzX;Aȋ@Ô)vm>\Xuj5jBg.xIaa6%Km&A   Z K1Za FuzaĤN,*y j&H5[3&J߶uϕ7Ξy+3#GEMb`j;r?|[=~!5Lie`<;hhHp9/1Lb.wSX]aђP6".-lȑA} MEX;+0U?xrҰӏȹbҬ4u?Z|񿽻>}xo7~O>?}x.>f?^Zۻ6GÎ'm3G[?\w~~pW+2mTOW>fǟO`wh lڴnhڒY88de[2sy>|ʔfL2||ACj\|ucV,]?u~ݻk熵Ɣ/,۸z[ɃCssM4uNC↺͛Ν?շL=/?ןZ6rOsyI&85ּKUGr3ϻ:}j82dZ!%-ZhZkC(Xw5V\l"d!g|f'TCxmIe:K ]k鿃C| u!=pkIZ^%](Vnz||zG`_ߡI%|?xpr'O̙:q95'=v𴊪y=sjf-]0{ʹ'LJ: ./>1wm^utL=y™sWz))[*L[g޻6"ktZrIӂm#=C#FX:LS[i=Z/h|ל€]l*ܷ5]N|u޽5c??ч~~{v>o⬏׎<݋Kw.:kֶe|S_?XP]\ rHp^#dDZ$\ZEKm:߀xc 9Rꐩ u:Q^K_@`BUNA7SU7^ȀkX;DWt=PߐYcN:ѻ^?y~cV.Yvd߁};wnZdewF׮?oO=k5 N6o+opth{)ۚJa $.ͽehqFzpK2ТPSPPP)XkhkEF:MA}wTDnX;`OUwN>.~]^Gr~ӏGj\忼|p/o{pᵵe̽ ?9_Ǧ|vϷ~~Ի'nA'wN;i@vHg3 $(v9#dR6+5oX{"4 ;<o k1CF&q~,C@yȧm)JK`Q.2N:jiͶ\_ntи9ZV.57)~> Æ$4oI%y&W猜1jC3g,=k|aNֹ'o޴ίo~ 9E-GA_gaɗ%nP=}mllKVn޶D$Whw&0J`^R5+u G(#ζnpؐ<\Vy̹|}t{zu>^Ӟ_nyف?\_W}vK˯{iGxaG]\ujv 4dc 6-N-X1# Ci㉔U{g[k">.O !dRBnjV]͝XF1vᚌ EFE@iRLPmo !xLk7/_7<ۯ*Ϛ'[DyOr20ҖyGߴ'/8?,?ZoEme555#S "uva"m8l Y3̜sl*l5c?SՁ)?_m~x}OW=>ǫ}s[~oO7O:3'=>=2‘וrpjy&uŒ~_^ss6~ȍwKm>Cq{WD( 4陪Ly#+'2L;e4,>3p}g׮Vbdd_Rr;A=} ̛]7cu˗~w:s.{eCSFȏgme\W߸qyʪV^6U@'57fO8+!j఼ѥS&Zjg~.q~Yn{(\|M>?H, h +Vm:l16b[~Ro/x]g:1w7rq`M>;#?80v͸Kͅg ߱pucst^n% R5PEe#? 7C`:|i _mp^)d8̂5DN$-_Ϙ6KXV`a@qş&}~H븺\`Ԍ/kp tsA*x)iX3:2a^ǤA({!PH*S< &-uTYi+Lc³"GͰs54+i\[]l=:gĩ{V/\>/~9Ӳd*雤k"pcXxɜEK ,\FL1250u;h8ŞZ"]Gs@# ?gN;A'vi`d5. һ7_QUKx~Dڂa232;é^ƳY;A^;\}E )Oה~~`K>߾?w4ܞ7kg[|j{ąAVs;n3kW"rx 1uj*)PyEs1j )BbV],7' @z &#=oΪ#S0/kp tsW!a|)+0W]l(UX `j HQ]z&EΎ<1wt투 N\=[3}A}ÚÍCKOMI ZR\S\4vxq{6wW% `h7QBJOw]Xba{pe ^ &:A"]gLj愀m[۴rQ.6̏z+xFj<ϒdz HC%IښQ撉.3,: l=i~;#N1=TA)-˽4+ENͼ`7[ŊEYnSY5;ݩ,nc{~:!X\hS zW>vڹzSufHF *ڪjZvkZUޙ^roc/8J7c"wgCcK FEikX/3tl5Jʰ**(h*9+0=] irO@ڧ%n3j )i׳~ ndx{:W;Dar$3?Bpb4ZL: 9]=D(' RCM!f *)d!|=c랳sM:!aHyu6=m첂9qj7MUdc瘕/6D4@lķRQ7 turX(Pbmd.t46% KxfV-/mذssIuퟘ2صOtgh}}f&TRWY45]BX-Eb:Q9DѰhieS([Ӝ}lޒ-فg+z!z;u#N̸Y6ꤢON eM,آ[e_V}\[naRNVY:v)gEΉ4kМCj2sBٚExxx akGS*UT3prv2w⩚+)I޺z ZN-!i8j8TԝbM't@b7kHF!+~~t " R1PReSe+ip6h1l4zY۽^kc ٛoOn⨳ELI`Ѓc3cW{bW&4Fmi-n|v <x9qwom1ά8S_Zei:d,m-[ MX, x{ o..0x$]eR{!m.}J5Zwu*|a)Z"1u\p#0Px+0uXgc˗y+Ե24I1pں& }z)ٛ[G[1)8piiь %i&孫wd٢5Sr|\=|mF|]oe*FA4PABKӧO뀖Cf_ D]#4X7 míCzv `&:T ]@A9Cj˚Y{oqa]ܣ;I(n22pM6wOJ2{ᾱC_pvȠooM^zìW/CEe`3eܔcF]ݵ="m-] @` ŦVab]Ou tJ<(*XmtP=NM`'I SQǓx*j:XYull h*(IUuxfs E"V|~baX3WŸdUc[eSfidP=l=kwĀ[#?EAҿ{ݬoJ.f-5o\r/ɟx'7cǪF;M^ ub%6vv֕6t%eqRHcC/?XW F.H6`:SyimNǁ h(pK^U5؋rJ^2 v R"0ng.H^uD` "V9'Z)BDT( +Yncj'0*q\HT^99N^CR-$|G O}sk/-{rY}vi3Ɩd%XdN ĺ"*JjֽiԫHCS[T媪adŁд^EuqClf' -/Cp}0]W<8@]QQjɇG'M~H D}bQX-o k,j;pb߶CkW7omRJogHʐ􂬴 T[S 6*:1_ahoijbk/ik"g2aP2b"L֩[Gr{([jX]m],JB$:- 8@!*VP(PTQU3RYkj ~bXb\fX*k 4ծ62hzS }]ևv<~c`е>c?:CUAn |{X9Yl3X:Ҩ4[,(3ӟhm>Twp!󼝊Mk  3ԋ:?2Q..V^p!Si .ʌV+l!~{Kh=?(FBʹfq?5FD74a?I;r\k&#$dlT<\w3BSٞNVƣc,jMs A`7cP`85vp+tA_HZr *@I/KbE>3e֎*C]ҰZ7)j,¸3n9M ]ߌ'6ԤCHM=3YvOIN=wjqȾ~)Ɔ^ Vsbj WDC0E5%U3MYHHlmmᅡG}ġ}Çw?޴fc sƎE#5MGm}/c mHKjG]ȳ3 KBMPQHcm$FI =="LSI'xZ*jg=k'\t&݁awC ޽UWsk^ZfwN[BQT Lv:ܣ6_rڈ煛$~yKTe (dGA35mOUupߡ?bCNY<U#r'd46K-B M|cfh}N8 ?$OGHHL Nvt;J -lC]<x9E;E*()XRM@S-5 J}jg`i`w8sjF0 "%U#uuGM>~X%eꌤ3 u5ZβқkgfQ_~v{]Z{PсoK.Wz܊sa2W{ L )F:K}'660NqKӖ$jikkMvm_hkױ T  B|FDj piGnn6j PMq鈺U`G_Ɛ0EEa\~ayE' PNFw,( DEً 1o479%0ٵq@#pI[}Ƨ~Y0 tLE .YoUbOwv<Ԭ=}#<(=cHOTC{}_?}>~_.}y\Ťхz}O љ&f>ŵf>jb'r^6e~Ond"p2 rIv %=g&TVP %" XC]ʇS t 45!b0(OKR%Hj u-675vW}W#7;Y^s1zu9i{iYqInDCRQd!%#E:Z# e6Nm-Fhiv47 . τR.LqWpCKkQC{>5@ .ZF]T "0+-[ FG5Bqq2q0bZ BN0EpZnq w):@R-0T!bS`M޵zvWa(u hyXzO ofK,M<%nzQUq԰)c% &-":O*QUQ55\H`H\5GOX\ ՚Hg[pnor^ NVls.kpS^o;|fJ[ Nv&Zl-I(K".k-LҴ$qQViuuP?uL@OumH_ipBAɁ-KWlS\b"C& 0(u"HBdhl0EEs*ONA.xCvlߩPEՈuacy/|Hӵ u߲~_SӦ{^8wuڍ9'Niw WPu8rD΄̺Y -ZQ9njj|^O>ǿ;lKoߊ3Oٰ~Ƕ{}TB3SXd ?xYg2SlT'VUVl#(LQEWg'z Ihm|v@0UO:CWw<}Ӎtۚ6og|ݞm-8wqf(Pl', L l?P?R$55*0З ԖI3-+lLGv[0CI!ZG6R+05 6 )25ԁ23F \@W'0[d.FJ`dN1En TC!z-$@bP X 5 f( b#K֮wN4 p\9^^ '8uS>9\Qٔ?"h&+wkfV;__~>_=u3g.OO)23v3~qUԒ5cK˧V*SWM%z(aܴwTRkh:1eM HLE !BKph^^TwXX"Mў&n0מoalmr" J]}5zjmufG=N[dҰDJO{nPc(PP:PG~?}}0X[f hEh[ijvJ` % ePG"0(2@yWO \E^𒶚EJCd"Ϊ@F BV])kƝg. >Sy]0I. SWpR!kB[ݽk -7O HɪY8~/LcgpƄs\rO6m6a\UKqQԩΝ9~/|ߟO>SVw OZ"62KL6}vA~iaaq]ݜysd fd;*pWHM4(i MB-[+0ЕZoE*|eXEU[MH]ZSÁi4<@]bqX'l$FK;EKkD/֘H'kk/6"՝fb0ؠBOlV+&kͶ[5c?HZ%0ɐjO4n#&KôEbQ?}iN@c/wP`q2CS2» *>vXv.E o!SáWVWDT$/E#.2"iAmJ]!SW"0f:o[S&v:'<+$pc0pۡҵ@(cJ 0|5P3q;et{' jih4nQ ׭駟]^Qޔبi ֭^yȱ?_osg֏t~oO:+692(4rFN߼yˀA~Sִ͘eZ+HmP=e}>>S+0޽*-<&߉7PWj<~45D $@$rA0ttI3iI tʴ<`~P?_GgP8\GkHX$6n4]fn4x(vtTg0}=$PKhb&:DF~@iXlb`7P6zP`Ct.:~C`bX gj )u )SP-PtKɝ,_9t"{:˯ }!Q( ?_TW(ciM8.Vиbիwltg,=(RZ(/>/_/,*,mn^vɷ?vxk|W~GhhT贤a~CƄ&7<&&<88%wTوc\\/>޽g뒥.\RZ2aM7n6u^brUChCp/HPETUՆBpG(@S$r]55 d-I0VXi#al-ΑD80CW4C[TO5H")ҖdhgD$FCN>,:E|fa_ERSSGCw # 1\Q"ϤL +$֨s N<+2%m ;0lA91C}E8 # ]Zrg\ڸa뎝fiݸuؒR?_~X~ݘ1.ݱs˶os7?sO={Ϫ%-4vJRtژѓ$y M 3(>~CɣC*'M|[ٵkϬsW:uj۶m[nӲ%+*x'iJ`pR78ʽ{V0ih@'|@- 0M~vNXr,aG_h}'-v㭛dWZp}Ͽ|wUVi g5E3u=sN:~̹KΜX;mٰՖQR#?mƍHEbH(nV"ĢD$^$ŢtfJ֎&DCt$C DP$H$$b8D$[ 9 ĒQ6t&|.O㹋E7k+Plp,2`(t D0}=]i+"/.B _:63v-韓 m:cW \*][ ipSQq"O"@?nŪe%vl>s9Om⯿~##+?_CK,;t}ń<izV3 n㪧/afal'I(5jJzj< EE!gSwk:hEx-qD"9B~Xs@;W+7ImMgzZM5rh9T3_bbɷkhhjK$<5 UEKlΓ5%bn ]"0x$o=,1j8HWXG])0fp[UpO0'p[%$^.Klc!P5A]O㕗7i'?ߑ#G;W^r]w-_|z %vovxGܽv֤ĄIӧT͜\Yq'h#xɼ L1?u1vRĪfK𘸂7YѼnq a*l9p#_|Nޝ'O,9o]{ڰ~kqѸAIVnڷ)aUW069978tB-(4yQYCDEHpth{HMm<- LwUWoLS`) [I$:#-KMGIED$xtSjkiL8I,HN2k* 39gSk xIqH,Zy<X7gJ"IG&倹[>nBڧt3]&0fn7d%a }/q9eapۧ0I{fظ贆-ɃKB>>cܙdW=##?9 ?(ԭ5[$5o/\TclG3'>S,`RlXf_"*s1<_ [9jKt$u!Y!bPKKC+nK85)v>^NcB RMCx;3%$kse%[(vAF`̄w+L]g2p]d U(L_j+oصk3޾r W/]}wn߼rśw?|7}~'޻os#߷+GX^3}j}C]ǚmZyysTՏSS;m) gN/<{ 0Oy<f?8\OK(L3ҩp1vGXZIon3RŶd<}D1a:Z6:_(T"ez6fּ0>]ag$QSq3UWVTUdia .(̀I*1?A^W6xx#v7n;Wc]8qsNx[օO߻ֹgNܹK._8PՃ>eAvх vl߿lkwpYM31n⌂aCR="#S[SQ@giO@5TH(, KKb%ZwG'h}mxB G0X0VL-[ihęxSe9^6ty"OK }Ḧ́-FkXO Lկ,sQ=y1 i^Yz2ȑlwfM9[g/ݺzؼʅs7^]}޻w>zO>}ɇ7޽ݻ޿/Z{ >rm{7l޳iٹSJ#P}x&jo%U5G>K,j3"Xȣu$>"QVn&g2unϋ21v׌3 EB*,z+V\japp\hч2I%U5-_SEQ[WWy<+ț!E_i؛Lg@t I.bMrexA+Gj];/sϾ}ԅ=߾~|'|}~p<wyܻ͛{/}ƥ Ξom[[s~QCG$ .w57GW`ڪj<&M(^Abqֿ)&  Zx=I]3! g3͹.1:$QȊ/v+*i5FZ(Xkԭ54 ՍZSz~&סt3/+OgZ~(|SƧ^xMv>uԕ#Gn۱՛^;k7o^qwn}[׮^|+օKΟ;sCwؿn͎wo޸w͚]'էe4$ 8:itD`U O݉w ]-7L, 5Cx0V$e_,NW;M.k^m;JZo?^V:B"WU5+(骨تk ʌ'k#hD(4iOټ{juAP.^4ҦԮشĖ':~[vu+oW]9wܙg9sЩOx=7mܾjvlްwiiO8ٿLj<3VCVCE\ֿ# k&5$I&fMkufK7/61TT2GcuI"2X'@ ybq'D#nt9#>KQ@^L .Xиyck7oZyv:~ԉC?A8Gl۷cܾk kö76.^3ae٘!)~mqA{+ T`5&RV1Wk}Xx<x 4VLzxxD{To~@4RSP!Zie`mb!PRr#vC44[iSlM~H.#ŒSWTn>ED '!v7Tz8qGO[2q[kV[z߲֬a;Wھr[oYrsҵsk.8qĉ R'xz{KFR˧Jji5i𘣡Y"-1"詪[~[$TVUV5+UE>zj&&GY:B5%#+[q-mKl]v^$+fQ0OO6Վ-P^#u6Exl;DfOxPM^#D^n0xW])gwA @:O PFJ{ufC jF$l@6mbx]Q"?!è_#6&0nT; x_,T;֐nl uK5~Xv^2 ܍qj5¼ Ո,@D 2o 2n/իcaj\ /y1c!Pv%%kCC^.~ >evKt`jpB5" +3@e V;Ch_$T; 7 kPC# 1_!$TD֧3_e{CA "A %(0AD.A! r AKP` \CA]&k [Oc%} 5yUtq:qLz+<PnEl-n[-ܚT!)U=&)O"rj8^򫱴`*}rQ 3cmN9@ߌŶ[BUJrn!rLa}{.Jo4§B,*1.ɭ0$'ҞڋtlH@C GnD]\ +E*G {PT#jt Du؟ 0X`/;H.|1v&9ĻTa}m*S:HG`'ϣEj8rTWNاT5R K1bbd>á{ @6V`5Ydž);HnWcUM61 0N iMHv/s4A9UdN>O9pxP{VZ,TY]Bq} *Ceab!Oy$\1τ&w7JcQ89/)=fYb91Uy0PÑ;Xiɻ+0,C !^:  g %C@ƅ"\1>0wлw!qIn,GF;ɨ:= fn~Nf_^fB(!wmBy C:*U}<b)V`mld \`q,HDH7P .b2jW{uJ:xP{ebPdN:HlD`E® r2l tpeR=%`g^\H5.m )~׃"4@z46pSBj!#s buI)0UNAE/z@7tZyx =rj8)W%oW`/T@! 5g1mUw@ aDDS^@G{F '5dg`'+RN*vP`H܆:-`/x*#%0 ІCxFbw X &8m஥(g$B%Ne Cz46p^"nCBʩʈC l6@90RH`XoE*1&CV`ܣP`Hh@~C B6P`o*\6+::-d˹GXQ1ln5p. 0)YqώCz4dPyy@T6c[P`oڪ3RߵuZT5VZ5=:<=0x@nC !?zTeDU#4A Di:_g)ryd7{؋m`řgA!=rj8]mo6 J/F:886=iKvڇ 0R=5) 0PZP`o<V9MamgVfuŪTö=Gңa< ӵx@N'X(a:x]m!6 Q {Fn(0Gx@nC kA"Ae mSoQyfU KT\2'vس# 0PZP`o<`3^le V 61==#uئrvxD!=rj8] 썇 v(l5RT j {vFh) vңa<  (R[L*<RȜNaT9m@!=rj8):(0[5 ( qqLڮK OV`,t tm H(j%4rzuVcHEO:7ңa< m5HYW9 W`,TS{"] hm H/ AlJ♐:,o(k! YBne,T  5 ,fJ,li=\u]ܣsQ}!=rj8)3ap7\ʹ%ƬlX\9;xoGx@nC A:EyBV3~H9wT 3B{A@z46pS/0V+)<:i@[,@.l},D` x =rj8)^lF*y&X_8jp K8/oGx@nC A:W`ܧdlؽ,oX{\-.xdEOȁP=# A rj8)XCm3S{@|CX{Q ֎a7ңa< QpAFa rW]\{Qc!r;7ңa< cKC!RHxؒ"{)l  5X[?[,d/ {G=ؽ(XuBJ`l,P7ңa< m56@T1XD` p,) J`,V~Ø+0u&oGx@nC A:+0(+ XBH@i@o4GD "6:lN})QvD!sa<  988DGGX 5I d@VonV]ܠ `9mM0PANAzx陀@laN(S>QԸܧ\H99mM0PANV`$@?b?Eo0'2eyJZ&sڋڂ7ңa< }~r"Xr^JEr(.2H܆t "0 V{61*F`ms){MACʹL> ooo@W.gBMY>Rrꭷ.}c.wޅrL UYʀ-e TW"0WƩ^lKm:uq坚!/0&< t%o#lC ]KgKz\~,z} ږ,紌,U_WH+C bŮHnSeV`㚯מW_[F em,{1 -Buu5[.ok _#b/X}$Pe S[J ~~ OCd %eLm,{?%.ؠ-S0"q_ H@)@^䑪#b!TMY{S * t2m,{!G^7/'dK [֠qUS6a?6d%/c]>PQy?ʌ:J%g} TeIʀ%!2 u: 6re+Z~q0^"8)t0Q+?b5.f^#IS B>W:2SAT^#@9;`٤Q5e*]{)P+i%T9u ,܋ ŭ/9Sr8\]W-eP`5_A$?^@7kdE;@՗)RdFCP*#)k@W[[dWOa,=몶% 쵦^D`dFmR(0̫ꊨ<ʗ Z[B՗)@Tw%?b{q%gBnkv,몶% &~|ku5lYq_ m6U_W#}gvQe )W-eP`5#al•쫋@EϽ( Ud*G^)̽Pe#{[,%"! bGȝ\-Db/xGuE^V]7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~CA!0PAAz7xA 5Ah@~guPP5޶/kPm U_`.# ?E0t)2 ?sa0LLRNU P\F !S$;QR3Oܞs,ܮRp,e`0jl Nma2~>Qa.# P%!( v,\F a'JI=eTU_`.# S$w CݖM0 #(\F )} K֠zA-T}Y0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`?| 0 CG)`9z"IENDB`simutrans-124.5/themes.src/pak64german/files_large/ls-symbols_stops.png000066400000000000000000000034601520675460500262770ustar00rootroot00000000000000PNG  IHDR@QgAMA aIDATx^148û^aO+8F !S,Od7Lyh5/g} @%m @@g7S 3 @OYj$@lX @xzY @@,  @<vR%@ 6h/bG~}U1F7x~Ot{u@ Ͼl/pDo>#E3lP@ 7_|kk؍5=Ok 4weIȊm"2cR_2oҸƯܱx+zƟ&_~K^tWgl!>.]E-cP(VEc~U1N3a4mLٚ]6.A ׋/bu:a_ly6Ҟs[gO1/D¾I?R>8vFXٷxUu;{n6PF  =nK{syilm8J V.AU0Y:f'\8?jϪ[^oBgQ-HduV7 O۷;|V&vYHߋms+^4 -Tr6C6M5b|>AG勃ouxp-uoՙo]\I^ǐjty#­:rjG l 5f/b&?.;%XTr|GԁqWβ;yZ|z/5]m׭3+Tu@ӹ;G@2uK5fx[h>]W`3,nk㽖׶oy+mǯcu T}ޑ2o,;O]AH6OZ~捧PS'0Tϱ`ixx h[ 8X9=16Rov]i_c8yKy_ڏ>?߅l| /ޙw8>6lz,;՝wmQl3lUݪl}׵[k{9evOOZrv5o8/g @}NC`LMN !@ ozʦ D@ ` 0@@@o2uRܴ_GlOIENDB`simutrans-124.5/themes.src/pak64german/files_large/ls-symbols_stops_new.png000066400000000000000000000055031520675460500271500ustar00rootroot00000000000000PNG  IHDR@QgAMA a pHYsod IDATx^?.9X;-<1NDBN@`"xj׾nwm{JSmu= l7`33܀p633[ l7`33܀p633[ l7`33܀p633[ l7`33܀p633[ l7`33܀p633[ l7`33܀p6ӷ^ȄS43lv'龉L8Eni_!̶l\h9NnD&K!̶l\b9Nn~[ l ?fw΃\verN[ l ?fź/;m%d&hPfyU~+!pu^"iGP!P^_Jd{1eL).ee}#o2#dlwJǾ\3_G 駿c|1,dl)Ņ xX+3yn_Bu.%䋙d%/O@!,?5)"?Wg%nqTfbcWRI|⋙ŗ>5oOԀe[]xI%ӧDHIyH68V.!% 7/Q|9 )/uT|1]rR CS l̆᚟"ϒn![/$7D68V.!% 7>v< ҙB|⋙ m볞ڜ(`T|a/܀evK*z)^r^ܸHs;X"qPLx6^YhmN{0*E07a0J2qNd&7n+RjRm@>@I}#/.-9ѽx@>iFp+^_qL{חO!):W`F!E" G 3TddY݀9ѽx@>iFp~OK΄.3]f^(Hx|3]fڣl} np\2Slz/|2&Rifmk?CkcHxYdLxz8S.E6:PzFksڣQ)r 0$Kx̗W/JɴG2:+) UȻpū#8| &^RF'^'o~fh(x)i yn! HjG<Wd5рoRd|g{[3y#8?&/t )3yvn[5|G"ɛQHy lEJ3RY|yš3vqnW!>e΃mr&o|䅎!!Eaq&̀p )AH5oy@ޘB,龉L037`33܀p633[ l7`33܀9iiNdIENDB`simutrans-124.5/themes.src/pak64german/files_large/new-symbols.png000066400000000000000000000022411520675460500252160ustar00rootroot00000000000000PNG  IHDR hIDATxۿeB4B d<"M4ւ?jCAEHk!X ]E,82:.֙}owwNU 2TgU_jUU*iOU<>(~TD|)ORnmTS^~9ڔ0.kWKThm;vo}qT[$w}OLzê?V'<=>R[Lu{埣hcӺSFDٙ.˝ 1a[[f __w 7jx (oh1d8|Z:P ]f}Qn̯|/P._*^<|.GClj/ff@_uٿf@[7?f@{ᕾ*h֟; QeZU!V~ XF07el~7N 7i TUt޳ڮP19"r/͕+W>NV4 oJm+\#c.J5?9?zI4Mi)G5;tN5d !4B`]٬.W$bp4E_ x-{/Ziο,Hx/@]J W`㭍ښ ѿ3`)}ҩ! .wGw!"p ?ch=gȧOw>Sx1LBD:{WCIӗw( -L_ޡO$115| on2@:/ëaZ>=!h fmx-?sd@-Oc4Lhh=LqQoJh8m=p#n}W\dۤ ȀZh!cePS,6X+}U1Wg pZN^ ܱ.Աn.YkYvmAle5Jj? a>jA˨F4W8'pr܎L\m.K ȀZk80zP~~s9P q,=Ԁ61z`,fu`.K hcCg g%/.mz/ H[4 mi@ҀiKҖ- H[4 mi@ҀiKҖ- H[4 mi@ҀiKҖ- H[4 mi@ҀiKҖ- H[4 mi@ҀiK҆*^1IENDB`simutrans-124.5/themes.src/pak64german/files_large/roundbutton.png000066400000000000000000000022311520675460500253210ustar00rootroot00000000000000PNG  IHDR@cK pHYsodKIDATx1kAfbe-ER%]A !"$(TA@,ELjL2L}dOq{^]/=i9oN><3H;gUI?uklKcw(7`eL( (]vwQD`,Ԁ ? >/ݩ4e$`w&tEOQ-PUx&`!`&`!`&` ` ` ` ` ` ` ` ` ` ` ` ` ` `) `I=v{l[GU7~toiJ<ۀKcw(7`eL( (]v}\D`,Ԁ ? >/# hTHܟ%LUK0[?=x 00 0C@L000   @@@@000   @@@000    @@@@000   @@@000V6`?_ MU'O?~gfU剽?h;8E  PP$?^Cu7;tQd`QPlLd W|.-Q-#s3W p./qpo Tl-DLL0L 0C@@000   @@@000    @@@0000   @@@000    @@@X1O?HIENDB`simutrans-124.5/themes.src/pak64german/files_large/scrollbar.png000066400000000000000000000153131520675460500247260ustar00rootroot00000000000000PNG  IHDR1gAMA a pHYsodmIDATx^MU/c/M6RlhTF%%\ D{iHiӟPn3٬Ω1,s(+T t| iCu% ܄rBr0*97| c1N|9YP?DWeeCt| TS}:$m$oӈAC%oWBn!&<^<~9TgCĆd tEHΑǷC?|>@I(ʹ 1N< g"Ĩ069C石҉ђ/*S,<,s#: 9vr6TWǍ ( 1T|>V賍!I^7!)h /*gCt]-҈.ʖ Cwe[lEw C.ބ䅋I+G9&g(h C!F"#t'FK^7!ڝ9x@rr$:{-6TW z]P4AWn߈2Q׈ BYI cЋwvC^kWA!jDDɄ1nivCwҙ UvMoiPvMo1Zr^^}#b@COF^#rσ1ZЦlE❲PeZU.*f!bȀ-g2!r̄qP,Etlw9iCuU0mJf^$)]N襓'Ogv! -L&Dp@tlw9T9K~x')]NP]ѦAPLAm$u9){>Dlhez!rσvCҲٿwvӟ9z3Gәf!bȀBDK( c&4)]UΒ_$Igv6TWm2lL()RK̈́φ}s!Cd2l17!>!17;g~7SCVh3ʄCt([L*gɇDuIJP]z&Z9{*=z K+!bCD( e!b*޽VLJhGh[$[L%eX!CdDv(utl1%F&b*iCu%3%/\ 1"6EOF( CC4zvb䅗Ӟ*jOJG]d@!DDN.ȷp1T9K":.&m$ݳBm E h$C;s-y崧SEQtp1P8 ";m%/\ UΒ? 䅋I+c^kzۡ- ?"#ǷC3 CNF'FKN=Vvhe2!rsP,y':ns Օpˎ vvb@CďȈ0㑳҉ђ/祓lϰ<"Z"GLy|;T9Kމ[y|;iCu% z B(C| C0<|9/]!7f!2BrPe*NBn>|`T^jD@e!2Qal!Ɖђ/s+! =DFHn>Lũ>@W6TW%M)Jԍhɥᔨ\*SqTLR ҆JƉ֔K%`81Zrr8%*.J@T\.*SqT|1fiCu%c2҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>|1fiCu%c2҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>|1fiCu%c2҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>|1fiCu%c2҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>|1fiCu%c2҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>Iҋc^y,nH. C/襊kf+砓y,nH. C/֌*@Xw(gJ< 7$JZR t3/ t/zY?Q !ӱ1ێE/Gky,4K% …*mˁ]r欑y,9!T Z32з? Yn;6`9kC<nH. i-6`9kC<U\x t=&gΚ4hm B6(&gΚ4hmts4*kf^L3g`HB6]ZKn~E49s y,Dk%3n~E49s y,Dk%ln~E49s y,Dkݥ֌*mxQ0MΜ5!i @w i-Y3 t̨Y3 t!\Y3 tZ3n~E49s y,Dk%Zkf^L3g`HB6]0kf^L3ϙIB6]†pkf^^Z3N!u: ^֒jmc/ t/_WfTq!mmHt,D0m"7ZfDH. C/+lnF/ t/zYfTqMNǒvێ%- IX2ܐ\*A^VPkI9Nǒvێ%-Nd!T Z3.c4s6Pcj1@1f1 b5ƘAl3 cc 6Pcj1@1f1 b5ƘAl3 cc 6Pcj1@1f1 b5ƘAl3 cc 6Pcj1@1f1 b5ƘAl3 cc 6Pcj1@1f ˡR ~7\:bFr#FV }џ 􅐼v }*K>Я>F|H^o^$y2>Q]Gb1#t6P3 ՕK%6*';$1z.3tPo5:.?\xviCu%|$ t=!t6P3 Օ#U({?$7Οh &=+rvNsH^ ։^NNH^oQOɫ6TW`v+rvl6gH+GPێ7C>ǐv evzmy·*։Z%ylSŤ Օ#@wAsgv6н(UI+߄| ʮ|(!7^evuR~5Z'ɫ;(UI+ t/XRvzm@l6]`(UI+ΟGr}QHn> :{WkVKVH^>L:!$/e- Օs ,2;W@OAύ1)!fzH< e˿CN\G:Opd#<~NDTBod*~'o!m$4-N 1< (z$j֡h!$f':GWC9s?83FALB^!N6ʫ:?҆Ja މ!@b -B:QTV%f'buyp;ubT̙sԂ7y$f'?/lN\G66TW h& @g`݋ t=iCu&G h&7s$Z"'p?G~vv#:#>ysNz#W呍 U t/6н@ד6TWm2)!fa~ܾa/l"GZ'|׿%7gӾk*pmQ?@x'W呍 U h @!@OQ?6 i`+ C_`FƄ\h}C|󎁞cCu)yvu-"['įw%ïבF m;iCuEM`VxJ^ (!"m'mHvzﭓ_=˯F0ɥm}Z'?˫UWVgr۱+׊mCodܶ" LNX<%ڎ l_۶6TW+փZKe/ɈqD([4yb*;a k?puB|(!7dܶ@g` v+rv҆! %Duτ\?nXB?kPxv;|!TQ # ?Er҆j ;Cr҆ iu~*ɐ2N[%ʥ|5\*U@r҆jCk U@r҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>|1fiCu%c2҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>|1fiCu%c2҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>|1fiCu%c2҆J1Ƙe Օc1H+c6TW1,#m$cYFP]I>|1fiCu%c2҆K;匦7{wooTv[J 7NCOLG? ]-b>]%b*P:O)NǴ+~]-ю1e=[| 0>!EHJCzv?\-bMlc0 :} >-!(1F+S؈Q|\DZ05Pkb*6+~(-+p6v-[L25xd3DZ0]1We@?BT Y?[j-[Le鰙T@^(jl1f ԌaײT ls-symbols.0.7 -------------------- Obj=symbol name=ColorOptions Image[0]=> ls-symbols.0.8 -------------------- Obj=symbol name=Flags Image[0]=> ls-symbols.0.2 -------------------- Obj=symbol name=Message Image[0]=> ls-symbols.0.0 -------------------- Obj=symbol name=NewYear Image[0]=> ls-symbols.0.4 -------------------- Obj=symbol name=BusStop #Image[0]=> ls-symbols.1.2 Image[0]=> ls-symbols_stops_new.0.2 -------------------- Obj=symbol name=TrainStop #Image[0]=> ls-symbols.1.1 Image[0]=> ls-symbols_stops_new.0.1 -------------------- Obj=symbol name=CarStop #Image[0]=> ls-symbols.1.0 Image[0]=> ls-symbols_stops_new.0.0 -------------------- Obj=symbol name=ShipStop #Image[0]=> ls-symbols.1.3 Image[0]=> ls-symbols_stops_new.0.3 -------------------- Obj=symbol name=AirStop #Image[0]=> ls-symbols.1.4 Image[0]=> ls-symbols_stops_new.0.4 -------------------- Obj=symbol name=MonorailStop #Image[0]=> ls-symbols.1.5 Image[0]=> ls-symbols_stops_new.0.5 -------------------- Obj=symbol name=TramStop #Image[0]=> ls-symbols.1.6 Image[0]=> ls-symbols_stops_new.0.6 -------------------- Obj=symbol name=MaglevStop #Image[0]=> ls-symbols.1.7 Image[0]=> ls-symbols_stops_new.0.7 -------------------- Obj=symbol name=NarrowgaugeStop #Image[0]=> ls-symbols.1.8 Image[0]=> ls-symbols_stops_new.0.9 -------------------- Obj=symbol name=Passagiere Image[0]=> stations_status.1.0 -------------------- Obj=symbol name=Post Image[0]=> stations_status.1.1 -------------------- Obj=symbol name=Waren Image[0]=> stations_status.1.2 -------------------- Obj=symbol name=Happy #copyright= Image[0]=> stations_status.0.0 -------------------- Obj=symbol name=Unhappy #copyright= Image[0]=> stations_status.0.1 -------------------- Obj=symbol name=NoRoute #copyright= Image[0]=> stations_status.0.2 -------------------- Obj=symbol name=MessageOptions Image[0]=> ls-symbols.2.3 -------------------- Obj=symbol name=Electricity Image[0]=> stations_status.2.0 -------------------- Obj=symbol name=InTown Image[0]=> stations_status.2.1 -------------------- Obj=symbol name=Seasons Image[3]=> ls-symbols.3.0 Image[0]=> ls-symbols.3.1 Image[1]=> ls-symbols.3.2 Image[2]=> ls-symbols.3.3 -------------------- Obj=symbol name=networksym Image[0]=> ls-symbols.3.4 -------------------- Obj=symbol name=timelinesym Image[0]=> ls-symbols.3.5 -------------------- Obj=symbol name=fastforwardsym Image[0]=> ls-symbols.3.6 -------------------- Obj=symbol name=pausesym Image[0]=> ls-symbols.3.7 -------------------- Obj=symbol name=CompassMap copyright=prissi Image[0]=> compass-map.0.0 Image[1]=> compass-map.0.1 Image[2]=> compass-map.0.2 Image[3]=> compass-map.0.3 Image[4]=> - #compass-map.0.4 Image[5]=> - #compass-map.0.5 Image[6]=> - #compass-map.0.6 Image[7]=> - #compass-map.0.7 -------------------- Obj=symbol name=station_type # used in line display only currently # the number is the stationtype (emtpy images must be defined as "-" # loadingbay=0, railstation = 1, dock = 2, busstop = 3, airstop = 4, monorailstop = 5, tramstop = 6, maglevstop=7, narrowgaugestop=8 # maximum size please 11x11 Image[0]=- Image[1]=- Image[2]=> ls-symbols.4.0 Image[3]=- Image[4]=> ls-symbols.4.1 Image[5]=- Image[6]=- Image[7]=> ls-symbols.4.2 Image[8]=- -------------------- Obj=symbol name=MessageOptions #Image[0]=> ls-symbols.2.3 Image[0]=> singlesMessageSymbols.0.0 Image[1]=> singlesMessageSymbols.0.1 Image[2]=> singlesMessageSymbols.0.2 -------------------- simutrans-124.5/themes.src/pak64german/files_large/singlesMessageSymbols.png000066400000000000000000000012011520675460500272540ustar00rootroot00000000000000PNG  IHDR@gAMA a pHYsod#IDATx^AJ@m.x^%zf8i?;<$a_.~ AqsYٝ }߭0E!<#5}¼씫vvzoV1А͊ Ȅ?sķN|߭0;;)B|Db$ry"~pvuas!4PK߁~õI|I|r:yp4 :ɭ@/OϏmP/R5/|t1f#.nyǣ- s7/5~8vzKe+vVGJ V]U:Esp+䃀j0 þKu=?s+w T>>n&0AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$ AB@$AU:#qIENDB`simutrans-124.5/themes.src/pak64german/files_large/skins_neu.png000066400000000000000000000401151520675460500247370ustar00rootroot00000000000000PNG  IHDRG/$gAMA a pHYsod?IDATx^r]ux~s4t;<{⁥[7וU4ī BE U**+Xu itGCT;TF JI+sN<+8ֻ?Cs?t藶 m9l7^|u{\wJ;H|ܾ{~ޭ;www_<{{wo\~Mx{=}ڃի:\yjԄ?٦1.L1#J;g1 eg?1ݿ+}禌ٓ.>Vd{p:Pwv#ݮ]RO̦1< o4+oXP?S'^,ϒ[&Qd[i捷_yO5Rsnw৽1@6#m#[~u_G@Lpnrkoɹn}޽vI'ҍjrɹwqrEwv|pz2w' 4>ݰot@aVt7K=SިC鄙 AO!wMxγG*yJ2,>´%Qސ{ko1K?:# c:c]D):2_d{]6P>rl.AO!ҭύ׹ݖ==}#'wۗ}Une4ůٴl2Mc:c]DB$N?BP) i0 = 5}=m oNL?۽&wFtju?>9W_@r@+/!_@ 5wzv5azR?l1%ͦcIn@;}2t|Xړ֝9cia%ɘt,ݦ[(ޭr_Qȹd*jo0A?3!hèfc:c]l=843u'w;cu&0= Hޤ{t<5͝gOl'Ӱ+3}S{np{;IӦr}'&sg S[} ;$z=b9~Ǡmi'n!4#ٙ-|6ӗq_=XGmnz>kM}x0XR@n%{;^ #Bx(4s m8fyGEpD>/w }Hv17~%[2Ym0}!馶/R!p@6#![ՑպSZGR;0 3AO!k==xFWs>?|]Z go(zɿѓßi)wn{w*nz~[jS=3/m!<-PAZW~pȌ\׺AIo%>홾3mqr[/=Ӄ4FhS95U7a93K<8בu8ʴu[gn AO!w~K~״Ӿnrj'({?_'w{WnzJLɽ侀rLg]ڌp.Z:JS+.LtBlS-(Ы~pȞޓɘ7z=;|4^tg=%|gO4^v .7=X taeu[5պ;'I'kc-!_+M}ߡJGۯ Q١7.*w'jy?۹rLgl SQkṱ8+診Ы'Cw?{6}$7䧖tu?)?~Oݭ 71Aq7y|pg 1J; |RG -~pȞVrGYi8]gMmkK֦VjxouӃ4n9~1]F1tajW[C݇➴Fy& Z섶AO!'\iLbSkߧ}n _<{&k~d9Z~$snp|@QmyhNp1|?螺uuC}Q{F{vߖo"[>1POϤcߖLrLgl S(&HGmݙSlvh_-AO1w7mɛdU22Ga^| K{F݉n֓2;m$uǭ{p5625ݛ'w~fa93Kd)YSV:Oށj| -= ǣ{Ϟ<ɳdg;1´mh )wM^&S+O!@"SnӇBGFӯMc:W hDH[:a+Oc-14wMCtNΚ/~dpѴK;r%O޿q|Jn{grYm}݆k4,-#.uv?,t;Ѕ $dvY:6w1R`@V?,-= @[;^,g_i;~tT5n?{򡶙-jئ1.Lq"Qܭ֝ɮsnj5:t4FO6:9N},|-oY@v;_j^i8݌O{ c:㟁.Lqb}gScNiqhҡO'P>/o$= L&'ڽKkwK7rw'ߕݦoųɴ[ݝ6tþrLg]G,ڹu;G5L^K{89mp_7߽J%=5Fg;|# ӖPGM{C'%~7N?a936ЅHȪN-54t a~8~ǠOVZun˞m_ާFWej>9 wzf.R6=X ta$!:O6PtNh-NiNik8 Ú>6ontB^iw8 ju?>9W_@r@+/!_@ e~i{22M&/af!DrLg]% y|nN@Y#i8dafp;$9?O~+~fwzv5azR?l1%ͦcIn@_+*NQ .mxfLAXu䌥sL4$eUtٻuUv!9LP[E &=?1m̰rLgl _~!:H.ut%P42#yӝn{ׄ6wj=yLîh]NM{ý}O$Mi(wKhr c:㟁. U'{Ɣ6ndg2{dpjy~cInӯK7Mc:G&--z{r"W/}[g&Kߚ?:x[4`r4ƫ~=4ar71.<6a #}mީ) hL -= SŽ[BvWt?5C燏_G@ ųCo\~U&O 8~se@8H'r=uj3cMc:㟁.Lq"tB Xg6 uFCstl AO!{[ofY֦}vI5}nr.YZm7)CԁO>Tt ?,t;ЅS+GrF' ;-apȉ&Wڽ.;CXi[l!cϞڦYNF yFv=#&a936Ѕ -}(y$/*yD+C}Q{F{vߖo"[>1POϤcߖLrLgl du,92Mfh:5,C[(c5jo(ے7ɪdd u,Ò#ݾ'evH[km$ej7O6"ͰrLg3Ѕ)T#9rɺSrZ zG=y(;gtw,cid=/>RgL.V(Cԁ:D&ݦ_?,t;Ѕ)t=)W: zy[(c i8t,G~Ӈvy˰q֓zT^vJ/'wSH=[rm 5l<ơWqMc:c9%6Pcie3ЭGxZrѦҗ{)RYN4?+?\%?'WWz͏?7گ|+g\kFyS(LpGr]@IhKݒ|Y(## [9J\(U)!A< #WRjvi.?~ɈrgIU TywV@%*sl,s9cl 2 B0~(U%tiSspS_`#=;P0~$!ߤU`/})Nd.#QCՎe.?ܿ^Qe$ÇHzל|Lb@Ґ ҮQ#'vب:a !##\L#z(K@7e>l ʙ=J;lϬe9.Ee>De;V/:{P|r͐"84US37g$hqϱ /ek:Ng"~p8cȧ5g3_|LrV)N39 9t/~o렼)\)00ag2ş*(\)(x „g)Ij>Up%I& g3O_|ZSpO YvOEBi**„D)\)(\)rݧ0Yʧ,gOq\|IR ;Yӗ"|SpE:aB{1}M,sla3;E-qr\)(x[l2!7ͩ`Eyy)(lR՟Soqp)Kd-+"NOY d>)rZ)6:œ@]*ʈ!(Y k@` +}MA(/ ֞OD !ݹYbD^@VA2_Exs,9䄪 [2ϋIЩe@fСF7yqs&se۝;RCN(ْ95lPfRˀ!W@yAxsJ3ʆ;w P %s"/&k N l"(\ )+y1-6JmSA9(//E^vNN.?g,d:`5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@1141ƭy?DZF}ڑev! [-!Œ a%]VKH{8r#¸B/>XZNP.a%=a9a]r,!hmCajs.LH~8jae# VKH{8r0!.mKx ҵ>*RɅ)°ᰁ.LXQ*`IXp$9 [-J}U 6О ta 0J%l= ”>(9?VaJ.@{36Ѕ)'|PsN(\$,g8l SNXQ*`IXp@A9 Trړᰁ.L9RscF 'a9a]r>*RhOr0J}U 6О ta 0J%l= ”>(9?VaJ.@{36Ѕ)'|PsN(\$,g8l SN|¨shOr0 tYB3@CU0n`6Х.KH~8r0 tYB33 kFD8XDVKNw„snONw-?'[-!BZBc",stg36Ѕ iGYe,°Ւ6rc+<6sJlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#_`?/ ~)K+_SR|*k)2E*$E?JUAaZc06PS(^zK+!f6ļ´ݘg27Pbn(5fl 3 Ԙ8@e@a8 VBmq 4LdBg@01a]6PcF [-YOj=ubUaEgZȇ*3"qe:)j VBur]T@nsl4np@W'O@  NSkO8dU*C8MUq1fP>gc &pȪ8~! tu&pȪ8~! _^?*tRp1b]?*tRp1 ԿbmZtR Ì t#d館34@ }FN=EZOf@m8a JF\ B&Dm`IVqa8a OflF+bXan3[7PEv%SOxɘh0Jȩ/ A1/[";h6 vw tnֹ"LKCP'dK Ajy XqccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlӈ ccL#6Pcij1@114b5ƘFlMS2G)OfK))⿧v/?N&dEdK欂:Vʪ9an8e'8K?L ?;- VX) i͟vN&dEdK欂:Vʪ9an8ej+liaugw'"C%sVX+e՜0m7&x~6P&xw2!+2$[2gձRVv/?Q[a[?L ?;- VX) i͟vN&dEdK欂:Vʪ9an8ej+liaugw'"C%sVX+e՜0m7&x~" _8rG%AyP* %5>J@(TPJCu(0L ¶~6ЅAHY#q)JT(J(Lk.\}*PjA9E.P`&x~6ЅAHY#q)JT(J(Lk.\}*PjA9E.P`_mm0m7l |2FSAP6P\(UԂrPB]( ա0m7jJ@(TPJCu(0L ¶~6ЅAHY#q)JT(J(Lk.\}*PjA9E.P`&x~6ЅAHY#q)JT(J(Lk.\}*PjA9E.P`_mm0m7l |2FSAP6P\(UԂrPB]( ա0m7jJ@(TPJCu(0L ¶~6ЅAHY#q)JT(J(Lk.\}*PjA9E.P`&x~6ЅAHY#q)JT(J(Lk.\}*PjA9E.P`_mm0m7l |2FSAP6P\(UԂrPB]( ա0m7jJ@(TPJCu(0L ¶~6ЅAHY#q)JT(J(Lk.\}*PjA9E.P`0v0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :Fmgia]䋔5GH2$(J´G PBiiAVv0)#k$ܑ>e@IP eC iP A-((ԅP vv0)#k$ܑ>e@IP eC iP A-((ԅP v ta/RFH#}ʀ<(ʆ Ӛ W% ZP*BQ :FmgiGZrVKNwؠZrVKNwبMpľ tarFYe,°Ւ6j36~:,°Ւ6yGXj" [-9a-i tarFYe,°Ւ6j3ŏj" [-9a.ZrVKNwjKo]`a%;0ltڌFoyZrVKNwK3\-9Ya%;l`C%շ.LNwu0ՒӝEZrFmF#z B܈ۺ·oc%Pzpb8*VwZoI8ˤ=͸0nKgI\T^yj bONآ+3UjC;/;ZvooiۖҩPs :@iKꗖ̡@$,ȯù#Q8y qsUހJхHWԽHliiW6@dӪPr.-- !: DT%b%E~--  zQ~;&Yo&`8S%\YN%V  uo78'?*ys+ vG,gw)K$?%hW8]DwZ'󃤵k-_}eYYc[HQWfH \.2B_ʀ*cr%FCSbZz[߰$ u~Ѳ){p.SDq ~BO1}3B gCG;@BZ:5xr,PkQCIJ:2)~% D\ _i|P.mJ!V'YbYmSl2ے8ǒu `r@-D!>.˷6J+[ nr/&\@\ ZvQΥLs+Խ= `^-?5bkW#,"Z]9;ڒ>r=N㎒Vu$+סmq,\\:6n Խ…!HH)Tblס+^np^MWXb[R{Zʯ`Z k?O2DoR~tJ|7NN(@ﰽmv.up /Ku@V T-\>D!jXUPz$+0y#.3 `F0qѸh\f4.3 `F0qѸh\f4.3 `F0qѸh\f4.3 `F0qѸh\f4.3ڟ.}j 268~<ڰ) ZZۨk. W+Pۀs1JƸe3A\ ${\ jTR[gdlbn8Ps  # Шx>TRj׮  wl :92D4rr%NMV%-§@I-d^ٶ6= \WJq]CQyECFFBjj*Cֹhy9> ("^WM68}=A9:tDENKjҏ:}m: mH{6ȸ\5DC+8Q5k¥K ..!22FKV->2_vO)X̀y=ξn9S qa,d$Pk$]@[W <ȸ\=8DC*)ů_ns͇-Hٶ+[ naSiS)$[/ CV.@@OU_=oTZӭp*׼)O mx z܊% !<. .ga nYIđt"m 9Oa etH=1v ?͂ȃ ߵ"Sx({ ^)k}>v@z]@Ȝ\]V-~cyAd^ <fM Yv\b/6F-f_eЏ2g#gsR>HWž}AYh%PDFTt+k蕂Oy>)íշ5=6&Pk\.@VM,M- >u;[ZG~s,bA槙a`_q3bfs8ǝ3PksH 2 ƿ :&>a(4`p+j?L! Ż8oî) qnt2v@?I8eK]<p,Pk;># wݠ;S61έVpK߀OIz9^lyi Pcܨk$xbI|,3[ .-0>pz7甿w)1nt3~TBҀ׋JݞF~@:9;.|?pPۂq$c nLX1m\>u ߞ3!/A;Qs 1ާ֮8'@ ücWNfpVN@]&\vS۶Êp&PI[06\fk -6o|0qѮ[qz@X^`Faf\f4.3 `F0qѸh\f4.3 `F0qѸh\f4.3 `F0qѸh\f.ϮF zڔ Q&@~_l5nG$Ԧ MIfmo%X>O]vd(LBmJN\BmJN2oУvMk܎ IM k܎ IMRqlu8j"CajS(\.W6Y5~X06̛#=N_G6d(LBm JԦdebt;( jU;MغNC-Pڔ\Fm E͙W~: .R~x<87.XC-P A͙Wp BmB:7.tD$C\ڿ΀AG.q @y>< IMQp5q FmJnkG{CUpy:{mвe6k/օ=+@߇Z0 )xjSr 7`л U B̃ѱc6E%=p@ϣ)d(LBmJN\BmJN) PPA,Cgt萍>y)9p )o vvHly= PBp)3e(+~>?Ee-A8}Uz6yZ0 ) v  Q,kXA$ԦdE>+w0 )Y]z/xa#CajSx72&6 @.2&6Nd(LBm 3 PA$Ԧ0s0 )d(LBm 3 PA$Ԧ0s0 )d(LBm 3 PA$Ԧ0s0 )d(LBm 3 PA$Ԧ0s0 )d(LBm 3 PA$Ԧ0s0 )d(LBm 3 PA$Ԧ0s0 )d(LBm 3 PA$Ԧ0s0 )d(LBm 3 PA>H IENDB`simutrans-124.5/themes.src/pak64german/files_large/theme.dat000066400000000000000000000121441520675460500240300ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: prissi, modified by Leartin # # All image name must starts with "> " to have them unzoomeable # # This theme has the scalable standard look for pak 192.comic # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will used the unporessed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # unpressed Image[0]=> roundbutton.0.0 Image[1]=> roundbutton.0.2 Image[2]=> roundbutton.0.1 Image[3]=> roundbutton.0.3 Image[4]=> roundbutton.0.5 Image[5]=> roundbutton.0.4 Image[6]=> roundbutton.0.6 Image[7]=> roundbutton.0.8 Image[8]=> roundbutton.0.7 # pressed Image[9]=> roundbutton.1.0 Image[10]=> roundbutton.1.2 Image[11]=> roundbutton.1.1 Image[12]=> roundbutton.1.3 Image[13]=> roundbutton.1.5 Image[14]=> roundbutton.1.4 Image[15]=> roundbutton.1.6 Image[16]=> roundbutton.1.8 Image[17]=> roundbutton.1.7 # disabled Image[18]=> roundbutton.2.0 Image[19]=> roundbutton.2.2 Image[20]=> roundbutton.2.1 Image[21]=> roundbutton.2.3 Image[22]=> roundbutton.2.5 Image[23]=> roundbutton.2.4 Image[24]=> roundbutton.2.6 Image[25]=> roundbutton.2.8 Image[26]=> roundbutton.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> skins_neu.0.2 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # unpressed pressed disabled left and right Image[0]=> skins_neu.2.2 Image[1]=> skins_neu.2.3 Image[2]=> scrollbar.0.2 Image[3]=> skins_neu.2.4 Image[4]=> skins_neu.2.5 Image[5]=> scrollbar.0.5 # scrollbar back left center right Image[6]=- Image[7]=> skins_neu.6.6 Image[8]=- # scrollbar knob left center right Image[9]=> skins_neu.6.0 Image[10]=> skins_neu.6.2 Image[11]=> skins_neu.6.1 # arrow buttons up unpressed pressed disabled Image[12]=> skins_neu.4.0 Image[13]=> skins_neu.4.1 Image[14]=> scrollbar.2.2 # arrow buttons down unpressed pressed disabled Image[15]=> skins_neu.4.2 Image[16]=> skins_neu.4.3 Image[17]=> scrollbar.2.5 # scrollbar back up center down Image[18]=- Image[19]=> skins_neu.5.6 Image[20]=- # scrollbar knob up center down Image[21]=> skins_neu.5.0 Image[22]=> skins_neu.5.2 Image[23]=> skins_neu.5.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget_new.0.0 # help Image[1]=> gadget_new.0.1 # minimize Image[2]=> gadget_new.0.2 # previous Image[3]=> gadget_new.0.3 # next Image[4]=> gadget_new.0.4 # unsticky Image[5]=> gadget_new.0.5 # sticky Image[6]=> gadget_new.0.6 # resize corner Image[7]=> gadget_new.0.7 # goto pos Image[8]=> gadget_new.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- -- simutrans-124.5/themes.src/pak64german/pak64german.tab000066400000000000000000000151221520675460500225660ustar00rootroot00000000000000# pak64.german theme # # name of the theme (will be shown with file selector) name=pak64.german ##################################tooltip stuff################################## # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 169 # tooltip text color (240=black, 215=white) tooltip_text_color = 145 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 ################################## ################################## # show graphs old style (right to left, default) or rather left to right left_to_right_graphs = 0 # larger icons too icon_scaling = 100 # Default button width gui_button_width = 130 # use the larger Button height gui_button_height = 22 # arrows gui_horizontal_arrow_width = 20 gui_horizontal_arrow_height = 18 gui_vertical_arrow_width = 18 gui_vertical_arrow_height = 20 # check button gui_checkbox_width = 18 gui_checkbox_height = 18 # The titlebar height gui_titlebar_height = 27 # width and hight of the window elements gui_gadget_width = 27 gui_gadget_height = 27 # request larger font size (the resulting spacing is about 15% larger due to decent) font_size = 17 # You can use a system font. BUT you must specify the whole path to it! # This can be only set in the user defined simuconf.tab #fontname=C:\Windows\fonts\arial.ttf # windows: msjhbd.ttc - Microsoft JhengHei # gui_indicator_size = 18,8 # size tabs gui_tab_header_vsize = 22 gui_tab_header_hsize = 50 # gui_filelist_vspace = 4 ##################################color stuff################################### # gui_color_empty = #ffffff # status bar and ticker # gui_color_statusbar_text = #000000 gui_color_statusbar_background = #D6ECF7 #gui_color_statusbar_divider = #6095B8 # gui_color_ticker_background = #D6ECF7 #gui_color_ticker_divider = #6095B8 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # in default theme white text gui_text_highlight= #FFFFFF # Color of the title text in the welcome screen and in help. gui_color_text_title = #0000FF # Shadow of some texts, like in the welcome screen and help. gui_color_text_shadow = #60A0FF # Background color of charts. gui_color_chart_background = #A49DBF # Text color of the lists area (background is listbox.png) #gui_color_list_text_selected_focus = #5c5c88 gui_color_list_text_selected_nofocus= #ffffff #gui_color_list_background_selected_focus = #72B2DD gui_color_list_background_selected_nofocus = #8c9bbc # Text color in edit areas. #gui_color_edit_text = #000000 #gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #7187b7 #gui_color_edit_background_selected = #72B2DD #gui_color_edit_beam = #000000 # Color of text in tabs, finance window headlines, ware list bonus text, minimap zoom # indicator, fps info in video options. gui_color_text_highlight = #1488E2 # Transparency color in factories boost icons (mail, energy, passengers) gui_color_image_transparency = #5f7e99 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 1 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window window_frame_active = 1 ## second: draw the title with a different brightness (0: dark ... 6: bright) ##front_window_bar_color = 1 ##bottom_window_bar_color = 3 # second: draw the title with a dark overlay (0=none, 100=full black) bottom_window_darkness = 25 # color used for the default titlebars default_window_title_color = 154 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = 215 bottom_window_text_color = 213 # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears toolbar_max_width = 20 toolbar_max_height = 0 # horizontal and vertical margins between objects gui_hspace = 5 gui_vspace = 5 ##################################labels#################################### # text labels # text margins inside the labels (left, top, right, bottom) gui_display_text_label_margins = 8, 6, 8, 5 # The color of the label text. Can be a number 1-7, to indicate player color # brightness or it cant be a RGB value like #000000 gui_display_text_label_color = #FFDD55 # station labels # text margins inside the labels (left, top, right, bottom) gui_display_station_label_margins = 8, 5, 8, 5 # The color of the label text. Can be a number 1-7, to indicate player color # brightness or it cant be a RGB value like #000000. gui_display_station_label_color = 7 # marker labels # text margins inside the labels (left, top, right, bottom) gui_display_marker_label_margins = 12, 6, 12, 4 # The color of the label text. Can be a number 1-7, to indicate player color # brightness or it cant be a RGB value like #000000. gui_display_marker_label_color = #000000 # factory labels # text margins inside the labels (left, top, right, bottom) gui_display_factory_label_margins = 12, 7, 12, 7 # The color of the label text. Can be a number 1-7, to indicate player color # brightness or it cant be a RGB value like #000000. gui_display_factory_label_color = #000000 ##################################pakstuff################################## # pak with the images for this theme themeimages=menu.pak64german.pak simutrans-124.5/themes.src/pak64german/pak64german_large.tab000066400000000000000000000133341520675460500237430ustar00rootroot00000000000000# pak64.german theme # # name of the theme (will be shown with file selector) name=pak64.german (large size) ##################################tooltip stuff################################## # show tooltips (default 1=show) show_tooltips = 1 # tooltip background color (+-1 around this index is used), taken from player-color table tooltip_background_color = 169 # tooltip text color (240=black, 215=white) tooltip_text_color = 145 # delay before showing tooltip in ms (default 500ms) tooltip_delay = 500 # duration in ms during tooltip is visible (default 5000ms=5s) tooltip_duration = 5000 ################################## ################################## # show graphs old style (right to left, default) or rather left to right left_to_right_graphs = 0 # larger icons too icon_scaling = 150 # Default button width gui_button_width = 130 # use the larger Button height gui_button_height = 38 # arrows gui_horizontal_arrow_width = 38 #gui_horizontal_arrow_height = 36 gui_vertical_arrow_width = 36 #gui_vertical_arrow_height = 38 # check button gui_checkbox_width = 32 gui_checkbox_height = 32 # The titlebar height gui_titlebar_height = 42 # width and hight of the window elements gui_gadget_width = 42 gui_gadget_height = 42 # request larger font size (the resulting spacing is about 15% larger due to decent) font_size = 19 # You can use a system font. BUT you must specify the whole path to it! # This can be only set in the user defined simuconf.tab #fontname=C:\Windows\fonts\arial.ttf # windows: msjhbd.ttc - Microsoft JhengHei # gui_indicator_size = 28,15 # size tabs gui_tab_header_vsize = 32 gui_tab_header_hsize = 150 # gui_filelist_vspace = 9 ##################################color stuff################################### # gui_color_empty = #ffffff # status bar and ticker # gui_color_statusbar_text = #000000 gui_color_statusbar_background = #D6ECF7 #gui_color_statusbar_divider = #6095B8 # gui_color_ticker_background = #D6ECF7 #gui_color_ticker_divider = #6095B8 # Color of cursor overlay, which is blended over marked ground tiles # The available colors and their numbers can be found on # http://simutrans-germany.com/wiki/wiki/tiki-index.php?page=en_FactoryDef#mapcolor # Suggested values (155 is the default) # -- pak64, pak.german, pak128 cursor_overlay_color = 155 # -- pak128.japan #cursor_overlay_color = 149 # in default theme white text gui_text_highlight= #FFFFFF # Color of the title text in the welcome screen and in help. gui_color_text_title = #0000FF # Shadow of some texts, like in the welcome screen and help. gui_color_text_shadow = #60A0FF # Background color of charts. gui_color_chart_background = #a49dbf # Text color of the lists area (background is listbox.png) #gui_color_list_text_selected_focus = #5c5c88 gui_color_list_text_selected_nofocus= #ffffff #gui_color_list_background_selected_focus = #72B2DD gui_color_list_background_selected_nofocus = #8c9bbc # Text color in edit areas #gui_color_edit_text = #000000 #gui_color_edit_text_selected = #FAFAFA gui_color_edit_text_disabled = #7187b7 #gui_color_edit_background_selected = #72B2DD #gui_color_edit_beam = #000000 # Color of text in tabs, finance window headlines, ware list bonus text, minimap zoom # indicator, fps info in video options. gui_color_text_highlight = #1488E2 # Transparency color in factories boost icons (mail, energy, passengers) gui_color_image_transparency = #5f7e99 ##################################window stuff################################## # when moving, you can use windows to snap onto each other seamlessly # if you do not like it, set the catch radius to zero window_snap_distance = 8 # show the windows close etc. buttons on the right (like windows 98) window_buttons_right = 0 # closes windows/toolbar when button clicked a second time (default off=0) second_open_closes_win = 1 # restores window position when window is reopening # This will be saved, but will be discarded when screen is resized remember_window_positions = 1 # there are three different ways to indicate an active window # first: draw a frame with titlebar color around active window window_frame_active = 1 ## second: draw the title with a different brightness (0: dark ... 6: bright) ##front_window_bar_color = 1 ##bottom_window_bar_color = 3 # second: draw the title with a dark overlay (0=none, 100=full black) bottom_window_darkness = 25 # color used for the default titlebars default_window_title_color = 154 # third (best together with 2nd):use different text color for bar # some colors are 215-white, 240-black 208-214- all shades of gray front_window_text_color = 215 bottom_window_text_color = 213 # How much shade for player colors (7 brightest 0 darkest) gui_player_color_bright = 5 gui_player_color_dark = 2 # Version 120.1.2+ # If and where to display a compass # 0=off (default for main screen) # 1=top, 2=vertical centre, 3=bottom, 4=left, 8=horizontal centre, 12=right # default for minimap is 1+12=13 compass_screen_position = 7 compass_map_position = 13 ##################################size stuff################################## # maximum size of tool bars in tools (0 = no limit) # if more tools than allowed by height, # next and prev arrows for scrolling appears toolbar_max_width = 30 toolbar_max_height = 0 # safe margin around windows gui_frame_left = 20 gui_frame_top = 20 gui_frame_right = 20 gui_frame_bottom = 20 # horizontal and vertical margins between objects gui_hspace = 23 gui_vspace = 20 ##################################pakstuff################################## # pak with the images for this theme themeimages=menu.pak64german_large.pak simutrans-124.5/themes.src/standard/000077500000000000000000000000001520675460500174375ustar00rootroot00000000000000simutrans-124.5/themes.src/standard/back.png000066400000000000000000000070031520675460500210450ustar00rootroot00000000000000PNG  IHDRݾP IDATxoyyfDr%ڔcl6l,ڠ-/( 7/P`fmZl(rp2(b2߳ A>oBޕ\ڿQ DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D"" 齴wqPh5[AXz)|/;wOwN:AFjXaz[VJ% aYՋ/d>lFCa5.&O߿N!_@N֪[erL>_./[Ë_ttݙ;9ij:Y.a4M#ͣ峋WK٨[V \P]/rx(aT*k[xt26 $p!ƱRyoZ!+أe5&ԒiBDz#!C8 Z3.?:;L&lsJ0UNJEgꔴ( < c Ӽ~2"O{(/P}\*E:SѬQ,m㑦iiyzάRi٠VQTk5Ǚbh\ ^?@h8D~0ly.͛|.7(M\VBd0o8ǯK4qBt=>N~}Q^ ATq^ rYoN%ɝ:kD4UsZ]z1k*(@r)W4ȁٴutQ맽R( Z+ZͲ,%' =*T]b E>Äz3}_ǶGD\=ݛZGzA맽RsuXze܃M>t8c U^Gn}Y4(A ~ś;w:#X ^yg<|V{!w]/(/ieg%yXl(%xf}0|S5hNٽtmup!V{~0;-JIIQ^ t':~j[LJb RM%" 2J^)cLf{ˋB.y< |CV맽RGǯ쵏3]1B_ Lh/_ +Y~s߼|Eה|/_맽Rhw5z}٢)1FTBߍNӋ'QPPeP\ozVB S0c0!SYv맽RDfmt).nӹ{{zH+)qiW0}/|]L|6K ̮$(/b1iޕM}|i S5 Dşf"tg}ߕߞG\Fڭi•*Ƶ"fAѽx:<]Eڝ;n5~/s ~O0Wk5D/WU -O{(/P2W/-9ھt]4h`8wK7/~zvv4M*5 h%~'E}0NW/УJ%/Vj5}TFpp۹sߝ:˷O_~!Kڞi%EJX맽R(pǍjutgS]`}) wzZ^lTM_`Z'~'EyP;ۢjAxszsJnoofLC}w>_}1|>ugnu?Ȉ-fy*?) >9qST gF0?KxdWMP(Lx0ƫ[Ӳ^? z߾2@ƃv3{|vo:'˧O>wƓ* ba\YN%mYofӶGJ9O{(\U^#U*৚^~y~{lu6R|'W#z\N!Ԇi,~3tIIQ^ VZT//UkUD4~5Ҩ7ZF;߂~:n sVv7繙lt&(/ۏǗϟO*5+ D&CѩVzWzٿmܚ r;gG%j}4ZF.Fn}OOzOYAer9"78=u&(6 *v/O1 U3vNѣ55*I|7tԂ=?)7цٸt1;3}??wc727jFՈoN]]m_OaͤO{(p/7`ᇾ|tQ>|tQ^ . Eyx>P(/J@颼@<(]|tQ^ . Eyx>P(/J@颼@<(]|tQ^ . Eyx>P/J@颼@<(]|tQ<(]@<(](REyx>P(/Jh.k;?s?ou'G%_=v@F ""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP "@D""(A DDP ""&qIENDB`simutrans-124.5/themes.src/standard/button.png000066400000000000000000000033331520675460500214620ustar00rootroot00000000000000PNG  IHDR@H^IDATx1oa2ЦK=ţ5tt8E 3& C{k@' <px s-U+Š@>{GqZfg^z Kh_<sh\~;`_/E99{ɻg~"w?[5zV?=a+tZۘt5z^__~*FczWvvo0@;eIɏ;0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH߻w<gc_yti>[n[6ãG޹3zO><5=v ?zVI1w]ZtOCOWУ' h_L.ۗYm](}yj˻y {1z'S$ Ijؘ;0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IHV'Fó'|3zw~/o\6z~O/_|xLGOʵ~:ϋke1;=}vp0zϞQ{ WOxMGOʓ'OFObtd0qŀM7^'un_xﵶ~|v+^͚\>@4?1zp@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I@$`$ I'?.`,/ξL/PG=O>`o^2ɯv5!L ...IIܛ7offf EEEZӧOƤL]v}P߽mmm2llIPnnk״dcvx{I9992tk/9]tt6ї]E}\"ϝ;'kA!b~u+^qϞ=3==}ee=Z{, JIIa<ޒpN>ӱXL>mY5o@U !I. /w_OKK[w|;>)0sssUUUݓ+i4moog܏MFFFG,((p႖]/^ mIлwwttv޺9gϞ6_ K~~,w-ߥU׭oXc.Afc=]{1$ Eݯ_޹B^/_ֵ O<ٹst _djjJ& q1#DFWCcw:@H-ҭWa{P. --/Ͻ}+f;x#ÇYYYOs$۷7m$wЊb;L.^*^6л2̴s—N|*aD;]Oޗ8d풛;88(5V[[+7Awa??)b򥤤v-..{}+{>ۡl˳S~fmׯ]o޼:v~P(۽FFF$٥7)8r D"zOsgFbqzG8iin7OPη>zVz ߟWZZ(vѵjOɄ`d$644e)uG56Gݪ$,0 vAT*Q`(0J@% PDT*Q`(0J@% PDT*Q`(0J@% PDT*Q`(0J@% PDT*Q`(0J@% PDT*Q`(0J@% Po&/CIENDB`simutrans-124.5/themes.src/standard/listbox.png000066400000000000000000000012001520675460500216220ustar00rootroot00000000000000PNG  IHDRݾPGIDATxݱiPEQ)I"E%#!oxth]eeZu=yD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$}cq~8'HD" D@$"HD" D@$"HDNA49HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" v\n3{=!ޮ9z@7u@?D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"HD" D@$"H~#I8IENDB`simutrans-124.5/themes.src/standard/posbutton.png000066400000000000000000000007261520675460500222070ustar00rootroot00000000000000PNG  IHDR@IDATx=NQ@Nc=$b\ 1,ł%0< =Õ%\P~ZãY9&mﲏWUu~?hz5mCٹnVuS،׋ɰ R،3nFv8O@CWGOg׮`O!$`$`eH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ iX,_v&s]޽{ssSͫW?lY7,/}9+{58|UmhWlΚ5~3zʦ٣'ӧ'elQ^ oggk >zQc߽67^Kӵ[ S)D @;0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IH0 $ IHVaIENDB`simutrans-124.5/themes.src/standard/scrollbar.png000066400000000000000000000046031520675460500221330ustar00rootroot00000000000000PNG  IHDR1 JIDATxܿof™0KHDfNU/ 韀EҨ:b &g *W|Kx 8%o{N^wS;k_ߪ=ˌ礼#Ldagg\^gGuurͿ=N8wg9,--hwZ:-fyּ h:w_nooמm9ö՞b"D]ZT{ 0WvF3?K.ea:766esg?kOdfN{hwhwP]k3MzMypFk6ek=`N#Oݭ-z?^穡O7= h=U P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P 48g{{oԍ7^{}?߮nnn6z{a/Wۏ;Ow_>-ǟoF}666[/N>:J\ct :T_7yZ3 jֿQ_^sw[{nee֟GiXNx9 }@zm@ zm@ zm@J}W@~/0Ap׿T2u[oϱk0Fߎ33NxHo.pÏ/,/gl? kk =QKӆ%s8%@5Y?9S)YCEg%.5vsggZ[?PUړ<'g|&R/_֞b4OP]ͭuh[{w7 OE$ $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $! (@H@B P $!Nu~-zOw= mmm^oN?|8 |37󿹹_]=zwq}{ ',=Y)wcŋW(yw^1g)Y.O u8MiG8> S…GWsfSG8+++{\U7h{NoVQ6 ʂ|L볟L7P՜~=hI&@56P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@B6P  d@Bo.NqIENDB`simutrans-124.5/themes.src/standard/standard.dat000066400000000000000000000115561520675460500217410ustar00rootroot00000000000000# # Simutrans window skin definitions for themes # Author: prissi # # All image names must start with "> " to have them non zoomable # # This theme has the scalable standard look # ################################################ # # Button have 3 rows with left middle right images for one state # # If only top row is there, button will be centered vertically in area if the size does not match. # If all nine are there, you can set any size in themes.tab # -- Obj=menu name=Button # unpressed Image[0]=> button.0.0 Image[1]=> button.0.2 Image[2]=> button.0.1 Image[3]=> button.0.3 Image[4]=> button.0.5 Image[5]=> button.0.4 Image[6]=> button.0.6 Image[7]=> button.0.8 Image[8]=> button.0.7 # pressed Image[9]=> button.1.0 Image[10]=> button.1.2 Image[11]=> button.1.1 Image[12]=> button.1.3 Image[13]=> button.1.5 Image[14]=> button.1.4 Image[15]=> button.1.6 Image[16]=> button.1.8 Image[17]=> button.1.7 # disabled (will use the unpressed color mask) Image[18]=> button.2.0 Image[19]=> button.2.2 Image[20]=> button.2.1 Image[21]=> button.2.3 Image[22]=> button.2.5 Image[23]=> button.2.4 Image[24]=> button.2.6 Image[25]=> button.2.8 Image[26]=> button.2.7 # color mask (will be blended, so it is a good idea to have a bright color for the foreground in this area) Image[27]=> button.3.0 Image[28]=> button.3.2 Image[29]=> button.3.1 Image[30]=> button.3.3 Image[31]=> button.3.5 Image[32]=> button.3.4 Image[33]=> button.3.6 Image[34]=> button.3.8 Image[35]=> button.3.7 # If the position of the colored area is different for a pressed button (i.e. protruding knobs) # then here can come another nine mask images -- Obj=menu name=Roundbutton # unpressed Image[0]=> roundbutton.0.0 Image[1]=> roundbutton.0.2 Image[2]=> roundbutton.0.1 Image[3]=> roundbutton.0.3 Image[4]=> roundbutton.0.5 Image[5]=> roundbutton.0.4 Image[6]=> roundbutton.0.6 Image[7]=> roundbutton.0.8 Image[8]=> roundbutton.0.7 # pressed Image[9]=> roundbutton.1.0 Image[10]=> roundbutton.1.2 Image[11]=> roundbutton.1.1 Image[12]=> roundbutton.1.3 Image[13]=> roundbutton.1.5 Image[14]=> roundbutton.1.4 Image[15]=> roundbutton.1.6 Image[16]=> roundbutton.1.8 Image[17]=> roundbutton.1.7 # disabled Image[18]=> roundbutton.2.0 Image[19]=> roundbutton.2.2 Image[20]=> roundbutton.2.1 Image[21]=> roundbutton.2.3 Image[22]=> roundbutton.2.5 Image[23]=> roundbutton.2.4 Image[24]=> roundbutton.2.6 Image[25]=> roundbutton.2.8 Image[26]=> roundbutton.2.7 -- Obj=menu name=Editfield #should be either scalable or at least LINESPACE+2px high Image[0]=> editfield.0.0 Image[1]=> editfield.0.1 Image[2]=> editfield.0.2 Image[3]=> editfield.1.0 Image[4]=> editfield.1.1 Image[5]=> editfield.1.2 Image[6]=> editfield.2.0 Image[7]=> editfield.2.1 Image[8]=> editfield.2.2 -- Obj=menu name=Listbox #Must be scalable! Image[0]=> listbox.0.0 Image[1]=> listbox.0.1 Image[2]=> listbox.0.2 Image[3]=> listbox.1.0 Image[4]=> listbox.1.1 Image[5]=> listbox.1.2 Image[6]=> listbox.2.0 Image[7]=> listbox.2.1 Image[8]=> listbox.2.2 -- # window background Obj=menu name=Back Image[0]=> back.0.0 Image[1]=> back.0.1 Image[2]=> back.0.2 Image[3]=> back.1.0 Image[4]=> back.1.1 Image[5]=> back.1.2 Image[6]=> back.2.0 Image[7]=> back.2.1 Image[8]=> back.2.2 -- ################################################################# # # The following control only have one (or three) images, and are not stretchable # Obj=menu name=Checkbutton # unpressed Image[0]=> checkbutton.0.0 # pressed Image[1]=> checkbutton.0.1 # disabled Image[2]=> checkbutton.0.2 -- Obj=menu name=Posbutton # unpressed Image[0]=> posbutton.0.0 # pressed Image[1]=> posbutton.0.1 # disabled Image[2]=> posbutton.0.2 -- Obj=menu name=Scrollbar # arrow buttons left unpressed pressed disabled Image[0]=> scrollbar.0.0 Image[1]=> scrollbar.0.1 Image[2]=> scrollbar.0.2 # arrow buttons right unpressed pressed disabled Image[3]=> scrollbar.0.3 Image[4]=> scrollbar.0.4 Image[5]=> scrollbar.0.5 # scrollbar back left center right Image[6]=- Image[7]=> scrollbar.1.6 Image[8]=- # scrollbar knob left center right Image[9]=> scrollbar.1.0 Image[10]=> scrollbar.1.2 Image[11]=> scrollbar.1.1 # arrow buttons up unpressed pressed disabled Image[12]=> scrollbar.2.0 Image[13]=> scrollbar.2.1 Image[14]=> scrollbar.2.2 # arrow buttons down unpressed pressed disabled Image[15]=> scrollbar.2.3 Image[16]=> scrollbar.2.4 Image[17]=> scrollbar.2.5 # scrollbar back up center down Image[18]=- Image[19]=> scrollbar.3.6 Image[20]=- # scrollbar knob up center down Image[21]=> scrollbar.3.0 Image[22]=> scrollbar.3.2 Image[23]=> scrollbar.3.1 -- # window symbols Obj=menu name=Gadget # close Image[0]=> gadget.0.0 # help Image[1]=> gadget.0.1 # minimize Image[2]=> gadget.0.2 # previous Image[3]=> gadget.0.3 # next Image[4]=> gadget.0.4 # unsticky Image[5]=> gadget.0.5 # sticky Image[6]=> gadget.0.6 # resize corner Image[7]=> gadget.0.7 # goto pos Image[8]=> gadget.0.8 -- # window background Obj=menu name=Divider Image[0]=- Image[1]=> divider.0.0 Image[2]=- -- simutrans-124.5/todo.txt000066400000000000000000000012351520675460500152730ustar00rootroot00000000000000To be done: - weight limit on ways by axle_load entry in vehicles (to avoid problem when going back and forth with diff. load) - busy platform images - city wayobj instead just sidewalks - building/tree heights in dats - fog view to the top? - bahnhofsdetaildialog (more text on the station buildings) - pier like extended instead monorail ground partially done: - connection weighted by their intermediate stops => A* for goods routing [A* did not worked out, but intermediate stops may be considered] reconsider: - leave stop if other convoi has arrived there patch - viewports (even if they are perfomance killers ... ) - routing penalty sign (but a proper one) simutrans-124.5/tools/000077500000000000000000000000001520675460500147245ustar00rootroot00000000000000simutrans-124.5/tools/android-helper/000077500000000000000000000000001520675460500176215ustar00rootroot00000000000000simutrans-124.5/tools/android-helper/build-only.sh000077500000000000000000000032351520675460500222410ustar00rootroot00000000000000#!/bin/bash cd simutrans-android-project/simutrans/jni # Assets cp -rf simutrans/simutrans/. ../src/main/assets # Revision cd simutrans/tools REVISION=$(./get_revision.sh) sed -i "s/versionCode [0-9]\+/versionCode $REVISION/" ../../../build.gradle cd ../../../.. VERSION=`sed -n 's/#define SIM_VERSION_MAJOR *\([0-9]*\)$/\1/ p' > ~/.profile echo "export ANDROID_SDK_HOME=/opt/android-sdk-linux" >> ~/.profile echo "export ANDROID_SDK_ROOT=/opt/android-sdk-linux" >> ~/.profile echo "export ANDROID_SDK=/opt/android-sdk-linux" >> ~/.profile echo "export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64" >> ~/.profile echo "export ANDROID_NDK=/opt/android-sdk-linux/ndk/29.0.14206865" >> ~/.profile echo 'alias bundletool="java -jar /opt/android-sdk-linux/cmdline-tools/latest/bin/bundletool-all-1.18.2.jar"' >> ~/.profile echo 'PATH=$PATH:/opt/android-sdk-linux/cmdline-tools/latest/bin:/opt/android-sdk-linux/platform-tools:/opt/android-sdk-linux/build-tools/35.0.0:/opt/android-sdk-linux/cmake/3.31.6/bin:/opt/android-sdk-linux/ndk/29.0.14206865:/opt/android-sdk-linux/ndk/29.0.14206865/toolchains/llvm/prebuilt/linux-x86_64/bin/' >> ~/.profile export ANDROID_HOME=/opt/android-sdk-linux export ANDROID_SDK_HOME=/opt/android-sdk-linux export ANDROID_SDK_ROOT=/opt/android-sdk-linux export ANDROID_SDK=/opt/android-sdk-linux export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 export ANDROID_NDK=/opt/android-sdk-linux/ndk/29.0.14206865/ alias bundletool="java -jar /opt/android-sdk-linux/cmdline-tools/latest/bin/bundletool-all-1.18.2.jar" PATH=$PATH:/opt/android-sdk-linux/cmdline-tools/latest/bin:/opt/android-sdk-linux/platform-tools:/opt/android-sdk-linux/build-tools/35.0.0:/opt/android-sdk-linux/cmake/3.31.6/bin:/opt/android-sdk-linux/ndk/29.0.14206865:/opt/android-sdk-linux/ndk/29.0.14206865/toolchains/llvm/prebuilt/linux-x86_64/bin/ cd /opt/android-sdk-linux wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip unzip commandlinetools-linux-11076708_latest.zip rm commandlinetools-linux-11076708_latest.zip mv cmdline-tools latest mkdir cmdline-tools mv latest cmdline-tools/latest yes | cmdline-tools/latest/bin/sdkmanager --licenses yes | cmdline-tools/latest/bin/sdkmanager --install "platform-tools" yes | cmdline-tools/latest/bin/sdkmanager --install "build-tools;35.0.0" yes | cmdline-tools/latest/bin/sdkmanager --install "cmake;3.31.6" yes | cmdline-tools/latest/bin/sdkmanager --install "ndk;29.0.14206865" ln -s llvm-objdump /opt/android-sdk-linux/ndk/29.0.14206865/toolchains/llvm/prebuilt/linux-x86_64/bin/objdump cd cmdline-tools/latest/bin wget https://github.com/google/bundletool/releases/download/1.18.2/bundletool-all-1.18.2.jar chmod 775 bundletool-all-1.18.2.jar mkdir ~/.android/ keytool -genkey -v -keystore ~/.android/debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 -keypass android -storepass android -dname "cn=example.com,ou=exampleou,dc=example,dc=com" export SIGNING_KEYSTORE=~/.android/debug.keystore export SIGNING_STORE_PASSWORD=android export SIGNING_KEY_PASSWORD=android export SIGNING_KEY_ALIAS=androiddebugkey echo "export SIGNING_KEYSTORE=~/.android/debug.keystore" >> ~/.profile echo "export SIGNING_STORE_PASSWORD=android" >> ~/.profile echo "export SIGNING_KEY_PASSWORD=android" >> ~/.profile echo "export SIGNING_KEY_ALIAS=androiddebugkey" >> ~/.profile source ~/.profile echo "**************************************************" echo "** Please logout before run the next setup script!" echo "**************************************************" simutrans-124.5/tools/android-helper/setup-simutrans-android.sh000077500000000000000000000011361520675460500247620ustar00rootroot00000000000000#!/bin/bash git clone --depth 1 https://github.com/simutrans/simutrans-android-project cd simutrans-android-project git submodule init git submodule update cd simutrans/jni/SDL/ echo "Reverting linekr flg removal in SDL2" git revert 53dbe18 --no-commit cd .. svn checkout svn://servers.simutrans.org/simutrans/trunk simutrans #./build_libraries.sh # Fluidsynth is a PITA to build; using the prebuilt release instead wget https://github.com/FluidSynth/fluidsynth/releases/download/v2.5.1/fluidsynth-2.5.1-android24.zip unzip fluidsynth-*.zip -d fluidsynth ./simutrans/src/android/AndroidPreBuild.sh simutrans-124.5/tools/cleanup_code.sh000077500000000000000000000010261520675460500177030ustar00rootroot00000000000000# # properly rename include guards (needs perl) echo "Checking include guards" find . -type f -name "*.h" | grep -v "squirrel/" | while read f; do guard="$(echo $f | cut -b 3- | tr '[[:lower:]]' '[[:upper:]]' | tr -C '[[:alnum:]]' '_' | rev | cut -c2- | rev)"; perl -i -p0e "s/(\n){2,}#ifndef [^\n]*\n#define [^\n]*(\n)+/\n\n#ifndef $guard\n#define $guard\n\n\n/" $f; done # # remove trailing spaces echo "Removing trailing whitespaces" find . -type f -name "*.h" -o -name "*.cc" | grep -v "squirrel" | xargs sed -i -e "s/[ \t]*$//" simutrans-124.5/tools/distribute.sh000077500000000000000000000173111520675460500174440ustar00rootroot00000000000000#!/bin/bash # # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # # parameters (in this order): # "-no-lang" prevents downloading the translations # "-no-rev" do not include revision number in zip file name # "-rev=###" overide SDL revision with ## (number) # # should be called from the main directory "tools/distribute.sh" # # get pthreads DLL getDLL() { # Use curl if available, else use wget echo "Downloading pthreadGC2.dll" curl -q -h > /dev/null if [ $? -eq 0 ]; then curl -q ftp://sourceware.org/pub/pthreads-win32/dll-latest/dll/x86/pthreadGC2.dll > pthreadGC2.dll || { echo "Error: download of PthreadGC2.dll failed (curl returned $?)" >&2 exit 4 } else wget -q --help > /dev/null if [ $? -eq 0 ]; then wget -q -N ftp://sourceware.org/pub/pthreads-win32/dll-latest/dll/x86/pthreadGC2.dll || { echo "Error: download of PthreadGC2.dll failed (wget returned $?)" >&2 exit 4 } else echo "Error: Neither curl or wget are available on your system, please install either and try again!" >&2 exit 6 fi fi } getSDL2mac() { # Use curl if available, else use wget curl -h > /dev/null if [ $? -eq 0 ]; then curl http://www.libsdl.org/release/SDL2-2.0.10.dmg > SDL2-2.0.10.dmg || { echo "Error: download of file SDL2-2.0.10.dmg failed (curl returned $?)" >&2 rm -f "SDL2-2.0.10.dmg" exit 4 } curl http://www.libsdl.org/release/SDL2-2.0.10.dmg > SDL2-2.0.10.dmg || { echo "Error: download of file SDL2-2.0.10.dmg failed (curl returned $?)" >&2 rm -f "SDL2-2.0.10.dmg" exit 4 } else wget --help > /dev/null if [ $? -eq 0 ]; then wget -N http://www.libsdl.org/release/SDL2-2.0.10.dmg || { echo "Error: download of file SDL2-2.0.10.dmg failed (wget returned $?)" >&2 rm -f "SDL2-2.0.10.dmg" exit 4 } wget -N http://www.libsdl.org/release/SDL2-2.0.10.dmg || { echo "Error: download of file SDL2-2.0.10.dmg failed (wget returned $?)" >&2 rm -f "SDL2-2.0.10.dmg" exit 4 } else echo "Error: Neither curl or wget are available on your system, please install either and try again!" >&2 exit 6 fi fi } # first assume unix name defaults ... simexe= updatepath="tools/" updater="get_pak.sh" OST=unknown # now get the OSTYPE from config.default and remove all spaces around OST=`grep "^OSTYPE" config.default | sed "s/OSTYPE[ ]*[:]*=[ ]*//" | sed "s/[ ]*\#.*//"` PGC=0 # now get the BUNDLE_PTHREADGC2 from config.default and remove all spaces around PGC=`grep "^BUNDLE_PTHREADGC2" config.default | sed -E "s/BUNDLE_PTHREADGC2[ :]*=[ ]*//" | sed -E "s/[ ]*\#.*//"` BUILDDIR=`grep "^PROGDIR" config.default | sed "s/PROGDIR[ ]*[:]*=[ ]*//" | sed "s/[ ]*\#.*//"` if [ -n "$BUILDDIR" ]; then BUILDDIR=../sim else BUILDDIR=../build/default/sim fi # now make the correct archive name simexe= if [ "$OST" = "mac" ]; then simarchivbase=simumac elif [ "$OST" = "haiku" ]; then simarchivbase=simuhaiku elif [ "$OST" = "mingw" ]; then simexe=.exe SDLTEST=`grep "^BACKEND" config.default | sed "s/BACKEND[ ]*[:]*=[ ]*//" | sed "s/[ ]*\#.*//"` if [ "$SDLTEST" = "sdl" ] || [ "$SDLTEST" = "sdl2" ]; then simarchivbase=simuwin-sdl else simarchivbase=simuwin # Missing: Copy matching SDL dll! fi cd simutrans if [ "$PGC" -ne 0 ]; then getDLL fi cd .. updatepath="src/Windows/nsis/" updater="download-paksets.exe" if ! [[ -f "$updatepath$updater" ]]; then (cd "$updatepath" && makensis onlineupgrade.nsi) fi elif [ "$OST" = "linux" ]; then simarchivbase=simulinux elif [ "$OST" = "freebsd" ]; then simarchivbase=simubsd elif [ "$OST" = "amiga" ]; then simarchivbase=simuamiga fi # Test if there is something to distribute ... if [ ! -f ./simutrans/$BUILDDIR$simexe ]; then echo "No simutrans executable found! Aborted!" exit 1 fi cp $updatepath$updater simutrans # now add revision number without any modificators # fetch language files if [ "$#" = "0" ]; then # try local answer assuming we use svn REV_NR=`svnversion` if [ -z "$REV_NR" ]; then # nothing, then use revision number from server (assuming we are up to date) REV_NR=`svn info --show-item revision svn://servers.simutrans.org/simutrans | sed "s/[0-9]*://" | sed "s/M.*//"` fi simarchiv=$simarchivbase-$REV_NR elif [ `expr match "$*" ".*-rev="` > 0 ]; then REV_NR=$(echo $* | sed "s/.*-rev=[ ]*//" | sed "s/[^0-9]*//") simarchiv=$simarchivbase-$REV_NR else echo "No revision given!" simarchiv=$simarchivbase fi echo "Targeting archive $simarchiv" # (otherwise there will be many .svn included under windows) distribute() { if [ "$OST" = "mingw" ]; then # pack all files of the current release (no pixelfont for windows) FILELISTE=`find simutrans -type f "(" -name "*.tab" -o -name "*.mid" -o -name "*.txt" -o -name "*.dll" -o -name "*.pak" -o -name "*.nut" -o -name "*.dll" ")"` else # pack all files of the current release FILELISTE=`find simutrans -type f "(" -name "*.tab" -o -name "*.mid" -o -name "*.bdf" -o -name "*.fnt" -o -name "*.txt" -o -name "*.dll" -o -name "*.pak" -o -name "*.nut" -o -name "*.dll" ")"` fi zip -9 $simarchiv.zip $FILELISTE simutrans/simutrans$simexe simutrans/$updater } buildOSX() { echo "Build Mac OS package" # builds a bundle for MAC OS mkdir -p "simutrans.app/Contents/MacOS" mkdir -p "simutrans.app/Contents/Resources" cp $BUILDDIR$simexe "simutrans.app/Contents/MacOS/simutrans" strip "simutrans.app/Contents/MacOS/simutrans" cp "src/OSX/simutrans.icns" "simutrans.app/Contents/Resources/simutrans.icns" localostype=`uname -o` if [ "Msys" == "$localostype" ] || [ "Linux" == "$localostype" ]; then # only 7z on linux and windows can do that ... getSDL2mac 7z x "SDL2-2.0.10.dmg" rm SDL2-2.0.10.dmg else # assume MacOS mkdir -p "simutrans.app/Contents/Frameworks/" cp "/usr/local/opt/freetype/lib/libfreetype.6.dylib" \ "/usr/local/opt/libpng/lib/libpng16.16.dylib" \ "/usr/local/opt/sdl2/lib/libSDL2-2.0.0.dylib" \ "simutrans.app/Contents/Frameworks/" install_name_tool -change "/usr/local/opt/freetype/lib/libfreetype.6.dylib" "@executable_path/../Frameworks/libfreetype.6.dylib" "simutrans.app/Contents/MacOS/simutrans" install_name_tool -change "/usr/local/opt/libpng/lib/libpng16.16.dylib" "@executable_path/../Frameworks/libpng16.16.dylib" "simutrans.app/Contents/MacOS/simutrans" sudo install_name_tool -change "/usr/local/opt/libpng/lib/libpng16.16.dylib" "@executable_path/../Frameworks/libpng16.16.dylib" "simutrans.app/Contents/Frameworks/libfreetype.6.dylib" install_name_tool -change "/usr/local/opt/sdl2/lib/libSDL2-2.0.0.dylib" "@executable_path/../Frameworks/libSDL2-2.0.0.dylib" "simutrans.app/Contents/MacOS/simutrans" fi echo "APPL????" > "simutrans.app/Contents/PkgInfo" sh src/OSX/plistgen.sh "simutrans.app" "simutrans" if [ ! -d "pak" ]; then curl --progress-bar -L -o "pak.zip" "http://downloads.sourceforge.net/project/simutrans/pak64/122-0/simupak64-122-0.zip" unzip -qoC "pak.zip" -d .. rm -f "pak.zip" fi } # fetch language files if [ "$#" = "0" ] || [ `expr match "$*" "-no-lang"` = "0" ]; then sh tools/get_lang_files.sh fi # now built the archive for distribution cd simutrans if [ "$OST" = "mac" ]; then buildOSX cd .. ls pwd zip -r -9 - simutrans > simumac.zip cd simutrans rm -rf simutrans.app exit 0 else echo "Build default zip archive" cp $BUILDDIR$simexe ./simutrans$simexe strip simutrans$simexe cp ../$updatepath$updater $updater cd .. distribute # .. finally delete executable and language files rm simutrans/simutrans$simexe fi # cleanup dll's if [[ -n "$PGC" ]] && [[ "$PGC" -ne 0 ]]; then rm simutrans/pthreadGC2.dll fi # swallow any error values, return success in any case exit 0 simutrans-124.5/tools/get_lang_files.ps1000066400000000000000000000022121520675460500203100ustar00rootroot00000000000000# Downloade translations after post request and extract them #$translator_url = "https://translator.simutrans.com" $translator_url = "https://makie.de/translator" <# not sure why the second does not work ... $translator_url = "https://makie.de/translator" # Stackoverflow: First we create the request and get the status code $HTTP_Request = [System.Net.WebRequest]::Create("$translator_url") $HTTP_Response = $HTTP_Request.GetResponse() $HTTP_Status = [int]$HTTP_Response.StatusCode If ($HTTP_Status -ne 200) { $translator_url = "https://makie.de/translator" } #> Write-Output "Downloading and installing languag files from $translator_url" # now download $dummy=(New-Object System.Net.WebClient).UploadString("$translator_url/script/main.php?page=wrap", "version=0&choice=all&submit=Export!") Invoke-WebRequest -Uri "$translator_url/data/tab/language_pack-Base+texts.zip" -OutFile "temp.zip" #(New-Object System.Net.WebClient).DownloadFile("$translator_url/data/tab/language_pack-Base+texts.zip", ".") Remove-Item -ErrorAction Ignore "simutrans/text" -Recurse ; $null expand-archive -path "temp.zip" -DestinationPath "simutrans/text" Remove-Item "temp.zip" simutrans-124.5/tools/get_lang_files.sh000077500000000000000000000116371520675460500202350ustar00rootroot00000000000000#!/bin/bash # # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # updating SVN too? UPDATE_SVN=0 if [ "$#" -gt 0 ] && [ "$1" = '-svn' ]; then echo "Updating SVN too" UPDATE_SVN=1 else echo "Use option -svn to update the repo." fi move_if_differ_add() { pattern="$1" dest_path="$2" mkdir -p $dest_path if [[ $UPDATE_SVN -eq 1 ]]; then # only copy changed texts for f in $pattern; do if [[ -f "$dest_path/$f" ]]; then cat $f | grep "^[^#]" > $f.tmp a=`cat $dest_path/$f | grep "^[^#]" | diff -wq $f.tmp -` if [[ ! -z "$a" ]] ; then echo "Updating $f to texts" mv $f $dest_path/$f fi else # new file: move it and add it to svn echo "Creating $f to texts" mv $f $dest_path/$f svn add $dest_path/$f svn ps svn:eol-style native $dest_path/$f fi done else # only copy changed texts for f in $pattern; do cat $f | grep "^[^#]" > $f.tmp a=`cat $dest_path/$f | grep "^[^#]" | diff -wq $f.tmp -` if [[ ! -z "$a" ]] ; then echo "Updating $f to texts" mv $f $dest_path/$f fi done fi } move_add() { pattern="$1" dest_path="$2" if ! [[ -d "$dest_path" ]]; then mkdir -p $dest_path if [[ $UPDATE_SVN -eq 1 ]];then svn add $dest_path fi fi # only copy changed texts for f in $pattern; do if [[ $UPDATE_SVN -eq 1 ]] && ! [[ -f "$dest_path/$f" ]]; then # new file: move it and add it to svn echo "Creating $f to texts" mv $f $dest_path/$f svn add $dest_path/$f svn ps svn:eol-style native $dest_path/$f else mv $f $dest_path/$f fi done } # # script to fetch language files # # assumes to be called from main dir "tools/get_lang_files.sh" # OUTPUT_DIR=simutrans/text TEMP_DIR=lang-tmp #TRANSLATOR_URL=https://translator.simutrans.com TRANSLATOR_URL=https://makie.de/translator # get the translations for basis # The first file is longer, but only because it contains SQL error messages # - discard it after complete download (although parsing it would give us the archive's name): # first test which URL actually works # Use curl if available, else use wget curl -q -h > /dev/null if [ $? -eq 0 ]; then if ![ curl --head --silent --fail $TRANSLATOR_URL 2> /dev/null ]; then TRANSLATOR_URL=https://translator.simutrans.com fi echo "Using translator at $TRANSLATOR_URL" curl -q -L -d "version=0&choice=all&submit=Export%21" $TRANSLATOR_URL/script/main.php?page=wrap > /dev/null || { echo "Error: generating file language_pack-Base+texts.zip failed (curl returned $?)" >&2; exit 3; } curl -q -L $TRANSLATOR_URL/data/tab/language_pack-Base+texts.zip > language_pack-Base+texts.zip || { echo "Error: download of file language_pack-Base+texts.zip failed (curl returned $?)" >&2 rm -f "language_pack-Base+texts.zip" exit 4 } else wget -q --help > /dev/null if [ $? -eq 0 ]; then if ![ wget -q --method=HEAD $TRANSLATOR_URL 2> /dev/null ]; then TRANSLATOR_URL=https://translator.simutrans.com fi echo "Using translator at $TRANSLATOR_URL" wget -q --post-data "version=0&choice=all&submit=Export!" --delete-after $TRANSLATOR_URL/script/main.php?page=wrap || { echo "Error: generating file language_pack-Base+texts.zip failed (wget returned $?)" >&2; exit 3; } wget -q -N $TRANSLATOR_URL/data/tab/language_pack-Base+texts.zip || { echo "Error: download of file language_pack-Base+texts.zip failed (wget returned $?)" >&2 rm -f "language_pack-Base+texts.zip" exit 4 } else echo "Error: Neither curl or wget are available on your system, please install either and try again!" >&2 exit 6 fi fi # create temporary directory rm -rf $TEMP_DIR mkdir $TEMP_DIR # test archive unzip -otv "language_pack-Base+texts.zip" -d $TEMP_DIR || { echo "Error: file language_pack-Base+texts.zip seems to be defective" >&2 rm -f "language_pack-Base+texts.zip" exit 5 } unzip -o "language_pack-Base+texts.zip" -d $TEMP_DIR rm language_pack-Base+texts.zip # now we do this inside the tmmp directory cd $TEMP_DIR # remove Chris English (may become simple English ... ) rm -f ce.tab rmdir ce rm -f _objectlist.txt # Remove check test #rm xx.tab #rm -rf xx # user credits mv _translate_users.txt ../$OUTPUT_DIR/translate_users.txt # only copy changed texts move_if_differ_add "*.tab" "../$OUTPUT_DIR/" #if [[ $UPDATE_SVN -eq 1 ]]; then # svn ps svn:eol-style native ../$OUTPUT_DIR/*.tab #fi # remove the unchanged files (apart from dates and translators) rm *.tab rm *.tab.tmp # now move the help files for f in *; do if [ -z "$(ls -A $f)" ]; then echo "Remove empty help files for $f" else cd $f move_add "*.txt" "../../$OUTPUT_DIR/$f" cd .. rm -rf $f fi done cd .. rm -rf $TEMP_DIR simutrans-124.5/tools/get_pak.ps1000066400000000000000000000006321520675460500167640ustar00rootroot00000000000000# Downloades a simple zip file and unzips it. Assumes everything is according to Simutrans norm. Write-Output "Downloading and installing "$($args[0]) (New-Object System.Net.WebClient).DownloadFile($args[0], "temp.zip") expand-archive -path "temp.zip" -DestinationPath "." -Force del "temp.zip" if (Test-Path -Path "simutrans") { Move-Item -Path 'simutrans\*' -Destination . -Force rmdir -Force "simutrans" }simutrans-124.5/tools/get_pak.sh000077500000000000000000000366551520675460500167140ustar00rootroot00000000000000#!/bin/bash # # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # # script to fetch pak sets # Downloads pak sets and installs them into $(pwd)/ # with option -generate_h it will instead update the NSIS and isntall files # # make sure that non-existing variables are not ignored set -u # fall back to "/tmp" if TEMP is not set TEMP=${TEMP:-/tmp} # # Download file. # Parameters: # URL # filename # # Status codes: # 0: Success # 1: Generic error # 2: File not found # do_download() { if which curl >/dev/null; then curl --progress-bar -L "$1" > "$2" || { echo "Error: download of file '$2' failed (curl returned $?)" >&2 rm -f "$2" return 1 } elif which wget >/dev/null; then wget -q --show-progress -O "$2" "$1" || { result=$? rm -f "$2" if [[ $result -eq 8 ]]; then echo "Error downloading file: Not found at URL '$1'" >&2 return 2 else echo "Unknown error downloading file (wget returned $result)" >&2 return 1 fi } else echo "Error: Neither curl or wget are available on your system, please install either and try again!" >&2 return 1 fi return 0 } install_cab() { pakzippath="$1" files=$(cabextract --list "$pakzippath" 2>/dev/null) || { #echo "Warning: no cabextract, assuming mingw on windows" >&2 files=$(extrac32 //D "$pakzippath" 2>/dev/null) || { echo "Error: Cannot extract .cab file (Either cabextract or makecab required)" >&2 return 1 } # this is likely Mingw on windows using extrac32 # First check if we only have a simutrans/ directory at the root. files=$(echo "$files" | grep ' simutrans\\') if [ $? -eq 0 ]; then # has simutrans folder, but cabextract cannot handle unix path on windows destdir="." else mkdir -p simutrans destdir="simutrans" fi files=$(extrac32 //L "$destdir" //Y //E "$pakzippath" 2>/dev/null) || { echo "Error: Could not extract '$pakzippath' to '$destdir'" >&2 return 1 } return 0 } # First check if we only have a simutrans/ directory at the root. files=$(echo "$files" | grep "| simutrans/") if [ $? -eq 0 ]; then # has simutrans folder, but cabextract cannot handle unix path on windows destdir="." else mkdir -p simutrans destdir="simutrans" fi cabextract -qd "$destdir" "$pakzippath" || { echo "Error: Could not extract '$pakzippath' to '$destdir'" >&2 return 1 } return 0 } install_tgz() { pakzippath="$1" files=$(tar -ztf "$pakzippath") || { echo "Error: Cannot extract .tar.gz file (tar with gzip support required)" >&2 return 1 } # First check if we only have a simutrans/ directory at the root. files=$(echo "$files"|grep "^simutrans") if [ $? -eq 0 ]; then # has simutrans folder destdir="$(pwd)" extra="--strip-components=1" else mkdir -p simutrans destdir="$(pwd)/simutrans" extra="" fi # No quotes around $extra, since adding quotes breaks extraction if $extra evaluates to the empty string # (This will be interpreted as the file(s) to extract and not as a command line parameter) tar -xzf "$pakzippath" $extra -C "$destdir" || { echo "Error: Could not extract '$pakzippath' to '$destdir'" >&2 return 1 } return 0 } install_zip() { pakzippath="$1" # First check if we only have a simutrans/ directory at the root. files=$(unzip -Z1 "$pakzippath" | grep "^simutrans/") if [ $? -eq 0 ]; then # we have simutrans already destdir="$(pwd)" echo "Extracting '$pakzippath' to '$destdir'..." temp=$(mktemp -d) && unzip -qoC "$pakzippath" -d "$temp" && mv "$temp"/*/* "$destdir" && rmdir "$temp"/* "$temp" else destdir="$(pwd)" echo "Extracting '$pakzippath' to '$destdir'..." unzip -qoC "$pakzippath" -d "$destdir" fi result=$? if [ $result -ge 2 ]; then # unzip failed echo "Error: Could not extract '$pakzippath' to '$destdir' (unzip returned: $result)" >&2 return 1 fi return 0 } # two parameters: # - url # - zipfilename download_and_install_pakset() { url="$1" pakzippath="$2" echo "Downloading from '$1'" do_download "$url" "$pakzippath" || return 1 if [ -n "$(file "$pakzippath" | grep -i "Microsoft Cabinet archive data")" ]; then # .cab file install_cab "$pakzippath" elif [ -n "$(file "$pakzippath" | grep -i "gzip compressed data")" ]; then # .tar.gz install_tgz "$pakzippath" elif [ -n "$(file "$pakzippath" | grep -i "Zip archive data")" ]; then # .zip install_zip "$pakzippath" elif [ "$(od --address-radix=n --read-bytes=4 --format=x1 $pakzippath)" = " 50 4b 03 04" ]; then # also .zip, if file failed for some reason install_zip "$pakzippath" else rm -f "$pakzippath" echo "Error: Cannot extract unknown archive format" >&2 return 1 fi result=$? rm -f "$pakzippath" if [ $result -ne 0 ]; then echo "Installation failed." >&2 return 1 fi echo "Installation completed." return 0 } # generated list of pak sets obsolete_start_index=12 paksets=( \ "http://downloads.sourceforge.net/project/simutrans/pak64/124-4/simupak64-124-4.zip" \ "http://downloads.sourceforge.net/project/simutrans/pak128/pak128%20for%20ST%20124.4up%20%282.10.1%29/simupak128-2-10-1-for124-4up.zip" \ "http://downloads.sourceforge.net/project/simutrans/pak192.comic/pak192.comic%20V0.7.2/pak192-comic.zip" \ "http://simutrans-germany.com/pak.german/pak64.german_0-124-4-0-1_full.zip" \ "http://downloads.sourceforge.net/project/simutrans/PAK128.german/PAK128.german_2.4_for_ST_124.3.1/PAK128.german_2.4_for_ST_124.3.1.zip" \ "http://downloads.sourceforge.net/project/simutrans/pak64.japan/123-0/simupak64.japan-123-0.zip" \ "https://github.com/wa-st/pak-nippon/releases/download/v0.6.2/pak.nippon-v0.6.2.zip" \ "http://downloads.sourceforge.net/project/simutrans/Pak128.CS/nightly%20builds/pak128.CS-r2096.zip" \ "http://downloads.sourceforge.net/project/simutrans/pak128.britain/pak128.Britain%20for%20120-3/pak128.Britain.1.18-120-3.zip" \ "https://github.com/Varkalandar/pak144.Excentrique/releases/download/r0.08/pak144.Excentrique_v008.zip" \ "http://downloads.sourceforge.net/project/simutrans/pakTTD/simupakTTD-124-0.zip" \ "http://codeberg.org/Nazalassa/pak48.bitlit/releases/download/0.1d/pak48.bitlit_0.1d.zip" \ "http://downloads.sourceforge.net/project/simutrans/pak96.comic/pak96.comic%20for%20111-3/pak96.comic-0.4.10-plus.zip" \ "http://pak128.jpn.org/souko/pak128.japan.120.0.cab" \ "http://downloads.sourceforge.net/project/simutrans/pak32.comic/pak32.comic%20for%20102-0/pak32.comic_102-0.zip" \ "https://github.com/Varkalandar/pak48.Excentrique/releases/download/v0.19_RC3/pak48.excentrique_v019rc3.zip" \ "http://downloads.sourceforge.net/project/simutrans/pak64.contrast/pak64.Contrast_910.zip" \ "http://downloads.sourceforge.net/project/simutrans/OldFiles/PakHD_v04B_100-0.zip" \ "http://downloads.sourceforge.net/project/simutrans/pakHAJO/pakHAJO_102-2-2/pakHAJO_0-102-2-2.zip" \ "http://downloads.sourceforge.net/project/simutrans/pak64.scifi/pak64.scifi_112.x_v0.2.zip" \ # "https://simutrans.bilkinfo.de/pak64.ho-scale-latest.tar.gz" \ #lost it ) # # main script # choices=() installpak=() paks_header="../../src/paksetinfo.h" nsis_header="../../src/Windows/nsis/paksets.nsh" if [ "$#" -gt 0 ] && [ "$1" = '-generate_h' ]; then echo "Generate input file for Simutrans and NSIS" mkdir tmp cd tmp mkdir simutrans echo "; Generated by \"get_pak.sh -generate_h\". DO NOT EDIT." > "$nsis_header" echo "// Generated by \"get_pak.sh -generate_h\". DO NOT EDIT." > "$paks_header" echo "#define PAKSET_COUNT ${#paksets[@]}" >> "$paks_header" echo "#define OBSOLETE_FROM ($obsolete_start_index)" >> "$paks_header" echo "paksetinfo_t pakinfo[PAKSET_COUNT] = {" >> "$paks_header" pakcount=0 for urlname in "${paksets[@]}" do zipname="${urlname##http*\/}" echo "Downloading $zipname" do_download "$urlname" "$zipname" || return 1 downloadcall="" skip_in_exe="0" destdir="" if [ -n "$(file "$zipname" | grep -i "Microsoft Cabinet archive data")" ]; then # .cab file install_cab "$zipname" skip_in_exe="1" downloadcall="DownloadInstallCabWithoutSimutrans" elif [ -n "$(file "$zipname" | grep -i "gzip compressed data")" ]; then # .tar.gz install_tgz "$zipname" skip_in_exe="1" downloadcall="DownloadInstallTgzWithoutSimutrans" elif [ -n "$(file "$zipname" | grep -i "Zip archive data")" ]; then # .zip # First check if we only have a simutrans/ directory at the root. files=$(unzip -Z1 "$zipname" | grep "^simutrans/") if [ $? -eq 0 ]; then # we have simutrans already downloadcall="DownloadInstallZip" unzip -qoC "$zipname" else downloadcall="DownloadInstallZipWithoutSimutrans" unzip -qoC "$zipname" -d "simutrans" echo "without" fi else rm -f "$zipname" echo "Error: Cannot extract unknown archive format" >&2 exit 1 fi size="$(du -s -Bk simutrans | sed 's/K.*$//')" echo "Size $size" rm -rf simutrans/themes rm -rf simutrans/config rm -rf simutrans/addons choicename="$(ls simutrans)" echo "choicename >$choicename<" versionstring="" count="$( od -An -tu2 -j 99 -N2 --endian=little simutrans/$choicename/ground.Outside.pak | tr -d ' ')" echo "count $count" if [ "$count" != "0" ] ; then versionstring="$(dd bs=1 skip=101 count=$count if=simutrans/$choicename/ground.Outside.pak status=none)" echo "version $versionstring" fi # paksetinfo.h echo $'\t'\{ "\"$urlname\", \"$choicename\", \"$versionstring\", $size }," >> "$paks_header" # nsh output if ((pakcount == $obsolete_start_index )); then # obsolete paks from here printf '\n; OBSOLETE PAKS from here\nSectionGroup /e "Not currently developed" slowPakgroup\n\n' >> "$nsis_header" fi if [ "$choicename" == "pak" ]; then # sectiongroup for pak64 printf "SectionGroup /e \"Pak64: main and addons\" pak64group\n\n" >> "$nsis_header" fi # normal section echo $"Section /o \"$choicename\" $choicename" >> "$nsis_header" echo $" AddSize $size" >> "$nsis_header" echo $" StrCpy \$downloadlink \"$urlname\"" >> "$nsis_header" echo " SetOutPath \$PAKDIR" >> "$nsis_header" echo " StrCpy \$archievename \"$zipname\"" >> "$nsis_header" echo " StrCpy \$downloadname \"$choicename\"" >> "$nsis_header" echo " StrCpy \$VersionString \"$versionstring\"" >> "$nsis_header" echo " Call $downloadcall" >> "$nsis_header" echo "SectionEnd" >> "$nsis_header" if [ "$choicename" == "pak" ]; then # pak64 addons printf "Section /o \"pak64 Food addon\"\n AddSize 228\n StrCpy \$downloadlink \"http://downloads.sourceforge.net/project/simutrans/pak64/121-0/simupak64-addon-food-120-4.zip\"\n StrCpy \$archievename \"simupak64-addon-food-120-4.zip\"\n StrCpy \$downloadname \"pak\"\n" >> "$nsis_header" printf " StrCpy \$VersionString \"\"\n StrCmp \$multiuserinstall \"1\" +3\n ; no multiuser => install in normal directory\n Call DownloadInstallAddonZipPortable\n goto +2\n Call DownloadInstallAddonZip\nSectionEnd\n\n" >> "$nsis_header" printf "SectionGroupEnd\n\n" >> "$nsis_header" fi echo "" >> "$nsis_header" rm -rf simutrans rm -rf $zipname mkdir simutrans pakcount=$((pakcount+1)) done printf "SectionGroupEnd\n\n" >> "$nsis_header" echo "};" >> "$paks_header" cd .. rm -rf tmp exit 0 fi pwd | grep "/simutrans$" >/dev/null || { echo "Warning install csb-paksets might fail if not in a simutrans/ directory" >&2 } # first find out, if we have a command options and just install these paks # parameter is the full path to the pakset if [ "$#" -gt 0 ]; then for pakname in "$@" do if [[ $pakname == "http"* ]]; then # direct download if whatever zipname="${pakname##http*\/}" choicename="${zipname%.*}" choicename="${choicename%.tar}" # for .tar.gz choicename="${choicename/simupak/pak}" echo "-- Installing $choicename --" tempzipname="$TEMP/$zipname" download_and_install_pakset "$urlname" "$tempzipname" || { echo "Error installing pakset $choicename" exit 1 } else # find pakset with this folder name has_match="0" for urlname in "${paksets[@]}" do if [[ $urlname == *"/$pakname"/* ]]; then zipname="${urlname##http*\/}" choicename="${zipname%.*}" choicename="${choicename%.tar}" # for .tar.gz choicename="${choicename/simupak/pak}" echo "-- Installing $choicename --" tempzipname="$TEMP/$zipname" download_and_install_pakset "$urlname" "$tempzipname" || { echo "Error installing pakset $choicename" exit 1 } has_match="1" fi done if [[ $has_match == "0" ]]; then echo "No pak matches $pakname" exit 1 fi fi done exit 0 fi echo "-- Choose at least one of these paks --" let setcount=0 let choicecount=0 let "maxcount = ${#paksets[*]}" while [ "$setcount" -lt "$maxcount" ]; do installpak[choicecount]=0 urlname=${paksets[$setcount]} zipname="${urlname##http*\/}" choicename="${zipname%.*}" choicename="${choicename%.tar}" # for .tar.gz choicename="${choicename/simupak/pak}" choices[choicecount]=$choicename if [ "$setcount" -eq "$obsolete_start_index" ]; then echo "---- Obsolete (unmaintained) paks below ----" fi let "setcount += 1" let "choicecount += 1" echo "${choicecount}) ${choicename}" done while true; do read -p "Which paks to install? (enter number or (i) to install or (x) to exit)" pak #exit? if [[ $pak = [xX] ]]; then exit 0 fi # test if installation now if [[ $pak = [iI] ]]; then echo "" echo "You will install now" let setcount=0 while [ $setcount -lt $choicecount ]; do if [ ${installpak[$setcount]} -gt 0 ]; then let "displaycount=$setcount+1" echo "${displaycount}) ${choices[$setcount]}" fi let "setcount += 1" done echo "into directory '$(pwd)'" read -p "Is this correct? (y/n)" yn if [[ $yn = [yY] ]]; then break fi # edit again echo "" echo "-- Choose again one of these paks --" let setcount=0 while [ $setcount -lt $choicecount ]; do echo "${setcount}) ${choices[$setcount]}" let "setcount += 1" done let "pak=0" fi # otherwise it should be a number if [[ $pak =~ ^[0-9]+$ ]]; then let "setcount=pak-1" if [ $setcount -lt $choicecount ]; then if [ $setcount -ge 0 ]; then status=${installpak[$setcount]} if [ $status -lt 1 ]; then echo "adding ${choices[$setcount]}" installpak[$setcount]=1 else echo "not installing ${choices[$setcount]}" installpak[$setcount]=0 fi fi fi fi done echo "" # install the regular pak sets first let setcount=0 let "maxcount = ${#paksets[*]}" fail=0 while [ "$setcount" -lt "$maxcount" ]; do if [ "${installpak[$setcount]}" -gt 0 ]; then urlname=${paksets[$setcount]} zipname="${urlname##http*\/}" echo "-- Installing ${choices[$setcount]} --" tempzipname="$TEMP/$zipname" download_and_install_pakset "$urlname" "$tempzipname" || { echo "Error installing pakset ${choices[$setcount]}" let "fail++" } echo "" fi let "setcount++" done exit $fail simutrans-124.5/tools/get_revision.bat000066400000000000000000000001751520675460500201140ustar00rootroot00000000000000powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -command "tools/get_revision.ps1 src/simutrans/revision.h" simutrans-124.5/tools/get_revision.ps1000066400000000000000000000026601520675460500200520ustar00rootroot00000000000000$revision = 0 $answer = svnversion $has_svn = $? #$answer=$answer.Trim("MSP:") $answer=$answer.substring(0,5); $is_number = ($answer -and $null -ne ($answer -as [int])) if($has_svn -and $is_number) { # svnversion valid number $revision = [int]$answer } else { # try git $answer = git log -1 if($?) { # we have git! $answer = $answer | Select-String -Pattern 'git-svn-id:' if($null -ne $answer) { $answer = $answer -match "trunk\@(.*) " $answer = $matches[1] $is_number = ($null -ne ($answer -as [int])) } if(!$is_number) { # not a standard git use some random number ... $answer=git rev-list --count --first-parent HEAD $is_number = ($null -ne ($answer -as [int])) if($is_number) { # svnversion valid number $answer = [int]$answer $answer = $answer + 331 } } } } if(!$is_number -and $has_svn) { # svn cammnd exists but not repo $answer = svn info svn://servers.simutrans.org/simutrans $answer = $answer | Select-String -Pattern 'Revision:' $a=$answer.tostring(); $answer = $a.Substring(10) $is_number = ($null -ne ($answer -as [int])) } if($is_number) { # valid revision ... $answer if($args.count -eq 1) { # create revision.h "Create $($args[0])" $revfile = "#define REVISION $answer`n" $revfile | Out-File $args[0] } } else { "0 (undefined)" if($args.count -eq 1) { # create revision.h $revfile = "#define REVISION unrevisioned`n" $revfile | Out-File $($args[0]) } } simutrans-124.5/tools/get_revision.sh000077500000000000000000000012271520675460500177620ustar00rootroot00000000000000#!/bin/bash # # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # # script to the revision number and put it into the file revision.h # svn info 2>/dev/null 1>/dev/null if [ $? -eq 0 ]; then # using svn for verything REV=$(svnversion) HASH=$REV else # try using git git describe --always 2>/dev/null 1>/dev/null if [ $? -eq 0 ]; then REV=$(git log -1 | grep "git-svn-id" | sed "s/^.*trunk\@//" | sed "s/ .*$//") if [ -z "$REV" ]; then # make preudo rev from git REV=$(git rev-list --count --first-parent HEAD) REV=`expr $REV + 328` fi else REV="" fi fi echo "$REV"simutrans-124.5/tools/install-building-libs-arm64.bat000066400000000000000000000007271520675460500225410ustar00rootroot00000000000000@echo Please add the english language pack to MSVC. Then clone @echo https://github.com/Microsoft/vcpkg @echo open a commandline in this folder and run @echo then run bootstrap-vcpkg.bat before running this script @echo from the vcpgk folder! @pause vcpkg integrate install @rem arm64 builds vcpkg install freetype:arm64-windows-static vcpkg install miniupnpc:arm64-windows-static vcpkg install pthreads:arm64-windows-static vcpkg install zstd:arm64-windows-static @pausesimutrans-124.5/tools/install-building-libs-x64.bat000066400000000000000000000007201520675460500222220ustar00rootroot00000000000000@echo Please add the english language pack to MSVC. Then clone @echo https://github.com/Microsoft/vcpkg @echo open a commandline in this folder and run @echo then run bootstrap-vcpkg.bat before running this script @echo from the vcpgk folder! @pause vcpkg integrate install @rem 64 bit builds vcpkg install freetype:x64-windows-static vcpkg install miniupnpc:x64-windows-static vcpkg install pthreads:x64-windows-static vcpkg install zstd:x64-windows-static @pausesimutrans-124.5/tools/install-building-libs-x86.bat000066400000000000000000000007201520675460500222260ustar00rootroot00000000000000@echo Please add the english language pack to MSVC. Then clone @echo https://github.com/Microsoft/vcpkg @echo open a commandline in this folder and run @echo then run bootstrap-vcpkg.bat before running this script @echo from the vcpgk folder! @pause vcpkg integrate install @rem 32 bit builds vcpkg install freetype:x86-windows-static vcpkg install miniupnpc:x86-windows-static vcpkg install pthreads:x86-windows-static vcpkg install zstd:x86-windows-static @pausesimutrans-124.5/tools/nwc_protocol_test/000077500000000000000000000000001520675460500204735ustar00rootroot00000000000000simutrans-124.5/tools/nwc_protocol_test/__init__.py000066400000000000000000000015721520675460500226110ustar00rootroot00000000000000# Black-box e2e tests for the multiplayer wire protocol in # src/simutrans/network/network_cmd*.cc. Each test spawns a headless # server, sends one hand-rolled packet, and asserts on the parsed # reply. Two purposes: pin reply shapes (NWC_AUTH_PLAYER, NWC_STEP # heartbeat) so a refactor that drops or reorders a field breaks CI, # and trip on the recent network security fixes that manifest on the # wire as "server consumes the packet then crashes". # # Run (from repo root): # python3 -m unittest discover -s tools/nwc_protocol_test -t . # python3 -m unittest discover -s tools/nwc_protocol_test -t . -k null # python3 -m unittest tools.nwc_protocol_test.test_auth_player # # Requires a built simutrans binary, a pak64-compatible pakset under # simutrans/pak/, and tests/empty-16x16.sve (the starter map leaves # player slots 2..14 unfilled, which the NULL-slot test relies on). simutrans-124.5/tools/nwc_protocol_test/test_auth_player.py000077500000000000000000000061531520675460500244310ustar00rootroot00000000000000#!/usr/bin/env python3 # NWC_AUTH_PLAYER wire pin + tripwire for the NULL-player-slot crash # in nwc_auth_player_t::execute (src/simutrans/network/network_cmd_ingame.cc). # Pre-fix, a peer-supplied player_nr in [2, 14] dereferenced a NULL # player slot in the default starter map. The fix returns the same # wrong-password reply shape, so a byte-exact reply pin doubles as # the regression check. import struct import unittest from . import wire # nwc_auth_player_t wire layout (NETWORK_VERSION=1): # header : u16 size | u16 version | u16 id (6 bytes) # base : u32 our_client_id (4 bytes) # payload : u8[20] hash | u8 player_nr | u16 player_unlocked (23 bytes) # Total: 33 bytes both ways. PACKET_SIZE = 33 def build(*, player_nr: int, our_client_id: int = 0, hash20: bytes = b"\x00" * 20, player_unlocked: int = 0) -> bytes: if len(hash20) != 20: raise ValueError(f"hash20 must be 20 bytes, got {len(hash20)}") body = struct.pack(" None: pkt = build(player_nr=player_nr, hash20=hash20) reply = wire.send_and_recv_exact(self.srv, pkt, PACKET_SIZE) if len(reply) != PACKET_SIZE: self.fail( f"reply length {len(reply)} != {PACKET_SIZE}; " f"raw: {reply.hex()}; stderr tail:\n" + self.srv.read_stderr_tail() ) size, version, pkt_id = wire.unpack_header(reply) self.assertEqual(size, PACKET_SIZE) self.assertEqual(version, wire.NETWORK_VERSION) self.assertEqual(pkt_id, wire.NWC_AUTH_PLAYER) player_unlocked = struct.unpack("get_player(...)->access_password_hash().""" self.check_reply(player_nr=3, expected_unlocked=0) def test_filled_slot_empty_password(self): """Public service (slot 1) with an all-zero hash matches the default empty password — bit 1 set in player_unlocked.""" self.check_reply(player_nr=1, expected_unlocked=1 << 1) def test_filled_slot_wrong_password(self): """Wrong password on a filled slot returns player_unlocked=0 — the silent-fail shape the NULL-slot path now mirrors.""" self.check_reply(player_nr=1, expected_unlocked=0, hash20=b"\xff" * 20) simutrans-124.5/tools/nwc_protocol_test/test_service.py000077500000000000000000000037741520675460500235620ustar00rootroot00000000000000#!/usr/bin/env python3 # NWC_SERVICE wire pin + tripwire for the pre-auth list-shaped OOM # in nwc_service_t::rdwr (src/simutrans/network/network_cmd.cc). # Pre-fix the server-side rdwr was wire-symmetric, so a forged # SRVC_GET_CLIENT_LIST with count=0xFFFFFFFF drove socket_list_t::rdwr # through ~4 billion allocations until std::bad_alloc. The # admin-auth gate in execute() only ran after rdwr returned. The # fix makes server-side reads of the list-shaped flags no-ops; only # the saving (response) leg touches socket_list_t / address_list_t. import struct import unittest from . import wire SRVC_LOGIN_ADMIN = 0 SRVC_ANNOUNCE_SERVER = 1 SRVC_GET_CLIENT_LIST = 2 SRVC_GET_BLACK_LIST = 5 # We always append the wire-controlled count so the packet shape # exercises the pre-fix vulnerable read path regardless of flag. def build(*, flag: int, number: int = 0, count: int = 0, our_client_id: int = 0) -> bytes: body = struct.pack(" bytes: if tool_client_id is None: tool_client_id = our_client_id body = struct.pack(".py. import fcntl import os import shutil import signal import socket import struct import subprocess import sys import tempfile import time from pathlib import Path ROOT = Path(__file__).resolve().parents[2] PAK_DIR = ROOT / "simutrans" / "pak" BASE_DIR = ROOT / "simutrans" EMPTY_SAVE = ROOT / "tests" / "empty-16x16.sve" NETWORK_VERSION = 1 HEADER_SIZE = 6 # Mirrors network_command_id in src/simutrans/network/network_cmd.h. NWC_GAMEINFO = 1 NWC_JOIN = 4 NWC_SYNC = 5 NWC_GAME = 6 NWC_READY = 7 NWC_TOOL = 8 NWC_CHECK = 9 NWC_SERVICE = 11 NWC_AUTH_PLAYER = 12 NWC_CHG_PLAYER = 13 NWC_STEP = 16 _PACKET_NAMES = { NWC_GAMEINFO: "NWC_GAMEINFO", NWC_JOIN: "NWC_JOIN", NWC_SYNC: "NWC_SYNC", NWC_GAME: "NWC_GAME", NWC_READY: "NWC_READY", NWC_TOOL: "NWC_TOOL", NWC_CHECK: "NWC_CHECK", NWC_SERVICE: "NWC_SERVICE", NWC_AUTH_PLAYER: "NWC_AUTH_PLAYER", NWC_CHG_PLAYER: "NWC_CHG_PLAYER", NWC_STEP: "NWC_STEP", } def pkt_name(pkt_id: int) -> str: return f"{_PACKET_NAMES.get(pkt_id, '?')}({pkt_id})" def find_simutrans() -> Path: for candidate in [ ROOT / "build-headless" / "simutrans" / "simutrans", ROOT / "build" / "simutrans" / "simutrans", ROOT / "sim", ]: if candidate.is_file() and os.access(candidate, os.X_OK): return candidate sys.exit("no simutrans binary found; build with cmake or autoconf first") def require_pakset(): if not PAK_DIR.is_dir() or not any(PAK_DIR.iterdir()): sys.exit(f"no pakset at {PAK_DIR}; run " "`cd simutrans && ../tools/get_pak.sh pak64` once") if not EMPTY_SAVE.is_file(): sys.exit(f"missing starter savegame {EMPTY_SAVE}") def _free_port() -> int: # Brief TOCTOU between close and simutrans binding; a collision # surfaces as the same "did not start listening" error as a real # launch failure, which is acceptable. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("127.0.0.1", 0)) return s.getsockname()[1] class Server: """Context manager spawning a headless server on a free port. Each instance gets its own temp userdir so concurrent runs don't collide with each other or with a developer's ~/simutrans tree.""" def __init__(self, port: int | None = None): self.port = port if port is not None else _free_port() self.proc: subprocess.Popen | None = None self.userdir: Path | None = None def __enter__(self) -> "Server": self.userdir = Path(tempfile.mkdtemp(prefix="nwc-e2e-")) save_dir = self.userdir / "save" save_dir.mkdir(parents=True) shutil.copyfile(EMPTY_SAVE, save_dir / "empty.sve") sim = find_simutrans() require_pakset() env = os.environ.copy() env.setdefault("SDL_VIDEODRIVER", "dummy") args = [ str(sim), "-set_basedir", str(BASE_DIR), "-set_userdir", str(self.userdir), "-objects", "pak", "-nomidi", "-nosound", "-mute", "-server", str(self.port), "-load", "empty", "-debug", "2", ] self.proc = subprocess.Popen( args, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, ) # Non-blocking so read_stderr_tail won't wait for a full read # or process exit when called from an assertion message. fd = self.proc.stderr.fileno() fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK) if not self._wait_listening(timeout=20.0): stderr = self.read_stderr_tail(limit=8192) self.__exit__(None, None, None) raise RuntimeError( f"server did not start listening on {self.port} within " f"20s; stderr tail:\n{stderr}" ) return self def __exit__(self, *exc): if self.proc is not None: if self.proc.poll() is None: self.proc.send_signal(signal.SIGTERM) try: self.proc.wait(timeout=5) except subprocess.TimeoutExpired: self.proc.kill() self.proc.wait() if self.proc.stderr is not None: self.proc.stderr.close() if self.userdir and self.userdir.exists(): shutil.rmtree(self.userdir, ignore_errors=True) def _wait_listening(self, timeout: float) -> bool: deadline = time.monotonic() + timeout while time.monotonic() < deadline: if self.proc and self.proc.poll() is not None: return False try: with socket.create_connection(("127.0.0.1", self.port), timeout=0.5): return True except OSError: time.sleep(0.2) return False def alive(self) -> bool: """Process running AND port accepting new connections.""" if self.proc is None or self.proc.poll() is not None: return False try: with socket.create_connection(("127.0.0.1", self.port), timeout=1.0): return True except OSError: return False def read_stderr_tail(self, limit: int = 4096) -> str: if not self.proc or not self.proc.stderr: return "" try: chunk = os.read(self.proc.stderr.fileno(), limit) except BlockingIOError: return "" return chunk.decode(errors="replace")[-2000:] def pack_header(packet_id: int, body: bytes) -> bytes: # `size` is the total packet length including header — matches # network_command_t::rdwr on the wire. size = HEADER_SIZE + len(body) return struct.pack(" tuple[int, int, int]: if len(data) < HEADER_SIZE: raise AssertionError( f"need {HEADER_SIZE}B for header, got {len(data)}: {data.hex()}" ) return struct.unpack(" bytes: sock.settimeout(timeout) buf = b"" while len(buf) < n: try: chunk = sock.recv(n - len(buf)) except (socket.timeout, OSError): break if not chunk: break buf += chunk return buf def read_packets(sock: socket.socket, timeout: float, max_packets: int = 8) -> list[tuple[int, bytes]]: """Drain whole packets from `sock` as `(packet_id, body)` tuples. Stops on close, timeout, or after `max_packets`.""" sock.settimeout(timeout) out: list[tuple[int, bytes]] = [] buf = b"" deadline = time.monotonic() + timeout while len(out) < max_packets and time.monotonic() < deadline: try: chunk = sock.recv(4096) except (socket.timeout, OSError): break if not chunk: break buf += chunk while True: if len(buf) < HEADER_SIZE: break size, version, pkt_id = struct.unpack("= max_packets: break return out def send_and_recv_exact(srv: "Server", pkt: bytes, n: int, recv_timeout: float = 3.0, connect_timeout: float = 2.0) -> bytes: with socket.create_connection(("127.0.0.1", srv.port), timeout=connect_timeout) as s: s.sendall(pkt) return recv_exact(s, n, timeout=recv_timeout) def assert_heartbeat(srv: "Server", pkt: bytes, drain_seconds: float = 3.0, min_steps: int = 1) -> None: """Send `pkt`, drain the reply, require ≥`min_steps` NWC_STEP heartbeats and a still-alive server. The "silently absorbs a malicious packet and keeps ticking" assertion.""" try: with socket.create_connection(("127.0.0.1", srv.port), timeout=2.0) as s: s.sendall(pkt) packets = read_packets(s, timeout=drain_seconds, max_packets=16) except OSError as e: raise AssertionError( f"send failed: {e}; stderr tail:\n{srv.read_stderr_tail()}" ) steps = [p for p in packets if p[0] == NWC_STEP] if len(steps) < min_steps: names = [pkt_name(pid) for pid, _ in packets] raise AssertionError( f"saw {len(steps)} NWC_STEP heartbeat(s), wanted " f">= {min_steps}; received = {names}; " f"stderr tail:\n{srv.read_stderr_tail()}" ) if not srv.alive(): raise AssertionError( "heartbeat seen but server failed to accept a new " "connection after; stderr tail:\n" + srv.read_stderr_tail() ) class ServerTestCase: """Mixin: fresh `Server` per test method on `self.srv`. Per-test isolation is deliberate — a packet that crashes the server must not poison the next test.""" def setUp(self): super().setUp() self.srv = self.enterContext(Server()) simutrans-124.5/tools/run-automated-tests.sh000077500000000000000000000014261520675460500212130ustar00rootroot00000000000000#!/bin/bash set -e pushd simutrans ../sim -use_workdir -objects pak -lang en -scenario automated-tests -addons -debug 2 2>&1 | ts -s | tee output.log & pid=$! result=1 while : do sleep 1 if [[ ! -d /proc/$pid/ ]] then # process crashed etc. echo "Process crashed (test failed)" result=1 break fi if [[ -n "$(grep 'Tests completed successfully.' output.log)" ]] then # kill echo "Killing process (test succeeded)" kill %1 result=0 break fi if [[ -n "$(grep 'error \[Call function failed\] calling' output.log)" || -n "$(grep 'error \[Reading / compiling script failed] calling' output.log)" || -n "$(grep '' output.log)" ]] then # kill echo "Killing process (test failed)" kill %1 result=1 break fi done popd exit $result simutrans-124.5/tools/setup-debian.sh000077500000000000000000000006161520675460500176460ustar00rootroot00000000000000#!/bin/bash echo "We need to have a current system or it fails" apt update apt -y upgrade echo "Now installing dependencies" # neccessary packets apt -y install autoconf pkg-config subversion zip unzip cmake make apt -y install gcc g++ libbz2-dev libfreetype-dev libfontconfig-dev libpng-dev libminiupnpc-dev libzstd-dev libfluidsynth-dev # optional for SLD2 builds: apt -y install libsdl2-dev simutrans-124.5/tools/setup-mingw.sh000077500000000000000000000025011520675460500175400ustar00rootroot00000000000000#!/bin/bash echo "First we update the system" pacman -Syu pacman -Syu echo "Now installing dependencies" # neccessary packets pacman -S --noconfirm make autoconf pkg-config subversion zip unzip zstd pacman -S --noconfirm $MINGW_PACKAGE_PREFIX-gcc $MINGW_PACKAGE_PREFIX-bzip2 $MINGW_PACKAGE_PREFIX-freetype $MINGW_PACKAGE_PREFIX-libpng $MINGW_PACKAGE_PREFIX-brotli $MINGW_PACKAGE_PREFIX-cmake # optional for SLD2 builds: pacman -S --noconfirm $MINGW_PACKAGE_PREFIX-SDL2 # optional for installer: pacman -S --noconfirm $MINGW_PACKAGE_PREFIX-nsis pacman -S --noconfirm $MINGW_PACKAGE_PREFIX-miniupnpc exit 0 # That should be not longer needed rm -rf master.zip wget https://github.com/miniupnp/miniupnp/archive/master.zip unzip -o master.zip cd miniupnp-master/miniupnpc cat Makefile.mingw | sed 's|[ \t]wingenminiupnpcstrings.exe |'"$(printf '\t')"'./wingenminiupnpcstrings.exe |' >Makefile.mingw2 make -f Makefile.mingw2 cp libminiupnpc.a $MINGW_PREFIX/lib cp *.h $MINGW_PREFIX/lib mkdir -p $MINGW_PREFIX/include/miniupnpc cp *.h $MINGW_PREFIX/include/miniupnpc cp include/*.h $MINGW_PREFIX/include/miniupnpc cd ../.. rm -rf master.zip rm -rf miniupnpc-master # libbrotli static is broken but needed for freetype for f in libbrotlidec libbrotlienc libbrotlicommon; do mv "$MINGW_PREFIX/lib/$f-static.a" "$MINGW_PREFIX/lib/$f.a" done simutrans-124.5/tools/zipsrc.sh000077500000000000000000000033741520675460500166040ustar00rootroot00000000000000#!/bin/bash # packen() { # pack all files of the current release FILELISTE=`find . -type f "(" -name "*.c" -o -name "*.h" -o -name "*.cc" -o -name "*.mm" ")" -not -path */.vs/* -not -path */vcpkg_installed/*` FILELISTF=`find . -type f "(" -name "*.sh" -o -name "*.mk" -o -name "*.nut" ")" -not -path */.vs/* -not -path */vcpkg_installed/*` FILELISTG=`find . -type f "(" -name "*.txt" -o -name "*.bdf" -o -name "*.fnt" -o -name "*.mid" -o -name "*.tab" ")" -not -path */.vs/* -not -path */vcpkg_installed/*` FILELISTH=`find . -type f "(" -name "*.tab" -o -name "*.dat" -o -name "*.png" -o -name "*.svg" -o -name "*.desktop" ")" -not -path */.vs/* -not -path */vcpkg_installed/*` FILELISTI=`find . -type f "(" -name "*.vcxproj" -o -name "*.vcxproj.filters" -o -name "*.sln" -o -name "*.vcxitems" -o -name "*.json" ")" -not -path */.vs/* -not -path */vcpkg_installed/*` rm ../simutrans-src.zip zip ../simutrans-src.zip $FILELISTE zip ../simutrans-src.zip $FILELISTF zip ../simutrans-src.zip $FILELISTG zip ../simutrans-src.zip $FILELISTH zip ../simutrans-src.zip $FILELISTI zip ../simutrans-src.zip Makefile src/makeobj/Makefile src/nettool/Makefile zip ../simutrans-src.zip cmake/*.cmake zip ../simutrans-src.zip configure.ac config.default.in config.template src/OSX/simutrans.icns zip -r ../simutrans-src.zip src/Windows } # not used (stumbles in private: in class definition ... :( ) beautify() { # convert all source files and reformats them FILELISTE=`find . -type f "(" -name "*.c" -o -name "*.h" -o -name "*.cc" ")"` for file in $FILELISTE; do bcpp -f 2 -t -i 2 -ylcnc -yq bcl -yo -qb 1000 < $file > _tmpfile touch -r $file _tmpfile echo "bcpp $file" cp -f _tmpfile $file rm -f _tmpfile done } # take action! cd .. autoconf packen simutrans-124.5/translate_code/000077500000000000000000000000001520675460500165535ustar00rootroot00000000000000simutrans-124.5/translate_code/base_things.csv000066400000000000000000000004431520675460500215570ustar00rootroot00000000000000neues_jahr,new_year zeige_info,open_info_window neuer_monat,new_month finde_anzahl_hersteller,count_producers anzahl_hersteller,producer_count anzahl_staedte,city_count vehikel_anzahl,vehicle_count get_bild_anzahl,get_count get_waren_anzahl,get_count get_anzahl_besch,get_count anzahl,count simutrans-124.5/translate_code/batch_replace.sh000077500000000000000000000006521520675460500216710ustar00rootroot00000000000000#/bin/bash while IFS=, read from to do echo "Replace $from by $to" REPLACE="s#$from#$to#g" find ../ -name "*.cc" -or -name "*.h" -or -name "Makefile" -or -name "*.*proj" | while read fname; do sed "${REPLACE}" $fname > tmp.sed if [ $? -ne 0 ]; then break fi cmp -s tmp.sed $fname res=$? if [ $res -eq 1 ] then echo "Replace in $fname" mv tmp.sed $fname else rm tmp.sed fi done done < $1 simutrans-124.5/translate_code/translate_besch.csv000066400000000000000000000130011520675460500224240ustar00rootroot00000000000000\\\\([^.]\\),_desc\\1 baum_besch_t,tree_desc_t comapility,compatibility compare_baum_desc,compare_tree_desc baumlist,tree_list baum_typen,tree_list baumtype,tree_id bruecke_besch_t,bridge_desc_t _Rampe,_Ramp _rampe,_ramp bruecken_by_name,desc_table finde_ende,find_end_pos ist_ende_ok,can_place_ramp baue_auffahrt,build_ramp baue_bruecke,build_bridge \\,build brueckenbauer_t,bridge_builder_t gewichtung,chance field_class_besch_t,field_class_desc_t field_group_besch_t,field_group_desc_t rauch_besch_t,smoke_desc_t fabrik_lieferant_besch_t,factory_supplier_desc_t kapazitaet,capacity verbrauch\\>,consumption fabrik_produkt_besch_t,factory_product_desc_t faktor,factor fabrik_besch_t,factory_desc_t platzierung,placement produktivitaet,productivity bereich,range kennfarbe\\([^._]\\),color\\1 lieferanten,supplier_count produkte\\>,product_count lieferant,supplier produkt,product get_rauch,get_smoke fabrikbauer_t,factory_builder_t fabs_to,factories_to finde_hersteller,find_producer verteile_tourist,distribute_attractions baue_fabrik,build_factory, baue_hierarchy,build_chain baue_link_hierarchie,build_chain_link ist_bauplatz,check_construction_site finde_zufallsbauplatz,find_random_construction_site fab_desc,fac_desc fablist,factory_list hersteller,producer_d \\,fd Wasser,Water Stadt,City fussgaenger_besch_t,pedestrian_desc_t grund_besch_t,ground_besch_t ausserhalb,outside haus_tile_besch_t,building_tile_desc_t haus_besch_t,building_desc_t phasen,phases \\,building haus_desc,building_desc FLAG_KEINE_INFO,FLAG_NO_INFO FLAG_KEINE_GRUBE,FLAG_NO_PIT get_h\\>,get_y get_b\\>,get_x ist_mit_boden,needs_ground ist_ohne_grube,no_construction_pit ist_ohne_info,no_info_window get_post_level,get_mail_level unbekannt,unknown denkmal,monument ist*_fabrik\\>,is_factory \\,factory ist_rathaus,is_townhall rathaus,townhall weitere,others ist_firmensitz,is_headquarter ist_ausflugsziel,is_attraction firmensitz,headquarter get_haus\\>,get_building \\,bdsc \\,tdsc layout_anpassen,adjust_layout hauslist,building_list baue_hierarchie,build_link alles_geladen,successfully_loaded waehle_aus_liste,get_random_desc waehle_sehenswuerdigkeit,get_random_attraction waehle_monument,get_random_monument monument_gebaut,monument_erected neues_gebaeude,build_station_extension_depot sehenswuerdigkeiten_land,attractions_land sehenswuerdigkeiten_city,attractions_city ungebaute_denkmaeler,unbuilt_monuments denkmaeler,monuments rathaeuser,townhalls wohnhaeuser,city_residential gewerbehaeuser,city_commercial industriehaeuser,city_industry warne_ungeladene,warn_missing_objects spezial_objekte,special_objects wegtyp1,waytype1 wegtyp2,waytype2 kreuzung_besch_t,crossing_desc_t roadsign_besch_t,roadsign_desc_t sound_besch_t,sound_desc_t \\,build_tunnel baue_einfahrt,build_tunnel_portal tunnelbauer_t,tunnel_builder_t find_tunnel,get_tunnel_desc zuladung,capacity gewicht,weight sum_gear_und_leistung,sum_gear_and_power leistung,power \\,leader_count \\,trailer_count get_vorgaenger_count,get_leader_count get_nachfolger_count,get_trailer_count get_vorgaenger,get_leader get_nachfolger,get_trailer get_betriebskosten,get_running_cost vehikelbauer_t,vehicle_builder_t weg_besch_t,way_desc_t weg_desc,way_desc alle_wegtypen,desc_table alle_wege_geladen,successfully_loaded bruecke_desc,bridge_desc wegbauer_t,way_builder_t baue_tunnel_und_bruecken,build_tunnel_and_bridges baue_tunnelboden,build_tunnel_tile baue_elevated,build_elevated baue_strasse,build_road baue_schiene,build_track baue_leitung,build_powerline baue_fluss,build_river route_fuer,init_builder check_for_leitung,check_powerline get_image_id_switch,get_switch_image_id get_hang_imageid,get_slope_image_id hang_img,slope_img get_diagonal_imageid,get_diagonal_image_id is_hintergrund_phases,is_background_animated vehikel_besch_t,vehicle_desc_t \\,list \\,list2d ware_besch_t,goods_desc_t good_reader_t,goods_reader_t good_writer_t,goods_writer_t warenbauer_t,goods_manager_t load_passagiere,load_passengers load_post,load_mail load_nichts,load_none \\,goods :post,:mail \\,passengers \\,none ware_index,goods_index get_post_enabled,get_mail_enabled \\,mail mail-p,post-p skin_besch_t,skin_desc_t xref_besch_t,xref_desc_t baum_besch,tree_desc bild_besch,image bruecke_besch,bridge_desc fabrik_besch,factory_desc fussgaenger_besch,pedestrian_desc groundobj_besch,groundobj_desc grund_besch,ground_desc haus_besch,building_desc kreuzung_besch,crossing_desc obj_besch,obj_desc obj_besch_std_name,obj_base_desc roadsign_besch,roadsign_desc skin_besch,skin_desc sound_besch,sound_desc stadtauto_besch,citycar_desc text_besch,text_desc tunnel_besch,tunnel_desc vehikel_besch,vehicle_desc ware_besch,goods_desc way_obj_besch,way_obj_desc weg_besch,way_desc xref_besch,xref_desc besch\\/,descriptor/ ground_besch,ground_desc _BESCH_H,_DESC_H GETBESCHFUNC,GETDESCFUNC begin_besch_class,begin_desc_class export_besch,export_desc _besch_,_desc_ besch_alt,besch_old fabesch,factory_desc besch1,desc1 besch2,desc2 besch_vorgaenger,desc_leader besch_nachfolger,desc_trailer Modulbeschreibung,Description besch_,desc_ besch\\\\,descriptor\\\\ \\,desc simutrans-124.5/translate_code/translate_bild.csv000066400000000000000000000016171520675460500222640ustar00rootroot00000000000000target_besch,target_image bild_dest,image_dest \\,imglist \\,imgarray get_daten,get_data get_nummer,get_id bild_besch_t,image_t get_bild_nr,get_image_id get_vordergrund,get_foreground get_hintergrund,get_background get_bilder,get_images get_bild,get_image bild_besch_t,image_t bildliste_besch_t,image_list_t bildliste2d_besch_t,image_array_t get_liste,get_list calc_bild,calc_image set_bild,set_image \\,image Vordergrundbilder,Foreground-images Hintergrundbilder,Background-images back_bild,back_image backbild,back_image after_bild,foreground_image tmp_bild,tmp_image bild_nr,imageid _image_nr,_imageid bild0,image0 basis_bild,base_image bild_data,image_data _bild\\>,_image Bildliste2D,Image-list 2d Bildliste,Image-list baum_bild_alter,tree_age_index ground_bild_list,ground_image_list bild_index,index bild_in,image_in Kindknoten,Child nodes Beschreibung\\>,Description (keine),(none) simutrans-124.5/translate_code/translate_dataobj.csv000066400000000000000000000021521520675460500227510ustar00rootroot00000000000000autofahrplan,truck_schedule zugfahrplan,train_schedule schifffahrplan,ship_schedule airfahrplan,airplane_schedule monorailfahrplan,monorail_schedule tramfahrplan,tram_schedule maglevfahrplan,maglev_schedule narrowgaugefahrplan,narrowgauge_schedule fpl,schedule ist_abgeschlossen,is_editing_finished abgeschlossen,editing_finished eingabe_abschliessen,finish_editing eingabe_beginnen,start_editing ladegrad,minimum_loading aktuell\\>,current_stop fehlermeldung,get_error_msg anzahl_staedte,city_count einwohnerzahl,citizen_count linieneintrag,schedule_entry current_eintrag,current_entry dummy_eintrag,dummy_entry eintrag\\>,entries mittlere_,mean_ zustand,state neue_karte,new_world \\,schedule fahrplan_gui_,schedule_gui_ tool_fahrplan_,tool_schedule_ groesse_x,size_x groesse_y,size_y groesse\\>,size karte_nummer,map_number nummer\\>,map_number verkehr_level,traffic_level position_bei,at FAHRPLANEINGABE,EDIT_SCHEDULE fahrplan_gui,schedule_gui fahrplaneingabe,open_schedule_editor fahrplan_h,schedule_h fahrplan_t,schedule_t set_fahrplan,set_schedule fahrplan_index,schedule_index ist_halt_erlaubt,is_stop_allowed simutrans-124.5/translate_code/translate_display.csv000066400000000000000000000000751520675460500230140ustar00rootroot00000000000000karte_ansicht_t,main_view_t stunden,hours IMG_LEER,IMG_EMPTY simutrans-124.5/translate_code/translate_fahrplan_h.csv000066400000000000000000000000731520675460500234470ustar00rootroot00000000000000fahrplan\\.h,schedule.h linieneintrag\\.h,schedule_entry.h simutrans-124.5/translate_code/translate_nsew.csv000066400000000000000000000001441520675460500223200ustar00rootroot00000000000000nordost,northeast suedost,southeast ostwest,eastwest nord,north sued,south \\,east nsow,nsew simutrans-124.5/translate_code/translate_ribi.csv000066400000000000000000000017301520675460500222730ustar00rootroot00000000000000hang_t::typ,slope_t::type hang_t::doppel,slope_t::doubles hang_t::wegbar_ns,slope_t::way_ns hang_t::wegbar_ow,slope_t::way_ew hang_t::einfach,slope_t::single hang_t::all_up,slope_t::all_up corner1,corner_sw corner2,corner_se corner3,corner_ne corner4,corner_nw slope_t::corner,slope4_t::corner hang_typ,slope_type hang_t::flach,slope_t::flat hang_t::erhoben,slope_t::raised hang_t::gegenueber,slope_t::opposite hang_t::ist_einfach,slope_t::is_single hang_t::ist_wegbar_ns,slope_t::is_way_ns hang_t::ist_wegbar_ow,slope_t::is_way_ew hang_t::ist_wegbar,slope_t::is_way hang_t::,slope_t:: ribi_t::ist_exakt_orthogonal,ribi_t::is_perpendicular ribi_t::ist_einfach,ribi_t::is_single ribi_t::ist_kurve,ribi_t::is_bend ribi_t::ist_gerade_ns,ribi_t::is_straight_ns ribi_t::ist_gerade_ow,ribi_t::is_straight_ew ribi_t::ist_gerade,ribi_t::is_straight ribi_t::doppelt,ribi_t::doubles ribi_t::rueckwaerts,ribi_t::backward ribi_t::keine,ribi_t::none ribi_t::alle,ribi_t::all ribi_typ\\>,ribi_type simutrans-124.5/uncommon.mk000066400000000000000000000031451520675460500157530ustar00rootroot00000000000000# # This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # # Source files located in ../ OBJS := $(patsubst %, $(BUILDDIR)/%-$(TOOL).o, $(basename $(patsubst ../simutrans/%, %,$(filter ../simutrans/%,$(SOURCES))))) # Source files located in current directory OBJS += $(patsubst %, $(BUILDDIR)/$(TOOL)/%-$(TOOL).o, $(basename $(filter-out ../simutrans/%,$(SOURCES)))) DEPS := $(patsubst %.o, %.d, $(OBJS)) DIRS := $(sort $(dir $(OBJS))) BUILDCONFIG_FILES := ../../uncommon.mk Makefile ../../config.$(CFG) # Make build directories DUMMY := $(shell mkdir -p $(DIRS)) .PHONY: all clean clean-prog ifeq ($(VERBOSE),) Q = @ else Q = endif all: $(TOOL_PROGDIR)/$(PROG) $(TOOL_PROGDIR)/$(PROG): $(OBJS) @echo "===> LD $@" $(Q)$(CXX) $(OBJS) $(LDFLAGS) $(STD_LIBS) $(LIBS) -o $(TOOL_PROGDIR)/$(PROG) clean-prog: $(Q)rm -f $(TOOL_PROGDIR)/$(PROG) clean: @echo "===> Cleaning up" $(Q)rm -f $(OBJS) $(Q)rm -f $(DEPS) $(Q)rm -f $(TOOL_PROGDIR)/$(PROG) -include $(DEPS) # Silence stale header dependency errors %.h: @true # Source files located in ../ $(BUILDDIR)/%-$(TOOL).o: ../simutrans/%.cc $(BUILDCONFIG_FILES) @echo "===> CC $<" $(Q)$(CC) $(CCFLAGS) -c -MMD -o $@ $< $(BUILDDIR)/%-$(TOOL).o: ../simutrans/%.cc $(BUILDCONFIG_FILES) @echo "===> CXX $<" $(Q)$(CXX) $(CXXFLAGS) -c -MMD -o $@ $< # Source files located in current directory $(BUILDDIR)/$(TOOL)/%-$(TOOL).o: %.c $(BUILDCONFIG_FILES) @echo "===> CC $<" $(Q)$(CC) $(CCFLAGS) -c -MMD -o $@ $< $(BUILDDIR)/$(TOOL)/%-$(TOOL).o: %.cc $(BUILDCONFIG_FILES) @echo "===> CXX $<" $(Q)$(CXX) $(CXXFLAGS) -c -MMD -o $@ $< simutrans-124.5/vcpkg.json000066400000000000000000000003521520675460500155710ustar00rootroot00000000000000{ "version-string": "1.0.0", "dependencies": [ "freetype", "miniupnpc", "pthread", "zstd", "sdl2", "zlib" ], "builtin-baseline": "3c76dc55f8bd2b7f4824bcd860055094bfbbb9ea" }

Os Detalhes do Comboio abrem-se do boto de opes Detalhes na janela da Informao do Comboio (use a ferramenta Consultar sobre um Comboio).
Se todos os itens no esto visveis, reajuste a janela dos Detalhes do Comboio ou use as barras laterais para ver os outros itens.